@usherlabs/cex-broker 0.2.26 → 0.2.28

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
@@ -274246,6 +274246,65 @@ function validateDeposit(policy, exchange, network, ticker) {
274246
274246
  return { valid: true };
274247
274247
  }
274248
274248
 
274249
+ // src/helpers/shared/errors.ts
274250
+ var MAX_ERROR_DETAIL_LENGTH = 512;
274251
+ var REDACTED_ERROR_MESSAGE = "redacted_error";
274252
+ function getErrorMessage(error) {
274253
+ return error instanceof Error ? error.message : typeof error === "string" ? error : "Unknown error";
274254
+ }
274255
+ function errorClassName(error) {
274256
+ if (!(error instanceof Error)) {
274257
+ return;
274258
+ }
274259
+ const name = error.constructor?.name || error.name;
274260
+ return name && name !== "Error" ? name : undefined;
274261
+ }
274262
+ function errorCode(error) {
274263
+ if (error === null || typeof error !== "object") {
274264
+ return;
274265
+ }
274266
+ const code = error.code;
274267
+ if (typeof code === "string") {
274268
+ return code.trim() || undefined;
274269
+ }
274270
+ if (typeof code === "number" && Number.isFinite(code)) {
274271
+ return String(code);
274272
+ }
274273
+ if (typeof code === "bigint") {
274274
+ return String(code);
274275
+ }
274276
+ return;
274277
+ }
274278
+ function sanitizeErrorDetail(error, options = {}) {
274279
+ const className = errorClassName(error);
274280
+ const message = getErrorMessage(error);
274281
+ const code = options.includeCode ? errorCode(error) : undefined;
274282
+ const prefix = [className, code ? `[code=${code}]` : undefined].filter((part) => Boolean(part)).join(" ");
274283
+ const detail = prefix ? `${prefix}: ${message}` : message;
274284
+ return detail.replace(/\s+/g, " ").trim().slice(0, MAX_ERROR_DETAIL_LENGTH);
274285
+ }
274286
+ function safeLogError(context2, error) {
274287
+ try {
274288
+ log.error(context2, { error });
274289
+ } catch {
274290
+ console.error(context2, error);
274291
+ }
274292
+ }
274293
+ function safeLogRedactedError(context2, error) {
274294
+ const errorType = errorClassName(error) ?? (error instanceof Error ? error.name : typeof error);
274295
+ try {
274296
+ log.error(context2, {
274297
+ error_type: errorType,
274298
+ error_message: REDACTED_ERROR_MESSAGE
274299
+ });
274300
+ } catch {
274301
+ console.error(context2, {
274302
+ error_type: errorType,
274303
+ error_message: REDACTED_ERROR_MESSAGE
274304
+ });
274305
+ }
274306
+ }
274307
+
274249
274308
  // src/helpers/shared/guards.ts
274250
274309
  function isRecord(value) {
274251
274310
  return value !== null && typeof value === "object" && !Array.isArray(value);
@@ -274266,7 +274325,6 @@ var NUMERIC_METRICS = [
274266
274325
  ["feeAmount", "cex_market_action_fee_amount"],
274267
274326
  ["feeRate", "cex_market_action_fee_rate"]
274268
274327
  ];
274269
- var REDACTED_ERROR_MESSAGE = "redacted_error";
274270
274328
  async function emitOrderExecutionTelemetry(otelMetrics, context2, order, error) {
274271
274329
  try {
274272
274330
  const telemetry = buildOrderExecutionTelemetry(context2, order, error);
@@ -274758,7 +274816,7 @@ function buildOrderEventArchiveRow(input) {
274758
274816
  fee_rate: telemetry.feeRate,
274759
274817
  exchange_timestamp: telemetry.exchangeTimestamp,
274760
274818
  error_type: telemetry.errorType,
274761
- error_message: telemetry.errorMessage,
274819
+ error_message: input.errorDetail ?? telemetry.errorMessage,
274762
274820
  payload_json: JSON.stringify(telemetry)
274763
274821
  })
274764
274822
  };
@@ -274890,386 +274948,126 @@ function buildMarketMetadataSnapshotRow(input) {
274890
274948
  };
274891
274949
  }
274892
274950
 
