From dfa60718d97f311c6fd05c087cc55af5a52abe82 Mon Sep 17 00:00:00 2001 From: kumavis Date: Mon, 22 Jan 2024 16:07:20 -1000 Subject: [PATCH 1/3] 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 2/3] 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 3/3] 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,