@depup/elastic-apm-node 4.15.0-depup.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.
- package/LICENSE +26 -0
- package/NOTICE.md +442 -0
- package/README.md +48 -0
- package/changes.json +78 -0
- package/index.d.ts +398 -0
- package/index.js +11 -0
- package/lib/InflightEventSet.js +53 -0
- package/lib/activation-method.js +119 -0
- package/lib/agent.js +941 -0
- package/lib/apm-client/apm-client.js +313 -0
- package/lib/apm-client/http-apm-client/CHANGELOG.md +271 -0
- package/lib/apm-client/http-apm-client/README.md +485 -0
- package/lib/apm-client/http-apm-client/central-config.js +41 -0
- package/lib/apm-client/http-apm-client/container-info.js +111 -0
- package/lib/apm-client/http-apm-client/detect-hostname.js +96 -0
- package/lib/apm-client/http-apm-client/index.js +1975 -0
- package/lib/apm-client/http-apm-client/logging.js +31 -0
- package/lib/apm-client/http-apm-client/ndjson.js +20 -0
- package/lib/apm-client/http-apm-client/truncate.js +434 -0
- package/lib/apm-client/noop-apm-client.js +73 -0
- package/lib/async-hooks-polyfill.js +58 -0
- package/lib/cloud-metadata/aws.js +175 -0
- package/lib/cloud-metadata/azure.js +123 -0
- package/lib/cloud-metadata/callback-coordination.js +159 -0
- package/lib/cloud-metadata/gcp.js +133 -0
- package/lib/cloud-metadata/index.js +175 -0
- package/lib/config/config.js +458 -0
- package/lib/config/normalizers.js +701 -0
- package/lib/config/schema.js +1007 -0
- package/lib/constants.js +35 -0
- package/lib/errors.js +303 -0
- package/lib/filters/sanitize-field-names.js +69 -0
- package/lib/http-request.js +249 -0
- package/lib/instrumentation/azure-functions.js +519 -0
- package/lib/instrumentation/context.js +56 -0
- package/lib/instrumentation/dropped-spans-stats.js +112 -0
- package/lib/instrumentation/elasticsearch-shared.js +63 -0
- package/lib/instrumentation/express-utils.js +91 -0
- package/lib/instrumentation/generic-span.js +322 -0
- package/lib/instrumentation/http-shared.js +424 -0
- package/lib/instrumentation/ids.js +39 -0
- package/lib/instrumentation/index.js +1127 -0
- package/lib/instrumentation/modules/@apollo/server.js +30 -0
- package/lib/instrumentation/modules/@aws-sdk/client-dynamodb.js +143 -0
- package/lib/instrumentation/modules/@aws-sdk/client-s3.js +230 -0
- package/lib/instrumentation/modules/@aws-sdk/client-sns.js +197 -0
- package/lib/instrumentation/modules/@aws-sdk/client-sqs.js +336 -0
- package/lib/instrumentation/modules/@elastic/elasticsearch.js +343 -0
- package/lib/instrumentation/modules/@hapi/hapi.js +221 -0
- package/lib/instrumentation/modules/@opentelemetry/api.js +86 -0
- package/lib/instrumentation/modules/@opentelemetry/sdk-metrics.js +79 -0
- package/lib/instrumentation/modules/@redis/client/dist/lib/client/commands-queue.js +178 -0
- package/lib/instrumentation/modules/@redis/client/dist/lib/client/index.js +49 -0
- package/lib/instrumentation/modules/@smithy/smithy-client.js +198 -0
- package/lib/instrumentation/modules/_lambda-handler.js +40 -0
- package/lib/instrumentation/modules/apollo-server-core.js +49 -0
- package/lib/instrumentation/modules/aws-sdk/dynamodb.js +155 -0
- package/lib/instrumentation/modules/aws-sdk/s3.js +184 -0
- package/lib/instrumentation/modules/aws-sdk/sns.js +232 -0
- package/lib/instrumentation/modules/aws-sdk/sqs.js +361 -0
- package/lib/instrumentation/modules/aws-sdk.js +76 -0
- package/lib/instrumentation/modules/bluebird.js +93 -0
- package/lib/instrumentation/modules/cassandra-driver.js +280 -0
- package/lib/instrumentation/modules/elasticsearch.js +191 -0
- package/lib/instrumentation/modules/express-graphql.js +66 -0
- package/lib/instrumentation/modules/express-queue.js +28 -0
- package/lib/instrumentation/modules/express.js +162 -0
- package/lib/instrumentation/modules/fastify.js +172 -0
- package/lib/instrumentation/modules/finalhandler.js +41 -0
- package/lib/instrumentation/modules/generic-pool.js +85 -0
- package/lib/instrumentation/modules/graphql.js +256 -0
- package/lib/instrumentation/modules/handlebars.js +22 -0
- package/lib/instrumentation/modules/http.js +112 -0
- package/lib/instrumentation/modules/http2.js +320 -0
- package/lib/instrumentation/modules/https.js +68 -0
- package/lib/instrumentation/modules/ioredis.js +94 -0
- package/lib/instrumentation/modules/jade.js +18 -0
- package/lib/instrumentation/modules/kafkajs.js +476 -0
- package/lib/instrumentation/modules/knex.js +91 -0
- package/lib/instrumentation/modules/koa-router.js +74 -0
- package/lib/instrumentation/modules/koa.js +15 -0
- package/lib/instrumentation/modules/memcached.js +99 -0
- package/lib/instrumentation/modules/mimic-response.js +45 -0
- package/lib/instrumentation/modules/mongodb/lib/cmap/connection_pool.js +40 -0
- package/lib/instrumentation/modules/mongodb-core.js +206 -0
- package/lib/instrumentation/modules/mongodb.js +259 -0
- package/lib/instrumentation/modules/mysql.js +200 -0
- package/lib/instrumentation/modules/mysql2.js +140 -0
- package/lib/instrumentation/modules/pg.js +148 -0
- package/lib/instrumentation/modules/pug.js +18 -0
- package/lib/instrumentation/modules/redis.js +176 -0
- package/lib/instrumentation/modules/restify.js +52 -0
- package/lib/instrumentation/modules/tedious.js +159 -0
- package/lib/instrumentation/modules/undici.js +270 -0
- package/lib/instrumentation/modules/ws.js +59 -0
- package/lib/instrumentation/noop-transaction.js +81 -0
- package/lib/instrumentation/run-context/AbstractRunContextManager.js +215 -0
- package/lib/instrumentation/run-context/AsyncHooksRunContextManager.js +106 -0
- package/lib/instrumentation/run-context/AsyncLocalStorageRunContextManager.js +73 -0
- package/lib/instrumentation/run-context/BasicRunContextManager.js +82 -0
- package/lib/instrumentation/run-context/RunContext.js +151 -0
- package/lib/instrumentation/run-context/index.js +23 -0
- package/lib/instrumentation/shimmer.js +123 -0
- package/lib/instrumentation/span-compression.js +239 -0
- package/lib/instrumentation/span.js +621 -0
- package/lib/instrumentation/template-shared.js +43 -0
- package/lib/instrumentation/timer.js +84 -0
- package/lib/instrumentation/transaction.js +571 -0
- package/lib/lambda.js +992 -0
- package/lib/load-source-map.js +100 -0
- package/lib/logging.js +212 -0
- package/lib/metrics/index.js +92 -0
- package/lib/metrics/platforms/generic/index.js +40 -0
- package/lib/metrics/platforms/generic/process-cpu.js +22 -0
- package/lib/metrics/platforms/generic/process-top.js +157 -0
- package/lib/metrics/platforms/generic/stats.js +34 -0
- package/lib/metrics/platforms/generic/system-cpu.js +51 -0
- package/lib/metrics/platforms/linux/index.js +19 -0
- package/lib/metrics/platforms/linux/stats.js +213 -0
- package/lib/metrics/queue.js +90 -0
- package/lib/metrics/registry.js +52 -0
- package/lib/metrics/reporter.js +119 -0
- package/lib/metrics/runtime.js +77 -0
- package/lib/middleware/connect.js +16 -0
- package/lib/opentelemetry-bridge/OTelBridgeNonRecordingSpan.js +150 -0
- package/lib/opentelemetry-bridge/OTelBridgeRunContext.js +124 -0
- package/lib/opentelemetry-bridge/OTelContextManager.js +82 -0
- package/lib/opentelemetry-bridge/OTelSpan.js +344 -0
- package/lib/opentelemetry-bridge/OTelTracer.js +201 -0
- package/lib/opentelemetry-bridge/OTelTracerProvider.js +25 -0
- package/lib/opentelemetry-bridge/README.md +244 -0
- package/lib/opentelemetry-bridge/index.js +15 -0
- package/lib/opentelemetry-bridge/oblog.js +23 -0
- package/lib/opentelemetry-bridge/opentelemetry-core-mini/README.md +3 -0
- package/lib/opentelemetry-bridge/opentelemetry-core-mini/internal/validators.js +52 -0
- package/lib/opentelemetry-bridge/opentelemetry-core-mini/trace/TraceState.js +109 -0
- package/lib/opentelemetry-bridge/otelutils.js +99 -0
- package/lib/opentelemetry-bridge/setup.js +76 -0
- package/lib/opentelemetry-metrics/ElasticApmMetricExporter.js +285 -0
- package/lib/opentelemetry-metrics/index.js +50 -0
- package/lib/parsers.js +225 -0
- package/lib/propwrap.js +147 -0
- package/lib/stacktraces.js +537 -0
- package/lib/symbols.js +15 -0
- package/lib/tracecontext/index.js +118 -0
- package/lib/tracecontext/traceparent.js +185 -0
- package/lib/tracecontext/tracestate.js +388 -0
- package/lib/wildcard-matcher.js +52 -0
- package/loader.mjs +7 -0
- package/package.json +299 -0
- package/start.d.ts +8 -0
- package/start.js +29 -0
- package/types/aws-lambda.d.ts +98 -0
- package/types/connect.d.ts +23 -0
package/lib/parsers.js
ADDED
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright Elasticsearch B.V. and other contributors where applicable.
|
|
3
|
+
* Licensed under the BSD 2-Clause License; you may not use this file except in
|
|
4
|
+
* compliance with the BSD 2-Clause License.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
const url = require('url');
|
|
10
|
+
|
|
11
|
+
const basicAuth = require('basic-auth');
|
|
12
|
+
const getUrlFromRequest = require('original-url');
|
|
13
|
+
const parseHttpHeadersFromReqOrRes = require('http-headers');
|
|
14
|
+
const cookie = require('cookie');
|
|
15
|
+
const stringify = require('fast-safe-stringify');
|
|
16
|
+
|
|
17
|
+
const REDACTED = require('./constants').REDACTED;
|
|
18
|
+
const {
|
|
19
|
+
redactKeysFromObject,
|
|
20
|
+
redactKeysFromPostedFormVariables,
|
|
21
|
+
} = require('./filters/sanitize-field-names');
|
|
22
|
+
|
|
23
|
+
// When redacting individual cookie field values, this string is used instead
|
|
24
|
+
// of `[REDACTED]`. The APM spec says:
|
|
25
|
+
// > The replacement string SHOULD be `[REDACTED]`.
|
|
26
|
+
// We diverge from spec here because, for better or worse, the `cookie` module
|
|
27
|
+
// does `encodeURIComponent/decodeURIComponent` encoding on cookie fields. If we
|
|
28
|
+
// used the brackets, then the reconstructed cookie would look like
|
|
29
|
+
// `foo=bar; session-id=%5BREDACTED%5D`, which isn't helpful.
|
|
30
|
+
const COOKIE_VAL_REDACTED = 'REDACTED';
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Extract appropriate `{transaction,error}.context.request` from an HTTP
|
|
34
|
+
* request object. This handles header and body capture and redaction
|
|
35
|
+
* according to the agent config.
|
|
36
|
+
*
|
|
37
|
+
* @param {Object} req - Typically `req` is a Node.js `http.IncomingMessage`
|
|
38
|
+
* (https://nodejs.org/api/all.html#all_http_class-httpincomingmessage).
|
|
39
|
+
* However, some cases (e.g. Lambda and Azure Functions instrumentation)
|
|
40
|
+
* create a pseudo-req object that matches well enough for this function.
|
|
41
|
+
* Some relevant fields: (TODO: document all used fields)
|
|
42
|
+
* - `headers` - Required. An object.
|
|
43
|
+
* - `body` - The incoming request body, if available. The `json` and
|
|
44
|
+
* `payload` fields are also checked to accomodate some web frameworks.
|
|
45
|
+
* - `bodyIsBase64Encoded` - An optional boolean. If `true`, then the `body`
|
|
46
|
+
* needs to be base64-decoded before inclusion and redaction. Used by
|
|
47
|
+
* Lambda instrumentation in some cases (e.g. for ELB triggers).
|
|
48
|
+
* @param {Object} conf - The full agent configuration.
|
|
49
|
+
* @param {String} type - 'errors' or 'transactions'. Indicates if this req
|
|
50
|
+
* is being captured for an APM error or transaction event.
|
|
51
|
+
*/
|
|
52
|
+
function getContextFromRequest(req, conf, type) {
|
|
53
|
+
var captureBody = conf.captureBody === type || conf.captureBody === 'all';
|
|
54
|
+
|
|
55
|
+
var context = {
|
|
56
|
+
http_version: req.httpVersion,
|
|
57
|
+
method: req.method,
|
|
58
|
+
url: getUrlFromRequest(req),
|
|
59
|
+
headers: undefined,
|
|
60
|
+
};
|
|
61
|
+
if (req.socket && req.socket.remoteAddress) {
|
|
62
|
+
context.socket = {
|
|
63
|
+
remote_address: req.socket.remoteAddress,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (conf.captureHeaders) {
|
|
68
|
+
context.headers = redactKeysFromObject(
|
|
69
|
+
req.headers,
|
|
70
|
+
conf.sanitizeFieldNamesRegExp,
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
if (context.headers.cookie && context.headers.cookie !== REDACTED) {
|
|
74
|
+
let cookies = cookie.parse(req.headers.cookie);
|
|
75
|
+
cookies = redactKeysFromObject(
|
|
76
|
+
cookies,
|
|
77
|
+
conf.sanitizeFieldNamesRegExp,
|
|
78
|
+
COOKIE_VAL_REDACTED,
|
|
79
|
+
);
|
|
80
|
+
try {
|
|
81
|
+
context.headers.cookie = Object.keys(cookies)
|
|
82
|
+
.map((k) => cookie.serialize(k, cookies[k]))
|
|
83
|
+
.join('; ');
|
|
84
|
+
} catch (_err) {
|
|
85
|
+
// Fallback to full redaction if there is an issue re-serializing.
|
|
86
|
+
context.headers.cookie = REDACTED;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
var contentLength = parseInt(req.headers['content-length'], 10);
|
|
92
|
+
var transferEncoding = req.headers['transfer-encoding'];
|
|
93
|
+
var chunked =
|
|
94
|
+
typeof transferEncoding === 'string' &&
|
|
95
|
+
transferEncoding.toLowerCase() === 'chunked';
|
|
96
|
+
var body = req.json || req.body || req.payload;
|
|
97
|
+
var haveBody = body && (chunked || contentLength > 0);
|
|
98
|
+
|
|
99
|
+
if (haveBody) {
|
|
100
|
+
if (!captureBody) {
|
|
101
|
+
context.body = '[REDACTED]';
|
|
102
|
+
} else if (Buffer.isBuffer(body)) {
|
|
103
|
+
context.body = '<Buffer>';
|
|
104
|
+
} else {
|
|
105
|
+
if (typeof body === 'string' && req.bodyIsBase64Encoded === true) {
|
|
106
|
+
body = Buffer.from(body, 'base64').toString('utf8');
|
|
107
|
+
}
|
|
108
|
+
body = redactKeysFromPostedFormVariables(
|
|
109
|
+
body,
|
|
110
|
+
req.headers,
|
|
111
|
+
conf.sanitizeFieldNamesRegExp,
|
|
112
|
+
);
|
|
113
|
+
if (typeof body !== 'string') {
|
|
114
|
+
body = tryJsonStringify(body) || stringify(body);
|
|
115
|
+
}
|
|
116
|
+
context.body = body;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// TODO: Tempoary fix for https://github.com/elastic/apm-agent-nodejs/issues/813
|
|
121
|
+
if (context.url && context.url.port) {
|
|
122
|
+
context.url.port = String(context.url.port);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return context;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Extract appropriate `{transaction,error}.context.response` from an HTTP
|
|
130
|
+
* response object. This handles header redaction according to the agent config.
|
|
131
|
+
*
|
|
132
|
+
* @param {Object} res - Typically `res` is a Node.js `http.ServerResponse`
|
|
133
|
+
* (https://nodejs.org/api/http.html#class-httpserverresponse).
|
|
134
|
+
* However, some cases (e.g. Lambda and Azure Functions instrumentation)
|
|
135
|
+
* create a pseudo-res object that matches well enough for this function.
|
|
136
|
+
* Some relevant fields: (TODO: document all used fields)
|
|
137
|
+
* - `statusCode` - Required. A number.
|
|
138
|
+
* - `headers` - An object.
|
|
139
|
+
* - `headersSent` - Boolean indicating if the headers have been sent
|
|
140
|
+
* (https://nodejs.org/api/http.html#outgoingmessageheaderssent)
|
|
141
|
+
* - `finished` - Boolean indicating if `response.end()` has been called
|
|
142
|
+
* (https://nodejs.org/api/http.html#responsefinished)
|
|
143
|
+
* @param {Object} conf - The full agent configuration.
|
|
144
|
+
* @param {Boolean} isError - Indicates if this response contains an error and
|
|
145
|
+
* some extra fields should be added to the context
|
|
146
|
+
*/
|
|
147
|
+
function getContextFromResponse(res, conf, isError) {
|
|
148
|
+
var context = {
|
|
149
|
+
status_code: res.statusCode,
|
|
150
|
+
headers: undefined,
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
if (conf.captureHeaders) {
|
|
154
|
+
context.headers = res.headers || parseHttpHeadersFromReqOrRes(res, true);
|
|
155
|
+
context.headers = redactKeysFromObject(
|
|
156
|
+
context.headers,
|
|
157
|
+
conf.sanitizeFieldNamesRegExp,
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
if (isError) {
|
|
162
|
+
context.headers_sent = res.headersSent;
|
|
163
|
+
if (typeof res.finished === 'boolean') {
|
|
164
|
+
context.finished = res.finished;
|
|
165
|
+
} else {
|
|
166
|
+
context.finished = res.writableEnded;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
return context;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Extract appropriate `{transaction,error}.context.user` from an HTTP
|
|
175
|
+
* request object.
|
|
176
|
+
*
|
|
177
|
+
* @param {Object} req - Typically `req` is a Node.js `http.IncomingMessage`.
|
|
178
|
+
* However, some cases (e.g. Lambda and Azure Functions instrumentation)
|
|
179
|
+
* create a pseudo-req object that matches well enough for this function.
|
|
180
|
+
* Some relevant fields: (TODO: document all used fields)
|
|
181
|
+
* - `headers` - Required. An object.
|
|
182
|
+
*/
|
|
183
|
+
function getUserContextFromRequest(req) {
|
|
184
|
+
var user = req.user || basicAuth(req) || req.session;
|
|
185
|
+
if (!user) {
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
var context = {};
|
|
190
|
+
|
|
191
|
+
if (typeof user.id === 'string' || typeof user.id === 'number') {
|
|
192
|
+
context.id = user.id;
|
|
193
|
+
} else if (typeof user._id === 'string' || typeof user._id === 'number') {
|
|
194
|
+
context.id = user._id;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
if (typeof user.username === 'string') {
|
|
198
|
+
context.username = user.username;
|
|
199
|
+
} else if (typeof user.name === 'string') {
|
|
200
|
+
context.username = user.name;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
if (typeof user.email === 'string') {
|
|
204
|
+
context.email = user.email;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
return context;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
function parseUrl(urlStr) {
|
|
211
|
+
return new url.URL(urlStr, 'relative:///');
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function tryJsonStringify(obj) {
|
|
215
|
+
try {
|
|
216
|
+
return JSON.stringify(obj);
|
|
217
|
+
} catch (e) {}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
module.exports = {
|
|
221
|
+
getContextFromRequest,
|
|
222
|
+
getContextFromResponse,
|
|
223
|
+
getUserContextFromRequest,
|
|
224
|
+
parseUrl,
|
|
225
|
+
};
|
package/lib/propwrap.js
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright Elasticsearch B.V. and other contributors where applicable.
|
|
3
|
+
* Licensed under the BSD 2-Clause License; you may not use this file except in
|
|
4
|
+
* compliance with the BSD 2-Clause License.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
// Utilities to wrap properties of an object.
|
|
10
|
+
//
|
|
11
|
+
// This is similar to "./instrumentation/shimmer.js". However, it uses a
|
|
12
|
+
// different technique to support wrapping properties that are only available
|
|
13
|
+
// via a getter (i.e. their property descriptor is `.writable === false`).
|
|
14
|
+
|
|
15
|
+
/*
|
|
16
|
+
* This block is derived from esbuild's bundling support.
|
|
17
|
+
* https://github.com/evanw/esbuild/blob/v0.14.42/internal/runtime/runtime.go#L22
|
|
18
|
+
*
|
|
19
|
+
* License:
|
|
20
|
+
* MIT License
|
|
21
|
+
*
|
|
22
|
+
* Copyright (c) 2020 Evan Wallace
|
|
23
|
+
*
|
|
24
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
25
|
+
* of this software and associated documentation files (the "Software"), to deal
|
|
26
|
+
* in the Software without restriction, including without limitation the rights
|
|
27
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
28
|
+
* copies of the Software, and to permit persons to whom the Software is
|
|
29
|
+
* furnished to do so, subject to the following conditions:
|
|
30
|
+
*
|
|
31
|
+
* The above copyright notice and this permission notice shall be included in all
|
|
32
|
+
* copies or substantial portions of the Software.
|
|
33
|
+
*
|
|
34
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
35
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
36
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
37
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
38
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
39
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
40
|
+
* SOFTWARE.
|
|
41
|
+
*/
|
|
42
|
+
var __defProp = Object.defineProperty;
|
|
43
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
44
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
45
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
46
|
+
var __copyProps = (to, from, except, desc) => {
|
|
47
|
+
if ((from && typeof from === 'object') || typeof from === 'function') {
|
|
48
|
+
for (const key of __getOwnPropNames(from)) {
|
|
49
|
+
if (!__hasOwnProp.call(to, key) && key !== except) {
|
|
50
|
+
__defProp(to, key, {
|
|
51
|
+
get: () => from[key],
|
|
52
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable,
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return to;
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Return a new object that is a copy of `obj`, with its `subpath` property
|
|
62
|
+
* replaced with the return value of `wrapper(original)`.
|
|
63
|
+
*
|
|
64
|
+
* For example:
|
|
65
|
+
* var os = wrap(require('os'), 'platform', (orig) => {
|
|
66
|
+
* return function wrappedPlatform () {
|
|
67
|
+
* return orig().toUpperCase()
|
|
68
|
+
* }
|
|
69
|
+
* })
|
|
70
|
+
* console.log(os.platform()) // => DARWIN
|
|
71
|
+
*
|
|
72
|
+
* The subpath can indicate a nested property. Each property in that subpath,
|
|
73
|
+
* except the last, must identify an *Object*.
|
|
74
|
+
*
|
|
75
|
+
* Limitations:
|
|
76
|
+
* - This doesn't handle possible Symbol properties on the copied object(s).
|
|
77
|
+
* - This cannot wrap a property of a function, because we cannot create a
|
|
78
|
+
* copy of the function.
|
|
79
|
+
*
|
|
80
|
+
* @param {Object} obj
|
|
81
|
+
* @param {String} subpath - The property subpath on `obj` to wrap. This may
|
|
82
|
+
* point to a nested property by using a '.' to separate levels. For example:
|
|
83
|
+
* var fs = wrap(fs, 'promises.sync', (orig) => { ... })
|
|
84
|
+
* @param {Function} wrapper - A function of the form `function (orig)`, where
|
|
85
|
+
* `orig` is the original property value. This must synchronously return the
|
|
86
|
+
* new property value.
|
|
87
|
+
* @returns {Object} A new object with the wrapped property.
|
|
88
|
+
* @throws {TypeError} if the subpath points to a non-existant property, or if
|
|
89
|
+
* any but the last subpath part points to a non-Object.
|
|
90
|
+
*/
|
|
91
|
+
function wrap(obj, subpath, wrapper) {
|
|
92
|
+
const parts = subpath.split('.');
|
|
93
|
+
const namespaces = [obj];
|
|
94
|
+
let namespace = obj;
|
|
95
|
+
let key;
|
|
96
|
+
let val;
|
|
97
|
+
|
|
98
|
+
// 1. Traverse the subpath parts to sanity check and get references to the
|
|
99
|
+
// Objects that will will be copying.
|
|
100
|
+
for (let i = 0; i < parts.length; i++) {
|
|
101
|
+
key = parts[i];
|
|
102
|
+
val = namespace[key];
|
|
103
|
+
if (!val) {
|
|
104
|
+
throw new TypeError(
|
|
105
|
+
`cannot wrap "${subpath}": "<obj>.${parts
|
|
106
|
+
.slice(0, i)
|
|
107
|
+
.join('.')}" is ${typeof val}`,
|
|
108
|
+
);
|
|
109
|
+
} else if (i < parts.length - 1) {
|
|
110
|
+
if (typeof val !== 'object') {
|
|
111
|
+
throw new TypeError(
|
|
112
|
+
`cannot wrap "${subpath}": "<obj>.${parts
|
|
113
|
+
.slice(0, i)
|
|
114
|
+
.join('.')}" is not an Object`,
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
namespace = val;
|
|
118
|
+
namespaces.push(namespace);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// 2. Now work backwards, wrapping each namespace with a new object that has a
|
|
123
|
+
// copy of all the properties, except the one that we've wrapped.
|
|
124
|
+
for (let i = parts.length - 1; i >= 0; i--) {
|
|
125
|
+
key = parts[i];
|
|
126
|
+
namespace = namespaces[i];
|
|
127
|
+
if (i === parts.length - 1) {
|
|
128
|
+
const orig = namespace[key];
|
|
129
|
+
val = wrapper(orig);
|
|
130
|
+
} else {
|
|
131
|
+
val = namespaces[i + 1];
|
|
132
|
+
}
|
|
133
|
+
const desc = __getOwnPropDesc(namespace, key);
|
|
134
|
+
const wrappedNamespace = __defProp({}, key, {
|
|
135
|
+
value: val,
|
|
136
|
+
enumerable: !desc || desc.enumerable,
|
|
137
|
+
});
|
|
138
|
+
__copyProps(wrappedNamespace, namespace, key);
|
|
139
|
+
namespaces[i] = wrappedNamespace;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return namespaces[0];
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
module.exports = {
|
|
146
|
+
wrap,
|
|
147
|
+
};
|