@pacspace-io/sdk 0.2.0 → 0.2.1

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.
@@ -3,6 +3,24 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.BalanceResource = void 0;
4
4
  const polling_1 = require("../utils/polling");
5
5
  const submission_1 = require("./submission");
6
+ const toRecordStatus = (status) => {
7
+ const normalized = String(status ?? '').toUpperCase();
8
+ if (normalized === 'PROCESSING')
9
+ return 'PROCESSING';
10
+ if (normalized === 'VERIFIED')
11
+ return 'VERIFIED';
12
+ if (normalized === 'FAILED')
13
+ return 'FAILED';
14
+ return 'QUEUED';
15
+ };
16
+ const toCheckpointType = (value) => {
17
+ return String(value) === 'recordId' ? 'recordId' : 'proofRoot';
18
+ };
19
+ const toApiCheckpointType = (value) => {
20
+ if (!value)
21
+ return undefined;
22
+ return value;
23
+ };
6
24
  /**
7
25
  * PacSpace Balance API resource.
8
26
  *
@@ -56,7 +74,8 @@ class BalanceResource {
56
74
  body.referenceId = referenceId;
57
75
  if (metadata !== undefined)
58
76
  body.metadata = metadata;
59
- return this.client.post('/api/v1/balance/delta', body, requestOptions);
77
+ const response = await this.client.post('/api/v1/balance/delta', body, requestOptions);
78
+ return this.normalizeEmitResponse(response);
60
79
  }
61
80
  /**
62
81
  * Record a delta and wait for it to be verified.
@@ -87,42 +106,45 @@ class BalanceResource {
87
106
  signal,
88
107
  });
89
108
  // If already terminal, return immediately
90
- if (['ANCHORED', 'VERIFIED', 'FAILED'].includes(initial.status)) {
109
+ if (['VERIFIED', 'FAILED'].includes(initial.status)) {
91
110
  return initial;
92
111
  }
93
- // Poll for terminal status using the balance delta endpoint
94
- return (0, polling_1.pollUntilTerminal)(() => this.client.get(`/api/v1/balance/delta/${initial.anchorId}`, { signal }), { timeout, pollInterval, signal });
112
+ // Poll for terminal status using the balance delta endpoint, then merge
113
+ // the latest status with the original emit metadata.
114
+ const terminalStatus = await (0, polling_1.pollUntilTerminal)(() => this.deltaStatus(initial.recordId, { signal }), { timeout, pollInterval, signal });
115
+ return this.mergeEmitWithStatus(initial, terminalStatus);
95
116
  }
96
117
  /**
97
118
  * Check the status of a previously emitted delta.
98
119
  *
99
- * @param anchorId - The anchor ID returned from emit.
120
+ * @param recordId - The record ID returned from emit.
100
121
  * @param options - Request overrides.
101
122
  * @returns Delta status with receipt and transaction details.
102
123
  *
103
124
  * @example
104
125
  * ```typescript
105
- * const status = await pac.balance.deltaStatus('anc_abc123');
106
- * console.log(status.status); // 'QUEUED', 'ANCHORED', etc.
126
+ * const status = await pac.balance.deltaStatus('rec_abc123');
127
+ * console.log(status.status); // 'QUEUED', 'PROCESSING', etc.
107
128
  * ```
108
129
  */
