dd-trace 2.30.1 → 2.32.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. package/LICENSE-3rdparty.csv +1 -1
  2. package/esbuild.js +3 -0
  3. package/index.d.ts +10 -9
  4. package/package.json +12 -12
  5. package/packages/datadog-core/src/storage/async_resource.js +1 -1
  6. package/packages/datadog-esbuild/index.js +9 -2
  7. package/packages/datadog-instrumentations/src/body-parser.js +15 -9
  8. package/packages/datadog-instrumentations/src/cucumber.js +11 -1
  9. package/packages/datadog-instrumentations/src/express.js +32 -0
  10. package/packages/datadog-instrumentations/src/helpers/instrument.js +1 -1
  11. package/packages/datadog-instrumentations/src/helpers/register.js +1 -1
  12. package/packages/datadog-instrumentations/src/http/server.js +2 -1
  13. package/packages/datadog-instrumentations/src/jest.js +6 -3
  14. package/packages/datadog-instrumentations/src/mocha.js +19 -2
  15. package/packages/datadog-instrumentations/src/playwright.js +5 -2
  16. package/packages/datadog-plugin-amqp10/src/consumer.js +1 -3
  17. package/packages/datadog-plugin-amqp10/src/producer.js +1 -3
  18. package/packages/datadog-plugin-amqplib/src/client.js +4 -3
  19. package/packages/datadog-plugin-amqplib/src/consumer.js +1 -3
  20. package/packages/datadog-plugin-amqplib/src/producer.js +1 -3
  21. package/packages/datadog-plugin-cucumber/src/index.js +6 -4
  22. package/packages/datadog-plugin-cypress/src/plugin.js +5 -1
  23. package/packages/datadog-plugin-cypress/src/support.js +4 -0
  24. package/packages/datadog-plugin-google-cloud-pubsub/src/client.js +4 -3
  25. package/packages/datadog-plugin-google-cloud-pubsub/src/consumer.js +1 -3
  26. package/packages/datadog-plugin-google-cloud-pubsub/src/producer.js +1 -3
  27. package/packages/datadog-plugin-http/src/client.js +1 -1
  28. package/packages/datadog-plugin-http/src/server.js +2 -2
  29. package/packages/datadog-plugin-http2/src/server.js +0 -5
  30. package/packages/datadog-plugin-jest/src/index.js +10 -5
  31. package/packages/datadog-plugin-kafkajs/src/consumer.js +1 -4
  32. package/packages/datadog-plugin-kafkajs/src/producer.js +1 -3
  33. package/packages/datadog-plugin-mocha/src/index.js +9 -4
  34. package/packages/datadog-plugin-playwright/src/index.js +6 -5
  35. package/packages/datadog-plugin-redis/src/index.js +16 -5
  36. package/packages/datadog-plugin-rhea/src/consumer.js +1 -3
  37. package/packages/datadog-plugin-rhea/src/producer.js +1 -5
  38. package/packages/dd-trace/src/appsec/addresses.js +0 -3
  39. package/packages/dd-trace/src/appsec/blocked_templates.js +2 -9
  40. package/packages/dd-trace/src/appsec/blocking.js +1 -1
  41. package/packages/dd-trace/src/appsec/{gateway/channels.js → channels.js} +4 -4
  42. package/packages/dd-trace/src/appsec/iast/index.js +1 -1
  43. package/packages/dd-trace/src/appsec/iast/taint-tracking/operations.js +21 -13
  44. package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +1 -1
  45. package/packages/dd-trace/src/appsec/iast/telemetry/logs.js +1 -1
  46. package/packages/dd-trace/src/appsec/index.js +87 -79
  47. package/packages/dd-trace/src/appsec/recommended.json +448 -121
  48. package/packages/dd-trace/src/appsec/remote_config/apply_states.js +7 -0
  49. package/packages/dd-trace/src/appsec/remote_config/capabilities.js +2 -0
  50. package/packages/dd-trace/src/appsec/remote_config/index.js +30 -11
  51. package/packages/dd-trace/src/appsec/remote_config/manager.js +33 -12
  52. package/packages/dd-trace/src/appsec/reporter.js +27 -58
  53. package/packages/dd-trace/src/appsec/rule_manager.js +160 -32
  54. package/packages/dd-trace/src/appsec/sdk/user_blocking.js +4 -12
  55. package/packages/dd-trace/src/appsec/waf/index.js +75 -0
  56. package/packages/dd-trace/src/appsec/waf/waf_context_wrapper.js +57 -0
  57. package/packages/dd-trace/src/appsec/waf/waf_manager.js +66 -0
  58. package/packages/dd-trace/src/config.js +18 -1
  59. package/packages/dd-trace/src/dcitm.js +2 -0
  60. package/packages/dd-trace/src/encode/0.4.js +12 -4
  61. package/packages/dd-trace/src/encode/tags-processors.js +40 -68
  62. package/packages/dd-trace/src/exporters/common/request.js +2 -2
  63. package/packages/dd-trace/src/format.js +2 -1
  64. package/packages/dd-trace/src/iitm.js +1 -1
  65. package/packages/dd-trace/src/log/channels.js +11 -12
  66. package/packages/dd-trace/src/opentracing/propagation/text_map.js +2 -2
  67. package/packages/dd-trace/src/plugin_manager.js +3 -1
  68. package/packages/dd-trace/src/plugins/client.js +3 -2
  69. package/packages/dd-trace/src/plugins/consumer.js +17 -2
  70. package/packages/dd-trace/src/plugins/inbound.js +7 -0
  71. package/packages/dd-trace/src/plugins/{outgoing.js → outbound.js} +2 -2
  72. package/packages/dd-trace/src/plugins/plugin.js +1 -1
  73. package/packages/dd-trace/src/plugins/producer.js +17 -2
  74. package/packages/dd-trace/src/plugins/server.js +2 -2
  75. package/packages/dd-trace/src/plugins/tracing.js +11 -0
  76. package/packages/dd-trace/src/plugins/util/test.js +19 -1
  77. package/packages/dd-trace/src/profiling/profilers/cpu.js +1 -1
  78. package/packages/dd-trace/src/ritm.js +1 -1
  79. package/packages/dd-trace/src/service-naming/index.js +41 -0
  80. package/packages/dd-trace/src/service-naming/schemas/definition.js +28 -0
  81. package/packages/dd-trace/src/service-naming/schemas/index.js +6 -0
  82. package/packages/dd-trace/src/service-naming/schemas/v0.js +66 -0
  83. package/packages/dd-trace/src/service-naming/schemas/v1.js +58 -0
  84. package/packages/dd-trace/src/span_stats.js +1 -1
  85. package/packages/dd-trace/src/telemetry/dependencies.js +1 -1
  86. package/packages/dd-trace/src/telemetry/index.js +1 -1
  87. package/packages/diagnostics_channel/index.js +3 -0
  88. package/packages/diagnostics_channel/src/index.js +57 -0
  89. package/packages/dd-trace/src/appsec/callbacks/ddwaf.js +0 -137
  90. package/packages/dd-trace/src/appsec/callbacks/index.js +0 -7
  91. package/packages/dd-trace/src/appsec/gateway/als.js +0 -6
  92. package/packages/dd-trace/src/appsec/gateway/engine/engine.js +0 -140
  93. package/packages/dd-trace/src/appsec/gateway/engine/index.js +0 -51
  94. package/packages/dd-trace/src/appsec/gateway/engine/runner.js +0 -42
  95. package/packages/dd-trace/src/instrumenter.js +0 -203
  96. package/packages/dd-trace/src/loader.js +0 -131
  97. package/packages/dd-trace/src/plugins/incoming.js +0 -7
