aboutsummaryrefslogtreecommitdiffstats
path: root/vanilla/node_modules/undici/lib/util/runtime-features.js
blob: 3e62dc004d3352dcb36b44ad9bff4688d293cf12 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
'use strict'

/** @typedef {`node:${string}`} NodeModuleName */

/** @type {Record<NodeModuleName, () => any>} */
const lazyLoaders = {
  __proto__: null,
  'node:crypto': () => require('node:crypto'),
  'node:sqlite': () => require('node:sqlite'),
  'node:worker_threads': () => require('node:worker_threads'),
  'node:zlib': () => require('node:zlib')
}

/**
 * @param {NodeModuleName} moduleName
 * @returns {boolean}
 */
function detectRuntimeFeatureByNodeModule (moduleName) {
  try {
    lazyLoaders[moduleName]()
    return true
  } catch (err) {
    if (err.code !== 'ERR_UNKNOWN_BUILTIN_MODULE' && err.code !== 'ERR_NO_CRYPTO') {
      throw err
    }
    return false
  }
}

/**
 * @param {NodeModuleName} moduleName
 * @param {string} property
 * @returns {boolean}
 */
function detectRuntimeFeatureByExportedProperty (moduleName, property) {
  const module = lazyLoaders[moduleName]()
  return typeof module[property] !== 'undefined'
}

const runtimeFeaturesByExportedProperty = /** @type {const} */ (['markAsUncloneable', 'zstd'])

/** @type {Record<RuntimeFeatureByExportedProperty, [NodeModuleName, string]>} */
const exportedPropertyLookup = {
  markAsUncloneable: ['node:worker_threads', 'markAsUncloneable'],
  zstd: ['node:zlib', 'createZstdDecompress']
}

/** @typedef {typeof runtimeFeaturesByExportedProperty[number]} RuntimeFeatureByExportedProperty */

const runtimeFeaturesAsNodeModule = /** @type {const} */ (['crypto', 'sqlite'])
/** @typedef {typeof runtimeFeaturesAsNodeModule[number]} RuntimeFeatureByNodeModule */

const features = /** @type {const} */ ([
  ...runtimeFeaturesAsNodeModule,
  ...runtimeFeaturesByExportedProperty
])

/** @typedef {typeof features[number]} Feature */

/**
 * @param {Feature} feature
 * @returns {boolean}
 */
function detectRuntimeFeature (feature) {
  if (runtimeFeaturesAsNodeModule.includes(/** @type {RuntimeFeatureByNodeModule} */ (feature))) {
    return detectRuntimeFeatureByNodeModule(`node:${feature}`)
  } else if (runtimeFeaturesByExportedProperty.includes(/** @type {RuntimeFeatureByExportedProperty} */ (feature))) {
    const [moduleName, property] = exportedPropertyLookup[feature]
    return detectRuntimeFeatureByExportedProperty(moduleName, property)
  }
  throw new TypeError(`unknown feature: ${feature}`)
}

/**
 * @class
 * @name RuntimeFeatures
 */
class RuntimeFeatures {
  /** @type {Map<Feature, boolean>} */
  #map = new Map()

  /**
   * Clears all cached feature detections.
   */
  clear () {
    this.#map.clear()
  }

  /**
   * @param {Feature} feature
   * @returns {boolean}
   */
  has (feature) {
    return (
      this.#map.get(feature) ?? this.#detectRuntimeFeature(feature)
    )
  }

  /**
   * @param {Feature} feature
   * @param {boolean} value
   */
  set (feature, value) {
    if (features.includes(feature) === false) {
      throw new TypeError(`unknown feature: ${feature}`)
    }
    this.#map.set(feature, value)
  }

  /**
   * @param {Feature} feature
   * @returns {boolean}
   */
  #detectRuntimeFeature (feature) {
    const result = detectRuntimeFeature(feature)
    this.#map.set(feature, result)
    return result
  }
}

const instance = new RuntimeFeatures()

module.exports.runtimeFeatures = instance
module.exports.default = instance