@rootcat/node 0.0.0-stage → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/CHANGELOG.md +83 -0
  2. package/LICENSE +202 -0
  3. package/README.md +294 -2
  4. package/dist/attach.d.ts +1 -0
  5. package/dist/attach.js +24 -0
  6. package/dist/attach.js.map +1 -0
  7. package/dist/callsite.d.ts +109 -0
  8. package/dist/callsite.js +565 -0
  9. package/dist/callsite.js.map +1 -0
  10. package/dist/coexist.d.ts +183 -0
  11. package/dist/coexist.js +587 -0
  12. package/dist/coexist.js.map +1 -0
  13. package/dist/config.d.ts +75 -0
  14. package/dist/config.js +182 -0
  15. package/dist/config.js.map +1 -0
  16. package/dist/drizzle.d.ts +21 -0
  17. package/dist/drizzle.js +127 -0
  18. package/dist/drizzle.js.map +1 -0
  19. package/dist/esm.mjs +57 -0
  20. package/dist/esmhook.d.ts +52 -0
  21. package/dist/esmhook.js +158 -0
  22. package/dist/esmhook.js.map +1 -0
  23. package/dist/iitm-hook.mjs +83 -0
  24. package/dist/index.d.ts +12 -0
  25. package/dist/index.js +52 -0
  26. package/dist/index.js.map +1 -0
  27. package/dist/intake.d.ts +40 -0
  28. package/dist/intake.js +164 -0
  29. package/dist/intake.js.map +1 -0
  30. package/dist/keys.d.ts +20 -0
  31. package/dist/keys.js +33 -0
  32. package/dist/keys.js.map +1 -0
  33. package/dist/notices.d.ts +2 -0
  34. package/dist/notices.js +21 -0
  35. package/dist/notices.js.map +1 -0
  36. package/dist/otlp.d.ts +75 -0
  37. package/dist/otlp.js +449 -0
  38. package/dist/otlp.js.map +1 -0
  39. package/dist/pg.d.ts +16 -0
  40. package/dist/pg.js +170 -0
  41. package/dist/pg.js.map +1 -0
  42. package/dist/pgpool.d.ts +20 -0
  43. package/dist/pgpool.js +99 -0
  44. package/dist/pgpool.js.map +1 -0
  45. package/dist/postgresjs.d.ts +60 -0
  46. package/dist/postgresjs.js +400 -0
  47. package/dist/postgresjs.js.map +1 -0
  48. package/dist/privacy.d.ts +38 -0
  49. package/dist/privacy.js +161 -0
  50. package/dist/privacy.js.map +1 -0
  51. package/dist/processor.d.ts +197 -0
  52. package/dist/processor.js +494 -0
  53. package/dist/processor.js.map +1 -0
  54. package/dist/register.d.ts +1 -0
  55. package/dist/register.js +12 -0
  56. package/dist/register.js.map +1 -0
  57. package/dist/sampler.d.ts +30 -0
  58. package/dist/sampler.js +101 -0
  59. package/dist/sampler.js.map +1 -0
  60. package/dist/sdk.d.ts +129 -0
  61. package/dist/sdk.js +606 -0
  62. package/dist/sdk.js.map +1 -0
  63. package/package.json +72 -4
