optropic 1.0.0 → 2.0.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 +132 -13
- package/dist/index.d.cts +180 -41
- package/dist/index.d.ts +180 -41
- package/dist/index.js +119 -12
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
2
3
|
var __defProp = Object.defineProperty;
|
|
3
4
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
5
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
8
|
var __export = (target, all) => {
|
|
7
9
|
for (var name in all)
|
|
@@ -15,6 +17,14 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
15
17
|
}
|
|
16
18
|
return to;
|
|
17
19
|
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
18
28
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
29
|
|
|
20
30
|
// src/index.ts
|
|
@@ -28,6 +38,7 @@ __export(index_exports, {
|
|
|
28
38
|
InvalidGTINError: () => InvalidGTINError,
|
|
29
39
|
InvalidSerialError: () => InvalidSerialError,
|
|
30
40
|
KeysResource: () => KeysResource,
|
|
41
|
+
KeysetsResource: () => KeysetsResource,
|
|
31
42
|
NetworkError: () => NetworkError,
|
|
32
43
|
OptropicClient: () => OptropicClient,
|
|
33
44
|
OptropicError: () => OptropicError,
|
|
@@ -37,7 +48,8 @@ __export(index_exports, {
|
|
|
37
48
|
SDK_VERSION: () => SDK_VERSION2,
|
|
38
49
|
ServiceUnavailableError: () => ServiceUnavailableError,
|
|
39
50
|
TimeoutError: () => TimeoutError,
|
|
40
|
-
createClient: () => createClient
|
|
51
|
+
createClient: () => createClient,
|
|
52
|
+
verifyWebhookSignature: () => verifyWebhookSignature
|
|
41
53
|
});
|
|
42
54
|
module.exports = __toCommonJS(index_exports);
|
|
43
55
|
|
|
@@ -425,14 +437,26 @@ function createErrorFromResponse(statusCode, body) {
|
|
|
425
437
|
|
|
426
438
|
// src/resources/assets.ts
|
|
427
439
|
var AssetsResource = class {
|
|
428
|
-
constructor(request) {
|
|
440
|
+
constructor(request, client) {
|
|
429
441
|
this.request = request;
|
|
442
|
+
this.client = client;
|
|
430
443
|
}
|
|
431
444
|
async create(params) {
|
|
432
445
|
return this.request({ method: "POST", path: "/v1/assets", body: params });
|
|
433
446
|
}
|
|
447
|
+
/**
|
|
448
|
+
* List assets with optional filtering and pagination.
|
|
449
|
+
*
|
|
450
|
+
* When the client uses a sandbox/test API key, `is_sandbox` is
|
|
451
|
+
* automatically set to `true` so sandbox clients only see sandbox
|
|
452
|
+
* assets. Pass an explicit `is_sandbox` value to override.
|
|
453
|
+
*/
|
|
434
454
|
async list(params) {
|
|
435
|
-
|
|
455
|
+
let effectiveParams = params;
|
|
456
|
+
if (this.client.isSandbox && (!params || params.is_sandbox === void 0)) {
|
|
457
|
+
effectiveParams = { ...params, is_sandbox: true };
|
|
458
|
+
}
|
|
459
|
+
const query = effectiveParams ? this.buildQuery(effectiveParams) : "";
|
|
436
460
|
return this.request({ method: "GET", path: `/v1/assets${query}` });
|
|
437
461
|
}
|
|
438
462
|
async get(assetId) {
|
|
@@ -467,17 +491,38 @@ var KeysResource = class {
|
|
|
467
491
|
return this.request({ method: "POST", path: "/v1/keys", body: params });
|
|
468
492
|
}
|
|
469
493
|
async list() {
|
|
470
|
-
|
|
494
|
+
const result = await this.request({ method: "GET", path: "/v1/keys" });
|
|
495
|
+
return result.data;
|
|
471
496
|
}
|
|
472
497
|
async revoke(keyId) {
|
|
473
498
|
await this.request({ method: "DELETE", path: `/v1/keys/${encodeURIComponent(keyId)}` });
|
|
474
499
|
}
|
|
475
500
|
};
|
|
476
501
|
|
|
502
|
+
// src/resources/keysets.ts
|
|
503
|
+
var KeysetsResource = class {
|
|
504
|
+
constructor(request) {
|
|
505
|
+
this.request = request;
|
|
506
|
+
}
|
|
507
|
+
async create(params) {
|
|
508
|
+
return this.request({ method: "POST", path: "/v1/keysets", body: params });
|
|
509
|
+
}
|
|
510
|
+
async list(params) {
|
|
511
|
+
const query = params ? this.buildQuery(params) : "";
|
|
512
|
+
return this.request({ method: "GET", path: `/v1/keysets${query}` });
|
|
513
|
+
}
|
|
514
|
+
buildQuery(params) {
|
|
515
|
+
const entries = Object.entries(params).filter(([, v]) => v !== void 0);
|
|
516
|
+
if (entries.length === 0) return "";
|
|
517
|
+
return "?" + entries.map(([k, v]) => `${k}=${encodeURIComponent(String(v))}`).join("&");
|
|
518
|
+
}
|
|
519
|
+
};
|
|
520
|
+
|
|
477
521
|
// src/client.ts
|
|
478
522
|
var DEFAULT_BASE_URL = "https://api.optropic.com";
|
|
479
523
|
var DEFAULT_TIMEOUT = 3e4;
|
|
480
|
-
var SDK_VERSION = "
|
|
524
|
+
var SDK_VERSION = "2.0.0";
|
|
525
|
+
var SANDBOX_PREFIXES = ["optr_test_"];
|
|
481
526
|
var DEFAULT_RETRY_CONFIG = {
|
|
482
527
|
maxRetries: 3,
|
|
483
528
|
baseDelay: 1e3,
|
|
@@ -487,8 +532,10 @@ var OptropicClient = class {
|
|
|
487
532
|
config;
|
|
488
533
|
baseUrl;
|
|
489
534
|
retryConfig;
|
|
535
|
+
_sandbox;
|
|
490
536
|
assets;
|
|
491
537
|
keys;
|
|
538
|
+
keysets;
|
|
492
539
|
constructor(config) {
|
|
493
540
|
if (!config.apiKey || !this.isValidApiKey(config.apiKey)) {
|
|
494
541
|
throw new AuthenticationError(
|
|
@@ -499,6 +546,11 @@ var OptropicClient = class {
|
|
|
499
546
|
...config,
|
|
500
547
|
timeout: config.timeout ?? DEFAULT_TIMEOUT
|
|
501
548
|
};
|
|
549
|
+
if (config.sandbox !== void 0) {
|
|
550
|
+
this._sandbox = config.sandbox;
|
|
551
|
+
} else {
|
|
552
|
+
this._sandbox = SANDBOX_PREFIXES.some((p) => config.apiKey.startsWith(p));
|
|
553
|
+
}
|
|
502
554
|
if (config.baseUrl) {
|
|
503
555
|
this.baseUrl = config.baseUrl.replace(/\/$/, "");
|
|
504
556
|
} else {
|
|
@@ -509,8 +561,24 @@ var OptropicClient = class {
|
|
|
509
561
|
...config.retry
|
|
510
562
|
};
|
|
511
563
|
const boundRequest = this.request.bind(this);
|
|
512
|
-
this.assets = new AssetsResource(boundRequest);
|
|
564
|
+
this.assets = new AssetsResource(boundRequest, this);
|
|
513
565
|
this.keys = new KeysResource(boundRequest);
|
|
566
|
+
this.keysets = new KeysetsResource(boundRequest);
|
|
567
|
+
}
|
|
568
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
569
|
+
// ENVIRONMENT DETECTION
|
|
570
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
571
|
+
/** True when the client is in sandbox mode (test API key or explicit override). */
|
|
572
|
+
get isSandbox() {
|
|
573
|
+
return this._sandbox;
|
|
574
|
+
}
|
|
575
|
+
/** True when the client is in live/production mode. */
|
|
576
|
+
get isLive() {
|
|
577
|
+
return !this._sandbox;
|
|
578
|
+
}
|
|
579
|
+
/** Returns 'sandbox' or 'live'. */
|
|
580
|
+
get environment() {
|
|
581
|
+
return this._sandbox ? "sandbox" : "live";
|
|
514
582
|
}
|
|
515
583
|
// ─────────────────────────────────────────────────────────────────────────
|
|
516
584
|
// PRIVATE METHODS
|
|
@@ -601,18 +669,22 @@ var OptropicClient = class {
|
|
|
601
669
|
requestId
|
|
602
670
|
});
|
|
603
671
|
}
|
|
672
|
+
if (response.status === 204) {
|
|
673
|
+
return void 0;
|
|
674
|
+
}
|
|
604
675
|
const json = await response.json();
|
|
605
|
-
if (json.error) {
|
|
676
|
+
if (json && typeof json === "object" && "error" in json && json.error) {
|
|
677
|
+
const err = json.error;
|
|
606
678
|
throw createErrorFromResponse(response.status, {
|
|
607
679
|
// Justified: Error code string from API may not match SDK's ErrorCode enum exactly
|
|
608
680
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
609
|
-
code:
|
|
610
|
-
message:
|
|
611
|
-
details:
|
|
681
|
+
code: err.code ?? "UNKNOWN_ERROR",
|
|
682
|
+
message: err.message ?? "Unknown error",
|
|
683
|
+
details: err.details,
|
|
612
684
|
requestId: json.requestId
|
|
613
685
|
});
|
|
614
686
|
}
|
|
615
|
-
return json
|
|
687
|
+
return json;
|
|
616
688
|
} catch (error) {
|
|
617
689
|
clearTimeout(timeoutId);
|
|
618
690
|
if (error instanceof OptropicError) {
|
|
@@ -637,8 +709,53 @@ function createClient(config) {
|
|
|
637
709
|
return new OptropicClient(config);
|
|
638
710
|
}
|
|
639
711
|
|
|
712
|
+
// src/webhooks.ts
|
|
713
|
+
async function computeHmacSha256(secret, message) {
|
|
714
|
+
const encoder = new TextEncoder();
|
|
715
|
+
if (typeof globalThis.crypto?.subtle !== "undefined") {
|
|
716
|
+
const key = await globalThis.crypto.subtle.importKey(
|
|
717
|
+
"raw",
|
|
718
|
+
encoder.encode(secret),
|
|
719
|
+
// OPSEC: Web Crypto API requires this exact algorithm identifier
|
|
720
|
+
{ name: "HMAC", hash: "SHA-256" },
|
|
721
|
+
false,
|
|
722
|
+
["sign"]
|
|
723
|
+
);
|
|
724
|
+
const sig = await globalThis.crypto.subtle.sign("HMAC", key, encoder.encode(message));
|
|
725
|
+
return Array.from(new Uint8Array(sig)).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
726
|
+
}
|
|
727
|
+
const { createHmac } = await import("crypto");
|
|
728
|
+
return createHmac("sha256", secret).update(message).digest("hex");
|
|
729
|
+
}
|
|
730
|
+
function timingSafeEqual(a, b) {
|
|
731
|
+
if (a.length !== b.length) return false;
|
|
732
|
+
let result = 0;
|
|
733
|
+
for (let i = 0; i < a.length; i++) {
|
|
734
|
+
result |= a.charCodeAt(i) ^ b.charCodeAt(i);
|
|
735
|
+
}
|
|
736
|
+
return result === 0;
|
|
737
|
+
}
|
|
738
|
+
async function verifyWebhookSignature(options) {
|
|
739
|
+
const { payload, signature, timestamp, secret, tolerance = 300 } = options;
|
|
740
|
+
const ts = parseInt(timestamp, 10);
|
|
741
|
+
if (isNaN(ts)) {
|
|
742
|
+
return { valid: false, reason: "Invalid timestamp" };
|
|
743
|
+
}
|
|
744
|
+
const age = Math.abs(Math.floor(Date.now() / 1e3) - ts);
|
|
745
|
+
if (age > tolerance) {
|
|
746
|
+
return { valid: false, reason: `Timestamp too old (${age}s > ${tolerance}s tolerance)` };
|
|
747
|
+
}
|
|
748
|
+
const signedPayload = `${timestamp}.${payload}`;
|
|
749
|
+
const expectedHex = await computeHmacSha256(secret, signedPayload);
|
|
750
|
+
const expected = `sha256=${expectedHex}`;
|
|
751
|
+
if (!timingSafeEqual(expected, signature)) {
|
|
752
|
+
return { valid: false, reason: "Signature mismatch" };
|
|
753
|
+
}
|
|
754
|
+
return { valid: true };
|
|
755
|
+
}
|
|
756
|
+
|
|
640
757
|
// src/index.ts
|
|
641
|
-
var SDK_VERSION2 = "
|
|
758
|
+
var SDK_VERSION2 = "2.0.0";
|
|
642
759
|
// Annotate the CommonJS export names for ESM import in node:
|
|
643
760
|
0 && (module.exports = {
|
|
644
761
|
AssetsResource,
|
|
@@ -649,6 +766,7 @@ var SDK_VERSION2 = "1.0.0";
|
|
|
649
766
|
InvalidGTINError,
|
|
650
767
|
InvalidSerialError,
|
|
651
768
|
KeysResource,
|
|
769
|
+
KeysetsResource,
|
|
652
770
|
NetworkError,
|
|
653
771
|
OptropicClient,
|
|
654
772
|
OptropicError,
|
|
@@ -658,5 +776,6 @@ var SDK_VERSION2 = "1.0.0";
|
|
|
658
776
|
SDK_VERSION,
|
|
659
777
|
ServiceUnavailableError,
|
|
660
778
|
TimeoutError,
|
|
661
|
-
createClient
|
|
779
|
+
createClient,
|
|
780
|
+
verifyWebhookSignature
|
|
662
781
|
});
|
package/dist/index.d.cts
CHANGED
|
@@ -21,6 +21,11 @@ interface OptropicConfig {
|
|
|
21
21
|
* Format: optr_live_xxx (production) or optr_test_xxx (test)
|
|
22
22
|
*/
|
|
23
23
|
readonly apiKey: string;
|
|
24
|
+
/**
|
|
25
|
+
* Explicitly set sandbox mode. When omitted, auto-detected from
|
|
26
|
+
* the API key prefix: `optr_test_*` → sandbox, `optr_live_*` → live.
|
|
27
|
+
*/
|
|
28
|
+
readonly sandbox?: boolean;
|
|
24
29
|
/**
|
|
25
30
|
* Base URL override (for self-hosted deployments).
|
|
26
31
|
* If not provided, uses Optropic's managed infrastructure.
|
|
@@ -67,60 +72,93 @@ interface RetryConfig {
|
|
|
67
72
|
*/
|
|
68
73
|
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';
|
|
69
74
|
|
|
75
|
+
/**
|
|
76
|
+
* Assets Resource
|
|
77
|
+
*
|
|
78
|
+
* CRUD operations for digitally signed assets.
|
|
79
|
+
*/
|
|
80
|
+
|
|
70
81
|
interface Asset {
|
|
71
82
|
readonly id: string;
|
|
72
|
-
readonly
|
|
73
|
-
readonly
|
|
74
|
-
readonly
|
|
83
|
+
readonly shortId: string;
|
|
84
|
+
readonly tenantId: string;
|
|
85
|
+
readonly keysetId: string;
|
|
86
|
+
readonly externalId: string | null;
|
|
87
|
+
readonly vertical: string;
|
|
88
|
+
readonly securityLevel: 'signed' | 'provenance';
|
|
89
|
+
readonly assetConfig: Record<string, unknown>;
|
|
90
|
+
readonly signatureHex: string;
|
|
75
91
|
readonly status: 'active' | 'revoked';
|
|
76
|
-
readonly
|
|
77
|
-
readonly
|
|
78
|
-
readonly
|
|
79
|
-
readonly
|
|
92
|
+
readonly isSandbox: boolean;
|
|
93
|
+
readonly verificationCount: number;
|
|
94
|
+
readonly metadata: Record<string, unknown> | null;
|
|
95
|
+
readonly createdAt: string;
|
|
96
|
+
readonly updatedAt: string;
|
|
80
97
|
}
|
|
81
98
|
interface CreateAssetParams {
|
|
82
|
-
readonly
|
|
83
|
-
readonly
|
|
84
|
-
readonly
|
|
99
|
+
readonly keysetId: string;
|
|
100
|
+
readonly externalId?: string;
|
|
101
|
+
readonly vertical?: string;
|
|
102
|
+
readonly securityLevel?: 'signed' | 'provenance';
|
|
103
|
+
readonly assetConfig?: Record<string, unknown>;
|
|
104
|
+
readonly metadata?: Record<string, unknown>;
|
|
85
105
|
}
|
|
86
106
|
interface ListAssetsParams {
|
|
87
|
-
readonly
|
|
88
|
-
readonly
|
|
107
|
+
readonly page?: number;
|
|
108
|
+
readonly per_page?: number;
|
|
89
109
|
readonly status?: 'active' | 'revoked';
|
|
90
|
-
readonly
|
|
110
|
+
readonly vertical?: string;
|
|
111
|
+
/** Filter by sandbox status. Auto-set when using a test API key. */
|
|
112
|
+
readonly is_sandbox?: boolean;
|
|
113
|
+
}
|
|
114
|
+
interface ListAssetsResponse {
|
|
115
|
+
readonly data: Asset[];
|
|
116
|
+
readonly pagination: {
|
|
117
|
+
readonly total: number;
|
|
118
|
+
readonly page: number;
|
|
119
|
+
readonly perPage: number;
|
|
120
|
+
readonly totalPages: number;
|
|
121
|
+
};
|
|
91
122
|
}
|
|
92
123
|
interface VerifyResult {
|
|
93
124
|
readonly id: string;
|
|
94
125
|
readonly status: string;
|
|
95
|
-
readonly
|
|
96
|
-
readonly
|
|
97
|
-
readonly
|
|
98
|
-
readonly
|
|
99
|
-
readonly
|
|
100
|
-
readonly
|
|
101
|
-
readonly
|
|
102
|
-
readonly evidence?: VerificationEvidence;
|
|
103
|
-
}
|
|
104
|
-
interface VerificationEvidence {
|
|
105
|
-
readonly signature_valid: boolean;
|
|
106
|
-
readonly revocation_status: string;
|
|
107
|
-
readonly verification_mode: string;
|
|
108
|
-
readonly provenance_valid?: boolean;
|
|
109
|
-
readonly security_level?: string;
|
|
110
|
-
readonly verification_count?: number;
|
|
126
|
+
readonly securityLevel: string;
|
|
127
|
+
readonly signatureValid: boolean;
|
|
128
|
+
readonly provenanceValid?: boolean;
|
|
129
|
+
readonly revocationStatus: 'active' | 'revoked';
|
|
130
|
+
readonly verificationMode: 'online' | 'offline';
|
|
131
|
+
readonly verificationCount: number;
|
|
132
|
+
readonly lastVerifiedAt: string;
|
|
111
133
|
}
|
|
112
134
|
interface BatchCreateParams {
|
|
113
|
-
readonly
|
|
135
|
+
readonly keysetId: string;
|
|
136
|
+
readonly assets: Array<{
|
|
137
|
+
readonly externalId?: string;
|
|
138
|
+
readonly vertical?: string;
|
|
139
|
+
readonly securityLevel?: 'signed' | 'provenance';
|
|
140
|
+
readonly assetConfig?: Record<string, unknown>;
|
|
141
|
+
readonly metadata?: Record<string, unknown>;
|
|
142
|
+
}>;
|
|
114
143
|
}
|
|
115
144
|
interface BatchCreateResult {
|
|
116
145
|
readonly created: number;
|
|
117
|
-
readonly
|
|
146
|
+
readonly requested: number;
|
|
147
|
+
readonly assets: Asset[];
|
|
118
148
|
}
|
|
119
149
|
declare class AssetsResource {
|
|
120
150
|
private readonly request;
|
|
121
|
-
|
|
151
|
+
private readonly client;
|
|
152
|
+
constructor(request: RequestFn, client: OptropicClient);
|
|
122
153
|
create(params: CreateAssetParams): Promise<Asset>;
|
|
123
|
-
|
|
154
|
+
/**
|
|
155
|
+
* List assets with optional filtering and pagination.
|
|
156
|
+
*
|
|
157
|
+
* When the client uses a sandbox/test API key, `is_sandbox` is
|
|
158
|
+
* automatically set to `true` so sandbox clients only see sandbox
|
|
159
|
+
* assets. Pass an explicit `is_sandbox` value to override.
|
|
160
|
+
*/
|
|
161
|
+
list(params?: ListAssetsParams): Promise<ListAssetsResponse>;
|
|
124
162
|
get(assetId: string): Promise<Asset>;
|
|
125
163
|
verify(assetId: string): Promise<VerifyResult>;
|
|
126
164
|
revoke(assetId: string, reason?: string): Promise<Asset>;
|
|
@@ -128,21 +166,31 @@ declare class AssetsResource {
|
|
|
128
166
|
private buildQuery;
|
|
129
167
|
}
|
|
130
168
|
|
|
169
|
+
/**
|
|
170
|
+
* Keys Resource
|
|
171
|
+
*
|
|
172
|
+
* API key management operations.
|
|
173
|
+
*/
|
|
174
|
+
|
|
131
175
|
interface ApiKey {
|
|
132
176
|
readonly id: string;
|
|
133
|
-
readonly
|
|
134
|
-
readonly
|
|
177
|
+
readonly label: string;
|
|
178
|
+
readonly prefix: string;
|
|
135
179
|
readonly environment: string;
|
|
136
|
-
readonly permissions: string[];
|
|
137
180
|
readonly created_at: string;
|
|
138
181
|
readonly last_used_at?: string;
|
|
139
182
|
}
|
|
140
183
|
interface CreateKeyParams {
|
|
141
|
-
readonly
|
|
142
|
-
readonly environment?: '
|
|
184
|
+
readonly label?: string;
|
|
185
|
+
readonly environment?: 'live' | 'test';
|
|
143
186
|
}
|
|
144
|
-
interface CreateKeyResult
|
|
187
|
+
interface CreateKeyResult {
|
|
145
188
|
readonly key: string;
|
|
189
|
+
readonly prefix: string;
|
|
190
|
+
readonly id: string;
|
|
191
|
+
readonly label: string;
|
|
192
|
+
readonly environment: string;
|
|
193
|
+
readonly created_at: string;
|
|
146
194
|
}
|
|
147
195
|
declare class KeysResource {
|
|
148
196
|
private readonly request;
|
|
@@ -152,6 +200,44 @@ declare class KeysResource {
|
|
|
152
200
|
revoke(keyId: string): Promise<void>;
|
|
153
201
|
}
|
|
154
202
|
|
|
203
|
+
/**
|
|
204
|
+
* Keysets Resource
|
|
205
|
+
*
|
|
206
|
+
* Signing keyset management operations.
|
|
207
|
+
*/
|
|
208
|
+
|
|
209
|
+
interface Keyset {
|
|
210
|
+
readonly id: string;
|
|
211
|
+
readonly name: string;
|
|
212
|
+
readonly publicKey: string;
|
|
213
|
+
readonly tenantId: string;
|
|
214
|
+
readonly isActive: boolean;
|
|
215
|
+
readonly createdAt: string;
|
|
216
|
+
}
|
|
217
|
+
interface CreateKeysetParams {
|
|
218
|
+
readonly name?: string;
|
|
219
|
+
}
|
|
220
|
+
interface ListKeysetsParams {
|
|
221
|
+
readonly page?: number;
|
|
222
|
+
readonly per_page?: number;
|
|
223
|
+
}
|
|
224
|
+
interface ListKeysetsResponse {
|
|
225
|
+
readonly data: Keyset[];
|
|
226
|
+
readonly pagination: {
|
|
227
|
+
readonly total: number;
|
|
228
|
+
readonly page: number;
|
|
229
|
+
readonly perPage: number;
|
|
230
|
+
readonly totalPages: number;
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
declare class KeysetsResource {
|
|
234
|
+
private readonly request;
|
|
235
|
+
constructor(request: RequestFn);
|
|
236
|
+
create(params?: CreateKeysetParams): Promise<Keyset>;
|
|
237
|
+
list(params?: ListKeysetsParams): Promise<ListKeysetsResponse>;
|
|
238
|
+
private buildQuery;
|
|
239
|
+
}
|
|
240
|
+
|
|
155
241
|
/**
|
|
156
242
|
* optropic - OptropicClient
|
|
157
243
|
*
|
|
@@ -193,9 +279,17 @@ declare class OptropicClient {
|
|
|
193
279
|
private readonly config;
|
|
194
280
|
private readonly baseUrl;
|
|
195
281
|
private readonly retryConfig;
|
|
282
|
+
private readonly _sandbox;
|
|
196
283
|
readonly assets: AssetsResource;
|
|
197
284
|
readonly keys: KeysResource;
|
|
285
|
+
readonly keysets: KeysetsResource;
|
|
198
286
|
constructor(config: OptropicConfig);
|
|
287
|
+
/** True when the client is in sandbox mode (test API key or explicit override). */
|
|
288
|
+
get isSandbox(): boolean;
|
|
289
|
+
/** True when the client is in live/production mode. */
|
|
290
|
+
get isLive(): boolean;
|
|
291
|
+
/** Returns 'sandbox' or 'live'. */
|
|
292
|
+
get environment(): 'sandbox' | 'live';
|
|
199
293
|
private isValidApiKey;
|
|
200
294
|
private request;
|
|
201
295
|
private executeRequest;
|
|
@@ -218,6 +312,51 @@ declare class OptropicClient {
|
|
|
218
312
|
*/
|
|
219
313
|
declare function createClient(config: OptropicConfig): OptropicClient;
|
|
220
314
|
|
|
315
|
+
/**
|
|
316
|
+
* Webhook Signature Verification
|
|
317
|
+
*
|
|
318
|
+
* Verifies that incoming webhook payloads were signed by Optropic.
|
|
319
|
+
* Uses the endpoint secret provided when the webhook was registered.
|
|
320
|
+
*
|
|
321
|
+
* @module optropic/webhooks
|
|
322
|
+
*/
|
|
323
|
+
interface WebhookVerifyOptions {
|
|
324
|
+
/** Raw request body (string). Must not be parsed JSON. */
|
|
325
|
+
readonly payload: string;
|
|
326
|
+
/** Value of the `X-Optropic-Signature` header. Format: `sha256=<hex>` */
|
|
327
|
+
readonly signature: string;
|
|
328
|
+
/** Value of the `X-Optropic-Timestamp` header (unix seconds). */
|
|
329
|
+
readonly timestamp: string;
|
|
330
|
+
/** The endpoint secret from webhook registration. */
|
|
331
|
+
readonly secret: string;
|
|
332
|
+
/** Maximum allowed age of the timestamp in seconds. @default 300 (5 minutes) */
|
|
333
|
+
readonly tolerance?: number;
|
|
334
|
+
}
|
|
335
|
+
interface WebhookVerifyResult {
|
|
336
|
+
readonly valid: boolean;
|
|
337
|
+
readonly reason?: string;
|
|
338
|
+
}
|
|
339
|
+
/**
|
|
340
|
+
* Verify a webhook payload signature.
|
|
341
|
+
*
|
|
342
|
+
* @example
|
|
343
|
+
* ```typescript
|
|
344
|
+
* import { verifyWebhookSignature } from 'optropic';
|
|
345
|
+
*
|
|
346
|
+
* const result = await verifyWebhookSignature({
|
|
347
|
+
* payload: req.body, // raw string body
|
|
348
|
+
* signature: req.headers['x-optropic-signature'],
|
|
349
|
+
* timestamp: req.headers['x-optropic-timestamp'],
|
|
350
|
+
* secret: process.env.WEBHOOK_SECRET!,
|
|
351
|
+
* });
|
|
352
|
+
*
|
|
353
|
+
* if (!result.valid) {
|
|
354
|
+
* return res.status(401).json({ error: result.reason });
|
|
355
|
+
* }
|
|
356
|
+
* ```
|
|
357
|
+
*/
|
|
358
|
+
declare function verifyWebhookSignature(options: WebhookVerifyOptions): Promise<WebhookVerifyResult>;
|
|
359
|
+
|
|
221
360
|
/**
|
|
222
361
|
* optropic - Error Classes
|
|
223
362
|
*
|
|
@@ -473,6 +612,6 @@ declare class ServiceUnavailableError extends OptropicError {
|
|
|
473
612
|
});
|
|
474
613
|
}
|
|
475
614
|
|
|
476
|
-
declare const SDK_VERSION = "
|
|
615
|
+
declare const SDK_VERSION = "2.0.0";
|
|
477
616
|
|
|
478
|
-
export { type ApiKey, type Asset, AssetsResource, AuthenticationError, type BatchCreateParams, type BatchCreateResult, BatchNotFoundError, CodeNotFoundError, type CreateAssetParams, type CreateKeyParams, type CreateKeyResult, type ErrorCode, InvalidCodeError, InvalidGTINError, InvalidSerialError, KeysResource, type ListAssetsParams, NetworkError, OptropicClient, type OptropicConfig, OptropicError, QuotaExceededError, RateLimitedError, type RequestFn, type RetryConfig, RevokedCodeError, SDK_VERSION, ServiceUnavailableError, TimeoutError, type
|
|
617
|
+
export { type ApiKey, type Asset, AssetsResource, AuthenticationError, type BatchCreateParams, type BatchCreateResult, BatchNotFoundError, CodeNotFoundError, type CreateAssetParams, type CreateKeyParams, type CreateKeyResult, type CreateKeysetParams, type ErrorCode, InvalidCodeError, InvalidGTINError, InvalidSerialError, KeysResource, type Keyset, KeysetsResource, type ListAssetsParams, type ListAssetsResponse, type ListKeysetsParams, type ListKeysetsResponse, NetworkError, OptropicClient, type OptropicConfig, OptropicError, QuotaExceededError, RateLimitedError, type RequestFn, type RetryConfig, RevokedCodeError, SDK_VERSION, ServiceUnavailableError, TimeoutError, type VerifyResult, type WebhookVerifyOptions, type WebhookVerifyResult, createClient, verifyWebhookSignature };
|
package/dist/index.d.ts
CHANGED
|
@@ -21,6 +21,11 @@ interface OptropicConfig {
|
|
|
21
21
|
* Format: optr_live_xxx (production) or optr_test_xxx (test)
|
|
22
22
|
*/
|
|
23
23
|
readonly apiKey: string;
|
|
24
|
+
/**
|
|
25
|
+
* Explicitly set sandbox mode. When omitted, auto-detected from
|
|
26
|
+
* the API key prefix: `optr_test_*` → sandbox, `optr_live_*` → live.
|
|
27
|
+
*/
|
|
28
|
+
readonly sandbox?: boolean;
|
|
24
29
|
/**
|
|
25
30
|
* Base URL override (for self-hosted deployments).
|
|
26
31
|
* If not provided, uses Optropic's managed infrastructure.
|
|
@@ -67,60 +72,93 @@ interface RetryConfig {
|
|
|
67
72
|
*/
|
|
68
73
|
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';
|
|
69
74
|
|
|
75
|
+
/**
|
|
76
|
+
* Assets Resource
|
|
77
|
+
*
|
|
78
|
+
* CRUD operations for digitally signed assets.
|
|
79
|
+
*/
|
|
80
|
+
|
|
70
81
|
interface Asset {
|
|
71
82
|
readonly id: string;
|
|
72
|
-
readonly
|
|
73
|
-
readonly
|
|
74
|
-
readonly
|
|
83
|
+
readonly shortId: string;
|
|
84
|
+
readonly tenantId: string;
|
|
85
|
+
readonly keysetId: string;
|
|
86
|
+
readonly externalId: string | null;
|
|
87
|
+
readonly vertical: string;
|
|
88
|
+
readonly securityLevel: 'signed' | 'provenance';
|
|
89
|
+
readonly assetConfig: Record<string, unknown>;
|
|
90
|
+
readonly signatureHex: string;
|
|
75
91
|
readonly status: 'active' | 'revoked';
|
|
76
|
-
readonly
|
|
77
|
-
readonly
|
|
78
|
-
readonly
|
|
79
|
-
readonly
|
|
92
|
+
readonly isSandbox: boolean;
|
|
93
|
+
readonly verificationCount: number;
|
|
94
|
+
readonly metadata: Record<string, unknown> | null;
|
|
95
|
+
readonly createdAt: string;
|
|
96
|
+
readonly updatedAt: string;
|
|
80
97
|
}
|
|
81
98
|
interface CreateAssetParams {
|
|
82
|
-
readonly
|
|
83
|
-
readonly
|
|
84
|
-
readonly
|
|
99
|
+
readonly keysetId: string;
|
|
100
|
+
readonly externalId?: string;
|
|
101
|
+
readonly vertical?: string;
|
|
102
|
+
readonly securityLevel?: 'signed' | 'provenance';
|
|
103
|
+
readonly assetConfig?: Record<string, unknown>;
|
|
104
|
+
readonly metadata?: Record<string, unknown>;
|
|
85
105
|
}
|
|
86
106
|
interface ListAssetsParams {
|
|
87
|
-
readonly
|
|
88
|
-
readonly
|
|
107
|
+
readonly page?: number;
|
|
108
|
+
readonly per_page?: number;
|
|
89
109
|
readonly status?: 'active' | 'revoked';
|
|
90
|
-
readonly
|
|
110
|
+
readonly vertical?: string;
|
|
111
|
+
/** Filter by sandbox status. Auto-set when using a test API key. */
|
|
112
|
+
readonly is_sandbox?: boolean;
|
|
113
|
+
}
|
|
114
|
+
interface ListAssetsResponse {
|
|
115
|
+
readonly data: Asset[];
|
|
116
|
+
readonly pagination: {
|
|
117
|
+
readonly total: number;
|
|
118
|
+
readonly page: number;
|
|
119
|
+
readonly perPage: number;
|
|
120
|
+
readonly totalPages: number;
|
|
121
|
+
};
|
|
91
122
|
}
|
|
92
123
|
interface VerifyResult {
|
|
93
124
|
readonly id: string;
|
|
94
125
|
readonly status: string;
|
|
95
|
-
readonly
|
|
96
|
-
readonly
|
|
97
|
-
readonly
|
|
98
|
-
readonly
|
|
99
|
-
readonly
|
|
100
|
-
readonly
|
|
101
|
-
readonly
|
|
102
|
-
readonly evidence?: VerificationEvidence;
|
|
103
|
-
}
|
|
104
|
-
interface VerificationEvidence {
|
|
105
|
-
readonly signature_valid: boolean;
|
|
106
|
-
readonly revocation_status: string;
|
|
107
|
-
readonly verification_mode: string;
|
|
108
|
-
readonly provenance_valid?: boolean;
|
|
109
|
-
readonly security_level?: string;
|
|
110
|
-
readonly verification_count?: number;
|
|
126
|
+
readonly securityLevel: string;
|
|
127
|
+
readonly signatureValid: boolean;
|
|
128
|
+
readonly provenanceValid?: boolean;
|
|
129
|
+
readonly revocationStatus: 'active' | 'revoked';
|
|
130
|
+
readonly verificationMode: 'online' | 'offline';
|
|
131
|
+
readonly verificationCount: number;
|
|
132
|
+
readonly lastVerifiedAt: string;
|
|
111
133
|
}
|
|
112
134
|
interface BatchCreateParams {
|
|
113
|
-
readonly
|
|
135
|
+
readonly keysetId: string;
|
|
136
|
+
readonly assets: Array<{
|
|
137
|
+
readonly externalId?: string;
|
|
138
|
+
readonly vertical?: string;
|
|
139
|
+
readonly securityLevel?: 'signed' | 'provenance';
|
|
140
|
+
readonly assetConfig?: Record<string, unknown>;
|
|
141
|
+
readonly metadata?: Record<string, unknown>;
|
|
142
|
+
}>;
|
|
114
143
|
}
|
|
115
144
|
interface BatchCreateResult {
|
|
116
145
|
readonly created: number;
|
|
117
|
-
readonly
|
|
146
|
+
readonly requested: number;
|
|
147
|
+
readonly assets: Asset[];
|
|
118
148
|
}
|
|
119
149
|
declare class AssetsResource {
|
|
120
150
|
private readonly request;
|
|
121
|
-
|
|
151
|
+
private readonly client;
|
|
152
|
+
constructor(request: RequestFn, client: OptropicClient);
|
|
122
153
|
create(params: CreateAssetParams): Promise<Asset>;
|
|
123
|
-
|
|
154
|
+
/**
|
|
155
|
+
* List assets with optional filtering and pagination.
|
|
156
|
+
*
|
|
157
|
+
* When the client uses a sandbox/test API key, `is_sandbox` is
|
|
158
|
+
* automatically set to `true` so sandbox clients only see sandbox
|
|
159
|
+
* assets. Pass an explicit `is_sandbox` value to override.
|
|
160
|
+
*/
|
|
161
|
+
list(params?: ListAssetsParams): Promise<ListAssetsResponse>;
|
|
124
162
|
get(assetId: string): Promise<Asset>;
|
|
125
163
|
verify(assetId: string): Promise<VerifyResult>;
|
|
126
164
|
revoke(assetId: string, reason?: string): Promise<Asset>;
|
|
@@ -128,21 +166,31 @@ declare class AssetsResource {
|
|
|
128
166
|
private buildQuery;
|
|
129
167
|
}
|
|
130
168
|
|
|
169
|
+
/**
|
|
170
|
+
* Keys Resource
|
|
171
|
+
*
|
|
172
|
+
* API key management operations.
|
|
173
|
+
*/
|
|
174
|
+
|
|
131
175
|
interface ApiKey {
|
|
132
176
|
readonly id: string;
|
|
133
|
-
readonly
|
|
134
|
-
readonly
|
|
177
|
+
readonly label: string;
|
|
178
|
+
readonly prefix: string;
|
|
135
179
|
readonly environment: string;
|
|
136
|
-
readonly permissions: string[];
|
|
137
180
|
readonly created_at: string;
|
|
138
181
|
readonly last_used_at?: string;
|
|
139
182
|
}
|
|
140
183
|
interface CreateKeyParams {
|
|
141
|
-
readonly
|
|
142
|
-
readonly environment?: '
|
|
184
|
+
readonly label?: string;
|
|
185
|
+
readonly environment?: 'live' | 'test';
|
|
143
186
|
}
|
|
144
|
-
interface CreateKeyResult
|
|
187
|
+
interface CreateKeyResult {
|
|
145
188
|
readonly key: string;
|
|
189
|
+
readonly prefix: string;
|
|
190
|
+
readonly id: string;
|
|
191
|
+
readonly label: string;
|
|
192
|
+
readonly environment: string;
|
|
193
|
+
readonly created_at: string;
|
|
146
194
|
}
|
|
147
195
|
declare class KeysResource {
|
|
148
196
|
private readonly request;
|
|
@@ -152,6 +200,44 @@ declare class KeysResource {
|
|
|
152
200
|
revoke(keyId: string): Promise<void>;
|
|
153
201
|
}
|
|
154
202
|
|
|
203
|
+
/**
|
|
204
|
+
* Keysets Resource
|
|
205
|
+
*
|
|
206
|
+
* Signing keyset management operations.
|
|
207
|
+
*/
|
|
208
|
+
|
|
209
|
+
interface Keyset {
|
|
210
|
+
readonly id: string;
|
|
211
|
+
readonly name: string;
|
|
212
|
+
readonly publicKey: string;
|
|
213
|
+
readonly tenantId: string;
|
|
214
|
+
readonly isActive: boolean;
|
|
215
|
+
readonly createdAt: string;
|
|
216
|
+
}
|
|
217
|
+
interface CreateKeysetParams {
|
|
218
|
+
readonly name?: string;
|
|
219
|
+
}
|
|
220
|
+
interface ListKeysetsParams {
|
|
221
|
+
readonly page?: number;
|
|
222
|
+
readonly per_page?: number;
|
|
223
|
+
}
|
|
224
|
+
interface ListKeysetsResponse {
|
|
225
|
+
readonly data: Keyset[];
|
|
226
|
+
readonly pagination: {
|
|
227
|
+
readonly total: number;
|
|
228
|
+
readonly page: number;
|
|
229
|
+
readonly perPage: number;
|
|
230
|
+
readonly totalPages: number;
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
declare class KeysetsResource {
|
|
234
|
+
private readonly request;
|
|
235
|
+
constructor(request: RequestFn);
|
|
236
|
+
create(params?: CreateKeysetParams): Promise<Keyset>;
|
|
237
|
+
list(params?: ListKeysetsParams): Promise<ListKeysetsResponse>;
|
|
238
|
+
private buildQuery;
|
|
239
|
+
}
|
|
240
|
+
|
|
155
241
|
/**
|
|
156
242
|
* optropic - OptropicClient
|
|
157
243
|
*
|
|
@@ -193,9 +279,17 @@ declare class OptropicClient {
|
|
|
193
279
|
private readonly config;
|
|
194
280
|
private readonly baseUrl;
|
|
195
281
|
private readonly retryConfig;
|
|
282
|
+
private readonly _sandbox;
|
|
196
283
|
readonly assets: AssetsResource;
|
|
197
284
|
readonly keys: KeysResource;
|
|
285
|
+
readonly keysets: KeysetsResource;
|
|
198
286
|
constructor(config: OptropicConfig);
|
|
287
|
+
/** True when the client is in sandbox mode (test API key or explicit override). */
|
|
288
|
+
get isSandbox(): boolean;
|
|
289
|
+
/** True when the client is in live/production mode. */
|
|
290
|
+
get isLive(): boolean;
|
|
291
|
+
/** Returns 'sandbox' or 'live'. */
|
|
292
|
+
get environment(): 'sandbox' | 'live';
|
|
199
293
|
private isValidApiKey;
|
|
200
294
|
private request;
|
|
201
295
|
private executeRequest;
|
|
@@ -218,6 +312,51 @@ declare class OptropicClient {
|
|
|
218
312
|
*/
|
|
219
313
|
declare function createClient(config: OptropicConfig): OptropicClient;
|
|
220
314
|
|
|
315
|
+
/**
|
|
316
|
+
* Webhook Signature Verification
|
|
317
|
+
*
|
|
318
|
+
* Verifies that incoming webhook payloads were signed by Optropic.
|
|
319
|
+
* Uses the endpoint secret provided when the webhook was registered.
|
|
320
|
+
*
|
|
321
|
+
* @module optropic/webhooks
|
|
322
|
+
*/
|
|
323
|
+
interface WebhookVerifyOptions {
|
|
324
|
+
/** Raw request body (string). Must not be parsed JSON. */
|
|
325
|
+
readonly payload: string;
|
|
326
|
+
/** Value of the `X-Optropic-Signature` header. Format: `sha256=<hex>` */
|
|
327
|
+
readonly signature: string;
|
|
328
|
+
/** Value of the `X-Optropic-Timestamp` header (unix seconds). */
|
|
329
|
+
readonly timestamp: string;
|
|
330
|
+
/** The endpoint secret from webhook registration. */
|
|
331
|
+
readonly secret: string;
|
|
332
|
+
/** Maximum allowed age of the timestamp in seconds. @default 300 (5 minutes) */
|
|
333
|
+
readonly tolerance?: number;
|
|
334
|
+
}
|
|
335
|
+
interface WebhookVerifyResult {
|
|
336
|
+
readonly valid: boolean;
|
|
337
|
+
readonly reason?: string;
|
|
338
|
+
}
|
|
339
|
+
/**
|
|
340
|
+
* Verify a webhook payload signature.
|
|
341
|
+
*
|
|
342
|
+
* @example
|
|
343
|
+
* ```typescript
|
|
344
|
+
* import { verifyWebhookSignature } from 'optropic';
|
|
345
|
+
*
|
|
346
|
+
* const result = await verifyWebhookSignature({
|
|
347
|
+
* payload: req.body, // raw string body
|
|
348
|
+
* signature: req.headers['x-optropic-signature'],
|
|
349
|
+
* timestamp: req.headers['x-optropic-timestamp'],
|
|
350
|
+
* secret: process.env.WEBHOOK_SECRET!,
|
|
351
|
+
* });
|
|
352
|
+
*
|
|
353
|
+
* if (!result.valid) {
|
|
354
|
+
* return res.status(401).json({ error: result.reason });
|
|
355
|
+
* }
|
|
356
|
+
* ```
|
|
357
|
+
*/
|
|
358
|
+
declare function verifyWebhookSignature(options: WebhookVerifyOptions): Promise<WebhookVerifyResult>;
|
|
359
|
+
|
|
221
360
|
/**
|
|
222
361
|
* optropic - Error Classes
|
|
223
362
|
*
|
|
@@ -473,6 +612,6 @@ declare class ServiceUnavailableError extends OptropicError {
|
|
|
473
612
|
});
|
|
474
613
|
}
|
|
475
614
|
|
|
476
|
-
declare const SDK_VERSION = "
|
|
615
|
+
declare const SDK_VERSION = "2.0.0";
|
|
477
616
|
|
|
478
|
-
export { type ApiKey, type Asset, AssetsResource, AuthenticationError, type BatchCreateParams, type BatchCreateResult, BatchNotFoundError, CodeNotFoundError, type CreateAssetParams, type CreateKeyParams, type CreateKeyResult, type ErrorCode, InvalidCodeError, InvalidGTINError, InvalidSerialError, KeysResource, type ListAssetsParams, NetworkError, OptropicClient, type OptropicConfig, OptropicError, QuotaExceededError, RateLimitedError, type RequestFn, type RetryConfig, RevokedCodeError, SDK_VERSION, ServiceUnavailableError, TimeoutError, type
|
|
617
|
+
export { type ApiKey, type Asset, AssetsResource, AuthenticationError, type BatchCreateParams, type BatchCreateResult, BatchNotFoundError, CodeNotFoundError, type CreateAssetParams, type CreateKeyParams, type CreateKeyResult, type CreateKeysetParams, type ErrorCode, InvalidCodeError, InvalidGTINError, InvalidSerialError, KeysResource, type Keyset, KeysetsResource, type ListAssetsParams, type ListAssetsResponse, type ListKeysetsParams, type ListKeysetsResponse, NetworkError, OptropicClient, type OptropicConfig, OptropicError, QuotaExceededError, RateLimitedError, type RequestFn, type RetryConfig, RevokedCodeError, SDK_VERSION, ServiceUnavailableError, TimeoutError, type VerifyResult, type WebhookVerifyOptions, type WebhookVerifyResult, createClient, verifyWebhookSignature };
|
package/dist/index.js
CHANGED
|
@@ -382,14 +382,26 @@ function createErrorFromResponse(statusCode, body) {
|
|
|
382
382
|
|
|
383
383
|
// src/resources/assets.ts
|
|
384
384
|
var AssetsResource = class {
|
|
385
|
-
constructor(request) {
|
|
385
|
+
constructor(request, client) {
|
|
386
386
|
this.request = request;
|
|
387
|
+
this.client = client;
|
|
387
388
|
}
|
|
388
389
|
async create(params) {
|
|
389
390
|
return this.request({ method: "POST", path: "/v1/assets", body: params });
|
|
390
391
|
}
|
|
392
|
+
/**
|
|
393
|
+
* List assets with optional filtering and pagination.
|
|
394
|
+
*
|
|
395
|
+
* When the client uses a sandbox/test API key, `is_sandbox` is
|
|
396
|
+
* automatically set to `true` so sandbox clients only see sandbox
|
|
397
|
+
* assets. Pass an explicit `is_sandbox` value to override.
|
|
398
|
+
*/
|
|
391
399
|
async list(params) {
|
|
392
|
-
|
|
400
|
+
let effectiveParams = params;
|
|
401
|
+
if (this.client.isSandbox && (!params || params.is_sandbox === void 0)) {
|
|
402
|
+
effectiveParams = { ...params, is_sandbox: true };
|
|
403
|
+
}
|
|
404
|
+
const query = effectiveParams ? this.buildQuery(effectiveParams) : "";
|
|
393
405
|
return this.request({ method: "GET", path: `/v1/assets${query}` });
|
|
394
406
|
}
|
|
395
407
|
async get(assetId) {
|
|
@@ -424,17 +436,38 @@ var KeysResource = class {
|
|
|
424
436
|
return this.request({ method: "POST", path: "/v1/keys", body: params });
|
|
425
437
|
}
|
|
426
438
|
async list() {
|
|
427
|
-
|
|
439
|
+
const result = await this.request({ method: "GET", path: "/v1/keys" });
|
|
440
|
+
return result.data;
|
|
428
441
|
}
|
|
429
442
|
async revoke(keyId) {
|
|
430
443
|
await this.request({ method: "DELETE", path: `/v1/keys/${encodeURIComponent(keyId)}` });
|
|
431
444
|
}
|
|
432
445
|
};
|
|
433
446
|
|
|
447
|
+
// src/resources/keysets.ts
|
|
448
|
+
var KeysetsResource = class {
|
|
449
|
+
constructor(request) {
|
|
450
|
+
this.request = request;
|
|
451
|
+
}
|
|
452
|
+
async create(params) {
|
|
453
|
+
return this.request({ method: "POST", path: "/v1/keysets", body: params });
|
|
454
|
+
}
|
|
455
|
+
async list(params) {
|
|
456
|
+
const query = params ? this.buildQuery(params) : "";
|
|
457
|
+
return this.request({ method: "GET", path: `/v1/keysets${query}` });
|
|
458
|
+
}
|
|
459
|
+
buildQuery(params) {
|
|
460
|
+
const entries = Object.entries(params).filter(([, v]) => v !== void 0);
|
|
461
|
+
if (entries.length === 0) return "";
|
|
462
|
+
return "?" + entries.map(([k, v]) => `${k}=${encodeURIComponent(String(v))}`).join("&");
|
|
463
|
+
}
|
|
464
|
+
};
|
|
465
|
+
|
|
434
466
|
// src/client.ts
|
|
435
467
|
var DEFAULT_BASE_URL = "https://api.optropic.com";
|
|
436
468
|
var DEFAULT_TIMEOUT = 3e4;
|
|
437
|
-
var SDK_VERSION = "
|
|
469
|
+
var SDK_VERSION = "2.0.0";
|
|
470
|
+
var SANDBOX_PREFIXES = ["optr_test_"];
|
|
438
471
|
var DEFAULT_RETRY_CONFIG = {
|
|
439
472
|
maxRetries: 3,
|
|
440
473
|
baseDelay: 1e3,
|
|
@@ -444,8 +477,10 @@ var OptropicClient = class {
|
|
|
444
477
|
config;
|
|
445
478
|
baseUrl;
|
|
446
479
|
retryConfig;
|
|
480
|
+
_sandbox;
|
|
447
481
|
assets;
|
|
448
482
|
keys;
|
|
483
|
+
keysets;
|
|
449
484
|
constructor(config) {
|
|
450
485
|
if (!config.apiKey || !this.isValidApiKey(config.apiKey)) {
|
|
451
486
|
throw new AuthenticationError(
|
|
@@ -456,6 +491,11 @@ var OptropicClient = class {
|
|
|
456
491
|
...config,
|
|
457
492
|
timeout: config.timeout ?? DEFAULT_TIMEOUT
|
|
458
493
|
};
|
|
494
|
+
if (config.sandbox !== void 0) {
|
|
495
|
+
this._sandbox = config.sandbox;
|
|
496
|
+
} else {
|
|
497
|
+
this._sandbox = SANDBOX_PREFIXES.some((p) => config.apiKey.startsWith(p));
|
|
498
|
+
}
|
|
459
499
|
if (config.baseUrl) {
|
|
460
500
|
this.baseUrl = config.baseUrl.replace(/\/$/, "");
|
|
461
501
|
} else {
|
|
@@ -466,8 +506,24 @@ var OptropicClient = class {
|
|
|
466
506
|
...config.retry
|
|
467
507
|
};
|
|
468
508
|
const boundRequest = this.request.bind(this);
|
|
469
|
-
this.assets = new AssetsResource(boundRequest);
|
|
509
|
+
this.assets = new AssetsResource(boundRequest, this);
|
|
470
510
|
this.keys = new KeysResource(boundRequest);
|
|
511
|
+
this.keysets = new KeysetsResource(boundRequest);
|
|
512
|
+
}
|
|
513
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
514
|
+
// ENVIRONMENT DETECTION
|
|
515
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
516
|
+
/** True when the client is in sandbox mode (test API key or explicit override). */
|
|
517
|
+
get isSandbox() {
|
|
518
|
+
return this._sandbox;
|
|
519
|
+
}
|
|
520
|
+
/** True when the client is in live/production mode. */
|
|
521
|
+
get isLive() {
|
|
522
|
+
return !this._sandbox;
|
|
523
|
+
}
|
|
524
|
+
/** Returns 'sandbox' or 'live'. */
|
|
525
|
+
get environment() {
|
|
526
|
+
return this._sandbox ? "sandbox" : "live";
|
|
471
527
|
}
|
|
472
528
|
// ─────────────────────────────────────────────────────────────────────────
|
|
473
529
|
// PRIVATE METHODS
|
|
@@ -558,18 +614,22 @@ var OptropicClient = class {
|
|
|
558
614
|
requestId
|
|
559
615
|
});
|
|
560
616
|
}
|
|
617
|
+
if (response.status === 204) {
|
|
618
|
+
return void 0;
|
|
619
|
+
}
|
|
561
620
|
const json = await response.json();
|
|
562
|
-
if (json.error) {
|
|
621
|
+
if (json && typeof json === "object" && "error" in json && json.error) {
|
|
622
|
+
const err = json.error;
|
|
563
623
|
throw createErrorFromResponse(response.status, {
|
|
564
624
|
// Justified: Error code string from API may not match SDK's ErrorCode enum exactly
|
|
565
625
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
566
|
-
code:
|
|
567
|
-
message:
|
|
568
|
-
details:
|
|
626
|
+
code: err.code ?? "UNKNOWN_ERROR",
|
|
627
|
+
message: err.message ?? "Unknown error",
|
|
628
|
+
details: err.details,
|
|
569
629
|
requestId: json.requestId
|
|
570
630
|
});
|
|
571
631
|
}
|
|
572
|
-
return json
|
|
632
|
+
return json;
|
|
573
633
|
} catch (error) {
|
|
574
634
|
clearTimeout(timeoutId);
|
|
575
635
|
if (error instanceof OptropicError) {
|
|
@@ -594,8 +654,53 @@ function createClient(config) {
|
|
|
594
654
|
return new OptropicClient(config);
|
|
595
655
|
}
|
|
596
656
|
|
|
657
|
+
// src/webhooks.ts
|
|
658
|
+
async function computeHmacSha256(secret, message) {
|
|
659
|
+
const encoder = new TextEncoder();
|
|
660
|
+
if (typeof globalThis.crypto?.subtle !== "undefined") {
|
|
661
|
+
const key = await globalThis.crypto.subtle.importKey(
|
|
662
|
+
"raw",
|
|
663
|
+
encoder.encode(secret),
|
|
664
|
+
// OPSEC: Web Crypto API requires this exact algorithm identifier
|
|
665
|
+
{ name: "HMAC", hash: "SHA-256" },
|
|
666
|
+
false,
|
|
667
|
+
["sign"]
|
|
668
|
+
);
|
|
669
|
+
const sig = await globalThis.crypto.subtle.sign("HMAC", key, encoder.encode(message));
|
|
670
|
+
return Array.from(new Uint8Array(sig)).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
671
|
+
}
|
|
672
|
+
const { createHmac } = await import("crypto");
|
|
673
|
+
return createHmac("sha256", secret).update(message).digest("hex");
|
|
674
|
+
}
|
|
675
|
+
function timingSafeEqual(a, b) {
|
|
676
|
+
if (a.length !== b.length) return false;
|
|
677
|
+
let result = 0;
|
|
678
|
+
for (let i = 0; i < a.length; i++) {
|
|
679
|
+
result |= a.charCodeAt(i) ^ b.charCodeAt(i);
|
|
680
|
+
}
|
|
681
|
+
return result === 0;
|
|
682
|
+
}
|
|
683
|
+
async function verifyWebhookSignature(options) {
|
|
684
|
+
const { payload, signature, timestamp, secret, tolerance = 300 } = options;
|
|
685
|
+
const ts = parseInt(timestamp, 10);
|
|
686
|
+
if (isNaN(ts)) {
|
|
687
|
+
return { valid: false, reason: "Invalid timestamp" };
|
|
688
|
+
}
|
|
689
|
+
const age = Math.abs(Math.floor(Date.now() / 1e3) - ts);
|
|
690
|
+
if (age > tolerance) {
|
|
691
|
+
return { valid: false, reason: `Timestamp too old (${age}s > ${tolerance}s tolerance)` };
|
|
692
|
+
}
|
|
693
|
+
const signedPayload = `${timestamp}.${payload}`;
|
|
694
|
+
const expectedHex = await computeHmacSha256(secret, signedPayload);
|
|
695
|
+
const expected = `sha256=${expectedHex}`;
|
|
696
|
+
if (!timingSafeEqual(expected, signature)) {
|
|
697
|
+
return { valid: false, reason: "Signature mismatch" };
|
|
698
|
+
}
|
|
699
|
+
return { valid: true };
|
|
700
|
+
}
|
|
701
|
+
|
|
597
702
|
// src/index.ts
|
|
598
|
-
var SDK_VERSION2 = "
|
|
703
|
+
var SDK_VERSION2 = "2.0.0";
|
|
599
704
|
export {
|
|
600
705
|
AssetsResource,
|
|
601
706
|
AuthenticationError,
|
|
@@ -605,6 +710,7 @@ export {
|
|
|
605
710
|
InvalidGTINError,
|
|
606
711
|
InvalidSerialError,
|
|
607
712
|
KeysResource,
|
|
713
|
+
KeysetsResource,
|
|
608
714
|
NetworkError,
|
|
609
715
|
OptropicClient,
|
|
610
716
|
OptropicError,
|
|
@@ -614,5 +720,6 @@ export {
|
|
|
614
720
|
SDK_VERSION2 as SDK_VERSION,
|
|
615
721
|
ServiceUnavailableError,
|
|
616
722
|
TimeoutError,
|
|
617
|
-
createClient
|
|
723
|
+
createClient,
|
|
724
|
+
verifyWebhookSignature
|
|
618
725
|
};
|