The code and data behind xeiaso.net
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Xeact talk

Signed-off-by: Xe Iaso <me@xeiaso.net>

Xe Iaso 95d775d4 b2d1a2eb

+964
+1
.gitignore
··· 8 8 /target 9 9 .patreon.json 10 10 .direnv 11 + node_modules
+3
flake.nix
··· 192 192 zig 193 193 nodejs 194 194 195 + jq 196 + jo 197 + 195 198 # tools 196 199 ispell 197 200 pandoc
+663
lume/src/talks/2024/xeact.mdx
··· 1 + --- 2 + title: "Xeact: The femtoframework you've been waiting for" 3 + date: 2024-01-25 4 + tags: 5 + - js 6 + - react 7 + series: xeact 8 + image: talks/2024/xeact/001 9 + --- 10 + 11 + ## Warning to readers 12 + 13 + This talk is about [a shitpost](https://github.com/Xe/Xeact) that got way out of hand. Out of respect for the material, this talk is also a shitpost and a work of satire. In order to help make this talk teach you things, I have included technical deep dives into aspects of how and why Xeact works. I hope you enjoy it and you can learn...something useful. 14 + 15 + Please don't repeat any of the absurd acts mentioned here in production. I am not responsible for any damage caused by this talk. You are the one copying and pasting my code samples into production. 16 + 17 + Otherwise, kick back, grab a beverage, and enjoy the ride. 18 + 19 + ## Video 20 + 21 + <XeblogVideo path="talks/2024/xeact" /> 22 + 23 + In case the video from XeDN doesn't load, try the [YouTube 24 + version](https://youtu.be/povkpv-mRKw). Please [let me 25 + know](/contact/) with the contents of 26 + [`cdn.xeiaso.net/cgi-cdn/wtf`](https://cdn.xeiaso.net/cgi-cdn/wtf) so 27 + I can diagnose the problem. Hopefully it's not boltdb acting up again. 28 + 29 + ## Transcript 30 + 31 + <XeblogSlide name="2024/xeact/001" essential /> 32 + 33 + Hi, I'm Xe. I'm the Philosopher-in-Chief at Techaro. You might know me 34 + from my blog or other hit talks I've given like ["AI, the not-so-good 35 + parts"](/talks/2024/prepare-unforeseen-consequences/), or ["Reaching the Unix philosophy's logical conclusion with WebAssembly"](/talks/unix-philosophy-logical-extreme-wasm/), but today 36 + I'm not going to talk about any of my employers. 37 + 38 + Today I'm talking about Xeact. It's the femtoframework that your 39 + editor has been waiting for. As I mentioned, Xeact is a 40 + femtoframework. It's designed to help you make your websites load 41 + faster, work with the script tag, and overall helps you live your life 42 + with love and light. It is the perfect basis for your projects and 43 + should scale infinitely. 44 + 45 + Asterisk. 46 + 47 + <XeblogSlide name="2024/xeact/003" essential /> 48 + 49 + React is a framework. It gives you all the goodness of working on the 50 + front end, but sometimes it's a bit too big for what you need. So you 51 + reach for Preact. Preact has a lot of the benefits of React, but it's 52 + smaller, which makes it more adaptable and easier to understand. Xeact 53 + is even smaller. It's a femtoframework, which takes ideas from both 54 + React and Preact to get you the smallest possible bundle that can do 55 + what you need. 56 + 57 + Today I'm going to cover the foundations of Xeact, how it works, what 58 + I learned in the process, and then we're going to look into the future 59 + with the help of Techaro. 60 + 61 + So our industry is based heavily on design. Design is the heart of our 62 + products and it's what our users crave. When I started working on 63 + Xeact, I just started working right on the design first and foremost. 64 + Here are the core principles that I used for designing Xeact. 65 + 66 + I wanted the source code to be understandable because let's face it, 67 + when you're debugging things, knowing what's going on means you know 68 + what's going on. Computer programming is complicated. There's a lot of 69 + indirection and access layers and everything that just makes it 70 + difficult to understand. I want the source code of Xeact to fit inside 71 + your mental model so that you don't have to think about it. It's 72 + beautiful. 73 + 74 + Zero compile steps. Two steps is too many. If you have more than a 75 + script tag, you're doing it wrong. That's my hope at least. Less time 76 + spending deploying your product, more time spending disrupting the 77 + burrito delivery industry or whatever. 78 + 79 + I want the source code to fit on a t-shirt because open source 80 + maintainer burnout is real and one of the main reasons why it happens 81 + is because people don't get paid adequately for their work. This way, 82 + by having the source code available on the t-shirt, you open up 83 + merchandising possibilities that help people make up for the lost 84 + income spent working on open source. 85 + 86 + And the entire thing GZipped should fit inside 500 bytes. Egress 87 + bandwidth? Expensive. One gigabyte through managed NAT gateway? Seven 88 + U.S. pennies. That adds up. The less amount of money you spend per 89 + user, the more money you make per user. Xeact saves you time, grief, 90 + your money, and saves your company from bankruptcy. 91 + 92 + So, in the beginning, it started out with a function like this. 93 + 94 + ```javascript 95 + const mkNode = (name, data = {}, children = []) => { 96 + let result = Object.assign(document.createElement(name), data); 97 + result.append(...children); 98 + return result; 99 + }; 100 + ``` 101 + 102 + In the beginning, the make node function was created. This has made a 103 + lot of people very happy and has been widely regarded as a good move. 104 + In this young state, this function is small, nimble, adaptable, and 105 + with it came syntax that only a Haskeller could love. But that's okay 106 + because I love Haskell. 107 + 108 + ```javascript 109 + const blockQuote = (text) => 110 + mkNode( 111 + "blockquote", 112 + { 113 + class: "p-4 border-l-4 border-gray-400 bg-gray-100 text-lg font-semibold", 114 + }, 115 + [ 116 + mkNode( 117 + "p", 118 + { 119 + class: "text-lg font-semibold", 120 + }, 121 + [text] 122 + ), 123 + ] 124 + ); 125 + ``` 126 + 127 + HTML is just a tree, right? Why should our code hide this? You can 128 + kind of see it, right? That kind of resembles how a tree of elements 129 + is. And if you're in the back and you can't see it, I just want you to 130 + imagine it because it's about as perfect as you think. 131 + 132 + With all this in mind, I had to rename this function, this make node 133 + function, because that's like, what, six characters and a shift key? 134 + You're going to be typing that all the time. It needs to be short, it 135 + needs to be rememberable, and most importantly, 136 + 137 + It needs to be representative of everything because this is 138 + effectively the one function that gives you anything. It's the 139 + universal source of meaning and meaninglessness in your program. 140 + 141 + <XeblogSlide name="2024/xeact/017" essential /> 142 + 143 + With all this in mind, I had one idea. I looked back into my life, I 144 + thought about everything, and one name stood out. This letter came to 145 + me in a divine vision with eight fantastic sides and eight awesome 146 + angles. This letter is the letter H, and with it came a divine vision 147 + that I will repeat now. 148 + 149 + One day on the road to Nazareth, Jesus Christ met with a group of 150 + theologians. The theologians realized who he was and stood with him. 151 + Jesus looked upon them in confusion and said unto them, "Who do you 152 + say that I am?" 153 + 154 + All at once they replied, "You are the astrological manifestation of 155 + the ground of our being, the kerygma of which we find the ultimate 156 + meaning in our interpersonal relationships. You are the source of love 157 + and life in the world. The foundation, the savior, the alpha, the 158 + omega, the beginning, the end, all and nothing simultaneously, and we 159 + love you for it all." 160 + 161 + Upon hearing this, Jesus was taken back a bit. This was a bit much for 162 + him, and he was confused. After some time, he replied, "What the fuck 163 + are you talking about"? Panic sprung up among the theologians. They 164 + had just confused their savior. They had sullied the thoughts of their 165 + messiah. The silence deafened the field. 166 + 167 + After some time, one of the theologians managed to speak up. 168 + And they spoke. "h". 169 + 170 + Jesus was enlightened. 171 + 172 + ```javascript 173 + const blockQuote = (text) => 174 + h( 175 + "blockquote", 176 + { 177 + class: "p-4 border-l-4 border-gray-400 bg-gray-100 text-lg font-semibold", 178 + }, 179 + [ 180 + h( 181 + "p", 182 + { 183 + class: "text-lg font-semibold", 184 + }, 185 + [text] 186 + ), 187 + ] 188 + ); 189 + ``` 190 + 191 + This is the name of the function. Now let me paste that code from 192 + earlier, but with the proper name. It just takes the name of a tag, 193 + the list of attributes to apply, and the list of children. And that's 194 + it. That's the entire thing. That's the entire femto framework. 195 + Everything else is just a bunch of helpers to make things more 196 + convenient, like plain text nodes or removing all the children or 197 + grabbing specific things out of the tree or debouncing or onload 198 + handlers. But, you know, that's just standard JavaScript stuff that I 199 + just remade because you can't stop me. 200 + 201 + So, this made everything a lot more easy because I could just assemble 202 + the nodes in the way that I understood, which, because I have back end 203 + brain rot syndrome, I just need to do in functions. 204 + 205 + And this made Xeact a success. 206 + 207 + With this shitpost, I was finally able to understand how to make 208 + frontend UI stuff work. I finally understood how to get an HTTP fetch, 209 + parse the JSON, crap out a bunch of nodes, and then throw it on the 210 + page for people to understand what's going on. And then this basically 211 + unblocked everything else so that I could use everything else 212 + normally. 213 + 214 + However, in the process, there was one small problem. Semantic 215 + satiation. If H is supposed to mean everything and it's everywhere, it 216 + loses its meaning. This cannot stand. 217 + 218 + So I ended up creating a library to help with it and I made the 219 + femtomixin Xeact HTML. If you look over the code of a bunch of all the 220 + popular websites, you'll see that they use like 15 HTML tags. And even 221 + then, you can put them into three categories, like stuff that usually 222 + takes attributes and children, stuff that only takes attributes or 223 + stuff that stands bu itself, like the horizontal rule tag. 224 + 225 + ```javascript 226 + const blockQuote = (text) => 227 + blockquote( 228 + { 229 + class: "p-4 border-l-4 border-gray-400 bg-gray-100 text-lg font-semibold", 230 + }, 231 + [p(text)] 232 + ); 233 + ``` 234 + 235 + With this knowledge, you can break this down into a list of things, and then you 236 + can create functions for all of them. So here's that same block quote 237 + function, but with you know the function blockquote to create a 238 + blockquote. Wow, so easy! And this is where things really started 239 + making sense because it was a lot easier for me to look at this token 240 + soup and then imagine what it would look like. Press f5. It shows up. I 241 + feel unstoppable. 242 + 243 + It was just a continuous series of golden moments that made me a real 244 + full stack developer: kernel to front end. 245 + 246 + ```javascript 247 + console.log(`import { h, t } from "./xeact.js";`); 248 + console.log(`const $tl = (kind) => (text, attrs = {}, children = []) => { 249 + children.unshift(t(text)); 250 + return h(kind, attrs, children); 251 + };`); 252 + 253 + [ 254 + "h1", 255 + "h2", 256 + "h3", 257 + "h4", 258 + "h5", 259 + "h6", 260 + "p", 261 + "b", 262 + "i", 263 + "u", 264 + "dd", 265 + "dt", 266 + "del", 267 + "sub", 268 + "sup", 269 + "strong", 270 + "small", 271 + ].forEach((tag) => console.log(`export const ${tag} = $tl("${tag}");`)); 272 + ``` 273 + 274 + So in order to make this and not lose whatever shred of sanity I had left, I 275 + made a code generator. This is a JavaScript program that prints JavaScript 276 + code to standard output so that you can include it in your JavaScript code. 277 + It's kind of beautiful. Again, if you're in the back, you can't see it because I'm 278 + very bad at formatting code, just imagine like a bunch of things in a list and then 279 + some small forEach at the bottom with an arrow function to actually generate the 280 + code. It's a lot more beautiful in practice than it looks. 281 + 282 + At this point, I feel like I need to clarify something very important. I'll circle back to Xeact, but just trust me where I'm going with this. I have a plan. 283 + 284 + I'm not an Arch Linux user (by the way), but do use something else that continues to be a source of wisdom. 285 + 286 + I use Emacs rigged to act like Vim. One of the main things about 287 + Emacs that continues to give me inspiration is extensibility in its 288 + own little language called Emacs Lisp. 289 + 290 + If you've never used Lisp before, it is a very unique kind of violence 291 + where lists are the core data type of everything where everything is 292 + your code and your data. Yes, you can represent data and code in the 293 + same way. And this allows you to create new code on the fly with while 294 + making it impossible to make something that won't parse. 295 + 296 + ```lisp 297 + ELISP> (+ 3 4) 298 + 7 299 + ``` 300 + 301 + So, by default, when you make a list in Lisp, it's considered a code 302 + list. This is something that just immediately gets evaluated. Like, 303 + for example, adding the numbers 3 and 4 and getting 7. 304 + 305 + ```lisp 306 + ELISP> '(+ 3 4) 307 + (+ 3 4) 308 + ``` 309 + 310 + This is nice and all, but sometimes you need to make data. So they 311 + have this concept of quoting a list to get the data associated with 312 + it. This is so common that it's considered one of the core functions 313 + in Lisp. I think, like, McCarthy has a paper that's, like, 11 314 + functions that you can use to bootstrap the entire Turing complete 315 + world. It's quite a paper, but it is very old and has some 1970s-isms 316 + in it. 317 + 318 + But if you notice, you either get all code or all data. Sometimes you 319 + need to go halfsies. 320 + 321 + That's why they have this magic thing called quasi-quoting. When 322 + you're assembling complicated things, you will need to mix code and 323 + data together. Quasi-quoting effectively lets you mix the two. Things 324 + normally get quoted, as in you normally have data, but then you can 325 + use a comma to unquote or inject the code value into it. 326 + 327 + ```lisp 328 + ELISP> (let ((user-name "Xe") 329 + (user-email "xeact@xeserv.us")) 330 + (json-serialize 331 + `((user . ,user-name) 332 + (email . ,user-email)))) 333 + "{\"user\":\"Xe\",\"email\":\"xeact@xeserv.us\"}" 334 + ``` 335 + 336 + So for example, for the sake of argument, I have this associative 337 + list, which is probably known as a hash map in any sensible language. 338 + And I have some variables with let for the user name and user email. 339 + And I build an associative array and I get it back. And why would you 340 + use this? JSON. That's why you would use it. You mix literal names 341 + with variable code. 342 + 343 + So if quasi-quoting lets you mix code and data to tree-like 344 + structures, and we mostly deal with web applications that deal with 345 + elements in a tree, what else could we do with this? 346 + 347 + HTML. HTML is a tree and we often need to mix literal data and 348 + variable data. So what if we put this all, what if we add this all up 349 + and put it together? 350 + 351 + It turns out there's prior art here. I'm not the first person to come 352 + up with the idea of JSX, but JSX is a compiler for JavaScript that 353 + lets you mix HTML literal code and variable JavaScript data, and it 354 + will just transparently compile to all the right JavaScript things 355 + under the hood. It's really nice because it's impossible to create 356 + invalid HTML syntax trees. Well, normally impossible. Otherwise you 357 + found a bug in the JSX compiler, but that's a different story. 358 + 359 + ```javascript 360 + export const jsx = (tag, data) => { 361 + let children = data.children; 362 + delete data.children; 363 + const result = h(tag, data, children); 364 + result.classList.value = result.class; 365 + return result; 366 + }; 367 + ``` 368 + 369 + So I experimented and in the process, I decided to see what would 370 + happen if I broke that core tenant of zero compile steps. The 371 + efficiency gains that you do this are phenomenal because code and data 372 + mixed together is just such a lovely pattern that I thought was a 373 + waste of time until I started doing it. I was wrong initially because 374 + I thought the extra step would just be another layer of annoyance, but 375 + it worked out pretty great. 376 + 377 + And yeah, what is this? Didn't you just make a big show about not 378 + needing a compile step? 379 + 380 + I never said you need to use JSX with this. It turns out that my H 381 + function is depressingly similar to react createElement function. So 382 + with this five line monster, I'm able to adapt it into the create 383 + element into the JSX signature that it wants. And you know, you're off 384 + to the races. You, if you want, you activate the JSX runtime, you 385 + write your backwards PHP all you want, and you're good to go. Instant 386 + deployment to any cloud, including no cloud. And like I said, don't 387 + want it. Don't turn it on. Do you want it? Do turn it on. It's great. 388 + 389 + Also, if you're deploying with Nix, the idiomatic Xeact deployment 390 + process, by the way, you should be able to just have Nix handle it for 391 + you, and then you don't even have to think about thinking about it. 392 + It's really great. 393 + 394 + It was really easy to get a basic Xeact JSX runtime created because of 395 + that similar function signature, but there was one key thing that it 396 + missed. Components. 397 + 398 + However, components in React are just functions, asterisk. All you 399 + have to do is detect if the HTML tag name argument is a function and 400 + then call it. So I did just that. 401 + 402 + ```javascript 403 + const h = (name, data = {}, children = []) => { 404 + const result = 405 + typeof name == "function" 406 + ? name(data) 407 + : Object.assign(document.createElement(name), data); 408 + if (!Array.isArray(children)) { 409 + children = [children]; 410 + } 411 + result.append(...children); 412 + return result; 413 + }; 414 + ``` 415 + 416 + And this is the current `h` function in the open source repo. This 417 + allowed me to have, this allowed with everything else, let me have 418 + modern syntax, compiler handrails, and a minimal JavaScript workflow 419 + at the same time. And not to mention, I had compatibility with 420 + React... 421 + 422 + ...asterisk. 423 + 424 + At this point, I had a very nice situation going on. I'd reached 425 + version 0.69.71. It was all in a private repo, internal to work. And 426 + the world of front end that had eluded me for so long was finally 427 + within my grasp and I was able to understand what the JavaScript runes 428 + did and bend them to my chaotic-good will. 429 + 430 + In a moment, I knew where this place could be, this new power could be 431 + used for good. I saw a place where it could fit in, and I saw a 432 + feature gap and jumped to fill it. 433 + 434 + It was for an internal tool, as one does, called DAB, data about 435 + business. It was effectively a sidebar for the support UI, so the 436 + support people didn't have to open as many tabs when they were trying 437 + to debug what was going on. It was designed with the kind of UI that 438 + only a terminal hacker could love, yet it was ruthlessly effective 439 + enough that when I deployed it in testing, and then realized that it 440 + showed up for the support people by default, because who would think 441 + about having something that doesn't show up by default for support, 442 + they were like, this is amazing, this has already cut down our ticket 443 + response time in half, so I knew I was onto something, so I just kept 444 + going. 445 + 446 + It was a success thanks to Xeact. 447 + 448 + It was then that I realized that the world needs this power of Xeact 449 + for their single page applications. Sometimes you only need to view 450 + some stuff that requires poking an API, and Xeact fits that goal, 451 + hackery tools that display simple information. 452 + 453 + Xeact remains open source software to this day, where it is used by 454 + thousands of milli-developers. 455 + 456 + However, when I got to a certain scale, I realized that I made another 457 + mistake here. I was missing something very important that made it 458 + impossible to scale beyond those simple hackery tools to view web 459 + forms. I was missing the fact that React components are monads. 460 + 461 + This isn't gonna be a monad tutorial. There's no burritos here. But 462 + the small reason, the reason why I was missing this is because of a 463 + fifth design principle that is a secret and will now be revealed. 464 + 465 + The separation of Church-Turing and state. The idea is that the state 466 + was supposed to be external to your Xeact components. And you know, 467 + the Xeact components were just supposed to render HTML notes. And this 468 + sounds insane, but it does work for very small tools like that. It 469 + just doesn't scale to the levels that I wanted to do something more 470 + complicated, like a form for filling out and creating a server in one 471 + of my other projects. 472 + 473 + I kind of regret this in the long run. So I remade it. I remade things 474 + to make it better. React components are monads because they have 475 + effects and they can leech into each other. And there's a whole bunch 476 + of math that you can talk about here. But the important thing is that 477 + they're just monads. The important part is that what I had was not 478 + monads. I needed to have effects and ways to handle state. 479 + 480 + So one of the main ways that you handle state in React is with this 481 + thing called useState. It's effectively a container for data. You put 482 + the data in on one end, it triggers re-renders on the other, and you 483 + know, it just works sorta. The only thing is that you need to 484 + implement the hook system and I'm crazy, but I'm not crazy enough to 485 + re-implement React's hook system in a weekend. 486 + 487 + ```javascript 488 + const useState = (x = undefined) => [() => x, (y) => (x = y)]; 489 + ``` 490 + 491 + So I made the Xeact version of useState, which is this golfed 492 + monstrosity. It is a mutable variable that returns a getter and a 493 + setter. This is effectively a burrito around the data. It lets you 494 + change what value X points to so that you can build up state with a 495 + text area and then grab it out when you're submitting it to a fetch 496 + call. It doesn't trigger re-renders because, again, I didn't want to 497 + reinvent React hooks for a very good reason, but the surprising part 498 + is that this works. 499 + 500 + Oh, by the way, fun cursed JavaScript fact for you. The reason that 501 + this works is because function arguments are mutable references. You 502 + can redefine what function arguments are in a function, and that just 503 + works. Try it in production with some people that are more junior and 504 + newer to JavaScript. You'll never expect what happens next. 505 + 506 + At this point, I thought Xeact was basically perfect. It had all the 507 + features I want, none of the features I didn't, and here's just where 508 + I decided to stop working on it, not because I lost interest or anything. 509 + Xeact made my front end tasks as hard as I wanted to make them. I 510 + could go from thought to photon in seconds and there was no madness 511 + unless I felt like it. 512 + 513 + Grug brain make you want dot key work? I added TypeScript types 514 + everywhere, so the dot key worked. Absolutely beautiful. Full 515 + TypeScript for the entire femtoframework. 516 + 517 + It has mature and robust integrations with tools like Nix to automate 518 + your production deployments so you can get back to disrupting the 519 + burrito drone delivery industry. Don't think about minutiae. Go back 520 + to innovation. Xeact is the femtoframework that developers love. Don't 521 + believe us? See the testimonials for yourself. 522 + 523 + <XeblogSlide name="2024/xeact/067" essential /> 524 + 525 + > Our engineering team was blown away. This is versatile, powerful, and 526 + > constantly updated. It's everything we've wanted and more. 527 + 528 + <XeblogSlide name="2024/xeact/068" essential /> 529 + 530 + > This software's cloud backend has only been hacked twice so far this 531 + > year. 532 + 533 + <XeblogSlide name="2024/xeact/069" essential /> 534 + 535 + > It shouldn't crash until the heat death of the universe. 536 + 537 + <XeblogSlide name="2024/xeact/070" essential /> 538 + 539 + > Oh God, where are my hands? Why have you taken my hands away from me? 540 + 541 + Overall, you can see that Xeact is an obvious choice for your front 542 + end projects. Small, fast, and...small. Xeact scales to your needs or 543 + your money back, and you didn't pay me. 544 + 545 + However, Xeact only focuses on the front end. There's a whole other 546 + half of the stack to look at and a whole other half of that stack to 547 + look at. What would Xeact for the backend look like? 548 + 549 + <XeblogPicture path="blog/2023/incredible-xeact-journey/our-incredible-journey" /> 550 + 551 + We think there's room for the Xeact way to be taken to the backend 552 + where it belongs. That's right. At the end of October last year, 553 + Techaro acquired Xeact. All the existing cloud services were shut down 554 + and refunds were issued to all affected customers with only 24 hours 555 + of notice. However, in the process, Xeact has become more and more 556 + popular. 557 + 558 + <XeblogSlide name="2024/xeact/074" essential /> 559 + 560 + In the process, we have a unique opportunity to ideate some more 561 + synergy and really disrupt everything. We're going to revolutionize 562 + the concept of disruption. With Techaro, we are going to make the 563 + Xeact development flow for the backend and the front end at the same 564 + time using PILK, or Putting Individual Lookups in Kubernetes. This 565 + will allow you to make infinitely scalable backend services as easily 566 + as you can make infinitely scalable front end services. 567 + 568 + Legacy frameworks like Next.js make it easy to mix front-end and 569 + back-end code, but they have limitations when it comes to scaling it. 570 + What happens when your search route uses too many resources and blocks 571 + your credit card processing route and you just turned off the money 572 + generator and you can't generate money anymore? 573 + 574 + We can't stand for this. Why do we accept this as an industry? 575 + 576 + <XeblogSlide name="2024/xeact/076" essential /> 577 + 578 + So we are leveraging the power of PILK to automagically put every 579 + server function into its own auto-scaling group with Kubernetes with 580 + its own URL that changes it every single time. Every deploy, so your 581 + backend is also unscrapable. Take that, ArchiveTeam! 582 + 583 + It's infinitely scalable. Everything has its own execution context and 584 + your cloud provider will love you. This makes PILK a win-win-win 585 + scenario for everyone involved. Your front-end logic? Easy. Your 586 + backend logic? Easy. Your SREs, they'll have nothing to do and they'll 587 + be very bored. 588 + 589 + Maybe even so bored, they'll invent a sarcastic JavaScript framework 590 + with a bunch of bad jokes. 591 + 592 + And this allows the Xeact to advance you towards infinity. Looking 593 + back, we saved developers from the despair of Webpack with the power 594 + of the script tag. We covered the amazing might of naming and how 595 + choosing good names makes the Xeact experience what developers crave. 596 + We covered JSX, quasi-quoting, DAB, and the useState monad. And then 597 + finally, we looked towards the future with the sheer glory of PILK. 598 + 599 + It's obvious. 600 + 601 + Xeact is the perfect choice for your server applications. If you want 602 + it to happen on the front-end, use Xeact. If you want it to happen on 603 + the backend tomorrow, use PILK. 604 + 605 + <XeblogSlide name="2024/xeact/081" essential /> 606 + 607 + By the way, a little disclaimer, the entire preceding talk was a work 608 + of satire. All characters, companies, or events referenced in this 609 + talk are products of my imagination and thus fictional. Any synergy 610 + with observable realities is purely coincidental. I was not speaking 611 + for any of my employers' past, present, or future, and ha-ha, gottem. 612 + 613 + <XeblogSlide name="2024/xeact/082" essential /> 614 + 615 + Thanks for having me tonight, and I hope you learned some- 616 + 617 + (thunderous applause) 618 + 619 + Thanks for having me tonight. I hope you learned...something. By the 620 + way, when I was writing this talk, I put at least one joke about the 621 + industry in it. Extra credit if you can tell me what it was. If I 622 + don't get to your questions, email xeact@xeserv.us, and I promise I will reply. 623 + Otherwise, any questions? 624 + 625 + ## Q&A 626 + 627 + <BlockQuote> 628 + If having small frameworks means you eventually build up all the code that the 629 + framework has anyways, do you see that happening with Xeact? 630 + </BlockQuote> 631 + 632 + Well, yeah. That's going to happen no matter what you do. That's just 633 + kind of how this industry is. And the best way to handle that is to, 634 + like, just find something that you can be at peace with and then cry. 635 + Or laugh. I think laughing is more healthy than crying. I think that's 636 + what my therapist said. Not entirely sure. It was a while ago. 637 + 638 + Any more questions? 639 + 640 + <BlockQuote>How does it handle "rage clicks"?</BlockQuote> 641 + 642 + <XeblogConv name="Mara" mood="hacker"> 643 + Context: one of the talks talked about how you measure "rage clicks", or 644 + repeated clicks to get the UI to do something when things are slow by 645 + frustrated users. 646 + </XeblogConv> 647 + 648 + It handles rage clicks thanks to the debounce timer built into the 649 + Xeact standard library. And if you forget to debounce it, then you 650 + have problems. So don't forget. 651 + 652 + <BlockQuote>Where is the documentation?</BlockQuote> 653 + 654 + The documentation for Xeact is at 655 + [github.com/Xe/Xeact](https://github.com/Xe/Xeact). And if you are in 656 + doubt, read the source code. Because the source code is designed to be 657 + readable. Asterisk. 658 + 659 + <BlockQuote>Did you actually make the Xeact source code T-shirt?</BlockQuote> 660 + 661 + Oh, the T-shirt, right? I was actually going to get a T-shirt as a 662 + bit. But I thought about that idea after the logistics wouldn't work 663 + out.
+204
package-lock.json
··· 1 + { 2 + "name": "@xeserv/xesite", 3 + "version": "4.0.0", 4 + "lockfileVersion": 3, 5 + "requires": true, 6 + "packages": { 7 + "": { 8 + "name": "@xeserv/xesite", 9 + "version": "4.0.0", 10 + "license": "ISC", 11 + "dependencies": { 12 + "execa": "^8.0.1" 13 + } 14 + }, 15 + "node_modules/cross-spawn": { 16 + "version": "7.0.3", 17 + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", 18 + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", 19 + "dependencies": { 20 + "path-key": "^3.1.0", 21 + "shebang-command": "^2.0.0", 22 + "which": "^2.0.1" 23 + }, 24 + "engines": { 25 + "node": ">= 8" 26 + } 27 + }, 28 + "node_modules/execa": { 29 + "version": "8.0.1", 30 + "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", 31 + "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", 32 + "dependencies": { 33 + "cross-spawn": "^7.0.3", 34 + "get-stream": "^8.0.1", 35 + "human-signals": "^5.0.0", 36 + "is-stream": "^3.0.0", 37 + "merge-stream": "^2.0.0", 38 + "npm-run-path": "^5.1.0", 39 + "onetime": "^6.0.0", 40 + "signal-exit": "^4.1.0", 41 + "strip-final-newline": "^3.0.0" 42 + }, 43 + "engines": { 44 + "node": ">=16.17" 45 + }, 46 + "funding": { 47 + "url": "https://github.com/sindresorhus/execa?sponsor=1" 48 + } 49 + }, 50 + "node_modules/get-stream": { 51 + "version": "8.0.1", 52 + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", 53 + "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", 54 + "engines": { 55 + "node": ">=16" 56 + }, 57 + "funding": { 58 + "url": "https://github.com/sponsors/sindresorhus" 59 + } 60 + }, 61 + "node_modules/human-signals": { 62 + "version": "5.0.0", 63 + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", 64 + "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", 65 + "engines": { 66 + "node": ">=16.17.0" 67 + } 68 + }, 69 + "node_modules/is-stream": { 70 + "version": "3.0.0", 71 + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", 72 + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", 73 + "engines": { 74 + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" 75 + }, 76 + "funding": { 77 + "url": "https://github.com/sponsors/sindresorhus" 78 + } 79 + }, 80 + "node_modules/isexe": { 81 + "version": "2.0.0", 82 + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", 83 + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" 84 + }, 85 + "node_modules/merge-stream": { 86 + "version": "2.0.0", 87 + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", 88 + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==" 89 + }, 90 + "node_modules/mimic-fn": { 91 + "version": "4.0.0", 92 + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", 93 + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", 94 + "engines": { 95 + "node": ">=12" 96 + }, 97 + "funding": { 98 + "url": "https://github.com/sponsors/sindresorhus" 99 + } 100 + }, 101 + "node_modules/npm-run-path": { 102 + "version": "5.2.0", 103 + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.2.0.tgz", 104 + "integrity": "sha512-W4/tgAXFqFA0iL7fk0+uQ3g7wkL8xJmx3XdK0VGb4cHW//eZTtKGvFBBoRKVTpY7n6ze4NL9ly7rgXcHufqXKg==", 105 + "dependencies": { 106 + "path-key": "^4.0.0" 107 + }, 108 + "engines": { 109 + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" 110 + }, 111 + "funding": { 112 + "url": "https://github.com/sponsors/sindresorhus" 113 + } 114 + }, 115 + "node_modules/npm-run-path/node_modules/path-key": { 116 + "version": "4.0.0", 117 + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", 118 + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", 119 + "engines": { 120 + "node": ">=12" 121 + }, 122 + "funding": { 123 + "url": "https://github.com/sponsors/sindresorhus" 124 + } 125 + }, 126 + "node_modules/onetime": { 127 + "version": "6.0.0", 128 + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", 129 + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", 130 + "dependencies": { 131 + "mimic-fn": "^4.0.0" 132 + }, 133 + "engines": { 134 + "node": ">=12" 135 + }, 136 + "funding": { 137 + "url": "https://github.com/sponsors/sindresorhus" 138 + } 139 + }, 140 + "node_modules/path-key": { 141 + "version": "3.1.1", 142 + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", 143 + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", 144 + "engines": { 145 + "node": ">=8" 146 + } 147 + }, 148 + "node_modules/shebang-command": { 149 + "version": "2.0.0", 150 + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", 151 + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", 152 + "dependencies": { 153 + "shebang-regex": "^3.0.0" 154 + }, 155 + "engines": { 156 + "node": ">=8" 157 + } 158 + }, 159 + "node_modules/shebang-regex": { 160 + "version": "3.0.0", 161 + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", 162 + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", 163 + "engines": { 164 + "node": ">=8" 165 + } 166 + }, 167 + "node_modules/signal-exit": { 168 + "version": "4.1.0", 169 + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", 170 + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", 171 + "engines": { 172 + "node": ">=14" 173 + }, 174 + "funding": { 175 + "url": "https://github.com/sponsors/isaacs" 176 + } 177 + }, 178 + "node_modules/strip-final-newline": { 179 + "version": "3.0.0", 180 + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", 181 + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", 182 + "engines": { 183 + "node": ">=12" 184 + }, 185 + "funding": { 186 + "url": "https://github.com/sponsors/sindresorhus" 187 + } 188 + }, 189 + "node_modules/which": { 190 + "version": "2.0.2", 191 + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", 192 + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", 193 + "dependencies": { 194 + "isexe": "^2.0.0" 195 + }, 196 + "bin": { 197 + "node-which": "bin/node-which" 198 + }, 199 + "engines": { 200 + "node": ">= 8" 201 + } 202 + } 203 + } 204 + }
+25
package.json
··· 1 + { 2 + "name": "@xeserv/xesite", 3 + "version": "4.0.0", 4 + "description": "Hacking because node", 5 + "main": "index.js", 6 + "directories": { 7 + "doc": "docs" 8 + }, 9 + "scripts": { 10 + "test": "echo \"Error: no test specified\" && exit 1" 11 + }, 12 + "repository": { 13 + "type": "git", 14 + "url": "git+https://github.com/Xe/site.git" 15 + }, 16 + "author": "", 17 + "license": "ISC", 18 + "bugs": { 19 + "url": "https://github.com/Xe/site/issues" 20 + }, 21 + "homepage": "https://github.com/Xe/site#readme", 22 + "dependencies": { 23 + "execa": "^8.0.1" 24 + } 25 + }
+12
scripts/nukestickercache.sh
··· 1 + #!/usr/bin/env bash 2 + 3 + XEDNS=$(tailscale status --json | jq '.Peer | to_entries[] | .value.HostName | select(. | test("^xedn-[a-z]{3}$"))' -c -r | sort) 4 + IFS=$'\n' 5 + 6 + jo -a $* 7 + 8 + for xedn in ${XEDNS}; do 9 + curl "http://${xedn}/xedn/purge" --data-binary "$(jo -a $*)" & 10 + done 11 + 12 + wait
+56
scripts/prebake-node.mjs
··· 1 + import { execaCommand } from "execa"; 2 + 3 + if (process.argv.length === 2) { 4 + console.error(`usage: node prebake-node.js <path> <video segment count>`); 5 + process.exit(1); 6 + } 7 + 8 + const [_node, _script, basePath, countStr] = process.argv; 9 + 10 + const instances = await (async () => { 11 + try { 12 + const { stdout } = await execaCommand( 13 + "flyctl machines list -a xedn --json --state started | jq [.[].ID]", 14 + { 15 + shell: true, 16 + }, 17 + ); 18 + const instances = JSON.parse(stdout); 19 + return instances; 20 + } catch (error) { 21 + console.error(error); 22 + } 23 + })(); 24 + 25 + for (const i of Array(parseInt(countStr) + 1).keys()) { 26 + try { 27 + const reqs = instances.map((x) => 28 + fetch( 29 + `https://cdn.xeiaso.net/file/christine-static/${basePath}/index${i}.ts`, 30 + { 31 + headers: { 32 + "fly-force-instance": x, 33 + }, 34 + }, 35 + ).then((resp) => { 36 + console.log(`${x}: response get: ${resp.status}`); 37 + return resp; 38 + }) 39 + ); 40 + 41 + const resps = await Promise.all(reqs); 42 + resps.forEach(async (resp) => { 43 + if (resp.status !== 200) { 44 + console.error( 45 + `failure: ${resp.url}: request ID: ${ 46 + resp.headers["fly-request-id"] 47 + }, body: ${await resp.text()}`, 48 + ); 49 + } 50 + 51 + console.log(`ok: ${resp.headers["fly-request-id"]}`); 52 + }); 53 + } catch (e) { 54 + console.error(e); 55 + } 56 + }