@use-tusk/drift-node-sdk 0.1.34 → 0.1.35
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.
- package/README.md +1 -1
- package/dist/index.cjs +622 -149
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +622 -149
- package/dist/index.js.map +1 -1
- package/dist/instrumentation-manifest.json +3 -5
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -304,6 +304,7 @@ var TdInstrumentationAbstract = class {
|
|
|
304
304
|
function safeJsonStringify(obj) {
|
|
305
305
|
const seen = /* @__PURE__ */ new WeakSet();
|
|
306
306
|
return JSON.stringify(obj, (key, value) => {
|
|
307
|
+
if (typeof value === "bigint") return value.toString();
|
|
307
308
|
if (typeof value === "object" && value !== null) {
|
|
308
309
|
if (seen.has(value)) return "[Circular]";
|
|
309
310
|
seen.add(value);
|
|
@@ -7926,7 +7927,7 @@ var SpanUtils = class SpanUtils {
|
|
|
7926
7927
|
...addSpanAttributesOptions.submodule && { [TdSpanAttributes.SUBMODULE_NAME]: addSpanAttributesOptions.submodule },
|
|
7927
7928
|
...addSpanAttributesOptions.isPreAppStart && { [TdSpanAttributes.IS_PRE_APP_START]: addSpanAttributesOptions.isPreAppStart },
|
|
7928
7929
|
...addSpanAttributesOptions.inputValue && { [TdSpanAttributes.INPUT_VALUE]: createSpanInputValue(addSpanAttributesOptions.inputValue) },
|
|
7929
|
-
...addSpanAttributesOptions.outputValue && { [TdSpanAttributes.OUTPUT_VALUE]:
|
|
7930
|
+
...addSpanAttributesOptions.outputValue && { [TdSpanAttributes.OUTPUT_VALUE]: safeJsonStringify(addSpanAttributesOptions.outputValue) },
|
|
7930
7931
|
...addSpanAttributesOptions.inputSchemaMerges && { [TdSpanAttributes.INPUT_SCHEMA_MERGES]: JSON.stringify(addSpanAttributesOptions.inputSchemaMerges) },
|
|
7931
7932
|
...addSpanAttributesOptions.outputSchemaMerges && { [TdSpanAttributes.OUTPUT_SCHEMA_MERGES]: JSON.stringify(addSpanAttributesOptions.outputSchemaMerges) },
|
|
7932
7933
|
...addSpanAttributesOptions.metadata && { [TdSpanAttributes.METADATA]: JSON.stringify(addSpanAttributesOptions.metadata) },
|
|
@@ -18773,6 +18774,11 @@ var IORedisInstrumentation = class extends TdInstrumentationBase {
|
|
|
18773
18774
|
if (self.mode === TuskDriftMode.REPLAY) return handleReplayMode({
|
|
18774
18775
|
noOpRequestHandler: () => {
|
|
18775
18776
|
process.nextTick(() => {
|
|
18777
|
+
this.status = "connecting";
|
|
18778
|
+
this.emit("connecting");
|
|
18779
|
+
this.status = "connect";
|
|
18780
|
+
this.emit("connect");
|
|
18781
|
+
this.status = "ready";
|
|
18776
18782
|
this.emit("ready");
|
|
18777
18783
|
});
|
|
18778
18784
|
return Promise.resolve();
|
|
@@ -18979,6 +18985,11 @@ var IORedisInstrumentation = class extends TdInstrumentationBase {
|
|
|
18979
18985
|
async _handleReplayConnect(thisContext) {
|
|
18980
18986
|
logger.debug(`[IORedisInstrumentation] Replaying IORedis connect`);
|
|
18981
18987
|
process.nextTick(() => {
|
|
18988
|
+
thisContext.status = "connecting";
|
|
18989
|
+
thisContext.emit("connecting");
|
|
18990
|
+
thisContext.status = "connect";
|
|
18991
|
+
thisContext.emit("connect");
|
|
18992
|
+
thisContext.status = "ready";
|
|
18982
18993
|
thisContext.emit("ready");
|
|
18983
18994
|
});
|
|
18984
18995
|
return Promise.resolve();
|
|
@@ -22279,11 +22290,34 @@ let PrismaErrorClassName = /* @__PURE__ */ function(PrismaErrorClassName$1) {
|
|
|
22279
22290
|
//#endregion
|
|
22280
22291
|
//#region src/instrumentation/libraries/prisma/Instrumentation.ts
|
|
22281
22292
|
var import_src$14 = /* @__PURE__ */ __toESM(require_src$7(), 1);
|
|
22282
|
-
|
|
22293
|
+
/**
|
|
22294
|
+
* Prisma instrumentation for recording and replaying database operations.
|
|
22295
|
+
*
|
|
22296
|
+
* ## Type deserialization
|
|
22297
|
+
*
|
|
22298
|
+
* Prisma returns special JS types (Date, BigInt, Decimal, Buffer) for certain
|
|
22299
|
+
* column types. JSON round-trip during record/replay loses this type information.
|
|
22300
|
+
* We reconstruct types during replay using two strategies:
|
|
22301
|
+
*
|
|
22302
|
+
* 1. **Model-based operations** (findFirst, create, update, etc.): The middleware
|
|
22303
|
+
* provides the model name, so we look up field types from `_runtimeDataModel`
|
|
22304
|
+
* at replay time. No extra metadata needed during recording.
|
|
22305
|
+
*
|
|
22306
|
+
* 2. **Raw queries** ($queryRaw): No model name is available. During recording,
|
|
22307
|
+
* we sniff JS types from the result and store a per-column `_tdTypeMap`
|
|
22308
|
+
* (e.g., `{bigNum: "bigint", data: "bytes"}`). During replay, we use this
|
|
22309
|
+
* map to reconstruct the values.
|
|
22310
|
+
*
|
|
22311
|
+
* Both strategies share `_reconstructSingleValue` for the actual conversion.
|
|
22312
|
+
* See docs/prisma-type-deserialization-bug.md for the full design doc.
|
|
22313
|
+
*/
|
|
22314
|
+
var PrismaInstrumentation = class PrismaInstrumentation extends TdInstrumentationBase {
|
|
22283
22315
|
constructor(config = {}) {
|
|
22284
22316
|
super("@prisma/client", config);
|
|
22285
22317
|
this.INSTRUMENTATION_NAME = "PrismaInstrumentation";
|
|
22286
22318
|
this.prismaErrorClasses = [];
|
|
22319
|
+
this.prismaClient = null;
|
|
22320
|
+
this.prismaNamespace = null;
|
|
22287
22321
|
this.mode = config.mode || TuskDriftMode.DISABLED;
|
|
22288
22322
|
this.tuskDrift = TuskDriftCore.getInstance();
|
|
22289
22323
|
}
|
|
@@ -22300,6 +22334,7 @@ var PrismaInstrumentation = class extends TdInstrumentationBase {
|
|
|
22300
22334
|
return prismaModule;
|
|
22301
22335
|
}
|
|
22302
22336
|
this._storePrismaErrorClasses(prismaModule);
|
|
22337
|
+
this.prismaNamespace = prismaModule.Prisma || prismaModule;
|
|
22303
22338
|
logger.debug(`[PrismaInstrumentation] Wrapping PrismaClient constructor`);
|
|
22304
22339
|
this._wrap(prismaModule, "PrismaClient", (OriginalPrismaClient) => {
|
|
22305
22340
|
const self = this;
|
|
@@ -22307,7 +22342,9 @@ var PrismaInstrumentation = class extends TdInstrumentationBase {
|
|
|
22307
22342
|
return class TdPrismaClient {
|
|
22308
22343
|
constructor(...args) {
|
|
22309
22344
|
logger.debug(`[PrismaInstrumentation] Creating patched PrismaClient instance`);
|
|
22310
|
-
|
|
22345
|
+
const prismaClient = new OriginalPrismaClient(...args);
|
|
22346
|
+
self.prismaClient = prismaClient;
|
|
22347
|
+
const extendedClient = prismaClient.$extends({ query: { async $allOperations({ model, operation, args: operationArgs, query }) {
|
|
22311
22348
|
logger.debug(`[PrismaInstrumentation] $allOperations intercepted: ${model}.${operation}`);
|
|
22312
22349
|
return self._handlePrismaOperation({
|
|
22313
22350
|
model,
|
|
@@ -22316,6 +22353,22 @@ var PrismaInstrumentation = class extends TdInstrumentationBase {
|
|
|
22316
22353
|
query
|
|
22317
22354
|
});
|
|
22318
22355
|
} } });
|
|
22356
|
+
if (self.mode === TuskDriftMode.REPLAY) {
|
|
22357
|
+
const originalTransaction = extendedClient.$transaction.bind(extendedClient);
|
|
22358
|
+
extendedClient.$transaction = async function(...txArgs) {
|
|
22359
|
+
const firstArg = txArgs[0];
|
|
22360
|
+
if (typeof firstArg === "function") {
|
|
22361
|
+
logger.debug(`[PrismaInstrumentation] Replay: bypassing interactive $transaction DB connection`);
|
|
22362
|
+
return firstArg(extendedClient);
|
|
22363
|
+
}
|
|
22364
|
+
if (Array.isArray(firstArg)) {
|
|
22365
|
+
logger.debug(`[PrismaInstrumentation] Replay: bypassing sequential $transaction DB connection`);
|
|
22366
|
+
return Promise.all(firstArg);
|
|
22367
|
+
}
|
|
22368
|
+
return originalTransaction(...txArgs);
|
|
22369
|
+
};
|
|
22370
|
+
}
|
|
22371
|
+
return extendedClient;
|
|
22319
22372
|
}
|
|
22320
22373
|
};
|
|
22321
22374
|
});
|
|
@@ -22372,7 +22425,7 @@ var PrismaInstrumentation = class extends TdInstrumentationBase {
|
|
|
22372
22425
|
inputValue,
|
|
22373
22426
|
isPreAppStart
|
|
22374
22427
|
}, (spanInfo) => {
|
|
22375
|
-
return this._handleRecordPrismaOperation(spanInfo, query, args);
|
|
22428
|
+
return this._handleRecordPrismaOperation(spanInfo, query, args, model);
|
|
22376
22429
|
});
|
|
22377
22430
|
},
|
|
22378
22431
|
spanKind: import_src$14.SpanKind.CLIENT
|
|
@@ -22399,13 +22452,15 @@ var PrismaInstrumentation = class extends TdInstrumentationBase {
|
|
|
22399
22452
|
});
|
|
22400
22453
|
} else return query(args);
|
|
22401
22454
|
}
|
|
22402
|
-
async _handleRecordPrismaOperation(spanInfo, query, args) {
|
|
22455
|
+
async _handleRecordPrismaOperation(spanInfo, query, args, model) {
|
|
22403
22456
|
try {
|
|
22404
22457
|
logger.debug(`[PrismaInstrumentation] Recording Prisma operation`);
|
|
22405
22458
|
const result = await query(args);
|
|
22459
|
+
const typeMap = model ? null : this._buildTypeMap(result);
|
|
22406
22460
|
const outputValue = {
|
|
22407
22461
|
prismaResult: result,
|
|
22408
|
-
_tdOriginalFormat: "result"
|
|
22462
|
+
_tdOriginalFormat: "result",
|
|
22463
|
+
...typeMap && { _tdTypeMap: typeMap }
|
|
22409
22464
|
};
|
|
22410
22465
|
try {
|
|
22411
22466
|
SpanUtils.addSpanAttributes(spanInfo.span, { outputValue });
|
|
@@ -22468,8 +22523,158 @@ var PrismaInstrumentation = class extends TdInstrumentationBase {
|
|
|
22468
22523
|
});
|
|
22469
22524
|
throw errorObj;
|
|
22470
22525
|
}
|
|
22526
|
+
let result = outputValue.prismaResult;
|
|
22527
|
+
try {
|
|
22528
|
+
if (inputValue.model) result = this._reconstructPrismaTypes(result, inputValue.model);
|
|
22529
|
+
else if (outputValue._tdTypeMap) result = this._reconstructFromTypeMap(result, outputValue._tdTypeMap);
|
|
22530
|
+
} catch (reconstructError) {
|
|
22531
|
+
logger.debug(`[PrismaInstrumentation] Failed to reconstruct types: ${reconstructError}`);
|
|
22532
|
+
}
|
|
22471
22533
|
SpanUtils.endSpan(spanInfo.span, { code: import_src$14.SpanStatusCode.OK });
|
|
22472
|
-
return
|
|
22534
|
+
return result;
|
|
22535
|
+
}
|
|
22536
|
+
/**
|
|
22537
|
+
* Sniff the JS type of a value returned by Prisma.
|
|
22538
|
+
*
|
|
22539
|
+
* Returns type names that mirror Prisma's internal `QueryIntrospectionBuiltinType`
|
|
22540
|
+
* enum from `@prisma/client/runtime` (used in `deserializeRawResults.ts`):
|
|
22541
|
+
* "bigint" | "bytes" | "decimal" | "datetime"
|
|
22542
|
+
*
|
|
22543
|
+
* We don't import these types directly — we use our own string literals that
|
|
22544
|
+
* match Prisma's naming convention so the type map is self-documenting and
|
|
22545
|
+
* consistent with what Prisma would produce internally.
|
|
22546
|
+
*/
|
|
22547
|
+
_sniffType(value) {
|
|
22548
|
+
if (typeof value === "bigint") return "bigint";
|
|
22549
|
+
if (value instanceof Date) return "datetime";
|
|
22550
|
+
if (Buffer.isBuffer(value) || value instanceof Uint8Array) return "bytes";
|
|
22551
|
+
if (typeof value === "object" && value !== null && typeof value.toFixed === "function" && typeof value.toExponential === "function" && typeof value.toSignificantDigits === "function") return "decimal";
|
|
22552
|
+
return null;
|
|
22553
|
+
}
|
|
22554
|
+
/**
|
|
22555
|
+
* Build a per-column type map by sniffing values in the result.
|
|
22556
|
+
* For arrays of objects (e.g., $queryRaw results), scans all rows to handle
|
|
22557
|
+
* cases where the first row has null values but later rows don't.
|
|
22558
|
+
* Returns null if no special types are detected.
|
|
22559
|
+
*/
|
|
22560
|
+
_buildTypeMap(result) {
|
|
22561
|
+
if (result === null || result === void 0) return null;
|
|
22562
|
+
const rows = Array.isArray(result) ? result : typeof result === "object" ? [result] : null;
|
|
22563
|
+
if (!rows || rows.length === 0) return null;
|
|
22564
|
+
const typeMap = {};
|
|
22565
|
+
let hasTypes = false;
|
|
22566
|
+
for (const row of rows) {
|
|
22567
|
+
if (!row || typeof row !== "object") continue;
|
|
22568
|
+
for (const [key, value] of Object.entries(row)) {
|
|
22569
|
+
if (typeMap[key] || value === null || value === void 0) continue;
|
|
22570
|
+
if (Array.isArray(value) && value.length > 0) {
|
|
22571
|
+
const elemType = this._sniffType(value[0]);
|
|
22572
|
+
if (elemType) {
|
|
22573
|
+
typeMap[key] = `${elemType}-array`;
|
|
22574
|
+
hasTypes = true;
|
|
22575
|
+
continue;
|
|
22576
|
+
}
|
|
22577
|
+
}
|
|
22578
|
+
const type = this._sniffType(value);
|
|
22579
|
+
if (type) {
|
|
22580
|
+
typeMap[key] = type;
|
|
22581
|
+
hasTypes = true;
|
|
22582
|
+
}
|
|
22583
|
+
}
|
|
22584
|
+
if (hasTypes && Object.keys(typeMap).length >= Object.keys(row).length) break;
|
|
22585
|
+
}
|
|
22586
|
+
return hasTypes ? typeMap : null;
|
|
22587
|
+
}
|
|
22588
|
+
/**
|
|
22589
|
+
* Reconstruct types from a _tdTypeMap (used for $queryRaw and other model-less operations).
|
|
22590
|
+
*/
|
|
22591
|
+
_reconstructFromTypeMap(result, typeMap) {
|
|
22592
|
+
if (result === null || result === void 0) return result;
|
|
22593
|
+
const reconstructRow = (row) => {
|
|
22594
|
+
if (typeof row !== "object" || row === null) return row;
|
|
22595
|
+
for (const [key, type] of Object.entries(typeMap)) {
|
|
22596
|
+
const value = row[key];
|
|
22597
|
+
if (value === null || value === void 0) continue;
|
|
22598
|
+
if (typeof type === "string" && type.endsWith("-array") && Array.isArray(value)) {
|
|
22599
|
+
const baseType = type.replace("-array", "");
|
|
22600
|
+
row[key] = value.map((v) => this._reconstructSingleValue(v, baseType));
|
|
22601
|
+
continue;
|
|
22602
|
+
}
|
|
22603
|
+
row[key] = this._reconstructSingleValue(value, type);
|
|
22604
|
+
}
|
|
22605
|
+
return row;
|
|
22606
|
+
};
|
|
22607
|
+
if (Array.isArray(result)) return result.map(reconstructRow);
|
|
22608
|
+
return reconstructRow(result);
|
|
22609
|
+
}
|
|
22610
|
+
/**
|
|
22611
|
+
* Reconstruct a single value from its JSON-deserialized form back to the
|
|
22612
|
+
* original JS type that Prisma would have returned.
|
|
22613
|
+
*
|
|
22614
|
+
* The `type` parameter uses the same names as Prisma's query engine types
|
|
22615
|
+
* (see _sniffType and PRISMA_SCHEMA_TO_ENGINE_TYPE). Both the model-based
|
|
22616
|
+
* and raw-query replay paths converge here.
|
|
22617
|
+
*/
|
|
22618
|
+
_reconstructSingleValue(value, type) {
|
|
22619
|
+
if (value === null || value === void 0) return value;
|
|
22620
|
+
switch (type) {
|
|
22621
|
+
case "bigint":
|
|
22622
|
+
if (typeof value === "string" || typeof value === "number") return BigInt(value);
|
|
22623
|
+
return value;
|
|
22624
|
+
case "datetime":
|
|
22625
|
+
if (typeof value === "string") return new Date(value);
|
|
22626
|
+
return value;
|
|
22627
|
+
case "decimal":
|
|
22628
|
+
if (typeof value === "string" || typeof value === "number") {
|
|
22629
|
+
if (this.prismaNamespace?.Decimal) return new this.prismaNamespace.Decimal(value);
|
|
22630
|
+
const decimalValue = String(value);
|
|
22631
|
+
return {
|
|
22632
|
+
toString: () => decimalValue,
|
|
22633
|
+
toFixed: (dp) => Number(decimalValue).toFixed(dp),
|
|
22634
|
+
valueOf: () => Number(decimalValue),
|
|
22635
|
+
[Symbol.toPrimitive]: (hint) => hint === "string" ? decimalValue : Number(decimalValue)
|
|
22636
|
+
};
|
|
22637
|
+
}
|
|
22638
|
+
return value;
|
|
22639
|
+
case "bytes":
|
|
22640
|
+
if (typeof value === "string") return Buffer.from(value, "base64");
|
|
22641
|
+
if (typeof value === "object" && !Buffer.isBuffer(value)) {
|
|
22642
|
+
const bufferData = value.data || Object.values(value);
|
|
22643
|
+
return Buffer.from(bufferData);
|
|
22644
|
+
}
|
|
22645
|
+
return value;
|
|
22646
|
+
default: return value;
|
|
22647
|
+
}
|
|
22648
|
+
}
|
|
22649
|
+
/**
|
|
22650
|
+
* Reconstruct Prisma types for model-based operations (findFirst, create, update, etc.).
|
|
22651
|
+
*
|
|
22652
|
+
* Uses `prismaClient._runtimeDataModel` — Prisma's internal schema representation
|
|
22653
|
+
* available at runtime — to determine field types. This avoids needing to store
|
|
22654
|
+
* type metadata during recording; the schema itself is the source of truth.
|
|
22655
|
+
*
|
|
22656
|
+
* Handles scalar fields, array fields (e.g., BigInt[]), and nested relations recursively.
|
|
22657
|
+
*/
|
|
22658
|
+
_reconstructPrismaTypes(result, modelName) {
|
|
22659
|
+
if (result === null || result === void 0) return result;
|
|
22660
|
+
const runtimeDataModel = this.prismaClient?._runtimeDataModel;
|
|
22661
|
+
if (!runtimeDataModel) return result;
|
|
22662
|
+
if (Array.isArray(result)) return result.map((item) => this._reconstructPrismaTypes(item, modelName));
|
|
22663
|
+
const model = runtimeDataModel.models[modelName];
|
|
22664
|
+
if (!model) return result;
|
|
22665
|
+
if (typeof result !== "object") return result;
|
|
22666
|
+
const fieldTypeMap = new Map(model.fields.map((f) => [f.name, f]));
|
|
22667
|
+
for (const [key, value] of Object.entries(result)) {
|
|
22668
|
+
const field = fieldTypeMap.get(key);
|
|
22669
|
+
if (!field || value === null || value === void 0) continue;
|
|
22670
|
+
if (field.kind === "scalar") {
|
|
22671
|
+
const engineType = PrismaInstrumentation.PRISMA_SCHEMA_TO_ENGINE_TYPE[field.type];
|
|
22672
|
+
if (engineType) if (Array.isArray(value)) result[key] = value.map((v) => this._reconstructSingleValue(v, engineType));
|
|
22673
|
+
else result[key] = this._reconstructSingleValue(value, engineType);
|
|
22674
|
+
}
|
|
22675
|
+
if (field.kind === "object" && field.type && typeof value === "object") result[key] = this._reconstructPrismaTypes(value, field.type);
|
|
22676
|
+
}
|
|
22677
|
+
return result;
|
|
22473
22678
|
}
|
|
22474
22679
|
_getPrismaErrorClassName(error) {
|
|
22475
22680
|
for (const errorInfo of this.prismaErrorClasses) if (error instanceof errorInfo.errorClass) return errorInfo.name;
|
|
@@ -22494,6 +22699,70 @@ var PrismaInstrumentation = class extends TdInstrumentationBase {
|
|
|
22494
22699
|
wrap(target, propertyName, wrapper);
|
|
22495
22700
|
}
|
|
22496
22701
|
};
|
|
22702
|
+
PrismaInstrumentation.PRISMA_SCHEMA_TO_ENGINE_TYPE = {
|
|
22703
|
+
DateTime: "datetime",
|
|
22704
|
+
BigInt: "bigint",
|
|
22705
|
+
Decimal: "decimal",
|
|
22706
|
+
Bytes: "bytes"
|
|
22707
|
+
};
|
|
22708
|
+
|
|
22709
|
+
//#endregion
|
|
22710
|
+
//#region src/instrumentation/libraries/mongodb/mocks/FakeTopology.ts
|
|
22711
|
+
/**
|
|
22712
|
+
* Fake MongoDB Topology for replay mode.
|
|
22713
|
+
*
|
|
22714
|
+
* When BulkOperationBase is constructed, it calls getTopology(collection)
|
|
22715
|
+
* which requires a connected topology. In replay mode, no real connection
|
|
22716
|
+
* exists, so we inject this fake topology to satisfy the constructor's
|
|
22717
|
+
* requirements without hitting a real server.
|
|
22718
|
+
*
|
|
22719
|
+
* The constructor reads:
|
|
22720
|
+
* - topology.lastHello() -> returns {} so all size limits use defaults
|
|
22721
|
+
* - topology.lastIsMaster() -> returns {} (legacy compat)
|
|
22722
|
+
* - topology.s.options -> returns {} so autoEncryption check is false
|
|
22723
|
+
*
|
|
22724
|
+
* The topology is also injected onto the shared MongoClient (via
|
|
22725
|
+
* _injectFakeTopology) and persists across requests. Other driver code
|
|
22726
|
+
* (e.g. ClientSession.loadBalanced getter) accesses
|
|
22727
|
+
* topology.description.type, so we provide a minimal description to
|
|
22728
|
+
* prevent TypeError when the property is read.
|
|
22729
|
+
*/
|
|
22730
|
+
var TdFakeTopology = class extends events.EventEmitter {
|
|
22731
|
+
constructor() {
|
|
22732
|
+
super();
|
|
22733
|
+
this.fakeServer = { description: { address: "fake:27017" } };
|
|
22734
|
+
this.s = { options: {} };
|
|
22735
|
+
this.description = {
|
|
22736
|
+
type: "Unknown",
|
|
22737
|
+
servers: /* @__PURE__ */ new Map(),
|
|
22738
|
+
hasServer: () => false
|
|
22739
|
+
};
|
|
22740
|
+
}
|
|
22741
|
+
lastHello() {
|
|
22742
|
+
return {};
|
|
22743
|
+
}
|
|
22744
|
+
lastIsMaster() {
|
|
22745
|
+
return {};
|
|
22746
|
+
}
|
|
22747
|
+
shouldCheckForSessionSupport() {
|
|
22748
|
+
return false;
|
|
22749
|
+
}
|
|
22750
|
+
hasSessionSupport() {
|
|
22751
|
+
return true;
|
|
22752
|
+
}
|
|
22753
|
+
isConnected() {
|
|
22754
|
+
return true;
|
|
22755
|
+
}
|
|
22756
|
+
isDestroyed() {
|
|
22757
|
+
return false;
|
|
22758
|
+
}
|
|
22759
|
+
selectServerAsync() {
|
|
22760
|
+
return Promise.resolve(this.fakeServer);
|
|
22761
|
+
}
|
|
22762
|
+
selectServer(_selector, _options, callback) {
|
|
22763
|
+
if (typeof callback === "function") callback(null, this.fakeServer);
|
|
22764
|
+
}
|
|
22765
|
+
};
|
|
22497
22766
|
|
|
22498
22767
|
//#endregion
|
|
22499
22768
|
//#region src/instrumentation/libraries/mongodb/handlers/ConnectionHandler.ts
|
|
@@ -22528,7 +22797,7 @@ var ConnectionHandler = class {
|
|
|
22528
22797
|
inputValue,
|
|
22529
22798
|
isPreAppStart: !this.isAppReady()
|
|
22530
22799
|
}, (spanInfo) => {
|
|
22531
|
-
return this.handleReplayConnect(spanInfo, thisArg);
|
|
22800
|
+
return this.handleReplayConnect(spanInfo, thisArg, args);
|
|
22532
22801
|
});
|
|
22533
22802
|
}
|
|
22534
22803
|
});
|
|
@@ -22559,17 +22828,21 @@ var ConnectionHandler = class {
|
|
|
22559
22828
|
* - DISABLED: Passthrough.
|
|
22560
22829
|
*/
|
|
22561
22830
|
handleClose(original, thisArg, args) {
|
|
22562
|
-
if (this.mode === TuskDriftMode.REPLAY)
|
|
22563
|
-
|
|
22564
|
-
|
|
22565
|
-
|
|
22566
|
-
|
|
22567
|
-
|
|
22568
|
-
|
|
22569
|
-
|
|
22570
|
-
|
|
22571
|
-
|
|
22572
|
-
|
|
22831
|
+
if (this.mode === TuskDriftMode.REPLAY) {
|
|
22832
|
+
const callback = args.length > 0 && typeof args[args.length - 1] === "function" ? args[args.length - 1] : void 0;
|
|
22833
|
+
return handleReplayMode({
|
|
22834
|
+
noOpRequestHandler: () => {
|
|
22835
|
+
if (callback) callback();
|
|
22836
|
+
return Promise.resolve();
|
|
22837
|
+
},
|
|
22838
|
+
isServerRequest: false,
|
|
22839
|
+
replayModeHandler: () => {
|
|
22840
|
+
logger.debug(`[${this.instrumentationName}] Replaying MongoDB close (no-op)`);
|
|
22841
|
+
if (callback) callback();
|
|
22842
|
+
return Promise.resolve();
|
|
22843
|
+
}
|
|
22844
|
+
});
|
|
22845
|
+
} else if (this.mode === TuskDriftMode.RECORD) return handleRecordMode({
|
|
22573
22846
|
originalFunctionCall: () => original.apply(thisArg, args),
|
|
22574
22847
|
recordModeHandler: ({ isPreAppStart }) => {
|
|
22575
22848
|
return SpanUtils.createAndExecuteSpan(this.mode, () => original.apply(thisArg, args), {
|
|
@@ -22599,7 +22872,18 @@ var ConnectionHandler = class {
|
|
|
22599
22872
|
return original.apply(thisArg, args);
|
|
22600
22873
|
}
|
|
22601
22874
|
handleRecordConnect(spanInfo, original, thisArg, args) {
|
|
22602
|
-
|
|
22875
|
+
const connectResult = original.apply(thisArg, args);
|
|
22876
|
+
if (!(connectResult && typeof connectResult.then === "function")) {
|
|
22877
|
+
try {
|
|
22878
|
+
logger.debug(`[${this.instrumentationName}] MongoDB connect returned a non-promise value; recording best-effort span`);
|
|
22879
|
+
SpanUtils.addSpanAttributes(spanInfo.span, { outputValue: { connected: true } });
|
|
22880
|
+
SpanUtils.endSpan(spanInfo.span, { code: import_src$13.SpanStatusCode.OK });
|
|
22881
|
+
} catch (error) {
|
|
22882
|
+
logger.error(`[${this.instrumentationName}] Error ending non-promise connect span:`, error);
|
|
22883
|
+
}
|
|
22884
|
+
return Promise.resolve(connectResult);
|
|
22885
|
+
}
|
|
22886
|
+
return connectResult.then((result) => {
|
|
22603
22887
|
try {
|
|
22604
22888
|
logger.debug(`[${this.instrumentationName}] MongoDB connection created successfully (${SpanUtils.getTraceInfo()})`);
|
|
22605
22889
|
SpanUtils.addSpanAttributes(spanInfo.span, { outputValue: { connected: true } });
|
|
@@ -22622,8 +22906,9 @@ var ConnectionHandler = class {
|
|
|
22622
22906
|
throw error;
|
|
22623
22907
|
});
|
|
22624
22908
|
}
|
|
22625
|
-
handleReplayConnect(spanInfo, thisArg) {
|
|
22909
|
+
handleReplayConnect(spanInfo, thisArg, args) {
|
|
22626
22910
|
logger.debug(`[${this.instrumentationName}] Replaying MongoDB connection (skipping TCP connect)`);
|
|
22911
|
+
this.injectFakeTopology(thisArg);
|
|
22627
22912
|
try {
|
|
22628
22913
|
SpanUtils.addSpanAttributes(spanInfo.span, { outputValue: {
|
|
22629
22914
|
connected: true,
|
|
@@ -22633,10 +22918,35 @@ var ConnectionHandler = class {
|
|
|
22633
22918
|
} catch (error) {
|
|
22634
22919
|
logger.error(`[${this.instrumentationName}] Error ending replay connect span:`, error);
|
|
22635
22920
|
}
|
|
22921
|
+
const callback = args.length > 0 && typeof args[args.length - 1] === "function" ? args[args.length - 1] : void 0;
|
|
22922
|
+
if (callback) {
|
|
22923
|
+
callback(null, thisArg);
|
|
22924
|
+
return Promise.resolve(thisArg);
|
|
22925
|
+
}
|
|
22636
22926
|
return Promise.resolve(thisArg);
|
|
22637
22927
|
}
|
|
22928
|
+
injectFakeTopology(client) {
|
|
22929
|
+
const fakeTopology = new TdFakeTopology();
|
|
22930
|
+
if (!client) return;
|
|
22931
|
+
if (!client.topology) client.topology = fakeTopology;
|
|
22932
|
+
if (client.s && !client.s.topology) client.s.topology = fakeTopology;
|
|
22933
|
+
if (client.s) {
|
|
22934
|
+
if (client.s.hasBeenClosed === true) client.s.hasBeenClosed = false;
|
|
22935
|
+
if (client.s.isMongoClient === false) client.s.isMongoClient = true;
|
|
22936
|
+
}
|
|
22937
|
+
}
|
|
22638
22938
|
handleRecordClose(spanInfo, original, thisArg, args) {
|
|
22639
|
-
|
|
22939
|
+
const closeResult = original.apply(thisArg, args);
|
|
22940
|
+
if (!(closeResult && typeof closeResult.then === "function")) {
|
|
22941
|
+
try {
|
|
22942
|
+
SpanUtils.addSpanAttributes(spanInfo.span, { outputValue: { closed: true } });
|
|
22943
|
+
SpanUtils.endSpan(spanInfo.span, { code: import_src$13.SpanStatusCode.OK });
|
|
22944
|
+
} catch (error) {
|
|
22945
|
+
logger.error(`[${this.instrumentationName}] Error ending non-promise close span:`, error);
|
|
22946
|
+
}
|
|
22947
|
+
return Promise.resolve();
|
|
22948
|
+
}
|
|
22949
|
+
return closeResult.then(() => {
|
|
22640
22950
|
try {
|
|
22641
22951
|
logger.debug(`[${this.instrumentationName}] MongoDB connection closed successfully (${SpanUtils.getTraceInfo()})`);
|
|
22642
22952
|
SpanUtils.addSpanAttributes(spanInfo.span, { outputValue: { closed: true } });
|
|
@@ -22717,21 +23027,37 @@ var TdFakeFindCursor = class TdFakeFindCursor {
|
|
|
22717
23027
|
});
|
|
22718
23028
|
if (this._mockLoadPromise) await this._mockLoadPromise;
|
|
22719
23029
|
}
|
|
22720
|
-
|
|
22721
|
-
|
|
22722
|
-
|
|
23030
|
+
toArray(callback) {
|
|
23031
|
+
const promise = this._ensureMockLoaded().then(() => [...this.documents]);
|
|
23032
|
+
if (typeof callback === "function") {
|
|
23033
|
+
promise.then((docs) => callback(null, docs), (error) => callback(error));
|
|
23034
|
+
return;
|
|
23035
|
+
}
|
|
23036
|
+
return promise;
|
|
22723
23037
|
}
|
|
22724
|
-
|
|
22725
|
-
|
|
22726
|
-
|
|
23038
|
+
next(callback) {
|
|
23039
|
+
const promise = this._ensureMockLoaded().then(() => this.documents[this.index++] ?? null);
|
|
23040
|
+
if (typeof callback === "function") {
|
|
23041
|
+
promise.then((doc) => callback(null, doc), (error) => callback(error));
|
|
23042
|
+
return;
|
|
23043
|
+
}
|
|
23044
|
+
return promise;
|
|
22727
23045
|
}
|
|
22728
|
-
|
|
22729
|
-
|
|
22730
|
-
|
|
23046
|
+
tryNext(callback) {
|
|
23047
|
+
const promise = this._ensureMockLoaded().then(() => this.documents[this.index++] ?? null);
|
|
23048
|
+
if (typeof callback === "function") {
|
|
23049
|
+
promise.then((doc) => callback(null, doc), (error) => callback(error));
|
|
23050
|
+
return;
|
|
23051
|
+
}
|
|
23052
|
+
return promise;
|
|
22731
23053
|
}
|
|
22732
|
-
|
|
22733
|
-
|
|
22734
|
-
|
|
23054
|
+
hasNext(callback) {
|
|
23055
|
+
const promise = this._ensureMockLoaded().then(() => this.index < this.documents.length);
|
|
23056
|
+
if (typeof callback === "function") {
|
|
23057
|
+
promise.then((hasNext) => callback(null, hasNext), (error) => callback(error));
|
|
23058
|
+
return;
|
|
23059
|
+
}
|
|
23060
|
+
return promise;
|
|
22735
23061
|
}
|
|
22736
23062
|
async forEach(fn) {
|
|
22737
23063
|
await this._ensureMockLoaded();
|
|
@@ -22804,7 +23130,14 @@ var TdFakeFindCursor = class TdFakeFindCursor {
|
|
|
22804
23130
|
map() {
|
|
22805
23131
|
return this;
|
|
22806
23132
|
}
|
|
22807
|
-
|
|
23133
|
+
close(callback) {
|
|
23134
|
+
const promise = Promise.resolve();
|
|
23135
|
+
if (typeof callback === "function") {
|
|
23136
|
+
promise.then(() => callback(), (error) => callback(error));
|
|
23137
|
+
return;
|
|
23138
|
+
}
|
|
23139
|
+
return promise;
|
|
23140
|
+
}
|
|
22808
23141
|
rewind() {
|
|
22809
23142
|
this.index = 0;
|
|
22810
23143
|
}
|
|
@@ -22922,45 +23255,6 @@ var TdFakeChangeStream = class {
|
|
|
22922
23255
|
}
|
|
22923
23256
|
};
|
|
22924
23257
|
|
|
22925
|
-
//#endregion
|
|
22926
|
-
//#region src/instrumentation/libraries/mongodb/mocks/FakeTopology.ts
|
|
22927
|
-
/**
|
|
22928
|
-
* Fake MongoDB Topology for replay mode.
|
|
22929
|
-
*
|
|
22930
|
-
* When BulkOperationBase is constructed, it calls getTopology(collection)
|
|
22931
|
-
* which requires a connected topology. In replay mode, no real connection
|
|
22932
|
-
* exists, so we inject this fake topology to satisfy the constructor's
|
|
22933
|
-
* requirements without hitting a real server.
|
|
22934
|
-
*
|
|
22935
|
-
* The constructor reads:
|
|
22936
|
-
* - topology.lastHello() -> returns {} so all size limits use defaults
|
|
22937
|
-
* - topology.lastIsMaster() -> returns {} (legacy compat)
|
|
22938
|
-
* - topology.s.options -> returns {} so autoEncryption check is false
|
|
22939
|
-
*
|
|
22940
|
-
* The topology is also injected onto the shared MongoClient (via
|
|
22941
|
-
* _injectFakeTopology) and persists across requests. Other driver code
|
|
22942
|
-
* (e.g. ClientSession.loadBalanced getter) accesses
|
|
22943
|
-
* topology.description.type, so we provide a minimal description to
|
|
22944
|
-
* prevent TypeError when the property is read.
|
|
22945
|
-
*/
|
|
22946
|
-
var TdFakeTopology = class extends events.EventEmitter {
|
|
22947
|
-
constructor() {
|
|
22948
|
-
super();
|
|
22949
|
-
this.s = { options: {} };
|
|
22950
|
-
this.description = {
|
|
22951
|
-
type: "Unknown",
|
|
22952
|
-
servers: /* @__PURE__ */ new Map(),
|
|
22953
|
-
hasServer: () => false
|
|
22954
|
-
};
|
|
22955
|
-
}
|
|
22956
|
-
lastHello() {
|
|
22957
|
-
return {};
|
|
22958
|
-
}
|
|
22959
|
-
lastIsMaster() {
|
|
22960
|
-
return {};
|
|
22961
|
-
}
|
|
22962
|
-
};
|
|
22963
|
-
|
|
22964
23258
|
//#endregion
|
|
22965
23259
|
//#region node_modules/bson/lib/bson.cjs
|
|
22966
23260
|
var require_bson = /* @__PURE__ */ __commonJS({ "node_modules/bson/lib/bson.cjs": ((exports) => {
|
|
@@ -27010,7 +27304,8 @@ const COLLECTION_METHODS_TO_WRAP = [
|
|
|
27010
27304
|
"createIndexes",
|
|
27011
27305
|
"dropIndex",
|
|
27012
27306
|
"dropIndexes",
|
|
27013
|
-
"indexes"
|
|
27307
|
+
"indexes",
|
|
27308
|
+
"drop"
|
|
27014
27309
|
];
|
|
27015
27310
|
/**
|
|
27016
27311
|
* Cursor-returning methods on Collection.prototype.
|
|
@@ -27035,6 +27330,12 @@ const DB_METHODS_TO_WRAP = [
|
|
|
27035
27330
|
* Db.prototype methods that return cursors.
|
|
27036
27331
|
*/
|
|
27037
27332
|
const DB_CURSOR_METHODS_TO_WRAP = ["listCollections", "aggregate"];
|
|
27333
|
+
const SUPPORTED_MONGODB_VERSIONS = [
|
|
27334
|
+
"4.*",
|
|
27335
|
+
"5.*",
|
|
27336
|
+
"6.*",
|
|
27337
|
+
"7.*"
|
|
27338
|
+
];
|
|
27038
27339
|
var MongodbInstrumentation = class extends TdInstrumentationBase {
|
|
27039
27340
|
constructor(config = {}) {
|
|
27040
27341
|
super("mongodb", config);
|
|
@@ -27046,38 +27347,37 @@ var MongodbInstrumentation = class extends TdInstrumentationBase {
|
|
|
27046
27347
|
init() {
|
|
27047
27348
|
return [new TdInstrumentationNodeModule({
|
|
27048
27349
|
name: "mongodb",
|
|
27049
|
-
supportedVersions: [
|
|
27050
|
-
|
|
27051
|
-
"6.*",
|
|
27052
|
-
"7.*"
|
|
27053
|
-
],
|
|
27054
|
-
patch: (moduleExports) => this._patchMongodbModule(moduleExports),
|
|
27350
|
+
supportedVersions: [...SUPPORTED_MONGODB_VERSIONS],
|
|
27351
|
+
patch: (moduleExports, moduleVersion) => this._patchMongodbModule(moduleExports, moduleVersion),
|
|
27055
27352
|
files: [
|
|
27056
27353
|
new TdInstrumentationNodeModuleFile({
|
|
27057
27354
|
name: "mongodb/lib/sessions.js",
|
|
27058
|
-
supportedVersions: [
|
|
27059
|
-
|
|
27060
|
-
|
|
27061
|
-
|
|
27062
|
-
|
|
27355
|
+
supportedVersions: [...SUPPORTED_MONGODB_VERSIONS],
|
|
27356
|
+
patch: (moduleExports) => this._patchSessionModule(moduleExports)
|
|
27357
|
+
}),
|
|
27358
|
+
new TdInstrumentationNodeModuleFile({
|
|
27359
|
+
name: "mongodb/lib/sessions",
|
|
27360
|
+
supportedVersions: [...SUPPORTED_MONGODB_VERSIONS],
|
|
27063
27361
|
patch: (moduleExports) => this._patchSessionModule(moduleExports)
|
|
27064
27362
|
}),
|
|
27065
27363
|
new TdInstrumentationNodeModuleFile({
|
|
27066
27364
|
name: "mongodb/lib/bulk/ordered.js",
|
|
27067
|
-
supportedVersions: [
|
|
27068
|
-
|
|
27069
|
-
|
|
27070
|
-
|
|
27071
|
-
|
|
27365
|
+
supportedVersions: [...SUPPORTED_MONGODB_VERSIONS],
|
|
27366
|
+
patch: (moduleExports) => this._patchOrderedBulkModule(moduleExports)
|
|
27367
|
+
}),
|
|
27368
|
+
new TdInstrumentationNodeModuleFile({
|
|
27369
|
+
name: "mongodb/lib/bulk/ordered",
|
|
27370
|
+
supportedVersions: [...SUPPORTED_MONGODB_VERSIONS],
|
|
27072
27371
|
patch: (moduleExports) => this._patchOrderedBulkModule(moduleExports)
|
|
27073
27372
|
}),
|
|
27074
27373
|
new TdInstrumentationNodeModuleFile({
|
|
27075
27374
|
name: "mongodb/lib/bulk/unordered.js",
|
|
27076
|
-
supportedVersions: [
|
|
27077
|
-
|
|
27078
|
-
|
|
27079
|
-
|
|
27080
|
-
|
|
27375
|
+
supportedVersions: [...SUPPORTED_MONGODB_VERSIONS],
|
|
27376
|
+
patch: (moduleExports) => this._patchUnorderedBulkModule(moduleExports)
|
|
27377
|
+
}),
|
|
27378
|
+
new TdInstrumentationNodeModuleFile({
|
|
27379
|
+
name: "mongodb/lib/bulk/unordered",
|
|
27380
|
+
supportedVersions: [...SUPPORTED_MONGODB_VERSIONS],
|
|
27081
27381
|
patch: (moduleExports) => this._patchUnorderedBulkModule(moduleExports)
|
|
27082
27382
|
})
|
|
27083
27383
|
]
|
|
@@ -27088,8 +27388,9 @@ var MongodbInstrumentation = class extends TdInstrumentationBase {
|
|
|
27088
27388
|
* Wraps MongoClient, Collection, Db, and cursor prototypes to intercept
|
|
27089
27389
|
* all database operations for record/replay.
|
|
27090
27390
|
*/
|
|
27091
|
-
_patchMongodbModule(mongodbModule) {
|
|
27092
|
-
|
|
27391
|
+
_patchMongodbModule(mongodbModule, moduleVersion) {
|
|
27392
|
+
this.mongodbMajorVersion = this._getMajorVersion(moduleVersion);
|
|
27393
|
+
logger.debug(`[${this.INSTRUMENTATION_NAME}] Patching MongoDB module v${moduleVersion || "unknown"} in ${this.mode} mode`);
|
|
27093
27394
|
if (this.isModulePatched(mongodbModule)) {
|
|
27094
27395
|
logger.debug(`[${this.INSTRUMENTATION_NAME}] MongoDB module already patched, skipping`);
|
|
27095
27396
|
return mongodbModule;
|
|
@@ -27156,6 +27457,11 @@ var MongodbInstrumentation = class extends TdInstrumentationBase {
|
|
|
27156
27457
|
logger.debug(`[${this.INSTRUMENTATION_NAME}] MongoDB module patching complete`);
|
|
27157
27458
|
return mongodbModule;
|
|
27158
27459
|
}
|
|
27460
|
+
_getMajorVersion(version$1) {
|
|
27461
|
+
if (!version$1) return void 0;
|
|
27462
|
+
const major$1 = Number(version$1.split(".")[0]);
|
|
27463
|
+
return Number.isFinite(major$1) ? major$1 : void 0;
|
|
27464
|
+
}
|
|
27159
27465
|
/**
|
|
27160
27466
|
* Patch all Collection.prototype CRUD methods that return Promises.
|
|
27161
27467
|
* Guards each method with typeof check for version compatibility.
|
|
@@ -27181,13 +27487,24 @@ var MongodbInstrumentation = class extends TdInstrumentationBase {
|
|
|
27181
27487
|
const submodule = `collection-${methodName}`;
|
|
27182
27488
|
return (original) => {
|
|
27183
27489
|
return function(...args) {
|
|
27490
|
+
const callback = args.length > 0 && typeof args[args.length - 1] === "function" ? args[args.length - 1] : void 0;
|
|
27491
|
+
const effectiveArgs = callback ? args.slice(0, -1) : args;
|
|
27184
27492
|
const collectionName = this?.s?.namespace?.collection;
|
|
27185
27493
|
const databaseName = this?.s?.namespace?.db;
|
|
27186
|
-
const inputValue = self._extractCollectionInput(methodName, collectionName, databaseName,
|
|
27494
|
+
const inputValue = self._extractCollectionInput(methodName, collectionName, databaseName, effectiveArgs);
|
|
27187
27495
|
if (self.mode === TuskDriftMode.DISABLED) return original.apply(this, args);
|
|
27188
|
-
|
|
27189
|
-
|
|
27190
|
-
|
|
27496
|
+
const invokeHandler = () => {
|
|
27497
|
+
if (self.mode === TuskDriftMode.RECORD) return self._handleRecordCollectionMethod(original, this, effectiveArgs, inputValue, spanName, submodule);
|
|
27498
|
+
if (self.mode === TuskDriftMode.REPLAY) return self._handleReplayCollectionMethod(original, this, effectiveArgs, inputValue, spanName, submodule, methodName);
|
|
27499
|
+
return original.apply(this, effectiveArgs);
|
|
27500
|
+
};
|
|
27501
|
+
if (callback) {
|
|
27502
|
+
Promise.resolve(invokeHandler()).then((result) => callback(null, result)).catch((error) => callback(error));
|
|
27503
|
+
return;
|
|
27504
|
+
}
|
|
27505
|
+
if (self.mode === TuskDriftMode.RECORD) return invokeHandler();
|
|
27506
|
+
if (self.mode === TuskDriftMode.REPLAY) return invokeHandler();
|
|
27507
|
+
return original.apply(this, effectiveArgs);
|
|
27191
27508
|
};
|
|
27192
27509
|
};
|
|
27193
27510
|
}
|
|
@@ -27210,7 +27527,8 @@ var MongodbInstrumentation = class extends TdInstrumentationBase {
|
|
|
27210
27527
|
isPreAppStart,
|
|
27211
27528
|
stopRecordingChildSpans: true
|
|
27212
27529
|
}, (spanInfo) => {
|
|
27213
|
-
|
|
27530
|
+
const resultPromise = original.apply(thisArg, args);
|
|
27531
|
+
return Promise.resolve(resultPromise).then((result) => {
|
|
27214
27532
|
try {
|
|
27215
27533
|
addOutputAttributesToSpan(spanInfo, wrapDirectOutput(result));
|
|
27216
27534
|
SpanUtils.endSpan(spanInfo.span, { code: import_src$12.SpanStatusCode.OK });
|
|
@@ -27247,7 +27565,7 @@ var MongodbInstrumentation = class extends TdInstrumentationBase {
|
|
|
27247
27565
|
},
|
|
27248
27566
|
isServerRequest: false,
|
|
27249
27567
|
replayModeHandler: () => {
|
|
27250
|
-
return SpanUtils.createAndExecuteSpan(this.mode, () =>
|
|
27568
|
+
return SpanUtils.createAndExecuteSpan(this.mode, () => Promise.resolve(this._getNoOpResult(methodName)), {
|
|
27251
27569
|
name: spanName,
|
|
27252
27570
|
kind: import_src$12.SpanKind.CLIENT,
|
|
27253
27571
|
submodule,
|
|
@@ -27402,10 +27720,16 @@ var MongodbInstrumentation = class extends TdInstrumentationBase {
|
|
|
27402
27720
|
};
|
|
27403
27721
|
if (typeof cursor.toArray === "function") {
|
|
27404
27722
|
const originalToArray = cursor.toArray.bind(cursor);
|
|
27405
|
-
cursor.toArray = () => {
|
|
27406
|
-
if (cursorState.recorded)
|
|
27723
|
+
cursor.toArray = (callback) => {
|
|
27724
|
+
if (cursorState.recorded) {
|
|
27725
|
+
if (typeof callback === "function") {
|
|
27726
|
+
originalToArray().then((result) => callback(null, result)).catch((error) => callback(error));
|
|
27727
|
+
return;
|
|
27728
|
+
}
|
|
27729
|
+
return originalToArray();
|
|
27730
|
+
}
|
|
27407
27731
|
cursorState.recorded = true;
|
|
27408
|
-
|
|
27732
|
+
const resultPromise = import_src$12.context.with(creationContext, () => {
|
|
27409
27733
|
return handleRecordMode({
|
|
27410
27734
|
originalFunctionCall: () => originalToArray(),
|
|
27411
27735
|
recordModeHandler: ({ isPreAppStart }) => {
|
|
@@ -27445,37 +27769,125 @@ var MongodbInstrumentation = class extends TdInstrumentationBase {
|
|
|
27445
27769
|
spanKind: import_src$12.SpanKind.CLIENT
|
|
27446
27770
|
});
|
|
27447
27771
|
});
|
|
27772
|
+
if (typeof callback === "function") {
|
|
27773
|
+
resultPromise.then((result) => callback(null, result)).catch((error) => callback(error));
|
|
27774
|
+
return;
|
|
27775
|
+
}
|
|
27776
|
+
return resultPromise;
|
|
27448
27777
|
};
|
|
27449
27778
|
}
|
|
27450
27779
|
if (typeof cursor.next === "function") {
|
|
27451
27780
|
const originalNext = cursor.next.bind(cursor);
|
|
27452
|
-
cursor.next =
|
|
27453
|
-
if (
|
|
27454
|
-
|
|
27455
|
-
|
|
27456
|
-
|
|
27781
|
+
cursor.next = (callback) => {
|
|
27782
|
+
if (typeof callback === "function") {
|
|
27783
|
+
if (cursorState.recorded) {
|
|
27784
|
+
let settled$1 = false;
|
|
27785
|
+
const finish$1 = (error, doc) => {
|
|
27786
|
+
if (settled$1) return;
|
|
27787
|
+
settled$1 = true;
|
|
27788
|
+
callback(error, doc);
|
|
27789
|
+
};
|
|
27790
|
+
const maybeResult$1 = originalNext((error, doc) => finish$1(error, doc));
|
|
27791
|
+
Promise.resolve(maybeResult$1).then((doc) => finish$1(null, doc)).catch((error) => finish$1(error, null));
|
|
27792
|
+
return;
|
|
27793
|
+
}
|
|
27794
|
+
if (!import_src$12.context.with(creationContext, () => ensureSpanCreated())) {
|
|
27795
|
+
let settled$1 = false;
|
|
27796
|
+
const finish$1 = (error, doc) => {
|
|
27797
|
+
if (settled$1) return;
|
|
27798
|
+
settled$1 = true;
|
|
27799
|
+
callback(error, doc);
|
|
27800
|
+
};
|
|
27801
|
+
const maybeResult$1 = originalNext((error, doc) => finish$1(error, doc));
|
|
27802
|
+
Promise.resolve(maybeResult$1).then((doc) => finish$1(null, doc)).catch((error) => finish$1(error, null));
|
|
27803
|
+
return;
|
|
27804
|
+
}
|
|
27805
|
+
let settled = false;
|
|
27806
|
+
const finish = (error, doc) => {
|
|
27807
|
+
if (settled) return;
|
|
27808
|
+
settled = true;
|
|
27809
|
+
if (error) {
|
|
27810
|
+
handleCursorError(error);
|
|
27811
|
+
callback(error);
|
|
27812
|
+
return;
|
|
27813
|
+
}
|
|
27814
|
+
if (doc !== null) cursorState.collectedDocuments.push(doc);
|
|
27815
|
+
else finalizeCursorSpan();
|
|
27816
|
+
callback(null, doc);
|
|
27817
|
+
};
|
|
27818
|
+
const runOriginal = () => originalNext((error, doc) => {
|
|
27819
|
+
finish(error, doc);
|
|
27820
|
+
});
|
|
27821
|
+
const maybeResult = cursorState.spanInfo ? SpanUtils.withSpan(cursorState.spanInfo, () => runOriginal()) : runOriginal();
|
|
27822
|
+
Promise.resolve(maybeResult).then((doc) => finish(null, doc)).catch((error) => finish(error, null));
|
|
27823
|
+
return;
|
|
27824
|
+
}
|
|
27825
|
+
if (cursorState.recorded) return callback === void 0 ? originalNext() : originalNext(callback);
|
|
27826
|
+
if (!import_src$12.context.with(creationContext, () => ensureSpanCreated())) return callback === void 0 ? originalNext() : originalNext(callback);
|
|
27827
|
+
return Promise.resolve(cursorState.spanInfo ? SpanUtils.withSpan(cursorState.spanInfo, () => callback === void 0 ? originalNext() : originalNext(callback)) : callback === void 0 ? originalNext() : originalNext(callback)).then((doc) => {
|
|
27457
27828
|
if (doc !== null) cursorState.collectedDocuments.push(doc);
|
|
27458
27829
|
else finalizeCursorSpan();
|
|
27459
27830
|
return doc;
|
|
27460
|
-
}
|
|
27831
|
+
}).catch((error) => {
|
|
27461
27832
|
handleCursorError(error);
|
|
27462
27833
|
throw error;
|
|
27463
|
-
}
|
|
27834
|
+
});
|
|
27464
27835
|
};
|
|
27465
27836
|
}
|
|
27466
27837
|
if (typeof cursor.hasNext === "function") {
|
|
27467
27838
|
const originalHasNext = cursor.hasNext.bind(cursor);
|
|
27468
|
-
cursor.hasNext =
|
|
27469
|
-
if (
|
|
27470
|
-
|
|
27471
|
-
|
|
27472
|
-
|
|
27839
|
+
cursor.hasNext = (callback) => {
|
|
27840
|
+
if (typeof callback === "function") {
|
|
27841
|
+
if (cursorState.recorded) {
|
|
27842
|
+
let settled$1 = false;
|
|
27843
|
+
const finish$1 = (error, result) => {
|
|
27844
|
+
if (settled$1) return;
|
|
27845
|
+
settled$1 = true;
|
|
27846
|
+
callback(error, result);
|
|
27847
|
+
};
|
|
27848
|
+
const maybeResult$1 = originalHasNext((error, result) => finish$1(error, result));
|
|
27849
|
+
Promise.resolve(maybeResult$1).then((result) => finish$1(null, result)).catch((error) => finish$1(error, false));
|
|
27850
|
+
return;
|
|
27851
|
+
}
|
|
27852
|
+
if (!import_src$12.context.with(creationContext, () => ensureSpanCreated())) {
|
|
27853
|
+
let settled$1 = false;
|
|
27854
|
+
const finish$1 = (error, result) => {
|
|
27855
|
+
if (settled$1) return;
|
|
27856
|
+
settled$1 = true;
|
|
27857
|
+
callback(error, result);
|
|
27858
|
+
};
|
|
27859
|
+
const maybeResult$1 = originalHasNext((error, result) => finish$1(error, result));
|
|
27860
|
+
Promise.resolve(maybeResult$1).then((result) => finish$1(null, result)).catch((error) => finish$1(error, false));
|
|
27861
|
+
return;
|
|
27862
|
+
}
|
|
27863
|
+
let settled = false;
|
|
27864
|
+
const finish = (error, result) => {
|
|
27865
|
+
if (settled) return;
|
|
27866
|
+
settled = true;
|
|
27867
|
+
if (error) {
|
|
27868
|
+
handleCursorError(error);
|
|
27869
|
+
callback(error);
|
|
27870
|
+
return;
|
|
27871
|
+
}
|
|
27872
|
+
if (!result) finalizeCursorSpan();
|
|
27873
|
+
callback(null, result);
|
|
27874
|
+
};
|
|
27875
|
+
const runOriginal = () => originalHasNext((error, result) => {
|
|
27876
|
+
finish(error, result);
|
|
27877
|
+
});
|
|
27878
|
+
const maybeResult = cursorState.spanInfo ? SpanUtils.withSpan(cursorState.spanInfo, () => runOriginal()) : runOriginal();
|
|
27879
|
+
Promise.resolve(maybeResult).then((result) => finish(null, result)).catch((error) => finish(error, false));
|
|
27880
|
+
return;
|
|
27881
|
+
}
|
|
27882
|
+
if (cursorState.recorded) return callback === void 0 ? originalHasNext() : originalHasNext(callback);
|
|
27883
|
+
if (!import_src$12.context.with(creationContext, () => ensureSpanCreated())) return callback === void 0 ? originalHasNext() : originalHasNext(callback);
|
|
27884
|
+
return Promise.resolve(cursorState.spanInfo ? SpanUtils.withSpan(cursorState.spanInfo, () => callback === void 0 ? originalHasNext() : originalHasNext(callback)) : callback === void 0 ? originalHasNext() : originalHasNext(callback)).then((result) => {
|
|
27473
27885
|
if (!result) finalizeCursorSpan();
|
|
27474
27886
|
return result;
|
|
27475
|
-
}
|
|
27887
|
+
}).catch((error) => {
|
|
27476
27888
|
handleCursorError(error);
|
|
27477
27889
|
throw error;
|
|
27478
|
-
}
|
|
27890
|
+
});
|
|
27479
27891
|
};
|
|
27480
27892
|
}
|
|
27481
27893
|
if (typeof cursor.forEach === "function") {
|
|
@@ -27531,14 +27943,30 @@ var MongodbInstrumentation = class extends TdInstrumentationBase {
|
|
|
27531
27943
|
}
|
|
27532
27944
|
if (typeof cursor.close === "function") {
|
|
27533
27945
|
const originalClose = cursor.close.bind(cursor);
|
|
27534
|
-
cursor.close =
|
|
27535
|
-
|
|
27536
|
-
|
|
27946
|
+
cursor.close = (callback) => {
|
|
27947
|
+
if (typeof callback === "function") {
|
|
27948
|
+
let settled = false;
|
|
27949
|
+
const finish = (error) => {
|
|
27950
|
+
if (settled) return;
|
|
27951
|
+
settled = true;
|
|
27952
|
+
if (error) {
|
|
27953
|
+
handleCursorError(error);
|
|
27954
|
+
callback(error);
|
|
27955
|
+
return;
|
|
27956
|
+
}
|
|
27957
|
+
finalizeCursorSpan();
|
|
27958
|
+
callback();
|
|
27959
|
+
};
|
|
27960
|
+
const maybeResult = originalClose((error) => finish(error));
|
|
27961
|
+
Promise.resolve(maybeResult).then(() => finish()).catch((error) => finish(error));
|
|
27962
|
+
return;
|
|
27963
|
+
}
|
|
27964
|
+
return Promise.resolve(callback === void 0 ? originalClose() : originalClose(callback)).then(() => {
|
|
27537
27965
|
finalizeCursorSpan();
|
|
27538
|
-
}
|
|
27966
|
+
}).catch((error) => {
|
|
27539
27967
|
handleCursorError(error);
|
|
27540
27968
|
throw error;
|
|
27541
|
-
}
|
|
27969
|
+
});
|
|
27542
27970
|
};
|
|
27543
27971
|
}
|
|
27544
27972
|
if (typeof cursor[Symbol.asyncIterator] === "function") {
|
|
@@ -27814,6 +28242,7 @@ var MongodbInstrumentation = class extends TdInstrumentationBase {
|
|
|
27814
28242
|
case "dropIndex": return {};
|
|
27815
28243
|
case "dropIndexes": return { ok: 1 };
|
|
27816
28244
|
case "indexes": return [];
|
|
28245
|
+
case "drop": return true;
|
|
27817
28246
|
default: return null;
|
|
27818
28247
|
}
|
|
27819
28248
|
}
|
|
@@ -27842,18 +28271,29 @@ var MongodbInstrumentation = class extends TdInstrumentationBase {
|
|
|
27842
28271
|
const submodule = `db-${methodName}`;
|
|
27843
28272
|
return (original) => {
|
|
27844
28273
|
return function(...args) {
|
|
28274
|
+
const callback = args.length > 0 && typeof args[args.length - 1] === "function" ? args[args.length - 1] : void 0;
|
|
28275
|
+
const effectiveArgs = callback ? args.slice(0, -1) : args;
|
|
27845
28276
|
const databaseName = this?.databaseName || this?.s?.namespace?.db;
|
|
27846
|
-
const inputValue = self._extractDbInput(methodName, databaseName,
|
|
28277
|
+
const inputValue = self._extractDbInput(methodName, databaseName, effectiveArgs);
|
|
27847
28278
|
if (self.mode === TuskDriftMode.DISABLED) return original.apply(this, args);
|
|
27848
|
-
|
|
27849
|
-
if (
|
|
27850
|
-
|
|
27851
|
-
|
|
27852
|
-
|
|
27853
|
-
if (
|
|
27854
|
-
|
|
28279
|
+
const invokeHandler = () => {
|
|
28280
|
+
if (self.mode === TuskDriftMode.RECORD) {
|
|
28281
|
+
if (methodName === "createCollection") return self._handleRecordCreateCollection(original, this, effectiveArgs, inputValue, spanName, submodule);
|
|
28282
|
+
return self._handleRecordDbMethod(original, this, effectiveArgs, inputValue, spanName, submodule);
|
|
28283
|
+
}
|
|
28284
|
+
if (self.mode === TuskDriftMode.REPLAY) {
|
|
28285
|
+
if (methodName === "createCollection") return self._handleReplayCreateCollection(this, effectiveArgs, inputValue, spanName, submodule);
|
|
28286
|
+
return self._handleReplayDbMethod(original, this, effectiveArgs, inputValue, spanName, submodule, methodName);
|
|
28287
|
+
}
|
|
28288
|
+
return original.apply(this, effectiveArgs);
|
|
28289
|
+
};
|
|
28290
|
+
if (callback) {
|
|
28291
|
+
Promise.resolve(invokeHandler()).then((result) => callback(null, result)).catch((error) => callback(error));
|
|
28292
|
+
return;
|
|
27855
28293
|
}
|
|
27856
|
-
|
|
28294
|
+
if (self.mode === TuskDriftMode.RECORD) return invokeHandler();
|
|
28295
|
+
if (self.mode === TuskDriftMode.REPLAY) return invokeHandler();
|
|
28296
|
+
return original.apply(this, effectiveArgs);
|
|
27857
28297
|
};
|
|
27858
28298
|
};
|
|
27859
28299
|
}
|
|
@@ -27876,7 +28316,8 @@ var MongodbInstrumentation = class extends TdInstrumentationBase {
|
|
|
27876
28316
|
isPreAppStart,
|
|
27877
28317
|
stopRecordingChildSpans: true
|
|
27878
28318
|
}, (spanInfo) => {
|
|
27879
|
-
|
|
28319
|
+
const resultPromise = original.apply(thisArg, args);
|
|
28320
|
+
return Promise.resolve(resultPromise).then((result) => {
|
|
27880
28321
|
try {
|
|
27881
28322
|
addOutputAttributesToSpan(spanInfo, wrapDirectOutput(result));
|
|
27882
28323
|
SpanUtils.endSpan(spanInfo.span, { code: import_src$12.SpanStatusCode.OK });
|
|
@@ -27913,7 +28354,7 @@ var MongodbInstrumentation = class extends TdInstrumentationBase {
|
|
|
27913
28354
|
},
|
|
27914
28355
|
isServerRequest: false,
|
|
27915
28356
|
replayModeHandler: () => {
|
|
27916
|
-
return SpanUtils.createAndExecuteSpan(this.mode, () =>
|
|
28357
|
+
return SpanUtils.createAndExecuteSpan(this.mode, () => Promise.resolve(this._getDbNoOpResult(methodName)), {
|
|
27917
28358
|
name: spanName,
|
|
27918
28359
|
kind: import_src$12.SpanKind.CLIENT,
|
|
27919
28360
|
submodule,
|
|
@@ -27971,7 +28412,8 @@ var MongodbInstrumentation = class extends TdInstrumentationBase {
|
|
|
27971
28412
|
isPreAppStart,
|
|
27972
28413
|
stopRecordingChildSpans: true
|
|
27973
28414
|
}, (spanInfo) => {
|
|
27974
|
-
|
|
28415
|
+
const resultPromise = original.apply(thisArg, args);
|
|
28416
|
+
return Promise.resolve(resultPromise).then((result) => {
|
|
27975
28417
|
try {
|
|
27976
28418
|
const collectionName = args[0];
|
|
27977
28419
|
SpanUtils.addSpanAttributes(spanInfo.span, { outputValue: sanitizeBsonValue({ collectionName }) });
|
|
@@ -28235,13 +28677,26 @@ var MongodbInstrumentation = class extends TdInstrumentationBase {
|
|
|
28235
28677
|
*/
|
|
28236
28678
|
_getStartSessionWrapper() {
|
|
28237
28679
|
const self = this;
|
|
28680
|
+
const REPLAY_WITH_TXN_PATCHED = Symbol("tdReplayWithTransactionPatched");
|
|
28238
28681
|
return (original) => {
|
|
28239
28682
|
return function(...args) {
|
|
28240
|
-
|
|
28241
|
-
|
|
28242
|
-
|
|
28683
|
+
const session = original.apply(this, args);
|
|
28684
|
+
if (self.mode === TuskDriftMode.REPLAY && self.mongodbMajorVersion === 4 && session && typeof session.withTransaction === "function" && !session[REPLAY_WITH_TXN_PATCHED]) {
|
|
28685
|
+
const originalWithTransaction = session.withTransaction.bind(session);
|
|
28686
|
+
session.withTransaction = async (fn, _options) => {
|
|
28687
|
+
try {
|
|
28688
|
+
await Promise.resolve(fn(session));
|
|
28689
|
+
if (typeof session.commitTransaction === "function") return await session.commitTransaction();
|
|
28690
|
+
return;
|
|
28691
|
+
} catch (error) {
|
|
28692
|
+
if (typeof session.abortTransaction === "function") await session.abortTransaction();
|
|
28693
|
+
throw error;
|
|
28694
|
+
}
|
|
28695
|
+
};
|
|
28696
|
+
session.__tdOriginalWithTransaction = originalWithTransaction;
|
|
28697
|
+
session[REPLAY_WITH_TXN_PATCHED] = true;
|
|
28243
28698
|
}
|
|
28244
|
-
return
|
|
28699
|
+
return session;
|
|
28245
28700
|
};
|
|
28246
28701
|
};
|
|
28247
28702
|
}
|
|
@@ -28317,7 +28772,8 @@ var MongodbInstrumentation = class extends TdInstrumentationBase {
|
|
|
28317
28772
|
isPreAppStart,
|
|
28318
28773
|
stopRecordingChildSpans: true
|
|
28319
28774
|
}, (spanInfo) => {
|
|
28320
|
-
|
|
28775
|
+
const resultPromise = original.apply(thisArg, args);
|
|
28776
|
+
return Promise.resolve(resultPromise).then((result) => {
|
|
28321
28777
|
try {
|
|
28322
28778
|
addOutputAttributesToSpan(spanInfo, result ?? { success: true });
|
|
28323
28779
|
SpanUtils.endSpan(spanInfo.span, { code: import_src$12.SpanStatusCode.OK });
|
|
@@ -28352,7 +28808,7 @@ var MongodbInstrumentation = class extends TdInstrumentationBase {
|
|
|
28352
28808
|
noOpRequestHandler: () => Promise.resolve(void 0),
|
|
28353
28809
|
isServerRequest: false,
|
|
28354
28810
|
replayModeHandler: () => {
|
|
28355
|
-
return SpanUtils.createAndExecuteSpan(this.mode, () =>
|
|
28811
|
+
return SpanUtils.createAndExecuteSpan(this.mode, () => Promise.resolve(void 0), {
|
|
28356
28812
|
name: spanName,
|
|
28357
28813
|
kind: import_src$12.SpanKind.CLIENT,
|
|
28358
28814
|
submodule,
|
|
@@ -28626,7 +29082,8 @@ var MongodbInstrumentation = class extends TdInstrumentationBase {
|
|
|
28626
29082
|
isPreAppStart,
|
|
28627
29083
|
stopRecordingChildSpans: true
|
|
28628
29084
|
}, (spanInfo) => {
|
|
28629
|
-
|
|
29085
|
+
const resultPromise = original.apply(thisArg, args);
|
|
29086
|
+
return Promise.resolve(resultPromise).then((result) => {
|
|
28630
29087
|
try {
|
|
28631
29088
|
addOutputAttributesToSpan(spanInfo, wrapDirectOutput(result));
|
|
28632
29089
|
SpanUtils.endSpan(spanInfo.span, { code: import_src$12.SpanStatusCode.OK });
|
|
@@ -28672,7 +29129,16 @@ var MongodbInstrumentation = class extends TdInstrumentationBase {
|
|
|
28672
29129
|
},
|
|
28673
29130
|
isServerRequest: false,
|
|
28674
29131
|
replayModeHandler: () => {
|
|
28675
|
-
return SpanUtils.createAndExecuteSpan(this.mode, () =>
|
|
29132
|
+
return SpanUtils.createAndExecuteSpan(this.mode, () => Promise.resolve({
|
|
29133
|
+
acknowledged: false,
|
|
29134
|
+
insertedCount: 0,
|
|
29135
|
+
matchedCount: 0,
|
|
29136
|
+
modifiedCount: 0,
|
|
29137
|
+
deletedCount: 0,
|
|
29138
|
+
upsertedCount: 0,
|
|
29139
|
+
insertedIds: {},
|
|
29140
|
+
upsertedIds: {}
|
|
29141
|
+
}), {
|
|
28676
29142
|
name: spanName,
|
|
28677
29143
|
kind: import_src$12.SpanKind.CLIENT,
|
|
28678
29144
|
submodule,
|
|
@@ -40907,7 +41373,7 @@ var require_src = /* @__PURE__ */ __commonJS({ "node_modules/@opentelemetry/sdk-
|
|
|
40907
41373
|
//#endregion
|
|
40908
41374
|
//#region package.json
|
|
40909
41375
|
var import_src$1 = /* @__PURE__ */ __toESM(require_src(), 1);
|
|
40910
|
-
var version = "0.1.
|
|
41376
|
+
var version = "0.1.35";
|
|
40911
41377
|
|
|
40912
41378
|
//#endregion
|
|
40913
41379
|
//#region src/version.ts
|
|
@@ -42808,7 +43274,14 @@ var TuskDriftCore = class TuskDriftCore {
|
|
|
42808
43274
|
logger.info(`Rust core path enabled at startup (env=${envDisplay}, reason=${status.reason}).`);
|
|
42809
43275
|
return;
|
|
42810
43276
|
}
|
|
42811
|
-
logger.
|
|
43277
|
+
logger.info(`Rust core path unavailable at startup; using JavaScript path instead (env=${envDisplay}, reason=${status.reason}, error=${status.bindingError}).`);
|
|
43278
|
+
}
|
|
43279
|
+
logStartupSummary() {
|
|
43280
|
+
const serviceName = this.config.service?.name || "unknown";
|
|
43281
|
+
const serviceId = this.config.service?.id || "<unset>";
|
|
43282
|
+
const environment = this.initParams.env || "<unset>";
|
|
43283
|
+
const exportSpans = this.config.recording?.export_spans || false;
|
|
43284
|
+
logger.info(`SDK initialized successfully (version=${SDK_VERSION}, mode=${this.mode}, env=${environment}, service=${serviceName}, serviceId=${serviceId}, exportSpans=${exportSpans}, samplingRate=${this.samplingRate}, logLevel=${logger.getLogLevel()}, runtime=node ${process.version}, platform=${process.platform}/${process.arch}).`);
|
|
42812
43285
|
}
|
|
42813
43286
|
validateSamplingRate(value, source) {
|
|
42814
43287
|
if (typeof value !== "number" || isNaN(value)) {
|
|
@@ -43055,7 +43528,7 @@ var TuskDriftCore = class TuskDriftCore {
|
|
|
43055
43528
|
this.initializeTracing({ baseDirectory });
|
|
43056
43529
|
this.createEnvVarsSnapshot();
|
|
43057
43530
|
this.initialized = true;
|
|
43058
|
-
|
|
43531
|
+
this.logStartupSummary();
|
|
43059
43532
|
}
|
|
43060
43533
|
markAppAsReady() {
|
|
43061
43534
|
if (!this.initialized) {
|