A Javascript ""Operating System""
Toast OS is split into two parts: the internal stuff, and the userland. Let's start with the internal stuff, as it was some of the first things built, and all the other stuff is built on top of it.
Kernel / Entrypoint
First things first, the Kernel: Toast OS's Kernel is the entrypoint for the whole project. The browser constructs the kernel with a new Kernel(), and then feeds it keyboard events. In turn, the Kernel provides the browser with a "Term" object, which describes all the stuff that's visible on-screen.
In addition, the Kernel is also responsible for spawning processes, and for maintaining all the Process (and Exit) Tables. A Process in Toast OS is just a function with an id. The function takes 2 args: (os: OSContext, args: string) and returns Promise<number>. Each Process launched by the Kernel gets its own Process id (PID) and gets added to internal Process and Return tables. Those tables store a number of things: command names, environment variables, exit-code promises, input / output Pipes, etc.
Wait, Pipes? What the heck are those? Funny you should ask...
Pipes
The Pipe class provides a mechanism to simulate true unix pipes - as-in, they represent a place where chunks of data can be pushed into and then pulled out of. Importantly, these Pipes contain no buffer, so any write will need an accompanying read to pull the data through. Any write without a reader, or a reader without a write will wait. Additionally, pipes notify the writer about whether the write was successful. Pipes can also be "piped" between each other, to automate the process of pushing data downstream, through a sequence of Pipes. These pipes are used to simulate all kinds of things: the default process streams (STDIN, STDOUT, and STDERR), file reads and writes, FIFO files, shell piping, etc.
OSContext / System Calls
Now, any running Process will need ways to make API calls into the Kernel. This unlocks the ability to do things like read / write to files, send signals to other processes, and all that kind of stuff. In a real OS, this API is sometimes called the OS's "syscalls", but in Toast OS, we have the OSContext object.
The OSContext is the first argument passed to a Process function, and describes the program's interface into the Kernel. It has a long list of async functions, which perform various tasks within the system. All tasks, no matter how trivial, are async. This will 1: create more yield points, improving multi-tasking, and 2: Gives us the ability to look into a list of cached errors for the process, and reject the return promise with one of those instead. (This is how signals are actually implemented, since we can't preempt a running function...)
The APIs have a wide range of complexities. These range from getPID(), which simply resolves the current process ID, (which is a single property lookup) to more complex tasks like fsStatPath(inode), which will walk throughout the filesystem, obeying things like relative / absolute paths, following symlinks, etc, and produce a complex stat object, describing a File.
Oh, and speaking of the filesystem...
The Filesystem
Filesystem is a JS class that provides a series of internal APIs around giant internal hierarchical map of inodes (file ids) to File objects. These APIs handle things like File creation, moving, deleting, resolving paths, etc.
But what are those Files? Well, they can be normal files with data, or directories with other files as descendants, or symlinks where data is a path, or named pipes / FIFOs where data is a Pipe object. But in Toast OS specifically, File is just a typescript interface that describes all of the high-level functionality that each File needs to support. The actual implementation of those Files can vary!
Right now, there are two classes that implement File: RealFile, and FancyFile.
RealFiles are Files as you might expect them - they store data, they have permissions, they can be connected into hierarchies, etc. They attempt to faithfully simulate the actions that happen to files within the system. Most Files within the system, and all Files created by the user at runtime are all RealFiles, behind-the-scenes. They store data, can be moved, can be chmod-ed, etc.FancyFiles are the opposite. They still implement the File interface, but most methods will throw Permission Denied errors, or only implement the BARE minimum functionality. That isn't too useful on its own, but there are many other classes that extend FancyFile to create Files that have special behavior. This gives us the ability to implement things such as device drivers (/dev/null, /dev/lp0, ...) as well as the/procdirectory.
TTY + Term
The TTY object is created during system startup, and manages all input/output for the system. New processes are automatically attached to it, and it will pipe process stdout / stderr pipes to the main system output pipe. In addition, it keeps track of processes, and specifically, which process is in the "foreground". This process will get keyboard events piped into its stdin.
That main system output pipe is then fed into "Term", which is the terminal emulator. The Term class reads the input, byte-by-byte, and will simulate those bytes as ANSI commands on a 24x80 display. It supports a significant number of ANSI commands, and especially SGR (Select Graphics Rendition) commands, which allow the output to be styled.
Conclusion
Those are the basic system components. Now on to the user-land...