@@ -0,0 +1,41 @@
1
+ const { schemaDefinitions } = require('./schemas')
2
+
3
+ const kindMap = {
4
+ messaging: {
5
+ client: 'controlPlane',
6
+ consumer: 'inbound',
7
+ producer: 'outbound'
8
+ }
9
+ }
10
+
11
+ class SchemaManager {
12
+ constructor () {
13
+ this.schemas = schemaDefinitions
14
+ this.config = { spanAttributeSchema: 'v0' }
15
+ }
16
+
17
+ get schema () {
18
+ return this.schemas[this.version]
19
+ }
20
+
21
+ get version () {
22
+ return this.config.spanAttributeSchema
23
+ }
24
+
25
+ opName (type, kind, plugin, opNameArgs) {
26
+ return this.schema.getOpName(type, kindMap[type][kind], plugin, opNameArgs)
27
+ }
28
+
29
+ serviceName (type, kind, plugin, serviceNameArgs) {
30
+ return this.schema.getServiceName(type, kindMap[type][kind], plugin, serviceNameArgs)
31
+ }
32
+
33
+ configure (config = {}) {
34
+ this.config = config
35
+ Object.values(this.schemas).forEach(schemaDef => {
36
+ schemaDef.configure(config)
37
+ })
38
+ }
39
+ }
40
+
41
+ module.exports = new SchemaManager()
@@ -0,0 +1,28 @@
1
+ class SchemaDefinition {
2
+ constructor (schema) {
3
+ this.schema = schema
4
+ }
5
+
6
+ getSchemaItem (type, subType, plugin) {
7
+ const schema = this.schema
8
+ if (schema && schema[type] && schema[type][subType] && schema[type][subType][plugin]) {
9
+ return schema[type][subType][plugin]
10
+ }
11
+ }
12
+
13
+ getOpName (type, subType, plugin, opNameArgs) {
14
+ const item = this.getSchemaItem(type, subType, plugin)
15
+ return item.opName(opNameArgs)
16
+ }
17
+
18
+ getServiceName (type, subType, plugin, serviceNameArgs) {
19
+ const item = this.getSchemaItem(type, subType, plugin)
20
+ return item.serviceName(this.service, serviceNameArgs)
21
+ }
22
+
23
+ configure ({ service }) {
24
+ this.service = service
25
+ }
26
+ }
27
+
28
+ module.exports = SchemaDefinition
@@ -0,0 +1,6 @@
1
+ const v0 = require('./v0')
2
+ const v1 = require('./v1')
3
+
4
+ module.exports = {
5
+ schemaDefinitions: { v0, v1 }
6
+ }
@@ -0,0 +1,66 @@
1
+ const SchemaDefinition = require('./definition')
2
+
3
+ function amqpServiceName (service) {
4
+ return `${service}-amqp`
5
+ }
6
+
7
+ const schema = {
8
+ messaging: {
9
+ outbound: {
10
+ amqplib: {
11
+ opName: () => 'amqp.command',
12
+ serviceName: amqpServiceName
13
+ },
14
+ amqp10: {
15
+ opName: () => 'amqp.send',
16
+ serviceName: amqpServiceName
17
+ },
18
+ 'google-cloud-pubsub': {
19
+ opName: () => 'pubsub.request',
20
+ serviceName: service => `${service}-pubsub`
21
+ },
22
+ kafkajs: {
23
+ opName: () => 'kafka.produce',
24
+ serviceName: service => `${service}-kafka`
25
+ },
26
+ rhea: {
27
+ opName: () => 'amqp.send',
28
+ serviceName: service => `${service}-amqp-producer`
29
+ }
30
+ },
31
+ inbound: {
32
+ amqplib: {
33
+ opName: () => 'amqp.command',
34
+ serviceName: amqpServiceName
35
+ },
36
+ amqp10: {
37
+ opName: () => 'amqp.receive',
38
+ serviceName: amqpServiceName
39
+ },
40
+ 'google-cloud-pubsub': {
41
+ opName: () => 'pubsub.receive',
42
+ serviceName: service => service
43
+ },
44
+ kafkajs: {
45
+ opName: () => 'kafka.consume',
46
+ serviceName: service => `${service}-kafka`
47
+ },
48
+ rhea: {
49
+ opName: () => 'amqp.receive',
50
+ serviceName: service => service
51
+ }
52
+ },
53
+ controlPlane: {
54
+ amqplib: {
55
+ opName: () => 'amqp.command',
56
+ serviceName: amqpServiceName
57
+ },
58
+ 'google-cloud-pubsub': {
59
+ opName: () => 'pubsub.request',
60
+ serviceName: service => `${service}-pubsub`
61
+ }
62
+ }
63
+ }
64
+ }
65
+
66
+ module.exports = new SchemaDefinition(schema)
@@ -0,0 +1,58 @@
1
+ const SchemaDefinition = require('./definition')
2
+
3
+ function identityService (service) {
4
+ return service
5
+ }
6
+
7
+ const amqpInbound = {
8
+ opName: () => 'amqp.process',
9
+ serviceName: identityService
10
+ }
11
+
12
+ const amqpOutbound = {
13
+ opName: () => 'amqp.send',
14
+ serviceName: identityService
15
+ }
16
+
17
+ const schema = {
18
+ messaging: {
19
+ outbound: {
20
+ amqplib: amqpOutbound,
21
+ amqp10: amqpOutbound,
22
+ 'google-cloud-pubsub': {
23
+ opName: () => 'gcp.pubsub.send',
24
+ serviceName: identityService
25
+ },
26
+ kafkajs: {
27
+ opName: () => 'kafka.send',
28
+ serviceName: identityService
29
+ },
30
+ rhea: amqpOutbound
31
+ },
32
+ inbound: {
33
+ amqplib: amqpInbound,
34
+ amqp10: amqpInbound,
35
+ 'google-cloud-pubsub': {
36
+ opName: () => 'gcp.pubsub.process',
37
+ serviceName: identityService
38
+ },
39
+ kafkajs: {
40
+ opName: () => 'kafka.process',
41
+ serviceName: identityService
42
+ },
43
+ rhea: amqpInbound
44
+ },
45
+ controlPlane: {
46
+ amqplib: {
47
+ opName: () => 'amqp.command',
48
+ serviceName: identityService
49
+ },
50
+ 'google-cloud-pubsub': {
51
+ opName: () => 'gcp.pubsub.request',
52
+ serviceName: identityService
53
+ }
54
+ }
55
+ }
56
+ }
57
+
58
+ module.exports = new SchemaDefinition(schema)
@@ -28,7 +28,7 @@ class SpanAggStats {
28
28
  }
