optropic 2.2.0 → 2.4.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
@@ -45,6 +45,7 @@ __export(index_exports, {
45
45
  NetworkError: () => NetworkError,
46
46
  OptropicClient: () => OptropicClient,
47
47
  OptropicError: () => OptropicError,
48
+ Permission: () => Permission,
48
49
  ProvenanceResource: () => ProvenanceResource,
49
50
  QuotaExceededError: () => QuotaExceededError,
50
51
  RateLimitedError: () => RateLimitedError,
@@ -512,6 +513,20 @@ var AssetsResource = class {
512
513
  async batchCreate(params) {
513
514
  return this.request({ method: "POST", path: "/v1/assets/batch", body: params });
514
515
  }
516
+ async batchVerify(assetIds) {
517
+ return this.request({
518
+ method: "POST",
519
+ path: "/v1/assets/batch-verify",
520
+ body: { asset_ids: assetIds }
521
+ });
522
+ }
523
+ async batchRevoke(assetIds, reason) {
524
+ return this.request({
525
+ method: "POST",
526
+ path: "/v1/assets/batch-revoke",
527
+ body: { asset_ids: assetIds, reason }
528
+ });
529
+ }
515
530
  buildQuery(params) {
516
531
  const entries = Object.entries(params).filter(([, v]) => v !== void 0);
517
532
  if (entries.length === 0) return "";
@@ -973,18 +988,21 @@ var SchemasResource = class {
973
988
  // src/client.ts
974
989
  var DEFAULT_BASE_URL = "https://api.optropic.com";
975
990
  var DEFAULT_TIMEOUT = 3e4;
976
- var SDK_VERSION = "2.2.0";
991
+ var SDK_VERSION = "2.4.0";
977
992
  var SANDBOX_PREFIXES = ["optr_test_"];
978
993
  var DEFAULT_RETRY_CONFIG = {
979
994
  maxRetries: 3,
980
995
  baseDelay: 1e3,
981
996
  maxDelay: 1e4
982
997
  };
998
+ var KEY_REDACT_RE = /(optr_(?:live|test)_)[a-zA-Z0-9_-]+/g;
983
999
  var OptropicClient = class {
984
1000
  config;
985
1001
  baseUrl;
986
1002
  retryConfig;
987
1003
  _sandbox;
1004
+ _debug;
1005
+ _rateLimit = null;
988
1006
  assets;
989
1007
  audit;
990
1008
  compliance;
@@ -1003,6 +1021,7 @@ var OptropicClient = class {
1003
1021
  ...config,
1004
1022
  timeout: config.timeout ?? DEFAULT_TIMEOUT
1005
1023
  };
1024
+ this._debug = config.debug ?? false;
1006
1025
  if (config.sandbox !== void 0) {
1007
1026
  this._sandbox = config.sandbox;
1008
1027
  } else {
@@ -1042,6 +1061,35 @@ var OptropicClient = class {
1042
1061
  get environment() {
1043
1062
  return this._sandbox ? "sandbox" : "live";
1044
1063
  }
1064
+ /** Last known rate limit status, updated after every API call. Returns null until the first request. */
1065
+ get rateLimit() {
1066
+ return this._rateLimit;
1067
+ }
1068
+ // ─────────────────────────────────────────────────────────────────────────
1069
+ // DEBUG LOGGING
1070
+ // ─────────────────────────────────────────────────────────────────────────
1071
+ redact(text) {
1072
+ return text.replace(KEY_REDACT_RE, "$1****");
1073
+ }
1074
+ logRequest(method, url, requestId, idempotencyKey) {
1075
+ if (!this._debug) return;
1076
+ const parts = [`${method} ${this.redact(url)}`, `req=${requestId}`];
1077
+ if (idempotencyKey) parts.push(`idempotency=${idempotencyKey}`);
1078
+ console.log(`[optropic] ${parts.join(" ")}`);
1079
+ }
1080
+ logResponse(method, path, status, durationMs, requestId, serverRequestId, attempt) {
1081
+ if (!this._debug) return;
1082
+ const parts = [`${method} ${path} \u2192 ${status} (${Math.round(durationMs)}ms)`, `req=${requestId}`];
1083
+ if (serverRequestId) parts.push(`server_req=${serverRequestId}`);
1084
+ if (attempt > 0) parts.push(`attempt=${attempt + 1}`);
1085
+ console.log(`[optropic] ${parts.join(" ")}`);
1086
+ }
1087
+ logRetry(method, path, attempt, delay, reason) {
1088
+ if (!this._debug) return;
1089
+ console.log(
1090
+ `[optropic] ${method} ${path} retry #${attempt + 1} in ${(delay / 1e3).toFixed(1)}s (${reason})`
1091
+ );
1092
+ }
1045
1093
  // ─────────────────────────────────────────────────────────────────────────
1046
1094
  // PRIVATE METHODS
1047
1095
  // ─────────────────────────────────────────────────────────────────────────
@@ -1051,20 +1099,26 @@ var OptropicClient = class {
1051
1099
  async request(options) {
1052
1100
  const { method, path, body, headers = {}, timeout = this.config.timeout, idempotencyKey } = options;
1053
1101
  const url = `${this.baseUrl}${path}`;
1102
+ const requestId = crypto.randomUUID();
1054
1103
  const requestHeaders = {
1055
1104
  "Content-Type": "application/json",
1056
1105
  "Accept": "application/json",
1057
1106
  "x-api-key": this.config.apiKey,
1058
1107
  "X-SDK-Version": SDK_VERSION,
1059
1108
  "X-SDK-Language": "typescript",
1109
+ "X-Request-ID": requestId,
1060
1110
  ...this.config.headers,
1061
1111
  ...headers
1062
1112
  };
1113
+ let effectiveIdempotencyKey;
1063
1114
  if (idempotencyKey) {
1064
1115
  requestHeaders["Idempotency-Key"] = idempotencyKey;
1116
+ effectiveIdempotencyKey = idempotencyKey;
1065
1117
  } else if (["POST", "PUT", "PATCH"].includes(method)) {
1066
- requestHeaders["Idempotency-Key"] = crypto.randomUUID();
1118
+ effectiveIdempotencyKey = crypto.randomUUID();
1119
+ requestHeaders["Idempotency-Key"] = effectiveIdempotencyKey;
1067
1120
  }
1121
+ this.logRequest(method, url, requestId, effectiveIdempotencyKey);
1068
1122
  let lastError = null;
1069
1123
  let attempt = 0;
1070
1124
  while (attempt <= this.retryConfig.maxRetries) {
@@ -1074,7 +1128,10 @@ var OptropicClient = class {
1074
1128
  method,
1075
1129
  requestHeaders,
1076
1130
  body,
1077
- timeout
1131
+ timeout,
1132
+ requestId,
1133
+ path,
1134
+ attempt
1078
1135
  );
1079
1136
  return response;
1080
1137
  } catch (error) {
@@ -1092,8 +1149,12 @@ var OptropicClient = class {
1092
1149
  const jitter = baseDelay * 0.5 * Math.random();
1093
1150
  const delay = baseDelay + jitter;
1094
1151
  if (error instanceof RateLimitedError) {
1095
- await this.sleep(error.retryAfter * 1e3);
1152
+ const retryDelay = error.retryAfter * 1e3;
1153
+ this.logRetry(method, path, attempt, retryDelay, "rate_limited");
1154
+ await this.sleep(retryDelay);
1096
1155
  } else {
1156
+ const statusCode = error instanceof OptropicError ? error.statusCode : 0;
1157
+ this.logRetry(method, path, attempt, delay, `status=${statusCode}`);
1097
1158
  await this.sleep(delay);
1098
1159
  }
1099
1160
  attempt++;
@@ -1101,9 +1162,10 @@ var OptropicClient = class {
1101
1162
  }
1102
1163
  throw lastError ?? new OptropicError("UNKNOWN_ERROR", "Request failed");
1103
1164
  }
1104
- async executeRequest(url, method, headers, body, timeout) {
1165
+ async executeRequest(url, method, headers, body, timeout, requestId, path, attempt) {
1105
1166
  const controller = new AbortController();
1106
1167
  const timeoutId = setTimeout(() => controller.abort(), timeout);
1168
+ const t0 = performance.now();
1107
1169
  try {
1108
1170
  const response = await fetch(url, {
1109
1171
  method,
@@ -1112,7 +1174,18 @@ var OptropicClient = class {
1112
1174
  signal: controller.signal
1113
1175
  });
1114
1176
  clearTimeout(timeoutId);
1115
- const requestId = response.headers.get("x-request-id") ?? "";
1177
+ const durationMs = performance.now() - t0;
1178
+ const serverRequestId = response.headers.get("x-request-id") ?? "";
1179
+ this.logResponse(method, path, response.status, durationMs, requestId, serverRequestId, attempt);
1180
+ const rlLimit = response.headers.get("x-ratelimit-limit");
1181
+ const rlRemaining = response.headers.get("x-ratelimit-remaining");
1182
+ if (rlLimit !== null || rlRemaining !== null) {
1183
+ this._rateLimit = {
1184
+ limit: rlLimit ? parseInt(rlLimit, 10) : 0,
1185
+ remaining: rlRemaining ? parseInt(rlRemaining, 10) : 0,
1186
+ reset: response.headers.get("x-ratelimit-reset") ?? void 0
1187
+ };
1188
+ }
1116
1189
  if (!response.ok) {
1117
1190
  let errorBody;
1118
1191
  try {
@@ -1135,7 +1208,7 @@ var OptropicClient = class {
1135
1208
  code: errorBody.code,
1136
1209
  message: errorBody.message,
1137
1210
  details: errorBody.details,
1138
- requestId
1211
+ requestId: serverRequestId || requestId
1139
1212
  });
1140
1213
  }
1141
1214
  if (response.status === 204) {
@@ -1161,8 +1234,12 @@ var OptropicClient = class {
1161
1234
  }
1162
1235
  if (error instanceof Error) {
1163
1236
  if (error.name === "AbortError") {
1237
+ const durationMs2 = performance.now() - t0;
1238
+ this.logResponse(method, path, 408, durationMs2, requestId, "", attempt);
1164
1239
  throw new TimeoutError(timeout);
1165
1240
  }
1241
+ const durationMs = performance.now() - t0;
1242
+ this.logResponse(method, path, 0, durationMs, requestId, "", attempt);
1166
1243
  throw new NetworkError(error.message, { cause: error });
1167
1244
  }
1168
1245
  throw new OptropicError("UNKNOWN_ERROR", "An unexpected error occurred", {
@@ -1178,6 +1255,39 @@ function createClient(config) {
1178
1255
  return new OptropicClient(config);
1179
1256
  }
1180
1257
 
1258
+ // src/types.ts
1259
+ var Permission = {
1260
+ ASSETS_READ: "assets:read",
1261
+ ASSETS_WRITE: "assets:write",
1262
+ ASSETS_VERIFY: "assets:verify",
1263
+ AUDIT_READ: "audit:read",
1264
+ COMPLIANCE_READ: "compliance:read",
1265
+ KEYS_MANAGE: "keys:manage",
1266
+ SCHEMAS_MANAGE: "schemas:manage",
1267
+ DOCUMENTS_ENROLL: "documents:enroll",
1268
+ DOCUMENTS_VERIFY: "documents:verify",
1269
+ PROVENANCE_READ: "provenance:read",
1270
+ PROVENANCE_WRITE: "provenance:write",
1271
+ WEBHOOKS_MANAGE: "webhooks:manage",
1272
+ /** All read permissions. */
1273
+ ALL_READ: ["assets:read", "audit:read", "compliance:read", "provenance:read"],
1274
+ /** All write permissions. */
1275
+ ALL_WRITE: [
1276
+ "assets:write",
1277
+ "assets:verify",
1278
+ "documents:enroll",
1279
+ "documents:verify",
1280
+ "provenance:write",
1281
+ "webhooks:manage",
1282
+ "keys:manage",
1283
+ "schemas:manage"
1284
+ ],
1285
+ /** All permissions combined. */
1286
+ all() {
1287
+ return [...this.ALL_READ, ...this.ALL_WRITE];
1288
+ }
1289
+ };
1290
+
1181
1291
  // src/filter-verify.ts
1182
1292
  var HEADER_SIZE = 19;
1183
1293
  var SIGNATURE_SIZE = 64;
@@ -1421,7 +1531,7 @@ async function verifyWebhookSignature(options) {
1421
1531
  }
1422
1532
 
1423
1533
  // src/index.ts
1424
- var SDK_VERSION2 = "2.2.0";
1534
+ var SDK_VERSION2 = "2.4.0";
1425
1535
  // Annotate the CommonJS export names for ESM import in node:
1426
1536
  0 && (module.exports = {
1427
1537
  AssetsResource,
@@ -1439,6 +1549,7 @@ var SDK_VERSION2 = "2.2.0";
1439
1549
  NetworkError,
1440
1550
  OptropicClient,
1441
1551
  OptropicError,
1552
+ Permission,
1442
1553
  ProvenanceResource,
1443
1554
  QuotaExceededError,
1444
1555
  RateLimitedError,
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.
@@ -71,6 +77,53 @@ interface RetryConfig {
71
77
  * Use these for programmatic error handling.
72
78
  */
73
79
  type ErrorCode = 'INVALID_API_KEY' | 'EXPIRED_API_KEY' | 'INSUFFICIENT_PERMISSIONS' | 'INVALID_GTIN' | 'INVALID_SERIAL' | 'INVALID_CODE_FORMAT' | 'INVALID_BATCH_CONFIG' | 'CODE_NOT_FOUND' | 'BATCH_NOT_FOUND' | 'PRODUCT_NOT_FOUND' | 'CODE_REVOKED' | 'CODE_EXPIRED' | 'BATCH_ALREADY_EXISTS' | 'RATE_LIMITED' | 'QUOTA_EXCEEDED' | 'NETWORK_ERROR' | 'TIMEOUT' | 'SERVICE_UNAVAILABLE' | 'INTERNAL_ERROR' | 'UNKNOWN_ERROR';
80
+ /**
81
+ * Type-safe permission constants for API key creation.
82
+ *
83
+ * @example
84
+ * ```typescript
85
+ * import { Permission } from 'optropic';
86
+ *
87
+ * const key = await client.keys.create({
88
+ * environment: 'live',
89
+ * label: 'Read-only integration',
90
+ * permissions: [Permission.ASSETS_READ, Permission.ASSETS_VERIFY],
91
+ * });
92
+ * ```
93
+ */
94
+ declare const Permission: {
95
+ readonly ASSETS_READ: "assets:read";
96
+ readonly ASSETS_WRITE: "assets:write";
97
+ readonly ASSETS_VERIFY: "assets:verify";
98
+ readonly AUDIT_READ: "audit:read";
99
+ readonly COMPLIANCE_READ: "compliance:read";
100
+ readonly KEYS_MANAGE: "keys:manage";
101
+ readonly SCHEMAS_MANAGE: "schemas:manage";
102
+ readonly DOCUMENTS_ENROLL: "documents:enroll";
103
+ readonly DOCUMENTS_VERIFY: "documents:verify";
104
+ readonly PROVENANCE_READ: "provenance:read";
105
+ readonly PROVENANCE_WRITE: "provenance:write";
106
+ readonly WEBHOOKS_MANAGE: "webhooks:manage";
107
+ /** All read permissions. */
108
+ readonly ALL_READ: readonly ["assets:read", "audit:read", "compliance:read", "provenance:read"];
109
+ /** All write permissions. */
110
+ readonly ALL_WRITE: readonly ["assets:write", "assets:verify", "documents:enroll", "documents:verify", "provenance:write", "webhooks:manage", "keys:manage", "schemas:manage"];
111
+ /** All permissions combined. */
112
+ readonly all: () => string[];
113
+ };
114
+ type PermissionValue = typeof Permission[keyof Omit<typeof Permission, 'ALL_READ' | 'ALL_WRITE' | 'all'>];
115
+ /**
116
+ * Rate limit status parsed from response headers.
117
+ * Updated automatically after every API call.
118
+ */
119
+ interface RateLimitInfo {
120
+ /** Maximum requests allowed per period. */
121
+ readonly limit: number;
122
+ /** Remaining requests in current period. */
123
+ readonly remaining: number;
124
+ /** ISO 8601 timestamp when the limit resets. */
125
+ readonly reset?: string;
126
+ }
74
127
 
75
128
  /**
76
129
  * Assets Resource
@@ -146,6 +199,31 @@ interface BatchCreateResult {
146
199
  readonly requested: number;
147
200
  readonly assets: Asset[];
148
201
  }
202
+ interface BatchVerifyResult {
203
+ readonly verified: number;
204
+ readonly requested: number;
205
+ readonly results: Array<{
206
+ readonly assetId: string;
207
+ readonly signatureValid: boolean;
208
+ readonly revocationStatus: string;
209
+ }>;
210
+ readonly errors?: Array<{
211
+ readonly assetId: string;
212
+ readonly error: string;
213
+ }>;
214
+ }
215
+ interface BatchRevokeResult {
216
+ readonly revoked: number;
217
+ readonly requested: number;
218
+ readonly results: Array<{
219
+ readonly assetId: string;
220
+ readonly status: string;
221
+ }>;
222
+ readonly errors?: Array<{
223
+ readonly assetId: string;
224
+ readonly error: string;
225
+ }>;
226
+ }
149
227
  declare class AssetsResource {
150
228
  private readonly request;
151
229
  private readonly client;
@@ -175,6 +253,8 @@ declare class AssetsResource {
175
253
  verify(assetId: string): Promise<VerifyResult>;
176
254
  revoke(assetId: string, reason?: string): Promise<Asset>;
177
255
  batchCreate(params: BatchCreateParams): Promise<BatchCreateResult>;
256
+ batchVerify(assetIds: string[]): Promise<BatchVerifyResult>;
257
+ batchRevoke(assetIds: string[], reason: string): Promise<BatchRevokeResult>;
178
258
  private buildQuery;
179
259
  }
180
260
 
@@ -718,6 +798,7 @@ interface RequestOptions {
718
798
  path: string;
719
799
  body?: unknown;
720
800
  headers?: Record<string, string>;
801
+ /** Per-operation timeout in ms. Overrides the client-level default. */
721
802
  timeout?: number;
722
803
  /** Idempotency key for safe mutation retries. Auto-generated for POST/PUT/PATCH if omitted. */
723
804
  idempotencyKey?: string;
@@ -732,6 +813,7 @@ type RequestFn = <T>(options: RequestOptions) => Promise<T>;
732
813
  *
733
814
  * const client = new OptropicClient({
734
815
  * apiKey: 'optr_live_xxxxxxxxxxxx',
816
+ * debug: true, // logs every request/response
735
817
  * });
736
818
  *
737
819
  * // Create an asset
@@ -740,7 +822,7 @@ type RequestFn = <T>(options: RequestOptions) => Promise<T>;
740
822
  * serial: 'SN001',
741
823
  * });
742
824
  *
743
- * // Verify an asset
825
+ * // Verify an asset (with per-operation timeout)
744
826
  * const result = await client.assets.verify(asset.id);
745
827
  * ```
746
828
  */
@@ -749,6 +831,8 @@ declare class OptropicClient {
749
831
  private readonly baseUrl;
750
832
  private readonly retryConfig;
751
833
  private readonly _sandbox;
834
+ private readonly _debug;
835
+ private _rateLimit;
752
836
  readonly assets: AssetsResource;
753
837
  readonly audit: AuditResource;
754
838
  readonly compliance: ComplianceResource;
@@ -764,6 +848,12 @@ declare class OptropicClient {
764
848
  get isLive(): boolean;
765
849
  /** Returns 'sandbox' or 'live'. */
766
850
  get environment(): 'sandbox' | 'live';
851
+ /** Last known rate limit status, updated after every API call. Returns null until the first request. */
852
+ get rateLimit(): RateLimitInfo | null;
853
+ private redact;
854
+ private logRequest;
855
+ private logResponse;
856
+ private logRetry;
767
857
  private isValidApiKey;
768
858
  private request;
769
859
  private executeRequest;
@@ -781,6 +871,7 @@ declare class OptropicClient {
781
871
  *
782
872
  * const client = createClient({
783
873
  * apiKey: process.env.OPTROPIC_API_KEY!,
874
+ * debug: true,
784
875
  * });
785
876
  * ```
786
877
  */
@@ -1310,6 +1401,6 @@ declare function createErrorFromResponse(statusCode: number, body: {
1310
1401
  requestId?: string;
1311
1402
  }): OptropicError;
1312
1403
 
1313
- declare const SDK_VERSION = "2.2.0";
1404
+ declare const SDK_VERSION = "2.4.0";
1314
1405
 
1315
- 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 };
1406
+ export { type ApiKey, type Asset, AssetsResource, type AuditEvent, AuditResource, AuthenticationError, type BatchCreateParams, type BatchCreateResult, BatchNotFoundError, type BatchRevokeResult, type BatchVerifyResult, 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, Permission, type PermissionValue, type ProvenanceChain, type ProvenanceEvent, type ProvenanceEventType, type ProvenanceLocation, ProvenanceResource, QuotaExceededError, type RateLimitInfo, 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.
@@ -71,6 +77,53 @@ interface RetryConfig {
71
77
  * Use these for programmatic error handling.
72
78
  */
73
79
  type ErrorCode = 'INVALID_API_KEY' | 'EXPIRED_API_KEY' | 'INSUFFICIENT_PERMISSIONS' | 'INVALID_GTIN' | 'INVALID_SERIAL' | 'INVALID_CODE_FORMAT' | 'INVALID_BATCH_CONFIG' | 'CODE_NOT_FOUND' | 'BATCH_NOT_FOUND' | 'PRODUCT_NOT_FOUND' | 'CODE_REVOKED' | 'CODE_EXPIRED' | 'BATCH_ALREADY_EXISTS' | 'RATE_LIMITED' | 'QUOTA_EXCEEDED' | 'NETWORK_ERROR' | 'TIMEOUT' | 'SERVICE_UNAVAILABLE' | 'INTERNAL_ERROR' | 'UNKNOWN_ERROR';
80
+ /**
81
+ * Type-safe permission constants for API key creation.
82
+ *
83
+ * @example
84
+ * ```typescript
85
+ * import { Permission } from 'optropic';
86
+ *
87
+ * const key = await client.keys.create({
88
+ * environment: 'live',
89
+ * label: 'Read-only integration',
90
+ * permissions: [Permission.ASSETS_READ, Permission.ASSETS_VERIFY],
91
+ * });
92
+ * ```
93
+ */
94
+ declare const Permission: {
95
+ readonly ASSETS_READ: "assets:read";
96
+ readonly ASSETS_WRITE: "assets:write";
97
+ readonly ASSETS_VERIFY: "assets:verify";
98
+ readonly AUDIT_READ: "audit:read";
99
+ readonly COMPLIANCE_READ: "compliance:read";
100
+ readonly KEYS_MANAGE: "keys:manage";
101
+ readonly SCHEMAS_MANAGE: "schemas:manage";
102
+ readonly DOCUMENTS_ENROLL: "documents:enroll";
103
+ readonly DOCUMENTS_VERIFY: "documents:verify";
104
+ readonly PROVENANCE_READ: "provenance:read";
105
+ readonly PROVENANCE_WRITE: "provenance:write";
106
+ readonly WEBHOOKS_MANAGE: "webhooks:manage";
107
+ /** All read permissions. */
108
+ readonly ALL_READ: readonly ["assets:read", "audit:read", "compliance:read", "provenance:read"];
109
+ /** All write permissions. */
110
+ readonly ALL_WRITE: readonly ["assets:write", "assets:verify", "documents:enroll", "documents:verify", "provenance:write", "webhooks:manage", "keys:manage", "schemas:manage"];
111
+ /** All permissions combined. */
112
+ readonly all: () => string[];
113
+ };
114
+ type PermissionValue = typeof Permission[keyof Omit<typeof Permission, 'ALL_READ' | 'ALL_WRITE' | 'all'>];
115
+ /**
116
+ * Rate limit status parsed from response headers.
117
+ * Updated automatically after every API call.
118
+ */
119
+ interface RateLimitInfo {
120
+ /** Maximum requests allowed per period. */
121
+ readonly limit: number;
122
+ /** Remaining requests in current period. */
123
+ readonly remaining: number;
124
+ /** ISO 8601 timestamp when the limit resets. */
125
+ readonly reset?: string;
126
+ }
74
127
 
75
128
  /**
76
129
  * Assets Resource
@@ -146,6 +199,31 @@ interface BatchCreateResult {
146
199
  readonly requested: number;
147
200
  readonly assets: Asset[];
148
201
  }
202
+ interface BatchVerifyResult {
203
+ readonly verified: number;
204
+ readonly requested: number;
205
+ readonly results: Array<{
206
+ readonly assetId: string;
207
+ readonly signatureValid: boolean;
208
+ readonly revocationStatus: string;
209
+ }>;
210
+ readonly errors?: Array<{
211
+ readonly assetId: string;
212
+ readonly error: string;
213
+ }>;
214
+ }
215
+ interface BatchRevokeResult {
216
+ readonly revoked: number;
217
+ readonly requested: number;
218
+ readonly results: Array<{
219
+ readonly assetId: string;
220
+ readonly status: string;
221
+ }>;
222
+ readonly errors?: Array<{
223
+ readonly assetId: string;
224
+ readonly error: string;
225
+ }>;
226
+ }
149
227
  declare class AssetsResource {
150
228
  private readonly request;
151
229
  private readonly client;
@@ -175,6 +253,8 @@ declare class AssetsResource {
175
253
  verify(assetId: string): Promise<VerifyResult>;
176
254
  revoke(assetId: string, reason?: string): Promise<Asset>;
177
255
  batchCreate(params: BatchCreateParams): Promise<BatchCreateResult>;
256
+ batchVerify(assetIds: string[]): Promise<BatchVerifyResult>;
257
+ batchRevoke(assetIds: string[], reason: string): Promise<BatchRevokeResult>;
178
258
  private buildQuery;
179
259
  }
180
260
 
@@ -718,6 +798,7 @@ interface RequestOptions {
718
798
  path: string;
719
799
  body?: unknown;
720
800
  headers?: Record<string, string>;
801
+ /** Per-operation timeout in ms. Overrides the client-level default. */
721
802
  timeout?: number;
722
803
  /** Idempotency key for safe mutation retries. Auto-generated for POST/PUT/PATCH if omitted. */
723
804
  idempotencyKey?: string;
@@ -732,6 +813,7 @@ type RequestFn = <T>(options: RequestOptions) => Promise<T>;
732
813
  *
733
814
  * const client = new OptropicClient({
734
815
  * apiKey: 'optr_live_xxxxxxxxxxxx',
816
+ * debug: true, // logs every request/response
735
817
  * });
736
818
  *
737
819
  * // Create an asset
@@ -740,7 +822,7 @@ type RequestFn = <T>(options: RequestOptions) => Promise<T>;
740
822
  * serial: 'SN001',
741
823
  * });
742
824
  *
743
- * // Verify an asset
825
+ * // Verify an asset (with per-operation timeout)
744
826
  * const result = await client.assets.verify(asset.id);
745
827
  * ```
746
828
  */
@@ -749,6 +831,8 @@ declare class OptropicClient {
749
831
  private readonly baseUrl;
750
832
  private readonly retryConfig;
751
833
  private readonly _sandbox;
834
+ private readonly _debug;
835
+ private _rateLimit;
752
836
  readonly assets: AssetsResource;
753
837
  readonly audit: AuditResource;
754
838
  readonly compliance: ComplianceResource;
@@ -764,6 +848,12 @@ declare class OptropicClient {
764
848
  get isLive(): boolean;
765
849
  /** Returns 'sandbox' or 'live'. */
766
850
  get environment(): 'sandbox' | 'live';
851
+ /** Last known rate limit status, updated after every API call. Returns null until the first request. */
852
+ get rateLimit(): RateLimitInfo | null;
853
+ private redact;
854
+ private logRequest;
855
+ private logResponse;
856
+ private logRetry;
767
857
  private isValidApiKey;
768
858
  private request;
769
859
  private executeRequest;
@@ -781,6 +871,7 @@ declare class OptropicClient {
781
871
  *
782
872
  * const client = createClient({
783
873
  * apiKey: process.env.OPTROPIC_API_KEY!,
874
+ * debug: true,
784
875
  * });
785
876
  * ```
786
877
  */
@@ -1310,6 +1401,6 @@ declare function createErrorFromResponse(statusCode: number, body: {
1310
1401
  requestId?: string;
1311
1402
  }): OptropicError;
1312
1403
 
1313
- declare const SDK_VERSION = "2.2.0";
1404
+ declare const SDK_VERSION = "2.4.0";
1314
1405
 
1315
- 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 };
1406
+ export { type ApiKey, type Asset, AssetsResource, type AuditEvent, AuditResource, AuthenticationError, type BatchCreateParams, type BatchCreateResult, BatchNotFoundError, type BatchRevokeResult, type BatchVerifyResult, 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, Permission, type PermissionValue, type ProvenanceChain, type ProvenanceEvent, type ProvenanceEventType, type ProvenanceLocation, ProvenanceResource, QuotaExceededError, type RateLimitInfo, 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
@@ -445,6 +445,20 @@ var AssetsResource = class {
445
445
  async batchCreate(params) {
446
446
  return this.request({ method: "POST", path: "/v1/assets/batch", body: params });
447
447
  }
448
+ async batchVerify(assetIds) {
449
+ return this.request({
450
+ method: "POST",
451
+ path: "/v1/assets/batch-verify",
452
+ body: { asset_ids: assetIds }
453
+ });
454
+ }
455
+ async batchRevoke(assetIds, reason) {
456
+ return this.request({
457
+ method: "POST",
458
+ path: "/v1/assets/batch-revoke",
459
+ body: { asset_ids: assetIds, reason }
460
+ });
461
+ }
448
462
  buildQuery(params) {
449
463
  const entries = Object.entries(params).filter(([, v]) => v !== void 0);
450
464
  if (entries.length === 0) return "";
@@ -906,18 +920,21 @@ var SchemasResource = class {
906
920
  // src/client.ts
907
921
  var DEFAULT_BASE_URL = "https://api.optropic.com";
908
922
  var DEFAULT_TIMEOUT = 3e4;
909
- var SDK_VERSION = "2.2.0";
923
+ var SDK_VERSION = "2.4.0";
910
924
  var SANDBOX_PREFIXES = ["optr_test_"];
911
925
  var DEFAULT_RETRY_CONFIG = {
912
926
  maxRetries: 3,
913
927
  baseDelay: 1e3,
914
928
  maxDelay: 1e4
915
929
  };
930
+ var KEY_REDACT_RE = /(optr_(?:live|test)_)[a-zA-Z0-9_-]+/g;
916
931
  var OptropicClient = class {
917
932
  config;
918
933
  baseUrl;
919
934
  retryConfig;
920
935
  _sandbox;
936
+ _debug;
937
+ _rateLimit = null;
921
938
  assets;
922
939
  audit;
923
940
  compliance;
@@ -936,6 +953,7 @@ var OptropicClient = class {
936
953
  ...config,
937
954
  timeout: config.timeout ?? DEFAULT_TIMEOUT
938
955
  };
956
+ this._debug = config.debug ?? false;
939
957
  if (config.sandbox !== void 0) {
940
958
  this._sandbox = config.sandbox;
941
959
  } else {
@@ -975,6 +993,35 @@ var OptropicClient = class {
975
993
  get environment() {
976
994
  return this._sandbox ? "sandbox" : "live";
977
995
  }
996
+ /** Last known rate limit status, updated after every API call. Returns null until the first request. */
997
+ get rateLimit() {
998
+ return this._rateLimit;
999
+ }
1000
+ // ─────────────────────────────────────────────────────────────────────────
1001
+ // DEBUG LOGGING
1002
+ // ─────────────────────────────────────────────────────────────────────────
1003
+ redact(text) {
1004
+ return text.replace(KEY_REDACT_RE, "$1****");
1005
+ }
1006
+ logRequest(method, url, requestId, idempotencyKey) {
1007
+ if (!this._debug) return;
1008
+ const parts = [`${method} ${this.redact(url)}`, `req=${requestId}`];
1009
+ if (idempotencyKey) parts.push(`idempotency=${idempotencyKey}`);
1010
+ console.log(`[optropic] ${parts.join(" ")}`);
1011
+ }
1012
+ logResponse(method, path, status, durationMs, requestId, serverRequestId, attempt) {
1013
+ if (!this._debug) return;
1014
+ const parts = [`${method} ${path} \u2192 ${status} (${Math.round(durationMs)}ms)`, `req=${requestId}`];
1015
+ if (serverRequestId) parts.push(`server_req=${serverRequestId}`);
1016
+ if (attempt > 0) parts.push(`attempt=${attempt + 1}`);
1017
+ console.log(`[optropic] ${parts.join(" ")}`);
1018
+ }
1019
+ logRetry(method, path, attempt, delay, reason) {
1020
+ if (!this._debug) return;
1021
+ console.log(
1022
+ `[optropic] ${method} ${path} retry #${attempt + 1} in ${(delay / 1e3).toFixed(1)}s (${reason})`
1023
+ );
1024
+ }
978
1025
  // ─────────────────────────────────────────────────────────────────────────
979
1026
  // PRIVATE METHODS
980
1027
  // ─────────────────────────────────────────────────────────────────────────
@@ -984,20 +1031,26 @@ var OptropicClient = class {
984
1031
  async request(options) {
985
1032
  const { method, path, body, headers = {}, timeout = this.config.timeout, idempotencyKey } = options;
986
1033
  const url = `${this.baseUrl}${path}`;
1034
+ const requestId = crypto.randomUUID();
987
1035
  const requestHeaders = {
988
1036
  "Content-Type": "application/json",
989
1037
  "Accept": "application/json",
990
1038
  "x-api-key": this.config.apiKey,
991
1039
  "X-SDK-Version": SDK_VERSION,
992
1040
  "X-SDK-Language": "typescript",
1041
+ "X-Request-ID": requestId,
993
1042
  ...this.config.headers,
994
1043
  ...headers
995
1044
  };
1045
+ let effectiveIdempotencyKey;
996
1046
  if (idempotencyKey) {
997
1047
  requestHeaders["Idempotency-Key"] = idempotencyKey;
1048
+ effectiveIdempotencyKey = idempotencyKey;
998
1049
  } else if (["POST", "PUT", "PATCH"].includes(method)) {
999
- requestHeaders["Idempotency-Key"] = crypto.randomUUID();
1050
+ effectiveIdempotencyKey = crypto.randomUUID();
1051
+ requestHeaders["Idempotency-Key"] = effectiveIdempotencyKey;
1000
1052
  }
1053
+ this.logRequest(method, url, requestId, effectiveIdempotencyKey);
1001
1054
  let lastError = null;
1002
1055
  let attempt = 0;
1003
1056
  while (attempt <= this.retryConfig.maxRetries) {
@@ -1007,7 +1060,10 @@ var OptropicClient = class {
1007
1060
  method,
1008
1061
  requestHeaders,
1009
1062
  body,
1010
- timeout
1063
+ timeout,
1064
+ requestId,
1065
+ path,
1066
+ attempt
1011
1067
  );
1012
1068
  return response;
1013
1069
  } catch (error) {
@@ -1025,8 +1081,12 @@ var OptropicClient = class {
1025
1081
  const jitter = baseDelay * 0.5 * Math.random();
1026
1082
  const delay = baseDelay + jitter;
1027
1083
  if (error instanceof RateLimitedError) {
1028
- await this.sleep(error.retryAfter * 1e3);
1084
+ const retryDelay = error.retryAfter * 1e3;
1085
+ this.logRetry(method, path, attempt, retryDelay, "rate_limited");
1086
+ await this.sleep(retryDelay);
1029
1087
  } else {
1088
+ const statusCode = error instanceof OptropicError ? error.statusCode : 0;
1089
+ this.logRetry(method, path, attempt, delay, `status=${statusCode}`);
1030
1090
  await this.sleep(delay);
1031
1091
  }
1032
1092
  attempt++;
@@ -1034,9 +1094,10 @@ var OptropicClient = class {
1034
1094
  }
1035
1095
  throw lastError ?? new OptropicError("UNKNOWN_ERROR", "Request failed");
1036
1096
  }
1037
- async executeRequest(url, method, headers, body, timeout) {
1097
+ async executeRequest(url, method, headers, body, timeout, requestId, path, attempt) {
1038
1098
  const controller = new AbortController();
1039
1099
  const timeoutId = setTimeout(() => controller.abort(), timeout);
1100
+ const t0 = performance.now();
1040
1101
  try {
1041
1102
  const response = await fetch(url, {
1042
1103
  method,
@@ -1045,7 +1106,18 @@ var OptropicClient = class {
1045
1106
  signal: controller.signal
1046
1107
  });
1047
1108
  clearTimeout(timeoutId);
1048
- const requestId = response.headers.get("x-request-id") ?? "";
1109
+ const durationMs = performance.now() - t0;
1110
+ const serverRequestId = response.headers.get("x-request-id") ?? "";
1111
+ this.logResponse(method, path, response.status, durationMs, requestId, serverRequestId, attempt);
1112
+ const rlLimit = response.headers.get("x-ratelimit-limit");
1113
+ const rlRemaining = response.headers.get("x-ratelimit-remaining");
1114
+ if (rlLimit !== null || rlRemaining !== null) {
1115
+ this._rateLimit = {
1116
+ limit: rlLimit ? parseInt(rlLimit, 10) : 0,
1117
+ remaining: rlRemaining ? parseInt(rlRemaining, 10) : 0,
1118
+ reset: response.headers.get("x-ratelimit-reset") ?? void 0
1119
+ };
1120
+ }
1049
1121
  if (!response.ok) {
1050
1122
  let errorBody;
1051
1123
  try {
@@ -1068,7 +1140,7 @@ var OptropicClient = class {
1068
1140
  code: errorBody.code,
1069
1141
  message: errorBody.message,
1070
1142
  details: errorBody.details,
1071
- requestId
1143
+ requestId: serverRequestId || requestId
1072
1144
  });
1073
1145
  }
1074
1146
  if (response.status === 204) {
@@ -1094,8 +1166,12 @@ var OptropicClient = class {
1094
1166
  }
1095
1167
  if (error instanceof Error) {
1096
1168
  if (error.name === "AbortError") {
1169
+ const durationMs2 = performance.now() - t0;
1170
+ this.logResponse(method, path, 408, durationMs2, requestId, "", attempt);
1097
1171
  throw new TimeoutError(timeout);
1098
1172
  }
1173
+ const durationMs = performance.now() - t0;
1174
+ this.logResponse(method, path, 0, durationMs, requestId, "", attempt);
1099
1175
  throw new NetworkError(error.message, { cause: error });
1100
1176
  }
1101
1177
  throw new OptropicError("UNKNOWN_ERROR", "An unexpected error occurred", {
@@ -1111,6 +1187,39 @@ function createClient(config) {
1111
1187
  return new OptropicClient(config);
1112
1188
  }
1113
1189
 
1190
+ // src/types.ts
1191
+ var Permission = {
1192
+ ASSETS_READ: "assets:read",
1193
+ ASSETS_WRITE: "assets:write",
1194
+ ASSETS_VERIFY: "assets:verify",
1195
+ AUDIT_READ: "audit:read",
1196
+ COMPLIANCE_READ: "compliance:read",
1197
+ KEYS_MANAGE: "keys:manage",
1198
+ SCHEMAS_MANAGE: "schemas:manage",
1199
+ DOCUMENTS_ENROLL: "documents:enroll",
1200
+ DOCUMENTS_VERIFY: "documents:verify",
1201
+ PROVENANCE_READ: "provenance:read",
1202
+ PROVENANCE_WRITE: "provenance:write",
1203
+ WEBHOOKS_MANAGE: "webhooks:manage",
1204
+ /** All read permissions. */
1205
+ ALL_READ: ["assets:read", "audit:read", "compliance:read", "provenance:read"],
1206
+ /** All write permissions. */
1207
+ ALL_WRITE: [
1208
+ "assets:write",
1209
+ "assets:verify",
1210
+ "documents:enroll",
1211
+ "documents:verify",
1212
+ "provenance:write",
1213
+ "webhooks:manage",
1214
+ "keys:manage",
1215
+ "schemas:manage"
1216
+ ],
1217
+ /** All permissions combined. */
1218
+ all() {
1219
+ return [...this.ALL_READ, ...this.ALL_WRITE];
1220
+ }
1221
+ };
1222
+
1114
1223
  // src/filter-verify.ts
1115
1224
  var HEADER_SIZE = 19;
1116
1225
  var SIGNATURE_SIZE = 64;
@@ -1354,7 +1463,7 @@ async function verifyWebhookSignature(options) {
1354
1463
  }
1355
1464
 
1356
1465
  // src/index.ts
1357
- var SDK_VERSION2 = "2.2.0";
1466
+ var SDK_VERSION2 = "2.4.0";
1358
1467
  export {
1359
1468
  AssetsResource,
1360
1469
  AuditResource,
@@ -1371,6 +1480,7 @@ export {
1371
1480
  NetworkError,
1372
1481
  OptropicClient,
1373
1482
  OptropicError,
1483
+ Permission,
1374
1484
  ProvenanceResource,
1375
1485
  QuotaExceededError,
1376
1486
  RateLimitedError,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "optropic",
3
- "version": "2.2.0",
3
+ "version": "2.4.0",
4
4
  "description": "Official Optropic SDK for TypeScript and JavaScript",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",