@vendure/core
Version:
A modern, headless ecommerce framework
115 lines • 5.28 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.ENABLE_INSTRUMENTATION_ENV_VAR = void 0;
exports.Instrument = Instrument;
exports.getInstrumentedClassTarget = getInstrumentedClassTarget;
const config_helpers_1 = require("../config/config-helpers");
const noop_instrumentation_strategy_1 = require("../config/system/noop-instrumentation-strategy");
exports.ENABLE_INSTRUMENTATION_ENV_VAR = 'VENDURE_ENABLE_INSTRUMENTATION';
const INSTRUMENTED_CLASS = Symbol('InstrumentedClassTarget');
/**
* @description
* This decorator is used to apply instrumentation to a class. It is intended to be used in conjunction
* with an {@link InstrumentationStrategy} which defines how the instrumentation should be applied.
*
* In order for the instrumentation to be applied, the `VENDURE_ENABLE_INSTRUMENTATION` environment
* variable (exported from the `@vendure/core` package as `ENABLE_INSTRUMENTATION_ENV_VAR`) must be set to `true`.
* This is done to avoid the overhead of instrumentation in environments where it is not needed.
*
* :::warning
* You should _not_ decorate GraphQL resolvers & REST controllers with this decorator. Those will
* already be instrumented, and adding the `@Instrument()` decorator will potentially
* interfere with other NestJS decorators on your resolver methods.
* :::
*
* For more information on how instrumentation is used, see docs on the TelemetryPlugin.
*
* @example
* ```ts
* import { Instrument } from '\@vendure/core';
* import { Injectable } from '\@nestjs/common';
*
* \@Injectable()
* \@Instrument() // [!code highlight]
* export class MyService {
*
* // Calls to this method will be instrumented
* myMethod() {
* // ...
* }
* }
* ```
*
* @since 3.3.0
* @docsCategory telemetry
*/
function Instrument() {
return function (target) {
// Since the instrumentation is not "free" (it will wrap all instrumented classes in a
// Proxy, which has some overhead), we will only do this if explicitly requested by the
// presence of this env var. The `@vendure/telemetry-plugin` package sets this in its configuration,
// which will be run before any of the Vendure code is loaded.
if (process.env[exports.ENABLE_INSTRUMENTATION_ENV_VAR] == null) {
return target;
}
// Add type guard to ensure target is a constructor
if (typeof target !== 'function') {
return target;
}
const InstrumentedClass = class extends target {
constructor(...args) {
// eslint-disable-next-line constructor-super
super(...args);
const config = (0, config_helpers_1.getConfig)();
const { instrumentationStrategy } = config.systemOptions;
if (!instrumentationStrategy) {
return this;
}
if (instrumentationStrategy instanceof noop_instrumentation_strategy_1.NoopInstrumentationStrategy) {
throw new Error('Please add a TelemetryPlugin to your VendureConfig');
}
// eslint-disable-next-line @typescript-eslint/no-this-alias
const instance = this;
const proxy = new Proxy(this, {
get: (obj, prop) => {
const original = obj[prop];
if (typeof original === 'function') {
// Bind the method to the proxy instance to ensure internal calls go through the proxy
const boundMethod = original.bind(proxy);
return function (...methodArgs) {
const applyOriginalFunction = boundMethod.constructor.name === 'AsyncFunction'
? async () => await boundMethod(...methodArgs)
: () => boundMethod(...methodArgs);
const wrappedMethodArgs = {
instance,
target,
methodName: String(prop),
args: methodArgs,
applyOriginalFunction,
};
return instrumentationStrategy.wrapMethod(wrappedMethodArgs);
};
}
return original;
},
});
return proxy;
}
};
// Set the name property of ProxiedClass to match the target's name
Object.defineProperty(InstrumentedClass, 'name', { value: target.name });
Object.defineProperty(InstrumentedClass, INSTRUMENTED_CLASS, { value: target });
return InstrumentedClass;
};
}
/**
* @description
* This function is used to retrieve the original class of an instrumented class. It is intended for
* use in an {@link InstrumentationStrategy} only, and should not generally be used in application code.
*
* @since 3.3.0
*/
function getInstrumentedClassTarget(input) {
return input[INSTRUMENTED_CLASS];
}
//# sourceMappingURL=instrument-decorator.js.map