current commit
260610.2 · lib: add lint rule to enforce use of `kEmptyObject`
'use strict';

const {
  ObjectIs,
  ReflectApply,
} = primordials;

const {
  validateObject,
} = require('internal/validators');

const AsyncContextFrame = require('internal/async_context_frame');
const { AsyncResource } = require('async_hooks');

const RunScope = require('internal/async_local_storage/run_scope');
const { kEmptyObject } = require('internal/util');

class AsyncLocalStorage {
  #defaultValue = undefined;
  #name = undefined;

  /**
   * @typedef {object} AsyncLocalStorageOptions
   * @property {any} [defaultValue] - The default value to use when no value is set.
   * @property {string} [name] - The name of the storage.
   */
  /**
   * @param {AsyncLocalStorageOptions} [options]
   */
  constructor(options = kEmptyObject) {
    validateObject(options, 'options');
    this.#defaultValue = options.defaultValue;

    if (options.name !== undefined) {
      this.#name = `${options.name}`;
    }
  }

  /** @type {string} */
  get name() { return this.#name || ''; }

  static bind(fn) {
    return AsyncResource.bind(fn);
  }

  static snapshot() {
    return AsyncLocalStorage.bind((cb, ...args) => cb(...args));
  }

  disable() {
    AsyncContextFrame.disable(this);
  }

  enterWith(data) {
    const frame = new AsyncContextFrame(this, data);
    AsyncContextFrame.set(frame);
  }

  run(data, fn, ...args) {
    const prior = this.getStore();
    if (ObjectIs(prior, data)) {
      return ReflectApply(fn, null, args);
    }
    this.enterWith(data);
    try {
      return ReflectApply(fn, null, args);
    } finally {
      this.enterWith(prior);
    }
  }

  exit(fn, ...args) {
    return this.run(undefined, fn, ...args);
  }

  getStore() {
    const frame = AsyncContextFrame.current();
    if (!frame?.has(this)) {
      return this.#defaultValue;
    }
    return frame?.get(this);
  }

  withScope(store) {
    return new RunScope(this, store);
  }
}

module.exports = AsyncLocalStorage;