ct') {
      // eslint-disable-next-line node-core/prefer-primordials
      return new Proxy(val, deprecationHandler);
    }
    return val;
  },

  setPrototypeOf(target, proto) {
    this.maybeWarn();
    return ObjectSetPrototypeOf(target, proto);
  }
};

// eslint-disable-next-line node-core/prefer-primordials
let processConfig = new Proxy(
  JSONParse(nativeModule.config),
  deprecationHandler);

ObjectDefineProperty(process, 'config', {
  enumerable: true,
  configurable: true,
  get() { return processConfig; },
  set(value) {
    deprecationHandler.maybeWarn();
    processConfig = value;
  }
});

require('internal/worker/js_transferable').setup();

// Bootstrappers for all threads, including worker threads and main thread
const perThreadSetup = require('internal/process/per_thread');
const rawMethods = internalBinding('process_methods');

// Set up methods on the process object for all threads
{
  process.dlopen = rawMethods.dlopen;
  process.uptime = rawMethods.uptime;

  // TODO(joyeecheung): either remove them or make them public
  process._getActiveRequests = rawMethods._getActiveRequests;
  process._getActiveHandles = rawMethods._getActiveHandles;

  process.getActiveResourcesInfo = function() {
    const timerCounts = internalTimers.getTimerCounts();
    return ArrayPrototypeConcat(
      rawMethods._getActiveRequestsInfo(),
      rawMethods._getActiveHandlesInfo(),
      ArrayPrototypeFill(new Array(timerCounts.timeoutCount), 'Timeout'),
      ArrayPrototypeFill(new Array(timerCounts.immediateCount), 'Immediate'));
  };

  // TODO(joyeecheung): remove these
  process.reallyExit = rawMethods.reallyExit;
  process._kill = rawMethods._kill;

  const wrapped = perThreadSetup.wrapProcessMethods(rawMethods);
  process._rawDebug = wrapped._rawDebug;
  process.cpuUsage = wrapped.cpuUsage;
  process.resourceUsage = wrapped.resourceUsage;
  process.memoryUsage = wrapped.memoryUsage;
  process.kill = wrapped.kill;
  process.exit = wrapped.exit;

  process.hrtime = perThreadSetup.hrtime;
  process.hrtime.bigint = perThreadSetup.hrtimeBigInt;

  process.openStdin = function() {
    process.stdin.resume();
    return process.stdin;
  };
}

const credentials = internalBinding('credentials');
if (credentials.implementsPosixCredentials) {
  process.getuid = credentials.getuid;
  process.geteuid = credentials.geteuid;
  process.getgid = credentials.getgid;
  process.getegid = credentials.getegid;
  process.getgroups = credentials.getgroups;
}

// Setup the callbacks that node::AsyncWrap will call when there are hooks to
// process. They use the same functions as the JS embedder API. These callbacks
// are setup immediately to prevent async_wrap.setupHooks() from being hijacked
// and the cost of doing so is negligible.
const { nativeHooks } = require('internal/async_hooks');
internalBinding('async_wrap').setupHooks(nativeHooks);

const {
  setupTaskQueue,
  queueMicrotask
} = require('internal/process/task_queues');

// Non-standard extensions:
const { BroadcastChannel } = require('internal/worker/io');
exposeInterface(globalThis, 'BroadcastChannel', BroadcastChannel);

defineOperation(globalThis, 'queueMicrotask', queueMicrotask);

const timers = require('timers');
defineOperation(globalThis, 'clearImmediate', timers.clearImmediate);
defineOperation(globalThis, 'setImmediate', timers.setImmediate);

const {
  structuredClone,
} = require('internal/structured_clone');
defineOperation(globalThis, 'structuredClone', structuredClone);

// Set the per-Environment callback that will be called
// when the TrackingTraceStateObserver updates trace state.
// Note that when NODE_USE_V8_PLATFORM is true, the observer is
// attached to the per-process TracingController.
const { setTraceCategoryStateUpdateHandler } = internalBinding('trace_events');
setTraceCategoryStateUpdateHandler(perThreadSetup.toggleTraceCategoryState);

// process.allowedNodeEnvironmentFlags
ObjectDefineProperty(process, 'allowedNodeEnvironmentFlags', {
  get() {
    const flags = perThreadSetup.buildAllowedFlags();
    process.allowedNodeEnvironmentFlags = flags;
    return process.allowedNodeEnvironmentFlags;
  },
  // If the user tries to set this to another value, override
  // this completely to that value.
  set(value) {
    ObjectDefineProperty(this, 'allowedNodeEnvironmentFlags', {
      value,
      configurable: true,
      enumerable: true,
      writable: true
    });
  },
  enumerable: true,
  configurable: true
});

// process.assert
process.assert = deprecate(
  perThreadSetup.assert,
  'process.assert() is deprecated. Please use the `assert` module instead.',
  'DEP0100');

