@usherlabs/cex-broker 0.2.48 → 0.2.50

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (26) hide show
  1. package/dist/commands/cex-canonical-orderbook-export.js +72 -358
  2. package/dist/commands/cex-canonical-orderbook-export.js.map +7 -19
  3. package/dist/commands/cli.js +224 -67
  4. package/dist/commands/market-data-vendor-backfill.js +975 -1198
  5. package/dist/commands/market-data-vendor-backfill.js.map +13 -25
  6. package/dist/helpers/logger.d.ts +4 -0
  7. package/dist/helpers/market-data-vendor-backfill/cryptohftdata.d.ts +3 -1
  8. package/dist/helpers/market-data-vendor-backfill/manifests.d.ts +58 -4
  9. package/dist/helpers/market-data-vendor-backfill/semantic-verification.d.ts +1 -0
  10. package/dist/helpers/otel.d.ts +1 -0
  11. package/dist/helpers/trace-context.d.ts +8 -0
  12. package/dist/index.js +183 -63
  13. package/dist/index.js.map +9 -8
  14. package/dist/market-data-preparation/fixtures/conformance-v2.json +8 -8
  15. package/dist/market-data-preparation/policies/capability-policy-v1.json +28 -0
  16. package/dist/market-data-preparation/policies/capability-policy.json +14 -3
  17. package/dist/market-data-preparation/policies/resource-policy-v1.json +16 -0
  18. package/dist/market-data-preparation/policies/resource-policy.json +3 -3
  19. package/dist/market-data-preparation.js +70 -359
  20. package/dist/market-data-preparation.js.map +9 -21
  21. package/dist/market-data-vendor-backfill/policies/capability-policy-v2.json +39 -0
  22. package/dist/market-data-vendor-backfill/policies/resource-policy-v2.json +16 -0
  23. package/dist/market-data-vendor-backfill.d.ts +2 -2
  24. package/dist/market-data-vendor-backfill.js +1528 -1750
  25. package/dist/market-data-vendor-backfill.js.map +13 -25
  26. package/package.json +1 -1
@@ -313534,16 +313534,20 @@ var tslogLogger = new Logger({
313534
313534
  minLevel: process.env.LOG_LEVEL === "debug" ? 0 : 3
313535
313535
  });
313536
313536
  var baseLogger = new LogLayer({
313537
- transport: [
313538
- new TsLogTransport({ id: "tslog", logger: tslogLogger }),
313539
- new OpenTelemetryTransport({
313540
- id: "otel",
313541
- enabled: true
313542
- })
313543
- ],
313537
+ transport: [new TsLogTransport({ id: "tslog", logger: tslogLogger })],
313544
313538
  plugins: [openTelemetryPlugin()],
313545
313539
  errorSerializer: serializeError
313546
313540
  });
313541
+ var OTEL_TRANSPORT_ID = "otel";
313542
+ function attachOtelLogTransport() {
313543
+ baseLogger.addTransport(new OpenTelemetryTransport({
313544
+ id: OTEL_TRANSPORT_ID,
313545
+ enabled: true
313546
+ }));
313547
+ }
313548
+ function detachOtelLogTransport() {
313549
+ baseLogger.removeTransport(OTEL_TRANSPORT_ID);
313550
+ }
313547
313551
  if (process.env.LOG_LEVEL !== "debug") {
313548
313552
  baseLogger.setLevel("info");
313549
313553
  }
@@ -317970,11 +317974,14 @@ var import_sdk_metrics = __toESM(require_src12(), 1);
317970
317974
  var DEFAULT_SERVICE = "cex-broker";
317971
317975
  var DEFAULT_OTLP_PORT = 4318;
317972
317976
  var EXPORT_INTERVAL_MS = 5000;
317977
+ var EXPORT_TIMEOUT_MS = 2000;
317978
+ var SHUTDOWN_TIMEOUT_MS = 3000;
317973
317979
 
