diff --git a/.github/component_owners.yml b/.github/component_owners.yml
index 94404286cd..53ad4775c1 100644
--- a/.github/component_owners.yml
+++ b/.github/component_owners.yml
@@ -72,6 +72,11 @@ components:
- mottibec
plugins/node/instrumentation-tedious: []
# Unmaintained
+ plugins/node/instrumentation-typeorm:
+ - seemk
+ - weyert
+ - t2t2
+ - mhennoch
plugins/node/opentelemetry-instrumentation-aws-lambda:
- jj22ee
plugins/node/opentelemetry-instrumentation-aws-sdk:
diff --git a/.release-please-manifest.json b/.release-please-manifest.json
index 16d91bc486..725b970f10 100644
--- a/.release-please-manifest.json
+++ b/.release-please-manifest.json
@@ -28,6 +28,7 @@
"plugins/node/instrumentation-runtime-node": "0.14.0",
"plugins/node/instrumentation-socket.io": "0.47.0",
"plugins/node/instrumentation-tedious": "0.19.0",
+ "plugins/node/instrumentation-typeorm": "0.0.1",
"plugins/node/instrumentation-undici": "0.11.0",
"plugins/node/opentelemetry-instrumentation-aws-lambda": "0.51.0",
"plugins/node/opentelemetry-instrumentation-aws-sdk": "0.51.0",
diff --git a/package-lock.json b/package-lock.json
index 103ffbeed7..ffd91915b4 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -8465,6 +8465,10 @@
"resolved": "plugins/node/instrumentation-tedious",
"link": true
},
+ "node_modules/@opentelemetry/instrumentation-typeorm": {
+ "resolved": "plugins/node/instrumentation-typeorm",
+ "link": true
+ },
"node_modules/@opentelemetry/instrumentation-undici": {
"resolved": "plugins/node/instrumentation-undici",
"link": true
@@ -10432,6 +10436,13 @@
"integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==",
"dev": true
},
+ "node_modules/@sqltools/formatter": {
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/@sqltools/formatter/-/formatter-1.2.5.tgz",
+ "integrity": "sha512-Uy0+khmZqUrUGm5dmMqVlnvufZRSK0FbYzVgp0UMstm+F5+W2/jnEEQyc9vo1ZR/E5ZI/B1WjjoTqBqwJL6Krw==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/@teppeis/multimaps": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@teppeis/multimaps/-/multimaps-2.0.0.tgz",
@@ -12188,6 +12199,16 @@
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
}
},
+ "node_modules/ansis": {
+ "version": "3.17.0",
+ "resolved": "https://registry.npmjs.org/ansis/-/ansis-3.17.0.tgz",
+ "integrity": "sha512-0qWUglt9JEqLFr3w1I1pbrChn1grhaiAR2ocX1PP/flRmxgtwTzPFFFnfIlD6aMOLQZgSuCRlidD70lvx8yhzg==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=14"
+ }
+ },
"node_modules/any-promise": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz",
@@ -12219,6 +12240,16 @@
"url": "https://github.com/sponsors/jonschlinkert"
}
},
+ "node_modules/app-root-path": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/app-root-path/-/app-root-path-3.1.0.tgz",
+ "integrity": "sha512-biN3PwB2gUtjaYy/isrU3aNWI5w+fAfvHkSvCKeQGxhmYpwKFUxudR3Yya+KqVRHBmEDYh+/lTozYCFbmzX4nA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 6.0.0"
+ }
+ },
"node_modules/append-field": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz",
@@ -14728,6 +14759,13 @@
"node": "*"
}
},
+ "node_modules/dayjs": {
+ "version": "1.11.13",
+ "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz",
+ "integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/debounce": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/debounce/-/debounce-1.2.1.tgz",
@@ -29230,6 +29268,20 @@
"integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
"dev": true
},
+ "node_modules/sha.js": {
+ "version": "2.4.11",
+ "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz",
+ "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==",
+ "dev": true,
+ "license": "(MIT AND BSD-3-Clause)",
+ "dependencies": {
+ "inherits": "^2.0.1",
+ "safe-buffer": "^5.0.1"
+ },
+ "bin": {
+ "sha.js": "bin.js"
+ }
+ },
"node_modules/shallow-clone": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz",
@@ -30145,6 +30197,23 @@
"integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==",
"dev": true
},
+ "node_modules/sql-highlight": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/sql-highlight/-/sql-highlight-6.0.0.tgz",
+ "integrity": "sha512-+fLpbAbWkQ+d0JEchJT/NrRRXbYRNbG15gFpANx73EwxQB1PRjj+k/OI0GTU0J63g8ikGkJECQp9z8XEJZvPRw==",
+ "dev": true,
+ "funding": [
+ "https://github.com/scriptcoded/sql-highlight?sponsor=1",
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/scriptcoded"
+ }
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">=14"
+ }
+ },
"node_modules/sqlite3": {
"version": "5.1.7",
"resolved": "https://registry.npmjs.org/sqlite3/-/sqlite3-5.1.7.tgz",
@@ -34523,6 +34592,527 @@
"node": ">=12.20"
}
},
+ "plugins/node/instrumentation-typeorm": {
+ "name": "@opentelemetry/instrumentation-typeorm",
+ "version": "0.0.1",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@opentelemetry/core": "^2.0.0",
+ "@opentelemetry/instrumentation": "^0.200.0"
+ },
+ "devDependencies": {
+ "@opentelemetry/api": "^1.3.0",
+ "@opentelemetry/contrib-test-utils": "^0.46.0",
+ "@opentelemetry/sdk-trace-base": "^2.0.0",
+ "@types/mocha": "10.0.10",
+ "@types/node": "18.18.14",
+ "nyc": "15.1.0",
+ "rimraf": "5.0.10",
+ "sqlite3": "^5.0.2",
+ "typeorm": "^0.3.21",
+ "typescript": "5.0.4"
+ },
+ "engines": {
+ "node": "^18.19.0 || >=20.6.0"
+ },
+ "peerDependencies": {
+ "@opentelemetry/api": "^1.3.0"
+ }
+ },
+ "plugins/node/instrumentation-typeorm/node_modules/@types/node": {
+ "version": "18.18.14",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-18.18.14.tgz",
+ "integrity": "sha512-iSOeNeXYNYNLLOMDSVPvIFojclvMZ/HDY2dU17kUlcsOsSQETbWIslJbYLZgA+ox8g2XQwSHKTkght1a5X26lQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "undici-types": "~5.26.4"
+ }
+ },
+ "plugins/node/instrumentation-typeorm/node_modules/brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "plugins/node/instrumentation-typeorm/node_modules/bson": {
+ "version": "5.5.1",
+ "resolved": "https://registry.npmjs.org/bson/-/bson-5.5.1.tgz",
+ "integrity": "sha512-ix0EwukN2EpC0SRWIj/7B5+A6uQMQy6KMREI9qQqvgpkV2frH63T0UDVd1SYedL6dNCmDBYB3QtXi4ISk9YT+g==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "peer": true,
+ "engines": {
+ "node": ">=14.20.1"
+ }
+ },
+ "plugins/node/instrumentation-typeorm/node_modules/buffer": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
+ "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "base64-js": "^1.3.1",
+ "ieee754": "^1.2.1"
+ }
+ },
+ "plugins/node/instrumentation-typeorm/node_modules/camelcase": {
+ "version": "5.3.1",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
+ "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "plugins/node/instrumentation-typeorm/node_modules/cliui": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz",
+ "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "string-width": "^4.2.0",
+ "strip-ansi": "^6.0.0",
+ "wrap-ansi": "^6.2.0"
+ }
+ },
+ "plugins/node/instrumentation-typeorm/node_modules/convert-source-map": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz",
+ "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "plugins/node/instrumentation-typeorm/node_modules/dotenv": {
+ "version": "16.4.7",
+ "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz",
+ "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://dotenvx.com"
+ }
+ },
+ "plugins/node/instrumentation-typeorm/node_modules/foreground-child": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz",
+ "integrity": "sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "cross-spawn": "^7.0.0",
+ "signal-exit": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=8.0.0"
+ }
+ },
+ "plugins/node/instrumentation-typeorm/node_modules/ieee754": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
+ "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "BSD-3-Clause"
+ },
+ "plugins/node/instrumentation-typeorm/node_modules/minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "plugins/node/instrumentation-typeorm/node_modules/mongodb": {
+ "version": "5.9.2",
+ "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-5.9.2.tgz",
+ "integrity": "sha512-H60HecKO4Bc+7dhOv4sJlgvenK4fQNqqUIlXxZYQNbfEWSALGAwGoyJd/0Qwk4TttFXUOHJ2ZJQe/52ScaUwtQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "peer": true,
+ "dependencies": {
+ "bson": "^5.5.0",
+ "mongodb-connection-string-url": "^2.6.0",
+ "socks": "^2.7.1"
+ },
+ "engines": {
+ "node": ">=14.20.1"
+ },
+ "optionalDependencies": {
+ "@mongodb-js/saslprep": "^1.1.0"
+ },
+ "peerDependencies": {
+ "@aws-sdk/credential-providers": "^3.188.0",
+ "@mongodb-js/zstd": "^1.0.0",
+ "kerberos": "^1.0.0 || ^2.0.0",
+ "mongodb-client-encryption": ">=2.3.0 <3",
+ "snappy": "^7.2.2"
+ },
+ "peerDependenciesMeta": {
+ "@aws-sdk/credential-providers": {
+ "optional": true
+ },
+ "@mongodb-js/zstd": {
+ "optional": true
+ },
+ "kerberos": {
+ "optional": true
+ },
+ "mongodb-client-encryption": {
+ "optional": true
+ },
+ "snappy": {
+ "optional": true
+ }
+ }
+ },
+ "plugins/node/instrumentation-typeorm/node_modules/nyc": {
+ "version": "15.1.0",
+ "resolved": "https://registry.npmjs.org/nyc/-/nyc-15.1.0.tgz",
+ "integrity": "sha512-jMW04n9SxKdKi1ZMGhvUTHBN0EICCRkHemEoE5jm6mTYcqcdas0ATzgUgejlQUHMvpnOZqGB5Xxsv9KxJW1j8A==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "@istanbuljs/load-nyc-config": "^1.0.0",
+ "@istanbuljs/schema": "^0.1.2",
+ "caching-transform": "^4.0.0",
+ "convert-source-map": "^1.7.0",
+ "decamelize": "^1.2.0",
+ "find-cache-dir": "^3.2.0",
+ "find-up": "^4.1.0",
+ "foreground-child": "^2.0.0",
+ "get-package-type": "^0.1.0",
+ "glob": "^7.1.6",
+ "istanbul-lib-coverage": "^3.0.0",
+ "istanbul-lib-hook": "^3.0.0",
+ "istanbul-lib-instrument": "^4.0.0",
+ "istanbul-lib-processinfo": "^2.0.2",
+ "istanbul-lib-report": "^3.0.0",
+ "istanbul-lib-source-maps": "^4.0.0",
+ "istanbul-reports": "^3.0.2",
+ "make-dir": "^3.0.0",
+ "node-preload": "^0.2.1",
+ "p-map": "^3.0.0",
+ "process-on-spawn": "^1.0.0",
+ "resolve-from": "^5.0.0",
+ "rimraf": "^3.0.0",
+ "signal-exit": "^3.0.2",
+ "spawn-wrap": "^2.0.0",
+ "test-exclude": "^6.0.0",
+ "yargs": "^15.0.2"
+ },
+ "bin": {
+ "nyc": "bin/nyc.js"
+ },
+ "engines": {
+ "node": ">=8.9"
+ }
+ },
+ "plugins/node/instrumentation-typeorm/node_modules/nyc/node_modules/glob": {
+ "version": "7.2.3",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
+ "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+ "deprecated": "Glob versions prior to v9 are no longer supported",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.1.1",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ },
+ "engines": {
+ "node": "*"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "plugins/node/instrumentation-typeorm/node_modules/nyc/node_modules/rimraf": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
+ "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
+ "deprecated": "Rimraf versions prior to v4 are no longer supported",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "glob": "^7.1.3"
+ },
+ "bin": {
+ "rimraf": "bin.js"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "plugins/node/instrumentation-typeorm/node_modules/nyc/node_modules/yargs": {
+ "version": "15.4.1",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz",
+ "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cliui": "^6.0.0",
+ "decamelize": "^1.2.0",
+ "find-up": "^4.1.0",
+ "get-caller-file": "^2.0.1",
+ "require-directory": "^2.1.1",
+ "require-main-filename": "^2.0.0",
+ "set-blocking": "^2.0.0",
+ "string-width": "^4.2.0",
+ "which-module": "^2.0.0",
+ "y18n": "^4.0.0",
+ "yargs-parser": "^18.1.2"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "plugins/node/instrumentation-typeorm/node_modules/p-map": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz",
+ "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "aggregate-error": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "plugins/node/instrumentation-typeorm/node_modules/reflect-metadata": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz",
+ "integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "peer": true
+ },
+ "plugins/node/instrumentation-typeorm/node_modules/signal-exit": {
+ "version": "3.0.7",
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
+ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "plugins/node/instrumentation-typeorm/node_modules/typeorm": {
+ "version": "0.3.21",
+ "resolved": "https://registry.npmjs.org/typeorm/-/typeorm-0.3.21.tgz",
+ "integrity": "sha512-lh4rUWl1liZGjyPTWpwcK8RNI5x4ekln+/JJOox1wCd7xbucYDOXWD+1cSzTN3L0wbTGxxOtloM5JlxbOxEufA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@sqltools/formatter": "^1.2.5",
+ "ansis": "^3.9.0",
+ "app-root-path": "^3.1.0",
+ "buffer": "^6.0.3",
+ "dayjs": "^1.11.9",
+ "debug": "^4.3.4",
+ "dotenv": "^16.0.3",
+ "glob": "^10.4.5",
+ "sha.js": "^2.4.11",
+ "sql-highlight": "^6.0.0",
+ "tslib": "^2.5.0",
+ "uuid": "^11.0.5",
+ "yargs": "^17.6.2"
+ },
+ "bin": {
+ "typeorm": "cli.js",
+ "typeorm-ts-node-commonjs": "cli-ts-node-commonjs.js",
+ "typeorm-ts-node-esm": "cli-ts-node-esm.js"
+ },
+ "engines": {
+ "node": ">=16.13.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/typeorm"
+ },
+ "peerDependencies": {
+ "@google-cloud/spanner": "^5.18.0",
+ "@sap/hana-client": "^2.12.25",
+ "better-sqlite3": "^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0",
+ "hdb-pool": "^0.1.6",
+ "ioredis": "^5.0.4",
+ "mongodb": "^5.8.0",
+ "mssql": "^9.1.1 || ^10.0.1 || ^11.0.1",
+ "mysql2": "^2.2.5 || ^3.0.1",
+ "oracledb": "^6.3.0",
+ "pg": "^8.5.1",
+ "pg-native": "^3.0.0",
+ "pg-query-stream": "^4.0.0",
+ "redis": "^3.1.1 || ^4.0.0",
+ "reflect-metadata": "^0.1.14 || ^0.2.0",
+ "sql.js": "^1.4.0",
+ "sqlite3": "^5.0.3",
+ "ts-node": "^10.7.0",
+ "typeorm-aurora-data-api-driver": "^2.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@google-cloud/spanner": {
+ "optional": true
+ },
+ "@sap/hana-client": {
+ "optional": true
+ },
+ "better-sqlite3": {
+ "optional": true
+ },
+ "hdb-pool": {
+ "optional": true
+ },
+ "ioredis": {
+ "optional": true
+ },
+ "mongodb": {
+ "optional": true
+ },
+ "mssql": {
+ "optional": true
+ },
+ "mysql2": {
+ "optional": true
+ },
+ "oracledb": {
+ "optional": true
+ },
+ "pg": {
+ "optional": true
+ },
+ "pg-native": {
+ "optional": true
+ },
+ "pg-query-stream": {
+ "optional": true
+ },
+ "redis": {
+ "optional": true
+ },
+ "sql.js": {
+ "optional": true
+ },
+ "sqlite3": {
+ "optional": true
+ },
+ "ts-node": {
+ "optional": true
+ },
+ "typeorm-aurora-data-api-driver": {
+ "optional": true
+ }
+ }
+ },
+ "plugins/node/instrumentation-typeorm/node_modules/typescript": {
+ "version": "5.0.4",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.0.4.tgz",
+ "integrity": "sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=12.20"
+ }
+ },
+ "plugins/node/instrumentation-typeorm/node_modules/uuid": {
+ "version": "11.1.0",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz",
+ "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==",
+ "dev": true,
+ "funding": [
+ "https://github.com/sponsors/broofa",
+ "https://github.com/sponsors/ctavan"
+ ],
+ "license": "MIT",
+ "bin": {
+ "uuid": "dist/esm/bin/uuid"
+ }
+ },
+ "plugins/node/instrumentation-typeorm/node_modules/wrap-ansi": {
+ "version": "6.2.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
+ "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "plugins/node/instrumentation-typeorm/node_modules/y18n": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz",
+ "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "plugins/node/instrumentation-typeorm/node_modules/yargs-parser": {
+ "version": "18.1.3",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz",
+ "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "camelcase": "^5.0.0",
+ "decamelize": "^1.2.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
"plugins/node/instrumentation-undici": {
"name": "@opentelemetry/instrumentation-undici",
"version": "0.11.0",
@@ -44816,6 +45406,291 @@
}
}
},
+ "@opentelemetry/instrumentation-typeorm": {
+ "version": "file:plugins/node/instrumentation-typeorm",
+ "requires": {
+ "@opentelemetry/api": "^1.3.0",
+ "@opentelemetry/contrib-test-utils": "^0.46.0",
+ "@opentelemetry/core": "^2.0.0",
+ "@opentelemetry/instrumentation": "^0.200.0",
+ "@opentelemetry/sdk-trace-base": "^2.0.0",
+ "@types/mocha": "10.0.10",
+ "@types/node": "18.18.14",
+ "nyc": "15.1.0",
+ "rimraf": "5.0.10",
+ "sqlite3": "^5.0.2",
+ "typeorm": "^0.3.21",
+ "typescript": "5.0.4"
+ },
+ "dependencies": {
+ "@types/node": {
+ "version": "18.18.14",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-18.18.14.tgz",
+ "integrity": "sha512-iSOeNeXYNYNLLOMDSVPvIFojclvMZ/HDY2dU17kUlcsOsSQETbWIslJbYLZgA+ox8g2XQwSHKTkght1a5X26lQ==",
+ "dev": true,
+ "requires": {
+ "undici-types": "~5.26.4"
+ }
+ },
+ "brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dev": true,
+ "requires": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "bson": {
+ "version": "5.5.1",
+ "resolved": "https://registry.npmjs.org/bson/-/bson-5.5.1.tgz",
+ "integrity": "sha512-ix0EwukN2EpC0SRWIj/7B5+A6uQMQy6KMREI9qQqvgpkV2frH63T0UDVd1SYedL6dNCmDBYB3QtXi4ISk9YT+g==",
+ "dev": true,
+ "optional": true,
+ "peer": true
+ },
+ "buffer": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
+ "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==",
+ "dev": true,
+ "requires": {
+ "base64-js": "^1.3.1",
+ "ieee754": "^1.2.1"
+ }
+ },
+ "camelcase": {
+ "version": "5.3.1",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
+ "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
+ "dev": true
+ },
+ "cliui": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz",
+ "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==",
+ "dev": true,
+ "requires": {
+ "string-width": "^4.2.0",
+ "strip-ansi": "^6.0.0",
+ "wrap-ansi": "^6.2.0"
+ }
+ },
+ "convert-source-map": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz",
+ "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==",
+ "dev": true
+ },
+ "dotenv": {
+ "version": "16.4.7",
+ "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz",
+ "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==",
+ "dev": true
+ },
+ "foreground-child": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz",
+ "integrity": "sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==",
+ "dev": true,
+ "requires": {
+ "cross-spawn": "^7.0.0",
+ "signal-exit": "^3.0.2"
+ }
+ },
+ "ieee754": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
+ "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
+ "dev": true
+ },
+ "minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dev": true,
+ "requires": {
+ "brace-expansion": "^1.1.7"
+ }
+ },
+ "mongodb": {
+ "version": "5.9.2",
+ "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-5.9.2.tgz",
+ "integrity": "sha512-H60HecKO4Bc+7dhOv4sJlgvenK4fQNqqUIlXxZYQNbfEWSALGAwGoyJd/0Qwk4TttFXUOHJ2ZJQe/52ScaUwtQ==",
+ "dev": true,
+ "optional": true,
+ "peer": true,
+ "requires": {
+ "@mongodb-js/saslprep": "^1.1.0",
+ "bson": "^5.5.0",
+ "mongodb-connection-string-url": "^2.6.0",
+ "socks": "^2.7.1"
+ }
+ },
+ "nyc": {
+ "version": "15.1.0",
+ "resolved": "https://registry.npmjs.org/nyc/-/nyc-15.1.0.tgz",
+ "integrity": "sha512-jMW04n9SxKdKi1ZMGhvUTHBN0EICCRkHemEoE5jm6mTYcqcdas0ATzgUgejlQUHMvpnOZqGB5Xxsv9KxJW1j8A==",
+ "dev": true,
+ "requires": {
+ "@istanbuljs/load-nyc-config": "^1.0.0",
+ "@istanbuljs/schema": "^0.1.2",
+ "caching-transform": "^4.0.0",
+ "convert-source-map": "^1.7.0",
+ "decamelize": "^1.2.0",
+ "find-cache-dir": "^3.2.0",
+ "find-up": "^4.1.0",
+ "foreground-child": "^2.0.0",
+ "get-package-type": "^0.1.0",
+ "glob": "^7.1.6",
+ "istanbul-lib-coverage": "^3.0.0",
+ "istanbul-lib-hook": "^3.0.0",
+ "istanbul-lib-instrument": "^4.0.0",
+ "istanbul-lib-processinfo": "^2.0.2",
+ "istanbul-lib-report": "^3.0.0",
+ "istanbul-lib-source-maps": "^4.0.0",
+ "istanbul-reports": "^3.0.2",
+ "make-dir": "^3.0.0",
+ "node-preload": "^0.2.1",
+ "p-map": "^3.0.0",
+ "process-on-spawn": "^1.0.0",
+ "resolve-from": "^5.0.0",
+ "rimraf": "^3.0.0",
+ "signal-exit": "^3.0.2",
+ "spawn-wrap": "^2.0.0",
+ "test-exclude": "^6.0.0",
+ "yargs": "^15.0.2"
+ },
+ "dependencies": {
+ "glob": {
+ "version": "7.2.3",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
+ "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+ "dev": true,
+ "requires": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.1.1",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ }
+ },
+ "rimraf": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
+ "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
+ "dev": true,
+ "requires": {
+ "glob": "^7.1.3"
+ }
+ },
+ "yargs": {
+ "version": "15.4.1",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz",
+ "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==",
+ "dev": true,
+ "requires": {
+ "cliui": "^6.0.0",
+ "decamelize": "^1.2.0",
+ "find-up": "^4.1.0",
+ "get-caller-file": "^2.0.1",
+ "require-directory": "^2.1.1",
+ "require-main-filename": "^2.0.0",
+ "set-blocking": "^2.0.0",
+ "string-width": "^4.2.0",
+ "which-module": "^2.0.0",
+ "y18n": "^4.0.0",
+ "yargs-parser": "^18.1.2"
+ }
+ }
+ }
+ },
+ "p-map": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz",
+ "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==",
+ "dev": true,
+ "requires": {
+ "aggregate-error": "^3.0.0"
+ }
+ },
+ "reflect-metadata": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz",
+ "integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==",
+ "dev": true,
+ "peer": true
+ },
+ "signal-exit": {
+ "version": "3.0.7",
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
+ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
+ "dev": true
+ },
+ "typeorm": {
+ "version": "0.3.21",
+ "resolved": "https://registry.npmjs.org/typeorm/-/typeorm-0.3.21.tgz",
+ "integrity": "sha512-lh4rUWl1liZGjyPTWpwcK8RNI5x4ekln+/JJOox1wCd7xbucYDOXWD+1cSzTN3L0wbTGxxOtloM5JlxbOxEufA==",
+ "dev": true,
+ "requires": {
+ "@sqltools/formatter": "^1.2.5",
+ "ansis": "^3.9.0",
+ "app-root-path": "^3.1.0",
+ "buffer": "^6.0.3",
+ "dayjs": "^1.11.9",
+ "debug": "^4.3.4",
+ "dotenv": "^16.0.3",
+ "glob": "^10.4.5",
+ "sha.js": "^2.4.11",
+ "sql-highlight": "^6.0.0",
+ "tslib": "^2.5.0",
+ "uuid": "^11.0.5",
+ "yargs": "^17.6.2"
+ }
+ },
+ "typescript": {
+ "version": "5.0.4",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.0.4.tgz",
+ "integrity": "sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==",
+ "dev": true
+ },
+ "uuid": {
+ "version": "11.1.0",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz",
+ "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==",
+ "dev": true
+ },
+ "wrap-ansi": {
+ "version": "6.2.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
+ "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ }
+ },
+ "y18n": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz",
+ "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==",
+ "dev": true
+ },
+ "yargs-parser": {
+ "version": "18.1.3",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz",
+ "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==",
+ "dev": true,
+ "requires": {
+ "camelcase": "^5.0.0",
+ "decamelize": "^1.2.0"
+ }
+ }
+ }
+ },
"@opentelemetry/instrumentation-undici": {
"version": "file:plugins/node/instrumentation-undici",
"requires": {
@@ -47050,6 +47925,12 @@
"integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==",
"dev": true
},
+ "@sqltools/formatter": {
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/@sqltools/formatter/-/formatter-1.2.5.tgz",
+ "integrity": "sha512-Uy0+khmZqUrUGm5dmMqVlnvufZRSK0FbYzVgp0UMstm+F5+W2/jnEEQyc9vo1ZR/E5ZI/B1WjjoTqBqwJL6Krw==",
+ "dev": true
+ },
"@teppeis/multimaps": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@teppeis/multimaps/-/multimaps-2.0.0.tgz",
@@ -48535,6 +49416,12 @@
"color-convert": "^2.0.1"
}
},
+ "ansis": {
+ "version": "3.17.0",
+ "resolved": "https://registry.npmjs.org/ansis/-/ansis-3.17.0.tgz",
+ "integrity": "sha512-0qWUglt9JEqLFr3w1I1pbrChn1grhaiAR2ocX1PP/flRmxgtwTzPFFFnfIlD6aMOLQZgSuCRlidD70lvx8yhzg==",
+ "dev": true
+ },
"any-promise": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz",
@@ -48559,6 +49446,12 @@
}
}
},
+ "app-root-path": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/app-root-path/-/app-root-path-3.1.0.tgz",
+ "integrity": "sha512-biN3PwB2gUtjaYy/isrU3aNWI5w+fAfvHkSvCKeQGxhmYpwKFUxudR3Yya+KqVRHBmEDYh+/lTozYCFbmzX4nA==",
+ "dev": true
+ },
"append-field": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz",
@@ -50508,6 +51401,12 @@
"integrity": "sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==",
"dev": true
},
+ "dayjs": {
+ "version": "1.11.13",
+ "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz",
+ "integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==",
+ "dev": true
+ },
"debounce": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/debounce/-/debounce-1.2.1.tgz",
@@ -61732,6 +62631,16 @@
"integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
"dev": true
},
+ "sha.js": {
+ "version": "2.4.11",
+ "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz",
+ "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==",
+ "dev": true,
+ "requires": {
+ "inherits": "^2.0.1",
+ "safe-buffer": "^5.0.1"
+ }
+ },
"shallow-clone": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz",
@@ -62434,6 +63343,12 @@
"integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==",
"dev": true
},
+ "sql-highlight": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/sql-highlight/-/sql-highlight-6.0.0.tgz",
+ "integrity": "sha512-+fLpbAbWkQ+d0JEchJT/NrRRXbYRNbG15gFpANx73EwxQB1PRjj+k/OI0GTU0J63g8ikGkJECQp9z8XEJZvPRw==",
+ "dev": true
+ },
"sqlite3": {
"version": "5.1.7",
"resolved": "https://registry.npmjs.org/sqlite3/-/sqlite3-5.1.7.tgz",
diff --git a/plugins/node/instrumentation-typeorm/.eslintignore b/plugins/node/instrumentation-typeorm/.eslintignore
new file mode 100644
index 0000000000..378eac25d3
--- /dev/null
+++ b/plugins/node/instrumentation-typeorm/.eslintignore
@@ -0,0 +1 @@
+build
diff --git a/plugins/node/instrumentation-typeorm/.eslintrc.js b/plugins/node/instrumentation-typeorm/.eslintrc.js
new file mode 100644
index 0000000000..f756f4488b
--- /dev/null
+++ b/plugins/node/instrumentation-typeorm/.eslintrc.js
@@ -0,0 +1,7 @@
+module.exports = {
+ "env": {
+ "mocha": true,
+ "node": true
+ },
+ ...require('../../../eslint.config.js')
+}
diff --git a/plugins/node/instrumentation-typeorm/.tav.yml b/plugins/node/instrumentation-typeorm/.tav.yml
new file mode 100644
index 0000000000..1df7d3bd2f
--- /dev/null
+++ b/plugins/node/instrumentation-typeorm/.tav.yml
@@ -0,0 +1,4 @@
+'typeorm':
+ versions: ">=0.3.0"
+ commands:
+ - npm run test
diff --git a/plugins/node/instrumentation-typeorm/LICENSE b/plugins/node/instrumentation-typeorm/LICENSE
new file mode 100644
index 0000000000..261eeb9e9f
--- /dev/null
+++ b/plugins/node/instrumentation-typeorm/LICENSE
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ 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
+
+ http://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.
diff --git a/plugins/node/instrumentation-typeorm/NOTICE b/plugins/node/instrumentation-typeorm/NOTICE
new file mode 100644
index 0000000000..61844b567e
--- /dev/null
+++ b/plugins/node/instrumentation-typeorm/NOTICE
@@ -0,0 +1,12 @@
+[Based on the instrumentation written by Aspecto](https://github.com/aspecto-io/opentelemetry-ext-js/tree/master/packages/instrumentation-typeorm).
+
+The library contains the following changes compared to the original:
+* Removed `moduleVersionAttributeName` configuration option.
+* Changed the function signature of `responseHook`.
+* Removed dependency on `is-promise` package.
+* Removed usage of SemanticAttributes in favor of explicit values.
+* Removed parsing of db operation from raw queries.
+* Changed db.database.parameters to db.typeorm.parameters.
+* Updated to OpenTelemetry semantic conventions 1.32.0.
+
+Refer to the changelog for any future changes.
diff --git a/plugins/node/instrumentation-typeorm/README.md b/plugins/node/instrumentation-typeorm/README.md
new file mode 100644
index 0000000000..5746935151
--- /dev/null
+++ b/plugins/node/instrumentation-typeorm/README.md
@@ -0,0 +1,80 @@
+# OpenTelemetry `typeorm` Instrumentation for Node.js
+
+[![NPM Published Version][npm-img]][npm-url]
+[![Apache License][license-image]][license-image]
+
+This module provides automatic instrumentation for the [`typeorm`](https://www.npmjs.com/package/typeorm) package, which may be loaded using the [`@opentelemetry/instrumentation`](https://github.com/open-telemetry/opentelemetry-js/tree/main/experimental/packages/opentelemetry-instrumentation) package.
+
+If total installation size is not constrained, it is recommended to use [@opentelemetry/sdk-node](`https://www.npmjs.com/package/@opentelemetry/sdk-node`) for the most seamless instrumentation experience.
+
+Compatible with OpenTelemetry JS API `^1.3.0` and SDK `2.0+`.
+
+## Installation
+
+```bash
+npm install --save @opentelemetry/instrumentation-typeorm
+```
+
+### Supported versions
+
+- `>=0.3.0 <1`
+
+## Usage
+
+```js
+const { NodeTracerProvider } = require('@opentelemetry/sdk-trace-node');
+const { TypeormInstrumentation } = require('@opentelemetry/instrumentation-typeorm');
+const { registerInstrumentations } = require('@opentelemetry/instrumentation');
+
+const provider = new NodeTracerProvider();
+provider.register();
+
+registerInstrumentations({
+ instrumentations: [
+ new TypeormInstrumentation({
+ // see below for available configuration
+ }),
+ ],
+});
+```
+
+### Instrumentation Options
+
+You can set the following:
+
+| Options | Type | Description |
+| ---------------------------- | -------------------------------------- | ------------------------------------------------------------------------------|
+| `responseHook` | `TypeormResponseCustomAttributesFunction` | Hook called before response is returned, which allows to add custom attributes to span. |
+| `suppressInternalInstrumentation` | boolean | Typeorm uses mongodb/postgres/mysql/mariadb/etc. under the hood. If, for example, postgres instrumentation is enabled, a postgres operation will also create a postgres span describing the communication. Setting the `suppressInternalInstrumentation` config value to `true` will cause the instrumentation to suppress instrumentation of underlying operations. |
+| `enableInternalInstrumentation` | boolean | Some methods such as `getManyAndCount` can generate internally multiple spans. To instrument those set this to `true` |
+| `enhancedDatabaseReporting` | boolean | set to `true` if you want to capture the parameter values for parameterized SQL queries (**may leak sensitive information**) |
+
+## Semantic Conventions
+
+Attributes collected:
+
+| Attribute | Short Description |
+| ---------------------| --------------------------------------------------------------------------- |
+| `db.namespace` | The name of the database being accessed. |
+| `db.operation.name` | The name of the operation being executed (e.g. the SQL keyword). |
+| `db.collection.name` | The name of the table being accessed. |
+| `db.query.text` | The database statement being executed. |
+| `db.system.name` | An identifier for the database management system (DBMS) product being used. |
+| `server.address` | Remote address of the database. |
+| `server.port` | Peer port number of the network connection. |
+
+## Useful links
+
+- For more information on OpenTelemetry, visit:
+- For more about OpenTelemetry JavaScript:
+- For help or feedback on this project, join us in [GitHub Discussions][discussions-url]
+
+## License
+
+Apache 2.0 - See [LICENSE][license-url] for more information.
+
+[discussions-url]: https://github.com/open-telemetry/opentelemetry-js/discussions
+[license-url]: https://github.com/open-telemetry/opentelemetry-js-contrib/blob/main/LICENSE
+[license-image]: https://img.shields.io/badge/license-Apache_2.0-green.svg?style=flat
+[npm-url]: https://www.npmjs.com/package/@opentelemetry/instrumentation-typeorm
+[npm-img]: https://badge.fury.io/js/%40opentelemetry%2Finstrumentation-typeorm.svg
diff --git a/plugins/node/instrumentation-typeorm/package.json b/plugins/node/instrumentation-typeorm/package.json
new file mode 100644
index 0000000000..393ea7b784
--- /dev/null
+++ b/plugins/node/instrumentation-typeorm/package.json
@@ -0,0 +1,63 @@
+{
+ "name": "@opentelemetry/instrumentation-typeorm",
+ "version": "0.0.1",
+ "description": "OpenTelemetry instrumentation for `typeorm` database data-mapper ORM",
+ "main": "build/src/index.js",
+ "types": "build/src/index.d.ts",
+ "repository": "open-telemetry/opentelemetry-js-contrib",
+ "scripts": {
+ "clean": "rimraf build/*",
+ "compile": "tsc -p .",
+ "lint": "eslint . --ext .ts",
+ "lint:fix": "eslint . --ext .ts --fix",
+ "lint:readme": "node ../../../scripts/lint-readme.js",
+ "prewatch": "npm run precompile",
+ "prepublishOnly": "npm run compile",
+ "tdd": "npm run test -- --watch-extensions ts --watch",
+ "test": "nyc mocha --require '@opentelemetry/contrib-test-utils' 'test/**/*.test.ts'",
+ "test-all-versions": "tav",
+ "test:debug": "mocha --inspect-brk --no-timeouts 'test/**/*.test.ts'",
+ "version:update": "node ../../../scripts/version-update.js",
+ "watch": "tsc -w"
+ },
+ "keywords": [
+ "typeorm",
+ "instrumentation",
+ "nodejs",
+ "opentelemetry",
+ "tracing"
+ ],
+ "author": "OpenTelemetry Authors",
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^18.19.0 || >=20.6.0"
+ },
+ "files": [
+ "build/src/**/*.js",
+ "build/src/**/*.js.map",
+ "build/src/**/*.d.ts"
+ ],
+ "publishConfig": {
+ "access": "public"
+ },
+ "peerDependencies": {
+ "@opentelemetry/api": "^1.3.0"
+ },
+ "devDependencies": {
+ "@opentelemetry/api": "^1.3.0",
+ "@opentelemetry/contrib-test-utils": "^0.46.0",
+ "@opentelemetry/sdk-trace-base": "^2.0.0",
+ "@types/mocha": "10.0.10",
+ "@types/node": "18.18.14",
+ "typeorm": "^0.3.21",
+ "nyc": "15.1.0",
+ "rimraf": "5.0.10",
+ "sqlite3": "^5.0.2",
+ "typescript": "5.0.4"
+ },
+ "dependencies": {
+ "@opentelemetry/instrumentation": "^0.200.0",
+ "@opentelemetry/core": "^2.0.0"
+ },
+ "homepage": "https://github.com/open-telemetry/opentelemetry-js-contrib/tree/main/plugins/node/instrumentation-typeorm#readme"
+}
diff --git a/plugins/node/instrumentation-typeorm/src/index.ts b/plugins/node/instrumentation-typeorm/src/index.ts
new file mode 100644
index 0000000000..a0cf02263c
--- /dev/null
+++ b/plugins/node/instrumentation-typeorm/src/index.ts
@@ -0,0 +1,17 @@
+/*
+ * Copyright The OpenTelemetry Authors, Aspecto
+ *
+ * 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.
+ */
+export * from './typeorm';
+export * from './types';
diff --git a/plugins/node/instrumentation-typeorm/src/semconv.ts b/plugins/node/instrumentation-typeorm/src/semconv.ts
new file mode 100644
index 0000000000..df934ac462
--- /dev/null
+++ b/plugins/node/instrumentation-typeorm/src/semconv.ts
@@ -0,0 +1,23 @@
+/*
+ * Copyright The OpenTelemetry Authors, Aspecto
+ *
+ * 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.
+ */
+
+export const ATTR_DB_NAMESPACE = 'db.namespace' as const;
+export const ATTR_DB_COLLECTION_NAME = 'db.collection.name' as const;
+export const ATTR_DB_OPERATION_NAME = 'db.operation.name' as const;
+export const ATTR_DB_QUERY_TEXT = 'db.query.text' as const;
+export const ATTR_DB_SYSTEM_NAME = 'db.system.name' as const;
+export const ATTR_SERVER_ADDRESS = 'server.address' as const;
+export const ATTR_SERVER_PORT = 'server.port' as const;
diff --git a/plugins/node/instrumentation-typeorm/src/typeorm.ts b/plugins/node/instrumentation-typeorm/src/typeorm.ts
new file mode 100644
index 0000000000..b655551eac
--- /dev/null
+++ b/plugins/node/instrumentation-typeorm/src/typeorm.ts
@@ -0,0 +1,439 @@
+/*
+ * Copyright The OpenTelemetry Authors, Aspecto
+ *
+ * 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 {
+ Span,
+ SpanKind,
+ SpanStatusCode,
+ trace,
+ context,
+} from '@opentelemetry/api';
+import { suppressTracing } from '@opentelemetry/core';
+import {
+ ATTR_DB_COLLECTION_NAME,
+ ATTR_DB_NAMESPACE,
+ ATTR_DB_OPERATION_NAME,
+ ATTR_DB_QUERY_TEXT,
+ ATTR_DB_SYSTEM_NAME,
+ ATTR_SERVER_ADDRESS,
+ ATTR_SERVER_PORT,
+} from './semconv';
+import {
+ ExtendedDatabaseAttribute,
+ TypeormInstrumentationConfig,
+} from './types';
+import {
+ getParamNames,
+ isTypeormInternalTracingSuppressed,
+ suppressTypeormInternalTracing,
+} from './utils';
+/** @knipignore */
+import { PACKAGE_NAME, PACKAGE_VERSION } from './version';
+import type * as typeorm from 'typeorm';
+import {
+ InstrumentationBase,
+ InstrumentationNodeModuleDefinition,
+ InstrumentationNodeModuleFile,
+ isWrapped,
+ safeExecuteInTheMiddle,
+} from '@opentelemetry/instrumentation';
+import * as util from 'util';
+
+// eslint-disable-next-line @typescript-eslint/no-explicit-any
+type SelectQueryBuilderMethod = keyof typeorm.SelectQueryBuilder;
+const selectQueryBuilderExecuteMethods: SelectQueryBuilderMethod[] = [
+ 'getRawOne',
+ 'getCount',
+ 'getManyAndCount',
+ 'stream',
+ 'getMany',
+ 'getOneOrFail',
+ 'getOne',
+ 'getRawAndEntities',
+ 'getRawMany',
+];
+const rawQueryFuncName = 'query';
+type EntityManagerMethods = keyof typeorm.EntityManager;
+const functionsUsingEntityPersistExecutor: EntityManagerMethods[] = [
+ 'save',
+ 'remove',
+ 'softRemove',
+ 'recover',
+];
+const functionsUsingQueryBuilder: EntityManagerMethods[] = [
+ 'insert',
+ 'update',
+ 'delete',
+ 'softDelete',
+ 'restore',
+ 'count',
+ 'find',
+ 'findAndCount',
+ 'findByIds',
+ 'findOne',
+ 'increment',
+ 'decrement',
+];
+const entityManagerMethods: EntityManagerMethods[] = [
+ ...functionsUsingEntityPersistExecutor,
+ ...functionsUsingQueryBuilder,
+];
+
+export class TypeormInstrumentation extends InstrumentationBase {
+ constructor(config: TypeormInstrumentationConfig = {}) {
+ super(PACKAGE_NAME, PACKAGE_VERSION, config);
+ }
+
+ protected init() {
+ const selectQueryBuilder = new InstrumentationNodeModuleFile(
+ 'typeorm/query-builder/SelectQueryBuilder.js',
+ ['>=0.3.0 <1'],
+ moduleExports => {
+ selectQueryBuilderExecuteMethods.map(method => {
+ if (isWrapped(moduleExports.SelectQueryBuilder.prototype?.[method])) {
+ this._unwrap(moduleExports.SelectQueryBuilder.prototype, method);
+ }
+ this._wrap(
+ moduleExports.SelectQueryBuilder.prototype,
+ method,
+ this._patchQueryBuilder()
+ );
+ });
+
+ return moduleExports;
+ },
+ moduleExports => {
+ selectQueryBuilderExecuteMethods.map(method => {
+ if (isWrapped(moduleExports.SelectQueryBuilder.prototype?.[method])) {
+ this._unwrap(moduleExports.SelectQueryBuilder.prototype, method);
+ }
+ });
+ return moduleExports;
+ }
+ );
+
+ const dataSource = new InstrumentationNodeModuleFile(
+ 'typeorm/data-source/DataSource.js',
+ ['>=0.3.0 <1'],
+ moduleExports => {
+ if (isWrapped(moduleExports.DataSource.prototype?.[rawQueryFuncName])) {
+ this._unwrap(moduleExports.DataSource.prototype, rawQueryFuncName);
+ }
+ this._wrap(
+ moduleExports.DataSource.prototype,
+ rawQueryFuncName,
+ this._patchRawQuery()
+ );
+
+ return moduleExports;
+ },
+ moduleExports => {
+ if (isWrapped(moduleExports.DataSource.prototype?.[rawQueryFuncName])) {
+ this._unwrap(moduleExports.DataSource.prototype, rawQueryFuncName);
+ }
+ return moduleExports;
+ }
+ );
+
+ const entityManager = new InstrumentationNodeModuleFile(
+ 'typeorm/entity-manager/EntityManager.js',
+ ['>=0.3.0 <1'],
+ moduleExports => {
+ entityManagerMethods.map(method => {
+ if (isWrapped(moduleExports.EntityManager.prototype?.[method])) {
+ this._unwrap(moduleExports.EntityManager.prototype, method);
+ }
+ this._wrap(
+ moduleExports.EntityManager.prototype,
+ method,
+ this._patchEntityManagerFunction(method)
+ );
+ });
+
+ return moduleExports;
+ },
+ moduleExports => {
+ entityManagerMethods.map(method => {
+ if (isWrapped(moduleExports.EntityManager.prototype?.[method])) {
+ this._unwrap(moduleExports.EntityManager.prototype, method);
+ }
+ });
+ return moduleExports;
+ }
+ );
+
+ const module = new InstrumentationNodeModuleDefinition(
+ 'typeorm',
+ ['>=0.3.0 <1'],
+ undefined,
+ undefined,
+ [selectQueryBuilder, entityManager, dataSource]
+ );
+ return module;
+ }
+
+ private _patchEntityManagerFunction(opName: string) {
+ const self = this;
+ return (original: Function) => {
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ return function (this: any, ...args: unknown[]) {
+ if (isTypeormInternalTracingSuppressed(context.active())) {
+ return original.apply(this, args);
+ }
+ const connectionOptions = this?.connection?.options ?? {};
+ const attributes: Record = {
+ [ATTR_DB_SYSTEM_NAME]: connectionOptions.type,
+ [ATTR_SERVER_ADDRESS]: connectionOptions.host,
+ [ATTR_SERVER_PORT]: connectionOptions.port,
+ [ATTR_DB_NAMESPACE]: connectionOptions.database,
+ [ATTR_DB_OPERATION_NAME]: opName,
+ [ATTR_DB_QUERY_TEXT]: JSON.stringify(buildStatement(original, args)),
+ };
+
+ //ignore EntityMetadataNotFoundError
+ try {
+ if (this.metadata) {
+ attributes[ATTR_DB_COLLECTION_NAME] = this.metadata.tableName;
+ } else {
+ const entity = args[0];
+ const name =
+ typeof entity === 'object' ? entity?.constructor?.name : entity;
+ const metadata = this.connection.getMetadata(name);
+ if (metadata?.tableName) {
+ attributes[ATTR_DB_COLLECTION_NAME] = metadata.tableName;
+ }
+ }
+ } catch {
+ /* */
+ }
+
+ Object.entries(attributes).forEach(([key, value]) => {
+ if (value === undefined) delete attributes[key];
+ });
+
+ const span: Span = self.tracer.startSpan(
+ buildSpanName(opName, attributes[ATTR_DB_COLLECTION_NAME]),
+ {
+ kind: SpanKind.CLIENT,
+ attributes,
+ }
+ );
+
+ const contextWithSpan = trace.setSpan(context.active(), span);
+
+ const traceContext = self.getConfig().enableInternalInstrumentation
+ ? contextWithSpan
+ : suppressTypeormInternalTracing(contextWithSpan);
+
+ const contextWithSuppressTracing = self.getConfig()
+ .suppressInternalInstrumentation
+ ? suppressTracing(traceContext)
+ : traceContext;
+
+ return context.with(contextWithSuppressTracing, () =>
+ self._endSpan(() => original.apply(this, args), span)
+ );
+ };
+ };
+ }
+
+ private _patchQueryBuilder() {
+ const self = this;
+ return (original: Function) => {
+ return function (
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ this: typeorm.SelectQueryBuilder,
+ ...args: unknown[]
+ ) {
+ if (isTypeormInternalTracingSuppressed(context.active())) {
+ return original.apply(this, args);
+ }
+ const sql = this.getQuery();
+ const parameters = this.getParameters();
+ const mainTableName = this.getMainTableName();
+ const operation = this.expressionMap.queryType;
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ const connectionOptions: any = this.connection?.options;
+ const attributes: Record = {
+ [ATTR_DB_SYSTEM_NAME]: connectionOptions.type,
+ [ATTR_SERVER_ADDRESS]: connectionOptions.host,
+ [ATTR_SERVER_PORT]: connectionOptions.port,
+ [ATTR_DB_NAMESPACE]: connectionOptions.database,
+ [ATTR_DB_OPERATION_NAME]: operation,
+ [ATTR_DB_QUERY_TEXT]: sql,
+ [ATTR_DB_COLLECTION_NAME]: mainTableName,
+ };
+ if (self.getConfig().enhancedDatabaseReporting) {
+ try {
+ attributes[ExtendedDatabaseAttribute.DB_STATEMENT_PARAMETERS] =
+ JSON.stringify(parameters);
+ } catch (err) {
+ /* */
+ }
+ }
+ const span: Span = self.tracer.startSpan(
+ buildSpanName(operation, attributes[ATTR_DB_COLLECTION_NAME]),
+ {
+ kind: SpanKind.CLIENT,
+ attributes,
+ }
+ );
+
+ const contextWithSpan = trace.setSpan(context.active(), span);
+
+ const traceContext = self.getConfig().enableInternalInstrumentation
+ ? contextWithSpan
+ : suppressTypeormInternalTracing(contextWithSpan);
+
+ const contextWithSuppressTracing = self.getConfig()
+ ?.suppressInternalInstrumentation
+ ? suppressTracing(traceContext)
+ : traceContext;
+
+ return context.with(contextWithSuppressTracing, () =>
+ self._endSpan(() => original.apply(this, args), span)
+ );
+ };
+ };
+ }
+
+ private _patchRawQuery() {
+ const self = this;
+ return (original: Function) => {
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ return function (this: any, ...args: unknown[]) {
+ if (isTypeormInternalTracingSuppressed(context.active())) {
+ return original.apply(this, args);
+ }
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ const sql = args[0] as any;
+ const operation = 'raw query';
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ const connectionOptions: any = this.options;
+ const attributes = {
+ [ATTR_DB_SYSTEM_NAME]: connectionOptions.type,
+ [ATTR_SERVER_ADDRESS]: connectionOptions.host,
+ [ATTR_SERVER_PORT]: connectionOptions.port,
+ [ATTR_DB_NAMESPACE]: connectionOptions.database,
+ [ATTR_DB_OPERATION_NAME]: operation,
+ [ATTR_DB_QUERY_TEXT]: sql,
+ };
+
+ const span: Span = self.tracer.startSpan(operation, {
+ kind: SpanKind.CLIENT,
+ attributes,
+ });
+
+ const contextWithSpan = trace.setSpan(context.active(), span);
+
+ const traceContext = self.getConfig().enableInternalInstrumentation
+ ? contextWithSpan
+ : suppressTypeormInternalTracing(contextWithSpan);
+
+ const contextWithSuppressTracing = self.getConfig()
+ ?.suppressInternalInstrumentation
+ ? suppressTracing(traceContext)
+ : traceContext;
+
+ return context.with(contextWithSuppressTracing, () =>
+ self._endSpan(() => original.apply(this, args), span)
+ );
+ };
+ };
+ }
+
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ private _endSpan(traced: any, span: Span) {
+ const executeResponseHook = (response: unknown) => {
+ const hook = this.getConfig().responseHook;
+ if (hook !== undefined) {
+ safeExecuteInTheMiddle(
+ () => hook(span, { response }),
+ e => {
+ if (e) this._diag.error('responseHook error', e);
+ },
+ true
+ );
+ }
+ return response;
+ };
+ try {
+ const response = traced();
+ if (util.types.isPromise(response)) {
+ return Promise.resolve(response)
+ .then(response => executeResponseHook(response))
+ .catch(err => {
+ if (err) {
+ if (typeof err === 'string') {
+ span.setStatus({ code: SpanStatusCode.ERROR, message: err });
+ } else {
+ span.recordException(err);
+ span.setStatus({
+ code: SpanStatusCode.ERROR,
+ message: err?.message,
+ });
+ }
+ }
+ throw err;
+ })
+ .finally(() => span.end());
+ } else {
+ span.end();
+ return executeResponseHook(response);
+ }
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ } catch (error: any) {
+ span.recordException(error);
+ span.setStatus({ code: SpanStatusCode.ERROR, message: error?.message });
+ span.end();
+ throw error;
+ }
+ }
+}
+
+const buildStatement = (func: Function, args: any[]) => {
+ const paramNames = getParamNames(func) || [];
+ const statement: Record = {};
+ paramNames.forEach((pName, i) => {
+ const value = args[i];
+ if (!value) return;
+
+ try {
+ const stringified = JSON.stringify(value);
+ if (stringified) {
+ statement[pName] = args[i];
+ return;
+ }
+ } catch (_err) {
+ /* */
+ }
+ if (value?.name) {
+ statement[pName] = value.name;
+ return;
+ }
+ if (value?.constructor?.name) {
+ statement[pName] = value.constructor.name;
+ }
+ });
+ return statement;
+};
+
+function buildSpanName(operation: string, target: string | undefined): string {
+ if (target !== undefined) {
+ return `${operation} ${target}`;
+ }
+
+ return operation;
+}
diff --git a/plugins/node/instrumentation-typeorm/src/types.ts b/plugins/node/instrumentation-typeorm/src/types.ts
new file mode 100644
index 0000000000..88eb5e8c3d
--- /dev/null
+++ b/plugins/node/instrumentation-typeorm/src/types.ts
@@ -0,0 +1,49 @@
+/*
+ * Copyright The OpenTelemetry Authors, Aspecto
+ *
+ * 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 type { Span } from '@opentelemetry/api';
+import { InstrumentationConfig } from '@opentelemetry/instrumentation';
+
+export enum ExtendedDatabaseAttribute {
+ DB_STATEMENT_PARAMETERS = 'db.typeorm.parameters',
+}
+
+export interface HookInfo {
+ response: any;
+}
+
+export type TypeormResponseCustomAttributesFunction = (
+ span: Span,
+ info: HookInfo
+) => void;
+
+export interface TypeormInstrumentationConfig extends InstrumentationConfig {
+ /** hook for adding custom attributes using the response payload */
+ responseHook?: TypeormResponseCustomAttributesFunction;
+ /**
+ * Typeorm operation use mongodb/postgres/mysql/mariadb/etc. under the hood.
+ * If, for example, postgres instrumentation is enabled, a postgres operation will also create
+ * a postgres span describing the communication.
+ * Setting the `suppressInternalInstrumentation` config value to `true` will
+ * cause the instrumentation to suppress instrumentation of underlying operations.
+ */
+ suppressInternalInstrumentation?: boolean;
+ /** Some methods such as `getManyAndCount` can generate internally multiple spans.
+ * To instrument those set this to `true`
+ */
+ enableInternalInstrumentation?: boolean;
+ /** set to `true` if you want to capture the parameter values for parameterized SQL queries (**may leak sensitive information**) */
+ enhancedDatabaseReporting?: boolean;
+}
diff --git a/plugins/node/instrumentation-typeorm/src/utils/get-func-param-names.ts b/plugins/node/instrumentation-typeorm/src/utils/get-func-param-names.ts
new file mode 100644
index 0000000000..61a9cfa33c
--- /dev/null
+++ b/plugins/node/instrumentation-typeorm/src/utils/get-func-param-names.ts
@@ -0,0 +1,24 @@
+/*
+ * Copyright The OpenTelemetry Authors, Aspecto
+ *
+ * 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.
+ */
+const STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/gm;
+const ARGUMENT_NAMES = /([^\s,]+)/g;
+
+export function getParamNames(func: Function) {
+ const fnStr = func.toString().replace(STRIP_COMMENTS, '');
+ return fnStr
+ .slice(fnStr.indexOf('(') + 1, fnStr.indexOf(')'))
+ .match(ARGUMENT_NAMES);
+}
diff --git a/plugins/node/instrumentation-typeorm/src/utils/index.ts b/plugins/node/instrumentation-typeorm/src/utils/index.ts
new file mode 100644
index 0000000000..1667497fa2
--- /dev/null
+++ b/plugins/node/instrumentation-typeorm/src/utils/index.ts
@@ -0,0 +1,17 @@
+/*
+ * Copyright The OpenTelemetry Authors, Aspecto
+ *
+ * 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.
+ */
+export * from './get-func-param-names';
+export * from './suppressTracing';
diff --git a/plugins/node/instrumentation-typeorm/src/utils/suppressTracing.ts b/plugins/node/instrumentation-typeorm/src/utils/suppressTracing.ts
new file mode 100644
index 0000000000..eb871c1991
--- /dev/null
+++ b/plugins/node/instrumentation-typeorm/src/utils/suppressTracing.ts
@@ -0,0 +1,26 @@
+/*
+ * Copyright The OpenTelemetry Authors, Aspecto
+ *
+ * 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 { createContextKey, Context } from '@opentelemetry/api';
+
+const SUPPRESS_TYPEORM_INTERNAL_TRACING_KEY = createContextKey(
+ 'instrumentation-typeorm Context Key SUPPRESS_TYPEORM_INTERNAL_TRACING'
+);
+
+export const suppressTypeormInternalTracing = (context: Context) =>
+ context.setValue(SUPPRESS_TYPEORM_INTERNAL_TRACING_KEY, true);
+
+export const isTypeormInternalTracingSuppressed = (context: Context) =>
+ context.getValue(SUPPRESS_TYPEORM_INTERNAL_TRACING_KEY) === true;
diff --git a/plugins/node/instrumentation-typeorm/test/Connection.test.ts b/plugins/node/instrumentation-typeorm/test/Connection.test.ts
new file mode 100644
index 0000000000..8999c67576
--- /dev/null
+++ b/plugins/node/instrumentation-typeorm/test/Connection.test.ts
@@ -0,0 +1,68 @@
+/*
+ * Copyright The OpenTelemetry Authors, Aspecto
+ *
+ * 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 assert from 'assert';
+import { SpanStatusCode } from '@opentelemetry/api';
+import {
+ ATTR_DB_SYSTEM_NAME,
+ ATTR_DB_NAMESPACE,
+ ATTR_DB_OPERATION_NAME,
+ ATTR_DB_QUERY_TEXT,
+} from '../src/semconv';
+import { TypeormInstrumentation } from '../src';
+import {
+ getTestSpans,
+ registerInstrumentationTesting,
+} from '@opentelemetry/contrib-test-utils';
+
+const instrumentation = registerInstrumentationTesting(
+ new TypeormInstrumentation()
+);
+
+import * as typeorm from 'typeorm';
+import { defaultOptions } from './utils';
+
+describe('Connection', () => {
+ after(() => {
+ instrumentation.enable();
+ });
+ beforeEach(() => {
+ instrumentation.enable();
+ });
+ afterEach(() => {
+ instrumentation.disable();
+ });
+
+ describe('single connection', () => {
+ it('raw query', async () => {
+ const options = { ...defaultOptions, name: 'rawQuery' };
+ const ds = new typeorm.DataSource(options);
+ await ds.initialize();
+ const query = 'select * from user';
+ await ds.query(query);
+ const typeOrmSpans = getTestSpans();
+
+ assert.strictEqual(typeOrmSpans.length, 1);
+ assert.strictEqual(typeOrmSpans[0].name, 'raw query');
+ assert.strictEqual(typeOrmSpans[0].status.code, SpanStatusCode.UNSET);
+ const attributes = typeOrmSpans[0].attributes;
+ assert.strictEqual(attributes[ATTR_DB_SYSTEM_NAME], options.type);
+ assert.strictEqual(attributes[ATTR_DB_NAMESPACE], options.database);
+ assert.strictEqual(attributes[ATTR_DB_OPERATION_NAME], 'raw query');
+ assert.strictEqual(attributes[ATTR_DB_QUERY_TEXT], query);
+ await ds.destroy();
+ });
+ });
+});
diff --git a/plugins/node/instrumentation-typeorm/test/EntityManager.test.ts b/plugins/node/instrumentation-typeorm/test/EntityManager.test.ts
new file mode 100644
index 0000000000..605ce93594
--- /dev/null
+++ b/plugins/node/instrumentation-typeorm/test/EntityManager.test.ts
@@ -0,0 +1,241 @@
+/*
+ * Copyright The OpenTelemetry Authors, Aspecto
+ *
+ * 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 assert from 'assert';
+import { SpanStatusCode } from '@opentelemetry/api';
+import {
+ ATTR_DB_COLLECTION_NAME,
+ ATTR_DB_SYSTEM_NAME,
+ ATTR_DB_NAMESPACE,
+ ATTR_DB_OPERATION_NAME,
+ ATTR_DB_QUERY_TEXT,
+} from '../src/semconv';
+import { TypeormInstrumentation } from '../src';
+import {
+ getTestSpans,
+ registerInstrumentationTesting,
+} from '@opentelemetry/contrib-test-utils';
+
+const instrumentation = registerInstrumentationTesting(
+ new TypeormInstrumentation()
+);
+import * as typeorm from 'typeorm';
+import { defaultOptions, User } from './utils';
+
+describe('EntityManager', () => {
+ after(() => {
+ instrumentation.enable();
+ });
+ beforeEach(() => {
+ instrumentation.enable();
+ });
+ afterEach(() => {
+ instrumentation.disable();
+ });
+
+ describe('single connection', () => {
+ it('save using connection.manager', async () => {
+ const options = defaultOptions;
+ const ds = new typeorm.DataSource(options);
+ await ds.initialize();
+
+ const user = new User(1, 'opentelemetry', 'io');
+ await ds.manager.save(user);
+ const typeOrmSpans = getTestSpans();
+
+ assert.strictEqual(typeOrmSpans.length, 1);
+ assert.strictEqual(typeOrmSpans[0].name, 'save user');
+ assert.strictEqual(typeOrmSpans[0].status.code, SpanStatusCode.UNSET);
+ const attributes = typeOrmSpans[0].attributes;
+ assert.strictEqual(attributes[ATTR_DB_COLLECTION_NAME], 'user');
+ assert.strictEqual(attributes[ATTR_DB_SYSTEM_NAME], options.type);
+ assert.strictEqual(attributes[ATTR_DB_NAMESPACE], options.database);
+ assert.strictEqual(attributes[ATTR_DB_OPERATION_NAME], 'save');
+ assert.strictEqual(
+ attributes[ATTR_DB_QUERY_TEXT],
+ JSON.stringify({ targetOrEntity: user })
+ );
+ await ds.destroy();
+ });
+
+ it('save', async () => {
+ const options = defaultOptions;
+ const ds = new typeorm.DataSource(options);
+ await ds.initialize();
+ const manager = ds.manager;
+ const user = new User(1, 'opentelemetry', 'io');
+ await manager.save(user);
+ const typeOrmSpans = getTestSpans();
+
+ assert.strictEqual(typeOrmSpans.length, 1);
+ assert.strictEqual(typeOrmSpans[0].name, 'save user');
+ assert.strictEqual(typeOrmSpans[0].status.code, SpanStatusCode.UNSET);
+ const attributes = typeOrmSpans[0].attributes;
+ assert.strictEqual(attributes[ATTR_DB_COLLECTION_NAME], 'user');
+ assert.strictEqual(attributes[ATTR_DB_SYSTEM_NAME], options.type);
+ assert.strictEqual(attributes[ATTR_DB_NAMESPACE], options.database);
+ assert.strictEqual(attributes[ATTR_DB_OPERATION_NAME], 'save');
+ assert.strictEqual(
+ attributes[ATTR_DB_QUERY_TEXT],
+ JSON.stringify({ targetOrEntity: user })
+ );
+ await ds.destroy();
+ });
+
+ it('remove', async () => {
+ const options = defaultOptions;
+ const ds = new typeorm.DataSource(options);
+ await ds.initialize();
+ const manager = ds.manager;
+
+ const user = new User(56, 'opentelemetry', 'io');
+ await manager.save(user);
+ await manager.remove(user);
+ const typeOrmSpans = getTestSpans();
+
+ assert.strictEqual(typeOrmSpans.length, 2);
+ assert.strictEqual(typeOrmSpans[1].name, 'remove user');
+ assert.strictEqual(typeOrmSpans[1].status.code, SpanStatusCode.UNSET);
+ const attributes = typeOrmSpans[1].attributes;
+ assert.strictEqual(attributes[ATTR_DB_COLLECTION_NAME], 'user');
+ assert.strictEqual(attributes[ATTR_DB_SYSTEM_NAME], options.type);
+ assert.strictEqual(attributes[ATTR_DB_NAMESPACE], options.database);
+ assert.strictEqual(attributes[ATTR_DB_OPERATION_NAME], 'remove');
+ assert.strictEqual(
+ attributes[ATTR_DB_QUERY_TEXT],
+ JSON.stringify({
+ targetOrEntity: {
+ id: 56,
+ firstName: 'opentelemetry',
+ lastName: 'io',
+ },
+ })
+ );
+ await ds.destroy();
+ });
+
+ it('update', async () => {
+ const options = defaultOptions;
+ const ds = new typeorm.DataSource(options);
+ await ds.initialize();
+ const manager = ds.manager;
+ const user = new User(56, 'opentelemetry', 'io');
+ await manager.save(user);
+ const partialEntity = { lastName: '.io' };
+ await manager.update(User, 56, partialEntity);
+ const typeOrmSpans = getTestSpans();
+
+ assert.strictEqual(typeOrmSpans.length, 2);
+ assert.strictEqual(typeOrmSpans[1].name, 'update user');
+ assert.strictEqual(typeOrmSpans[1].status.code, SpanStatusCode.UNSET);
+ const attributes = typeOrmSpans[1].attributes;
+ assert.strictEqual(attributes[ATTR_DB_COLLECTION_NAME], 'user');
+ assert.strictEqual(attributes[ATTR_DB_SYSTEM_NAME], options.type);
+ assert.strictEqual(attributes[ATTR_DB_NAMESPACE], options.database);
+ assert.strictEqual(attributes[ATTR_DB_OPERATION_NAME], 'update');
+ assert.strictEqual(
+ attributes[ATTR_DB_QUERY_TEXT],
+ JSON.stringify({ target: 'User', criteria: 56, partialEntity })
+ );
+ await ds.destroy();
+ });
+
+ it('Sets failure status when function throws', async () => {
+ const ds = new typeorm.DataSource(defaultOptions);
+ await ds.initialize();
+ const manager = ds.manager;
+ try {
+ await manager.find({} as any);
+ } catch (err) {}
+
+ const typeOrmSpans = getTestSpans();
+ assert.strictEqual(typeOrmSpans.length, 1);
+ assert.strictEqual(typeOrmSpans[0].name, 'find');
+ assert.strictEqual(typeOrmSpans[0].status.code, SpanStatusCode.ERROR);
+ assert.strictEqual(
+ typeOrmSpans[0].status.message,
+ 'No metadata for "[object Object]" was found.'
+ );
+ await ds.destroy();
+ });
+ });
+
+ describe('multiple connections', () => {
+ const options2: any = {
+ name: 'connection2',
+ type: 'sqlite',
+ database: 'connection2.db',
+ entities: [User],
+ synchronize: true,
+ };
+
+ it('appends matching connection details to span', async () => {
+ const ds1 = new typeorm.DataSource(defaultOptions);
+ await ds1.initialize();
+ const ds2 = new typeorm.DataSource(options2);
+ await ds2.initialize();
+
+ const manager1 = ds1.manager;
+ const manager2 = ds2.manager;
+
+ const user = new User(1, 'opentelemetry', 'io');
+ await manager1.save(user);
+ await manager2.remove(user);
+
+ const spans = getTestSpans();
+ assert.strictEqual(spans.length, 2);
+ const sqlite1Span = spans[0];
+ const sqlite2Span = spans[1];
+
+ assert.strictEqual(sqlite1Span.name, 'save user');
+ assert.strictEqual(
+ sqlite1Span.attributes[ATTR_DB_SYSTEM_NAME],
+ defaultOptions.type
+ );
+ assert.strictEqual(
+ sqlite1Span.attributes[ATTR_DB_NAMESPACE],
+ defaultOptions.database
+ );
+ assert.strictEqual(
+ sqlite1Span.attributes[ATTR_DB_OPERATION_NAME],
+ 'save'
+ );
+ assert.strictEqual(
+ sqlite1Span.attributes[ATTR_DB_COLLECTION_NAME],
+ 'user'
+ );
+
+ assert.strictEqual(sqlite2Span.name, 'remove user');
+ assert.strictEqual(
+ sqlite2Span.attributes[ATTR_DB_SYSTEM_NAME],
+ options2.type
+ );
+ assert.strictEqual(
+ sqlite2Span.attributes[ATTR_DB_NAMESPACE],
+ options2.database
+ );
+ assert.strictEqual(
+ sqlite2Span.attributes[ATTR_DB_OPERATION_NAME],
+ 'remove'
+ );
+ assert.strictEqual(
+ sqlite2Span.attributes[ATTR_DB_COLLECTION_NAME],
+ 'user'
+ );
+ await ds1.destroy();
+ await ds2.destroy();
+ });
+ });
+});
diff --git a/plugins/node/instrumentation-typeorm/test/QueryBuilder.test.ts b/plugins/node/instrumentation-typeorm/test/QueryBuilder.test.ts
new file mode 100644
index 0000000000..710155d7cf
--- /dev/null
+++ b/plugins/node/instrumentation-typeorm/test/QueryBuilder.test.ts
@@ -0,0 +1,72 @@
+/*
+ * Copyright The OpenTelemetry Authors, Aspecto
+ *
+ * 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 assert from 'assert';
+import { SpanStatusCode } from '@opentelemetry/api';
+import {
+ ATTR_DB_NAMESPACE,
+ ATTR_DB_COLLECTION_NAME,
+ ATTR_DB_QUERY_TEXT,
+ ATTR_DB_SYSTEM_NAME,
+ ATTR_SERVER_ADDRESS,
+ ATTR_SERVER_PORT,
+} from '../src/semconv';
+import { TypeormInstrumentation } from '../src';
+import {
+ getTestSpans,
+ registerInstrumentationTesting,
+} from '@opentelemetry/contrib-test-utils';
+
+const instrumentation = registerInstrumentationTesting(
+ new TypeormInstrumentation()
+);
+
+import * as typeorm from 'typeorm';
+import { defaultOptions, User } from './utils';
+
+describe('QueryBuilder', () => {
+ beforeEach(() => {
+ instrumentation.enable();
+ });
+
+ afterEach(() => {
+ instrumentation.disable();
+ });
+
+ it('getManyAndCount', async () => {
+ const ds = new typeorm.DataSource(defaultOptions);
+ await ds.initialize();
+ const queryBuilder = ds.getRepository(User).createQueryBuilder('user');
+ const users = await queryBuilder
+ .where('user.id = :userId', { userId: '1' })
+ .getManyAndCount();
+ assert.strictEqual(users.length, 2);
+ const typeOrmSpans = getTestSpans();
+ assert.strictEqual(typeOrmSpans.length, 1);
+ assert.strictEqual(typeOrmSpans[0].name, 'select user');
+ assert.strictEqual(typeOrmSpans[0].status.code, SpanStatusCode.UNSET);
+ const attributes = typeOrmSpans[0].attributes;
+ assert.strictEqual(attributes[ATTR_DB_SYSTEM_NAME], defaultOptions.type);
+ assert.strictEqual(attributes[ATTR_SERVER_ADDRESS], defaultOptions.host);
+ assert.strictEqual(attributes[ATTR_SERVER_PORT], defaultOptions.port);
+ assert.strictEqual(attributes[ATTR_DB_NAMESPACE], defaultOptions.database);
+ assert.strictEqual(attributes[ATTR_DB_COLLECTION_NAME], 'user');
+ assert.strictEqual(
+ attributes[ATTR_DB_QUERY_TEXT],
+ 'SELECT "user"."id" AS "user_id", "user"."firstName" AS "user_firstName", "user"."lastName" AS "user_lastName" FROM "user" "user" WHERE "user"."id" = :userId'
+ );
+ await ds.destroy();
+ });
+});
diff --git a/plugins/node/instrumentation-typeorm/test/Repository.test.ts b/plugins/node/instrumentation-typeorm/test/Repository.test.ts
new file mode 100644
index 0000000000..8dec19a1f1
--- /dev/null
+++ b/plugins/node/instrumentation-typeorm/test/Repository.test.ts
@@ -0,0 +1,54 @@
+/*
+ * Copyright The OpenTelemetry Authors, Aspecto
+ *
+ * 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 assert from 'assert';
+import { TypeormInstrumentation } from '../src';
+import {
+ getTestSpans,
+ registerInstrumentationTesting,
+} from '@opentelemetry/contrib-test-utils';
+
+const instrumentation = registerInstrumentationTesting(
+ new TypeormInstrumentation()
+);
+
+import { defaultOptions, User } from './utils';
+import * as typeorm from 'typeorm';
+import { ATTR_DB_COLLECTION_NAME } from '../src/semconv';
+
+describe('Repository', () => {
+ beforeEach(() => {
+ instrumentation.enable();
+ });
+
+ afterEach(() => {
+ instrumentation.disable();
+ });
+
+ it('findAndCount', async () => {
+ const ds = new typeorm.DataSource(defaultOptions);
+ await ds.initialize();
+ const repo = ds.getRepository(User);
+ const [_users, count] = await repo.findAndCount();
+ assert(count === 0);
+ const spans = getTestSpans();
+ assert.strictEqual(spans.length, 1);
+ const span = spans[0];
+ const attributes = span.attributes;
+ assert.strictEqual(span.name, 'findAndCount user');
+ assert.strictEqual(attributes[ATTR_DB_COLLECTION_NAME], 'user');
+ await ds.destroy();
+ });
+});
diff --git a/plugins/node/instrumentation-typeorm/test/config.test.ts b/plugins/node/instrumentation-typeorm/test/config.test.ts
new file mode 100644
index 0000000000..f1854002b8
--- /dev/null
+++ b/plugins/node/instrumentation-typeorm/test/config.test.ts
@@ -0,0 +1,159 @@
+/*
+ * Copyright The OpenTelemetry Authors, Aspecto
+ *
+ * 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 assert from 'assert';
+import {
+ ATTR_SERVER_PORT,
+ ATTR_SERVER_ADDRESS,
+ ATTR_DB_COLLECTION_NAME,
+ ATTR_DB_NAMESPACE,
+ ATTR_DB_QUERY_TEXT,
+ ATTR_DB_SYSTEM_NAME,
+ ATTR_DB_OPERATION_NAME,
+} from '../src/semconv';
+import {
+ ExtendedDatabaseAttribute,
+ TypeormInstrumentation,
+ TypeormInstrumentationConfig,
+} from '../src';
+
+import {
+ getTestSpans,
+ registerInstrumentationTesting,
+} from '@opentelemetry/contrib-test-utils';
+
+const instrumentation = registerInstrumentationTesting(
+ new TypeormInstrumentation()
+);
+
+import * as typeorm from 'typeorm';
+import { defaultOptions, User } from './utils';
+import { SpanStatusCode } from '@opentelemetry/api';
+
+describe('TypeormInstrumentationConfig', () => {
+ it('responseHook', async function () {
+ this.timeout(3_000);
+ instrumentation.disable();
+ const config: TypeormInstrumentationConfig = {
+ responseHook: (span, { response }) => {
+ span.setAttribute('test', JSON.stringify(response));
+ },
+ };
+ instrumentation.setConfig(config);
+ instrumentation.enable();
+
+ const ds = new typeorm.DataSource(defaultOptions);
+ await ds.initialize();
+ const user = new User(1, 'opentelemetry', 'io');
+ await ds.manager.save(user);
+ const typeOrmSpans = getTestSpans();
+ assert.strictEqual(typeOrmSpans.length, 1);
+ const attributes = typeOrmSpans[0].attributes;
+
+ assert.strictEqual(attributes['test'], JSON.stringify(user));
+ assert.strictEqual(attributes[ATTR_DB_OPERATION_NAME], 'save');
+ assert.strictEqual(attributes[ATTR_DB_SYSTEM_NAME], defaultOptions.type);
+ await ds.destroy();
+ });
+
+ it('enableInternalInstrumentation:true', async () => {
+ const config: TypeormInstrumentationConfig = {
+ enableInternalInstrumentation: true,
+ };
+ instrumentation.setConfig(config);
+ const ds = new typeorm.DataSource(defaultOptions);
+ await ds.initialize();
+ await ds.manager.findAndCount(User);
+ const spans = getTestSpans();
+ assert.strictEqual(spans.length, 2);
+
+ const findAndCountSpan = spans.find(
+ s => s.name.indexOf('findAndCount') !== -1
+ );
+ assert.notStrictEqual(findAndCountSpan, undefined);
+ assert.strictEqual(
+ findAndCountSpan?.attributes[ATTR_DB_OPERATION_NAME],
+ 'findAndCount'
+ );
+ assert.strictEqual(
+ findAndCountSpan?.attributes[ATTR_DB_COLLECTION_NAME],
+ 'user'
+ );
+
+ const selectSpan = spans.find(s => s.name.indexOf('select') !== -1);
+ assert.notStrictEqual(selectSpan, undefined);
+ assert.strictEqual(
+ selectSpan?.attributes[ATTR_DB_OPERATION_NAME],
+ 'select'
+ );
+ assert.strictEqual(selectSpan?.attributes[ATTR_DB_COLLECTION_NAME], 'user');
+ await ds.destroy();
+ });
+
+ it('enableInternalInstrumentation:false', async () => {
+ const config: TypeormInstrumentationConfig = {
+ enableInternalInstrumentation: false,
+ };
+ instrumentation.setConfig(config);
+ const ds = new typeorm.DataSource(defaultOptions);
+ await ds.initialize();
+ await ds.manager.findAndCount(User);
+ const spans = getTestSpans();
+ assert.strictEqual(spans.length, 1);
+ const attributes = spans[0].attributes;
+ assert.strictEqual(attributes[ATTR_DB_OPERATION_NAME], 'findAndCount');
+ assert.strictEqual(attributes[ATTR_DB_SYSTEM_NAME], defaultOptions.type);
+ assert.strictEqual(attributes[ATTR_DB_COLLECTION_NAME], 'user');
+ await ds.destroy();
+ });
+
+ it('enhancedDatabaseReporting:true', async () => {
+ const config: TypeormInstrumentationConfig = {
+ enhancedDatabaseReporting: true,
+ };
+ instrumentation.setConfig(config);
+ const connectionOptions = defaultOptions;
+ const ds = new typeorm.DataSource(connectionOptions);
+ await ds.initialize();
+ await ds
+ .getRepository(User)
+ .createQueryBuilder('user')
+ .where('user.id = :userId', { userId: '1' })
+ .andWhere('user.firstName = :firstName', { firstName: 'bob' })
+ .andWhere('user.lastName = :lastName', { lastName: 'dow' })
+ .getMany();
+ const typeOrmSpans = getTestSpans();
+ assert.strictEqual(typeOrmSpans.length, 1);
+ assert.strictEqual(typeOrmSpans[0].status.code, SpanStatusCode.UNSET);
+ const attributes = typeOrmSpans[0].attributes;
+ assert.strictEqual(attributes[ATTR_DB_SYSTEM_NAME], connectionOptions.type);
+ assert.strictEqual(attributes[ATTR_SERVER_ADDRESS], connectionOptions.host);
+ assert.strictEqual(attributes[ATTR_SERVER_PORT], connectionOptions.port);
+ assert.strictEqual(
+ attributes[ATTR_DB_NAMESPACE],
+ connectionOptions.database
+ );
+ assert.strictEqual(attributes[ATTR_DB_COLLECTION_NAME], 'user');
+ assert.strictEqual(
+ attributes[ATTR_DB_QUERY_TEXT],
+ 'SELECT "user"."id" AS "user_id", "user"."firstName" AS "user_firstName", "user"."lastName" AS "user_lastName" FROM "user" "user" WHERE "user"."id" = :userId AND "user"."firstName" = :firstName AND "user"."lastName" = :lastName'
+ );
+ assert.strictEqual(
+ attributes[ExtendedDatabaseAttribute.DB_STATEMENT_PARAMETERS],
+ JSON.stringify({ userId: '1', firstName: 'bob', lastName: 'dow' })
+ );
+ await ds.destroy();
+ });
+});
diff --git a/plugins/node/instrumentation-typeorm/test/utils.ts b/plugins/node/instrumentation-typeorm/test/utils.ts
new file mode 100644
index 0000000000..9c039e4685
--- /dev/null
+++ b/plugins/node/instrumentation-typeorm/test/utils.ts
@@ -0,0 +1,42 @@
+/*
+ * Copyright The OpenTelemetry Authors, Aspecto
+ *
+ * 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 typeorm from 'typeorm';
+
+@typeorm.Entity()
+export class User {
+ @typeorm.PrimaryColumn()
+ id: number;
+
+ @typeorm.Column()
+ firstName: string;
+
+ @typeorm.Column()
+ lastName: string;
+
+ constructor(id: number, firstName: string, lastName: string) {
+ this.id = id;
+ this.firstName = firstName;
+ this.lastName = lastName;
+ }
+}
+
+export const defaultOptions: any = {
+ type: 'sqlite',
+ database: ':memory:',
+ dropSchema: true,
+ synchronize: true,
+ entities: [User],
+};
diff --git a/plugins/node/instrumentation-typeorm/tsconfig.json b/plugins/node/instrumentation-typeorm/tsconfig.json
new file mode 100644
index 0000000000..9078465762
--- /dev/null
+++ b/plugins/node/instrumentation-typeorm/tsconfig.json
@@ -0,0 +1,13 @@
+{
+ "extends": "../../../tsconfig.base",
+ "compilerOptions": {
+ "rootDir": ".",
+ "outDir": "build",
+ "experimentalDecorators": true,
+ "emitDecoratorMetadata": true
+ },
+ "include": [
+ "src/**/*.ts",
+ "test/**/*.ts"
+ ]
+}
diff --git a/release-please-config.json b/release-please-config.json
index a55d06f9ec..c66d81cda9 100644
--- a/release-please-config.json
+++ b/release-please-config.json
@@ -35,6 +35,7 @@
"plugins/node/instrumentation-runtime-node": {},
"plugins/node/instrumentation-socket.io": {},
"plugins/node/instrumentation-tedious": {},
+ "plugins/node/instrumentation-typeorm": {},
"plugins/node/instrumentation-undici": {},
"plugins/node/opentelemetry-instrumentation-aws-lambda": {},
"plugins/node/opentelemetry-instrumentation-aws-sdk": {},