profile
viewpoint

bmeck/callbackhell 9

utility for callback hell

bmeck/babel-analysis 8

SSA/CFG for babylon

bmeck/auther 5

node.js service for logins and api tokens

bmeck/atxjs-offline 3

Place for Austin Javascript Offline Application Example

bmeck/clip 3

Express meets the CLI

bmeck/--- 2

A repository that doesn't like search engines.

bmeck/candor.io 2

candor.io is a server framework that uses the candor language combined with libuv to make fast scripted network servers.

bmeck/cejs 2

cejs (pronounced Sieges) is a simple ejs compiler

push eventbmeck/ecma262

Bradley Farias

commit sha 272cf6b33d1e2be49fa1d71f773bc1ab65c40a04

lint

view details

push time in 9 hours

push eventbmeck/ecma262

Bradley Farias

commit sha cf251d2ac1ff6fac786f4e10aab1368e6bc27861

abbreviated forms

view details

push time in 10 hours

PullRequestReviewEvent

issue commentnodejs/node

Proposal: mark AsyncResource and AsyncLocalStorage as stable

It needs to be until the end of the tick though because the point is that we want to capture everything in that tick. An APM product wants to trace an entire request, not just part of it. At the start of the tick which runs the request handler until the end of it, we want to know everything that happens within that tick and within every async task descending from that tick so we can attribute that activity to the request that spawned it. If, for example, running the request handler was followed by running a setTimeout to timeout the request if it's not finished soon enough, we want to see that. We need our context object, which is probably our tracer, to be propagated into that so we have some way of finding the current trace within the descending async execution. I created enterWith() specifically because there are a few scenarios where we need to capture until the end of the tick. I was quite explicit in the documentation about warning exactly what this means. The suggested API for 90% of users is definitely the run() method, enterWith() is a special-case API.

As stated in my comment above, this is an assumption that the tick is only related to a single thing. This is not something we can specify to people creating code clearly without a delineation mechanism; otherwise, we must state that per a given tick, only 1 thing can be safely related. The problem that I keep bringing up is not that you want to see a setTimeout related to a request but per my example, a setTimeout that is not related to the thing, but queued on the same tick, hence the naming in my example code. I understand that the documentation explains the contract to people using .enterWith but it does not provide means for people being observed by these mechanisms to delineate when things become unrelated.

If we are to state that assumption, we could try a tweak such that multiple .enterWith on the same tick causes a thrown error to prevent that? I personally think the API is still a problem, but at the minimum that would cause the creator of the .enterWith code to see that something is outside of the expectations of the .enterWith API.

Also, yes, we actually do have a guarantee that each tick has a single async resource, which is even exposed through the async_hooks.executionAsyncResource() API, which is exactly what AsyncLocalStorage is built on top of. Without that guarantee, async_hooks wouldn't even function properly, because it attributes the before/after around a tick to a given async resource by its id.

We have a single "incumbent" AsyncResource in WHATWG terms, but my concern is about the less concrete idea of simpler async resources (tasks?) / user land things created by your current tick. Perhaps we can adopt terminology to more clearly differentiate things.


If desirable we can setup a call next week sometime to directly discuss these 2 APIs and perhaps get a better understanding as I feel we are currently talking past each other. Such a discussion could be intended as a discussion about needs to get the API's solutions to problems out of experimental not necessarily about removal or persistence of the exact existing API.

Qard

comment created time in 4 days

issue commentnodejs/node

Proposal: mark AsyncResource and AsyncLocalStorage as stable

@Qard that makes the assumption that there is a contract that all calls grouped within the same tick are co-related, this is not true in the language and not clearly delineated in the code. I am asking that we have clear delineation, not coincidental relation. If we can somehow delineate clearly that we are exiting the relation I think having implicit / dynamic changing of context is acceptable as a compromise, TC39 disagreed due to dynamic scoping concerns. My compromise is that we have a clear delineation not that we prevent dynamic scoping and is based upon examples of having same tick but unrelated events. It would be a nigh impossible burden to ensure that every tick does not have multiple async resources that collide given the current APIs to my knowledge. I am only not requesting removal and didn't prevent landing because it is experimental and I hope over time we can tweak the API for clear delineations. I understand the APIs have the side effects you want for some scenarios, but the API contract is not clear nor reliable given my example above. Your assumption that all calls in the same tick needs more proof and to address counter concerns of having multiple unrelated things in the same tick. Clear delineation would satisfy my concerns even if it doesn't prevent code from violating the implicit and unobservable contract from existing. I am trying to be reasonable but I still am unclear on the WHY of the API design given counter examples. Stating that the API is working as intended isn't really a way for me to better understand why my counter examples are out of scope. Stating that my counter example doesn't fit the APIs in question's expected implicit contract raises more concern, not lessens it.

Qard

comment created time in 4 days

issue commentnodejs/node

Proposal: mark AsyncResource and AsyncLocalStorage as stable

Anyway, my point is, the ability to change the context partway through a tick is an important feature for APMs in many places.

I agree to this and do think we should have some way to solve it, but not to the points in my comments being non-trivial.

@bmeck Do you have some proposal how an API should look like to reach the requirement described by @Qard to attach to an existing resource for it's lifetime?

I have no real demands on the actual signature but it shouldn't be the end of the current tick that determines when it is changed. An API that matches the following would work fine to me:

Something that persists until a synchronous termination before the end of the tick. APIs like eventEmitter.emit('request') are a good example of something that the assignment should stop at the end of. I am not opposed to it escaping the local frame event.

Right now, the API to me bleeds past the intended synchronous call frame that it was hoping to wrap and I still don't see an explanation of WHY going past that call frame is intended and necessary, just explanations of that it does and it is intended. I am familiar with these APIs and async queueing, I don't think everyone needs to establish their level of familiarity here. TC39 also has had presentations on these issues which I have been a part of and I have had plenty of discussions on why AsyncLocalStorage style APIs are rejected there, most recently with Async Contexts. I am not trying to be as absolute as I would be in TC39 and do want to find a middle ground, but find that subset of the current APIs to be non-starters without clarification and exhaustive production usage from many sources showing no issues (either with development or runtime).

Qard

comment created time in 6 days

issue commentnodejs/node

Proposal: mark AsyncResource and AsyncLocalStorage as stable

@Qard It is unclear to me what you are talking about at this point, you are describing the API and not the problem it is trying to solve. I'm asking to understand why the API isn't considered to be leaking given that it doesn't appear to be wrapping the event but rather the event plus a long tail until the sync stack of anything unwinds. Repeating that you have achieve an intended goal is well and good, but it doesn't explain what the goal was, which I understand to be setting the store value to be something during the event so that it can be persisted. A more concrete example rephrasing my above comment:

var { AsyncLocalStorage } = require('async_hooks');
var { EventEmitter } = require('events');
var als = new AsyncLocalStorage();
var ee = new EventEmitter();
var incr = ((i = 1) => () => i++)();
ee.on('my-event', () => {
  var value = incr();
  console.log('pre-entering: %s previous: %s', value, als.getStore());
  als.enterWith(value);
});
ee.on('my-event', () => {
  console.log('post-entering: %s', als.getStore());
});
function run() {
  console.log('run');
  ee.emit('my-event');
  setTimeout(() => {
    console.log('unrelated timeout got: %s', als.getStore());
  }, 0);
}
run();
run();

You can see old values crossing into "previous:" and the timeout getting values even if it is outside the event. It would be good to understand why this is not problematic. I remain uncomfortable with those 2 APIs given examples like this one.

Qard

comment created time in 6 days

issue commentnodejs/node

Proposal: mark AsyncResource and AsyncLocalStorage as stable

@qard it goes beyond the events and outside of the emit. I do not dispute the documentation, just that the API is a leaking abstraction that goes beyond the goal of wrapping a specific time frame and instead wraps past that time frame. It would be good to understand why having the store set to something AFTER the emit() in my example completes is intended and needed. I can understand why it needs to go after the first listener, that is understood. Per my comment above, the current API can generate stale references.

Qard

comment created time in 6 days

issue commentnodejs/modules

Clarification for bundlers re "import" / "require" matching

I'd be fine removing the wording on it being exhaustive entirely.

guybedford

comment created time in 6 days

issue commentnodejs/node

Proposal: mark AsyncResource and AsyncLocalStorage as stable

@Qard I understand that is the design of the API, but disagree on the intent of the API being to be the API. The API is to solve the problem of wrapping around these contextual event spans. My example above is showing that the API is leaking outside of those spans. I understand how you can achieve some things with the APIs but I am not sold on the leakage being non-trivial. The problem that is attempting to be solved by those APIs seems to differ from the affects of the APIs.

Qard

comment created time in 6 days

issue commentnodejs/node

Proposal: mark AsyncResource and AsyncLocalStorage as stable

