Skip to content

Commit

Permalink
Merge endo into endo merged with master
Browse files Browse the repository at this point in the history
  • Loading branch information
kriskowal committed Jan 23, 2024
2 parents d07b7df + 2ce0938 commit 6569aca
Show file tree
Hide file tree
Showing 6 changed files with 149 additions and 57 deletions.
90 changes: 42 additions & 48 deletions packages/daemon/src/daemon.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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:
Expand All @@ -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
Expand Down
29 changes: 29 additions & 0 deletions packages/daemon/src/formula-identifier.js
Original file line number Diff line number Diff line change
@@ -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 };
};
12 changes: 10 additions & 2 deletions packages/daemon/src/guest.js
Original file line number Diff line number Diff line change
Expand Up @@ -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<import('./types.js').EndoGuest>} */
const guest = Far('EndoGuest', {
Expand All @@ -80,8 +86,10 @@ export const makeGuestMaker = ({
send,
list,
followNames,
followMessages,
listMessages,
followMessages,
listEntries,
followEntries,
resolve,
reject,
dismiss,
Expand Down
10 changes: 9 additions & 1 deletion packages/daemon/src/host.js
Original file line number Diff line number Diff line change
Expand Up @@ -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', {
Expand All @@ -434,6 +440,8 @@ export const makeHostMaker = ({
send,
list,
followNames,
listEntries,
followEntries,
remove,
rename,
store,
Expand Down
50 changes: 45 additions & 5 deletions packages/daemon/src/pet-store.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -25,7 +26,7 @@ export const makePetStoreMaker = (filePowers, locator) => {
const petNames = new Map();
/** @type {Map<string, Set<string>>} */
const formulaIdentifiers = new Map();
/** @type {import('./types.js').Topic<{ add: string } | { remove: string }>} */
/** @type {import('./types.js').Topic<({ add: string, value: import('./types.js').FormulaIdentifierRecord } | { remove: string })>} */
const changesTopic = makeChangeTopic();

/** @param {string} petName */
Expand Down Expand Up @@ -104,22 +105,55 @@ 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 formulaIdentifierRecord = parseFormulaIdentifier(formulaIdentifier);
changesTopic.publisher.next({
add: petName,
value: formulaIdentifierRecord,
});
};

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 /** @type {{ add: string }} */({ add: name });
const formulaIdentifierRecord =
formulaIdentifierRecordForName(name);
yield /** type {{ add:string, value: import('./types.js').FormulaIdentifierRecord }} */ {
add: name,
value: formulaIdentifierRecord,
};
}
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
*/
Expand Down Expand Up @@ -199,7 +233,11 @@ export const makePetStoreMaker = (filePowers, locator) => {
formulaPetNames.add(toName);
}

changesTopic.publisher.next({ add: toName });
const formulaIdentifierRecord = parseFormulaIdentifier(formulaIdentifier);
changesTopic.publisher.next({
add: toName,
value: formulaIdentifierRecord,
});
changesTopic.publisher.next({ remove: fromName });
// TODO consider retaining a backlog of overwritten names for recovery
};
Expand All @@ -225,6 +263,8 @@ export const makePetStoreMaker = (filePowers, locator) => {
reverseLookup,
list,
follow,
listEntries,
followEntries,
write,
remove,
rename,
Expand Down
15 changes: 14 additions & 1 deletion packages/daemon/src/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,11 @@ export type MignonicPowers = {
};
};

type FormulaIdentifierRecord = {
type: string;
number: string;
};

type GuestFormula = {
type: 'guest';
host: string;
Expand Down Expand Up @@ -158,12 +163,20 @@ export interface Controller<External = unknown, Internal = unknown> {
export interface PetStore {
has(petName: string): boolean;
list(): Array<string>;
follow(): Promise<FarRef<Reader<{ add: string } | { remove: string }>>>;
listEntries(): Array<[string, FormulaIdentifierRecord]>;
followEntries(): Promise<
FarRef<
Reader<
{ add: string; value: FormulaIdentifierRecord } | { remove: string }
>
>
>;
write(petName: string, formulaIdentifier: string): Promise<void>;
remove(petName: string);
rename(fromPetName: string, toPetName: string);
lookup(petName: string): string | undefined;
reverseLookup(formulaIdentifier: string): Array<string>;
follow(): Promise<FarRef<Reader<{ add: string } | { remove: string }>>>;
}

export type RequestFn = (
Expand Down

0 comments on commit 6569aca

Please sign in to comment.