optropic 2.2.0 → 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -973,18 +973,20 @@ var SchemasResource = class {
973
973
  // src/client.ts
974
974
  var DEFAULT_BASE_URL = "https://api.optropic.com";
975
975
  var DEFAULT_TIMEOUT = 3e4;
976
- var SDK_VERSION = "2.2.0";
976
+ var SDK_VERSION = "2.3.0";
977
977
  var SANDBOX_PREFIXES = ["optr_test_"];
978
978
  var DEFAULT_RETRY_CONFIG = {
979
979
  maxRetries: 3,
980
980
  baseDelay: 1e3,
981
981
  maxDelay: 1e4
982
982
  };
983
+ var KEY_REDACT_RE = /(optr_(?:live|test)_)[a-zA-Z0-9_-]+/g;
983
984
  var OptropicClient = class {
984
985
  config;
985
986
  baseUrl;
986
987
  retryConfig;
987
988
  _sandbox;
989
+ _debug;
988
990
  assets;
989
991
  audit;
990
992
  compliance;
@@ -1003,6 +1005,7 @@ var OptropicClient = class {
1003
1005
  ...config,
1004
1006
  timeout: config.timeout ?? DEFAULT_TIMEOUT
1005
1007
  };
1008
+ this._debug = config.debug ?? false;
1006
1009
  if (config.sandbox !== void 0) {
1007
1010
  this._sandbox = config.sandbox;
1008
1011
  } else {
@@ -1043,6 +1046,31 @@ var OptropicClient = class {
1043
1046
  return this._sandbox ? "sandbox" : "live";
1044
1047
  }
1045
1048
  // ─────────────────────────────────────────────────────────────────────────
1049
+ // DEBUG LOGGING
1050
+ // ─────────────────────────────────────────────────────────────────────────
1051
+ redact(text) {
1052
+ return text.replace(KEY_REDACT_RE, "$1****");
1053
+ }
1054
+ logRequest(method, url, requestId, idempotencyKey) {
1055
+ if (!this._debug) return;
1056
+ const parts = [`${method} ${this.redact(url)}`, `req=${requestId}`];
1057
+ if (idempotencyKey) parts.push(`idempotency=${idempotencyKey}`);
1058
+ console.log(`[optropic] ${parts.join(" ")}`);
1059
+ }
1060
+ logResponse(method, path, status, durationMs, requestId, serverRequestId, attempt) {
1061
+ if (!this._debug) return;
1062
+ const parts = [`${method} ${path} \u2192 ${status} (${Math.round(durationMs)}ms)`, `req=${requestId}`];
1063
+ if (serverRequestId) parts.push(`server_req=${serverRequestId}`);
1064
+ if (attempt > 0) parts.push(`attempt=${attempt + 1}`);
1065
+ console.log(`[optropic] ${parts.join(" ")}`);
1066
+ }
1067
+ logRetry(method, path, attempt, delay, reason) {
1068
+ if (!this._debug) return;
1069
+ console.log(
1070
+ `[optropic] ${method} ${path} retry #${attempt + 1} in ${(delay / 1e3).toFixed(1)}s (${reason})`
1071
+ );
1072
+ }
1073
+ // ─────────────────────────────────────────────────────────────────────────
1046
1074
  // PRIVATE METHODS
1047
1075
  // ─────────────────────────────────────────────────────────────────────────
1048
1076
  isValidApiKey(apiKey) {
@@ -1051,20 +1079,26 @@ var OptropicClient = class {
1051
1079
  async request(options) {
1052
1080
  const { method, path, body, headers = {}, timeout = this.config.timeout, idempotencyKey } = options;
1053
1081
  const url = `${this.baseUrl}${path}`;
1082
+ const requestId = crypto.randomUUID();
1054
1083
  const requestHeaders = {
1055
1084
  "Content-Type": "application/json",
1056
1085
  "Accept": "application/json",
1057
1086
  "x-api-key": this.config.apiKey,
1058
1087
  "X-SDK-Version": SDK_VERSION,
1059
1088
  "X-SDK-Language": "typescript",
1089
+ "X-Request-ID": requestId,
1060
1090
  ...this.config.headers,
1061
1091
  ...headers
1062
1092
  };
1093
+ let effectiveIdempotencyKey;
1063
1094
  if (idempotencyKey) {
1064
1095
  requestHeaders["Idempotency-Key"] = idempotencyKey;
1096
+ effectiveIdempotencyKey = idempotencyKey;
1065
1097
  } else if (["POST", "PUT", "PATCH"].includes(method)) {
1066
- requestHeaders["Idempotency-Key"] = crypto.randomUUID();
1098
+ effectiveIdempotencyKey = crypto.randomUUID();
1099
+ requestHeaders["Idempotency-Key"] = effectiveIdempotencyKey;
1067
1100
  }
1101
+ this.logRequest(method, url, requestId, effectiveIdempotencyKey);
1068
1102
  let lastError = null;
1069
1103
  let attempt = 0;
1070
1104
  while (attempt <= this.retryConfig.maxRetries) {
@@ -1074,7 +1108,10 @@ var OptropicClient = class {
1074
1108
  method,
1075
1109
  requestHeaders,
1076
1110
  body,
1077
- timeout
1111
+ timeout,
1112
+ requestId,
1113
+ path,
1114
+ attempt
1078
1115
  );
1079
1116
  return response;
1080
1117
  } catch (error) {
@@ -1092,8 +1129,12 @@ var OptropicClient = class {
1092
1129
  const jitter = baseDelay * 0.5 * Math.random();
1093
1130
  const delay = baseDelay + jitter;
1094
1131
  if (error instanceof RateLimitedError) {
1095
- await this.sleep(error.retryAfter * 1e3);
1132
+ const retryDelay = error.retryAfter * 1e3;
1133
+ this.logRetry(method, path, attempt, retryDelay, "rate_limited");
1134
+ await this.sleep(retryDelay);
1096
1135
  } else {
1136
+ const statusCode = error instanceof OptropicError ? error.statusCode : 0;
1137
+ this.logRetry(method, path, attempt, delay, `status=${statusCode}`);
1097
1138
  await this.sleep(delay);
1098
1139
  }
1099
1140
  attempt++;
@@ -1101,9 +1142,10 @@ var OptropicClient = class {
1101
1142
  }
1102
1143
  throw lastError ?? new OptropicError("UNKNOWN_ERROR", "Request failed");
1103
1144
  }
1104
- async executeRequest(url, method, headers, body, timeout) {
1145
+ async executeRequest(url, method, headers, body, timeout, requestId, path, attempt) {
1105
1146
  const controller = new AbortController();
1106
1147
  const timeoutId = setTimeout(() => controller.abort(), timeout);
1148
+ const t0 = performance.now();
1107
1149
  try {
1108
1150
  const response = await fetch(url, {
1109
1151
  method,
@@ -1112,7 +1154,9 @@ var OptropicClient = class {
1112
1154
  signal: controller.signal
1113
1155
  });
1114
1156
  clearTimeout(timeoutId);
1115
- const requestId = response.headers.get("x-request-id") ?? "";
1157
+ const durationMs = performance.now() - t0;
1158
+ const serverRequestId = response.headers.get("x-request-id") ?? "";
1159
+ this.logResponse(method, path, response.status, durationMs, requestId, serverRequestId, attempt);
1116
1160
  if (!response.ok) {
1117
1161
  let errorBody;
1118
1162
  try {
@@ -1135,7 +1179,7 @@ var OptropicClient = class {
1135
1179
  code: errorBody.code,
1136
1180
  message: errorBody.message,
1137
1181
  details: errorBody.details,
1138
- requestId
1182
+ requestId: serverRequestId || requestId
1139
1183
  });
1140
1184
  }
1141
1185
  if (response.status === 204) {
@@ -1161,8 +1205,12 @@ var OptropicClient = class {
1161
1205
  }
1162
1206
  if (error instanceof Error) {
1163
1207
  if (error.name === "AbortError") {
1208
+ const durationMs2 = performance.now() - t0;
1209
+ this.logResponse(method, path, 408, durationMs2, requestId, "", attempt);
1164
1210
  throw new TimeoutError(timeout);
1165
1211
  }
1212
+ const durationMs = performance.now() - t0;
1213
+ this.logResponse(method, path, 0, durationMs, requestId, "", attempt);
1166
1214
  throw new NetworkError(error.message, { cause: error });
1167
1215
  }
1168
1216
  throw new OptropicError("UNKNOWN_ERROR", "An unexpected error occurred", {
@@ -1421,7 +1469,7 @@ async function verifyWebhookSignature(options) {
1421
1469
  }
1422
1470
 
1423
1471
  // src/index.ts
1424
- var SDK_VERSION2 = "2.2.0";
1472
+ var SDK_VERSION2 = "2.3.0";
1425
1473
  // Annotate the CommonJS export names for ESM import in node:
1426
1474
  0 && (module.exports = {
1427
1475
  AssetsResource,
package/dist/index.d.cts CHANGED
@@ -44,6 +44,12 @@ interface OptropicConfig {
44
44
  * Retry configuration for failed requests.
45
45
  */
46
46
  readonly retry?: RetryConfig;
47
+ /**
48
+ * Enable debug logging. When ``true``, every request and response
49
+ * is logged to the console with secrets redacted.
50
+ * @default false
51
+ */
52
+ readonly debug?: boolean;
47
53
  }
48
54
  /**
49
55
  * Retry configuration for network requests.
@@ -718,6 +724,7 @@ interface RequestOptions {
718
724
  path: string;
719
725
  body?: unknown;
720
726
  headers?: Record<string, string>;
727
+ /** Per-operation timeout in ms. Overrides the client-level default. */
721
728
  timeout?: number;
722
729
  /** Idempotency key for safe mutation retries. Auto-generated for POST/PUT/PATCH if omitted. */
723
730
  idempotencyKey?: string;
@@ -732,6 +739,7 @@ type RequestFn = <T>(options: RequestOptions) => Promise<T>;
732
739
  *
733
740
  * const client = new OptropicClient({
734
741
  * apiKey: 'optr_live_xxxxxxxxxxxx',
742
+ * debug: true, // logs every request/response
735
743
  * });
736
744
  *
737
745
  * // Create an asset
@@ -740,7 +748,7 @@ type RequestFn = <T>(options: RequestOptions) => Promise<T>;
740
748
  * serial: 'SN001',
741
749
  * });
742
750
  *
743
- * // Verify an asset
751
+ * // Verify an asset (with per-operation timeout)
744
752
  * const result = await client.assets.verify(asset.id);
745
753
  * ```
746
754
  */
@@ -749,6 +757,7 @@ declare class OptropicClient {
749
757
  private readonly baseUrl;
750
758
  private readonly retryConfig;
751
759
  private readonly _sandbox;
760
+ private readonly _debug;
752
761
  readonly assets: AssetsResource;
753
762
  readonly audit: AuditResource;
754
763
  readonly compliance: ComplianceResource;
@@ -764,6 +773,10 @@ declare class OptropicClient {
764
773
  get isLive(): boolean;
765
774
  /** Returns 'sandbox' or 'live'. */
766
775
  get environment(): 'sandbox' | 'live';
776
+ private redact;
777
+ private logRequest;
778
+ private logResponse;
779
+ private logRetry;
767
780
  private isValidApiKey;
768
781
  private request;
769
782
  private executeRequest;
@@ -781,6 +794,7 @@ declare class OptropicClient {
781
794
  *
782
795
  * const client = createClient({
783
796
  * apiKey: process.env.OPTROPIC_API_KEY!,
797
+ * debug: true,
784
798
  * });
785
799
  * ```
786
800
  */
@@ -1310,6 +1324,6 @@ declare function createErrorFromResponse(statusCode: number, body: {
1310
1324
  requestId?: string;
1311
1325
  }): OptropicError;
1312
1326
 
1313
- declare const SDK_VERSION = "2.2.0";
1327
+ declare const SDK_VERSION = "2.3.0";
1314
1328
 
1315
1329
  export { type ApiKey, type Asset, AssetsResource, type AuditEvent, AuditResource, AuthenticationError, type BatchCreateParams, type BatchCreateResult, BatchNotFoundError, type BatteryPassportData, type ChainVerifyResult, CodeNotFoundError, type ComplianceConfig, ComplianceResource, type ConformityDeclaration, type CreateAssetParams, type CreateAuditEventParams, type CreateKeyParams, type CreateKeyResult, type CreateKeysetParams, type CreateSchemaParams, type DPPCategory, type DPPMetadata, type Document, type DocumentVerifyResult, DocumentsResource, type EnrollDocumentParams, type ErrorCode, type ExportParams, type ExportResult, type FiberEntry, type FilterHeader, type FilterSyncResult, InvalidCodeError, InvalidGTINError, InvalidSerialError, KeysResource, type Keyset, KeysetsResource, type ListAssetsParams, type ListAssetsResponse, type ListAuditParams, type ListAuditResponse, type ListDocumentsParams, type ListDocumentsResponse, type ListKeysetsParams, type ListKeysetsResponse, type ListProvenanceParams, type ListProvenanceResponse, type ListSchemasParams, type ListSchemasResponse, type MerkleProof, type MerkleRoot, NetworkError, type OfflineVerifyOptions, type OfflineVerifyResult, OptropicClient, type OptropicConfig, OptropicError, type ProvenanceChain, type ProvenanceEvent, type ProvenanceEventType, type ProvenanceLocation, ProvenanceResource, QuotaExceededError, RateLimitedError, type RecordProvenanceParams, type RequestFn, type RetryConfig, RevokedCodeError, SDK_VERSION, type SchemaValidationResult, SchemasResource, ServiceUnavailableError, StaleFilterError, type SubstanceEntry, type TextilePassportData, TimeoutError, type UpdateSchemaParams, type VerifyDocumentParams, type VerifyProvenanceResult, type VerifyResult, type VerticalSchema, type WebhookVerifyOptions, type WebhookVerifyResult, buildDPPConfig, createClient, createErrorFromResponse, parseFilterHeader, parseSaltsHeader, validateDPPMetadata, verifyOffline, verifyWebhookSignature };
package/dist/index.d.ts CHANGED
@@ -44,6 +44,12 @@ interface OptropicConfig {
44
44
  * Retry configuration for failed requests.
45
45
  */
46
46
  readonly retry?: RetryConfig;
47
+ /**
48
+ * Enable debug logging. When ``true``, every request and response
49
+ * is logged to the console with secrets redacted.
50
+ * @default false
51
+ */
52
+ readonly debug?: boolean;
47
53
  }
48
54
  /**
49
55
  * Retry configuration for network requests.
@@ -718,6 +724,7 @@ interface RequestOptions {
718
724
  path: string;
719
725
  body?: unknown;
720
726
  headers?: Record<string, string>;
727
+ /** Per-operation timeout in ms. Overrides the client-level default. */
721
728
  timeout?: number;
722
729
  /** Idempotency key for safe mutation retries. Auto-generated for POST/PUT/PATCH if omitted. */
723
730
  idempotencyKey?: string;
@@ -732,6 +739,7 @@ type RequestFn = <T>(options: RequestOptions) => Promise<T>;
732
739
  *
733
740
  * const client = new OptropicClient({
734
741
  * apiKey: 'optr_live_xxxxxxxxxxxx',
742
+ * debug: true, // logs every request/response
735
743
  * });
736
744
  *
737
745
  * // Create an asset
@@ -740,7 +748,7 @@ type RequestFn = <T>(options: RequestOptions) => Promise<T>;
740
748
  * serial: 'SN001',
741
749
  * });
742
750
  *
743
- * // Verify an asset
751
+ * // Verify an asset (with per-operation timeout)
744
752
  * const result = await client.assets.verify(asset.id);
745
753
  * ```
746
754
  */
@@ -749,6 +757,7 @@ declare class OptropicClient {
749
757
  private readonly baseUrl;
750
758
  private readonly retryConfig;
751
759
  private readonly _sandbox;
760
+ private readonly _debug;
752
761
  readonly assets: AssetsResource;
753
762
  readonly audit: AuditResource;
754
763
  readonly compliance: ComplianceResource;
@@ -764,6 +773,10 @@ declare class OptropicClient {
764
773
  get isLive(): boolean;
765
774
  /** Returns 'sandbox' or 'live'. */
766
775
  get environment(): 'sandbox' | 'live';
776
+ private redact;
777
+ private logRequest;
778
+ private logResponse;
779
+ private logRetry;
767
780
  private isValidApiKey;
768
781
  private request;
769
782
  private executeRequest;
@@ -781,6 +794,7 @@ declare class OptropicClient {
781
794
  *
782
795
  * const client = createClient({
783
796
  * apiKey: process.env.OPTROPIC_API_KEY!,
797
+ * debug: true,
784
798
  * });
785
799
  * ```
786
800
  */
@@ -1310,6 +1324,6 @@ declare function createErrorFromResponse(statusCode: number, body: {
1310
1324
  requestId?: string;
1311
1325
  }): OptropicError;
1312
1326
 
1313
- declare const SDK_VERSION = "2.2.0";
1327
+ declare const SDK_VERSION = "2.3.0";
1314
1328
 
1315
1329
  export { type ApiKey, type Asset, AssetsResource, type AuditEvent, AuditResource, AuthenticationError, type BatchCreateParams, type BatchCreateResult, BatchNotFoundError, type BatteryPassportData, type ChainVerifyResult, CodeNotFoundError, type ComplianceConfig, ComplianceResource, type ConformityDeclaration, type CreateAssetParams, type CreateAuditEventParams, type CreateKeyParams, type CreateKeyResult, type CreateKeysetParams, type CreateSchemaParams, type DPPCategory, type DPPMetadata, type Document, type DocumentVerifyResult, DocumentsResource, type EnrollDocumentParams, type ErrorCode, type ExportParams, type ExportResult, type FiberEntry, type FilterHeader, type FilterSyncResult, InvalidCodeError, InvalidGTINError, InvalidSerialError, KeysResource, type Keyset, KeysetsResource, type ListAssetsParams, type ListAssetsResponse, type ListAuditParams, type ListAuditResponse, type ListDocumentsParams, type ListDocumentsResponse, type ListKeysetsParams, type ListKeysetsResponse, type ListProvenanceParams, type ListProvenanceResponse, type ListSchemasParams, type ListSchemasResponse, type MerkleProof, type MerkleRoot, NetworkError, type OfflineVerifyOptions, type OfflineVerifyResult, OptropicClient, type OptropicConfig, OptropicError, type ProvenanceChain, type ProvenanceEvent, type ProvenanceEventType, type ProvenanceLocation, ProvenanceResource, QuotaExceededError, RateLimitedError, type RecordProvenanceParams, type RequestFn, type RetryConfig, RevokedCodeError, SDK_VERSION, type SchemaValidationResult, SchemasResource, ServiceUnavailableError, StaleFilterError, type SubstanceEntry, type TextilePassportData, TimeoutError, type UpdateSchemaParams, type VerifyDocumentParams, type VerifyProvenanceResult, type VerifyResult, type VerticalSchema, type WebhookVerifyOptions, type WebhookVerifyResult, buildDPPConfig, createClient, createErrorFromResponse, parseFilterHeader, parseSaltsHeader, validateDPPMetadata, verifyOffline, verifyWebhookSignature };
package/dist/index.js CHANGED
@@ -906,18 +906,20 @@ var SchemasResource = class {
906
906
  // src/client.ts
907
907
  var DEFAULT_BASE_URL = "https://api.optropic.com";
908
908
  var DEFAULT_TIMEOUT = 3e4;
909
- var SDK_VERSION = "2.2.0";
909
+ var SDK_VERSION = "2.3.0";
910
910
  var SANDBOX_PREFIXES = ["optr_test_"];
911
911
  var DEFAULT_RETRY_CONFIG = {
912
912
  maxRetries: 3,
913
913
  baseDelay: 1e3,
914
914
  maxDelay: 1e4
915
915
  };
916
+ var KEY_REDACT_RE = /(optr_(?:live|test)_)[a-zA-Z0-9_-]+/g;
916
917
  var OptropicClient = class {
917
918
  config;
918
919
  baseUrl;
919
920
  retryConfig;
920
921
  _sandbox;
922
+ _debug;
921
923
  assets;
922
924
  audit;
923
925
  compliance;
@@ -936,6 +938,7 @@ var OptropicClient = class {
936
938
  ...config,
937
939
  timeout: config.timeout ?? DEFAULT_TIMEOUT
938
940
  };
941
+ this._debug = config.debug ?? false;
939
942
  if (config.sandbox !== void 0) {
940
943
  this._sandbox = config.sandbox;
941
944
  } else {
@@ -976,6 +979,31 @@ var OptropicClient = class {
976
979
  return this._sandbox ? "sandbox" : "live";
977
980
  }
978
981
  // ─────────────────────────────────────────────────────────────────────────
982
+ // DEBUG LOGGING
983
+ // ─────────────────────────────────────────────────────────────────────────
984
+ redact(text) {
985
+ return text.replace(KEY_REDACT_RE, "$1****");
986
+ }
987
+ logRequest(method, url, requestId, idempotencyKey) {
988
+ if (!this._debug) return;
989
+ const parts = [`${method} ${this.redact(url)}`, `req=${requestId}`];
990
+ if (idempotencyKey) parts.push(`idempotency=${idempotencyKey}`);
991
+ console.log(`[optropic] ${parts.join(" ")}`);
992
+ }
993
+ logResponse(method, path, status, durationMs, requestId, serverRequestId, attempt) {
994
+ if (!this._debug) return;
995
+ const parts = [`${method} ${path} \u2192 ${status} (${Math.round(durationMs)}ms)`, `req=${requestId}`];
996
+ if (serverRequestId) parts.push(`server_req=${serverRequestId}`);
997
+ if (attempt > 0) parts.push(`attempt=${attempt + 1}`);
998
+ console.log(`[optropic] ${parts.join(" ")}`);
999
+ }
1000
+ logRetry(method, path, attempt, delay, reason) {
1001
+ if (!this._debug) return;
1002
+ console.log(
1003
+ `[optropic] ${method} ${path} retry #${attempt + 1} in ${(delay / 1e3).toFixed(1)}s (${reason})`
1004
+ );
1005
+ }
1006
+ // ─────────────────────────────────────────────────────────────────────────
979
1007
  // PRIVATE METHODS
980
1008
  // ─────────────────────────────────────────────────────────────────────────
981
1009
  isValidApiKey(apiKey) {
@@ -984,20 +1012,26 @@ var OptropicClient = class {
984
1012
  async request(options) {
985
1013
  const { method, path, body, headers = {}, timeout = this.config.timeout, idempotencyKey } = options;
986
1014
  const url = `${this.baseUrl}${path}`;
1015
+ const requestId = crypto.randomUUID();
987
1016
  const requestHeaders = {
988
1017
  "Content-Type": "application/json",
989
1018
  "Accept": "application/json",
990
1019
  "x-api-key": this.config.apiKey,
991
1020
  "X-SDK-Version": SDK_VERSION,
992
1021
  "X-SDK-Language": "typescript",
1022
+ "X-Request-ID": requestId,
993
1023
  ...this.config.headers,
994
1024
  ...headers
995
1025
  };
1026
+ let effectiveIdempotencyKey;
996
1027
  if (idempotencyKey) {
997
1028
  requestHeaders["Idempotency-Key"] = idempotencyKey;
1029
+ effectiveIdempotencyKey = idempotencyKey;
998
1030
  } else if (["POST", "PUT", "PATCH"].includes(method)) {
999
- requestHeaders["Idempotency-Key"] = crypto.randomUUID();
1031
+ effectiveIdempotencyKey = crypto.randomUUID();
1032
+ requestHeaders["Idempotency-Key"] = effectiveIdempotencyKey;
1000
1033
  }
1034
+ this.logRequest(method, url, requestId, effectiveIdempotencyKey);
1001
1035
  let lastError = null;
1002
1036
  let attempt = 0;
1003
1037
  while (attempt <= this.retryConfig.maxRetries) {
@@ -1007,7 +1041,10 @@ var OptropicClient = class {
1007
1041
  method,
1008
1042
  requestHeaders,
1009
1043
  body,
1010
- timeout
1044
+ timeout,
1045
+ requestId,
1046
+ path,
1047
+ attempt
1011
1048
  );
1012
1049
  return response;
1013
1050
  } catch (error) {
@@ -1025,8 +1062,12 @@ var OptropicClient = class {
1025
1062
  const jitter = baseDelay * 0.5 * Math.random();
1026
1063
  const delay = baseDelay + jitter;
1027
1064
  if (error instanceof RateLimitedError) {
1028
- await this.sleep(error.retryAfter * 1e3);
1065
+ const retryDelay = error.retryAfter * 1e3;
1066
+ this.logRetry(method, path, attempt, retryDelay, "rate_limited");
1067
+ await this.sleep(retryDelay);
1029
1068
  } else {
1069
+ const statusCode = error instanceof OptropicError ? error.statusCode : 0;
1070
+ this.logRetry(method, path, attempt, delay, `status=${statusCode}`);
1030
1071
  await this.sleep(delay);
1031
1072
  }
1032
1073
  attempt++;
@@ -1034,9 +1075,10 @@ var OptropicClient = class {
1034
1075
  }
1035
1076
  throw lastError ?? new OptropicError("UNKNOWN_ERROR", "Request failed");
1036
1077
  }
1037
- async executeRequest(url, method, headers, body, timeout) {
1078
+ async executeRequest(url, method, headers, body, timeout, requestId, path, attempt) {
1038
1079
  const controller = new AbortController();
1039
1080
  const timeoutId = setTimeout(() => controller.abort(), timeout);
1081
+ const t0 = performance.now();
1040
1082
  try {
1041
1083
  const response = await fetch(url, {
1042
1084
  method,
@@ -1045,7 +1087,9 @@ var OptropicClient = class {
1045
1087
  signal: controller.signal
1046
1088
  });
1047
1089
  clearTimeout(timeoutId);
1048
- const requestId = response.headers.get("x-request-id") ?? "";
1090
+ const durationMs = performance.now() - t0;
1091
+ const serverRequestId = response.headers.get("x-request-id") ?? "";
1092
+ this.logResponse(method, path, response.status, durationMs, requestId, serverRequestId, attempt);
1049
1093
  if (!response.ok) {
1050
1094
  let errorBody;
1051
1095
  try {
@@ -1068,7 +1112,7 @@ var OptropicClient = class {
1068
1112
  code: errorBody.code,
1069
1113
  message: errorBody.message,
1070
1114
  details: errorBody.details,
1071
- requestId
1115
+ requestId: serverRequestId || requestId
1072
1116
  });
1073
1117
  }
1074
1118
  if (response.status === 204) {
@@ -1094,8 +1138,12 @@ var OptropicClient = class {
1094
1138
  }
1095
1139
  if (error instanceof Error) {
1096
1140
  if (error.name === "AbortError") {
1141
+ const durationMs2 = performance.now() - t0;
1142
+ this.logResponse(method, path, 408, durationMs2, requestId, "", attempt);
1097
1143
  throw new TimeoutError(timeout);
1098
1144
  }
1145
+ const durationMs = performance.now() - t0;
1146
+ this.logResponse(method, path, 0, durationMs, requestId, "", attempt);
1099
1147
  throw new NetworkError(error.message, { cause: error });
1100
1148
  }
1101
1149
  throw new OptropicError("UNKNOWN_ERROR", "An unexpected error occurred", {
@@ -1354,7 +1402,7 @@ async function verifyWebhookSignature(options) {
1354
1402
  }
1355
1403
 
1356
1404
  // src/index.ts
1357
- var SDK_VERSION2 = "2.2.0";
1405
+ var SDK_VERSION2 = "2.3.0";
1358
1406
  export {
1359
1407
  AssetsResource,
1360
1408
  AuditResource,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "optropic",
3
- "version": "2.2.0",
3
+ "version": "2.3.0",
4
4
  "description": "Official Optropic SDK for TypeScript and JavaScript",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",