@use-tusk/drift-node-sdk 0.1.30 → 0.1.31

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/dist/index.js CHANGED
@@ -6203,7 +6203,7 @@ var JsonSchema$Type = class extends import_commonjs$7.MessageType {
6203
6203
  const JsonSchema = new JsonSchema$Type();
6204
6204
 
6205
6205
  //#endregion
6206
- //#region node_modules/@use-tusk/drift-schemas/dist/span-CXrr1reB.js
6206
+ //#region node_modules/@use-tusk/drift-schemas/dist/span-Cklxe7uf.js
6207
6207
  var import_commonjs$6 = /* @__PURE__ */ __toESM(require_commonjs$2(), 1);
6208
6208
  /**
6209
6209
  * Package type classification enum
@@ -6561,6 +6561,13 @@ var Span$Type = class extends import_commonjs$6.MessageType {
6561
6561
  kind: "scalar",
6562
6562
  opt: true,
6563
6563
  T: 9
6564
+ },
6565
+ {
6566
+ no: 25,
6567
+ name: "id",
6568
+ kind: "scalar",
6569
+ opt: true,
6570
+ T: 9
6564
6571
  }
6565
6572
  ]);
6566
6573
  }
@@ -6661,6 +6668,9 @@ var Span$Type = class extends import_commonjs$6.MessageType {
6661
6668
  case 24:
6662
6669
  message.environment = reader.string();
6663
6670
  break;
6671
+ case 25:
6672
+ message.id = reader.string();
6673
+ break;
6664
6674
  default:
6665
6675
  let u = options.readUnknownField;
6666
6676
  if (u === "throw") throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`);
@@ -6695,6 +6705,7 @@ var Span$Type = class extends import_commonjs$6.MessageType {
6695
6705
  if (message.isRootSpan !== false) writer.tag(22, import_commonjs$6.WireType.Varint).bool(message.isRootSpan);
6696
6706
  if (message.metadata) Struct.internalBinaryWrite(message.metadata, writer.tag(23, import_commonjs$6.WireType.LengthDelimited).fork(), options).join();
6697
6707
  if (message.environment !== void 0) writer.tag(24, import_commonjs$6.WireType.LengthDelimited).string(message.environment);
6708
+ if (message.id !== void 0) writer.tag(25, import_commonjs$6.WireType.LengthDelimited).string(message.id);
6698
6709
  let u = options.writeUnknownFields;
6699
6710
  if (u !== false) (u == true ? import_commonjs$6.UnknownFieldHandler.onWrite : u)(this.typeName, message, writer);
6700
6711
  return writer;
@@ -10225,7 +10236,7 @@ var require_commonjs$1 = /* @__PURE__ */ __commonJS({ "node_modules/@protobuf-ts
10225
10236
  }) });
10226
10237
 
10227
10238
  //#endregion
10228
- //#region node_modules/@use-tusk/drift-schemas/dist/span_export_service-RmRqNxn1.js
10239
+ //#region node_modules/@use-tusk/drift-schemas/dist/span_export_service-BjPTFjCJ.js
10229
10240
  var import_commonjs$4 = /* @__PURE__ */ __toESM(require_commonjs$1(), 1);
10230
10241
  var import_commonjs$5 = /* @__PURE__ */ __toESM(require_commonjs$2(), 1);