@Qard it is more complicated than that, consider:

const als = new AsyncLocalStorage();
emitter.on('my-event', () => {
  als.enterWith(store);
});
emitter.on('my-event', () => {
  als.getStore();
});
ee.emit('my-event');

The problem with these bleed through APIs unlike .run is you don't have fine delineation.

ee.prependListener('my-event', () => {
  als.getStore(); // gets the store BEFORE enterWith
});

Will escape this kind of implicit timing based side effect.

ee.emit('my-event');
// AFTER
als.getStore(); // still has the store meant to span 'my-event'

Will keep the store after the span it sought to encompass.

If you combine the 2, you can get stale store values. I can imagine plenty more scenarios where this is true, but its pretty simple to see. I'm not against solving the problem of needing to span across calls, I am only uncomfortable with the current APIs.


Per your example with .run that same sharing has a problem with .enterWith but is not the major concern of mine. I'd agree that sharing is dangerous regardless but other issues around accidental sharing as my example in this comment are the real worry.

Qard

comment created time in 7 days

push eventreadnow/reading-tutor

push time in 7 days

issue commentnodejs/node

Proposal: mark AsyncResource and AsyncLocalStorage as stable

@Qard I do not agree with that last comment, hence me not wanting to move those methods out of experimental. We can have this discussion here if necessary but it likely would be more useful to have it elsewhere. Perhaps an explanation of why that specific API design which does have non-local affects is necessary would be more helpful. I'm still unclear on why APM is unable to use .run. I will repeat that I am extremely uncomfortable with those APIs and having convenience in usage is not my priority in allowing those to move forward to stable. The rest of the APIs seem fine to me.

Qard

comment created time in 7 days

issue commentnodejs/modules

Document nodejs: scheme

@Jamesernator that isn't stable and actually has been a discussion on if it should be node: since that is what policies use. Whenever we converge it will be documented

Jamesernator

comment created time in 7 days

issue commentnodejs/node

Proposal: mark AsyncResource and AsyncLocalStorage as stable

@Qard

Sharing a context object with other modules or other peoples code in general is a bad idea and not encouraged.

Agreed, but the concerns of sharing it with only .run are much different from those 2 methods, hence me not wanting to move those methods out of experimental. The .run method cannot alter non-locally. We can wait on more usage before coming back to those.

Qard

comment created time in 7 days

issue commentnodejs/node

Proposal: mark AsyncResource and AsyncLocalStorage as stable

@qard The problem is just like how domain allows non-local and non-scoped manipulation of context in non-async boundaries. Your argument that only a "owner" will manipulate assumes that no-shared async storage would require that consumers have a specific coding style (in particular, not exposing a direct reference to an AsyncLocalStorage instance) which I don't find compelling. The docs show a local storage that is shared across multiple locations for example.

Qard

comment created time in 7 days

issue commentnodejs/node

Proposal: mark AsyncResource and AsyncLocalStorage as stable

I'm extremely not comfortable with enterWith or exit being considered non-experimental since they are an action at a distance, but the rest seems possible to me at least. If concrete and necessary use cases in the wild exist for those methods beyond being easier to consume than run I'd be open to conversation about why they are needed but I'm still not seeing wide adoption that forces them to exist. Those APIs are within the realm of what caused problems with domains in the post mortem and I'd be very hard pressed to understand why they are not the same danger.

Qard

comment created time in 7 days

Pull request review commentnodejs/node

lib: create diagnostics_channel module

