optropic 2.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 CHANGED
@@ -31,9 +31,11 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
31
31
  var index_exports = {};
32
32
  __export(index_exports, {
33
33
  AssetsResource: () => AssetsResource,
34
+ AuditResource: () => AuditResource,
34
35
  AuthenticationError: () => AuthenticationError,
35
36
  BatchNotFoundError: () => BatchNotFoundError,
36
37
  CodeNotFoundError: () => CodeNotFoundError,
38
+ ComplianceResource: () => ComplianceResource,
37
39
  InvalidCodeError: () => InvalidCodeError,
38
40
  InvalidGTINError: () => InvalidGTINError,
39
41
  InvalidSerialError: () => InvalidSerialError,
@@ -46,6 +48,7 @@ __export(index_exports, {
46
48
  RateLimitedError: () => RateLimitedError,
47
49
  RevokedCodeError: () => RevokedCodeError,
48
50
  SDK_VERSION: () => SDK_VERSION2,
51
+ SchemasResource: () => SchemasResource,
49
52
  ServiceUnavailableError: () => ServiceUnavailableError,
50
53
  TimeoutError: () => TimeoutError,
51
54
  createClient: () => createClient,
@@ -482,6 +485,117 @@ var AssetsResource = class {
482
485
  }
483
486
  };
484
487
 
488
+ // src/resources/audit.ts
489
+ var AuditResource = class {
490
+ constructor(request) {
491
+ this.request = request;
492
+ }
493
+ /**
494
+ * List audit events with optional filtering and pagination.
495
+ */
496
+ async list(params) {
497
+ const query = params ? this.buildQuery(params) : "";
498
+ return this.request({ method: "GET", path: `/v1/audit${query}` });
499
+ }
500
+ /**
501
+ * Retrieve a single audit event by ID.
502
+ */
503
+ async get(eventId) {
504
+ return this.request({
505
+ method: "GET",
506
+ path: `/v1/audit/${encodeURIComponent(eventId)}`
507
+ });
508
+ }
509
+ /**
510
+ * Record a custom audit event.
511
+ */
512
+ async create(params) {
513
+ return this.request({
514
+ method: "POST",
515
+ path: "/v1/audit",
516
+ body: {
517
+ event_type: params.eventType,
518
+ ...params.resourceId !== void 0 && { resource_id: params.resourceId },
519
+ ...params.resourceType !== void 0 && { resource_type: params.resourceType },
520
+ ...params.details !== void 0 && { details: params.details }
521
+ }
522
+ });
523
+ }
524
+ buildQuery(params) {
525
+ const entries = Object.entries(params).filter(([, v]) => v !== void 0);
526
+ if (entries.length === 0) return "";
527
+ return "?" + entries.map(([k, v]) => `${k}=${encodeURIComponent(String(v))}`).join("&");
528
+ }
529
+ };
530
+
531
+ // src/resources/compliance.ts
532
+ var ComplianceResource = class {
533
+ constructor(request) {
534
+ this.request = request;
535
+ }
536
+ /**
537
+ * Verify the integrity of the full audit chain.
538
+ */
539
+ async verifyChain() {
540
+ return this.request({
541
+ method: "POST",
542
+ path: "/v1/compliance/verify-chain"
543
+ });
544
+ }
545
+ /**
546
+ * Return all Merkle roots.
547
+ */
548
+ async listMerkleRoots() {
549
+ return this.request({
550
+ method: "GET",
551
+ path: "/v1/compliance/merkle-roots"
552
+ });
553
+ }
554
+ /**
555
+ * Return a Merkle inclusion proof for a specific audit event.
556
+ */
557
+ async getMerkleProof(eventId) {
558
+ return this.request({
559
+ method: "GET",
560
+ path: `/v1/compliance/merkle-proof/${encodeURIComponent(eventId)}`
561
+ });
562
+ }
563
+ /**
564
+ * Export audit data as a signed CSV.
565
+ */
566
+ async exportAudit(params) {
567
+ const query = params ? this.buildQuery(params) : "";
568
+ return this.request({
569
+ method: "GET",
570
+ path: `/v1/compliance/export${query}`
571
+ });
572
+ }
573
+ /**
574
+ * Retrieve the current compliance configuration.
575
+ */
576
+ async getConfig() {
577
+ return this.request({
578
+ method: "GET",
579
+ path: "/v1/compliance/config"
580
+ });
581
+ }
582
+ /**
583
+ * Update the compliance mode.
584
+ */
585
+ async updateConfig(mode) {
586
+ return this.request({
587
+ method: "POST",
588
+ path: "/v1/compliance/config",
589
+ body: { compliance_mode: mode }
590
+ });
591
+ }
592
+ buildQuery(params) {
593
+ const entries = Object.entries(params).filter(([, v]) => v !== void 0);
594
+ if (entries.length === 0) return "";
595
+ return "?" + entries.map(([k, v]) => `${k}=${encodeURIComponent(String(v))}`).join("&");
596
+ }
597
+ };
598
+
485
599
  // src/resources/keys.ts
486
600
  var KeysResource = class {
487
601
  constructor(request) {
@@ -518,6 +632,130 @@ var KeysetsResource = class {
518
632
  }
519
633
  };
520
634
 
635
+ // src/resources/schemas.ts
636
+ function checkType(value, expected) {
637
+ switch (expected) {
638
+ case "string":
639
+ return typeof value === "string";
640
+ case "number":
641
+ return typeof value === "number" && !Number.isNaN(value);
642
+ case "boolean":
643
+ return typeof value === "boolean";
644
+ case "date":
645
+ return typeof value === "string";
646
+ // ISO 8601 string
647
+ case "array":
648
+ return Array.isArray(value);
649
+ default:
650
+ return true;
651
+ }
652
+ }
653
+ var SchemasResource = class {
654
+ constructor(request) {
655
+ this.request = request;
656
+ }
657
+ /**
658
+ * Register or update a vertical config schema.
659
+ * If a schema already exists for the verticalId, it will be updated.
660
+ */
661
+ async create(params) {
662
+ const body = this.stripUndefined({
663
+ vertical_id: params.verticalId,
664
+ metadata_schema: params.metadataSchema,
665
+ version: params.version,
666
+ export_formats: params.exportFormats,
667
+ description: params.description
668
+ });
669
+ return this.request({ method: "POST", path: "/v1/schemas", body });
670
+ }
671
+ /**
672
+ * List registered vertical schemas with pagination.
673
+ */
674
+ async list(params) {
675
+ const query = params ? this.buildQuery(params) : "";
676
+ return this.request({ method: "GET", path: `/v1/schemas${query}` });
677
+ }
678
+ /**
679
+ * Get the active schema for a specific vertical.
680
+ */
681
+ async get(verticalId) {
682
+ return this.request({
683
+ method: "GET",
684
+ path: `/v1/schemas/${encodeURIComponent(verticalId)}`
685
+ });
686
+ }
687
+ /**
688
+ * Update an existing vertical schema.
689
+ */
690
+ async update(verticalId, params) {
691
+ const body = this.stripUndefined({
692
+ version: params.version,
693
+ metadata_schema: params.metadataSchema,
694
+ export_formats: params.exportFormats,
695
+ description: params.description,
696
+ is_active: params.isActive
697
+ });
698
+ return this.request({
699
+ method: "PUT",
700
+ path: `/v1/schemas/${encodeURIComponent(verticalId)}`,
701
+ body
702
+ });
703
+ }
704
+ /**
705
+ * Deactivate a vertical schema (soft delete).
706
+ */
707
+ async delete(verticalId) {
708
+ await this.request({
709
+ method: "DELETE",
710
+ path: `/v1/schemas/${encodeURIComponent(verticalId)}`
711
+ });
712
+ }
713
+ /**
714
+ * Pre-flight validation: check if assetConfig matches the registered schema.
715
+ *
716
+ * This is a client-side convenience that fetches the schema and validates locally.
717
+ * The server also validates on asset creation.
718
+ */
719
+ async validate(verticalId, assetConfig) {
720
+ let schema;
721
+ try {
722
+ schema = await this.get(verticalId);
723
+ } catch {
724
+ return { valid: true, errors: [] };
725
+ }
726
+ const errors = [];
727
+ const metadataSchema = schema.metadataSchema ?? {};
728
+ for (const [fieldName, fieldDef] of Object.entries(metadataSchema)) {
729
+ if (typeof fieldDef !== "object" || fieldDef === null) continue;
730
+ const def = fieldDef;
731
+ const value = assetConfig[fieldName];
732
+ if (def.required && (value === void 0 || value === null || value === "")) {
733
+ const label = def.label ?? fieldName;
734
+ errors.push({ field: fieldName, message: `Required field "${label}" is missing` });
735
+ continue;
736
+ }
737
+ if (value === void 0 || value === null) continue;
738
+ const expectedType = def.type ?? "string";
739
+ if (!checkType(value, expectedType)) {
740
+ errors.push({
741
+ field: fieldName,
742
+ message: `"${def.label ?? fieldName}" must be a ${expectedType}`,
743
+ received: typeof value
744
+ });
745
+ }
746
+ }
747
+ return { valid: errors.length === 0, errors };
748
+ }
749
+ buildQuery(params) {
750
+ const entries = Object.entries(params).filter(([, v]) => v !== void 0);
751
+ if (entries.length === 0) return "";
752
+ return "?" + entries.map(([k, v]) => `${k}=${encodeURIComponent(String(v))}`).join("&");
753
+ }
754
+ stripUndefined(obj) {
755
+ return Object.fromEntries(Object.entries(obj).filter(([, v]) => v !== void 0));
756
+ }
757
+ };
758
+
521
759
  // src/client.ts
522
760
  var DEFAULT_BASE_URL = "https://api.optropic.com";
523
761
  var DEFAULT_TIMEOUT = 3e4;
@@ -534,8 +772,11 @@ var OptropicClient = class {
534
772
  retryConfig;
535
773
  _sandbox;
536
774
  assets;
775
+ audit;
776
+ compliance;
537
777
  keys;
538
778
  keysets;
779
+ schemas;
539
780
  constructor(config) {
540
781
  if (!config.apiKey || !this.isValidApiKey(config.apiKey)) {
541
782
  throw new AuthenticationError(
@@ -562,8 +803,11 @@ var OptropicClient = class {
562
803
  };
563
804
  const boundRequest = this.request.bind(this);
564
805
  this.assets = new AssetsResource(boundRequest, this);
806
+ this.audit = new AuditResource(boundRequest);
807
+ this.compliance = new ComplianceResource(boundRequest);
565
808
  this.keys = new KeysResource(boundRequest);
566
809
  this.keysets = new KeysetsResource(boundRequest);
810
+ this.schemas = new SchemasResource(boundRequest);
567
811
  }
568
812
  // ─────────────────────────────────────────────────────────────────────────
569
813
  // ENVIRONMENT DETECTION
@@ -759,9 +1003,11 @@ var SDK_VERSION2 = "2.0.0";
759
1003
  // Annotate the CommonJS export names for ESM import in node:
760
1004
  0 && (module.exports = {
761
1005
  AssetsResource,
1006
+ AuditResource,
762
1007
  AuthenticationError,
763
1008
  BatchNotFoundError,
764
1009
  CodeNotFoundError,
1010
+ ComplianceResource,
765
1011
  InvalidCodeError,
766
1012
  InvalidGTINError,
767
1013
  InvalidSerialError,
@@ -774,6 +1020,7 @@ var SDK_VERSION2 = "2.0.0";
774
1020
  RateLimitedError,
775
1021
  RevokedCodeError,
776
1022
  SDK_VERSION,
1023
+ SchemasResource,
777
1024
  ServiceUnavailableError,
778
1025
  TimeoutError,
779
1026
  createClient,
package/dist/index.d.cts CHANGED
@@ -166,6 +166,144 @@ declare class AssetsResource {
166
166
  private buildQuery;
167
167
  }
168
168
 
169
+ /**
170
+ * Audit Resource
171
+ *
172
+ * Immutable audit log operations — list, get, and create custom audit events.
173
+ * Mirrors the Python SDK's `optropic.resources.audit` module.
174
+ */
175
+
176
+ interface AuditEvent {
177
+ readonly id: string;
178
+ readonly tenantId: string;
179
+ readonly eventType: string;
180
+ readonly actor: string;
181
+ readonly createdAt: string;
182
+ readonly resourceId?: string;
183
+ readonly resourceType?: string;
184
+ readonly details?: Record<string, unknown>;
185
+ readonly eventHash?: string;
186
+ readonly chainSequence?: number;
187
+ }
188
+ interface ListAuditParams {
189
+ readonly page?: number;
190
+ readonly limit?: number;
191
+ readonly event_type?: string;
192
+ readonly resource_id?: string;
193
+ readonly from_date?: string;
194
+ readonly to_date?: string;
195
+ }
196
+ interface ListAuditResponse {
197
+ readonly data: AuditEvent[];
198
+ readonly pagination: {
199
+ readonly total: number;
200
+ readonly page: number;
201
+ readonly perPage: number;
202
+ readonly totalPages: number;
203
+ };
204
+ }
205
+ interface CreateAuditEventParams {
206
+ readonly eventType: string;
207
+ readonly resourceId?: string;
208
+ readonly resourceType?: string;
209
+ readonly details?: Record<string, unknown>;
210
+ }
211
+ declare class AuditResource {
212
+ private readonly request;
213
+ constructor(request: RequestFn);
214
+ /**
215
+ * List audit events with optional filtering and pagination.
216
+ */
217
+ list(params?: ListAuditParams): Promise<ListAuditResponse>;
218
+ /**
219
+ * Retrieve a single audit event by ID.
220
+ */
221
+ get(eventId: string): Promise<AuditEvent>;
222
+ /**
223
+ * Record a custom audit event.
224
+ */
225
+ create(params: CreateAuditEventParams): Promise<AuditEvent>;
226
+ private buildQuery;
227
+ }
228
+
229
+ /**
230
+ * Compliance Resource
231
+ *
232
+ * Chain verification, Merkle proofs, audit export, and compliance configuration.
233
+ * Mirrors the Python SDK's `optropic.resources.compliance` module.
234
+ */
235
+
236
+ interface ChainVerifyResult {
237
+ readonly tenantId: string;
238
+ readonly chainValid: boolean;
239
+ readonly eventsChecked: number;
240
+ readonly verifiedAt: string;
241
+ readonly brokenAtSequence?: number;
242
+ }
243
+ interface MerkleRoot {
244
+ readonly id: string;
245
+ readonly rootHash: string;
246
+ readonly eventCount: number;
247
+ readonly periodStart: string;
248
+ readonly periodEnd: string;
249
+ readonly createdAt: string;
250
+ }
251
+ interface MerkleProof {
252
+ readonly eventId: string;
253
+ readonly eventHash: string;
254
+ readonly merkleRoot: string;
255
+ readonly proof: string[];
256
+ readonly verified: boolean;
257
+ readonly period: string;
258
+ }
259
+ interface ExportParams {
260
+ readonly from_date?: string;
261
+ readonly to_date?: string;
262
+ readonly limit?: number;
263
+ }
264
+ interface ExportResult {
265
+ readonly filename: string;
266
+ readonly eventCount: number;
267
+ readonly csv: string;
268
+ readonly signature?: string;
269
+ readonly publicKeyHex?: string;
270
+ readonly keyId?: string;
271
+ readonly algorithm?: string;
272
+ }
273
+ interface ComplianceConfig {
274
+ readonly complianceMode: string;
275
+ readonly retentionPolicy: Record<string, unknown>;
276
+ }
277
+ declare class ComplianceResource {
278
+ private readonly request;
279
+ constructor(request: RequestFn);
280
+ /**
281
+ * Verify the integrity of the full audit chain.
282
+ */
283
+ verifyChain(): Promise<ChainVerifyResult>;
284
+ /**
285
+ * Return all Merkle roots.
286
+ */
287
+ listMerkleRoots(): Promise<MerkleRoot[]>;
288
+ /**
289
+ * Return a Merkle inclusion proof for a specific audit event.
290
+ */
291
+ getMerkleProof(eventId: string): Promise<MerkleProof>;
292
+ /**
293
+ * Export audit data as a signed CSV.
294
+ */
295
+ exportAudit(params?: ExportParams): Promise<ExportResult>;
296
+ /**
297
+ * Retrieve the current compliance configuration.
298
+ */
299
+ getConfig(): Promise<ComplianceConfig>;
300
+ /**
301
+ * Update the compliance mode.
302
+ */
303
+ updateConfig(mode: string): Promise<ComplianceConfig>;
304
+ private buildQuery;
305
+ }
306
+
169
307
  /**
170
308
  * Keys Resource
171
309
  *
@@ -238,6 +376,96 @@ declare class KeysetsResource {
238
376
  private buildQuery;
239
377
  }
240
378
 
379
+ /**
380
+ * Schemas Resource
381
+ *
382
+ * Schema Registry operations — register and manage vertical config schemas.
383
+ * Mirrors the Python SDK's `optropic.resources.schemas` module.
384
+ */
385
+
386
+ interface VerticalSchema {
387
+ readonly id: string;
388
+ readonly tenantId: string;
389
+ readonly verticalId: string;
390
+ readonly version: string;
391
+ readonly metadataSchema: Record<string, unknown>;
392
+ readonly exportFormats: string[];
393
+ readonly isActive: boolean;
394
+ readonly createdAt: string;
395
+ readonly updatedAt: string;
396
+ readonly description?: string;
397
+ }
398
+ interface CreateSchemaParams {
399
+ readonly verticalId: string;
400
+ readonly metadataSchema: Record<string, unknown>;
401
+ readonly version?: string;
402
+ readonly exportFormats?: string[];
403
+ readonly description?: string;
404
+ }
405
+ interface UpdateSchemaParams {
406
+ readonly version?: string;
407
+ readonly metadataSchema?: Record<string, unknown>;
408
+ readonly exportFormats?: string[];
409
+ readonly description?: string;
410
+ readonly isActive?: boolean;
411
+ }
412
+ interface ListSchemasParams {
413
+ readonly page?: number;
414
+ readonly per_page?: number;
415
+ readonly include_inactive?: boolean;
416
+ }
417
+ interface ListSchemasResponse {
418
+ readonly data: VerticalSchema[];
419
+ readonly pagination: {
420
+ readonly total: number;
421
+ readonly page: number;
422
+ readonly perPage: number;
423
+ readonly totalPages: number;
424
+ };
425
+ }
426
+ interface SchemaValidationResult {
427
+ readonly valid: boolean;
428
+ readonly errors: Array<{
429
+ field: string;
430
+ message: string;
431
+ received?: string;
432
+ }>;
433
+ }
434
+ declare class SchemasResource {
435
+ private readonly request;
436
+ constructor(request: RequestFn);
437
+ /**
438
+ * Register or update a vertical config schema.
439
+ * If a schema already exists for the verticalId, it will be updated.
440
+ */
441
+ create(params: CreateSchemaParams): Promise<VerticalSchema>;
442
+ /**
443
+ * List registered vertical schemas with pagination.
444
+ */
445
+ list(params?: ListSchemasParams): Promise<ListSchemasResponse>;
446
+ /**
447
+ * Get the active schema for a specific vertical.
448
+ */
449
+ get(verticalId: string): Promise<VerticalSchema>;
450
+ /**
451
+ * Update an existing vertical schema.
452
+ */
453
+ update(verticalId: string, params: UpdateSchemaParams): Promise<VerticalSchema>;
454
+ /**
455
+ * Deactivate a vertical schema (soft delete).
456
+ */
457
+ delete(verticalId: string): Promise<void>;
458
+ /**
459
+ * Pre-flight validation: check if assetConfig matches the registered schema.
460
+ *
461
+ * This is a client-side convenience that fetches the schema and validates locally.
462
+ * The server also validates on asset creation.
463
+ */
464
+ validate(verticalId: string, assetConfig: Record<string, unknown>): Promise<SchemaValidationResult>;
465
+ private buildQuery;
466
+ private stripUndefined;
467
+ }
468
+
241
469
  /**
242
470
  * optropic - OptropicClient
243
471
  *
@@ -281,8 +509,11 @@ declare class OptropicClient {
281
509
  private readonly retryConfig;
282
510
  private readonly _sandbox;
283
511
  readonly assets: AssetsResource;
512
+ readonly audit: AuditResource;
513
+ readonly compliance: ComplianceResource;
284
514
  readonly keys: KeysResource;
285
515
  readonly keysets: KeysetsResource;
516
+ readonly schemas: SchemasResource;
286
517
  constructor(config: OptropicConfig);
287
518
  /** True when the client is in sandbox mode (test API key or explicit override). */
288
519
  get isSandbox(): boolean;
@@ -614,4 +845,4 @@ declare class ServiceUnavailableError extends OptropicError {
614
845
 
615
846
  declare const SDK_VERSION = "2.0.0";
616
847
 
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 };
848
+ export { type ApiKey, type Asset, AssetsResource, type AuditEvent, AuditResource, AuthenticationError, type BatchCreateParams, type BatchCreateResult, BatchNotFoundError, type ChainVerifyResult, CodeNotFoundError, type ComplianceConfig, ComplianceResource, type CreateAssetParams, type CreateAuditEventParams, type CreateKeyParams, type CreateKeyResult, type CreateKeysetParams, type CreateSchemaParams, type ErrorCode, type ExportParams, type ExportResult, InvalidCodeError, InvalidGTINError, InvalidSerialError, KeysResource, type Keyset, KeysetsResource, type ListAssetsParams, type ListAssetsResponse, type ListAuditParams, type ListAuditResponse, type ListKeysetsParams, type ListKeysetsResponse, type ListSchemasParams, type ListSchemasResponse, type MerkleProof, type MerkleRoot, NetworkError, OptropicClient, type OptropicConfig, OptropicError, QuotaExceededError, RateLimitedError, type RequestFn, type RetryConfig, RevokedCodeError, SDK_VERSION, type SchemaValidationResult, SchemasResource, ServiceUnavailableError, TimeoutError, type UpdateSchemaParams, type VerifyResult, type VerticalSchema, type WebhookVerifyOptions, type WebhookVerifyResult, createClient, verifyWebhookSignature };
package/dist/index.d.ts CHANGED
@@ -166,6 +166,144 @@ declare class AssetsResource {
166
166
  private buildQuery;
167
167
  }
168
168
 
169
+ /**
170
+ * Audit Resource
171
+ *
172
+ * Immutable audit log operations — list, get, and create custom audit events.
173
+ * Mirrors the Python SDK's `optropic.resources.audit` module.
174
+ */
175
+
176
+ interface AuditEvent {
177
+ readonly id: string;
178
+ readonly tenantId: string;
179
+ readonly eventType: string;
180
+ readonly actor: string;
181
+ readonly createdAt: string;
182
+ readonly resourceId?: string;
183
+ readonly resourceType?: string;
184
+ readonly details?: Record<string, unknown>;
185
+ readonly eventHash?: string;
186
+ readonly chainSequence?: number;
187
+ }
188
+ interface ListAuditParams {
189
+ readonly page?: number;
190
+ readonly limit?: number;
191
+ readonly event_type?: string;
192
+ readonly resource_id?: string;
193
+ readonly from_date?: string;
194
+ readonly to_date?: string;
195
+ }
196
+ interface ListAuditResponse {
197
+ readonly data: AuditEvent[];
198
+ readonly pagination: {
199
+ readonly total: number;
200
+ readonly page: number;
201
+ readonly perPage: number;
202
+ readonly totalPages: number;
203
+ };
204
+ }
205
+ interface CreateAuditEventParams {
206
+ readonly eventType: string;
207
+ readonly resourceId?: string;
208
+ readonly resourceType?: string;
209
+ readonly details?: Record<string, unknown>;
210
+ }
211
+ declare class AuditResource {
212
+ private readonly request;
213
+ constructor(request: RequestFn);
214
+ /**
215
+ * List audit events with optional filtering and pagination.
216
+ */
217
+ list(params?: ListAuditParams): Promise<ListAuditResponse>;
218
+ /**
219
+ * Retrieve a single audit event by ID.
220
+ */
221
+ get(eventId: string): Promise<AuditEvent>;
222
+ /**
223
+ * Record a custom audit event.
224
+ */
225
+ create(params: CreateAuditEventParams): Promise<AuditEvent>;
226
+ private buildQuery;
227
+ }
228
+
229
+ /**
230
+ * Compliance Resource
231
+ *
232
+ * Chain verification, Merkle proofs, audit export, and compliance configuration.
233
+ * Mirrors the Python SDK's `optropic.resources.compliance` module.
234
+ */
235
+
236
+ interface ChainVerifyResult {
237
+ readonly tenantId: string;
238
+ readonly chainValid: boolean;
239
+ readonly eventsChecked: number;
240
+ readonly verifiedAt: string;
241
+ readonly brokenAtSequence?: number;
242
+ }
243
+ interface MerkleRoot {
244
+ readonly id: string;
245
+ readonly rootHash: string;
246
+ readonly eventCount: number;
247
+ readonly periodStart: string;
248
+ readonly periodEnd: string;
249
+ readonly createdAt: string;
250
+ }
251
+ interface MerkleProof {
252
+ readonly eventId: string;
253
+ readonly eventHash: string;
254
+ readonly merkleRoot: string;
255
+ readonly proof: string[];
256
+ readonly verified: boolean;
257
+ readonly period: string;
258
+ }
259
+ interface ExportParams {
260
+ readonly from_date?: string;
261
+ readonly to_date?: string;
262
+ readonly limit?: number;
263
+ }
264
+ interface ExportResult {
265
+ readonly filename: string;
266
+ readonly eventCount: number;
267
+ readonly csv: string;
268
+ readonly signature?: string;
269
+ readonly publicKeyHex?: string;
270
+ readonly keyId?: string;
271
+ readonly algorithm?: string;
272
+ }
273
+ interface ComplianceConfig {
274
+ readonly complianceMode: string;
275
+ readonly retentionPolicy: Record<string, unknown>;
276
+ }
277
+ declare class ComplianceResource {
278
+ private readonly request;
279
+ constructor(request: RequestFn);
280
+ /**
281
+ * Verify the integrity of the full audit chain.
282
+ */
283
+ verifyChain(): Promise<ChainVerifyResult>;
284
+ /**
285
+ * Return all Merkle roots.
286
+ */
287
+ listMerkleRoots(): Promise<MerkleRoot[]>;
288
+ /**
289
+ * Return a Merkle inclusion proof for a specific audit event.
290
+ */
291
+ getMerkleProof(eventId: string): Promise<MerkleProof>;
292
+ /**
293
+ * Export audit data as a signed CSV.
294
+ */
295
+ exportAudit(params?: ExportParams): Promise<ExportResult>;
296
+ /**
297
+ * Retrieve the current compliance configuration.
298
+ */
299
+ getConfig(): Promise<ComplianceConfig>;
300
+ /**
301
+ * Update the compliance mode.
302
+ */
303
+ updateConfig(mode: string): Promise<ComplianceConfig>;
304
+ private buildQuery;
305
+ }
306
+
169
307
  /**
170
308
  * Keys Resource
171
309
  *
@@ -238,6 +376,96 @@ declare class KeysetsResource {
238
376
  private buildQuery;
239
377
  }
240
378
 
379
+ /**
380
+ * Schemas Resource
381
+ *
382
+ * Schema Registry operations — register and manage vertical config schemas.
383
+ * Mirrors the Python SDK's `optropic.resources.schemas` module.
384
+ */
385
+
386
+ interface VerticalSchema {
387
+ readonly id: string;
388
+ readonly tenantId: string;
389
+ readonly verticalId: string;
390
+ readonly version: string;
391
+ readonly metadataSchema: Record<string, unknown>;
392
+ readonly exportFormats: string[];
393
+ readonly isActive: boolean;
394
+ readonly createdAt: string;
395
+ readonly updatedAt: string;
396
+ readonly description?: string;
397
+ }
398
+ interface CreateSchemaParams {
399
+ readonly verticalId: string;
400
+ readonly metadataSchema: Record<string, unknown>;
401
+ readonly version?: string;
402
+ readonly exportFormats?: string[];
403
+ readonly description?: string;
404
+ }
405
+ interface UpdateSchemaParams {
406
+ readonly version?: string;
407
+ readonly metadataSchema?: Record<string, unknown>;
408
+ readonly exportFormats?: string[];
409
+ readonly description?: string;
410
+ readonly isActive?: boolean;
411
+ }
412
+ interface ListSchemasParams {
413
+ readonly page?: number;
414
+ readonly per_page?: number;
415
+ readonly include_inactive?: boolean;
416
+ }
417
+ interface ListSchemasResponse {
418
+ readonly data: VerticalSchema[];
419
+ readonly pagination: {
420
+ readonly total: number;
421
+ readonly page: number;
422
+ readonly perPage: number;
423
+ readonly totalPages: number;
424
+ };
425
+ }
426
+ interface SchemaValidationResult {
427
+ readonly valid: boolean;
428
+ readonly errors: Array<{
429
+ field: string;
430
+ message: string;
431
+ received?: string;
432
+ }>;
433
+ }
434
+ declare class SchemasResource {
435
+ private readonly request;
436
+ constructor(request: RequestFn);
437
+ /**
438
+ * Register or update a vertical config schema.
439
+ * If a schema already exists for the verticalId, it will be updated.
440
+ */
441
+ create(params: CreateSchemaParams): Promise<VerticalSchema>;
442
+ /**
443
+ * List registered vertical schemas with pagination.
444
+ */
445
+ list(params?: ListSchemasParams): Promise<ListSchemasResponse>;
446
+ /**
447
+ * Get the active schema for a specific vertical.
448
+ */
449
+ get(verticalId: string): Promise<VerticalSchema>;
450
+ /**
451
+ * Update an existing vertical schema.
452
+ */
453
+ update(verticalId: string, params: UpdateSchemaParams): Promise<VerticalSchema>;
454
+ /**
455
+ * Deactivate a vertical schema (soft delete).
456
+ */
457
+ delete(verticalId: string): Promise<void>;
458
+ /**
459
+ * Pre-flight validation: check if assetConfig matches the registered schema.
460
+ *
461
+ * This is a client-side convenience that fetches the schema and validates locally.
462
+ * The server also validates on asset creation.
463
+ */
464
+ validate(verticalId: string, assetConfig: Record<string, unknown>): Promise<SchemaValidationResult>;
465
+ private buildQuery;
466
+ private stripUndefined;
467
+ }
468
+
241
469
  /**
242
470
  * optropic - OptropicClient
243
471
  *
@@ -281,8 +509,11 @@ declare class OptropicClient {
281
509
  private readonly retryConfig;
282
510
  private readonly _sandbox;
283
511
  readonly assets: AssetsResource;
512
+ readonly audit: AuditResource;
513
+ readonly compliance: ComplianceResource;
284
514
  readonly keys: KeysResource;
285
515
  readonly keysets: KeysetsResource;
516
+ readonly schemas: SchemasResource;
286
517
  constructor(config: OptropicConfig);
287
518
  /** True when the client is in sandbox mode (test API key or explicit override). */
288
519
  get isSandbox(): boolean;
@@ -614,4 +845,4 @@ declare class ServiceUnavailableError extends OptropicError {
614
845
 
615
846
  declare const SDK_VERSION = "2.0.0";
616
847
 
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 };
848
+ export { type ApiKey, type Asset, AssetsResource, type AuditEvent, AuditResource, AuthenticationError, type BatchCreateParams, type BatchCreateResult, BatchNotFoundError, type ChainVerifyResult, CodeNotFoundError, type ComplianceConfig, ComplianceResource, type CreateAssetParams, type CreateAuditEventParams, type CreateKeyParams, type CreateKeyResult, type CreateKeysetParams, type CreateSchemaParams, type ErrorCode, type ExportParams, type ExportResult, InvalidCodeError, InvalidGTINError, InvalidSerialError, KeysResource, type Keyset, KeysetsResource, type ListAssetsParams, type ListAssetsResponse, type ListAuditParams, type ListAuditResponse, type ListKeysetsParams, type ListKeysetsResponse, type ListSchemasParams, type ListSchemasResponse, type MerkleProof, type MerkleRoot, NetworkError, OptropicClient, type OptropicConfig, OptropicError, QuotaExceededError, RateLimitedError, type RequestFn, type RetryConfig, RevokedCodeError, SDK_VERSION, type SchemaValidationResult, SchemasResource, ServiceUnavailableError, TimeoutError, type UpdateSchemaParams, type VerifyResult, type VerticalSchema, type WebhookVerifyOptions, type WebhookVerifyResult, createClient, verifyWebhookSignature };
package/dist/index.js CHANGED
@@ -427,6 +427,117 @@ var AssetsResource = class {
427
427
  }
428
428
  };
429
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
+
430
541
  // src/resources/keys.ts
431
542
  var KeysResource = class {
432
543
  constructor(request) {
@@ -463,6 +574,130 @@ var KeysetsResource = class {
463
574
  }
464
575
  };
465
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
+
466
701
  // src/client.ts
467
702
  var DEFAULT_BASE_URL = "https://api.optropic.com";
468
703
  var DEFAULT_TIMEOUT = 3e4;
@@ -479,8 +714,11 @@ var OptropicClient = class {
479
714
  retryConfig;
480
715
  _sandbox;
481
716
  assets;
717
+ audit;
718
+ compliance;
482
719
  keys;
483
720
  keysets;
721
+ schemas;
484
722
  constructor(config) {
485
723
  if (!config.apiKey || !this.isValidApiKey(config.apiKey)) {
486
724
  throw new AuthenticationError(
@@ -507,8 +745,11 @@ var OptropicClient = class {
507
745
  };
508
746
  const boundRequest = this.request.bind(this);
509
747
  this.assets = new AssetsResource(boundRequest, this);
748
+ this.audit = new AuditResource(boundRequest);
749
+ this.compliance = new ComplianceResource(boundRequest);
510
750
  this.keys = new KeysResource(boundRequest);
511
751
  this.keysets = new KeysetsResource(boundRequest);
752
+ this.schemas = new SchemasResource(boundRequest);
512
753
  }
513
754
  // ─────────────────────────────────────────────────────────────────────────
514
755
  // ENVIRONMENT DETECTION
@@ -703,9 +944,11 @@ async function verifyWebhookSignature(options) {
703
944
  var SDK_VERSION2 = "2.0.0";
704
945
  export {
705
946
  AssetsResource,
947
+ AuditResource,
706
948
  AuthenticationError,
707
949
  BatchNotFoundError,
708
950
  CodeNotFoundError,
951
+ ComplianceResource,
709
952
  InvalidCodeError,
710
953
  InvalidGTINError,
711
954
  InvalidSerialError,
@@ -718,6 +961,7 @@ export {
718
961
  RateLimitedError,
719
962
  RevokedCodeError,
720
963
  SDK_VERSION2 as SDK_VERSION,
964
+ SchemasResource,
721
965
  ServiceUnavailableError,
722
966
  TimeoutError,
723
967
  createClient,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "optropic",
3
- "version": "2.0.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": "Optropic GmbH",
35
+ "author": {
36
+ "name": "Virtrex GmbH",
37
+ "url": "https://optropic.com"
38
+ },
36
39
  "license": "MIT",
37
40
  "repository": {
38
41
  "type": "git",