From aa69ea2d99957c3d43731223930d1bc17e30e2ca Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Thu, 15 Dec 2022 15:35:54 -0800 Subject: [PATCH 001/234] fix(daemon): Log to state, not cache --- packages/daemon/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/daemon/index.js b/packages/daemon/index.js index 279537ee2f..803ef6b0c6 100644 --- a/packages/daemon/index.js +++ b/packages/daemon/index.js @@ -56,7 +56,7 @@ export const start = async (locator = defaultLocator) => { }); await cachePathCreated; - const logPath = path.join(locator.cachePath, 'endo.log'); + const logPath = path.join(locator.statePath, 'endo.log'); await statePathCreated; const output = fs.openSync(logPath, 'a'); From 858f16e4da7c4a39d40c2461543835f131eed5a2 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Mon, 19 Dec 2022 16:49:36 -0800 Subject: [PATCH 002/234] refactor(daemon): Debugging comments --- packages/daemon/src/connection.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/daemon/src/connection.js b/packages/daemon/src/connection.js index 880af8efe8..1e2f4ac66c 100644 --- a/packages/daemon/src/connection.js +++ b/packages/daemon/src/connection.js @@ -44,6 +44,7 @@ const makeCapTPWithStreams = (name, writer, reader, cancelled, bootstrap) => { /** @param {any} message */ const messageToBytes = message => { const text = JSON.stringify(message); + // console.log('->', text); const bytes = textEncoder.encode(text); return bytes; }; @@ -51,6 +52,7 @@ const messageToBytes = message => { /** @param {Uint8Array} bytes */ const bytesToMessage = bytes => { const text = textDecoder.decode(bytes); + // console.log('<-', text); const message = JSON.parse(text); return message; }; From 6d06a67b58e46523ea44c6a27cc2334d7c8e7a18 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Thu, 8 Dec 2022 16:38:54 -0800 Subject: [PATCH 003/234] refactor(daemon): Endo daemon bootstrap is the private facet --- packages/cli/src/endo.js | 3 +-- packages/daemon/README.md | 6 ++---- packages/daemon/index.js | 2 +- packages/daemon/src/daemon.js | 19 ++++--------------- packages/daemon/test/test-endo.js | 2 +- 5 files changed, 9 insertions(+), 23 deletions(-) diff --git a/packages/cli/src/endo.js b/packages/cli/src/endo.js index 09ffbf316f..dd5347527e 100644 --- a/packages/cli/src/endo.js +++ b/packages/cli/src/endo.js @@ -130,8 +130,7 @@ export const main = async rawArgs => { cancelled, ); const bootstrap = getBootstrap(); - const publicFacet = E.get(bootstrap).publicFacet; - await E(publicFacet).ping(); + await E(bootstrap).ping(); process.stderr.write('ok\n'); }); diff --git a/packages/daemon/README.md b/packages/daemon/README.md index 14086a50c4..c382312183 100644 --- a/packages/daemon/README.md +++ b/packages/daemon/README.md @@ -10,7 +10,5 @@ with the user, and manages per-user storage and compute access. Over that channel, the daemon communicates in CapTP over netstring message envelopes. -The bootstrap object has public and private facets. -All access over the public facet are mediate on the private facet. -So, for example, a request for a handle would be posed on the public facet, and -the user would be obliged to answer on the private facet. +The bootstrap provides the user agent API from which one can derive facets for +other agents. diff --git a/packages/daemon/index.js b/packages/daemon/index.js index 803ef6b0c6..5714e7a7e1 100644 --- a/packages/daemon/index.js +++ b/packages/daemon/index.js @@ -41,7 +41,7 @@ export const terminate = async (locator = defaultLocator) => { cancelled, ); const bootstrap = getBootstrap(); - await E(E.get(bootstrap).privateFacet).terminate(); + await E(bootstrap).terminate(); // @ts-expect-error zero-argument promise resolve cancel(); await closed; diff --git a/packages/daemon/src/daemon.js b/packages/daemon/src/daemon.js index 3e1f2fae76..3492364208 100644 --- a/packages/daemon/src/daemon.js +++ b/packages/daemon/src/daemon.js @@ -97,14 +97,11 @@ const makeWorker = async locator => { /** * @param {import('../types.js').Locator} locator */ -const makeEndoFacets = locator => { - const publicFacet = Far('EndoPublicFacet', { +const makeEndoBootstrap = locator => + Far('EndoPublicFacet', { async ping() { return 'pong'; }, - }); - - const privateFacet = Far('EndoPrivateFacet', { async terminate() { console.error('Endo daemon received terminate request'); cancel(Error('Terminate')); @@ -114,14 +111,6 @@ const makeEndoFacets = locator => { }, }); - const endoFacet = harden({ - publicFacet, - privateFacet, - }); - - return endoFacet; -}; - export const main = async () => { console.error(`Endo daemon starting on PID ${process.pid}`); process.once('exit', () => { @@ -142,7 +131,7 @@ export const main = async () => { const locator = { sockPath, statePath, cachePath }; - const endoFacets = makeEndoFacets(locator); + const endoBootstrap = makeEndoBootstrap(locator); const statePathP = fs.promises.mkdir(statePath, { recursive: true }); const cachePathP = fs.promises.mkdir(cachePath, { recursive: true }); @@ -181,7 +170,7 @@ export const main = async () => { conn, conn, cancelled, - endoFacets, + endoBootstrap, ); closed.catch(sinkError); }); diff --git a/packages/daemon/test/test-endo.js b/packages/daemon/test/test-endo.js index 4fc5da9c7c..e0abfd1475 100644 --- a/packages/daemon/test/test-endo.js +++ b/packages/daemon/test/test-endo.js @@ -48,7 +48,7 @@ test.serial('lifecycle', async t => { cancelled, ); const bootstrap = getBootstrap(); - const worker = await E(E.get(bootstrap).privateFacet).makeWorker(); + const worker = await E(bootstrap).makeWorker(); await E(E.get(worker).actions).terminate(); cancel(Error('Cancelled')); await closed; From 6d2b5429ea113d84097e4bbb8b235c4b5f67d42b Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Mon, 19 Dec 2022 16:42:07 -0800 Subject: [PATCH 004/234] refactor(daemon): Tautological restart condition --- packages/daemon/index.js | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/packages/daemon/index.js b/packages/daemon/index.js index 5714e7a7e1..56db6ca93e 100644 --- a/packages/daemon/index.js +++ b/packages/daemon/index.js @@ -108,12 +108,8 @@ export const clean = async (locator = defaultLocator) => { }; export const restart = async (locator = defaultLocator) => { - // TODO: Refactor this guaranteed-true condition - // @ts-expect-error - if (restart) { - await terminate(locator).catch(() => {}); - await clean(locator); - } + await terminate(locator).catch(() => {}); + await clean(locator); return start(locator); }; From 8802bb060d40eb8548898146d039bbdc85269636 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Mon, 2 Jan 2023 17:24:49 -0800 Subject: [PATCH 005/234] feat(daemon): Thread ephemeral state path --- packages/daemon/index.js | 33 ++++++++++++++++++++++++++----- packages/daemon/src/daemon.js | 9 +++++---- packages/daemon/src/worker.js | 6 +++--- packages/daemon/test/test-endo.js | 1 + packages/daemon/types.d.ts | 1 + 5 files changed, 38 insertions(+), 12 deletions(-) diff --git a/packages/daemon/index.js b/packages/daemon/index.js index 56db6ca93e..64e06d6a1a 100644 --- a/packages/daemon/index.js +++ b/packages/daemon/index.js @@ -9,7 +9,12 @@ import os from 'os'; import { E } from '@endo/eventual-send'; import { makePromiseKit } from '@endo/promise-kit'; -import { whereEndoState, whereEndoSock, whereEndoCache } from '@endo/where'; +import { + whereEndoState, + whereEndoEphemeralState, + whereEndoSock, + whereEndoCache, +} from '@endo/where'; import { makeEndoClient } from './src/client.js'; // Reexports: @@ -25,6 +30,11 @@ const info = { const defaultLocator = { statePath: whereEndoState(process.platform, process.env, info), + ephemeralStatePath: whereEndoEphemeralState( + process.platform, + process.env, + info, + ), sockPath: whereEndoSock(process.platform, process.env, info), cachePath: whereEndoCache(process.platform, process.env, info), }; @@ -63,7 +73,12 @@ export const start = async (locator = defaultLocator) => { const child = popen.fork( endoDaemonPath, - [locator.sockPath, locator.statePath, locator.cachePath], + [ + locator.sockPath, + locator.statePath, + locator.ephemeralStatePath, + locator.cachePath, + ], { detached: true, stdio: ['ignore', output, output, 'ipc'], @@ -119,11 +134,19 @@ export const stop = async (locator = defaultLocator) => { export const reset = async (locator = defaultLocator) => { const cleanedUp = clean(locator); - const restated = fs.promises + const removedState = fs.promises .rm(locator.statePath, { recursive: true }) .catch(enoentOk); - const cachedOut = fs.promises + const removedEphemeralState = fs.promises + .rm(locator.ephemeralStatePath, { recursive: true }) + .catch(enoentOk); + const removedCache = fs.promises .rm(locator.cachePath, { recursive: true }) .catch(enoentOk); - await Promise.all([cleanedUp, restated, cachedOut]); + await Promise.all([ + cleanedUp, + removedState, + removedEphemeralState, + removedCache, + ]); }; diff --git a/packages/daemon/src/daemon.js b/packages/daemon/src/daemon.js index 3492364208..f4dc4f8433 100644 --- a/packages/daemon/src/daemon.js +++ b/packages/daemon/src/daemon.js @@ -118,8 +118,8 @@ export const main = async () => { }); if (process.argv.length < 5) { - throw Error( - `daemon.js requires arguments [sockPath] [statePath] [cachePath], got ${process.argv.join( + throw new Error( + `daemon.js requires arguments [sockPath] [statePath] [ephemeralStatePath] [cachePath], got ${process.argv.join( ', ', )}`, ); @@ -127,9 +127,10 @@ export const main = async () => { const sockPath = process.argv[2]; const statePath = process.argv[3]; - const cachePath = process.argv[4]; + const ephemeralStatePath = process.argv[4]; + const cachePath = process.argv[5]; - const locator = { sockPath, statePath, cachePath }; + const locator = { sockPath, statePath, ephemeralStatePath, cachePath }; const endoBootstrap = makeEndoBootstrap(locator); diff --git a/packages/daemon/src/worker.js b/packages/daemon/src/worker.js index c1c5c70764..46a44a04fd 100644 --- a/packages/daemon/src/worker.js +++ b/packages/daemon/src/worker.js @@ -35,9 +35,9 @@ export const main = async () => { console.error('Endo worker exiting'); }); - if (process.argv.length < 2) { - throw Error( - `worker.js requires arguments [uuid] [workerCachePath], got ${process.argv.join( + if (process.argv.length < 4) { + throw new Error( + `worker.js requires arguments uuid, workerStatePath, workerEphemeralStatePath, workerCachePath, got ${process.argv.join( ', ', )}`, ); diff --git a/packages/daemon/test/test-endo.js b/packages/daemon/test/test-endo.js index e0abfd1475..9dfaebf381 100644 --- a/packages/daemon/test/test-endo.js +++ b/packages/daemon/test/test-endo.js @@ -26,6 +26,7 @@ const dirname = url.fileURLToPath(new URL('..', import.meta.url)).toString(); const locator = { statePath: path.join(dirname, 'tmp/state'), + ephemeralStatePath: path.join(dirname, 'tmp/run'), cachePath: path.join(dirname, 'tmp/cache'), sockPath: process.platform === 'win32' diff --git a/packages/daemon/types.d.ts b/packages/daemon/types.d.ts index 83dedd592a..fc672f9250 100644 --- a/packages/daemon/types.d.ts +++ b/packages/daemon/types.d.ts @@ -1,5 +1,6 @@ type Locator = { statePath: string; + ephemeralStatePath: string; cachePath: string; sockPath: string; }; From 3b96b5a8762f9224d2fe9bcdeebccc80c82ee357 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Mon, 19 Dec 2022 16:42:47 -0800 Subject: [PATCH 006/234] feat(daemon): Clean up socket on halt --- packages/daemon/index.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/daemon/index.js b/packages/daemon/index.js index 64e06d6a1a..86158126bd 100644 --- a/packages/daemon/index.js +++ b/packages/daemon/index.js @@ -129,7 +129,8 @@ export const restart = async (locator = defaultLocator) => { }; export const stop = async (locator = defaultLocator) => { - return terminate(locator).catch(() => {}); + await terminate(locator).catch(() => {}); + await clean(locator); }; export const reset = async (locator = defaultLocator) => { From abd5baa9e795f9520387efd6000f5bc5dd8af0a7 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Mon, 19 Dec 2022 16:44:32 -0800 Subject: [PATCH 007/234] feat(daemon): Reset restarts if currently running --- packages/daemon/index.js | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/packages/daemon/index.js b/packages/daemon/index.js index 86158126bd..439ef93084 100644 --- a/packages/daemon/index.js +++ b/packages/daemon/index.js @@ -51,10 +51,12 @@ export const terminate = async (locator = defaultLocator) => { cancelled, ); const bootstrap = getBootstrap(); - await E(bootstrap).terminate(); + await E(bootstrap) + .terminate() + .catch(() => {}); // @ts-expect-error zero-argument promise resolve cancel(); - await closed; + await closed.catch(() => {}); }; export const start = async (locator = defaultLocator) => { @@ -134,6 +136,13 @@ export const stop = async (locator = defaultLocator) => { }; export const reset = async (locator = defaultLocator) => { + // Attempt to restore to a running state if currently running, based on + // whether we manage to terminate it. + const needsRestart = await terminate(locator).then( + () => true, + () => false, + ); + const cleanedUp = clean(locator); const removedState = fs.promises .rm(locator.statePath, { recursive: true }) @@ -150,4 +159,8 @@ export const reset = async (locator = defaultLocator) => { removedEphemeralState, removedCache, ]); + + if (needsRestart) { + await start(locator); + } }; From 230303a64b45f8fe9fa91a704af88da941404261 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Mon, 19 Dec 2022 16:50:40 -0800 Subject: [PATCH 008/234] chore(daemon): Ignore test output artifacts --- packages/daemon/.gitignore | 1 + 1 file changed, 1 insertion(+) create mode 100644 packages/daemon/.gitignore diff --git a/packages/daemon/.gitignore b/packages/daemon/.gitignore new file mode 100644 index 0000000000..ceeb05b410 --- /dev/null +++ b/packages/daemon/.gitignore @@ -0,0 +1 @@ +/tmp From e4e99171eb1d4f0e54652fb05ff685cff783af76 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Mon, 2 Jan 2023 17:30:59 -0800 Subject: [PATCH 009/234] feat(cli): Thread ephemeral state path --- packages/cli/src/endo.js | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/packages/cli/src/endo.js b/packages/cli/src/endo.js index dd5347527e..ea130c38be 100644 --- a/packages/cli/src/endo.js +++ b/packages/cli/src/endo.js @@ -22,7 +22,12 @@ import { reset, makeEndoClient, } from '@endo/daemon'; -import { whereEndoState, whereEndoSock, whereEndoCache } from '@endo/where'; +import { + whereEndoState, + whereEndoEphemeralState, + whereEndoSock, + whereEndoCache, +} from '@endo/where'; import { mapLocation, hashLocation, @@ -52,6 +57,11 @@ const info = { }; const statePath = whereEndoState(process.platform, process.env, info); +const ephemeralStatePath = whereEndoEphemeralState( + process.platform, + process.env, + info, +); const sockPath = whereEndoSock(process.platform, process.env, info); const cachePath = whereEndoCache(process.platform, process.env, info); const logPath = path.join(statePath, 'endo.log'); @@ -77,6 +87,10 @@ export const main = async rawArgs => { process.stdout.write(`${statePath}\n`); }); + where.command('run').action(async _cmd => { + process.stdout.write(`${ephemeralStatePath}\n`); + }); + where.command('sock').action(async _cmd => { process.stdout.write(`${sockPath}\n`); }); From 2296c62d9d080d784e37bde4ef2ecc1fe5874287 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Mon, 19 Dec 2022 16:53:08 -0800 Subject: [PATCH 010/234] feat(cli): Log follow option --- packages/cli/src/endo.js | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/packages/cli/src/endo.js b/packages/cli/src/endo.js index ea130c38be..8b0258c26d 100644 --- a/packages/cli/src/endo.js +++ b/packages/cli/src/endo.js @@ -123,19 +123,21 @@ export const main = async rawArgs => { await reset(); }); - const log = program.command('log').action(async cmd => { - await new Promise((resolve, reject) => { - const args = cmd.opts().follow ? ['-f'] : []; - const child = spawn('tail', [...args, logPath], { - stdio: ['inherit', 'inherit', 'inherit'], + program + .command('log') + .option('-f, --follow', 'follow the tail of the log') + .action(async cmd => { + // TODO rerun follower command after reset + await new Promise((resolve, reject) => { + const args = cmd.opts().follow ? ['-f'] : []; + const child = spawn('tail', [...args, logPath], { + stdio: ['inherit', 'inherit', 'inherit'], + }); + child.on('error', reject); + child.on('exit', resolve); + cancelled.catch(() => child.kill()); }); - child.on('error', reject); - child.on('exit', resolve); - cancelled.catch(() => child.kill()); }); - }); - - log.option('-f, --follow', 'follow the tail of the log'); program.command('ping').action(async _cmd => { const { getBootstrap } = await makeEndoClient( From cce8d8ff5946eea1cafe4f9f25bdef3de4a9e1e9 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Mon, 19 Dec 2022 16:41:13 -0800 Subject: [PATCH 011/234] feat(daemon): RefReader and ReaderRef --- packages/daemon/index.js | 2 ++ packages/daemon/package.json | 1 + packages/daemon/src/reader-ref.js | 49 +++++++++++++++++++++++++++++++ packages/daemon/src/ref-reader.js | 31 +++++++++++++++++++ 4 files changed, 83 insertions(+) create mode 100644 packages/daemon/src/reader-ref.js create mode 100644 packages/daemon/src/ref-reader.js diff --git a/packages/daemon/index.js b/packages/daemon/index.js index 439ef93084..b1108fe0e9 100644 --- a/packages/daemon/index.js +++ b/packages/daemon/index.js @@ -19,6 +19,8 @@ import { makeEndoClient } from './src/client.js'; // Reexports: export { makeEndoClient } from './src/client.js'; +export { makeRefReader, makeRefIterator } from './src/ref-reader.js'; +export { makeReaderRef, makeIteratorRef } from './src/reader-ref.js'; const { username, homedir } = os.userInfo(); const temp = os.tmpdir(); diff --git a/packages/daemon/package.json b/packages/daemon/package.json index 35ef9c2e7e..122a3e0e95 100644 --- a/packages/daemon/package.json +++ b/packages/daemon/package.json @@ -36,6 +36,7 @@ "test": "ava" }, "dependencies": { + "@endo/base64": "^0.2.34", "@endo/captp": "^3.1.4", "@endo/eventual-send": "^0.17.5", "@endo/far": "^0.2.21", diff --git a/packages/daemon/src/reader-ref.js b/packages/daemon/src/reader-ref.js new file mode 100644 index 0000000000..1661e26c94 --- /dev/null +++ b/packages/daemon/src/reader-ref.js @@ -0,0 +1,49 @@ +// @ts-check + +import { encodeBase64 } from '@endo/base64'; +import { mapReader } from '@endo/stream'; +import { Far } from '@endo/far'; + +export const asyncIterate = iterable => { + let iterator; + if (iterable[Symbol.asyncIterator]) { + iterator = iterable[Symbol.asyncIterator](); + } else if (iterable[Symbol.iterator]) { + iterator = iterable[Symbol.iterator](); + } else if ('next' in iterable) { + iterator = iterable; + } + return iterator; +}; + +export const makeIteratorRef = iterator => { + return Far('AsyncIterator', { + async next() { + return iterator.next(); + }, + /** + * @param {any} value + */ + async return(value) { + if (iterator.return !== undefined) { + return iterator.return(value); + } + return harden({ done: true, value: undefined }); + }, + /** + * @param {any} error + */ + async throw(error) { + if (iterator.throw !== undefined) { + return iterator.throw(error); + } + return harden({ done: true, value: undefined }); + }, + [Symbol.asyncIterator]() { + return this; + }, + }); +}; + +export const makeReaderRef = readable => + makeIteratorRef(mapReader(asyncIterate(readable), encodeBase64)); diff --git a/packages/daemon/src/ref-reader.js b/packages/daemon/src/ref-reader.js new file mode 100644 index 0000000000..d55b255f46 --- /dev/null +++ b/packages/daemon/src/ref-reader.js @@ -0,0 +1,31 @@ +// @ts-check + +import { decodeBase64 } from '@endo/base64'; +import { mapReader } from '@endo/stream'; +import { E } from '@endo/far'; + +/** + * @template TValue + * @template TReturn + * @template TNext + * @param {import('@endo/far').ERef>} iteratorRef + */ +export const makeRefIterator = iteratorRef => { + const iterator = { + /** @param {[] | [TNext]} args */ + next: async (...args) => E(iteratorRef).next(...args), + /** @param {[] | [TReturn]} args */ + return: async (...args) => E(iteratorRef).return(...args), + /** @param {any} error */ + throw: async error => E(iteratorRef).throw(error), + [Symbol.asyncIterator]: () => iterator, + }; + return iterator; +}; + +/** + * @param {import('@endo/far').ERef>} readerRef + * @returns {AsyncIterableIterator} + */ +export const makeRefReader = readerRef => + mapReader(makeRefIterator(readerRef), decodeBase64); From b07f7b3881ea657d29ac9aca364c607e798f813a Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Mon, 19 Dec 2022 16:40:28 -0800 Subject: [PATCH 012/234] feat(daemon): Pet names, spawn, eval, store, durable ephemera, worker termination refactor --- packages/daemon/index.js | 41 +- packages/daemon/src/client.js | 24 +- packages/daemon/src/connection.js | 29 +- packages/daemon/src/daemon-node-powers.js | 221 +++++++ packages/daemon/src/daemon-node.js | 55 ++ packages/daemon/src/daemon.js | 686 +++++++++++++++++----- packages/daemon/src/types.d.ts | 90 +++ packages/daemon/src/worker-node-powers.js | 32 + packages/daemon/src/worker-node.js | 45 ++ packages/daemon/src/worker.js | 96 +-- packages/daemon/test/test-endo.js | 107 +++- packages/daemon/types.d.ts | 21 +- 12 files changed, 1208 insertions(+), 239 deletions(-) create mode 100644 packages/daemon/src/daemon-node-powers.js create mode 100644 packages/daemon/src/daemon-node.js create mode 100644 packages/daemon/src/types.d.ts create mode 100644 packages/daemon/src/worker-node-powers.js create mode 100644 packages/daemon/src/worker-node.js diff --git a/packages/daemon/index.js b/packages/daemon/index.js index b1108fe0e9..459f01a0c8 100644 --- a/packages/daemon/index.js +++ b/packages/daemon/index.js @@ -22,6 +22,17 @@ export { makeEndoClient } from './src/client.js'; export { makeRefReader, makeRefIterator } from './src/ref-reader.js'; export { makeReaderRef, makeIteratorRef } from './src/reader-ref.js'; +const removePath = async removalPath => { + return fs.promises + .rm(removalPath, { recursive: true, force: true }) + .catch(cause => { + /** @type {object} */ + const error = new Error(cause.message, { cause }); + error.code = cause.code; + throw error; + }); +}; + const { username, homedir } = os.userInfo(); const temp = os.tmpdir(); const info = { @@ -42,7 +53,7 @@ const defaultLocator = { }; const endoDaemonPath = url.fileURLToPath( - new URL('src/daemon.js', import.meta.url), + new URL('src/daemon-node.js', import.meta.url), ); export const terminate = async (locator = defaultLocator) => { @@ -62,17 +73,10 @@ export const terminate = async (locator = defaultLocator) => { }; export const start = async (locator = defaultLocator) => { - const cachePathCreated = fs.promises.mkdir(locator.cachePath, { - recursive: true, - }); - const statePathCreated = fs.promises.mkdir(locator.statePath, { + await fs.promises.mkdir(locator.statePath, { recursive: true, }); - - await cachePathCreated; const logPath = path.join(locator.statePath, 'endo.log'); - - await statePathCreated; const output = fs.openSync(logPath, 'a'); const child = popen.fork( @@ -107,8 +111,7 @@ export const start = async (locator = defaultLocator) => { child.on('message', _message => { child.disconnect(); child.unref(); - // @ts-expect-error zero-argument promise resolve - resolve(); + resolve(undefined); }); }); }; @@ -122,7 +125,7 @@ const enoentOk = error => { export const clean = async (locator = defaultLocator) => { if (process.platform !== 'win32') { - await fs.promises.rm(locator.sockPath).catch(enoentOk); + await removePath(locator.sockPath).catch(enoentOk); } }; @@ -146,15 +149,11 @@ export const reset = async (locator = defaultLocator) => { ); const cleanedUp = clean(locator); - const removedState = fs.promises - .rm(locator.statePath, { recursive: true }) - .catch(enoentOk); - const removedEphemeralState = fs.promises - .rm(locator.ephemeralStatePath, { recursive: true }) - .catch(enoentOk); - const removedCache = fs.promises - .rm(locator.cachePath, { recursive: true }) - .catch(enoentOk); + const removedState = removePath(locator.statePath).catch(enoentOk); + const removedEphemeralState = removePath(locator.ephemeralStatePath).catch( + enoentOk, + ); + const removedCache = removePath(locator.cachePath).catch(enoentOk); await Promise.all([ cleanedUp, removedState, diff --git a/packages/daemon/src/client.js b/packages/daemon/src/client.js index f02c2dc4e4..5245598ca8 100644 --- a/packages/daemon/src/client.js +++ b/packages/daemon/src/client.js @@ -1,7 +1,8 @@ // @ts-check import net from 'net'; -import { makeNodeNetstringCapTP } from './connection.js'; +import { makeNodeReader, makeNodeWriter } from '@endo/stream-node'; +import { makeNetstringCapTP } from './connection.js'; /** * @template TBootstrap @@ -14,7 +15,24 @@ export const makeEndoClient = async (name, sockPath, cancelled, bootstrap) => { const conn = net.connect(sockPath); await new Promise((resolve, reject) => { conn.on('connect', resolve); - conn.on('error', reject); + conn.on('error', (/** @type {any} */ error) => { + if (error.code === 'ENOENT') { + reject( + new Error( + `Cannot connect to Endo. Is Endo running? ${error.message}`, + ), + ); + } else { + reject(error); + } + }); }); - return makeNodeNetstringCapTP(name, conn, conn, cancelled, bootstrap); + + return makeNetstringCapTP( + name, + makeNodeWriter(conn), + makeNodeReader(conn), + cancelled, + bootstrap, + ); }; diff --git a/packages/daemon/src/connection.js b/packages/daemon/src/connection.js index 1e2f4ac66c..6bb43113da 100644 --- a/packages/daemon/src/connection.js +++ b/packages/daemon/src/connection.js @@ -3,7 +3,6 @@ import { makeCapTP } from '@endo/captp'; import { mapWriter, mapReader } from '@endo/stream'; import { makeNetstringReader, makeNetstringWriter } from '@endo/netstring'; -import { makeNodeReader, makeNodeWriter } from '@endo/stream-node'; const textEncoder = new TextEncoder(); const textDecoder = new TextDecoder(); @@ -16,7 +15,7 @@ const textDecoder = new TextDecoder(); * @param {Promise} cancelled * @param {TBootstrap} bootstrap */ -const makeCapTPWithStreams = (name, writer, reader, cancelled, bootstrap) => { +const makeMessageCapTP = (name, writer, reader, cancelled, bootstrap) => { /** @param {any} message */ const send = message => { return writer.next(message); @@ -60,25 +59,31 @@ const bytesToMessage = bytes => { /** * @template TBootstrap * @param {string} name - * @param {import('stream').Writable} nodeWriter - * @param {import('stream').Readable} nodeReader + * @param {import('@endo/stream').Writer} bytesWriter + * @param {import('@endo/stream').Reader} bytesReader * @param {Promise} cancelled * @param {TBootstrap} bootstrap */ -export const makeNodeNetstringCapTP = ( +export const makeNetstringCapTP = ( name, - nodeWriter, - nodeReader, + bytesWriter, + bytesReader, cancelled, bootstrap, ) => { - const writer = mapWriter( - makeNetstringWriter(makeNodeWriter(nodeWriter), { chunked: true }), + const messageWriter = mapWriter( + makeNetstringWriter(bytesWriter, { chunked: true }), messageToBytes, ); - const reader = mapReader( - makeNetstringReader(makeNodeReader(nodeReader)), + const messageReader = mapReader( + makeNetstringReader(bytesReader), bytesToMessage, ); - return makeCapTPWithStreams(name, writer, reader, cancelled, bootstrap); + return makeMessageCapTP( + name, + messageWriter, + messageReader, + cancelled, + bootstrap, + ); }; diff --git a/packages/daemon/src/daemon-node-powers.js b/packages/daemon/src/daemon-node-powers.js new file mode 100644 index 0000000000..030ea6ba77 --- /dev/null +++ b/packages/daemon/src/daemon-node-powers.js @@ -0,0 +1,221 @@ +// @ts-check +/* global process, setTimeout, clearTimeout */ +/* eslint-disable no-void */ + +import { makePromiseKit } from '@endo/promise-kit'; +import { makePipe } from '@endo/stream'; +import { makeNodeReader, makeNodeWriter } from '@endo/stream-node'; + +/** + * @param {object} modules + * @param {typeof import('crypto')} modules.crypto + * @param {typeof import('net')} modules.net + * @param {typeof import('fs')} modules.fs + * @param {typeof import('path')} modules.path + * @param {typeof import('child_process')} modules.popen + * @param {typeof import('url')} modules.url + * @returns {import('./types.js').DaemonicPowers} + */ +export const makePowers = ({ crypto, net, fs, path: fspath, popen, url }) => { + /** @param {Error} error */ + const sinkError = error => { + console.error(error); + }; + + /** @param {Error} error */ + const exitOnError = error => { + console.error(error); + process.exit(-1); + }; + + const makeSha512 = () => { + const digester = crypto.createHash('sha512'); + return harden({ + update: chunk => digester.update(chunk), + digestHex: () => digester.digest('hex'), + }); + }; + + const randomUuid = () => crypto.randomUUID(); + + const listenOnPath = async (sockPath, cancelled) => { + const [ + /** @type {Reader} */ readFrom, + /** @type {Writer { + err(error); + void writeTo.throw(error); + }); + server.on('close', () => { + void writeTo.return(undefined); + }); + + cancelled.catch(error => { + server.close(); + void writeTo.throw(error); + }); + + const listening = new Promise(resolve => + server.listen({ path: sockPath }, () => resolve(undefined)), + ); + + await Promise.race([erred, cancelled, listening]); + + server.on('connection', conn => { + const reader = makeNodeReader(conn); + const writer = makeNodeWriter(conn); + const closed = new Promise(resolve => conn.on('close', resolve)); + // TODO Respect back-pressure signal and avoid accepting new connections. + void writeTo.next({ reader, writer, closed }); + }); + + return readFrom; + }; + + /** + * @param {string} path + */ + const informParentWhenListeningOnPath = path => { + if (process.send) { + process.send({ type: 'listening', path }); + } + }; + + /** + * @param {string} path + */ + const makeFileReader = path => { + const nodeReadStream = fs.createReadStream(path); + return makeNodeReader(nodeReadStream); + }; + + /** + * @param {string} path + */ + const makeFileWriter = path => { + const nodeWriteStream = fs.createWriteStream(path); + return makeNodeWriter(nodeWriteStream); + }; + + /** + * @param {string} path + * @param {string} text + */ + const writeFileText = async (path, text) => { + await fs.promises.writeFile(path, text); + }; + + /** + * @param {string} path + */ + const readFileText = async path => { + return fs.promises.readFile(path, 'utf-8'); + }; + + /** + * @param {string} path + */ + const makePath = async path => { + await fs.promises.mkdir(path, { recursive: true }); + }; + + const renamePath = async (source, target) => + fs.promises.rename(source, target); + + const joinPath = (...components) => fspath.join(...components); + + const delay = async (ms, cancelled) => { + // Do not attempt to set up a timer if already cancelled. + await Promise.race([cancelled, undefined]); + return new Promise((resolve, reject) => { + const handle = setTimeout(resolve, ms); + cancelled.catch(error => { + reject(error); + clearTimeout(handle); + }); + }); + }; + + /** + * @param {string} uuid + * @param {string} path + * @param {string} logPath + * @param {string} pidPath + * @param {string} sockPath + * @param {string} statePath + * @param {string} ephemeralStatePath + * @param {string} cachePath + * @param {Promise} cancelled + */ + const makeWorker = async ( + uuid, + path, + logPath, + pidPath, + sockPath, + statePath, + ephemeralStatePath, + cachePath, + cancelled, + ) => { + const log = fs.openSync(logPath, 'a'); + const child = popen.fork( + path, + [uuid, sockPath, statePath, ephemeralStatePath, cachePath], + { + stdio: ['ignore', log, log, 'pipe', 'pipe', 'ipc'], + // @ts-ignore Stale Node.js type definition. + windowsHide: true, + }, + ); + const nodeWriter = /** @type {import('stream').Writable} */ ( + child.stdio[3] + ); + const nodeReader = /** @type {import('stream').Readable} */ ( + child.stdio[4] + ); + assert(nodeWriter); + assert(nodeReader); + const reader = makeNodeReader(nodeReader); + const writer = makeNodeWriter(nodeWriter); + + const closed = new Promise(resolve => { + child.on('exit', () => resolve(undefined)); + }); + + await writeFileText(pidPath, `${child.pid}\n`); + + cancelled.catch(async () => { + child.kill(); + }); + + return { reader, writer, closed, pid: child.pid }; + }; + + const endoWorkerPath = url.fileURLToPath( + new URL('worker-node.js', import.meta.url), + ); + + return harden({ + sinkError, + exitOnError, + makeSha512, + randomUuid, + listenOnPath, + informParentWhenListeningOnPath, + makeFileReader, + makeFileWriter, + writeFileText, + readFileText, + makePath, + joinPath, + renamePath, + delay, + makeWorker, + endoWorkerPath, + }); +}; diff --git a/packages/daemon/src/daemon-node.js b/packages/daemon/src/daemon-node.js new file mode 100644 index 0000000000..3dc79079a4 --- /dev/null +++ b/packages/daemon/src/daemon-node.js @@ -0,0 +1,55 @@ +/* global process */ + +// Establish a perimeter: +import 'ses'; +import '@endo/eventual-send/shim.js'; +import '@endo/promise-kit/shim.js'; +import '@endo/lockdown/commit.js'; + +import crypto from 'crypto'; +import net from 'net'; +import fs from 'fs'; +import path from 'path'; +import popen from 'child_process'; +import url from 'url'; + +import { makePromiseKit } from '@endo/promise-kit'; +import { main } from './daemon.js'; +import { makePowers } from './daemon-node-powers.js'; + +if (process.argv.length < 5) { + throw new Error( + `daemon.js requires arguments [sockPath] [statePath] [ephemeralStatePath] [cachePath], got ${process.argv.join( + ', ', + )}`, + ); +} + +const [sockPath, statePath, ephemeralStatePath, cachePath] = + process.argv.slice(2); + +/** @type {import('../index.js').Locator} */ +const locator = { + sockPath, + statePath, + ephemeralStatePath, + cachePath, +}; + +const powers = makePowers({ + crypto, + net, + fs, + path, + popen, + url, +}); + +const { promise: cancelled, reject: cancel } = + /** @type {import('@endo/promise-kit').PromiseKit} */ ( + makePromiseKit() + ); + +process.once('SIGINT', () => cancel(new Error('SIGINT'))); + +main(powers, locator, process.pid, cancel, cancelled).catch(powers.exitOnError); diff --git a/packages/daemon/src/daemon.js b/packages/daemon/src/daemon.js index f4dc4f8433..9beab8abc3 100644 --- a/packages/daemon/src/daemon.js +++ b/packages/daemon/src/daemon.js @@ -1,6 +1,5 @@ // @ts-check /// -/* global process, setTimeout */ // Establish a perimeter: import 'ses'; @@ -8,177 +7,592 @@ import '@endo/eventual-send/shim.js'; import '@endo/promise-kit/shim.js'; import '@endo/lockdown/commit.js'; -import crypto from 'crypto'; -import net from 'net'; -import fs from 'fs'; -import path from 'path'; -import popen from 'child_process'; -import url from 'url'; - import { E, Far } from '@endo/far'; import { makePromiseKit } from '@endo/promise-kit'; -import { makeNodeNetstringCapTP } from './connection.js'; +import { makeNetstringCapTP } from './connection.js'; +import { makeRefReader } from './ref-reader.js'; +import { makeReaderRef } from './reader-ref.js'; const { quote: q } = assert; -const { promise: cancelled, reject: cancel } = makePromiseKit(); - -// TODO thread through command arguments. -const gracePeriodMs = 100; - -const grace = cancelled.catch(async () => { - await new Promise(resolve => setTimeout(resolve, gracePeriodMs)); -}); - -const endoWorkerPath = url.fileURLToPath(new URL('worker.js', import.meta.url)); - -/** @param {Error} error */ -const sinkError = error => { - console.error(error); -}; +const validNamePattern = /^[a-zA-Z][a-zA-Z0-9]{0,127}$/; /** - * @param {import('../types.js').Locator} locator + * @param {import('./types.js').DaemonicPowers} powers + * @param {import('./types.js').Locator} locator + * @param {object} args + * @param {Promise} args.cancelled + * @param {(error: Error) => void} args.cancel + * @param {number} args.gracePeriodMs + * @param {Promise} args.gracePeriodElapsed */ -const makeWorker = async locator => { - // @ts-ignore Node.js crypto does in fact have randomUUID. - const uuid = await crypto.randomUUID(); - const workerCachePath = path.join(locator.cachePath, uuid); - await fs.promises.mkdir(workerCachePath, { recursive: true }); - const logPath = path.join(workerCachePath, 'worker.log'); - const output = fs.openSync(logPath, 'w'); - const child = popen.fork(endoWorkerPath, [uuid, workerCachePath], { - stdio: ['ignore', output, output, 'pipe', 'ipc'], - }); - console.error(`Endo worker started PID ${child.pid} UUID ${uuid}`); - const stream = /** @type {import('stream').Duplex} */ (child.stdio[3]); - assert(stream); - const { getBootstrap, closed } = makeNodeNetstringCapTP( - `Worker ${uuid}`, - stream, - stream, - cancelled, - undefined, - ); +const makeEndoBootstrap = ( + powers, + locator, + { cancelled, cancel, gracePeriodMs, gracePeriodElapsed }, +) => { + /** @type {Map} */ + const pets = new Map(); + /** @type {Map} */ + const values = new Map(); + /** @type {WeakMap>} */ + const workerBootstraps = new WeakMap(); - const bootstrap = getBootstrap(); + const petNameDirectoryPath = powers.joinPath(locator.statePath, 'pet-name'); - const exited = new Promise(resolve => { - child.on('exit', () => { - console.error(`Endo worker stopped PID ${child.pid} UUID ${uuid}`); - resolve(undefined); + /** + * @param {string} sha512 + */ + const makeReadableSha512 = sha512 => { + const storageDirectoryPath = powers.joinPath( + locator.statePath, + 'store-sha512', + ); + const storagePath = powers.joinPath(storageDirectoryPath, sha512); + const stream = async () => { + const reader = powers.makeFileReader(storagePath); + return makeReaderRef(reader); + }; + const text = async () => { + return powers.readFileText(storagePath); + }; + return Far(`Readable file with SHA-512 ${sha512.slice(0, 8)}...`, { + sha512: () => sha512, + stream, + text, + [Symbol.asyncIterator]: stream, }); - }); + }; + + /** + * @param {string} sha512 + */ + const provideReadableSha512 = sha512 => { + // TODO Contemplate using a different map for storage. + // For the moment, there's no risk of a UUID colliding with a SHA512. + let readable = values.get(sha512); + if (readable === undefined) { + readable = makeReadableSha512(sha512); + values.set(sha512, readable); + } + return readable; + }; - const terminated = Promise.all([exited, closed]); + /** + * @param {import('@endo/eventual-send').ERef>} readerRef + * @param {string} [name] + */ + const store = async (readerRef, name) => { + if (name !== undefined) { + if (!validNamePattern.test(name)) { + throw new Error(`Invalid pet name ${q(name)}`); + } + } - const { reject: cancelWorker, promise: workerCancelled } = makePromiseKit(); + const storageDirectoryPath = powers.joinPath( + locator.statePath, + 'store-sha512', + ); + await powers.makePath(storageDirectoryPath); - cancelled.catch(async error => cancelWorker(error)); + // Pump the reader into a temporary file and hash. + // We use a temporary file to avoid leaving a partially writen object, + // but also because we won't know the name we will use until we've + // completed the hash. + const digester = powers.makeSha512(); + const storageUuid = powers.randomUuid(); + const temporaryStoragePath = powers.joinPath( + storageDirectoryPath, + storageUuid, + ); + const writer = powers.makeFileWriter(temporaryStoragePath); + for await (const chunk of makeRefReader(readerRef)) { + await writer.next(chunk); + digester.update(chunk); + } + await writer.return(undefined); + const sha512 = digester.digestHex(); - workerCancelled.then(async () => { - const responded = E(bootstrap).terminate(); - await Promise.race([grace, terminated, responded]); - child.kill(); - }); + // Retain the pet name first (to win a garbage collection race) + if (name !== undefined) { + await powers.makePath(petNameDirectoryPath); + const petNamePath = powers.joinPath(petNameDirectoryPath, `${name}.json`); + await powers.writeFileText( + petNamePath, + `${JSON.stringify({ + type: 'readableSha512', + readableSha512: sha512, + })}\n`, + ); + } - const terminate = () => { - cancelWorker(Error('Terminated')); + // Finish with an atomic rename. + const storagePath = powers.joinPath(storageDirectoryPath, sha512); + await powers.renamePath(temporaryStoragePath, storagePath); + return makeReadableSha512(sha512); }; - return harden({ - actions: Far('EndoWorkerActions', { - terminate, - }), - terminated, - }); -}; + /** + * @param {string} workerUuid + */ + const makeWorkerBootstrap = async workerUuid => { + return Far(`Endo for worker ${workerUuid}`, {}); + }; -/** - * @param {import('../types.js').Locator} locator - */ -const makeEndoBootstrap = locator => - Far('EndoPublicFacet', { - async ping() { - return 'pong'; - }, - async terminate() { - console.error('Endo daemon received terminate request'); - cancel(Error('Terminate')); - }, - async makeWorker() { - return makeWorker(locator); - }, - }); + /** + * @param {string} workerUuid + * @param {string} [workerName] + */ + const makeWorkerUuid = async (workerUuid, workerName) => { + const workerCachePath = powers.joinPath( + locator.cachePath, + 'worker-uuid', + workerUuid, + ); + const workerStatePath = powers.joinPath( + locator.statePath, + 'worker-uuid', + workerUuid, + ); + const workerEphemeralStatePath = powers.joinPath( + locator.ephemeralStatePath, + 'worker-uuid', + workerUuid, + ); -export const main = async () => { - console.error(`Endo daemon starting on PID ${process.pid}`); - process.once('exit', () => { - console.error(`Endo daemon stopping on PID ${process.pid}`); - }); + await Promise.all([ + powers.makePath(workerStatePath), + powers.makePath(workerEphemeralStatePath), + ]); + + if (workerName !== undefined) { + await powers.makePath(petNameDirectoryPath); + const petNamePath = powers.joinPath( + petNameDirectoryPath, + `${workerName}.json`, + ); + await powers.writeFileText( + petNamePath, + `${JSON.stringify({ + type: 'workerUuid', + workerUuid, + })}\n`, + ); + } + + const { reject: cancelWorker, promise: workerCancelled } = + /** @type {import('@endo/promise-kit').PromiseKit} */ ( + makePromiseKit() + ); + cancelled.catch(async error => cancelWorker(error)); - if (process.argv.length < 5) { - throw new Error( - `daemon.js requires arguments [sockPath] [statePath] [ephemeralStatePath] [cachePath], got ${process.argv.join( - ', ', - )}`, + const logPath = powers.joinPath(workerStatePath, 'worker.log'); + const workerPidPath = powers.joinPath( + workerEphemeralStatePath, + 'worker.pid', + ); + const { + reader, + writer, + closed: workerClosed, + pid: workerPid, + } = await powers.makeWorker( + workerUuid, + powers.endoWorkerPath, + logPath, + workerPidPath, + locator.sockPath, + workerStatePath, + workerEphemeralStatePath, + workerCachePath, + workerCancelled, + ); + + console.log(`Endo worker started PID ${workerPid} UUID ${workerUuid}`); + + const { getBootstrap, closed: capTpClosed } = makeNetstringCapTP( + `Worker ${workerUuid}`, + writer, + reader, + gracePeriodElapsed, + makeWorkerBootstrap(workerUuid), ); - } - const sockPath = process.argv[2]; - const statePath = process.argv[3]; - const ephemeralStatePath = process.argv[4]; - const cachePath = process.argv[5]; + const closed = Promise.race([workerClosed, capTpClosed]).finally(() => { + console.log(`Endo worker stopped PID ${workerPid} UUID ${workerUuid}`); + }); - const locator = { sockPath, statePath, ephemeralStatePath, cachePath }; + /** @type {import('@endo/eventual-send').ERef} */ + const workerBootstrap = getBootstrap(); - const endoBootstrap = makeEndoBootstrap(locator); + const terminate = async () => { + E.sendOnly(workerBootstrap).terminate(); + const cancelWorkerGracePeriod = () => { + throw new Error('Exited gracefully before grace period elapsed'); + }; + const workerGracePeriodCancelled = Promise.race([ + gracePeriodElapsed, + closed, + ]).then(cancelWorkerGracePeriod, cancelWorkerGracePeriod); + await powers + .delay(gracePeriodMs, workerGracePeriodCancelled) + .then(() => { + throw new Error( + `Worker termination grace period ${gracePeriodMs}ms elapsed`, + ); + }) + .catch(cancelWorker); + }; - const statePathP = fs.promises.mkdir(statePath, { recursive: true }); - const cachePathP = fs.promises.mkdir(cachePath, { recursive: true }); - await Promise.all([statePathP, cachePathP]); + const worker = Far('EndoWorker', { + terminate, - const pidPath = path.join(cachePath, 'endo.pid'); - await fs.promises.writeFile(pidPath, `${process.pid}\n`); + whenTerminated: () => closed, - const server = net.createServer(); + /** + * @param {string} source + * @param {Array} codeNames + * @param {Array} petNames + * @param {string} resultName + */ + evaluate: async (source, codeNames, petNames, resultName) => { + if (!validNamePattern.test(resultName)) { + throw new Error(`Invalid pet name ${q(resultName)}`); + } + if (petNames.length !== codeNames.length) { + throw new Error('Evaluator requires one pet name for each code name'); + // TODO and they must all be strings. Use pattern language. + } - server.listen( - { - path: sockPath, - }, - () => { - console.log( - `Endo daemon listening on ${q(sockPath)} ${new Date().toISOString()}`, + await powers.makePath(petNameDirectoryPath); + const refs = Object.fromEntries( + await Promise.all( + petNames.map(async (endowmentPetName, index) => { + const endowmentCodeName = codeNames[index]; + const petNamePath = powers.joinPath( + petNameDirectoryPath, + `${endowmentPetName}.json`, + ); + const petNameText = await powers.readFileText(petNamePath); + try { + // TODO validate + /** @type {[string, import('./types.js').Ref]} */ + return [endowmentCodeName, JSON.parse(petNameText)]; + } catch (error) { + throw new TypeError( + `Corrupt pet name description for ${endowmentPetName}: ${error.message}`, + ); + } + }), + ), + ); + + const ref = { + /** @type {'eval'} */ + type: 'eval', + workerUuid, + source, + refs, + }; + // Behold, recursion: + // eslint-disable-next-line no-use-before-define + return makeRef(ref, resultName); + }, + }); + + workerBootstraps.set(worker, workerBootstrap); + + return worker; + }; + + /** + * @param {string} workerUuid + * @param {string} [name] + */ + const provideWorkerUuid = async (workerUuid, name) => { + let worker = + /** @type {import('@endo/eventual-send').ERef>} */ ( + values.get(workerUuid) ); - // Inform parent that we have an open unix domain socket, if we were - // spawned with IPC. - if (process.send) { - process.send({ type: 'listening', path: sockPath }); + if (worker === undefined) { + worker = makeWorkerUuid(workerUuid, name); + values.set(workerUuid, worker); + } + return worker; + }; + + /** + * @param {string} workerUuid + * @param {string} source + * @param {Record} refs + */ + const provideEval = async (workerUuid, source, refs) => { + const workerFacet = await provideWorkerUuid(workerUuid); + const workerBootstrap = workerBootstraps.get(workerFacet); + assert(workerBootstrap); + const codeNames = Object.keys(refs); + const endowmentValues = await Promise.all( + // Behold, recursion: + // eslint-disable-next-line no-use-before-define + Object.values(refs).map(ref => provideRef(ref)), + ); + return E(workerBootstrap).evaluate(source, codeNames, endowmentValues); + }; + + /** + * @param {string} valueUuid + */ + const makeValueUuid = async valueUuid => { + const valuesDirectoryPath = powers.joinPath( + locator.statePath, + 'value-uuid', + ); + const valuePath = powers.joinPath(valuesDirectoryPath, `${valueUuid}.json`); + const refText = await powers.readFileText(valuePath); + const ref = (() => { + try { + return JSON.parse(refText); + } catch (error) { + throw new TypeError( + `Corrupt description for value to be derived according to file ${valuePath}: ${error.message}`, + ); + } + })(); + // Behold, recursion: + // eslint-disable-next-line no-use-before-define + return provideRef(ref); + }; + + /** + * @param {string} valueUuid + */ + const provideValueUuid = async valueUuid => { + let value = values.get(valueUuid); + if (value === undefined) { + value = makeValueUuid(valueUuid); + values.set(valueUuid, value); + } + return value; + }; + + /** + * @param {import('./types.js').Ref} ref + */ + const provideRef = async ref => { + if (ref.type === 'readableSha512') { + return provideReadableSha512(ref.readableSha512); + } else if (ref.type === 'workerUuid') { + return provideWorkerUuid(ref.workerUuid); + } else if (ref.type === 'valueUuid') { + return provideValueUuid(ref.valueUuid); + } else if (ref.type === 'eval') { + return provideEval(ref.workerUuid, ref.source, ref.refs); + } else { + throw new TypeError(`Invalid reference: ${JSON.stringify(ref)}`); + } + }; + + /** + * @param {import('./types.js').Ref} ref + * @param {string} [petName] + */ + const makeRef = async (ref, petName) => { + const value = await provideRef(ref); + if (petName !== undefined) { + const valueUuid = powers.randomUuid(); + + // Persist instructions for revival (this can be collected) + const valuesDirectoryPath = powers.joinPath( + locator.statePath, + 'value-uuid', + ); + await powers.makePath(valuesDirectoryPath); + const valuePath = powers.joinPath( + valuesDirectoryPath, + `${valueUuid}.json`, + ); + await powers.writeFileText(valuePath, `${JSON.stringify(ref)}\n`); + + // Make a reference by pet name (this can be overwritten) + await powers.makePath(petNameDirectoryPath); + const petNamePath = powers.joinPath( + petNameDirectoryPath, + `${petName}.json`, + ); + await powers.writeFileText( + petNamePath, + `${JSON.stringify({ + type: 'valueUuid', + valueUuid, + })}\n`, + ); + + values.set(valueUuid, value); + } + return value; + }; + + /** + * @param {string} refPath + */ + const provideRefPath = async refPath => { + const refText = await powers.readFileText(refPath).catch(() => { + // TODO handle EMFILE gracefully + throw new ReferenceError(`No reference exists at path ${refPath}`); + }); + const ref = (() => { + try { + return JSON.parse(refText); + } catch (error) { + throw new TypeError( + `Corrupt description for reference in file ${refPath}: ${error.message}`, + ); } + })(); + // TODO validate + return provideRef(ref); + }; + + /** + * @param {string} name + */ + const revive = async name => { + const petNamePath = powers.joinPath(petNameDirectoryPath, `${name}.json`); + return provideRefPath(petNamePath).catch(error => { + throw new Error(`Corrupt pet name ${name}: ${error.message}`); + }); + }; + + /** + * @param {string} name + */ + const provide = async name => { + if (!validNamePattern.test(name)) { + throw new Error(`Invalid pet name ${q(name)}`); + } + + let pet = pets.get(name); + if (pet === undefined) { + pet = revive(name); + pets.set(name, pet); + } + return pet; + }; + + return Far('Endo private facet', { + // TODO for user named + + ping: async () => 'pong', + + terminate: async () => { + cancel(new Error('Termination requested')); }, - ); - server.on('error', error => { - sinkError(error); - process.exit(-1); + + /** + * @param {string} [name] + */ + makeWorker: async name => { + // @ts-ignore Node.js crypto does in fact have randomUUID. + const workerUuid = powers.randomUuid(); + return provideWorkerUuid(workerUuid, name); + }, + + store, + provide, + }); +}; + +/* + * @param {import('./types.js').DaemonicPowers} powers + * @param {import('./types.js').Locator} locator + * @param {number | undefined} pid + * @param {(error: Error) => void} cancel + * @param {Promise} cancelled + */ +export const main = async (powers, locator, pid, cancel, cancelled) => { + console.log(`Endo daemon starting on PID ${pid}`); + cancelled.catch(() => { + console.log(`Endo daemon stopping on PID ${pid}`); }); - server.on('connection', conn => { - console.error( - `Endo daemon received connection ${new Date().toISOString()}`, + + const { promise: gracePeriodCancelled, reject: cancelGracePeriod } = + /** @type {import('@endo/promise-kit').PromiseKit} */ ( + makePromiseKit() ); - const { closed } = makeNodeNetstringCapTP( - 'Endo', - conn, - conn, - cancelled, - endoBootstrap, + + // TODO thread through command arguments. + const gracePeriodMs = 100; + + /** @type {Promise} */ + const gracePeriodElapsed = cancelled.catch(async error => { + await powers.delay(gracePeriodMs, gracePeriodCancelled); + console.log( + `Endo daemon grace period ${gracePeriodMs}ms elapsed on PID ${pid}`, ); - closed.catch(sinkError); + throw error; }); - cancelled.catch(() => { - server.close(); + const endoBootstrap = makeEndoBootstrap(powers, locator, { + cancelled, + cancel, + gracePeriodMs, + gracePeriodElapsed, }); -}; -main().catch(sinkError); + const statePathP = powers.makePath(locator.statePath); + const ephemeralStatePathP = powers.makePath(locator.ephemeralStatePath); + const cachePathP = powers.makePath(locator.cachePath); + await Promise.all([statePathP, cachePathP, ephemeralStatePathP]); + + const pidPath = powers.joinPath(locator.ephemeralStatePath, 'endo.pid'); + await powers.writeFileText(pidPath, `${pid}\n`); + + const connections = await powers.listenOnPath(locator.sockPath, cancelled); + // Resolve a promise in the Endo CLI through the IPC channel: + powers.informParentWhenListeningOnPath(locator.sockPath); + console.log( + `Endo daemon listening on ${q( + locator.sockPath, + )} ${new Date().toISOString()}`, + ); + let nextConnectionNumber = 0; + /** @type {Set>} */ + const connectionClosedPromises = new Set(); + try { + for await (const { + reader, + writer, + closed: connectionClosed, + } of connections) { + const connectionNumber = nextConnectionNumber; + nextConnectionNumber += 1; + console.log( + `Endo daemon received connection ${connectionNumber} at ${new Date().toISOString()}`, + ); + + const { closed: capTpClosed } = makeNetstringCapTP( + 'Endo', + writer, + reader, + cancelled, + endoBootstrap, + ); + + const closed = Promise.race([connectionClosed, capTpClosed]); + connectionClosedPromises.add(closed); + closed.finally(() => { + connectionClosedPromises.delete(closed); + console.log( + `Endo daemon closed connection ${connectionNumber} at ${new Date().toISOString()}`, + ); + }); + } + } catch (error) { + cancel(error); + cancelGracePeriod(error); + } finally { + await Promise.all(Array.from(connectionClosedPromises)); + cancel(new Error('Terminated normally')); + cancelGracePeriod(new Error('Terminated normally')); + } +}; diff --git a/packages/daemon/src/types.d.ts b/packages/daemon/src/types.d.ts new file mode 100644 index 0000000000..c907e28907 --- /dev/null +++ b/packages/daemon/src/types.d.ts @@ -0,0 +1,90 @@ +import type { Reader, Writer } from '@endo/stream'; + +export type Locator = { + statePath: string; + ephemeralStatePath: string; + cachePath: string; + sockPath: string; +}; + +export type Sha512 = { + update: (chunk: Uint8Array) => void; + digestHex: () => string; +}; + +export type Connection = { + reader: Reader; + writer: Writer; + closed: Promise; +}; + +export type Worker = Connection & { + pid: number | undefined; +}; + +export type DaemonicPowers = { + sinkError: (error) => void; + exitOnError: (error) => void; + makeSha512: () => Sha512; + randomUuid: () => string; + listenOnPath: ( + path: string, + cancelled: Promise, + ) => Promise>; + informParentWhenListeningOnPath: (path: string) => void; + makeFileReader: (path: string) => Reader; + makeFileWriter: (path: string) => Writer; + readFileText: (path: string) => Promise; + writeFileText: (path: string, text: string) => Promise; + makePath: (path: string) => Promise; + renamePath: (source: string, target: string) => Promise; + joinPath: (...components: Array) => string; + delay: (ms: number, cancelled: Promise) => Promise; + makeWorker: ( + uuid: string, + path: string, + logPath: string, + pidPath: string, + sockPath: string, + statePath: string, + ephemeralStatePath: string, + cachePath: string, + cancelled: Promise, + ) => Promise; + endoWorkerPath: string; +}; + +export type MignonicPowers = { + exitOnError: (error) => void; + connection: { + reader: Reader; + writer: Writer; + }; +}; + +type ReadableSha512Ref = { + type: 'readableSha512'; + readableSha512: string; +}; + +type WorkerUuidRef = { + type: 'workerUuid'; + workerUuid: string; +}; + +// Reference to a reference. +type ValueUuid = { + type: 'valueUuid'; + valueUuid: string; +}; + +type EvalRef = { + type: 'eval'; + workerUuid: string; + source: string; + // Behold: recursion + // eslint-disable-next-line no-use-before-define + refs: Record; +}; + +export type Ref = ReadableSha512Ref | WorkerUuidRef | ValueUuid | EvalRef; diff --git a/packages/daemon/src/worker-node-powers.js b/packages/daemon/src/worker-node-powers.js new file mode 100644 index 0000000000..0635a22a34 --- /dev/null +++ b/packages/daemon/src/worker-node-powers.js @@ -0,0 +1,32 @@ +// @ts-check +/* global process */ + +import { makeNodeReader, makeNodeWriter } from '@endo/stream-node'; + +/** + * @param {object} modules + * @param {typeof import('fs')} modules.fs + * @returns {import('./types.js').MignonicPowers} + */ +export const makePowers = ({ fs }) => { + /** @param {Error} error */ + const exitOnError = error => { + console.error(error); + process.exit(-1); + }; + + // @ts-ignore This is in fact how you open a file descriptor. + const reader = makeNodeReader(fs.createReadStream(null, { fd: 3 })); + // @ts-ignore This is in fact how you open a file descriptor. + const writer = makeNodeWriter(fs.createWriteStream(null, { fd: 4 })); + + const connection = { + reader, + writer, + }; + + return harden({ + exitOnError, + connection, + }); +}; diff --git a/packages/daemon/src/worker-node.js b/packages/daemon/src/worker-node.js new file mode 100644 index 0000000000..81f5de9757 --- /dev/null +++ b/packages/daemon/src/worker-node.js @@ -0,0 +1,45 @@ +/* global process */ + +// Establish a perimeter: +import 'ses'; +import '@endo/eventual-send/shim.js'; +import '@endo/promise-kit/shim.js'; +import '@endo/lockdown/commit.js'; + +import fs from 'fs'; + +import { makePromiseKit } from '@endo/promise-kit'; +import { main } from './worker.js'; +import { makePowers } from './worker-node-powers.js'; + +if (process.argv.length < 7) { + throw new Error( + `worker.js requires arguments workerUuid, daemonSockPath, workerStatePath, workerEphemeralStatePath, workerCachePath, got ${process.argv.join( + ', ', + )}`, + ); +} + +const [workerUuid, sockPath, statePath, ephemeralStatePath, cachePath] = + process.argv.slice(2); + +/** @type {import('../index.js').Locator} */ +const locator = { + sockPath, + statePath, + ephemeralStatePath, + cachePath, +}; + +const powers = makePowers({ fs }); + +const { promise: cancelled, reject: cancel } = + /** @type {import('@endo/promise-kit').PromiseKit} */ ( + makePromiseKit() + ); + +process.once('SIGINT', () => cancel(new Error('SIGINT'))); + +main(powers, locator, workerUuid, process.pid, cancel, cancelled).catch( + powers.exitOnError, +); diff --git a/packages/daemon/src/worker.js b/packages/daemon/src/worker.js index 46a44a04fd..79c87e15c9 100644 --- a/packages/daemon/src/worker.js +++ b/packages/daemon/src/worker.js @@ -1,59 +1,73 @@ // @ts-check /// -/* global process */ -// Establish a perimeter: -import 'ses'; -import '@endo/eventual-send/shim.js'; -import '@endo/lockdown/commit.js'; +import { E, Far } from '@endo/far'; +import { makeNetstringCapTP } from './connection.js'; -import fs from 'fs'; +const endowments = harden({ + assert, + E, + Far, + TextEncoder, + TextDecoder, + URL, +}); -import { Far } from '@endo/far'; -import { makePromiseKit } from '@endo/promise-kit'; -import { makeNodeNetstringCapTP } from './connection.js'; +/** + * @typedef {ReturnType} WorkerBootstrap + */ -/** @param {Error} error */ -const sinkError = error => { - console.error(error); -}; - -const { promise: cancelled, reject: cancel } = makePromiseKit(); - -const makeWorkerFacet = () => { +/** + * @param {() => any} _getDaemonBootstrap + * @param {(error: Error) => void} cancel + */ +const makeWorkerFacet = (_getDaemonBootstrap, cancel) => { return Far('EndoWorkerFacet', { - async terminate() { + terminate: async () => { console.error('Endo worker received terminate request'); cancel(Error('terminate')); }, + + /** + * @param {string} source + * @param {Array} names + * @param {Array} values + */ + evaluate: async (source, names, values) => { + const compartment = new Compartment( + harden({ + ...endowments, + ...Object.fromEntries( + names.map((name, index) => [name, values[index]]), + ), + }), + ); + return compartment.evaluate(source); + }, }); }; -export const main = async () => { - console.error('Endo worker started'); - process.once('exit', () => { - console.error('Endo worker exiting'); +/** + * @param {import('./types.js').MignonicPowers} powers + * @param {import('./types.js').Locator} locator + * @param {string} uuid + * @param {number | undefined} pid + * @param {(error: Error) => void} cancel + * @param {Promise} cancelled + */ +export const main = async (powers, locator, uuid, pid, cancel, cancelled) => { + console.error(`Endo worker started on pid ${pid}`); + cancelled.catch(() => { + console.error(`Endo worker exiting on pid ${pid}`); }); - if (process.argv.length < 4) { - throw new Error( - `worker.js requires arguments uuid, workerStatePath, workerEphemeralStatePath, workerCachePath, got ${process.argv.join( - ', ', - )}`, - ); - } - - // const uuid = process.argv[2]; - // const workerCachePath = process.argv[3]; + const { reader, writer } = powers.connection; - // @ts-ignore This is in fact how you open a file descriptor. - const reader = fs.createReadStream(null, { fd: 3 }); - // @ts-ignore This is in fact how you open a file descriptor. - const writer = fs.createWriteStream(null, { fd: 3 }); + // Behold: reference cycle + // eslint-disable-next-line no-use-before-define + const workerFacet = makeWorkerFacet(() => getBootstrap(), cancel); - const workerFacet = makeWorkerFacet(); - - const { closed } = makeNodeNetstringCapTP( + const { closed, getBootstrap } = makeNetstringCapTP( 'Endo', writer, reader, @@ -61,7 +75,5 @@ export const main = async () => { workerFacet, ); - closed.catch(sinkError); + return Promise.race([cancelled, closed]); }; - -main().catch(sinkError); diff --git a/packages/daemon/test/test-endo.js b/packages/daemon/test/test-endo.js index 9dfaebf381..6c7c3db7a0 100644 --- a/packages/daemon/test/test-endo.js +++ b/packages/daemon/test/test-endo.js @@ -24,19 +24,26 @@ const { raw } = String; const dirname = url.fileURLToPath(new URL('..', import.meta.url)).toString(); -const locator = { - statePath: path.join(dirname, 'tmp/state'), - ephemeralStatePath: path.join(dirname, 'tmp/run'), - cachePath: path.join(dirname, 'tmp/cache'), - sockPath: - process.platform === 'win32' - ? raw`\\?\pipe\endo-test.sock` - : path.join(dirname, 'tmp/endo.sock'), +/** @param {Array} root */ +const makeLocator = (...root) => { + return { + statePath: path.join(dirname, ...root, 'state'), + ephemeralStatePath: path.join(dirname, ...root, 'run'), + cachePath: path.join(dirname, ...root, 'cache'), + sockPath: + process.platform === 'win32' + ? raw`\\?\pipe\endo-${root.join('-')}-test.sock` + : path.join(dirname, ...root, 'endo.sock'), + pets: new Map(), + values: new Map(), + }; }; -test.serial('lifecycle', async t => { +test('lifecycle', async t => { const { reject: cancel, promise: cancelled } = makePromiseKit(); + const locator = makeLocator('tmp', 'lifecycle'); + await stop(locator).catch(() => {}); await reset(locator); await clean(locator); await start(locator); @@ -50,11 +57,85 @@ test.serial('lifecycle', async t => { ); const bootstrap = getBootstrap(); const worker = await E(bootstrap).makeWorker(); - await E(E.get(worker).actions).terminate(); - cancel(Error('Cancelled')); - await closed; + await E(worker) + .terminate() + .catch(() => {}); + cancel(new Error('Cancelled')); + await closed.catch(() => {}); + + t.pass(); +}); + +test('spawn and evaluate', async t => { + const { promise: cancelled } = makePromiseKit(); + const locator = makeLocator('tmp', 'spawn-eval'); + + await stop(locator).catch(() => {}); + await reset(locator); + await start(locator); + + const { getBootstrap } = await makeEndoClient( + 'client', + locator.sockPath, + cancelled, + ); + const bootstrap = getBootstrap(); + + const worker = E(bootstrap).makeWorker(); + const ten = await E(worker).evaluate('10', [], []); + t.is(10, ten); await stop(locator); +}); - t.pass(); +test('persist spawn and evaluation', async t => { + const { promise: cancelled } = makePromiseKit(); + const locator = makeLocator('tmp', 'persist-spawn-eval'); + + await stop(locator).catch(() => {}); + await reset(locator); + await start(locator); + + { + const { getBootstrap } = await makeEndoClient( + 'client', + locator.sockPath, + cancelled, + ); + const bootstrap = getBootstrap(); + + const worker = E(bootstrap).makeWorker(); + const ten = await E(worker).evaluate('10', [], [], 'ten'); + t.is(10, ten); + const twenty = await E(worker).evaluate( + 'number * 2', + ['number'], + ['ten'], + 'twenty', + ); + + // TODO + // Erase the pet name for 'ten', demonstrating that the evaluation record + // does not retain its dependencies by their name. + // await E(worker).forget('ten'); + + t.is(20, twenty); + } + + await restart(locator); + + { + const { getBootstrap } = await makeEndoClient( + 'client', + locator.sockPath, + cancelled, + ); + + const bootstrap = getBootstrap(); + + const retwenty = await E(bootstrap).provide('twenty'); + t.is(20, retwenty); + } + + await stop(locator); }); diff --git a/packages/daemon/types.d.ts b/packages/daemon/types.d.ts index fc672f9250..a2bb010d7e 100644 --- a/packages/daemon/types.d.ts +++ b/packages/daemon/types.d.ts @@ -1,18 +1,15 @@ -type Locator = { - statePath: string; - ephemeralStatePath: string; - cachePath: string; - sockPath: string; -}; +import type { FarRef } from '@endo/far'; +import type { Locator } from './src/types.js'; + +export type { Locator }; + +export { makeEndoClient } from './src/client.js'; +export { makeRefReader, makeRefIterator } from './src/ref-reader.js'; +export { makeReaderRef, makeIteratorRef } from './src/reader-ref.js'; + export async function start(locator?: Locator); export async function stop(locator?: Locator); export async function restart(locator?: Locator); export async function terminate(locator?: Locator); export async function clean(locator?: Locator); export async function reset(locator?: Locator); -export async function makeEndoClient( - name: string, - sockPath: string, - cancelled: Promise, - bootstrap?: TBootstrap, -); From f287056c443288a9b788ea839c2d8db4c009b231 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Sat, 7 Jan 2023 16:34:16 -0800 Subject: [PATCH 013/234] test(daemon): Test store --- packages/daemon/test/test-endo.js | 33 +++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/packages/daemon/test/test-endo.js b/packages/daemon/test/test-endo.js index 6c7c3db7a0..3bb6b5d818 100644 --- a/packages/daemon/test/test-endo.js +++ b/packages/daemon/test/test-endo.js @@ -18,6 +18,7 @@ import { clean, reset, makeEndoClient, + makeReaderRef, } from '../index.js'; const { raw } = String; @@ -139,3 +140,35 @@ test('persist spawn and evaluation', async t => { await stop(locator); }); + +test('store', async t => { + const { promise: cancelled } = makePromiseKit(); + const locator = makeLocator('tmp', 'store'); + + await stop(locator).catch(() => {}); + await reset(locator); + await start(locator); + + { + const { getBootstrap } = await makeEndoClient( + 'client', + locator.sockPath, + cancelled, + ); + const bootstrap = getBootstrap(); + const readerRef = makeReaderRef([new TextEncoder().encode('hello\n')]); + await E(bootstrap).store(readerRef, 'helloText'); + } + + { + const { getBootstrap } = await makeEndoClient( + 'client', + locator.sockPath, + cancelled, + ); + const bootstrap = getBootstrap(); + const readable = await E(bootstrap).provide('helloText'); + const actualText = await E(readable).text(); + t.is(actualText, 'hello\n'); + } +}); From 3bb20d69c52f7d7da9d54be71d0ecd9a2d80e88e Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Sat, 7 Jan 2023 16:34:48 -0800 Subject: [PATCH 014/234] test(daemon): Closure state lost on restart using eval --- packages/daemon/test/test-endo.js | 86 +++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) diff --git a/packages/daemon/test/test-endo.js b/packages/daemon/test/test-endo.js index 3bb6b5d818..5ea124dbad 100644 --- a/packages/daemon/test/test-endo.js +++ b/packages/daemon/test/test-endo.js @@ -172,3 +172,89 @@ test('store', async t => { t.is(actualText, 'hello\n'); } }); + +test('closure state lost by restart', async t => { + const { promise: cancelled } = makePromiseKit(); + const locator = makeLocator('tmp', 'restart-closures'); + + await stop(locator).catch(() => {}); + await reset(locator); + await start(locator); + + { + const { getBootstrap } = await makeEndoClient( + 'client', + locator.sockPath, + cancelled, + ); + const bootstrap = getBootstrap(); + const worker = await E(bootstrap).makeWorker('w1'); + await E(worker).evaluate( + ` + Far('Counter Maker', { + makeCounter: (value = 0) => Far('Counter', { + incr: () => value += 1, + decr: () => value -= 1, + }), + }) + `, + [], + [], + 'counterMaker', + ); + await E(worker).evaluate( + `E(cm).makeCounter() `, + ['cm'], + ['counterMaker'], + 'counter', + ); + const one = await E(worker).evaluate( + `E(counter).incr()`, + ['counter'], + ['counter'], + ); + const two = await E(worker).evaluate( + `E(counter).incr()`, + ['counter'], + ['counter'], + ); + const three = await E(worker).evaluate( + `E(counter).incr()`, + ['counter'], + ['counter'], + ); + t.is(one, 1); + t.is(two, 2); + t.is(three, 3); + } + + await restart(locator); + + { + const { getBootstrap } = await makeEndoClient( + 'client', + locator.sockPath, + cancelled, + ); + const bootstrap = getBootstrap(); + const worker = await E(bootstrap).provide('w1'); + const one = await E(worker).evaluate( + `E(counter).incr()`, + ['counter'], + ['counter'], + ); + const two = await E(worker).evaluate( + `E(counter).incr()`, + ['counter'], + ['counter'], + ); + const three = await E(worker).evaluate( + `E(counter).incr()`, + ['counter'], + ['counter'], + ); + t.is(one, 1); + t.is(two, 2); + t.is(three, 3); + } +}); From 3f022b4997f14d5f7423163d5e9dda201be0ac12 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Tue, 20 Dec 2022 14:14:42 -0800 Subject: [PATCH 015/234] feat(cli): Store archive as pet name --- packages/cli/src/endo.js | 42 +++++++++++++++++++++++++++++++--------- 1 file changed, 33 insertions(+), 9 deletions(-) diff --git a/packages/cli/src/endo.js b/packages/cli/src/endo.js index 8b0258c26d..d98ea7660c 100644 --- a/packages/cli/src/endo.js +++ b/packages/cli/src/endo.js @@ -21,6 +21,7 @@ import { clean, reset, makeEndoClient, + makeReaderRef, } from '@endo/daemon'; import { whereEndoState, @@ -172,16 +173,39 @@ export const main = async rawArgs => { }); program - .command('archive ') - .action(async (_cmd, [archivePath, applicationPath]) => { - const archiveLocation = url.pathToFileURL(archivePath); + .command('archive ') + .option('-n,--name ', 'Store the archive into Endo') + .option('-f,--file ', 'Store the archive into a file') + .action(async (applicationPath, cmd) => { + const archiveName = cmd.opts().name; + const archivePath = cmd.opts().file; const applicationLocation = url.pathToFileURL(applicationPath); - await writeArchive( - write, - readPowers, - archiveLocation, - applicationLocation, - ); + if (archiveName !== undefined) { + const archiveBytes = await makeArchive(readPowers, applicationLocation); + const readerRef = makeReaderRef([archiveBytes]); + const { getBootstrap } = await makeEndoClient( + 'cli', + sockPath, + cancelled, + ); + try { + const bootstrap = getBootstrap(); + await E(bootstrap).store(readerRef, archiveName); + } catch (error) { + console.error(error); + cancel(error); + } + } else if (archivePath !== undefined) { + const archiveLocation = url.pathToFileURL(archivePath); + await writeArchive( + write, + readPowers, + archiveLocation, + applicationLocation, + ); + } else { + throw new TypeError('Archive command requires either a name or a path'); + } }); // Throw an error instead of exiting directly. From 1e1f41bd47c8cbb11faa6389b0cd5e15c6db0680 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Tue, 20 Dec 2022 14:15:40 -0800 Subject: [PATCH 016/234] feat(cli): Store readable blob --- packages/cli/package.json | 1 + packages/cli/src/endo.js | 24 ++++++++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/packages/cli/package.json b/packages/cli/package.json index 1f2f7431aa..f62f1c6483 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -32,6 +32,7 @@ "@endo/far": "^0.2.21", "@endo/lockdown": "^0.1.31", "@endo/promise-kit": "^0.2.59", + "@endo/stream-node": "^0.2.29", "@endo/where": "^0.3.4", "commander": "^5.0.0", "ses": "^0.18.7" diff --git a/packages/cli/src/endo.js b/packages/cli/src/endo.js index d98ea7660c..5db0c98ed1 100644 --- a/packages/cli/src/endo.js +++ b/packages/cli/src/endo.js @@ -33,12 +33,14 @@ import { mapLocation, hashLocation, loadArchive, + makeArchive, writeArchive, } from '@endo/compartment-mapper'; import { makeReadPowers, makeWritePowers, } from '@endo/compartment-mapper/node-powers.js'; +import { makeNodeReader } from '@endo/stream-node'; import { E } from '@endo/far'; const readPowers = makeReadPowers({ fs, url, crypto }); @@ -208,6 +210,28 @@ export const main = async rawArgs => { } }); + program + .command('store ') + .option( + '-n,--name ', + 'Assigns a pet name to the result for future reference', + ) + .action(async (storablePath, cmd) => { + const { name } = cmd.opts(); + const nodeReadStream = fs.createReadStream(storablePath); + const reader = makeNodeReader(nodeReadStream); + const readerRef = makeReaderRef(reader); + + const { getBootstrap } = await makeEndoClient('cli', sockPath, cancelled); + try { + const bootstrap = getBootstrap(); + await E(bootstrap).store(readerRef, name); + } catch (error) { + console.error(error); + cancel(error); + } + }); + // Throw an error instead of exiting directly. program.exitOverride(); From 659d3473977c58b6862cec31ce40385478aec849 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Tue, 20 Dec 2022 14:16:34 -0800 Subject: [PATCH 017/234] feat(cli): Spawn worker --- packages/cli/src/endo.js | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/packages/cli/src/endo.js b/packages/cli/src/endo.js index 5db0c98ed1..8da01cc0b0 100644 --- a/packages/cli/src/endo.js +++ b/packages/cli/src/endo.js @@ -232,6 +232,17 @@ export const main = async rawArgs => { } }); + program.command('spawn ').action(async name => { + const { getBootstrap } = await makeEndoClient('cli', sockPath, cancelled); + try { + const bootstrap = getBootstrap(); + await E(bootstrap).makeWorker(name); + } catch (error) { + console.error(error); + cancel(error); + } + }); + // Throw an error instead of exiting directly. program.exitOverride(); From 3f5e98321f8cb0d6c1ccfd38480ad91693b7610e Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Tue, 20 Dec 2022 14:17:09 -0800 Subject: [PATCH 018/234] feat(cli): Show pet name --- packages/cli/src/endo.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/packages/cli/src/endo.js b/packages/cli/src/endo.js index 8da01cc0b0..b68f4df17e 100644 --- a/packages/cli/src/endo.js +++ b/packages/cli/src/endo.js @@ -243,6 +243,18 @@ export const main = async rawArgs => { } }); + program.command('show ').action(async name => { + const { getBootstrap } = await makeEndoClient('cli', sockPath, cancelled); + try { + const bootstrap = getBootstrap(); + const pet = await E(bootstrap).provide(name); + console.log(pet); + } catch (error) { + console.error(error); + cancel(error); + } + }); + // Throw an error instead of exiting directly. program.exitOverride(); From a3802972b615d18a918023c369f1a9deb11567af Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Tue, 20 Dec 2022 14:17:48 -0800 Subject: [PATCH 019/234] feat(cli): Cat command --- packages/cli/src/endo.js | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/packages/cli/src/endo.js b/packages/cli/src/endo.js index b68f4df17e..ef96ac3fb7 100644 --- a/packages/cli/src/endo.js +++ b/packages/cli/src/endo.js @@ -22,6 +22,7 @@ import { reset, makeEndoClient, makeReaderRef, + makeRefReader, } from '@endo/daemon'; import { whereEndoState, @@ -255,6 +256,22 @@ export const main = async rawArgs => { } }); + program.command('cat ').action(async name => { + const { getBootstrap } = await makeEndoClient('cli', sockPath, cancelled); + try { + const bootstrap = getBootstrap(); + const readable = await E(bootstrap).provide(name); + const readerRef = E(readable).stream(); + const reader = makeRefReader(readerRef); + for await (const chunk of reader) { + process.stdout.write(chunk); + } + } catch (error) { + console.error(error); + cancel(error); + } + }); + // Throw an error instead of exiting directly. program.exitOverride(); From 463c6e693952cccb7e71e769f4b0b93fb83e3d6f Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Tue, 20 Dec 2022 14:18:16 -0800 Subject: [PATCH 020/234] feat(cli): Eval in worker --- packages/cli/src/endo.js | 44 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/packages/cli/src/endo.js b/packages/cli/src/endo.js index ef96ac3fb7..1284cae146 100644 --- a/packages/cli/src/endo.js +++ b/packages/cli/src/endo.js @@ -272,6 +272,50 @@ export const main = async rawArgs => { } }); + program + .command('eval [names...]') + .option( + '-n,--name ', + 'Assigns a name to the result for future reference, persisted between restarts', + ) + .action(async (worker, source, names, cmd) => { + const { name: resultName } = cmd.opts(); + const { getBootstrap } = await makeEndoClient('cli', sockPath, cancelled); + try { + const bootstrap = getBootstrap(); + const workerRef = E(bootstrap).provide(worker); + + const pairs = names.map(name => { + /** @type {Array} */ + const pair = name.split(':'); + if (pair.length === 1) { + return [name, name]; + } + if (pair.length > 2) { + throw new Error( + `Specify either a name endowmentName:pet-name, got: ${JSON.stringify( + name, + )}`, + ); + } + return pair; + }); + const codeNames = pairs.map(pair => pair[0]); + const endowmentNames = pairs.map(pair => pair[1]); + + const result = await E(workerRef).evaluate( + source, + codeNames, + endowmentNames, + resultName, + ); + console.log(result); + } catch (error) { + console.error(error); + cancel(error); + } + }); + // Throw an error instead of exiting directly. program.exitOverride(); From 26243d48ab17871a246df3d5c97471befb225aeb Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Tue, 27 Dec 2022 23:04:51 -0800 Subject: [PATCH 021/234] feat(cli): Start daemon on demand --- packages/cli/src/endo.js | 48 +++++++++++++++++++++++++++++++++++----- 1 file changed, 42 insertions(+), 6 deletions(-) diff --git a/packages/cli/src/endo.js b/packages/cli/src/endo.js index 1284cae146..355315126f 100644 --- a/packages/cli/src/endo.js +++ b/packages/cli/src/endo.js @@ -70,6 +70,22 @@ const sockPath = whereEndoSock(process.platform, process.env, info); const cachePath = whereEndoCache(process.platform, process.env, info); const logPath = path.join(statePath, 'endo.log'); +const provideEndoClient = async (...args) => { + try { + // It is okay to fail to connect because the daemon is not running. + return await makeEndoClient(...args); + } catch { + console.error('Starting Endo daemon...'); + // It is also okay to fail the race to start. + await start().catch(() => {}); + // But not okay to fail to connect after starting. + // We are not going to contemplate reliably in the face of a worker getting + // stopped the moment after it was started. + // That is a bridge too far. + return makeEndoClient(...args); + } +}; + export const main = async rawArgs => { const { promise: cancelled, reject: cancel } = makePromiseKit(); cancelled.catch(() => {}); @@ -186,7 +202,7 @@ export const main = async rawArgs => { if (archiveName !== undefined) { const archiveBytes = await makeArchive(readPowers, applicationLocation); const readerRef = makeReaderRef([archiveBytes]); - const { getBootstrap } = await makeEndoClient( + const { getBootstrap } = await provideEndoClient( 'cli', sockPath, cancelled, @@ -223,7 +239,11 @@ export const main = async rawArgs => { const reader = makeNodeReader(nodeReadStream); const readerRef = makeReaderRef(reader); - const { getBootstrap } = await makeEndoClient('cli', sockPath, cancelled); + const { getBootstrap } = await provideEndoClient( + 'cli', + sockPath, + cancelled, + ); try { const bootstrap = getBootstrap(); await E(bootstrap).store(readerRef, name); @@ -234,7 +254,11 @@ export const main = async rawArgs => { }); program.command('spawn ').action(async name => { - const { getBootstrap } = await makeEndoClient('cli', sockPath, cancelled); + const { getBootstrap } = await provideEndoClient( + 'cli', + sockPath, + cancelled, + ); try { const bootstrap = getBootstrap(); await E(bootstrap).makeWorker(name); @@ -245,7 +269,11 @@ export const main = async rawArgs => { }); program.command('show ').action(async name => { - const { getBootstrap } = await makeEndoClient('cli', sockPath, cancelled); + const { getBootstrap } = await provideEndoClient( + 'cli', + sockPath, + cancelled, + ); try { const bootstrap = getBootstrap(); const pet = await E(bootstrap).provide(name); @@ -257,7 +285,11 @@ export const main = async rawArgs => { }); program.command('cat ').action(async name => { - const { getBootstrap } = await makeEndoClient('cli', sockPath, cancelled); + const { getBootstrap } = await provideEndoClient( + 'cli', + sockPath, + cancelled, + ); try { const bootstrap = getBootstrap(); const readable = await E(bootstrap).provide(name); @@ -280,7 +312,11 @@ export const main = async rawArgs => { ) .action(async (worker, source, names, cmd) => { const { name: resultName } = cmd.opts(); - const { getBootstrap } = await makeEndoClient('cli', sockPath, cancelled); + const { getBootstrap } = await provideEndoClient( + 'cli', + sockPath, + cancelled, + ); try { const bootstrap = getBootstrap(); const workerRef = E(bootstrap).provide(worker); From f6bd1d6c939e33b32f1d1e0fb6da42b93c3fd2d1 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Fri, 23 Dec 2022 14:48:15 -0800 Subject: [PATCH 022/234] feat(cli): Log follow watches for reset --- packages/cli/src/endo.js | 51 ++++++++++++++++++++++++++++++++-------- 1 file changed, 41 insertions(+), 10 deletions(-) diff --git a/packages/cli/src/endo.js b/packages/cli/src/endo.js index 355315126f..d718e912e5 100644 --- a/packages/cli/src/endo.js +++ b/packages/cli/src/endo.js @@ -1,4 +1,5 @@ -/* global process */ +/* global process, setTimeout, clearTimeout */ +/* eslint-disable no-await-in-loop */ // Establish a perimeter: import 'ses'; @@ -146,17 +147,47 @@ export const main = async rawArgs => { program .command('log') .option('-f, --follow', 'follow the tail of the log') + .option('-p,--ping ', 'milliseconds between daemon reset checks') .action(async cmd => { - // TODO rerun follower command after reset - await new Promise((resolve, reject) => { - const args = cmd.opts().follow ? ['-f'] : []; - const child = spawn('tail', [...args, logPath], { - stdio: ['inherit', 'inherit', 'inherit'], + const follow = cmd.opts().follow; + const ping = cmd.opts().ping; + const logCheckIntervalMs = ping !== undefined ? Number(ping) : 5_000; + + do { + // Scope cancellation and propagate. + const { promise: followCancelled, reject: cancelFollower } = + makePromiseKit(); + cancelled.catch(cancelFollower); + + (async () => { + const { getBootstrap } = await makeEndoClient( + 'log-follower-probe', + sockPath, + followCancelled, + ); + const bootstrap = await getBootstrap(); + for (;;) { + await delay(logCheckIntervalMs, followCancelled); + await E(bootstrap).ping(); + } + })().catch(cancelFollower); + + await new Promise((resolve, reject) => { + const args = follow ? ['-f'] : []; + const child = spawn('tail', [...args, logPath], { + stdio: ['inherit', 'inherit', 'inherit'], + }); + child.on('error', reject); + child.on('exit', resolve); + followCancelled.catch(() => { + child.kill(); + }); }); - child.on('error', reject); - child.on('exit', resolve); - cancelled.catch(() => child.kill()); - }); + + if (follow) { + await delay(logCheckIntervalMs, cancelled); + } + } while (follow); }); program.command('ping').action(async _cmd => { From b029b5fe77b60ef230f82892bced8269bf9bbe4a Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Tue, 27 Dec 2022 17:16:48 -0800 Subject: [PATCH 023/234] feat(cli): Follow command --- packages/cli/src/endo.js | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/packages/cli/src/endo.js b/packages/cli/src/endo.js index d718e912e5..cc93042a78 100644 --- a/packages/cli/src/endo.js +++ b/packages/cli/src/endo.js @@ -23,6 +23,7 @@ import { reset, makeEndoClient, makeReaderRef, + makeRefIterator, makeRefReader, } from '@endo/daemon'; import { @@ -71,6 +72,18 @@ const sockPath = whereEndoSock(process.platform, process.env, info); const cachePath = whereEndoCache(process.platform, process.env, info); const logPath = path.join(statePath, 'endo.log'); +const delay = async (ms, cancelled) => { + // Do not attempt to set up a timer if already cancelled. + await Promise.race([cancelled, undefined]); + return new Promise((resolve, reject) => { + const handle = setTimeout(resolve, ms); + cancelled.catch(error => { + reject(error); + clearTimeout(handle); + }); + }); +}; + const provideEndoClient = async (...args) => { try { // It is okay to fail to connect because the daemon is not running. @@ -315,6 +328,25 @@ export const main = async rawArgs => { } }); + program.command('follow ').action(async name => { + const { getBootstrap } = await provideEndoClient( + 'cli', + sockPath, + cancelled, + ); + try { + const bootstrap = getBootstrap(); + const iterable = await E(bootstrap).provide(name); + const iterator = await E(iterable)[Symbol.asyncIterator](); + for await (const iterand of makeRefIterator(iterator)) { + console.log(iterand); + } + } catch (error) { + console.error(error); + cancel(error); + } + }); + program.command('cat ').action(async name => { const { getBootstrap } = await provideEndoClient( 'cli', From 5f05257918fd7c26b268942d26fbc16250b452a9 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Thu, 29 Dec 2022 22:53:03 -0800 Subject: [PATCH 024/234] feat(daemon): Support importUnsafe0 --- packages/daemon/src/daemon.js | 25 +++++++++++++++++++ packages/daemon/src/types.d.ts | 14 ++++++++++- packages/daemon/src/worker-node-powers.js | 6 ++++- packages/daemon/src/worker-node.js | 3 ++- packages/daemon/src/worker.js | 29 +++++++++++++++++------ 5 files changed, 67 insertions(+), 10 deletions(-) diff --git a/packages/daemon/src/daemon.js b/packages/daemon/src/daemon.js index 9beab8abc3..e13c53005b 100644 --- a/packages/daemon/src/daemon.js +++ b/packages/daemon/src/daemon.js @@ -298,6 +298,18 @@ const makeEndoBootstrap = ( // eslint-disable-next-line no-use-before-define return makeRef(ref, resultName); }, + + importUnsafe0: async (importPath, resultName) => { + const ref = { + /** @type {'importUnsafe0'} */ + type: 'importUnsafe0', + workerUuid, + importPath, + }; + // Behold, recursion: + // eslint-disable-next-line no-use-before-define + return makeRef(ref, resultName); + }, }); workerBootstraps.set(worker, workerBootstrap); @@ -339,6 +351,17 @@ const makeEndoBootstrap = ( return E(workerBootstrap).evaluate(source, codeNames, endowmentValues); }; + /** + * @param {string} workerUuid + * @param {string} importPath + */ + const provideImportUnsafe0 = async (workerUuid, importPath) => { + const workerFacet = await provideWorkerUuid(workerUuid); + const workerBootstrap = workerBootstraps.get(workerFacet); + assert(workerBootstrap); + return E(workerBootstrap).importUnsafe0(importPath); + }; + /** * @param {string} valueUuid */ @@ -387,6 +410,8 @@ const makeEndoBootstrap = ( return provideValueUuid(ref.valueUuid); } else if (ref.type === 'eval') { return provideEval(ref.workerUuid, ref.source, ref.refs); + } else if (ref.type === 'importUnsafe0') { + return provideImportUnsafe0(ref.workerUuid, ref.importPath); } else { throw new TypeError(`Invalid reference: ${JSON.stringify(ref)}`); } diff --git a/packages/daemon/src/types.d.ts b/packages/daemon/src/types.d.ts index c907e28907..17570edd6a 100644 --- a/packages/daemon/src/types.d.ts +++ b/packages/daemon/src/types.d.ts @@ -56,6 +56,7 @@ export type DaemonicPowers = { export type MignonicPowers = { exitOnError: (error) => void; + pathToFileURL: (path: string) => string; connection: { reader: Reader; writer: Writer; @@ -87,4 +88,15 @@ type EvalRef = { refs: Record; }; -export type Ref = ReadableSha512Ref | WorkerUuidRef | ValueUuid | EvalRef; +type ImportUnsafe0Ref = { + type: 'importUnsafe0'; + workerUuid: string; + importPath: string; +}; + +export type Ref = + | ReadableSha512Ref + | WorkerUuidRef + | ValueUuid + | EvalRef + | ImportUnsafe0Ref; diff --git a/packages/daemon/src/worker-node-powers.js b/packages/daemon/src/worker-node-powers.js index 0635a22a34..2161a0f89c 100644 --- a/packages/daemon/src/worker-node-powers.js +++ b/packages/daemon/src/worker-node-powers.js @@ -6,9 +6,10 @@ import { makeNodeReader, makeNodeWriter } from '@endo/stream-node'; /** * @param {object} modules * @param {typeof import('fs')} modules.fs + * @param {typeof import('url')} modules.url * @returns {import('./types.js').MignonicPowers} */ -export const makePowers = ({ fs }) => { +export const makePowers = ({ fs, url }) => { /** @param {Error} error */ const exitOnError = error => { console.error(error); @@ -25,8 +26,11 @@ export const makePowers = ({ fs }) => { writer, }; + const { pathToFileURL } = url; + return harden({ exitOnError, connection, + pathToFileURL: path => pathToFileURL(path).toString(), }); }; diff --git a/packages/daemon/src/worker-node.js b/packages/daemon/src/worker-node.js index 81f5de9757..a3c12fba24 100644 --- a/packages/daemon/src/worker-node.js +++ b/packages/daemon/src/worker-node.js @@ -7,6 +7,7 @@ import '@endo/promise-kit/shim.js'; import '@endo/lockdown/commit.js'; import fs from 'fs'; +import url from 'url'; import { makePromiseKit } from '@endo/promise-kit'; import { main } from './worker.js'; @@ -31,7 +32,7 @@ const locator = { cachePath, }; -const powers = makePowers({ fs }); +const powers = makePowers({ fs, url }); const { promise: cancelled, reject: cancel } = /** @type {import('@endo/promise-kit').PromiseKit} */ ( diff --git a/packages/daemon/src/worker.js b/packages/daemon/src/worker.js index 79c87e15c9..4e0db5bc64 100644 --- a/packages/daemon/src/worker.js +++ b/packages/daemon/src/worker.js @@ -18,10 +18,13 @@ const endowments = harden({ */ /** - * @param {() => any} _getDaemonBootstrap - * @param {(error: Error) => void} cancel + * @param {object} args + * @param {(error: Error) => void} args.cancel + * @param {(path: string) => string} args.pathToFileURL */ -const makeWorkerFacet = (_getDaemonBootstrap, cancel) => { +export const makeWorkerFacet = ({ pathToFileURL, cancel }) => { + const powerBox = Far('EndoPowerBox', {}); + return Far('EndoWorkerFacet', { terminate: async () => { console.error('Endo worker received terminate request'); @@ -44,6 +47,15 @@ const makeWorkerFacet = (_getDaemonBootstrap, cancel) => { ); return compartment.evaluate(source); }, + + /** + * @param {string} path + */ + importUnsafe0: async path => { + const url = pathToFileURL(path); + const namespace = await import(url); + return namespace.main0(powerBox); + }, }); }; @@ -61,13 +73,16 @@ export const main = async (powers, locator, uuid, pid, cancel, cancelled) => { console.error(`Endo worker exiting on pid ${pid}`); }); + const { pathToFileURL } = powers; + const { reader, writer } = powers.connection; - // Behold: reference cycle - // eslint-disable-next-line no-use-before-define - const workerFacet = makeWorkerFacet(() => getBootstrap(), cancel); + const workerFacet = makeWorkerFacet({ + pathToFileURL, + cancel, + }); - const { closed, getBootstrap } = makeNetstringCapTP( + const { closed } = makeNetstringCapTP( 'Endo', writer, reader, From df70f426fba89894160d73dda27f5475d63af23d Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Thu, 29 Dec 2022 22:55:01 -0800 Subject: [PATCH 025/234] feat(cli): Add import-unsafe0 command --- packages/cli/src/endo.js | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/packages/cli/src/endo.js b/packages/cli/src/endo.js index cc93042a78..34a900d035 100644 --- a/packages/cli/src/endo.js +++ b/packages/cli/src/endo.js @@ -415,6 +415,34 @@ export const main = async rawArgs => { } }); + program + .command('import-unsafe0 ') + .option( + '-n,--name ', + 'Assigns a name to the result for future reference, persisted between restarts', + ) + .action(async (worker, importPath, cmd) => { + const { name: resultName } = cmd.opts(); + const { getBootstrap } = await provideEndoClient( + 'cli', + sockPath, + cancelled, + ); + try { + const bootstrap = getBootstrap(); + const workerRef = E(bootstrap).provide(worker); + + const result = await E(workerRef).importUnsafe0( + path.resolve(importPath), + resultName, + ); + console.log(result); + } catch (error) { + console.error(error); + cancel(error); + } + }); + // Throw an error instead of exiting directly. program.exitOverride(); From cd5a84bd2f3e37320cfc2f6485999a0c4887e98a Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Wed, 25 Jan 2023 18:58:21 -0800 Subject: [PATCH 026/234] feat(daemon): Persistable worker endowments --- packages/daemon/src/daemon.js | 111 ++++++++++++++++++++++++++++++++- packages/daemon/src/types.d.ts | 14 +++++ packages/daemon/src/worker.js | 19 +++++- 3 files changed, 139 insertions(+), 5 deletions(-) diff --git a/packages/daemon/src/daemon.js b/packages/daemon/src/daemon.js index e13c53005b..885b459b18 100644 --- a/packages/daemon/src/daemon.js +++ b/packages/daemon/src/daemon.js @@ -11,7 +11,7 @@ import { E, Far } from '@endo/far'; import { makePromiseKit } from '@endo/promise-kit'; import { makeNetstringCapTP } from './connection.js'; import { makeRefReader } from './ref-reader.js'; -import { makeReaderRef } from './reader-ref.js'; +import { makeReaderRef, makeIteratorRef } from './reader-ref.js'; const { quote: q } = assert; @@ -35,6 +35,11 @@ const makeEndoBootstrap = ( const pets = new Map(); /** @type {Map} */ const values = new Map(); + + const requests = new Map(); + const resolvers = new WeakMap(); + let nextRequestNumber = 0; + /** @type {WeakMap>} */ const workerBootstraps = new WeakMap(); @@ -136,7 +141,13 @@ const makeEndoBootstrap = ( * @param {string} workerUuid */ const makeWorkerBootstrap = async workerUuid => { - return Far(`Endo for worker ${workerUuid}`, {}); + return Far(`Endo for worker ${workerUuid}`, { + request: async (what, resultName) => { + // Behold, recursion: + // eslint-disable-next-line no-use-before-define + return request(what, resultName, workerUuid); + }, + }); }; /** @@ -504,6 +515,98 @@ const makeEndoBootstrap = ( return pet; }; + const inbox = async () => makeIteratorRef(requests.values()); + + const requestRef = async (workerUuid, what) => { + const { promise, resolve } = makePromiseKit(); + const requestNumber = nextRequestNumber; + nextRequestNumber += 1; + const settle = () => { + requests.delete(requestNumber); + }; + const settled = promise.then(settle, settle); + /** @type {import('./types.js').Request} */ + const req = harden({ + type: /** @type {'request'} */ ('request'), + number: requestNumber, + who: workerUuid, + what, + when: new Date().toISOString(), + settled, + }); + requests.set(requestNumber, req); + resolvers.set(req, resolve); + return promise; + }; + + const request = async (what, requestName, workerUuid) => { + if (requestName !== undefined && workerUuid !== undefined) { + const workerPetNameDirectoryPath = powers.joinPath( + locator.statePath, + 'worker-uuid', + workerUuid, + 'pet-name', + ); + const petNamePath = powers.joinPath( + workerPetNameDirectoryPath, + `${requestName}.json`, + ); + return powers.readFileText(petNamePath).then( + async petNameText => { + // The named reference exists, just restore. + // TODO validate + /** @type {import('./types.js').Ref} */ + const ref = JSON.parse(petNameText); + return provideRef(ref); + }, + async () => { + // The named reference hasn't been created, so create and record. + const ref = await requestRef(workerUuid, what); + const newPetNameText = `${JSON.stringify(ref)}\n`; + await powers.makePath(workerPetNameDirectoryPath); + await powers.writeFileText(petNamePath, newPetNameText); + return provideRef(ref); + }, + ); + } + // The reference is not named nor to be named. + const ref = await requestRef(what); + return provideRef(ref); + }; + + const resolve = async (requestNumber, resolutionName) => { + if (!validNamePattern.test(resolutionName)) { + throw new Error(`Invalid pet name ${q(resolutionName)}`); + } + const req = requests.get(requestNumber); + const resolveRequest = resolvers.get(req); + if (resolveRequest !== undefined) { + const petNamePath = powers.joinPath( + petNameDirectoryPath, + `${resolutionName}.json`, + ); + const refText = await powers.readFileText(petNamePath); + const ref = (() => { + try { + return JSON.parse(refText); + } catch (cause) { + throw new TypeError( + `Corrupt pet name ${resolutionName}: ${cause.message}`, + { cause }, + ); + } + })(); + resolveRequest(ref); + } + }; + + const reject = async (requestNumber, message = 'Declined') => { + const req = requests.get(requestNumber); + if (req !== undefined) { + req.resolver.resolve(harden(Promise.reject(harden(new Error(message))))); + } + }; + return Far('Endo private facet', { // TODO for user named @@ -524,6 +627,10 @@ const makeEndoBootstrap = ( store, provide, + inbox, + request, + resolve, + reject, }); }; diff --git a/packages/daemon/src/types.d.ts b/packages/daemon/src/types.d.ts index 17570edd6a..d67f5d6f3f 100644 --- a/packages/daemon/src/types.d.ts +++ b/packages/daemon/src/types.d.ts @@ -100,3 +100,17 @@ export type Ref = | ValueUuid | EvalRef | ImportUnsafe0Ref; + +export type Label = { + number: number; + who: string; + when: string; +}; + +export type Request = { + type: 'request'; + what: string; + settled: Promise; +}; + +export type Message = Label & Request; diff --git a/packages/daemon/src/worker.js b/packages/daemon/src/worker.js index 4e0db5bc64..b8872c92f0 100644 --- a/packages/daemon/src/worker.js +++ b/packages/daemon/src/worker.js @@ -19,11 +19,21 @@ const endowments = harden({ /** * @param {object} args + * @param {() => any} args.getDaemonBootstrap * @param {(error: Error) => void} args.cancel * @param {(path: string) => string} args.pathToFileURL */ -export const makeWorkerFacet = ({ pathToFileURL, cancel }) => { - const powerBox = Far('EndoPowerBox', {}); +export const makeWorkerFacet = ({ + getDaemonBootstrap, + pathToFileURL, + cancel, +}) => { + const powerBox = Far('EndoPowerBox', { + request: async (what, name) => { + const daemon = getDaemonBootstrap(); + return E(daemon).request(what, name); + }, + }); return Far('EndoWorkerFacet', { terminate: async () => { @@ -78,11 +88,14 @@ export const main = async (powers, locator, uuid, pid, cancel, cancelled) => { const { reader, writer } = powers.connection; const workerFacet = makeWorkerFacet({ + // Behold: reference cycle + // eslint-disable-next-line no-use-before-define + getDaemonBootstrap: () => getBootstrap(), pathToFileURL, cancel, }); - const { closed } = makeNetstringCapTP( + const { closed, getBootstrap } = makeNetstringCapTP( 'Endo', writer, reader, From f50320d5a6fd10ac82e115b1e0b80c9fc97ce830 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Tue, 27 Dec 2022 20:55:04 -0800 Subject: [PATCH 027/234] feat(cli): Inbox, resolve, and reject commands --- packages/cli/src/endo.js | 58 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/packages/cli/src/endo.js b/packages/cli/src/endo.js index 34a900d035..a712d6fb0b 100644 --- a/packages/cli/src/endo.js +++ b/packages/cli/src/endo.js @@ -367,6 +367,64 @@ export const main = async rawArgs => { } }); + program.command('inbox').action(async () => { + const { getBootstrap } = await provideEndoClient( + 'cli', + sockPath, + cancelled, + ); + try { + const bootstrap = getBootstrap(); + const iterable = await E(bootstrap).inbox(); + const iterator = await E(iterable)[Symbol.asyncIterator](); + for await (const { number, who, what } of makeRefIterator(iterator)) { + // TODO ensure the description is ASCII. + console.log(`${number}. ${who}: ${what}`); + } + } catch (error) { + console.error(error); + cancel(error); + } + }); + + program + .command('resolve ') + .action(async (requestNumberText, resolutionName) => { + // TODO less bad number parsing. + const requestNumber = Number(requestNumberText); + const { getBootstrap } = await provideEndoClient( + 'cli', + sockPath, + cancelled, + ); + try { + const bootstrap = getBootstrap(); + await E(bootstrap).resolve(requestNumber, resolutionName); + } catch (error) { + console.error(error); + cancel(error); + } + }); + + program + .command('reject [message]') + .action(async (requestNumberText, message) => { + // TODO less bad number parsing. + const requestNumber = Number(requestNumberText); + const { getBootstrap } = await provideEndoClient( + 'cli', + sockPath, + cancelled, + ); + try { + const bootstrap = getBootstrap(); + await E(bootstrap).reject(requestNumber, message); + } catch (error) { + console.error(error); + cancel(error); + } + }); + program .command('eval [names...]') .option( From 82f23b46bee265ffe3e6593aa7e76256ec0d4450 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Tue, 6 Jun 2023 16:55:37 -0700 Subject: [PATCH 028/234] feat(daemon): Inline temporary pubsub implementation --- packages/daemon/package.json | 2 + packages/daemon/src/pubsub.js | 72 +++++++++++++++ packages/daemon/src/types.d.ts | 12 ++- packages/daemon/test/test-pubsub.js | 131 ++++++++++++++++++++++++++++ 4 files changed, 216 insertions(+), 1 deletion(-) create mode 100644 packages/daemon/src/pubsub.js create mode 100644 packages/daemon/test/test-pubsub.js diff --git a/packages/daemon/package.json b/packages/daemon/package.json index 122a3e0e95..a76316ce58 100644 --- a/packages/daemon/package.json +++ b/packages/daemon/package.json @@ -49,6 +49,8 @@ "ses": "^0.18.7" }, "devDependencies": { + "@endo/init": "^0.5.57", + "@endo/ses-ava": "^0.2.41", "ava": "^5.3.0", "babel-eslint": "^10.0.3", "c8": "^7.14.0", diff --git a/packages/daemon/src/pubsub.js b/packages/daemon/src/pubsub.js new file mode 100644 index 0000000000..407814cf17 --- /dev/null +++ b/packages/daemon/src/pubsub.js @@ -0,0 +1,72 @@ +import { makePromiseKit } from '@endo/promise-kit'; +import { makeStream } from '@endo/stream'; + +// TypeScript ReadOnly semantics are not sufficiently expressive to distinguish +// a value one promises not to alter from a value one must not alter, +// making it useless. +const freeze = /** @type {(v: T | Readonly) => T} */ (Object.freeze); + +/** + * @template TValue TValue + * @param {TValue} value + * @returns {import('./types.js').AsyncQueue} + */ +export const makeNullQueue = value => + harden({ + put: () => {}, + get: async () => value, + }); + +export const nullIteratorQueue = makeNullQueue( + harden({ value: undefined, done: false }), +); + +/** + * @template TValue + */ +export const makeChangePubSub = () => { + // Request pubsub async queue internals + let { promise: tailPromise, resolve: tailResolve } = makePromiseKit(); + + const sink = { + /** + * @param {TValue} value + */ + put: value => { + const { resolve, promise } = makePromiseKit(); + tailResolve(freeze({ value, promise })); + tailResolve = resolve; + // Unlike a queue, advance the read head for future subscribers. + tailPromise = promise; + }, + }; + + const makeSpring = () => { + // Capture the read head for the next published value. + let cursor = tailPromise; + return { + get: () => { + const promise = cursor.then(next => next.value); + cursor = cursor.then(next => next.promise); + return harden(promise); + }, + }; + }; + + return harden({ sink, makeSpring }); +}; +harden(makeChangePubSub); + +/** + * @template TValue + * @returns {import('./types.js').Topic} + */ +export const makeChangeTopic = () => { + /** @type {ReturnType>} */ + const { sink, makeSpring } = makeChangePubSub(); + return harden({ + publisher: makeStream(nullIteratorQueue, sink), + subscribe: () => makeStream(makeSpring(), nullIteratorQueue), + }); +}; +harden(makeChangeTopic); diff --git a/packages/daemon/src/types.d.ts b/packages/daemon/src/types.d.ts index d67f5d6f3f..2e22248264 100644 --- a/packages/daemon/src/types.d.ts +++ b/packages/daemon/src/types.d.ts @@ -1,4 +1,4 @@ -import type { Reader, Writer } from '@endo/stream'; +import type { Reader, Writer, Stream } from '@endo/stream'; export type Locator = { statePath: string; @@ -114,3 +114,13 @@ export type Request = { }; export type Message = Label & Request; + +export interface Topic< + TRead, + TWrite = undefined, + TReadReturn = undefined, + TWriteReturn = undefined, +> { + publisher: Stream; + subscribe(): Stream; +} diff --git a/packages/daemon/test/test-pubsub.js b/packages/daemon/test/test-pubsub.js new file mode 100644 index 0000000000..c187bbdbbc --- /dev/null +++ b/packages/daemon/test/test-pubsub.js @@ -0,0 +1,131 @@ +// @ts-check +/* eslint-disable no-await-in-loop */ + +import '@endo/init/debug.js'; + +import rawTest from 'ava'; +import { wrapTest } from '@endo/ses-ava'; +import { makePromiseKit } from '@endo/promise-kit'; +import { makeChangeTopic } from '../src/pubsub.js'; + +const test = wrapTest(rawTest); + +test('change topic supports parallel subscriptions', async (/** @type {import('ava').Assertions} */ t) => { + const { publisher, subscribe } = makeChangeTopic(); + + // Coordinated checkpoints. + const [p1, p2, p4, s1, s2, s3, s4] = (function* generatePromiseKits() { + for (;;) yield makePromiseKit(); + })(); + + await Promise.all([ + (async () => { + // This await ensures that the synchronous portion of the first, full + // subscription obtains a subscriber before it yields to the event loop + // the first time. + await null; + await publisher.next(1); + p1.resolve(); + + await s1.promise; + await publisher.next(2); + p2.resolve(); + + await Promise.all([s2.promise, s3.promise]); + await publisher.next(3); + + await publisher.next(4); + p4.resolve(); + + await s4.promise; + await publisher.return('EOL'); + })(), + + // Full subscription. + (async () => { + let expected = 1; + const subscription = subscribe(); + for await (const actual of subscription) { + t.is(actual, expected); + expected += 1; + } + t.is(expected, 5); + })(), + + // Delayed subscriber. + (async () => { + await p1.promise; + const subscription = subscribe(); + s1.resolve(); + + let expected = 2; + for await (const actual of subscription) { + t.is(actual, expected); + expected += 1; + } + t.is(expected, 5); + })(), + + // Further delayed subscriber. + (async () => { + await p2.promise; + const subscription = subscribe(); + s2.resolve(); + + let expected = 3; + for await (const actual of subscription) { + t.is(actual, expected); + expected += 1; + } + t.is(expected, 5); + })(), + + // Same subscription timing as previous, but using the iterator protocol to + // see the final value. + (async () => { + await p2.promise; + const subscription = subscribe(); + s3.resolve(); + + let expected = 3; + for (;;) { + const iteratorResult = await subscription.next(); + if (iteratorResult.done) { + t.is(iteratorResult.value, 'EOL'); + break; + } + t.is(iteratorResult.value, expected); + expected += 1; + } + })(), + + // Observe the completion value by dint of yield*. + (async () => { + await p4.promise; + const subscription = subscribe(); + s4.resolve(); + + const generator = (async function* consumer() { + return yield* subscription; + })(); + const { value, done } = await generator.next(); + t.is(done, true); + t.is(value, 'EOL'); + })(), + ]); + + t.pass(); +}); + +test('change topic terminates with error', async (/** @type {import('ava').Assertions} */ t) => { + const { publisher, subscribe } = makeChangeTopic(); + + const subscription = subscribe(); + + await publisher.throw(new TypeError('sentinel')); + + await t.throwsAsync(() => subscription.next(), { + instanceOf: TypeError, + message: 'sentinel', + }); +}); From 160a0ba58ea0fc59251ad0d0511e579e63e79c80 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Sat, 7 Jan 2023 16:28:57 -0800 Subject: [PATCH 029/234] feat(daemon): Implement followInbox --- packages/daemon/src/daemon.js | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/packages/daemon/src/daemon.js b/packages/daemon/src/daemon.js index 885b459b18..f03e8132ef 100644 --- a/packages/daemon/src/daemon.js +++ b/packages/daemon/src/daemon.js @@ -9,6 +9,7 @@ import '@endo/lockdown/commit.js'; import { E, Far } from '@endo/far'; import { makePromiseKit } from '@endo/promise-kit'; +import { makeChangeTopic } from './pubsub.js'; import { makeNetstringCapTP } from './connection.js'; import { makeRefReader } from './ref-reader.js'; import { makeReaderRef, makeIteratorRef } from './reader-ref.js'; @@ -38,6 +39,8 @@ const makeEndoBootstrap = ( const requests = new Map(); const resolvers = new WeakMap(); + /** @type {import('./types.js').Topic} */ + const requestsTopic = makeChangeTopic(); let nextRequestNumber = 0; /** @type {WeakMap>} */ @@ -517,6 +520,15 @@ const makeEndoBootstrap = ( const inbox = async () => makeIteratorRef(requests.values()); + const followInbox = async () => + makeIteratorRef( + (async function* currentAndSubsequentMessages() { + const subsequentRequests = requestsTopic.subscribe(); + yield* requests.values(); + yield* subsequentRequests; + })(), + ); + const requestRef = async (workerUuid, what) => { const { promise, resolve } = makePromiseKit(); const requestNumber = nextRequestNumber; @@ -525,7 +537,6 @@ const makeEndoBootstrap = ( requests.delete(requestNumber); }; const settled = promise.then(settle, settle); - /** @type {import('./types.js').Request} */ const req = harden({ type: /** @type {'request'} */ ('request'), number: requestNumber, @@ -536,6 +547,7 @@ const makeEndoBootstrap = ( }); requests.set(requestNumber, req); resolvers.set(req, resolve); + requestsTopic.publisher.next(req); return promise; }; @@ -628,6 +640,7 @@ const makeEndoBootstrap = ( store, provide, inbox, + followInbox, request, resolve, reject, From afb86f6ca193c827100945a9eecd88c1e13b92b9 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Sun, 8 Jan 2023 08:27:43 -0800 Subject: [PATCH 030/234] feat(cli): Add follow inbox flag --- packages/cli/src/endo.js | 43 +++++++++++++++++++++++----------------- 1 file changed, 25 insertions(+), 18 deletions(-) diff --git a/packages/cli/src/endo.js b/packages/cli/src/endo.js index a712d6fb0b..6d95c230d5 100644 --- a/packages/cli/src/endo.js +++ b/packages/cli/src/endo.js @@ -367,25 +367,32 @@ export const main = async rawArgs => { } }); - program.command('inbox').action(async () => { - const { getBootstrap } = await provideEndoClient( - 'cli', - sockPath, - cancelled, - ); - try { - const bootstrap = getBootstrap(); - const iterable = await E(bootstrap).inbox(); - const iterator = await E(iterable)[Symbol.asyncIterator](); - for await (const { number, who, what } of makeRefIterator(iterator)) { - // TODO ensure the description is ASCII. - console.log(`${number}. ${who}: ${what}`); + program + .command('inbox') + .option('-n,--name ', 'The name of an alternate inbox') + .option('-f,--follow', 'Follow the inbox for messages as they arrive') + .action(async cmd => { + const { name: inboxName, follow } = cmd.opts(); + const { getBootstrap } = await provideEndoClient( + 'cli', + sockPath, + cancelled, + ); + try { + const bootstrap = getBootstrap(); + const inbox = + inboxName === undefined ? bootstrap : E(bootstrap).provide(inboxName); + const iterable = follow ? E(inbox).followInbox() : E(inbox).inbox(); + const iterator = await E(iterable)[Symbol.asyncIterator](); + for await (const { number, who, what } of makeRefIterator(iterator)) { + // TODO ensure the description is ASCII. + console.log(`${number}. ${who}: ${what}`); + } + } catch (error) { + console.error(error); + cancel(error); } - } catch (error) { - console.error(error); - cancel(error); - } - }); + }); program .command('resolve ') From aa8bf5b8930a920ebbdffa01c82d89d418f2eccc Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Sat, 7 Jan 2023 16:35:23 -0800 Subject: [PATCH 031/234] test(daemon): Import unsafe services, persist requests --- packages/daemon/test/service.js | 12 +++++ packages/daemon/test/test-endo.js | 74 +++++++++++++++++++++++++++++++ 2 files changed, 86 insertions(+) create mode 100644 packages/daemon/test/service.js diff --git a/packages/daemon/test/service.js b/packages/daemon/test/service.js new file mode 100644 index 0000000000..153e349fac --- /dev/null +++ b/packages/daemon/test/service.js @@ -0,0 +1,12 @@ +import { E, Far } from '@endo/far'; + +export const main0 = powers => { + return Far('Service', { + async ask() { + return E(powers).request( + 'the meaning of life, the universe, everything', + 'answer', + ); + }, + }); +}; diff --git a/packages/daemon/test/test-endo.js b/packages/daemon/test/test-endo.js index 5ea124dbad..0d98e4140f 100644 --- a/packages/daemon/test/test-endo.js +++ b/packages/daemon/test/test-endo.js @@ -258,3 +258,77 @@ test('closure state lost by restart', async t => { t.is(three, 3); } }); + +test('persist import-unsafe0 services and their requests', async t => { + const { promise: cancelled } = makePromiseKit(); + const locator = makeLocator('tmp', 'import-unsafe0'); + + await stop(locator).catch(() => {}); + await reset(locator); + await start(locator); + + const inboxFinished = (async () => { + const { promise: followerCancelled, reject: cancelFollower } = + makePromiseKit(); + cancelled.catch(cancelFollower); + const { getBootstrap } = await makeEndoClient( + 'client', + locator.sockPath, + followerCancelled, + ); + const bootstrap = getBootstrap(); + const worker = await E(bootstrap).makeWorker('userWorker'); + await E(worker).evaluate( + ` + Far('Answer', { + value: () => 42, + }) + `, + [], + [], + 'answer', + ); + const iteratorRef = E(bootstrap).followInbox(); + const { value: message } = await E(iteratorRef).next(); + const { number } = E.get(message); + await E(bootstrap).resolve(await number, 'answer'); + })(); + + const workflowFinished = (async () => { + const { getBootstrap } = await makeEndoClient( + 'client', + locator.sockPath, + cancelled, + ); + const bootstrap = getBootstrap(); + const w1 = await E(bootstrap).makeWorker('w1'); + const servicePath = path.join(dirname, 'test', 'service.js'); + await E(w1).importUnsafe0(servicePath, 's1'); + + const w2 = await E(bootstrap).makeWorker('w2'); + const answer = await E(w2).evaluate( + 'E(service).ask()', + ['service'], + ['s1'], + 'answer', + ); + const number = await E(answer).value(); + t.is(number, 42); + })(); + + await Promise.all([inboxFinished, workflowFinished]); + + await restart(locator); + + { + const { getBootstrap } = await makeEndoClient( + 'client', + locator.sockPath, + cancelled, + ); + const bootstrap = getBootstrap(); + const answer = await E(bootstrap).provide('answer'); + const number = await E(answer).value(); + t.is(number, 42); + } +}); From 0492b6190840919c25b7dd8294b6fc060383b473 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Sat, 7 Jan 2023 16:27:29 -0800 Subject: [PATCH 032/234] feat(daemon): Support importBundle0 (confined) --- packages/daemon/package.json | 7 +++--- packages/daemon/src/daemon.js | 44 ++++++++++++++++++++++++++++++++++ packages/daemon/src/types.d.ts | 11 ++++++++- packages/daemon/src/worker.js | 13 ++++++++++ 4 files changed, 71 insertions(+), 4 deletions(-) diff --git a/packages/daemon/package.json b/packages/daemon/package.json index a76316ce58..46f242062f 100644 --- a/packages/daemon/package.json +++ b/packages/daemon/package.json @@ -46,11 +46,12 @@ "@endo/stream": "^0.3.28", "@endo/stream-node": "^0.2.29", "@endo/where": "^0.3.4", - "ses": "^0.18.7" + "ses": "^0.18.7", + "@endo/import-bundle": "^0.4.1" }, "devDependencies": { - "@endo/init": "^0.5.57", - "@endo/ses-ava": "^0.2.41", + "@endo/init": "^0.5.59", + "@endo/ses-ava": "^0.2.43", "ava": "^5.3.0", "babel-eslint": "^10.0.3", "c8": "^7.14.0", diff --git a/packages/daemon/src/daemon.js b/packages/daemon/src/daemon.js index f03e8132ef..aab45b2cba 100644 --- a/packages/daemon/src/daemon.js +++ b/packages/daemon/src/daemon.js @@ -324,6 +324,24 @@ const makeEndoBootstrap = ( // eslint-disable-next-line no-use-before-define return makeRef(ref, resultName); }, + + /** + * @param {string} readableBundleName + * @param {string} resultName + */ + importBundle0: async (readableBundleName, resultName) => { + const ref = { + /** @type {'importBundle0'} */ + type: 'importBundle0', + workerUuid, + // Behold, recursion: + // eslint-disable-next-line no-use-before-define + readableBundleRef: await readRef(readableBundleName), + }; + // Behold, recursion: + // eslint-disable-next-line no-use-before-define + return makeRef(ref, resultName); + }, }); workerBootstraps.set(worker, workerBootstrap); @@ -376,6 +394,20 @@ const makeEndoBootstrap = ( return E(workerBootstrap).importUnsafe0(importPath); }; + /** + * @param {string} workerUuid + * @param {import('./types.js').Ref} readableBundleRef + */ + const provideImportBundle0 = async (workerUuid, readableBundleRef) => { + const workerFacet = await provideWorkerUuid(workerUuid); + const workerBootstrap = workerBootstraps.get(workerFacet); + assert(workerBootstrap); + // Behold, recursion: + // eslint-disable-next-line no-use-before-define + const readableBundle = await provideRef(readableBundleRef); + return E(workerBootstrap).importBundle0(readableBundle); + }; + /** * @param {string} valueUuid */ @@ -426,6 +458,8 @@ const makeEndoBootstrap = ( return provideEval(ref.workerUuid, ref.source, ref.refs); } else if (ref.type === 'importUnsafe0') { return provideImportUnsafe0(ref.workerUuid, ref.importPath); + } else if (ref.type === 'importBundle0') { + return provideImportBundle0(ref.workerUuid, ref.readableBundleRef); } else { throw new TypeError(`Invalid reference: ${JSON.stringify(ref)}`); } @@ -551,6 +585,16 @@ const makeEndoBootstrap = ( return promise; }; + const readRef = petName => { + const petNamePath = powers.joinPath( + petNameDirectoryPath, + `${petName}.json`, + ); + return powers + .readFileText(petNamePath) + .then(petNameText => JSON.parse(petNameText)); + }; + const request = async (what, requestName, workerUuid) => { if (requestName !== undefined && workerUuid !== undefined) { const workerPetNameDirectoryPath = powers.joinPath( diff --git a/packages/daemon/src/types.d.ts b/packages/daemon/src/types.d.ts index 2e22248264..dd147cc7ed 100644 --- a/packages/daemon/src/types.d.ts +++ b/packages/daemon/src/types.d.ts @@ -94,12 +94,21 @@ type ImportUnsafe0Ref = { importPath: string; }; +type ImportBundle0Ref = { + type: 'importBundle0'; + workerUuid: string; + // Behold: recursion + // eslint-disable-next-line no-use-before-define + readableBundleRef: Ref; +}; + export type Ref = | ReadableSha512Ref | WorkerUuidRef | ValueUuid | EvalRef - | ImportUnsafe0Ref; + | ImportUnsafe0Ref + | ImportBundle0Ref; export type Label = { number: number; diff --git a/packages/daemon/src/worker.js b/packages/daemon/src/worker.js index b8872c92f0..13b53079c7 100644 --- a/packages/daemon/src/worker.js +++ b/packages/daemon/src/worker.js @@ -66,6 +66,19 @@ export const makeWorkerFacet = ({ const namespace = await import(url); return namespace.main0(powerBox); }, + + importBundle0: async readable => { + const bundleText = await E(readable).text(); + const bundle = JSON.parse(bundleText); + + // We defer importing the import-bundle machinery to this in order to + // avoid an up-front cost for workers that never use importBundle. + const { importBundle } = await import('@endo/import-bundle'); + const namespace = await importBundle(bundle, { + endowments, + }); + return namespace.main0(powerBox); + }, }); }; From 7cf294a7e59850254f5b0462615071de4ca42f43 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Sun, 8 Jan 2023 08:27:02 -0800 Subject: [PATCH 033/234] feat(cli): Add bundle command --- packages/cli/package.json | 1 + packages/cli/src/endo.js | 27 +++++++++++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/packages/cli/package.json b/packages/cli/package.json index f62f1c6483..291f86059a 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -34,6 +34,7 @@ "@endo/promise-kit": "^0.2.59", "@endo/stream-node": "^0.2.29", "@endo/where": "^0.3.4", + "@endo/bundle-source": "^2.7.0", "commander": "^5.0.0", "ses": "^0.18.7" }, diff --git a/packages/cli/src/endo.js b/packages/cli/src/endo.js index 6d95c230d5..78f562a65f 100644 --- a/packages/cli/src/endo.js +++ b/packages/cli/src/endo.js @@ -39,6 +39,7 @@ import { makeArchive, writeArchive, } from '@endo/compartment-mapper'; +import bundleSource from '@endo/bundle-source'; import { makeReadPowers, makeWritePowers, @@ -54,6 +55,8 @@ const packageDescriptorPath = url.fileURLToPath( new URL('../package.json', import.meta.url), ); +const textEncoder = new TextEncoder(); + const { username, homedir } = os.userInfo(); const temp = os.tmpdir(); const info = { @@ -271,6 +274,30 @@ export const main = async rawArgs => { } }); + program + .command('bundle ') + .option('-n,--name ', 'Store the bundle into Endo') + .action(async (applicationPath, cmd) => { + const bundleName = cmd.opts().name; + const bundle = await bundleSource(applicationPath); + console.log(bundle.endoZipBase64Sha512); + const bundleText = JSON.stringify(bundle); + const bundleBytes = textEncoder.encode(bundleText); + const readerRef = makeReaderRef([bundleBytes]); + const { getBootstrap } = await provideEndoClient( + 'cli', + sockPath, + cancelled, + ); + try { + const bootstrap = getBootstrap(); + await E(bootstrap).store(readerRef, bundleName); + } catch (error) { + console.error(error); + cancel(error); + } + }); + program .command('store ') .option( From c6ac0ab54bf015e1e2e6d9d50c7433e93e4ad88d Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Sun, 8 Jan 2023 08:29:01 -0800 Subject: [PATCH 034/234] feat(cli): Add import-bundle0 command --- packages/cli/src/endo.js | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/packages/cli/src/endo.js b/packages/cli/src/endo.js index 78f562a65f..425ebe13fd 100644 --- a/packages/cli/src/endo.js +++ b/packages/cli/src/endo.js @@ -535,6 +535,34 @@ export const main = async rawArgs => { } }); + program + .command('import-bundle0 ') + .option( + '-n,--name ', + 'Assigns a name to the result for future reference, persisted between restarts', + ) + .action(async (worker, readableBundleName, cmd) => { + const { name: resultName } = cmd.opts(); + const { getBootstrap } = await provideEndoClient( + 'cli', + sockPath, + cancelled, + ); + try { + const bootstrap = getBootstrap(); + const workerRef = E(bootstrap).provide(worker); + + const result = await E(workerRef).importBundle0( + readableBundleName, + resultName, + ); + console.log(result); + } catch (error) { + console.error(error); + cancel(error); + } + }); + // Throw an error instead of exiting directly. program.exitOverride(); From 1d4572e64b7a370f69f7efc1fc66a92b417172b8 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Tue, 31 Jan 2023 22:14:26 -0800 Subject: [PATCH 035/234] docs(cli): Descriptions for all subcommands --- packages/cli/src/endo.js | 328 ++++++++++++++++++++++++--------------- 1 file changed, 204 insertions(+), 124 deletions(-) diff --git a/packages/cli/src/endo.js b/packages/cli/src/endo.js index 425ebe13fd..b4336a7835 100644 --- a/packages/cli/src/endo.js +++ b/packages/cli/src/endo.js @@ -116,54 +116,87 @@ export const main = async rawArgs => { packageDescriptorPath, ); const packageDescriptor = JSON.parse(packageDescriptorBytes); - program.name(packageDescriptor.name).version(packageDescriptor.version); + program.name('endo').version(packageDescriptor.version); - const where = program.command('where'); + const where = program + .command('where') + .description('prints paths for state, logs, caches, socket, pids'); - where.command('state').action(async _cmd => { - process.stdout.write(`${statePath}\n`); - }); + where + .command('state') + .description('prints the state directory path') + .action(async _cmd => { + process.stdout.write(`${statePath}\n`); + }); - where.command('run').action(async _cmd => { - process.stdout.write(`${ephemeralStatePath}\n`); - }); + where + .command('run') + .description('prints the daemon PID file path') + .action(async _cmd => { + process.stdout.write(`${ephemeralStatePath}\n`); + }); - where.command('sock').action(async _cmd => { - process.stdout.write(`${sockPath}\n`); - }); + where + .command('sock') + .description('prints the UNIX domain socket or Windows named pipe path') + .action(async _cmd => { + process.stdout.write(`${sockPath}\n`); + }); - where.command('log').action(async _cmd => { - process.stdout.write(`${logPath}\n`); - }); + where + .command('log') + .description('prints the log file path') + .action(async _cmd => { + process.stdout.write(`${logPath}\n`); + }); - where.command('cache').action(async _cmd => { - process.stdout.write(`${cachePath}\n`); - }); + where + .command('cache') + .description('prints the cache directory path') + .action(async _cmd => { + process.stdout.write(`${cachePath}\n`); + }); - program.command('start').action(async _cmd => { - await start(); - }); + program + .command('start') + .description('start the endo daemon') + .action(async _cmd => { + await start(); + }); - program.command('stop').action(async _cmd => { - await stop(); - }); + program + .command('stop') + .description('stop the endo daemon') + .action(async _cmd => { + await stop(); + }); - program.command('restart').action(async _cmd => { - await restart(); - }); + program + .command('restart') + .description('stop and start the daemon') + .action(async _cmd => { + await restart(); + }); - program.command('clean').action(async _cmd => { - await clean(); - }); + program + .command('clean') + .description('erases ephemeral state') + .action(async _cmd => { + await clean(); + }); - program.command('reset').action(async _cmd => { - await reset(); - }); + program + .command('reset') + .description('erases persistent state and restarts if running') + .action(async _cmd => { + await reset(); + }); program .command('log') .option('-f, --follow', 'follow the tail of the log') .option('-p,--ping ', 'milliseconds between daemon reset checks') + .description('writes out the daemon log, optionally following updates') .action(async cmd => { const follow = cmd.opts().follow; const ping = cmd.opts().ping; @@ -206,42 +239,61 @@ export const main = async rawArgs => { } while (follow); }); - program.command('ping').action(async _cmd => { - const { getBootstrap } = await makeEndoClient( - 'health-checker', - sockPath, - cancelled, - ); - const bootstrap = getBootstrap(); - await E(bootstrap).ping(); - process.stderr.write('ok\n'); - }); + program + .command('ping') + .description('prints ok if the daemon is responsive') + .action(async _cmd => { + const { getBootstrap } = await makeEndoClient( + 'health-checker', + sockPath, + cancelled, + ); + const bootstrap = getBootstrap(); + await E(bootstrap).ping(); + process.stderr.write('ok\n'); + }); - program.command('map ').action(async applicationPath => { - const applicationLocation = url.pathToFileURL(applicationPath); - const compartmentMapBytes = await mapLocation( - readPowers, - applicationLocation, - ); - process.stdout.write(compartmentMapBytes); - }); + program + .command('map ') + .description( + 'prints a compartment-map.json for the path to an entry module path', + ) + .action(async (_cmd, [applicationPath]) => { + const applicationLocation = url.pathToFileURL(applicationPath); + const compartmentMapBytes = await mapLocation( + readPowers, + applicationLocation, + ); + process.stdout.write(compartmentMapBytes); + }); - program.command('hash ').action(async applicationPath => { - const applicationLocation = url.pathToFileURL(applicationPath); - const sha512 = await hashLocation(readPowers, applicationLocation); - process.stdout.write(`${sha512}\n`); - }); + program + .command('hash ') + .description( + 'prints the SHA-512 of the compartment-map.json for the path to an entry module path', + ) + .action(async (_cmd, [applicationPath]) => { + const applicationLocation = url.pathToFileURL(applicationPath); + const sha512 = await hashLocation(readPowers, applicationLocation); + process.stdout.write(`${sha512}\n`); + }); - program.command('hash-archive ').action(async archivePath => { - const archiveLocation = url.pathToFileURL(archivePath); - const { sha512 } = await loadArchive(readPowers, archiveLocation); - process.stdout.write(`${sha512}\n`); - }); + program + .command('hash-archive ') + .description( + 'prints the SHA-512 of the compartment-map.json of an archive generated from the entry module path', + ) + .action(async (_cmd, [archivePath]) => { + const archiveLocation = url.pathToFileURL(archivePath); + const { sha512 } = await loadArchive(readPowers, archiveLocation); + process.stdout.write(`${sha512}\n`); + }); program .command('archive ') .option('-n,--name ', 'Store the archive into Endo') .option('-f,--file ', 'Store the archive into a file') + .description('captures an archive from an entry module path') .action(async (applicationPath, cmd) => { const archiveName = cmd.opts().name; const archivePath = cmd.opts().file; @@ -277,6 +329,9 @@ export const main = async rawArgs => { program .command('bundle ') .option('-n,--name ', 'Store the bundle into Endo') + .description( + 'captures a JSON bundle containing an archive for an entry module path', + ) .action(async (applicationPath, cmd) => { const bundleName = cmd.opts().name; const bundle = await bundleSource(applicationPath); @@ -304,6 +359,7 @@ export const main = async rawArgs => { '-n,--name ', 'Assigns a pet name to the result for future reference', ) + .description('stores a readable file') .action(async (storablePath, cmd) => { const { name } = cmd.opts(); const nodeReadStream = fs.createReadStream(storablePath); @@ -324,80 +380,95 @@ export const main = async rawArgs => { } }); - program.command('spawn ').action(async name => { - const { getBootstrap } = await provideEndoClient( - 'cli', - sockPath, - cancelled, - ); - try { - const bootstrap = getBootstrap(); - await E(bootstrap).makeWorker(name); - } catch (error) { - console.error(error); - cancel(error); - } - }); + program + .command('spawn ') + .description('creates a worker for evaluating or importing programs') + .action(async name => { + const { getBootstrap } = await provideEndoClient( + 'cli', + sockPath, + cancelled, + ); + try { + const bootstrap = getBootstrap(); + await E(bootstrap).makeWorker(name); + } catch (error) { + console.error(error); + cancel(error); + } + }); - program.command('show ').action(async name => { - const { getBootstrap } = await provideEndoClient( - 'cli', - sockPath, - cancelled, - ); - try { - const bootstrap = getBootstrap(); - const pet = await E(bootstrap).provide(name); - console.log(pet); - } catch (error) { - console.error(error); - cancel(error); - } - }); + program + .command('show ') + .description('prints a representation of the named value') + .action(async name => { + const { getBootstrap } = await provideEndoClient( + 'cli', + sockPath, + cancelled, + ); + try { + const bootstrap = getBootstrap(); + const pet = await E(bootstrap).provide(name); + console.log(pet); + } catch (error) { + console.error(error); + cancel(error); + } + }); - program.command('follow ').action(async name => { - const { getBootstrap } = await provideEndoClient( - 'cli', - sockPath, - cancelled, - ); - try { - const bootstrap = getBootstrap(); - const iterable = await E(bootstrap).provide(name); - const iterator = await E(iterable)[Symbol.asyncIterator](); - for await (const iterand of makeRefIterator(iterator)) { - console.log(iterand); + program + .command('follow ') + .description( + 'prints a representation of each value from the named async iterable as it arrives', + ) + .action(async name => { + const { getBootstrap } = await provideEndoClient( + 'cli', + sockPath, + cancelled, + ); + try { + const bootstrap = getBootstrap(); + const iterable = await E(bootstrap).provide(name); + const iterator = await E(iterable)[Symbol.asyncIterator](); + for await (const iterand of makeRefIterator(iterator)) { + console.log(iterand); + } + } catch (error) { + console.error(error); + cancel(error); } - } catch (error) { - console.error(error); - cancel(error); - } - }); + }); - program.command('cat ').action(async name => { - const { getBootstrap } = await provideEndoClient( - 'cli', - sockPath, - cancelled, - ); - try { - const bootstrap = getBootstrap(); - const readable = await E(bootstrap).provide(name); - const readerRef = E(readable).stream(); - const reader = makeRefReader(readerRef); - for await (const chunk of reader) { - process.stdout.write(chunk); + program + .command('cat ') + .description('prints the content of the named readable file') + .action(async name => { + const { getBootstrap } = await provideEndoClient( + 'cli', + sockPath, + cancelled, + ); + try { + const bootstrap = getBootstrap(); + const readable = await E(bootstrap).provide(name); + const readerRef = E(readable).stream(); + const reader = makeRefReader(readerRef); + for await (const chunk of reader) { + process.stdout.write(chunk); + } + } catch (error) { + console.error(error); + cancel(error); } - } catch (error) { - console.error(error); - cancel(error); - } - }); + }); program .command('inbox') .option('-n,--name ', 'The name of an alternate inbox') .option('-f,--follow', 'Follow the inbox for messages as they arrive') + .description('prints pending requests that have been sent to you') .action(async cmd => { const { name: inboxName, follow } = cmd.opts(); const { getBootstrap } = await provideEndoClient( @@ -423,6 +494,7 @@ export const main = async rawArgs => { program .command('resolve ') + .description('responds to a pending request with the named value') .action(async (requestNumberText, resolutionName) => { // TODO less bad number parsing. const requestNumber = Number(requestNumberText); @@ -442,6 +514,7 @@ export const main = async rawArgs => { program .command('reject [message]') + .description('responds to a pending request with the rejection message') .action(async (requestNumberText, message) => { // TODO less bad number parsing. const requestNumber = Number(requestNumberText); @@ -465,6 +538,7 @@ export const main = async rawArgs => { '-n,--name ', 'Assigns a name to the result for future reference, persisted between restarts', ) + .description('evaluates a string with the endowed values in scope') .action(async (worker, source, names, cmd) => { const { name: resultName } = cmd.opts(); const { getBootstrap } = await provideEndoClient( @@ -513,6 +587,9 @@ export const main = async rawArgs => { '-n,--name ', 'Assigns a name to the result for future reference, persisted between restarts', ) + .description( + 'imports the module at the given path and runs its main0 function with all of your authority', + ) .action(async (worker, importPath, cmd) => { const { name: resultName } = cmd.opts(); const { getBootstrap } = await provideEndoClient( @@ -541,6 +618,9 @@ export const main = async rawArgs => { '-n,--name ', 'Assigns a name to the result for future reference, persisted between restarts', ) + .description( + 'imports the named bundle in a confined space within a worker and runs its main0 without any authority', + ) .action(async (worker, readableBundleName, cmd) => { const { name: resultName } = cmd.opts(); const { getBootstrap } = await provideEndoClient( From 4ca2779b01147b971b7fa8c4d9e2eaee333e005e Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Mon, 12 Jun 2023 15:58:54 -0700 Subject: [PATCH 036/234] feat(daemon): Formulas and Pet Stores --- packages/daemon/src/daemon-node-powers.js | 16 + packages/daemon/src/daemon.js | 634 ++++++++++++---------- packages/daemon/src/pet-store.js | 213 ++++++++ packages/daemon/src/types.d.ts | 61 +-- packages/daemon/test/test-endo.js | 4 +- 5 files changed, 591 insertions(+), 337 deletions(-) create mode 100644 packages/daemon/src/pet-store.js diff --git a/packages/daemon/src/daemon-node-powers.js b/packages/daemon/src/daemon-node-powers.js index 030ea6ba77..0a6b851dcc 100644 --- a/packages/daemon/src/daemon-node-powers.js +++ b/packages/daemon/src/daemon-node-powers.js @@ -116,6 +116,13 @@ export const makePowers = ({ crypto, net, fs, path: fspath, popen, url }) => { return fs.promises.readFile(path, 'utf-8'); }; + /** + * @param {string} path + */ + const readDirectory = async path => { + return fs.promises.readdir(path); + }; + /** * @param {string} path */ @@ -123,6 +130,13 @@ export const makePowers = ({ crypto, net, fs, path: fspath, popen, url }) => { await fs.promises.mkdir(path, { recursive: true }); }; + /** + * @param {string} path + */ + const removePath = async path => { + return fs.promises.rm(path); + }; + const renamePath = async (source, target) => fs.promises.rename(source, target); @@ -211,8 +225,10 @@ export const makePowers = ({ crypto, net, fs, path: fspath, popen, url }) => { makeFileWriter, writeFileText, readFileText, + readDirectory, makePath, joinPath, + removePath, renamePath, delay, makeWorker, diff --git a/packages/daemon/src/daemon.js b/packages/daemon/src/daemon.js index aab45b2cba..668a5016a4 100644 --- a/packages/daemon/src/daemon.js +++ b/packages/daemon/src/daemon.js @@ -13,6 +13,7 @@ import { makeChangeTopic } from './pubsub.js'; import { makeNetstringCapTP } from './connection.js'; import { makeRefReader } from './ref-reader.js'; import { makeReaderRef, makeIteratorRef } from './reader-ref.js'; +import { makeOwnPetStore, makeUuidPetStore } from './pet-store.js'; const { quote: q } = assert; @@ -33,9 +34,11 @@ const makeEndoBootstrap = ( { cancelled, cancel, gracePeriodMs, gracePeriodElapsed }, ) => { /** @type {Map} */ - const pets = new Map(); - /** @type {Map} */ - const values = new Map(); + const valuePromiseForFormulaIdentifier = new Map(); + // Reverse look-up, for answering "what is my name for this near or far + // reference", and not for "what is my name for this promise". + /** @type {WeakMap} */ + const formulaIdentifierForRef = new WeakMap(); const requests = new Map(); const resolvers = new WeakMap(); @@ -46,12 +49,12 @@ const makeEndoBootstrap = ( /** @type {WeakMap>} */ const workerBootstraps = new WeakMap(); - const petNameDirectoryPath = powers.joinPath(locator.statePath, 'pet-name'); + const petStoreP = makeOwnPetStore(powers, locator); /** * @param {string} sha512 */ - const makeReadableSha512 = sha512 => { + const makeSha512ReadableBlob = sha512 => { const storageDirectoryPath = powers.joinPath( locator.statePath, 'store-sha512', @@ -72,28 +75,14 @@ const makeEndoBootstrap = ( }); }; - /** - * @param {string} sha512 - */ - const provideReadableSha512 = sha512 => { - // TODO Contemplate using a different map for storage. - // For the moment, there's no risk of a UUID colliding with a SHA512. - let readable = values.get(sha512); - if (readable === undefined) { - readable = makeReadableSha512(sha512); - values.set(sha512, readable); - } - return readable; - }; - /** * @param {import('@endo/eventual-send').ERef>} readerRef - * @param {string} [name] + * @param {string} [petName] */ - const store = async (readerRef, name) => { - if (name !== undefined) { - if (!validNamePattern.test(name)) { - throw new Error(`Invalid pet name ${q(name)}`); + const store = async (readerRef, petName) => { + if (petName !== undefined) { + if (!validNamePattern.test(petName)) { + throw new Error(`Invalid pet name ${q(petName)}`); } } @@ -122,42 +111,68 @@ const makeEndoBootstrap = ( const sha512 = digester.digestHex(); // Retain the pet name first (to win a garbage collection race) - if (name !== undefined) { - await powers.makePath(petNameDirectoryPath); - const petNamePath = powers.joinPath(petNameDirectoryPath, `${name}.json`); - await powers.writeFileText( - petNamePath, - `${JSON.stringify({ - type: 'readableSha512', - readableSha512: sha512, - })}\n`, - ); + if (petName !== undefined) { + const formulaIdentifier = `readable-blob-sha512:${sha512}`; + await E(petStoreP).write(petName, formulaIdentifier); } // Finish with an atomic rename. const storagePath = powers.joinPath(storageDirectoryPath, sha512); await powers.renamePath(temporaryStoragePath, storagePath); - return makeReadableSha512(sha512); + return makeSha512ReadableBlob(sha512); }; /** * @param {string} workerUuid + * @param {string} workerFormulaIdentifier */ - const makeWorkerBootstrap = async workerUuid => { + const makeWorkerBootstrap = async (workerUuid, workerFormulaIdentifier) => { + // TODO validate workerUuid, workerFormulaIdentifier + + /** @type {Map>} */ + const responses = new Map(); + + // TODO, the petStore should be associated not with the worker but with the + // powers that were granted a specific program. + // There should not be a worker pet store, but a different Powers object + // for each bundle or some such, and a separate memo as well. + const workerPetStore = await makeUuidPetStore(powers, locator, workerUuid); + return Far(`Endo for worker ${workerUuid}`, { - request: async (what, resultName) => { - // Behold, recursion: - // eslint-disable-next-line no-use-before-define - return request(what, resultName, workerUuid); + request: async (what, responseName) => { + if (responseName === undefined) { + // Behold, recursion: + // eslint-disable-next-line no-use-before-define + return request( + what, + responseName, + workerFormulaIdentifier, + workerPetStore, + ); + } + let responseP = responses.get(responseName); + if (responseP === undefined) { + // Behold, recursion: + // eslint-disable-next-line no-use-before-define + responseP = request( + what, + responseName, + workerFormulaIdentifier, + workerPetStore, + ); + responses.set(responseName, responseP); + } + return responseP; }, }); }; /** * @param {string} workerUuid - * @param {string} [workerName] */ - const makeWorkerUuid = async (workerUuid, workerName) => { + const makeUuidWorker = async workerUuid => { + // TODO validate workerUuid + const workerFormulaIdentifier = `worker-uuid:${workerUuid}`; const workerCachePath = powers.joinPath( locator.cachePath, 'worker-uuid', @@ -179,21 +194,6 @@ const makeEndoBootstrap = ( powers.makePath(workerEphemeralStatePath), ]); - if (workerName !== undefined) { - await powers.makePath(petNameDirectoryPath); - const petNamePath = powers.joinPath( - petNameDirectoryPath, - `${workerName}.json`, - ); - await powers.writeFileText( - petNamePath, - `${JSON.stringify({ - type: 'workerUuid', - workerUuid, - })}\n`, - ); - } - const { reject: cancelWorker, promise: workerCancelled } = /** @type {import('@endo/promise-kit').PromiseKit} */ ( makePromiseKit() @@ -229,7 +229,7 @@ const makeEndoBootstrap = ( writer, reader, gracePeriodElapsed, - makeWorkerBootstrap(workerUuid), + makeWorkerBootstrap(workerUuid, workerFormulaIdentifier), ); const closed = Promise.race([workerClosed, capTpClosed]).finally(() => { @@ -270,7 +270,7 @@ const makeEndoBootstrap = ( * @param {string} resultName */ evaluate: async (source, codeNames, petNames, resultName) => { - if (!validNamePattern.test(resultName)) { + if (resultName !== undefined && !validNamePattern.test(resultName)) { throw new Error(`Invalid pet name ${q(resultName)}`); } if (petNames.length !== codeNames.length) { @@ -278,69 +278,70 @@ const makeEndoBootstrap = ( // TODO and they must all be strings. Use pattern language. } - await powers.makePath(petNameDirectoryPath); - const refs = Object.fromEntries( - await Promise.all( - petNames.map(async (endowmentPetName, index) => { - const endowmentCodeName = codeNames[index]; - const petNamePath = powers.joinPath( - petNameDirectoryPath, - `${endowmentPetName}.json`, - ); - const petNameText = await powers.readFileText(petNamePath); - try { - // TODO validate - /** @type {[string, import('./types.js').Ref]} */ - return [endowmentCodeName, JSON.parse(petNameText)]; - } catch (error) { - throw new TypeError( - `Corrupt pet name description for ${endowmentPetName}: ${error.message}`, - ); - } - }), - ), + const formulaIdentifiers = await Promise.all( + petNames.map(async petName => { + const formulaIdentifier = await E(petStoreP).get(petName); + if (formulaIdentifier === undefined) { + throw new Error(`Unknown pet name ${q(petName)}`); + } + return formulaIdentifier; + }), ); - const ref = { + const formula = { /** @type {'eval'} */ type: 'eval', - workerUuid, + worker: workerFormulaIdentifier, source, - refs, + names: codeNames, + values: formulaIdentifiers, }; + // Behold, recursion: // eslint-disable-next-line no-use-before-define - return makeRef(ref, resultName); + return provideValueForFormula(formula, 'eval-uuid', resultName); }, importUnsafe0: async (importPath, resultName) => { - const ref = { - /** @type {'importUnsafe0'} */ - type: 'importUnsafe0', - workerUuid, + const formula = { + /** @type {'import-unsafe0'} */ + type: 'import-unsafe0', + worker: workerFormulaIdentifier, importPath, }; + // Behold, recursion: // eslint-disable-next-line no-use-before-define - return makeRef(ref, resultName); + return provideValueForFormula( + formula, + 'import-unsafe0-uuid', + resultName, + ); }, /** - * @param {string} readableBundleName + * @param {string} bundleName * @param {string} resultName */ - importBundle0: async (readableBundleName, resultName) => { - const ref = { - /** @type {'importBundle0'} */ - type: 'importBundle0', - workerUuid, - // Behold, recursion: - // eslint-disable-next-line no-use-before-define - readableBundleRef: await readRef(readableBundleName), + importBundle0: async (bundleName, resultName) => { + const bundleFormulaIdentifier = await E(petStoreP).get(bundleName); + if (bundleFormulaIdentifier === undefined) { + throw new TypeError(`Unknown pet name for bundle: ${bundleName}`); + } + const formula = { + /** @type {'import-bundle0'} */ + type: 'import-bundle0', + worker: workerFormulaIdentifier, + bundle: bundleFormulaIdentifier, }; + // Behold, recursion: // eslint-disable-next-line no-use-before-define - return makeRef(ref, resultName); + return provideValueForFormula( + formula, + 'import-bundle0-uuid', + resultName, + ); }, }); @@ -350,206 +351,252 @@ const makeEndoBootstrap = ( }; /** - * @param {string} workerUuid - * @param {string} [name] - */ - const provideWorkerUuid = async (workerUuid, name) => { - let worker = - /** @type {import('@endo/eventual-send').ERef>} */ ( - values.get(workerUuid) - ); - if (worker === undefined) { - worker = makeWorkerUuid(workerUuid, name); - values.set(workerUuid, worker); - } - return worker; - }; - - /** - * @param {string} workerUuid + * @param {string} workerFormulaIdentifier * @param {string} source - * @param {Record} refs + * @param {Array} codeNames + * @param {Array} formulaIdentifiers */ - const provideEval = async (workerUuid, source, refs) => { - const workerFacet = await provideWorkerUuid(workerUuid); + const makeValueForEval = async ( + workerFormulaIdentifier, + source, + codeNames, + formulaIdentifiers, + ) => { + // Behold, recursion: + // eslint-disable-next-line no-use-before-define + const workerFacet = await provideValueForFormulaIdentifier( + workerFormulaIdentifier, + ); + // TODO consider a better mechanism for hiding the private facet. + // Maybe all these internal functions should return { public, private } + // duples. const workerBootstrap = workerBootstraps.get(workerFacet); assert(workerBootstrap); - const codeNames = Object.keys(refs); const endowmentValues = await Promise.all( - // Behold, recursion: - // eslint-disable-next-line no-use-before-define - Object.values(refs).map(ref => provideRef(ref)), + formulaIdentifiers.map(formulaIdentifier => + // Behold, recursion: + // eslint-disable-next-line no-use-before-define + provideValueForFormulaIdentifier(formulaIdentifier), + ), ); return E(workerBootstrap).evaluate(source, codeNames, endowmentValues); }; /** - * @param {string} workerUuid + * @param {string} workerFormulaIdentifier * @param {string} importPath */ - const provideImportUnsafe0 = async (workerUuid, importPath) => { - const workerFacet = await provideWorkerUuid(workerUuid); + const makeValueForImportUnsafe0 = async ( + workerFormulaIdentifier, + importPath, + ) => { + // Behold, recursion: + // eslint-disable-next-line no-use-before-define + const workerFacet = await provideValueForFormulaIdentifier( + workerFormulaIdentifier, + ); const workerBootstrap = workerBootstraps.get(workerFacet); assert(workerBootstrap); return E(workerBootstrap).importUnsafe0(importPath); }; /** - * @param {string} workerUuid - * @param {import('./types.js').Ref} readableBundleRef + * @param {string} workerFormulaIdentifier + * @param {string} bundleFormulaIdentifier */ - const provideImportBundle0 = async (workerUuid, readableBundleRef) => { - const workerFacet = await provideWorkerUuid(workerUuid); + const makeValueForImportBundle0 = async ( + workerFormulaIdentifier, + bundleFormulaIdentifier, + ) => { + // Behold, recursion: + // eslint-disable-next-line no-use-before-define + const workerFacet = await provideValueForFormulaIdentifier( + workerFormulaIdentifier, + ); const workerBootstrap = workerBootstraps.get(workerFacet); assert(workerBootstrap); // Behold, recursion: // eslint-disable-next-line no-use-before-define - const readableBundle = await provideRef(readableBundleRef); + const readableBundle = await provideValueForFormulaIdentifier( + bundleFormulaIdentifier, + ); return E(workerBootstrap).importBundle0(readableBundle); }; /** - * @param {string} valueUuid + * @param {import('./types.js').Formula} formula */ - const makeValueUuid = async valueUuid => { - const valuesDirectoryPath = powers.joinPath( + const makeValueForFormula = async formula => { + if (formula.type === 'eval') { + return makeValueForEval( + formula.worker, + formula.source, + formula.names, + formula.values, + ); + } else if (formula.type === 'import-unsafe0') { + return makeValueForImportUnsafe0(formula.worker, formula.importPath); + } else if (formula.type === 'import-bundle0') { + return makeValueForImportBundle0(formula.worker, formula.bundle); + } else { + throw new TypeError(`Invalid formula: ${q(formula)}`); + } + }; + + /** + * @param {string} formulaType + * @param {string} formulaUuid + */ + const makeFormulaPath = (formulaType, formulaUuid) => { + if (formulaUuid.length < 3) { + throw new TypeError( + `Invalid formula identifier, unrecognized uuid ${q( + formulaUuid, + )} for formula of type ${q(formulaType)}`, + ); + } + const head = formulaUuid.slice(0, 2); + const tail = formulaUuid.slice(3); + const directory = powers.joinPath( locator.statePath, - 'value-uuid', + 'formulas', + `${formulaType}-uuid`, + head, ); - const valuePath = powers.joinPath(valuesDirectoryPath, `${valueUuid}.json`); - const refText = await powers.readFileText(valuePath); - const ref = (() => { + const file = powers.joinPath(directory, `${tail}.json`); + return { directory, file }; + }; + + // Persist instructions for revival (this can be collected) + const writeFormula = async (formula, formulaType, formulaUuid) => { + const { directory, file } = makeFormulaPath(formulaType, formulaUuid); + await powers.makePath(directory); + await powers.writeFileText(file, `${q(formula)}\n`); + }; + + /** + * @param {string} formulaPath + */ + const makeValueForFormulaAtPath = async formulaPath => { + const formulaText = await powers.readFileText(formulaPath).catch(() => { + // TODO handle EMFILE gracefully + throw new ReferenceError(`No reference exists at path ${formulaPath}`); + }); + const formula = (() => { try { - return JSON.parse(refText); + return JSON.parse(formulaText); } catch (error) { throw new TypeError( - `Corrupt description for value to be derived according to file ${valuePath}: ${error.message}`, + `Corrupt description for reference in file ${formulaPath}: ${error.message}`, ); } })(); - // Behold, recursion: - // eslint-disable-next-line no-use-before-define - return provideRef(ref); + // TODO validate + return makeValueForFormula(formula); }; /** - * @param {string} valueUuid + * @param {string} formulaIdentifier */ - const provideValueUuid = async valueUuid => { - let value = values.get(valueUuid); - if (value === undefined) { - value = makeValueUuid(valueUuid); - values.set(valueUuid, value); + const makeValueForFormulaIdentifier = async formulaIdentifier => { + const delimiterIndex = formulaIdentifier.indexOf(':'); + if (delimiterIndex < 0) { + throw new TypeError( + `Formula identifier must have a colon: ${q(formulaIdentifier)}`, + ); } - return value; - }; - - /** - * @param {import('./types.js').Ref} ref - */ - const provideRef = async ref => { - if (ref.type === 'readableSha512') { - return provideReadableSha512(ref.readableSha512); - } else if (ref.type === 'workerUuid') { - return provideWorkerUuid(ref.workerUuid); - } else if (ref.type === 'valueUuid') { - return provideValueUuid(ref.valueUuid); - } else if (ref.type === 'eval') { - return provideEval(ref.workerUuid, ref.source, ref.refs); - } else if (ref.type === 'importUnsafe0') { - return provideImportUnsafe0(ref.workerUuid, ref.importPath); - } else if (ref.type === 'importBundle0') { - return provideImportBundle0(ref.workerUuid, ref.readableBundleRef); + const prefix = formulaIdentifier.slice(0, delimiterIndex); + const suffix = formulaIdentifier.slice(delimiterIndex + 1); + if (prefix === 'readable-blob-sha512') { + return makeSha512ReadableBlob(suffix); + } else if (prefix === 'worker-uuid') { + return makeUuidWorker(suffix); + } else if (prefix === 'pet-store-uuid') { + return makeUuidPetStore(powers, locator, suffix); + } else if ( + ['eval-uuid', 'import-unsafe0-uuid', 'import-bundle0-uuid'].includes( + prefix, + ) + ) { + const { file: path } = makeFormulaPath(prefix, suffix); + return makeValueForFormulaAtPath(path); } else { - throw new TypeError(`Invalid reference: ${JSON.stringify(ref)}`); + throw new TypeError( + `Invalid formula identifier, unrecognized type ${q(formulaIdentifier)}`, + ); } }; + // The two functions provideValueForFormula and provideValueForFormulaIdentifier + // share a responsibility for maintaining the memoization tables + // valuePromiseForFormulaIdentifier and formulaIdentifierForRef, since the + // former bypasses the latter in order to avoid a round trip with disk. + /** - * @param {import('./types.js').Ref} ref - * @param {string} [petName] + * @param {import('./types.js').Formula} formula + * @param {string} formulaType + * @param {string} [resultName] */ - const makeRef = async (ref, petName) => { - const value = await provideRef(ref); - if (petName !== undefined) { - const valueUuid = powers.randomUuid(); + const provideValueForFormula = async (formula, formulaType, resultName) => { + const formulaUuid = powers.randomUuid(); + const formulaIdentifier = `${formulaType}:${formulaUuid}`; + await writeFormula(formula, formulaType, formulaUuid); + // Behold, recursion: + // eslint-disable-next-line no-use-before-define + const promiseForValue = makeValueForFormula(formula); - // Persist instructions for revival (this can be collected) - const valuesDirectoryPath = powers.joinPath( - locator.statePath, - 'value-uuid', - ); - await powers.makePath(valuesDirectoryPath); - const valuePath = powers.joinPath( - valuesDirectoryPath, - `${valueUuid}.json`, - ); - await powers.writeFileText(valuePath, `${JSON.stringify(ref)}\n`); + // Memoize provide. + valuePromiseForFormulaIdentifier.set(formulaIdentifier, promiseForValue); - // Make a reference by pet name (this can be overwritten) - await powers.makePath(petNameDirectoryPath); - const petNamePath = powers.joinPath( - petNameDirectoryPath, - `${petName}.json`, - ); - await powers.writeFileText( - petNamePath, - `${JSON.stringify({ - type: 'valueUuid', - valueUuid, - })}\n`, - ); + // Prepare an entry for forward-lookup of formula for pet name. + if (resultName !== undefined) { + await E(petStoreP).write(resultName, formulaIdentifier); + } - values.set(valueUuid, value); + // Prepare an entry for reverse-lookup of formula for presence. + const value = await promiseForValue; + if (typeof value === 'object' && value !== null) { + formulaIdentifierForRef.set(value, formulaIdentifier); } return value; }; /** - * @param {string} refPath + * @param {string} formulaIdentifier */ - const provideRefPath = async refPath => { - const refText = await powers.readFileText(refPath).catch(() => { - // TODO handle EMFILE gracefully - throw new ReferenceError(`No reference exists at path ${refPath}`); - }); - const ref = (() => { - try { - return JSON.parse(refText); - } catch (error) { - throw new TypeError( - `Corrupt description for reference in file ${refPath}: ${error.message}`, - ); - } - })(); - // TODO validate - return provideRef(ref); - }; - - /** - * @param {string} name - */ - const revive = async name => { - const petNamePath = powers.joinPath(petNameDirectoryPath, `${name}.json`); - return provideRefPath(petNamePath).catch(error => { - throw new Error(`Corrupt pet name ${name}: ${error.message}`); - }); + const provideValueForFormulaIdentifier = async formulaIdentifier => { + let promiseForValue = + valuePromiseForFormulaIdentifier.get(formulaIdentifier); + if (promiseForValue === undefined) { + promiseForValue = makeValueForFormulaIdentifier(formulaIdentifier); + valuePromiseForFormulaIdentifier.set(formulaIdentifier, promiseForValue); + } + const value = await promiseForValue; + if (typeof value === 'object' && value !== null) { + formulaIdentifierForRef.set(value, formulaIdentifier); + } + return value; }; /** - * @param {string} name + * @param {string} petName */ - const provide = async name => { - if (!validNamePattern.test(name)) { - throw new Error(`Invalid pet name ${q(name)}`); + const provide = async petName => { + const formulaIdentifier = await E(petStoreP).get(petName); + if (formulaIdentifier === undefined) { + throw new TypeError(`Unknown pet name: ${q(petName)}`); } + return provideValueForFormulaIdentifier(formulaIdentifier); + }; - let pet = pets.get(name); - if (pet === undefined) { - pet = revive(name); - pets.set(name, pet); + const makePetStore = async petName => { + // @ts-ignore Node.js crypto does in fact have randomUUID. + const petStoreUuid = powers.randomUuid(); + const formulaIdentifier = `pet-store-uuid:${petStoreUuid}`; + if (petName !== undefined) { + await E(petStoreP).write(petName, formulaIdentifier); } - return pet; + return provideValueForFormulaIdentifier(formulaIdentifier); }; const inbox = async () => makeIteratorRef(requests.values()); @@ -563,7 +610,12 @@ const makeEndoBootstrap = ( })(), ); - const requestRef = async (workerUuid, what) => { + /** + * @param {string} what - user visible description of the desired value + * @param {string} who - formula identifier of the requester + */ + const requestFormulaIdentifier = async (what, who) => { + /** @type {import('@endo/promise-kit/src/types.js').PromiseKit} */ const { promise, resolve } = makePromiseKit(); const requestNumber = nextRequestNumber; nextRequestNumber += 1; @@ -574,7 +626,7 @@ const makeEndoBootstrap = ( const req = harden({ type: /** @type {'request'} */ ('request'), number: requestNumber, - who: workerUuid, + who, what, when: new Date().toISOString(), settled, @@ -585,49 +637,36 @@ const makeEndoBootstrap = ( return promise; }; - const readRef = petName => { - const petNamePath = powers.joinPath( - petNameDirectoryPath, - `${petName}.json`, - ); - return powers - .readFileText(petNamePath) - .then(petNameText => JSON.parse(petNameText)); - }; - - const request = async (what, requestName, workerUuid) => { - if (requestName !== undefined && workerUuid !== undefined) { - const workerPetNameDirectoryPath = powers.joinPath( - locator.statePath, - 'worker-uuid', - workerUuid, - 'pet-name', - ); - const petNamePath = powers.joinPath( - workerPetNameDirectoryPath, - `${requestName}.json`, - ); - return powers.readFileText(petNamePath).then( - async petNameText => { - // The named reference exists, just restore. - // TODO validate - /** @type {import('./types.js').Ref} */ - const ref = JSON.parse(petNameText); - return provideRef(ref); - }, - async () => { - // The named reference hasn't been created, so create and record. - const ref = await requestRef(workerUuid, what); - const newPetNameText = `${JSON.stringify(ref)}\n`; - await powers.makePath(workerPetNameDirectoryPath); - await powers.writeFileText(petNamePath, newPetNameText); - return provideRef(ref); - }, - ); + /** + * @param {string} what + * @param {string} responseName + * @param {string} fromFormulaIdentifier + * @param {import('./types.js').PetStore} workerPetStore + */ + const request = async ( + what, + responseName, + fromFormulaIdentifier, + workerPetStore, + ) => { + if (responseName !== undefined) { + /** @type {string | undefined} */ + let formulaIdentifier = workerPetStore.get(responseName); + if (formulaIdentifier === undefined) { + formulaIdentifier = await requestFormulaIdentifier( + what, + fromFormulaIdentifier, + ); + await workerPetStore.write(responseName, formulaIdentifier); + } + return provideValueForFormulaIdentifier(formulaIdentifier); } // The reference is not named nor to be named. - const ref = await requestRef(what); - return provideRef(ref); + const formulaIdentifier = await requestFormulaIdentifier( + what, + fromFormulaIdentifier, + ); + return provideValueForFormulaIdentifier(formulaIdentifier); }; const resolve = async (requestNumber, resolutionName) => { @@ -636,24 +675,16 @@ const makeEndoBootstrap = ( } const req = requests.get(requestNumber); const resolveRequest = resolvers.get(req); - if (resolveRequest !== undefined) { - const petNamePath = powers.joinPath( - petNameDirectoryPath, - `${resolutionName}.json`, + if (resolveRequest === undefined) { + throw new Error(`No pending request for number ${requestNumber}`); + } + const formulaIdentifier = await E(petStoreP).get(resolutionName); + if (formulaIdentifier === undefined) { + throw new TypeError( + `No formula exists for the pet name ${q(resolutionName)}`, ); - const refText = await powers.readFileText(petNamePath); - const ref = (() => { - try { - return JSON.parse(refText); - } catch (cause) { - throw new TypeError( - `Corrupt pet name ${resolutionName}: ${cause.message}`, - { cause }, - ); - } - })(); - resolveRequest(ref); } + resolveRequest(formulaIdentifier); }; const reject = async (requestNumber, message = 'Declined') => { @@ -673,14 +704,21 @@ const makeEndoBootstrap = ( }, /** - * @param {string} [name] + * @param {string} [petName] */ - makeWorker: async name => { + makeWorker: async petName => { // @ts-ignore Node.js crypto does in fact have randomUUID. const workerUuid = powers.randomUuid(); - return provideWorkerUuid(workerUuid, name); + const formulaIdentifier = `worker-uuid:${workerUuid}`; + if (petName !== undefined) { + await E(petStoreP).write(petName, formulaIdentifier); + } + return provideValueForFormulaIdentifier(formulaIdentifier); }, + petStore: () => petStoreP, + makePetStore, + store, provide, inbox, diff --git a/packages/daemon/src/pet-store.js b/packages/daemon/src/pet-store.js new file mode 100644 index 0000000000..fa1bc2d67c --- /dev/null +++ b/packages/daemon/src/pet-store.js @@ -0,0 +1,213 @@ +import { Far } from '@endo/far'; + +const { quote: q } = assert; + +const validNamePattern = /^[a-zA-Z][a-zA-Z0-9]{0,127}$/; +const validUuidPattern = + /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/; +const validFormulaPattern = + /^(?:readable-blob-sha512:[0-9a-f]{128}|(?:worker-uuid|pet-store-uuid|eval-uuid|import-unsafe0-uuid|import-bundle0-uuid):(?:[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}))$/; + +/** + * @param {import('./types.js').DaemonicPowers} powers + * @param {string} petNameDirectoryPath + */ +const makePetStoreAtPath = async (powers, petNameDirectoryPath) => { + /** @type {Map} */ + const petNames = new Map(); + /** @type {Map>} */ + const formulaIdentifiers = new Map(); + + /** @param {string} petName */ + const read = async petName => { + const petNamePath = powers.joinPath(petNameDirectoryPath, petName); + const petNameText = await powers.readFileText(petNamePath); + const formulaIdentifier = petNameText.trim(); + if (!validFormulaPattern.test(formulaIdentifier)) { + throw new Error( + `Invalid formula identifier ${q(formulaIdentifier)} for pet name ${q( + petName, + )}`, + ); + } + return formulaIdentifier; + }; + + await powers.makePath(petNameDirectoryPath); + + const fileNames = await powers.readDirectory(petNameDirectoryPath); + await Promise.all( + fileNames.map(async petName => { + if (!validNamePattern.test(petName)) { + throw new Error(`Invalid pet name ${q(petName)}`); + } + const formulaIdentifier = await read(petName); + petNames.set(petName, formulaIdentifier); + const formulaPetNames = formulaIdentifiers.get(formulaIdentifier); + if (formulaPetNames !== undefined) { + formulaPetNames.add(petName); + } else { + formulaIdentifiers.set(formulaIdentifier, new Set([petName])); + } + }), + ); + + /** @param {string} petName */ + const get = petName => { + if (!validNamePattern.test(petName)) { + throw new Error(`Invalid pet name ${q(petName)}`); + } + return petNames.get(petName); + }; + + /** + * @param {string} petName + * @param {string} formulaIdentifier + */ + const write = async (petName, formulaIdentifier) => { + if (!validNamePattern.test(petName)) { + throw new Error(`Invalid pet name ${q(petName)}`); + } + if (!validFormulaPattern.test(formulaIdentifier)) { + throw new Error(`Invalid formula identifier ${q(formulaIdentifier)}`); + } + petNames.set(petName, formulaIdentifier); + const petNamePath = powers.joinPath(petNameDirectoryPath, petName); + const petNameText = `${formulaIdentifier}\n`; + await powers.writeFileText(petNamePath, petNameText); + }; + + const list = () => harden([...petNames.keys()].sort()); + + /** + * @param {string} petName + */ + const remove = async petName => { + if (!validNamePattern.test(petName)) { + throw new Error(`Invalid pet name ${q(petName)}`); + } + const formulaIdentifier = petNames.get(petName); + if (formulaIdentifier === undefined) { + throw new Error( + `Formula does not exist for pet name ${JSON.stringify(petName)}`, + ); + } + if (!validFormulaPattern.test(formulaIdentifier)) { + throw new Error(`Invalid formula identifier ${q(formulaIdentifier)}`); + } + + const petNamePath = powers.joinPath(petNameDirectoryPath, petName); + await powers.removePath(petNamePath); + petNames.delete(petName); + const formulaPetNames = formulaIdentifiers.get(petName); + if (formulaPetNames !== undefined) { + formulaPetNames.delete(petName); + } + // TODO consider retaining a backlog of deleted names for recovery + // TODO consider tracking historical pet names for formulas + }; + + /** + * @param {string} fromName + * @param {string} toName + */ + const rename = async (fromName, toName) => { + if (!validNamePattern.test(fromName)) { + throw new Error(`Invalid pet name ${q(fromName)}`); + } + if (!validNamePattern.test(toName)) { + throw new Error(`Invalid pet name ${q(toName)}`); + } + if (fromName === toName) { + return; + } + const formulaIdentifier = petNames.get(fromName); + const overwrittenFormulaIdentifier = petNames.get(toName); + if (formulaIdentifier === undefined) { + throw new Error( + `Formula does not exist for pet name ${JSON.stringify(fromName)}`, + ); + } + if (!validFormulaPattern.test(formulaIdentifier)) { + throw new Error(`Invalid formula identifier ${q(formulaIdentifier)}`); + } + if ( + overwrittenFormulaIdentifier !== undefined && + !validFormulaPattern.test(overwrittenFormulaIdentifier) + ) { + throw new Error( + `Invalid formula identifier ${q(overwrittenFormulaIdentifier)}`, + ); + } + + const fromPath = powers.joinPath(petNameDirectoryPath, fromName); + const toPath = powers.joinPath(petNameDirectoryPath, toName); + await powers.renamePath(fromPath, toPath); + petNames.set(toName, formulaIdentifier); + petNames.delete(fromName); + + // Delete the back-reference for the overwritten pet name if it existed. + if (overwrittenFormulaIdentifier !== undefined) { + const overwrittenFormulaPetNames = formulaIdentifiers.get( + overwrittenFormulaIdentifier, + ); + if (overwrittenFormulaPetNames !== undefined) { + overwrittenFormulaPetNames.delete(toName); + } + } + + // Change the back-reference for the old pet name. + const formulaPetNames = formulaIdentifiers.get(formulaIdentifier); + if (formulaPetNames !== undefined) { + formulaPetNames.delete(fromName); + formulaPetNames.add(toName); + } + + // TODO consider retaining a backlog of overwritten names for recovery + }; + + /** + * @param {string} formulaIdentifier + */ + const lookup = formulaIdentifier => { + if (!validFormulaPattern.test(formulaIdentifier)) { + throw new Error(`Invalid formula identifier ${q(formulaIdentifier)}`); + } + const formulaPetNames = formulaIdentifiers.get(formulaIdentifier); + if (formulaPetNames === undefined) { + return harden([]); + } + return harden([...formulaPetNames]); + }; + + return Far('PetStore', { get, write, list, remove, rename, lookup }); +}; + +/** + * @param {import('./types.js').DaemonicPowers} powers + * @param {import('./types.js').Locator} locator + * @param {string} uuid + */ +export const makeUuidPetStore = (powers, locator, uuid) => { + if (!validUuidPattern.test(uuid)) { + throw new Error(`Invalid UUID for pet store ${q(uuid)}`); + } + const prefix = uuid.slice(0, 2); + const suffix = uuid.slice(3); + const petNameDirectoryPath = powers.joinPath( + locator.statePath, + 'pet-store-uuid', + prefix, + suffix, + ); + return makePetStoreAtPath(powers, petNameDirectoryPath); +}; + +/** + * @param {import('./types.js').DaemonicPowers} powers + * @param {import('./types.js').Locator} locator + */ +export const makeOwnPetStore = (powers, locator) => { + const petNameDirectoryPath = powers.joinPath(locator.statePath, 'pet-store'); + return makePetStoreAtPath(powers, petNameDirectoryPath); +}; diff --git a/packages/daemon/src/types.d.ts b/packages/daemon/src/types.d.ts index dd147cc7ed..104ae0eb09 100644 --- a/packages/daemon/src/types.d.ts +++ b/packages/daemon/src/types.d.ts @@ -35,9 +35,11 @@ export type DaemonicPowers = { makeFileReader: (path: string) => Reader; makeFileWriter: (path: string) => Writer; readFileText: (path: string) => Promise; + readDirectory: (path: string) => Promise>; writeFileText: (path: string, text: string) => Promise; makePath: (path: string) => Promise; renamePath: (source: string, target: string) => Promise; + removePath: (path: string) => Promise; joinPath: (...components: Array) => string; delay: (ms: number, cancelled: Promise) => Promise; makeWorker: ( @@ -63,52 +65,30 @@ export type MignonicPowers = { }; }; -type ReadableSha512Ref = { - type: 'readableSha512'; - readableSha512: string; -}; - -type WorkerUuidRef = { - type: 'workerUuid'; - workerUuid: string; -}; - -// Reference to a reference. -type ValueUuid = { - type: 'valueUuid'; - valueUuid: string; -}; - -type EvalRef = { +type EvalFormula = { type: 'eval'; - workerUuid: string; + worker: string; source: string; - // Behold: recursion - // eslint-disable-next-line no-use-before-define - refs: Record; + names: Array; // lexical names + values: Array; // formula identifiers + // TODO formula slots }; -type ImportUnsafe0Ref = { - type: 'importUnsafe0'; - workerUuid: string; +type ImportUnsafe0Formula = { + type: 'import-unsafe0'; + worker: string; importPath: string; + // TODO formula slots }; -type ImportBundle0Ref = { - type: 'importBundle0'; - workerUuid: string; - // Behold: recursion - // eslint-disable-next-line no-use-before-define - readableBundleRef: Ref; +type ImportBundle0Formula = { + type: 'import-bundle0'; + worker: string; + bundle: string; + // TODO formula slots }; -export type Ref = - | ReadableSha512Ref - | WorkerUuidRef - | ValueUuid - | EvalRef - | ImportUnsafe0Ref - | ImportBundle0Ref; +export type Formula = EvalFormula | ImportUnsafe0Formula | ImportBundle0Formula; export type Label = { number: number; @@ -133,3 +113,10 @@ export interface Topic< publisher: Stream; subscribe(): Stream; } + +export interface PetStore { + get(petName: string): string | undefined; + write(petName: string, formulaIdentifier: string): Promise; + list(): Array; + lookup(formulaIdentifier: string): Array; +} diff --git a/packages/daemon/test/test-endo.js b/packages/daemon/test/test-endo.js index 0d98e4140f..06e92359db 100644 --- a/packages/daemon/test/test-endo.js +++ b/packages/daemon/test/test-endo.js @@ -286,12 +286,12 @@ test('persist import-unsafe0 services and their requests', async t => { `, [], [], - 'answer', + 'grant', ); const iteratorRef = E(bootstrap).followInbox(); const { value: message } = await E(iteratorRef).next(); const { number } = E.get(message); - await E(bootstrap).resolve(await number, 'answer'); + await E(bootstrap).resolve(await number, 'grant'); })(); const workflowFinished = (async () => { From 102ec83aecfb7efef06a01a3f510c723d6935008 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Wed, 14 Jun 2023 15:36:31 -0700 Subject: [PATCH 037/234] feat(cli): Add list pet names command --- packages/cli/src/endo.js | 94 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) diff --git a/packages/cli/src/endo.js b/packages/cli/src/endo.js index b4336a7835..6f3ebe2bee 100644 --- a/packages/cli/src/endo.js +++ b/packages/cli/src/endo.js @@ -464,6 +464,100 @@ export const main = async rawArgs => { } }); + program + .command('list') + .option('-n,--name ', 'The name of an alternate pet store') + .description('lists pet names') + .action(async cmd => { + const { name: petStoreName } = cmd.opts(); + const { getBootstrap } = await provideEndoClient( + 'cli', + sockPath, + cancelled, + ); + try { + const bootstrap = getBootstrap(); + const petStore = + petStoreName === undefined + ? E(bootstrap).petStore() + : E(bootstrap).provide(petStoreName); + const petNames = await E(petStore).list(); + for await (const petName of petNames) { + console.log(petName); + } + } catch (error) { + console.error(error); + cancel(error); + } + }); + + program + .command('remove [names...]') + .description('removes pet names') + .option('-n,--name ', 'The name of an alternate pet store') + .action(async (petNames, cmd) => { + const { name: petStoreName } = cmd.opts(); + const { getBootstrap } = await provideEndoClient( + 'cli', + sockPath, + cancelled, + ); + try { + const bootstrap = getBootstrap(); + const petStore = + petStoreName === undefined + ? E(bootstrap).petStore() + : E(bootstrap).provide(petStoreName); + await Promise.all(petNames.map(petName => E(petStore).remove(petName))); + } catch (error) { + console.error(error); + cancel(error); + } + }); + + program + .command('rename ') + .description('renames a value') + .option('-n,--name ', 'The name of an alternate pet store') + .action(async (fromName, toName, cmd) => { + const { name: petStoreName } = cmd.opts(); + const { getBootstrap } = await provideEndoClient( + 'cli', + sockPath, + cancelled, + ); + try { + const bootstrap = getBootstrap(); + const petStore = + petStoreName === undefined + ? E(bootstrap).petStore() + : E(bootstrap).provide(petStoreName); + await E(petStore).rename(fromName, toName); + } catch (error) { + console.error(error); + cancel(error); + } + }); + + program + .command('make-pet-store ') + .description('creates a new pet store') + .action(async name => { + const { getBootstrap } = await provideEndoClient( + 'cli', + sockPath, + cancelled, + ); + try { + const bootstrap = getBootstrap(); + const petStore = await E(bootstrap).makePetStore(name); + console.log(petStore); + } catch (error) { + console.error(error); + cancel(error); + } + }); + program .command('inbox') .option('-n,--name ', 'The name of an alternate inbox') From c9f1fe8b3f1fa54117b6062bd98e1aab88c26e93 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Wed, 14 Jun 2023 16:53:25 -0700 Subject: [PATCH 038/234] feat(cli): Spawn for multiple workers --- packages/cli/src/endo.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/cli/src/endo.js b/packages/cli/src/endo.js index 6f3ebe2bee..9c02c14ac4 100644 --- a/packages/cli/src/endo.js +++ b/packages/cli/src/endo.js @@ -381,9 +381,9 @@ export const main = async rawArgs => { }); program - .command('spawn ') - .description('creates a worker for evaluating or importing programs') - .action(async name => { + .command('spawn [names...]') + .description('creates workers for evaluating or importing programs') + .action(async petNames => { const { getBootstrap } = await provideEndoClient( 'cli', sockPath, @@ -391,7 +391,9 @@ export const main = async rawArgs => { ); try { const bootstrap = getBootstrap(); - await E(bootstrap).makeWorker(name); + for (const petName of petNames) { + await E(bootstrap).makeWorker(petName); + } } catch (error) { console.error(error); cancel(error); From fe32b37e98253e00d2f23022ee3fa839996c0578 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Fri, 16 Jun 2023 23:15:05 -0700 Subject: [PATCH 039/234] refactor(daemon): Replace UUIDs with crypto random 512 bit identifiers --- packages/daemon/src/daemon-node-powers.js | 19 +++- packages/daemon/src/daemon.js | 108 ++++++++++++---------- packages/daemon/src/pet-store.js | 19 ++-- packages/daemon/src/types.d.ts | 4 +- 4 files changed, 82 insertions(+), 68 deletions(-) diff --git a/packages/daemon/src/daemon-node-powers.js b/packages/daemon/src/daemon-node-powers.js index 0a6b851dcc..1b84742597 100644 --- a/packages/daemon/src/daemon-node-powers.js +++ b/packages/daemon/src/daemon-node-powers.js @@ -36,7 +36,16 @@ export const makePowers = ({ crypto, net, fs, path: fspath, popen, url }) => { }); }; - const randomUuid = () => crypto.randomUUID(); + const randomHex512 = () => + new Promise((resolve, reject) => + crypto.randomBytes(64, (err, bytes) => { + if (err) { + reject(err); + } else { + resolve(bytes.toString('hex')); + } + }), + ); const listenOnPath = async (sockPath, cancelled) => { const [ @@ -155,7 +164,7 @@ export const makePowers = ({ crypto, net, fs, path: fspath, popen, url }) => { }; /** - * @param {string} uuid + * @param {string} id * @param {string} path * @param {string} logPath * @param {string} pidPath @@ -166,7 +175,7 @@ export const makePowers = ({ crypto, net, fs, path: fspath, popen, url }) => { * @param {Promise} cancelled */ const makeWorker = async ( - uuid, + id, path, logPath, pidPath, @@ -179,7 +188,7 @@ export const makePowers = ({ crypto, net, fs, path: fspath, popen, url }) => { const log = fs.openSync(logPath, 'a'); const child = popen.fork( path, - [uuid, sockPath, statePath, ephemeralStatePath, cachePath], + [id, sockPath, statePath, ephemeralStatePath, cachePath], { stdio: ['ignore', log, log, 'pipe', 'pipe', 'ipc'], // @ts-ignore Stale Node.js type definition. @@ -218,7 +227,7 @@ export const makePowers = ({ crypto, net, fs, path: fspath, popen, url }) => { sinkError, exitOnError, makeSha512, - randomUuid, + randomHex512, listenOnPath, informParentWhenListeningOnPath, makeFileReader, diff --git a/packages/daemon/src/daemon.js b/packages/daemon/src/daemon.js index 668a5016a4..900df56a38 100644 --- a/packages/daemon/src/daemon.js +++ b/packages/daemon/src/daemon.js @@ -13,7 +13,7 @@ import { makeChangeTopic } from './pubsub.js'; import { makeNetstringCapTP } from './connection.js'; import { makeRefReader } from './ref-reader.js'; import { makeReaderRef, makeIteratorRef } from './reader-ref.js'; -import { makeOwnPetStore, makeUuidPetStore } from './pet-store.js'; +import { makeOwnPetStore, makeIdentifiedPetStore } from './pet-store.js'; const { quote: q } = assert; @@ -97,10 +97,10 @@ const makeEndoBootstrap = ( // but also because we won't know the name we will use until we've // completed the hash. const digester = powers.makeSha512(); - const storageUuid = powers.randomUuid(); + const storageId512 = await powers.randomHex512(); const temporaryStoragePath = powers.joinPath( storageDirectoryPath, - storageUuid, + storageId512, ); const writer = powers.makeFileWriter(temporaryStoragePath); for await (const chunk of makeRefReader(readerRef)) { @@ -123,11 +123,11 @@ const makeEndoBootstrap = ( }; /** - * @param {string} workerUuid + * @param {string} workerId512 * @param {string} workerFormulaIdentifier */ - const makeWorkerBootstrap = async (workerUuid, workerFormulaIdentifier) => { - // TODO validate workerUuid, workerFormulaIdentifier + const makeWorkerBootstrap = async (workerId512, workerFormulaIdentifier) => { + // TODO validate workerId512, workerFormulaIdentifier /** @type {Map>} */ const responses = new Map(); @@ -136,9 +136,13 @@ const makeEndoBootstrap = ( // powers that were granted a specific program. // There should not be a worker pet store, but a different Powers object // for each bundle or some such, and a separate memo as well. - const workerPetStore = await makeUuidPetStore(powers, locator, workerUuid); + const workerPetStore = await makeIdentifiedPetStore( + powers, + locator, + workerId512, + ); - return Far(`Endo for worker ${workerUuid}`, { + return Far(`Endo for worker ${workerId512}`, { request: async (what, responseName) => { if (responseName === undefined) { // Behold, recursion: @@ -168,25 +172,25 @@ const makeEndoBootstrap = ( }; /** - * @param {string} workerUuid + * @param {string} workerId512 */ - const makeUuidWorker = async workerUuid => { - // TODO validate workerUuid - const workerFormulaIdentifier = `worker-uuid:${workerUuid}`; + const makeIdentifiedWorker = async workerId512 => { + // TODO validate workerId512 + const workerFormulaIdentifier = `worker-id512:${workerId512}`; const workerCachePath = powers.joinPath( locator.cachePath, - 'worker-uuid', - workerUuid, + 'worker-id512', + workerId512, ); const workerStatePath = powers.joinPath( locator.statePath, - 'worker-uuid', - workerUuid, + 'worker-id512', + workerId512, ); const workerEphemeralStatePath = powers.joinPath( locator.ephemeralStatePath, - 'worker-uuid', - workerUuid, + 'worker-id512', + workerId512, ); await Promise.all([ @@ -211,7 +215,7 @@ const makeEndoBootstrap = ( closed: workerClosed, pid: workerPid, } = await powers.makeWorker( - workerUuid, + workerId512, powers.endoWorkerPath, logPath, workerPidPath, @@ -222,18 +226,22 @@ const makeEndoBootstrap = ( workerCancelled, ); - console.log(`Endo worker started PID ${workerPid} UUID ${workerUuid}`); + console.log( + `Endo worker started PID ${workerPid} unique identifier ${workerId512}`, + ); const { getBootstrap, closed: capTpClosed } = makeNetstringCapTP( - `Worker ${workerUuid}`, + `Worker ${workerId512}`, writer, reader, gracePeriodElapsed, - makeWorkerBootstrap(workerUuid, workerFormulaIdentifier), + makeWorkerBootstrap(workerId512, workerFormulaIdentifier), ); const closed = Promise.race([workerClosed, capTpClosed]).finally(() => { - console.log(`Endo worker stopped PID ${workerPid} UUID ${workerUuid}`); + console.log( + `Endo worker stopped PID ${workerPid} with unique identifier ${workerId512}`, + ); }); /** @type {import('@endo/eventual-send').ERef} */ @@ -299,7 +307,7 @@ const makeEndoBootstrap = ( // Behold, recursion: // eslint-disable-next-line no-use-before-define - return provideValueForFormula(formula, 'eval-uuid', resultName); + return provideValueForFormula(formula, 'eval-id512', resultName); }, importUnsafe0: async (importPath, resultName) => { @@ -314,7 +322,7 @@ const makeEndoBootstrap = ( // eslint-disable-next-line no-use-before-define return provideValueForFormula( formula, - 'import-unsafe0-uuid', + 'import-unsafe0-id512', resultName, ); }, @@ -339,7 +347,7 @@ const makeEndoBootstrap = ( // eslint-disable-next-line no-use-before-define return provideValueForFormula( formula, - 'import-bundle0-uuid', + 'import-bundle0-id512', resultName, ); }, @@ -445,22 +453,22 @@ const makeEndoBootstrap = ( /** * @param {string} formulaType - * @param {string} formulaUuid + * @param {string} formulaId512 */ - const makeFormulaPath = (formulaType, formulaUuid) => { - if (formulaUuid.length < 3) { + const makeFormulaPath = (formulaType, formulaId512) => { + if (formulaId512.length < 3) { throw new TypeError( - `Invalid formula identifier, unrecognized uuid ${q( - formulaUuid, - )} for formula of type ${q(formulaType)}`, + `Invalid formula identifier ${q(formulaId512)} for formula of type ${q( + formulaType, + )}`, ); } - const head = formulaUuid.slice(0, 2); - const tail = formulaUuid.slice(3); + const head = formulaId512.slice(0, 2); + const tail = formulaId512.slice(3); const directory = powers.joinPath( locator.statePath, 'formulas', - `${formulaType}-uuid`, + formulaType, head, ); const file = powers.joinPath(directory, `${tail}.json`); @@ -468,8 +476,8 @@ const makeEndoBootstrap = ( }; // Persist instructions for revival (this can be collected) - const writeFormula = async (formula, formulaType, formulaUuid) => { - const { directory, file } = makeFormulaPath(formulaType, formulaUuid); + const writeFormula = async (formula, formulaType, formulaId512) => { + const { directory, file } = makeFormulaPath(formulaType, formulaId512); await powers.makePath(directory); await powers.writeFileText(file, `${q(formula)}\n`); }; @@ -509,12 +517,12 @@ const makeEndoBootstrap = ( const suffix = formulaIdentifier.slice(delimiterIndex + 1); if (prefix === 'readable-blob-sha512') { return makeSha512ReadableBlob(suffix); - } else if (prefix === 'worker-uuid') { - return makeUuidWorker(suffix); - } else if (prefix === 'pet-store-uuid') { - return makeUuidPetStore(powers, locator, suffix); + } else if (prefix === 'worker-id512') { + return makeIdentifiedWorker(suffix); + } else if (prefix === 'pet-store-id512') { + return makeIdentifiedPetStore(powers, locator, suffix); } else if ( - ['eval-uuid', 'import-unsafe0-uuid', 'import-bundle0-uuid'].includes( + ['eval-id512', 'import-unsafe0-id512', 'import-bundle0-id512'].includes( prefix, ) ) { @@ -538,9 +546,9 @@ const makeEndoBootstrap = ( * @param {string} [resultName] */ const provideValueForFormula = async (formula, formulaType, resultName) => { - const formulaUuid = powers.randomUuid(); - const formulaIdentifier = `${formulaType}:${formulaUuid}`; - await writeFormula(formula, formulaType, formulaUuid); + const formulaId512 = await powers.randomHex512(); + const formulaIdentifier = `${formulaType}:${formulaId512}`; + await writeFormula(formula, formulaType, formulaId512); // Behold, recursion: // eslint-disable-next-line no-use-before-define const promiseForValue = makeValueForFormula(formula); @@ -590,9 +598,8 @@ const makeEndoBootstrap = ( }; const makePetStore = async petName => { - // @ts-ignore Node.js crypto does in fact have randomUUID. - const petStoreUuid = powers.randomUuid(); - const formulaIdentifier = `pet-store-uuid:${petStoreUuid}`; + const petStoreId512 = await powers.randomHex512(); + const formulaIdentifier = `pet-store-id512:${petStoreId512}`; if (petName !== undefined) { await E(petStoreP).write(petName, formulaIdentifier); } @@ -707,9 +714,8 @@ const makeEndoBootstrap = ( * @param {string} [petName] */ makeWorker: async petName => { - // @ts-ignore Node.js crypto does in fact have randomUUID. - const workerUuid = powers.randomUuid(); - const formulaIdentifier = `worker-uuid:${workerUuid}`; + const workerId512 = await powers.randomHex512(); + const formulaIdentifier = `worker-id512:${workerId512}`; if (petName !== undefined) { await E(petStoreP).write(petName, formulaIdentifier); } diff --git a/packages/daemon/src/pet-store.js b/packages/daemon/src/pet-store.js index fa1bc2d67c..95bab01bb4 100644 --- a/packages/daemon/src/pet-store.js +++ b/packages/daemon/src/pet-store.js @@ -3,10 +3,9 @@ import { Far } from '@endo/far'; const { quote: q } = assert; const validNamePattern = /^[a-zA-Z][a-zA-Z0-9]{0,127}$/; -const validUuidPattern = - /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/; +const validIdPattern = /^[0-9a-f]{128}$/; const validFormulaPattern = - /^(?:readable-blob-sha512:[0-9a-f]{128}|(?:worker-uuid|pet-store-uuid|eval-uuid|import-unsafe0-uuid|import-bundle0-uuid):(?:[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}))$/; + /^(?:readable-blob-sha512|worker-id512|pet-store-id512|eval-id512|import-unsafe0-id512|import-bundle0-id512):[0-9a-f]{128}$/; /** * @param {import('./types.js').DaemonicPowers} powers @@ -186,17 +185,17 @@ const makePetStoreAtPath = async (powers, petNameDirectoryPath) => { /** * @param {import('./types.js').DaemonicPowers} powers * @param {import('./types.js').Locator} locator - * @param {string} uuid + * @param {string} id */ -export const makeUuidPetStore = (powers, locator, uuid) => { - if (!validUuidPattern.test(uuid)) { - throw new Error(`Invalid UUID for pet store ${q(uuid)}`); +export const makeIdentifiedPetStore = (powers, locator, id) => { + if (!validIdPattern.test(id)) { + throw new Error(`Invalid identifier for pet store ${q(id)}`); } - const prefix = uuid.slice(0, 2); - const suffix = uuid.slice(3); + const prefix = id.slice(0, 2); + const suffix = id.slice(3); const petNameDirectoryPath = powers.joinPath( locator.statePath, - 'pet-store-uuid', + 'pet-store-id512', prefix, suffix, ); diff --git a/packages/daemon/src/types.d.ts b/packages/daemon/src/types.d.ts index 104ae0eb09..4a0c79f19a 100644 --- a/packages/daemon/src/types.d.ts +++ b/packages/daemon/src/types.d.ts @@ -26,7 +26,7 @@ export type DaemonicPowers = { sinkError: (error) => void; exitOnError: (error) => void; makeSha512: () => Sha512; - randomUuid: () => string; + randomHex512: () => Promise; listenOnPath: ( path: string, cancelled: Promise, @@ -43,7 +43,7 @@ export type DaemonicPowers = { joinPath: (...components: Array) => string; delay: (ms: number, cancelled: Promise) => Promise; makeWorker: ( - uuid: string, + id: string, path: string, logPath: string, pidPath: string, From 1bc555bcd1b39f53d9d3c8892a3a1fdf0f5632e3 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Fri, 16 Jun 2023 23:16:59 -0700 Subject: [PATCH 040/234] refactor(daemon): Change extension entry method to provide0 --- packages/daemon/src/worker.js | 4 ++-- packages/daemon/test/service.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/daemon/src/worker.js b/packages/daemon/src/worker.js index 13b53079c7..58de12d264 100644 --- a/packages/daemon/src/worker.js +++ b/packages/daemon/src/worker.js @@ -64,7 +64,7 @@ export const makeWorkerFacet = ({ importUnsafe0: async path => { const url = pathToFileURL(path); const namespace = await import(url); - return namespace.main0(powerBox); + return namespace.provide0(powerBox); }, importBundle0: async readable => { @@ -77,7 +77,7 @@ export const makeWorkerFacet = ({ const namespace = await importBundle(bundle, { endowments, }); - return namespace.main0(powerBox); + return namespace.provide0(powerBox); }, }); }; diff --git a/packages/daemon/test/service.js b/packages/daemon/test/service.js index 153e349fac..1c6937eb09 100644 --- a/packages/daemon/test/service.js +++ b/packages/daemon/test/service.js @@ -1,6 +1,6 @@ import { E, Far } from '@endo/far'; -export const main0 = powers => { +export const provide0 = powers => { return Far('Service', { async ask() { return E(powers).request( From 11f86a552d25570596ef20fc0928989abcdb8687 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Tue, 20 Jun 2023 17:08:43 -0700 Subject: [PATCH 041/234] feat(daemon): Reify inboxes and outboxes --- packages/cli/src/endo.js | 319 ++++++++--- packages/daemon/src/daemon.js | 906 ++++++++++++++++++++---------- packages/daemon/src/pet-store.js | 11 +- packages/daemon/src/reader-ref.js | 3 +- packages/daemon/src/types.d.ts | 84 ++- packages/daemon/src/worker.js | 22 +- packages/daemon/test/test-endo.js | 112 ++-- 7 files changed, 1025 insertions(+), 432 deletions(-) diff --git a/packages/cli/src/endo.js b/packages/cli/src/endo.js index 9c02c14ac4..30f5cae50b 100644 --- a/packages/cli/src/endo.js +++ b/packages/cli/src/endo.js @@ -51,6 +51,8 @@ const readPowers = makeReadPowers({ fs, url, crypto }); const writePowers = makeWritePowers({ fs, url }); const { write } = writePowers; +const collect = (value, values) => values.concat([value]); + const packageDescriptorPath = url.fileURLToPath( new URL('../package.json', import.meta.url), ); @@ -291,12 +293,16 @@ export const main = async rawArgs => { program .command('archive ') + .option('-i,--inbox ', 'An alternate inbox', collect, []) .option('-n,--name ', 'Store the archive into Endo') .option('-f,--file ', 'Store the archive into a file') .description('captures an archive from an entry module path') .action(async (applicationPath, cmd) => { - const archiveName = cmd.opts().name; - const archivePath = cmd.opts().file; + const { + name: archiveName, + file: archivePath, + inbox: inboxNames, + } = cmd.opts(); const applicationLocation = url.pathToFileURL(applicationPath); if (archiveName !== undefined) { const archiveBytes = await makeArchive(readPowers, applicationLocation); @@ -308,7 +314,11 @@ export const main = async rawArgs => { ); try { const bootstrap = getBootstrap(); - await E(bootstrap).store(readerRef, archiveName); + let inbox = E(bootstrap).inbox(); + for (const inboxName of inboxNames) { + inbox = E(inbox).provide(inboxName); + } + await E(inbox).store(readerRef, archiveName); } catch (error) { console.error(error); cancel(error); @@ -328,12 +338,13 @@ export const main = async rawArgs => { program .command('bundle ') + .option('-i,--inbox ', 'An alternate inbox', collect, []) .option('-n,--name ', 'Store the bundle into Endo') .description( 'captures a JSON bundle containing an archive for an entry module path', ) .action(async (applicationPath, cmd) => { - const bundleName = cmd.opts().name; + const { name: bundleName, inbox: inboxNames } = cmd.opts(); const bundle = await bundleSource(applicationPath); console.log(bundle.endoZipBase64Sha512); const bundleText = JSON.stringify(bundle); @@ -346,7 +357,11 @@ export const main = async rawArgs => { ); try { const bootstrap = getBootstrap(); - await E(bootstrap).store(readerRef, bundleName); + let inbox = E(bootstrap).inbox(); + for (const inboxName of inboxNames) { + inbox = E(inbox).provide(inboxName); + } + await E(inbox).store(readerRef, bundleName); } catch (error) { console.error(error); cancel(error); @@ -355,13 +370,14 @@ export const main = async rawArgs => { program .command('store ') + .option('-i,--inbox ', 'An alternate inbox', collect, []) .option( '-n,--name ', 'Assigns a pet name to the result for future reference', ) .description('stores a readable file') .action(async (storablePath, cmd) => { - const { name } = cmd.opts(); + const { name, inbox: inboxNames } = cmd.opts(); const nodeReadStream = fs.createReadStream(storablePath); const reader = makeNodeReader(nodeReadStream); const readerRef = makeReaderRef(reader); @@ -373,7 +389,11 @@ export const main = async rawArgs => { ); try { const bootstrap = getBootstrap(); - await E(bootstrap).store(readerRef, name); + let inbox = E(bootstrap).inbox(); + for (const inboxName of inboxNames) { + inbox = E(inbox).provide(inboxName); + } + await E(inbox).store(readerRef, name); } catch (error) { console.error(error); cancel(error); @@ -383,7 +403,9 @@ export const main = async rawArgs => { program .command('spawn [names...]') .description('creates workers for evaluating or importing programs') - .action(async petNames => { + .option('-i,--inbox ', 'An alternate inbox', collect, []) + .action(async (petNames, cmd) => { + const { inbox: inboxNames } = cmd.opts(); const { getBootstrap } = await provideEndoClient( 'cli', sockPath, @@ -391,8 +413,12 @@ export const main = async rawArgs => { ); try { const bootstrap = getBootstrap(); + let inbox = E(bootstrap).inbox(); + for (const inboxName of inboxNames) { + inbox = E(inbox).provide(inboxName); + } for (const petName of petNames) { - await E(bootstrap).makeWorker(petName); + await E(inbox).makeWorker(petName); } } catch (error) { console.error(error); @@ -402,8 +428,10 @@ export const main = async rawArgs => { program .command('show ') - .description('prints a representation of the named value') - .action(async name => { + .option('-i,--inbox ', 'An alternate inbox', collect, []) + .description('prints the named value') + .action(async (name, cmd) => { + const { inbox: inboxNames } = cmd.opts(); const { getBootstrap } = await provideEndoClient( 'cli', sockPath, @@ -411,7 +439,11 @@ export const main = async rawArgs => { ); try { const bootstrap = getBootstrap(); - const pet = await E(bootstrap).provide(name); + let inbox = E(bootstrap).inbox(); + for (const inboxName of inboxNames) { + inbox = E(inbox).provide(inboxName); + } + const pet = await E(inbox).provide(name); console.log(pet); } catch (error) { console.error(error); @@ -421,10 +453,12 @@ export const main = async rawArgs => { program .command('follow ') + .option('-i,--inbox ', 'An alternate inbox', collect, []) .description( 'prints a representation of each value from the named async iterable as it arrives', ) - .action(async name => { + .action(async (name, cmd) => { + const { inbox: inboxNames } = cmd.opts(); const { getBootstrap } = await provideEndoClient( 'cli', sockPath, @@ -432,9 +466,12 @@ export const main = async rawArgs => { ); try { const bootstrap = getBootstrap(); - const iterable = await E(bootstrap).provide(name); - const iterator = await E(iterable)[Symbol.asyncIterator](); - for await (const iterand of makeRefIterator(iterator)) { + let inbox = E(bootstrap).inbox(); + for (const inboxName of inboxNames) { + inbox = E(inbox).provide(inboxName); + } + const iterable = await E(inbox).provide(name); + for await (const iterand of makeRefIterator(iterable)) { console.log(iterand); } } catch (error) { @@ -445,8 +482,10 @@ export const main = async rawArgs => { program .command('cat ') + .option('-i,--inbox ', 'An alternate inbox', collect, []) .description('prints the content of the named readable file') - .action(async name => { + .action(async (name, cmd) => { + const { inbox: inboxNames } = cmd.opts(); const { getBootstrap } = await provideEndoClient( 'cli', sockPath, @@ -454,7 +493,11 @@ export const main = async rawArgs => { ); try { const bootstrap = getBootstrap(); - const readable = await E(bootstrap).provide(name); + let inbox = E(bootstrap).inbox(); + for (const inboxName of inboxNames) { + inbox = E(inbox).provide(inboxName); + } + const readable = await E(inbox).provide(name); const readerRef = E(readable).stream(); const reader = makeRefReader(readerRef); for await (const chunk of reader) { @@ -468,10 +511,10 @@ export const main = async rawArgs => { program .command('list') - .option('-n,--name ', 'The name of an alternate pet store') + .option('-i,--inbox ', 'An alternate inbox', collect, []) .description('lists pet names') .action(async cmd => { - const { name: petStoreName } = cmd.opts(); + const { inbox: inboxNames } = cmd.opts(); const { getBootstrap } = await provideEndoClient( 'cli', sockPath, @@ -479,11 +522,11 @@ export const main = async rawArgs => { ); try { const bootstrap = getBootstrap(); - const petStore = - petStoreName === undefined - ? E(bootstrap).petStore() - : E(bootstrap).provide(petStoreName); - const petNames = await E(petStore).list(); + let inbox = E(bootstrap).inbox(); + for (const inboxName of inboxNames) { + inbox = E(inbox).provide(inboxName); + } + const petNames = await E(inbox).list(); for await (const petName of petNames) { console.log(petName); } @@ -496,9 +539,9 @@ export const main = async rawArgs => { program .command('remove [names...]') .description('removes pet names') - .option('-n,--name ', 'The name of an alternate pet store') + .option('-i,--inbox ', 'An alternate inbox', collect, []) .action(async (petNames, cmd) => { - const { name: petStoreName } = cmd.opts(); + const { inbox: inboxNames } = cmd.opts(); const { getBootstrap } = await provideEndoClient( 'cli', sockPath, @@ -506,11 +549,11 @@ export const main = async rawArgs => { ); try { const bootstrap = getBootstrap(); - const petStore = - petStoreName === undefined - ? E(bootstrap).petStore() - : E(bootstrap).provide(petStoreName); - await Promise.all(petNames.map(petName => E(petStore).remove(petName))); + let inbox = E(bootstrap).inbox(); + for (const inboxName of inboxNames) { + inbox = E(inbox).provide(inboxName); + } + await Promise.all(petNames.map(petName => E(inbox).remove(petName))); } catch (error) { console.error(error); cancel(error); @@ -520,9 +563,9 @@ export const main = async rawArgs => { program .command('rename ') .description('renames a value') - .option('-n,--name ', 'The name of an alternate pet store') + .option('-i,--inbox ', 'An alternate inbox', collect, []) .action(async (fromName, toName, cmd) => { - const { name: petStoreName } = cmd.opts(); + const { inbox: inboxNames } = cmd.opts(); const { getBootstrap } = await provideEndoClient( 'cli', sockPath, @@ -530,11 +573,11 @@ export const main = async rawArgs => { ); try { const bootstrap = getBootstrap(); - const petStore = - petStoreName === undefined - ? E(bootstrap).petStore() - : E(bootstrap).provide(petStoreName); - await E(petStore).rename(fromName, toName); + let inbox = E(bootstrap).inbox(); + for (const inboxName of inboxNames) { + inbox = E(inbox).provide(inboxName); + } + await E(inbox).rename(fromName, toName); } catch (error) { console.error(error); cancel(error); @@ -542,9 +585,11 @@ export const main = async rawArgs => { }); program - .command('make-pet-store ') - .description('creates a new pet store') - .action(async name => { + .command('make-inbox ') + .option('-i,--inbox ', 'An alternate inbox', collect, []) + .description('creates a new inbox') + .action(async (name, cmd) => { + const { inbox: inboxNames } = cmd.opts(); const { getBootstrap } = await provideEndoClient( 'cli', sockPath, @@ -552,8 +597,37 @@ export const main = async rawArgs => { ); try { const bootstrap = getBootstrap(); - const petStore = await E(bootstrap).makePetStore(name); - console.log(petStore); + let inbox = E(bootstrap).inbox(); + for (const inboxName of inboxNames) { + inbox = E(inbox).provide(inboxName); + } + const newInbox = await E(inbox).makeInbox(name); + console.log(newInbox); + } catch (error) { + console.error(error); + cancel(error); + } + }); + + program + .command('make-outbox ') + .option('-i,--inbox ', 'An alternate inbox', collect, []) + .description('creates a new outbox') + .action(async (name, cmd) => { + const { inbox: inboxNames } = cmd.opts(); + const { getBootstrap } = await provideEndoClient( + 'cli', + sockPath, + cancelled, + ); + try { + const bootstrap = getBootstrap(); + let inbox = E(bootstrap).inbox(); + for (const inboxName of inboxNames) { + inbox = E(inbox).provide(inboxName); + } + const newOutbox = await E(inbox).makeOutbox(name); + console.log(newOutbox); } catch (error) { console.error(error); cancel(error); @@ -562,11 +636,11 @@ export const main = async rawArgs => { program .command('inbox') - .option('-n,--name ', 'The name of an alternate inbox') + .option('-i,--inbox ', 'An alternate inbox', collect, []) .option('-f,--follow', 'Follow the inbox for messages as they arrive') .description('prints pending requests that have been sent to you') .action(async cmd => { - const { name: inboxName, follow } = cmd.opts(); + const { inbox: inboxNames, follow } = cmd.opts(); const { getBootstrap } = await provideEndoClient( 'cli', sockPath, @@ -574,13 +648,53 @@ export const main = async rawArgs => { ); try { const bootstrap = getBootstrap(); - const inbox = - inboxName === undefined ? bootstrap : E(bootstrap).provide(inboxName); - const iterable = follow ? E(inbox).followInbox() : E(inbox).inbox(); - const iterator = await E(iterable)[Symbol.asyncIterator](); - for await (const { number, who, what } of makeRefIterator(iterator)) { - // TODO ensure the description is ASCII. - console.log(`${number}. ${who}: ${what}`); + let inbox = E(bootstrap).inbox(); + for (const inboxName of inboxNames) { + inbox = E(inbox).provide(inboxName); + } + const requests = follow + ? makeRefIterator(E(inbox).followMessages()) + : await E(inbox).listMessages(); + for await (const { number, who, what } of requests) { + if (who !== undefined) { + // TODO ensure the description is all printable ASCII and so + // contains no TTY control codes. + console.log(`${number}. ${who}: ${what}`); + } + } + } catch (error) { + console.error(error); + cancel(error); + } + }); + + program + .command('request ') + .description('requests a reference with the given description') + .option('-i,--inbox ', 'An alternate inbox', collect, []) + .option( + '-n,--name ', + 'Assigns a name to the result for future reference, persisted between restarts', + ) + .option('-w,--wait', 'Waits for and prints the response') + .action(async (outboxName, description, cmd) => { + const { name: resultName, inbox: inboxNames, wait } = cmd.opts(); + const { getBootstrap } = await provideEndoClient( + 'cli', + sockPath, + cancelled, + ); + try { + const bootstrap = getBootstrap(); + let inbox = E(bootstrap).inbox(); + for (const inboxName of inboxNames) { + inbox = E(inbox).provide(inboxName); + } + const outboxP = E(inbox).provide(outboxName); + const resultP = E(outboxP).request(description, resultName); + if (wait || resultName === undefined) { + const result = await resultP; + console.log(result); } } catch (error) { console.error(error); @@ -590,8 +704,10 @@ export const main = async rawArgs => { program .command('resolve ') + .option('-i,--inbox ', 'An alternate inbox', collect, []) .description('responds to a pending request with the named value') - .action(async (requestNumberText, resolutionName) => { + .action(async (requestNumberText, resolutionName, cmd) => { + const { inbox: inboxNames } = cmd.opts(); // TODO less bad number parsing. const requestNumber = Number(requestNumberText); const { getBootstrap } = await provideEndoClient( @@ -601,7 +717,11 @@ export const main = async rawArgs => { ); try { const bootstrap = getBootstrap(); - await E(bootstrap).resolve(requestNumber, resolutionName); + let inbox = E(bootstrap).inbox(); + for (const inboxName of inboxNames) { + inbox = E(inbox).provide(inboxName); + } + await E(inbox).resolve(requestNumber, resolutionName); } catch (error) { console.error(error); cancel(error); @@ -611,7 +731,9 @@ export const main = async rawArgs => { program .command('reject [message]') .description('responds to a pending request with the rejection message') - .action(async (requestNumberText, message) => { + .option('-i,--inbox ', 'An alternate inbox', collect, []) + .action(async (requestNumberText, message, cmd) => { + const { inbox: inboxNames } = cmd.opts(); // TODO less bad number parsing. const requestNumber = Number(requestNumberText); const { getBootstrap } = await provideEndoClient( @@ -621,7 +743,11 @@ export const main = async rawArgs => { ); try { const bootstrap = getBootstrap(); - await E(bootstrap).reject(requestNumber, message); + let inbox = E(bootstrap).inbox(); + for (const inboxName of inboxNames) { + inbox = E(inbox).provide(inboxName); + } + await E(inbox).reject(requestNumber, message); } catch (error) { console.error(error); cancel(error); @@ -629,14 +755,23 @@ export const main = async rawArgs => { }); program - .command('eval [names...]') + .command('eval [names...]') + .description('evaluates a string with the endowed values in scope') + .option('-i,--inbox ', 'An alternate inbox', collect, []) + .option( + '-w,--worker ', + 'Reuse an existing worker rather than create a new one', + ) .option( '-n,--name ', 'Assigns a name to the result for future reference, persisted between restarts', ) - .description('evaluates a string with the endowed values in scope') - .action(async (worker, source, names, cmd) => { - const { name: resultName } = cmd.opts(); + .action(async (source, names, cmd) => { + const { + name: resultName, + worker: workerName, + inbox: inboxNames, + } = cmd.opts(); const { getBootstrap } = await provideEndoClient( 'cli', sockPath, @@ -644,7 +779,10 @@ export const main = async rawArgs => { ); try { const bootstrap = getBootstrap(); - const workerRef = E(bootstrap).provide(worker); + let inbox = E(bootstrap).inbox(); + for (const inboxName of inboxNames) { + inbox = E(inbox).provide(inboxName); + } const pairs = names.map(name => { /** @type {Array} */ @@ -664,7 +802,8 @@ export const main = async rawArgs => { const codeNames = pairs.map(pair => pair[0]); const endowmentNames = pairs.map(pair => pair[1]); - const result = await E(workerRef).evaluate( + const result = await E(inbox).evaluate( + workerName, source, codeNames, endowmentNames, @@ -678,16 +817,21 @@ export const main = async rawArgs => { }); program - .command('import-unsafe0 ') + .command('import-unsafe0 ') + .description( + 'imports the module at the given path and runs its provide0 function with all of your authority', + ) + .option('-i,--inbox ', 'An alternate inbox', collect, []) .option( '-n,--name ', 'Assigns a name to the result for future reference, persisted between restarts', ) - .description( - 'imports the module at the given path and runs its main0 function with all of your authority', + .option( + '-w,--worker ', + 'Reuse an existing worker rather than create a new one', ) - .action(async (worker, importPath, cmd) => { - const { name: resultName } = cmd.opts(); + .action(async (importPath, outboxName, cmd) => { + const { name: resultName, worker: workerName, inboxNames } = cmd.opts(); const { getBootstrap } = await provideEndoClient( 'cli', sockPath, @@ -695,10 +839,14 @@ export const main = async rawArgs => { ); try { const bootstrap = getBootstrap(); - const workerRef = E(bootstrap).provide(worker); - - const result = await E(workerRef).importUnsafe0( + let inbox = E(bootstrap).inbox(); + for (const inboxName of inboxNames) { + inbox = E(inbox).provide(inboxName); + } + const result = await E(inbox).importUnsafe0( + workerName, path.resolve(importPath), + outboxName, resultName, ); console.log(result); @@ -709,16 +857,25 @@ export const main = async rawArgs => { }); program - .command('import-bundle0 ') + .command('import-bundle0 ') + .description( + 'imports the named bundle in a confined space within a worker and runs its provide0 without any initial authority', + ) + .option('-i,--inbox ', 'An alternate inbox', collect, []) .option( '-n,--name ', 'Assigns a name to the result for future reference, persisted between restarts', ) - .description( - 'imports the named bundle in a confined space within a worker and runs its main0 without any authority', + .option( + '-w,--worker ', + 'Reuse an existing worker rather than create a new one', ) - .action(async (worker, readableBundleName, cmd) => { - const { name: resultName } = cmd.opts(); + .action(async (readableBundleName, outboxName, cmd) => { + const { + name: resultName, + worker: workerName, + inbox: inboxNames, + } = cmd.opts(); const { getBootstrap } = await provideEndoClient( 'cli', sockPath, @@ -726,10 +883,14 @@ export const main = async rawArgs => { ); try { const bootstrap = getBootstrap(); - const workerRef = E(bootstrap).provide(worker); - - const result = await E(workerRef).importBundle0( + let inbox = E(bootstrap).inbox(); + for (const inboxName of inboxNames) { + inbox = E(inbox).provide(inboxName); + } + const result = await E(inbox).importBundle0( + workerName, readableBundleName, + outboxName, resultName, ); console.log(result); diff --git a/packages/daemon/src/daemon.js b/packages/daemon/src/daemon.js index 900df56a38..394e911cf2 100644 --- a/packages/daemon/src/daemon.js +++ b/packages/daemon/src/daemon.js @@ -39,18 +39,12 @@ const makeEndoBootstrap = ( // reference", and not for "what is my name for this promise". /** @type {WeakMap} */ const formulaIdentifierForRef = new WeakMap(); - - const requests = new Map(); - const resolvers = new WeakMap(); - /** @type {import('./types.js').Topic} */ - const requestsTopic = makeChangeTopic(); - let nextRequestNumber = 0; + /** @type {WeakMap} */ + const inboxRequestFunctions = new WeakMap(); /** @type {WeakMap>} */ const workerBootstraps = new WeakMap(); - const petStoreP = makeOwnPetStore(powers, locator); - /** * @param {string} sha512 */ @@ -77,15 +71,8 @@ const makeEndoBootstrap = ( /** * @param {import('@endo/eventual-send').ERef>} readerRef - * @param {string} [petName] */ - const store = async (readerRef, petName) => { - if (petName !== undefined) { - if (!validNamePattern.test(petName)) { - throw new Error(`Invalid pet name ${q(petName)}`); - } - } - + const storeReaderRef = async readerRef => { const storageDirectoryPath = powers.joinPath( locator.statePath, 'store-sha512', @@ -110,16 +97,11 @@ const makeEndoBootstrap = ( await writer.return(undefined); const sha512 = digester.digestHex(); - // Retain the pet name first (to win a garbage collection race) - if (petName !== undefined) { - const formulaIdentifier = `readable-blob-sha512:${sha512}`; - await E(petStoreP).write(petName, formulaIdentifier); - } - // Finish with an atomic rename. const storagePath = powers.joinPath(storageDirectoryPath, sha512); await powers.renamePath(temporaryStoragePath, storagePath); - return makeSha512ReadableBlob(sha512); + + return `readable-blob-sha512:${sha512}`; }; /** @@ -128,47 +110,7 @@ const makeEndoBootstrap = ( */ const makeWorkerBootstrap = async (workerId512, workerFormulaIdentifier) => { // TODO validate workerId512, workerFormulaIdentifier - - /** @type {Map>} */ - const responses = new Map(); - - // TODO, the petStore should be associated not with the worker but with the - // powers that were granted a specific program. - // There should not be a worker pet store, but a different Powers object - // for each bundle or some such, and a separate memo as well. - const workerPetStore = await makeIdentifiedPetStore( - powers, - locator, - workerId512, - ); - - return Far(`Endo for worker ${workerId512}`, { - request: async (what, responseName) => { - if (responseName === undefined) { - // Behold, recursion: - // eslint-disable-next-line no-use-before-define - return request( - what, - responseName, - workerFormulaIdentifier, - workerPetStore, - ); - } - let responseP = responses.get(responseName); - if (responseP === undefined) { - // Behold, recursion: - // eslint-disable-next-line no-use-before-define - responseP = request( - what, - responseName, - workerFormulaIdentifier, - workerPetStore, - ); - responses.set(responseName, responseP); - } - return responseP; - }, - }); + return Far(`Endo for worker ${workerId512}`, {}); }; /** @@ -270,87 +212,6 @@ const makeEndoBootstrap = ( terminate, whenTerminated: () => closed, - - /** - * @param {string} source - * @param {Array} codeNames - * @param {Array} petNames - * @param {string} resultName - */ - evaluate: async (source, codeNames, petNames, resultName) => { - if (resultName !== undefined && !validNamePattern.test(resultName)) { - throw new Error(`Invalid pet name ${q(resultName)}`); - } - if (petNames.length !== codeNames.length) { - throw new Error('Evaluator requires one pet name for each code name'); - // TODO and they must all be strings. Use pattern language. - } - - const formulaIdentifiers = await Promise.all( - petNames.map(async petName => { - const formulaIdentifier = await E(petStoreP).get(petName); - if (formulaIdentifier === undefined) { - throw new Error(`Unknown pet name ${q(petName)}`); - } - return formulaIdentifier; - }), - ); - - const formula = { - /** @type {'eval'} */ - type: 'eval', - worker: workerFormulaIdentifier, - source, - names: codeNames, - values: formulaIdentifiers, - }; - - // Behold, recursion: - // eslint-disable-next-line no-use-before-define - return provideValueForFormula(formula, 'eval-id512', resultName); - }, - - importUnsafe0: async (importPath, resultName) => { - const formula = { - /** @type {'import-unsafe0'} */ - type: 'import-unsafe0', - worker: workerFormulaIdentifier, - importPath, - }; - - // Behold, recursion: - // eslint-disable-next-line no-use-before-define - return provideValueForFormula( - formula, - 'import-unsafe0-id512', - resultName, - ); - }, - - /** - * @param {string} bundleName - * @param {string} resultName - */ - importBundle0: async (bundleName, resultName) => { - const bundleFormulaIdentifier = await E(petStoreP).get(bundleName); - if (bundleFormulaIdentifier === undefined) { - throw new TypeError(`Unknown pet name for bundle: ${bundleName}`); - } - const formula = { - /** @type {'import-bundle0'} */ - type: 'import-bundle0', - worker: workerFormulaIdentifier, - bundle: bundleFormulaIdentifier, - }; - - // Behold, recursion: - // eslint-disable-next-line no-use-before-define - return provideValueForFormula( - formula, - 'import-bundle0-id512', - resultName, - ); - }, }); workerBootstraps.set(worker, workerBootstrap); @@ -392,10 +253,12 @@ const makeEndoBootstrap = ( /** * @param {string} workerFormulaIdentifier + * @param {string} outboxFormulaIdentifier * @param {string} importPath */ const makeValueForImportUnsafe0 = async ( workerFormulaIdentifier, + outboxFormulaIdentifier, importPath, ) => { // Behold, recursion: @@ -405,15 +268,22 @@ const makeEndoBootstrap = ( ); const workerBootstrap = workerBootstraps.get(workerFacet); assert(workerBootstrap); - return E(workerBootstrap).importUnsafe0(importPath); + const outboxP = /** @type {Promise} */ ( + // Behold, recursion: + // eslint-disable-next-line no-use-before-define + provideValueForFormulaIdentifier(outboxFormulaIdentifier) + ); + return E(workerBootstrap).importUnsafe0(importPath, outboxP); }; /** * @param {string} workerFormulaIdentifier + * @param {string} outboxFormulaIdentifier * @param {string} bundleFormulaIdentifier */ const makeValueForImportBundle0 = async ( workerFormulaIdentifier, + outboxFormulaIdentifier, bundleFormulaIdentifier, ) => { // Behold, recursion: @@ -425,16 +295,553 @@ const makeEndoBootstrap = ( assert(workerBootstrap); // Behold, recursion: // eslint-disable-next-line no-use-before-define - const readableBundle = await provideValueForFormulaIdentifier( - bundleFormulaIdentifier, + const readableBundleP = + /** @type {Promise} */ ( + // Behold, recursion: + // eslint-disable-next-line no-use-before-define + provideValueForFormulaIdentifier(bundleFormulaIdentifier) + ); + const outboxP = /** @type {Promise} */ ( + // Behold, recursion: + // eslint-disable-next-line no-use-before-define + provideValueForFormulaIdentifier(outboxFormulaIdentifier) + ); + return E(workerBootstrap).importBundle0(readableBundleP, outboxP); + }; + + /** + * @param {string} outboxFormulaIdentifier + * @param {string} inboxFormulaIdentifier + * @param {string} petStoreFormulaIdentifier + */ + const makeIdentifiedOutbox = async ( + outboxFormulaIdentifier, + inboxFormulaIdentifier, + petStoreFormulaIdentifier, + ) => { + /** @type {Map>} */ + const responses = new Map(); + + const outboxPetStore = /** @type {import('./types.js').PetStore} */ ( + // Behold, recursion: + // eslint-disable-next-line no-use-before-define + await provideValueForFormulaIdentifier(petStoreFormulaIdentifier) + ); + const inbox = /** @type {object} */ ( + // Behold, recursion: + // eslint-disable-next-line no-use-before-define + await provideValueForFormulaIdentifier(inboxFormulaIdentifier) + ); + + const request = inboxRequestFunctions.get(inbox); + if (request === undefined) { + throw new Error( + `Programmer invariant failed: an inbox request function must exist for every inbox`, + ); + } + + /** + * @param {string} petName + */ + const provide = async petName => { + const formulaIdentifier = outboxPetStore.get(petName); + if (formulaIdentifier === undefined) { + throw new TypeError(`Unknown pet name: ${q(petName)}`); + } + // Behold, recursion: + // eslint-disable-next-line no-use-before-define + return provideValueForFormulaIdentifier(formulaIdentifier); + }; + + const { list, remove, rename } = outboxPetStore; + + /** @type {import('@endo/eventual-send').ERef} */ + const outbox = Far('EndoOutbox', { + request: async (what, responseName) => { + if (responseName === undefined) { + // Behold, recursion: + // eslint-disable-next-line no-use-before-define + return request(what, responseName, outbox, outboxPetStore); + } + const responseP = responses.get(responseName); + if (responseP !== undefined) { + return responseP; + } + // Behold, recursion: + // eslint-disable-next-line no-use-before-define + const newResponseP = request( + what, + responseName, + outbox, + outboxPetStore, + ); + responses.set(responseName, newResponseP); + return newResponseP; + }, + list, + remove, + rename, + provide, + }); + + return outbox; + }; + + /** + * @param {string} inboxFormulaIdentifier + * @param {string} storeFormulaIdentifier + */ + const makeIdentifiedInbox = async ( + inboxFormulaIdentifier, + storeFormulaIdentifier, + ) => { + const petStore = /** @type {import('./types.js').PetStore} */ ( + // Behold, recursion: + // eslint-disable-next-line no-use-before-define + await provideValueForFormulaIdentifier(storeFormulaIdentifier) ); - return E(workerBootstrap).importBundle0(readableBundle); + + /** @type {Map} */ + const requests = new Map(); + /** @type {WeakMap void>} */ + const resolvers = new WeakMap(); + /** @type {import('./types.js').Topic} */ + const requestsTopic = makeChangeTopic(); + let nextRequestNumber = 0; + + const listMessages = async () => harden(Array.from(requests.values())); + + const followMessages = async () => + makeIteratorRef( + (async function* currentAndSubsequentMessages() { + const subsequentRequests = requestsTopic.subscribe(); + yield* requests.values(); + yield* subsequentRequests; + })(), + ); + + /** + * @param {string} what - user visible description of the desired value + * @param {unknown} whom - the requester + */ + const requestFormulaIdentifier = async (what, whom) => { + /** @type {import('@endo/promise-kit/src/types.js').PromiseKit} */ + const { promise, resolve } = makePromiseKit(); + const requestNumber = nextRequestNumber; + nextRequestNumber += 1; + const settle = () => { + requests.delete(requestNumber); + }; + const settled = promise.then(settle, settle); + + // How does the receiver know the sender? + const formulaIdentifier = formulaIdentifierForRef.get(whom); + if (formulaIdentifier === undefined) { + throw new Error( + `Programmer invariant failed: requestFormulaIdentifier must be called with an Outbox (who) that was obtained through provideValueFor*`, + ); + } + const [who] = petStore.lookup(formulaIdentifier); + // TODO consider having an invariant that a formula dictionary + // can only have one name for each formula identifier, + // so any attempt to copy a name is effectively enforced as a rename. + + const req = harden({ + type: /** @type {'request'} */ ('request'), + number: requestNumber, + who, + what, + when: new Date().toISOString(), + settled, + }); + + requests.set(requestNumber, req); + resolvers.set(req, resolve); + requestsTopic.publisher.next(req); + return promise; + }; + + /** + * @param {string} what + * @param {string} responseName + * @param {import('./types.js').EndoOutbox} who + * @param {import('./types.js').PetStore} outboxPetStore + */ + const request = async (what, responseName, who, outboxPetStore) => { + if (responseName !== undefined) { + /** @type {string | undefined} */ + let formulaIdentifier = outboxPetStore.get(responseName); + if (formulaIdentifier === undefined) { + formulaIdentifier = await requestFormulaIdentifier(what, who); + await outboxPetStore.write(responseName, formulaIdentifier); + } + // Behold, recursion: + // eslint-disable-next-line no-use-before-define + return provideValueForFormulaIdentifier(formulaIdentifier); + } + // The reference is not named nor to be named. + const formulaIdentifier = await requestFormulaIdentifier(what, who); + // Behold, recursion: + // eslint-disable-next-line no-use-before-define + return provideValueForFormulaIdentifier(formulaIdentifier); + }; + + const resolve = async (requestNumber, resolutionName) => { + if (!validNamePattern.test(resolutionName)) { + throw new Error(`Invalid pet name ${q(resolutionName)}`); + } + if ( + typeof requestNumber !== 'number' || + requestNumber >= Number.MAX_SAFE_INTEGER + ) { + throw new Error(`Invalid request number ${q(requestNumber)}`); + } + const req = requests.get(requestNumber); + const resolveRequest = resolvers.get(req); + if (resolveRequest === undefined) { + throw new Error(`No pending request for number ${requestNumber}`); + } + const formulaIdentifier = petStore.get(resolutionName); + if (formulaIdentifier === undefined) { + throw new TypeError( + `No formula exists for the pet name ${q(resolutionName)}`, + ); + } + resolveRequest(formulaIdentifier); + }; + + // TODO test reject + /** + * @param {number} requestNumber + * @param {string} [message] + */ + const reject = async (requestNumber, message = 'Declined') => { + const req = requests.get(requestNumber); + if (req !== undefined) { + const resolveRequest = resolvers.get(req); + if (resolveRequest === undefined) { + throw new Error( + `Programmer invariant violated: a resolver must exist for every request`, + ); + } + resolveRequest(harden(Promise.reject(harden(new Error(message))))); + } + }; + + /** + * @param {string} petName + */ + const makeOutbox = async petName => { + const outboxStoreId512 = await powers.randomHex512(); + const outboxStoreFormulaIdentifier = `pet-store-id512:${outboxStoreId512}`; + /** @type {import('./types.js').OutboxFormula} */ + const formula = { + type: /* @type {'outbox'} */ 'outbox', + inbox: inboxFormulaIdentifier, + store: outboxStoreFormulaIdentifier, + }; + // Behold, recursion: + // eslint-disable-next-line no-use-before-define + const { value, formulaIdentifier } = await provideValueForFormula( + formula, + 'outbox-id512', + ); + if (petName !== undefined) { + await petStore.write(petName, formulaIdentifier); + } + return /** @type {Promise} */ (value); + }; + + /** + * @param {import('@endo/eventual-send').ERef>} readerRef + * @param {string} [petName] + */ + const store = async (readerRef, petName) => { + if (petName !== undefined) { + if (!validNamePattern.test(petName)) { + throw new Error(`Invalid pet name ${q(petName)}`); + } + } + + const formulaIdentifier = await storeReaderRef(readerRef); + + if (petName !== undefined) { + await petStore.write(petName, formulaIdentifier); + } + }; + + /** + * @param {string} petName + */ + const provide = async petName => { + const formulaIdentifier = petStore.get(petName); + if (formulaIdentifier === undefined) { + throw new TypeError(`Unknown pet name: ${q(petName)}`); + } + // Behold, recursion: + // eslint-disable-next-line no-use-before-define + return provideValueForFormulaIdentifier(formulaIdentifier); + }; + + const lookup = async presence => { + const formulaIdentifier = formulaIdentifierForRef.get(await presence); + if (formulaIdentifier === undefined) { + return []; + } + return E(petStore).lookup(formulaIdentifier); + }; + + /** + * @param {string | undefined} workerName + */ + const provideWorkerFormulaIdentifier = async workerName => { + /** @type {string | undefined} */ + let workerFormulaIdentifier; + if (workerName === undefined) { + const workerId512 = await powers.randomHex512(); + workerFormulaIdentifier = `worker-id512:${workerId512}`; + } else { + workerFormulaIdentifier = petStore.get(workerName); + if (workerFormulaIdentifier === undefined) { + throw new Error(`Unknown worker for pet name: ${q(workerName)}`); + } + } + if (workerFormulaIdentifier === undefined) { + throw new Error( + `Programmer invariant failed: workerFormulaIdentifier must be defined`, + ); + } + return workerFormulaIdentifier; + }; + + /** + * @param {string} outboxName + */ + const provideOutboxFormulaIdentifier = async outboxName => { + let outboxFormulaIdentifier = petStore.get(outboxName); + if (outboxFormulaIdentifier === undefined) { + const outbox = await makeOutbox(outboxName); + outboxFormulaIdentifier = formulaIdentifierForRef.get(outbox); + if (outboxFormulaIdentifier === undefined) { + throw new Error( + `Programmer invariant violated: makeOutbox must return an outbox with a corresponding formula identifier`, + ); + } + } + return outboxFormulaIdentifier; + }; + + /** + * @param {string | undefined} workerName + * @param {string} source + * @param {Array} codeNames + * @param {Array} petNames + * @param {string} resultName + */ + const evaluate = async ( + workerName, + source, + codeNames, + petNames, + resultName, + ) => { + const workerFormulaIdentifier = await provideWorkerFormulaIdentifier( + workerName, + ); + + if (resultName !== undefined && !validNamePattern.test(resultName)) { + throw new Error(`Invalid pet name ${q(resultName)}`); + } + if (petNames.length !== codeNames.length) { + throw new Error('Evaluator requires one pet name for each code name'); + } + + const formulaIdentifiers = await Promise.all( + petNames.map(async (petName, index) => { + assertPetName(petName); + if (typeof codeNames[index] !== 'string') { + throw new Error(`Invalid endowment name: ${q(codeNames[index])}`); + } + const formulaIdentifier = petStore.get(petName); + if (formulaIdentifier === undefined) { + throw new Error(`Unknown pet name ${q(petName)}`); + } + return formulaIdentifier; + }), + ); + + const formula = { + /** @type {'eval'} */ + type: 'eval', + worker: workerFormulaIdentifier, + source, + names: codeNames, + values: formulaIdentifiers, + }; + + // Behold, recursion: + // eslint-disable-next-line no-use-before-define + const { formulaIdentifier, value } = await provideValueForFormula( + formula, + 'eval-id512', + ); + if (resultName !== undefined) { + await petStore.write(resultName, formulaIdentifier); + } + return value; + }; + + /** + * @param {string | undefined} workerName + * @param {string} importPath + * @param {string} outboxName + * @param {string} resultName + */ + const importUnsafe0 = async ( + workerName, + importPath, + outboxName, + resultName, + ) => { + const workerFormulaIdentifier = await provideWorkerFormulaIdentifier( + workerName, + ); + + const outboxFormulaIdentifier = await provideOutboxFormulaIdentifier( + outboxName, + ); + + const formula = { + /** @type {'import-unsafe0'} */ + type: 'import-unsafe0', + worker: workerFormulaIdentifier, + outbox: outboxFormulaIdentifier, + importPath, + }; + + // Behold, recursion: + // eslint-disable-next-line no-use-before-define + const { formulaIdentifier, value } = await provideValueForFormula( + formula, + 'import-unsafe0-id512', + ); + if (resultName !== undefined) { + await petStore.write(resultName, formulaIdentifier); + } + return value; + }; + + /** + * @param {string} workerName + * @param {string} bundleName + * @param {string} outboxName + * @param {string} resultName + */ + const importBundle0 = async ( + workerName, + bundleName, + outboxName, + resultName, + ) => { + const workerFormulaIdentifier = await provideWorkerFormulaIdentifier( + workerName, + ); + + const bundleFormulaIdentifier = petStore.get(bundleName); + if (bundleFormulaIdentifier === undefined) { + throw new TypeError(`Unknown pet name for bundle: ${bundleName}`); + } + + const outboxFormulaIdentifier = await provideOutboxFormulaIdentifier( + outboxName, + ); + + const formula = { + /** @type {'import-bundle0'} */ + type: 'import-bundle0', + worker: workerFormulaIdentifier, + outbox: outboxFormulaIdentifier, + bundle: bundleFormulaIdentifier, + }; + + // Behold, recursion: + // eslint-disable-next-line no-use-before-define + const { value, formulaIdentifier } = await provideValueForFormula( + formula, + 'import-bundle0-id512', + ); + + if (resultName !== undefined) { + await petStore.write(resultName, formulaIdentifier); + } + + return value; + }; + + /** + * @param {string} [petName] + */ + const makeWorker = async petName => { + const workerId512 = await powers.randomHex512(); + const formulaIdentifier = `worker-id512:${workerId512}`; + if (petName !== undefined) { + await petStore.write(petName, formulaIdentifier); + } + return /** @type {Promise} */ ( + // Behold, recursion: + // eslint-disable-next-line no-use-before-define + provideValueForFormulaIdentifier(formulaIdentifier) + ); + }; + + /** + * @param {string} [petName] + */ + const makeInbox = async petName => { + const inboxId512 = await powers.randomHex512(); + const formulaIdentifier = `inbox-id512:${inboxId512}`; + if (petName !== undefined) { + await petStore.write(petName, formulaIdentifier); + } + return /** @type {Promise} */ ( + // Behold, recursion: + // eslint-disable-next-line no-use-before-define + provideValueForFormulaIdentifier(formulaIdentifier) + ); + }; + + const { list, remove, rename } = petStore; + + /** @type {import('./types.js').EndoInbox} */ + const inbox = Far('EndoInbox', { + listMessages, + followMessages, + provide, + resolve, + reject, + lookup, + list, + remove, + rename, + store, + makeOutbox, + makeInbox, + makeWorker, + evaluate, + importUnsafe0, + importBundle0, + }); + + inboxRequestFunctions.set(inbox, request); + + return inbox; }; /** + * @param {string} formulaIdentifier * @param {import('./types.js').Formula} formula */ - const makeValueForFormula = async formula => { + const makeValueForFormula = async (formulaIdentifier, formula) => { if (formula.type === 'eval') { return makeValueForEval( formula.worker, @@ -443,9 +850,23 @@ const makeEndoBootstrap = ( formula.values, ); } else if (formula.type === 'import-unsafe0') { - return makeValueForImportUnsafe0(formula.worker, formula.importPath); + return makeValueForImportUnsafe0( + formula.worker, + formula.outbox, + formula.importPath, + ); } else if (formula.type === 'import-bundle0') { - return makeValueForImportBundle0(formula.worker, formula.bundle); + return makeValueForImportBundle0( + formula.worker, + formula.outbox, + formula.bundle, + ); + } else if (formula.type === 'outbox') { + return makeIdentifiedOutbox( + formulaIdentifier, + formula.inbox, + formula.store, + ); } else { throw new TypeError(`Invalid formula: ${q(formula)}`); } @@ -483,9 +904,10 @@ const makeEndoBootstrap = ( }; /** + * @param {string} formulaIdentifier * @param {string} formulaPath */ - const makeValueForFormulaAtPath = async formulaPath => { + const makeValueForFormulaAtPath = async (formulaIdentifier, formulaPath) => { const formulaText = await powers.readFileText(formulaPath).catch(() => { // TODO handle EMFILE gracefully throw new ReferenceError(`No reference exists at path ${formulaPath}`); @@ -500,13 +922,18 @@ const makeEndoBootstrap = ( } })(); // TODO validate - return makeValueForFormula(formula); + return makeValueForFormula(formulaIdentifier, formula); }; /** * @param {string} formulaIdentifier */ const makeValueForFormulaIdentifier = async formulaIdentifier => { + if (formulaIdentifier === 'pet-store') { + return makeOwnPetStore(powers, locator); + } else if (formulaIdentifier === 'inbox') { + return makeIdentifiedInbox(formulaIdentifier, 'pet-store'); + } const delimiterIndex = formulaIdentifier.indexOf(':'); if (delimiterIndex < 0) { throw new TypeError( @@ -521,13 +948,21 @@ const makeEndoBootstrap = ( return makeIdentifiedWorker(suffix); } else if (prefix === 'pet-store-id512') { return makeIdentifiedPetStore(powers, locator, suffix); + } else if (prefix === 'inbox-id512') { + return makeIdentifiedInbox( + formulaIdentifier, + `pet-store-id512:${suffix}`, + ); } else if ( - ['eval-id512', 'import-unsafe0-id512', 'import-bundle0-id512'].includes( - prefix, - ) + [ + 'eval-id512', + 'import-unsafe0-id512', + 'import-bundle0-id512', + 'outbox-id512', + ].includes(prefix) ) { const { file: path } = makeFormulaPath(prefix, suffix); - return makeValueForFormulaAtPath(path); + return makeValueForFormulaAtPath(formulaIdentifier, path); } else { throw new TypeError( `Invalid formula identifier, unrecognized type ${q(formulaIdentifier)}`, @@ -543,30 +978,25 @@ const makeEndoBootstrap = ( /** * @param {import('./types.js').Formula} formula * @param {string} formulaType - * @param {string} [resultName] */ - const provideValueForFormula = async (formula, formulaType, resultName) => { + const provideValueForFormula = async (formula, formulaType) => { const formulaId512 = await powers.randomHex512(); const formulaIdentifier = `${formulaType}:${formulaId512}`; await writeFormula(formula, formulaType, formulaId512); // Behold, recursion: // eslint-disable-next-line no-use-before-define - const promiseForValue = makeValueForFormula(formula); + const promiseForValue = makeValueForFormula(formulaIdentifier, formula); // Memoize provide. valuePromiseForFormulaIdentifier.set(formulaIdentifier, promiseForValue); - // Prepare an entry for forward-lookup of formula for pet name. - if (resultName !== undefined) { - await E(petStoreP).write(resultName, formulaIdentifier); - } - // Prepare an entry for reverse-lookup of formula for presence. const value = await promiseForValue; if (typeof value === 'object' && value !== null) { formulaIdentifierForRef.set(value, formulaIdentifier); } - return value; + + return { formulaIdentifier, value }; }; /** @@ -586,122 +1016,7 @@ const makeEndoBootstrap = ( return value; }; - /** - * @param {string} petName - */ - const provide = async petName => { - const formulaIdentifier = await E(petStoreP).get(petName); - if (formulaIdentifier === undefined) { - throw new TypeError(`Unknown pet name: ${q(petName)}`); - } - return provideValueForFormulaIdentifier(formulaIdentifier); - }; - - const makePetStore = async petName => { - const petStoreId512 = await powers.randomHex512(); - const formulaIdentifier = `pet-store-id512:${petStoreId512}`; - if (petName !== undefined) { - await E(petStoreP).write(petName, formulaIdentifier); - } - return provideValueForFormulaIdentifier(formulaIdentifier); - }; - - const inbox = async () => makeIteratorRef(requests.values()); - - const followInbox = async () => - makeIteratorRef( - (async function* currentAndSubsequentMessages() { - const subsequentRequests = requestsTopic.subscribe(); - yield* requests.values(); - yield* subsequentRequests; - })(), - ); - - /** - * @param {string} what - user visible description of the desired value - * @param {string} who - formula identifier of the requester - */ - const requestFormulaIdentifier = async (what, who) => { - /** @type {import('@endo/promise-kit/src/types.js').PromiseKit} */ - const { promise, resolve } = makePromiseKit(); - const requestNumber = nextRequestNumber; - nextRequestNumber += 1; - const settle = () => { - requests.delete(requestNumber); - }; - const settled = promise.then(settle, settle); - const req = harden({ - type: /** @type {'request'} */ ('request'), - number: requestNumber, - who, - what, - when: new Date().toISOString(), - settled, - }); - requests.set(requestNumber, req); - resolvers.set(req, resolve); - requestsTopic.publisher.next(req); - return promise; - }; - - /** - * @param {string} what - * @param {string} responseName - * @param {string} fromFormulaIdentifier - * @param {import('./types.js').PetStore} workerPetStore - */ - const request = async ( - what, - responseName, - fromFormulaIdentifier, - workerPetStore, - ) => { - if (responseName !== undefined) { - /** @type {string | undefined} */ - let formulaIdentifier = workerPetStore.get(responseName); - if (formulaIdentifier === undefined) { - formulaIdentifier = await requestFormulaIdentifier( - what, - fromFormulaIdentifier, - ); - await workerPetStore.write(responseName, formulaIdentifier); - } - return provideValueForFormulaIdentifier(formulaIdentifier); - } - // The reference is not named nor to be named. - const formulaIdentifier = await requestFormulaIdentifier( - what, - fromFormulaIdentifier, - ); - return provideValueForFormulaIdentifier(formulaIdentifier); - }; - - const resolve = async (requestNumber, resolutionName) => { - if (!validNamePattern.test(resolutionName)) { - throw new Error(`Invalid pet name ${q(resolutionName)}`); - } - const req = requests.get(requestNumber); - const resolveRequest = resolvers.get(req); - if (resolveRequest === undefined) { - throw new Error(`No pending request for number ${requestNumber}`); - } - const formulaIdentifier = await E(petStoreP).get(resolutionName); - if (formulaIdentifier === undefined) { - throw new TypeError( - `No formula exists for the pet name ${q(resolutionName)}`, - ); - } - resolveRequest(formulaIdentifier); - }; - - const reject = async (requestNumber, message = 'Declined') => { - const req = requests.get(requestNumber); - if (req !== undefined) { - req.resolver.resolve(harden(Promise.reject(harden(new Error(message))))); - } - }; - - return Far('Endo private facet', { + const endoBootstrap = Far('Endo private facet', { // TODO for user named ping: async () => 'pong', @@ -710,29 +1025,10 @@ const makeEndoBootstrap = ( cancel(new Error('Termination requested')); }, - /** - * @param {string} [petName] - */ - makeWorker: async petName => { - const workerId512 = await powers.randomHex512(); - const formulaIdentifier = `worker-id512:${workerId512}`; - if (petName !== undefined) { - await E(petStoreP).write(petName, formulaIdentifier); - } - return provideValueForFormulaIdentifier(formulaIdentifier); - }, - - petStore: () => petStoreP, - makePetStore, - - store, - provide, - inbox, - followInbox, - request, - resolve, - reject, + inbox: () => provideValueForFormulaIdentifier('inbox'), }); + + return endoBootstrap; }; /* diff --git a/packages/daemon/src/pet-store.js b/packages/daemon/src/pet-store.js index 95bab01bb4..88f691e1a4 100644 --- a/packages/daemon/src/pet-store.js +++ b/packages/daemon/src/pet-store.js @@ -5,7 +5,7 @@ const { quote: q } = assert; const validNamePattern = /^[a-zA-Z][a-zA-Z0-9]{0,127}$/; const validIdPattern = /^[0-9a-f]{128}$/; const validFormulaPattern = - /^(?:readable-blob-sha512|worker-id512|pet-store-id512|eval-id512|import-unsafe0-id512|import-bundle0-id512):[0-9a-f]{128}$/; + /^(?:inbox|pet-store|(?:readable-blob-sha512|worker-id512|pet-store-id512|eval-id512|import-unsafe0-id512|import-bundle0-id512|inbox-id512|outbox-id512):[0-9a-f]{128})$/; /** * @param {import('./types.js').DaemonicPowers} powers @@ -70,7 +70,16 @@ const makePetStoreAtPath = async (powers, petNameDirectoryPath) => { if (!validFormulaPattern.test(formulaIdentifier)) { throw new Error(`Invalid formula identifier ${q(formulaIdentifier)}`); } + petNames.set(petName, formulaIdentifier); + + const formulaPetNames = formulaIdentifiers.get(formulaIdentifier); + if (formulaPetNames === undefined) { + formulaIdentifiers.set(formulaIdentifier, new Set([petName])); + } else { + formulaPetNames.add(petName); + } + const petNamePath = powers.joinPath(petNameDirectoryPath, petName); const petNameText = `${formulaIdentifier}\n`; await powers.writeFileText(petNamePath, petNameText); diff --git a/packages/daemon/src/reader-ref.js b/packages/daemon/src/reader-ref.js index 1661e26c94..cdfb62acfe 100644 --- a/packages/daemon/src/reader-ref.js +++ b/packages/daemon/src/reader-ref.js @@ -16,7 +16,8 @@ export const asyncIterate = iterable => { return iterator; }; -export const makeIteratorRef = iterator => { +export const makeIteratorRef = iterable => { + const iterator = asyncIterate(iterable); return Far('AsyncIterator', { async next() { return iterator.next(); diff --git a/packages/daemon/src/types.d.ts b/packages/daemon/src/types.d.ts index 4a0c79f19a..a2199cecb4 100644 --- a/packages/daemon/src/types.d.ts +++ b/packages/daemon/src/types.d.ts @@ -1,3 +1,4 @@ +import type { ERef } from '@endo/eventual-send'; import type { Reader, Writer, Stream } from '@endo/stream'; export type Locator = { @@ -65,6 +66,17 @@ export type MignonicPowers = { }; }; +type InboxFormula = { + type: 'inbox'; + store: string; +}; + +type OutboxFormula = { + type: 'outbox'; + store: string; + inbox: string; +}; + type EvalFormula = { type: 'eval'; worker: string; @@ -77,6 +89,7 @@ type EvalFormula = { type ImportUnsafe0Formula = { type: 'import-unsafe0'; worker: string; + outbox: string; importPath: string; // TODO formula slots }; @@ -84,11 +97,17 @@ type ImportUnsafe0Formula = { type ImportBundle0Formula = { type: 'import-bundle0'; worker: string; + outbox: string; bundle: string; // TODO formula slots }; -export type Formula = EvalFormula | ImportUnsafe0Formula | ImportBundle0Formula; +export type Formula = + | InboxFormula + | OutboxFormula + | EvalFormula + | ImportUnsafe0Formula + | ImportBundle0Formula; export type Label = { number: number; @@ -118,5 +137,68 @@ export interface PetStore { get(petName: string): string | undefined; write(petName: string, formulaIdentifier: string): Promise; list(): Array; + remove(petName: string); + rename(fromPetName: string, toPetName: string); lookup(formulaIdentifier: string): Array; } + +export type RequestFn = ( + what: string, + responseName: string, + outbox: object, + outboxPetStore: PetStore, +) => Promise; + +export interface EndoReadable { + sha512(): string; + stream(): ERef>; + text(): Promise; + [Symbol.asyncIterator]: Reader; +} + +export interface EndoWorker { + terminate(): void; + whenTerminated(): Promise; +} + +export interface EndoOutbox { + request(what: string, responseName: string): Promise; +} + +export interface EndoInbox { + listMessages(): Promise>; + followMessages(): ERef>; + provide(petName: string): Promise; + resolve(requestNumber: number, petName: string); + reject(requestNumber: number, message: string); + lookup(ref: object): Promise>; + remove(petName: string); + rename(fromPetName: string, toPetName: string); + list(): Array; // pet names + store( + readerRef: ERef>, + petName: string, + ): Promise; + makeOutbox(petName?: string): Promise; + makeInbox(petName?: string): Promise; + makeWorker(petName: string): Promise; + evaluate( + workerPetName: string | undefined, + source: string, + codeNames: Array, + petNames: Array, + resultName?: string, + ); + importUnsafe0( + workerPetName: string | undefined, + importPath: string, + outboxName: string, + resultName?: string, + ): Promise; + importBundle0( + workerPetName: string | undefined, + bundleName: string, + outboxName: string, + resultName?: string, + ): Promise; +} diff --git a/packages/daemon/src/worker.js b/packages/daemon/src/worker.js index 58de12d264..fd8d1c50c8 100644 --- a/packages/daemon/src/worker.js +++ b/packages/daemon/src/worker.js @@ -28,13 +28,6 @@ export const makeWorkerFacet = ({ pathToFileURL, cancel, }) => { - const powerBox = Far('EndoPowerBox', { - request: async (what, name) => { - const daemon = getDaemonBootstrap(); - return E(daemon).request(what, name); - }, - }); - return Far('EndoWorkerFacet', { terminate: async () => { console.error('Endo worker received terminate request'); @@ -60,15 +53,20 @@ export const makeWorkerFacet = ({ /** * @param {string} path + * @param {import('@endo/eventual-send').ERef} outboxP */ - importUnsafe0: async path => { + importUnsafe0: async (path, outboxP) => { const url = pathToFileURL(path); const namespace = await import(url); - return namespace.provide0(powerBox); + return namespace.provide0(outboxP); }, - importBundle0: async readable => { - const bundleText = await E(readable).text(); + /** + * @param {import('@endo/eventual-send').ERef} readableP + * @param {import('@endo/eventual-send').ERef} outboxP + */ + importBundle0: async (readableP, outboxP) => { + const bundleText = await E(readableP).text(); const bundle = JSON.parse(bundleText); // We defer importing the import-bundle machinery to this in order to @@ -77,7 +75,7 @@ export const makeWorkerFacet = ({ const namespace = await importBundle(bundle, { endowments, }); - return namespace.provide0(powerBox); + return namespace.provide0(await outboxP); }, }); }; diff --git a/packages/daemon/test/test-endo.js b/packages/daemon/test/test-endo.js index 06e92359db..0724175097 100644 --- a/packages/daemon/test/test-endo.js +++ b/packages/daemon/test/test-endo.js @@ -57,7 +57,8 @@ test('lifecycle', async t => { cancelled, ); const bootstrap = getBootstrap(); - const worker = await E(bootstrap).makeWorker(); + const inbox = E(bootstrap).inbox(); + const worker = await E(inbox).makeWorker(); await E(worker) .terminate() .catch(() => {}); @@ -81,9 +82,30 @@ test('spawn and evaluate', async t => { cancelled, ); const bootstrap = getBootstrap(); + const inbox = E(bootstrap).inbox(); + await E(inbox).makeWorker('w1'); + const ten = await E(inbox).evaluate('w1', '10', [], []); + t.is(10, ten); + + await stop(locator); +}); + +test('anonymous spawn and evaluate', async t => { + const { promise: cancelled } = makePromiseKit(); + const locator = makeLocator('tmp', 'spawn-eval-anon'); + + await stop(locator).catch(() => {}); + await reset(locator); + await start(locator); - const worker = E(bootstrap).makeWorker(); - const ten = await E(worker).evaluate('10', [], []); + const { getBootstrap } = await makeEndoClient( + 'client', + locator.sockPath, + cancelled, + ); + const bootstrap = getBootstrap(); + const inbox = E(bootstrap).inbox(); + const ten = await E(inbox).evaluate(undefined, '10', [], []); t.is(10, ten); await stop(locator); @@ -104,21 +126,24 @@ test('persist spawn and evaluation', async t => { cancelled, ); const bootstrap = getBootstrap(); + const inbox = E(bootstrap).inbox(); - const worker = E(bootstrap).makeWorker(); - const ten = await E(worker).evaluate('10', [], [], 'ten'); + await E(inbox).makeWorker('w1'); + + const ten = await E(inbox).evaluate('w1', '10', [], [], 'ten'); t.is(10, ten); - const twenty = await E(worker).evaluate( + const twenty = await E(inbox).evaluate( + 'w1', 'number * 2', ['number'], ['ten'], 'twenty', ); - // TODO - // Erase the pet name for 'ten', demonstrating that the evaluation record - // does not retain its dependencies by their name. - // await E(worker).forget('ten'); + // Forget the pet name of the intermediate formula, demonstrating that pet + // names are ephemeral but formulas persist as long as their is a retention + // chain among them. + await E(inbox).remove('ten'); t.is(20, twenty); } @@ -133,8 +158,9 @@ test('persist spawn and evaluation', async t => { ); const bootstrap = getBootstrap(); + const inbox = E(bootstrap).inbox(); - const retwenty = await E(bootstrap).provide('twenty'); + const retwenty = await E(inbox).provide('twenty'); t.is(20, retwenty); } @@ -156,8 +182,9 @@ test('store', async t => { cancelled, ); const bootstrap = getBootstrap(); + const inbox = E(bootstrap).inbox(); const readerRef = makeReaderRef([new TextEncoder().encode('hello\n')]); - await E(bootstrap).store(readerRef, 'helloText'); + await E(inbox).store(readerRef, 'helloText'); } { @@ -167,7 +194,8 @@ test('store', async t => { cancelled, ); const bootstrap = getBootstrap(); - const readable = await E(bootstrap).provide('helloText'); + const inbox = E(bootstrap).inbox(); + const readable = await E(inbox).provide('helloText'); const actualText = await E(readable).text(); t.is(actualText, 'hello\n'); } @@ -188,8 +216,11 @@ test('closure state lost by restart', async t => { cancelled, ); const bootstrap = getBootstrap(); - const worker = await E(bootstrap).makeWorker('w1'); - await E(worker).evaluate( + const inbox = E(bootstrap).inbox(); + await E(inbox).makeWorker('w1'); + + await E(inbox).evaluate( + 'w1', ` Far('Counter Maker', { makeCounter: (value = 0) => Far('Counter', { @@ -202,23 +233,27 @@ test('closure state lost by restart', async t => { [], 'counterMaker', ); - await E(worker).evaluate( + await E(inbox).evaluate( + 'w1', `E(cm).makeCounter() `, ['cm'], ['counterMaker'], 'counter', ); - const one = await E(worker).evaluate( + const one = await E(inbox).evaluate( + 'w1', `E(counter).incr()`, ['counter'], ['counter'], ); - const two = await E(worker).evaluate( + const two = await E(inbox).evaluate( + 'w1', `E(counter).incr()`, ['counter'], ['counter'], ); - const three = await E(worker).evaluate( + const three = await E(inbox).evaluate( + 'w1', `E(counter).incr()`, ['counter'], ['counter'], @@ -237,18 +272,22 @@ test('closure state lost by restart', async t => { cancelled, ); const bootstrap = getBootstrap(); - const worker = await E(bootstrap).provide('w1'); - const one = await E(worker).evaluate( + const inbox = E(bootstrap).inbox(); + await E(inbox).provide('w1'); + const one = await E(inbox).evaluate( + 'w1', `E(counter).incr()`, ['counter'], ['counter'], ); - const two = await E(worker).evaluate( + const two = await E(inbox).evaluate( + 'w1', `E(counter).incr()`, ['counter'], ['counter'], ); - const three = await E(worker).evaluate( + const three = await E(inbox).evaluate( + 'w1', `E(counter).incr()`, ['counter'], ['counter'], @@ -277,8 +316,10 @@ test('persist import-unsafe0 services and their requests', async t => { followerCancelled, ); const bootstrap = getBootstrap(); - const worker = await E(bootstrap).makeWorker('userWorker'); - await E(worker).evaluate( + const inbox = E(bootstrap).inbox(); + await E(inbox).makeWorker('userWorker'); + await E(inbox).evaluate( + 'userWorker', ` Far('Answer', { value: () => 42, @@ -288,10 +329,11 @@ test('persist import-unsafe0 services and their requests', async t => { [], 'grant', ); - const iteratorRef = E(bootstrap).followInbox(); + const iteratorRef = E(inbox).followMessages(); const { value: message } = await E(iteratorRef).next(); - const { number } = E.get(message); - await E(bootstrap).resolve(await number, 'grant'); + const { number, who } = E.get(message); + t.is(await who, 'o1'); + await E(inbox).resolve(await number, 'grant'); })(); const workflowFinished = (async () => { @@ -301,12 +343,15 @@ test('persist import-unsafe0 services and their requests', async t => { cancelled, ); const bootstrap = getBootstrap(); - const w1 = await E(bootstrap).makeWorker('w1'); + const inbox = E(bootstrap).inbox(); + await E(inbox).makeWorker('w1'); + await E(inbox).makeOutbox('o1'); const servicePath = path.join(dirname, 'test', 'service.js'); - await E(w1).importUnsafe0(servicePath, 's1'); + await E(inbox).importUnsafe0('w1', servicePath, 'o1', 's1'); - const w2 = await E(bootstrap).makeWorker('w2'); - const answer = await E(w2).evaluate( + await E(inbox).makeWorker('w2'); + const answer = await E(inbox).evaluate( + 'w2', 'E(service).ask()', ['service'], ['s1'], @@ -327,7 +372,8 @@ test('persist import-unsafe0 services and their requests', async t => { cancelled, ); const bootstrap = getBootstrap(); - const answer = await E(bootstrap).provide('answer'); + const inbox = E(bootstrap).inbox(); + const answer = await E(inbox).provide('answer'); const number = await E(answer).value(); t.is(number, 42); } From a83b466294cdc8ec9ed772b451eb181c750104c0 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Tue, 27 Jun 2023 16:17:21 -0700 Subject: [PATCH 042/234] refactor: Endow rather than provide in caplet call convention --- packages/cli/src/endo.js | 12 ++++++------ packages/daemon/src/daemon.js | 32 +++++++++++++++---------------- packages/daemon/src/pet-store.js | 2 +- packages/daemon/src/types.d.ts | 16 ++++++++-------- packages/daemon/src/worker.js | 12 ++++++------ packages/daemon/test/service.js | 2 +- packages/daemon/test/test-endo.js | 6 +++--- 7 files changed, 41 insertions(+), 41 deletions(-) diff --git a/packages/cli/src/endo.js b/packages/cli/src/endo.js index 30f5cae50b..826eff7387 100644 --- a/packages/cli/src/endo.js +++ b/packages/cli/src/endo.js @@ -817,9 +817,9 @@ export const main = async rawArgs => { }); program - .command('import-unsafe0 ') + .command('import-unsafe ') .description( - 'imports the module at the given path and runs its provide0 function with all of your authority', + 'imports the module at the given path and runs its endow function with all of your authority', ) .option('-i,--inbox ', 'An alternate inbox', collect, []) .option( @@ -843,7 +843,7 @@ export const main = async rawArgs => { for (const inboxName of inboxNames) { inbox = E(inbox).provide(inboxName); } - const result = await E(inbox).importUnsafe0( + const result = await E(inbox).importUnsafeAndEndow( workerName, path.resolve(importPath), outboxName, @@ -857,9 +857,9 @@ export const main = async rawArgs => { }); program - .command('import-bundle0 ') + .command('import-bundle ') .description( - 'imports the named bundle in a confined space within a worker and runs its provide0 without any initial authority', + 'imports the named bundle in a confined space within a worker and runs its endow without any initial authority', ) .option('-i,--inbox ', 'An alternate inbox', collect, []) .option( @@ -887,7 +887,7 @@ export const main = async rawArgs => { for (const inboxName of inboxNames) { inbox = E(inbox).provide(inboxName); } - const result = await E(inbox).importBundle0( + const result = await E(inbox).importBundleAndEndow( workerName, readableBundleName, outboxName, diff --git a/packages/daemon/src/daemon.js b/packages/daemon/src/daemon.js index 394e911cf2..76c3ed51e2 100644 --- a/packages/daemon/src/daemon.js +++ b/packages/daemon/src/daemon.js @@ -273,7 +273,7 @@ const makeEndoBootstrap = ( // eslint-disable-next-line no-use-before-define provideValueForFormulaIdentifier(outboxFormulaIdentifier) ); - return E(workerBootstrap).importUnsafe0(importPath, outboxP); + return E(workerBootstrap).importUnsafeAndEndow(importPath, outboxP); }; /** @@ -306,7 +306,7 @@ const makeEndoBootstrap = ( // eslint-disable-next-line no-use-before-define provideValueForFormulaIdentifier(outboxFormulaIdentifier) ); - return E(workerBootstrap).importBundle0(readableBundleP, outboxP); + return E(workerBootstrap).importBundleAndEndow(readableBundleP, outboxP); }; /** @@ -697,7 +697,7 @@ const makeEndoBootstrap = ( * @param {string} outboxName * @param {string} resultName */ - const importUnsafe0 = async ( + const importUnsafeAndEndow = async ( workerName, importPath, outboxName, @@ -712,8 +712,8 @@ const makeEndoBootstrap = ( ); const formula = { - /** @type {'import-unsafe0'} */ - type: 'import-unsafe0', + /** @type {'import-unsafe'} */ + type: 'import-unsafe', worker: workerFormulaIdentifier, outbox: outboxFormulaIdentifier, importPath, @@ -723,7 +723,7 @@ const makeEndoBootstrap = ( // eslint-disable-next-line no-use-before-define const { formulaIdentifier, value } = await provideValueForFormula( formula, - 'import-unsafe0-id512', + 'import-unsafe-id512', ); if (resultName !== undefined) { await petStore.write(resultName, formulaIdentifier); @@ -737,7 +737,7 @@ const makeEndoBootstrap = ( * @param {string} outboxName * @param {string} resultName */ - const importBundle0 = async ( + const importBundleAndEndow = async ( workerName, bundleName, outboxName, @@ -757,8 +757,8 @@ const makeEndoBootstrap = ( ); const formula = { - /** @type {'import-bundle0'} */ - type: 'import-bundle0', + /** @type {'import-bundle'} */ + type: 'import-bundle', worker: workerFormulaIdentifier, outbox: outboxFormulaIdentifier, bundle: bundleFormulaIdentifier, @@ -768,7 +768,7 @@ const makeEndoBootstrap = ( // eslint-disable-next-line no-use-before-define const { value, formulaIdentifier } = await provideValueForFormula( formula, - 'import-bundle0-id512', + 'import-bundle-id512', ); if (resultName !== undefined) { @@ -828,8 +828,8 @@ const makeEndoBootstrap = ( makeInbox, makeWorker, evaluate, - importUnsafe0, - importBundle0, + importUnsafeAndEndow, + importBundleAndEndow, }); inboxRequestFunctions.set(inbox, request); @@ -849,13 +849,13 @@ const makeEndoBootstrap = ( formula.names, formula.values, ); - } else if (formula.type === 'import-unsafe0') { + } else if (formula.type === 'import-unsafe') { return makeValueForImportUnsafe0( formula.worker, formula.outbox, formula.importPath, ); - } else if (formula.type === 'import-bundle0') { + } else if (formula.type === 'import-bundle') { return makeValueForImportBundle0( formula.worker, formula.outbox, @@ -956,8 +956,8 @@ const makeEndoBootstrap = ( } else if ( [ 'eval-id512', - 'import-unsafe0-id512', - 'import-bundle0-id512', + 'import-unsafe-id512', + 'import-bundle-id512', 'outbox-id512', ].includes(prefix) ) { diff --git a/packages/daemon/src/pet-store.js b/packages/daemon/src/pet-store.js index 88f691e1a4..235b0ddad3 100644 --- a/packages/daemon/src/pet-store.js +++ b/packages/daemon/src/pet-store.js @@ -5,7 +5,7 @@ const { quote: q } = assert; const validNamePattern = /^[a-zA-Z][a-zA-Z0-9]{0,127}$/; const validIdPattern = /^[0-9a-f]{128}$/; const validFormulaPattern = - /^(?:inbox|pet-store|(?:readable-blob-sha512|worker-id512|pet-store-id512|eval-id512|import-unsafe0-id512|import-bundle0-id512|inbox-id512|outbox-id512):[0-9a-f]{128})$/; + /^(?:inbox|pet-store|(?:readable-blob-sha512|worker-id512|pet-store-id512|eval-id512|import-unsafe-id512|import-bundle-id512|inbox-id512|outbox-id512):[0-9a-f]{128})$/; /** * @param {import('./types.js').DaemonicPowers} powers diff --git a/packages/daemon/src/types.d.ts b/packages/daemon/src/types.d.ts index a2199cecb4..d293b9cc2b 100644 --- a/packages/daemon/src/types.d.ts +++ b/packages/daemon/src/types.d.ts @@ -86,16 +86,16 @@ type EvalFormula = { // TODO formula slots }; -type ImportUnsafe0Formula = { - type: 'import-unsafe0'; +type ImportUnsafeFormula = { + type: 'import-unsafe'; worker: string; outbox: string; importPath: string; // TODO formula slots }; -type ImportBundle0Formula = { - type: 'import-bundle0'; +type ImportBundleFormula = { + type: 'import-bundle'; worker: string; outbox: string; bundle: string; @@ -106,8 +106,8 @@ export type Formula = | InboxFormula | OutboxFormula | EvalFormula - | ImportUnsafe0Formula - | ImportBundle0Formula; + | ImportUnsafeFormula + | ImportBundleFormula; export type Label = { number: number; @@ -189,13 +189,13 @@ export interface EndoInbox { petNames: Array, resultName?: string, ); - importUnsafe0( + importUnsafeAndEndow( workerPetName: string | undefined, importPath: string, outboxName: string, resultName?: string, ): Promise; - importBundle0( + importBundleAndEndow( workerPetName: string | undefined, bundleName: string, outboxName: string, diff --git a/packages/daemon/src/worker.js b/packages/daemon/src/worker.js index fd8d1c50c8..53635edc96 100644 --- a/packages/daemon/src/worker.js +++ b/packages/daemon/src/worker.js @@ -53,19 +53,19 @@ export const makeWorkerFacet = ({ /** * @param {string} path - * @param {import('@endo/eventual-send').ERef} outboxP + * @param {unknown} powersP */ - importUnsafe0: async (path, outboxP) => { + importUnsafeAndEndow: async (path, powersP) => { const url = pathToFileURL(path); const namespace = await import(url); - return namespace.provide0(outboxP); + return namespace.endow(powersP); }, /** * @param {import('@endo/eventual-send').ERef} readableP - * @param {import('@endo/eventual-send').ERef} outboxP + * @param {unknown} powersP */ - importBundle0: async (readableP, outboxP) => { + importBundleAndEndow: async (readableP, powersP) => { const bundleText = await E(readableP).text(); const bundle = JSON.parse(bundleText); @@ -75,7 +75,7 @@ export const makeWorkerFacet = ({ const namespace = await importBundle(bundle, { endowments, }); - return namespace.provide0(await outboxP); + return namespace.endow(powersP); }, }); }; diff --git a/packages/daemon/test/service.js b/packages/daemon/test/service.js index 1c6937eb09..bb6796cc94 100644 --- a/packages/daemon/test/service.js +++ b/packages/daemon/test/service.js @@ -1,6 +1,6 @@ import { E, Far } from '@endo/far'; -export const provide0 = powers => { +export const endow = powers => { return Far('Service', { async ask() { return E(powers).request( diff --git a/packages/daemon/test/test-endo.js b/packages/daemon/test/test-endo.js index 0724175097..2cae7018df 100644 --- a/packages/daemon/test/test-endo.js +++ b/packages/daemon/test/test-endo.js @@ -298,9 +298,9 @@ test('closure state lost by restart', async t => { } }); -test('persist import-unsafe0 services and their requests', async t => { +test('persist unsafe services and their requests', async t => { const { promise: cancelled } = makePromiseKit(); - const locator = makeLocator('tmp', 'import-unsafe0'); + const locator = makeLocator('tmp', 'import-unsafe'); await stop(locator).catch(() => {}); await reset(locator); @@ -347,7 +347,7 @@ test('persist import-unsafe0 services and their requests', async t => { await E(inbox).makeWorker('w1'); await E(inbox).makeOutbox('o1'); const servicePath = path.join(dirname, 'test', 'service.js'); - await E(inbox).importUnsafe0('w1', servicePath, 'o1', 's1'); + await E(inbox).importUnsafeAndEndow('w1', servicePath, 'o1', 's1'); await E(inbox).makeWorker('w2'); const answer = await E(inbox).evaluate( From 7c64fc28c4679f9bc83cba4a34f9ddffc6bd3df2 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Tue, 27 Jun 2023 15:57:45 -0700 Subject: [PATCH 043/234] feat(daemon): Export reader-ref ref-reader individually --- packages/daemon/index.js | 9 + packages/daemon/package.json | 7 +- packages/daemon/reader-ref.js | 1 + packages/daemon/ref-reader.js | 1 + packages/daemon/src/connection.js | 12 +- packages/daemon/src/daemon-node-powers.js | 192 ++++++++++- packages/daemon/src/daemon-node.js | 18 +- packages/daemon/src/daemon.js | 387 +++++++++++++++++++--- packages/daemon/src/pet-store.js | 7 +- packages/daemon/src/types.d.ts | 50 ++- packages/daemon/src/web-page-bundler.js | 16 + packages/daemon/src/web-page.js | 67 ++++ packages/daemon/src/worker-node-powers.js | 8 - packages/daemon/src/worker-node.js | 10 +- packages/daemon/test/test-endo.js | 1 + 15 files changed, 693 insertions(+), 93 deletions(-) create mode 100644 packages/daemon/reader-ref.js create mode 100644 packages/daemon/ref-reader.js create mode 100644 packages/daemon/src/web-page-bundler.js create mode 100644 packages/daemon/src/web-page.js diff --git a/packages/daemon/index.js b/packages/daemon/index.js index 459f01a0c8..f9d16f9ce6 100644 --- a/packages/daemon/index.js +++ b/packages/daemon/index.js @@ -79,6 +79,11 @@ export const start = async (locator = defaultLocator) => { const logPath = path.join(locator.statePath, 'endo.log'); const output = fs.openSync(logPath, 'a'); + const env = { ...process.env }; + if (locator.httpPort !== undefined) { + env.ENDO_HTTP_PORT = `${locator.httpPort}`; + } + const child = popen.fork( endoDaemonPath, [ @@ -89,6 +94,7 @@ export const start = async (locator = defaultLocator) => { ], { detached: true, + env, stdio: ['ignore', output, output, 'ipc'], }, ); @@ -109,6 +115,9 @@ export const start = async (locator = defaultLocator) => { ); }); child.on('message', _message => { + // This message corresponds to process.send({ type: 'ready' }) in + // src/daemon-node-powers.js and indicates the daemon is ready to receive + // clients. child.disconnect(); child.unref(); resolve(undefined); diff --git a/packages/daemon/package.json b/packages/daemon/package.json index 46f242062f..d98f57bd71 100644 --- a/packages/daemon/package.json +++ b/packages/daemon/package.json @@ -22,6 +22,8 @@ "types": "./types.d.ts", "exports": { ".": "./index.js", + "./ref-reader.js": "./ref-reader.js", + "./reader-ref.js": "./reader-ref.js", "./package.json": "./package.json" }, "scripts": { @@ -38,8 +40,11 @@ "dependencies": { "@endo/base64": "^0.2.34", "@endo/captp": "^3.1.4", + "@endo/compartment-mapper": "^0.9.1", "@endo/eventual-send": "^0.17.5", "@endo/far": "^0.2.21", + "@endo/import-bundle": "^0.4.1", + "@endo/init": "^0.5.59", "@endo/lockdown": "^0.1.31", "@endo/netstring": "^0.3.29", "@endo/promise-kit": "^0.2.59", @@ -47,7 +52,7 @@ "@endo/stream-node": "^0.2.29", "@endo/where": "^0.3.4", "ses": "^0.18.7", - "@endo/import-bundle": "^0.4.1" + "ws": "^8.13.0" }, "devDependencies": { "@endo/init": "^0.5.59", diff --git a/packages/daemon/reader-ref.js b/packages/daemon/reader-ref.js new file mode 100644 index 0000000000..b8ad6d1a7a --- /dev/null +++ b/packages/daemon/reader-ref.js @@ -0,0 +1 @@ +export * from './src/reader-ref.js'; diff --git a/packages/daemon/ref-reader.js b/packages/daemon/ref-reader.js new file mode 100644 index 0000000000..4eb0a72918 --- /dev/null +++ b/packages/daemon/ref-reader.js @@ -0,0 +1 @@ +export * from './src/ref-reader.js'; diff --git a/packages/daemon/src/connection.js b/packages/daemon/src/connection.js index 6bb43113da..1cc2614f09 100644 --- a/packages/daemon/src/connection.js +++ b/packages/daemon/src/connection.js @@ -15,7 +15,13 @@ const textDecoder = new TextDecoder(); * @param {Promise} cancelled * @param {TBootstrap} bootstrap */ -const makeMessageCapTP = (name, writer, reader, cancelled, bootstrap) => { +export const makeMessageCapTP = ( + name, + writer, + reader, + cancelled, + bootstrap, +) => { /** @param {any} message */ const send = message => { return writer.next(message); @@ -41,7 +47,7 @@ const makeMessageCapTP = (name, writer, reader, cancelled, bootstrap) => { }; /** @param {any} message */ -const messageToBytes = message => { +export const messageToBytes = message => { const text = JSON.stringify(message); // console.log('->', text); const bytes = textEncoder.encode(text); @@ -49,7 +55,7 @@ const messageToBytes = message => { }; /** @param {Uint8Array} bytes */ -const bytesToMessage = bytes => { +export const bytesToMessage = bytes => { const text = textDecoder.decode(bytes); // console.log('<-', text); const message = JSON.parse(text); diff --git a/packages/daemon/src/daemon-node-powers.js b/packages/daemon/src/daemon-node-powers.js index 1b84742597..3913d76140 100644 --- a/packages/daemon/src/daemon-node-powers.js +++ b/packages/daemon/src/daemon-node-powers.js @@ -6,6 +6,155 @@ import { makePromiseKit } from '@endo/promise-kit'; import { makePipe } from '@endo/stream'; import { makeNodeReader, makeNodeWriter } from '@endo/stream-node'; +const textEncoder = new TextEncoder(); +const medialIterationResult = harden({ done: false }); +const finalIterationResult = harden({ done: false }); + +/** + * @param {object} modules + * @param {typeof import('ws')} modules.ws + * @param {typeof import('http')} modules.http + */ +export const makeHttpPowers = ({ http, ws }) => { + // @ts-ignore Not sure why TypeScript gets this wrong. + const { WebSocketServer } = ws; + const { createServer } = http; + + /** + * @param {object} args + * @param {number} args.port + * @param {import('./types.js').HttpRespond} [args.respond] + * @param {import('./types.js').HttpConnect} [args.connect] + * @param {Promise} args.cancelled + */ + const serveHttp = async ({ port, respond, connect, cancelled }) => { + const server = createServer(); + + server.on('error', error => { + console.error(error); + }); + + if (respond) { + server.on('request', (req, res) => { + (async () => { + if (req.method === undefined) { + return; + } + if (req.url === undefined) { + return; + } + const response = await respond( + harden({ + method: req.method, + url: req.url, + // TODO Node.js headers are a far more detailed type. + headers: harden( + /** @type {Record | undefined>} */ ( + req.headers + ), + ), + }), + ); + res.writeHead(response.status, response.headers); + if (response.content === undefined) { + res.end(); + } else if ( + typeof response.content === 'string' || + response.content instanceof Uint8Array + ) { + res.end(response.content); + } else { + for await (const chunk of response.content) { + res.write(chunk); + } + res.end(); + } + })(); + }); + } + + if (connect) { + const wss = new WebSocketServer({ server }); + wss.on('connection', (socket, req) => { + const { + promise: closed, + resolve: close, + reject: abort, + } = makePromiseKit(); + + closed.finally(() => socket.close()); + + const [reader, sink] = makePipe(); + + socket.on('message', (bytes, isBinary) => { + if (!isBinary) { + abort(new Error('expected binary')); + return; + } + // TODO Ignoring back-pressure signal: + // Unclear whether WebSocketServer accounts for this possibility. + sink.next(bytes); + }); + socket.on('close', () => { + sink.return(undefined); + socket.close(); + close(finalIterationResult); + }); + + const writer = { + async next(bytes) { + socket.send(bytes, { binary: true }); + return Promise.race([closed, medialIterationResult]); + }, + async return() { + socket.close(); + return Promise.race([closed, medialIterationResult]); + }, + async throw(error) { + socket.close(); + abort(error); + return Promise.race([closed, medialIterationResult]); + }, + [Symbol.asyncIterator]() { + return this; + }, + }; + + connect( + harden({ + reader, + writer, + closed: closed.then(() => {}), + }), + harden({ + method: req.method, + url: req.url, + headers: req.headers, + }), + ); + }); + } + + return new Promise((resolve, reject) => { + server.listen(port, error => { + if (error) { + reject(error); + } else { + cancelled.catch(() => server.close()); + const address = server.address(); + if (address === null || typeof address === 'string') { + reject(new Error('expected listener to be assigned a port')); + } else { + resolve(address.port); + } + } + }); + }); + }; + + return harden({ serveHttp }); +}; + /** * @param {object} modules * @param {typeof import('crypto')} modules.crypto @@ -14,24 +163,34 @@ import { makeNodeReader, makeNodeWriter } from '@endo/stream-node'; * @param {typeof import('path')} modules.path * @param {typeof import('child_process')} modules.popen * @param {typeof import('url')} modules.url + * @param {typeof import('ws')} modules.ws + * @param {typeof import('http')} modules.http + * @param {Record} modules.env + * @param {(pid: number) => void} modules.kill * @returns {import('./types.js').DaemonicPowers} */ -export const makePowers = ({ crypto, net, fs, path: fspath, popen, url }) => { +export const makePowers = ({ + crypto, + net, + fs, + path: fspath, + popen, + url, + http, + ws, + kill, + env, +}) => { /** @param {Error} error */ const sinkError = error => { console.error(error); }; - /** @param {Error} error */ - const exitOnError = error => { - console.error(error); - process.exit(-1); - }; - const makeSha512 = () => { const digester = crypto.createHash('sha512'); return harden({ update: chunk => digester.update(chunk), + updateText: chunk => digester.update(textEncoder.encode(chunk)), digestHex: () => digester.digest('hex'), }); }; @@ -85,12 +244,9 @@ export const makePowers = ({ crypto, net, fs, path: fspath, popen, url }) => { return readFrom; }; - /** - * @param {string} path - */ - const informParentWhenListeningOnPath = path => { + const informParentWhenReady = () => { if (process.send) { - process.send({ type: 'listening', path }); + process.send({ type: 'ready' }); } }; @@ -219,17 +375,20 @@ export const makePowers = ({ crypto, net, fs, path: fspath, popen, url }) => { return { reader, writer, closed, pid: child.pid }; }; - const endoWorkerPath = url.fileURLToPath( + const { fileURLToPath } = url; + + const endoWorkerPath = fileURLToPath( new URL('worker-node.js', import.meta.url), ); return harden({ + kill: pid => kill(pid), + env: { ...env }, sinkError, - exitOnError, makeSha512, randomHex512, listenOnPath, - informParentWhenListeningOnPath, + informParentWhenReady, makeFileReader, makeFileWriter, writeFileText, @@ -242,5 +401,8 @@ export const makePowers = ({ crypto, net, fs, path: fspath, popen, url }) => { delay, makeWorker, endoWorkerPath, + fileURLToPath, + + ...makeHttpPowers({ http, ws }), }); }; diff --git a/packages/daemon/src/daemon-node.js b/packages/daemon/src/daemon-node.js index 3dc79079a4..c94a104b56 100644 --- a/packages/daemon/src/daemon-node.js +++ b/packages/daemon/src/daemon-node.js @@ -12,6 +12,8 @@ import fs from 'fs'; import path from 'path'; import popen from 'child_process'; import url from 'url'; +import http from 'http'; +import * as ws from 'ws'; import { makePromiseKit } from '@endo/promise-kit'; import { main } from './daemon.js'; @@ -36,6 +38,8 @@ const locator = { cachePath, }; +const { env, kill } = process; + const powers = makePowers({ crypto, net, @@ -43,6 +47,10 @@ const powers = makePowers({ path, popen, url, + http, + ws, + env, + kill, }); const { promise: cancelled, reject: cancel } = @@ -52,4 +60,12 @@ const { promise: cancelled, reject: cancel } = process.once('SIGINT', () => cancel(new Error('SIGINT'))); -main(powers, locator, process.pid, cancel, cancelled).catch(powers.exitOnError); +process.exitCode = 1; +main(powers, locator, process.pid, cancel, cancelled).then( + () => { + process.exitCode = 0; + }, + error => { + console.error(error); + }, +); diff --git a/packages/daemon/src/daemon.js b/packages/daemon/src/daemon.js index 76c3ed51e2..7b22966d98 100644 --- a/packages/daemon/src/daemon.js +++ b/packages/daemon/src/daemon.js @@ -9,8 +9,14 @@ import '@endo/lockdown/commit.js'; import { E, Far } from '@endo/far'; import { makePromiseKit } from '@endo/promise-kit'; +import { mapReader, mapWriter } from '@endo/stream'; import { makeChangeTopic } from './pubsub.js'; -import { makeNetstringCapTP } from './connection.js'; +import { + makeMessageCapTP, + makeNetstringCapTP, + messageToBytes, + bytesToMessage, +} from './connection.js'; import { makeRefReader } from './ref-reader.js'; import { makeReaderRef, makeIteratorRef } from './reader-ref.js'; import { makeOwnPetStore, makeIdentifiedPetStore } from './pet-store.js'; @@ -18,10 +24,15 @@ import { makeOwnPetStore, makeIdentifiedPetStore } from './pet-store.js'; const { quote: q } = assert; const validNamePattern = /^[a-zA-Z][a-zA-Z0-9]{0,127}$/; +const zero512 = + '0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'; + +const defaultHttpPort = 8920; // Eight Nine Duo Oh: ENDO. /** * @param {import('./types.js').DaemonicPowers} powers * @param {import('./types.js').Locator} locator + * @param {number} httpPort * @param {object} args * @param {Promise} args.cancelled * @param {(error: Error) => void} args.cancel @@ -31,6 +42,7 @@ const validNamePattern = /^[a-zA-Z][a-zA-Z0-9]{0,127}$/; const makeEndoBootstrap = ( powers, locator, + httpPort, { cancelled, cancel, gracePeriodMs, gracePeriodElapsed }, ) => { /** @type {Map} */ @@ -61,10 +73,14 @@ const makeEndoBootstrap = ( const text = async () => { return powers.readFileText(storagePath); }; + const json = async () => { + return JSON.parse(await text()); + }; return Far(`Readable file with SHA-512 ${sha512.slice(0, 8)}...`, { sha512: () => sha512, stream, text, + json, [Symbol.asyncIterator]: stream, }); }; @@ -432,7 +448,16 @@ const makeEndoBootstrap = ( const settle = () => { requests.delete(requestNumber); }; - const settled = promise.then(settle, settle); + const settled = promise.then( + () => { + settle(); + return /** @type {'fulfilled'} */ ('fulfilled'); + }, + () => { + settle(); + return /** @type {'rejected'} */ ('rejected'); + }, + ); // How does the receiver know the sender? const formulaIdentifier = formulaIdentifierForRef.get(whom); @@ -598,6 +623,7 @@ const makeEndoBootstrap = ( /** @type {string | undefined} */ let workerFormulaIdentifier; if (workerName === undefined) { + // TODO Using worker 0 should be an option, for evaluate formulas. const workerId512 = await powers.randomHex512(); workerFormulaIdentifier = `worker-id512:${workerId512}`; } else { @@ -615,9 +641,12 @@ const makeEndoBootstrap = ( }; /** - * @param {string} outboxName + * @param {string} [outboxName] */ - const provideOutboxFormulaIdentifier = async outboxName => { + const providePowersFormulaIdentifier = async outboxName => { + if (outboxName === undefined) { + return inboxFormulaIdentifier; + } let outboxFormulaIdentifier = petStore.get(outboxName); if (outboxFormulaIdentifier === undefined) { const outbox = await makeOutbox(outboxName); @@ -694,7 +723,7 @@ const makeEndoBootstrap = ( /** * @param {string | undefined} workerName * @param {string} importPath - * @param {string} outboxName + * @param {string | undefined} outboxName * @param {string} resultName */ const importUnsafeAndEndow = async ( @@ -707,7 +736,7 @@ const makeEndoBootstrap = ( workerName, ); - const outboxFormulaIdentifier = await provideOutboxFormulaIdentifier( + const outboxFormulaIdentifier = await providePowersFormulaIdentifier( outboxName, ); @@ -734,7 +763,7 @@ const makeEndoBootstrap = ( /** * @param {string} workerName * @param {string} bundleName - * @param {string} outboxName + * @param {string | undefined} outboxName * @param {string} resultName */ const importBundleAndEndow = async ( @@ -752,7 +781,7 @@ const makeEndoBootstrap = ( throw new TypeError(`Unknown pet name for bundle: ${bundleName}`); } - const outboxFormulaIdentifier = await provideOutboxFormulaIdentifier( + const outboxFormulaIdentifier = await providePowersFormulaIdentifier( outboxName, ); @@ -810,6 +839,48 @@ const makeEndoBootstrap = ( ); }; + /** + * @param {string} webPageName + * @param {string} bundleName + * @param {string | undefined} powersName + */ + const provideWebPage = async (webPageName, bundleName, powersName) => { + const bundleFormulaIdentifier = petStore.get(bundleName); + if (bundleFormulaIdentifier === undefined) { + throw new Error(`Unknown pet name: ${q(bundleName)}`); + } + + const powersFormulaIdentifier = await providePowersFormulaIdentifier( + powersName, + ); + + const digester = powers.makeSha512(); + digester.updateText( + `${bundleFormulaIdentifier},${powersFormulaIdentifier}`, + ); + const formulaNumber = digester.digestHex().slice(32, 64); + + const formula = { + type: 'web-bundle', + bundle: bundleFormulaIdentifier, + powers: powersFormulaIdentifier, + }; + + // Behold, recursion: + // eslint-disable-next-line no-use-before-define + const { value, formulaIdentifier } = await provideValueForNumberedFormula( + 'web-bundle', + formulaNumber, + formula, + ); + + if (webPageName !== undefined) { + await petStore.write(webPageName, formulaIdentifier); + } + + return value; + }; + const { list, remove, rename } = petStore; /** @type {import('./types.js').EndoInbox} */ @@ -830,6 +901,7 @@ const makeEndoBootstrap = ( evaluate, importUnsafeAndEndow, importBundleAndEndow, + provideWebPage, }); inboxRequestFunctions.set(inbox, request); @@ -839,9 +911,14 @@ const makeEndoBootstrap = ( /** * @param {string} formulaIdentifier + * @param {string} formulaNumber * @param {import('./types.js').Formula} formula */ - const makeValueForFormula = async (formulaIdentifier, formula) => { + const makeValueForFormula = async ( + formulaIdentifier, + formulaNumber, + formula, + ) => { if (formula.type === 'eval') { return makeValueForEval( formula.worker, @@ -867,6 +944,16 @@ const makeEndoBootstrap = ( formula.inbox, formula.store, ); + } else if (formula.type === 'web-bundle') { + return harden({ + url: `http://${formulaNumber}.endo.localhost:${httpPort}`, + // Behold, recursion: + // eslint-disable-next-line no-use-before-define + bundle: provideValueForFormulaIdentifier(formula.bundle), + // Behold, recursion: + // eslint-disable-next-line no-use-before-define + powers: provideValueForFormulaIdentifier(formula.powers), + }); } else { throw new TypeError(`Invalid formula: ${q(formula)}`); } @@ -899,15 +986,21 @@ const makeEndoBootstrap = ( // Persist instructions for revival (this can be collected) const writeFormula = async (formula, formulaType, formulaId512) => { const { directory, file } = makeFormulaPath(formulaType, formulaId512); + // TODO Take care to write atomically with a rename here. await powers.makePath(directory); await powers.writeFileText(file, `${q(formula)}\n`); }; /** * @param {string} formulaIdentifier + * @param {string} formulaNumber * @param {string} formulaPath */ - const makeValueForFormulaAtPath = async (formulaIdentifier, formulaPath) => { + const makeValueForFormulaAtPath = async ( + formulaIdentifier, + formulaNumber, + formulaPath, + ) => { const formulaText = await powers.readFileText(formulaPath).catch(() => { // TODO handle EMFILE gracefully throw new ReferenceError(`No reference exists at path ${formulaPath}`); @@ -922,36 +1015,45 @@ const makeEndoBootstrap = ( } })(); // TODO validate - return makeValueForFormula(formulaIdentifier, formula); + return makeValueForFormula(formulaIdentifier, formulaNumber, formula); }; /** * @param {string} formulaIdentifier */ const makeValueForFormulaIdentifier = async formulaIdentifier => { - if (formulaIdentifier === 'pet-store') { - return makeOwnPetStore(powers, locator); - } else if (formulaIdentifier === 'inbox') { - return makeIdentifiedInbox(formulaIdentifier, 'pet-store'); - } const delimiterIndex = formulaIdentifier.indexOf(':'); if (delimiterIndex < 0) { + if (formulaIdentifier === 'pet-store') { + return makeOwnPetStore(powers, locator, 'pet-store'); + } else if (formulaIdentifier === 'inbox') { + return makeIdentifiedInbox(formulaIdentifier, 'pet-store'); + } else if (formulaIdentifier === 'web-page-js') { + return makeValueForFormula('web-page-js', zero512, { + type: /** @type {'import-unsafe'} */ ('import-unsafe'), + worker: `worker-id512:${zero512}`, + outbox: 'inbox', + importPath: powers.fileURLToPath( + new URL('web-page-bundler.js', import.meta.url).href, + ), + }); + } throw new TypeError( `Formula identifier must have a colon: ${q(formulaIdentifier)}`, ); } const prefix = formulaIdentifier.slice(0, delimiterIndex); - const suffix = formulaIdentifier.slice(delimiterIndex + 1); + const formulaNumber = formulaIdentifier.slice(delimiterIndex + 1); if (prefix === 'readable-blob-sha512') { - return makeSha512ReadableBlob(suffix); + return makeSha512ReadableBlob(formulaNumber); } else if (prefix === 'worker-id512') { - return makeIdentifiedWorker(suffix); + return makeIdentifiedWorker(formulaNumber); } else if (prefix === 'pet-store-id512') { - return makeIdentifiedPetStore(powers, locator, suffix); + return makeIdentifiedPetStore(powers, locator, formulaNumber); } else if (prefix === 'inbox-id512') { return makeIdentifiedInbox( formulaIdentifier, - `pet-store-id512:${suffix}`, + `pet-store-id512:${formulaNumber}`, ); } else if ( [ @@ -959,10 +1061,11 @@ const makeEndoBootstrap = ( 'import-unsafe-id512', 'import-bundle-id512', 'outbox-id512', + 'web-bundle', ].includes(prefix) ) { - const { file: path } = makeFormulaPath(prefix, suffix); - return makeValueForFormulaAtPath(formulaIdentifier, path); + const { file: path } = makeFormulaPath(prefix, formulaNumber); + return makeValueForFormulaAtPath(formulaIdentifier, formulaNumber, path); } else { throw new TypeError( `Invalid formula identifier, unrecognized type ${q(formulaIdentifier)}`, @@ -975,17 +1078,21 @@ const makeEndoBootstrap = ( // valuePromiseForFormulaIdentifier and formulaIdentifierForRef, since the // former bypasses the latter in order to avoid a round trip with disk. - /** - * @param {import('./types.js').Formula} formula - * @param {string} formulaType - */ - const provideValueForFormula = async (formula, formulaType) => { - const formulaId512 = await powers.randomHex512(); - const formulaIdentifier = `${formulaType}:${formulaId512}`; - await writeFormula(formula, formulaType, formulaId512); + const provideValueForNumberedFormula = async ( + formulaType, + formulaNumber, + formula, + ) => { + const formulaIdentifier = `${formulaType}:${formulaNumber}`; + + await writeFormula(formula, formulaType, formulaNumber); // Behold, recursion: // eslint-disable-next-line no-use-before-define - const promiseForValue = makeValueForFormula(formulaIdentifier, formula); + const promiseForValue = makeValueForFormula( + formulaIdentifier, + formulaNumber, + formula, + ); // Memoize provide. valuePromiseForFormulaIdentifier.set(formulaIdentifier, promiseForValue); @@ -999,6 +1106,15 @@ const makeEndoBootstrap = ( return { formulaIdentifier, value }; }; + /** + * @param {import('./types.js').Formula} formula + * @param {string} formulaType + */ + const provideValueForFormula = async (formula, formulaType) => { + const formulaNumber = await powers.randomHex512(); + return provideValueForNumberedFormula(formulaType, formulaNumber, formula); + }; + /** * @param {string} formulaIdentifier */ @@ -1026,6 +1142,21 @@ const makeEndoBootstrap = ( }, inbox: () => provideValueForFormulaIdentifier('inbox'), + + webPageJs: () => provideValueForFormulaIdentifier('web-page-js'), + + importAndEndowInWebPage: async (webPageP, webPageNumber) => { + const { bundle: bundleBlob, powers: endowedPowers } = + /** @type {import('./types.js').EndoWebBundle} */ ( + await provideValueForFormulaIdentifier( + `web-bundle:${webPageNumber}`, + ).catch(() => { + throw new Error('Not found'); + }) + ); + const bundle = await E(bundleBlob).json(); + await E(webPageP).importBundleAndEndow(bundle, endowedPowers); + }, }); return endoBootstrap; @@ -1061,7 +1192,147 @@ export const main = async (powers, locator, pid, cancel, cancelled) => { throw error; }); - const endoBootstrap = makeEndoBootstrap(powers, locator, { + /** @param {Error} error */ + const exitWithError = error => { + cancel(error); + cancelGracePeriod(error); + }; + + let nextConnectionNumber = 0; + /** @type {Set>} */ + const connectionClosedPromises = new Set(); + + const respond = async request => { + if (request.method === 'GET') { + if (request.url === '/') { + return { + status: 200, + headers: { 'Content-Type': 'text/html', Charset: 'utf-8' }, + content: `\ + + + +

+ +`, + }; + } else if (request.url === '/bootstrap.js') { + // TODO readable mutable file formula (with watcher?) + // Behold, recursion: + // eslint-disable-next-line no-use-before-define + const webPageJs = await E(endoBootstrap).webPageJs(); + return { + status: 200, + headers: { 'Content-Type': 'application/javascript' }, + content: webPageJs, + }; + } + } + return { + status: 404, + headers: { 'Content-Type': 'text/plain' }, + content: `Not found: ${request.method} ${request.url}`, + }; + }; + + const requestedHttpPortString = powers.env.ENDO_HTTP_PORT; + const requestedHttpPort = requestedHttpPortString + ? Number(requestedHttpPortString) + : defaultHttpPort; + + const httpReadyP = powers.serveHttp({ + port: requestedHttpPort, + respond, + connect(connection, request) { + // TODO select attenuated bootstrap based on subdomain + (async () => { + const { + reader: frameReader, + writer: frameWriter, + closed: connectionClosed, + } = connection; + + const connectionNumber = nextConnectionNumber; + nextConnectionNumber += 1; + console.log( + `Endo daemon received local web socket connection ${connectionNumber} at ${new Date().toISOString()}`, + ); + + const messageWriter = mapWriter(frameWriter, messageToBytes); + const messageReader = mapReader(frameReader, bytesToMessage); + + const { closed: capTpClosed, getBootstrap } = makeMessageCapTP( + 'Endo', + messageWriter, + messageReader, + cancelled, + undefined, // bootstrap + ); + + const webBootstrap = getBootstrap(); + + // TODO set up heart monitor + E.sendOnly(webBootstrap).ping(); + + const host = request.headers.host; + if (host === undefined) { + throw new Error('Host header required'); + } + const match = + /^([0-9a-f]{32})\.endo\.localhost:([1-9][0-9]{0,4})$/.exec(host); + if (match === null) { + throw new Error(`Invalid host ${host}`); + } + const [_, formulaNumber, portNumber] = match; + // eslint-disable-next-line no-use-before-define + if (assignedHttpPort !== +portNumber) { + console.error( + 'Connected browser misreported port number in host header', + ); + E(webBootstrap) + .reject( + 'Your browser misreported your port number in the host header', + ) + .catch(error => { + console.error(error); + }); + } else { + // eslint-disable-next-line no-use-before-define + E(endoBootstrap) + .importAndEndowInWebPage(webBootstrap, formulaNumber) + .catch(error => { + E.sendOnly(webBootstrap).reject(error.message); + }) + .catch(error => { + console.error(error); + }); + } + + const closed = Promise.race([connectionClosed, capTpClosed]); + connectionClosedPromises.add(closed); + closed.finally(() => { + connectionClosedPromises.delete(closed); + console.log( + `Endo daemon closed local web socket connection ${connectionNumber} at ${new Date().toISOString()}`, + ); + }); + })().catch(exitWithError); + }, + cancelled, + }); + + const connectionsP = powers.listenOnPath(locator.sockPath, cancelled); + + await Promise.all([connectionsP, httpReadyP]); + + const assignedHttpPort = await httpReadyP; + console.log( + `Endo daemon listening for HTTP on ${q( + assignedHttpPort, + )} ${new Date().toISOString()}`, + ); + + const endoBootstrap = makeEndoBootstrap(powers, locator, assignedHttpPort, { cancelled, cancel, gracePeriodMs, @@ -1074,29 +1345,37 @@ export const main = async (powers, locator, pid, cancel, cancelled) => { await Promise.all([statePathP, cachePathP, ephemeralStatePathP]); const pidPath = powers.joinPath(locator.ephemeralStatePath, 'endo.pid'); + + await powers + .readFileText(pidPath) + .then(pidText => { + const oldPid = Number(pidText); + powers.kill(oldPid); + }) + .catch(() => {}); + await powers.writeFileText(pidPath, `${pid}\n`); - const connections = await powers.listenOnPath(locator.sockPath, cancelled); + const connections = await connectionsP; // Resolve a promise in the Endo CLI through the IPC channel: - powers.informParentWhenListeningOnPath(locator.sockPath); console.log( - `Endo daemon listening on ${q( + `Endo daemon listening for CapTP on ${q( locator.sockPath, )} ${new Date().toISOString()}`, ); - let nextConnectionNumber = 0; - /** @type {Set>} */ - const connectionClosedPromises = new Set(); - try { - for await (const { - reader, - writer, - closed: connectionClosed, - } of connections) { + + powers.informParentWhenReady(); + + for await (const { + reader, + writer, + closed: connectionClosed, + } of connections) { + (async () => { const connectionNumber = nextConnectionNumber; nextConnectionNumber += 1; console.log( - `Endo daemon received connection ${connectionNumber} at ${new Date().toISOString()}`, + `Endo daemon received domain connection ${connectionNumber} at ${new Date().toISOString()}`, ); const { closed: capTpClosed } = makeNetstringCapTP( @@ -1112,16 +1391,14 @@ export const main = async (powers, locator, pid, cancel, cancelled) => { closed.finally(() => { connectionClosedPromises.delete(closed); console.log( - `Endo daemon closed connection ${connectionNumber} at ${new Date().toISOString()}`, + `Endo daemon closed domain connection ${connectionNumber} at ${new Date().toISOString()}`, ); }); - } - } catch (error) { - cancel(error); - cancelGracePeriod(error); - } finally { - await Promise.all(Array.from(connectionClosedPromises)); - cancel(new Error('Terminated normally')); - cancelGracePeriod(new Error('Terminated normally')); + })().catch(exitWithError); } + + await Promise.all(Array.from(connectionClosedPromises)); + + cancel(new Error('Terminated normally')); + cancelGracePeriod(new Error('Terminated normally')); }; diff --git a/packages/daemon/src/pet-store.js b/packages/daemon/src/pet-store.js index 235b0ddad3..1eb0215148 100644 --- a/packages/daemon/src/pet-store.js +++ b/packages/daemon/src/pet-store.js @@ -5,7 +5,7 @@ const { quote: q } = assert; const validNamePattern = /^[a-zA-Z][a-zA-Z0-9]{0,127}$/; const validIdPattern = /^[0-9a-f]{128}$/; const validFormulaPattern = - /^(?:inbox|pet-store|(?:readable-blob-sha512|worker-id512|pet-store-id512|eval-id512|import-unsafe-id512|import-bundle-id512|inbox-id512|outbox-id512):[0-9a-f]{128})$/; + /^(?:inbox|pet-store|(?:readable-blob-sha512|worker-id512|pet-store-id512|eval-id512|import-unsafe-id512|import-bundle-id512|inbox-id512|outbox-id512):[0-9a-f]{128}|web-bundle:[0-9a-f]{32})$/; /** * @param {import('./types.js').DaemonicPowers} powers @@ -214,8 +214,9 @@ export const makeIdentifiedPetStore = (powers, locator, id) => { /** * @param {import('./types.js').DaemonicPowers} powers * @param {import('./types.js').Locator} locator + * @param {string} name */ -export const makeOwnPetStore = (powers, locator) => { - const petNameDirectoryPath = powers.joinPath(locator.statePath, 'pet-store'); +export const makeOwnPetStore = (powers, locator, name) => { + const petNameDirectoryPath = powers.joinPath(locator.statePath, name); return makePetStoreAtPath(powers, petNameDirectoryPath); }; diff --git a/packages/daemon/src/types.d.ts b/packages/daemon/src/types.d.ts index d293b9cc2b..2c9bcd7f7b 100644 --- a/packages/daemon/src/types.d.ts +++ b/packages/daemon/src/types.d.ts @@ -3,6 +3,7 @@ import type { Reader, Writer, Stream } from '@endo/stream'; export type Locator = { statePath: string; + httpPort?: number; ephemeralStatePath: string; cachePath: string; sockPath: string; @@ -10,6 +11,7 @@ export type Locator = { export type Sha512 = { update: (chunk: Uint8Array) => void; + updateText: (chunk: string) => void; digestHex: () => string; }; @@ -23,16 +25,40 @@ export type Worker = Connection & { pid: number | undefined; }; +export type HttpRequest = { + method: string; + url: string; + headers: Record | undefined>; +}; + +export type HttpResponse = { + status: number; + headers: Record; + content: AsyncIterable | string | Uint8Array | undefined; +}; + +export type HttpRespond = (request: HttpRequest) => Promise; +export type HttpConnect = ( + connection: Connection, + request: HttpRequest, +) => void; + export type DaemonicPowers = { + env: Record; sinkError: (error) => void; - exitOnError: (error) => void; makeSha512: () => Sha512; randomHex512: () => Promise; listenOnPath: ( path: string, cancelled: Promise, ) => Promise>; - informParentWhenListeningOnPath: (path: string) => void; + serveHttp: (args: { + port: number; + respond?: HttpRespond; + connect?: HttpConnect; + cancelled: Promise; + }) => void; + informParentWhenReady: () => void; makeFileReader: (path: string) => Reader; makeFileWriter: (path: string) => Writer; readFileText: (path: string) => Promise; @@ -55,10 +81,10 @@ export type DaemonicPowers = { cancelled: Promise, ) => Promise; endoWorkerPath: string; + fileURLToPath: (url: string) => string; }; export type MignonicPowers = { - exitOnError: (error) => void; pathToFileURL: (path: string) => string; connection: { reader: Reader; @@ -102,12 +128,19 @@ type ImportBundleFormula = { // TODO formula slots }; +type WebBundleFormula = { + type: 'web-bundle'; + bundle: string; + powers: string; +}; + export type Formula = | InboxFormula | OutboxFormula | EvalFormula | ImportUnsafeFormula - | ImportBundleFormula; + | ImportBundleFormula + | WebBundleFormula; export type Label = { number: number; @@ -118,7 +151,7 @@ export type Label = { export type Request = { type: 'request'; what: string; - settled: Promise; + settled: Promise<'fulfilled' | 'rejected'>; }; export type Message = Label & Request; @@ -153,6 +186,7 @@ export interface EndoReadable { sha512(): string; stream(): ERef>; text(): Promise; + json(): Promise; [Symbol.asyncIterator]: Reader; } @@ -202,3 +236,9 @@ export interface EndoInbox { resultName?: string, ): Promise; } + +export type EndoWebBundle = { + url: string; + bundle: ERef; + powers: ERef; +}; diff --git a/packages/daemon/src/web-page-bundler.js b/packages/daemon/src/web-page-bundler.js new file mode 100644 index 0000000000..14da52df34 --- /dev/null +++ b/packages/daemon/src/web-page-bundler.js @@ -0,0 +1,16 @@ +// This is a built-in unsafe plugin for lazily constructing the web-page.js +// bundle for booting up web caplets. +// The hard-coded 'web-page-js' formula is a hard-coded 'import-unsafe' formula +// that runs this program in worker 0. +// It does not accept its endowed powers. + +import 'ses'; +import fs from 'node:fs'; +import { makeBundle } from '@endo/compartment-mapper/bundle.js'; +import { fileURLToPath } from 'url'; + +const read = async location => fs.promises.readFile(fileURLToPath(location)); + +export const endow = async () => { + return makeBundle(read, new URL('web-page.js', import.meta.url).href); +}; diff --git a/packages/daemon/src/web-page.js b/packages/daemon/src/web-page.js new file mode 100644 index 0000000000..fd844f2f2b --- /dev/null +++ b/packages/daemon/src/web-page.js @@ -0,0 +1,67 @@ +/* global window, document */ + +import '@endo/init/debug.js'; +import { makeCapTP } from '@endo/captp'; +import { E, Far } from '@endo/far'; +import { importBundle } from '@endo/import-bundle'; + +const hardenedEndowments = harden({ + assert, + E, + Far, + TextEncoder, + TextDecoder, + URL, +}); + +const endowments = Object.freeze({ + ...hardenedEndowments, + window, + document, + console, +}); + +const url = new URL('/', window.location); +url.protocol = 'ws'; + +const bootstrap = Far('WebFacet', { + ping() { + console.log('received ping'); + return 'pong'; + }, + async importBundleAndEndow(bundle, powers) { + const namespace = await importBundle(bundle, { + endowments, + }); + return namespace.endow(powers); + }, + reject(message) { + document.body.innerHTML = ''; + const $title = document.createElement('h1'); + $title.innerText = `💔 ${message}`; + document.body.appendChild($title); + }, +}); + +const textEncoder = new TextEncoder(); +const textDecoder = new TextDecoder(); + +const ws = new WebSocket(url.href); +ws.binaryType = 'arraybuffer'; +ws.addEventListener('open', () => { + const send = message => { + // console.log('send', message); + ws.send(textEncoder.encode(JSON.stringify(message))); + }; + const { dispatch, abort } = makeCapTP('WebClient', send, bootstrap); + ws.addEventListener('message', event => { + const message = JSON.parse(textDecoder.decode(event.data)); + // console.log('received', message); + dispatch(message); + }); + ws.addEventListener('close', () => { + abort(); + }); +}); + +document.body.innerHTML = '

⌛️

'; diff --git a/packages/daemon/src/worker-node-powers.js b/packages/daemon/src/worker-node-powers.js index 2161a0f89c..4026e4b04d 100644 --- a/packages/daemon/src/worker-node-powers.js +++ b/packages/daemon/src/worker-node-powers.js @@ -1,5 +1,4 @@ // @ts-check -/* global process */ import { makeNodeReader, makeNodeWriter } from '@endo/stream-node'; @@ -10,12 +9,6 @@ import { makeNodeReader, makeNodeWriter } from '@endo/stream-node'; * @returns {import('./types.js').MignonicPowers} */ export const makePowers = ({ fs, url }) => { - /** @param {Error} error */ - const exitOnError = error => { - console.error(error); - process.exit(-1); - }; - // @ts-ignore This is in fact how you open a file descriptor. const reader = makeNodeReader(fs.createReadStream(null, { fd: 3 })); // @ts-ignore This is in fact how you open a file descriptor. @@ -29,7 +22,6 @@ export const makePowers = ({ fs, url }) => { const { pathToFileURL } = url; return harden({ - exitOnError, connection, pathToFileURL: path => pathToFileURL(path).toString(), }); diff --git a/packages/daemon/src/worker-node.js b/packages/daemon/src/worker-node.js index a3c12fba24..f5e8221610 100644 --- a/packages/daemon/src/worker-node.js +++ b/packages/daemon/src/worker-node.js @@ -41,6 +41,12 @@ const { promise: cancelled, reject: cancel } = process.once('SIGINT', () => cancel(new Error('SIGINT'))); -main(powers, locator, workerUuid, process.pid, cancel, cancelled).catch( - powers.exitOnError, +process.exitCode = 1; +main(powers, locator, workerUuid, process.pid, cancel, cancelled).then( + () => { + process.exitCode = 0; + }, + error => { + console.error(error); + }, ); diff --git a/packages/daemon/test/test-endo.js b/packages/daemon/test/test-endo.js index 2cae7018df..4ffb9e9da2 100644 --- a/packages/daemon/test/test-endo.js +++ b/packages/daemon/test/test-endo.js @@ -28,6 +28,7 @@ const dirname = url.fileURLToPath(new URL('..', import.meta.url)).toString(); /** @param {Array} root */ const makeLocator = (...root) => { return { + httpPort: 0, statePath: path.join(dirname, ...root, 'state'), ephemeralStatePath: path.join(dirname, ...root, 'run'), cachePath: path.join(dirname, ...root, 'cache'), From 5b6a00acd31cf98fb8076f275c62244bffea4a0d Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Tue, 27 Jun 2023 16:11:33 -0700 Subject: [PATCH 044/234] feat(cli): Add open command --- packages/cli/package.json | 3 +- packages/cli/src/endo.js | 78 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 80 insertions(+), 1 deletion(-) diff --git a/packages/cli/package.json b/packages/cli/package.json index 291f86059a..a63f3cf1de 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -36,7 +36,8 @@ "@endo/where": "^0.3.4", "@endo/bundle-source": "^2.7.0", "commander": "^5.0.0", - "ses": "^0.18.7" + "ses": "^0.18.7", + "open": "^9.1.0" }, "devDependencies": { "ava": "^5.3.0", diff --git a/packages/cli/src/endo.js b/packages/cli/src/endo.js index 826eff7387..c3dfc4598d 100644 --- a/packages/cli/src/endo.js +++ b/packages/cli/src/endo.js @@ -12,6 +12,7 @@ import url from 'url'; import crypto from 'crypto'; import { spawn } from 'child_process'; import os from 'os'; +import open from 'open'; import { Command } from 'commander'; import { makePromiseKit } from '@endo/promise-kit'; @@ -900,6 +901,83 @@ export const main = async rawArgs => { } }); + program + .command('open ') + .description('opens a web page') + .option('-i,--inbox ', 'An alternate inbox', collect, []) + .option('-b,--bundle ', 'Bundle for a web page to open') + .option('-f,--file ', 'Build the named bundle from JavaScript file') + .option('-g,--guest ', 'Endowment to give the web page') + .option('-h,--host', 'Endow the web page with the powers of the host') + .action(async (webPageName, cmd) => { + const { + inbox: inboxNames, + file: programPath, + bundle: bundleName, + guest: guestName, + host: endowHost, + } = cmd.opts(); + const { getBootstrap } = await provideEndoClient( + 'cli', + sockPath, + cancelled, + ); + + /** @type {import('@endo/eventual-send').ERef> | undefined} */ + let bundleReaderRef; + if (programPath !== undefined) { + if (bundleName === undefined) { + // TODO come up with something for a temporary reference. + // Maybe expose a nonce. + throw new Error('bundle name is required for page from file'); + } + const bundle = await bundleSource(programPath); + console.log(bundle.endoZipBase64Sha512); + const bundleText = JSON.stringify(bundle); + const bundleBytes = textEncoder.encode(bundleText); + bundleReaderRef = makeReaderRef([bundleBytes]); + } + + try { + const bootstrap = getBootstrap(); + let inbox = E(bootstrap).inbox(); + for (const inboxName of inboxNames) { + inbox = E(inbox).provide(inboxName); + } + + // Prepare a bundle, with the given name. + if (bundleReaderRef !== undefined) { + await E(inbox).store(bundleReaderRef, bundleName); + } + + /** @type {string | undefined} */ + let webPageUrl; + if (guestName !== undefined && endowHost) { + throw new Error('choose either guest or host endowment, not both'); + } else if ( + bundleName !== undefined && + (guestName !== undefined || endowHost) + ) { + ({ url: webPageUrl } = await E(inbox).provideWebPage( + webPageName, + bundleName, + guestName, // undefined if endowHost + )); + } else if (bundleName === undefined && guestName === undefined) { + ({ url: webPageUrl } = await E(inbox).provide(webPageName)); + } else if (webPageUrl === undefined) { + // webPageUrl will always be undefined if we fall through to here, + // but calling it out helps narrow the type below. + throw new Error('both or neither bundle and endowment required'); + } + console.log(webPageUrl); + open(webPageUrl); + } catch (error) { + console.error(error); + cancel(error); + } + }); + // Throw an error instead of exiting directly. program.exitOverride(); From 9ca4b48df064efcd5437e0d316636ca2423fd1bb Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Wed, 28 Jun 2023 16:47:53 -0700 Subject: [PATCH 045/234] docs(cli): Add familiar chat demo --- packages/cli/demo/cat.js | 98 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 packages/cli/demo/cat.js diff --git a/packages/cli/demo/cat.js b/packages/cli/demo/cat.js new file mode 100644 index 0000000000..0a9ade039d --- /dev/null +++ b/packages/cli/demo/cat.js @@ -0,0 +1,98 @@ +// This is a demo weblet that demonstrates a permission management UI for the +// pet daemon itself. +// +// This command will set up the cat page, create a URL, +// and open it. +// +// > endo open --file cat.js --bundle catBundle --host catPage +// +// Thereafter, +// +// > endo open catPage +// +// To interact with the permission manager, you can mock requests from a fake +// guest. +// +// > endo eval 42 -n ft +// > endo mkguest cat +// > endo request cat 'pet me' +// +// At this point, the command will pause, waiting for a response. +// In the Familiar Chat window, resolve the request with the pet name "ft" and +// the request will exit with the number 42. + +/* global window document */ + +import { E } from '@endo/far'; +import { makeRefIterator } from '@endo/daemon/ref-reader.js'; + +const dateFormatter = new window.Intl.DateTimeFormat(undefined, { + dateStyle: 'full', + timeStyle: 'long', +}); + +const followMessagesComponent = async (parentElement, powers) => { + for await (const message of makeRefIterator(E(powers).followMessages())) { + if (message.type === 'request') { + const { number, what, when, who, settled } = message; + + const $message = document.createElement('div'); + parentElement.appendChild($message); + + const $number = document.createElement('span'); + $number.innerText = `${number}. `; + $message.appendChild($number); + + const $who = document.createElement('b'); + $who.innerText = `${who}:`; + $message.appendChild($who); + + const $what = document.createElement('span'); + $what.innerText = ` ${what} `; + $message.appendChild($what); + + const $when = document.createElement('i'); + $when.innerText = dateFormatter.format(Date.parse(when)); + $message.appendChild($when); + + const $input = document.createElement('span'); + $message.appendChild($input); + + const $pet = document.createElement('input'); + $input.appendChild($pet); + + const $resolve = document.createElement('button'); + $resolve.innerText = 'resolve'; + $input.appendChild($resolve); + + const $reject = document.createElement('button'); + $reject.innerText = 'reject'; + $reject.onclick = () => { + E(powers).reject(number, $pet.value).catch(window.reportError); + }; + $input.appendChild($reject); + + const $error = document.createElement('span'); + $error.style.color = 'red'; + $input.appendChild($error); + + $resolve.onclick = () => { + E(powers) + .resolve(number, $pet.value) + .catch(error => { + $error.innerText = ` ${error.message}`; + }); + }; + + settled.then(status => { + $input.innerText = ` ${status} `; + }); + } + } +}; + +export const endow = async powers => { + document.body.innerHTML = + '

🐈‍⬛ Familiar Chat

Or: Le Chat Familier

'; + followMessagesComponent(document.body, powers).catch(window.reportError); +}; From befc1b10be0887e313ad29d60d62668e7883c5ea Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Wed, 28 Jun 2023 16:32:37 -0700 Subject: [PATCH 046/234] refactor(daemon): Rename inbox/outbox to host/guest --- packages/daemon/src/daemon.js | 238 +++++++++++++++++------------- packages/daemon/src/pet-store.js | 2 +- packages/daemon/src/types.d.ts | 34 ++--- packages/daemon/test/test-endo.js | 92 ++++++------ 4 files changed, 196 insertions(+), 170 deletions(-) diff --git a/packages/daemon/src/daemon.js b/packages/daemon/src/daemon.js index 7b22966d98..ff9be16cc7 100644 --- a/packages/daemon/src/daemon.js +++ b/packages/daemon/src/daemon.js @@ -51,8 +51,8 @@ const makeEndoBootstrap = ( // reference", and not for "what is my name for this promise". /** @type {WeakMap} */ const formulaIdentifierForRef = new WeakMap(); - /** @type {WeakMap} */ - const inboxRequestFunctions = new WeakMap(); + /** @type {WeakMap} */ + const hostRequestFunctions = new WeakMap(); /** @type {WeakMap>} */ const workerBootstraps = new WeakMap(); @@ -269,12 +269,12 @@ const makeEndoBootstrap = ( /** * @param {string} workerFormulaIdentifier - * @param {string} outboxFormulaIdentifier + * @param {string} guestFormulaIdentifier * @param {string} importPath */ const makeValueForImportUnsafe0 = async ( workerFormulaIdentifier, - outboxFormulaIdentifier, + guestFormulaIdentifier, importPath, ) => { // Behold, recursion: @@ -284,22 +284,22 @@ const makeEndoBootstrap = ( ); const workerBootstrap = workerBootstraps.get(workerFacet); assert(workerBootstrap); - const outboxP = /** @type {Promise} */ ( + const guestP = /** @type {Promise} */ ( // Behold, recursion: // eslint-disable-next-line no-use-before-define - provideValueForFormulaIdentifier(outboxFormulaIdentifier) + provideValueForFormulaIdentifier(guestFormulaIdentifier) ); - return E(workerBootstrap).importUnsafeAndEndow(importPath, outboxP); + return E(workerBootstrap).importUnsafeAndEndow(importPath, guestP); }; /** * @param {string} workerFormulaIdentifier - * @param {string} outboxFormulaIdentifier + * @param {string} guestFormulaIdentifier * @param {string} bundleFormulaIdentifier */ const makeValueForImportBundle0 = async ( workerFormulaIdentifier, - outboxFormulaIdentifier, + guestFormulaIdentifier, bundleFormulaIdentifier, ) => { // Behold, recursion: @@ -317,42 +317,42 @@ const makeEndoBootstrap = ( // eslint-disable-next-line no-use-before-define provideValueForFormulaIdentifier(bundleFormulaIdentifier) ); - const outboxP = /** @type {Promise} */ ( + const guestP = /** @type {Promise} */ ( // Behold, recursion: // eslint-disable-next-line no-use-before-define - provideValueForFormulaIdentifier(outboxFormulaIdentifier) + provideValueForFormulaIdentifier(guestFormulaIdentifier) ); - return E(workerBootstrap).importBundleAndEndow(readableBundleP, outboxP); + return E(workerBootstrap).importBundleAndEndow(readableBundleP, guestP); }; /** - * @param {string} outboxFormulaIdentifier - * @param {string} inboxFormulaIdentifier + * @param {string} guestFormulaIdentifier + * @param {string} hostFormulaIdentifier * @param {string} petStoreFormulaIdentifier */ - const makeIdentifiedOutbox = async ( - outboxFormulaIdentifier, - inboxFormulaIdentifier, + const makeIdentifiedGuest = async ( + guestFormulaIdentifier, + hostFormulaIdentifier, petStoreFormulaIdentifier, ) => { /** @type {Map>} */ const responses = new Map(); - const outboxPetStore = /** @type {import('./types.js').PetStore} */ ( + const guestPetStore = /** @type {import('./types.js').PetStore} */ ( // Behold, recursion: // eslint-disable-next-line no-use-before-define await provideValueForFormulaIdentifier(petStoreFormulaIdentifier) ); - const inbox = /** @type {object} */ ( + const host = /** @type {object} */ ( // Behold, recursion: // eslint-disable-next-line no-use-before-define - await provideValueForFormulaIdentifier(inboxFormulaIdentifier) + await provideValueForFormulaIdentifier(hostFormulaIdentifier) ); - const request = inboxRequestFunctions.get(inbox); + const request = hostRequestFunctions.get(host); if (request === undefined) { throw new Error( - `Programmer invariant failed: an inbox request function must exist for every inbox`, + `panic: a host request function must exist for every host`, ); } @@ -360,7 +360,7 @@ const makeEndoBootstrap = ( * @param {string} petName */ const provide = async petName => { - const formulaIdentifier = outboxPetStore.get(petName); + const formulaIdentifier = guestPetStore.get(petName); if (formulaIdentifier === undefined) { throw new TypeError(`Unknown pet name: ${q(petName)}`); } @@ -369,15 +369,15 @@ const makeEndoBootstrap = ( return provideValueForFormulaIdentifier(formulaIdentifier); }; - const { list, remove, rename } = outboxPetStore; + const { list, remove, rename } = guestPetStore; - /** @type {import('@endo/eventual-send').ERef} */ - const outbox = Far('EndoOutbox', { + /** @type {import('@endo/eventual-send').ERef} */ + const guest = Far('EndoGuest', { request: async (what, responseName) => { if (responseName === undefined) { // Behold, recursion: // eslint-disable-next-line no-use-before-define - return request(what, responseName, outbox, outboxPetStore); + return request(what, responseName, guest, guestPetStore); } const responseP = responses.get(responseName); if (responseP !== undefined) { @@ -385,12 +385,7 @@ const makeEndoBootstrap = ( } // Behold, recursion: // eslint-disable-next-line no-use-before-define - const newResponseP = request( - what, - responseName, - outbox, - outboxPetStore, - ); + const newResponseP = request(what, responseName, guest, guestPetStore); responses.set(responseName, newResponseP); return newResponseP; }, @@ -400,15 +395,15 @@ const makeEndoBootstrap = ( provide, }); - return outbox; + return guest; }; /** - * @param {string} inboxFormulaIdentifier + * @param {string} hostFormulaIdentifier * @param {string} storeFormulaIdentifier */ - const makeIdentifiedInbox = async ( - inboxFormulaIdentifier, + const makeIdentifiedHost = async ( + hostFormulaIdentifier, storeFormulaIdentifier, ) => { const petStore = /** @type {import('./types.js').PetStore} */ ( @@ -463,7 +458,7 @@ const makeEndoBootstrap = ( const formulaIdentifier = formulaIdentifierForRef.get(whom); if (formulaIdentifier === undefined) { throw new Error( - `Programmer invariant failed: requestFormulaIdentifier must be called with an Outbox (who) that was obtained through provideValueFor*`, + `panic: requestFormulaIdentifier must be called with a party (who) that was obtained through provideValueFor*`, ); } const [who] = petStore.lookup(formulaIdentifier); @@ -489,16 +484,16 @@ const makeEndoBootstrap = ( /** * @param {string} what * @param {string} responseName - * @param {import('./types.js').EndoOutbox} who - * @param {import('./types.js').PetStore} outboxPetStore + * @param {import('./types.js').EndoGuest} who + * @param {import('./types.js').PetStore} guestPetStore */ - const request = async (what, responseName, who, outboxPetStore) => { + const request = async (what, responseName, who, guestPetStore) => { if (responseName !== undefined) { /** @type {string | undefined} */ - let formulaIdentifier = outboxPetStore.get(responseName); + let formulaIdentifier = guestPetStore.get(responseName); if (formulaIdentifier === undefined) { formulaIdentifier = await requestFormulaIdentifier(what, who); - await outboxPetStore.write(responseName, formulaIdentifier); + await guestPetStore.write(responseName, formulaIdentifier); } // Behold, recursion: // eslint-disable-next-line no-use-before-define @@ -546,7 +541,7 @@ const makeEndoBootstrap = ( const resolveRequest = resolvers.get(req); if (resolveRequest === undefined) { throw new Error( - `Programmer invariant violated: a resolver must exist for every request`, + `panic: a resolver must exist for every request`, ); } resolveRequest(harden(Promise.reject(harden(new Error(message))))); @@ -556,25 +551,43 @@ const makeEndoBootstrap = ( /** * @param {string} petName */ - const makeOutbox = async petName => { - const outboxStoreId512 = await powers.randomHex512(); - const outboxStoreFormulaIdentifier = `pet-store-id512:${outboxStoreId512}`; - /** @type {import('./types.js').OutboxFormula} */ - const formula = { - type: /* @type {'outbox'} */ 'outbox', - inbox: inboxFormulaIdentifier, - store: outboxStoreFormulaIdentifier, - }; - // Behold, recursion: - // eslint-disable-next-line no-use-before-define - const { value, formulaIdentifier } = await provideValueForFormula( - formula, - 'outbox-id512', - ); + const provideGuest = async petName => { + /** @type {string | undefined} */ + let formulaIdentifier; if (petName !== undefined) { - await petStore.write(petName, formulaIdentifier); + formulaIdentifier = petStore.get(petName); + } + if (formulaIdentifier === undefined) { + const id512 = await powers.randomHex512(); + const guestStoreFormulaIdentifier = `pet-store-id512:${id512}`; + /** @type {import('./types.js').GuestFormula} */ + const formula = { + type: /* @type {'guest'} */ 'guest', + host: hostFormulaIdentifier, + store: guestStoreFormulaIdentifier, + }; + // Behold, recursion: + // eslint-disable-next-line no-use-before-define + const { value, formulaIdentifier } = await provideValueForFormula( + formula, + 'guest-id512', + ); + if (petName !== undefined) { + await petStore.write(petName, formulaIdentifier); + } + return value; + } else if (!formulaIdentifier.startsWith('guest-id512:')) { + throw new Error( + `Existing pet name does not designate a guest powers capability: ${q( + petName, + )}`, + ); } - return /** @type {Promise} */ (value); + return /** @type {Promise} */ ( + // Behold, recursion: + // eslint-disable-next-line no-use-before-define + provideValueForFormulaIdentifier(formulaIdentifier) + ); }; /** @@ -634,30 +647,30 @@ const makeEndoBootstrap = ( } if (workerFormulaIdentifier === undefined) { throw new Error( - `Programmer invariant failed: workerFormulaIdentifier must be defined`, + `panic: workerFormulaIdentifier must be defined`, ); } return workerFormulaIdentifier; }; /** - * @param {string} [outboxName] + * @param {string} [guestName] */ - const providePowersFormulaIdentifier = async outboxName => { - if (outboxName === undefined) { - return inboxFormulaIdentifier; + const providePowersFormulaIdentifier = async guestName => { + if (guestName === undefined) { + return hostFormulaIdentifier; } - let outboxFormulaIdentifier = petStore.get(outboxName); - if (outboxFormulaIdentifier === undefined) { - const outbox = await makeOutbox(outboxName); - outboxFormulaIdentifier = formulaIdentifierForRef.get(outbox); - if (outboxFormulaIdentifier === undefined) { + let guestFormulaIdentifier = petStore.get(guestName); + if (guestFormulaIdentifier === undefined) { + const guest = await makeGuest(guestName); + guestFormulaIdentifier = formulaIdentifierForRef.get(guest); + if (guestFormulaIdentifier === undefined) { throw new Error( - `Programmer invariant violated: makeOutbox must return an outbox with a corresponding formula identifier`, + `panic: provideGuest must return an guest with a corresponding formula identifier`, ); } } - return outboxFormulaIdentifier; + return guestFormulaIdentifier; }; /** @@ -723,28 +736,28 @@ const makeEndoBootstrap = ( /** * @param {string | undefined} workerName * @param {string} importPath - * @param {string | undefined} outboxName + * @param {string | undefined} guestName * @param {string} resultName */ const importUnsafeAndEndow = async ( workerName, importPath, - outboxName, + guestName, resultName, ) => { const workerFormulaIdentifier = await provideWorkerFormulaIdentifier( workerName, ); - const outboxFormulaIdentifier = await providePowersFormulaIdentifier( - outboxName, + const guestFormulaIdentifier = await providePowersFormulaIdentifier( + guestName, ); const formula = { /** @type {'import-unsafe'} */ type: 'import-unsafe', worker: workerFormulaIdentifier, - outbox: outboxFormulaIdentifier, + powers: guestFormulaIdentifier, importPath, }; @@ -763,13 +776,13 @@ const makeEndoBootstrap = ( /** * @param {string} workerName * @param {string} bundleName - * @param {string | undefined} outboxName + * @param {string | undefined} guestName * @param {string} resultName */ const importBundleAndEndow = async ( workerName, bundleName, - outboxName, + guestName, resultName, ) => { const workerFormulaIdentifier = await provideWorkerFormulaIdentifier( @@ -781,15 +794,15 @@ const makeEndoBootstrap = ( throw new TypeError(`Unknown pet name for bundle: ${bundleName}`); } - const outboxFormulaIdentifier = await providePowersFormulaIdentifier( - outboxName, + const guestFormulaIdentifier = await providePowersFormulaIdentifier( + guestName, ); const formula = { /** @type {'import-bundle'} */ type: 'import-bundle', worker: workerFormulaIdentifier, - outbox: outboxFormulaIdentifier, + guest: guestFormulaIdentifier, bundle: bundleFormulaIdentifier, }; @@ -826,13 +839,26 @@ const makeEndoBootstrap = ( /** * @param {string} [petName] */ - const makeInbox = async petName => { - const inboxId512 = await powers.randomHex512(); - const formulaIdentifier = `inbox-id512:${inboxId512}`; + const provideHost = async petName => { + /** @type {string | undefined} */ + let formulaIdentifier; if (petName !== undefined) { - await petStore.write(petName, formulaIdentifier); + formulaIdentifier = petStore.get(petName); + } + if (formulaIdentifier === undefined) { + const id512 = await powers.randomHex512(); + formulaIdentifier = `host-id512:${id512}`; + if (petName !== undefined) { + await petStore.write(petName, formulaIdentifier); + } + } else if (!formulaIdentifier.startsWith('host-id512:')) { + throw new Error( + `Existing pet name does not designate a host powers capability: ${q( + petName, + )}`, + ); } - return /** @type {Promise} */ ( + return /** @type {Promise} */ ( // Behold, recursion: // eslint-disable-next-line no-use-before-define provideValueForFormulaIdentifier(formulaIdentifier) @@ -883,8 +909,8 @@ const makeEndoBootstrap = ( const { list, remove, rename } = petStore; - /** @type {import('./types.js').EndoInbox} */ - const inbox = Far('EndoInbox', { + /** @type {import('./types.js').EndoHost} */ + const host = Far('EndoHost', { listMessages, followMessages, provide, @@ -895,8 +921,8 @@ const makeEndoBootstrap = ( remove, rename, store, - makeOutbox, - makeInbox, + provideGuest, + provideHost, makeWorker, evaluate, importUnsafeAndEndow, @@ -904,9 +930,9 @@ const makeEndoBootstrap = ( provideWebPage, }); - inboxRequestFunctions.set(inbox, request); + hostRequestFunctions.set(host, request); - return inbox; + return host; }; /** @@ -929,19 +955,19 @@ const makeEndoBootstrap = ( } else if (formula.type === 'import-unsafe') { return makeValueForImportUnsafe0( formula.worker, - formula.outbox, + formula.powers, formula.importPath, ); } else if (formula.type === 'import-bundle') { return makeValueForImportBundle0( formula.worker, - formula.outbox, + formula.powers, formula.bundle, ); - } else if (formula.type === 'outbox') { - return makeIdentifiedOutbox( + } else if (formula.type === 'guest') { + return makeIdentifiedGuest( formulaIdentifier, - formula.inbox, + formula.host, formula.store, ); } else if (formula.type === 'web-bundle') { @@ -1026,13 +1052,13 @@ const makeEndoBootstrap = ( if (delimiterIndex < 0) { if (formulaIdentifier === 'pet-store') { return makeOwnPetStore(powers, locator, 'pet-store'); - } else if (formulaIdentifier === 'inbox') { - return makeIdentifiedInbox(formulaIdentifier, 'pet-store'); + } else if (formulaIdentifier === 'host') { + return makeIdentifiedHost(formulaIdentifier, 'pet-store'); } else if (formulaIdentifier === 'web-page-js') { return makeValueForFormula('web-page-js', zero512, { type: /** @type {'import-unsafe'} */ ('import-unsafe'), worker: `worker-id512:${zero512}`, - outbox: 'inbox', + powers: 'host', importPath: powers.fileURLToPath( new URL('web-page-bundler.js', import.meta.url).href, ), @@ -1050,8 +1076,8 @@ const makeEndoBootstrap = ( return makeIdentifiedWorker(formulaNumber); } else if (prefix === 'pet-store-id512') { return makeIdentifiedPetStore(powers, locator, formulaNumber); - } else if (prefix === 'inbox-id512') { - return makeIdentifiedInbox( + } else if (prefix === 'host-id512') { + return makeIdentifiedHost( formulaIdentifier, `pet-store-id512:${formulaNumber}`, ); @@ -1060,7 +1086,7 @@ const makeEndoBootstrap = ( 'eval-id512', 'import-unsafe-id512', 'import-bundle-id512', - 'outbox-id512', + 'guest-id512', 'web-bundle', ].includes(prefix) ) { @@ -1141,7 +1167,7 @@ const makeEndoBootstrap = ( cancel(new Error('Termination requested')); }, - inbox: () => provideValueForFormulaIdentifier('inbox'), + host: () => provideValueForFormulaIdentifier('host'), webPageJs: () => provideValueForFormulaIdentifier('web-page-js'), diff --git a/packages/daemon/src/pet-store.js b/packages/daemon/src/pet-store.js index 1eb0215148..49b24dbe22 100644 --- a/packages/daemon/src/pet-store.js +++ b/packages/daemon/src/pet-store.js @@ -5,7 +5,7 @@ const { quote: q } = assert; const validNamePattern = /^[a-zA-Z][a-zA-Z0-9]{0,127}$/; const validIdPattern = /^[0-9a-f]{128}$/; const validFormulaPattern = - /^(?:inbox|pet-store|(?:readable-blob-sha512|worker-id512|pet-store-id512|eval-id512|import-unsafe-id512|import-bundle-id512|inbox-id512|outbox-id512):[0-9a-f]{128}|web-bundle:[0-9a-f]{32})$/; + /^(?:host|pet-store|(?:readable-blob-sha512|worker-id512|pet-store-id512|eval-id512|import-unsafe-id512|import-bundle-id512|host-id512|guest-id512):[0-9a-f]{128}|web-bundle:[0-9a-f]{32})$/; /** * @param {import('./types.js').DaemonicPowers} powers diff --git a/packages/daemon/src/types.d.ts b/packages/daemon/src/types.d.ts index 2c9bcd7f7b..1a8469ef03 100644 --- a/packages/daemon/src/types.d.ts +++ b/packages/daemon/src/types.d.ts @@ -92,15 +92,15 @@ export type MignonicPowers = { }; }; -type InboxFormula = { - type: 'inbox'; +type HostFormula = { + type: 'host'; store: string; }; -type OutboxFormula = { - type: 'outbox'; +type GuestFormula = { + type: 'guest'; store: string; - inbox: string; + host: string; }; type EvalFormula = { @@ -115,7 +115,7 @@ type EvalFormula = { type ImportUnsafeFormula = { type: 'import-unsafe'; worker: string; - outbox: string; + powers: string; importPath: string; // TODO formula slots }; @@ -123,7 +123,7 @@ type ImportUnsafeFormula = { type ImportBundleFormula = { type: 'import-bundle'; worker: string; - outbox: string; + powers: string; bundle: string; // TODO formula slots }; @@ -135,8 +135,8 @@ type WebBundleFormula = { }; export type Formula = - | InboxFormula - | OutboxFormula + | HostFormula + | GuestFormula | EvalFormula | ImportUnsafeFormula | ImportBundleFormula @@ -178,8 +178,8 @@ export interface PetStore { export type RequestFn = ( what: string, responseName: string, - outbox: object, - outboxPetStore: PetStore, + guest: object, + guestPetStore: PetStore, ) => Promise; export interface EndoReadable { @@ -195,11 +195,11 @@ export interface EndoWorker { whenTerminated(): Promise; } -export interface EndoOutbox { +export interface EndoGuest { request(what: string, responseName: string): Promise; } -export interface EndoInbox { +export interface EndoHost { listMessages(): Promise>; followMessages(): ERef>; provide(petName: string): Promise; @@ -213,8 +213,8 @@ export interface EndoInbox { readerRef: ERef>, petName: string, ): Promise; - makeOutbox(petName?: string): Promise; - makeInbox(petName?: string): Promise; + provideGuest(petName?: string): Promise; + provideHost(petName?: string): Promise; makeWorker(petName: string): Promise; evaluate( workerPetName: string | undefined, @@ -226,13 +226,13 @@ export interface EndoInbox { importUnsafeAndEndow( workerPetName: string | undefined, importPath: string, - outboxName: string, + powersName: string, resultName?: string, ): Promise; importBundleAndEndow( workerPetName: string | undefined, bundleName: string, - outboxName: string, + powersName: string, resultName?: string, ): Promise; } diff --git a/packages/daemon/test/test-endo.js b/packages/daemon/test/test-endo.js index 4ffb9e9da2..ccc7c286e5 100644 --- a/packages/daemon/test/test-endo.js +++ b/packages/daemon/test/test-endo.js @@ -58,8 +58,8 @@ test('lifecycle', async t => { cancelled, ); const bootstrap = getBootstrap(); - const inbox = E(bootstrap).inbox(); - const worker = await E(inbox).makeWorker(); + const host = E(bootstrap).host(); + const worker = await E(host).makeWorker(); await E(worker) .terminate() .catch(() => {}); @@ -83,9 +83,9 @@ test('spawn and evaluate', async t => { cancelled, ); const bootstrap = getBootstrap(); - const inbox = E(bootstrap).inbox(); - await E(inbox).makeWorker('w1'); - const ten = await E(inbox).evaluate('w1', '10', [], []); + const host = E(bootstrap).host(); + await E(host).makeWorker('w1'); + const ten = await E(host).evaluate('w1', '10', [], []); t.is(10, ten); await stop(locator); @@ -105,8 +105,8 @@ test('anonymous spawn and evaluate', async t => { cancelled, ); const bootstrap = getBootstrap(); - const inbox = E(bootstrap).inbox(); - const ten = await E(inbox).evaluate(undefined, '10', [], []); + const host = E(bootstrap).host(); + const ten = await E(host).evaluate(undefined, '10', [], []); t.is(10, ten); await stop(locator); @@ -127,13 +127,13 @@ test('persist spawn and evaluation', async t => { cancelled, ); const bootstrap = getBootstrap(); - const inbox = E(bootstrap).inbox(); + const host = E(bootstrap).host(); - await E(inbox).makeWorker('w1'); + await E(host).makeWorker('w1'); - const ten = await E(inbox).evaluate('w1', '10', [], [], 'ten'); + const ten = await E(host).evaluate('w1', '10', [], [], 'ten'); t.is(10, ten); - const twenty = await E(inbox).evaluate( + const twenty = await E(host).evaluate( 'w1', 'number * 2', ['number'], @@ -144,7 +144,7 @@ test('persist spawn and evaluation', async t => { // Forget the pet name of the intermediate formula, demonstrating that pet // names are ephemeral but formulas persist as long as their is a retention // chain among them. - await E(inbox).remove('ten'); + await E(host).remove('ten'); t.is(20, twenty); } @@ -159,9 +159,9 @@ test('persist spawn and evaluation', async t => { ); const bootstrap = getBootstrap(); - const inbox = E(bootstrap).inbox(); + const host = E(bootstrap).host(); - const retwenty = await E(inbox).provide('twenty'); + const retwenty = await E(host).provide('twenty'); t.is(20, retwenty); } @@ -183,9 +183,9 @@ test('store', async t => { cancelled, ); const bootstrap = getBootstrap(); - const inbox = E(bootstrap).inbox(); + const host = E(bootstrap).host(); const readerRef = makeReaderRef([new TextEncoder().encode('hello\n')]); - await E(inbox).store(readerRef, 'helloText'); + await E(host).store(readerRef, 'helloText'); } { @@ -195,8 +195,8 @@ test('store', async t => { cancelled, ); const bootstrap = getBootstrap(); - const inbox = E(bootstrap).inbox(); - const readable = await E(inbox).provide('helloText'); + const host = E(bootstrap).host(); + const readable = await E(host).provide('helloText'); const actualText = await E(readable).text(); t.is(actualText, 'hello\n'); } @@ -217,10 +217,10 @@ test('closure state lost by restart', async t => { cancelled, ); const bootstrap = getBootstrap(); - const inbox = E(bootstrap).inbox(); - await E(inbox).makeWorker('w1'); + const host = E(bootstrap).host(); + await E(host).makeWorker('w1'); - await E(inbox).evaluate( + await E(host).evaluate( 'w1', ` Far('Counter Maker', { @@ -234,26 +234,26 @@ test('closure state lost by restart', async t => { [], 'counterMaker', ); - await E(inbox).evaluate( + await E(host).evaluate( 'w1', `E(cm).makeCounter() `, ['cm'], ['counterMaker'], 'counter', ); - const one = await E(inbox).evaluate( + const one = await E(host).evaluate( 'w1', `E(counter).incr()`, ['counter'], ['counter'], ); - const two = await E(inbox).evaluate( + const two = await E(host).evaluate( 'w1', `E(counter).incr()`, ['counter'], ['counter'], ); - const three = await E(inbox).evaluate( + const three = await E(host).evaluate( 'w1', `E(counter).incr()`, ['counter'], @@ -273,21 +273,21 @@ test('closure state lost by restart', async t => { cancelled, ); const bootstrap = getBootstrap(); - const inbox = E(bootstrap).inbox(); - await E(inbox).provide('w1'); - const one = await E(inbox).evaluate( + const host = E(bootstrap).host(); + await E(host).provide('w1'); + const one = await E(host).evaluate( 'w1', `E(counter).incr()`, ['counter'], ['counter'], ); - const two = await E(inbox).evaluate( + const two = await E(host).evaluate( 'w1', `E(counter).incr()`, ['counter'], ['counter'], ); - const three = await E(inbox).evaluate( + const three = await E(host).evaluate( 'w1', `E(counter).incr()`, ['counter'], @@ -307,7 +307,7 @@ test('persist unsafe services and their requests', async t => { await reset(locator); await start(locator); - const inboxFinished = (async () => { + const responderFinished = (async () => { const { promise: followerCancelled, reject: cancelFollower } = makePromiseKit(); cancelled.catch(cancelFollower); @@ -317,9 +317,9 @@ test('persist unsafe services and their requests', async t => { followerCancelled, ); const bootstrap = getBootstrap(); - const inbox = E(bootstrap).inbox(); - await E(inbox).makeWorker('userWorker'); - await E(inbox).evaluate( + const host = E(bootstrap).host(); + await E(host).makeWorker('userWorker'); + await E(host).evaluate( 'userWorker', ` Far('Answer', { @@ -330,28 +330,28 @@ test('persist unsafe services and their requests', async t => { [], 'grant', ); - const iteratorRef = E(inbox).followMessages(); + const iteratorRef = E(host).followMessages(); const { value: message } = await E(iteratorRef).next(); const { number, who } = E.get(message); t.is(await who, 'o1'); - await E(inbox).resolve(await number, 'grant'); + await E(host).resolve(await number, 'grant'); })(); - const workflowFinished = (async () => { + const requesterFinished = (async () => { const { getBootstrap } = await makeEndoClient( 'client', locator.sockPath, cancelled, ); const bootstrap = getBootstrap(); - const inbox = E(bootstrap).inbox(); - await E(inbox).makeWorker('w1'); - await E(inbox).makeOutbox('o1'); + const host = E(bootstrap).host(); + await E(host).makeWorker('w1'); + await E(host).provideGuest('o1'); const servicePath = path.join(dirname, 'test', 'service.js'); - await E(inbox).importUnsafeAndEndow('w1', servicePath, 'o1', 's1'); + await E(host).importUnsafeAndEndow('w1', servicePath, 'o1', 's1'); - await E(inbox).makeWorker('w2'); - const answer = await E(inbox).evaluate( + await E(host).makeWorker('w2'); + const answer = await E(host).evaluate( 'w2', 'E(service).ask()', ['service'], @@ -362,7 +362,7 @@ test('persist unsafe services and their requests', async t => { t.is(number, 42); })(); - await Promise.all([inboxFinished, workflowFinished]); + await Promise.all([responderFinished, requesterFinished]); await restart(locator); @@ -373,8 +373,8 @@ test('persist unsafe services and their requests', async t => { cancelled, ); const bootstrap = getBootstrap(); - const inbox = E(bootstrap).inbox(); - const answer = await E(inbox).provide('answer'); + const host = E(bootstrap).host(); + const answer = await E(host).provide('answer'); const number = await E(answer).value(); t.is(number, 42); } From 6711a627b0b2fcdab4f0c7080c1824be8d684849 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Wed, 28 Jun 2023 16:34:47 -0700 Subject: [PATCH 047/234] refactor(cli): Rename inbox/outbox to host/guest --- packages/cli/src/endo.js | 396 ++++++++++++++++++++++------------ packages/daemon/src/daemon.js | 20 +- 2 files changed, 261 insertions(+), 155 deletions(-) diff --git a/packages/cli/src/endo.js b/packages/cli/src/endo.js index c3dfc4598d..2c92548998 100644 --- a/packages/cli/src/endo.js +++ b/packages/cli/src/endo.js @@ -294,7 +294,12 @@ export const main = async rawArgs => { program .command('archive ') - .option('-i,--inbox ', 'An alternate inbox', collect, []) + .option( + '-a,--as ', + 'Pose as named party (as named by current party)', + collect, + [], + ) .option('-n,--name ', 'Store the archive into Endo') .option('-f,--file ', 'Store the archive into a file') .description('captures an archive from an entry module path') @@ -302,7 +307,7 @@ export const main = async rawArgs => { const { name: archiveName, file: archivePath, - inbox: inboxNames, + as: partyNames, } = cmd.opts(); const applicationLocation = url.pathToFileURL(applicationPath); if (archiveName !== undefined) { @@ -315,11 +320,11 @@ export const main = async rawArgs => { ); try { const bootstrap = getBootstrap(); - let inbox = E(bootstrap).inbox(); - for (const inboxName of inboxNames) { - inbox = E(inbox).provide(inboxName); + let party = E(bootstrap).host(); + for (const partyName of partyNames) { + party = E(party).provide(partyName); } - await E(inbox).store(readerRef, archiveName); + await E(party).store(readerRef, archiveName); } catch (error) { console.error(error); cancel(error); @@ -339,13 +344,18 @@ export const main = async rawArgs => { program .command('bundle ') - .option('-i,--inbox ', 'An alternate inbox', collect, []) + .option( + '-a,--as ', + 'Pose as named party (as named by current party)', + collect, + [], + ) .option('-n,--name ', 'Store the bundle into Endo') .description( 'captures a JSON bundle containing an archive for an entry module path', ) .action(async (applicationPath, cmd) => { - const { name: bundleName, inbox: inboxNames } = cmd.opts(); + const { name: bundleName, as: partyNames } = cmd.opts(); const bundle = await bundleSource(applicationPath); console.log(bundle.endoZipBase64Sha512); const bundleText = JSON.stringify(bundle); @@ -358,11 +368,11 @@ export const main = async rawArgs => { ); try { const bootstrap = getBootstrap(); - let inbox = E(bootstrap).inbox(); - for (const inboxName of inboxNames) { - inbox = E(inbox).provide(inboxName); + let party = E(bootstrap).host(); + for (const partyName of partyNames) { + party = E(party).provide(partyName); } - await E(inbox).store(readerRef, bundleName); + await E(party).store(readerRef, bundleName); } catch (error) { console.error(error); cancel(error); @@ -371,14 +381,19 @@ export const main = async rawArgs => { program .command('store ') - .option('-i,--inbox ', 'An alternate inbox', collect, []) + .option( + '-a,--as ', + 'Pose as named party (as named by current party)', + collect, + [], + ) .option( '-n,--name ', 'Assigns a pet name to the result for future reference', ) .description('stores a readable file') .action(async (storablePath, cmd) => { - const { name, inbox: inboxNames } = cmd.opts(); + const { name, as: partyNames } = cmd.opts(); const nodeReadStream = fs.createReadStream(storablePath); const reader = makeNodeReader(nodeReadStream); const readerRef = makeReaderRef(reader); @@ -390,11 +405,11 @@ export const main = async rawArgs => { ); try { const bootstrap = getBootstrap(); - let inbox = E(bootstrap).inbox(); - for (const inboxName of inboxNames) { - inbox = E(inbox).provide(inboxName); + let party = E(bootstrap).host(); + for (const partyName of partyNames) { + party = E(party).provide(partyName); } - await E(inbox).store(readerRef, name); + await E(party).store(readerRef, name); } catch (error) { console.error(error); cancel(error); @@ -404,9 +419,14 @@ export const main = async rawArgs => { program .command('spawn [names...]') .description('creates workers for evaluating or importing programs') - .option('-i,--inbox ', 'An alternate inbox', collect, []) + .option( + '-a,--as ', + 'Pose as named party (as named by current party)', + collect, + [], + ) .action(async (petNames, cmd) => { - const { inbox: inboxNames } = cmd.opts(); + const { as: partyNames } = cmd.opts(); const { getBootstrap } = await provideEndoClient( 'cli', sockPath, @@ -414,12 +434,12 @@ export const main = async rawArgs => { ); try { const bootstrap = getBootstrap(); - let inbox = E(bootstrap).inbox(); - for (const inboxName of inboxNames) { - inbox = E(inbox).provide(inboxName); + let party = E(bootstrap).host(); + for (const partyName of partyNames) { + party = E(party).provide(partyName); } for (const petName of petNames) { - await E(inbox).makeWorker(petName); + await E(party).makeWorker(petName); } } catch (error) { console.error(error); @@ -429,10 +449,15 @@ export const main = async rawArgs => { program .command('show ') - .option('-i,--inbox ', 'An alternate inbox', collect, []) + .option( + '-a,--as ', + 'Pose as named party (as named by current party)', + collect, + [], + ) .description('prints the named value') .action(async (name, cmd) => { - const { inbox: inboxNames } = cmd.opts(); + const { as: partyNames } = cmd.opts(); const { getBootstrap } = await provideEndoClient( 'cli', sockPath, @@ -440,11 +465,11 @@ export const main = async rawArgs => { ); try { const bootstrap = getBootstrap(); - let inbox = E(bootstrap).inbox(); - for (const inboxName of inboxNames) { - inbox = E(inbox).provide(inboxName); + let party = E(bootstrap).host(); + for (const partyName of partyNames) { + party = E(party).provide(partyName); } - const pet = await E(inbox).provide(name); + const pet = await E(party).provide(name); console.log(pet); } catch (error) { console.error(error); @@ -454,12 +479,17 @@ export const main = async rawArgs => { program .command('follow ') - .option('-i,--inbox ', 'An alternate inbox', collect, []) + .option( + '-a,--as ', + 'Pose as named party (as named by current party)', + collect, + [], + ) .description( 'prints a representation of each value from the named async iterable as it arrives', ) .action(async (name, cmd) => { - const { inbox: inboxNames } = cmd.opts(); + const { as: partyNames } = cmd.opts(); const { getBootstrap } = await provideEndoClient( 'cli', sockPath, @@ -467,11 +497,11 @@ export const main = async rawArgs => { ); try { const bootstrap = getBootstrap(); - let inbox = E(bootstrap).inbox(); - for (const inboxName of inboxNames) { - inbox = E(inbox).provide(inboxName); + let party = E(bootstrap).host(); + for (const partyName of partyNames) { + party = E(party).provide(partyName); } - const iterable = await E(inbox).provide(name); + const iterable = await E(party).provide(name); for await (const iterand of makeRefIterator(iterable)) { console.log(iterand); } @@ -483,10 +513,15 @@ export const main = async rawArgs => { program .command('cat ') - .option('-i,--inbox ', 'An alternate inbox', collect, []) + .option( + '-a,--as ', + 'Pose as named party (as named by current party)', + collect, + [], + ) .description('prints the content of the named readable file') .action(async (name, cmd) => { - const { inbox: inboxNames } = cmd.opts(); + const { as: partyNames } = cmd.opts(); const { getBootstrap } = await provideEndoClient( 'cli', sockPath, @@ -494,11 +529,11 @@ export const main = async rawArgs => { ); try { const bootstrap = getBootstrap(); - let inbox = E(bootstrap).inbox(); - for (const inboxName of inboxNames) { - inbox = E(inbox).provide(inboxName); + let party = E(bootstrap).host(); + for (const partyName of partyNames) { + party = E(party).provide(partyName); } - const readable = await E(inbox).provide(name); + const readable = await E(party).provide(name); const readerRef = E(readable).stream(); const reader = makeRefReader(readerRef); for await (const chunk of reader) { @@ -512,10 +547,15 @@ export const main = async rawArgs => { program .command('list') - .option('-i,--inbox ', 'An alternate inbox', collect, []) + .option( + '-a,--as ', + 'Pose as named party (as named by current party)', + collect, + [], + ) .description('lists pet names') .action(async cmd => { - const { inbox: inboxNames } = cmd.opts(); + const { as: partyNames } = cmd.opts(); const { getBootstrap } = await provideEndoClient( 'cli', sockPath, @@ -523,11 +563,11 @@ export const main = async rawArgs => { ); try { const bootstrap = getBootstrap(); - let inbox = E(bootstrap).inbox(); - for (const inboxName of inboxNames) { - inbox = E(inbox).provide(inboxName); + let party = E(bootstrap).host(); + for (const partyName of partyNames) { + party = E(party).provide(partyName); } - const petNames = await E(inbox).list(); + const petNames = await E(party).list(); for await (const petName of petNames) { console.log(petName); } @@ -540,9 +580,14 @@ export const main = async rawArgs => { program .command('remove [names...]') .description('removes pet names') - .option('-i,--inbox ', 'An alternate inbox', collect, []) + .option( + '-a,--as ', + 'Pose as named party (as named by current party)', + collect, + [], + ) .action(async (petNames, cmd) => { - const { inbox: inboxNames } = cmd.opts(); + const { as: partyNames } = cmd.opts(); const { getBootstrap } = await provideEndoClient( 'cli', sockPath, @@ -550,11 +595,11 @@ export const main = async rawArgs => { ); try { const bootstrap = getBootstrap(); - let inbox = E(bootstrap).inbox(); - for (const inboxName of inboxNames) { - inbox = E(inbox).provide(inboxName); + let party = E(bootstrap).host(); + for (const partyName of partyNames) { + party = E(party).provide(partyName); } - await Promise.all(petNames.map(petName => E(inbox).remove(petName))); + await Promise.all(petNames.map(petName => E(party).remove(petName))); } catch (error) { console.error(error); cancel(error); @@ -564,9 +609,14 @@ export const main = async rawArgs => { program .command('rename ') .description('renames a value') - .option('-i,--inbox ', 'An alternate inbox', collect, []) + .option( + '-a,--as ', + 'Pose as named party (as named by current party)', + collect, + [], + ) .action(async (fromName, toName, cmd) => { - const { inbox: inboxNames } = cmd.opts(); + const { as: partyNames } = cmd.opts(); const { getBootstrap } = await provideEndoClient( 'cli', sockPath, @@ -574,11 +624,11 @@ export const main = async rawArgs => { ); try { const bootstrap = getBootstrap(); - let inbox = E(bootstrap).inbox(); - for (const inboxName of inboxNames) { - inbox = E(inbox).provide(inboxName); + let party = E(bootstrap).host(); + for (const partyName of partyNames) { + party = E(party).provide(partyName); } - await E(inbox).rename(fromName, toName); + await E(party).rename(fromName, toName); } catch (error) { console.error(error); cancel(error); @@ -586,11 +636,16 @@ export const main = async rawArgs => { }); program - .command('make-inbox ') - .option('-i,--inbox ', 'An alternate inbox', collect, []) - .description('creates a new inbox') + .command('mkhost ') + .option( + '-a,--as ', + 'Pose as named party (as named by current party)', + collect, + [], + ) + .description('creates new host powers, pet store, and mailbox') .action(async (name, cmd) => { - const { inbox: inboxNames } = cmd.opts(); + const { as: partyNames } = cmd.opts(); const { getBootstrap } = await provideEndoClient( 'cli', sockPath, @@ -598,12 +653,12 @@ export const main = async rawArgs => { ); try { const bootstrap = getBootstrap(); - let inbox = E(bootstrap).inbox(); - for (const inboxName of inboxNames) { - inbox = E(inbox).provide(inboxName); + let party = E(bootstrap).host(); + for (const partyName of partyNames) { + party = E(party).provide(partyName); } - const newInbox = await E(inbox).makeInbox(name); - console.log(newInbox); + const newHost = await E(party).provideHost(name); + console.log(newHost); } catch (error) { console.error(error); cancel(error); @@ -611,11 +666,16 @@ export const main = async rawArgs => { }); program - .command('make-outbox ') - .option('-i,--inbox ', 'An alternate inbox', collect, []) - .description('creates a new outbox') + .command('mkguest ') + .option( + '-a,--as ', + 'Pose as named party (as named by current party)', + collect, + [], + ) + .description('creates new guest powers, pet store, and mailbox') .action(async (name, cmd) => { - const { inbox: inboxNames } = cmd.opts(); + const { as: partyNames } = cmd.opts(); const { getBootstrap } = await provideEndoClient( 'cli', sockPath, @@ -623,12 +683,12 @@ export const main = async rawArgs => { ); try { const bootstrap = getBootstrap(); - let inbox = E(bootstrap).inbox(); - for (const inboxName of inboxNames) { - inbox = E(inbox).provide(inboxName); + let party = E(bootstrap).host(); + for (const partyName of partyNames) { + party = E(party).provide(partyName); } - const newOutbox = await E(inbox).makeOutbox(name); - console.log(newOutbox); + const newGuest = await E(party).provideGuest(name); + console.log(newGuest); } catch (error) { console.error(error); cancel(error); @@ -637,11 +697,16 @@ export const main = async rawArgs => { program .command('inbox') - .option('-i,--inbox ', 'An alternate inbox', collect, []) + .option( + '-a,--as ', + 'Pose as named party (as named by current party)', + collect, + [], + ) .option('-f,--follow', 'Follow the inbox for messages as they arrive') .description('prints pending requests that have been sent to you') .action(async cmd => { - const { inbox: inboxNames, follow } = cmd.opts(); + const { as: partyNames, follow } = cmd.opts(); const { getBootstrap } = await provideEndoClient( 'cli', sockPath, @@ -649,18 +714,30 @@ export const main = async rawArgs => { ); try { const bootstrap = getBootstrap(); - let inbox = E(bootstrap).inbox(); - for (const inboxName of inboxNames) { - inbox = E(inbox).provide(inboxName); + let party = E(bootstrap).host(); + for (const partyName of partyNames) { + party = E(party).provide(partyName); } - const requests = follow - ? makeRefIterator(E(inbox).followMessages()) - : await E(inbox).listMessages(); - for await (const { number, who, what } of requests) { - if (who !== undefined) { - // TODO ensure the description is all printable ASCII and so - // contains no TTY control codes. - console.log(`${number}. ${who}: ${what}`); + const messages = follow + ? makeRefIterator(E(party).followMessages()) + : await E(party).listMessages(); + for await (const message of messages) { + const { number, who, when } = message; + if (message.type === 'request') { + const { what } = message; + console.log( + `${number}. ${JSON.stringify(who)} requested ${JSON.stringify( + what, + )} at ${JSON.stringify(when)}`, + ); + } else { + console.log( + `${number}. ${JSON.stringify( + who, + )} sent an unrecognizable message at ${JSON.stringify( + when, + )}. Consider upgrading.`, + ); } } } catch (error) { @@ -670,16 +747,21 @@ export const main = async rawArgs => { }); program - .command('request ') + .command('request ') .description('requests a reference with the given description') - .option('-i,--inbox ', 'An alternate inbox', collect, []) + .option( + '-a,--as ', + 'Pose as named party (as named by current party)', + collect, + [], + ) .option( '-n,--name ', 'Assigns a name to the result for future reference, persisted between restarts', ) .option('-w,--wait', 'Waits for and prints the response') - .action(async (outboxName, description, cmd) => { - const { name: resultName, inbox: inboxNames, wait } = cmd.opts(); + .action(async (guestName, description, cmd) => { + const { name: resultName, as: partyNames, wait } = cmd.opts(); const { getBootstrap } = await provideEndoClient( 'cli', sockPath, @@ -687,12 +769,12 @@ export const main = async rawArgs => { ); try { const bootstrap = getBootstrap(); - let inbox = E(bootstrap).inbox(); - for (const inboxName of inboxNames) { - inbox = E(inbox).provide(inboxName); + let party = E(bootstrap).host(); + for (const partyName of partyNames) { + party = E(party).provideGuest(partyName); } - const outboxP = E(inbox).provide(outboxName); - const resultP = E(outboxP).request(description, resultName); + const guestP = E(party).provide(guestName); + const resultP = E(guestP).request(description, resultName); if (wait || resultName === undefined) { const result = await resultP; console.log(result); @@ -705,10 +787,15 @@ export const main = async rawArgs => { program .command('resolve ') - .option('-i,--inbox ', 'An alternate inbox', collect, []) + .option( + '-a,--as ', + 'Pose as named party (as named by current party)', + collect, + [], + ) .description('responds to a pending request with the named value') .action(async (requestNumberText, resolutionName, cmd) => { - const { inbox: inboxNames } = cmd.opts(); + const { as: partyNames } = cmd.opts(); // TODO less bad number parsing. const requestNumber = Number(requestNumberText); const { getBootstrap } = await provideEndoClient( @@ -718,11 +805,11 @@ export const main = async rawArgs => { ); try { const bootstrap = getBootstrap(); - let inbox = E(bootstrap).inbox(); - for (const inboxName of inboxNames) { - inbox = E(inbox).provide(inboxName); + let party = E(bootstrap).host(); + for (const partyName of partyNames) { + party = E(party).provide(partyName); } - await E(inbox).resolve(requestNumber, resolutionName); + await E(party).resolve(requestNumber, resolutionName); } catch (error) { console.error(error); cancel(error); @@ -732,9 +819,14 @@ export const main = async rawArgs => { program .command('reject [message]') .description('responds to a pending request with the rejection message') - .option('-i,--inbox ', 'An alternate inbox', collect, []) + .option( + '-a,--as ', + 'Pose as named party (as named by current party)', + collect, + [], + ) .action(async (requestNumberText, message, cmd) => { - const { inbox: inboxNames } = cmd.opts(); + const { as: partyNames } = cmd.opts(); // TODO less bad number parsing. const requestNumber = Number(requestNumberText); const { getBootstrap } = await provideEndoClient( @@ -744,11 +836,11 @@ export const main = async rawArgs => { ); try { const bootstrap = getBootstrap(); - let inbox = E(bootstrap).inbox(); - for (const inboxName of inboxNames) { - inbox = E(inbox).provide(inboxName); + let party = E(bootstrap).host(); + for (const partyName of partyNames) { + party = E(party).provide(partyName); } - await E(inbox).reject(requestNumber, message); + await E(party).reject(requestNumber, message); } catch (error) { console.error(error); cancel(error); @@ -758,7 +850,12 @@ export const main = async rawArgs => { program .command('eval [names...]') .description('evaluates a string with the endowed values in scope') - .option('-i,--inbox ', 'An alternate inbox', collect, []) + .option( + '-a,--as ', + 'Pose as named party (as named by current party)', + collect, + [], + ) .option( '-w,--worker ', 'Reuse an existing worker rather than create a new one', @@ -771,7 +868,7 @@ export const main = async rawArgs => { const { name: resultName, worker: workerName, - inbox: inboxNames, + as: partyNames, } = cmd.opts(); const { getBootstrap } = await provideEndoClient( 'cli', @@ -780,9 +877,9 @@ export const main = async rawArgs => { ); try { const bootstrap = getBootstrap(); - let inbox = E(bootstrap).inbox(); - for (const inboxName of inboxNames) { - inbox = E(inbox).provide(inboxName); + let party = E(bootstrap).host(); + for (const partyName of partyNames) { + party = E(party).provide(partyName); } const pairs = names.map(name => { @@ -803,7 +900,7 @@ export const main = async rawArgs => { const codeNames = pairs.map(pair => pair[0]); const endowmentNames = pairs.map(pair => pair[1]); - const result = await E(inbox).evaluate( + const result = await E(party).evaluate( workerName, source, codeNames, @@ -822,7 +919,12 @@ export const main = async rawArgs => { .description( 'imports the module at the given path and runs its endow function with all of your authority', ) - .option('-i,--inbox ', 'An alternate inbox', collect, []) + .option( + '-a,--as ', + 'Pose as named party (as named by current party)', + collect, + [], + ) .option( '-n,--name ', 'Assigns a name to the result for future reference, persisted between restarts', @@ -831,8 +933,8 @@ export const main = async rawArgs => { '-w,--worker ', 'Reuse an existing worker rather than create a new one', ) - .action(async (importPath, outboxName, cmd) => { - const { name: resultName, worker: workerName, inboxNames } = cmd.opts(); + .action(async (importPath, guestName, cmd) => { + const { name: resultName, worker: workerName, partyNames } = cmd.opts(); const { getBootstrap } = await provideEndoClient( 'cli', sockPath, @@ -840,14 +942,14 @@ export const main = async rawArgs => { ); try { const bootstrap = getBootstrap(); - let inbox = E(bootstrap).inbox(); - for (const inboxName of inboxNames) { - inbox = E(inbox).provide(inboxName); + let party = E(bootstrap).host(); + for (const partyName of partyNames) { + party = E(party).provide(partyName); } - const result = await E(inbox).importUnsafeAndEndow( + const result = await E(party).importUnsafeAndEndow( workerName, path.resolve(importPath), - outboxName, + guestName, resultName, ); console.log(result); @@ -862,7 +964,12 @@ export const main = async rawArgs => { .description( 'imports the named bundle in a confined space within a worker and runs its endow without any initial authority', ) - .option('-i,--inbox ', 'An alternate inbox', collect, []) + .option( + '-a,--as ', + 'Pose as named party (as named by current party)', + collect, + [], + ) .option( '-n,--name ', 'Assigns a name to the result for future reference, persisted between restarts', @@ -871,11 +978,11 @@ export const main = async rawArgs => { '-w,--worker ', 'Reuse an existing worker rather than create a new one', ) - .action(async (readableBundleName, outboxName, cmd) => { + .action(async (readableBundleName, guestName, cmd) => { const { name: resultName, worker: workerName, - inbox: inboxNames, + as: partyNames, } = cmd.opts(); const { getBootstrap } = await provideEndoClient( 'cli', @@ -884,14 +991,14 @@ export const main = async rawArgs => { ); try { const bootstrap = getBootstrap(); - let inbox = E(bootstrap).inbox(); - for (const inboxName of inboxNames) { - inbox = E(inbox).provide(inboxName); + let party = E(bootstrap).host(); + for (const partyName of partyNames) { + party = E(party).provide(partyName); } - const result = await E(inbox).importBundleAndEndow( + const result = await E(party).importBundleAndEndow( workerName, readableBundleName, - outboxName, + guestName, resultName, ); console.log(result); @@ -904,14 +1011,19 @@ export const main = async rawArgs => { program .command('open ') .description('opens a web page') - .option('-i,--inbox ', 'An alternate inbox', collect, []) + .option( + '-a,--as ', + 'Pose as named party (as named by current party)', + collect, + [], + ) .option('-b,--bundle ', 'Bundle for a web page to open') .option('-f,--file ', 'Build the named bundle from JavaScript file') .option('-g,--guest ', 'Endowment to give the web page') .option('-h,--host', 'Endow the web page with the powers of the host') .action(async (webPageName, cmd) => { const { - inbox: inboxNames, + as: partyNames, file: programPath, bundle: bundleName, guest: guestName, @@ -940,14 +1052,14 @@ export const main = async rawArgs => { try { const bootstrap = getBootstrap(); - let inbox = E(bootstrap).inbox(); - for (const inboxName of inboxNames) { - inbox = E(inbox).provide(inboxName); + let party = E(bootstrap).host(); + for (const partyName of partyNames) { + party = E(party).provide(partyName); } // Prepare a bundle, with the given name. if (bundleReaderRef !== undefined) { - await E(inbox).store(bundleReaderRef, bundleName); + await E(party).store(bundleReaderRef, bundleName); } /** @type {string | undefined} */ @@ -958,13 +1070,13 @@ export const main = async rawArgs => { bundleName !== undefined && (guestName !== undefined || endowHost) ) { - ({ url: webPageUrl } = await E(inbox).provideWebPage( + ({ url: webPageUrl } = await E(party).provideWebPage( webPageName, bundleName, guestName, // undefined if endowHost )); } else if (bundleName === undefined && guestName === undefined) { - ({ url: webPageUrl } = await E(inbox).provide(webPageName)); + ({ url: webPageUrl } = await E(party).provide(webPageName)); } else if (webPageUrl === undefined) { // webPageUrl will always be undefined if we fall through to here, // but calling it out helps narrow the type below. diff --git a/packages/daemon/src/daemon.js b/packages/daemon/src/daemon.js index ff9be16cc7..aa3502d23e 100644 --- a/packages/daemon/src/daemon.js +++ b/packages/daemon/src/daemon.js @@ -540,9 +540,7 @@ const makeEndoBootstrap = ( if (req !== undefined) { const resolveRequest = resolvers.get(req); if (resolveRequest === undefined) { - throw new Error( - `panic: a resolver must exist for every request`, - ); + throw new Error(`panic: a resolver must exist for every request`); } resolveRequest(harden(Promise.reject(harden(new Error(message))))); } @@ -566,14 +564,12 @@ const makeEndoBootstrap = ( host: hostFormulaIdentifier, store: guestStoreFormulaIdentifier, }; - // Behold, recursion: - // eslint-disable-next-line no-use-before-define - const { value, formulaIdentifier } = await provideValueForFormula( - formula, - 'guest-id512', - ); + const { value, formulaIdentifier: guestFormulaIdentifier } = + // Behold, recursion: + // eslint-disable-next-line no-use-before-define + await provideValueForFormula(formula, 'guest-id512'); if (petName !== undefined) { - await petStore.write(petName, formulaIdentifier); + await petStore.write(petName, guestFormulaIdentifier); } return value; } else if (!formulaIdentifier.startsWith('guest-id512:')) { @@ -646,9 +642,7 @@ const makeEndoBootstrap = ( } } if (workerFormulaIdentifier === undefined) { - throw new Error( - `panic: workerFormulaIdentifier must be defined`, - ); + throw new Error(`panic: workerFormulaIdentifier must be defined`); } return workerFormulaIdentifier; }; From 3cd564bf8c3fe0261f18e0a05bfab286218f3cae Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Fri, 30 Jun 2023 22:37:12 -0700 Subject: [PATCH 048/234] refactor(daemon): kebab-case petnames --- packages/daemon/src/daemon.js | 2 +- packages/daemon/src/pet-store.js | 2 +- packages/daemon/test/test-endo.js | 12 ++++++------ 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/daemon/src/daemon.js b/packages/daemon/src/daemon.js index aa3502d23e..4c51a1eb6b 100644 --- a/packages/daemon/src/daemon.js +++ b/packages/daemon/src/daemon.js @@ -23,7 +23,7 @@ import { makeOwnPetStore, makeIdentifiedPetStore } from './pet-store.js'; const { quote: q } = assert; -const validNamePattern = /^[a-zA-Z][a-zA-Z0-9]{0,127}$/; +const validNamePattern = /^[a-z][a-z0-9-]{0,127}$/; const zero512 = '0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'; diff --git a/packages/daemon/src/pet-store.js b/packages/daemon/src/pet-store.js index 49b24dbe22..00becfe5f2 100644 --- a/packages/daemon/src/pet-store.js +++ b/packages/daemon/src/pet-store.js @@ -2,7 +2,7 @@ import { Far } from '@endo/far'; const { quote: q } = assert; -const validNamePattern = /^[a-zA-Z][a-zA-Z0-9]{0,127}$/; +const validNamePattern = /^[a-z][a-z0-9-]{0,127}$/; const validIdPattern = /^[0-9a-f]{128}$/; const validFormulaPattern = /^(?:host|pet-store|(?:readable-blob-sha512|worker-id512|pet-store-id512|eval-id512|import-unsafe-id512|import-bundle-id512|host-id512|guest-id512):[0-9a-f]{128}|web-bundle:[0-9a-f]{32})$/; diff --git a/packages/daemon/test/test-endo.js b/packages/daemon/test/test-endo.js index ccc7c286e5..f8d1a0afaf 100644 --- a/packages/daemon/test/test-endo.js +++ b/packages/daemon/test/test-endo.js @@ -185,7 +185,7 @@ test('store', async t => { const bootstrap = getBootstrap(); const host = E(bootstrap).host(); const readerRef = makeReaderRef([new TextEncoder().encode('hello\n')]); - await E(host).store(readerRef, 'helloText'); + await E(host).store(readerRef, 'hello-text'); } { @@ -196,7 +196,7 @@ test('store', async t => { ); const bootstrap = getBootstrap(); const host = E(bootstrap).host(); - const readable = await E(host).provide('helloText'); + const readable = await E(host).provide('hello-text'); const actualText = await E(readable).text(); t.is(actualText, 'hello\n'); } @@ -232,13 +232,13 @@ test('closure state lost by restart', async t => { `, [], [], - 'counterMaker', + 'counter-maker', ); await E(host).evaluate( 'w1', `E(cm).makeCounter() `, ['cm'], - ['counterMaker'], + ['counter-maker'], 'counter', ); const one = await E(host).evaluate( @@ -318,9 +318,9 @@ test('persist unsafe services and their requests', async t => { ); const bootstrap = getBootstrap(); const host = E(bootstrap).host(); - await E(host).makeWorker('userWorker'); + await E(host).makeWorker('user-worker'); await E(host).evaluate( - 'userWorker', + 'user-worker', ` Far('Answer', { value: () => 42, From 474ff33db7cbf750432f5454f52bf8fb574b6b82 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Sat, 1 Jul 2023 13:36:49 -0700 Subject: [PATCH 049/234] refactor(daemon): Consolidate assertPetName --- packages/daemon/src/daemon.js | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/packages/daemon/src/daemon.js b/packages/daemon/src/daemon.js index 4c51a1eb6b..1b3140139a 100644 --- a/packages/daemon/src/daemon.js +++ b/packages/daemon/src/daemon.js @@ -27,6 +27,15 @@ const validNamePattern = /^[a-z][a-z0-9-]{0,127}$/; const zero512 = '0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'; +/** + * @param {string} petName + */ +const assertPetName = petName => { + if (typeof petName !== 'string' || !validNamePattern.test(petName)) { + throw new Error(`Invalid pet name ${q(petName)}`); + } +}; + const defaultHttpPort = 8920; // Eight Nine Duo Oh: ENDO. /** @@ -360,6 +369,7 @@ const makeEndoBootstrap = ( * @param {string} petName */ const provide = async petName => { + assertPetName(petName); const formulaIdentifier = guestPetStore.get(petName); if (formulaIdentifier === undefined) { throw new TypeError(`Unknown pet name: ${q(petName)}`); @@ -507,9 +517,7 @@ const makeEndoBootstrap = ( }; const resolve = async (requestNumber, resolutionName) => { - if (!validNamePattern.test(resolutionName)) { - throw new Error(`Invalid pet name ${q(resolutionName)}`); - } + assertPetName(resolutionName); if ( typeof requestNumber !== 'number' || requestNumber >= Number.MAX_SAFE_INTEGER @@ -592,9 +600,7 @@ const makeEndoBootstrap = ( */ const store = async (readerRef, petName) => { if (petName !== undefined) { - if (!validNamePattern.test(petName)) { - throw new Error(`Invalid pet name ${q(petName)}`); - } + assertPetName(petName); } const formulaIdentifier = await storeReaderRef(readerRef); @@ -685,8 +691,8 @@ const makeEndoBootstrap = ( workerName, ); - if (resultName !== undefined && !validNamePattern.test(resultName)) { - throw new Error(`Invalid pet name ${q(resultName)}`); + if (resultName !== undefined) { + assertPetName(resultName); } if (petNames.length !== codeNames.length) { throw new Error('Evaluator requires one pet name for each code name'); From de7959da33d3b80e38710612fb95ef838a563be4 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Sat, 1 Jul 2023 12:23:28 -0700 Subject: [PATCH 050/234] fix(daemon): Remove and rename must adjust live memo --- packages/daemon/src/daemon.js | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/packages/daemon/src/daemon.js b/packages/daemon/src/daemon.js index 1b3140139a..7e9f5672f6 100644 --- a/packages/daemon/src/daemon.js +++ b/packages/daemon/src/daemon.js @@ -379,7 +379,33 @@ const makeEndoBootstrap = ( return provideValueForFormulaIdentifier(formulaIdentifier); }; - const { list, remove, rename } = guestPetStore; + /** + * @param {string} fromName + * @param {string} toName + */ + const rename = async (fromName, toName) => { + assertPetName(fromName); + assertPetName(toName); + await guestPetStore.rename(fromName, toName); + const formulaIdentifier = responses.get(fromName); + if (formulaIdentifier === undefined) { + throw new Error( + `panic: the pet store rename must ensure that the renamed identifier exists`, + ); + } + responses.set(toName, formulaIdentifier); + responses.delete(fromName); + }; + + /** + * @param {string} petName + */ + const remove = async petName => { + await guestPetStore.remove(petName); + responses.delete(petName); + }; + + const { list } = guestPetStore; /** @type {import('@endo/eventual-send').ERef} */ const guest = Far('EndoGuest', { From bb7ad7b486f587a217aae40c42d9a6bfeeaf6212 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Sat, 1 Jul 2023 12:28:59 -0700 Subject: [PATCH 051/234] refactor(daemon): Support constants for workers and parties --- packages/daemon/src/daemon.js | 82 +++++++++++++++++++------------ packages/daemon/test/test-endo.js | 2 +- 2 files changed, 51 insertions(+), 33 deletions(-) diff --git a/packages/daemon/src/daemon.js b/packages/daemon/src/daemon.js index 7e9f5672f6..e85ba7bec4 100644 --- a/packages/daemon/src/daemon.js +++ b/packages/daemon/src/daemon.js @@ -38,6 +38,13 @@ const assertPetName = petName => { const defaultHttpPort = 8920; // Eight Nine Duo Oh: ENDO. +/** @type {import('./types.js').EndoGuest} */ +const leastAuthority = Far('EndoGuest', { + async request() { + throw new Error('declined'); + }, +}); + /** * @param {import('./types.js').DaemonicPowers} powers * @param {import('./types.js').Locator} locator @@ -658,37 +665,40 @@ const makeEndoBootstrap = ( }; /** - * @param {string | undefined} workerName + * @param {string | 'MAIN' | 'NEW'} workerName */ const provideWorkerFormulaIdentifier = async workerName => { - /** @type {string | undefined} */ - let workerFormulaIdentifier; - if (workerName === undefined) { - // TODO Using worker 0 should be an option, for evaluate formulas. + if (workerName === 'MAIN') { + return `worker-id512:${zero512}`; + } else if (workerName === 'NEW') { const workerId512 = await powers.randomHex512(); - workerFormulaIdentifier = `worker-id512:${workerId512}`; - } else { - workerFormulaIdentifier = petStore.get(workerName); - if (workerFormulaIdentifier === undefined) { - throw new Error(`Unknown worker for pet name: ${q(workerName)}`); - } + return `worker-id512:${workerId512}`; } + assertPetName(workerName); + let workerFormulaIdentifier = petStore.get(workerName); if (workerFormulaIdentifier === undefined) { - throw new Error(`panic: workerFormulaIdentifier must be defined`); + const workerId512 = await powers.randomHex512(); + workerFormulaIdentifier = `worker-id512:${workerId512}`; + await petStore.write(workerName, workerFormulaIdentifier); } return workerFormulaIdentifier; }; /** - * @param {string} [guestName] + * @param {string | 'NONE' | 'HOST' | 'ENDO'} partyName */ - const providePowersFormulaIdentifier = async guestName => { - if (guestName === undefined) { - return hostFormulaIdentifier; + const providePowersFormulaIdentifier = async partyName => { + if (partyName === 'NONE') { + return 'least-authority'; + } else if (partyName === 'HOST') { + return 'host'; + } else if (partyName === 'ENDO') { + return 'endo'; } - let guestFormulaIdentifier = petStore.get(guestName); + assertPetName(partyName); + let guestFormulaIdentifier = petStore.get(partyName); if (guestFormulaIdentifier === undefined) { - const guest = await makeGuest(guestName); + const guest = await provideGuest(partyName); guestFormulaIdentifier = formulaIdentifierForRef.get(guest); if (guestFormulaIdentifier === undefined) { throw new Error( @@ -700,7 +710,7 @@ const makeEndoBootstrap = ( }; /** - * @param {string | undefined} workerName + * @param {string | 'MAIN' | 'NEW'} workerName * @param {string} source * @param {Array} codeNames * @param {Array} petNames @@ -760,30 +770,30 @@ const makeEndoBootstrap = ( }; /** - * @param {string | undefined} workerName + * @param {string | 'NEW' | 'MAIN'} workerName * @param {string} importPath - * @param {string | undefined} guestName + * @param {string | 'NONE' | 'HOST' | 'ENDO'} powersName * @param {string} resultName */ const importUnsafeAndEndow = async ( workerName, importPath, - guestName, + powersName, resultName, ) => { const workerFormulaIdentifier = await provideWorkerFormulaIdentifier( workerName, ); - const guestFormulaIdentifier = await providePowersFormulaIdentifier( - guestName, + const powersFormulaIdentifier = await providePowersFormulaIdentifier( + powersName, ); const formula = { /** @type {'import-unsafe'} */ type: 'import-unsafe', worker: workerFormulaIdentifier, - powers: guestFormulaIdentifier, + powers: powersFormulaIdentifier, importPath, }; @@ -800,15 +810,15 @@ const makeEndoBootstrap = ( }; /** - * @param {string} workerName + * @param {string | 'MAIN' | 'NEW'} workerName * @param {string} bundleName - * @param {string | undefined} guestName + * @param {string | 'NONE' | 'HOST' | 'ENDO'} powersName * @param {string} resultName */ const importBundleAndEndow = async ( workerName, bundleName, - guestName, + powersName, resultName, ) => { const workerFormulaIdentifier = await provideWorkerFormulaIdentifier( @@ -820,15 +830,15 @@ const makeEndoBootstrap = ( throw new TypeError(`Unknown pet name for bundle: ${bundleName}`); } - const guestFormulaIdentifier = await providePowersFormulaIdentifier( - guestName, + const powersFormulaIdentifier = await providePowersFormulaIdentifier( + powersName, ); const formula = { /** @type {'import-bundle'} */ type: 'import-bundle', worker: workerFormulaIdentifier, - guest: guestFormulaIdentifier, + powers: powersFormulaIdentifier, bundle: bundleFormulaIdentifier, }; @@ -894,7 +904,7 @@ const makeEndoBootstrap = ( /** * @param {string} webPageName * @param {string} bundleName - * @param {string | undefined} powersName + * @param {string | 'NONE' | 'HOST' | 'ENDO'} powersName */ const provideWebPage = async (webPageName, bundleName, powersName) => { const bundleFormulaIdentifier = petStore.get(bundleName); @@ -1080,6 +1090,12 @@ const makeEndoBootstrap = ( return makeOwnPetStore(powers, locator, 'pet-store'); } else if (formulaIdentifier === 'host') { return makeIdentifiedHost(formulaIdentifier, 'pet-store'); + } else if (formulaIdentifier === 'endo') { + // Behold, self-referentiality: + // eslint-disable-next-line no-use-before-define + return endoBootstrap; + } else if (formulaIdentifier === 'least-authority') { + return leastAuthority; } else if (formulaIdentifier === 'web-page-js') { return makeValueForFormula('web-page-js', zero512, { type: /** @type {'import-unsafe'} */ ('import-unsafe'), @@ -1195,6 +1211,8 @@ const makeEndoBootstrap = ( host: () => provideValueForFormulaIdentifier('host'), + leastAuthority: () => leastAuthority, + webPageJs: () => provideValueForFormulaIdentifier('web-page-js'), importAndEndowInWebPage: async (webPageP, webPageNumber) => { diff --git a/packages/daemon/test/test-endo.js b/packages/daemon/test/test-endo.js index f8d1a0afaf..91bbb02307 100644 --- a/packages/daemon/test/test-endo.js +++ b/packages/daemon/test/test-endo.js @@ -106,7 +106,7 @@ test('anonymous spawn and evaluate', async t => { ); const bootstrap = getBootstrap(); const host = E(bootstrap).host(); - const ten = await E(host).evaluate(undefined, '10', [], []); + const ten = await E(host).evaluate('MAIN', '10', [], []); t.is(10, ten); await stop(locator); From 235e6b3127c0e1692a05d53a35f4dca06c662bd8 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Sat, 1 Jul 2023 17:05:07 -0700 Subject: [PATCH 052/234] refactor(cli): Conslidate import* as make, use worker/powers constants --- packages/cli/src/endo.js | 141 +++++++++++++++++++++++---------------- 1 file changed, 83 insertions(+), 58 deletions(-) diff --git a/packages/cli/src/endo.js b/packages/cli/src/endo.js index 2c92548998..c0797f88ec 100644 --- a/packages/cli/src/endo.js +++ b/packages/cli/src/endo.js @@ -90,6 +90,17 @@ const delay = async (ms, cancelled) => { }); }; +const randomHex16 = () => + new Promise((resolve, reject) => + crypto.randomBytes(16, (err, bytes) => { + if (err) { + reject(err); + } else { + resolve(bytes.toString('hex')); + } + }), + ); + const provideEndoClient = async (...args) => { try { // It is okay to fail to connect because the daemon is not running. @@ -867,7 +878,7 @@ export const main = async rawArgs => { .action(async (source, names, cmd) => { const { name: resultName, - worker: workerName, + worker: workerName = 'MAIN', as: partyNames, } = cmd.opts(); const { getBootstrap } = await provideEndoClient( @@ -915,61 +926,17 @@ export const main = async rawArgs => { }); program - .command('import-unsafe ') - .description( - 'imports the module at the given path and runs its endow function with all of your authority', - ) - .option( - '-a,--as ', - 'Pose as named party (as named by current party)', - collect, - [], - ) - .option( - '-n,--name ', - 'Assigns a name to the result for future reference, persisted between restarts', - ) - .option( - '-w,--worker ', - 'Reuse an existing worker rather than create a new one', - ) - .action(async (importPath, guestName, cmd) => { - const { name: resultName, worker: workerName, partyNames } = cmd.opts(); - const { getBootstrap } = await provideEndoClient( - 'cli', - sockPath, - cancelled, - ); - try { - const bootstrap = getBootstrap(); - let party = E(bootstrap).host(); - for (const partyName of partyNames) { - party = E(party).provide(partyName); - } - const result = await E(party).importUnsafeAndEndow( - workerName, - path.resolve(importPath), - guestName, - resultName, - ); - console.log(result); - } catch (error) { - console.error(error); - cancel(error); - } - }); - - program - .command('import-bundle ') - .description( - 'imports the named bundle in a confined space within a worker and runs its endow without any initial authority', - ) + .command('make [file]') + .description('Makes a plugin or a worker caplet (worklet)') + .option('-b,--bundle ', 'Bundle for a web page to open') + .option('--UNSAFE ', 'Path to a Node.js module') .option( '-a,--as ', 'Pose as named party (as named by current party)', collect, [], ) + .option('-p,--powers ', 'Name of powers to grant or NONE, HOST, ENDO') .option( '-n,--name ', 'Assigns a name to the result for future reference, persisted between restarts', @@ -978,12 +945,51 @@ export const main = async rawArgs => { '-w,--worker ', 'Reuse an existing worker rather than create a new one', ) - .action(async (readableBundleName, guestName, cmd) => { + .action(async (filePath, cmd) => { const { + UNSAFE: importPath, name: resultName, - worker: workerName, + worker: workerName = 'NEW', as: partyNames, + powers: powersName = 'NONE', } = cmd.opts(); + let { bundle: bundleName } = cmd.opts(); + + if (filePath !== undefined && importPath !== undefined) { + console.error('Specify only one of [file] or --UNSAFE '); + process.exitCode = 1; + return; + } + if ( + filePath === undefined && + importPath === undefined && + bundleName === undefined + ) { + console.error( + 'Specify at least one of [file], --bundle , or --UNSAFE ', + ); + process.exitCode = 1; + return; + } + + /** @type {import('@endo/eventual-send').ERef> | undefined} */ + let bundleReaderRef; + /** @type {string | undefined} */ + let temporaryBundleName; + if (filePath !== undefined) { + if (bundleName === undefined) { + // TODO alternately, make a temporary session-scoped GC pet store + // overshadowing the permanent one, which gets implicitly dropped + // when this CLI CapTP session ends. + temporaryBundleName = `tmp-bundle-${await randomHex16()}`; + bundleName = temporaryBundleName; + } + const bundle = await bundleSource(filePath); + const bundleText = JSON.stringify(bundle); + const bundleBytes = textEncoder.encode(bundleText); + bundleReaderRef = makeReaderRef([bundleBytes]); + } + const { getBootstrap } = await provideEndoClient( 'cli', sockPath, @@ -995,13 +1001,32 @@ export const main = async rawArgs => { for (const partyName of partyNames) { party = E(party).provide(partyName); } - const result = await E(party).importBundleAndEndow( - workerName, - readableBundleName, - guestName, - resultName, - ); + + // Prepare a bundle, with the given name. + if (bundleReaderRef !== undefined) { + await E(party).store(bundleReaderRef, bundleName); + } + + const resultP = + importPath !== undefined + ? E(party).importUnsafeAndEndow( + workerName, + path.resolve(importPath), + powersName, + resultName, + ) + : E(party).importBundleAndEndow( + workerName, + bundleName, + powersName, + resultName, + ); + const result = await resultP; console.log(result); + + if (temporaryBundleName) { + await E(party).remove(temporaryBundleName); + } } catch (error) { console.error(error); cancel(error); From 7513bc652634b03bda07fce02642ddfe97640d52 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Sat, 1 Jul 2023 12:32:18 -0700 Subject: [PATCH 053/234] refactor(daemon, cli): Rename namespace endow to make --- packages/cli/demo/cat.js | 2 +- packages/cli/src/endo.js | 12 ++++++++---- packages/daemon/src/web-page-bundler.js | 2 +- packages/daemon/src/web-page.js | 2 +- packages/daemon/src/worker.js | 4 ++-- packages/daemon/test/service.js | 2 +- 6 files changed, 14 insertions(+), 10 deletions(-) diff --git a/packages/cli/demo/cat.js b/packages/cli/demo/cat.js index 0a9ade039d..9ff4f011ca 100644 --- a/packages/cli/demo/cat.js +++ b/packages/cli/demo/cat.js @@ -91,7 +91,7 @@ const followMessagesComponent = async (parentElement, powers) => { } }; -export const endow = async powers => { +export const make = async powers => { document.body.innerHTML = '

🐈‍⬛ Familiar Chat

Or: Le Chat Familier

'; followMessagesComponent(document.body, powers).catch(window.reportError); diff --git a/packages/cli/src/endo.js b/packages/cli/src/endo.js index c0797f88ec..0c020c2e40 100644 --- a/packages/cli/src/endo.js +++ b/packages/cli/src/endo.js @@ -758,7 +758,7 @@ export const main = async rawArgs => { }); program - .command('request ') + .command('request ') .description('requests a reference with the given description') .option( '-a,--as ', @@ -771,8 +771,13 @@ export const main = async rawArgs => { 'Assigns a name to the result for future reference, persisted between restarts', ) .option('-w,--wait', 'Waits for and prints the response') - .action(async (guestName, description, cmd) => { + .action(async (description, cmd) => { const { name: resultName, as: partyNames, wait } = cmd.opts(); + if (partyNames.length === 0) { + console.error('Specify the name of a guest with -a or --as '); + process.exitCode = 1; + return; + } const { getBootstrap } = await provideEndoClient( 'cli', sockPath, @@ -784,8 +789,7 @@ export const main = async rawArgs => { for (const partyName of partyNames) { party = E(party).provideGuest(partyName); } - const guestP = E(party).provide(guestName); - const resultP = E(guestP).request(description, resultName); + const resultP = E(party).request(description, resultName); if (wait || resultName === undefined) { const result = await resultP; console.log(result); diff --git a/packages/daemon/src/web-page-bundler.js b/packages/daemon/src/web-page-bundler.js index 14da52df34..e8ffda7407 100644 --- a/packages/daemon/src/web-page-bundler.js +++ b/packages/daemon/src/web-page-bundler.js @@ -11,6 +11,6 @@ import { fileURLToPath } from 'url'; const read = async location => fs.promises.readFile(fileURLToPath(location)); -export const endow = async () => { +export const make = async () => { return makeBundle(read, new URL('web-page.js', import.meta.url).href); }; diff --git a/packages/daemon/src/web-page.js b/packages/daemon/src/web-page.js index fd844f2f2b..796fcf075d 100644 --- a/packages/daemon/src/web-page.js +++ b/packages/daemon/src/web-page.js @@ -33,7 +33,7 @@ const bootstrap = Far('WebFacet', { const namespace = await importBundle(bundle, { endowments, }); - return namespace.endow(powers); + return namespace.make(powers); }, reject(message) { document.body.innerHTML = ''; diff --git a/packages/daemon/src/worker.js b/packages/daemon/src/worker.js index 53635edc96..851248331f 100644 --- a/packages/daemon/src/worker.js +++ b/packages/daemon/src/worker.js @@ -58,7 +58,7 @@ export const makeWorkerFacet = ({ importUnsafeAndEndow: async (path, powersP) => { const url = pathToFileURL(path); const namespace = await import(url); - return namespace.endow(powersP); + return namespace.make(powersP); }, /** @@ -75,7 +75,7 @@ export const makeWorkerFacet = ({ const namespace = await importBundle(bundle, { endowments, }); - return namespace.endow(powersP); + return namespace.make(powersP); }, }); }; diff --git a/packages/daemon/test/service.js b/packages/daemon/test/service.js index bb6796cc94..68edf88e8b 100644 --- a/packages/daemon/test/service.js +++ b/packages/daemon/test/service.js @@ -1,6 +1,6 @@ import { E, Far } from '@endo/far'; -export const endow = powers => { +export const make = powers => { return Far('Service', { async ask() { return E(powers).request( From 04a9af2862c77ac86678a0a70f6d4360a9a53226 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Sat, 1 Jul 2023 15:51:57 -0700 Subject: [PATCH 054/234] fix(cli): Commands do not flush unless you await the result --- packages/cli/src/endo.js | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/packages/cli/src/endo.js b/packages/cli/src/endo.js index 0c020c2e40..e7cda60474 100644 --- a/packages/cli/src/endo.js +++ b/packages/cli/src/endo.js @@ -770,9 +770,8 @@ export const main = async rawArgs => { '-n,--name ', 'Assigns a name to the result for future reference, persisted between restarts', ) - .option('-w,--wait', 'Waits for and prints the response') .action(async (description, cmd) => { - const { name: resultName, as: partyNames, wait } = cmd.opts(); + const { name: resultName, as: partyNames } = cmd.opts(); if (partyNames.length === 0) { console.error('Specify the name of a guest with -a or --as '); process.exitCode = 1; @@ -789,11 +788,8 @@ export const main = async rawArgs => { for (const partyName of partyNames) { party = E(party).provideGuest(partyName); } - const resultP = E(party).request(description, resultName); - if (wait || resultName === undefined) { - const result = await resultP; - console.log(result); - } + const result = await E(party).request(description, resultName); + console.log(result); } catch (error) { console.error(error); cancel(error); From 8ce61be38ff5c942624e278ba2639b6fff5747d2 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Sat, 1 Jul 2023 17:14:06 -0700 Subject: [PATCH 055/234] feat(cli): Support runlets --- packages/cli/package.json | 1 + packages/cli/src/endo.js | 34 ++++++++++++ packages/cli/src/run.js | 107 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 142 insertions(+) create mode 100644 packages/cli/src/run.js diff --git a/packages/cli/package.json b/packages/cli/package.json index a63f3cf1de..c2ac6aa430 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -35,6 +35,7 @@ "@endo/stream-node": "^0.2.29", "@endo/where": "^0.3.4", "@endo/bundle-source": "^2.7.0", + "@endo/import-bundle": "^0.4.1", "commander": "^5.0.0", "ses": "^0.18.7", "open": "^9.1.0" diff --git a/packages/cli/src/endo.js b/packages/cli/src/endo.js index e7cda60474..c858447df0 100644 --- a/packages/cli/src/endo.js +++ b/packages/cli/src/endo.js @@ -1115,6 +1115,40 @@ export const main = async rawArgs => { } }); + program + .command('run [] [...]') + .description( + 'import a caplet to run at the CLI (runlet), endow it with capabilities, make and store its public API', + ) + .option( + '-a,--as ', + 'Pose as named party (as named by current party)', + collect, + [], + ) + .option('-b,--bundle ', 'Bundle name for the caplet program') + .option('--UNSAFE ', 'Or path of an unsafe plugin to run in Node.js') + .option( + '-p,--powers ', + 'Endowment to give the worklet (a name, NONE, HOST, or ENDO)', + ) + .action(async (filePath, args, cmd) => { + const { run } = await import('./run.js'); + return run( + { + provideEndoClient, + cancel, + cancelled, + sockPath, + }, + { + file: filePath, + args, + ...cmd.opts(), + }, + ); + }); + // Throw an error instead of exiting directly. program.exitOverride(); diff --git a/packages/cli/src/run.js b/packages/cli/src/run.js new file mode 100644 index 0000000000..b2a49059bb --- /dev/null +++ b/packages/cli/src/run.js @@ -0,0 +1,107 @@ +/* global process */ +import { E, Far } from '@endo/far'; +import bundleSource from '@endo/bundle-source'; +import url from 'url'; + +const endowments = harden({ + assert, + E, + Far, + TextEncoder, + TextDecoder, + URL, + console, +}); + +export const run = async ( + { provideEndoClient, cancel, cancelled, sockPath }, + { + as: partyNames, + file: filePath, + bundle: bundleName, + UNSAFE: importPath, + powers: powersName = 'NONE', + args, + }, +) => { + if ( + filePath === undefined && + importPath === undefined && + bundleName === undefined + ) { + console.error('Specify at least one of --file, --bundle, or --UNSAFE'); + process.exitCode = 1; + return; + } + + const { getBootstrap } = await provideEndoClient('cli', sockPath, cancelled); + try { + const bootstrap = getBootstrap(); + let party = E(bootstrap).host(); + for (const partyName of partyNames) { + party = E(party).provide(partyName); + } + + let powersP; + if (powersName === 'NONE') { + powersP = E(bootstrap).leastAuthority(); + } else if (powersName === 'HOST') { + powersP = party; + } else if (powersName === 'ENDO') { + powersP = bootstrap; + } else { + powersP = E(party).provideGuest(powersName); + } + + if (importPath !== undefined) { + if (bundleName !== undefined) { + console.error('Must specify either --bundle or --UNSAFE, not both'); + process.exitCode = 1; + return; + } + if (filePath !== undefined) { + args.unshift(filePath); + } + + const importUrl = url.pathToFileURL(importPath); + const namespace = await import(importUrl); + const result = await namespace.main(powersP, ...args); + if (result !== undefined) { + console.log(result); + } + } else { + /** @type {any} */ + let bundle; + if (bundleName !== undefined) { + if (importPath !== undefined) { + console.error('Must specify either --bundle or --UNSAFE, not both'); + process.exitCode = 1; + return; + } + if (filePath !== undefined) { + args.unshift(filePath); + } + + const readableP = E(party).provide(bundleName); + const bundleText = await E(readableP).text(); + bundle = JSON.parse(bundleText); + } else { + bundle = await bundleSource(filePath); + } + + // We defer importing the import-bundle machinery to this in order to + // avoid an up-front cost for workers that never use importBundle. + const { importBundle } = await import('@endo/import-bundle'); + const namespace = await importBundle(bundle, { + endowments, + }); + const result = await namespace.main(powersP, ...args); + if (result !== undefined) { + console.log(result); + } + } + } catch (error) { + console.error(error); + cancel(error); + } +}; From fcc3c8e0f081e9e2a7d1b10e0eedb42f43bd86fb Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Sat, 1 Jul 2023 17:14:59 -0700 Subject: [PATCH 056/234] refactor(cli): Realign weblets with worklets and runlets --- packages/cli/src/endo.js | 60 +++++++++++++++++++--------------------- 1 file changed, 28 insertions(+), 32 deletions(-) diff --git a/packages/cli/src/endo.js b/packages/cli/src/endo.js index c858447df0..961a7606fc 100644 --- a/packages/cli/src/endo.js +++ b/packages/cli/src/endo.js @@ -1034,7 +1034,7 @@ export const main = async rawArgs => { }); program - .command('open ') + .command('open [filePath]') .description('opens a web page') .option( '-a,--as ', @@ -1043,30 +1043,25 @@ export const main = async rawArgs => { [], ) .option('-b,--bundle ', 'Bundle for a web page to open') - .option('-f,--file ', 'Build the named bundle from JavaScript file') - .option('-g,--guest ', 'Endowment to give the web page') - .option('-h,--host', 'Endow the web page with the powers of the host') - .action(async (webPageName, cmd) => { - const { - as: partyNames, - file: programPath, - bundle: bundleName, - guest: guestName, - host: endowHost, - } = cmd.opts(); - const { getBootstrap } = await provideEndoClient( - 'cli', - sockPath, - cancelled, - ); + .option( + '-p,--powers ', + 'Endowment to give the weblet (a name, NONE, HOST, or ENDO)', + ) + .action(async (webPageName, programPath, cmd) => { + const { as: partyNames, powers: powersName = 'NONE' } = cmd.opts(); + let { bundle: bundleName } = cmd.opts(); /** @type {import('@endo/eventual-send').ERef> | undefined} */ let bundleReaderRef; + /** @type {string | undefined} */ + let temporaryBundleName; if (programPath !== undefined) { if (bundleName === undefined) { - // TODO come up with something for a temporary reference. - // Maybe expose a nonce. - throw new Error('bundle name is required for page from file'); + // TODO alternately, make a temporary session-scoped GC pet store + // overshadowing the permanent one, which gets implicitly dropped + // when this CLI CapTP session ends. + temporaryBundleName = `tmp-bundle-${await randomHex16()}`; + bundleName = temporaryBundleName; } const bundle = await bundleSource(programPath); console.log(bundle.endoZipBase64Sha512); @@ -1075,6 +1070,12 @@ export const main = async rawArgs => { bundleReaderRef = makeReaderRef([bundleBytes]); } + const { getBootstrap } = await provideEndoClient( + 'cli', + sockPath, + cancelled, + ); + try { const bootstrap = getBootstrap(); let party = E(bootstrap).host(); @@ -1089,26 +1090,21 @@ export const main = async rawArgs => { /** @type {string | undefined} */ let webPageUrl; - if (guestName !== undefined && endowHost) { - throw new Error('choose either guest or host endowment, not both'); - } else if ( - bundleName !== undefined && - (guestName !== undefined || endowHost) - ) { + if (bundleName !== undefined) { ({ url: webPageUrl } = await E(party).provideWebPage( webPageName, bundleName, - guestName, // undefined if endowHost + powersName, )); - } else if (bundleName === undefined && guestName === undefined) { + } else { ({ url: webPageUrl } = await E(party).provide(webPageName)); - } else if (webPageUrl === undefined) { - // webPageUrl will always be undefined if we fall through to here, - // but calling it out helps narrow the type below. - throw new Error('both or neither bundle and endowment required'); } console.log(webPageUrl); open(webPageUrl); + + if (temporaryBundleName) { + await E(party).remove(temporaryBundleName); + } } catch (error) { console.error(error); cancel(error); From 65796f25e1b578a9b360c8cabac24e7d165ce457 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Sat, 1 Jul 2023 17:15:39 -0700 Subject: [PATCH 057/234] docs(cli): Add demos --- packages/cli/demo/README.md | 236 +++++++++++++++++++++++++++++++++++ packages/cli/demo/cat.js | 9 +- packages/cli/demo/counter.js | 11 ++ packages/cli/demo/doubler.js | 11 ++ packages/cli/demo/runlet.js | 4 + 5 files changed, 266 insertions(+), 5 deletions(-) create mode 100644 packages/cli/demo/README.md create mode 100644 packages/cli/demo/counter.js create mode 100644 packages/cli/demo/doubler.js create mode 100644 packages/cli/demo/runlet.js diff --git a/packages/cli/demo/README.md b/packages/cli/demo/README.md new file mode 100644 index 0000000000..b29962f744 --- /dev/null +++ b/packages/cli/demo/README.md @@ -0,0 +1,236 @@ + +# Install Endo + +> npm install -g @endo/cli + +# A counter example + +This is a worker caplet, or worklet, that counts numbers. + +```js +import { Far } from '@endo/far'; + +export const make = () => { + let counter = 0; + return Far('Counter', { + incr() { + counter += 1; + return counter; + }, + }); +}; +``` + +We can create an instance of the counter and give it a name. + +``` +> endo make counter.js --name counter +``` + +Then, we can send messages to the counter and see their responses. +These `endo eval` commands are executing a tightly confined JavaScript program +and reporting the program's completion value. +Because of the confinement to a private Hardened JavaScript Compartment, +the program must be endowed with all of its dependencies, +in this case, a `counter`. + +``` +> endo eval 'E(counter).incr()' counter +1 +> endo eval 'E(counter).incr()' counter +2 +> endo eval 'E(counter).incr()' counter +3 +``` + +> Aside: in all the above cases, we use `counter` both as the property name +> that will appear in the compartment's global object (and global scope) as +> well as the name of formula that produced the value. +> These may be different. +> +> ``` +> > endo eval 'E(c).incr()' c:counter +> 4 +> ``` + +Endo preserves the commands that led to the creation of the `counter` value, +which form a directed acyclic graph of "formulas". +If we kill the Endo Pet Daemon and all its workers (or "vats"), the memory +of how these values were made remains, but their memory is otherwise lost. + +``` +> endo restart +> endo eval 'E(counter).incr()' counter +1 +> endo eval 'E(counter).incr()' counter +2 +> endo eval 'E(counter).incr()' counter +3 +``` + +> Aside, since Eventual Send, the machinery under the `E` operator, abstracts +> the counter reference in both space and time, it does not matter much which +> process these evaluations occur in. +> The default is a worker called `MAIN`, whose formula number is 0. +> Use the `-w` or `--worker` flag to specify a different worker. +> +> ``` +> > endo spawn greeter +> > endo eval --worker greeter '"Hello, World!"' --name greeting +> Hello, World! +> > endo show greeting +> Hello, World! +> ``` + +# Doubler Agent + +The counter example requires no additional authority. +It provides a simple service and depends only on the ability to compute. + +The doubler worklet depends upon a counter, which it doubles. +We can use the doubler to demonstrate how caplets can run as guests +and request additional capabilities from the user. + +```js +import { E, Far } from '@endo/far'; + +export const make = powers => { + const counter = E(powers).request( + 'a counter, suitable for doubling', + 'my-counter' + ); + return Far('Doubler', { + async incr() { + const n = await E(counter).incr(); + return n * 2; + }, + }); +}; +``` + +The doubler receives a `powers` object: an interface granted by the host user +through which it obtains all of its authority. +In this example, the doubler requests another counter from the user. + +We make a doubler mostly the same way we made the counter. +However, we must give a name to the agent running the doubler, which we will +later use to recognize requests coming from the doubler. + +``` +> endo make doubler.js --name doubler --powers doubler-agent +``` + +This creates a doubler, but the doubler cannot respond until we +resolve its request for a counter. + +``` +> endo inbox +0. "doubler-agent" requested "please give me a counter" +> endo resolve 0 counter +``` + +> Aside, `endo reject 0` would have rejected the request, +> leaving the `doubler-agent` permanently broken. + +Now we can get a response from the doubler. + +``` +> endo eval 'E(doubler).incr()' doubler +8 +> endo eval 'E(doubler).incr()' doubler +10 +> endo eval 'E(doubler).incr()' doubler +12 +``` + +Also, in the optional second argument to `request`, `doubler.js` names the +request `my-counter`. +Any subsequent time a worklet running with the powers of `doubler-agent` asks +for a power using the name `my-counter`, it will get a reference to the same +eventual response. +The daemon preserves the formulas needed to recreate `my-counter` across +restarts and reboots. + +``` +> endo restart +> endo eval 'E(doubler).incr()' doubler +2 +> endo eval 'E(doubler).incr()' doubler +4 +> endo eval 'E(doubler).incr()' doubler +6 +``` + +# Familiar Chat + +The pet daemon (or familiar, if you will) maintains a petstore and mailbox for +each agent, like you (the host), and all your guests (like the doubler-agent). +The `endo list` command shows you the pet names in your pet store. +The `endo inbox` command (and `endo inbox --follow` command), shows +messages from your various guests. + +Weblets are web page caplets. +These are programs, like the counter and doubler above, except that +they run in a web page. +The pet daemon can host any number of fully independent web applications on +subdomains of `endo.localhost:8920`. +Each of these applications is connected to the pet daemon and can make +the same kinds of requests for data and powers. + +_Familiar Chat_ is an example application that provides a web app for +interacting with your pet daemon. + +``` +> endo open familiar-chat cat.js --powers HOST +``` + +This command creates a web page named familiar-chat and endows it with the +authority to maintain your petstore and mailbox. + +So, if you were to simulate a request from your cat: + +``` +> endo request 'pet me' --as cat +``` + +This will appear in your Familiar Chat web page, where you can resolve +or reject that request with any value you have a pet name for. +For example, in your web browser, you will see something like: + +> ## Familiar Chat +> 1. *cat* requests "pet me" `[ ]` `[resolve]` `[reject]` + +If you enter the name `counter` and press `[resolve]` and return to your +terminal, you will see that the `endo request 'pet me' --as cat` command has +received the counter and exited. + +> ## Familiar Chat +> 1. *cat* requests "pet me" _fulfilled_ + +# Running a confined script + +Beyond weblets and worklets, a runlet is more like a `node` script: +it runs in your shell and interacts with you directly, not in a supervised +worker process behind the scenes. +But, like other caplets, it is fully confined and only receives the powers +you have granted. +They can also receive additional command line arguments. + +Instead of exporting a `make` function, a runlet exports a `main` function +with a slightly different signature. + +```js +export const main = async (powers, ...args) => { + console.log('Hello, World!', args); + return 42; +}; +``` + +If a runlet returns a promise for some value, it will print that value +before exiting gracefully. + +``` +> endo run runlet.js a b c +Hello, World! [ 'a', 'b', 'c' ] +42 +``` diff --git a/packages/cli/demo/cat.js b/packages/cli/demo/cat.js index 9ff4f011ca..4b3d020a00 100644 --- a/packages/cli/demo/cat.js +++ b/packages/cli/demo/cat.js @@ -4,18 +4,17 @@ // This command will set up the cat page, create a URL, // and open it. // -// > endo open --file cat.js --bundle catBundle --host catPage +// > endo open familiar-chat cat.js --powers HOST // // Thereafter, // -// > endo open catPage +// > endo open fami ar-chat // // To interact with the permission manager, you can mock requests from a fake // guest. // -// > endo eval 42 -n ft -// > endo mkguest cat -// > endo request cat 'pet me' +// > endo eval 42 --name ft +// > endo request --as cat 'pet me' // // At this point, the command will pause, waiting for a response. // In the Familiar Chat window, resolve the request with the pet name "ft" and diff --git a/packages/cli/demo/counter.js b/packages/cli/demo/counter.js new file mode 100644 index 0000000000..87f44e5b9f --- /dev/null +++ b/packages/cli/demo/counter.js @@ -0,0 +1,11 @@ +import { Far } from '@endo/far'; + +export const make = powers => { + let counter = 0; + return Far('Counter', { + incr() { + counter += 1; + return counter; + }, + }); +}; diff --git a/packages/cli/demo/doubler.js b/packages/cli/demo/doubler.js new file mode 100644 index 0000000000..db2be5d876 --- /dev/null +++ b/packages/cli/demo/doubler.js @@ -0,0 +1,11 @@ +import { E, Far } from '@endo/far'; + +export const make = powers => { + const counter = E(powers).request('please give me a counter', 'counter'); + return Far('Doubler', { + async incr() { + const n = await E(counter).incr(); + return n * 2; + }, + }); +}; diff --git a/packages/cli/demo/runlet.js b/packages/cli/demo/runlet.js new file mode 100644 index 0000000000..3cfe70138b --- /dev/null +++ b/packages/cli/demo/runlet.js @@ -0,0 +1,4 @@ +export const main = async (powers, ...args) => { + console.log('Hello, World!', args); + return 42; +}; From c24c8afb2073495a151b6fa6b35219301d12190f Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Wed, 5 Jul 2023 16:19:35 -0700 Subject: [PATCH 058/234] fix(daemon): Create directories earlier --- packages/daemon/src/daemon.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/daemon/src/daemon.js b/packages/daemon/src/daemon.js index e85ba7bec4..d5123a1b89 100644 --- a/packages/daemon/src/daemon.js +++ b/packages/daemon/src/daemon.js @@ -1268,6 +1268,11 @@ export const main = async (powers, locator, pid, cancel, cancelled) => { cancelGracePeriod(error); }; + const statePathP = powers.makePath(locator.statePath); + const ephemeralStatePathP = powers.makePath(locator.ephemeralStatePath); + const cachePathP = powers.makePath(locator.cachePath); + await Promise.all([statePathP, cachePathP, ephemeralStatePathP]); + let nextConnectionNumber = 0; /** @type {Set>} */ const connectionClosedPromises = new Set(); @@ -1409,11 +1414,6 @@ export const main = async (powers, locator, pid, cancel, cancelled) => { gracePeriodElapsed, }); - const statePathP = powers.makePath(locator.statePath); - const ephemeralStatePathP = powers.makePath(locator.ephemeralStatePath); - const cachePathP = powers.makePath(locator.cachePath); - await Promise.all([statePathP, cachePathP, ephemeralStatePathP]); - const pidPath = powers.joinPath(locator.ephemeralStatePath, 'endo.pid'); await powers From 291713e3f2c3b1401e0a7037fe1badd50cba196f Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Wed, 5 Jul 2023 16:23:46 -0700 Subject: [PATCH 059/234] feat(cli): Add endo bin to package descriptor --- packages/cli/package.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/cli/package.json b/packages/cli/package.json index c2ac6aa430..0e538cc2f8 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -13,6 +13,9 @@ "bugs": { "url": "https://github.com/endojs/endo/issues" }, + "bin": { + "endo": "./bin/endo" + }, "type": "module", "exports": {}, "scripts": { From 72a92adbae1522638692ef0d967eec483d3b29cd Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Thu, 10 Aug 2023 10:52:33 -0700 Subject: [PATCH 060/234] chore: Update yarn.lock --- yarn.lock | 137 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 136 insertions(+), 1 deletion(-) diff --git a/yarn.lock b/yarn.lock index f7070d72b4..63e09d9b45 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3085,6 +3085,11 @@ benchmark@^2.1.4: lodash "^4.17.4" platform "^1.3.3" +big-integer@^1.6.44: + version "1.6.51" + resolved "https://registry.yarnpkg.com/big-integer/-/big-integer-1.6.51.tgz#0df92a5d9880560d3ff2d5fd20245c889d130686" + integrity sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg== + big.js@^5.2.2: version "5.2.2" resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" @@ -3153,6 +3158,13 @@ boolbase@^1.0.0, boolbase@~1.0.0: resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" integrity sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww== +bplist-parser@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/bplist-parser/-/bplist-parser-0.2.0.tgz#43a9d183e5bf9d545200ceac3e712f79ebbe8d0e" + integrity sha512-z0M+byMThzQmD9NILRniCUXYsYpjwnlO8N5uCFaCqIOpqRsJCrQL9NK3JsD67CN5a08nF5oIL2bD6loTdHOuKw== + dependencies: + big-integer "^1.6.44" + brace-expansion@^1.1.7: version "1.1.11" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" @@ -3422,6 +3434,13 @@ builtins@^5.0.0: dependencies: semver "^7.0.0" +bundle-name@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/bundle-name/-/bundle-name-3.0.0.tgz#ba59bcc9ac785fb67ccdbf104a2bf60c099f0e1a" + integrity sha512-PKA4BeSvBpQKQ8iPOGCSiell+N8P+Tf1DlwqmYhpe2gAhKPHn8EYOxVT+ShuGmhg8lN8XiSlS80yiExKXrURlw== + dependencies: + run-applescript "^5.0.0" + byte-size@^7.0.0: version "7.0.1" resolved "https://registry.yarnpkg.com/byte-size/-/byte-size-7.0.1.tgz#b1daf3386de7ab9d706b941a748dbfc71130dee3" @@ -4592,6 +4611,24 @@ deepmerge@^4.2.2: resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955" integrity sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg== +default-browser-id@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/default-browser-id/-/default-browser-id-3.0.0.tgz#bee7bbbef1f4e75d31f98f4d3f1556a14cea790c" + integrity sha512-OZ1y3y0SqSICtE8DE4S8YOE9UZOJ8wO16fKWVP5J1Qz42kV9jcnMVFrEE/noXb/ss3Q4pZIH79kxofzyNNtUNA== + dependencies: + bplist-parser "^0.2.0" + untildify "^4.0.0" + +default-browser@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/default-browser/-/default-browser-4.0.0.tgz#53c9894f8810bf86696de117a6ce9085a3cbc7da" + integrity sha512-wX5pXO1+BrhMkSbROFsyxUm0i/cJEScyNhA4PPxc41ICuv05ZZB/MX28s8aZx6xjmatvebIapF6hLEKEcpneUA== + dependencies: + bundle-name "^3.0.0" + default-browser-id "^3.0.0" + execa "^7.1.1" + titleize "^3.0.0" + defaults@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.3.tgz#c656051e9817d9ff08ed881477f3fe4019f3ef7d" @@ -4604,6 +4641,11 @@ define-lazy-prop@^2.0.0: resolved "https://registry.yarnpkg.com/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz#3f7ae421129bcaaac9bc74905c98a0009ec9ee7f" integrity sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og== +define-lazy-prop@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz#dbb19adfb746d7fc6d734a06b72f4a00d021255f" + integrity sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg== + define-properties@^1.1.3, define-properties@^1.1.4, define-properties@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.2.0.tgz#52988570670c9eacedd8064f4a990f2405849bd5" @@ -5471,6 +5513,21 @@ execa@^5.0.0: signal-exit "^3.0.3" strip-final-newline "^2.0.0" +execa@^7.1.1: + version "7.2.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-7.2.0.tgz#657e75ba984f42a70f38928cedc87d6f2d4fe4e9" + integrity sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA== + dependencies: + cross-spawn "^7.0.3" + get-stream "^6.0.1" + human-signals "^4.3.0" + is-stream "^3.0.0" + merge-stream "^2.0.0" + npm-run-path "^5.1.0" + onetime "^6.0.0" + signal-exit "^3.0.7" + strip-final-newline "^3.0.0" + execution-time@^1.2.0: version "1.4.1" resolved "https://registry.yarnpkg.com/execution-time/-/execution-time-1.4.1.tgz#d942f835ca9fd608691c4912d55458fc4a495271" @@ -6014,7 +6071,7 @@ get-port@^5.1.1: resolved "https://registry.yarnpkg.com/get-port/-/get-port-5.1.1.tgz#0469ed07563479de6efb986baf053dcd7d4e3193" integrity sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ== -get-stream@^6.0.0: +get-stream@^6.0.0, get-stream@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== @@ -6598,6 +6655,11 @@ human-signals@^2.1.0: resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== +human-signals@^4.3.0: + version "4.3.1" + resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-4.3.1.tgz#ab7f811e851fca97ffbd2c1fe9a958964de321b2" + integrity sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ== + humanize-ms@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/humanize-ms/-/humanize-ms-1.2.1.tgz#c46e3159a293f6b896da29316d8b6fe8bb79bbed" @@ -7036,6 +7098,11 @@ is-docker@^2.0.0, is-docker@^2.1.1: resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa" integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ== +is-docker@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-3.0.0.tgz#90093aa3106277d8a77a5910dbae71747e15a200" + integrity sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ== + is-error@^2.2.2: version "2.2.2" resolved "https://registry.yarnpkg.com/is-error/-/is-error-2.2.2.tgz#c10ade187b3c93510c5470a5567833ee25649843" @@ -7101,6 +7168,13 @@ is-html@^1.1.0: dependencies: html-tags "^1.0.0" +is-inside-container@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-inside-container/-/is-inside-container-1.0.0.tgz#e81fba699662eb31dbdaf26766a61d4814717ea4" + integrity sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA== + dependencies: + is-docker "^3.0.0" + is-interactive@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e" @@ -7231,6 +7305,11 @@ is-stream@^2.0.0: resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== +is-stream@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-3.0.0.tgz#e6bfd7aa6bef69f4f472ce9bb681e3e57b4319ac" + integrity sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA== + is-string@^1.0.5, is-string@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.7.tgz#0dd12bf2006f255bb58f695110eff7491eebc0fd" @@ -8740,6 +8819,13 @@ npm-run-path@^4.0.1: dependencies: path-key "^3.0.0" +npm-run-path@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-5.1.0.tgz#bc62f7f3f6952d9894bd08944ba011a6ee7b7e00" + integrity sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q== + dependencies: + path-key "^4.0.0" + npmlog@^6.0.0, npmlog@^6.0.2: version "6.0.2" resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-6.0.2.tgz#c8166017a42f2dea92d6453168dd865186a70830" @@ -8925,6 +9011,13 @@ onetime@^5.1.0, onetime@^5.1.2: dependencies: mimic-fn "^2.1.0" +onetime@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-6.0.0.tgz#7c24c18ed1fd2e9bca4bd26806a33613c77d34b4" + integrity sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ== + dependencies: + mimic-fn "^4.0.0" + open@^7.4.2: version "7.4.2" resolved "https://registry.yarnpkg.com/open/-/open-7.4.2.tgz#b8147e26dcf3e426316c730089fd71edd29c2321" @@ -8942,6 +9035,16 @@ open@^8.4.0: is-docker "^2.1.1" is-wsl "^2.2.0" +open@^9.1.0: + version "9.1.0" + resolved "https://registry.yarnpkg.com/open/-/open-9.1.0.tgz#684934359c90ad25742f5a26151970ff8c6c80b6" + integrity sha512-OS+QTnw1/4vrf+9hh1jc1jnYjzSG4ttTBB8UxOwAnInG3Uo4ssetzC1ihqaIHjLJnA5GGlRl6QlZXOTQhRBUvg== + dependencies: + default-browser "^4.0.0" + define-lazy-prop "^3.0.0" + is-inside-container "^1.0.0" + is-wsl "^2.2.0" + opn@^5.1.0: version "5.5.0" resolved "https://registry.yarnpkg.com/opn/-/opn-5.5.0.tgz#fc7164fab56d235904c51c3b27da6758ca3b9bfc" @@ -9420,6 +9523,11 @@ path-key@^3.0.0, path-key@^3.1.0: resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== +path-key@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-4.0.0.tgz#295588dc3aee64154f877adb9d780b81c554bf18" + integrity sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ== + path-parse@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" @@ -10632,6 +10740,13 @@ rollup@^2.79.1: optionalDependencies: fsevents "~2.3.2" +run-applescript@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/run-applescript/-/run-applescript-5.0.0.tgz#e11e1c932e055d5c6b40d98374e0268d9b11899c" + integrity sha512-XcT5rBksx1QdIhlFOCtgZkB99ZEouFZ1E2Kc2LHqNW13U3/74YGdkQRmThTwxy4QIyookibDKYZOPqX//6BlAg== + dependencies: + execa "^5.0.0" + run-async@^2.4.0: version "2.4.1" resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" @@ -11403,6 +11518,11 @@ strip-final-newline@^2.0.0: resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== +strip-final-newline@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-3.0.0.tgz#52894c313fbff318835280aed60ff71ebf12b8fd" + integrity sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw== + strip-indent@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-2.0.0.tgz#5ef8db295d01e6ed6cbf7aab96998d7822527b68" @@ -11724,6 +11844,11 @@ tiny-inflate@^1.0.0: resolved "https://registry.yarnpkg.com/tiny-inflate/-/tiny-inflate-1.0.3.tgz#122715494913a1805166aaf7c93467933eea26c4" integrity sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw== +titleize@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/titleize/-/titleize-3.0.0.tgz#71c12eb7fdd2558aa8a44b0be83b8a76694acd53" + integrity sha512-KxVu8EYHDPBdUYdKZdKtU2aj2XfEx9AfjXxE/Aj0vT06w2icA09Vus1rh6eSu1y01akYg6BjIK/hxyLJINoMLQ== + tmp@^0.0.33: version "0.0.33" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" @@ -12135,6 +12260,11 @@ unset-value@^1.0.0: has-value "^0.3.1" isobject "^3.0.0" +untildify@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/untildify/-/untildify-4.0.0.tgz#2bc947b953652487e4600949fb091e3ae8cd919b" + integrity sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw== + upath@^1.1.1: version "1.2.0" resolved "https://registry.yarnpkg.com/upath/-/upath-1.2.0.tgz#8f66dbcd55a883acdae4408af8b035a5044c1894" @@ -12613,6 +12743,11 @@ ws@^6.1.0, ws@^6.1.2: dependencies: async-limiter "~1.0.0" +ws@^8.13.0: + version "8.13.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.13.0.tgz#9a9fb92f93cf41512a0735c8f4dd09b8a1211cd0" + integrity sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA== + xml-name-validator@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a" From 0f7e943c234cc8df6e27c4e4d6d6bd2de6747357 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Thu, 10 Aug 2023 15:06:53 -0700 Subject: [PATCH 061/234] refactor(daemon): Generalize host to party request functions and promote request method to outer scope --- packages/daemon/src/daemon.js | 85 ++++++++++++++++++++++++++--------- 1 file changed, 64 insertions(+), 21 deletions(-) diff --git a/packages/daemon/src/daemon.js b/packages/daemon/src/daemon.js index d5123a1b89..c9d645591c 100644 --- a/packages/daemon/src/daemon.js +++ b/packages/daemon/src/daemon.js @@ -67,8 +67,8 @@ const makeEndoBootstrap = ( // reference", and not for "what is my name for this promise". /** @type {WeakMap} */ const formulaIdentifierForRef = new WeakMap(); - /** @type {WeakMap} */ - const hostRequestFunctions = new WeakMap(); + /** @type {WeakMap} */ + const partyRequestFunctions = new WeakMap(); /** @type {WeakMap>} */ const workerBootstraps = new WeakMap(); @@ -365,8 +365,8 @@ const makeEndoBootstrap = ( await provideValueForFormulaIdentifier(hostFormulaIdentifier) ); - const request = hostRequestFunctions.get(host); - if (request === undefined) { + const hostRequest = partyRequestFunctions.get(host); + if (hostRequest === undefined) { throw new Error( `panic: a host request function must exist for every host`, ); @@ -414,24 +414,67 @@ const makeEndoBootstrap = ( const { list } = guestPetStore; - /** @type {import('@endo/eventual-send').ERef} */ - const guest = Far('EndoGuest', { - request: async (what, responseName) => { - if (responseName === undefined) { - // Behold, recursion: - // eslint-disable-next-line no-use-before-define - return request(what, responseName, guest, guestPetStore); - } - const responseP = responses.get(responseName); - if (responseP !== undefined) { - return responseP; - } + const request = async (what, responseName) => { + if (responseName === undefined) { // Behold, recursion: // eslint-disable-next-line no-use-before-define - const newResponseP = request(what, responseName, guest, guestPetStore); - responses.set(responseName, newResponseP); - return newResponseP; - }, + return hostRequest(what, responseName, guest, guestPetStore); + } + const responseP = responses.get(responseName); + if (responseP !== undefined) { + return responseP; + } + // Behold, recursion: + // eslint-disable-next-line + const newResponseP = hostRequest( + what, + responseName, + // eslint-disable-next-line no-use-before-define + guest, + guestPetStore, + ); + responses.set(responseName, newResponseP); + return newResponseP; + }; + + /** + * @param {Array} strings + * @param {Array} edgeNames + * @param {Array} petNames + */ + const send = async (strings, edgeNames, petNames) => { + petNames.forEach(assertPetName); + edgeNames.forEach(assertPetName); + if (petNames.length !== edgeNames.length) { + throw new Error( + `Message must have one edge name (${q( + edgeNames.length, + )}) for every pet name (${q(petNames.length)})`, + ); + } + if (strings.length < petNames.length) { + throw new Error( + `Message must have one string before every value delivered`, + ); + } + + const receive = partyReceiveFunctions.get(host); + if (receive === undefined) { + throw new Error(`panic: Message not deliverable`); + } + const formulaIdentifiers = petNames.map(petName => { + const formulaIdentifier = guestPetStore.get(petName); + if (formulaIdentifier === undefined) { + throw new Error(`Unknown pet name ${q(petName)}`); + } + return formulaIdentifier; + }); + receive(guestFormulaIdentifier, strings, edgeNames, formulaIdentifiers); + }; + + /** @type {import('@endo/eventual-send').ERef} */ + const guest = Far('EndoGuest', { + request, list, remove, rename, @@ -966,7 +1009,7 @@ const makeEndoBootstrap = ( provideWebPage, }); - hostRequestFunctions.set(host, request); + partyRequestFunctions.set(host, request); return host; }; From d64f47bdf034c8f5438179c617dc6d9d61eceeca Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Thu, 10 Aug 2023 15:07:29 -0700 Subject: [PATCH 062/234] feat(daemon): Add host provideWorker method --- packages/daemon/src/daemon.js | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/packages/daemon/src/daemon.js b/packages/daemon/src/daemon.js index c9d645591c..b198cfaa33 100644 --- a/packages/daemon/src/daemon.js +++ b/packages/daemon/src/daemon.js @@ -699,6 +699,28 @@ const makeEndoBootstrap = ( return provideValueForFormulaIdentifier(formulaIdentifier); }; + /** + * @param {string} workerName + */ + const provideWorker = async workerName => { + if (typeof workerName !== 'string') { + throw new Error('worker name must be string'); + } + let workerFormulaIdentifier = petStore.get(workerName); + if (workerFormulaIdentifier === undefined) { + const workerId512 = await powers.randomHex512(); + workerFormulaIdentifier = `worker-id512:${workerId512}`; + await petStore.write(workerName, workerFormulaIdentifier); + } else if (!workerFormulaIdentifier.startsWith('worker-id512:')) { + throw new Error(`Not a worker ${q(workerName)}`); + } + return /** @type {Promise} */ ( + // Behold, recursion: + // eslint-disable-next-line no-use-before-define + provideValueForFormulaIdentifier(workerFormulaIdentifier) + ); + }; + const lookup = async presence => { const formulaIdentifier = formulaIdentifierForRef.get(await presence); if (formulaIdentifier === undefined) { @@ -1003,6 +1025,7 @@ const makeEndoBootstrap = ( provideGuest, provideHost, makeWorker, + provideWorker, evaluate, importUnsafeAndEndow, importBundleAndEndow, From 388d23a6d5987e65ad611688538b0bc37a5355ef Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Thu, 10 Aug 2023 15:08:28 -0700 Subject: [PATCH 063/234] feat(daemon): Send message from guest to host, Host adopt and dismiss commands --- packages/daemon/src/daemon.js | 147 +++++++++++++++++++++++++----- packages/daemon/src/types.d.ts | 19 +++- packages/daemon/test/test-endo.js | 29 ++++++ 3 files changed, 169 insertions(+), 26 deletions(-) diff --git a/packages/daemon/src/daemon.js b/packages/daemon/src/daemon.js index b198cfaa33..8ab867468b 100644 --- a/packages/daemon/src/daemon.js +++ b/packages/daemon/src/daemon.js @@ -69,6 +69,8 @@ const makeEndoBootstrap = ( const formulaIdentifierForRef = new WeakMap(); /** @type {WeakMap} */ const partyRequestFunctions = new WeakMap(); + /** @type {WeakMap} */ + const partyReceiveFunctions = new WeakMap(); /** @type {WeakMap>} */ const workerBootstraps = new WeakMap(); @@ -442,7 +444,7 @@ const makeEndoBootstrap = ( * @param {Array} edgeNames * @param {Array} petNames */ - const send = async (strings, edgeNames, petNames) => { + const receive = async (strings, edgeNames, petNames) => { petNames.forEach(assertPetName); edgeNames.forEach(assertPetName); if (petNames.length !== edgeNames.length) { @@ -458,8 +460,8 @@ const makeEndoBootstrap = ( ); } - const receive = partyReceiveFunctions.get(host); - if (receive === undefined) { + const partyReceive = partyReceiveFunctions.get(host); + if (partyReceive === undefined) { throw new Error(`panic: Message not deliverable`); } const formulaIdentifiers = petNames.map(petName => { @@ -469,12 +471,18 @@ const makeEndoBootstrap = ( } return formulaIdentifier; }); - receive(guestFormulaIdentifier, strings, edgeNames, formulaIdentifiers); + partyReceive( + guestFormulaIdentifier, + strings, + edgeNames, + formulaIdentifiers, + ); }; /** @type {import('@endo/eventual-send').ERef} */ const guest = Far('EndoGuest', { request, + receive, list, remove, rename, @@ -499,20 +507,22 @@ const makeEndoBootstrap = ( ); /** @type {Map} */ - const requests = new Map(); + const messages = new Map(); /** @type {WeakMap void>} */ const resolvers = new WeakMap(); + /** @type {WeakMap void>} */ + const dismissers = new WeakMap(); /** @type {import('./types.js').Topic} */ - const requestsTopic = makeChangeTopic(); - let nextRequestNumber = 0; + const messagesTopic = makeChangeTopic(); + let nextMessageNumber = 0; - const listMessages = async () => harden(Array.from(requests.values())); + const listMessages = async () => harden(Array.from(messages.values())); const followMessages = async () => makeIteratorRef( (async function* currentAndSubsequentMessages() { - const subsequentRequests = requestsTopic.subscribe(); - yield* requests.values(); + const subsequentRequests = messagesTopic.subscribe(); + yield* messages.values(); yield* subsequentRequests; })(), ); @@ -524,10 +534,10 @@ const makeEndoBootstrap = ( const requestFormulaIdentifier = async (what, whom) => { /** @type {import('@endo/promise-kit/src/types.js').PromiseKit} */ const { promise, resolve } = makePromiseKit(); - const requestNumber = nextRequestNumber; - nextRequestNumber += 1; + const messageNumber = nextMessageNumber; + nextMessageNumber += 1; const settle = () => { - requests.delete(requestNumber); + messages.delete(messageNumber); }; const settled = promise.then( () => { @@ -554,16 +564,16 @@ const makeEndoBootstrap = ( const req = harden({ type: /** @type {'request'} */ ('request'), - number: requestNumber, + number: messageNumber, who, what, when: new Date().toISOString(), settled, }); - requests.set(requestNumber, req); + messages.set(messageNumber, req); resolvers.set(req, resolve); - requestsTopic.publisher.next(req); + messagesTopic.publisher.next(req); return promise; }; @@ -592,18 +602,18 @@ const makeEndoBootstrap = ( return provideValueForFormulaIdentifier(formulaIdentifier); }; - const resolve = async (requestNumber, resolutionName) => { + const resolve = async (messageNumber, resolutionName) => { assertPetName(resolutionName); if ( - typeof requestNumber !== 'number' || - requestNumber >= Number.MAX_SAFE_INTEGER + typeof messageNumber !== 'number' || + messageNumber >= Number.MAX_SAFE_INTEGER ) { - throw new Error(`Invalid request number ${q(requestNumber)}`); + throw new Error(`Invalid request number ${q(messageNumber)}`); } - const req = requests.get(requestNumber); + const req = messages.get(messageNumber); const resolveRequest = resolvers.get(req); if (resolveRequest === undefined) { - throw new Error(`No pending request for number ${requestNumber}`); + throw new Error(`No pending request for number ${messageNumber}`); } const formulaIdentifier = petStore.get(resolutionName); if (formulaIdentifier === undefined) { @@ -614,13 +624,97 @@ const makeEndoBootstrap = ( resolveRequest(formulaIdentifier); }; + /** + * @param {string} senderFormulaIdentifier + * @param {Array} strings + * @param {Array} edgeNames + * @param {Array} formulaIdentifiers + */ + const receive = ( + senderFormulaIdentifier, + strings, + edgeNames, + formulaIdentifiers, + ) => { + /** @type {import('@endo/promise-kit/src/types.js').PromiseKit} */ + const dismissal = makePromiseKit(); + const messageNumber = nextMessageNumber; + nextMessageNumber += 1; + + const message = harden({ + type: /** @type {const} */ ('package'), + number: messageNumber, + strings, + names: edgeNames, + formulas: formulaIdentifiers, // TODO should not be visible to recipient + who: senderFormulaIdentifier, + when: new Date().toISOString(), + dismissed: dismissal.promise, + }); + + messages.set(messageNumber, message); + dismissers.set(message, () => { + messages.delete(messageNumber); + dismissal.resolve(); + }); + messagesTopic.publisher.next(message); + }; + + const dismiss = async messageNumber => { + if ( + typeof messageNumber !== 'number' || + messageNumber >= Number.MAX_SAFE_INTEGER + ) { + throw new Error(`Invalid request number ${q(messageNumber)}`); + } + const message = messages.get(messageNumber); + const dismissMessage = dismissers.get(message); + if (dismissMessage === undefined) { + throw new Error(`No dismissable message for number ${messageNumber}`); + } + dismissMessage(); + }; + + const adopt = async (messageNumber, edgeName, petName) => { + assertPetName(edgeName); + assertPetName(petName); + if ( + typeof messageNumber !== 'number' || + messageNumber >= Number.MAX_SAFE_INTEGER + ) { + throw new Error(`Invalid message number ${q(messageNumber)}`); + } + const message = messages.get(messageNumber); + if (message === undefined) { + throw new Error(`No such message with number ${q(messageNumber)}`); + } + if (message.type !== 'package') { + throw new Error(`Message must be a package ${q(messageNumber)}`); + } + const index = message.names.lastIndexOf(edgeName); + if (index === -1) { + throw new Error( + `No reference named ${q(edgeName)} in message ${q(messageNumber)}`, + ); + } + const formulaIdentifier = message.formulas[index]; + if (formulaIdentifier === undefined) { + throw new Error( + `panic: message must contain a formula for every name, including the name ${q( + edgeName, + )} at ${q(index)}`, + ); + } + await petStore.write(petName, formulaIdentifier); + }; + // TODO test reject /** - * @param {number} requestNumber + * @param {number} messageNumber * @param {string} [message] */ - const reject = async (requestNumber, message = 'Declined') => { - const req = requests.get(requestNumber); + const reject = async (messageNumber, message = 'Declined') => { + const req = messages.get(messageNumber); if (req !== undefined) { const resolveRequest = resolvers.get(req); if (resolveRequest === undefined) { @@ -1017,6 +1111,8 @@ const makeEndoBootstrap = ( provide, resolve, reject, + adopt, + dismiss, lookup, list, remove, @@ -1032,6 +1128,7 @@ const makeEndoBootstrap = ( provideWebPage, }); + partyReceiveFunctions.set(host, receive); partyRequestFunctions.set(host, request); return host; diff --git a/packages/daemon/src/types.d.ts b/packages/daemon/src/types.d.ts index 1a8469ef03..a5d362457a 100644 --- a/packages/daemon/src/types.d.ts +++ b/packages/daemon/src/types.d.ts @@ -154,7 +154,17 @@ export type Request = { settled: Promise<'fulfilled' | 'rejected'>; }; -export type Message = Label & Request; +export type Package = { + type: 'package'; + strings: Array; // text that appears before, between, and after named formulas. + names: Array; // edge names + formulas: Array; // formula identifiers + dismissed: Promise; +}; + +export type Payload = Request | Package; + +export type Message = Label & Payload; export interface Topic< TRead, @@ -182,6 +192,13 @@ export type RequestFn = ( guestPetStore: PetStore, ) => Promise; +export type ReceiveFn = ( + senderFormulaIdentifier: string, + strings: Array, + edgeNames: Array, + formulaIdentifiers: Array, +) => void; + export interface EndoReadable { sha512(): string; stream(): ERef>; diff --git a/packages/daemon/test/test-endo.js b/packages/daemon/test/test-endo.js index 91bbb02307..5fdb7afb93 100644 --- a/packages/daemon/test/test-endo.js +++ b/packages/daemon/test/test-endo.js @@ -379,3 +379,32 @@ test('persist unsafe services and their requests', async t => { t.is(number, 42); } }); + +test('guest facet receives a message for host', async t => { + const { promise: cancelled } = makePromiseKit(); + const locator = makeLocator('tmp', 'guest-sends-host'); + + await start(locator); + + const { getBootstrap } = await makeEndoClient( + 'client', + locator.sockPath, + cancelled, + ); + const bootstrap = getBootstrap(); + const host = E(bootstrap).host(); + const guest = E(host).provideGuest('guest'); + await E(host).provideWorker('worker'); + await E(host).evaluate('worker', '10', [], [], 'ten1'); + const iteratorRef = E(host).followMessages(); + E.sendOnly(guest).request('a number', 'number'); + const { value: message0 } = await E(iteratorRef).next(); + t.is(message0.number, 0); + await E(host).resolve(message0.number, 'ten1'); + await E(guest).receive('Hello, World!', ['gift'], ['number']); + const { value: message1 } = await E(iteratorRef).next(); + t.is(message1.number, 1); + await E(host).adopt(message1.number, 'gift', 'ten2'); + const ten = await E(host).provide('ten2'); + t.is(ten, 10); +}); From 043d1b804b6de81e601b96404b8380c2ff3f52d3 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Thu, 10 Aug 2023 16:54:18 -0700 Subject: [PATCH 064/234] refactor(cli): Simplify run commandlet signature --- packages/cli/src/client.js | 17 ++++++++++++++ packages/cli/src/endo.js | 48 +++++++++++++++----------------------- packages/cli/src/run.js | 24 ++++++++++--------- 3 files changed, 49 insertions(+), 40 deletions(-) create mode 100644 packages/cli/src/client.js diff --git a/packages/cli/src/client.js b/packages/cli/src/client.js new file mode 100644 index 0000000000..355f414b75 --- /dev/null +++ b/packages/cli/src/client.js @@ -0,0 +1,17 @@ +import { start, makeEndoClient } from '@endo/daemon'; + +export const provideEndoClient = async (...args) => { + try { + // It is okay to fail to connect because the daemon is not running. + return await makeEndoClient(...args); + } catch { + console.error('Starting Endo daemon...'); + // It is also okay to fail the race to start. + await start().catch(() => {}); + // But not okay to fail to connect after starting. + // We are not going to contemplate reliably in the face of a worker getting + // stopped the moment after it was started. + // That is a bridge too far. + return makeEndoClient(...args); + } +}; diff --git a/packages/cli/src/endo.js b/packages/cli/src/endo.js index 961a7606fc..846d970a89 100644 --- a/packages/cli/src/endo.js +++ b/packages/cli/src/endo.js @@ -48,6 +48,8 @@ import { import { makeNodeReader } from '@endo/stream-node'; import { E } from '@endo/far'; +import { provideEndoClient } from './client.js'; + const readPowers = makeReadPowers({ fs, url, crypto }); const writePowers = makeWritePowers({ fs, url }); const { write } = writePowers; @@ -101,22 +103,6 @@ const randomHex16 = () => }), ); -const provideEndoClient = async (...args) => { - try { - // It is okay to fail to connect because the daemon is not running. - return await makeEndoClient(...args); - } catch { - console.error('Starting Endo daemon...'); - // It is also okay to fail the race to start. - await start().catch(() => {}); - // But not okay to fail to connect after starting. - // We are not going to contemplate reliably in the face of a worker getting - // stopped the moment after it was started. - // That is a bridge too far. - return makeEndoClient(...args); - } -}; - export const main = async rawArgs => { const { promise: cancelled, reject: cancel } = makePromiseKit(); cancelled.catch(() => {}); @@ -1129,20 +1115,24 @@ export const main = async rawArgs => { 'Endowment to give the worklet (a name, NONE, HOST, or ENDO)', ) .action(async (filePath, args, cmd) => { + const { + as: partyNames, + bundle: bundleName, + UNSAFE: importPath, + powers: powersName = 'NONE', + } = cmd.opts(); const { run } = await import('./run.js'); - return run( - { - provideEndoClient, - cancel, - cancelled, - sockPath, - }, - { - file: filePath, - args, - ...cmd.opts(), - }, - ); + return run({ + cancel, + cancelled, + sockPath, + filePath, + args, + bundleName, + importPath, + powersName, + partyNames, + }); }); // Throw an error instead of exiting directly. diff --git a/packages/cli/src/run.js b/packages/cli/src/run.js index b2a49059bb..1c33ab6c0e 100644 --- a/packages/cli/src/run.js +++ b/packages/cli/src/run.js @@ -3,6 +3,8 @@ import { E, Far } from '@endo/far'; import bundleSource from '@endo/bundle-source'; import url from 'url'; +import { provideEndoClient } from './client.js'; + const endowments = harden({ assert, E, @@ -13,17 +15,17 @@ const endowments = harden({ console, }); -export const run = async ( - { provideEndoClient, cancel, cancelled, sockPath }, - { - as: partyNames, - file: filePath, - bundle: bundleName, - UNSAFE: importPath, - powers: powersName = 'NONE', - args, - }, -) => { +export const run = async ({ + cancel, + cancelled, + sockPath, + filePath, + args, + bundleName, + importPath, + powersName, + partyNames, +}) => { if ( filePath === undefined && importPath === undefined && From 69e1594450d93fa126b8865f058a3eeb6d2e3152 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Thu, 10 Aug 2023 16:55:14 -0700 Subject: [PATCH 065/234] refactor(cli): Move random hex into library --- packages/cli/src/endo.js | 12 +----------- packages/cli/src/random.js | 12 ++++++++++++ 2 files changed, 13 insertions(+), 11 deletions(-) create mode 100644 packages/cli/src/random.js diff --git a/packages/cli/src/endo.js b/packages/cli/src/endo.js index 846d970a89..65f984745b 100644 --- a/packages/cli/src/endo.js +++ b/packages/cli/src/endo.js @@ -49,6 +49,7 @@ import { makeNodeReader } from '@endo/stream-node'; import { E } from '@endo/far'; import { provideEndoClient } from './client.js'; +import { randomHex16 } from './random.js'; const readPowers = makeReadPowers({ fs, url, crypto }); const writePowers = makeWritePowers({ fs, url }); @@ -92,17 +93,6 @@ const delay = async (ms, cancelled) => { }); }; -const randomHex16 = () => - new Promise((resolve, reject) => - crypto.randomBytes(16, (err, bytes) => { - if (err) { - reject(err); - } else { - resolve(bytes.toString('hex')); - } - }), - ); - export const main = async rawArgs => { const { promise: cancelled, reject: cancel } = makePromiseKit(); cancelled.catch(() => {}); diff --git a/packages/cli/src/random.js b/packages/cli/src/random.js new file mode 100644 index 0000000000..5861003da2 --- /dev/null +++ b/packages/cli/src/random.js @@ -0,0 +1,12 @@ +import crypto from 'crypto'; + +export const randomHex16 = () => + new Promise((resolve, reject) => + crypto.randomBytes(16, (err, bytes) => { + if (err) { + reject(err); + } else { + resolve(bytes.toString('hex')); + } + }), + ); From 18133087d51ab18b577b84f9ff74813deccf8998 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Thu, 10 Aug 2023 16:55:38 -0700 Subject: [PATCH 066/234] refactor(cli): Move open into command library --- packages/cli/src/endo.js | 76 ++++++++-------------------------------- packages/cli/src/open.js | 76 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 91 insertions(+), 61 deletions(-) create mode 100644 packages/cli/src/open.js diff --git a/packages/cli/src/endo.js b/packages/cli/src/endo.js index 65f984745b..247d4d45a4 100644 --- a/packages/cli/src/endo.js +++ b/packages/cli/src/endo.js @@ -12,7 +12,6 @@ import url from 'url'; import crypto from 'crypto'; import { spawn } from 'child_process'; import os from 'os'; -import open from 'open'; import { Command } from 'commander'; import { makePromiseKit } from '@endo/promise-kit'; @@ -1024,67 +1023,22 @@ export const main = async rawArgs => { 'Endowment to give the weblet (a name, NONE, HOST, or ENDO)', ) .action(async (webPageName, programPath, cmd) => { - const { as: partyNames, powers: powersName = 'NONE' } = cmd.opts(); - let { bundle: bundleName } = cmd.opts(); - - /** @type {import('@endo/eventual-send').ERef> | undefined} */ - let bundleReaderRef; - /** @type {string | undefined} */ - let temporaryBundleName; - if (programPath !== undefined) { - if (bundleName === undefined) { - // TODO alternately, make a temporary session-scoped GC pet store - // overshadowing the permanent one, which gets implicitly dropped - // when this CLI CapTP session ends. - temporaryBundleName = `tmp-bundle-${await randomHex16()}`; - bundleName = temporaryBundleName; - } - const bundle = await bundleSource(programPath); - console.log(bundle.endoZipBase64Sha512); - const bundleText = JSON.stringify(bundle); - const bundleBytes = textEncoder.encode(bundleText); - bundleReaderRef = makeReaderRef([bundleBytes]); - } - - const { getBootstrap } = await provideEndoClient( - 'cli', - sockPath, + const { + bundle: bundleName, + powers: powersName = 'NONE', + as: partyNames, + } = cmd.opts(); + const { open } = await import('./open.js'); + return open({ + cancel, cancelled, - ); - - try { - const bootstrap = getBootstrap(); - let party = E(bootstrap).host(); - for (const partyName of partyNames) { - party = E(party).provide(partyName); - } - - // Prepare a bundle, with the given name. - if (bundleReaderRef !== undefined) { - await E(party).store(bundleReaderRef, bundleName); - } - - /** @type {string | undefined} */ - let webPageUrl; - if (bundleName !== undefined) { - ({ url: webPageUrl } = await E(party).provideWebPage( - webPageName, - bundleName, - powersName, - )); - } else { - ({ url: webPageUrl } = await E(party).provide(webPageName)); - } - console.log(webPageUrl); - open(webPageUrl); - - if (temporaryBundleName) { - await E(party).remove(temporaryBundleName); - } - } catch (error) { - console.error(error); - cancel(error); - } + sockPath, + webPageName, + programPath, + bundleName, + powersName, + partyNames, + }); }); program diff --git a/packages/cli/src/open.js b/packages/cli/src/open.js new file mode 100644 index 0000000000..b581c91edd --- /dev/null +++ b/packages/cli/src/open.js @@ -0,0 +1,76 @@ +/* global process */ + +import openWebPage from 'open'; +import { E } from '@endo/far'; +import { makeReaderRef } from '@endo/daemon'; +import bundleSource from '@endo/bundle-source'; + +import { provideEndoClient } from './client.js'; +import { randomHex16 } from './random.js'; + +const textEncoder = new TextEncoder(); + +export const open = async ({ + cancel, + cancelled, + sockPath, + bundleName, + partyNames, + powersName, + webPageName, + programPath, +}) => { + /** @type {import('@endo/eventual-send').ERef> | undefined} */ + let bundleReaderRef; + /** @type {string | undefined} */ + let temporaryBundleName; + if (programPath !== undefined) { + if (bundleName === undefined) { + // TODO alternately, make a temporary session-scoped GC pet store + // overshadowing the permanent one, which gets implicitly dropped + // when this CLI CapTP session ends. + temporaryBundleName = `tmp-bundle-${await randomHex16()}`; + bundleName = temporaryBundleName; + } + const bundle = await bundleSource(programPath); + const bundleText = JSON.stringify(bundle); + const bundleBytes = textEncoder.encode(bundleText); + bundleReaderRef = makeReaderRef([bundleBytes]); + } + + const { getBootstrap } = await provideEndoClient('cli', sockPath, cancelled); + + try { + const bootstrap = getBootstrap(); + let party = E(bootstrap).host(); + for (const partyName of partyNames) { + party = E(party).provide(partyName); + } + + // Prepare a bundle, with the given name. + if (bundleReaderRef !== undefined) { + await E(party).store(bundleReaderRef, bundleName); + } + + /** @type {string | undefined} */ + let webPageUrl; + if (bundleName !== undefined) { + ({ url: webPageUrl } = await E(party).provideWebPage( + webPageName, + bundleName, + powersName, + )); + } else { + ({ url: webPageUrl } = await E(party).provide(webPageName)); + } + process.stdout.write(`${webPageUrl}\n`); + openWebPage(webPageUrl); + + if (temporaryBundleName) { + await E(party).remove(temporaryBundleName); + } + } catch (error) { + console.error(error); + cancel(error); + } +}; From 75b8682d56644e3584a913a84f86ea479882288b Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Thu, 10 Aug 2023 17:02:36 -0700 Subject: [PATCH 067/234] refactor(cli): Move make into command library --- packages/cli/src/endo.js | 91 ++++++------------------------------- packages/cli/src/make.js | 98 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 111 insertions(+), 78 deletions(-) create mode 100644 packages/cli/src/make.js diff --git a/packages/cli/src/endo.js b/packages/cli/src/endo.js index 247d4d45a4..e87e88b925 100644 --- a/packages/cli/src/endo.js +++ b/packages/cli/src/endo.js @@ -48,7 +48,6 @@ import { makeNodeReader } from '@endo/stream-node'; import { E } from '@endo/far'; import { provideEndoClient } from './client.js'; -import { randomHex16 } from './random.js'; const readPowers = makeReadPowers({ fs, url, crypto }); const writePowers = makeWritePowers({ fs, url }); @@ -924,88 +923,24 @@ export const main = async rawArgs => { const { UNSAFE: importPath, name: resultName, + bundle: bundleName, worker: workerName = 'NEW', as: partyNames, powers: powersName = 'NONE', } = cmd.opts(); - let { bundle: bundleName } = cmd.opts(); - - if (filePath !== undefined && importPath !== undefined) { - console.error('Specify only one of [file] or --UNSAFE '); - process.exitCode = 1; - return; - } - if ( - filePath === undefined && - importPath === undefined && - bundleName === undefined - ) { - console.error( - 'Specify at least one of [file], --bundle , or --UNSAFE ', - ); - process.exitCode = 1; - return; - } - - /** @type {import('@endo/eventual-send').ERef> | undefined} */ - let bundleReaderRef; - /** @type {string | undefined} */ - let temporaryBundleName; - if (filePath !== undefined) { - if (bundleName === undefined) { - // TODO alternately, make a temporary session-scoped GC pet store - // overshadowing the permanent one, which gets implicitly dropped - // when this CLI CapTP session ends. - temporaryBundleName = `tmp-bundle-${await randomHex16()}`; - bundleName = temporaryBundleName; - } - const bundle = await bundleSource(filePath); - const bundleText = JSON.stringify(bundle); - const bundleBytes = textEncoder.encode(bundleText); - bundleReaderRef = makeReaderRef([bundleBytes]); - } - - const { getBootstrap } = await provideEndoClient( - 'cli', - sockPath, + const { makeCommand } = await import('./make.js'); + return makeCommand({ + cancel, cancelled, - ); - try { - const bootstrap = getBootstrap(); - let party = E(bootstrap).host(); - for (const partyName of partyNames) { - party = E(party).provide(partyName); - } - - // Prepare a bundle, with the given name. - if (bundleReaderRef !== undefined) { - await E(party).store(bundleReaderRef, bundleName); - } - - const resultP = - importPath !== undefined - ? E(party).importUnsafeAndEndow( - workerName, - path.resolve(importPath), - powersName, - resultName, - ) - : E(party).importBundleAndEndow( - workerName, - bundleName, - powersName, - resultName, - ); - const result = await resultP; - console.log(result); - - if (temporaryBundleName) { - await E(party).remove(temporaryBundleName); - } - } catch (error) { - console.error(error); - cancel(error); - } + sockPath, + filePath, + importPath, + resultName, + bundleName, + workerName, + partyNames, + powersName, + }); }); program diff --git a/packages/cli/src/make.js b/packages/cli/src/make.js new file mode 100644 index 0000000000..fb86174d0e --- /dev/null +++ b/packages/cli/src/make.js @@ -0,0 +1,98 @@ +/* global process */ + +import path from 'path'; + +import bundleSource from '@endo/bundle-source'; +import { makeReaderRef } from '@endo/daemon'; +import { E } from '@endo/far'; + +import { provideEndoClient } from './client.js'; +import { randomHex16 } from './random.js'; + +const textEncoder = new TextEncoder(); + +export const makeCommand = async ({ + cancel, + cancelled, + sockPath, + filePath, + importPath, + resultName, + bundleName, + workerName, + partyNames, + powersName, +}) => { + if (filePath !== undefined && importPath !== undefined) { + console.error('Specify only one of [file] or --UNSAFE '); + process.exitCode = 1; + return; + } + if ( + filePath === undefined && + importPath === undefined && + bundleName === undefined + ) { + console.error( + 'Specify at least one of [file], --bundle , or --UNSAFE ', + ); + process.exitCode = 1; + return; + } + + /** @type {import('@endo/eventual-send').ERef> | undefined} */ + let bundleReaderRef; + /** @type {string | undefined} */ + let temporaryBundleName; + if (filePath !== undefined) { + if (bundleName === undefined) { + // TODO alternately, make a temporary session-scoped GC pet store + // overshadowing the permanent one, which gets implicitly dropped + // when this CLI CapTP session ends. + temporaryBundleName = `tmp-bundle-${await randomHex16()}`; + bundleName = temporaryBundleName; + } + const bundle = await bundleSource(filePath); + const bundleText = JSON.stringify(bundle); + const bundleBytes = textEncoder.encode(bundleText); + bundleReaderRef = makeReaderRef([bundleBytes]); + } + + const { getBootstrap } = await provideEndoClient('cli', sockPath, cancelled); + try { + const bootstrap = getBootstrap(); + let party = E(bootstrap).host(); + for (const partyName of partyNames) { + party = E(party).provide(partyName); + } + + // Prepare a bundle, with the given name. + if (bundleReaderRef !== undefined) { + await E(party).store(bundleReaderRef, bundleName); + } + + const resultP = + importPath !== undefined + ? E(party).importUnsafeAndEndow( + workerName, + path.resolve(importPath), + powersName, + resultName, + ) + : E(party).importBundleAndEndow( + workerName, + bundleName, + powersName, + resultName, + ); + const result = await resultP; + console.log(result); + + if (temporaryBundleName) { + await E(party).remove(temporaryBundleName); + } + } catch (error) { + console.error(error); + cancel(error); + } +}; From c014076e168310a108aeb9bb44a7eb79fbc1de6c Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Thu, 10 Aug 2023 17:08:23 -0700 Subject: [PATCH 068/234] refactor(cli): Move bundle into command library --- packages/cli/src/bundle.js | 36 ++++++++++++++++++++++++++++++++++++ packages/cli/src/endo.js | 31 ++++++++----------------------- 2 files changed, 44 insertions(+), 23 deletions(-) create mode 100644 packages/cli/src/bundle.js diff --git a/packages/cli/src/bundle.js b/packages/cli/src/bundle.js new file mode 100644 index 0000000000..981cb35687 --- /dev/null +++ b/packages/cli/src/bundle.js @@ -0,0 +1,36 @@ +/* global process */ + +import { E } from '@endo/far'; +import bundleSource from '@endo/bundle-source'; +import { makeReaderRef } from '@endo/daemon'; + +import { provideEndoClient } from './client.js'; + +const textEncoder = new TextEncoder(); + +export const bundleCommand = async ({ + cancel, + cancelled, + sockPath, + applicationPath, + bundleName, + partyNames, +}) => { + const bundle = await bundleSource(applicationPath); + process.stdout.write(`${bundle.endoZipBase64Sha512}\n`); + const bundleText = JSON.stringify(bundle); + const bundleBytes = textEncoder.encode(bundleText); + const readerRef = makeReaderRef([bundleBytes]); + const { getBootstrap } = await provideEndoClient('cli', sockPath, cancelled); + try { + const bootstrap = getBootstrap(); + let party = E(bootstrap).host(); + for (const partyName of partyNames) { + party = E(party).provide(partyName); + } + await E(party).store(readerRef, bundleName); + } catch (error) { + console.error(error); + cancel(error); + } +}; diff --git a/packages/cli/src/endo.js b/packages/cli/src/endo.js index e87e88b925..c9e29712b7 100644 --- a/packages/cli/src/endo.js +++ b/packages/cli/src/endo.js @@ -39,7 +39,6 @@ import { makeArchive, writeArchive, } from '@endo/compartment-mapper'; -import bundleSource from '@endo/bundle-source'; import { makeReadPowers, makeWritePowers, @@ -59,8 +58,6 @@ const packageDescriptorPath = url.fileURLToPath( new URL('../package.json', import.meta.url), ); -const textEncoder = new TextEncoder(); - const { username, homedir } = os.userInfo(); const temp = os.tmpdir(); const info = { @@ -341,27 +338,15 @@ export const main = async rawArgs => { ) .action(async (applicationPath, cmd) => { const { name: bundleName, as: partyNames } = cmd.opts(); - const bundle = await bundleSource(applicationPath); - console.log(bundle.endoZipBase64Sha512); - const bundleText = JSON.stringify(bundle); - const bundleBytes = textEncoder.encode(bundleText); - const readerRef = makeReaderRef([bundleBytes]); - const { getBootstrap } = await provideEndoClient( - 'cli', - sockPath, + const { bundleCommand } = await import('./bundle.js'); + return bundleCommand({ + cancel, cancelled, - ); - try { - const bootstrap = getBootstrap(); - let party = E(bootstrap).host(); - for (const partyName of partyNames) { - party = E(party).provide(partyName); - } - await E(party).store(readerRef, bundleName); - } catch (error) { - console.error(error); - cancel(error); - } + sockPath, + applicationPath, + bundleName, + partyNames, + }); }); program From 7ea4074fa9911929ed0e4fa2de17b927fe1822e6 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Thu, 10 Aug 2023 17:13:55 -0700 Subject: [PATCH 069/234] chore(cli)!: Remove hash command (redundant with bundle) --- packages/cli/src/endo.js | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/packages/cli/src/endo.js b/packages/cli/src/endo.js index c9e29712b7..eabb04ac53 100644 --- a/packages/cli/src/endo.js +++ b/packages/cli/src/endo.js @@ -252,17 +252,6 @@ export const main = async rawArgs => { process.stdout.write(compartmentMapBytes); }); - program - .command('hash ') - .description( - 'prints the SHA-512 of the compartment-map.json for the path to an entry module path', - ) - .action(async (_cmd, [applicationPath]) => { - const applicationLocation = url.pathToFileURL(applicationPath); - const sha512 = await hashLocation(readPowers, applicationLocation); - process.stdout.write(`${sha512}\n`); - }); - program .command('hash-archive ') .description( From b24132a48513599e4818977117852af3515f4765 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Thu, 10 Aug 2023 17:18:19 -0700 Subject: [PATCH 070/234] chore(cli)!: Remove archive commands in favor of bundles --- packages/cli/src/endo.js | 91 ---------------------------------------- 1 file changed, 91 deletions(-) diff --git a/packages/cli/src/endo.js b/packages/cli/src/endo.js index eabb04ac53..56120d8c4f 100644 --- a/packages/cli/src/endo.js +++ b/packages/cli/src/endo.js @@ -9,7 +9,6 @@ import '@endo/lockdown/commit.js'; import fs from 'fs'; import path from 'path'; import url from 'url'; -import crypto from 'crypto'; import { spawn } from 'child_process'; import os from 'os'; @@ -32,26 +31,11 @@ import { whereEndoSock, whereEndoCache, } from '@endo/where'; -import { - mapLocation, - hashLocation, - loadArchive, - makeArchive, - writeArchive, -} from '@endo/compartment-mapper'; -import { - makeReadPowers, - makeWritePowers, -} from '@endo/compartment-mapper/node-powers.js'; import { makeNodeReader } from '@endo/stream-node'; import { E } from '@endo/far'; import { provideEndoClient } from './client.js'; -const readPowers = makeReadPowers({ fs, url, crypto }); -const writePowers = makeWritePowers({ fs, url }); -const { write } = writePowers; - const collect = (value, values) => values.concat([value]); const packageDescriptorPath = url.fileURLToPath( @@ -238,81 +222,6 @@ export const main = async rawArgs => { process.stderr.write('ok\n'); }); - program - .command('map ') - .description( - 'prints a compartment-map.json for the path to an entry module path', - ) - .action(async (_cmd, [applicationPath]) => { - const applicationLocation = url.pathToFileURL(applicationPath); - const compartmentMapBytes = await mapLocation( - readPowers, - applicationLocation, - ); - process.stdout.write(compartmentMapBytes); - }); - - program - .command('hash-archive ') - .description( - 'prints the SHA-512 of the compartment-map.json of an archive generated from the entry module path', - ) - .action(async (_cmd, [archivePath]) => { - const archiveLocation = url.pathToFileURL(archivePath); - const { sha512 } = await loadArchive(readPowers, archiveLocation); - process.stdout.write(`${sha512}\n`); - }); - - program - .command('archive ') - .option( - '-a,--as ', - 'Pose as named party (as named by current party)', - collect, - [], - ) - .option('-n,--name ', 'Store the archive into Endo') - .option('-f,--file ', 'Store the archive into a file') - .description('captures an archive from an entry module path') - .action(async (applicationPath, cmd) => { - const { - name: archiveName, - file: archivePath, - as: partyNames, - } = cmd.opts(); - const applicationLocation = url.pathToFileURL(applicationPath); - if (archiveName !== undefined) { - const archiveBytes = await makeArchive(readPowers, applicationLocation); - const readerRef = makeReaderRef([archiveBytes]); - const { getBootstrap } = await provideEndoClient( - 'cli', - sockPath, - cancelled, - ); - try { - const bootstrap = getBootstrap(); - let party = E(bootstrap).host(); - for (const partyName of partyNames) { - party = E(party).provide(partyName); - } - await E(party).store(readerRef, archiveName); - } catch (error) { - console.error(error); - cancel(error); - } - } else if (archivePath !== undefined) { - const archiveLocation = url.pathToFileURL(archivePath); - await writeArchive( - write, - readPowers, - archiveLocation, - applicationLocation, - ); - } else { - throw new TypeError('Archive command requires either a name or a path'); - } - }); - program .command('bundle ') .option( From 02f9663bf659399799cea5fed3c8b40be15ade52 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Mon, 14 Aug 2023 15:05:00 -0700 Subject: [PATCH 071/234] refactor(cli): Move log into command library --- packages/cli/src/endo.js | 63 ++++++-------------------------- packages/cli/src/log.js | 77 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 87 insertions(+), 53 deletions(-) create mode 100644 packages/cli/src/log.js diff --git a/packages/cli/src/endo.js b/packages/cli/src/endo.js index 56120d8c4f..a75e0816a6 100644 --- a/packages/cli/src/endo.js +++ b/packages/cli/src/endo.js @@ -1,4 +1,4 @@ -/* global process, setTimeout, clearTimeout */ +/* global process */ /* eslint-disable no-await-in-loop */ // Establish a perimeter: @@ -9,7 +9,6 @@ import '@endo/lockdown/commit.js'; import fs from 'fs'; import path from 'path'; import url from 'url'; -import { spawn } from 'child_process'; import os from 'os'; import { Command } from 'commander'; @@ -60,18 +59,6 @@ const sockPath = whereEndoSock(process.platform, process.env, info); const cachePath = whereEndoCache(process.platform, process.env, info); const logPath = path.join(statePath, 'endo.log'); -const delay = async (ms, cancelled) => { - // Do not attempt to set up a timer if already cancelled. - await Promise.race([cancelled, undefined]); - return new Promise((resolve, reject) => { - const handle = setTimeout(resolve, ms); - cancelled.catch(error => { - reject(error); - clearTimeout(handle); - }); - }); -}; - export const main = async rawArgs => { const { promise: cancelled, reject: cancel } = makePromiseKit(); cancelled.catch(() => {}); @@ -167,45 +154,15 @@ export const main = async rawArgs => { .option('-p,--ping ', 'milliseconds between daemon reset checks') .description('writes out the daemon log, optionally following updates') .action(async cmd => { - const follow = cmd.opts().follow; - const ping = cmd.opts().ping; - const logCheckIntervalMs = ping !== undefined ? Number(ping) : 5_000; - - do { - // Scope cancellation and propagate. - const { promise: followCancelled, reject: cancelFollower } = - makePromiseKit(); - cancelled.catch(cancelFollower); - - (async () => { - const { getBootstrap } = await makeEndoClient( - 'log-follower-probe', - sockPath, - followCancelled, - ); - const bootstrap = await getBootstrap(); - for (;;) { - await delay(logCheckIntervalMs, followCancelled); - await E(bootstrap).ping(); - } - })().catch(cancelFollower); - - await new Promise((resolve, reject) => { - const args = follow ? ['-f'] : []; - const child = spawn('tail', [...args, logPath], { - stdio: ['inherit', 'inherit', 'inherit'], - }); - child.on('error', reject); - child.on('exit', resolve); - followCancelled.catch(() => { - child.kill(); - }); - }); - - if (follow) { - await delay(logCheckIntervalMs, cancelled); - } - } while (follow); + const { follow, ping } = cmd.opts(); + const { log: logCommand } = await import('./log.js'); + return logCommand({ + cancel, + cancelled, + sockPath, + follow, + ping, + }); }); program diff --git a/packages/cli/src/log.js b/packages/cli/src/log.js new file mode 100644 index 0000000000..c39d6ce0b6 --- /dev/null +++ b/packages/cli/src/log.js @@ -0,0 +1,77 @@ +/* global process, setTimeout, clearTimeout */ +/* eslint-disable no-await-in-loop */ + +import os from 'os'; +import path from 'path'; +import { spawn } from 'child_process'; +import { makePromiseKit } from '@endo/promise-kit'; +import { makeEndoClient } from '@endo/daemon'; +import { whereEndoState, whereEndoSock } from '@endo/where'; +import { E } from '@endo/far'; +import { withInterrupt } from './context.js'; + +const delay = async (ms, cancelled) => { + // Do not attempt to set up a timer if already cancelled. + await Promise.race([cancelled, undefined]); + return new Promise((resolve, reject) => { + const handle = setTimeout(resolve, ms); + cancelled.catch(error => { + reject(error); + clearTimeout(handle); + }); + }); +}; + +export const log = async ({ follow, ping }) => + withInterrupt(async ({ cancel, cancelled }) => { + const logCheckIntervalMs = ping !== undefined ? Number(ping) : 5_000; + + const { username, homedir } = os.userInfo(); + const temp = os.tmpdir(); + const info = { + user: username, + home: homedir, + temp, + }; + + const statePath = whereEndoState(process.platform, process.env, info); + const sockPath = whereEndoSock(process.platform, process.env, info); + + const logPath = path.join(statePath, 'endo.log'); + + do { + // Scope cancellation and propagate. + const { promise: followCancelled, reject: cancelFollower } = + makePromiseKit(); + cancelled.catch(cancelFollower); + + (async () => { + const { getBootstrap } = await makeEndoClient( + 'log-follower-probe', + sockPath, + followCancelled, + ); + const bootstrap = await getBootstrap(); + for (;;) { + await delay(logCheckIntervalMs, followCancelled); + await E(bootstrap).ping(); + } + })().catch(cancelFollower); + + await new Promise((resolve, reject) => { + const args = follow ? ['-f'] : []; + const child = spawn('tail', [...args, logPath], { + stdio: ['inherit', 'inherit', 'inherit'], + }); + child.on('error', reject); + child.on('exit', resolve); + followCancelled.catch(() => { + child.kill(); + }); + }); + + if (follow) { + await delay(logCheckIntervalMs, cancelled); + } + } while (follow); + }); From 3814ab1691b846f11a0e6eb268331fe1062acc02 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Mon, 14 Aug 2023 15:14:31 -0700 Subject: [PATCH 072/234] refactor(cli): Move ping into command library --- packages/cli/src/endo.js | 12 ++++-------- packages/cli/src/ping.js | 15 +++++++++++++++ 2 files changed, 19 insertions(+), 8 deletions(-) create mode 100644 packages/cli/src/ping.js diff --git a/packages/cli/src/endo.js b/packages/cli/src/endo.js index a75e0816a6..e7a551e273 100644 --- a/packages/cli/src/endo.js +++ b/packages/cli/src/endo.js @@ -19,7 +19,6 @@ import { restart, clean, reset, - makeEndoClient, makeReaderRef, makeRefIterator, makeRefReader, @@ -169,14 +168,11 @@ export const main = async rawArgs => { .command('ping') .description('prints ok if the daemon is responsive') .action(async _cmd => { - const { getBootstrap } = await makeEndoClient( - 'health-checker', - sockPath, + const { ping } = await import('./ping.js'); + return ping({ cancelled, - ); - const bootstrap = getBootstrap(); - await E(bootstrap).ping(); - process.stderr.write('ok\n'); + sockPath, + }); }); program diff --git a/packages/cli/src/ping.js b/packages/cli/src/ping.js new file mode 100644 index 0000000000..20d59ef28a --- /dev/null +++ b/packages/cli/src/ping.js @@ -0,0 +1,15 @@ +/* global process */ + +import { E } from '@endo/far'; +import { makeEndoClient } from '@endo/daemon'; + +export const ping = async ({ cancelled, sockPath }) => { + const { getBootstrap } = await makeEndoClient( + 'health-checker', + sockPath, + cancelled, + ); + const bootstrap = getBootstrap(); + await E(bootstrap).ping(); + process.stderr.write('ok\n'); +}; From 81509ed17dbd238b787f489a389147c77f3ba2c2 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Mon, 14 Aug 2023 15:29:10 -0700 Subject: [PATCH 073/234] refactor(cli): Move store into command library --- packages/cli/src/endo.js | 29 ++++++++--------------------- packages/cli/src/store.js | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 21 deletions(-) create mode 100644 packages/cli/src/store.js diff --git a/packages/cli/src/endo.js b/packages/cli/src/endo.js index e7a551e273..df7161c6a0 100644 --- a/packages/cli/src/endo.js +++ b/packages/cli/src/endo.js @@ -19,7 +19,6 @@ import { restart, clean, reset, - makeReaderRef, makeRefIterator, makeRefReader, } from '@endo/daemon'; @@ -29,7 +28,6 @@ import { whereEndoSock, whereEndoCache, } from '@endo/where'; -import { makeNodeReader } from '@endo/stream-node'; import { E } from '@endo/far'; import { provideEndoClient } from './client.js'; @@ -215,26 +213,15 @@ export const main = async rawArgs => { .description('stores a readable file') .action(async (storablePath, cmd) => { const { name, as: partyNames } = cmd.opts(); - const nodeReadStream = fs.createReadStream(storablePath); - const reader = makeNodeReader(nodeReadStream); - const readerRef = makeReaderRef(reader); - - const { getBootstrap } = await provideEndoClient( - 'cli', - sockPath, + const { store } = await import('./store.js'); + return store({ + cancel, cancelled, - ); - try { - const bootstrap = getBootstrap(); - let party = E(bootstrap).host(); - for (const partyName of partyNames) { - party = E(party).provide(partyName); - } - await E(party).store(readerRef, name); - } catch (error) { - console.error(error); - cancel(error); - } + sockPath, + storablePath, + name, + partyNames, + }); }); program diff --git a/packages/cli/src/store.js b/packages/cli/src/store.js new file mode 100644 index 0000000000..98adb86332 --- /dev/null +++ b/packages/cli/src/store.js @@ -0,0 +1,32 @@ +import fs from 'fs'; + +import { makeNodeReader } from '@endo/stream-node'; +import { makeReaderRef } from '@endo/daemon'; +import { E } from '@endo/far'; +import { provideEndoClient } from './client.js'; + +export const store = async ({ + cancel, + cancelled, + sockPath, + storablePath, + name, + partyNames, +}) => { + const nodeReadStream = fs.createReadStream(storablePath); + const reader = makeNodeReader(nodeReadStream); + const readerRef = makeReaderRef(reader); + + const { getBootstrap } = await provideEndoClient('cli', sockPath, cancelled); + try { + const bootstrap = getBootstrap(); + let party = E(bootstrap).host(); + for (const partyName of partyNames) { + party = E(party).provide(partyName); + } + await E(party).store(readerRef, name); + } catch (error) { + console.error(error); + cancel(error); + } +}; From 3463156dd8601cbcd90a54e5465a9ca8b8d62d88 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Mon, 14 Aug 2023 15:39:06 -0700 Subject: [PATCH 074/234] refactor(cli): Move spawn to command library --- packages/cli/src/endo.js | 24 +++++++----------------- packages/cli/src/spawn.js | 27 +++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 17 deletions(-) create mode 100644 packages/cli/src/spawn.js diff --git a/packages/cli/src/endo.js b/packages/cli/src/endo.js index df7161c6a0..7097381cbd 100644 --- a/packages/cli/src/endo.js +++ b/packages/cli/src/endo.js @@ -235,24 +235,14 @@ export const main = async rawArgs => { ) .action(async (petNames, cmd) => { const { as: partyNames } = cmd.opts(); - const { getBootstrap } = await provideEndoClient( - 'cli', - sockPath, + const { spawn } = await import('./spawn.js'); + return spawn({ + cancel, cancelled, - ); - try { - const bootstrap = getBootstrap(); - let party = E(bootstrap).host(); - for (const partyName of partyNames) { - party = E(party).provide(partyName); - } - for (const petName of petNames) { - await E(party).makeWorker(petName); - } - } catch (error) { - console.error(error); - cancel(error); - } + sockPath, + petNames, + partyNames, + }); }); program diff --git a/packages/cli/src/spawn.js b/packages/cli/src/spawn.js new file mode 100644 index 0000000000..8277ae0abb --- /dev/null +++ b/packages/cli/src/spawn.js @@ -0,0 +1,27 @@ +/* eslint-disable no-await-in-loop */ + +import { E } from '@endo/far'; +import { provideEndoClient } from './client.js'; + +export const spawn = async ({ + cancel, + cancelled, + sockPath, + petNames, + partyNames, +}) => { + const { getBootstrap } = await provideEndoClient('cli', sockPath, cancelled); + try { + const bootstrap = getBootstrap(); + let party = E(bootstrap).host(); + for (const partyName of partyNames) { + party = E(party).provide(partyName); + } + for (const petName of petNames) { + await E(party).makeWorker(petName); + } + } catch (error) { + console.error(error); + cancel(error); + } +}; From 66a7565e314fec129c14576e7782fe4961a98b53 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Mon, 14 Aug 2023 15:42:59 -0700 Subject: [PATCH 075/234] refactor(cli): Move show to command library --- packages/cli/src/endo.js | 23 +++++++---------------- packages/cli/src/show.js | 24 ++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 16 deletions(-) create mode 100644 packages/cli/src/show.js diff --git a/packages/cli/src/endo.js b/packages/cli/src/endo.js index 7097381cbd..ab30705be8 100644 --- a/packages/cli/src/endo.js +++ b/packages/cli/src/endo.js @@ -256,23 +256,14 @@ export const main = async rawArgs => { .description('prints the named value') .action(async (name, cmd) => { const { as: partyNames } = cmd.opts(); - const { getBootstrap } = await provideEndoClient( - 'cli', - sockPath, + const { show } = await import('./show.js'); + return show({ + cancel, cancelled, - ); - try { - const bootstrap = getBootstrap(); - let party = E(bootstrap).host(); - for (const partyName of partyNames) { - party = E(party).provide(partyName); - } - const pet = await E(party).provide(name); - console.log(pet); - } catch (error) { - console.error(error); - cancel(error); - } + sockPath, + name, + partyNames, + }); }); program diff --git a/packages/cli/src/show.js b/packages/cli/src/show.js new file mode 100644 index 0000000000..d23fad34d1 --- /dev/null +++ b/packages/cli/src/show.js @@ -0,0 +1,24 @@ +import { E } from '@endo/far'; +import { provideEndoClient } from './client.js'; + +export const show = async ({ + cancel, + cancelled, + sockPath, + name, + partyNames, +}) => { + const { getBootstrap } = await provideEndoClient('cli', sockPath, cancelled); + try { + const bootstrap = getBootstrap(); + let party = E(bootstrap).host(); + for (const partyName of partyNames) { + party = E(party).provide(partyName); + } + const pet = await E(party).provide(name); + console.log(pet); + } catch (error) { + console.error(error); + cancel(error); + } +}; From a368c99dbc200b37a97731578ef2d952320605ba Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Mon, 14 Aug 2023 15:47:20 -0700 Subject: [PATCH 076/234] refactor(cli): Move follow to command library --- packages/cli/src/endo.js | 25 +++++++------------------ packages/cli/src/follow.js | 27 +++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 18 deletions(-) create mode 100644 packages/cli/src/follow.js diff --git a/packages/cli/src/endo.js b/packages/cli/src/endo.js index ab30705be8..a7f7502e8f 100644 --- a/packages/cli/src/endo.js +++ b/packages/cli/src/endo.js @@ -279,25 +279,14 @@ export const main = async rawArgs => { ) .action(async (name, cmd) => { const { as: partyNames } = cmd.opts(); - const { getBootstrap } = await provideEndoClient( - 'cli', - sockPath, + const { follow } = await import('./follow.js'); + return follow({ + cancel, cancelled, - ); - try { - const bootstrap = getBootstrap(); - let party = E(bootstrap).host(); - for (const partyName of partyNames) { - party = E(party).provide(partyName); - } - const iterable = await E(party).provide(name); - for await (const iterand of makeRefIterator(iterable)) { - console.log(iterand); - } - } catch (error) { - console.error(error); - cancel(error); - } + sockPath, + name, + partyNames, + }); }); program diff --git a/packages/cli/src/follow.js b/packages/cli/src/follow.js new file mode 100644 index 0000000000..6a601a19dc --- /dev/null +++ b/packages/cli/src/follow.js @@ -0,0 +1,27 @@ +import { E } from '@endo/far'; +import { makeRefIterator } from '@endo/daemon'; +import { provideEndoClient } from './client.js'; + +export const followCommand = async ({ + cancel, + cancelled, + sockPath, + name, + partyNames, +}) => { + const { getBootstrap } = await provideEndoClient('cli', sockPath, cancelled); + try { + const bootstrap = getBootstrap(); + let party = E(bootstrap).host(); + for (const partyName of partyNames) { + party = E(party).provide(partyName); + } + const iterable = await E(party).provide(name); + for await (const iterand of makeRefIterator(iterable)) { + console.log(iterand); + } + } catch (error) { + console.error(error); + cancel(error); + } +}; From bd68113b2afbccd7523e0885fe8c1169e69a5c37 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Mon, 14 Aug 2023 16:06:29 -0700 Subject: [PATCH 077/234] refactor(cli): Move cat to command library --- packages/cli/src/cat.js | 37 +++++++++++++++++++++++++++++++++++++ packages/cli/src/endo.js | 28 +++++++--------------------- 2 files changed, 44 insertions(+), 21 deletions(-) create mode 100644 packages/cli/src/cat.js diff --git a/packages/cli/src/cat.js b/packages/cli/src/cat.js new file mode 100644 index 0000000000..0136d03d03 --- /dev/null +++ b/packages/cli/src/cat.js @@ -0,0 +1,37 @@ +/* global process */ + +import { E } from '@endo/far'; +import { makeRefReader } from '@endo/daemon'; +import { provideEndoClient } from './client.js'; + +export const cat = async ({ + cancel, + cancelled, + sockPath, + name, + partyNames, +}) => { + const { getBootstrap } = await provideEndoClient( + 'cli', + sockPath, + cancelled, + name, + partyNames, + ); + try { + const bootstrap = getBootstrap(); + let party = E(bootstrap).host(); + for (const partyName of partyNames) { + party = E(party).provide(partyName); + } + const readable = await E(party).provide(name); + const readerRef = E(readable).stream(); + const reader = makeRefReader(readerRef); + for await (const chunk of reader) { + process.stdout.write(chunk); + } + } catch (error) { + console.error(error); + cancel(error); + } +}; diff --git a/packages/cli/src/endo.js b/packages/cli/src/endo.js index a7f7502e8f..c2b19e4d2d 100644 --- a/packages/cli/src/endo.js +++ b/packages/cli/src/endo.js @@ -20,7 +20,6 @@ import { clean, reset, makeRefIterator, - makeRefReader, } from '@endo/daemon'; import { whereEndoState, @@ -300,27 +299,14 @@ export const main = async rawArgs => { .description('prints the content of the named readable file') .action(async (name, cmd) => { const { as: partyNames } = cmd.opts(); - const { getBootstrap } = await provideEndoClient( - 'cli', - sockPath, + const { cat } = await import('./cat.js'); + return cat({ + cancel, cancelled, - ); - try { - const bootstrap = getBootstrap(); - let party = E(bootstrap).host(); - for (const partyName of partyNames) { - party = E(party).provide(partyName); - } - const readable = await E(party).provide(name); - const readerRef = E(readable).stream(); - const reader = makeRefReader(readerRef); - for await (const chunk of reader) { - process.stdout.write(chunk); - } - } catch (error) { - console.error(error); - cancel(error); - } + sockPath, + name, + partyNames, + }); }); program From bdaaaa1abf7f247cc94795c1a07cf2ce8f9b84f5 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Mon, 14 Aug 2023 16:14:16 -0700 Subject: [PATCH 078/234] refactor(cli): Move list to command library --- packages/cli/src/endo.js | 24 ++++++------------------ packages/cli/src/list.js | 20 ++++++++++++++++++++ 2 files changed, 26 insertions(+), 18 deletions(-) create mode 100644 packages/cli/src/list.js diff --git a/packages/cli/src/endo.js b/packages/cli/src/endo.js index c2b19e4d2d..1530e36abb 100644 --- a/packages/cli/src/endo.js +++ b/packages/cli/src/endo.js @@ -320,25 +320,13 @@ export const main = async rawArgs => { .description('lists pet names') .action(async cmd => { const { as: partyNames } = cmd.opts(); - const { getBootstrap } = await provideEndoClient( - 'cli', - sockPath, + const { list } = await import('./list.js'); + return list({ + cancel, cancelled, - ); - try { - const bootstrap = getBootstrap(); - let party = E(bootstrap).host(); - for (const partyName of partyNames) { - party = E(party).provide(partyName); - } - const petNames = await E(party).list(); - for await (const petName of petNames) { - console.log(petName); - } - } catch (error) { - console.error(error); - cancel(error); - } + sockPath, + partyNames, + }); }); program diff --git a/packages/cli/src/list.js b/packages/cli/src/list.js new file mode 100644 index 0000000000..aba219bf4a --- /dev/null +++ b/packages/cli/src/list.js @@ -0,0 +1,20 @@ +import { E } from '@endo/far'; +import { provideEndoClient } from './client.js'; + +export const list = async ({ cancel, cancelled, sockPath, partyNames }) => { + const { getBootstrap } = await provideEndoClient('cli', sockPath, cancelled); + try { + const bootstrap = getBootstrap(); + let party = E(bootstrap).host(); + for (const partyName of partyNames) { + party = E(party).provide(partyName); + } + const petNames = await E(party).list(); + for await (const petName of petNames) { + console.log(petName); + } + } catch (error) { + console.error(error); + cancel(error); + } +}; From 9cff5cf1c3fb38f23ef854abb5e8fe2c754b9bf8 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Mon, 14 Aug 2023 16:16:46 -0700 Subject: [PATCH 079/234] refactor(cli): Move remove to command library --- packages/cli/src/endo.js | 22 +++++++--------------- packages/cli/src/remove.js | 23 +++++++++++++++++++++++ 2 files changed, 30 insertions(+), 15 deletions(-) create mode 100644 packages/cli/src/remove.js diff --git a/packages/cli/src/endo.js b/packages/cli/src/endo.js index 1530e36abb..e9469d0266 100644 --- a/packages/cli/src/endo.js +++ b/packages/cli/src/endo.js @@ -340,22 +340,14 @@ export const main = async rawArgs => { ) .action(async (petNames, cmd) => { const { as: partyNames } = cmd.opts(); - const { getBootstrap } = await provideEndoClient( - 'cli', - sockPath, + const { remove } = await import('./remove.js'); + return remove({ + cancel, cancelled, - ); - try { - const bootstrap = getBootstrap(); - let party = E(bootstrap).host(); - for (const partyName of partyNames) { - party = E(party).provide(partyName); - } - await Promise.all(petNames.map(petName => E(party).remove(petName))); - } catch (error) { - console.error(error); - cancel(error); - } + sockPath, + petNames, + partyNames, + }); }); program diff --git a/packages/cli/src/remove.js b/packages/cli/src/remove.js new file mode 100644 index 0000000000..2823412f8b --- /dev/null +++ b/packages/cli/src/remove.js @@ -0,0 +1,23 @@ +import { E } from '@endo/far'; +import { provideEndoClient } from './client.js'; + +export const remove = async ({ + cancel, + cancelled, + sockPath, + petNames, + partyNames, +}) => { + const { getBootstrap } = await provideEndoClient('cli', sockPath, cancelled); + try { + const bootstrap = getBootstrap(); + let party = E(bootstrap).host(); + for (const partyName of partyNames) { + party = E(party).provide(partyName); + } + await Promise.all(petNames.map(petName => E(party).remove(petName))); + } catch (error) { + console.error(error); + cancel(error); + } +}; From 870a4489e0af65adf99739b217cbf6d6d20624f9 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Mon, 14 Aug 2023 16:20:05 -0700 Subject: [PATCH 080/234] refactor(cli): Move rename to command library --- packages/cli/src/endo.js | 23 ++++++++--------------- packages/cli/src/rename.js | 24 ++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 15 deletions(-) create mode 100644 packages/cli/src/rename.js diff --git a/packages/cli/src/endo.js b/packages/cli/src/endo.js index e9469d0266..12588926a1 100644 --- a/packages/cli/src/endo.js +++ b/packages/cli/src/endo.js @@ -361,22 +361,15 @@ export const main = async rawArgs => { ) .action(async (fromName, toName, cmd) => { const { as: partyNames } = cmd.opts(); - const { getBootstrap } = await provideEndoClient( - 'cli', - sockPath, + const { rename } = await import('./rename.js'); + return rename({ + cancel, cancelled, - ); - try { - const bootstrap = getBootstrap(); - let party = E(bootstrap).host(); - for (const partyName of partyNames) { - party = E(party).provide(partyName); - } - await E(party).rename(fromName, toName); - } catch (error) { - console.error(error); - cancel(error); - } + sockPath, + fromName, + toName, + partyNames, + }); }); program diff --git a/packages/cli/src/rename.js b/packages/cli/src/rename.js new file mode 100644 index 0000000000..260019afc4 --- /dev/null +++ b/packages/cli/src/rename.js @@ -0,0 +1,24 @@ +import { E } from '@endo/far'; +import { provideEndoClient } from './client.js'; + +export const rename = async ({ + cancel, + cancelled, + sockPath, + fromName, + toName, + partyNames, +}) => { + const { getBootstrap } = await provideEndoClient('cli', sockPath, cancelled); + try { + const bootstrap = getBootstrap(); + let party = E(bootstrap).host(); + for (const partyName of partyNames) { + party = E(party).provide(partyName); + } + await E(party).rename(fromName, toName); + } catch (error) { + console.error(error); + cancel(error); + } +}; From 1921888dc24bf26953a4f0357c6537e8c45e6bc4 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Mon, 14 Aug 2023 16:25:01 -0700 Subject: [PATCH 081/234] refactor(cli): Move mkguest to command library --- packages/cli/src/endo.js | 19 ++----------------- packages/cli/src/mkguest.js | 24 ++++++++++++++++++++++++ 2 files changed, 26 insertions(+), 17 deletions(-) create mode 100644 packages/cli/src/mkguest.js diff --git a/packages/cli/src/endo.js b/packages/cli/src/endo.js index 12588926a1..385b66193d 100644 --- a/packages/cli/src/endo.js +++ b/packages/cli/src/endo.js @@ -413,23 +413,8 @@ export const main = async rawArgs => { .description('creates new guest powers, pet store, and mailbox') .action(async (name, cmd) => { const { as: partyNames } = cmd.opts(); - const { getBootstrap } = await provideEndoClient( - 'cli', - sockPath, - cancelled, - ); - try { - const bootstrap = getBootstrap(); - let party = E(bootstrap).host(); - for (const partyName of partyNames) { - party = E(party).provide(partyName); - } - const newGuest = await E(party).provideGuest(name); - console.log(newGuest); - } catch (error) { - console.error(error); - cancel(error); - } + const { mkguest } = await import('./mkguest.js'); + return mkguest({ cancel, cancelled, sockPath, name, partyNames }); }); program diff --git a/packages/cli/src/mkguest.js b/packages/cli/src/mkguest.js new file mode 100644 index 0000000000..c1552602a7 --- /dev/null +++ b/packages/cli/src/mkguest.js @@ -0,0 +1,24 @@ +import { E } from '@endo/far'; +import { provideEndoClient } from './client.js'; + +export const mkguest = async ({ + cancel, + cancelled, + sockPath, + name, + partyNames, +}) => { + const { getBootstrap } = await provideEndoClient('cli', sockPath, cancelled); + try { + const bootstrap = getBootstrap(); + let party = E(bootstrap).host(); + for (const partyName of partyNames) { + party = E(party).provide(partyName); + } + const newGuest = await E(party).provideGuest(name); + console.log(newGuest); + } catch (error) { + console.error(error); + cancel(error); + } +}; From c411ae477b88656d25fcd6ed51acb4dde2b57661 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Mon, 14 Aug 2023 16:26:57 -0700 Subject: [PATCH 082/234] refactor(cli): Move mkhost into command library --- packages/cli/src/endo.js | 23 +++++++---------------- packages/cli/src/mkhost.js | 24 ++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 16 deletions(-) create mode 100644 packages/cli/src/mkhost.js diff --git a/packages/cli/src/endo.js b/packages/cli/src/endo.js index 385b66193d..3220ea2d14 100644 --- a/packages/cli/src/endo.js +++ b/packages/cli/src/endo.js @@ -383,23 +383,14 @@ export const main = async rawArgs => { .description('creates new host powers, pet store, and mailbox') .action(async (name, cmd) => { const { as: partyNames } = cmd.opts(); - const { getBootstrap } = await provideEndoClient( - 'cli', - sockPath, + const { mkhost } = await import('./mkhost.js'); + return mkhost({ + cancel, cancelled, - ); - try { - const bootstrap = getBootstrap(); - let party = E(bootstrap).host(); - for (const partyName of partyNames) { - party = E(party).provide(partyName); - } - const newHost = await E(party).provideHost(name); - console.log(newHost); - } catch (error) { - console.error(error); - cancel(error); - } + sockPath, + name, + partyNames, + }); }); program diff --git a/packages/cli/src/mkhost.js b/packages/cli/src/mkhost.js new file mode 100644 index 0000000000..d043651158 --- /dev/null +++ b/packages/cli/src/mkhost.js @@ -0,0 +1,24 @@ +import { E } from '@endo/far'; +import { provideEndoClient } from './client.js'; + +export const mkhost = async ({ + cancel, + cancelled, + sockPath, + name, + partyNames, +}) => { + const { getBootstrap } = await provideEndoClient('cli', sockPath, cancelled); + try { + const bootstrap = getBootstrap(); + let party = E(bootstrap).host(); + for (const partyName of partyNames) { + party = E(party).provide(partyName); + } + const newHost = await E(party).provideHost(name); + console.log(newHost); + } catch (error) { + console.error(error); + cancel(error); + } +}; From 6f5d9a62e55f032b860a65301fab9a0848e05d52 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Mon, 14 Aug 2023 16:32:19 -0700 Subject: [PATCH 083/234] refactor(cli): Move inbox to command library --- packages/cli/src/endo.js | 56 +++++++-------------------------------- packages/cli/src/inbox.js | 45 +++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 46 deletions(-) create mode 100644 packages/cli/src/inbox.js diff --git a/packages/cli/src/endo.js b/packages/cli/src/endo.js index 3220ea2d14..5207620088 100644 --- a/packages/cli/src/endo.js +++ b/packages/cli/src/endo.js @@ -13,14 +13,7 @@ import os from 'os'; import { Command } from 'commander'; import { makePromiseKit } from '@endo/promise-kit'; -import { - start, - stop, - restart, - clean, - reset, - makeRefIterator, -} from '@endo/daemon'; +import { start, stop, restart, clean, reset } from '@endo/daemon'; import { whereEndoState, whereEndoEphemeralState, @@ -278,8 +271,8 @@ export const main = async rawArgs => { ) .action(async (name, cmd) => { const { as: partyNames } = cmd.opts(); - const { follow } = await import('./follow.js'); - return follow({ + const { followCommand } = await import('./follow.js'); + return followCommand({ cancel, cancelled, sockPath, @@ -420,43 +413,14 @@ export const main = async rawArgs => { .description('prints pending requests that have been sent to you') .action(async cmd => { const { as: partyNames, follow } = cmd.opts(); - const { getBootstrap } = await provideEndoClient( - 'cli', - sockPath, + const { inbox } = await import('./inbox.js'); + return inbox({ + cancel, cancelled, - ); - try { - const bootstrap = getBootstrap(); - let party = E(bootstrap).host(); - for (const partyName of partyNames) { - party = E(party).provide(partyName); - } - const messages = follow - ? makeRefIterator(E(party).followMessages()) - : await E(party).listMessages(); - for await (const message of messages) { - const { number, who, when } = message; - if (message.type === 'request') { - const { what } = message; - console.log( - `${number}. ${JSON.stringify(who)} requested ${JSON.stringify( - what, - )} at ${JSON.stringify(when)}`, - ); - } else { - console.log( - `${number}. ${JSON.stringify( - who, - )} sent an unrecognizable message at ${JSON.stringify( - when, - )}. Consider upgrading.`, - ); - } - } - } catch (error) { - console.error(error); - cancel(error); - } + sockPath, + follow, + partyNames, + }); }); program diff --git a/packages/cli/src/inbox.js b/packages/cli/src/inbox.js new file mode 100644 index 0000000000..b30ee811fd --- /dev/null +++ b/packages/cli/src/inbox.js @@ -0,0 +1,45 @@ +import { E } from '@endo/far'; +import { makeRefIterator } from '@endo/daemon'; +import { provideEndoClient } from './client.js'; + +export const followCommand = async ({ + cancel, + cancelled, + sockPath, + follow, + partyNames, +}) => { + const { getBootstrap } = await provideEndoClient('cli', sockPath, cancelled); + try { + const bootstrap = getBootstrap(); + let party = E(bootstrap).host(); + for (const partyName of partyNames) { + party = E(party).provide(partyName); + } + const messages = follow + ? makeRefIterator(E(party).followMessages()) + : await E(party).listMessages(); + for await (const message of messages) { + const { number, who, when } = message; + if (message.type === 'request') { + const { what } = message; + console.log( + `${number}. ${JSON.stringify(who)} requested ${JSON.stringify( + what, + )} at ${JSON.stringify(when)}`, + ); + } else { + console.log( + `${number}. ${JSON.stringify( + who, + )} sent an unrecognizable message at ${JSON.stringify( + when, + )}. Consider upgrading.`, + ); + } + } + } catch (error) { + console.error(error); + cancel(error); + } +}; From c425eb6e8a367ac86f9d958db920925ff8738dbc Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Mon, 14 Aug 2023 16:35:33 -0700 Subject: [PATCH 084/234] refactor(cli): Move request to command library --- packages/cli/src/endo.js | 29 ++++++++--------------------- packages/cli/src/request.js | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 21 deletions(-) create mode 100644 packages/cli/src/request.js diff --git a/packages/cli/src/endo.js b/packages/cli/src/endo.js index 5207620088..e2e3e5ee2f 100644 --- a/packages/cli/src/endo.js +++ b/packages/cli/src/endo.js @@ -438,28 +438,15 @@ export const main = async rawArgs => { ) .action(async (description, cmd) => { const { name: resultName, as: partyNames } = cmd.opts(); - if (partyNames.length === 0) { - console.error('Specify the name of a guest with -a or --as '); - process.exitCode = 1; - return; - } - const { getBootstrap } = await provideEndoClient( - 'cli', - sockPath, + const { request } = await import('./request.js'); + return request({ + cancel, cancelled, - ); - try { - const bootstrap = getBootstrap(); - let party = E(bootstrap).host(); - for (const partyName of partyNames) { - party = E(party).provideGuest(partyName); - } - const result = await E(party).request(description, resultName); - console.log(result); - } catch (error) { - console.error(error); - cancel(error); - } + sockPath, + description, + resultName, + partyNames, + }); }); program diff --git a/packages/cli/src/request.js b/packages/cli/src/request.js new file mode 100644 index 0000000000..049d7bba8f --- /dev/null +++ b/packages/cli/src/request.js @@ -0,0 +1,32 @@ +/* global process */ + +import { E } from '@endo/far'; +import { provideEndoClient } from './client.js'; + +export const request = async ({ + cancel, + cancelled, + sockPath, + description, + resultName, + partyNames, +}) => { + if (partyNames.length === 0) { + console.error('Specify the name of a guest with -a or --as '); + process.exitCode = 1; + return; + } + const { getBootstrap } = await provideEndoClient('cli', sockPath, cancelled); + try { + const bootstrap = getBootstrap(); + let party = E(bootstrap).host(); + for (const partyName of partyNames) { + party = E(party).provideGuest(partyName); + } + const result = await E(party).request(description, resultName); + console.log(result); + } catch (error) { + console.error(error); + cancel(error); + } +}; From 6f269c1c83ddedf5a718f96d6930f6d786bbf551 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Mon, 14 Aug 2023 16:39:00 -0700 Subject: [PATCH 085/234] refactor(cli): Move resolve to command library --- packages/cli/src/endo.js | 25 ++++++++----------------- packages/cli/src/resolve.js | 23 +++++++++++++++++++++++ 2 files changed, 31 insertions(+), 17 deletions(-) create mode 100644 packages/cli/src/resolve.js diff --git a/packages/cli/src/endo.js b/packages/cli/src/endo.js index e2e3e5ee2f..fec763c7c0 100644 --- a/packages/cli/src/endo.js +++ b/packages/cli/src/endo.js @@ -460,24 +460,15 @@ export const main = async rawArgs => { .description('responds to a pending request with the named value') .action(async (requestNumberText, resolutionName, cmd) => { const { as: partyNames } = cmd.opts(); - // TODO less bad number parsing. - const requestNumber = Number(requestNumberText); - const { getBootstrap } = await provideEndoClient( - 'cli', - sockPath, + const { resolveCommand } = await import('./resolve.js'); + return resolveCommand({ + cancel, cancelled, - ); - try { - const bootstrap = getBootstrap(); - let party = E(bootstrap).host(); - for (const partyName of partyNames) { - party = E(party).provide(partyName); - } - await E(party).resolve(requestNumber, resolutionName); - } catch (error) { - console.error(error); - cancel(error); - } + sockPath, + requestNumberText, + resolutionName, + partyNames, + }); }); program diff --git a/packages/cli/src/resolve.js b/packages/cli/src/resolve.js new file mode 100644 index 0000000000..ba8ae61e9e --- /dev/null +++ b/packages/cli/src/resolve.js @@ -0,0 +1,23 @@ +import { E } from '@endo/far'; +import { provideEndoClient } from './client.js'; + +export const resolveCommand = async ({ + requestNumberText, + resolutionName, + partyNames, +}) => { + // TODO less bad number parsing. + const requestNumber = Number(requestNumberText); + const { getBootstrap } = await provideEndoClient('cli', sockPath, cancelled); + try { + const bootstrap = getBootstrap(); + let party = E(bootstrap).host(); + for (const partyName of partyNames) { + party = E(party).provide(partyName); + } + await E(party).resolve(requestNumber, resolutionName); + } catch (error) { + console.error(error); + cancel(error); + } +}; From b143be02f03880871d24429e0aa0c9f37c93c267 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Mon, 14 Aug 2023 16:42:10 -0700 Subject: [PATCH 086/234] refactor(cli): Move reject to command library --- packages/cli/src/endo.js | 25 ++++++++----------------- packages/cli/src/reject.js | 26 ++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 17 deletions(-) create mode 100644 packages/cli/src/reject.js diff --git a/packages/cli/src/endo.js b/packages/cli/src/endo.js index fec763c7c0..eb4b73457a 100644 --- a/packages/cli/src/endo.js +++ b/packages/cli/src/endo.js @@ -482,24 +482,15 @@ export const main = async rawArgs => { ) .action(async (requestNumberText, message, cmd) => { const { as: partyNames } = cmd.opts(); - // TODO less bad number parsing. - const requestNumber = Number(requestNumberText); - const { getBootstrap } = await provideEndoClient( - 'cli', - sockPath, + const { rejectCommand } = await import('./reject.js'); + return rejectCommand({ + cancel, cancelled, - ); - try { - const bootstrap = getBootstrap(); - let party = E(bootstrap).host(); - for (const partyName of partyNames) { - party = E(party).provide(partyName); - } - await E(party).reject(requestNumber, message); - } catch (error) { - console.error(error); - cancel(error); - } + sockPath, + requestNumberText, + message, + partyNames, + }); }); program diff --git a/packages/cli/src/reject.js b/packages/cli/src/reject.js new file mode 100644 index 0000000000..a0c8bc437e --- /dev/null +++ b/packages/cli/src/reject.js @@ -0,0 +1,26 @@ +import { E } from '@endo/far'; +import { provideEndoClient } from './client.js'; + +export const rejectCommand = async ({ + cancel, + cancelled, + sockPath, + requestNumberText, + message, + partyNames, +}) => { + // TODO less bad number parsing. + const requestNumber = Number(requestNumberText); + const { getBootstrap } = await provideEndoClient('cli', sockPath, cancelled); + try { + const bootstrap = getBootstrap(); + let party = E(bootstrap).host(); + for (const partyName of partyNames) { + party = E(party).provide(partyName); + } + await E(party).reject(requestNumber, message); + } catch (error) { + console.error(error); + cancel(error); + } +}; From 3a59ec5be4337b369bb7ce9e0a1e6b6d1385e3ff Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Mon, 14 Aug 2023 16:45:08 -0700 Subject: [PATCH 087/234] refactor(cli): Move eval to command library --- packages/cli/src/endo.js | 54 ++++++++-------------------------------- packages/cli/src/eval.js | 52 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 44 deletions(-) create mode 100644 packages/cli/src/eval.js diff --git a/packages/cli/src/endo.js b/packages/cli/src/endo.js index eb4b73457a..382d5b697e 100644 --- a/packages/cli/src/endo.js +++ b/packages/cli/src/endo.js @@ -20,9 +20,6 @@ import { whereEndoSock, whereEndoCache, } from '@endo/where'; -import { E } from '@endo/far'; - -import { provideEndoClient } from './client.js'; const collect = (value, values) => values.concat([value]); @@ -516,48 +513,17 @@ export const main = async rawArgs => { worker: workerName = 'MAIN', as: partyNames, } = cmd.opts(); - const { getBootstrap } = await provideEndoClient( - 'cli', - sockPath, + const { evalCommand } = await import('./eval.js'); + return evalCommand({ + cancel, cancelled, - ); - try { - const bootstrap = getBootstrap(); - let party = E(bootstrap).host(); - for (const partyName of partyNames) { - party = E(party).provide(partyName); - } - - const pairs = names.map(name => { - /** @type {Array} */ - const pair = name.split(':'); - if (pair.length === 1) { - return [name, name]; - } - if (pair.length > 2) { - throw new Error( - `Specify either a name endowmentName:pet-name, got: ${JSON.stringify( - name, - )}`, - ); - } - return pair; - }); - const codeNames = pairs.map(pair => pair[0]); - const endowmentNames = pairs.map(pair => pair[1]); - - const result = await E(party).evaluate( - workerName, - source, - codeNames, - endowmentNames, - resultName, - ); - console.log(result); - } catch (error) { - console.error(error); - cancel(error); - } + sockPath, + source, + names, + resultName, + workerName, + partyNames, + }); }); program diff --git a/packages/cli/src/eval.js b/packages/cli/src/eval.js new file mode 100644 index 0000000000..f403265233 --- /dev/null +++ b/packages/cli/src/eval.js @@ -0,0 +1,52 @@ +import { E } from '@endo/far'; +import { provideEndoClient } from './client.js'; + +export const evalCommand = async ({ + cancel, + cancelled, + sockPath, + source, + names, + resultName, + workerName, + partyNames, +}) => { + const { getBootstrap } = await provideEndoClient('cli', sockPath, cancelled); + try { + const bootstrap = getBootstrap(); + let party = E(bootstrap).host(); + for (const partyName of partyNames) { + party = E(party).provide(partyName); + } + + const pairs = names.map(name => { + /** @type {Array} */ + const pair = name.split(':'); + if (pair.length === 1) { + return [name, name]; + } + if (pair.length > 2) { + throw new Error( + `Specify either a name endowmentName:pet-name, got: ${JSON.stringify( + name, + )}`, + ); + } + return pair; + }); + const codeNames = pairs.map(pair => pair[0]); + const endowmentNames = pairs.map(pair => pair[1]); + + const result = await E(party).evaluate( + workerName, + source, + codeNames, + endowmentNames, + resultName, + ); + console.log(result); + } catch (error) { + console.error(error); + cancel(error); + } +}; From 55c9e7d1d76521be18f487fda9ca44015668585e Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Mon, 14 Aug 2023 22:37:00 -0700 Subject: [PATCH 088/234] refactor(cli): Deduplicate client connection logic --- packages/cli/src/bundle.js | 20 ++---- packages/cli/src/cat.js | 31 ++------- packages/cli/src/context.js | 80 +++++++++++++++++++++++ packages/cli/src/endo.js | 126 ++++-------------------------------- packages/cli/src/eval.js | 24 ++----- packages/cli/src/follow.js | 27 ++------ packages/cli/src/inbox.js | 23 +++---- packages/cli/src/list.js | 20 ++---- packages/cli/src/make.js | 18 ++---- packages/cli/src/mkguest.js | 20 ++---- packages/cli/src/mkhost.js | 20 ++---- packages/cli/src/open.js | 22 ++----- packages/cli/src/ping.js | 35 ++++++---- packages/cli/src/reject.js | 24 +++---- packages/cli/src/remove.js | 22 ++----- packages/cli/src/rename.js | 27 ++------ packages/cli/src/request.js | 23 +++---- packages/cli/src/resolve.js | 24 +++---- packages/cli/src/run.js | 116 ++++++++++++++++----------------- packages/cli/src/show.js | 26 ++------ packages/cli/src/spawn.js | 25 ++----- packages/cli/src/store.js | 28 ++------ 22 files changed, 286 insertions(+), 495 deletions(-) create mode 100644 packages/cli/src/context.js diff --git a/packages/cli/src/bundle.js b/packages/cli/src/bundle.js index 981cb35687..d168e586e6 100644 --- a/packages/cli/src/bundle.js +++ b/packages/cli/src/bundle.js @@ -1,17 +1,14 @@ /* global process */ +import os from 'os'; import { E } from '@endo/far'; import bundleSource from '@endo/bundle-source'; import { makeReaderRef } from '@endo/daemon'; - -import { provideEndoClient } from './client.js'; +import { withEndoParty } from './context.js'; const textEncoder = new TextEncoder(); export const bundleCommand = async ({ - cancel, - cancelled, - sockPath, applicationPath, bundleName, partyNames, @@ -21,16 +18,7 @@ export const bundleCommand = async ({ const bundleText = JSON.stringify(bundle); const bundleBytes = textEncoder.encode(bundleText); const readerRef = makeReaderRef([bundleBytes]); - const { getBootstrap } = await provideEndoClient('cli', sockPath, cancelled); - try { - const bootstrap = getBootstrap(); - let party = E(bootstrap).host(); - for (const partyName of partyNames) { - party = E(party).provide(partyName); - } + return withEndoParty(partyNames, { os, process }, async ({ party }) => { await E(party).store(readerRef, bundleName); - } catch (error) { - console.error(error); - cancel(error); - } + }); }; diff --git a/packages/cli/src/cat.js b/packages/cli/src/cat.js index 0136d03d03..fb9cc422ff 100644 --- a/packages/cli/src/cat.js +++ b/packages/cli/src/cat.js @@ -1,37 +1,16 @@ /* global process */ +import os from 'os'; import { E } from '@endo/far'; import { makeRefReader } from '@endo/daemon'; -import { provideEndoClient } from './client.js'; +import { withEndoParty } from './context.js'; -export const cat = async ({ - cancel, - cancelled, - sockPath, - name, - partyNames, -}) => { - const { getBootstrap } = await provideEndoClient( - 'cli', - sockPath, - cancelled, - name, - partyNames, - ); - try { - const bootstrap = getBootstrap(); - let party = E(bootstrap).host(); - for (const partyName of partyNames) { - party = E(party).provide(partyName); - } +export const cat = async ({ name, partyNames }) => + withEndoParty(partyNames, { os, process }, async ({ party }) => { const readable = await E(party).provide(name); const readerRef = E(readable).stream(); const reader = makeRefReader(readerRef); for await (const chunk of reader) { process.stdout.write(chunk); } - } catch (error) { - console.error(error); - cancel(error); - } -}; + }); diff --git a/packages/cli/src/context.js b/packages/cli/src/context.js new file mode 100644 index 0000000000..37829c2d7c --- /dev/null +++ b/packages/cli/src/context.js @@ -0,0 +1,80 @@ +/* global process */ + +import { makePromiseKit } from '@endo/promise-kit'; +import { E } from '@endo/far'; +import { whereEndoSock } from '@endo/where'; +import { provideEndoClient } from './client.js'; + +export const withInterrupt = async callback => { + const { promise: cancelled, reject: cancel } = makePromiseKit(); + cancelled.catch(() => {}); + process.once('SIGINT', () => cancel(Error('SIGINT'))); + + try { + await callback({ cancel, cancelled }); + cancel(Error('normal termination')); + } catch (error) { + console.error(error); + cancel(error); + } +}; + +export const withEndoBootstrap = ( + { os, process, clientName = 'cli' }, + callback, +) => + withInterrupt(async ({ cancel, cancelled }) => { + const { username, homedir } = os.userInfo(); + const temp = os.tmpdir(); + const info = { + user: username, + home: homedir, + temp, + }; + + const sockPath = whereEndoSock(process.platform, process.env, info); + + const { getBootstrap } = await provideEndoClient( + clientName, + sockPath, + cancelled, + ); + const bootstrap = getBootstrap(); + await callback({ + cancel, + cancelled, + bootstrap, + }); + }); + +export const withEndoHost = ({ os, process }, callback) => + withEndoBootstrap( + { os, process }, + async ({ cancel, cancelled, bootstrap }) => { + const host = E(bootstrap).host(); + await callback({ + cancel, + cancelled, + bootstrap, + host, + }); + }, + ); + +export const withEndoParty = (partyNames, { os, process }, callback) => + withEndoHost( + { os, process }, + async ({ cancel, cancelled, bootstrap, host }) => { + let party = host; + for (const partyName of partyNames) { + party = E(party).provide(partyName); + } + await callback({ + cancel, + cancelled, + bootstrap, + host, + party, + }); + }, + ); diff --git a/packages/cli/src/endo.js b/packages/cli/src/endo.js index 382d5b697e..5edf41963f 100644 --- a/packages/cli/src/endo.js +++ b/packages/cli/src/endo.js @@ -12,7 +12,6 @@ import url from 'url'; import os from 'os'; import { Command } from 'commander'; -import { makePromiseKit } from '@endo/promise-kit'; import { start, stop, restart, clean, reset } from '@endo/daemon'; import { whereEndoState, @@ -46,10 +45,6 @@ const cachePath = whereEndoCache(process.platform, process.env, info); const logPath = path.join(statePath, 'endo.log'); export const main = async rawArgs => { - const { promise: cancelled, reject: cancel } = makePromiseKit(); - cancelled.catch(() => {}); - process.once('SIGINT', () => cancel(Error('SIGINT'))); - const program = new Command(); program.storeOptionsAsProperties(false); @@ -142,13 +137,7 @@ export const main = async rawArgs => { .action(async cmd => { const { follow, ping } = cmd.opts(); const { log: logCommand } = await import('./log.js'); - return logCommand({ - cancel, - cancelled, - sockPath, - follow, - ping, - }); + await logCommand({ follow, ping }); }); program @@ -156,10 +145,7 @@ export const main = async rawArgs => { .description('prints ok if the daemon is responsive') .action(async _cmd => { const { ping } = await import('./ping.js'); - return ping({ - cancelled, - sockPath, - }); + await ping(); }); program @@ -178,9 +164,6 @@ export const main = async rawArgs => { const { name: bundleName, as: partyNames } = cmd.opts(); const { bundleCommand } = await import('./bundle.js'); return bundleCommand({ - cancel, - cancelled, - sockPath, applicationPath, bundleName, partyNames, @@ -204,9 +187,6 @@ export const main = async rawArgs => { const { name, as: partyNames } = cmd.opts(); const { store } = await import('./store.js'); return store({ - cancel, - cancelled, - sockPath, storablePath, name, partyNames, @@ -225,13 +205,7 @@ export const main = async rawArgs => { .action(async (petNames, cmd) => { const { as: partyNames } = cmd.opts(); const { spawn } = await import('./spawn.js'); - return spawn({ - cancel, - cancelled, - sockPath, - petNames, - partyNames, - }); + return spawn({ petNames, partyNames }); }); program @@ -246,13 +220,7 @@ export const main = async rawArgs => { .action(async (name, cmd) => { const { as: partyNames } = cmd.opts(); const { show } = await import('./show.js'); - return show({ - cancel, - cancelled, - sockPath, - name, - partyNames, - }); + return show({ name, partyNames }); }); program @@ -269,13 +237,7 @@ export const main = async rawArgs => { .action(async (name, cmd) => { const { as: partyNames } = cmd.opts(); const { followCommand } = await import('./follow.js'); - return followCommand({ - cancel, - cancelled, - sockPath, - name, - partyNames, - }); + return followCommand({ name, partyNames }); }); program @@ -290,13 +252,7 @@ export const main = async rawArgs => { .action(async (name, cmd) => { const { as: partyNames } = cmd.opts(); const { cat } = await import('./cat.js'); - return cat({ - cancel, - cancelled, - sockPath, - name, - partyNames, - }); + return cat({ name, partyNames }); }); program @@ -311,12 +267,7 @@ export const main = async rawArgs => { .action(async cmd => { const { as: partyNames } = cmd.opts(); const { list } = await import('./list.js'); - return list({ - cancel, - cancelled, - sockPath, - partyNames, - }); + return list({ partyNames }); }); program @@ -331,13 +282,7 @@ export const main = async rawArgs => { .action(async (petNames, cmd) => { const { as: partyNames } = cmd.opts(); const { remove } = await import('./remove.js'); - return remove({ - cancel, - cancelled, - sockPath, - petNames, - partyNames, - }); + return remove({ petNames, partyNames }); }); program @@ -352,14 +297,7 @@ export const main = async rawArgs => { .action(async (fromName, toName, cmd) => { const { as: partyNames } = cmd.opts(); const { rename } = await import('./rename.js'); - return rename({ - cancel, - cancelled, - sockPath, - fromName, - toName, - partyNames, - }); + return rename({ fromName, toName, partyNames }); }); program @@ -374,13 +312,7 @@ export const main = async rawArgs => { .action(async (name, cmd) => { const { as: partyNames } = cmd.opts(); const { mkhost } = await import('./mkhost.js'); - return mkhost({ - cancel, - cancelled, - sockPath, - name, - partyNames, - }); + return mkhost({ name, partyNames }); }); program @@ -395,7 +327,7 @@ export const main = async rawArgs => { .action(async (name, cmd) => { const { as: partyNames } = cmd.opts(); const { mkguest } = await import('./mkguest.js'); - return mkguest({ cancel, cancelled, sockPath, name, partyNames }); + return mkguest({ name, partyNames }); }); program @@ -411,13 +343,7 @@ export const main = async rawArgs => { .action(async cmd => { const { as: partyNames, follow } = cmd.opts(); const { inbox } = await import('./inbox.js'); - return inbox({ - cancel, - cancelled, - sockPath, - follow, - partyNames, - }); + return inbox({ follow, partyNames }); }); program @@ -436,14 +362,7 @@ export const main = async rawArgs => { .action(async (description, cmd) => { const { name: resultName, as: partyNames } = cmd.opts(); const { request } = await import('./request.js'); - return request({ - cancel, - cancelled, - sockPath, - description, - resultName, - partyNames, - }); + return request({ description, resultName, partyNames }); }); program @@ -459,9 +378,6 @@ export const main = async rawArgs => { const { as: partyNames } = cmd.opts(); const { resolveCommand } = await import('./resolve.js'); return resolveCommand({ - cancel, - cancelled, - sockPath, requestNumberText, resolutionName, partyNames, @@ -481,9 +397,6 @@ export const main = async rawArgs => { const { as: partyNames } = cmd.opts(); const { rejectCommand } = await import('./reject.js'); return rejectCommand({ - cancel, - cancelled, - sockPath, requestNumberText, message, partyNames, @@ -515,9 +428,6 @@ export const main = async rawArgs => { } = cmd.opts(); const { evalCommand } = await import('./eval.js'); return evalCommand({ - cancel, - cancelled, - sockPath, source, names, resultName, @@ -557,9 +467,6 @@ export const main = async rawArgs => { } = cmd.opts(); const { makeCommand } = await import('./make.js'); return makeCommand({ - cancel, - cancelled, - sockPath, filePath, importPath, resultName, @@ -592,9 +499,6 @@ export const main = async rawArgs => { } = cmd.opts(); const { open } = await import('./open.js'); return open({ - cancel, - cancelled, - sockPath, webPageName, programPath, bundleName, @@ -629,9 +533,6 @@ export const main = async rawArgs => { } = cmd.opts(); const { run } = await import('./run.js'); return run({ - cancel, - cancelled, - sockPath, filePath, args, bundleName, @@ -646,7 +547,6 @@ export const main = async rawArgs => { try { await program.parseAsync(rawArgs, { from: 'user' }); - cancel(Error('normal termination')); } catch (e) { if (e && e.name === 'CommanderError') { return e.exitCode; diff --git a/packages/cli/src/eval.js b/packages/cli/src/eval.js index f403265233..d00c705928 100644 --- a/packages/cli/src/eval.js +++ b/packages/cli/src/eval.js @@ -1,24 +1,16 @@ +/* global process */ +import os from 'os'; import { E } from '@endo/far'; -import { provideEndoClient } from './client.js'; +import { withEndoParty } from './context.js'; export const evalCommand = async ({ - cancel, - cancelled, - sockPath, source, names, resultName, workerName, partyNames, -}) => { - const { getBootstrap } = await provideEndoClient('cli', sockPath, cancelled); - try { - const bootstrap = getBootstrap(); - let party = E(bootstrap).host(); - for (const partyName of partyNames) { - party = E(party).provide(partyName); - } - +}) => + withEndoParty(partyNames, { os, process }, async ({ party }) => { const pairs = names.map(name => { /** @type {Array} */ const pair = name.split(':'); @@ -45,8 +37,4 @@ export const evalCommand = async ({ resultName, ); console.log(result); - } catch (error) { - console.error(error); - cancel(error); - } -}; + }); diff --git a/packages/cli/src/follow.js b/packages/cli/src/follow.js index 6a601a19dc..7c5a16019c 100644 --- a/packages/cli/src/follow.js +++ b/packages/cli/src/follow.js @@ -1,27 +1,14 @@ +/* global process */ + +import os from 'os'; import { E } from '@endo/far'; import { makeRefIterator } from '@endo/daemon'; -import { provideEndoClient } from './client.js'; +import { withEndoParty } from './context.js'; -export const followCommand = async ({ - cancel, - cancelled, - sockPath, - name, - partyNames, -}) => { - const { getBootstrap } = await provideEndoClient('cli', sockPath, cancelled); - try { - const bootstrap = getBootstrap(); - let party = E(bootstrap).host(); - for (const partyName of partyNames) { - party = E(party).provide(partyName); - } +export const followCommand = async ({ name, partyNames }) => + withEndoParty(partyNames, { os, process }, async ({ party }) => { const iterable = await E(party).provide(name); for await (const iterand of makeRefIterator(iterable)) { console.log(iterand); } - } catch (error) { - console.error(error); - cancel(error); - } -}; + }); diff --git a/packages/cli/src/inbox.js b/packages/cli/src/inbox.js index b30ee811fd..86917b6ce0 100644 --- a/packages/cli/src/inbox.js +++ b/packages/cli/src/inbox.js @@ -1,21 +1,18 @@ +/* global process */ + +import os from 'os'; import { E } from '@endo/far'; import { makeRefIterator } from '@endo/daemon'; -import { provideEndoClient } from './client.js'; +import { withEndoParty } from './context.js'; -export const followCommand = async ({ +export const inbox = async ({ cancel, cancelled, sockPath, follow, partyNames, -}) => { - const { getBootstrap } = await provideEndoClient('cli', sockPath, cancelled); - try { - const bootstrap = getBootstrap(); - let party = E(bootstrap).host(); - for (const partyName of partyNames) { - party = E(party).provide(partyName); - } +}) => + withEndoParty(partyNames, { os, process }, async ({ party }) => { const messages = follow ? makeRefIterator(E(party).followMessages()) : await E(party).listMessages(); @@ -38,8 +35,4 @@ export const followCommand = async ({ ); } } - } catch (error) { - console.error(error); - cancel(error); - } -}; + }); diff --git a/packages/cli/src/list.js b/packages/cli/src/list.js index aba219bf4a..1172a19a81 100644 --- a/packages/cli/src/list.js +++ b/packages/cli/src/list.js @@ -1,20 +1,12 @@ +/* global process */ +import os from 'os'; import { E } from '@endo/far'; -import { provideEndoClient } from './client.js'; +import { withEndoParty } from './context.js'; -export const list = async ({ cancel, cancelled, sockPath, partyNames }) => { - const { getBootstrap } = await provideEndoClient('cli', sockPath, cancelled); - try { - const bootstrap = getBootstrap(); - let party = E(bootstrap).host(); - for (const partyName of partyNames) { - party = E(party).provide(partyName); - } +export const list = async ({ partyNames }) => + withEndoParty(partyNames, { os, process }, async ({ party }) => { const petNames = await E(party).list(); for await (const petName of petNames) { console.log(petName); } - } catch (error) { - console.error(error); - cancel(error); - } -}; + }); diff --git a/packages/cli/src/make.js b/packages/cli/src/make.js index fb86174d0e..06c7c52f48 100644 --- a/packages/cli/src/make.js +++ b/packages/cli/src/make.js @@ -1,12 +1,12 @@ /* global process */ +import os from 'os'; import path from 'path'; import bundleSource from '@endo/bundle-source'; import { makeReaderRef } from '@endo/daemon'; import { E } from '@endo/far'; - -import { provideEndoClient } from './client.js'; +import { withEndoParty } from './context.js'; import { randomHex16 } from './random.js'; const textEncoder = new TextEncoder(); @@ -58,14 +58,7 @@ export const makeCommand = async ({ bundleReaderRef = makeReaderRef([bundleBytes]); } - const { getBootstrap } = await provideEndoClient('cli', sockPath, cancelled); - try { - const bootstrap = getBootstrap(); - let party = E(bootstrap).host(); - for (const partyName of partyNames) { - party = E(party).provide(partyName); - } - + await withEndoParty(partyNames, { os, process }, async ({ party }) => { // Prepare a bundle, with the given name. if (bundleReaderRef !== undefined) { await E(party).store(bundleReaderRef, bundleName); @@ -91,8 +84,5 @@ export const makeCommand = async ({ if (temporaryBundleName) { await E(party).remove(temporaryBundleName); } - } catch (error) { - console.error(error); - cancel(error); - } + }); }; diff --git a/packages/cli/src/mkguest.js b/packages/cli/src/mkguest.js index c1552602a7..727caf92e3 100644 --- a/packages/cli/src/mkguest.js +++ b/packages/cli/src/mkguest.js @@ -1,5 +1,7 @@ +/* global process */ +import os from 'os'; import { E } from '@endo/far'; -import { provideEndoClient } from './client.js'; +import { withEndoParty } from './context.js'; export const mkguest = async ({ cancel, @@ -7,18 +9,8 @@ export const mkguest = async ({ sockPath, name, partyNames, -}) => { - const { getBootstrap } = await provideEndoClient('cli', sockPath, cancelled); - try { - const bootstrap = getBootstrap(); - let party = E(bootstrap).host(); - for (const partyName of partyNames) { - party = E(party).provide(partyName); - } +}) => + withEndoParty(partyNames, { os, process }, async ({ party }) => { const newGuest = await E(party).provideGuest(name); console.log(newGuest); - } catch (error) { - console.error(error); - cancel(error); - } -}; + }); diff --git a/packages/cli/src/mkhost.js b/packages/cli/src/mkhost.js index d043651158..9636596c65 100644 --- a/packages/cli/src/mkhost.js +++ b/packages/cli/src/mkhost.js @@ -1,5 +1,7 @@ +/* global process */ +import os from 'os'; import { E } from '@endo/far'; -import { provideEndoClient } from './client.js'; +import { withEndoParty } from './context.js'; export const mkhost = async ({ cancel, @@ -7,18 +9,8 @@ export const mkhost = async ({ sockPath, name, partyNames, -}) => { - const { getBootstrap } = await provideEndoClient('cli', sockPath, cancelled); - try { - const bootstrap = getBootstrap(); - let party = E(bootstrap).host(); - for (const partyName of partyNames) { - party = E(party).provide(partyName); - } +}) => + withEndoParty(partyNames, { os, process }, async ({ party }) => { const newHost = await E(party).provideHost(name); console.log(newHost); - } catch (error) { - console.error(error); - cancel(error); - } -}; + }); diff --git a/packages/cli/src/open.js b/packages/cli/src/open.js index b581c91edd..5bfc8a6b80 100644 --- a/packages/cli/src/open.js +++ b/packages/cli/src/open.js @@ -1,19 +1,18 @@ /* global process */ +import os from 'os'; + import openWebPage from 'open'; import { E } from '@endo/far'; import { makeReaderRef } from '@endo/daemon'; import bundleSource from '@endo/bundle-source'; -import { provideEndoClient } from './client.js'; +import { withEndoParty } from './context.js'; import { randomHex16 } from './random.js'; const textEncoder = new TextEncoder(); export const open = async ({ - cancel, - cancelled, - sockPath, bundleName, partyNames, powersName, @@ -38,15 +37,7 @@ export const open = async ({ bundleReaderRef = makeReaderRef([bundleBytes]); } - const { getBootstrap } = await provideEndoClient('cli', sockPath, cancelled); - - try { - const bootstrap = getBootstrap(); - let party = E(bootstrap).host(); - for (const partyName of partyNames) { - party = E(party).provide(partyName); - } - + await withEndoParty(partyNames, { os, process }, async ({ party }) => { // Prepare a bundle, with the given name. if (bundleReaderRef !== undefined) { await E(party).store(bundleReaderRef, bundleName); @@ -69,8 +60,5 @@ export const open = async ({ if (temporaryBundleName) { await E(party).remove(temporaryBundleName); } - } catch (error) { - console.error(error); - cancel(error); - } + }); }; diff --git a/packages/cli/src/ping.js b/packages/cli/src/ping.js index 20d59ef28a..c7ef87a0e3 100644 --- a/packages/cli/src/ping.js +++ b/packages/cli/src/ping.js @@ -1,15 +1,28 @@ /* global process */ - +import os from 'os'; import { E } from '@endo/far'; +import { whereEndoSock } from '@endo/where'; import { makeEndoClient } from '@endo/daemon'; +import { withInterrupt } from './context.js'; + +export const ping = () => + withInterrupt(async ({ cancel, cancelled }) => { + const { username, homedir } = os.userInfo(); + const temp = os.tmpdir(); + const info = { + user: username, + home: homedir, + temp, + }; + + const sockPath = whereEndoSock(process.platform, process.env, info); -export const ping = async ({ cancelled, sockPath }) => { - const { getBootstrap } = await makeEndoClient( - 'health-checker', - sockPath, - cancelled, - ); - const bootstrap = getBootstrap(); - await E(bootstrap).ping(); - process.stderr.write('ok\n'); -}; + const { getBootstrap } = await makeEndoClient( + 'health-checker', + sockPath, + cancelled, + ); + const bootstrap = getBootstrap(); + await E(bootstrap).ping(); + process.stderr.write('ok\n'); + }); diff --git a/packages/cli/src/reject.js b/packages/cli/src/reject.js index a0c8bc437e..3cad299696 100644 --- a/packages/cli/src/reject.js +++ b/packages/cli/src/reject.js @@ -1,5 +1,7 @@ +/* global process */ +import os from 'os'; import { E } from '@endo/far'; -import { provideEndoClient } from './client.js'; +import { withEndoParty } from './context.js'; export const rejectCommand = async ({ cancel, @@ -8,19 +10,9 @@ export const rejectCommand = async ({ requestNumberText, message, partyNames, -}) => { - // TODO less bad number parsing. - const requestNumber = Number(requestNumberText); - const { getBootstrap } = await provideEndoClient('cli', sockPath, cancelled); - try { - const bootstrap = getBootstrap(); - let party = E(bootstrap).host(); - for (const partyName of partyNames) { - party = E(party).provide(partyName); - } +}) => + withEndoParty(partyNames, { os, process }, async ({ party }) => { + // TODO less bad number parsing. + const requestNumber = Number(requestNumberText); await E(party).reject(requestNumber, message); - } catch (error) { - console.error(error); - cancel(error); - } -}; + }); diff --git a/packages/cli/src/remove.js b/packages/cli/src/remove.js index 2823412f8b..6c07c1c356 100644 --- a/packages/cli/src/remove.js +++ b/packages/cli/src/remove.js @@ -1,5 +1,7 @@ +/* global process */ +import os from 'os'; import { E } from '@endo/far'; -import { provideEndoClient } from './client.js'; +import { withEndoParty } from './context.js'; export const remove = async ({ cancel, @@ -7,17 +9,7 @@ export const remove = async ({ sockPath, petNames, partyNames, -}) => { - const { getBootstrap } = await provideEndoClient('cli', sockPath, cancelled); - try { - const bootstrap = getBootstrap(); - let party = E(bootstrap).host(); - for (const partyName of partyNames) { - party = E(party).provide(partyName); - } - await Promise.all(petNames.map(petName => E(party).remove(petName))); - } catch (error) { - console.error(error); - cancel(error); - } -}; +}) => + withEndoParty(partyNames, { os, process }, async ({ party }) => + Promise.all(petNames.map(petName => E(party).remove(petName))), + ); diff --git a/packages/cli/src/rename.js b/packages/cli/src/rename.js index 260019afc4..02107b3323 100644 --- a/packages/cli/src/rename.js +++ b/packages/cli/src/rename.js @@ -1,24 +1,9 @@ +/* global process */ +import os from 'os'; import { E } from '@endo/far'; -import { provideEndoClient } from './client.js'; +import { withEndoParty } from './context.js'; -export const rename = async ({ - cancel, - cancelled, - sockPath, - fromName, - toName, - partyNames, -}) => { - const { getBootstrap } = await provideEndoClient('cli', sockPath, cancelled); - try { - const bootstrap = getBootstrap(); - let party = E(bootstrap).host(); - for (const partyName of partyNames) { - party = E(party).provide(partyName); - } +export const rename = async ({ fromName, toName, partyNames }) => + withEndoParty(partyNames, { os, process }, async ({ party }) => { await E(party).rename(fromName, toName); - } catch (error) { - console.error(error); - cancel(error); - } -}; + }); diff --git a/packages/cli/src/request.js b/packages/cli/src/request.js index 049d7bba8f..c15cae8d02 100644 --- a/packages/cli/src/request.js +++ b/packages/cli/src/request.js @@ -1,7 +1,7 @@ /* global process */ - +import os from 'os'; import { E } from '@endo/far'; -import { provideEndoClient } from './client.js'; +import { withEndoParty } from './context.js'; export const request = async ({ cancel, @@ -16,17 +16,12 @@ export const request = async ({ process.exitCode = 1; return; } - const { getBootstrap } = await provideEndoClient('cli', sockPath, cancelled); - try { - const bootstrap = getBootstrap(); - let party = E(bootstrap).host(); - for (const partyName of partyNames) { - party = E(party).provideGuest(partyName); - } - const result = await E(party).request(description, resultName); + // The last party name must be a guest and will be created if not already + // present. + const lastPartyName = partyNames.pop(); + await withEndoParty(partyNames, { os, process }, async ({ party }) => { + const guest = E(party).provideGuest(lastPartyName); + const result = await E(guest).request(description, resultName); console.log(result); - } catch (error) { - console.error(error); - cancel(error); - } + }); }; diff --git a/packages/cli/src/resolve.js b/packages/cli/src/resolve.js index ba8ae61e9e..5bdb1a5d7f 100644 --- a/packages/cli/src/resolve.js +++ b/packages/cli/src/resolve.js @@ -1,23 +1,15 @@ +/* global process */ +import os from 'os'; import { E } from '@endo/far'; -import { provideEndoClient } from './client.js'; +import { withEndoParty } from './context.js'; export const resolveCommand = async ({ requestNumberText, resolutionName, partyNames, -}) => { - // TODO less bad number parsing. - const requestNumber = Number(requestNumberText); - const { getBootstrap } = await provideEndoClient('cli', sockPath, cancelled); - try { - const bootstrap = getBootstrap(); - let party = E(bootstrap).host(); - for (const partyName of partyNames) { - party = E(party).provide(partyName); - } +}) => + withEndoParty(partyNames, { os, process }, async ({ party }) => { + // TODO less bad number parsing. + const requestNumber = Number(requestNumberText); await E(party).resolve(requestNumber, resolutionName); - } catch (error) { - console.error(error); - cancel(error); - } -}; + }); diff --git a/packages/cli/src/run.js b/packages/cli/src/run.js index 1c33ab6c0e..12886b7c13 100644 --- a/packages/cli/src/run.js +++ b/packages/cli/src/run.js @@ -1,9 +1,10 @@ /* global process */ +import url from 'url'; +import os from 'os'; import { E, Far } from '@endo/far'; import bundleSource from '@endo/bundle-source'; -import url from 'url'; -import { provideEndoClient } from './client.js'; +import { withEndoParty } from './context.js'; const endowments = harden({ assert, @@ -16,9 +17,6 @@ const endowments = harden({ }); export const run = async ({ - cancel, - cancelled, - sockPath, filePath, args, bundleName, @@ -36,46 +34,23 @@ export const run = async ({ return; } - const { getBootstrap } = await provideEndoClient('cli', sockPath, cancelled); - try { - const bootstrap = getBootstrap(); - let party = E(bootstrap).host(); - for (const partyName of partyNames) { - party = E(party).provide(partyName); - } - - let powersP; - if (powersName === 'NONE') { - powersP = E(bootstrap).leastAuthority(); - } else if (powersName === 'HOST') { - powersP = party; - } else if (powersName === 'ENDO') { - powersP = bootstrap; - } else { - powersP = E(party).provideGuest(powersName); - } - - if (importPath !== undefined) { - if (bundleName !== undefined) { - console.error('Must specify either --bundle or --UNSAFE, not both'); - process.exitCode = 1; - return; - } - if (filePath !== undefined) { - args.unshift(filePath); + await withEndoParty( + partyNames, + { os, process }, + async ({ bootstrap, party }) => { + let powersP; + if (powersName === 'NONE') { + powersP = E(bootstrap).leastAuthority(); + } else if (powersName === 'HOST') { + powersP = party; + } else if (powersName === 'ENDO') { + powersP = bootstrap; + } else { + powersP = E(party).provideGuest(powersName); } - const importUrl = url.pathToFileURL(importPath); - const namespace = await import(importUrl); - const result = await namespace.main(powersP, ...args); - if (result !== undefined) { - console.log(result); - } - } else { - /** @type {any} */ - let bundle; - if (bundleName !== undefined) { - if (importPath !== undefined) { + if (importPath !== undefined) { + if (bundleName !== undefined) { console.error('Must specify either --bundle or --UNSAFE, not both'); process.exitCode = 1; return; @@ -84,26 +59,43 @@ export const run = async ({ args.unshift(filePath); } - const readableP = E(party).provide(bundleName); - const bundleText = await E(readableP).text(); - bundle = JSON.parse(bundleText); + const importUrl = url.pathToFileURL(importPath); + const namespace = await import(importUrl); + const result = await namespace.main(powersP, ...args); + if (result !== undefined) { + console.log(result); + } } else { - bundle = await bundleSource(filePath); - } + /** @type {any} */ + let bundle; + if (bundleName !== undefined) { + if (importPath !== undefined) { + console.error('Must specify either --bundle or --UNSAFE, not both'); + process.exitCode = 1; + return; + } + if (filePath !== undefined) { + args.unshift(filePath); + } + + const readableP = E(party).provide(bundleName); + const bundleText = await E(readableP).text(); + bundle = JSON.parse(bundleText); + } else { + bundle = await bundleSource(filePath); + } - // We defer importing the import-bundle machinery to this in order to - // avoid an up-front cost for workers that never use importBundle. - const { importBundle } = await import('@endo/import-bundle'); - const namespace = await importBundle(bundle, { - endowments, - }); - const result = await namespace.main(powersP, ...args); - if (result !== undefined) { - console.log(result); + // We defer importing the import-bundle machinery to this in order to + // avoid an up-front cost for workers that never use importBundle. + const { importBundle } = await import('@endo/import-bundle'); + const namespace = await importBundle(bundle, { + endowments, + }); + const result = await namespace.main(powersP, ...args); + if (result !== undefined) { + console.log(result); + } } - } - } catch (error) { - console.error(error); - cancel(error); - } + }, + ); }; diff --git a/packages/cli/src/show.js b/packages/cli/src/show.js index d23fad34d1..8863732b3d 100644 --- a/packages/cli/src/show.js +++ b/packages/cli/src/show.js @@ -1,24 +1,10 @@ +/* global process */ +import os from 'os'; import { E } from '@endo/far'; -import { provideEndoClient } from './client.js'; +import { withEndoParty } from './context.js'; -export const show = async ({ - cancel, - cancelled, - sockPath, - name, - partyNames, -}) => { - const { getBootstrap } = await provideEndoClient('cli', sockPath, cancelled); - try { - const bootstrap = getBootstrap(); - let party = E(bootstrap).host(); - for (const partyName of partyNames) { - party = E(party).provide(partyName); - } +export const show = async ({ cancel, cancelled, sockPath, name, partyNames }) => + withEndoParty(partyNames, { os, process }, async ({ party }) => { const pet = await E(party).provide(name); console.log(pet); - } catch (error) { - console.error(error); - cancel(error); - } -}; + }); diff --git a/packages/cli/src/spawn.js b/packages/cli/src/spawn.js index 8277ae0abb..2420dc1501 100644 --- a/packages/cli/src/spawn.js +++ b/packages/cli/src/spawn.js @@ -1,7 +1,8 @@ /* eslint-disable no-await-in-loop */ - +/* global process */ +import os from 'os'; import { E } from '@endo/far'; -import { provideEndoClient } from './client.js'; +import { withEndoParty } from './context.js'; export const spawn = async ({ cancel, @@ -9,19 +10,7 @@ export const spawn = async ({ sockPath, petNames, partyNames, -}) => { - const { getBootstrap } = await provideEndoClient('cli', sockPath, cancelled); - try { - const bootstrap = getBootstrap(); - let party = E(bootstrap).host(); - for (const partyName of partyNames) { - party = E(party).provide(partyName); - } - for (const petName of petNames) { - await E(party).makeWorker(petName); - } - } catch (error) { - console.error(error); - cancel(error); - } -}; +}) => + withEndoParty(partyNames, { os, process }, async ({ party }) => + Promise.all(petNames.map(petName => E(party).makeWorker(petName))), + ); diff --git a/packages/cli/src/store.js b/packages/cli/src/store.js index 98adb86332..71881bbd6e 100644 --- a/packages/cli/src/store.js +++ b/packages/cli/src/store.js @@ -1,32 +1,18 @@ +/* global process */ import fs from 'fs'; +import os from 'os'; import { makeNodeReader } from '@endo/stream-node'; import { makeReaderRef } from '@endo/daemon'; import { E } from '@endo/far'; -import { provideEndoClient } from './client.js'; -export const store = async ({ - cancel, - cancelled, - sockPath, - storablePath, - name, - partyNames, -}) => { +import { withEndoParty } from './context.js'; + +export const store = async ({ storablePath, name, partyNames }) => { const nodeReadStream = fs.createReadStream(storablePath); const reader = makeNodeReader(nodeReadStream); const readerRef = makeReaderRef(reader); - - const { getBootstrap } = await provideEndoClient('cli', sockPath, cancelled); - try { - const bootstrap = getBootstrap(); - let party = E(bootstrap).host(); - for (const partyName of partyNames) { - party = E(party).provide(partyName); - } + await withEndoParty(partyNames, { os, process }, async ({ party }) => { await E(party).store(readerRef, name); - } catch (error) { - console.error(error); - cancel(error); - } + }); }; From 547ce4d4d58a4a20d83445016767fb9f4e7d4bba Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Tue, 15 Aug 2023 15:41:52 -0700 Subject: [PATCH 089/234] feat(cli): Add receive message command --- packages/cli/src/endo.js | 15 +++++++++++++++ packages/cli/src/message-parse.js | 28 ++++++++++++++++++++++++++++ packages/cli/src/receive.js | 21 +++++++++++++++++++++ 3 files changed, 64 insertions(+) create mode 100644 packages/cli/src/message-parse.js create mode 100644 packages/cli/src/receive.js diff --git a/packages/cli/src/endo.js b/packages/cli/src/endo.js index 5edf41963f..2079451a0d 100644 --- a/packages/cli/src/endo.js +++ b/packages/cli/src/endo.js @@ -403,6 +403,21 @@ export const main = async rawArgs => { }); }); + program + .command('receive ') + .description('delivers a message to the underlying host') + .option( + '-a,--as ', + 'Pose as named party (as named by current party)', + collect, + [], + ) + .action(async (message, cmd) => { + const { as: partyNames } = cmd.opts(); + const { receive } = await import('./receive.js'); + return receive({ message, partyNames }); + }); + program .command('eval [names...]') .description('evaluates a string with the endowed values in scope') diff --git a/packages/cli/src/message-parse.js b/packages/cli/src/message-parse.js new file mode 100644 index 0000000000..95124de049 --- /dev/null +++ b/packages/cli/src/message-parse.js @@ -0,0 +1,28 @@ +const pattern = /@([a-z][a-z0-9-]{0,127})(?::([a-z][a-z0-9-]{0,127}))?/g; + +export const parseMessage = message => { + const strings = []; + const petNames = []; + const edgeNames = []; + let start = 0; + message.replace(pattern, (match, edgeName, petName, stop) => { + strings.push(message.slice(start, stop)); + start = stop + match.length; + + edgeNames.push(edgeName); + petNames.push(petName ?? edgeName); + return ''; + }); + strings.push(message.slice(start)); + return { + strings, + petNames, + edgeNames, + }; +}; + +// console.log(parseMessage('before @pet-name:edge-name and @other-pet-name to the end')); +// console.log(parseMessage('@pet-name')); +// console.log(parseMessage('@pet-name:edge-name')); +// console.log(parseMessage('@pet-name:edge-name trailer')); +// console.log(parseMessage('header @pet-name:edge-name trailer')); diff --git a/packages/cli/src/receive.js b/packages/cli/src/receive.js new file mode 100644 index 0000000000..083011eb71 --- /dev/null +++ b/packages/cli/src/receive.js @@ -0,0 +1,21 @@ +/* global process */ +import os from 'os'; +import { E } from '@endo/far'; +import { withEndoParty } from './context.js'; +import { parseMessage } from './message-parse.js'; + +export const receive = async ({ message, partyNames }) => { + const { strings, edgeNames, petNames } = parseMessage(message); + if (partyNames.length === 0) { + console.error('Specify the name of a guest with -a or --as '); + process.exitCode = 1; + return; + } + // The last party name must be a guest and will be created if not already + // present. + const lastPartyName = partyNames.pop(); + await withEndoParty(partyNames, { os, process }, async ({ party }) => { + const guest = E(party).provideGuest(lastPartyName); + await E(guest).receive(strings, edgeNames, petNames); + }); +}; From c423281eae174ab7bbb3ba4d6b1a66d0f2479a21 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Tue, 15 Aug 2023 15:44:30 -0700 Subject: [PATCH 090/234] feat(cli): Inbox support for package type messages --- packages/cli/src/inbox.js | 9 +++++++++ packages/cli/src/message-format.js | 20 ++++++++++++++++++++ 2 files changed, 29 insertions(+) create mode 100644 packages/cli/src/message-format.js diff --git a/packages/cli/src/inbox.js b/packages/cli/src/inbox.js index 86917b6ce0..7f9cfe0e8e 100644 --- a/packages/cli/src/inbox.js +++ b/packages/cli/src/inbox.js @@ -4,6 +4,7 @@ import os from 'os'; import { E } from '@endo/far'; import { makeRefIterator } from '@endo/daemon'; import { withEndoParty } from './context.js'; +import { formatMessage } from './message-format.js'; export const inbox = async ({ cancel, @@ -25,6 +26,14 @@ export const inbox = async ({ what, )} at ${JSON.stringify(when)}`, ); + } else if (message.type === 'package') { + const { strings, names: edgeNames } = message; + console.log( + `${number}. ${JSON.stringify(who)} sent ${formatMessage( + strings, + edgeNames, + )} at ${JSON.stringify(when)}`, + ); } else { console.log( `${number}. ${JSON.stringify( diff --git a/packages/cli/src/message-format.js b/packages/cli/src/message-format.js new file mode 100644 index 0000000000..9f8ddd07aa --- /dev/null +++ b/packages/cli/src/message-format.js @@ -0,0 +1,20 @@ +/** + * @param {Array} strings + * @param {Array} edgeNames + */ +export const formatMessage = (strings, edgeNames) => { + let message = ''; + let index = 0; + for ( + index = 0; + index < Math.min(strings.length, edgeNames.length); + index += 1 + ) { + message += strings[0].replace(/@/g, '\\@'); + message += `@${edgeNames[index]}`; + } + if (strings.length > edgeNames.length) { + message += strings[index]; + } + return JSON.stringify(message); +}; From ea55ff6a2660942fec048edc102f4faa879fced4 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Tue, 15 Aug 2023 17:10:20 -0700 Subject: [PATCH 091/234] feat(daemon): Release CLI if Endo start fails --- packages/daemon/index.js | 24 ++++++++++++++++++----- packages/daemon/src/daemon-node-powers.js | 7 +++++++ packages/daemon/src/daemon.js | 5 ++++- packages/daemon/src/types.d.ts | 1 + 4 files changed, 31 insertions(+), 6 deletions(-) diff --git a/packages/daemon/index.js b/packages/daemon/index.js index f9d16f9ce6..6e9a3f2e8b 100644 --- a/packages/daemon/index.js +++ b/packages/daemon/index.js @@ -114,13 +114,27 @@ export const start = async (locator = defaultLocator) => { ), ); }); - child.on('message', _message => { - // This message corresponds to process.send({ type: 'ready' }) in - // src/daemon-node-powers.js and indicates the daemon is ready to receive - // clients. + child.on('message', message => { child.disconnect(); child.unref(); - resolve(undefined); + if ( + typeof message === 'object' && + message !== null && + 'type' in message + ) { + if (message.type === 'ready') { + // This message corresponds to process.send({ type: 'ready' }) in + // src/daemon-node-powers.js and indicates the daemon is ready to receive + // clients. + resolve(undefined); + } else if ( + message.type === 'error' && + 'message' in message && + typeof message.message === 'string' + ) { + reject(new Error(message.message)); + } + } }); }); }; diff --git a/packages/daemon/src/daemon-node-powers.js b/packages/daemon/src/daemon-node-powers.js index 3913d76140..a86441a7f2 100644 --- a/packages/daemon/src/daemon-node-powers.js +++ b/packages/daemon/src/daemon-node-powers.js @@ -250,6 +250,12 @@ export const makePowers = ({ } }; + const reportErrorToParent = message => { + if (process.send) { + process.send({ type: 'error', message }); + } + }; + /** * @param {string} path */ @@ -389,6 +395,7 @@ export const makePowers = ({ randomHex512, listenOnPath, informParentWhenReady, + reportErrorToParent, makeFileReader, makeFileWriter, writeFileText, diff --git a/packages/daemon/src/daemon.js b/packages/daemon/src/daemon.js index 8ab867468b..9d03d4dda7 100644 --- a/packages/daemon/src/daemon.js +++ b/packages/daemon/src/daemon.js @@ -1561,7 +1561,10 @@ export const main = async (powers, locator, pid, cancel, cancelled) => { const connectionsP = powers.listenOnPath(locator.sockPath, cancelled); - await Promise.all([connectionsP, httpReadyP]); + await Promise.all([connectionsP, httpReadyP]).catch(error => { + powers.reportErrorToParent(error.message); + throw error; + }); const assignedHttpPort = await httpReadyP; console.log( diff --git a/packages/daemon/src/types.d.ts b/packages/daemon/src/types.d.ts index a5d362457a..3b0bcb2675 100644 --- a/packages/daemon/src/types.d.ts +++ b/packages/daemon/src/types.d.ts @@ -59,6 +59,7 @@ export type DaemonicPowers = { cancelled: Promise; }) => void; informParentWhenReady: () => void; + reportErrorToParent: (message: string) => void; makeFileReader: (path: string) => Reader; makeFileWriter: (path: string) => Writer; readFileText: (path: string) => Promise; From 3d8ee2c996e7a3b987df9e9073acfe697286120d Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Tue, 15 Aug 2023 17:25:44 -0700 Subject: [PATCH 092/234] feat(cli): Add dismiss message command --- packages/cli/src/dismiss.js | 18 ++++++++++++++++++ packages/cli/src/endo.js | 18 ++++++++++++++++++ 2 files changed, 36 insertions(+) create mode 100644 packages/cli/src/dismiss.js diff --git a/packages/cli/src/dismiss.js b/packages/cli/src/dismiss.js new file mode 100644 index 0000000000..2bcf6b22a1 --- /dev/null +++ b/packages/cli/src/dismiss.js @@ -0,0 +1,18 @@ +/* global process */ +import os from 'os'; +import { E } from '@endo/far'; +import { withEndoParty } from './context.js'; + +export const dismissCommand = async ({ + cancel, + cancelled, + sockPath, + messageNumberText, + message, + partyNames, +}) => + withEndoParty(partyNames, { os, process }, async ({ party }) => { + // TODO less bad number parsing. + const messageNumber = Number(messageNumberText); + await E(party).dismiss(messageNumber); + }); diff --git a/packages/cli/src/endo.js b/packages/cli/src/endo.js index 2079451a0d..ad526cedf2 100644 --- a/packages/cli/src/endo.js +++ b/packages/cli/src/endo.js @@ -418,6 +418,24 @@ export const main = async rawArgs => { return receive({ message, partyNames }); }); + program + .command('dismiss ') + .description('dismisses a message and drops any references it carried') + .option( + '-a,--as ', + 'Pose as named party (as named by current party)', + collect, + [], + ) + .action(async (messageNumberText, cmd) => { + const { as: partyNames } = cmd.opts(); + const { dismissCommand } = await import('./dismiss.js'); + return dismissCommand({ + messageNumberText, + partyNames, + }); + }); + program .command('eval [names...]') .description('evaluates a string with the endowed values in scope') From b0561642828b4ec382b106f2116a8bc4d8c9a07d Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Tue, 15 Aug 2023 23:04:06 -0700 Subject: [PATCH 093/234] feat(cli): Add adopt command --- packages/cli/src/adopt.js | 16 ++++++++++++++++ packages/cli/src/endo.js | 24 ++++++++++++++++++++++++ 2 files changed, 40 insertions(+) create mode 100644 packages/cli/src/adopt.js diff --git a/packages/cli/src/adopt.js b/packages/cli/src/adopt.js new file mode 100644 index 0000000000..8cbb812dde --- /dev/null +++ b/packages/cli/src/adopt.js @@ -0,0 +1,16 @@ +/* global process */ +import os from 'os'; +import { E } from '@endo/far'; +import { withEndoParty } from './context.js'; + +export const adoptCommand = async ({ + messageNumberText, + edgeName, + name, + partyNames, +}) => + withEndoParty(partyNames, { os, process }, async ({ party }) => { + // TODO less bad number parsing. + const messageNumber = Number(messageNumberText); + await E(party).adopt(messageNumber, edgeName, name); + }); diff --git a/packages/cli/src/endo.js b/packages/cli/src/endo.js index ad526cedf2..819b9bc955 100644 --- a/packages/cli/src/endo.js +++ b/packages/cli/src/endo.js @@ -418,6 +418,30 @@ export const main = async rawArgs => { return receive({ message, partyNames }); }); + program + .command('adopt ') + .option( + '-n,--name ', + 'Name to use, if different than the suggested name.', + ) + .option( + '-a,--as ', + 'Pose as named party (as named by current party)', + collect, + [], + ) + .description('Adopts a name from a received message') + .action(async (messageNumberText, edgeName, cmd) => { + const { name = edgeName, as: partyNames } = cmd.opts(); + const { adoptCommand } = await import('./adopt.js'); + return adoptCommand({ + messageNumberText, + edgeName, + name, + partyNames, + }); + }); + program .command('dismiss ') .description('dismisses a message and drops any references it carried') From b394c66a63c2066b9712ffaaee1447c15104f939 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Tue, 15 Aug 2023 22:46:03 -0700 Subject: [PATCH 094/234] refactor(daemon): Clarify internal vs external message representation --- packages/daemon/src/daemon.js | 83 +++++++++++++++++++++++----------- packages/daemon/src/types.d.ts | 10 +++- 2 files changed, 64 insertions(+), 29 deletions(-) diff --git a/packages/daemon/src/daemon.js b/packages/daemon/src/daemon.js index 9d03d4dda7..c559236338 100644 --- a/packages/daemon/src/daemon.js +++ b/packages/daemon/src/daemon.js @@ -420,7 +420,12 @@ const makeEndoBootstrap = ( if (responseName === undefined) { // Behold, recursion: // eslint-disable-next-line no-use-before-define - return hostRequest(what, responseName, guest, guestPetStore); + return hostRequest( + what, + responseName, + guestFormulaIdentifier, + guestPetStore, + ); } const responseP = responses.get(responseName); if (responseP !== undefined) { @@ -431,8 +436,7 @@ const makeEndoBootstrap = ( const newResponseP = hostRequest( what, responseName, - // eslint-disable-next-line no-use-before-define - guest, + guestFormulaIdentifier, guestPetStore, ); responses.set(responseName, newResponseP); @@ -506,32 +510,58 @@ const makeEndoBootstrap = ( await provideValueForFormulaIdentifier(storeFormulaIdentifier) ); - /** @type {Map} */ + /** @type {Map} */ const messages = new Map(); /** @type {WeakMap void>} */ const resolvers = new WeakMap(); /** @type {WeakMap void>} */ const dismissers = new WeakMap(); - /** @type {import('./types.js').Topic} */ + /** @type {import('./types.js').Topic} */ const messagesTopic = makeChangeTopic(); let nextMessageNumber = 0; - const listMessages = async () => harden(Array.from(messages.values())); + /** + * @param {import('./types.js').InternalMessage} message + * @returns {import('./types.js').Message} + */ + const dubMessage = message => { + if (message.type === 'request') { + const { who: senderFormula, ...rest } = message; + const [senderName] = petStore.lookup(senderFormula); + if (senderName !== undefined) { + return { who: senderName, ...rest }; + } + } else if (message.type === 'package') { + const { formulas: _, who: senderFormula, ...rest } = message; + const [senderName] = petStore.lookup(senderFormula); + if (senderName !== undefined) { + return { who: senderName, ...rest }; + } + } + throw new Error(`panic: Unknown message type ${message.type}`); + }; + + const listMessages = async () => + harden(Array.from(messages.values(), dubMessage)); const followMessages = async () => makeIteratorRef( (async function* currentAndSubsequentMessages() { const subsequentRequests = messagesTopic.subscribe(); - yield* messages.values(); - yield* subsequentRequests; + for (const message of messages.values()) { + yield dubMessage(message); + } + for await (const message of subsequentRequests) { + yield dubMessage(message); + } })(), ); /** * @param {string} what - user visible description of the desired value - * @param {unknown} whom - the requester + * @param {string} guestFormulaIdentifier */ - const requestFormulaIdentifier = async (what, whom) => { + const requestFormulaIdentifier = async (what, guestFormulaIdentifier) => { /** @type {import('@endo/promise-kit/src/types.js').PromiseKit} */ const { promise, resolve } = makePromiseKit(); const messageNumber = nextMessageNumber; @@ -550,22 +580,10 @@ const makeEndoBootstrap = ( }, ); - // How does the receiver know the sender? - const formulaIdentifier = formulaIdentifierForRef.get(whom); - if (formulaIdentifier === undefined) { - throw new Error( - `panic: requestFormulaIdentifier must be called with a party (who) that was obtained through provideValueFor*`, - ); - } - const [who] = petStore.lookup(formulaIdentifier); - // TODO consider having an invariant that a formula dictionary - // can only have one name for each formula identifier, - // so any attempt to copy a name is effectively enforced as a rename. - const req = harden({ type: /** @type {'request'} */ ('request'), number: messageNumber, - who, + who: guestFormulaIdentifier, what, when: new Date().toISOString(), settled, @@ -580,15 +598,23 @@ const makeEndoBootstrap = ( /** * @param {string} what * @param {string} responseName - * @param {import('./types.js').EndoGuest} who + * @param {string} guestFormulaIdentifier * @param {import('./types.js').PetStore} guestPetStore */ - const request = async (what, responseName, who, guestPetStore) => { + const request = async ( + what, + responseName, + guestFormulaIdentifier, + guestPetStore, + ) => { if (responseName !== undefined) { /** @type {string | undefined} */ let formulaIdentifier = guestPetStore.get(responseName); if (formulaIdentifier === undefined) { - formulaIdentifier = await requestFormulaIdentifier(what, who); + formulaIdentifier = await requestFormulaIdentifier( + what, + guestFormulaIdentifier, + ); await guestPetStore.write(responseName, formulaIdentifier); } // Behold, recursion: @@ -596,7 +622,10 @@ const makeEndoBootstrap = ( return provideValueForFormulaIdentifier(formulaIdentifier); } // The reference is not named nor to be named. - const formulaIdentifier = await requestFormulaIdentifier(what, who); + const formulaIdentifier = await requestFormulaIdentifier( + what, + guestFormulaIdentifier, + ); // Behold, recursion: // eslint-disable-next-line no-use-before-define return provideValueForFormulaIdentifier(formulaIdentifier); diff --git a/packages/daemon/src/types.d.ts b/packages/daemon/src/types.d.ts index 3b0bcb2675..475ff04668 100644 --- a/packages/daemon/src/types.d.ts +++ b/packages/daemon/src/types.d.ts @@ -148,24 +148,30 @@ export type Label = { who: string; when: string; }; +export type InternalLabel = Label; export type Request = { type: 'request'; what: string; settled: Promise<'fulfilled' | 'rejected'>; }; +export type InternalRequest = Request; export type Package = { type: 'package'; strings: Array; // text that appears before, between, and after named formulas. names: Array; // edge names - formulas: Array; // formula identifiers dismissed: Promise; }; +export type InternalPackage = Package & { + formulas: Array; // formula identifiers +}; export type Payload = Request | Package; +export type InternalPayload = InternalRequest | InternalPackage; export type Message = Label & Payload; +export type InternalMessage = InternalLabel & InternalPayload; export interface Topic< TRead, @@ -189,7 +195,7 @@ export interface PetStore { export type RequestFn = ( what: string, responseName: string, - guest: object, + guestFormulaIdentifier: string, guestPetStore: PetStore, ) => Promise; From fa4d69ebd9729821b4c7793c4984141ca6cba6f5 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Wed, 16 Aug 2023 14:18:14 -0700 Subject: [PATCH 095/234] refactor(daemon): Extract guest, host, and pet-name modules --- packages/daemon/src/daemon.js | 861 ++----------------------------- packages/daemon/src/guest.js | 161 ++++++ packages/daemon/src/host.js | 691 +++++++++++++++++++++++++ packages/daemon/src/pet-name.js | 12 + packages/daemon/src/pet-store.js | 26 +- 5 files changed, 900 insertions(+), 851 deletions(-) create mode 100644 packages/daemon/src/guest.js create mode 100644 packages/daemon/src/host.js create mode 100644 packages/daemon/src/pet-name.js diff --git a/packages/daemon/src/daemon.js b/packages/daemon/src/daemon.js index c559236338..9ed9cb7bbe 100644 --- a/packages/daemon/src/daemon.js +++ b/packages/daemon/src/daemon.js @@ -10,7 +10,6 @@ import '@endo/lockdown/commit.js'; import { E, Far } from '@endo/far'; import { makePromiseKit } from '@endo/promise-kit'; import { mapReader, mapWriter } from '@endo/stream'; -import { makeChangeTopic } from './pubsub.js'; import { makeMessageCapTP, makeNetstringCapTP, @@ -18,24 +17,16 @@ import { bytesToMessage, } from './connection.js'; import { makeRefReader } from './ref-reader.js'; -import { makeReaderRef, makeIteratorRef } from './reader-ref.js'; +import { makeReaderRef } from './reader-ref.js'; import { makeOwnPetStore, makeIdentifiedPetStore } from './pet-store.js'; +import { makeGuestMaker } from './guest.js'; +import { makeHostMaker } from './host.js'; const { quote: q } = assert; -const validNamePattern = /^[a-z][a-z0-9-]{0,127}$/; const zero512 = '0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'; -/** - * @param {string} petName - */ -const assertPetName = petName => { - if (typeof petName !== 'string' || !validNamePattern.test(petName)) { - throw new Error(`Invalid pet name ${q(petName)}`); - } -}; - const defaultHttpPort = 8920; // Eight Nine Duo Oh: ENDO. /** @type {import('./types.js').EndoGuest} */ @@ -61,6 +52,8 @@ const makeEndoBootstrap = ( httpPort, { cancelled, cancel, gracePeriodMs, gracePeriodElapsed }, ) => { + const { randomHex512, makeSha512 } = powers; + /** @type {Map} */ const valuePromiseForFormulaIdentifier = new Map(); // Reverse look-up, for answering "what is my name for this near or far @@ -343,826 +336,6 @@ const makeEndoBootstrap = ( return E(workerBootstrap).importBundleAndEndow(readableBundleP, guestP); }; - /** - * @param {string} guestFormulaIdentifier - * @param {string} hostFormulaIdentifier - * @param {string} petStoreFormulaIdentifier - */ - const makeIdentifiedGuest = async ( - guestFormulaIdentifier, - hostFormulaIdentifier, - petStoreFormulaIdentifier, - ) => { - /** @type {Map>} */ - const responses = new Map(); - - const guestPetStore = /** @type {import('./types.js').PetStore} */ ( - // Behold, recursion: - // eslint-disable-next-line no-use-before-define - await provideValueForFormulaIdentifier(petStoreFormulaIdentifier) - ); - const host = /** @type {object} */ ( - // Behold, recursion: - // eslint-disable-next-line no-use-before-define - await provideValueForFormulaIdentifier(hostFormulaIdentifier) - ); - - const hostRequest = partyRequestFunctions.get(host); - if (hostRequest === undefined) { - throw new Error( - `panic: a host request function must exist for every host`, - ); - } - - /** - * @param {string} petName - */ - const provide = async petName => { - assertPetName(petName); - const formulaIdentifier = guestPetStore.get(petName); - if (formulaIdentifier === undefined) { - throw new TypeError(`Unknown pet name: ${q(petName)}`); - } - // Behold, recursion: - // eslint-disable-next-line no-use-before-define - return provideValueForFormulaIdentifier(formulaIdentifier); - }; - - /** - * @param {string} fromName - * @param {string} toName - */ - const rename = async (fromName, toName) => { - assertPetName(fromName); - assertPetName(toName); - await guestPetStore.rename(fromName, toName); - const formulaIdentifier = responses.get(fromName); - if (formulaIdentifier === undefined) { - throw new Error( - `panic: the pet store rename must ensure that the renamed identifier exists`, - ); - } - responses.set(toName, formulaIdentifier); - responses.delete(fromName); - }; - - /** - * @param {string} petName - */ - const remove = async petName => { - await guestPetStore.remove(petName); - responses.delete(petName); - }; - - const { list } = guestPetStore; - - const request = async (what, responseName) => { - if (responseName === undefined) { - // Behold, recursion: - // eslint-disable-next-line no-use-before-define - return hostRequest( - what, - responseName, - guestFormulaIdentifier, - guestPetStore, - ); - } - const responseP = responses.get(responseName); - if (responseP !== undefined) { - return responseP; - } - // Behold, recursion: - // eslint-disable-next-line - const newResponseP = hostRequest( - what, - responseName, - guestFormulaIdentifier, - guestPetStore, - ); - responses.set(responseName, newResponseP); - return newResponseP; - }; - - /** - * @param {Array} strings - * @param {Array} edgeNames - * @param {Array} petNames - */ - const receive = async (strings, edgeNames, petNames) => { - petNames.forEach(assertPetName); - edgeNames.forEach(assertPetName); - if (petNames.length !== edgeNames.length) { - throw new Error( - `Message must have one edge name (${q( - edgeNames.length, - )}) for every pet name (${q(petNames.length)})`, - ); - } - if (strings.length < petNames.length) { - throw new Error( - `Message must have one string before every value delivered`, - ); - } - - const partyReceive = partyReceiveFunctions.get(host); - if (partyReceive === undefined) { - throw new Error(`panic: Message not deliverable`); - } - const formulaIdentifiers = petNames.map(petName => { - const formulaIdentifier = guestPetStore.get(petName); - if (formulaIdentifier === undefined) { - throw new Error(`Unknown pet name ${q(petName)}`); - } - return formulaIdentifier; - }); - partyReceive( - guestFormulaIdentifier, - strings, - edgeNames, - formulaIdentifiers, - ); - }; - - /** @type {import('@endo/eventual-send').ERef} */ - const guest = Far('EndoGuest', { - request, - receive, - list, - remove, - rename, - provide, - }); - - return guest; - }; - - /** - * @param {string} hostFormulaIdentifier - * @param {string} storeFormulaIdentifier - */ - const makeIdentifiedHost = async ( - hostFormulaIdentifier, - storeFormulaIdentifier, - ) => { - const petStore = /** @type {import('./types.js').PetStore} */ ( - // Behold, recursion: - // eslint-disable-next-line no-use-before-define - await provideValueForFormulaIdentifier(storeFormulaIdentifier) - ); - - /** @type {Map} */ - const messages = new Map(); - /** @type {WeakMap void>} */ - const resolvers = new WeakMap(); - /** @type {WeakMap void>} */ - const dismissers = new WeakMap(); - /** @type {import('./types.js').Topic} */ - const messagesTopic = makeChangeTopic(); - let nextMessageNumber = 0; - - /** - * @param {import('./types.js').InternalMessage} message - * @returns {import('./types.js').Message} - */ - const dubMessage = message => { - if (message.type === 'request') { - const { who: senderFormula, ...rest } = message; - const [senderName] = petStore.lookup(senderFormula); - if (senderName !== undefined) { - return { who: senderName, ...rest }; - } - } else if (message.type === 'package') { - const { formulas: _, who: senderFormula, ...rest } = message; - const [senderName] = petStore.lookup(senderFormula); - if (senderName !== undefined) { - return { who: senderName, ...rest }; - } - } - throw new Error(`panic: Unknown message type ${message.type}`); - }; - - const listMessages = async () => - harden(Array.from(messages.values(), dubMessage)); - - const followMessages = async () => - makeIteratorRef( - (async function* currentAndSubsequentMessages() { - const subsequentRequests = messagesTopic.subscribe(); - for (const message of messages.values()) { - yield dubMessage(message); - } - for await (const message of subsequentRequests) { - yield dubMessage(message); - } - })(), - ); - - /** - * @param {string} what - user visible description of the desired value - * @param {string} guestFormulaIdentifier - */ - const requestFormulaIdentifier = async (what, guestFormulaIdentifier) => { - /** @type {import('@endo/promise-kit/src/types.js').PromiseKit} */ - const { promise, resolve } = makePromiseKit(); - const messageNumber = nextMessageNumber; - nextMessageNumber += 1; - const settle = () => { - messages.delete(messageNumber); - }; - const settled = promise.then( - () => { - settle(); - return /** @type {'fulfilled'} */ ('fulfilled'); - }, - () => { - settle(); - return /** @type {'rejected'} */ ('rejected'); - }, - ); - - const req = harden({ - type: /** @type {'request'} */ ('request'), - number: messageNumber, - who: guestFormulaIdentifier, - what, - when: new Date().toISOString(), - settled, - }); - - messages.set(messageNumber, req); - resolvers.set(req, resolve); - messagesTopic.publisher.next(req); - return promise; - }; - - /** - * @param {string} what - * @param {string} responseName - * @param {string} guestFormulaIdentifier - * @param {import('./types.js').PetStore} guestPetStore - */ - const request = async ( - what, - responseName, - guestFormulaIdentifier, - guestPetStore, - ) => { - if (responseName !== undefined) { - /** @type {string | undefined} */ - let formulaIdentifier = guestPetStore.get(responseName); - if (formulaIdentifier === undefined) { - formulaIdentifier = await requestFormulaIdentifier( - what, - guestFormulaIdentifier, - ); - await guestPetStore.write(responseName, formulaIdentifier); - } - // Behold, recursion: - // eslint-disable-next-line no-use-before-define - return provideValueForFormulaIdentifier(formulaIdentifier); - } - // The reference is not named nor to be named. - const formulaIdentifier = await requestFormulaIdentifier( - what, - guestFormulaIdentifier, - ); - // Behold, recursion: - // eslint-disable-next-line no-use-before-define - return provideValueForFormulaIdentifier(formulaIdentifier); - }; - - const resolve = async (messageNumber, resolutionName) => { - assertPetName(resolutionName); - if ( - typeof messageNumber !== 'number' || - messageNumber >= Number.MAX_SAFE_INTEGER - ) { - throw new Error(`Invalid request number ${q(messageNumber)}`); - } - const req = messages.get(messageNumber); - const resolveRequest = resolvers.get(req); - if (resolveRequest === undefined) { - throw new Error(`No pending request for number ${messageNumber}`); - } - const formulaIdentifier = petStore.get(resolutionName); - if (formulaIdentifier === undefined) { - throw new TypeError( - `No formula exists for the pet name ${q(resolutionName)}`, - ); - } - resolveRequest(formulaIdentifier); - }; - - /** - * @param {string} senderFormulaIdentifier - * @param {Array} strings - * @param {Array} edgeNames - * @param {Array} formulaIdentifiers - */ - const receive = ( - senderFormulaIdentifier, - strings, - edgeNames, - formulaIdentifiers, - ) => { - /** @type {import('@endo/promise-kit/src/types.js').PromiseKit} */ - const dismissal = makePromiseKit(); - const messageNumber = nextMessageNumber; - nextMessageNumber += 1; - - const message = harden({ - type: /** @type {const} */ ('package'), - number: messageNumber, - strings, - names: edgeNames, - formulas: formulaIdentifiers, // TODO should not be visible to recipient - who: senderFormulaIdentifier, - when: new Date().toISOString(), - dismissed: dismissal.promise, - }); - - messages.set(messageNumber, message); - dismissers.set(message, () => { - messages.delete(messageNumber); - dismissal.resolve(); - }); - messagesTopic.publisher.next(message); - }; - - const dismiss = async messageNumber => { - if ( - typeof messageNumber !== 'number' || - messageNumber >= Number.MAX_SAFE_INTEGER - ) { - throw new Error(`Invalid request number ${q(messageNumber)}`); - } - const message = messages.get(messageNumber); - const dismissMessage = dismissers.get(message); - if (dismissMessage === undefined) { - throw new Error(`No dismissable message for number ${messageNumber}`); - } - dismissMessage(); - }; - - const adopt = async (messageNumber, edgeName, petName) => { - assertPetName(edgeName); - assertPetName(petName); - if ( - typeof messageNumber !== 'number' || - messageNumber >= Number.MAX_SAFE_INTEGER - ) { - throw new Error(`Invalid message number ${q(messageNumber)}`); - } - const message = messages.get(messageNumber); - if (message === undefined) { - throw new Error(`No such message with number ${q(messageNumber)}`); - } - if (message.type !== 'package') { - throw new Error(`Message must be a package ${q(messageNumber)}`); - } - const index = message.names.lastIndexOf(edgeName); - if (index === -1) { - throw new Error( - `No reference named ${q(edgeName)} in message ${q(messageNumber)}`, - ); - } - const formulaIdentifier = message.formulas[index]; - if (formulaIdentifier === undefined) { - throw new Error( - `panic: message must contain a formula for every name, including the name ${q( - edgeName, - )} at ${q(index)}`, - ); - } - await petStore.write(petName, formulaIdentifier); - }; - - // TODO test reject - /** - * @param {number} messageNumber - * @param {string} [message] - */ - const reject = async (messageNumber, message = 'Declined') => { - const req = messages.get(messageNumber); - if (req !== undefined) { - const resolveRequest = resolvers.get(req); - if (resolveRequest === undefined) { - throw new Error(`panic: a resolver must exist for every request`); - } - resolveRequest(harden(Promise.reject(harden(new Error(message))))); - } - }; - - /** - * @param {string} petName - */ - const provideGuest = async petName => { - /** @type {string | undefined} */ - let formulaIdentifier; - if (petName !== undefined) { - formulaIdentifier = petStore.get(petName); - } - if (formulaIdentifier === undefined) { - const id512 = await powers.randomHex512(); - const guestStoreFormulaIdentifier = `pet-store-id512:${id512}`; - /** @type {import('./types.js').GuestFormula} */ - const formula = { - type: /* @type {'guest'} */ 'guest', - host: hostFormulaIdentifier, - store: guestStoreFormulaIdentifier, - }; - const { value, formulaIdentifier: guestFormulaIdentifier } = - // Behold, recursion: - // eslint-disable-next-line no-use-before-define - await provideValueForFormula(formula, 'guest-id512'); - if (petName !== undefined) { - await petStore.write(petName, guestFormulaIdentifier); - } - return value; - } else if (!formulaIdentifier.startsWith('guest-id512:')) { - throw new Error( - `Existing pet name does not designate a guest powers capability: ${q( - petName, - )}`, - ); - } - return /** @type {Promise} */ ( - // Behold, recursion: - // eslint-disable-next-line no-use-before-define - provideValueForFormulaIdentifier(formulaIdentifier) - ); - }; - - /** - * @param {import('@endo/eventual-send').ERef>} readerRef - * @param {string} [petName] - */ - const store = async (readerRef, petName) => { - if (petName !== undefined) { - assertPetName(petName); - } - - const formulaIdentifier = await storeReaderRef(readerRef); - - if (petName !== undefined) { - await petStore.write(petName, formulaIdentifier); - } - }; - - /** - * @param {string} petName - */ - const provide = async petName => { - const formulaIdentifier = petStore.get(petName); - if (formulaIdentifier === undefined) { - throw new TypeError(`Unknown pet name: ${q(petName)}`); - } - // Behold, recursion: - // eslint-disable-next-line no-use-before-define - return provideValueForFormulaIdentifier(formulaIdentifier); - }; - - /** - * @param {string} workerName - */ - const provideWorker = async workerName => { - if (typeof workerName !== 'string') { - throw new Error('worker name must be string'); - } - let workerFormulaIdentifier = petStore.get(workerName); - if (workerFormulaIdentifier === undefined) { - const workerId512 = await powers.randomHex512(); - workerFormulaIdentifier = `worker-id512:${workerId512}`; - await petStore.write(workerName, workerFormulaIdentifier); - } else if (!workerFormulaIdentifier.startsWith('worker-id512:')) { - throw new Error(`Not a worker ${q(workerName)}`); - } - return /** @type {Promise} */ ( - // Behold, recursion: - // eslint-disable-next-line no-use-before-define - provideValueForFormulaIdentifier(workerFormulaIdentifier) - ); - }; - - const lookup = async presence => { - const formulaIdentifier = formulaIdentifierForRef.get(await presence); - if (formulaIdentifier === undefined) { - return []; - } - return E(petStore).lookup(formulaIdentifier); - }; - - /** - * @param {string | 'MAIN' | 'NEW'} workerName - */ - const provideWorkerFormulaIdentifier = async workerName => { - if (workerName === 'MAIN') { - return `worker-id512:${zero512}`; - } else if (workerName === 'NEW') { - const workerId512 = await powers.randomHex512(); - return `worker-id512:${workerId512}`; - } - assertPetName(workerName); - let workerFormulaIdentifier = petStore.get(workerName); - if (workerFormulaIdentifier === undefined) { - const workerId512 = await powers.randomHex512(); - workerFormulaIdentifier = `worker-id512:${workerId512}`; - await petStore.write(workerName, workerFormulaIdentifier); - } - return workerFormulaIdentifier; - }; - - /** - * @param {string | 'NONE' | 'HOST' | 'ENDO'} partyName - */ - const providePowersFormulaIdentifier = async partyName => { - if (partyName === 'NONE') { - return 'least-authority'; - } else if (partyName === 'HOST') { - return 'host'; - } else if (partyName === 'ENDO') { - return 'endo'; - } - assertPetName(partyName); - let guestFormulaIdentifier = petStore.get(partyName); - if (guestFormulaIdentifier === undefined) { - const guest = await provideGuest(partyName); - guestFormulaIdentifier = formulaIdentifierForRef.get(guest); - if (guestFormulaIdentifier === undefined) { - throw new Error( - `panic: provideGuest must return an guest with a corresponding formula identifier`, - ); - } - } - return guestFormulaIdentifier; - }; - - /** - * @param {string | 'MAIN' | 'NEW'} workerName - * @param {string} source - * @param {Array} codeNames - * @param {Array} petNames - * @param {string} resultName - */ - const evaluate = async ( - workerName, - source, - codeNames, - petNames, - resultName, - ) => { - const workerFormulaIdentifier = await provideWorkerFormulaIdentifier( - workerName, - ); - - if (resultName !== undefined) { - assertPetName(resultName); - } - if (petNames.length !== codeNames.length) { - throw new Error('Evaluator requires one pet name for each code name'); - } - - const formulaIdentifiers = await Promise.all( - petNames.map(async (petName, index) => { - assertPetName(petName); - if (typeof codeNames[index] !== 'string') { - throw new Error(`Invalid endowment name: ${q(codeNames[index])}`); - } - const formulaIdentifier = petStore.get(petName); - if (formulaIdentifier === undefined) { - throw new Error(`Unknown pet name ${q(petName)}`); - } - return formulaIdentifier; - }), - ); - - const formula = { - /** @type {'eval'} */ - type: 'eval', - worker: workerFormulaIdentifier, - source, - names: codeNames, - values: formulaIdentifiers, - }; - - // Behold, recursion: - // eslint-disable-next-line no-use-before-define - const { formulaIdentifier, value } = await provideValueForFormula( - formula, - 'eval-id512', - ); - if (resultName !== undefined) { - await petStore.write(resultName, formulaIdentifier); - } - return value; - }; - - /** - * @param {string | 'NEW' | 'MAIN'} workerName - * @param {string} importPath - * @param {string | 'NONE' | 'HOST' | 'ENDO'} powersName - * @param {string} resultName - */ - const importUnsafeAndEndow = async ( - workerName, - importPath, - powersName, - resultName, - ) => { - const workerFormulaIdentifier = await provideWorkerFormulaIdentifier( - workerName, - ); - - const powersFormulaIdentifier = await providePowersFormulaIdentifier( - powersName, - ); - - const formula = { - /** @type {'import-unsafe'} */ - type: 'import-unsafe', - worker: workerFormulaIdentifier, - powers: powersFormulaIdentifier, - importPath, - }; - - // Behold, recursion: - // eslint-disable-next-line no-use-before-define - const { formulaIdentifier, value } = await provideValueForFormula( - formula, - 'import-unsafe-id512', - ); - if (resultName !== undefined) { - await petStore.write(resultName, formulaIdentifier); - } - return value; - }; - - /** - * @param {string | 'MAIN' | 'NEW'} workerName - * @param {string} bundleName - * @param {string | 'NONE' | 'HOST' | 'ENDO'} powersName - * @param {string} resultName - */ - const importBundleAndEndow = async ( - workerName, - bundleName, - powersName, - resultName, - ) => { - const workerFormulaIdentifier = await provideWorkerFormulaIdentifier( - workerName, - ); - - const bundleFormulaIdentifier = petStore.get(bundleName); - if (bundleFormulaIdentifier === undefined) { - throw new TypeError(`Unknown pet name for bundle: ${bundleName}`); - } - - const powersFormulaIdentifier = await providePowersFormulaIdentifier( - powersName, - ); - - const formula = { - /** @type {'import-bundle'} */ - type: 'import-bundle', - worker: workerFormulaIdentifier, - powers: powersFormulaIdentifier, - bundle: bundleFormulaIdentifier, - }; - - // Behold, recursion: - // eslint-disable-next-line no-use-before-define - const { value, formulaIdentifier } = await provideValueForFormula( - formula, - 'import-bundle-id512', - ); - - if (resultName !== undefined) { - await petStore.write(resultName, formulaIdentifier); - } - - return value; - }; - - /** - * @param {string} [petName] - */ - const makeWorker = async petName => { - const workerId512 = await powers.randomHex512(); - const formulaIdentifier = `worker-id512:${workerId512}`; - if (petName !== undefined) { - await petStore.write(petName, formulaIdentifier); - } - return /** @type {Promise} */ ( - // Behold, recursion: - // eslint-disable-next-line no-use-before-define - provideValueForFormulaIdentifier(formulaIdentifier) - ); - }; - - /** - * @param {string} [petName] - */ - const provideHost = async petName => { - /** @type {string | undefined} */ - let formulaIdentifier; - if (petName !== undefined) { - formulaIdentifier = petStore.get(petName); - } - if (formulaIdentifier === undefined) { - const id512 = await powers.randomHex512(); - formulaIdentifier = `host-id512:${id512}`; - if (petName !== undefined) { - await petStore.write(petName, formulaIdentifier); - } - } else if (!formulaIdentifier.startsWith('host-id512:')) { - throw new Error( - `Existing pet name does not designate a host powers capability: ${q( - petName, - )}`, - ); - } - return /** @type {Promise} */ ( - // Behold, recursion: - // eslint-disable-next-line no-use-before-define - provideValueForFormulaIdentifier(formulaIdentifier) - ); - }; - - /** - * @param {string} webPageName - * @param {string} bundleName - * @param {string | 'NONE' | 'HOST' | 'ENDO'} powersName - */ - const provideWebPage = async (webPageName, bundleName, powersName) => { - const bundleFormulaIdentifier = petStore.get(bundleName); - if (bundleFormulaIdentifier === undefined) { - throw new Error(`Unknown pet name: ${q(bundleName)}`); - } - - const powersFormulaIdentifier = await providePowersFormulaIdentifier( - powersName, - ); - - const digester = powers.makeSha512(); - digester.updateText( - `${bundleFormulaIdentifier},${powersFormulaIdentifier}`, - ); - const formulaNumber = digester.digestHex().slice(32, 64); - - const formula = { - type: 'web-bundle', - bundle: bundleFormulaIdentifier, - powers: powersFormulaIdentifier, - }; - - // Behold, recursion: - // eslint-disable-next-line no-use-before-define - const { value, formulaIdentifier } = await provideValueForNumberedFormula( - 'web-bundle', - formulaNumber, - formula, - ); - - if (webPageName !== undefined) { - await petStore.write(webPageName, formulaIdentifier); - } - - return value; - }; - - const { list, remove, rename } = petStore; - - /** @type {import('./types.js').EndoHost} */ - const host = Far('EndoHost', { - listMessages, - followMessages, - provide, - resolve, - reject, - adopt, - dismiss, - lookup, - list, - remove, - rename, - store, - provideGuest, - provideHost, - makeWorker, - provideWorker, - evaluate, - importUnsafeAndEndow, - importBundleAndEndow, - provideWebPage, - }); - - partyReceiveFunctions.set(host, receive); - partyRequestFunctions.set(host, request); - - return host; - }; - /** * @param {string} formulaIdentifier * @param {string} formulaNumber @@ -1193,6 +366,8 @@ const makeEndoBootstrap = ( formula.bundle, ); } else if (formula.type === 'guest') { + // Behold, recursion: + // eslint-disable-next-line no-use-before-define return makeIdentifiedGuest( formulaIdentifier, formula.host, @@ -1281,6 +456,8 @@ const makeEndoBootstrap = ( if (formulaIdentifier === 'pet-store') { return makeOwnPetStore(powers, locator, 'pet-store'); } else if (formulaIdentifier === 'host') { + // Behold, recursion: + // eslint-disable-next-line no-use-before-define return makeIdentifiedHost(formulaIdentifier, 'pet-store'); } else if (formulaIdentifier === 'endo') { // Behold, self-referentiality: @@ -1311,6 +488,8 @@ const makeEndoBootstrap = ( } else if (prefix === 'pet-store-id512') { return makeIdentifiedPetStore(powers, locator, formulaNumber); } else if (prefix === 'host-id512') { + // Behold, recursion: + // eslint-disable-next-line no-use-before-define return makeIdentifiedHost( formulaIdentifier, `pet-store-id512:${formulaNumber}`, @@ -1392,6 +571,24 @@ const makeEndoBootstrap = ( return value; }; + const makeIdentifiedGuest = makeGuestMaker({ + provideValueForFormulaIdentifier, + partyReceiveFunctions, + partyRequestFunctions, + }); + + const makeIdentifiedHost = makeHostMaker({ + provideValueForFormulaIdentifier, + provideValueForFormula, + provideValueForNumberedFormula, + formulaIdentifierForRef, + partyReceiveFunctions, + partyRequestFunctions, + storeReaderRef, + randomHex512, + makeSha512, + }); + const endoBootstrap = Far('Endo private facet', { // TODO for user named diff --git a/packages/daemon/src/guest.js b/packages/daemon/src/guest.js new file mode 100644 index 0000000000..a77e9f2347 --- /dev/null +++ b/packages/daemon/src/guest.js @@ -0,0 +1,161 @@ +import { Far } from '@endo/far'; +import { assertPetName } from './pet-name.js'; + +const { quote: q } = assert; + +export const makeGuestMaker = ({ + provideValueForFormulaIdentifier, + partyReceiveFunctions, + partyRequestFunctions, +}) => { + /** + * @param {string} guestFormulaIdentifier + * @param {string} hostFormulaIdentifier + * @param {string} petStoreFormulaIdentifier + */ + const makeIdentifiedGuest = async ( + guestFormulaIdentifier, + hostFormulaIdentifier, + petStoreFormulaIdentifier, + ) => { + /** @type {Map>} */ + const responses = new Map(); + + const guestPetStore = /** @type {import('./types.js').PetStore} */ ( + await provideValueForFormulaIdentifier(petStoreFormulaIdentifier) + ); + const host = /** @type {object} */ ( + await provideValueForFormulaIdentifier(hostFormulaIdentifier) + ); + + const hostRequest = partyRequestFunctions.get(host); + if (hostRequest === undefined) { + throw new Error( + `panic: a host request function must exist for every host`, + ); + } + + /** + * @param {string} petName + */ + const provide = async petName => { + assertPetName(petName); + const formulaIdentifier = guestPetStore.get(petName); + if (formulaIdentifier === undefined) { + throw new TypeError(`Unknown pet name: ${q(petName)}`); + } + // Behold, recursion: + // eslint-disable-next-line no-use-before-define + return provideValueForFormulaIdentifier(formulaIdentifier); + }; + + /** + * @param {string} fromName + * @param {string} toName + */ + const rename = async (fromName, toName) => { + assertPetName(fromName); + assertPetName(toName); + await guestPetStore.rename(fromName, toName); + const formulaIdentifier = responses.get(fromName); + if (formulaIdentifier === undefined) { + throw new Error( + `panic: the pet store rename must ensure that the renamed identifier exists`, + ); + } + responses.set(toName, formulaIdentifier); + responses.delete(fromName); + }; + + /** + * @param {string} petName + */ + const remove = async petName => { + await guestPetStore.remove(petName); + responses.delete(petName); + }; + + const { list } = guestPetStore; + + const request = async (what, responseName) => { + if (responseName === undefined) { + // Behold, recursion: + // eslint-disable-next-line no-use-before-define + return hostRequest( + what, + responseName, + guestFormulaIdentifier, + guestPetStore, + ); + } + const responseP = responses.get(responseName); + if (responseP !== undefined) { + return responseP; + } + // Behold, recursion: + // eslint-disable-next-line + const newResponseP = hostRequest( + what, + responseName, + guestFormulaIdentifier, + guestPetStore, + ); + responses.set(responseName, newResponseP); + return newResponseP; + }; + + /** + * @param {Array} strings + * @param {Array} edgeNames + * @param {Array} petNames + */ + const receive = async (strings, edgeNames, petNames) => { + petNames.forEach(assertPetName); + edgeNames.forEach(assertPetName); + if (petNames.length !== edgeNames.length) { + throw new Error( + `Message must have one edge name (${q( + edgeNames.length, + )}) for every pet name (${q(petNames.length)})`, + ); + } + if (strings.length < petNames.length) { + throw new Error( + `Message must have one string before every value delivered`, + ); + } + + const partyReceive = partyReceiveFunctions.get(host); + if (partyReceive === undefined) { + throw new Error(`panic: Message not deliverable`); + } + const formulaIdentifiers = petNames.map(petName => { + const formulaIdentifier = guestPetStore.get(petName); + if (formulaIdentifier === undefined) { + throw new Error(`Unknown pet name ${q(petName)}`); + } + return formulaIdentifier; + }); + partyReceive( + guestFormulaIdentifier, + strings, + edgeNames, + formulaIdentifiers, + ); + }; + + /** @type {import('@endo/eventual-send').ERef} */ + const guest = Far('EndoGuest', { + request, + receive, + list, + remove, + rename, + provide, + }); + + return guest; + }; + + return makeIdentifiedGuest; +}; diff --git a/packages/daemon/src/host.js b/packages/daemon/src/host.js new file mode 100644 index 0000000000..4682bbfdcc --- /dev/null +++ b/packages/daemon/src/host.js @@ -0,0 +1,691 @@ +import { makePromiseKit } from '@endo/promise-kit'; +import { E, Far } from '@endo/far'; +import { makeIteratorRef } from './reader-ref.js'; +import { makeChangeTopic } from './pubsub.js'; +import { assertPetName } from './pet-name.js'; + +const { quote: q } = assert; + +const zero512 = + '0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'; + +export const makeHostMaker = ({ + provideValueForFormulaIdentifier, + provideValueForFormula, + provideValueForNumberedFormula, + partyReceiveFunctions, + partyRequestFunctions, + formulaIdentifierForRef, + storeReaderRef, + makeSha512, + randomHex512, +}) => { + /** + * @param {string} hostFormulaIdentifier + * @param {string} storeFormulaIdentifier + */ + const makeIdentifiedHost = async ( + hostFormulaIdentifier, + storeFormulaIdentifier, + ) => { + const petStore = /** @type {import('./types.js').PetStore} */ ( + // Behold, recursion: + // eslint-disable-next-line no-use-before-define + await provideValueForFormulaIdentifier(storeFormulaIdentifier) + ); + + /** @type {Map} */ + const messages = new Map(); + /** @type {WeakMap void>} */ + const resolvers = new WeakMap(); + /** @type {WeakMap void>} */ + const dismissers = new WeakMap(); + /** @type {import('./types.js').Topic} */ + const messagesTopic = makeChangeTopic(); + let nextMessageNumber = 0; + + /** + * @param {import('./types.js').InternalMessage} message + * @returns {import('./types.js').Message} + */ + const dubMessage = message => { + if (message.type === 'request') { + const { who: senderFormula, ...rest } = message; + const [senderName] = petStore.lookup(senderFormula); + if (senderName !== undefined) { + return { who: senderName, ...rest }; + } + } else if (message.type === 'package') { + const { formulas: _, who: senderFormula, ...rest } = message; + const [senderName] = petStore.lookup(senderFormula); + if (senderName !== undefined) { + return { who: senderName, ...rest }; + } + } + throw new Error(`panic: Unknown message type ${message.type}`); + }; + + const listMessages = async () => + harden(Array.from(messages.values(), dubMessage)); + + const followMessages = async () => + makeIteratorRef( + (async function* currentAndSubsequentMessages() { + const subsequentRequests = messagesTopic.subscribe(); + for (const message of messages.values()) { + yield dubMessage(message); + } + for await (const message of subsequentRequests) { + yield dubMessage(message); + } + })(), + ); + + /** + * @param {string} what - user visible description of the desired value + * @param {string} guestFormulaIdentifier + */ + const requestFormulaIdentifier = async (what, guestFormulaIdentifier) => { + /** @type {import('@endo/promise-kit/src/types.js').PromiseKit} */ + const { promise, resolve } = makePromiseKit(); + const messageNumber = nextMessageNumber; + nextMessageNumber += 1; + const settle = () => { + messages.delete(messageNumber); + }; + const settled = promise.then( + () => { + settle(); + return /** @type {'fulfilled'} */ ('fulfilled'); + }, + () => { + settle(); + return /** @type {'rejected'} */ ('rejected'); + }, + ); + + const req = harden({ + type: /** @type {'request'} */ ('request'), + number: messageNumber, + who: guestFormulaIdentifier, + what, + when: new Date().toISOString(), + settled, + }); + + messages.set(messageNumber, req); + resolvers.set(req, resolve); + messagesTopic.publisher.next(req); + return promise; + }; + + /** + * @param {string} what + * @param {string} responseName + * @param {string} guestFormulaIdentifier + * @param {import('./types.js').PetStore} guestPetStore + */ + const request = async ( + what, + responseName, + guestFormulaIdentifier, + guestPetStore, + ) => { + if (responseName !== undefined) { + /** @type {string | undefined} */ + let formulaIdentifier = guestPetStore.get(responseName); + if (formulaIdentifier === undefined) { + formulaIdentifier = await requestFormulaIdentifier( + what, + guestFormulaIdentifier, + ); + await guestPetStore.write(responseName, formulaIdentifier); + } + // Behold, recursion: + // eslint-disable-next-line no-use-before-define + return provideValueForFormulaIdentifier(formulaIdentifier); + } + // The reference is not named nor to be named. + const formulaIdentifier = await requestFormulaIdentifier( + what, + guestFormulaIdentifier, + ); + // Behold, recursion: + // eslint-disable-next-line no-use-before-define + return provideValueForFormulaIdentifier(formulaIdentifier); + }; + + const resolve = async (messageNumber, resolutionName) => { + assertPetName(resolutionName); + if ( + typeof messageNumber !== 'number' || + messageNumber >= Number.MAX_SAFE_INTEGER + ) { + throw new Error(`Invalid request number ${q(messageNumber)}`); + } + const req = messages.get(messageNumber); + const resolveRequest = resolvers.get(req); + if (resolveRequest === undefined) { + throw new Error(`No pending request for number ${messageNumber}`); + } + const formulaIdentifier = petStore.get(resolutionName); + if (formulaIdentifier === undefined) { + throw new TypeError( + `No formula exists for the pet name ${q(resolutionName)}`, + ); + } + resolveRequest(formulaIdentifier); + }; + + /** + * @param {string} senderFormulaIdentifier + * @param {Array} strings + * @param {Array} edgeNames + * @param {Array} formulaIdentifiers + */ + const receive = ( + senderFormulaIdentifier, + strings, + edgeNames, + formulaIdentifiers, + ) => { + /** @type {import('@endo/promise-kit/src/types.js').PromiseKit} */ + const dismissal = makePromiseKit(); + const messageNumber = nextMessageNumber; + nextMessageNumber += 1; + + const message = harden({ + type: /** @type {const} */ ('package'), + number: messageNumber, + strings, + names: edgeNames, + formulas: formulaIdentifiers, // TODO should not be visible to recipient + who: senderFormulaIdentifier, + when: new Date().toISOString(), + dismissed: dismissal.promise, + }); + + messages.set(messageNumber, message); + dismissers.set(message, () => { + messages.delete(messageNumber); + dismissal.resolve(); + }); + messagesTopic.publisher.next(message); + }; + + const dismiss = async messageNumber => { + if ( + typeof messageNumber !== 'number' || + messageNumber >= Number.MAX_SAFE_INTEGER + ) { + throw new Error(`Invalid request number ${q(messageNumber)}`); + } + const message = messages.get(messageNumber); + const dismissMessage = dismissers.get(message); + if (dismissMessage === undefined) { + throw new Error(`No dismissable message for number ${messageNumber}`); + } + dismissMessage(); + }; + + const adopt = async (messageNumber, edgeName, petName) => { + assertPetName(edgeName); + assertPetName(petName); + if ( + typeof messageNumber !== 'number' || + messageNumber >= Number.MAX_SAFE_INTEGER + ) { + throw new Error(`Invalid message number ${q(messageNumber)}`); + } + const message = messages.get(messageNumber); + if (message === undefined) { + throw new Error(`No such message with number ${q(messageNumber)}`); + } + if (message.type !== 'package') { + throw new Error(`Message must be a package ${q(messageNumber)}`); + } + const index = message.names.lastIndexOf(edgeName); + if (index === -1) { + throw new Error( + `No reference named ${q(edgeName)} in message ${q(messageNumber)}`, + ); + } + const formulaIdentifier = message.formulas[index]; + if (formulaIdentifier === undefined) { + throw new Error( + `panic: message must contain a formula for every name, including the name ${q( + edgeName, + )} at ${q(index)}`, + ); + } + await petStore.write(petName, formulaIdentifier); + }; + + // TODO test reject + /** + * @param {number} messageNumber + * @param {string} [message] + */ + const reject = async (messageNumber, message = 'Declined') => { + const req = messages.get(messageNumber); + if (req !== undefined) { + const resolveRequest = resolvers.get(req); + if (resolveRequest === undefined) { + throw new Error(`panic: a resolver must exist for every request`); + } + resolveRequest(harden(Promise.reject(harden(new Error(message))))); + } + }; + + /** + * @param {string} petName + */ + const provideGuest = async petName => { + /** @type {string | undefined} */ + let formulaIdentifier; + if (petName !== undefined) { + formulaIdentifier = petStore.get(petName); + } + if (formulaIdentifier === undefined) { + const id512 = await randomHex512(); + const guestStoreFormulaIdentifier = `pet-store-id512:${id512}`; + /** @type {import('./types.js').GuestFormula} */ + const formula = { + type: /* @type {'guest'} */ 'guest', + host: hostFormulaIdentifier, + store: guestStoreFormulaIdentifier, + }; + const { value, formulaIdentifier: guestFormulaIdentifier } = + // Behold, recursion: + // eslint-disable-next-line no-use-before-define + await provideValueForFormula(formula, 'guest-id512'); + if (petName !== undefined) { + await petStore.write(petName, guestFormulaIdentifier); + } + return value; + } else if (!formulaIdentifier.startsWith('guest-id512:')) { + throw new Error( + `Existing pet name does not designate a guest powers capability: ${q( + petName, + )}`, + ); + } + return /** @type {Promise} */ ( + // Behold, recursion: + // eslint-disable-next-line no-use-before-define + provideValueForFormulaIdentifier(formulaIdentifier) + ); + }; + + /** + * @param {import('@endo/eventual-send').ERef>} readerRef + * @param {string} [petName] + */ + const store = async (readerRef, petName) => { + if (petName !== undefined) { + assertPetName(petName); + } + + const formulaIdentifier = await storeReaderRef(readerRef); + + if (petName !== undefined) { + await petStore.write(petName, formulaIdentifier); + } + }; + + /** + * @param {string} petName + */ + const provide = async petName => { + const formulaIdentifier = petStore.get(petName); + if (formulaIdentifier === undefined) { + throw new TypeError(`Unknown pet name: ${q(petName)}`); + } + // Behold, recursion: + // eslint-disable-next-line no-use-before-define + return provideValueForFormulaIdentifier(formulaIdentifier); + }; + + /** + * @param {string} workerName + */ + const provideWorker = async workerName => { + if (typeof workerName !== 'string') { + throw new Error('worker name must be string'); + } + let workerFormulaIdentifier = petStore.get(workerName); + if (workerFormulaIdentifier === undefined) { + const workerId512 = await randomHex512(); + workerFormulaIdentifier = `worker-id512:${workerId512}`; + await petStore.write(workerName, workerFormulaIdentifier); + } else if (!workerFormulaIdentifier.startsWith('worker-id512:')) { + throw new Error(`Not a worker ${q(workerName)}`); + } + return /** @type {Promise} */ ( + // Behold, recursion: + // eslint-disable-next-line no-use-before-define + provideValueForFormulaIdentifier(workerFormulaIdentifier) + ); + }; + + const lookup = async presence => { + const formulaIdentifier = formulaIdentifierForRef.get(await presence); + if (formulaIdentifier === undefined) { + return []; + } + return E(petStore).lookup(formulaIdentifier); + }; + + /** + * @param {string | 'MAIN' | 'NEW'} workerName + */ + const provideWorkerFormulaIdentifier = async workerName => { + if (workerName === 'MAIN') { + return `worker-id512:${zero512}`; + } else if (workerName === 'NEW') { + const workerId512 = await randomHex512(); + return `worker-id512:${workerId512}`; + } + assertPetName(workerName); + let workerFormulaIdentifier = petStore.get(workerName); + if (workerFormulaIdentifier === undefined) { + const workerId512 = await randomHex512(); + workerFormulaIdentifier = `worker-id512:${workerId512}`; + await petStore.write(workerName, workerFormulaIdentifier); + } + return workerFormulaIdentifier; + }; + + /** + * @param {string | 'NONE' | 'HOST' | 'ENDO'} partyName + */ + const providePowersFormulaIdentifier = async partyName => { + if (partyName === 'NONE') { + return 'least-authority'; + } else if (partyName === 'HOST') { + return 'host'; + } else if (partyName === 'ENDO') { + return 'endo'; + } + assertPetName(partyName); + let guestFormulaIdentifier = petStore.get(partyName); + if (guestFormulaIdentifier === undefined) { + const guest = await provideGuest(partyName); + guestFormulaIdentifier = formulaIdentifierForRef.get(guest); + if (guestFormulaIdentifier === undefined) { + throw new Error( + `panic: provideGuest must return an guest with a corresponding formula identifier`, + ); + } + } + return guestFormulaIdentifier; + }; + + /** + * @param {string | 'MAIN' | 'NEW'} workerName + * @param {string} source + * @param {Array} codeNames + * @param {Array} petNames + * @param {string} resultName + */ + const evaluate = async ( + workerName, + source, + codeNames, + petNames, + resultName, + ) => { + const workerFormulaIdentifier = await provideWorkerFormulaIdentifier( + workerName, + ); + + if (resultName !== undefined) { + assertPetName(resultName); + } + if (petNames.length !== codeNames.length) { + throw new Error('Evaluator requires one pet name for each code name'); + } + + const formulaIdentifiers = await Promise.all( + petNames.map(async (petName, index) => { + assertPetName(petName); + if (typeof codeNames[index] !== 'string') { + throw new Error(`Invalid endowment name: ${q(codeNames[index])}`); + } + const formulaIdentifier = petStore.get(petName); + if (formulaIdentifier === undefined) { + throw new Error(`Unknown pet name ${q(petName)}`); + } + return formulaIdentifier; + }), + ); + + const formula = { + /** @type {'eval'} */ + type: 'eval', + worker: workerFormulaIdentifier, + source, + names: codeNames, + values: formulaIdentifiers, + }; + + // Behold, recursion: + // eslint-disable-next-line no-use-before-define + const { formulaIdentifier, value } = await provideValueForFormula( + formula, + 'eval-id512', + ); + if (resultName !== undefined) { + await petStore.write(resultName, formulaIdentifier); + } + return value; + }; + + /** + * @param {string | 'NEW' | 'MAIN'} workerName + * @param {string} importPath + * @param {string | 'NONE' | 'HOST' | 'ENDO'} powersName + * @param {string} resultName + */ + const importUnsafeAndEndow = async ( + workerName, + importPath, + powersName, + resultName, + ) => { + const workerFormulaIdentifier = await provideWorkerFormulaIdentifier( + workerName, + ); + + const powersFormulaIdentifier = await providePowersFormulaIdentifier( + powersName, + ); + + const formula = { + /** @type {'import-unsafe'} */ + type: 'import-unsafe', + worker: workerFormulaIdentifier, + powers: powersFormulaIdentifier, + importPath, + }; + + // Behold, recursion: + // eslint-disable-next-line no-use-before-define + const { formulaIdentifier, value } = await provideValueForFormula( + formula, + 'import-unsafe-id512', + ); + if (resultName !== undefined) { + await petStore.write(resultName, formulaIdentifier); + } + return value; + }; + + /** + * @param {string | 'MAIN' | 'NEW'} workerName + * @param {string} bundleName + * @param {string | 'NONE' | 'HOST' | 'ENDO'} powersName + * @param {string} resultName + */ + const importBundleAndEndow = async ( + workerName, + bundleName, + powersName, + resultName, + ) => { + const workerFormulaIdentifier = await provideWorkerFormulaIdentifier( + workerName, + ); + + const bundleFormulaIdentifier = petStore.get(bundleName); + if (bundleFormulaIdentifier === undefined) { + throw new TypeError(`Unknown pet name for bundle: ${bundleName}`); + } + + const powersFormulaIdentifier = await providePowersFormulaIdentifier( + powersName, + ); + + const formula = { + /** @type {'import-bundle'} */ + type: 'import-bundle', + worker: workerFormulaIdentifier, + powers: powersFormulaIdentifier, + bundle: bundleFormulaIdentifier, + }; + + // Behold, recursion: + // eslint-disable-next-line no-use-before-define + const { value, formulaIdentifier } = await provideValueForFormula( + formula, + 'import-bundle-id512', + ); + + if (resultName !== undefined) { + await petStore.write(resultName, formulaIdentifier); + } + + return value; + }; + + /** + * @param {string} [petName] + */ + const makeWorker = async petName => { + const workerId512 = await randomHex512(); + const formulaIdentifier = `worker-id512:${workerId512}`; + if (petName !== undefined) { + await petStore.write(petName, formulaIdentifier); + } + return /** @type {Promise} */ ( + // Behold, recursion: + // eslint-disable-next-line no-use-before-define + provideValueForFormulaIdentifier(formulaIdentifier) + ); + }; + + /** + * @param {string} [petName] + */ + const provideHost = async petName => { + /** @type {string | undefined} */ + let formulaIdentifier; + if (petName !== undefined) { + formulaIdentifier = petStore.get(petName); + } + if (formulaIdentifier === undefined) { + const id512 = await randomHex512(); + formulaIdentifier = `host-id512:${id512}`; + if (petName !== undefined) { + await petStore.write(petName, formulaIdentifier); + } + } else if (!formulaIdentifier.startsWith('host-id512:')) { + throw new Error( + `Existing pet name does not designate a host powers capability: ${q( + petName, + )}`, + ); + } + return /** @type {Promise} */ ( + // Behold, recursion: + // eslint-disable-next-line no-use-before-define + provideValueForFormulaIdentifier(formulaIdentifier) + ); + }; + + /** + * @param {string} webPageName + * @param {string} bundleName + * @param {string | 'NONE' | 'HOST' | 'ENDO'} powersName + */ + const provideWebPage = async (webPageName, bundleName, powersName) => { + const bundleFormulaIdentifier = petStore.get(bundleName); + if (bundleFormulaIdentifier === undefined) { + throw new Error(`Unknown pet name: ${q(bundleName)}`); + } + + const powersFormulaIdentifier = await providePowersFormulaIdentifier( + powersName, + ); + + const digester = makeSha512(); + digester.updateText( + `${bundleFormulaIdentifier},${powersFormulaIdentifier}`, + ); + const formulaNumber = digester.digestHex().slice(32, 64); + + const formula = { + type: 'web-bundle', + bundle: bundleFormulaIdentifier, + powers: powersFormulaIdentifier, + }; + + // Behold, recursion: + // eslint-disable-next-line no-use-before-define + const { value, formulaIdentifier } = await provideValueForNumberedFormula( + 'web-bundle', + formulaNumber, + formula, + ); + + if (webPageName !== undefined) { + await petStore.write(webPageName, formulaIdentifier); + } + + return value; + }; + + const { list, remove, rename } = petStore; + + /** @type {import('./types.js').EndoHost} */ + const host = Far('EndoHost', { + listMessages, + followMessages, + provide, + resolve, + reject, + adopt, + dismiss, + lookup, + list, + remove, + rename, + store, + provideGuest, + provideHost, + makeWorker, + provideWorker, + evaluate, + importUnsafeAndEndow, + importBundleAndEndow, + provideWebPage, + }); + + partyReceiveFunctions.set(host, receive); + partyRequestFunctions.set(host, request); + + return host; + }; + + return makeIdentifiedHost; +}; diff --git a/packages/daemon/src/pet-name.js b/packages/daemon/src/pet-name.js new file mode 100644 index 0000000000..1502133a99 --- /dev/null +++ b/packages/daemon/src/pet-name.js @@ -0,0 +1,12 @@ +const { quote: q } = assert; + +const validNamePattern = /^[a-z][a-z0-9-]{0,127}$/; + +/** + * @param {string} petName + */ +export const assertPetName = petName => { + if (typeof petName !== 'string' || !validNamePattern.test(petName)) { + throw new Error(`Invalid pet name ${q(petName)}`); + } +}; diff --git a/packages/daemon/src/pet-store.js b/packages/daemon/src/pet-store.js index 00becfe5f2..ea835870a2 100644 --- a/packages/daemon/src/pet-store.js +++ b/packages/daemon/src/pet-store.js @@ -1,8 +1,8 @@ import { Far } from '@endo/far'; +import { assertPetName } from './pet-name.js'; const { quote: q } = assert; -const validNamePattern = /^[a-z][a-z0-9-]{0,127}$/; const validIdPattern = /^[0-9a-f]{128}$/; const validFormulaPattern = /^(?:host|pet-store|(?:readable-blob-sha512|worker-id512|pet-store-id512|eval-id512|import-unsafe-id512|import-bundle-id512|host-id512|guest-id512):[0-9a-f]{128}|web-bundle:[0-9a-f]{32})$/; @@ -37,9 +37,7 @@ const makePetStoreAtPath = async (powers, petNameDirectoryPath) => { const fileNames = await powers.readDirectory(petNameDirectoryPath); await Promise.all( fileNames.map(async petName => { - if (!validNamePattern.test(petName)) { - throw new Error(`Invalid pet name ${q(petName)}`); - } + assertPetName(petName); const formulaIdentifier = await read(petName); petNames.set(petName, formulaIdentifier); const formulaPetNames = formulaIdentifiers.get(formulaIdentifier); @@ -53,9 +51,7 @@ const makePetStoreAtPath = async (powers, petNameDirectoryPath) => { /** @param {string} petName */ const get = petName => { - if (!validNamePattern.test(petName)) { - throw new Error(`Invalid pet name ${q(petName)}`); - } + assertPetName(petName); return petNames.get(petName); }; @@ -64,9 +60,7 @@ const makePetStoreAtPath = async (powers, petNameDirectoryPath) => { * @param {string} formulaIdentifier */ const write = async (petName, formulaIdentifier) => { - if (!validNamePattern.test(petName)) { - throw new Error(`Invalid pet name ${q(petName)}`); - } + assertPetName(petName); if (!validFormulaPattern.test(formulaIdentifier)) { throw new Error(`Invalid formula identifier ${q(formulaIdentifier)}`); } @@ -91,9 +85,7 @@ const makePetStoreAtPath = async (powers, petNameDirectoryPath) => { * @param {string} petName */ const remove = async petName => { - if (!validNamePattern.test(petName)) { - throw new Error(`Invalid pet name ${q(petName)}`); - } + assertPetName(petName); const formulaIdentifier = petNames.get(petName); if (formulaIdentifier === undefined) { throw new Error( @@ -120,12 +112,8 @@ const makePetStoreAtPath = async (powers, petNameDirectoryPath) => { * @param {string} toName */ const rename = async (fromName, toName) => { - if (!validNamePattern.test(fromName)) { - throw new Error(`Invalid pet name ${q(fromName)}`); - } - if (!validNamePattern.test(toName)) { - throw new Error(`Invalid pet name ${q(toName)}`); - } + assertPetName(fromName); + assertPetName(toName); if (fromName === toName) { return; } From 28d28bdf4dd0614ab911e92f996d8ae3a8b48706 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Wed, 16 Aug 2023 22:07:33 -0700 Subject: [PATCH 096/234] feat(daemon): Main worker for each host and guest --- packages/daemon/src/daemon.js | 10 ++++++++-- packages/daemon/src/guest.js | 2 ++ packages/daemon/src/host.js | 7 +++---- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/packages/daemon/src/daemon.js b/packages/daemon/src/daemon.js index 9ed9cb7bbe..8b50614f17 100644 --- a/packages/daemon/src/daemon.js +++ b/packages/daemon/src/daemon.js @@ -371,7 +371,8 @@ const makeEndoBootstrap = ( return makeIdentifiedGuest( formulaIdentifier, formula.host, - formula.store, + `pet-store-id512:${formulaNumber}`, + `worker-id512:${formulaNumber}`, ); } else if (formula.type === 'web-bundle') { return harden({ @@ -458,7 +459,11 @@ const makeEndoBootstrap = ( } else if (formulaIdentifier === 'host') { // Behold, recursion: // eslint-disable-next-line no-use-before-define - return makeIdentifiedHost(formulaIdentifier, 'pet-store'); + return makeIdentifiedHost( + formulaIdentifier, + 'pet-store', + `worker-id512:${zero512}`, + ); } else if (formulaIdentifier === 'endo') { // Behold, self-referentiality: // eslint-disable-next-line no-use-before-define @@ -493,6 +498,7 @@ const makeEndoBootstrap = ( return makeIdentifiedHost( formulaIdentifier, `pet-store-id512:${formulaNumber}`, + `worker-id512:${formulaNumber}`, ); } else if ( [ diff --git a/packages/daemon/src/guest.js b/packages/daemon/src/guest.js index a77e9f2347..3646aa1992 100644 --- a/packages/daemon/src/guest.js +++ b/packages/daemon/src/guest.js @@ -12,11 +12,13 @@ export const makeGuestMaker = ({ * @param {string} guestFormulaIdentifier * @param {string} hostFormulaIdentifier * @param {string} petStoreFormulaIdentifier + * @param {string} mainWorkerFormulaIdentifier */ const makeIdentifiedGuest = async ( guestFormulaIdentifier, hostFormulaIdentifier, petStoreFormulaIdentifier, + mainWorkerFormulaIdentifier, ) => { /** @type {Map>} */ const responses = new Map(); diff --git a/packages/daemon/src/host.js b/packages/daemon/src/host.js index 4682bbfdcc..e5f17addce 100644 --- a/packages/daemon/src/host.js +++ b/packages/daemon/src/host.js @@ -6,9 +6,6 @@ import { assertPetName } from './pet-name.js'; const { quote: q } = assert; -const zero512 = - '0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'; - export const makeHostMaker = ({ provideValueForFormulaIdentifier, provideValueForFormula, @@ -23,10 +20,12 @@ export const makeHostMaker = ({ /** * @param {string} hostFormulaIdentifier * @param {string} storeFormulaIdentifier + * @param {string} mainWorkerFormulaIdentifier */ const makeIdentifiedHost = async ( hostFormulaIdentifier, storeFormulaIdentifier, + mainWorkerFormulaIdentifier, ) => { const petStore = /** @type {import('./types.js').PetStore} */ ( // Behold, recursion: @@ -381,7 +380,7 @@ export const makeHostMaker = ({ */ const provideWorkerFormulaIdentifier = async workerName => { if (workerName === 'MAIN') { - return `worker-id512:${zero512}`; + return mainWorkerFormulaIdentifier; } else if (workerName === 'NEW') { const workerId512 = await randomHex512(); return `worker-id512:${workerId512}`; From 22f873e5848e29a118ce0b9bee0620d9e7be3be7 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Thu, 17 Aug 2023 10:59:49 -0700 Subject: [PATCH 097/234] feat(daemon): Implement message send for host and guest parties --- packages/daemon/src/daemon.js | 13 +- packages/daemon/src/guest.js | 153 +++++------ packages/daemon/src/host.js | 325 ++++++----------------- packages/daemon/src/mail.js | 460 +++++++++++++++++++++++++++++++++ packages/daemon/src/types.d.ts | 7 - 5 files changed, 604 insertions(+), 354 deletions(-) create mode 100644 packages/daemon/src/mail.js diff --git a/packages/daemon/src/daemon.js b/packages/daemon/src/daemon.js index 8b50614f17..23bbb86841 100644 --- a/packages/daemon/src/daemon.js +++ b/packages/daemon/src/daemon.js @@ -19,6 +19,7 @@ import { import { makeRefReader } from './ref-reader.js'; import { makeReaderRef } from './reader-ref.js'; import { makeOwnPetStore, makeIdentifiedPetStore } from './pet-store.js'; +import { makeMailboxMaker } from './mail.js'; import { makeGuestMaker } from './guest.js'; import { makeHostMaker } from './host.js'; @@ -60,10 +61,6 @@ const makeEndoBootstrap = ( // reference", and not for "what is my name for this promise". /** @type {WeakMap} */ const formulaIdentifierForRef = new WeakMap(); - /** @type {WeakMap} */ - const partyRequestFunctions = new WeakMap(); - /** @type {WeakMap} */ - const partyReceiveFunctions = new WeakMap(); /** @type {WeakMap>} */ const workerBootstraps = new WeakMap(); @@ -577,10 +574,17 @@ const makeEndoBootstrap = ( return value; }; + const { makeMailbox, partyReceiveFunctions, partyRequestFunctions } = + makeMailboxMaker({ + formulaIdentifierForRef, + provideValueForFormulaIdentifier, + }); + const makeIdentifiedGuest = makeGuestMaker({ provideValueForFormulaIdentifier, partyReceiveFunctions, partyRequestFunctions, + makeMailbox, }); const makeIdentifiedHost = makeHostMaker({ @@ -593,6 +597,7 @@ const makeEndoBootstrap = ( storeReaderRef, randomHex512, makeSha512, + makeMailbox, }); const endoBootstrap = Far('Endo private facet', { diff --git a/packages/daemon/src/guest.js b/packages/daemon/src/guest.js index 3646aa1992..de458faee2 100644 --- a/packages/daemon/src/guest.js +++ b/packages/daemon/src/guest.js @@ -7,6 +7,7 @@ export const makeGuestMaker = ({ provideValueForFormulaIdentifier, partyReceiveFunctions, partyRequestFunctions, + makeMailbox, }) => { /** * @param {string} guestFormulaIdentifier @@ -20,29 +21,49 @@ export const makeGuestMaker = ({ petStoreFormulaIdentifier, mainWorkerFormulaIdentifier, ) => { - /** @type {Map>} */ - const responses = new Map(); - - const guestPetStore = /** @type {import('./types.js').PetStore} */ ( + const petStore = /** @type {import('./types.js').PetStore} */ ( await provideValueForFormulaIdentifier(petStoreFormulaIdentifier) ); const host = /** @type {object} */ ( await provideValueForFormulaIdentifier(hostFormulaIdentifier) ); - const hostRequest = partyRequestFunctions.get(host); - if (hostRequest === undefined) { + const deliverToHost = partyRequestFunctions.get(host); + if (deliverToHost === undefined) { throw new Error( `panic: a host request function must exist for every host`, ); } + const { + lookup, + lookupFormulaIdentifierForName, + followMessages, + listMessages, + resolve, + reject, + dismiss, + adopt, + sendMail, + receiveMail, + receiveRequest, + sendRequest, + rename, + remove, + } = makeMailbox({ + petStore, + specialNames: { + SELF: guestFormulaIdentifier, + HOST: hostFormulaIdentifier, + }, + }); + /** * @param {string} petName */ const provide = async petName => { assertPetName(petName); - const formulaIdentifier = guestPetStore.get(petName); + const formulaIdentifier = lookupFormulaIdentifierForName(petName); if (formulaIdentifier === undefined) { throw new TypeError(`Unknown pet name: ${q(petName)}`); } @@ -51,98 +72,39 @@ export const makeGuestMaker = ({ return provideValueForFormulaIdentifier(formulaIdentifier); }; - /** - * @param {string} fromName - * @param {string} toName - */ - const rename = async (fromName, toName) => { - assertPetName(fromName); - assertPetName(toName); - await guestPetStore.rename(fromName, toName); - const formulaIdentifier = responses.get(fromName); - if (formulaIdentifier === undefined) { - throw new Error( - `panic: the pet store rename must ensure that the renamed identifier exists`, - ); - } - responses.set(toName, formulaIdentifier); - responses.delete(fromName); - }; + const { list } = petStore; - /** - * @param {string} petName - */ - const remove = async petName => { - await guestPetStore.remove(petName); - responses.delete(petName); - }; - - const { list } = guestPetStore; - - const request = async (what, responseName) => { - if (responseName === undefined) { - // Behold, recursion: - // eslint-disable-next-line no-use-before-define - return hostRequest( - what, - responseName, - guestFormulaIdentifier, - guestPetStore, - ); - } - const responseP = responses.get(responseName); - if (responseP !== undefined) { - return responseP; - } - // Behold, recursion: - // eslint-disable-next-line - const newResponseP = hostRequest( - what, - responseName, + const receive = (strings, edgeNames, petNames) => { + return sendMail( guestFormulaIdentifier, - guestPetStore, + hostFormulaIdentifier, + strings, + edgeNames, + petNames, ); - responses.set(responseName, newResponseP); - return newResponseP; }; - /** - * @param {Array} strings - * @param {Array} edgeNames - * @param {Array} petNames - */ - const receive = async (strings, edgeNames, petNames) => { - petNames.forEach(assertPetName); - edgeNames.forEach(assertPetName); - if (petNames.length !== edgeNames.length) { - throw new Error( - `Message must have one edge name (${q( - edgeNames.length, - )}) for every pet name (${q(petNames.length)})`, - ); - } - if (strings.length < petNames.length) { - throw new Error( - `Message must have one string before every value delivered`, - ); - } - - const partyReceive = partyReceiveFunctions.get(host); - if (partyReceive === undefined) { - throw new Error(`panic: Message not deliverable`); + const send = async (recipientName, strings, edgeNames, petNames) => { + const recipientFormulaIdentifier = + lookupFormulaIdentifierForName(recipientName); + if (recipientFormulaIdentifier === undefined) { + throw new Error(`Unknown pet name for party: ${recipientName}`); } - const formulaIdentifiers = petNames.map(petName => { - const formulaIdentifier = guestPetStore.get(petName); - if (formulaIdentifier === undefined) { - throw new Error(`Unknown pet name ${q(petName)}`); - } - return formulaIdentifier; - }); - partyReceive( + return sendMail( guestFormulaIdentifier, + recipientFormulaIdentifier, strings, edgeNames, - formulaIdentifiers, + petNames, + ); + }; + + const request = async (what, responseName) => { + return sendRequest( + guestFormulaIdentifier, + hostFormulaIdentifier, + what, + responseName, ); }; @@ -150,12 +112,23 @@ export const makeGuestMaker = ({ const guest = Far('EndoGuest', { request, receive, + send, list, + followMessages, + listMessages, + resolve, + reject, + dismiss, + adopt, remove, rename, provide, + lookup, }); + partyReceiveFunctions.set(guest, receiveMail); + partyRequestFunctions.set(guest, receiveRequest); + return guest; }; diff --git a/packages/daemon/src/host.js b/packages/daemon/src/host.js index e5f17addce..e36363a538 100644 --- a/packages/daemon/src/host.js +++ b/packages/daemon/src/host.js @@ -1,7 +1,4 @@ -import { makePromiseKit } from '@endo/promise-kit'; -import { E, Far } from '@endo/far'; -import { makeIteratorRef } from './reader-ref.js'; -import { makeChangeTopic } from './pubsub.js'; +import { Far } from '@endo/far'; import { assertPetName } from './pet-name.js'; const { quote: q } = assert; @@ -16,6 +13,7 @@ export const makeHostMaker = ({ storeReaderRef, makeSha512, randomHex512, + makeMailbox, }) => { /** * @param {string} hostFormulaIdentifier @@ -33,247 +31,66 @@ export const makeHostMaker = ({ await provideValueForFormulaIdentifier(storeFormulaIdentifier) ); - /** @type {Map} */ - const messages = new Map(); - /** @type {WeakMap void>} */ - const resolvers = new WeakMap(); - /** @type {WeakMap void>} */ - const dismissers = new WeakMap(); - /** @type {import('./types.js').Topic} */ - const messagesTopic = makeChangeTopic(); - let nextMessageNumber = 0; - - /** - * @param {import('./types.js').InternalMessage} message - * @returns {import('./types.js').Message} - */ - const dubMessage = message => { - if (message.type === 'request') { - const { who: senderFormula, ...rest } = message; - const [senderName] = petStore.lookup(senderFormula); - if (senderName !== undefined) { - return { who: senderName, ...rest }; - } - } else if (message.type === 'package') { - const { formulas: _, who: senderFormula, ...rest } = message; - const [senderName] = petStore.lookup(senderFormula); - if (senderName !== undefined) { - return { who: senderName, ...rest }; - } - } - throw new Error(`panic: Unknown message type ${message.type}`); - }; - - const listMessages = async () => - harden(Array.from(messages.values(), dubMessage)); - - const followMessages = async () => - makeIteratorRef( - (async function* currentAndSubsequentMessages() { - const subsequentRequests = messagesTopic.subscribe(); - for (const message of messages.values()) { - yield dubMessage(message); - } - for await (const message of subsequentRequests) { - yield dubMessage(message); - } - })(), - ); + const { + lookup, + lookupFormulaIdentifierForName, + listMessages, + followMessages, + resolve, + reject, + receiveRequest: sendRequest, + sendRequest: receiveRequest, + receiveMail: sendMail, + sendMail: receiveMail, + dismiss, + adopt, + rename, + remove, + } = makeMailbox({ + petStore, + specialNames: { + SELF: hostFormulaIdentifier, + }, + }); /** - * @param {string} what - user visible description of the desired value - * @param {string} guestFormulaIdentifier + * @param {string} recipientName + * @param {Array} strings + * @param {Array} edgeNames + * @param {Array} petNames */ - const requestFormulaIdentifier = async (what, guestFormulaIdentifier) => { - /** @type {import('@endo/promise-kit/src/types.js').PromiseKit} */ - const { promise, resolve } = makePromiseKit(); - const messageNumber = nextMessageNumber; - nextMessageNumber += 1; - const settle = () => { - messages.delete(messageNumber); - }; - const settled = promise.then( - () => { - settle(); - return /** @type {'fulfilled'} */ ('fulfilled'); - }, - () => { - settle(); - return /** @type {'rejected'} */ ('rejected'); - }, + const send = async (recipientName, strings, edgeNames, petNames) => { + const recipentFormulaIdentifier = + lookupFormulaIdentifierForName(recipientName); + if (recipentFormulaIdentifier === undefined) { + throw new Error(`Unknown pet name for party: ${recipientName}`); + } + return receiveMail( + hostFormulaIdentifier, + recipentFormulaIdentifier, + strings, + edgeNames, + petNames, ); - - const req = harden({ - type: /** @type {'request'} */ ('request'), - number: messageNumber, - who: guestFormulaIdentifier, - what, - when: new Date().toISOString(), - settled, - }); - - messages.set(messageNumber, req); - resolvers.set(req, resolve); - messagesTopic.publisher.next(req); - return promise; }; /** + * @param {string} recipientName * @param {string} what * @param {string} responseName - * @param {string} guestFormulaIdentifier - * @param {import('./types.js').PetStore} guestPetStore */ - const request = async ( - what, - responseName, - guestFormulaIdentifier, - guestPetStore, - ) => { - if (responseName !== undefined) { - /** @type {string | undefined} */ - let formulaIdentifier = guestPetStore.get(responseName); - if (formulaIdentifier === undefined) { - formulaIdentifier = await requestFormulaIdentifier( - what, - guestFormulaIdentifier, - ); - await guestPetStore.write(responseName, formulaIdentifier); - } - // Behold, recursion: - // eslint-disable-next-line no-use-before-define - return provideValueForFormulaIdentifier(formulaIdentifier); - } - // The reference is not named nor to be named. - const formulaIdentifier = await requestFormulaIdentifier( + const request = async (recipientName, what, responseName) => { + const recipentFormulaIdentifier = + lookupFormulaIdentifierForName(recipientName); + if (recipentFormulaIdentifier === undefined) { + throw new Error(`Unknown pet name for party: ${recipientName}`); + } + return receiveRequest( + hostFormulaIdentifier, + recipentFormulaIdentifier, what, - guestFormulaIdentifier, + responseName, ); - // Behold, recursion: - // eslint-disable-next-line no-use-before-define - return provideValueForFormulaIdentifier(formulaIdentifier); - }; - - const resolve = async (messageNumber, resolutionName) => { - assertPetName(resolutionName); - if ( - typeof messageNumber !== 'number' || - messageNumber >= Number.MAX_SAFE_INTEGER - ) { - throw new Error(`Invalid request number ${q(messageNumber)}`); - } - const req = messages.get(messageNumber); - const resolveRequest = resolvers.get(req); - if (resolveRequest === undefined) { - throw new Error(`No pending request for number ${messageNumber}`); - } - const formulaIdentifier = petStore.get(resolutionName); - if (formulaIdentifier === undefined) { - throw new TypeError( - `No formula exists for the pet name ${q(resolutionName)}`, - ); - } - resolveRequest(formulaIdentifier); - }; - - /** - * @param {string} senderFormulaIdentifier - * @param {Array} strings - * @param {Array} edgeNames - * @param {Array} formulaIdentifiers - */ - const receive = ( - senderFormulaIdentifier, - strings, - edgeNames, - formulaIdentifiers, - ) => { - /** @type {import('@endo/promise-kit/src/types.js').PromiseKit} */ - const dismissal = makePromiseKit(); - const messageNumber = nextMessageNumber; - nextMessageNumber += 1; - - const message = harden({ - type: /** @type {const} */ ('package'), - number: messageNumber, - strings, - names: edgeNames, - formulas: formulaIdentifiers, // TODO should not be visible to recipient - who: senderFormulaIdentifier, - when: new Date().toISOString(), - dismissed: dismissal.promise, - }); - - messages.set(messageNumber, message); - dismissers.set(message, () => { - messages.delete(messageNumber); - dismissal.resolve(); - }); - messagesTopic.publisher.next(message); - }; - - const dismiss = async messageNumber => { - if ( - typeof messageNumber !== 'number' || - messageNumber >= Number.MAX_SAFE_INTEGER - ) { - throw new Error(`Invalid request number ${q(messageNumber)}`); - } - const message = messages.get(messageNumber); - const dismissMessage = dismissers.get(message); - if (dismissMessage === undefined) { - throw new Error(`No dismissable message for number ${messageNumber}`); - } - dismissMessage(); - }; - - const adopt = async (messageNumber, edgeName, petName) => { - assertPetName(edgeName); - assertPetName(petName); - if ( - typeof messageNumber !== 'number' || - messageNumber >= Number.MAX_SAFE_INTEGER - ) { - throw new Error(`Invalid message number ${q(messageNumber)}`); - } - const message = messages.get(messageNumber); - if (message === undefined) { - throw new Error(`No such message with number ${q(messageNumber)}`); - } - if (message.type !== 'package') { - throw new Error(`Message must be a package ${q(messageNumber)}`); - } - const index = message.names.lastIndexOf(edgeName); - if (index === -1) { - throw new Error( - `No reference named ${q(edgeName)} in message ${q(messageNumber)}`, - ); - } - const formulaIdentifier = message.formulas[index]; - if (formulaIdentifier === undefined) { - throw new Error( - `panic: message must contain a formula for every name, including the name ${q( - edgeName, - )} at ${q(index)}`, - ); - } - await petStore.write(petName, formulaIdentifier); - }; - - // TODO test reject - /** - * @param {number} messageNumber - * @param {string} [message] - */ - const reject = async (messageNumber, message = 'Declined') => { - const req = messages.get(messageNumber); - if (req !== undefined) { - const resolveRequest = resolvers.get(req); - if (resolveRequest === undefined) { - throw new Error(`panic: a resolver must exist for every request`); - } - resolveRequest(harden(Promise.reject(harden(new Error(message))))); - } }; /** @@ -283,7 +100,7 @@ export const makeHostMaker = ({ /** @type {string | undefined} */ let formulaIdentifier; if (petName !== undefined) { - formulaIdentifier = petStore.get(petName); + formulaIdentifier = lookupFormulaIdentifierForName(petName); } if (formulaIdentifier === undefined) { const id512 = await randomHex512(); @@ -299,6 +116,7 @@ export const makeHostMaker = ({ // eslint-disable-next-line no-use-before-define await provideValueForFormula(formula, 'guest-id512'); if (petName !== undefined) { + assertPetName(petName); await petStore.write(petName, guestFormulaIdentifier); } return value; @@ -336,7 +154,7 @@ export const makeHostMaker = ({ * @param {string} petName */ const provide = async petName => { - const formulaIdentifier = petStore.get(petName); + const formulaIdentifier = lookupFormulaIdentifierForName(petName); if (formulaIdentifier === undefined) { throw new TypeError(`Unknown pet name: ${q(petName)}`); } @@ -352,10 +170,11 @@ export const makeHostMaker = ({ if (typeof workerName !== 'string') { throw new Error('worker name must be string'); } - let workerFormulaIdentifier = petStore.get(workerName); + let workerFormulaIdentifier = lookupFormulaIdentifierForName(workerName); if (workerFormulaIdentifier === undefined) { const workerId512 = await randomHex512(); workerFormulaIdentifier = `worker-id512:${workerId512}`; + assertPetName(workerName); await petStore.write(workerName, workerFormulaIdentifier); } else if (!workerFormulaIdentifier.startsWith('worker-id512:')) { throw new Error(`Not a worker ${q(workerName)}`); @@ -367,14 +186,6 @@ export const makeHostMaker = ({ ); }; - const lookup = async presence => { - const formulaIdentifier = formulaIdentifierForRef.get(await presence); - if (formulaIdentifier === undefined) { - return []; - } - return E(petStore).lookup(formulaIdentifier); - }; - /** * @param {string | 'MAIN' | 'NEW'} workerName */ @@ -386,10 +197,11 @@ export const makeHostMaker = ({ return `worker-id512:${workerId512}`; } assertPetName(workerName); - let workerFormulaIdentifier = petStore.get(workerName); + let workerFormulaIdentifier = lookupFormulaIdentifierForName(workerName); if (workerFormulaIdentifier === undefined) { const workerId512 = await randomHex512(); workerFormulaIdentifier = `worker-id512:${workerId512}`; + assertPetName(workerName); await petStore.write(workerName, workerFormulaIdentifier); } return workerFormulaIdentifier; @@ -407,7 +219,7 @@ export const makeHostMaker = ({ return 'endo'; } assertPetName(partyName); - let guestFormulaIdentifier = petStore.get(partyName); + let guestFormulaIdentifier = lookupFormulaIdentifierForName(partyName); if (guestFormulaIdentifier === undefined) { const guest = await provideGuest(partyName); guestFormulaIdentifier = formulaIdentifierForRef.get(guest); @@ -451,7 +263,7 @@ export const makeHostMaker = ({ if (typeof codeNames[index] !== 'string') { throw new Error(`Invalid endowment name: ${q(codeNames[index])}`); } - const formulaIdentifier = petStore.get(petName); + const formulaIdentifier = lookupFormulaIdentifierForName(petName); if (formulaIdentifier === undefined) { throw new Error(`Unknown pet name ${q(petName)}`); } @@ -536,7 +348,8 @@ export const makeHostMaker = ({ workerName, ); - const bundleFormulaIdentifier = petStore.get(bundleName); + const bundleFormulaIdentifier = + lookupFormulaIdentifierForName(bundleName); if (bundleFormulaIdentifier === undefined) { throw new TypeError(`Unknown pet name for bundle: ${bundleName}`); } @@ -574,6 +387,7 @@ export const makeHostMaker = ({ const workerId512 = await randomHex512(); const formulaIdentifier = `worker-id512:${workerId512}`; if (petName !== undefined) { + assertPetName(petName); await petStore.write(petName, formulaIdentifier); } return /** @type {Promise} */ ( @@ -590,12 +404,13 @@ export const makeHostMaker = ({ /** @type {string | undefined} */ let formulaIdentifier; if (petName !== undefined) { - formulaIdentifier = petStore.get(petName); + formulaIdentifier = lookupFormulaIdentifierForName(petName); } if (formulaIdentifier === undefined) { const id512 = await randomHex512(); formulaIdentifier = `host-id512:${id512}`; if (petName !== undefined) { + assertPetName(petName); await petStore.write(petName, formulaIdentifier); } } else if (!formulaIdentifier.startsWith('host-id512:')) { @@ -618,7 +433,8 @@ export const makeHostMaker = ({ * @param {string | 'NONE' | 'HOST' | 'ENDO'} powersName */ const provideWebPage = async (webPageName, bundleName, powersName) => { - const bundleFormulaIdentifier = petStore.get(bundleName); + const bundleFormulaIdentifier = + lookupFormulaIdentifierForName(bundleName); if (bundleFormulaIdentifier === undefined) { throw new Error(`Unknown pet name: ${q(bundleName)}`); } @@ -648,13 +464,14 @@ export const makeHostMaker = ({ ); if (webPageName !== undefined) { + assertPetName(webPageName); await petStore.write(webPageName, formulaIdentifier); } return value; }; - const { list, remove, rename } = petStore; + const { list } = petStore; /** @type {import('./types.js').EndoHost} */ const host = Far('EndoHost', { @@ -665,6 +482,8 @@ export const makeHostMaker = ({ reject, adopt, dismiss, + request, + send, lookup, list, remove, @@ -680,8 +499,8 @@ export const makeHostMaker = ({ provideWebPage, }); - partyReceiveFunctions.set(host, receive); - partyRequestFunctions.set(host, request); + partyReceiveFunctions.set(host, sendMail); + partyRequestFunctions.set(host, sendRequest); return host; }; diff --git a/packages/daemon/src/mail.js b/packages/daemon/src/mail.js new file mode 100644 index 0000000000..8828a5ec6d --- /dev/null +++ b/packages/daemon/src/mail.js @@ -0,0 +1,460 @@ +import { makePromiseKit } from '@endo/promise-kit'; +import { makeChangeTopic } from './pubsub.js'; +import { makeIteratorRef } from './reader-ref.js'; +import { assertPetName } from './pet-name.js'; + +const { quote: q } = assert; + +export const makeMailboxMaker = ({ + provideValueForFormulaIdentifier, + formulaIdentifierForRef, +}) => { + /** @type {WeakMap} */ + const partyRequestFunctions = new WeakMap(); + /** @type {WeakMap} */ + const partyReceiveFunctions = new WeakMap(); + + const makeMailbox = ({ petStore, specialNames }) => { + /** @type {Map>} */ + const responses = new Map(); + /** @type {Map} */ + const messages = new Map(); + /** @type {WeakMap void>} */ + const resolvers = new WeakMap(); + /** @type {WeakMap void>} */ + const dismissers = new WeakMap(); + /** @type {import('./types.js').Topic} */ + const messagesTopic = makeChangeTopic(); + let nextMessageNumber = 0; + + /** + * @param {string} petName + */ + const lookupFormulaIdentifierForName = petName => { + if (Object.hasOwn(specialNames, petName)) { + return specialNames[petName]; + } + return petStore.get(petName); + }; + + /** + * @param {string} formulaIdentifier + */ + const lookupNamesForFormulaIdentifier = formulaIdentifier => { + const names = Array.from(petStore.lookup(formulaIdentifier)); + for (const [specialName, specialFormulaIdentifier] of Object.entries( + specialNames, + )) { + if (specialFormulaIdentifier === formulaIdentifier) { + names.push(specialName); + } + } + return harden(names); + }; + + /** + * @param {unknown} presence + */ + const lookup = async presence => { + const formulaIdentifier = formulaIdentifierForRef.get(await presence); + if (formulaIdentifier === undefined) { + return harden([]); + } + return lookupNamesForFormulaIdentifier(formulaIdentifier); + }; + + /** + * @param {import('./types.js').InternalMessage} message + * @returns {import('./types.js').Message | undefined} + */ + const dubMessage = message => { + if (message.type === 'request') { + const { who: senderFormulaIdentifier, ...rest } = message; + const [senderName] = lookupNamesForFormulaIdentifier( + senderFormulaIdentifier, + ); + if (senderName !== undefined) { + return { who: senderName, ...rest }; + } + return undefined; + } else if (message.type === 'package') { + const { formulas: _, who: senderFormulaIdentifier, ...rest } = message; + const [senderName] = lookupNamesForFormulaIdentifier( + senderFormulaIdentifier, + ); + if (senderName !== undefined) { + return { who: senderName, ...rest }; + } + return undefined; + } + throw new Error(`panic: Unknown message type ${message.type}`); + }; + + const listMessages = async () => + harden(Array.from(messages.values(), dubMessage)); + + const followMessages = async () => + makeIteratorRef( + (async function* currentAndSubsequentMessages() { + const subsequentRequests = messagesTopic.subscribe(); + for (const message of messages.values()) { + const dubbedMessage = dubMessage(message); + if (dubbedMessage !== undefined) { + yield dubMessage(message); + } + } + for await (const message of subsequentRequests) { + const dubbedMessage = dubMessage(message); + if (dubbedMessage !== undefined) { + yield dubMessage(message); + } + } + })(), + ); + + /** + * @param {string} what - user visible description of the desired value + * @param {string} guestFormulaIdentifier + */ + const requestFormulaIdentifier = async (what, guestFormulaIdentifier) => { + /** @type {import('@endo/promise-kit/src/types.js').PromiseKit} */ + const { promise, resolve } = makePromiseKit(); + const messageNumber = nextMessageNumber; + nextMessageNumber += 1; + const settle = () => { + messages.delete(messageNumber); + }; + const settled = promise.then( + () => { + settle(); + return /** @type {'fulfilled'} */ ('fulfilled'); + }, + () => { + settle(); + return /** @type {'rejected'} */ ('rejected'); + }, + ); + + const req = harden({ + type: /** @type {'request'} */ ('request'), + number: messageNumber, + who: guestFormulaIdentifier, + what, + when: new Date().toISOString(), + settled, + }); + + messages.set(messageNumber, req); + resolvers.set(req, resolve); + messagesTopic.publisher.next(req); + return promise; + }; + + /** + * @param {string} what + * @param {string} responseName + * @param {string} senderFormulaIdentifier + * @param {import('./types.js').PetStore} senderPetStore + */ + const receiveRequest = async ( + what, + responseName, + senderFormulaIdentifier, + senderPetStore, + ) => { + if (responseName !== undefined) { + /** @type {string | undefined} */ + let formulaIdentifier = senderPetStore.get(responseName); + if (formulaIdentifier === undefined) { + formulaIdentifier = await requestFormulaIdentifier( + what, + senderFormulaIdentifier, + ); + await senderPetStore.write(responseName, formulaIdentifier); + } + // Behold, recursion: + // eslint-disable-next-line no-use-before-define + return provideValueForFormulaIdentifier(formulaIdentifier); + } + // The reference is not named nor to be named. + const formulaIdentifier = await requestFormulaIdentifier( + what, + senderFormulaIdentifier, + ); + // Behold, recursion: + // eslint-disable-next-line no-use-before-define + return provideValueForFormulaIdentifier(formulaIdentifier); + }; + + const resolve = async (messageNumber, resolutionName) => { + assertPetName(resolutionName); + if ( + typeof messageNumber !== 'number' || + messageNumber >= Number.MAX_SAFE_INTEGER + ) { + throw new Error(`Invalid request number ${q(messageNumber)}`); + } + const req = messages.get(messageNumber); + const resolveRequest = resolvers.get(req); + if (resolveRequest === undefined) { + throw new Error(`No pending request for number ${messageNumber}`); + } + const formulaIdentifier = lookupFormulaIdentifierForName(resolutionName); + if (formulaIdentifier === undefined) { + throw new TypeError( + `No formula exists for the pet name ${q(resolutionName)}`, + ); + } + resolveRequest(formulaIdentifier); + }; + + // TODO test reject + /** + * @param {number} messageNumber + * @param {string} [message] + */ + const reject = async (messageNumber, message = 'Declined') => { + const req = messages.get(messageNumber); + if (req !== undefined) { + const resolveRequest = resolvers.get(req); + if (resolveRequest === undefined) { + throw new Error(`panic: a resolver must exist for every request`); + } + resolveRequest(harden(Promise.reject(harden(new Error(message))))); + } + }; + + /** + * @param {string} senderFormulaIdentifier + * @param {Array} strings + * @param {Array} edgeNames + * @param {Array} formulaIdentifiers + */ + const receiveMail = ( + senderFormulaIdentifier, + strings, + edgeNames, + formulaIdentifiers, + ) => { + /** @type {import('@endo/promise-kit/src/types.js').PromiseKit} */ + const dismissal = makePromiseKit(); + const messageNumber = nextMessageNumber; + nextMessageNumber += 1; + + const message = harden({ + type: /** @type {const} */ ('package'), + number: messageNumber, + strings, + names: edgeNames, + formulas: formulaIdentifiers, + who: senderFormulaIdentifier, + when: new Date().toISOString(), + dismissed: dismissal.promise, + }); + + messages.set(messageNumber, message); + dismissers.set(message, () => { + messages.delete(messageNumber); + dismissal.resolve(); + }); + messagesTopic.publisher.next(message); + }; + + /** + * @param {string} senderFormulaIdentifier + * @param {object} receiverFormulaIdentifier + * @param {Array} strings + * @param {Array} edgeNames + * @param {Array} petNames + */ + const sendMail = async ( + senderFormulaIdentifier, + receiverFormulaIdentifier, + strings, + edgeNames, + petNames, + ) => { + const receiver = await provideValueForFormulaIdentifier( + receiverFormulaIdentifier, + ); + petNames.forEach(assertPetName); + edgeNames.forEach(assertPetName); + if (petNames.length !== edgeNames.length) { + throw new Error( + `Message must have one edge name (${q( + edgeNames.length, + )}) for every pet name (${q(petNames.length)})`, + ); + } + if (strings.length < petNames.length) { + throw new Error( + `Message must have one string before every value delivered`, + ); + } + + const partyReceive = partyReceiveFunctions.get(receiver); + if (partyReceive === undefined) { + throw new Error(`panic: Message not deliverable`); + } + const formulaIdentifiers = petNames.map(petName => { + const formulaIdentifier = lookupFormulaIdentifierForName(petName); + if (formulaIdentifier === undefined) { + throw new Error(`Unknown pet name ${q(petName)}`); + } + return formulaIdentifier; + }); + partyReceive( + senderFormulaIdentifier, + strings, + edgeNames, + formulaIdentifiers, + ); + }; + + const dismiss = async messageNumber => { + if ( + typeof messageNumber !== 'number' || + messageNumber >= Number.MAX_SAFE_INTEGER + ) { + throw new Error(`Invalid request number ${q(messageNumber)}`); + } + const message = messages.get(messageNumber); + const dismissMessage = dismissers.get(message); + if (dismissMessage === undefined) { + throw new Error(`No dismissable message for number ${messageNumber}`); + } + dismissMessage(); + }; + + const adopt = async (messageNumber, edgeName, petName) => { + assertPetName(edgeName); + assertPetName(petName); + if ( + typeof messageNumber !== 'number' || + messageNumber >= Number.MAX_SAFE_INTEGER + ) { + throw new Error(`Invalid message number ${q(messageNumber)}`); + } + const message = messages.get(messageNumber); + if (message === undefined) { + throw new Error(`No such message with number ${q(messageNumber)}`); + } + if (message.type !== 'package') { + throw new Error(`Message must be a package ${q(messageNumber)}`); + } + const index = message.names.lastIndexOf(edgeName); + if (index === -1) { + throw new Error( + `No reference named ${q(edgeName)} in message ${q(messageNumber)}`, + ); + } + const formulaIdentifier = message.formulas[index]; + if (formulaIdentifier === undefined) { + throw new Error( + `panic: message must contain a formula for every name, including the name ${q( + edgeName, + )} at ${q(index)}`, + ); + } + await petStore.write(petName, formulaIdentifier); + }; + + /** + * @param {string} senderFormulaIdentifier + * @param {object} receiverFormulaIdentifier + * @param {string} what + * @param {string} responseName + */ + const sendRequest = async ( + senderFormulaIdentifier, + receiverFormulaIdentifier, + what, + responseName, + ) => { + const receiver = /** @type {object} */ ( + await provideValueForFormulaIdentifier(receiverFormulaIdentifier) + ); + + const deliverToRecipient = partyRequestFunctions.get(receiver); + if (deliverToRecipient === undefined) { + throw new Error( + `panic: a receive request function must exist for every party`, + ); + } + if (responseName === undefined) { + // Behold, recursion: + // eslint-disable-next-line no-use-before-define + return deliverToRecipient( + what, + responseName, + senderFormulaIdentifier, + petStore, + ); + } + const responseP = responses.get(responseName); + if (responseP !== undefined) { + return responseP; + } + // Behold, recursion: + // eslint-disable-next-line + const newResponseP = deliverToRecipient( + what, + responseName, + senderFormulaIdentifier, + petStore, + ); + responses.set(responseName, newResponseP); + return newResponseP; + }; + + /** + * @param {string} fromName + * @param {string} toName + */ + const rename = async (fromName, toName) => { + assertPetName(fromName); + assertPetName(toName); + await petStore.rename(fromName, toName); + const formulaIdentifier = responses.get(fromName); + if (formulaIdentifier === undefined) { + throw new Error( + `panic: the pet store rename must ensure that the renamed identifier exists`, + ); + } + responses.set(toName, formulaIdentifier); + responses.delete(fromName); + }; + + /** + * @param {string} petName + */ + const remove = async petName => { + await petStore.remove(petName); + responses.delete(petName); + }; + + return harden({ + lookup, + lookupNamesForFormulaIdentifier, + lookupFormulaIdentifierForName, + followMessages, + listMessages, + receiveRequest, + sendRequest, + resolve, + reject, + receiveMail, + sendMail, + dismiss, + adopt, + rename, + remove, + }); + }; + + return { + makeMailbox, + partyRequestFunctions, + partyReceiveFunctions, + }; +}; diff --git a/packages/daemon/src/types.d.ts b/packages/daemon/src/types.d.ts index 475ff04668..3538e18319 100644 --- a/packages/daemon/src/types.d.ts +++ b/packages/daemon/src/types.d.ts @@ -93,14 +93,8 @@ export type MignonicPowers = { }; }; -type HostFormula = { - type: 'host'; - store: string; -}; - type GuestFormula = { type: 'guest'; - store: string; host: string; }; @@ -136,7 +130,6 @@ type WebBundleFormula = { }; export type Formula = - | HostFormula | GuestFormula | EvalFormula | ImportUnsafeFormula From e4d64e2bad7f39f14e965ccfe5cf5013d90b0e87 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Thu, 17 Aug 2023 10:57:28 -0700 Subject: [PATCH 098/234] feat(cli): Add send command --- packages/cli/src/endo.js | 15 +++++++++++++++ packages/cli/src/send.js | 12 ++++++++++++ 2 files changed, 27 insertions(+) create mode 100644 packages/cli/src/send.js diff --git a/packages/cli/src/endo.js b/packages/cli/src/endo.js index 819b9bc955..e18111eaaa 100644 --- a/packages/cli/src/endo.js +++ b/packages/cli/src/endo.js @@ -403,6 +403,21 @@ export const main = async rawArgs => { }); }); + program + .command('send ') + .description('delivers a message to the underlying host') + .option( + '-a,--as ', + 'Pose as named party (as named by current party)', + collect, + [], + ) + .action(async (partyName, message, cmd) => { + const { as: partyNames } = cmd.opts(); + const { send } = await import('./send.js'); + return send({ message, partyName, partyNames }); + }); + program .command('receive ') .description('delivers a message to the underlying host') diff --git a/packages/cli/src/send.js b/packages/cli/src/send.js new file mode 100644 index 0000000000..203850aea5 --- /dev/null +++ b/packages/cli/src/send.js @@ -0,0 +1,12 @@ +/* global process */ +import os from 'os'; +import { E } from '@endo/far'; +import { withEndoParty } from './context.js'; +import { parseMessage } from './message-parse.js'; + +export const send = async ({ message, partyName, partyNames }) => { + const { strings, edgeNames, petNames } = parseMessage(message); + await withEndoParty(partyNames, { os, process }, async ({ party }) => { + await E(party).send(partyName, strings, edgeNames, petNames); + }); +}; From 341fbab5c8c1dcaaf8585ab20cd38de88b782c4e Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Thu, 17 Aug 2023 11:00:10 -0700 Subject: [PATCH 099/234] fix(daemon): Better test teardown --- packages/daemon/test/test-endo.js | 32 ++++++++++++++++++++++++------- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/packages/daemon/test/test-endo.js b/packages/daemon/test/test-endo.js index 5fdb7afb93..ad316f38e7 100644 --- a/packages/daemon/test/test-endo.js +++ b/packages/daemon/test/test-endo.js @@ -43,6 +43,7 @@ const makeLocator = (...root) => { test('lifecycle', async t => { const { reject: cancel, promise: cancelled } = makePromiseKit(); + t.teardown(() => cancel(Error('teardown'))); const locator = makeLocator('tmp', 'lifecycle'); await stop(locator).catch(() => {}); @@ -66,11 +67,13 @@ test('lifecycle', async t => { cancel(new Error('Cancelled')); await closed.catch(() => {}); + await stop(locator); t.pass(); }); test('spawn and evaluate', async t => { - const { promise: cancelled } = makePromiseKit(); + const { promise: cancelled, reject: cancel } = makePromiseKit(); + t.teardown(() => cancel(Error('teardown'))); const locator = makeLocator('tmp', 'spawn-eval'); await stop(locator).catch(() => {}); @@ -92,7 +95,8 @@ test('spawn and evaluate', async t => { }); test('anonymous spawn and evaluate', async t => { - const { promise: cancelled } = makePromiseKit(); + const { promise: cancelled, reject: cancel } = makePromiseKit(); + t.teardown(() => cancel(Error('teardown'))); const locator = makeLocator('tmp', 'spawn-eval-anon'); await stop(locator).catch(() => {}); @@ -113,7 +117,8 @@ test('anonymous spawn and evaluate', async t => { }); test('persist spawn and evaluation', async t => { - const { promise: cancelled } = makePromiseKit(); + const { promise: cancelled, reject: cancel } = makePromiseKit(); + t.teardown(() => cancel(Error('teardown'))); const locator = makeLocator('tmp', 'persist-spawn-eval'); await stop(locator).catch(() => {}); @@ -169,7 +174,8 @@ test('persist spawn and evaluation', async t => { }); test('store', async t => { - const { promise: cancelled } = makePromiseKit(); + const { promise: cancelled, reject: cancel } = makePromiseKit(); + t.teardown(() => cancel(Error('teardown'))); const locator = makeLocator('tmp', 'store'); await stop(locator).catch(() => {}); @@ -200,10 +206,13 @@ test('store', async t => { const actualText = await E(readable).text(); t.is(actualText, 'hello\n'); } + + await stop(locator); }); test('closure state lost by restart', async t => { - const { promise: cancelled } = makePromiseKit(); + const { promise: cancelled, reject: cancel } = makePromiseKit(); + t.teardown(() => cancel(Error('teardown'))); const locator = makeLocator('tmp', 'restart-closures'); await stop(locator).catch(() => {}); @@ -297,10 +306,13 @@ test('closure state lost by restart', async t => { t.is(two, 2); t.is(three, 3); } + + await stop(locator); }); test('persist unsafe services and their requests', async t => { - const { promise: cancelled } = makePromiseKit(); + const { promise: cancelled, reject: cancel } = makePromiseKit(); + t.teardown(() => cancel(Error('teardown'))); const locator = makeLocator('tmp', 'import-unsafe'); await stop(locator).catch(() => {}); @@ -378,10 +390,14 @@ test('persist unsafe services and their requests', async t => { const number = await E(answer).value(); t.is(number, 42); } + + await stop(locator); }); test('guest facet receives a message for host', async t => { - const { promise: cancelled } = makePromiseKit(); + const { promise: cancelled, reject: cancel } = makePromiseKit(); + t.teardown(() => cancel(Error('teardown'))); + const locator = makeLocator('tmp', 'guest-sends-host'); await start(locator); @@ -407,4 +423,6 @@ test('guest facet receives a message for host', async t => { await E(host).adopt(message1.number, 'gift', 'ten2'); const ten = await E(host).provide('ten2'); t.is(ten, 10); + + await stop(locator); }); From 975f61e974efa26e3f012be5572bf5fa33034d1f Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Thu, 17 Aug 2023 11:19:27 -0700 Subject: [PATCH 100/234] refactor(daemon)!: Add recipient name argument to Guest request method, allowing HOST --- packages/daemon/src/guest.js | 9 +++++++-- packages/daemon/test/service.js | 1 + packages/daemon/test/test-endo.js | 2 +- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/packages/daemon/src/guest.js b/packages/daemon/src/guest.js index de458faee2..b75adfcbd4 100644 --- a/packages/daemon/src/guest.js +++ b/packages/daemon/src/guest.js @@ -99,10 +99,15 @@ export const makeGuestMaker = ({ ); }; - const request = async (what, responseName) => { + const request = async (recipientName, what, responseName) => { + const recipientFormulaIdentifier = + lookupFormulaIdentifierForName(recipientName); + if (recipientFormulaIdentifier === undefined) { + throw new Error(`Unknown pet name for party: ${recipientName}`); + } return sendRequest( guestFormulaIdentifier, - hostFormulaIdentifier, + recipientFormulaIdentifier, what, responseName, ); diff --git a/packages/daemon/test/service.js b/packages/daemon/test/service.js index 68edf88e8b..dabc9f08af 100644 --- a/packages/daemon/test/service.js +++ b/packages/daemon/test/service.js @@ -4,6 +4,7 @@ export const make = powers => { return Far('Service', { async ask() { return E(powers).request( + 'HOST', 'the meaning of life, the universe, everything', 'answer', ); diff --git a/packages/daemon/test/test-endo.js b/packages/daemon/test/test-endo.js index ad316f38e7..667a45fdbe 100644 --- a/packages/daemon/test/test-endo.js +++ b/packages/daemon/test/test-endo.js @@ -413,7 +413,7 @@ test('guest facet receives a message for host', async t => { await E(host).provideWorker('worker'); await E(host).evaluate('worker', '10', [], [], 'ten1'); const iteratorRef = E(host).followMessages(); - E.sendOnly(guest).request('a number', 'number'); + E.sendOnly(guest).request('HOST', 'a number', 'number'); const { value: message0 } = await E(iteratorRef).next(); t.is(message0.number, 0); await E(host).resolve(message0.number, 'ten1'); From bc0a6ce3e74859a2d60193c52f851002d8da27eb Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Thu, 17 Aug 2023 11:19:53 -0700 Subject: [PATCH 101/234] fix(cli): Compensate for new request recipient name argument --- packages/cli/demo/README.md | 1 + packages/cli/demo/doubler.js | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/cli/demo/README.md b/packages/cli/demo/README.md index b29962f744..ecfb37d913 100644 --- a/packages/cli/demo/README.md +++ b/packages/cli/demo/README.md @@ -96,6 +96,7 @@ import { E, Far } from '@endo/far'; export const make = powers => { const counter = E(powers).request( + 'HOST', 'a counter, suitable for doubling', 'my-counter' ); diff --git a/packages/cli/demo/doubler.js b/packages/cli/demo/doubler.js index db2be5d876..81d7b8c041 100644 --- a/packages/cli/demo/doubler.js +++ b/packages/cli/demo/doubler.js @@ -1,7 +1,11 @@ import { E, Far } from '@endo/far'; export const make = powers => { - const counter = E(powers).request('please give me a counter', 'counter'); + const counter = E(powers).request( + 'HOST', + 'please give me a counter', + 'counter', + ); return Far('Doubler', { async incr() { const n = await E(counter).incr(); From 6d95690c5697e7521c96ae8a5baead6fbea8a7a2 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Thu, 17 Aug 2023 11:20:20 -0700 Subject: [PATCH 102/234] feat(cli): Generalize request method so it is suitable for the host --- packages/cli/src/endo.js | 12 ++++++++++-- packages/cli/src/request.js | 12 ++---------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/packages/cli/src/endo.js b/packages/cli/src/endo.js index e18111eaaa..5d7968538b 100644 --- a/packages/cli/src/endo.js +++ b/packages/cli/src/endo.js @@ -349,6 +349,10 @@ export const main = async rawArgs => { program .command('request ') .description('requests a reference with the given description') + .option( + '-t,--to ', + 'Send the request to another party (default: HOST)', + ) .option( '-a,--as ', 'Pose as named party (as named by current party)', @@ -360,9 +364,13 @@ export const main = async rawArgs => { 'Assigns a name to the result for future reference, persisted between restarts', ) .action(async (description, cmd) => { - const { name: resultName, as: partyNames } = cmd.opts(); + const { + name: resultName, + as: partyNames, + to: toName = 'HOST', + } = cmd.opts(); const { request } = await import('./request.js'); - return request({ description, resultName, partyNames }); + return request({ toName, description, resultName, partyNames }); }); program diff --git a/packages/cli/src/request.js b/packages/cli/src/request.js index c15cae8d02..5d17df95a4 100644 --- a/packages/cli/src/request.js +++ b/packages/cli/src/request.js @@ -8,20 +8,12 @@ export const request = async ({ cancelled, sockPath, description, + toName, resultName, partyNames, }) => { - if (partyNames.length === 0) { - console.error('Specify the name of a guest with -a or --as '); - process.exitCode = 1; - return; - } - // The last party name must be a guest and will be created if not already - // present. - const lastPartyName = partyNames.pop(); await withEndoParty(partyNames, { os, process }, async ({ party }) => { - const guest = E(party).provideGuest(lastPartyName); - const result = await E(guest).request(description, resultName); + const result = await E(party).request(toName, description, resultName); console.log(result); }); }; From d7def3986f9453ad8dca6192fdb7e07ccc648531 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Thu, 17 Aug 2023 11:47:54 -0700 Subject: [PATCH 103/234] refactor(daemon): Pet store lookup and reverseLookup renames --- packages/daemon/src/mail.js | 6 +++--- packages/daemon/src/pet-store.js | 13 ++++++++++--- packages/daemon/src/types.d.ts | 2 +- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/packages/daemon/src/mail.js b/packages/daemon/src/mail.js index 8828a5ec6d..5447868eaf 100644 --- a/packages/daemon/src/mail.js +++ b/packages/daemon/src/mail.js @@ -34,14 +34,14 @@ export const makeMailboxMaker = ({ if (Object.hasOwn(specialNames, petName)) { return specialNames[petName]; } - return petStore.get(petName); + return petStore.lookup(petName); }; /** * @param {string} formulaIdentifier */ const lookupNamesForFormulaIdentifier = formulaIdentifier => { - const names = Array.from(petStore.lookup(formulaIdentifier)); + const names = Array.from(petStore.reverseLookup(formulaIdentifier)); for (const [specialName, specialFormulaIdentifier] of Object.entries( specialNames, )) { @@ -164,7 +164,7 @@ export const makeMailboxMaker = ({ ) => { if (responseName !== undefined) { /** @type {string | undefined} */ - let formulaIdentifier = senderPetStore.get(responseName); + let formulaIdentifier = senderPetStore.lookup(responseName); if (formulaIdentifier === undefined) { formulaIdentifier = await requestFormulaIdentifier( what, diff --git a/packages/daemon/src/pet-store.js b/packages/daemon/src/pet-store.js index ea835870a2..6aca49a00f 100644 --- a/packages/daemon/src/pet-store.js +++ b/packages/daemon/src/pet-store.js @@ -50,7 +50,7 @@ const makePetStoreAtPath = async (powers, petNameDirectoryPath) => { ); /** @param {string} petName */ - const get = petName => { + const lookup = petName => { assertPetName(petName); return petNames.get(petName); }; @@ -165,7 +165,7 @@ const makePetStoreAtPath = async (powers, petNameDirectoryPath) => { /** * @param {string} formulaIdentifier */ - const lookup = formulaIdentifier => { + const reverseLookup = formulaIdentifier => { if (!validFormulaPattern.test(formulaIdentifier)) { throw new Error(`Invalid formula identifier ${q(formulaIdentifier)}`); } @@ -176,7 +176,14 @@ const makePetStoreAtPath = async (powers, petNameDirectoryPath) => { return harden([...formulaPetNames]); }; - return Far('PetStore', { get, write, list, remove, rename, lookup }); + return Far('PetStore', { + lookup, + write, + list, + remove, + rename, + reverseLookup, + }); }; /** diff --git a/packages/daemon/src/types.d.ts b/packages/daemon/src/types.d.ts index 3538e18319..8bf3cc77b4 100644 --- a/packages/daemon/src/types.d.ts +++ b/packages/daemon/src/types.d.ts @@ -182,7 +182,7 @@ export interface PetStore { list(): Array; remove(petName: string); rename(fromPetName: string, toPetName: string); - lookup(formulaIdentifier: string): Array; + reverseLookup(formulaIdentifier: string): Array; } export type RequestFn = ( From 264a200b6924be705aa39582b2b7c480b979ff25 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Thu, 17 Aug 2023 11:58:53 -0700 Subject: [PATCH 104/234] refactor(daemon)!: Rename lookup to reverseLookup --- packages/daemon/src/guest.js | 4 ++-- packages/daemon/src/host.js | 4 ++-- packages/daemon/src/mail.js | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/daemon/src/guest.js b/packages/daemon/src/guest.js index b75adfcbd4..38c78bdb97 100644 --- a/packages/daemon/src/guest.js +++ b/packages/daemon/src/guest.js @@ -36,7 +36,7 @@ export const makeGuestMaker = ({ } const { - lookup, + reverseLookup, lookupFormulaIdentifierForName, followMessages, listMessages, @@ -128,7 +128,7 @@ export const makeGuestMaker = ({ remove, rename, provide, - lookup, + reverseLookup, }); partyReceiveFunctions.set(guest, receiveMail); diff --git a/packages/daemon/src/host.js b/packages/daemon/src/host.js index e36363a538..74be7d0818 100644 --- a/packages/daemon/src/host.js +++ b/packages/daemon/src/host.js @@ -32,7 +32,7 @@ export const makeHostMaker = ({ ); const { - lookup, + reverseLookup, lookupFormulaIdentifierForName, listMessages, followMessages, @@ -484,7 +484,7 @@ export const makeHostMaker = ({ dismiss, request, send, - lookup, + reverseLookup, list, remove, rename, diff --git a/packages/daemon/src/mail.js b/packages/daemon/src/mail.js index 5447868eaf..2de50c35e1 100644 --- a/packages/daemon/src/mail.js +++ b/packages/daemon/src/mail.js @@ -55,7 +55,7 @@ export const makeMailboxMaker = ({ /** * @param {unknown} presence */ - const lookup = async presence => { + const reverseLookup = async presence => { const formulaIdentifier = formulaIdentifierForRef.get(await presence); if (formulaIdentifier === undefined) { return harden([]); @@ -434,7 +434,7 @@ export const makeMailboxMaker = ({ }; return harden({ - lookup, + reverseLookup, lookupNamesForFormulaIdentifier, lookupFormulaIdentifierForName, followMessages, From c6785a00edbf87861076426df2e6a78370db4b0d Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Thu, 17 Aug 2023 12:13:36 -0700 Subject: [PATCH 105/234] refactor(cli, daemon): Rename provide to lookup to match name hubs elsewhere --- packages/cli/src/cat.js | 2 +- packages/cli/src/context.js | 2 +- packages/cli/src/follow.js | 2 +- packages/cli/src/open.js | 2 +- packages/cli/src/run.js | 2 +- packages/cli/src/show.js | 2 +- packages/daemon/src/guest.js | 6 +++--- packages/daemon/src/host.js | 6 +++--- packages/daemon/test/test-endo.js | 10 +++++----- 9 files changed, 17 insertions(+), 17 deletions(-) diff --git a/packages/cli/src/cat.js b/packages/cli/src/cat.js index fb9cc422ff..c56d06f01c 100644 --- a/packages/cli/src/cat.js +++ b/packages/cli/src/cat.js @@ -7,7 +7,7 @@ import { withEndoParty } from './context.js'; export const cat = async ({ name, partyNames }) => withEndoParty(partyNames, { os, process }, async ({ party }) => { - const readable = await E(party).provide(name); + const readable = await E(party).lookup(name); const readerRef = E(readable).stream(); const reader = makeRefReader(readerRef); for await (const chunk of reader) { diff --git a/packages/cli/src/context.js b/packages/cli/src/context.js index 37829c2d7c..c76e522797 100644 --- a/packages/cli/src/context.js +++ b/packages/cli/src/context.js @@ -67,7 +67,7 @@ export const withEndoParty = (partyNames, { os, process }, callback) => async ({ cancel, cancelled, bootstrap, host }) => { let party = host; for (const partyName of partyNames) { - party = E(party).provide(partyName); + party = E(party).lookup(partyName); } await callback({ cancel, diff --git a/packages/cli/src/follow.js b/packages/cli/src/follow.js index 7c5a16019c..fdc2d17344 100644 --- a/packages/cli/src/follow.js +++ b/packages/cli/src/follow.js @@ -7,7 +7,7 @@ import { withEndoParty } from './context.js'; export const followCommand = async ({ name, partyNames }) => withEndoParty(partyNames, { os, process }, async ({ party }) => { - const iterable = await E(party).provide(name); + const iterable = await E(party).lookup(name); for await (const iterand of makeRefIterator(iterable)) { console.log(iterand); } diff --git a/packages/cli/src/open.js b/packages/cli/src/open.js index 5bfc8a6b80..1be31edb9d 100644 --- a/packages/cli/src/open.js +++ b/packages/cli/src/open.js @@ -52,7 +52,7 @@ export const open = async ({ powersName, )); } else { - ({ url: webPageUrl } = await E(party).provide(webPageName)); + ({ url: webPageUrl } = await E(party).lookup(webPageName)); } process.stdout.write(`${webPageUrl}\n`); openWebPage(webPageUrl); diff --git a/packages/cli/src/run.js b/packages/cli/src/run.js index 12886b7c13..8a0f1641a0 100644 --- a/packages/cli/src/run.js +++ b/packages/cli/src/run.js @@ -78,7 +78,7 @@ export const run = async ({ args.unshift(filePath); } - const readableP = E(party).provide(bundleName); + const readableP = E(party).lookup(bundleName); const bundleText = await E(readableP).text(); bundle = JSON.parse(bundleText); } else { diff --git a/packages/cli/src/show.js b/packages/cli/src/show.js index 8863732b3d..5dd98e72f7 100644 --- a/packages/cli/src/show.js +++ b/packages/cli/src/show.js @@ -5,6 +5,6 @@ import { withEndoParty } from './context.js'; export const show = async ({ cancel, cancelled, sockPath, name, partyNames }) => withEndoParty(partyNames, { os, process }, async ({ party }) => { - const pet = await E(party).provide(name); + const pet = await E(party).lookup(name); console.log(pet); }); diff --git a/packages/daemon/src/guest.js b/packages/daemon/src/guest.js index 38c78bdb97..317c00d054 100644 --- a/packages/daemon/src/guest.js +++ b/packages/daemon/src/guest.js @@ -61,7 +61,7 @@ export const makeGuestMaker = ({ /** * @param {string} petName */ - const provide = async petName => { + const lookup = async petName => { assertPetName(petName); const formulaIdentifier = lookupFormulaIdentifierForName(petName); if (formulaIdentifier === undefined) { @@ -115,6 +115,8 @@ export const makeGuestMaker = ({ /** @type {import('@endo/eventual-send').ERef} */ const guest = Far('EndoGuest', { + lookup, + reverseLookup, request, receive, send, @@ -127,8 +129,6 @@ export const makeGuestMaker = ({ adopt, remove, rename, - provide, - reverseLookup, }); partyReceiveFunctions.set(guest, receiveMail); diff --git a/packages/daemon/src/host.js b/packages/daemon/src/host.js index 74be7d0818..da3f09a82c 100644 --- a/packages/daemon/src/host.js +++ b/packages/daemon/src/host.js @@ -153,7 +153,7 @@ export const makeHostMaker = ({ /** * @param {string} petName */ - const provide = async petName => { + const lookup = async petName => { const formulaIdentifier = lookupFormulaIdentifierForName(petName); if (formulaIdentifier === undefined) { throw new TypeError(`Unknown pet name: ${q(petName)}`); @@ -475,16 +475,16 @@ export const makeHostMaker = ({ /** @type {import('./types.js').EndoHost} */ const host = Far('EndoHost', { + lookup, + reverseLookup, listMessages, followMessages, - provide, resolve, reject, adopt, dismiss, request, send, - reverseLookup, list, remove, rename, diff --git a/packages/daemon/test/test-endo.js b/packages/daemon/test/test-endo.js index 667a45fdbe..7931a9aa69 100644 --- a/packages/daemon/test/test-endo.js +++ b/packages/daemon/test/test-endo.js @@ -166,7 +166,7 @@ test('persist spawn and evaluation', async t => { const bootstrap = getBootstrap(); const host = E(bootstrap).host(); - const retwenty = await E(host).provide('twenty'); + const retwenty = await E(host).lookup('twenty'); t.is(20, retwenty); } @@ -202,7 +202,7 @@ test('store', async t => { ); const bootstrap = getBootstrap(); const host = E(bootstrap).host(); - const readable = await E(host).provide('hello-text'); + const readable = await E(host).lookup('hello-text'); const actualText = await E(readable).text(); t.is(actualText, 'hello\n'); } @@ -283,7 +283,7 @@ test('closure state lost by restart', async t => { ); const bootstrap = getBootstrap(); const host = E(bootstrap).host(); - await E(host).provide('w1'); + await E(host).lookup('w1'); const one = await E(host).evaluate( 'w1', `E(counter).incr()`, @@ -386,7 +386,7 @@ test('persist unsafe services and their requests', async t => { ); const bootstrap = getBootstrap(); const host = E(bootstrap).host(); - const answer = await E(host).provide('answer'); + const answer = await E(host).lookup('answer'); const number = await E(answer).value(); t.is(number, 42); } @@ -421,7 +421,7 @@ test('guest facet receives a message for host', async t => { const { value: message1 } = await E(iteratorRef).next(); t.is(message1.number, 1); await E(host).adopt(message1.number, 'gift', 'ten2'); - const ten = await E(host).provide('ten2'); + const ten = await E(host).lookup('ten2'); t.is(ten, 10); await stop(locator); From fb86db237b94a20ee1bd40223557eb5777905799 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Thu, 17 Aug 2023 12:17:53 -0700 Subject: [PATCH 106/234] refactor(daemon): Deduplicate lookup --- packages/daemon/src/guest.js | 15 +-------------- packages/daemon/src/host.js | 14 +------------- packages/daemon/src/mail.js | 24 +++++++++++++++++++----- 3 files changed, 21 insertions(+), 32 deletions(-) diff --git a/packages/daemon/src/guest.js b/packages/daemon/src/guest.js index 317c00d054..270f7e3104 100644 --- a/packages/daemon/src/guest.js +++ b/packages/daemon/src/guest.js @@ -36,6 +36,7 @@ export const makeGuestMaker = ({ } const { + lookup, reverseLookup, lookupFormulaIdentifierForName, followMessages, @@ -58,20 +59,6 @@ export const makeGuestMaker = ({ }, }); - /** - * @param {string} petName - */ - const lookup = async petName => { - assertPetName(petName); - const formulaIdentifier = lookupFormulaIdentifierForName(petName); - if (formulaIdentifier === undefined) { - throw new TypeError(`Unknown pet name: ${q(petName)}`); - } - // Behold, recursion: - // eslint-disable-next-line no-use-before-define - return provideValueForFormulaIdentifier(formulaIdentifier); - }; - const { list } = petStore; const receive = (strings, edgeNames, petNames) => { diff --git a/packages/daemon/src/host.js b/packages/daemon/src/host.js index da3f09a82c..f47dd76aee 100644 --- a/packages/daemon/src/host.js +++ b/packages/daemon/src/host.js @@ -32,6 +32,7 @@ export const makeHostMaker = ({ ); const { + lookup, reverseLookup, lookupFormulaIdentifierForName, listMessages, @@ -150,19 +151,6 @@ export const makeHostMaker = ({ } }; - /** - * @param {string} petName - */ - const lookup = async petName => { - const formulaIdentifier = lookupFormulaIdentifierForName(petName); - if (formulaIdentifier === undefined) { - throw new TypeError(`Unknown pet name: ${q(petName)}`); - } - // Behold, recursion: - // eslint-disable-next-line no-use-before-define - return provideValueForFormulaIdentifier(formulaIdentifier); - }; - /** * @param {string} workerName */ diff --git a/packages/daemon/src/mail.js b/packages/daemon/src/mail.js index 2de50c35e1..ca87d2505e 100644 --- a/packages/daemon/src/mail.js +++ b/packages/daemon/src/mail.js @@ -37,10 +37,24 @@ export const makeMailboxMaker = ({ return petStore.lookup(petName); }; + /** + * @param {string} petName + */ + const lookup = async petName => { + assertPetName(petName); + const formulaIdentifier = lookupFormulaIdentifierForName(petName); + if (formulaIdentifier === undefined) { + throw new TypeError(`Unknown pet name: ${q(petName)}`); + } + // Behold, recursion: + // eslint-disable-next-line no-use-before-define + return provideValueForFormulaIdentifier(formulaIdentifier); + }; + /** * @param {string} formulaIdentifier */ - const lookupNamesForFormulaIdentifier = formulaIdentifier => { + const reverseLookupFormulaIdentifier = formulaIdentifier => { const names = Array.from(petStore.reverseLookup(formulaIdentifier)); for (const [specialName, specialFormulaIdentifier] of Object.entries( specialNames, @@ -60,7 +74,7 @@ export const makeMailboxMaker = ({ if (formulaIdentifier === undefined) { return harden([]); } - return lookupNamesForFormulaIdentifier(formulaIdentifier); + return reverseLookupFormulaIdentifier(formulaIdentifier); }; /** @@ -70,7 +84,7 @@ export const makeMailboxMaker = ({ const dubMessage = message => { if (message.type === 'request') { const { who: senderFormulaIdentifier, ...rest } = message; - const [senderName] = lookupNamesForFormulaIdentifier( + const [senderName] = reverseLookupFormulaIdentifier( senderFormulaIdentifier, ); if (senderName !== undefined) { @@ -79,7 +93,7 @@ export const makeMailboxMaker = ({ return undefined; } else if (message.type === 'package') { const { formulas: _, who: senderFormulaIdentifier, ...rest } = message; - const [senderName] = lookupNamesForFormulaIdentifier( + const [senderName] = reverseLookupFormulaIdentifier( senderFormulaIdentifier, ); if (senderName !== undefined) { @@ -435,7 +449,7 @@ export const makeMailboxMaker = ({ return harden({ reverseLookup, - lookupNamesForFormulaIdentifier, + reverseLookupFormulaIdentifier, lookupFormulaIdentifierForName, followMessages, listMessages, From 7e992a9ee51d00551fc36f05c247d2dc31f70b0e Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Thu, 17 Aug 2023 12:34:53 -0700 Subject: [PATCH 107/234] refactor(daemon): Deduplicate request, send, receive --- packages/daemon/src/guest.js | 49 +++--------------------------- packages/daemon/src/host.js | 53 +++++--------------------------- packages/daemon/src/mail.js | 58 ++++++++++++++++++++++++++++++++++-- 3 files changed, 66 insertions(+), 94 deletions(-) diff --git a/packages/daemon/src/guest.js b/packages/daemon/src/guest.js index 270f7e3104..14d3d96892 100644 --- a/packages/daemon/src/guest.js +++ b/packages/daemon/src/guest.js @@ -1,7 +1,4 @@ import { Far } from '@endo/far'; -import { assertPetName } from './pet-name.js'; - -const { quote: q } = assert; export const makeGuestMaker = ({ provideValueForFormulaIdentifier, @@ -38,21 +35,22 @@ export const makeGuestMaker = ({ const { lookup, reverseLookup, - lookupFormulaIdentifierForName, followMessages, listMessages, resolve, reject, dismiss, adopt, - sendMail, + send, + receive, receiveMail, receiveRequest, - sendRequest, + request, rename, remove, } = makeMailbox({ petStore, + selfFormulaIdentifier: guestFormulaIdentifier, specialNames: { SELF: guestFormulaIdentifier, HOST: hostFormulaIdentifier, @@ -61,45 +59,6 @@ export const makeGuestMaker = ({ const { list } = petStore; - const receive = (strings, edgeNames, petNames) => { - return sendMail( - guestFormulaIdentifier, - hostFormulaIdentifier, - strings, - edgeNames, - petNames, - ); - }; - - const send = async (recipientName, strings, edgeNames, petNames) => { - const recipientFormulaIdentifier = - lookupFormulaIdentifierForName(recipientName); - if (recipientFormulaIdentifier === undefined) { - throw new Error(`Unknown pet name for party: ${recipientName}`); - } - return sendMail( - guestFormulaIdentifier, - recipientFormulaIdentifier, - strings, - edgeNames, - petNames, - ); - }; - - const request = async (recipientName, what, responseName) => { - const recipientFormulaIdentifier = - lookupFormulaIdentifierForName(recipientName); - if (recipientFormulaIdentifier === undefined) { - throw new Error(`Unknown pet name for party: ${recipientName}`); - } - return sendRequest( - guestFormulaIdentifier, - recipientFormulaIdentifier, - what, - responseName, - ); - }; - /** @type {import('@endo/eventual-send').ERef} */ const guest = Far('EndoGuest', { lookup, diff --git a/packages/daemon/src/host.js b/packages/daemon/src/host.js index f47dd76aee..4fceb30232 100644 --- a/packages/daemon/src/host.js +++ b/packages/daemon/src/host.js @@ -39,61 +39,22 @@ export const makeHostMaker = ({ followMessages, resolve, reject, - receiveRequest: sendRequest, - sendRequest: receiveRequest, - receiveMail: sendMail, - sendMail: receiveMail, + receiveRequest, + request, + receiveMail, + send, dismiss, adopt, rename, remove, } = makeMailbox({ petStore, + selfFormulaIdentifier: hostFormulaIdentifier, specialNames: { SELF: hostFormulaIdentifier, }, }); - /** - * @param {string} recipientName - * @param {Array} strings - * @param {Array} edgeNames - * @param {Array} petNames - */ - const send = async (recipientName, strings, edgeNames, petNames) => { - const recipentFormulaIdentifier = - lookupFormulaIdentifierForName(recipientName); - if (recipentFormulaIdentifier === undefined) { - throw new Error(`Unknown pet name for party: ${recipientName}`); - } - return receiveMail( - hostFormulaIdentifier, - recipentFormulaIdentifier, - strings, - edgeNames, - petNames, - ); - }; - - /** - * @param {string} recipientName - * @param {string} what - * @param {string} responseName - */ - const request = async (recipientName, what, responseName) => { - const recipentFormulaIdentifier = - lookupFormulaIdentifierForName(recipientName); - if (recipentFormulaIdentifier === undefined) { - throw new Error(`Unknown pet name for party: ${recipientName}`); - } - return receiveRequest( - hostFormulaIdentifier, - recipentFormulaIdentifier, - what, - responseName, - ); - }; - /** * @param {string} petName */ @@ -487,8 +448,8 @@ export const makeHostMaker = ({ provideWebPage, }); - partyReceiveFunctions.set(host, sendMail); - partyRequestFunctions.set(host, sendRequest); + partyReceiveFunctions.set(host, receiveMail); + partyRequestFunctions.set(host, receiveRequest); return host; }; diff --git a/packages/daemon/src/mail.js b/packages/daemon/src/mail.js index ca87d2505e..127cfd9494 100644 --- a/packages/daemon/src/mail.js +++ b/packages/daemon/src/mail.js @@ -14,7 +14,7 @@ export const makeMailboxMaker = ({ /** @type {WeakMap} */ const partyReceiveFunctions = new WeakMap(); - const makeMailbox = ({ petStore, specialNames }) => { + const makeMailbox = ({ selfFormulaIdentifier, petStore, specialNames }) => { /** @type {Map>} */ const responses = new Map(); /** @type {Map} */ @@ -325,6 +325,37 @@ export const makeMailboxMaker = ({ ); }; + /** + * @param {string} recipientName + * @param {Array} strings + * @param {Array} edgeNames + * @param {Array} petNames + */ + const send = async (recipientName, strings, edgeNames, petNames) => { + const recipentFormulaIdentifier = + lookupFormulaIdentifierForName(recipientName); + if (recipentFormulaIdentifier === undefined) { + throw new Error(`Unknown pet name for party: ${recipientName}`); + } + return sendMail( + selfFormulaIdentifier, + recipentFormulaIdentifier, + strings, + edgeNames, + petNames, + ); + }; + + const receive = (strings, edgeNames, petNames) => { + return sendMail( + selfFormulaIdentifier, + 'HOST', + strings, + edgeNames, + petNames, + ); + }; + const dismiss = async messageNumber => { if ( typeof messageNumber !== 'number' || @@ -421,6 +452,25 @@ export const makeMailboxMaker = ({ return newResponseP; }; + /** + * @param {string} recipientName + * @param {string} what + * @param {string} responseName + */ + const request = async (recipientName, what, responseName) => { + const recipientFormulaIdentifier = + lookupFormulaIdentifierForName(recipientName); + if (recipientFormulaIdentifier === undefined) { + throw new Error(`Unknown pet name for party: ${recipientName}`); + } + return sendRequest( + selfFormulaIdentifier, + recipientFormulaIdentifier, + what, + responseName, + ); + }; + /** * @param {string} fromName * @param {string} toName @@ -448,17 +498,19 @@ export const makeMailboxMaker = ({ }; return harden({ + lookup, reverseLookup, reverseLookupFormulaIdentifier, lookupFormulaIdentifierForName, followMessages, listMessages, + request, receiveRequest, - sendRequest, resolve, reject, receiveMail, - sendMail, + send, + receive, dismiss, adopt, rename, From 21f5715276743546a51ccee21765e0bbd57bdbf5 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Thu, 17 Aug 2023 12:38:53 -0700 Subject: [PATCH 108/234] refactor(daemon)!: Remove receive method --- packages/daemon/src/guest.js | 2 -- packages/daemon/src/mail.js | 11 ----------- packages/daemon/test/test-endo.js | 2 +- 3 files changed, 1 insertion(+), 14 deletions(-) diff --git a/packages/daemon/src/guest.js b/packages/daemon/src/guest.js index 14d3d96892..12581e0b6e 100644 --- a/packages/daemon/src/guest.js +++ b/packages/daemon/src/guest.js @@ -42,7 +42,6 @@ export const makeGuestMaker = ({ dismiss, adopt, send, - receive, receiveMail, receiveRequest, request, @@ -64,7 +63,6 @@ export const makeGuestMaker = ({ lookup, reverseLookup, request, - receive, send, list, followMessages, diff --git a/packages/daemon/src/mail.js b/packages/daemon/src/mail.js index 127cfd9494..5651508a10 100644 --- a/packages/daemon/src/mail.js +++ b/packages/daemon/src/mail.js @@ -346,16 +346,6 @@ export const makeMailboxMaker = ({ ); }; - const receive = (strings, edgeNames, petNames) => { - return sendMail( - selfFormulaIdentifier, - 'HOST', - strings, - edgeNames, - petNames, - ); - }; - const dismiss = async messageNumber => { if ( typeof messageNumber !== 'number' || @@ -510,7 +500,6 @@ export const makeMailboxMaker = ({ reject, receiveMail, send, - receive, dismiss, adopt, rename, diff --git a/packages/daemon/test/test-endo.js b/packages/daemon/test/test-endo.js index 7931a9aa69..64e78bead6 100644 --- a/packages/daemon/test/test-endo.js +++ b/packages/daemon/test/test-endo.js @@ -417,7 +417,7 @@ test('guest facet receives a message for host', async t => { const { value: message0 } = await E(iteratorRef).next(); t.is(message0.number, 0); await E(host).resolve(message0.number, 'ten1'); - await E(guest).receive('Hello, World!', ['gift'], ['number']); + await E(guest).send('HOST', 'Hello, World!', ['gift'], ['number']); const { value: message1 } = await E(iteratorRef).next(); t.is(message1.number, 1); await E(host).adopt(message1.number, 'gift', 'ten2'); From d77cd79e1378123b7188e45451226931698c3f6e Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Thu, 17 Aug 2023 12:38:28 -0700 Subject: [PATCH 109/234] fix(cli): Remove receive (duplicative with send) --- packages/cli/src/endo.js | 15 --------------- packages/cli/src/receive.js | 21 --------------------- 2 files changed, 36 deletions(-) delete mode 100644 packages/cli/src/receive.js diff --git a/packages/cli/src/endo.js b/packages/cli/src/endo.js index 5d7968538b..165494f4bf 100644 --- a/packages/cli/src/endo.js +++ b/packages/cli/src/endo.js @@ -426,21 +426,6 @@ export const main = async rawArgs => { return send({ message, partyName, partyNames }); }); - program - .command('receive ') - .description('delivers a message to the underlying host') - .option( - '-a,--as ', - 'Pose as named party (as named by current party)', - collect, - [], - ) - .action(async (message, cmd) => { - const { as: partyNames } = cmd.opts(); - const { receive } = await import('./receive.js'); - return receive({ message, partyNames }); - }); - program .command('adopt ') .option( diff --git a/packages/cli/src/receive.js b/packages/cli/src/receive.js deleted file mode 100644 index 083011eb71..0000000000 --- a/packages/cli/src/receive.js +++ /dev/null @@ -1,21 +0,0 @@ -/* global process */ -import os from 'os'; -import { E } from '@endo/far'; -import { withEndoParty } from './context.js'; -import { parseMessage } from './message-parse.js'; - -export const receive = async ({ message, partyNames }) => { - const { strings, edgeNames, petNames } = parseMessage(message); - if (partyNames.length === 0) { - console.error('Specify the name of a guest with -a or --as '); - process.exitCode = 1; - return; - } - // The last party name must be a guest and will be created if not already - // present. - const lastPartyName = partyNames.pop(); - await withEndoParty(partyNames, { os, process }, async ({ party }) => { - const guest = E(party).provideGuest(lastPartyName); - await E(guest).receive(strings, edgeNames, petNames); - }); -}; From e9e1f88e16d40b8b2c800f77c304999823b22e2f Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Thu, 17 Aug 2023 12:57:04 -0700 Subject: [PATCH 110/234] refactor(daemon): Consolidate send and request --- packages/daemon/src/mail.js | 89 ++++++++++--------------------------- 1 file changed, 23 insertions(+), 66 deletions(-) diff --git a/packages/daemon/src/mail.js b/packages/daemon/src/mail.js index 5651508a10..8e7c2039da 100644 --- a/packages/daemon/src/mail.js +++ b/packages/daemon/src/mail.js @@ -275,21 +275,19 @@ export const makeMailboxMaker = ({ }; /** - * @param {string} senderFormulaIdentifier - * @param {object} receiverFormulaIdentifier + * @param {string} recipientName * @param {Array} strings * @param {Array} edgeNames * @param {Array} petNames */ - const sendMail = async ( - senderFormulaIdentifier, - receiverFormulaIdentifier, - strings, - edgeNames, - petNames, - ) => { - const receiver = await provideValueForFormulaIdentifier( - receiverFormulaIdentifier, + const send = async (recipientName, strings, edgeNames, petNames) => { + const recipientFormulaIdentifier = + lookupFormulaIdentifierForName(recipientName); + if (recipientFormulaIdentifier === undefined) { + throw new Error(`Unknown pet name for party: ${recipientName}`); + } + const recipient = await provideValueForFormulaIdentifier( + recipientFormulaIdentifier, ); petNames.forEach(assertPetName); edgeNames.forEach(assertPetName); @@ -306,7 +304,7 @@ export const makeMailboxMaker = ({ ); } - const partyReceive = partyReceiveFunctions.get(receiver); + const partyReceive = partyReceiveFunctions.get(recipient); if (partyReceive === undefined) { throw new Error(`panic: Message not deliverable`); } @@ -318,31 +316,10 @@ export const makeMailboxMaker = ({ return formulaIdentifier; }); partyReceive( - senderFormulaIdentifier, - strings, - edgeNames, - formulaIdentifiers, - ); - }; - - /** - * @param {string} recipientName - * @param {Array} strings - * @param {Array} edgeNames - * @param {Array} petNames - */ - const send = async (recipientName, strings, edgeNames, petNames) => { - const recipentFormulaIdentifier = - lookupFormulaIdentifierForName(recipientName); - if (recipentFormulaIdentifier === undefined) { - throw new Error(`Unknown pet name for party: ${recipientName}`); - } - return sendMail( selfFormulaIdentifier, - recipentFormulaIdentifier, strings, edgeNames, - petNames, + formulaIdentifiers, ); }; @@ -395,22 +372,21 @@ export const makeMailboxMaker = ({ }; /** - * @param {string} senderFormulaIdentifier - * @param {object} receiverFormulaIdentifier + * @param {string} recipientName * @param {string} what * @param {string} responseName */ - const sendRequest = async ( - senderFormulaIdentifier, - receiverFormulaIdentifier, - what, - responseName, - ) => { - const receiver = /** @type {object} */ ( - await provideValueForFormulaIdentifier(receiverFormulaIdentifier) + const request = async (recipientName, what, responseName) => { + const recipientFormulaIdentifier = + lookupFormulaIdentifierForName(recipientName); + if (recipientFormulaIdentifier === undefined) { + throw new Error(`Unknown pet name for party: ${recipientName}`); + } + const recipient = /** @type {object} */ ( + await provideValueForFormulaIdentifier(recipientFormulaIdentifier) ); - const deliverToRecipient = partyRequestFunctions.get(receiver); + const deliverToRecipient = partyRequestFunctions.get(recipient); if (deliverToRecipient === undefined) { throw new Error( `panic: a receive request function must exist for every party`, @@ -422,7 +398,7 @@ export const makeMailboxMaker = ({ return deliverToRecipient( what, responseName, - senderFormulaIdentifier, + selfFormulaIdentifier, petStore, ); } @@ -435,32 +411,13 @@ export const makeMailboxMaker = ({ const newResponseP = deliverToRecipient( what, responseName, - senderFormulaIdentifier, + selfFormulaIdentifier, petStore, ); responses.set(responseName, newResponseP); return newResponseP; }; - /** - * @param {string} recipientName - * @param {string} what - * @param {string} responseName - */ - const request = async (recipientName, what, responseName) => { - const recipientFormulaIdentifier = - lookupFormulaIdentifierForName(recipientName); - if (recipientFormulaIdentifier === undefined) { - throw new Error(`Unknown pet name for party: ${recipientName}`); - } - return sendRequest( - selfFormulaIdentifier, - recipientFormulaIdentifier, - what, - responseName, - ); - }; - /** * @param {string} fromName * @param {string} toName From 851eff5bf5c674a33fe81f4813cd968a180254f1 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Thu, 17 Aug 2023 13:02:11 -0700 Subject: [PATCH 111/234] refactor(daemon): Converge on receive and respond names --- packages/daemon/src/guest.js | 8 ++++---- packages/daemon/src/host.js | 8 ++++---- packages/daemon/src/mail.js | 8 ++++---- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/packages/daemon/src/guest.js b/packages/daemon/src/guest.js index 12581e0b6e..9f0f5daf31 100644 --- a/packages/daemon/src/guest.js +++ b/packages/daemon/src/guest.js @@ -42,8 +42,8 @@ export const makeGuestMaker = ({ dismiss, adopt, send, - receiveMail, - receiveRequest, + receive, + respond, request, rename, remove, @@ -75,8 +75,8 @@ export const makeGuestMaker = ({ rename, }); - partyReceiveFunctions.set(guest, receiveMail); - partyRequestFunctions.set(guest, receiveRequest); + partyReceiveFunctions.set(guest, receive); + partyRequestFunctions.set(guest, respond); return guest; }; diff --git a/packages/daemon/src/host.js b/packages/daemon/src/host.js index 4fceb30232..46f4dbd265 100644 --- a/packages/daemon/src/host.js +++ b/packages/daemon/src/host.js @@ -39,9 +39,9 @@ export const makeHostMaker = ({ followMessages, resolve, reject, - receiveRequest, + respond, request, - receiveMail, + receive, send, dismiss, adopt, @@ -448,8 +448,8 @@ export const makeHostMaker = ({ provideWebPage, }); - partyReceiveFunctions.set(host, receiveMail); - partyRequestFunctions.set(host, receiveRequest); + partyReceiveFunctions.set(host, receive); + partyRequestFunctions.set(host, respond); return host; }; diff --git a/packages/daemon/src/mail.js b/packages/daemon/src/mail.js index 8e7c2039da..b460484156 100644 --- a/packages/daemon/src/mail.js +++ b/packages/daemon/src/mail.js @@ -170,7 +170,7 @@ export const makeMailboxMaker = ({ * @param {string} senderFormulaIdentifier * @param {import('./types.js').PetStore} senderPetStore */ - const receiveRequest = async ( + const respond = async ( what, responseName, senderFormulaIdentifier, @@ -244,7 +244,7 @@ export const makeMailboxMaker = ({ * @param {Array} edgeNames * @param {Array} formulaIdentifiers */ - const receiveMail = ( + const receive = ( senderFormulaIdentifier, strings, edgeNames, @@ -452,10 +452,10 @@ export const makeMailboxMaker = ({ followMessages, listMessages, request, - receiveRequest, + respond, resolve, reject, - receiveMail, + receive, send, dismiss, adopt, From 36b28fc6234e99dcaff9f383446de10029e68eb4 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Thu, 17 Aug 2023 13:57:44 -0700 Subject: [PATCH 112/234] feat(daemon): Dismissal orthogonal to resolution --- packages/daemon/src/mail.js | 77 +++++++++++++++------------------- packages/daemon/src/types.d.ts | 2 +- 2 files changed, 35 insertions(+), 44 deletions(-) diff --git a/packages/daemon/src/mail.js b/packages/daemon/src/mail.js index b460484156..1604d748a8 100644 --- a/packages/daemon/src/mail.js +++ b/packages/daemon/src/mail.js @@ -126,41 +126,48 @@ export const makeMailboxMaker = ({ })(), ); + const deliver = partialMessage => { + /** @type {import('@endo/promise-kit/src/types.js').PromiseKit} */ + const dismissal = makePromiseKit(); + const messageNumber = nextMessageNumber; + nextMessageNumber += 1; + + const message = harden({ + number: messageNumber, + when: new Date().toISOString(), + dismissed: dismissal.promise, + ...partialMessage, + }); + + dismissers.set(message, () => { + messages.delete(messageNumber); + dismissal.resolve(); + }); + + messages.set(messageNumber, message); + messagesTopic.publisher.next(message); + + return message; + }; + /** * @param {string} what - user visible description of the desired value - * @param {string} guestFormulaIdentifier + * @param {string} who */ - const requestFormulaIdentifier = async (what, guestFormulaIdentifier) => { + const requestFormulaIdentifier = async (what, who) => { /** @type {import('@endo/promise-kit/src/types.js').PromiseKit} */ const { promise, resolve } = makePromiseKit(); - const messageNumber = nextMessageNumber; - nextMessageNumber += 1; - const settle = () => { - messages.delete(messageNumber); - }; const settled = promise.then( - () => { - settle(); - return /** @type {'fulfilled'} */ ('fulfilled'); - }, - () => { - settle(); - return /** @type {'rejected'} */ ('rejected'); - }, + () => 'fulfilled', + () => 'rejected', ); - - const req = harden({ - type: /** @type {'request'} */ ('request'), - number: messageNumber, - who: guestFormulaIdentifier, + const message = deliver({ + type: /** @type {const} */ ('request'), + who, what, - when: new Date().toISOString(), settled, }); - - messages.set(messageNumber, req); - resolvers.set(req, resolve); - messagesTopic.publisher.next(req); + resolvers.set(message, resolve); return promise; }; @@ -249,31 +256,15 @@ export const makeMailboxMaker = ({ strings, edgeNames, formulaIdentifiers, - ) => { - /** @type {import('@endo/promise-kit/src/types.js').PromiseKit} */ - const dismissal = makePromiseKit(); - const messageNumber = nextMessageNumber; - nextMessageNumber += 1; - - const message = harden({ + ) => + deliver({ type: /** @type {const} */ ('package'), - number: messageNumber, strings, names: edgeNames, formulas: formulaIdentifiers, who: senderFormulaIdentifier, - when: new Date().toISOString(), - dismissed: dismissal.promise, }); - messages.set(messageNumber, message); - dismissers.set(message, () => { - messages.delete(messageNumber); - dismissal.resolve(); - }); - messagesTopic.publisher.next(message); - }; - /** * @param {string} recipientName * @param {Array} strings diff --git a/packages/daemon/src/types.d.ts b/packages/daemon/src/types.d.ts index 8bf3cc77b4..a20f3582ff 100644 --- a/packages/daemon/src/types.d.ts +++ b/packages/daemon/src/types.d.ts @@ -140,6 +140,7 @@ export type Label = { number: number; who: string; when: string; + dismissed: Promise; }; export type InternalLabel = Label; @@ -154,7 +155,6 @@ export type Package = { type: 'package'; strings: Array; // text that appears before, between, and after named formulas. names: Array; // edge names - dismissed: Promise; }; export type InternalPackage = Package & { formulas: Array; // formula identifiers From 90e459aeebd9d51ab21cdd4af940b1a5b87e0ab0 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Thu, 17 Aug 2023 15:57:06 -0700 Subject: [PATCH 113/234] docs(cli): Revise command descriptions --- packages/cli/src/endo.js | 622 +++++++++++++++++++-------------------- 1 file changed, 308 insertions(+), 314 deletions(-) diff --git a/packages/cli/src/endo.js b/packages/cli/src/endo.js index 165494f4bf..7c75d3c8ea 100644 --- a/packages/cli/src/endo.js +++ b/packages/cli/src/endo.js @@ -55,422 +55,364 @@ export const main = async rawArgs => { const packageDescriptor = JSON.parse(packageDescriptorBytes); program.name('endo').version(packageDescriptor.version); - const where = program - .command('where') - .description('prints paths for state, logs, caches, socket, pids'); - - where - .command('state') - .description('prints the state directory path') - .action(async _cmd => { - process.stdout.write(`${statePath}\n`); - }); - - where - .command('run') - .description('prints the daemon PID file path') - .action(async _cmd => { - process.stdout.write(`${ephemeralStatePath}\n`); - }); - - where - .command('sock') - .description('prints the UNIX domain socket or Windows named pipe path') - .action(async _cmd => { - process.stdout.write(`${sockPath}\n`); - }); - - where - .command('log') - .description('prints the log file path') - .action(async _cmd => { - process.stdout.write(`${logPath}\n`); - }); - - where - .command('cache') - .description('prints the cache directory path') - .action(async _cmd => { - process.stdout.write(`${cachePath}\n`); - }); - - program - .command('start') - .description('start the endo daemon') - .action(async _cmd => { - await start(); - }); - - program - .command('stop') - .description('stop the endo daemon') - .action(async _cmd => { - await stop(); - }); - - program - .command('restart') - .description('stop and start the daemon') - .action(async _cmd => { - await restart(); - }); - - program - .command('clean') - .description('erases ephemeral state') - .action(async _cmd => { - await clean(); - }); - - program - .command('reset') - .description('erases persistent state and restarts if running') - .action(async _cmd => { - await reset(); - }); - - program - .command('log') - .option('-f, --follow', 'follow the tail of the log') - .option('-p,--ping ', 'milliseconds between daemon reset checks') - .description('writes out the daemon log, optionally following updates') - .action(async cmd => { - const { follow, ping } = cmd.opts(); - const { log: logCommand } = await import('./log.js'); - await logCommand({ follow, ping }); - }); - - program - .command('ping') - .description('prints ok if the daemon is responsive') - .action(async _cmd => { - const { ping } = await import('./ping.js'); - await ping(); - }); - program - .command('bundle ') + .command('open [filePath]') + .description('opens a web page (weblet)') .option( '-a,--as ', 'Pose as named party (as named by current party)', collect, [], ) - .option('-n,--name ', 'Store the bundle into Endo') - .description( - 'captures a JSON bundle containing an archive for an entry module path', + .option('-b,--bundle ', 'Bundle for a web page to open') + .option( + '-p,--powers ', + 'Endowment to give the weblet (a name, NONE, HOST, or ENDO)', ) - .action(async (applicationPath, cmd) => { - const { name: bundleName, as: partyNames } = cmd.opts(); - const { bundleCommand } = await import('./bundle.js'); - return bundleCommand({ - applicationPath, + .action(async (webPageName, programPath, cmd) => { + const { + bundle: bundleName, + powers: powersName = 'NONE', + as: partyNames, + } = cmd.opts(); + const { open } = await import('./open.js'); + return open({ + webPageName, + programPath, bundleName, + powersName, partyNames, }); }); program - .command('store ') + .command('run [] [...]') + .description('runs a program (runlet)') .option( '-a,--as ', 'Pose as named party (as named by current party)', collect, [], ) + .option('-b,--bundle ', 'Bundle name for the caplet program') + .option('--UNSAFE ', 'Or path of an unsafe plugin to run in Node.js') .option( - '-n,--name ', - 'Assigns a pet name to the result for future reference', + '-p,--powers ', + 'Endowment to give the worklet (a name, NONE, HOST, or ENDO)', ) - .description('stores a readable file') - .action(async (storablePath, cmd) => { - const { name, as: partyNames } = cmd.opts(); - const { store } = await import('./store.js'); - return store({ - storablePath, - name, + .action(async (filePath, args, cmd) => { + const { + as: partyNames, + bundle: bundleName, + UNSAFE: importPath, + powers: powersName = 'NONE', + } = cmd.opts(); + const { run } = await import('./run.js'); + return run({ + filePath, + args, + bundleName, + importPath, + powersName, partyNames, }); }); program - .command('spawn [names...]') - .description('creates workers for evaluating or importing programs') + .command('make [file]') + .description('make a plugin or a worker caplet (worklet)') + .option('-b,--bundle ', 'Bundle for a web page to open') + .option('--UNSAFE ', 'Path to a Node.js module') .option( '-a,--as ', 'Pose as named party (as named by current party)', collect, [], ) - .action(async (petNames, cmd) => { - const { as: partyNames } = cmd.opts(); - const { spawn } = await import('./spawn.js'); - return spawn({ petNames, partyNames }); + .option('-p,--powers ', 'Name of powers to grant or NONE, HOST, ENDO') + .option( + '-n,--name ', + 'Assigns a name to the result for future reference, persisted between restarts', + ) + .option( + '-w,--worker ', + 'Reuse an existing worker rather than create a new one', + ) + .action(async (filePath, cmd) => { + const { + UNSAFE: importPath, + name: resultName, + bundle: bundleName, + worker: workerName = 'NEW', + as: partyNames, + powers: powersName = 'NONE', + } = cmd.opts(); + const { makeCommand } = await import('./make.js'); + return makeCommand({ + filePath, + importPath, + resultName, + bundleName, + workerName, + partyNames, + powersName, + }); }); program - .command('show ') + .command('inbox') .option( '-a,--as ', 'Pose as named party (as named by current party)', collect, [], ) - .description('prints the named value') - .action(async (name, cmd) => { - const { as: partyNames } = cmd.opts(); - const { show } = await import('./show.js'); - return show({ name, partyNames }); + .option('-f,--follow', 'Follow the inbox for messages as they arrive') + .description('read messages') + .action(async cmd => { + const { as: partyNames, follow } = cmd.opts(); + const { inbox } = await import('./inbox.js'); + return inbox({ follow, partyNames }); }); program - .command('follow ') + .command('request ') + .description('ask someone for something') + .option( + '-t,--to ', + 'Send the request to another party (default: HOST)', + ) .option( '-a,--as ', 'Pose as named party (as named by current party)', collect, [], ) - .description( - 'prints a representation of each value from the named async iterable as it arrives', + .option( + '-n,--name ', + 'Assigns a name to the result for future reference, persisted between restarts', ) - .action(async (name, cmd) => { - const { as: partyNames } = cmd.opts(); - const { followCommand } = await import('./follow.js'); - return followCommand({ name, partyNames }); + .action(async (description, cmd) => { + const { + name: resultName, + as: partyNames, + to: toName = 'HOST', + } = cmd.opts(); + const { request } = await import('./request.js'); + return request({ toName, description, resultName, partyNames }); }); program - .command('cat ') + .command('resolve ') + .description('grant a request') .option( '-a,--as ', 'Pose as named party (as named by current party)', collect, [], ) - .description('prints the content of the named readable file') - .action(async (name, cmd) => { + .action(async (requestNumberText, resolutionName, cmd) => { const { as: partyNames } = cmd.opts(); - const { cat } = await import('./cat.js'); - return cat({ name, partyNames }); + const { resolveCommand } = await import('./resolve.js'); + return resolveCommand({ + requestNumberText, + resolutionName, + partyNames, + }); }); program - .command('list') + .command('reject [message]') + .description('deny a request') .option( '-a,--as ', 'Pose as named party (as named by current party)', collect, [], ) - .description('lists pet names') - .action(async cmd => { + .action(async (requestNumberText, message, cmd) => { const { as: partyNames } = cmd.opts(); - const { list } = await import('./list.js'); - return list({ partyNames }); + const { rejectCommand } = await import('./reject.js'); + return rejectCommand({ + requestNumberText, + message, + partyNames, + }); }); program - .command('remove [names...]') - .description('removes pet names') + .command('send ') + .description('send a message with @named-values @for-you:from-me') .option( '-a,--as ', 'Pose as named party (as named by current party)', collect, [], ) - .action(async (petNames, cmd) => { + .action(async (partyName, message, cmd) => { const { as: partyNames } = cmd.opts(); - const { remove } = await import('./remove.js'); - return remove({ petNames, partyNames }); + const { send } = await import('./send.js'); + return send({ message, partyName, partyNames }); }); program - .command('rename ') - .description('renames a value') + .command('adopt ') .option( - '-a,--as ', - 'Pose as named party (as named by current party)', - collect, - [], + '-n,--name ', + 'Name to use, if different than the suggested name.', ) - .action(async (fromName, toName, cmd) => { - const { as: partyNames } = cmd.opts(); - const { rename } = await import('./rename.js'); - return rename({ fromName, toName, partyNames }); - }); - - program - .command('mkhost ') .option( '-a,--as ', 'Pose as named party (as named by current party)', collect, [], ) - .description('creates new host powers, pet store, and mailbox') - .action(async (name, cmd) => { - const { as: partyNames } = cmd.opts(); - const { mkhost } = await import('./mkhost.js'); - return mkhost({ name, partyNames }); + .description('accept a @value from a message') + .action(async (messageNumberText, edgeName, cmd) => { + const { name = edgeName, as: partyNames } = cmd.opts(); + const { adoptCommand } = await import('./adopt.js'); + return adoptCommand({ + messageNumberText, + edgeName, + name, + partyNames, + }); }); program - .command('mkguest ') + .command('dismiss ') + .description('delete a message') .option( '-a,--as ', 'Pose as named party (as named by current party)', collect, [], ) - .description('creates new guest powers, pet store, and mailbox') - .action(async (name, cmd) => { + .action(async (messageNumberText, cmd) => { const { as: partyNames } = cmd.opts(); - const { mkguest } = await import('./mkguest.js'); - return mkguest({ name, partyNames }); + const { dismissCommand } = await import('./dismiss.js'); + return dismissCommand({ + messageNumberText, + partyNames, + }); }); program - .command('inbox') + .command('list') .option( '-a,--as ', 'Pose as named party (as named by current party)', collect, [], ) - .option('-f,--follow', 'Follow the inbox for messages as they arrive') - .description('prints pending requests that have been sent to you') + .description('show names') .action(async cmd => { - const { as: partyNames, follow } = cmd.opts(); - const { inbox } = await import('./inbox.js'); - return inbox({ follow, partyNames }); + const { as: partyNames } = cmd.opts(); + const { list } = await import('./list.js'); + return list({ partyNames }); }); program - .command('request ') - .description('requests a reference with the given description') - .option( - '-t,--to ', - 'Send the request to another party (default: HOST)', - ) + .command('remove [names...]') + .description('forget a named value') .option( '-a,--as ', 'Pose as named party (as named by current party)', collect, [], ) - .option( - '-n,--name ', - 'Assigns a name to the result for future reference, persisted between restarts', - ) - .action(async (description, cmd) => { - const { - name: resultName, - as: partyNames, - to: toName = 'HOST', - } = cmd.opts(); - const { request } = await import('./request.js'); - return request({ toName, description, resultName, partyNames }); + .action(async (petNames, cmd) => { + const { as: partyNames } = cmd.opts(); + const { remove } = await import('./remove.js'); + return remove({ petNames, partyNames }); }); program - .command('resolve ') + .command('rename ') + .description('change the name for a value') .option( '-a,--as ', 'Pose as named party (as named by current party)', collect, [], ) - .description('responds to a pending request with the named value') - .action(async (requestNumberText, resolutionName, cmd) => { + .action(async (fromName, toName, cmd) => { const { as: partyNames } = cmd.opts(); - const { resolveCommand } = await import('./resolve.js'); - return resolveCommand({ - requestNumberText, - resolutionName, - partyNames, - }); + const { rename } = await import('./rename.js'); + return rename({ fromName, toName, partyNames }); }); program - .command('reject [message]') - .description('responds to a pending request with the rejection message') + .command('show ') + .description('prints the named value') .option( '-a,--as ', 'Pose as named party (as named by current party)', collect, [], ) - .action(async (requestNumberText, message, cmd) => { + .action(async (name, cmd) => { const { as: partyNames } = cmd.opts(); - const { rejectCommand } = await import('./reject.js'); - return rejectCommand({ - requestNumberText, - message, - partyNames, - }); + const { show } = await import('./show.js'); + return show({ name, partyNames }); }); program - .command('send ') - .description('delivers a message to the underlying host') + .command('follow ') .option( '-a,--as ', 'Pose as named party (as named by current party)', collect, [], ) - .action(async (partyName, message, cmd) => { + .description('subscribe to a stream of values') + .action(async (name, cmd) => { const { as: partyNames } = cmd.opts(); - const { send } = await import('./send.js'); - return send({ message, partyName, partyNames }); + const { followCommand } = await import('./follow.js'); + return followCommand({ name, partyNames }); }); program - .command('adopt ') - .option( - '-n,--name ', - 'Name to use, if different than the suggested name.', - ) + .command('cat ') + .description('dumps a blob') .option( '-a,--as ', 'Pose as named party (as named by current party)', collect, [], ) - .description('Adopts a name from a received message') - .action(async (messageNumberText, edgeName, cmd) => { - const { name = edgeName, as: partyNames } = cmd.opts(); - const { adoptCommand } = await import('./adopt.js'); - return adoptCommand({ - messageNumberText, - edgeName, - name, - partyNames, - }); + .action(async (name, cmd) => { + const { as: partyNames } = cmd.opts(); + const { cat } = await import('./cat.js'); + return cat({ name, partyNames }); }); program - .command('dismiss ') - .description('dismisses a message and drops any references it carried') + .command('store ') + .description('stores a blob') .option( '-a,--as ', 'Pose as named party (as named by current party)', collect, [], ) - .action(async (messageNumberText, cmd) => { - const { as: partyNames } = cmd.opts(); - const { dismissCommand } = await import('./dismiss.js'); - return dismissCommand({ - messageNumberText, + .option( + '-n,--name ', + 'Assigns a pet name to the result for future reference', + ) + .action(async (storablePath, cmd) => { + const { name, as: partyNames } = cmd.opts(); + const { store } = await import('./store.js'); + return store({ + storablePath, + name, partyNames, }); }); program .command('eval [names...]') - .description('evaluates a string with the endowed values in scope') + .description('creates a value') .option( '-a,--as ', 'Pose as named party (as named by current party)', @@ -502,109 +444,161 @@ export const main = async rawArgs => { }); program - .command('make [file]') - .description('Makes a plugin or a worker caplet (worklet)') - .option('-b,--bundle ', 'Bundle for a web page to open') - .option('--UNSAFE ', 'Path to a Node.js module') + .command('spawn [names...]') + .description('creates a worker') .option( '-a,--as ', 'Pose as named party (as named by current party)', collect, [], ) - .option('-p,--powers ', 'Name of powers to grant or NONE, HOST, ENDO') - .option( - '-n,--name ', - 'Assigns a name to the result for future reference, persisted between restarts', - ) - .option( - '-w,--worker ', - 'Reuse an existing worker rather than create a new one', - ) - .action(async (filePath, cmd) => { - const { - UNSAFE: importPath, - name: resultName, - bundle: bundleName, - worker: workerName = 'NEW', - as: partyNames, - powers: powersName = 'NONE', - } = cmd.opts(); - const { makeCommand } = await import('./make.js'); - return makeCommand({ - filePath, - importPath, - resultName, - bundleName, - workerName, - partyNames, - powersName, - }); + .action(async (petNames, cmd) => { + const { as: partyNames } = cmd.opts(); + const { spawn } = await import('./spawn.js'); + return spawn({ petNames, partyNames }); }); program - .command('open [filePath]') - .description('opens a web page') + .command('bundle ') + .description('stores a program') .option( '-a,--as ', 'Pose as named party (as named by current party)', collect, [], ) - .option('-b,--bundle ', 'Bundle for a web page to open') - .option( - '-p,--powers ', - 'Endowment to give the weblet (a name, NONE, HOST, or ENDO)', - ) - .action(async (webPageName, programPath, cmd) => { - const { - bundle: bundleName, - powers: powersName = 'NONE', - as: partyNames, - } = cmd.opts(); - const { open } = await import('./open.js'); - return open({ - webPageName, - programPath, + .option('-n,--name ', 'Store the bundle into Endo') + .action(async (applicationPath, cmd) => { + const { name: bundleName, as: partyNames } = cmd.opts(); + const { bundleCommand } = await import('./bundle.js'); + return bundleCommand({ + applicationPath, bundleName, - powersName, partyNames, }); }); program - .command('run [] [...]') - .description( - 'import a caplet to run at the CLI (runlet), endow it with capabilities, make and store its public API', - ) + .command('mkhost ') .option( '-a,--as ', 'Pose as named party (as named by current party)', collect, [], ) - .option('-b,--bundle ', 'Bundle name for the caplet program') - .option('--UNSAFE ', 'Or path of an unsafe plugin to run in Node.js') + .description('makes a separate mailbox and storage for you') + .action(async (name, cmd) => { + const { as: partyNames } = cmd.opts(); + const { mkhost } = await import('./mkhost.js'); + return mkhost({ name, partyNames }); + }); + + program + .command('mkguest ') .option( - '-p,--powers ', - 'Endowment to give the worklet (a name, NONE, HOST, or ENDO)', + '-a,--as ', + 'Pose as named party (as named by current party)', + collect, + [], ) - .action(async (filePath, args, cmd) => { - const { - as: partyNames, - bundle: bundleName, - UNSAFE: importPath, - powers: powersName = 'NONE', - } = cmd.opts(); - const { run } = await import('./run.js'); - return run({ - filePath, - args, - bundleName, - importPath, - powersName, - partyNames, - }); + .description('makes a mailbox and storage for a guest (peer or program)') + .action(async (name, cmd) => { + const { as: partyNames } = cmd.opts(); + const { mkguest } = await import('./mkguest.js'); + return mkguest({ name, partyNames }); + }); + + const where = program + .command('where') + .description('prints paths for state, logs, caches, socket, pids'); + + where + .command('state') + .description('prints the state directory path') + .action(async _cmd => { + process.stdout.write(`${statePath}\n`); + }); + + where + .command('run') + .description('prints the daemon PID file path') + .action(async _cmd => { + process.stdout.write(`${ephemeralStatePath}\n`); + }); + + where + .command('sock') + .description('prints the UNIX domain socket or Windows named pipe path') + .action(async _cmd => { + process.stdout.write(`${sockPath}\n`); + }); + + where + .command('log') + .description('prints the log file path') + .action(async _cmd => { + process.stdout.write(`${logPath}\n`); + }); + + where + .command('cache') + .description('prints the cache directory path') + .action(async _cmd => { + process.stdout.write(`${cachePath}\n`); + }); + + program + .command('start') + .description('start the endo daemon') + .action(async _cmd => { + await start(); + }); + + program + .command('stop') + .description('stop the endo daemon') + .action(async _cmd => { + await stop(); + }); + + program + .command('restart') + .description('stop and start the daemon') + .action(async _cmd => { + await restart(); + }); + + program + .command('clean') + .description('erases ephemeral state') + .action(async _cmd => { + await clean(); + }); + + program + .command('reset') + .description('erases persistent state and restarts if running') + .action(async _cmd => { + await reset(); + }); + + program + .command('log') + .option('-f, --follow', 'follow the tail of the log') + .option('-p,--ping ', 'milliseconds between daemon reset checks') + .description('writes out the daemon log, optionally following updates') + .action(async cmd => { + const { follow, ping } = cmd.opts(); + const { log: logCommand } = await import('./log.js'); + await logCommand({ follow, ping }); + }); + + program + .command('ping') + .description('prints ok if the daemon is responsive') + .action(async _cmd => { + const { ping } = await import('./ping.js'); + await ping(); }); // Throw an error instead of exiting directly. From 58d8f2d39775fbe3c08744696df30df69644eb93 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Thu, 17 Aug 2023 16:29:45 -0700 Subject: [PATCH 114/234] feat(daemon): Publish pet store name list changes --- packages/daemon/src/guest.js | 3 ++- packages/daemon/src/host.js | 3 ++- packages/daemon/src/pet-store.js | 20 ++++++++++++++++++++ 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/packages/daemon/src/guest.js b/packages/daemon/src/guest.js index 9f0f5daf31..2be2283fa5 100644 --- a/packages/daemon/src/guest.js +++ b/packages/daemon/src/guest.js @@ -56,7 +56,7 @@ export const makeGuestMaker = ({ }, }); - const { list } = petStore; + const { list, follow: followNames } = petStore; /** @type {import('@endo/eventual-send').ERef} */ const guest = Far('EndoGuest', { @@ -65,6 +65,7 @@ export const makeGuestMaker = ({ request, send, list, + followNames, followMessages, listMessages, resolve, diff --git a/packages/daemon/src/host.js b/packages/daemon/src/host.js index 46f4dbd265..5a3176e1d7 100644 --- a/packages/daemon/src/host.js +++ b/packages/daemon/src/host.js @@ -420,7 +420,7 @@ export const makeHostMaker = ({ return value; }; - const { list } = petStore; + const { list, follow: followNames } = petStore; /** @type {import('./types.js').EndoHost} */ const host = Far('EndoHost', { @@ -435,6 +435,7 @@ export const makeHostMaker = ({ request, send, list, + followNames, remove, rename, store, diff --git a/packages/daemon/src/pet-store.js b/packages/daemon/src/pet-store.js index 6aca49a00f..b14cf2530c 100644 --- a/packages/daemon/src/pet-store.js +++ b/packages/daemon/src/pet-store.js @@ -1,5 +1,7 @@ import { Far } from '@endo/far'; import { assertPetName } from './pet-name.js'; +import { makeChangeTopic } from './pubsub.js'; +import { makeIteratorRef } from './reader-ref.js'; const { quote: q } = assert; @@ -16,6 +18,8 @@ const makePetStoreAtPath = async (powers, petNameDirectoryPath) => { const petNames = new Map(); /** @type {Map>} */ const formulaIdentifiers = new Map(); + /** @type {import('./types.js').Topic} */ + const changesTopic = makeChangeTopic(); /** @param {string} petName */ const read = async petName => { @@ -77,10 +81,22 @@ const makePetStoreAtPath = async (powers, petNameDirectoryPath) => { const petNamePath = powers.joinPath(petNameDirectoryPath, petName); const petNameText = `${formulaIdentifier}\n`; await powers.writeFileText(petNamePath, petNameText); + changesTopic.publisher.next({ add: petName }); }; const list = () => harden([...petNames.keys()].sort()); + const follow = async () => + makeIteratorRef( + (async function* currentAndSubsequentNames() { + const changes = changesTopic.subscribe(); + for (const name of [...petNames.keys()].sort()) { + yield { add: name }; + } + yield* changes; + })(), + ); + /** * @param {string} petName */ @@ -103,6 +119,7 @@ const makePetStoreAtPath = async (powers, petNameDirectoryPath) => { if (formulaPetNames !== undefined) { formulaPetNames.delete(petName); } + changesTopic.publisher.next({ remove: petName }); // TODO consider retaining a backlog of deleted names for recovery // TODO consider tracking historical pet names for formulas }; @@ -159,6 +176,8 @@ const makePetStoreAtPath = async (powers, petNameDirectoryPath) => { formulaPetNames.add(toName); } + changesTopic.publisher.next({ add: toName }); + changesTopic.publisher.next({ remove: fromName }); // TODO consider retaining a backlog of overwritten names for recovery }; @@ -180,6 +199,7 @@ const makePetStoreAtPath = async (powers, petNameDirectoryPath) => { lookup, write, list, + follow, remove, rename, reverseLookup, From 3918dccd7965791b18395f6935647d4eea94bfe9 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Thu, 17 Aug 2023 16:31:00 -0700 Subject: [PATCH 115/234] refactor(daemon): Relax some pet name constraints --- packages/daemon/src/host.js | 1 - packages/daemon/src/mail.js | 12 +++--------- packages/daemon/src/pet-store.js | 4 ++-- 3 files changed, 5 insertions(+), 12 deletions(-) diff --git a/packages/daemon/src/host.js b/packages/daemon/src/host.js index 5a3176e1d7..1fa3947c25 100644 --- a/packages/daemon/src/host.js +++ b/packages/daemon/src/host.js @@ -208,7 +208,6 @@ export const makeHostMaker = ({ const formulaIdentifiers = await Promise.all( petNames.map(async (petName, index) => { - assertPetName(petName); if (typeof codeNames[index] !== 'string') { throw new Error(`Invalid endowment name: ${q(codeNames[index])}`); } diff --git a/packages/daemon/src/mail.js b/packages/daemon/src/mail.js index 1604d748a8..70479a5b4e 100644 --- a/packages/daemon/src/mail.js +++ b/packages/daemon/src/mail.js @@ -41,7 +41,6 @@ export const makeMailboxMaker = ({ * @param {string} petName */ const lookup = async petName => { - assertPetName(petName); const formulaIdentifier = lookupFormulaIdentifierForName(petName); if (formulaIdentifier === undefined) { throw new TypeError(`Unknown pet name: ${q(petName)}`); @@ -414,17 +413,12 @@ export const makeMailboxMaker = ({ * @param {string} toName */ const rename = async (fromName, toName) => { - assertPetName(fromName); - assertPetName(toName); await petStore.rename(fromName, toName); const formulaIdentifier = responses.get(fromName); - if (formulaIdentifier === undefined) { - throw new Error( - `panic: the pet store rename must ensure that the renamed identifier exists`, - ); + if (formulaIdentifier !== undefined) { + responses.set(toName, formulaIdentifier); + responses.delete(fromName); } - responses.set(toName, formulaIdentifier); - responses.delete(fromName); }; /** diff --git a/packages/daemon/src/pet-store.js b/packages/daemon/src/pet-store.js index b14cf2530c..98a485b054 100644 --- a/packages/daemon/src/pet-store.js +++ b/packages/daemon/src/pet-store.js @@ -197,12 +197,12 @@ const makePetStoreAtPath = async (powers, petNameDirectoryPath) => { return Far('PetStore', { lookup, - write, + reverseLookup, list, follow, + write, remove, rename, - reverseLookup, }); }; From 1b474987cf205f2cb903d691d856daf193f4b608 Mon Sep 17 00:00:00 2001 From: Dan Connolly Date: Sun, 27 Aug 2023 15:20:08 -0500 Subject: [PATCH 116/234] chore(daemon): endow worker with console --- packages/daemon/src/worker.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/daemon/src/worker.js b/packages/daemon/src/worker.js index 851248331f..cfa2179e92 100644 --- a/packages/daemon/src/worker.js +++ b/packages/daemon/src/worker.js @@ -6,6 +6,7 @@ import { makeNetstringCapTP } from './connection.js'; const endowments = harden({ assert, + console, E, Far, TextEncoder, From db43d93297b041758b18dce2fac701e798990c4f Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Fri, 25 Aug 2023 21:52:54 -0700 Subject: [PATCH 117/234] refactor(daemon): Extract web and socket servers from daemon --- packages/daemon/src/daemon-node-powers.js | 40 ++- packages/daemon/src/daemon.js | 243 +++++------------- packages/daemon/src/serve-private-path.js | 62 +++++ .../daemon/src/serve-private-port-http.js | 147 +++++++++++ packages/daemon/src/types.d.ts | 19 +- 5 files changed, 313 insertions(+), 198 deletions(-) create mode 100644 packages/daemon/src/serve-private-path.js create mode 100644 packages/daemon/src/serve-private-port-http.js diff --git a/packages/daemon/src/daemon-node-powers.js b/packages/daemon/src/daemon-node-powers.js index a86441a7f2..3fd33e1148 100644 --- a/packages/daemon/src/daemon-node-powers.js +++ b/packages/daemon/src/daemon-node-powers.js @@ -23,11 +23,18 @@ export const makeHttpPowers = ({ http, ws }) => { /** * @param {object} args * @param {number} args.port + * @param {string} args.host * @param {import('./types.js').HttpRespond} [args.respond] * @param {import('./types.js').HttpConnect} [args.connect] * @param {Promise} args.cancelled */ - const serveHttp = async ({ port, respond, connect, cancelled }) => { + const servePortHttp = async ({ + port, + host = '0.0.0.0', + respond, + connect, + cancelled, + }) => { const server = createServer(); server.on('error', error => { @@ -136,7 +143,7 @@ export const makeHttpPowers = ({ http, ws }) => { } return new Promise((resolve, reject) => { - server.listen(port, error => { + server.listen(port, host, error => { if (error) { reject(error); } else { @@ -152,7 +159,7 @@ export const makeHttpPowers = ({ http, ws }) => { }); }; - return harden({ serveHttp }); + return harden({ servePortHttp }); }; /** @@ -206,7 +213,7 @@ export const makePowers = ({ }), ); - const listenOnPath = async (sockPath, cancelled) => { + const serveListener = async (listen, cancelled) => { const [ /** @type {Reader} */ readFrom, /** @type {Writer - server.listen({ path: sockPath }, () => resolve(undefined)), - ); + const listening = listen(server); await Promise.race([erred, cancelled, listening]); @@ -244,6 +249,24 @@ export const makePowers = ({ return readFrom; }; + const servePort = async ({ port, host = '0.0.0.0', cancelled }) => + serveListener( + server => + new Promise(resolve => + server.listen(port, host, () => resolve(undefined)), + ), + cancelled, + ); + + const servePath = async ({ path, cancelled }) => + serveListener( + server => + new Promise(resolve => + server.listen({ path }, () => resolve(undefined)), + ), + cancelled, + ); + const informParentWhenReady = () => { if (process.send) { process.send({ type: 'ready' }); @@ -393,7 +416,8 @@ export const makePowers = ({ sinkError, makeSha512, randomHex512, - listenOnPath, + servePort, + servePath, informParentWhenReady, reportErrorToParent, makeFileReader, diff --git a/packages/daemon/src/daemon.js b/packages/daemon/src/daemon.js index 23bbb86841..a390fdf993 100644 --- a/packages/daemon/src/daemon.js +++ b/packages/daemon/src/daemon.js @@ -9,19 +9,15 @@ import '@endo/lockdown/commit.js'; import { E, Far } from '@endo/far'; import { makePromiseKit } from '@endo/promise-kit'; -import { mapReader, mapWriter } from '@endo/stream'; -import { - makeMessageCapTP, - makeNetstringCapTP, - messageToBytes, - bytesToMessage, -} from './connection.js'; +import { makeNetstringCapTP } from './connection.js'; import { makeRefReader } from './ref-reader.js'; import { makeReaderRef } from './reader-ref.js'; import { makeOwnPetStore, makeIdentifiedPetStore } from './pet-store.js'; import { makeMailboxMaker } from './mail.js'; import { makeGuestMaker } from './guest.js'; import { makeHostMaker } from './host.js'; +import { servePrivatePortHttp } from './serve-private-port-http.js'; +import { servePrivatePath } from './serve-private-path.js'; const { quote: q } = assert; @@ -40,7 +36,7 @@ const leastAuthority = Far('EndoGuest', { /** * @param {import('./types.js').DaemonicPowers} powers * @param {import('./types.js').Locator} locator - * @param {number} httpPort + * @param {Promise} webletPortP * @param {object} args * @param {Promise} args.cancelled * @param {(error: Error) => void} args.cancel @@ -50,7 +46,7 @@ const leastAuthority = Far('EndoGuest', { const makeEndoBootstrap = ( powers, locator, - httpPort, + webletPortP, { cancelled, cancel, gracePeriodMs, gracePeriodElapsed }, ) => { const { randomHex512, makeSha512 } = powers; @@ -373,7 +369,7 @@ const makeEndoBootstrap = ( ); } else if (formula.type === 'web-bundle') { return harden({ - url: `http://${formulaNumber}.endo.localhost:${httpPort}`, + url: `http://${formulaNumber}.endo.localhost:${await webletPortP}`, // Behold, recursion: // eslint-disable-next-line no-use-before-define bundle: provideValueForFormulaIdentifier(formula.bundle), @@ -673,149 +669,69 @@ export const main = async (powers, locator, pid, cancel, cancelled) => { const cachePathP = powers.makePath(locator.cachePath); await Promise.all([statePathP, cachePathP, ephemeralStatePathP]); - let nextConnectionNumber = 0; - /** @type {Set>} */ - const connectionClosedPromises = new Set(); - - const respond = async request => { - if (request.method === 'GET') { - if (request.url === '/') { - return { - status: 200, - headers: { 'Content-Type': 'text/html', Charset: 'utf-8' }, - content: `\ - - - -

- -`, - }; - } else if (request.url === '/bootstrap.js') { - // TODO readable mutable file formula (with watcher?) - // Behold, recursion: - // eslint-disable-next-line no-use-before-define - const webPageJs = await E(endoBootstrap).webPageJs(); - return { - status: 200, - headers: { 'Content-Type': 'application/javascript' }, - content: webPageJs, - }; - } - } - return { - status: 404, - headers: { 'Content-Type': 'text/plain' }, - content: `Not found: ${request.method} ${request.url}`, - }; - }; - - const requestedHttpPortString = powers.env.ENDO_HTTP_PORT; - const requestedHttpPort = requestedHttpPortString - ? Number(requestedHttpPortString) + const requestedWebletPortText = powers.env.ENDO_HTTP_PORT; + const requestedWebletPort = requestedWebletPortText + ? Number(requestedWebletPortText) : defaultHttpPort; - const httpReadyP = powers.serveHttp({ - port: requestedHttpPort, - respond, - connect(connection, request) { - // TODO select attenuated bootstrap based on subdomain - (async () => { - const { - reader: frameReader, - writer: frameWriter, - closed: connectionClosed, - } = connection; - - const connectionNumber = nextConnectionNumber; - nextConnectionNumber += 1; - console.log( - `Endo daemon received local web socket connection ${connectionNumber} at ${new Date().toISOString()}`, - ); + const { promise: assignedWebletPortP, resolve: assignWebletPort } = + /** @type {import('@endo/promise-kit').PromiseKit} */ ( + makePromiseKit() + ); + + const endoBootstrap = makeEndoBootstrap( + powers, + locator, + assignedWebletPortP, + { + cancelled, + cancel, + gracePeriodMs, + gracePeriodElapsed, + }, + ); - const messageWriter = mapWriter(frameWriter, messageToBytes); - const messageReader = mapReader(frameReader, bytesToMessage); + const connectionNumbers = (function* generateNumbers() { + let n = 0; + for (;;) { + yield n; + n += 1; + } + })(); - const { closed: capTpClosed, getBootstrap } = makeMessageCapTP( - 'Endo', - messageWriter, - messageReader, - cancelled, - undefined, // bootstrap - ); + const { servePath, servePortHttp } = powers; - const webBootstrap = getBootstrap(); - - // TODO set up heart monitor - E.sendOnly(webBootstrap).ping(); - - const host = request.headers.host; - if (host === undefined) { - throw new Error('Host header required'); - } - const match = - /^([0-9a-f]{32})\.endo\.localhost:([1-9][0-9]{0,4})$/.exec(host); - if (match === null) { - throw new Error(`Invalid host ${host}`); - } - const [_, formulaNumber, portNumber] = match; - // eslint-disable-next-line no-use-before-define - if (assignedHttpPort !== +portNumber) { - console.error( - 'Connected browser misreported port number in host header', - ); - E(webBootstrap) - .reject( - 'Your browser misreported your port number in the host header', - ) - .catch(error => { - console.error(error); - }); - } else { - // eslint-disable-next-line no-use-before-define - E(endoBootstrap) - .importAndEndowInWebPage(webBootstrap, formulaNumber) - .catch(error => { - E.sendOnly(webBootstrap).reject(error.message); - }) - .catch(error => { - console.error(error); - }); - } - - const closed = Promise.race([connectionClosed, capTpClosed]); - connectionClosedPromises.add(closed); - closed.finally(() => { - connectionClosedPromises.delete(closed); - console.log( - `Endo daemon closed local web socket connection ${connectionNumber} at ${new Date().toISOString()}`, - ); - }); - })().catch(exitWithError); - }, + const privatePathService = servePrivatePath(locator.sockPath, endoBootstrap, { + servePath, + connectionNumbers, cancelled, + exitWithError, }); - const connectionsP = powers.listenOnPath(locator.sockPath, cancelled); + const privateHttpService = servePrivatePortHttp( + requestedWebletPort, + endoBootstrap, + { + servePortHttp, + connectionNumbers, + cancelled, + exitWithError, + }, + ); - await Promise.all([connectionsP, httpReadyP]).catch(error => { - powers.reportErrorToParent(error.message); - throw error; - }); + assignWebletPort(privateHttpService.started); - const assignedHttpPort = await httpReadyP; - console.log( - `Endo daemon listening for HTTP on ${q( - assignedHttpPort, - )} ${new Date().toISOString()}`, - ); + const services = [privatePathService, privateHttpService]; - const endoBootstrap = makeEndoBootstrap(powers, locator, assignedHttpPort, { - cancelled, - cancel, - gracePeriodMs, - gracePeriodElapsed, - }); + await Promise.all(services.map(({ started }) => started)).then( + () => { + powers.informParentWhenReady(); + }, + error => { + powers.reportErrorToParent(error.message); + throw error; + }, + ); const pidPath = powers.joinPath(locator.ephemeralStatePath, 'endo.pid'); @@ -829,48 +745,7 @@ export const main = async (powers, locator, pid, cancel, cancelled) => { await powers.writeFileText(pidPath, `${pid}\n`); - const connections = await connectionsP; - // Resolve a promise in the Endo CLI through the IPC channel: - console.log( - `Endo daemon listening for CapTP on ${q( - locator.sockPath, - )} ${new Date().toISOString()}`, - ); - - powers.informParentWhenReady(); - - for await (const { - reader, - writer, - closed: connectionClosed, - } of connections) { - (async () => { - const connectionNumber = nextConnectionNumber; - nextConnectionNumber += 1; - console.log( - `Endo daemon received domain connection ${connectionNumber} at ${new Date().toISOString()}`, - ); - - const { closed: capTpClosed } = makeNetstringCapTP( - 'Endo', - writer, - reader, - cancelled, - endoBootstrap, - ); - - const closed = Promise.race([connectionClosed, capTpClosed]); - connectionClosedPromises.add(closed); - closed.finally(() => { - connectionClosedPromises.delete(closed); - console.log( - `Endo daemon closed domain connection ${connectionNumber} at ${new Date().toISOString()}`, - ); - }); - })().catch(exitWithError); - } - - await Promise.all(Array.from(connectionClosedPromises)); + await Promise.all(services.map(({ stopped }) => stopped)); cancel(new Error('Terminated normally')); cancelGracePeriod(new Error('Terminated normally')); diff --git a/packages/daemon/src/serve-private-path.js b/packages/daemon/src/serve-private-path.js new file mode 100644 index 0000000000..8a17e4b077 --- /dev/null +++ b/packages/daemon/src/serve-private-path.js @@ -0,0 +1,62 @@ +import { makeNetstringCapTP } from './connection.js'; + +const { quote: q } = assert; + +export const servePrivatePath = ( + sockPath, + endoBootstrap, + { servePath, connectionNumbers, cancelled, exitWithError }, +) => { + const connectionsP = servePath({ path: sockPath, cancelled }); + + const started = async () => { + await connectionsP; + // Resolve a promise in the Endo CLI through the IPC channel: + console.log( + `Endo daemon listening for CapTP on ${q( + sockPath, + )} ${new Date().toISOString()}`, + ); + }; + + const stopped = (async () => { + /** @type {Set>} */ + const connectionClosedPromises = new Set(); + + const connections = await connectionsP; + + for await (const { + reader, + writer, + closed: connectionClosed, + } of connections) { + (async () => { + const connectionNumber = connectionNumbers.next(); + console.log( + `Endo daemon received domain connection ${connectionNumber} at ${new Date().toISOString()}`, + ); + + const { closed: capTpClosed } = makeNetstringCapTP( + 'Endo', + writer, + reader, + cancelled, + endoBootstrap, + ); + + const closed = Promise.race([connectionClosed, capTpClosed]); + connectionClosedPromises.add(closed); + closed.finally(() => { + connectionClosedPromises.delete(closed); + console.log( + `Endo daemon closed domain connection ${connectionNumber} at ${new Date().toISOString()}`, + ); + }); + })().catch(exitWithError); + } + + await Promise.all(Array.from(connectionClosedPromises)); + })(); + + return harden({ started, stopped }); +}; diff --git a/packages/daemon/src/serve-private-port-http.js b/packages/daemon/src/serve-private-port-http.js new file mode 100644 index 0000000000..de176d82c3 --- /dev/null +++ b/packages/daemon/src/serve-private-port-http.js @@ -0,0 +1,147 @@ +import { E } from '@endo/far'; +import { mapReader, mapWriter } from '@endo/stream'; +import { + makeMessageCapTP, + messageToBytes, + bytesToMessage, +} from './connection.js'; + +const { quote: q } = assert; + +export const servePrivatePortHttp = ( + requestedWebletPort, + endoBootstrap, + { servePortHttp, connectionNumbers, cancelled, exitWithError }, +) => { + /** @type {Set>} */ + const connectionClosedPromises = new Set(); + + const respond = async request => { + if (request.method === 'GET') { + if (request.url === '/') { + return { + status: 200, + headers: { 'Content-Type': 'text/html', Charset: 'utf-8' }, + content: `\ + + + +

+ +`, + }; + } else if (request.url === '/bootstrap.js') { + // TODO readable mutable file formula (with watcher?) + // Behold, recursion: + // eslint-disable-next-line no-use-before-define + const webPageJs = await E(endoBootstrap).webPageJs(); + return { + status: 200, + headers: { 'Content-Type': 'application/javascript' }, + content: webPageJs, + }; + } + } + return { + status: 404, + headers: { 'Content-Type': 'text/plain' }, + content: `Not found: ${request.method} ${request.url}`, + }; + }; + + const started = servePortHttp({ + port: requestedWebletPort, + host: '127.0.0.1', + respond, + connect(connection, request) { + // TODO select attenuated bootstrap based on subdomain + (async () => { + const assignedHttpPort = await started; + const { + reader: frameReader, + writer: frameWriter, + closed: connectionClosed, + } = connection; + + const connectionNumber = connectionNumbers.next(); + console.log( + `Endo daemon received local web socket connection ${connectionNumber} at ${new Date().toISOString()}`, + ); + + const messageWriter = mapWriter(frameWriter, messageToBytes); + const messageReader = mapReader(frameReader, bytesToMessage); + + const { closed: capTpClosed, getBootstrap } = makeMessageCapTP( + 'Endo', + messageWriter, + messageReader, + cancelled, + undefined, // bootstrap + ); + + const webBootstrap = getBootstrap(); + + // TODO set up heart monitor + E.sendOnly(webBootstrap).ping(); + + const host = request.headers.host; + if (host === undefined) { + throw new Error('Host header required'); + } + const match = + /^([0-9a-f]{32})\.endo\.localhost:([1-9][0-9]{0,4})$/.exec(host); + if (match === null) { + throw new Error(`Invalid host ${host}`); + } + const [_, formulaNumber, portNumber] = match; + // eslint-disable-next-line no-use-before-define + if (assignedHttpPort !== +portNumber) { + console.error( + 'Connected browser misreported port number in host header', + ); + E(webBootstrap) + .reject( + 'Your browser misreported your port number in the host header', + ) + .catch(error => { + console.error(error); + }); + } else { + // eslint-disable-next-line no-use-before-define + E(endoBootstrap) + .importAndEndowInWebPage(webBootstrap, formulaNumber) + .catch(error => { + E.sendOnly(webBootstrap).reject(error.message); + }) + .catch(error => { + console.error(error); + }); + } + + const closed = Promise.race([connectionClosed, capTpClosed]); + connectionClosedPromises.add(closed); + closed.finally(() => { + connectionClosedPromises.delete(closed); + console.log( + `Endo daemon closed local web socket connection ${connectionNumber} at ${new Date().toISOString()}`, + ); + }); + })().catch(exitWithError); + }, + cancelled, + }); + + started.then(assignedHttpPort => { + console.log( + `Endo daemon listening for HTTP on ${q( + assignedHttpPort, + )} ${new Date().toISOString()}`, + ); + }); + + const stopped = cancelled.catch(() => + Promise.all(Array.from(connectionClosedPromises)), + ); + + return harden({ started, stopped }); +}; diff --git a/packages/daemon/src/types.d.ts b/packages/daemon/src/types.d.ts index a20f3582ff..95c235f5d0 100644 --- a/packages/daemon/src/types.d.ts +++ b/packages/daemon/src/types.d.ts @@ -48,16 +48,23 @@ export type DaemonicPowers = { sinkError: (error) => void; makeSha512: () => Sha512; randomHex512: () => Promise; - listenOnPath: ( - path: string, - cancelled: Promise, - ) => Promise>; - serveHttp: (args: { + servePath: (args: { + path: string; + host?: string; + cancelled: Promise; + }) => Promise>; + servePort: (args: { + port: number; + host?: string; + cancelled: Promise; + }) => Promise>; + servePortHttp: (args: { port: number; + host?: string; respond?: HttpRespond; connect?: HttpConnect; cancelled: Promise; - }) => void; + }) => Promise; informParentWhenReady: () => void; reportErrorToParent: (message: string) => void; makeFileReader: (path: string) => Reader; From 81b4651b5b0bdf679b086b10d6dba89c35d7d4d7 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Mon, 28 Aug 2023 15:38:04 -0700 Subject: [PATCH 118/234] fix(cli): Corrections for demo instructions --- packages/cli/demo/README.md | 73 ++++++++++++++++++++++++++++++++++++ packages/cli/demo/counter.js | 2 +- packages/cli/demo/doubler.js | 4 +- 3 files changed, 76 insertions(+), 3 deletions(-) diff --git a/packages/cli/demo/README.md b/packages/cli/demo/README.md index ecfb37d913..a5420b2924 100644 --- a/packages/cli/demo/README.md +++ b/packages/cli/demo/README.md @@ -1,8 +1,17 @@ # Install Endo +One day, this will be as simple as: + > npm install -g @endo/cli +Until that day, clone the Endo repository, `yarn install`, and arrange to use +`packages/cli/bin/endo` to be `endo` in your shell. +For example, + +> yarn +> alias endo=$PWD/packages/cli/bin/endo + # A counter example This is a worker caplet, or worklet, that counts numbers. @@ -118,6 +127,7 @@ However, we must give a name to the agent running the doubler, which we will later use to recognize requests coming from the doubler. ``` +> endo mkguest doubler-agent > endo make doubler.js --name doubler --powers doubler-agent ``` @@ -162,6 +172,68 @@ restarts and reboots. 6 ``` +# Sending Messages + +So far, we have run guest programs like the doubler. +Guests and hosts can exchange messages and those messages can convey powerful +objects. + +In this example, we create a fake guest named "alice" and we send them our +"doubler". +Then, assuming the guise of "alice", we find the message in our inbox +and adopt the "doubler" object into our own store. + +``` +> endo mkguest alice +> endo send alice 'Please enjoy this @doubler.' +> endo inbox --as alice +0. "HOST" sent "Please enjoy this @doubler." +> endo adopt --as alice 0 doubler +> endo list --as alice +doubler +> endo dismiss --as alice 0 +``` + +# Names in transit are no-one's names + +Sending a message with the `@name` notation means that the recipient +will see the name we chose for the object. +This is convenient in the common case, but not necessary. +The sender can choose a different name for the capability they send with the +notation `@name-they-see:name-we-have`. +Then, the receiver may choose to adopt the capability with a different name of +their own. + +In this example, we send alice our "doubler" but let it appear as merely +"counter" in the message body. +Then, alice adopts "counter", giving it their own name, "redoubler". + +``` +> endo send alice 'Please enjoy this @counter:doubler.' +> endo inbox --as alice +1. "HOST" sent "Please enjoy this @counter." +> endo adopt --as alice 1 counter --name redoubler +> endo list --as alice +redoubler +> endo dismiss --as alice 1 +``` + +# Mailboxes are symmetric + +Guests can also send their host messages. +In this example, "alice" send the doubler back to us, their host. + +``` +> endo send HOST --as alice 'This is the @doubler you sent me.' +> endo inbox +0. "alice" sent "This is the @doubler you sent me." +> endo adopt 0 doubler doubler-from-alice +> endo dismiss 0 +``` + +For a guest, the reserved name HOST refers to their host. +For both hosts and guests, SELF is the name of their own powers object. + # Familiar Chat The pet daemon (or familiar, if you will) maintains a petstore and mailbox for @@ -191,6 +263,7 @@ authority to maintain your petstore and mailbox. So, if you were to simulate a request from your cat: ``` +> endo mkguest cat > endo request 'pet me' --as cat ``` diff --git a/packages/cli/demo/counter.js b/packages/cli/demo/counter.js index 87f44e5b9f..853556ce3c 100644 --- a/packages/cli/demo/counter.js +++ b/packages/cli/demo/counter.js @@ -1,6 +1,6 @@ import { Far } from '@endo/far'; -export const make = powers => { +export const make = () => { let counter = 0; return Far('Counter', { incr() { diff --git a/packages/cli/demo/doubler.js b/packages/cli/demo/doubler.js index 81d7b8c041..514cb266b2 100644 --- a/packages/cli/demo/doubler.js +++ b/packages/cli/demo/doubler.js @@ -3,8 +3,8 @@ import { E, Far } from '@endo/far'; export const make = powers => { const counter = E(powers).request( 'HOST', - 'please give me a counter', - 'counter', + 'a counter, suitable for doubling', + 'my-counter', ); return Far('Doubler', { async incr() { From 378c81e26c016784d064641ef722264706b7ace7 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Mon, 28 Aug 2023 17:24:09 -0700 Subject: [PATCH 119/234] feat(cli): Demo support for message send --- packages/cli/demo/cat.js | 140 ++++++++++++++++++++++++++++++++++----- 1 file changed, 122 insertions(+), 18 deletions(-) diff --git a/packages/cli/demo/cat.js b/packages/cli/demo/cat.js index 4b3d020a00..e02d431afc 100644 --- a/packages/cli/demo/cat.js +++ b/packages/cli/demo/cat.js @@ -30,21 +30,32 @@ const dateFormatter = new window.Intl.DateTimeFormat(undefined, { timeStyle: 'long', }); -const followMessagesComponent = async (parentElement, powers) => { +const followMessagesComponent = async ($parent, $end, powers) => { for await (const message of makeRefIterator(E(powers).followMessages())) { - if (message.type === 'request') { - const { number, what, when, who, settled } = message; + const { number, who, when, dismissed } = message; + + const $error = document.createElement('span'); + $error.style.color = 'red'; + $error.innerText = ''; + // To be inserted later, but declared here for reference. + + const $message = document.createElement('div'); + $parent.insertBefore($message, $end); - const $message = document.createElement('div'); - parentElement.appendChild($message); + dismissed.then(() => { + $message.remove(); + }); - const $number = document.createElement('span'); - $number.innerText = `${number}. `; - $message.appendChild($number); + const $number = document.createElement('span'); + $number.innerText = `${number}. `; + $message.appendChild($number); - const $who = document.createElement('b'); - $who.innerText = `${who}:`; - $message.appendChild($who); + const $who = document.createElement('b'); + $who.innerText = `${who}:`; + $message.appendChild($who); + + if (message.type === 'request') { + const { what, settled } = message; const $what = document.createElement('span'); $what.innerText = ` ${what} `; @@ -71,10 +82,6 @@ const followMessagesComponent = async (parentElement, powers) => { }; $input.appendChild($reject); - const $error = document.createElement('span'); - $error.style.color = 'red'; - $input.appendChild($error); - $resolve.onclick = () => { E(powers) .resolve(number, $pet.value) @@ -86,12 +93,109 @@ const followMessagesComponent = async (parentElement, powers) => { settled.then(status => { $input.innerText = ` ${status} `; }); + } else if (message.type === 'package') { + const { strings, names } = message; + assert(Array.isArray(strings)); + assert(Array.isArray(names)); + + $message.appendChild(document.createTextNode(' "')); + + let index = 0; + for ( + index = 0; + index < Math.min(strings.length, names.length); + index += 1 + ) { + assert.typeof(strings[index], 'string'); + const outer = JSON.stringify(strings[index]); + const inner = outer.slice(1, outer.length - 1); + $message.appendChild(document.createTextNode(inner)); + assert.typeof(names[index], 'string'); + const name = `@${names[index]}`; + const $name = document.createElement('b'); + $name.innerText = name; + $message.appendChild($name); + } + if (strings.length > names.length) { + const outer = JSON.stringify(strings[index]); + const inner = outer.slice(1, outer.length - 1); + $message.appendChild(document.createTextNode(inner)); + } + + $message.appendChild(document.createTextNode('" ')); + + const $when = document.createElement('i'); + $when.innerText = dateFormatter.format(Date.parse(when)); + $message.appendChild($when); + + $message.appendChild(document.createTextNode(' ')); + + if (names.length > 0) { + const $names = document.createElement('select'); + $message.appendChild($names); + for (const name of names) { + const $name = document.createElement('option'); + $name.innerText = name; + $names.appendChild($name); + } + + $message.appendChild(document.createTextNode(' ')); + + const $as = document.createElement('input'); + $as.type = 'text'; + $message.appendChild($as); + + $message.appendChild(document.createTextNode(' ')); + + const $adopt = document.createElement('button'); + $adopt.innerText = 'Adopt'; + $message.appendChild($adopt); + $adopt.onclick = () => { + console.log($as.value, $as); + E(powers) + .adopt(number, $names.value, $as.value || $names.value) + .then( + () => { + $as.value = ''; + }, + error => { + $error.innerText = ` ${error.message}`; + }, + ); + }; + } } + + $message.appendChild(document.createTextNode(' ')); + + const $dismiss = document.createElement('button'); + $dismiss.innerText = 'Dismiss'; + $message.appendChild($dismiss); + $dismiss.onclick = () => { + E(powers) + .dismiss(number) + .catch(error => { + $error.innerText = ` ${error.message}`; + }); + }; + + $message.appendChild($error); } }; +const bodyComponent = ($parent, powers) => { + const $title = document.createElement('h1'); + $title.innerText = '🐈‍⬛'; + $parent.appendChild($title); + + const $endOfMessages = document.createTextNode(''); + $parent.appendChild($endOfMessages); + followMessagesComponent($parent, $endOfMessages, powers).catch( + window.reportError, + ); +}; + export const make = async powers => { - document.body.innerHTML = - '

🐈‍⬛ Familiar Chat

Or: Le Chat Familier

'; - followMessagesComponent(document.body, powers).catch(window.reportError); + document.body.innerHTML = ''; + bodyComponent(document.body, powers); }; From 7cb7e5a66ba97bd634c3dd7d62168a42932c6735 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Mon, 28 Aug 2023 17:49:03 -0700 Subject: [PATCH 120/234] feat(cli): Familiar Chat pet name inventory --- packages/cli/demo/cat.js | 41 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/packages/cli/demo/cat.js b/packages/cli/demo/cat.js index e02d431afc..671c1aa3e1 100644 --- a/packages/cli/demo/cat.js +++ b/packages/cli/demo/cat.js @@ -183,6 +183,43 @@ const followMessagesComponent = async ($parent, $end, powers) => { } }; +const followNamesComponent = async ($parent, $end, powers) => { + const $title = document.createElement('h2'); + $title.innerText = 'Inventory'; + $parent.insertBefore($title, $end); + + const $ul = document.createElement('ul'); + $parent.insertBefore($ul, $end); + + const $names = new Map(); + for await (const change of makeRefIterator(E(powers).followNames())) { + console.log(change); + if ('add' in change) { + const name = change.add; + + const $li = document.createElement('li'); + $ul.appendChild($li); + + const $name = document.createTextNode(`${name} `); + $li.appendChild($name); + $name.innerText = change.add; + + const $remove = document.createElement('button'); + $li.appendChild($remove); + $remove.innerText = 'Remove'; + $remove.onclick = () => E(powers).remove(name).catch(window.reportError); + + $names.set(name, $li); + } else if ('remove' in change) { + const $li = $names.get(change.remove); + if ($li !== undefined) { + $li.remove(); + $names.delete(change.remove); + } + } + } +}; + const bodyComponent = ($parent, powers) => { const $title = document.createElement('h1'); $title.innerText = '🐈‍⬛'; @@ -193,6 +230,10 @@ const bodyComponent = ($parent, powers) => { followMessagesComponent($parent, $endOfMessages, powers).catch( window.reportError, ); + + const $endOfNames = document.createTextNode(''); + $parent.appendChild($endOfNames); + followNamesComponent($parent, $endOfNames, powers).catch(window.reportError); }; export const make = async powers => { From d98d224e7f4b73a685797f6d76325bb1839b6a00 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Wed, 20 Sep 2023 16:15:42 -0700 Subject: [PATCH 121/234] chore: Update yarn.lock --- yarn.lock | 144 +++++++++++++++++++++++++++++++----------------------- 1 file changed, 84 insertions(+), 60 deletions(-) diff --git a/yarn.lock b/yarn.lock index 63e09d9b45..6cd64a3b5a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2187,90 +2187,89 @@ resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.0.tgz#591c1ce3a702c45ee15f47a42ade72c2fd78978a" integrity sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw== -"@typescript-eslint/eslint-plugin@^6.2.1": - version "6.2.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.2.1.tgz#41b79923fee46a745a3a50cba1c33c622aa3c79a" - integrity sha512-iZVM/ALid9kO0+I81pnp1xmYiFyqibAHzrqX4q5YvvVEyJqY+e6rfTXSCsc2jUxGNqJqTfFSSij/NFkZBiBzLw== +"@typescript-eslint/eslint-plugin@^6.6.0": + version "6.7.2" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.7.2.tgz#f18cc75c9cceac8080a9dc2e7d166008c5207b9f" + integrity sha512-ooaHxlmSgZTM6CHYAFRlifqh1OAr3PAQEwi7lhYhaegbnXrnh7CDcHmc3+ihhbQC7H0i4JF0psI5ehzkF6Yl6Q== dependencies: "@eslint-community/regexpp" "^4.5.1" - "@typescript-eslint/scope-manager" "6.2.1" - "@typescript-eslint/type-utils" "6.2.1" - "@typescript-eslint/utils" "6.2.1" - "@typescript-eslint/visitor-keys" "6.2.1" + "@typescript-eslint/scope-manager" "6.7.2" + "@typescript-eslint/type-utils" "6.7.2" + "@typescript-eslint/utils" "6.7.2" + "@typescript-eslint/visitor-keys" "6.7.2" debug "^4.3.4" graphemer "^1.4.0" ignore "^5.2.4" natural-compare "^1.4.0" - natural-compare-lite "^1.4.0" semver "^7.5.4" ts-api-utils "^1.0.1" -"@typescript-eslint/parser@^6.2.1": - version "6.2.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-6.2.1.tgz#e18a31eea1cca8841a565f1701960c8123ed07f9" - integrity sha512-Ld+uL1kYFU8e6btqBFpsHkwQ35rw30IWpdQxgOqOh4NfxSDH6uCkah1ks8R/RgQqI5hHPXMaLy9fbFseIe+dIg== +"@typescript-eslint/parser@^6.6.0": + version "6.7.2" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-6.7.2.tgz#e0ae93771441b9518e67d0660c79e3a105497af4" + integrity sha512-KA3E4ox0ws+SPyxQf9iSI25R6b4Ne78ORhNHeVKrPQnoYsb9UhieoiRoJgrzgEeKGOXhcY1i8YtOeCHHTDa6Fw== dependencies: - "@typescript-eslint/scope-manager" "6.2.1" - "@typescript-eslint/types" "6.2.1" - "@typescript-eslint/typescript-estree" "6.2.1" - "@typescript-eslint/visitor-keys" "6.2.1" + "@typescript-eslint/scope-manager" "6.7.2" + "@typescript-eslint/types" "6.7.2" + "@typescript-eslint/typescript-estree" "6.7.2" + "@typescript-eslint/visitor-keys" "6.7.2" debug "^4.3.4" -"@typescript-eslint/scope-manager@6.2.1": - version "6.2.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-6.2.1.tgz#b6f43a867b84e5671fe531f2b762e0b68f7cf0c4" - integrity sha512-UCqBF9WFqv64xNsIEPfBtenbfodPXsJ3nPAr55mGPkQIkiQvgoWNo+astj9ZUfJfVKiYgAZDMnM6dIpsxUMp3Q== +"@typescript-eslint/scope-manager@6.7.2": + version "6.7.2" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-6.7.2.tgz#cf59a2095d2f894770c94be489648ad1c78dc689" + integrity sha512-bgi6plgyZjEqapr7u2mhxGR6E8WCzKNUFWNh6fkpVe9+yzRZeYtDTbsIBzKbcxI+r1qVWt6VIoMSNZ4r2A+6Yw== dependencies: - "@typescript-eslint/types" "6.2.1" - "@typescript-eslint/visitor-keys" "6.2.1" + "@typescript-eslint/types" "6.7.2" + "@typescript-eslint/visitor-keys" "6.7.2" -"@typescript-eslint/type-utils@6.2.1": - version "6.2.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-6.2.1.tgz#8eb8a2cccdf39cd7cf93e02bd2c3782dc90b0525" - integrity sha512-fTfCgomBMIgu2Dh2Or3gMYgoNAnQm3RLtRp+jP7A8fY+LJ2+9PNpi5p6QB5C4RSP+U3cjI0vDlI3mspAkpPVbQ== +"@typescript-eslint/type-utils@6.7.2": + version "6.7.2" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-6.7.2.tgz#ed921c9db87d72fa2939fee242d700561454f367" + integrity sha512-36F4fOYIROYRl0qj95dYKx6kybddLtsbmPIYNK0OBeXv2j9L5nZ17j9jmfy+bIDHKQgn2EZX+cofsqi8NPATBQ== dependencies: - "@typescript-eslint/typescript-estree" "6.2.1" - "@typescript-eslint/utils" "6.2.1" + "@typescript-eslint/typescript-estree" "6.7.2" + "@typescript-eslint/utils" "6.7.2" debug "^4.3.4" ts-api-utils "^1.0.1" -"@typescript-eslint/types@6.2.1": - version "6.2.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-6.2.1.tgz#7fcdeceb503aab601274bf5e210207050d88c8ab" - integrity sha512-528bGcoelrpw+sETlyM91k51Arl2ajbNT9L4JwoXE2dvRe1yd8Q64E4OL7vHYw31mlnVsf+BeeLyAZUEQtqahQ== +"@typescript-eslint/types@6.7.2": + version "6.7.2" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-6.7.2.tgz#75a615a6dbeca09cafd102fe7f465da1d8a3c066" + integrity sha512-flJYwMYgnUNDAN9/GAI3l8+wTmvTYdv64fcH8aoJK76Y+1FCZ08RtI5zDerM/FYT5DMkAc+19E4aLmd5KqdFyg== -"@typescript-eslint/typescript-estree@6.2.1": - version "6.2.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-6.2.1.tgz#2af6e90c1e91cb725a5fe1682841a3f74549389e" - integrity sha512-G+UJeQx9AKBHRQBpmvr8T/3K5bJa485eu+4tQBxFq0KoT22+jJyzo1B50JDT9QdC1DEmWQfdKsa8ybiNWYsi0Q== +"@typescript-eslint/typescript-estree@6.7.2": + version "6.7.2" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-6.7.2.tgz#ce5883c23b581a5caf878af641e49dd0349238c7" + integrity sha512-kiJKVMLkoSciGyFU0TOY0fRxnp9qq1AzVOHNeN1+B9erKFCJ4Z8WdjAkKQPP+b1pWStGFqezMLltxO+308dJTQ== dependencies: - "@typescript-eslint/types" "6.2.1" - "@typescript-eslint/visitor-keys" "6.2.1" + "@typescript-eslint/types" "6.7.2" + "@typescript-eslint/visitor-keys" "6.7.2" debug "^4.3.4" globby "^11.1.0" is-glob "^4.0.3" semver "^7.5.4" ts-api-utils "^1.0.1" -"@typescript-eslint/utils@6.2.1", "@typescript-eslint/utils@^6.2.1": - version "6.2.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-6.2.1.tgz#2aa4279ec13053d05615bcbde2398e1e8f08c334" - integrity sha512-eBIXQeupYmxVB6S7x+B9SdBeB6qIdXKjgQBge2J+Ouv8h9Cxm5dHf/gfAZA6dkMaag+03HdbVInuXMmqFB/lKQ== +"@typescript-eslint/utils@6.7.2", "@typescript-eslint/utils@^6.6.0": + version "6.7.2" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-6.7.2.tgz#b9ef0da6f04932167a9222cb4ac59cb187165ebf" + integrity sha512-ZCcBJug/TS6fXRTsoTkgnsvyWSiXwMNiPzBUani7hDidBdj1779qwM1FIAmpH4lvlOZNF3EScsxxuGifjpLSWQ== dependencies: "@eslint-community/eslint-utils" "^4.4.0" "@types/json-schema" "^7.0.12" "@types/semver" "^7.5.0" - "@typescript-eslint/scope-manager" "6.2.1" - "@typescript-eslint/types" "6.2.1" - "@typescript-eslint/typescript-estree" "6.2.1" + "@typescript-eslint/scope-manager" "6.7.2" + "@typescript-eslint/types" "6.7.2" + "@typescript-eslint/typescript-estree" "6.7.2" semver "^7.5.4" -"@typescript-eslint/visitor-keys@6.2.1": - version "6.2.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-6.2.1.tgz#442e7c09fe94b715a54ebe30e967987c3c41fbf4" - integrity sha512-iTN6w3k2JEZ7cyVdZJTVJx2Lv7t6zFA8DCrJEHD2mwfc16AEvvBWVhbFh34XyG2NORCd0viIgQY1+u7kPI0WpA== +"@typescript-eslint/visitor-keys@6.7.2": + version "6.7.2" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-6.7.2.tgz#4cb2bd786f1f459731b0ad1584c9f73e1c7a4d5c" + integrity sha512-uVw9VIMFBUTz8rIeaUT3fFe8xIUx8r4ywAdlQv1ifH+6acn/XF8Y6rwJ7XNmkNMDrTW+7+vxFFPIF40nJCVsMQ== dependencies: - "@typescript-eslint/types" "6.2.1" + "@typescript-eslint/types" "6.7.2" eslint-visitor-keys "^3.4.1" "@webassemblyjs/ast@1.9.0": @@ -5528,6 +5527,21 @@ execa@^7.1.1: signal-exit "^3.0.7" strip-final-newline "^3.0.0" +execa@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/execa/-/execa-8.0.1.tgz#51f6a5943b580f963c3ca9c6321796db8cc39b8c" + integrity sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg== + dependencies: + cross-spawn "^7.0.3" + get-stream "^8.0.1" + human-signals "^5.0.0" + is-stream "^3.0.0" + merge-stream "^2.0.0" + npm-run-path "^5.1.0" + onetime "^6.0.0" + signal-exit "^4.1.0" + strip-final-newline "^3.0.0" + execution-time@^1.2.0: version "1.4.1" resolved "https://registry.yarnpkg.com/execution-time/-/execution-time-1.4.1.tgz#d942f835ca9fd608691c4912d55458fc4a495271" @@ -6076,6 +6090,11 @@ get-stream@^6.0.0, get-stream@^6.0.1: resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== +get-stream@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-8.0.1.tgz#def9dfd71742cd7754a7761ed43749a27d02eca2" + integrity sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA== + get-symbol-description@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.0.0.tgz#7fdb81c900101fbd564dd5f1a30af5aadc1e58d6" @@ -6660,6 +6679,11 @@ human-signals@^4.3.0: resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-4.3.1.tgz#ab7f811e851fca97ffbd2c1fe9a958964de321b2" integrity sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ== +human-signals@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-5.0.0.tgz#42665a284f9ae0dade3ba41ebc37eb4b852f3a28" + integrity sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ== + humanize-ms@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/humanize-ms/-/humanize-ms-1.2.1.tgz#c46e3159a293f6b896da29316d8b6fe8bb79bbed" @@ -8550,11 +8574,6 @@ nanomatch@^1.2.9: snapdragon "^0.8.1" to-regex "^3.0.1" -natural-compare-lite@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz#17b09581988979fddafe0201e931ba933c96cbb4" - integrity sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g== - natural-compare@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" @@ -11024,6 +11043,11 @@ signal-exit@^4.0.1: resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.0.2.tgz#ff55bb1d9ff2114c13b400688fa544ac63c36967" integrity sha512-MY2/qGx4enyjprQnFaZsHib3Yadh3IXyV2C321GY0pjGfVBu4un0uDJkwgdxqO+Rdx8JMT8IfJIRwbYVz3Ob3Q== +signal-exit@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" + integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== + simple-concat@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/simple-concat/-/simple-concat-1.0.1.tgz#f46976082ba35c2263f1c8ab5edfe26c41c9552f" @@ -12102,10 +12126,10 @@ typedarray@^0.0.6: resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.5.tgz#095979f9bcc0d09da324d58d03ce8f8374cbe65a" integrity sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g== -typescript@~5.1.3: - version "5.1.6" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.1.6.tgz#02f8ac202b6dad2c0dd5e0913745b47a37998274" - integrity sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA== +typescript@~5.2.2: + version "5.2.2" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.2.2.tgz#5ebb5e5a5b75f085f22bc3f8460fba308310fa78" + integrity sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w== uglify-js@^3.1.4: version "3.17.2" From 6f392092d64d5f5d6959d17cf8af9b8d6abd0687 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Mon, 28 Aug 2023 22:12:43 -0700 Subject: [PATCH 122/234] test(cli): Test and fix formatMessage --- packages/cli/package.json | 2 +- packages/cli/src/message-format.js | 4 ++-- packages/cli/test/test-message-format.js | 15 +++++++++++++++ 3 files changed, 18 insertions(+), 3 deletions(-) create mode 100644 packages/cli/test/test-message-format.js diff --git a/packages/cli/package.json b/packages/cli/package.json index 20c154cab8..b82ed72da6 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -26,7 +26,7 @@ "lint-fix": "eslint --fix .", "lint:js": "eslint .", "lint:types": "tsc -p jsconfig.json", - "test": "exit 0" + "test": "ava" }, "dependencies": { "@endo/compartment-mapper": "^0.9.2", diff --git a/packages/cli/src/message-format.js b/packages/cli/src/message-format.js index 9f8ddd07aa..2882e1d2c9 100644 --- a/packages/cli/src/message-format.js +++ b/packages/cli/src/message-format.js @@ -10,11 +10,11 @@ export const formatMessage = (strings, edgeNames) => { index < Math.min(strings.length, edgeNames.length); index += 1 ) { - message += strings[0].replace(/@/g, '\\@'); + message += strings[index].replace(/@/g, '\\@'); message += `@${edgeNames[index]}`; } if (strings.length > edgeNames.length) { - message += strings[index]; + message += strings[index].replace(/@/g, '\\@'); } return JSON.stringify(message); }; diff --git a/packages/cli/test/test-message-format.js b/packages/cli/test/test-message-format.js new file mode 100644 index 0000000000..1faf255342 --- /dev/null +++ b/packages/cli/test/test-message-format.js @@ -0,0 +1,15 @@ +import test from 'ava'; +import { formatMessage } from '../src/message-format.js'; + +test('message format', t => { + t.is(formatMessage([], []), JSON.stringify('')); + t.is(formatMessage(['Hello.'], []), JSON.stringify('Hello.')); + t.is( + formatMessage(['Hello, ', '!'], ['world']), + JSON.stringify('Hello, @world!'), + ); + t.is( + formatMessage(['team@example.com'], []), + JSON.stringify(String.raw`team\@example.com`), + ); +}); From 6c1439bdd3f087e22f435832f1c0a15a64e6f74f Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Mon, 28 Aug 2023 22:13:46 -0700 Subject: [PATCH 123/234] fix(cli): Correct command description kebab-case inconsistency --- packages/cli/src/endo.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cli/src/endo.js b/packages/cli/src/endo.js index 7c75d3c8ea..df53551f36 100644 --- a/packages/cli/src/endo.js +++ b/packages/cli/src/endo.js @@ -241,7 +241,7 @@ export const main = async rawArgs => { }); program - .command('send ') + .command('send ') .description('send a message with @named-values @for-you:from-me') .option( '-a,--as ', From 9167b187f51974d9044ee59098ae6cf368481b78 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Thu, 21 Sep 2023 11:44:50 -0700 Subject: [PATCH 124/234] docs(cli): Clarify how to try out endo today --- packages/cli/demo/README.md | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/packages/cli/demo/README.md b/packages/cli/demo/README.md index a5420b2924..17f12f654b 100644 --- a/packages/cli/demo/README.md +++ b/packages/cli/demo/README.md @@ -5,12 +5,16 @@ One day, this will be as simple as: > npm install -g @endo/cli -Until that day, clone the Endo repository, `yarn install`, and arrange to use -`packages/cli/bin/endo` to be `endo` in your shell. -For example, +Until that day, you will need to clone the Endo repository, check out the Endo +branch, install dependencies using Yarn, and arrange for the Endo command to +be available as `endo` in whatever shell you favor. +``` +> git clone https://github.com/endojs/endo.git +> git checkout endo > yarn > alias endo=$PWD/packages/cli/bin/endo +``` # A counter example From dd131ad0f0f3fbf7deb4e8c3da827cc80f4c5493 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Thu, 21 Sep 2023 11:45:08 -0700 Subject: [PATCH 125/234] fix(cli): Remove console debug line --- packages/cli/demo/cat.js | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/cli/demo/cat.js b/packages/cli/demo/cat.js index 671c1aa3e1..69ce79362b 100644 --- a/packages/cli/demo/cat.js +++ b/packages/cli/demo/cat.js @@ -193,7 +193,6 @@ const followNamesComponent = async ($parent, $end, powers) => { const $names = new Map(); for await (const change of makeRefIterator(E(powers).followNames())) { - console.log(change); if ('add' in change) { const name = change.add; From 7536e0e444ab3b17396a15e6aa2e1908b1026c5f Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Thu, 21 Sep 2023 15:00:08 -0700 Subject: [PATCH 126/234] refactor(cli): Lazy load locator and daemon modules if needed --- packages/cli/src/endo.js | 37 ++++++++++--------------------------- packages/cli/src/locator.js | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 27 deletions(-) create mode 100644 packages/cli/src/locator.js diff --git a/packages/cli/src/endo.js b/packages/cli/src/endo.js index df53551f36..8c66ee1ebf 100644 --- a/packages/cli/src/endo.js +++ b/packages/cli/src/endo.js @@ -7,18 +7,9 @@ import '@endo/eventual-send/shim.js'; import '@endo/lockdown/commit.js'; import fs from 'fs'; -import path from 'path'; import url from 'url'; -import os from 'os'; import { Command } from 'commander'; -import { start, stop, restart, clean, reset } from '@endo/daemon'; -import { - whereEndoState, - whereEndoEphemeralState, - whereEndoSock, - whereEndoCache, -} from '@endo/where'; const collect = (value, values) => values.concat([value]); @@ -26,24 +17,6 @@ const packageDescriptorPath = url.fileURLToPath( new URL('../package.json', import.meta.url), ); -const { username, homedir } = os.userInfo(); -const temp = os.tmpdir(); -const info = { - user: username, - home: homedir, - temp, -}; - -const statePath = whereEndoState(process.platform, process.env, info); -const ephemeralStatePath = whereEndoEphemeralState( - process.platform, - process.env, - info, -); -const sockPath = whereEndoSock(process.platform, process.env, info); -const cachePath = whereEndoCache(process.platform, process.env, info); -const logPath = path.join(statePath, 'endo.log'); - export const main = async rawArgs => { const program = new Command(); @@ -516,6 +489,7 @@ export const main = async rawArgs => { .command('state') .description('prints the state directory path') .action(async _cmd => { + const { statePath } = await import('./locator.js'); process.stdout.write(`${statePath}\n`); }); @@ -523,6 +497,7 @@ export const main = async rawArgs => { .command('run') .description('prints the daemon PID file path') .action(async _cmd => { + const { ephemeralStatePath } = await import('./locator.js'); process.stdout.write(`${ephemeralStatePath}\n`); }); @@ -530,6 +505,7 @@ export const main = async rawArgs => { .command('sock') .description('prints the UNIX domain socket or Windows named pipe path') .action(async _cmd => { + const { sockPath } = await import('./locator.js'); process.stdout.write(`${sockPath}\n`); }); @@ -537,6 +513,7 @@ export const main = async rawArgs => { .command('log') .description('prints the log file path') .action(async _cmd => { + const { logPath } = await import('./locator.js'); process.stdout.write(`${logPath}\n`); }); @@ -544,6 +521,7 @@ export const main = async rawArgs => { .command('cache') .description('prints the cache directory path') .action(async _cmd => { + const { cachePath } = await import('./locator.js'); process.stdout.write(`${cachePath}\n`); }); @@ -551,6 +529,7 @@ export const main = async rawArgs => { .command('start') .description('start the endo daemon') .action(async _cmd => { + const { start } = await import('@endo/daemon'); await start(); }); @@ -558,6 +537,7 @@ export const main = async rawArgs => { .command('stop') .description('stop the endo daemon') .action(async _cmd => { + const { stop } = await import('@endo/daemon'); await stop(); }); @@ -565,6 +545,7 @@ export const main = async rawArgs => { .command('restart') .description('stop and start the daemon') .action(async _cmd => { + const { restart } = await import('@endo/daemon'); await restart(); }); @@ -572,6 +553,7 @@ export const main = async rawArgs => { .command('clean') .description('erases ephemeral state') .action(async _cmd => { + const { clean } = await import('@endo/daemon'); await clean(); }); @@ -579,6 +561,7 @@ export const main = async rawArgs => { .command('reset') .description('erases persistent state and restarts if running') .action(async _cmd => { + const { reset } = await import('@endo/daemon'); await reset(); }); diff --git a/packages/cli/src/locator.js b/packages/cli/src/locator.js new file mode 100644 index 0000000000..bf92355b1d --- /dev/null +++ b/packages/cli/src/locator.js @@ -0,0 +1,32 @@ +/* global process */ +import os from 'os'; +import path from 'path'; + +import { + whereEndoState, + whereEndoEphemeralState, + whereEndoSock, + whereEndoCache, +} from '@endo/where'; + +const { username, homedir } = os.userInfo(); +const temp = os.tmpdir(); +const info = { + user: username, + home: homedir, + temp, +}; + +export const statePath = whereEndoState(process.platform, process.env, info); + +export const logPath = path.join(statePath, 'endo.log'); + +export const ephemeralStatePath = whereEndoEphemeralState( + process.platform, + process.env, + info, +); + +export const cachePath = whereEndoCache(process.platform, process.env, info); + +export const sockPath = whereEndoSock(process.platform, process.env, info); From f80f3c65dbea243c5351cdfa0f65043929b64c3f Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Thu, 21 Sep 2023 15:00:43 -0700 Subject: [PATCH 127/234] refactor(cli): Remove superfluous mkguest parameters --- packages/cli/src/mkguest.js | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/packages/cli/src/mkguest.js b/packages/cli/src/mkguest.js index 727caf92e3..5b513da270 100644 --- a/packages/cli/src/mkguest.js +++ b/packages/cli/src/mkguest.js @@ -3,13 +3,7 @@ import os from 'os'; import { E } from '@endo/far'; import { withEndoParty } from './context.js'; -export const mkguest = async ({ - cancel, - cancelled, - sockPath, - name, - partyNames, -}) => +export const mkguest = async ({ name, partyNames }) => withEndoParty(partyNames, { os, process }, async ({ party }) => { const newGuest = await E(party).provideGuest(name); console.log(newGuest); From 214fd1190fcac941ee101f880b25d4caaf959245 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Mon, 28 Aug 2023 22:15:34 -0700 Subject: [PATCH 128/234] fix(daemon): Classify read errors --- packages/daemon/src/daemon-node-powers.js | 15 +++++++++++++++ packages/daemon/src/daemon.js | 6 +++--- packages/daemon/src/types.d.ts | 1 + 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/packages/daemon/src/daemon-node-powers.js b/packages/daemon/src/daemon-node-powers.js index 3fd33e1148..189cf8afac 100644 --- a/packages/daemon/src/daemon-node-powers.js +++ b/packages/daemon/src/daemon-node-powers.js @@ -310,6 +310,20 @@ export const makePowers = ({ return fs.promises.readFile(path, 'utf-8'); }; + /** + * @param {string} path + */ + const maybeReadFileText = async path => + readFileText(path).catch(error => { + if ( + error.message.startsWith('ENOENT: ') || + error.message.startsWith('EISDIR: ') + ) { + return undefined; + } + throw error; + }); + /** * @param {string} path */ @@ -424,6 +438,7 @@ export const makePowers = ({ makeFileWriter, writeFileText, readFileText, + maybeReadFileText, readDirectory, makePath, joinPath, diff --git a/packages/daemon/src/daemon.js b/packages/daemon/src/daemon.js index a390fdf993..7859b0f943 100644 --- a/packages/daemon/src/daemon.js +++ b/packages/daemon/src/daemon.js @@ -424,10 +424,10 @@ const makeEndoBootstrap = ( formulaNumber, formulaPath, ) => { - const formulaText = await powers.readFileText(formulaPath).catch(() => { - // TODO handle EMFILE gracefully + const formulaText = await powers.maybeReadFileText(formulaPath); + if (formulaText === undefined) { throw new ReferenceError(`No reference exists at path ${formulaPath}`); - }); + } const formula = (() => { try { return JSON.parse(formulaText); diff --git a/packages/daemon/src/types.d.ts b/packages/daemon/src/types.d.ts index 95c235f5d0..ee615cc027 100644 --- a/packages/daemon/src/types.d.ts +++ b/packages/daemon/src/types.d.ts @@ -70,6 +70,7 @@ export type DaemonicPowers = { makeFileReader: (path: string) => Reader; makeFileWriter: (path: string) => Writer; readFileText: (path: string) => Promise; + maybeReadFileText: (path: string) => Promise; readDirectory: (path: string) => Promise>; writeFileText: (path: string, text: string) => Promise; makePath: (path: string) => Promise; From 302679f8f3704b64c486a4c893fbb56d936d853a Mon Sep 17 00:00:00 2001 From: kumavis Date: Fri, 13 Oct 2023 11:41:05 -1000 Subject: [PATCH 129/234] refactor(daemon): rework powers for platform portability --- packages/daemon/src/daemon-node-powers.js | 473 +++++++++++++++++----- packages/daemon/src/daemon-node.js | 117 +++++- packages/daemon/src/daemon.js | 317 +++------------ packages/daemon/src/guest.js | 2 + packages/daemon/src/host.js | 5 +- packages/daemon/src/mail.js | 9 +- packages/daemon/src/pet-store.js | 410 ++++++++++--------- packages/daemon/src/reader-ref.js | 19 +- packages/daemon/src/types.d.ts | 157 ++++--- 9 files changed, 862 insertions(+), 647 deletions(-) diff --git a/packages/daemon/src/daemon-node-powers.js b/packages/daemon/src/daemon-node-powers.js index 189cf8afac..f78b0f9046 100644 --- a/packages/daemon/src/daemon-node-powers.js +++ b/packages/daemon/src/daemon-node-powers.js @@ -1,15 +1,23 @@ // @ts-check -/* global process, setTimeout, clearTimeout */ /* eslint-disable no-void */ import { makePromiseKit } from '@endo/promise-kit'; import { makePipe } from '@endo/stream'; import { makeNodeReader, makeNodeWriter } from '@endo/stream-node'; +import { makeReaderRef } from './reader-ref.js'; +import { makePetStoreMaker } from './pet-store.js'; +import { servePrivatePortHttp } from './serve-private-port-http.js'; +import { servePrivatePath } from './serve-private-path.js'; + +const { quote: q } = assert; const textEncoder = new TextEncoder(); const medialIterationResult = harden({ done: false }); const finalIterationResult = harden({ done: false }); +const zero512 = + '0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'; + /** * @param {object} modules * @param {typeof import('ws')} modules.ws @@ -83,6 +91,13 @@ export const makeHttpPowers = ({ http, ws }) => { if (connect) { const wss = new WebSocketServer({ server }); wss.on('connection', (socket, req) => { + if (req.method === undefined) { + throw Error('expected method in request'); + } + if (req.url === undefined) { + throw Error('expected url in request'); + } + const { promise: closed, resolve: close, @@ -164,54 +179,13 @@ export const makeHttpPowers = ({ http, ws }) => { /** * @param {object} modules - * @param {typeof import('crypto')} modules.crypto * @param {typeof import('net')} modules.net - * @param {typeof import('fs')} modules.fs - * @param {typeof import('path')} modules.path - * @param {typeof import('child_process')} modules.popen - * @param {typeof import('url')} modules.url - * @param {typeof import('ws')} modules.ws * @param {typeof import('http')} modules.http - * @param {Record} modules.env - * @param {(pid: number) => void} modules.kill - * @returns {import('./types.js').DaemonicPowers} + * @param {typeof import('ws')} modules.ws + * @returns {import('./types.js').NetworkPowers} */ -export const makePowers = ({ - crypto, - net, - fs, - path: fspath, - popen, - url, - http, - ws, - kill, - env, -}) => { - /** @param {Error} error */ - const sinkError = error => { - console.error(error); - }; - - const makeSha512 = () => { - const digester = crypto.createHash('sha512'); - return harden({ - update: chunk => digester.update(chunk), - updateText: chunk => digester.update(textEncoder.encode(chunk)), - digestHex: () => digester.digest('hex'), - }); - }; - - const randomHex512 = () => - new Promise((resolve, reject) => - crypto.randomBytes(64, (err, bytes) => { - if (err) { - reject(err); - } else { - resolve(bytes.toString('hex')); - } - }), - ); +export const makeNetworkPowers = ({ http, ws, net }) => { + const { servePortHttp } = makeHttpPowers({ http, ws }); const serveListener = async (listen, cancelled) => { const [ @@ -267,18 +241,73 @@ export const makePowers = ({ cancelled, ); - const informParentWhenReady = () => { - if (process.send) { - process.send({ type: 'ready' }); + const connectionNumbers = (function* generateNumbers() { + let n = 0; + for (;;) { + yield n; + n += 1; } + })(); + + /** + * @param {import('@endo/far').FarRef} endoBootstrap + * @param {string} sockPath + * @param {Promise} cancelled + * @param {(error: Error) => void} exitWithError + * @returns {{ started: () => Promise, stopped: Promise }} + */ + const makePrivatePathService = ( + endoBootstrap, + sockPath, + cancelled, + exitWithError, + ) => { + const privatePathService = servePrivatePath(sockPath, endoBootstrap, { + servePath, + connectionNumbers, + cancelled, + exitWithError, + }); + return privatePathService; }; - const reportErrorToParent = message => { - if (process.send) { - process.send({ type: 'error', message }); - } + /** + * @param {import('@endo/far').FarRef} endoBootstrap + * @param {number} port + * @param {(port: Promise) => void} assignWebletPort + * @param {Promise} cancelled + * @param {(error: Error) => void} exitWithError + * @returns {{ started: () => Promise, stopped: Promise }} + */ + const makePrivateHttpService = ( + endoBootstrap, + port, + assignWebletPort, + cancelled, + exitWithError, + ) => { + const privateHttpService = servePrivatePortHttp(port, endoBootstrap, { + servePortHttp, + connectionNumbers, + cancelled, + exitWithError, + }); + + assignWebletPort(privateHttpService.started); + + return privateHttpService; }; + return harden({ + servePortHttp, + servePort, + servePath, + makePrivatePathService, + makePrivateHttpService, + }); +}; + +export const makeFilePowers = ({ fs, path: fspath }) => { /** * @param {string} path */ @@ -289,6 +318,7 @@ export const makePowers = ({ /** * @param {string} path + * @returns {import('@endo/stream').Writer} */ const makeFileWriter = path => { const nodeWriteStream = fs.createWriteStream(path); @@ -350,44 +380,254 @@ export const makePowers = ({ const joinPath = (...components) => fspath.join(...components); - const delay = async (ms, cancelled) => { - // Do not attempt to set up a timer if already cancelled. - await Promise.race([cancelled, undefined]); - return new Promise((resolve, reject) => { - const handle = setTimeout(resolve, ms); - cancelled.catch(error => { - reject(error); - clearTimeout(handle); - }); + return harden({ + makeFileReader, + makeFileWriter, + writeFileText, + readFileText, + maybeReadFileText, + readDirectory, + makePath, + joinPath, + removePath, + renamePath, + }); +}; + +/** + * @param {typeof import('crypto')} crypto + * @returns {import('./types.js').CryptoPowers} + */ +export const makeCryptoPowers = crypto => { + const makeSha512 = () => { + const digester = crypto.createHash('sha512'); + return harden({ + update: chunk => digester.update(chunk), + updateText: chunk => digester.update(textEncoder.encode(chunk)), + digestHex: () => digester.digest('hex'), + }); + }; + + const randomHex512 = () => + new Promise((resolve, reject) => + crypto.randomBytes(64, (err, bytes) => { + if (err) { + reject(err); + } else { + resolve(bytes.toString('hex')); + } + }), + ); + + return harden({ + makeSha512, + randomHex512, + }); +}; + +/** + * @param {(URL) => string} fileURLToPath + * @param {import('./types.js').FilePowers} filePowers + * @param {import('./types.js').CryptoPowers} cryptoPowers + * @param {import('./types.js').Locator} locator + * @param {boolean} [includeWebPageBundler] + * @returns {import('./types.js').DaemonicPersistencePowers} + */ +export const makeDaemonicPersistencePowers = ( + fileURLToPath, + filePowers, + cryptoPowers, + locator, + includeWebPageBundler = true, +) => { + const initializePersistence = async () => { + const { statePath, ephemeralStatePath, cachePath } = locator; + const statePathP = filePowers.makePath(statePath); + const ephemeralStatePathP = filePowers.makePath(ephemeralStatePath); + const cachePathP = filePowers.makePath(cachePath); + await Promise.all([statePathP, cachePathP, ephemeralStatePathP]); + }; + + const makeContentSha512Store = () => { + const { statePath } = locator; + const storageDirectoryPath = filePowers.joinPath(statePath, 'store-sha512'); + + return harden({ + /** + * @param {AsyncIterable} readable + * @returns {Promise} + */ + async store(readable) { + const digester = cryptoPowers.makeSha512(); + const storageId512 = await cryptoPowers.randomHex512(); + const temporaryStoragePath = filePowers.joinPath( + storageDirectoryPath, + storageId512, + ); + + // Stream to temporary file and calculate hash. + await filePowers.makePath(storageDirectoryPath); + const fileWriter = filePowers.makeFileWriter(temporaryStoragePath); + for await (const chunk of readable) { + digester.update(chunk); + await fileWriter.next(chunk); + } + await fileWriter.return(undefined); + + // Calculate hash. + const sha512 = digester.digestHex(); + // Finish with an atomic rename. + const storagePath = filePowers.joinPath(storageDirectoryPath, sha512); + await filePowers.renamePath(temporaryStoragePath, storagePath); + return sha512; + }, + /** + * @param {string} sha512 + * @returns {import('./types.js').AlmostEndoReadable} + */ + fetch(sha512) { + const storagePath = filePowers.joinPath(storageDirectoryPath, sha512); + const stream = () => { + const reader = filePowers.makeFileReader(storagePath); + return makeReaderRef(reader); + }; + const text = async () => { + return filePowers.readFileText(storagePath); + }; + const json = async () => { + return JSON.parse(await text()); + }; + return harden({ + sha512: () => sha512, + stream, + text, + json, + [Symbol.asyncIterator]: () => stream(), + }); + }, }); }; + /** + * @param {string} formulaType + * @param {string} formulaId512 + */ + const makeFormulaPath = (formulaType, formulaId512) => { + const { statePath } = locator; + if (formulaId512.length < 3) { + throw new TypeError( + `Invalid formula identifier ${q(formulaId512)} for formula of type ${q( + formulaType, + )}`, + ); + } + const head = formulaId512.slice(0, 2); + const tail = formulaId512.slice(3); + const directory = filePowers.joinPath( + statePath, + 'formulas', + formulaType, + head, + ); + const file = filePowers.joinPath(directory, `${tail}.json`); + return harden({ directory, file }); + }; + + /** + * @param {string} prefix + * @param {string} formulaNumber + * @returns {Promise} + */ + const readFormula = async (prefix, formulaNumber) => { + const { file: formulaPath } = makeFormulaPath(prefix, formulaNumber); + const formulaText = await filePowers.maybeReadFileText(formulaPath); + if (formulaText === undefined) { + throw new ReferenceError(`No reference exists at path ${formulaPath}`); + } + const formula = (() => { + try { + return JSON.parse(formulaText); + } catch (error) { + throw new TypeError( + `Corrupt description for reference in file ${formulaPath}: ${error.message}`, + ); + } + })(); + return formula; + }; + + // Persist instructions for revival (this can be collected) + const writeFormula = async (formula, formulaType, formulaId512) => { + const { directory, file } = makeFormulaPath(formulaType, formulaId512); + // TODO Take care to write atomically with a rename here. + await filePowers.makePath(directory); + await filePowers.writeFileText(file, `${q(formula)}\n`); + }; + + const webPageBundlerFormula = includeWebPageBundler + ? { + type: /** @type {'import-unsafe'} */ ('import-unsafe'), + worker: `worker-id512:${zero512}`, + powers: 'host', + importPath: fileURLToPath( + new URL('web-page-bundler.js', import.meta.url).href, + ), + } + : undefined; + + return harden({ + initializePersistence, + makeContentSha512Store, + readFormula, + writeFormula, + webPageBundlerFormula, + }); +}; + +export const makeDaemonicControlPowers = ( + locator, + fileURLToPath, + filePowers, + fs, + popen, +) => { + const endoWorkerPath = fileURLToPath( + new URL('worker-node.js', import.meta.url), + ); + /** * @param {string} id - * @param {string} path - * @param {string} logPath - * @param {string} pidPath - * @param {string} sockPath - * @param {string} statePath - * @param {string} ephemeralStatePath - * @param {string} cachePath * @param {Promise} cancelled */ - const makeWorker = async ( - id, - path, - logPath, - pidPath, - sockPath, - statePath, - ephemeralStatePath, - cachePath, - cancelled, - ) => { + const makeWorker = async (id, cancelled) => { + const { cachePath, statePath, ephemeralStatePath, sockPath } = locator; + + const workerCachePath = filePowers.joinPath(cachePath, 'worker-id512', id); + const workerStatePath = filePowers.joinPath(statePath, 'worker-id512', id); + const workerEphemeralStatePath = filePowers.joinPath( + ephemeralStatePath, + 'worker-id512', + id, + ); + + await Promise.all([ + filePowers.makePath(workerStatePath), + filePowers.makePath(workerEphemeralStatePath), + ]); + + const logPath = filePowers.joinPath(workerStatePath, 'worker.log'); + const pidPath = filePowers.joinPath(workerEphemeralStatePath, 'worker.pid'); + const log = fs.openSync(logPath, 'a'); const child = popen.fork( - path, - [id, sockPath, statePath, ephemeralStatePath, cachePath], + endoWorkerPath, + [ + id, + sockPath, + workerStatePath, + workerEphemeralStatePath, + workerCachePath, + ], { stdio: ['ignore', log, log, 'pipe', 'pipe', 'ipc'], // @ts-ignore Stale Node.js type definition. @@ -409,7 +649,7 @@ export const makePowers = ({ child.on('exit', () => resolve(undefined)); }); - await writeFileText(pidPath, `${child.pid}\n`); + await filePowers.writeFileText(pidPath, `${child.pid}\n`); cancelled.catch(async () => { child.kill(); @@ -418,37 +658,50 @@ export const makePowers = ({ return { reader, writer, closed, pid: child.pid }; }; + return harden({ + makeWorker, + }); +}; + +/** + * @param {object} opts + * @param {import('./types.js').Locator} opts.locator + * @param {typeof import('fs')} opts.fs + * @param {typeof import('child_process')} opts.popen + * @param {typeof import('url')} opts.url + * @param {import('./types.js').FilePowers} opts.filePowers + * @param {import('./types.js').CryptoPowers} opts.cryptoPowers + * @returns {import('./types.js').DaemonicPowers} + */ +export const makeDaemonicPowers = ({ + locator, + fs, + popen, + url, + filePowers, + cryptoPowers, +}) => { const { fileURLToPath } = url; - const endoWorkerPath = fileURLToPath( - new URL('worker-node.js', import.meta.url), + const petStorePowers = makePetStoreMaker(filePowers, locator); + const daemonicPersistencePowers = makeDaemonicPersistencePowers( + fileURLToPath, + filePowers, + cryptoPowers, + locator, ); - - return harden({ - kill: pid => kill(pid), - env: { ...env }, - sinkError, - makeSha512, - randomHex512, - servePort, - servePath, - informParentWhenReady, - reportErrorToParent, - makeFileReader, - makeFileWriter, - writeFileText, - readFileText, - maybeReadFileText, - readDirectory, - makePath, - joinPath, - removePath, - renamePath, - delay, - makeWorker, - endoWorkerPath, + const daemonicControlPowers = makeDaemonicControlPowers( + locator, fileURLToPath, + filePowers, + fs, + popen, + ); - ...makeHttpPowers({ http, ws }), + return harden({ + crypto: cryptoPowers, + petStore: petStorePowers, + persistence: daemonicPersistencePowers, + control: daemonicControlPowers, }); }; diff --git a/packages/daemon/src/daemon-node.js b/packages/daemon/src/daemon-node.js index c94a104b56..1915b544d3 100644 --- a/packages/daemon/src/daemon-node.js +++ b/packages/daemon/src/daemon-node.js @@ -1,3 +1,4 @@ +// @ts-check /* global process */ // Establish a perimeter: @@ -16,8 +17,13 @@ import http from 'http'; import * as ws from 'ws'; import { makePromiseKit } from '@endo/promise-kit'; -import { main } from './daemon.js'; -import { makePowers } from './daemon-node-powers.js'; +import { makeDaemon } from './daemon.js'; +import { + makeFilePowers, + makeNetworkPowers, + makeDaemonicPowers, + makeCryptoPowers, +} from './daemon-node-powers.js'; if (process.argv.length < 5) { throw new Error( @@ -30,7 +36,9 @@ if (process.argv.length < 5) { const [sockPath, statePath, ephemeralStatePath, cachePath] = process.argv.slice(2); -/** @type {import('../index.js').Locator} */ +const defaultHttpPort = 8920; // Eight Nine Duo Oh: ENDO. + +/** @type {import('../types.js').Locator} */ const locator = { sockPath, statePath, @@ -38,30 +46,113 @@ const locator = { cachePath, }; -const { env, kill } = process; +const { pid, env, kill } = process; -const powers = makePowers({ - crypto, - net, +const networkPowers = makeNetworkPowers({ http, ws, net }); +const filePowers = makeFilePowers({ fs, path }); +const cryptoPowers = makeCryptoPowers(crypto); +const powers = makeDaemonicPowers({ + locator, fs, - path, popen, url, - http, - ws, - env, - kill, + filePowers, + cryptoPowers, }); +const { persistence: daemonicPersistencePowers } = powers; + +const informParentWhenReady = () => { + if (process.send) { + process.send({ type: 'ready' }); + } +}; + +const reportErrorToParent = message => { + if (process.send) { + process.send({ type: 'error', message }); + } +}; const { promise: cancelled, reject: cancel } = /** @type {import('@endo/promise-kit').PromiseKit} */ ( makePromiseKit() ); +const updateRecordedPid = async () => { + const pidPath = filePowers.joinPath(ephemeralStatePath, 'endo.pid'); + + await filePowers + .readFileText(pidPath) + .then(pidText => { + const oldPid = Number(pidText); + kill(oldPid); + }) + .catch(() => {}); + + await filePowers.writeFileText(pidPath, `${pid}\n`); +}; + +const main = async () => { + const daemonLabel = `daemon on PID ${pid}`; + console.log(`Endo daemon starting on PID ${pid}`); + cancelled.catch(() => { + console.log(`Endo daemon stopping on PID ${pid}`); + }); + const requestedWebletPortText = env.ENDO_HTTP_PORT; + const requestedWebletPort = requestedWebletPortText + ? Number(requestedWebletPortText) + : defaultHttpPort; + + await daemonicPersistencePowers.initializePersistence(); + + const { endoBootstrap, cancelGracePeriod, assignWebletPort } = + await makeDaemon(powers, daemonLabel, cancel, cancelled); + + /** @param {Error} error */ + const exitWithError = error => { + cancel(error); + cancelGracePeriod(error); + }; + + // Start network services + const privatePathService = networkPowers.makePrivatePathService( + endoBootstrap, + sockPath, + cancelled, + exitWithError, + ); + const privateHttpService = networkPowers.makePrivateHttpService( + endoBootstrap, + requestedWebletPort, + assignWebletPort, + cancelled, + exitWithError, + ); + const services = [privatePathService, privateHttpService]; + await Promise.all(services.map(({ started }) => started)).then( + () => { + informParentWhenReady(); + }, + error => { + reportErrorToParent(error.message); + throw error; + }, + ); + const servicesStopped = Promise.all(services.map(({ stopped }) => stopped)); + + // Record self as official daemon process + await updateRecordedPid(); + + // Wait for services to end normally + await servicesStopped; + cancel(new Error('Terminated normally')); + cancelGracePeriod(new Error('Terminated normally')); +}; + process.once('SIGINT', () => cancel(new Error('SIGINT'))); process.exitCode = 1; -main(powers, locator, process.pid, cancel, cancelled).then( +main().then( () => { process.exitCode = 0; }, diff --git a/packages/daemon/src/daemon.js b/packages/daemon/src/daemon.js index 7859b0f943..5cc5d45b4f 100644 --- a/packages/daemon/src/daemon.js +++ b/packages/daemon/src/daemon.js @@ -1,31 +1,21 @@ // @ts-check /// -// Establish a perimeter: -import 'ses'; -import '@endo/eventual-send/shim.js'; -import '@endo/promise-kit/shim.js'; -import '@endo/lockdown/commit.js'; +/* global setTimeout, clearTimeout */ import { E, Far } from '@endo/far'; import { makePromiseKit } from '@endo/promise-kit'; import { makeNetstringCapTP } from './connection.js'; import { makeRefReader } from './ref-reader.js'; -import { makeReaderRef } from './reader-ref.js'; -import { makeOwnPetStore, makeIdentifiedPetStore } from './pet-store.js'; import { makeMailboxMaker } from './mail.js'; import { makeGuestMaker } from './guest.js'; import { makeHostMaker } from './host.js'; -import { servePrivatePortHttp } from './serve-private-port-http.js'; -import { servePrivatePath } from './serve-private-path.js'; const { quote: q } = assert; const zero512 = '0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'; -const defaultHttpPort = 8920; // Eight Nine Duo Oh: ENDO. - /** @type {import('./types.js').EndoGuest} */ const leastAuthority = Far('EndoGuest', { async request() { @@ -33,9 +23,20 @@ const leastAuthority = Far('EndoGuest', { }, }); +const delay = async (ms, cancelled) => { + // Do not attempt to set up a timer if already cancelled. + await Promise.race([cancelled, undefined]); + return new Promise((resolve, reject) => { + const handle = setTimeout(resolve, ms); + cancelled.catch(error => { + reject(error); + clearTimeout(handle); + }); + }); +}; + /** * @param {import('./types.js').DaemonicPowers} powers - * @param {import('./types.js').Locator} locator * @param {Promise} webletPortP * @param {object} args * @param {Promise} args.cancelled @@ -45,11 +46,17 @@ const leastAuthority = Far('EndoGuest', { */ const makeEndoBootstrap = ( powers, - locator, webletPortP, { cancelled, cancel, gracePeriodMs, gracePeriodElapsed }, ) => { - const { randomHex512, makeSha512 } = powers; + const { + crypto: cryptoPowers, + petStore: petStorePowers, + persistence: persistencePowers, + control: controlPowers, + } = powers; + const { randomHex512, makeSha512 } = cryptoPowers; + const contentStore = persistencePowers.makeContentSha512Store(); /** @type {Map} */ const valuePromiseForFormulaIdentifier = new Map(); @@ -65,21 +72,7 @@ const makeEndoBootstrap = ( * @param {string} sha512 */ const makeSha512ReadableBlob = sha512 => { - const storageDirectoryPath = powers.joinPath( - locator.statePath, - 'store-sha512', - ); - const storagePath = powers.joinPath(storageDirectoryPath, sha512); - const stream = async () => { - const reader = powers.makeFileReader(storagePath); - return makeReaderRef(reader); - }; - const text = async () => { - return powers.readFileText(storagePath); - }; - const json = async () => { - return JSON.parse(await text()); - }; + const { text, json, stream } = contentStore.fetch(sha512); return Far(`Readable file with SHA-512 ${sha512.slice(0, 8)}...`, { sha512: () => sha512, stream, @@ -93,35 +86,8 @@ const makeEndoBootstrap = ( * @param {import('@endo/eventual-send').ERef>} readerRef */ const storeReaderRef = async readerRef => { - const storageDirectoryPath = powers.joinPath( - locator.statePath, - 'store-sha512', - ); - await powers.makePath(storageDirectoryPath); - - // Pump the reader into a temporary file and hash. - // We use a temporary file to avoid leaving a partially writen object, - // but also because we won't know the name we will use until we've - // completed the hash. - const digester = powers.makeSha512(); - const storageId512 = await powers.randomHex512(); - const temporaryStoragePath = powers.joinPath( - storageDirectoryPath, - storageId512, - ); - const writer = powers.makeFileWriter(temporaryStoragePath); - for await (const chunk of makeRefReader(readerRef)) { - await writer.next(chunk); - digester.update(chunk); - } - await writer.return(undefined); - const sha512 = digester.digestHex(); - - // Finish with an atomic rename. - const storagePath = powers.joinPath(storageDirectoryPath, sha512); - await powers.renamePath(temporaryStoragePath, storagePath); - - return `readable-blob-sha512:${sha512}`; + const sha512Hex = await contentStore.store(makeRefReader(readerRef)); + return `readable-blob-sha512:${sha512Hex}`; }; /** @@ -139,26 +105,6 @@ const makeEndoBootstrap = ( const makeIdentifiedWorker = async workerId512 => { // TODO validate workerId512 const workerFormulaIdentifier = `worker-id512:${workerId512}`; - const workerCachePath = powers.joinPath( - locator.cachePath, - 'worker-id512', - workerId512, - ); - const workerStatePath = powers.joinPath( - locator.statePath, - 'worker-id512', - workerId512, - ); - const workerEphemeralStatePath = powers.joinPath( - locator.ephemeralStatePath, - 'worker-id512', - workerId512, - ); - - await Promise.all([ - powers.makePath(workerStatePath), - powers.makePath(workerEphemeralStatePath), - ]); const { reject: cancelWorker, promise: workerCancelled } = /** @type {import('@endo/promise-kit').PromiseKit} */ ( @@ -166,27 +112,12 @@ const makeEndoBootstrap = ( ); cancelled.catch(async error => cancelWorker(error)); - const logPath = powers.joinPath(workerStatePath, 'worker.log'); - const workerPidPath = powers.joinPath( - workerEphemeralStatePath, - 'worker.pid', - ); const { reader, writer, closed: workerClosed, pid: workerPid, - } = await powers.makeWorker( - workerId512, - powers.endoWorkerPath, - logPath, - workerPidPath, - locator.sockPath, - workerStatePath, - workerEphemeralStatePath, - workerCachePath, - workerCancelled, - ); + } = await controlPowers.makeWorker(workerId512, workerCancelled); console.log( `Endo worker started PID ${workerPid} unique identifier ${workerId512}`, @@ -218,8 +149,7 @@ const makeEndoBootstrap = ( gracePeriodElapsed, closed, ]).then(cancelWorkerGracePeriod, cancelWorkerGracePeriod); - await powers - .delay(gracePeriodMs, workerGracePeriodCancelled) + await delay(gracePeriodMs, workerGracePeriodCancelled) .then(() => { throw new Error( `Worker termination grace period ${gracePeriodMs}ms elapsed`, @@ -382,65 +312,6 @@ const makeEndoBootstrap = ( } }; - /** - * @param {string} formulaType - * @param {string} formulaId512 - */ - const makeFormulaPath = (formulaType, formulaId512) => { - if (formulaId512.length < 3) { - throw new TypeError( - `Invalid formula identifier ${q(formulaId512)} for formula of type ${q( - formulaType, - )}`, - ); - } - const head = formulaId512.slice(0, 2); - const tail = formulaId512.slice(3); - const directory = powers.joinPath( - locator.statePath, - 'formulas', - formulaType, - head, - ); - const file = powers.joinPath(directory, `${tail}.json`); - return { directory, file }; - }; - - // Persist instructions for revival (this can be collected) - const writeFormula = async (formula, formulaType, formulaId512) => { - const { directory, file } = makeFormulaPath(formulaType, formulaId512); - // TODO Take care to write atomically with a rename here. - await powers.makePath(directory); - await powers.writeFileText(file, `${q(formula)}\n`); - }; - - /** - * @param {string} formulaIdentifier - * @param {string} formulaNumber - * @param {string} formulaPath - */ - const makeValueForFormulaAtPath = async ( - formulaIdentifier, - formulaNumber, - formulaPath, - ) => { - const formulaText = await powers.maybeReadFileText(formulaPath); - if (formulaText === undefined) { - throw new ReferenceError(`No reference exists at path ${formulaPath}`); - } - const formula = (() => { - try { - return JSON.parse(formulaText); - } catch (error) { - throw new TypeError( - `Corrupt description for reference in file ${formulaPath}: ${error.message}`, - ); - } - })(); - // TODO validate - return makeValueForFormula(formulaIdentifier, formulaNumber, formula); - }; - /** * @param {string} formulaIdentifier */ @@ -448,7 +319,7 @@ const makeEndoBootstrap = ( const delimiterIndex = formulaIdentifier.indexOf(':'); if (delimiterIndex < 0) { if (formulaIdentifier === 'pet-store') { - return makeOwnPetStore(powers, locator, 'pet-store'); + return petStorePowers.makeOwnPetStore('pet-store'); } else if (formulaIdentifier === 'host') { // Behold, recursion: // eslint-disable-next-line no-use-before-define @@ -464,14 +335,14 @@ const makeEndoBootstrap = ( } else if (formulaIdentifier === 'least-authority') { return leastAuthority; } else if (formulaIdentifier === 'web-page-js') { - return makeValueForFormula('web-page-js', zero512, { - type: /** @type {'import-unsafe'} */ ('import-unsafe'), - worker: `worker-id512:${zero512}`, - powers: 'host', - importPath: powers.fileURLToPath( - new URL('web-page-bundler.js', import.meta.url).href, - ), - }); + if (persistencePowers.webPageBundlerFormula === undefined) { + throw Error('No web-page-js formula provided.'); + } + return makeValueForFormula( + 'web-page-js', + zero512, + persistencePowers.webPageBundlerFormula, + ); } throw new TypeError( `Formula identifier must have a colon: ${q(formulaIdentifier)}`, @@ -484,7 +355,7 @@ const makeEndoBootstrap = ( } else if (prefix === 'worker-id512') { return makeIdentifiedWorker(formulaNumber); } else if (prefix === 'pet-store-id512') { - return makeIdentifiedPetStore(powers, locator, formulaNumber); + return petStorePowers.makeIdentifiedPetStore(formulaNumber); } else if (prefix === 'host-id512') { // Behold, recursion: // eslint-disable-next-line no-use-before-define @@ -502,8 +373,12 @@ const makeEndoBootstrap = ( 'web-bundle', ].includes(prefix) ) { - const { file: path } = makeFormulaPath(prefix, formulaNumber); - return makeValueForFormulaAtPath(formulaIdentifier, formulaNumber, path); + const formula = await persistencePowers.readFormula( + prefix, + formulaNumber, + ); + // TODO validate + return makeValueForFormula(formulaIdentifier, formulaNumber, formula); } else { throw new TypeError( `Invalid formula identifier, unrecognized type ${q(formulaIdentifier)}`, @@ -523,7 +398,7 @@ const makeEndoBootstrap = ( ) => { const formulaIdentifier = `${formulaType}:${formulaNumber}`; - await writeFormula(formula, formulaType, formulaNumber); + await persistencePowers.writeFormula(formula, formulaType, formulaNumber); // Behold, recursion: // eslint-disable-next-line no-use-before-define const promiseForValue = makeValueForFormula( @@ -549,7 +424,7 @@ const makeEndoBootstrap = ( * @param {string} formulaType */ const provideValueForFormula = async (formula, formulaType) => { - const formulaNumber = await powers.randomHex512(); + const formulaNumber = await randomHex512(); return provideValueForNumberedFormula(formulaType, formulaNumber, formula); }; @@ -628,19 +503,13 @@ const makeEndoBootstrap = ( return endoBootstrap; }; -/* +/** * @param {import('./types.js').DaemonicPowers} powers - * @param {import('./types.js').Locator} locator - * @param {number | undefined} pid + * @param {string} daemonLabel * @param {(error: Error) => void} cancel * @param {Promise} cancelled */ -export const main = async (powers, locator, pid, cancel, cancelled) => { - console.log(`Endo daemon starting on PID ${pid}`); - cancelled.catch(() => { - console.log(`Endo daemon stopping on PID ${pid}`); - }); - +export const makeDaemon = async (powers, daemonLabel, cancel, cancelled) => { const { promise: gracePeriodCancelled, reject: cancelGracePeriod } = /** @type {import('@endo/promise-kit').PromiseKit} */ ( makePromiseKit() @@ -651,102 +520,24 @@ export const main = async (powers, locator, pid, cancel, cancelled) => { /** @type {Promise} */ const gracePeriodElapsed = cancelled.catch(async error => { - await powers.delay(gracePeriodMs, gracePeriodCancelled); + await delay(gracePeriodMs, gracePeriodCancelled); console.log( - `Endo daemon grace period ${gracePeriodMs}ms elapsed on PID ${pid}`, + `Endo daemon grace period ${gracePeriodMs}ms elapsed for ${daemonLabel}`, ); throw error; }); - /** @param {Error} error */ - const exitWithError = error => { - cancel(error); - cancelGracePeriod(error); - }; - - const statePathP = powers.makePath(locator.statePath); - const ephemeralStatePathP = powers.makePath(locator.ephemeralStatePath); - const cachePathP = powers.makePath(locator.cachePath); - await Promise.all([statePathP, cachePathP, ephemeralStatePathP]); - - const requestedWebletPortText = powers.env.ENDO_HTTP_PORT; - const requestedWebletPort = requestedWebletPortText - ? Number(requestedWebletPortText) - : defaultHttpPort; - const { promise: assignedWebletPortP, resolve: assignWebletPort } = /** @type {import('@endo/promise-kit').PromiseKit} */ ( makePromiseKit() ); - const endoBootstrap = makeEndoBootstrap( - powers, - locator, - assignedWebletPortP, - { - cancelled, - cancel, - gracePeriodMs, - gracePeriodElapsed, - }, - ); - - const connectionNumbers = (function* generateNumbers() { - let n = 0; - for (;;) { - yield n; - n += 1; - } - })(); - - const { servePath, servePortHttp } = powers; - - const privatePathService = servePrivatePath(locator.sockPath, endoBootstrap, { - servePath, - connectionNumbers, + const endoBootstrap = makeEndoBootstrap(powers, assignedWebletPortP, { cancelled, - exitWithError, + cancel, + gracePeriodMs, + gracePeriodElapsed, }); - const privateHttpService = servePrivatePortHttp( - requestedWebletPort, - endoBootstrap, - { - servePortHttp, - connectionNumbers, - cancelled, - exitWithError, - }, - ); - - assignWebletPort(privateHttpService.started); - - const services = [privatePathService, privateHttpService]; - - await Promise.all(services.map(({ started }) => started)).then( - () => { - powers.informParentWhenReady(); - }, - error => { - powers.reportErrorToParent(error.message); - throw error; - }, - ); - - const pidPath = powers.joinPath(locator.ephemeralStatePath, 'endo.pid'); - - await powers - .readFileText(pidPath) - .then(pidText => { - const oldPid = Number(pidText); - powers.kill(oldPid); - }) - .catch(() => {}); - - await powers.writeFileText(pidPath, `${pid}\n`); - - await Promise.all(services.map(({ stopped }) => stopped)); - - cancel(new Error('Terminated normally')); - cancelGracePeriod(new Error('Terminated normally')); + return { endoBootstrap, cancelGracePeriod, assignWebletPort }; }; diff --git a/packages/daemon/src/guest.js b/packages/daemon/src/guest.js index 2be2283fa5..a09dfb8619 100644 --- a/packages/daemon/src/guest.js +++ b/packages/daemon/src/guest.js @@ -1,3 +1,5 @@ +// @ts-check + import { Far } from '@endo/far'; export const makeGuestMaker = ({ diff --git a/packages/daemon/src/host.js b/packages/daemon/src/host.js index 1fa3947c25..52c168629e 100644 --- a/packages/daemon/src/host.js +++ b/packages/daemon/src/host.js @@ -1,3 +1,5 @@ +// @ts-check + import { Far } from '@endo/far'; import { assertPetName } from './pet-name.js'; @@ -65,13 +67,10 @@ export const makeHostMaker = ({ formulaIdentifier = lookupFormulaIdentifierForName(petName); } if (formulaIdentifier === undefined) { - const id512 = await randomHex512(); - const guestStoreFormulaIdentifier = `pet-store-id512:${id512}`; /** @type {import('./types.js').GuestFormula} */ const formula = { type: /* @type {'guest'} */ 'guest', host: hostFormulaIdentifier, - store: guestStoreFormulaIdentifier, }; const { value, formulaIdentifier: guestFormulaIdentifier } = // Behold, recursion: diff --git a/packages/daemon/src/mail.js b/packages/daemon/src/mail.js index 70479a5b4e..e02adf1077 100644 --- a/packages/daemon/src/mail.js +++ b/packages/daemon/src/mail.js @@ -1,3 +1,5 @@ +// @ts-check + import { makePromiseKit } from '@endo/promise-kit'; import { makeChangeTopic } from './pubsub.js'; import { makeIteratorRef } from './reader-ref.js'; @@ -81,7 +83,8 @@ export const makeMailboxMaker = ({ * @returns {import('./types.js').Message | undefined} */ const dubMessage = message => { - if (message.type === 'request') { + const { type } = message; + if (type === 'request') { const { who: senderFormulaIdentifier, ...rest } = message; const [senderName] = reverseLookupFormulaIdentifier( senderFormulaIdentifier, @@ -90,7 +93,7 @@ export const makeMailboxMaker = ({ return { who: senderName, ...rest }; } return undefined; - } else if (message.type === 'package') { + } else if (type === 'package') { const { formulas: _, who: senderFormulaIdentifier, ...rest } = message; const [senderName] = reverseLookupFormulaIdentifier( senderFormulaIdentifier, @@ -100,7 +103,7 @@ export const makeMailboxMaker = ({ } return undefined; } - throw new Error(`panic: Unknown message type ${message.type}`); + throw Error(`panic: Unknown message type ${type}`); }; const listMessages = async () => diff --git a/packages/daemon/src/pet-store.js b/packages/daemon/src/pet-store.js index 98a485b054..7e97388a82 100644 --- a/packages/daemon/src/pet-store.js +++ b/packages/daemon/src/pet-store.js @@ -1,3 +1,5 @@ +// @ts-check + import { Far } from '@endo/far'; import { assertPetName } from './pet-name.js'; import { makeChangeTopic } from './pubsub.js'; @@ -10,228 +12,238 @@ const validFormulaPattern = /^(?:host|pet-store|(?:readable-blob-sha512|worker-id512|pet-store-id512|eval-id512|import-unsafe-id512|import-bundle-id512|host-id512|guest-id512):[0-9a-f]{128}|web-bundle:[0-9a-f]{32})$/; /** - * @param {import('./types.js').DaemonicPowers} powers - * @param {string} petNameDirectoryPath + * @param {import('./types.js').FilePowers} filePowers + * @param {import('./types.js').Locator} locator */ -const makePetStoreAtPath = async (powers, petNameDirectoryPath) => { - /** @type {Map} */ - const petNames = new Map(); - /** @type {Map>} */ - const formulaIdentifiers = new Map(); - /** @type {import('./types.js').Topic} */ - const changesTopic = makeChangeTopic(); - - /** @param {string} petName */ - const read = async petName => { - const petNamePath = powers.joinPath(petNameDirectoryPath, petName); - const petNameText = await powers.readFileText(petNamePath); - const formulaIdentifier = petNameText.trim(); - if (!validFormulaPattern.test(formulaIdentifier)) { - throw new Error( - `Invalid formula identifier ${q(formulaIdentifier)} for pet name ${q( - petName, - )}`, - ); - } - return formulaIdentifier; - }; - - await powers.makePath(petNameDirectoryPath); +export const makePetStoreMaker = (filePowers, locator) => { + /** + * @param {string} petNameDirectoryPath + * @returns {Promise>} + */ + const makePetStoreAtPath = async petNameDirectoryPath => { + /** @type {Map} */ + const petNames = new Map(); + /** @type {Map>} */ + const formulaIdentifiers = new Map(); + /** @type {import('./types.js').Topic} */ + const changesTopic = makeChangeTopic(); + + /** @param {string} petName */ + const read = async petName => { + const petNamePath = filePowers.joinPath(petNameDirectoryPath, petName); + const petNameText = await filePowers.readFileText(petNamePath); + const formulaIdentifier = petNameText.trim(); + if (!validFormulaPattern.test(formulaIdentifier)) { + throw new Error( + `Invalid formula identifier ${q(formulaIdentifier)} for pet name ${q( + petName, + )}`, + ); + } + return formulaIdentifier; + }; + + await filePowers.makePath(petNameDirectoryPath); + + const fileNames = await filePowers.readDirectory(petNameDirectoryPath); + await Promise.all( + fileNames.map(async petName => { + assertPetName(petName); + const formulaIdentifier = await read(petName); + petNames.set(petName, formulaIdentifier); + const formulaPetNames = formulaIdentifiers.get(formulaIdentifier); + if (formulaPetNames !== undefined) { + formulaPetNames.add(petName); + } else { + formulaIdentifiers.set(formulaIdentifier, new Set([petName])); + } + }), + ); - const fileNames = await powers.readDirectory(petNameDirectoryPath); - await Promise.all( - fileNames.map(async petName => { + /** @param {string} petName */ + const lookup = petName => { + assertPetName(petName); + return petNames.get(petName); + }; + + /** + * @param {string} petName + * @param {string} formulaIdentifier + */ + const write = async (petName, formulaIdentifier) => { assertPetName(petName); - const formulaIdentifier = await read(petName); + if (!validFormulaPattern.test(formulaIdentifier)) { + throw new Error(`Invalid formula identifier ${q(formulaIdentifier)}`); + } + petNames.set(petName, formulaIdentifier); + const formulaPetNames = formulaIdentifiers.get(formulaIdentifier); - if (formulaPetNames !== undefined) { - formulaPetNames.add(petName); - } else { + if (formulaPetNames === undefined) { formulaIdentifiers.set(formulaIdentifier, new Set([petName])); + } else { + formulaPetNames.add(petName); } - }), - ); - /** @param {string} petName */ - const lookup = petName => { - assertPetName(petName); - return petNames.get(petName); - }; - - /** - * @param {string} petName - * @param {string} formulaIdentifier - */ - const write = async (petName, formulaIdentifier) => { - assertPetName(petName); - if (!validFormulaPattern.test(formulaIdentifier)) { - throw new Error(`Invalid formula identifier ${q(formulaIdentifier)}`); - } - - petNames.set(petName, formulaIdentifier); - - const formulaPetNames = formulaIdentifiers.get(formulaIdentifier); - if (formulaPetNames === undefined) { - formulaIdentifiers.set(formulaIdentifier, new Set([petName])); - } else { - formulaPetNames.add(petName); - } + const petNamePath = filePowers.joinPath(petNameDirectoryPath, petName); + const petNameText = `${formulaIdentifier}\n`; + await filePowers.writeFileText(petNamePath, petNameText); + changesTopic.publisher.next({ add: petName }); + }; + + const list = () => harden([...petNames.keys()].sort()); + + const follow = async () => + makeIteratorRef( + (async function* currentAndSubsequentNames() { + const changes = changesTopic.subscribe(); + for (const name of [...petNames.keys()].sort()) { + yield { add: name }; + } + yield* changes; + })(), + ); - const petNamePath = powers.joinPath(petNameDirectoryPath, petName); - const petNameText = `${formulaIdentifier}\n`; - await powers.writeFileText(petNamePath, petNameText); - changesTopic.publisher.next({ add: petName }); - }; + /** + * @param {string} petName + */ + const remove = async petName => { + assertPetName(petName); + const formulaIdentifier = petNames.get(petName); + if (formulaIdentifier === undefined) { + throw new Error( + `Formula does not exist for pet name ${JSON.stringify(petName)}`, + ); + } + if (!validFormulaPattern.test(formulaIdentifier)) { + throw new Error(`Invalid formula identifier ${q(formulaIdentifier)}`); + } - const list = () => harden([...petNames.keys()].sort()); + const petNamePath = filePowers.joinPath(petNameDirectoryPath, petName); + await filePowers.removePath(petNamePath); + petNames.delete(petName); + const formulaPetNames = formulaIdentifiers.get(petName); + if (formulaPetNames !== undefined) { + formulaPetNames.delete(petName); + } + changesTopic.publisher.next({ remove: petName }); + // TODO consider retaining a backlog of deleted names for recovery + // TODO consider tracking historical pet names for formulas + }; + + /** + * @param {string} fromName + * @param {string} toName + */ + const rename = async (fromName, toName) => { + assertPetName(fromName); + assertPetName(toName); + if (fromName === toName) { + return; + } + const formulaIdentifier = petNames.get(fromName); + const overwrittenFormulaIdentifier = petNames.get(toName); + if (formulaIdentifier === undefined) { + throw new Error( + `Formula does not exist for pet name ${JSON.stringify(fromName)}`, + ); + } + if (!validFormulaPattern.test(formulaIdentifier)) { + throw new Error(`Invalid formula identifier ${q(formulaIdentifier)}`); + } + if ( + overwrittenFormulaIdentifier !== undefined && + !validFormulaPattern.test(overwrittenFormulaIdentifier) + ) { + throw new Error( + `Invalid formula identifier ${q(overwrittenFormulaIdentifier)}`, + ); + } - const follow = async () => - makeIteratorRef( - (async function* currentAndSubsequentNames() { - const changes = changesTopic.subscribe(); - for (const name of [...petNames.keys()].sort()) { - yield { add: name }; + const fromPath = filePowers.joinPath(petNameDirectoryPath, fromName); + const toPath = filePowers.joinPath(petNameDirectoryPath, toName); + await filePowers.renamePath(fromPath, toPath); + petNames.set(toName, formulaIdentifier); + petNames.delete(fromName); + + // Delete the back-reference for the overwritten pet name if it existed. + if (overwrittenFormulaIdentifier !== undefined) { + const overwrittenFormulaPetNames = formulaIdentifiers.get( + overwrittenFormulaIdentifier, + ); + if (overwrittenFormulaPetNames !== undefined) { + overwrittenFormulaPetNames.delete(toName); } - yield* changes; - })(), - ); + } - /** - * @param {string} petName - */ - const remove = async petName => { - assertPetName(petName); - const formulaIdentifier = petNames.get(petName); - if (formulaIdentifier === undefined) { - throw new Error( - `Formula does not exist for pet name ${JSON.stringify(petName)}`, - ); - } - if (!validFormulaPattern.test(formulaIdentifier)) { - throw new Error(`Invalid formula identifier ${q(formulaIdentifier)}`); - } + // Change the back-reference for the old pet name. + const formulaPetNames = formulaIdentifiers.get(formulaIdentifier); + if (formulaPetNames !== undefined) { + formulaPetNames.delete(fromName); + formulaPetNames.add(toName); + } - const petNamePath = powers.joinPath(petNameDirectoryPath, petName); - await powers.removePath(petNamePath); - petNames.delete(petName); - const formulaPetNames = formulaIdentifiers.get(petName); - if (formulaPetNames !== undefined) { - formulaPetNames.delete(petName); - } - changesTopic.publisher.next({ remove: petName }); - // TODO consider retaining a backlog of deleted names for recovery - // TODO consider tracking historical pet names for formulas + changesTopic.publisher.next({ add: toName }); + changesTopic.publisher.next({ remove: fromName }); + // TODO consider retaining a backlog of overwritten names for recovery + }; + + /** + * @param {string} formulaIdentifier + */ + const reverseLookup = formulaIdentifier => { + if (!validFormulaPattern.test(formulaIdentifier)) { + throw new Error(`Invalid formula identifier ${q(formulaIdentifier)}`); + } + const formulaPetNames = formulaIdentifiers.get(formulaIdentifier); + if (formulaPetNames === undefined) { + return harden([]); + } + return harden([...formulaPetNames]); + }; + + /** @type {import('./types.js').PetStore} */ + const petStore = { + lookup, + reverseLookup, + list, + follow, + write, + remove, + rename, + }; + + return Far('PetStore', petStore); }; /** - * @param {string} fromName - * @param {string} toName + * @param {string} id */ - const rename = async (fromName, toName) => { - assertPetName(fromName); - assertPetName(toName); - if (fromName === toName) { - return; - } - const formulaIdentifier = petNames.get(fromName); - const overwrittenFormulaIdentifier = petNames.get(toName); - if (formulaIdentifier === undefined) { - throw new Error( - `Formula does not exist for pet name ${JSON.stringify(fromName)}`, - ); - } - if (!validFormulaPattern.test(formulaIdentifier)) { - throw new Error(`Invalid formula identifier ${q(formulaIdentifier)}`); - } - if ( - overwrittenFormulaIdentifier !== undefined && - !validFormulaPattern.test(overwrittenFormulaIdentifier) - ) { - throw new Error( - `Invalid formula identifier ${q(overwrittenFormulaIdentifier)}`, - ); - } - - const fromPath = powers.joinPath(petNameDirectoryPath, fromName); - const toPath = powers.joinPath(petNameDirectoryPath, toName); - await powers.renamePath(fromPath, toPath); - petNames.set(toName, formulaIdentifier); - petNames.delete(fromName); - - // Delete the back-reference for the overwritten pet name if it existed. - if (overwrittenFormulaIdentifier !== undefined) { - const overwrittenFormulaPetNames = formulaIdentifiers.get( - overwrittenFormulaIdentifier, - ); - if (overwrittenFormulaPetNames !== undefined) { - overwrittenFormulaPetNames.delete(toName); - } - } - - // Change the back-reference for the old pet name. - const formulaPetNames = formulaIdentifiers.get(formulaIdentifier); - if (formulaPetNames !== undefined) { - formulaPetNames.delete(fromName); - formulaPetNames.add(toName); + const makeIdentifiedPetStore = id => { + if (!validIdPattern.test(id)) { + throw new Error(`Invalid identifier for pet store ${q(id)}`); } - - changesTopic.publisher.next({ add: toName }); - changesTopic.publisher.next({ remove: fromName }); - // TODO consider retaining a backlog of overwritten names for recovery + const prefix = id.slice(0, 2); + const suffix = id.slice(3); + const petNameDirectoryPath = filePowers.joinPath( + locator.statePath, + 'pet-store-id512', + prefix, + suffix, + ); + return makePetStoreAtPath(petNameDirectoryPath); }; /** - * @param {string} formulaIdentifier + * @param {string} name */ - const reverseLookup = formulaIdentifier => { - if (!validFormulaPattern.test(formulaIdentifier)) { - throw new Error(`Invalid formula identifier ${q(formulaIdentifier)}`); - } - const formulaPetNames = formulaIdentifiers.get(formulaIdentifier); - if (formulaPetNames === undefined) { - return harden([]); - } - return harden([...formulaPetNames]); + const makeOwnPetStore = name => { + const petNameDirectoryPath = filePowers.joinPath(locator.statePath, name); + return makePetStoreAtPath(petNameDirectoryPath); }; - return Far('PetStore', { - lookup, - reverseLookup, - list, - follow, - write, - remove, - rename, - }); -}; - -/** - * @param {import('./types.js').DaemonicPowers} powers - * @param {import('./types.js').Locator} locator - * @param {string} id - */ -export const makeIdentifiedPetStore = (powers, locator, id) => { - if (!validIdPattern.test(id)) { - throw new Error(`Invalid identifier for pet store ${q(id)}`); - } - const prefix = id.slice(0, 2); - const suffix = id.slice(3); - const petNameDirectoryPath = powers.joinPath( - locator.statePath, - 'pet-store-id512', - prefix, - suffix, - ); - return makePetStoreAtPath(powers, petNameDirectoryPath); -}; - -/** - * @param {import('./types.js').DaemonicPowers} powers - * @param {import('./types.js').Locator} locator - * @param {string} name - */ -export const makeOwnPetStore = (powers, locator, name) => { - const petNameDirectoryPath = powers.joinPath(locator.statePath, name); - return makePetStoreAtPath(powers, petNameDirectoryPath); + return { + makeIdentifiedPetStore, + makeOwnPetStore, + }; }; diff --git a/packages/daemon/src/reader-ref.js b/packages/daemon/src/reader-ref.js index cdfb62acfe..2b3ef8dc87 100644 --- a/packages/daemon/src/reader-ref.js +++ b/packages/daemon/src/reader-ref.js @@ -4,6 +4,14 @@ import { encodeBase64 } from '@endo/base64'; import { mapReader } from '@endo/stream'; import { Far } from '@endo/far'; +/** + * Returns the iterator for the given iterable object. + * Supports both synchronous and asynchronous iterables. + * + * @template T The item type of the iterable. + * @param {import('./types').SomehowAsyncIterable} iterable The iterable object. + * @returns {import('@endo/stream').Stream} sort of fudging this into a stream to appease "mapReader" + */ export const asyncIterate = iterable => { let iterator; if (iterable[Symbol.asyncIterator]) { @@ -16,11 +24,16 @@ export const asyncIterate = iterable => { return iterator; }; +/** + * @template T + * @param {import('./types').SomehowAsyncIterable} iterable The iterable object. + * @returns {import('@endo/far').FarRef>} + */ export const makeIteratorRef = iterable => { const iterator = asyncIterate(iterable); return Far('AsyncIterator', { async next() { - return iterator.next(); + return iterator.next(undefined); }, /** * @param {any} value @@ -46,5 +59,9 @@ export const makeIteratorRef = iterable => { }); }; +/** + * @param {import('./types').SomehowAsyncIterable} readable + * @returns {import('@endo/far').FarRef>} + */ export const makeReaderRef = readable => makeIteratorRef(mapReader(asyncIterate(readable), encodeBase64)); diff --git a/packages/daemon/src/types.d.ts b/packages/daemon/src/types.d.ts index ee615cc027..bc67d624d4 100644 --- a/packages/daemon/src/types.d.ts +++ b/packages/daemon/src/types.d.ts @@ -1,6 +1,12 @@ import type { ERef } from '@endo/eventual-send'; +import { FarRef } from '@endo/far'; import type { Reader, Writer, Stream } from '@endo/stream'; +export type SomehowAsyncIterable = + | AsyncIterable + | Iterable + | { next: () => IteratorResult }; + export type Locator = { statePath: string; httpPort?: number; @@ -43,56 +49,6 @@ export type HttpConnect = ( request: HttpRequest, ) => void; -export type DaemonicPowers = { - env: Record; - sinkError: (error) => void; - makeSha512: () => Sha512; - randomHex512: () => Promise; - servePath: (args: { - path: string; - host?: string; - cancelled: Promise; - }) => Promise>; - servePort: (args: { - port: number; - host?: string; - cancelled: Promise; - }) => Promise>; - servePortHttp: (args: { - port: number; - host?: string; - respond?: HttpRespond; - connect?: HttpConnect; - cancelled: Promise; - }) => Promise; - informParentWhenReady: () => void; - reportErrorToParent: (message: string) => void; - makeFileReader: (path: string) => Reader; - makeFileWriter: (path: string) => Writer; - readFileText: (path: string) => Promise; - maybeReadFileText: (path: string) => Promise; - readDirectory: (path: string) => Promise>; - writeFileText: (path: string, text: string) => Promise; - makePath: (path: string) => Promise; - renamePath: (source: string, target: string) => Promise; - removePath: (path: string) => Promise; - joinPath: (...components: Array) => string; - delay: (ms: number, cancelled: Promise) => Promise; - makeWorker: ( - id: string, - path: string, - logPath: string, - pidPath: string, - sockPath: string, - statePath: string, - ephemeralStatePath: string, - cachePath: string, - cancelled: Promise, - ) => Promise; - endoWorkerPath: string; - fileURLToPath: (url: string) => string; -}; - export type MignonicPowers = { pathToFileURL: (path: string) => string; connection: { @@ -185,12 +141,13 @@ export interface Topic< } export interface PetStore { - get(petName: string): string | undefined; - write(petName: string, formulaIdentifier: string): Promise; + lookup(petName: string): string | undefined; + reverseLookup(formulaIdentifier: string): Array; list(): Array; + follow(): Promise>>; + write(petName: string, formulaIdentifier: string): Promise; remove(petName: string); rename(fromPetName: string, toPetName: string); - reverseLookup(formulaIdentifier: string): Array; } export type RequestFn = ( @@ -209,7 +166,7 @@ export type ReceiveFn = ( export interface EndoReadable { sha512(): string; - stream(): ERef>; + stream(): FarRef>; text(): Promise; json(): Promise; [Symbol.asyncIterator]: Reader; @@ -227,7 +184,6 @@ export interface EndoGuest { export interface EndoHost { listMessages(): Promise>; followMessages(): ERef>; - provide(petName: string): Promise; resolve(requestNumber: number, petName: string); reject(requestNumber: number, message: string); lookup(ref: object): Promise>; @@ -267,3 +223,94 @@ export type EndoWebBundle = { bundle: ERef; powers: ERef; }; + +export type CryptoPowers = { + makeSha512: () => Sha512; + randomHex512: () => Promise; +}; + +export type FilePowers = { + makeFileReader: (path: string) => Reader; + makeFileWriter: (path: string) => Writer; + writeFileText: (path: string, text: string) => Promise; + readFileText: (path: string) => Promise; + maybeReadFileText: (path: string) => Promise; + readDirectory: (path: string) => Promise>; + makePath: (path: string) => Promise; + joinPath: (...components: Array) => string; + removePath: (path: string) => Promise; + renamePath: (source: string, target: string) => Promise; +}; + +export type PetStorePowers = { + makeIdentifiedPetStore: (id: string) => Promise>; + makeOwnPetStore: (name: string) => Promise>; +}; + +export type NetworkPowers = { + servePath: (args: { + path: string; + host?: string; + cancelled: Promise; + }) => Promise>; + servePort: (args: { + port: number; + host?: string; + cancelled: Promise; + }) => Promise>; + servePortHttp: (args: { + port: number; + host?: string; + respond?: HttpRespond; + connect?: HttpConnect; + cancelled: Promise; + }) => Promise; + makePrivatePathService: ( + endoBootstrap: FarRef, + sockPath: string, + cancelled: Promise, + exitWithError: (error: Error) => void, + ) => { started: () => Promise; stopped: Promise }; + makePrivateHttpService: ( + endoBootstrap: FarRef, + port: number, + assignWebletPort: (portP: Promise) => void, + cancelled: Promise, + exitWithError: (error: Error) => void, + ) => { started: () => Promise; stopped: Promise }; +}; + +// The return type here is almost an EndoReadable, but not quite. Should fix. +export type AlmostEndoReadable = { + sha512(): string; + stream(): FarRef>; + text(): Promise; + json(): Promise; + [Symbol.asyncIterator]: any; +}; + +export type DaemonicPersistencePowers = { + initializePersistence: () => Promise; + makeContentSha512Store: () => { + store: (readable: AsyncIterable) => Promise; + fetch: (sha512: string) => AlmostEndoReadable; + }; + readFormula: (prefix: string, formulaNumber: string) => Promise; + writeFormula: ( + formula: Formula, + formulaType: string, + formulaId512: string, + ) => Promise; + webPageBundlerFormula?: Formula; +}; + +export type DaemonicControlPowers = { + makeWorker: (id: string, cancelled: Promise) => Promise; +}; + +export type DaemonicPowers = { + crypto: CryptoPowers; + petStore: PetStorePowers; + persistence: DaemonicPersistencePowers; + control: DaemonicControlPowers; +}; From 10c82b368913c96a0bc17ba0a12031ad958098e7 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Thu, 12 Oct 2023 14:58:28 -0700 Subject: [PATCH 130/234] feat(stream): Relax makeStream queue argument types --- packages/stream/index.js | 4 ++-- packages/stream/types.d.ts | 13 ++++++++++--- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/packages/stream/index.js b/packages/stream/index.js index 4ab2588338..1a2ce9188c 100644 --- a/packages/stream/index.js +++ b/packages/stream/index.js @@ -54,8 +54,8 @@ harden(makeQueue); * @template TWrite * @template TReadReturn * @template TWriteReturn - * @param {import('./types.js').AsyncQueue>} acks - * @param {import('./types.js').AsyncQueue>} data + * @param {import('./types.js').AsyncSpring>} acks + * @param {import('./types.js').AsyncSink>} data */ export const makeStream = (acks, data) => { const stream = harden({ diff --git a/packages/stream/types.d.ts b/packages/stream/types.d.ts index ecdecb9118..c5a95930de 100644 --- a/packages/stream/types.d.ts +++ b/packages/stream/types.d.ts @@ -1,8 +1,15 @@ -export interface AsyncQueue { +export interface AsyncSink { put(value: TValue | Promise): void; +} + +export interface AsyncSpring { get(): Promise; } +export interface AsyncQueue + extends AsyncSpring, + AsyncSink {} + // Stream is nearly identical to AsyncGenerator and AsyncGenerator should // probably be identical to this definition of Stream. // Stream does not make the mistake of conflating the read and write return @@ -40,8 +47,8 @@ export declare function makeStream< TReadReturn = undefined, TWriteReturn = undefined, >( - acks: AsyncQueue>, - data: AsyncQueue>, + acks: AsyncSpring>, + data: AsyncSink>, ): Stream; export declare function makePipe< From 163bd884f357ff796d785d2c2328b46ffc114be8 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Thu, 12 Oct 2023 15:04:45 -0700 Subject: [PATCH 131/234] types(daemon): ts-check all the sources --- packages/daemon/src/pet-name.js | 2 ++ packages/daemon/src/pubsub.js | 2 ++ packages/daemon/src/serve-private-path.js | 2 ++ packages/daemon/src/serve-private-port-http.js | 2 ++ packages/daemon/src/web-page-bundler.js | 2 ++ packages/daemon/src/web-page.js | 1 + packages/daemon/src/worker-node.js | 1 + 7 files changed, 12 insertions(+) diff --git a/packages/daemon/src/pet-name.js b/packages/daemon/src/pet-name.js index 1502133a99..337a836135 100644 --- a/packages/daemon/src/pet-name.js +++ b/packages/daemon/src/pet-name.js @@ -1,3 +1,5 @@ +// @ts-check + const { quote: q } = assert; const validNamePattern = /^[a-z][a-z0-9-]{0,127}$/; diff --git a/packages/daemon/src/pubsub.js b/packages/daemon/src/pubsub.js index 407814cf17..9be31ae94d 100644 --- a/packages/daemon/src/pubsub.js +++ b/packages/daemon/src/pubsub.js @@ -1,3 +1,5 @@ +// @ts-check + import { makePromiseKit } from '@endo/promise-kit'; import { makeStream } from '@endo/stream'; diff --git a/packages/daemon/src/serve-private-path.js b/packages/daemon/src/serve-private-path.js index 8a17e4b077..fb6629c397 100644 --- a/packages/daemon/src/serve-private-path.js +++ b/packages/daemon/src/serve-private-path.js @@ -1,3 +1,5 @@ +// @ts-check + import { makeNetstringCapTP } from './connection.js'; const { quote: q } = assert; diff --git a/packages/daemon/src/serve-private-port-http.js b/packages/daemon/src/serve-private-port-http.js index de176d82c3..9344196c6a 100644 --- a/packages/daemon/src/serve-private-port-http.js +++ b/packages/daemon/src/serve-private-port-http.js @@ -1,3 +1,5 @@ +// @ts-check + import { E } from '@endo/far'; import { mapReader, mapWriter } from '@endo/stream'; import { diff --git a/packages/daemon/src/web-page-bundler.js b/packages/daemon/src/web-page-bundler.js index e8ffda7407..3431106e3a 100644 --- a/packages/daemon/src/web-page-bundler.js +++ b/packages/daemon/src/web-page-bundler.js @@ -1,3 +1,5 @@ +// @ts-check + // This is a built-in unsafe plugin for lazily constructing the web-page.js // bundle for booting up web caplets. // The hard-coded 'web-page-js' formula is a hard-coded 'import-unsafe' formula diff --git a/packages/daemon/src/web-page.js b/packages/daemon/src/web-page.js index 796fcf075d..bfc7ee47bc 100644 --- a/packages/daemon/src/web-page.js +++ b/packages/daemon/src/web-page.js @@ -1,3 +1,4 @@ +// @ts-check /* global window, document */ import '@endo/init/debug.js'; diff --git a/packages/daemon/src/worker-node.js b/packages/daemon/src/worker-node.js index f5e8221610..9360ec8567 100644 --- a/packages/daemon/src/worker-node.js +++ b/packages/daemon/src/worker-node.js @@ -1,3 +1,4 @@ +// @ts-check /* global process */ // Establish a perimeter: From 89f6554a8d720f7aea77980fef8cda0604723950 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Wed, 11 Oct 2023 17:53:26 -0700 Subject: [PATCH 132/234] fix(daemon): Clarify types of server daemon powers --- packages/daemon/src/daemon-node-powers.js | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/packages/daemon/src/daemon-node-powers.js b/packages/daemon/src/daemon-node-powers.js index f78b0f9046..260694f905 100644 --- a/packages/daemon/src/daemon-node-powers.js +++ b/packages/daemon/src/daemon-node-powers.js @@ -223,6 +223,12 @@ export const makeNetworkPowers = ({ http, ws, net }) => { return readFrom; }; + /** + * @param {object} args + * @param {number} args.port + * @param {string} [args.host] + * @param {Promise} args.cancelled + */ const servePort = async ({ port, host = '0.0.0.0', cancelled }) => serveListener( server => @@ -232,6 +238,11 @@ export const makeNetworkPowers = ({ http, ws, net }) => { cancelled, ); + /** + * @param {object} args + * @param {string} args.path + * @param {Promise} args.cancelled + */ const servePath = async ({ path, cancelled }) => serveListener( server => From 6d0bb32a735a21213e75a383c92bdf08bd04488a Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Thu, 12 Oct 2023 15:16:43 -0700 Subject: [PATCH 133/234] fix(daemon): Realign pet store and host types --- packages/daemon/src/types.d.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/daemon/src/types.d.ts b/packages/daemon/src/types.d.ts index bc67d624d4..bdc337e19f 100644 --- a/packages/daemon/src/types.d.ts +++ b/packages/daemon/src/types.d.ts @@ -141,13 +141,13 @@ export interface Topic< } export interface PetStore { - lookup(petName: string): string | undefined; - reverseLookup(formulaIdentifier: string): Array; list(): Array; - follow(): Promise>>; write(petName: string, formulaIdentifier: string): Promise; remove(petName: string); rename(fromPetName: string, toPetName: string); + lookup(petName: string): string | undefined; + reverseLookup(formulaIdentifier: string): Array; + follow(): Promise>>; } export type RequestFn = ( @@ -184,9 +184,10 @@ export interface EndoGuest { export interface EndoHost { listMessages(): Promise>; followMessages(): ERef>; + lookup(petName: string): Promise; resolve(requestNumber: number, petName: string); reject(requestNumber: number, message: string); - lookup(ref: object): Promise>; + reverseLookup(ref: object): Promise>; remove(petName: string); rename(fromPetName: string, toPetName: string); list(): Array; // pet names From 29a9f5c078910dc5b4c5cad2546049cf9e48a008 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Thu, 12 Oct 2023 15:17:47 -0700 Subject: [PATCH 134/234] fix(daemon): Relocate type reference for AsyncQueue --- packages/daemon/src/pubsub.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/daemon/src/pubsub.js b/packages/daemon/src/pubsub.js index 9be31ae94d..1a03ba5853 100644 --- a/packages/daemon/src/pubsub.js +++ b/packages/daemon/src/pubsub.js @@ -11,7 +11,7 @@ const freeze = /** @type {(v: T | Readonly) => T} */ (Object.freeze); /** * @template TValue TValue * @param {TValue} value - * @returns {import('./types.js').AsyncQueue} + * @returns {import('@endo/stream').AsyncQueue} */ export const makeNullQueue = value => harden({ From a470ce6a0c3dd1c6c4484389a5229edf9820c08f Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Thu, 12 Oct 2023 15:18:28 -0700 Subject: [PATCH 135/234] fix(daemon): Narrow type of window location for web page bootstrap --- packages/daemon/src/web-page.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/daemon/src/web-page.js b/packages/daemon/src/web-page.js index bfc7ee47bc..02b80c75aa 100644 --- a/packages/daemon/src/web-page.js +++ b/packages/daemon/src/web-page.js @@ -22,7 +22,7 @@ const endowments = Object.freeze({ console, }); -const url = new URL('/', window.location); +const url = new URL('/', `${window.location}`); url.protocol = 'ws'; const bootstrap = Far('WebFacet', { From 83b3be447363a032e3f3d2e003d8b07dac5d876e Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Thu, 12 Oct 2023 15:20:15 -0700 Subject: [PATCH 136/234] fix(daemon): Fix locator type reference --- packages/daemon/src/worker-node.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/daemon/src/worker-node.js b/packages/daemon/src/worker-node.js index 9360ec8567..168afbff5a 100644 --- a/packages/daemon/src/worker-node.js +++ b/packages/daemon/src/worker-node.js @@ -25,7 +25,7 @@ if (process.argv.length < 7) { const [workerUuid, sockPath, statePath, ephemeralStatePath, cachePath] = process.argv.slice(2); -/** @type {import('../index.js').Locator} */ +/** @type {import('./types.js').Locator} */ const locator = { sockPath, statePath, From 2f8aa51c97356327ca101af58a8c7aa8e74099cf Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Thu, 12 Oct 2023 15:20:48 -0700 Subject: [PATCH 137/234] fix(daemon): Loosen type for panic message --- packages/daemon/src/mail.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/daemon/src/mail.js b/packages/daemon/src/mail.js index e02adf1077..9e8bda7858 100644 --- a/packages/daemon/src/mail.js +++ b/packages/daemon/src/mail.js @@ -103,7 +103,9 @@ export const makeMailboxMaker = ({ } return undefined; } - throw Error(`panic: Unknown message type ${type}`); + throw new Error( + `panic: Unknown message type ${/** @type {any} */ (message).type}`, + ); }; const listMessages = async () => From 3144c116c2ed35996b57f6fe246ddb8a35150974 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Thu, 5 Oct 2023 13:20:22 -0700 Subject: [PATCH 138/234] fix(daemon): Stale comment re provide vs lookup --- packages/daemon/src/daemon.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/daemon/src/daemon.js b/packages/daemon/src/daemon.js index 5cc5d45b4f..eb7f7ef5b8 100644 --- a/packages/daemon/src/daemon.js +++ b/packages/daemon/src/daemon.js @@ -407,7 +407,7 @@ const makeEndoBootstrap = ( formula, ); - // Memoize provide. + // Memoize for lookup. valuePromiseForFormulaIdentifier.set(formulaIdentifier, promiseForValue); // Prepare an entry for reverse-lookup of formula for presence. From a45f693579da3dbe65a0e1a7bc4d03f71b6e8ebf Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Thu, 5 Oct 2023 13:30:14 -0700 Subject: [PATCH 139/234] fix(daemon): Private port started promise resolved early --- packages/daemon/src/daemon-node-powers.js | 4 ++-- packages/daemon/src/serve-private-path.js | 8 ++++---- packages/daemon/src/serve-private-port-http.js | 2 +- packages/daemon/src/types.d.ts | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/daemon/src/daemon-node-powers.js b/packages/daemon/src/daemon-node-powers.js index 260694f905..c35ba630b6 100644 --- a/packages/daemon/src/daemon-node-powers.js +++ b/packages/daemon/src/daemon-node-powers.js @@ -265,7 +265,7 @@ export const makeNetworkPowers = ({ http, ws, net }) => { * @param {string} sockPath * @param {Promise} cancelled * @param {(error: Error) => void} exitWithError - * @returns {{ started: () => Promise, stopped: Promise }} + * @returns {{ started: Promise, stopped: Promise }} */ const makePrivatePathService = ( endoBootstrap, @@ -288,7 +288,7 @@ export const makeNetworkPowers = ({ http, ws, net }) => { * @param {(port: Promise) => void} assignWebletPort * @param {Promise} cancelled * @param {(error: Error) => void} exitWithError - * @returns {{ started: () => Promise, stopped: Promise }} + * @returns {{ started: Promise, stopped: Promise }} */ const makePrivateHttpService = ( endoBootstrap, diff --git a/packages/daemon/src/serve-private-path.js b/packages/daemon/src/serve-private-path.js index fb6629c397..4e095126a9 100644 --- a/packages/daemon/src/serve-private-path.js +++ b/packages/daemon/src/serve-private-path.js @@ -11,15 +11,15 @@ export const servePrivatePath = ( ) => { const connectionsP = servePath({ path: sockPath, cancelled }); - const started = async () => { + const started = (async () => { await connectionsP; // Resolve a promise in the Endo CLI through the IPC channel: console.log( - `Endo daemon listening for CapTP on ${q( + `Endo daemon listening for private CapTP on ${q( sockPath, )} ${new Date().toISOString()}`, ); - }; + })(); const stopped = (async () => { /** @type {Set>} */ @@ -33,7 +33,7 @@ export const servePrivatePath = ( closed: connectionClosed, } of connections) { (async () => { - const connectionNumber = connectionNumbers.next(); + const { value: connectionNumber } = connectionNumbers.next(); console.log( `Endo daemon received domain connection ${connectionNumber} at ${new Date().toISOString()}`, ); diff --git a/packages/daemon/src/serve-private-port-http.js b/packages/daemon/src/serve-private-port-http.js index 9344196c6a..80131d8f42 100644 --- a/packages/daemon/src/serve-private-port-http.js +++ b/packages/daemon/src/serve-private-port-http.js @@ -65,7 +65,7 @@ export const servePrivatePortHttp = ( closed: connectionClosed, } = connection; - const connectionNumber = connectionNumbers.next(); + const { value: connectionNumber } = connectionNumbers.next(); console.log( `Endo daemon received local web socket connection ${connectionNumber} at ${new Date().toISOString()}`, ); diff --git a/packages/daemon/src/types.d.ts b/packages/daemon/src/types.d.ts index bdc337e19f..ade4ee1b7b 100644 --- a/packages/daemon/src/types.d.ts +++ b/packages/daemon/src/types.d.ts @@ -271,14 +271,14 @@ export type NetworkPowers = { sockPath: string, cancelled: Promise, exitWithError: (error: Error) => void, - ) => { started: () => Promise; stopped: Promise }; + ) => { started: Promise; stopped: Promise }; makePrivateHttpService: ( endoBootstrap: FarRef, port: number, assignWebletPort: (portP: Promise) => void, cancelled: Promise, exitWithError: (error: Error) => void, - ) => { started: () => Promise; stopped: Promise }; + ) => { started: Promise; stopped: Promise }; }; // The return type here is almost an EndoReadable, but not quite. Should fix. From 4aea6a3b3647e9f7a43309f2b7942fd6ac2a5c94 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Thu, 5 Oct 2023 11:56:39 -0700 Subject: [PATCH 140/234] fix(daemon): Evidently there is a limit on the length of a domain socket path --- packages/daemon/src/daemon-node-powers.js | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/packages/daemon/src/daemon-node-powers.js b/packages/daemon/src/daemon-node-powers.js index c35ba630b6..7182ba97e4 100644 --- a/packages/daemon/src/daemon-node-powers.js +++ b/packages/daemon/src/daemon-node-powers.js @@ -244,13 +244,22 @@ export const makeNetworkPowers = ({ http, ws, net }) => { * @param {Promise} args.cancelled */ const servePath = async ({ path, cancelled }) => - serveListener( - server => - new Promise(resolve => - server.listen({ path }, () => resolve(undefined)), - ), - cancelled, - ); + serveListener(server => { + return new Promise((resolve, reject) => + server.listen({ path }, error => { + if (error) { + if (path.length >= 104) { + console.warn( + `Warning: Length of path for domain socket or named path exceeeds common maximum (104, possibly 108) for some platforms (length: ${path.length}, path: ${path})`, + ); + } + reject(error); + } else { + resolve(undefined); + } + }), + ); + }, cancelled); const connectionNumbers = (function* generateNumbers() { let n = 0; From 95a6e1e8ed987a560abe8f476f6a0df0624f0bce Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Fri, 20 Oct 2023 15:28:14 -0700 Subject: [PATCH 141/234] fix(daemon): Fix bug in send test --- packages/daemon/test/test-endo.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/daemon/test/test-endo.js b/packages/daemon/test/test-endo.js index 64e78bead6..d0c78f5295 100644 --- a/packages/daemon/test/test-endo.js +++ b/packages/daemon/test/test-endo.js @@ -417,7 +417,7 @@ test('guest facet receives a message for host', async t => { const { value: message0 } = await E(iteratorRef).next(); t.is(message0.number, 0); await E(host).resolve(message0.number, 'ten1'); - await E(guest).send('HOST', 'Hello, World!', ['gift'], ['number']); + await E(guest).send('HOST', ['Hello, World!'], ['gift'], ['number']); const { value: message1 } = await E(iteratorRef).next(); t.is(message1.number, 1); await E(host).adopt(message1.number, 'gift', 'ten2'); From a0a9168d66e5263d63e94f267ca834cacf9d2beb Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Fri, 20 Oct 2023 15:27:40 -0700 Subject: [PATCH 142/234] fix(daemon): Finish consolidating special names in host --- packages/daemon/src/host.js | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/packages/daemon/src/host.js b/packages/daemon/src/host.js index 52c168629e..e92cb937ba 100644 --- a/packages/daemon/src/host.js +++ b/packages/daemon/src/host.js @@ -54,6 +54,8 @@ export const makeHostMaker = ({ selfFormulaIdentifier: hostFormulaIdentifier, specialNames: { SELF: hostFormulaIdentifier, + NONE: 'least-authority', + ENDO: 'endo', }, }); @@ -159,14 +161,6 @@ export const makeHostMaker = ({ * @param {string | 'NONE' | 'HOST' | 'ENDO'} partyName */ const providePowersFormulaIdentifier = async partyName => { - if (partyName === 'NONE') { - return 'least-authority'; - } else if (partyName === 'HOST') { - return 'host'; - } else if (partyName === 'ENDO') { - return 'endo'; - } - assertPetName(partyName); let guestFormulaIdentifier = lookupFormulaIdentifierForName(partyName); if (guestFormulaIdentifier === undefined) { const guest = await provideGuest(partyName); From 4453f2f1ad1eb02975e1e0df1c6a8429aceaba21 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Mon, 16 Oct 2023 14:48:15 -0700 Subject: [PATCH 143/234] refactor(daemon): Parameterize pet name validator --- packages/daemon/src/daemon.js | 8 ++++++-- packages/daemon/src/pet-store.js | 26 ++++++++++++++------------ packages/daemon/src/types.d.ts | 12 ++++++++++-- 3 files changed, 30 insertions(+), 16 deletions(-) diff --git a/packages/daemon/src/daemon.js b/packages/daemon/src/daemon.js index eb7f7ef5b8..bc39ebaf80 100644 --- a/packages/daemon/src/daemon.js +++ b/packages/daemon/src/daemon.js @@ -10,6 +10,7 @@ import { makeRefReader } from './ref-reader.js'; import { makeMailboxMaker } from './mail.js'; import { makeGuestMaker } from './guest.js'; import { makeHostMaker } from './host.js'; +import { assertPetName } from './pet-name.js'; const { quote: q } = assert; @@ -319,7 +320,7 @@ const makeEndoBootstrap = ( const delimiterIndex = formulaIdentifier.indexOf(':'); if (delimiterIndex < 0) { if (formulaIdentifier === 'pet-store') { - return petStorePowers.makeOwnPetStore('pet-store'); + return petStorePowers.makeOwnPetStore('pet-store', assertPetName); } else if (formulaIdentifier === 'host') { // Behold, recursion: // eslint-disable-next-line no-use-before-define @@ -355,7 +356,10 @@ const makeEndoBootstrap = ( } else if (prefix === 'worker-id512') { return makeIdentifiedWorker(formulaNumber); } else if (prefix === 'pet-store-id512') { - return petStorePowers.makeIdentifiedPetStore(formulaNumber); + return petStorePowers.makeIdentifiedPetStore( + formulaNumber, + assertPetName, + ); } else if (prefix === 'host-id512') { // Behold, recursion: // eslint-disable-next-line no-use-before-define diff --git a/packages/daemon/src/pet-store.js b/packages/daemon/src/pet-store.js index 7e97388a82..bb4a6736e9 100644 --- a/packages/daemon/src/pet-store.js +++ b/packages/daemon/src/pet-store.js @@ -1,7 +1,6 @@ // @ts-check import { Far } from '@endo/far'; -import { assertPetName } from './pet-name.js'; import { makeChangeTopic } from './pubsub.js'; import { makeIteratorRef } from './reader-ref.js'; @@ -18,9 +17,10 @@ const validFormulaPattern = export const makePetStoreMaker = (filePowers, locator) => { /** * @param {string} petNameDirectoryPath + * @param {(name: string) => void} assertValidName * @returns {Promise>} */ - const makePetStoreAtPath = async petNameDirectoryPath => { + const makePetStoreAtPath = async (petNameDirectoryPath, assertValidName) => { /** @type {Map} */ const petNames = new Map(); /** @type {Map>} */ @@ -48,7 +48,7 @@ export const makePetStoreMaker = (filePowers, locator) => { const fileNames = await filePowers.readDirectory(petNameDirectoryPath); await Promise.all( fileNames.map(async petName => { - assertPetName(petName); + assertValidName(petName); const formulaIdentifier = await read(petName); petNames.set(petName, formulaIdentifier); const formulaPetNames = formulaIdentifiers.get(formulaIdentifier); @@ -62,7 +62,7 @@ export const makePetStoreMaker = (filePowers, locator) => { /** @param {string} petName */ const lookup = petName => { - assertPetName(petName); + assertValidName(petName); return petNames.get(petName); }; @@ -71,7 +71,7 @@ export const makePetStoreMaker = (filePowers, locator) => { * @param {string} formulaIdentifier */ const write = async (petName, formulaIdentifier) => { - assertPetName(petName); + assertValidName(petName); if (!validFormulaPattern.test(formulaIdentifier)) { throw new Error(`Invalid formula identifier ${q(formulaIdentifier)}`); } @@ -108,7 +108,7 @@ export const makePetStoreMaker = (filePowers, locator) => { * @param {string} petName */ const remove = async petName => { - assertPetName(petName); + assertValidName(petName); const formulaIdentifier = petNames.get(petName); if (formulaIdentifier === undefined) { throw new Error( @@ -136,8 +136,8 @@ export const makePetStoreMaker = (filePowers, locator) => { * @param {string} toName */ const rename = async (fromName, toName) => { - assertPetName(fromName); - assertPetName(toName); + assertValidName(fromName); + assertValidName(toName); if (fromName === toName) { return; } @@ -218,8 +218,9 @@ export const makePetStoreMaker = (filePowers, locator) => { /** * @param {string} id + * @param {(name: string) => void} assertValidName */ - const makeIdentifiedPetStore = id => { + const makeIdentifiedPetStore = (id, assertValidName) => { if (!validIdPattern.test(id)) { throw new Error(`Invalid identifier for pet store ${q(id)}`); } @@ -231,15 +232,16 @@ export const makePetStoreMaker = (filePowers, locator) => { prefix, suffix, ); - return makePetStoreAtPath(petNameDirectoryPath); + return makePetStoreAtPath(petNameDirectoryPath, assertValidName); }; /** * @param {string} name + * @param {(name: string) => void} assertValidName */ - const makeOwnPetStore = name => { + const makeOwnPetStore = (name, assertValidName) => { const petNameDirectoryPath = filePowers.joinPath(locator.statePath, name); - return makePetStoreAtPath(petNameDirectoryPath); + return makePetStoreAtPath(petNameDirectoryPath, assertValidName); }; return { diff --git a/packages/daemon/src/types.d.ts b/packages/daemon/src/types.d.ts index ade4ee1b7b..a310cd9b00 100644 --- a/packages/daemon/src/types.d.ts +++ b/packages/daemon/src/types.d.ts @@ -243,9 +243,17 @@ export type FilePowers = { renamePath: (source: string, target: string) => Promise; }; +export type AssertValidNameFn = (name: string) => void; + export type PetStorePowers = { - makeIdentifiedPetStore: (id: string) => Promise>; - makeOwnPetStore: (name: string) => Promise>; + makeIdentifiedPetStore: ( + id: string, + assertValidName: AssertValidNameFn, + ) => Promise>; + makeOwnPetStore: ( + name: string, + assertValidName: AssertValidNameFn, + ) => Promise>; }; export type NetworkPowers = { From fd253d19b1cb0f1e81c1ef23fa8593db281ecb78 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Wed, 11 Oct 2023 16:29:43 -0700 Subject: [PATCH 144/234] refactor(daemon): Envelop living values in a controller object --- packages/daemon/src/daemon.js | 96 +++++++++++++++++++++------------- packages/daemon/src/guest.js | 6 +-- packages/daemon/src/host.js | 2 +- packages/daemon/src/mail.js | 9 ++-- packages/daemon/src/types.d.ts | 8 +++ 5 files changed, 78 insertions(+), 43 deletions(-) diff --git a/packages/daemon/src/daemon.js b/packages/daemon/src/daemon.js index bc39ebaf80..123e73898d 100644 --- a/packages/daemon/src/daemon.js +++ b/packages/daemon/src/daemon.js @@ -59,8 +59,8 @@ const makeEndoBootstrap = ( const { randomHex512, makeSha512 } = cryptoPowers; const contentStore = persistencePowers.makeContentSha512Store(); - /** @type {Map} */ - const valuePromiseForFormulaIdentifier = new Map(); + /** @type {Map>} */ + const controllerForFormulaIdentifier = new Map(); // Reverse look-up, for answering "what is my name for this near or far // reference", and not for "what is my name for this promise". /** @type {WeakMap} */ @@ -74,13 +74,14 @@ const makeEndoBootstrap = ( */ const makeSha512ReadableBlob = sha512 => { const { text, json, stream } = contentStore.fetch(sha512); - return Far(`Readable file with SHA-512 ${sha512.slice(0, 8)}...`, { + const promise = Far(`Readable file with SHA-512 ${sha512.slice(0, 8)}...`, { sha512: () => sha512, stream, text, json, [Symbol.asyncIterator]: stream, }); + return { promise }; }; /** @@ -167,7 +168,7 @@ const makeEndoBootstrap = ( workerBootstraps.set(worker, workerBootstrap); - return worker; + return { promise: worker }; }; /** @@ -199,7 +200,12 @@ const makeEndoBootstrap = ( provideValueForFormulaIdentifier(formulaIdentifier), ), ); - return E(workerBootstrap).evaluate(source, codeNames, endowmentValues); + const promise = E(workerBootstrap).evaluate( + source, + codeNames, + endowmentValues, + ); + return { promise }; }; /** @@ -224,7 +230,8 @@ const makeEndoBootstrap = ( // eslint-disable-next-line no-use-before-define provideValueForFormulaIdentifier(guestFormulaIdentifier) ); - return E(workerBootstrap).importUnsafeAndEndow(importPath, guestP); + const promise = E(workerBootstrap).importUnsafeAndEndow(importPath, guestP); + return { promise }; }; /** @@ -257,7 +264,11 @@ const makeEndoBootstrap = ( // eslint-disable-next-line no-use-before-define provideValueForFormulaIdentifier(guestFormulaIdentifier) ); - return E(workerBootstrap).importBundleAndEndow(readableBundleP, guestP); + const promise = E(workerBootstrap).importBundleAndEndow( + readableBundleP, + guestP, + ); + return { promise }; }; /** @@ -265,11 +276,7 @@ const makeEndoBootstrap = ( * @param {string} formulaNumber * @param {import('./types.js').Formula} formula */ - const makeValueForFormula = async ( - formulaIdentifier, - formulaNumber, - formula, - ) => { + const makeValueForFormula = (formulaIdentifier, formulaNumber, formula) => { if (formula.type === 'eval') { return makeValueForEval( formula.worker, @@ -299,15 +306,18 @@ const makeEndoBootstrap = ( `worker-id512:${formulaNumber}`, ); } else if (formula.type === 'web-bundle') { - return harden({ - url: `http://${formulaNumber}.endo.localhost:${await webletPortP}`, - // Behold, recursion: - // eslint-disable-next-line no-use-before-define - bundle: provideValueForFormulaIdentifier(formula.bundle), - // Behold, recursion: - // eslint-disable-next-line no-use-before-define - powers: provideValueForFormulaIdentifier(formula.powers), - }); + return { + promise: (async () => + harden({ + url: `http://${formulaNumber}.endo.localhost:${await webletPortP}`, + // Behold, recursion: + // eslint-disable-next-line no-use-before-define + bundle: provideValueForFormulaIdentifier(formula.bundle), + // Behold, recursion: + // eslint-disable-next-line no-use-before-define + powers: provideValueForFormulaIdentifier(formula.powers), + }))(), + }; } else { throw new TypeError(`Invalid formula: ${q(formula)}`); } @@ -320,7 +330,8 @@ const makeEndoBootstrap = ( const delimiterIndex = formulaIdentifier.indexOf(':'); if (delimiterIndex < 0) { if (formulaIdentifier === 'pet-store') { - return petStorePowers.makeOwnPetStore('pet-store', assertPetName); + const promise = petStorePowers.makeOwnPetStore('pet-store', assertPetName); + return { promise }; } else if (formulaIdentifier === 'host') { // Behold, recursion: // eslint-disable-next-line no-use-before-define @@ -332,9 +343,9 @@ const makeEndoBootstrap = ( } else if (formulaIdentifier === 'endo') { // Behold, self-referentiality: // eslint-disable-next-line no-use-before-define - return endoBootstrap; + return { promise: endoBootstrap }; } else if (formulaIdentifier === 'least-authority') { - return leastAuthority; + return { promise: leastAuthority }; } else if (formulaIdentifier === 'web-page-js') { if (persistencePowers.webPageBundlerFormula === undefined) { throw Error('No web-page-js formula provided.'); @@ -356,10 +367,11 @@ const makeEndoBootstrap = ( } else if (prefix === 'worker-id512') { return makeIdentifiedWorker(formulaNumber); } else if (prefix === 'pet-store-id512') { - return petStorePowers.makeIdentifiedPetStore( + const promise = petStorePowers.makeIdentifiedPetStore( formulaNumber, assertPetName, ); + return { promise }; } else if (prefix === 'host-id512') { // Behold, recursion: // eslint-disable-next-line no-use-before-define @@ -392,7 +404,7 @@ const makeEndoBootstrap = ( // The two functions provideValueForFormula and provideValueForFormulaIdentifier // share a responsibility for maintaining the memoization tables - // valuePromiseForFormulaIdentifier and formulaIdentifierForRef, since the + // controllerForFormulaIdentifier and formulaIdentifierForRef, since the // former bypasses the latter in order to avoid a round trip with disk. const provideValueForNumberedFormula = async ( @@ -405,17 +417,17 @@ const makeEndoBootstrap = ( await persistencePowers.writeFormula(formula, formulaType, formulaNumber); // Behold, recursion: // eslint-disable-next-line no-use-before-define - const promiseForValue = makeValueForFormula( + const controller = await makeValueForFormula( formulaIdentifier, formulaNumber, formula, ); // Memoize for lookup. - valuePromiseForFormulaIdentifier.set(formulaIdentifier, promiseForValue); + controllerForFormulaIdentifier.set(formulaIdentifier, controller); // Prepare an entry for reverse-lookup of formula for presence. - const value = await promiseForValue; + const value = await controller.promise; if (typeof value === 'object' && value !== null) { formulaIdentifierForRef.set(value, formulaIdentifier); } @@ -435,14 +447,26 @@ const makeEndoBootstrap = ( /** * @param {string} formulaIdentifier */ - const provideValueForFormulaIdentifier = async formulaIdentifier => { - let promiseForValue = - valuePromiseForFormulaIdentifier.get(formulaIdentifier); - if (promiseForValue === undefined) { - promiseForValue = makeValueForFormulaIdentifier(formulaIdentifier); - valuePromiseForFormulaIdentifier.set(formulaIdentifier, promiseForValue); + const provideControllerForFormulaIdentifier = async formulaIdentifier => { + let controller = controllerForFormulaIdentifier.get(formulaIdentifier); + if (controller === undefined) { + controller = await makeValueForFormulaIdentifier(formulaIdentifier); + if (controller === undefined) { + throw new Error('panic: not sure why the type is not narrower here'); + } + controllerForFormulaIdentifier.set(formulaIdentifier, controller); } - const value = await promiseForValue; + return controller; + }; + + /** + * @param {string} formulaIdentifier + */ + const provideValueForFormulaIdentifier = async formulaIdentifier => { + const controller = /** @type {import('./types.js').Controller<>} */ ( + await provideControllerForFormulaIdentifier(formulaIdentifier) + ); + const value = await controller.promise; if (typeof value === 'object' && value !== null) { formulaIdentifierForRef.set(value, formulaIdentifier); } diff --git a/packages/daemon/src/guest.js b/packages/daemon/src/guest.js index a09dfb8619..79a1db1897 100644 --- a/packages/daemon/src/guest.js +++ b/packages/daemon/src/guest.js @@ -23,8 +23,8 @@ export const makeGuestMaker = ({ const petStore = /** @type {import('./types.js').PetStore} */ ( await provideValueForFormulaIdentifier(petStoreFormulaIdentifier) ); - const host = /** @type {object} */ ( - await provideValueForFormulaIdentifier(hostFormulaIdentifier) + const hostController = /** @type {import('./types.js').Controller<>} */ ( + await provideControllerForFormulaIdentifier(hostFormulaIdentifier) ); const deliverToHost = partyRequestFunctions.get(host); @@ -81,7 +81,7 @@ export const makeGuestMaker = ({ partyReceiveFunctions.set(guest, receive); partyRequestFunctions.set(guest, respond); - return guest; + return { promise: guest }; }; return makeIdentifiedGuest; diff --git a/packages/daemon/src/host.js b/packages/daemon/src/host.js index e92cb937ba..f470bb85b5 100644 --- a/packages/daemon/src/host.js +++ b/packages/daemon/src/host.js @@ -444,7 +444,7 @@ export const makeHostMaker = ({ partyReceiveFunctions.set(host, receive); partyRequestFunctions.set(host, respond); - return host; + return { promise: host }; }; return makeIdentifiedHost; diff --git a/packages/daemon/src/mail.js b/packages/daemon/src/mail.js index 9e8bda7858..e95acdad4d 100644 --- a/packages/daemon/src/mail.js +++ b/packages/daemon/src/mail.js @@ -377,9 +377,12 @@ export const makeMailboxMaker = ({ if (recipientFormulaIdentifier === undefined) { throw new Error(`Unknown pet name for party: ${recipientName}`); } - const recipient = /** @type {object} */ ( - await provideValueForFormulaIdentifier(recipientFormulaIdentifier) - ); + const recipientController = + /** @type {import('./types.js').Controller<>} */ ( + await provideControllerForFormulaIdentifier( + recipientFormulaIdentifier, + ) + ); const deliverToRecipient = partyRequestFunctions.get(recipient); if (deliverToRecipient === undefined) { diff --git a/packages/daemon/src/types.d.ts b/packages/daemon/src/types.d.ts index a310cd9b00..1116a925ec 100644 --- a/packages/daemon/src/types.d.ts +++ b/packages/daemon/src/types.d.ts @@ -140,6 +140,14 @@ export interface Topic< subscribe(): Stream; } +export interface Controller { + promise: Promise; + internal: Internal; + terminate?: () => {}; + terminating?: Promise; + terminated?: Promise; +} + export interface PetStore { list(): Array; write(petName: string, formulaIdentifier: string): Promise; From 89f3bf80162fe40664d60fc728bd6f510a26c94b Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Wed, 11 Oct 2023 22:41:28 -0700 Subject: [PATCH 145/234] refactor(daemon): Move worker termination to controller --- packages/daemon/src/daemon.js | 16 ++++++++++------ packages/daemon/src/host.js | 2 ++ packages/daemon/src/mail.js | 19 +++++++++++++++++++ packages/daemon/test/test-endo.js | 6 ++---- 4 files changed, 33 insertions(+), 10 deletions(-) diff --git a/packages/daemon/src/daemon.js b/packages/daemon/src/daemon.js index 123e73898d..426cdff8ae 100644 --- a/packages/daemon/src/daemon.js +++ b/packages/daemon/src/daemon.js @@ -108,6 +108,10 @@ const makeEndoBootstrap = ( // TODO validate workerId512 const workerFormulaIdentifier = `worker-id512:${workerId512}`; + const { resolve: notifyTerminating, promise: terminating } = + /** @type {import('@endo/promise-kit').PromiseKit} */ ( + makePromiseKit() + ); const { reject: cancelWorker, promise: workerCancelled } = /** @type {import('@endo/promise-kit').PromiseKit} */ ( makePromiseKit() @@ -133,7 +137,7 @@ const makeEndoBootstrap = ( makeWorkerBootstrap(workerId512, workerFormulaIdentifier), ); - const closed = Promise.race([workerClosed, capTpClosed]).finally(() => { + const terminated = Promise.race([workerClosed, capTpClosed]).finally(() => { console.log( `Endo worker stopped PID ${workerPid} with unique identifier ${workerId512}`, ); @@ -143,13 +147,14 @@ const makeEndoBootstrap = ( const workerBootstrap = getBootstrap(); const terminate = async () => { + notifyTerminating(); E.sendOnly(workerBootstrap).terminate(); const cancelWorkerGracePeriod = () => { throw new Error('Exited gracefully before grace period elapsed'); }; const workerGracePeriodCancelled = Promise.race([ gracePeriodElapsed, - closed, + terminated, ]).then(cancelWorkerGracePeriod, cancelWorkerGracePeriod); await delay(gracePeriodMs, workerGracePeriodCancelled) .then(() => { @@ -161,14 +166,12 @@ const makeEndoBootstrap = ( }; const worker = Far('EndoWorker', { - terminate, - - whenTerminated: () => closed, + whenTerminated: () => terminated, }); workerBootstraps.set(worker, workerBootstrap); - return { promise: worker }; + return { promise: worker, terminate, terminating, terminated }; }; /** @@ -477,6 +480,7 @@ const makeEndoBootstrap = ( makeMailboxMaker({ formulaIdentifierForRef, provideValueForFormulaIdentifier, + provideControllerForFormulaIdentifier, }); const makeIdentifiedGuest = makeGuestMaker({ diff --git a/packages/daemon/src/host.js b/packages/daemon/src/host.js index f470bb85b5..63ab055156 100644 --- a/packages/daemon/src/host.js +++ b/packages/daemon/src/host.js @@ -49,6 +49,7 @@ export const makeHostMaker = ({ adopt, rename, remove, + terminate, } = makeMailbox({ petStore, selfFormulaIdentifier: hostFormulaIdentifier, @@ -436,6 +437,7 @@ export const makeHostMaker = ({ makeWorker, provideWorker, evaluate, + terminate, importUnsafeAndEndow, importBundleAndEndow, provideWebPage, diff --git a/packages/daemon/src/mail.js b/packages/daemon/src/mail.js index e95acdad4d..422554f788 100644 --- a/packages/daemon/src/mail.js +++ b/packages/daemon/src/mail.js @@ -9,6 +9,7 @@ const { quote: q } = assert; export const makeMailboxMaker = ({ provideValueForFormulaIdentifier, + provideControllerForFormulaIdentifier, formulaIdentifierForRef, }) => { /** @type {WeakMap} */ @@ -52,6 +53,23 @@ export const makeMailboxMaker = ({ return provideValueForFormulaIdentifier(formulaIdentifier); }; + const terminate = async petName => { + const formulaIdentifier = lookupFormulaIdentifierForName(petName); + if (formulaIdentifier === undefined) { + throw new TypeError(`Unknown pet name: ${q(petName)}`); + } + // Behold, recursion: + // eslint-disable-next-line no-use-before-define + const controller = await provideControllerForFormulaIdentifier( + formulaIdentifier, + ); + if (controller.terminate) { + controller.terminate(); + return controller.terminated; + } + return undefined; + }; + /** * @param {string} formulaIdentifier */ @@ -454,6 +472,7 @@ export const makeMailboxMaker = ({ adopt, rename, remove, + terminate, }); }; diff --git a/packages/daemon/test/test-endo.js b/packages/daemon/test/test-endo.js index d0c78f5295..25e0c0f289 100644 --- a/packages/daemon/test/test-endo.js +++ b/packages/daemon/test/test-endo.js @@ -60,10 +60,8 @@ test('lifecycle', async t => { ); const bootstrap = getBootstrap(); const host = E(bootstrap).host(); - const worker = await E(host).makeWorker(); - await E(worker) - .terminate() - .catch(() => {}); + await E(host).makeWorker('worker'); + await E(host).terminate('worker'); cancel(new Error('Cancelled')); await closed.catch(() => {}); From a4e068d79f0e860d67e00cd693bc3fcc9402b3a6 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Wed, 11 Oct 2023 22:27:04 -0700 Subject: [PATCH 146/234] refactor(daemon): Replace mailbox side tables with internal API facet on controller --- packages/daemon/src/daemon.js | 16 ++++++---------- packages/daemon/src/guest.js | 21 +++++++++++++------- packages/daemon/src/host.js | 7 ++----- packages/daemon/src/mail.js | 36 +++++++++++++++++++---------------- 4 files changed, 42 insertions(+), 38 deletions(-) diff --git a/packages/daemon/src/daemon.js b/packages/daemon/src/daemon.js index 426cdff8ae..d979743f2e 100644 --- a/packages/daemon/src/daemon.js +++ b/packages/daemon/src/daemon.js @@ -476,17 +476,15 @@ const makeEndoBootstrap = ( return value; }; - const { makeMailbox, partyReceiveFunctions, partyRequestFunctions } = - makeMailboxMaker({ - formulaIdentifierForRef, - provideValueForFormulaIdentifier, - provideControllerForFormulaIdentifier, - }); + const makeMailbox = makeMailboxMaker({ + formulaIdentifierForRef, + provideValueForFormulaIdentifier, + provideControllerForFormulaIdentifier, + }); const makeIdentifiedGuest = makeGuestMaker({ provideValueForFormulaIdentifier, - partyReceiveFunctions, - partyRequestFunctions, + provideControllerForFormulaIdentifier, makeMailbox, }); @@ -495,8 +493,6 @@ const makeEndoBootstrap = ( provideValueForFormula, provideValueForNumberedFormula, formulaIdentifierForRef, - partyReceiveFunctions, - partyRequestFunctions, storeReaderRef, randomHex512, makeSha512, diff --git a/packages/daemon/src/guest.js b/packages/daemon/src/guest.js index 79a1db1897..aba34d2fe0 100644 --- a/packages/daemon/src/guest.js +++ b/packages/daemon/src/guest.js @@ -4,8 +4,7 @@ import { Far } from '@endo/far'; export const makeGuestMaker = ({ provideValueForFormulaIdentifier, - partyReceiveFunctions, - partyRequestFunctions, + provideControllerForFormulaIdentifier, makeMailbox, }) => { /** @@ -26,9 +25,15 @@ export const makeGuestMaker = ({ const hostController = /** @type {import('./types.js').Controller<>} */ ( await provideControllerForFormulaIdentifier(hostFormulaIdentifier) ); - - const deliverToHost = partyRequestFunctions.get(host); + const { internal: hostPrivateFacet } = hostController; + if (hostPrivateFacet === undefined) { + throw new Error( + `panic: a host request function must exist for every host`, + ); + } + const { respond: deliverToHost } = hostPrivateFacet; if (deliverToHost === undefined) { + console.log(hostController); throw new Error( `panic: a host request function must exist for every host`, ); @@ -78,10 +83,12 @@ export const makeGuestMaker = ({ rename, }); - partyReceiveFunctions.set(guest, receive); - partyRequestFunctions.set(guest, respond); + const internal = { + receive, + respond, + }; - return { promise: guest }; + return { promise: guest, internal }; }; return makeIdentifiedGuest; diff --git a/packages/daemon/src/host.js b/packages/daemon/src/host.js index 63ab055156..5b04ac4e04 100644 --- a/packages/daemon/src/host.js +++ b/packages/daemon/src/host.js @@ -9,8 +9,6 @@ export const makeHostMaker = ({ provideValueForFormulaIdentifier, provideValueForFormula, provideValueForNumberedFormula, - partyReceiveFunctions, - partyRequestFunctions, formulaIdentifierForRef, storeReaderRef, makeSha512, @@ -443,10 +441,9 @@ export const makeHostMaker = ({ provideWebPage, }); - partyReceiveFunctions.set(host, receive); - partyRequestFunctions.set(host, respond); + const internal = { receive, respond }; - return { promise: host }; + return { promise: host, internal }; }; return makeIdentifiedHost; diff --git a/packages/daemon/src/mail.js b/packages/daemon/src/mail.js index 422554f788..fede367e19 100644 --- a/packages/daemon/src/mail.js +++ b/packages/daemon/src/mail.js @@ -12,11 +12,6 @@ export const makeMailboxMaker = ({ provideControllerForFormulaIdentifier, formulaIdentifierForRef, }) => { - /** @type {WeakMap} */ - const partyRequestFunctions = new WeakMap(); - /** @type {WeakMap} */ - const partyReceiveFunctions = new WeakMap(); - const makeMailbox = ({ selfFormulaIdentifier, petStore, specialNames }) => { /** @type {Map>} */ const responses = new Map(); @@ -299,9 +294,18 @@ export const makeMailboxMaker = ({ if (recipientFormulaIdentifier === undefined) { throw new Error(`Unknown pet name for party: ${recipientName}`); } - const recipient = await provideValueForFormulaIdentifier( + const recipientController = await provideControllerForFormulaIdentifier( recipientFormulaIdentifier, ); + const { internal: recipientPrivateFacet } = recipientController; + if (recipientPrivateFacet === undefined) { + throw new Error(`Recipient cannot receive messages: ${recipientName}`); + } + const { receive: partyReceive } = recipientPrivateFacet; + if (partyReceive === undefined) { + throw new Error(`Recipient cannot receive messages: ${recipientName}`); + } + petNames.forEach(assertPetName); edgeNames.forEach(assertPetName); if (petNames.length !== edgeNames.length) { @@ -317,10 +321,6 @@ export const makeMailboxMaker = ({ ); } - const partyReceive = partyReceiveFunctions.get(recipient); - if (partyReceive === undefined) { - throw new Error(`panic: Message not deliverable`); - } const formulaIdentifiers = petNames.map(petName => { const formulaIdentifier = lookupFormulaIdentifierForName(petName); if (formulaIdentifier === undefined) { @@ -402,12 +402,20 @@ export const makeMailboxMaker = ({ ) ); - const deliverToRecipient = partyRequestFunctions.get(recipient); + const { internal: recipientPrivateFacet } = recipientController; + if (recipientPrivateFacet === undefined) { + throw new Error( + `panic: a receive request function must exist for every party`, + ); + } + + const { respond: deliverToRecipient } = recipientPrivateFacet; if (deliverToRecipient === undefined) { throw new Error( `panic: a receive request function must exist for every party`, ); } + if (responseName === undefined) { // Behold, recursion: // eslint-disable-next-line no-use-before-define @@ -476,9 +484,5 @@ export const makeMailboxMaker = ({ }); }; - return { - makeMailbox, - partyRequestFunctions, - partyReceiveFunctions, - }; + return makeMailbox; }; From c9182d2323cef24f99c255c3e6332d3a65fd6a39 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Wed, 11 Oct 2023 23:04:40 -0700 Subject: [PATCH 147/234] refactor(daemon): Move worker internal facets from side table to controller --- packages/daemon/src/daemon.js | 48 +++++++++++++++++++++-------------- 1 file changed, 29 insertions(+), 19 deletions(-) diff --git a/packages/daemon/src/daemon.js b/packages/daemon/src/daemon.js index d979743f2e..86f19a03ce 100644 --- a/packages/daemon/src/daemon.js +++ b/packages/daemon/src/daemon.js @@ -66,9 +66,6 @@ const makeEndoBootstrap = ( /** @type {WeakMap} */ const formulaIdentifierForRef = new WeakMap(); - /** @type {WeakMap>} */ - const workerBootstraps = new WeakMap(); - /** * @param {string} sha512 */ @@ -169,9 +166,13 @@ const makeEndoBootstrap = ( whenTerminated: () => terminated, }); - workerBootstraps.set(worker, workerBootstrap); - - return { promise: worker, terminate, terminating, terminated }; + return { + promise: worker, + internal: workerBootstrap, + terminate, + terminating, + terminated, + }; }; /** @@ -188,14 +189,14 @@ const makeEndoBootstrap = ( ) => { // Behold, recursion: // eslint-disable-next-line no-use-before-define - const workerFacet = await provideValueForFormulaIdentifier( + const workerController = await provideControllerForFormulaIdentifier( workerFormulaIdentifier, ); - // TODO consider a better mechanism for hiding the private facet. - // Maybe all these internal functions should return { public, private } - // duples. - const workerBootstrap = workerBootstraps.get(workerFacet); - assert(workerBootstrap); + const workerBootstrap = workerController.internal; + assert( + workerBootstrap, + `panic: No internal bootstrap for worker ${workerFormulaIdentifier}`, + ); const endowmentValues = await Promise.all( formulaIdentifiers.map(formulaIdentifier => // Behold, recursion: @@ -223,11 +224,14 @@ const makeEndoBootstrap = ( ) => { // Behold, recursion: // eslint-disable-next-line no-use-before-define - const workerFacet = await provideValueForFormulaIdentifier( + const workerController = await provideControllerForFormulaIdentifier( workerFormulaIdentifier, ); - const workerBootstrap = workerBootstraps.get(workerFacet); - assert(workerBootstrap); + const workerBootstrap = workerController.internal; + assert( + workerBootstrap, + `panic: No internal bootstrap for worker ${workerFormulaIdentifier}`, + ); const guestP = /** @type {Promise} */ ( // Behold, recursion: // eslint-disable-next-line no-use-before-define @@ -249,11 +253,14 @@ const makeEndoBootstrap = ( ) => { // Behold, recursion: // eslint-disable-next-line no-use-before-define - const workerFacet = await provideValueForFormulaIdentifier( + const workerController = await provideControllerForFormulaIdentifier( workerFormulaIdentifier, ); - const workerBootstrap = workerBootstraps.get(workerFacet); - assert(workerBootstrap); + const workerBootstrap = workerController.internal; + assert( + workerBootstrap, + `panic: No internal bootstrap for worker ${workerFormulaIdentifier}`, + ); // Behold, recursion: // eslint-disable-next-line no-use-before-define const readableBundleP = @@ -333,7 +340,10 @@ const makeEndoBootstrap = ( const delimiterIndex = formulaIdentifier.indexOf(':'); if (delimiterIndex < 0) { if (formulaIdentifier === 'pet-store') { - const promise = petStorePowers.makeOwnPetStore('pet-store', assertPetName); + const promise = petStorePowers.makeOwnPetStore( + 'pet-store', + assertPetName, + ); return { promise }; } else if (formulaIdentifier === 'host') { // Behold, recursion: From 76ec7ea6bb1328ea0f530571a8762c0e471911c0 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Thu, 19 Oct 2023 16:13:21 -0700 Subject: [PATCH 148/234] feat(daemon): Support termination DAG generally --- packages/daemon/src/daemon.js | 292 ++++++++++++++++++-------- packages/daemon/src/guest.js | 20 +- packages/daemon/src/host.js | 12 +- packages/daemon/src/mail.js | 29 +-- packages/daemon/src/pet-store.js | 3 +- packages/daemon/src/terminator.js | 76 +++++++ packages/daemon/src/types.d.ts | 21 +- packages/daemon/src/worker.js | 4 +- packages/daemon/test/counter-party.js | 11 + packages/daemon/test/counter.js | 11 + packages/daemon/test/test-endo.js | 246 ++++++++++++++++++++++ 11 files changed, 611 insertions(+), 114 deletions(-) create mode 100644 packages/daemon/src/terminator.js create mode 100644 packages/daemon/test/counter-party.js create mode 100644 packages/daemon/test/counter.js diff --git a/packages/daemon/src/daemon.js b/packages/daemon/src/daemon.js index 86f19a03ce..96fa30a5b7 100644 --- a/packages/daemon/src/daemon.js +++ b/packages/daemon/src/daemon.js @@ -11,6 +11,7 @@ import { makeMailboxMaker } from './mail.js'; import { makeGuestMaker } from './guest.js'; import { makeHostMaker } from './host.js'; import { assertPetName } from './pet-name.js'; +import { makeTerminatorMaker } from './terminator.js'; const { quote: q } = assert; @@ -69,16 +70,15 @@ const makeEndoBootstrap = ( /** * @param {string} sha512 */ - const makeSha512ReadableBlob = sha512 => { + const makeReadableBlob = sha512 => { const { text, json, stream } = contentStore.fetch(sha512); - const promise = Far(`Readable file with SHA-512 ${sha512.slice(0, 8)}...`, { + return Far(`Readable file with SHA-512 ${sha512.slice(0, 8)}...`, { sha512: () => sha512, stream, text, json, [Symbol.asyncIterator]: stream, }); - return { promise }; }; /** @@ -100,15 +100,12 @@ const makeEndoBootstrap = ( /** * @param {string} workerId512 + * @param {import('./types.js').Terminator} terminator */ - const makeIdentifiedWorker = async workerId512 => { + const makeIdentifiedWorkerController = async (workerId512, terminator) => { // TODO validate workerId512 const workerFormulaIdentifier = `worker-id512:${workerId512}`; - const { resolve: notifyTerminating, promise: terminating } = - /** @type {import('@endo/promise-kit').PromiseKit} */ ( - makePromiseKit() - ); const { reject: cancelWorker, promise: workerCancelled } = /** @type {import('@endo/promise-kit').PromiseKit} */ ( makePromiseKit() @@ -144,7 +141,6 @@ const makeEndoBootstrap = ( const workerBootstrap = getBootstrap(); const terminate = async () => { - notifyTerminating(); E.sendOnly(workerBootstrap).terminate(); const cancelWorkerGracePeriod = () => { throw new Error('Exited gracefully before grace period elapsed'); @@ -160,43 +156,55 @@ const makeEndoBootstrap = ( ); }) .catch(cancelWorker); + await terminated; }; + terminator.onTerminate(terminate); + const worker = Far('EndoWorker', { - whenTerminated: () => terminated, + terminate: terminator.terminate, + whenTerminated: () => terminator.terminated, }); return { - promise: worker, + external: worker, internal: workerBootstrap, - terminate, - terminating, - terminated, }; }; /** + * @param {string} evalFormulaIdentifier * @param {string} workerFormulaIdentifier * @param {string} source * @param {Array} codeNames * @param {Array} formulaIdentifiers + * @param {import('./types.js').Terminator} terminator */ - const makeValueForEval = async ( + const makeControllerForEval = async ( + evalFormulaIdentifier, workerFormulaIdentifier, source, codeNames, formulaIdentifiers, + terminator, ) => { - // Behold, recursion: - // eslint-disable-next-line no-use-before-define - const workerController = await provideControllerForFormulaIdentifier( - workerFormulaIdentifier, - ); + terminator.thisDiesIfThatDies(workerFormulaIdentifier); + for (const formulaIdentifier of formulaIdentifiers) { + terminator.thisDiesIfThatDies(formulaIdentifier); + } + + const workerController = + /** @type {import('./types.js').Controller} */ ( + // Behold, recursion: + // eslint-disable-next-line no-use-before-define + provideControllerForFormulaIdentifier(workerFormulaIdentifier) + ); const workerBootstrap = workerController.internal; assert( workerBootstrap, `panic: No internal bootstrap for worker ${workerFormulaIdentifier}`, ); + const endowmentValues = await Promise.all( formulaIdentifiers.map(formulaIdentifier => // Behold, recursion: @@ -204,29 +212,50 @@ const makeEndoBootstrap = ( provideValueForFormulaIdentifier(formulaIdentifier), ), ); - const promise = E(workerBootstrap).evaluate( + + const external = E(workerBootstrap).evaluate( source, codeNames, endowmentValues, + terminator.terminated.then(() => { + throw new Error('Terminated'); + }), ); - return { promise }; + + // TODO check whether the promise resolves to data that can be marshalled + // into the content-address-store and truncate the dependency chain. + // That will require some funny business around allowing eval formulas to + // have a level of indirection where the settled formula depends on how + // the indirect formula resolves. + // That might mean racing two formulas and terminating the evaluator + // if it turns out the value can be captured. + + return { external, internal: undefined }; }; /** + * @param {string} valueFormulaIdentifier * @param {string} workerFormulaIdentifier * @param {string} guestFormulaIdentifier * @param {string} importPath + * @param {import('./types.js').Terminator} terminator */ - const makeValueForImportUnsafe0 = async ( + const makeControllerForUnsafePlugin = async ( + valueFormulaIdentifier, workerFormulaIdentifier, guestFormulaIdentifier, importPath, + terminator, ) => { - // Behold, recursion: - // eslint-disable-next-line no-use-before-define - const workerController = await provideControllerForFormulaIdentifier( - workerFormulaIdentifier, - ); + terminator.thisDiesIfThatDies(workerFormulaIdentifier); + terminator.thisDiesIfThatDies(guestFormulaIdentifier); + + const workerController = + /** @type {import('./types.js').Controller} */ ( + // Behold, recursion: + // eslint-disable-next-line no-use-before-define + provideControllerForFormulaIdentifier(workerFormulaIdentifier) + ); const workerBootstrap = workerController.internal; assert( workerBootstrap, @@ -237,25 +266,36 @@ const makeEndoBootstrap = ( // eslint-disable-next-line no-use-before-define provideValueForFormulaIdentifier(guestFormulaIdentifier) ); - const promise = E(workerBootstrap).importUnsafeAndEndow(importPath, guestP); - return { promise }; + const external = E(workerBootstrap).importUnsafeAndEndow( + importPath, + guestP, + ); + return { external, internal: undefined }; }; /** + * @param {string} valueFormulaIdentifier * @param {string} workerFormulaIdentifier * @param {string} guestFormulaIdentifier * @param {string} bundleFormulaIdentifier + * @param {import('./types.js').Terminator} terminator */ - const makeValueForImportBundle0 = async ( + const makeControllerForSafeBundle = async ( + valueFormulaIdentifier, workerFormulaIdentifier, guestFormulaIdentifier, bundleFormulaIdentifier, + terminator, ) => { - // Behold, recursion: - // eslint-disable-next-line no-use-before-define - const workerController = await provideControllerForFormulaIdentifier( - workerFormulaIdentifier, - ); + terminator.thisDiesIfThatDies(workerFormulaIdentifier); + terminator.thisDiesIfThatDies(guestFormulaIdentifier); + + const workerController = + /** @type {import('./types.js').Controller} */ ( + // Behold, recursion: + // eslint-disable-next-line no-use-before-define + provideControllerForFormulaIdentifier(workerFormulaIdentifier) + ); const workerBootstrap = workerController.internal; assert( workerBootstrap, @@ -274,50 +314,69 @@ const makeEndoBootstrap = ( // eslint-disable-next-line no-use-before-define provideValueForFormulaIdentifier(guestFormulaIdentifier) ); - const promise = E(workerBootstrap).importBundleAndEndow( + const external = E(workerBootstrap).importBundleAndEndow( readableBundleP, guestP, ); - return { promise }; + return { external, internal: undefined }; }; /** * @param {string} formulaIdentifier * @param {string} formulaNumber * @param {import('./types.js').Formula} formula + * @param {import('./types.js').Terminator} terminator */ - const makeValueForFormula = (formulaIdentifier, formulaNumber, formula) => { + const makeControllerForFormula = async ( + formulaIdentifier, + formulaNumber, + formula, + terminator, + ) => { if (formula.type === 'eval') { - return makeValueForEval( + return makeControllerForEval( + formulaIdentifier, formula.worker, formula.source, formula.names, formula.values, + terminator, ); } else if (formula.type === 'import-unsafe') { - return makeValueForImportUnsafe0( + return makeControllerForUnsafePlugin( + formulaIdentifier, formula.worker, formula.powers, formula.importPath, + terminator, ); } else if (formula.type === 'import-bundle') { - return makeValueForImportBundle0( + return makeControllerForSafeBundle( + formulaIdentifier, formula.worker, formula.powers, formula.bundle, + terminator, ); } else if (formula.type === 'guest') { + const storeFormulaIdentifier = `pet-store-id512:${formulaNumber}`; + const workerFormulaIdentifier = `worker-id512:${formulaNumber}`; // Behold, recursion: // eslint-disable-next-line no-use-before-define - return makeIdentifiedGuest( + return makeIdentifiedGuestController( formulaIdentifier, formula.host, - `pet-store-id512:${formulaNumber}`, - `worker-id512:${formulaNumber}`, + storeFormulaIdentifier, + workerFormulaIdentifier, + terminator, ); } else if (formula.type === 'web-bundle') { + // Behold, forward-reference: + // eslint-disable-next-line no-use-before-define + terminator.thisDiesIfThatDies(formula.bundle); + terminator.thisDiesIfThatDies(formula.powers); return { - promise: (async () => + external: (async () => harden({ url: `http://${formulaNumber}.endo.localhost:${await webletPortP}`, // Behold, recursion: @@ -327,6 +386,7 @@ const makeEndoBootstrap = ( // eslint-disable-next-line no-use-before-define powers: provideValueForFormulaIdentifier(formula.powers), }))(), + internal: undefined, }; } else { throw new TypeError(`Invalid formula: ${q(formula)}`); @@ -335,38 +395,48 @@ const makeEndoBootstrap = ( /** * @param {string} formulaIdentifier + * @param {import('./types.js').Terminator} terminator */ - const makeValueForFormulaIdentifier = async formulaIdentifier => { + const makeControllerForFormulaIdentifier = async ( + formulaIdentifier, + terminator, + ) => { const delimiterIndex = formulaIdentifier.indexOf(':'); if (delimiterIndex < 0) { if (formulaIdentifier === 'pet-store') { - const promise = petStorePowers.makeOwnPetStore( + const external = petStorePowers.makeOwnPetStore( 'pet-store', assertPetName, ); - return { promise }; + return { external, internal: undefined }; } else if (formulaIdentifier === 'host') { + const storeFormulaIdentifier = 'pet-store'; + const workerFormulaIdentifier = `worker-id512:${zero512}`; // Behold, recursion: // eslint-disable-next-line no-use-before-define return makeIdentifiedHost( formulaIdentifier, - 'pet-store', - `worker-id512:${zero512}`, + storeFormulaIdentifier, + workerFormulaIdentifier, + terminator, ); } else if (formulaIdentifier === 'endo') { + // TODO reframe "cancelled" as termination of the "endo" object and + // ensure that all values ultimately depend on "endo". // Behold, self-referentiality: // eslint-disable-next-line no-use-before-define - return { promise: endoBootstrap }; + return { external: endoBootstrap, internal: undefined }; } else if (formulaIdentifier === 'least-authority') { - return { promise: leastAuthority }; + return { external: leastAuthority, internal: undefined }; } else if (formulaIdentifier === 'web-page-js') { if (persistencePowers.webPageBundlerFormula === undefined) { throw Error('No web-page-js formula provided.'); } - return makeValueForFormula( + return makeControllerForFormula( 'web-page-js', zero512, persistencePowers.webPageBundlerFormula, + terminator, ); } throw new TypeError( @@ -376,23 +446,30 @@ const makeEndoBootstrap = ( const prefix = formulaIdentifier.slice(0, delimiterIndex); const formulaNumber = formulaIdentifier.slice(delimiterIndex + 1); if (prefix === 'readable-blob-sha512') { - return makeSha512ReadableBlob(formulaNumber); + // Behold, forward-reference: + // eslint-disable-next-line no-use-before-define + const external = makeReadableBlob(formulaNumber); + return { external, internal: undefined }; } else if (prefix === 'worker-id512') { - return makeIdentifiedWorker(formulaNumber); + return makeIdentifiedWorkerController(formulaNumber, terminator); } else if (prefix === 'pet-store-id512') { - const promise = petStorePowers.makeIdentifiedPetStore( + const external = petStorePowers.makeIdentifiedPetStore( formulaNumber, assertPetName, ); - return { promise }; + return { external, internal: undefined }; } else if (prefix === 'host-id512') { + const storeFormulaIdentifier = `pet-store-id512:${formulaNumber}`; + const workerFormulaIdentifier = `worker-id512:${formulaNumber}`; // Behold, recursion: // eslint-disable-next-line no-use-before-define - return makeIdentifiedHost( + const external = makeIdentifiedHost( formulaIdentifier, - `pet-store-id512:${formulaNumber}`, - `worker-id512:${formulaNumber}`, + storeFormulaIdentifier, + workerFormulaIdentifier, + terminator, ); + return { external, internal: undefined }; } else if ( [ 'eval-id512', @@ -407,7 +484,12 @@ const makeEndoBootstrap = ( formulaNumber, ); // TODO validate - return makeValueForFormula(formulaIdentifier, formulaNumber, formula); + return makeControllerForFormula( + formulaIdentifier, + formulaNumber, + formula, + terminator, + ); } else { throw new TypeError( `Invalid formula identifier, unrecognized type ${q(formulaIdentifier)}`, @@ -427,25 +509,43 @@ const makeEndoBootstrap = ( ) => { const formulaIdentifier = `${formulaType}:${formulaNumber}`; - await persistencePowers.writeFormula(formula, formulaType, formulaNumber); + // Memoize for lookup. + console.log(`Making ${formulaIdentifier}`); + const { promise: partial, resolve } = + /** @type {import('@endo/promise-kit').PromiseKit>} */ ( + makePromiseKit() + ); + // Behold, recursion: // eslint-disable-next-line no-use-before-define - const controller = await makeValueForFormula( - formulaIdentifier, - formulaNumber, - formula, - ); - - // Memoize for lookup. + const terminator = makeTerminator(formulaIdentifier); + partial.catch(error => terminator.terminate()); + const controller = harden({ + terminator, + external: E.get(partial).external.then(value => { + if (typeof value === 'object' && value !== null) { + formulaIdentifierForRef.set(value, formulaIdentifier); + } + return value; + }), + internal: E.get(partial).internal, + }); controllerForFormulaIdentifier.set(formulaIdentifier, controller); - // Prepare an entry for reverse-lookup of formula for presence. - const value = await controller.promise; - if (typeof value === 'object' && value !== null) { - formulaIdentifierForRef.set(value, formulaIdentifier); - } + await persistencePowers.writeFormula(formula, formulaType, formulaNumber); + resolve( + makeControllerForFormula( + formulaIdentifier, + formulaNumber, + formula, + terminator, + ), + ); - return { formulaIdentifier, value }; + return harden({ + formulaIdentifier, + value: controller.external, + }); }; /** @@ -460,15 +560,30 @@ const makeEndoBootstrap = ( /** * @param {string} formulaIdentifier */ - const provideControllerForFormulaIdentifier = async formulaIdentifier => { + const provideControllerForFormulaIdentifier = formulaIdentifier => { let controller = controllerForFormulaIdentifier.get(formulaIdentifier); - if (controller === undefined) { - controller = await makeValueForFormulaIdentifier(formulaIdentifier); - if (controller === undefined) { - throw new Error('panic: not sure why the type is not narrower here'); - } - controllerForFormulaIdentifier.set(formulaIdentifier, controller); + if (controller !== undefined) { + return controller; } + + console.log(`Making ${formulaIdentifier}`); + const { promise: partial, resolve } = + /** @type {import('@endo/promise-kit').PromiseKit>} */ ( + makePromiseKit() + ); + + // Behold, recursion: + // eslint-disable-next-line no-use-before-define + const terminator = makeTerminator(formulaIdentifier); + partial.catch(error => terminator.terminate()); + controller = harden({ + terminator, + external: E.get(partial).external, + internal: E.get(partial).internal, + }); + controllerForFormulaIdentifier.set(formulaIdentifier, controller); + resolve(makeControllerForFormulaIdentifier(formulaIdentifier, terminator)); + return controller; }; @@ -477,22 +592,27 @@ const makeEndoBootstrap = ( */ const provideValueForFormulaIdentifier = async formulaIdentifier => { const controller = /** @type {import('./types.js').Controller<>} */ ( - await provideControllerForFormulaIdentifier(formulaIdentifier) + provideControllerForFormulaIdentifier(formulaIdentifier) ); - const value = await controller.promise; + const value = await controller.external; if (typeof value === 'object' && value !== null) { formulaIdentifierForRef.set(value, formulaIdentifier); } return value; }; + const makeTerminator = makeTerminatorMaker({ + controllerForFormulaIdentifier, + provideControllerForFormulaIdentifier, + }); + const makeMailbox = makeMailboxMaker({ formulaIdentifierForRef, provideValueForFormulaIdentifier, provideControllerForFormulaIdentifier, }); - const makeIdentifiedGuest = makeGuestMaker({ + const makeIdentifiedGuestController = makeGuestMaker({ provideValueForFormulaIdentifier, provideControllerForFormulaIdentifier, makeMailbox, diff --git a/packages/daemon/src/guest.js b/packages/daemon/src/guest.js index aba34d2fe0..ef6da3eca5 100644 --- a/packages/daemon/src/guest.js +++ b/packages/daemon/src/guest.js @@ -12,20 +12,26 @@ export const makeGuestMaker = ({ * @param {string} hostFormulaIdentifier * @param {string} petStoreFormulaIdentifier * @param {string} mainWorkerFormulaIdentifier + * @param {import('./types.js').Terminator} terminator */ - const makeIdentifiedGuest = async ( + const makeIdentifiedGuestController = async ( guestFormulaIdentifier, hostFormulaIdentifier, petStoreFormulaIdentifier, mainWorkerFormulaIdentifier, + terminator, ) => { + terminator.thisDiesIfThatDies(hostFormulaIdentifier); + terminator.thisDiesIfThatDies(petStoreFormulaIdentifier); + terminator.thisDiesIfThatDies(mainWorkerFormulaIdentifier); + const petStore = /** @type {import('./types.js').PetStore} */ ( await provideValueForFormulaIdentifier(petStoreFormulaIdentifier) ); const hostController = /** @type {import('./types.js').Controller<>} */ ( await provideControllerForFormulaIdentifier(hostFormulaIdentifier) ); - const { internal: hostPrivateFacet } = hostController; + const hostPrivateFacet = await hostController.internal; if (hostPrivateFacet === undefined) { throw new Error( `panic: a host request function must exist for every host`, @@ -33,7 +39,6 @@ export const makeGuestMaker = ({ } const { respond: deliverToHost } = hostPrivateFacet; if (deliverToHost === undefined) { - console.log(hostController); throw new Error( `panic: a host request function must exist for every host`, ); @@ -61,6 +66,7 @@ export const makeGuestMaker = ({ SELF: guestFormulaIdentifier, HOST: hostFormulaIdentifier, }, + terminator, }); const { list, follow: followNames } = petStore; @@ -83,13 +89,13 @@ export const makeGuestMaker = ({ rename, }); - const internal = { + const internal = harden({ receive, respond, - }; + }); - return { promise: guest, internal }; + return harden({ external: guest, internal }); }; - return makeIdentifiedGuest; + return makeIdentifiedGuestController; }; diff --git a/packages/daemon/src/host.js b/packages/daemon/src/host.js index 5b04ac4e04..226f3e373c 100644 --- a/packages/daemon/src/host.js +++ b/packages/daemon/src/host.js @@ -19,12 +19,17 @@ export const makeHostMaker = ({ * @param {string} hostFormulaIdentifier * @param {string} storeFormulaIdentifier * @param {string} mainWorkerFormulaIdentifier + * @param {import('./types.js').Terminator} terminator */ const makeIdentifiedHost = async ( hostFormulaIdentifier, storeFormulaIdentifier, mainWorkerFormulaIdentifier, + terminator, ) => { + terminator.thisDiesIfThatDies(storeFormulaIdentifier); + terminator.thisDiesIfThatDies(mainWorkerFormulaIdentifier); + const petStore = /** @type {import('./types.js').PetStore} */ ( // Behold, recursion: // eslint-disable-next-line no-use-before-define @@ -56,6 +61,7 @@ export const makeHostMaker = ({ NONE: 'least-authority', ENDO: 'endo', }, + terminator, }); /** @@ -441,9 +447,11 @@ export const makeHostMaker = ({ provideWebPage, }); - const internal = { receive, respond }; + const internal = harden({ receive, respond }); + + await provideValueForFormulaIdentifier(mainWorkerFormulaIdentifier); - return { promise: host, internal }; + return harden({ external: host, internal }); }; return makeIdentifiedHost; diff --git a/packages/daemon/src/mail.js b/packages/daemon/src/mail.js index fede367e19..c41c4d8d5f 100644 --- a/packages/daemon/src/mail.js +++ b/packages/daemon/src/mail.js @@ -12,7 +12,12 @@ export const makeMailboxMaker = ({ provideControllerForFormulaIdentifier, formulaIdentifierForRef, }) => { - const makeMailbox = ({ selfFormulaIdentifier, petStore, specialNames }) => { + const makeMailbox = ({ + selfFormulaIdentifier, + petStore, + specialNames, + terminator, + }) => { /** @type {Map>} */ const responses = new Map(); /** @type {Map} */ @@ -58,11 +63,8 @@ export const makeMailboxMaker = ({ const controller = await provideControllerForFormulaIdentifier( formulaIdentifier, ); - if (controller.terminate) { - controller.terminate(); - return controller.terminated; - } - return undefined; + console.log('Terminating:'); + return controller.terminator.terminate(); }; /** @@ -219,6 +221,8 @@ export const makeMailboxMaker = ({ what, senderFormulaIdentifier, ); + // TODO: + // terminator.thisDiesIfThatDies(formulaIdentifier); // Behold, recursion: // eslint-disable-next-line no-use-before-define return provideValueForFormulaIdentifier(formulaIdentifier); @@ -297,11 +301,11 @@ export const makeMailboxMaker = ({ const recipientController = await provideControllerForFormulaIdentifier( recipientFormulaIdentifier, ); - const { internal: recipientPrivateFacet } = recipientController; - if (recipientPrivateFacet === undefined) { + const recipientInternal = await recipientController.internal; + if (recipientInternal === undefined) { throw new Error(`Recipient cannot receive messages: ${recipientName}`); } - const { receive: partyReceive } = recipientPrivateFacet; + const { receive: partyReceive } = recipientInternal; if (partyReceive === undefined) { throw new Error(`Recipient cannot receive messages: ${recipientName}`); } @@ -381,6 +385,7 @@ export const makeMailboxMaker = ({ )} at ${q(index)}`, ); } + terminator.thisDiesIfThatDies(formulaIdentifier); await petStore.write(petName, formulaIdentifier); }; @@ -402,14 +407,14 @@ export const makeMailboxMaker = ({ ) ); - const { internal: recipientPrivateFacet } = recipientController; - if (recipientPrivateFacet === undefined) { + const recipientInternal = await recipientController.internal; + if (recipientInternal === undefined) { throw new Error( `panic: a receive request function must exist for every party`, ); } - const { respond: deliverToRecipient } = recipientPrivateFacet; + const { respond: deliverToRecipient } = await recipientInternal; if (deliverToRecipient === undefined) { throw new Error( `panic: a receive request function must exist for every party`, diff --git a/packages/daemon/src/pet-store.js b/packages/daemon/src/pet-store.js index bb4a6736e9..f839c547da 100644 --- a/packages/daemon/src/pet-store.js +++ b/packages/daemon/src/pet-store.js @@ -63,7 +63,8 @@ export const makePetStoreMaker = (filePowers, locator) => { /** @param {string} petName */ const lookup = petName => { assertValidName(petName); - return petNames.get(petName); + const formulaIdentifier = petNames.get(petName); + return formulaIdentifier; }; /** diff --git a/packages/daemon/src/terminator.js b/packages/daemon/src/terminator.js new file mode 100644 index 0000000000..db9fd11bb6 --- /dev/null +++ b/packages/daemon/src/terminator.js @@ -0,0 +1,76 @@ +// @ts-check + +import { makePromiseKit } from '@endo/promise-kit'; + +export const makeTerminatorMaker = ({ + controllerForFormulaIdentifier, + provideControllerForFormulaIdentifier, +}) => { + /** + * @param {string} formulaIdentifier + */ + const makeTerminator = formulaIdentifier => { + let terminating = false; + const { promise: terminated, resolve: resolveTerminated } = + /** @type {import('@endo/promise-kit').PromiseKit} */ ( + makePromiseKit() + ); + + /** @type {Map} */ + const dependents = new Map(); + /** @type {Array<() => void>} */ + const hooks = []; + + const terminate = (prefix = '*') => { + if (terminating) return terminated; + terminating = true; + + console.log(`${prefix} ${formulaIdentifier}`); + + controllerForFormulaIdentifier.delete(formulaIdentifier); + for (const dependentTerminator of dependents.values()) { + dependentTerminator.terminate(` ${prefix}`); + } + dependents.clear(); + + resolveTerminated(Promise.all(hooks.map(hook => hook())).then(() => {})); + + return terminated; + }; + + const thatDiesIfThisDies = dependentFormulaIdentifier => { + assert(!terminating); + const dependentController = provideControllerForFormulaIdentifier( + dependentFormulaIdentifier, + ); + dependents.set( + dependentFormulaIdentifier, + dependentController.terminator, + ); + }; + + const thisDiesIfThatDies = dependencyIdentifier => { + const dependencyController = + provideControllerForFormulaIdentifier(dependencyIdentifier); + dependencyController.terminator.thatDiesIfThisDies(formulaIdentifier); + }; + + /** + * @param {() => void} hook + */ + const onTerminate = hook => { + assert(!terminating); + hooks.push(hook); + }; + + return { + terminate, + terminated, + thatDiesIfThisDies, + thisDiesIfThatDies, + onTerminate, + }; + }; + + return makeTerminator; +}; diff --git a/packages/daemon/src/types.d.ts b/packages/daemon/src/types.d.ts index 1116a925ec..51d87a485b 100644 --- a/packages/daemon/src/types.d.ts +++ b/packages/daemon/src/types.d.ts @@ -140,12 +140,23 @@ export interface Topic< subscribe(): Stream; } -export interface Controller { - promise: Promise; +export interface Terminator { + terminate: (logPrefix?: string) => Promise; + terminated: Promise; + thisDiesIfThatDies: (formulaIdentifier: string) => void; + thatDiesIfThisDies: (formulaIdentifier: string) => void; + onTerminate: (hook: () => void | Promise) => void; +} + +export interface InternalExternal { + external: External; internal: Internal; - terminate?: () => {}; - terminating?: Promise; - terminated?: Promise; +} + +export interface Controller { + external: Promise; + internal: Promise; + terminator: Terminator; } export interface PetStore { diff --git a/packages/daemon/src/worker.js b/packages/daemon/src/worker.js index cfa2179e92..15a98f385e 100644 --- a/packages/daemon/src/worker.js +++ b/packages/daemon/src/worker.js @@ -39,11 +39,13 @@ export const makeWorkerFacet = ({ * @param {string} source * @param {Array} names * @param {Array} values + * @param {Promise} cancelled */ - evaluate: async (source, names, values) => { + evaluate: async (source, names, values, cancelled) => { const compartment = new Compartment( harden({ ...endowments, + cancelled, ...Object.fromEntries( names.map((name, index) => [name, values[index]]), ), diff --git a/packages/daemon/test/counter-party.js b/packages/daemon/test/counter-party.js new file mode 100644 index 0000000000..da05665130 --- /dev/null +++ b/packages/daemon/test/counter-party.js @@ -0,0 +1,11 @@ +import { E, Far } from '@endo/far'; + +export const make = async powers => { + let counter = await E(powers).request('HOST', 'starting number', 'start'); + return Far('Counter', { + incr() { + counter += 1; + return counter; + }, + }); +}; diff --git a/packages/daemon/test/counter.js b/packages/daemon/test/counter.js new file mode 100644 index 0000000000..853556ce3c --- /dev/null +++ b/packages/daemon/test/counter.js @@ -0,0 +1,11 @@ +import { Far } from '@endo/far'; + +export const make = () => { + let counter = 0; + return Far('Counter', { + incr() { + counter += 1; + return counter; + }, + }); +}; diff --git a/packages/daemon/test/test-endo.js b/packages/daemon/test/test-endo.js index 25e0c0f289..6071600330 100644 --- a/packages/daemon/test/test-endo.js +++ b/packages/daemon/test/test-endo.js @@ -424,3 +424,249 @@ test('guest facet receives a message for host', async t => { await stop(locator); }); + +test('direct termination', async t => { + const { promise: cancelled, reject: cancel } = makePromiseKit(); + t.teardown(() => cancel(Error('teardown'))); + + const locator = makeLocator('tmp', 'termination-direct'); + + await start(locator); + t.teardown(() => stop(locator)); + + const { getBootstrap } = await makeEndoClient( + 'client', + locator.sockPath, + cancelled, + ); + const bootstrap = getBootstrap(); + const host = E(bootstrap).host(); + await E(host).provideWorker('worker'); + + const counterPath = path.join(dirname, 'test', 'counter.js'); + await E(host).importUnsafeAndEndow('worker', counterPath, 'NONE', 'counter'); + t.is( + 1, + await E(host).evaluate( + 'worker', + 'E(counter).incr()', + ['counter'], + ['counter'], + ), + ); + t.is( + 2, + await E(host).evaluate( + 'worker', + 'E(counter).incr()', + ['counter'], + ['counter'], + ), + ); + t.is( + 3, + await E(host).evaluate( + 'worker', + 'E(counter).incr()', + ['counter'], + ['counter'], + ), + ); + + await E(host).terminate('counter'); + t.is( + 1, + await E(host).evaluate( + 'worker', + 'E(counter).incr()', + ['counter'], + ['counter'], + ), + ); + t.is( + 2, + await E(host).evaluate( + 'worker', + 'E(counter).incr()', + ['counter'], + ['counter'], + ), + ); + t.is( + 3, + await E(host).evaluate( + 'worker', + 'E(counter).incr()', + ['counter'], + ['counter'], + ), + ); + + t.pass(); +}); + +test('indirect termination', async t => { + const { promise: cancelled, reject: cancel } = makePromiseKit(); + t.teardown(() => cancel(Error('teardown'))); + + const locator = makeLocator('tmp', 'termination-indirect'); + + await start(locator); + t.teardown(() => stop(locator)); + + const { getBootstrap } = await makeEndoClient( + 'client', + locator.sockPath, + cancelled, + ); + const bootstrap = getBootstrap(); + const host = E(bootstrap).host(); + await E(host).provideWorker('worker'); + + const counterPath = path.join(dirname, 'test', 'counter.js'); + await E(host).importUnsafeAndEndow('worker', counterPath, 'SELF', 'counter'); + t.is( + 1, + await E(host).evaluate( + 'worker', + 'E(counter).incr()', + ['counter'], + ['counter'], + ), + ); + t.is( + 2, + await E(host).evaluate( + 'worker', + 'E(counter).incr()', + ['counter'], + ['counter'], + ), + ); + t.is( + 3, + await E(host).evaluate( + 'worker', + 'E(counter).incr()', + ['counter'], + ['counter'], + ), + ); + + await E(host).terminate('worker'); + + t.is( + 1, + await E(host).evaluate( + 'worker', + 'E(counter).incr()', + ['counter'], + ['counter'], + ), + ); + t.is( + 2, + await E(host).evaluate( + 'worker', + 'E(counter).incr()', + ['counter'], + ['counter'], + ), + ); + t.is( + 3, + await E(host).evaluate( + 'worker', + 'E(counter).incr()', + ['counter'], + ['counter'], + ), + ); +}); + +test('terminate because of requested capability', async t => { + const { promise: cancelled, reject: cancel } = makePromiseKit(); + t.teardown(() => cancel(Error('teardown'))); + + const locator = makeLocator('tmp', 'termination-via-request'); + + await start(locator); + t.teardown(() => stop(locator)); + + const { getBootstrap } = await makeEndoClient( + 'client', + locator.sockPath, + cancelled, + ); + const bootstrap = getBootstrap(); + const host = E(bootstrap).host(); + await E(host).provideWorker('worker'); + await E(host).provideGuest('guest'); + + const messages = E(host).followMessages(); + + const counterPath = path.join(dirname, 'test', 'counter-party.js'); + E(host).importUnsafeAndEndow('worker', counterPath, 'guest', 'counter'); + + await E(host).evaluate('worker', '0', [], [], 'zero'); + await E(messages).next(); + E(host).resolve(0, 'zero'); + + t.is( + 1, + await E(host).evaluate( + 'worker', + 'E(counter).incr()', + ['counter'], + ['counter'], + ), + ); + t.is( + 2, + await E(host).evaluate( + 'worker', + 'E(counter).incr()', + ['counter'], + ['counter'], + ), + ); + t.is( + 3, + await E(host).evaluate( + 'worker', + 'E(counter).incr()', + ['counter'], + ['counter'], + ), + ); + + await E(host).terminate('guest'); + + t.is( + 1, + await E(host).evaluate( + 'worker', + 'E(counter).incr()', + ['counter'], + ['counter'], + ), + ); + t.is( + 2, + await E(host).evaluate( + 'worker', + 'E(counter).incr()', + ['counter'], + ['counter'], + ), + ); + t.is( + 3, + await E(host).evaluate( + 'worker', + 'E(counter).incr()', + ['counter'], + ['counter'], + ), + ); +}); From e4cf09e84285e8bc4c469ec0a0b0890c10f3c8e1 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Fri, 13 Oct 2023 16:12:19 -0700 Subject: [PATCH 149/234] feat(cli): kill command --- packages/cli/src/endo.js | 15 +++++++++++++++ packages/cli/src/kill.js | 9 +++++++++ 2 files changed, 24 insertions(+) create mode 100644 packages/cli/src/kill.js diff --git a/packages/cli/src/endo.js b/packages/cli/src/endo.js index 8c66ee1ebf..5391a89f07 100644 --- a/packages/cli/src/endo.js +++ b/packages/cli/src/endo.js @@ -481,6 +481,21 @@ export const main = async rawArgs => { return mkguest({ name, partyNames }); }); + program + .command('kill ') + .option( + '-a,--as ', + 'Pose as named party (as named by current party)', + collect, + [], + ) + .description('terminate a value and its deps, recovering resources') + .action(async (name, cmd) => { + const { as: partyNames } = cmd.opts(); + const { killCommand } = await import('./kill.js'); + return killCommand({ name, partyNames }); + }); + const where = program .command('where') .description('prints paths for state, logs, caches, socket, pids'); diff --git a/packages/cli/src/kill.js b/packages/cli/src/kill.js new file mode 100644 index 0000000000..e87632ee88 --- /dev/null +++ b/packages/cli/src/kill.js @@ -0,0 +1,9 @@ +/* global process */ +import os from 'os'; +import { E } from '@endo/far'; +import { withEndoParty } from './context.js'; + +export const killCommand = async ({ name, partyNames }) => + withEndoParty(partyNames, { os, process }, async ({ party }) => { + await E(party).terminate(name); + }); From aff6fa867eff50ee9ad8fcc7b9db69d0d3757f8a Mon Sep 17 00:00:00 2001 From: kumavis Date: Fri, 1 Dec 2023 09:03:00 -1000 Subject: [PATCH 150/234] feat(daemon): add "has" method to pet-store --- packages/daemon/src/pet-store.js | 7 +++++++ packages/daemon/src/types.d.ts | 1 + 2 files changed, 8 insertions(+) diff --git a/packages/daemon/src/pet-store.js b/packages/daemon/src/pet-store.js index f839c547da..dea68ae808 100644 --- a/packages/daemon/src/pet-store.js +++ b/packages/daemon/src/pet-store.js @@ -60,6 +60,12 @@ export const makePetStoreMaker = (filePowers, locator) => { }), ); + /** @param {string} petName */ + const has = petName => { + assertValidName(petName); + return petNames.has(petName); + }; + /** @param {string} petName */ const lookup = petName => { assertValidName(petName); @@ -205,6 +211,7 @@ export const makePetStoreMaker = (filePowers, locator) => { /** @type {import('./types.js').PetStore} */ const petStore = { + has, lookup, reverseLookup, list, diff --git a/packages/daemon/src/types.d.ts b/packages/daemon/src/types.d.ts index 51d87a485b..3ce7a92ac6 100644 --- a/packages/daemon/src/types.d.ts +++ b/packages/daemon/src/types.d.ts @@ -160,6 +160,7 @@ export interface Controller { } export interface PetStore { + has(petName: string): boolean; list(): Array; write(petName: string, formulaIdentifier: string): Promise; remove(petName: string); From 57693937cd99a3e4977fef56a2c3bb06edad30f4 Mon Sep 17 00:00:00 2001 From: kumavis Date: Fri, 1 Dec 2023 09:03:25 -1000 Subject: [PATCH 151/234] feat(daemon): expose pet-store "has" method to host and guest --- packages/daemon/src/guest.js | 3 ++- packages/daemon/src/host.js | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/daemon/src/guest.js b/packages/daemon/src/guest.js index ef6da3eca5..69635a554a 100644 --- a/packages/daemon/src/guest.js +++ b/packages/daemon/src/guest.js @@ -69,10 +69,11 @@ export const makeGuestMaker = ({ terminator, }); - const { list, follow: followNames } = petStore; + const { has, list, follow: followNames } = petStore; /** @type {import('@endo/eventual-send').ERef} */ const guest = Far('EndoGuest', { + has, lookup, reverseLookup, request, diff --git a/packages/daemon/src/host.js b/packages/daemon/src/host.js index 226f3e373c..6a8e7e2fa5 100644 --- a/packages/daemon/src/host.js +++ b/packages/daemon/src/host.js @@ -417,10 +417,11 @@ export const makeHostMaker = ({ return value; }; - const { list, follow: followNames } = petStore; + const { has, list, follow: followNames } = petStore; /** @type {import('./types.js').EndoHost} */ const host = Far('EndoHost', { + has, lookup, reverseLookup, listMessages, From 6050df83414152b344195c102d0e71cf1bde1a60 Mon Sep 17 00:00:00 2001 From: kumavis Date: Fri, 1 Dec 2023 11:03:34 -1000 Subject: [PATCH 152/234] fix(daemon): pet-store removes petname when overwriting with write --- packages/daemon/src/pet-store.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/packages/daemon/src/pet-store.js b/packages/daemon/src/pet-store.js index dea68ae808..8e2da048e8 100644 --- a/packages/daemon/src/pet-store.js +++ b/packages/daemon/src/pet-store.js @@ -83,6 +83,15 @@ export const makePetStoreMaker = (filePowers, locator) => { throw new Error(`Invalid formula identifier ${q(formulaIdentifier)}`); } + if (petNames.has(petName)) { + // Perform cleanup on the overwritten pet name. + const formulaPetNames = formulaIdentifiers.get(petName); + if (formulaPetNames !== undefined) { + formulaPetNames.delete(petName); + } + changesTopic.publisher.next({ remove: petName }); + } + petNames.set(petName, formulaIdentifier); const formulaPetNames = formulaIdentifiers.get(formulaIdentifier); From 0491e07edc2b46cc498a8b8793033639530e4e76 Mon Sep 17 00:00:00 2001 From: kumavis Date: Fri, 1 Dec 2023 15:33:59 -1000 Subject: [PATCH 153/234] feat(daemon): web-page endowments include all compatible window properties --- packages/daemon/src/web-page.js | 58 +++++++++++++++++++++++++++++++-- 1 file changed, 55 insertions(+), 3 deletions(-) diff --git a/packages/daemon/src/web-page.js b/packages/daemon/src/web-page.js index 02b80c75aa..781b2db6d5 100644 --- a/packages/daemon/src/web-page.js +++ b/packages/daemon/src/web-page.js @@ -6,6 +6,51 @@ import { makeCapTP } from '@endo/captp'; import { E, Far } from '@endo/far'; import { importBundle } from '@endo/import-bundle'; +const getPrototypeChain = obj => { + const chain = []; + while (obj) { + chain.push(obj); + obj = Object.getPrototypeOf(obj); + } + return chain; +}; +const collectPropsAndBind = target => { + const container = {}; + for (const obj of getPrototypeChain(target)) { + for (const [name, propDesc] of Object.entries( + Object.getOwnPropertyDescriptors(obj), + )) { + if (name in container) { + // eslint-disable-next-line no-continue + continue; + } + let value = propDesc.value; + if (propDesc.get) { + value = propDesc.get.call(target); + } + if (typeof value === 'function') { + // This wrapper is a bind that works for constructors as well. + const wrapper = function bindWrapperFn(...args) { + if (new.target) { + // eslint-disable-next-line new-cap + return new value(...args); + } else { + return value.call(target, ...args); + } + }; + Object.defineProperties( + wrapper, + Object.getOwnPropertyDescriptors(value), + ); + container[name] = wrapper; + } else { + container[name] = value; + } + } + } + return container; +}; + const hardenedEndowments = harden({ assert, E, @@ -15,11 +60,18 @@ const hardenedEndowments = harden({ URL, }); +const globalProps = collectPropsAndBind(window); +// These properties conflict with Compartment globals. +delete globalProps.undefined; +delete globalProps.NaN; +delete globalProps.Infinity; + const endowments = Object.freeze({ ...hardenedEndowments, - window, - document, - console, + ...globalProps, + process: { + env: {}, + }, }); const url = new URL('/', `${window.location}`); From 92ad4284a26581a4af554698b49dbd0a2cbb0692 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Tue, 12 Dec 2023 18:03:50 -0800 Subject: [PATCH 154/234] refactor(daemon): Remove worker connection protocol coupling from daemon --- packages/daemon/src/daemon-node-powers.js | 57 ++++++++++++++++---- packages/daemon/src/daemon.js | 63 +++++++++-------------- packages/daemon/src/types.d.ts | 27 ++++++++-- 3 files changed, 93 insertions(+), 54 deletions(-) diff --git a/packages/daemon/src/daemon-node-powers.js b/packages/daemon/src/daemon-node-powers.js index 7182ba97e4..3fae26540b 100644 --- a/packages/daemon/src/daemon-node-powers.js +++ b/packages/daemon/src/daemon-node-powers.js @@ -4,6 +4,7 @@ import { makePromiseKit } from '@endo/promise-kit'; import { makePipe } from '@endo/stream'; import { makeNodeReader, makeNodeWriter } from '@endo/stream-node'; +import { makeNetstringCapTP } from './connection.js'; import { makeReaderRef } from './reader-ref.js'; import { makePetStoreMaker } from './pet-store.js'; import { servePrivatePortHttp } from './serve-private-port-http.js'; @@ -616,18 +617,27 @@ export const makeDaemonicControlPowers = ( ); /** - * @param {string} id + * @param {string} workerId + * @param {import('./types.js').DaemonWorkerFacet} daemonWorkerFacet * @param {Promise} cancelled */ - const makeWorker = async (id, cancelled) => { + const makeWorker = async (workerId, daemonWorkerFacet, cancelled) => { const { cachePath, statePath, ephemeralStatePath, sockPath } = locator; - const workerCachePath = filePowers.joinPath(cachePath, 'worker-id512', id); - const workerStatePath = filePowers.joinPath(statePath, 'worker-id512', id); + const workerCachePath = filePowers.joinPath( + cachePath, + 'worker-id512', + workerId, + ); + const workerStatePath = filePowers.joinPath( + statePath, + 'worker-id512', + workerId, + ); const workerEphemeralStatePath = filePowers.joinPath( ephemeralStatePath, 'worker-id512', - id, + workerId, ); await Promise.all([ @@ -642,7 +652,7 @@ export const makeDaemonicControlPowers = ( const child = popen.fork( endoWorkerPath, [ - id, + workerId, sockPath, workerStatePath, workerEphemeralStatePath, @@ -654,6 +664,7 @@ export const makeDaemonicControlPowers = ( windowsHide: true, }, ); + const workerPid = child.pid; const nodeWriter = /** @type {import('stream').Writable} */ ( child.stdio[3] ); @@ -665,8 +676,13 @@ export const makeDaemonicControlPowers = ( const reader = makeNodeReader(nodeReader); const writer = makeNodeWriter(nodeWriter); - const closed = new Promise(resolve => { - child.on('exit', () => resolve(undefined)); + const workerClosed = new Promise(resolve => { + child.on('exit', () => { + console.log( + `Endo worker exited for PID ${workerPid} with unique identifier ${workerId}`, + ); + resolve(undefined); + }); }); await filePowers.writeFileText(pidPath, `${child.pid}\n`); @@ -675,7 +691,30 @@ export const makeDaemonicControlPowers = ( child.kill(); }); - return { reader, writer, closed, pid: child.pid }; + console.log( + `Endo worker started PID ${workerPid} unique identifier ${workerId}`, + ); + + const { getBootstrap, closed: capTpClosed } = makeNetstringCapTP( + `Worker ${workerId}`, + writer, + reader, + cancelled, + daemonWorkerFacet, + ); + + capTpClosed.finally(() => { + console.log( + `Endo worker connection closed for PID ${workerPid} with unique identifier ${workerId}`, + ); + }); + + const workerTerminated = Promise.race([workerClosed, capTpClosed]); + + /** @type {import('@endo/eventual-send').ERef} */ + const workerDaemonFacet = getBootstrap(); + + return { workerTerminated, workerDaemonFacet }; }; return harden({ diff --git a/packages/daemon/src/daemon.js b/packages/daemon/src/daemon.js index 96fa30a5b7..dfe736aec1 100644 --- a/packages/daemon/src/daemon.js +++ b/packages/daemon/src/daemon.js @@ -5,7 +5,6 @@ import { E, Far } from '@endo/far'; import { makePromiseKit } from '@endo/promise-kit'; -import { makeNetstringCapTP } from './connection.js'; import { makeRefReader } from './ref-reader.js'; import { makeMailboxMaker } from './mail.js'; import { makeGuestMaker } from './guest.js'; @@ -106,48 +105,32 @@ const makeEndoBootstrap = ( // TODO validate workerId512 const workerFormulaIdentifier = `worker-id512:${workerId512}`; + const daemonWorkerFacet = makeWorkerBootstrap( + workerId512, + workerFormulaIdentifier, + ); + const { reject: cancelWorker, promise: workerCancelled } = /** @type {import('@endo/promise-kit').PromiseKit} */ ( makePromiseKit() ); cancelled.catch(async error => cancelWorker(error)); - const { - reader, - writer, - closed: workerClosed, - pid: workerPid, - } = await controlPowers.makeWorker(workerId512, workerCancelled); - - console.log( - `Endo worker started PID ${workerPid} unique identifier ${workerId512}`, - ); - - const { getBootstrap, closed: capTpClosed } = makeNetstringCapTP( - `Worker ${workerId512}`, - writer, - reader, - gracePeriodElapsed, - makeWorkerBootstrap(workerId512, workerFormulaIdentifier), - ); - - const terminated = Promise.race([workerClosed, capTpClosed]).finally(() => { - console.log( - `Endo worker stopped PID ${workerPid} with unique identifier ${workerId512}`, + const { workerTerminated, workerDaemonFacet } = + await controlPowers.makeWorker( + workerId512, + daemonWorkerFacet, + Promise.race([workerCancelled, gracePeriodElapsed]), ); - }); - - /** @type {import('@endo/eventual-send').ERef} */ - const workerBootstrap = getBootstrap(); const terminate = async () => { - E.sendOnly(workerBootstrap).terminate(); + E.sendOnly(workerDaemonFacet).terminate(); const cancelWorkerGracePeriod = () => { throw new Error('Exited gracefully before grace period elapsed'); }; const workerGracePeriodCancelled = Promise.race([ gracePeriodElapsed, - terminated, + workerTerminated, ]).then(cancelWorkerGracePeriod, cancelWorkerGracePeriod); await delay(gracePeriodMs, workerGracePeriodCancelled) .then(() => { @@ -156,7 +139,7 @@ const makeEndoBootstrap = ( ); }) .catch(cancelWorker); - await terminated; + await workerTerminated; }; terminator.onTerminate(terminate); @@ -168,7 +151,7 @@ const makeEndoBootstrap = ( return { external: worker, - internal: workerBootstrap, + internal: workerDaemonFacet, }; }; @@ -199,9 +182,9 @@ const makeEndoBootstrap = ( // eslint-disable-next-line no-use-before-define provideControllerForFormulaIdentifier(workerFormulaIdentifier) ); - const workerBootstrap = workerController.internal; + const workerDaemonFacet = workerController.internal; assert( - workerBootstrap, + workerDaemonFacet, `panic: No internal bootstrap for worker ${workerFormulaIdentifier}`, ); @@ -213,7 +196,7 @@ const makeEndoBootstrap = ( ), ); - const external = E(workerBootstrap).evaluate( + const external = E(workerDaemonFacet).evaluate( source, codeNames, endowmentValues, @@ -256,9 +239,9 @@ const makeEndoBootstrap = ( // eslint-disable-next-line no-use-before-define provideControllerForFormulaIdentifier(workerFormulaIdentifier) ); - const workerBootstrap = workerController.internal; + const workerDaemonFacet = workerController.internal; assert( - workerBootstrap, + workerDaemonFacet, `panic: No internal bootstrap for worker ${workerFormulaIdentifier}`, ); const guestP = /** @type {Promise} */ ( @@ -266,7 +249,7 @@ const makeEndoBootstrap = ( // eslint-disable-next-line no-use-before-define provideValueForFormulaIdentifier(guestFormulaIdentifier) ); - const external = E(workerBootstrap).importUnsafeAndEndow( + const external = E(workerDaemonFacet).importUnsafeAndEndow( importPath, guestP, ); @@ -296,9 +279,9 @@ const makeEndoBootstrap = ( // eslint-disable-next-line no-use-before-define provideControllerForFormulaIdentifier(workerFormulaIdentifier) ); - const workerBootstrap = workerController.internal; + const workerDaemonFacet = workerController.internal; assert( - workerBootstrap, + workerDaemonFacet, `panic: No internal bootstrap for worker ${workerFormulaIdentifier}`, ); // Behold, recursion: @@ -314,7 +297,7 @@ const makeEndoBootstrap = ( // eslint-disable-next-line no-use-before-define provideValueForFormulaIdentifier(guestFormulaIdentifier) ); - const external = E(workerBootstrap).importBundleAndEndow( + const external = E(workerDaemonFacet).importBundleAndEndow( readableBundleP, guestP, ); diff --git a/packages/daemon/src/types.d.ts b/packages/daemon/src/types.d.ts index 3ce7a92ac6..b89ab91e38 100644 --- a/packages/daemon/src/types.d.ts +++ b/packages/daemon/src/types.d.ts @@ -27,10 +27,6 @@ export type Connection = { closed: Promise; }; -export type Worker = Connection & { - pid: number | undefined; -}; - export type HttpRequest = { method: string; url: string; @@ -333,8 +329,29 @@ export type DaemonicPersistencePowers = { webPageBundlerFormula?: Formula; }; +export interface DaemonWorkerFacet {} + +export interface WorkerDaemonFacet { + terminate(): void; + evaluate( + source: string, + names: Array, + values: Array, + cancelled: Promise, + ): Promise; + importBundleAndEndow(bundle: ERef, powers: ERef); + importUnsafeAndEndow(path: string, powers: ERef); +} + export type DaemonicControlPowers = { - makeWorker: (id: string, cancelled: Promise) => Promise; + makeWorker: ( + id: string, + daemonWorkerFacet: DaemonWorkerFacet, + cancelled: Promise, + ) => Promise<{ + workerTerminated: Promise; + workerDaemonFacet: ERef; + }>; }; export type DaemonicPowers = { From 6716862fca6dee0ad685d163101f157fd66682b0 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Wed, 13 Dec 2023 15:49:34 -0800 Subject: [PATCH 155/234] fix: Settle the readable types --- packages/cli/src/cat.js | 2 +- packages/daemon/src/daemon-node-powers.js | 7 +++---- packages/daemon/src/daemon.js | 5 ++--- packages/daemon/src/reader-ref.js | 2 +- packages/daemon/src/types.d.ts | 14 ++------------ 5 files changed, 9 insertions(+), 21 deletions(-) diff --git a/packages/cli/src/cat.js b/packages/cli/src/cat.js index c56d06f01c..ac20bd1ded 100644 --- a/packages/cli/src/cat.js +++ b/packages/cli/src/cat.js @@ -8,7 +8,7 @@ import { withEndoParty } from './context.js'; export const cat = async ({ name, partyNames }) => withEndoParty(partyNames, { os, process }, async ({ party }) => { const readable = await E(party).lookup(name); - const readerRef = E(readable).stream(); + const readerRef = E(readable).streamBase64(); const reader = makeRefReader(readerRef); for await (const chunk of reader) { process.stdout.write(chunk); diff --git a/packages/daemon/src/daemon-node-powers.js b/packages/daemon/src/daemon-node-powers.js index 3fae26540b..4968bd28c3 100644 --- a/packages/daemon/src/daemon-node-powers.js +++ b/packages/daemon/src/daemon-node-powers.js @@ -504,11 +504,11 @@ export const makeDaemonicPersistencePowers = ( }, /** * @param {string} sha512 - * @returns {import('./types.js').AlmostEndoReadable} + * @returns {import('./types.js').EndoReadable} */ fetch(sha512) { const storagePath = filePowers.joinPath(storageDirectoryPath, sha512); - const stream = () => { + const streamBase64 = () => { const reader = filePowers.makeFileReader(storagePath); return makeReaderRef(reader); }; @@ -520,10 +520,9 @@ export const makeDaemonicPersistencePowers = ( }; return harden({ sha512: () => sha512, - stream, + streamBase64, text, json, - [Symbol.asyncIterator]: () => stream(), }); }, }); diff --git a/packages/daemon/src/daemon.js b/packages/daemon/src/daemon.js index dfe736aec1..f087ceaaf4 100644 --- a/packages/daemon/src/daemon.js +++ b/packages/daemon/src/daemon.js @@ -70,13 +70,12 @@ const makeEndoBootstrap = ( * @param {string} sha512 */ const makeReadableBlob = sha512 => { - const { text, json, stream } = contentStore.fetch(sha512); + const { text, json, streamBase64 } = contentStore.fetch(sha512); return Far(`Readable file with SHA-512 ${sha512.slice(0, 8)}...`, { sha512: () => sha512, - stream, + streamBase64, text, json, - [Symbol.asyncIterator]: stream, }); }; diff --git a/packages/daemon/src/reader-ref.js b/packages/daemon/src/reader-ref.js index 2b3ef8dc87..d18b4215c4 100644 --- a/packages/daemon/src/reader-ref.js +++ b/packages/daemon/src/reader-ref.js @@ -27,7 +27,7 @@ export const asyncIterate = iterable => { /** * @template T * @param {import('./types').SomehowAsyncIterable} iterable The iterable object. - * @returns {import('@endo/far').FarRef>} + * @returns {import('@endo/far').FarRef>} */ export const makeIteratorRef = iterable => { const iterator = asyncIterate(iterable); diff --git a/packages/daemon/src/types.d.ts b/packages/daemon/src/types.d.ts index b89ab91e38..9d5314fc64 100644 --- a/packages/daemon/src/types.d.ts +++ b/packages/daemon/src/types.d.ts @@ -182,10 +182,9 @@ export type ReceiveFn = ( export interface EndoReadable { sha512(): string; - stream(): FarRef>; + streamBase64(): FarRef>; text(): Promise; json(): Promise; - [Symbol.asyncIterator]: Reader; } export interface EndoWorker { @@ -305,20 +304,11 @@ export type NetworkPowers = { ) => { started: Promise; stopped: Promise }; }; -// The return type here is almost an EndoReadable, but not quite. Should fix. -export type AlmostEndoReadable = { - sha512(): string; - stream(): FarRef>; - text(): Promise; - json(): Promise; - [Symbol.asyncIterator]: any; -}; - export type DaemonicPersistencePowers = { initializePersistence: () => Promise; makeContentSha512Store: () => { store: (readable: AsyncIterable) => Promise; - fetch: (sha512: string) => AlmostEndoReadable; + fetch: (sha512: string) => EndoReadable; }; readFormula: (prefix: string, formulaNumber: string) => Promise; writeFormula: ( From 512a64538bdeccc3bd75f5ac6c4b2034168cfd89 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Thu, 14 Dec 2023 17:09:39 -0800 Subject: [PATCH 156/234] feat(cli): Extract install from open command --- packages/cli/src/endo.js | 27 +++++++++++++--- packages/cli/src/install.js | 64 +++++++++++++++++++++++++++++++++++++ packages/cli/src/open.js | 52 ++---------------------------- 3 files changed, 88 insertions(+), 55 deletions(-) create mode 100644 packages/cli/src/install.js diff --git a/packages/cli/src/endo.js b/packages/cli/src/endo.js index 5391a89f07..e859f4fba0 100644 --- a/packages/cli/src/endo.js +++ b/packages/cli/src/endo.js @@ -29,15 +29,15 @@ export const main = async rawArgs => { program.name('endo').version(packageDescriptor.version); program - .command('open [filePath]') - .description('opens a web page (weblet)') + .command('install [filePath]') + .description('installs a web page (weblet)') .option( '-a,--as ', 'Pose as named party (as named by current party)', collect, [], ) - .option('-b,--bundle ', 'Bundle for a web page to open') + .option('-b,--bundle ', 'Bundle for a web page (weblet)') .option( '-p,--powers ', 'Endowment to give the weblet (a name, NONE, HOST, or ENDO)', @@ -48,8 +48,8 @@ export const main = async rawArgs => { powers: powersName = 'NONE', as: partyNames, } = cmd.opts(); - const { open } = await import('./open.js'); - return open({ + const { install } = await import('./install.js'); + return install({ webPageName, programPath, bundleName, @@ -58,6 +58,23 @@ export const main = async rawArgs => { }); }); + program + .command('open ') + .description('opens a web page (weblet)') + .option( + '-a,--as ', + 'Pose as named party (as named by current party)', + collect, + [], + ) + .action(async (webPageName, cmd) => { + const { as: partyNames } = cmd.opts(); + const { open } = await import('./open.js'); + return open({ + webPageName, + partyNames, + }); + }); program .command('run [] [...]') .description('runs a program (runlet)') diff --git a/packages/cli/src/install.js b/packages/cli/src/install.js new file mode 100644 index 0000000000..428a7aaed4 --- /dev/null +++ b/packages/cli/src/install.js @@ -0,0 +1,64 @@ +/* global process */ + +import os from 'os'; + +import { E } from '@endo/far'; +import { makeReaderRef } from '@endo/daemon'; +import bundleSource from '@endo/bundle-source'; + +import { withEndoParty } from './context.js'; +import { randomHex16 } from './random.js'; + +const textEncoder = new TextEncoder(); + +export const install = async ({ + bundleName, + partyNames, + powersName, + webPageName, + programPath, +}) => { + /** @type {import('@endo/eventual-send').ERef> | undefined} */ + let bundleReaderRef; + /** @type {string | undefined} */ + let temporaryBundleName; + if (programPath !== undefined) { + if (bundleName === undefined) { + // TODO alternately, make a temporary session-scoped GC pet store + // overshadowing the permanent one, which gets implicitly dropped + // when this CLI CapTP session ends. + temporaryBundleName = `tmp-bundle-${await randomHex16()}`; + bundleName = temporaryBundleName; + } + const bundle = await bundleSource(programPath); + const bundleText = JSON.stringify(bundle); + const bundleBytes = textEncoder.encode(bundleText); + bundleReaderRef = makeReaderRef([bundleBytes]); + } + + await withEndoParty(partyNames, { os, process }, async ({ party }) => { + // Prepare a bundle, with the given name. + if (bundleReaderRef !== undefined) { + await E(party).store(bundleReaderRef, bundleName); + } + + try { + /** @type {string | undefined} */ + let webPageUrl; + if (bundleName !== undefined) { + ({ url: webPageUrl } = await E(party).provideWebPage( + webPageName, + bundleName, + powersName, + )); + } else { + ({ url: webPageUrl } = await E(party).lookup(webPageName)); + } + process.stdout.write(`${webPageUrl}\n`); + } finally { + if (temporaryBundleName) { + await E(party).remove(temporaryBundleName); + } + } + }); +}; diff --git a/packages/cli/src/open.js b/packages/cli/src/open.js index 1be31edb9d..b7bd57c1d1 100644 --- a/packages/cli/src/open.js +++ b/packages/cli/src/open.js @@ -4,61 +4,13 @@ import os from 'os'; import openWebPage from 'open'; import { E } from '@endo/far'; -import { makeReaderRef } from '@endo/daemon'; -import bundleSource from '@endo/bundle-source'; import { withEndoParty } from './context.js'; -import { randomHex16 } from './random.js'; - -const textEncoder = new TextEncoder(); - -export const open = async ({ - bundleName, - partyNames, - powersName, - webPageName, - programPath, -}) => { - /** @type {import('@endo/eventual-send').ERef> | undefined} */ - let bundleReaderRef; - /** @type {string | undefined} */ - let temporaryBundleName; - if (programPath !== undefined) { - if (bundleName === undefined) { - // TODO alternately, make a temporary session-scoped GC pet store - // overshadowing the permanent one, which gets implicitly dropped - // when this CLI CapTP session ends. - temporaryBundleName = `tmp-bundle-${await randomHex16()}`; - bundleName = temporaryBundleName; - } - const bundle = await bundleSource(programPath); - const bundleText = JSON.stringify(bundle); - const bundleBytes = textEncoder.encode(bundleText); - bundleReaderRef = makeReaderRef([bundleBytes]); - } +export const open = async ({ webPageName, partyNames }) => { await withEndoParty(partyNames, { os, process }, async ({ party }) => { - // Prepare a bundle, with the given name. - if (bundleReaderRef !== undefined) { - await E(party).store(bundleReaderRef, bundleName); - } - - /** @type {string | undefined} */ - let webPageUrl; - if (bundleName !== undefined) { - ({ url: webPageUrl } = await E(party).provideWebPage( - webPageName, - bundleName, - powersName, - )); - } else { - ({ url: webPageUrl } = await E(party).lookup(webPageName)); - } + const { url: webPageUrl } = await E(party).lookup(webPageName); process.stdout.write(`${webPageUrl}\n`); openWebPage(webPageUrl); - - if (temporaryBundleName) { - await E(party).remove(temporaryBundleName); - } }); }; From 3f625c18e157a4e6b89ebbaf22f85a81635fbab7 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Thu, 28 Dec 2023 14:24:15 -0800 Subject: [PATCH 157/234] fix(daemon): makeIdentifiedHost wrapped controller bug --- packages/daemon/src/daemon.js | 3 +-- packages/daemon/test/test-endo.js | 24 ++++++++++++++++++++++++ 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/packages/daemon/src/daemon.js b/packages/daemon/src/daemon.js index f087ceaaf4..918725b775 100644 --- a/packages/daemon/src/daemon.js +++ b/packages/daemon/src/daemon.js @@ -445,13 +445,12 @@ const makeEndoBootstrap = ( const workerFormulaIdentifier = `worker-id512:${formulaNumber}`; // Behold, recursion: // eslint-disable-next-line no-use-before-define - const external = makeIdentifiedHost( + return makeIdentifiedHost( formulaIdentifier, storeFormulaIdentifier, workerFormulaIdentifier, terminator, ); - return { external, internal: undefined }; } else if ( [ 'eval-id512', diff --git a/packages/daemon/test/test-endo.js b/packages/daemon/test/test-endo.js index 6071600330..8ec36ac0aa 100644 --- a/packages/daemon/test/test-endo.js +++ b/packages/daemon/test/test-endo.js @@ -670,3 +670,27 @@ test('terminate because of requested capability', async t => { ), ); }); + +test('make a host', async t => { + const { promise: cancelled, reject: cancel } = makePromiseKit(); + t.teardown(() => cancel(Error('teardown'))); + const locator = makeLocator('tmp', 'make-host'); + + await stop(locator).catch(() => {}); + await reset(locator); + await start(locator); + + const { getBootstrap } = await makeEndoClient( + 'client', + locator.sockPath, + cancelled, + ); + const bootstrap = getBootstrap(); + const host = E(bootstrap).host(); + const host2 = E(host).provideHost('fellow-host'); + await E(host2).makeWorker('w1'); + const ten = await E(host2).evaluate('w1', '10', [], []); + t.is(ten, 10); + + await stop(locator); +}); From 54d30eefc6add6731bcc276148c5760e078cf781 Mon Sep 17 00:00:00 2001 From: Erik Marks Date: Sun, 21 Jan 2024 18:12:25 -0800 Subject: [PATCH 158/234] refactor(cli): Rename `reset` command to `purge` Renames the CLI `reset` command to `purge` in order to better distinguish it from `restart`. Also adds a confirmation prompt to the command, which can be overridden with the `-f`/`--force` flag. --- packages/cli/src/endo.js | 18 ++++++++++++++---- packages/cli/src/prompt.js | 23 +++++++++++++++++++++++ 2 files changed, 37 insertions(+), 4 deletions(-) create mode 100644 packages/cli/src/prompt.js diff --git a/packages/cli/src/endo.js b/packages/cli/src/endo.js index e859f4fba0..fba98b87b4 100644 --- a/packages/cli/src/endo.js +++ b/packages/cli/src/endo.js @@ -10,6 +10,7 @@ import fs from 'fs'; import url from 'url'; import { Command } from 'commander'; +import { prompt } from './prompt.js'; const collect = (value, values) => values.concat([value]); @@ -590,11 +591,20 @@ export const main = async rawArgs => { }); program - .command('reset') + .command('purge') + .option('-f, --force', 'skip the confirmation prompt') .description('erases persistent state and restarts if running') - .action(async _cmd => { - const { reset } = await import('@endo/daemon'); - await reset(); + .action(async cmd => { + const { force } = cmd.opts(); + const doPurge = + force || + /^y(es)?$/u.test( + await prompt('Are you sure you want to erase all state? (y/n)'), + ); + if (doPurge) { + const { reset } = await import('@endo/daemon'); + await reset(); + } }); program diff --git a/packages/cli/src/prompt.js b/packages/cli/src/prompt.js new file mode 100644 index 0000000000..8f339db063 --- /dev/null +++ b/packages/cli/src/prompt.js @@ -0,0 +1,23 @@ +import { stdin, stdout } from 'process'; +import readline from 'readline'; + +/** + * Prompts the user for input. The answer is trimmed and converted to + * lowercase. + * + * @param {string} question - The question to ask the user. + * @returns {Promise} The user's answer. + */ +export async function prompt(question) { + const rl = readline.createInterface({ + input: stdin, + output: stdout, + }); + + return new Promise(resolve => { + rl.question(`${question}\n`, answer => { + rl.close(); + resolve(answer.trim().toLowerCase()); + }); + }); +} From 7bf79dbed8fbccb345aaf06b44cf87bd2b84e285 Mon Sep 17 00:00:00 2001 From: Erik Marks <25517051+rekmarks@users.noreply.github.com> Date: Mon, 22 Jan 2024 10:44:19 -0800 Subject: [PATCH 159/234] Update `purge` confirmation message --- packages/cli/src/endo.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/cli/src/endo.js b/packages/cli/src/endo.js index fba98b87b4..c400d785f7 100644 --- a/packages/cli/src/endo.js +++ b/packages/cli/src/endo.js @@ -599,7 +599,9 @@ export const main = async rawArgs => { const doPurge = force || /^y(es)?$/u.test( - await prompt('Are you sure you want to erase all state? (y/n)'), + await prompt( + 'Are you sure you want to erase all state? This irreversible action will permanently sever all peer connections. Continue? (y/n)', + ), ); if (doPurge) { const { reset } = await import('@endo/daemon'); From dfa60718d97f311c6fd05c087cc55af5a52abe82 Mon Sep 17 00:00:00 2001 From: kumavis Date: Mon, 22 Jan 2024 16:07:20 -1000 Subject: [PATCH 160/234] refactor(daemon): use parseFormulaIdentifier utility --- packages/daemon/src/daemon.js | 90 +++++++++++------------ packages/daemon/src/formula-identifier.js | 29 ++++++++ packages/daemon/src/types.d.ts | 5 ++ 3 files changed, 76 insertions(+), 48 deletions(-) create mode 100644 packages/daemon/src/formula-identifier.js diff --git a/packages/daemon/src/daemon.js b/packages/daemon/src/daemon.js index 918725b775..7c03636556 100644 --- a/packages/daemon/src/daemon.js +++ b/packages/daemon/src/daemon.js @@ -11,6 +11,7 @@ import { makeGuestMaker } from './guest.js'; import { makeHostMaker } from './host.js'; import { assertPetName } from './pet-name.js'; import { makeTerminatorMaker } from './terminator.js'; +import { parseFormulaIdentifier } from './formula-identifier.js'; const { quote: q } = assert; @@ -383,64 +384,57 @@ const makeEndoBootstrap = ( formulaIdentifier, terminator, ) => { - const delimiterIndex = formulaIdentifier.indexOf(':'); - if (delimiterIndex < 0) { - if (formulaIdentifier === 'pet-store') { - const external = petStorePowers.makeOwnPetStore( - 'pet-store', - assertPetName, - ); - return { external, internal: undefined }; - } else if (formulaIdentifier === 'host') { - const storeFormulaIdentifier = 'pet-store'; - const workerFormulaIdentifier = `worker-id512:${zero512}`; - // Behold, recursion: - // eslint-disable-next-line no-use-before-define - return makeIdentifiedHost( - formulaIdentifier, - storeFormulaIdentifier, - workerFormulaIdentifier, - terminator, - ); - } else if (formulaIdentifier === 'endo') { - // TODO reframe "cancelled" as termination of the "endo" object and - // ensure that all values ultimately depend on "endo". - // Behold, self-referentiality: - // eslint-disable-next-line no-use-before-define - return { external: endoBootstrap, internal: undefined }; - } else if (formulaIdentifier === 'least-authority') { - return { external: leastAuthority, internal: undefined }; - } else if (formulaIdentifier === 'web-page-js') { - if (persistencePowers.webPageBundlerFormula === undefined) { - throw Error('No web-page-js formula provided.'); - } - return makeControllerForFormula( - 'web-page-js', - zero512, - persistencePowers.webPageBundlerFormula, - terminator, - ); + const { type: formulaType, number: formulaNumber } = + parseFormulaIdentifier(formulaIdentifier); + if (formulaIdentifier === 'pet-store') { + const external = petStorePowers.makeOwnPetStore( + 'pet-store', + assertPetName, + ); + return { external, internal: undefined }; + } else if (formulaIdentifier === 'host') { + const storeFormulaIdentifier = 'pet-store'; + const workerFormulaIdentifier = `worker-id512:${zero512}`; + // Behold, recursion: + // eslint-disable-next-line no-use-before-define + return makeIdentifiedHost( + formulaIdentifier, + storeFormulaIdentifier, + workerFormulaIdentifier, + terminator, + ); + } else if (formulaIdentifier === 'endo') { + // TODO reframe "cancelled" as termination of the "endo" object and + // ensure that all values ultimately depend on "endo". + // Behold, self-referentiality: + // eslint-disable-next-line no-use-before-define + return { external: endoBootstrap, internal: undefined }; + } else if (formulaIdentifier === 'least-authority') { + return { external: leastAuthority, internal: undefined }; + } else if (formulaIdentifier === 'web-page-js') { + if (persistencePowers.webPageBundlerFormula === undefined) { + throw Error('No web-page-js formula provided.'); } - throw new TypeError( - `Formula identifier must have a colon: ${q(formulaIdentifier)}`, + return makeControllerForFormula( + 'web-page-js', + zero512, + persistencePowers.webPageBundlerFormula, + terminator, ); - } - const prefix = formulaIdentifier.slice(0, delimiterIndex); - const formulaNumber = formulaIdentifier.slice(delimiterIndex + 1); - if (prefix === 'readable-blob-sha512') { + } else if (formulaType === 'readable-blob-sha512') { // Behold, forward-reference: // eslint-disable-next-line no-use-before-define const external = makeReadableBlob(formulaNumber); return { external, internal: undefined }; - } else if (prefix === 'worker-id512') { + } else if (formulaType === 'worker-id512') { return makeIdentifiedWorkerController(formulaNumber, terminator); - } else if (prefix === 'pet-store-id512') { + } else if (formulaType === 'pet-store-id512') { const external = petStorePowers.makeIdentifiedPetStore( formulaNumber, assertPetName, ); return { external, internal: undefined }; - } else if (prefix === 'host-id512') { + } else if (formulaType === 'host-id512') { const storeFormulaIdentifier = `pet-store-id512:${formulaNumber}`; const workerFormulaIdentifier = `worker-id512:${formulaNumber}`; // Behold, recursion: @@ -458,10 +452,10 @@ const makeEndoBootstrap = ( 'import-bundle-id512', 'guest-id512', 'web-bundle', - ].includes(prefix) + ].includes(formulaType) ) { const formula = await persistencePowers.readFormula( - prefix, + formulaType, formulaNumber, ); // TODO validate diff --git a/packages/daemon/src/formula-identifier.js b/packages/daemon/src/formula-identifier.js new file mode 100644 index 0000000000..8e301a0a50 --- /dev/null +++ b/packages/daemon/src/formula-identifier.js @@ -0,0 +1,29 @@ +const { quote: q } = assert; + +const numberlessFormulasIdentifiers = new Set([ + 'pet-store', + 'host', + 'endo', + 'least-authority', + 'web-page-js', +]); + +/** + * @param {string} formulaIdentifier + * @returns {import("./types").FormulaIdentifierRecord} + */ +export const parseFormulaIdentifier = formulaIdentifier => { + const delimiterIndex = formulaIdentifier.indexOf(':'); + if (delimiterIndex < 0) { + if (numberlessFormulasIdentifiers.has(formulaIdentifier)) { + return { type: formulaIdentifier, number: '' }; + } else { + throw new TypeError( + `Formula identifier must have a colon: ${q(formulaIdentifier)}`, + ); + } + } + const type = formulaIdentifier.slice(0, delimiterIndex); + const number = formulaIdentifier.slice(delimiterIndex + 1); + return { type, number }; +}; diff --git a/packages/daemon/src/types.d.ts b/packages/daemon/src/types.d.ts index 9d5314fc64..d62eecc2d9 100644 --- a/packages/daemon/src/types.d.ts +++ b/packages/daemon/src/types.d.ts @@ -53,6 +53,11 @@ export type MignonicPowers = { }; }; +type FormulaIdentifierRecord = { + type: string; + number: string; +}; + type GuestFormula = { type: 'guest'; host: string; From 025d7541566235552c7d89c96af3a0c8aaab7910 Mon Sep 17 00:00:00 2001 From: kumavis Date: Mon, 22 Jan 2024 16:14:13 -1000 Subject: [PATCH 161/234] feat(daemon): petstore follow includes value + add listEntries --- packages/daemon/src/pet-store.js | 43 +++++++++++++++++++++++++++++--- packages/daemon/src/types.d.ts | 10 +++++++- 2 files changed, 49 insertions(+), 4 deletions(-) diff --git a/packages/daemon/src/pet-store.js b/packages/daemon/src/pet-store.js index 8e2da048e8..509d8b3354 100644 --- a/packages/daemon/src/pet-store.js +++ b/packages/daemon/src/pet-store.js @@ -3,6 +3,7 @@ import { Far } from '@endo/far'; import { makeChangeTopic } from './pubsub.js'; import { makeIteratorRef } from './reader-ref.js'; +import { parseFormulaIdentifier } from './formula-identifier.js'; const { quote: q } = assert; @@ -104,22 +105,56 @@ export const makePetStoreMaker = (filePowers, locator) => { const petNamePath = filePowers.joinPath(petNameDirectoryPath, petName); const petNameText = `${formulaIdentifier}\n`; await filePowers.writeFileText(petNamePath, petNameText); - changesTopic.publisher.next({ add: petName }); + const formularIdentifierRecord = + parseFormulaIdentifier(formulaIdentifier); + changesTopic.publisher.next({ + add: petName, + value: formularIdentifierRecord, + }); }; - const list = () => harden([...petNames.keys()].sort()); + /** + * @param {string} petName + * @returns {import('./types.js').FormulaIdentifierRecord} + */ + const formulaIdentifierRecordForName = petName => { + const formulaIdentifier = petNames.get(petName); + if (formulaIdentifier === undefined) { + throw new Error(`Formula does not exist for pet name ${q(petName)}`); + } + return parseFormulaIdentifier(formulaIdentifier); + }; + // Returns in an Array format. + const list = () => harden([...petNames.keys()].sort()); + // Returns in an object operations format ({ add, value } or { remove }). const follow = async () => makeIteratorRef( (async function* currentAndSubsequentNames() { const changes = changesTopic.subscribe(); for (const name of [...petNames.keys()].sort()) { - yield { add: name }; + const formularIdentifierRecord = + formulaIdentifierRecordForName(name); + yield { + add: name, + value: formularIdentifierRecord, + }; } yield* changes; })(), ); + // Returns in Object.fromEntries format. + /** @returns {Array<[string, import('./types.js').FormulaIdentifierRecord]>} */ + const listEntries = () => + harden( + [...petNames.keys()].sort().map(name => { + return [name, formulaIdentifierRecordForName(name)]; + }), + ); + // Provided as an alias for follow, with naming symmetry to listEntries. + const followEntries = follow; + /** * @param {string} petName */ @@ -225,6 +260,8 @@ export const makePetStoreMaker = (filePowers, locator) => { reverseLookup, list, follow, + listEntries, + followEntries, write, remove, rename, diff --git a/packages/daemon/src/types.d.ts b/packages/daemon/src/types.d.ts index d62eecc2d9..91edff7379 100644 --- a/packages/daemon/src/types.d.ts +++ b/packages/daemon/src/types.d.ts @@ -163,12 +163,20 @@ export interface Controller { export interface PetStore { has(petName: string): boolean; list(): Array; + follow(): Promise>>; + listEntries(): Array<[string, FormulaIdentifierRecord]>; + followEntries(): Promise< + FarRef< + String< + { add: string; value: FormulaIdentifierRecord } | { remove: string } + > + > + >; write(petName: string, formulaIdentifier: string): Promise; remove(petName: string); rename(fromPetName: string, toPetName: string); lookup(petName: string): string | undefined; reverseLookup(formulaIdentifier: string): Array; - follow(): Promise>>; } export type RequestFn = ( From 2ce09385719ca3b7ecb4949de120623111dda07a Mon Sep 17 00:00:00 2001 From: kumavis Date: Mon, 22 Jan 2024 16:15:04 -1000 Subject: [PATCH 162/234] feat(daemon): expose listEntries and followEntries on host and guest --- packages/daemon/src/guest.js | 12 ++++++++++-- packages/daemon/src/host.js | 10 +++++++++- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/packages/daemon/src/guest.js b/packages/daemon/src/guest.js index 69635a554a..3ec17b4804 100644 --- a/packages/daemon/src/guest.js +++ b/packages/daemon/src/guest.js @@ -69,7 +69,13 @@ export const makeGuestMaker = ({ terminator, }); - const { has, list, follow: followNames } = petStore; + const { + has, + list, + follow: followNames, + listEntries, + followEntries, + } = petStore; /** @type {import('@endo/eventual-send').ERef} */ const guest = Far('EndoGuest', { @@ -80,8 +86,10 @@ export const makeGuestMaker = ({ send, list, followNames, - followMessages, listMessages, + followMessages, + listEntries, + followEntries, resolve, reject, dismiss, diff --git a/packages/daemon/src/host.js b/packages/daemon/src/host.js index 6a8e7e2fa5..0b72dfbf1a 100644 --- a/packages/daemon/src/host.js +++ b/packages/daemon/src/host.js @@ -417,7 +417,13 @@ export const makeHostMaker = ({ return value; }; - const { has, list, follow: followNames } = petStore; + const { + has, + list, + follow: followNames, + listEntries, + followEntries, + } = petStore; /** @type {import('./types.js').EndoHost} */ const host = Far('EndoHost', { @@ -434,6 +440,8 @@ export const makeHostMaker = ({ send, list, followNames, + listEntries, + followEntries, remove, rename, store, From d07b7dfa5513391e8716bdcb658ec9aa2a660fd6 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Tue, 23 Jan 2024 15:05:40 -0800 Subject: [PATCH 163/234] fix: Resolve remaining merge issues --- packages/daemon/src/daemon-node.js | 2 +- packages/daemon/src/pet-store.js | 4 ++-- packages/daemon/src/types.d.ts | 2 +- packages/daemon/types.d.ts | 15 +++++++-------- 4 files changed, 11 insertions(+), 12 deletions(-) diff --git a/packages/daemon/src/daemon-node.js b/packages/daemon/src/daemon-node.js index 1915b544d3..e741c8f596 100644 --- a/packages/daemon/src/daemon-node.js +++ b/packages/daemon/src/daemon-node.js @@ -14,7 +14,7 @@ import path from 'path'; import popen from 'child_process'; import url from 'url'; import http from 'http'; -import * as ws from 'ws'; +import ws from 'ws'; import { makePromiseKit } from '@endo/promise-kit'; import { makeDaemon } from './daemon.js'; diff --git a/packages/daemon/src/pet-store.js b/packages/daemon/src/pet-store.js index 8e2da048e8..b15759ffdd 100644 --- a/packages/daemon/src/pet-store.js +++ b/packages/daemon/src/pet-store.js @@ -25,7 +25,7 @@ export const makePetStoreMaker = (filePowers, locator) => { const petNames = new Map(); /** @type {Map>} */ const formulaIdentifiers = new Map(); - /** @type {import('./types.js').Topic} */ + /** @type {import('./types.js').Topic<{ add: string } | { remove: string }>} */ const changesTopic = makeChangeTopic(); /** @param {string} petName */ @@ -114,7 +114,7 @@ export const makePetStoreMaker = (filePowers, locator) => { (async function* currentAndSubsequentNames() { const changes = changesTopic.subscribe(); for (const name of [...petNames.keys()].sort()) { - yield { add: name }; + yield /** @type {{ add: string }} */({ add: name }); } yield* changes; })(), diff --git a/packages/daemon/src/types.d.ts b/packages/daemon/src/types.d.ts index 9d5314fc64..ae64f980f0 100644 --- a/packages/daemon/src/types.d.ts +++ b/packages/daemon/src/types.d.ts @@ -163,7 +163,7 @@ export interface PetStore { rename(fromPetName: string, toPetName: string); lookup(petName: string): string | undefined; reverseLookup(formulaIdentifier: string): Array; - follow(): Promise>>; + follow(): Promise>>; } export type RequestFn = ( diff --git a/packages/daemon/types.d.ts b/packages/daemon/types.d.ts index d01e1da2d0..70d42a37ee 100644 --- a/packages/daemon/types.d.ts +++ b/packages/daemon/types.d.ts @@ -1,4 +1,3 @@ -export { makeEndoClient } from './src/client.js'; export { makeRefReader, makeRefIterator } from './src/ref-reader.js'; export { makeReaderRef, makeIteratorRef } from './src/reader-ref.js'; @@ -10,13 +9,13 @@ export type Locator = { sockPath: string; }; -export async function start(locator?: Locator); -export async function stop(locator?: Locator); -export async function restart(locator?: Locator); -export async function terminate(locator?: Locator); -export async function clean(locator?: Locator); -export async function reset(locator?: Locator); -export async function makeEndoClient( +export function start(locator?: Locator); +export function stop(locator?: Locator); +export function restart(locator?: Locator); +export function terminate(locator?: Locator); +export function clean(locator?: Locator); +export function reset(locator?: Locator); +export function makeEndoClient( name: string, sockPath: string, cancelled: Promise, From 4ece3f828e833d57b57e6ff587e2cb6d5435b095 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Tue, 23 Jan 2024 16:38:08 -0800 Subject: [PATCH 164/234] fix(cli): Appease TypeScript --- packages/cli/src/run.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cli/src/run.js b/packages/cli/src/run.js index 8a0f1641a0..c1bcd906b9 100644 --- a/packages/cli/src/run.js +++ b/packages/cli/src/run.js @@ -59,7 +59,7 @@ export const run = async ({ args.unshift(filePath); } - const importUrl = url.pathToFileURL(importPath); + const importUrl = url.pathToFileURL(importPath).href; const namespace = await import(importUrl); const result = await namespace.main(powersP, ...args); if (result !== undefined) { From bd346f39f8ebeb7f7a74aef69acc258f4fa37e2f Mon Sep 17 00:00:00 2001 From: Erik Marks Date: Tue, 23 Jan 2024 17:19:06 -0800 Subject: [PATCH 165/234] chore(daemon): Remove unused valueFormulaIdentifier parameter --- packages/daemon/src/daemon.js | 21 +++------------------ 1 file changed, 3 insertions(+), 18 deletions(-) diff --git a/packages/daemon/src/daemon.js b/packages/daemon/src/daemon.js index 7c03636556..c0ec3d4e8b 100644 --- a/packages/daemon/src/daemon.js +++ b/packages/daemon/src/daemon.js @@ -90,10 +90,9 @@ const makeEndoBootstrap = ( /** * @param {string} workerId512 - * @param {string} workerFormulaIdentifier */ - const makeWorkerBootstrap = async (workerId512, workerFormulaIdentifier) => { - // TODO validate workerId512, workerFormulaIdentifier + const makeWorkerBootstrap = async workerId512 => { + // TODO validate workerId512 return Far(`Endo for worker ${workerId512}`, {}); }; @@ -103,12 +102,7 @@ const makeEndoBootstrap = ( */ const makeIdentifiedWorkerController = async (workerId512, terminator) => { // TODO validate workerId512 - const workerFormulaIdentifier = `worker-id512:${workerId512}`; - - const daemonWorkerFacet = makeWorkerBootstrap( - workerId512, - workerFormulaIdentifier, - ); + const daemonWorkerFacet = makeWorkerBootstrap(workerId512); const { reject: cancelWorker, promise: workerCancelled } = /** @type {import('@endo/promise-kit').PromiseKit} */ ( @@ -156,7 +150,6 @@ const makeEndoBootstrap = ( }; /** - * @param {string} evalFormulaIdentifier * @param {string} workerFormulaIdentifier * @param {string} source * @param {Array} codeNames @@ -164,7 +157,6 @@ const makeEndoBootstrap = ( * @param {import('./types.js').Terminator} terminator */ const makeControllerForEval = async ( - evalFormulaIdentifier, workerFormulaIdentifier, source, codeNames, @@ -217,14 +209,12 @@ const makeEndoBootstrap = ( }; /** - * @param {string} valueFormulaIdentifier * @param {string} workerFormulaIdentifier * @param {string} guestFormulaIdentifier * @param {string} importPath * @param {import('./types.js').Terminator} terminator */ const makeControllerForUnsafePlugin = async ( - valueFormulaIdentifier, workerFormulaIdentifier, guestFormulaIdentifier, importPath, @@ -257,14 +247,12 @@ const makeEndoBootstrap = ( }; /** - * @param {string} valueFormulaIdentifier * @param {string} workerFormulaIdentifier * @param {string} guestFormulaIdentifier * @param {string} bundleFormulaIdentifier * @param {import('./types.js').Terminator} terminator */ const makeControllerForSafeBundle = async ( - valueFormulaIdentifier, workerFormulaIdentifier, guestFormulaIdentifier, bundleFormulaIdentifier, @@ -318,7 +306,6 @@ const makeEndoBootstrap = ( ) => { if (formula.type === 'eval') { return makeControllerForEval( - formulaIdentifier, formula.worker, formula.source, formula.names, @@ -327,7 +314,6 @@ const makeEndoBootstrap = ( ); } else if (formula.type === 'import-unsafe') { return makeControllerForUnsafePlugin( - formulaIdentifier, formula.worker, formula.powers, formula.importPath, @@ -335,7 +321,6 @@ const makeEndoBootstrap = ( ); } else if (formula.type === 'import-bundle') { return makeControllerForSafeBundle( - formulaIdentifier, formula.worker, formula.powers, formula.bundle, From 5cf3de96cd2e87a19c94eb48e0bab68ec41956ce Mon Sep 17 00:00:00 2001 From: Erik Marks Date: Wed, 24 Jan 2024 20:20:20 -0800 Subject: [PATCH 166/234] chore(daemon): Remove unused getDaemonBootstrap parameter --- packages/daemon/src/worker.js | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/packages/daemon/src/worker.js b/packages/daemon/src/worker.js index 15a98f385e..b6de6cbc6b 100644 --- a/packages/daemon/src/worker.js +++ b/packages/daemon/src/worker.js @@ -20,15 +20,10 @@ const endowments = harden({ /** * @param {object} args - * @param {() => any} args.getDaemonBootstrap * @param {(error: Error) => void} args.cancel * @param {(path: string) => string} args.pathToFileURL */ -export const makeWorkerFacet = ({ - getDaemonBootstrap, - pathToFileURL, - cancel, -}) => { +export const makeWorkerFacet = ({ pathToFileURL, cancel }) => { return Far('EndoWorkerFacet', { terminate: async () => { console.error('Endo worker received terminate request'); @@ -102,14 +97,11 @@ export const main = async (powers, locator, uuid, pid, cancel, cancelled) => { const { reader, writer } = powers.connection; const workerFacet = makeWorkerFacet({ - // Behold: reference cycle - // eslint-disable-next-line no-use-before-define - getDaemonBootstrap: () => getBootstrap(), pathToFileURL, cancel, }); - const { closed, getBootstrap } = makeNetstringCapTP( + const { closed } = makeNetstringCapTP( 'Endo', writer, reader, From 3eaba3818af7d9acdb1fbdb2cb353b18b8661ec4 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Tue, 23 Jan 2024 17:06:22 -0800 Subject: [PATCH 167/234] fix: Appease lint harder --- packages/cli/src/client.js | 18 +++++++++++++++--- packages/cli/src/install.js | 2 +- packages/cli/src/make.js | 2 +- packages/daemon/src/client.js | 2 +- packages/daemon/src/daemon-node.js | 1 + packages/daemon/src/reader-ref.js | 4 ++-- packages/daemon/src/ref-reader.js | 2 +- packages/daemon/src/worker-node.js | 1 + 8 files changed, 23 insertions(+), 9 deletions(-) diff --git a/packages/cli/src/client.js b/packages/cli/src/client.js index 355f414b75..2846913eec 100644 --- a/packages/cli/src/client.js +++ b/packages/cli/src/client.js @@ -1,9 +1,21 @@ import { start, makeEndoClient } from '@endo/daemon'; -export const provideEndoClient = async (...args) => { +/** + * @template TBootstrap + * @param {string} name + * @param {string} sockPath + * @param {Promise} cancelled + * @param {TBootstrap} [bootstrap] + */ +export const provideEndoClient = async ( + name, + sockPath, + cancelled, + bootstrap, +) => { try { // It is okay to fail to connect because the daemon is not running. - return await makeEndoClient(...args); + return await makeEndoClient(name, sockPath, cancelled, bootstrap); } catch { console.error('Starting Endo daemon...'); // It is also okay to fail the race to start. @@ -12,6 +24,6 @@ export const provideEndoClient = async (...args) => { // We are not going to contemplate reliably in the face of a worker getting // stopped the moment after it was started. // That is a bridge too far. - return makeEndoClient(...args); + return makeEndoClient(name, sockPath, cancelled, bootstrap); } }; diff --git a/packages/cli/src/install.js b/packages/cli/src/install.js index 428a7aaed4..2909605e16 100644 --- a/packages/cli/src/install.js +++ b/packages/cli/src/install.js @@ -18,7 +18,7 @@ export const install = async ({ webPageName, programPath, }) => { - /** @type {import('@endo/eventual-send').ERef> | undefined} */ + /** @type {import('@endo/eventual-send').FarRef> | undefined} */ let bundleReaderRef; /** @type {string | undefined} */ let temporaryBundleName; diff --git a/packages/cli/src/make.js b/packages/cli/src/make.js index 06c7c52f48..6c7e1a3e30 100644 --- a/packages/cli/src/make.js +++ b/packages/cli/src/make.js @@ -40,7 +40,7 @@ export const makeCommand = async ({ return; } - /** @type {import('@endo/eventual-send').ERef> | undefined} */ + /** @type {import('@endo/eventual-send').FarRef> | undefined} */ let bundleReaderRef; /** @type {string | undefined} */ let temporaryBundleName; diff --git a/packages/daemon/src/client.js b/packages/daemon/src/client.js index 5245598ca8..5a6c916383 100644 --- a/packages/daemon/src/client.js +++ b/packages/daemon/src/client.js @@ -9,7 +9,7 @@ import { makeNetstringCapTP } from './connection.js'; * @param {string} name * @param {string} sockPath * @param {Promise} cancelled - * @param {TBootstrap=} bootstrap + * @param {TBootstrap} [bootstrap] */ export const makeEndoClient = async (name, sockPath, cancelled, bootstrap) => { const conn = net.connect(sockPath); diff --git a/packages/daemon/src/daemon-node.js b/packages/daemon/src/daemon-node.js index 8563019863..3a1c40ef89 100644 --- a/packages/daemon/src/daemon-node.js +++ b/packages/daemon/src/daemon-node.js @@ -153,6 +153,7 @@ const main = async () => { process.once('SIGINT', () => cancel(new Error('SIGINT'))); +// @ts-ignore Yes, we can assign to exitCode, typedoc. process.exitCode = 1; main().then( () => { diff --git a/packages/daemon/src/reader-ref.js b/packages/daemon/src/reader-ref.js index d18b4215c4..c53b129d4e 100644 --- a/packages/daemon/src/reader-ref.js +++ b/packages/daemon/src/reader-ref.js @@ -10,7 +10,7 @@ import { Far } from '@endo/far'; * * @template T The item type of the iterable. * @param {import('./types').SomehowAsyncIterable} iterable The iterable object. - * @returns {import('@endo/stream').Stream} sort of fudging this into a stream to appease "mapReader" + * @returns {import('@endo/stream').Reader} sort of fudging this into a stream to appease "mapReader" */ export const asyncIterate = iterable => { let iterator; @@ -61,7 +61,7 @@ export const makeIteratorRef = iterable => { /** * @param {import('./types').SomehowAsyncIterable} readable - * @returns {import('@endo/far').FarRef>} + * @returns {import('@endo/far').FarRef>} */ export const makeReaderRef = readable => makeIteratorRef(mapReader(asyncIterate(readable), encodeBase64)); diff --git a/packages/daemon/src/ref-reader.js b/packages/daemon/src/ref-reader.js index d55b255f46..914ad3b572 100644 --- a/packages/daemon/src/ref-reader.js +++ b/packages/daemon/src/ref-reader.js @@ -25,7 +25,7 @@ export const makeRefIterator = iteratorRef => { /** * @param {import('@endo/far').ERef>} readerRef - * @returns {AsyncIterableIterator} + * @returns {AsyncIterableIterator} */ export const makeRefReader = readerRef => mapReader(makeRefIterator(readerRef), decodeBase64); diff --git a/packages/daemon/src/worker-node.js b/packages/daemon/src/worker-node.js index 168afbff5a..ab8812303d 100644 --- a/packages/daemon/src/worker-node.js +++ b/packages/daemon/src/worker-node.js @@ -42,6 +42,7 @@ const { promise: cancelled, reject: cancel } = process.once('SIGINT', () => cancel(new Error('SIGINT'))); +// @ts-ignore Yes, we can assign to exitCode, typedoc. process.exitCode = 1; main(powers, locator, workerUuid, process.pid, cancel, cancelled).then( () => { From ba33e82e3de063665ee85c0f7ab0a30c8f7a6018 Mon Sep 17 00:00:00 2001 From: Erik Marks Date: Thu, 25 Jan 2024 16:03:59 -0800 Subject: [PATCH 168/234] feat(cli)!: Rename --UNSAFE to --UNCONFINED Renames the `UNSAFE` flag to `UNCONFINED`, which is more specific. --- packages/cli/src/endo.js | 11 +++++++---- packages/cli/src/make.js | 4 ++-- packages/cli/src/run.js | 10 +++++++--- 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/packages/cli/src/endo.js b/packages/cli/src/endo.js index c400d785f7..1183e113b4 100644 --- a/packages/cli/src/endo.js +++ b/packages/cli/src/endo.js @@ -86,7 +86,10 @@ export const main = async rawArgs => { [], ) .option('-b,--bundle ', 'Bundle name for the caplet program') - .option('--UNSAFE ', 'Or path of an unsafe plugin to run in Node.js') + .option( + '--UNCONFINED ', + 'Or path of an unsafe plugin to run in Node.js', + ) .option( '-p,--powers ', 'Endowment to give the worklet (a name, NONE, HOST, or ENDO)', @@ -95,7 +98,7 @@ export const main = async rawArgs => { const { as: partyNames, bundle: bundleName, - UNSAFE: importPath, + UNCONFINED: importPath, powers: powersName = 'NONE', } = cmd.opts(); const { run } = await import('./run.js'); @@ -113,7 +116,7 @@ export const main = async rawArgs => { .command('make [file]') .description('make a plugin or a worker caplet (worklet)') .option('-b,--bundle ', 'Bundle for a web page to open') - .option('--UNSAFE ', 'Path to a Node.js module') + .option('--UNCONFINED ', 'Path to a Node.js module') .option( '-a,--as ', 'Pose as named party (as named by current party)', @@ -131,7 +134,7 @@ export const main = async rawArgs => { ) .action(async (filePath, cmd) => { const { - UNSAFE: importPath, + UNCONFINED: importPath, name: resultName, bundle: bundleName, worker: workerName = 'NEW', diff --git a/packages/cli/src/make.js b/packages/cli/src/make.js index 06c7c52f48..e63f2b9262 100644 --- a/packages/cli/src/make.js +++ b/packages/cli/src/make.js @@ -24,7 +24,7 @@ export const makeCommand = async ({ powersName, }) => { if (filePath !== undefined && importPath !== undefined) { - console.error('Specify only one of [file] or --UNSAFE '); + console.error('Specify only one of [file] or --UNCONFINED '); process.exitCode = 1; return; } @@ -34,7 +34,7 @@ export const makeCommand = async ({ bundleName === undefined ) { console.error( - 'Specify at least one of [file], --bundle , or --UNSAFE ', + 'Specify at least one of [file], --bundle , or --UNCONFINED ', ); process.exitCode = 1; return; diff --git a/packages/cli/src/run.js b/packages/cli/src/run.js index 8a0f1641a0..db99963506 100644 --- a/packages/cli/src/run.js +++ b/packages/cli/src/run.js @@ -29,7 +29,7 @@ export const run = async ({ importPath === undefined && bundleName === undefined ) { - console.error('Specify at least one of --file, --bundle, or --UNSAFE'); + console.error('Specify at least one of --file, --bundle, or --UNCONFINED'); process.exitCode = 1; return; } @@ -51,7 +51,9 @@ export const run = async ({ if (importPath !== undefined) { if (bundleName !== undefined) { - console.error('Must specify either --bundle or --UNSAFE, not both'); + console.error( + 'Must specify either --bundle or --UNCONFINED, not both', + ); process.exitCode = 1; return; } @@ -70,7 +72,9 @@ export const run = async ({ let bundle; if (bundleName !== undefined) { if (importPath !== undefined) { - console.error('Must specify either --bundle or --UNSAFE, not both'); + console.error( + 'Must specify either --bundle or --UNCONFINED, not both', + ); process.exitCode = 1; return; } From 5623d608056586c33d3d35798d8171d6ac69c5a5 Mon Sep 17 00:00:00 2001 From: Erik Marks Date: Thu, 25 Jan 2024 17:09:10 -0800 Subject: [PATCH 169/234] feat(daemon,cli)!: Rename internals using "unsafe" Renames all internals that use "unsafe" in their names to use "unconfined" instead. Will require an `endo purge -f`. --- packages/cli/src/make.js | 9 ++------- packages/daemon/src/daemon-node-powers.js | 2 +- packages/daemon/src/daemon.js | 20 +++++++------------- packages/daemon/src/host.js | 20 ++++++++++---------- packages/daemon/src/pet-store.js | 2 +- packages/daemon/src/types.d.ts | 12 ++++++------ packages/daemon/src/web-page-bundler.js | 2 +- packages/daemon/src/web-page.js | 2 +- packages/daemon/src/worker.js | 4 ++-- packages/daemon/test/test-endo.js | 10 +++++----- 10 files changed, 36 insertions(+), 47 deletions(-) diff --git a/packages/cli/src/make.js b/packages/cli/src/make.js index e63f2b9262..a49658f49d 100644 --- a/packages/cli/src/make.js +++ b/packages/cli/src/make.js @@ -66,18 +66,13 @@ export const makeCommand = async ({ const resultP = importPath !== undefined - ? E(party).importUnsafeAndEndow( + ? E(party).makeUnconfined( workerName, path.resolve(importPath), powersName, resultName, ) - : E(party).importBundleAndEndow( - workerName, - bundleName, - powersName, - resultName, - ); + : E(party).makeBundle(workerName, bundleName, powersName, resultName); const result = await resultP; console.log(result); diff --git a/packages/daemon/src/daemon-node-powers.js b/packages/daemon/src/daemon-node-powers.js index 4968bd28c3..f954d08817 100644 --- a/packages/daemon/src/daemon-node-powers.js +++ b/packages/daemon/src/daemon-node-powers.js @@ -586,7 +586,7 @@ export const makeDaemonicPersistencePowers = ( const webPageBundlerFormula = includeWebPageBundler ? { - type: /** @type {'import-unsafe'} */ ('import-unsafe'), + type: /** @type {'make-unconfined'} */ ('make-unconfined'), worker: `worker-id512:${zero512}`, powers: 'host', importPath: fileURLToPath( diff --git a/packages/daemon/src/daemon.js b/packages/daemon/src/daemon.js index c0ec3d4e8b..090e132cb8 100644 --- a/packages/daemon/src/daemon.js +++ b/packages/daemon/src/daemon.js @@ -239,10 +239,7 @@ const makeEndoBootstrap = ( // eslint-disable-next-line no-use-before-define provideValueForFormulaIdentifier(guestFormulaIdentifier) ); - const external = E(workerDaemonFacet).importUnsafeAndEndow( - importPath, - guestP, - ); + const external = E(workerDaemonFacet).makeUnconfined(importPath, guestP); return { external, internal: undefined }; }; @@ -285,10 +282,7 @@ const makeEndoBootstrap = ( // eslint-disable-next-line no-use-before-define provideValueForFormulaIdentifier(guestFormulaIdentifier) ); - const external = E(workerDaemonFacet).importBundleAndEndow( - readableBundleP, - guestP, - ); + const external = E(workerDaemonFacet).makeBundle(readableBundleP, guestP); return { external, internal: undefined }; }; @@ -312,14 +306,14 @@ const makeEndoBootstrap = ( formula.values, terminator, ); - } else if (formula.type === 'import-unsafe') { + } else if (formula.type === 'make-unconfined') { return makeControllerForUnsafePlugin( formula.worker, formula.powers, formula.importPath, terminator, ); - } else if (formula.type === 'import-bundle') { + } else if (formula.type === 'make-bundle') { return makeControllerForSafeBundle( formula.worker, formula.powers, @@ -433,8 +427,8 @@ const makeEndoBootstrap = ( } else if ( [ 'eval-id512', - 'import-unsafe-id512', - 'import-bundle-id512', + 'make-unconfined-id512', + 'make-bundle-id512', 'guest-id512', 'web-bundle', ].includes(formulaType) @@ -614,7 +608,7 @@ const makeEndoBootstrap = ( }) ); const bundle = await E(bundleBlob).json(); - await E(webPageP).importBundleAndEndow(bundle, endowedPowers); + await E(webPageP).makeBundle(bundle, endowedPowers); }, }); diff --git a/packages/daemon/src/host.js b/packages/daemon/src/host.js index 0b72dfbf1a..2fe576495b 100644 --- a/packages/daemon/src/host.js +++ b/packages/daemon/src/host.js @@ -244,7 +244,7 @@ export const makeHostMaker = ({ * @param {string | 'NONE' | 'HOST' | 'ENDO'} powersName * @param {string} resultName */ - const importUnsafeAndEndow = async ( + const makeUnconfined = async ( workerName, importPath, powersName, @@ -259,8 +259,8 @@ export const makeHostMaker = ({ ); const formula = { - /** @type {'import-unsafe'} */ - type: 'import-unsafe', + /** @type {'make-unconfined'} */ + type: 'make-unconfined', worker: workerFormulaIdentifier, powers: powersFormulaIdentifier, importPath, @@ -270,7 +270,7 @@ export const makeHostMaker = ({ // eslint-disable-next-line no-use-before-define const { formulaIdentifier, value } = await provideValueForFormula( formula, - 'import-unsafe-id512', + 'make-unconfined-id512', ); if (resultName !== undefined) { await petStore.write(resultName, formulaIdentifier); @@ -284,7 +284,7 @@ export const makeHostMaker = ({ * @param {string | 'NONE' | 'HOST' | 'ENDO'} powersName * @param {string} resultName */ - const importBundleAndEndow = async ( + const makeBundle = async ( workerName, bundleName, powersName, @@ -305,8 +305,8 @@ export const makeHostMaker = ({ ); const formula = { - /** @type {'import-bundle'} */ - type: 'import-bundle', + /** @type {'make-bundle'} */ + type: 'make-bundle', worker: workerFormulaIdentifier, powers: powersFormulaIdentifier, bundle: bundleFormulaIdentifier, @@ -316,7 +316,7 @@ export const makeHostMaker = ({ // eslint-disable-next-line no-use-before-define const { value, formulaIdentifier } = await provideValueForFormula( formula, - 'import-bundle-id512', + 'make-bundle-id512', ); if (resultName !== undefined) { @@ -451,8 +451,8 @@ export const makeHostMaker = ({ provideWorker, evaluate, terminate, - importUnsafeAndEndow, - importBundleAndEndow, + makeUnconfined, + makeBundle, provideWebPage, }); diff --git a/packages/daemon/src/pet-store.js b/packages/daemon/src/pet-store.js index 509d8b3354..37558fa60b 100644 --- a/packages/daemon/src/pet-store.js +++ b/packages/daemon/src/pet-store.js @@ -9,7 +9,7 @@ const { quote: q } = assert; const validIdPattern = /^[0-9a-f]{128}$/; const validFormulaPattern = - /^(?:host|pet-store|(?:readable-blob-sha512|worker-id512|pet-store-id512|eval-id512|import-unsafe-id512|import-bundle-id512|host-id512|guest-id512):[0-9a-f]{128}|web-bundle:[0-9a-f]{32})$/; + /^(?:host|pet-store|(?:readable-blob-sha512|worker-id512|pet-store-id512|eval-id512|make-unconfined-id512|make-bundle-id512|host-id512|guest-id512):[0-9a-f]{128}|web-bundle:[0-9a-f]{32})$/; /** * @param {import('./types.js').FilePowers} filePowers diff --git a/packages/daemon/src/types.d.ts b/packages/daemon/src/types.d.ts index 91edff7379..8be89817a9 100644 --- a/packages/daemon/src/types.d.ts +++ b/packages/daemon/src/types.d.ts @@ -73,7 +73,7 @@ type EvalFormula = { }; type ImportUnsafeFormula = { - type: 'import-unsafe'; + type: 'make-unconfined'; worker: string; powers: string; importPath: string; @@ -81,7 +81,7 @@ type ImportUnsafeFormula = { }; type ImportBundleFormula = { - type: 'import-bundle'; + type: 'make-bundle'; worker: string; powers: string; bundle: string; @@ -233,13 +233,13 @@ export interface EndoHost { petNames: Array, resultName?: string, ); - importUnsafeAndEndow( + makeUnconfined( workerPetName: string | undefined, importPath: string, powersName: string, resultName?: string, ): Promise; - importBundleAndEndow( + makeBundle( workerPetName: string | undefined, bundleName: string, powersName: string, @@ -342,8 +342,8 @@ export interface WorkerDaemonFacet { values: Array, cancelled: Promise, ): Promise; - importBundleAndEndow(bundle: ERef, powers: ERef); - importUnsafeAndEndow(path: string, powers: ERef); + makeBundle(bundle: ERef, powers: ERef); + makeUnconfined(path: string, powers: ERef); } export type DaemonicControlPowers = { diff --git a/packages/daemon/src/web-page-bundler.js b/packages/daemon/src/web-page-bundler.js index 3431106e3a..e39cc2e21e 100644 --- a/packages/daemon/src/web-page-bundler.js +++ b/packages/daemon/src/web-page-bundler.js @@ -2,7 +2,7 @@ // This is a built-in unsafe plugin for lazily constructing the web-page.js // bundle for booting up web caplets. -// The hard-coded 'web-page-js' formula is a hard-coded 'import-unsafe' formula +// The hard-coded 'web-page-js' formula is a hard-coded 'make-unconfined' formula // that runs this program in worker 0. // It does not accept its endowed powers. diff --git a/packages/daemon/src/web-page.js b/packages/daemon/src/web-page.js index 781b2db6d5..c68e1f7ed8 100644 --- a/packages/daemon/src/web-page.js +++ b/packages/daemon/src/web-page.js @@ -82,7 +82,7 @@ const bootstrap = Far('WebFacet', { console.log('received ping'); return 'pong'; }, - async importBundleAndEndow(bundle, powers) { + async makeBundle(bundle, powers) { const namespace = await importBundle(bundle, { endowments, }); diff --git a/packages/daemon/src/worker.js b/packages/daemon/src/worker.js index b6de6cbc6b..2f54c6822a 100644 --- a/packages/daemon/src/worker.js +++ b/packages/daemon/src/worker.js @@ -53,7 +53,7 @@ export const makeWorkerFacet = ({ pathToFileURL, cancel }) => { * @param {string} path * @param {unknown} powersP */ - importUnsafeAndEndow: async (path, powersP) => { + makeUnconfined: async (path, powersP) => { const url = pathToFileURL(path); const namespace = await import(url); return namespace.make(powersP); @@ -63,7 +63,7 @@ export const makeWorkerFacet = ({ pathToFileURL, cancel }) => { * @param {import('@endo/eventual-send').ERef} readableP * @param {unknown} powersP */ - importBundleAndEndow: async (readableP, powersP) => { + makeBundle: async (readableP, powersP) => { const bundleText = await E(readableP).text(); const bundle = JSON.parse(bundleText); diff --git a/packages/daemon/test/test-endo.js b/packages/daemon/test/test-endo.js index 8ec36ac0aa..213799db79 100644 --- a/packages/daemon/test/test-endo.js +++ b/packages/daemon/test/test-endo.js @@ -311,7 +311,7 @@ test('closure state lost by restart', async t => { test('persist unsafe services and their requests', async t => { const { promise: cancelled, reject: cancel } = makePromiseKit(); t.teardown(() => cancel(Error('teardown'))); - const locator = makeLocator('tmp', 'import-unsafe'); + const locator = makeLocator('tmp', 'make-unconfined'); await stop(locator).catch(() => {}); await reset(locator); @@ -358,7 +358,7 @@ test('persist unsafe services and their requests', async t => { await E(host).makeWorker('w1'); await E(host).provideGuest('o1'); const servicePath = path.join(dirname, 'test', 'service.js'); - await E(host).importUnsafeAndEndow('w1', servicePath, 'o1', 's1'); + await E(host).makeUnconfined('w1', servicePath, 'o1', 's1'); await E(host).makeWorker('w2'); const answer = await E(host).evaluate( @@ -444,7 +444,7 @@ test('direct termination', async t => { await E(host).provideWorker('worker'); const counterPath = path.join(dirname, 'test', 'counter.js'); - await E(host).importUnsafeAndEndow('worker', counterPath, 'NONE', 'counter'); + await E(host).makeUnconfined('worker', counterPath, 'NONE', 'counter'); t.is( 1, await E(host).evaluate( @@ -524,7 +524,7 @@ test('indirect termination', async t => { await E(host).provideWorker('worker'); const counterPath = path.join(dirname, 'test', 'counter.js'); - await E(host).importUnsafeAndEndow('worker', counterPath, 'SELF', 'counter'); + await E(host).makeUnconfined('worker', counterPath, 'SELF', 'counter'); t.is( 1, await E(host).evaluate( @@ -606,7 +606,7 @@ test('terminate because of requested capability', async t => { const messages = E(host).followMessages(); const counterPath = path.join(dirname, 'test', 'counter-party.js'); - E(host).importUnsafeAndEndow('worker', counterPath, 'guest', 'counter'); + E(host).makeUnconfined('worker', counterPath, 'guest', 'counter'); await E(host).evaluate('worker', '0', [], [], 'zero'); await E(messages).next(); From 5ca73312ededd64707a9a8a07a6d96b2f25fd0b8 Mon Sep 17 00:00:00 2001 From: Erik Marks Date: Thu, 25 Jan 2024 17:50:31 -0800 Subject: [PATCH 170/234] refactor(daemon): Rename additional "unsafe" internals --- packages/cli/src/endo.js | 2 +- packages/daemon/src/daemon.js | 4 ++-- packages/daemon/src/types.d.ts | 8 ++++---- packages/daemon/src/web-page-bundler.js | 2 +- packages/daemon/test/test-endo.js | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/cli/src/endo.js b/packages/cli/src/endo.js index 1183e113b4..6a9f740a4e 100644 --- a/packages/cli/src/endo.js +++ b/packages/cli/src/endo.js @@ -88,7 +88,7 @@ export const main = async rawArgs => { .option('-b,--bundle ', 'Bundle name for the caplet program') .option( '--UNCONFINED ', - 'Or path of an unsafe plugin to run in Node.js', + 'Or path of an unconfined plugin to run in Node.js', ) .option( '-p,--powers ', diff --git a/packages/daemon/src/daemon.js b/packages/daemon/src/daemon.js index 090e132cb8..c6442959a8 100644 --- a/packages/daemon/src/daemon.js +++ b/packages/daemon/src/daemon.js @@ -214,7 +214,7 @@ const makeEndoBootstrap = ( * @param {string} importPath * @param {import('./types.js').Terminator} terminator */ - const makeControllerForUnsafePlugin = async ( + const makeControllerForUnconfinedPlugin = async ( workerFormulaIdentifier, guestFormulaIdentifier, importPath, @@ -307,7 +307,7 @@ const makeEndoBootstrap = ( terminator, ); } else if (formula.type === 'make-unconfined') { - return makeControllerForUnsafePlugin( + return makeControllerForUnconfinedPlugin( formula.worker, formula.powers, formula.importPath, diff --git a/packages/daemon/src/types.d.ts b/packages/daemon/src/types.d.ts index 8be89817a9..5880b82739 100644 --- a/packages/daemon/src/types.d.ts +++ b/packages/daemon/src/types.d.ts @@ -72,7 +72,7 @@ type EvalFormula = { // TODO formula slots }; -type ImportUnsafeFormula = { +type MakeUnconfinedFormula = { type: 'make-unconfined'; worker: string; powers: string; @@ -80,7 +80,7 @@ type ImportUnsafeFormula = { // TODO formula slots }; -type ImportBundleFormula = { +type MakeBundleFormula = { type: 'make-bundle'; worker: string; powers: string; @@ -97,8 +97,8 @@ type WebBundleFormula = { export type Formula = | GuestFormula | EvalFormula - | ImportUnsafeFormula - | ImportBundleFormula + | MakeUnconfinedFormula + | MakeBundleFormula | WebBundleFormula; export type Label = { diff --git a/packages/daemon/src/web-page-bundler.js b/packages/daemon/src/web-page-bundler.js index e39cc2e21e..a59502ea39 100644 --- a/packages/daemon/src/web-page-bundler.js +++ b/packages/daemon/src/web-page-bundler.js @@ -1,6 +1,6 @@ // @ts-check -// This is a built-in unsafe plugin for lazily constructing the web-page.js +// This is a built-in unconfined plugin for lazily constructing the web-page.js // bundle for booting up web caplets. // The hard-coded 'web-page-js' formula is a hard-coded 'make-unconfined' formula // that runs this program in worker 0. diff --git a/packages/daemon/test/test-endo.js b/packages/daemon/test/test-endo.js index 213799db79..6e27c37c86 100644 --- a/packages/daemon/test/test-endo.js +++ b/packages/daemon/test/test-endo.js @@ -308,7 +308,7 @@ test('closure state lost by restart', async t => { await stop(locator); }); -test('persist unsafe services and their requests', async t => { +test('persist unconfined services and their requests', async t => { const { promise: cancelled, reject: cancel } = makePromiseKit(); t.teardown(() => cancel(Error('teardown'))); const locator = makeLocator('tmp', 'make-unconfined'); From 3c2d9247277491f47c83de0fee9511b6e8d9bff3 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Wed, 13 Dec 2023 17:50:54 -0800 Subject: [PATCH 171/234] feat(daemon): Undeniable host INFO tool --- packages/daemon/src/daemon.js | 129 +++++++++++++++++++++++++++++++ packages/daemon/src/host.js | 3 + packages/daemon/src/pet-store.js | 2 +- 3 files changed, 133 insertions(+), 1 deletion(-) diff --git a/packages/daemon/src/daemon.js b/packages/daemon/src/daemon.js index c6442959a8..d4c0a90bb4 100644 --- a/packages/daemon/src/daemon.js +++ b/packages/daemon/src/daemon.js @@ -37,6 +37,17 @@ const delay = async (ms, cancelled) => { }); }; +const makeInfo = (type, number, record) => + Far(`Inspector (${type} ${number})`, { + lookup: async petName => { + if (!Object.hasOwn(record, petName)) { + return undefined; + } + return record[petName]; + }, + list: () => Object.keys(record), + }); + /** * @param {import('./types.js').DaemonicPowers} powers * @param {Promise} webletPortP @@ -371,14 +382,21 @@ const makeEndoBootstrap = ( assertPetName, ); return { external, internal: undefined }; + } else if (formulaIdentifier === 'pet-inspector') { + // Behold, unavoidable forward-reference: + // eslint-disable-next-line no-use-before-define + const external = makeIdentifiedInspector('pet-store'); + return { external, internal: undefined }; } else if (formulaIdentifier === 'host') { const storeFormulaIdentifier = 'pet-store'; + const infoFormulaIdentifier = 'pet-inspector'; const workerFormulaIdentifier = `worker-id512:${zero512}`; // Behold, recursion: // eslint-disable-next-line no-use-before-define return makeIdentifiedHost( formulaIdentifier, storeFormulaIdentifier, + infoFormulaIdentifier, workerFormulaIdentifier, terminator, ); @@ -407,6 +425,13 @@ const makeEndoBootstrap = ( return { external, internal: undefined }; } else if (formulaType === 'worker-id512') { return makeIdentifiedWorkerController(formulaNumber, terminator); + } else if (formulaType === 'pet-inspector-id512') { + // Behold, unavoidable forward-reference: + // eslint-disable-next-line no-use-before-define + const external = makeIdentifiedInspector( + `pet-store-id512:${formulaNumber}`, + ); + return { external, internal: undefined }; } else if (formulaType === 'pet-store-id512') { const external = petStorePowers.makeIdentifiedPetStore( formulaNumber, @@ -415,12 +440,14 @@ const makeEndoBootstrap = ( return { external, internal: undefined }; } else if (formulaType === 'host-id512') { const storeFormulaIdentifier = `pet-store-id512:${formulaNumber}`; + const infoFormulaIdentifier = `pet-inspector-id512:${formulaNumber}`; const workerFormulaIdentifier = `worker-id512:${formulaNumber}`; // Behold, recursion: // eslint-disable-next-line no-use-before-define return makeIdentifiedHost( formulaIdentifier, storeFormulaIdentifier, + infoFormulaIdentifier, workerFormulaIdentifier, terminator, ); @@ -583,6 +610,108 @@ const makeEndoBootstrap = ( makeMailbox, }); + const makeIdentifiedInspector = async petStoreFormulaIdentifier => { + const petStore = await provideValueForFormulaIdentifier( + petStoreFormulaIdentifier, + ); + + /** @param {string} petName */ + const lookup = async petName => { + const formulaIdentifier = petStore.lookup(petName); + if (formulaIdentifier === undefined) { + throw new Error(`Unknown pet name ${petName}`); + } + const delimiterIndex = formulaIdentifier.indexOf(':'); + // eslint-disable-next-line @endo/restrict-comparison-operands + if (delimiterIndex < 0) { + return undefined; + } + const prefix = formulaIdentifier.slice(0, delimiterIndex); + const formulaNumber = formulaIdentifier.slice(delimiterIndex + 1); + if ( + ![ + 'eval-id512', + 'import-unsafe-id512', + 'import-bundle-id512', + 'guest-id512', + 'web-bundle', + ].includes(prefix) + ) { + return makeInfo(prefix, formulaNumber, harden({})); + } + const formula = await persistencePowers.readFormula( + prefix, + formulaNumber, + ); + if (formula.type === 'eval') { + return makeInfo( + formula.type, + formulaNumber, + harden({ + SOURCE: formula.source, + WORKER: provideValueForFormulaIdentifier(formula.worker), + ENDOWMENTS: Object.fromEntries( + formula.names.map((name, index) => { + return [ + name, + provideValueForFormulaIdentifier(formula.values[index]), + ]; + }), + ), + }), + ); + } else if (formula.type === 'import-unsafe') { + return makeInfo( + formula.type, + formulaNumber, + harden({ + SPECIFIER: formula.type, + WORKER: provideValueForFormulaIdentifier(formula.worker), + POWERS: provideValueForFormulaIdentifier(formula.powers), + }), + ); + } else if (formula.type === 'import-bundle') { + return makeInfo( + formula.type, + formulaNumber, + harden({ + WORKER: provideValueForFormulaIdentifier(formula.worker), + BUNDLE: provideValueForFormulaIdentifier(formula.bundle), + POWERS: provideValueForFormulaIdentifier(formula.powers), + }), + ); + } else if (formula.type === 'guest') { + return makeInfo( + formula.type, + formulaNumber, + harden({ + HOST: provideValueForFormulaIdentifier(formula.host), + }), + ); + } else if (formula.type === 'web-bundle') { + return makeInfo( + formula.type, + formulaNumber, + harden({ + BUNDLE: provideValueForFormulaIdentifier(formula.bundle), + POWERS: provideValueForFormulaIdentifier(formula.powers), + }), + ); + } + // @ts-expect-error this should never occur + return makeInfo(formula.type, formulaNumber, harden({})); + }; + + const list = () => petStore.list(); + + const info = Far('Endo info facet', { + lookup, + list, + }); + + return info; + }; + const endoBootstrap = Far('Endo private facet', { // TODO for user named diff --git a/packages/daemon/src/host.js b/packages/daemon/src/host.js index 2fe576495b..ba212e53d8 100644 --- a/packages/daemon/src/host.js +++ b/packages/daemon/src/host.js @@ -18,12 +18,14 @@ export const makeHostMaker = ({ /** * @param {string} hostFormulaIdentifier * @param {string} storeFormulaIdentifier + * @param {string} infoFormulaIdentifier * @param {string} mainWorkerFormulaIdentifier * @param {import('./types.js').Terminator} terminator */ const makeIdentifiedHost = async ( hostFormulaIdentifier, storeFormulaIdentifier, + infoFormulaIdentifier, mainWorkerFormulaIdentifier, terminator, ) => { @@ -58,6 +60,7 @@ export const makeHostMaker = ({ selfFormulaIdentifier: hostFormulaIdentifier, specialNames: { SELF: hostFormulaIdentifier, + INFO: infoFormulaIdentifier, NONE: 'least-authority', ENDO: 'endo', }, diff --git a/packages/daemon/src/pet-store.js b/packages/daemon/src/pet-store.js index c936722ced..c4dd30d400 100644 --- a/packages/daemon/src/pet-store.js +++ b/packages/daemon/src/pet-store.js @@ -9,7 +9,7 @@ const { quote: q } = assert; const validIdPattern = /^[0-9a-f]{128}$/; const validFormulaPattern = - /^(?:host|pet-store|(?:readable-blob-sha512|worker-id512|pet-store-id512|eval-id512|make-unconfined-id512|make-bundle-id512|host-id512|guest-id512):[0-9a-f]{128}|web-bundle:[0-9a-f]{32})$/; + /^(?:host|pet-store|pet-inspector|(?:readable-blob-sha512|worker-id512|pet-store-id512|eval-id512|make-unconfined-id512|make-bundle-id512|host-id512|guest-id512):[0-9a-f]{128}|web-bundle:[0-9a-f]{32})$/; /** * @param {import('./types.js').FilePowers} filePowers From c23dc27d2679d7632552d2bf2116782ee8c34a14 Mon Sep 17 00:00:00 2001 From: Erik Marks Date: Mon, 22 Jan 2024 22:29:56 -0800 Subject: [PATCH 172/234] refactor(daemon): Use parseFormulaIdentifier in makeIdentifiedInspector --- packages/daemon/src/daemon.js | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/packages/daemon/src/daemon.js b/packages/daemon/src/daemon.js index d4c0a90bb4..152d36b70e 100644 --- a/packages/daemon/src/daemon.js +++ b/packages/daemon/src/daemon.js @@ -621,13 +621,8 @@ const makeEndoBootstrap = ( if (formulaIdentifier === undefined) { throw new Error(`Unknown pet name ${petName}`); } - const delimiterIndex = formulaIdentifier.indexOf(':'); - // eslint-disable-next-line @endo/restrict-comparison-operands - if (delimiterIndex < 0) { - return undefined; - } - const prefix = formulaIdentifier.slice(0, delimiterIndex); - const formulaNumber = formulaIdentifier.slice(delimiterIndex + 1); + const { type: formulaType, number: formulaNumber } = + parseFormulaIdentifier(formulaIdentifier); if ( ![ 'eval-id512', @@ -635,12 +630,12 @@ const makeEndoBootstrap = ( 'import-bundle-id512', 'guest-id512', 'web-bundle', - ].includes(prefix) + ].includes(formulaType) ) { - return makeInfo(prefix, formulaNumber, harden({})); + return makeInfo(formulaType, formulaNumber, harden({})); } const formula = await persistencePowers.readFormula( - prefix, + formulaType, formulaNumber, ); if (formula.type === 'eval') { From d0eaab150d2accb3be7ef1ad21a6e48c49dd5ee0 Mon Sep 17 00:00:00 2001 From: Erik Marks Date: Wed, 24 Jan 2024 20:36:30 -0800 Subject: [PATCH 173/234] fix(daemon): Support pet-inspector in parseFormulaIdentifier --- packages/daemon/src/formula-identifier.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/daemon/src/formula-identifier.js b/packages/daemon/src/formula-identifier.js index 8e301a0a50..b37e4a59a2 100644 --- a/packages/daemon/src/formula-identifier.js +++ b/packages/daemon/src/formula-identifier.js @@ -1,10 +1,11 @@ const { quote: q } = assert; const numberlessFormulasIdentifiers = new Set([ - 'pet-store', - 'host', 'endo', + 'host', 'least-authority', + 'pet-inspector', + 'pet-store', 'web-page-js', ]); From 59a96302cea2dcc99c5b6ed8ffcaf304e9f1ed8c Mon Sep 17 00:00:00 2001 From: kumavis Date: Wed, 24 Jan 2024 16:31:45 -1000 Subject: [PATCH 174/234] feat(daemon): mailbox tracks recipient --- packages/daemon/src/mail.js | 33 ++++++++++++++++++++++++++++----- packages/daemon/src/types.d.ts | 1 + 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/packages/daemon/src/mail.js b/packages/daemon/src/mail.js index c41c4d8d5f..e593e760ee 100644 --- a/packages/daemon/src/mail.js +++ b/packages/daemon/src/mail.js @@ -100,21 +100,27 @@ export const makeMailboxMaker = ({ const dubMessage = message => { const { type } = message; if (type === 'request') { - const { who: senderFormulaIdentifier, ...rest } = message; + const { who: senderFormulaIdentifier, dest: recipientFormulaIdentifier, ...rest } = message; const [senderName] = reverseLookupFormulaIdentifier( senderFormulaIdentifier, ); + const [recipientName] = reverseLookupFormulaIdentifier( + recipientFormulaIdentifier, + ); if (senderName !== undefined) { - return { who: senderName, ...rest }; + return { who: senderName, dest: recipientName, ...rest }; } return undefined; } else if (type === 'package') { - const { formulas: _, who: senderFormulaIdentifier, ...rest } = message; + const { formulas: _, who: senderFormulaIdentifier, dest: recipientFormulaIdentifier, ...rest } = message; const [senderName] = reverseLookupFormulaIdentifier( senderFormulaIdentifier, ); + const [recipientName] = reverseLookupFormulaIdentifier( + recipientFormulaIdentifier, + ); if (senderName !== undefined) { - return { who: senderName, ...rest }; + return { who: senderName, dest: recipientName, ...rest }; } return undefined; } @@ -172,8 +178,9 @@ export const makeMailboxMaker = ({ /** * @param {string} what - user visible description of the desired value * @param {string} who + * @param {string} dest */ - const requestFormulaIdentifier = async (what, who) => { + const requestFormulaIdentifier = async (what, who, dest) => { /** @type {import('@endo/promise-kit/src/types.js').PromiseKit} */ const { promise, resolve } = makePromiseKit(); const settled = promise.then( @@ -183,6 +190,7 @@ export const makeMailboxMaker = ({ const message = deliver({ type: /** @type {const} */ ('request'), who, + dest, what, settled, }); @@ -209,6 +217,7 @@ export const makeMailboxMaker = ({ formulaIdentifier = await requestFormulaIdentifier( what, senderFormulaIdentifier, + selfFormulaIdentifier, ); await senderPetStore.write(responseName, formulaIdentifier); } @@ -220,6 +229,7 @@ export const makeMailboxMaker = ({ const formulaIdentifier = await requestFormulaIdentifier( what, senderFormulaIdentifier, + selfFormulaIdentifier, ); // TODO: // terminator.thisDiesIfThatDies(formulaIdentifier); @@ -271,12 +281,14 @@ export const makeMailboxMaker = ({ * @param {Array} strings * @param {Array} edgeNames * @param {Array} formulaIdentifiers + * @param {string} receiverFormulaIdentifier */ const receive = ( senderFormulaIdentifier, strings, edgeNames, formulaIdentifiers, + receiverFormulaIdentifier = selfFormulaIdentifier, ) => deliver({ type: /** @type {const} */ ('package'), @@ -284,6 +296,7 @@ export const makeMailboxMaker = ({ names: edgeNames, formulas: formulaIdentifiers, who: senderFormulaIdentifier, + dest: receiverFormulaIdentifier, }); /** @@ -332,11 +345,21 @@ export const makeMailboxMaker = ({ } return formulaIdentifier; }); + // add to recipient mailbox partyReceive( selfFormulaIdentifier, strings, edgeNames, formulaIdentifiers, + recipientFormulaIdentifier, + ); + // add to own mailbox + receive( + selfFormulaIdentifier, + strings, + edgeNames, + formulaIdentifiers, + recipientFormulaIdentifier, ); }; diff --git a/packages/daemon/src/types.d.ts b/packages/daemon/src/types.d.ts index 0d37136b7e..93d2d88701 100644 --- a/packages/daemon/src/types.d.ts +++ b/packages/daemon/src/types.d.ts @@ -104,6 +104,7 @@ export type Formula = export type Label = { number: number; who: string; + dest: string; when: string; dismissed: Promise; }; From eb559f74c18f52cf46bcb6d93cb46c4fccd95837 Mon Sep 17 00:00:00 2001 From: kumavis Date: Wed, 24 Jan 2024 16:45:02 -1000 Subject: [PATCH 175/234] feat(daemon): include formula identifiers in package messages --- packages/daemon/src/mail.js | 12 ++++++++++-- packages/daemon/src/types.d.ts | 3 +-- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/packages/daemon/src/mail.js b/packages/daemon/src/mail.js index e593e760ee..6fd856c840 100644 --- a/packages/daemon/src/mail.js +++ b/packages/daemon/src/mail.js @@ -100,7 +100,11 @@ export const makeMailboxMaker = ({ const dubMessage = message => { const { type } = message; if (type === 'request') { - const { who: senderFormulaIdentifier, dest: recipientFormulaIdentifier, ...rest } = message; + const { + who: senderFormulaIdentifier, + dest: recipientFormulaIdentifier, + ...rest + } = message; const [senderName] = reverseLookupFormulaIdentifier( senderFormulaIdentifier, ); @@ -112,7 +116,11 @@ export const makeMailboxMaker = ({ } return undefined; } else if (type === 'package') { - const { formulas: _, who: senderFormulaIdentifier, dest: recipientFormulaIdentifier, ...rest } = message; + const { + who: senderFormulaIdentifier, + dest: recipientFormulaIdentifier, + ...rest + } = message; const [senderName] = reverseLookupFormulaIdentifier( senderFormulaIdentifier, ); diff --git a/packages/daemon/src/types.d.ts b/packages/daemon/src/types.d.ts index 93d2d88701..7d50ab1520 100644 --- a/packages/daemon/src/types.d.ts +++ b/packages/daemon/src/types.d.ts @@ -121,10 +121,9 @@ export type Package = { type: 'package'; strings: Array; // text that appears before, between, and after named formulas. names: Array; // edge names -}; -export type InternalPackage = Package & { formulas: Array; // formula identifiers }; +export type InternalPackage = Package; export type Payload = Request | Package; export type InternalPayload = InternalRequest | InternalPackage; From 21fc1afc5c8f255bab2a14e8f592be5e67b86ec9 Mon Sep 17 00:00:00 2001 From: kumavis Date: Fri, 26 Jan 2024 12:21:11 -1000 Subject: [PATCH 176/234] test(daemon): package messages are also sent to sender --- packages/daemon/test/test-endo.js | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/packages/daemon/test/test-endo.js b/packages/daemon/test/test-endo.js index 6e27c37c86..e61e922d93 100644 --- a/packages/daemon/test/test-endo.js +++ b/packages/daemon/test/test-endo.js @@ -421,6 +421,24 @@ test('guest facet receives a message for host', async t => { await E(host).adopt(message1.number, 'gift', 'ten2'); const ten = await E(host).lookup('ten2'); t.is(ten, 10); + // Host should have received messages. + const hostInbox = await E(host).listMessages(); + t.deepEqual( + hostInbox.map(({ type, who, dest }) => ({ type, who, dest })), + [ + { type: 'request', who: 'guest', dest: 'SELF' }, + { type: 'package', who: 'guest', dest: 'SELF' }, + ], + ); + // Guest should have own sent messages. + const guestInbox = await E(guest).listMessages(); + t.deepEqual( + guestInbox.map(({ type, who, dest }) => ({ type, who, dest })), + [ + // { type: 'request', who: 'SELF', dest: 'HOST' }, + { type: 'package', who: 'SELF', dest: 'HOST' }, + ], + ); await stop(locator); }); From 26f6c01ff6324ad441b9bd1dfa6e5e1fec50fa4e Mon Sep 17 00:00:00 2001 From: kumavis Date: Fri, 26 Jan 2024 14:42:51 -1000 Subject: [PATCH 177/234] feat(daemon): request messages are sent to own inbox --- packages/daemon/src/mail.js | 43 +++++++++++++++++++------------ packages/daemon/test/test-endo.js | 2 +- 2 files changed, 27 insertions(+), 18 deletions(-) diff --git a/packages/daemon/src/mail.js b/packages/daemon/src/mail.js index 6fd856c840..11648f7195 100644 --- a/packages/daemon/src/mail.js +++ b/packages/daemon/src/mail.js @@ -211,12 +211,14 @@ export const makeMailboxMaker = ({ * @param {string} responseName * @param {string} senderFormulaIdentifier * @param {import('./types.js').PetStore} senderPetStore + * @param {string} [recipientFormulaIdentifier] */ const respond = async ( what, responseName, senderFormulaIdentifier, senderPetStore, + recipientFormulaIdentifier = selfFormulaIdentifier, ) => { if (responseName !== undefined) { /** @type {string | undefined} */ @@ -225,7 +227,7 @@ export const makeMailboxMaker = ({ formulaIdentifier = await requestFormulaIdentifier( what, senderFormulaIdentifier, - selfFormulaIdentifier, + recipientFormulaIdentifier, ); await senderPetStore.write(responseName, formulaIdentifier); } @@ -237,7 +239,7 @@ export const makeMailboxMaker = ({ const formulaIdentifier = await requestFormulaIdentifier( what, senderFormulaIdentifier, - selfFormulaIdentifier, + recipientFormulaIdentifier, ); // TODO: // terminator.thisDiesIfThatDies(formulaIdentifier); @@ -452,29 +454,36 @@ export const makeMailboxMaker = ({ ); } - if (responseName === undefined) { - // Behold, recursion: - // eslint-disable-next-line no-use-before-define - return deliverToRecipient( - what, - responseName, - selfFormulaIdentifier, - petStore, - ); - } - const responseP = responses.get(responseName); - if (responseP !== undefined) { - return responseP; + if (responseName !== undefined) { + const responseP = responses.get(responseName); + if (responseP !== undefined) { + return responseP; + } } + + // Note: consider sending to each mailbox with different powers. // Behold, recursion: // eslint-disable-next-line - const newResponseP = deliverToRecipient( + const recipientResponseP = deliverToRecipient( what, responseName, selfFormulaIdentifier, petStore, ); - responses.set(responseName, newResponseP); + // Send to own inbox. + const selfResponseP = respond( + what, + responseName, + selfFormulaIdentifier, + petStore, + recipientFormulaIdentifier, + ); + const newResponseP = Promise.race([recipientResponseP, selfResponseP]); + + if (responseName !== undefined) { + responses.set(responseName, newResponseP); + } + return newResponseP; }; diff --git a/packages/daemon/test/test-endo.js b/packages/daemon/test/test-endo.js index e61e922d93..81dba0203d 100644 --- a/packages/daemon/test/test-endo.js +++ b/packages/daemon/test/test-endo.js @@ -435,7 +435,7 @@ test('guest facet receives a message for host', async t => { t.deepEqual( guestInbox.map(({ type, who, dest }) => ({ type, who, dest })), [ - // { type: 'request', who: 'SELF', dest: 'HOST' }, + { type: 'request', who: 'SELF', dest: 'HOST' }, { type: 'package', who: 'SELF', dest: 'HOST' }, ], ); From a3f0c241aa83fba3e966e607d5331cc88c8adf5c Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Fri, 26 Jan 2024 18:30:26 -0800 Subject: [PATCH 178/234] fix(daemon): Address off-by-nybble formula path error --- packages/daemon/src/daemon-node-powers.js | 2 +- packages/daemon/src/pet-store.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/daemon/src/daemon-node-powers.js b/packages/daemon/src/daemon-node-powers.js index f954d08817..d0e29334fb 100644 --- a/packages/daemon/src/daemon-node-powers.js +++ b/packages/daemon/src/daemon-node-powers.js @@ -542,7 +542,7 @@ export const makeDaemonicPersistencePowers = ( ); } const head = formulaId512.slice(0, 2); - const tail = formulaId512.slice(3); + const tail = formulaId512.slice(2); const directory = filePowers.joinPath( statePath, 'formulas', diff --git a/packages/daemon/src/pet-store.js b/packages/daemon/src/pet-store.js index c936722ced..b5c18369b5 100644 --- a/packages/daemon/src/pet-store.js +++ b/packages/daemon/src/pet-store.js @@ -282,7 +282,7 @@ export const makePetStoreMaker = (filePowers, locator) => { throw new Error(`Invalid identifier for pet store ${q(id)}`); } const prefix = id.slice(0, 2); - const suffix = id.slice(3); + const suffix = id.slice(2); const petNameDirectoryPath = filePowers.joinPath( locator.statePath, 'pet-store-id512', From 3a4fc457a592c7530c3917339a3c0f42b540bb85 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Fri, 15 Dec 2023 14:03:42 -0800 Subject: [PATCH 179/234] feat(cli): Add install --open flag --- packages/cli/demo/README.md | 7 ++++++- packages/cli/src/endo.js | 3 +++ packages/cli/src/install.js | 6 ++++++ 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/packages/cli/demo/README.md b/packages/cli/demo/README.md index 17f12f654b..aff6b4d3d2 100644 --- a/packages/cli/demo/README.md +++ b/packages/cli/demo/README.md @@ -258,11 +258,16 @@ _Familiar Chat_ is an example application that provides a web app for interacting with your pet daemon. ``` -> endo open familiar-chat cat.js --powers HOST +> endo install familiar-chat cat.js --powers SELF ``` This command creates a web page named familiar-chat and endows it with the authority to maintain your petstore and mailbox. +You can then open that page. + +``` +> endo open familiar-chat +``` So, if you were to simulate a request from your cat: diff --git a/packages/cli/src/endo.js b/packages/cli/src/endo.js index 26a71390c4..f8535dc5da 100644 --- a/packages/cli/src/endo.js +++ b/packages/cli/src/endo.js @@ -44,14 +44,17 @@ export const main = async rawArgs => { '-p,--powers ', 'Endowment to give the weblet (a name, NONE, HOST, or ENDO)', ) + .option('-o,--open', 'Open the new web page immediately (weblet)') .action(async (webPageName, programPath, cmd) => { const { bundle: bundleName, powers: powersName = 'NONE', as: partyNames, + open: doOpen, } = cmd.opts(); const { install } = await import('./install.js'); return install({ + doOpen, webPageName, programPath, bundleName, diff --git a/packages/cli/src/install.js b/packages/cli/src/install.js index 2909605e16..8f77183a9e 100644 --- a/packages/cli/src/install.js +++ b/packages/cli/src/install.js @@ -2,6 +2,7 @@ import os from 'os'; +import openWebPage from 'open'; import { E } from '@endo/far'; import { makeReaderRef } from '@endo/daemon'; import bundleSource from '@endo/bundle-source'; @@ -17,6 +18,7 @@ export const install = async ({ powersName, webPageName, programPath, + doOpen, }) => { /** @type {import('@endo/eventual-send').FarRef> | undefined} */ let bundleReaderRef; @@ -54,7 +56,11 @@ export const install = async ({ } else { ({ url: webPageUrl } = await E(party).lookup(webPageName)); } + assert(webPageUrl !== undefined); process.stdout.write(`${webPageUrl}\n`); + if (doOpen) { + openWebPage(webPageUrl); + } } finally { if (temporaryBundleName) { await E(party).remove(temporaryBundleName); From f5b2d3f93ce0cf7244c94cafa3d3c653fe2aeba3 Mon Sep 17 00:00:00 2001 From: Erik Marks Date: Sat, 27 Jan 2024 10:28:19 -0800 Subject: [PATCH 180/234] docs(daemon,cli): Replace outdated references to HOST with SELF --- packages/cli/demo/cat.js | 4 ++-- packages/cli/src/endo.js | 4 ++-- packages/daemon/src/host.js | 8 ++++---- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/cli/demo/cat.js b/packages/cli/demo/cat.js index 69ce79362b..385e5f13f2 100644 --- a/packages/cli/demo/cat.js +++ b/packages/cli/demo/cat.js @@ -4,11 +4,11 @@ // This command will set up the cat page, create a URL, // and open it. // -// > endo open familiar-chat cat.js --powers HOST +// > endo install familiar-chat cat.js --powers SELF // // Thereafter, // -// > endo open fami ar-chat +// > endo open familiar-chat // // To interact with the permission manager, you can mock requests from a fake // guest. diff --git a/packages/cli/src/endo.js b/packages/cli/src/endo.js index f8535dc5da..d043c31fa3 100644 --- a/packages/cli/src/endo.js +++ b/packages/cli/src/endo.js @@ -42,7 +42,7 @@ export const main = async rawArgs => { .option('-b,--bundle ', 'Bundle for a web page (weblet)') .option( '-p,--powers ', - 'Endowment to give the weblet (a name, NONE, HOST, or ENDO)', + 'Endowment to give the weblet (a name, NONE, SELF, or ENDO)', ) .option('-o,--open', 'Open the new web page immediately (weblet)') .action(async (webPageName, programPath, cmd) => { @@ -127,7 +127,7 @@ export const main = async rawArgs => { collect, [], ) - .option('-p,--powers ', 'Name of powers to grant or NONE, HOST, ENDO') + .option('-p,--powers ', 'Name of powers to grant or NONE, SELF, ENDO') .option( '-n,--name ', 'Assigns a name to the result for future reference, persisted between restarts', diff --git a/packages/daemon/src/host.js b/packages/daemon/src/host.js index 2fe576495b..a1a4649eae 100644 --- a/packages/daemon/src/host.js +++ b/packages/daemon/src/host.js @@ -163,7 +163,7 @@ export const makeHostMaker = ({ }; /** - * @param {string | 'NONE' | 'HOST' | 'ENDO'} partyName + * @param {string | 'NONE' | 'SELF' | 'ENDO'} partyName */ const providePowersFormulaIdentifier = async partyName => { let guestFormulaIdentifier = lookupFormulaIdentifierForName(partyName); @@ -241,7 +241,7 @@ export const makeHostMaker = ({ /** * @param {string | 'NEW' | 'MAIN'} workerName * @param {string} importPath - * @param {string | 'NONE' | 'HOST' | 'ENDO'} powersName + * @param {string | 'NONE' | 'SELF' | 'ENDO'} powersName * @param {string} resultName */ const makeUnconfined = async ( @@ -281,7 +281,7 @@ export const makeHostMaker = ({ /** * @param {string | 'MAIN' | 'NEW'} workerName * @param {string} bundleName - * @param {string | 'NONE' | 'HOST' | 'ENDO'} powersName + * @param {string | 'NONE' | 'SELF' | 'ENDO'} powersName * @param {string} resultName */ const makeBundle = async ( @@ -376,7 +376,7 @@ export const makeHostMaker = ({ /** * @param {string} webPageName * @param {string} bundleName - * @param {string | 'NONE' | 'HOST' | 'ENDO'} powersName + * @param {string | 'NONE' | 'SELF' | 'ENDO'} powersName */ const provideWebPage = async (webPageName, bundleName, powersName) => { const bundleFormulaIdentifier = From e7898f05c84e3717312dc48f86716a0c51bd303d Mon Sep 17 00:00:00 2001 From: Erik Marks Date: Mon, 29 Jan 2024 21:55:02 -0800 Subject: [PATCH 181/234] feat(daemon): Add inspector types - Updates INFO-related internals to refer to "inspector" instead of "info". - Adds types for different "known" inspectors (e.g. for `eval` formulas). - Lower-cases known inspector special names (e.g. `bundle`). --- packages/daemon/src/daemon.js | 90 +++++++++++++++++++++------------- packages/daemon/src/host.js | 6 +-- packages/daemon/src/types.d.ts | 15 ++++++ 3 files changed, 74 insertions(+), 37 deletions(-) diff --git a/packages/daemon/src/daemon.js b/packages/daemon/src/daemon.js index 152d36b70e..cb63a4ba62 100644 --- a/packages/daemon/src/daemon.js +++ b/packages/daemon/src/daemon.js @@ -37,7 +37,15 @@ const delay = async (ms, cancelled) => { }); }; -const makeInfo = (type, number, record) => +/** + * Creates an inspector object for a formula. + * + * @param {string} type - The formula type. + * @param {string} number - The formula number. + * @param {Record} record - A mapping from special names to formula values. + * @returns {import('./types.js').EndoInspector} The inspector for the given formula. + */ +const makeInspector = (type, number, record) => Far(`Inspector (${type} ${number})`, { lookup: async petName => { if (!Object.hasOwn(record, petName)) { @@ -385,18 +393,18 @@ const makeEndoBootstrap = ( } else if (formulaIdentifier === 'pet-inspector') { // Behold, unavoidable forward-reference: // eslint-disable-next-line no-use-before-define - const external = makeIdentifiedInspector('pet-store'); + const external = makePetStoreInspector('pet-store'); return { external, internal: undefined }; } else if (formulaIdentifier === 'host') { const storeFormulaIdentifier = 'pet-store'; - const infoFormulaIdentifier = 'pet-inspector'; + const inspectorFormulaIdentifier = 'pet-inspector'; const workerFormulaIdentifier = `worker-id512:${zero512}`; // Behold, recursion: // eslint-disable-next-line no-use-before-define return makeIdentifiedHost( formulaIdentifier, storeFormulaIdentifier, - infoFormulaIdentifier, + inspectorFormulaIdentifier, workerFormulaIdentifier, terminator, ); @@ -428,7 +436,7 @@ const makeEndoBootstrap = ( } else if (formulaType === 'pet-inspector-id512') { // Behold, unavoidable forward-reference: // eslint-disable-next-line no-use-before-define - const external = makeIdentifiedInspector( + const external = makePetStoreInspector( `pet-store-id512:${formulaNumber}`, ); return { external, internal: undefined }; @@ -440,14 +448,14 @@ const makeEndoBootstrap = ( return { external, internal: undefined }; } else if (formulaType === 'host-id512') { const storeFormulaIdentifier = `pet-store-id512:${formulaNumber}`; - const infoFormulaIdentifier = `pet-inspector-id512:${formulaNumber}`; + const inspectorFormulaIdentifier = `pet-inspector-id512:${formulaNumber}`; const workerFormulaIdentifier = `worker-id512:${formulaNumber}`; // Behold, recursion: // eslint-disable-next-line no-use-before-define return makeIdentifiedHost( formulaIdentifier, storeFormulaIdentifier, - infoFormulaIdentifier, + inspectorFormulaIdentifier, workerFormulaIdentifier, terminator, ); @@ -610,12 +618,25 @@ const makeEndoBootstrap = ( makeMailbox, }); - const makeIdentifiedInspector = async petStoreFormulaIdentifier => { + /** + * Creates an inspector for the current party's pet store, used to create + * inspectors for values therein. Notably, can provide references to otherwise + * un-nameable values such as the `MAIN` worker. See `KnownEndoInspectors` for + * more details. + * + * @param {string} petStoreFormulaIdentifier + * @returns {Promise} + */ + const makePetStoreInspector = async petStoreFormulaIdentifier => { const petStore = await provideValueForFormulaIdentifier( petStoreFormulaIdentifier, ); - /** @param {string} petName */ + /** + * @param {string} petName - The pet name to inspect. + * @returns {Promise} An + * inspector for the value of the given pet name. + */ const lookup = async petName => { const formulaIdentifier = petStore.lookup(petName); if (formulaIdentifier === undefined) { @@ -626,26 +647,24 @@ const makeEndoBootstrap = ( if ( ![ 'eval-id512', - 'import-unsafe-id512', - 'import-bundle-id512', + 'make-unconfined-id512', + 'make-bundle-id512', 'guest-id512', 'web-bundle', ].includes(formulaType) ) { - return makeInfo(formulaType, formulaNumber, harden({})); + return makeInspector(formulaType, formulaNumber, harden({})); } const formula = await persistencePowers.readFormula( formulaType, formulaNumber, ); if (formula.type === 'eval') { - return makeInfo( + return makeInspector( formula.type, formulaNumber, harden({ - SOURCE: formula.source, - WORKER: provideValueForFormulaIdentifier(formula.worker), - ENDOWMENTS: Object.fromEntries( + endowments: Object.fromEntries( formula.names.map((name, index) => { return [ name, @@ -653,53 +672,56 @@ const makeEndoBootstrap = ( ]; }), ), + source: formula.source, + worker: provideValueForFormulaIdentifier(formula.worker), }), ); - } else if (formula.type === 'import-unsafe') { - return makeInfo( + } else if (formula.type === 'guest') { + return makeInspector( formula.type, formulaNumber, harden({ - SPECIFIER: formula.type, - WORKER: provideValueForFormulaIdentifier(formula.worker), - POWERS: provideValueForFormulaIdentifier(formula.powers), + host: provideValueForFormulaIdentifier(formula.host), }), ); - } else if (formula.type === 'import-bundle') { - return makeInfo( + } else if (formula.type === 'make-bundle') { + return makeInspector( formula.type, formulaNumber, harden({ - WORKER: provideValueForFormulaIdentifier(formula.worker), - BUNDLE: provideValueForFormulaIdentifier(formula.bundle), - POWERS: provideValueForFormulaIdentifier(formula.powers), + bundle: provideValueForFormulaIdentifier(formula.bundle), + powers: provideValueForFormulaIdentifier(formula.powers), + worker: provideValueForFormulaIdentifier(formula.worker), }), ); - } else if (formula.type === 'guest') { - return makeInfo( + } else if (formula.type === 'make-unconfined') { + return makeInspector( formula.type, formulaNumber, harden({ - HOST: provideValueForFormulaIdentifier(formula.host), + powers: provideValueForFormulaIdentifier(formula.powers), + specifier: formula.type, + worker: provideValueForFormulaIdentifier(formula.worker), }), ); } else if (formula.type === 'web-bundle') { - return makeInfo( + return makeInspector( formula.type, formulaNumber, harden({ - BUNDLE: provideValueForFormulaIdentifier(formula.bundle), - POWERS: provideValueForFormulaIdentifier(formula.powers), + bundle: provideValueForFormulaIdentifier(formula.bundle), + powers: provideValueForFormulaIdentifier(formula.powers), }), ); } // @ts-expect-error this should never occur - return makeInfo(formula.type, formulaNumber, harden({})); + return makeInspector(formula.type, formulaNumber, harden({})); }; + /** @returns {string[]} The list of all names in the pet store. */ const list = () => petStore.list(); - const info = Far('Endo info facet', { + const info = Far('Endo inspector facet', { lookup, list, }); diff --git a/packages/daemon/src/host.js b/packages/daemon/src/host.js index ba212e53d8..da046475d8 100644 --- a/packages/daemon/src/host.js +++ b/packages/daemon/src/host.js @@ -18,14 +18,14 @@ export const makeHostMaker = ({ /** * @param {string} hostFormulaIdentifier * @param {string} storeFormulaIdentifier - * @param {string} infoFormulaIdentifier + * @param {string} inspectorFormulaIdentifier * @param {string} mainWorkerFormulaIdentifier * @param {import('./types.js').Terminator} terminator */ const makeIdentifiedHost = async ( hostFormulaIdentifier, storeFormulaIdentifier, - infoFormulaIdentifier, + inspectorFormulaIdentifier, mainWorkerFormulaIdentifier, terminator, ) => { @@ -60,7 +60,7 @@ export const makeHostMaker = ({ selfFormulaIdentifier: hostFormulaIdentifier, specialNames: { SELF: hostFormulaIdentifier, - INFO: infoFormulaIdentifier, + INFO: inspectorFormulaIdentifier, NONE: 'least-authority', ENDO: 'endo', }, diff --git a/packages/daemon/src/types.d.ts b/packages/daemon/src/types.d.ts index 0d37136b7e..5e0d3095ab 100644 --- a/packages/daemon/src/types.d.ts +++ b/packages/daemon/src/types.d.ts @@ -247,6 +247,21 @@ export interface EndoHost { ): Promise; } +export type EndoInspector = { + lookup: (petName: Record) => Promise; + list: () => Record[]; +}; + +export type KnownEndoInspectors = { + 'eval-id512': EndoInspector<'endowments' | 'source' | 'worker'>; + 'make-unconfined-id512': EndoInspector<'host'>; + 'make-bundle-id512': EndoInspector<'bundle' | 'powers' | 'worker'>; + 'guest-id512': EndoInspector<'bundle' | 'powers'>; + 'web-bundle': EndoInspector<'powers' | 'specifier' | 'worker'>; + // This is an "empty" inspector, in that there is nothing to `lookup()` or `list()`. + [formulaType: string]: EndoInspector; +}; + export type EndoWebBundle = { url: string; bundle: ERef; From d43e1bda55cd216d0199fde8a18bf750275458d1 Mon Sep 17 00:00:00 2001 From: Erik Marks Date: Mon, 29 Jan 2024 22:29:08 -0800 Subject: [PATCH 182/234] test(daemon): Add inspector naming and reuse test --- packages/daemon/test/test-endo.js | 41 +++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/packages/daemon/test/test-endo.js b/packages/daemon/test/test-endo.js index 6e27c37c86..8c54969ece 100644 --- a/packages/daemon/test/test-endo.js +++ b/packages/daemon/test/test-endo.js @@ -694,3 +694,44 @@ test('make a host', async t => { await stop(locator); }); + +test('name and reuse inspector', async t => { + const { promise: cancelled, reject: cancel } = makePromiseKit(); + t.teardown(() => cancel(Error('teardown'))); + const locator = makeLocator('tmp', 'inspector-reuse'); + + await stop(locator).catch(() => {}); + await reset(locator); + await start(locator); + + const { getBootstrap } = await makeEndoClient( + 'client', + locator.sockPath, + cancelled, + ); + const bootstrap = getBootstrap(); + const host = E(bootstrap).host(); + await E(host).provideWorker('worker'); + + const counterPath = path.join(dirname, 'test', 'counter.js'); + await E(host).makeUnconfined('worker', counterPath, 'NONE', 'counter'); + + const inspector = await E(host).evaluate( + 'worker', + 'E(INFO).lookup("counter")', + ['INFO'], + ['INFO'], + 'inspector', + ); + t.regex(String(inspector), /Alleged: Inspector.+make-unconfined/u); + + const worker = await E(host).evaluate( + 'worker', + 'E(inspector).lookup("worker")', + ['inspector'], + ['inspector'], + ); + t.regex(String(worker), /Alleged: EndoWorker/u); + + await stop(locator); +}); From 4b45a7aa473870c80e6e386390129d36054368e8 Mon Sep 17 00:00:00 2001 From: Erik Marks Date: Tue, 30 Jan 2024 09:48:27 -0800 Subject: [PATCH 183/234] test(daemon): Add test for eval-mediated worker names --- packages/daemon/test/test-endo.js | 49 +++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/packages/daemon/test/test-endo.js b/packages/daemon/test/test-endo.js index 8c54969ece..53423186fe 100644 --- a/packages/daemon/test/test-endo.js +++ b/packages/daemon/test/test-endo.js @@ -735,3 +735,52 @@ test('name and reuse inspector', async t => { await stop(locator); }); + +// TODO: This test verifies existing behavior when pet-naming workers. +// This behavior is undesirable. See: https://github.com/endojs/endo/issues/2021 +test('eval-mediated worker name', async t => { + const { promise: cancelled, reject: cancel } = makePromiseKit(); + t.teardown(() => cancel(Error('teardown'))); + const locator = makeLocator('tmp', 'eval-worker-name'); + + await stop(locator).catch(() => {}); + await reset(locator); + await start(locator); + + const { getBootstrap } = await makeEndoClient( + 'client', + locator.sockPath, + cancelled, + ); + const bootstrap = getBootstrap(); + const host = E(bootstrap).host(); + await E(host).provideWorker('worker'); + + const counterPath = path.join(dirname, 'test', 'counter.js'); + await E(host).makeUnconfined('worker', counterPath, 'NONE', 'counter'); + + // We create a petname for the worker of `counter`. + // Note that while `worker === counter-worker`, it doesn't matter here. + const counterWorker = await E(host).evaluate( + 'worker', + 'E(E(INFO).lookup("counter")).lookup("worker")', + ['INFO'], + ['INFO'], + 'counter-worker', + ); + t.regex(String(counterWorker), /Alleged: EndoWorker/u); + + try { + await E(host).evaluate( + 'counter-worker', // Our worker pet name + 'E(counter).incr()', + ['counter'], + ['counter'], + ); + t.fail('should have thrown'); + } catch (error) { + // This is the error that we don't want + t.regex(error.message, /typeof target is "undefined"/u); + await stop(locator); + } +}); From 85cd6e66341daab3b8ae8b856d4b8166d8d9e762 Mon Sep 17 00:00:00 2001 From: Erik Marks Date: Wed, 31 Jan 2024 10:39:15 -0800 Subject: [PATCH 184/234] feat(daemon): Add mailbox petname path lookup Implements mailbox petname path lookup by making the mailbox `lookup()` method variadic. --- packages/daemon/src/mail.js | 17 +++++++++----- packages/daemon/src/types.d.ts | 2 +- packages/daemon/test/test-endo.js | 38 ++++++++++++++++++++++++++++--- 3 files changed, 47 insertions(+), 10 deletions(-) diff --git a/packages/daemon/src/mail.js b/packages/daemon/src/mail.js index 11648f7195..b3241ac2d3 100644 --- a/packages/daemon/src/mail.js +++ b/packages/daemon/src/mail.js @@ -1,5 +1,6 @@ // @ts-check +import { E } from '@endo/far'; import { makePromiseKit } from '@endo/promise-kit'; import { makeChangeTopic } from './pubsub.js'; import { makeIteratorRef } from './reader-ref.js'; @@ -41,16 +42,20 @@ export const makeMailboxMaker = ({ }; /** - * @param {string} petName + * @param {...string} petNamePath - A sequence of pet names. + * @returns {Promise} The value resolved by the pet name path. */ - const lookup = async petName => { - const formulaIdentifier = lookupFormulaIdentifierForName(petName); + const lookup = async (...petNamePath) => { + const [headName, ...tailNames] = petNamePath; + const formulaIdentifier = lookupFormulaIdentifierForName(headName); if (formulaIdentifier === undefined) { - throw new TypeError(`Unknown pet name: ${q(petName)}`); + throw new TypeError(`Unknown pet name: ${q(headName)}`); } // Behold, recursion: - // eslint-disable-next-line no-use-before-define - return provideValueForFormulaIdentifier(formulaIdentifier); + return tailNames.reduce( + (currentValue, petName) => E(currentValue).lookup(petName), + provideValueForFormulaIdentifier(formulaIdentifier), + ); }; const terminate = async petName => { diff --git a/packages/daemon/src/types.d.ts b/packages/daemon/src/types.d.ts index 28a7de4a75..77f1b0eb83 100644 --- a/packages/daemon/src/types.d.ts +++ b/packages/daemon/src/types.d.ts @@ -212,7 +212,7 @@ export interface EndoGuest { export interface EndoHost { listMessages(): Promise>; followMessages(): ERef>; - lookup(petName: string): Promise; + lookup(...petNamePath: string[]): Promise; resolve(requestNumber: number, petName: string); reject(requestNumber: number, message: string); reverseLookup(ref: object): Promise>; diff --git a/packages/daemon/test/test-endo.js b/packages/daemon/test/test-endo.js index 7ece10de73..5a106bef1c 100644 --- a/packages/daemon/test/test-endo.js +++ b/packages/daemon/test/test-endo.js @@ -87,7 +87,7 @@ test('spawn and evaluate', async t => { const host = E(bootstrap).host(); await E(host).makeWorker('w1'); const ten = await E(host).evaluate('w1', '10', [], []); - t.is(10, ten); + t.is(ten, 10); await stop(locator); }); @@ -109,7 +109,7 @@ test('anonymous spawn and evaluate', async t => { const bootstrap = getBootstrap(); const host = E(bootstrap).host(); const ten = await E(host).evaluate('MAIN', '10', [], []); - t.is(10, ten); + t.is(ten, 10); await stop(locator); }); @@ -135,7 +135,7 @@ test('persist spawn and evaluation', async t => { await E(host).makeWorker('w1'); const ten = await E(host).evaluate('w1', '10', [], [], 'ten'); - t.is(10, ten); + t.is(ten, 10); const twenty = await E(host).evaluate( 'w1', 'number * 2', @@ -802,3 +802,35 @@ test('eval-mediated worker name', async t => { await stop(locator); } }); + +test('lookup with petname path', async t => { + const { promise: cancelled, reject: cancel } = makePromiseKit(); + t.teardown(() => cancel(Error('teardown'))); + const locator = makeLocator('tmp', 'path-lookup'); + + await stop(locator).catch(() => {}); + await reset(locator); + await start(locator); + + const { getBootstrap } = await makeEndoClient( + 'client', + locator.sockPath, + cancelled, + ); + const bootstrap = getBootstrap(); + const host = E(bootstrap).host(); + await E(host).provideGuest('guest'); + + const ten = await E(host).evaluate('MAIN', '10', [], [], 'ten'); + t.is(ten, 10); + + const resolvedValue = await E(host).evaluate( + 'MAIN', + 'E(SELF).lookup("guest", "HOST", "ten")', + ['SELF'], + ['SELF'], + ); + t.is(resolvedValue, ten); + + await stop(locator); +}); From a4e96718da48d799d6b6382e7ecef3ab10cc268a Mon Sep 17 00:00:00 2001 From: Erik Marks Date: Wed, 31 Jan 2024 14:37:00 -0800 Subject: [PATCH 185/234] test(daemon): Add tests for lookup with petname paths Adds the following test cases for mailbox `lookup()`: - A single petname (i.e. path of depth 1) - A path of depth greater than 1 using INFO - A path of depth greater than 1 using a caplet with its own `lookup` - A path including an intermediary value without `lookup` (i.e. the error case) --- packages/daemon/test/lookup.js | 9 +++ packages/daemon/test/test-endo.js | 100 ++++++++++++++++++++++++++++-- 2 files changed, 104 insertions(+), 5 deletions(-) create mode 100644 packages/daemon/test/lookup.js diff --git a/packages/daemon/test/lookup.js b/packages/daemon/test/lookup.js new file mode 100644 index 0000000000..9398e7e6af --- /dev/null +++ b/packages/daemon/test/lookup.js @@ -0,0 +1,9 @@ +import { Far } from '@endo/far'; + +export const make = () => { + return Far('Lookup', { + lookup(petName) { + return `Looked up: ${petName}`; + }, + }); +}; diff --git a/packages/daemon/test/test-endo.js b/packages/daemon/test/test-endo.js index 5a106bef1c..59918a57dd 100644 --- a/packages/daemon/test/test-endo.js +++ b/packages/daemon/test/test-endo.js @@ -803,10 +803,10 @@ test('eval-mediated worker name', async t => { } }); -test('lookup with petname path', async t => { +test('lookup with single petname', async t => { const { promise: cancelled, reject: cancel } = makePromiseKit(); t.teardown(() => cancel(Error('teardown'))); - const locator = makeLocator('tmp', 'path-lookup'); + const locator = makeLocator('tmp', 'lookup-single-petname'); await stop(locator).catch(() => {}); await reset(locator); @@ -820,17 +820,107 @@ test('lookup with petname path', async t => { const bootstrap = getBootstrap(); const host = E(bootstrap).host(); await E(host).provideGuest('guest'); - const ten = await E(host).evaluate('MAIN', '10', [], [], 'ten'); t.is(ten, 10); const resolvedValue = await E(host).evaluate( 'MAIN', - 'E(SELF).lookup("guest", "HOST", "ten")', + 'E(SELF).lookup("ten")', + ['SELF'], + ['SELF'], + ); + t.is(resolvedValue, 10); + + await stop(locator); +}); + +test('lookup with petname path (inspector)', async t => { + const { promise: cancelled, reject: cancel } = makePromiseKit(); + t.teardown(() => cancel(Error('teardown'))); + const locator = makeLocator('tmp', 'lookup-petname-path-inspector'); + + await stop(locator).catch(() => {}); + await reset(locator); + await start(locator); + + const { getBootstrap } = await makeEndoClient( + 'client', + locator.sockPath, + cancelled, + ); + const bootstrap = getBootstrap(); + const host = E(bootstrap).host(); + await E(host).evaluate('MAIN', '10', [], [], 'ten'); + + const resolvedValue = await E(host).evaluate( + 'MAIN', + 'E(SELF).lookup("INFO", "ten", "source")', ['SELF'], ['SELF'], ); - t.is(resolvedValue, ten); + t.is(resolvedValue, '10'); + + await stop(locator); +}); + +test('lookup with petname path (caplet with lookup method)', async t => { + const { promise: cancelled, reject: cancel } = makePromiseKit(); + t.teardown(() => cancel(Error('teardown'))); + const locator = makeLocator('tmp', 'lookup-petname-path-caplet'); + + await stop(locator).catch(() => {}); + await reset(locator); + await start(locator); + + const { getBootstrap } = await makeEndoClient( + 'client', + locator.sockPath, + cancelled, + ); + const bootstrap = getBootstrap(); + const host = E(bootstrap).host(); + + const lookupPath = path.join(dirname, 'test', 'lookup.js'); + await E(host).makeUnconfined('MAIN', lookupPath, 'NONE', 'lookup'); + + const resolvedValue = await E(host).evaluate( + 'MAIN', + 'E(SELF).lookup("lookup", "name")', + ['SELF'], + ['SELF'], + ); + t.is(resolvedValue, 'Looked up: name'); + + await stop(locator); +}); + +test('lookup with petname path (value has no lookup method)', async t => { + const { promise: cancelled, reject: cancel } = makePromiseKit(); + t.teardown(() => cancel(Error('teardown'))); + const locator = makeLocator('tmp', 'lookup-petname-path-no-method'); + + await stop(locator).catch(() => {}); + await reset(locator); + await start(locator); + + const { getBootstrap } = await makeEndoClient( + 'client', + locator.sockPath, + cancelled, + ); + const bootstrap = getBootstrap(); + const host = E(bootstrap).host(); + + await E(host).evaluate('MAIN', '10', [], [], 'ten'); + await t.throwsAsync( + E(host).evaluate( + 'MAIN', + 'E(SELF).lookup("ten", "someName")', + ['SELF'], + ['SELF'], + ), + { message: 'target has no method "lookup", has []' }, + ); await stop(locator); }); From 3f575bafb31b89830cbd3326d05a086acdeecdc2 Mon Sep 17 00:00:00 2001 From: Erik Marks Date: Thu, 1 Feb 2024 09:19:36 -0800 Subject: [PATCH 186/234] refactor(cli): Create dedicated directory for CLI commmands The completely flat `src` directory structure of the CLI made its structure difficult to comprehend. All files that exclusively implement a CLI command have therefore been moved into a new directory, `src/commands`. --- packages/cli/src/{ => commands}/adopt.js | 2 +- packages/cli/src/{ => commands}/bundle.js | 2 +- packages/cli/src/{ => commands}/cat.js | 2 +- packages/cli/src/{ => commands}/dismiss.js | 2 +- packages/cli/src/{ => commands}/eval.js | 2 +- packages/cli/src/{ => commands}/follow.js | 2 +- packages/cli/src/{ => commands}/inbox.js | 4 +- packages/cli/src/{ => commands}/install.js | 4 +- packages/cli/src/{ => commands}/kill.js | 2 +- packages/cli/src/{ => commands}/list.js | 2 +- packages/cli/src/{ => commands}/log.js | 2 +- packages/cli/src/{ => commands}/make.js | 4 +- packages/cli/src/{ => commands}/mkguest.js | 2 +- packages/cli/src/{ => commands}/mkhost.js | 2 +- packages/cli/src/{ => commands}/open.js | 2 +- packages/cli/src/{ => commands}/ping.js | 2 +- packages/cli/src/{ => commands}/reject.js | 2 +- packages/cli/src/{ => commands}/remove.js | 2 +- packages/cli/src/{ => commands}/rename.js | 2 +- packages/cli/src/{ => commands}/request.js | 2 +- packages/cli/src/{ => commands}/resolve.js | 2 +- packages/cli/src/{ => commands}/run.js | 2 +- packages/cli/src/{ => commands}/send.js | 4 +- packages/cli/src/{ => commands}/show.js | 2 +- packages/cli/src/{ => commands}/spawn.js | 2 +- packages/cli/src/{ => commands}/store.js | 2 +- packages/cli/src/endo.js | 52 +++++++++++----------- 27 files changed, 56 insertions(+), 56 deletions(-) rename packages/cli/src/{ => commands}/adopt.js (89%) rename packages/cli/src/{ => commands}/bundle.js (93%) rename packages/cli/src/{ => commands}/cat.js (90%) rename packages/cli/src/{ => commands}/dismiss.js (89%) rename packages/cli/src/{ => commands}/eval.js (95%) rename packages/cli/src/{ => commands}/follow.js (89%) rename packages/cli/src/{ => commands}/inbox.js (92%) rename packages/cli/src/{ => commands}/install.js (95%) rename packages/cli/src/{ => commands}/kill.js (83%) rename packages/cli/src/{ => commands}/list.js (87%) rename packages/cli/src/{ => commands}/log.js (97%) rename packages/cli/src/{ => commands}/make.js (96%) rename packages/cli/src/{ => commands}/mkguest.js (85%) rename packages/cli/src/{ => commands}/mkhost.js (87%) rename packages/cli/src/{ => commands}/open.js (89%) rename packages/cli/src/{ => commands}/ping.js (93%) rename packages/cli/src/{ => commands}/reject.js (89%) rename packages/cli/src/{ => commands}/remove.js (86%) rename packages/cli/src/{ => commands}/rename.js (84%) rename packages/cli/src/{ => commands}/request.js (89%) rename packages/cli/src/{ => commands}/resolve.js (89%) rename packages/cli/src/{ => commands}/run.js (98%) rename packages/cli/src/{ => commands}/send.js (78%) rename packages/cli/src/{ => commands}/show.js (86%) rename packages/cli/src/{ => commands}/spawn.js (88%) rename packages/cli/src/{ => commands}/store.js (92%) diff --git a/packages/cli/src/adopt.js b/packages/cli/src/commands/adopt.js similarity index 89% rename from packages/cli/src/adopt.js rename to packages/cli/src/commands/adopt.js index 8cbb812dde..1f7eac98e4 100644 --- a/packages/cli/src/adopt.js +++ b/packages/cli/src/commands/adopt.js @@ -1,7 +1,7 @@ /* global process */ import os from 'os'; import { E } from '@endo/far'; -import { withEndoParty } from './context.js'; +import { withEndoParty } from '../context.js'; export const adoptCommand = async ({ messageNumberText, diff --git a/packages/cli/src/bundle.js b/packages/cli/src/commands/bundle.js similarity index 93% rename from packages/cli/src/bundle.js rename to packages/cli/src/commands/bundle.js index d168e586e6..2ba8c60483 100644 --- a/packages/cli/src/bundle.js +++ b/packages/cli/src/commands/bundle.js @@ -4,7 +4,7 @@ import os from 'os'; import { E } from '@endo/far'; import bundleSource from '@endo/bundle-source'; import { makeReaderRef } from '@endo/daemon'; -import { withEndoParty } from './context.js'; +import { withEndoParty } from '../context.js'; const textEncoder = new TextEncoder(); diff --git a/packages/cli/src/cat.js b/packages/cli/src/commands/cat.js similarity index 90% rename from packages/cli/src/cat.js rename to packages/cli/src/commands/cat.js index ac20bd1ded..250093fdb4 100644 --- a/packages/cli/src/cat.js +++ b/packages/cli/src/commands/cat.js @@ -3,7 +3,7 @@ import os from 'os'; import { E } from '@endo/far'; import { makeRefReader } from '@endo/daemon'; -import { withEndoParty } from './context.js'; +import { withEndoParty } from '../context.js'; export const cat = async ({ name, partyNames }) => withEndoParty(partyNames, { os, process }, async ({ party }) => { diff --git a/packages/cli/src/dismiss.js b/packages/cli/src/commands/dismiss.js similarity index 89% rename from packages/cli/src/dismiss.js rename to packages/cli/src/commands/dismiss.js index 2bcf6b22a1..7488b33f61 100644 --- a/packages/cli/src/dismiss.js +++ b/packages/cli/src/commands/dismiss.js @@ -1,7 +1,7 @@ /* global process */ import os from 'os'; import { E } from '@endo/far'; -import { withEndoParty } from './context.js'; +import { withEndoParty } from '../context.js'; export const dismissCommand = async ({ cancel, diff --git a/packages/cli/src/eval.js b/packages/cli/src/commands/eval.js similarity index 95% rename from packages/cli/src/eval.js rename to packages/cli/src/commands/eval.js index d00c705928..b4341fb37c 100644 --- a/packages/cli/src/eval.js +++ b/packages/cli/src/commands/eval.js @@ -1,7 +1,7 @@ /* global process */ import os from 'os'; import { E } from '@endo/far'; -import { withEndoParty } from './context.js'; +import { withEndoParty } from '../context.js'; export const evalCommand = async ({ source, diff --git a/packages/cli/src/follow.js b/packages/cli/src/commands/follow.js similarity index 89% rename from packages/cli/src/follow.js rename to packages/cli/src/commands/follow.js index fdc2d17344..cf9cacbd3a 100644 --- a/packages/cli/src/follow.js +++ b/packages/cli/src/commands/follow.js @@ -3,7 +3,7 @@ import os from 'os'; import { E } from '@endo/far'; import { makeRefIterator } from '@endo/daemon'; -import { withEndoParty } from './context.js'; +import { withEndoParty } from '../context.js'; export const followCommand = async ({ name, partyNames }) => withEndoParty(partyNames, { os, process }, async ({ party }) => { diff --git a/packages/cli/src/inbox.js b/packages/cli/src/commands/inbox.js similarity index 92% rename from packages/cli/src/inbox.js rename to packages/cli/src/commands/inbox.js index 7f9cfe0e8e..88084cca94 100644 --- a/packages/cli/src/inbox.js +++ b/packages/cli/src/commands/inbox.js @@ -3,8 +3,8 @@ import os from 'os'; import { E } from '@endo/far'; import { makeRefIterator } from '@endo/daemon'; -import { withEndoParty } from './context.js'; -import { formatMessage } from './message-format.js'; +import { withEndoParty } from '../context.js'; +import { formatMessage } from '../message-format.js'; export const inbox = async ({ cancel, diff --git a/packages/cli/src/install.js b/packages/cli/src/commands/install.js similarity index 95% rename from packages/cli/src/install.js rename to packages/cli/src/commands/install.js index 8f77183a9e..39c56cafba 100644 --- a/packages/cli/src/install.js +++ b/packages/cli/src/commands/install.js @@ -7,8 +7,8 @@ import { E } from '@endo/far'; import { makeReaderRef } from '@endo/daemon'; import bundleSource from '@endo/bundle-source'; -import { withEndoParty } from './context.js'; -import { randomHex16 } from './random.js'; +import { withEndoParty } from '../context.js'; +import { randomHex16 } from '../random.js'; const textEncoder = new TextEncoder(); diff --git a/packages/cli/src/kill.js b/packages/cli/src/commands/kill.js similarity index 83% rename from packages/cli/src/kill.js rename to packages/cli/src/commands/kill.js index e87632ee88..1de03a9781 100644 --- a/packages/cli/src/kill.js +++ b/packages/cli/src/commands/kill.js @@ -1,7 +1,7 @@ /* global process */ import os from 'os'; import { E } from '@endo/far'; -import { withEndoParty } from './context.js'; +import { withEndoParty } from '../context.js'; export const killCommand = async ({ name, partyNames }) => withEndoParty(partyNames, { os, process }, async ({ party }) => { diff --git a/packages/cli/src/list.js b/packages/cli/src/commands/list.js similarity index 87% rename from packages/cli/src/list.js rename to packages/cli/src/commands/list.js index 1172a19a81..d190a3caf3 100644 --- a/packages/cli/src/list.js +++ b/packages/cli/src/commands/list.js @@ -1,7 +1,7 @@ /* global process */ import os from 'os'; import { E } from '@endo/far'; -import { withEndoParty } from './context.js'; +import { withEndoParty } from '../context.js'; export const list = async ({ partyNames }) => withEndoParty(partyNames, { os, process }, async ({ party }) => { diff --git a/packages/cli/src/log.js b/packages/cli/src/commands/log.js similarity index 97% rename from packages/cli/src/log.js rename to packages/cli/src/commands/log.js index c39d6ce0b6..1338fa851e 100644 --- a/packages/cli/src/log.js +++ b/packages/cli/src/commands/log.js @@ -8,7 +8,7 @@ import { makePromiseKit } from '@endo/promise-kit'; import { makeEndoClient } from '@endo/daemon'; import { whereEndoState, whereEndoSock } from '@endo/where'; import { E } from '@endo/far'; -import { withInterrupt } from './context.js'; +import { withInterrupt } from '../context.js'; const delay = async (ms, cancelled) => { // Do not attempt to set up a timer if already cancelled. diff --git a/packages/cli/src/make.js b/packages/cli/src/commands/make.js similarity index 96% rename from packages/cli/src/make.js rename to packages/cli/src/commands/make.js index dbf9bf1c0c..15ebe99850 100644 --- a/packages/cli/src/make.js +++ b/packages/cli/src/commands/make.js @@ -6,8 +6,8 @@ import path from 'path'; import bundleSource from '@endo/bundle-source'; import { makeReaderRef } from '@endo/daemon'; import { E } from '@endo/far'; -import { withEndoParty } from './context.js'; -import { randomHex16 } from './random.js'; +import { withEndoParty } from '../context.js'; +import { randomHex16 } from '../random.js'; const textEncoder = new TextEncoder(); diff --git a/packages/cli/src/mkguest.js b/packages/cli/src/commands/mkguest.js similarity index 85% rename from packages/cli/src/mkguest.js rename to packages/cli/src/commands/mkguest.js index 5b513da270..8e59ec4de6 100644 --- a/packages/cli/src/mkguest.js +++ b/packages/cli/src/commands/mkguest.js @@ -1,7 +1,7 @@ /* global process */ import os from 'os'; import { E } from '@endo/far'; -import { withEndoParty } from './context.js'; +import { withEndoParty } from '../context.js'; export const mkguest = async ({ name, partyNames }) => withEndoParty(partyNames, { os, process }, async ({ party }) => { diff --git a/packages/cli/src/mkhost.js b/packages/cli/src/commands/mkhost.js similarity index 87% rename from packages/cli/src/mkhost.js rename to packages/cli/src/commands/mkhost.js index 9636596c65..fd733452e1 100644 --- a/packages/cli/src/mkhost.js +++ b/packages/cli/src/commands/mkhost.js @@ -1,7 +1,7 @@ /* global process */ import os from 'os'; import { E } from '@endo/far'; -import { withEndoParty } from './context.js'; +import { withEndoParty } from '../context.js'; export const mkhost = async ({ cancel, diff --git a/packages/cli/src/open.js b/packages/cli/src/commands/open.js similarity index 89% rename from packages/cli/src/open.js rename to packages/cli/src/commands/open.js index b7bd57c1d1..eae3944176 100644 --- a/packages/cli/src/open.js +++ b/packages/cli/src/commands/open.js @@ -5,7 +5,7 @@ import os from 'os'; import openWebPage from 'open'; import { E } from '@endo/far'; -import { withEndoParty } from './context.js'; +import { withEndoParty } from '../context.js'; export const open = async ({ webPageName, partyNames }) => { await withEndoParty(partyNames, { os, process }, async ({ party }) => { diff --git a/packages/cli/src/ping.js b/packages/cli/src/commands/ping.js similarity index 93% rename from packages/cli/src/ping.js rename to packages/cli/src/commands/ping.js index c7ef87a0e3..2d7ac74fef 100644 --- a/packages/cli/src/ping.js +++ b/packages/cli/src/commands/ping.js @@ -3,7 +3,7 @@ import os from 'os'; import { E } from '@endo/far'; import { whereEndoSock } from '@endo/where'; import { makeEndoClient } from '@endo/daemon'; -import { withInterrupt } from './context.js'; +import { withInterrupt } from '../context.js'; export const ping = () => withInterrupt(async ({ cancel, cancelled }) => { diff --git a/packages/cli/src/reject.js b/packages/cli/src/commands/reject.js similarity index 89% rename from packages/cli/src/reject.js rename to packages/cli/src/commands/reject.js index 3cad299696..fda70fe0e2 100644 --- a/packages/cli/src/reject.js +++ b/packages/cli/src/commands/reject.js @@ -1,7 +1,7 @@ /* global process */ import os from 'os'; import { E } from '@endo/far'; -import { withEndoParty } from './context.js'; +import { withEndoParty } from '../context.js'; export const rejectCommand = async ({ cancel, diff --git a/packages/cli/src/remove.js b/packages/cli/src/commands/remove.js similarity index 86% rename from packages/cli/src/remove.js rename to packages/cli/src/commands/remove.js index 6c07c1c356..10a5cc7202 100644 --- a/packages/cli/src/remove.js +++ b/packages/cli/src/commands/remove.js @@ -1,7 +1,7 @@ /* global process */ import os from 'os'; import { E } from '@endo/far'; -import { withEndoParty } from './context.js'; +import { withEndoParty } from '../context.js'; export const remove = async ({ cancel, diff --git a/packages/cli/src/rename.js b/packages/cli/src/commands/rename.js similarity index 84% rename from packages/cli/src/rename.js rename to packages/cli/src/commands/rename.js index 02107b3323..8874f3202e 100644 --- a/packages/cli/src/rename.js +++ b/packages/cli/src/commands/rename.js @@ -1,7 +1,7 @@ /* global process */ import os from 'os'; import { E } from '@endo/far'; -import { withEndoParty } from './context.js'; +import { withEndoParty } from '../context.js'; export const rename = async ({ fromName, toName, partyNames }) => withEndoParty(partyNames, { os, process }, async ({ party }) => { diff --git a/packages/cli/src/request.js b/packages/cli/src/commands/request.js similarity index 89% rename from packages/cli/src/request.js rename to packages/cli/src/commands/request.js index 5d17df95a4..2b7b129381 100644 --- a/packages/cli/src/request.js +++ b/packages/cli/src/commands/request.js @@ -1,7 +1,7 @@ /* global process */ import os from 'os'; import { E } from '@endo/far'; -import { withEndoParty } from './context.js'; +import { withEndoParty } from '../context.js'; export const request = async ({ cancel, diff --git a/packages/cli/src/resolve.js b/packages/cli/src/commands/resolve.js similarity index 89% rename from packages/cli/src/resolve.js rename to packages/cli/src/commands/resolve.js index 5bdb1a5d7f..ea9f05aad9 100644 --- a/packages/cli/src/resolve.js +++ b/packages/cli/src/commands/resolve.js @@ -1,7 +1,7 @@ /* global process */ import os from 'os'; import { E } from '@endo/far'; -import { withEndoParty } from './context.js'; +import { withEndoParty } from '../context.js'; export const resolveCommand = async ({ requestNumberText, diff --git a/packages/cli/src/run.js b/packages/cli/src/commands/run.js similarity index 98% rename from packages/cli/src/run.js rename to packages/cli/src/commands/run.js index 869515d805..ba8f051de2 100644 --- a/packages/cli/src/run.js +++ b/packages/cli/src/commands/run.js @@ -4,7 +4,7 @@ import os from 'os'; import { E, Far } from '@endo/far'; import bundleSource from '@endo/bundle-source'; -import { withEndoParty } from './context.js'; +import { withEndoParty } from '../context.js'; const endowments = harden({ assert, diff --git a/packages/cli/src/send.js b/packages/cli/src/commands/send.js similarity index 78% rename from packages/cli/src/send.js rename to packages/cli/src/commands/send.js index 203850aea5..a0ae9dc67f 100644 --- a/packages/cli/src/send.js +++ b/packages/cli/src/commands/send.js @@ -1,8 +1,8 @@ /* global process */ import os from 'os'; import { E } from '@endo/far'; -import { withEndoParty } from './context.js'; -import { parseMessage } from './message-parse.js'; +import { withEndoParty } from '../context.js'; +import { parseMessage } from '../message-parse.js'; export const send = async ({ message, partyName, partyNames }) => { const { strings, edgeNames, petNames } = parseMessage(message); diff --git a/packages/cli/src/show.js b/packages/cli/src/commands/show.js similarity index 86% rename from packages/cli/src/show.js rename to packages/cli/src/commands/show.js index 5dd98e72f7..554da43b16 100644 --- a/packages/cli/src/show.js +++ b/packages/cli/src/commands/show.js @@ -1,7 +1,7 @@ /* global process */ import os from 'os'; import { E } from '@endo/far'; -import { withEndoParty } from './context.js'; +import { withEndoParty } from '../context.js'; export const show = async ({ cancel, cancelled, sockPath, name, partyNames }) => withEndoParty(partyNames, { os, process }, async ({ party }) => { diff --git a/packages/cli/src/spawn.js b/packages/cli/src/commands/spawn.js similarity index 88% rename from packages/cli/src/spawn.js rename to packages/cli/src/commands/spawn.js index 2420dc1501..6418ad3326 100644 --- a/packages/cli/src/spawn.js +++ b/packages/cli/src/commands/spawn.js @@ -2,7 +2,7 @@ /* global process */ import os from 'os'; import { E } from '@endo/far'; -import { withEndoParty } from './context.js'; +import { withEndoParty } from '../context.js'; export const spawn = async ({ cancel, diff --git a/packages/cli/src/store.js b/packages/cli/src/commands/store.js similarity index 92% rename from packages/cli/src/store.js rename to packages/cli/src/commands/store.js index 71881bbd6e..406bd14061 100644 --- a/packages/cli/src/store.js +++ b/packages/cli/src/commands/store.js @@ -6,7 +6,7 @@ import { makeNodeReader } from '@endo/stream-node'; import { makeReaderRef } from '@endo/daemon'; import { E } from '@endo/far'; -import { withEndoParty } from './context.js'; +import { withEndoParty } from '../context.js'; export const store = async ({ storablePath, name, partyNames }) => { const nodeReadStream = fs.createReadStream(storablePath); diff --git a/packages/cli/src/endo.js b/packages/cli/src/endo.js index d043c31fa3..c624f328ee 100644 --- a/packages/cli/src/endo.js +++ b/packages/cli/src/endo.js @@ -52,7 +52,7 @@ export const main = async rawArgs => { as: partyNames, open: doOpen, } = cmd.opts(); - const { install } = await import('./install.js'); + const { install } = await import('./commands/install.js'); return install({ doOpen, webPageName, @@ -74,7 +74,7 @@ export const main = async rawArgs => { ) .action(async (webPageName, cmd) => { const { as: partyNames } = cmd.opts(); - const { open } = await import('./open.js'); + const { open } = await import('./commands/open.js'); return open({ webPageName, partyNames, @@ -105,7 +105,7 @@ export const main = async rawArgs => { UNCONFINED: importPath, powers: powersName = 'NONE', } = cmd.opts(); - const { run } = await import('./run.js'); + const { run } = await import('./commands/run.js'); return run({ filePath, args, @@ -145,7 +145,7 @@ export const main = async rawArgs => { as: partyNames, powers: powersName = 'NONE', } = cmd.opts(); - const { makeCommand } = await import('./make.js'); + const { makeCommand } = await import('./commands/make.js'); return makeCommand({ filePath, importPath, @@ -169,7 +169,7 @@ export const main = async rawArgs => { .description('read messages') .action(async cmd => { const { as: partyNames, follow } = cmd.opts(); - const { inbox } = await import('./inbox.js'); + const { inbox } = await import('./commands/inbox.js'); return inbox({ follow, partyNames }); }); @@ -196,7 +196,7 @@ export const main = async rawArgs => { as: partyNames, to: toName = 'HOST', } = cmd.opts(); - const { request } = await import('./request.js'); + const { request } = await import('./commands/request.js'); return request({ toName, description, resultName, partyNames }); }); @@ -211,7 +211,7 @@ export const main = async rawArgs => { ) .action(async (requestNumberText, resolutionName, cmd) => { const { as: partyNames } = cmd.opts(); - const { resolveCommand } = await import('./resolve.js'); + const { resolveCommand } = await import('./commands/resolve.js'); return resolveCommand({ requestNumberText, resolutionName, @@ -230,7 +230,7 @@ export const main = async rawArgs => { ) .action(async (requestNumberText, message, cmd) => { const { as: partyNames } = cmd.opts(); - const { rejectCommand } = await import('./reject.js'); + const { rejectCommand } = await import('./commands/reject.js'); return rejectCommand({ requestNumberText, message, @@ -249,7 +249,7 @@ export const main = async rawArgs => { ) .action(async (partyName, message, cmd) => { const { as: partyNames } = cmd.opts(); - const { send } = await import('./send.js'); + const { send } = await import('./commands/send.js'); return send({ message, partyName, partyNames }); }); @@ -268,7 +268,7 @@ export const main = async rawArgs => { .description('accept a @value from a message') .action(async (messageNumberText, edgeName, cmd) => { const { name = edgeName, as: partyNames } = cmd.opts(); - const { adoptCommand } = await import('./adopt.js'); + const { adoptCommand } = await import('./commands/adopt.js'); return adoptCommand({ messageNumberText, edgeName, @@ -288,7 +288,7 @@ export const main = async rawArgs => { ) .action(async (messageNumberText, cmd) => { const { as: partyNames } = cmd.opts(); - const { dismissCommand } = await import('./dismiss.js'); + const { dismissCommand } = await import('./commands/dismiss.js'); return dismissCommand({ messageNumberText, partyNames, @@ -306,7 +306,7 @@ export const main = async rawArgs => { .description('show names') .action(async cmd => { const { as: partyNames } = cmd.opts(); - const { list } = await import('./list.js'); + const { list } = await import('./commands/list.js'); return list({ partyNames }); }); @@ -321,7 +321,7 @@ export const main = async rawArgs => { ) .action(async (petNames, cmd) => { const { as: partyNames } = cmd.opts(); - const { remove } = await import('./remove.js'); + const { remove } = await import('./commands/remove.js'); return remove({ petNames, partyNames }); }); @@ -336,7 +336,7 @@ export const main = async rawArgs => { ) .action(async (fromName, toName, cmd) => { const { as: partyNames } = cmd.opts(); - const { rename } = await import('./rename.js'); + const { rename } = await import('./commands/rename.js'); return rename({ fromName, toName, partyNames }); }); @@ -351,7 +351,7 @@ export const main = async rawArgs => { ) .action(async (name, cmd) => { const { as: partyNames } = cmd.opts(); - const { show } = await import('./show.js'); + const { show } = await import('./commands/show.js'); return show({ name, partyNames }); }); @@ -366,7 +366,7 @@ export const main = async rawArgs => { .description('subscribe to a stream of values') .action(async (name, cmd) => { const { as: partyNames } = cmd.opts(); - const { followCommand } = await import('./follow.js'); + const { followCommand } = await import('./commands/follow.js'); return followCommand({ name, partyNames }); }); @@ -381,7 +381,7 @@ export const main = async rawArgs => { ) .action(async (name, cmd) => { const { as: partyNames } = cmd.opts(); - const { cat } = await import('./cat.js'); + const { cat } = await import('./commands/cat.js'); return cat({ name, partyNames }); }); @@ -400,7 +400,7 @@ export const main = async rawArgs => { ) .action(async (storablePath, cmd) => { const { name, as: partyNames } = cmd.opts(); - const { store } = await import('./store.js'); + const { store } = await import('./commands/store.js'); return store({ storablePath, name, @@ -431,7 +431,7 @@ export const main = async rawArgs => { worker: workerName = 'MAIN', as: partyNames, } = cmd.opts(); - const { evalCommand } = await import('./eval.js'); + const { evalCommand } = await import('./commands/eval.js'); return evalCommand({ source, names, @@ -452,7 +452,7 @@ export const main = async rawArgs => { ) .action(async (petNames, cmd) => { const { as: partyNames } = cmd.opts(); - const { spawn } = await import('./spawn.js'); + const { spawn } = await import('./commands/spawn.js'); return spawn({ petNames, partyNames }); }); @@ -468,7 +468,7 @@ export const main = async rawArgs => { .option('-n,--name ', 'Store the bundle into Endo') .action(async (applicationPath, cmd) => { const { name: bundleName, as: partyNames } = cmd.opts(); - const { bundleCommand } = await import('./bundle.js'); + const { bundleCommand } = await import('./commands/bundle.js'); return bundleCommand({ applicationPath, bundleName, @@ -487,7 +487,7 @@ export const main = async rawArgs => { .description('makes a separate mailbox and storage for you') .action(async (name, cmd) => { const { as: partyNames } = cmd.opts(); - const { mkhost } = await import('./mkhost.js'); + const { mkhost } = await import('./commands/mkhost.js'); return mkhost({ name, partyNames }); }); @@ -502,7 +502,7 @@ export const main = async rawArgs => { .description('makes a mailbox and storage for a guest (peer or program)') .action(async (name, cmd) => { const { as: partyNames } = cmd.opts(); - const { mkguest } = await import('./mkguest.js'); + const { mkguest } = await import('./commands/mkguest.js'); return mkguest({ name, partyNames }); }); @@ -517,7 +517,7 @@ export const main = async rawArgs => { .description('terminate a value and its deps, recovering resources') .action(async (name, cmd) => { const { as: partyNames } = cmd.opts(); - const { killCommand } = await import('./kill.js'); + const { killCommand } = await import('./commands/kill.js'); return killCommand({ name, partyNames }); }); @@ -623,7 +623,7 @@ export const main = async rawArgs => { .description('writes out the daemon log, optionally following updates') .action(async cmd => { const { follow, ping } = cmd.opts(); - const { log: logCommand } = await import('./log.js'); + const { log: logCommand } = await import('./commands/log.js'); await logCommand({ follow, ping }); }); @@ -631,7 +631,7 @@ export const main = async rawArgs => { .command('ping') .description('prints ok if the daemon is responsive') .action(async _cmd => { - const { ping } = await import('./ping.js'); + const { ping } = await import('./commands/ping.js'); await ping(); }); From 21ebc3411bd0c850c0aa16312fb489290548e34b Mon Sep 17 00:00:00 2001 From: Erik Marks Date: Thu, 1 Feb 2024 08:37:13 -0800 Subject: [PATCH 187/234] test(daemon): Add `makeBundle` test Tests confined bundles using `makeBundle`. Adds a utility to make using `makeBundle` significantly easier in tests for the daemon. --- packages/cli/src/commands/make.js | 3 - packages/daemon/package.json | 1 + packages/daemon/test/test-endo.js | 117 ++++++++++++++++++++++++++++++ 3 files changed, 118 insertions(+), 3 deletions(-) diff --git a/packages/cli/src/commands/make.js b/packages/cli/src/commands/make.js index 15ebe99850..bfabc0bb11 100644 --- a/packages/cli/src/commands/make.js +++ b/packages/cli/src/commands/make.js @@ -12,9 +12,6 @@ import { randomHex16 } from '../random.js'; const textEncoder = new TextEncoder(); export const makeCommand = async ({ - cancel, - cancelled, - sockPath, filePath, importPath, resultName, diff --git a/packages/daemon/package.json b/packages/daemon/package.json index fdebc1300b..6a7b68b416 100644 --- a/packages/daemon/package.json +++ b/packages/daemon/package.json @@ -58,6 +58,7 @@ "ws": "^8.13.0" }, "devDependencies": { + "@endo/bundle-source": "^3.0.2", "@endo/init": "^1.0.2", "@endo/ses-ava": "^1.1.0", "ava": "^5.3.0", diff --git a/packages/daemon/test/test-endo.js b/packages/daemon/test/test-endo.js index 59918a57dd..a2082b2501 100644 --- a/packages/daemon/test/test-endo.js +++ b/packages/daemon/test/test-endo.js @@ -11,6 +11,7 @@ import url from 'url'; import path from 'path'; import { E } from '@endo/far'; import { makePromiseKit } from '@endo/promise-kit'; +import bundleSource from '@endo/bundle-source'; import { start, stop, @@ -41,6 +42,36 @@ const makeLocator = (...root) => { }; }; +// The id of the next bundle to make. +let bundleId = 0; +const textEncoder = new TextEncoder(); + +// TODO: We should be able to use {import('../src/types').EndoHost} for `host`, +// but when laundered through `E()` it becomes `never`. +/** + * Performs the necessary rituals to go from an endo `host` and a module `filePath` + * to calling `makeBundle` without leaving temporary pet names behind. + * + * @param {any} host - The host to use. + * @param {string} filePath - The path to the file to bundle. + * @param {(bundleName: string) => Promise} callback - A function that calls `makeBundle` + * on the `host`. + * @returns {Promise} The result of the `callback`. + */ +const doMakeBundle = async (host, filePath, callback) => { + const bundleName = `tmp-bundle-${bundleId}`; + bundleId += 1; + const bundle = await bundleSource(filePath); + const bundleText = JSON.stringify(bundle); + const bundleBytes = textEncoder.encode(bundleText); + const bundleReaderRef = makeReaderRef([bundleBytes]); + + await E(host).store(bundleReaderRef, bundleName); + const result = await callback(bundleName); + await E(host).remove(bundleName); + return result; +}; + test('lifecycle', async t => { const { reject: cancel, promise: cancelled } = makePromiseKit(); t.teardown(() => cancel(Error('teardown'))); @@ -392,6 +423,92 @@ test('persist unconfined services and their requests', async t => { await stop(locator); }); +test('persist confined services and their requests', async t => { + const { promise: cancelled, reject: cancel } = makePromiseKit(); + t.teardown(() => cancel(Error('teardown'))); + const locator = makeLocator('tmp', 'make-bundle'); + + await stop(locator).catch(() => {}); + await reset(locator); + await start(locator); + + const responderFinished = (async () => { + const { promise: followerCancelled, reject: cancelFollower } = + makePromiseKit(); + cancelled.catch(cancelFollower); + const { getBootstrap } = await makeEndoClient( + 'client', + locator.sockPath, + followerCancelled, + ); + const bootstrap = getBootstrap(); + const host = E(bootstrap).host(); + await E(host).makeWorker('user-worker'); + await E(host).evaluate( + 'user-worker', + ` + Far('Answer', { + value: () => 42, + }) + `, + [], + [], + 'grant', + ); + const iteratorRef = E(host).followMessages(); + const { value: message } = await E(iteratorRef).next(); + const { number, who } = E.get(message); + t.is(await who, 'o1'); + await E(host).resolve(await number, 'grant'); + })(); + + const requesterFinished = (async () => { + const { getBootstrap } = await makeEndoClient( + 'client', + locator.sockPath, + cancelled, + ); + const bootstrap = getBootstrap(); + const host = E(bootstrap).host(); + await E(host).makeWorker('w1'); + await E(host).provideGuest('o1'); + const servicePath = path.join(dirname, 'test', 'service.js'); + await doMakeBundle(host, servicePath, bundleName => + E(host).makeBundle('w1', bundleName, 'o1', 's1'), + ); + + await E(host).makeWorker('w2'); + const answer = await E(host).evaluate( + 'w2', + 'E(service).ask()', + ['service'], + ['s1'], + 'answer', + ); + const number = await E(answer).value(); + t.is(number, 42); + })(); + + await Promise.all([responderFinished, requesterFinished]); + + await restart(locator); + + { + const { getBootstrap } = await makeEndoClient( + 'client', + locator.sockPath, + cancelled, + ); + const bootstrap = getBootstrap(); + const host = E(bootstrap).host(); + const answer = await E(host).lookup('answer'); + const number = await E(answer).value(); + t.is(number, 42); + } + + await stop(locator); +}); + test('guest facet receives a message for host', async t => { const { promise: cancelled, reject: cancel } = makePromiseKit(); t.teardown(() => cancel(Error('teardown'))); From d35bbe23f9f0bdea928e5b8f6b50328a90f9c71f Mon Sep 17 00:00:00 2001 From: Erik Marks Date: Mon, 5 Feb 2024 12:29:37 -0800 Subject: [PATCH 188/234] feat(cli,daemon): Support dot-delimited petname paths in eval Adds support for dot-delimited petname paths to the CLI `eval` command. This required modifications to the `evaluate` method of the daemon's `host.js`, and the introduction of a new formula type, `lookup`, modeled on the `web-bundle` formula. Also adds a unit test for this behavior. The `lookup` formula type is necessary because the names in a lookup path may have neither petnames nor formula identifiers associated with them. Consider: ```text > endo eval '10' --name ten 10 > endo eval 'foo' foo:INFO.ten.source 10 ``` The only way retrieve the value for `source` is to call `E(HOST).lookup('INFO', 'ten', 'source')`, and since the `eval` formula expects its values to be mediated by formula identifiers, the lookup of `INFO.ten.source` must itself be stored as a formula. --- packages/cli/src/commands/eval.js | 5 +-- packages/cli/src/pet-name.js | 18 +++++++++++ packages/daemon/src/daemon.js | 40 +++++++++++++++++++++++- packages/daemon/src/host.js | 52 ++++++++++++++++++++++++------- packages/daemon/src/pet-name.js | 8 +++++ packages/daemon/src/pet-store.js | 2 +- packages/daemon/src/types.d.ts | 15 +++++++++ packages/daemon/test/test-endo.js | 30 ++++++++++++++++++ 8 files changed, 154 insertions(+), 16 deletions(-) create mode 100644 packages/cli/src/pet-name.js diff --git a/packages/cli/src/commands/eval.js b/packages/cli/src/commands/eval.js index b4341fb37c..a62adaa688 100644 --- a/packages/cli/src/commands/eval.js +++ b/packages/cli/src/commands/eval.js @@ -2,6 +2,7 @@ import os from 'os'; import { E } from '@endo/far'; import { withEndoParty } from '../context.js'; +import { parsePetNamePath } from '../pet-name.js'; export const evalCommand = async ({ source, @@ -27,13 +28,13 @@ export const evalCommand = async ({ return pair; }); const codeNames = pairs.map(pair => pair[0]); - const endowmentNames = pairs.map(pair => pair[1]); + const petNames = pairs.map(pair => parsePetNamePath(pair[1])); const result = await E(party).evaluate( workerName, source, codeNames, - endowmentNames, + petNames, resultName, ); console.log(result); diff --git a/packages/cli/src/pet-name.js b/packages/cli/src/pet-name.js new file mode 100644 index 0000000000..fdea2a8214 --- /dev/null +++ b/packages/cli/src/pet-name.js @@ -0,0 +1,18 @@ +/** + * Splits a dot-delimited pet name path into an array of pet names. + * Throws if any of the path segments are empty. + * + * @param {string} petNamePath - A dot-delimited pet name path. + * @returns {string[]} - The pet name path, as an array of pet names. + */ +export const parsePetNamePath = petNamePath => { + const petNames = petNamePath.split('.'); + for (const petName of petNames) { + if (petName === '') { + throw new Error( + `Pet name path "${petNamePath}" contains an empty segment.`, + ); + } + } + return petNames; +}; diff --git a/packages/daemon/src/daemon.js b/packages/daemon/src/daemon.js index cb63a4ba62..ba4b40f956 100644 --- a/packages/daemon/src/daemon.js +++ b/packages/daemon/src/daemon.js @@ -227,6 +227,26 @@ const makeEndoBootstrap = ( return { external, internal: undefined }; }; + /** + * @param {string} agentFormulaIdentifier + * @param {string[]} path + * @param {import('./types.js').Terminator} terminator + */ + const makeControllerForLookup = async ( + agentFormulaIdentifier, + path, + terminator, + ) => { + terminator.thisDiesIfThatDies(agentFormulaIdentifier); + + // Behold, recursion: + // eslint-disable-next-line no-use-before-define + const agent = provideValueForFormulaIdentifier(agentFormulaIdentifier); + + const external = E(agent).lookup(...path); + return { external, internal: undefined }; + }; + /** * @param {string} workerFormulaIdentifier * @param {string} guestFormulaIdentifier @@ -325,6 +345,8 @@ const makeEndoBootstrap = ( formula.values, terminator, ); + } else if (formula.type === 'lookup') { + return makeControllerForLookup(formula.agent, formula.path, terminator); } else if (formula.type === 'make-unconfined') { return makeControllerForUnconfinedPlugin( formula.worker, @@ -491,6 +513,12 @@ const makeEndoBootstrap = ( // controllerForFormulaIdentifier and formulaIdentifierForRef, since the // former bypasses the latter in order to avoid a round trip with disk. + /** + * @param {string} formulaType - The type of the formula. + * @param {string} formulaNumber - The number of the formula. + * @param {import('./types').Formula} formula - The formula. + * @returns {Promise} The value of the formula. + */ const provideValueForNumberedFormula = async ( formulaType, formulaNumber, @@ -508,7 +536,7 @@ const makeEndoBootstrap = ( // Behold, recursion: // eslint-disable-next-line no-use-before-define const terminator = makeTerminator(formulaIdentifier); - partial.catch(error => terminator.terminate()); + partial.catch(() => terminator.terminate()); const controller = harden({ terminator, external: E.get(partial).external.then(value => { @@ -647,6 +675,7 @@ const makeEndoBootstrap = ( if ( ![ 'eval-id512', + 'lookup', 'make-unconfined-id512', 'make-bundle-id512', 'guest-id512', @@ -676,6 +705,15 @@ const makeEndoBootstrap = ( worker: provideValueForFormulaIdentifier(formula.worker), }), ); + } else if (formula.type === 'lookup') { + return makeInspector( + formula.type, + formulaNumber, + harden({ + agent: provideValueForFormulaIdentifier(formula.agent), + path: formula.path, + }), + ); } else if (formula.type === 'guest') { return makeInspector( formula.type, diff --git a/packages/daemon/src/host.js b/packages/daemon/src/host.js index c6d3aa4601..6fe23bf1e0 100644 --- a/packages/daemon/src/host.js +++ b/packages/daemon/src/host.js @@ -1,7 +1,7 @@ // @ts-check import { Far } from '@endo/far'; -import { assertPetName } from './pet-name.js'; +import { assertPetName, petNamePathFrom } from './pet-name.js'; const { quote: q } = assert; @@ -185,15 +185,15 @@ export const makeHostMaker = ({ /** * @param {string | 'MAIN' | 'NEW'} workerName * @param {string} source - * @param {Array} codeNames - * @param {Array} petNames + * @param {string[]} codeNames + * @param {(string | string[])[]} petNamePaths * @param {string} resultName */ const evaluate = async ( workerName, source, codeNames, - petNames, + petNamePaths, resultName, ) => { const workerFormulaIdentifier = await provideWorkerFormulaIdentifier( @@ -203,24 +203,52 @@ export const makeHostMaker = ({ if (resultName !== undefined) { assertPetName(resultName); } - if (petNames.length !== codeNames.length) { + if (petNamePaths.length !== codeNames.length) { throw new Error('Evaluator requires one pet name for each code name'); } const formulaIdentifiers = await Promise.all( - petNames.map(async (petName, index) => { + petNamePaths.map(async (petNameOrPath, index) => { if (typeof codeNames[index] !== 'string') { throw new Error(`Invalid endowment name: ${q(codeNames[index])}`); } - const formulaIdentifier = lookupFormulaIdentifierForName(petName); - if (formulaIdentifier === undefined) { - throw new Error(`Unknown pet name ${q(petName)}`); + + const petNamePath = petNamePathFrom(petNameOrPath); + if (petNamePath.length === 1) { + const formulaIdentifier = lookupFormulaIdentifierForName( + petNamePath[0], + ); + if (formulaIdentifier === undefined) { + throw new Error(`Unknown pet name ${q(petNamePath[0])}`); + } + return formulaIdentifier; } - return formulaIdentifier; + + const lookupAgent = lookupFormulaIdentifierForName('SELF'); + const digester = makeSha512(); + digester.updateText(`${lookupAgent},${petNamePath.join(',')}`); + const lookupFormulaNumber = digester.digestHex().slice(32, 64); + + // TODO:lookup Check if the lookup formula already exists in the store + + const lookupFormula = { + /** @type {'lookup'} */ + type: 'lookup', + agent: lookupAgent, + path: petNamePath, + }; + + const { formulaIdentifier: lookupFormulaIdentifier } = + await provideValueForNumberedFormula( + 'lookup', + lookupFormulaNumber, + lookupFormula, + ); + return lookupFormulaIdentifier; }), ); - const formula = { + const evalFormula = { /** @type {'eval'} */ type: 'eval', worker: workerFormulaIdentifier, @@ -232,7 +260,7 @@ export const makeHostMaker = ({ // Behold, recursion: // eslint-disable-next-line no-use-before-define const { formulaIdentifier, value } = await provideValueForFormula( - formula, + evalFormula, 'eval-id512', ); if (resultName !== undefined) { diff --git a/packages/daemon/src/pet-name.js b/packages/daemon/src/pet-name.js index 337a836135..81e3c87574 100644 --- a/packages/daemon/src/pet-name.js +++ b/packages/daemon/src/pet-name.js @@ -12,3 +12,11 @@ export const assertPetName = petName => { throw new Error(`Invalid pet name ${q(petName)}`); } }; + +/** + * @param {string | string[]} petNameOrPetNamePath + */ +export const petNamePathFrom = petNameOrPetNamePath => + typeof petNameOrPetNamePath === 'string' + ? [petNameOrPetNamePath] + : petNameOrPetNamePath; diff --git a/packages/daemon/src/pet-store.js b/packages/daemon/src/pet-store.js index ac8509e2cc..3b51332425 100644 --- a/packages/daemon/src/pet-store.js +++ b/packages/daemon/src/pet-store.js @@ -9,7 +9,7 @@ const { quote: q } = assert; const validIdPattern = /^[0-9a-f]{128}$/; const validFormulaPattern = - /^(?:host|pet-store|pet-inspector|(?:readable-blob-sha512|worker-id512|pet-store-id512|eval-id512|make-unconfined-id512|make-bundle-id512|host-id512|guest-id512):[0-9a-f]{128}|web-bundle:[0-9a-f]{32})$/; + /^(?:host|pet-store|pet-inspector|(?:readable-blob-sha512|worker-id512|pet-store-id512|eval-id512|make-unconfined-id512|make-bundle-id512|host-id512|guest-id512):[0-9a-f]{128}|(?:lookup|web-bundle):[0-9a-f]{32})$/; /** * @param {import('./types.js').FilePowers} filePowers diff --git a/packages/daemon/src/types.d.ts b/packages/daemon/src/types.d.ts index 77f1b0eb83..5404fa51e6 100644 --- a/packages/daemon/src/types.d.ts +++ b/packages/daemon/src/types.d.ts @@ -72,6 +72,20 @@ type EvalFormula = { // TODO formula slots }; +type LookupFormula = { + type: 'lookup'; + + /** + * The formula identifier of the guest or host to call lookup on. + */ + agent: string; + + /** + * The pet name path. + */ + path: Array; +}; + type MakeUnconfinedFormula = { type: 'make-unconfined'; worker: string; @@ -97,6 +111,7 @@ type WebBundleFormula = { export type Formula = | GuestFormula | EvalFormula + | LookupFormula | MakeUnconfinedFormula | MakeBundleFormula | WebBundleFormula; diff --git a/packages/daemon/test/test-endo.js b/packages/daemon/test/test-endo.js index a2082b2501..683aafc99e 100644 --- a/packages/daemon/test/test-endo.js +++ b/packages/daemon/test/test-endo.js @@ -1041,3 +1041,33 @@ test('lookup with petname path (value has no lookup method)', async t => { await stop(locator); }); + +test('evaluate name resolved by lookup path', async t => { + const { promise: cancelled, reject: cancel } = makePromiseKit(); + t.teardown(() => cancel(Error('teardown'))); + const locator = makeLocator('tmp', 'name-resolved-by-lookup-path'); + + await stop(locator).catch(() => {}); + await reset(locator); + await start(locator); + + const { getBootstrap } = await makeEndoClient( + 'client', + locator.sockPath, + cancelled, + ); + const bootstrap = getBootstrap(); + const host = E(bootstrap).host(); + + await E(host).evaluate('MAIN', '10', [], [], 'ten'); + + const resolvedValue = await E(host).evaluate( + 'MAIN', + 'foo', + ['foo'], + [['INFO', 'ten', 'source']], + ); + t.is(resolvedValue, '10'); + + await stop(locator); +}); From 55aa89b37f9d3a7b59d0f3c4ece0510502e68eab Mon Sep 17 00:00:00 2001 From: Erik Marks Date: Mon, 5 Feb 2024 17:09:25 -0800 Subject: [PATCH 189/234] refactor(daemon): Associate lookup formula creation with mailbox --- packages/daemon/src/daemon.js | 7 ++++++- packages/daemon/src/host.js | 21 ++------------------- packages/daemon/src/mail.js | 33 +++++++++++++++++++++++++++++++++ 3 files changed, 41 insertions(+), 20 deletions(-) diff --git a/packages/daemon/src/daemon.js b/packages/daemon/src/daemon.js index ba4b40f956..a9404dc4e6 100644 --- a/packages/daemon/src/daemon.js +++ b/packages/daemon/src/daemon.js @@ -228,6 +228,9 @@ const makeEndoBootstrap = ( }; /** + * Creates a controller for a `lookup` formula. The external facet is the + * resolved value of the lookup. + * * @param {string} agentFormulaIdentifier * @param {string[]} path * @param {import('./types.js').Terminator} terminator @@ -517,7 +520,7 @@ const makeEndoBootstrap = ( * @param {string} formulaType - The type of the formula. * @param {string} formulaNumber - The number of the formula. * @param {import('./types').Formula} formula - The formula. - * @returns {Promise} The value of the formula. + * @returns {Promise<{ formulaIdentifier: string, value: unknown }>} The value of the formula. */ const provideValueForNumberedFormula = async ( formulaType, @@ -627,6 +630,8 @@ const makeEndoBootstrap = ( formulaIdentifierForRef, provideValueForFormulaIdentifier, provideControllerForFormulaIdentifier, + makeSha512, + provideValueForNumberedFormula, }); const makeIdentifiedGuestController = makeGuestMaker({ diff --git a/packages/daemon/src/host.js b/packages/daemon/src/host.js index 6fe23bf1e0..cc81133491 100644 --- a/packages/daemon/src/host.js +++ b/packages/daemon/src/host.js @@ -43,6 +43,7 @@ export const makeHostMaker = ({ reverseLookup, lookupFormulaIdentifierForName, listMessages, + provideLookupFormula, followMessages, resolve, reject, @@ -224,26 +225,8 @@ export const makeHostMaker = ({ return formulaIdentifier; } - const lookupAgent = lookupFormulaIdentifierForName('SELF'); - const digester = makeSha512(); - digester.updateText(`${lookupAgent},${petNamePath.join(',')}`); - const lookupFormulaNumber = digester.digestHex().slice(32, 64); - - // TODO:lookup Check if the lookup formula already exists in the store - - const lookupFormula = { - /** @type {'lookup'} */ - type: 'lookup', - agent: lookupAgent, - path: petNamePath, - }; - const { formulaIdentifier: lookupFormulaIdentifier } = - await provideValueForNumberedFormula( - 'lookup', - lookupFormulaNumber, - lookupFormula, - ); + await provideLookupFormula(petNamePath); return lookupFormulaIdentifier; }), ); diff --git a/packages/daemon/src/mail.js b/packages/daemon/src/mail.js index b3241ac2d3..ac216fd3b6 100644 --- a/packages/daemon/src/mail.js +++ b/packages/daemon/src/mail.js @@ -12,6 +12,8 @@ export const makeMailboxMaker = ({ provideValueForFormulaIdentifier, provideControllerForFormulaIdentifier, formulaIdentifierForRef, + makeSha512, + provideValueForNumberedFormula, }) => { const makeMailbox = ({ selfFormulaIdentifier, @@ -98,6 +100,36 @@ export const makeMailboxMaker = ({ return reverseLookupFormulaIdentifier(formulaIdentifier); }; + /** + * Takes a sequence of pet names and returns a formula identifier and value + * for the corresponding lookup formula. + * + * @param {string[]} petNamePath - A sequence of pet names. + * @returns {Promise<{ formulaIdentifier: string, value: unknown }>} The formula + * identifier and value of the lookup formula. + */ + const provideLookupFormula = async petNamePath => { + const agentFormulaIdentifier = lookupFormulaIdentifierForName('SELF'); + const digester = makeSha512(); + digester.updateText(`${agentFormulaIdentifier},${petNamePath.join(',')}`); + const lookupFormulaNumber = digester.digestHex().slice(32, 64); + + // TODO:lookup Check if the lookup formula already exists in the store + + const lookupFormula = { + /** @type {'lookup'} */ + type: 'lookup', + agent: agentFormulaIdentifier, + path: petNamePath, + }; + + return provideValueForNumberedFormula( + 'lookup', + lookupFormulaNumber, + lookupFormula, + ); + }; + /** * @param {import('./types.js').InternalMessage} message * @returns {import('./types.js').Message | undefined} @@ -518,6 +550,7 @@ export const makeMailboxMaker = ({ reverseLookup, reverseLookupFormulaIdentifier, lookupFormulaIdentifierForName, + provideLookupFormula, followMessages, listMessages, request, From c6b5b82d4f93bdf956459369e4392fa0f7bb3d82 Mon Sep 17 00:00:00 2001 From: Erik Marks Date: Tue, 6 Feb 2024 19:13:11 -0800 Subject: [PATCH 190/234] refactor(daemon): Convert lookup formula number to id512 --- packages/daemon/src/daemon.js | 2 +- packages/daemon/src/mail.js | 4 ++-- packages/daemon/src/pet-store.js | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/daemon/src/daemon.js b/packages/daemon/src/daemon.js index a9404dc4e6..0631c2b992 100644 --- a/packages/daemon/src/daemon.js +++ b/packages/daemon/src/daemon.js @@ -680,7 +680,7 @@ const makeEndoBootstrap = ( if ( ![ 'eval-id512', - 'lookup', + 'lookup-id512', 'make-unconfined-id512', 'make-bundle-id512', 'guest-id512', diff --git a/packages/daemon/src/mail.js b/packages/daemon/src/mail.js index ac216fd3b6..548bbcfd0e 100644 --- a/packages/daemon/src/mail.js +++ b/packages/daemon/src/mail.js @@ -112,7 +112,7 @@ export const makeMailboxMaker = ({ const agentFormulaIdentifier = lookupFormulaIdentifierForName('SELF'); const digester = makeSha512(); digester.updateText(`${agentFormulaIdentifier},${petNamePath.join(',')}`); - const lookupFormulaNumber = digester.digestHex().slice(32, 64); + const lookupFormulaNumber = digester.digestHex(); // TODO:lookup Check if the lookup formula already exists in the store @@ -124,7 +124,7 @@ export const makeMailboxMaker = ({ }; return provideValueForNumberedFormula( - 'lookup', + 'lookup-id512', lookupFormulaNumber, lookupFormula, ); diff --git a/packages/daemon/src/pet-store.js b/packages/daemon/src/pet-store.js index 3b51332425..30bbaec6e9 100644 --- a/packages/daemon/src/pet-store.js +++ b/packages/daemon/src/pet-store.js @@ -9,7 +9,7 @@ const { quote: q } = assert; const validIdPattern = /^[0-9a-f]{128}$/; const validFormulaPattern = - /^(?:host|pet-store|pet-inspector|(?:readable-blob-sha512|worker-id512|pet-store-id512|eval-id512|make-unconfined-id512|make-bundle-id512|host-id512|guest-id512):[0-9a-f]{128}|(?:lookup|web-bundle):[0-9a-f]{32})$/; + /^(?:host|pet-store|pet-inspector|(?:readable-blob-sha512|worker-id512|pet-store-id512|eval-id512|lookup-id512|make-unconfined-id512|make-bundle-id512|host-id512|guest-id512):[0-9a-f]{128}|web-bundle:[0-9a-f]{32})$/; /** * @param {import('./types.js').FilePowers} filePowers From e6f36a17c0ac8ea76d94256e1bcb05c9b649458c Mon Sep 17 00:00:00 2001 From: Erik Marks Date: Thu, 8 Feb 2024 10:32:48 -0800 Subject: [PATCH 191/234] refactor(daemon): Replace "agent" language with "hub" for lookup Lookup formulas must be associated with the namespace for which the pet name path is valid. In practice, this means an object with a variadic `lookup()` method. Previously, these objects were referred to as "agents", but in the future, not all such object will be agents (i.e. guests or hosts). Therefore, here we replace all references to "agent" with "hub", short for "naming hub". --- packages/daemon/src/daemon.js | 14 +++++++------- packages/daemon/src/mail.js | 10 +++++++--- packages/daemon/src/types.d.ts | 5 +++-- 3 files changed, 17 insertions(+), 12 deletions(-) diff --git a/packages/daemon/src/daemon.js b/packages/daemon/src/daemon.js index 0631c2b992..9d3e8b28b5 100644 --- a/packages/daemon/src/daemon.js +++ b/packages/daemon/src/daemon.js @@ -231,22 +231,22 @@ const makeEndoBootstrap = ( * Creates a controller for a `lookup` formula. The external facet is the * resolved value of the lookup. * - * @param {string} agentFormulaIdentifier + * @param {string} hubFormulaIdentifier * @param {string[]} path * @param {import('./types.js').Terminator} terminator */ const makeControllerForLookup = async ( - agentFormulaIdentifier, + hubFormulaIdentifier, path, terminator, ) => { - terminator.thisDiesIfThatDies(agentFormulaIdentifier); + terminator.thisDiesIfThatDies(hubFormulaIdentifier); // Behold, recursion: // eslint-disable-next-line no-use-before-define - const agent = provideValueForFormulaIdentifier(agentFormulaIdentifier); + const hub = provideValueForFormulaIdentifier(hubFormulaIdentifier); - const external = E(agent).lookup(...path); + const external = E(hub).lookup(...path); return { external, internal: undefined }; }; @@ -349,7 +349,7 @@ const makeEndoBootstrap = ( terminator, ); } else if (formula.type === 'lookup') { - return makeControllerForLookup(formula.agent, formula.path, terminator); + return makeControllerForLookup(formula.hub, formula.path, terminator); } else if (formula.type === 'make-unconfined') { return makeControllerForUnconfinedPlugin( formula.worker, @@ -715,7 +715,7 @@ const makeEndoBootstrap = ( formula.type, formulaNumber, harden({ - agent: provideValueForFormulaIdentifier(formula.agent), + hub: provideValueForFormulaIdentifier(formula.hub), path: formula.path, }), ); diff --git a/packages/daemon/src/mail.js b/packages/daemon/src/mail.js index 548bbcfd0e..3290280b5d 100644 --- a/packages/daemon/src/mail.js +++ b/packages/daemon/src/mail.js @@ -109,9 +109,13 @@ export const makeMailboxMaker = ({ * identifier and value of the lookup formula. */ const provideLookupFormula = async petNamePath => { - const agentFormulaIdentifier = lookupFormulaIdentifierForName('SELF'); + // The lookup formula identifier consists of the hash of the associated + // naming hub's formula identifier and the pet name path. + // A "naming hub" is an objected with a variadic lookup method. At present, + // the only such objects are guests and hosts. + const hubFormulaIdentifier = lookupFormulaIdentifierForName('SELF'); const digester = makeSha512(); - digester.updateText(`${agentFormulaIdentifier},${petNamePath.join(',')}`); + digester.updateText(`${hubFormulaIdentifier},${petNamePath.join(',')}`); const lookupFormulaNumber = digester.digestHex(); // TODO:lookup Check if the lookup formula already exists in the store @@ -119,7 +123,7 @@ export const makeMailboxMaker = ({ const lookupFormula = { /** @type {'lookup'} */ type: 'lookup', - agent: agentFormulaIdentifier, + hub: hubFormulaIdentifier, path: petNamePath, }; diff --git a/packages/daemon/src/types.d.ts b/packages/daemon/src/types.d.ts index 5404fa51e6..0a3e4a6654 100644 --- a/packages/daemon/src/types.d.ts +++ b/packages/daemon/src/types.d.ts @@ -76,9 +76,10 @@ type LookupFormula = { type: 'lookup'; /** - * The formula identifier of the guest or host to call lookup on. + * The formula identifier of the naming hub to call lookup on. + * A "naming hub" is an object with a variadic `lookup()` method. */ - agent: string; + hub: string; /** * The pet name path. From 9848676ddbd7ccee88a78635462d75f08c7ed46a Mon Sep 17 00:00:00 2001 From: Erik Marks Date: Fri, 9 Feb 2024 16:25:51 -0800 Subject: [PATCH 192/234] feat(cli): Support dot-delimited paths for `--as` option The CLI `--as` option previously supported paths like this: ``` endo list --as foo --as bar ``` This does away with the ability to specify the option multiple times, and replaces it with dot-delimited path support: ``` endo list --as foo.bar ``` --- packages/cli/src/context.js | 11 +-- packages/cli/src/endo.js | 174 ++++++------------------------------ 2 files changed, 34 insertions(+), 151 deletions(-) diff --git a/packages/cli/src/context.js b/packages/cli/src/context.js index c76e522797..53adffdbfe 100644 --- a/packages/cli/src/context.js +++ b/packages/cli/src/context.js @@ -4,6 +4,7 @@ import { makePromiseKit } from '@endo/promise-kit'; import { E } from '@endo/far'; import { whereEndoSock } from '@endo/where'; import { provideEndoClient } from './client.js'; +import { parsePetNamePath } from './pet-name.js'; export const withInterrupt = async callback => { const { promise: cancelled, reject: cancel } = makePromiseKit(); @@ -61,14 +62,14 @@ export const withEndoHost = ({ os, process }, callback) => }, ); -export const withEndoParty = (partyNames, { os, process }, callback) => +export const withEndoParty = (partyNamePath, { os, process }, callback) => withEndoHost( { os, process }, async ({ cancel, cancelled, bootstrap, host }) => { - let party = host; - for (const partyName of partyNames) { - party = E(party).lookup(partyName); - } + const party = + partyNamePath === undefined + ? host + : E(host).lookup(...parsePetNamePath(partyNamePath)); await callback({ cancel, cancelled, diff --git a/packages/cli/src/endo.js b/packages/cli/src/endo.js index c624f328ee..32907f03c2 100644 --- a/packages/cli/src/endo.js +++ b/packages/cli/src/endo.js @@ -13,12 +13,14 @@ import url from 'url'; import { Command } from 'commander'; import { prompt } from './prompt.js'; -const collect = (value, values) => values.concat([value]); - const packageDescriptorPath = url.fileURLToPath( new URL('../package.json', import.meta.url), ); +const commonOptions = { + as: ['-a,--as ', 'Pose as named party (as named by current party)'], +}; + export const main = async rawArgs => { const program = new Command(); @@ -33,12 +35,7 @@ export const main = async rawArgs => { program .command('install [filePath]') .description('installs a web page (weblet)') - .option( - '-a,--as ', - 'Pose as named party (as named by current party)', - collect, - [], - ) + .option(...commonOptions.as) .option('-b,--bundle ', 'Bundle for a web page (weblet)') .option( '-p,--powers ', @@ -66,12 +63,7 @@ export const main = async rawArgs => { program .command('open ') .description('opens a web page (weblet)') - .option( - '-a,--as ', - 'Pose as named party (as named by current party)', - collect, - [], - ) + .option(...commonOptions.as) .action(async (webPageName, cmd) => { const { as: partyNames } = cmd.opts(); const { open } = await import('./commands/open.js'); @@ -83,12 +75,7 @@ export const main = async rawArgs => { program .command('run [] [...]') .description('runs a program (runlet)') - .option( - '-a,--as ', - 'Pose as named party (as named by current party)', - collect, - [], - ) + .option(...commonOptions.as) .option('-b,--bundle ', 'Bundle name for the caplet program') .option( '--UNCONFINED ', @@ -121,12 +108,7 @@ export const main = async rawArgs => { .description('make a plugin or a worker caplet (worklet)') .option('-b,--bundle ', 'Bundle for a web page to open') .option('--UNCONFINED ', 'Path to a Node.js module') - .option( - '-a,--as ', - 'Pose as named party (as named by current party)', - collect, - [], - ) + .option(...commonOptions.as) .option('-p,--powers ', 'Name of powers to grant or NONE, SELF, ENDO') .option( '-n,--name ', @@ -159,12 +141,7 @@ export const main = async rawArgs => { program .command('inbox') - .option( - '-a,--as ', - 'Pose as named party (as named by current party)', - collect, - [], - ) + .option(...commonOptions.as) .option('-f,--follow', 'Follow the inbox for messages as they arrive') .description('read messages') .action(async cmd => { @@ -180,12 +157,7 @@ export const main = async rawArgs => { '-t,--to ', 'Send the request to another party (default: HOST)', ) - .option( - '-a,--as ', - 'Pose as named party (as named by current party)', - collect, - [], - ) + .option(...commonOptions.as) .option( '-n,--name ', 'Assigns a name to the result for future reference, persisted between restarts', @@ -203,12 +175,7 @@ export const main = async rawArgs => { program .command('resolve ') .description('grant a request') - .option( - '-a,--as ', - 'Pose as named party (as named by current party)', - collect, - [], - ) + .option(...commonOptions.as) .action(async (requestNumberText, resolutionName, cmd) => { const { as: partyNames } = cmd.opts(); const { resolveCommand } = await import('./commands/resolve.js'); @@ -222,12 +189,7 @@ export const main = async rawArgs => { program .command('reject [message]') .description('deny a request') - .option( - '-a,--as ', - 'Pose as named party (as named by current party)', - collect, - [], - ) + .option(...commonOptions.as) .action(async (requestNumberText, message, cmd) => { const { as: partyNames } = cmd.opts(); const { rejectCommand } = await import('./commands/reject.js'); @@ -241,12 +203,7 @@ export const main = async rawArgs => { program .command('send ') .description('send a message with @named-values @for-you:from-me') - .option( - '-a,--as ', - 'Pose as named party (as named by current party)', - collect, - [], - ) + .option(...commonOptions.as) .action(async (partyName, message, cmd) => { const { as: partyNames } = cmd.opts(); const { send } = await import('./commands/send.js'); @@ -259,12 +216,7 @@ export const main = async rawArgs => { '-n,--name ', 'Name to use, if different than the suggested name.', ) - .option( - '-a,--as ', - 'Pose as named party (as named by current party)', - collect, - [], - ) + .option(...commonOptions.as) .description('accept a @value from a message') .action(async (messageNumberText, edgeName, cmd) => { const { name = edgeName, as: partyNames } = cmd.opts(); @@ -280,12 +232,7 @@ export const main = async rawArgs => { program .command('dismiss ') .description('delete a message') - .option( - '-a,--as ', - 'Pose as named party (as named by current party)', - collect, - [], - ) + .option(...commonOptions.as) .action(async (messageNumberText, cmd) => { const { as: partyNames } = cmd.opts(); const { dismissCommand } = await import('./commands/dismiss.js'); @@ -297,12 +244,7 @@ export const main = async rawArgs => { program .command('list') - .option( - '-a,--as ', - 'Pose as named party (as named by current party)', - collect, - [], - ) + .option(...commonOptions.as) .description('show names') .action(async cmd => { const { as: partyNames } = cmd.opts(); @@ -313,12 +255,7 @@ export const main = async rawArgs => { program .command('remove [names...]') .description('forget a named value') - .option( - '-a,--as ', - 'Pose as named party (as named by current party)', - collect, - [], - ) + .option(...commonOptions.as) .action(async (petNames, cmd) => { const { as: partyNames } = cmd.opts(); const { remove } = await import('./commands/remove.js'); @@ -328,12 +265,7 @@ export const main = async rawArgs => { program .command('rename ') .description('change the name for a value') - .option( - '-a,--as ', - 'Pose as named party (as named by current party)', - collect, - [], - ) + .option(...commonOptions.as) .action(async (fromName, toName, cmd) => { const { as: partyNames } = cmd.opts(); const { rename } = await import('./commands/rename.js'); @@ -343,12 +275,7 @@ export const main = async rawArgs => { program .command('show ') .description('prints the named value') - .option( - '-a,--as ', - 'Pose as named party (as named by current party)', - collect, - [], - ) + .option(...commonOptions.as) .action(async (name, cmd) => { const { as: partyNames } = cmd.opts(); const { show } = await import('./commands/show.js'); @@ -357,12 +284,7 @@ export const main = async rawArgs => { program .command('follow ') - .option( - '-a,--as ', - 'Pose as named party (as named by current party)', - collect, - [], - ) + .option(...commonOptions.as) .description('subscribe to a stream of values') .action(async (name, cmd) => { const { as: partyNames } = cmd.opts(); @@ -373,12 +295,7 @@ export const main = async rawArgs => { program .command('cat ') .description('dumps a blob') - .option( - '-a,--as ', - 'Pose as named party (as named by current party)', - collect, - [], - ) + .option(...commonOptions.as) .action(async (name, cmd) => { const { as: partyNames } = cmd.opts(); const { cat } = await import('./commands/cat.js'); @@ -388,12 +305,7 @@ export const main = async rawArgs => { program .command('store ') .description('stores a blob') - .option( - '-a,--as ', - 'Pose as named party (as named by current party)', - collect, - [], - ) + .option(...commonOptions.as) .option( '-n,--name ', 'Assigns a pet name to the result for future reference', @@ -411,12 +323,7 @@ export const main = async rawArgs => { program .command('eval [names...]') .description('creates a value') - .option( - '-a,--as ', - 'Pose as named party (as named by current party)', - collect, - [], - ) + .option(...commonOptions.as) .option( '-w,--worker ', 'Reuse an existing worker rather than create a new one', @@ -444,12 +351,7 @@ export const main = async rawArgs => { program .command('spawn [names...]') .description('creates a worker') - .option( - '-a,--as ', - 'Pose as named party (as named by current party)', - collect, - [], - ) + .option(...commonOptions.as) .action(async (petNames, cmd) => { const { as: partyNames } = cmd.opts(); const { spawn } = await import('./commands/spawn.js'); @@ -459,12 +361,7 @@ export const main = async rawArgs => { program .command('bundle ') .description('stores a program') - .option( - '-a,--as ', - 'Pose as named party (as named by current party)', - collect, - [], - ) + .option(...commonOptions.as) .option('-n,--name ', 'Store the bundle into Endo') .action(async (applicationPath, cmd) => { const { name: bundleName, as: partyNames } = cmd.opts(); @@ -478,12 +375,7 @@ export const main = async rawArgs => { program .command('mkhost ') - .option( - '-a,--as ', - 'Pose as named party (as named by current party)', - collect, - [], - ) + .option(...commonOptions.as) .description('makes a separate mailbox and storage for you') .action(async (name, cmd) => { const { as: partyNames } = cmd.opts(); @@ -493,12 +385,7 @@ export const main = async rawArgs => { program .command('mkguest ') - .option( - '-a,--as ', - 'Pose as named party (as named by current party)', - collect, - [], - ) + .option(...commonOptions.as) .description('makes a mailbox and storage for a guest (peer or program)') .action(async (name, cmd) => { const { as: partyNames } = cmd.opts(); @@ -508,12 +395,7 @@ export const main = async rawArgs => { program .command('kill ') - .option( - '-a,--as ', - 'Pose as named party (as named by current party)', - collect, - [], - ) + .option(...commonOptions.as) .description('terminate a value and its deps, recovering resources') .action(async (name, cmd) => { const { as: partyNames } = cmd.opts(); From d570060fd07bb5f01af478fb9b452671d0479a4d Mon Sep 17 00:00:00 2001 From: kumavis Date: Mon, 12 Feb 2024 10:49:31 -1000 Subject: [PATCH 193/234] feat: endo bundle command supports specifying commonDeps --- packages/bundle-source/src/zip-base64.js | 3 ++- packages/cli/src/commands/bundle.js | 10 ++++++++- packages/cli/src/endo.js | 26 +++++++++++++++++++++++- 3 files changed, 36 insertions(+), 3 deletions(-) diff --git a/packages/bundle-source/src/zip-base64.js b/packages/bundle-source/src/zip-base64.js index 82813a3e93..b81102f497 100644 --- a/packages/bundle-source/src/zip-base64.js +++ b/packages/bundle-source/src/zip-base64.js @@ -22,7 +22,7 @@ export async function bundleZipBase64( options = {}, grantedPowers = {}, ) { - const { dev = false, cacheSourceMaps = false } = options; + const { dev = false, cacheSourceMaps = false, commonDependencies } = options; const powers = { ...readPowers, ...grantedPowers }; const { computeSha512, @@ -174,6 +174,7 @@ export async function bundleZipBase64( sourceMapHook(sourceMap, sourceDescriptor) { sourceMapJobs.add(writeSourceMap(sourceMap, sourceDescriptor)); }, + commonDependencies, }); assert(sha512); await Promise.all(sourceMapJobs); diff --git a/packages/cli/src/commands/bundle.js b/packages/cli/src/commands/bundle.js index 2ba8c60483..78fcad558c 100644 --- a/packages/cli/src/commands/bundle.js +++ b/packages/cli/src/commands/bundle.js @@ -12,8 +12,16 @@ export const bundleCommand = async ({ applicationPath, bundleName, partyNames, + bundleOptions, }) => { - const bundle = await bundleSource(applicationPath); + const bundle = + /** @type {{ moduleFormat: 'endoZipBase64', endoZipBase64: string, endoZipBase64Sha512: string }} */ + ( + await bundleSource(applicationPath, { + ...bundleOptions, + format: 'endoZipBase64', + }) + ); process.stdout.write(`${bundle.endoZipBase64Sha512}\n`); const bundleText = JSON.stringify(bundle); const bundleBytes = textEncoder.encode(bundleText); diff --git a/packages/cli/src/endo.js b/packages/cli/src/endo.js index 32907f03c2..514ca9aacb 100644 --- a/packages/cli/src/endo.js +++ b/packages/cli/src/endo.js @@ -21,6 +21,17 @@ const commonOptions = { as: ['-a,--as ', 'Pose as named party (as named by current party)'], }; +const parseOptionAsMapping = (optionValueString, obj) => { + // arguments can be provided as "a:b" or just "a" + // eslint-disable-next-line prefer-const + let [from, to] = optionValueString.split(':'); + if (to === undefined) { + to = from; + } + obj[from] = to; + return obj; +}; + export const main = async rawArgs => { const program = new Command(); @@ -363,13 +374,26 @@ export const main = async rawArgs => { .description('stores a program') .option(...commonOptions.as) .option('-n,--name ', 'Store the bundle into Endo') + .option( + '--common-dep ', + 'Specify common dependency for bundle (eg node builtin package shims)', + parseOptionAsMapping, + {}, + ) .action(async (applicationPath, cmd) => { - const { name: bundleName, as: partyNames } = cmd.opts(); + const { + name: bundleName, + as: partyNames, + commonDep: commonDependencies, + } = cmd.opts(); const { bundleCommand } = await import('./commands/bundle.js'); return bundleCommand({ applicationPath, bundleName, partyNames, + bundleOptions: { + commonDependencies, + }, }); }); From a3de821fee85d8ef38aa0eef2e22dab3d6263cb1 Mon Sep 17 00:00:00 2001 From: kumavis Date: Sat, 10 Feb 2024 10:27:03 -1000 Subject: [PATCH 194/234] fix(daemon): rename petStore lookup to identifyLocal --- packages/daemon/src/daemon.js | 2 +- packages/daemon/src/mail.js | 4 ++-- packages/daemon/src/pet-store.js | 12 +++++++----- packages/daemon/src/types.d.ts | 2 +- 4 files changed, 11 insertions(+), 9 deletions(-) diff --git a/packages/daemon/src/daemon.js b/packages/daemon/src/daemon.js index 9d3e8b28b5..a5347575ca 100644 --- a/packages/daemon/src/daemon.js +++ b/packages/daemon/src/daemon.js @@ -671,7 +671,7 @@ const makeEndoBootstrap = ( * inspector for the value of the given pet name. */ const lookup = async petName => { - const formulaIdentifier = petStore.lookup(petName); + const formulaIdentifier = petStore.identifyLocal(petName); if (formulaIdentifier === undefined) { throw new Error(`Unknown pet name ${petName}`); } diff --git a/packages/daemon/src/mail.js b/packages/daemon/src/mail.js index 3290280b5d..46cb4200da 100644 --- a/packages/daemon/src/mail.js +++ b/packages/daemon/src/mail.js @@ -40,7 +40,7 @@ export const makeMailboxMaker = ({ if (Object.hasOwn(specialNames, petName)) { return specialNames[petName]; } - return petStore.lookup(petName); + return petStore.identifyLocal(petName); }; /** @@ -263,7 +263,7 @@ export const makeMailboxMaker = ({ ) => { if (responseName !== undefined) { /** @type {string | undefined} */ - let formulaIdentifier = senderPetStore.lookup(responseName); + let formulaIdentifier = senderPetStore.identifyLocal(responseName); if (formulaIdentifier === undefined) { formulaIdentifier = await requestFormulaIdentifier( what, diff --git a/packages/daemon/src/pet-store.js b/packages/daemon/src/pet-store.js index 30bbaec6e9..a085dedce4 100644 --- a/packages/daemon/src/pet-store.js +++ b/packages/daemon/src/pet-store.js @@ -67,11 +67,13 @@ export const makePetStoreMaker = (filePowers, locator) => { return petNames.has(petName); }; - /** @param {string} petName */ - const lookup = petName => { + /** + * @param {string} petName + * @returns {string | undefined} + */ + const identifyLocal = petName => { assertValidName(petName); - const formulaIdentifier = petNames.get(petName); - return formulaIdentifier; + return petNames.get(petName); }; /** @@ -259,7 +261,7 @@ export const makePetStoreMaker = (filePowers, locator) => { /** @type {import('./types.js').PetStore} */ const petStore = { has, - lookup, + identifyLocal, reverseLookup, list, follow, diff --git a/packages/daemon/src/types.d.ts b/packages/daemon/src/types.d.ts index 0a3e4a6654..65c489f13c 100644 --- a/packages/daemon/src/types.d.ts +++ b/packages/daemon/src/types.d.ts @@ -178,6 +178,7 @@ export interface Controller { export interface PetStore { has(petName: string): boolean; + identifyLocal(petName: string): string | undefined; list(): Array; follow(): Promise>>; listEntries(): Array<[string, FormulaIdentifierRecord]>; @@ -191,7 +192,6 @@ export interface PetStore { write(petName: string, formulaIdentifier: string): Promise; remove(petName: string); rename(fromPetName: string, toPetName: string); - lookup(petName: string): string | undefined; reverseLookup(formulaIdentifier: string): Array; } From fef70c344fa03a9f154911be04105b8831f71e24 Mon Sep 17 00:00:00 2001 From: kumavis Date: Sat, 10 Feb 2024 10:36:26 -1000 Subject: [PATCH 195/234] fix(daemon): rename mail lookupFormulaIdentifierForName to identifyLocal --- packages/daemon/src/host.js | 22 +++++++++------------- packages/daemon/src/mail.js | 21 ++++++++++----------- 2 files changed, 19 insertions(+), 24 deletions(-) diff --git a/packages/daemon/src/host.js b/packages/daemon/src/host.js index cc81133491..0975dd2127 100644 --- a/packages/daemon/src/host.js +++ b/packages/daemon/src/host.js @@ -41,7 +41,7 @@ export const makeHostMaker = ({ const { lookup, reverseLookup, - lookupFormulaIdentifierForName, + identifyLocal, listMessages, provideLookupFormula, followMessages, @@ -75,7 +75,7 @@ export const makeHostMaker = ({ /** @type {string | undefined} */ let formulaIdentifier; if (petName !== undefined) { - formulaIdentifier = lookupFormulaIdentifierForName(petName); + formulaIdentifier = identifyLocal(petName); } if (formulaIdentifier === undefined) { /** @type {import('./types.js').GuestFormula} */ @@ -129,7 +129,7 @@ export const makeHostMaker = ({ if (typeof workerName !== 'string') { throw new Error('worker name must be string'); } - let workerFormulaIdentifier = lookupFormulaIdentifierForName(workerName); + let workerFormulaIdentifier = identifyLocal(workerName); if (workerFormulaIdentifier === undefined) { const workerId512 = await randomHex512(); workerFormulaIdentifier = `worker-id512:${workerId512}`; @@ -156,7 +156,7 @@ export const makeHostMaker = ({ return `worker-id512:${workerId512}`; } assertPetName(workerName); - let workerFormulaIdentifier = lookupFormulaIdentifierForName(workerName); + let workerFormulaIdentifier = identifyLocal(workerName); if (workerFormulaIdentifier === undefined) { const workerId512 = await randomHex512(); workerFormulaIdentifier = `worker-id512:${workerId512}`; @@ -170,7 +170,7 @@ export const makeHostMaker = ({ * @param {string | 'NONE' | 'SELF' | 'ENDO'} partyName */ const providePowersFormulaIdentifier = async partyName => { - let guestFormulaIdentifier = lookupFormulaIdentifierForName(partyName); + let guestFormulaIdentifier = identifyLocal(partyName); if (guestFormulaIdentifier === undefined) { const guest = await provideGuest(partyName); guestFormulaIdentifier = formulaIdentifierForRef.get(guest); @@ -216,9 +216,7 @@ export const makeHostMaker = ({ const petNamePath = petNamePathFrom(petNameOrPath); if (petNamePath.length === 1) { - const formulaIdentifier = lookupFormulaIdentifierForName( - petNamePath[0], - ); + const formulaIdentifier = identifyLocal(petNamePath[0]); if (formulaIdentifier === undefined) { throw new Error(`Unknown pet name ${q(petNamePath[0])}`); } @@ -308,8 +306,7 @@ export const makeHostMaker = ({ workerName, ); - const bundleFormulaIdentifier = - lookupFormulaIdentifierForName(bundleName); + const bundleFormulaIdentifier = identifyLocal(bundleName); if (bundleFormulaIdentifier === undefined) { throw new TypeError(`Unknown pet name for bundle: ${bundleName}`); } @@ -364,7 +361,7 @@ export const makeHostMaker = ({ /** @type {string | undefined} */ let formulaIdentifier; if (petName !== undefined) { - formulaIdentifier = lookupFormulaIdentifierForName(petName); + formulaIdentifier = identifyLocal(petName); } if (formulaIdentifier === undefined) { const id512 = await randomHex512(); @@ -393,8 +390,7 @@ export const makeHostMaker = ({ * @param {string | 'NONE' | 'SELF' | 'ENDO'} powersName */ const provideWebPage = async (webPageName, bundleName, powersName) => { - const bundleFormulaIdentifier = - lookupFormulaIdentifierForName(bundleName); + const bundleFormulaIdentifier = identifyLocal(bundleName); if (bundleFormulaIdentifier === undefined) { throw new Error(`Unknown pet name: ${q(bundleName)}`); } diff --git a/packages/daemon/src/mail.js b/packages/daemon/src/mail.js index 46cb4200da..9fa128a3cf 100644 --- a/packages/daemon/src/mail.js +++ b/packages/daemon/src/mail.js @@ -35,8 +35,9 @@ export const makeMailboxMaker = ({ /** * @param {string} petName + * @returns {string | undefined} */ - const lookupFormulaIdentifierForName = petName => { + const identifyLocal = petName => { if (Object.hasOwn(specialNames, petName)) { return specialNames[petName]; } @@ -49,7 +50,7 @@ export const makeMailboxMaker = ({ */ const lookup = async (...petNamePath) => { const [headName, ...tailNames] = petNamePath; - const formulaIdentifier = lookupFormulaIdentifierForName(headName); + const formulaIdentifier = identifyLocal(headName); if (formulaIdentifier === undefined) { throw new TypeError(`Unknown pet name: ${q(headName)}`); } @@ -61,7 +62,7 @@ export const makeMailboxMaker = ({ }; const terminate = async petName => { - const formulaIdentifier = lookupFormulaIdentifierForName(petName); + const formulaIdentifier = identifyLocal(petName); if (formulaIdentifier === undefined) { throw new TypeError(`Unknown pet name: ${q(petName)}`); } @@ -113,7 +114,7 @@ export const makeMailboxMaker = ({ // naming hub's formula identifier and the pet name path. // A "naming hub" is an objected with a variadic lookup method. At present, // the only such objects are guests and hosts. - const hubFormulaIdentifier = lookupFormulaIdentifierForName('SELF'); + const hubFormulaIdentifier = identifyLocal('SELF'); const digester = makeSha512(); digester.updateText(`${hubFormulaIdentifier},${petNamePath.join(',')}`); const lookupFormulaNumber = digester.digestHex(); @@ -302,7 +303,7 @@ export const makeMailboxMaker = ({ if (resolveRequest === undefined) { throw new Error(`No pending request for number ${messageNumber}`); } - const formulaIdentifier = lookupFormulaIdentifierForName(resolutionName); + const formulaIdentifier = identifyLocal(resolutionName); if (formulaIdentifier === undefined) { throw new TypeError( `No formula exists for the pet name ${q(resolutionName)}`, @@ -357,8 +358,7 @@ export const makeMailboxMaker = ({ * @param {Array} petNames */ const send = async (recipientName, strings, edgeNames, petNames) => { - const recipientFormulaIdentifier = - lookupFormulaIdentifierForName(recipientName); + const recipientFormulaIdentifier = identifyLocal(recipientName); if (recipientFormulaIdentifier === undefined) { throw new Error(`Unknown pet name for party: ${recipientName}`); } @@ -390,7 +390,7 @@ export const makeMailboxMaker = ({ } const formulaIdentifiers = petNames.map(petName => { - const formulaIdentifier = lookupFormulaIdentifierForName(petName); + const formulaIdentifier = identifyLocal(petName); if (formulaIdentifier === undefined) { throw new Error(`Unknown pet name ${q(petName)}`); } @@ -469,8 +469,7 @@ export const makeMailboxMaker = ({ * @param {string} responseName */ const request = async (recipientName, what, responseName) => { - const recipientFormulaIdentifier = - lookupFormulaIdentifierForName(recipientName); + const recipientFormulaIdentifier = identifyLocal(recipientName); if (recipientFormulaIdentifier === undefined) { throw new Error(`Unknown pet name for party: ${recipientName}`); } @@ -553,7 +552,7 @@ export const makeMailboxMaker = ({ lookup, reverseLookup, reverseLookupFormulaIdentifier, - lookupFormulaIdentifierForName, + identifyLocal, provideLookupFormula, followMessages, listMessages, From 481e3cb3607896996aacb48576bebc5e5ae098f6 Mon Sep 17 00:00:00 2001 From: kumavis Date: Mon, 12 Feb 2024 11:10:20 -1000 Subject: [PATCH 196/234] feat(daemon): expose petStore on host/guest internal facet --- packages/daemon/src/guest.js | 1 + packages/daemon/src/host.js | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/daemon/src/guest.js b/packages/daemon/src/guest.js index 3ec17b4804..c027cb8ee8 100644 --- a/packages/daemon/src/guest.js +++ b/packages/daemon/src/guest.js @@ -101,6 +101,7 @@ export const makeGuestMaker = ({ const internal = harden({ receive, respond, + petStore, }); return harden({ external: guest, internal }); diff --git a/packages/daemon/src/host.js b/packages/daemon/src/host.js index 0975dd2127..a0406d4a73 100644 --- a/packages/daemon/src/host.js +++ b/packages/daemon/src/host.js @@ -466,7 +466,7 @@ export const makeHostMaker = ({ provideWebPage, }); - const internal = harden({ receive, respond }); + const internal = harden({ receive, respond, petStore }); await provideValueForFormulaIdentifier(mainWorkerFormulaIdentifier); From 047e4471c6f081a4cca6be68d8dedf741d202396 Mon Sep 17 00:00:00 2001 From: kumavis Date: Mon, 12 Feb 2024 13:21:14 -1000 Subject: [PATCH 197/234] feat(daemon): can specify introducedNames in provideHost and provideGuest --- packages/daemon/src/daemon.js | 1 + packages/daemon/src/host.js | 56 ++++++++++++++++++++++++---------- packages/daemon/src/types.d.ts | 14 +++++++-- 3 files changed, 53 insertions(+), 18 deletions(-) diff --git a/packages/daemon/src/daemon.js b/packages/daemon/src/daemon.js index a5347575ca..b2e550aaa3 100644 --- a/packages/daemon/src/daemon.js +++ b/packages/daemon/src/daemon.js @@ -644,6 +644,7 @@ const makeEndoBootstrap = ( provideValueForFormulaIdentifier, provideValueForFormula, provideValueForNumberedFormula, + provideControllerForFormulaIdentifier, formulaIdentifierForRef, storeReaderRef, randomHex512, diff --git a/packages/daemon/src/host.js b/packages/daemon/src/host.js index a0406d4a73..ab900d1c6c 100644 --- a/packages/daemon/src/host.js +++ b/packages/daemon/src/host.js @@ -9,6 +9,7 @@ export const makeHostMaker = ({ provideValueForFormulaIdentifier, provideValueForFormula, provideValueForNumberedFormula, + provideControllerForFormulaIdentifier, formulaIdentifierForRef, storeReaderRef, makeSha512, @@ -69,9 +70,28 @@ export const makeHostMaker = ({ }); /** - * @param {string} petName + * @param {import('./types.js').Controller} newController + * @param {Record} introducedNames + * @returns {Promise} */ - const provideGuest = async petName => { + const introduceNamesToNewHostOrGuest = async ( + newController, + introducedNames, + ) => { + const { petStore: newPetStore } = await newController.internal; + await Promise.all( + Object.entries(introducedNames).map(async ([parentName, childName]) => { + const introducedFormulaIdentifier = identifyLocal(parentName); + if (introducedFormulaIdentifier === undefined) { + return; + } + await newPetStore.write(childName, introducedFormulaIdentifier); + }), + ); + }; + + /** @type {import('./types.js').EndoHost['provideGuest']} */ + const provideGuest = async (petName, { introducedNames = {} } = {}) => { /** @type {string | undefined} */ let formulaIdentifier; if (petName !== undefined) { @@ -99,11 +119,14 @@ export const makeHostMaker = ({ )}`, ); } - return /** @type {Promise} */ ( - // Behold, recursion: - // eslint-disable-next-line no-use-before-define - provideValueForFormulaIdentifier(formulaIdentifier) - ); + const newGuestController = + /** @type {import('./types.js').Controller<>} */ ( + provideControllerForFormulaIdentifier(formulaIdentifier) + ); + if (introducedNames !== undefined) { + introduceNamesToNewHostOrGuest(newGuestController, introducedNames); + } + return /** @type {Promise} */ newGuestController.external; }; /** @@ -354,10 +377,8 @@ export const makeHostMaker = ({ ); }; - /** - * @param {string} [petName] - */ - const provideHost = async petName => { + /** @type {import('./types.js').EndoHost['provideHost']} */ + const provideHost = async (petName, { introducedNames = {} } = {}) => { /** @type {string | undefined} */ let formulaIdentifier; if (petName !== undefined) { @@ -377,11 +398,14 @@ export const makeHostMaker = ({ )}`, ); } - return /** @type {Promise} */ ( - // Behold, recursion: - // eslint-disable-next-line no-use-before-define - provideValueForFormulaIdentifier(formulaIdentifier) - ); + const newHostController = + /** @type {import('./types.js').Controller<>} */ ( + provideControllerForFormulaIdentifier(formulaIdentifier) + ); + if (introducedNames !== undefined) { + introduceNamesToNewHostOrGuest(newHostController, introducedNames); + } + return /** @type {Promise} */ newHostController.external; }; /** diff --git a/packages/daemon/src/types.d.ts b/packages/daemon/src/types.d.ts index 65c489f13c..8487b2a56e 100644 --- a/packages/daemon/src/types.d.ts +++ b/packages/daemon/src/types.d.ts @@ -221,6 +221,10 @@ export interface EndoWorker { whenTerminated(): Promise; } +export type MakeHostOrGuestOptions = { + introducedNames?: Record; +}; + export interface EndoGuest { request(what: string, responseName: string): Promise; } @@ -239,8 +243,14 @@ export interface EndoHost { readerRef: ERef>, petName: string, ): Promise; - provideGuest(petName?: string): Promise; - provideHost(petName?: string): Promise; + provideGuest( + petName?: string, + opts?: MakeHostOrGuestOptions, + ): Promise; + provideHost( + petName?: string, + opts?: MakeHostOrGuestOptions, + ): Promise; makeWorker(petName: string): Promise; evaluate( workerPetName: string | undefined, From eb3bb854295902fb205abbc8b2d29ca1f728f803 Mon Sep 17 00:00:00 2001 From: kumavis Date: Mon, 12 Feb 2024 11:26:56 -1000 Subject: [PATCH 198/234] feat(cli): can specify introducedNames in mkhost and mkguest --- packages/cli/src/commands/mkguest.js | 4 ++-- packages/cli/src/commands/mkhost.js | 3 ++- packages/cli/src/endo.js | 20 ++++++++++++++++---- 3 files changed, 20 insertions(+), 7 deletions(-) diff --git a/packages/cli/src/commands/mkguest.js b/packages/cli/src/commands/mkguest.js index 8e59ec4de6..d0b231e5bd 100644 --- a/packages/cli/src/commands/mkguest.js +++ b/packages/cli/src/commands/mkguest.js @@ -3,8 +3,8 @@ import os from 'os'; import { E } from '@endo/far'; import { withEndoParty } from '../context.js'; -export const mkguest = async ({ name, partyNames }) => +export const mkguest = async ({ name, partyNames, introducedNames }) => withEndoParty(partyNames, { os, process }, async ({ party }) => { - const newGuest = await E(party).provideGuest(name); + const newGuest = await E(party).provideGuest(name, { introducedNames }); console.log(newGuest); }); diff --git a/packages/cli/src/commands/mkhost.js b/packages/cli/src/commands/mkhost.js index fd733452e1..2188075c06 100644 --- a/packages/cli/src/commands/mkhost.js +++ b/packages/cli/src/commands/mkhost.js @@ -9,8 +9,9 @@ export const mkhost = async ({ sockPath, name, partyNames, + introducedNames, }) => withEndoParty(partyNames, { os, process }, async ({ party }) => { - const newHost = await E(party).provideHost(name); + const newHost = await E(party).provideHost(name, { introducedNames }); console.log(newHost); }); diff --git a/packages/cli/src/endo.js b/packages/cli/src/endo.js index 514ca9aacb..6eb3809b91 100644 --- a/packages/cli/src/endo.js +++ b/packages/cli/src/endo.js @@ -400,21 +400,33 @@ export const main = async rawArgs => { program .command('mkhost ') .option(...commonOptions.as) + .option( + '--introduce ', + 'Specify initial petnames for the new host. Use multiple times for multiple petnames. Format: --introduce myPetname:theirPetname', + parseOptionAsMapping, + {}, + ) .description('makes a separate mailbox and storage for you') .action(async (name, cmd) => { - const { as: partyNames } = cmd.opts(); + const { as: partyNames, introduce: introducedNames } = cmd.opts(); const { mkhost } = await import('./commands/mkhost.js'); - return mkhost({ name, partyNames }); + return mkhost({ name, partyNames, introducedNames }); }); program .command('mkguest ') .option(...commonOptions.as) + .option( + '--introduce ', + 'Specify initial petnames for the new guest. Use multiple times for multiple petnames. Format: --introduce myPetname:theirPetname', + parseOptionAsMapping, + {}, + ) .description('makes a mailbox and storage for a guest (peer or program)') .action(async (name, cmd) => { - const { as: partyNames } = cmd.opts(); + const { as: partyNames, introduce: introducedNames } = cmd.opts(); const { mkguest } = await import('./commands/mkguest.js'); - return mkguest({ name, partyNames }); + return mkguest({ name, partyNames, introducedNames }); }); program From 4ac8348fc927bd4c59e41a68371da82317eb22ac Mon Sep 17 00:00:00 2001 From: Erik Marks Date: Mon, 12 Feb 2024 16:56:04 -0800 Subject: [PATCH 199/234] chore(daemon): Add test:clean script --- packages/daemon/package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/daemon/package.json b/packages/daemon/package.json index 6a7b68b416..8ba9073629 100644 --- a/packages/daemon/package.json +++ b/packages/daemon/package.json @@ -38,7 +38,8 @@ "lint-fix": "eslint --fix .", "lint:eslint": "eslint .", "lint:types": "tsc", - "test": "ava" + "test": "ava", + "test:clean": "rm -rf tmp && yarn test" }, "dependencies": { "@endo/base64": "^1.0.1", From c5b51047bebcbd132c65f81991669b084bf38846 Mon Sep 17 00:00:00 2001 From: kumavis Date: Mon, 12 Feb 2024 14:56:57 -1000 Subject: [PATCH 200/234] feat(daemon): in host breakout makeHost/makeGuest from provideHost/provideGuest --- packages/daemon/src/daemon.js | 1 - packages/daemon/src/host.js | 51 ++++++++++++++++++++++++++++------- 2 files changed, 41 insertions(+), 11 deletions(-) diff --git a/packages/daemon/src/daemon.js b/packages/daemon/src/daemon.js index b2e550aaa3..1bdb4f4302 100644 --- a/packages/daemon/src/daemon.js +++ b/packages/daemon/src/daemon.js @@ -645,7 +645,6 @@ const makeEndoBootstrap = ( provideValueForFormula, provideValueForNumberedFormula, provideControllerForFormulaIdentifier, - formulaIdentifierForRef, storeReaderRef, randomHex512, makeSha512, diff --git a/packages/daemon/src/host.js b/packages/daemon/src/host.js index ab900d1c6c..545d6345e2 100644 --- a/packages/daemon/src/host.js +++ b/packages/daemon/src/host.js @@ -10,7 +10,6 @@ export const makeHostMaker = ({ provideValueForFormula, provideValueForNumberedFormula, provideControllerForFormulaIdentifier, - formulaIdentifierForRef, storeReaderRef, makeSha512, randomHex512, @@ -90,8 +89,12 @@ export const makeHostMaker = ({ ); }; - /** @type {import('./types.js').EndoHost['provideGuest']} */ - const provideGuest = async (petName, { introducedNames = {} } = {}) => { + /** + * @param {string} [petName] + * @param {import('./types.js').MakeHostOrGuestOptions} [opts] + * @returns {Promise<{formulaIdentifier: string, value: Promise}>} + */ + const makeGuest = async (petName, { introducedNames = {} } = {}) => { /** @type {string | undefined} */ let formulaIdentifier; if (petName !== undefined) { @@ -111,7 +114,7 @@ export const makeHostMaker = ({ assertPetName(petName); await petStore.write(petName, guestFormulaIdentifier); } - return value; + return { value, formulaIdentifier: guestFormulaIdentifier }; } else if (!formulaIdentifier.startsWith('guest-id512:')) { throw new Error( `Existing pet name does not designate a guest powers capability: ${q( @@ -126,7 +129,18 @@ export const makeHostMaker = ({ if (introducedNames !== undefined) { introduceNamesToNewHostOrGuest(newGuestController, introducedNames); } - return /** @type {Promise} */ newGuestController.external; + return { + formulaIdentifier, + value: /** @type {Promise} */ ( + newGuestController.external + ), + }; + }; + + /** @type {import('./types.js').EndoHost['provideGuest']} */ + const provideGuest = async (petName, opts) => { + const { value } = await makeGuest(petName, opts); + return value; }; /** @@ -191,12 +205,14 @@ export const makeHostMaker = ({ /** * @param {string | 'NONE' | 'SELF' | 'ENDO'} partyName + * @returns {Promise} */ const providePowersFormulaIdentifier = async partyName => { let guestFormulaIdentifier = identifyLocal(partyName); if (guestFormulaIdentifier === undefined) { - const guest = await provideGuest(partyName); - guestFormulaIdentifier = formulaIdentifierForRef.get(guest); + ({ formulaIdentifier: guestFormulaIdentifier } = await makeGuest( + partyName, + )); if (guestFormulaIdentifier === undefined) { throw new Error( `panic: provideGuest must return an guest with a corresponding formula identifier`, @@ -377,8 +393,12 @@ export const makeHostMaker = ({ ); }; - /** @type {import('./types.js').EndoHost['provideHost']} */ - const provideHost = async (petName, { introducedNames = {} } = {}) => { + /** + * @param {string} [petName] + * @param {import('./types.js').MakeHostOrGuestOptions} [opts] + * @returns {Promise<{formulaIdentifier: string, value: Promise}>} + */ + const makeHost = async (petName, { introducedNames = {} } = {}) => { /** @type {string | undefined} */ let formulaIdentifier; if (petName !== undefined) { @@ -405,7 +425,18 @@ export const makeHostMaker = ({ if (introducedNames !== undefined) { introduceNamesToNewHostOrGuest(newHostController, introducedNames); } - return /** @type {Promise} */ newHostController.external; + return { + formulaIdentifier, + value: /** @type {Promise} */ ( + newHostController.external + ), + }; + }; + + /** @type {import('./types.js').EndoHost['provideHost']} */ + const provideHost = async (petName, opts) => { + const { value } = await makeHost(petName, opts); + return value; }; /** From 89bc1e1a1c53098a592409c8f4ef37b776400aa1 Mon Sep 17 00:00:00 2001 From: Erik Marks Date: Mon, 12 Feb 2024 22:43:55 -0800 Subject: [PATCH 201/234] fix(daemon): Fix length of zero512 constant Updates the length of the `zero512` constant from 130 to the desired 128, and unifies its multiple declarations in a single file, `common.js`. --- packages/daemon/src/common.js | 2 ++ packages/daemon/src/daemon-node-powers.js | 4 +--- packages/daemon/src/daemon.js | 4 +--- 3 files changed, 4 insertions(+), 6 deletions(-) create mode 100644 packages/daemon/src/common.js diff --git a/packages/daemon/src/common.js b/packages/daemon/src/common.js new file mode 100644 index 0000000000..1a396e5b14 --- /dev/null +++ b/packages/daemon/src/common.js @@ -0,0 +1,2 @@ +export const zero512 = + '00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'; diff --git a/packages/daemon/src/daemon-node-powers.js b/packages/daemon/src/daemon-node-powers.js index d0e29334fb..31d737d637 100644 --- a/packages/daemon/src/daemon-node-powers.js +++ b/packages/daemon/src/daemon-node-powers.js @@ -9,6 +9,7 @@ import { makeReaderRef } from './reader-ref.js'; import { makePetStoreMaker } from './pet-store.js'; import { servePrivatePortHttp } from './serve-private-port-http.js'; import { servePrivatePath } from './serve-private-path.js'; +import { zero512 } from './common.js'; const { quote: q } = assert; @@ -16,9 +17,6 @@ const textEncoder = new TextEncoder(); const medialIterationResult = harden({ done: false }); const finalIterationResult = harden({ done: false }); -const zero512 = - '0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'; - /** * @param {object} modules * @param {typeof import('ws')} modules.ws diff --git a/packages/daemon/src/daemon.js b/packages/daemon/src/daemon.js index 1bdb4f4302..e2b0b7d7a7 100644 --- a/packages/daemon/src/daemon.js +++ b/packages/daemon/src/daemon.js @@ -12,12 +12,10 @@ import { makeHostMaker } from './host.js'; import { assertPetName } from './pet-name.js'; import { makeTerminatorMaker } from './terminator.js'; import { parseFormulaIdentifier } from './formula-identifier.js'; +import { zero512 } from './common.js'; const { quote: q } = assert; -const zero512 = - '0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'; - /** @type {import('./types.js').EndoGuest} */ const leastAuthority = Far('EndoGuest', { async request() { From c3bc771fe26bb60dda006b2bfde734f2984fa0f5 Mon Sep 17 00:00:00 2001 From: Erik Marks Date: Mon, 12 Feb 2024 22:47:42 -0800 Subject: [PATCH 202/234] refactor(daemon): Remove numberless 'pet-store' formula Removes the numberless 'pet-store' formula. The host pet store now the zero id512. In the future we can make this random. --- packages/daemon/src/daemon.js | 12 +++--------- packages/daemon/src/formula-identifier.js | 1 - packages/daemon/src/pet-store.js | 12 +----------- packages/daemon/src/types.d.ts | 4 ---- 4 files changed, 4 insertions(+), 25 deletions(-) diff --git a/packages/daemon/src/daemon.js b/packages/daemon/src/daemon.js index e2b0b7d7a7..f2fc047f66 100644 --- a/packages/daemon/src/daemon.js +++ b/packages/daemon/src/daemon.js @@ -407,19 +407,13 @@ const makeEndoBootstrap = ( ) => { const { type: formulaType, number: formulaNumber } = parseFormulaIdentifier(formulaIdentifier); - if (formulaIdentifier === 'pet-store') { - const external = petStorePowers.makeOwnPetStore( - 'pet-store', - assertPetName, - ); - return { external, internal: undefined }; - } else if (formulaIdentifier === 'pet-inspector') { + if (formulaIdentifier === 'pet-inspector') { // Behold, unavoidable forward-reference: // eslint-disable-next-line no-use-before-define - const external = makePetStoreInspector('pet-store'); + const external = makePetStoreInspector(`pet-store-id512:${zero512}`); return { external, internal: undefined }; } else if (formulaIdentifier === 'host') { - const storeFormulaIdentifier = 'pet-store'; + const storeFormulaIdentifier = `pet-store-id512:${zero512}`; const inspectorFormulaIdentifier = 'pet-inspector'; const workerFormulaIdentifier = `worker-id512:${zero512}`; // Behold, recursion: diff --git a/packages/daemon/src/formula-identifier.js b/packages/daemon/src/formula-identifier.js index b37e4a59a2..4f1edf86bb 100644 --- a/packages/daemon/src/formula-identifier.js +++ b/packages/daemon/src/formula-identifier.js @@ -5,7 +5,6 @@ const numberlessFormulasIdentifiers = new Set([ 'host', 'least-authority', 'pet-inspector', - 'pet-store', 'web-page-js', ]); diff --git a/packages/daemon/src/pet-store.js b/packages/daemon/src/pet-store.js index a085dedce4..5c9c8ce492 100644 --- a/packages/daemon/src/pet-store.js +++ b/packages/daemon/src/pet-store.js @@ -9,7 +9,7 @@ const { quote: q } = assert; const validIdPattern = /^[0-9a-f]{128}$/; const validFormulaPattern = - /^(?:host|pet-store|pet-inspector|(?:readable-blob-sha512|worker-id512|pet-store-id512|eval-id512|lookup-id512|make-unconfined-id512|make-bundle-id512|host-id512|guest-id512):[0-9a-f]{128}|web-bundle:[0-9a-f]{32})$/; + /^(?:host|pet-inspector|(?:readable-blob-sha512|worker-id512|pet-store-id512|eval-id512|lookup-id512|make-unconfined-id512|make-bundle-id512|host-id512|guest-id512):[0-9a-f]{128}|web-bundle:[0-9a-f]{32})$/; /** * @param {import('./types.js').FilePowers} filePowers @@ -294,17 +294,7 @@ export const makePetStoreMaker = (filePowers, locator) => { return makePetStoreAtPath(petNameDirectoryPath, assertValidName); }; - /** - * @param {string} name - * @param {(name: string) => void} assertValidName - */ - const makeOwnPetStore = (name, assertValidName) => { - const petNameDirectoryPath = filePowers.joinPath(locator.statePath, name); - return makePetStoreAtPath(petNameDirectoryPath, assertValidName); - }; - return { makeIdentifiedPetStore, - makeOwnPetStore, }; }; diff --git a/packages/daemon/src/types.d.ts b/packages/daemon/src/types.d.ts index 8487b2a56e..38ffb872ff 100644 --- a/packages/daemon/src/types.d.ts +++ b/packages/daemon/src/types.d.ts @@ -319,10 +319,6 @@ export type PetStorePowers = { id: string, assertValidName: AssertValidNameFn, ) => Promise>; - makeOwnPetStore: ( - name: string, - assertValidName: AssertValidNameFn, - ) => Promise>; }; export type NetworkPowers = { From 5ce98ee75e46c767ef3f7a64f9da79d921f5acfe Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Tue, 24 Oct 2023 12:11:22 -0700 Subject: [PATCH 203/234] refactor(daemon): Extract SocketPowers --- packages/daemon/src/daemon-node-powers.js | 22 ++++++++++++++++------ packages/daemon/src/types.d.ts | 5 ++++- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/packages/daemon/src/daemon-node-powers.js b/packages/daemon/src/daemon-node-powers.js index 31d737d637..327a368ec2 100644 --- a/packages/daemon/src/daemon-node-powers.js +++ b/packages/daemon/src/daemon-node-powers.js @@ -179,13 +179,9 @@ export const makeHttpPowers = ({ http, ws }) => { /** * @param {object} modules * @param {typeof import('net')} modules.net - * @param {typeof import('http')} modules.http - * @param {typeof import('ws')} modules.ws - * @returns {import('./types.js').NetworkPowers} + * @returns {import('./types.js').SocketPowers} */ -export const makeNetworkPowers = ({ http, ws, net }) => { - const { servePortHttp } = makeHttpPowers({ http, ws }); - +export const makeSocketPowers = ({ net }) => { const serveListener = async (listen, cancelled) => { const [ /** @type {Reader} */ readFrom, @@ -260,6 +256,20 @@ export const makeNetworkPowers = ({ http, ws, net }) => { ); }, cancelled); + return { servePort, servePath }; +}; + +/** + * @param {object} modules + * @param {typeof import('net')} modules.net + * @param {typeof import('http')} modules.http + * @param {typeof import('ws')} modules.ws + * @returns {import('./types.js').NetworkPowers} + */ +export const makeNetworkPowers = ({ http, ws, net }) => { + const { servePortHttp } = makeHttpPowers({ http, ws }); + const { servePort, servePath } = makeSocketPowers({ net }); + const connectionNumbers = (function* generateNumbers() { let n = 0; for (;;) { diff --git a/packages/daemon/src/types.d.ts b/packages/daemon/src/types.d.ts index 38ffb872ff..21ea1f2acc 100644 --- a/packages/daemon/src/types.d.ts +++ b/packages/daemon/src/types.d.ts @@ -321,7 +321,7 @@ export type PetStorePowers = { ) => Promise>; }; -export type NetworkPowers = { +export type SocketPowers = { servePath: (args: { path: string; host?: string; @@ -332,6 +332,9 @@ export type NetworkPowers = { host?: string; cancelled: Promise; }) => Promise>; +}; + +export type NetworkPowers = SocketPowers & { servePortHttp: (args: { port: number; host?: string; From d0a9168f79595e1fb9ef05566003acf5d994f589 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Mon, 1 Jan 2024 22:30:15 -0800 Subject: [PATCH 204/234] feat(daemon): Node socket server exposes assigned port --- packages/daemon/src/daemon-node-powers.js | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/packages/daemon/src/daemon-node-powers.js b/packages/daemon/src/daemon-node-powers.js index 327a368ec2..28514dad09 100644 --- a/packages/daemon/src/daemon-node-powers.js +++ b/packages/daemon/src/daemon-node-powers.js @@ -215,7 +215,12 @@ export const makeSocketPowers = ({ net }) => { void writeTo.next({ reader, writer, closed }); }); - return readFrom; + const port = await listening; + + return harden({ + port, + connections: readFrom, + }); }; /** @@ -228,7 +233,7 @@ export const makeSocketPowers = ({ net }) => { serveListener( server => new Promise(resolve => - server.listen(port, host, () => resolve(undefined)), + server.listen(port, host, () => resolve(server.address().port)), ), cancelled, ); @@ -238,8 +243,8 @@ export const makeSocketPowers = ({ net }) => { * @param {string} args.path * @param {Promise} args.cancelled */ - const servePath = async ({ path, cancelled }) => - serveListener(server => { + const servePath = async ({ path, cancelled }) => { + const { connections } = await serveListener(server => { return new Promise((resolve, reject) => server.listen({ path }, error => { if (error) { @@ -255,6 +260,8 @@ export const makeSocketPowers = ({ net }) => { }), ); }, cancelled); + return connections; + }; return { servePort, servePath }; }; From 82c45af49fabcf484aa3837e3a24bcb05b0e9806 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Mon, 1 Jan 2024 22:30:43 -0800 Subject: [PATCH 205/234] feat(daemon): Add TCP connect to Node powers --- packages/daemon/src/daemon-node-powers.js | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/packages/daemon/src/daemon-node-powers.js b/packages/daemon/src/daemon-node-powers.js index 28514dad09..e9e44dadf2 100644 --- a/packages/daemon/src/daemon-node-powers.js +++ b/packages/daemon/src/daemon-node-powers.js @@ -238,6 +238,24 @@ export const makeSocketPowers = ({ net }) => { cancelled, ); + const connectPort = ({ port, host, cancelled }) => + new Promise((resolve, reject) => { + const conn = net.connect(port, host, err => { + if (err) { + reject(err); + return; + } + const reader = makeNodeReader(conn); + const writer = makeNodeWriter(conn); + const closed = new Promise(close => conn.on('close', close)); + resolve({ + reader, + writer, + closed, + }); + }); + }); + /** * @param {object} args * @param {string} args.path @@ -263,7 +281,7 @@ export const makeSocketPowers = ({ net }) => { return connections; }; - return { servePort, servePath }; + return { servePort, servePath, connectPort }; }; /** @@ -275,7 +293,7 @@ export const makeSocketPowers = ({ net }) => { */ export const makeNetworkPowers = ({ http, ws, net }) => { const { servePortHttp } = makeHttpPowers({ http, ws }); - const { servePort, servePath } = makeSocketPowers({ net }); + const { servePort, servePath, connectPort } = makeSocketPowers({ net }); const connectionNumbers = (function* generateNumbers() { let n = 0; @@ -338,6 +356,7 @@ export const makeNetworkPowers = ({ http, ws, net }) => { servePortHttp, servePort, servePath, + connectPort, makePrivatePathService, makePrivateHttpService, }); From cb844215679dcdbd7f55c9b7d84f19f71dc5279a Mon Sep 17 00:00:00 2001 From: kumavis Date: Tue, 13 Feb 2024 00:06:26 -1000 Subject: [PATCH 206/234] chore(daemon): improve types for SocketPowers --- packages/daemon/src/daemon-node-powers.js | 14 +++----------- packages/daemon/src/types.d.ts | 15 +++++++++++---- 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/packages/daemon/src/daemon-node-powers.js b/packages/daemon/src/daemon-node-powers.js index e9e44dadf2..b6af7e6fff 100644 --- a/packages/daemon/src/daemon-node-powers.js +++ b/packages/daemon/src/daemon-node-powers.js @@ -223,12 +223,7 @@ export const makeSocketPowers = ({ net }) => { }); }; - /** - * @param {object} args - * @param {number} args.port - * @param {string} [args.host] - * @param {Promise} args.cancelled - */ + /** @type {import('./types.js').SocketPowers['servePort']} */ const servePort = async ({ port, host = '0.0.0.0', cancelled }) => serveListener( server => @@ -238,6 +233,7 @@ export const makeSocketPowers = ({ net }) => { cancelled, ); + /** @type {import('./types.js').SocketPowers['connectPort']} */ const connectPort = ({ port, host, cancelled }) => new Promise((resolve, reject) => { const conn = net.connect(port, host, err => { @@ -256,11 +252,7 @@ export const makeSocketPowers = ({ net }) => { }); }); - /** - * @param {object} args - * @param {string} args.path - * @param {Promise} args.cancelled - */ + /** @type {import('./types.js').SocketPowers['servePath']} */ const servePath = async ({ path, cancelled }) => { const { connections } = await serveListener(server => { return new Promise((resolve, reject) => diff --git a/packages/daemon/src/types.d.ts b/packages/daemon/src/types.d.ts index 21ea1f2acc..6c3b3ba83b 100644 --- a/packages/daemon/src/types.d.ts +++ b/packages/daemon/src/types.d.ts @@ -322,15 +322,22 @@ export type PetStorePowers = { }; export type SocketPowers = { - servePath: (args: { - path: string; + servePort: (args: { + port: number; host?: string; cancelled: Promise; - }) => Promise>; - servePort: (args: { + }) => Promise<{ + port: number; + connections: Reader; + }>; + connectPort: (args: { port: number; host?: string; cancelled: Promise; + }) => Promise; + servePath: (args: { + path: string; + cancelled: Promise; }) => Promise>; }; From 87e49224a860c555397e6879f5c4ccfcf767cb54 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Wed, 20 Dec 2023 13:48:27 -0800 Subject: [PATCH 207/234] fix(cli): Minor adopt description typo --- packages/cli/src/endo.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cli/src/endo.js b/packages/cli/src/endo.js index 6eb3809b91..fd0f898c1d 100644 --- a/packages/cli/src/endo.js +++ b/packages/cli/src/endo.js @@ -228,7 +228,7 @@ export const main = async rawArgs => { 'Name to use, if different than the suggested name.', ) .option(...commonOptions.as) - .description('accept a @value from a message') + .description('adopt a @value from a message') .action(async (messageNumberText, edgeName, cmd) => { const { name = edgeName, as: partyNames } = cmd.opts(); const { adoptCommand } = await import('./commands/adopt.js'); From dd9552dba6292e798b7ebedd1482466f49582ceb Mon Sep 17 00:00:00 2001 From: Erik Marks Date: Tue, 13 Feb 2024 09:13:12 -0800 Subject: [PATCH 208/234] refactor(daemon): Remove numberless inspector formula Replaces the numberless `pet-inspector` formula with the id512-equivalent. The root inspector has the zero id. This can be replaced with a random number in the future. --- packages/daemon/src/daemon.js | 9 ++------- packages/daemon/src/formula-identifier.js | 1 - packages/daemon/src/pet-store.js | 2 +- 3 files changed, 3 insertions(+), 9 deletions(-) diff --git a/packages/daemon/src/daemon.js b/packages/daemon/src/daemon.js index f2fc047f66..5dc11d9097 100644 --- a/packages/daemon/src/daemon.js +++ b/packages/daemon/src/daemon.js @@ -407,14 +407,9 @@ const makeEndoBootstrap = ( ) => { const { type: formulaType, number: formulaNumber } = parseFormulaIdentifier(formulaIdentifier); - if (formulaIdentifier === 'pet-inspector') { - // Behold, unavoidable forward-reference: - // eslint-disable-next-line no-use-before-define - const external = makePetStoreInspector(`pet-store-id512:${zero512}`); - return { external, internal: undefined }; - } else if (formulaIdentifier === 'host') { + if (formulaIdentifier === 'host') { const storeFormulaIdentifier = `pet-store-id512:${zero512}`; - const inspectorFormulaIdentifier = 'pet-inspector'; + const inspectorFormulaIdentifier = `pet-inspector-id512:${zero512}`; const workerFormulaIdentifier = `worker-id512:${zero512}`; // Behold, recursion: // eslint-disable-next-line no-use-before-define diff --git a/packages/daemon/src/formula-identifier.js b/packages/daemon/src/formula-identifier.js index 4f1edf86bb..8464c68a9a 100644 --- a/packages/daemon/src/formula-identifier.js +++ b/packages/daemon/src/formula-identifier.js @@ -4,7 +4,6 @@ const numberlessFormulasIdentifiers = new Set([ 'endo', 'host', 'least-authority', - 'pet-inspector', 'web-page-js', ]); diff --git a/packages/daemon/src/pet-store.js b/packages/daemon/src/pet-store.js index 5c9c8ce492..bfc9014bb4 100644 --- a/packages/daemon/src/pet-store.js +++ b/packages/daemon/src/pet-store.js @@ -9,7 +9,7 @@ const { quote: q } = assert; const validIdPattern = /^[0-9a-f]{128}$/; const validFormulaPattern = - /^(?:host|pet-inspector|(?:readable-blob-sha512|worker-id512|pet-store-id512|eval-id512|lookup-id512|make-unconfined-id512|make-bundle-id512|host-id512|guest-id512):[0-9a-f]{128}|web-bundle:[0-9a-f]{32})$/; + /^(?:host|(?:readable-blob-sha512|worker-id512|pet-store-id512|pet-inspector-id512|eval-id512|lookup-id512|make-unconfined-id512|make-bundle-id512|host-id512|guest-id512):[0-9a-f]{128}|web-bundle:[0-9a-f]{32})$/; /** * @param {import('./types.js').FilePowers} filePowers From bfcf9feb49f9477ba0b87cc7615a596a7bc15eb4 Mon Sep 17 00:00:00 2001 From: Erik Marks Date: Tue, 13 Feb 2024 09:06:26 -0800 Subject: [PATCH 209/234] refactor(daemon): Remove numberless host formula Replaces the numberless `host` formula with the zero `host-id512` formula. Can be replaced with something random in the future. --- packages/daemon/src/daemon-node-powers.js | 2 +- packages/daemon/src/daemon.js | 18 +++--------------- packages/daemon/src/formula-identifier.js | 1 - packages/daemon/src/pet-store.js | 2 +- 4 files changed, 5 insertions(+), 18 deletions(-) diff --git a/packages/daemon/src/daemon-node-powers.js b/packages/daemon/src/daemon-node-powers.js index 31d737d637..8208d8e809 100644 --- a/packages/daemon/src/daemon-node-powers.js +++ b/packages/daemon/src/daemon-node-powers.js @@ -586,7 +586,7 @@ export const makeDaemonicPersistencePowers = ( ? { type: /** @type {'make-unconfined'} */ ('make-unconfined'), worker: `worker-id512:${zero512}`, - powers: 'host', + powers: `host-id512:${zero512}`, importPath: fileURLToPath( new URL('web-page-bundler.js', import.meta.url).href, ), diff --git a/packages/daemon/src/daemon.js b/packages/daemon/src/daemon.js index 5dc11d9097..1c9e1abf94 100644 --- a/packages/daemon/src/daemon.js +++ b/packages/daemon/src/daemon.js @@ -407,20 +407,7 @@ const makeEndoBootstrap = ( ) => { const { type: formulaType, number: formulaNumber } = parseFormulaIdentifier(formulaIdentifier); - if (formulaIdentifier === 'host') { - const storeFormulaIdentifier = `pet-store-id512:${zero512}`; - const inspectorFormulaIdentifier = `pet-inspector-id512:${zero512}`; - const workerFormulaIdentifier = `worker-id512:${zero512}`; - // Behold, recursion: - // eslint-disable-next-line no-use-before-define - return makeIdentifiedHost( - formulaIdentifier, - storeFormulaIdentifier, - inspectorFormulaIdentifier, - workerFormulaIdentifier, - terminator, - ); - } else if (formulaIdentifier === 'endo') { + if (formulaIdentifier === 'endo') { // TODO reframe "cancelled" as termination of the "endo" object and // ensure that all values ultimately depend on "endo". // Behold, self-referentiality: @@ -462,6 +449,7 @@ const makeEndoBootstrap = ( const storeFormulaIdentifier = `pet-store-id512:${formulaNumber}`; const inspectorFormulaIdentifier = `pet-inspector-id512:${formulaNumber}`; const workerFormulaIdentifier = `worker-id512:${formulaNumber}`; + // Behold, recursion: // eslint-disable-next-line no-use-before-define return makeIdentifiedHost( @@ -768,7 +756,7 @@ const makeEndoBootstrap = ( cancel(new Error('Termination requested')); }, - host: () => provideValueForFormulaIdentifier('host'), + host: () => provideValueForFormulaIdentifier(`host-id512:${zero512}`), leastAuthority: () => leastAuthority, diff --git a/packages/daemon/src/formula-identifier.js b/packages/daemon/src/formula-identifier.js index 8464c68a9a..e1ec46326b 100644 --- a/packages/daemon/src/formula-identifier.js +++ b/packages/daemon/src/formula-identifier.js @@ -2,7 +2,6 @@ const { quote: q } = assert; const numberlessFormulasIdentifiers = new Set([ 'endo', - 'host', 'least-authority', 'web-page-js', ]); diff --git a/packages/daemon/src/pet-store.js b/packages/daemon/src/pet-store.js index bfc9014bb4..5586febb6d 100644 --- a/packages/daemon/src/pet-store.js +++ b/packages/daemon/src/pet-store.js @@ -9,7 +9,7 @@ const { quote: q } = assert; const validIdPattern = /^[0-9a-f]{128}$/; const validFormulaPattern = - /^(?:host|(?:readable-blob-sha512|worker-id512|pet-store-id512|pet-inspector-id512|eval-id512|lookup-id512|make-unconfined-id512|make-bundle-id512|host-id512|guest-id512):[0-9a-f]{128}|web-bundle:[0-9a-f]{32})$/; + /^(?:(?:readable-blob-sha512|worker-id512|pet-store-id512|pet-inspector-id512|eval-id512|lookup-id512|make-unconfined-id512|make-bundle-id512|host-id512|guest-id512):[0-9a-f]{128}|web-bundle:[0-9a-f]{32})$/; /** * @param {import('./types.js').FilePowers} filePowers From 9f04c3be4b636d7acbf3d40f00bbcb04a8f85dd3 Mon Sep 17 00:00:00 2001 From: Erik Marks Date: Tue, 13 Feb 2024 10:20:59 -0800 Subject: [PATCH 210/234] refactor(daemon): Make least-authority formula numbered Converts the `least-authority` formula to a numbered, id512 equivalent. The formula number used will always be the zero id512, but this can be changed in the future. --- packages/daemon/src/daemon.js | 4 +++- packages/daemon/src/formula-identifier.js | 6 +----- packages/daemon/src/host.js | 4 +++- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/daemon/src/daemon.js b/packages/daemon/src/daemon.js index 1c9e1abf94..3af3d6e066 100644 --- a/packages/daemon/src/daemon.js +++ b/packages/daemon/src/daemon.js @@ -413,7 +413,7 @@ const makeEndoBootstrap = ( // Behold, self-referentiality: // eslint-disable-next-line no-use-before-define return { external: endoBootstrap, internal: undefined }; - } else if (formulaIdentifier === 'least-authority') { + } else if (formulaType === 'least-authority-id512') { return { external: leastAuthority, internal: undefined }; } else if (formulaIdentifier === 'web-page-js') { if (persistencePowers.webPageBundlerFormula === undefined) { @@ -449,6 +449,7 @@ const makeEndoBootstrap = ( const storeFormulaIdentifier = `pet-store-id512:${formulaNumber}`; const inspectorFormulaIdentifier = `pet-inspector-id512:${formulaNumber}`; const workerFormulaIdentifier = `worker-id512:${formulaNumber}`; + const leastAuthorityFormulaIdentifier = `least-authority-id512:${zero512}`; // Behold, recursion: // eslint-disable-next-line no-use-before-define @@ -457,6 +458,7 @@ const makeEndoBootstrap = ( storeFormulaIdentifier, inspectorFormulaIdentifier, workerFormulaIdentifier, + leastAuthorityFormulaIdentifier, terminator, ); } else if ( diff --git a/packages/daemon/src/formula-identifier.js b/packages/daemon/src/formula-identifier.js index e1ec46326b..af42d6dd10 100644 --- a/packages/daemon/src/formula-identifier.js +++ b/packages/daemon/src/formula-identifier.js @@ -1,10 +1,6 @@ const { quote: q } = assert; -const numberlessFormulasIdentifiers = new Set([ - 'endo', - 'least-authority', - 'web-page-js', -]); +const numberlessFormulasIdentifiers = new Set(['endo', 'web-page-js']); /** * @param {string} formulaIdentifier diff --git a/packages/daemon/src/host.js b/packages/daemon/src/host.js index 545d6345e2..d79df16c15 100644 --- a/packages/daemon/src/host.js +++ b/packages/daemon/src/host.js @@ -20,6 +20,7 @@ export const makeHostMaker = ({ * @param {string} storeFormulaIdentifier * @param {string} inspectorFormulaIdentifier * @param {string} mainWorkerFormulaIdentifier + * @param {string} leastAuthorityFormulaIdentifier * @param {import('./types.js').Terminator} terminator */ const makeIdentifiedHost = async ( @@ -27,6 +28,7 @@ export const makeHostMaker = ({ storeFormulaIdentifier, inspectorFormulaIdentifier, mainWorkerFormulaIdentifier, + leastAuthorityFormulaIdentifier, terminator, ) => { terminator.thisDiesIfThatDies(storeFormulaIdentifier); @@ -62,7 +64,7 @@ export const makeHostMaker = ({ specialNames: { SELF: hostFormulaIdentifier, INFO: inspectorFormulaIdentifier, - NONE: 'least-authority', + NONE: leastAuthorityFormulaIdentifier, ENDO: 'endo', }, terminator, From 44207ad8dac48163e90364e03ae34d42ebf69243 Mon Sep 17 00:00:00 2001 From: Erik Marks Date: Tue, 13 Feb 2024 10:34:25 -0800 Subject: [PATCH 211/234] refactor(daemon): Make web-page-js formula numbered Converts the `web-page-js` formula to a numbered, id512 equivalent. The formula number used will always be the zero id512, but this can be changed in the future. --- packages/daemon/src/daemon.js | 12 ++++++++---- packages/daemon/src/formula-identifier.js | 2 +- packages/daemon/src/web-page-bundler.js | 2 +- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/packages/daemon/src/daemon.js b/packages/daemon/src/daemon.js index 3af3d6e066..3ad08462b2 100644 --- a/packages/daemon/src/daemon.js +++ b/packages/daemon/src/daemon.js @@ -415,12 +415,15 @@ const makeEndoBootstrap = ( return { external: endoBootstrap, internal: undefined }; } else if (formulaType === 'least-authority-id512') { return { external: leastAuthority, internal: undefined }; - } else if (formulaIdentifier === 'web-page-js') { + } else if (formulaType === 'web-page-js-id512') { if (persistencePowers.webPageBundlerFormula === undefined) { - throw Error('No web-page-js formula provided.'); + throw Error('No web-page-js-id512 formula provided.'); + } + if (formulaNumber !== zero512) { + throw Error('Invalid web-page-js-id512 formula number.'); } return makeControllerForFormula( - 'web-page-js', + formulaIdentifier, zero512, persistencePowers.webPageBundlerFormula, terminator, @@ -762,7 +765,8 @@ const makeEndoBootstrap = ( leastAuthority: () => leastAuthority, - webPageJs: () => provideValueForFormulaIdentifier('web-page-js'), + webPageJs: () => + provideValueForFormulaIdentifier(`web-page-js-id512:${zero512}`), importAndEndowInWebPage: async (webPageP, webPageNumber) => { const { bundle: bundleBlob, powers: endowedPowers } = diff --git a/packages/daemon/src/formula-identifier.js b/packages/daemon/src/formula-identifier.js index af42d6dd10..b11bbbfb58 100644 --- a/packages/daemon/src/formula-identifier.js +++ b/packages/daemon/src/formula-identifier.js @@ -1,6 +1,6 @@ const { quote: q } = assert; -const numberlessFormulasIdentifiers = new Set(['endo', 'web-page-js']); +const numberlessFormulasIdentifiers = new Set(['endo']); /** * @param {string} formulaIdentifier diff --git a/packages/daemon/src/web-page-bundler.js b/packages/daemon/src/web-page-bundler.js index a59502ea39..00516da8aa 100644 --- a/packages/daemon/src/web-page-bundler.js +++ b/packages/daemon/src/web-page-bundler.js @@ -2,7 +2,7 @@ // This is a built-in unconfined plugin for lazily constructing the web-page.js // bundle for booting up web caplets. -// The hard-coded 'web-page-js' formula is a hard-coded 'make-unconfined' formula +// The hard-coded 'web-page-js-id512' formula is a hard-coded 'make-unconfined' formula // that runs this program in worker 0. // It does not accept its endowed powers. From 382bbf46dec944588d8410953500aae007389fb2 Mon Sep 17 00:00:00 2001 From: Erik Marks Date: Tue, 13 Feb 2024 11:50:08 -0800 Subject: [PATCH 212/234] refactor(daemon): Make endo formula numbered Converts the `endo` formula to a numbered, id512 equivalent. The formula number used will always be the zero id512, but this can be changed in the future. --- packages/daemon/src/daemon.js | 8 +++++++- packages/daemon/src/formula-identifier.js | 13 ++++--------- packages/daemon/src/host.js | 4 +++- 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/packages/daemon/src/daemon.js b/packages/daemon/src/daemon.js index 3ad08462b2..b45b17ee79 100644 --- a/packages/daemon/src/daemon.js +++ b/packages/daemon/src/daemon.js @@ -407,7 +407,11 @@ const makeEndoBootstrap = ( ) => { const { type: formulaType, number: formulaNumber } = parseFormulaIdentifier(formulaIdentifier); - if (formulaIdentifier === 'endo') { + if (formulaType === 'endo-id512') { + if (formulaNumber !== zero512) { + throw Error('Invalid endo-id512 formula number.'); + } + // TODO reframe "cancelled" as termination of the "endo" object and // ensure that all values ultimately depend on "endo". // Behold, self-referentiality: @@ -449,6 +453,7 @@ const makeEndoBootstrap = ( ); return { external, internal: undefined }; } else if (formulaType === 'host-id512') { + const endoFormulaIdentifier = `endo-id512:${zero512}`; const storeFormulaIdentifier = `pet-store-id512:${formulaNumber}`; const inspectorFormulaIdentifier = `pet-inspector-id512:${formulaNumber}`; const workerFormulaIdentifier = `worker-id512:${formulaNumber}`; @@ -458,6 +463,7 @@ const makeEndoBootstrap = ( // eslint-disable-next-line no-use-before-define return makeIdentifiedHost( formulaIdentifier, + endoFormulaIdentifier, storeFormulaIdentifier, inspectorFormulaIdentifier, workerFormulaIdentifier, diff --git a/packages/daemon/src/formula-identifier.js b/packages/daemon/src/formula-identifier.js index b11bbbfb58..a93e86f014 100644 --- a/packages/daemon/src/formula-identifier.js +++ b/packages/daemon/src/formula-identifier.js @@ -1,7 +1,5 @@ const { quote: q } = assert; -const numberlessFormulasIdentifiers = new Set(['endo']); - /** * @param {string} formulaIdentifier * @returns {import("./types").FormulaIdentifierRecord} @@ -9,14 +7,11 @@ const numberlessFormulasIdentifiers = new Set(['endo']); export const parseFormulaIdentifier = formulaIdentifier => { const delimiterIndex = formulaIdentifier.indexOf(':'); if (delimiterIndex < 0) { - if (numberlessFormulasIdentifiers.has(formulaIdentifier)) { - return { type: formulaIdentifier, number: '' }; - } else { - throw new TypeError( - `Formula identifier must have a colon: ${q(formulaIdentifier)}`, - ); - } + throw new TypeError( + `Formula identifier must have a colon: ${q(formulaIdentifier)}`, + ); } + const type = formulaIdentifier.slice(0, delimiterIndex); const number = formulaIdentifier.slice(delimiterIndex + 1); return { type, number }; diff --git a/packages/daemon/src/host.js b/packages/daemon/src/host.js index d79df16c15..5e0173100a 100644 --- a/packages/daemon/src/host.js +++ b/packages/daemon/src/host.js @@ -17,6 +17,7 @@ export const makeHostMaker = ({ }) => { /** * @param {string} hostFormulaIdentifier + * @param {string} endoFormulaIdentifier * @param {string} storeFormulaIdentifier * @param {string} inspectorFormulaIdentifier * @param {string} mainWorkerFormulaIdentifier @@ -25,6 +26,7 @@ export const makeHostMaker = ({ */ const makeIdentifiedHost = async ( hostFormulaIdentifier, + endoFormulaIdentifier, storeFormulaIdentifier, inspectorFormulaIdentifier, mainWorkerFormulaIdentifier, @@ -63,9 +65,9 @@ export const makeHostMaker = ({ selfFormulaIdentifier: hostFormulaIdentifier, specialNames: { SELF: hostFormulaIdentifier, + ENDO: endoFormulaIdentifier, INFO: inspectorFormulaIdentifier, NONE: leastAuthorityFormulaIdentifier, - ENDO: 'endo', }, terminator, }); From c3516f42904eb20a676decc675c162122d7c35a1 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Thu, 14 Dec 2023 15:07:09 -0800 Subject: [PATCH 213/234] feat(daemon): List special names --- packages/daemon/src/guest.js | 13 ++++++------- packages/daemon/src/host.js | 13 ++++++------- packages/daemon/src/mail.js | 10 ++++++++++ 3 files changed, 22 insertions(+), 14 deletions(-) diff --git a/packages/daemon/src/guest.js b/packages/daemon/src/guest.js index c027cb8ee8..1aa66389db 100644 --- a/packages/daemon/src/guest.js +++ b/packages/daemon/src/guest.js @@ -59,6 +59,9 @@ export const makeGuestMaker = ({ request, rename, remove, + list, + listSpecial, + listAll, } = makeMailbox({ petStore, selfFormulaIdentifier: guestFormulaIdentifier, @@ -69,13 +72,7 @@ export const makeGuestMaker = ({ terminator, }); - const { - has, - list, - follow: followNames, - listEntries, - followEntries, - } = petStore; + const { has, follow: followNames, listEntries, followEntries } = petStore; /** @type {import('@endo/eventual-send').ERef} */ const guest = Far('EndoGuest', { @@ -85,6 +82,8 @@ export const makeGuestMaker = ({ request, send, list, + listSpecial, + listAll, followNames, listMessages, followMessages, diff --git a/packages/daemon/src/host.js b/packages/daemon/src/host.js index 5e0173100a..3d3dae0d5c 100644 --- a/packages/daemon/src/host.js +++ b/packages/daemon/src/host.js @@ -57,6 +57,9 @@ export const makeHostMaker = ({ send, dismiss, adopt, + list, + listAll, + listSpecial, rename, remove, terminate, @@ -486,13 +489,7 @@ export const makeHostMaker = ({ return value; }; - const { - has, - list, - follow: followNames, - listEntries, - followEntries, - } = petStore; + const { has, follow: followNames, listEntries, followEntries } = petStore; /** @type {import('./types.js').EndoHost} */ const host = Far('EndoHost', { @@ -508,6 +505,8 @@ export const makeHostMaker = ({ request, send, list, + listSpecial, + listAll, followNames, listEntries, followEntries, diff --git a/packages/daemon/src/mail.js b/packages/daemon/src/mail.js index 9fa128a3cf..ec0d9bdec8 100644 --- a/packages/daemon/src/mail.js +++ b/packages/daemon/src/mail.js @@ -75,6 +75,13 @@ export const makeMailboxMaker = ({ return controller.terminator.terminate(); }; + const list = () => harden(petStore.list()); + + const listSpecial = () => harden(Object.keys(specialNames).sort()); + + const listAll = () => + harden([...Object.keys(specialNames).sort(), ...petStore.list()]); + /** * @param {string} formulaIdentifier */ @@ -564,6 +571,9 @@ export const makeMailboxMaker = ({ send, dismiss, adopt, + list, + listSpecial, + listAll, rename, remove, terminate, From 23fba2d382c362912c06fd5d6a8e1f618fa32a30 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Fri, 29 Dec 2023 16:56:35 -0800 Subject: [PATCH 214/234] test(daemon): Test list methods --- packages/daemon/test/test-endo.js | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/packages/daemon/test/test-endo.js b/packages/daemon/test/test-endo.js index 683aafc99e..70f086f760 100644 --- a/packages/daemon/test/test-endo.js +++ b/packages/daemon/test/test-endo.js @@ -1071,3 +1071,34 @@ test('evaluate name resolved by lookup path', async t => { await stop(locator); }); + +test('list special names', async t => { + const { promise: cancelled, reject: cancel } = makePromiseKit(); + t.teardown(() => cancel(Error('teardown'))); + const locator = makeLocator('tmp', 'list-names'); + + await stop(locator).catch(() => {}); + await reset(locator); + await start(locator); + + const { getBootstrap } = await makeEndoClient( + 'client', + locator.sockPath, + cancelled, + ); + const bootstrap = getBootstrap(); + const host = E(bootstrap).host(); + + const readerRef = makeReaderRef([new TextEncoder().encode('hello\n')]); + await E(host).store(readerRef, 'hello-text'); + + const names = await E(host).list(); + /** @type {string[]} */ + const specialNames = await E(host).listSpecial(); + const allNames = await E(host).listAll(); + + t.deepEqual(['hello-text'], names); + t.assert(specialNames.length > 0); + t.assert(specialNames.every(name => name.toUpperCase() === name)); + t.deepEqual([...specialNames, ...names], allNames); +}); From df056e5a11f662d6004862ff46e1ed35fe3c4ab9 Mon Sep 17 00:00:00 2001 From: kumavis Date: Tue, 13 Feb 2024 13:57:09 -1000 Subject: [PATCH 215/234] feat(cli): List named directory --- packages/cli/src/commands/list.js | 9 ++++++--- packages/cli/src/endo.js | 8 +++----- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/packages/cli/src/commands/list.js b/packages/cli/src/commands/list.js index d190a3caf3..e0ffcf84ab 100644 --- a/packages/cli/src/commands/list.js +++ b/packages/cli/src/commands/list.js @@ -1,10 +1,13 @@ /* global process */ import os from 'os'; import { E } from '@endo/far'; -import { withEndoParty } from '../context.js'; +import { withEndoHost } from '../context.js'; -export const list = async ({ partyNames }) => - withEndoParty(partyNames, { os, process }, async ({ party }) => { +export const list = async ({ directoryName }) => + withEndoHost({ os, process }, async ({ party }) => { + if (directoryName !== undefined) { + party = E(party).lookup(directoryName); + } const petNames = await E(party).list(); for await (const petName of petNames) { console.log(petName); diff --git a/packages/cli/src/endo.js b/packages/cli/src/endo.js index fd0f898c1d..14008acb5b 100644 --- a/packages/cli/src/endo.js +++ b/packages/cli/src/endo.js @@ -254,13 +254,11 @@ export const main = async rawArgs => { }); program - .command('list') - .option(...commonOptions.as) + .command('list [directory]') .description('show names') - .action(async cmd => { - const { as: partyNames } = cmd.opts(); + .action(async directoryName => { const { list } = await import('./commands/list.js'); - return list({ partyNames }); + return list({ directoryName }); }); program From e0de1ff28995969c9f95414b29fe253741c26940 Mon Sep 17 00:00:00 2001 From: kumavis Date: Tue, 13 Feb 2024 14:00:06 -1000 Subject: [PATCH 216/234] feat(cli): List all, list special --- packages/cli/src/commands/list.js | 10 +++++++--- packages/cli/src/endo.js | 9 ++++++--- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/packages/cli/src/commands/list.js b/packages/cli/src/commands/list.js index e0ffcf84ab..548b005ce3 100644 --- a/packages/cli/src/commands/list.js +++ b/packages/cli/src/commands/list.js @@ -3,12 +3,16 @@ import os from 'os'; import { E } from '@endo/far'; import { withEndoHost } from '../context.js'; -export const list = async ({ directoryName }) => - withEndoHost({ os, process }, async ({ party }) => { +export const list = async ({ directoryName, special, all }) => + withEndoHost({ os, process }, async ({ host: party }) => { if (directoryName !== undefined) { party = E(party).lookup(directoryName); } - const petNames = await E(party).list(); + const petNames = await (() => { + if (all) return E(party).listAll(); + if (special) return E(party).listSpecial(); + return E(party).list(); + })(); for await (const petName of petNames) { console.log(petName); } diff --git a/packages/cli/src/endo.js b/packages/cli/src/endo.js index 14008acb5b..389ac66059 100644 --- a/packages/cli/src/endo.js +++ b/packages/cli/src/endo.js @@ -255,10 +255,13 @@ export const main = async rawArgs => { program .command('list [directory]') - .description('show names') - .action(async directoryName => { + .description('show names known to the current or specified directory') + .option('-s,--special', 'show special names') + .option('--all', 'show all names') + .action(async (directoryName, cmd) => { + const { special, all } = cmd.opts(); const { list } = await import('./commands/list.js'); - return list({ directoryName }); + return list({ directoryName, special, all }); }); program From a0f141f20e059e9988d9117c066f23f1bcbff559 Mon Sep 17 00:00:00 2001 From: kumavis Date: Tue, 13 Feb 2024 15:12:11 -1000 Subject: [PATCH 217/234] refactor(daemon)!: Change unsafe import formula from path to specifier --- packages/cli/src/commands/make.js | 3 ++- packages/daemon/src/daemon-node-powers.js | 4 +-- packages/daemon/src/daemon.js | 8 +++--- packages/daemon/src/host.js | 11 +++----- packages/daemon/src/types.d.ts | 9 +++---- packages/daemon/src/worker.js | 32 ++++++++++++++++------- packages/daemon/test/test-endo.js | 12 ++++++--- 7 files changed, 45 insertions(+), 34 deletions(-) diff --git a/packages/cli/src/commands/make.js b/packages/cli/src/commands/make.js index bfabc0bb11..1ef9f4a2b3 100644 --- a/packages/cli/src/commands/make.js +++ b/packages/cli/src/commands/make.js @@ -2,6 +2,7 @@ import os from 'os'; import path from 'path'; +import url from 'url'; import bundleSource from '@endo/bundle-source'; import { makeReaderRef } from '@endo/daemon'; @@ -65,7 +66,7 @@ export const makeCommand = async ({ importPath !== undefined ? E(party).makeUnconfined( workerName, - path.resolve(importPath), + url.pathToFileURL(path.resolve(importPath)).href, powersName, resultName, ) diff --git a/packages/daemon/src/daemon-node-powers.js b/packages/daemon/src/daemon-node-powers.js index 9b6bdef1b6..93760e4a71 100644 --- a/packages/daemon/src/daemon-node-powers.js +++ b/packages/daemon/src/daemon-node-powers.js @@ -615,9 +615,7 @@ export const makeDaemonicPersistencePowers = ( type: /** @type {'make-unconfined'} */ ('make-unconfined'), worker: `worker-id512:${zero512}`, powers: `host-id512:${zero512}`, - importPath: fileURLToPath( - new URL('web-page-bundler.js', import.meta.url).href, - ), + specifier: new URL('web-page-bundler.js', import.meta.url).href, } : undefined; diff --git a/packages/daemon/src/daemon.js b/packages/daemon/src/daemon.js index b45b17ee79..de3dd0361c 100644 --- a/packages/daemon/src/daemon.js +++ b/packages/daemon/src/daemon.js @@ -251,13 +251,13 @@ const makeEndoBootstrap = ( /** * @param {string} workerFormulaIdentifier * @param {string} guestFormulaIdentifier - * @param {string} importPath + * @param {string} specifier * @param {import('./types.js').Terminator} terminator */ const makeControllerForUnconfinedPlugin = async ( workerFormulaIdentifier, guestFormulaIdentifier, - importPath, + specifier, terminator, ) => { terminator.thisDiesIfThatDies(workerFormulaIdentifier); @@ -279,7 +279,7 @@ const makeEndoBootstrap = ( // eslint-disable-next-line no-use-before-define provideValueForFormulaIdentifier(guestFormulaIdentifier) ); - const external = E(workerDaemonFacet).makeUnconfined(importPath, guestP); + const external = E(workerDaemonFacet).makeUnconfined(specifier, guestP); return { external, internal: undefined }; }; @@ -352,7 +352,7 @@ const makeEndoBootstrap = ( return makeControllerForUnconfinedPlugin( formula.worker, formula.powers, - formula.importPath, + formula.specifier, terminator, ); } else if (formula.type === 'make-bundle') { diff --git a/packages/daemon/src/host.js b/packages/daemon/src/host.js index 3d3dae0d5c..d49ebad59f 100644 --- a/packages/daemon/src/host.js +++ b/packages/daemon/src/host.js @@ -296,15 +296,10 @@ export const makeHostMaker = ({ return value; }; - /** - * @param {string | 'NEW' | 'MAIN'} workerName - * @param {string} importPath - * @param {string | 'NONE' | 'SELF' | 'ENDO'} powersName - * @param {string} resultName - */ + /** @type {import('./types.js').EndoHost['makeUnconfined']} */ const makeUnconfined = async ( workerName, - importPath, + specifier, powersName, resultName, ) => { @@ -321,7 +316,7 @@ export const makeHostMaker = ({ type: 'make-unconfined', worker: workerFormulaIdentifier, powers: powersFormulaIdentifier, - importPath, + specifier, }; // Behold, recursion: diff --git a/packages/daemon/src/types.d.ts b/packages/daemon/src/types.d.ts index 6c3b3ba83b..42439a2b99 100644 --- a/packages/daemon/src/types.d.ts +++ b/packages/daemon/src/types.d.ts @@ -46,7 +46,6 @@ export type HttpConnect = ( ) => void; export type MignonicPowers = { - pathToFileURL: (path: string) => string; connection: { reader: Reader; writer: Writer; @@ -91,7 +90,7 @@ type MakeUnconfinedFormula = { type: 'make-unconfined'; worker: string; powers: string; - importPath: string; + specifier: string; // TODO formula slots }; @@ -260,9 +259,9 @@ export interface EndoHost { resultName?: string, ); makeUnconfined( - workerPetName: string | undefined, - importPath: string, - powersName: string, + workerName: string | 'NEW' | 'MAIN', + specifier: string, + powersName: string | 'NONE' | 'SELF' | 'ENDO', resultName?: string, ): Promise; makeBundle( diff --git a/packages/daemon/src/worker.js b/packages/daemon/src/worker.js index 2f54c6822a..e2eec34095 100644 --- a/packages/daemon/src/worker.js +++ b/packages/daemon/src/worker.js @@ -14,6 +14,21 @@ const endowments = harden({ URL, }); +const normalizeFilePath = path => { + // Check if the path is already a file URL. + if (path.startsWith('file://')) { + return path; + } + // Windows path detection and conversion (look for a drive letter at the start). + const isWindowsPath = /^[a-zA-Z]:/.test(path); + if (isWindowsPath) { + // Correctly format the Windows path with three slashes. + return `file:///${path}`; + } + // For non-Windows paths, prepend the file protocol. + return `file://${path}`; +}; + /** * @typedef {ReturnType} WorkerBootstrap */ @@ -21,9 +36,8 @@ const endowments = harden({ /** * @param {object} args * @param {(error: Error) => void} args.cancel - * @param {(path: string) => string} args.pathToFileURL */ -export const makeWorkerFacet = ({ pathToFileURL, cancel }) => { +export const makeWorkerFacet = ({ cancel }) => { return Far('EndoWorkerFacet', { terminate: async () => { console.error('Endo worker received terminate request'); @@ -50,12 +64,15 @@ export const makeWorkerFacet = ({ pathToFileURL, cancel }) => { }, /** - * @param {string} path + * @param {string} specifier * @param {unknown} powersP */ - makeUnconfined: async (path, powersP) => { - const url = pathToFileURL(path); - const namespace = await import(url); + makeUnconfined: async (specifier, powersP) => { + // Windows absolute path includes drive letter which is confused for + // protocol specifier. So, we reformat the specifier to include the + // file protocol. + const specifierUrl = normalizeFilePath(specifier); + const namespace = await import(specifierUrl); return namespace.make(powersP); }, @@ -92,12 +109,9 @@ export const main = async (powers, locator, uuid, pid, cancel, cancelled) => { console.error(`Endo worker exiting on pid ${pid}`); }); - const { pathToFileURL } = powers; - const { reader, writer } = powers.connection; const workerFacet = makeWorkerFacet({ - pathToFileURL, cancel, }); diff --git a/packages/daemon/test/test-endo.js b/packages/daemon/test/test-endo.js index 70f086f760..0d7fd9eeac 100644 --- a/packages/daemon/test/test-endo.js +++ b/packages/daemon/test/test-endo.js @@ -389,7 +389,8 @@ test('persist unconfined services and their requests', async t => { await E(host).makeWorker('w1'); await E(host).provideGuest('o1'); const servicePath = path.join(dirname, 'test', 'service.js'); - await E(host).makeUnconfined('w1', servicePath, 'o1', 's1'); + const serviceLocation = url.pathToFileURL(servicePath).href; + await E(host).makeUnconfined('w1', serviceLocation, 'o1', 's1'); await E(host).makeWorker('w2'); const answer = await E(host).evaluate( @@ -579,7 +580,8 @@ test('direct termination', async t => { await E(host).provideWorker('worker'); const counterPath = path.join(dirname, 'test', 'counter.js'); - await E(host).makeUnconfined('worker', counterPath, 'NONE', 'counter'); + const counterLocation = url.pathToFileURL(counterPath).href; + await E(host).makeUnconfined('worker', counterLocation, 'NONE', 'counter'); t.is( 1, await E(host).evaluate( @@ -659,7 +661,8 @@ test('indirect termination', async t => { await E(host).provideWorker('worker'); const counterPath = path.join(dirname, 'test', 'counter.js'); - await E(host).makeUnconfined('worker', counterPath, 'SELF', 'counter'); + const counterLocation = url.pathToFileURL(counterPath).href; + await E(host).makeUnconfined('worker', counterLocation, 'SELF', 'counter'); t.is( 1, await E(host).evaluate( @@ -741,7 +744,8 @@ test('terminate because of requested capability', async t => { const messages = E(host).followMessages(); const counterPath = path.join(dirname, 'test', 'counter-party.js'); - E(host).makeUnconfined('worker', counterPath, 'guest', 'counter'); + const counterLocation = url.pathToFileURL(counterPath).href; + E(host).makeUnconfined('worker', counterLocation, 'guest', 'counter'); await E(host).evaluate('worker', '0', [], [], 'zero'); await E(messages).next(); From 46800337cb57fdfe3a0255ba6eb784f9e2e5ca66 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Thu, 28 Dec 2023 17:58:28 -0800 Subject: [PATCH 218/234] refactor(daemon): Make all formula numbers unique Ensures that all formula numbers unique by either deriving them from a root "seed" value or randomly generating them. The seed value exists until the next `purge`. We in some cases rely on formula number derivation paths to reference other values. For example, the pet store number is derived from the inspector number, so that the inspector factory function can easily reference the pet store. Co-authored by: Erik Marks <25517051+rekmarks@users.noreply.github.com> --- packages/daemon/src/common.js | 2 - packages/daemon/src/daemon-node-powers.js | 24 +++-- packages/daemon/src/daemon.js | 102 +++++++++++++--------- packages/daemon/src/types.d.ts | 6 +- 4 files changed, 85 insertions(+), 49 deletions(-) delete mode 100644 packages/daemon/src/common.js diff --git a/packages/daemon/src/common.js b/packages/daemon/src/common.js deleted file mode 100644 index 1a396e5b14..0000000000 --- a/packages/daemon/src/common.js +++ /dev/null @@ -1,2 +0,0 @@ -export const zero512 = - '00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'; diff --git a/packages/daemon/src/daemon-node-powers.js b/packages/daemon/src/daemon-node-powers.js index 93760e4a71..ecaf4394ec 100644 --- a/packages/daemon/src/daemon-node-powers.js +++ b/packages/daemon/src/daemon-node-powers.js @@ -9,7 +9,6 @@ import { makeReaderRef } from './reader-ref.js'; import { makePetStoreMaker } from './pet-store.js'; import { servePrivatePortHttp } from './serve-private-port-http.js'; import { servePrivatePath } from './serve-private-path.js'; -import { zero512 } from './common.js'; const { quote: q } = assert; @@ -495,6 +494,16 @@ export const makeDaemonicPersistencePowers = ( await Promise.all([statePathP, cachePathP, ephemeralStatePathP]); }; + const provideRootNonce = async () => { + const noncePath = filePowers.joinPath(locator.statePath, 'nonce'); + let nonce = await filePowers.maybeReadFileText(noncePath); + if (nonce === undefined) { + nonce = await cryptoPowers.randomHex512(); + await filePowers.writeFileText(noncePath, `${nonce}\n`); + } + return nonce.trim(); + }; + const makeContentSha512Store = () => { const { statePath } = locator; const storageDirectoryPath = filePowers.joinPath(statePath, 'store-sha512'); @@ -610,21 +619,22 @@ export const makeDaemonicPersistencePowers = ( await filePowers.writeFileText(file, `${q(formula)}\n`); }; - const webPageBundlerFormula = includeWebPageBundler - ? { + const getWebPageBundlerFormula = includeWebPageBundler + ? (workerFormulaIdentifier, powersFormulaIdentifier) => ({ type: /** @type {'make-unconfined'} */ ('make-unconfined'), - worker: `worker-id512:${zero512}`, - powers: `host-id512:${zero512}`, + worker: workerFormulaIdentifier, + powers: powersFormulaIdentifier, specifier: new URL('web-page-bundler.js', import.meta.url).href, - } + }) : undefined; return harden({ initializePersistence, + provideRootNonce, makeContentSha512Store, readFormula, writeFormula, - webPageBundlerFormula, + getWebPageBundlerFormula, }); }; diff --git a/packages/daemon/src/daemon.js b/packages/daemon/src/daemon.js index de3dd0361c..4b8d182b77 100644 --- a/packages/daemon/src/daemon.js +++ b/packages/daemon/src/daemon.js @@ -12,7 +12,6 @@ import { makeHostMaker } from './host.js'; import { assertPetName } from './pet-name.js'; import { makeTerminatorMaker } from './terminator.js'; import { parseFormulaIdentifier } from './formula-identifier.js'; -import { zero512 } from './common.js'; const { quote: q } = assert; @@ -63,7 +62,7 @@ const makeInspector = (type, number, record) => * @param {number} args.gracePeriodMs * @param {Promise} args.gracePeriodElapsed */ -const makeEndoBootstrap = ( +const makeEndoBootstrap = async ( powers, webletPortP, { cancelled, cancel, gracePeriodMs, gracePeriodElapsed }, @@ -75,7 +74,24 @@ const makeEndoBootstrap = ( control: controlPowers, } = powers; const { randomHex512, makeSha512 } = cryptoPowers; + const derive = (...path) => { + const digester = makeSha512(); + digester.updateText(path.join(':')); + return digester.digestHex(); + }; + const contentStore = persistencePowers.makeContentSha512Store(); + const rootNonce = await persistencePowers.provideRootNonce(); + + const endoFormulaIdentifier = `endo-id512:${rootNonce}`; + const defaultHostFormulaNumber = derive(rootNonce, 'host'); + const defaultHostFormulaIdentifier = `host-id512:${defaultHostFormulaNumber}`; + const webPageJsFormulaIdentifier = `web-page-js-id512:${derive( + rootNonce, + 'web-page-js', + )}`; + const leastAuthorityFormulaNumber = derive(rootNonce, 'least-authority'); + const leastAuthorityFormulaIdentifier = `least-authority-id512:${leastAuthorityFormulaNumber}`; /** @type {Map>} */ const controllerForFormulaIdentifier = new Map(); @@ -407,32 +423,7 @@ const makeEndoBootstrap = ( ) => { const { type: formulaType, number: formulaNumber } = parseFormulaIdentifier(formulaIdentifier); - if (formulaType === 'endo-id512') { - if (formulaNumber !== zero512) { - throw Error('Invalid endo-id512 formula number.'); - } - - // TODO reframe "cancelled" as termination of the "endo" object and - // ensure that all values ultimately depend on "endo". - // Behold, self-referentiality: - // eslint-disable-next-line no-use-before-define - return { external: endoBootstrap, internal: undefined }; - } else if (formulaType === 'least-authority-id512') { - return { external: leastAuthority, internal: undefined }; - } else if (formulaType === 'web-page-js-id512') { - if (persistencePowers.webPageBundlerFormula === undefined) { - throw Error('No web-page-js-id512 formula provided.'); - } - if (formulaNumber !== zero512) { - throw Error('Invalid web-page-js-id512 formula number.'); - } - return makeControllerForFormula( - formulaIdentifier, - zero512, - persistencePowers.webPageBundlerFormula, - terminator, - ); - } else if (formulaType === 'readable-blob-sha512') { + if (formulaType === 'readable-blob-sha512') { // Behold, forward-reference: // eslint-disable-next-line no-use-before-define const external = makeReadableBlob(formulaNumber); @@ -440,11 +431,11 @@ const makeEndoBootstrap = ( } else if (formulaType === 'worker-id512') { return makeIdentifiedWorkerController(formulaNumber, terminator); } else if (formulaType === 'pet-inspector-id512') { + const storeFormulaNumber = derive(formulaNumber, 'pet-store'); + const storeFormulaIdentifier = `pet-store-id512:${storeFormulaNumber}`; // Behold, unavoidable forward-reference: // eslint-disable-next-line no-use-before-define - const external = makePetStoreInspector( - `pet-store-id512:${formulaNumber}`, - ); + const external = makePetStoreInspector(storeFormulaIdentifier); return { external, internal: undefined }; } else if (formulaType === 'pet-store-id512') { const external = petStorePowers.makeIdentifiedPetStore( @@ -453,11 +444,14 @@ const makeEndoBootstrap = ( ); return { external, internal: undefined }; } else if (formulaType === 'host-id512') { - const endoFormulaIdentifier = `endo-id512:${zero512}`; - const storeFormulaIdentifier = `pet-store-id512:${formulaNumber}`; - const inspectorFormulaIdentifier = `pet-inspector-id512:${formulaNumber}`; - const workerFormulaIdentifier = `worker-id512:${formulaNumber}`; - const leastAuthorityFormulaIdentifier = `least-authority-id512:${zero512}`; + const workerFormulaNumber = derive(formulaNumber, 'worker'); + const workerFormulaIdentifier = `worker-id512:${workerFormulaNumber}`; + const inspectorFormulaNumber = derive(formulaNumber, 'pet-inspector'); + const inspectorFormulaIdentifier = `pet-inspector-id512:${inspectorFormulaNumber}`; + // Note the pet store formula number derivation path: + // root -> host -> inspector -> pet store + const storeFormulaNumber = derive(inspectorFormulaNumber, 'pet-store'); + const storeFormulaIdentifier = `pet-store-id512:${storeFormulaNumber}`; // Behold, recursion: // eslint-disable-next-line no-use-before-define @@ -470,6 +464,36 @@ const makeEndoBootstrap = ( leastAuthorityFormulaIdentifier, terminator, ); + } else if (formulaType === 'endo-id512') { + if (formulaNumber !== rootNonce) { + throw new Error('Invalid endo-id512 formula number.'); + } + + // TODO reframe "cancelled" as termination of the "endo" object and + // ensure that all values ultimately depend on "endo". + // Behold, self-referentiality: + // eslint-disable-next-line no-use-before-define + return { external: endoBootstrap, internal: undefined }; + } else if (formulaType === 'least-authority-id512') { + return { external: leastAuthority, internal: undefined }; + } else if (formulaType === 'web-page-js-id512') { + if (persistencePowers.getWebPageBundlerFormula === undefined) { + throw Error('No web-page-js formula provided.'); + } + // Note that this worker is hardcoded to be the "MAIN" worker of the + // default host. + const workerFormulaNumber = derive(defaultHostFormulaNumber, 'worker'); + const workerFormulaIdentifier = `worker-id512:${workerFormulaNumber}`; + + return makeControllerForFormula( + 'web-page-js', + derive(formulaNumber, 'web-page-js'), + persistencePowers.getWebPageBundlerFormula( + workerFormulaIdentifier, + defaultHostFormulaIdentifier, + ), + terminator, + ); } else if ( [ 'eval-id512', @@ -581,7 +605,7 @@ const makeEndoBootstrap = ( // Behold, recursion: // eslint-disable-next-line no-use-before-define const terminator = makeTerminator(formulaIdentifier); - partial.catch(error => terminator.terminate()); + partial.catch(() => terminator.terminate()); controller = harden({ terminator, external: E.get(partial).external, @@ -767,12 +791,12 @@ const makeEndoBootstrap = ( cancel(new Error('Termination requested')); }, - host: () => provideValueForFormulaIdentifier(`host-id512:${zero512}`), + host: () => provideValueForFormulaIdentifier(defaultHostFormulaIdentifier), leastAuthority: () => leastAuthority, webPageJs: () => - provideValueForFormulaIdentifier(`web-page-js-id512:${zero512}`), + provideValueForFormulaIdentifier(webPageJsFormulaIdentifier), importAndEndowInWebPage: async (webPageP, webPageNumber) => { const { bundle: bundleBlob, powers: endowedPowers } = diff --git a/packages/daemon/src/types.d.ts b/packages/daemon/src/types.d.ts index 42439a2b99..7c2ee55f82 100644 --- a/packages/daemon/src/types.d.ts +++ b/packages/daemon/src/types.d.ts @@ -365,6 +365,7 @@ export type NetworkPowers = SocketPowers & { export type DaemonicPersistencePowers = { initializePersistence: () => Promise; + provideRootNonce: () => Promise; makeContentSha512Store: () => { store: (readable: AsyncIterable) => Promise; fetch: (sha512: string) => EndoReadable; @@ -375,7 +376,10 @@ export type DaemonicPersistencePowers = { formulaType: string, formulaId512: string, ) => Promise; - webPageBundlerFormula?: Formula; + getWebPageBundlerFormula?: ( + workerFormulaIdentifier: string, + powersFormulaIdentifier: string, + ) => Formula; }; export interface DaemonWorkerFacet {} From 34a2a24fccc70950002a2a7a8aee5aeb27c2158a Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Thu, 28 Dec 2023 13:43:46 -0800 Subject: [PATCH 219/234] refactor(daemon): Pivot terminate to cancel --- packages/daemon/src/daemon.js | 104 ++++++++++++++---------------- packages/daemon/src/guest.js | 12 ++-- packages/daemon/src/host.js | 14 ++-- packages/daemon/src/mail.js | 14 ++-- packages/daemon/src/terminator.js | 51 ++++++++------- packages/daemon/src/types.d.ts | 17 +++-- packages/daemon/test/test-endo.js | 10 +-- 7 files changed, 113 insertions(+), 109 deletions(-) diff --git a/packages/daemon/src/daemon.js b/packages/daemon/src/daemon.js index 4b8d182b77..bf78de7ba8 100644 --- a/packages/daemon/src/daemon.js +++ b/packages/daemon/src/daemon.js @@ -10,7 +10,7 @@ import { makeMailboxMaker } from './mail.js'; import { makeGuestMaker } from './guest.js'; import { makeHostMaker } from './host.js'; import { assertPetName } from './pet-name.js'; -import { makeTerminatorMaker } from './terminator.js'; +import { makeContextMaker } from './terminator.js'; import { parseFormulaIdentifier } from './formula-identifier.js'; const { quote: q } = assert; @@ -131,26 +131,25 @@ const makeEndoBootstrap = async ( /** * @param {string} workerId512 - * @param {import('./types.js').Terminator} terminator + * @param {import('./types.js').Context} context */ - const makeIdentifiedWorkerController = async (workerId512, terminator) => { + const makeIdentifiedWorkerController = async (workerId512, context) => { // TODO validate workerId512 const daemonWorkerFacet = makeWorkerBootstrap(workerId512); - const { reject: cancelWorker, promise: workerCancelled } = + const { promise: hardCancelled, reject: hardCancel } = /** @type {import('@endo/promise-kit').PromiseKit} */ ( makePromiseKit() ); - cancelled.catch(async error => cancelWorker(error)); const { workerTerminated, workerDaemonFacet } = await controlPowers.makeWorker( workerId512, daemonWorkerFacet, - Promise.race([workerCancelled, gracePeriodElapsed]), + Promise.race([hardCancelled, gracePeriodElapsed]), ); - const terminate = async () => { + const softCancel = async () => { E.sendOnly(workerDaemonFacet).terminate(); const cancelWorkerGracePeriod = () => { throw new Error('Exited gracefully before grace period elapsed'); @@ -165,16 +164,13 @@ const makeEndoBootstrap = async ( `Worker termination grace period ${gracePeriodMs}ms elapsed`, ); }) - .catch(cancelWorker); + .catch(hardCancel); await workerTerminated; }; - terminator.onTerminate(terminate); + context.onCancel(softCancel); - const worker = Far('EndoWorker', { - terminate: terminator.terminate, - whenTerminated: () => terminator.terminated, - }); + const worker = Far('EndoWorker', {}); return { external: worker, @@ -187,18 +183,18 @@ const makeEndoBootstrap = async ( * @param {string} source * @param {Array} codeNames * @param {Array} formulaIdentifiers - * @param {import('./types.js').Terminator} terminator + * @param {import('./types.js').Context} context */ const makeControllerForEval = async ( workerFormulaIdentifier, source, codeNames, formulaIdentifiers, - terminator, + context, ) => { - terminator.thisDiesIfThatDies(workerFormulaIdentifier); + context.thisDiesIfThatDies(workerFormulaIdentifier); for (const formulaIdentifier of formulaIdentifiers) { - terminator.thisDiesIfThatDies(formulaIdentifier); + context.thisDiesIfThatDies(formulaIdentifier); } const workerController = @@ -225,9 +221,7 @@ const makeEndoBootstrap = async ( source, codeNames, endowmentValues, - terminator.terminated.then(() => { - throw new Error('Terminated'); - }), + context.cancelled, ); // TODO check whether the promise resolves to data that can be marshalled @@ -247,14 +241,14 @@ const makeEndoBootstrap = async ( * * @param {string} hubFormulaIdentifier * @param {string[]} path - * @param {import('./types.js').Terminator} terminator + * @param {import('./types.js').Context} context */ const makeControllerForLookup = async ( hubFormulaIdentifier, path, - terminator, + context, ) => { - terminator.thisDiesIfThatDies(hubFormulaIdentifier); + context.thisDiesIfThatDies(hubFormulaIdentifier); // Behold, recursion: // eslint-disable-next-line no-use-before-define @@ -268,16 +262,16 @@ const makeEndoBootstrap = async ( * @param {string} workerFormulaIdentifier * @param {string} guestFormulaIdentifier * @param {string} specifier - * @param {import('./types.js').Terminator} terminator + * @param {import('./types.js').Context} context */ const makeControllerForUnconfinedPlugin = async ( workerFormulaIdentifier, guestFormulaIdentifier, specifier, - terminator, + context, ) => { - terminator.thisDiesIfThatDies(workerFormulaIdentifier); - terminator.thisDiesIfThatDies(guestFormulaIdentifier); + context.thisDiesIfThatDies(workerFormulaIdentifier); + context.thisDiesIfThatDies(guestFormulaIdentifier); const workerController = /** @type {import('./types.js').Controller} */ ( @@ -303,16 +297,16 @@ const makeEndoBootstrap = async ( * @param {string} workerFormulaIdentifier * @param {string} guestFormulaIdentifier * @param {string} bundleFormulaIdentifier - * @param {import('./types.js').Terminator} terminator + * @param {import('./types.js').Context} context */ const makeControllerForSafeBundle = async ( workerFormulaIdentifier, guestFormulaIdentifier, bundleFormulaIdentifier, - terminator, + context, ) => { - terminator.thisDiesIfThatDies(workerFormulaIdentifier); - terminator.thisDiesIfThatDies(guestFormulaIdentifier); + context.thisDiesIfThatDies(workerFormulaIdentifier); + context.thisDiesIfThatDies(guestFormulaIdentifier); const workerController = /** @type {import('./types.js').Controller} */ ( @@ -346,13 +340,13 @@ const makeEndoBootstrap = async ( * @param {string} formulaIdentifier * @param {string} formulaNumber * @param {import('./types.js').Formula} formula - * @param {import('./types.js').Terminator} terminator + * @param {import('./types.js').Context} context */ const makeControllerForFormula = async ( formulaIdentifier, formulaNumber, formula, - terminator, + context, ) => { if (formula.type === 'eval') { return makeControllerForEval( @@ -360,23 +354,23 @@ const makeEndoBootstrap = async ( formula.source, formula.names, formula.values, - terminator, + context, ); } else if (formula.type === 'lookup') { - return makeControllerForLookup(formula.hub, formula.path, terminator); + return makeControllerForLookup(formula.hub, formula.path, context); } else if (formula.type === 'make-unconfined') { return makeControllerForUnconfinedPlugin( formula.worker, formula.powers, formula.specifier, - terminator, + context, ); } else if (formula.type === 'make-bundle') { return makeControllerForSafeBundle( formula.worker, formula.powers, formula.bundle, - terminator, + context, ); } else if (formula.type === 'guest') { const storeFormulaIdentifier = `pet-store-id512:${formulaNumber}`; @@ -388,13 +382,13 @@ const makeEndoBootstrap = async ( formula.host, storeFormulaIdentifier, workerFormulaIdentifier, - terminator, + context, ); } else if (formula.type === 'web-bundle') { // Behold, forward-reference: // eslint-disable-next-line no-use-before-define - terminator.thisDiesIfThatDies(formula.bundle); - terminator.thisDiesIfThatDies(formula.powers); + context.thisDiesIfThatDies(formula.bundle); + context.thisDiesIfThatDies(formula.powers); return { external: (async () => harden({ @@ -415,11 +409,11 @@ const makeEndoBootstrap = async ( /** * @param {string} formulaIdentifier - * @param {import('./types.js').Terminator} terminator + * @param {import('./types.js').Context} context */ const makeControllerForFormulaIdentifier = async ( formulaIdentifier, - terminator, + context, ) => { const { type: formulaType, number: formulaNumber } = parseFormulaIdentifier(formulaIdentifier); @@ -429,7 +423,7 @@ const makeEndoBootstrap = async ( const external = makeReadableBlob(formulaNumber); return { external, internal: undefined }; } else if (formulaType === 'worker-id512') { - return makeIdentifiedWorkerController(formulaNumber, terminator); + return makeIdentifiedWorkerController(formulaNumber, context); } else if (formulaType === 'pet-inspector-id512') { const storeFormulaNumber = derive(formulaNumber, 'pet-store'); const storeFormulaIdentifier = `pet-store-id512:${storeFormulaNumber}`; @@ -462,7 +456,7 @@ const makeEndoBootstrap = async ( inspectorFormulaIdentifier, workerFormulaIdentifier, leastAuthorityFormulaIdentifier, - terminator, + context, ); } else if (formulaType === 'endo-id512') { if (formulaNumber !== rootNonce) { @@ -492,7 +486,7 @@ const makeEndoBootstrap = async ( workerFormulaIdentifier, defaultHostFormulaIdentifier, ), - terminator, + context, ); } else if ( [ @@ -512,7 +506,7 @@ const makeEndoBootstrap = async ( formulaIdentifier, formulaNumber, formula, - terminator, + context, ); } else { throw new TypeError( @@ -548,10 +542,10 @@ const makeEndoBootstrap = async ( // Behold, recursion: // eslint-disable-next-line no-use-before-define - const terminator = makeTerminator(formulaIdentifier); - partial.catch(() => terminator.terminate()); + const context = makeContext(formulaIdentifier); + partial.catch(context.cancel); const controller = harden({ - terminator, + context, external: E.get(partial).external.then(value => { if (typeof value === 'object' && value !== null) { formulaIdentifierForRef.set(value, formulaIdentifier); @@ -568,7 +562,7 @@ const makeEndoBootstrap = async ( formulaIdentifier, formulaNumber, formula, - terminator, + context, ), ); @@ -604,15 +598,15 @@ const makeEndoBootstrap = async ( // Behold, recursion: // eslint-disable-next-line no-use-before-define - const terminator = makeTerminator(formulaIdentifier); - partial.catch(() => terminator.terminate()); + const context = makeContext(formulaIdentifier); + partial.catch(context.cancel); controller = harden({ - terminator, + context, external: E.get(partial).external, internal: E.get(partial).internal, }); controllerForFormulaIdentifier.set(formulaIdentifier, controller); - resolve(makeControllerForFormulaIdentifier(formulaIdentifier, terminator)); + resolve(makeControllerForFormulaIdentifier(formulaIdentifier, context)); return controller; }; @@ -631,7 +625,7 @@ const makeEndoBootstrap = async ( return value; }; - const makeTerminator = makeTerminatorMaker({ + const makeContext = makeContextMaker({ controllerForFormulaIdentifier, provideControllerForFormulaIdentifier, }); diff --git a/packages/daemon/src/guest.js b/packages/daemon/src/guest.js index 1aa66389db..9a3b203146 100644 --- a/packages/daemon/src/guest.js +++ b/packages/daemon/src/guest.js @@ -12,18 +12,18 @@ export const makeGuestMaker = ({ * @param {string} hostFormulaIdentifier * @param {string} petStoreFormulaIdentifier * @param {string} mainWorkerFormulaIdentifier - * @param {import('./types.js').Terminator} terminator + * @param {import('./types.js').Context} context */ const makeIdentifiedGuestController = async ( guestFormulaIdentifier, hostFormulaIdentifier, petStoreFormulaIdentifier, mainWorkerFormulaIdentifier, - terminator, + context, ) => { - terminator.thisDiesIfThatDies(hostFormulaIdentifier); - terminator.thisDiesIfThatDies(petStoreFormulaIdentifier); - terminator.thisDiesIfThatDies(mainWorkerFormulaIdentifier); + context.thisDiesIfThatDies(hostFormulaIdentifier); + context.thisDiesIfThatDies(petStoreFormulaIdentifier); + context.thisDiesIfThatDies(mainWorkerFormulaIdentifier); const petStore = /** @type {import('./types.js').PetStore} */ ( await provideValueForFormulaIdentifier(petStoreFormulaIdentifier) @@ -69,7 +69,7 @@ export const makeGuestMaker = ({ SELF: guestFormulaIdentifier, HOST: hostFormulaIdentifier, }, - terminator, + context, }); const { has, follow: followNames, listEntries, followEntries } = petStore; diff --git a/packages/daemon/src/host.js b/packages/daemon/src/host.js index d49ebad59f..84ba729e1c 100644 --- a/packages/daemon/src/host.js +++ b/packages/daemon/src/host.js @@ -22,7 +22,7 @@ export const makeHostMaker = ({ * @param {string} inspectorFormulaIdentifier * @param {string} mainWorkerFormulaIdentifier * @param {string} leastAuthorityFormulaIdentifier - * @param {import('./types.js').Terminator} terminator + * @param {import('./types.js').Context} context */ const makeIdentifiedHost = async ( hostFormulaIdentifier, @@ -31,10 +31,10 @@ export const makeHostMaker = ({ inspectorFormulaIdentifier, mainWorkerFormulaIdentifier, leastAuthorityFormulaIdentifier, - terminator, + context, ) => { - terminator.thisDiesIfThatDies(storeFormulaIdentifier); - terminator.thisDiesIfThatDies(mainWorkerFormulaIdentifier); + context.thisDiesIfThatDies(storeFormulaIdentifier); + context.thisDiesIfThatDies(mainWorkerFormulaIdentifier); const petStore = /** @type {import('./types.js').PetStore} */ ( // Behold, recursion: @@ -62,7 +62,7 @@ export const makeHostMaker = ({ listSpecial, rename, remove, - terminate, + cancel, } = makeMailbox({ petStore, selfFormulaIdentifier: hostFormulaIdentifier, @@ -72,7 +72,7 @@ export const makeHostMaker = ({ INFO: inspectorFormulaIdentifier, NONE: leastAuthorityFormulaIdentifier, }, - terminator, + context, }); /** @@ -513,7 +513,7 @@ export const makeHostMaker = ({ makeWorker, provideWorker, evaluate, - terminate, + cancel, makeUnconfined, makeBundle, provideWebPage, diff --git a/packages/daemon/src/mail.js b/packages/daemon/src/mail.js index ec0d9bdec8..14bffb1daf 100644 --- a/packages/daemon/src/mail.js +++ b/packages/daemon/src/mail.js @@ -19,7 +19,7 @@ export const makeMailboxMaker = ({ selfFormulaIdentifier, petStore, specialNames, - terminator, + context, }) => { /** @type {Map>} */ const responses = new Map(); @@ -61,7 +61,7 @@ export const makeMailboxMaker = ({ ); }; - const terminate = async petName => { + const cancel = async (petName, reason = 'Cancelled') => { const formulaIdentifier = identifyLocal(petName); if (formulaIdentifier === undefined) { throw new TypeError(`Unknown pet name: ${q(petName)}`); @@ -71,8 +71,8 @@ export const makeMailboxMaker = ({ const controller = await provideControllerForFormulaIdentifier( formulaIdentifier, ); - console.log('Terminating:'); - return controller.terminator.terminate(); + console.log('Cancelled:'); + return controller.context.cancel(new Error(reason)); }; const list = () => harden(petStore.list()); @@ -291,7 +291,7 @@ export const makeMailboxMaker = ({ recipientFormulaIdentifier, ); // TODO: - // terminator.thisDiesIfThatDies(formulaIdentifier); + // context.thisDiesIfThatDies(formulaIdentifier); // Behold, recursion: // eslint-disable-next-line no-use-before-define return provideValueForFormulaIdentifier(formulaIdentifier); @@ -466,7 +466,7 @@ export const makeMailboxMaker = ({ )} at ${q(index)}`, ); } - terminator.thisDiesIfThatDies(formulaIdentifier); + context.thisDiesIfThatDies(formulaIdentifier); await petStore.write(petName, formulaIdentifier); }; @@ -576,7 +576,7 @@ export const makeMailboxMaker = ({ listAll, rename, remove, - terminate, + cancel, }); }; diff --git a/packages/daemon/src/terminator.js b/packages/daemon/src/terminator.js index db9fd11bb6..89f78be57d 100644 --- a/packages/daemon/src/terminator.js +++ b/packages/daemon/src/terminator.js @@ -2,75 +2,78 @@ import { makePromiseKit } from '@endo/promise-kit'; -export const makeTerminatorMaker = ({ +export const makeContextMaker = ({ controllerForFormulaIdentifier, provideControllerForFormulaIdentifier, }) => { /** * @param {string} formulaIdentifier */ - const makeTerminator = formulaIdentifier => { - let terminating = false; - const { promise: terminated, resolve: resolveTerminated } = + const makeContext = formulaIdentifier => { + let done = false; + const { promise: cancelled, reject: rejectCancelled } = + /** @type {import('@endo/promise-kit').PromiseKit} */ ( + makePromiseKit() + ); + const { promise: disposed, resolve: resolveDisposed } = /** @type {import('@endo/promise-kit').PromiseKit} */ ( makePromiseKit() ); - /** @type {Map} */ + /** @type {Map} */ const dependents = new Map(); /** @type {Array<() => void>} */ const hooks = []; - const terminate = (prefix = '*') => { - if (terminating) return terminated; - terminating = true; + const cancel = (reason, prefix = '*') => { + if (done) return disposed; + done = true; + rejectCancelled(reason || harden(new Error('Cancelled'))); console.log(`${prefix} ${formulaIdentifier}`); controllerForFormulaIdentifier.delete(formulaIdentifier); - for (const dependentTerminator of dependents.values()) { - dependentTerminator.terminate(` ${prefix}`); + for (const dependentContext of dependents.values()) { + dependentContext.cancel(reason, ` ${prefix}`); } dependents.clear(); - resolveTerminated(Promise.all(hooks.map(hook => hook())).then(() => {})); + resolveDisposed(Promise.all(hooks.map(hook => hook())).then(() => {})); - return terminated; + return disposed; }; const thatDiesIfThisDies = dependentFormulaIdentifier => { - assert(!terminating); + assert(!done); const dependentController = provideControllerForFormulaIdentifier( dependentFormulaIdentifier, ); - dependents.set( - dependentFormulaIdentifier, - dependentController.terminator, - ); + dependents.set(dependentFormulaIdentifier, dependentController.context); }; const thisDiesIfThatDies = dependencyIdentifier => { const dependencyController = provideControllerForFormulaIdentifier(dependencyIdentifier); - dependencyController.terminator.thatDiesIfThisDies(formulaIdentifier); + dependencyController.context.thatDiesIfThisDies(formulaIdentifier); }; /** * @param {() => void} hook */ - const onTerminate = hook => { - assert(!terminating); + const onCancel = hook => { + assert(!done); hooks.push(hook); }; return { - terminate, - terminated, + cancel, + cancelled, + disposed, thatDiesIfThisDies, thisDiesIfThatDies, - onTerminate, + onCancel, }; }; - return makeTerminator; + return makeContext; }; diff --git a/packages/daemon/src/types.d.ts b/packages/daemon/src/types.d.ts index 7c2ee55f82..3390f4749e 100644 --- a/packages/daemon/src/types.d.ts +++ b/packages/daemon/src/types.d.ts @@ -156,12 +156,19 @@ export interface Topic< subscribe(): Stream; } -export interface Terminator { - terminate: (logPrefix?: string) => Promise; - terminated: Promise; +export interface Context { + cancel: (reason?: string, logPrefix?: string) => Promise; + cancelled: Promise; + disposed: Promise; thisDiesIfThatDies: (formulaIdentifier: string) => void; thatDiesIfThisDies: (formulaIdentifier: string) => void; - onTerminate: (hook: () => void | Promise) => void; + onCancel: (hook: () => void | Promise) => void; +} + +export interface FarContext { + cancel: (reason: string) => Promise; + whenCancelled: () => Promise; + whenDisposed: () => Promise; } export interface InternalExternal { @@ -172,7 +179,7 @@ export interface InternalExternal { export interface Controller { external: Promise; internal: Promise; - terminator: Terminator; + context: Context; } export interface PetStore { diff --git a/packages/daemon/test/test-endo.js b/packages/daemon/test/test-endo.js index 0d7fd9eeac..395e77216f 100644 --- a/packages/daemon/test/test-endo.js +++ b/packages/daemon/test/test-endo.js @@ -92,7 +92,7 @@ test('lifecycle', async t => { const bootstrap = getBootstrap(); const host = E(bootstrap).host(); await E(host).makeWorker('worker'); - await E(host).terminate('worker'); + await E(host).cancel('worker'); cancel(new Error('Cancelled')); await closed.catch(() => {}); @@ -610,7 +610,7 @@ test('direct termination', async t => { ), ); - await E(host).terminate('counter'); + await E(host).cancel('counter'); t.is( 1, await E(host).evaluate( @@ -691,7 +691,7 @@ test('indirect termination', async t => { ), ); - await E(host).terminate('worker'); + await E(host).cancel('worker'); t.is( 1, @@ -722,7 +722,7 @@ test('indirect termination', async t => { ); }); -test('terminate because of requested capability', async t => { +test('cancel because of requested capability', async t => { const { promise: cancelled, reject: cancel } = makePromiseKit(); t.teardown(() => cancel(Error('teardown'))); @@ -779,7 +779,7 @@ test('terminate because of requested capability', async t => { ), ); - await E(host).terminate('guest'); + await E(host).cancel('guest'); t.is( 1, From 8ea41fb2e0a25fec7aff88f5a93f7e97b64f5a31 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Thu, 28 Dec 2023 13:46:11 -0800 Subject: [PATCH 220/234] refactor(daemon): Rename terminator to context, the file --- packages/daemon/src/{terminator.js => context.js} | 0 packages/daemon/src/daemon.js | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename packages/daemon/src/{terminator.js => context.js} (100%) diff --git a/packages/daemon/src/terminator.js b/packages/daemon/src/context.js similarity index 100% rename from packages/daemon/src/terminator.js rename to packages/daemon/src/context.js diff --git a/packages/daemon/src/daemon.js b/packages/daemon/src/daemon.js index bf78de7ba8..e09c4ac703 100644 --- a/packages/daemon/src/daemon.js +++ b/packages/daemon/src/daemon.js @@ -10,7 +10,7 @@ import { makeMailboxMaker } from './mail.js'; import { makeGuestMaker } from './guest.js'; import { makeHostMaker } from './host.js'; import { assertPetName } from './pet-name.js'; -import { makeContextMaker } from './terminator.js'; +import { makeContextMaker } from './context.js'; import { parseFormulaIdentifier } from './formula-identifier.js'; const { quote: q } = assert; From 2a5f379d06520f95d00287f3ba07eea1d7ac2f20 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Thu, 28 Dec 2023 13:46:28 -0800 Subject: [PATCH 221/234] refactor(cli): Rename kill to cancel --- packages/cli/src/commands/{kill.js => cancel.js} | 4 ++-- packages/cli/src/endo.js | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) rename packages/cli/src/commands/{kill.js => cancel.js} (63%) diff --git a/packages/cli/src/commands/kill.js b/packages/cli/src/commands/cancel.js similarity index 63% rename from packages/cli/src/commands/kill.js rename to packages/cli/src/commands/cancel.js index 1de03a9781..3c2322b391 100644 --- a/packages/cli/src/commands/kill.js +++ b/packages/cli/src/commands/cancel.js @@ -3,7 +3,7 @@ import os from 'os'; import { E } from '@endo/far'; import { withEndoParty } from '../context.js'; -export const killCommand = async ({ name, partyNames }) => +export const cancelCommand = async ({ name, partyNames, reason }) => withEndoParty(partyNames, { os, process }, async ({ party }) => { - await E(party).terminate(name); + await E(party).cancel(name, reason); }); diff --git a/packages/cli/src/endo.js b/packages/cli/src/endo.js index 389ac66059..30fe47d7e0 100644 --- a/packages/cli/src/endo.js +++ b/packages/cli/src/endo.js @@ -431,13 +431,13 @@ export const main = async rawArgs => { }); program - .command('kill ') + .command('cancel [reason]') .option(...commonOptions.as) - .description('terminate a value and its deps, recovering resources') - .action(async (name, cmd) => { + .description('cancel a value and its deps, recovering resources') + .action(async (name, reason, cmd) => { const { as: partyNames } = cmd.opts(); - const { killCommand } = await import('./commands/kill.js'); - return killCommand({ name, partyNames }); + const { cancelCommand } = await import('./commands/cancel.js'); + return cancelCommand({ name, partyNames, reason }); }); const where = program From a29cbe87323eae4eeb26dc396b9df8f3cb230ab2 Mon Sep 17 00:00:00 2001 From: Erik Marks Date: Wed, 14 Feb 2024 11:11:30 -0800 Subject: [PATCH 222/234] refactor(daemon): Rename soft/hard cancel to graceful/force cancel --- packages/daemon/src/context.js | 1 + packages/daemon/src/daemon.js | 10 +++++----- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/packages/daemon/src/context.js b/packages/daemon/src/context.js index 89f78be57d..557bb11aff 100644 --- a/packages/daemon/src/context.js +++ b/packages/daemon/src/context.js @@ -38,6 +38,7 @@ export const makeContextMaker = ({ } dependents.clear(); + // Execute all cancellation hooks and resolve a single `undefined` for them. resolveDisposed(Promise.all(hooks.map(hook => hook())).then(() => {})); return disposed; diff --git a/packages/daemon/src/daemon.js b/packages/daemon/src/daemon.js index e09c4ac703..85289968a1 100644 --- a/packages/daemon/src/daemon.js +++ b/packages/daemon/src/daemon.js @@ -137,7 +137,7 @@ const makeEndoBootstrap = async ( // TODO validate workerId512 const daemonWorkerFacet = makeWorkerBootstrap(workerId512); - const { promise: hardCancelled, reject: hardCancel } = + const { promise: forceCancelled, reject: forceCancel } = /** @type {import('@endo/promise-kit').PromiseKit} */ ( makePromiseKit() ); @@ -146,10 +146,10 @@ const makeEndoBootstrap = async ( await controlPowers.makeWorker( workerId512, daemonWorkerFacet, - Promise.race([hardCancelled, gracePeriodElapsed]), + Promise.race([forceCancelled, gracePeriodElapsed]), ); - const softCancel = async () => { + const gracefulCancel = async () => { E.sendOnly(workerDaemonFacet).terminate(); const cancelWorkerGracePeriod = () => { throw new Error('Exited gracefully before grace period elapsed'); @@ -164,11 +164,11 @@ const makeEndoBootstrap = async ( `Worker termination grace period ${gracePeriodMs}ms elapsed`, ); }) - .catch(hardCancel); + .catch(forceCancel); await workerTerminated; }; - context.onCancel(softCancel); + context.onCancel(gracefulCancel); const worker = Far('EndoWorker', {}); From bfc7c7f9139064d555a68fb93f951e80c7a59e95 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Thu, 28 Dec 2023 19:26:25 -0800 Subject: [PATCH 223/234] refactor(daemon)!: Despecify id512/sha512 in formula type --- packages/daemon/src/daemon-node-powers.js | 37 ++++------- packages/daemon/src/daemon.js | 77 ++++++++++++----------- packages/daemon/src/host.js | 27 ++++---- packages/daemon/src/mail.js | 2 +- packages/daemon/src/pet-store.js | 4 +- packages/daemon/src/types.d.ts | 10 +-- packages/daemon/src/web-page-bundler.js | 4 +- packages/daemon/test/test-endo.js | 5 ++ 8 files changed, 84 insertions(+), 82 deletions(-) diff --git a/packages/daemon/src/daemon-node-powers.js b/packages/daemon/src/daemon-node-powers.js index ecaf4394ec..683da6a79b 100644 --- a/packages/daemon/src/daemon-node-powers.js +++ b/packages/daemon/src/daemon-node-powers.js @@ -565,25 +565,20 @@ export const makeDaemonicPersistencePowers = ( /** * @param {string} formulaType - * @param {string} formulaId512 + * @param {string} formulaNumber */ - const makeFormulaPath = (formulaType, formulaId512) => { + const makeFormulaPath = (formulaType, formulaNumber) => { const { statePath } = locator; - if (formulaId512.length < 3) { + if (formulaNumber.length < 3) { throw new TypeError( - `Invalid formula identifier ${q(formulaId512)} for formula of type ${q( + `Invalid formula identifier ${q(formulaNumber)} for formula of type ${q( formulaType, )}`, ); } - const head = formulaId512.slice(0, 2); - const tail = formulaId512.slice(2); - const directory = filePowers.joinPath( - statePath, - 'formulas', - formulaType, - head, - ); + const head = formulaNumber.slice(0, 2); + const tail = formulaNumber.slice(2); + const directory = filePowers.joinPath(statePath, 'formulas', head); const file = filePowers.joinPath(directory, `${tail}.json`); return harden({ directory, file }); }; @@ -612,8 +607,8 @@ export const makeDaemonicPersistencePowers = ( }; // Persist instructions for revival (this can be collected) - const writeFormula = async (formula, formulaType, formulaId512) => { - const { directory, file } = makeFormulaPath(formulaType, formulaId512); + const writeFormula = async (formula, formulaType, formulaNumber) => { + const { directory, file } = makeFormulaPath(formulaType, formulaNumber); // TODO Take care to write atomically with a rename here. await filePowers.makePath(directory); await filePowers.writeFileText(file, `${q(formula)}\n`); @@ -657,19 +652,11 @@ export const makeDaemonicControlPowers = ( const makeWorker = async (workerId, daemonWorkerFacet, cancelled) => { const { cachePath, statePath, ephemeralStatePath, sockPath } = locator; - const workerCachePath = filePowers.joinPath( - cachePath, - 'worker-id512', - workerId, - ); - const workerStatePath = filePowers.joinPath( - statePath, - 'worker-id512', - workerId, - ); + const workerCachePath = filePowers.joinPath(cachePath, 'worker', workerId); + const workerStatePath = filePowers.joinPath(statePath, 'worker', workerId); const workerEphemeralStatePath = filePowers.joinPath( ephemeralStatePath, - 'worker-id512', + 'worker', workerId, ); diff --git a/packages/daemon/src/daemon.js b/packages/daemon/src/daemon.js index 85289968a1..dedb0960e0 100644 --- a/packages/daemon/src/daemon.js +++ b/packages/daemon/src/daemon.js @@ -83,15 +83,15 @@ const makeEndoBootstrap = async ( const contentStore = persistencePowers.makeContentSha512Store(); const rootNonce = await persistencePowers.provideRootNonce(); - const endoFormulaIdentifier = `endo-id512:${rootNonce}`; + const endoFormulaIdentifier = `endo:${rootNonce}`; const defaultHostFormulaNumber = derive(rootNonce, 'host'); - const defaultHostFormulaIdentifier = `host-id512:${defaultHostFormulaNumber}`; - const webPageJsFormulaIdentifier = `web-page-js-id512:${derive( + const defaultHostFormulaIdentifier = `host:${defaultHostFormulaNumber}`; + const webPageJsFormulaIdentifier = `web-page-js:${derive( rootNonce, 'web-page-js', )}`; const leastAuthorityFormulaNumber = derive(rootNonce, 'least-authority'); - const leastAuthorityFormulaIdentifier = `least-authority-id512:${leastAuthorityFormulaNumber}`; + const leastAuthorityFormulaIdentifier = `least-authority:${leastAuthorityFormulaNumber}`; /** @type {Map>} */ const controllerForFormulaIdentifier = new Map(); @@ -118,7 +118,7 @@ const makeEndoBootstrap = async ( */ const storeReaderRef = async readerRef => { const sha512Hex = await contentStore.store(makeRefReader(readerRef)); - return `readable-blob-sha512:${sha512Hex}`; + return `readable-blob:${sha512Hex}`; }; /** @@ -373,8 +373,8 @@ const makeEndoBootstrap = async ( context, ); } else if (formula.type === 'guest') { - const storeFormulaIdentifier = `pet-store-id512:${formulaNumber}`; - const workerFormulaIdentifier = `worker-id512:${formulaNumber}`; + const storeFormulaIdentifier = `pet-store:${formulaNumber}`; + const workerFormulaIdentifier = `worker:${formulaNumber}`; // Behold, recursion: // eslint-disable-next-line no-use-before-define return makeIdentifiedGuestController( @@ -408,44 +408,45 @@ const makeEndoBootstrap = async ( }; /** - * @param {string} formulaIdentifier + * @param {string} formulaType + * @param {string} formulaNumber * @param {import('./types.js').Context} context */ const makeControllerForFormulaIdentifier = async ( - formulaIdentifier, + formulaType, + formulaNumber, context, ) => { - const { type: formulaType, number: formulaNumber } = - parseFormulaIdentifier(formulaIdentifier); - if (formulaType === 'readable-blob-sha512') { + const formulaIdentifier = `${formulaType}:${formulaNumber}`; + if (formulaType === 'readable-blob') { // Behold, forward-reference: // eslint-disable-next-line no-use-before-define const external = makeReadableBlob(formulaNumber); return { external, internal: undefined }; - } else if (formulaType === 'worker-id512') { + } else if (formulaType === 'worker') { return makeIdentifiedWorkerController(formulaNumber, context); - } else if (formulaType === 'pet-inspector-id512') { + } else if (formulaType === 'pet-inspector') { const storeFormulaNumber = derive(formulaNumber, 'pet-store'); - const storeFormulaIdentifier = `pet-store-id512:${storeFormulaNumber}`; + const storeFormulaIdentifier = `pet-store:${storeFormulaNumber}`; // Behold, unavoidable forward-reference: // eslint-disable-next-line no-use-before-define const external = makePetStoreInspector(storeFormulaIdentifier); return { external, internal: undefined }; - } else if (formulaType === 'pet-store-id512') { + } else if (formulaType === 'pet-store') { const external = petStorePowers.makeIdentifiedPetStore( formulaNumber, assertPetName, ); return { external, internal: undefined }; - } else if (formulaType === 'host-id512') { + } else if (formulaType === 'host') { const workerFormulaNumber = derive(formulaNumber, 'worker'); - const workerFormulaIdentifier = `worker-id512:${workerFormulaNumber}`; + const workerFormulaIdentifier = `worker:${workerFormulaNumber}`; const inspectorFormulaNumber = derive(formulaNumber, 'pet-inspector'); - const inspectorFormulaIdentifier = `pet-inspector-id512:${inspectorFormulaNumber}`; + const inspectorFormulaIdentifier = `pet-inspector:${inspectorFormulaNumber}`; // Note the pet store formula number derivation path: // root -> host -> inspector -> pet store const storeFormulaNumber = derive(inspectorFormulaNumber, 'pet-store'); - const storeFormulaIdentifier = `pet-store-id512:${storeFormulaNumber}`; + const storeFormulaIdentifier = `pet-store:${storeFormulaNumber}`; // Behold, recursion: // eslint-disable-next-line no-use-before-define @@ -458,9 +459,9 @@ const makeEndoBootstrap = async ( leastAuthorityFormulaIdentifier, context, ); - } else if (formulaType === 'endo-id512') { + } else if (formulaType === 'endo') { if (formulaNumber !== rootNonce) { - throw new Error('Invalid endo-id512 formula number.'); + throw new Error('Invalid endo formula number.'); } // TODO reframe "cancelled" as termination of the "endo" object and @@ -468,16 +469,16 @@ const makeEndoBootstrap = async ( // Behold, self-referentiality: // eslint-disable-next-line no-use-before-define return { external: endoBootstrap, internal: undefined }; - } else if (formulaType === 'least-authority-id512') { + } else if (formulaType === 'least-authority') { return { external: leastAuthority, internal: undefined }; - } else if (formulaType === 'web-page-js-id512') { + } else if (formulaType === 'web-page-js') { if (persistencePowers.getWebPageBundlerFormula === undefined) { throw Error('No web-page-js formula provided.'); } // Note that this worker is hardcoded to be the "MAIN" worker of the // default host. const workerFormulaNumber = derive(defaultHostFormulaNumber, 'worker'); - const workerFormulaIdentifier = `worker-id512:${workerFormulaNumber}`; + const workerFormulaIdentifier = `worker:${workerFormulaNumber}`; return makeControllerForFormula( 'web-page-js', @@ -490,10 +491,10 @@ const makeEndoBootstrap = async ( ); } else if ( [ - 'eval-id512', - 'make-unconfined-id512', - 'make-bundle-id512', - 'guest-id512', + 'eval', + 'make-unconfined', + 'make-bundle', + 'guest', 'web-bundle', ].includes(formulaType) ) { @@ -585,6 +586,9 @@ const makeEndoBootstrap = async ( * @param {string} formulaIdentifier */ const provideControllerForFormulaIdentifier = formulaIdentifier => { + const { type: formulaType, number: formulaNumber } = + parseFormulaIdentifier(formulaIdentifier); + let controller = controllerForFormulaIdentifier.get(formulaIdentifier); if (controller !== undefined) { return controller; @@ -606,7 +610,10 @@ const makeEndoBootstrap = async ( internal: E.get(partial).internal, }); controllerForFormulaIdentifier.set(formulaIdentifier, controller); - resolve(makeControllerForFormulaIdentifier(formulaIdentifier, context)); + + resolve( + makeControllerForFormulaIdentifier(formulaType, formulaNumber, context), + ); return controller; }; @@ -683,11 +690,11 @@ const makeEndoBootstrap = async ( parseFormulaIdentifier(formulaIdentifier); if ( ![ - 'eval-id512', - 'lookup-id512', - 'make-unconfined-id512', - 'make-bundle-id512', - 'guest-id512', + 'eval', + 'lookup', + 'make-unconfined', + 'make-bundle', + 'guest', 'web-bundle', ].includes(formulaType) ) { diff --git a/packages/daemon/src/host.js b/packages/daemon/src/host.js index 84ba729e1c..fdf86f225b 100644 --- a/packages/daemon/src/host.js +++ b/packages/daemon/src/host.js @@ -107,6 +107,7 @@ export const makeHostMaker = ({ if (petName !== undefined) { formulaIdentifier = identifyLocal(petName); } + if (formulaIdentifier === undefined) { /** @type {import('./types.js').GuestFormula} */ const formula = { @@ -116,19 +117,21 @@ export const makeHostMaker = ({ const { value, formulaIdentifier: guestFormulaIdentifier } = // Behold, recursion: // eslint-disable-next-line no-use-before-define - await provideValueForFormula(formula, 'guest-id512'); + await provideValueForFormula(formula, 'guest'); if (petName !== undefined) { assertPetName(petName); await petStore.write(petName, guestFormulaIdentifier); } + return { value, formulaIdentifier: guestFormulaIdentifier }; - } else if (!formulaIdentifier.startsWith('guest-id512:')) { + } else if (!formulaIdentifier.startsWith('guest:')) { throw new Error( `Existing pet name does not designate a guest powers capability: ${q( petName, )}`, ); } + const newGuestController = /** @type {import('./types.js').Controller<>} */ ( provideControllerForFormulaIdentifier(formulaIdentifier) @@ -176,10 +179,10 @@ export const makeHostMaker = ({ let workerFormulaIdentifier = identifyLocal(workerName); if (workerFormulaIdentifier === undefined) { const workerId512 = await randomHex512(); - workerFormulaIdentifier = `worker-id512:${workerId512}`; + workerFormulaIdentifier = `worker:${workerId512}`; assertPetName(workerName); await petStore.write(workerName, workerFormulaIdentifier); - } else if (!workerFormulaIdentifier.startsWith('worker-id512:')) { + } else if (!workerFormulaIdentifier.startsWith('worker:')) { throw new Error(`Not a worker ${q(workerName)}`); } return /** @type {Promise} */ ( @@ -197,13 +200,13 @@ export const makeHostMaker = ({ return mainWorkerFormulaIdentifier; } else if (workerName === 'NEW') { const workerId512 = await randomHex512(); - return `worker-id512:${workerId512}`; + return `worker:${workerId512}`; } assertPetName(workerName); let workerFormulaIdentifier = identifyLocal(workerName); if (workerFormulaIdentifier === undefined) { const workerId512 = await randomHex512(); - workerFormulaIdentifier = `worker-id512:${workerId512}`; + workerFormulaIdentifier = `worker:${workerId512}`; assertPetName(workerName); await petStore.write(workerName, workerFormulaIdentifier); } @@ -288,7 +291,7 @@ export const makeHostMaker = ({ // eslint-disable-next-line no-use-before-define const { formulaIdentifier, value } = await provideValueForFormula( evalFormula, - 'eval-id512', + 'eval', ); if (resultName !== undefined) { await petStore.write(resultName, formulaIdentifier); @@ -323,7 +326,7 @@ export const makeHostMaker = ({ // eslint-disable-next-line no-use-before-define const { formulaIdentifier, value } = await provideValueForFormula( formula, - 'make-unconfined-id512', + 'make-unconfined', ); if (resultName !== undefined) { await petStore.write(resultName, formulaIdentifier); @@ -368,7 +371,7 @@ export const makeHostMaker = ({ // eslint-disable-next-line no-use-before-define const { value, formulaIdentifier } = await provideValueForFormula( formula, - 'make-bundle-id512', + 'make-bundle', ); if (resultName !== undefined) { @@ -383,7 +386,7 @@ export const makeHostMaker = ({ */ const makeWorker = async petName => { const workerId512 = await randomHex512(); - const formulaIdentifier = `worker-id512:${workerId512}`; + const formulaIdentifier = `worker:${workerId512}`; if (petName !== undefined) { assertPetName(petName); await petStore.write(petName, formulaIdentifier); @@ -408,12 +411,12 @@ export const makeHostMaker = ({ } if (formulaIdentifier === undefined) { const id512 = await randomHex512(); - formulaIdentifier = `host-id512:${id512}`; + formulaIdentifier = `host:${id512}`; if (petName !== undefined) { assertPetName(petName); await petStore.write(petName, formulaIdentifier); } - } else if (!formulaIdentifier.startsWith('host-id512:')) { + } else if (!formulaIdentifier.startsWith('host:')) { throw new Error( `Existing pet name does not designate a host powers capability: ${q( petName, diff --git a/packages/daemon/src/mail.js b/packages/daemon/src/mail.js index 14bffb1daf..ea03e986e4 100644 --- a/packages/daemon/src/mail.js +++ b/packages/daemon/src/mail.js @@ -136,7 +136,7 @@ export const makeMailboxMaker = ({ }; return provideValueForNumberedFormula( - 'lookup-id512', + 'lookup', lookupFormulaNumber, lookupFormula, ); diff --git a/packages/daemon/src/pet-store.js b/packages/daemon/src/pet-store.js index 5586febb6d..d3395cf9a5 100644 --- a/packages/daemon/src/pet-store.js +++ b/packages/daemon/src/pet-store.js @@ -9,7 +9,7 @@ const { quote: q } = assert; const validIdPattern = /^[0-9a-f]{128}$/; const validFormulaPattern = - /^(?:(?:readable-blob-sha512|worker-id512|pet-store-id512|pet-inspector-id512|eval-id512|lookup-id512|make-unconfined-id512|make-bundle-id512|host-id512|guest-id512):[0-9a-f]{128}|web-bundle:[0-9a-f]{32})$/; + /^(?:(?:readable-blob|worker|pet-store|pet-inspector|eval|lookup|make-unconfined|make-bundle|host|guest):[0-9a-f]{128}|web-bundle:[0-9a-f]{32})$/; /** * @param {import('./types.js').FilePowers} filePowers @@ -287,7 +287,7 @@ export const makePetStoreMaker = (filePowers, locator) => { const suffix = id.slice(2); const petNameDirectoryPath = filePowers.joinPath( locator.statePath, - 'pet-store-id512', + 'pet-store', prefix, suffix, ); diff --git a/packages/daemon/src/types.d.ts b/packages/daemon/src/types.d.ts index 3390f4749e..e6e1cdde0d 100644 --- a/packages/daemon/src/types.d.ts +++ b/packages/daemon/src/types.d.ts @@ -285,10 +285,10 @@ export type EndoInspector = { }; export type KnownEndoInspectors = { - 'eval-id512': EndoInspector<'endowments' | 'source' | 'worker'>; - 'make-unconfined-id512': EndoInspector<'host'>; - 'make-bundle-id512': EndoInspector<'bundle' | 'powers' | 'worker'>; - 'guest-id512': EndoInspector<'bundle' | 'powers'>; + eval: EndoInspector<'endowments' | 'source' | 'worker'>; + 'make-unconfined': EndoInspector<'host'>; + 'make-bundle': EndoInspector<'bundle' | 'powers' | 'worker'>; + guest: EndoInspector<'bundle' | 'powers'>; 'web-bundle': EndoInspector<'powers' | 'specifier' | 'worker'>; // This is an "empty" inspector, in that there is nothing to `lookup()` or `list()`. [formulaType: string]: EndoInspector; @@ -381,7 +381,7 @@ export type DaemonicPersistencePowers = { writeFormula: ( formula: Formula, formulaType: string, - formulaId512: string, + formulaNumber: string, ) => Promise; getWebPageBundlerFormula?: ( workerFormulaIdentifier: string, diff --git a/packages/daemon/src/web-page-bundler.js b/packages/daemon/src/web-page-bundler.js index 00516da8aa..fc208eee8a 100644 --- a/packages/daemon/src/web-page-bundler.js +++ b/packages/daemon/src/web-page-bundler.js @@ -2,8 +2,8 @@ // This is a built-in unconfined plugin for lazily constructing the web-page.js // bundle for booting up web caplets. -// The hard-coded 'web-page-js-id512' formula is a hard-coded 'make-unconfined' formula -// that runs this program in worker 0. +// The 'web-page-js' formula is a hard-coded 'make-unconfined' formula that runs +// this program in worker 0. // It does not accept its endowed powers. import 'ses'; diff --git a/packages/daemon/test/test-endo.js b/packages/daemon/test/test-endo.js index 395e77216f..5b92e8d936 100644 --- a/packages/daemon/test/test-endo.js +++ b/packages/daemon/test/test-endo.js @@ -528,17 +528,21 @@ test('guest facet receives a message for host', async t => { const guest = E(host).provideGuest('guest'); await E(host).provideWorker('worker'); await E(host).evaluate('worker', '10', [], [], 'ten1'); + const iteratorRef = E(host).followMessages(); E.sendOnly(guest).request('HOST', 'a number', 'number'); const { value: message0 } = await E(iteratorRef).next(); t.is(message0.number, 0); await E(host).resolve(message0.number, 'ten1'); + await E(guest).send('HOST', ['Hello, World!'], ['gift'], ['number']); + const { value: message1 } = await E(iteratorRef).next(); t.is(message1.number, 1); await E(host).adopt(message1.number, 'gift', 'ten2'); const ten = await E(host).lookup('ten2'); t.is(ten, 10); + // Host should have received messages. const hostInbox = await E(host).listMessages(); t.deepEqual( @@ -548,6 +552,7 @@ test('guest facet receives a message for host', async t => { { type: 'package', who: 'guest', dest: 'SELF' }, ], ); + // Guest should have own sent messages. const guestInbox = await E(guest).listMessages(); t.deepEqual( From 56c9138742fd3f43cdb4f45b45a2aad038ab0d2c Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Fri, 12 Jan 2024 17:24:38 -0800 Subject: [PATCH 224/234] fix(cli): Improve log behavior when stopped --- packages/cli/src/commands/log.js | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/packages/cli/src/commands/log.js b/packages/cli/src/commands/log.js index 1338fa851e..1c4abbb973 100644 --- a/packages/cli/src/commands/log.js +++ b/packages/cli/src/commands/log.js @@ -46,12 +46,23 @@ export const log = async ({ follow, ping }) => cancelled.catch(cancelFollower); (async () => { - const { getBootstrap } = await makeEndoClient( + const client = await makeEndoClient( 'log-follower-probe', sockPath, followCancelled, - ); - const bootstrap = await getBootstrap(); + ).catch(error => { + console.error(`Endo offline: ${error.message}`); + }); + if (client === undefined) { + return; + } + const { getBootstrap } = client; + const bootstrap = await getBootstrap().catch(error => { + console.error(`Endo offline: ${error.message}`); + }); + if (bootstrap === undefined) { + return; + } for (;;) { await delay(logCheckIntervalMs, followCancelled); await E(bootstrap).ping(); From c30e7b3adab609d05e27d4858e78d64c62b3bf33 Mon Sep 17 00:00:00 2001 From: Erik Marks Date: Wed, 14 Feb 2024 19:18:31 -0800 Subject: [PATCH 225/234] fix(cli): Remove unused arguments to command handlers Co-authored-by: Kris Kowal --- packages/cli/src/commands/dismiss.js | 9 +-------- packages/cli/src/commands/inbox.js | 8 +------- packages/cli/src/commands/log.js | 2 +- packages/cli/src/commands/mkhost.js | 9 +-------- packages/cli/src/commands/ping.js | 2 +- packages/cli/src/commands/reject.js | 3 --- packages/cli/src/commands/remove.js | 8 +------- packages/cli/src/commands/request.js | 3 --- packages/cli/src/commands/show.js | 2 +- packages/cli/src/commands/spawn.js | 8 +------- 10 files changed, 8 insertions(+), 46 deletions(-) diff --git a/packages/cli/src/commands/dismiss.js b/packages/cli/src/commands/dismiss.js index 7488b33f61..9446d7f95e 100644 --- a/packages/cli/src/commands/dismiss.js +++ b/packages/cli/src/commands/dismiss.js @@ -3,14 +3,7 @@ import os from 'os'; import { E } from '@endo/far'; import { withEndoParty } from '../context.js'; -export const dismissCommand = async ({ - cancel, - cancelled, - sockPath, - messageNumberText, - message, - partyNames, -}) => +export const dismissCommand = async ({ messageNumberText, partyNames }) => withEndoParty(partyNames, { os, process }, async ({ party }) => { // TODO less bad number parsing. const messageNumber = Number(messageNumberText); diff --git a/packages/cli/src/commands/inbox.js b/packages/cli/src/commands/inbox.js index 88084cca94..545e77ff74 100644 --- a/packages/cli/src/commands/inbox.js +++ b/packages/cli/src/commands/inbox.js @@ -6,13 +6,7 @@ import { makeRefIterator } from '@endo/daemon'; import { withEndoParty } from '../context.js'; import { formatMessage } from '../message-format.js'; -export const inbox = async ({ - cancel, - cancelled, - sockPath, - follow, - partyNames, -}) => +export const inbox = async ({ follow, partyNames }) => withEndoParty(partyNames, { os, process }, async ({ party }) => { const messages = follow ? makeRefIterator(E(party).followMessages()) diff --git a/packages/cli/src/commands/log.js b/packages/cli/src/commands/log.js index 1c4abbb973..83efe283f3 100644 --- a/packages/cli/src/commands/log.js +++ b/packages/cli/src/commands/log.js @@ -23,7 +23,7 @@ const delay = async (ms, cancelled) => { }; export const log = async ({ follow, ping }) => - withInterrupt(async ({ cancel, cancelled }) => { + withInterrupt(async ({ cancelled }) => { const logCheckIntervalMs = ping !== undefined ? Number(ping) : 5_000; const { username, homedir } = os.userInfo(); diff --git a/packages/cli/src/commands/mkhost.js b/packages/cli/src/commands/mkhost.js index 2188075c06..0ac783d02b 100644 --- a/packages/cli/src/commands/mkhost.js +++ b/packages/cli/src/commands/mkhost.js @@ -3,14 +3,7 @@ import os from 'os'; import { E } from '@endo/far'; import { withEndoParty } from '../context.js'; -export const mkhost = async ({ - cancel, - cancelled, - sockPath, - name, - partyNames, - introducedNames, -}) => +export const mkhost = async ({ name, partyNames, introducedNames }) => withEndoParty(partyNames, { os, process }, async ({ party }) => { const newHost = await E(party).provideHost(name, { introducedNames }); console.log(newHost); diff --git a/packages/cli/src/commands/ping.js b/packages/cli/src/commands/ping.js index 2d7ac74fef..1c8502a943 100644 --- a/packages/cli/src/commands/ping.js +++ b/packages/cli/src/commands/ping.js @@ -6,7 +6,7 @@ import { makeEndoClient } from '@endo/daemon'; import { withInterrupt } from '../context.js'; export const ping = () => - withInterrupt(async ({ cancel, cancelled }) => { + withInterrupt(async ({ cancelled }) => { const { username, homedir } = os.userInfo(); const temp = os.tmpdir(); const info = { diff --git a/packages/cli/src/commands/reject.js b/packages/cli/src/commands/reject.js index fda70fe0e2..af73d3227d 100644 --- a/packages/cli/src/commands/reject.js +++ b/packages/cli/src/commands/reject.js @@ -4,9 +4,6 @@ import { E } from '@endo/far'; import { withEndoParty } from '../context.js'; export const rejectCommand = async ({ - cancel, - cancelled, - sockPath, requestNumberText, message, partyNames, diff --git a/packages/cli/src/commands/remove.js b/packages/cli/src/commands/remove.js index 10a5cc7202..bc81271cc6 100644 --- a/packages/cli/src/commands/remove.js +++ b/packages/cli/src/commands/remove.js @@ -3,13 +3,7 @@ import os from 'os'; import { E } from '@endo/far'; import { withEndoParty } from '../context.js'; -export const remove = async ({ - cancel, - cancelled, - sockPath, - petNames, - partyNames, -}) => +export const remove = async ({ petNames, partyNames }) => withEndoParty(partyNames, { os, process }, async ({ party }) => Promise.all(petNames.map(petName => E(party).remove(petName))), ); diff --git a/packages/cli/src/commands/request.js b/packages/cli/src/commands/request.js index 2b7b129381..f9d0c19f6f 100644 --- a/packages/cli/src/commands/request.js +++ b/packages/cli/src/commands/request.js @@ -4,9 +4,6 @@ import { E } from '@endo/far'; import { withEndoParty } from '../context.js'; export const request = async ({ - cancel, - cancelled, - sockPath, description, toName, resultName, diff --git a/packages/cli/src/commands/show.js b/packages/cli/src/commands/show.js index 554da43b16..1ce602ee77 100644 --- a/packages/cli/src/commands/show.js +++ b/packages/cli/src/commands/show.js @@ -3,7 +3,7 @@ import os from 'os'; import { E } from '@endo/far'; import { withEndoParty } from '../context.js'; -export const show = async ({ cancel, cancelled, sockPath, name, partyNames }) => +export const show = async ({ name, partyNames }) => withEndoParty(partyNames, { os, process }, async ({ party }) => { const pet = await E(party).lookup(name); console.log(pet); diff --git a/packages/cli/src/commands/spawn.js b/packages/cli/src/commands/spawn.js index 6418ad3326..7708490ecf 100644 --- a/packages/cli/src/commands/spawn.js +++ b/packages/cli/src/commands/spawn.js @@ -4,13 +4,7 @@ import os from 'os'; import { E } from '@endo/far'; import { withEndoParty } from '../context.js'; -export const spawn = async ({ - cancel, - cancelled, - sockPath, - petNames, - partyNames, -}) => +export const spawn = async ({ petNames, partyNames }) => withEndoParty(partyNames, { os, process }, async ({ party }) => Promise.all(petNames.map(petName => E(party).makeWorker(petName))), ); From 986af720a64af07a4e5d6435ed9820727f2f283f Mon Sep 17 00:00:00 2001 From: Erik Marks Date: Thu, 15 Feb 2024 08:41:17 -0800 Subject: [PATCH 226/234] feat(cli,daemon): Make packages private Set `package.json['private']` to `true` to prevent accidentally publishing `@endo/daemon` and `@endo/cli`. --- packages/cli/package.json | 1 + packages/daemon/package.json | 1 + 2 files changed, 2 insertions(+) diff --git a/packages/cli/package.json b/packages/cli/package.json index ae4d1449fe..97a3b74b33 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,7 @@ { "name": "@endo/cli", "version": "1.0.2", + "private": true, "description": "Endo command line interface", "keywords": [], "author": "Endo contributors", diff --git a/packages/daemon/package.json b/packages/daemon/package.json index 8ba9073629..071f03e0ed 100644 --- a/packages/daemon/package.json +++ b/packages/daemon/package.json @@ -1,6 +1,7 @@ { "name": "@endo/daemon", "version": "1.0.2", + "private": true, "description": "Endo daemon", "keywords": [ "endo", From daf5258499dee2f20c6a572f48a26d1bd4d673f9 Mon Sep 17 00:00:00 2001 From: Erik Marks Date: Thu, 15 Feb 2024 08:48:27 -0800 Subject: [PATCH 227/234] refactor(cli,daemon): Rename leftover "reset" references to "purge" --- packages/cli/src/endo.js | 9 +++++--- packages/daemon/index.js | 2 +- packages/daemon/test/test-endo.js | 36 +++++++++++++++---------------- packages/daemon/types.d.ts | 2 +- 4 files changed, 26 insertions(+), 23 deletions(-) diff --git a/packages/cli/src/endo.js b/packages/cli/src/endo.js index 30fe47d7e0..69444fb0de 100644 --- a/packages/cli/src/endo.js +++ b/packages/cli/src/endo.js @@ -530,15 +530,18 @@ export const main = async rawArgs => { ), ); if (doPurge) { - const { reset } = await import('@endo/daemon'); - await reset(); + const { purge } = await import('@endo/daemon'); + await purge(); } }); program .command('log') .option('-f, --follow', 'follow the tail of the log') - .option('-p,--ping ', 'milliseconds between daemon reset checks') + .option( + '-p,--ping ', + 'milliseconds between daemon restart checks', + ) .description('writes out the daemon log, optionally following updates') .action(async cmd => { const { follow, ping } = cmd.opts(); diff --git a/packages/daemon/index.js b/packages/daemon/index.js index 6e9a3f2e8b..f1822f0a49 100644 --- a/packages/daemon/index.js +++ b/packages/daemon/index.js @@ -163,7 +163,7 @@ export const stop = async (locator = defaultLocator) => { await clean(locator); }; -export const reset = async (locator = defaultLocator) => { +export const purge = async (locator = defaultLocator) => { // Attempt to restore to a running state if currently running, based on // whether we manage to terminate it. const needsRestart = await terminate(locator).then( diff --git a/packages/daemon/test/test-endo.js b/packages/daemon/test/test-endo.js index 5b92e8d936..c039e8999c 100644 --- a/packages/daemon/test/test-endo.js +++ b/packages/daemon/test/test-endo.js @@ -17,7 +17,7 @@ import { stop, restart, clean, - reset, + purge, makeEndoClient, makeReaderRef, } from '../index.js'; @@ -78,7 +78,7 @@ test('lifecycle', async t => { const locator = makeLocator('tmp', 'lifecycle'); await stop(locator).catch(() => {}); - await reset(locator); + await purge(locator); await clean(locator); await start(locator); await stop(locator); @@ -106,7 +106,7 @@ test('spawn and evaluate', async t => { const locator = makeLocator('tmp', 'spawn-eval'); await stop(locator).catch(() => {}); - await reset(locator); + await purge(locator); await start(locator); const { getBootstrap } = await makeEndoClient( @@ -129,7 +129,7 @@ test('anonymous spawn and evaluate', async t => { const locator = makeLocator('tmp', 'spawn-eval-anon'); await stop(locator).catch(() => {}); - await reset(locator); + await purge(locator); await start(locator); const { getBootstrap } = await makeEndoClient( @@ -151,7 +151,7 @@ test('persist spawn and evaluation', async t => { const locator = makeLocator('tmp', 'persist-spawn-eval'); await stop(locator).catch(() => {}); - await reset(locator); + await purge(locator); await start(locator); { @@ -208,7 +208,7 @@ test('store', async t => { const locator = makeLocator('tmp', 'store'); await stop(locator).catch(() => {}); - await reset(locator); + await purge(locator); await start(locator); { @@ -245,7 +245,7 @@ test('closure state lost by restart', async t => { const locator = makeLocator('tmp', 'restart-closures'); await stop(locator).catch(() => {}); - await reset(locator); + await purge(locator); await start(locator); { @@ -345,7 +345,7 @@ test('persist unconfined services and their requests', async t => { const locator = makeLocator('tmp', 'make-unconfined'); await stop(locator).catch(() => {}); - await reset(locator); + await purge(locator); await start(locator); const responderFinished = (async () => { @@ -430,7 +430,7 @@ test('persist confined services and their requests', async t => { const locator = makeLocator('tmp', 'make-bundle'); await stop(locator).catch(() => {}); - await reset(locator); + await purge(locator); await start(locator); const responderFinished = (async () => { @@ -821,7 +821,7 @@ test('make a host', async t => { const locator = makeLocator('tmp', 'make-host'); await stop(locator).catch(() => {}); - await reset(locator); + await purge(locator); await start(locator); const { getBootstrap } = await makeEndoClient( @@ -845,7 +845,7 @@ test('name and reuse inspector', async t => { const locator = makeLocator('tmp', 'inspector-reuse'); await stop(locator).catch(() => {}); - await reset(locator); + await purge(locator); await start(locator); const { getBootstrap } = await makeEndoClient( @@ -888,7 +888,7 @@ test('eval-mediated worker name', async t => { const locator = makeLocator('tmp', 'eval-worker-name'); await stop(locator).catch(() => {}); - await reset(locator); + await purge(locator); await start(locator); const { getBootstrap } = await makeEndoClient( @@ -935,7 +935,7 @@ test('lookup with single petname', async t => { const locator = makeLocator('tmp', 'lookup-single-petname'); await stop(locator).catch(() => {}); - await reset(locator); + await purge(locator); await start(locator); const { getBootstrap } = await makeEndoClient( @@ -966,7 +966,7 @@ test('lookup with petname path (inspector)', async t => { const locator = makeLocator('tmp', 'lookup-petname-path-inspector'); await stop(locator).catch(() => {}); - await reset(locator); + await purge(locator); await start(locator); const { getBootstrap } = await makeEndoClient( @@ -995,7 +995,7 @@ test('lookup with petname path (caplet with lookup method)', async t => { const locator = makeLocator('tmp', 'lookup-petname-path-caplet'); await stop(locator).catch(() => {}); - await reset(locator); + await purge(locator); await start(locator); const { getBootstrap } = await makeEndoClient( @@ -1026,7 +1026,7 @@ test('lookup with petname path (value has no lookup method)', async t => { const locator = makeLocator('tmp', 'lookup-petname-path-no-method'); await stop(locator).catch(() => {}); - await reset(locator); + await purge(locator); await start(locator); const { getBootstrap } = await makeEndoClient( @@ -1057,7 +1057,7 @@ test('evaluate name resolved by lookup path', async t => { const locator = makeLocator('tmp', 'name-resolved-by-lookup-path'); await stop(locator).catch(() => {}); - await reset(locator); + await purge(locator); await start(locator); const { getBootstrap } = await makeEndoClient( @@ -1087,7 +1087,7 @@ test('list special names', async t => { const locator = makeLocator('tmp', 'list-names'); await stop(locator).catch(() => {}); - await reset(locator); + await purge(locator); await start(locator); const { getBootstrap } = await makeEndoClient( diff --git a/packages/daemon/types.d.ts b/packages/daemon/types.d.ts index 70d42a37ee..b695309656 100644 --- a/packages/daemon/types.d.ts +++ b/packages/daemon/types.d.ts @@ -14,7 +14,7 @@ export function stop(locator?: Locator); export function restart(locator?: Locator); export function terminate(locator?: Locator); export function clean(locator?: Locator); -export function reset(locator?: Locator); +export function purge(locator?: Locator); export function makeEndoClient( name: string, sockPath: string, From 2456db7c9b7912513920d142ce10c2e5a54f5364 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Thu, 15 Feb 2024 10:37:43 -0800 Subject: [PATCH 228/234] chore: Update yarn.lock --- yarn.lock | 5689 +++++------------------------------------------------ 1 file changed, 542 insertions(+), 5147 deletions(-) diff --git a/yarn.lock b/yarn.lock index 85abcf2ebd..a673631944 100644 --- a/yarn.lock +++ b/yarn.lock @@ -23,13 +23,6 @@ dependencies: "@babel/highlight" "^7.18.6" -"@babel/code-frame@^7.0.0 <7.4.0": - version "7.0.0" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.0.0.tgz#06e2ab19bdb535385559aabb5ba59729482800f8" - integrity sha512-OfC2uemaknXr87bdLUkWog7nYuliM9Ij5HUcajsVcMCpQrcLmtxRbVFTIqmcSkSeYRBFBRxs2FiUqFJDLdiebA== - dependencies: - "@babel/highlight" "^7.0.0" - "@babel/code-frame@^7.22.13", "@babel/code-frame@^7.23.5": version "7.23.5" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.23.5.tgz#9009b69a8c602293476ad598ff53e4562e15c244" @@ -38,43 +31,7 @@ "@babel/highlight" "^7.23.4" chalk "^2.4.2" -"@babel/compat-data@^7.18.8", "@babel/compat-data@^7.20.0": - version "7.20.1" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.20.1.tgz#f2e6ef7790d8c8dbf03d379502dcc246dcce0b30" - integrity sha512-EWZ4mE2diW3QALKvDMiXnbZpRvlj+nayZ112nK93SnhqOtpdsbVD4W+2tEoT3YNBAG9RBR0ISY758ZkOgsn6pQ== - -"@babel/core@^7.0.0 <7.4.0": - version "7.3.4" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.3.4.tgz#921a5a13746c21e32445bf0798680e9d11a6530b" - integrity sha512-jRsuseXBo9pN197KnDwhhaaBzyZr2oIcLHHTt2oDdQrej5Qp57dCCJafWx5ivU8/alEYDpssYqv1MUqcxwQlrA== - dependencies: - "@babel/code-frame" "^7.0.0" - "@babel/generator" "^7.3.4" - "@babel/helpers" "^7.2.0" - "@babel/parser" "^7.3.4" - "@babel/template" "^7.2.2" - "@babel/traverse" "^7.3.4" - "@babel/types" "^7.3.4" - convert-source-map "^1.1.0" - debug "^4.1.0" - json5 "^2.1.0" - lodash "^4.17.11" - resolve "^1.3.2" - semver "^5.4.1" - source-map "^0.5.0" - -"@babel/generator@^7.0.0 <7.4.0": - version "7.3.4" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.3.4.tgz#9aa48c1989257877a9d971296e5b73bfe72e446e" - integrity sha512-8EXhHRFqlVVWXPezBW5keTiQi/rJMQTg/Y9uVCEZ0CAF3PKtCCaVRnp64Ii1ujhkoDhhF1fVsImoN4yJ2uz4Wg== - dependencies: - "@babel/types" "^7.3.4" - jsesc "^2.5.1" - lodash "^4.17.11" - source-map "^0.5.0" - trim-right "^1.0.1" - -"@babel/generator@^7.20.1", "@babel/generator@^7.3.4": +"@babel/generator@^7.20.1": version "7.20.4" resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.20.4.tgz#4d9f8f0c30be75fd90a0562099a26e5839602ab8" integrity sha512-luCf7yk/cm7yab6CAW1aiFnmEfBJplb/JojV56MYEK7ziWfGmFlTfmL9Ehwfy4gFhbjBfWO1wj7/TuSbVNEEtA== @@ -93,47 +50,6 @@ "@jridgewell/trace-mapping" "^0.3.17" jsesc "^2.5.1" -"@babel/helper-annotate-as-pure@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz#eaa49f6f80d5a33f9a5dd2276e6d6e451be0a6bb" - integrity sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA== - dependencies: - "@babel/types" "^7.18.6" - -"@babel/helper-builder-binary-assignment-operator-visitor@^7.18.6": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.18.9.tgz#acd4edfd7a566d1d51ea975dff38fd52906981bb" - integrity sha512-yFQ0YCHoIqarl8BCRwBL8ulYUaZpz3bNsA7oFepAzee+8/+ImtADXNOmO5vJvsPff3qi+hvpkY/NYBTrBQgdNw== - dependencies: - "@babel/helper-explode-assignable-expression" "^7.18.6" - "@babel/types" "^7.18.9" - -"@babel/helper-builder-react-jsx@^7.3.0": - version "7.19.0" - resolved "https://registry.yarnpkg.com/@babel/helper-builder-react-jsx/-/helper-builder-react-jsx-7.19.0.tgz#a1f4fef805388eda4b3c1bd8994dc585b0afa351" - integrity sha512-xvrbORmJ13lWrqyMErk4vczhXNNWdOSg1BZ+R/7D34SjDjToR5g3M5UpD6MyUekstI50qAHLWA1j7w5o1WK2Pw== - dependencies: - "@babel/helper-annotate-as-pure" "^7.18.6" - "@babel/types" "^7.19.0" - -"@babel/helper-compilation-targets@^7.18.9", "@babel/helper-compilation-targets@^7.19.0": - version "7.20.0" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.0.tgz#6bf5374d424e1b3922822f1d9bdaa43b1a139d0a" - integrity sha512-0jp//vDGp9e8hZzBc6N/KwA5ZK3Wsm/pfm4CrY7vzegkVxc65SgSn6wYOnwHe9Js9HRQ1YTCKLGPzDtaS3RoLQ== - dependencies: - "@babel/compat-data" "^7.20.0" - "@babel/helper-validator-option" "^7.18.6" - browserslist "^4.21.3" - semver "^6.3.0" - -"@babel/helper-create-regexp-features-plugin@^7.18.6", "@babel/helper-create-regexp-features-plugin@^7.19.0": - version "7.19.0" - resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.19.0.tgz#7976aca61c0984202baca73d84e2337a5424a41b" - integrity sha512-htnV+mHX32DF81amCDrwIDr8nrp1PTm+3wfBN9/v8QJOLEioOCOG7qNyq0nHeFiWbT3Eb7gsPwEmV64UCQ1jzw== - dependencies: - "@babel/helper-annotate-as-pure" "^7.18.6" - regexpu-core "^5.1.0" - "@babel/helper-environment-visitor@^7.18.9": version "7.18.9" resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz#0c0cee9b35d2ca190478756865bb3528422f51be" @@ -144,14 +60,7 @@ resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz#96159db61d34a29dba454c959f5ae4a649ba9167" integrity sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA== -"@babel/helper-explode-assignable-expression@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.18.6.tgz#41f8228ef0a6f1a036b8dfdfec7ce94f9a6bc096" - integrity sha512-eyAYAsQmB80jNfg4baAtLeWAQHfHFiR483rzFK+BhETlGZaQC9bsfrugfXDCbRHLQbIA7U5NxhhOxN7p/dWIcg== - dependencies: - "@babel/types" "^7.18.6" - -"@babel/helper-function-name@^7.1.0", "@babel/helper-function-name@^7.18.9", "@babel/helper-function-name@^7.19.0": +"@babel/helper-function-name@^7.19.0": version "7.19.0" resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz#941574ed5390682e872e52d3f38ce9d1bef4648c" integrity sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w== @@ -181,82 +90,7 @@ dependencies: "@babel/types" "^7.22.5" -"@babel/helper-member-expression-to-functions@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.18.9.tgz#1531661e8375af843ad37ac692c132841e2fd815" - integrity sha512-RxifAh2ZoVU67PyKIO4AMi1wTenGfMR/O/ae0CCRqwgBAt5v7xjdtRw7UoSbsreKrQn5t7r89eruK/9JjYHuDg== - dependencies: - "@babel/types" "^7.18.9" - -"@babel/helper-module-imports@^7.0.0", "@babel/helper-module-imports@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz#1e3ebdbbd08aad1437b428c50204db13c5a3ca6e" - integrity sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA== - dependencies: - "@babel/types" "^7.18.6" - -"@babel/helper-module-transforms@^7.1.0", "@babel/helper-module-transforms@^7.18.6", "@babel/helper-module-transforms@^7.19.0": - version "7.20.2" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.20.2.tgz#ac53da669501edd37e658602a21ba14c08748712" - integrity sha512-zvBKyJXRbmK07XhMuujYoJ48B5yvvmM6+wcpv6Ivj4Yg6qO7NOZOSnvZN9CRl1zz1Z4cKf8YejmCMh8clOoOeA== - dependencies: - "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-module-imports" "^7.18.6" - "@babel/helper-simple-access" "^7.20.2" - "@babel/helper-split-export-declaration" "^7.18.6" - "@babel/helper-validator-identifier" "^7.19.1" - "@babel/template" "^7.18.10" - "@babel/traverse" "^7.20.1" - "@babel/types" "^7.20.2" - -"@babel/helper-optimise-call-expression@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.18.6.tgz#9369aa943ee7da47edab2cb4e838acf09d290ffe" - integrity sha512-HP59oD9/fEHQkdcbgFCnbmgH5vIQTJbxh2yf+CdM89/glUNnuzr87Q8GIjGEnOktTROemO0Pe0iPAYbqZuOUiA== - dependencies: - "@babel/types" "^7.18.6" - -"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.18.9", "@babel/helper-plugin-utils@^7.19.0", "@babel/helper-plugin-utils@^7.8.0": - version "7.19.0" - resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.19.0.tgz#4796bb14961521f0f8715990bee2fb6e51ce21bf" - integrity sha512-40Ryx7I8mT+0gaNxm8JGTZFUITNqdLAgdg0hXzeVZxVD6nFsdhQvip6v8dqkRHzsz1VFpFAaOCHNn0vKBL7Czw== - -"@babel/helper-remap-async-to-generator@^7.18.6", "@babel/helper-remap-async-to-generator@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.18.9.tgz#997458a0e3357080e54e1d79ec347f8a8cd28519" - integrity sha512-dI7q50YKd8BAv3VEfgg7PS7yD3Rtbi2J1XMXaalXO0W0164hYLnh8zpjRS0mte9MfVp/tltvr/cfdXPvJr1opA== - dependencies: - "@babel/helper-annotate-as-pure" "^7.18.6" - "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-wrap-function" "^7.18.9" - "@babel/types" "^7.18.9" - -"@babel/helper-replace-supers@^7.18.6", "@babel/helper-replace-supers@^7.18.9": - version "7.19.1" - resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.19.1.tgz#e1592a9b4b368aa6bdb8784a711e0bcbf0612b78" - integrity sha512-T7ahH7wV0Hfs46SFh5Jz3s0B6+o8g3c+7TMxu7xKfmHikg7EAZ3I2Qk9LFhjxXq8sL7UkP5JflezNwoZa8WvWw== - dependencies: - "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-member-expression-to-functions" "^7.18.9" - "@babel/helper-optimise-call-expression" "^7.18.6" - "@babel/traverse" "^7.19.1" - "@babel/types" "^7.19.0" - -"@babel/helper-simple-access@^7.1.0", "@babel/helper-simple-access@^7.20.2": - version "7.20.2" - resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz#0ab452687fe0c2cfb1e2b9e0015de07fc2d62dd9" - integrity sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA== - dependencies: - "@babel/types" "^7.20.2" - -"@babel/helper-skip-transparent-expression-wrappers@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.18.9.tgz#778d87b3a758d90b471e7b9918f34a9a02eb5818" - integrity sha512-imytd2gHi3cJPsybLRbmFrF7u5BIEuI2cNheyKi3/iOBC63kNn3q8Crn2xVuESli0aM4KYsyEqKyS7lFL8YVtw== - dependencies: - "@babel/types" "^7.18.9" - -"@babel/helper-split-export-declaration@^7.0.0", "@babel/helper-split-export-declaration@^7.18.6": +"@babel/helper-split-export-declaration@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz#7367949bc75b20c6d5a5d4a97bba2824ae8ef075" integrity sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA== @@ -290,31 +124,7 @@ resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz#c4ae002c61d2879e724581d96665583dbc1dc0e0" integrity sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A== -"@babel/helper-validator-option@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz#bf0d2b5a509b1f336099e4ff36e1a63aa5db4db8" - integrity sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw== - -"@babel/helper-wrap-function@^7.18.9": - version "7.19.0" - resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.19.0.tgz#89f18335cff1152373222f76a4b37799636ae8b1" - integrity sha512-txX8aN8CZyYGTwcLhlk87KRqncAzhh5TpQamZUa0/u3an36NtDpUP6bQgBCBcLeBs09R/OwQu3OjK0k/HwfNDg== - dependencies: - "@babel/helper-function-name" "^7.19.0" - "@babel/template" "^7.18.10" - "@babel/traverse" "^7.19.0" - "@babel/types" "^7.19.0" - -"@babel/helpers@^7.2.0": - version "7.20.1" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.20.1.tgz#2ab7a0fcb0a03b5bf76629196ed63c2d7311f4c9" - integrity sha512-J77mUVaDTUJFZ5BpP6mMn6OIl3rEWymk2ZxDBQJUG3P+PbmyMcF3bYWvz0ma69Af1oobDqT/iAsvzhB58xhQUg== - dependencies: - "@babel/template" "^7.18.10" - "@babel/traverse" "^7.20.1" - "@babel/types" "^7.20.0" - -"@babel/highlight@^7.0.0", "@babel/highlight@^7.18.6": +"@babel/highlight@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.18.6.tgz#81158601e93e2563795adcbfbdf5d64be3f2ecdf" integrity sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g== @@ -332,12 +142,7 @@ chalk "^2.4.2" js-tokens "^4.0.0" -"@babel/parser@^7.0.0 <7.4.0": - version "7.3.4" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.3.4.tgz#a43357e4bbf4b92a437fb9e465c192848287f27c" - integrity sha512-tXZCqWtlOOP4wgCp6RjRvLmfuhnqTLy9VHwRochJBCP2nDm27JnnuFEnXFASVyQNHk36jD1tAammsCEEqgscIQ== - -"@babel/parser@^7.18.10", "@babel/parser@^7.2.2", "@babel/parser@^7.20.1", "@babel/parser@^7.3.4", "@babel/parser@^7.7.0": +"@babel/parser@^7.18.10", "@babel/parser@^7.20.1", "@babel/parser@^7.7.0": version "7.20.3" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.20.3.tgz#5358cf62e380cf69efcb87a7bb922ff88bfac6e2" integrity sha512-OP/s5a94frIPXwjzEcv5S/tpQfc6XhxYUnmWpgdqMWGgYCuErA3SzozaRAMQgSZWKeTJxht9aWAkUY+0UzvOFg== @@ -347,407 +152,7 @@ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.23.6.tgz#ba1c9e512bda72a47e285ae42aff9d2a635a9e3b" integrity sha512-Z2uID7YJ7oNvAI20O9X0bblw7Qqs8Q2hFy0R9tAfnfLkp5MW0UH9eUvnDSnFwKZ0AvgS1ucqR4KzvVHgnke1VQ== -"@babel/plugin-proposal-async-generator-functions@^7.2.0": - version "7.19.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.19.1.tgz#34f6f5174b688529342288cd264f80c9ea9fb4a7" - integrity sha512-0yu8vNATgLy4ivqMNBIwb1HebCelqN7YX8SL3FDXORv/RqT0zEEWUCH4GH44JsSrvCu6GqnAdR5EBFAPeNBB4Q== - dependencies: - "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-plugin-utils" "^7.19.0" - "@babel/helper-remap-async-to-generator" "^7.18.9" - "@babel/plugin-syntax-async-generators" "^7.8.4" - -"@babel/plugin-proposal-json-strings@^7.2.0": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.18.6.tgz#7e8788c1811c393aff762817e7dbf1ebd0c05f0b" - integrity sha512-lr1peyn9kOdbYc0xr0OdHTZ5FMqS6Di+H0Fz2I/JwMzGmzJETNeOFq2pBySw6X/KFL5EWDjlJuMsUGRFb8fQgQ== - dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - "@babel/plugin-syntax-json-strings" "^7.8.3" - -"@babel/plugin-proposal-object-rest-spread@^7.3.4": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.18.9.tgz#f9434f6beb2c8cae9dfcf97d2a5941bbbf9ad4e7" - integrity sha512-kDDHQ5rflIeY5xl69CEqGEZ0KY369ehsCIEbTGb4siHG5BE9sga/T0r0OUwyZNLMmZE79E1kbsqAjwFCW4ds6Q== - dependencies: - "@babel/compat-data" "^7.18.8" - "@babel/helper-compilation-targets" "^7.18.9" - "@babel/helper-plugin-utils" "^7.18.9" - "@babel/plugin-syntax-object-rest-spread" "^7.8.3" - "@babel/plugin-transform-parameters" "^7.18.8" - -"@babel/plugin-proposal-optional-catch-binding@^7.2.0": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.18.6.tgz#f9400d0e6a3ea93ba9ef70b09e72dd6da638a2cb" - integrity sha512-Q40HEhs9DJQyaZfUjjn6vE8Cv4GmMHCYuMGIWUnlxH6400VGxOuwWsPt4FxXxJkC/5eOzgn0z21M9gMT4MOhbw== - dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" - -"@babel/plugin-proposal-unicode-property-regex@^7.2.0": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.18.6.tgz#af613d2cd5e643643b65cded64207b15c85cb78e" - integrity sha512-2BShG/d5yoZyXZfVePH91urL5wTG6ASZU9M4o03lKK8u8UW1y08OMttBSOADTcJrnPMpvDXRG3G8fyLh4ovs8w== - dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.6" - -"@babel/plugin-syntax-async-generators@^7.2.0", "@babel/plugin-syntax-async-generators@^7.8.4": - version "7.8.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d" - integrity sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-flow@^7.2.0": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.18.6.tgz#774d825256f2379d06139be0c723c4dd444f3ca1" - integrity sha512-LUbR+KNTBWCUAqRG9ex5Gnzu2IOkt8jRJbHHXFT9q+L9zm7M/QQbEqXyw1n1pohYvOyWC8CjeyjrSaIwiYjK7A== - dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - -"@babel/plugin-syntax-json-strings@^7.2.0", "@babel/plugin-syntax-json-strings@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz#01ca21b668cd8218c9e640cb6dd88c5412b2c96a" - integrity sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-jsx@^7.2.0": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.18.6.tgz#a8feef63b010150abd97f1649ec296e849943ca0" - integrity sha512-6mmljtAedFGTWu2p/8WIORGwy+61PLgOMPOdazc7YoJ9ZCWUyFy3A6CpPkRKLKD1ToAesxX8KGEViAiLo9N+7Q== - dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - -"@babel/plugin-syntax-object-rest-spread@^7.2.0", "@babel/plugin-syntax-object-rest-spread@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz#60e225edcbd98a640332a2e72dd3e66f1af55871" - integrity sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-optional-catch-binding@^7.2.0", "@babel/plugin-syntax-optional-catch-binding@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz#6111a265bcfb020eb9efd0fdfd7d26402b9ed6c1" - integrity sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-transform-arrow-functions@^7.2.0": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.18.6.tgz#19063fcf8771ec7b31d742339dac62433d0611fe" - integrity sha512-9S9X9RUefzrsHZmKMbDXxweEH+YlE8JJEuat9FdvW9Qh1cw7W64jELCtWNkPBPX5En45uy28KGvA/AySqUh8CQ== - dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - -"@babel/plugin-transform-async-to-generator@^7.3.4": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.18.6.tgz#ccda3d1ab9d5ced5265fdb13f1882d5476c71615" - integrity sha512-ARE5wZLKnTgPW7/1ftQmSi1CmkqqHo2DNmtztFhvgtOWSDfq0Cq9/9L+KnZNYSNrydBekhW3rwShduf59RoXag== - dependencies: - "@babel/helper-module-imports" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.6" - "@babel/helper-remap-async-to-generator" "^7.18.6" - -"@babel/plugin-transform-block-scoped-functions@^7.2.0": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.18.6.tgz#9187bf4ba302635b9d70d986ad70f038726216a8" - integrity sha512-ExUcOqpPWnliRcPqves5HJcJOvHvIIWfuS4sroBUenPuMdmW+SMHDakmtS7qOo13sVppmUijqeTv7qqGsvURpQ== - dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - -"@babel/plugin-transform-block-scoping@^7.3.4": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.18.9.tgz#f9b7e018ac3f373c81452d6ada8bd5a18928926d" - integrity sha512-5sDIJRV1KtQVEbt/EIBwGy4T01uYIo4KRB3VUqzkhrAIOGx7AoctL9+Ux88btY0zXdDyPJ9mW+bg+v+XEkGmtw== - dependencies: - "@babel/helper-plugin-utils" "^7.18.9" - -"@babel/plugin-transform-classes@^7.3.4": - version "7.19.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.19.0.tgz#0e61ec257fba409c41372175e7c1e606dc79bb20" - integrity sha512-YfeEE9kCjqTS9IitkgfJuxjcEtLUHMqa8yUJ6zdz8vR7hKuo6mOy2C05P0F1tdMmDCeuyidKnlrw/iTppHcr2A== - dependencies: - "@babel/helper-annotate-as-pure" "^7.18.6" - "@babel/helper-compilation-targets" "^7.19.0" - "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-function-name" "^7.19.0" - "@babel/helper-optimise-call-expression" "^7.18.6" - "@babel/helper-plugin-utils" "^7.19.0" - "@babel/helper-replace-supers" "^7.18.9" - "@babel/helper-split-export-declaration" "^7.18.6" - globals "^11.1.0" - -"@babel/plugin-transform-computed-properties@^7.2.0": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.18.9.tgz#2357a8224d402dad623caf6259b611e56aec746e" - integrity sha512-+i0ZU1bCDymKakLxn5srGHrsAPRELC2WIbzwjLhHW9SIE1cPYkLCL0NlnXMZaM1vhfgA2+M7hySk42VBvrkBRw== - dependencies: - "@babel/helper-plugin-utils" "^7.18.9" - -"@babel/plugin-transform-destructuring@^7.2.0": - version "7.18.13" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.18.13.tgz#9e03bc4a94475d62b7f4114938e6c5c33372cbf5" - integrity sha512-TodpQ29XekIsex2A+YJPj5ax2plkGa8YYY6mFjCohk/IG9IY42Rtuj1FuDeemfg2ipxIFLzPeA83SIBnlhSIow== - dependencies: - "@babel/helper-plugin-utils" "^7.18.9" - -"@babel/plugin-transform-dotall-regex@^7.2.0": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.18.6.tgz#b286b3e7aae6c7b861e45bed0a2fafd6b1a4fef8" - integrity sha512-6S3jpun1eEbAxq7TdjLotAsl4WpQI9DxfkycRcKrjhQYzU87qpXdknpBg/e+TdcMehqGnLFi7tnFUBR02Vq6wg== - dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.6" - -"@babel/plugin-transform-duplicate-keys@^7.2.0": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.18.9.tgz#687f15ee3cdad6d85191eb2a372c4528eaa0ae0e" - integrity sha512-d2bmXCtZXYc59/0SanQKbiWINadaJXqtvIQIzd4+hNwkWBgyCd5F/2t1kXoUdvPMrxzPvhK6EMQRROxsue+mfw== - dependencies: - "@babel/helper-plugin-utils" "^7.18.9" - -"@babel/plugin-transform-exponentiation-operator@^7.2.0": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.18.6.tgz#421c705f4521888c65e91fdd1af951bfefd4dacd" - integrity sha512-wzEtc0+2c88FVR34aQmiz56dxEkxr2g8DQb/KfaFa1JYXOFVsbhvAonFN6PwVWj++fKmku8NP80plJ5Et4wqHw== - dependencies: - "@babel/helper-builder-binary-assignment-operator-visitor" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.6" - -"@babel/plugin-transform-flow-strip-types@^7.0.0 <7.4.0": - version "7.3.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.3.4.tgz#00156236defb7dedddc2d3c9477dcc01a4494327" - integrity sha512-PmQC9R7DwpBFA+7ATKMyzViz3zCaMNouzZMPZN2K5PnbBbtL3AXFYTkDk+Hey5crQq2A90UG5Uthz0mel+XZrA== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/plugin-syntax-flow" "^7.2.0" - -"@babel/plugin-transform-for-of@^7.2.0": - version "7.18.8" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.18.8.tgz#6ef8a50b244eb6a0bdbad0c7c61877e4e30097c1" - integrity sha512-yEfTRnjuskWYo0k1mHUqrVWaZwrdq8AYbfrpqULOJOaucGSp4mNMVps+YtA8byoevxS/urwU75vyhQIxcCgiBQ== - dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - -"@babel/plugin-transform-function-name@^7.2.0": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.18.9.tgz#cc354f8234e62968946c61a46d6365440fc764e0" - integrity sha512-WvIBoRPaJQ5yVHzcnJFor7oS5Ls0PYixlTYE63lCj2RtdQEl15M68FXQlxnG6wdraJIXRdR7KI+hQ7q/9QjrCQ== - dependencies: - "@babel/helper-compilation-targets" "^7.18.9" - "@babel/helper-function-name" "^7.18.9" - "@babel/helper-plugin-utils" "^7.18.9" - -"@babel/plugin-transform-literals@^7.2.0": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.18.9.tgz#72796fdbef80e56fba3c6a699d54f0de557444bc" - integrity sha512-IFQDSRoTPnrAIrI5zoZv73IFeZu2dhu6irxQjY9rNjTT53VmKg9fenjvoiOWOkJ6mm4jKVPtdMzBY98Fp4Z4cg== - dependencies: - "@babel/helper-plugin-utils" "^7.18.9" - -"@babel/plugin-transform-modules-amd@^7.2.0": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.18.6.tgz#8c91f8c5115d2202f277549848874027d7172d21" - integrity sha512-Pra5aXsmTsOnjM3IajS8rTaLCy++nGM4v3YR4esk5PCsyg9z8NA5oQLwxzMUtDBd8F+UmVza3VxoAaWCbzH1rg== - dependencies: - "@babel/helper-module-transforms" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.6" - babel-plugin-dynamic-import-node "^2.3.3" - -"@babel/plugin-transform-modules-commonjs@^7.0.0 <7.4.0", "@babel/plugin-transform-modules-commonjs@^7.2.0": - version "7.2.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.2.0.tgz#c4f1933f5991d5145e9cfad1dfd848ea1727f404" - integrity sha512-V6y0uaUQrQPXUrmj+hgnks8va2L0zcZymeU7TtWEgdRLNkceafKXEduv7QzgQAE4lT+suwooG9dC7LFhdRAbVQ== - dependencies: - "@babel/helper-module-transforms" "^7.1.0" - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/helper-simple-access" "^7.1.0" - -"@babel/plugin-transform-modules-systemjs@^7.3.4": - version "7.19.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.19.0.tgz#5f20b471284430f02d9c5059d9b9a16d4b085a1f" - integrity sha512-x9aiR0WXAWmOWsqcsnrzGR+ieaTMVyGyffPVA7F8cXAGt/UxefYv6uSHZLkAFChN5M5Iy1+wjE+xJuPt22H39A== - dependencies: - "@babel/helper-hoist-variables" "^7.18.6" - "@babel/helper-module-transforms" "^7.19.0" - "@babel/helper-plugin-utils" "^7.19.0" - "@babel/helper-validator-identifier" "^7.18.6" - babel-plugin-dynamic-import-node "^2.3.3" - -"@babel/plugin-transform-modules-umd@^7.2.0": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.18.6.tgz#81d3832d6034b75b54e62821ba58f28ed0aab4b9" - integrity sha512-dcegErExVeXcRqNtkRU/z8WlBLnvD4MRnHgNs3MytRO1Mn1sHRyhbcpYbVMGclAqOjdW+9cfkdZno9dFdfKLfQ== - dependencies: - "@babel/helper-module-transforms" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.6" - -"@babel/plugin-transform-named-capturing-groups-regex@^7.3.0": - version "7.19.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.19.1.tgz#ec7455bab6cd8fb05c525a94876f435a48128888" - integrity sha512-oWk9l9WItWBQYS4FgXD4Uyy5kq898lvkXpXQxoJEY1RnvPk4R/Dvu2ebXU9q8lP+rlMwUQTFf2Ok6d78ODa0kw== - dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.19.0" - "@babel/helper-plugin-utils" "^7.19.0" - -"@babel/plugin-transform-new-target@^7.0.0": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.18.6.tgz#d128f376ae200477f37c4ddfcc722a8a1b3246a8" - integrity sha512-DjwFA/9Iu3Z+vrAn+8pBUGcjhxKguSMlsFqeCKbhb9BAV756v0krzVK04CRDi/4aqmk8BsHb4a/gFcaA5joXRw== - dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - -"@babel/plugin-transform-object-super@^7.2.0": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.18.6.tgz#fb3c6ccdd15939b6ff7939944b51971ddc35912c" - integrity sha512-uvGz6zk+pZoS1aTZrOvrbj6Pp/kK2mp45t2B+bTDre2UgsZZ8EZLSJtUg7m/no0zOJUWgFONpB7Zv9W2tSaFlA== - dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - "@babel/helper-replace-supers" "^7.18.6" - -"@babel/plugin-transform-parameters@^7.18.8", "@babel/plugin-transform-parameters@^7.2.0": - version "7.18.8" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.18.8.tgz#ee9f1a0ce6d78af58d0956a9378ea3427cccb48a" - integrity sha512-ivfbE3X2Ss+Fj8nnXvKJS6sjRG4gzwPMsP+taZC+ZzEGjAYlvENixmt1sZ5Ca6tWls+BlKSGKPJ6OOXvXCbkFg== - dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - -"@babel/plugin-transform-react-jsx@^7.0.0 <7.4.0": - version "7.3.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.3.0.tgz#f2cab99026631c767e2745a5368b331cfe8f5290" - integrity sha512-a/+aRb7R06WcKvQLOu4/TpjKOdvVEKRLWFpKcNuHhiREPgGRB4TQJxq07+EZLS8LFVYpfq1a5lDUnuMdcCpBKg== - dependencies: - "@babel/helper-builder-react-jsx" "^7.3.0" - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/plugin-syntax-jsx" "^7.2.0" - -"@babel/plugin-transform-regenerator@^7.3.4": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.18.6.tgz#585c66cb84d4b4bf72519a34cfce761b8676ca73" - integrity sha512-poqRI2+qiSdeldcz4wTSTXBRryoq3Gc70ye7m7UD5Ww0nE29IXqMl6r7Nd15WBgRd74vloEMlShtH6CKxVzfmQ== - dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - regenerator-transform "^0.15.0" - -"@babel/plugin-transform-shorthand-properties@^7.2.0": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.18.6.tgz#6d6df7983d67b195289be24909e3f12a8f664dc9" - integrity sha512-eCLXXJqv8okzg86ywZJbRn19YJHU4XUa55oz2wbHhaQVn/MM+XhukiT7SYqp/7o00dg52Rj51Ny+Ecw4oyoygw== - dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - -"@babel/plugin-transform-spread@^7.2.0": - version "7.19.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.19.0.tgz#dd60b4620c2fec806d60cfaae364ec2188d593b6" - integrity sha512-RsuMk7j6n+r752EtzyScnWkQyuJdli6LdO5Klv8Yx0OfPVTcQkIUfS8clx5e9yHXzlnhOZF3CbQ8C2uP5j074w== - dependencies: - "@babel/helper-plugin-utils" "^7.19.0" - "@babel/helper-skip-transparent-expression-wrappers" "^7.18.9" - -"@babel/plugin-transform-sticky-regex@^7.2.0": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.18.6.tgz#c6706eb2b1524028e317720339583ad0f444adcc" - integrity sha512-kfiDrDQ+PBsQDO85yj1icueWMfGfJFKN1KCkndygtu/C9+XUfydLC8Iv5UYJqRwy4zk8EcplRxEOeLyjq1gm6Q== - dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - -"@babel/plugin-transform-template-literals@^7.2.0": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.18.9.tgz#04ec6f10acdaa81846689d63fae117dd9c243a5e" - integrity sha512-S8cOWfT82gTezpYOiVaGHrCbhlHgKhQt8XH5ES46P2XWmX92yisoZywf5km75wv5sYcXDUCLMmMxOLCtthDgMA== - dependencies: - "@babel/helper-plugin-utils" "^7.18.9" - -"@babel/plugin-transform-typeof-symbol@^7.2.0": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.18.9.tgz#c8cea68263e45addcd6afc9091429f80925762c0" - integrity sha512-SRfwTtF11G2aemAZWivL7PD+C9z52v9EvMqH9BuYbabyPuKUvSWks3oCg6041pT925L4zVFqaVBeECwsmlguEw== - dependencies: - "@babel/helper-plugin-utils" "^7.18.9" - -"@babel/plugin-transform-unicode-regex@^7.2.0": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.18.6.tgz#194317225d8c201bbae103364ffe9e2cea36cdca" - integrity sha512-gE7A6Lt7YLnNOL3Pb9BNeZvi+d8l7tcRrG4+pwJjK9hD2xX4mEvjlQW60G9EEmfXVYRPv9VRQcyegIVHCql/AA== - dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.6" - -"@babel/preset-env@^7.0.0 <7.4.0": - version "7.3.4" - resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.3.4.tgz#887cf38b6d23c82f19b5135298bdb160062e33e1" - integrity sha512-2mwqfYMK8weA0g0uBKOt4FE3iEodiHy9/CW0b+nWXcbL+pGzLx8ESYc+j9IIxr6LTDHWKgPm71i9smo02bw+gA== - dependencies: - "@babel/helper-module-imports" "^7.0.0" - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/plugin-proposal-async-generator-functions" "^7.2.0" - "@babel/plugin-proposal-json-strings" "^7.2.0" - "@babel/plugin-proposal-object-rest-spread" "^7.3.4" - "@babel/plugin-proposal-optional-catch-binding" "^7.2.0" - "@babel/plugin-proposal-unicode-property-regex" "^7.2.0" - "@babel/plugin-syntax-async-generators" "^7.2.0" - "@babel/plugin-syntax-json-strings" "^7.2.0" - "@babel/plugin-syntax-object-rest-spread" "^7.2.0" - "@babel/plugin-syntax-optional-catch-binding" "^7.2.0" - "@babel/plugin-transform-arrow-functions" "^7.2.0" - "@babel/plugin-transform-async-to-generator" "^7.3.4" - "@babel/plugin-transform-block-scoped-functions" "^7.2.0" - "@babel/plugin-transform-block-scoping" "^7.3.4" - "@babel/plugin-transform-classes" "^7.3.4" - "@babel/plugin-transform-computed-properties" "^7.2.0" - "@babel/plugin-transform-destructuring" "^7.2.0" - "@babel/plugin-transform-dotall-regex" "^7.2.0" - "@babel/plugin-transform-duplicate-keys" "^7.2.0" - "@babel/plugin-transform-exponentiation-operator" "^7.2.0" - "@babel/plugin-transform-for-of" "^7.2.0" - "@babel/plugin-transform-function-name" "^7.2.0" - "@babel/plugin-transform-literals" "^7.2.0" - "@babel/plugin-transform-modules-amd" "^7.2.0" - "@babel/plugin-transform-modules-commonjs" "^7.2.0" - "@babel/plugin-transform-modules-systemjs" "^7.3.4" - "@babel/plugin-transform-modules-umd" "^7.2.0" - "@babel/plugin-transform-named-capturing-groups-regex" "^7.3.0" - "@babel/plugin-transform-new-target" "^7.0.0" - "@babel/plugin-transform-object-super" "^7.2.0" - "@babel/plugin-transform-parameters" "^7.2.0" - "@babel/plugin-transform-regenerator" "^7.3.4" - "@babel/plugin-transform-shorthand-properties" "^7.2.0" - "@babel/plugin-transform-spread" "^7.2.0" - "@babel/plugin-transform-sticky-regex" "^7.2.0" - "@babel/plugin-transform-template-literals" "^7.2.0" - "@babel/plugin-transform-typeof-symbol" "^7.2.0" - "@babel/plugin-transform-unicode-regex" "^7.2.0" - browserslist "^4.3.4" - invariant "^2.2.2" - js-levenshtein "^1.1.3" - semver "^5.3.0" - -"@babel/runtime@^7.0.0 <7.4.0": - version "7.3.4" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.3.4.tgz#73d12ba819e365fcf7fd152aed56d6df97d21c83" - integrity sha512-IvfvnMdSaLBateu0jfsYIpZTxAc2cKEXEMiezGGN75QcBcecDUKd3PgLAncT0oOgxKy8dd8hrJKj9MfzgfZd6g== - dependencies: - regenerator-runtime "^0.12.0" - -"@babel/runtime@^7.8.4": - version "7.19.0" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.19.0.tgz#22b11c037b094d27a8a2504ea4dcff00f50e2259" - integrity sha512-eR8Lo9hnDS7tqkO7NsV+mKvCmv5boaXFSZ70DnfhcgiEne8hv9oCEd36Klw74EtizEqLsy4YnW8UWwpBVolHZA== - dependencies: - regenerator-runtime "^0.13.4" - -"@babel/template@^7.0.0 <7.4.0": - version "7.2.2" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.2.2.tgz#005b3fdf0ed96e88041330379e0da9a708eb2907" - integrity sha512-zRL0IMM02AUDwghf5LMSSDEz7sBCO2YnNmpg3uWTZj/v1rcG2BmQUvaGU8GhU8BvfMh1k2KIAYZ7Ji9KXPUg7g== - dependencies: - "@babel/code-frame" "^7.0.0" - "@babel/parser" "^7.2.2" - "@babel/types" "^7.2.2" - -"@babel/template@^7.18.10", "@babel/template@^7.2.2": +"@babel/template@^7.18.10": version "7.18.10" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.18.10.tgz#6f9134835970d1dbf0835c0d100c9f38de0c5e71" integrity sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA== @@ -765,22 +170,23 @@ "@babel/parser" "^7.22.15" "@babel/types" "^7.22.15" -"@babel/traverse@^7.0.0 <7.4.0": - version "7.3.4" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.3.4.tgz#1330aab72234f8dea091b08c4f8b9d05c7119e06" - integrity sha512-TvTHKp6471OYEcE/91uWmhR6PrrYywQntCHSaZ8CM8Vmp+pjAusal4nGB2WCCQd0rvI7nOMKn9GnbcvTUz3/ZQ== +"@babel/traverse@^7.23.6": + version "7.23.7" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.23.7.tgz#9a7bf285c928cb99b5ead19c3b1ce5b310c9c305" + integrity sha512-tY3mM8rH9jM0YHFGyfC0/xf+SB5eKUu7HPj7/k3fpi9dAlsMc5YbQvDi0Sh2QTPXqMhyaAtzAr807TIyfQrmyg== dependencies: - "@babel/code-frame" "^7.0.0" - "@babel/generator" "^7.3.4" - "@babel/helper-function-name" "^7.1.0" - "@babel/helper-split-export-declaration" "^7.0.0" - "@babel/parser" "^7.3.4" - "@babel/types" "^7.3.4" - debug "^4.1.0" + "@babel/code-frame" "^7.23.5" + "@babel/generator" "^7.23.6" + "@babel/helper-environment-visitor" "^7.22.20" + "@babel/helper-function-name" "^7.23.0" + "@babel/helper-hoist-variables" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.22.6" + "@babel/parser" "^7.23.6" + "@babel/types" "^7.23.6" + debug "^4.3.1" globals "^11.1.0" - lodash "^4.17.11" -"@babel/traverse@^7.19.0", "@babel/traverse@^7.19.1", "@babel/traverse@^7.20.1", "@babel/traverse@^7.3.4", "@babel/traverse@^7.7.0": +"@babel/traverse@^7.7.0": version "7.20.1" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.20.1.tgz#9b15ccbf882f6d107eeeecf263fbcdd208777ec8" integrity sha512-d3tN8fkVJwFLkHkBN479SOsw4DMZnz8cdbL/gvuDuzy3TS6Nfw80HuQqhw1pITbIruHyh7d1fMA47kWzmcUEGA== @@ -796,22 +202,6 @@ debug "^4.1.0" globals "^11.1.0" -"@babel/traverse@^7.23.6": - version "7.23.7" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.23.7.tgz#9a7bf285c928cb99b5ead19c3b1ce5b310c9c305" - integrity sha512-tY3mM8rH9jM0YHFGyfC0/xf+SB5eKUu7HPj7/k3fpi9dAlsMc5YbQvDi0Sh2QTPXqMhyaAtzAr807TIyfQrmyg== - dependencies: - "@babel/code-frame" "^7.23.5" - "@babel/generator" "^7.23.6" - "@babel/helper-environment-visitor" "^7.22.20" - "@babel/helper-function-name" "^7.23.0" - "@babel/helper-hoist-variables" "^7.22.5" - "@babel/helper-split-export-declaration" "^7.22.6" - "@babel/parser" "^7.23.6" - "@babel/types" "^7.23.6" - debug "^4.3.1" - globals "^11.1.0" - "@babel/types@^7.0.0", "@babel/types@^7.20.7", "@babel/types@^7.22.15", "@babel/types@^7.22.5", "@babel/types@^7.23.0", "@babel/types@^7.23.6": version "7.23.6" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.23.6.tgz#be33fdb151e1f5a56877d704492c240fc71c7ccd" @@ -821,16 +211,7 @@ "@babel/helper-validator-identifier" "^7.22.20" to-fast-properties "^2.0.0" -"@babel/types@^7.0.0 <7.4.0": - version "7.3.4" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.3.4.tgz#bf482eaeaffb367a28abbf9357a94963235d90ed" - integrity sha512-WEkp8MsLftM7O/ty580wAmZzN1nDmCACc5+jFzUt+GUFNNIi3LdRlueYz0YIlmJhlZx1QYDMZL5vdWCL0fNjFQ== - dependencies: - esutils "^2.0.2" - lodash "^4.17.11" - to-fast-properties "^2.0.0" - -"@babel/types@^7.17.0", "@babel/types@^7.18.10", "@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.19.0", "@babel/types@^7.2.2", "@babel/types@^7.20.0", "@babel/types@^7.20.2", "@babel/types@^7.3.4", "@babel/types@^7.7.0": +"@babel/types@^7.17.0", "@babel/types@^7.18.10", "@babel/types@^7.18.6", "@babel/types@^7.19.0", "@babel/types@^7.20.0", "@babel/types@^7.20.2", "@babel/types@^7.7.0": version "7.20.2" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.20.2.tgz#67ac09266606190f496322dbaff360fdaa5e7842" integrity sha512-FnnvsNWgZCr232sqtXggapvlkk/tuwR/qhGzcmxI0GXLCjmPYQPzio2FbdlWuY6y1sHFfQKk+rRbUZ9VStQMog== @@ -921,11 +302,6 @@ resolved "https://registry.yarnpkg.com/@hutson/parse-repository-url/-/parse-repository-url-3.0.2.tgz#98c23c950a3d9b6c8f0daed06da6c3af06981340" integrity sha512-H9XAx3hc0BQHY6l+IFSWHDySypcXsvsuLhgYLUGywmJ5pswRVQJUHpOsobnLYp2ZUaUlKiKDrgWWhosOwAEM8Q== -"@iarna/toml@^2.2.0": - version "2.2.5" - resolved "https://registry.yarnpkg.com/@iarna/toml/-/toml-2.2.5.tgz#b32366c89b43c6f8cefbdefac778b9c828e3ba8c" - integrity sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg== - "@isaacs/string-locale-compare@^1.1.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@isaacs/string-locale-compare/-/string-locale-compare-1.1.0.tgz#291c227e93fd407a96ecd59879a35809120e432b" @@ -2062,31 +1438,6 @@ dependencies: "@octokit/openapi-types" "^14.0.0" -"@parcel/fs@^1.11.0": - version "1.11.0" - resolved "https://registry.yarnpkg.com/@parcel/fs/-/fs-1.11.0.tgz#fb8a2be038c454ad46a50dc0554c1805f13535cd" - integrity sha512-86RyEqULbbVoeo8OLcv+LQ1Vq2PKBAvWTU9fCgALxuCTbbs5Ppcvll4Vr+Ko1AnmMzja/k++SzNAwJfeQXVlpA== - dependencies: - "@parcel/utils" "^1.11.0" - mkdirp "^0.5.1" - rimraf "^2.6.2" - -"@parcel/logger@^1.11.0": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@parcel/logger/-/logger-1.11.1.tgz#c55b0744bcbe84ebc291155627f0ec406a23e2e6" - integrity sha512-9NF3M6UVeP2udOBDILuoEHd8VrF4vQqoWHEafymO1pfSoOMfxrSJZw1MfyAAIUN/IFp9qjcpDCUbDZB+ioVevA== - dependencies: - "@parcel/workers" "^1.11.0" - chalk "^2.1.0" - grapheme-breaker "^0.3.2" - ora "^2.1.0" - strip-ansi "^4.0.0" - -"@parcel/utils@^1.11.0": - version "1.11.0" - resolved "https://registry.yarnpkg.com/@parcel/utils/-/utils-1.11.0.tgz#539e08fff8af3b26eca11302be80b522674b51ea" - integrity sha512-cA3p4jTlaMeOtAKR/6AadanOPvKeg8VwgnHhOyfi0yClD0TZS/hi9xu12w4EzA/8NtHu0g6o4RDfcNjqN8l1AQ== - "@parcel/watcher@2.0.4": version "2.0.4" resolved "https://registry.yarnpkg.com/@parcel/watcher/-/watcher-2.0.4.tgz#f300fef4cc38008ff4b8c29d92588eced3ce014b" @@ -2095,22 +1446,6 @@ node-addon-api "^3.2.1" node-gyp-build "^4.3.0" -"@parcel/watcher@^1.12.0": - version "1.12.1" - resolved "https://registry.yarnpkg.com/@parcel/watcher/-/watcher-1.12.1.tgz#b98b3df309fcab93451b5583fc38e40826696dad" - integrity sha512-od+uCtCxC/KoNQAIE1vWx1YTyKYY+7CTrxBJPRh3cDWw/C0tCtlBMVlrbplscGoEpt6B27KhJDCv82PBxOERNA== - dependencies: - "@parcel/utils" "^1.11.0" - chokidar "^2.1.5" - -"@parcel/workers@^1.11.0": - version "1.11.0" - resolved "https://registry.yarnpkg.com/@parcel/workers/-/workers-1.11.0.tgz#7b8dcf992806f4ad2b6cecf629839c41c2336c59" - integrity sha512-USSjRAAQYsZFlv43FUPdD+jEGML5/8oLF0rUzPQTtK4q9kvaXr49F5ZplyLz5lox78cLZ0TxN2bIDQ1xhOkulQ== - dependencies: - "@parcel/utils" "^1.11.0" - physical-cpu-count "^2.0.0" - "@phenomnomnominal/tsquery@4.1.1": version "4.1.1" resolved "https://registry.yarnpkg.com/@phenomnomnominal/tsquery/-/tsquery-4.1.1.tgz#42971b83590e9d853d024ddb04a18085a36518df" @@ -2118,19 +1453,6 @@ dependencies: esquery "^1.0.1" -"@rollup/plugin-commonjs@^13.0.0": - version "13.0.2" - resolved "https://registry.yarnpkg.com/@rollup/plugin-commonjs/-/plugin-commonjs-13.0.2.tgz#b7783f0db049450c72d60bc06cf48d4951515e58" - integrity sha512-9JXf2k8xqvMYfqmhgtB6eCgMN9fbxwF1XDF3mGKJc6pkAmt0jnsqurxQ0tC1akQKNSXCm7c3unQxa3zuxtZ7mQ== - dependencies: - "@rollup/pluginutils" "^3.0.8" - commondir "^1.0.1" - estree-walker "^1.0.1" - glob "^7.1.2" - is-reference "^1.1.2" - magic-string "^0.25.2" - resolve "^1.11.0" - "@rollup/plugin-commonjs@^19.0.0": version "19.0.2" resolved "https://registry.yarnpkg.com/@rollup/plugin-commonjs/-/plugin-commonjs-19.0.2.tgz#1ccc3d63878d1bc9846f8969f09dd3b3e4ecc244" @@ -2156,18 +1478,7 @@ is-module "^1.0.0" resolve "^1.19.0" -"@rollup/plugin-node-resolve@^6.1.0": - version "6.1.0" - resolved "https://registry.yarnpkg.com/@rollup/plugin-node-resolve/-/plugin-node-resolve-6.1.0.tgz#0d2909f4bf606ae34d43a9bc8be06a9b0c850cf0" - integrity sha512-Cv7PDIvxdE40SWilY5WgZpqfIUEaDxFxs89zCAHjqyRwlTSuql4M5hjIuc5QYJkOH0/vyiyNXKD72O+LhRipGA== - dependencies: - "@rollup/pluginutils" "^3.0.0" - "@types/resolve" "0.0.8" - builtin-modules "^3.1.0" - is-module "^1.0.0" - resolve "^1.11.1" - -"@rollup/pluginutils@^3.0.0", "@rollup/pluginutils@^3.0.8", "@rollup/pluginutils@^3.1.0": +"@rollup/pluginutils@^3.1.0": version "3.1.0" resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-3.1.0.tgz#706b4524ee6dc8b103b3c995533e5ad680c02b9b" integrity sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg== @@ -2298,18 +1609,6 @@ resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA== -"@types/q@^1.5.1": - version "1.5.5" - resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.5.tgz#75a2a8e7d8ab4b230414505d92335d1dcb53a6df" - integrity sha512-L28j2FcJfSZOnL1WBjDYp2vUHCeIFlyYI/53EwD/rKUBQ7MtUUfbQWiyKJGpcnv4/WgrhWsFKrcPstcAt/J0tQ== - -"@types/resolve@0.0.8": - version "0.0.8" - resolved "https://registry.yarnpkg.com/@types/resolve/-/resolve-0.0.8.tgz#f26074d238e02659e323ce1a13d041eee280e194" - integrity sha512-auApPaJf3NPfe18hSoJkp8EbZzer2ISk7o8mCC3M9he/a04+gbMF97NkpD2S8riMGvm4BMRI59/SZQSaLTKpsQ== - dependencies: - "@types/node" "*" - "@types/resolve@1.17.1": version "1.17.1" resolved "https://registry.yarnpkg.com/@types/resolve/-/resolve-1.17.1.tgz#3afd6ad8967c77e4376c598a82ddd58f46ec45d6" @@ -2408,161 +1707,6 @@ "@typescript-eslint/types" "6.19.1" eslint-visitor-keys "^3.4.1" -"@webassemblyjs/ast@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.9.0.tgz#bd850604b4042459a5a41cd7d338cbed695ed964" - integrity sha512-C6wW5L+b7ogSDVqymbkkvuW9kruN//YisMED04xzeBBqjHa2FYnmvOlS6Xj68xWQRgWvI9cIglsjFowH/RJyEA== - dependencies: - "@webassemblyjs/helper-module-context" "1.9.0" - "@webassemblyjs/helper-wasm-bytecode" "1.9.0" - "@webassemblyjs/wast-parser" "1.9.0" - -"@webassemblyjs/floating-point-hex-parser@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.9.0.tgz#3c3d3b271bddfc84deb00f71344438311d52ffb4" - integrity sha512-TG5qcFsS8QB4g4MhrxK5TqfdNe7Ey/7YL/xN+36rRjl/BlGE/NcBvJcqsRgCP6Z92mRE+7N50pRIi8SmKUbcQA== - -"@webassemblyjs/helper-api-error@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.9.0.tgz#203f676e333b96c9da2eeab3ccef33c45928b6a2" - integrity sha512-NcMLjoFMXpsASZFxJ5h2HZRcEhDkvnNFOAKneP5RbKRzaWJN36NC4jqQHKwStIhGXu5mUWlUUk7ygdtrO8lbmw== - -"@webassemblyjs/helper-buffer@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.9.0.tgz#a1442d269c5feb23fcbc9ef759dac3547f29de00" - integrity sha512-qZol43oqhq6yBPx7YM3m9Bv7WMV9Eevj6kMi6InKOuZxhw+q9hOkvq5e/PpKSiLfyetpaBnogSbNCfBwyB00CA== - -"@webassemblyjs/helper-code-frame@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.9.0.tgz#647f8892cd2043a82ac0c8c5e75c36f1d9159f27" - integrity sha512-ERCYdJBkD9Vu4vtjUYe8LZruWuNIToYq/ME22igL+2vj2dQ2OOujIZr3MEFvfEaqKoVqpsFKAGsRdBSBjrIvZA== - dependencies: - "@webassemblyjs/wast-printer" "1.9.0" - -"@webassemblyjs/helper-fsm@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-fsm/-/helper-fsm-1.9.0.tgz#c05256b71244214671f4b08ec108ad63b70eddb8" - integrity sha512-OPRowhGbshCb5PxJ8LocpdX9Kl0uB4XsAjl6jH/dWKlk/mzsANvhwbiULsaiqT5GZGT9qinTICdj6PLuM5gslw== - -"@webassemblyjs/helper-module-context@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-module-context/-/helper-module-context-1.9.0.tgz#25d8884b76839871a08a6c6f806c3979ef712f07" - integrity sha512-MJCW8iGC08tMk2enck1aPW+BE5Cw8/7ph/VGZxwyvGbJwjktKkDK7vy7gAmMDx88D7mhDTCNKAW5tED+gZ0W8g== - dependencies: - "@webassemblyjs/ast" "1.9.0" - -"@webassemblyjs/helper-wasm-bytecode@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.9.0.tgz#4fed8beac9b8c14f8c58b70d124d549dd1fe5790" - integrity sha512-R7FStIzyNcd7xKxCZH5lE0Bqy+hGTwS3LJjuv1ZVxd9O7eHCedSdrId/hMOd20I+v8wDXEn+bjfKDLzTepoaUw== - -"@webassemblyjs/helper-wasm-section@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.9.0.tgz#5a4138d5a6292ba18b04c5ae49717e4167965346" - integrity sha512-XnMB8l3ek4tvrKUUku+IVaXNHz2YsJyOOmz+MMkZvh8h1uSJpSen6vYnw3IoQ7WwEuAhL8Efjms1ZWjqh2agvw== - dependencies: - "@webassemblyjs/ast" "1.9.0" - "@webassemblyjs/helper-buffer" "1.9.0" - "@webassemblyjs/helper-wasm-bytecode" "1.9.0" - "@webassemblyjs/wasm-gen" "1.9.0" - -"@webassemblyjs/ieee754@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.9.0.tgz#15c7a0fbaae83fb26143bbacf6d6df1702ad39e4" - integrity sha512-dcX8JuYU/gvymzIHc9DgxTzUUTLexWwt8uCTWP3otys596io0L5aW02Gb1RjYpx2+0Jus1h4ZFqjla7umFniTg== - dependencies: - "@xtuc/ieee754" "^1.2.0" - -"@webassemblyjs/leb128@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.9.0.tgz#f19ca0b76a6dc55623a09cffa769e838fa1e1c95" - integrity sha512-ENVzM5VwV1ojs9jam6vPys97B/S65YQtv/aanqnU7D8aSoHFX8GyhGg0CMfyKNIHBuAVjy3tlzd5QMMINa7wpw== - dependencies: - "@xtuc/long" "4.2.2" - -"@webassemblyjs/utf8@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.9.0.tgz#04d33b636f78e6a6813227e82402f7637b6229ab" - integrity sha512-GZbQlWtopBTP0u7cHrEx+73yZKrQoBMpwkGEIqlacljhXCkVM1kMQge/Mf+csMJAjEdSwhOyLAS0AoR3AG5P8w== - -"@webassemblyjs/wasm-edit@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.9.0.tgz#3fe6d79d3f0f922183aa86002c42dd256cfee9cf" - integrity sha512-FgHzBm80uwz5M8WKnMTn6j/sVbqilPdQXTWraSjBwFXSYGirpkSWE2R9Qvz9tNiTKQvoKILpCuTjBKzOIm0nxw== - dependencies: - "@webassemblyjs/ast" "1.9.0" - "@webassemblyjs/helper-buffer" "1.9.0" - "@webassemblyjs/helper-wasm-bytecode" "1.9.0" - "@webassemblyjs/helper-wasm-section" "1.9.0" - "@webassemblyjs/wasm-gen" "1.9.0" - "@webassemblyjs/wasm-opt" "1.9.0" - "@webassemblyjs/wasm-parser" "1.9.0" - "@webassemblyjs/wast-printer" "1.9.0" - -"@webassemblyjs/wasm-gen@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.9.0.tgz#50bc70ec68ded8e2763b01a1418bf43491a7a49c" - integrity sha512-cPE3o44YzOOHvlsb4+E9qSqjc9Qf9Na1OO/BHFy4OI91XDE14MjFN4lTMezzaIWdPqHnsTodGGNP+iRSYfGkjA== - dependencies: - "@webassemblyjs/ast" "1.9.0" - "@webassemblyjs/helper-wasm-bytecode" "1.9.0" - "@webassemblyjs/ieee754" "1.9.0" - "@webassemblyjs/leb128" "1.9.0" - "@webassemblyjs/utf8" "1.9.0" - -"@webassemblyjs/wasm-opt@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.9.0.tgz#2211181e5b31326443cc8112eb9f0b9028721a61" - integrity sha512-Qkjgm6Anhm+OMbIL0iokO7meajkzQD71ioelnfPEj6r4eOFuqm4YC3VBPqXjFyyNwowzbMD+hizmprP/Fwkl2A== - dependencies: - "@webassemblyjs/ast" "1.9.0" - "@webassemblyjs/helper-buffer" "1.9.0" - "@webassemblyjs/wasm-gen" "1.9.0" - "@webassemblyjs/wasm-parser" "1.9.0" - -"@webassemblyjs/wasm-parser@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.9.0.tgz#9d48e44826df4a6598294aa6c87469d642fff65e" - integrity sha512-9+wkMowR2AmdSWQzsPEjFU7njh8HTO5MqO8vjwEHuM+AMHioNqSBONRdr0NQQ3dVQrzp0s8lTcYqzUdb7YgELA== - dependencies: - "@webassemblyjs/ast" "1.9.0" - "@webassemblyjs/helper-api-error" "1.9.0" - "@webassemblyjs/helper-wasm-bytecode" "1.9.0" - "@webassemblyjs/ieee754" "1.9.0" - "@webassemblyjs/leb128" "1.9.0" - "@webassemblyjs/utf8" "1.9.0" - -"@webassemblyjs/wast-parser@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-parser/-/wast-parser-1.9.0.tgz#3031115d79ac5bd261556cecc3fa90a3ef451914" - integrity sha512-qsqSAP3QQ3LyZjNC/0jBJ/ToSxfYJ8kYyuiGvtn/8MK89VrNEfwj7BPQzJVHi0jGTRK2dGdJ5PRqhtjzoww+bw== - dependencies: - "@webassemblyjs/ast" "1.9.0" - "@webassemblyjs/floating-point-hex-parser" "1.9.0" - "@webassemblyjs/helper-api-error" "1.9.0" - "@webassemblyjs/helper-code-frame" "1.9.0" - "@webassemblyjs/helper-fsm" "1.9.0" - "@xtuc/long" "4.2.2" - -"@webassemblyjs/wast-printer@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.9.0.tgz#4935d54c85fef637b00ce9f52377451d00d47899" - integrity sha512-2J0nE95rHXHyQ24cWjMKJ1tqB/ds8z/cyeOZxJhcb+rW+SQASVjuznUSmdz5GpVJTzU8JkhYut0D3siFDD6wsA== - dependencies: - "@webassemblyjs/ast" "1.9.0" - "@webassemblyjs/wast-parser" "1.9.0" - "@xtuc/long" "4.2.2" - -"@xtuc/ieee754@^1.2.0": - version "1.2.0" - resolved "https://registry.yarnpkg.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz#eef014a3145ae477a1cbc00cd1e552336dceb790" - integrity sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA== - -"@xtuc/long@4.2.2": - version "4.2.2" - resolved "https://registry.yarnpkg.com/@xtuc/long/-/long-4.2.2.tgz#d291c6a4e97989b5c61d9acf396ae4fe133a718d" - integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ== - "@yarnpkg/lockfile@^1.1.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz#e77a97fbd345b76d83245edcd17d393b1b41fb31" @@ -2583,7 +1727,7 @@ dependencies: argparse "^2.0.1" -JSONStream@^1.0.3, JSONStream@^1.0.4: +JSONStream@^1.0.4: version "1.3.5" resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-1.3.5.tgz#3208c1f08d3a4d99261ab64f92302bc15e111ca0" integrity sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ== @@ -2591,63 +1735,29 @@ JSONStream@^1.0.3, JSONStream@^1.0.4: jsonparse "^1.2.0" through ">=2.2.7 <3" -abab@^2.0.0: - version "2.0.6" - resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.6.tgz#41b80f2c871d19686216b82309231cfd3cb3d291" - integrity sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA== - abbrev@1, abbrev@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== -acorn-globals@^4.3.0: - version "4.3.4" - resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-4.3.4.tgz#9fa1926addc11c97308c4e66d7add0d40c3272e7" - integrity sha512-clfQEh21R+D0leSbUdWf3OcfqyaCSAQ8Ryq00bofSekfr9W8u1jyYZo6ir0xu9Gtcf7BjcHJpnbZH7JOCpP60A== +accepts@~1.3.4: + version "1.3.8" + resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e" + integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw== dependencies: - acorn "^6.0.1" - acorn-walk "^6.0.1" + mime-types "~2.1.34" + negotiator "0.6.3" acorn-jsx@^5.3.2: version "5.3.2" resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== -acorn-node@^1.2.0, acorn-node@^1.3.0, acorn-node@^1.5.2, acorn-node@^1.8.2: - version "1.8.2" - resolved "https://registry.yarnpkg.com/acorn-node/-/acorn-node-1.8.2.tgz#114c95d64539e53dede23de8b9d96df7c7ae2af8" - integrity sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A== - dependencies: - acorn "^7.0.0" - acorn-walk "^7.0.0" - xtend "^4.0.2" - -acorn-walk@^6.0.1: - version "6.2.0" - resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-6.2.0.tgz#123cb8f3b84c2171f1f7fb252615b1c78a6b1a8c" - integrity sha512-7evsyfH1cLOCdAzZAd43Cic04yKydNx0cF+7tiA19p1XnLLPU4dpCQOqpjqwokFe//vS0QqfqqjCS2JkiIs0cA== - -acorn-walk@^7.0.0: - version "7.2.0" - resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc" - integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA== - acorn-walk@^8.2.0: version "8.2.0" resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1" integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA== -acorn@^6.0.1, acorn@^6.0.4, acorn@^6.4.1: - version "6.4.2" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.2.tgz#35866fd710528e92de10cf06016498e47e39e1e6" - integrity sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ== - -acorn@^7.0.0, acorn@^7.1.0, acorn@^7.1.1: - version "7.4.1" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" - integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== - acorn@^8.2.4, acorn@^8.5.0, acorn@^8.8.2, acorn@^8.9.0: version "8.10.0" resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.10.0.tgz#8be5b3907a67221a81ab23c7889c4c5526b62ec5" @@ -2658,6 +1768,11 @@ add-stream@^1.0.0: resolved "https://registry.yarnpkg.com/add-stream/-/add-stream-1.0.0.tgz#6a7990437ca736d5e1288db92bd3266d5f5cb2aa" integrity sha512-qQLMr+8o0WC4FZGQTcJiKBVC59JylcPSrTtk6usvmIDFUOCKegapy1VHQwRbFMOFyb/inzUVqHs+eMYKDM1YeQ== +after@0.8.2: + version "0.8.2" + resolved "https://registry.yarnpkg.com/after/-/after-0.8.2.tgz#fedb394f9f0e02aa9768e702bda23b505fae7e1f" + integrity sha512-QbJ0NTQ/I9DI3uSJA4cbexiwQeRAfjPScqIbSjUDd9TOrcg6pTkdgziesOqxBMBzit8vFCTwrP27t13vFOORRA== + agent-base@6, agent-base@^6.0.2: version "6.0.2" resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" @@ -2665,13 +1780,6 @@ agent-base@6, agent-base@^6.0.2: dependencies: debug "4" -agent-base@^4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.3.0.tgz#8165f01c436009bccad0b1d122f05ed770efc6ee" - integrity sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg== - dependencies: - es6-promisify "^5.0.0" - agentkeepalive@^4.2.1: version "4.2.1" resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-4.2.1.tgz#a7975cbb9f83b367f06c90cc51ff28fe7d499717" @@ -2697,17 +1805,7 @@ aggregate-error@^4.0.0: clean-stack "^4.0.0" indent-string "^5.0.0" -ajv-errors@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/ajv-errors/-/ajv-errors-1.0.1.tgz#f35986aceb91afadec4102fbd85014950cefa64d" - integrity sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ== - -ajv-keywords@^3.1.0, ajv-keywords@^3.4.1: - version "3.5.2" - resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d" - integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== - -ajv@^6.1.0, ajv@^6.10.2, ajv@^6.12.3, ajv@^6.12.4: +ajv@^6.12.4: version "6.12.6" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== @@ -2717,11 +1815,6 @@ ajv@^6.1.0, ajv@^6.10.2, ajv@^6.12.3, ajv@^6.12.4: json-schema-traverse "^0.4.1" uri-js "^4.2.2" -alphanum-sort@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/alphanum-sort/-/alphanum-sort-1.0.2.tgz#97a1119649b211ad33691d9f9f486a8ec9fbe0a3" - integrity sha512-0FcBfdcmaumGPQ0qPn7Q5qTgz/ooXgIyp1rf8ik5bGX8mpE2YHjC0P/eyQvxu1GURYQgq9ozf2mteQ5ZD9YiyQ== - ansi-colors@^4.1.1: version "4.1.3" resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.3.tgz#37611340eb2243e70cc604cad35d63270d48781b" @@ -2734,16 +1827,6 @@ ansi-escapes@^4.2.1, ansi-escapes@^4.3.1: dependencies: type-fest "^0.21.3" -ansi-regex@^2.0.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" - integrity sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA== - -ansi-regex@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.1.tgz#123d6479e92ad45ad897d4054e3c7ca7db4944e1" - integrity sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw== - ansi-regex@^4.1.0: version "4.1.1" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.1.tgz#164daac87ab2d6f6db3a29875e2d1766582dabed" @@ -2764,11 +1847,6 @@ ansi-sequence-parser@^1.1.0: resolved "https://registry.yarnpkg.com/ansi-sequence-parser/-/ansi-sequence-parser-1.1.1.tgz#e0aa1cdcbc8f8bb0b5bca625aac41f5f056973cf" integrity sha512-vJXt3yiaUL4UU546s3rPXlsry/RnM730G1+HkpKE012AN0sx1eOrxSu95oKDIonskeLTijMgqWZ3uDEe3NFvyg== -ansi-styles@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" - integrity sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA== - ansi-styles@^3.2.0, ansi-styles@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" @@ -2793,21 +1871,6 @@ ansi-styles@^6.0.0, ansi-styles@^6.2.1: resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5" integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== -ansi-to-html@^0.6.4: - version "0.6.15" - resolved "https://registry.yarnpkg.com/ansi-to-html/-/ansi-to-html-0.6.15.tgz#ac6ad4798a00f6aa045535d7f6a9cb9294eebea7" - integrity sha512-28ijx2aHJGdzbs+O5SNQF65r6rrKYnkuwTYm8lZlChuoJ9P1vVzIpWO20sQTqTPDXYp6NFwk326vApTtLVFXpQ== - dependencies: - entities "^2.0.0" - -anymatch@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb" - integrity sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw== - dependencies: - micromatch "^3.1.4" - normalize-path "^2.1.1" - anymatch@~3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716" @@ -2821,11 +1884,6 @@ anymatch@~3.1.2: resolved "https://registry.yarnpkg.com/aproba/-/aproba-2.0.0.tgz#52520b8ae5b569215b354efc0caa3fe1e45a8adc" integrity sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ== -aproba@^1.1.1: - version "1.2.0" - resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" - integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw== - are-docs-informative@^0.0.2: version "0.0.2" resolved "https://registry.yarnpkg.com/are-docs-informative/-/are-docs-informative-0.0.2.tgz#387f0e93f5d45280373d387a59d34c96db321963" @@ -2879,11 +1937,6 @@ array-differ@^3.0.0: resolved "https://registry.yarnpkg.com/array-differ/-/array-differ-3.0.0.tgz#3cbb3d0f316810eafcc47624734237d6aee4ae6b" integrity sha512-THtfYS6KtME/yIAhKjZ2ul7XI96lQGHRputJQHO80LAWQnuGP4iCIN8vdMRboGbIEYBwU33q8Tch1os2+X0kMg== -array-equal@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/array-equal/-/array-equal-1.0.0.tgz#8c2a5ef2472fd9ea742b04c77a75093ba2757c93" - integrity sha512-H3LU5RLiSsGXPhN+Nipar0iR0IofH+8r89G2y1tBKxQ/agagKyAjhkAFDRBfodP2caPrNKHpAWNIM/c9yeL7uA== - array-find-index@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/array-find-index/-/array-find-index-1.0.2.tgz#df010aa1287e164bbda6f9723b0a96a1ec4187a1" @@ -2927,16 +1980,6 @@ array-unique@^0.3.2: resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" integrity sha512-SleRWjh9JUud2wH1hPs9rZBZ33H6T9HOiL0uwGnGx9FpE6wKGyfWugmbkEOIs6qWrZhg0LWeLziLrEwQJhs5mQ== -array.prototype.every@^1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/array.prototype.every/-/array.prototype.every-1.1.4.tgz#2762daecd9cec87cb63f3ca6be576817074a684e" - integrity sha512-Aui35iRZk1HHLRAyF7QP0KAnOnduaQ6fo6k1NVWfRc0xTs2AZ70ytlXvOmkC6Di4JmUs2Wv3DYzGtCQFSk5uGg== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.20.4" - is-string "^1.0.7" - array.prototype.findlastindex@^1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.3.tgz#b37598438f97b579166940814e2c0493a4f50207" @@ -2968,17 +2011,6 @@ array.prototype.flatmap@^1.3.2: es-abstract "^1.22.1" es-shim-unscopables "^1.0.0" -array.prototype.reduce@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/array.prototype.reduce/-/array.prototype.reduce-1.0.4.tgz#8167e80089f78bff70a99e20bd4201d4663b0a6f" - integrity sha512-WnM+AjG/DvLRLo4DDl+r+SvCzYtD2Jd9oeBYMcEaI7t3fFrHY9M53/wdLcTvmZNQ70IU6Htj0emFkZ5TS+lrdw== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.19.2" - es-array-method-boxes-properly "^1.0.0" - is-string "^1.0.7" - arraybuffer.prototype.slice@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.2.tgz#98bd561953e3e74bb34938e77647179dfe6e9f12" @@ -2992,6 +2024,11 @@ arraybuffer.prototype.slice@^1.0.2: is-array-buffer "^3.0.2" is-shared-array-buffer "^1.0.2" +arraybuffer.slice@~0.0.7: + version "0.0.7" + resolved "https://registry.yarnpkg.com/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz#3bbc4275dd584cc1b10809b89d4e8b63a69e7675" + integrity sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog== + arrgv@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/arrgv/-/arrgv-1.0.2.tgz#025ed55a6a433cad9b604f8112fc4292715a6ec0" @@ -3012,60 +2049,20 @@ arrify@^3.0.0: resolved "https://registry.yarnpkg.com/arrify/-/arrify-3.0.0.tgz#ccdefb8eaf2a1d2ab0da1ca2ce53118759fd46bc" integrity sha512-tLkvA81vQG/XqE2mjDkGQHoOINtMHtysSnemrmoGe6PydDPMRbVugqyk4A6V/WDWEfm3l+0d8anA9r8cv/5Jaw== -asap@^2.0.0: - version "2.0.6" - resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" - integrity sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA== - -asn1.js@^5.2.0: - version "5.4.1" - resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-5.4.1.tgz#11a980b84ebb91781ce35b0fdc2ee294e3783f07" - integrity sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA== - dependencies: - bn.js "^4.0.0" - inherits "^2.0.1" - minimalistic-assert "^1.0.0" - safer-buffer "^2.1.0" - -asn1@~0.2.3: - version "0.2.6" - resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.6.tgz#0d3a7bb6e64e02a90c0303b31f292868ea09a08d" - integrity sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ== - dependencies: - safer-buffer "~2.1.0" - -assert-plus@1.0.0, assert-plus@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" - integrity sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw== - -assert@^1.1.1, assert@^1.4.0: - version "1.5.0" - resolved "https://registry.yarnpkg.com/assert/-/assert-1.5.0.tgz#55c109aaf6e0aefdb3dc4b71240c70bf574b18eb" - integrity sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA== - dependencies: - object-assign "^4.1.1" - util "0.10.3" +asap@^2.0.0: + version "2.0.6" + resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" + integrity sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA== assign-symbols@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" integrity sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw== -async-array-reduce@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/async-array-reduce/-/async-array-reduce-0.2.1.tgz#c8be010a2b5cd00dea96c81116034693dfdd82d1" - integrity sha512-/ywTADOcaEnwiAnOEi0UB/rAcIq5bTFfCV9euv3jLYFUMmy6KvKccTQUnLlp8Ensmfj43wHSmbGiPqjsZ6RhNA== - -async-each@^1.0.1: - version "1.0.3" - resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.3.tgz#b727dbf87d7651602f06f4d4ac387f47d91b0cbf" - integrity sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ== - -async-limiter@~1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd" - integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ== +ast-types@0.12.4: + version "0.12.4" + resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.12.4.tgz#71ce6383800f24efc9a1a3308f3a6e420a0974d1" + integrity sha512-ky/YVYCbtVAS8TdMIaTiPFHwEpRB5z1hctepJplTr3UW5q8TDrpIMCILyk8pmLxGtn2KCtC/lSn7zOsaI7nzDw== async@^3.2.3: version "3.2.4" @@ -3141,16 +2138,6 @@ available-typed-arrays@^1.0.5: resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz#92f95616501069d07d10edb2fc37d3e1c65123b7" integrity sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw== -aws-sign2@~0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" - integrity sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA== - -aws4@^1.8.0: - version "1.11.0" - resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.11.0.tgz#d61f46d83b2519250e2784daf5b09479a8b41c59" - integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA== - axios@^1.0.0: version "1.2.1" resolved "https://registry.yarnpkg.com/axios/-/axios-1.2.1.tgz#44cf04a3c9f0c2252ebd85975361c026cb9f864a" @@ -3172,50 +2159,31 @@ babel-eslint@^10.0.3: eslint-visitor-keys "^1.0.0" resolve "^1.12.0" -babel-plugin-dynamic-import-node@^2.3.3: - version "2.3.3" - resolved "https://registry.yarnpkg.com/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz#84fda19c976ec5c6defef57f9427b3def66e17a3" - integrity sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ== - dependencies: - object.assign "^4.1.0" - -babel-runtime@^6.11.6, babel-runtime@^6.26.0: - version "6.26.0" - resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe" - integrity sha512-ITKNuq2wKlW1fJg9sSW52eepoYgZBggvOAHC0u/CYu/qxQ9EVzThCgR69BnSXLHjy2f7SY5zaQ4yt7H9ZVxY2g== - dependencies: - core-js "^2.4.0" - regenerator-runtime "^0.11.0" - -babel-types@^6.15.0: - version "6.26.0" - resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-6.26.0.tgz#a3b073f94ab49eb6fa55cd65227a334380632497" - integrity sha512-zhe3V/26rCWsEZK8kZN+HaQj5yQ1CilTObixFzKW1UWjqG7618Twz6YEsCnjfg5gBcJh02DrpCkS9h98ZqDY+g== - dependencies: - babel-runtime "^6.26.0" - esutils "^2.0.2" - lodash "^4.17.4" - to-fast-properties "^1.0.3" - -babylon-walk@^1.0.2: +backo2@1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/babylon-walk/-/babylon-walk-1.0.2.tgz#3b15a5ddbb482a78b4ce9c01c8ba181702d9d6ce" - integrity sha512-/AcxC8CZ6YzmKNfiH3+XLjJDbhED3qxSrd4uFNvJ91pcsPuwMNXxfjwHxhiYOidhpis0BiBu/gupOdv2EYyglg== - dependencies: - babel-runtime "^6.11.6" - babel-types "^6.15.0" - lodash.clone "^4.5.0" + resolved "https://registry.yarnpkg.com/backo2/-/backo2-1.0.2.tgz#31ab1ac8b129363463e35b3ebb69f4dfcfba7947" + integrity sha512-zj6Z6M7Eq+PBZ7PQxl5NT665MvJdAkzp0f60nAJ+sLaSCBPMwVak5ZegFbgVCzFcCJTKFoMizvM5Ld7+JrRJHA== balanced-match@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== -base64-js@^1.0.2, base64-js@^1.3.1: +base64-arraybuffer@0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/base64-arraybuffer/-/base64-arraybuffer-0.1.4.tgz#9818c79e059b1355f97e0428a017c838e90ba812" + integrity sha512-a1eIFi4R9ySrbiMuyTGx5e92uRH5tQY6kArNcFaKBUleIoLjdjBg7Zxm3Mqm3Kmkf27HLR/1fnxX9q8GQ7Iavg== + +base64-js@^1.3.1: version "1.5.1" resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== +base64id@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/base64id/-/base64id-2.0.0.tgz#2770ac6bc47d312af97a8bf9a634342e0cd25cb6" + integrity sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog== + base@^0.11.1: version "0.11.2" resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f" @@ -3229,13 +2197,6 @@ base@^0.11.1: mixin-deep "^1.2.0" pascalcase "^0.1.1" -bcrypt-pbkdf@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" - integrity sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w== - dependencies: - tweetnacl "^0.14.3" - before-after-hook@^2.2.0: version "2.2.2" resolved "https://registry.yarnpkg.com/before-after-hook/-/before-after-hook-2.2.2.tgz#a6e8ca41028d90ee2c24222f201c90956091613e" @@ -3254,11 +2215,6 @@ big-integer@^1.6.44: resolved "https://registry.yarnpkg.com/big-integer/-/big-integer-1.6.51.tgz#0df92a5d9880560d3ff2d5fd20245c889d130686" integrity sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg== -big.js@^5.2.2: - version "5.2.2" - resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" - integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ== - bin-links@^3.0.0: version "3.0.3" resolved "https://registry.yarnpkg.com/bin-links/-/bin-links-3.0.3.tgz#3842711ef3db2cd9f16a5f404a996a12db355a6e" @@ -3271,23 +2227,11 @@ bin-links@^3.0.0: rimraf "^3.0.0" write-file-atomic "^4.0.0" -binary-extensions@^1.0.0: - version "1.13.1" - resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.13.1.tgz#598afe54755b2868a5330d2aff9d4ebb53209b65" - integrity sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw== - binary-extensions@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== -bindings@^1.5.0: - version "1.5.0" - resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df" - integrity sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ== - dependencies: - file-uri-to-path "1.0.0" - bl@^4.0.3, bl@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a" @@ -3297,31 +2241,16 @@ bl@^4.0.3, bl@^4.1.0: inherits "^2.0.4" readable-stream "^3.4.0" -bluebird@^3.5.5: - version "3.7.2" - resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" - integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== +blob@0.0.5: + version "0.0.5" + resolved "https://registry.yarnpkg.com/blob/-/blob-0.0.5.tgz#d680eeef25f8cd91ad533f5b01eed48e64caf683" + integrity sha512-gaqbzQPqOoamawKg0LGVd7SzLgXS+JH61oWprSLH+P+abTczqJbhTR8CmJ2u9/bUYNmHTGJx/UEmn6doAvvuig== blueimp-md5@^2.10.0: version "2.19.0" resolved "https://registry.yarnpkg.com/blueimp-md5/-/blueimp-md5-2.19.0.tgz#b53feea5498dcb53dc6ec4b823adb84b729c4af0" integrity sha512-DRQrD6gJyy8FbiE4s+bDoXS9hiW3Vbx5uCdwvcCf3zLHL+Iv7LtGHLpr+GZV8rHG8tK766FGYBwRbu8pELTt+w== -bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.11.9: - version "4.12.0" - resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88" - integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA== - -bn.js@^5.0.0, bn.js@^5.1.1: - version "5.2.1" - resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.1.tgz#0bc527a6a0d18d0aa8d5b0538ce4a77dccfa7b70" - integrity sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ== - -boolbase@^1.0.0, boolbase@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" - integrity sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww== - bplist-parser@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/bplist-parser/-/bplist-parser-0.2.0.tgz#43a9d183e5bf9d545200ceac3e712f79ebbe8d0e" @@ -3344,7 +2273,7 @@ brace-expansion@^2.0.1: dependencies: balanced-match "^1.0.0" -braces@^2.3.1, braces@^2.3.2: +braces@^2.3.1: version "2.3.2" resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729" integrity sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w== @@ -3367,199 +2296,11 @@ braces@^3.0.2, braces@~3.0.2: dependencies: fill-range "^7.0.1" -brfs@^1.2.0: - version "1.6.1" - resolved "https://registry.yarnpkg.com/brfs/-/brfs-1.6.1.tgz#b78ce2336d818e25eea04a0947cba6d4fb8849c3" - integrity sha512-OfZpABRQQf+Xsmju8XE9bDjs+uU4vLREGolP7bDgcpsI17QREyZ4Bl+2KLxxx1kCgA0fAIhKQBaBYh+PEcCqYQ== - dependencies: - quote-stream "^1.0.1" - resolve "^1.1.5" - static-module "^2.2.0" - through2 "^2.0.0" - -brorand@^1.0.1, brorand@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" - integrity sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w== - -browser-pack@^6.0.1: - version "6.1.0" - resolved "https://registry.yarnpkg.com/browser-pack/-/browser-pack-6.1.0.tgz#c34ba10d0b9ce162b5af227c7131c92c2ecd5774" - integrity sha512-erYug8XoqzU3IfcU8fUgyHqyOXqIE4tUTTQ+7mqUjQlvnXkOO6OlT9c/ZoJVHYoAaqGxr09CN53G7XIsO4KtWA== - dependencies: - JSONStream "^1.0.3" - combine-source-map "~0.8.0" - defined "^1.0.0" - safe-buffer "^5.1.1" - through2 "^2.0.0" - umd "^3.0.0" - -browser-process-hrtime@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz#3c9b4b7d782c8121e56f10106d84c0d0ffc94626" - integrity sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow== - -browser-resolve@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/browser-resolve/-/browser-resolve-2.0.0.tgz#99b7304cb392f8d73dba741bb2d7da28c6d7842b" - integrity sha512-7sWsQlYL2rGLy2IWm8WL8DCTJvYLc/qlOnsakDac87SOoCd16WLsaAMdCiAqsTNHIe+SXfaqyxyo6THoWqs8WQ== - dependencies: - resolve "^1.17.0" - -browserify-aes@^1.0.0, browserify-aes@^1.0.4: - version "1.2.0" - resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.2.0.tgz#326734642f403dabc3003209853bb70ad428ef48" - integrity sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA== - dependencies: - buffer-xor "^1.0.3" - cipher-base "^1.0.0" - create-hash "^1.1.0" - evp_bytestokey "^1.0.3" - inherits "^2.0.1" - safe-buffer "^5.0.1" - -browserify-cipher@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/browserify-cipher/-/browserify-cipher-1.0.1.tgz#8d6474c1b870bfdabcd3bcfcc1934a10e94f15f0" - integrity sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w== - dependencies: - browserify-aes "^1.0.4" - browserify-des "^1.0.0" - evp_bytestokey "^1.0.0" - -browserify-des@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/browserify-des/-/browserify-des-1.0.2.tgz#3af4f1f59839403572f1c66204375f7a7f703e9c" - integrity sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A== - dependencies: - cipher-base "^1.0.1" - des.js "^1.0.0" - inherits "^2.0.1" - safe-buffer "^5.1.2" - -browserify-rsa@^4.0.0, browserify-rsa@^4.0.1: - version "4.1.0" - resolved "https://registry.yarnpkg.com/browserify-rsa/-/browserify-rsa-4.1.0.tgz#b2fd06b5b75ae297f7ce2dc651f918f5be158c8d" - integrity sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog== - dependencies: - bn.js "^5.0.0" - randombytes "^2.0.1" - -browserify-sign@^4.0.0: - version "4.2.1" - resolved "https://registry.yarnpkg.com/browserify-sign/-/browserify-sign-4.2.1.tgz#eaf4add46dd54be3bb3b36c0cf15abbeba7956c3" - integrity sha512-/vrA5fguVAKKAVTNJjgSm1tRQDHUU6DbwO9IROu/0WAzC8PKhucDSh18J0RMvVeHAn5puMd+QHC2erPRNf8lmg== - dependencies: - bn.js "^5.1.1" - browserify-rsa "^4.0.1" - create-hash "^1.2.0" - create-hmac "^1.1.7" - elliptic "^6.5.3" - inherits "^2.0.4" - parse-asn1 "^5.1.5" - readable-stream "^3.6.0" - safe-buffer "^5.2.0" - -browserify-zlib@^0.2.0, browserify-zlib@~0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/browserify-zlib/-/browserify-zlib-0.2.0.tgz#2869459d9aa3be245fe8fe2ca1f46e2e7f54d73f" - integrity sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA== - dependencies: - pako "~1.0.5" - -browserify@^17.0.0: - version "17.0.0" - resolved "https://registry.yarnpkg.com/browserify/-/browserify-17.0.0.tgz#4c48fed6c02bfa2b51fd3b670fddb805723cdc22" - integrity sha512-SaHqzhku9v/j6XsQMRxPyBrSP3gnwmE27gLJYZgMT2GeK3J0+0toN+MnuNYDfHwVGQfLiMZ7KSNSIXHemy905w== - dependencies: - JSONStream "^1.0.3" - assert "^1.4.0" - browser-pack "^6.0.1" - browser-resolve "^2.0.0" - browserify-zlib "~0.2.0" - buffer "~5.2.1" - cached-path-relative "^1.0.0" - concat-stream "^1.6.0" - console-browserify "^1.1.0" - constants-browserify "~1.0.0" - crypto-browserify "^3.0.0" - defined "^1.0.0" - deps-sort "^2.0.1" - domain-browser "^1.2.0" - duplexer2 "~0.1.2" - events "^3.0.0" - glob "^7.1.0" - has "^1.0.0" - htmlescape "^1.1.0" - https-browserify "^1.0.0" - inherits "~2.0.1" - insert-module-globals "^7.2.1" - labeled-stream-splicer "^2.0.0" - mkdirp-classic "^0.5.2" - module-deps "^6.2.3" - os-browserify "~0.3.0" - parents "^1.0.1" - path-browserify "^1.0.0" - process "~0.11.0" - punycode "^1.3.2" - querystring-es3 "~0.2.0" - read-only-stream "^2.0.0" - readable-stream "^2.0.2" - resolve "^1.1.4" - shasum-object "^1.0.0" - shell-quote "^1.6.1" - stream-browserify "^3.0.0" - stream-http "^3.0.0" - string_decoder "^1.1.1" - subarg "^1.0.0" - syntax-error "^1.1.1" - through2 "^2.0.0" - timers-browserify "^1.0.1" - tty-browserify "0.0.1" - url "~0.11.0" - util "~0.12.0" - vm-browserify "^1.0.0" - xtend "^4.0.0" - -browserslist@^4.0.0, browserslist@^4.1.0, browserslist@^4.21.3, browserslist@^4.3.4: - version "4.21.4" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.4.tgz#e7496bbc67b9e39dd0f98565feccdcb0d4ff6987" - integrity sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw== - dependencies: - caniuse-lite "^1.0.30001400" - electron-to-chromium "^1.4.251" - node-releases "^2.0.6" - update-browserslist-db "^1.0.9" - -buffer-crc32@~0.2.3: - version "0.2.13" - resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" - integrity sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ== - -buffer-equal@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/buffer-equal/-/buffer-equal-0.0.1.tgz#91bc74b11ea405bc916bc6aa908faafa5b4aac4b" - integrity sha512-RgSV6InVQ9ODPdLWJ5UAqBqJBOg370Nz6ZQtRzpt6nUjc8v0St97uJ4PYC6NztqIScrAXafKM3mZPMygSe1ggA== - buffer-from@^1.0.0: version "1.1.2" resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== -buffer-xor@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9" - integrity sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ== - -buffer@^4.3.0: - version "4.9.2" - resolved "https://registry.yarnpkg.com/buffer/-/buffer-4.9.2.tgz#230ead344002988644841ab0244af8c44bbe3ef8" - integrity sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg== - dependencies: - base64-js "^1.0.2" - ieee754 "^1.1.4" - isarray "^1.0.0" - buffer@^5.5.0: version "5.7.1" resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" @@ -3568,24 +2309,11 @@ buffer@^5.5.0: base64-js "^1.3.1" ieee754 "^1.1.13" -buffer@~5.2.1: - version "5.2.1" - resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.2.1.tgz#dd57fa0f109ac59c602479044dca7b8b3d0b71d6" - integrity sha512-c+Ko0loDaFfuPWiL02ls9Xd3GO3cPVmUobQ6t3rXNUk304u6hGq+8N/kFi+QEIKhzK3uwolVhLzszmfLmMLnqg== - dependencies: - base64-js "^1.0.2" - ieee754 "^1.1.4" - -builtin-modules@^3.1.0, builtin-modules@^3.3.0: +builtin-modules@^3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-3.3.0.tgz#cae62812b89801e9656336e46223e030386be7b6" integrity sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw== -builtin-status-codes@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8" - integrity sha512-HpGFw18DgFWlncDfjTa2rcQ4W88O1mC8e8yZ2AvQY5KDaktSTwo+KRf6nHK6FRI5FyRyb/5T6+TSxfP7QyGsmQ== - builtins@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/builtins/-/builtins-1.0.3.tgz#cb94faeb61c8696451db36534e1422f94f0aee88" @@ -3628,27 +2356,6 @@ c8@^7.14.0: yargs "^16.2.0" yargs-parser "^20.2.9" -cacache@^12.0.2: - version "12.0.4" - resolved "https://registry.yarnpkg.com/cacache/-/cacache-12.0.4.tgz#668bcbd105aeb5f1d92fe25570ec9525c8faa40c" - integrity sha512-a0tMB40oefvuInr4Cwb3GerbL9xTj1D5yg0T5xrjGCGyfvbxseIXX7BAO/u/hIXdafzOI5JC3wDwHyf24buOAQ== - dependencies: - bluebird "^3.5.5" - chownr "^1.1.1" - figgy-pudding "^3.5.1" - glob "^7.1.4" - graceful-fs "^4.1.15" - infer-owner "^1.0.3" - lru-cache "^5.1.1" - mississippi "^3.0.0" - mkdirp "^0.5.1" - move-concurrently "^1.0.1" - promise-inflight "^1.0.1" - rimraf "^2.6.3" - ssri "^6.0.1" - unique-filename "^1.1.1" - y18n "^4.0.0" - cacache@^16.0.0, cacache@^16.0.6, cacache@^16.1.0: version "16.1.3" resolved "https://registry.yarnpkg.com/cacache/-/cacache-16.1.3.tgz#a02b9f34ecfaf9a78c9f4bc16fceb94d5d67a38e" @@ -3688,11 +2395,6 @@ cache-base@^1.0.1: union-value "^1.0.0" unset-value "^1.0.0" -cached-path-relative@^1.0.0, cached-path-relative@^1.0.2: - version "1.1.0" - resolved "https://registry.yarnpkg.com/cached-path-relative/-/cached-path-relative-1.1.0.tgz#865576dfef39c0d6a7defde794d078f5308e3ef3" - integrity sha512-WF0LihfemtesFcJgO7xfOoOcnWzY/QHR4qeDqV44jPU3HTI54+LnfXK3SA27AVVGCdZFgjjFFaqUA9Jx7dMJZA== - call-bind@^1.0.0, call-bind@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" @@ -3715,25 +2417,6 @@ call-me-maybe@^1.0.1: resolved "https://registry.yarnpkg.com/call-me-maybe/-/call-me-maybe-1.0.1.tgz#26d208ea89e37b5cbde60250a15f031c16a4d66b" integrity sha512-wCyFsDQkKPwwF8BDwOiWNx/9K45L/hvggQiDbve+viMNMQnWhrlYIuBk09offfwCRtCO9P6XwUttufzU11WCVw== -caller-callsite@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/caller-callsite/-/caller-callsite-2.0.0.tgz#847e0fce0a223750a9a027c54b33731ad3154134" - integrity sha512-JuG3qI4QOftFsZyOn1qq87fq5grLIyk1JYd5lJmdA+fG7aQ9pA/i3JIJGcO3q0MrRcHlOt1U+ZeHW8Dq9axALQ== - dependencies: - callsites "^2.0.0" - -caller-path@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/caller-path/-/caller-path-2.0.0.tgz#468f83044e369ab2010fac5f06ceee15bb2cb1f4" - integrity sha512-MCL3sf6nCSXOwCTzvPKhN18TU7AHTvdtam8DAogxcrJ8Rjfbbg7Lgng64H9Iy+vUV6VGFClN/TyxBkAebLRR4A== - dependencies: - caller-callsite "^2.0.0" - -callsites@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/callsites/-/callsites-2.0.0.tgz#06eb84f00eea413da86affefacbffb36093b3c50" - integrity sha512-ksWePWBloaWPxJYQ8TL0JHvtci6G5QTKwQ95RcWAa/lzoAKuAOflGdAK92hpHXjkwb8zLxoLNUoNYZgVsaJzvQ== - callsites@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" @@ -3772,26 +2455,6 @@ camelcase@^5.0.0, camelcase@^5.3.1: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== -caniuse-api@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/caniuse-api/-/caniuse-api-3.0.0.tgz#5e4d90e2274961d46291997df599e3ed008ee4c0" - integrity sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw== - dependencies: - browserslist "^4.0.0" - caniuse-lite "^1.0.0" - lodash.memoize "^4.1.2" - lodash.uniq "^4.5.0" - -caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001400: - version "1.0.30001414" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001414.tgz#5f1715e506e71860b4b07c50060ea6462217611e" - integrity sha512-t55jfSaWjCdocnFdKQoO+d2ct9C59UZg4dY3OnUlSZ447r8pUtIKdp0hpAzrGFultmTC+Us+KpKi4GZl/LXlFg== - -caseless@~0.12.0: - version "0.12.0" - resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" - integrity sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw== - cbor@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/cbor/-/cbor-8.1.0.tgz#cfc56437e770b73417a2ecbfc9caf6b771af60d5" @@ -3807,18 +2470,7 @@ chalk@4.1.0: ansi-styles "^4.1.0" supports-color "^7.1.0" -chalk@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" - integrity sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A== - dependencies: - ansi-styles "^2.2.1" - escape-string-regexp "^1.0.2" - has-ansi "^2.0.0" - strip-ansi "^3.0.0" - supports-color "^2.0.0" - -chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.0, chalk@^2.3.1, chalk@^2.4.1, chalk@^2.4.2: +chalk@^2.0.0, chalk@^2.3.0, chalk@^2.4.2: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== @@ -3845,26 +2497,7 @@ chardet@^0.7.0: resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== -chokidar@^2.1.5, chokidar@^2.1.8: - version "2.1.8" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.8.tgz#804b3a7b6a99358c3c5c61e71d8728f041cff917" - integrity sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg== - dependencies: - anymatch "^2.0.0" - async-each "^1.0.1" - braces "^2.3.2" - glob-parent "^3.1.0" - inherits "^2.0.3" - is-binary-path "^1.0.0" - is-glob "^4.0.0" - normalize-path "^3.0.0" - path-is-absolute "^1.0.0" - readdirp "^2.2.1" - upath "^1.1.1" - optionalDependencies: - fsevents "^1.2.7" - -chokidar@^3.4.1, chokidar@^3.5.1, chokidar@^3.5.3: +chokidar@^3.5.1, chokidar@^3.5.3: version "3.5.3" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== @@ -3879,21 +2512,11 @@ chokidar@^3.4.1, chokidar@^3.5.1, chokidar@^3.5.3: optionalDependencies: fsevents "~2.3.2" -chownr@^1.1.1: - version "1.1.4" - resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" - integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== - chownr@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece" integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ== -chrome-trace-event@^1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz#1015eced4741e15d06664a957dbbf50d041e26ac" - integrity sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg== - chunkd@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/chunkd/-/chunkd-2.0.1.tgz#49cd1d7b06992dc4f7fccd962fe2a101ee7da920" @@ -3914,14 +2537,6 @@ ci-parallel-vars@^1.0.1: resolved "https://registry.yarnpkg.com/ci-parallel-vars/-/ci-parallel-vars-1.0.1.tgz#e87ff0625ccf9d286985b29b4ada8485ca9ffbc2" integrity sha512-uvzpYrpmidaoxvIQHM+rKSrigjOe9feHYbw4uOI2gdfe1C3xIlxO+kVXq83WQWNniTf8bAxVpy+cQeFQsMERKg== -cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3: - version "1.0.4" - resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.4.tgz#8760e4ecc272f4c363532f926d874aae2c1397de" - integrity sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q== - dependencies: - inherits "^2.0.1" - safe-buffer "^5.0.1" - class-utils@^0.3.5: version "0.3.6" resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463" @@ -3956,23 +2571,11 @@ cli-cursor@3.1.0, cli-cursor@^3.1.0: dependencies: restore-cursor "^3.1.0" -cli-cursor@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5" - integrity sha512-8lgKz8LmCRYZZQDpRyT2m5rKJ08TnU4tR9FFFW2rxpxR1FzWi4PQ/NfyODchAatHaUgnSPVcx/R5w6NuTBzFiw== - dependencies: - restore-cursor "^2.0.0" - cli-spinners@2.6.1, cli-spinners@^2.5.0: version "2.6.1" resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.6.1.tgz#adc954ebe281c37a6319bfa401e6dd2488ffb70d" integrity sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g== -cli-spinners@^1.1.0: - version "1.3.1" - resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-1.3.1.tgz#002c1990912d0d59580c93bd36c056de99e4259a" - integrity sha512-1QL4544moEsDVH9T/l6Cemov/37iv1RtoKf7NJ04A60+4MREXNfx/QvavbH6QoGdsD4N4Mwy49cmaINR/o2mdg== - cli-truncate@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-3.1.0.tgz#3f23ab12535e3d73e839bb43e73c9de487db1389" @@ -4027,16 +2630,6 @@ clone@^1.0.2: resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" integrity sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg== -clone@^2.1.1: - version "2.1.2" - resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.2.tgz#1b7f4b9f591f1e8f83670401600345a02887435f" - integrity sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w== - -clones@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/clones/-/clones-1.2.0.tgz#b34c872045446a9f264ccceb7731bca05c529b71" - integrity sha512-FXDYw4TjR8wgPZYui2LeTqWh1BLpfQ8lB6upMtlpDF6WlOOxghmTTxWyngdKTgozqBgKnHbTVwTE+hOHqAykuQ== - cmd-shim@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/cmd-shim/-/cmd-shim-5.0.0.tgz#8d0aaa1a6b0708630694c4dbde070ed94c707724" @@ -4044,15 +2637,6 @@ cmd-shim@^5.0.0: dependencies: mkdirp-infer-owner "^2.0.0" -coa@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/coa/-/coa-2.0.2.tgz#43f6c21151b4ef2bf57187db0d73de229e3e7ec3" - integrity sha512-q5/jG+YQnSy4nRTV4F7lPepBJZ8qBNJJDBuJdoejDyLXgmL7IEo+Le2JDZudFTFt7mrCqIRaSjws4ygRCTCAXA== - dependencies: - "@types/q" "^1.5.1" - chalk "^2.4.1" - q "^1.1.2" - code-excerpt@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/code-excerpt/-/code-excerpt-4.0.0.tgz#2de7d46e98514385cb01f7b3b741320115f4c95e" @@ -4068,7 +2652,7 @@ collection-visit@^1.0.0: map-visit "^1.0.0" object-visit "^1.0.0" -color-convert@^1.9.0, color-convert@^1.9.3: +color-convert@^1.9.0: version "1.9.3" resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== @@ -4087,32 +2671,16 @@ color-name@1.1.3: resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== -color-name@^1.0.0, color-name@~1.1.4: +color-name@~1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== -color-string@^1.6.0: - version "1.9.1" - resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.9.1.tgz#4467f9146f036f855b764dfb5bf8582bf342c7a4" - integrity sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg== - dependencies: - color-name "^1.0.0" - simple-swizzle "^0.2.2" - color-support@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2" integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg== -color@^3.0.0: - version "3.2.1" - resolved "https://registry.yarnpkg.com/color/-/color-3.2.1.tgz#3544dc198caf4490c3ecc9a790b54fe9ff45e164" - integrity sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA== - dependencies: - color-convert "^1.9.3" - color-string "^1.6.0" - columnify@^1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/columnify/-/columnify-1.6.0.tgz#6989531713c9008bb29735e61e37acf5bd553cf3" @@ -4121,29 +2689,14 @@ columnify@^1.6.0: strip-ansi "^6.0.1" wcwidth "^1.0.0" -combine-source-map@^0.8.0, combine-source-map@~0.8.0: - version "0.8.0" - resolved "https://registry.yarnpkg.com/combine-source-map/-/combine-source-map-0.8.0.tgz#a58d0df042c186fcf822a8e8015f5450d2d79a8b" - integrity sha512-UlxQ9Vw0b/Bt/KYwCFqdEwsQ1eL8d1gibiFb7lxQJFdvTgc2hIZi6ugsg+kyhzhPV+QEpUiEIwInIAIrgoEkrg== - dependencies: - convert-source-map "~1.1.0" - inline-source-map "~0.6.0" - lodash.memoize "~3.0.3" - source-map "~0.5.3" - -combined-stream@^1.0.6, combined-stream@^1.0.8, combined-stream@~1.0.6: +combined-stream@^1.0.8: version "1.0.8" resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== dependencies: delayed-stream "~1.0.0" -command-exists@^1.2.6: - version "1.2.9" - resolved "https://registry.yarnpkg.com/command-exists/-/command-exists-1.2.9.tgz#c50725af3808c8ab0260fd60b01fbfa25b954f69" - integrity sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w== - -commander@^2.11.0, commander@^2.19.0, commander@^2.20.0: +commander@^2.20.0: version "2.20.3" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== @@ -4181,26 +2734,36 @@ compare-func@^2.0.0: array-ify "^1.0.0" dot-prop "^5.1.0" +component-bind@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/component-bind/-/component-bind-1.0.0.tgz#00c608ab7dcd93897c0009651b1d3a8e1e73bbd1" + integrity sha512-WZveuKPeKAG9qY+FkYDeADzdHyTYdIboXS59ixDeRJL5ZhxpqUnxSOwop4FQjMsiYm3/Or8cegVbpAHNA7pHxw== + +component-emitter@1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.2.1.tgz#137918d6d78283f7df7a6b7c5a63e140e69425e6" + integrity sha512-jPatnhd33viNplKjqXKRkGU345p263OIWzDL2wH3LGIGp5Kojo+uXizHmOADRvhGFFTnJqX3jBAKP6vvmSDKcA== + component-emitter@^1.2.1: version "1.3.0" resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0" integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg== +component-emitter@~1.3.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.1.tgz#ef1d5796f7d93f135ee6fb684340b26403c97d17" + integrity sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ== + +component-inherit@0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/component-inherit/-/component-inherit-0.0.3.tgz#645fc4adf58b72b649d5cae65135619db26ff143" + integrity sha512-w+LhYREhatpVqTESyGFg3NlP6Iu0kEKUHETY9GoZP/pQyW4mHFZuFWRUCIqVPZ36ueVLtoOEZaAqbCF2RDndaA== + concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== -concat-stream@^1.5.0, concat-stream@^1.6.0, concat-stream@^1.6.1, concat-stream@^1.6.2, concat-stream@~1.6.0: - version "1.6.2" - resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" - integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== - dependencies: - buffer-from "^1.0.0" - inherits "^2.0.3" - readable-stream "^2.2.2" - typedarray "^0.0.6" - concat-stream@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-2.0.0.tgz#414cf5af790a48c60ab9be4527d56d5e41133cb1" @@ -4225,7 +2788,7 @@ concordance@^5.0.4: semver "^7.3.2" well-known-symbols "^2.0.0" -config-chain@^1.1.12, config-chain@^1.1.13: +config-chain@^1.1.12: version "1.1.13" resolved "https://registry.yarnpkg.com/config-chain/-/config-chain-1.1.13.tgz#fad0795aa6a6cdaff9ed1b68e9dff94372c232f4" integrity sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ== @@ -4238,21 +2801,11 @@ confusing-browser-globals@^1.0.10: resolved "https://registry.yarnpkg.com/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz#ae40e9b57cdd3915408a2805ebd3a5585608dc81" integrity sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA== -console-browserify@^1.1.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.2.0.tgz#67063cef57ceb6cf4993a2ab3a55840ae8c49336" - integrity sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA== - console-control-strings@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" integrity sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ== -constants-browserify@^1.0.0, constants-browserify@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aaf1c16021760cd27fcb8cb75" - integrity sha512-xFxOwqIzR/e1k1gLiWEophSCMqXcwVHIH7akf7b/vxcUeGunlj3hvZaaqxwHsTgn+IndtkQJgSztIDWeumWJDQ== - conventional-changelog-angular@^5.0.12: version "5.0.13" resolved "https://registry.yarnpkg.com/conventional-changelog-angular/-/conventional-changelog-angular-5.0.13.tgz#896885d63b914a70d4934b59d2fe7bde1832b28c" @@ -4344,304 +2897,68 @@ conventional-recommended-bump@^6.1.0: meow "^8.0.0" q "^1.5.1" -convert-source-map@^1.1.0, convert-source-map@^1.5.1, convert-source-map@^1.6.0: +convert-source-map@^1.6.0: version "1.8.0" resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.8.0.tgz#f3373c32d21b4d780dd8004514684fb791ca4369" integrity sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA== dependencies: safe-buffer "~5.1.1" -convert-source-map@~1.1.0: - version "1.1.3" - resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.1.3.tgz#4829c877e9fe49b3161f3bf3673888e204699860" - integrity sha512-Y8L5rp6jo+g9VEPgvqNfEopjTR4OTYct8lXlS8iVQdmnjDvbdbzYe9rjtFCB9egC86JoNCU61WRY+ScjkZpnIg== - convert-to-spaces@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/convert-to-spaces/-/convert-to-spaces-2.0.1.tgz#61a6c98f8aa626c16b296b862a91412a33bceb6b" integrity sha512-rcQ1bsQO9799wq24uE5AM2tAILy4gXGIK/njFWcVQkGNZ96edlpY+A7bjwvzjYvLDyzmG1MmMLZhpcsb+klNMQ== -copy-concurrently@^1.0.0: - version "1.0.5" - resolved "https://registry.yarnpkg.com/copy-concurrently/-/copy-concurrently-1.0.5.tgz#92297398cae34937fcafd6ec8139c18051f0b5e0" - integrity sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A== - dependencies: - aproba "^1.1.1" - fs-write-stream-atomic "^1.0.8" - iferr "^0.1.5" - mkdirp "^0.5.1" - rimraf "^2.5.4" - run-queue "^1.0.0" +cookie@~0.4.1: + version "0.4.2" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.2.tgz#0e41f24de5ecf317947c82fc789e06a884824432" + integrity sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA== copy-descriptor@^0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" - integrity sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw== - -core-js@^2.4.0: - version "2.6.12" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.12.tgz#d9333dfa7b065e347cc5682219d6f690859cc2ec" - integrity sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ== - -core-js@^3.31.0: - version "3.31.0" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.31.0.tgz#4471dd33e366c79d8c0977ed2d940821719db344" - integrity sha512-NIp2TQSGfR6ba5aalZD+ZQ1fSxGhDo/s1w0nx3RYzf2pnJxt7YynxFlFScP6eV7+GZsKO95NSjGxyJsU3DZgeQ== - -core-util-is@1.0.2, core-util-is@~1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" - integrity sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ== - -cosmiconfig@^5.0.0: - version "5.2.1" - resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-5.2.1.tgz#040f726809c591e77a17c0a3626ca45b4f168b1a" - integrity sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA== - dependencies: - import-fresh "^2.0.0" - is-directory "^0.3.1" - js-yaml "^3.13.1" - parse-json "^4.0.0" - -cosmiconfig@^7.0.0: - version "7.1.0" - resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-7.1.0.tgz#1443b9afa596b670082ea46cbd8f6a62b84635f6" - integrity sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA== - dependencies: - "@types/parse-json" "^4.0.0" - import-fresh "^3.2.1" - parse-json "^5.0.0" - path-type "^4.0.0" - yaml "^1.10.0" - -create-ecdh@^4.0.0: - version "4.0.4" - resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.4.tgz#d6e7f4bffa66736085a0762fd3a632684dabcc4e" - integrity sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A== - dependencies: - bn.js "^4.1.0" - elliptic "^6.5.3" - -create-hash@^1.1.0, create-hash@^1.1.2, create-hash@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.2.0.tgz#889078af11a63756bcfb59bd221996be3a9ef196" - integrity sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg== - dependencies: - cipher-base "^1.0.1" - inherits "^2.0.1" - md5.js "^1.3.4" - ripemd160 "^2.0.1" - sha.js "^2.4.0" - -create-hmac@^1.1.0, create-hmac@^1.1.4, create-hmac@^1.1.7: - version "1.1.7" - resolved "https://registry.yarnpkg.com/create-hmac/-/create-hmac-1.1.7.tgz#69170c78b3ab957147b2b8b04572e47ead2243ff" - integrity sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg== - dependencies: - cipher-base "^1.0.3" - create-hash "^1.1.0" - inherits "^2.0.1" - ripemd160 "^2.0.0" - safe-buffer "^5.0.1" - sha.js "^2.4.8" - -cross-spawn@^6.0.4, cross-spawn@^6.0.5: - version "6.0.5" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" - integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== - dependencies: - nice-try "^1.0.4" - path-key "^2.0.1" - semver "^5.5.0" - shebang-command "^1.2.0" - which "^1.2.9" - -cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3: - version "7.0.3" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" - integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== - dependencies: - path-key "^3.1.0" - shebang-command "^2.0.0" - which "^2.0.1" - -crypto-browserify@^3.0.0, crypto-browserify@^3.11.0: - version "3.12.0" - resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec" - integrity sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg== - dependencies: - browserify-cipher "^1.0.0" - browserify-sign "^4.0.0" - create-ecdh "^4.0.0" - create-hash "^1.1.0" - create-hmac "^1.1.0" - diffie-hellman "^5.0.0" - inherits "^2.0.1" - pbkdf2 "^3.0.3" - public-encrypt "^4.0.0" - randombytes "^2.0.0" - randomfill "^1.0.3" - -css-color-names@0.0.4, css-color-names@^0.0.4: - version "0.0.4" - resolved "https://registry.yarnpkg.com/css-color-names/-/css-color-names-0.0.4.tgz#808adc2e79cf84738069b646cb20ec27beb629e0" - integrity sha512-zj5D7X1U2h2zsXOAM8EyUREBnnts6H+Jm+d1M2DbiQQcUtnqgQsMrdo8JW9R80YFUmIdBZeMu5wvYM7hcgWP/Q== - -css-declaration-sorter@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/css-declaration-sorter/-/css-declaration-sorter-4.0.1.tgz#c198940f63a76d7e36c1e71018b001721054cb22" - integrity sha512-BcxQSKTSEEQUftYpBVnsH4SF05NTuBokb19/sBt6asXGKZ/6VP7PLG1CBCkFDYOnhXhPh0jMhO6xZ71oYHXHBA== - dependencies: - postcss "^7.0.1" - timsort "^0.3.0" - -css-modules-loader-core@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/css-modules-loader-core/-/css-modules-loader-core-1.1.0.tgz#5908668294a1becd261ae0a4ce21b0b551f21d16" - integrity sha512-XWOBwgy5nwBn76aA+6ybUGL/3JBnCtBX9Ay9/OWIpzKYWlVHMazvJ+WtHumfi+xxdPF440cWK7JCYtt8xDifew== - dependencies: - icss-replace-symbols "1.1.0" - postcss "6.0.1" - postcss-modules-extract-imports "1.1.0" - postcss-modules-local-by-default "1.2.0" - postcss-modules-scope "1.1.0" - postcss-modules-values "1.3.0" - -css-select-base-adapter@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz#3b2ff4972cc362ab88561507a95408a1432135d7" - integrity sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w== - -css-select@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/css-select/-/css-select-2.1.0.tgz#6a34653356635934a81baca68d0255432105dbef" - integrity sha512-Dqk7LQKpwLoH3VovzZnkzegqNSuAziQyNZUcrdDM401iY+R5NkGBXGmtO05/yaXQziALuPogeG0b7UAgjnTJTQ== - dependencies: - boolbase "^1.0.0" - css-what "^3.2.1" - domutils "^1.7.0" - nth-check "^1.0.2" - -css-selector-tokenizer@^0.7.0: - version "0.7.3" - resolved "https://registry.yarnpkg.com/css-selector-tokenizer/-/css-selector-tokenizer-0.7.3.tgz#735f26186e67c749aaf275783405cf0661fae8f1" - integrity sha512-jWQv3oCEL5kMErj4wRnK/OPoBi0D+P1FR2cDCKYPaMeD2eW3/mttav8HT4hT1CKopiJI/psEULjkClhvJo4Lvg== - dependencies: - cssesc "^3.0.0" - fastparse "^1.1.2" - -css-tree@1.0.0-alpha.37: - version "1.0.0-alpha.37" - resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.0.0-alpha.37.tgz#98bebd62c4c1d9f960ec340cf9f7522e30709a22" - integrity sha512-DMxWJg0rnz7UgxKT0Q1HU/L9BeJI0M6ksor0OgqOnF+aRCDWg/N2641HmVyU9KVIu0OVVWOb2IpC9A+BJRnejg== - dependencies: - mdn-data "2.0.4" - source-map "^0.6.1" - -css-tree@^1.1.2: - version "1.1.3" - resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.1.3.tgz#eb4870fb6fd7707327ec95c2ff2ab09b5e8db91d" - integrity sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q== - dependencies: - mdn-data "2.0.14" - source-map "^0.6.1" - -css-what@^3.2.1: - version "3.4.2" - resolved "https://registry.yarnpkg.com/css-what/-/css-what-3.4.2.tgz#ea7026fcb01777edbde52124e21f327e7ae950e4" - integrity sha512-ACUm3L0/jiZTqfzRM3Hi9Q8eZqd6IK37mMWPLz9PJxkLWllYeRf+EHUSHYEtFop2Eqytaq1FizFVh7XfBnXCDQ== - -cssesc@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee" - integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg== - -cssnano-preset-default@^4.0.8: - version "4.0.8" - resolved "https://registry.yarnpkg.com/cssnano-preset-default/-/cssnano-preset-default-4.0.8.tgz#920622b1fc1e95a34e8838203f1397a504f2d3ff" - integrity sha512-LdAyHuq+VRyeVREFmuxUZR1TXjQm8QQU/ktoo/x7bz+SdOge1YKc5eMN6pRW7YWBmyq59CqYba1dJ5cUukEjLQ== - dependencies: - css-declaration-sorter "^4.0.1" - cssnano-util-raw-cache "^4.0.1" - postcss "^7.0.0" - postcss-calc "^7.0.1" - postcss-colormin "^4.0.3" - postcss-convert-values "^4.0.1" - postcss-discard-comments "^4.0.2" - postcss-discard-duplicates "^4.0.2" - postcss-discard-empty "^4.0.1" - postcss-discard-overridden "^4.0.1" - postcss-merge-longhand "^4.0.11" - postcss-merge-rules "^4.0.3" - postcss-minify-font-values "^4.0.2" - postcss-minify-gradients "^4.0.2" - postcss-minify-params "^4.0.2" - postcss-minify-selectors "^4.0.2" - postcss-normalize-charset "^4.0.1" - postcss-normalize-display-values "^4.0.2" - postcss-normalize-positions "^4.0.2" - postcss-normalize-repeat-style "^4.0.2" - postcss-normalize-string "^4.0.2" - postcss-normalize-timing-functions "^4.0.2" - postcss-normalize-unicode "^4.0.1" - postcss-normalize-url "^4.0.1" - postcss-normalize-whitespace "^4.0.2" - postcss-ordered-values "^4.1.2" - postcss-reduce-initial "^4.0.3" - postcss-reduce-transforms "^4.0.2" - postcss-svgo "^4.0.3" - postcss-unique-selectors "^4.0.1" - -cssnano-util-get-arguments@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/cssnano-util-get-arguments/-/cssnano-util-get-arguments-4.0.0.tgz#ed3a08299f21d75741b20f3b81f194ed49cc150f" - integrity sha512-6RIcwmV3/cBMG8Aj5gucQRsJb4vv4I4rn6YjPbVWd5+Pn/fuG+YseGvXGk00XLkoZkaj31QOD7vMUpNPC4FIuw== - -cssnano-util-get-match@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/cssnano-util-get-match/-/cssnano-util-get-match-4.0.0.tgz#c0e4ca07f5386bb17ec5e52250b4f5961365156d" - integrity sha512-JPMZ1TSMRUPVIqEalIBNoBtAYbi8okvcFns4O0YIhcdGebeYZK7dMyHJiQ6GqNBA9kE0Hym4Aqym5rPdsV/4Cw== + integrity sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw== -cssnano-util-raw-cache@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/cssnano-util-raw-cache/-/cssnano-util-raw-cache-4.0.1.tgz#b26d5fd5f72a11dfe7a7846fb4c67260f96bf282" - integrity sha512-qLuYtWK2b2Dy55I8ZX3ky1Z16WYsx544Q0UWViebptpwn/xDBmog2TLg4f+DBMg1rJ6JDWtn96WHbOKDWt1WQA== - dependencies: - postcss "^7.0.0" +core-js@^3.31.0: + version "3.31.0" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.31.0.tgz#4471dd33e366c79d8c0977ed2d940821719db344" + integrity sha512-NIp2TQSGfR6ba5aalZD+ZQ1fSxGhDo/s1w0nx3RYzf2pnJxt7YynxFlFScP6eV7+GZsKO95NSjGxyJsU3DZgeQ== -cssnano-util-same-parent@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/cssnano-util-same-parent/-/cssnano-util-same-parent-4.0.1.tgz#574082fb2859d2db433855835d9a8456ea18bbf3" - integrity sha512-WcKx5OY+KoSIAxBW6UBBRay1U6vkYheCdjyVNDm85zt5K9mHoGOfsOsqIszfAqrQQFIIKgjh2+FDgIj/zsl21Q== +core-util-is@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" + integrity sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ== -cssnano@^4.0.0, cssnano@^4.1.11: - version "4.1.11" - resolved "https://registry.yarnpkg.com/cssnano/-/cssnano-4.1.11.tgz#c7b5f5b81da269cb1fd982cb960c1200910c9a99" - integrity sha512-6gZm2htn7xIPJOHY824ERgj8cNPgPxyCSnkXc4v7YvNW+TdVfzgngHcEhy/8D11kUWRUMbke+tC+AUcUsnMz2g== +cosmiconfig@^7.0.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-7.1.0.tgz#1443b9afa596b670082ea46cbd8f6a62b84635f6" + integrity sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA== dependencies: - cosmiconfig "^5.0.0" - cssnano-preset-default "^4.0.8" - is-resolvable "^1.0.0" - postcss "^7.0.0" + "@types/parse-json" "^4.0.0" + import-fresh "^3.2.1" + parse-json "^5.0.0" + path-type "^4.0.0" + yaml "^1.10.0" -csso@^4.0.2: - version "4.2.0" - resolved "https://registry.yarnpkg.com/csso/-/csso-4.2.0.tgz#ea3a561346e8dc9f546d6febedd50187cf389529" - integrity sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA== +cross-spawn@^6.0.5: + version "6.0.5" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" + integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== dependencies: - css-tree "^1.1.2" - -cssom@0.3.x, cssom@^0.3.4: - version "0.3.8" - resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.8.tgz#9f1276f5b2b463f2114d3f2c75250af8c1a36f4a" - integrity sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg== + nice-try "^1.0.4" + path-key "^2.0.1" + semver "^5.5.0" + shebang-command "^1.2.0" + which "^1.2.9" -cssstyle@^1.1.1: - version "1.4.0" - resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-1.4.0.tgz#9d31328229d3c565c61e586b02041a28fccdccf1" - integrity sha512-GBrLZYZ4X4x6/QEoBnIrqb8B/f5l4+8me2dkom/j1Gtbxy0kBv6OGzKuAsGM75bkGwGAFkt56Iwg28S3XTZgSA== +cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3: + version "7.0.3" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" + integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== dependencies: - cssom "0.3.x" + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" currently-unhandled@^0.4.1: version "0.4.1" @@ -4650,37 +2967,11 @@ currently-unhandled@^0.4.1: dependencies: array-find-index "^1.0.1" -cyclist@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-1.0.1.tgz#596e9698fd0c80e12038c2b82d6eb1b35b6224d9" - integrity sha512-NJGVKPS81XejHcLhaLJS7plab0fK3slPh11mESeeDq2W4ZI5kUKK/LRRdVDvjJseojbPB7ZwjnyOybg3Igea/A== - dargs@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/dargs/-/dargs-7.0.0.tgz#04015c41de0bcb69ec84050f3d9be0caf8d6d5cc" integrity sha512-2iy1EkLdlBzQGvbweYRFxmFath8+K7+AKB0TlhHWkNuH+TmovaMH/Wp7V7R4u7f4SnX3OgLsU9t1NI9ioDnUpg== -dash-ast@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/dash-ast/-/dash-ast-1.0.0.tgz#12029ba5fb2f8aa6f0a861795b23c1b4b6c27d37" - integrity sha512-Vy4dx7gquTeMcQR/hDkYLGUnwVil6vk4FOOct+djUnHOUWt+zJPJAaRIXaAFkPXtJjvlY7o3rfRu0/3hpnwoUA== - -dashdash@^1.12.0: - version "1.14.1" - resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" - integrity sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g== - dependencies: - assert-plus "^1.0.0" - -data-urls@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-1.1.0.tgz#15ee0582baa5e22bb59c77140da8f9c76963bbfe" - integrity sha512-YTWYI9se1P55u58gL5GkQHW4P6VJBJ5iBT+B5a7i2Tjadhv52paJG0qHX4A0OR6/t52odI64KP2YvFpkDOi3eQ== - dependencies: - abab "^2.0.0" - whatwg-mimetype "^2.2.0" - whatwg-url "^7.0.0" - date-time@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/date-time/-/date-time-3.1.0.tgz#0d1e934d170579f481ed8df1e2b8ff70ee845e1e" @@ -4693,35 +2984,41 @@ dateformat@^3.0.0: resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-3.0.3.tgz#a6e37499a4d9a9cf85ef5872044d62901c9889ae" integrity sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q== -deasync@^0.1.14: - version "0.1.28" - resolved "https://registry.yarnpkg.com/deasync/-/deasync-0.1.28.tgz#9b447b79b3f822432f0ab6a8614c0062808b5ad2" - integrity sha512-QqLF6inIDwiATrfROIyQtwOQxjZuek13WRYZ7donU5wJPLoP67MnYxA6QtqdvdBy2mMqv5m3UefBVdJjvevOYg== +debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4: + version "4.3.4" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" + integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== dependencies: - bindings "^1.5.0" - node-addon-api "^1.7.1" + ms "2.1.2" -debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.9: +debug@^2.2.0, debug@^2.3.3: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== dependencies: ms "2.0.0" -debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4: - version "4.3.4" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" - integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== - dependencies: - ms "2.1.2" - -debug@^3.1.0, debug@^3.2.7: +debug@^3.2.7: version "3.2.7" resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== dependencies: ms "^2.1.1" +debug@~3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" + integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g== + dependencies: + ms "2.0.0" + +debug@~4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" + integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw== + dependencies: + ms "^2.1.1" + debuglog@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/debuglog/-/debuglog-1.0.1.tgz#aa24ffb9ac3df9a2351837cfb2d279360cd78492" @@ -4750,31 +3047,7 @@ dedent@^0.7.0: resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c" integrity sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA== -deep-equal@^2.2.0: - version "2.2.1" - resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-2.2.1.tgz#c72ab22f3a7d3503a4ca87dde976fe9978816739" - integrity sha512-lKdkdV6EOGoVn65XaOsPdH4rMxTZOnmFyuIkMjM1i5HHCbfjC97dawgTAy0deYNfuqUqW+Q5VrVaQYtUpSd6yQ== - dependencies: - array-buffer-byte-length "^1.0.0" - call-bind "^1.0.2" - es-get-iterator "^1.1.3" - get-intrinsic "^1.2.0" - is-arguments "^1.1.1" - is-array-buffer "^3.0.2" - is-date-object "^1.0.5" - is-regex "^1.1.4" - is-shared-array-buffer "^1.0.2" - isarray "^2.0.5" - object-is "^1.1.5" - object-keys "^1.1.1" - object.assign "^4.1.4" - regexp.prototype.flags "^1.5.0" - side-channel "^1.0.4" - which-boxed-primitive "^1.0.2" - which-collection "^1.0.1" - which-typed-array "^1.1.9" - -deep-is@^0.1.3, deep-is@~0.1.3: +deep-is@^0.1.3: version "0.1.4" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== @@ -4858,11 +3131,6 @@ define-property@^2.0.2: is-descriptor "^1.0.2" isobject "^3.0.1" -defined@^1.0.0, defined@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.1.tgz#c0b9db27bfaffd95d6f61399419b893df0f91ebf" - integrity sha512-hsBd2qSVCRE+5PmNdHt1uzyrFu5d3RwmFDKzyNZMFq/EwDNJF7Ee5+D5oEKF0hU6LhtoUF1macFvOe4AskQC1Q== - delayed-stream@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" @@ -4873,11 +3141,6 @@ delegates@^1.0.0: resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" integrity sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ== -depd@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" - integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== - depd@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" @@ -4888,34 +3151,6 @@ deprecation@^2.0.0, deprecation@^2.3.1: resolved "https://registry.yarnpkg.com/deprecation/-/deprecation-2.3.1.tgz#6368cbdb40abf3373b525ac87e4a260c3a700919" integrity sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ== -deps-sort@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/deps-sort/-/deps-sort-2.0.1.tgz#9dfdc876d2bcec3386b6829ac52162cda9fa208d" - integrity sha512-1orqXQr5po+3KI6kQb9A4jnXT1PBwggGl2d7Sq2xsnOeI9GPcE/tGcF9UiSZtZBM7MukY4cAh7MemS6tZYipfw== - dependencies: - JSONStream "^1.0.3" - shasum-object "^1.0.0" - subarg "^1.0.0" - through2 "^2.0.0" - -des.js@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.0.1.tgz#5382142e1bdc53f85d86d53e5f4aa7deb91e0843" - integrity sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA== - dependencies: - inherits "^2.0.1" - minimalistic-assert "^1.0.0" - -destroy@1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015" - integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg== - -detect-file@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/detect-file/-/detect-file-1.0.0.tgz#f0d66d03672a825cb1b73bdb3fe62310c8e552b7" - integrity sha512-DtCOLG98P007x7wiiOmfI0fi3eIKyWiLTGJ2MDnVi/E04lWGbf+JzrRHMm0rgIIZJGtHpKpbVgLWHrv8xXpc3Q== - detect-indent@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-5.0.0.tgz#3871cc0a6a002e8c3e5b3cf7f336264675f06b9d" @@ -4926,15 +3161,6 @@ detect-indent@^6.0.0: resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-6.1.0.tgz#592485ebbbf6b3b1ab2be175c8393d04ca0d57e6" integrity sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA== -detective@^5.2.0: - version "5.2.1" - resolved "https://registry.yarnpkg.com/detective/-/detective-5.2.1.tgz#6af01eeda11015acb0e73f933242b70f24f91034" - integrity sha512-v9XE1zRnz1wRtgurGu0Bs8uHKFSTdteYZNbIPFVhUZ39L/S79ppMpdmVOZAnoz1jfEFodc48n6MX483Xo3t1yw== - dependencies: - acorn-node "^1.8.2" - defined "^1.0.0" - minimist "^1.2.6" - dezalgo@^1.0.0: version "1.0.4" resolved "https://registry.yarnpkg.com/dezalgo/-/dezalgo-1.0.4.tgz#751235260469084c132157dfa857f386d4c33d81" @@ -4953,15 +3179,6 @@ diff@^5.1.0: resolved "https://registry.yarnpkg.com/diff/-/diff-5.1.0.tgz#bc52d298c5ea8df9194800224445ed43ffc87e40" integrity sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw== -diffie-hellman@^5.0.0: - version "5.0.3" - resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.3.tgz#40e8ee98f55a2149607146921c63e1ae5f3d2875" - integrity sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg== - dependencies: - bn.js "^4.1.0" - miller-rabin "^4.0.0" - randombytes "^2.0.0" - dir-glob@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-2.0.0.tgz#0b205d2b6aef98238ca286598a8204d29d0a0034" @@ -4991,77 +3208,7 @@ doctrine@^3.0.0: dependencies: esutils "^2.0.2" -dom-serializer@0: - version "0.2.2" - resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.2.2.tgz#1afb81f533717175d478655debc5e332d9f9bb51" - integrity sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g== - dependencies: - domelementtype "^2.0.1" - entities "^2.0.0" - -dom-serializer@^1.0.1: - version "1.4.1" - resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-1.4.1.tgz#de5d41b1aea290215dc45a6dae8adcf1d32e2d30" - integrity sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag== - dependencies: - domelementtype "^2.0.1" - domhandler "^4.2.0" - entities "^2.0.0" - -domain-browser@^1.1.1, domain-browser@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.2.0.tgz#3d31f50191a6749dd1375a7f522e823d42e54eda" - integrity sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA== - -domelementtype@1, domelementtype@^1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.1.tgz#d048c44b37b0d10a7f2a3d5fee3f4333d790481f" - integrity sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w== - -domelementtype@^2.0.1, domelementtype@^2.2.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.3.0.tgz#5c45e8e869952626331d7aab326d01daf65d589d" - integrity sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw== - -domexception@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/domexception/-/domexception-1.0.1.tgz#937442644ca6a31261ef36e3ec677fe805582c90" - integrity sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug== - dependencies: - webidl-conversions "^4.0.2" - -domhandler@^2.3.0: - version "2.4.2" - resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-2.4.2.tgz#8805097e933d65e85546f726d60f5eb88b44f803" - integrity sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA== - dependencies: - domelementtype "1" - -domhandler@^4.0.0, domhandler@^4.2.0: - version "4.3.1" - resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.3.1.tgz#8d792033416f59d68bc03a5aa7b018c1ca89279c" - integrity sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ== - dependencies: - domelementtype "^2.2.0" - -domutils@^1.5.1, domutils@^1.7.0: - version "1.7.0" - resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.7.0.tgz#56ea341e834e06e6748af7a1cb25da67ea9f8c2a" - integrity sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg== - dependencies: - dom-serializer "0" - domelementtype "1" - -domutils@^2.5.2: - version "2.8.0" - resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.8.0.tgz#4437def5db6e2d1f5d6ee859bd95ca7d02048135" - integrity sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A== - dependencies: - dom-serializer "^1.0.1" - domelementtype "^2.2.0" - domhandler "^4.2.0" - -dot-prop@^5.1.0, dot-prop@^5.2.0: +dot-prop@^5.1.0: version "5.3.0" resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-5.3.0.tgz#90ccce708cd9cd82cc4dc8c3ddd9abdd55b20e88" integrity sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q== @@ -5075,78 +3222,21 @@ dot-prop@^6.0.1: dependencies: is-obj "^2.0.0" -dotenv-expand@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/dotenv-expand/-/dotenv-expand-4.2.0.tgz#def1f1ca5d6059d24a766e587942c21106ce1275" - integrity sha512-pHWVt6L/YkqbBCMb1hG6e7oO0WdMhlapDIibl+BZ9PncVE3i+G77uvNr8GUxW2ItSituOK8QOYC9oOJjwWD94A== - -dotenv@^5.0.0: - version "5.0.1" - resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-5.0.1.tgz#a5317459bd3d79ab88cff6e44057a6a3fbb1fcef" - integrity sha512-4As8uPrjfwb7VXC+WnLCbXK7y+Ueb2B3zgNCePYfhxS1PYeaO1YTeplffTEcbfLhvFNGLAz90VvJs9yomG7bow== - dotenv@~10.0.0: version "10.0.0" resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-10.0.0.tgz#3d4227b8fb95f81096cdd2b66653fb2c7085ba81" integrity sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q== -dotignore@^0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/dotignore/-/dotignore-0.1.2.tgz#f942f2200d28c3a76fbdd6f0ee9f3257c8a2e905" - integrity sha512-UGGGWfSauusaVJC+8fgV+NVvBXkCTmVv7sk6nojDZZvuOUNGUy0Zk4UpHQD6EDjS0jpBwcACvH4eofvyzBcRDw== - dependencies: - minimatch "^3.0.4" - -duplexer2@^0.1.2, duplexer2@~0.1.0, duplexer2@~0.1.2, duplexer2@~0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/duplexer2/-/duplexer2-0.1.4.tgz#8b12dab878c0d69e3e7891051662a32fc6bddcc1" - integrity sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA== - dependencies: - readable-stream "^2.0.2" - duplexer@^0.1.1: version "0.1.2" resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.2.tgz#3abe43aef3835f8ae077d136ddce0f276b0400e6" integrity sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg== -duplexify@^3.4.2, duplexify@^3.6.0: - version "3.7.1" - resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-3.7.1.tgz#2a4df5317f6ccfd91f86d6fd25d8d8a103b88309" - integrity sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g== - dependencies: - end-of-stream "^1.0.0" - inherits "^2.0.1" - readable-stream "^2.0.0" - stream-shift "^1.0.0" - eastasianwidth@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== -ecc-jsbn@~0.1.1: - version "0.1.2" - resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" - integrity sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw== - dependencies: - jsbn "~0.1.0" - safer-buffer "^2.1.0" - -editorconfig@^0.15.3: - version "0.15.3" - resolved "https://registry.yarnpkg.com/editorconfig/-/editorconfig-0.15.3.tgz#bef84c4e75fb8dcb0ce5cee8efd51c15999befc5" - integrity sha512-M9wIMFx96vq0R4F+gRpY3o2exzb8hEj/n9S8unZtHSvYjibBp/iMufSzvmOcV/laG0ZtuTVGtiJggPOSW2r93g== - dependencies: - commander "^2.19.0" - lru-cache "^4.1.5" - semver "^5.6.0" - sigmund "^1.0.1" - -ee-first@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" - integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== - ejs@^3.1.7: version "3.1.8" resolved "https://registry.yarnpkg.com/ejs/-/ejs-3.1.8.tgz#758d32910c78047585c7ef1f92f9ee041c1c190b" @@ -5154,24 +3244,6 @@ ejs@^3.1.7: dependencies: jake "^10.8.5" -electron-to-chromium@^1.4.251: - version "1.4.270" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.270.tgz#2c6ea409b45cdb5c3e0cb2c08cf6c0ba7e0f2c26" - integrity sha512-KNhIzgLiJmDDC444dj9vEOpZEgsV96ult9Iff98Vanumn+ShJHd5se8aX6KeVxdc0YQeqdrezBZv89rleDbvSg== - -elliptic@^6.5.3: - version "6.5.4" - resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.4.tgz#da37cebd31e79a1367e941b592ed1fbebd58abbb" - integrity sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ== - dependencies: - bn.js "^4.11.9" - brorand "^1.1.0" - hash.js "^1.0.0" - hmac-drbg "^1.0.1" - inherits "^2.0.4" - minimalistic-assert "^1.0.1" - minimalistic-crypto-utils "^1.0.1" - emittery@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/emittery/-/emittery-1.0.1.tgz#e0cf36e2d7eef94dbd025969f642d57ae50a56cd" @@ -5192,16 +3264,6 @@ emoji-regex@^9.2.2: resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== -emojis-list@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78" - integrity sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q== - -encodeurl@~1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" - integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w== - encoding@^0.1.13: version "0.1.13" resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.13.tgz#56574afdd791f54a8e9b2785c0582a2d26210fa9" @@ -5209,21 +3271,52 @@ encoding@^0.1.13: dependencies: iconv-lite "^0.6.2" -end-of-stream@^1.0.0, end-of-stream@^1.1.0, end-of-stream@^1.4.1: +end-of-stream@^1.4.1: version "1.4.4" resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== dependencies: once "^1.4.0" -enhanced-resolve@^4.1.1, enhanced-resolve@^4.5.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-4.5.0.tgz#2f3cfd84dbe3b487f18f2db2ef1e064a571ca5ec" - integrity sha512-Nv9m36S/vxpsI+Hc4/ZGRs0n9mXqSWGGq49zxb/cJfPAQMbUtttJAlNPS4AQzaBdw/pKskw5bMbekT/Y7W/Wlg== +engine.io-client@~3.5.0: + version "3.5.3" + resolved "https://registry.yarnpkg.com/engine.io-client/-/engine.io-client-3.5.3.tgz#3254f61fdbd53503dc9a6f9d46a52528871ca0d7" + integrity sha512-qsgyc/CEhJ6cgMUwxRRtOndGVhIu5hpL5tR4umSpmX/MvkFoIxUTM7oFMDQumHNzlNLwSVy6qhstFPoWTf7dOw== + dependencies: + component-emitter "~1.3.0" + component-inherit "0.0.3" + debug "~3.1.0" + engine.io-parser "~2.2.0" + has-cors "1.1.0" + indexof "0.0.1" + parseqs "0.0.6" + parseuri "0.0.6" + ws "~7.4.2" + xmlhttprequest-ssl "~1.6.2" + yeast "0.1.2" + +engine.io-parser@~2.2.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/engine.io-parser/-/engine.io-parser-2.2.1.tgz#57ce5611d9370ee94f99641b589f94c97e4f5da7" + integrity sha512-x+dN/fBH8Ro8TFwJ+rkB2AmuVw9Yu2mockR/p3W8f8YtExwFgDvBDi0GWyb4ZLkpahtDGZgtr3zLovanJghPqg== dependencies: - graceful-fs "^4.1.2" - memory-fs "^0.5.0" - tapable "^1.0.0" + after "0.8.2" + arraybuffer.slice "~0.0.7" + base64-arraybuffer "0.1.4" + blob "0.0.5" + has-binary2 "~1.0.2" + +engine.io@~3.6.0: + version "3.6.1" + resolved "https://registry.yarnpkg.com/engine.io/-/engine.io-3.6.1.tgz#7ca4c7779c20865e30d208751bde08ca1e800256" + integrity sha512-dfs8EVg/i7QjFsXxn7cCRQ+Wai1G1TlEvHhdYEi80fxn5R1vZ2K661O6v/rezj1FP234SZ14r9CmJke99iYDGg== + dependencies: + accepts "~1.3.4" + base64id "2.0.0" + cookie "~0.4.1" + debug "~4.1.0" + engine.io-parser "~2.2.0" + ws "~7.4.2" enquirer@~2.3.6: version "2.3.6" @@ -5232,16 +3325,6 @@ enquirer@~2.3.6: dependencies: ansi-colors "^4.1.1" -entities@^1.1.1: - version "1.1.2" - resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.2.tgz#bdfa735299664dfafd34529ed4f8522a275fea56" - integrity sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w== - -entities@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55" - integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A== - env-paths@^2.2.0: version "2.2.1" resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.2.1.tgz#420399d416ce1fbe9bc0a07c62fa68d67fd0f8f2" @@ -5257,13 +3340,6 @@ err-code@^2.0.2: resolved "https://registry.yarnpkg.com/err-code/-/err-code-2.0.3.tgz#23c2f3b756ffdfc608d30e27c9a941024807e7f9" integrity sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA== -errno@^0.1.3, errno@~0.1.7: - version "0.1.8" - resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.8.tgz#8bb3e9c7d463be4976ff888f76b4809ebc2e811f" - integrity sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A== - dependencies: - prr "~1.0.1" - error-ex@^1.3.1: version "1.3.2" resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" @@ -5271,7 +3347,14 @@ error-ex@^1.3.1: dependencies: is-arrayish "^0.2.1" -es-abstract@^1.17.2, es-abstract@^1.19.0, es-abstract@^1.19.2, es-abstract@^1.20.1, es-abstract@^1.20.4: +error-stack-parser@^1.3.3: + version "1.3.6" + resolved "https://registry.yarnpkg.com/error-stack-parser/-/error-stack-parser-1.3.6.tgz#e0e73b93e417138d1cd7c0b746b1a4a14854c292" + integrity sha512-xhuSYd8wLgOXwNgjcPeXMPL/IiiA1Huck+OPvClpJViVNNlJVtM41o+1emp7bPvlCJwCatFX2DWc05/DgfbWzA== + dependencies: + stackframe "^0.3.1" + +es-abstract@^1.19.0, es-abstract@^1.20.4: version "1.21.1" resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.21.1.tgz#e6105a099967c08377830a0c9cb589d570dd86c6" integrity sha512-QudMsPOz86xYz/1dG1OuGBKOELjCh99IIWHLzy5znUB6j8xG2yMA7bfTV86VSqKF+Y/H08vQPR+9jyXpuC6hfg== @@ -5355,26 +3438,6 @@ es-abstract@^1.22.1: unbox-primitive "^1.0.2" which-typed-array "^1.1.13" -es-array-method-boxes-properly@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz#873f3e84418de4ee19c5be752990b2e44718d09e" - integrity sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA== - -es-get-iterator@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/es-get-iterator/-/es-get-iterator-1.1.3.tgz#3ef87523c5d464d41084b2c3c9c214f1199763d6" - integrity sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw== - dependencies: - call-bind "^1.0.2" - get-intrinsic "^1.1.3" - has-symbols "^1.0.3" - is-arguments "^1.1.1" - is-map "^2.0.2" - is-set "^2.0.2" - is-string "^1.0.7" - isarray "^2.0.5" - stop-iteration-iterator "^1.0.0" - es-set-tostringtag@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz#338d502f6f674301d710b80c8592de8a15f09cd8" @@ -5400,34 +3463,17 @@ es-to-primitive@^1.2.1: is-date-object "^1.0.1" is-symbol "^1.0.2" -es6-promise@^4.0.3: - version "4.2.8" - resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a" - integrity sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w== - -es6-promisify@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/es6-promisify/-/es6-promisify-5.0.0.tgz#5109d62f3e56ea967c4b63505aef08291c8a5203" - integrity sha512-C+d6UdsYDk0lMebHNR4S2NybQMMngAOnOwYBQjTOiv0MkoJMP0Myw2mgpDLBcpfCmRLxyFqYhS/CfOENq4SJhQ== - dependencies: - es6-promise "^4.0.3" - escalade@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== -escape-html@~1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" - integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow== - escape-string-regexp@5.0.0, escape-string-regexp@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz#4683126b500b61762f2dbebace1806e8be31b1c8" integrity sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw== -escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: +escape-string-regexp@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== @@ -5442,29 +3488,18 @@ escape-string-regexp@^4.0.0: resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== -escodegen@^1.11.0, escodegen@^1.11.1: - version "1.14.3" - resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.14.3.tgz#4e7b81fba61581dc97582ed78cab7f0e8d63f503" - integrity sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw== - dependencies: - esprima "^4.0.1" - estraverse "^4.2.0" - esutils "^2.0.2" - optionator "^0.8.1" - optionalDependencies: - source-map "~0.6.1" - -escodegen@~1.9.0: - version "1.9.1" - resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.9.1.tgz#dbae17ef96c8e4bedb1356f4504fa4cc2f7cb7e2" - integrity sha512-6hTjO1NAWkHnDk3OqQ4YrCuwwmGHL9S3nPlzBOUG/R44rda3wLNrfvQ5fkSGjyhHFKM7ALPKcKGrwvCLe0lC7Q== +eshost@^9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/eshost/-/eshost-9.0.0.tgz#a576b46442c8cc25c4b296931fb21bfdcb1aec50" + integrity sha512-hq1phapnwdDoBO/o5PhcGoex9ueAY2xlQEcvA0zq/u4J+yUypFnBEh1gZABLlmdM1ZmOBDbjawUb/ICDdvyJVw== dependencies: - esprima "^3.1.3" - estraverse "^4.2.0" - esutils "^2.0.2" - optionator "^0.8.1" - optionalDependencies: - source-map "~0.6.1" + error-stack-parser "^1.3.3" + is-symlink "^0.1.1" + recast "^0.17.5" + selenium-webdriver "^4.0.0-alpha.1" + server-destroy "^1.0.1" + socket.io "^2.3.0" + unique-temp-dir "^1.0.0" eslint-config-airbnb-base@^15.0.0: version "15.0.0" @@ -5524,7 +3559,7 @@ eslint-plugin-eslint-comments@^3.1.2: escape-string-regexp "^1.0.5" ignore "^5.0.5" -eslint-plugin-import@^2.29.0: +eslint-plugin-import@^2.27.5, eslint-plugin-import@^2.29.0: version "2.29.1" resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.29.1.tgz#d45b37b5ef5901d639c15270d74d46d161150643" integrity sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw== @@ -5567,14 +3602,6 @@ eslint-rule-docs@^1.1.5: resolved "https://registry.yarnpkg.com/eslint-rule-docs/-/eslint-rule-docs-1.1.235.tgz#be6ef1fc3525f17b3c859ae2997fedadc89bfb9b" integrity sha512-+TQ+x4JdTnDoFEXXb3fDvfGOwnyNV7duH8fXWTPD1ieaBmB8omj7Gw/pMBBu4uI2uJCCU8APDaQJzWuXnTsH4A== -eslint-scope@^4.0.3: - version "4.0.3" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-4.0.3.tgz#ca03833310f6889a3264781aa82e63eb9cfe7848" - integrity sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg== - dependencies: - esrecurse "^4.1.0" - estraverse "^4.1.1" - eslint-scope@^7.2.2: version "7.2.2" resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.2.2.tgz#deb4f92563390f32006894af62a22dba1c46423f" @@ -5636,11 +3663,6 @@ eslint@^8.46.0: strip-ansi "^6.0.1" text-table "^0.2.0" -esm@^3.2.25: - version "3.2.25" - resolved "https://registry.yarnpkg.com/esm/-/esm-3.2.25.tgz#342c18c29d56157688ba5ce31f8431fbb795cc10" - integrity sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA== - espree@^9.6.0, espree@^9.6.1: version "9.6.1" resolved "https://registry.yarnpkg.com/espree/-/espree-9.6.1.tgz#a2a17b8e434690a5432f2f8018ce71d331a48c6f" @@ -5650,12 +3672,7 @@ espree@^9.6.0, espree@^9.6.1: acorn-jsx "^5.3.2" eslint-visitor-keys "^3.4.1" -esprima@^3.1.3: - version "3.1.3" - resolved "https://registry.yarnpkg.com/esprima/-/esprima-3.1.3.tgz#fdca51cee6133895e3c88d535ce49dbff62a4633" - integrity sha512-AWwVMNxwhN8+NIPQzAQZCm7RkLC4RbM3B1OobMuyp3i+w73X57KCKaVIxaRZb+DYCojq7rspo+fmuQfAboyhFg== - -esprima@^4.0.0, esprima@^4.0.1: +esprima@^4.0.0, esprima@~4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== @@ -5667,28 +3684,18 @@ esquery@^1.0.1, esquery@^1.4.2, esquery@^1.5.0: dependencies: estraverse "^5.1.0" -esrecurse@^4.1.0, esrecurse@^4.3.0: +esrecurse@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== dependencies: estraverse "^5.2.0" -estraverse@^4.1.1, estraverse@^4.2.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" - integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== - estraverse@^5.1.0, estraverse@^5.2.0: version "5.3.0" resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== -estree-walker@^0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-0.6.1.tgz#53049143f40c6eb918b23671d1fe3219f3a1b362" - integrity sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w== - estree-walker@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-1.0.1.tgz#31bc5d612c96b704106b477e6dd5d8aa138cb700" @@ -5704,29 +3711,11 @@ esutils@^2.0.2, esutils@^2.0.3: resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== -etag@~1.8.1: - version "1.8.1" - resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" - integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg== - eventemitter3@^4.0.4: version "4.0.7" resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== -events@^3.0.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" - integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== - -evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz#7fcbdb198dc71959432efe13842684e0525acb02" - integrity sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA== - dependencies: - md5.js "^1.3.4" - safe-buffer "^5.1.1" - execa@^5.0.0: version "5.1.1" resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" @@ -5792,13 +3781,6 @@ expand-brackets@^2.1.4: snapdragon "^0.8.1" to-regex "^3.0.1" -expand-tilde@^2.0.0, expand-tilde@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/expand-tilde/-/expand-tilde-2.0.2.tgz#97e801aa052df02454de46b02bf621642cdc8502" - integrity sha512-A5EmesHW6rfnZ9ysHQjPdJRni0SRar0tjtG5MNtm9n5TUvsYU8oozprtRD4AqHxcZWWlVuAmQo2nWKfN9oyjTw== - dependencies: - homedir-polyfill "^1.0.1" - extend-shallow@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" @@ -5814,11 +3796,6 @@ extend-shallow@^3.0.0, extend-shallow@^3.0.2: assign-symbols "^1.0.0" is-extendable "^1.0.1" -extend@~3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" - integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== - external-editor@^3.0.3: version "3.1.0" resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.1.0.tgz#cb03f740befae03ea4d283caed2741a83f335495" @@ -5842,29 +3819,6 @@ extglob@^2.0.4: snapdragon "^0.8.1" to-regex "^3.0.1" -extract-zip@^1.6.6: - version "1.7.0" - resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-1.7.0.tgz#556cc3ae9df7f452c493a0cfb51cc30277940927" - integrity sha512-xoh5G1W/PB0/27lXgMQyIhP5DSY/LhoCsOyZgb+6iMmRtCwVBo55uKaMoEYrDCKQhWvqEip5ZPKAc6eFNyf/MA== - dependencies: - concat-stream "^1.6.2" - debug "^2.6.9" - mkdirp "^0.5.4" - yauzl "^2.10.0" - -extsprintf@1.3.0, extsprintf@^1.2.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" - integrity sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g== - -falafel@^2.1.0: - version "2.2.5" - resolved "https://registry.yarnpkg.com/falafel/-/falafel-2.2.5.tgz#3ccb4970a09b094e9e54fead2deee64b4a589d56" - integrity sha512-HuC1qF9iTnHDnML9YZAdCDQwT0yKl/U55K4XSUXqGAA2GLoafFgWRqdAbhWJxXaYD4pyoVxAJ8wH670jMpI9DQ== - dependencies: - acorn "^7.1.1" - isarray "^2.0.1" - fast-check@^3.0.0: version "3.1.1" resolved "https://registry.yarnpkg.com/fast-check/-/fast-check-3.1.1.tgz#72c5ae7022a4e86504762e773adfb8a5b0b01252" @@ -5904,7 +3858,7 @@ fast-glob@3.2.7: merge2 "^1.3.0" micromatch "^4.0.4" -fast-glob@^2.0.2, fast-glob@^2.2.2: +fast-glob@^2.0.2: version "2.2.7" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-2.2.7.tgz#6953857c3afa475fff92ee6015d52da70a4cd39d" integrity sha512-g1KuQwHOZAmOZMuBtHdxDtju+T2RT8jgCC9aANsbpdiDDTSnjgfuVsIBNKbUeJI3oKMRExcfNDtJl4OhbffMsw== @@ -5932,21 +3886,11 @@ fast-json-stable-stringify@^2.0.0: resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== -fast-levenshtein@^2.0.6, fast-levenshtein@~2.0.6: +fast-levenshtein@^2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== -fast-safe-stringify@^2.0.7: - version "2.1.1" - resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz#c406a83b6e70d9e35ce3b30a81141df30aeba884" - integrity sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA== - -fastparse@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/fastparse/-/fastparse-1.1.2.tgz#91728c5a5942eced8531283c79441ee4122c35a9" - integrity sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ== - fastq@^1.6.0: version "1.15.0" resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.15.0.tgz#d04d07c6a2a68fe4599fea8d2e103a937fae6b3a" @@ -5954,18 +3898,6 @@ fastq@^1.6.0: dependencies: reusify "^1.0.4" -fd-slicer@~1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e" - integrity sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g== - dependencies: - pend "~1.2.0" - -figgy-pudding@^3.5.1: - version "3.5.2" - resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.2.tgz#b4eee8148abb01dcf1d1ac34367d59e12fa61d6e" - integrity sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw== - figures@3.2.0, figures@^3.0.0, figures@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af" @@ -5988,11 +3920,6 @@ file-entry-cache@^6.0.1: dependencies: flat-cache "^3.0.4" -file-uri-to-path@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" - integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw== - filelist@^1.0.1: version "1.0.4" resolved "https://registry.yarnpkg.com/filelist/-/filelist-1.0.4.tgz#f78978a1e944775ff9e62e744424f215e58352b5" @@ -6000,11 +3927,6 @@ filelist@^1.0.1: dependencies: minimatch "^5.0.1" -filesize@^3.6.0: - version "3.6.1" - resolved "https://registry.yarnpkg.com/filesize/-/filesize-3.6.1.tgz#090bb3ee01b6f801a8a8be99d31710b3422bb317" - integrity sha512-7KjR1vv6qnicaPMi1iiTcI85CyYwRO/PSFCu6SvqL8jN2Wjt/NIYQTFtFs7fSDCYOstUkEWIQGFUg5YZQfjlcg== - fill-range@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" @@ -6022,15 +3944,6 @@ fill-range@^7.0.1: dependencies: to-regex-range "^5.0.1" -find-cache-dir@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-2.1.0.tgz#8d0f94cd13fe43c6c7c261a0d86115ca918c05f7" - integrity sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ== - dependencies: - commondir "^1.0.1" - make-dir "^2.0.0" - pkg-dir "^3.0.0" - find-up@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7" @@ -6076,16 +3989,6 @@ find-yarn-workspace-root@^2.0.0: dependencies: micromatch "^4.0.2" -findup-sync@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/findup-sync/-/findup-sync-3.0.0.tgz#17b108f9ee512dfb7a5c7f3c8b27ea9e1a9c08d1" - integrity sha512-YbffarhcicEhOrm4CtrwdKBdCuz576RLdhJDsIfvNtxUuhdRet1qZcsMjqbePtAseKdAnDyM/IyXbu7PRPRLYg== - dependencies: - detect-file "^1.0.0" - is-glob "^4.0.0" - micromatch "^3.0.4" - resolve-dir "^1.0.1" - flat-cache@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.0.4.tgz#61b0338302b2fe9f957dcc32fc2a87f1c3048b11" @@ -6104,14 +4007,6 @@ flatted@^3.1.0: resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.7.tgz#609f39207cb614b89d0765b477cb2d437fbf9787" integrity sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ== -flush-write-stream@^1.0.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/flush-write-stream/-/flush-write-stream-1.1.1.tgz#8dd7d873a1babc207d94ead0c2e0e44276ebf2e8" - integrity sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w== - dependencies: - inherits "^2.0.3" - readable-stream "^2.3.6" - follow-redirects@^1.15.0: version "1.15.2" resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13" @@ -6137,11 +4032,6 @@ foreground-child@^2.0.0: cross-spawn "^7.0.0" signal-exit "^3.0.2" -forever-agent@~0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" - integrity sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw== - form-data@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" @@ -6151,15 +4041,6 @@ form-data@^4.0.0: combined-stream "^1.0.8" mime-types "^2.1.12" -form-data@~2.3.2: - version "2.3.3" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" - integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ== - dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.6" - mime-types "^2.1.12" - fragment-cache@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19" @@ -6167,19 +4048,6 @@ fragment-cache@^0.2.1: dependencies: map-cache "^0.2.2" -fresh@0.5.2: - version "0.5.2" - resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" - integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q== - -from2@^2.1.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/from2/-/from2-2.3.0.tgz#8bfb5502bde4a4d36cfdeea007fcca21d7e382af" - integrity sha512-OMcX/4IC/uqEPVgGeyfN22LJk6AZrMkRZHxcHBMBvHScDGgwTm2GT2Wkgtocyd3JfZffjj2kYUDXXII0Fk9W0g== - dependencies: - inherits "^2.0.1" - readable-stream "^2.0.0" - fs-constants@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" @@ -6220,29 +4088,11 @@ fs-minipass@^2.0.0, fs-minipass@^2.1.0: dependencies: minipass "^3.0.0" -fs-write-stream-atomic@^1.0.8: - version "1.0.10" - resolved "https://registry.yarnpkg.com/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz#b47df53493ef911df75731e70a9ded0189db40c9" - integrity sha512-gehEzmPn2nAwr39eay+x3X34Ra+M2QlVUTLhkXPjWdeO8RF9kszk116avgBJM3ZyNHgHXBNx+VmPaFC36k0PzA== - dependencies: - graceful-fs "^4.1.2" - iferr "^0.1.5" - imurmurhash "^0.1.4" - readable-stream "1 || 2" - fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== -fsevents@^1.2.7: - version "1.2.13" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.2.13.tgz#f325cb0455592428bcf11b383370ef70e3bfcc38" - integrity sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw== - dependencies: - bindings "^1.5.0" - nan "^2.12.1" - fsevents@~2.3.2: version "2.3.2" resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" @@ -6297,11 +4147,6 @@ gauge@^4.0.3: strip-ansi "^6.0.1" wide-align "^1.1.5" -get-assigned-identifiers@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/get-assigned-identifiers/-/get-assigned-identifiers-1.2.0.tgz#6dbf411de648cbaf8d9169ebb0d2d576191e2ff1" - integrity sha512-mBBwmeGTrxEMO4pMaaf/uUEFHnYtwr8FTe8Y/mer4rcV/bye0qGm6pw1bGZFGStxC5O76c5ZAVBGnqHmOaJpdQ== - get-caller-file@^2.0.1, get-caller-file@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" @@ -6326,11 +4171,6 @@ get-intrinsic@^1.2.1, get-intrinsic@^1.2.2: has-symbols "^1.0.3" hasown "^2.0.0" -get-package-type@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" - integrity sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q== - get-pkg-repo@^4.0.0: version "4.2.1" resolved "https://registry.yarnpkg.com/get-pkg-repo/-/get-pkg-repo-4.2.1.tgz#75973e1c8050c73f48190c52047c4cee3acbf385" @@ -6341,11 +4181,6 @@ get-pkg-repo@^4.0.0: through2 "^2.0.0" yargs "^16.2.0" -get-port@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/get-port/-/get-port-3.2.0.tgz#dd7ce7de187c06c8bf353796ac71e099f0980ebc" - integrity sha512-x5UJKlgeUiNT8nyo/AcnwLnZuZNcSjSw0kogRB+Whd1fjjFq4B1hySFxSFWWSn4mIBzg3sRNUDFYc4g5gjPoLg== - get-port@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/get-port/-/get-port-5.1.1.tgz#0469ed07563479de6efb986baf053dcd7d4e3193" @@ -6374,13 +4209,6 @@ get-value@^2.0.3, get-value@^2.0.6: resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" integrity sha512-Ln0UQDlxH1BapMu3GPtf7CuYNwRZf2gwCuPqbyG6pB8WfmFpzqcy4xtAaAMUhnNqjMKTiCPZG2oMT3YSx8U2NA== -getpass@^0.1.1: - version "0.1.7" - resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" - integrity sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng== - dependencies: - assert-plus "^1.0.0" - git-raw-commits@^2.0.8: version "2.0.11" resolved "https://registry.yarnpkg.com/git-raw-commits/-/git-raw-commits-2.0.11.tgz#bc3576638071d18655e1cc60d7f524920008d723" @@ -6469,7 +4297,7 @@ glob@7.1.4: once "^1.3.0" path-is-absolute "^1.0.0" -glob@^7.0.0, glob@^7.1.0, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6, glob@^7.2.3: +glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: version "7.2.3" resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== @@ -6481,7 +4309,7 @@ glob@^7.0.0, glob@^7.1.0, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6, gl once "^1.3.0" path-is-absolute "^1.0.0" -glob@^8.0.1, glob@^8.0.3: +glob@^8.0.1: version "8.0.3" resolved "https://registry.yarnpkg.com/glob/-/glob-8.0.3.tgz#415c6eb2deed9e502c68fa44a272e6da6eeca42e" integrity sha512-ull455NHSHI/Y1FqGaaYFaLGkNMMJbavMrEGFXG/PGrg6y7sutWHUHrz6gy6WEBH6akM1M414dWKCNs+IhKdiQ== @@ -6492,42 +4320,6 @@ glob@^8.0.1, glob@^8.0.3: minimatch "^5.0.1" once "^1.3.0" -global-modules@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/global-modules/-/global-modules-1.0.0.tgz#6d770f0eb523ac78164d72b5e71a8877265cc3ea" - integrity sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg== - dependencies: - global-prefix "^1.0.1" - is-windows "^1.0.1" - resolve-dir "^1.0.0" - -global-modules@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/global-modules/-/global-modules-2.0.0.tgz#997605ad2345f27f51539bea26574421215c7780" - integrity sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A== - dependencies: - global-prefix "^3.0.0" - -global-prefix@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/global-prefix/-/global-prefix-1.0.2.tgz#dbf743c6c14992593c655568cb66ed32c0122ebe" - integrity sha512-5lsx1NUDHtSjfg0eHlmYvZKv8/nVqX4ckFbM+FrGcQ+04KWcWFo9P5MxPZYSzUvyzmdTbI7Eix8Q4IbELDqzKg== - dependencies: - expand-tilde "^2.0.2" - homedir-polyfill "^1.0.1" - ini "^1.3.4" - is-windows "^1.0.1" - which "^1.2.14" - -global-prefix@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/global-prefix/-/global-prefix-3.0.0.tgz#fc85f73064df69f50421f47f883fe5b913ba9b97" - integrity sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg== - dependencies: - ini "^1.3.5" - kind-of "^6.0.2" - which "^1.3.1" - globals@^11.1.0: version "11.12.0" resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" @@ -6595,13 +4387,10 @@ graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6 resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c" integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== -grapheme-breaker@^0.3.2: - version "0.3.2" - resolved "https://registry.yarnpkg.com/grapheme-breaker/-/grapheme-breaker-0.3.2.tgz#5b9e6b78c3832452d2ba2bb1cb830f96276410ac" - integrity sha512-mB6rwkw1Z7z4z2RkFFTd/+q6Ug1gnCgjKAervAKgBeNI1mSr8E5EUWoYzFNOZsLHFArLfpk+O8X8qXC7uvuawQ== - dependencies: - brfs "^1.2.0" - unicode-trie "^0.3.1" +graceful-fs@^4.1.9: + version "4.2.11" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" + integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== graphemer@^1.4.0: version "1.4.0" @@ -6620,48 +4409,27 @@ handlebars@^4.7.7: optionalDependencies: uglify-js "^3.1.4" -har-schema@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" - integrity sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q== - -har-validator@~5.1.3: - version "5.1.5" - resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.5.tgz#1f0803b9f8cb20c0fa13822df1ecddb36bde1efd" - integrity sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w== - dependencies: - ajv "^6.12.3" - har-schema "^2.0.0" - hard-rejection@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/hard-rejection/-/hard-rejection-2.1.0.tgz#1c6eda5c1685c63942766d79bb40ae773cecd883" integrity sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA== -has-ansi@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" - integrity sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg== - dependencies: - ansi-regex "^2.0.0" - has-bigints@^1.0.1, has-bigints@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.2.tgz#0871bd3e3d51626f6ca0966668ba35d5602d6eaa" integrity sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ== -has-dynamic-import@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/has-dynamic-import/-/has-dynamic-import-2.0.1.tgz#9bca87846aa264f2ad224fcd014946f5e5182f52" - integrity sha512-X3fbtsZmwb6W7fJGR9o7x65fZoodygCrZ3TVycvghP62yYQfS0t4RS0Qcz+j5tQYUKeSWS09tHkWW6WhFV3XhQ== +has-binary2@~1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has-binary2/-/has-binary2-1.0.3.tgz#7776ac627f3ea77250cfc332dab7ddf5e4f5d11d" + integrity sha512-G1LWKhDSvhGeAQ8mPVQlqNcOB2sJdwATtZKl2pDKKHfpf/rYj24lkinxf69blJbnsvtqqNU+L3SL50vzZhXOnw== dependencies: - call-bind "^1.0.2" - get-intrinsic "^1.1.1" + isarray "2.0.1" -has-flag@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-1.0.0.tgz#9d9e793165ce017a00f00418c43f942a7b1d11fa" - integrity sha512-DyYHfIYwAJmjAjSSPKANxI8bFY9YtFrgkAfinBojQ8YJTOuOuav64tMUJv584SES4xl74PmuaevIyaLESHdTAA== +has-cors@1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/has-cors/-/has-cors-1.1.0.tgz#5e474793f7ea9843d1bb99c23eef49ff126fff39" + integrity sha512-g5VNKdkFuUuVCP9gYfDJHjK2nqdQJ7aDLTnycnc2+RvsOQbuLdF5pm7vuE5J76SEBIQjs4kQY/BWq74JUmjbXA== has-flag@^3.0.0: version "3.0.0" @@ -6673,13 +4441,6 @@ has-flag@^4.0.0: resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== -has-glob@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/has-glob/-/has-glob-1.0.0.tgz#9aaa9eedbffb1ba3990a7b0010fb678ee0081207" - integrity sha512-D+8A457fBShSEI3tFCj65PAbT++5sKiFtdCdOam0gnfBgw9D277OERk+HM9qYJXmdVLZ/znez10SqHN0BBQ50g== - dependencies: - is-glob "^3.0.0" - has-property-descriptors@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz#610708600606d36961ed04c196193b6a607fa861" @@ -6699,7 +4460,7 @@ has-proto@^1.0.1: resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.1.tgz#1885c1305538958aff469fef37937c22795408e0" integrity sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg== -has-symbols@^1.0.1, has-symbols@^1.0.2, has-symbols@^1.0.3: +has-symbols@^1.0.2, has-symbols@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== @@ -6747,30 +4508,13 @@ has-values@^1.0.0: is-number "^3.0.0" kind-of "^4.0.0" -has@^1.0.0, has@^1.0.1, has@^1.0.3: +has@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== dependencies: function-bind "^1.1.1" -hash-base@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-3.1.0.tgz#55c381d9e06e1d2997a883b4a3fddfe7f0d3af33" - integrity sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA== - dependencies: - inherits "^2.0.4" - readable-stream "^3.6.0" - safe-buffer "^5.2.0" - -hash.js@^1.0.0, hash.js@^1.0.3: - version "1.1.7" - resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42" - integrity sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA== - dependencies: - inherits "^2.0.3" - minimalistic-assert "^1.0.1" - hasown@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.0.tgz#f4c513d454a57b7c7e1650778de226b11700546c" @@ -6778,27 +4522,6 @@ hasown@^2.0.0: dependencies: function-bind "^1.1.2" -hex-color-regex@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/hex-color-regex/-/hex-color-regex-1.1.0.tgz#4c06fccb4602fe2602b3c93df82d7e7dbf1a8a8e" - integrity sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ== - -hmac-drbg@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" - integrity sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg== - dependencies: - hash.js "^1.0.3" - minimalistic-assert "^1.0.0" - minimalistic-crypto-utils "^1.0.1" - -homedir-polyfill@^1.0.1: - version "1.0.3" - resolved "https://registry.yarnpkg.com/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz#743298cef4e5af3e194161fbadcc2151d3a058e8" - integrity sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA== - dependencies: - parse-passwd "^1.0.0" - hosted-git-info@^2.1.4: version "2.8.9" resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9" @@ -6825,91 +4548,16 @@ hosted-git-info@^5.0.0: dependencies: lru-cache "^7.5.1" -hsl-regex@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/hsl-regex/-/hsl-regex-1.0.0.tgz#d49330c789ed819e276a4c0d272dffa30b18fe6e" - integrity sha512-M5ezZw4LzXbBKMruP+BNANf0k+19hDQMgpzBIYnya//Al+fjNct9Wf3b1WedLqdEs2hKBvxq/jh+DsHJLj0F9A== - -hsla-regex@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/hsla-regex/-/hsla-regex-1.0.0.tgz#c1ce7a3168c8c6614033a4b5f7877f3b225f9c38" - integrity sha512-7Wn5GMLuHBjZCb2bTmnDOycho0p/7UVaAeqXZGbHrBCl6Yd/xDhQJAXe6Ga9AXJH2I5zY1dEdYw2u1UptnSBJA== - -html-encoding-sniffer@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-1.0.2.tgz#e70d84b94da53aa375e11fe3a351be6642ca46f8" - integrity sha512-71lZziiDnsuabfdYiUeWdCVyKuqwWi23L8YeIgV9jSSZHCtb6wB1BKWooH7L3tn4/FuZJMVWyNaIDr4RGmaSYw== - dependencies: - whatwg-encoding "^1.0.1" - html-escaper@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== -html-tags@^1.0.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/html-tags/-/html-tags-1.2.0.tgz#c78de65b5663aa597989dd2b7ab49200d7e4db98" - integrity sha512-uVteDXUCs08M7QJx0eY6ue7qQztwIfknap81vAtNob2sdEPKa8PjPinx0vxbs2JONPamovZjMvKZWNW44/PBKg== - -htmlescape@^1.1.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/htmlescape/-/htmlescape-1.1.1.tgz#3a03edc2214bca3b66424a3e7959349509cb0351" - integrity sha512-eVcrzgbR4tim7c7soKQKtxa/kQM4TzjnlU83rcZ9bHU6t31ehfV7SktN6McWgwPWg+JYMA/O3qpGxBvFq1z2Jg== - -htmlnano@^0.2.2: - version "0.2.9" - resolved "https://registry.yarnpkg.com/htmlnano/-/htmlnano-0.2.9.tgz#5723a26afa0d1343ea8648c2d5be8170744af9a7" - integrity sha512-jWTtP3dCd7R8x/tt9DK3pvpcQd7HDMcRPUqPxr/i9989q2k5RHIhmlRDFeyQ/LSd8IKrteG8Ce5g0Ig4eGIipg== - dependencies: - cssnano "^4.1.11" - posthtml "^0.15.1" - purgecss "^2.3.0" - relateurl "^0.2.7" - srcset "^3.0.0" - svgo "^1.3.2" - terser "^5.6.1" - timsort "^0.3.0" - uncss "^0.17.3" - -htmlparser2@^3.9.2: - version "3.10.1" - resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.10.1.tgz#bd679dc3f59897b6a34bb10749c855bb53a9392f" - integrity sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ== - dependencies: - domelementtype "^1.3.1" - domhandler "^2.3.0" - domutils "^1.5.1" - entities "^1.1.1" - inherits "^2.0.1" - readable-stream "^3.1.1" - -htmlparser2@^6.0.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-6.1.0.tgz#c4d762b6c3371a05dbe65e94ae43a9f845fb8fb7" - integrity sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A== - dependencies: - domelementtype "^2.0.1" - domhandler "^4.0.0" - domutils "^2.5.2" - entities "^2.0.0" - http-cache-semantics@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390" integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ== -http-errors@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3" - integrity sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ== - dependencies: - depd "2.0.0" - inherits "2.0.4" - setprototypeof "1.2.0" - statuses "2.0.1" - toidentifier "1.0.1" - http-proxy-agent@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz#5129800203520d434f142bc78ff3c170800f2b43" @@ -6919,28 +4567,6 @@ http-proxy-agent@^5.0.0: agent-base "6" debug "4" -http-signature@~1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" - integrity sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ== - dependencies: - assert-plus "^1.0.0" - jsprim "^1.2.2" - sshpk "^1.7.0" - -https-browserify@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73" - integrity sha512-J+FkSdyD+0mA0N+81tMotaRMfSL9SGi+xpD3T6YApKsc3bGSXJlfXri3VyFOeYkfLRQisDk1W+jIFFKBeUBbBg== - -https-proxy-agent@^2.2.1: - version "2.2.4" - resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz#4ee7a737abd92678a293d9b34a1af4d0d08c787b" - integrity sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg== - dependencies: - agent-base "^4.3.0" - debug "^3.1.0" - https-proxy-agent@^5.0.0: version "5.0.1" resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6" @@ -6971,7 +4597,7 @@ humanize-ms@^1.2.1: dependencies: ms "^2.0.0" -iconv-lite@0.4.24, iconv-lite@^0.4.24: +iconv-lite@^0.4.24: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== @@ -6985,21 +4611,11 @@ iconv-lite@^0.6.2: dependencies: safer-buffer ">= 2.1.2 < 3.0.0" -icss-replace-symbols@1.1.0, icss-replace-symbols@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz#06ea6f83679a7749e386cfe1fe812ae5db223ded" - integrity sha512-chIaY3Vh2mh2Q3RGXttaDIzeiPvaVXJ+C4DAh/w3c37SKZ/U6PGMmuicR2EQQp9bKG8zLMCl7I+PtIoOOPp8Gg== - -ieee754@^1.1.13, ieee754@^1.1.4: +ieee754@^1.1.13: version "1.2.1" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== -iferr@^0.1.5: - version "0.1.5" - resolved "https://registry.yarnpkg.com/iferr/-/iferr-0.1.5.tgz#c60eed69e6d8fdb6b3104a1fcbca1c192dc5b501" - integrity sha512-DUNFN5j7Tln0D+TxzloUjKB+CtVu6myn0JEFak6dG18mNt9YkQ6lzGCdafwofISZ1lLF3xRHJ98VKy9ynkcFaA== - ignore-by-default@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/ignore-by-default/-/ignore-by-default-2.1.0.tgz#c0e0de1a99b6065bdc93315a6f728867981464db" @@ -7022,13 +4638,10 @@ ignore@^5.0.4, ignore@^5.0.5, ignore@^5.2.0, ignore@^5.2.4: resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.4.tgz#a291c0c6178ff1b960befe47fcdec301674a6324" integrity sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ== -import-fresh@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-2.0.0.tgz#d81355c15612d386c61f9ddd3922d4304822a546" - integrity sha512-eZ5H8rcgYazHbKC3PG4ClHNykCSxtAhxSSEM+2mb+7evD2CKF5V7c0dNum7AdpDh0ZdICwZY9sRSn8f+KH96sg== - dependencies: - caller-path "^2.0.0" - resolve-from "^3.0.0" +immediate@~3.0.5: + version "3.0.6" + resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.0.6.tgz#9db1dbd0faf8de6fbe0f5dd5e56bb606280de69b" + integrity sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ== import-fresh@^3.2.1: version "3.3.0" @@ -7038,14 +4651,6 @@ import-fresh@^3.2.1: parent-module "^1.0.0" resolve-from "^4.0.0" -import-local@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/import-local/-/import-local-2.0.0.tgz#55070be38a5993cf18ef6db7e961f5bee5c5a09d" - integrity sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ== - dependencies: - pkg-dir "^3.0.0" - resolve-cwd "^2.0.0" - import-local@^3.0.2: version "3.1.0" resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.1.0.tgz#b4479df8a5fd44f6cdce24070675676063c95cb4" @@ -7074,12 +4679,12 @@ indent-string@^5.0.0: resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-5.0.0.tgz#4fd2980fccaf8622d14c64d694f4cf33c81951a5" integrity sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg== -indexes-of@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/indexes-of/-/indexes-of-1.0.1.tgz#f30f716c8e2bd346c7b67d3df3915566a7c05607" - integrity sha512-bup+4tap3Hympa+JBJUG7XuOsdNQ6fxt0MHyXMKuLBKn0OqsTfvUxkUrroEX1+B2VsSHvCjiIcZVxRtYa4nllA== +indexof@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/indexof/-/indexof-0.0.1.tgz#82dc336d232b9062179d05ab3293a66059fd435d" + integrity sha512-i0G7hLJ1z0DE8dsqJa2rycj9dBmNKgXBvotXtZYXakU9oivfB9Uj2ZBC27qqef2U58/ZLwalxa1X/RDCdkHtVg== -infer-owner@^1.0.3, infer-owner@^1.0.4: +infer-owner@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/infer-owner/-/infer-owner-1.0.4.tgz#c4cefcaa8e51051c2a40ba2ce8a3d27295af9467" integrity sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A== @@ -7092,22 +4697,12 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.1, inherits@~2.0.3, inherits@~2.0.4: +inherits@2, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== -inherits@2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1" - integrity sha512-8nWq2nLTAwd02jTqJExUYFSD/fKq6VH9Y/oG2accc/kdI0V98Bag8d5a4gi3XHz73rDWa2PvTtvcWYquKqSENA== - -inherits@2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" - integrity sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw== - -ini@^1.3.2, ini@^1.3.4, ini@^1.3.5: +ini@^1.3.2, ini@^1.3.4: version "1.3.8" resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== @@ -7125,13 +4720,6 @@ init-package-json@^3.0.2: validate-npm-package-license "^3.0.4" validate-npm-package-name "^4.0.0" -inline-source-map@~0.6.0: - version "0.6.2" - resolved "https://registry.yarnpkg.com/inline-source-map/-/inline-source-map-0.6.2.tgz#f9393471c18a79d1724f863fa38b586370ade2a5" - integrity sha512-0mVWSSbNDvedDWIN4wxLsdPM4a7cIPcpyMxj3QZ406QRwQ6ePGB1YIHxVPjqpcUGbWQ5C+nHTwGNWAGvt7ggVA== - dependencies: - source-map "~0.5.3" - inquirer-autocomplete-prompt@^1.0.1: version "1.4.0" resolved "https://registry.yarnpkg.com/inquirer-autocomplete-prompt/-/inquirer-autocomplete-prompt-1.4.0.tgz#e767592f747e3d5bb6336fe71fb4094352e4c317" @@ -7183,22 +4771,6 @@ inquirer@^8.2.4: through "^2.3.6" wrap-ansi "^7.0.0" -insert-module-globals@^7.2.1: - version "7.2.1" - resolved "https://registry.yarnpkg.com/insert-module-globals/-/insert-module-globals-7.2.1.tgz#d5e33185181a4e1f33b15f7bf100ee91890d5cb3" - integrity sha512-ufS5Qq9RZN+Bu899eA9QCAYThY+gGW7oRkmb0vC93Vlyu/CFGcH0OYPEjVkDXA5FEbTt1+VWzdoOD3Ny9N+8tg== - dependencies: - JSONStream "^1.0.3" - acorn-node "^1.5.2" - combine-source-map "^0.8.0" - concat-stream "^1.6.1" - is-buffer "^1.1.0" - path-is-absolute "^1.0.1" - process "~0.11.0" - through2 "^2.0.0" - undeclared-identifiers "^1.1.2" - xtend "^4.0.0" - internal-slot@^1.0.4: version "1.0.5" resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.5.tgz#f2a2ee21f668f8627a4667f309dc0f4fb6674986" @@ -7217,18 +4789,6 @@ internal-slot@^1.0.5: hasown "^2.0.0" side-channel "^1.0.4" -interpret@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.4.0.tgz#665ab8bc4da27a774a40584e812e3e0fa45b1a1e" - integrity sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA== - -invariant@^2.2.2: - version "2.2.4" - resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" - integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA== - dependencies: - loose-envify "^1.0.0" - ip@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ip/-/ip-2.0.0.tgz#4cf4ab182fee2314c75ede1276f8c80b479936da" @@ -7239,16 +4799,6 @@ irregular-plurals@^3.2.0, irregular-plurals@^3.3.0: resolved "https://registry.yarnpkg.com/irregular-plurals/-/irregular-plurals-3.3.0.tgz#67d0715d4361a60d9fd9ee80af3881c631a31ee2" integrity sha512-MVBLKUTangM3EfRPFROhmWQQKRDsrgI83J8GS3jXy+OwYqiR2/aoWndYQ5416jLE3uaGgLH7ncme3X9y09gZ3g== -is-absolute-url@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-absolute-url/-/is-absolute-url-2.1.0.tgz#50530dfb84fcc9aa7dbe7852e83a37b93b9f2aa6" - integrity sha512-vOx7VprsKyllwjSkLV79NIhpyLfr3jAp7VaTCMXOJHu4m0Ew1CZ2fcjASwmV1jI3BWuWHB013M48eyeldk9gYg== - -is-absolute-url@^3.0.1: - version "3.0.3" - resolved "https://registry.yarnpkg.com/is-absolute-url/-/is-absolute-url-3.0.3.tgz#96c6a22b6a23929b11ea0afb1836c36ad4a5d698" - integrity sha512-opmNIX7uFnS96NtPmhWQgQx6/NYFgsUXYMllcfzwWKUMwfo8kku1TvE6hkNcH+Q1ts5cMVrsY7j0bxXQDciu9Q== - is-accessor-descriptor@^0.1.6: version "0.1.6" resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6" @@ -7263,14 +4813,6 @@ is-accessor-descriptor@^1.0.0: dependencies: kind-of "^6.0.0" -is-arguments@^1.0.4, is-arguments@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.1.tgz#15b3f88fda01f2a97fec84ca761a560f123efa9b" - integrity sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA== - dependencies: - call-bind "^1.0.2" - has-tostringtag "^1.0.0" - is-array-buffer@^3.0.1, is-array-buffer@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/is-array-buffer/-/is-array-buffer-3.0.2.tgz#f2653ced8412081638ecb0ebbd0c41c6e0aecbbe" @@ -7285,11 +4827,6 @@ is-arrayish@^0.2.1: resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" integrity sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg== -is-arrayish@^0.3.1: - version "0.3.2" - resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.3.2.tgz#4574a2ae56f7ab206896fb431eaeed066fdf8f03" - integrity sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ== - is-bigint@^1.0.1: version "1.0.4" resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.4.tgz#08147a1875bc2b32005d41ccd8291dffc6691df3" @@ -7297,13 +4834,6 @@ is-bigint@^1.0.1: dependencies: has-bigints "^1.0.1" -is-binary-path@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-1.0.1.tgz#75f16642b480f187a711c814161fd3a4a7655898" - integrity sha512-9fRVlXc0uCxEDj1nQzaWONSpbTfx0FmJfzHF7pwlI8DkWGoHBBea4Pg5Ky0ojwwxQmnSifgbKkI06Qv0Ljgj+Q== - dependencies: - binary-extensions "^1.0.0" - is-binary-path@~2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" @@ -7319,7 +4849,7 @@ is-boolean-object@^1.1.0: call-bind "^1.0.2" has-tostringtag "^1.0.0" -is-buffer@^1.1.0, is-buffer@^1.1.5: +is-buffer@^1.1.5: version "1.1.6" resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== @@ -7343,18 +4873,6 @@ is-ci@^2.0.0: dependencies: ci-info "^2.0.0" -is-color-stop@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-color-stop/-/is-color-stop-1.1.0.tgz#cfff471aee4dd5c9e158598fbe12967b5cdad345" - integrity sha512-H1U8Vz0cfXNujrJzEcvvwMDW9Ra+biSYA3ThdQvAnMLJkEHQXn6bWzLkxHtVYJ+Sdbx0b6finn3jZiaVe7MAHA== - dependencies: - css-color-names "^0.0.4" - hex-color-regex "^1.1.0" - hsl-regex "^1.0.0" - hsla-regex "^1.0.0" - rgb-regex "^1.0.1" - rgba-regex "^1.0.0" - is-core-module@^2.13.0, is-core-module@^2.13.1: version "2.13.1" resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.13.1.tgz#ad0d7532c6fea9da1ebdc82742d74525c6273384" @@ -7383,7 +4901,7 @@ is-data-descriptor@^1.0.0: dependencies: kind-of "^6.0.0" -is-date-object@^1.0.1, is-date-object@^1.0.5: +is-date-object@^1.0.1: version "1.0.5" resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.5.tgz#0841d5536e724c25597bf6ea62e1bd38298df31f" integrity sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ== @@ -7408,11 +4926,6 @@ is-descriptor@^1.0.0, is-descriptor@^1.0.2: is-data-descriptor "^1.0.0" kind-of "^6.0.2" -is-directory@^0.3.1: - version "0.3.1" - resolved "https://registry.yarnpkg.com/is-directory/-/is-directory-0.3.1.tgz#61339b6f2475fc772fd9c9d83f5c8575dc154ae1" - integrity sha512-yVChGzahRFvbkscn2MlwGismPO12i9+znNruC5gVEntG3qu0xQMzsGg/JFbrsqDOHtHFPci+V5aP5T9I+yeKqw== - is-docker@^2.0.0, is-docker@^2.1.1: version "2.2.1" resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa" @@ -7460,14 +4973,7 @@ is-fullwidth-code-point@^4.0.0: resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz#fae3167c729e7463f8461ce512b080a49268aa88" integrity sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ== -is-generator-function@^1.0.7: - version "1.0.10" - resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.0.10.tgz#f1558baf1ac17e0deea7c0415c438351ff2b3c72" - integrity sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A== - dependencies: - has-tostringtag "^1.0.0" - -is-glob@^3.0.0, is-glob@^3.1.0: +is-glob@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-3.1.0.tgz#7ba5ae24217804ac70707b96922567486cc3e84a" integrity sha512-UFpDDrPgM6qpnFNI+rh/p3bUaq9hKLZN8bMUWzxmcnZVS3omf4IPK+BrewlnWjO1WmUsMYuSjKh4UJuV4+Lqmw== @@ -7481,13 +4987,6 @@ is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: dependencies: is-extglob "^2.1.1" -is-html@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-html/-/is-html-1.1.0.tgz#e04f1c18d39485111396f9a0273eab51af218464" - integrity sha512-eoGsQVAAyvLFRKnbt4jo7Il56agsH5I04pDymPoxRp/tnna5yiIpdNzvKPOy5G1Ff0zY/jfN2hClb7ju+sOrdA== - dependencies: - html-tags "^1.0.0" - is-inside-container@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-inside-container/-/is-inside-container-1.0.0.tgz#e81fba699662eb31dbdaf26766a61d4814717ea4" @@ -7505,11 +5004,6 @@ is-lambda@^1.0.1: resolved "https://registry.yarnpkg.com/is-lambda/-/is-lambda-1.0.1.tgz#3d9877899e6a53efc0160504cde15f82e6f061d5" integrity sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ== -is-map@^2.0.1, is-map@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/is-map/-/is-map-2.0.2.tgz#00922db8c9bf73e81b7a335827bc2a43f2b91127" - integrity sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg== - is-module@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-module/-/is-module-1.0.0.tgz#3258fb69f78c14d5b815d664336b4cffb6441591" @@ -7571,17 +5065,12 @@ is-plain-object@^5.0.0: resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-5.0.0.tgz#4427f50ab3429e9025ea7d52e9043a9ef4159344" integrity sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q== -is-promise@^2.1.0: - version "2.2.2" - resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.2.2.tgz#39ab959ccbf9a774cf079f7b40c7a26f763135f1" - integrity sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ== - is-promise@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-4.0.0.tgz#42ff9f84206c1991d26debf520dd5c01042dd2f3" integrity sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ== -is-reference@^1.1.2, is-reference@^1.2.1: +is-reference@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/is-reference/-/is-reference-1.2.1.tgz#8b2dac0b371f4bc994fdeaba9eb542d03002d0b7" integrity sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ== @@ -7596,16 +5085,6 @@ is-regex@^1.1.4: call-bind "^1.0.2" has-tostringtag "^1.0.0" -is-resolvable@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.1.0.tgz#fb18f87ce1feb925169c9a407c19318a3206ed88" - integrity sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg== - -is-set@^2.0.1, is-set@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/is-set/-/is-set-2.0.2.tgz#90755fa4c2562dc1c5d4024760d6119b94ca18ec" - integrity sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g== - is-shared-array-buffer@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz#8f259c573b60b6a32d4058a1a07430c0a7344c79" @@ -7644,6 +5123,11 @@ is-symbol@^1.0.2, is-symbol@^1.0.3: dependencies: has-symbols "^1.0.2" +is-symlink@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/is-symlink/-/is-symlink-0.1.1.tgz#cfcac0712aec8c0ebfd7d9b061b14c6629782f56" + integrity sha512-HAC7xFRN4t4mVutcQfWzwAFNlD8tQFUSVwOeBLbTtgUf+Lb74AfhMT54ZcsYZ1vB5T08BLJH2z8y8im5lLcCMg== + is-text-path@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/is-text-path/-/is-text-path-1.0.1.tgz#4e1aa0fb51bfbcb3e92688001397202c1775b66e" @@ -7651,7 +5135,7 @@ is-text-path@^1.0.1: dependencies: text-extensions "^1.0.0" -is-typed-array@^1.1.10, is-typed-array@^1.1.3, is-typed-array@^1.1.9: +is-typed-array@^1.1.10, is-typed-array@^1.1.9: version "1.1.10" resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.10.tgz#36a5b5cb4189b575d1a3e4b08536bfb485801e3f" integrity sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A== @@ -7669,7 +5153,7 @@ is-typed-array@^1.1.12: dependencies: which-typed-array "^1.1.11" -is-typedarray@^1.0.0, is-typedarray@~1.0.0: +is-typedarray@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" integrity sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA== @@ -7684,21 +5168,6 @@ is-unicode-supported@^1.2.0: resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz#d824984b616c292a2e198207d4a609983842f714" integrity sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ== -is-url@^1.2.2: - version "1.2.4" - resolved "https://registry.yarnpkg.com/is-url/-/is-url-1.2.4.tgz#04a4df46d28c4cff3d73d01ff06abeb318a1aa52" - integrity sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww== - -is-valid-glob@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-valid-glob/-/is-valid-glob-1.0.0.tgz#29bf3eff701be2d4d315dbacc39bc39fe8f601aa" - integrity sha512-AhiROmoEFDSsjx8hW+5sGwgKVIORcXnrlAx/R0ZSeaPw70Vw0CqkGBBhHGL58Uox2eXnU1AnvXJl1XlyedO5bA== - -is-weakmap@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/is-weakmap/-/is-weakmap-2.0.1.tgz#5008b59bdc43b698201d18f62b37b2ca243e8cf2" - integrity sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA== - is-weakref@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.2.tgz#9529f383a9338205e89765e0392efc2f100f06f2" @@ -7706,24 +5175,11 @@ is-weakref@^1.0.2: dependencies: call-bind "^1.0.2" -is-weakset@^2.0.1: - version "2.0.2" - resolved "https://registry.yarnpkg.com/is-weakset/-/is-weakset-2.0.2.tgz#4569d67a747a1ce5a994dfd4ef6dcea76e7c0a1d" - integrity sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg== - dependencies: - call-bind "^1.0.2" - get-intrinsic "^1.1.1" - -is-windows@^1.0.1, is-windows@^1.0.2: +is-windows@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== -is-wsl@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d" - integrity sha512-gfygJYZ2gLTDlmbWMI0CE2MwnFzSN/2SZfkMlItC4K/JBlsWVDB0bO6XhqcY13YXE7iMcAJnzTCJjPiTeJJ0Mw== - is-wsl@^2.1.1, is-wsl@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271" @@ -7736,12 +5192,17 @@ isarray@0.0.1: resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" integrity sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ== -isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0: +isarray@1.0.0, isarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ== -isarray@^2.0.1, isarray@^2.0.5: +isarray@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.1.tgz#a37d94ed9cda2d59865c9f76fe596ee1f338741e" + integrity sha512-c2cu3UxbI+b6kR3fy0nRnAhodsvR9dx7U5+znCOzdj6IfP3upFURTr0Xl5BlQZNKZjEtxrmVyfSdeE3O57smoQ== + +isarray@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723" integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw== @@ -7763,11 +5224,6 @@ isobject@^3.0.0, isobject@^3.0.1: resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" integrity sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg== -isstream@~0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" - integrity sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g== - istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz#189e7909d0a39fa5a3dfad5b03f71947770191d3" @@ -7815,27 +5271,12 @@ jest-get-type@^29.4.3: resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-29.4.3.tgz#1ab7a5207c995161100b5187159ca82dd48b3dd5" integrity sha512-J5Xez4nRRMjk8emnTpWrlkyb9pfRQQanDrvWHhsR1+VUfbwxi30eVcZFlcdGInRibU4G5LwHXpI7IRHU0CY+gg== -js-beautify@^1.8.9: - version "1.14.6" - resolved "https://registry.yarnpkg.com/js-beautify/-/js-beautify-1.14.6.tgz#b23ca5d74a462c282c7711bb51150bcc97f2b507" - integrity sha512-GfofQY5zDp+cuHc+gsEXKPpNw2KbPddreEo35O6jT6i0RVK6LhsoYBhq5TvK4/n74wnA0QbK8gGd+jUZwTMKJw== - dependencies: - config-chain "^1.1.13" - editorconfig "^0.15.3" - glob "^8.0.3" - nopt "^6.0.0" - -js-levenshtein@^1.1.3: - version "1.1.6" - resolved "https://registry.yarnpkg.com/js-levenshtein/-/js-levenshtein-1.1.6.tgz#c6cee58eb3550372df8deb85fad5ce66ce01d59d" - integrity sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g== - js-string-escape@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/js-string-escape/-/js-string-escape-1.0.1.tgz#e2625badbc0d67c7533e9edc1068c587ae4137ef" integrity sha512-Smw4xcfIQ5LVjAOuJCvN/zIodzA/BBSsluuoSykP+lUvScIi4U6RJLfwHet5cxFnCswUjISV8oAXaqaJDY3chg== -"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: +js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== @@ -7847,7 +5288,7 @@ js-yaml@4.1.0, js-yaml@^4.1.0: dependencies: argparse "^2.0.1" -js-yaml@^3.10.0, js-yaml@^3.13.1, js-yaml@^3.14.1, js-yaml@^3.2.1: +js-yaml@^3.10.0, js-yaml@^3.14.1, js-yaml@^3.2.1: version "3.14.1" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== @@ -7855,59 +5296,17 @@ js-yaml@^3.10.0, js-yaml@^3.13.1, js-yaml@^3.14.1, js-yaml@^3.2.1: argparse "^1.0.7" esprima "^4.0.0" -jsbn@~0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" - integrity sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg== - jsdoc-type-pratt-parser@~4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-4.0.0.tgz#136f0571a99c184d84ec84662c45c29ceff71114" integrity sha512-YtOli5Cmzy3q4dP26GraSOeAhqecewG04hoO8DY56CH4KJ9Fvv5qKWUCCo3HZob7esJQHCv6/+bnTy72xZZaVQ== -jsdom@^14.1.0: - version "14.1.0" - resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-14.1.0.tgz#916463b6094956b0a6c1782c94e380cd30e1981b" - integrity sha512-O901mfJSuTdwU2w3Sn+74T+RnDVP+FuV5fH8tcPWyqrseRAb0s5xOtPgCFiPOtLcyK7CLIJwPyD83ZqQWvA5ng== - dependencies: - abab "^2.0.0" - acorn "^6.0.4" - acorn-globals "^4.3.0" - array-equal "^1.0.0" - cssom "^0.3.4" - cssstyle "^1.1.1" - data-urls "^1.1.0" - domexception "^1.0.1" - escodegen "^1.11.0" - html-encoding-sniffer "^1.0.2" - nwsapi "^2.1.3" - parse5 "5.1.0" - pn "^1.1.0" - request "^2.88.0" - request-promise-native "^1.0.5" - saxes "^3.1.9" - symbol-tree "^3.2.2" - tough-cookie "^2.5.0" - w3c-hr-time "^1.0.1" - w3c-xmlserializer "^1.1.2" - webidl-conversions "^4.0.2" - whatwg-encoding "^1.0.5" - whatwg-mimetype "^2.3.0" - whatwg-url "^7.0.0" - ws "^6.1.2" - xml-name-validator "^3.0.0" - jsesc@^2.5.1: version "2.5.2" resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== -jsesc@~0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" - integrity sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA== - -json-parse-better-errors@^1.0.1, json-parse-better-errors@^1.0.2: +json-parse-better-errors@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw== @@ -7922,11 +5321,6 @@ json-schema-traverse@^0.4.1: resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== -json-schema@0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.4.0.tgz#f7de4cf6efab838ebaeb3236474cbba5a1930ab5" - integrity sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA== - json-stable-stringify-without-jsonify@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" @@ -7937,7 +5331,7 @@ json-stringify-nice@^1.1.4: resolved "https://registry.yarnpkg.com/json-stringify-nice/-/json-stringify-nice-1.1.4.tgz#2c937962b80181d3f317dd39aa323e14f5a60a67" integrity sha512-5Z5RFW63yxReJ7vANgW6eZFGWaQvnPE3WNmZoOJrSkGju2etKA2L5rrOa1sm877TVTFt57A80BH1bArcmlLfPw== -json-stringify-safe@^5.0.1, json-stringify-safe@~5.0.1: +json-stringify-safe@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" integrity sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA== @@ -7949,11 +5343,6 @@ json5@^1.0.1, json5@^1.0.2: dependencies: minimist "^1.2.0" -json5@^2.1.0: - version "2.2.1" - resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.1.tgz#655d50ed1e6f95ad1a3caababd2b0efda10b395c" - integrity sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA== - jsonc-parser@3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-3.2.0.tgz#31ff3f4c2b9793f89c67212627c51c6394f88e76" @@ -7985,15 +5374,15 @@ jsonparse@^1.2.0, jsonparse@^1.3.1: resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280" integrity sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg== -jsprim@^1.2.2: - version "1.4.2" - resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.2.tgz#712c65533a15c878ba59e9ed5f0e26d5b77c5feb" - integrity sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw== +jszip@^3.10.1: + version "3.10.1" + resolved "https://registry.yarnpkg.com/jszip/-/jszip-3.10.1.tgz#34aee70eb18ea1faec2f589208a157d1feb091c2" + integrity sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g== dependencies: - assert-plus "1.0.0" - extsprintf "1.3.0" - json-schema "0.4.0" - verror "1.10.0" + lie "~3.3.0" + pako "~1.0.2" + readable-stream "~2.3.6" + setimmediate "^1.0.5" just-diff-apply@^5.2.0: version "5.5.0" @@ -8041,13 +5430,12 @@ klaw-sync@^6.0.0: dependencies: graceful-fs "^4.1.11" -labeled-stream-splicer@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/labeled-stream-splicer/-/labeled-stream-splicer-2.0.2.tgz#42a41a16abcd46fd046306cf4f2c3576fffb1c21" - integrity sha512-Ca4LSXFFZUjPScRaqOcFxneA0VpKZr4MMYCljyQr4LIewTLb3Y0IUTIsnBBsVubIeEfxeSZpSjSsRM8APEQaAw== +klaw@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/klaw/-/klaw-2.1.1.tgz#42b76894701169cc910fd0d19ce677b5fb378af1" + integrity sha512-kuInGWCNc98b1ghOqBJfqPOvAKn9HHgm+SdluR5VNfdA7rs7uNsuXGy7CCqsP6pFKPpUoCH4s9o00GEj9xONHg== dependencies: - inherits "^2.0.1" - stream-splicer "^2.0.0" + graceful-fs "^4.1.9" lerna-update-wizard@^0.17.5: version "0.17.8" @@ -8102,14 +5490,6 @@ levn@^0.4.1: prelude-ls "^1.2.1" type-check "~0.4.0" -levn@~0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" - integrity sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA== - dependencies: - prelude-ls "~1.1.2" - type-check "~0.3.2" - libnpmaccess@^6.0.3: version "6.0.4" resolved "https://registry.yarnpkg.com/libnpmaccess/-/libnpmaccess-6.0.4.tgz#2dd158bd8a071817e2207d3b201d37cf1ad6ae6b" @@ -8131,6 +5511,13 @@ libnpmpublish@^6.0.4: semver "^7.3.7" ssri "^9.0.0" +lie@~3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/lie/-/lie-3.3.0.tgz#dcf82dee545f46074daf200c7c1c5a08e0f40f6a" + integrity sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ== + dependencies: + immediate "~3.0.5" + lines-and-columns@^1.1.6: version "1.2.4" resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" @@ -8161,20 +5548,6 @@ load-json-file@^7.0.0: resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-7.0.1.tgz#a3c9fde6beffb6bedb5acf104fad6bb1604e1b00" integrity sha512-Gnxj3ev3mB5TkVBGad0JM6dmLiQL+o0t23JPBZ9sd+yvSLk05mFoqKBw5N8gbbkU4TNXyqCgIrl/VM17OgUIgQ== -loader-runner@^2.4.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.4.0.tgz#ed47066bfe534d7e84c4c7b9998c2a75607d9357" - integrity sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw== - -loader-utils@^1.2.3, loader-utils@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.4.0.tgz#c579b5e34cb34b1a74edc6c1fb36bfa371d5a613" - integrity sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA== - dependencies: - big.js "^5.2.2" - emojis-list "^3.0.0" - json5 "^1.0.1" - locate-path@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" @@ -8212,11 +5585,6 @@ locate-path@^7.1.0: dependencies: p-locate "^6.0.0" -lodash.clone@^4.5.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/lodash.clone/-/lodash.clone-4.5.0.tgz#195870450f5a13192478df4bc3d23d2dea1907b6" - integrity sha512-GhrVeweiTD6uTmmn5hV/lzgCQhccwReIVRLHp7LT4SopOjqEZ5BbX8b5WWEtAKasjmy8hR7ZPwsYlxRCku5odg== - lodash.get@^4.4.2: version "4.4.2" resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99" @@ -8227,43 +5595,16 @@ lodash.ismatch@^4.4.0: resolved "https://registry.yarnpkg.com/lodash.ismatch/-/lodash.ismatch-4.4.0.tgz#756cb5150ca3ba6f11085a78849645f188f85f37" integrity sha512-fPMfXjGQEV9Xsq/8MTSgUf255gawYRbjwMyDbcvDhXgV7enSZA0hynz6vMPnpAb5iONEzBHBPsT+0zes5Z301g== -lodash.memoize@^4.1.2: - version "4.1.2" - resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" - integrity sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag== - -lodash.memoize@~3.0.3: - version "3.0.4" - resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-3.0.4.tgz#2dcbd2c287cbc0a55cc42328bd0c736150d53e3f" - integrity sha512-eDn9kqrAmVUC1wmZvlQ6Uhde44n+tXpqPrN8olQJbttgh0oKclk+SF54P47VEGE9CEiMeRwAP8BaM7UHvBkz2A== - lodash.merge@^4.6.2: version "4.6.2" resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== -lodash.sortby@^4.7.0: - version "4.7.0" - resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438" - integrity sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA== - -lodash.uniq@^4.5.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" - integrity sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ== - -lodash@^4.17.11, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.21, lodash@^4.17.4: +lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.21, lodash@^4.17.4: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== -log-symbols@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-2.2.0.tgz#5740e1c5d6f0dfda4ad9323b5332107ef6b4c40a" - integrity sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg== - dependencies: - chalk "^2.0.1" - log-symbols@^4.0.0, log-symbols@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" @@ -8272,13 +5613,6 @@ log-symbols@^4.0.0, log-symbols@^4.1.0: chalk "^4.1.0" is-unicode-supported "^0.1.0" -loose-envify@^1.0.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" - integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== - dependencies: - js-tokens "^3.0.0 || ^4.0.0" - loud-rejection@^1.0.0: version "1.6.0" resolved "https://registry.yarnpkg.com/loud-rejection/-/loud-rejection-1.6.0.tgz#5b46f80147edee578870f086d04821cf998e551f" @@ -8287,21 +5621,6 @@ loud-rejection@^1.0.0: currently-unhandled "^0.4.1" signal-exit "^3.0.0" -lru-cache@^4.1.5: - version "4.1.5" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd" - integrity sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g== - dependencies: - pseudomap "^1.0.2" - yallist "^2.1.2" - -lru-cache@^5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" - integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== - dependencies: - yallist "^3.0.2" - lru-cache@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" @@ -8319,21 +5638,14 @@ lunr@^2.3.9: resolved "https://registry.yarnpkg.com/lunr/-/lunr-2.3.9.tgz#18b123142832337dd6e964df1a5a7707b25d35e1" integrity sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow== -magic-string@^0.22.4: - version "0.22.5" - resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.22.5.tgz#8e9cf5afddf44385c1da5bc2a6a0dbd10b03657e" - integrity sha512-oreip9rJZkzvA8Qzk9HFs8fZGF/u7H/gtrE8EN6RjKJ9kh2HlC+yQ2QezifqTZfGyiuAV0dRv5a+y/8gBb1m9w== - dependencies: - vlq "^0.2.2" - -magic-string@^0.25.2, magic-string@^0.25.7: +magic-string@^0.25.7: version "0.25.9" resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.9.tgz#de7f9faf91ef8a1c91d02c2e5314c8277dbcdd1c" integrity sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ== dependencies: sourcemap-codec "^1.4.8" -make-dir@^2.0.0, make-dir@^2.1.0: +make-dir@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5" integrity sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA== @@ -8409,18 +5721,6 @@ marked@^4.3.0: resolved "https://registry.yarnpkg.com/marked/-/marked-4.3.0.tgz#796362821b019f734054582038b116481b456cf3" integrity sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A== -matched@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/matched/-/matched-1.0.2.tgz#1d95d77dd5f1b5075a9e94acde5462ffd85f317a" - integrity sha512-7ivM1jFZVTOOS77QsR+TtYHH0ecdLclMkqbf5qiJdX2RorqfhsL65QHySPZgDE0ZjHoh+mQUNHTanNXIlzXd0Q== - dependencies: - arr-union "^3.1.0" - async-array-reduce "^0.2.1" - glob "^7.1.2" - has-glob "^1.0.0" - is-valid-glob "^1.0.0" - resolve-dir "^1.0.0" - matcher@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/matcher/-/matcher-5.0.0.tgz#cd82f1c7ae7ee472a9eeaf8ec7cac45e0fe0da62" @@ -8435,25 +5735,6 @@ md5-hex@^3.0.1: dependencies: blueimp-md5 "^2.10.0" -md5.js@^1.3.4: - version "1.3.5" - resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f" - integrity sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg== - dependencies: - hash-base "^3.0.0" - inherits "^2.0.1" - safe-buffer "^5.1.2" - -mdn-data@2.0.14: - version "2.0.14" - resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.14.tgz#7113fc4281917d63ce29b43446f701e68c25ba50" - integrity sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow== - -mdn-data@2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.4.tgz#699b3c38ac6f1d728091a64650b65d388502fd5b" - integrity sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA== - mem@^9.0.2: version "9.0.2" resolved "https://registry.yarnpkg.com/mem/-/mem-9.0.2.tgz#bbc2d40be045afe30749681e8f5d554cee0c0354" @@ -8462,22 +5743,6 @@ mem@^9.0.2: map-age-cleaner "^0.1.3" mimic-fn "^4.0.0" -memory-fs@^0.4.1: - version "0.4.1" - resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.4.1.tgz#3a9a20b8462523e447cfbc7e8bb80ed667bfc552" - integrity sha512-cda4JKCxReDXFXRqOHPQscuIYg1PvxbE2S2GP45rnwfEK+vZaXC8C1OFvdHIbgw0DLzowXGVoxLaAmlgRy14GQ== - dependencies: - errno "^0.1.3" - readable-stream "^2.0.1" - -memory-fs@^0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.5.0.tgz#324c01288b88652966d161db77838720845a8e3c" - integrity sha512-jA0rdU5KoQMC0e6ppoNRtpp6vjFq6+NY7r8hywnC7V+1Xj/MtHwGIbB1QaK/dunyjWteJzmkpd7ooeWg10T7GA== - dependencies: - errno "^0.1.3" - readable-stream "^2.0.1" - meow@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/meow/-/meow-4.0.1.tgz#d48598f6f4b1472f35bf6317a95945ace347f975" @@ -8528,13 +5793,6 @@ meow@^9.0.0: type-fest "^0.18.0" yargs-parser "^20.2.3" -merge-source-map@1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/merge-source-map/-/merge-source-map-1.0.4.tgz#a5de46538dae84d4114cc5ea02b4772a6346701f" - integrity sha512-PGSmS0kfnTnMJCzJ16BLLCEe6oeYCamKFFdQKshi4BmM6FUwipjVOcBFGxqtQtirtAG4iZvHlqST9CpZKqlRjA== - dependencies: - source-map "^0.5.6" - merge-stream@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" @@ -8545,7 +5803,7 @@ merge2@^1.2.3, merge2@^1.3.0, merge2@^1.4.1: resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== -micromatch@^3.0.4, micromatch@^3.1.10, micromatch@^3.1.4: +micromatch@^3.1.10: version "3.1.10" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" integrity sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg== @@ -8572,41 +5830,18 @@ micromatch@^4.0.2, micromatch@^4.0.4: braces "^3.0.2" picomatch "^2.3.1" -miller-rabin@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.1.tgz#f080351c865b0dc562a8462966daa53543c78a4d" - integrity sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA== - dependencies: - bn.js "^4.0.0" - brorand "^1.0.1" - mime-db@1.52.0: version "1.52.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== -mime-types@^2.1.12, mime-types@~2.1.19: +mime-types@^2.1.12, mime-types@~2.1.34: version "2.1.35" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== dependencies: mime-db "1.52.0" -mime@1.6.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" - integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== - -mime@^2.0.3: - version "2.6.0" - resolved "https://registry.yarnpkg.com/mime/-/mime-2.6.0.tgz#a2a682a95cd4d0cb1d6257e28f83da7e35800367" - integrity sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg== - -mimic-fn@^1.0.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022" - integrity sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ== - mimic-fn@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" @@ -8622,16 +5857,6 @@ min-indent@^1.0.0: resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869" integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg== -minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" - integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== - -minimalistic-crypto-utils@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" - integrity sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg== - minimatch@3.0.5: version "3.0.5" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.5.tgz#4da8f1290ee0f0f8e83d60ca69f8f134068604a3" @@ -8677,7 +5902,7 @@ minimist-options@^3.0.1: arrify "^1.0.1" is-plain-obj "^1.1.0" -minimist@1, minimist@^1.1.0, minimist@^1.1.3, minimist@^1.2.0, minimist@^1.2.3, minimist@^1.2.5, minimist@^1.2.6, minimist@^1.2.7: +minimist@1, minimist@^1.1.3, minimist@^1.2.0, minimist@^1.2.3, minimist@^1.2.5, minimist@^1.2.6: version "1.2.8" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== @@ -8751,22 +5976,6 @@ minizlib@^2.1.1, minizlib@^2.1.2: minipass "^3.0.0" yallist "^4.0.0" -mississippi@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/mississippi/-/mississippi-3.0.0.tgz#ea0a3291f97e0b5e8776b363d5f0a12d94c67022" - integrity sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA== - dependencies: - concat-stream "^1.5.0" - duplexify "^3.4.2" - end-of-stream "^1.1.0" - flush-write-stream "^1.0.0" - from2 "^2.1.0" - parallel-transform "^1.1.0" - pump "^3.0.0" - pumpify "^1.3.3" - stream-each "^1.1.0" - through2 "^2.0.0" - mixin-deep@^1.2.0: version "1.3.2" resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.2.tgz#1120b43dc359a785dce65b55b82e257ccf479566" @@ -8775,11 +5984,6 @@ mixin-deep@^1.2.0: for-in "^1.0.2" is-extendable "^1.0.1" -mkdirp-classic@^0.5.2: - version "0.5.3" - resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113" - integrity sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A== - mkdirp-infer-owner@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/mkdirp-infer-owner/-/mkdirp-infer-owner-2.0.0.tgz#55d3b368e7d89065c38f32fd38e638f0ab61d316" @@ -8789,7 +5993,7 @@ mkdirp-infer-owner@^2.0.0: infer-owner "^1.0.4" mkdirp "^1.0.3" -mkdirp@^0.5.1, mkdirp@^0.5.3, mkdirp@^0.5.4, mkdirp@~0.5.1: +mkdirp@^0.5.1: version "0.5.6" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6" integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw== @@ -8806,39 +6010,6 @@ modify-values@^1.0.0: resolved "https://registry.yarnpkg.com/modify-values/-/modify-values-1.0.1.tgz#b3939fa605546474e3e3e3c63d64bd43b4ee6022" integrity sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw== -module-deps@^6.2.3: - version "6.2.3" - resolved "https://registry.yarnpkg.com/module-deps/-/module-deps-6.2.3.tgz#15490bc02af4b56cf62299c7c17cba32d71a96ee" - integrity sha512-fg7OZaQBcL4/L+AK5f4iVqf9OMbCclXfy/znXRxTVhJSeW5AIlS9AwheYwDaXM3lVW7OBeaeUEY3gbaC6cLlSA== - dependencies: - JSONStream "^1.0.3" - browser-resolve "^2.0.0" - cached-path-relative "^1.0.2" - concat-stream "~1.6.0" - defined "^1.0.0" - detective "^5.2.0" - duplexer2 "^0.1.2" - inherits "^2.0.1" - parents "^1.0.0" - readable-stream "^2.0.2" - resolve "^1.4.0" - stream-combiner2 "^1.1.1" - subarg "^1.0.0" - through2 "^2.0.0" - xtend "^4.0.0" - -move-concurrently@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/move-concurrently/-/move-concurrently-1.0.1.tgz#be2c005fda32e0b29af1f05d7c4b33214c701f92" - integrity sha512-hdrFxZOycD/g6A6SoI2bB5NA/5NEqD0569+S47WZhPvm46sD50ZHdYaFmnua5lndde9rCHGjmfK7Z8BuCt/PcQ== - dependencies: - aproba "^1.1.1" - copy-concurrently "^1.0.0" - fs-write-stream-atomic "^1.0.8" - mkdirp "^0.5.1" - rimraf "^2.5.4" - run-queue "^1.0.3" - ms@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" @@ -8849,7 +6020,7 @@ ms@2.1.2: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== -ms@2.1.3, ms@^2.0.0, ms@^2.1.1, ms@^2.1.3: +ms@^2.0.0, ms@^2.1.1, ms@^2.1.3: version "2.1.3" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== @@ -8870,11 +6041,6 @@ mute-stream@0.0.8, mute-stream@~0.0.4: resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== -nan@^2.12.1: - version "2.16.0" - resolved "https://registry.yarnpkg.com/nan/-/nan-2.16.0.tgz#664f43e45460fb98faf00edca0bb0d7b8dce7916" - integrity sha512-UdAqHyFngu7TfQKsCBgAA6pWDkT8MAO7d0jyOecVhN5354xbLqdn8mV9Tat9gepAupm0bt2DbeaSC8vS52MuFA== - nanomatch@^1.2.9: version "1.2.13" resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" @@ -8897,12 +6063,12 @@ natural-compare@^1.4.0: resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== -negotiator@^0.6.3: +negotiator@0.6.3, negotiator@^0.6.3: version "0.6.3" resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== -neo-async@^2.5.0, neo-async@^2.6.0, neo-async@^2.6.1: +neo-async@^2.6.0: version "2.6.2" resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== @@ -8923,11 +6089,6 @@ nise@^5.1.4: just-extend "^4.0.2" path-to-regexp "^1.7.0" -node-addon-api@^1.7.1: - version "1.7.2" - resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-1.7.2.tgz#3df30b95720b53c24e59948b49532b662444f54d" - integrity sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg== - node-addon-api@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-3.2.1.tgz#81325e0a2117789c0128dab65e7e38f07ceba161" @@ -8940,11 +6101,6 @@ node-fetch@^2.6.1, node-fetch@^2.6.7: dependencies: whatwg-url "^5.0.0" -node-forge@^0.7.1: - version "0.7.6" - resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.7.6.tgz#fdf3b418aee1f94f0ef642cd63486c77ca9724ac" - integrity sha512-sol30LUpz1jQFBjOKwbjxijiE3b6pjd74YwfD0fJOKPjF+fONKb2Yg8rYgS6+bK6VDl+/wfr4IYpC7jDzLUIfw== - node-gyp-build@^4.3.0: version "4.5.0" resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.5.0.tgz#7a64eefa0b21112f89f58379da128ac177f20e40" @@ -8966,40 +6122,6 @@ node-gyp@^9.0.0: tar "^6.1.2" which "^2.0.2" -node-libs-browser@^2.0.0, node-libs-browser@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/node-libs-browser/-/node-libs-browser-2.2.1.tgz#b64f513d18338625f90346d27b0d235e631f6425" - integrity sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q== - dependencies: - assert "^1.1.1" - browserify-zlib "^0.2.0" - buffer "^4.3.0" - console-browserify "^1.1.0" - constants-browserify "^1.0.0" - crypto-browserify "^3.11.0" - domain-browser "^1.1.1" - events "^3.0.0" - https-browserify "^1.0.0" - os-browserify "^0.3.0" - path-browserify "0.0.1" - process "^0.11.10" - punycode "^1.2.4" - querystring-es3 "^0.2.0" - readable-stream "^2.3.3" - stream-browserify "^2.0.1" - stream-http "^2.7.2" - string_decoder "^1.0.0" - timers-browserify "^2.0.4" - tty-browserify "0.0.0" - url "^0.11.0" - util "^0.11.0" - vm-browserify "^1.0.1" - -node-releases@^2.0.6: - version "2.0.6" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.6.tgz#8a7088c63a55e493845683ebf3c828d8c51c5503" - integrity sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg== - nofilter@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/nofilter/-/nofilter-3.1.0.tgz#c757ba68801d41ff930ba2ec55bab52ca184aa66" @@ -9054,18 +6176,6 @@ normalize-path@3, normalize-path@^3.0.0, normalize-path@~3.0.0: resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== -normalize-path@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" - integrity sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w== - dependencies: - remove-trailing-separator "^1.0.1" - -normalize-url@^3.0.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-3.3.0.tgz#b2e1c4dc4f7c6d57743df733a4f5978d18650559" - integrity sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg== - npm-bundled@^1.1.1: version "1.1.2" resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.1.2.tgz#944c78789bd739035b70baa2ca5cc32b8d860bc1" @@ -9173,18 +6283,6 @@ npmlog@^6.0.0, npmlog@^6.0.2: gauge "^4.0.3" set-blocking "^2.0.0" -nth-check@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-1.0.2.tgz#b2bd295c37e3dd58a3bf0700376663ba4d9cf05c" - integrity sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg== - dependencies: - boolbase "~1.0.0" - -nwsapi@^2.1.3: - version "2.2.2" - resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.2.tgz#e5418863e7905df67d51ec95938d67bf801f0bb0" - integrity sha512-90yv+6538zuvUMnN+zCr8LuV6bPFdq50304114vJYJ8RDyK8D5O9Phpbd6SZWgI7PwzmmfN1upeOJlvybDSgCw== - nx@15.3.3, "nx@>=14.8.1 < 16": version "15.3.3" resolved "https://registry.yarnpkg.com/nx/-/nx-15.3.3.tgz#4ad357310112bad1c4fbfded965bbbe00a2a906f" @@ -9226,16 +6324,6 @@ nx@15.3.3, "nx@>=14.8.1 < 16": yargs "^17.6.2" yargs-parser "21.1.1" -oauth-sign@~0.9.0: - version "0.9.0" - resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" - integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== - -object-assign@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" - integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== - object-copy@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c" @@ -9245,7 +6333,7 @@ object-copy@^0.1.0: define-property "^0.2.5" kind-of "^3.0.3" -object-inspect@^1.12.2, object-inspect@^1.12.3, object-inspect@^1.9.0: +object-inspect@^1.12.2, object-inspect@^1.9.0: version "1.12.3" resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.3.tgz#ba62dffd67ee256c8c086dfae69e016cd1f198b9" integrity sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g== @@ -9255,19 +6343,6 @@ object-inspect@^1.13.1: resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.1.tgz#b96c6109324ccfef6b12216a956ca4dc2ff94bc2" integrity sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ== -object-inspect@~1.4.0: - version "1.4.1" - resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.4.1.tgz#37ffb10e71adaf3748d05f713b4c9452f402cbc4" - integrity sha512-wqdhLpfCUbEsoEwl3FXwGyv8ief1k/1aUdIPCqVnupM6e8l63BEJdiF/0swtn04/8p05tG/T0FrpTlfwvljOdw== - -object-is@^1.1.5: - version "1.1.5" - resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.5.tgz#b9deeaa5fc7f1846a0faecdceec138e5778f53ac" - integrity sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" - object-keys@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" @@ -9280,7 +6355,7 @@ object-visit@^1.0.0: dependencies: isobject "^3.0.0" -object.assign@^4.1.0, object.assign@^4.1.2, object.assign@^4.1.4: +object.assign@^4.1.2, object.assign@^4.1.4: version "4.1.4" resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.4.tgz#9673c7c7c351ab8c4d0b516f4343ebf4dfb7799f" integrity sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ== @@ -9308,16 +6383,6 @@ object.fromentries@^2.0.7: define-properties "^1.2.0" es-abstract "^1.22.1" -object.getownpropertydescriptors@^2.1.0: - version "2.1.4" - resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.4.tgz#7965e6437a57278b587383831a9b829455a4bc37" - integrity sha512-sccv3L/pMModT6dJAYF3fzGMVcb38ysQ0tEE6ixv2yXJDtEIPph268OlAdJj5/qZMZDq2g/jqvwppt36uS/uQQ== - dependencies: - array.prototype.reduce "^1.0.4" - call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.20.1" - object.groupby@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/object.groupby/-/object.groupby-1.0.1.tgz#d41d9f3c8d6c778d9cbac86b4ee9f5af103152ee" @@ -9335,15 +6400,6 @@ object.pick@^1.3.0: dependencies: isobject "^3.0.1" -object.values@^1.1.0: - version "1.1.6" - resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.6.tgz#4abbaa71eba47d63589d402856f908243eea9b1d" - integrity sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.20.4" - object.values@^1.1.7: version "1.1.7" resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.7.tgz#617ed13272e7e1071b43973aa1655d9291b8442a" @@ -9353,27 +6409,13 @@ object.values@^1.1.7: define-properties "^1.2.0" es-abstract "^1.22.1" -on-finished@2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f" - integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg== - dependencies: - ee-first "1.1.1" - -once@^1.3.0, once@^1.3.1, once@^1.4.0: +once@^1.3.0, once@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== dependencies: wrappy "1" -onetime@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4" - integrity sha512-oyyPpiMaKARvvcgip+JV+7zci5L8D1W9RZIz2l1o08AM3pfspitVWnPt3mzHcBPp12oYMTy0pqrFs/C+m3EwsQ== - dependencies: - mimic-fn "^1.0.0" - onetime@^5.1.0, onetime@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" @@ -9415,25 +6457,6 @@ open@^9.1.0: is-inside-container "^1.0.0" is-wsl "^2.2.0" -opn@^5.1.0: - version "5.5.0" - resolved "https://registry.yarnpkg.com/opn/-/opn-5.5.0.tgz#fc7164fab56d235904c51c3b27da6758ca3b9bfc" - integrity sha512-PqHpggC9bLV0VeWcdKhkpxY+3JTzetLSqTCWL/z/tFIbI6G8JCjondXklT1JinczLz2Xib62sSp0T/gKT4KksA== - dependencies: - is-wsl "^1.1.0" - -optionator@^0.8.1: - version "0.8.3" - resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" - integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA== - dependencies: - deep-is "~0.1.3" - fast-levenshtein "~2.0.6" - levn "~0.3.0" - prelude-ls "~1.1.2" - type-check "~0.3.2" - word-wrap "~1.2.3" - optionator@^0.9.3: version "0.9.3" resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.3.tgz#007397d44ed1872fdc6ed31360190f81814e2c64" @@ -9446,18 +6469,6 @@ optionator@^0.9.3: prelude-ls "^1.2.1" type-check "^0.4.0" -ora@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/ora/-/ora-2.1.0.tgz#6caf2830eb924941861ec53a173799e008b51e5b" - integrity sha512-hNNlAd3gfv/iPmsNxYoAPLvxg7HuPozww7fFonMZvL84tP6Ox5igfk5j/+a9rtJJwqMgKK+JgWsAQik5o0HTLA== - dependencies: - chalk "^2.3.1" - cli-cursor "^2.1.0" - cli-spinners "^1.1.0" - log-symbols "^2.2.0" - strip-ansi "^4.0.0" - wcwidth "^1.0.1" - ora@^5.4.1: version "5.4.1" resolved "https://registry.yarnpkg.com/ora/-/ora-5.4.1.tgz#1b2678426af4ac4a509008e5e4ac9e9959db9e18" @@ -9473,12 +6484,7 @@ ora@^5.4.1: strip-ansi "^6.0.0" wcwidth "^1.0.1" -os-browserify@^0.3.0, os-browserify@~0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.3.0.tgz#854373c7f5c2315914fc9bfc6bd8238fdda1ec27" - integrity sha512-gjcpUc3clBf9+210TRaDWbf+rZZZEshZ+DlXMRCeAjp0xhTrnQsKHypIy1J3d5hKdUzj69t708EHtU8P6bUn0A== - -os-tmpdir@~1.0.2: +os-tmpdir@^1.0.1, os-tmpdir@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" integrity sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g== @@ -9656,88 +6662,11 @@ pacote@^13.0.3, pacote@^13.6.1: ssri "^9.0.0" tar "^6.1.11" -pako@^0.2.5: - version "0.2.9" - resolved "https://registry.yarnpkg.com/pako/-/pako-0.2.9.tgz#f3f7522f4ef782348da8161bad9ecfd51bf83a75" - integrity sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA== - -pako@~1.0.5: +pako@~1.0.2: version "1.0.11" resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf" integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw== -parallel-transform@^1.1.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/parallel-transform/-/parallel-transform-1.2.0.tgz#9049ca37d6cb2182c3b1d2c720be94d14a5814fc" - integrity sha512-P2vSmIu38uIlvdcU7fDkyrxj33gTUy/ABO5ZUbGowxNCopBq/OoD42bP4UmMrJoPyk4Uqf0mu3mtWBhHCZD8yg== - dependencies: - cyclist "^1.0.1" - inherits "^2.0.3" - readable-stream "^2.1.5" - -parcel@1.12.3: - version "1.12.3" - resolved "https://registry.yarnpkg.com/parcel/-/parcel-1.12.3.tgz#1f1341589380f20be924f1dd67c7fed193b346ec" - integrity sha512-j9XCVLeol9qZvGemRKt2z8bptbXq9LVy8/IzjqWQKMiKd8DR0NpDAlRHV0zyF72/J/UUTsdsrhnw6UGo9nGI+Q== - dependencies: - "@babel/code-frame" "^7.0.0 <7.4.0" - "@babel/core" "^7.0.0 <7.4.0" - "@babel/generator" "^7.0.0 <7.4.0" - "@babel/parser" "^7.0.0 <7.4.0" - "@babel/plugin-transform-flow-strip-types" "^7.0.0 <7.4.0" - "@babel/plugin-transform-modules-commonjs" "^7.0.0 <7.4.0" - "@babel/plugin-transform-react-jsx" "^7.0.0 <7.4.0" - "@babel/preset-env" "^7.0.0 <7.4.0" - "@babel/runtime" "^7.0.0 <7.4.0" - "@babel/template" "^7.0.0 <7.4.0" - "@babel/traverse" "^7.0.0 <7.4.0" - "@babel/types" "^7.0.0 <7.4.0" - "@iarna/toml" "^2.2.0" - "@parcel/fs" "^1.11.0" - "@parcel/logger" "^1.11.0" - "@parcel/utils" "^1.11.0" - "@parcel/watcher" "^1.12.0" - "@parcel/workers" "^1.11.0" - ansi-to-html "^0.6.4" - babylon-walk "^1.0.2" - browserslist "^4.1.0" - chalk "^2.1.0" - clone "^2.1.1" - command-exists "^1.2.6" - commander "^2.11.0" - cross-spawn "^6.0.4" - css-modules-loader-core "^1.1.0" - cssnano "^4.0.0" - deasync "^0.1.14" - dotenv "^5.0.0" - dotenv-expand "^4.2.0" - fast-glob "^2.2.2" - filesize "^3.6.0" - get-port "^3.2.0" - htmlnano "^0.2.2" - is-glob "^4.0.0" - is-url "^1.2.2" - js-yaml "^3.10.0" - json5 "^1.0.1" - micromatch "^3.0.4" - mkdirp "^0.5.1" - node-forge "^0.7.1" - node-libs-browser "^2.0.0" - opn "^5.1.0" - postcss "^7.0.11" - postcss-value-parser "^3.3.1" - posthtml "^0.11.2" - posthtml-parser "^0.4.0" - posthtml-render "^1.1.3" - resolve "^1.4.0" - semver "^5.4.1" - serialize-to-js "^1.1.1" - serve-static "^1.12.4" - source-map "0.6.1" - terser "^3.7.3" - v8-compile-cache "^2.0.0" - ws "^5.1.1" - parent-module@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" @@ -9745,24 +6674,6 @@ parent-module@^1.0.0: dependencies: callsites "^3.0.0" -parents@^1.0.0, parents@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/parents/-/parents-1.0.1.tgz#fedd4d2bf193a77745fe71e371d73c3307d9c751" - integrity sha512-mXKF3xkoUt5td2DoxpLmtOmZvko9VfFpwRwkKDHSNvgmpLAeBo18YDhcPbBzJq+QLCHMbGOfzia2cX4U+0v9Mg== - dependencies: - path-platform "~0.11.15" - -parse-asn1@^5.0.0, parse-asn1@^5.1.5: - version "5.1.6" - resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.6.tgz#385080a3ec13cb62a62d39409cb3e88844cdaed4" - integrity sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw== - dependencies: - asn1.js "^5.2.0" - browserify-aes "^1.0.0" - evp_bytestokey "^1.0.0" - pbkdf2 "^3.0.3" - safe-buffer "^5.1.1" - parse-conflict-json@^2.0.1: version "2.0.2" resolved "https://registry.yarnpkg.com/parse-conflict-json/-/parse-conflict-json-2.0.2.tgz#3d05bc8ffe07d39600dc6436c6aefe382033d323" @@ -9795,11 +6706,6 @@ parse-ms@^3.0.0: resolved "https://registry.yarnpkg.com/parse-ms/-/parse-ms-3.0.0.tgz#3ea24a934913345fcc3656deda72df921da3a70e" integrity sha512-Tpb8Z7r7XbbtBTrM9UhpkzzaMrqA2VXMT3YChzYltwV3P3pM6t8wl7TvpMnSTosz1aQAdVib7kdoys7vYOPerw== -parse-passwd@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/parse-passwd/-/parse-passwd-1.0.0.tgz#6d5b934a456993b23d37f40a382d6f1666a8e5c6" - integrity sha512-1Y1A//QUXEZK7YKz+rD9WydcE1+EuPr6ZBgKecAB8tmoW6UFv0NREVJe1p+jRxtThkcbbKkfwIbWJe/IeE6m2Q== - parse-path@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/parse-path/-/parse-path-7.0.0.tgz#605a2d58d0a749c8594405d8cc3a2bf76d16099b" @@ -9814,15 +6720,15 @@ parse-url@^8.1.0: dependencies: parse-path "^7.0.0" -parse5@5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/parse5/-/parse5-5.1.0.tgz#c59341c9723f414c452975564c7c00a68d58acd2" - integrity sha512-fxNG2sQjHvlVAYmzBZS9YlDp6PTSSDwa98vkD4QgVDDCAo84z5X1t5XyJQ62ImdLXx5NdIIfihey6xpum9/gRQ== +parseqs@0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/parseqs/-/parseqs-0.0.6.tgz#8e4bb5a19d1cdc844a08ac974d34e273afa670d5" + integrity sha512-jeAGzMDbfSHHA091hr0r31eYfTig+29g3GKKE/PPbEQ65X0lmMwlEoqmhzu0iztID5uJpZsFlUPDP8ThPL7M8w== -parseurl@~1.3.3: - version "1.3.3" - resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" - integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== +parseuri@0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/parseuri/-/parseuri-0.0.6.tgz#e1496e829e3ac2ff47f39a4dd044b32823c4a25a" + integrity sha512-AUjen8sAkGgao7UyCX6Ahv0gIK2fABKmYjvP4xmy5JaKvcbTRueIqIPHLAfq30xJddqSE033IOMUSOMCcK3Sow== pascalcase@^0.1.1: version "0.1.1" @@ -9848,16 +6754,6 @@ patch-package@^6.2.2: slash "^2.0.0" tmp "^0.0.33" -path-browserify@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-0.0.1.tgz#e6c4ddd7ed3aa27c68a20cc4e50e1a4ee83bbc4a" - integrity sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ== - -path-browserify@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-1.0.1.tgz#d98454a9c3753d5790860f16f68867b9e46be1fd" - integrity sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g== - path-dirname@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/path-dirname/-/path-dirname-1.0.2.tgz#cc33d24d525e099a5388c0336c6e32b9160609e0" @@ -9878,7 +6774,7 @@ path-exists@^5.0.0: resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-5.0.0.tgz#a6aad9489200b21fab31e49cf09277e5116fb9e7" integrity sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ== -path-is-absolute@^1.0.0, path-is-absolute@^1.0.1: +path-is-absolute@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== @@ -9903,11 +6799,6 @@ path-parse@^1.0.7: resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== -path-platform@~0.11.15: - version "0.11.15" - resolved "https://registry.yarnpkg.com/path-platform/-/path-platform-0.11.15.tgz#e864217f74c36850f0852b78dc7bf7d4a5721bf2" - integrity sha512-Y30dB6rab1A/nfEKsZxmr01nUotHX0c/ZiIAsCTatEe1CmS5Pm5He7fZ195bPT7RdquoaL8lLxFCMQi/bS7IJg== - path-to-regexp@^1.7.0: version "1.8.0" resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-1.8.0.tgz#887b3ba9d84393e87a0a0b9f4cb756198b53548a" @@ -9927,481 +6818,75 @@ path-type@^4.0.0: resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== -pbkdf2@^3.0.3: - version "3.1.2" - resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.1.2.tgz#dd822aa0887580e52f1a039dc3eda108efae3075" - integrity sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA== - dependencies: - create-hash "^1.1.2" - create-hmac "^1.1.4" - ripemd160 "^2.0.1" - safe-buffer "^5.0.1" - sha.js "^2.4.8" - -pend@~1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" - integrity sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg== - -performance-now@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" - integrity sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow== - -physical-cpu-count@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/physical-cpu-count/-/physical-cpu-count-2.0.0.tgz#18de2f97e4bf7a9551ad7511942b5496f7aba660" - integrity sha512-rxJOljMuWtYlvREBmd6TZYanfcPhNUKtGDZBjBBS8WG1dpN2iwPsRJZgQqN/OtJuiQckdRFOfzogqJClTrsi7g== - -picocolors@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" - integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== - picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.2, picomatch@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== -pify@^2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" - integrity sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog== - -pify@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" - integrity sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg== - -pify@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" - integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== - -pify@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/pify/-/pify-5.0.0.tgz#1f5eca3f5e87ebec28cc6d54a0e4aaf00acc127f" - integrity sha512-eW/gHNMlxdSP6dmG6uJip6FXN0EQBwm2clYYd8Wul42Cwu/DK8HEftzsapcNdYe2MfLiIwZqsDk2RDEsTE79hA== - -pkg-conf@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/pkg-conf/-/pkg-conf-4.0.0.tgz#63ace00cbacfa94c2226aee133800802d3e3b80c" - integrity sha512-7dmgi4UY4qk+4mj5Cd8v/GExPo0K+SlY+hulOSdfZ/T6jVH6//y7NtzZo5WrfhDBxuQ0jCa7fLZmNaNh7EWL/w== - dependencies: - find-up "^6.0.0" - load-json-file "^7.0.0" - -pkg-dir@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-3.0.0.tgz#2749020f239ed990881b1f71210d51eb6523bea3" - integrity sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw== - dependencies: - find-up "^3.0.0" - -pkg-dir@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" - integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== - dependencies: - find-up "^4.0.0" - -platform@^1.3.3: - version "1.3.6" - resolved "https://registry.yarnpkg.com/platform/-/platform-1.3.6.tgz#48b4ce983164b209c2d45a107adb31f473a6e7a7" - integrity sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg== - -plur@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/plur/-/plur-4.0.0.tgz#729aedb08f452645fe8c58ef115bf16b0a73ef84" - integrity sha512-4UGewrYgqDFw9vV6zNV+ADmPAUAfJPKtGvb/VdpQAx25X5f3xXdGdyOEVFwkl8Hl/tl7+xbeHqSEM+D5/TirUg== - dependencies: - irregular-plurals "^3.2.0" - -plur@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/plur/-/plur-5.1.0.tgz#bff58c9f557b9061d60d8ebf93959cf4b08594ae" - integrity sha512-VP/72JeXqak2KiOzjgKtQen5y3IZHn+9GOuLDafPv0eXa47xq0At93XahYBs26MsifCQ4enGKwbjBTKgb9QJXg== - dependencies: - irregular-plurals "^3.3.0" - -pn@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/pn/-/pn-1.1.0.tgz#e2f4cef0e219f463c179ab37463e4e1ecdccbafb" - integrity sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA== - -posix-character-classes@^0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" - integrity sha512-xTgYBc3fuo7Yt7JbiuFxSYGToMoz8fLoE6TC9Wx1P/u+LfeThMOAqmuyECnlBaaJb+u1m9hHiXUEtwW4OzfUJg== - -postcss-calc@^7.0.1: - version "7.0.5" - resolved "https://registry.yarnpkg.com/postcss-calc/-/postcss-calc-7.0.5.tgz#f8a6e99f12e619c2ebc23cf6c486fdc15860933e" - integrity sha512-1tKHutbGtLtEZF6PT4JSihCHfIVldU72mZ8SdZHIYriIZ9fh9k9aWSppaT8rHsyI3dX+KSR+W+Ix9BMY3AODrg== - dependencies: - postcss "^7.0.27" - postcss-selector-parser "^6.0.2" - postcss-value-parser "^4.0.2" - -postcss-colormin@^4.0.3: - version "4.0.3" - resolved "https://registry.yarnpkg.com/postcss-colormin/-/postcss-colormin-4.0.3.tgz#ae060bce93ed794ac71264f08132d550956bd381" - integrity sha512-WyQFAdDZpExQh32j0U0feWisZ0dmOtPl44qYmJKkq9xFWY3p+4qnRzCHeNrkeRhwPHz9bQ3mo0/yVkaply0MNw== - dependencies: - browserslist "^4.0.0" - color "^3.0.0" - has "^1.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-convert-values@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/postcss-convert-values/-/postcss-convert-values-4.0.1.tgz#ca3813ed4da0f812f9d43703584e449ebe189a7f" - integrity sha512-Kisdo1y77KUC0Jmn0OXU/COOJbzM8cImvw1ZFsBgBgMgb1iL23Zs/LXRe3r+EZqM3vGYKdQ2YJVQ5VkJI+zEJQ== - dependencies: - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-discard-comments@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-discard-comments/-/postcss-discard-comments-4.0.2.tgz#1fbabd2c246bff6aaad7997b2b0918f4d7af4033" - integrity sha512-RJutN259iuRf3IW7GZyLM5Sw4GLTOH8FmsXBnv8Ab/Tc2k4SR4qbV4DNbyyY4+Sjo362SyDmW2DQ7lBSChrpkg== - dependencies: - postcss "^7.0.0" - -postcss-discard-duplicates@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-discard-duplicates/-/postcss-discard-duplicates-4.0.2.tgz#3fe133cd3c82282e550fc9b239176a9207b784eb" - integrity sha512-ZNQfR1gPNAiXZhgENFfEglF93pciw0WxMkJeVmw8eF+JZBbMD7jp6C67GqJAXVZP2BWbOztKfbsdmMp/k8c6oQ== - dependencies: - postcss "^7.0.0" - -postcss-discard-empty@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/postcss-discard-empty/-/postcss-discard-empty-4.0.1.tgz#c8c951e9f73ed9428019458444a02ad90bb9f765" - integrity sha512-B9miTzbznhDjTfjvipfHoqbWKwd0Mj+/fL5s1QOz06wufguil+Xheo4XpOnc4NqKYBCNqqEzgPv2aPBIJLox0w== - dependencies: - postcss "^7.0.0" - -postcss-discard-overridden@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/postcss-discard-overridden/-/postcss-discard-overridden-4.0.1.tgz#652aef8a96726f029f5e3e00146ee7a4e755ff57" - integrity sha512-IYY2bEDD7g1XM1IDEsUT4//iEYCxAmP5oDSFMVU/JVvT7gh+l4fmjciLqGgwjdWpQIdb0Che2VX00QObS5+cTg== - dependencies: - postcss "^7.0.0" - -postcss-merge-longhand@^4.0.11: - version "4.0.11" - resolved "https://registry.yarnpkg.com/postcss-merge-longhand/-/postcss-merge-longhand-4.0.11.tgz#62f49a13e4a0ee04e7b98f42bb16062ca2549e24" - integrity sha512-alx/zmoeXvJjp7L4mxEMjh8lxVlDFX1gqWHzaaQewwMZiVhLo42TEClKaeHbRf6J7j82ZOdTJ808RtN0ZOZwvw== - dependencies: - css-color-names "0.0.4" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - stylehacks "^4.0.0" - -postcss-merge-rules@^4.0.3: - version "4.0.3" - resolved "https://registry.yarnpkg.com/postcss-merge-rules/-/postcss-merge-rules-4.0.3.tgz#362bea4ff5a1f98e4075a713c6cb25aefef9a650" - integrity sha512-U7e3r1SbvYzO0Jr3UT/zKBVgYYyhAz0aitvGIYOYK5CPmkNih+WDSsS5tvPrJ8YMQYlEMvsZIiqmn7HdFUaeEQ== - dependencies: - browserslist "^4.0.0" - caniuse-api "^3.0.0" - cssnano-util-same-parent "^4.0.0" - postcss "^7.0.0" - postcss-selector-parser "^3.0.0" - vendors "^1.0.0" - -postcss-minify-font-values@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-minify-font-values/-/postcss-minify-font-values-4.0.2.tgz#cd4c344cce474343fac5d82206ab2cbcb8afd5a6" - integrity sha512-j85oO6OnRU9zPf04+PZv1LYIYOprWm6IA6zkXkrJXyRveDEuQggG6tvoy8ir8ZwjLxLuGfNkCZEQG7zan+Hbtg== - dependencies: - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-minify-gradients@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-minify-gradients/-/postcss-minify-gradients-4.0.2.tgz#93b29c2ff5099c535eecda56c4aa6e665a663471" - integrity sha512-qKPfwlONdcf/AndP1U8SJ/uzIJtowHlMaSioKzebAXSG4iJthlWC9iSWznQcX4f66gIWX44RSA841HTHj3wK+Q== - dependencies: - cssnano-util-get-arguments "^4.0.0" - is-color-stop "^1.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-minify-params@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-minify-params/-/postcss-minify-params-4.0.2.tgz#6b9cef030c11e35261f95f618c90036d680db874" - integrity sha512-G7eWyzEx0xL4/wiBBJxJOz48zAKV2WG3iZOqVhPet/9geefm/Px5uo1fzlHu+DOjT+m0Mmiz3jkQzVHe6wxAWg== - dependencies: - alphanum-sort "^1.0.0" - browserslist "^4.0.0" - cssnano-util-get-arguments "^4.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - uniqs "^2.0.0" - -postcss-minify-selectors@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-minify-selectors/-/postcss-minify-selectors-4.0.2.tgz#e2e5eb40bfee500d0cd9243500f5f8ea4262fbd8" - integrity sha512-D5S1iViljXBj9kflQo4YutWnJmwm8VvIsU1GeXJGiG9j8CIg9zs4voPMdQDUmIxetUOh60VilsNzCiAFTOqu3g== - dependencies: - alphanum-sort "^1.0.0" - has "^1.0.0" - postcss "^7.0.0" - postcss-selector-parser "^3.0.0" - -postcss-modules-extract-imports@1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-1.1.0.tgz#b614c9720be6816eaee35fb3a5faa1dba6a05ddb" - integrity sha512-zF9+UIEvtpeqMGxhpeT9XaIevQSrBBCz9fi7SwfkmjVacsSj8DY5eFVgn+wY8I9vvdDDwK5xC8Myq4UkoLFIkA== - dependencies: - postcss "^6.0.1" - -postcss-modules-local-by-default@1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-1.2.0.tgz#f7d80c398c5a393fa7964466bd19500a7d61c069" - integrity sha512-X4cquUPIaAd86raVrBwO8fwRfkIdbwFu7CTfEOjiZQHVQwlHRSkTgH5NLDmMm5+1hQO8u6dZ+TOOJDbay1hYpA== - dependencies: - css-selector-tokenizer "^0.7.0" - postcss "^6.0.1" - -postcss-modules-scope@1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-1.1.0.tgz#d6ea64994c79f97b62a72b426fbe6056a194bb90" - integrity sha512-LTYwnA4C1He1BKZXIx1CYiHixdSe9LWYVKadq9lK5aCCMkoOkFyZ7aigt+srfjlRplJY3gIol6KUNefdMQJdlw== - dependencies: - css-selector-tokenizer "^0.7.0" - postcss "^6.0.1" - -postcss-modules-values@1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/postcss-modules-values/-/postcss-modules-values-1.3.0.tgz#ecffa9d7e192518389f42ad0e83f72aec456ea20" - integrity sha512-i7IFaR9hlQ6/0UgFuqM6YWaCfA1Ej8WMg8A5DggnH1UGKJvTV/ugqq/KaULixzzOi3T/tF6ClBXcHGCzdd5unA== - dependencies: - icss-replace-symbols "^1.1.0" - postcss "^6.0.1" - -postcss-normalize-charset@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/postcss-normalize-charset/-/postcss-normalize-charset-4.0.1.tgz#8b35add3aee83a136b0471e0d59be58a50285dd4" - integrity sha512-gMXCrrlWh6G27U0hF3vNvR3w8I1s2wOBILvA87iNXaPvSNo5uZAMYsZG7XjCUf1eVxuPfyL4TJ7++SGZLc9A3g== - dependencies: - postcss "^7.0.0" - -postcss-normalize-display-values@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-normalize-display-values/-/postcss-normalize-display-values-4.0.2.tgz#0dbe04a4ce9063d4667ed2be476bb830c825935a" - integrity sha512-3F2jcsaMW7+VtRMAqf/3m4cPFhPD3EFRgNs18u+k3lTJJlVe7d0YPO+bnwqo2xg8YiRpDXJI2u8A0wqJxMsQuQ== - dependencies: - cssnano-util-get-match "^4.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-normalize-positions@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-normalize-positions/-/postcss-normalize-positions-4.0.2.tgz#05f757f84f260437378368a91f8932d4b102917f" - integrity sha512-Dlf3/9AxpxE+NF1fJxYDeggi5WwV35MXGFnnoccP/9qDtFrTArZ0D0R+iKcg5WsUd8nUYMIl8yXDCtcrT8JrdA== - dependencies: - cssnano-util-get-arguments "^4.0.0" - has "^1.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-normalize-repeat-style@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-4.0.2.tgz#c4ebbc289f3991a028d44751cbdd11918b17910c" - integrity sha512-qvigdYYMpSuoFs3Is/f5nHdRLJN/ITA7huIoCyqqENJe9PvPmLhNLMu7QTjPdtnVf6OcYYO5SHonx4+fbJE1+Q== - dependencies: - cssnano-util-get-arguments "^4.0.0" - cssnano-util-get-match "^4.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-normalize-string@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-normalize-string/-/postcss-normalize-string-4.0.2.tgz#cd44c40ab07a0c7a36dc5e99aace1eca4ec2690c" - integrity sha512-RrERod97Dnwqq49WNz8qo66ps0swYZDSb6rM57kN2J+aoyEAJfZ6bMx0sx/F9TIEX0xthPGCmeyiam/jXif0eA== - dependencies: - has "^1.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-normalize-timing-functions@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-4.0.2.tgz#8e009ca2a3949cdaf8ad23e6b6ab99cb5e7d28d9" - integrity sha512-acwJY95edP762e++00Ehq9L4sZCEcOPyaHwoaFOhIwWCDfik6YvqsYNxckee65JHLKzuNSSmAdxwD2Cud1Z54A== - dependencies: - cssnano-util-get-match "^4.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-normalize-unicode@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/postcss-normalize-unicode/-/postcss-normalize-unicode-4.0.1.tgz#841bd48fdcf3019ad4baa7493a3d363b52ae1cfb" - integrity sha512-od18Uq2wCYn+vZ/qCOeutvHjB5jm57ToxRaMeNuf0nWVHaP9Hua56QyMF6fs/4FSUnVIw0CBPsU0K4LnBPwYwg== - dependencies: - browserslist "^4.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-normalize-url@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/postcss-normalize-url/-/postcss-normalize-url-4.0.1.tgz#10e437f86bc7c7e58f7b9652ed878daaa95faae1" - integrity sha512-p5oVaF4+IHwu7VpMan/SSpmpYxcJMtkGppYf0VbdH5B6hN8YNmVyJLuY9FmLQTzY3fag5ESUUHDqM+heid0UVA== - dependencies: - is-absolute-url "^2.0.0" - normalize-url "^3.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-normalize-whitespace@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-normalize-whitespace/-/postcss-normalize-whitespace-4.0.2.tgz#bf1d4070fe4fcea87d1348e825d8cc0c5faa7d82" - integrity sha512-tO8QIgrsI3p95r8fyqKV+ufKlSHh9hMJqACqbv2XknufqEDhDvbguXGBBqxw9nsQoXWf0qOqppziKJKHMD4GtA== - dependencies: - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-ordered-values@^4.1.2: - version "4.1.2" - resolved "https://registry.yarnpkg.com/postcss-ordered-values/-/postcss-ordered-values-4.1.2.tgz#0cf75c820ec7d5c4d280189559e0b571ebac0eee" - integrity sha512-2fCObh5UanxvSxeXrtLtlwVThBvHn6MQcu4ksNT2tsaV2Fg76R2CV98W7wNSlX+5/pFwEyaDwKLLoEV7uRybAw== - dependencies: - cssnano-util-get-arguments "^4.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-reduce-initial@^4.0.3: - version "4.0.3" - resolved "https://registry.yarnpkg.com/postcss-reduce-initial/-/postcss-reduce-initial-4.0.3.tgz#7fd42ebea5e9c814609639e2c2e84ae270ba48df" - integrity sha512-gKWmR5aUulSjbzOfD9AlJiHCGH6AEVLaM0AV+aSioxUDd16qXP1PCh8d1/BGVvpdWn8k/HiK7n6TjeoXN1F7DA== - dependencies: - browserslist "^4.0.0" - caniuse-api "^3.0.0" - has "^1.0.0" - postcss "^7.0.0" - -postcss-reduce-transforms@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-reduce-transforms/-/postcss-reduce-transforms-4.0.2.tgz#17efa405eacc6e07be3414a5ca2d1074681d4e29" - integrity sha512-EEVig1Q2QJ4ELpJXMZR8Vt5DQx8/mo+dGWSR7vWXqcob2gQLyQGsionYcGKATXvQzMPn6DSN1vTN7yFximdIAg== - dependencies: - cssnano-util-get-match "^4.0.0" - has "^1.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-selector-parser@6.0.2, postcss-selector-parser@^6.0.2: - version "6.0.2" - resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.2.tgz#934cf799d016c83411859e09dcecade01286ec5c" - integrity sha512-36P2QR59jDTOAiIkqEprfJDsoNrvwFei3eCqKd1Y0tUsBimsq39BLp7RD+JWny3WgB1zGhJX8XVePwm9k4wdBg== - dependencies: - cssesc "^3.0.0" - indexes-of "^1.0.1" - uniq "^1.0.1" - -postcss-selector-parser@^3.0.0: - version "3.1.2" - resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz#b310f5c4c0fdaf76f94902bbaa30db6aa84f5270" - integrity sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA== - dependencies: - dot-prop "^5.2.0" - indexes-of "^1.0.1" - uniq "^1.0.1" +pify@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" + integrity sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog== -postcss-svgo@^4.0.3: - version "4.0.3" - resolved "https://registry.yarnpkg.com/postcss-svgo/-/postcss-svgo-4.0.3.tgz#343a2cdbac9505d416243d496f724f38894c941e" - integrity sha512-NoRbrcMWTtUghzuKSoIm6XV+sJdvZ7GZSc3wdBN0W19FTtp2ko8NqLsgoh/m9CzNhU3KLPvQmjIwtaNFkaFTvw== - dependencies: - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - svgo "^1.0.0" +pify@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" + integrity sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg== -postcss-unique-selectors@^4.0.1: +pify@^4.0.1: version "4.0.1" - resolved "https://registry.yarnpkg.com/postcss-unique-selectors/-/postcss-unique-selectors-4.0.1.tgz#9446911f3289bfd64c6d680f073c03b1f9ee4bac" - integrity sha512-+JanVaryLo9QwZjKrmJgkI4Fn8SBgRO6WXQBJi7KiAVPlmxikB5Jzc4EvXMT2H0/m0RjrVVm9rGNhZddm/8Spg== - dependencies: - alphanum-sort "^1.0.0" - postcss "^7.0.0" - uniqs "^2.0.0" - -postcss-value-parser@^3.0.0, postcss-value-parser@^3.3.1: - version "3.3.1" - resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz#9ff822547e2893213cf1c30efa51ac5fd1ba8281" - integrity sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ== + resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" + integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== -postcss-value-parser@^4.0.2: - version "4.2.0" - resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514" - integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ== +pify@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/pify/-/pify-5.0.0.tgz#1f5eca3f5e87ebec28cc6d54a0e4aaf00acc127f" + integrity sha512-eW/gHNMlxdSP6dmG6uJip6FXN0EQBwm2clYYd8Wul42Cwu/DK8HEftzsapcNdYe2MfLiIwZqsDk2RDEsTE79hA== -postcss@6.0.1, postcss@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-6.0.1.tgz#000dbd1f8eef217aa368b9a212c5fc40b2a8f3f2" - integrity sha512-VbGX1LQgQbf9l3cZ3qbUuC3hGqIEOGQFHAEHQ/Diaeo0yLgpgK5Rb8J+OcamIfQ9PbAU/fzBjVtQX3AhJHUvZw== +pkg-conf@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/pkg-conf/-/pkg-conf-4.0.0.tgz#63ace00cbacfa94c2226aee133800802d3e3b80c" + integrity sha512-7dmgi4UY4qk+4mj5Cd8v/GExPo0K+SlY+hulOSdfZ/T6jVH6//y7NtzZo5WrfhDBxuQ0jCa7fLZmNaNh7EWL/w== dependencies: - chalk "^1.1.3" - source-map "^0.5.6" - supports-color "^3.2.3" + find-up "^6.0.0" + load-json-file "^7.0.0" -postcss@7.0.32, postcss@^7.0.0, postcss@^7.0.1, postcss@^7.0.11, postcss@^7.0.17, postcss@^7.0.27: - version "7.0.32" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.32.tgz#4310d6ee347053da3433db2be492883d62cec59d" - integrity sha512-03eXong5NLnNCD05xscnGKGDZ98CyzoqPSMjOe6SuoQY7Z2hIj0Ld1g/O/UQRuOle2aRtiIRDg9tDcTGAkLfKw== +pkg-dir@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" + integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== dependencies: - chalk "^2.4.2" - source-map "^0.6.1" - supports-color "^6.1.0" + find-up "^4.0.0" -posthtml-parser@^0.4.0, posthtml-parser@^0.4.1: - version "0.4.2" - resolved "https://registry.yarnpkg.com/posthtml-parser/-/posthtml-parser-0.4.2.tgz#a132bbdf0cd4bc199d34f322f5c1599385d7c6c1" - integrity sha512-BUIorsYJTvS9UhXxPTzupIztOMVNPa/HtAm9KHni9z6qEfiJ1bpOBL5DfUOL9XAc3XkLIEzBzpph+Zbm4AdRAg== - dependencies: - htmlparser2 "^3.9.2" +platform@^1.3.3: + version "1.3.6" + resolved "https://registry.yarnpkg.com/platform/-/platform-1.3.6.tgz#48b4ce983164b209c2d45a107adb31f473a6e7a7" + integrity sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg== -posthtml-parser@^0.7.2: - version "0.7.2" - resolved "https://registry.yarnpkg.com/posthtml-parser/-/posthtml-parser-0.7.2.tgz#3fba3375544d824bb1c8504f0d69f6e0b95774db" - integrity sha512-LjEEG/3fNcWZtBfsOE3Gbyg1Li4CmsZRkH1UmbMR7nKdMXVMYI3B4/ZMiCpaq8aI1Aym4FRMMW9SAOLSwOnNsQ== +plur@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/plur/-/plur-4.0.0.tgz#729aedb08f452645fe8c58ef115bf16b0a73ef84" + integrity sha512-4UGewrYgqDFw9vV6zNV+ADmPAUAfJPKtGvb/VdpQAx25X5f3xXdGdyOEVFwkl8Hl/tl7+xbeHqSEM+D5/TirUg== dependencies: - htmlparser2 "^6.0.0" - -posthtml-render@^1.1.3, posthtml-render@^1.1.5, posthtml-render@^1.3.1: - version "1.4.0" - resolved "https://registry.yarnpkg.com/posthtml-render/-/posthtml-render-1.4.0.tgz#40114070c45881cacb93347dae3eff53afbcff13" - integrity sha512-W1779iVHGfq0Fvh2PROhCe2QhB8mEErgqzo1wpIt36tCgChafP+hbXIhLDOM8ePJrZcFs0vkNEtdibEWVqChqw== + irregular-plurals "^3.2.0" -posthtml@^0.11.2: - version "0.11.6" - resolved "https://registry.yarnpkg.com/posthtml/-/posthtml-0.11.6.tgz#e349d51af7929d0683b9d8c3abd8166beecc90a8" - integrity sha512-C2hrAPzmRdpuL3iH0TDdQ6XCc9M7Dcc3zEW5BLerY65G4tWWszwv6nG/ksi6ul5i2mx22ubdljgktXCtNkydkw== +plur@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/plur/-/plur-5.1.0.tgz#bff58c9f557b9061d60d8ebf93959cf4b08594ae" + integrity sha512-VP/72JeXqak2KiOzjgKtQen5y3IZHn+9GOuLDafPv0eXa47xq0At93XahYBs26MsifCQ4enGKwbjBTKgb9QJXg== dependencies: - posthtml-parser "^0.4.1" - posthtml-render "^1.1.5" + irregular-plurals "^3.3.0" -posthtml@^0.15.1: - version "0.15.2" - resolved "https://registry.yarnpkg.com/posthtml/-/posthtml-0.15.2.tgz#739cf0d3ffec70868b87121dc7393478e1898c9c" - integrity sha512-YugEJ5ze/0DLRIVBjCpDwANWL4pPj1kHJ/2llY8xuInr0nbkon3qTiMPe5LQa+cCwNjxS7nAZZTp+1M+6mT4Zg== - dependencies: - posthtml-parser "^0.7.2" - posthtml-render "^1.3.1" +posix-character-classes@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" + integrity sha512-xTgYBc3fuo7Yt7JbiuFxSYGToMoz8fLoE6TC9Wx1P/u+LfeThMOAqmuyECnlBaaJb+u1m9hHiXUEtwW4OzfUJg== prelude-ls@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== -prelude-ls@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" - integrity sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w== - prettier@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.0.0.tgz#e7b19f691245a21d618c68bc54dc06122f6105ae" @@ -10428,6 +6913,11 @@ pretty-ms@^8.0.0: dependencies: parse-ms "^3.0.0" +private@^0.1.8: + version "0.1.8" + resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff" + integrity sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg== + proc-log@^2.0.0, proc-log@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/proc-log/-/proc-log-2.0.1.tgz#8f3f69a1f608de27878f91f5c688b225391cb685" @@ -10438,16 +6928,6 @@ process-nextick-args@~2.0.0: resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== -process@^0.11.10, process@~0.11.0: - version "0.11.10" - resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" - integrity sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A== - -progress@^2.0.1: - version "2.0.3" - resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" - integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== - promise-all-reject-late@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/promise-all-reject-late/-/promise-all-reject-late-1.0.1.tgz#f8ebf13483e5ca91ad809ccc2fcf25f26f8643c2" @@ -10488,122 +6968,26 @@ protocols@^2.0.0, protocols@^2.0.1: resolved "https://registry.yarnpkg.com/protocols/-/protocols-2.0.1.tgz#8f155da3fc0f32644e83c5782c8e8212ccf70a86" integrity sha512-/XJ368cyBJ7fzLMwLKv1e4vLxOju2MNAIokcr7meSaNcVbWz/CPcW22cP04mwxOErdA5mwjA8Q6w/cdAQxVn7Q== -proxy-from-env@^1.0.0, proxy-from-env@^1.1.0: +proxy-from-env@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== -prr@~1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476" - integrity sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw== - -pseudomap@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" - integrity sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ== - -psl@^1.1.28: - version "1.9.0" - resolved "https://registry.yarnpkg.com/psl/-/psl-1.9.0.tgz#d0df2a137f00794565fcaf3b2c00cd09f8d5a5a7" - integrity sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag== - -public-encrypt@^4.0.0: - version "4.0.3" - resolved "https://registry.yarnpkg.com/public-encrypt/-/public-encrypt-4.0.3.tgz#4fcc9d77a07e48ba7527e7cbe0de33d0701331e0" - integrity sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q== - dependencies: - bn.js "^4.1.0" - browserify-rsa "^4.0.0" - create-hash "^1.1.0" - parse-asn1 "^5.0.0" - randombytes "^2.0.1" - safe-buffer "^5.1.2" - -pump@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/pump/-/pump-2.0.1.tgz#12399add6e4cf7526d973cbc8b5ce2e2908b3909" - integrity sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA== - dependencies: - end-of-stream "^1.1.0" - once "^1.3.1" - -pump@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" - integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== - dependencies: - end-of-stream "^1.1.0" - once "^1.3.1" - -pumpify@^1.3.3: - version "1.5.1" - resolved "https://registry.yarnpkg.com/pumpify/-/pumpify-1.5.1.tgz#36513be246ab27570b1a374a5ce278bfd74370ce" - integrity sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ== - dependencies: - duplexify "^3.6.0" - inherits "^2.0.3" - pump "^2.0.0" - -punycode@1.3.2, punycode@^1.2.4, punycode@^1.3.2: - version "1.3.2" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" - integrity sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw== - -punycode@^2.1.0, punycode@^2.1.1: +punycode@^2.1.0: version "2.1.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== -puppeteer@^1.13.0: - version "1.20.0" - resolved "https://registry.yarnpkg.com/puppeteer/-/puppeteer-1.20.0.tgz#e3d267786f74e1d87cf2d15acc59177f471bbe38" - integrity sha512-bt48RDBy2eIwZPrkgbcwHtb51mj2nKvHOPMaSH2IsWiv7lOG9k9zhaRzpDZafrk05ajMc3cu+lSQYYOfH2DkVQ== - dependencies: - debug "^4.1.0" - extract-zip "^1.6.6" - https-proxy-agent "^2.2.1" - mime "^2.0.3" - progress "^2.0.1" - proxy-from-env "^1.0.0" - rimraf "^2.6.1" - ws "^6.1.0" - pure-rand@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/pure-rand/-/pure-rand-5.0.1.tgz#97a287b4b4960b2a3448c0932bf28f2405cac51d" integrity sha512-ksWccjmXOHU2gJBnH0cK1lSYdvSZ0zLoCMSz/nTGh6hDvCSgcRxDyIcOBD6KNxFz3xhMPm/T267Tbe2JRymKEQ== -purgecss@^2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/purgecss/-/purgecss-2.3.0.tgz#5327587abf5795e6541517af8b190a6fb5488bb3" - integrity sha512-BE5CROfVGsx2XIhxGuZAT7rTH9lLeQx/6M0P7DTXQH4IUc3BBzs9JUzt4yzGf3JrH9enkeq6YJBe9CTtkm1WmQ== - dependencies: - commander "^5.0.0" - glob "^7.0.0" - postcss "7.0.32" - postcss-selector-parser "^6.0.2" - -q@^1.1.2, q@^1.5.1: +q@^1.5.1: version "1.5.1" resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" integrity sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw== -qs@~6.5.2: - version "6.5.3" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.3.tgz#3aeeffc91967ef6e35c0e488ef46fb296ab76aad" - integrity sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA== - -querystring-es3@^0.2.0, querystring-es3@~0.2.0: - version "0.2.1" - resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73" - integrity sha512-773xhDQnZBMFobEiztv8LIl70ch5MSF/jUQVlhwFyBILqq96anmoctVIYz+ZRp0qbCKATTn6ev02M3r7Ga5vqA== - -querystring@0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" - integrity sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g== - queue-microtask@^1.2.2: version "1.2.3" resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" @@ -10619,35 +7003,6 @@ quick-lru@^4.0.1: resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-4.0.1.tgz#5b8878f113a58217848c6482026c73e1ba57727f" integrity sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g== -quote-stream@^1.0.1, quote-stream@~1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/quote-stream/-/quote-stream-1.0.2.tgz#84963f8c9c26b942e153feeb53aae74652b7e0b2" - integrity sha512-kKr2uQ2AokadPjvTyKJQad9xELbZwYzWlNfI3Uz2j/ib5u6H9lDP7fUUR//rMycd0gv4Z5P1qXMfXR8YpIxrjQ== - dependencies: - buffer-equal "0.0.1" - minimist "^1.1.3" - through2 "^2.0.0" - -randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5, randombytes@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" - integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== - dependencies: - safe-buffer "^5.1.0" - -randomfill@^1.0.3: - version "1.0.4" - resolved "https://registry.yarnpkg.com/randomfill/-/randomfill-1.0.4.tgz#c92196fc86ab42be983f1bf31778224931d61458" - integrity sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw== - dependencies: - randombytes "^2.0.5" - safe-buffer "^5.1.0" - -range-parser@~1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" - integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== - react-is@^18.0.0: version "18.2.0" resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b" @@ -10658,13 +7013,6 @@ read-cmd-shim@^3.0.0: resolved "https://registry.yarnpkg.com/read-cmd-shim/-/read-cmd-shim-3.0.1.tgz#868c235ec59d1de2db69e11aec885bc095aea087" integrity sha512-kEmDUoYf/CDy8yZbLTmhB1X9kkjf9Q80PCNsDMb7ufrGd6zZSQA1+UyjrO+pZm5K/S4OXCWJeiIt1JA8kAsa6g== -read-only-stream@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/read-only-stream/-/read-only-stream-2.0.0.tgz#2724fd6a8113d73764ac288d4386270c1dbf17f0" - integrity sha512-3ALe0bjBVZtkdWKIcThYpQCLbBMd/+Tbh2CDSrAIDO3UsZ4Xs+tnyjv2MjCOMMgBG+AsUOeuP1cgtY1INISc8w== - dependencies: - readable-stream "^2.0.2" - read-package-json-fast@^2.0.2, read-package-json-fast@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/read-package-json-fast/-/read-package-json-fast-2.0.3.tgz#323ca529630da82cb34b36cc0b996693c98c2b83" @@ -10726,7 +7074,16 @@ read@1, read@^1.0.7: dependencies: mute-stream "~0.0.4" -"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.3, readable-stream@^2.3.6, readable-stream@~2.3.3, readable-stream@~2.3.6: +readable-stream@3, readable-stream@^3.0.0, readable-stream@^3.0.2, readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" + integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + +readable-stream@~2.3.6: version "2.3.7" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== @@ -10739,15 +7096,6 @@ read@1, read@^1.0.7: string_decoder "~1.1.1" util-deprecate "~1.0.1" -readable-stream@3, readable-stream@^3.0.0, readable-stream@^3.0.2, readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.5.0, readable-stream@^3.6.0: - version "3.6.0" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" - integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== - dependencies: - inherits "^2.0.3" - string_decoder "^1.1.1" - util-deprecate "^1.0.1" - readdir-scoped-modules@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/readdir-scoped-modules/-/readdir-scoped-modules-1.1.0.tgz#8d45407b4f870a0dcaebc0e28670d18e74514309" @@ -10758,15 +7106,6 @@ readdir-scoped-modules@^1.1.0: graceful-fs "^4.1.2" once "^1.3.0" -readdirp@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.2.1.tgz#0e87622a3325aa33e892285caf8b4e846529a525" - integrity sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ== - dependencies: - graceful-fs "^4.1.11" - micromatch "^3.1.10" - readable-stream "^2.0.2" - readdirp@~3.6.0: version "3.6.0" resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" @@ -10774,6 +7113,16 @@ readdirp@~3.6.0: dependencies: picomatch "^2.2.1" +recast@^0.17.5: + version "0.17.6" + resolved "https://registry.yarnpkg.com/recast/-/recast-0.17.6.tgz#64ae98d0d2dfb10ff92ff5fb9ffb7371823b69fa" + integrity sha512-yoQRMRrK1lszNtbkGyM4kN45AwylV5hMiuEveUBlxytUViWevjvX6w+tzJt1LH4cfUhWt4NZvy3ThIhu6+m5wQ== + dependencies: + ast-types "0.12.4" + esprima "~4.0.0" + private "^0.1.8" + source-map "~0.6.1" + redent@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/redent/-/redent-2.0.0.tgz#c1b2007b42d57eb1389079b3c8333639d5e1ccaa" @@ -10790,40 +7139,6 @@ redent@^3.0.0: indent-string "^4.0.0" strip-indent "^3.0.0" -regenerate-unicode-properties@^10.1.0: - version "10.1.0" - resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.0.tgz#7c3192cab6dd24e21cb4461e5ddd7dd24fa8374c" - integrity sha512-d1VudCLoIGitcU/hEg2QqvyGZQmdC0Lf8BqdOMXGFSvJP4bNV1+XqbPQeHHLD51Jh4QJJ225dlIFvY4Ly6MXmQ== - dependencies: - regenerate "^1.4.2" - -regenerate@^1.4.2: - version "1.4.2" - resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.2.tgz#b9346d8827e8f5a32f7ba29637d398b69014848a" - integrity sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A== - -regenerator-runtime@^0.11.0: - version "0.11.1" - resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9" - integrity sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg== - -regenerator-runtime@^0.12.0: - version "0.12.1" - resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.12.1.tgz#fa1a71544764c036f8c49b13a08b2594c9f8a0de" - integrity sha512-odxIc1/vDlo4iZcfXqRYFj0vpXFNoGdKMAUieAlFYO6m/nl5e9KR/beGf41z4a1FI+aQgtjhuaSlDxQ0hmkrHg== - -regenerator-runtime@^0.13.4: - version "0.13.9" - resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz#8925742a98ffd90814988d7566ad30ca3b263b52" - integrity sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA== - -regenerator-transform@^0.15.0: - version "0.15.0" - resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.15.0.tgz#cbd9ead5d77fae1a48d957cf889ad0586adb6537" - integrity sha512-LsrGtPmbYg19bcPHwdtmXwbW+TqNvtY4riE3P83foeHRroMbH6/2ddFBfab3t7kbzc7v7p4wbkIecHImqt0QNg== - dependencies: - "@babel/runtime" "^7.8.4" - regex-not@^1.0.0, regex-not@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c" @@ -10832,7 +7147,7 @@ regex-not@^1.0.0, regex-not@^1.0.2: extend-shallow "^3.0.2" safe-regex "^1.1.0" -regexp.prototype.flags@^1.4.3, regexp.prototype.flags@^1.5.0: +regexp.prototype.flags@^1.4.3: version "1.5.0" resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.0.tgz#fe7ce25e7e4cca8db37b6634c8a2c7009199b9cb" integrity sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA== @@ -10850,40 +7165,6 @@ regexp.prototype.flags@^1.5.1: define-properties "^1.2.0" set-function-name "^2.0.0" -regexpu-core@^5.1.0: - version "5.2.1" - resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-5.2.1.tgz#a69c26f324c1e962e9ffd0b88b055caba8089139" - integrity sha512-HrnlNtpvqP1Xkb28tMhBUO2EbyUHdQlsnlAhzWcwHy8WJR53UWr7/MAvqrsQKMbV4qdpv03oTMG8iIhfsPFktQ== - dependencies: - regenerate "^1.4.2" - regenerate-unicode-properties "^10.1.0" - regjsgen "^0.7.1" - regjsparser "^0.9.1" - unicode-match-property-ecmascript "^2.0.0" - unicode-match-property-value-ecmascript "^2.0.0" - -regjsgen@^0.7.1: - version "0.7.1" - resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.7.1.tgz#ee5ef30e18d3f09b7c369b76e7c2373ed25546f6" - integrity sha512-RAt+8H2ZEzHeYWxZ3H2z6tF18zyyOnlcdaafLrm21Bguj7uZy6ULibiAFdXEtKQY4Sy7wDTwDiOazasMLc4KPA== - -regjsparser@^0.9.1: - version "0.9.1" - resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.9.1.tgz#272d05aa10c7c1f67095b1ff0addae8442fc5709" - integrity sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ== - dependencies: - jsesc "~0.5.0" - -relateurl@^0.2.7: - version "0.2.7" - resolved "https://registry.yarnpkg.com/relateurl/-/relateurl-0.2.7.tgz#54dbf377e51440aca90a4cd274600d3ff2d888a9" - integrity sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog== - -remove-trailing-separator@^1.0.1: - version "1.1.0" - resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" - integrity sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw== - repeat-element@^1.1.2: version "1.1.4" resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.4.tgz#be681520847ab58c7568ac75fbfad28ed42d39e9" @@ -10894,48 +7175,6 @@ repeat-string@^1.6.1: resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" integrity sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w== -request-promise-core@1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/request-promise-core/-/request-promise-core-1.1.4.tgz#3eedd4223208d419867b78ce815167d10593a22f" - integrity sha512-TTbAfBBRdWD7aNNOoVOBH4pN/KigV6LyapYNNlAPA8JwbovRti1E88m3sYAwsLi5ryhPKsE9APwnjFTgdUjTpw== - dependencies: - lodash "^4.17.19" - -request-promise-native@^1.0.5: - version "1.0.9" - resolved "https://registry.yarnpkg.com/request-promise-native/-/request-promise-native-1.0.9.tgz#e407120526a5efdc9a39b28a5679bf47b9d9dc28" - integrity sha512-wcW+sIUiWnKgNY0dqCpOZkUbF/I+YPi+f09JZIDa39Ec+q82CpSYniDp+ISgTTbKmnpJWASeJBPZmoxH84wt3g== - dependencies: - request-promise-core "1.1.4" - stealthy-require "^1.1.1" - tough-cookie "^2.3.3" - -request@^2.88.0: - version "2.88.2" - resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3" - integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw== - dependencies: - aws-sign2 "~0.7.0" - aws4 "^1.8.0" - caseless "~0.12.0" - combined-stream "~1.0.6" - extend "~3.0.2" - forever-agent "~0.6.1" - form-data "~2.3.2" - har-validator "~5.1.3" - http-signature "~1.2.0" - is-typedarray "~1.0.0" - isstream "~0.1.2" - json-stringify-safe "~5.0.1" - mime-types "~2.1.19" - oauth-sign "~0.9.0" - performance-now "^2.1.0" - qs "~6.5.2" - safe-buffer "^5.1.2" - tough-cookie "~2.5.0" - tunnel-agent "^0.6.0" - uuid "^3.3.2" - require-directory@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" @@ -10951,13 +7190,6 @@ requireindex@~1.1.0: resolved "https://registry.yarnpkg.com/requireindex/-/requireindex-1.1.0.tgz#e5404b81557ef75db6e49c5a72004893fe03e162" integrity sha512-LBnkqsDE7BZKvqylbmn7lTIVdpx4K/QCduRATpO5R+wtPmky/a8pN1bO2D6wXppn1497AJF9mNjqAXr6bdl9jg== -resolve-cwd@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-2.0.0.tgz#00a9f7387556e27038eae232caa372a6a59b665a" - integrity sha512-ccu8zQTrzVr954472aUVPLEcB3YpKSYR3cg/3lo1okzobPBM+1INXBbBZlDbnI/hbEocnf8j0QVo43hQKrbchg== - dependencies: - resolve-from "^3.0.0" - resolve-cwd@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d" @@ -10965,19 +7197,6 @@ resolve-cwd@^3.0.0: dependencies: resolve-from "^5.0.0" -resolve-dir@^1.0.0, resolve-dir@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/resolve-dir/-/resolve-dir-1.0.1.tgz#79a40644c362be82f26effe739c9bb5382046f43" - integrity sha512-R7uiTjECzvOsWSfdM0QKFNBVFcK27aHOUwdvK53BcW8zqnGdYp0Fbj82cy54+2A4P2tFM22J5kRfe1R+lM/1yg== - dependencies: - expand-tilde "^2.0.0" - global-modules "^1.0.0" - -resolve-from@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748" - integrity sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw== - resolve-from@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" @@ -10993,7 +7212,7 @@ resolve-url@^0.2.1: resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" integrity sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg== -resolve@^1.1.4, resolve@^1.1.5, resolve@^1.10.0, resolve@^1.11.0, resolve@^1.11.1, resolve@^1.12.0, resolve@^1.17.0, resolve@^1.19.0, resolve@^1.3.2, resolve@^1.4.0: +resolve@^1.10.0, resolve@^1.12.0, resolve@^1.17.0, resolve@^1.19.0: version "1.22.1" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177" integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw== @@ -11011,23 +7230,6 @@ resolve@^1.22.4: path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" -resolve@^2.0.0-next.4: - version "2.0.0-next.4" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-2.0.0-next.4.tgz#3d37a113d6429f496ec4752d2a2e58efb1fd4660" - integrity sha512-iMDbmAWtfU+MHpxt/I5iWI7cY6YVEZUQ3MBgPQ++XD1PELuJHIl82xBmObyP2KyQmkNB2dsqF7seoQQiAn5yDQ== - dependencies: - is-core-module "^2.9.0" - path-parse "^1.0.7" - supports-preserve-symlinks-flag "^1.0.0" - -restore-cursor@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf" - integrity sha512-6IzJLuGi4+R14vwagDHX+JrXmPVtPpn4mffDJ1UdR7/Edm87fl6yi8mMBIVvFtJaNTUvjughmW4hwLhRG7gC1Q== - dependencies: - onetime "^2.0.0" - signal-exit "^3.0.2" - restore-cursor@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e" @@ -11036,13 +7238,6 @@ restore-cursor@^3.1.0: onetime "^5.1.0" signal-exit "^3.0.2" -resumer@^0.0.0: - version "0.0.0" - resolved "https://registry.yarnpkg.com/resumer/-/resumer-0.0.0.tgz#f1e8f461e4064ba39e82af3cdc2a8c893d076759" - integrity sha512-Fn9X8rX8yYF4m81rZCK/5VmrmsSbqS/i3rDLl6ZZHAXgC2nTAx3dhwG8q8odP/RmdLa2YrybDJaAMg+X1ajY3w== - dependencies: - through "~2.3.4" - ret@~0.1.10: version "0.1.15" resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" @@ -11058,17 +7253,7 @@ reusify@^1.0.4: resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== -rgb-regex@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/rgb-regex/-/rgb-regex-1.0.1.tgz#c0e0d6882df0e23be254a475e8edd41915feaeb1" - integrity sha512-gDK5mkALDFER2YLqH6imYvK6g02gpNGM4ILDZ472EwWfXZnC2ZEpoB2ECXTyOVUKuk/bPJZMzwQPBYICzP+D3w== - -rgba-regex@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/rgba-regex/-/rgba-regex-1.0.0.tgz#43374e2e2ca0968b0ef1523460b7d730ff22eeb3" - integrity sha512-zgn5OjNQXLUTdq8m17KdaicF6w89TZs8ZU8y0AYENIU6wG8GG6LLm0yLSiPY8DmaYmHdgRW8rnApjoT0fQRfMg== - -rimraf@^2.5.4, rimraf@^2.6.1, rimraf@^2.6.2, rimraf@^2.6.3: +rimraf@^2.6.3: version "2.7.1" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== @@ -11082,45 +7267,6 @@ rimraf@^3.0.0, rimraf@^3.0.2: dependencies: glob "^7.1.3" -ripemd160@^2.0.0, ripemd160@^2.0.1: - version "2.0.2" - resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c" - integrity sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA== - dependencies: - hash-base "^3.0.0" - inherits "^2.0.1" - -rollup-plugin-multi-entry@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/rollup-plugin-multi-entry/-/rollup-plugin-multi-entry-2.1.0.tgz#64a7287adfd437cab33bf6364a8d8ab1e7a7725d" - integrity sha512-YVVsI15uvbxMKdeYS5NXQa5zbVr/DYdDBBwseC80+KAc7mqDUjM6Qe4wl+jFucVw1yvBDZFk0PPSBZqoLq8xUA== - dependencies: - matched "^1.0.2" - -rollup-plugin-replace@^2.1.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/rollup-plugin-replace/-/rollup-plugin-replace-2.2.0.tgz#f41ae5372e11e7a217cde349c8b5d5fd115e70e3" - integrity sha512-/5bxtUPkDHyBJAKketb4NfaeZjL5yLZdeUihSfbF2PQMz+rSTEb8ARKoOl3UBT4m7/X+QOXJo3sLTcq+yMMYTA== - dependencies: - magic-string "^0.25.2" - rollup-pluginutils "^2.6.0" - -rollup-pluginutils@^2.6.0: - version "2.8.2" - resolved "https://registry.yarnpkg.com/rollup-pluginutils/-/rollup-pluginutils-2.8.2.tgz#72f2af0748b592364dbd3389e600e5a9444a351e" - integrity sha512-EEp9NhnUkwY8aif6bxgovPHMoMoNr2FulJziTndpt5H9RdwC47GSGuII9XxpSdzVGM0GWrNPHV6ie1LTNJPaLQ== - dependencies: - estree-walker "^0.6.1" - -rollup@1.31.0: - version "1.31.0" - resolved "https://registry.yarnpkg.com/rollup/-/rollup-1.31.0.tgz#e2a87212e96aa7850f3eb53fdd02cf89f2d2fe9a" - integrity sha512-9C6ovSyNeEwvuRuUUmsTpJcXac1AwSL1a3x+O5lpmQKZqi5mmrjauLeqIjvREC+yNRR8fPdzByojDng+af3nVw== - dependencies: - "@types/estree" "*" - "@types/node" "*" - acorn "^7.1.0" - rollup@^2.79.1: version "2.79.1" resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.79.1.tgz#bedee8faef7c9f93a2647ac0108748f497f081c7" @@ -11147,14 +7293,7 @@ run-parallel@^1.1.9: dependencies: queue-microtask "^1.2.2" -run-queue@^1.0.0, run-queue@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/run-queue/-/run-queue-1.0.3.tgz#e848396f057d223f24386924618e25694161ec47" - integrity sha512-ntymy489o0/QQplUDnpYAYUsO50K9SBrIVaKCWDOJzYJts0f9WH9RFJkyagebkw5+y1oi00R7ynNW/d12GBumg== - dependencies: - aproba "^1.1.1" - -rxjs@^6.6.0, rxjs@^6.6.2: +rxjs@^6.4.0, rxjs@^6.6.0, rxjs@^6.6.2: version "6.6.7" resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.7.tgz#90ac018acabf491bf65044235d5863c4dab804c9" integrity sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ== @@ -11178,11 +7317,6 @@ safe-array-concat@^1.0.1: has-symbols "^1.0.3" isarray "^2.0.5" -safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@^5.2.0: - version "5.2.1" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" - integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== - safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.2" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" @@ -11204,45 +7338,26 @@ safe-regex@^1.1.0: dependencies: ret "~0.1.10" -"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: +"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0": version "2.1.2" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== -safer-eval@^1.3.0: - version "1.3.6" - resolved "https://registry.yarnpkg.com/safer-eval/-/safer-eval-1.3.6.tgz#ee51e3348c39fdc4117a47dfb4b69df56a2e40cf" - integrity sha512-DN9tBsZgtUOHODzSfO1nGCLhZtxc7Qq/d8/2SNxQZ9muYXZspSh1fO7HOsrf4lcelBNviAJLCxB/ggmG+jV1aw== - dependencies: - clones "^1.2.0" - -sax@~1.2.4: - version "1.2.4" - resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" - integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== - -saxes@^3.1.9: - version "3.1.11" - resolved "https://registry.yarnpkg.com/saxes/-/saxes-3.1.11.tgz#d59d1fd332ec92ad98a2e0b2ee644702384b1c5b" - integrity sha512-Ydydq3zC+WYDJK1+gRxRapLIED9PWeSuuS41wqyoRmzvhhh9nc+QQrVMKJYzJFULazeGhzSV0QleN2wD3boh2g== +selenium-webdriver@^4.0.0-alpha.1: + version "4.17.0" + resolved "https://registry.yarnpkg.com/selenium-webdriver/-/selenium-webdriver-4.17.0.tgz#f6c93a9df3e0543df7dc2329d81968af42845a7f" + integrity sha512-e2E+2XBlGepzwgFbyQfSwo9Cbj6G5fFfs9MzAS00nC99EewmcS2rwn2MwtgfP7I5p1e7DYv4HQJXtWedsu6DvA== dependencies: - xmlchars "^2.1.1" - -schema-utils@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-1.0.0.tgz#0b79a93204d7b600d4b2850d1f66c2a34951c770" - integrity sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g== - dependencies: - ajv "^6.1.0" - ajv-errors "^1.0.0" - ajv-keywords "^3.1.0" + jszip "^3.10.1" + tmp "^0.2.1" + ws ">=8.14.2" semver-compare@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/semver-compare/-/semver-compare-1.0.0.tgz#0dee216a1c941ab37e9efb1788f6afc5ff5537fc" integrity sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow== -"semver@2 || 3 || 4 || 5", semver@^5.3.0, semver@^5.4.1, semver@^5.5.0, semver@^5.6.0: +"semver@2 || 3 || 4 || 5", semver@^5.5.0, semver@^5.6.0: version "5.7.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== @@ -11271,25 +7386,6 @@ semver@^7.0.0, semver@^7.1.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semve dependencies: lru-cache "^6.0.0" -send@0.18.0: - version "0.18.0" - resolved "https://registry.yarnpkg.com/send/-/send-0.18.0.tgz#670167cc654b05f5aa4a767f9113bb371bc706be" - integrity sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg== - dependencies: - debug "2.6.9" - depd "2.0.0" - destroy "1.2.0" - encodeurl "~1.0.2" - escape-html "~1.0.3" - etag "~1.8.1" - fresh "0.5.2" - http-errors "2.0.0" - mime "1.6.0" - ms "2.1.3" - on-finished "2.4.1" - range-parser "~1.2.1" - statuses "2.0.1" - serialize-error@^7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/serialize-error/-/serialize-error-7.0.1.tgz#f1360b0447f61ffb483ec4157c737fab7d778e18" @@ -11297,30 +7393,10 @@ serialize-error@^7.0.1: dependencies: type-fest "^0.13.1" -serialize-javascript@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-4.0.0.tgz#b525e1238489a5ecfc42afacc3fe99e666f4b1aa" - integrity sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw== - dependencies: - randombytes "^2.1.0" - -serialize-to-js@^1.1.1: - version "1.2.2" - resolved "https://registry.yarnpkg.com/serialize-to-js/-/serialize-to-js-1.2.2.tgz#1a567b0c9bf557bc7d7b77b503dfae0a8218d15d" - integrity sha512-mUc8vA5iJghe+O+3s0YDGFLMJcqitVFk787YKiv8a4sf6RX5W0u81b+gcHrp15O0fFa010dRBVZvwcKXOWsL9Q== - dependencies: - js-beautify "^1.8.9" - safer-eval "^1.3.0" - -serve-static@^1.12.4: - version "1.15.0" - resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.15.0.tgz#faaef08cffe0a1a62f60cad0c4e513cff0ac9540" - integrity sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g== - dependencies: - encodeurl "~1.0.2" - escape-html "~1.0.3" - parseurl "~1.3.3" - send "0.18.0" +server-destroy@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/server-destroy/-/server-destroy-1.0.1.tgz#f13bf928e42b9c3e79383e61cc3998b5d14e6cdd" + integrity sha512-rb+9B5YBIEzYcD6x2VKidaa+cqYBJQKnU4oe4E3ANwRRN56yk/ua1YCJT1n21NTS8w6CcOclAKNP3PhdCXKYtQ== set-blocking@^2.0.0: version "2.0.0" @@ -11357,24 +7433,11 @@ set-value@^2.0.0, set-value@^2.0.1: is-plain-object "^2.0.3" split-string "^3.0.1" -setimmediate@^1.0.4: +setimmediate@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" integrity sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA== -setprototypeof@1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" - integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== - -sha.js@^2.4.0, sha.js@^2.4.8: - version "2.4.11" - resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.11.tgz#37a5cf0b81ecbc6943de109ba2960d1b26584ae7" - integrity sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ== - dependencies: - inherits "^2.0.1" - safe-buffer "^5.0.1" - shallow-clone@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-3.0.1.tgz#8f2981ad92531f55035b01fb230769a40e02efa3" @@ -11382,18 +7445,6 @@ shallow-clone@^3.0.0: dependencies: kind-of "^6.0.2" -shallow-copy@~0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/shallow-copy/-/shallow-copy-0.0.1.tgz#415f42702d73d810330292cc5ee86eae1a11a170" - integrity sha512-b6i4ZpVuUxB9h5gfCxPiusKYkqTMOjEbBs4wMaFbkfia4yFv92UKZ6Df8WXcKbn08JNL/abvg3FnMAOfakDvUw== - -shasum-object@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/shasum-object/-/shasum-object-1.0.0.tgz#0b7b74ff5b66ecf9035475522fa05090ac47e29e" - integrity sha512-Iqo5rp/3xVi6M4YheapzZhhGPVs0yZwHj7wvwQ1B9z8H6zk+FEnI7y3Teq7qwnekfEhu8WmG2z0z4iWZaxLWVg== - dependencies: - fast-safe-stringify "^2.0.7" - shebang-command@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" @@ -11418,11 +7469,6 @@ shebang-regex@^3.0.0: resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== -shell-quote@^1.6.1: - version "1.7.3" - resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.7.3.tgz#aa40edac170445b9a431e17bb62c0b881b9c4123" - integrity sha512-Vpfqwm4EnqGdlsBFNmHhxhElJYrdfcxPThu+ryKS5J8L/fhAwLazFZtq+S+TWZ9ANj2piSQLGj6NQg+lKPmxrw== - shiki@^0.14.7: version "0.14.7" resolved "https://registry.yarnpkg.com/shiki/-/shiki-0.14.7.tgz#c3c9e1853e9737845f1d2ef81b31bcfb07056d4e" @@ -11442,11 +7488,6 @@ side-channel@^1.0.4: get-intrinsic "^1.0.2" object-inspect "^1.9.0" -sigmund@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/sigmund/-/sigmund-1.0.1.tgz#3ff21f198cad2175f9f3b781853fd94d0d19b590" - integrity sha512-fCvEXfh6NWpm+YSuY2bpXb/VIihqWA6hLsgboC+0nl71Q7N7o2eaCW8mJa/NLvQhs6jpd3VZV4UiUQlV6+lc8g== - signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^3.0.3, signal-exit@^3.0.7: version "3.0.7" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" @@ -11462,18 +7503,6 @@ signal-exit@^4.1.0: resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== -simple-concat@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/simple-concat/-/simple-concat-1.0.1.tgz#f46976082ba35c2263f1c8ab5edfe26c41c9552f" - integrity sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q== - -simple-swizzle@^0.2.2: - version "0.2.2" - resolved "https://registry.yarnpkg.com/simple-swizzle/-/simple-swizzle-0.2.2.tgz#a4da6b635ffcccca33f70d17cb92592de95e557a" - integrity sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg== - dependencies: - is-arrayish "^0.3.1" - sinon@^15.1.0: version "15.1.0" resolved "https://registry.yarnpkg.com/sinon/-/sinon-15.1.0.tgz#87656841545f7c63bd1e291df409fafd0e9aec09" @@ -11549,6 +7578,58 @@ snapdragon@^0.8.1: source-map-resolve "^0.5.0" use "^3.1.0" +socket.io-adapter@~1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/socket.io-adapter/-/socket.io-adapter-1.1.2.tgz#ab3f0d6f66b8fc7fca3959ab5991f82221789be9" + integrity sha512-WzZRUj1kUjrTIrUKpZLEzFZ1OLj5FwLlAFQs9kuZJzJi5DKdU7FsWc36SNmA8iDOtwBQyT8FkrriRM8vXLYz8g== + +socket.io-client@2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/socket.io-client/-/socket.io-client-2.5.0.tgz#34f486f3640dde9c2211fce885ac2746f9baf5cb" + integrity sha512-lOO9clmdgssDykiOmVQQitwBAF3I6mYcQAo7hQ7AM6Ny5X7fp8hIJ3HcQs3Rjz4SoggoxA1OgrQyY8EgTbcPYw== + dependencies: + backo2 "1.0.2" + component-bind "1.0.0" + component-emitter "~1.3.0" + debug "~3.1.0" + engine.io-client "~3.5.0" + has-binary2 "~1.0.2" + indexof "0.0.1" + parseqs "0.0.6" + parseuri "0.0.6" + socket.io-parser "~3.3.0" + to-array "0.1.4" + +socket.io-parser@~3.3.0: + version "3.3.3" + resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-3.3.3.tgz#3a8b84823eba87f3f7624e64a8aaab6d6318a72f" + integrity sha512-qOg87q1PMWWTeO01768Yh9ogn7chB9zkKtQnya41Y355S0UmpXgpcrFwAgjYJxu9BdKug5r5e9YtVSeWhKBUZg== + dependencies: + component-emitter "~1.3.0" + debug "~3.1.0" + isarray "2.0.1" + +socket.io-parser@~3.4.0: + version "3.4.3" + resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-3.4.3.tgz#b19bdaad38ed39fd68fba3f9d86768f667df0c29" + integrity sha512-1rE4dZN3kCI/E5wixd393hmbqa78vVpkKmnEJhLeWoS/C5hbFYAbcSfnWoaVH43u9ToUVtzKjguxEZq+1XZfCQ== + dependencies: + component-emitter "1.2.1" + debug "~4.1.0" + isarray "2.0.1" + +socket.io@^2.3.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/socket.io/-/socket.io-2.5.0.tgz#e1c7fb1823f7fa09dfebb5bb68f9d2ee03a0a2e3" + integrity sha512-gGunfS0od3VpwDBpGwVkzSZx6Aqo9uOcf1afJj2cKnKFAoyl16fvhpsUhmUFd4Ldbvl5JvRQed6eQw6oQp6n8w== + dependencies: + debug "~4.1.0" + engine.io "~3.6.0" + has-binary2 "~1.0.2" + socket.io-adapter "~1.1.0" + socket.io-client "2.5.0" + socket.io-parser "~3.4.0" + socks-proxy-agent@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-7.0.0.tgz#dc069ecf34436621acb41e3efa66ca1b5fed15b6" @@ -11580,11 +7661,6 @@ sort-keys@^4.0.0: dependencies: is-plain-obj "^2.0.0" -source-list-map@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34" - integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw== - source-map-resolve@^0.5.0: version "0.5.3" resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.3.tgz#190866bece7553e1f8f267a2ee82c606b5509a1a" @@ -11596,7 +7672,7 @@ source-map-resolve@^0.5.0: source-map-url "^0.4.0" urix "^0.1.0" -source-map-support@~0.5.10, source-map-support@~0.5.12, source-map-support@~0.5.20: +source-map-support@~0.5.20: version "0.5.21" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== @@ -11609,21 +7685,21 @@ source-map-url@^0.4.0: resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.1.tgz#0af66605a745a5a2f91cf1bbf8a7afbc283dec56" integrity sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw== -source-map@0.6.1, source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" - integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== - source-map@0.7.4: version "0.7.4" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.4.tgz#a9bbe705c9d8846f4e08ff6765acf0f1b0898656" integrity sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA== -source-map@^0.5.0, source-map@^0.5.6, source-map@~0.5.3: +source-map@^0.5.0, source-map@^0.5.6: version "0.5.7" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" integrity sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ== +source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + sourcemap-codec@^1.4.8: version "1.4.8" resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4" @@ -11678,35 +7754,8 @@ split@^1.0.0: sprintf-js@~1.0.2: version "1.0.3" - resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" - integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g== - -srcset@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/srcset/-/srcset-3.0.1.tgz#3a09637782e71ded70126320e71b8eb92ce2ad6c" - integrity sha512-MM8wDGg5BQJEj94tDrZDrX9wrC439/Eoeg3sgmVLPMjHgrAFeXAKk3tmFlCbKw5k+yOEhPXRpPlRcisQmqWVSQ== - -sshpk@^1.7.0: - version "1.17.0" - resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.17.0.tgz#578082d92d4fe612b13007496e543fa0fbcbe4c5" - integrity sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ== - dependencies: - asn1 "~0.2.3" - assert-plus "^1.0.0" - bcrypt-pbkdf "^1.0.0" - dashdash "^1.12.0" - ecc-jsbn "~0.1.1" - getpass "^0.1.1" - jsbn "~0.1.0" - safer-buffer "^2.0.2" - tweetnacl "~0.14.0" - -ssri@^6.0.1: - version "6.0.2" - resolved "https://registry.yarnpkg.com/ssri/-/ssri-6.0.2.tgz#157939134f20464e7301ddba3e90ffa8f7728ac5" - integrity sha512-cepbSq/neFK7xB6A50KHN0xHDotYzq58wWCa5LeWqnPrHG8GzfEjO/4O8kpmcGW+oaxkvhEJCWgbgNk4/ZV93Q== - dependencies: - figgy-pudding "^3.5.1" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g== ssri@^9.0.0, ssri@^9.0.1: version "9.0.1" @@ -11715,11 +7764,6 @@ ssri@^9.0.0, ssri@^9.0.1: dependencies: minipass "^3.1.1" -stable@^0.1.8: - version "0.1.8" - resolved "https://registry.yarnpkg.com/stable/-/stable-0.1.8.tgz#836eb3c8382fe2936feaf544631017ce7d47a3cf" - integrity sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w== - stack-utils@^2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.6.tgz#aaf0748169c02fc33c8232abccf933f54a1cc34f" @@ -11727,12 +7771,10 @@ stack-utils@^2.0.6: dependencies: escape-string-regexp "^2.0.0" -static-eval@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/static-eval/-/static-eval-2.1.0.tgz#a16dbe54522d7fa5ef1389129d813fd47b148014" - integrity sha512-agtxZ/kWSsCkI5E4QifRwsaPs0P0JmZV6dkLz6ILYfFYQGn+5plctanRN+IC8dJRiFkyXHrwEE3W9Wmx67uDbw== - dependencies: - escodegen "^1.11.1" +stackframe@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/stackframe/-/stackframe-0.3.1.tgz#33aa84f1177a5548c8935533cbfeb3420975f5a4" + integrity sha512-XmoiF4T5nuWEp2x2w92WdGjdHGY/cZa6LIbRsDRQR/Xlk4uW0PAUlH1zJYVffocwKpCdwyuypIp25xsSXEtZHw== static-extend@^0.1.1: version "0.1.2" @@ -11742,109 +7784,6 @@ static-extend@^0.1.1: define-property "^0.2.5" object-copy "^0.1.0" -static-module@^2.2.0: - version "2.2.5" - resolved "https://registry.yarnpkg.com/static-module/-/static-module-2.2.5.tgz#bd40abceae33da6b7afb84a0e4329ff8852bfbbf" - integrity sha512-D8vv82E/Kpmz3TXHKG8PPsCPg+RAX6cbCOyvjM6x04qZtQ47EtJFVwRsdov3n5d6/6ynrOY9XB4JkaZwB2xoRQ== - dependencies: - concat-stream "~1.6.0" - convert-source-map "^1.5.1" - duplexer2 "~0.1.4" - escodegen "~1.9.0" - falafel "^2.1.0" - has "^1.0.1" - magic-string "^0.22.4" - merge-source-map "1.0.4" - object-inspect "~1.4.0" - quote-stream "~1.0.2" - readable-stream "~2.3.3" - shallow-copy "~0.0.1" - static-eval "^2.0.0" - through2 "~2.0.3" - -statuses@2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" - integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== - -stealthy-require@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/stealthy-require/-/stealthy-require-1.1.1.tgz#35b09875b4ff49f26a777e509b3090a3226bf24b" - integrity sha512-ZnWpYnYugiOVEY5GkcuJK1io5V8QmNYChG62gSit9pQVGErXtrKuPC55ITaVSukmMta5qpMU7vqLt2Lnni4f/g== - -stop-iteration-iterator@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz#6a60be0b4ee757d1ed5254858ec66b10c49285e4" - integrity sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ== - dependencies: - internal-slot "^1.0.4" - -stream-browserify@^2.0.1: - version "2.0.2" - resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-2.0.2.tgz#87521d38a44aa7ee91ce1cd2a47df0cb49dd660b" - integrity sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg== - dependencies: - inherits "~2.0.1" - readable-stream "^2.0.2" - -stream-browserify@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-3.0.0.tgz#22b0a2850cdf6503e73085da1fc7b7d0c2122f2f" - integrity sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA== - dependencies: - inherits "~2.0.4" - readable-stream "^3.5.0" - -stream-combiner2@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/stream-combiner2/-/stream-combiner2-1.1.1.tgz#fb4d8a1420ea362764e21ad4780397bebcb41cbe" - integrity sha512-3PnJbYgS56AeWgtKF5jtJRT6uFJe56Z0Hc5Ngg/6sI6rIt8iiMBTa9cvdyFfpMQjaVHr8dusbNeFGIIonxOvKw== - dependencies: - duplexer2 "~0.1.0" - readable-stream "^2.0.2" - -stream-each@^1.1.0: - version "1.2.3" - resolved "https://registry.yarnpkg.com/stream-each/-/stream-each-1.2.3.tgz#ebe27a0c389b04fbcc233642952e10731afa9bae" - integrity sha512-vlMC2f8I2u/bZGqkdfLQW/13Zihpej/7PmSiMQsbYddxuTsJp8vRe2x2FvVExZg7FaOds43ROAuFJwPR4MTZLw== - dependencies: - end-of-stream "^1.1.0" - stream-shift "^1.0.0" - -stream-http@^2.7.2: - version "2.8.3" - resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-2.8.3.tgz#b2d242469288a5a27ec4fe8933acf623de6514fc" - integrity sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw== - dependencies: - builtin-status-codes "^3.0.0" - inherits "^2.0.1" - readable-stream "^2.3.6" - to-arraybuffer "^1.0.0" - xtend "^4.0.0" - -stream-http@^3.0.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-3.2.0.tgz#1872dfcf24cb15752677e40e5c3f9cc1926028b5" - integrity sha512-Oq1bLqisTyK3TSCXpPbT4sdeYNdmyZJv1LxpEm2vu1ZhK89kSE5YXwZc3cWk0MagGaKriBh9mCFbVGtO+vY29A== - dependencies: - builtin-status-codes "^3.0.0" - inherits "^2.0.4" - readable-stream "^3.6.0" - xtend "^4.0.2" - -stream-shift@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.1.tgz#d7088281559ab2778424279b0877da3c392d5a3d" - integrity sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ== - -stream-splicer@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/stream-splicer/-/stream-splicer-2.0.1.tgz#0b13b7ee2b5ac7e0609a7463d83899589a363fcd" - integrity sha512-Xizh4/NPuYSyAXyT7g8IvdJ9HJpxIGL9PjyhtywCZvvP0OPIdqyrr4dMikeuvY8xahpdKEBlBTySe583totajg== - dependencies: - inherits "^2.0.1" - readable-stream "^2.0.2" - "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" @@ -11872,15 +7811,6 @@ string-width@^5.0.0: emoji-regex "^9.2.2" strip-ansi "^7.0.1" -string.prototype.trim@^1.2.7: - version "1.2.7" - resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.7.tgz#a68352740859f6893f14ce3ef1bb3037f7a90533" - integrity sha512-p6TmeT1T3411M8Cgg9wBTMRtY2q9+PNy9EV1i2lIXUN/btt763oIfxwN3RR8VU6wHX8j/1CFy0L+YuThm6bgOg== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.20.4" - string.prototype.trim@^1.2.8: version "1.2.8" resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.8.tgz#f9ac6f8af4bd55ddfa8895e6aea92a96395393bd" @@ -11926,27 +7856,13 @@ string.prototype.trimstart@^1.0.7: define-properties "^1.2.0" es-abstract "^1.22.1" -string_decoder@^1.0.0, string_decoder@^1.1.1, string_decoder@~1.1.1: +string_decoder@^1.1.1, string_decoder@~1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== dependencies: safe-buffer "~5.1.0" -strip-ansi@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" - integrity sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg== - dependencies: - ansi-regex "^2.0.0" - -strip-ansi@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" - integrity sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow== - dependencies: - ansi-regex "^3.0.0" - strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" @@ -12014,22 +7930,6 @@ strong-log-transformer@^2.1.0: minimist "^1.2.0" through "^2.3.4" -stylehacks@^4.0.0: - version "4.0.3" - resolved "https://registry.yarnpkg.com/stylehacks/-/stylehacks-4.0.3.tgz#6718fcaf4d1e07d8a1318690881e8d96726a71d5" - integrity sha512-7GlLk9JwlElY4Y6a/rmbH2MhVlTyVmiJd1PfTCqFaIBEGMYNsrO/v3SeGTdhBThLg4Z+NbOk/qFMwCa+J+3p/g== - dependencies: - browserslist "^4.0.0" - postcss "^7.0.0" - postcss-selector-parser "^3.0.0" - -subarg@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/subarg/-/subarg-1.0.0.tgz#f62cf17581e996b48fc965699f54c06ae268b8d2" - integrity sha512-RIrIdRY0X1xojthNcVtgT9sjpOGagEUKpZdgBUi054OEPFo282yg+zE+t1Rj3+RqKq2xStL7uUHhY+AjbC4BXg== - dependencies: - minimist "^1.1.0" - supertap@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/supertap/-/supertap-3.0.1.tgz#aa89e4522104402c6e8fe470a7d2db6dc4037c6a" @@ -12040,18 +7940,6 @@ supertap@^3.0.1: serialize-error "^7.0.1" strip-ansi "^7.0.1" -supports-color@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" - integrity sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g== - -supports-color@^3.2.3: - version "3.2.3" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.2.3.tgz#65ac0504b3954171d8a64946b2ae3cbb8a5f54f6" - integrity sha512-Jds2VIYDrlp5ui7t8abHN2bjAu4LV/q4N2KivFPpGH0lrka0BMq/33AmECUXlKPcHigkNaqfXRENFju+rlcy+A== - dependencies: - has-flag "^1.0.0" - supports-color@^5.3.0: version "5.5.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" @@ -12059,13 +7947,6 @@ supports-color@^5.3.0: dependencies: has-flag "^3.0.0" -supports-color@^6.1.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-6.1.0.tgz#0764abc69c63d5ac842dd4867e8d025e880df8f3" - integrity sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ== - dependencies: - has-flag "^3.0.0" - supports-color@^7.0.0, supports-color@^7.1.0, supports-color@^7.2.0: version "7.2.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" @@ -12086,77 +7967,6 @@ supports-preserve-symlinks-flag@^1.0.0: resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== -svgo@^1.0.0, svgo@^1.3.2: - version "1.3.2" - resolved "https://registry.yarnpkg.com/svgo/-/svgo-1.3.2.tgz#b6dc511c063346c9e415b81e43401145b96d4167" - integrity sha512-yhy/sQYxR5BkC98CY7o31VGsg014AKLEPxdfhora76l36hD9Rdy5NZA/Ocn6yayNPgSamYdtX2rFJdcv07AYVw== - dependencies: - chalk "^2.4.1" - coa "^2.0.2" - css-select "^2.0.0" - css-select-base-adapter "^0.1.1" - css-tree "1.0.0-alpha.37" - csso "^4.0.2" - js-yaml "^3.13.1" - mkdirp "~0.5.1" - object.values "^1.1.0" - sax "~1.2.4" - stable "^0.1.8" - unquote "~1.1.1" - util.promisify "~1.0.0" - -symbol-tree@^3.2.2: - version "3.2.4" - resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" - integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== - -syntax-error@^1.1.1: - version "1.4.0" - resolved "https://registry.yarnpkg.com/syntax-error/-/syntax-error-1.4.0.tgz#2d9d4ff5c064acb711594a3e3b95054ad51d907c" - integrity sha512-YPPlu67mdnHGTup2A8ff7BC2Pjq0e0Yp/IyTFN03zWO0RcK07uLcbi7C2KpGR2FvWbaB0+bfE27a+sBKebSo7w== - dependencies: - acorn-node "^1.2.0" - -tapable@^1.0.0, tapable@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.3.tgz#a1fccc06b58db61fd7a45da2da44f5f3a3e67ba2" - integrity sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA== - -tape-promise@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/tape-promise/-/tape-promise-4.0.0.tgz#c1f3553959b2e9d64b1546e7276b8a017c616897" - integrity sha512-mNi5yhWAKDuNgZCfFKeZbsXvraVOf+I8UZG+lf+aoRrzX4+jd4mpNBjYh16/VcpEMUtS0iFndBgnfxxZbtyLFw== - dependencies: - is-promise "^2.1.0" - onetime "^2.0.0" - -tape@^5.6.3: - version "5.6.3" - resolved "https://registry.yarnpkg.com/tape/-/tape-5.6.3.tgz#0d3cc82f96b0906f73b0981df1a38a44fec7901d" - integrity sha512-cUDDGSbyoSIpdUAqbqLI/r7i/S4BHuCB9M5j7E/LrLs/x/i4zeAJ798aqo+FGo+kr9seBZwr8AkZW6rjceyAMQ== - dependencies: - array.prototype.every "^1.1.4" - call-bind "^1.0.2" - deep-equal "^2.2.0" - defined "^1.0.1" - dotignore "^0.1.2" - for-each "^0.3.3" - get-package-type "^0.1.0" - glob "^7.2.3" - has "^1.0.3" - has-dynamic-import "^2.0.1" - inherits "^2.0.4" - is-regex "^1.1.4" - minimist "^1.2.7" - object-inspect "^1.12.3" - object-is "^1.1.5" - object-keys "^1.1.1" - object.assign "^4.1.4" - resolve "^2.0.0-next.4" - resumer "^0.0.0" - string.prototype.trim "^1.2.7" - through "^2.3.8" - tar-stream@~2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.2.0.tgz#acad84c284136b060dc3faa64474aa9aebd77287" @@ -12190,40 +8000,7 @@ temp-dir@^3.0.0: resolved "https://registry.yarnpkg.com/temp-dir/-/temp-dir-3.0.0.tgz#7f147b42ee41234cc6ba3138cd8e8aa2302acffa" integrity sha512-nHc6S/bwIilKHNRgK/3jlhDoIHcp45YgyiwcAk46Tr0LfEqGBVpmiAyuiuxeVE44m3mXnEeVhaipLOEWmH+Njw== -terser-webpack-plugin@^1.4.3: - version "1.4.5" - resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-1.4.5.tgz#a217aefaea330e734ffacb6120ec1fa312d6040b" - integrity sha512-04Rfe496lN8EYruwi6oPQkG0vo8C+HT49X687FZnpPF0qMAIHONI6HEXYPKDOE8e5HjXTyKfqRd/agHtH0kOtw== - dependencies: - cacache "^12.0.2" - find-cache-dir "^2.1.0" - is-wsl "^1.1.0" - schema-utils "^1.0.0" - serialize-javascript "^4.0.0" - source-map "^0.6.1" - terser "^4.1.2" - webpack-sources "^1.4.0" - worker-farm "^1.7.0" - -terser@^3.7.3: - version "3.17.0" - resolved "https://registry.yarnpkg.com/terser/-/terser-3.17.0.tgz#f88ffbeda0deb5637f9d24b0da66f4e15ab10cb2" - integrity sha512-/FQzzPJmCpjAH9Xvk2paiWrFq+5M6aVOf+2KRbwhByISDX/EujxsK+BAvrhb6H+2rtrLCHK9N01wO014vrIwVQ== - dependencies: - commander "^2.19.0" - source-map "~0.6.1" - source-map-support "~0.5.10" - -terser@^4.1.2: - version "4.8.1" - resolved "https://registry.yarnpkg.com/terser/-/terser-4.8.1.tgz#a00e5634562de2239fd404c649051bf6fc21144f" - integrity sha512-4GnLC0x667eJG0ewJTa6z/yXrbLGv80D9Ru6HIpCQmO+Q4PfEtBFi0ObSckqwL6VyQv/7ENJieXHo2ANmdQwgw== - dependencies: - commander "^2.20.0" - source-map "~0.6.1" - source-map-support "~0.5.12" - -terser@^5.16.6, terser@^5.6.1: +terser@^5.16.6: version "5.16.6" resolved "https://registry.yarnpkg.com/terser/-/terser-5.16.6.tgz#f6c7a14a378ee0630fbe3ac8d1f41b4681109533" integrity sha512-IBZ+ZQIA9sMaXmRZCUMDjNH0D5AQQfdn4WUjHL0+1lF4TP1IHRJbrhb6fNaXWikrYQTSkb7SLxkeXAiy1p7mbg== @@ -12242,13 +8019,24 @@ test-exclude@^6.0.0: glob "^7.1.4" minimatch "^3.0.4" -test262-parser@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/test262-parser/-/test262-parser-2.2.0.tgz#5a723922d04b3409e8da770c1b9cb9197aeab22a" - integrity sha512-SO5LlFZdnvVAnIHTfh9udDA72ij0gKVi0UcInftz0vSMhg/rXw16XxABXKEmI60ZJFhbVbuJW0/tUJER8DmG3Q== +test262-harness@^10.0.0: + version "10.0.0" + resolved "https://registry.yarnpkg.com/test262-harness/-/test262-harness-10.0.0.tgz#e6b8c92a69a4396530f21d8300e9645d68e6f8b5" + integrity sha512-ntNOi+q6NMXYNAa7nwg7IgP+8ahcdqvPvkEUu6TN38nkTHIBjB/KY5jLIUD70Qk4EKd15rHHfSGDc1SVdblj4w== + dependencies: + eshost "^9.0.0" + minimatch "^3.0.4" + rxjs "^6.4.0" + test262-stream "^1.3.0" + yargs "^13.2.2" + +test262-stream@^1.3.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/test262-stream/-/test262-stream-1.4.0.tgz#6e40d803464fa47480b0c43eb9c73c8071e52921" + integrity sha512-s364askxqgyWAtIwvYCG5nYT3P32g9ByEt1ML49ubFlPE52GA6fG5ZZGmf4y/YJgKtppRAZZ7YVd9NOsk1oUxA== dependencies: js-yaml "^3.2.1" - through "^2.3.4" + klaw "^2.1.0" text-extensions@^1.0.0: version "1.9.0" @@ -12260,7 +8048,7 @@ text-table@^0.2.0: resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== -through2@^2.0.0, through2@~2.0.3: +through2@^2.0.0: version "2.0.5" resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd" integrity sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ== @@ -12275,7 +8063,7 @@ through2@^4.0.0: dependencies: readable-stream "3" -through@2, "through@>=2.2.7 <3", through@^2.3.4, through@^2.3.6, through@^2.3.8, through@~2.3.4: +through@2, "through@>=2.2.7 <3", through@^2.3.4, through@^2.3.6: version "2.3.8" resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg== @@ -12285,30 +8073,6 @@ time-zone@^1.0.0: resolved "https://registry.yarnpkg.com/time-zone/-/time-zone-1.0.0.tgz#99c5bf55958966af6d06d83bdf3800dc82faec5d" integrity sha512-TIsDdtKo6+XrPtiTm1ssmMngN1sAhyKnTO2kunQWqNPWIVvCm15Wmw4SWInwTVgJ5u/Tr04+8Ei9TNcw4x4ONA== -timers-browserify@^1.0.1: - version "1.4.2" - resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-1.4.2.tgz#c9c58b575be8407375cb5e2462dacee74359f41d" - integrity sha512-PIxwAupJZiYU4JmVZYwXp9FKsHMXb5h0ZEFyuXTAn8WLHOlcij+FEcbrvDsom1o5dr1YggEtFbECvGCW2sT53Q== - dependencies: - process "~0.11.0" - -timers-browserify@^2.0.4: - version "2.0.12" - resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-2.0.12.tgz#44a45c11fbf407f34f97bccd1577c652361b00ee" - integrity sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ== - dependencies: - setimmediate "^1.0.4" - -timsort@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/timsort/-/timsort-0.3.0.tgz#405411a8e7e6339fe64db9a234de11dc31e02bd4" - integrity sha512-qsdtZH+vMoCARQtyod4imc2nIJwg9Cc7lPRrw9CzF8ZKR0khdr8+2nX80PBhET3tcyTtJDxAffGh2rXH4tyU8A== - -tiny-inflate@^1.0.0: - version "1.0.3" - resolved "https://registry.yarnpkg.com/tiny-inflate/-/tiny-inflate-1.0.3.tgz#122715494913a1805166aaf7c93467933eea26c4" - integrity sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw== - titleize@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/titleize/-/titleize-3.0.0.tgz#71c12eb7fdd2558aa8a44b0be83b8a76694acd53" @@ -12321,22 +8085,17 @@ tmp@^0.0.33: dependencies: os-tmpdir "~1.0.2" -tmp@~0.2.1: +tmp@^0.2.1, tmp@~0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.2.1.tgz#8457fc3037dcf4719c251367a1af6500ee1ccf14" integrity sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ== dependencies: rimraf "^3.0.0" -to-arraybuffer@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz#7d229b1fcc637e466ca081180836a7aabff83f43" - integrity sha512-okFlQcoGTi4LQBG/PgSYblw9VOyptsz2KJZqc6qtgGdes8VktzUQkj4BI2blit072iS8VODNcMA+tvnS9dnuMA== - -to-fast-properties@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-1.0.3.tgz#b83571fa4d8c25b82e231b06e3a3055de4ca1a47" - integrity sha512-lxrWP8ejsq+7E3nNjwYmUBMAgjMTZoTI+sdBOpvNyijeDLa29LUn9QaoXAHv4+Z578hbmHHJKZknzxVtvo77og== +to-array@0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/to-array/-/to-array-0.1.4.tgz#17e6c11f73dd4f3d74cda7a4ff3238e9ad9bf890" + integrity sha512-LhVdShQD/4Mk4zXNroIQZJC+Ap3zgLcDuwEdcmLv9CCO73NWockQDwyUnW/m8VX/EElfL6FcYx7EeutN4HJA6A== to-fast-properties@^2.0.0: version "2.0.0" @@ -12375,26 +8134,6 @@ to-regex@^3.0.1, to-regex@^3.0.2: regex-not "^1.0.2" safe-regex "^1.1.0" -toidentifier@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" - integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== - -tough-cookie@^2.3.3, tough-cookie@^2.5.0, tough-cookie@~2.5.0: - version "2.5.0" - resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" - integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g== - dependencies: - psl "^1.1.28" - punycode "^2.1.1" - -tr46@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/tr46/-/tr46-1.0.1.tgz#a8b13fd6bfd2489519674ccde55ba3693b706d09" - integrity sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA== - dependencies: - punycode "^2.1.0" - tr46@~0.0.3: version "0.0.3" resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" @@ -12415,16 +8154,16 @@ trim-newlines@^3.0.0: resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-3.0.1.tgz#260a5d962d8b752425b32f3a7db0dcacd176c144" integrity sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw== -trim-right@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003" - integrity sha512-WZGXGstmCWgeevgTL54hrCuw1dyMQIzWy7ZfqRJfSmJZBwklI15egmQytFP6bPidmw3M8d5yEowl1niq4vmqZw== - ts-api-utils@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-1.0.1.tgz#8144e811d44c749cd65b2da305a032510774452d" integrity sha512-lC/RGlPmwdrIBFTX59wwNzqh7aR2otPNPR/5brHZm/XKFYKsfqxihXUe9pU3JI+3vGkl+vyCoNNnPhJn3aLK1A== +ts-api-utils@~1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-1.0.3.tgz#f12c1c781d04427313dbac808f453f050e54a331" + integrity sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg== + tsconfig-paths@^3.15.0: version "3.15.0" resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz#5299ec605e55b1abb23ec939ef15edaf483070d4" @@ -12480,28 +8219,6 @@ tsutils@3, tsutils@~3.21.0: dependencies: tslib "^1.8.1" -tty-browserify@0.0.0: - version "0.0.0" - resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6" - integrity sha512-JVa5ijo+j/sOoHGjw0sxw734b1LhBkQ3bvUGNdxnVXDCX81Yx7TFgnZygxrIIWn23hbfTaMYLwRmAxFyDuFmIw== - -tty-browserify@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.1.tgz#3f05251ee17904dfd0677546670db9651682b811" - integrity sha512-C3TaO7K81YvjCgQH9Q1S3R3P3BtN3RIM8n+OvX4il1K1zgE8ZhI0op7kClgkxtutIE8hQrcrHBXvIheqKUUCxw== - -tunnel-agent@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" - integrity sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w== - dependencies: - safe-buffer "^5.0.1" - -tweetnacl@^0.14.3, tweetnacl@~0.14.0: - version "0.14.5" - resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" - integrity sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA== - type-check@^0.4.0, type-check@~0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" @@ -12509,13 +8226,6 @@ type-check@^0.4.0, type-check@~0.4.0: dependencies: prelude-ls "^1.2.1" -type-check@~0.3.2: - version "0.3.2" - resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" - integrity sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg== - dependencies: - prelude-ls "~1.1.2" - type-coverage-core@^2.27.1: version "2.27.1" resolved "https://registry.yarnpkg.com/type-coverage-core/-/type-coverage-core-2.27.1.tgz#f552cdabfcbe71408a4cd3886a2c9e4e50cbdbc7" @@ -12641,6 +8351,11 @@ typedoc@^0.25.7: resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.5.tgz#095979f9bcc0d09da324d58d03ce8f8374cbe65a" integrity sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g== +typescript@~5.2.2: + version "5.2.2" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.2.2.tgz#5ebb5e5a5b75f085f22bc3f8460fba308310fa78" + integrity sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w== + typescript@~5.3.3: version "5.3.3" resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.3.3.tgz#b3ce6ba258e72e6305ba66f5c9b452aaee3ffe37" @@ -12651,10 +8366,10 @@ uglify-js@^3.1.4: resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.17.2.tgz#f55f668b9a64b213977ae688703b6bbb7ca861c6" integrity sha512-bbxglRjsGQMchfvXZNusUcYgiB9Hx2K4AHYXQy2DITZ9Rd+JzhX7+hoocE5Winr7z2oHvPsekkBwXtigvxevXg== -umd@^3.0.0: - version "3.0.3" - resolved "https://registry.yarnpkg.com/umd/-/umd-3.0.3.tgz#aa9fe653c42b9097678489c01000acb69f0b26cf" - integrity sha512-4IcGSufhFshvLNcMCV80UnQVlZ5pMOC8mvNPForqwA4+lzYQuetTESLDQkeLmihq8bRcnpbQa48Wb8Lh16/xow== +uid2@0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/uid2/-/uid2-0.0.3.tgz#483126e11774df2f71b8b639dcd799c376162b82" + integrity sha512-5gSP1liv10Gjp8cMEnFd6shzkL/D6W1uhXSFNCxDC+YI8+L8wkCYCbJ7n77Ezb4wE/xzMogecE+DtamEe9PZjg== unbox-primitive@^1.0.2: version "1.0.2" @@ -12666,63 +8381,6 @@ unbox-primitive@^1.0.2: has-symbols "^1.0.3" which-boxed-primitive "^1.0.2" -uncss@^0.17.3: - version "0.17.3" - resolved "https://registry.yarnpkg.com/uncss/-/uncss-0.17.3.tgz#50fc1eb4ed573ffff763458d801cd86e4d69ea11" - integrity sha512-ksdDWl81YWvF/X14fOSw4iu8tESDHFIeyKIeDrK6GEVTQvqJc1WlOEXqostNwOCi3qAj++4EaLsdAgPmUbEyog== - dependencies: - commander "^2.20.0" - glob "^7.1.4" - is-absolute-url "^3.0.1" - is-html "^1.1.0" - jsdom "^14.1.0" - lodash "^4.17.15" - postcss "^7.0.17" - postcss-selector-parser "6.0.2" - request "^2.88.0" - -undeclared-identifiers@^1.1.2: - version "1.1.3" - resolved "https://registry.yarnpkg.com/undeclared-identifiers/-/undeclared-identifiers-1.1.3.tgz#9254c1d37bdac0ac2b52de4b6722792d2a91e30f" - integrity sha512-pJOW4nxjlmfwKApE4zvxLScM/njmwj/DiUBv7EabwE4O8kRUy+HIwxQtZLBPll/jx1LJyBcqNfB3/cpv9EZwOw== - dependencies: - acorn-node "^1.3.0" - dash-ast "^1.0.0" - get-assigned-identifiers "^1.2.0" - simple-concat "^1.0.0" - xtend "^4.0.1" - -unicode-canonical-property-names-ecmascript@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz#301acdc525631670d39f6146e0e77ff6bbdebddc" - integrity sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ== - -unicode-match-property-ecmascript@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz#54fd16e0ecb167cf04cf1f756bdcc92eba7976c3" - integrity sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q== - dependencies: - unicode-canonical-property-names-ecmascript "^2.0.0" - unicode-property-aliases-ecmascript "^2.0.0" - -unicode-match-property-value-ecmascript@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.0.0.tgz#1a01aa57247c14c568b89775a54938788189a714" - integrity sha512-7Yhkc0Ye+t4PNYzOGKedDhXbYIBe1XEQYQxOPyhcXNMJ0WCABqqj6ckydd6pWRZTHV4GuCPKdBAUiMc60tsKVw== - -unicode-property-aliases-ecmascript@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz#43d41e3be698bd493ef911077c9b131f827e8ccd" - integrity sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w== - -unicode-trie@^0.3.1: - version "0.3.1" - resolved "https://registry.yarnpkg.com/unicode-trie/-/unicode-trie-0.3.1.tgz#d671dddd89101a08bac37b6a5161010602052085" - integrity sha512-WgVuO0M2jDl7hVfbPgXv2LUrD81HM0bQj/bvLGiw6fJ4Zo8nNFnDrA0/hU2Te/wz6pjxCm5cxJwtLjo2eyV51Q== - dependencies: - pako "^0.2.5" - tiny-inflate "^1.0.0" - union-value@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.1.tgz#0b6fe7b835aecda61c6ea4d4f02c14221e109847" @@ -12733,23 +8391,6 @@ union-value@^1.0.0: is-extendable "^0.1.1" set-value "^2.0.1" -uniq@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/uniq/-/uniq-1.0.1.tgz#b31c5ae8254844a3a8281541ce2b04b865a734ff" - integrity sha512-Gw+zz50YNKPDKXs+9d+aKAjVwpjNwqzvNpLigIruT4HA9lMZNdMqs9x07kKHB/L9WRzqp4+DlTU5s4wG2esdoA== - -uniqs@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/uniqs/-/uniqs-2.0.0.tgz#ffede4b36b25290696e6e165d4a59edb998e6b02" - integrity sha512-mZdDpf3vBV5Efh29kMw5tXoup/buMgxLzOt/XKFKcVmi+15ManNQWr6HfZ2aiZTYlYixbdNJ0KFmIZIv52tHSQ== - -unique-filename@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-1.1.1.tgz#1d69769369ada0583103a1e6ae87681b56573230" - integrity sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ== - dependencies: - unique-slug "^2.0.0" - unique-filename@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-2.0.1.tgz#e785f8675a9a7589e0ac77e0b5c34d2eaeac6da2" @@ -12757,13 +8398,6 @@ unique-filename@^2.0.0: dependencies: unique-slug "^3.0.0" -unique-slug@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/unique-slug/-/unique-slug-2.0.2.tgz#baabce91083fc64e945b0f3ad613e264f7cd4e6c" - integrity sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w== - dependencies: - imurmurhash "^0.1.4" - unique-slug@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/unique-slug/-/unique-slug-3.0.0.tgz#6d347cf57c8a7a7a6044aabd0e2d74e4d76dc7c9" @@ -12771,6 +8405,15 @@ unique-slug@^3.0.0: dependencies: imurmurhash "^0.1.4" +unique-temp-dir@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unique-temp-dir/-/unique-temp-dir-1.0.0.tgz#6dce95b2681ca003eebfb304a415f9cbabcc5385" + integrity sha512-tE68ki2FndoVdPioyiz8mYaJeX3xU/9lk4dml7KlLKEkWLtDGAYeg5LGjE2dMkzB8d6R3HbcKTn/I14nukP2dw== + dependencies: + mkdirp "^0.5.1" + os-tmpdir "^1.0.1" + uid2 "0.0.3" + universal-user-agent@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/universal-user-agent/-/universal-user-agent-6.0.0.tgz#3381f8503b251c0d9cd21bc1de939ec9df5480ee" @@ -12786,11 +8429,6 @@ universalify@^2.0.0: resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717" integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ== -unquote@~1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/unquote/-/unquote-1.1.1.tgz#8fded7324ec6e88a0ff8b905e7c098cdc086d544" - integrity sha512-vRCqFv6UhXpWxZPyGDh/F3ZpNv8/qo7w6iufLpQg9aKnQ71qM4B5KiI7Mia9COcjEhrO9LueHpMYjYzsWH3OIg== - unset-value@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559" @@ -12804,24 +8442,11 @@ untildify@^4.0.0: resolved "https://registry.yarnpkg.com/untildify/-/untildify-4.0.0.tgz#2bc947b953652487e4600949fb091e3ae8cd919b" integrity sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw== -upath@^1.1.1: - version "1.2.0" - resolved "https://registry.yarnpkg.com/upath/-/upath-1.2.0.tgz#8f66dbcd55a883acdae4408af8b035a5044c1894" - integrity sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg== - upath@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/upath/-/upath-2.0.1.tgz#50c73dea68d6f6b990f51d279ce6081665d61a8b" integrity sha512-1uEe95xksV1O0CYKXo8vQvN1JEbtJp7lb7C5U9HMsIp6IVwntkH/oNUzyVNQSd4S1sYk2FpSSW44FqMc8qee5w== -update-browserslist-db@^1.0.9: - version "1.0.9" - resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.9.tgz#2924d3927367a38d5c555413a7ce138fc95fcb18" - integrity sha512-/xsqn21EGVdXI3EXSum1Yckj3ZVZugqyOZQ/CxYPBD/R+ko9NSUScf8tFF4dOKY+2pvSSJA/S+5B8s4Zr4kyvg== - dependencies: - escalade "^3.1.1" - picocolors "^1.0.0" - uri-js@^4.2.2: version "4.4.1" resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" @@ -12834,14 +8459,6 @@ urix@^0.1.0: resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" integrity sha512-Am1ousAhSLBeB9cG/7k7r2R0zj50uDRlZHPGbazid5s9rlF1F/QKYObEKSIunSjIOkJZqwRRLpvewjEkM7pSqg== -url@^0.11.0, url@~0.11.0: - version "0.11.0" - resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1" - integrity sha512-kbailJa29QrtXnxgq+DdCEGlbTeYM2eJUxsz6vjZavrCYPMIFHMKQmSKYAIuUK2i7hgPm28a8piX5NTUtM/LKQ== - dependencies: - punycode "1.3.2" - querystring "0.2.0" - use@^3.1.0: version "3.1.1" resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" @@ -12852,52 +8469,12 @@ util-deprecate@^1.0.1, util-deprecate@~1.0.1: resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== -util.promisify@~1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/util.promisify/-/util.promisify-1.0.1.tgz#6baf7774b80eeb0f7520d8b81d07982a59abbaee" - integrity sha512-g9JpC/3He3bm38zsLupWryXHoEcS22YHthuPQSJdMy6KNrzIRzWqcsHzD/WUnqe45whVou4VIsPew37DoXWNrA== - dependencies: - define-properties "^1.1.3" - es-abstract "^1.17.2" - has-symbols "^1.0.1" - object.getownpropertydescriptors "^2.1.0" - -util@0.10.3: - version "0.10.3" - resolved "https://registry.yarnpkg.com/util/-/util-0.10.3.tgz#7afb1afe50805246489e3db7fe0ed379336ac0f9" - integrity sha512-5KiHfsmkqacuKjkRkdV7SsfDJ2EGiPsK92s2MhNSY0craxjTdKTtqKsJaCWp4LW33ZZ0OPUv1WO/TFvNQRiQxQ== - dependencies: - inherits "2.0.1" - -util@^0.11.0: - version "0.11.1" - resolved "https://registry.yarnpkg.com/util/-/util-0.11.1.tgz#3236733720ec64bb27f6e26f421aaa2e1b588d61" - integrity sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ== - dependencies: - inherits "2.0.3" - -util@~0.12.0: - version "0.12.5" - resolved "https://registry.yarnpkg.com/util/-/util-0.12.5.tgz#5f17a6059b73db61a875668781a1c2b136bd6fbc" - integrity sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA== - dependencies: - inherits "^2.0.3" - is-arguments "^1.0.4" - is-generator-function "^1.0.7" - is-typed-array "^1.1.3" - which-typed-array "^1.1.2" - -uuid@^3.3.2: - version "3.4.0" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" - integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== - uuid@^8.3.2: version "8.3.2" resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== -v8-compile-cache@2.3.0, v8-compile-cache@^2.0.0, v8-compile-cache@^2.1.1: +v8-compile-cache@2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee" integrity sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA== @@ -12933,30 +8510,6 @@ validate-npm-package-name@^4.0.0: dependencies: builtins "^5.0.0" -vendors@^1.0.0: - version "1.0.4" - resolved "https://registry.yarnpkg.com/vendors/-/vendors-1.0.4.tgz#e2b800a53e7a29b93506c3cf41100d16c4c4ad8e" - integrity sha512-/juG65kTL4Cy2su4P8HjtkTxk6VmJDiOPBufWniqQ6wknac6jNiXS9vU+hO3wgusiyqWlzTbVHi0dyJqRONg3w== - -verror@1.10.0: - version "1.10.0" - resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" - integrity sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw== - dependencies: - assert-plus "^1.0.0" - core-util-is "1.0.2" - extsprintf "^1.2.0" - -vlq@^0.2.2: - version "0.2.3" - resolved "https://registry.yarnpkg.com/vlq/-/vlq-0.2.3.tgz#8f3e4328cf63b1540c0d67e1b2778386f8975b26" - integrity sha512-DRibZL6DsNhIgYQ+wNdWDL2SL3bKPlVrRiBqV5yuMm++op8W4kGFtaQfCs4KEJn0wBZcHVHJ3eoywX8983k1ow== - -vm-browserify@^1.0.0, vm-browserify@^1.0.1: - version "1.1.2" - resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0" - integrity sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ== - vscode-oniguruma@^1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/vscode-oniguruma/-/vscode-oniguruma-1.7.0.tgz#439bfad8fe71abd7798338d1cd3dc53a8beea94b" @@ -12967,45 +8520,11 @@ vscode-textmate@^8.0.0: resolved "https://registry.yarnpkg.com/vscode-textmate/-/vscode-textmate-8.0.0.tgz#2c7a3b1163ef0441097e0b5d6389cd5504b59e5d" integrity sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg== -w3c-hr-time@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz#0a89cdf5cc15822df9c360543676963e0cc308cd" - integrity sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ== - dependencies: - browser-process-hrtime "^1.0.0" - -w3c-xmlserializer@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-1.1.2.tgz#30485ca7d70a6fd052420a3d12fd90e6339ce794" - integrity sha512-p10l/ayESzrBMYWRID6xbuCKh2Fp77+sA0doRuGn4tTIMrrZVeqfpKjXHY+oDh3K4nLdPgNwMTVP6Vp4pvqbNg== - dependencies: - domexception "^1.0.1" - webidl-conversions "^4.0.2" - xml-name-validator "^3.0.0" - walk-up-path@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/walk-up-path/-/walk-up-path-1.0.0.tgz#d4745e893dd5fd0dbb58dd0a4c6a33d9c9fec53e" integrity sha512-hwj/qMDUEjCU5h0xr90KGCf0tg0/LgJbmOWgrWKYlcJZM7XvquvUJZ0G/HMGr7F7OQMOUuPHWP9JpriinkAlkg== -watchpack-chokidar2@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/watchpack-chokidar2/-/watchpack-chokidar2-2.0.1.tgz#38500072ee6ece66f3769936950ea1771be1c957" - integrity sha512-nCFfBIPKr5Sh61s4LPpy1Wtfi0HE8isJ3d2Yb5/Ppw2P2B/3eVSEBjKfN0fmHJSK14+31KwMKmcrzs2GM4P0Ww== - dependencies: - chokidar "^2.1.8" - -watchpack@^1.7.4: - version "1.7.5" - resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.7.5.tgz#1267e6c55e0b9b5be44c2023aed5437a2c26c453" - integrity sha512-9P3MWk6SrKjHsGkLT2KHXdQ/9SNkyoJbabxnKOoJepsvJjJG8uYTR3yTPxPQvNDI3w4Nz1xnE0TLHK4RIVe/MQ== - dependencies: - graceful-fs "^4.1.2" - neo-async "^2.5.0" - optionalDependencies: - chokidar "^3.4.1" - watchpack-chokidar2 "^2.0.1" - wcwidth@^1.0.0, wcwidth@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8" @@ -13018,82 +8537,11 @@ webidl-conversions@^3.0.0: resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ== -webidl-conversions@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad" - integrity sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg== - -webpack-cli@^3.2.3: - version "3.3.12" - resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-3.3.12.tgz#94e9ada081453cd0aa609c99e500012fd3ad2d4a" - integrity sha512-NVWBaz9k839ZH/sinurM+HcDvJOTXwSjYp1ku+5XKeOC03z8v5QitnK/x+lAxGXFyhdayoIf/GOpv85z3/xPag== - dependencies: - chalk "^2.4.2" - cross-spawn "^6.0.5" - enhanced-resolve "^4.1.1" - findup-sync "^3.0.0" - global-modules "^2.0.0" - import-local "^2.0.0" - interpret "^1.4.0" - loader-utils "^1.4.0" - supports-color "^6.1.0" - v8-compile-cache "^2.1.1" - yargs "^13.3.2" - -webpack-sources@^1.4.0, webpack-sources@^1.4.1: - version "1.4.3" - resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.4.3.tgz#eedd8ec0b928fbf1cbfe994e22d2d890f330a933" - integrity sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ== - dependencies: - source-list-map "^2.0.0" - source-map "~0.6.1" - -webpack@^4.29.6: - version "4.46.0" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.46.0.tgz#bf9b4404ea20a073605e0a011d188d77cb6ad542" - integrity sha512-6jJuJjg8znb/xRItk7bkT0+Q7AHCYjjFnvKIWQPkNIOyRqoCGvkOs0ipeQzrqz4l5FtN5ZI/ukEHroeX/o1/5Q== - dependencies: - "@webassemblyjs/ast" "1.9.0" - "@webassemblyjs/helper-module-context" "1.9.0" - "@webassemblyjs/wasm-edit" "1.9.0" - "@webassemblyjs/wasm-parser" "1.9.0" - acorn "^6.4.1" - ajv "^6.10.2" - ajv-keywords "^3.4.1" - chrome-trace-event "^1.0.2" - enhanced-resolve "^4.5.0" - eslint-scope "^4.0.3" - json-parse-better-errors "^1.0.2" - loader-runner "^2.4.0" - loader-utils "^1.2.3" - memory-fs "^0.4.1" - micromatch "^3.1.10" - mkdirp "^0.5.3" - neo-async "^2.6.1" - node-libs-browser "^2.2.1" - schema-utils "^1.0.0" - tapable "^1.1.3" - terser-webpack-plugin "^1.4.3" - watchpack "^1.7.4" - webpack-sources "^1.4.1" - well-known-symbols@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/well-known-symbols/-/well-known-symbols-2.0.0.tgz#e9c7c07dbd132b7b84212c8174391ec1f9871ba5" integrity sha512-ZMjC3ho+KXo0BfJb7JgtQ5IBuvnShdlACNkKkdsqBmYw3bPAaJfPeYUo6tLUaT5tG/Gkh7xkpBhKRQ9e7pyg9Q== -whatwg-encoding@^1.0.1, whatwg-encoding@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz#5abacf777c32166a51d085d6b4f3e7d27113ddb0" - integrity sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw== - dependencies: - iconv-lite "0.4.24" - -whatwg-mimetype@^2.2.0, whatwg-mimetype@^2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf" - integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g== - whatwg-url@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" @@ -13102,15 +8550,6 @@ whatwg-url@^5.0.0: tr46 "~0.0.3" webidl-conversions "^3.0.0" -whatwg-url@^7.0.0: - version "7.1.0" - resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-7.1.0.tgz#c2c492f1eca612988efd3d2266be1b9fc6170d06" - integrity sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg== - dependencies: - lodash.sortby "^4.7.0" - tr46 "^1.0.1" - webidl-conversions "^4.0.2" - which-boxed-primitive@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6" @@ -13122,16 +8561,6 @@ which-boxed-primitive@^1.0.2: is-string "^1.0.5" is-symbol "^1.0.3" -which-collection@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/which-collection/-/which-collection-1.0.1.tgz#70eab71ebbbd2aefaf32f917082fc62cdcb70906" - integrity sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A== - dependencies: - is-map "^2.0.1" - is-set "^2.0.1" - is-weakmap "^2.0.1" - is-weakset "^2.0.1" - which-module@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" @@ -13148,7 +8577,7 @@ which-typed-array@^1.1.11, which-typed-array@^1.1.13: gopd "^1.0.1" has-tostringtag "^1.0.0" -which-typed-array@^1.1.2, which-typed-array@^1.1.9: +which-typed-array@^1.1.9: version "1.1.9" resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.9.tgz#307cf898025848cf995e795e8423c7f337efbde6" integrity sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA== @@ -13160,7 +8589,7 @@ which-typed-array@^1.1.2, which-typed-array@^1.1.9: has-tostringtag "^1.0.0" is-typed-array "^1.1.10" -which@^1.2.14, which@^1.2.9, which@^1.3.1: +which@^1.2.9: version "1.3.1" resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== @@ -13181,23 +8610,11 @@ wide-align@^1.1.5: dependencies: string-width "^1.0.2 || 2 || 3 || 4" -word-wrap@~1.2.3: - version "1.2.3" - resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" - integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== - wordwrap@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" integrity sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q== -worker-farm@^1.7.0: - version "1.7.0" - resolved "https://registry.yarnpkg.com/worker-farm/-/worker-farm-1.7.0.tgz#26a94c5391bbca926152002f69b84a4bf772e5a8" - integrity sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw== - dependencies: - errno "~0.1.7" - wrap-ansi@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-5.1.0.tgz#1fd1f67235d5b6d0fee781056001bfb694c03b09" @@ -13289,36 +8706,27 @@ write-pkg@^4.0.0: type-fest "^0.4.1" write-json-file "^3.2.0" -ws@^5.1.1: - version "5.2.3" - resolved "https://registry.yarnpkg.com/ws/-/ws-5.2.3.tgz#05541053414921bc29c63bee14b8b0dd50b07b3d" - integrity sha512-jZArVERrMsKUatIdnLzqvcfydI85dvd/Fp1u/VOpfdDWQ4c9qWXe+VIeAbQ5FrDwciAkr+lzofXLz3Kuf26AOA== - dependencies: - async-limiter "~1.0.0" - -ws@^6.1.0, ws@^6.1.2: - version "6.2.2" - resolved "https://registry.yarnpkg.com/ws/-/ws-6.2.2.tgz#dd5cdbd57a9979916097652d78f1cc5faea0c32e" - integrity sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw== - dependencies: - async-limiter "~1.0.0" +ws@>=8.14.2: + version "8.16.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.16.0.tgz#d1cd774f36fbc07165066a60e40323eab6446fd4" + integrity sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ== ws@^8.13.0: version "8.13.0" resolved "https://registry.yarnpkg.com/ws/-/ws-8.13.0.tgz#9a9fb92f93cf41512a0735c8f4dd09b8a1211cd0" integrity sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA== -xml-name-validator@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a" - integrity sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw== +ws@~7.4.2: + version "7.4.6" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.6.tgz#5654ca8ecdeee47c33a9a4bf6d28e2be2980377c" + integrity sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A== -xmlchars@^2.1.1: - version "2.2.0" - resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" - integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw== +xmlhttprequest-ssl@~1.6.2: + version "1.6.3" + resolved "https://registry.yarnpkg.com/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.6.3.tgz#03b713873b01659dfa2c1c5d056065b27ddc2de6" + integrity sha512-3XfeQE/wNkvrIktn2Kf0869fC0BN6UpydVasGIeSm2B1Llihf7/0UfZM+eCkOw3P7bP4+qPgqhm7ZoxuJtFU0Q== -xtend@^4.0.0, xtend@^4.0.1, xtend@^4.0.2, xtend@~4.0.1: +xtend@~4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== @@ -13333,16 +8741,6 @@ y18n@^5.0.5: resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== -yallist@^2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" - integrity sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A== - -yallist@^3.0.2: - version "3.1.1" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" - integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== - yallist@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" @@ -13376,7 +8774,7 @@ yargs-parser@^20.2.2, yargs-parser@^20.2.3, yargs-parser@^20.2.9: resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== -yargs@^13.3.2: +yargs@^13.2.2: version "13.3.2" resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.3.2.tgz#ad7ffefec1aa59565ac915f82dccb38a9c31a2dd" integrity sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw== @@ -13418,13 +8816,10 @@ yargs@^17.6.2, yargs@^17.7.2: y18n "^5.0.5" yargs-parser "^21.1.1" -yauzl@^2.10.0: - version "2.10.0" - resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9" - integrity sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g== - dependencies: - buffer-crc32 "~0.2.3" - fd-slicer "~1.1.0" +yeast@0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/yeast/-/yeast-0.1.2.tgz#008e06d8094320c372dbc2f8ed76a0ca6c8ac419" + integrity sha512-8HFIh676uyGYP6wP13R/j6OJ/1HwJ46snpvzE7aHAN3Ryqh2yX6Xox2B4CUmTwwOIzlG3Bs7ocsP5dZH/R1Qbg== yocto-queue@^0.1.0: version "0.1.0" From f69a72909ab36b251427f0a8290120b11924945d Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Thu, 15 Feb 2024 10:38:10 -0800 Subject: [PATCH 229/234] chore: Deduplicate yarn.lock --- yarn.lock | 413 ++++++------------------------------------------------ 1 file changed, 40 insertions(+), 373 deletions(-) diff --git a/yarn.lock b/yarn.lock index a673631944..cf6770106b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -16,14 +16,7 @@ jsesc "^2.5.1" source-map "^0.5.0" -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.18.6.tgz#3b25d38c89600baa2dcc219edfa88a74eb2c427a" - integrity sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q== - dependencies: - "@babel/highlight" "^7.18.6" - -"@babel/code-frame@^7.22.13", "@babel/code-frame@^7.23.5": +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.18.6", "@babel/code-frame@^7.22.13", "@babel/code-frame@^7.23.5": version "7.23.5" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.23.5.tgz#9009b69a8c602293476ad598ff53e4562e15c244" integrity sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA== @@ -31,16 +24,7 @@ "@babel/highlight" "^7.23.4" chalk "^2.4.2" -"@babel/generator@^7.20.1": - version "7.20.4" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.20.4.tgz#4d9f8f0c30be75fd90a0562099a26e5839602ab8" - integrity sha512-luCf7yk/cm7yab6CAW1aiFnmEfBJplb/JojV56MYEK7ziWfGmFlTfmL9Ehwfy4gFhbjBfWO1wj7/TuSbVNEEtA== - dependencies: - "@babel/types" "^7.20.2" - "@jridgewell/gen-mapping" "^0.3.2" - jsesc "^2.5.1" - -"@babel/generator@^7.23.6": +"@babel/generator@^7.20.1", "@babel/generator@^7.23.6": version "7.23.6" resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.23.6.tgz#9e1fca4811c77a10580d17d26b57b036133f3c2e" integrity sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw== @@ -50,25 +34,12 @@ "@jridgewell/trace-mapping" "^0.3.17" jsesc "^2.5.1" -"@babel/helper-environment-visitor@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz#0c0cee9b35d2ca190478756865bb3528422f51be" - integrity sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg== - -"@babel/helper-environment-visitor@^7.22.20": +"@babel/helper-environment-visitor@^7.18.9", "@babel/helper-environment-visitor@^7.22.20": version "7.22.20" resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz#96159db61d34a29dba454c959f5ae4a649ba9167" integrity sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA== -"@babel/helper-function-name@^7.19.0": - version "7.19.0" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz#941574ed5390682e872e52d3f38ce9d1bef4648c" - integrity sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w== - dependencies: - "@babel/template" "^7.18.10" - "@babel/types" "^7.19.0" - -"@babel/helper-function-name@^7.23.0": +"@babel/helper-function-name@^7.19.0", "@babel/helper-function-name@^7.23.0": version "7.23.0" resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz#1f9a3cdbd5b2698a670c30d2735f9af95ed52759" integrity sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw== @@ -76,64 +47,31 @@ "@babel/template" "^7.22.15" "@babel/types" "^7.23.0" -"@babel/helper-hoist-variables@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz#d4d2c8fb4baeaa5c68b99cc8245c56554f926678" - integrity sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q== - dependencies: - "@babel/types" "^7.18.6" - -"@babel/helper-hoist-variables@^7.22.5": +"@babel/helper-hoist-variables@^7.18.6", "@babel/helper-hoist-variables@^7.22.5": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz#c01a007dac05c085914e8fb652b339db50d823bb" integrity sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw== dependencies: "@babel/types" "^7.22.5" -"@babel/helper-split-export-declaration@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz#7367949bc75b20c6d5a5d4a97bba2824ae8ef075" - integrity sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA== - dependencies: - "@babel/types" "^7.18.6" - -"@babel/helper-split-export-declaration@^7.22.6": +"@babel/helper-split-export-declaration@^7.18.6", "@babel/helper-split-export-declaration@^7.22.6": version "7.22.6" resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz#322c61b7310c0997fe4c323955667f18fcefb91c" integrity sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g== dependencies: "@babel/types" "^7.22.5" -"@babel/helper-string-parser@^7.19.4": - version "7.19.4" - resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz#38d3acb654b4701a9b77fb0615a96f775c3a9e63" - integrity sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw== - -"@babel/helper-string-parser@^7.23.4": +"@babel/helper-string-parser@^7.19.4", "@babel/helper-string-parser@^7.23.4": version "7.23.4" resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz#9478c707febcbbe1ddb38a3d91a2e054ae622d83" integrity sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ== -"@babel/helper-validator-identifier@^7.18.6", "@babel/helper-validator-identifier@^7.19.1": - version "7.19.1" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz#7eea834cf32901ffdc1a7ee555e2f9c27e249ca2" - integrity sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w== - -"@babel/helper-validator-identifier@^7.22.20": +"@babel/helper-validator-identifier@^7.18.6", "@babel/helper-validator-identifier@^7.19.1", "@babel/helper-validator-identifier@^7.22.20": version "7.22.20" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz#c4ae002c61d2879e724581d96665583dbc1dc0e0" integrity sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A== -"@babel/highlight@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.18.6.tgz#81158601e93e2563795adcbfbdf5d64be3f2ecdf" - integrity sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g== - dependencies: - "@babel/helper-validator-identifier" "^7.18.6" - chalk "^2.0.0" - js-tokens "^4.0.0" - -"@babel/highlight@^7.23.4": +"@babel/highlight@^7.18.6", "@babel/highlight@^7.23.4": version "7.23.4" resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.23.4.tgz#edaadf4d8232e1a961432db785091207ead0621b" integrity sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A== @@ -142,26 +80,12 @@ chalk "^2.4.2" js-tokens "^4.0.0" -"@babel/parser@^7.18.10", "@babel/parser@^7.20.1", "@babel/parser@^7.7.0": - version "7.20.3" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.20.3.tgz#5358cf62e380cf69efcb87a7bb922ff88bfac6e2" - integrity sha512-OP/s5a94frIPXwjzEcv5S/tpQfc6XhxYUnmWpgdqMWGgYCuErA3SzozaRAMQgSZWKeTJxht9aWAkUY+0UzvOFg== - -"@babel/parser@^7.22.15", "@babel/parser@^7.23.6": +"@babel/parser@^7.18.10", "@babel/parser@^7.20.1", "@babel/parser@^7.22.15", "@babel/parser@^7.23.6", "@babel/parser@^7.7.0": version "7.23.6" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.23.6.tgz#ba1c9e512bda72a47e285ae42aff9d2a635a9e3b" integrity sha512-Z2uID7YJ7oNvAI20O9X0bblw7Qqs8Q2hFy0R9tAfnfLkp5MW0UH9eUvnDSnFwKZ0AvgS1ucqR4KzvVHgnke1VQ== -"@babel/template@^7.18.10": - version "7.18.10" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.18.10.tgz#6f9134835970d1dbf0835c0d100c9f38de0c5e71" - integrity sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA== - dependencies: - "@babel/code-frame" "^7.18.6" - "@babel/parser" "^7.18.10" - "@babel/types" "^7.18.10" - -"@babel/template@^7.22.15": +"@babel/template@^7.18.10", "@babel/template@^7.22.15": version "7.22.15" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.22.15.tgz#09576efc3830f0430f4548ef971dde1350ef2f38" integrity sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w== @@ -170,7 +94,7 @@ "@babel/parser" "^7.22.15" "@babel/types" "^7.22.15" -"@babel/traverse@^7.23.6": +"@babel/traverse@^7.23.6", "@babel/traverse@^7.7.0": version "7.23.7" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.23.7.tgz#9a7bf285c928cb99b5ead19c3b1ce5b310c9c305" integrity sha512-tY3mM8rH9jM0YHFGyfC0/xf+SB5eKUu7HPj7/k3fpi9dAlsMc5YbQvDi0Sh2QTPXqMhyaAtzAr807TIyfQrmyg== @@ -186,23 +110,7 @@ debug "^4.3.1" globals "^11.1.0" -"@babel/traverse@^7.7.0": - version "7.20.1" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.20.1.tgz#9b15ccbf882f6d107eeeecf263fbcdd208777ec8" - integrity sha512-d3tN8fkVJwFLkHkBN479SOsw4DMZnz8cdbL/gvuDuzy3TS6Nfw80HuQqhw1pITbIruHyh7d1fMA47kWzmcUEGA== - dependencies: - "@babel/code-frame" "^7.18.6" - "@babel/generator" "^7.20.1" - "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-function-name" "^7.19.0" - "@babel/helper-hoist-variables" "^7.18.6" - "@babel/helper-split-export-declaration" "^7.18.6" - "@babel/parser" "^7.20.1" - "@babel/types" "^7.20.0" - debug "^4.1.0" - globals "^11.1.0" - -"@babel/types@^7.0.0", "@babel/types@^7.20.7", "@babel/types@^7.22.15", "@babel/types@^7.22.5", "@babel/types@^7.23.0", "@babel/types@^7.23.6": +"@babel/types@^7.0.0", "@babel/types@^7.17.0", "@babel/types@^7.18.10", "@babel/types@^7.18.6", "@babel/types@^7.19.0", "@babel/types@^7.20.0", "@babel/types@^7.20.2", "@babel/types@^7.20.7", "@babel/types@^7.22.15", "@babel/types@^7.22.5", "@babel/types@^7.23.0", "@babel/types@^7.23.6", "@babel/types@^7.7.0": version "7.23.6" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.23.6.tgz#be33fdb151e1f5a56877d704492c240fc71c7ccd" integrity sha512-+uarb83brBzPKN38NX1MkB6vb6+mwvR6amUulqAE7ccQw1pEl+bCia9TbdG1lsnFP7lZySvUn37CHyXQdfTwzg== @@ -211,15 +119,6 @@ "@babel/helper-validator-identifier" "^7.22.20" to-fast-properties "^2.0.0" -"@babel/types@^7.17.0", "@babel/types@^7.18.10", "@babel/types@^7.18.6", "@babel/types@^7.19.0", "@babel/types@^7.20.0", "@babel/types@^7.20.2", "@babel/types@^7.7.0": - version "7.20.2" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.20.2.tgz#67ac09266606190f496322dbaff360fdaa5e7842" - integrity sha512-FnnvsNWgZCr232sqtXggapvlkk/tuwR/qhGzcmxI0GXLCjmPYQPzio2FbdlWuY6y1sHFfQKk+rRbUZ9VStQMog== - dependencies: - "@babel/helper-string-parser" "^7.19.4" - "@babel/helper-validator-identifier" "^7.19.1" - to-fast-properties "^2.0.0" - "@bcoe/v8-coverage@^0.2.3": version "0.2.3" resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" @@ -335,12 +234,7 @@ "@jridgewell/sourcemap-codec" "^1.4.10" "@jridgewell/trace-mapping" "^0.3.9" -"@jridgewell/resolve-uri@^3.0.3": - version "3.1.0" - resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78" - integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w== - -"@jridgewell/resolve-uri@^3.1.0": +"@jridgewell/resolve-uri@^3.0.3", "@jridgewell/resolve-uri@^3.1.0": version "3.1.1" resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz#c08679063f279615a3326583ba3a90d1d82cc721" integrity sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA== @@ -358,25 +252,12 @@ "@jridgewell/gen-mapping" "^0.3.0" "@jridgewell/trace-mapping" "^0.3.9" -"@jridgewell/sourcemap-codec@^1.4.10": - version "1.4.14" - resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24" - integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== - -"@jridgewell/sourcemap-codec@^1.4.14": +"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14": version "1.4.15" resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== -"@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.9": - version "0.3.15" - resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.15.tgz#aba35c48a38d3fd84b37e66c9c0423f9744f9774" - integrity sha512-oWZNOULl+UbhsgB51uuZzglikfIKSUBO/M9W2OfEjn7cmqoAiCgmv9lyACTUacZwBz0ITnJ2NqjU8Tx0DHL88g== - dependencies: - "@jridgewell/resolve-uri" "^3.0.3" - "@jridgewell/sourcemap-codec" "^1.4.10" - -"@jridgewell/trace-mapping@^0.3.17": +"@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.9": version "0.3.22" resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.22.tgz#72a621e5de59f5f1ef792d0793a82ee20f645e4c" integrity sha512-Wf963MzWtA2sjrNt+g18IAln9lKnlRp+K2eH4jjIoF1wYeq3aMREpG09xhlhdzS0EjwU7qmUJYangWa+151vZw== @@ -2395,15 +2276,7 @@ cache-base@^1.0.1: union-value "^1.0.0" unset-value "^1.0.0" -call-bind@^1.0.0, call-bind@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" - integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA== - dependencies: - function-bind "^1.1.1" - get-intrinsic "^1.0.2" - -call-bind@^1.0.4, call-bind@^1.0.5: +call-bind@^1.0.0, call-bind@^1.0.2, call-bind@^1.0.4, call-bind@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.5.tgz#6fa2b7845ce0ea49bf4d8b9ef64727a2c2e2e513" integrity sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ== @@ -2744,12 +2617,7 @@ component-emitter@1.2.1: resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.2.1.tgz#137918d6d78283f7df7a6b7c5a63e140e69425e6" integrity sha512-jPatnhd33viNplKjqXKRkGU345p263OIWzDL2wH3LGIGp5Kojo+uXizHmOADRvhGFFTnJqX3jBAKP6vvmSDKcA== -component-emitter@^1.2.1: - version "1.3.0" - resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0" - integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg== - -component-emitter@~1.3.0: +component-emitter@^1.2.1, component-emitter@~1.3.0: version "1.3.1" resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.1.tgz#ef1d5796f7d93f135ee6fb684340b26403c97d17" integrity sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ== @@ -3354,46 +3222,7 @@ error-stack-parser@^1.3.3: dependencies: stackframe "^0.3.1" -es-abstract@^1.19.0, es-abstract@^1.20.4: - version "1.21.1" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.21.1.tgz#e6105a099967c08377830a0c9cb589d570dd86c6" - integrity sha512-QudMsPOz86xYz/1dG1OuGBKOELjCh99IIWHLzy5znUB6j8xG2yMA7bfTV86VSqKF+Y/H08vQPR+9jyXpuC6hfg== - dependencies: - available-typed-arrays "^1.0.5" - call-bind "^1.0.2" - es-set-tostringtag "^2.0.1" - es-to-primitive "^1.2.1" - function-bind "^1.1.1" - function.prototype.name "^1.1.5" - get-intrinsic "^1.1.3" - get-symbol-description "^1.0.0" - globalthis "^1.0.3" - gopd "^1.0.1" - has "^1.0.3" - has-property-descriptors "^1.0.0" - has-proto "^1.0.1" - has-symbols "^1.0.3" - internal-slot "^1.0.4" - is-array-buffer "^3.0.1" - is-callable "^1.2.7" - is-negative-zero "^2.0.2" - is-regex "^1.1.4" - is-shared-array-buffer "^1.0.2" - is-string "^1.0.7" - is-typed-array "^1.1.10" - is-weakref "^1.0.2" - object-inspect "^1.12.2" - object-keys "^1.1.1" - object.assign "^4.1.4" - regexp.prototype.flags "^1.4.3" - safe-regex-test "^1.0.0" - string.prototype.trimend "^1.0.6" - string.prototype.trimstart "^1.0.6" - typed-array-length "^1.0.4" - unbox-primitive "^1.0.2" - which-typed-array "^1.1.9" - -es-abstract@^1.22.1: +es-abstract@^1.19.0, es-abstract@^1.20.4, es-abstract@^1.22.1: version "1.22.3" resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.22.3.tgz#48e79f5573198de6dee3589195727f4f74bc4f32" integrity sha512-eiiY8HQeYfYH2Con2berK+To6GrK2RxbPawDkGq4UiCQQfZHb6wX9qQqkbpPqaxQFcl8d9QzZqo0tGE0VcrdwA== @@ -3836,7 +3665,7 @@ fast-diff@^1.2.0: resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.2.0.tgz#73ee11982d86caaf7959828d519cfe927fac5f03" integrity sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w== -fast-glob@3: +fast-glob@3, fast-glob@^3.2.11, fast-glob@^3.2.9: version "3.3.2" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.2.tgz#a904501e57cfdd2ffcded45e99a54fef55e46129" integrity sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow== @@ -3870,17 +3699,6 @@ fast-glob@^2.0.2: merge2 "^1.2.3" micromatch "^3.1.10" -fast-glob@^3.2.11, fast-glob@^3.2.9: - version "3.3.0" - resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.0.tgz#7c40cb491e1e2ed5664749e87bfb516dbe8727c0" - integrity sha512-ChDuvbOypPuNjO8yIDf36x7BlZX1smcUMTTcyoIjycexOxd6DFsKsg21qVBzEmr3G7fUKIRy2/psii+CIUt7FA== - dependencies: - "@nodelib/fs.stat" "^2.0.2" - "@nodelib/fs.walk" "^1.2.3" - glob-parent "^5.1.2" - merge2 "^1.3.0" - micromatch "^4.0.4" - fast-json-stable-stringify@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" @@ -4098,27 +3916,12 @@ fsevents@~2.3.2: resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== -function-bind@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" - integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== - -function-bind@^1.1.2: +function-bind@^1.1.1, function-bind@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== -function.prototype.name@^1.1.5: - version "1.1.5" - resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.5.tgz#cce0505fe1ffb80503e6f9e46cc64e46a12a9621" - integrity sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.19.0" - functions-have-names "^1.2.2" - -function.prototype.name@^1.1.6: +function.prototype.name@^1.1.5, function.prototype.name@^1.1.6: version "1.1.6" resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.6.tgz#cdf315b7d90ee77a4c6ee216c3c3362da07533fd" integrity sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg== @@ -4152,16 +3955,7 @@ get-caller-file@^2.0.1, get-caller-file@^2.0.5: resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== -get-intrinsic@^1.0.2, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3, get-intrinsic@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.0.tgz#7ad1dc0535f3a2904bba075772763e5051f6d05f" - integrity sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q== - dependencies: - function-bind "^1.1.1" - has "^1.0.3" - has-symbols "^1.0.3" - -get-intrinsic@^1.2.1, get-intrinsic@^1.2.2: +get-intrinsic@^1.0.2, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3, get-intrinsic@^1.2.0, get-intrinsic@^1.2.1, get-intrinsic@^1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.2.tgz#281b7622971123e1ef4b3c90fd7539306da93f3b" integrity sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA== @@ -4382,12 +4176,7 @@ gopd@^1.0.1: dependencies: get-intrinsic "^1.1.3" -graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.6: - version "4.2.10" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c" - integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== - -graceful-fs@^4.1.9: +graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.1.9, graceful-fs@^4.2.0, graceful-fs@^4.2.6: version "4.2.11" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== @@ -4441,14 +4230,7 @@ has-flag@^4.0.0: resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== -has-property-descriptors@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz#610708600606d36961ed04c196193b6a607fa861" - integrity sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ== - dependencies: - get-intrinsic "^1.1.1" - -has-property-descriptors@^1.0.1: +has-property-descriptors@^1.0.0, has-property-descriptors@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz#52ba30b6c5ec87fd89fa574bc1c39125c6f65340" integrity sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg== @@ -4771,16 +4553,7 @@ inquirer@^8.2.4: through "^2.3.6" wrap-ansi "^7.0.0" -internal-slot@^1.0.4: - version "1.0.5" - resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.5.tgz#f2a2ee21f668f8627a4667f309dc0f4fb6674986" - integrity sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ== - dependencies: - get-intrinsic "^1.2.0" - has "^1.0.3" - side-channel "^1.0.4" - -internal-slot@^1.0.5: +internal-slot@^1.0.4, internal-slot@^1.0.5: version "1.0.6" resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.6.tgz#37e756098c4911c5e912b8edbf71ed3aa116f930" integrity sha512-Xj6dv+PsbtwyPpEflsejS+oIZxmMlV44zAhG479uYu89MsjcYOhCFnNyKrkJrihbsiasQyY0afoCl/9BLR65bg== @@ -4873,20 +4646,13 @@ is-ci@^2.0.0: dependencies: ci-info "^2.0.0" -is-core-module@^2.13.0, is-core-module@^2.13.1: +is-core-module@^2.13.0, is-core-module@^2.13.1, is-core-module@^2.5.0, is-core-module@^2.8.1, is-core-module@^2.9.0: version "2.13.1" resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.13.1.tgz#ad0d7532c6fea9da1ebdc82742d74525c6273384" integrity sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw== dependencies: hasown "^2.0.0" -is-core-module@^2.5.0, is-core-module@^2.8.1, is-core-module@^2.9.0: - version "2.11.0" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.11.0.tgz#ad4cb3e3863e814523c96f3f58d26cc570ff0144" - integrity sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw== - dependencies: - has "^1.0.3" - is-data-descriptor@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" @@ -5135,18 +4901,7 @@ is-text-path@^1.0.1: dependencies: text-extensions "^1.0.0" -is-typed-array@^1.1.10, is-typed-array@^1.1.9: - version "1.1.10" - resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.10.tgz#36a5b5cb4189b575d1a3e4b08536bfb485801e3f" - integrity sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A== - dependencies: - available-typed-arrays "^1.0.5" - call-bind "^1.0.2" - for-each "^0.3.3" - gopd "^1.0.1" - has-tostringtag "^1.0.0" - -is-typed-array@^1.1.12: +is-typed-array@^1.1.10, is-typed-array@^1.1.12, is-typed-array@^1.1.9: version "1.1.12" resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.12.tgz#d0bab5686ef4a76f7a73097b95470ab199c57d4a" integrity sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg== @@ -6333,12 +6088,7 @@ object-copy@^0.1.0: define-property "^0.2.5" kind-of "^3.0.3" -object-inspect@^1.12.2, object-inspect@^1.9.0: - version "1.12.3" - resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.3.tgz#ba62dffd67ee256c8c086dfae69e016cd1f198b9" - integrity sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g== - -object-inspect@^1.13.1: +object-inspect@^1.12.2, object-inspect@^1.13.1, object-inspect@^1.9.0: version "1.13.1" resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.1.tgz#b96c6109324ccfef6b12216a956ca4dc2ff94bc2" integrity sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ== @@ -7147,16 +6897,7 @@ regex-not@^1.0.0, regex-not@^1.0.2: extend-shallow "^3.0.2" safe-regex "^1.1.0" -regexp.prototype.flags@^1.4.3: - version "1.5.0" - resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.0.tgz#fe7ce25e7e4cca8db37b6634c8a2c7009199b9cb" - integrity sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA== - dependencies: - call-bind "^1.0.2" - define-properties "^1.2.0" - functions-have-names "^1.2.3" - -regexp.prototype.flags@^1.5.1: +regexp.prototype.flags@^1.4.3, regexp.prototype.flags@^1.5.1: version "1.5.1" resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.1.tgz#90ce989138db209f81492edd734183ce99f9677e" integrity sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg== @@ -7212,16 +6953,7 @@ resolve-url@^0.2.1: resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" integrity sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg== -resolve@^1.10.0, resolve@^1.12.0, resolve@^1.17.0, resolve@^1.19.0: - version "1.22.1" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177" - integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw== - dependencies: - is-core-module "^2.9.0" - path-parse "^1.0.7" - supports-preserve-symlinks-flag "^1.0.0" - -resolve@^1.22.4: +resolve@^1.10.0, resolve@^1.12.0, resolve@^1.17.0, resolve@^1.19.0, resolve@^1.22.4: version "1.22.8" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d" integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw== @@ -7369,12 +7101,7 @@ semver@7.3.4: dependencies: lru-cache "^6.0.0" -semver@^6.0.0, semver@^6.3.0: - version "6.3.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" - integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== - -semver@^6.3.1: +semver@^6.0.0, semver@^6.3.0, semver@^6.3.1: version "6.3.1" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== @@ -7493,12 +7220,7 @@ signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^3.0.3, signal-exit@^3.0.7: resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== -signal-exit@^4.0.1: - version "4.0.2" - resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.0.2.tgz#ff55bb1d9ff2114c13b400688fa544ac63c36967" - integrity sha512-MY2/qGx4enyjprQnFaZsHib3Yadh3IXyV2C321GY0pjGfVBu4un0uDJkwgdxqO+Rdx8JMT8IfJIRwbYVz3Ob3Q== - -signal-exit@^4.1.0: +signal-exit@^4.0.1, signal-exit@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== @@ -7820,16 +7542,7 @@ string.prototype.trim@^1.2.8: define-properties "^1.2.0" es-abstract "^1.22.1" -string.prototype.trimend@^1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz#c4a27fa026d979d79c04f17397f250a462944533" - integrity sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.20.4" - -string.prototype.trimend@^1.0.7: +string.prototype.trimend@^1.0.6, string.prototype.trimend@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.7.tgz#1bb3afc5008661d73e2dc015cd4853732d6c471e" integrity sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA== @@ -7838,16 +7551,7 @@ string.prototype.trimend@^1.0.7: define-properties "^1.2.0" es-abstract "^1.22.1" -string.prototype.trimstart@^1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz#e90ab66aa8e4007d92ef591bbf3cd422c56bdcf4" - integrity sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.20.4" - -string.prototype.trimstart@^1.0.7: +string.prototype.trimstart@^1.0.6, string.prototype.trimstart@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.7.tgz#d4cdb44b83a4737ffbac2d406e405d43d0184298" integrity sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg== @@ -8154,17 +7858,12 @@ trim-newlines@^3.0.0: resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-3.0.1.tgz#260a5d962d8b752425b32f3a7db0dcacd176c144" integrity sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw== -ts-api-utils@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-1.0.1.tgz#8144e811d44c749cd65b2da305a032510774452d" - integrity sha512-lC/RGlPmwdrIBFTX59wwNzqh7aR2otPNPR/5brHZm/XKFYKsfqxihXUe9pU3JI+3vGkl+vyCoNNnPhJn3aLK1A== - -ts-api-utils@~1.0.1: +ts-api-utils@^1.0.1, ts-api-utils@~1.0.1: version "1.0.3" resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-1.0.3.tgz#f12c1c781d04427313dbac808f453f050e54a331" integrity sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg== -tsconfig-paths@^3.15.0: +tsconfig-paths@^3.15.0, tsconfig-paths@^3.9.0: version "3.15.0" resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz#5299ec605e55b1abb23ec939ef15edaf483070d4" integrity sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg== @@ -8174,16 +7873,6 @@ tsconfig-paths@^3.15.0: minimist "^1.2.6" strip-bom "^3.0.0" -tsconfig-paths@^3.9.0: - version "3.14.1" - resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz#ba0734599e8ea36c862798e920bcf163277b137a" - integrity sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ== - dependencies: - "@types/json5" "^0.0.29" - json5 "^1.0.1" - minimist "^1.2.6" - strip-bom "^3.0.0" - tsd@^0.28.1: version "0.28.1" resolved "https://registry.yarnpkg.com/tsd/-/tsd-0.28.1.tgz#a470bd88a80ff138496c71606072893fe5820e62" @@ -8197,7 +7886,7 @@ tsd@^0.28.1: path-exists "^4.0.0" read-pkg-up "^7.0.0" -"tslib@1 || 2": +"tslib@1 || 2", tslib@^2.1.0, tslib@^2.3.0, tslib@^2.4.0: version "2.6.2" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae" integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q== @@ -8207,11 +7896,6 @@ tslib@^1.8.1, tslib@^1.9.0: resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== -tslib@^2.1.0, tslib@^2.3.0, tslib@^2.4.0: - version "2.4.1" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.1.tgz#0d0bfbaac2880b91e22df0768e55be9753a5b17e" - integrity sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA== - tsutils@3, tsutils@~3.21.0: version "3.21.0" resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623" @@ -8566,7 +8250,7 @@ which-module@^2.0.0: resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" integrity sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q== -which-typed-array@^1.1.11, which-typed-array@^1.1.13: +which-typed-array@^1.1.11, which-typed-array@^1.1.13, which-typed-array@^1.1.9: version "1.1.13" resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.13.tgz#870cd5be06ddb616f504e7b039c4c24898184d36" integrity sha512-P5Nra0qjSncduVPEAr7xhoF5guty49ArDTwzJ/yNuPIbZppyRxFQsRCWrocxIY+CnMVG+qfbU2FmDKyvSGClow== @@ -8577,18 +8261,6 @@ which-typed-array@^1.1.11, which-typed-array@^1.1.13: gopd "^1.0.1" has-tostringtag "^1.0.0" -which-typed-array@^1.1.9: - version "1.1.9" - resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.9.tgz#307cf898025848cf995e795e8423c7f337efbde6" - integrity sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA== - dependencies: - available-typed-arrays "^1.0.5" - call-bind "^1.0.2" - for-each "^0.3.3" - gopd "^1.0.1" - has-tostringtag "^1.0.0" - is-typed-array "^1.1.10" - which@^1.2.9: version "1.3.1" resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" @@ -8706,16 +8378,11 @@ write-pkg@^4.0.0: type-fest "^0.4.1" write-json-file "^3.2.0" -ws@>=8.14.2: +ws@>=8.14.2, ws@^8.13.0: version "8.16.0" resolved "https://registry.yarnpkg.com/ws/-/ws-8.16.0.tgz#d1cd774f36fbc07165066a60e40323eab6446fd4" integrity sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ== -ws@^8.13.0: - version "8.13.0" - resolved "https://registry.yarnpkg.com/ws/-/ws-8.13.0.tgz#9a9fb92f93cf41512a0735c8f4dd09b8a1211cd0" - integrity sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA== - ws@~7.4.2: version "7.4.6" resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.6.tgz#5654ca8ecdeee47c33a9a4bf6d28e2be2980377c" From a9ba43b45411bad85f3e3d5d142f559b927dee4f Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Thu, 15 Feb 2024 10:38:43 -0800 Subject: [PATCH 230/234] chore: Reupdate yarn.lock --- yarn.lock | 50 +++++++++++++++++++++++++------------------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/yarn.lock b/yarn.lock index cf6770106b..0e33a09b98 100644 --- a/yarn.lock +++ b/yarn.lock @@ -16,7 +16,7 @@ jsesc "^2.5.1" source-map "^0.5.0" -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.18.6", "@babel/code-frame@^7.22.13", "@babel/code-frame@^7.23.5": +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.22.13", "@babel/code-frame@^7.23.5": version "7.23.5" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.23.5.tgz#9009b69a8c602293476ad598ff53e4562e15c244" integrity sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA== @@ -24,7 +24,7 @@ "@babel/highlight" "^7.23.4" chalk "^2.4.2" -"@babel/generator@^7.20.1", "@babel/generator@^7.23.6": +"@babel/generator@^7.23.6": version "7.23.6" resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.23.6.tgz#9e1fca4811c77a10580d17d26b57b036133f3c2e" integrity sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw== @@ -34,12 +34,12 @@ "@jridgewell/trace-mapping" "^0.3.17" jsesc "^2.5.1" -"@babel/helper-environment-visitor@^7.18.9", "@babel/helper-environment-visitor@^7.22.20": +"@babel/helper-environment-visitor@^7.22.20": version "7.22.20" resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz#96159db61d34a29dba454c959f5ae4a649ba9167" integrity sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA== -"@babel/helper-function-name@^7.19.0", "@babel/helper-function-name@^7.23.0": +"@babel/helper-function-name@^7.23.0": version "7.23.0" resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz#1f9a3cdbd5b2698a670c30d2735f9af95ed52759" integrity sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw== @@ -47,31 +47,31 @@ "@babel/template" "^7.22.15" "@babel/types" "^7.23.0" -"@babel/helper-hoist-variables@^7.18.6", "@babel/helper-hoist-variables@^7.22.5": +"@babel/helper-hoist-variables@^7.22.5": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz#c01a007dac05c085914e8fb652b339db50d823bb" integrity sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw== dependencies: "@babel/types" "^7.22.5" -"@babel/helper-split-export-declaration@^7.18.6", "@babel/helper-split-export-declaration@^7.22.6": +"@babel/helper-split-export-declaration@^7.22.6": version "7.22.6" resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz#322c61b7310c0997fe4c323955667f18fcefb91c" integrity sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g== dependencies: "@babel/types" "^7.22.5" -"@babel/helper-string-parser@^7.19.4", "@babel/helper-string-parser@^7.23.4": +"@babel/helper-string-parser@^7.23.4": version "7.23.4" resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz#9478c707febcbbe1ddb38a3d91a2e054ae622d83" integrity sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ== -"@babel/helper-validator-identifier@^7.18.6", "@babel/helper-validator-identifier@^7.19.1", "@babel/helper-validator-identifier@^7.22.20": +"@babel/helper-validator-identifier@^7.22.20": version "7.22.20" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz#c4ae002c61d2879e724581d96665583dbc1dc0e0" integrity sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A== -"@babel/highlight@^7.18.6", "@babel/highlight@^7.23.4": +"@babel/highlight@^7.23.4": version "7.23.4" resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.23.4.tgz#edaadf4d8232e1a961432db785091207ead0621b" integrity sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A== @@ -80,12 +80,12 @@ chalk "^2.4.2" js-tokens "^4.0.0" -"@babel/parser@^7.18.10", "@babel/parser@^7.20.1", "@babel/parser@^7.22.15", "@babel/parser@^7.23.6", "@babel/parser@^7.7.0": +"@babel/parser@^7.22.15", "@babel/parser@^7.23.6", "@babel/parser@^7.7.0": version "7.23.6" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.23.6.tgz#ba1c9e512bda72a47e285ae42aff9d2a635a9e3b" integrity sha512-Z2uID7YJ7oNvAI20O9X0bblw7Qqs8Q2hFy0R9tAfnfLkp5MW0UH9eUvnDSnFwKZ0AvgS1ucqR4KzvVHgnke1VQ== -"@babel/template@^7.18.10", "@babel/template@^7.22.15": +"@babel/template@^7.22.15": version "7.22.15" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.22.15.tgz#09576efc3830f0430f4548ef971dde1350ef2f38" integrity sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w== @@ -110,7 +110,7 @@ debug "^4.3.1" globals "^11.1.0" -"@babel/types@^7.0.0", "@babel/types@^7.17.0", "@babel/types@^7.18.10", "@babel/types@^7.18.6", "@babel/types@^7.19.0", "@babel/types@^7.20.0", "@babel/types@^7.20.2", "@babel/types@^7.20.7", "@babel/types@^7.22.15", "@babel/types@^7.22.5", "@babel/types@^7.23.0", "@babel/types@^7.23.6", "@babel/types@^7.7.0": +"@babel/types@^7.0.0", "@babel/types@^7.17.0", "@babel/types@^7.20.7", "@babel/types@^7.22.15", "@babel/types@^7.22.5", "@babel/types@^7.23.0", "@babel/types@^7.23.6", "@babel/types@^7.7.0": version "7.23.6" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.23.6.tgz#be33fdb151e1f5a56877d704492c240fc71c7ccd" integrity sha512-+uarb83brBzPKN38NX1MkB6vb6+mwvR6amUulqAE7ccQw1pEl+bCia9TbdG1lsnFP7lZySvUn37CHyXQdfTwzg== @@ -234,7 +234,7 @@ "@jridgewell/sourcemap-codec" "^1.4.10" "@jridgewell/trace-mapping" "^0.3.9" -"@jridgewell/resolve-uri@^3.0.3", "@jridgewell/resolve-uri@^3.1.0": +"@jridgewell/resolve-uri@^3.1.0": version "3.1.1" resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz#c08679063f279615a3326583ba3a90d1d82cc721" integrity sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA== @@ -2343,7 +2343,7 @@ chalk@4.1.0: ansi-styles "^4.1.0" supports-color "^7.1.0" -chalk@^2.0.0, chalk@^2.3.0, chalk@^2.4.2: +chalk@^2.3.0, chalk@^2.4.2: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== @@ -3222,7 +3222,7 @@ error-stack-parser@^1.3.3: dependencies: stackframe "^0.3.1" -es-abstract@^1.19.0, es-abstract@^1.20.4, es-abstract@^1.22.1: +es-abstract@^1.20.4, es-abstract@^1.22.1: version "1.22.3" resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.22.3.tgz#48e79f5573198de6dee3589195727f4f74bc4f32" integrity sha512-eiiY8HQeYfYH2Con2berK+To6GrK2RxbPawDkGq4UiCQQfZHb6wX9qQqkbpPqaxQFcl8d9QzZqo0tGE0VcrdwA== @@ -3921,7 +3921,7 @@ function-bind@^1.1.1, function-bind@^1.1.2: resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== -function.prototype.name@^1.1.5, function.prototype.name@^1.1.6: +function.prototype.name@^1.1.6: version "1.1.6" resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.6.tgz#cdf315b7d90ee77a4c6ee216c3c3362da07533fd" integrity sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg== @@ -3931,7 +3931,7 @@ function.prototype.name@^1.1.5, function.prototype.name@^1.1.6: es-abstract "^1.22.1" functions-have-names "^1.2.3" -functions-have-names@^1.2.2, functions-have-names@^1.2.3: +functions-have-names@^1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== @@ -4553,7 +4553,7 @@ inquirer@^8.2.4: through "^2.3.6" wrap-ansi "^7.0.0" -internal-slot@^1.0.4, internal-slot@^1.0.5: +internal-slot@^1.0.5: version "1.0.6" resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.6.tgz#37e756098c4911c5e912b8edbf71ed3aa116f930" integrity sha512-Xj6dv+PsbtwyPpEflsejS+oIZxmMlV44zAhG479uYu89MsjcYOhCFnNyKrkJrihbsiasQyY0afoCl/9BLR65bg== @@ -4646,7 +4646,7 @@ is-ci@^2.0.0: dependencies: ci-info "^2.0.0" -is-core-module@^2.13.0, is-core-module@^2.13.1, is-core-module@^2.5.0, is-core-module@^2.8.1, is-core-module@^2.9.0: +is-core-module@^2.13.0, is-core-module@^2.13.1, is-core-module@^2.5.0, is-core-module@^2.8.1: version "2.13.1" resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.13.1.tgz#ad0d7532c6fea9da1ebdc82742d74525c6273384" integrity sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw== @@ -5091,7 +5091,7 @@ json-stringify-safe@^5.0.1: resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" integrity sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA== -json5@^1.0.1, json5@^1.0.2: +json5@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.2.tgz#63d98d60f21b313b77c4d6da18bfa69d80e1d593" integrity sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA== @@ -6088,7 +6088,7 @@ object-copy@^0.1.0: define-property "^0.2.5" kind-of "^3.0.3" -object-inspect@^1.12.2, object-inspect@^1.13.1, object-inspect@^1.9.0: +object-inspect@^1.13.1, object-inspect@^1.9.0: version "1.13.1" resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.1.tgz#b96c6109324ccfef6b12216a956ca4dc2ff94bc2" integrity sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ== @@ -6897,7 +6897,7 @@ regex-not@^1.0.0, regex-not@^1.0.2: extend-shallow "^3.0.2" safe-regex "^1.1.0" -regexp.prototype.flags@^1.4.3, regexp.prototype.flags@^1.5.1: +regexp.prototype.flags@^1.5.1: version "1.5.1" resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.1.tgz#90ce989138db209f81492edd734183ce99f9677e" integrity sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg== @@ -7542,7 +7542,7 @@ string.prototype.trim@^1.2.8: define-properties "^1.2.0" es-abstract "^1.22.1" -string.prototype.trimend@^1.0.6, string.prototype.trimend@^1.0.7: +string.prototype.trimend@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.7.tgz#1bb3afc5008661d73e2dc015cd4853732d6c471e" integrity sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA== @@ -7551,7 +7551,7 @@ string.prototype.trimend@^1.0.6, string.prototype.trimend@^1.0.7: define-properties "^1.2.0" es-abstract "^1.22.1" -string.prototype.trimstart@^1.0.6, string.prototype.trimstart@^1.0.7: +string.prototype.trimstart@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.7.tgz#d4cdb44b83a4737ffbac2d406e405d43d0184298" integrity sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg== @@ -8250,7 +8250,7 @@ which-module@^2.0.0: resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" integrity sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q== -which-typed-array@^1.1.11, which-typed-array@^1.1.13, which-typed-array@^1.1.9: +which-typed-array@^1.1.11, which-typed-array@^1.1.13: version "1.1.13" resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.13.tgz#870cd5be06ddb616f504e7b039c4c24898184d36" integrity sha512-P5Nra0qjSncduVPEAr7xhoF5guty49ArDTwzJ/yNuPIbZppyRxFQsRCWrocxIY+CnMVG+qfbU2FmDKyvSGClow== From 6822eb439650214e6808ce9092df02077f837809 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Thu, 15 Feb 2024 10:41:32 -0800 Subject: [PATCH 231/234] docs(cli): Update demo instructions to reflect branch change --- packages/cli/demo/README.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/packages/cli/demo/README.md b/packages/cli/demo/README.md index aff6b4d3d2..498b291f38 100644 --- a/packages/cli/demo/README.md +++ b/packages/cli/demo/README.md @@ -5,13 +5,12 @@ One day, this will be as simple as: > npm install -g @endo/cli -Until that day, you will need to clone the Endo repository, check out the Endo -branch, install dependencies using Yarn, and arrange for the Endo command to -be available as `endo` in whatever shell you favor. +Until that day, you will need to clone the Endo repository, install +dependencies using Yarn, and arrange for the Endo command to be available as +`endo` in whatever shell you favor. ``` > git clone https://github.com/endojs/endo.git -> git checkout endo > yarn > alias endo=$PWD/packages/cli/bin/endo ``` From bdb188c9d3960cbb7d25bf6dcccb16a3bf65e6de Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Thu, 15 Feb 2024 10:44:01 -0800 Subject: [PATCH 232/234] chore: Reformat --- packages/bundle-source/cache.js | 5 ++--- packages/captp/src/ts-types.d.ts | 8 ++++---- packages/compartment-mapper/src/policy.js | 5 +++-- packages/ses/src/error/tame-console.js | 16 ++++++++-------- packages/ses/test/package/test.cjs | 4 ++-- 5 files changed, 19 insertions(+), 19 deletions(-) diff --git a/packages/bundle-source/cache.js b/packages/bundle-source/cache.js index c09102e09e..5467ea43e5 100644 --- a/packages/bundle-source/cache.js +++ b/packages/bundle-source/cache.js @@ -265,9 +265,8 @@ export const makeBundleCache = (wr, cwd, readPowers, opts) => { const todo = makePromiseKit(); loaded.set(targetName, { rootPath, bundle: todo.promise }); const bundle = await validateOrAdd(rootPath, targetName, log) - .then( - ({ bundleFileName }) => - import(`${wr.readOnly().neighbor(bundleFileName)}`), + .then(({ bundleFileName }) => + import(`${wr.readOnly().neighbor(bundleFileName)}`), ) .then(m => harden(m.default)); assert.equal(bundle.moduleFormat, 'endoZipBase64'); diff --git a/packages/captp/src/ts-types.d.ts b/packages/captp/src/ts-types.d.ts index 4f056d05be..e5b8f5ea2a 100644 --- a/packages/captp/src/ts-types.d.ts +++ b/packages/captp/src/ts-types.d.ts @@ -14,10 +14,10 @@ import type { ESingleMethod, Unpromise } from '@endo/eventual-send'; export type TrapHandler = T extends (...args: infer P) => infer R ? (...args: P) => Unpromise : T extends Record - ? { - [K in keyof T]: Unpromise; - } - : T; + ? { + [K in keyof T]: Unpromise; + } + : T; /* Types for Trap proxy calls. */ type TrapSingleMethod = { diff --git a/packages/compartment-mapper/src/policy.js b/packages/compartment-mapper/src/policy.js index ce724a171f..02dc1b6f9c 100644 --- a/packages/compartment-mapper/src/policy.js +++ b/packages/compartment-mapper/src/policy.js @@ -240,8 +240,9 @@ export const makeDeferredAttenuatorsProvider = ( } attenuatorSpecifier = defaultAttenuator; } - const { namespace } = - await compartments[ATTENUATORS_COMPARTMENT].import(attenuatorSpecifier); + const { namespace } = await compartments[ATTENUATORS_COMPARTMENT].import( + attenuatorSpecifier, + ); return namespace; }; } diff --git a/packages/ses/src/error/tame-console.js b/packages/ses/src/error/tame-console.js index b0726bfc67..c16e3190ff 100644 --- a/packages/ses/src/error/tame-console.js +++ b/packages/ses/src/error/tame-console.js @@ -58,14 +58,14 @@ export const tameConsole = ( typeof globalThis.console !== 'undefined' ? globalThis.console : typeof globalThis.print === 'function' - ? // Make a good-enough console for eshost (including only functions that - // log at a specific level with no special argument interpretation). - // https://console.spec.whatwg.org/#logging - (p => freeze({ debug: p, log: p, info: p, warn: p, error: p }))( - // eslint-disable-next-line no-undef - wrapLogger(globalThis.print), - ) - : undefined + ? // Make a good-enough console for eshost (including only functions that + // log at a specific level with no special argument interpretation). + // https://console.spec.whatwg.org/#logging + (p => freeze({ debug: p, log: p, info: p, warn: p, error: p }))( + // eslint-disable-next-line no-undef + wrapLogger(globalThis.print), + ) + : undefined ); // Upgrade a log-only console (as in `eshost -h SpiderMonkey`). diff --git a/packages/ses/test/package/test.cjs b/packages/ses/test/package/test.cjs index 75967cc16f..fe16acf552 100644 --- a/packages/ses/test/package/test.cjs +++ b/packages/ses/test/package/test.cjs @@ -6,8 +6,8 @@ let globalRef = typeof global !== 'undefined' ? global : typeof self !== 'undefined' - ? self - : undefined; + ? self + : undefined; if (globalRef && !globalRef.globalThis) { globalRef.globalThis = globalRef; } From 4a4ae5497427e97fb41e51a44c27c13b4d0d4c26 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Thu, 15 Feb 2024 10:51:15 -0800 Subject: [PATCH 233/234] fix(daemon): Import order lint fix --- packages/daemon/src/daemon.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/daemon/src/daemon.js b/packages/daemon/src/daemon.js index 73b9099434..b35578ead7 100644 --- a/packages/daemon/src/daemon.js +++ b/packages/daemon/src/daemon.js @@ -5,6 +5,7 @@ import { E, Far } from '@endo/far'; import { makePromiseKit } from '@endo/promise-kit'; +import { q } from '@endo/errors'; import { makeRefReader } from './ref-reader.js'; import { makeMailboxMaker } from './mail.js'; import { makeGuestMaker } from './guest.js'; @@ -12,7 +13,6 @@ import { makeHostMaker } from './host.js'; import { assertPetName } from './pet-name.js'; import { makeContextMaker } from './context.js'; import { parseFormulaIdentifier } from './formula-identifier.js'; -import { q } from '@endo/errors'; /** @type {import('./types.js').EndoGuest} */ const leastAuthority = Far('EndoGuest', { From ff58c065130b774ccb3c9cddbb7562505f0e43a0 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Thu, 15 Feb 2024 11:09:45 -0800 Subject: [PATCH 234/234] fix: Relax lint for optional chaining and nullish coallescing for daemon --- packages/cli/package.json | 2 +- packages/daemon/package.json | 2 +- packages/eslint-plugin/lib/configs/daemon.js | 7 +++++++ 3 files changed, 9 insertions(+), 2 deletions(-) create mode 100644 packages/eslint-plugin/lib/configs/daemon.js diff --git a/packages/cli/package.json b/packages/cli/package.json index 327c0a5bf8..d35e61b52d 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -66,7 +66,7 @@ }, "eslintConfig": { "extends": [ - "plugin:@endo/internal" + "plugin:@endo/daemon" ] }, "ava": { diff --git a/packages/daemon/package.json b/packages/daemon/package.json index ad9df7ac7c..e5e9c4662b 100644 --- a/packages/daemon/package.json +++ b/packages/daemon/package.json @@ -88,7 +88,7 @@ }, "eslintConfig": { "extends": [ - "plugin:@endo/internal" + "plugin:@endo/daemon" ] }, "ava": { diff --git a/packages/eslint-plugin/lib/configs/daemon.js b/packages/eslint-plugin/lib/configs/daemon.js new file mode 100644 index 0000000000..782ec9d1da --- /dev/null +++ b/packages/eslint-plugin/lib/configs/daemon.js @@ -0,0 +1,7 @@ +module.exports = { + extends: ['plugin:@endo/internal'], + rules: { + '@endo/no-optional-chaining': 'off', + '@endo/no-nullish-coalescing': 'off', + }, +};