// TODO(joyeecheung): this property has not been well-maintained, should we
// deprecate it in favor of a better API?
const { isDebugBuild, hasOpenSSL, hasInspector } = config;
const features = {
  inspector: hasInspector,
  debug: isDebugBuild,
  uv: true,
  ipv6: true,  // TODO(bnoordhuis) ping libuv
  tls_alpn: hasOpenSSL,
  tls_sni: hasOpenSSL,
  tls_ocsp: hasOpenSSL,
  tls: hasOpenSSL,
  // This needs to be dynamic because snapshot is built without code cache.
  // TODO(joyeecheung): https://github.com/nodejs/node/issues/31074
  // Make it possible to build snapshot with code cache
  get cached_builtins() {
    return nativeModule.hasCachedBuiltins();
  }
};

ObjectDefineProperty(process, 'features', {
  enumerable: true,
  writable: false,
  configurable: false,
  value: features
});

{
  const {
    onGlobalUncaughtException,
    setUncaughtExceptionCaptureCallback,
    hasUncaughtExceptionCaptureCallback
  } = require('internal/process/execution');

  // For legacy reasons this is still called `_fatalException`, even
  // though it is now a global uncaught exception handler.
  // The C++ land node::errors::TriggerUncaughtException grabs it
  // from the process object because it has been monkey-patchable.
  // TODO(joyeecheung): investigate whether process._fatalException
  // can be deprecated.
  process._fatalException = onGlobalUncaughtException;
  process.setUncaughtExceptionCaptureCallback =
    setUncaughtExceptionCaptureCallback;
  process.hasUncaughtExceptionCaptureCallback =
    hasUncaughtExceptionCaptureCallback;
}

const { emitWarning } = require('internal/process/warning');
process.emitWarning = emitWarning;

// We initialize the tick callbacks and the timer callbacks last during
// bootstrap to make sure that any operation done before this are synchronous.
// If any ticks or timers are scheduled before this they are unlikely to work.
{
  const { nextTick, runNextTicks } = setupTaskQueue();
  process.nextTick = nextTick;
  // Used to emulate a tick manually in the JS land.
  // A better name for this function would be `runNextTicks` but
  // it has been exposed to the process object so we keep this legacy name
  // TODO(joyeecheung): either remove it or make it public
  process._tickCallback = runNextTicks;

  const { setupTimers } = internalBinding('timers');
  const {
    processImmediate,
    processTimers,
  } = internalTimers.getTimerCallbacks(runNextTicks);
  // Sets two per-Environment callbacks that will be run from libuv:
  // - processImmediate will be run in the callback of the per-Environment
  //   check handle.
  // - processTimers will be run in the callback of the per-Environment timer.
  setupTimers(processImmediate, processTimers);
  // Note: only after this point are the timers effective
}

// Preload modules so that they are included in the builtin snapshot.
require('fs');
require('v8');
require('vm');
require('url');
require('internal/options');
if (config.hasOpenSSL) {
  require('crypto');
}

function setupPrepareStackTrace() {
  const {
    setEnhanceStackForFatalException,
    setPrepareStackTraceCallback
  } = internalBinding('errors');
  const {
    prepareStackTrace,
    fatalExceptionStackEnhancers: {
      beforeInspector,
      afterInspector
    }
  } = require('internal/errors');
  // Tell our PrepareStackTraceCallback passed to the V8 API
  // to call prepareStackTrace().
  setPrepareStackTraceCallback(prepareStackTrace);
  // Set the function used to enhance the error stack for printing
  setEnhanceStackForFatalException(beforeInspector, afterInspector);
}

function setupProcessObject() {
  const EventEmitter = require('events');
  const origProcProto = ObjectGetPrototypeOf(process);
  ObjectSetPrototypeOf(origProcProto, EventEmitter.prototype);
  FunctionPrototypeCall(EventEmitter, process);
  ObjectDefineProperty(process, SymbolToStringTag, {
    enumerable: false,
    writable: true,
    configurable: false,
    value: 'process'
  });
  // Make process globally available to users by putting it on the global proxy
  ObjectDefineProperty(globalThis, 'process', {
    value: process,
    enumerable: false,
    writable: true,
    configurable: true
  });
}

function setupGlobalProxy() {
  ObjectDefineProperty(globalThis, SymbolToStringTag, {
    value: 'global',
    writable: false,
    enumerable: false,
    configurable: true
  });
}

function setupBuffer() {
  const {
    Blob,
    Buffer,
    atob,
    btoa,
  } = require('buffer');
  const bufferBinding = internalBinding('buffer');

  // Only after this point can C++ use Buffer::New()
  bufferBinding.setBufferPrototype(Buffer.prototype);
  delete bufferBinding.setBufferPrototype;
  delete bufferBinding.zeroFill;

  ObjectDefineProperties(globalThis, {
    'Blob': {
      value: Blob,
      enumerable: false,
      writable: true,
      configurable: true,
    },
    'Buffer': {
      value: Buffer,
      enumerable: false,
      writable: true,
      configurable: true,
    },
    'atob': {
      value: atob,
      enumerable: false,
      writable: true,
      configurable: true,
    },
    'btoa': {
      value: btoa,
      enumerable: false,
      writable: true,
      configurable: true,
    },
  });
}