274893
- // src/helpers/broker-execution-archive/capture.ts
274894
- function archiveOrderExecutionInBackground(archiver, context2, order, error, options) {
274895
- if (!archiver?.isEnabled()) {
274896
- return;
274951
+ // src/helpers/broker-execution-archive/writer.ts
274952
+ import { closeSync, fsyncSync, openSync, writeSync } from "fs";
274953
+ import { request as httpRequest2 } from "http";
274954
+ import { request as httpsRequest2 } from "https";
274955
+
274956
+ // node_modules/@opentelemetry/api-logs/build/esm/types/LogRecord.js
274957
+ var SeverityNumber2;
274958
+ (function(SeverityNumber3) {
274959
+ SeverityNumber3[SeverityNumber3["UNSPECIFIED"] = 0] = "UNSPECIFIED";
274960
+ SeverityNumber3[SeverityNumber3["TRACE"] = 1] = "TRACE";
274961
+ SeverityNumber3[SeverityNumber3["TRACE2"] = 2] = "TRACE2";
274962
+ SeverityNumber3[SeverityNumber3["TRACE3"] = 3] = "TRACE3";
274963
+ SeverityNumber3[SeverityNumber3["TRACE4"] = 4] = "TRACE4";
274964
+ SeverityNumber3[SeverityNumber3["DEBUG"] = 5] = "DEBUG";
274965
+ SeverityNumber3[SeverityNumber3["DEBUG2"] = 6] = "DEBUG2";
274966
+ SeverityNumber3[SeverityNumber3["DEBUG3"] = 7] = "DEBUG3";
274967
+ SeverityNumber3[SeverityNumber3["DEBUG4"] = 8] = "DEBUG4";
274968
+ SeverityNumber3[SeverityNumber3["INFO"] = 9] = "INFO";
274969
+ SeverityNumber3[SeverityNumber3["INFO2"] = 10] = "INFO2";
274970
+ SeverityNumber3[SeverityNumber3["INFO3"] = 11] = "INFO3";
274971
+ SeverityNumber3[SeverityNumber3["INFO4"] = 12] = "INFO4";
274972
+ SeverityNumber3[SeverityNumber3["WARN"] = 13] = "WARN";
274973
+ SeverityNumber3[SeverityNumber3["WARN2"] = 14] = "WARN2";
274974
+ SeverityNumber3[SeverityNumber3["WARN3"] = 15] = "WARN3";
274975
+ SeverityNumber3[SeverityNumber3["WARN4"] = 16] = "WARN4";
274976
+ SeverityNumber3[SeverityNumber3["ERROR"] = 17] = "ERROR";
274977
+ SeverityNumber3[SeverityNumber3["ERROR2"] = 18] = "ERROR2";
274978
+ SeverityNumber3[SeverityNumber3["ERROR3"] = 19] = "ERROR3";
274979
+ SeverityNumber3[SeverityNumber3["ERROR4"] = 20] = "ERROR4";
274980
+ SeverityNumber3[SeverityNumber3["FATAL"] = 21] = "FATAL";
274981
+ SeverityNumber3[SeverityNumber3["FATAL2"] = 22] = "FATAL2";
274982
+ SeverityNumber3[SeverityNumber3["FATAL3"] = 23] = "FATAL3";
274983
+ SeverityNumber3[SeverityNumber3["FATAL4"] = 24] = "FATAL4";
274984
+ })(SeverityNumber2 || (SeverityNumber2 = {}));
274985
+ // node_modules/@opentelemetry/api-logs/build/esm/NoopLogger.js
274986
+ class NoopLogger2 {
274987
+ emit(_logRecord) {}
274988
+ }
274989
+ var NOOP_LOGGER2 = new NoopLogger2;
274990
+ // node_modules/@opentelemetry/api-logs/build/esm/internal/global-utils.js
274991
+ var GLOBAL_LOGS_API_KEY2 = Symbol.for("io.opentelemetry.js.api.logs");
274992
+ var _global3 = globalThis;
274993
+ function makeGetter2(requiredVersion, instance, fallback) {
274994
+ return (version3) => version3 === requiredVersion ? instance : fallback;
274995
+ }
274996
+ var API_BACKWARDS_COMPATIBILITY_VERSION2 = 1;
274997
+
274998
+ // node_modules/@opentelemetry/api-logs/build/esm/NoopLoggerProvider.js
274999
+ class NoopLoggerProvider2 {
275000
+ getLogger(_name, _version, _options) {
275001
+ return new NoopLogger2;
274897
275002
  }
274898
- queueMicrotask(() => {
274899
- try {
274900
- const telemetry = buildOrderExecutionTelemetry(context2, order, error);
274901
- const tags = buildCommonArchiveTags({
274902
- deploymentId: archiver.getDeploymentId(),
274903
- accountSelector: context2.accountLabel,
274904
- exchange: context2.cex,
274905
- symbol: telemetry.symbol,
274906
- brokerObservedTimestamp: telemetry.brokerObservedTimestamp
274907
- });
274908
- archiver.enqueue(buildOrderEventArchiveRow({
274909
- tags,
274910
- action: context2.action,
274911
- telemetry,
274912
- marketMetadataHash: options?.marketMetadataHash
274913
- }));
274914
- } catch (archiveError) {
274915
- log.warn("Failed to archive order execution", { error: archiveError });
274916
- }
274917
- });
274918
275003
  }
274919
- function archiveSubscribeStreamInBackground(archiver, input) {
274920
- if (!archiver?.isEnabled()) {
274921
- return;
275004
+ var NOOP_LOGGER_PROVIDER2 = new NoopLoggerProvider2;
275005
+
275006
+ // node_modules/@opentelemetry/api-logs/build/esm/ProxyLogger.js
275007
+ class ProxyLogger2 {
275008
+ constructor(provider, name, version3, options) {
275009
+ this._provider = provider;
275010
+ this.name = name;
275011
+ this.version = version3;
275012
+ this.options = options;
274922
275013
  }
274923
- queueMicrotask(() => {
274924
- try {
274925
- const tags = buildCommonArchiveTags({
274926
- deploymentId: archiver.getDeploymentId(),
274927
- accountSelector: input.accountSelector,
274928
- exchange: input.exchange,
274929
- symbol: input.symbol
274930
- });
274931
- archiver.enqueue(buildSubscribeStreamArchiveRow({
274932
- tags,
274933
- subscriptionType: input.subscriptionType,
274934
- streamPayload: input.streamPayload,
274935
- secretLiterals: input.secretLiterals
274936
- }));
274937
- } catch (archiveError) {
274938
- log.warn("Failed to archive subscribe stream event", {
274939
- error: archiveError
274940
- });
275014
+ emit(logRecord) {
275015
+ this._getLogger().emit(logRecord);
275016
+ }
275017
+ _getLogger() {
275018
+ if (this._delegate) {
275019
+ return this._delegate;
274941
275020
  }
274942
- });
275021
+ const logger = this._provider._getDelegateLogger(this.name, this.version, this.options);
275022
+ if (!logger) {
275023
+ return NOOP_LOGGER2;
275024
+ }
275025
+ this._delegate = logger;
275026
+ return this._delegate;
275027
+ }
274943
275028
  }
274944
- function archiveTransferEventInBackground(archiver, input) {
274945
- if (!archiver?.isEnabled()) {
274946
- return;
275029
+
275030
+ // node_modules/@opentelemetry/api-logs/build/esm/ProxyLoggerProvider.js
275031
+ class ProxyLoggerProvider2 {
275032
+ getLogger(name, version3, options) {
275033
+ var _a3;
275034
+ return (_a3 = this._getDelegateLogger(name, version3, options)) !== null && _a3 !== undefined ? _a3 : new ProxyLogger2(this, name, version3, options);
275035
+ }
275036
+ _getDelegate() {
275037
+ var _a3;
275038
+ return (_a3 = this._delegate) !== null && _a3 !== undefined ? _a3 : NOOP_LOGGER_PROVIDER2;
275039
+ }
275040
+ _setDelegate(delegate) {
275041
+ this._delegate = delegate;
275042
+ }
275043
+ _getDelegateLogger(name, version3, options) {
275044
+ var _a3;
275045
+ return (_a3 = this._delegate) === null || _a3 === undefined ? undefined : _a3.getLogger(name, version3, options);
274947
275046
  }
274948
- queueMicrotask(() => {
274949
- try {
274950
- const tags = buildCommonArchiveTags({
274951
- deploymentId: archiver.getDeploymentId(),
274952
- accountSelector: input.accountSelector,
274953
- exchange: input.exchange,
274954
- symbol: input.assetSymbol,
274955
- brokerObservedTimestamp: input.brokerObservedTimestamp
274956
- });
274957
- archiver.enqueue(buildTransferEventArchiveRow({ tags, transfer: input.transfer }));
274958
- } catch (archiveError) {
274959
- log.warn("Failed to archive transfer event", { error: archiveError });
274960
- }
274961
- });
274962
275047
  }
274963
- function archiveWithdrawalObservationsInBackground(archiver, tracker, input) {
274964
- try {
274965
- if (!archiver?.isEnabled() || !Array.isArray(input.transactions)) {
274966
- return;
275048
+
275049
+ // node_modules/@opentelemetry/api-logs/build/esm/api/logs.js
275050
+ class LogsAPI2 {
275051
+ constructor() {
275052
+ this._proxyLoggerProvider = new ProxyLoggerProvider2;
275053
+ }
275054
+ static getInstance() {
275055
+ if (!this._instance) {
275056
+ this._instance = new LogsAPI2;
274967
275057
  }
274968
- const brokerObservedTimestamp = new Date().toISOString();
274969
- for (const [resultIndex, transaction] of input.transactions.entries()) {
274970
- const normalized = normalizeCcxtTransactionForArchive(transaction);
274971
- const assetSymbol = normalized.assetSymbol;
274972
- if (!tracker.shouldArchive({
274973
- exchange: input.exchange,
274974
- accountSelector: input.accountSelector,
274975
- assetSymbol,
274976
- transaction,
274977
- normalized
274978
- })) {
274979
- continue;
274980
- }
274981
- archiveTransferEventInBackground(archiver, {
274982
- exchange: input.exchange,
274983
- accountSelector: input.accountSelector,
274984
- assetSymbol,
274985
- brokerObservedTimestamp,
274986
- transfer: {
274987
- eventKind: "withdrawal",
274988
- lifecycleAction: "observe_withdrawal",
274989
- status: normalized.status,
274990
- amount: normalized.amount,
274991
- address: normalized.address,
274992
- network: normalized.network,
274993
- externalId: normalized.externalId,
274994
- txid: normalized.txid,
274995
- resultIndex,
274996
- feeAmount: normalized.feeAmount,
274997
- feeCurrency: normalized.feeCurrency,
274998
- exchangeTimestamp: normalized.exchangeTimestamp,
274999
- payload: transaction
275000
- }
275001
- });
275058
+ return this._instance;
275059
+ }
275060
+ setGlobalLoggerProvider(provider) {
275061
+ if (_global3[GLOBAL_LOGS_API_KEY2]) {
275062
+ return this.getLoggerProvider();
275002
275063
  }
275003
- } catch (archiveError) {
275004
- log.warn("Failed to archive withdrawal observations", {
275005
- error: archiveError
275006
- });
275064
+ _global3[GLOBAL_LOGS_API_KEY2] = makeGetter2(API_BACKWARDS_COMPATIBILITY_VERSION2, provider, NOOP_LOGGER_PROVIDER2);
275065
+ this._proxyLoggerProvider._setDelegate(provider);
275066
+ return provider;
275007
275067
  }
275008
- }
275009
- async function captureMarketMetadataSnapshot(archiver, broker, input) {
275010
- if (!archiver?.canPersistMarketMetadataSnapshot()) {
275011
- return;
275012
- }
275013
- try {
275014
- const fetchOrderBook = broker.fetchOrderBook;
275015
- if (typeof fetchOrderBook !== "function") {
275016
- return;
275017
- }
275018
- const orderBook = await fetchOrderBook.call(broker, input.symbol, 5);
275019
- const record = asRecord(orderBook);
275020
- const snapshot = {
275021
- action: input.action,
275022
- symbol: input.symbol,
275023
- bids: record?.bids,
275024
- asks: record?.asks,
275025
- timestamp: record?.timestamp ?? Date.now(),
275026
- datetime: record?.datetime,
275027
- nonce: record?.nonce
275028
- };
275029
- const tags = buildCommonArchiveTags({
275030
- deploymentId: archiver.getDeploymentId(),
275031
- accountSelector: input.accountSelector,
275032
- exchange: input.exchange,
275033
- symbol: input.symbol,
275034
- brokerObservedTimestamp: input.brokerObservedTimestamp
275035
- });
275036
- const row = buildMarketMetadataSnapshotRow({
275037
- tags,
275038
- clientOrderId: input.clientOrderId,
275039
- orderId: input.orderId,
275040
- makerActionId: input.makerActionId,
275041
- idempotencyId: input.idempotencyId,
275042
- marketSnapshot: snapshot
275043
- });
275044
- archiver.enqueue(row);
275045
- const hash4 = row.row.market_metadata_hash;
275046
- return typeof hash4 === "string" ? hash4 : undefined;
275047
- } catch (archiveError) {
275048
- log.warn("Failed to capture market metadata snapshot", {
275049
- error: archiveError
275050
- });
275051
- return;
275052
- }
275053
- }
275054
- // src/helpers/broker-execution-archive/withdrawal-observation-tracker.ts
275055
- var DEFAULT_WITHDRAWAL_OBSERVATION_TRACKER_MAX_ENTRIES = 1e4;
275056
- var VENUE_LIFECYCLE_EVIDENCE_FIELDS = [
275057
- "updated",
275058
- "updatedAt",
275059
- "updated_at",
275060
- "updateTime",
275061
- "update_time",
275062
- "updateTimestamp",
275063
- "lastUpdateTimestamp",
275064
- "completed",
275065
- "completedAt",
275066
- "completed_at",
275067
- "completeTime",
275068
- "complete_time",
275069
- "completionTime",
275070
- "successTime"
275071
- ];
275072
- function fingerprintEvidenceValue(value) {
275073
- if (value === undefined || value === null) {
275074
- return value;
275075
- }
275076
- if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
275077
- return value;
275078
- }
275079
- if (typeof value === "bigint") {
275080
- return value.toString();
275081
- }
275082
- try {
275083
- return JSON.stringify(value);
275084
- } catch {
275085
- return String(value);
275086
- }
275087
- }
275088
- function venueLifecycleEvidence(transaction) {
275089
- const record = asRecord(transaction);
275090
- const info = asRecord(record?.info);
275091
- const evidence = [];
275092
- for (const [scope, source] of [
275093
- ["transaction", record],
275094
- ["info", info]
275095
- ]) {
275096
- for (const field of VENUE_LIFECYCLE_EVIDENCE_FIELDS) {
275097
- const value = source?.[field];
275098
- if (value !== undefined && value !== null) {
275099
- evidence.push([scope, field, fingerprintEvidenceValue(value)]);
275100
- }
275101
- }
275102
- }
275103
- return evidence;
275104
- }
275105
- function observationFingerprint(observation) {
275106
- const { normalized, transaction } = observation;
275107
- return JSON.stringify({
275108
- status: normalized.status ?? null,
275109
- txid: normalized.txid ?? null,
275110
- amount: normalized.amount ?? null,
275111
- feeAmount: normalized.feeAmount ?? null,
275112
- feeCurrency: normalized.feeCurrency ?? null,
275113
- address: normalized.address ?? null,
275114
- network: normalized.network ?? null,
275115
- exchangeTimestamp: normalized.exchangeTimestamp ?? null,
275116
- venueLifecycleEvidence: venueLifecycleEvidence(transaction)
275117
- });
275118
- }
275119
-
275120
- class WithdrawalObservationTracker {
275121
- #fingerprints = new Map;
275122
- #maxEntries;
275123
- #missingIdSequence = 0n;
275124
- constructor(options) {
275125
- const maxEntries = options?.maxEntries ?? DEFAULT_WITHDRAWAL_OBSERVATION_TRACKER_MAX_ENTRIES;
275126
- this.#maxEntries = Number.isFinite(maxEntries) ? Math.max(1, Math.floor(maxEntries)) : DEFAULT_WITHDRAWAL_OBSERVATION_TRACKER_MAX_ENTRIES;
275127
- }
275128
- shouldArchive(observation) {
275129
- const externalId = observation.normalized.externalId?.trim();
275130
- const identity = JSON.stringify([
275131
- observation.exchange.trim().toLowerCase() || "unknown",
275132
- observation.accountSelector?.trim() || "unknown",
275133
- observation.assetSymbol?.trim().toUpperCase() || "unknown",
275134
- externalId || `missing:${++this.#missingIdSequence}`
275135
- ]);
275136
- const fingerprint = observationFingerprint(observation);
275137
- if (this.#fingerprints.get(identity) === fingerprint) {
275138
- return false;
275139
- }
275140
- this.#fingerprints.delete(identity);
275141
- this.#fingerprints.set(identity, fingerprint);
275142
- while (this.#fingerprints.size > this.#maxEntries) {
275143
- const oldest = this.#fingerprints.keys().next().value;
275144
- if (oldest === undefined)
275145
- break;
275146
- this.#fingerprints.delete(oldest);
275147
- }
275148
- return true;
275149
- }
275150
- getSize() {
275151
- return this.#fingerprints.size;
275152
- }
275153
- }
275154
- // src/helpers/broker-execution-archive/writer.ts
275155
- import { request as httpRequest2 } from "http";
275156
- import { request as httpsRequest2 } from "https";
275157
-
275158
- // node_modules/@opentelemetry/api-logs/build/esm/types/LogRecord.js
275159
- var SeverityNumber2;
275160
- (function(SeverityNumber3) {
275161
- SeverityNumber3[SeverityNumber3["UNSPECIFIED"] = 0] = "UNSPECIFIED";
275162
- SeverityNumber3[SeverityNumber3["TRACE"] = 1] = "TRACE";
275163
- SeverityNumber3[SeverityNumber3["TRACE2"] = 2] = "TRACE2";
275164
- SeverityNumber3[SeverityNumber3["TRACE3"] = 3] = "TRACE3";
275165
- SeverityNumber3[SeverityNumber3["TRACE4"] = 4] = "TRACE4";
275166
- SeverityNumber3[SeverityNumber3["DEBUG"] = 5] = "DEBUG";
275167
- SeverityNumber3[SeverityNumber3["DEBUG2"] = 6] = "DEBUG2";
275168
- SeverityNumber3[SeverityNumber3["DEBUG3"] = 7] = "DEBUG3";
275169
- SeverityNumber3[SeverityNumber3["DEBUG4"] = 8] = "DEBUG4";
275170
- SeverityNumber3[SeverityNumber3["INFO"] = 9] = "INFO";
275171
- SeverityNumber3[SeverityNumber3["INFO2"] = 10] = "INFO2";
275172
- SeverityNumber3[SeverityNumber3["INFO3"] = 11] = "INFO3";
275173
- SeverityNumber3[SeverityNumber3["INFO4"] = 12] = "INFO4";
275174
- SeverityNumber3[SeverityNumber3["WARN"] = 13] = "WARN";
275175
- SeverityNumber3[SeverityNumber3["WARN2"] = 14] = "WARN2";
275176
- SeverityNumber3[SeverityNumber3["WARN3"] = 15] = "WARN3";
275177
- SeverityNumber3[SeverityNumber3["WARN4"] = 16] = "WARN4";
275178
- SeverityNumber3[SeverityNumber3["ERROR"] = 17] = "ERROR";
275179
- SeverityNumber3[SeverityNumber3["ERROR2"] = 18] = "ERROR2";
275180
- SeverityNumber3[SeverityNumber3["ERROR3"] = 19] = "ERROR3";
275181
- SeverityNumber3[SeverityNumber3["ERROR4"] = 20] = "ERROR4";
275182
- SeverityNumber3[SeverityNumber3["FATAL"] = 21] = "FATAL";
275183
- SeverityNumber3[SeverityNumber3["FATAL2"] = 22] = "FATAL2";
275184
- SeverityNumber3[SeverityNumber3["FATAL3"] = 23] = "FATAL3";
275185
- SeverityNumber3[SeverityNumber3["FATAL4"] = 24] = "FATAL4";
275186
- })(SeverityNumber2 || (SeverityNumber2 = {}));
275187
- // node_modules/@opentelemetry/api-logs/build/esm/NoopLogger.js
275188
- class NoopLogger2 {
275189
- emit(_logRecord) {}
275190
- }
275191
- var NOOP_LOGGER2 = new NoopLogger2;
275192
- // node_modules/@opentelemetry/api-logs/build/esm/internal/global-utils.js
275193
- var GLOBAL_LOGS_API_KEY2 = Symbol.for("io.opentelemetry.js.api.logs");
275194
- var _global3 = globalThis;
275195
- function makeGetter2(requiredVersion, instance, fallback) {
275196
- return (version3) => version3 === requiredVersion ? instance : fallback;
275197
- }
275198
- var API_BACKWARDS_COMPATIBILITY_VERSION2 = 1;
275199
-
275200
- // node_modules/@opentelemetry/api-logs/build/esm/NoopLoggerProvider.js
275201
- class NoopLoggerProvider2 {
275202
- getLogger(_name, _version, _options) {
275203
- return new NoopLogger2;
275204
- }
275205
- }
275206
- var NOOP_LOGGER_PROVIDER2 = new NoopLoggerProvider2;
275207
-
275208
- // node_modules/@opentelemetry/api-logs/build/esm/ProxyLogger.js
275209
- class ProxyLogger2 {
275210
- constructor(provider, name, version3, options) {
275211
- this._provider = provider;
275212
- this.name = name;
275213
- this.version = version3;
275214
- this.options = options;
275215
- }
275216
- emit(logRecord) {
275217
- this._getLogger().emit(logRecord);
275218
- }
275219
- _getLogger() {
275220
- if (this._delegate) {
275221
- return this._delegate;
275222
- }
275223
- const logger = this._provider._getDelegateLogger(this.name, this.version, this.options);
275224
- if (!logger) {
275225
- return NOOP_LOGGER2;
275226
- }
275227
- this._delegate = logger;
275228
- return this._delegate;
275229
- }
275230
- }
275231
-
275232
- // node_modules/@opentelemetry/api-logs/build/esm/ProxyLoggerProvider.js
275233
- class ProxyLoggerProvider2 {
275234
- getLogger(name, version3, options) {
275235
- var _a3;
275236
- return (_a3 = this._getDelegateLogger(name, version3, options)) !== null && _a3 !== undefined ? _a3 : new ProxyLogger2(this, name, version3, options);
275237
- }
275238
- _getDelegate() {
275239
- var _a3;
275240
- return (_a3 = this._delegate) !== null && _a3 !== undefined ? _a3 : NOOP_LOGGER_PROVIDER2;
275241
- }
275242
- _setDelegate(delegate) {
275243
- this._delegate = delegate;
275244
- }
275245
- _getDelegateLogger(name, version3, options) {
275246
- var _a3;
275247
- return (_a3 = this._delegate) === null || _a3 === undefined ? undefined : _a3.getLogger(name, version3, options);
275248
- }
275249
- }
275250
-
275251
- // node_modules/@opentelemetry/api-logs/build/esm/api/logs.js
275252
- class LogsAPI2 {
275253
- constructor() {
275254
- this._proxyLoggerProvider = new ProxyLoggerProvider2;
275255
- }
275256
- static getInstance() {
275257
- if (!this._instance) {
275258
- this._instance = new LogsAPI2;
275259
- }
275260
- return this._instance;
275261
- }
275262
- setGlobalLoggerProvider(provider) {
275263
- if (_global3[GLOBAL_LOGS_API_KEY2]) {
275264
- return this.getLoggerProvider();
275265
- }
275266
- _global3[GLOBAL_LOGS_API_KEY2] = makeGetter2(API_BACKWARDS_COMPATIBILITY_VERSION2, provider, NOOP_LOGGER_PROVIDER2);
275267
- this._proxyLoggerProvider._setDelegate(provider);
275268
- return provider;
275269
- }
275270
- getLoggerProvider() {
275271
- var _a3, _b2;
275272
- return (_b2 = (_a3 = _global3[GLOBAL_LOGS_API_KEY2]) === null || _a3 === undefined ? undefined : _a3.call(_global3, API_BACKWARDS_COMPATIBILITY_VERSION2)) !== null && _b2 !== undefined ? _b2 : this._proxyLoggerProvider;
275068
+ getLoggerProvider() {
275069
+ var _a3, _b2;
275070
+ return (_b2 = (_a3 = _global3[GLOBAL_LOGS_API_KEY2]) === null || _a3 === undefined ? undefined : _a3.call(_global3, API_BACKWARDS_COMPATIBILITY_VERSION2)) !== null && _b2 !== undefined ? _b2 : this._proxyLoggerProvider;
275273
275071
  }
275274
275072
  getLogger(name, version3, options) {
275275
275073
  return this.getLoggerProvider().getLogger(name, version3, options);
@@ -275283,18 +275081,6 @@ class LogsAPI2 {
275283
275081
  // node_modules/@opentelemetry/api-logs/build/esm/index.js
275284
275082
  var logs2 = LogsAPI2.getInstance();
275285
275083
 
275286
- // src/helpers/market-data-archive/types.ts
275287
- var MARKET_ARCHIVE_TABLES = new Set([
275288
- "market_data.orderbook_snapshots",
275289
- "market_data.candles",
275290
- "market_data.cex_stream_events",
275291
- "market_data.cex_ticker_events",
275292
- "market_data.cex_trades"
275293
- ]);
275294
- function isMarketArchiveTable(table) {
275295
- return MARKET_ARCHIVE_TABLES.has(table);
275296
- }
275297
-
275298
275084
  // src/helpers/broker-execution-archive/writer.ts
275299
275085
  var BROKER_EXECUTION_ARCHIVE_TABLES = new Set([
275300
275086
  "broker_execution.order_events",
@@ -275305,29 +275091,28 @@ var BROKER_EXECUTION_ARCHIVE_TABLES = new Set([
275305
275091
  function isBrokerExecutionArchiveTable(table) {
275306
275092
  return BROKER_EXECUTION_ARCHIVE_TABLES.has(table);
275307
275093
  }
275094
+
275095
+ class BrokerExecutionArchiveDurabilityError extends Error {
275096
+ constructor(message, options) {
275097
+ super(message, options);
275098
+ this.name = "BrokerExecutionArchiveDurabilityError";
275099
+ }
275100
+ }
275101
+ function rethrowArchiveDurabilityError(error) {
275102
+ if (error instanceof BrokerExecutionArchiveDurabilityError) {
275103
+ throw error;
275104
+ }
275105
+ }
275308
275106
  var DEFAULT_MAX_QUEUE_SIZE = 1e4;
275309
275107
  var DEFAULT_BATCH_SIZE = 10;
275310
275108
  var DEFAULT_FLUSH_INTERVAL_MS = 1000;
275311
275109
  var DEFAULT_FORWARDER_TIMEOUT_MS = 3000;
275312
- var DEFAULT_ARCHIVE_FORWARDER_PATH = "/archive";
275313
- var DEFAULT_ARCHIVE_FORWARDER_PORT = 8090;
275110
+ var SHED_WARN_INTERVAL_MS = 60000;
275314
275111
  function isArchiveOtelLogsEnabled() {
275315
275112
  return process.env.CEX_BROKER_ARCHIVE_OTEL_LOGS_ENABLED === "true";
275316
275113
  }
275317
275114
  function resolveArchiveForwarderUrlFromEnv() {
275318
- const explicit = process.env.CEX_BROKER_ARCHIVE_FORWARDER_URL?.trim();
275319
- if (explicit) {
275320
- return explicit;
275321
- }
275322
- const host = process.env.CEX_BROKER_ARCHIVE_FORWARDER_HOST?.trim() || process.env.CEX_BROKER_CLICKHOUSE_HOST?.trim();
275323
- if (!host) {
275324
- return;
275325
- }
275326
- const protocol = process.env.CEX_BROKER_CLICKHOUSE_PROTOCOL || "http";
275327
- const port = parsePositiveInt(process.env.CEX_BROKER_ARCHIVE_FORWARDER_PORT, DEFAULT_ARCHIVE_FORWARDER_PORT);
275328
- const path = process.env.CEX_BROKER_ARCHIVE_FORWARDER_PATH?.trim() || DEFAULT_ARCHIVE_FORWARDER_PATH;
275329
- const normalizedPath = path.startsWith("/") ? path : `/${path}`;
275330
- return `${protocol}://${host}:${port}${normalizedPath}`;
275115
+ return process.env.CEX_BROKER_ARCHIVE_FORWARDER_URL?.trim() || undefined;
275331
275116
  }
275332
275117
 
275333
275118
  class BrokerExecutionArchiver {
@@ -275335,6 +275120,8 @@ class BrokerExecutionArchiver {
275335
275120
  otelLogs;
275336
275121
  otelMetrics;
275337
275122
  forwarderUrl;
275123
+ deadLetterPath;
275124
+ deadLetterFd;
275338
275125
  maxQueueSize;
275339
275126
  batchSize;
275340
275127
  flushIntervalMs;
@@ -275348,8 +275135,8 @@ class BrokerExecutionArchiver {
275348
275135
  };
275349
275136
  flushTimer = null;
275350
275137
  flushInFlight = null;
275138
+ lastShedWarnAtMs = 0;
275351
275139
  closed = false;
275352
- loggedMissingMarketForwarder = false;
275353
275140
  enabled;
275354
275141
  forwarderAuthToken;
275355
275142
  constructor(options) {
@@ -275357,61 +275144,81 @@ class BrokerExecutionArchiver {
275357
275144
  this.otelLogs = options.otelLogs;
275358
275145
  this.otelMetrics = options.otelMetrics;
275359
275146
  this.forwarderUrl = options.forwarderUrl?.trim();
275147
+ this.deadLetterPath = options.deadLetterPath?.trim();
275360
275148
  this.maxQueueSize = options.maxQueueSize ?? DEFAULT_MAX_QUEUE_SIZE;
275361
275149
  this.batchSize = options.batchSize ?? DEFAULT_BATCH_SIZE;
275362
275150
  this.flushIntervalMs = options.flushIntervalMs ?? DEFAULT_FLUSH_INTERVAL_MS;
275363
275151
  this.forwarderTimeoutMs = options.forwarderTimeoutMs ?? DEFAULT_FORWARDER_TIMEOUT_MS;
275364
275152
  this.enabled = options.enabled;
275365
275153
  this.forwarderAuthToken = process.env.CEX_BROKER_ARCHIVE_FORWARDER_TOKEN?.trim() || undefined;
275366
- if (this.enabled) {
275154
+ if (!this.enabled) {
275155
+ log.info("Broker execution archive disabled", { enabled: false });
275156
+ return;
275157
+ }
275158
+ validateForwarderUrl(this.forwarderUrl);
275159
+ if (!this.deadLetterPath) {
275160
+ throw new Error("Broker execution archive is enabled but CEX_BROKER_ARCHIVE_DEAD_LETTER_PATH is missing");
275161
+ }
275162
+ try {
275163
+ this.deadLetterFd = openSync(this.deadLetterPath, "a", 384);
275164
+ } catch {
275165
+ throw new Error("Broker execution archive cannot open CEX_BROKER_ARCHIVE_DEAD_LETTER_PATH for append");
275166
+ }
275167
+ try {
275367
275168
  this.flushTimer = setInterval(() => {
275368
- this.flush().catch((error) => {
275369
- log.warn("Broker execution archive flush failed", { error });
275370
- });
275169
+ this.flush();
275371
275170
  }, this.flushIntervalMs);
275372
275171
  this.flushTimer.unref?.();
275172
+ log.info("Broker execution archive enabled", {
275173
+ enabled: true,
275174
+ otel_mirror_enabled: Boolean(this.otelLogs?.isOtelEnabled())
275175
+ });
275176
+ } catch (error) {
275177
+ this.closeLossJournal();
275178
+ throw error;
275373
275179
  }
275374
275180
  }
275375
275181
  static disabled() {
275376
275182
  return new BrokerExecutionArchiver({ enabled: false });
275377
275183
  }
275378
275184
  static create(options) {
275379
- const hasSink = Boolean(options.otelLogs?.isOtelEnabled()) || Boolean(options.forwarderUrl);
275380
- const enabled = process.env.CEX_BROKER_ARCHIVE_ENABLED !== "false" && hasSink;
275381
- return new BrokerExecutionArchiver({ ...options, enabled });
275185
+ return new BrokerExecutionArchiver({ ...options, enabled: true });
275382
275186
  }
275383
275187
  getDeploymentId() {
275384
275188
  return this.deploymentId;
275385
275189
  }
275386
275190
  isEnabled() {
275387
- return this.enabled && !this.closed && (Boolean(this.otelLogs) || Boolean(this.forwarderUrl));
275191
+ return this.enabled && !this.closed;
275388
275192
  }
275389
275193
  canPersistMarketMetadataSnapshot() {
275390
- return this.isEnabled() && (Boolean(this.otelLogs?.isOtelEnabled()) || Boolean(this.forwarderUrl));
275194
+ return this.isEnabled();
275391
275195
  }
275392
275196
  canPersistAccountBalanceSnapshots() {
275393
275197
  return this.isEnabled() && Boolean(this.forwarderUrl);
275394
275198
  }
275395
275199
  enqueue(row) {
275396
- if (!this.enabled || this.closed || !this.otelLogs && !this.forwarderUrl) {
275397
- return;
275398
- }
275399
- if (isMarketArchiveTable(row.table) && !this.forwarderUrl) {
275400
- if (!this.loggedMissingMarketForwarder) {
275401
- this.loggedMissingMarketForwarder = true;
275402
- log.warn("Market data archive row dropped: configure CEX_BROKER_ARCHIVE_FORWARDER_URL or CEX_BROKER_CLICKHOUSE_HOST", { table: row.table });
275403
- }
275404
- return;
275405
- }
275406
- if (row.table === "broker_account.balance_snapshots" && !this.forwarderUrl) {
275200
+ if (!this.enabled || this.closed) {
275407
275201
  return;
275408
275202
  }
275409
275203
  if (this.queue.length >= this.maxQueueSize) {
275410
- this.queue.shift();
275204
+ const shedRow = this.queue[0];
275205
+ if (shedRow) {
275206
+ this.appendLossRecords([shedRow], "queue_shed");
275207
+ this.queue.shift();
275208
+ }
275411
275209
  this.stats.shed += 1;
275412
275210
  this.recordArchiveMetric("cex_archive_rows_shed_total", {
275413
- table: row.table
275211
+ table: shedRow?.table ?? "unknown"
275414
275212
  });
275213
+ const now3 = Date.now();
275214
+ if (now3 - this.lastShedWarnAtMs >= SHED_WARN_INTERVAL_MS) {
275215
+ log.warn("Archive queue full: shedding oldest rows", {
275216
+ shed_total: this.stats.shed,
275217
+ queue_max: this.maxQueueSize,
275218
+ table: shedRow?.table ?? "unknown"
275219
+ });
275220
+ this.lastShedWarnAtMs = now3;
275221
+ }
275415
275222
  }
275416
275223
  this.queue.push(row);
275417
275224
  this.stats.enqueued += 1;
@@ -275419,9 +275226,7 @@ class BrokerExecutionArchiver {
275419
275226
  table: row.table
275420
275227
  });
275421
275228
  if (this.queue.length >= this.batchSize) {
275422
- this.flush().catch((error) => {
275423
- log.warn("Broker execution archive flush failed", { error });
275424
- });
275229
+ this.flush();
275425
275230
  }
275426
275231
  }
275427
275232
  enqueueInBackground(row) {
@@ -275446,22 +275251,37 @@ class BrokerExecutionArchiver {
275446
275251
  if (this.flushTimer) {
275447
275252
  clearInterval(this.flushTimer);
275448
275253
  this.flushTimer = null;
275449
- }
275450
- while (this.queue.length > 0 || this.flushInFlight) {
275451
- if (this.flushInFlight) {
275452
- await this.flushInFlight;
275453
- continue;
275454
- }
275455
- const depthBefore = this.queue.length;
275456
- const flushed = await this.flushBatch();
275457
- if (!flushed && this.queue.length >= depthBefore && depthBefore > 0) {
275458
- const dropped = this.queue.length;
275459
- this.queue.length = 0;
275460
- log.warn(`Archive shutdown dropped ${dropped} undelivered row(s) after forwarder failure`);
275461
- break;
275254
+ }
275255
+ let closeError;
275256
+ try {
275257
+ while (this.queue.length > 0 || this.flushInFlight) {
275258
+ if (this.flushInFlight) {
275259
+ await this.flushInFlight;
275260
+ continue;
275261
+ }
275262
+ const depthBefore = this.queue.length;
275263
+ const flushed = await this.flushBatch();
275264
+ if (!flushed && this.queue.length >= depthBefore && depthBefore > 0) {
275265
+ const undelivered = [...this.queue];
275266
+ this.appendLossRecords(undelivered, "shutdown_forwarder_failure");
275267
+ this.queue.length = 0;
275268
+ break;
275269
+ }
275462
275270
  }
275271
+ } catch (error) {
275272
+ closeError = error;
275463
275273
  }
275464
275274
  this.closed = true;
275275
+ if (this.deadLetterFd !== undefined) {
275276
+ try {
275277
+ this.closeLossJournal();
275278
+ } catch (error) {
275279
+ closeError ??= error;
275280
+ }
275281
+ }
275282
+ if (closeError) {
275283
+ throw closeError;
275284
+ }
275465
275285
  }
275466
275286
  getStats() {
275467
275287
  return { ...this.stats };
@@ -275469,15 +275289,60 @@ class BrokerExecutionArchiver {
275469
275289
  getQueueDepth() {
275470
275290
  return this.queue.length;
275471
275291
  }
275292
+ closeLossJournal() {
275293
+ if (this.deadLetterFd === undefined) {
275294
+ return;
275295
+ }
275296
+ const fd2 = this.deadLetterFd;
275297
+ try {
275298
+ closeSync(fd2);
275299
+ } catch (error) {
275300
+ throw new BrokerExecutionArchiveDurabilityError("Broker execution archive failed to close the configured CEX_BROKER_ARCHIVE_DEAD_LETTER_PATH loss journal", { cause: error });
275301
+ } finally {
275302
+ this.deadLetterFd = undefined;
275303
+ }
275304
+ }
275472
275305
  enforceQueueBound() {
275473
275306
  while (this.queue.length > this.maxQueueSize) {
275474
- const dropped = this.queue.shift();
275307
+ const dropped = this.queue[0];
275308
+ if (!dropped) {
275309
+ return;
275310
+ }
275311
+ this.appendLossRecords([dropped], "queue_shed");
275312
+ this.queue.shift();
275475
275313
  this.stats.shed += 1;
275476
275314
  this.recordArchiveMetric("cex_archive_rows_shed_total", {
275477
275315
  table: dropped?.table ?? "unknown"
275478
275316
  });
275479
275317
  }
275480
275318
  }
275319
+ appendLossRecords(rows, reason) {
275320
+ if (rows.length === 0) {
275321
+ return;
275322
+ }
275323
+ if (this.deadLetterFd === undefined) {
275324
+ throw new BrokerExecutionArchiveDurabilityError(`Broker execution archive cannot record ${reason}: dead-letter file is not open`);
275325
+ }
275326
+ const timestamp = new Date().toISOString();
275327
+ const records = rows.map((payload) => ({
275328
+ timestamp,
275329
+ deployment_id: this.deploymentId,
275330
+ reason,
275331
+ payload
275332
+ }));
275333
+ try {
275334
+ const bytes2 = Buffer.from(records.map((record) => JSON.stringify(record)).join(`
275335
+ `) + `
275336
+ `);
275337
+ const written = writeSync(this.deadLetterFd, bytes2);
275338
+ if (written !== bytes2.length) {
275339
+ throw new Error(`wrote ${written} of ${bytes2.length} bytes`);
275340
+ }
275341
+ fsyncSync(this.deadLetterFd);
275342
+ } catch (error) {
275343
+ throw new BrokerExecutionArchiveDurabilityError(`Broker execution archive failed to durably record ${reason}; affected row(s) were retained`, { cause: error });
275344
+ }
275345
+ }
275481
275346
  async flushBatch() {
275482
275347
  const batch = this.queue.splice(0, this.batchSize);
275483
275348
  if (batch.length === 0) {
@@ -275514,111 +275379,407 @@ class BrokerExecutionArchiver {
275514
275379
  for (const [table, count2] of countByTable) {
275515
275380
  this.recordArchiveMetric("cex_archive_rows_flushed_total", { table }, count2);
275516
275381
  }
275517
- this.recordArchiveGauge("cex_archive_last_flush_success", Math.floor(Date.now() / 1000));
275518
- }
275519
- async recordArchiveMetric(metricName, labels, value = 1) {
275520
- try {
275521
- await this.otelMetrics?.recordCounter(metricName, value, labels);
275522
- } catch {}
275382
+ this.recordArchiveGauge("cex_archive_last_flush_success", Math.floor(Date.now() / 1000));
275383
+ }
275384
+ async recordArchiveMetric(metricName, labels, value = 1) {
275385
+ try {
275386
+ await this.otelMetrics?.recordCounter(metricName, value, labels);
275387
+ } catch {}
275388
+ }
275389
+ async recordArchiveGauge(metricName, value) {
275390
+ try {
275391
+ await this.otelMetrics?.recordGauge(metricName, value, {});
275392
+ } catch {}
275393
+ }
275394
+ emitOtelLog(entry) {
275395
+ if (!this.otelLogs?.isOtelEnabled()) {
275396
+ return;
275397
+ }
275398
+ try {
275399
+ this.otelLogs.emit({
275400
+ body: entry.table,
275401
+ severityNumber: SeverityNumber2.INFO,
275402
+ severityText: "INFO",
275403
+ attributes: flattenArchiveAttributes(redactArchiveErrorForOtel(entry))
275404
+ });
275405
+ } catch (error) {
275406
+ log.warn("Broker execution archive OTLP emit failed", { error });
275407
+ }
275408
+ }
275409
+ postToForwarder(batch) {
275410
+ if (!this.forwarderUrl || batch.length === 0) {
275411
+ return Promise.resolve();
275412
+ }
275413
+ const body = JSON.stringify({
275414
+ source: "broker_write",
275415
+ deployment_id: this.deploymentId,
275416
+ rows: batch
275417
+ });
275418
+ const url2 = new URL(this.forwarderUrl);
275419
+ const doRequest = url2.protocol === "http:" ? httpRequest2 : httpsRequest2;
275420
+ const headers = {
275421
+ "content-type": "application/json",
275422
+ "content-length": Buffer.byteLength(body)
275423
+ };
275424
+ if (this.forwarderAuthToken) {
275425
+ headers.authorization = `Bearer ${this.forwarderAuthToken}`;
275426
+ }
275427
+ return new Promise((resolve, reject) => {
275428
+ const req = doRequest(url2, {
275429
+ method: "POST",
275430
+ headers,
275431
+ timeout: this.forwarderTimeoutMs
275432
+ }, (res) => {
275433
+ res.on("data", () => {});
275434
+ res.on("end", () => {
275435
+ const status = res.statusCode ?? 0;
275436
+ if (status < 200 || status >= 300) {
275437
+ reject(new Error(`Archive forwarder returned ${status} ${res.statusMessage ?? ""}`));
275438
+ return;
275439
+ }
275440
+ resolve();
275441
+ });
275442
+ });
275443
+ req.on("error", reject);
275444
+ req.on("timeout", () => {
275445
+ req.destroy(new Error("Archive forwarder request timed out"));
275446
+ });
275447
+ req.write(body);
275448
+ req.end();
275449
+ });
275450
+ }
275451
+ }
275452
+ function redactArchiveErrorForOtel(entry) {
275453
+ if (entry.table !== "broker_execution.order_events" || typeof entry.row.error_message !== "string" || entry.row.error_message.length === 0) {
275454
+ return entry;
275455
+ }
275456
+ return {
275457
+ ...entry,
275458
+ row: { ...entry.row, error_message: REDACTED_ERROR_MESSAGE }
275459
+ };
275460
+ }
275461
+ function flattenArchiveAttributes(entry) {
275462
+ const attributes = {
275463
+ ch_table: entry.table
275464
+ };
275465
+ for (const [key, value] of Object.entries(entry.row)) {
275466
+ if (value === undefined || value === null) {
275467
+ continue;
275468
+ }
275469
+ if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
275470
+ attributes[key] = value;
275471
+ } else {
275472
+ attributes[key] = JSON.stringify(value);
275473
+ }
275474
+ }
275475
+ return attributes;
275476
+ }
275477
+ function createBrokerExecutionArchiverFromEnv(otelLogs, otelMetrics) {
275478
+ if (process.env.CEX_BROKER_ARCHIVE_ENABLED !== "true") {
275479
+ return BrokerExecutionArchiver.disabled();
275480
+ }
275481
+ const forwarderUrl = resolveArchiveForwarderUrlFromEnv();
275482
+ const archiveOtelLogs = isArchiveOtelLogsEnabled() ? otelLogs : undefined;
275483
+ return BrokerExecutionArchiver.create({
275484
+ otelLogs: archiveOtelLogs,
275485
+ otelMetrics,
275486
+ forwarderUrl: forwarderUrl ?? "",
275487
+ deadLetterPath: process.env.CEX_BROKER_ARCHIVE_DEAD_LETTER_PATH?.trim() ?? "",
275488
+ deploymentId: process.env.CEX_BROKER_DEPLOYMENT_ID,
275489
+ maxQueueSize: parsePositiveInt(process.env.CEX_BROKER_ARCHIVE_QUEUE_MAX, DEFAULT_MAX_QUEUE_SIZE),
275490
+ batchSize: parsePositiveInt(process.env.CEX_BROKER_ARCHIVE_BATCH_SIZE, DEFAULT_BATCH_SIZE),
275491
+ flushIntervalMs: parsePositiveInt(process.env.CEX_BROKER_ARCHIVE_FLUSH_INTERVAL_MS, DEFAULT_FLUSH_INTERVAL_MS)
275492
+ });
275493
+ }
275494
+ function validateForwarderUrl(value) {
275495
+ if (!value) {
275496
+ throw new Error("Broker execution archive is enabled but CEX_BROKER_ARCHIVE_FORWARDER_URL is missing");
275497
+ }
275498
+ let url2;
275499
+ try {
275500
+ url2 = new URL(value);
275501
+ } catch (error) {
275502
+ throw new Error("Broker execution archive requires a valid CEX_BROKER_ARCHIVE_FORWARDER_URL", { cause: error });
275503
+ }
275504
+ if (url2.protocol !== "http:" && url2.protocol !== "https:") {
275505
+ throw new Error("Broker execution archive forwarder URL must use http or https");
275506
+ }
275507
+ return url2;
275508
+ }
275509
+ function parsePositiveInt(value, fallback) {
275510
+ if (!value) {
275511
+ return fallback;
275512
+ }
275513
+ const parsed = Number.parseInt(value, 10);
275514
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
275515
+ }
275516
+
275517
+ // src/helpers/broker-execution-archive/capture.ts
275518
+ function archiveOrderExecutionInBackground(archiver, context2, order, error, options) {
275519
+ if (!archiver?.isEnabled()) {
275520
+ return;
275521
+ }
275522
+ queueMicrotask(() => {
275523
+ try {
275524
+ const telemetry = buildOrderExecutionTelemetry(context2, order, error);
275525
+ const tags = buildCommonArchiveTags({
275526
+ deploymentId: archiver.getDeploymentId(),
275527
+ accountSelector: context2.accountLabel,
275528
+ exchange: context2.cex,
275529
+ symbol: telemetry.symbol,
275530
+ brokerObservedTimestamp: telemetry.brokerObservedTimestamp
275531
+ });
275532
+ archiver.enqueue(buildOrderEventArchiveRow({
275533
+ tags,
275534
+ action: context2.action,
275535
+ telemetry,
275536
+ errorDetail: context2.action === "CreateOrder" && error !== undefined ? sanitizeErrorDetail(error, { includeCode: true }) : undefined,
275537
+ marketMetadataHash: options?.marketMetadataHash
275538
+ }));
275539
+ } catch (archiveError) {
275540
+ rethrowArchiveDurabilityError(archiveError);
275541
+ log.warn("Failed to archive order execution", { error: archiveError });
275542
+ }
275543
+ });
275544
+ }
275545
+ function archiveSubscribeStreamInBackground(archiver, input) {
275546
+ if (!archiver?.isEnabled()) {
275547
+ return;
275548
+ }
275549
+ queueMicrotask(() => {
275550
+ try {
275551
+ const tags = buildCommonArchiveTags({
275552
+ deploymentId: archiver.getDeploymentId(),
275553
+ accountSelector: input.accountSelector,
275554
+ exchange: input.exchange,
275555
+ symbol: input.symbol
275556
+ });
275557
+ archiver.enqueue(buildSubscribeStreamArchiveRow({
275558
+ tags,
275559
+ subscriptionType: input.subscriptionType,
275560
+ streamPayload: input.streamPayload,
275561
+ secretLiterals: input.secretLiterals
275562
+ }));
275563
+ } catch (archiveError) {
275564
+ rethrowArchiveDurabilityError(archiveError);
275565
+ log.warn("Failed to archive subscribe stream event", {
275566
+ error: archiveError
275567
+ });
275568
+ }
275569
+ });
275570
+ }
275571
+ function archiveTransferEventInBackground(archiver, input) {
275572
+ if (!archiver?.isEnabled()) {
275573
+ return;
275574
+ }
275575
+ queueMicrotask(() => {
275576
+ try {
275577
+ const tags = buildCommonArchiveTags({
275578
+ deploymentId: archiver.getDeploymentId(),
275579
+ accountSelector: input.accountSelector,
275580
+ exchange: input.exchange,
275581
+ symbol: input.assetSymbol,
275582
+ brokerObservedTimestamp: input.brokerObservedTimestamp
275583
+ });
275584
+ archiver.enqueue(buildTransferEventArchiveRow({ tags, transfer: input.transfer }));
275585
+ } catch (archiveError) {
275586
+ rethrowArchiveDurabilityError(archiveError);
275587
+ log.warn("Failed to archive transfer event", { error: archiveError });
275588
+ }
275589
+ });
275590
+ }
275591
+ function archiveWithdrawalObservationsInBackground(archiver, tracker, input) {
275592
+ try {
275593
+ if (!archiver?.isEnabled() || !Array.isArray(input.transactions)) {
275594
+ return;
275595
+ }
275596
+ const brokerObservedTimestamp = new Date().toISOString();
275597
+ for (const [resultIndex, transaction] of input.transactions.entries()) {
275598
+ const normalized = normalizeCcxtTransactionForArchive(transaction);
275599
+ const assetSymbol = normalized.assetSymbol;
275600
+ if (!tracker.shouldArchive({
275601
+ exchange: input.exchange,
275602
+ accountSelector: input.accountSelector,
275603
+ assetSymbol,
275604
+ transaction,
275605
+ normalized
275606
+ })) {
275607
+ continue;
275608
+ }
275609
+ archiveTransferEventInBackground(archiver, {
275610
+ exchange: input.exchange,
275611
+ accountSelector: input.accountSelector,
275612
+ assetSymbol,
275613
+ brokerObservedTimestamp,
275614
+ transfer: {
275615
+ eventKind: "withdrawal",
275616
+ lifecycleAction: "observe_withdrawal",
275617
+ status: normalized.status,
275618
+ amount: normalized.amount,
275619
+ address: normalized.address,
275620
+ network: normalized.network,
275621
+ externalId: normalized.externalId,
275622
+ txid: normalized.txid,
275623
+ resultIndex,
275624
+ feeAmount: normalized.feeAmount,
275625
+ feeCurrency: normalized.feeCurrency,
275626
+ exchangeTimestamp: normalized.exchangeTimestamp,
275627
+ payload: transaction
275628
+ }
275629
+ });
275630
+ }
275631
+ } catch (archiveError) {
275632
+ rethrowArchiveDurabilityError(archiveError);
275633
+ log.warn("Failed to archive withdrawal observations", {
275634
+ error: archiveError
275635
+ });
275523
275636
  }
275524
- async recordArchiveGauge(metricName, value) {
275525
- try {
275526
- await this.otelMetrics?.recordGauge(metricName, value, {});
275527
- } catch {}
275637
+ }
275638
+ async function captureMarketMetadataSnapshot(archiver, broker, input) {
275639
+ if (!archiver?.canPersistMarketMetadataSnapshot()) {
275640
+ return;
275528
275641
  }
275529
- emitOtelLog(entry) {
275530
- if (!this.otelLogs?.isOtelEnabled()) {
275642
+ try {
275643
+ const fetchOrderBook = broker.fetchOrderBook;
275644
+ if (typeof fetchOrderBook !== "function") {
275531
275645
  return;
275532
275646
  }
275533
- try {
275534
- this.otelLogs.emit({
275535
- body: entry.table,
275536
- severityNumber: SeverityNumber2.INFO,
275537
- severityText: "INFO",
275538
- attributes: flattenArchiveAttributes(entry)
275539
- });
275540
- } catch (error) {
275541
- log.warn("Broker execution archive OTLP emit failed", { error });
275542
- }
275543
- }
275544
- postToForwarder(batch) {
275545
- if (!this.forwarderUrl || batch.length === 0) {
275546
- return Promise.resolve();
275547
- }
275548
- const body = JSON.stringify({
275549
- source: "broker_write",
275550
- deployment_id: this.deploymentId,
275551
- rows: batch
275552
- });
275553
- const url2 = new URL(this.forwarderUrl);
275554
- const doRequest = url2.protocol === "http:" ? httpRequest2 : httpsRequest2;
275555
- const headers = {
275556
- "content-type": "application/json",
275557
- "content-length": Buffer.byteLength(body)
275647
+ const orderBook = await fetchOrderBook.call(broker, input.symbol, 5);
275648
+ const record = asRecord(orderBook);
275649
+ const snapshot = {
275650
+ action: input.action,
275651
+ symbol: input.symbol,
275652
+ bids: record?.bids,
275653
+ asks: record?.asks,
275654
+ timestamp: record?.timestamp ?? Date.now(),
275655
+ datetime: record?.datetime,
275656
+ nonce: record?.nonce
275558
275657
  };
275559
- if (this.forwarderAuthToken) {
275560
- headers.authorization = `Bearer ${this.forwarderAuthToken}`;
275561
- }
275562
- return new Promise((resolve, reject) => {
275563
- const req = doRequest(url2, {
275564
- method: "POST",
275565
- headers,
275566
- timeout: this.forwarderTimeoutMs
275567
- }, (res) => {
275568
- res.on("data", () => {});
275569
- res.on("end", () => {
275570
- const status = res.statusCode ?? 0;
275571
- if (status < 200 || status >= 300) {
275572
- reject(new Error(`Archive forwarder returned ${status} ${res.statusMessage ?? ""}`));
275573
- return;
275574
- }
275575
- resolve();
275576
- });
275577
- });
275578
- req.on("error", reject);
275579
- req.on("timeout", () => {
275580
- req.destroy(new Error("Archive forwarder request timed out"));
275581
- });
275582
- req.write(body);
275583
- req.end();
275658
+ const tags = buildCommonArchiveTags({
275659
+ deploymentId: archiver.getDeploymentId(),
275660
+ accountSelector: input.accountSelector,
275661
+ exchange: input.exchange,
275662
+ symbol: input.symbol,
275663
+ brokerObservedTimestamp: input.brokerObservedTimestamp
275664
+ });
275665
+ const row = buildMarketMetadataSnapshotRow({
275666
+ tags,
275667
+ clientOrderId: input.clientOrderId,
275668
+ orderId: input.orderId,
275669
+ makerActionId: input.makerActionId,
275670
+ idempotencyId: input.idempotencyId,
275671
+ marketSnapshot: snapshot
275672
+ });
275673
+ archiver.enqueue(row);
275674
+ const hash4 = row.row.market_metadata_hash;
275675
+ return typeof hash4 === "string" ? hash4 : undefined;
275676
+ } catch (archiveError) {
275677
+ rethrowArchiveDurabilityError(archiveError);
275678
+ log.warn("Failed to capture market metadata snapshot", {
275679
+ error: archiveError
275584
275680
  });
275681
+ return;
275585
275682
  }
275586
275683
  }
275587
- function flattenArchiveAttributes(entry) {
275588
- const attributes = {
275589
- ch_table: entry.table
275590
- };
275591
- for (const [key, value] of Object.entries(entry.row)) {
275592
- if (value === undefined || value === null) {
275593
- continue;
275594
- }
275595
- if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
275596
- attributes[key] = value;
275597
- } else {
275598
- attributes[key] = JSON.stringify(value);
275684
+ // src/helpers/broker-execution-archive/withdrawal-observation-tracker.ts
275685
+ var DEFAULT_WITHDRAWAL_OBSERVATION_TRACKER_MAX_ENTRIES = 1e4;
275686
+ var VENUE_LIFECYCLE_EVIDENCE_FIELDS = [
275687
+ "updated",
275688
+ "updatedAt",
275689
+ "updated_at",
275690
+ "updateTime",
275691
+ "update_time",
275692
+ "updateTimestamp",
275693
+ "lastUpdateTimestamp",
275694
+ "completed",
275695
+ "completedAt",
275696
+ "completed_at",
275697
+ "completeTime",
275698
+ "complete_time",
275699
+ "completionTime",
275700
+ "successTime"
275701
+ ];
275702
+ function fingerprintEvidenceValue(value) {
275703
+ if (value === undefined || value === null) {
275704
+ return value;
275705
+ }
275706
+ if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
275707
+ return value;
275708
+ }
275709
+ if (typeof value === "bigint") {
275710
+ return value.toString();
275711
+ }
275712
+ try {
275713
+ return JSON.stringify(value);
275714
+ } catch {
275715
+ return String(value);
275716
+ }
275717
+ }
275718
+ function venueLifecycleEvidence(transaction) {
275719
+ const record = asRecord(transaction);
275720
+ const info = asRecord(record?.info);
275721
+ const evidence = [];
275722
+ for (const [scope, source] of [
275723
+ ["transaction", record],
275724
+ ["info", info]
275725
+ ]) {
275726
+ for (const field of VENUE_LIFECYCLE_EVIDENCE_FIELDS) {
275727
+ const value = source?.[field];
275728
+ if (value !== undefined && value !== null) {
275729
+ evidence.push([scope, field, fingerprintEvidenceValue(value)]);
275730
+ }
275599
275731
  }
275600
275732
  }
275601
- return attributes;
275733
+ return evidence;
275602
275734
  }
275603
- function createBrokerExecutionArchiverFromEnv(otelLogs, otelMetrics) {
275604
- const forwarderUrl = resolveArchiveForwarderUrlFromEnv();
275605
- const archiveOtelLogs = isArchiveOtelLogsEnabled() ? otelLogs : undefined;
275606
- return BrokerExecutionArchiver.create({
275607
- otelLogs: archiveOtelLogs,
275608
- otelMetrics,
275609
- forwarderUrl,
275610
- deploymentId: process.env.CEX_BROKER_DEPLOYMENT_ID,
275611
- maxQueueSize: parsePositiveInt(process.env.CEX_BROKER_ARCHIVE_QUEUE_MAX, DEFAULT_MAX_QUEUE_SIZE),
275612
- batchSize: parsePositiveInt(process.env.CEX_BROKER_ARCHIVE_BATCH_SIZE, DEFAULT_BATCH_SIZE),
275613
- flushIntervalMs: parsePositiveInt(process.env.CEX_BROKER_ARCHIVE_FLUSH_INTERVAL_MS, DEFAULT_FLUSH_INTERVAL_MS)
275735
+ function observationFingerprint(observation) {
275736
+ const { normalized, transaction } = observation;
275737
+ return JSON.stringify({
275738
+ status: normalized.status ?? null,
275739
+ txid: normalized.txid ?? null,
275740
+ amount: normalized.amount ?? null,
275741
+ feeAmount: normalized.feeAmount ?? null,
275742
+ feeCurrency: normalized.feeCurrency ?? null,
275743
+ address: normalized.address ?? null,
275744
+ network: normalized.network ?? null,
275745
+ exchangeTimestamp: normalized.exchangeTimestamp ?? null,
275746
+ venueLifecycleEvidence: venueLifecycleEvidence(transaction)
275614
275747
  });
275615
275748
  }
275616
- function parsePositiveInt(value, fallback) {
275617
- if (!value) {
275618
- return fallback;
275749
+
275750
+ class WithdrawalObservationTracker {
275751
+ #fingerprints = new Map;
275752
+ #maxEntries;
275753
+ #missingIdSequence = 0n;
275754
+ constructor(options) {
275755
+ const maxEntries = options?.maxEntries ?? DEFAULT_WITHDRAWAL_OBSERVATION_TRACKER_MAX_ENTRIES;
275756
+ this.#maxEntries = Number.isFinite(maxEntries) ? Math.max(1, Math.floor(maxEntries)) : DEFAULT_WITHDRAWAL_OBSERVATION_TRACKER_MAX_ENTRIES;
275757
+ }
275758
+ shouldArchive(observation) {
275759
+ const externalId = observation.normalized.externalId?.trim();
275760
+ const identity = JSON.stringify([
275761
+ observation.exchange.trim().toLowerCase() || "unknown",
275762
+ observation.accountSelector?.trim() || "unknown",
275763
+ observation.assetSymbol?.trim().toUpperCase() || "unknown",
275764
+ externalId || `missing:${++this.#missingIdSequence}`
275765
+ ]);
275766
+ const fingerprint = observationFingerprint(observation);
275767
+ if (this.#fingerprints.get(identity) === fingerprint) {
275768
+ return false;
275769
+ }
275770
+ this.#fingerprints.delete(identity);
275771
+ this.#fingerprints.set(identity, fingerprint);
275772
+ while (this.#fingerprints.size > this.#maxEntries) {
275773
+ const oldest = this.#fingerprints.keys().next().value;
275774
+ if (oldest === undefined)
275775
+ break;
275776
+ this.#fingerprints.delete(oldest);
275777
+ }
275778
+ return true;
275779
+ }
275780
+ getSize() {
275781
+ return this.#fingerprints.size;
275619
275782
  }
275620
- const parsed = Number.parseInt(value, 10);
275621
- return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
275622
275783
  }
275623
275784
  // src/helpers/account-balance-archive-poller.ts
275624
275785
  var DEFAULT_CONFIG = {
@@ -275712,6 +275873,7 @@ class AccountBalanceArchivePoller {
275712
275873
  this.params.metrics?.recordGauge("cex_account_balance_poll_last_success_timestamp_seconds", Math.floor(successMs / 1000), labels);
275713
275874
  this.#recordFreshness(labels, successMs, successMs);
275714
275875
  } catch (error) {
275876
+ rethrowArchiveDurabilityError(error);
275715
275877
  this.params.metrics?.recordCounter("cex_account_balance_poll_failures_total", 1, labels);
275716
275878
  const now3 = Date.now();
275717
275879
  const lastSuccess = this.#lastSuccessMs.get(this.#targetKey(target));
@@ -275741,6 +275903,7 @@ class AccountBalanceArchivePoller {
275741
275903
  try {
275742
275904
  await this.pollAllOnce();
275743
275905
  } catch (error) {
275906
+ rethrowArchiveDurabilityError(error);
275744
275907
  log.error("Account balance archive poller tick failed", error);
275745
275908
  } finally {
275746
275909
  if (!this.#stopped) {
@@ -275804,6 +275967,7 @@ class FillArchivePoller {
275804
275967
  try {
275805
275968
  await this.pollTrackedOnce();
275806
275969
  } catch (error) {
275970
+ rethrowArchiveDurabilityError(error);
275807
275971
  log.error("Fill archive poller tick failed", error);
275808
275972
  } finally {
275809
275973
  this.#running = false;
@@ -279795,32 +279959,6 @@ import * as grpc14 from "@grpc/grpc-js";
279795
279959
  // src/handlers/execute-action/deposit.ts
279796
279960
  import * as grpc3 from "@grpc/grpc-js";
279797
279961
 
279798
- // src/helpers/shared/errors.ts
279799
- var MAX_ERROR_DETAIL_LENGTH = 512;
279800
- function getErrorMessage(error) {
279801
- return error instanceof Error ? error.message : typeof error === "string" ? error : "Unknown error";
279802
- }
279803
- function errorClassName(error) {
279804
- if (!(error instanceof Error)) {
279805
- return;
279806
- }
279807
- const name = error.constructor?.name || error.name;
279808
- return name && name !== "Error" ? name : undefined;
279809
- }
279810
- function sanitizeErrorDetail(error) {
279811
- const className = errorClassName(error);
279812
- const message = getErrorMessage(error);
279813
- const detail = className ? `${className}: ${message}` : message;
279814
- return detail.replace(/\s+/g, " ").trim().slice(0, MAX_ERROR_DETAIL_LENGTH);
279815
- }
279816
- function safeLogError(context3, error) {
279817
- try {
279818
- log.error(context3, { error });
279819
- } catch {
279820
- console.error(context3, error);
279821
- }
279822
- }
279823
-
279824
279962
  // src/helpers/treasury-discovery.ts
279825
279963
  function callArgs(args, params) {
279826
279964
  const argsArray = Array.isArray(args) ? [...args] : [];
@@ -294347,7 +294485,8 @@ async function handleCreateOrder(ctx) {
294347
294485
  archiveOrderExecutionInBackground(brokerArchiver, createOrderContext, order, undefined, { marketMetadataHash });
294348
294486
  ctx.wrappedCallback(null, { result: JSON.stringify({ ...order }) });
294349
294487
  } catch (error48) {
294350
- safeLogError("Order Creation failed", error48);
294488
+ rethrowArchiveDurabilityError(error48);
294489
+ safeLogRedactedError("Order Creation failed", error48);
294351
294490
  const failedCreateContext = {
294352
294491
  action: "CreateOrder",
294353
294492
  cex: cex3,
@@ -295622,8 +295761,8 @@ class BinanceSpotUserDataStream {
295622
295761
  }
295623
295762
  if ("status" in message && typeof message.status === "number" && message.status !== 200) {
295624
295763
  const errorMessage = message.error?.msg ?? message.error?.message ?? `Binance user-data request failed with status ${message.status}`;
295625
- const errorCode = message.error?.code;
295626
- this.fail(new Error(typeof errorCode === "number" ? `${errorMessage} (code ${errorCode})` : errorMessage));
295764
+ const errorCode2 = message.error?.code;
295765
+ this.fail(new Error(typeof errorCode2 === "number" ? `${errorMessage} (code ${errorCode2})` : errorMessage));
295627
295766
  return;
295628
295767
  }
295629
295768
  if (!("event" in message) || !message.event) {
@@ -295775,19 +295914,26 @@ class OhlcvBarTracker {
295775
295914
  if (!firstBar || !lastBar) {
295776
295915
  return [];
295777
295916
  }
295778
- if (this.lastOpenTimeMs !== null && lastBar.openTimeMs < this.lastOpenTimeMs) {
295917
+ const lastOpenTimeMs = this.lastOpenTimeMs;
295918
+ if (lastOpenTimeMs !== null && lastBar.openTimeMs < lastOpenTimeMs) {
295919
+ return [];
295920
+ }
295921
+ const barsToProcess = lastOpenTimeMs === null ? bars : bars.filter((bar) => bar.openTimeMs >= lastOpenTimeMs);
295922
+ const firstBarToProcess = barsToProcess[0];
295923
+ const lastBarToProcess = barsToProcess[barsToProcess.length - 1];
295924
+ if (!firstBarToProcess || !lastBarToProcess) {
295779
295925
  return [];
295780
295926
  }
295781
295927
  const candidates = [];
295782
- if (this.lastBar !== null && this.lastOpenTimeMs !== null && !bars.some((bar) => bar.openTimeMs === this.lastOpenTimeMs) && this.lastOpenTimeMs < firstBar.openTimeMs) {
295928
+ if (this.lastBar !== null && lastOpenTimeMs !== null && lastOpenTimeMs < firstBarToProcess.openTimeMs) {
295783
295929
  candidates.push({
295784
295930
  bar: this.lastBar,
295785
295931
  isClosed: true,
295786
295932
  brokerVersion
295787
295933
  });
295788
295934
  }
295789
- for (let index2 = 0;index2 < bars.length - 1; index2 += 1) {
295790
- const bar = bars[index2];
295935
+ for (let index2 = 0;index2 < barsToProcess.length - 1; index2 += 1) {
295936
+ const bar = barsToProcess[index2];
295791
295937
  if (bar) {
295792
295938
  candidates.push({
295793
295939
  bar,
@@ -295797,12 +295943,12 @@ class OhlcvBarTracker {
295797
295943
  }
295798
295944
  }
295799
295945
  candidates.push({
295800
- bar: lastBar,
295946
+ bar: lastBarToProcess,
295801
295947
  isClosed: false,
295802
295948
  brokerVersion
295803
295949
  });
295804
- this.lastOpenTimeMs = lastBar.openTimeMs;
295805
- this.lastBar = lastBar;
295950
+ this.lastOpenTimeMs = lastBarToProcess.openTimeMs;
295951
+ this.lastBar = lastBarToProcess;
295806
295952
  return candidates;
295807
295953
  }
295808
295954
  }
@@ -296214,6 +296360,7 @@ function archiveOrderbookInBackground(archiver, otelMetrics, input, options) {
296214
296360
  recordWatchMetric(otelMetrics, "cex_watch_frames_archived_total", labels);
296215
296361
  }
296216
296362
  } catch (error48) {
296363
+ rethrowArchiveDurabilityError(error48);
296217
296364
  log.warn("Failed to archive orderbook snapshot", { error: error48 });
296218
296365
  }
296219
296366
  });
@@ -296241,6 +296388,7 @@ function archiveOhlcvInBackground(archiver, otelMetrics, tracker, input) {
296241
296388
  recordWatchMetric(otelMetrics, "cex_watch_frames_archived_total", labels);
296242
296389
  }
296243
296390
  } catch (error48) {
296391
+ rethrowArchiveDurabilityError(error48);
296244
296392
  log.warn("Failed to archive OHLCV candle", { error: error48 });
296245
296393
  }
296246
296394
  });
@@ -296267,6 +296415,7 @@ function archiveMarketRowsInBackground(archiver, otelMetrics, stream4, input, en
296267
296415
  recordWatchMetric(otelMetrics, "cex_watch_frames_archived_total", labels);
296268
296416
  }
296269
296417
  } catch (error48) {
296418
+ rethrowArchiveDurabilityError(error48);
296270
296419
  log.warn(`Failed to archive ${stream4} market data`, { error: error48 });
296271
296420
  }
296272
296421
  });
@@ -296302,7 +296451,6 @@ function resolveOhlcvBootstrapLimit(optionValue) {
296302
296451
  }
296303
296452
  return Math.min(parsed, MAX_OHLCV_BOOTSTRAP_LIMIT);
296304
296453
  }
296305
-
296306
296454
  // src/helpers/market-data-archive/ohlcv-history.ts
296307
296455
  function supportsFetchOhlcv(broker) {
296308
296456
  const fetchOHLCV = broker.fetchOHLCV;
@@ -296787,7 +296935,7 @@ function createSubscribeHandler(deps) {
296787
296935
  }
296788
296936
  }
296789
296937
  while (ohlcvStreamActive && !isStreamClosed()) {
296790
- const data = await broker.fetchOHLCVWs(resolvedSymbol, timeframe);
296938
+ const data = await broker.watchOHLCV(resolvedSymbol, timeframe);
296791
296939
  const receivedTimestamp = Date.now();
296792
296940
  if (!await writeSubscribeFrame(call, isStreamClosed, {
296793
296941
  data: JSON.stringify(data),
@@ -297412,4 +297560,4 @@ export {
297412
297560
  CEXBroker as default
297413
297561
  };
297414
297562
 
297415
- //# debugId=1D08CFF8FA968C4064756E2164756E21
297563
+ //# debugId=9DC3823C033EB0C864756E2164756E21