 Hello, this is me, this is Jake. I'll be talking about Index TV and web standards in a few minutes with my good colleague, Surma. Hello, I'm Surma. Hello. So we are good to go. Do I need face powder or am I shiny? I sometimes get shiny. So, I'm going to try and do something a little bit different this time. I can tell. We have a laptop here. We don't have a laptop here, usually. What's happening? Now, do you remember the YouTube series Supercharged? Ooh, I'm not very familiar with it. Right. Well, I thought I would try and do that, basically. So after I said, you know, this is the end of Supercharged, you're not like, I'll bring it back. Lol. I'm bringing it back. All right, let's do it. Let's do it. There's a little twist. Oh, OK. And instead of coding something, like you're building something, I'm going to edit a web spec. Ooh. Because it's something that I only really learned to do the past few years. It's not something a lot of people do. I found a feature that I wanted to add. It's relatively small. So I thought we'll go for the whole end to end. Let's do it. Because I've never done actual spec editing. The only thing I've done is I contributed links to the stream spec so we can click the links and make it more navigational. And I can read the odd spec every now and then. I've never actually went through the normative sections or anything. So that's all right. I'm going to have questions. Got a problem at you? That's good. That's good. Yes. So I'm going to start with trying to describe the problem. The problem is IndexedDB. Now, there are a lot of problems with IndexedDB. That's a bad start. Yeah, we're going to talk about one specific one. Are we fixing IndexedDB? Is it going to be better? Very, very slightly better in a very small way. Better than worse. So when you do IndexedDB, this is me trying to remember the API. The actual API. Not your nice promise wrapper on top. Yeah, this is raw IndexedDB. You get a request object. Let's see. And there's on success. I really love auto-complete in editors. It's absolutely saves me. Yes. So right, we've got our own success. This is going to give us a database, which is dot result. Yes, I'm remembering the API. Because why would it be a parameter to the on success function, right? Of course not. No, no, right. IndexedDB is a horrible API. It's a fractal of weird design, really. Yes. So the way you work with IndexedDB is you create a transaction. So I'm going to get a transaction. You say, like, you give it the name. I mean, we're kind of assuming that people know what IndexedDB is. It's like it's a database for on the client side. And you can have multiple storages. And you can put things into the storages. You have transactions, as we've shown here. It's a pretty, in a sense, a pretty powerful API. Yes. Once you have wrapped your head around the kind of weird API that got shipped in the end. Yes. And there's a very specific part of it that I ran into, like a problem that I ran into. And it's with cursors. So. All right, so you create a transaction. The transaction is on a specific store, which can have multiple entries. You can have multiple, you know, if you imagine a SQL database with your tables and rows, a table would be a store, and in that store can be multiple entries as in rows in a SQL database. Yes. And so you have to limit a transaction to a set of stores, I think. Yep, so a transaction. In this case, it's just one, but it could be multiple. Exactly, exactly. So I'm going to go for on success here. And this is how you open a cursor in IndexedDB. In a cursor, it's basically a little data circuit that allows you to traverse all the entries in a store. Yes. So much I do know about IDB. So I've done that now. So this is the sort of boilerplate. So quick and easy. This is a really easy episode to explain. This is basically the start. Boilerplate. So I've been creating a little wrapper library around IndexedDB. For a while now. For a while now. Yeah, it's been released for a while. I'm working on the next version. The next major version with breaking changes, I guess, and everything. It's called IDB. It is called IDB. That was available in MPM. Great. That is surprising. I'll take it. So because I guess we should be clear, IDB is your library. This API is called IndexedDB. Yes, I made it very easy to understand. It's very common to call IndexedDB IDB, but technically. Which is why I thought I'll take that namespace and try and make it sound all official. Great, well done. So what I was trying to do is create an API where, if you get given a normal IndexedDB object, you can pass it into my API and it will enhance it with all of the extra stuff and make it easier to use. And the problem I ran into is with cursors. So once you've got a cursor, the cursor will give you the key and the value of this one entry. Of the row that it's currently at in the storage. Yes. And when you want to go to the next row, you do cursor.continue. And now this doesn't return anything. What it does is it causes the request for the cursor to fire its success event again. Right, so this is basically a hidden way of calling this function recursive. Yes. Yes, it's a super weird way of doing that. And then if the cursor, well, let's just say, if the cursor is no, that's you done. Then you reach the end of your object store. Exactly. OK. So I face a problem with this, because if someone just passes me the cursor object, I have the means to. When you say me, you mean you as the author of IDB. Yes, as the library. So someone, or if you pass a cursor to another piece of code, if we keep it generic, you could then call continue. And you're screwed. Yeah, you've got no way of hearing that that worked or that failed, because you would also need to have the request. Yes, you need to be in control of the on success handler. Yes, and I noticed that in index DB, there's often routes back like up the chain of. The hierarchy. The hierarchy. So from the store, you can get to the transaction. You can get to the database, I think. It's DB. Yes, you get to database. But there is no link between the cursor and the request. So I was like, wouldn't it be nice if there was the request? The request. There we go. If there was something like this. And then I, as a library, could temporarily override on success, do the continue bits, do my work, and then be done. I could pass you a cursor, and you could use that cursor. That's great. That seems like a very reasonable thing to have, Jake. Ship it. Exactly. Well, that's what we're going to try and do. That's what we're doing today. Yes, it is. So I thought, how do I start this? Yeah, well, how do you start? Because now you want to have this on the web platform, right? I mean, you could probably polyfill it by just attaching random things to the cursor. But that wouldn't be standard. We want to make it a standard. So we have to look into the spec, I guess. Yeah, I would say what my library is doing is it depends on being the thing which opens the cursor. Because then it has the original request. And then it can maintain that link itself. But if you just pass the cursor, yeah, you're screwed. So if I was going to do some work on a spec, what I would do is go to MDN. So. Interesting. I've got muscle memory for when I search to put MDN or NPM at the start of everything. I get the two mixed up all the time. But this case, I do want indexedDB. Yes. Let's have a look. It's called indexedDB, not indexedDB. I call it indexedDB all the time. So do I. But I'm just saying, technically, you're wrong. I'm technically wrong about many, many things. And I'm sure many of them are going to come to light in this episode. So yeah, I think indexedDB at MDN has always a very good browser port table and links to the specs, which I value a lot. Yes. And if you want to find which spec something is defined in, MDN is the best way, I think, to get into that. But see, we already have two specs. We have indexedDB API and indexedDB API version 2.0. Yes. It's useful, isn't it? So I'm guessing the cursor is so basic I would expect it to be in the version of 1.0. Well, that's true. It's a different question, but that would be my gut feeling. If you've got two choices between two specs, let's open them both. We'll have a look at the URLs here. So this is the URL for one of them. All right. But even that one says 2.0 here. Yes. That's because 2.0 has now shipped. We're now on to 3.0. But the links in MDN. We should fix the links in MDN. Someone should update MDN. If the link to a spec has TR, there's a good chance you're looking at the out-of-date one. Yes. Those are the snapshots, which I've learned in Houdini. Those are the snapshots. Like, this is a version. We have now officially, this is the snapshot that has this version. And I've been told what you should do. You look at this spec, and you find the address draft. And that's what you look at. And there will be a link from Houdini. Usually there will be a link. Usually. The joke is TR stands for trash. Oh, harsh. And that's not always true, because sometimes that is literally the latest version of a spec. But to be fair, it's only been recently, I feel like, that everything turned into more like a rolling release approach rather than working from version, major version to major version. Yes. All right. So if we have the red bar on the left side, it means we are on an address draft. And this will most likely be the most recent spec. Yes, exactly. That doesn't that mean that there could be stuff in there that isn't even in browsers? That is true. Yes. But for this use case, we want the latest version, because the thing we are wanting to create might already be created. True. Oh, so we're looking at the latest version to see if it's already created. If not, then our stuff should be something that is added to the latest version. So that's why we're working on this one. Yes. So if I was looking for something that I want to be exposed to JavaScript, I'm looking in the spec for the API section. Right. Here it is here. Yes, because there is a section called API, which gives you the typical web IDL thing. So we have an almost TypeScript definition of the different things. Yeah. So here it is. This is the one for cursor. This predates TypeScript by many, many years. Sure. I'm not saying it has types. That's why I mentioned TypeScript. Yes, exactly. I mean, it would be interesting to see if we started writing specs now from scratch, would we use TypeScript? Is that enough? There's some extra little bits of annotation around here. But it is doing the same job. It's defining the types of all of these things. But if that is the section, what is the section above? Because I was looking at the sidebar and said, oh, 2.10. That's cursor. That's where we go, right? Why didn't you go there? So specs tend to be split into two sections. One is the constructs or the concepts, different specs. Let's call it different things. And this is just describing how the feature works irregardless of JavaScript. So not into a JavaScript. So it says, like, we look at a database. A database has a name. A database has a version. This is just talking about the structure. It doesn't mean those things are exposed to JavaScript. So this doesn't help you write code. It potentially helps you or probably helps implementers understand the nuances and details. Yeah. This is describing how the system works. And then we would see down in IDB database, this is describing the JavaScript interface, you'll see it does have a name and a version. But this needs to be explicitly set up. So when you click on name there, it says the name attribute get and must return the name of the connected database. We click that. It will then link up to the concept. So this is where we connect the IDL stuff to the definitions in the constructs area. So this stuff at the top here, this lives in C++ land. It can live on another thread, all of that sort of stuff. So that's probably more relevant to implementers, not really relevant to me as a web developer. Yes, I would say so. If you're looking to see what a thing is capable of or what the API is, straight into the API section. Cool. And we can see cursor here. I feel much more at home with this view already, because it looks a little bit like JavaScript. And I get this. Yes. So we can see here, there's this function called advance. Oh, we have advance and continue now. Advance and continue. What's the difference? So advance takes a number. So I'm saying skip five items. So it's like calling continue five times. You can't call continue five times. You can only call one of these advancing functions once. Can you tell I've been working with IndexedDB recently? I've had to get the spec in my head. So yeah, it would be convenient to just call that. Then unsuccessful called, I call continue again. Do that five times. Then it would be equivalent. That would be fine. But it tends to be also annoying. Yes, it sees it as a bug if you try and tell it to do two. Because if you said advance five and advance two, it sees that as like, which do you mean rather than adding the two together? OK. So we do see on the cursor that there is no request. Right. Which is what we were expected to find, but we knew it wasn't implemented. So what do we do? Do we just add read-only attribute request and show it? Not yet. Oh, OK. Because the next thing is to propose the feature. Oh, yeah, you can't just decide this on your own. I mean, you could. You're not going to get anywhere with it. So you're going to be very disappointed. So at the top of the spec, there's a link here for issue tracking GitHub. It's mostly GitHub nowadays. It is mostly GitHub nowadays. So I'm going to go into here. And so some of this. Oh, look at this. There's a cursor.request issue opened by Jake Archibald. Yes, there is. So I've set this up in advance a bit. In fact, when I started doing this, I wasn't thinking about making an episode. But what I did was I went in and said, this is a bit weird. Why isn't the cursor.request is essentially what I said. Well, link to this in the description because some people might want to know how you phrased and how the discussion went. Yes, exactly. So this is what I wrote, just basically requesting the feature when you pass something around. And do note, it's not like a long-form, formal tone proposal, just like, I have this idea. What do people think? It's still a very human approach, even though it is working in spec world right now. Yeah, I'm describing the problem. I do suggest a solution, but I described the problem first. And that's a common problem I see in some feature discussion. Someone comes to go, I want this. And the next question is why. Or why? So I start with the why. And here's my proposal because in case the person who knows more than me about this stuff can come and say, here's a better way to solve your problem. Or here's a better solution to the problem. They don't have to wait for the back and forth. And it turns out, this is Josh Bell, who does the implementation in Chrome. Also does the spec work. He's a Googler. And he's saying, yeah, sounds all right. Oh, this doesn't usually happen. And sort of agrees with the use case. He asks other spec folks to chime in. And then we've got Ali from Microsoft, Andrew from Mozilla, and Brady from Apple saying, it's fine. This has never happened before, but never this quickly. That is very, very frictionless. Yes, and I think it's because it's such a small feature and it fits in with index DB so well. And the implementers are like, they have a feeling it's really easy for them to implement. Yeah, I mean, it feels like the thing that under the hood should be very easy to do. I'm speaking as someone who has never shipped anything in a browser myself. But literally just exposing something that already exists. And it's probably internally already tied to the request anyway. Exactly, exactly. And so that's what I thought. I thought, well, why not? Because it feels like a small change. Let's do it now. So that's the intro to this episode. Cue the intro. Are we going to put a remix of the title sequence of Super Charge, don't we? Oh, yes. Can we do that? Can we put comics on it? So we're going to do this. We're going to do this. So where do we start? Is it? So there's two things you need to land for a spec change. Spec change and tests. Tests are very important. And I am going to do the tests first. Oh, you're going test-driven development. And I don't always do this. But all the times I've done it, it's been incredibly successful. So I don't know how I don't do it more often. So we have this thing called web platform tests, which you can get into by WPT.FYI. Well, I would say you can get into it from the spec as well. So right at the top there, a link to the test suite. It links to web platform tests, which is the repository it has. Yes. Literally the entire web platform tests. Yes. And every API is supposed to have a test suite in here. Yes, absolutely. So this is all the index DB ones. That's a lot of tests. There is a lot of tests from many years ago, some of them two years ago, to five days ago. That's cool. Five years ago. Five years ago. Excellent. Any advance on five years ago? I'm not going to do that. I'm thoughtful. So there is a lot of files. Yes. And so writing tests is a really good way to contribute to the web platform, because the tests are in JavaScript. So you don't need to understand the specification stuff in order to contribute. You just write the test to describe how the feature you want works. And I have contributed some tests myself. And while it is a bit odd, because it's obviously a kind of organically grown system over time, you just look at some other tests and you will get the hang of it quite quickly on how it works. And that is exactly what we're going to do. So the repo, yeah, it covers a lot of scrolling, all of the stuff you need to set up, something like this. It is really well documented. I had no problem setting it up the first time. I wanted to. It just worked pretty much straight away. So well done, then. So you would check it out, fork it, and then follow these instructions, because there are some things you need to add to your host's file, that sort of stuff. So what I'm going to do is, so I don't need to fork web platform tests, because I actually have commit rights to that. I know. Check me out. So let's go to, and this is me trying to remember. Yeah, there we go. Brilliant. Web platform tests. And this is what we're going to start editing. So we're going to go to WPT serve, which is this is all documented. Yeah, the start of the test server. Look at all that. It makes total sense. It starts the test server on many ports. This is the one I'm going to use. This is the normal unencrypted one. Yeah, we have multiple ports and multiple pulse names in this repository, because sometimes you need to do tests for cross origin things. Cross scheme things, like HTTP to HTTPS, that sort of stuff. The server has to provide all of that. That's why that might have looked a bit overwhelming. Yes, so I'm going to pick up one of these. All right, so now in the index to be test folder, if you open any of the HTML files, as far as I know, the tests will run, and you will see the results. Cool. There we go. Look at that. How did you decide which file you are? Is it just like to show the results, or is it the file you're going to edit? No, so this is the, well, it's a little bit different specs right at the test differently. I've picked a file called idbcursor-source. I would assume. I say I'm assuming. I know, because I've looked at it in advance. These are the tests for cursor.source. So from the cursor objects, you can go to the source, which gives you back the store or the index. So from the cursor, you can go to the store, but not the request. So the request is literally like the missing link. Yeah, exactly. Exactly. Interesting. OK. So we've got that. So we're going to edit this. So how afraid should I be of picking the wrong file? If I want to continue, I'm not sure which HTML file I should add my tests to. I mean, not that afraid, right? In the review, people are going to be like, good tests. Just move them to this file, please. And then that's going to be a very small file. The people that are usually in my experience are really friendly. Yeah, they are, actually. And super grateful for having people work on this stuff. Because it's dead handy. So let's go for web platform tests. Great to break out the VS code. Here we go. All right. So yeah, we want to write tests for the feature. Now, one of the benefits of doing tests first is implementers who are super keen can start implementing based on your tests while you're working on the spec. This actually happened with the abortable fetch stuff. Interesting. So this is why it's a really good place to start. So that means it's not even frowned upon to open a PR for my new tests, even if the spec change hasn't landed yet. Correct. And you can have those reviews going in parallel. So here it is. This is the source for the tests we were just looking at. OK. So let's dissect this a little bit. So we have the test harness, test harness report, which is test harness. We can ignore that for now. That's the framework for the tests. It's literally the harness for the tests. The equivalent of Mocha and Chai, that sort of thing. Cool. Support is probably literally that support functions, I guess. Yes, specifically for the index DB stuff. Right. Because in this folder, it's not a global support file. It's in the index DB. And that's how I knew that just by the path there. OK. And the rest is test functions. It is test functions. OK. So yeah, there's a cursor source test. And it seems like this is generated tests, because they're calling this function twice. It's passing in a function to. I would say this is a very complicated written test. OK. Difficult to follow. But one of the important things for me here is seeing this index DB test. So it's a special index DB test function. Yeah, and I know this is not part of the framework of the web platform test framework. So one of the things you can get from right at the bottom of the readme for web platform tests is how to write and review tests. Well, that seems helpful. I don't know why that links right at the bottom, but it is. So we go into writing tests. It's test harness is that that's one of the includes that we had in there. And it will give you the documentation for test harness. And this is going to tell you how to call tests like you do that. Right, so I've seen these before. You have a test function. So I was surprised to see that index DB has apparently their own version of the test function. Yes. Yes. So we can see here there's async tests. There's ways of doing all of this sort of stuff. And then towards the bottom, you get all of the lists of different asserts. Here they are. So a certain array is not equal to. So it would be the mocha chai thing. Yeah, so this is chai. The rest of it was mocha. If that's what you look at. Mocha, mocha, who knows? Mocha, mocha, I don't know. So index DB test is not in there. This is something special. So that will probably be in the support just, then I would assume. Probably. And how much easier has ECMAScript modules made our lives that it would be so easy to find where that was coming from? Especially with TypeScript and then VS Code, we just press F12. Yes. So I'm just going to do index sup. And it's going to give me that file. And we can have a look at it. And it was an index DB test. Oh, there we go. So here it is. So it uses async tests under the hood, as we can see straight away. Exactly. Do we care about the details? You provide an upgrade function, an open function, descriptions, and options. All right. Yes. So upgrade function, this is something index DB has as a way of setting up the schema for a database. Open function is, once it's open, once it's upgraded. The success handler, basically, more or less, I guess. Exactly. Exactly. And then just the name of the text. So I'm guessing they wrapped it so it makes sure it cleans up after you, deletes all the stray, collect objects that might have been created. Deletes an advance test there. Make sure it throws if it can't delete the database, if it can't open the database. Because turns out index DB is supposed to disk. So if we didn't clean up after ourselves, we might blow up the disk usage. Yes, and it gets really messy, especially if you get the tests overlapping each other. Yeah, so this handles all of that, which is nice. So let's go back to cursor source, brilliant. So we can see here that, yes, there's going to be a couple of callbacks to the first one there. This is where it's setting up the database. That's the upgrade function. I think, if I remember correctly, the upgrade function, which is called, as I said at the start, is the only function where you can create object stores. Yes, create object stores, create indexes. You can add stuff to the store, but you can do that elsewhere as well. But yes, it's the only place where you can actually modify the structure of the database. And then the next stuff is coming in. This is where we actually write the test, I assume. Yeah, and we can see here it's being passed the t, which is how I know it's the test object, because that will appear here, t.step function. This is how. So the white platform tests have promise tests, which are so much easier to deal with. Because you write an async function, and it's just sequential. IndexedDB a bit more. It doesn't have promises. It doesn't have promises. Hence your library. Hence the library. So we're dealing with this step function, which is you declaring, I'm giving you a callback that must be called. This is definitely. That meaning if a callback doesn't get called, the test will fail? Well, actually maybe not, because this certain reach is not going to happen. Do you know what? I'm not sure. For every callback, you have to wrap it in step function. This is why I normally do promise tests. So we're going to cargo call this a little bit, and write the test for the feature we want. So I just can duplicate in this. You can if you copy paste. Oh, copy paste, of course. Brilliant. There we go. So I'm going to call this. So you create a new file. Will that automatically run just by existing? Yes. When browsers run all of these tests at once, it just crawls through the system. Cool. Yeah. And we're going to have IDB cursor request. I'll probably change this bit at chromium.org. Have I spelled that right? Yes, I have. That's about right. Excellent. And get rid of that. You don't have any cool letters in your name. I know. It makes it easier to type on a British keyboard, though. So we're going to write these tests, then. Delete the old ones. Delete the old ones. Yeah, I guess we don't really need anything else. I kind of wasted everyone's time copying and pasting that because we're going to start with pretty much this. So actually, what I will do is we'll keep this around so we can double check. For reference. How to do stuff. Yes, because it's not something I've done a lot of. Is it index DB test? Yes. And then we've got our three functions coming up. So this first one is going to be setting up a database. So let's do that. It takes a test object and a DB, I think. Is that what it did? Yeah. All right. Do you know what? This looks pretty good to me. I'm just going to take that. Why not? We don't even need an index, do we? We don't need an index. We do need data. We do need data. I will take our data, an entire data. And just to prove that, I didn't steal it. Modern JavaScript. It's mine now. It's actually, I wanted to ask, because you used an error function, you used const. What JavaScript is usually acceptable? It comes down to? Because there was a time where letting const or error functions were in some browser, but not others. Absolutely. So the idea is to make, you want these tests to run easily in the browsers you want them implemented in. So I wrote some service worker tests. And I used broadcast channel to communication. And it's kind of like, if you want Safari to get this right, you need to stop using broadcast channel. I was just using it as a way to pass messages around. It wasn't able to the test, but it was making it fail in Safari. So here, this is only going to be adopted by new browsers. We're talking about a new feature. So it's not going to some, not something that we're targeting at IE 11, for example. Exactly. So we are, we can look at the most recent versions of all the standard browsers. And if they have a certain feature, it's cool to use it in the test. Is that basically fair to say? It is absolutely fair to say. So what am I going to do now? So where does this come from? Oh, OK. So they're passing this function in as a way of creating a cursor. So I don't know how to steal that code as well, I reckon. So let's do that and format that all nicely. So we've got a database, creating a transaction, creating an object store, we're not using an index. And that's going to be a request for our cursor. Cool. Sounds good. So our request dot on success. And this is where we need to bring in that. The step function. The step function. And because I have a zero memory, I'm always going to look at a step function. Really annoys me that it's underscore-based. I saw a lot of, are they called snake case? Yes. It is, in a way, JavaScript unineumatic, I think. I think, in terms of, it has mostly settled on camel case. Camel case. And this is, I think, a lot of this framework is Python based. So I think it maybe has inherited from that. It's still readable. So it's not too big a deal, I think. And I think I could just pass a function into that. Right, that's fine. And so we're kind of at a point now. I've got my cursor. And this is one of the reasons why we needed to add data into this store, because if we didn't do that, we wouldn't get a cursor. We wouldn't get a cursor because there's nothing in there. Right. Exactly. So I'm going to request dot result. So currently, you don't have a description of what this test is testing. Oh, that's a good point. Where would that go? It's the third argument here. So I will write. Well, let's take some inspiration from how the other ones are written. They are not. They are brilliant. Excellent. There's a test name here. So where is it getting that from? Oh, it's the first argument. Oh, I see. So they're taking document. Wow. Are you going to steal that? Yeah. Why not? Because I should. It's pointed out that there's a bit I've forgotten here. So this is so hacky. It's a document or title. That's it. That's our test. That's our test. Brilliant. Sorted. So now I need to write some tests for this feature. I'm hoping someone says in the review that this is not a good title for the test. Because it's just ID because actually it is a good. No, no, it's because it's got dot request. Yeah, it's actually a decent title. Although it really, because to me, and this is a totally off topic bug, but when people write this, to me, this looks like a static property of the class. Oh, that debate. Yeah. It's not because that request is not on the ID because a constructor. It's on an instance of the ID because. I've seen some people do this, or you could do. Oh, no. No. I mean, that's, well, whatever, fine. Technically correct. Technically correct, I think you'll find. So I've got the cursor, and now I want to write some asserts. I think the, I'm going to do assert equals. There we go. So it's the actual value, the expected value, and the description. All right. That seems doable. Because what I want is, I guess, is there a strict equal? I would usually go for a strict equal. I would have looked at it like, I'm guessing you want to just check for existence, right? Or for not null? Yeah, well, I think it might be strict by default. I would hope so. Because if you assert. It's not in Chai. This is the thing. So I've been writing Chai this morning, so. Strictly true. It's strictly, and but down here. Relies on triple equals. Oh, it relies on triple equals. Well done. So it uses strictly there, and then uses. What kind of documentation? Brilliant, excellent. It is actually really good docs. So assert equals. I want a cursor, see, I can't spell cursor dot request. And I want that to equal. The wreck. The wreck. So has request. Can't type. How do you get stuff done? Well, I don't. That's what happens. So we've actually tested quite a lot in this one line. Because we've not only tested that cursor dot request exists. We've kind of tested its type. And the correct value. And the correct value. And this is. And this is all you're asking for. You just want the dot request to exist and be the value of the original request. Yes. But this test also tells implementers that it's not a new request object. Yeah, it represents the same underlying request. It is literally the same JavaScript object. It's going to be equals equals equals. So that's actually interesting. Is that something that you care about as a developer? If you just want to have access to the request with all the data it provides, do you actually care about it being the exact same instance? I think you do. Because, OK, there's not a lot of time when you'll be doing equals equals equals or something. But you might be using weak maps. Good point. And so you want the same representation, the same thing to be exactly the same. Yes, you want the quality to hold. All right, that makes sense. And what I will also do is I'm going to steal from. Let's see what else have they got here. So they're testing instances. We don't need to do that. That's already taken care of. Stringified object, that's already taken care of. Because if it's equal to request, that's somewhere else. But this, we're going to steal. Because I want to make sure that, like the other things, that cursor.request is going to be read-only. All right, so that's actually a different. So you don't pass in cursor.request, but the name of the property. Because I'm guessing it gets the property descriptor and checks for the read-only file. Yes, and I guess it will try and set to it maybe as well. I'm not actually sure what the implementation is. But that essentially tests the two things that will make it similar to the other bits, but the thing that we want. And I think that's it. So now we can run the test and see it fail. And now we can run the test and see it fail. So I should just be able to request. And it failed. It failed. And it turned equals on the finite of the error I was expecting. No, it is not the one I was expecting. So is it assert equals or? I would, yes. Yeah, look at that, brilliant. One time you don't copy paste. I know. I'm such a copy paste, sir. So it should still fail. Expected object, but got undefined. Hurrah, we have uncovered this test is failing. All right, so. And this is actually, if you wanted to stop here, and you have helped massively with web standards, like the feature you want, that you've been told, yeah, great, writing the test for it is a solid, solid effort, right? It's more than most people do. So that's actually a really good point in the sense like, if you're not comfortable engaging necessarily with spec language and all the patterns and stuff that comes with that, this is a very web developer approachable way to how can you and drive features for that you personally might want. Exactly. And it's really difficult writing tests for things that don't exist because you don't really know how, like, does your test actually work? I mean, we spotted something there. But maybe you would do something like, I don't know, what if we did cursor.request equals, like, we'll just do that. And, oh, right. So it's got onto the next test, but it's showing us that it's not read-only. So we at least know that it passed that first test, so we can kind of prove. So that's why the only test is actually important. It's actually important. So we know we've got somewhere. We know we've got a solid test. Yeah. All right, so now what you would do now, you would commit this PR and then we'll move on to the spec? Yeah, I will do them in tandem and we'll do the PRs at the end. But, yes, if you wanted to stop here, you could do that. You could have people check it over. As you said, this would already help browser developers to implement this feature. Exactly, exactly. But we're going to go do the spec. Oh, boy. Oh, boy. So we need to have a think about what we actually need to change. We've already talked about the structure of the spec. It's into the two parts. So we would need to create a link between the cursor and the request and then find a way of exposing that. Well, I guess we should check first if under the hood the cursor isn't already has a link defined to the request, right? Exactly, and in the same way that if you call cursor.continue, it works somehow. So there's a good suggestion. And then use the request, right? Yes, so it must have some kind of connection to the request under the hood. And we will, you see here. The cursor has a request. Yes, which is the request used to open the cursor. Great definition. It's there. It was just never exposed. Interesting. To JavaScript. So I guess what you just did, you just skim read the bold things, I guess, right? Like, is there something that's the kind of eye you develop if the more time you spend in specs where you're able to look at this flat of words and don't actually have to read every paragraph? Well, the way I actually discovered that is I went into Object Store. I went into Open Cursor. I started sort of reading how does Open Cursor actually work, because this is where you get the request. So this is the fifth section called algorithms, I guess? Yes, which is just sort of where the two things are matched together. So but it's not quite code. It's prose in a step-by-step fashion. Yes. But that is basically where the implementers then turn into C++ code most of the time. Yeah, not prose, in servo, that sort of thing. So what's actually running under the hood? And it was just where I saw step eight, which was set Cursor's request to request. That's ironic. There it is. And linking back to it, a cursor has a request. Is that right? It's there. Yes. So that means that tells it that the implementers already have this in their code. A cursor has a request. So that would tell me from what you've told me so far that the only thing that's actually needed is to put something into the API bit. To link those things together? Yeah. Pretty much. So, well, let's dive into it. So at this point, you would go to GitHub, clone the spec. Clone the spec. Then you've got a copy of it. What does a spec look like in code, Jake? Well, well, let's have a look. Is it in next DB? It is. Now look, I did some work with intersection observer at some point. I can't remember that, but fair enough. Let's sort out the way item likes to resize things. Is that? There we go. That's fine. So if we have a look at what's here. That's a lot. There's a lot of stuff. The actual spec is. So there is an HTML file. But that's not it. That's generated. That's the compiled output, isn't it? Because the source is a language called Bikeshed. Bikeshed. And this is this file here, index.bs. It's very funny. Bikeshed. And it's a special format kind of based on markdown properties. Show to me, show me the thing. Let's dive in. So here we go. More window resizing. More window resizing. Oh, look, the tab's already open. The tab's already open. It's almost as if I've been looking at it already. So this is it. So if we. Wait, let's go to the start at the top. We don't have time to go through the whole thing, man. No, but what do you get greeted with when you open this file? When you open this file. Oh, look, it's HTML. Yeah, you can put HTML in it. It is like markdown in that respect. So you can see a list of editors. This is just a metadata section. So is this what will be translated into the section we saw at the start of the spec where the list of the get-of-its? So this is actually something that will also be parsed. It's not just a generic pre-section. It is actually being parsed and turned into the links that you see at the start of this. Exactly, exactly. So there's more metadata, some styles, because it is just HTML. They've got some custom styles here for whatever reason. And then we're starting to get into markdown. So markdown heading. I like this little comment thing they've done there. You don't need to do that, but it looks nice. Also, oh, soft wrapping. I dislike it. I do, too. Because I can turn wrapping on this. Why do I have to do? And also, what column is this wrapped at? It seems it's entirely arbitrary. Why is that column? And this one runs over. I suppose that's code. And you can see this, you know. That looks familiar. Familiar markdown, if you use get-of-it-before. See, this is wait. Anyway, that's a different topic. If you're editing a spec, the best thing to do is to just sort of figure out what styles they use. Go with the flow. Don't stand out. Make it consistent as much as you can. Don't go about fixing other stuff, because it will make your diff or your PR much harder to look through. The thing is, you add, make it seem like it's part of the spec. Right? And then? And some specs use more HTML. Some use more of these bike sheds, shortcuts. There's quite a few shortcuts going on here, which I prefer, than writing out HTML by hand. I should say that if you want to dive into bike sheds. If you wanted to know what kind of shortcuts they are for bike sheds. Yes, so if you wanted to get bike sheds, wanted to use bike sheds. From our colleague, Tabadkins. From Tabadkins. It's not the only spec compiling thing available. There's Respec as well. I don't really know a lot about Respec. So I've not done a lot of work on those standards before. But there is documentation. The documentation has also been generated with bike shed. Very good, very good. Very recursive. And so it looks like a living standard. And it details all of these shortcuts, how to deal with these. And to install it as well, which you might be interested in. Yes, I would say that there's also a web service for you just give it a bike shed file. It gives you HTML back, which is very, very, very handy. So let's sort of dive into how we were going to tackle this. All right, where would you want to go first? Are we going to the API section? I would go to the API section. It's going to the API section. So how do you go there? Because it's a big document. It's a big document. And do you know what I do? I look for some text near the thing. It's just super, right. I mean, IDL should be fairly precise to find, right? Yes. I mean, the word interface, which I think. So let's maybe look for this. Interface is going to be everywhere. That might be the entire thing. Oh, the entire thing. Whether that would be in tags or not, this looks good. So this is OK. We've only got a few of these. There it is. IDB Cursor, interface IDB Cursor. This is where we want to be. This is where we want to be. And we want to add a new thing here. Let's read only attribute. Now, the way Web IDL, what it calls an attribute. And this really confused me for a long time. I thought it meant HTML attribute, because that's where I was seeing it in specs. What it means is get a set, which is silly. If you want to look at Web IDL, there is, of course, a spec for this. So when you say get a set, I couldn't we just remove the rid only and only define the getter? You could. Now, the reason Web IDL tends to be quite detailed is browsers actually use it for code generation. Right. So there tends to be quite a lot of information embedded in this stuff. So browsers will actually parse this. It will parse this bit and know where to expose that stuff. And it will essentially generate a lot of code. And the developer just has to sort of fill in the intents. The browser developer. The browser developer. Exactly. So what do we want to do here? Well, so the first bit is the return type, which is going to be IDB request. Which you looked up some time beforehand that there is also the interface IDB request. Yes. Yes. From using IDB, I know it's that. And that's called request, because that makes sense. And there we go. Let's see if it compiles. Are we done? We're not done. Oh, boy. So because I've installed Bikeshed, I can do Bikeshed. Watch. Let's do that. And off it goes. It has a think about some stuff. Done. Great. Excellent. And now I guess the HTML file has been updated. Yeah, and Bikeshed checks a lot of stuff. So we have actually got a warning here. It says there's a definition that's not referenced locally. If I remove that edit we made, that error is still going to come up. So I would always say run first before editing. And if there's errors, they're the ones you can ignore, because they're not your fault. That's good, because I can't tell what this entry is by. So what it means, what's actually happening here is that's equivalent. So this is just a web server that I started up. Because Bikeshed doesn't do that for you. Maybe it can, but you didn't do that just now. Yeah, it can't do it. Because it's just an HTML file that works anywhere. I always run a web server. You can even just open the file. You don't even need a web server. You probably could. But I always run a web server on my dev folder. Oh, I'm a real developer. I've got it set up to happen at startup, because I always need one at some point. And it's normally in that folder. So what were we looking up? It was this weird equivalent thing. So we can see this in bold definition. So we can see the end result is equivalent, but nothing's actually referencing it. And it's not exported. So it's flagging it up and going, you've got this thing here that you're not using. Why? Which is kind of cool. All right, that's helpful. But you know what? Not our problem. Not our problem. We didn't write it. Let's have a look at the cursor API stuff. Oh my god, look at that. But it's bold and not clickable, Jake. Yeah, it's not clickable, because this is the only reference to this thing, because we've not referenced it elsewhere. We've been looking at sources being something similar to what we've got. So where does it link? And so this just happens below this block. The source attributes getter. So once again, pros must return the source of this cursor. And these are all linked up, and you can sort of see what it is, which is the index of the object store. Which is back there. Once again, I think it will never return null or throw. All right, cool. So yeah, it's interesting. So every spec offer has their own sort of style and quotes. I would say this attribute never returns null is something I would put in a note, because the fact that it never returns null is defined elsewhere. That's kind of just like a code comment, which I would put in a note, because it's not essential information. Neither of these approaches are wrong, though. You can do it this way. You can do it your way. Both seem to be well-defined, because this is in a spec. One thing that is not how I would write a spec, but I see a lot in spec land. It's actually something I used to do, and I was told off for it. It says this returns the source of the cursor. But the source points to this thing up in the constructs land. And the spec doesn't describe how to turn that into a JavaScript object. Right, which should be explicit, really. And this is when I started doing this. Boris Sabarsky, who's one of the huge minds at Mozilla. So it says, well, how do I convert to this? It's like, well, isn't it obvious? It's like, yeah. But what if you've got three iframes on your page? Should I create the object in that iframe? I'm like, no, that would be stupid. But you need to write this down. And also, if you access to get it twice, does it give you a new object each time? I'm like, oh, I see. And so the idea is that that stuff should be spec'd. So someone could follow this spec to the letter and return a different object every time that represents the same underlying thing. Yeah, leaving a room for interpretation is something that leads to interrupt issues, which is something we've been trying to avoid really, really hard. Exactly. But we're going to just follow what they do. The good news is the whole thing around object equivalence, we have covered in the tests. True. So even if there's room for interpretation in the text version of the spec, the tests can remove that ambiguity. Absolutely. So I'm going to, do you know what? See, I like me some copy pasting. So I'm going to sort of move this down to here. Let's go for this. So this is Bikeshed syntax. It's a definition. It's an attribute in terms of IDL speed. It's for IDB cursor. So this is saying this thing that we're defining is a child of IDB cursor. And that's how I actually know where to link. Yeah, and that's referencing that there. So get a must, return the, and so this is IDB, this is Bikeshed syntax for creating a reference. And you can reference things not just in this spec. You can reference things outside of this spec. Oh, you can see it like, I mean, I've seen it and seen it a couple of times where one spec will link to another. Yes. So this is because Bikeshed has this big database of stuff. So what I could put here returns the, I don't know, let's call it a URL. So these double curly braces means I'm referencing an interface. OK. So if I did that, and we should see this rebuilding, what's it telling us now? Multiple possible URLs, right. So what I'm going to ask it is, I'm going to put a slash there to say I want the top level URL, not the URL that belongs to something else. It might be that that still breaks. No, that seems fine. We've got multiple attribute definitions because of our copy and pasting. Which is fatal, but it's still compile. Do you know how Bikeshed is great for this sort of thing? It just gets the job done. So now we would see down here, we've got the source appearing again, must return the URL because I put that in there. And it links to the URL spec in the WG. And it links to the URL in the WG. That's handy. So this one. Not that we need it right now, but it's good to know. No, I was just, yeah, yummy. Because the change we actually need to make is super boring. It's very simple, isn't it? So the request attribute getter must return the, let's get rid of that URL, like the source request of this cursor. You want to remove the rest? Whatever. You are going to remove the rest. And now let's see that build. No fatal errors. Well done, us. No additional errors from the ones we had before. Very important to point that out. And so we can see now that our IDB requests. It's now a link. It's now a link. And it says the request attribute getter must return the request of this cursor. Request links up to the thing. That is amazing. That's it. So now we commit these two things, our tests, and our spec changes. Open the PR. Yeah, so that's what I would do. I would hop, well, let's create a branch, right? Let's call it. I know you have committed rights to master. I don't actually. Not on IndexedDB. Oh, true. This is IndexedDB. So let's call it cursor request. Excellent. Now, I like my Gitgooies. You go for it. I use my Gitgooies for everything. But can you zoom in? No, you cannot. Oh. Nope. Handy. Tell you what, I can do this. There we go. And this is why I like a Gitgooie, because it's nice and visual, showing me my changes. There's nothing crept in there that I didn't expect. It is a very small change. It's a very small change. Which is good. But I like this kind of overview in general when I'm coding, because the amount of times I've caught, like console logs, debugger statements, misspellings. It's my little code review. And I'm like, yes, I'll have that. And yes, I'll have that. I'm not going to commit the HTML. I don't know what the, well, it's telling me it's a new file, which is. That means a negative ignore or something. Or maybe it should be an ignore. So I'm going to ignore it. And I'm just going to go add a curse or dot a request. And I will push those changes. Yeah, so that's pushed. But one thing that I realized while actually editing the spec is we've got two types of cursor. There's IDBCursor. And further down, there is IDBCursor with value. I was an extended interface. As an extended interface, because you can get a cursor for just the keys of a store, and not including the value as well. And it tends to be what I do when I edit specs. Like I'll do the tests, I'll do the spec, and then I'll find something that makes me think, hang on, are my tests correct? And I'm a bit worried in this case, because when we were doing the tests, we are. Assuming it has a value. Well, no, we're doing object store. An object store is a cursor with value. That's what I meant. So we are having a cursor with value. Yes, exactly. And our spec change actually defines it on a cursor with potentially without a value. Which, well, yes, so the parent class. And that's where we want it to be. So this is the point where I go, ah, do you know what? I'm going to put a key cursor. Which is the IDB cursor, the one without a value. Exactly, exactly that. So it's like, yeah, just to make sure it's on the parent class rather than on just that one subclass. Is this something that probably would have been caught by the code review? Because these reviews will happen in separate repositories, right? Yeah, usually when you do spec work like this, you make a commit, you do that, and some people come back and go, oh, I found this edge case. Do you want to patch that up? Make sure that that can't happen. So it's not like, it's not necessarily like a fatal mistake if we would have shipped it without the open key cursor or the open cursor instead. Exactly, exactly. So just like turning the trigger up, because I would personally be very afraid of like, oh, if I don't notice this, if I'm not even good enough for web platform tests, right? Right, no, I mean, people will help, right? You go and have a look at any of my spec PRs. There's like 50 comments of discussion. I don't know if it's going to be for this one, because it's like a two line change. I would be impressed. Because we're going to, I'm literally writing this now, so people could go and have a look at the PR and see if that is what happened. So yeah, so say, or cursor, request. And yeah, so this is me committing the tests now, with my Git GUI, it's beach balling, it's fine. Yeah, web platform tests is huge. It has like 100 branches, because a lot of the browsers default upstream from their own test stuff into it. So, but I'm looking at that, that's fine. It's got the right stuff in. Yes, in this case, we do want a new file, because we created it. Yep, so cursor.request, I'm happy with that. And push that up to Git. We're done. And we are done. So now we will open PRs, and we'll wait for the feedback from the other browser vendors on it. Exactly. And so hopefully by the time this episode goes out, people are just going to have said, it's totally fine, there's no mistakes, you haven't done anything wrong. And this feature can just be shipped very easily in browsers. Amazing. Thank you. Right, let's, oh, look at that, look at that resizing being totally unhelpful. Let's tell you what, I'm not going to do that. Tab. I'm going to create another window or create, yeah, create another tab. Is that going to resize badly as well? Yeah, it is, of course it is. Why not? All right, let's go like that. So something for the edit. Something for the edit.