-
Notifications
You must be signed in to change notification settings - Fork 0
feat: add server support #48
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
9f1ed24
ca72abb
c58b8da
bbfd831
36c21f4
4c651fa
dfa078a
b596732
61fe79d
33ca325
d58fc6e
289d157
67edd74
6152ee7
65ad74a
9850db4
c74cf99
e360201
7bb3345
3f0cb45
0dba8a5
b161972
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -503,6 +503,22 @@ class Runtime extends EventEmitter { | |
| */ | ||
| this.isPackaged = false; | ||
|
|
||
| /** | ||
| * omni: We support a "privileged" mode. This usually is set when the project is running as a server, | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. for comments related to our stuff, we usually use ob, not omni (as a way to stay on par with TurboWarp's tw) but this is okay it's just a TIIIIINY nitpick |
||
| * but other privileged clients can use this too. | ||
| * This is mainly to indicate that system APIs (possibly mocked and/or with a permission system) | ||
| * can be accessed, as provided by the privileged client. | ||
| */ | ||
| this.isPrivileged = false; | ||
|
|
||
| /** | ||
| * omni: Privileged utilities, so that the VM can communicate with a privileged client. | ||
| * This is usally filled in by the server client, but another client can fill this in too, | ||
| * as long as they are compatible and set isPrivileged to true. | ||
| * @type {Object<string, function>} | ||
| */ | ||
| this.privilegedUtils = Object.create(null); | ||
|
|
||
| /** | ||
| * Contains information about the external communication methods that the scripts inside the project | ||
| * can use to send data from inside the project to an external server. | ||
|
|
@@ -947,6 +963,20 @@ class Runtime extends EventEmitter { | |
| return 'PLATFORM_MISMATCH'; | ||
| } | ||
|
|
||
| /** | ||
| * omni: Event name when a web request is forwarded to the VM. | ||
| */ | ||
| static get SERVER_REQUEST () { | ||
| return 'SERVER_REQUEST'; | ||
| } | ||
|
|
||
| /** | ||
| * omni: Event name when a response to a web request is forwarded to the web request handler. | ||
| */ | ||
| static get SERVER_RESPONSE () { | ||
| return 'SERVER_RESPONSE'; | ||
| } | ||
|
|
||
| /** | ||
| * How rapidly we try to step threads by default, in ms. | ||
| */ | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,6 @@ | ||
| const log = require("../util/log"); | ||
| const switches = {}; | ||
| const parser = new DOMParser(); | ||
| const parser = typeof DOMParser === 'undefined' ? null : new DOMParser(); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nice touch to prveent crash go boom There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. hehehe crash go boom |
||
|
|
||
| const define_error_noop = (msg) => { | ||
| log.warn(msg); | ||
|
|
@@ -12,6 +12,21 @@ const define_error_noop = (msg) => { | |
| }; | ||
|
|
||
| function get_extension_switches(id, blocks) { | ||
| // I have no idea what this is doing and why it is trying to monkeypatch Blockly via the DOM from | ||
| // the VM; but, it is blocking server support, so I'm going to mock it for running in Node.js and | ||
| // hope for the best. Contact @someCatInTheWorld if this mocking breaks something horribly. | ||
| if (typeof process !== 'undefined' && process.versions?.node) return { | ||
| opcode: 'un_supported', | ||
| msg: 'unsupported', | ||
|
|
||
| mapFieldValues: {}, | ||
| remapInputName: {}, | ||
|
|
||
| createInputs: {}, | ||
| splitInputs: [], | ||
| remapShadowType: {}, | ||
| }; | ||
someCatInTheWorld marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
coderabbitai[bot] marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| let _switches = {}; | ||
| for (let block of blocks) { | ||
| var blockswitches = block.info.switches; | ||
|
|
||
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,92 @@ | ||
| /* eslint-env node */ | ||
| /* eslint-disable no-console */ | ||
|
|
||
| (async () => { | ||
| const fs = require('node:fs'); | ||
| const os = require('node:os'); | ||
|
|
||
| const Server = require('./server'); | ||
| const setupFileSecurity = require('./setup-file-security'); | ||
|
|
||
| const {resolvePath} = require('./resolve-path'); | ||
|
|
||
| const {default: yargs} = await import('yargs'); | ||
| const {hideBin} = await import('yargs/helpers'); | ||
|
|
||
| const permissions = { | ||
| fileReadAccess: false, | ||
| fileWriteAccess: false, | ||
| fileScope: [os.homedir()], | ||
| networkAccess: false | ||
| }; | ||
|
|
||
| yargs(hideBin(process.argv)) | ||
| .command( | ||
| 'serve [file] [port]', | ||
| 'Runs the project in server mode', | ||
| yarg => ( | ||
| yarg | ||
| .positional('file', { | ||
| type: 'string', | ||
| describe: 'The file to run' | ||
| }) | ||
| .positional('port', { | ||
| type: 'number', | ||
| describe: 'The port to bind on', | ||
| default: 8080 | ||
| }) | ||
| .option('dev', { | ||
| alias: 'D', | ||
| type: 'boolean', | ||
| description: 'Runs with the ability to hot-swap projects' | ||
| }) | ||
| .option('allow-file-read', { | ||
| type: 'boolean', | ||
| description: 'Allows the project to read any file in your home folder' | ||
| }) | ||
| .option('allow-file-write', { | ||
| type: 'boolean', | ||
| description: 'Allows the project to write to any file in your home folder' | ||
| }) | ||
| .option('allow-network-access', { | ||
| type: 'boolean', | ||
| description: 'Allows the project to access anything on the network' | ||
| }) | ||
supervoidcoder marked this conversation as resolved.
Show resolved
Hide resolved
coderabbitai[bot] marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| .option('file-scope', { | ||
| type: 'array', | ||
| description: 'Allows the project to read from the specified folders only' | ||
| }) | ||
| ), argv => { | ||
| if (!argv.file) { | ||
| console.log('No project inputted.'); | ||
| process.exitCode = 1; | ||
| return; | ||
| } | ||
|
|
||
| if (argv.allowFileRead) permissions.fileReadAccess = true; | ||
| if (argv.allowFileWrite) permissions.fileWriteAccess = true; | ||
| if (argv.allowNetworkAccess) permissions.networkAccess = true; | ||
|
|
||
| if (argv.fileScope) { | ||
| permissions.fileScope = argv.fileScope.map(location => resolvePath(location)); | ||
| } | ||
coderabbitai[bot] marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| const server = new Server(!!argv.dev, argv.port); | ||
| setupFileSecurity(server.securityManager, permissions); | ||
|
|
||
| const projectLoadError = () => { | ||
| console.log('Failed to load the project. :('); | ||
| server.halt(); | ||
| process.exitCode = 2; | ||
| }; | ||
|
|
||
| try { | ||
| server.runProject(fs.readFileSync(resolvePath(argv.file))).catch(projectLoadError); | ||
| } catch { | ||
| projectLoadError(); | ||
| return; | ||
| } | ||
coderabbitai[bot] marked this conversation as resolved.
Show resolved
Hide resolved
someCatInTheWorld marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| }) | ||
| .demandCommand() | ||
| .parse(); | ||
| })(); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,43 @@ | ||
| /* eslint-env node */ | ||
|
|
||
| const path = require('node:path'); | ||
| const os = require('node:os'); | ||
|
|
||
| /** | ||
| * omni: A curried function that returns a custom path resolver, based on the inputted values. | ||
| * @param {() => string} homeDir The home directory. | ||
| * @param {() => string} workingDir The working directory. | ||
| * @returns {(location: string) => string} The path resolver. | ||
| */ | ||
| const makePathResolver = (homeDir, workingDir) => { | ||
| if (typeof homeDir !== 'function') throw new TypeError('"homeDir" must be a function.'); | ||
| if (typeof workingDir !== 'function') throw new TypeError('"workingDir" must be a function.'); | ||
|
|
||
| /** | ||
| * omni: A parser that normalizes a path and converts relative paths to absolute paths, | ||
| * based on the inputted values. | ||
| * @param {string} location An absolute or relative path. | ||
| * @returns {string} An normalized absolute path. | ||
| */ | ||
| return location => { | ||
| if (typeof location !== 'string') throw new TypeError('"location" must be a string.'); | ||
| const normalizedPath = path.normalize(location); | ||
|
|
||
| if (location.startsWith('~')) return path.join(homeDir(), normalizedPath.slice(1)); | ||
| if (path.isAbsolute(location)) return normalizedPath; | ||
| return path.join(workingDir(), normalizedPath); | ||
coderabbitai[bot] marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| }; | ||
| }; | ||
|
|
||
| /** | ||
| * omni: A parser that normalizes a path and converts relative paths to absolute paths, | ||
| * based on the current working directory. | ||
| * @param {string} location An absolute or relative path. | ||
| * @returns {string} An normalized absolute path. | ||
| */ | ||
| const resolvePath = makePathResolver(os.homedir, process.cwd); | ||
|
|
||
| module.exports = { | ||
| makePathResolver, | ||
| resolvePath | ||
| }; | ||
Uh oh!
There was an error while loading. Please reload this page.