@apm-js-collab/code-transformer 0.9.0 → 0.11.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/README.md +17 -44
- package/index.d.ts +111 -3
- package/index.js +3 -18
- package/lib/index.js +17 -0
- package/lib/matcher.js +83 -0
- package/lib/transformer.js +282 -0
- package/lib/transforms.js +459 -0
- package/package.json +17 -13
- package/pkg/orchestrion_js.d.ts +0 -97
- package/pkg/orchestrion_js.js +0 -563
|
@@ -0,0 +1,459 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const esquery = require('esquery')
|
|
4
|
+
const { parse } = require('meriyah')
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Returns `true` if `node` is already a `tracingChannel` import/require statement,
|
|
8
|
+
* used to avoid duplicate injections.
|
|
9
|
+
*
|
|
10
|
+
* @param {import('estree').Node} node
|
|
11
|
+
* @returns {boolean}
|
|
12
|
+
*/
|
|
13
|
+
const tracingChannelPredicate = (node) => (
|
|
14
|
+
node.declarations?.[0]?.id?.properties?.[0]?.value?.name === 'tr_ch_apm_tracingChannel'
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
const CHANNEL_REGEX = /[^\w]/g
|
|
18
|
+
/**
|
|
19
|
+
* Formats the channel variable name by replacing any non-whitespace characters with `_`
|
|
20
|
+
*
|
|
21
|
+
* @param {string} channelName
|
|
22
|
+
*/
|
|
23
|
+
const formatChannelVariable = (channelName) => `tr_ch_apm$${channelName.replace(CHANNEL_REGEX, '_')}`
|
|
24
|
+
|
|
25
|
+
const transforms = module.exports = {
|
|
26
|
+
/**
|
|
27
|
+
* Injects a `tracingChannel` import/require into the program body if one is not
|
|
28
|
+
* already present.
|
|
29
|
+
*
|
|
30
|
+
* @param {{ dcModule: string, sourceType: 'module'|'script' }} state
|
|
31
|
+
* @param {import('estree').Program} node - The program root node.
|
|
32
|
+
*/
|
|
33
|
+
tracingChannelImport ({ dcModule, moduleType }, node) {
|
|
34
|
+
if (node.body.some(tracingChannelPredicate)) return
|
|
35
|
+
|
|
36
|
+
const options = { module: moduleType === 'esm' }
|
|
37
|
+
const index = node.body.findIndex(child => child.directive === 'use strict')
|
|
38
|
+
const dc = moduleType === 'esm'
|
|
39
|
+
? `import tr_ch_apm_dc from "${dcModule}"`
|
|
40
|
+
: `const tr_ch_apm_dc = require("${dcModule}")`
|
|
41
|
+
const tracingChannel = 'const { tracingChannel: tr_ch_apm_tracingChannel } = tr_ch_apm_dc'
|
|
42
|
+
const hasSubscribers = `const tr_ch_apm_hasSubscribers = ch => ch.start.hasSubscribers
|
|
43
|
+
|| ch.end.hasSubscribers
|
|
44
|
+
|| ch.asyncStart.hasSubscribers
|
|
45
|
+
|| ch.asyncEnd.hasSubscribers
|
|
46
|
+
|| ch.error.hasSubscribers`
|
|
47
|
+
|
|
48
|
+
node.body.splice(
|
|
49
|
+
index + 1,
|
|
50
|
+
0,
|
|
51
|
+
parse(dc, options).body[0],
|
|
52
|
+
parse(tracingChannel, options).body[0],
|
|
53
|
+
parse(hasSubscribers, options).body[0]
|
|
54
|
+
)
|
|
55
|
+
},
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Injects a `tracingChannel(...)` variable declaration for the config's channel
|
|
59
|
+
* into the program body, also ensuring the import is present.
|
|
60
|
+
*
|
|
61
|
+
* @param {{ channelName: string, module: { name: string }, dcModule: string, sourceType: 'module'|'script' }} state
|
|
62
|
+
* @param {import('estree').Program} node - The program root node.
|
|
63
|
+
*/
|
|
64
|
+
tracingChannelDeclaration (state, node) {
|
|
65
|
+
const { channelName, module: { name } } = state
|
|
66
|
+
const channelVariable = formatChannelVariable(channelName)
|
|
67
|
+
|
|
68
|
+
if (node.body.some(child => child.declarations?.[0]?.id?.name === channelVariable)) return
|
|
69
|
+
|
|
70
|
+
transforms.tracingChannelImport(state, node)
|
|
71
|
+
|
|
72
|
+
const index = node.body.findIndex(tracingChannelPredicate)
|
|
73
|
+
const code = `
|
|
74
|
+
const ${channelVariable} = tr_ch_apm_tracingChannel("orchestrion:${name}:${channelName}")
|
|
75
|
+
`
|
|
76
|
+
|
|
77
|
+
node.body.splice(index + 1, 0, parse(code).body[0])
|
|
78
|
+
},
|
|
79
|
+
|
|
80
|
+
traceCallback: traceAny,
|
|
81
|
+
tracePromise: traceAny,
|
|
82
|
+
traceSync: traceAny,
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Generic trace entry point. Dispatches to {@link traceInstanceMethod} for class
|
|
87
|
+
* nodes or {@link traceFunction} for all other function nodes.
|
|
88
|
+
*
|
|
89
|
+
* @param {object} state - Merged instrumentation + runtime state.
|
|
90
|
+
* @param {import('estree').Node} node - Matched AST node.
|
|
91
|
+
* @param {import('estree').Node} _parent
|
|
92
|
+
* @param {import('estree').Node[]} ancestry - Full ancestor chain, root last.
|
|
93
|
+
*/
|
|
94
|
+
function traceAny (state, node, _parent, ancestry) {
|
|
95
|
+
const program = ancestry[ancestry.length - 1]
|
|
96
|
+
|
|
97
|
+
if (node.type === 'ClassDeclaration' || node.type === 'ClassExpression') {
|
|
98
|
+
traceInstanceMethod(state, node, program)
|
|
99
|
+
} else {
|
|
100
|
+
traceFunction(state, node, program)
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Wraps a function node's body with diagnostics_channel tracing.
|
|
106
|
+
*
|
|
107
|
+
* Injects the channel declaration, wraps the original body in an inner function,
|
|
108
|
+
* and rewrites `super` references if needed.
|
|
109
|
+
*
|
|
110
|
+
* @param {object} state
|
|
111
|
+
* @param {import('estree').Function} node - The function node to instrument.
|
|
112
|
+
* @param {import('estree').Program} program
|
|
113
|
+
*/
|
|
114
|
+
function traceFunction (state, node, program) {
|
|
115
|
+
transforms.tracingChannelDeclaration(state, program)
|
|
116
|
+
|
|
117
|
+
const { functionQuery: { methodName, privateMethodName, functionName, expressionName } } = state
|
|
118
|
+
const isConstructor = methodName === 'constructor' ||
|
|
119
|
+
(!methodName && !privateMethodName && !functionName && !expressionName)
|
|
120
|
+
const type = isConstructor ? 'ArrowFunctionExpression' : 'FunctionExpression'
|
|
121
|
+
|
|
122
|
+
node.body = wrap(state, {
|
|
123
|
+
type,
|
|
124
|
+
params: node.params,
|
|
125
|
+
body: node.body,
|
|
126
|
+
async: node.async,
|
|
127
|
+
expression: false,
|
|
128
|
+
generator: node.generator,
|
|
129
|
+
}, program)
|
|
130
|
+
|
|
131
|
+
// The original function no longer contains any calls to `await` or `yield` as
|
|
132
|
+
// the function body is copied to the internal wrapped function, so we set
|
|
133
|
+
// these to false to avoid altering the return value of the wrapper. The old
|
|
134
|
+
// values are instead copied to the new AST node above.
|
|
135
|
+
node.generator = false
|
|
136
|
+
node.async = false
|
|
137
|
+
|
|
138
|
+
wrapSuper(state, node)
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Instruments an instance method that may not exist on the class at definition
|
|
143
|
+
* time by patching it inside the constructor.
|
|
144
|
+
*
|
|
145
|
+
* If the method is already defined on the class body it is skipped (it will be
|
|
146
|
+
* handled via {@link traceFunction} when the child node is visited). Otherwise a
|
|
147
|
+
* constructor is synthesised (or extended) to wrap `this[methodName]` at runtime.
|
|
148
|
+
*
|
|
149
|
+
* @param {object} state
|
|
150
|
+
* @param {import('estree').ClassDeclaration|import('estree').ClassExpression} node
|
|
151
|
+
* @param {import('estree').Program} program
|
|
152
|
+
*/
|
|
153
|
+
function traceInstanceMethod (state, node, program) {
|
|
154
|
+
const { functionQuery, operator } = state
|
|
155
|
+
const { methodName } = functionQuery
|
|
156
|
+
|
|
157
|
+
// No methodName means a constructor-only config — the constructor FunctionExpression
|
|
158
|
+
// is matched directly by the other queries and handled via traceFunction instead.
|
|
159
|
+
if (!methodName) return
|
|
160
|
+
|
|
161
|
+
const classBody = node.body
|
|
162
|
+
|
|
163
|
+
// If the method exists on the class, we return as it will be patched later
|
|
164
|
+
// while traversing child nodes later on.
|
|
165
|
+
if (classBody.body.some(({ key }) => key.name === methodName)) return
|
|
166
|
+
|
|
167
|
+
// Method doesn't exist on the class so we assume an instance method and
|
|
168
|
+
// wrap it in the constructor instead.
|
|
169
|
+
let ctor = classBody.body.find(({ kind }) => kind === 'constructor')
|
|
170
|
+
|
|
171
|
+
transforms.tracingChannelDeclaration(state, program)
|
|
172
|
+
|
|
173
|
+
if (!ctor) {
|
|
174
|
+
ctor = parse(
|
|
175
|
+
node.superClass
|
|
176
|
+
? 'class A extends Object { constructor (...args) { super(...args) } }'
|
|
177
|
+
: 'class A { constructor () {} }'
|
|
178
|
+
).body[0].body.body[0] // Extract constructor from dummy class body.
|
|
179
|
+
|
|
180
|
+
classBody.body.unshift(ctor)
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const ctorBody = parse(`
|
|
184
|
+
const __apm$${methodName} = this["${methodName}"]
|
|
185
|
+
this["${methodName}"] = function () {}
|
|
186
|
+
`).body
|
|
187
|
+
|
|
188
|
+
// Extract only right-hand side function of line 2.
|
|
189
|
+
const fn = ctorBody[1].expression.right
|
|
190
|
+
|
|
191
|
+
fn.async = operator === 'tracePromise'
|
|
192
|
+
fn.body = wrap(state, { type: 'Identifier', name: `__apm$${methodName}` }, program)
|
|
193
|
+
|
|
194
|
+
wrapSuper(state, fn)
|
|
195
|
+
|
|
196
|
+
ctor.value.body.body.push(...ctorBody)
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Builds the replacement block statement for a function body.
|
|
201
|
+
*
|
|
202
|
+
* Selects the appropriate wrapper template (`wrapSync`, `wrapPromise`, etc.) and
|
|
203
|
+
* prepends the shared `__apm$ctx` / `__apm$traced` preamble before returning the
|
|
204
|
+
* resulting block statement body.
|
|
205
|
+
*
|
|
206
|
+
* @param {object} state
|
|
207
|
+
* @param {import('estree').Node} node - The original function (or identifier for instance methods).
|
|
208
|
+
* @param {import('estree').Program} program
|
|
209
|
+
* @returns {import('estree').BlockStatement['body']}
|
|
210
|
+
*/
|
|
211
|
+
function wrap (state, node, program) {
|
|
212
|
+
const { operator, moduleVersion } = state
|
|
213
|
+
|
|
214
|
+
let wrapper
|
|
215
|
+
|
|
216
|
+
if (operator === 'traceCallback') wrapper = wrapCallback(state, node)
|
|
217
|
+
if (operator === 'tracePromise') wrapper = wrapPromise(state, node, program)
|
|
218
|
+
if (operator === 'traceSync') wrapper = wrapSync(state, node, program)
|
|
219
|
+
|
|
220
|
+
const block = wrapper.body[0].body // Extract only block statement of function body.
|
|
221
|
+
const common = parse(node.type === 'ArrowFunctionExpression'
|
|
222
|
+
? `
|
|
223
|
+
const __apm$ctx = {
|
|
224
|
+
arguments,
|
|
225
|
+
moduleVersion: ${JSON.stringify(moduleVersion)}
|
|
226
|
+
};
|
|
227
|
+
const __apm$traced = () => {
|
|
228
|
+
const __apm$wrapped = () => {};
|
|
229
|
+
return __apm$wrapped(...arguments);
|
|
230
|
+
};
|
|
231
|
+
`
|
|
232
|
+
: `
|
|
233
|
+
const __apm$ctx = {
|
|
234
|
+
arguments,
|
|
235
|
+
self: this,
|
|
236
|
+
moduleVersion: ${JSON.stringify(moduleVersion)}
|
|
237
|
+
};
|
|
238
|
+
const __apm$traced = () => {
|
|
239
|
+
const __apm$wrapped = () => {};
|
|
240
|
+
return __apm$wrapped.apply(this, arguments);
|
|
241
|
+
};
|
|
242
|
+
`).body
|
|
243
|
+
|
|
244
|
+
block.body.unshift(...common)
|
|
245
|
+
|
|
246
|
+
// Replace the right-hand side assignment of `const __apm$wrapped = () => {}`.
|
|
247
|
+
esquery.query(block, '[id.name=__apm$wrapped]')[0].init = node
|
|
248
|
+
|
|
249
|
+
return block
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Rewrites `super.method(...)` calls inside a moved function body.
|
|
254
|
+
*
|
|
255
|
+
* Because the original body is copied into a nested arrow/function, `super` would
|
|
256
|
+
* no longer be in scope. Each unique `super.x` reference is replaced with
|
|
257
|
+
* `__apm$super['x']` and a preamble that captures the super-binding in the
|
|
258
|
+
* outermost method scope is prepended.
|
|
259
|
+
*
|
|
260
|
+
* @param {object} _state
|
|
261
|
+
* @param {import('estree').Function} node - The outer (wrapper) function node.
|
|
262
|
+
*/
|
|
263
|
+
function wrapSuper (_state, node) {
|
|
264
|
+
const members = new Set()
|
|
265
|
+
|
|
266
|
+
esquery.traverse(
|
|
267
|
+
node.body,
|
|
268
|
+
esquery.parse('[object.type=Super]'),
|
|
269
|
+
(node, parent) => {
|
|
270
|
+
const { name } = node.property
|
|
271
|
+
|
|
272
|
+
let child
|
|
273
|
+
|
|
274
|
+
if (parent.callee) {
|
|
275
|
+
// This is needed because for generator functions we have to move the
|
|
276
|
+
// original function to a nested wrapped function, but we can't use an
|
|
277
|
+
// arrow function because arrow function cannot be generator functions,
|
|
278
|
+
// and `super` cannot be called from a nested function, so we have to
|
|
279
|
+
// rewrite any `super` call to not use the keyword.
|
|
280
|
+
const { expression } = parse(`__apm$super['${name}'].call(this)`).body[0]
|
|
281
|
+
|
|
282
|
+
parent.callee = child = expression.callee
|
|
283
|
+
parent.arguments.unshift(...expression.arguments)
|
|
284
|
+
} else {
|
|
285
|
+
parent.expression = child = parse(`__apm$super['${name}']`).body[0]
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
child.computed = parent.callee.computed
|
|
289
|
+
child.optional = parent.callee.optional
|
|
290
|
+
|
|
291
|
+
members.add(name)
|
|
292
|
+
}
|
|
293
|
+
)
|
|
294
|
+
|
|
295
|
+
for (const name of members) {
|
|
296
|
+
const member = parse(`
|
|
297
|
+
class Wrapper {
|
|
298
|
+
wrapper () {
|
|
299
|
+
__apm$super['${name}'] = super['${name}']
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
`).body[0].body.body[0].value.body.body[0]
|
|
303
|
+
|
|
304
|
+
node.body.body.unshift(member)
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
if (members.size > 0) {
|
|
308
|
+
node.body.body.unshift(parse('const __apm$super = {}').body[0])
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* Builds the wrapper AST for a callback-style function.
|
|
314
|
+
*
|
|
315
|
+
* Replaces the callback argument (at `callbackIndex`) with a wrapped version that
|
|
316
|
+
* publishes to the tracing channel on completion or error.
|
|
317
|
+
*
|
|
318
|
+
* @param {{ channelName: string, functionQuery: { callbackIndex?: number } }} state
|
|
319
|
+
* @param {import('estree').Node} node - The original function node (unused; replaced by preamble).
|
|
320
|
+
* @returns {import('estree').Program} Parsed wrapper function program.
|
|
321
|
+
*/
|
|
322
|
+
function wrapCallback (state, node) {
|
|
323
|
+
const { channelName, functionQuery: { callbackIndex = -1 } } = state
|
|
324
|
+
const channelVariable = formatChannelVariable(channelName)
|
|
325
|
+
|
|
326
|
+
return parse(`
|
|
327
|
+
function wrapper () {
|
|
328
|
+
const __apm$cb = Array.prototype.at.call(arguments, ${callbackIndex});
|
|
329
|
+
|
|
330
|
+
if (!${channelVariable}.start.hasSubscribers) return __apm$traced();
|
|
331
|
+
|
|
332
|
+
function __apm$wrappedCb(err, res) {
|
|
333
|
+
if (err) {
|
|
334
|
+
__apm$ctx.error = err;
|
|
335
|
+
${channelVariable}.error.publish(__apm$ctx);
|
|
336
|
+
} else {
|
|
337
|
+
__apm$ctx.result = res;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
${channelVariable}.asyncStart.runStores(__apm$ctx, () => {
|
|
341
|
+
try {
|
|
342
|
+
if (__apm$cb) {
|
|
343
|
+
return __apm$cb.apply(this, arguments);
|
|
344
|
+
}
|
|
345
|
+
} finally {
|
|
346
|
+
${channelVariable}.asyncEnd.publish(__apm$ctx);
|
|
347
|
+
}
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
if (typeof __apm$cb !== 'function') {
|
|
352
|
+
return __apm$traced();
|
|
353
|
+
}
|
|
354
|
+
Array.prototype.splice.call(arguments, ${callbackIndex}, 1, __apm$wrappedCb);
|
|
355
|
+
|
|
356
|
+
return ${channelVariable}.start.runStores(__apm$ctx, () => {
|
|
357
|
+
try {
|
|
358
|
+
return __apm$traced();
|
|
359
|
+
} catch (err) {
|
|
360
|
+
__apm$ctx.error = err;
|
|
361
|
+
${channelVariable}.error.publish(__apm$ctx);
|
|
362
|
+
throw err;
|
|
363
|
+
} finally {
|
|
364
|
+
__apm$ctx.self ??= this;
|
|
365
|
+
${channelVariable}.end.publish(__apm$ctx);
|
|
366
|
+
}
|
|
367
|
+
});
|
|
368
|
+
}
|
|
369
|
+
`)
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
/**
|
|
373
|
+
* Builds the wrapper AST for a Promise-returning function.
|
|
374
|
+
*
|
|
375
|
+
* Uses `runStores` to propagate async context and publishes
|
|
376
|
+
* `asyncStart`/`asyncEnd`/`error` channel events on settlement.
|
|
377
|
+
*
|
|
378
|
+
* @param {{ channelName: string }} state
|
|
379
|
+
* @param {import('estree').Node} node
|
|
380
|
+
* @returns {import('estree').Program} Parsed wrapper function program.
|
|
381
|
+
*/
|
|
382
|
+
function wrapPromise (state, node) {
|
|
383
|
+
const { channelName } = state
|
|
384
|
+
const channelVariable = formatChannelVariable(channelName)
|
|
385
|
+
|
|
386
|
+
return parse(`
|
|
387
|
+
function wrapper () {
|
|
388
|
+
if (!tr_ch_apm_hasSubscribers(${channelVariable})) return __apm$traced();
|
|
389
|
+
|
|
390
|
+
return ${channelVariable}.start.runStores(__apm$ctx, () => {
|
|
391
|
+
try {
|
|
392
|
+
let promise = __apm$traced();
|
|
393
|
+
if (typeof promise?.then !== 'function') {
|
|
394
|
+
__apm$ctx.result = promise;
|
|
395
|
+
return promise;
|
|
396
|
+
}
|
|
397
|
+
return promise.then(
|
|
398
|
+
result => {
|
|
399
|
+
__apm$ctx.result = result;
|
|
400
|
+
${channelVariable}.asyncStart.publish(__apm$ctx);
|
|
401
|
+
${channelVariable}.asyncEnd.publish(__apm$ctx);
|
|
402
|
+
return result;
|
|
403
|
+
},
|
|
404
|
+
err => {
|
|
405
|
+
__apm$ctx.error = err;
|
|
406
|
+
${channelVariable}.error.publish(__apm$ctx);
|
|
407
|
+
${channelVariable}.asyncStart.publish(__apm$ctx);
|
|
408
|
+
${channelVariable}.asyncEnd.publish(__apm$ctx);
|
|
409
|
+
throw err;
|
|
410
|
+
}
|
|
411
|
+
);
|
|
412
|
+
} catch (err) {
|
|
413
|
+
__apm$ctx.error = err;
|
|
414
|
+
${channelVariable}.error.publish(__apm$ctx);
|
|
415
|
+
throw err;
|
|
416
|
+
} finally {
|
|
417
|
+
__apm$ctx.self ??= this;
|
|
418
|
+
${channelVariable}.end.publish(__apm$ctx);
|
|
419
|
+
}
|
|
420
|
+
});
|
|
421
|
+
}
|
|
422
|
+
`)
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
/**
|
|
426
|
+
* Builds the wrapper AST for a synchronous function.
|
|
427
|
+
*
|
|
428
|
+
* Uses `runStores` for context propagation and publishes `error` channel events
|
|
429
|
+
* on throw. The result is stored in `__apm$ctx.result` on success.
|
|
430
|
+
*
|
|
431
|
+
* @param {{ channelName: string }} state
|
|
432
|
+
* @param {import('estree').Node} node
|
|
433
|
+
* @returns {import('estree').Program} Parsed wrapper function program.
|
|
434
|
+
*/
|
|
435
|
+
function wrapSync (state, node) {
|
|
436
|
+
const { channelName } = state
|
|
437
|
+
const channelVariable = formatChannelVariable(channelName)
|
|
438
|
+
|
|
439
|
+
return parse(`
|
|
440
|
+
function wrapper () {
|
|
441
|
+
if (!tr_ch_apm_hasSubscribers(${channelVariable})) return __apm$traced();
|
|
442
|
+
|
|
443
|
+
return ${channelVariable}.start.runStores(__apm$ctx, () => {
|
|
444
|
+
try {
|
|
445
|
+
const result = __apm$traced();
|
|
446
|
+
__apm$ctx.result = result;
|
|
447
|
+
return result;
|
|
448
|
+
} catch (err) {
|
|
449
|
+
__apm$ctx.error = err;
|
|
450
|
+
${channelVariable}.error.publish(__apm$ctx);
|
|
451
|
+
throw err;
|
|
452
|
+
} finally {
|
|
453
|
+
__apm$ctx.self ??= this;
|
|
454
|
+
${channelVariable}.end.publish(__apm$ctx);
|
|
455
|
+
}
|
|
456
|
+
});
|
|
457
|
+
}
|
|
458
|
+
`)
|
|
459
|
+
}
|
package/package.json
CHANGED
|
@@ -1,37 +1,41 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@apm-js-collab/code-transformer",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.0",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
7
7
|
"url": "https://github.com/nodejs/orchestrion-js.git"
|
|
8
8
|
},
|
|
9
9
|
"files": [
|
|
10
|
-
"./pkg/orchestrion_js.js",
|
|
11
|
-
"./pkg/orchestrion_js.d.ts",
|
|
12
10
|
"./index.js",
|
|
13
11
|
"./index.d.ts",
|
|
12
|
+
"./lib/**/*.js",
|
|
14
13
|
"LICENSE",
|
|
15
14
|
"NOTICE"
|
|
16
15
|
],
|
|
17
16
|
"main": "./index.js",
|
|
18
17
|
"types": "./index.d.ts",
|
|
19
18
|
"scripts": {
|
|
20
|
-
"
|
|
21
|
-
"
|
|
22
|
-
"
|
|
23
|
-
"test": "
|
|
24
|
-
"test:update-snapshots": "vitest -u run",
|
|
25
|
-
"test:watch": "vitest"
|
|
19
|
+
"lint": "eslint .",
|
|
20
|
+
"lint:fix": "eslint --fix .",
|
|
21
|
+
"test": "node --test tests/tests.test.mjs",
|
|
22
|
+
"test:watch": "node --watch --test tests/tests.test.mjs"
|
|
26
23
|
},
|
|
27
24
|
"devDependencies": {
|
|
28
25
|
"@types/node": "^18.0.0",
|
|
29
|
-
"
|
|
30
|
-
"
|
|
31
|
-
"
|
|
32
|
-
"wasm-pack": "^0.13.1"
|
|
26
|
+
"eslint": "^9.39.4",
|
|
27
|
+
"neostandard": "^0.13.0",
|
|
28
|
+
"typescript": "^5.8.3"
|
|
33
29
|
},
|
|
34
30
|
"volta": {
|
|
35
31
|
"node": "22.15.0"
|
|
32
|
+
},
|
|
33
|
+
"dependencies": {
|
|
34
|
+
"@types/estree": "^1.0.8",
|
|
35
|
+
"astring": "^1.9.0",
|
|
36
|
+
"esquery": "^1.7.0",
|
|
37
|
+
"meriyah": "^6.1.4",
|
|
38
|
+
"semifies": "^1.0.0",
|
|
39
|
+
"source-map": "^0.6.0"
|
|
36
40
|
}
|
|
37
41
|
}
|
package/pkg/orchestrion_js.d.ts
DELETED
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
/* tslint:disable */
|
|
2
|
-
/* eslint-disable */
|
|
3
|
-
/**
|
|
4
|
-
* Create a new instrumentation matcher from an array of instrumentation configs.
|
|
5
|
-
*/
|
|
6
|
-
export function create(configs: InstrumentationConfig[], dc_module?: string | null): InstrumentationMatcher;
|
|
7
|
-
/**
|
|
8
|
-
* Output of a transformation operation
|
|
9
|
-
*/
|
|
10
|
-
export interface TransformOutput {
|
|
11
|
-
/**
|
|
12
|
-
* The transformed JavaScript code
|
|
13
|
-
*/
|
|
14
|
-
code: string;
|
|
15
|
-
/**
|
|
16
|
-
* The sourcemap for the transformation (if generated)
|
|
17
|
-
*/
|
|
18
|
-
map: string | undefined;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Describes which function to instrument
|
|
23
|
-
*/
|
|
24
|
-
export type FunctionQuery = { className: string; methodName: string; kind: FunctionKind; index?: number; isExportAlias?: boolean } | { className: string; privateMethodName: string; kind: FunctionKind; index?: number } | { className: string; index?: number; isExportAlias?: boolean } | { methodName: string; kind: FunctionKind; index?: number } | { functionName: string; kind: FunctionKind; index?: number; isExportAlias?: boolean } | { expressionName: string; kind: FunctionKind; index?: number; isExportAlias?: boolean };
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* The kind of function - Sync or returns a promise
|
|
28
|
-
*/
|
|
29
|
-
export type FunctionKind = "Sync" | "Async";
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Configuration for injecting instrumentation code
|
|
33
|
-
*/
|
|
34
|
-
export interface InstrumentationConfig {
|
|
35
|
-
/**
|
|
36
|
-
* The name of the diagnostics channel to publish to
|
|
37
|
-
*/
|
|
38
|
-
channelName: string;
|
|
39
|
-
/**
|
|
40
|
-
* The module matcher to identify the module and file to instrument
|
|
41
|
-
*/
|
|
42
|
-
module: ModuleMatcher;
|
|
43
|
-
/**
|
|
44
|
-
* The function query to identify the function to instrument
|
|
45
|
-
*/
|
|
46
|
-
functionQuery: FunctionQuery;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Describes the module and file path you would like to match
|
|
51
|
-
*/
|
|
52
|
-
export interface ModuleMatcher {
|
|
53
|
-
/**
|
|
54
|
-
* The name of the module you want to match
|
|
55
|
-
*/
|
|
56
|
-
name: string;
|
|
57
|
-
/**
|
|
58
|
-
* The semver range that you want to match
|
|
59
|
-
*/
|
|
60
|
-
versionRange: string;
|
|
61
|
-
/**
|
|
62
|
-
* The path of the file you want to match from the module root
|
|
63
|
-
*/
|
|
64
|
-
filePath: string;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* The type of module being passed - ESM, CJS or unknown
|
|
69
|
-
*/
|
|
70
|
-
export type ModuleType = "esm" | "cjs" | "unknown";
|
|
71
|
-
|
|
72
|
-
/**
|
|
73
|
-
* The InstrumentationMatcher is responsible for matching specific modules
|
|
74
|
-
*/
|
|
75
|
-
export class InstrumentationMatcher {
|
|
76
|
-
private constructor();
|
|
77
|
-
free(): void;
|
|
78
|
-
/**
|
|
79
|
-
* Get a transformer for the given module name, version and file path.
|
|
80
|
-
* Returns `undefined` if no matching instrumentations are found.
|
|
81
|
-
*/
|
|
82
|
-
getTransformer(module_name: string, version: string, file_path: string): Transformer | undefined;
|
|
83
|
-
}
|
|
84
|
-
/**
|
|
85
|
-
* The Transformer is responsible for transforming JavaScript code.
|
|
86
|
-
*/
|
|
87
|
-
export class Transformer {
|
|
88
|
-
private constructor();
|
|
89
|
-
free(): void;
|
|
90
|
-
/**
|
|
91
|
-
* Transform JavaScript code and optionally sourcemap.
|
|
92
|
-
*
|
|
93
|
-
* # Errors
|
|
94
|
-
* Returns an error if the transformation fails to find injection points.
|
|
95
|
-
*/
|
|
96
|
-
transform(code: string, module_type: ModuleType, sourcemap?: string | null): TransformOutput;
|
|
97
|
-
}
|