+'use strict';++const {+  ArrayPrototypeIndexOf,+  ArrayPrototypePush,+  ArrayPrototypeShift,+  ArrayPrototypeSplice,+  ObjectCreate,+  ObjectSetPrototypeOf,+  Map,+  MapPrototypeDelete,+  MapPrototypeGet,+  MapPrototypeSet,+  Promise,+  PromiseResolve,+  Symbol,+  SymbolAsyncIterator+} = primordials;++const {+  codes: {+    ERR_INVALID_ARG_TYPE,+  }+} = require('internal/errors');++const { triggerUncaughtException } = internalBinding('errors');++const { WeakReference } = internalBinding('util');++function identity(v) {+  return v;+}++class AsyncIterableChannel {+  constructor(channel) {+    this.channel = channel;+    this.events = [];+    this.waiting = [];++    this.subscriber = (message) => {+      const resolve = ArrayPrototypeShift(this.waiting);+      if (resolve) {+        return resolve({+          value: message,+          done: false+        });+      }++      ArrayPrototypePush(this.events, message);+    };++    channel.subscribe(this.subscriber);+  }++  [SymbolAsyncIterator]() {+    return this;+  }++  return() {+    const data = { done: true };+    this.done = true;++    this.channel.unsubscribe(this.subscriber);++    for (let i = 0; i < this.waiting.length; i++) {+      const resolve = this.waiting[i];+      resolve(data);+    }++    return PromiseResolve(data);+  }++  next() {+    const event = ArrayPrototypeShift(this.events);+    if (event) {+      return PromiseResolve({+        value: event,+        done: false+      });+    }++    if (this.done) {+      return PromiseResolve({+        done: true+      });+    }++    return new Promise((resolve) => {+      ArrayPrototypePush(this.waiting, resolve);+    });+  }+}++class SpanMessage {+  constructor(action, id, data) {+    this.action = action;+    this.id = id;+    this.data = data;+  }++  // Augment with type: span to identify over the wire through JSON+  toJSON() {+    return {+      type: 'span',+      ...this

should type: be after the spread so that we ensure it has type: 'span' rather than a different value if somehow the SpanMessage is given a .type property?

Qard

comment created time in 8 days

PullRequestReviewEvent

Pull request review commentnodejs/node

lib: create diagnostics_channel module

+'use strict';++const {+  ArrayPrototypeIndexOf,+  ArrayPrototypePush,+  ArrayPrototypeShift,+  ArrayPrototypeSplice,+  ObjectCreate,+  ObjectSetPrototypeOf,+  Map,+  MapPrototypeDelete,+  MapPrototypeGet,+  MapPrototypeSet,+  Promise,+  PromiseResolve,+  Symbol,+  SymbolAsyncIterator+} = primordials;++const {+  codes: {+    ERR_INVALID_ARG_TYPE,+  }+} = require('internal/errors');++const { triggerUncaughtException } = internalBinding('errors');++const { WeakReference } = internalBinding('util');++function identity(v) {+  return v;+}++class AsyncIterableChannel {+  constructor(channel) {+    this.channel = channel;+    this.events = [];+    this.waiting = [];++    this.subscriber = (message) => {+      const resolve = ArrayPrototypeShift(this.waiting);+      if (resolve) {+        return resolve({+          value: message,+          done: false+        });+      }++      ArrayPrototypePush(this.events, message);

is there any reason someone might replace the .events property on this?

Qard

comment created time in 8 days

PullRequestReviewEvent

push eventreadnow/reading-tutor

Bradley Farias

commit sha df56f1e5364fb60498f9cc60480097dc760a825a

init

view details

push time in 8 days

issue commentnodejs/node

investigate failing permutation in test-policy-integrity

@trott this is the file file:///home/iojs/node-tmp/package.json somehow being put outside the test tmpdir, probably by a different test? Do we know if that file was created recently?

Trott

comment created time in 8 days

issue commentnodejs/node

requires - require modules securely (restricted access to fs,net, etc)

We could just mark this as known / blocked

Z3TA

comment created time in 8 days

issue commentnodejs/node

requires - require modules securely (restricted access to fs,net, etc)

I think the ability to "require"/"import" is solved, but this thread is about increased granularity for each reference, of which I don't have a solution or even a plan on how to tackle. The issues with the granularity described are laid out in particular in the model writeup I did a few years ago. There is simply too much to do such as preventing side channel leakage and dealing with Node's mutable core before we can get to that point for me to state the concerns in the thread are able to be addressed currently.

Z3TA

comment created time in 8 days

PullRequestReviewEvent

push eventtc39/agendas

Bradley Farias

commit sha 02a6caaf96582058e8b2950514a817d89fbd599e

Update 09.md

view details

push time in 18 days

pull request commenttc39/ecma262

Normative: Arbitrary module namespace identifier names

Updated, now depends on #2155

bmeck

comment created time in 18 days

push eventbmeck/ecma262

Bradley Farias

commit sha 767c72c6de706a9ac62c84dd8a630a23b99c7a97

fixup

view details

push time in 18 days

PullRequestReviewEvent

pull request commentnodejs/node

test: revise test-policy-integrity

So I actually went about deleting due to memory and pushing the limit up to try and speed things up as I was originally trying to get ARM to be happy, these seem fine.

Trott

comment created time in 19 days

pull request commentnodejs/node

fs: loosen validation to allow objects with an own toString function

@ljharb confirmed, just looks like docs don't state this special case

ljharb

comment created time in 20 days

pull request commentnodejs/node

fs: loosen validation to allow objects with an own toString function

I'd be +1 with that change and a blurb in the docs about this special toString case

ljharb

comment created time in 20 days

PullRequestReviewEvent

pull request commentnodejs/node

fs: loosen validation to allow objects with an own toString function

this change means that supplying a Buffer is no longer guaranteed to have a simple path if I understand this? @ljharb do you know if the fix needs to run on Buffer/TypedArrayss or just random objects? I'd prefer we not invoke a toString on the special cased types.

ljharb

comment created time in 20 days

Pull request review commentnodejs/node

lib: create diagnostics_channel module

+'use strict';++const {+  ArrayPrototypeIncludes,+  ArrayPrototypePush,+  ArrayPrototypeSplice,+  ObjectCreate,+  ObjectSetPrototypeOf,+  Promise,+  PromiseResolve,+  RegExp,+  Symbol+} = primordials;++const {+  codes: {+    ERR_INVALID_ARG_TYPE,+  }+} = require('internal/errors');++const { triggerUncaughtException } = internalBinding('errors');++const { WeakReference } = internalBinding('util');++function alwaysMatch() {+  return true;+}++function strictEqualMatch(string) {+  return function(value) {+    return value === string;+  };+}++function regexpMatch(regexp) {+  return function(value) {+    return regexp.test(value);+  };+}++function makeSubscriberFor(matcher, onMessage) {+  if (typeof onMessage !== 'function' && typeof matcher !== 'function') {+    throw new ERR_INVALID_ARG_TYPE('onMessage', ['function'], onMessage);+  }++  switch (typeof matcher) {+    case 'function':+      if (typeof onMessage === 'function') {+        return [matcher, onMessage];+      } else if (typeof onMessage === 'undefined') {+        return [alwaysMatch, matcher];+      }+      break;+    case 'string':+    case 'symbol':+      return [strictEqualMatch(matcher), onMessage];+    case 'object':+      if (matcher instanceof RegExp) {+        return [regexpMatch(matcher), onMessage];+      }+      break;+  }++  throw new ERR_INVALID_ARG_TYPE('matcher',+                                 ['string', 'symbol', 'function', 'RegExp'],+                                 matcher);+}++class AsyncIterableChannel {+  constructor(channel) {+    this.channel = channel;+    this.events = [];+    this.waiting = [];++    this.subscriber = (message) => {+      const resolve = this.waiting.shift();+      if (resolve) {+        return resolve({+          value: message,+          done: false+        });+      }++      this.events.push(message);+    };++    channel.subscribe(this.subscriber);+  }++  [Symbol.asyncIterator]() {+    return this;+  }++  return() {+    const data = { done: true };+    this.done = true;++    this.channel.unsubscribe(this.subscriber);++    for (let i = 0; i < this.waiting.length; i++) {+      const resolve = this.waiting[i];+      resolve(data);+    }++    return PromiseResolve(data);+  }++  next() {+    const event = this.events.shift();+    if (event) {+      return PromiseResolve({+        value: event,+        done: false+      });+    }++    if (this.done) {+      return PromiseResolve({+        done: true+      });+    }++    return new Promise((resolve) => {+      this.waiting.push(resolve);+    });+  }+}++// TODO(qard): should there be a C++ channel interface?+class ActiveChannel {+  [Symbol.asyncIterator]() {+    return new AsyncIterableChannel(this);+  }++  subscribe(subscription) {+    ArrayPrototypePush(this._subscribers, subscription);+  }++  unsubscribe(subscription) {+    const index = this._subscribers.indexOf(subscription);+    if (index >= 0) {+      ArrayPrototypeSplice(this._subscribers, index, 1);++      // When there are no more active subscribers, restore to fast prototype.+      if (!this._subscribers.length) {+        // eslint-disable-next-line no-use-before-define+        ObjectSetPrototypeOf(this, Channel.prototype);+      }+    }+  }++  shouldPublish() {+    return true;+  }++  publish(data) {+    for (var i = 0; i < this._subscribers.length; i++) {+      try {+        const onMessage = this._subscribers[i];+        onMessage(data, this.name);+      } catch (err) {+        process.nextTick(() => {+          triggerUncaughtException(err, false);+        });+      }+    }+  }+}++class Channel {+  constructor(name) {+    this._subscribers = undefined;+    this.name = name;+  }++  [Symbol.asyncIterator]() {+    return new AsyncIterableChannel(this);+  }++  subscribe(subscription) {+    ObjectSetPrototypeOf(this, ActiveChannel.prototype);+    this._subscribers = [];+    this.subscribe(subscription);+  }++  shouldPublish() {+    return false;+  }++  publish() {}+}++class DiagnosticsChannel {+  constructor() {+    this._channels = ObjectCreate(null);+    this._subscribers = [];+  }++  channel(name) {+    let channel;+    const ref = this._channels[name];+    if (ref) channel = ref.get();+    if (channel) return channel;++    if (typeof name !== 'string' && typeof name !== 'symbol') {+      throw new ERR_INVALID_ARG_TYPE('channel', ['string', 'symbol'], name);+    }++    channel = new Channel(name);++    // New channels should check existing subscribers+    for (let i = 0; i < this._subscribers.length; i++) {+      const [matcher, onMessage] = this._subscribers[i];+      if (matcher(channel.name)) {+        channel.subscribe(onMessage);+      }+    }++    this._channels[name] = new WeakReference(channel);+    return channel;+  }++  subscribe(matcher, onMessage) {+    const subscriber = makeSubscriberFor(matcher, onMessage);++    if (!ArrayPrototypeIncludes(this._subscribers, subscriber)) {+      ArrayPrototypePush(this._subscribers, subscriber);++      // New subscribers should check existing channels+      for (const key in this._channels) {+        const channel = this._channels[key].get();+        if (!channel) {+          delete this._channels[key];+          continue;+        }++        const [matcher, onMessage] = subscriber;+        if (matcher(channel.name)) {+          channel.subscribe(onMessage);+        }+      }+    }++    return () => {+      const index = this._subscribers.indexOf(subscriber);+      if (index >= 0) {+        ArrayPrototypeSplice(this._subscribers, index, 1);+      }++      for (const key in this._channels) {+        const channel = this._channels[key].get();+        if (!channel) {+          delete this._channels[key];+          continue;+        }++        const [matcher, onMessage] = subscriber;
        const {0: matcher, 1: onMessage} = subscriber;
Qard

comment created time in 20 days

PullRequestReviewEvent

Pull request review commentnodejs/node

lib: create diagnostics_channel module

+'use strict';++const {+  ArrayPrototypeIncludes,+  ArrayPrototypePush,+  ArrayPrototypeSplice,+  ObjectCreate,+  ObjectSetPrototypeOf,+  Promise,+  PromiseResolve,+  RegExp,+  Symbol+} = primordials;++const {+  codes: {+    ERR_INVALID_ARG_TYPE,+  }+} = require('internal/errors');++const { triggerUncaughtException } = internalBinding('errors');++const { WeakReference } = internalBinding('util');++function alwaysMatch() {+  return true;+}++function strictEqualMatch(string) {+  return function(value) {+    return value === string;+  };+}++function regexpMatch(regexp) {+  return function(value) {+    return regexp.test(value);+  };+}++function makeSubscriberFor(matcher, onMessage) {+  if (typeof onMessage !== 'function' && typeof matcher !== 'function') {+    throw new ERR_INVALID_ARG_TYPE('onMessage', ['function'], onMessage);+  }++  switch (typeof matcher) {+    case 'function':+      if (typeof onMessage === 'function') {+        return [matcher, onMessage];+      } else if (typeof onMessage === 'undefined') {+        return [alwaysMatch, matcher];+      }+      break;+    case 'string':+    case 'symbol':+      return [strictEqualMatch(matcher), onMessage];+    case 'object':+      if (matcher instanceof RegExp) {+        return [regexpMatch(matcher), onMessage];+      }+      break;+  }++  throw new ERR_INVALID_ARG_TYPE('matcher',+                                 ['string', 'symbol', 'function', 'RegExp'],+                                 matcher);+}++class AsyncIterableChannel {+  constructor(channel) {+    this.channel = channel;+    this.events = [];+    this.waiting = [];++    this.subscriber = (message) => {+      const resolve = this.waiting.shift();+      if (resolve) {+        return resolve({+          value: message,+          done: false+        });+      }++      this.events.push(message);+    };++    channel.subscribe(this.subscriber);+  }++  [Symbol.asyncIterator]() {+    return this;+  }++  return() {+    const data = { done: true };+    this.done = true;++    this.channel.unsubscribe(this.subscriber);++    for (let i = 0; i < this.waiting.length; i++) {+      const resolve = this.waiting[i];+      resolve(data);+    }++    return PromiseResolve(data);+  }++  next() {+    const event = this.events.shift();+    if (event) {+      return PromiseResolve({+        value: event,+        done: false+      });+    }++    if (this.done) {+      return PromiseResolve({+        done: true+      });+    }++    return new Promise((resolve) => {+      this.waiting.push(resolve);+    });+  }+}++// TODO(qard): should there be a C++ channel interface?+class ActiveChannel {+  [Symbol.asyncIterator]() {+    return new AsyncIterableChannel(this);+  }++  subscribe(subscription) {+    ArrayPrototypePush(this._subscribers, subscription);+  }++  unsubscribe(subscription) {+    const index = this._subscribers.indexOf(subscription);+    if (index >= 0) {+      ArrayPrototypeSplice(this._subscribers, index, 1);++      // When there are no more active subscribers, restore to fast prototype.+      if (!this._subscribers.length) {+        // eslint-disable-next-line no-use-before-define+        ObjectSetPrototypeOf(this, Channel.prototype);+      }+    }+  }++  shouldPublish() {+    return true;+  }++  publish(data) {+    for (var i = 0; i < this._subscribers.length; i++) {+      try {+        const onMessage = this._subscribers[i];+        onMessage(data, this.name);+      } catch (err) {+        process.nextTick(() => {+          triggerUncaughtException(err, false);+        });+      }+    }+  }+}++class Channel {+  constructor(name) {+    this._subscribers = undefined;+    this.name = name;+  }++  [Symbol.asyncIterator]() {+    return new AsyncIterableChannel(this);+  }++  subscribe(subscription) {+    ObjectSetPrototypeOf(this, ActiveChannel.prototype);+    this._subscribers = [];+    this.subscribe(subscription);+  }++  shouldPublish() {+    return false;+  }++  publish() {}+}++class DiagnosticsChannel {+  constructor() {+    this._channels = ObjectCreate(null);+    this._subscribers = [];+  }++  channel(name) {+    let channel;+    const ref = this._channels[name];+    if (ref) channel = ref.get();+    if (channel) return channel;++    if (typeof name !== 'string' && typeof name !== 'symbol') {+      throw new ERR_INVALID_ARG_TYPE('channel', ['string', 'symbol'], name);+    }++    channel = new Channel(name);++    // New channels should check existing subscribers+    for (let i = 0; i < this._subscribers.length; i++) {+      const [matcher, onMessage] = this._subscribers[i];+      if (matcher(channel.name)) {+        channel.subscribe(onMessage);+      }+    }++    this._channels[name] = new WeakReference(channel);+    return channel;+  }++  subscribe(matcher, onMessage) {+    const subscriber = makeSubscriberFor(matcher, onMessage);++    if (!ArrayPrototypeIncludes(this._subscribers, subscriber)) {+      ArrayPrototypePush(this._subscribers, subscriber);++      // New subscribers should check existing channels+      for (const key in this._channels) {+        const channel = this._channels[key].get();+        if (!channel) {+          delete this._channels[key];+          continue;+        }++        const [matcher, onMessage] = subscriber;+        if (matcher(channel.name)) {+          channel.subscribe(onMessage);+        }+      }+    }++    return () => {+      const index = this._subscribers.indexOf(subscriber);
      const index = ArrayPrototypeIndexOf(this._subscribers, subscriber);
Qard

comment created time in 20 days

PullRequestReviewEvent

Pull request review commentnodejs/node

lib: create diagnostics_channel module

+'use strict';++const {+  ArrayPrototypeIncludes,+  ArrayPrototypePush,+  ArrayPrototypeSplice,+  ObjectCreate,+  ObjectSetPrototypeOf,+  Promise,+  PromiseResolve,+  RegExp,+  Symbol+} = primordials;++const {+  codes: {+    ERR_INVALID_ARG_TYPE,+  }+} = require('internal/errors');++const { triggerUncaughtException } = internalBinding('errors');++const { WeakReference } = internalBinding('util');++function alwaysMatch() {+  return true;+}++function strictEqualMatch(string) {+  return function(value) {+    return value === string;+  };+}++function regexpMatch(regexp) {+  return function(value) {+    return regexp.test(value);+  };+}++function makeSubscriberFor(matcher, onMessage) {+  if (typeof onMessage !== 'function' && typeof matcher !== 'function') {+    throw new ERR_INVALID_ARG_TYPE('onMessage', ['function'], onMessage);+  }++  switch (typeof matcher) {+    case 'function':+      if (typeof onMessage === 'function') {+        return [matcher, onMessage];+      } else if (typeof onMessage === 'undefined') {+        return [alwaysMatch, matcher];+      }+      break;+    case 'string':+    case 'symbol':+      return [strictEqualMatch(matcher), onMessage];+    case 'object':+      if (matcher instanceof RegExp) {+        return [regexpMatch(matcher), onMessage];+      }+      break;+  }++  throw new ERR_INVALID_ARG_TYPE('matcher',+                                 ['string', 'symbol', 'function', 'RegExp'],+                                 matcher);+}++class AsyncIterableChannel {+  constructor(channel) {+    this.channel = channel;+    this.events = [];+    this.waiting = [];++    this.subscriber = (message) => {+      const resolve = this.waiting.shift();+      if (resolve) {+        return resolve({+          value: message,+          done: false+        });+      }++      this.events.push(message);+    };++    channel.subscribe(this.subscriber);+  }++  [Symbol.asyncIterator]() {+    return this;+  }++  return() {+    const data = { done: true };+    this.done = true;++    this.channel.unsubscribe(this.subscriber);++    for (let i = 0; i < this.waiting.length; i++) {+      const resolve = this.waiting[i];+      resolve(data);+    }++    return PromiseResolve(data);+  }++  next() {+    const event = this.events.shift();+    if (event) {+      return PromiseResolve({+        value: event,+        done: false+      });+    }++    if (this.done) {+      return PromiseResolve({+        done: true+      });+    }++    return new Promise((resolve) => {+      this.waiting.push(resolve);+    });+  }+}++// TODO(qard): should there be a C++ channel interface?+class ActiveChannel {+  [Symbol.asyncIterator]() {+    return new AsyncIterableChannel(this);+  }++  subscribe(subscription) {+    ArrayPrototypePush(this._subscribers, subscription);+  }++  unsubscribe(subscription) {+    const index = this._subscribers.indexOf(subscription);+    if (index >= 0) {+      ArrayPrototypeSplice(this._subscribers, index, 1);++      // When there are no more active subscribers, restore to fast prototype.+      if (!this._subscribers.length) {+        // eslint-disable-next-line no-use-before-define+        ObjectSetPrototypeOf(this, Channel.prototype);+      }+    }+  }++  shouldPublish() {+    return true;+  }++  publish(data) {+    for (var i = 0; i < this._subscribers.length; i++) {+      try {+        const onMessage = this._subscribers[i];+        onMessage(data, this.name);+      } catch (err) {+        process.nextTick(() => {+          triggerUncaughtException(err, false);+        });+      }+    }+  }+}++class Channel {+  constructor(name) {+    this._subscribers = undefined;+    this.name = name;+  }++  [Symbol.asyncIterator]() {+    return new AsyncIterableChannel(this);+  }++  subscribe(subscription) {+    ObjectSetPrototypeOf(this, ActiveChannel.prototype);+    this._subscribers = [];+    this.subscribe(subscription);+  }++  shouldPublish() {+    return false;+  }++  publish() {}+}++class DiagnosticsChannel {+  constructor() {+    this._channels = ObjectCreate(null);+    this._subscribers = [];+  }++  channel(name) {+    let channel;+    const ref = this._channels[name];+    if (ref) channel = ref.get();+    if (channel) return channel;++    if (typeof name !== 'string' && typeof name !== 'symbol') {+      throw new ERR_INVALID_ARG_TYPE('channel', ['string', 'symbol'], name);+    }++    channel = new Channel(name);++    // New channels should check existing subscribers+    for (let i = 0; i < this._subscribers.length; i++) {+      const [matcher, onMessage] = this._subscribers[i];+      if (matcher(channel.name)) {+        channel.subscribe(onMessage);+      }+    }++    this._channels[name] = new WeakReference(channel);+    return channel;+  }++  subscribe(matcher, onMessage) {+    const subscriber = makeSubscriberFor(matcher, onMessage);++    if (!ArrayPrototypeIncludes(this._subscribers, subscriber)) {+      ArrayPrototypePush(this._subscribers, subscriber);++      // New subscribers should check existing channels+      for (const key in this._channels) {+        const channel = this._channels[key].get();+        if (!channel) {+          delete this._channels[key];+          continue;+        }++        const [matcher, onMessage] = subscriber;
        const {0: matcher, 1: onMessage} = subscriber;
Qard

comment created time in 20 days

PullRequestReviewEvent

Pull request review commentnodejs/node

lib: create diagnostics_channel module

+'use strict';++const {+  ArrayPrototypeIncludes,+  ArrayPrototypePush,+  ArrayPrototypeSplice,+  ObjectCreate,+  ObjectSetPrototypeOf,+  Promise,+  PromiseResolve,+  RegExp,+  Symbol+} = primordials;++const {+  codes: {+    ERR_INVALID_ARG_TYPE,+  }+} = require('internal/errors');++const { triggerUncaughtException } = internalBinding('errors');++const { WeakReference } = internalBinding('util');++function alwaysMatch() {+  return true;+}++function strictEqualMatch(string) {+  return function(value) {+    return value === string;+  };+}++function regexpMatch(regexp) {+  return function(value) {+    return regexp.test(value);+  };+}++function makeSubscriberFor(matcher, onMessage) {+  if (typeof onMessage !== 'function' && typeof matcher !== 'function') {+    throw new ERR_INVALID_ARG_TYPE('onMessage', ['function'], onMessage);+  }++  switch (typeof matcher) {+    case 'function':+      if (typeof onMessage === 'function') {+        return [matcher, onMessage];+      } else if (typeof onMessage === 'undefined') {+        return [alwaysMatch, matcher];+      }+      break;+    case 'string':+    case 'symbol':+      return [strictEqualMatch(matcher), onMessage];+    case 'object':+      if (matcher instanceof RegExp) {+        return [regexpMatch(matcher), onMessage];+      }+      break;+  }++  throw new ERR_INVALID_ARG_TYPE('matcher',+                                 ['string', 'symbol', 'function', 'RegExp'],+                                 matcher);+}++class AsyncIterableChannel {+  constructor(channel) {+    this.channel = channel;+    this.events = [];+    this.waiting = [];++    this.subscriber = (message) => {+      const resolve = this.waiting.shift();+      if (resolve) {+        return resolve({+          value: message,+          done: false+        });+      }++      this.events.push(message);+    };++    channel.subscribe(this.subscriber);+  }++  [Symbol.asyncIterator]() {+    return this;+  }++  return() {+    const data = { done: true };+    this.done = true;++    this.channel.unsubscribe(this.subscriber);++    for (let i = 0; i < this.waiting.length; i++) {+      const resolve = this.waiting[i];+      resolve(data);+    }++    return PromiseResolve(data);+  }++  next() {+    const event = this.events.shift();+    if (event) {+      return PromiseResolve({+        value: event,+        done: false+      });+    }++    if (this.done) {+      return PromiseResolve({+        done: true+      });+    }++    return new Promise((resolve) => {+      this.waiting.push(resolve);+    });+  }+}++// TODO(qard): should there be a C++ channel interface?+class ActiveChannel {+  [Symbol.asyncIterator]() {+    return new AsyncIterableChannel(this);+  }++  subscribe(subscription) {+    ArrayPrototypePush(this._subscribers, subscription);+  }++  unsubscribe(subscription) {+    const index = this._subscribers.indexOf(subscription);+    if (index >= 0) {+      ArrayPrototypeSplice(this._subscribers, index, 1);++      // When there are no more active subscribers, restore to fast prototype.+      if (!this._subscribers.length) {+        // eslint-disable-next-line no-use-before-define+        ObjectSetPrototypeOf(this, Channel.prototype);+      }+    }+  }++  shouldPublish() {+    return true;+  }++  publish(data) {+    for (var i = 0; i < this._subscribers.length; i++) {+      try {+        const onMessage = this._subscribers[i];+        onMessage(data, this.name);+      } catch (err) {+        process.nextTick(() => {+          triggerUncaughtException(err, false);+        });+      }+    }+  }+}++class Channel {+  constructor(name) {+    this._subscribers = undefined;+    this.name = name;+  }++  [Symbol.asyncIterator]() {+    return new AsyncIterableChannel(this);+  }++  subscribe(subscription) {+    ObjectSetPrototypeOf(this, ActiveChannel.prototype);+    this._subscribers = [];+    this.subscribe(subscription);+  }++  shouldPublish() {+    return false;+  }++  publish() {}+}++class DiagnosticsChannel {+  constructor() {+    this._channels = ObjectCreate(null);+    this._subscribers = [];+  }++  channel(name) {+    let channel;+    const ref = this._channels[name];+    if (ref) channel = ref.get();+    if (channel) return channel;++    if (typeof name !== 'string' && typeof name !== 'symbol') {+      throw new ERR_INVALID_ARG_TYPE('channel', ['string', 'symbol'], name);+    }++    channel = new Channel(name);++    // New channels should check existing subscribers+    for (let i = 0; i < this._subscribers.length; i++) {+      const [matcher, onMessage] = this._subscribers[i];

Avoid mutated Array.prototype[Symbol.iterator], might also be nicer to use an object with named fields

      const {0: matcher, 1: onMessage} = this._subscribers[i];
Qard

comment created time in 20 days

PullRequestReviewEvent

Pull request review commentnodejs/node

lib: create diagnostics_channel module

+'use strict';++const {+  ArrayPrototypeIncludes,+  ArrayPrototypePush,+  ArrayPrototypeSplice,+  ObjectCreate,+  ObjectSetPrototypeOf,+  Promise,+  PromiseResolve,+  RegExp,+  Symbol+} = primordials;++const {+  codes: {+    ERR_INVALID_ARG_TYPE,+  }+} = require('internal/errors');++const { triggerUncaughtException } = internalBinding('errors');++const { WeakReference } = internalBinding('util');++function alwaysMatch() {+  return true;+}++function strictEqualMatch(string) {+  return function(value) {+    return value === string;+  };+}++function regexpMatch(regexp) {+  return function(value) {+    return regexp.test(value);+  };+}++function makeSubscriberFor(matcher, onMessage) {+  if (typeof onMessage !== 'function' && typeof matcher !== 'function') {+    throw new ERR_INVALID_ARG_TYPE('onMessage', ['function'], onMessage);+  }++  switch (typeof matcher) {+    case 'function':+      if (typeof onMessage === 'function') {+        return [matcher, onMessage];+      } else if (typeof onMessage === 'undefined') {+        return [alwaysMatch, matcher];+      }+      break;+    case 'string':+    case 'symbol':+      return [strictEqualMatch(matcher), onMessage];+    case 'object':+      if (matcher instanceof RegExp) {+        return [regexpMatch(matcher), onMessage];+      }+      break;+  }++  throw new ERR_INVALID_ARG_TYPE('matcher',+                                 ['string', 'symbol', 'function', 'RegExp'],+                                 matcher);+}++class AsyncIterableChannel {+  constructor(channel) {+    this.channel = channel;+    this.events = [];+    this.waiting = [];++    this.subscriber = (message) => {+      const resolve = this.waiting.shift();+      if (resolve) {+        return resolve({+          value: message,+          done: false+        });+      }++      this.events.push(message);+    };++    channel.subscribe(this.subscriber);+  }++  [Symbol.asyncIterator]() {+    return this;+  }++  return() {+    const data = { done: true };+    this.done = true;++    this.channel.unsubscribe(this.subscriber);++    for (let i = 0; i < this.waiting.length; i++) {+      const resolve = this.waiting[i];+      resolve(data);+    }++    return PromiseResolve(data);+  }++  next() {+    const event = this.events.shift();+    if (event) {+      return PromiseResolve({+        value: event,+        done: false+      });+    }++    if (this.done) {+      return PromiseResolve({+        done: true+      });+    }++    return new Promise((resolve) => {+      this.waiting.push(resolve);+    });+  }+}++// TODO(qard): should there be a C++ channel interface?+class ActiveChannel {+  [Symbol.asyncIterator]() {+    return new AsyncIterableChannel(this);+  }++  subscribe(subscription) {+    ArrayPrototypePush(this._subscribers, subscription);+  }++  unsubscribe(subscription) {+    const index = this._subscribers.indexOf(subscription);
    const index = ArrayPrototypeIndexOf(this._subscribers, subscription);
Qard

comment created time in 20 days

PullRequestReviewEvent

Pull request review commentnodejs/node

lib: create diagnostics_channel module

+'use strict';++const {+  ArrayPrototypeIncludes,+  ArrayPrototypePush,+  ArrayPrototypeSplice,+  ObjectCreate,+  ObjectSetPrototypeOf,+  Promise,+  PromiseResolve,+  RegExp,+  Symbol+} = primordials;++const {+  codes: {+    ERR_INVALID_ARG_TYPE,+  }+} = require('internal/errors');++const { triggerUncaughtException } = internalBinding('errors');++const { WeakReference } = internalBinding('util');++function alwaysMatch() {+  return true;+}++function strictEqualMatch(string) {+  return function(value) {+    return value === string;+  };+}++function regexpMatch(regexp) {+  return function(value) {+    return regexp.test(value);+  };+}++function makeSubscriberFor(matcher, onMessage) {+  if (typeof onMessage !== 'function' && typeof matcher !== 'function') {+    throw new ERR_INVALID_ARG_TYPE('onMessage', ['function'], onMessage);+  }++  switch (typeof matcher) {+    case 'function':+      if (typeof onMessage === 'function') {+        return [matcher, onMessage];+      } else if (typeof onMessage === 'undefined') {+        return [alwaysMatch, matcher];+      }+      break;+    case 'string':+    case 'symbol':+      return [strictEqualMatch(matcher), onMessage];+    case 'object':+      if (matcher instanceof RegExp) {+        return [regexpMatch(matcher), onMessage];+      }+      break;+  }++  throw new ERR_INVALID_ARG_TYPE('matcher',+                                 ['string', 'symbol', 'function', 'RegExp'],+                                 matcher);+}++class AsyncIterableChannel {+  constructor(channel) {+    this.channel = channel;+    this.events = [];+    this.waiting = [];++    this.subscriber = (message) => {+      const resolve = this.waiting.shift();+      if (resolve) {+        return resolve({+          value: message,+          done: false+        });+      }++      this.events.push(message);+    };++    channel.subscribe(this.subscriber);+  }++  [Symbol.asyncIterator]() {+    return this;+  }++  return() {+    const data = { done: true };+    this.done = true;++    this.channel.unsubscribe(this.subscriber);++    for (let i = 0; i < this.waiting.length; i++) {+      const resolve = this.waiting[i];+      resolve(data);+    }++    return PromiseResolve(data);+  }++  next() {+    const event = this.events.shift();+    if (event) {+      return PromiseResolve({+        value: event,+        done: false+      });+    }++    if (this.done) {+      return PromiseResolve({+        done: true+      });+    }++    return new Promise((resolve) => {+      this.waiting.push(resolve);
      ArrayPrototypePush(this.waiting, resolve);
Qard

comment created time in 20 days

PullRequestReviewEvent

Pull request review commentnodejs/node

lib: create diagnostics_channel module

+'use strict';++const {+  ArrayPrototypeIncludes,+  ArrayPrototypePush,+  ArrayPrototypeSplice,+  ObjectCreate,+  ObjectSetPrototypeOf,+  Promise,+  PromiseResolve,+  RegExp,+  Symbol+} = primordials;++const {+  codes: {+    ERR_INVALID_ARG_TYPE,+  }+} = require('internal/errors');++const { triggerUncaughtException } = internalBinding('errors');++const { WeakReference } = internalBinding('util');++function alwaysMatch() {+  return true;+}++function strictEqualMatch(string) {+  return function(value) {+    return value === string;+  };+}++function regexpMatch(regexp) {+  return function(value) {+    return regexp.test(value);+  };+}++function makeSubscriberFor(matcher, onMessage) {+  if (typeof onMessage !== 'function' && typeof matcher !== 'function') {+    throw new ERR_INVALID_ARG_TYPE('onMessage', ['function'], onMessage);+  }++  switch (typeof matcher) {+    case 'function':+      if (typeof onMessage === 'function') {+        return [matcher, onMessage];+      } else if (typeof onMessage === 'undefined') {+        return [alwaysMatch, matcher];+      }+      break;+    case 'string':+    case 'symbol':+      return [strictEqualMatch(matcher), onMessage];+    case 'object':+      if (matcher instanceof RegExp) {+        return [regexpMatch(matcher), onMessage];+      }+      break;+  }++  throw new ERR_INVALID_ARG_TYPE('matcher',+                                 ['string', 'symbol', 'function', 'RegExp'],+                                 matcher);+}++class AsyncIterableChannel {+  constructor(channel) {+    this.channel = channel;+    this.events = [];+    this.waiting = [];++    this.subscriber = (message) => {+      const resolve = this.waiting.shift();+      if (resolve) {+        return resolve({+          value: message,+          done: false+        });+      }++      this.events.push(message);+    };++    channel.subscribe(this.subscriber);+  }++  [Symbol.asyncIterator]() {+    return this;+  }++  return() {+    const data = { done: true };+    this.done = true;++    this.channel.unsubscribe(this.subscriber);++    for (let i = 0; i < this.waiting.length; i++) {+      const resolve = this.waiting[i];+      resolve(data);+    }++    return PromiseResolve(data);+  }++  next() {+    const event = this.events.shift();
    const event = ArrayPrototypeShift(this.events);
Qard

comment created time in 20 days

Pull request review commentnodejs/node

lib: create diagnostics_channel module

+'use strict';++const {+  ArrayPrototypeIncludes,+  ArrayPrototypePush,+  ArrayPrototypeSplice,+  ObjectCreate,+  ObjectSetPrototypeOf,+  Promise,+  PromiseResolve,+  RegExp,+  Symbol+} = primordials;++const {+  codes: {+    ERR_INVALID_ARG_TYPE,+  }+} = require('internal/errors');++const { triggerUncaughtException } = internalBinding('errors');++const { WeakReference } = internalBinding('util');++function alwaysMatch() {+  return true;+}++function strictEqualMatch(string) {+  return function(value) {+    return value === string;+  };+}++function regexpMatch(regexp) {+  return function(value) {+    return regexp.test(value);+  };+}++function makeSubscriberFor(matcher, onMessage) {+  if (typeof onMessage !== 'function' && typeof matcher !== 'function') {+    throw new ERR_INVALID_ARG_TYPE('onMessage', ['function'], onMessage);+  }++  switch (typeof matcher) {+    case 'function':+      if (typeof onMessage === 'function') {+        return [matcher, onMessage];+      } else if (typeof onMessage === 'undefined') {+        return [alwaysMatch, matcher];+      }+      break;+    case 'string':+    case 'symbol':+      return [strictEqualMatch(matcher), onMessage];+    case 'object':+      if (matcher instanceof RegExp) {+        return [regexpMatch(matcher), onMessage];+      }+      break;+  }++  throw new ERR_INVALID_ARG_TYPE('matcher',+                                 ['string', 'symbol', 'function', 'RegExp'],+                                 matcher);+}++class AsyncIterableChannel {+  constructor(channel) {+    this.channel = channel;+    this.events = [];+    this.waiting = [];++    this.subscriber = (message) => {+      const resolve = this.waiting.shift();+      if (resolve) {+        return resolve({+          value: message,+          done: false+        });+      }++      this.events.push(message);+    };++    channel.subscribe(this.subscriber);+  }++  [Symbol.asyncIterator]() {

use primordial

Qard

comment created time in 20 days

PullRequestReviewEvent

Pull request review commentnodejs/node

lib: create diagnostics_channel module

+'use strict';++const {+  ArrayPrototypeIncludes,+  ArrayPrototypePush,+  ArrayPrototypeSplice,+  ObjectCreate,+  ObjectSetPrototypeOf,+  Promise,+  PromiseResolve,+  RegExp,+  Symbol+} = primordials;++const {+  codes: {+    ERR_INVALID_ARG_TYPE,+  }+} = require('internal/errors');++const { triggerUncaughtException } = internalBinding('errors');++const { WeakReference } = internalBinding('util');++function alwaysMatch() {+  return true;+}++function strictEqualMatch(string) {+  return function(value) {+    return value === string;+  };+}++function regexpMatch(regexp) {+  return function(value) {+    return regexp.test(value);+  };+}++function makeSubscriberFor(matcher, onMessage) {+  if (typeof onMessage !== 'function' && typeof matcher !== 'function') {+    throw new ERR_INVALID_ARG_TYPE('onMessage', ['function'], onMessage);+  }++  switch (typeof matcher) {+    case 'function':+      if (typeof onMessage === 'function') {+        return [matcher, onMessage];+      } else if (typeof onMessage === 'undefined') {+        return [alwaysMatch, matcher];+      }+      break;+    case 'string':+    case 'symbol':+      return [strictEqualMatch(matcher), onMessage];+    case 'object':+      if (matcher instanceof RegExp) {+        return [regexpMatch(matcher), onMessage];+      }+      break;+  }++  throw new ERR_INVALID_ARG_TYPE('matcher',+                                 ['string', 'symbol', 'function', 'RegExp'],+                                 matcher);+}++class AsyncIterableChannel {+  constructor(channel) {+    this.channel = channel;+    this.events = [];+    this.waiting = [];++    this.subscriber = (message) => {+      const resolve = this.waiting.shift();+      if (resolve) {+        return resolve({+          value: message,+          done: false+        });+      }++      this.events.push(message);+    };++    channel.subscribe(this.subscriber);

do we care if this is not the expected type / subscribe method?

Qard

comment created time in 20 days

PullRequestReviewEvent

Pull request review commentnodejs/node

lib: create diagnostics_channel module

+'use strict';++const {+  ArrayPrototypeIncludes,+  ArrayPrototypePush,+  ArrayPrototypeSplice,+  ObjectCreate,+  ObjectSetPrototypeOf,+  Promise,+  PromiseResolve,+  RegExp,+  Symbol+} = primordials;++const {+  codes: {+    ERR_INVALID_ARG_TYPE,+  }+} = require('internal/errors');++const { triggerUncaughtException } = internalBinding('errors');++const { WeakReference } = internalBinding('util');++function alwaysMatch() {+  return true;+}++function strictEqualMatch(string) {+  return function(value) {+    return value === string;+  };+}++function regexpMatch(regexp) {+  return function(value) {+    return regexp.test(value);+  };+}++function makeSubscriberFor(matcher, onMessage) {+  if (typeof onMessage !== 'function' && typeof matcher !== 'function') {+    throw new ERR_INVALID_ARG_TYPE('onMessage', ['function'], onMessage);+  }++  switch (typeof matcher) {+    case 'function':+      if (typeof onMessage === 'function') {+        return [matcher, onMessage];+      } else if (typeof onMessage === 'undefined') {+        return [alwaysMatch, matcher];+      }+      break;+    case 'string':+    case 'symbol':+      return [strictEqualMatch(matcher), onMessage];+    case 'object':+      if (matcher instanceof RegExp) {+        return [regexpMatch(matcher), onMessage];+      }+      break;+  }++  throw new ERR_INVALID_ARG_TYPE('matcher',+                                 ['string', 'symbol', 'function', 'RegExp'],+                                 matcher);+}++class AsyncIterableChannel {+  constructor(channel) {+    this.channel = channel;+    this.events = [];+    this.waiting = [];++    this.subscriber = (message) => {+      const resolve = this.waiting.shift();+      if (resolve) {+        return resolve({+          value: message,+          done: false+        });+      }++      this.events.push(message);
      ArrayPrototypePush(this.events, message);
Qard

comment created time in 20 days

PullRequestReviewEvent

Pull request review commentnodejs/node

lib: create diagnostics_channel module

+'use strict';++const {+  ArrayPrototypeIncludes,+  ArrayPrototypePush,+  ArrayPrototypeSplice,+  ObjectCreate,+  ObjectSetPrototypeOf,+  Promise,+  PromiseResolve,+  RegExp,+  Symbol+} = primordials;++const {+  codes: {+    ERR_INVALID_ARG_TYPE,+  }+} = require('internal/errors');++const { triggerUncaughtException } = internalBinding('errors');++const { WeakReference } = internalBinding('util');++function alwaysMatch() {+  return true;+}++function strictEqualMatch(string) {+  return function(value) {+    return value === string;+  };+}++function regexpMatch(regexp) {+  return function(value) {+    return regexp.test(value);+  };+}++function makeSubscriberFor(matcher, onMessage) {+  if (typeof onMessage !== 'function' && typeof matcher !== 'function') {+    throw new ERR_INVALID_ARG_TYPE('onMessage', ['function'], onMessage);+  }++  switch (typeof matcher) {+    case 'function':+      if (typeof onMessage === 'function') {+        return [matcher, onMessage];+      } else if (typeof onMessage === 'undefined') {+        return [alwaysMatch, matcher];+      }+      break;+    case 'string':+    case 'symbol':+      return [strictEqualMatch(matcher), onMessage];+    case 'object':+      if (matcher instanceof RegExp) {+        return [regexpMatch(matcher), onMessage];+      }+      break;+  }++  throw new ERR_INVALID_ARG_TYPE('matcher',+                                 ['string', 'symbol', 'function', 'RegExp'],+                                 matcher);+}++class AsyncIterableChannel {+  constructor(channel) {+    this.channel = channel;+    this.events = [];+    this.waiting = [];++    this.subscriber = (message) => {+      const resolve = this.waiting.shift();
      const resolve = ArrayPrototypeShift(this.waiting);
Qard

comment created time in 20 days

PullRequestReviewEvent

Pull request review commentnodejs/node

lib: create diagnostics_channel module

+'use strict';++const {+  ArrayPrototypeIncludes,+  ArrayPrototypePush,+  ArrayPrototypeSplice,+  ObjectCreate,+  ObjectSetPrototypeOf,+  Promise,+  PromiseResolve,+  RegExp,+  Symbol+} = primordials;++const {+  codes: {+    ERR_INVALID_ARG_TYPE,+  }+} = require('internal/errors');++const { triggerUncaughtException } = internalBinding('errors');++const { WeakReference } = internalBinding('util');++function alwaysMatch() {+  return true;+}++function strictEqualMatch(string) {+  return function(value) {+    return value === string;+  };+}++function regexpMatch(regexp) {+  return function(value) {+    return regexp.test(value);
    return RegExpPrototypeTest(regexp, value);

Might also make more sense to use FunctionPrototypeBind here at least.

Qard

comment created time in 20 days

PullRequestReviewEvent

issue commentnodejs/modules

import assertions RFC

The Import Assertions champions are seeking to ask for Stage 3 this coming TC39 meeting, we should try and iron out if there are any actionable feedback items we wish to ask for. In particular the web browser restriction on cache key seems something I would remain uncomfortable with keeping in the import assertions proposals since node may wish to make use of matching the web.

bmeck

comment created time in 21 days

pull request commentnodejs/node

module: fix specifier resolution option value

@addaleax any behavior seems fine, just want docs.

Himself65

comment created time in 21 days

pull request commentnodejs/node

module: fix specifier resolution option value

Do we have a documented priority of the option value if 2 conflicting values are supplied?

Himself65

comment created time in 21 days

PullRequestReviewEvent

pull request commentnodejs/node

module: exports patterns

Re-reading this, I did notice that this alters the method of determining if a left hand side is chosen in a somewhat odd way.

  1. for conditions, we use object iteration order (top down)
  2. for patterns, we are using the longest pattern

Is it feasible that we have patterns also preserved by iteration ordering? This would avoid the human issue of needing to read all left hand sides before determining if a path is taken; you can always order longest pattern first if you want and lint for it. By using iteration ordering a human can read in a consistent direction of top down and incrementally to determine if the path is taken.

guybedford

comment created time in 25 days

issue commentnodejs/modules

Enabling import meta resolve by default?

do you mean via the distinction of require.resolve and import.meta.resolve, or do you mean enabling an option into the resolve function?

I mean having 1 function that I can throw data into that handles all the basic resolutions I might be curious about. This is not in any way critical as if we have N functions I can just wrap them into a single function anyway.

daKmoR

comment created time in a month

PullRequestReviewEvent

Pull request review commentnodejs/node

module: exports patterns

 function packageImportsResolve(name, base, conditions) {         const keys = ObjectGetOwnPropertyNames(imports);         for (let i = 0; i < keys.length; i++) {           const key = keys[i];-          if (key[key.length - 1] === '/' &&+          if (key[key.length - 1] === '*' &&+              StringPrototypeStartsWith(name, key.slice(0, -1)) &&
              StringPrototypeStartsWith(name, StringPrototypeSlice(key, 0, -1)) &&
guybedford

comment created time in a month

Pull request review commentnodejs/node

module: exports patterns

 function packageExportsResolve(   const keys = ObjectGetOwnPropertyNames(exports);   for (let i = 0; i < keys.length; i++) {     const key = keys[i];-    if (key[key.length - 1] === '/' &&+    if (key[key.length - 1] === '*' &&+        StringPrototypeStartsWith(packageSubpath, key.slice(0, -1)) &&
        StringPrototypeStartsWith(packageSubpath, StringPrototypeSlice(key, 0, -1)) &&
guybedford

comment created time in a month

PullRequestReviewEvent

Pull request review commentnodejs/node

module: exports patterns

 function resolvePackageTargetString(   if (RegExpPrototypeTest(invalidSegmentRegEx, subpath))     throwInvalidSubpath(match + subpath, packageJSONUrl, internal, base); +  if (pattern)+    return new URL(resolved.href.replace(/\*/g, subpath));
    return new URL(StringPrototypeReplace(resolved.href, /\*/g, subpath));
guybedford

comment created time in a month

PullRequestReviewEvent

Pull request review commentnodejs/node

module: exports patterns

       "require": "./resolve-self-invalid.js",       "import": "./resolve-self-invalid.mjs"     },-    "./subpath/": "./subpath/"+    "./subpath/": "./subpath/",+    "./subpath/sub-*": "./subpath/dir1/*.js"

needs a test for multiple "*"s

guybedford

comment created time in a month

PullRequestReviewEvent
PullRequestReviewEvent

issue commentnodejs/modules

Enabling import meta resolve by default?

I do like the API being usable to determine either require or import personally.

daKmoR

comment created time in a month

issue commentnodejs/modules

Mocking use cases

@GeoffreyBooth that is correct. However, if a Module is linked it won't be garbage collected even if you never reference it again in V8's implementation. Per setScriptSource the issues with supporting Module are not high priority for V8 and it would not re-run the top scope so it might not be a complete fix.

guybedford

comment created time in a month

issue commentnodejs/modules

Mocking use cases

@boneskull

One of the biggest questions about programmatic usage is how to avoid it from being used inappropriately, --loader is meant to have some level of isolation from application code and not meant to do things in the same way that caused various require APIs to become deprecated. This goes so far as to start leaking into policies to some extent as 3rd party loaders can invalidate guarantees of such policy configurations (which is intended!). This just needs a thorough write up.

In general I've have found the flag sufficient to prevent an arbitrary 3rd party module from taking over the loading process since it is done at bootstrap. The key of finding a way to prevent usage and/or limit it is key here. If the problem is having any configuration done we could look at a drop privileges style approach where you can drop the ability to inject loaders after an API is called, but that likely would still want a flag to enable the space of time were you can inject loaders. Similarly --require does have precedent of running prior to --frozen-intrinsics so it isn't a new concept.

Additionally, a lot of effort has gone in to make loaders at least in theory compatible with ahead of time tooling perhaps to the detriment of runtime tooling. Discussion of how to keep ahead of time tooling working in these contexts is pretty important in particular so that we can have a model for what is/is not guaranteed. Removing any guarantees about ahead of time tooling is likely untenable, but creating carve outs seems good.

I would note that in my opinion we don't need to necessarily have 100% compatibility with any given workflow for how mocking should be done as various issues do exist due to the design of ESM so it isn't a simple Object graph in JS unfortunately. Phrasing things around the specific needs or workflows and in particular avoiding mixing topics like mocking and unloading would be ideal. In general, unloading simply isn't workable, but altering the global (not local) module resolution for new (specifier, referrer) pairs is possible (this will not cause old modules to be unloaded and may cause version mismatches).

guybedford

comment created time in a month

issue commenttc39/proposal-json-modules

Should JSON modules export Records/Tuples?

I will restate; I think the biggest issue is that various tools already load JSON as Objects. I do not think it appropriate to alter that de-facto output / expectation from tooling, but do think any sort of new expectations can just use a new attribute to flag the import for specialized evaluation.

dandclark

comment created time in a month

PullRequestReviewEvent

push eventbmeck/ecma262

Bradley Farias

commit sha 49e5892ba2820f11b2e51f1884b6c7cdbfc4df3e

star shouldnt be a string

view details

push time in a month

push eventbmeck/ecma262

Bradley Farias

commit sha d786811e655117c598ff30fe0540887a740b5db9

forgot to redo some stuff

view details

push time in a month

push eventbmeck/ecma262

Bradley Farias

commit sha cd03c4032d9994692ba99d9e2336c72f580de0c0

typo

view details

push time in a month

Pull request review commenttc39/ecma262

Normative: Arbitrary module namespace identifier names

 <h1>IsStringPrefix ( _p_, _q_ )</h1>       </emu-note>     </emu-clause> +    <emu-clause id="sec-isstringvalidunicode" aoid="IsStringValidUnicode">+      <h1>Static Semantics: IsStringValidUnicode ( _string_ )</h1>+      <p>+        The abstract operation IsStringValidUnicode takes an argument _string_ ( a String ). It interprets _string_ as a sequence of UTF-16 encoded code points, as described in <emu-xref href="#sec-ecmascript-language-types-string-type"></emu-xref>, and returns if it is a complete valid UTF-16 sequence. It performs the following steps when called:+      </p>+      <emu-alg>+        1. Let _strLen_ be the number of code units in string.+        1. Let _k_ be 0.+        1. Repeat,+          1. If _k_ equals _strLen_, return.

redone

bmeck

comment created time in a month

PullRequestReviewEvent

push eventbmeck/ecma262

Bradley Farias

commit sha 17c517d3a992e11dbc4405a167218c39ba8c474a

style Co-authored-by: Jordan Harband <ljharb@gmail.com>

view details

push time in a month

push eventbmeck/ecma262

Bradley Farias

commit sha 3ad0eda3523696ae0eb7412a5eac84ccd3216a83

spaces Co-authored-by: Jordan Harband <ljharb@gmail.com>

view details

push time in a month

Pull request review commenttc39/ecma262

Normative: Arbitrary module namespace identifier names

 <h2>Syntax</h2>          ExportFromClause :           `*`-          `*` `as` IdentifierName+          `*` `as` ExportSpecifier

should be fixed

bmeck

comment created time in a month

PullRequestReviewEvent

Pull request review commenttc39/ecma262

Normative: Arbitrary module namespace identifier names

 <h1>IsStringPrefix ( _p_, _q_ )</h1>       </emu-note>     </emu-clause> +    <emu-clause id="sec-isstringvalidunicode" aoid="IsStringValidUnicode">+      <h1>Static Semantics: IsStringValidUnicode ( _string_ )</h1>+      <p>+        The abstract operation IsStringValidUnicode takes an argument _string_ ( a String ). It interprets _string_ as a sequence of UTF-16 encoded code points, as described in <emu-xref href="#sec-ecmascript-language-types-string-type"></emu-xref>, and returns if it is a complete valid UTF-16 sequence. It performs the following steps when called:+      </p>+      <emu-alg>+        1. Let _strLen_ be the number of code units in string.+        1. Let _k_ be 0.+        1. Repeat,+          1. If _k_ equals _strLen_, return.

should be fixed

bmeck

comment created time in a month

PullRequestReviewEvent

push eventbmeck/ecma262

Bradley Farias

commit sha 5d2c8bb62f04604741f5020ca407e56bc077852f

isStringValidUnicode bug

view details

push time in a month

push eventbmeck/ecma262

Bradley Farias

commit sha 406c8166bfbb9bb93aef724bc64cc7238da3e0a4

export from clause bug

view details

push time in a month

Pull request review commenttc39/ecma262

Normative: Arbitrary module namespace identifier names

 <h1>IsStringPrefix ( _p_, _q_ )</h1>       </emu-note>     </emu-clause> +    <emu-clause id="sec-isstringvalidunicode" aoid="IsStringValidUnicode">+      <h1>Static Semantics: IsStringValidUnicode ( _string_ )</h1>+      <p>+        The abstract operation IsStringValidUnicode takes an argument _string_ ( a String ). It interprets _string_ as a sequence of UTF-16 encoded code points, as described in <emu-xref href="#sec-ecmascript-language-types-string-type"></emu-xref>, and returns if it is a complete valid UTF-16 sequence. It performs the following steps when called:+      </p>+      <emu-alg>+        1. Let _strLen_ be the number of code units in string.+        1. Let _k_ be 0.+        1. Repeat,+          1. If _k_ equals _strLen_, return.

ah, that should be breaking out of the loop, it should make the whole algorithm return true

bmeck

comment created time in a month

PullRequestReviewEvent

Pull request review commenttc39/ecma262

Normative: Arbitrary module namespace identifier names

 <h2>Syntax</h2>          ExportFromClause :           `*`-          `*` `as` IdentifierName+          `*` `as` ExportSpecifier
          `*` `as` ModuleExportName

?

bmeck

comment created time in a month

PullRequestReviewEvent

Pull request review commenttc39/ecma262

Normative: Arbitrary module namespace identifier names

 <h2>Syntax</h2>          ExportFromClause :           `*`-          `*` `as` IdentifierName+          `*` `as` ExportSpecifier

Ah, good catch

bmeck

comment created time in a month

PullRequestReviewEvent

Pull request review commenttc39/ecma262

Normative: Arbitrary module namespace identifier names

 <h1>Static Semantics: BoundNames</h1>         AsyncFunctionDeclaration : `async` `function` `(` FormalParameters `)` `{` AsyncFunctionBody `}`       </emu-grammar>       <emu-alg>-        1. Return &laquo; *"\*default\*"* &raquo;.+        1. Return &laquo; ~default~ &raquo;.

I'm only doing this here because it needs to be done for this PR, feel free to rip it out to a separate PR.

bmeck

comment created time in a month

PullRequestReviewEvent

PR opened tc39/ecma262

Arbitrary module namespace identifier names

<!-- If you are changing the signature or behavior of an existing construct, please check if this affects downstream dependencies (searching for the construct's name is sufficient) and if needed file an issue:

I do not believe this affects any integration downstream.

This PR aims to allow:

import {"\0any unicode" as foo} from "";
export {foo as "\0 any unicode"};

This matches the restriction that WASM has that the string names for exports must be valid Unicode.

+84 -29

0 comment

1 changed file

pr created time in a month

more