| 00:30 | <joyee> | I am fairly certain ESM in Node.js just isn’t optimized enough because even require(esm) was 1.2x faster than import esm when it came out 🤷♀️ |
| 15:06 | <Ryan Cavanaugh> | TCQ isn't showing a Queue for me |
| 15:06 | <Ryan Cavanaugh> | oh, because the tab is too narrow. nm |
| 15:08 | <Jesse> | I'll add this to the agenda, but here are Ben's slides for the amount continuation: https://notes.igalia.com/p/2025-09-tc39-plenary-amount-continuation#/ |
| 15:10 | <bakkot> | Ryan Cavanaugh: can you comment on https://github.com/tc39/notes/pull/382 |
| 15:10 | <bakkot> | we don't have an alias for you in the notes yet |
| 15:11 | <Ryan Cavanaugh> | Yeah I approved that earlier |
| 15:11 | <bakkot> | sorry, I mean, Rob had a comment about the choice of alias |
| 15:11 | <Ryan Cavanaugh> | oh |
| 15:12 | <Ryan Cavanaugh> | done thanks |
| 15:13 | <Ashley Claymore> | I've been wondering. Was the J a middle name? My money's on 'John'? |
| 15:14 | <Ryan Cavanaugh> | yep |
| 15:15 | <nicolo-ribaudo> | Missed opportunity for your parents to call you Ryan JavaScript Cavanaugh |
| 15:16 | <kriskowal> | I am just going to say that technicalities did not stop Kim Dot Com. |
| 15:22 | <keith_miller> | Where can I find the HTML amount proposal? |
| 15:26 | <bakkot> | "there's a unit on the end so you need to strip out the unit before converting to a string" hahaha if only our parseFloat actually worked like that |
| 15:26 | <Ryan Cavanaugh> | Making a unit called "e3" just for funsies |
| 15:27 | <Chris de Almeida> | there is no such proposal AFAIK |
| 15:31 | <keith_miller> | Oh maybe I misunderstood. Is there a an HML amount that's going to proposed? What was the context here? |
| 15:31 | <bakkot> | it was definitely good that BigInt was correctly pigeonholed by the name, people adopting it as a generic Integer would have been much much worse |
| 15:33 | <ljharb> | that's fair, but since then we got complaints about a lack of adoption, perhaps we shouldn't have added it at all |
| 15:34 | <bakkot> | Lack of adoption was because the use cases it was for didn't actually end up happening that much, not because of the name |
| 15:34 | <bakkot> | engines would not have been happier if people started using it for the things they're currently doing, that would be worse |
| 15:34 | <ljharb> | i'll agree it's not the best example here |
| 15:34 | <nicolo-ribaudo> | https://github.com/mozilla/explainers/blob/main/amount.md |
| 15:35 | <bakkot> | (that said, it is extremely useful for wasm interop so I'm still very glad we have it.) |
| 15:35 | <nicolo-ribaudo> | Right now the idea is that you hover over it, and it shows a localized popup |
| 15:35 | <nicolo-ribaudo> | Kind of like <span title=...>...</span> |
| 15:36 | <James M Snell> | that's fair, but since then we got complaints about a lack of adoption, perhaps we shouldn't have added it at all |
| 15:36 | <Ryan Cavanaugh> | It seems like you would only ever construct an Amount right before formatting a number |
| 15:36 | <nicolo-ribaudo> | No, as soon as you want to add information to that number, so that you pass them around as a single "thing" |
| 15:37 | <ljharb> | that's also fine. but browsers have blocked decimal primitives (and record and tuple primitives) because bigint didn't get enough adoption |
| 15:37 | <nicolo-ribaudo> | If it's right before, you can just pass the number to Intl as-is |
| 15:37 | <bakkot> | oh jeeze the idea of people using this for representing JSON output from other languages which can represent values not representable by double or BigInt is... not appealing to me |
| 15:38 | <Ryan Cavanaugh> | How do you write code that handles an arbitrary Amount? |
| 15:39 | <nicolo-ribaudo> | Mostly passes it around as-is, as an opaque set of information |
| 15:40 | <James M Snell> | that's also fine. but browsers have blocked decimal primitives (and record and tuple primitives) because bigint didn't get enough adoption |
| 15:41 | <Ryan Cavanaugh> | We already have a type that represents a value with an encoded number of significant digits and unit which you shouldn't do math on, it's string. Unless this can be sent natively over the wire it doesn't even help the case where you have a server "Amount" that the client should render in its own locale |
| 15:41 | <ljharb> | that type represents many other things too tho, this would basically be a structured subset of what strings can do |
| 15:42 | <Ryan Cavanaugh> | The "other things" are what though? |
| 15:42 | <James M Snell> | But even decimal and bigint serve a sufficiently different use cases from each other that even that argument is tenuous. There are more use cases for decimal than bigint. I'm far less convinced about Amount |
| 15:42 | <ljharb> | prose/poetry, bytes, anything that's not a value? |
| 15:43 | <Ryan Cavanaugh> | oh I thought "that type" referred to Amount |
| 15:45 | <ljharb> | sorry for the confusion |
| 15:51 | <nicolo-ribaudo> | Fwiw even with
\ + checking that the units match |
| 15:53 | <ljharb> | so then why bother removing it |
| 15:54 | <nicolo-ribaudo> | That message was directed to those that prefer to remove it. I'm neutral |
| 15:55 | <nicolo-ribaudo> | waldemar Do you think we could finish the 0.00&co discussion on GitHub rather than now? |
| 15:58 | <Ryan Cavanaugh> | This feels like we're trying to create a new Array type that encodes which delimiter you should use when That's not a new noun, it's just metadata attached to an existing primitivelike. Combining those into one new object isn't precedented in the language AFAICT? |
| 16:00 | <bakkot> | MDN has a page for the duck-type Iterator incidentally: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Iterator |
| 16:05 | <eemeli> | One difference cf. the given examples of protocol objects is that Amounts are more likely to persist, i.e. be stored by a user and used in potentially multiplayer places. So if we go that way, should we provide an `isAmount()` function somewhere? |
| 16:05 | <ljharb> | i'd prefer yes, but we don't have an "is iterator result" function or "is regexp match object", so there's not much precedent |
| 16:07 | <eemeli> | Are either of those commonly stored, or just immediately used? |
| 16:08 | <nicolo-ribaudo> | I think there is a difference in that those two things are just a workaround because we don't support multiple return values from functions |
| 16:08 | <nicolo-ribaudo> | If we did those two object shapes would not exist |
| 16:08 | <nicolo-ribaudo> | But we'd still have this proposal |
| 16:16 | <bakkot> | iterables, and to a lesser extent iterators, are very likely to be stored by a user and passed around |
| 16:20 | <bakkot> | one difference with iterables is that you often (usually, in fact) want other behavior too when making an iterable, e.g. you're making a Map and it just happens to also be iterable |
| 16:20 | <nicolo-ribaudo> | And users very rarely write iterators by hand, we provide utilities/factories for that |
| 16:21 | <bakkot> | this is probably less true for Amount. On the other hand, maybe it is not that rare? if you're representing some type of data, you might well want to e.g. provide arithmetic functions for that data. |
| 16:21 | <bakkot> | If this is a protocol you might want to implement on other objects, that would be a strong reason not to have a constructor. |
| 16:21 | <bakkot> | i'm guessing that is rare though |
| 16:24 | <nicolo-ribaudo> | If it was a protocol, do you think there should no built-in utility to make it more reachable? e.g. Number.prototype.toAmount(unit) = return { value: this, unit }, without it being a class |
| 16:25 | <bakkot> | return foo.toAmount('kg') is about the same as return { value: foo, unit: 'kg' } IMO, and the latter is easier to learn |
| 16:27 | <bakkot> | so, probably no. but this opinion is not strongly held; if there's cases where making it more reachable actually does significantly improve usability I'm open to that |
| 16:28 | <eemeli> | To me, `isAmount` seems like a much more important function than `toAmount`, if going the protocol route. |
| 16:28 | <bakkot> | I cannot imagine when you would want an isAmount function |
| 16:28 | <nicolo-ribaudo> | If we had this being just a protocol, the main reason to have this be very easily reachable is that it needs to be easier for people to create these objects rather than to separately pass the precision and the values to Intl formatters |
| 16:29 | <nicolo-ribaudo> | Since that's a common source of bugs |
| 16:30 | <eemeli> | I cannot imagine when you would want an |
| 16:30 | <bakkot> | why do you have a string value of { value } whose provenance is not known to you? |
| 16:43 | <eemeli> | why do you have a string value of |
| 16:44 | <bakkot> | in my experience interchange protocols are always either tagged with types or have out-of-band schemas |
| 16:45 | <bakkot> | you're not just taking an arbitrary collection of values and trying to interpret them |
| 16:45 | <bakkot> | that doesn't really work |
| 16:58 | <bakkot> | ljharb: as far as I am aware you are literally the only person in the world who is of the opinion that the point of having a class in general is that other things can't pretend to be that thing |
| 16:58 | <bakkot> | that is a valid opinion but other people aren't generally going to assume that reason without you making the case for it |
| 16:58 | <ljharb> | i mean, everything with internal slots and everything with private fields qualifies |
| 16:59 | <ljharb> | that's how every builtin in this language works, with a very short list of exceptions |
| 16:59 | <bakkot> | there is a massive distinction between "that is how it works" and "that is the point of it" |
| 17:01 | <ljharb> | fair enough |
| 17:01 | <ljharb> | that's something that comes with "having a class", regardless of hte point |
| 17:01 | <ljharb> | if we're not using internal slots at all then there's no point having anything but a plain object, like an iterator result |
| 17:03 | <bakkot> | the Iterator class doesn't use internal slots at all and is nevertheless extremely useful |
| 17:06 | <ljharb> | fair, it's IteratorHelper that has the internal slots, but iterator's a bit of a special case in lots of ways |
| 17:09 | <ljharb> | notably we don't have an IteratorResult class tho, and if there's no internal slots or helper methods, then Amount seems more like that than a stateful iterator |
| 18:04 | <bakkot> | ok JS developers do often have lots of dependencies but hundreds of thousands of dependencies is probably not actually a thing |
| 18:08 | <Chris de Almeida> | perhaps worth noting, many (most?) are transitive and unshipped |
| 18:08 | <bakkot> | transitive I buy, unshipped I don't buy |
| 18:08 | <bakkot> | the static analysis developers are using is, unfortunately, still terrible |
| 18:09 | <Chris de Almeida> | one of our applications, out of thousands of deps, only like 30 have code that actually end up in the shipped bundle |
| 18:09 | <bakkot> | I'm assuming we're only talking about actual deps, not dev deps |
| 18:09 | <bakkot> | certainly most dev deps do not end up contributing shipping code, with exception of like babel or webpack |
| 18:13 | <Chris de Almeida> | yes, the actual deps. esp when using batteries-included libraries/frameworks, a lot of unused stuff gets shaken out |
| 18:14 | <bakkot> | huh, that is very much not my experience |
| 18:15 | <keith_miller> | Even if it's only on the order of hundreds, it still seems like an intractable problem to vet all of them for transitive problems. |
| 18:15 | <kriskowal> | That was certainly the least opportune moment for Google to insist I reauthenticate. |
| 18:15 | <Chris de Almeida> | bold of you to authenticate in the first place |
| 18:17 | <keith_miller> | The usefulness of Compartments also seems to depend on frozen globals. Does anyone deploy that at scale with lots of dependencies? |
| 18:18 | <Rob Palmer> | I thought V8 had a bunch of optimizations for proxies. Would love to hear for sure if this include JITing across the boundary. |
| 18:21 | <rekmarks> | The dependencies can still collude to e.g. exfiltrate data, but preventing single dependencies from exceeding their authority eliminates an entire class of attacks, e.g. the event-stream incident. Compromising multiple dependencies and shepherding them to conduct an attack is a higher bar that makes a practical difference for the security of applications with many dependencies (i.e. the common case). |
| 18:25 | <mgaudet> | Yeah -- I appreciate Kris's comment -- particularly the "unknown" aspect; |
| 18:26 | <keith_miller> | I guess I'd propose an alternative question. The approach that's becoming increasingly common on the web is that the core web process is assumed to be compromised. Instead privileged things are isolated from the core web process and are hardened much more aggressively. Why is that insufficient or intractable here? |
| 18:27 | <rekmarks> | What's the "core web process"? |
| 18:27 | <keith_miller> | The rendering process (JS/DOM) |
| 18:28 | <bakkot> | because the bank login runs in the core web process |
| 18:28 | <bakkot> | inherently |
| 18:28 | <Olivier Flückiger> | or even just JS, V8 has a heap sandbox now within the renderer... |
| 18:28 | <keith_miller> | I'm saying the same approach in JS. Not at the C++ layer |
| 18:29 | <bakkot> | I kind of doubt that you can feasibly move "access to the DOM" out of the root JS process |
| 18:30 | <keith_miller> | That's an analogy not what I'm actually proposing for sites. Although one could do it by origin isolating them. |
| 18:31 | <mgaudet> | This is just a small factoid, but hints at some of my personal worries: At least in SpiderMonkey we already have "oops wrong realm" objects that we're aware of, but have never fixed because it's been too low in our priority list (usually errors thrown that are from the wrong realm). I say this not yet really having made the conceptual leap from the previous module-global propsal to the current more compartment like one |
| 18:32 | <bakkot> | I know at least some engines had some inconsistencies about the realms of iterator result objects at some point, possibly also the realms of promises for certain kinds of async functions |
| 18:33 | <bakkot> | which, yes, I have usually avoided adding tests for these because I don't think we should usually care |
| 18:33 | <mgaudet> | (And to colour in the rest of my allusion -- if we make mistakes around this, it turns into compartment violation security bug) |
| 18:34 | <bakkot> | the current proposal does not involve having separate versions of Error or Promise or anything, so I don't think it ends up really mattering directly |
| 18:34 | <bakkot> | it's only Function (and friends) specifically which are different. not even their prototypes, just the ctor itself. |
| 18:34 | <bakkot> | the reason this is thought to be OK is because it is assumed that all the intrinsics are frozen (by something) |
| 18:35 | <bakkot> | and also that any powerful intrinsics have not been provided to the compartment |
| 18:35 | <mgaudet> | That's nice at least. I'll be excited to read the updated readme so I can try to piece it together |
| 18:35 | <Ashley Claymore> | Yeah. Language level values like Iterators and RangeError don't change |
| 18:37 | <Ashley Claymore> | Compartments: with {} done right |
| 18:43 | <bakkot> | I should say part of my previous topic is that wasm is increasingly moving in the direction of exposing information about modules to runtime: https://github.com/WebAssembly/js-types/blob/main/proposals/js-types/Overview.md |
| 18:43 | <bakkot> | and this is good and allows you to do lots of useful things |
| 18:48 | <kriskowal> | We are very much in favor of also exposing more information on ModuleSource instances, like all the externally visible bindings. |
| 18:49 | <kriskowal> | Like, writing a bundler that uses ModuleSource to gather the static transitive dependencies of a module graph would greatly benefit from that analysis. Last updated 3 years ago https://github.com/tc39/proposal-compartments/blob/master/1-static-analysis.md |
| 18:50 | <bakkot> | re: LLMs writing attacks: there's also the "just have an LLM do the attack at runtime" approach https://www.anthropic.com/news/detecting-countering-misuse-aug-2025#vibe-hacking-how-cybercriminals-used-claude-code-to-scale-a-data-extortion-operation |
| 18:54 | <Chris de Almeida> | link to "w3c amount" ? |
| 18:55 | <keith_miller> | You mean https://github.com/mozilla/explainers/blob/main/amount.md? |
| 18:56 | <Chris de Almeida> | looks like it |
| 19:03 | <Mathieu Hofman> | I think this is really the crux of it. The way I see it, it's mostly adding a "terminating scope" before evaluating code (including modules). We already have all the logic around to evaluate code in an existing scope stack. |
| 19:04 | <Bradford Smith> | bakkot: in my personal notes from the very end of the previous Amount continuation I have "KG: In order to maintain invariants, it will be necessary to use internal slots." Did I misunderstand you there? Isn't that a clear indicator that a class is needed? |
| 19:05 | <bakkot> | That is a claim WH made, not a position I advocate |
| 19:05 | <bakkot> | it is not clear to me which invariants exist or whether consumers actually need to be able to rely on them; my inclination would be to answer the latter question in the negative |
| 19:06 | <ljharb> | i am very skeptical that this is good; it can make things about the way you wrote the code be part of your observable API :-/ |
| 19:06 | <bakkot> | the exports of code is always part of its observable API |
| 19:07 | <ljharb> | oh sure, exposing metadata about existing observable things is great! |
| 19:07 | <bakkot> | the imports less directly so but they're still pretty observable in practice |
| 19:07 | <ljharb> | i'm not super clear on how one would do that (from code, obv you can do it from dev tools etc) |
| 19:07 | <bakkot> | and with Compartments as proposed imports would be very observable anyway because there is an importHook |
| 19:08 | <ljharb> | that is true, and that is a concern i have for that part of the proposal |
| 19:10 | <kriskowal> | There are some funny things about ESM that bindings reflection does need to hide. You can’t just ship the import and export statements, because they entrain too many implementation details. We’ve discussed this pretty hard at Module Harmony. |
| 19:11 | <kriskowal> | It also has the downside that you cannot trivially create a proxy for another module by copying all the bindings from a module source to the virtual module source. But, that’s just a reality we have to live with. |
| 19:12 | <kriskowal> | (Or not pursue virtual modules sources at all, to be clear, but they’d be useful for CJS migration, for one.) |
| 19:12 | <ljharb> | couldn't that be enabled without actually exposing the list? like, provide an opaque identifier that just represents the list |
| 19:14 | <waldemar> | An Amount is not like a tuple. When receiving an Amount it would be problematic if the various fields such as digit counts and stored value were inconsistent with each other. So you don't want to do duck typing. There are various other ways to ensure the invariants. |
| 21:09 | <Chris de Almeida> | the agenda for the next meeting is available and ready for your topics to be added! 😀 https://github.com/tc39/agendas/blob/main/2025/11.md we currently have ~15 hours of time available |