317974
317980
  class BaseOtelSignal {
317975
317981
  signal;
317976
317982
  provider = null;
317977
317983
  isEnabled = false;
317984
+ closePromise = null;
317978
317985
  serviceName;
317979
317986
  constructor(config, signal) {
317980
317987
  this.signal = signal;
@@ -318006,19 +318013,35 @@ class BaseOtelSignal {
318006
318013
  isOtelEnabled() {
318007
318014
  return this.isEnabled && this.provider !== null;
318008
318015
  }
318009
- async close() {
318010
- if (!this.provider) {
318011
- return;
318012
- }
318013
- try {
318014
- await this.shutdownProvider(this.provider);
318015
- log.info(`OTel ${this.signal} provider shut down`);
318016
- } catch (error) {
318017
- log.error(`Error shutting down OTel ${this.signal} provider:`, error);
318018
- }
318016
+ close() {
318017
+ if (this.closePromise)
318018
+ return this.closePromise;
318019
+ const provider = this.provider;
318019
318020
  this.provider = null;
318020
318021
  this.isEnabled = false;
318021
318022
  this.onProviderClosed();
318023
+ if (!provider) {
318024
+ this.closePromise = Promise.resolve();
318025
+ return this.closePromise;
318026
+ }
318027
+ const shutdown = Promise.resolve().then(() => this.shutdownProvider(provider)).then(() => {
318028
+ log.info(`OTel ${this.signal} provider shut down`);
318029
+ }, (error) => {
318030
+ log.error(`Error shutting down OTel ${this.signal} provider:`, error);
318031
+ });
318032
+ let deadlineTimer;
318033
+ const deadline = new Promise((resolve) => {
318034
+ deadlineTimer = setTimeout(() => {
318035
+ deadlineTimer = undefined;
318036
+ log.warn(`OTel ${this.signal} provider shutdown exceeded ${SHUTDOWN_TIMEOUT_MS}ms; continuing shutdown`);
318037
+ resolve();
318038
+ }, SHUTDOWN_TIMEOUT_MS);
318039
+ });
318040
+ this.closePromise = Promise.race([shutdown, deadline]).finally(() => {
318041
+ if (deadlineTimer !== undefined)
318042
+ clearTimeout(deadlineTimer);
318043
+ });
318044
+ return this.closePromise;
318022
318045
  }
318023
318046
  }
318024
318047
  function toAttributes(labels, service) {
@@ -318041,11 +318064,13 @@ class OtelMetrics extends BaseOtelSignal {
318041
318064
  }
318042
318065
  createProvider(endpoint, serviceName, appendSignalPath) {
318043
318066
  const exporter = new import_exporter_metrics_otlp_http.OTLPMetricExporter({
318044
- url: appendOtlpPath(endpoint, "metrics", appendSignalPath)
318067
+ url: appendOtlpPath(endpoint, "metrics", appendSignalPath),
318068
+ timeoutMillis: EXPORT_TIMEOUT_MS
318045
318069
  });
318046
318070
  const reader = new import_sdk_metrics.PeriodicExportingMetricReader({
318047
318071
  exporter,
318048
- exportIntervalMillis: EXPORT_INTERVAL_MS
318072
+ exportIntervalMillis: EXPORT_INTERVAL_MS,
318073
+ exportTimeoutMillis: EXPORT_TIMEOUT_MS
318049
318074
  });
318050
318075
  const resource = import_resources.resourceFromAttributes({
318051
318076
  "service.name": serviceName
@@ -318166,15 +318191,23 @@ class OtelMetrics extends BaseOtelSignal {
318166
318191
  }
318167
318192
 
318168
318193
  class OtelLogs extends BaseOtelSignal {
318169
- logger = null;
318194
+ logger;
318170
318195
  constructor(config) {
318171
318196
  super(config, "logs");
318197
+ const provider = this.getProvider();
318198
+ this.logger = provider?.getLogger("cex-broker-logs", "1.0.0") ?? null;
318199
+ if (provider) {
318200
+ attachOtelLogTransport();
318201
+ }
318172
318202
  }
318173
318203
  createProvider(endpoint, serviceName, appendSignalPath) {
318174
318204
  const exporter = new import_exporter_logs_otlp_http.OTLPLogExporter({
318175
- url: appendOtlpPath(endpoint, "logs", appendSignalPath)
318205
+ url: appendOtlpPath(endpoint, "logs", appendSignalPath),
318206
+ timeoutMillis: EXPORT_TIMEOUT_MS
318207
+ });
318208
+ const processor = new import_sdk_logs.BatchLogRecordProcessor(exporter, {
318209
+ exportTimeoutMillis: EXPORT_TIMEOUT_MS
318176
318210
  });
318177
- const processor = new import_sdk_logs.BatchLogRecordProcessor(exporter);
318178
318211
  const resource = import_resources.resourceFromAttributes({
318179
318212
  "service.name": serviceName
318180
318213
  });
@@ -318185,12 +318218,12 @@ class OtelLogs extends BaseOtelSignal {
318185
318218
  }
318186
318219
  onProviderCreated(provider) {
318187
318220
  import_api_logs3.logs.setGlobalLoggerProvider(provider);
318188
- this.logger = provider.getLogger("cex-broker-logs", "1.0.0");
318189
318221
  }
318190
318222
  shutdownProvider(provider) {
318191
- return provider.forceFlush().then(() => provider.shutdown());
318223
+ return provider.shutdown();
318192
318224
  }
318193
318225
  onProviderClosed() {
318226
+ detachOtelLogTransport();
318194
318227
  this.logger = null;
318195
318228
  }
318196
318229
  emit(record) {
@@ -335121,6 +335154,24 @@ function selectBrokerAccountForCex(normalizedCex, brokers, metadata) {
335121
335154
  return selectBrokerAccount(brokers[normalizedCex], metadata) ?? undefined;
335122
335155
  }
335123
335156
 
335157
+ // src/helpers/trace-context.ts
335158
+ var TRACE_METADATA_KEY = "x-trace-id";
335159
+ var MAX_TRACE_METADATA_LENGTH = 128;
335160
+ var OTEL_TRACE_ID_PATTERN = /^[0-9a-f]{32}$/;
335161
+ var UUID_V4_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
335162
+ var ZERO_OTEL_TRACE_ID = "00000000000000000000000000000000";
335163
+ function extractTraceId(metadata) {
335164
+ const raw = metadata.get(TRACE_METADATA_KEY)[0];
335165
+ if (typeof raw !== "string" || raw.length > MAX_TRACE_METADATA_LENGTH) {
335166
+ return;
335167
+ }
335168
+ const traceId = raw.trim();
335169
+ if (OTEL_TRACE_ID_PATTERN.test(traceId) && traceId !== ZERO_OTEL_TRACE_ID || UUID_V4_PATTERN.test(traceId)) {
335170
+ return traceId;
335171
+ }
335172
+ return;
335173
+ }
335174
+
335124
335175
  // src/handlers/execute-action/order-book-call.ts
335125
335176
  var grpc4 = __toESM(require_src3(), 1);
335126
335177
  async function handleOrderBookCall(ctx) {
@@ -336424,6 +336475,15 @@ function isPublicMarketDataAction(action, payload) {
336424
336475
  return false;
336425
336476
  return isOrderBookCallMethod(payload?.method ?? payload?.functionName);
336426
336477
  }
336478
+ function grpcStatusName(error48) {
336479
+ if (!error48) {
336480
+ return "OK";
336481
+ }
336482
+ if (typeof error48.code !== "number") {
336483
+ return "UNKNOWN";
336484
+ }
336485
+ return grpc12.status[error48.code] ?? "UNKNOWN";
336486
+ }
336427
336487
  function createExecuteActionHandler(deps) {
336428
336488
  const {
336429
336489
  policy,
@@ -336440,12 +336500,28 @@ function createExecuteActionHandler(deps) {
336440
336500
  const startTime = Date.now();
336441
336501
  const { action: rawAction, cex: cex3, symbol: symbol2 } = call.request;
336442
336502
  const action = resolveAction(rawAction);
336503
+ const actionName = getActionName(action);
336504
+ const operationalCex = cex3?.trim().toLowerCase() || "unknown";
336505
+ const traceId = extractTraceId(call.metadata);
336506
+ const traceFields = traceId === undefined ? {} : { trace_id: traceId };
336443
336507
  let actionCompleted = false;
336444
336508
  const wrappedCallback = (error48, value) => {
336445
336509
  if (!actionCompleted) {
336446
336510
  actionCompleted = true;
336447
336511
  const latency = Date.now() - startTime;
336448
- const actionName = getActionName(action);
336512
+ const terminalFields = {
336513
+ action: actionName,
336514
+ cex: operationalCex,
336515
+ latency_ms: latency,
336516
+ outcome: error48 ? "error" : "success",
336517
+ grpc_status: grpcStatusName(error48),
336518
+ ...traceFields
336519
+ };
336520
+ if (error48) {
336521
+ log.withMetadata(terminalFields).error("ExecuteAction failed");
336522
+ } else {
336523
+ log.withMetadata(terminalFields).info("ExecuteAction completed");
336524
+ }
336449
336525
  otelMetrics?.recordHistogram("execute_action_duration_ms", latency, {
336450
336526
  action: actionName,
336451
336527
  cex: cex3 || "unknown"
@@ -336466,9 +336542,13 @@ function createExecuteActionHandler(deps) {
336466
336542
  callback(error48, value);
336467
336543
  };
336468
336544
  try {
336469
- log.info(`Request - ExecuteAction:`, { action, cex: cex3, symbol: symbol2 });
336545
+ log.withMetadata({
336546
+ action: actionName,
336547
+ cex: operationalCex,
336548
+ ...traceFields
336549
+ }).info("ExecuteAction started");
336470
336550
  otelMetrics?.recordCounter("execute_action_requests_total", 1, {
336471
- action: getActionName(action),
336551
+ action: actionName,
336472
336552
  cex: cex3 || "unknown"
336473
336553
  });
336474
336554
  if (!authenticateRequest(call, whitelistIps)) {
@@ -336706,6 +336786,9 @@ async function writeSubscribeError(call, isClosed, frame) {
336706
336786
  call.end();
336707
336787
  }
336708
336788
  }
336789
+ function grpcStatusName2(status14) {
336790
+ return typeof status14 === "number" ? grpc13.status[status14] ?? "UNKNOWN" : "UNKNOWN";
336791
+ }
336709
336792
  function getBinanceEventMarketId(event) {
336710
336793
  const value = event.s;
336711
336794
  return typeof value === "string" ? value : null;
@@ -336838,6 +336921,24 @@ function createSubscribeHandler(deps) {
336838
336921
  });
336839
336922
  return async (call) => {
336840
336923
  const subscribeStartTime = Date.now();
336924
+ const request = call.request;
336925
+ const { cex: cex3, symbol: symbol2, type: type2 } = request;
336926
+ const metadata = call.metadata;
336927
+ const traceId = extractTraceId(metadata);
336928
+ const traceFields = traceId === undefined ? {} : { trace_id: traceId };
336929
+ const operationalCex = cex3?.trim().toLowerCase() || "unknown";
336930
+ const operationalSymbol = symbol2?.trim() || "unknown";
336931
+ const subscriptionType2 = resolveSubscriptionType(type2);
336932
+ const subscriptionTypeName = getSubscriptionTypeName(subscriptionType2);
336933
+ const operationalFields = {
336934
+ cex: operationalCex,
336935
+ symbol: operationalSymbol,
336936
+ subscription_type: subscriptionTypeName,
336937
+ ...traceFields
336938
+ };
336939
+ let terminalLogged = false;
336940
+ let terminalOutcome = "completed";
336941
+ let terminalGrpcStatus = "OK";
336841
336942
  let streamClosed = false;
336842
336943
  let ownedBroker = null;
336843
336944
  let ownedBrokerClosePromise;
@@ -336860,12 +336961,41 @@ function createSubscribeHandler(deps) {
336860
336961
  const closeOwnedBrokerOnCallEnd = () => {
336861
336962
  closeOwnedBroker();
336862
336963
  };
336863
- call.once("cancelled", markStreamClosed);
336964
+ const logTerminalOutcome = (outcome) => {
336965
+ if (terminalLogged) {
336966
+ return;
336967
+ }
336968
+ terminalLogged = true;
336969
+ const fields = {
336970
+ ...operationalFields,
336971
+ duration_ms: Date.now() - subscribeStartTime,
336972
+ outcome,
336973
+ grpc_status: outcome === "cancelled" ? "CANCELLED" : terminalGrpcStatus
336974
+ };
336975
+ if (outcome === "error") {
336976
+ log.withMetadata(fields).error("Subscribe failed");
336977
+ } else if (outcome === "cancelled") {
336978
+ log.withMetadata(fields).info("Subscribe cancelled");
336979
+ } else {
336980
+ log.withMetadata(fields).info("Subscribe ended");
336981
+ }
336982
+ };
336983
+ const writeTerminalError = async (frame, status14 = grpc13.status.UNKNOWN) => {
336984
+ terminalOutcome = "error";
336985
+ terminalGrpcStatus = grpcStatusName2(status14);
336986
+ await writeSubscribeError(call, isStreamClosed, frame);
336987
+ };
336988
+ log.withMetadata(operationalFields).info("Subscribe started");
336989
+ call.once("cancelled", () => {
336990
+ terminalGrpcStatus = "CANCELLED";
336991
+ markStreamClosed();
336992
+ logTerminalOutcome("cancelled");
336993
+ });
336864
336994
  call.once("cancelled", closeOwnedBrokerOnCallEnd);
336865
336995
  call.once("error", closeOwnedBrokerOnCallEnd);
336866
336996
  call.once("end", () => {
336867
336997
  markStreamClosed();
336868
- log.info("Subscribe stream ended");
336998
+ logTerminalOutcome(terminalOutcome);
336869
336999
  const duration3 = Date.now() - subscribeStartTime;
336870
337000
  otelMetrics?.recordHistogram("subscribe_duration_ms", duration3, {
336871
337001
  cex: call.request?.cex || "unknown",
@@ -336874,12 +337004,16 @@ function createSubscribeHandler(deps) {
336874
337004
  });
336875
337005
  call.once("error", (error48) => {
336876
337006
  markStreamClosed();
336877
- log.error("Subscribe stream error:", error48);
337007
+ terminalOutcome = "error";
337008
+ terminalGrpcStatus = grpcStatusName2(typeof error48 === "object" && error48 !== null && "code" in error48 ? error48.code : undefined);
337009
+ logTerminalOutcome(call.cancelled ? "cancelled" : "error");
336878
337010
  otelMetrics?.recordCounter("subscribe_errors_total", 1, {
336879
337011
  error_type: error48 instanceof Error ? error48.message : "unknown"
336880
337012
  });
336881
337013
  });
336882
337014
  if (!authenticateRequest(call, whitelistIps)) {
337015
+ terminalOutcome = "error";
337016
+ terminalGrpcStatus = "PERMISSION_DENIED";
336883
337017
  otelMetrics?.recordCounter("subscribe_errors_total", 1, {
336884
337018
  error_type: "permission_denied"
336885
337019
  });
@@ -336890,32 +337024,22 @@ function createSubscribeHandler(deps) {
336890
337024
  call.destroy(new Error("Access denied: Unauthorized IP"));
336891
337025
  return;
336892
337026
  }
336893
- const metadata = call.metadata;
336894
- let subscriptionType2 = SubscriptionType.ORDERBOOK;
336895
337027
  try {
336896
- const request = call.request;
336897
- const { cex: cex3, symbol: symbol2, type: type2, options } = request;
336898
- subscriptionType2 = resolveSubscriptionType(type2);
336899
- log.info(`Request - Subscribe:`, {
336900
- cex: request.cex,
336901
- symbol: request.symbol,
336902
- type: subscriptionType2
336903
- });
336904
- const subscriptionTypeName = getSubscriptionTypeName(subscriptionType2);
337028
+ const { options } = request;
336905
337029
  otelMetrics?.recordCounter("subscribe_requests_total", 1, {
336906
337030
  cex: cex3 || "unknown",
336907
337031
  symbol: symbol2 || "unknown",
336908
337032
  type: subscriptionTypeName
336909
337033
  });
336910
337034
  if (!cex3 || !symbol2) {
336911
- await writeSubscribeError(call, isStreamClosed, {
337035
+ await writeTerminalError({
336912
337036
  data: JSON.stringify({
336913
337037
  error: "cex, symbol, and type are required"
336914
337038
  }),
336915
337039
  timestamp: Date.now(),
336916
337040
  symbol: symbol2 || "",
336917
337041
  type: subscriptionType2
336918
- });
337042
+ }, grpc13.status.INVALID_ARGUMENT);
336919
337043
  return;
336920
337044
  }
336921
337045
  if (isPublicMarketDataSubscription(subscriptionType2)) {
@@ -336944,7 +337068,7 @@ function createSubscribeHandler(deps) {
336944
337068
  } catch (error48) {
336945
337069
  const message = getErrorMessage(error48);
336946
337070
  if (!isStreamClosed()) {
336947
- await writeSubscribeError(call, isStreamClosed, {
337071
+ await writeTerminalError({
336948
337072
  data: JSON.stringify({ error: message }),
336949
337073
  timestamp: Date.now(),
336950
337074
  symbol: symbol2,
@@ -336965,14 +337089,14 @@ function createSubscribeHandler(deps) {
336965
337089
  const selectedBroker = selectedBrokerAccount?.exchange ?? createBroker(normalizedCex, metadata);
336966
337090
  const broker = selectedBroker ?? createPublicBroker(normalizedCex);
336967
337091
  if (!broker) {
336968
- await writeSubscribeError(call, isStreamClosed, {
337092
+ await writeTerminalError({
336969
337093
  data: JSON.stringify({
336970
337094
  error: "Exchange not registered and no API metadata found"
336971
337095
  }),
336972
337096
  timestamp: Date.now(),
336973
337097
  symbol: symbol2,
336974
337098
  type: subscriptionType2
336975
- });
337099
+ }, grpc13.status.NOT_FOUND);
336976
337100
  return;
336977
337101
  }
336978
337102
  if (!selectedBrokerAccount) {
@@ -337000,28 +337124,28 @@ function createSubscribeHandler(deps) {
337000
337124
  if (isBinanceSpotAccountSubscription(normalizedCex, subscriptionType2, options?.marketType)) {
337001
337125
  const accountBroker = selectedBrokerAccount?.exchange ?? selectedBroker;
337002
337126
  if (!accountBroker) {
337003
- await writeSubscribeError(call, isStreamClosed, {
337127
+ await writeTerminalError({
337004
337128
  data: JSON.stringify({
337005
337129
  error: "Binance account subscriptions require API credentials"
337006
337130
  }),
337007
337131
  timestamp: Date.now(),
337008
337132
  symbol: resolvedSymbol,
337009
337133
  type: subscriptionType2
337010
- });
337134
+ }, grpc13.status.FAILED_PRECONDITION);
337011
337135
  return;
337012
337136
  }
337013
337137
  const marketId = subscriptionType2 === SubscriptionType.ORDERS ? await getBinanceMarketId(accountBroker, resolvedSymbol) : undefined;
337014
337138
  let userDataSource;
337015
337139
  if (selectedBrokerAccount) {
337016
337140
  if (!userDataStreamSupervisor) {
337017
- await writeSubscribeError(call, isStreamClosed, {
337141
+ await writeTerminalError({
337018
337142
  data: JSON.stringify({
337019
337143
  error: "Configured account user-data supervisor is unavailable"
337020
337144
  }),
337021
337145
  timestamp: Date.now(),
337022
337146
  symbol: resolvedSymbol,
337023
337147
  type: subscriptionType2
337024
- });
337148
+ }, grpc13.status.FAILED_PRECONDITION);
337025
337149
  return;
337026
337150
  }
337027
337151
  userDataSource = userDataStreamSupervisor.subscribe({
@@ -337044,7 +337168,7 @@ function createSubscribeHandler(deps) {
337044
337168
  } catch (error48) {
337045
337169
  const message = getErrorMessage(error48);
337046
337170
  log.error(`Error fetching balance for ${cex3}:`, error48);
337047
- await writeSubscribeError(call, isStreamClosed, {
337171
+ await writeTerminalError({
337048
337172
  data: JSON.stringify({
337049
337173
  error: `Failed to fetch balance: ${message}`
337050
337174
  }),
@@ -337063,7 +337187,7 @@ function createSubscribeHandler(deps) {
337063
337187
  } catch (error48) {
337064
337188
  log.error(`Error fetching orders for ${resolvedSymbol} on ${cex3}:`, error48);
337065
337189
  const message = getErrorMessage(error48);
337066
- await writeSubscribeError(call, isStreamClosed, {
337190
+ await writeTerminalError({
337067
337191
  data: JSON.stringify({
337068
337192
  error: `Failed to fetch orders: ${message}`
337069
337193
  }),
@@ -337074,26 +337198,27 @@ function createSubscribeHandler(deps) {
337074
337198
  }
337075
337199
  break;
337076
337200
  default:
337077
- await writeSubscribeError(call, isStreamClosed, {
337201
+ await writeTerminalError({
337078
337202
  data: JSON.stringify({ error: "Invalid subscription type" }),
337079
337203
  timestamp: Date.now(),
337080
337204
  symbol: symbol2,
337081
337205
  type: subscriptionType2
337082
- });
337206
+ }, grpc13.status.INVALID_ARGUMENT);
337083
337207
  }
337084
337208
  } catch (error48) {
337085
337209
  log.error("Error in Subscribe stream:", error48);
337086
337210
  const message = getErrorMessage(error48);
337087
- await writeSubscribeError(call, isStreamClosed, {
337211
+ await writeTerminalError({
337088
337212
  data: JSON.stringify({ error: `Internal server error: ${message}` }),
337089
337213
  timestamp: Date.now(),
337090
337214
  symbol: "",
337091
337215
  type: subscriptionType2
337092
- });
337216
+ }, grpc13.status.INTERNAL);
337093
337217
  } finally {
337094
337218
  call.off("cancelled", closeOwnedBrokerOnCallEnd);
337095
337219
  call.off("error", closeOwnedBrokerOnCallEnd);
337096
337220
  await closeOwnedBroker();
337221
+ logTerminalOutcome(call.cancelled ? "cancelled" : terminalOutcome);
337097
337222
  }
337098
337223
  };
337099
337224
  }
@@ -337587,12 +337712,7 @@ class CEXBroker {
337587
337712
  if (this.brokerArchiver) {
337588
337713
  await this.brokerArchiver.close();
337589
337714
  }
337590
- if (this.otelMetrics) {
337591
- await this.otelMetrics.close();
337592
- }
337593
- if (this.otelLogs) {
337594
- await this.otelLogs.close();
337595
- }
337715
+ await Promise.all([this.otelMetrics?.close(), this.otelLogs?.close()]);
337596
337716
  }
337597
337717
  async run() {
337598
337718
  const marketArchiveState = resolveMarketCaptureArchiveState({
@@ -337717,7 +337837,24 @@ async function startBrokerCommand(policyPath, port, whitelistIps, verityProverUr
337717
337837
  });
337718
337838
  broker.loadEnvConfig();
337719
337839
  await broker.run();
337840
+ return broker;
337841
+ }
337842
+
337843
+ // src/helpers/logger.ts
337844
+ var tslogLogger2 = new Logger({
337845
+ type: "pretty",
337846
+ stylePrettyLogs: true,
337847
+ minLevel: process.env.LOG_LEVEL === "debug" ? 0 : 3
337848
+ });
337849
+ var baseLogger2 = new LogLayer({
337850
+ transport: [new TsLogTransport({ id: "tslog", logger: tslogLogger2 })],
337851
+ plugins: [openTelemetryPlugin()],
337852
+ errorSerializer: serializeError
337853
+ });
337854
+ if (process.env.LOG_LEVEL !== "debug") {
337855
+ baseLogger2.setLevel("info");
337720
337856
  }
337857
+ var log2 = baseLogger2;
337721
337858
 
337722
337859
  // src/cli.ts
337723
337860
  var program2 = new Command;
@@ -337728,15 +337865,35 @@ program2.name("cex-broker").description("CLI to start the CEXBroker service").re
337728
337865
  const isValidIPv4 = (ip) => /^(\d{1,3}\.){3}\d{1,3}$/.test(ip) && ip.split(".").every((part) => Number(part) >= 0 && Number(part) <= 255);
337729
337866
  for (const ip of whitelist) {
337730
337867
  if (!isValidIPv4(ip)) {
337731
- console.error(`\u274C Invalid IPv4 address: ${ip}`);
337732
- process.exit(1);
337868
+ throw new Error(`Invalid IPv4 address: ${ip}`);
337733
337869
  }
337734
337870
  }
337735
337871
  }
337736
- await startBrokerCommand(options.policy, parseInt(options.port, 10), whitelist, options.verityProverUrl);
337872
+ const broker = await startBrokerCommand(options.policy, parseInt(options.port, 10), whitelist, options.verityProverUrl);
337873
+ let shutdownPromise;
337874
+ const onSignal = (signal) => {
337875
+ if (shutdownPromise)
337876
+ return;
337877
+ log2.withMetadata({ signal }).info("CEXBroker graceful shutdown requested");
337878
+ shutdownPromise = (async () => {
337879
+ try {
337880
+ await broker.stop();
337881
+ log2.withMetadata({ signal }).info("CEXBroker graceful shutdown complete");
337882
+ process.exitCode = 0;
337883
+ } catch (error48) {
337884
+ log2.withMetadata({ signal, error: error48 }).error("CEXBroker graceful shutdown failed");
337885
+ process.exitCode = 1;
337886
+ } finally {
337887
+ process.off("SIGTERM", onSignal);
337888
+ process.off("SIGINT", onSignal);
337889
+ }
337890
+ })();
337891
+ };
337892
+ process.on("SIGTERM", onSignal);
337893
+ process.on("SIGINT", onSignal);
337737
337894
  } catch (err2) {
337738
337895
  console.error("\u274C Failed to start broker:", err2);
337739
- process.exit(1);
337896
+ process.exitCode = 1;
337740
337897
  }
337741
337898
  });
337742
- program2.parse(process.argv);
337899
+ await program2.parseAsync(process.argv);