diff --git a/packages/extension/src/endoify.test.ts b/packages/extension/src/endoify.test.ts new file mode 100644 index 000000000..8b8c02a43 --- /dev/null +++ b/packages/extension/src/endoify.test.ts @@ -0,0 +1,44 @@ +/* eslint-disable @typescript-eslint/explicit-function-return-type */ +import endoified from '@ocap/test-utils/endoified'; +import { describe } from 'vitest'; +import * as vitest from 'vitest'; + +/* eslint-disable vitest/valid-describe-callback */ +describe( + `endoify`, + endoified( + // eslint-disable-next-line @typescript-eslint/no-shadow + ({ it, expect }) => { + for (const assertion of [ + () => typeof globalThis === 'object', + () => typeof lockdown === 'function', + () => typeof repairIntrinsics === 'function', + () => typeof Compartment === 'function', + () => typeof assert === 'function', + () => typeof HandledPromise === 'function', + () => typeof harden === 'function', + () => typeof getStackString === 'function', + () => { + try { + return !Object.assign(harden({ a: 1 }), { b: 2 }); + } catch { + return true; + } + }, + ]) { + it(`asserts ${String(assertion)}`, () => { + console.log(`${String(assertion)} => ${assertion()}`); + expect(assertion(), `${String(assertion)}`).toBe(true); + }); + } + }, + { ...vitest }, + ), +); + +declare global { + // eslint-disable-next-line no-var + var getStackString: (error: Error) => string; + // eslint-disable-next-line no-var, @typescript-eslint/consistent-type-imports + var HandledPromise: import('@endo/eventual-send').HandledPromiseConstructor; +} diff --git a/packages/extension/vite.config.ts b/packages/extension/vite.config.ts index 7d5e5d364..eccec9946 100644 --- a/packages/extension/vite.config.ts +++ b/packages/extension/vite.config.ts @@ -29,9 +29,6 @@ const staticCopyTargets: readonly string[] = [ // External modules 'dev-console.js', '../../shims/dist/endoify.mjs', - // Dependencies of external modules - '../../shims/dist/eventual-send.mjs', - '../../../node_modules/ses/dist/ses.mjs', ]; // https://vitejs.dev/config/ diff --git a/packages/shims/scripts/bundle.js b/packages/shims/scripts/bundle.js index 45157d98d..76e1d4cd4 100644 --- a/packages/shims/scripts/bundle.js +++ b/packages/shims/scripts/bundle.js @@ -1,42 +1,56 @@ +// eslint-disable-next-line import-x/no-unassigned-import import 'ses'; +// eslint-disable-next-line import-x/no-unassigned-import, import-x/extensions import '@endo/lockdown/commit.js'; import bundleSource from '@endo/bundle-source'; -import { copyFile, mkdir, writeFile } from 'fs/promises'; +import { mkdir, writeFile } from 'fs/promises'; import path from 'path'; import { rimraf } from 'rimraf'; import { fileURLToPath } from 'url'; console.log('Bundling shims...'); -const rootDir = path.resolve(import.meta.dirname, '..'); +const rootDir = fileURLToPath(new URL('..', import.meta.url)); const src = path.resolve(rootDir, 'src'); const dist = path.resolve(rootDir, 'dist'); + +await mkdir(dist, { recursive: true }); +await rimraf(`${dist}/*`, { glob: true }); + const fileNames = { endoify: 'endoify.mjs', - eventualSend: 'eventual-send.mjs', - applyLockdown: 'apply-lockdown.mjs', }; -await mkdir(dist, { recursive: true }); -await rimraf(`${dist}/*`); +const { source: endoifyBundleSource } = await bundleSource( + path.resolve(src, fileNames.endoify), + { format: 'endoScript' }, +); -for (const fileName of [fileNames.endoify, fileNames.applyLockdown]) { - await copyFile(path.resolve(src, fileName), path.resolve(dist, fileName)); -} +await writeFile(path.resolve(dist, fileNames.endoify), endoifyBundleSource); -const eventualSendSourcePath = fileURLToPath( - import.meta.resolve('@endo/eventual-send/shim.js'), -); +// const fileNames = { +// endoify: 'endoify.mjs', +// eventualSend: 'eventual-send.mjs', +// applyLockdown: 'apply-lockdown.mjs', +// }; -const { source: eventualSendBundleSource } = await bundleSource( - eventualSendSourcePath, - { format: 'endoScript' }, -); +// for (const fileName of [fileNames.endoify, fileNames.applyLockdown]) { +// await copyFile(path.resolve(src, fileName), path.resolve(dist, fileName)); +// } -await writeFile( - path.resolve(dist, fileNames.eventualSend), - eventualSendBundleSource, -); +// const eventualSendSourcePath = fileURLToPath( +// import.meta.resolve('@endo/eventual-send/shim.js'), +// ); + +// const { source: eventualSendBundleSource } = await bundleSource( +// eventualSendSourcePath, +// { format: 'endoScript' }, +// ); + +// await writeFile( +// path.resolve(dist, fileNames.eventualSend), +// eventualSendBundleSource, +// ); console.log('Success!'); diff --git a/packages/shims/src/apply-lockdown.mjs b/packages/shims/src/apply-lockdown.mjs deleted file mode 100644 index e01804d3a..000000000 --- a/packages/shims/src/apply-lockdown.mjs +++ /dev/null @@ -1,11 +0,0 @@ -import './ses.mjs'; - -lockdown({ - consoleTaming: 'unsafe', - dateTaming: 'unsafe', - domainTaming: 'unsafe', - errorTaming: 'unsafe', - mathTaming: 'unsafe', - overrideTaming: 'severe', - stackFiltering: 'verbose', -}); diff --git a/packages/shims/src/endoify.mjs b/packages/shims/src/endoify.mjs index 824aa9c32..2208eeac8 100644 --- a/packages/shims/src/endoify.mjs +++ b/packages/shims/src/endoify.mjs @@ -1,5 +1,5 @@ -import './ses.mjs'; -import './eventual-send.mjs'; +import 'ses'; +import '@endo/eventual-send/shim.js'; lockdown({ consoleTaming: 'unsafe', diff --git a/packages/test-utils/package.json b/packages/test-utils/package.json index c239be901..c577ca1f2 100644 --- a/packages/test-utils/package.json +++ b/packages/test-utils/package.json @@ -10,6 +10,7 @@ "type": "module", "exports": { ".": "./src/index.ts", + "./endoified": "./src/endoified.ts", "./package.json": "./package.json" }, "scripts": { @@ -17,6 +18,7 @@ "test": "echo 'No tests.' && exit 0" }, "devDependencies": { + "jsdom": "^24.1.1", "rimraf": "^6.0.1", "ses": "^1.7.0", "typescript": "~4.9.5", diff --git a/packages/test-utils/src/endoified.ts b/packages/test-utils/src/endoified.ts new file mode 100644 index 000000000..a3cbb64d7 --- /dev/null +++ b/packages/test-utils/src/endoified.ts @@ -0,0 +1,78 @@ +/* eslint-disable n/no-sync */ +import jsdom from 'jsdom'; +import { spawnSync } from 'node:child_process'; +import { readFileSync, existsSync } from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const endoifyBundleSourceTextURL = new URL( + '../../shims/dist/endoify.mjs', + import.meta.url.replace(/^https?:\/\/.*?\/@fs\//u, 'file:///'), +); +const endoifyShimsBundleSourceLocation = fileURLToPath( + endoifyBundleSourceTextURL.href, +); + +/** + * Run an endoified test with endowments. + * + * @param test - The test to run. It must be a synchronous function that takes `endowments` as its only parameter. + * @param endowments - Endowments passed to the test. They must include `vitest` capabilities like `it` and `expect`. + * @param endoifySourceLocation - Optional path to the endoify bundle. It must either be absolute or relative to `process.cwd()`. + * @returns The return value of the test. + */ +export default function endoified< + // eslint-disable-next-line @typescript-eslint/consistent-type-imports + Endowments extends Partial, +>( + test: (endowments: Endowments) => void, + endowments: Endowments, + endoifySourceLocation?: string | URL, +): () => void { + return () => { + // console.log({ endoifySourceLocation: `${endoifySourceLocation}` }); + + const absoluteEndoifySourcePath = + // eslint-disable-next-line no-nested-ternary + endoifySourceLocation && typeof endoifySourceLocation === 'string' + ? endoifySourceLocation + : endoifySourceLocation && endoifySourceLocation instanceof URL + ? fileURLToPath(endoifySourceLocation.href) + : endoifyShimsBundleSourceLocation; + + const relativeEndoifySourcePath = path.relative( + '.', + absoluteEndoifySourcePath, + ); + + if ( + absoluteEndoifySourcePath === endoifyShimsBundleSourceLocation && + !existsSync(endoifyShimsBundleSourceLocation) + ) { + spawnSync( + 'node', + [ + fileURLToPath( + new URL( + '../../shims/scripts/bundle.js', + import.meta.url.replace(/^https?:\/\/.*?\/@fs\//u, 'file:///'), + ).href, + ), + ], + { stdio: 'inherit' }, + ); + } + + // console.log({ relativeEndoifySourcePath }); + + const evaluatorSourceText = String(test); + const endoifySourceText = readFileSync(relativeEndoifySourcePath, 'utf-8'); + + // console.log({ endoifySourceText }); + + const dom = new jsdom.JSDOM(``, { runScripts: 'outside-only' }); + + dom.window.eval(endoifySourceText); + (dom.window.eval(evaluatorSourceText) as typeof test)(endowments); + }; +} diff --git a/yarn.lock b/yarn.lock index d5df286f8..9a9bbcf84 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1357,6 +1357,7 @@ __metadata: version: 0.0.0-use.local resolution: "@ocap/test-utils@workspace:packages/test-utils" dependencies: + jsdom: "npm:^24.1.1" rimraf: "npm:^6.0.1" ses: "npm:^1.7.0" typescript: "npm:~4.9.5"