10231
10242
  var ExportSpansRequest$Type = class extends import_commonjs$5.MessageType {
@@ -15764,7 +15775,7 @@ var TdMysql2ConnectionMock = class extends EventEmitter {
15764
15775
  }
15765
15776
  query(...args) {
15766
15777
  logger.debug(`[TdMysql2ConnectionMock] Mock connection query intercepted in REPLAY mode`);
15767
- const stackTrace = captureStackTrace(["TdMysql2ConnectionMock"]);
15778
+ const stackTrace = captureStackTrace(["TdMysql2ConnectionMock", "Mysql2Instrumentation"]);
15768
15779
  const queryConfig = this.mysql2Instrumentation.parseQueryArgs(args);
15769
15780
  if (!queryConfig || !queryConfig.sql) {
15770
15781
  logger.debug(`[TdMysql2ConnectionMock] Could not parse mock connection query, returning empty result`);
@@ -15779,16 +15790,16 @@ var TdMysql2ConnectionMock = class extends EventEmitter {
15779
15790
  return Promise.resolve([emptyResult.rows, emptyResult.fields]);
15780
15791
  }
15781
15792
  const inputValue = createMockInputValue({
15782
- sql: queryConfig.sql,
15793
+ sql: this.mysql2Instrumentation.normalizeSqlForMockMatching(queryConfig.sql),
15783
15794
  values: queryConfig.values || [],
15784
15795
  clientType: this.clientType
15785
15796
  });
15786
- if (this.spanInfo) return this.mysql2Instrumentation.handleReplayQuery(queryConfig, inputValue, this.spanInfo, stackTrace);
15797
+ if (this.spanInfo) return this.mysql2Instrumentation.handleReplayQuery(queryConfig, inputValue, this.spanInfo, "query", stackTrace);
15787
15798
  else return this.mysql2Instrumentation.handleNoOpReplayQuery(queryConfig);
15788
15799
  }
15789
15800
  execute(...args) {
15790
15801
  logger.debug(`[TdMysql2ConnectionMock] Mock connection execute intercepted in REPLAY mode`);
15791
- const stackTrace = captureStackTrace(["TdMysql2ConnectionMock"]);
15802
+ const stackTrace = captureStackTrace(["TdMysql2ConnectionMock", "Mysql2Instrumentation"]);
15792
15803
  const queryConfig = this.mysql2Instrumentation.parseQueryArgs(args);
15793
15804
  if (!queryConfig || !queryConfig.sql) {
15794
15805
  logger.debug(`[TdMysql2ConnectionMock] Could not parse mock connection execute, returning empty result`);
@@ -15803,11 +15814,11 @@ var TdMysql2ConnectionMock = class extends EventEmitter {
15803
15814
  return Promise.resolve([emptyResult.rows, emptyResult.fields]);
15804
15815
  }
15805
15816
  const inputValue = createMockInputValue({
15806
- sql: queryConfig.sql,
15817
+ sql: this.mysql2Instrumentation.normalizeSqlForMockMatching(queryConfig.sql),
15807
15818
  values: queryConfig.values || [],
15808
15819
  clientType: this.clientType
15809
15820
  });
15810
- if (this.spanInfo) return this.mysql2Instrumentation.handleReplayQuery(queryConfig, inputValue, this.spanInfo, stackTrace);
15821
+ if (this.spanInfo) return this.mysql2Instrumentation.handleReplayQuery(queryConfig, inputValue, this.spanInfo, "execute", stackTrace);
15811
15822
  else return this.mysql2Instrumentation.handleNoOpReplayQuery(queryConfig);
15812
15823
  }
15813
15824
  release() {
@@ -16142,6 +16153,8 @@ var Mysql2Instrumentation = class extends TdInstrumentationBase {
16142
16153
  constructor(config = {}) {
16143
16154
  super("mysql2", config);
16144
16155
  this.INSTRUMENTATION_NAME = "Mysql2Instrumentation";
16156
+ this.CONTEXT_BOUND_CONNECTION = Symbol("mysql2-context-bound-connection");
16157
+ this.CONTEXT_BOUND_PARENT_CONTEXT = Symbol("mysql2-bound-parent-context");
16145
16158
  this.mode = config.mode || TuskDriftMode.DISABLED;
16146
16159
  this.queryMock = new TdMysql2QueryMock();
16147
16160
  }
@@ -16371,7 +16384,7 @@ var Mysql2Instrumentation = class extends TdInstrumentationBase {
16371
16384
  return originalQuery.apply(this, args);
16372
16385
  }
16373
16386
  const inputValue = {
16374
- sql: queryConfig.sql,
16387
+ sql: self.normalizeSqlForMockMatching(queryConfig.sql),
16375
16388
  values: queryConfig.values || [],
16376
16389
  clientType
16377
16390
  };
@@ -16438,7 +16451,7 @@ var Mysql2Instrumentation = class extends TdInstrumentationBase {
16438
16451
  return originalExecute.apply(this, args);
16439
16452
  }
16440
16453
  const inputValue = {
16441
- sql: queryConfig.sql,
16454
+ sql: self.normalizeSqlForMockMatching(queryConfig.sql),
16442
16455
  values: queryConfig.values || [],
16443
16456
  clientType
16444
16457
  };
@@ -17034,6 +17047,14 @@ var Mysql2Instrumentation = class extends TdInstrumentationBase {
17034
17047
  }
17035
17048
  return null;
17036
17049
  }
17050
+ normalizeSqlForMockMatching(sql) {
17051
+ if (!sql) return sql;
17052
+ const trimmed = sql.trim();
17053
+ if (/^savepoint\s+trx\d+\s*;?$/i.test(trimmed)) return trimmed.replace(/^(savepoint\s+)trx\d+(\s*;?)$/i, "$1trx$2");
17054
+ if (/^release\s+savepoint\s+trx\d+\s*;?$/i.test(trimmed)) return trimmed.replace(/^(release\s+savepoint\s+)trx\d+(\s*;?)$/i, "$1trx$2");
17055
+ if (/^rollback\s+to\s+savepoint\s+trx\d+\s*;?$/i.test(trimmed)) return trimmed.replace(/^(rollback\s+to\s+savepoint\s+)trx\d+(\s*;?)$/i, "$1trx$2");
17056
+ return sql;
17057
+ }
17037
17058
  _handleRecordQueryInSpan(spanInfo, originalQuery, queryConfig, args, context$6) {
17038
17059
  const hasCallback = !!queryConfig.callback;
17039
17060
  const invokeOriginal = (invokeArgs) => {
@@ -17117,8 +17138,10 @@ var Mysql2Instrumentation = class extends TdInstrumentationBase {
17117
17138
  return this.queryMock.handleNoOpReplayQuery(queryConfig);
17118
17139
  }
17119
17140
  _handleRecordPoolGetConnectionInSpan(spanInfo, originalGetConnection, callback, context$6) {
17141
+ const parentContext = import_src$22.context.active();
17120
17142
  if (callback) {
17121
17143
  const wrappedCallback = (error, connection) => {
17144
+ const scopedConnection = connection ? this._bindConnectionMethodsToContext(connection, parentContext) : connection;
17122
17145
  if (error) {
17123
17146
  logger.debug(`[Mysql2Instrumentation] MySQL2 Pool getConnection error: ${error.message} (${SpanUtils.getTraceInfo()})`);
17124
17147
  try {
@@ -17134,44 +17157,71 @@ var Mysql2Instrumentation = class extends TdInstrumentationBase {
17134
17157
  try {
17135
17158
  SpanUtils.addSpanAttributes(spanInfo.span, { outputValue: {
17136
17159
  connected: true,
17137
- hasConnection: !!connection
17160
+ hasConnection: !!scopedConnection
17138
17161
  } });
17139
17162
  SpanUtils.endSpan(spanInfo.span, { code: import_src$22.SpanStatusCode.OK });
17140
17163
  } catch (error$1) {
17141
17164
  logger.error(`[Mysql2Instrumentation] error processing getConnection response:`, error$1);
17142
17165
  }
17143
17166
  }
17144
- return callback(error, connection);
17167
+ return import_src$22.context.with(parentContext, () => callback(error, scopedConnection));
17145
17168
  };
17146
- return originalGetConnection.call(context$6, wrappedCallback);
17147
- } else return originalGetConnection.call(context$6).then((connection) => {
17148
- logger.debug(`[Mysql2Instrumentation] MySQL2 Pool getConnection completed successfully (${SpanUtils.getTraceInfo()})`);
17149
- try {
17150
- SpanUtils.addSpanAttributes(spanInfo.span, { outputValue: {
17151
- connected: true,
17152
- hasConnection: !!connection
17153
- } });
17154
- SpanUtils.endSpan(spanInfo.span, { code: import_src$22.SpanStatusCode.OK });
17155
- } catch (error) {
17156
- logger.error(`[Mysql2Instrumentation] error processing getConnection response:`, error);
17157
- }
17158
- return connection;
17169
+ return import_src$22.context.with(parentContext, () => originalGetConnection.call(context$6, wrappedCallback));
17170
+ } else return import_src$22.context.with(parentContext, () => originalGetConnection.call(context$6)).then((connection) => {
17171
+ const scopedConnection = this._bindConnectionMethodsToContext(connection, parentContext);
17172
+ return import_src$22.context.with(parentContext, () => {
17173
+ logger.debug(`[Mysql2Instrumentation] MySQL2 Pool getConnection completed successfully (${SpanUtils.getTraceInfo()})`);
17174
+ try {
17175
+ SpanUtils.addSpanAttributes(spanInfo.span, { outputValue: {
17176
+ connected: true,
17177
+ hasConnection: !!scopedConnection
17178
+ } });
17179
+ SpanUtils.endSpan(spanInfo.span, { code: import_src$22.SpanStatusCode.OK });
17180
+ } catch (error) {
17181
+ logger.error(`[Mysql2Instrumentation] error processing getConnection response:`, error);
17182
+ }
17183
+ return scopedConnection;
17184
+ });
17159
17185
  }).catch((error) => {
17160
- logger.debug(`[Mysql2Instrumentation] MySQL2 Pool getConnection error: ${error.message} (${SpanUtils.getTraceInfo()})`);
17161
- try {
17162
- SpanUtils.endSpan(spanInfo.span, {
17163
- code: import_src$22.SpanStatusCode.ERROR,
17164
- message: error.message
17165
- });
17166
- } catch (error$1) {
17167
- logger.error(`[Mysql2Instrumentation] error ending span:`, error$1);
17168
- }
17169
- throw error;
17186
+ return import_src$22.context.with(parentContext, () => {
17187
+ logger.debug(`[Mysql2Instrumentation] MySQL2 Pool getConnection error: ${error.message} (${SpanUtils.getTraceInfo()})`);
17188
+ try {
17189
+ SpanUtils.endSpan(spanInfo.span, {
17190
+ code: import_src$22.SpanStatusCode.ERROR,
17191
+ message: error.message
17192
+ });
17193
+ } catch (error$1) {
17194
+ logger.error(`[Mysql2Instrumentation] error ending span:`, error$1);
17195
+ }
17196
+ throw error;
17197
+ });
17170
17198
  });
17171
17199
  }
17200
+ _bindConnectionMethodsToContext(connection, parentContext) {
17201
+ const conn = connection;
17202
+ if (!conn) return connection;
17203
+ conn[this.CONTEXT_BOUND_PARENT_CONTEXT] = parentContext;
17204
+ if (conn[this.CONTEXT_BOUND_CONNECTION]) return connection;
17205
+ for (const method of [
17206
+ "query",
17207
+ "execute",
17208
+ "beginTransaction",
17209
+ "commit",
17210
+ "rollback"
17211
+ ]) {
17212
+ const original = conn[method];
17213
+ if (typeof original !== "function") continue;
17214
+ conn[method] = (...args) => {
17215
+ const boundContext = conn[this.CONTEXT_BOUND_PARENT_CONTEXT] ?? import_src$22.context.active();
17216
+ return import_src$22.context.with(boundContext, () => original.apply(conn, args));
17217
+ };
17218
+ }
17219
+ conn[this.CONTEXT_BOUND_CONNECTION] = true;
17220
+ return connection;
17221
+ }
17172
17222
  handleNoOpReplayGetConnection(callback) {
17173
17223
  logger.debug(`[Mysql2Instrumentation] Background getConnection detected, returning mock connection`);
17174
- const mockConnection = new TdMysql2ConnectionMock(this, "pool");
17224
+ const mockConnection = new TdMysql2ConnectionMock(this, "connection");
17175
17225
  if (callback) {
17176
17226
  process.nextTick(() => callback(null, mockConnection));
17177
17227
  return;
@@ -17180,7 +17230,7 @@ var Mysql2Instrumentation = class extends TdInstrumentationBase {
17180
17230
  }
17181
17231
  _handleReplayPoolGetConnection(spanInfo, callback) {
17182
17232
  logger.debug(`[Mysql2Instrumentation] Replaying MySQL2 Pool getConnection`);
17183
- const mockConnection = new TdMysql2ConnectionMock(this, "pool", spanInfo);
17233
+ const mockConnection = new TdMysql2ConnectionMock(this, "connection", spanInfo);
17184
17234
  if (callback) {
17185
17235
  process.nextTick(() => callback(null, mockConnection));
17186
17236
  return;
@@ -25894,13 +25944,188 @@ var require_src$6 = /* @__PURE__ */ __commonJS({ "node_modules/@opentelemetry/co
25894
25944
  }) });
25895
25945
 
25896
25946
  //#endregion
25897
- //#region src/core/tracing/SpanTransformer.ts
25947
+ //#region src/core/rustCoreBinding.ts
25898
25948
  var import_src$10 = /* @__PURE__ */ __toESM(require_src$6(), 1);
25949
+ let bindingLoadAttempted = false;
25950
+ let binding = null;
25951
+ let bindingLoadError = null;
25952
+ function getRustCoreEnvDecision() {
25953
+ const raw = process.env.TUSK_USE_RUST_CORE;
25954
+ if (!raw) return {
25955
+ enabled: true,
25956
+ reason: "default_on",
25957
+ rawEnv: null
25958
+ };
25959
+ const normalized = raw.trim().toLowerCase();
25960
+ if ([
25961
+ "1",
25962
+ "true",
25963
+ "yes",
25964
+ "on"
25965
+ ].includes(normalized)) return {
25966
+ enabled: true,
25967
+ reason: "env_enabled",
25968
+ rawEnv: raw
25969
+ };
25970
+ if ([
25971
+ "0",
25972
+ "false",
25973
+ "no",
25974
+ "off"
25975
+ ].includes(normalized)) return {
25976
+ enabled: false,
25977
+ reason: "env_disabled",
25978
+ rawEnv: raw
25979
+ };
25980
+ return {
25981
+ enabled: true,
25982
+ reason: "invalid_env_value_defaulted",
25983
+ rawEnv: raw
25984
+ };
25985
+ }
25986
+ function loadBinding() {
25987
+ if (bindingLoadAttempted) return binding;
25988
+ bindingLoadAttempted = true;
25989
+ if (!getRustCoreEnvDecision().enabled) return null;
25990
+ try {
25991
+ binding = __require("@use-tusk/drift-core-node");
25992
+ bindingLoadError = null;
25993
+ } catch (error) {
25994
+ binding = null;
25995
+ bindingLoadError = error instanceof Error ? `${error.name}: ${error.message}` : String(error);
25996
+ }
25997
+ return binding;
25998
+ }
25999
+ function getRustCoreStartupStatus() {
26000
+ const decision = getRustCoreEnvDecision();
26001
+ const loaded = decision.enabled ? loadBinding() : null;
26002
+ return {
26003
+ enabled: decision.enabled,
26004
+ reason: decision.reason,
26005
+ rawEnv: decision.rawEnv,
26006
+ bindingLoaded: loaded !== null,
26007
+ bindingError: bindingLoadError
26008
+ };
26009
+ }
26010
+ function toRustSchemaMerges(schemaMerges) {
26011
+ if (!schemaMerges) return;
26012
+ const out = {};
26013
+ for (const [key, value] of Object.entries(schemaMerges)) out[key] = {
26014
+ ...value.encoding !== void 0 ? { encoding: value.encoding } : {},
26015
+ ...value.decodedType !== void 0 ? { decoded_type: value.decodedType } : {},
26016
+ ...value.matchImportance !== void 0 ? { match_importance: value.matchImportance } : {}
26017
+ };
26018
+ return out;
26019
+ }
26020
+ function normalizeSchemaKeys(value) {
26021
+ if (Array.isArray(value)) return value.map(normalizeSchemaKeys);
26022
+ if (!value || typeof value !== "object") return value;
26023
+ const out = {};
26024
+ for (const [k, v] of Object.entries(value)) if (k === "decoded_type") out.decodedType = normalizeSchemaKeys(v);
26025
+ else if (k === "match_importance") out.matchImportance = normalizeSchemaKeys(v);
26026
+ else out[k] = normalizeSchemaKeys(v);
26027
+ return out;
26028
+ }
26029
+ function denormalizeSchemaKeys(value) {
26030
+ if (Array.isArray(value)) return value.map(denormalizeSchemaKeys);
26031
+ if (!value || typeof value !== "object") return value;
26032
+ const out = {};
26033
+ for (const [k, v] of Object.entries(value)) if (k === "decodedType") out.decoded_type = denormalizeSchemaKeys(v);
26034
+ else if (k === "matchImportance") out.match_importance = denormalizeSchemaKeys(v);
26035
+ else out[k] = denormalizeSchemaKeys(v);
26036
+ return out;
26037
+ }
26038
+ function processExportPayloadJsonable(payload, schemaMerges) {
26039
+ const loaded = loadBinding();
26040
+ if (!loaded) return null;
26041
+ try {
26042
+ const payloadJson = JSON.stringify(payload);
26043
+ const rustSchemaMerges = toRustSchemaMerges(schemaMerges);
26044
+ const schemaMergesJson = rustSchemaMerges ? JSON.stringify(rustSchemaMerges) : void 0;
26045
+ const result = loaded.processExportPayload(payloadJson, schemaMergesJson);
26046
+ return {
26047
+ normalizedValue: JSON.parse(result.normalizedJson),
26048
+ decodedValueHash: result.decodedValueHash,
26049
+ decodedSchema: normalizeSchemaKeys(JSON.parse(result.decodedSchemaJson)),
26050
+ decodedSchemaHash: result.decodedSchemaHash
26051
+ };
26052
+ } catch {
26053
+ return null;
26054
+ }
26055
+ }
26056
+ function buildSpanProtoBytes(input) {
26057
+ const loaded = loadBinding();
26058
+ if (!loaded) return null;
26059
+ try {
26060
+ const rustInput = {
26061
+ traceId: input.traceId,
26062
+ spanId: input.spanId,
26063
+ parentSpanId: input.parentSpanId,
26064
+ name: input.name,
26065
+ packageName: input.packageName,
26066
+ instrumentationName: input.instrumentationName,
26067
+ submoduleName: input.submoduleName,
26068
+ packageType: input.packageType,
26069
+ environment: input.environment,
26070
+ kind: input.kind,
26071
+ inputSchemaJson: JSON.stringify(denormalizeSchemaKeys(input.inputSchema)),
26072
+ outputSchemaJson: JSON.stringify(denormalizeSchemaKeys(input.outputSchema)),
26073
+ inputSchemaHash: input.inputSchemaHash,
26074
+ outputSchemaHash: input.outputSchemaHash,
26075
+ inputValueHash: input.inputValueHash,
26076
+ outputValueHash: input.outputValueHash,
26077
+ statusCode: input.statusCode,
26078
+ statusMessage: input.statusMessage,
26079
+ isPreAppStart: input.isPreAppStart,
26080
+ isRootSpan: input.isRootSpan,
26081
+ timestampSeconds: input.timestampSeconds,
26082
+ timestampNanos: input.timestampNanos,
26083
+ durationSeconds: input.durationSeconds,
26084
+ durationNanos: input.durationNanos,
26085
+ metadataJson: input.metadata === void 0 ? void 0 : JSON.stringify(input.metadata),
26086
+ inputValueJson: input.inputValue === void 0 ? void 0 : JSON.stringify(input.inputValue),
26087
+ outputValueJson: input.outputValue === void 0 ? void 0 : JSON.stringify(input.outputValue),
26088
+ inputValueProtoStructBytes: input.inputValueProtoStructBytes,
26089
+ outputValueProtoStructBytes: input.outputValueProtoStructBytes
26090
+ };
26091
+ return loaded.buildSpanProtoBytes(rustInput);
26092
+ } catch {
26093
+ return null;
26094
+ }
26095
+ }
26096
+ function buildExportSpansRequestBytes(observableServiceId, environment, sdkVersion, sdkInstanceId, spanProtoBytesList) {
26097
+ const loaded = loadBinding();
26098
+ if (!loaded) return null;
26099
+ try {
26100
+ return loaded.buildExportSpansRequestBytes(observableServiceId, environment, sdkVersion, sdkInstanceId, spanProtoBytesList);
26101
+ } catch {
26102
+ return null;
26103
+ }
26104
+ }
26105
+
26106
+ //#endregion
26107
+ //#region src/core/tracing/SpanTransformer.ts
25899
26108
  var import_src$11 = /* @__PURE__ */ __toESM(require_src$7(), 1);
25900
26109
  /**
25901
26110
  * Utility class for transforming OpenTelemetry spans to CleanSpanData
25902
26111
  */
25903
26112
  var SpanTransformer = class SpanTransformer {
26113
+ static processPayload(data, schemaMerges) {
26114
+ const rustResult = processExportPayloadJsonable(data, schemaMerges);
26115
+ if (rustResult) return {
26116
+ normalizedValue: rustResult.normalizedValue,
26117
+ schema: rustResult.decodedSchema,
26118
+ decodedValueHash: rustResult.decodedValueHash,
26119
+ decodedSchemaHash: rustResult.decodedSchemaHash
26120
+ };
26121
+ const { schema, decodedValueHash, decodedSchemaHash } = JsonSchemaHelper.generateSchemaAndHash(data, schemaMerges);
26122
+ return {
26123
+ normalizedValue: data,
26124
+ schema,
26125
+ decodedValueHash,
26126
+ decodedSchemaHash
26127
+ };
26128
+ }
25904
26129
  /**
25905
26130
  * Transform OpenTelemetry span to clean JSON format with compile-time type safety
25906
26131
  * Return type is derived from protobuf schema but uses clean JSON.
@@ -25916,7 +26141,7 @@ var SpanTransformer = class SpanTransformer {
25916
26141
  const inputData = JSON.parse(inputValueString);
25917
26142
  const inputSchemaMergesString = attributes[TdSpanAttributes.INPUT_SCHEMA_MERGES];
25918
26143
  const inputSchemaMerges = inputSchemaMergesString ? JSON.parse(inputSchemaMergesString) : void 0;
25919
- const { schema: inputSchema, decodedValueHash: inputValueHash, decodedSchemaHash: inputSchemaHash } = JsonSchemaHelper.generateSchemaAndHash(inputData, inputSchemaMerges);
26144
+ const { normalizedValue: normalizedInputData, schema: inputSchema, decodedValueHash: inputValueHash, decodedSchemaHash: inputSchemaHash } = SpanTransformer.processPayload(inputData, inputSchemaMerges);
25920
26145
  let outputData = {};
25921
26146
  let outputSchema = {
25922
26147
  type: JsonSchemaType.OBJECT,
@@ -25929,8 +26154,8 @@ var SpanTransformer = class SpanTransformer {
25929
26154
  outputData = JSON.parse(outputValueString);
25930
26155
  const outputSchemaMergesString = attributes[TdSpanAttributes.OUTPUT_SCHEMA_MERGES];
25931
26156
  const outputSchemaMerges = outputSchemaMergesString ? JSON.parse(outputSchemaMergesString) : void 0;
25932
- ({schema: outputSchema, decodedValueHash: outputValueHash, decodedSchemaHash: outputSchemaHash} = JsonSchemaHelper.generateSchemaAndHash(outputData, outputSchemaMerges));
25933
- } else ({schema: outputSchema, decodedValueHash: outputValueHash, decodedSchemaHash: outputSchemaHash} = JsonSchemaHelper.generateSchemaAndHash(outputData));
26157
+ ({normalizedValue: outputData, schema: outputSchema, decodedValueHash: outputValueHash, decodedSchemaHash: outputSchemaHash} = SpanTransformer.processPayload(outputData, outputSchemaMerges));
26158
+ } else ({normalizedValue: outputData, schema: outputSchema, decodedValueHash: outputValueHash, decodedSchemaHash: outputSchemaHash} = SpanTransformer.processPayload(outputData));
25934
26159
  let metadata = void 0;
25935
26160
  if (attributes[TdSpanAttributes.METADATA]) metadata = JSON.parse(attributes[TdSpanAttributes.METADATA]);
25936
26161
  let transformMetadata;
@@ -25940,6 +26165,35 @@ var SpanTransformer = class SpanTransformer {
25940
26165
  } catch (error) {
25941
26166
  logger.warn("[SpanTransformer] Failed to parse transform metadata", error);
25942
26167
  }
26168
+ const protoSpanBytes = buildSpanProtoBytes({
26169
+ traceId: span.spanContext().traceId,
26170
+ spanId: span.spanContext().spanId,
26171
+ parentSpanId: span.parentSpanId || "",
26172
+ name: attributes[TdSpanAttributes.NAME] || "",
26173
+ packageName,
26174
+ instrumentationName,
26175
+ submoduleName: submoduleName || "",
26176
+ packageType: attributes[TdSpanAttributes.PACKAGE_TYPE] || PackageType.UNSPECIFIED,
26177
+ environment,
26178
+ kind: span.kind,
26179
+ inputSchema,
26180
+ outputSchema,
26181
+ inputSchemaHash,
26182
+ outputSchemaHash,
26183
+ inputValueHash,
26184
+ outputValueHash,
26185
+ statusCode: span.status.code === 1 ? StatusCode.OK : StatusCode.ERROR,
26186
+ statusMessage: span.status.message || "",
26187
+ isPreAppStart: attributes[TdSpanAttributes.IS_PRE_APP_START] === true,
26188
+ isRootSpan,
26189
+ timestampSeconds: span.startTime[0],
26190
+ timestampNanos: span.startTime[1],
26191
+ durationSeconds: span.duration[0],
26192
+ durationNanos: span.duration[1],
26193
+ metadata,
26194
+ inputValue: normalizedInputData,
26195
+ outputValue: outputData
26196
+ });
25943
26197
  return {
25944
26198
  traceId: span.spanContext().traceId,
25945
26199
  spanId: span.spanContext().spanId,
@@ -25950,7 +26204,7 @@ var SpanTransformer = class SpanTransformer {
25950
26204
  submoduleName: submoduleName || "",
25951
26205
  packageType: attributes[TdSpanAttributes.PACKAGE_TYPE] ?? void 0,
25952
26206
  environment,
25953
- inputValue: inputData,
26207
+ inputValue: normalizedInputData,
25954
26208
  outputValue: outputData,
25955
26209
  inputSchema,
25956
26210
  outputSchema,
@@ -25974,7 +26228,8 @@ var SpanTransformer = class SpanTransformer {
25974
26228
  },
25975
26229
  isRootSpan,
25976
26230
  metadata,
25977
- transformMetadata
26231
+ transformMetadata,
26232
+ protoSpanBytes: protoSpanBytes ?? void 0
25978
26233
  };
25979
26234
  }
25980
26235
  /**
@@ -26398,6 +26653,8 @@ const DRIFT_API_PATH = "/api/drift";
26398
26653
  var ApiSpanAdapter = class {
26399
26654
  constructor(config) {
26400
26655
  this.name = "api";
26656
+ this.apiKey = config.apiKey;
26657
+ this.tuskBackendBaseUrl = config.tuskBackendBaseUrl;
26401
26658
  this.observableServiceId = config.observableServiceId;
26402
26659
  this.environment = config.environment;
26403
26660
  this.sdkVersion = config.sdkVersion;
@@ -26413,6 +26670,24 @@ var ApiSpanAdapter = class {
26413
26670
  }
26414
26671
  async exportSpans(spans) {
26415
26672
  try {
26673
+ const rustRequestBytes = buildExportSpansRequestBytes(this.observableServiceId, this.environment || "", this.sdkVersion, this.sdkInstanceId, spans.map((s) => s.protoSpanBytes).filter((s) => Buffer.isBuffer(s)));
26674
+ if (spans.length > 0 && spans.every((s) => Buffer.isBuffer(s.protoSpanBytes)) && rustRequestBytes) {
26675
+ const response$1 = await fetch(`${this.tuskBackendBaseUrl}${DRIFT_API_PATH}/tusk.drift.backend.v1.SpanExportService/ExportSpans`, {
26676
+ method: "POST",
26677
+ headers: {
26678
+ "x-api-key": this.apiKey,
26679
+ "x-td-skip-instrumentation": "true",
26680
+ "Content-Type": "application/protobuf"
26681
+ },
26682
+ body: new Uint8Array(rustRequestBytes)
26683
+ });
26684
+ if (!response$1.ok) throw new Error(`Remote export failed with status ${response$1.status}`);
26685
+ const responseBytes = new Uint8Array(await response$1.arrayBuffer());
26686
+ const parsed = ExportSpansResponse.fromBinary(responseBytes);
26687
+ if (!parsed.success) throw new Error(`Remote export failed: ${parsed.message}`);
26688
+ logger.debug(`Successfully exported ${spans.length} spans to remote endpoint (rust binary path)`);
26689
+ return { code: import_src$8.ExportResultCode.SUCCESS };
26690
+ }
26416
26691
  const protoSpans = spans.map((span) => this.transformSpanToProtobuf(span));
26417
26692
  const request = {
26418
26693
  observableServiceId: this.observableServiceId,
@@ -33724,7 +33999,7 @@ var require_src = /* @__PURE__ */ __commonJS({ "node_modules/@opentelemetry/sdk-
33724
33999
  //#endregion
33725
34000
  //#region package.json
33726
34001
  var import_src$1 = /* @__PURE__ */ __toESM(require_src(), 1);
33727
- var version = "0.1.30";
34002
+ var version = "0.1.31";
33728
34003
 
33729
34004
  //#endregion
33730
34005
  //#region src/version.ts
@@ -33732,7 +34007,7 @@ const SDK_VERSION = version;
33732
34007
  const MIN_CLI_VERSION = "0.1.0";
33733
34008
 
33734
34009
  //#endregion
33735
- //#region node_modules/@use-tusk/drift-schemas/dist/communication-GAvDDkJW.js
34010
+ //#region node_modules/@use-tusk/drift-schemas/dist/communication-DR7GLSjb.js
33736
34011
  var import_commonjs = /* @__PURE__ */ __toESM(require_commonjs$1(), 1);
33737
34012
  var import_commonjs$1 = /* @__PURE__ */ __toESM(require_commonjs$2(), 1);
33738
34013
  /**
@@ -35613,6 +35888,20 @@ var TuskDriftCore = class TuskDriftCore {
35613
35888
  default: return TuskDriftMode.DISABLED;
35614
35889
  }
35615
35890
  }
35891
+ logRustCoreStartupStatus() {
35892
+ const status = getRustCoreStartupStatus();
35893
+ const envDisplay = status.rawEnv ?? "<unset>";
35894
+ if (status.reason === "invalid_env_value_defaulted") logger.warn(`Invalid TUSK_USE_RUST_CORE value '${envDisplay}'; defaulting to enabled rust core path.`);
35895
+ if (!status.enabled) {
35896
+ logger.info(`Rust core path disabled at startup (env=${envDisplay}, reason=${status.reason}).`);
35897
+ return;
35898
+ }
35899
+ if (status.bindingLoaded) {
35900
+ logger.info(`Rust core path enabled at startup (env=${envDisplay}, reason=${status.reason}).`);
35901
+ return;
35902
+ }
35903
+ logger.warn(`Rust core path requested but binding unavailable; falling back to JavaScript path (env=${envDisplay}, reason=${status.reason}, error=${status.bindingError}).`);
35904
+ }
35616
35905
  validateSamplingRate(value, source) {
35617
35906
  if (typeof value !== "number" || isNaN(value)) {
35618
35907
  logger.warn(`Invalid sampling rate from ${source}: not a number. Ignoring.`);
@@ -35795,6 +36084,7 @@ var TuskDriftCore = class TuskDriftCore {
35795
36084
  logger.debug("SDK disabled via environment variable");
35796
36085
  return;
35797
36086
  }
36087
+ this.logRustCoreStartupStatus();
35798
36088
  logger.debug(`Initializing in ${this.mode} mode`);
35799
36089
  if (!this.initParams.env) {
35800
36090
  const nodeEnv = OriginalGlobalUtils.getOriginalProcessEnvVar("NODE_ENV") || "development";