@@ -0,0 +1,75 @@
1
+ /** Config is the package's whole configuration, parsed. */
2
+ export interface Config {
3
+ /** enabled is false when ROOTCAT_DISABLED is set; nothing starts then. */
4
+ enabled: boolean;
5
+ /** url is the full OTLP/HTTP traces URL spans are posted to. */
6
+ url: string;
7
+ /** serviceName is the service.name resource attribute. */
8
+ serviceName: string;
9
+ /** serviceVersion is the service.version resource attribute, "" when unknown. */
10
+ serviceVersion: string;
11
+ /** environment is deployment.environment.name, "" when unknown. */
12
+ environment: string;
13
+ /** sampleRatio is the share of traces recorded, 0 to 1 (ROOTCAT_SAMPLE). */
14
+ sampleRatio: number;
15
+ /**
16
+ * callsiteSample is the sampling divisor for call-site capture: 1 captures
17
+ * every eligible span, N one in N, 0 captures none.
18
+ */
19
+ callsiteSample: number;
20
+ /** callsiteDepth is how many stack frames a capture may walk; fixed. */
21
+ callsiteDepth: number;
22
+ /**
23
+ * statement says whether query text keeps its shape with literals
24
+ * masked ("masked", the default) or only its first keyword ("off").
25
+ */
26
+ statement: 'masked' | 'off';
27
+ /** debug turns the OpenTelemetry diagnostic logger on, to stderr. */
28
+ debug: boolean;
29
+ /** disabled lists instrumentation names switched off by ROOTCAT_DISABLE. */
30
+ disabled: Set<string>;
31
+ /** cwd is the application's working directory (service identity, Prisma). */
32
+ cwd: string;
33
+ }
34
+ /** Bounds on what is read while looking for the service identity. */
35
+ export declare const maxPackageJSONBytes: number;
36
+ /** Defaults; the two sampling values are the knobs the environment has. */
37
+ export declare const defaultSampleRatio = 1;
38
+ export declare const defaultCallsiteSample = 1;
39
+ export declare const callsiteDepth = 32;
40
+ export declare const otlpPort = 4318;
41
+ /** Env is the subset of process.env the package reads. */
42
+ export type Env = Record<string, string | undefined>;
43
+ /** ServiceIdentity is what the resource says about the service. */
44
+ export interface ServiceIdentity {
45
+ name: string;
46
+ version: string;
47
+ }
48
+ /**
49
+ * serviceIdentity resolves the service name from ROOTCAT_SERVICE, then
50
+ * OTEL_SERVICE_NAME, then the nearest package.json above cwd (at most
51
+ * eight levels, 64 KiB), else "unknown_service:node"; the version comes
52
+ * from the same package.json when it has one.
53
+ */
54
+ export declare function serviceIdentity(env: Env, cwd: string): ServiceIdentity;
55
+ /** nearestPackageJSON reads name and version from the closest package.json. */
56
+ export declare function nearestPackageJSON(cwd: string): {
57
+ name?: string;
58
+ version?: string;
59
+ } | undefined;
60
+ /** clean trims a value and bounds it to 256 characters; "" when unset. */
61
+ export declare function clean(value: string | undefined): string;
62
+ /** positiveInt parses a non-negative integer, else returns fallback. */
63
+ export declare function nonNegativeInt(value: string | undefined, fallback: number, max: number): number;
64
+ /**
65
+ * tracesURL turns the configured intake address into the traces endpoint:
66
+ * a bare origin gets /v1/traces, a URL already ending in /v1/traces is kept.
67
+ */
68
+ export declare function tracesURL(base: string): string;
69
+ /** ratio parses a share between 0 and 1, else returns fallback. */
70
+ export declare function ratio(value: string | undefined, fallback: number): number;
71
+ /**
72
+ * readConfig parses the environment. discover is asked for the intake
73
+ * origin only when ROOTCAT_OTLP_URL is unset.
74
+ */
75
+ export declare function readConfig(env: Env, cwd: string, discover: () => string): Config;
package/dist/config.js ADDED
@@ -0,0 +1,182 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.otlpPort = exports.callsiteDepth = exports.defaultCallsiteSample = exports.defaultSampleRatio = exports.maxPackageJSONBytes = void 0;
37
+ exports.serviceIdentity = serviceIdentity;
38
+ exports.nearestPackageJSON = nearestPackageJSON;
39
+ exports.clean = clean;
40
+ exports.nonNegativeInt = nonNegativeInt;
41
+ exports.tracesURL = tracesURL;
42
+ exports.ratio = ratio;
43
+ exports.readConfig = readConfig;
44
+ // Configuration: what the environment says, with every value validated
45
+ // once here and passed inward as a parsed value. The package reads only
46
+ // ROOTCAT_* variables (and OTEL_SERVICE_NAME for the service name); the
47
+ // OTEL_EXPORTER_OTLP_* variables are ignored so that spans go to the host
48
+ // agent and nowhere else.
49
+ const fs = __importStar(require("node:fs"));
50
+ const path = __importStar(require("node:path"));
51
+ /** Bounds on what is read while looking for the service identity. */
52
+ exports.maxPackageJSONBytes = 64 * 1024;
53
+ const maxPackageJSONLevels = 8;
54
+ /** Defaults; the two sampling values are the knobs the environment has. */
55
+ exports.defaultSampleRatio = 1;
56
+ exports.defaultCallsiteSample = 1;
57
+ exports.callsiteDepth = 32;
58
+ exports.otlpPort = 4318;
59
+ /**
60
+ * serviceIdentity resolves the service name from ROOTCAT_SERVICE, then
61
+ * OTEL_SERVICE_NAME, then the nearest package.json above cwd (at most
62
+ * eight levels, 64 KiB), else "unknown_service:node"; the version comes
63
+ * from the same package.json when it has one.
64
+ */
65
+ function serviceIdentity(env, cwd) {
66
+ const pkg = nearestPackageJSON(cwd);
67
+ const fromEnv = clean(env.ROOTCAT_SERVICE) || clean(env.OTEL_SERVICE_NAME);
68
+ return {
69
+ name: fromEnv || pkg?.name || 'unknown_service:node',
70
+ version: pkg?.version ?? '',
71
+ };
72
+ }
73
+ /** nearestPackageJSON reads name and version from the closest package.json. */
74
+ function nearestPackageJSON(cwd) {
75
+ let dir = path.resolve(cwd);
76
+ for (let level = 0; level < maxPackageJSONLevels; level++) {
77
+ const file = path.join(dir, 'package.json');
78
+ const parsed = readPackageJSON(file);
79
+ if (parsed) {
80
+ return parsed;
81
+ }
82
+ const parent = path.dirname(dir);
83
+ if (parent === dir) {
84
+ break;
85
+ }
86
+ dir = parent;
87
+ }
88
+ return undefined;
89
+ }
90
+ function readPackageJSON(file) {
91
+ let text;
92
+ try {
93
+ const stat = fs.statSync(file);
94
+ if (!stat.isFile() || stat.size > exports.maxPackageJSONBytes) {
95
+ return undefined;
96
+ }
97
+ text = fs.readFileSync(file, 'utf8');
98
+ }
99
+ catch {
100
+ return undefined;
101
+ }
102
+ try {
103
+ const parsed = JSON.parse(text);
104
+ if (!parsed || typeof parsed !== 'object') {
105
+ return undefined;
106
+ }
107
+ const record = parsed;
108
+ return {
109
+ name: clean(typeof record.name === 'string' ? record.name : undefined) || undefined,
110
+ version: clean(typeof record.version === 'string' ? record.version : undefined) || undefined,
111
+ };
112
+ }
113
+ catch {
114
+ return undefined;
115
+ }
116
+ }
117
+ /** clean trims a value and bounds it to 256 characters; "" when unset. */
118
+ function clean(value) {
119
+ if (!value) {
120
+ return '';
121
+ }
122
+ return value.trim().slice(0, 256);
123
+ }
124
+ /** positiveInt parses a non-negative integer, else returns fallback. */
125
+ function nonNegativeInt(value, fallback, max) {
126
+ const text = clean(value);
127
+ if (!/^\d{1,9}$/.test(text)) {
128
+ return fallback;
129
+ }
130
+ const n = Number(text);
131
+ return n > max ? max : n;
132
+ }
133
+ /**
134
+ * tracesURL turns the configured intake address into the traces endpoint:
135
+ * a bare origin gets /v1/traces, a URL already ending in /v1/traces is kept.
136
+ */
137
+ function tracesURL(base) {
138
+ const trimmed = base.trim().replace(/\/+$/, '');
139
+ if (trimmed.endsWith('/v1/traces')) {
140
+ return trimmed;
141
+ }
142
+ return trimmed + '/v1/traces';
143
+ }
144
+ /** ratio parses a share between 0 and 1, else returns fallback. */
145
+ function ratio(value, fallback) {
146
+ const text = clean(value);
147
+ if (!/^(0|1|0?\.\d{1,6}|1\.0+)$/.test(text)) {
148
+ return fallback;
149
+ }
150
+ return Number(text);
151
+ }
152
+ /**
153
+ * readConfig parses the environment. discover is asked for the intake
154
+ * origin only when ROOTCAT_OTLP_URL is unset.
155
+ */
156
+ function readConfig(env, cwd, discover) {
157
+ const identity = serviceIdentity(env, cwd);
158
+ const base = clean(env.ROOTCAT_OTLP_URL) || discover();
159
+ const disabled = new Set(clean(env.ROOTCAT_DISABLE)
160
+ .split(',')
161
+ .map((name) => name.trim().toLowerCase())
162
+ .filter((name) => name !== ''));
163
+ return {
164
+ enabled: !isSet(env.ROOTCAT_DISABLED),
165
+ url: tracesURL(base),
166
+ serviceName: identity.name,
167
+ serviceVersion: identity.version,
168
+ environment: clean(env.ROOTCAT_ENVIRONMENT) || clean(env.NODE_ENV),
169
+ sampleRatio: ratio(env.ROOTCAT_SAMPLE, exports.defaultSampleRatio),
170
+ callsiteSample: nonNegativeInt(env.ROOTCAT_CALLSITE_SAMPLE, exports.defaultCallsiteSample, 1_000_000),
171
+ callsiteDepth: exports.callsiteDepth,
172
+ statement: clean(env.ROOTCAT_DB_STATEMENT).toLowerCase() === 'off' ? 'off' : 'masked',
173
+ debug: isSet(env.ROOTCAT_DEBUG),
174
+ disabled,
175
+ cwd: path.resolve(cwd),
176
+ };
177
+ }
178
+ function isSet(value) {
179
+ const text = clean(value).toLowerCase();
180
+ return text !== '' && text !== '0' && text !== 'false' && text !== 'off' && text !== 'no';
181
+ }
182
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ import { InstrumentationBase, type InstrumentationConfig, InstrumentationNodeModuleDefinition } from '@opentelemetry/instrumentation';
2
+ import type { CallSiteSampling } from './processor.js';
3
+ /** DrizzleEntryOptions bounds and gates the capture. */
4
+ export interface DrizzleEntryOptions extends InstrumentationConfig {
5
+ depth: number;
6
+ sampling: CallSiteSampling;
7
+ isApp?: (file: string) => boolean;
8
+ }
9
+ export declare class DrizzleEntryInstrumentation extends InstrumentationBase<DrizzleEntryOptions> {
10
+ /** built holds the call site and root decision of every builder constructed. */
11
+ private readonly built;
12
+ constructor(options: DrizzleEntryOptions);
13
+ protected init(): InstrumentationNodeModuleDefinition;
14
+ /**
15
+ * patchClass replaces the named export with a capturing subclass. An ES
16
+ * module namespace takes the assignment; the CommonJS build exports
17
+ * getters, so a fresh exports object is returned in its place.
18
+ */
19
+ private patchClass;
20
+ private capturing;
21
+ }
@@ -0,0 +1,127 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DrizzleEntryInstrumentation = void 0;
4
+ // Drizzle entry: a Drizzle query is a thenable built in application code
5
+ // (db.select().from(t), db.insert(t).values(v), db.query.t.findMany(),
6
+ // db.execute(sql)) and run when awaited, from a job where no application
7
+ // frame remains. The builders descend from two classes the package
8
+ // exports, QueryPromise (insert, update, delete, raw, relational queries)
9
+ // and TypedQueryBuilder (selects, which take QueryPromise's then/catch/
10
+ // finally as mixins). Both are replaced, at module load, by subclasses
11
+ // that capture the call site when a builder is constructed, and then()
12
+ // runs under a context carrying that site, so the driver span started
13
+ // later (pg, postgres.js, mysql2, or Drizzle's own drizzle.* spans) is
14
+ // given it. Nothing else of Drizzle is touched; a Drizzle bundled into an
15
+ // application file is not seen and keeps working untraced.
16
+ const api_1 = require("@opentelemetry/api");
17
+ const instrumentation_1 = require("@opentelemetry/instrumentation");
18
+ const callsite_js_1 = require("./callsite.js");
19
+ const keys_js_1 = require("./keys.js");
20
+ const supportedVersions = ['>=0.28.0 <2'];
21
+ class DrizzleEntryInstrumentation extends instrumentation_1.InstrumentationBase {
22
+ /** built holds the call site and root decision of every builder constructed. */
23
+ built = new WeakMap();
24
+ constructor(options) {
25
+ super('@rootcat/node/drizzle-entry', '0.2.0', options);
26
+ }
27
+ init() {
28
+ // Both loaders name an internal file by its path inside the package:
29
+ // the .cjs build under require, the .js build under import (the ESM
30
+ // entry includes Drizzle's files in the loader's list for this). A
31
+ // file that does not export the class is left alone.
32
+ const file = (name, exportName, withThen) => new instrumentation_1.InstrumentationNodeModuleFile(name, supportedVersions, (moduleExports) => this.patchClass(moduleExports, exportName, withThen), () => undefined);
33
+ return new instrumentation_1.InstrumentationNodeModuleDefinition('drizzle-orm', supportedVersions, undefined, undefined, [
34
+ file('drizzle-orm/query-promise.js', 'QueryPromise', true),
35
+ file('drizzle-orm/query-promise.cjs', 'QueryPromise', true),
36
+ file('drizzle-orm/query-builders/query-builder.js', 'TypedQueryBuilder', false),
37
+ file('drizzle-orm/query-builders/query-builder.cjs', 'TypedQueryBuilder', false),
38
+ ]);
39
+ }
40
+ /**
41
+ * patchClass replaces the named export with a capturing subclass. An ES
42
+ * module namespace takes the assignment; the CommonJS build exports
43
+ * getters, so a fresh exports object is returned in its place.
44
+ */
45
+ patchClass(moduleExports, exportName, withThen) {
46
+ const ns = moduleExports;
47
+ const Original = ns?.[exportName];
48
+ if (typeof Original !== 'function') {
49
+ return moduleExports;
50
+ }
51
+ const Traced = this.capturing(Original, withThen);
52
+ try {
53
+ ns[exportName] = Traced;
54
+ }
55
+ catch {
56
+ // getters only: replaced below
57
+ }
58
+ if (ns[exportName] === Traced) {
59
+ return moduleExports;
60
+ }
61
+ const replacement = {};
62
+ for (const key of Object.getOwnPropertyNames(ns)) {
63
+ replacement[key] = key === exportName ? Traced : ns[key];
64
+ }
65
+ return replacement;
66
+ }
67
+ capturing(Original, withThen) {
68
+ const instrumentation = this;
69
+ const Traced = class extends Original {
70
+ constructor(...args) {
71
+ super(...args);
72
+ if (!instrumentation.isEnabled()) {
73
+ return;
74
+ }
75
+ try {
76
+ const config = instrumentation.getConfig();
77
+ const carried = config.sampling.carry(() => (0, callsite_js_1.captureCallSite)(config.depth, config.isApp, Traced));
78
+ const site = carried?.getValue(keys_js_1.callSiteContextKey);
79
+ const decided = carried?.getValue(keys_js_1.rootDecisionKey);
80
+ if (site || decided !== undefined) {
81
+ instrumentation.built.set(this, { site, decided });
82
+ }
83
+ }
84
+ catch {
85
+ // a builder without a site
86
+ }
87
+ }
88
+ };
89
+ Object.defineProperty(Traced, 'name', { value: Original.name });
90
+ if (withThen) {
91
+ // Selects take these as mixins (own properties copied by name), so
92
+ // they must be own properties here. then() runs the query under the
93
+ // context where it is awaited, with the builder's own site (which
94
+ // wins over any other: a query built in the continuation of another
95
+ // is its own) and its root decision; the caller's callbacks are
96
+ // bound to the caller's context, so nothing carried leaks into them.
97
+ const proto = Original.prototype;
98
+ const originalThen = proto.then;
99
+ Object.defineProperty(Traced.prototype, 'then', {
100
+ configurable: true,
101
+ writable: true,
102
+ value: function then(onFulfilled, onRejected) {
103
+ const built = instrumentation.built.get(this);
104
+ if (!built) {
105
+ return originalThen.call(this, onFulfilled, onRejected);
106
+ }
107
+ const outer = api_1.context.active();
108
+ let carried = built.site ? outer.setValue(keys_js_1.callSiteContextKey, built.site) : outer;
109
+ if (built.decided !== undefined && !api_1.trace.getSpanContext(outer)) {
110
+ carried = carried.setValue(keys_js_1.rootDecisionKey, built.decided);
111
+ }
112
+ const bound = (fn) => (typeof fn === 'function' ? api_1.context.bind(outer, fn) : fn);
113
+ return api_1.context.with(carried, () => originalThen.call(this, bound(onFulfilled), bound(onRejected)));
114
+ },
115
+ });
116
+ for (const name of ['catch', 'finally']) {
117
+ const descriptor = Object.getOwnPropertyDescriptor(proto, name);
118
+ if (descriptor) {
119
+ Object.defineProperty(Traced.prototype, name, descriptor);
120
+ }
121
+ }
122
+ }
123
+ return Traced;
124
+ }
125
+ }
126
+ exports.DrizzleEntryInstrumentation = DrizzleEntryInstrumentation;
127
+ //# sourceMappingURL=drizzle.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"drizzle.js","sourceRoot":"","sources":["../src/drizzle.ts"],"names":[],"mappings":"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"}
package/dist/esm.mjs ADDED
@@ -0,0 +1,57 @@
1
+ // The --import entry for ES module applications:
2
+ // NODE_OPTIONS="--import @rootcat/node/esm"
3
+ // Registers the module loader hook the instrumentations need to patch ES
4
+ // module imports, tells the loader which modules to watch, waits for it
5
+ // to acknowledge them before the application's first import resolves,
6
+ // then starts the same tracing as the CommonJS entry. The hook module and
7
+ // its message channel come from the same import-in-the-middle copy that
8
+ // the instrumentation package uses, so the two sides always agree.
9
+ import { createRequire, register } from 'node:module';
10
+ import { pathToFileURL } from 'node:url';
11
+ import { isMainThread } from 'node:worker_threads';
12
+
13
+ const require = createRequire(import.meta.url);
14
+
15
+ async function main() {
16
+ if (!isMainThread) {
17
+ return; // the loader hooks thread runs the same preloads; nothing to do there
18
+ }
19
+ let waitForAllMessagesAcknowledged = async () => undefined;
20
+ // An application whose tracer sets up OpenTelemetry when it initialises
21
+ // (Sentry) starts Rootcat after it (startAlongside); this entry runs
22
+ // before it, so it registers nothing and register.js says what to do
23
+ // instead.
24
+ const { providerRegistered, tracerDependency } = require('./coexist.js');
25
+ const waitsForTracer = !providerRegistered() && tracerDependency(process.cwd()) !== undefined;
26
+ if (!waitsForTracer) {
27
+ try {
28
+ const { hookedPackages, esmHookMarker, ownInstancePossible, noInstanceLine } = require('./esmhook.js');
29
+ if (!ownInstancePossible()) {
30
+ throw Object.assign(new Error('no create-hook.mjs'), { line: noInstanceLine });
31
+ }
32
+ const iitmPath = createRequire(require.resolve('@opentelemetry/instrumentation/package.json')).resolve('import-in-the-middle');
33
+ const iitm = await import(pathToFileURL(iitmPath).href);
34
+ const channel = iitm.createAddHookMessageChannel();
35
+ // The channel lists only the packages the instrumentations name, by
36
+ // bare specifier; a package patched through one of its internal files
37
+ // (Drizzle's query classes) must be matched by path as well.
38
+ channel.registerOptions.data.include.push(hookedPackages);
39
+ // The package's own import-in-the-middle instance (iitm-hook.mjs),
40
+ // never a second registration of a hook another SDK registered.
41
+ register(new URL('./iitm-hook.mjs', import.meta.url).href, import.meta.url, channel.registerOptions);
42
+ globalThis[esmHookMarker] = true;
43
+ waitForAllMessagesAcknowledged = channel.waitForAllMessagesAcknowledged;
44
+ } catch (error) {
45
+ if (typeof error?.line === 'string') {
46
+ process.stderr.write(error.line);
47
+ } else {
48
+ const reason = error instanceof Error ? error.message : String(error);
49
+ process.stderr.write(`rootcat: ES module hook not registered (${reason.slice(0, 200)}); CommonJS modules are still traced\n`);
50
+ }
51
+ }
52
+ }
53
+ require('./register.js');
54
+ await waitForAllMessagesAcknowledged();
55
+ }
56
+
57
+ await main();
@@ -0,0 +1,52 @@
1
+ /** esmHookMarker is set on globalThis once a hook is registered. */
2
+ export declare const esmHookMarker: unique symbol;
3
+ /**
4
+ * hookedPackages are the modules the loader wraps for the
5
+ * instrumentations. The CommonJS packages are matched whole (their
6
+ * internal files load through require, never through the loader). Of
7
+ * the ES module packages only the files the instrumentations patch are
8
+ * wrapped: postgres.js's entry, and Drizzle's two query classes. Wrapping
9
+ * every file of postgres.js would also hand its internal query.js to any
10
+ * other SDK's hook for it: Sentry patches that file to trace the
11
+ * driver's own connection queries, which it otherwise does only in
12
+ * CommonJS.
13
+ */
14
+ export declare const hookedPackages: RegExp;
15
+ /**
16
+ * noInstanceLine is said when the installed import-in-the-middle has no
17
+ * create-hook.mjs: the package cannot make a hook instance of its own,
18
+ * and a second registration of the shared hook.mjs would replace the
19
+ * options of another SDK that registered it (Sentry's, behind its request
20
+ * isolation), so it registers none: CommonJS modules are still traced.
21
+ */
22
+ export declare const noInstanceLine = "rootcat: the installed import-in-the-middle has no create-hook.mjs; the ES module hook is not registered (ES modules are not traced; CommonJS modules are)\n";
23
+ /**
24
+ * ownInstancePossible reports whether the import-in-the-middle copy
25
+ * @opentelemetry/instrumentation uses has create-hook.mjs (see
26
+ * iitm-hook.mjs).
27
+ */
28
+ export declare function ownInstancePossible(): boolean;
29
+ /**
30
+ * ownedModules are the ES module files only the package patches:
31
+ * postgres.js's entry and Drizzle's two query classes. Beside a tracer
32
+ * that records nothing, the package's hook takes them over from another
33
+ * import-in-the-middle hook that marked them (see iitm-hook.mjs); inside
34
+ * a tracer that records spans, Drizzle's only (that tracer may
35
+ * instrument postgres.js itself).
36
+ */
37
+ export declare const ownedModules: RegExp;
38
+ export declare const drizzleModules: RegExp;
39
+ /** ESMHook says whether the hook was registered. */
40
+ export type ESMHook = 'on' | 'off';
41
+ /**
42
+ * quietESMHook makes the package's loader hook pass every module through
43
+ * from now on: a loader hook cannot be removed once registered, so a
44
+ * stopped package leaves it in place, idle.
45
+ */
46
+ export declare function quietESMHook(): void;
47
+ /**
48
+ * registerESMHook registers the instrumentation loader hook once and
49
+ * reports whether it is in place. A failure to register never reaches the
50
+ * application.
51
+ */
52
+ export declare function registerESMHook(report?: (reason: string) => void, takeOver?: RegExp, include?: RegExp): ESMHook;