forked from tools/josh
Christen "josh" and add version number
Oh and make persistence a bit more manageable.main
parent
f5713fdf31
commit
11523b5046
21
Gruntfile.js
21
Gruntfile.js
|
@ -17,6 +17,18 @@ module.exports = grunt => {
|
|||
files: [{expand: true, cwd: "src/main/", src: "**/*.css", dest: "build/"}]
|
||||
}
|
||||
},
|
||||
replace: {
|
||||
default: {
|
||||
src: "./build/bundle.js",
|
||||
replacements: [
|
||||
{
|
||||
from: "%%VERSION_NUMBER%%",
|
||||
to: "<%= pkg.version %>"
|
||||
}
|
||||
],
|
||||
overwrite: true
|
||||
}
|
||||
},
|
||||
webpack: {
|
||||
options: {
|
||||
entry: "./src/main/js/Main.ts",
|
||||
|
@ -49,6 +61,7 @@ module.exports = grunt => {
|
|||
|
||||
grunt.loadNpmTasks("grunt-contrib-clean");
|
||||
grunt.loadNpmTasks("grunt-contrib-copy");
|
||||
grunt.loadNpmTasks("grunt-text-replace");
|
||||
grunt.loadNpmTasks("grunt-webpack");
|
||||
|
||||
grunt.registerTask("dev", [
|
||||
|
@ -59,7 +72,9 @@ module.exports = grunt => {
|
|||
"copy:html",
|
||||
"copy:css",
|
||||
// Compile
|
||||
"webpack:dev"
|
||||
"webpack:dev",
|
||||
// Post
|
||||
"replace"
|
||||
]);
|
||||
grunt.registerTask("deploy", [
|
||||
// Pre
|
||||
|
@ -69,7 +84,9 @@ module.exports = grunt => {
|
|||
"copy:html",
|
||||
"copy:css",
|
||||
// Compile JS
|
||||
"webpack:deploy"
|
||||
"webpack:deploy",
|
||||
// Post
|
||||
"replace"
|
||||
]);
|
||||
|
||||
grunt.registerTask("default", ["dev"]);
|
||||
|
|
|
@ -1055,6 +1055,14 @@
|
|||
"semver": "^5.5.0",
|
||||
"shebang-command": "^1.2.0",
|
||||
"which": "^1.2.9"
|
||||
},
|
||||
"dependencies": {
|
||||
"semver": {
|
||||
"version": "5.7.1",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
|
||||
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"crypto-browserify": {
|
||||
|
@ -1761,7 +1769,8 @@
|
|||
"ansi-regex": {
|
||||
"version": "2.1.1",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"aproba": {
|
||||
"version": "1.2.0",
|
||||
|
@ -1782,12 +1791,14 @@
|
|||
"balanced-match": {
|
||||
"version": "1.0.0",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"brace-expansion": {
|
||||
"version": "1.1.11",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"balanced-match": "^1.0.0",
|
||||
"concat-map": "0.0.1"
|
||||
|
@ -1802,17 +1813,20 @@
|
|||
"code-point-at": {
|
||||
"version": "1.1.0",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"concat-map": {
|
||||
"version": "0.0.1",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"console-control-strings": {
|
||||
"version": "1.1.0",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"core-util-is": {
|
||||
"version": "1.0.2",
|
||||
|
@ -1929,7 +1943,8 @@
|
|||
"inherits": {
|
||||
"version": "2.0.3",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"ini": {
|
||||
"version": "1.3.5",
|
||||
|
@ -1941,6 +1956,7 @@
|
|||
"version": "1.0.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"number-is-nan": "^1.0.0"
|
||||
}
|
||||
|
@ -1955,6 +1971,7 @@
|
|||
"version": "3.0.4",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
}
|
||||
|
@ -1962,12 +1979,14 @@
|
|||
"minimist": {
|
||||
"version": "0.0.8",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"minipass": {
|
||||
"version": "2.3.5",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"safe-buffer": "^5.1.2",
|
||||
"yallist": "^3.0.0"
|
||||
|
@ -1986,6 +2005,7 @@
|
|||
"version": "0.5.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"minimist": "0.0.8"
|
||||
}
|
||||
|
@ -2066,7 +2086,8 @@
|
|||
"number-is-nan": {
|
||||
"version": "1.0.1",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"object-assign": {
|
||||
"version": "4.1.1",
|
||||
|
@ -2078,6 +2099,7 @@
|
|||
"version": "1.4.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"wrappy": "1"
|
||||
}
|
||||
|
@ -2163,7 +2185,8 @@
|
|||
"safe-buffer": {
|
||||
"version": "5.1.2",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"safer-buffer": {
|
||||
"version": "2.1.2",
|
||||
|
@ -2199,6 +2222,7 @@
|
|||
"version": "1.0.2",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"code-point-at": "^1.0.0",
|
||||
"is-fullwidth-code-point": "^1.0.0",
|
||||
|
@ -2218,6 +2242,7 @@
|
|||
"version": "3.0.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"ansi-regex": "^2.0.0"
|
||||
}
|
||||
|
@ -2261,12 +2286,14 @@
|
|||
"wrappy": {
|
||||
"version": "1.0.2",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"yallist": {
|
||||
"version": "3.0.3",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -2546,6 +2573,12 @@
|
|||
"which": "~1.3.0"
|
||||
}
|
||||
},
|
||||
"grunt-text-replace": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/grunt-text-replace/-/grunt-text-replace-0.4.0.tgz",
|
||||
"integrity": "sha1-252c5Z4v5J2id+nbwZXD4Rz7FsI=",
|
||||
"dev": true
|
||||
},
|
||||
"grunt-webpack": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/grunt-webpack/-/grunt-webpack-3.1.3.tgz",
|
||||
|
@ -3197,6 +3230,12 @@
|
|||
"resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz",
|
||||
"integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==",
|
||||
"dev": true
|
||||
},
|
||||
"semver": {
|
||||
"version": "5.7.1",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
|
||||
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -3611,6 +3650,14 @@
|
|||
"requires": {
|
||||
"object.getownpropertydescriptors": "^2.0.3",
|
||||
"semver": "^5.7.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"semver": {
|
||||
"version": "5.7.1",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
|
||||
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node-libs-browser": {
|
||||
|
@ -3671,6 +3718,14 @@
|
|||
"resolve": "^1.10.0",
|
||||
"semver": "2 || 3 || 4 || 5",
|
||||
"validate-npm-package-license": "^3.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"semver": {
|
||||
"version": "5.7.1",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
|
||||
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"normalize-path": {
|
||||
|
@ -4440,12 +4495,6 @@
|
|||
"ajv-keywords": "^3.1.0"
|
||||
}
|
||||
},
|
||||
"semver": {
|
||||
"version": "5.7.1",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
|
||||
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
|
||||
"dev": true
|
||||
},
|
||||
"serialize-javascript": {
|
||||
"version": "1.9.1",
|
||||
"resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-1.9.1.tgz",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "fwdekker.com",
|
||||
"version": "1.0.0",
|
||||
"version": "1.6.4",
|
||||
"description": "The source code of [my personal website](https://fwdekker.com/).",
|
||||
"author": "Felix W. Dekker",
|
||||
"repository": {
|
||||
|
@ -26,6 +26,7 @@
|
|||
"grunt-cli": "^1.3.2",
|
||||
"grunt-contrib-clean": "^2.0.0",
|
||||
"grunt-contrib-copy": "^1.0.0",
|
||||
"grunt-text-replace": "^0.4.0",
|
||||
"grunt-webpack": "^3.1.3",
|
||||
"mocha": "^6.2.2",
|
||||
"ts-loader": "^6.2.1",
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import * as Cookies from "js-cookie";
|
||||
import "./Extensions"
|
||||
import {Environment} from "./Environment";
|
||||
import {Directory, File, FileSystem, Path} from "./FileSystem"
|
||||
import {Persistence} from "./Persistence";
|
||||
import {IllegalArgumentError, IllegalStateError} from "./Shared";
|
||||
import {InputArgs} from "./Shell";
|
||||
import {EscapeCharacters} from "./Terminal";
|
||||
|
@ -229,8 +229,7 @@ export class Commands {
|
|||
*/
|
||||
execute(input: InputArgs): string {
|
||||
if (input.command === "factory-reset") {
|
||||
Cookies.remove("files");
|
||||
Cookies.remove("env");
|
||||
Persistence.reset();
|
||||
location.reload();
|
||||
throw new Error("Goodbye");
|
||||
}
|
||||
|
@ -369,7 +368,7 @@ export class Commands {
|
|||
<b>List of commands</b>
|
||||
${commandEntries.join("\n")}
|
||||
|
||||
Write "help [COMMAND]" or click a command in the list above for more information on a command.\\\
|
||||
Write "help [COMMAND]" or click a command in the list above for more information.\\\
|
||||
`.trimMultiLines();
|
||||
}
|
||||
}
|
||||
|
@ -476,12 +475,9 @@ export class Commands {
|
|||
if (userName === "")
|
||||
throw new IllegalStateError("Cannot execute `poweroff` while not logged in.");
|
||||
|
||||
Cookies.set("poweroff", "true", {
|
||||
"expires": new Date(new Date().setSeconds(new Date().getSeconds() + 30)),
|
||||
"path": "/"
|
||||
});
|
||||
|
||||
Persistence.setPoweroff(true);
|
||||
setTimeout(() => location.reload(), 2000);
|
||||
|
||||
return `Shutdown NOW!
|
||||
|
||||
*** FINAL System shutdown message from ${userName}@fwdekker.com ***
|
||||
|
|
|
@ -29,5 +29,5 @@ addOnLoad(() => {
|
|||
|
||||
// @ts-ignore: Ugly hack to execute it anyway
|
||||
if (window.terminal.shell.environment.get("user") !== "")
|
||||
window.terminal.processInput("ls");
|
||||
window.execute("ls");
|
||||
});
|
||||
|
|
|
@ -0,0 +1,115 @@
|
|||
import * as Cookies from "js-cookie";
|
||||
import {Environment} from "./Environment";
|
||||
import {Directory, FileSystem, Node, Path} from "./FileSystem";
|
||||
import {UserList} from "./UserList";
|
||||
|
||||
|
||||
/**
|
||||
* Manages persistence of state.
|
||||
*/
|
||||
export class Persistence {
|
||||
/**
|
||||
* Deserializes a file system from persistent storage, or returns the default file system if the deserialization
|
||||
* failed.
|
||||
*/
|
||||
static getFileSystem(): FileSystem {
|
||||
const filesString = Cookies.get("files");
|
||||
|
||||
let files: Directory | undefined = undefined;
|
||||
if (filesString !== undefined) {
|
||||
try {
|
||||
const parsedFiles = Node.deserialize(filesString);
|
||||
if (parsedFiles instanceof Directory)
|
||||
files = parsedFiles;
|
||||
else
|
||||
console.warn("`files` cookie contains non-directory.");
|
||||
} catch (error) {
|
||||
console.warn("Failed to deserialize `files` cookie.", error);
|
||||
}
|
||||
}
|
||||
|
||||
return new FileSystem(files);
|
||||
}
|
||||
|
||||
/**
|
||||
* Persists the given file system.
|
||||
*
|
||||
* @param fileSystem the file system to persist
|
||||
*/
|
||||
static setFileSystem(fileSystem: FileSystem) {
|
||||
Cookies.set("files", fileSystem.root, {
|
||||
"expires": new Date(new Date().setFullYear(new Date().getFullYear() + 25)),
|
||||
"path": "/"
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Deserializes an environment from persistent storage, or returns the default environment if the deserialization
|
||||
* failed.
|
||||
*
|
||||
* @param fileSystem the file system used to validate the `cwd` environment variable
|
||||
* @param userList the list of users used to validate the `user` environment variable
|
||||
*/
|
||||
static getEnvironment(fileSystem: FileSystem, userList: UserList): Environment {
|
||||
const environmentString = Cookies.get("env") ?? "{}";
|
||||
|
||||
let environment: Environment;
|
||||
try {
|
||||
environment = new Environment(["cwd", "home", "user"], JSON.parse(environmentString));
|
||||
} catch (error) {
|
||||
console.warn("Failed to set environment from cookie.");
|
||||
environment = new Environment(["cwd", "home", "user"]);
|
||||
}
|
||||
|
||||
// Check user in environment
|
||||
if (!environment.has("user")) {
|
||||
environment.set("user", "felix");
|
||||
} else if (environment.get("user") !== "" && !userList.has(environment.get("user"))) {
|
||||
console.warn(`Invalid user '${environment.get("user")}' in environment.`);
|
||||
environment.set("user", "felix");
|
||||
}
|
||||
|
||||
// Set home directory
|
||||
environment.set("home", userList.get(environment.get("user"))?.home ?? "/");
|
||||
|
||||
// Check cwd in environment
|
||||
if (!environment.has("cwd")) {
|
||||
environment.set("cwd", environment.get("home"));
|
||||
} else if (!fileSystem.has(new Path(environment.get("cwd")))) {
|
||||
console.warn(`Invalid cwd '${environment.get("cwd")}' in environment.`);
|
||||
environment.set("cwd", environment.get("home"));
|
||||
}
|
||||
|
||||
return environment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Persists the given environment.
|
||||
*
|
||||
* @param environment the environment to persist
|
||||
*/
|
||||
static setEnvironment(environment: Environment) {
|
||||
Cookies.set("env", environment.variables, {"path": "/"});
|
||||
}
|
||||
|
||||
/**
|
||||
* Persists the "power off" setting.
|
||||
*
|
||||
* @param value the value to persist for the "power off" setting
|
||||
*/
|
||||
static setPoweroff(value: boolean) {
|
||||
Cookies.set("poweroff", `${value}`, {
|
||||
"expires": new Date(new Date().setSeconds(new Date().getSeconds() + 30)),
|
||||
"path": "/"
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all persistent storage.
|
||||
*/
|
||||
static reset() {
|
||||
Cookies.remove("files");
|
||||
Cookies.remove("env");
|
||||
Cookies.remove("poweroff");
|
||||
}
|
||||
}
|
|
@ -1,8 +1,8 @@
|
|||
import * as Cookies from "js-cookie";
|
||||
import {Commands} from "./Commands";
|
||||
import {Environment} from "./Environment";
|
||||
import {Directory, File, FileSystem, Node, Path} from "./FileSystem";
|
||||
import {Directory, File, FileSystem, Path} from "./FileSystem";
|
||||
import {InputParser} from "./InputParser";
|
||||
import {Persistence} from "./Persistence";
|
||||
import {asciiHeaderHtml, IllegalStateError, stripHtmlTags} from "./Shared";
|
||||
import {EscapeCharacters, InputHistory} from "./Terminal";
|
||||
import {UserList} from "./UserList";
|
||||
|
@ -48,8 +48,8 @@ export class Shell {
|
|||
constructor(inputHistory: InputHistory) {
|
||||
this.inputHistory = inputHistory;
|
||||
this.userList = new UserList();
|
||||
this.fileSystem = Shell.loadFileSystem();
|
||||
this.environment = Shell.loadEnvironment(this.fileSystem, this.userList);
|
||||
this.fileSystem = Persistence.getFileSystem();
|
||||
this.environment = Persistence.getEnvironment(this.fileSystem, this.userList);
|
||||
this.commands = new Commands(this.environment, this.userList, this.fileSystem);
|
||||
|
||||
this.saveState();
|
||||
|
@ -71,6 +71,7 @@ export class Shell {
|
|||
</span>
|
||||
Type "<a href="#" onclick="execute('help');">help</a>" for help.
|
||||
|
||||
Welcome to josh v%%VERSION_NUMBER%%, the javascript online shell.
|
||||
`.trimLines();
|
||||
}
|
||||
|
||||
|
@ -205,74 +206,13 @@ export class Shell {
|
|||
|
||||
|
||||
/**
|
||||
* Saves the shell's state in cookies.
|
||||
* Persists the shell's state.
|
||||
*
|
||||
* @see Persistence
|
||||
*/
|
||||
private saveState() {
|
||||
Cookies.set("files", this.fileSystem.root, {
|
||||
"expires": new Date(new Date().setFullYear(new Date().getFullYear() + 25)),
|
||||
"path": "/"
|
||||
});
|
||||
Cookies.set("env", this.environment.variables, {"path": "/"});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the file system loaded from a cookie, or the default file system if no cookie is present or the cookie
|
||||
* is invalid.
|
||||
*/
|
||||
private static loadFileSystem(): FileSystem {
|
||||
let files: Directory | undefined = undefined;
|
||||
const filesString = Cookies.get("files");
|
||||
if (filesString !== undefined) {
|
||||
try {
|
||||
const parsedFiles = Node.deserialize(filesString);
|
||||
if (parsedFiles instanceof Directory)
|
||||
files = parsedFiles;
|
||||
else
|
||||
console.warn("`files` cookie contains non-directory.");
|
||||
} catch (error) {
|
||||
console.warn("Failed to deserialize `files` cookie.", error);
|
||||
}
|
||||
}
|
||||
return new FileSystem(files);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the environment loaded from a cookie, or the default environment if no cookie is present or the cookie
|
||||
* is invalid.
|
||||
*
|
||||
* @param fileSystem the file system used to validate the `cwd` environment variable
|
||||
* @param userList the list of users used to validate the `user` environment variable
|
||||
*/
|
||||
private static loadEnvironment(fileSystem: FileSystem, userList: UserList): Environment {
|
||||
const environmentString = Cookies.get("env") ?? "{}";
|
||||
let environment: Environment;
|
||||
try {
|
||||
environment = new Environment(["cwd", "home", "user"], JSON.parse(environmentString));
|
||||
} catch (error) {
|
||||
console.warn("Failed to set environment from cookie.");
|
||||
environment = new Environment(["cwd", "home", "user"]);
|
||||
}
|
||||
|
||||
// Check user in environment
|
||||
if (!environment.has("user")) {
|
||||
environment.set("user", "felix");
|
||||
} else if (environment.get("user") !== "" && !userList.has(environment.get("user"))) {
|
||||
console.warn(`Invalid user '${environment.get("user")}' in environment.`);
|
||||
environment.set("user", "felix");
|
||||
}
|
||||
|
||||
// Set home directory
|
||||
environment.set("home", userList.get(environment.get("user"))?.home ?? "/");
|
||||
|
||||
// Check cwd in environment
|
||||
if (!environment.has("cwd")) {
|
||||
environment.set("cwd", environment.get("home"));
|
||||
} else if (!fileSystem.has(new Path(environment.get("cwd")))) {
|
||||
console.warn(`Invalid cwd '${environment.get("cwd")}' in environment.`);
|
||||
environment.set("cwd", environment.get("home"));
|
||||
}
|
||||
|
||||
return environment;
|
||||
Persistence.setFileSystem(this.fileSystem);
|
||||
Persistence.setEnvironment(this.environment);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue