Next.js website for Rocky Mountain Vending company featuring: - Product catalog with Stripe integration - Service areas and parts pages - Admin dashboard with Clerk authentication - SEO optimized pages with JSON-LD structured data Co-authored-by: Cursor <cursoragent@cursor.com>
278 lines
No EOL
11 KiB
Text
278 lines
No EOL
11 KiB
Text
/*
|
|
* Copyright The OpenTelemetry Authors
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* https://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
import * as path from 'path';
|
|
import { types as utilTypes } from 'util';
|
|
import { satisfies } from 'semver';
|
|
import { wrap, unwrap } from 'shimmer';
|
|
import { InstrumentationAbstract } from '../../instrumentation';
|
|
import { RequireInTheMiddleSingleton, } from './RequireInTheMiddleSingleton';
|
|
import { Hook as HookImport } from 'import-in-the-middle';
|
|
import { diag } from '@opentelemetry/api';
|
|
import { Hook as HookRequire } from 'require-in-the-middle';
|
|
import { readFileSync } from 'fs';
|
|
import { isWrapped } from '../../utils';
|
|
/**
|
|
* Base abstract class for instrumenting node plugins
|
|
*/
|
|
export class InstrumentationBase extends InstrumentationAbstract {
|
|
constructor(instrumentationName, instrumentationVersion, config) {
|
|
super(instrumentationName, instrumentationVersion, config);
|
|
this._hooks = [];
|
|
this._requireInTheMiddleSingleton = RequireInTheMiddleSingleton.getInstance();
|
|
this._enabled = false;
|
|
this._wrap = (moduleExports, name, wrapper) => {
|
|
if (isWrapped(moduleExports[name])) {
|
|
this._unwrap(moduleExports, name);
|
|
}
|
|
if (!utilTypes.isProxy(moduleExports)) {
|
|
return wrap(moduleExports, name, wrapper);
|
|
}
|
|
else {
|
|
const wrapped = wrap(Object.assign({}, moduleExports), name, wrapper);
|
|
Object.defineProperty(moduleExports, name, {
|
|
value: wrapped,
|
|
});
|
|
return wrapped;
|
|
}
|
|
};
|
|
this._unwrap = (moduleExports, name) => {
|
|
if (!utilTypes.isProxy(moduleExports)) {
|
|
return unwrap(moduleExports, name);
|
|
}
|
|
else {
|
|
return Object.defineProperty(moduleExports, name, {
|
|
value: moduleExports[name],
|
|
});
|
|
}
|
|
};
|
|
this._massWrap = (moduleExportsArray, names, wrapper) => {
|
|
if (!moduleExportsArray) {
|
|
diag.error('must provide one or more modules to patch');
|
|
return;
|
|
}
|
|
else if (!Array.isArray(moduleExportsArray)) {
|
|
moduleExportsArray = [moduleExportsArray];
|
|
}
|
|
if (!(names && Array.isArray(names))) {
|
|
diag.error('must provide one or more functions to wrap on modules');
|
|
return;
|
|
}
|
|
moduleExportsArray.forEach(moduleExports => {
|
|
names.forEach(name => {
|
|
this._wrap(moduleExports, name, wrapper);
|
|
});
|
|
});
|
|
};
|
|
this._massUnwrap = (moduleExportsArray, names) => {
|
|
if (!moduleExportsArray) {
|
|
diag.error('must provide one or more modules to patch');
|
|
return;
|
|
}
|
|
else if (!Array.isArray(moduleExportsArray)) {
|
|
moduleExportsArray = [moduleExportsArray];
|
|
}
|
|
if (!(names && Array.isArray(names))) {
|
|
diag.error('must provide one or more functions to wrap on modules');
|
|
return;
|
|
}
|
|
moduleExportsArray.forEach(moduleExports => {
|
|
names.forEach(name => {
|
|
this._unwrap(moduleExports, name);
|
|
});
|
|
});
|
|
};
|
|
let modules = this.init();
|
|
if (modules && !Array.isArray(modules)) {
|
|
modules = [modules];
|
|
}
|
|
this._modules = modules || [];
|
|
if (this._config.enabled) {
|
|
this.enable();
|
|
}
|
|
}
|
|
_warnOnPreloadedModules() {
|
|
this._modules.forEach((module) => {
|
|
const { name } = module;
|
|
try {
|
|
const resolvedModule = require.resolve(name);
|
|
if (require.cache[resolvedModule]) {
|
|
// Module is already cached, which means the instrumentation hook might not work
|
|
this._diag.warn(`Module ${name} has been loaded before ${this.instrumentationName} so it might not work, please initialize it before requiring ${name}`);
|
|
}
|
|
}
|
|
catch (_a) {
|
|
// Module isn't available, we can simply skip
|
|
}
|
|
});
|
|
}
|
|
_extractPackageVersion(baseDir) {
|
|
try {
|
|
const json = readFileSync(path.join(baseDir, 'package.json'), {
|
|
encoding: 'utf8',
|
|
});
|
|
const version = JSON.parse(json).version;
|
|
return typeof version === 'string' ? version : undefined;
|
|
}
|
|
catch (error) {
|
|
diag.warn('Failed extracting version', baseDir);
|
|
}
|
|
return undefined;
|
|
}
|
|
_onRequire(module, exports, name, baseDir) {
|
|
var _a;
|
|
if (!baseDir) {
|
|
if (typeof module.patch === 'function') {
|
|
module.moduleExports = exports;
|
|
if (this._enabled) {
|
|
this._diag.debug('Applying instrumentation patch for nodejs core module on require hook', {
|
|
module: module.name,
|
|
});
|
|
return module.patch(exports);
|
|
}
|
|
}
|
|
return exports;
|
|
}
|
|
const version = this._extractPackageVersion(baseDir);
|
|
module.moduleVersion = version;
|
|
if (module.name === name) {
|
|
// main module
|
|
if (isSupported(module.supportedVersions, version, module.includePrerelease)) {
|
|
if (typeof module.patch === 'function') {
|
|
module.moduleExports = exports;
|
|
if (this._enabled) {
|
|
this._diag.debug('Applying instrumentation patch for module on require hook', {
|
|
module: module.name,
|
|
version: module.moduleVersion,
|
|
baseDir,
|
|
});
|
|
return module.patch(exports, module.moduleVersion);
|
|
}
|
|
}
|
|
}
|
|
return exports;
|
|
}
|
|
// internal file
|
|
const files = (_a = module.files) !== null && _a !== void 0 ? _a : [];
|
|
const normalizedName = path.normalize(name);
|
|
const supportedFileInstrumentations = files
|
|
.filter(f => f.name === normalizedName)
|
|
.filter(f => isSupported(f.supportedVersions, version, module.includePrerelease));
|
|
return supportedFileInstrumentations.reduce((patchedExports, file) => {
|
|
file.moduleExports = patchedExports;
|
|
if (this._enabled) {
|
|
this._diag.debug('Applying instrumentation patch for nodejs module file on require hook', {
|
|
module: module.name,
|
|
version: module.moduleVersion,
|
|
fileName: file.name,
|
|
baseDir,
|
|
});
|
|
// patch signature is not typed, so we cast it assuming it's correct
|
|
return file.patch(patchedExports, module.moduleVersion);
|
|
}
|
|
return patchedExports;
|
|
}, exports);
|
|
}
|
|
enable() {
|
|
if (this._enabled) {
|
|
return;
|
|
}
|
|
this._enabled = true;
|
|
// already hooked, just call patch again
|
|
if (this._hooks.length > 0) {
|
|
for (const module of this._modules) {
|
|
if (typeof module.patch === 'function' && module.moduleExports) {
|
|
this._diag.debug('Applying instrumentation patch for nodejs module on instrumentation enabled', {
|
|
module: module.name,
|
|
version: module.moduleVersion,
|
|
});
|
|
module.patch(module.moduleExports, module.moduleVersion);
|
|
}
|
|
for (const file of module.files) {
|
|
if (file.moduleExports) {
|
|
this._diag.debug('Applying instrumentation patch for nodejs module file on instrumentation enabled', {
|
|
module: module.name,
|
|
version: module.moduleVersion,
|
|
fileName: file.name,
|
|
});
|
|
file.patch(file.moduleExports, module.moduleVersion);
|
|
}
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
this._warnOnPreloadedModules();
|
|
for (const module of this._modules) {
|
|
const hookFn = (exports, name, baseDir) => {
|
|
if (!baseDir && path.isAbsolute(name)) {
|
|
const parsedPath = path.parse(name);
|
|
name = parsedPath.name;
|
|
baseDir = parsedPath.dir;
|
|
}
|
|
return this._onRequire(module, exports, name, baseDir);
|
|
};
|
|
const onRequire = (exports, name, baseDir) => {
|
|
return this._onRequire(module, exports, name, baseDir);
|
|
};
|
|
// `RequireInTheMiddleSingleton` does not support absolute paths.
|
|
// For an absolute paths, we must create a separate instance of the
|
|
// require-in-the-middle `Hook`.
|
|
const hook = path.isAbsolute(module.name)
|
|
? new HookRequire([module.name], { internals: true }, onRequire)
|
|
: this._requireInTheMiddleSingleton.register(module.name, onRequire);
|
|
this._hooks.push(hook);
|
|
const esmHook = new HookImport([module.name], { internals: false }, hookFn);
|
|
this._hooks.push(esmHook);
|
|
}
|
|
}
|
|
disable() {
|
|
if (!this._enabled) {
|
|
return;
|
|
}
|
|
this._enabled = false;
|
|
for (const module of this._modules) {
|
|
if (typeof module.unpatch === 'function' && module.moduleExports) {
|
|
this._diag.debug('Removing instrumentation patch for nodejs module on instrumentation disabled', {
|
|
module: module.name,
|
|
version: module.moduleVersion,
|
|
});
|
|
module.unpatch(module.moduleExports, module.moduleVersion);
|
|
}
|
|
for (const file of module.files) {
|
|
if (file.moduleExports) {
|
|
this._diag.debug('Removing instrumentation patch for nodejs module file on instrumentation disabled', {
|
|
module: module.name,
|
|
version: module.moduleVersion,
|
|
fileName: file.name,
|
|
});
|
|
file.unpatch(file.moduleExports, module.moduleVersion);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
isEnabled() {
|
|
return this._enabled;
|
|
}
|
|
}
|
|
function isSupported(supportedVersions, version, includePrerelease) {
|
|
if (typeof version === 'undefined') {
|
|
// If we don't have the version, accept the wildcard case only
|
|
return supportedVersions.includes('*');
|
|
}
|
|
return supportedVersions.some(supportedVersion => {
|
|
return satisfies(version, supportedVersion, { includePrerelease });
|
|
});
|
|
}
|
|
//# sourceMappingURL=instrumentation.js.map |