AI Generated Video Summary
Deano comes with batteries included. We got a lot of tooling that allows you to get up and running in seconds. You got Formatter, TestRunner, Linter. You can even create a self-contained binary, that is you'll package all of the source code you provide and then give you a single executable file. It's kind of similar to the PKG MPM package you can use. Deano is also very heavily invested in web APIs. We've got a lot of them, most notably Fetch, TextEncoder, TextDecoder, Blob. We got support for WebStreams API as well as WebWorker API. There's many, many more of these APIs and I strongly suggest you visit the MDM page for the API you're interested in and see if Deano supports the API.
2. Deano in Rust: Building Custom Runtimes
Deano in Rust was a significant decision for the project, as it allowed for quick development and leveraged a vast ecosystem of high-quality crates. Building Deano is easy with Cargo integration, and pre-built binary archives save time. Rust's crate system enables modular code organization, and Deano offers two runtimes: CLI for local development and Deano deploy for cloud deployment. Users can mix and match crates and APIs to create custom runtimes. The run.js example runtime already supports ES modules, transpilation, file I/O, console APIs, and a basic fetch API.
So Deano was written in Rust, which is a low level language, or system's language that doesn't have a garbage collector. And I can say that this is probably the most influential decision ever made for the Deano project. Rust allows us to move very quickly and it allows us to leverage a huge ecosystem of the crates available. Most of the crates are very high quality, for example, an http crate that implements the http protocol is very well tested and it has a great coverage, so we're very confident using it that we will be adhering to the defined protocol.
3. Implementing a setTimeout API in runJS
And then we do await run.js.fetches. So we're also using top level await here. And sorry for the squeaky mice, but it seems my VS code setup doesn't understand it here.
But on the other hand, you will use much less resources, which leads us to the second point that you might be resource constrained. So Dno ships with batteries included. We got a lot of tooling in the CLI, but sometimes that binary might be too heavy for you, or you might not need every API that we provide. And we provide many of them. You can interact with file system. You can do networking. There's a huge implementation of various web APIs. But if you don't need them, maybe you can just use a subset of them, resulting in a smaller and slimmer binary. Or lastly, maybe your project could benefit from essentially shipping a self-contained single binary. You can already do this with Dino, but sometimes you maybe want to customize it a bit more or again, remove some of the fat that is not needed in your particular use case. Right here, I linked you to a project of mine, a site project, where I tried to wrap ESLint into a binary file that uses the same technologies we're going to be using in this example.
Okay, so let's actually do some coding. Let's do something a bit exciting. I think we may try to implement a setTimeout API and runJS. Of course, this will not be the same setTimeout API you can see in browsers or in Dino or in Node.js, because while this API is really easy to grasp and use, there are a lot of intricacies to it. What I want to do is essentially implement setTimeout in a way that will take a delay in milliseconds. It will take a callback and then just fire this callback.
4. Implementing setTimeout in Runtime.js
But we will skip validation, ensuring that everything is property-adhering to WebIDL and so forth and so on. All right. So, let's jump right into the code. I'm going back to my VS Code.
I'm going to drop into the Runtime.js file, which is a file in which we implement all the APIs available in our Run.js example. So, as you can see here, we are assigning to globaldisk.console a console object, which oops, I don't want to see the typing, I want to see the actual object. Our console object has log and error function, and then we're assigning globaldisk.runjs with run.js, which contains the four APIs we already talked about. So, let's add another one. This time it's going to be globaldisk.settimeout. And our settimeout needs to accept two parameters. The first one is the callback, and the second one is the delay in milliseconds. So, how are we going to do that? Well, copilot is jumping the gun here, but that's not exactly what we'll do. We need to call into Rust to do something that will wait for the given delay, and only when we've finished waiting we will fire out the callback. So, let's start by adding, by calling car of async, and we'll call it offsetTimeout. Then we'll do delay as a first argument. That seems reasonable. And then we want to fire the callback.
5. Fixing the Undefined Function Error
So, if we just try to run it now, we get an error saying that ops name is not a function. Let's fix it by defining the function in the main.rs file, which implements our runtime.
6. Implementing the Opposite Timeout Function
We define the op attribute and implement the opposite timeout function using fast grades and Tokyo. We create a duration from the given delay in milliseconds and asynchronously wait for it. After registering the function with our extension, we successfully wait for a second and print to the console.
Let's just use one of them as an example to provide our opposite timeout. I'm going to start with defining the op attribute. The op attribute is coming from Deno. So, this is something we provided for you. Our opposite timeout will return a result. This will be just identity, and then return any error, but we don't really need this error here, but let's just keep it. So, the return value will be okay, and now let's actually do the fun part.
We need to somehow sleep for given delay, which I haven't passed here yet. So, we get delay, which will be U64, and now I'm going to leverage the fantastic ecosystem of fast grades to actually asynchronously wait for the given delay. So, I'm going to use Tokyo for that, which we already used in another API. So, I'm going to use Tokyo, time, delay, sleep, and now I need to pass a duration. Duration comes from STD time duration, and now I need to create it from milliseconds. Sure. Whoops, seems like my IntelliSense is acting up. So, we're going to sleep for a duration taken from the delay variable defined in milliseconds, and then we're just going to await it. So, will this actually be enough? Oh, sure. We don't need to use a question mark here.
7. Creating a Basic SET TIMEOUT Implementation
And to be honest, the possibilities of what you create with this infrastructure and writing out more on your own is just endless. You're limited by your own imagination here. We're going to keep doing these blog posts and recordings for how you can roll your own runtime with more and more features in the future, so please stay tuned for the next part. And that's it from me. I'm sorry that it was so short and I had to speed run through it, but I hope you found it interesting. All of the source code, all the nine lines that we did here will be available at this repository, so please drop by and visit it. And again, if you have any questions, I'll be more than happy to answer them. Shoot me an email or send me a DM over Twitter, and make sure to visit our website. Thank you for having me here, and I hope you enjoyed this talk.