109
- async deltaStatus(anchorId, options) {
110
- return this.client.get(`/api/v1/balance/delta/${encodeURIComponent(anchorId)}`, options);
130
+ async deltaStatus(recordId, options) {
131
+ const response = await this.client.get(`/api/v1/balance/delta/${encodeURIComponent(recordId)}`, options);
132
+ return this.normalizeDeltaStatusResponse(response);
111
133
  }
112
134
  /**
113
135
  * Alias for `deltaStatus()` with lifecycle-oriented naming.
114
136
  */
115
- async getAnchorStatus(anchorId, options) {
116
- return this.deltaStatus(anchorId, options);
137
+ async getRecordStatus(recordId, options) {
138
+ return this.deltaStatus(recordId, options);
117
139
  }
118
140
  /**
119
- * Poll anchor lifecycle until it reaches a terminal status.
141
+ * Poll record lifecycle until it reaches a terminal status.
120
142
  *
121
- * Terminal statuses are `ANCHORED`, `VERIFIED`, and `FAILED`.
143
+ * Terminal statuses are `VERIFIED` and `FAILED`.
122
144
  */
123
- async waitForAnchored(anchorId, options) {
145
+ async waitForVerified(recordId, options) {
124
146
  const { timeout = 60000, pollInterval = 2000, signal, ...requestOptions } = options ?? {};
125
- return (0, polling_1.pollUntilTerminal)(() => this.getAnchorStatus(anchorId, {
147
+ return (0, polling_1.pollUntilTerminal)(() => this.getRecordStatus(recordId, {
126
148
  ...requestOptions,
127
149
  signal,
128
150
  }), { timeout, pollInterval, signal });
@@ -149,7 +171,8 @@ class BalanceResource {
149
171
  params.set('limit', String(limit));
150
172
  const query = params.toString();
151
173
  const path = `/api/v1/balance/gaps/${encodeURIComponent(customerId)}${query ? `?${query}` : ''}`;
152
- return this.client.get(path, requestOptions);
174
+ const response = await this.client.get(path, requestOptions);
175
+ return this.normalizeSequenceGapsResponse(response);
153
176
  }
154
177
  /**
155
178
  * Record multiple deltas in a single batch request.
@@ -172,7 +195,8 @@ class BalanceResource {
172
195
  */
173
196
  async emitBatch(deltas, options) {
174
197
  const { ...requestOptions } = options ?? {};
175
- return this.client.post('/api/v1/balance/delta/batch', { deltas }, requestOptions);
198
+ const response = await this.client.post('/api/v1/balance/delta/batch', { deltas }, requestOptions);
199
+ return this.normalizeBulkEmitResponse(response);
176
200
  }
177
201
  /**
178
202
  * Queue one tenant-produced summary for SDK-managed submission.
@@ -250,8 +274,9 @@ class BalanceResource {
250
274
  if (startingCheckpoint !== undefined) {
251
275
  params.set('startingCheckpoint', startingCheckpoint);
252
276
  }
253
- if (startingCheckpointType !== undefined) {
254
- params.set('startingCheckpointType', startingCheckpointType);
277
+ const apiCheckpointType = toApiCheckpointType(startingCheckpointType);
278
+ if (apiCheckpointType !== undefined) {
279
+ params.set('startingCheckpointType', apiCheckpointType);
255
280
  }
256
281
  if (limit !== undefined) {
257
282
  params.set('limit', String(limit));
@@ -261,7 +286,8 @@ class BalanceResource {
261
286
  }
262
287
  const query = params.toString();
263
288
  const path = `/api/v1/balance/derive/${encodeURIComponent(customerId)}${query ? `?${query}` : ''}`;
264
- return this.client.get(path, requestOptions);
289
+ const response = await this.client.get(path, requestOptions);
290
+ return this.normalizeDeriveResponse(response);
265
291
  }
266
292
  // -------------------------------------------------------------------------
267
293
  // Compare
@@ -301,10 +327,12 @@ class BalanceResource {
301
327
  if (startingCheckpoint !== undefined) {
302
328
  body.startingCheckpoint = startingCheckpoint;
303
329
  }
304
- if (startingCheckpointType !== undefined) {
305
- body.startingCheckpointType = startingCheckpointType;
330
+ const apiCheckpointType = toApiCheckpointType(startingCheckpointType);
331
+ if (apiCheckpointType !== undefined) {
332
+ body.startingCheckpointType = apiCheckpointType;
306
333
  }
307
- return this.client.post('/api/v1/balance/compare', body, requestOptions);
334
+ const response = await this.client.post('/api/v1/balance/compare', body, requestOptions);
335
+ return this.normalizeCompareResponse(response);
308
336
  }
309
337
  // -------------------------------------------------------------------------
310
338
  // Receipt
@@ -314,7 +342,7 @@ class BalanceResource {
314
342
  *
315
343
  * When `options.period` is provided, returns a period-specific receipt (proof root,
316
344
  * verifyUrl, verificationReference) suitable for sharing on invoices. When
317
- * omitted, returns the full ledger receipt with all verified deltas.
345
+ * omitted, returns the full record receipt with all verified deltas.
318
346
  *
319
347
  * @param customerId - The customer account to generate a receipt for.
320
348
  * @param options - Optional period (YYYY-MM) and request overrides.
@@ -327,7 +355,7 @@ class BalanceResource {
327
355
  * console.log(receipt.proofRoot); // Include on invoice
328
356
  * console.log(receipt.verifyUrl); // Share with counterparty
329
357
  *
330
- * // Full ledger receipt
358
+ * // Full record receipt
331
359
  * const full = await pac.balance.receipt('cust_123');
332
360
  * console.log(full.finalBalance);
333
361
  * ```
@@ -343,7 +371,8 @@ class BalanceResource {
343
371
  }
344
372
  const { period: _period, timePreset: _timePreset, startDate: _startDate, endDate: _endDate, ...requestOptions } = opts;
345
373
  const hasOverrides = Object.keys(requestOptions).length > 0;
346
- return this.client.get(`/api/v1/balance/receipt/${encodeURIComponent(customerId)}`, hasOverrides ? requestOptions : undefined);
374
+ const response = await this.client.get(`/api/v1/balance/receipt/${encodeURIComponent(customerId)}`, hasOverrides ? requestOptions : undefined);
375
+ return this.normalizeReceiptResponse(response);
347
376
  }
348
377
  /**
349
378
  * @internal
@@ -362,7 +391,8 @@ class BalanceResource {
362
391
  params.set('endDate', endDate);
363
392
  const query = params.toString();
364
393
  const path = `/api/v1/balance/invoice-proof/${encodeURIComponent(customerId)}${query ? `?${query}` : ''}`;
365
- return this.client.get(path, requestOptions);
394
+ const response = await this.client.get(path, requestOptions);
395
+ return this.normalizeInvoiceProofResponse(response);
366
396
  }
367
397
  // -------------------------------------------------------------------------
368
398
  // Checkpoint
@@ -376,14 +406,14 @@ class BalanceResource {
376
406
  *
377
407
  * @param customerId - Customer to checkpoint (omit for all customers).
378
408
  * @param options - Period (YYYY-MM) and request overrides.
379
- * @returns Checkpoint details with Merkle root and status.
409
+ * @returns Checkpoint details with proof root and status.
380
410
  *
381
411
  * @example
382
412
  * ```typescript
383
413
  * const checkpoint = await pac.balance.checkpoint('cust_123', {
384
414
  * period: '2026-02',
385
415
  * });
386
- * console.log(checkpoint.merkleRoot); // Include in your invoice
416
+ * console.log(checkpoint.proofRoot); // Include in your invoice
387
417
  * ```
388
418
  */
389
419
  async checkpoint(customerId, options) {
@@ -403,7 +433,8 @@ class BalanceResource {
403
433
  body.mode = mode;
404
434
  if (fingerprints !== undefined)
405
435
  body.fingerprints = fingerprints;
406
- return this.client.post('/api/v1/balance/checkpoint', body, requestOptions);
436
+ const response = await this.client.post('/api/v1/balance/checkpoint', body, requestOptions);
437
+ return this.normalizeCheckpointResponse(response);
407
438
  }
408
439
  // -------------------------------------------------------------------------
409
440
  // List Checkpoints
@@ -444,7 +475,8 @@ class BalanceResource {
444
475
  params.set('offset', String(offset));
445
476
  const query = params.toString();
446
477
  const path = `/api/v1/balance/checkpoints${query ? `?${query}` : ''}`;
447
- return this.client.get(path, requestOptions);
478
+ const response = await this.client.get(path, requestOptions);
479
+ return this.normalizeListCheckpointsResponse(response);
448
480
  }
449
481
  // -------------------------------------------------------------------------
450
482
  // Webhook Deliveries
@@ -494,13 +526,13 @@ class BalanceResource {
494
526
  // Customer Ledgers
495
527
  // -------------------------------------------------------------------------
496
528
  /**
497
- * List all customer ledgers for your account.
529
+ * List all customer balance records for your account.
498
530
  *
499
531
  * Each unique customerId passed to emit() automatically creates an
500
- * isolated ledger. This method returns a paginated list of all ledgers.
532
+ * isolated customer record. This method returns a paginated list.
501
533
  *
502
534
  * @param options - Search, pagination, and request overrides.
503
- * @returns Paginated list of customer ledgers.
535
+ * @returns Paginated list of customer records.
504
536
  *
505
537
  * @example
506
538
  * ```typescript
@@ -521,24 +553,24 @@ class BalanceResource {
521
553
  params.set('page', String(page));
522
554
  const query = params.toString();
523
555
  const path = `/api/v1/balance/customers${query ? `?${query}` : ''}`;
524
- return this.client.get(path, requestOptions);
556
+ const response = await this.client.get(path, requestOptions);
557
+ return this.normalizeListCustomersResponse(response);
525
558
  }
526
559
  /**
527
- * Get the full ledger detail for a specific customer.
560
+ * Get the full record detail for a specific customer.
528
561
  *
529
- * Returns the derived ledger identifier, computed balance, delta count,
530
- * and latest checkpoint. The ledger identifier is deterministic --
531
- * the same customerId always produces the same identifier.
562
+ * Returns the derived customer reference, computed balance, delta count,
563
+ * and latest checkpoint. The customer reference is deterministic.
532
564
  *
533
565
  * @param customerId - The customer to look up.
534
566
  * @param options - Activity pagination and request overrides.
535
- * @returns Customer ledger detail including balance and identifiers.
567
+ * @returns Customer detail including balance and references.
536
568
  *
537
569
  * @example
538
570
  * ```typescript
539
- * const ledger = await pac.balance.customer('cust_001');
540
- * console.log(ledger.ledgerIdentifier); // 0xA1b2...Ef34
541
- * console.log(ledger.computedBalance); // 4500.00
571
+ * const customer = await pac.balance.customer('cust_001');
572
+ * console.log(customer.customerReference); // 0xA1b2...Ef34
573
+ * console.log(customer.computedBalance); // 4500.00
542
574
  * ```
543
575
  */
544
576
  async customer(customerId, options) {
@@ -550,7 +582,380 @@ class BalanceResource {
550
582
  params.set('deltaLimit', String(deltaLimit));
551
583
  const query = params.toString();
552
584
  const path = `/api/v1/balance/customers/${encodeURIComponent(customerId)}${query ? `?${query}` : ''}`;
553
- return this.client.get(path, requestOptions);
585
+ const response = await this.client.get(path, requestOptions);
586
+ return this.normalizeCustomerDetailResponse(response);
587
+ }
588
+ normalizeEmitResponse(data) {
589
+ const proofRoot = String(data.proofRoot ?? data.receiptId ?? '');
590
+ return {
591
+ recordId: String(data.recordId ?? ''),
592
+ customerId: String(data.customerId ?? ''),
593
+ delta: Number(data.delta ?? 0),
594
+ sequenceNumber: data.sequenceNumber == null ? undefined : String(data.sequenceNumber),
595
+ status: toRecordStatus(data.status),
596
+ receiptId: String(data.receiptId ?? proofRoot),
597
+ proofRoot,
598
+ itemHashes: Array.isArray(data.itemHashes)
599
+ ? data.itemHashes.map((value) => String(value))
600
+ : [],
601
+ estimatedVerifiedDeltas: Number(data.estimatedVerifiedDeltas ?? 0),
602
+ estimatedCredits: Number(data.estimatedCredits ?? 0),
603
+ receivedAt: String(data.receivedAt ?? ''),
604
+ message: String(data.message ?? ''),
605
+ };
606
+ }
607
+ mergeEmitWithStatus(initial, status) {
608
+ const firstDelta = status.deltas[0];
609
+ const customerId = status.customerId ?? firstDelta?.customerId ?? initial.customerId;
610
+ const delta = status.delta ?? firstDelta?.delta ?? initial.delta;
611
+ const sequenceNumber = firstDelta?.sequenceNumber ?? initial.sequenceNumber;
612
+ return {
613
+ ...initial,
614
+ recordId: status.recordId || initial.recordId,
615
+ customerId: customerId == null ? initial.customerId : String(customerId),
616
+ delta: delta == null ? initial.delta : Number(delta),
617
+ sequenceNumber: sequenceNumber ?? undefined,
618
+ status: status.status,
619
+ receiptId: status.receiptId ?? initial.receiptId,
620
+ proofRoot: status.receiptId ?? initial.proofRoot,
621
+ receivedAt: status.receivedAt || initial.receivedAt,
622
+ message: status.status === 'VERIFIED'
623
+ ? 'Delta verified.'
624
+ : status.status === 'FAILED'
625
+ ? 'Delta verification failed.'
626
+ : initial.message,
627
+ };
628
+ }
629
+ normalizeDeltaStatusResponse(data) {
630
+ const deltas = Array.isArray(data.deltas)
631
+ ? data.deltas.map((entry) => {
632
+ const item = entry;
633
+ return {
634
+ customerId: item.customerId == null ? null : String(item.customerId),
635
+ delta: item.delta == null ? null : Number(item.delta),
636
+ sequenceNumber: item.sequenceNumber == null ? null : String(item.sequenceNumber),
637
+ };
638
+ })
639
+ : [];
640
+ return {
641
+ recordId: String(data.recordId ?? ''),
642
+ status: toRecordStatus(data.status),
643
+ deltaCount: Number(data.deltaCount ?? deltas.length),
644
+ customerId: data.customerId == null ? null : String(data.customerId),
645
+ delta: data.delta == null ? null : Number(data.delta),
646
+ deltas,
647
+ receiptId: data.receiptId == null ? null : String(data.receiptId),
648
+ verificationReference: data.verificationReference == null ? null : String(data.verificationReference),
649
+ receivedAt: String(data.receivedAt ?? ''),
650
+ verifiedAt: data.verifiedAt == null ? null : String(data.verifiedAt),
651
+ };
652
+ }
653
+ normalizeDeriveResponse(data) {
654
+ const deltas = Array.isArray(data.deltas)
655
+ ? data.deltas.map((entry) => {
656
+ const item = entry;
657
+ const proofRoot = String(item.proofRoot ?? item.receiptId ?? '');
658
+ return {
659
+ recordId: String(item.recordId ?? ''),
660
+ itemHash: String(item.itemHash ?? ''),
661
+ proofRoot,
662
+ receiptId: String(item.receiptId ?? proofRoot),
663
+ sequenceNumber: item.sequenceNumber == null ? null : String(item.sequenceNumber),
664
+ delta: Number(item.delta ?? 0),
665
+ reason: item.reason == null ? null : String(item.reason),
666
+ referenceId: item.referenceId == null ? null : String(item.referenceId),
667
+ window: String(item.window ?? ''),
668
+ declaredTimestamp: item.declaredTimestamp == null
669
+ ? null
670
+ : String(item.declaredTimestamp),
671
+ blockTimestamp: item.blockTimestamp == null ? null : String(item.blockTimestamp),
672
+ dataPurged: Boolean(item.dataPurged),
673
+ verified: true,
674
+ };
675
+ })
676
+ : [];
677
+ const verificationProof = (data.verificationProof ??
678
+ {});
679
+ const windowSummaries = Array.isArray(data.windowSummaries)
680
+ ? data.windowSummaries.map((entry) => {
681
+ const item = entry;
682
+ return {
683
+ window: String(item.window ?? ''),
684
+ deltasCount: Number(item.deltasCount ?? 0),
685
+ netDelta: Number(item.netDelta ?? 0),
686
+ firstBlockTimestamp: item.firstBlockTimestamp == null
687
+ ? null
688
+ : String(item.firstBlockTimestamp),
689
+ lastBlockTimestamp: item.lastBlockTimestamp == null
690
+ ? null
691
+ : String(item.lastBlockTimestamp),
692
+ };
693
+ })
694
+ : [];
695
+ const pagination = (data.pagination ?? {});
696
+ const hint = data._hint;
697
+ return {
698
+ customerId: String(data.customerId ?? ''),
699
+ startingBalance: Number(data.startingBalance ?? 0),
700
+ startingCheckpoint: String(data.startingCheckpoint ?? 'genesis'),
701
+ startingCheckpointType: toCheckpointType(data.startingCheckpointType),
702
+ deltasCount: Number(data.deltasCount ?? deltas.length),
703
+ computedBalance: Number(data.computedBalance ?? 0),
704
+ latestCheckpoint: data.latestCheckpoint == null ? null : String(data.latestCheckpoint),
705
+ latestReceiptId: data.latestReceiptId == null ? null : String(data.latestReceiptId),
706
+ deltas,
707
+ windowSummaries,
708
+ verificationProof: {
709
+ proofRoot: verificationProof.proofRoot == null
710
+ ? null
711
+ : String(verificationProof.proofRoot),
712
+ message: String(verificationProof.message ?? ''),
713
+ },
714
+ pagination: {
715
+ total: Number(pagination.total ?? deltas.length),
716
+ limit: Number(pagination.limit ?? deltas.length),
717
+ offset: Number(pagination.offset ?? 0),
718
+ },
719
+ _hint: hint == null
720
+ ? undefined
721
+ : {
722
+ message: String(hint.message ?? ''),
723
+ recommendation: String(hint.recommendation ?? ''),
724
+ docsUrl: String(hint.docsUrl ?? ''),
725
+ },
726
+ };
727
+ }
728
+ normalizeCompareResponse(data) {
729
+ const proof = (data.proof ?? {});
730
+ return {
731
+ customerId: String(data.customerId ?? ''),
732
+ yourBalance: Number(data.yourBalance ?? 0),
733
+ theirBalance: Number(data.theirBalance ?? 0),
734
+ neutralBalance: Number(data.neutralBalance ?? 0),
735
+ matchesYours: Boolean(data.matchesYours),
736
+ matchesTheirs: Boolean(data.matchesTheirs),
737
+ deltasVerified: Number(data.deltasVerified ?? 0),
738
+ discrepancyReport: data.discrepancyReport ?? null,
739
+ proof: {
740
+ proofRoot: proof.proofRoot == null ? null : String(proof.proofRoot),
741
+ latestCheckpoint: proof.latestCheckpoint == null ? null : String(proof.latestCheckpoint),
742
+ windowSummaries: proof.windowSummaries ??
743
+ [],
744
+ },
745
+ };
746
+ }
747
+ normalizeReceiptResponse(data) {
748
+ const verification = (data.verification ?? {});
749
+ return {
750
+ customerId: String(data.customerId ?? ''),
751
+ generatedAt: String(data.generatedAt ?? ''),
752
+ deltasCount: Number(data.deltasCount ?? 0),
753
+ finalBalance: Number(data.finalBalance ?? 0),
754
+ proofRoot: data.proofRoot == null ? null : String(data.proofRoot),
755
+ receiptId: data.receiptId == null ? null : String(data.receiptId),
756
+ latestCheckpoint: data.latestCheckpoint == null ? null : String(data.latestCheckpoint),
757
+ deltas: data.deltas ?? [],
758
+ windowSummaries: data.windowSummaries ?? [],
759
+ verification: {
760
+ message: String(verification.message ?? ''),
761
+ itemHashes: Array.isArray(verification.itemHashes)
762
+ ? verification.itemHashes.map((value) => String(value))
763
+ : [],
764
+ },
765
+ };
766
+ }
767
+ normalizeInvoiceProofResponse(data) {
768
+ return {
769
+ customerId: String(data.customerId ?? ''),
770
+ period: String(data.period ?? ''),
771
+ proofRoot: String(data.proofRoot ?? ''),
772
+ verificationReference: data.verificationReference == null
773
+ ? null
774
+ : String(data.verificationReference),
775
+ verificationExplorerUrl: data.verificationExplorerUrl == null
776
+ ? null
777
+ : String(data.verificationExplorerUrl),
778
+ verifyUrl: data.verifyUrl == null ? null : String(data.verifyUrl),
779
+ deltaCount: Number(data.deltaCount ?? 0),
780
+ startingBalance: Number(data.startingBalance ?? 0),
781
+ finalBalance: Number(data.finalBalance ?? 0),
782
+ windowSummary: data.windowSummary ??
783
+ {
784
+ window: '',
785
+ deltasCount: 0,
786
+ netDelta: 0,
787
+ firstDeltaAt: null,
788
+ lastDeltaAt: null,
789
+ },
790
+ deltas: data.deltas ?? [],
791
+ verification: data.verification ??
792
+ {
793
+ proofRoot: null,
794
+ totalBalance: 0,
795
+ periodBalance: 0,
796
+ },
797
+ };
798
+ }
799
+ normalizeCheckpointResponse(data) {
800
+ return {
801
+ checkpointId: String(data.checkpointId ?? ''),
802
+ period: String(data.period ?? ''),
803
+ proofRoot: String(data.proofRoot ?? ''),
804
+ scope: data.scope,
805
+ mode: data.mode,
806
+ deltaCount: Number(data.deltaCount ?? 0),
807
+ selectedFingerprintCount: data.selectedFingerprintCount == null
808
+ ? undefined
809
+ : Number(data.selectedFingerprintCount),
810
+ eligibleFingerprintCount: data.eligibleFingerprintCount == null
811
+ ? undefined
812
+ : Number(data.eligibleFingerprintCount),
813
+ selectedFingerprints: Array.isArray(data.selectedFingerprints)
814
+ ? data.selectedFingerprints.map((value) => String(value))
815
+ : undefined,
816
+ fromIndex: Number(data.fromIndex ?? 0),
817
+ toIndex: Number(data.toIndex ?? 0),
818
+ customerId: String(data.customerId ?? ''),
819
+ status: toRecordStatus(data.status),
820
+ message: String(data.message ?? ''),
821
+ };
822
+ }
823
+ normalizeListCheckpointsResponse(data) {
824
+ const checkpoints = Array.isArray(data.checkpoints)
825
+ ? data.checkpoints.map((entry) => {
826
+ const item = entry;
827
+ return {
828
+ checkpointId: String(item.checkpointId ?? ''),
829
+ period: String(item.period ?? ''),
830
+ proofRoot: item.proofRoot == null ? null : String(item.proofRoot),
831
+ scope: item.scope,
832
+ mode: item.mode,
833
+ deltaCount: Number(item.deltaCount ?? 0),
834
+ selectedFingerprintCount: item.selectedFingerprintCount == null
835
+ ? undefined
836
+ : Number(item.selectedFingerprintCount),
837
+ eligibleFingerprintCount: item.eligibleFingerprintCount == null
838
+ ? undefined
839
+ : Number(item.eligibleFingerprintCount),
840
+ customerId: String(item.customerId ?? ''),
841
+ status: toRecordStatus(item.status),
842
+ verificationReference: item.verificationReference == null
843
+ ? null
844
+ : String(item.verificationReference),
845
+ verifiedAt: item.verifiedAt == null ? null : String(item.verifiedAt),
846
+ receivedAt: String(item.receivedAt ?? ''),
847
+ };
848
+ })
849
+ : [];
850
+ const pagination = (data.pagination ?? {});
851
+ return {
852
+ checkpoints,
853
+ pagination: {
854
+ total: Number(pagination.total ?? checkpoints.length),
855
+ limit: Number(pagination.limit ?? checkpoints.length),
856
+ offset: Number(pagination.offset ?? 0),
857
+ },
858
+ };
859
+ }
860
+ normalizeSequenceGapsResponse(data) {
861
+ const gaps = Array.isArray(data.gaps)
862
+ ? data.gaps.map((entry) => {
863
+ const item = entry;
864
+ return {
865
+ startSequence: String(item.startSequence ?? ''),
866
+ endSequence: String(item.endSequence ?? ''),
867
+ missingCount: String(item.missingCount ?? '0'),
868
+ };
869
+ })
870
+ : [];
871
+ return {
872
+ customerId: String(data.customerId ?? ''),
873
+ customerReference: String(data.customerReference ?? ''),
874
+ customerPartition: String(data.customerPartition ?? ''),
875
+ nextSequence: String(data.nextSequence ?? '0'),
876
+ rangeStart: String(data.rangeStart ?? '0'),
877
+ rangeEnd: String(data.rangeEnd ?? '0'),
878
+ gaps,
879
+ hasGaps: Boolean(data.hasGaps),
880
+ missingRanges: Number(data.missingRanges ?? gaps.length),
881
+ missingCountInPage: String(data.missingCountInPage ?? '0'),
882
+ truncated: Boolean(data.truncated),
883
+ };
884
+ }
885
+ normalizeBulkEmitResponse(data) {
886
+ const results = Array.isArray(data.results)
887
+ ? data.results.map((entry) => {
888
+ const item = entry;
889
+ return {
890
+ index: Number(item.index ?? 0),
891
+ customerId: String(item.customerId ?? ''),
892
+ delta: Number(item.delta ?? 0),
893
+ recordId: item.recordId == null ? undefined : String(item.recordId),
894
+ itemHash: item.itemHash == null ? undefined : String(item.itemHash),
895
+ receiptId: item.receiptId == null ? undefined : String(item.receiptId),
896
+ referenceId: item.referenceId == null ? undefined : String(item.referenceId),
897
+ sequenceNumber: item.sequenceNumber == null
898
+ ? undefined
899
+ : String(item.sequenceNumber),
900
+ idempotent: item.idempotent == null ? undefined : Boolean(item.idempotent),
901
+ status: String(item.status ?? ''),
902
+ error: item.error == null ? undefined : String(item.error),
903
+ };
904
+ })
905
+ : [];
906
+ return {
907
+ totalQueued: Number(data.totalQueued ?? results.length),
908
+ totalFailed: Number(data.totalFailed ?? 0),
909
+ results,
910
+ _efficiency: data._efficiency,
911
+ };
912
+ }
913
+ normalizeListCustomersResponse(data) {
914
+ const customers = Array.isArray(data.customers)
915
+ ? data.customers.map((entry) => {
916
+ const item = entry;
917
+ return {
918
+ customerId: String(item.customerId ?? ''),
919
+ customerReference: String(item.customerReference ?? ''),
920
+ totalDeltas: Number(item.totalDeltas ?? 0),
921
+ firstDeltaAt: item.firstDeltaAt == null ? null : String(item.firstDeltaAt),
922
+ lastDeltaAt: item.lastDeltaAt == null ? null : String(item.lastDeltaAt),
923
+ };
924
+ })
925
+ : [];
926
+ const pagination = (data.pagination ?? {});
927
+ return {
928
+ customers,
929
+ pagination: {
930
+ total: Number(pagination.total ?? customers.length),
931
+ page: Number(pagination.page ?? 1),
932
+ limit: Number(pagination.limit ?? customers.length),
933
+ totalPages: Number(pagination.totalPages ?? 1),
934
+ },
935
+ };
936
+ }
937
+ normalizeCustomerDetailResponse(data) {
938
+ return {
939
+ customerId: String(data.customerId ?? ''),
940
+ customerReference: String(data.customerReference ?? ''),
941
+ totalDeltas: Number(data.totalDeltas ?? 0),
942
+ firstDeltaAt: data.firstDeltaAt == null ? null : String(data.firstDeltaAt),
943
+ lastDeltaAt: data.lastDeltaAt == null ? null : String(data.lastDeltaAt),
944
+ customerPartition: String(data.customerPartition ?? ''),
945
+ computedBalance: Number(data.computedBalance ?? 0),
946
+ latestCheckpoint: data.latestCheckpoint == null ? null : String(data.latestCheckpoint),
947
+ privacyNote: String(data.privacyNote ?? ''),
948
+ recentActivity: data.recentActivity ??
949
+ {
950
+ items: [],
951
+ pagination: {
952
+ total: 0,
953
+ page: 1,
954
+ limit: 20,
955
+ totalPages: 1,
956
+ },
957
+ },
958
+ };
554
959
  }
555
960
  // -------------------------------------------------------------------------
556
961
  // Invoice Proof (deprecated — use receipt with period)