29
29
 
30
30
  record (span) {
31
- const durationNs = span._duration * 1e6
31
+ const durationNs = span.duration
32
32
  this.hits++
33
33
  this.duration += durationNs
34
34
 
@@ -4,7 +4,7 @@ const path = require('path')
4
4
  const parse = require('module-details-from-path')
5
5
  const requirePackageJson = require('../require-package-json')
6
6
  const { sendData } = require('./send-data')
7
- const dc = require('diagnostics_channel')
7
+ const dc = require('../../../diagnostics_channel')
8
8
  const { fileURLToPath } = require('url')
9
9
 
10
10
  const savedDependencies = new Set()
@@ -1,7 +1,7 @@
1
1
  'use strict'
2
2
 
3
3
  const tracerVersion = require('../../../../package.json').version
4
- const dc = require('diagnostics_channel')
4
+ const dc = require('../../../diagnostics_channel')
5
5
  const os = require('os')
6
6
  const dependencies = require('./dependencies')
7
7
  const { sendData } = require('./send-data')
@@ -0,0 +1,3 @@
1
+ 'use strict'
2
+
3
+ module.exports = require('./src')
@@ -0,0 +1,57 @@
1
+ 'use strict'
2
+
3
+ const {
4
+ Channel,
5
+ channel
6
+ } = require('diagnostics_channel') // eslint-disable-line n/no-restricted-require
7
+
8
+ const [major, minor] = process.versions.node.split('.')
9
+ const channels = new WeakSet()
10
+
11
+ // Our own DC with a limited subset of functionality stable across Node versions.
12
+ // TODO: Move the rest of the polyfill here.
13
+ // TODO: Switch to using global subscribe/unsubscribe/hasSubscribers.
14
+ const dc = { channel }
15
+
16
+ // Prevent going to 0 subscribers to avoid bug in Node.
17
+ // See https://github.com/nodejs/node/pull/47520
18
+ if (major === '19' && minor === '9') {
19
+ dc.channel = function () {
20
+ const ch = channel.apply(this, arguments)
21
+
22
+ if (!channels.has(ch)) {
23
+ const subscribe = ch.subscribe
24
+ const unsubscribe = ch.unsubscribe
25
+
26
+ ch.subscribe = function () {
27
+ delete ch.subscribe
28
+ delete ch.unsubscribe
29
+
30
+ const result = subscribe.apply(this, arguments)
31
+
32
+ this.subscribe(() => {}) // Keep it active forever.
33
+
34
+ return result
35
+ }
36
+
37
+ if (ch.unsubscribe === Channel.prototype.unsubscribe) {
38
+ // Needed because another subscriber could have subscribed to something
39
+ // that we unsubscribe to before the library is loaded.
40
+ ch.unsubscribe = function () {
41
+ delete ch.subscribe
42
+ delete ch.unsubscribe
43
+
44
+ this.subscribe(() => {}) // Keep it active forever.
45
+
46
+ return unsubscribe.apply(this, arguments)
47
+ }
48
+ }
49
+
50
+ channels.add(ch)
51
+ }
52
+
53
+ return ch
54
+ }
55
+ }
56
+
57
+ module.exports = dc
@@ -1,137 +0,0 @@
1
- 'use strict'
2
-
3
- const log = require('../../log')
4
- const addresses = require('../addresses')
5
- const Gateway = require('../gateway/engine')
6
- const Reporter = require('../reporter')
7
-
8
- const validAddressSet = new Set(Object.values(addresses))
9
-
10
- // TODO: put reusable code in a base class
11
- class WAFCallback {
12
- static loadDDWAF (rules, config) {
13
- try {
14
- // require in `try/catch` because this can throw at require time
15
- const { DDWAF } = require('@datadog/native-appsec')
16
-
17
- return new DDWAF(rules, config)
18
- } catch (err) {
19
- log.error('AppSec could not load native package. In-app WAF features will not be available.')
20
-
21
- throw err
22
- }
23
- }
24
-
25
- constructor (rules, config) {
26
- const { wafTimeout, obfuscatorKeyRegex, obfuscatorValueRegex } = config
27
-
28
- this.ddwaf = WAFCallback.loadDDWAF(rules, { obfuscatorKeyRegex, obfuscatorValueRegex })
29
-
30
- this.wafTimeout = wafTimeout
31
-
32
- Reporter.metricsQueue.set('_dd.appsec.waf.version', this.ddwaf.constructor.version())
33
-
34
- const { loaded, failed, errors } = this.ddwaf.rulesInfo
35
-
36
- Reporter.metricsQueue.set('_dd.appsec.event_rules.loaded', loaded)
37
- Reporter.metricsQueue.set('_dd.appsec.event_rules.error_count', failed)
38
- if (failed) Reporter.metricsQueue.set('_dd.appsec.event_rules.errors', JSON.stringify(errors))
39
-
40
- Reporter.metricsQueue.set('manual.keep', 'true')
41
-
42
- this.wafContextCache = new WeakMap()
43
-
44
- // closures are faster than binds
45
- const self = this
46
- const method = (params, store) => {
47
- return self.action(params, store)
48
- }
49
-
50
- // might be its own class with more info later
51
- const callback = { method }
52
-
53
- const subscribedAddresses = new Set()
54
-
55
- for (const rule of rules.rules) {
56
- for (const condition of rule.conditions) {
57
- for (const input of condition.parameters.inputs) {
58
- const address = input.address.split(':', 2)[0]
59
-
60
- if (!validAddressSet.has(address) || subscribedAddresses.has(address)) continue
61
-
62
- subscribedAddresses.add(address)
63
-
64
- Gateway.manager.addSubscription({ addresses: [address], callback })
65
- }
66
- }
67
- }
68
- }
69
-
70
- action (params, store) {
71
- let wafContext
72
-
73
- if (store) {
74
- const key = store.get('context')
75
-
76
- if (key) {
77
- if (this.wafContextCache.has(key)) {
78
- wafContext = this.wafContextCache.get(key)
79
- } else {
80
- wafContext = this.ddwaf.createContext()
81
- this.wafContextCache.set(key, wafContext)
82
- }
83
- }
84
- }
85
-
86
- if (!wafContext || wafContext.disposed) {
87
- wafContext = this.ddwaf.createContext()
88
- }
89
-
90
- // cast status code to string
91
- if (params[addresses.HTTP_INCOMING_RESPONSE_CODE]) {
92
- params[addresses.HTTP_INCOMING_RESPONSE_CODE] = params[addresses.HTTP_INCOMING_RESPONSE_CODE] + ''
93
- }
94
-
95
- try {
96
- // TODO: possible optimizaion: only send params that haven't already been sent to this wafContext
97
- const start = process.hrtime.bigint()
98
-
99
- const result = wafContext.run(params, this.wafTimeout)
100
-
101
- result.durationExt = parseInt(process.hrtime.bigint() - start)
102
-
103
- return this.applyResult(result, store)
104
- } catch (err) {
105
- log.error('Error while running the AppSec WAF')
106
- log.error(err)
107
- } finally {
108
- wafContext.dispose()
109
- }
110
- }
111
-
112
- applyResult (result, store) {
113
- Reporter.reportMetrics({
114
- duration: result.totalRuntime / 1e3,
115
- durationExt: result.durationExt / 1e3,
116
- rulesVersion: this.ddwaf.rulesInfo.version
117
- }, store)
118
-
119
- if (result.data && result.data !== '[]') {
120
- Reporter.reportAttack(result.data, store)
121
- }
122
-
123
- return result.actions
124
- }
125
-
126
- updateRuleData (ruleData) {
127
- this.ddwaf.updateRuleData(ruleData)
128
- }
129
-
130
- clear () {
131
- this.ddwaf.dispose()
132
-
133
- this.wafContextCache = new WeakMap()
134
- }
135
- }
136
-
137
- module.exports = WAFCallback
@@ -1,7 +0,0 @@
1
- 'use strict'
2
-
3
- // lazy loading
4
- // TODO: cache the returned value
5
- module.exports = {
6
- get DDWAF () { return require('./ddwaf') }
7
- }
@@ -1,6 +0,0 @@
1
- 'use strict'
2
-
3
- // TODO: use datadog-core storage instead
4
- const { AsyncLocalStorage } = require('async_hooks')
5
-
6
- module.exports = new AsyncLocalStorage()
@@ -1,140 +0,0 @@
1
- 'use strict'
2
-
3
- const Runner = require('./runner')
4
-
5
- const MAX_CONTEXT_SIZE = 1024
6
-
7
- class SubscriptionManager {
8
- constructor () {
9
- this.addressToSubscriptions = new Map()
10
- this.addresses = new Set()
11
- this.subscriptions = new Set()
12
- }
13
-
14
- clear () {
15
- this.addressToSubscriptions = new Map()
16
- this.addresses = new Set()
17
- this.subscriptions = new Set()
18
- }
19
-
20
- addSubscription (subscription) {
21
- if (!subscription.addresses.length || this.subscriptions.has(subscription)) return
22
-
23
- for (let i = 0; i < subscription.addresses.length; ++i) {
24
- const address = subscription.addresses[i]
25
-
26
- this.addresses.add(address)
27
-
28
- const list = this.addressToSubscriptions.get(address)
29
-
30
- if (list === undefined) {
31
- this.addressToSubscriptions.set(address, [subscription])
32
- } else {
33
- list.push(subscription)
34
- }
35
- }
36
-
37
- this.subscriptions.add(subscription)
38
- }
39
-
40
- matchSubscriptions (newAddresses, allAddresses) {
41
- const addresses = new Set()
42
- const subscriptions = new Set()
43
- const knownSubscriptions = new Set()
44
-
45
- // TODO: possible optimization: collect matchedSubscriptions on the fly in Context#setValue
46
- newAddresses.forEach((newAddress) => {
47
- const matchedSubscriptions = this.addressToSubscriptions.get(newAddress)
48
-
49
- if (matchedSubscriptions === undefined) return
50
-
51
- for (let j = 0; j < matchedSubscriptions.length; ++j) {
52
- const subscription = matchedSubscriptions[j]
53
-
54
- if (knownSubscriptions.has(subscription) === true) continue
55
- knownSubscriptions.add(subscription)
56
-
57
- const isFulfilled = subscription.addresses.every(allAddresses.has, allAddresses)
58
-
59
- if (isFulfilled === true) {
60
- for (let k = 0; k < subscription.addresses.length; ++k) {
61
- addresses.add(subscription.addresses[k])
62
- }
63
-
64
- subscriptions.add(subscription)
65
- }
66
- }
67
- })
68
-
69
- return { addresses, subscriptions }
70
- }
71
-
72
- dispatch (newAddresses, allAddresses, context) {
73
- const matches = this.matchSubscriptions(newAddresses, allAddresses)
74
-
75
- // TODO: possible optimization
76
- // check if matches.subscriptions is empty here instead of in runner.js
77
-
78
- const params = {}
79
-
80
- matches.addresses.forEach((address) => {
81
- params[address] = context.resolve(address)
82
- })
83
-
84
- return Runner.runSubscriptions(matches.subscriptions, params)
85
- }
86
- }
87
-
88
- class Context {
89
- static setManager (manager) {
90
- this.manager = manager
91
- }
92
-
93
- constructor () {
94
- // TODO: this probably don't need to be a Map()
95
- this.store = new Map()
96
- this.allAddresses = new Set()
97
- this.newAddresses = new Set()
98
- }
99
-
100
- clear () {
101
- this.store = new Map()
102
- this.allAddresses = new Set()
103
- this.newAddresses = new Set()
104
- }
105
-
106
- setValue (address, value) {
107
- if (this.allAddresses.size >= MAX_CONTEXT_SIZE) return this
108
-
109
- // cannot optimize for objects because they're pointers
110
- if (typeof value !== 'object') {
111
- const oldValue = this.store.get(address)
112
- if (oldValue === value) return this
113
- }
114
-
115
- this.store.set(address, value)
116
- this.allAddresses.add(address)
117
- this.newAddresses.add(address)
118
-
119
- return this
120
- }
121
-
122
- dispatch () {
123
- if (this.newAddresses.size === 0) return []
124
-
125
- const result = Context.manager.dispatch(this.newAddresses, this.allAddresses, this)
126
-
127
- this.newAddresses.clear()
128
-
129
- return result
130
- }
131
-
132
- resolve (address) {
133
- return this.store.get(address)
134
- }
135
- }
136
-
137
- module.exports = {
138
- SubscriptionManager,
139
- Context
140
- }
@@ -1,51 +0,0 @@
1
- 'use strict'
2
-
3
- const { SubscriptionManager, Context } = require('./engine')
4
- const als = require('../als')
5
-
6
- const manager = new SubscriptionManager()
7
- Context.setManager(manager)
8
-
9
- function startContext () {
10
- const store = new Map()
11
-
12
- store.set('context', new Context())
13
-
14
- als.enterWith(store)
15
-
16
- return store
17
- }
18
-
19
- function getContext () {
20
- const store = als.getStore()
21
-
22
- return store && store.get('context')
23
- }
24
-
25
- function needsAddress (address) {
26
- return manager.addresses.has(address)
27
- }
28
-
29
- function propagate (data, context = getContext()) {
30
- if (!context) return
31
-
32
- const keys = Object.keys(data)
33
-
34
- for (let i = 0; i < keys.length; ++i) {
35
- const key = keys[i]
36
-
37
- if (needsAddress(key)) {
38
- context.setValue(key, data[key])
39
- }
40
- }
41
-
42
- return context.dispatch()
43
- }
44
-
45
- module.exports = {
46
- manager,
47
- startContext,
48
- getContext,
49
- needsAddress,
50
- propagate
51
- }
@@ -1,42 +0,0 @@
1
- 'use strict'
2
-
3
- const als = require('../als')
4
- const log = require('../../../log')
5
-
6
- let lock = false // lock to prevent recursive calls to runSubscriptions
7
-
8
- function runSubscriptions (subscriptions, params) {
9
- const results = []
10
-
11
- if (lock || !subscriptions.size) return results
12
- lock = true
13
-
14
- const store = als.getStore()
15
-
16
- // TODO: possible optimization
17
- // can we deduplicate those before ?
18
- const executedCallbacks = new Set()
19
-
20
- for (const subscription of subscriptions) {
21
- if (executedCallbacks.has(subscription.callback)) continue
22
- executedCallbacks.add(subscription.callback)
23
-
24
- let result
25
-
26
- try {
27
- result = subscription.callback.method(params, store)
28
- } catch (err) {
29
- log.warn(`Error running subscription ${err}`)
30
- }
31
-
32
- results.push(result)
33
- }
34
-
35
- lock = false
36
-
37
- return results
38
- }
39
-
40
- module.exports = {
41
- runSubscriptions
42
- }