Skip to content

Commit

Permalink
web hijinx
Browse files Browse the repository at this point in the history
  • Loading branch information
kumavis committed Oct 5, 2023
1 parent efabcda commit 11c91c7
Show file tree
Hide file tree
Showing 11 changed files with 1,512 additions and 39 deletions.
12 changes: 12 additions & 0 deletions packages/daemon/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Daemon</title>
</head>
<body>
<h1>Daemon</h1>
<p>Daemon is running.</p>
<script src="./dist-daemon-web-bundle.js"> </script>
</body>
</html>
13 changes: 12 additions & 1 deletion packages/daemon/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,10 @@
"./package.json": "./package.json"
},
"scripts": {
"build": "exit 0",
"build": "node ./src/build-web.js",
"dev": "(trap 'kill 0' SIGINT; yarn dev:serve & yarn dev:build)",
"dev:serve": "serve -s .",
"dev:build": "nodemon --watch src --exec yarn build",
"prepack": "tsc --build jsconfig.build.json",
"postpack": "git clean -f '*.d.ts*'",
"cover": "c8 ava",
Expand All @@ -51,7 +54,14 @@
"@endo/stream": "^0.3.29",
"@endo/stream-node": "^0.2.30",
"@endo/where": "^0.3.5",
"browserfs": "^2.0.0",
"buffer": "^6.0.3",
"events": "^3.3.0",
"make-dir": "^4.0.0",
"path-browserify": "^1.0.1",
"ses": "^0.18.8",
"url": "^0.11.3",
"util": "^0.12.5",
"ws": "^8.13.0"
},
"devDependencies": {
Expand All @@ -66,6 +76,7 @@
"eslint-plugin-eslint-comments": "^3.1.2",
"eslint-plugin-import": "^2.27.5",
"prettier": "^3.0.0",
"serve": "^14.2.1",
"typescript": "~5.2.2"
},
"files": [
Expand Down
42 changes: 25 additions & 17 deletions packages/daemon/src/browserfs.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -2414,6 +2414,8 @@ __export(emulation_exports, {
});

// src/ApiError.ts
// bugfix kumavis
var ErrorCode = 1
var ErrorCode = /* @__PURE__ */ ((ErrorCode2) => {
ErrorCode2[ErrorCode2["EPERM"] = 1] = "EPERM";
ErrorCode2[ErrorCode2["ENOENT"] = 2] = "ENOENT";
Expand All @@ -2431,23 +2433,29 @@ var ErrorCode = /* @__PURE__ */ ((ErrorCode2) => {
ErrorCode2[ErrorCode2["ENOTEMPTY"] = 39] = "ENOTEMPTY";
ErrorCode2[ErrorCode2["ENOTSUP"] = 95] = "ENOTSUP";
return ErrorCode2;
})(ErrorCode || {});
var ErrorStrings = {};
ErrorStrings[1 /* EPERM */] = "Operation not permitted.";
ErrorStrings[2 /* ENOENT */] = "No such file or directory.";
ErrorStrings[5 /* EIO */] = "Input/output error.";
ErrorStrings[9 /* EBADF */] = "Bad file descriptor.";
ErrorStrings[13 /* EACCES */] = "Permission denied.";
ErrorStrings[16 /* EBUSY */] = "Resource busy or locked.";
ErrorStrings[17 /* EEXIST */] = "File exists.";
ErrorStrings[20 /* ENOTDIR */] = "File is not a directory.";
ErrorStrings[21 /* EISDIR */] = "File is a directory.";
ErrorStrings[22 /* EINVAL */] = "Invalid argument.";
ErrorStrings[27 /* EFBIG */] = "File is too big.";
ErrorStrings[28 /* ENOSPC */] = "No space left on disk.";
ErrorStrings[30 /* EROFS */] = "Cannot modify a read-only file system.";
ErrorStrings[39 /* ENOTEMPTY */] = "Directory is not empty.";
ErrorStrings[95 /* ENOTSUP */] = "Operation is not supported.";
// bugfix kumavis
})({});
// bugfix kumavis
var ErrorStrings = 1
var ErrorStrings = ((ErrorStrings2) => {
ErrorStrings2[1 /* EPERM */] = "Operation not permitted.";
ErrorStrings2[2 /* ENOENT */] = "No such file or directory.";
ErrorStrings2[5 /* EIO */] = "Input/output error.";
ErrorStrings2[9 /* EBADF */] = "Bad file descriptor.";
ErrorStrings2[13 /* EACCES */] = "Permission denied.";
ErrorStrings2[16 /* EBUSY */] = "Resource busy or locked.";
ErrorStrings2[17 /* EEXIST */] = "File exists.";
ErrorStrings2[20 /* ENOTDIR */] = "File is not a directory.";
ErrorStrings2[21 /* EISDIR */] = "File is a directory.";
ErrorStrings2[22 /* EINVAL */] = "Invalid argument.";
ErrorStrings2[27 /* EFBIG */] = "File is too big.";
ErrorStrings2[28 /* ENOSPC */] = "No space left on disk.";
ErrorStrings2[30 /* EROFS */] = "Cannot modify a read-only file system.";
ErrorStrings2[39 /* ENOTEMPTY */] = "Directory is not empty.";
ErrorStrings2[95 /* ENOTSUP */] = "Operation is not supported.";
return ErrorStrings2;
})({});

var ApiError = class extends Error {
/**
* Represents a BrowserFS error. Passed back to applications after a failed
Expand Down
62 changes: 62 additions & 0 deletions packages/daemon/src/build-web.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// 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 { writeBundle } from '@endo/compartment-mapper/bundle.js';
import { makeWritePowers, makeReadPowers } from '@endo/compartment-mapper/node-powers.js';

const { write } = makeWritePowers({ fs });
const { read } = makeReadPowers({ fs });


const daemonModuleLocation = new URL(
'daemon-web.js',
import.meta.url,
).toString();
const daemonBundleLocation = new URL(
'../dist-daemon-web-bundle.js',
import.meta.url,
).toString();
const workerModuleLocation = new URL(
'worker-web.js',
import.meta.url,
).toString();
const workerBundleLocation = new URL(
'../dist-worker-web-bundle.js',
import.meta.url,
).toString();

const bundleOptions = {
// node builtin shims for browser
commonDependencies: {
'util': 'util',
'path': 'path-browserify',
'events': 'events',
'buffer': 'buffer',
// dummy fs
'fs': 'util',
},
}

async function main() {
await writeBundle(
write,
read,
daemonBundleLocation,
daemonModuleLocation,
bundleOptions,
)
await writeBundle(
write,
read,
workerBundleLocation,
workerModuleLocation,
bundleOptions,
)
}

main()
187 changes: 187 additions & 0 deletions packages/daemon/src/daemon-web-powers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
// @ts-check
import { makeQueue } from '@endo/stream';

import {
makeCryptoPowers,
makeFilePowers,
makeDaemonicPersistencePowers,
} from './daemon-node-powers.js';
import { makePetStoreMaker } from './pet-store.js';

export {
// I would prefer to use the web-crypto API, but the hash operations are async
// so we just use node-powers with browserify-crypto.
makeCryptoPowers,
makeFilePowers,
};

/**
* @param {object} opts
* @param {import('./types.js').Locator} opts.locator
* @param {typeof import('url')} opts.url
* @param {import('./types.js').FilePowers} opts.filePowers
* @param {import('./types.js').CryptoPowers} opts.cryptoPowers
* @param {() => Worker} opts.makeWebWorker
* @returns {import('./types.js').DaemonicPowers}
*/
export const makeDaemonicPowers = ({
locator,
url,
filePowers,
cryptoPowers,
makeWebWorker,
}) => {
const { fileURLToPath } = url;

const petStorePowers = makePetStoreMaker(filePowers, locator);
const daemonicPersistencePowers = makeDaemonicPersistencePowers(
fileURLToPath,
filePowers,
cryptoPowers,
locator,
false,
);
const daemonicControlPowers = makeDaemonicControlPowers(
locator,
filePowers,
makeWebWorker,
);

return harden({
crypto: cryptoPowers,
petStore: petStorePowers,
persistence: daemonicPersistencePowers,
control: daemonicControlPowers,
});
};

export const makeWebWorkerWriter = workerContext => {
const finalIterationResult = harden({ done: true, value: undefined });
const nonFinalIterationResult = harden({ done: false, value: undefined });
/** @type {import('@endo/stream').Writer<Uint8Array>} */
const webWorkerWriter = harden({
/** @param {Uint8Array} value */
async next(value) {
workerContext.postMessage({ type: 'next', value });
return nonFinalIterationResult;
},
async return(value) {
workerContext.postMessage({ type: 'return', value });
return finalIterationResult;
},
/**
* @param {Error} error
*/
async throw(error) {
workerContext.postMessage({ type: 'throw', value: error });
return finalIterationResult;
},
[Symbol.asyncIterator]() {
return webWorkerWriter;
},
});
return webWorkerWriter;
};

export const makeWebWorkerReader = workerContext => {
const finalIterationResult = harden({ done: true, value: undefined });

const queue = makeQueue();
workerContext.addEventListener('message', event => {
switch (event.data.type) {
case 'next':
queue.put({ value: event.data.value, done: false });
break;
case 'return':
queue.put({ value: undefined, done: true });
break;
case 'throw':
queue.put(Promise.reject(event.data.value));
break;
}
});

// Adapt the AsyncIterator to the more strict interface of a Stream: must
// have return and throw methods.
/** @type {import('@endo/stream').Reader<Buffer>} */
const reader = {
async next() {
return queue.get();
},
// @ts-ignore
async return() {
return finalIterationResult;
},
// @ts-ignore
async throw(error) {
console.log('> webworker reader throw requested', error, {
isInWorker: typeof window === 'undefined',
});
return finalIterationResult;
},
[Symbol.asyncIterator]() {
return reader;
},
};

return reader;
};

/**
* @param {import('./types.js').Locator} locator
* @param {import('./types.js').FilePowers} filePowers
* @param {() => Worker} makeWebWorker
*/
export const makeDaemonicControlPowers = (locator, filePowers, makeWebWorker) => {
const { cachePath, statePath, ephemeralStatePath } = locator;

/**
* @param {string} id
* @param {Promise<never>} cancelled
*/
const makeWorker = async (id, cancelled) => {
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 worker = makeWebWorker();

const workerReadyP = new Promise(resolve =>
worker.addEventListener('message', resolve, { once: true }),
);

const reader = makeWebWorkerReader(worker);
const writer = makeWebWorkerWriter(worker);

const workerClosed = new Promise(resolve => {});

await workerReadyP;
worker.postMessage({
type: 'ENDO_WORKER_INIT',
id,
statePath: workerStatePath,
ephemeralStatePath: workerEphemeralStatePath,
cachePath: workerCachePath,
});

return {
reader,
writer,
closed: workerClosed,
pid: 0,
};
};

return harden({
makeWorker,
});
};
Loading

0 comments on commit 11c91c7

Please sign in to comment.