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 +119 -8
- package/dist/index.d.cts +94 -3
- package/dist/index.d.ts +94 -3
- package/dist/index.js +118 -8
- package/package.json +1 -1
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.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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.
|
|
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,
|