diff --git a/doc/contributing/dependencies.md b/doc/contributing/dependencies.md
index 6675f25231f..b21af2b86e4 100644
--- a/doc/contributing/dependencies.md
+++ b/doc/contributing/dependencies.md
@@ -23,7 +23,7 @@ An exception is granted for dependencies on `@opentelemetry/api`, which, if used
## Third-Party Library Dependencies
-Packages categorized as third-party and listed under the `"dependencies"` section (e.g., @grpc/grpc-js, @grpc/proto-loader, shimmer, etc.) should remain unpinned and utilize the caret (`^`) symbol. This approach offers several advantages:
+Packages categorized as third-party and listed under the `"dependencies"` section (e.g., @grpc/grpc-js, @grpc/proto-loader, etc.) should remain unpinned and utilize the caret (`^`) symbol. This approach offers several advantages:
- Our users could get bug fixes of those 3rd-party packages easily, without waiting for us to update our library.
- In cases where multiple packages have dependencies on different versions of the same package, npm will opt for the most recent version, saving space and preventing potential disruptions.
diff --git a/experimental/CHANGELOG.md b/experimental/CHANGELOG.md
index c53c4178997..9f94622f4a4 100644
--- a/experimental/CHANGELOG.md
+++ b/experimental/CHANGELOG.md
@@ -14,6 +14,8 @@ For notes on migrating to 2.x / 0.200.x see [the upgrade guide](doc/upgrade-to-2
### :bug: Bug Fixes
+* fix(instrumentation): remove dependency on the shimmer module [#5652](https://github.com/open-telemetry/opentelemetry-js/pull/5652) @cjihrig
+
### :books: Documentation
### :house: Internal
diff --git a/experimental/packages/opentelemetry-instrumentation/LICENSES/shimmer/LICENSE b/experimental/packages/opentelemetry-instrumentation/LICENSES/shimmer/LICENSE
new file mode 100644
index 00000000000..a87c6555432
--- /dev/null
+++ b/experimental/packages/opentelemetry-instrumentation/LICENSES/shimmer/LICENSE
@@ -0,0 +1,25 @@
+BSD 2-Clause License
+
+Copyright (c) 2013-2019, Forrest L Norvell
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/experimental/packages/opentelemetry-instrumentation/README.md b/experimental/packages/opentelemetry-instrumentation/README.md
index 865592e282b..92ee119c395 100644
--- a/experimental/packages/opentelemetry-instrumentation/README.md
+++ b/experimental/packages/opentelemetry-instrumentation/README.md
@@ -245,6 +245,8 @@ Instrumentations for external modules (e.g. express, mongodb,...) hooks the `req
Apache 2.0 - See [LICENSE][license-url] for more information.
+Third-party licenses and copyright notices can be found in the [LICENSES directory](./LICENSES).
+
## Useful links
- For more information on OpenTelemetry, visit:
diff --git a/experimental/packages/opentelemetry-instrumentation/package.json b/experimental/packages/opentelemetry-instrumentation/package.json
index a73128671c7..cf927d37332 100644
--- a/experimental/packages/opentelemetry-instrumentation/package.json
+++ b/experimental/packages/opentelemetry-instrumentation/package.json
@@ -35,6 +35,7 @@
"hook.mjs",
"doc",
"LICENSE",
+ "LICENSES/**/*",
"README.md"
],
"scripts": {
@@ -71,10 +72,8 @@
},
"dependencies": {
"@opentelemetry/api-logs": "0.201.1",
- "@types/shimmer": "^1.2.0",
"import-in-the-middle": "^1.8.1",
- "require-in-the-middle": "^7.1.1",
- "shimmer": "^1.2.1"
+ "require-in-the-middle": "^7.1.1"
},
"peerDependencies": {
"@opentelemetry/api": "^1.3.0"
diff --git a/experimental/packages/opentelemetry-instrumentation/src/instrumentation.ts b/experimental/packages/opentelemetry-instrumentation/src/instrumentation.ts
index 114f25d67bb..572de28e18b 100644
--- a/experimental/packages/opentelemetry-instrumentation/src/instrumentation.ts
+++ b/experimental/packages/opentelemetry-instrumentation/src/instrumentation.ts
@@ -26,7 +26,7 @@ import {
Span,
} from '@opentelemetry/api';
import { Logger, LoggerProvider, logs } from '@opentelemetry/api-logs';
-import * as shimmer from 'shimmer';
+import * as shimmer from './shimmer';
import {
InstrumentationModuleDefinition,
Instrumentation,
diff --git a/experimental/packages/opentelemetry-instrumentation/src/platform/node/instrumentation.ts b/experimental/packages/opentelemetry-instrumentation/src/platform/node/instrumentation.ts
index a74b687f4ad..8c63389cd95 100644
--- a/experimental/packages/opentelemetry-instrumentation/src/platform/node/instrumentation.ts
+++ b/experimental/packages/opentelemetry-instrumentation/src/platform/node/instrumentation.ts
@@ -18,7 +18,7 @@ import * as types from '../../types';
import * as path from 'path';
import { types as utilTypes } from 'util';
import { satisfies } from '../../semver';
-import { wrap, unwrap, massWrap, massUnwrap } from 'shimmer';
+import { wrap, unwrap, massWrap, massUnwrap } from '../../shimmer';
import { InstrumentationAbstract } from '../../instrumentation';
import {
RequireInTheMiddleSingleton,
diff --git a/experimental/packages/opentelemetry-instrumentation/src/shimmer.ts b/experimental/packages/opentelemetry-instrumentation/src/shimmer.ts
new file mode 100644
index 00000000000..8e633b134a2
--- /dev/null
+++ b/experimental/packages/opentelemetry-instrumentation/src/shimmer.ts
@@ -0,0 +1,197 @@
+/*
+ * 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.
+ */
+
+/*
+ * BSD 2-Clause License
+ *
+ * Copyright (c) 2013-2019, Forrest L Norvell
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* Modified by OpenTelemetry Authors
+ * - converted to TypeScript
+ * - aligned with style-guide
+ */
+
+import { ShimWrapped } from './types';
+
+// Default to complaining loudly when things don't go according to plan.
+// eslint-disable-next-line no-console
+let logger: typeof console.error = console.error.bind(console);
+
+// Sets a property on an object, preserving its enumerability.
+// This function assumes that the property is already writable.
+function defineProperty(obj: object, name: PropertyKey, value: unknown): void {
+ const enumerable =
+ !!obj[name as keyof typeof obj] &&
+ Object.prototype.propertyIsEnumerable.call(obj, name);
+
+ Object.defineProperty(obj, name, {
+ configurable: true,
+ enumerable,
+ writable: true,
+ value,
+ });
+}
+
+export const wrap = (
+ nodule: Nodule,
+ name: FieldName,
+ wrapper: (original: Nodule[FieldName], name: FieldName) => Nodule[FieldName]
+): ShimWrapped | undefined => {
+ if (!nodule || !nodule[name]) {
+ logger('no original function ' + String(name) + ' to wrap');
+ return;
+ }
+
+ if (!wrapper) {
+ logger('no wrapper function');
+ logger(new Error().stack);
+ return;
+ }
+
+ const original = nodule[name];
+
+ if (typeof original !== 'function' || typeof wrapper !== 'function') {
+ logger('original object and wrapper must be functions');
+ return;
+ }
+
+ const wrapped = wrapper(original, name) as object;
+
+ defineProperty(wrapped, '__original', original);
+ defineProperty(wrapped, '__unwrap', () => {
+ if (nodule[name] === wrapped) {
+ defineProperty(nodule, name, original);
+ }
+ });
+ defineProperty(wrapped, '__wrapped', true);
+ defineProperty(nodule, name, wrapped);
+ return wrapped as ShimWrapped;
+};
+
+export const massWrap = (
+ nodules: Nodule[],
+ names: FieldName[],
+ wrapper: (original: Nodule[FieldName]) => Nodule[FieldName]
+): void => {
+ if (!nodules) {
+ logger('must provide one or more modules to patch');
+ logger(new Error().stack);
+ return;
+ } else if (!Array.isArray(nodules)) {
+ nodules = [nodules];
+ }
+
+ if (!(names && Array.isArray(names))) {
+ logger('must provide one or more functions to wrap on modules');
+ return;
+ }
+
+ nodules.forEach(nodule => {
+ names.forEach(name => {
+ wrap(nodule, name, wrapper);
+ });
+ });
+};
+
+export const unwrap = (
+ nodule: Nodule,
+ name: keyof Nodule
+): void => {
+ if (!nodule || !nodule[name]) {
+ logger('no function to unwrap.');
+ logger(new Error().stack);
+ return;
+ }
+
+ const wrapped = nodule[name] as unknown as ShimWrapped;
+
+ if (!wrapped.__unwrap) {
+ logger(
+ 'no original to unwrap to -- has ' +
+ String(name) +
+ ' already been unwrapped?'
+ );
+ } else {
+ wrapped.__unwrap();
+ return;
+ }
+};
+
+export const massUnwrap = (
+ nodules: Nodule[],
+ names: Array
+): void => {
+ if (!nodules) {
+ logger('must provide one or more modules to patch');
+ logger(new Error().stack);
+ return;
+ } else if (!Array.isArray(nodules)) {
+ nodules = [nodules];
+ }
+
+ if (!(names && Array.isArray(names))) {
+ logger('must provide one or more functions to unwrap on modules');
+ return;
+ }
+
+ nodules.forEach(nodule => {
+ names.forEach(name => {
+ unwrap(nodule, name);
+ });
+ });
+};
+
+export interface ShimmerOptions {
+ logger?: typeof console.error;
+}
+
+export default function shimmer(options: ShimmerOptions): void {
+ if (options && options.logger) {
+ if (typeof options.logger !== 'function') {
+ logger("new logger isn't a function, not replacing");
+ } else {
+ logger = options.logger;
+ }
+ }
+}
+
+shimmer.wrap = wrap;
+shimmer.massWrap = massWrap;
+shimmer.unwrap = unwrap;
+shimmer.massUnwrap = massUnwrap;
diff --git a/experimental/packages/opentelemetry-instrumentation/test/common/shimmer.test.ts b/experimental/packages/opentelemetry-instrumentation/test/common/shimmer.test.ts
new file mode 100644
index 00000000000..b2a11dcf582
--- /dev/null
+++ b/experimental/packages/opentelemetry-instrumentation/test/common/shimmer.test.ts
@@ -0,0 +1,750 @@
+/*
+ * 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.
+ */
+
+/*
+ * BSD 2-Clause License
+ *
+ * Copyright (c) 2013-2019, Forrest L Norvell
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* Modified by OpenTelemetry Authors
+ * - converted to TypeScript
+ * - adapted tests to use `node:assert` and `mocha`
+ * - aligned with style-guide
+ */
+
+import * as assert from 'assert';
+import * as sinon from 'sinon';
+import shimmer from '../../src/shimmer';
+import { ShimWrapped } from '../../src';
+
+describe('Shimmer', function () {
+ describe('init', function () {
+ it('shimmer initialization', function () {
+ assert.doesNotThrow(function () {
+ (shimmer as any)();
+ });
+ const mock = sinon.expectation
+ .create('logger')
+ .withArgs('no original function undefined to wrap')
+ .once();
+
+ assert.doesNotThrow(function () {
+ shimmer({ logger: mock });
+ }, "initializer doesn't throw");
+
+ assert.doesNotThrow(function () {
+ (shimmer as any).wrap();
+ }, "invoking the wrap method with no params doesn't throw");
+
+ assert.doesNotThrow(function () {
+ mock.verify();
+ }, 'logger method was called with the expected message');
+ });
+
+ it('shimmer initialized with non-function logger', function () {
+ const mock = sinon.expectation
+ .create('logger')
+ .withArgs("new logger isn't a function, not replacing")
+ .once();
+
+ shimmer({ logger: mock });
+
+ assert.doesNotThrow(function () {
+ shimmer({ logger: { ham: 'chunx' } } as any);
+ }, "even bad initialization doesn't throw");
+
+ assert.doesNotThrow(function () {
+ mock.verify();
+ }, 'logger initialization failed in the expected way');
+ });
+ });
+
+ describe('wrap', function () {
+ let outsider = 0;
+
+ function counter() {
+ return ++outsider;
+ }
+ function anticounter() {
+ return --outsider;
+ }
+
+ const generator: any = {
+ inc: counter,
+ };
+ Object.defineProperty(generator, 'dec', {
+ value: anticounter,
+ writable: true,
+ configurable: true,
+ enumerable: false,
+ });
+
+ it('should wrap safely', function () {
+ assert.equal(counter, generator.inc, 'method is mapped to function');
+ assert.doesNotThrow(function () {
+ generator.inc();
+ }, 'original function works');
+ assert.equal(1, outsider, 'calls have side effects');
+
+ let count = 0;
+ function wrapper(original: any) {
+ return function (this: any) {
+ count++;
+ const returned = original.apply(this, arguments);
+ count++;
+ return returned;
+ };
+ }
+ shimmer.wrap(generator, 'inc', wrapper);
+
+ assert.ok(
+ (generator.inc as unknown as ShimWrapped).__wrapped,
+ "function tells us it's wrapped"
+ );
+ assert.equal(
+ (generator.inc as unknown as ShimWrapped).__original,
+ counter,
+ 'original function is available'
+ );
+ assert.doesNotThrow(function () {
+ generator.inc();
+ }, 'wrapping works');
+ assert.equal(
+ 2,
+ count,
+ 'both pre and post increments should have happened'
+ );
+ assert.equal(2, outsider, 'original function has still been called');
+ assert.ok(
+ Object.prototype.propertyIsEnumerable.call(generator, 'inc'),
+ 'wrapped enumerable property is still enumerable'
+ );
+ assert.equal(
+ Object.keys(generator.inc).length,
+ 0,
+ 'wrapped object has no additional properties'
+ );
+
+ shimmer.wrap(generator, 'dec', function (original) {
+ return function (this: any) {
+ return original.apply(this, arguments);
+ };
+ });
+
+ assert.ok(
+ !Object.prototype.propertyIsEnumerable.call(generator, 'dec'),
+ 'wrapped unenumerable property is still unenumerable'
+ );
+ });
+
+ it('wrap called with no arguments', function () {
+ const mock = sinon.expectation
+ .create('logger')
+ .withExactArgs('no original function undefined to wrap')
+ .once();
+ shimmer({ logger: mock });
+
+ assert.doesNotThrow(function () {
+ (shimmer as any).wrap();
+ }, "wrapping with no arguments doesn't throw");
+
+ assert.doesNotThrow(function () {
+ mock.verify();
+ }, 'logger was called with the expected message');
+ });
+
+ it('wrap called with module but nothing else', function () {
+ const mock = sinon.expectation
+ .create('logger')
+ .withExactArgs('no original function undefined to wrap')
+ .once();
+ shimmer({ logger: mock });
+
+ assert.doesNotThrow(function () {
+ (shimmer as any).wrap(generator);
+ }, "wrapping with only 1 argument doesn't throw");
+
+ assert.doesNotThrow(function () {
+ mock.verify();
+ }, 'logger was called with the expected message');
+ });
+
+ it('wrap called with original but no wrapper', function () {
+ const mock = sinon.expectation.create('logger').twice();
+ shimmer({ logger: mock });
+
+ assert.doesNotThrow(function () {
+ (shimmer as any).wrap(generator, 'inc');
+ }, "wrapping with only original method doesn't throw");
+
+ assert.doesNotThrow(function () {
+ mock.verify();
+ }, 'logger was called with the expected message');
+ });
+
+ it('wrap called with non-function original', function () {
+ const mock = sinon.expectation
+ .create('logger')
+ .withExactArgs('original object and wrapper must be functions')
+ .once();
+ shimmer({ logger: mock });
+
+ assert.doesNotThrow(function () {
+ shimmer.wrap({ orange: 'slices' }, 'orange', function () {} as any);
+ }, "wrapping non-function original doesn't throw");
+
+ assert.doesNotThrow(function () {
+ mock.verify();
+ }, 'logger was called with the expected message');
+ });
+
+ it('wrap called with non-function wrapper', function () {
+ const mock = sinon.expectation
+ .create('logger')
+ .withArgs('original object and wrapper must be functions')
+ .once();
+ shimmer({ logger: mock });
+
+ assert.doesNotThrow(function () {
+ (shimmer as any).wrap({ orange: function () {} }, 'orange', 'hamchunx');
+ }, "wrapping with non-function wrapper doesn't throw");
+
+ assert.doesNotThrow(function () {
+ mock.verify();
+ }, 'logger was called with the expected message');
+ });
+ });
+
+ describe('unwrap', function () {
+ let outsider = 0;
+
+ function counter() {
+ return ++outsider;
+ }
+
+ const generator = {
+ inc: counter,
+ };
+
+ it('should unwrap safely', function () {
+ assert.equal(
+ counter,
+ generator.inc,
+ 'basic function equality testing should work'
+ );
+ assert.doesNotThrow(function () {
+ generator.inc();
+ });
+ assert.equal(1, outsider, 'calls have side effects');
+
+ function wrapper(original: any) {
+ return function (this: any) {
+ return original.apply(this, arguments);
+ };
+ }
+ shimmer.wrap(generator, 'inc', wrapper);
+
+ assert.notEqual(counter, generator.inc, 'function should be wrapped');
+
+ assert.doesNotThrow(function () {
+ generator.inc();
+ });
+ assert.equal(2, outsider, 'original function has still been called');
+
+ shimmer.unwrap(generator, 'inc');
+ assert.equal(
+ counter,
+ generator.inc,
+ 'basic function equality testing should work'
+ );
+ assert.doesNotThrow(function () {
+ generator.inc();
+ });
+ assert.equal(3, outsider, 'original function has still been called');
+ });
+
+ it("shouldn't throw on double unwrapping", function () {
+ assert.equal(
+ counter,
+ generator.inc,
+ 'basic function equality testing should work'
+ );
+
+ const mock = sinon.expectation
+ .create('logger')
+ .withArgs(
+ 'no original to unwrap to -- ' + 'has inc already been unwrapped?'
+ )
+ .once();
+ shimmer({ logger: mock });
+
+ function wrapper(original: any) {
+ return function (this: any) {
+ return original.apply(this, arguments);
+ };
+ }
+ shimmer.wrap(generator, 'inc', wrapper);
+
+ assert.notEqual(counter, generator.inc, 'function should be wrapped');
+
+ shimmer.unwrap(generator, 'inc');
+ assert.equal(
+ counter,
+ generator.inc,
+ 'basic function equality testing should work'
+ );
+
+ assert.doesNotThrow(function () {
+ shimmer.unwrap(generator, 'inc');
+ }, 'should double unwrap without issue');
+ assert.equal(
+ counter,
+ generator.inc,
+ 'function is unchanged after unwrapping'
+ );
+
+ mock.verify();
+ });
+
+ it('unwrap called with no arguments', function () {
+ const mock = sinon.expectation.create('logger').twice();
+ shimmer({ logger: mock });
+
+ assert.doesNotThrow(function () {
+ (shimmer as any).unwrap();
+ }, 'should log instead of throwing');
+
+ mock.verify();
+ });
+
+ it('unwrap called with module but no name', function () {
+ const mock = sinon.expectation.create('logger').twice();
+ shimmer({ logger: mock });
+
+ assert.doesNotThrow(function () {
+ (shimmer as any).unwrap({});
+ }, 'should log instead of throwing');
+
+ mock.verify();
+ });
+ });
+
+ describe('massWrap', function () {
+ let outsider = 0;
+ function counter() {
+ return ++outsider;
+ }
+ function anticounter() {
+ return --outsider;
+ }
+
+ const generator: any = {
+ inc: counter,
+ dec: anticounter,
+ };
+
+ const arrow = {
+ in: counter,
+ out: anticounter,
+ };
+
+ const nester = {
+ in: counter,
+ out: anticounter,
+ };
+
+ it('should wrap multiple functions safely', function () {
+ assert.equal(
+ counter,
+ generator.inc,
+ 'basic function equality testing should work'
+ );
+ assert.equal(
+ anticounter,
+ generator.dec,
+ 'basic function equality testing should work'
+ );
+ assert.doesNotThrow(function () {
+ generator.inc();
+ });
+ assert.doesNotThrow(function () {
+ generator.dec();
+ });
+ assert.equal(0, outsider, 'calls have side effects');
+
+ let count = 0;
+ function wrapper(original: any) {
+ return function (this: any) {
+ count++;
+ const returned = original.apply(this, arguments);
+ count++;
+ return returned;
+ };
+ }
+ (shimmer as any).massWrap(generator, ['inc', 'dec'], wrapper);
+
+ assert.doesNotThrow(function () {
+ generator.inc();
+ });
+ assert.doesNotThrow(function () {
+ generator.dec();
+ });
+ assert.equal(
+ 4,
+ count,
+ 'both pre and post increments should have happened'
+ );
+ assert.equal(0, outsider, 'original function has still been called');
+ });
+
+ it('should wrap multiple functions on multiple modules safely', function () {
+ assert.equal(
+ counter,
+ arrow.in,
+ 'basic function equality testing should work'
+ );
+ assert.equal(
+ counter,
+ nester.in,
+ 'basic function equality testing should work'
+ );
+ assert.equal(
+ anticounter,
+ arrow.out,
+ 'basic function equality testing should work'
+ );
+ assert.equal(
+ anticounter,
+ nester.out,
+ 'basic function equality testing should work'
+ );
+
+ assert.doesNotThrow(function () {
+ arrow.in();
+ });
+ assert.doesNotThrow(function () {
+ nester.in();
+ });
+ assert.doesNotThrow(function () {
+ arrow.out();
+ });
+ assert.doesNotThrow(function () {
+ nester.out();
+ });
+
+ assert.equal(0, outsider, 'calls have side effects');
+
+ let count = 0;
+
+ function wrapper(original: any) {
+ return function (this: any) {
+ count++;
+ const returned = original.apply(this, arguments);
+ count++;
+ return returned;
+ };
+ }
+ shimmer.massWrap([arrow, nester], ['in', 'out'], wrapper);
+
+ assert.doesNotThrow(function () {
+ arrow.in();
+ });
+ assert.doesNotThrow(function () {
+ arrow.out();
+ });
+ assert.doesNotThrow(function () {
+ nester.in();
+ });
+ assert.doesNotThrow(function () {
+ nester.out();
+ });
+
+ assert.equal(
+ 8,
+ count,
+ 'both pre and post increments should have happened'
+ );
+ assert.equal(0, outsider, 'original function has still been called');
+ });
+
+ it('wrap called with no arguments', function () {
+ const mock = sinon.expectation.create('logger').twice();
+ shimmer({ logger: mock });
+
+ assert.doesNotThrow(function () {
+ (shimmer as any).massWrap();
+ }, "wrapping with no arguments doesn't throw");
+
+ assert.doesNotThrow(function () {
+ mock.verify();
+ }, 'logger was called with the expected message');
+ });
+
+ it('wrap called with module but nothing else', function () {
+ const mock = sinon.expectation
+ .create('logger')
+ .withExactArgs('must provide one or more functions to wrap on modules')
+ .once();
+ shimmer({ logger: mock });
+
+ assert.doesNotThrow(function () {
+ (shimmer as any).massWrap(generator);
+ }, "wrapping with only 1 argument doesn't throw");
+
+ assert.doesNotThrow(function () {
+ mock.verify();
+ }, 'logger was called with the expected message');
+ });
+
+ it('wrap called with original but no wrapper', function () {
+ const mock = sinon.expectation.create('logger').twice();
+ shimmer({ logger: mock });
+
+ assert.doesNotThrow(function () {
+ (shimmer as any).massWrap(generator, ['inc']);
+ }, "wrapping with only original function doesn't throw");
+
+ mock.verify();
+ });
+
+ it('wrap called with non-function original', function () {
+ const mock = sinon.expectation
+ .create('logger')
+ .withExactArgs('must provide one or more functions to wrap on modules')
+ .once();
+ shimmer({ logger: mock });
+
+ assert.doesNotThrow(function () {
+ (shimmer as any).massWrap(
+ { orange: 'slices' },
+ 'orange',
+ function () {}
+ );
+ }, "wrapping non-function original doesn't throw");
+
+ mock.verify();
+ });
+
+ it('wrap called with non-function wrapper', function () {
+ const mock = sinon.expectation
+ .create('logger')
+ .withArgs('must provide one or more functions to wrap on modules')
+ .once();
+ shimmer({ logger: mock });
+
+ assert.doesNotThrow(function () {
+ (shimmer as any).massWrap(
+ { orange: function () {} },
+ 'orange',
+ 'hamchunx'
+ );
+ }, "wrapping with non-function wrapper doesn't throw");
+
+ mock.verify();
+ });
+ });
+
+ describe('massUnwrap', function () {
+ let outsider = 0;
+
+ function counter() {
+ return ++outsider;
+ }
+ function anticounter() {
+ return --outsider;
+ }
+
+ const generator: any = {
+ inc: counter,
+ dec: anticounter,
+ };
+
+ it('should unwrap safely', function () {
+ assert.equal(
+ counter,
+ generator.inc,
+ 'basic function equality testing should work'
+ );
+ assert.equal(
+ anticounter,
+ generator.dec,
+ 'basic function equality testing should work'
+ );
+ assert.doesNotThrow(function () {
+ generator.inc();
+ });
+ assert.equal(1, outsider, 'calls have side effects');
+ assert.doesNotThrow(function () {
+ generator.dec();
+ });
+ assert.equal(0, outsider, 'calls have side effects');
+
+ function wrapper(original: any) {
+ return function (this: any) {
+ return original.apply(this, arguments);
+ };
+ }
+
+ (shimmer as any).massWrap(generator, ['inc', 'dec'], wrapper);
+
+ assert.notEqual(counter, generator.inc, 'function should be wrapped');
+ assert.notEqual(anticounter, generator.dec, 'function should be wrapped');
+
+ assert.doesNotThrow(function () {
+ generator.inc();
+ });
+ assert.equal(1, outsider, 'original function has still been called');
+ assert.doesNotThrow(function () {
+ generator.dec();
+ });
+ assert.equal(0, outsider, 'original function has still been called');
+
+ shimmer.massUnwrap(generator, ['inc', 'dec']);
+ assert.equal(
+ counter,
+ generator.inc,
+ 'basic function equality testing should work'
+ );
+ assert.equal(
+ anticounter,
+ generator.dec,
+ 'basic function equality testing should work'
+ );
+
+ assert.doesNotThrow(function () {
+ generator.inc();
+ });
+ assert.equal(1, outsider, 'original function has still been called');
+ assert.doesNotThrow(function () {
+ generator.dec();
+ });
+ assert.equal(0, outsider, 'original function has still been called');
+ });
+
+ it("shouldn't throw on double unwrapping", function () {
+ assert.equal(
+ counter,
+ generator.inc,
+ 'basic function equality testing should work'
+ );
+ assert.equal(
+ anticounter,
+ generator.dec,
+ 'basic function equality testing should work'
+ );
+
+ const mock = sinon.stub();
+ shimmer({ logger: mock });
+
+ function wrapper(original: any) {
+ return function (this: any) {
+ return original.apply(this, arguments);
+ };
+ }
+ shimmer.wrap(generator, 'inc', wrapper);
+ shimmer.wrap(generator, 'dec', wrapper);
+
+ assert.notEqual(counter, generator.inc, 'function should be wrapped');
+ assert.notEqual(anticounter, generator.dec, 'function should be wrapped');
+
+ shimmer.massUnwrap(generator, ['inc', 'dec']);
+ assert.equal(
+ counter,
+ generator.inc,
+ 'basic function equality testing should work'
+ );
+ assert.equal(
+ anticounter,
+ generator.dec,
+ 'basic function equality testing should work'
+ );
+
+ assert.doesNotThrow(function () {
+ shimmer.massUnwrap(generator, ['inc', 'dec']);
+ }, 'should double unwrap without issue');
+ assert.equal(
+ counter,
+ generator.inc,
+ 'function is unchanged after unwrapping'
+ );
+ assert.equal(
+ anticounter,
+ generator.dec,
+ 'function is unchanged after unwrapping'
+ );
+
+ sinon.assert.calledWith(
+ mock,
+ 'no original to unwrap to -- ' + 'has inc already been unwrapped?'
+ );
+ sinon.assert.calledWith(
+ mock,
+ 'no original to unwrap to -- ' + 'has dec already been unwrapped?'
+ );
+ sinon.assert.calledTwice(mock);
+ });
+
+ it('massUnwrap called with no arguments', function () {
+ const mock = sinon.expectation.create('logger').twice();
+ shimmer({ logger: mock });
+
+ assert.doesNotThrow(function () {
+ (shimmer as any).massUnwrap();
+ }, 'should log instead of throwing');
+
+ mock.verify();
+ });
+
+ it('massUnwrap called with module but nothing else', function () {
+ const mock = sinon.expectation
+ .create('logger')
+ .withExactArgs(
+ 'must provide one or more functions to unwrap on modules'
+ )
+ .once();
+ shimmer({ logger: mock });
+
+ assert.doesNotThrow(function () {
+ (shimmer as any).massUnwrap(generator);
+ }, "wrapping with only 1 argument doesn't throw");
+
+ mock.verify();
+ });
+ });
+});
diff --git a/package-lock.json b/package-lock.json
index 83b3c489238..b23004300c2 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1731,10 +1731,8 @@
"license": "Apache-2.0",
"dependencies": {
"@opentelemetry/api-logs": "0.201.1",
- "@types/shimmer": "^1.2.0",
"import-in-the-middle": "^1.8.1",
- "require-in-the-middle": "^7.1.1",
- "shimmer": "^1.2.1"
+ "require-in-the-middle": "^7.1.1"
},
"devDependencies": {
"@babel/core": "7.27.1",
@@ -8929,12 +8927,6 @@
"@types/send": "*"
}
},
- "node_modules/@types/shimmer": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/@types/shimmer/-/shimmer-1.2.0.tgz",
- "integrity": "sha512-UE7oxhQLLd9gub6JKIAhDq06T0F6FnztwMNRvYgjeQSBeMc1ZG/tA47EwfduvkuQS8apbkM/lpLpWsaCeYsXVg==",
- "license": "MIT"
- },
"node_modules/@types/sinon": {
"version": "17.0.4",
"resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-17.0.4.tgz",
@@ -35664,7 +35656,6 @@
"@opentelemetry/sdk-metrics": "2.0.1",
"@types/mocha": "10.0.10",
"@types/node": "18.6.5",
- "@types/shimmer": "^1.2.0",
"@types/sinon": "17.0.4",
"@types/webpack-env": "1.16.3",
"babel-loader": "10.0.0",
@@ -35682,7 +35673,6 @@
"mocha": "11.1.0",
"nyc": "17.1.0",
"require-in-the-middle": "^7.1.1",
- "shimmer": "^1.2.1",
"sinon": "15.1.2",
"ts-loader": "9.5.2",
"typescript": "5.0.4",
@@ -38048,11 +38038,6 @@
"@types/send": "*"
}
},
- "@types/shimmer": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/@types/shimmer/-/shimmer-1.2.0.tgz",
- "integrity": "sha512-UE7oxhQLLd9gub6JKIAhDq06T0F6FnztwMNRvYgjeQSBeMc1ZG/tA47EwfduvkuQS8apbkM/lpLpWsaCeYsXVg=="
- },
"@types/sinon": {
"version": "17.0.4",
"resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-17.0.4.tgz",