optropic 1.0.0 → 2.1.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 +379 -13
- package/dist/index.d.cts +411 -41
- package/dist/index.d.ts +411 -41
- package/dist/index.js +363 -12
- package/package.json +5 -2
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) {
|
|
@@ -415,6 +427,117 @@ var AssetsResource = class {
|
|
|
415
427
|
}
|
|
416
428
|
};
|
|
417
429
|
|
|
430
|
+
// src/resources/audit.ts
|
|
431
|
+
var AuditResource = class {
|
|
432
|
+
constructor(request) {
|
|
433
|
+
this.request = request;
|
|
434
|
+
}
|
|
435
|
+
/**
|
|
436
|
+
* List audit events with optional filtering and pagination.
|
|
437
|
+
*/
|
|
438
|
+
async list(params) {
|
|
439
|
+
const query = params ? this.buildQuery(params) : "";
|
|
440
|
+
return this.request({ method: "GET", path: `/v1/audit${query}` });
|
|
441
|
+
}
|
|
442
|
+
/**
|
|
443
|
+
* Retrieve a single audit event by ID.
|
|
444
|
+
*/
|
|
445
|
+
async get(eventId) {
|
|
446
|
+
return this.request({
|
|
447
|
+
method: "GET",
|
|
448
|
+
path: `/v1/audit/${encodeURIComponent(eventId)}`
|
|
449
|
+
});
|
|
450
|
+
}
|
|
451
|
+
/**
|
|
452
|
+
* Record a custom audit event.
|
|
453
|
+
*/
|
|
454
|
+
async create(params) {
|
|
455
|
+
return this.request({
|
|
456
|
+
method: "POST",
|
|
457
|
+
path: "/v1/audit",
|
|
458
|
+
body: {
|
|
459
|
+
event_type: params.eventType,
|
|
460
|
+
...params.resourceId !== void 0 && { resource_id: params.resourceId },
|
|
461
|
+
...params.resourceType !== void 0 && { resource_type: params.resourceType },
|
|
462
|
+
...params.details !== void 0 && { details: params.details }
|
|
463
|
+
}
|
|
464
|
+
});
|
|
465
|
+
}
|
|
466
|
+
buildQuery(params) {
|
|
467
|
+
const entries = Object.entries(params).filter(([, v]) => v !== void 0);
|
|
468
|
+
if (entries.length === 0) return "";
|
|
469
|
+
return "?" + entries.map(([k, v]) => `${k}=${encodeURIComponent(String(v))}`).join("&");
|
|
470
|
+
}
|
|
471
|
+
};
|
|
472
|
+
|
|
473
|
+
// src/resources/compliance.ts
|
|
474
|
+
var ComplianceResource = class {
|
|
475
|
+
constructor(request) {
|
|
476
|
+
this.request = request;
|
|
477
|
+
}
|
|
478
|
+
/**
|
|
479
|
+
* Verify the integrity of the full audit chain.
|
|
480
|
+
*/
|
|
481
|
+
async verifyChain() {
|
|
482
|
+
return this.request({
|
|
483
|
+
method: "POST",
|
|
484
|
+
path: "/v1/compliance/verify-chain"
|
|
485
|
+
});
|
|
486
|
+
}
|
|
487
|
+
/**
|
|
488
|
+
* Return all Merkle roots.
|
|
489
|
+
*/
|
|
490
|
+
async listMerkleRoots() {
|
|
491
|
+
return this.request({
|
|
492
|
+
method: "GET",
|
|
493
|
+
path: "/v1/compliance/merkle-roots"
|
|
494
|
+
});
|
|
495
|
+
}
|
|
496
|
+
/**
|
|
497
|
+
* Return a Merkle inclusion proof for a specific audit event.
|
|
498
|
+
*/
|
|
499
|
+
async getMerkleProof(eventId) {
|
|
500
|
+
return this.request({
|
|
501
|
+
method: "GET",
|
|
502
|
+
path: `/v1/compliance/merkle-proof/${encodeURIComponent(eventId)}`
|
|
503
|
+
});
|
|
504
|
+
}
|
|
505
|
+
/**
|
|
506
|
+
* Export audit data as a signed CSV.
|
|
507
|
+
*/
|
|
508
|
+
async exportAudit(params) {
|
|
509
|
+
const query = params ? this.buildQuery(params) : "";
|
|
510
|
+
return this.request({
|
|
511
|
+
method: "GET",
|
|
512
|
+
path: `/v1/compliance/export${query}`
|
|
513
|
+
});
|
|
514
|
+
}
|
|
515
|
+
/**
|
|
516
|
+
* Retrieve the current compliance configuration.
|
|
517
|
+
*/
|
|
518
|
+
async getConfig() {
|
|
519
|
+
return this.request({
|
|
520
|
+
method: "GET",
|
|
521
|
+
path: "/v1/compliance/config"
|
|
522
|
+
});
|
|
523
|
+
}
|
|
524
|
+
/**
|
|
525
|
+
* Update the compliance mode.
|
|
526
|
+
*/
|
|
527
|
+
async updateConfig(mode) {
|
|
528
|
+
return this.request({
|
|
529
|
+
method: "POST",
|
|
530
|
+
path: "/v1/compliance/config",
|
|
531
|
+
body: { compliance_mode: mode }
|
|
532
|
+
});
|
|
533
|
+
}
|
|
534
|
+
buildQuery(params) {
|
|
535
|
+
const entries = Object.entries(params).filter(([, v]) => v !== void 0);
|
|
536
|
+
if (entries.length === 0) return "";
|
|
537
|
+
return "?" + entries.map(([k, v]) => `${k}=${encodeURIComponent(String(v))}`).join("&");
|
|
538
|
+
}
|
|
539
|
+
};
|
|
540
|
+
|
|
418
541
|
// src/resources/keys.ts
|
|
419
542
|
var KeysResource = class {
|
|
420
543
|
constructor(request) {
|
|
@@ -424,17 +547,162 @@ var KeysResource = class {
|
|
|
424
547
|
return this.request({ method: "POST", path: "/v1/keys", body: params });
|
|
425
548
|
}
|
|
426
549
|
async list() {
|
|
427
|
-
|
|
550
|
+
const result = await this.request({ method: "GET", path: "/v1/keys" });
|
|
551
|
+
return result.data;
|
|
428
552
|
}
|
|
429
553
|
async revoke(keyId) {
|
|
430
554
|
await this.request({ method: "DELETE", path: `/v1/keys/${encodeURIComponent(keyId)}` });
|
|
431
555
|
}
|
|
432
556
|
};
|
|
433
557
|
|
|
558
|
+
// src/resources/keysets.ts
|
|
559
|
+
var KeysetsResource = class {
|
|
560
|
+
constructor(request) {
|
|
561
|
+
this.request = request;
|
|
562
|
+
}
|
|
563
|
+
async create(params) {
|
|
564
|
+
return this.request({ method: "POST", path: "/v1/keysets", body: params });
|
|
565
|
+
}
|
|
566
|
+
async list(params) {
|
|
567
|
+
const query = params ? this.buildQuery(params) : "";
|
|
568
|
+
return this.request({ method: "GET", path: `/v1/keysets${query}` });
|
|
569
|
+
}
|
|
570
|
+
buildQuery(params) {
|
|
571
|
+
const entries = Object.entries(params).filter(([, v]) => v !== void 0);
|
|
572
|
+
if (entries.length === 0) return "";
|
|
573
|
+
return "?" + entries.map(([k, v]) => `${k}=${encodeURIComponent(String(v))}`).join("&");
|
|
574
|
+
}
|
|
575
|
+
};
|
|
576
|
+
|
|
577
|
+
// src/resources/schemas.ts
|
|
578
|
+
function checkType(value, expected) {
|
|
579
|
+
switch (expected) {
|
|
580
|
+
case "string":
|
|
581
|
+
return typeof value === "string";
|
|
582
|
+
case "number":
|
|
583
|
+
return typeof value === "number" && !Number.isNaN(value);
|
|
584
|
+
case "boolean":
|
|
585
|
+
return typeof value === "boolean";
|
|
586
|
+
case "date":
|
|
587
|
+
return typeof value === "string";
|
|
588
|
+
// ISO 8601 string
|
|
589
|
+
case "array":
|
|
590
|
+
return Array.isArray(value);
|
|
591
|
+
default:
|
|
592
|
+
return true;
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
var SchemasResource = class {
|
|
596
|
+
constructor(request) {
|
|
597
|
+
this.request = request;
|
|
598
|
+
}
|
|
599
|
+
/**
|
|
600
|
+
* Register or update a vertical config schema.
|
|
601
|
+
* If a schema already exists for the verticalId, it will be updated.
|
|
602
|
+
*/
|
|
603
|
+
async create(params) {
|
|
604
|
+
const body = this.stripUndefined({
|
|
605
|
+
vertical_id: params.verticalId,
|
|
606
|
+
metadata_schema: params.metadataSchema,
|
|
607
|
+
version: params.version,
|
|
608
|
+
export_formats: params.exportFormats,
|
|
609
|
+
description: params.description
|
|
610
|
+
});
|
|
611
|
+
return this.request({ method: "POST", path: "/v1/schemas", body });
|
|
612
|
+
}
|
|
613
|
+
/**
|
|
614
|
+
* List registered vertical schemas with pagination.
|
|
615
|
+
*/
|
|
616
|
+
async list(params) {
|
|
617
|
+
const query = params ? this.buildQuery(params) : "";
|
|
618
|
+
return this.request({ method: "GET", path: `/v1/schemas${query}` });
|
|
619
|
+
}
|
|
620
|
+
/**
|
|
621
|
+
* Get the active schema for a specific vertical.
|
|
622
|
+
*/
|
|
623
|
+
async get(verticalId) {
|
|
624
|
+
return this.request({
|
|
625
|
+
method: "GET",
|
|
626
|
+
path: `/v1/schemas/${encodeURIComponent(verticalId)}`
|
|
627
|
+
});
|
|
628
|
+
}
|
|
629
|
+
/**
|
|
630
|
+
* Update an existing vertical schema.
|
|
631
|
+
*/
|
|
632
|
+
async update(verticalId, params) {
|
|
633
|
+
const body = this.stripUndefined({
|
|
634
|
+
version: params.version,
|
|
635
|
+
metadata_schema: params.metadataSchema,
|
|
636
|
+
export_formats: params.exportFormats,
|
|
637
|
+
description: params.description,
|
|
638
|
+
is_active: params.isActive
|
|
639
|
+
});
|
|
640
|
+
return this.request({
|
|
641
|
+
method: "PUT",
|
|
642
|
+
path: `/v1/schemas/${encodeURIComponent(verticalId)}`,
|
|
643
|
+
body
|
|
644
|
+
});
|
|
645
|
+
}
|
|
646
|
+
/**
|
|
647
|
+
* Deactivate a vertical schema (soft delete).
|
|
648
|
+
*/
|
|
649
|
+
async delete(verticalId) {
|
|
650
|
+
await this.request({
|
|
651
|
+
method: "DELETE",
|
|
652
|
+
path: `/v1/schemas/${encodeURIComponent(verticalId)}`
|
|
653
|
+
});
|
|
654
|
+
}
|
|
655
|
+
/**
|
|
656
|
+
* Pre-flight validation: check if assetConfig matches the registered schema.
|
|
657
|
+
*
|
|
658
|
+
* This is a client-side convenience that fetches the schema and validates locally.
|
|
659
|
+
* The server also validates on asset creation.
|
|
660
|
+
*/
|
|
661
|
+
async validate(verticalId, assetConfig) {
|
|
662
|
+
let schema;
|
|
663
|
+
try {
|
|
664
|
+
schema = await this.get(verticalId);
|
|
665
|
+
} catch {
|
|
666
|
+
return { valid: true, errors: [] };
|
|
667
|
+
}
|
|
668
|
+
const errors = [];
|
|
669
|
+
const metadataSchema = schema.metadataSchema ?? {};
|
|
670
|
+
for (const [fieldName, fieldDef] of Object.entries(metadataSchema)) {
|
|
671
|
+
if (typeof fieldDef !== "object" || fieldDef === null) continue;
|
|
672
|
+
const def = fieldDef;
|
|
673
|
+
const value = assetConfig[fieldName];
|
|
674
|
+
if (def.required && (value === void 0 || value === null || value === "")) {
|
|
675
|
+
const label = def.label ?? fieldName;
|
|
676
|
+
errors.push({ field: fieldName, message: `Required field "${label}" is missing` });
|
|
677
|
+
continue;
|
|
678
|
+
}
|
|
679
|
+
if (value === void 0 || value === null) continue;
|
|
680
|
+
const expectedType = def.type ?? "string";
|
|
681
|
+
if (!checkType(value, expectedType)) {
|
|
682
|
+
errors.push({
|
|
683
|
+
field: fieldName,
|
|
684
|
+
message: `"${def.label ?? fieldName}" must be a ${expectedType}`,
|
|
685
|
+
received: typeof value
|
|
686
|
+
});
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
return { valid: errors.length === 0, errors };
|
|
690
|
+
}
|
|
691
|
+
buildQuery(params) {
|
|
692
|
+
const entries = Object.entries(params).filter(([, v]) => v !== void 0);
|
|
693
|
+
if (entries.length === 0) return "";
|
|
694
|
+
return "?" + entries.map(([k, v]) => `${k}=${encodeURIComponent(String(v))}`).join("&");
|
|
695
|
+
}
|
|
696
|
+
stripUndefined(obj) {
|
|
697
|
+
return Object.fromEntries(Object.entries(obj).filter(([, v]) => v !== void 0));
|
|
698
|
+
}
|
|
699
|
+
};
|
|
700
|
+
|
|
434
701
|
// src/client.ts
|
|
435
702
|
var DEFAULT_BASE_URL = "https://api.optropic.com";
|
|
436
703
|
var DEFAULT_TIMEOUT = 3e4;
|
|
437
|
-
var SDK_VERSION = "
|
|
704
|
+
var SDK_VERSION = "2.0.0";
|
|
705
|
+
var SANDBOX_PREFIXES = ["optr_test_"];
|
|
438
706
|
var DEFAULT_RETRY_CONFIG = {
|
|
439
707
|
maxRetries: 3,
|
|
440
708
|
baseDelay: 1e3,
|
|
@@ -444,8 +712,13 @@ var OptropicClient = class {
|
|
|
444
712
|
config;
|
|
445
713
|
baseUrl;
|
|
446
714
|
retryConfig;
|
|
715
|
+
_sandbox;
|
|
447
716
|
assets;
|
|
717
|
+
audit;
|
|
718
|
+
compliance;
|
|
448
719
|
keys;
|
|
720
|
+
keysets;
|
|
721
|
+
schemas;
|
|
449
722
|
constructor(config) {
|
|
450
723
|
if (!config.apiKey || !this.isValidApiKey(config.apiKey)) {
|
|
451
724
|
throw new AuthenticationError(
|
|
@@ -456,6 +729,11 @@ var OptropicClient = class {
|
|
|
456
729
|
...config,
|
|
457
730
|
timeout: config.timeout ?? DEFAULT_TIMEOUT
|
|
458
731
|
};
|
|
732
|
+
if (config.sandbox !== void 0) {
|
|
733
|
+
this._sandbox = config.sandbox;
|
|
734
|
+
} else {
|
|
735
|
+
this._sandbox = SANDBOX_PREFIXES.some((p) => config.apiKey.startsWith(p));
|
|
736
|
+
}
|
|
459
737
|
if (config.baseUrl) {
|
|
460
738
|
this.baseUrl = config.baseUrl.replace(/\/$/, "");
|
|
461
739
|
} else {
|
|
@@ -466,8 +744,27 @@ var OptropicClient = class {
|
|
|
466
744
|
...config.retry
|
|
467
745
|
};
|
|
468
746
|
const boundRequest = this.request.bind(this);
|
|
469
|
-
this.assets = new AssetsResource(boundRequest);
|
|
747
|
+
this.assets = new AssetsResource(boundRequest, this);
|
|
748
|
+
this.audit = new AuditResource(boundRequest);
|
|
749
|
+
this.compliance = new ComplianceResource(boundRequest);
|
|
470
750
|
this.keys = new KeysResource(boundRequest);
|
|
751
|
+
this.keysets = new KeysetsResource(boundRequest);
|
|
752
|
+
this.schemas = new SchemasResource(boundRequest);
|
|
753
|
+
}
|
|
754
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
755
|
+
// ENVIRONMENT DETECTION
|
|
756
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
757
|
+
/** True when the client is in sandbox mode (test API key or explicit override). */
|
|
758
|
+
get isSandbox() {
|
|
759
|
+
return this._sandbox;
|
|
760
|
+
}
|
|
761
|
+
/** True when the client is in live/production mode. */
|
|
762
|
+
get isLive() {
|
|
763
|
+
return !this._sandbox;
|
|
764
|
+
}
|
|
765
|
+
/** Returns 'sandbox' or 'live'. */
|
|
766
|
+
get environment() {
|
|
767
|
+
return this._sandbox ? "sandbox" : "live";
|
|
471
768
|
}
|
|
472
769
|
// ─────────────────────────────────────────────────────────────────────────
|
|
473
770
|
// PRIVATE METHODS
|
|
@@ -558,18 +855,22 @@ var OptropicClient = class {
|
|
|
558
855
|
requestId
|
|
559
856
|
});
|
|
560
857
|
}
|
|
858
|
+
if (response.status === 204) {
|
|
859
|
+
return void 0;
|
|
860
|
+
}
|
|
561
861
|
const json = await response.json();
|
|
562
|
-
if (json.error) {
|
|
862
|
+
if (json && typeof json === "object" && "error" in json && json.error) {
|
|
863
|
+
const err = json.error;
|
|
563
864
|
throw createErrorFromResponse(response.status, {
|
|
564
865
|
// Justified: Error code string from API may not match SDK's ErrorCode enum exactly
|
|
565
866
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
566
|
-
code:
|
|
567
|
-
message:
|
|
568
|
-
details:
|
|
867
|
+
code: err.code ?? "UNKNOWN_ERROR",
|
|
868
|
+
message: err.message ?? "Unknown error",
|
|
869
|
+
details: err.details,
|
|
569
870
|
requestId: json.requestId
|
|
570
871
|
});
|
|
571
872
|
}
|
|
572
|
-
return json
|
|
873
|
+
return json;
|
|
573
874
|
} catch (error) {
|
|
574
875
|
clearTimeout(timeoutId);
|
|
575
876
|
if (error instanceof OptropicError) {
|
|
@@ -594,17 +895,65 @@ function createClient(config) {
|
|
|
594
895
|
return new OptropicClient(config);
|
|
595
896
|
}
|
|
596
897
|
|
|
898
|
+
// src/webhooks.ts
|
|
899
|
+
async function computeHmacSha256(secret, message) {
|
|
900
|
+
const encoder = new TextEncoder();
|
|
901
|
+
if (typeof globalThis.crypto?.subtle !== "undefined") {
|
|
902
|
+
const key = await globalThis.crypto.subtle.importKey(
|
|
903
|
+
"raw",
|
|
904
|
+
encoder.encode(secret),
|
|
905
|
+
// OPSEC: Web Crypto API requires this exact algorithm identifier
|
|
906
|
+
{ name: "HMAC", hash: "SHA-256" },
|
|
907
|
+
false,
|
|
908
|
+
["sign"]
|
|
909
|
+
);
|
|
910
|
+
const sig = await globalThis.crypto.subtle.sign("HMAC", key, encoder.encode(message));
|
|
911
|
+
return Array.from(new Uint8Array(sig)).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
912
|
+
}
|
|
913
|
+
const { createHmac } = await import("crypto");
|
|
914
|
+
return createHmac("sha256", secret).update(message).digest("hex");
|
|
915
|
+
}
|
|
916
|
+
function timingSafeEqual(a, b) {
|
|
917
|
+
if (a.length !== b.length) return false;
|
|
918
|
+
let result = 0;
|
|
919
|
+
for (let i = 0; i < a.length; i++) {
|
|
920
|
+
result |= a.charCodeAt(i) ^ b.charCodeAt(i);
|
|
921
|
+
}
|
|
922
|
+
return result === 0;
|
|
923
|
+
}
|
|
924
|
+
async function verifyWebhookSignature(options) {
|
|
925
|
+
const { payload, signature, timestamp, secret, tolerance = 300 } = options;
|
|
926
|
+
const ts = parseInt(timestamp, 10);
|
|
927
|
+
if (isNaN(ts)) {
|
|
928
|
+
return { valid: false, reason: "Invalid timestamp" };
|
|
929
|
+
}
|
|
930
|
+
const age = Math.abs(Math.floor(Date.now() / 1e3) - ts);
|
|
931
|
+
if (age > tolerance) {
|
|
932
|
+
return { valid: false, reason: `Timestamp too old (${age}s > ${tolerance}s tolerance)` };
|
|
933
|
+
}
|
|
934
|
+
const signedPayload = `${timestamp}.${payload}`;
|
|
935
|
+
const expectedHex = await computeHmacSha256(secret, signedPayload);
|
|
936
|
+
const expected = `sha256=${expectedHex}`;
|
|
937
|
+
if (!timingSafeEqual(expected, signature)) {
|
|
938
|
+
return { valid: false, reason: "Signature mismatch" };
|
|
939
|
+
}
|
|
940
|
+
return { valid: true };
|
|
941
|
+
}
|
|
942
|
+
|
|
597
943
|
// src/index.ts
|
|
598
|
-
var SDK_VERSION2 = "
|
|
944
|
+
var SDK_VERSION2 = "2.0.0";
|
|
599
945
|
export {
|
|
600
946
|
AssetsResource,
|
|
947
|
+
AuditResource,
|
|
601
948
|
AuthenticationError,
|
|
602
949
|
BatchNotFoundError,
|
|
603
950
|
CodeNotFoundError,
|
|
951
|
+
ComplianceResource,
|
|
604
952
|
InvalidCodeError,
|
|
605
953
|
InvalidGTINError,
|
|
606
954
|
InvalidSerialError,
|
|
607
955
|
KeysResource,
|
|
956
|
+
KeysetsResource,
|
|
608
957
|
NetworkError,
|
|
609
958
|
OptropicClient,
|
|
610
959
|
OptropicError,
|
|
@@ -612,7 +961,9 @@ export {
|
|
|
612
961
|
RateLimitedError,
|
|
613
962
|
RevokedCodeError,
|
|
614
963
|
SDK_VERSION2 as SDK_VERSION,
|
|
964
|
+
SchemasResource,
|
|
615
965
|
ServiceUnavailableError,
|
|
616
966
|
TimeoutError,
|
|
617
|
-
createClient
|
|
967
|
+
createClient,
|
|
968
|
+
verifyWebhookSignature
|
|
618
969
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "optropic",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "2.1.0",
|
|
4
4
|
"description": "Official Optropic SDK for TypeScript and JavaScript",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -32,7 +32,10 @@
|
|
|
32
32
|
"gtin",
|
|
33
33
|
"serialization"
|
|
34
34
|
],
|
|
35
|
-
"author":
|
|
35
|
+
"author": {
|
|
36
|
+
"name": "Virtrex GmbH",
|
|
37
|
+
"url": "https://optropic.com"
|
|
38
|
+
},
|
|
36
39
|
"license": "MIT",
|
|
37
40
|
"repository": {
|
|
38
41
|
"type": "git",
|