Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions .eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ module.exports = {
// This prevents importing Node.js builtins. We currently use them in
// our codebase, so this rule is disabled. This rule should be disabled
// in `@metamask/eslint-config-nodejs` in the future.
'import/no-nodejs-modules': 'off',
'import-x/no-nodejs-modules': 'off',

'import/no-useless-path-segments': [
'import-x/no-useless-path-segments': [
'error',
{
// Enabling this causes false errors in ESM files.
Expand Down Expand Up @@ -63,8 +63,8 @@ module.exports = {
ecmaVersion: '2022',
},
rules: {
'import/extensions': 'off',
'import/no-unassigned-import': 'off',
'import-x/extensions': 'off',
'import-x/no-unassigned-import': 'off',
},
},

Expand Down Expand Up @@ -94,7 +94,7 @@ module.exports = {
{
files: ['*.d.ts'],
rules: {
'import/unambiguous': 'off',
'import-x/unambiguous': 'off',
},
},

Expand Down
20 changes: 10 additions & 10 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,29 +37,29 @@
"@lavamoat/allow-scripts": "^3.0.4",
"@lavamoat/preinstall-always-fail": "^2.0.0",
"@metamask/auto-changelog": "^3.4.4",
"@metamask/eslint-config": "^12.2.0",
"@metamask/eslint-config-nodejs": "^12.1.0",
"@metamask/eslint-config-typescript": "^12.1.0",
"@metamask/eslint-config": "^13.0.0",
"@metamask/eslint-config-nodejs": "^13.0.0",
"@metamask/eslint-config-typescript": "^13.0.0",
"@ts-bridge/cli": "^0.3.0",
"@ts-bridge/shims": "^0.1.1",
"@types/node": "^18.18.14",
"@typescript-eslint/eslint-plugin": "^5.43.0",
"@typescript-eslint/parser": "^5.43.0",
"@typescript-eslint/eslint-plugin": "^8.1.0",
"@typescript-eslint/parser": "^8.1.0",
"@yarnpkg/types": "^4.0.0",
"depcheck": "^1.4.3",
"eslint": "^8.56.0",
"eslint": "^8.57.0",
"eslint-config-prettier": "^8.8.0",
"eslint-plugin-import": "~2.26.0",
"eslint-plugin-jsdoc": "^39.9.1",
"eslint-plugin-n": "^15.7.0",
"eslint-plugin-import-x": "^0.5.1",
"eslint-plugin-jsdoc": "^47.0.2",
"eslint-plugin-n": "^16.6.2",
"eslint-plugin-prettier": "^4.2.1",
"eslint-plugin-promise": "^6.1.1",
"eslint-plugin-vitest": "^0.4.1",
"prettier": "^2.7.1",
"prettier-plugin-packagejson": "^2.3.0",
"rimraf": "^6.0.1",
"typedoc": "^0.24.8",
"typescript": "~4.9.5",
"typescript": "~5.5.4",
"vite": "^5.3.5",
"vite-tsconfig-paths": "^4.3.2",
"vitest": "^2.0.5"
Expand Down
2 changes: 1 addition & 1 deletion packages/extension/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
"rimraf": "^6.0.1",
"typedoc": "^0.24.8",
"typedoc-plugin-missing-exports": "^2.0.0",
"typescript": "~4.9.5",
"typescript": "~5.5.4",
"vite": "^5.3.5",
"vite-plugin-static-copy": "^1.0.6",
"vitest": "^2.0.5"
Expand Down
11 changes: 6 additions & 5 deletions packages/extension/src/background.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/* eslint-disable import/no-unassigned-import */
/* eslint-disable import-x/no-unassigned-import */
import './dev-console.js';
import './endoify.mjs';
/* eslint-enable import/no-unassigned-import */
/* eslint-enable import-x/no-unassigned-import */

import type { ExtensionMessage } from './shared.js';
import { Command, makeHandledCallback } from './shared.js';
Expand All @@ -28,11 +28,12 @@ chrome.action.onClicked.addListener(() => {

/**
* Send a message to the offscreen document.
*
* @param type - The message type.
* @param data - The message data.
* @param data.name - The name to include in the message.
*/
async function sendMessage(type: string, data?: string) {
async function sendMessage(type: string, data?: string): Promise<void> {
await provideOffScreenDocument();

await chrome.runtime.sendMessage({
Expand All @@ -45,7 +46,7 @@ async function sendMessage(type: string, data?: string) {
/**
* Create the offscreen document if it doesn't already exist.
*/
async function provideOffScreenDocument() {
async function provideOffScreenDocument(): Promise<void> {
if (!(await chrome.offscreen.hasDocument())) {
await chrome.offscreen.createDocument({
url: OFFSCREEN_DOCUMENT_PATH,
Expand Down Expand Up @@ -83,7 +84,7 @@ chrome.runtime.onMessage.addListener(
/**
* Close the offscreen document if it exists.
*/
async function closeOffscreenDocument() {
async function closeOffscreenDocument(): Promise<void> {
if (!(await chrome.offscreen.hasDocument())) {
return;
}
Expand Down
2 changes: 1 addition & 1 deletion packages/extension/src/dev-console.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/* eslint-disable import/unambiguous */
/* eslint-disable import-x/unambiguous */
// We set this property on globalThis in the background before lockdown.
Object.defineProperty(globalThis, 'kernel', {
configurable: false,
Expand Down
3 changes: 2 additions & 1 deletion packages/extension/src/iframe-manager.test.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import type { makePromiseKit } from '@endo/promise-kit';
import * as snapsUtils from '@metamask/snaps-utils';
import { vi, beforeEach, describe, it, expect } from 'vitest';

import { Command } from './shared.js';

vi.mock('@endo/promise-kit', () => ({
makePromiseKit: () => {
makePromiseKit: (): ReturnType<typeof makePromiseKit> => {
let resolve: (value: unknown) => void, reject: (reason?: unknown) => void;
const promise = new Promise((_resolve, _reject) => {
resolve = _resolve;
Expand Down
23 changes: 14 additions & 9 deletions packages/extension/src/iframe-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { Command, isWrappedIframeMessage } from './shared.js';
const IFRAME_URI = 'iframe.html';

// The actual <iframe> id, for greater collision resistance.
const getHtmlId = (id: string) => `ocap-iframe-${id}`;
const getHtmlId = (id: string): string => `ocap-iframe-${id}`;

type PromiseCallbacks = Omit<PromiseKit<unknown>, 'promise'>;

Expand All @@ -20,9 +20,9 @@ export class IframeManager {

#currentId: number;

#unresolvedMessages: Map<string, PromiseCallbacks>;
readonly #unresolvedMessages: Map<string, PromiseCallbacks>;

#iframes: Map<string, Window>;
readonly #iframes: Map<string, Window>;

/**
* Create a new IframeManager.
Expand All @@ -48,6 +48,7 @@ export class IframeManager {

/**
* Get the singleton instance of IframeManager.
*
* @returns The singleton instance of IframeManager.
*/
public static getInstance(): IframeManager {
Expand All @@ -59,11 +60,12 @@ export class IframeManager {

/**
* Create a new iframe.
*
* @param id - The id of the iframe to create.
* @returns The iframe's content window, and its id.
*/
async create(id?: string): Promise<readonly [Window, string]> {
const actualId = id === undefined ? this.#nextId() : id;
const actualId = id ?? this.#nextId();
const newWindow = await createWindow(IFRAME_URI, getHtmlId(actualId));
this.#iframes.set(actualId, newWindow);
await this.sendMessage(actualId, { type: Command.Ping, data: null });
Expand All @@ -73,9 +75,10 @@ export class IframeManager {

/**
* Delete an iframe.
*
* @param id - The id of the iframe to delete.
*/
delete(id: string) {
delete(id: string): void {
if (this.#iframes.has(id)) {
// TODO: Handle orphaned messages
this.#iframes.delete(id);
Expand All @@ -95,13 +98,15 @@ export class IframeManager {

/**
* Send a message to an iframe.
*
* @param id - The id of the iframe to send the message to.
* @param message - The message to send.
* @returns A promise that resolves the response to the message.
*/
async sendMessage(
id: string,
message: IframeMessage<Command, string | null>,
) {
): Promise<unknown> {
const iframeWindow = this.#get(id);
if (iframeWindow === undefined) {
throw new Error(`No iframe with id "${id}"`);
Expand All @@ -114,7 +119,7 @@ export class IframeManager {
return promise;
}

#handleMessage(event: MessageEvent) {
#handleMessage(event: MessageEvent): void {
console.debug('Offscreen received message', event);

if (!isWrappedIframeMessage(event.data)) {
Expand All @@ -135,13 +140,13 @@ export class IframeManager {
promiseCallbacks.resolve(message.data);
}

#nextId() {
#nextId(): string {
const id = this.#currentId;
this.#currentId += 1;
return String(id);
}

#get(id: string) {
#get(id: string): Window | undefined {
return this.#iframes.get(id);
}
}
9 changes: 6 additions & 3 deletions packages/extension/src/iframe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,11 @@ window.addEventListener('message', (event: MessageEvent) => {

/**
* Evaluate a string in the default compartment.
*
* @param source - The source string to evaluate.
* @returns The result of the evaluation, or an error message.
*/
function safelyEvaluate(source: string) {
function safelyEvaluate(source: string): string {
try {
return defaultCompartment.evaluate(source);
} catch (error) {
Expand All @@ -59,10 +60,11 @@ function safelyEvaluate(source: string) {

/**
* Stringify an evaluation result.
*
* @param result - The result to stringify.
* @returns The stringified result.
*/
function stringifyResult(result: unknown) {
function stringifyResult(result: unknown): string {
try {
return JSON.stringify(result, null, 2);
} catch {
Expand All @@ -72,6 +74,7 @@ function stringifyResult(result: unknown) {

/**
* Reply to the parent window.
*
* @param event - The message event to respond to.
* @param id - The id of the message to reply to.
* @param messageType - The message type.
Expand All @@ -82,7 +85,7 @@ function reply(
id: string,
messageType: Command,
data: string,
) {
): void {
event.source.postMessage(
{ id, message: { type: messageType, data } },
// @ts-expect-error Incorrect DOM types
Expand Down
6 changes: 4 additions & 2 deletions packages/extension/src/offscreen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,11 @@ iframeManager.create(IFRAME_ID).catch((error) => {

/**
* Reply to the background script.
*
* @param type - The message type.
* @param data - The message data.
*/
async function reply(type: Command, data?: string) {
async function reply(type: Command, data?: string): Promise<void> {
await chrome.runtime.sendMessage({
data: data ?? null,
target: 'background',
Expand All @@ -50,10 +51,11 @@ async function reply(type: Command, data?: string) {

/**
* Evaluate a string in the default iframe.
*
* @param source - The source string to evaluate.
* @returns The result of the evaluation, or an error message.
*/
async function evaluate(source: string) {
async function evaluate(source: string): Promise<string> {
try {
const result = await iframeManager.sendMessage(IFRAME_ID, {
type: Command.Evaluate,
Expand Down
1 change: 1 addition & 0 deletions packages/extension/src/shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export const isWrappedIframeMessage = (

/**
* Wrap an async callback to ensure any errors are at least logged.
*
* @param callback - The async callback to wrap.
* @returns The wrapped callback.
*/
Expand Down
7 changes: 4 additions & 3 deletions packages/extension/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const projectRoot = './src';
* Module specifiers that will be ignored by Rollup if imported, and therefore
* not transformed. **Only applies to JavaScript and TypeScript files.**
*/
const externalModules: Readonly<string[]> = [
const externalModules: readonly string[] = [
'./dev-console.js',
'./endoify.mjs',
];
Expand All @@ -23,7 +23,7 @@ const externalModules: Readonly<string[]> = [
* Files that need to be statically copied to the destination directory.
* Paths are relative from the project root directory.
*/
const staticCopyTargets: Readonly<string[]> = [
const staticCopyTargets: readonly string[] = [
// The extension manifest
'manifest.json',
// External modules
Expand Down Expand Up @@ -67,6 +67,7 @@ export default defineConfig({

/**
* Vite plugin to insert the endoify script before the first script in the head element.
*
* @throws If the HTML document already references the endoify script or lacks the expected
* structure.
* @returns The Vite plugin.
Expand All @@ -76,7 +77,7 @@ function endoifyHtmlFilesPlugin(): Plugin {

return {
name: 'externalize-plugin',
async transformIndexHtml(htmlString) {
async transformIndexHtml(htmlString): Promise<string> {
if (htmlString.includes('endoify.mjs')) {
throw new Error(
`HTML document already references endoify script:\n${htmlString}`,
Expand Down
2 changes: 1 addition & 1 deletion packages/extension/vitest.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@

import { defineConfig, mergeConfig } from 'vitest/config';

import { getDefaultConfig } from '../../vitest.config.packages.js';
import viteConfig from './vite.config.js';
import { getDefaultConfig } from '../../vitest.config.packages.js';

const defaultConfig = getDefaultConfig();
// @ts-expect-error We can and will delete this.
Expand Down
6 changes: 3 additions & 3 deletions packages/shims/.eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ module.exports = {
files: ['src/**/*.mjs'],
globals: { lockdown: 'readonly' },
rules: {
'import/extensions': 'off',
'import/no-unassigned-import': 'off',
'import/no-unresolved': 'off',
'import-x/extensions': 'off',
'import-x/no-unassigned-import': 'off',
'import-x/no-unresolved': 'off',
},
},
],
Expand Down
2 changes: 1 addition & 1 deletion packages/test-utils/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
"devDependencies": {
"rimraf": "^6.0.1",
"ses": "^1.7.0",
"typescript": "~4.9.5",
"typescript": "~5.5.4",
"vitest": "^2.0.5"
},
"engines": {
Expand Down
2 changes: 1 addition & 1 deletion packages/test-utils/src/env/mock-endo.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// eslint-disable-next-line spaced-comment
/// <reference types="ses"/>

globalThis.lockdown = () => undefined;
globalThis.lockdown = (): void => undefined;
globalThis.harden = <Value>(value: Value): Readonly<Value> => value;

export {};
Loading