@fairmint/canton-node-sdk 0.0.238 → 0.0.240

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.
Files changed (177) hide show
  1. package/build/src/clients/ledger-json-api/LedgerJsonApiClient.generated.d.ts +51 -51
  2. package/build/src/clients/ledger-json-api/LedgerJsonApiClient.generated.d.ts.map +1 -1
  3. package/build/src/clients/ledger-json-api/LedgerJsonApiClient.generated.js +51 -51
  4. package/build/src/clients/ledger-json-api/LedgerJsonApiClient.generated.js.map +1 -1
  5. package/build/src/clients/ledger-json-api/operations/v2/commands/async/submit.d.ts +26 -26
  6. package/build/src/clients/ledger-json-api/operations/v2/commands/completions.d.ts +1 -2
  7. package/build/src/clients/ledger-json-api/operations/v2/commands/completions.d.ts.map +1 -1
  8. package/build/src/clients/ledger-json-api/operations/v2/commands/completions.js +2 -2
  9. package/build/src/clients/ledger-json-api/operations/v2/commands/completions.js.map +1 -1
  10. package/build/src/clients/ledger-json-api/operations/v2/commands/submit-and-wait-for-transaction.d.ts +26 -26
  11. package/build/src/clients/ledger-json-api/operations/v2/events/get-events-by-contract-id.d.ts.map +1 -1
  12. package/build/src/clients/ledger-json-api/operations/v2/events/get-events-by-contract-id.js +1 -0
  13. package/build/src/clients/ledger-json-api/operations/v2/events/get-events-by-contract-id.js.map +1 -1
  14. package/build/src/clients/ledger-json-api/operations/v2/interactive-submission/get-preferred-packages.d.ts.map +1 -1
  15. package/build/src/clients/ledger-json-api/operations/v2/interactive-submission/get-preferred-packages.js +1 -0
  16. package/build/src/clients/ledger-json-api/operations/v2/interactive-submission/get-preferred-packages.js.map +1 -1
  17. package/build/src/clients/ledger-json-api/operations/v2/interactive-submission/prepare.d.ts.map +1 -1
  18. package/build/src/clients/ledger-json-api/operations/v2/interactive-submission/prepare.js +1 -0
  19. package/build/src/clients/ledger-json-api/operations/v2/interactive-submission/prepare.js.map +1 -1
  20. package/build/src/clients/ledger-json-api/operations/v2/parties/aggregate-parties.d.ts +2 -2
  21. package/build/src/clients/ledger-json-api/operations/v2/parties/aggregate-parties.d.ts.map +1 -1
  22. package/build/src/clients/ledger-json-api/operations/v2/parties/aggregate-parties.js +25 -8
  23. package/build/src/clients/ledger-json-api/operations/v2/parties/aggregate-parties.js.map +1 -1
  24. package/build/src/clients/ledger-json-api/operations/v2/parties/external/generate-topology.d.ts.map +1 -1
  25. package/build/src/clients/ledger-json-api/operations/v2/parties/external/generate-topology.js +1 -0
  26. package/build/src/clients/ledger-json-api/operations/v2/parties/external/generate-topology.js.map +1 -1
  27. package/build/src/clients/ledger-json-api/operations/v2/parties/get.d.ts +2 -2
  28. package/build/src/clients/ledger-json-api/operations/v2/parties/get.d.ts.map +1 -1
  29. package/build/src/clients/ledger-json-api/operations/v2/parties/get.js +3 -2
  30. package/build/src/clients/ledger-json-api/operations/v2/parties/get.js.map +1 -1
  31. package/build/src/clients/ledger-json-api/operations/v2/parties/list.d.ts +2 -2
  32. package/build/src/clients/ledger-json-api/operations/v2/parties/list.d.ts.map +1 -1
  33. package/build/src/clients/ledger-json-api/operations/v2/parties/list.js +3 -2
  34. package/build/src/clients/ledger-json-api/operations/v2/parties/list.js.map +1 -1
  35. package/build/src/clients/ledger-json-api/operations/v2/updates/get-transaction-by-id.d.ts.map +1 -1
  36. package/build/src/clients/ledger-json-api/operations/v2/updates/get-transaction-by-id.js +1 -0
  37. package/build/src/clients/ledger-json-api/operations/v2/updates/get-transaction-by-id.js.map +1 -1
  38. package/build/src/clients/ledger-json-api/operations/v2/updates/get-transaction-by-offset.d.ts.map +1 -1
  39. package/build/src/clients/ledger-json-api/operations/v2/updates/get-transaction-by-offset.js +1 -0
  40. package/build/src/clients/ledger-json-api/operations/v2/updates/get-transaction-by-offset.js.map +1 -1
  41. package/build/src/clients/ledger-json-api/operations/v2/updates/get-update-by-id.d.ts.map +1 -1
  42. package/build/src/clients/ledger-json-api/operations/v2/updates/get-update-by-id.js +1 -0
  43. package/build/src/clients/ledger-json-api/operations/v2/updates/get-update-by-id.js.map +1 -1
  44. package/build/src/clients/ledger-json-api/operations/v2/updates/get-update-by-offset.d.ts.map +1 -1
  45. package/build/src/clients/ledger-json-api/operations/v2/updates/get-update-by-offset.js +1 -0
  46. package/build/src/clients/ledger-json-api/operations/v2/updates/get-update-by-offset.js.map +1 -1
  47. package/build/src/clients/scan-api/ScanApiClient.d.ts +7 -6
  48. package/build/src/clients/scan-api/ScanApiClient.d.ts.map +1 -1
  49. package/build/src/clients/scan-api/ScanApiClient.generated.d.ts +62 -62
  50. package/build/src/clients/scan-api/ScanApiClient.generated.d.ts.map +1 -1
  51. package/build/src/clients/scan-api/ScanApiClient.generated.js +62 -62
  52. package/build/src/clients/scan-api/ScanApiClient.generated.js.map +1 -1
  53. package/build/src/clients/scan-api/ScanApiClient.js +75 -35
  54. package/build/src/clients/scan-api/ScanApiClient.js.map +1 -1
  55. package/build/src/clients/scan-api/operations/v0/registry/allocation-instruction/v1/get-allocation-factory-from-registry.d.ts.map +1 -1
  56. package/build/src/clients/scan-api/operations/v0/registry/allocation-instruction/v1/get-allocation-factory-from-registry.js +1 -0
  57. package/build/src/clients/scan-api/operations/v0/registry/allocation-instruction/v1/get-allocation-factory-from-registry.js.map +1 -1
  58. package/build/src/clients/scan-api/operations/v0/registry/allocation-instruction/v2/get-allocation-factory-v2-from-registry.d.ts.map +1 -1
  59. package/build/src/clients/scan-api/operations/v0/registry/allocation-instruction/v2/get-allocation-factory-v2-from-registry.js +4 -9
  60. package/build/src/clients/scan-api/operations/v0/registry/allocation-instruction/v2/get-allocation-factory-v2-from-registry.js.map +1 -1
  61. package/build/src/clients/scan-api/operations/v0/scan.d.ts.map +1 -1
  62. package/build/src/clients/scan-api/operations/v0/scan.js +17 -0
  63. package/build/src/clients/scan-api/operations/v0/scan.js.map +1 -1
  64. package/build/src/clients/validator-api/ValidatorApiClient.generated.d.ts +61 -61
  65. package/build/src/clients/validator-api/ValidatorApiClient.generated.d.ts.map +1 -1
  66. package/build/src/clients/validator-api/ValidatorApiClient.generated.js +61 -61
  67. package/build/src/clients/validator-api/ValidatorApiClient.generated.js.map +1 -1
  68. package/build/src/clients/validator-api/operations/v0/admin/generate-external-party-topology.d.ts.map +1 -1
  69. package/build/src/clients/validator-api/operations/v0/admin/generate-external-party-topology.js +1 -0
  70. package/build/src/clients/validator-api/operations/v0/admin/generate-external-party-topology.js.map +1 -1
  71. package/build/src/clients/validator-api/operations/v0/admin/prepare-accept-external-party-setup-proposal.d.ts.map +1 -1
  72. package/build/src/clients/validator-api/operations/v0/admin/prepare-accept-external-party-setup-proposal.js +1 -0
  73. package/build/src/clients/validator-api/operations/v0/admin/prepare-accept-external-party-setup-proposal.js.map +1 -1
  74. package/build/src/clients/validator-api/operations/v0/admin/prepare-transfer-preapproval-send.d.ts.map +1 -1
  75. package/build/src/clients/validator-api/operations/v0/admin/prepare-transfer-preapproval-send.js +1 -0
  76. package/build/src/clients/validator-api/operations/v0/admin/prepare-transfer-preapproval-send.js.map +1 -1
  77. package/build/src/clients/validator-api/operations/v0/ans/get-rules.d.ts.map +1 -1
  78. package/build/src/clients/validator-api/operations/v0/ans/get-rules.js +1 -0
  79. package/build/src/clients/validator-api/operations/v0/ans/get-rules.js.map +1 -1
  80. package/build/src/clients/validator-api/operations/v0/scan-proxy/get-member-traffic-status.d.ts +2 -1
  81. package/build/src/clients/validator-api/operations/v0/scan-proxy/get-member-traffic-status.d.ts.map +1 -1
  82. package/build/src/clients/validator-api/operations/v0/scan-proxy/get-member-traffic-status.js +47 -9
  83. package/build/src/clients/validator-api/operations/v0/scan-proxy/get-member-traffic-status.js.map +1 -1
  84. package/build/src/clients/validator-api/operations/v0/scan-proxy/registry/allocation-instruction/v1/get-allocation-factory.d.ts.map +1 -1
  85. package/build/src/clients/validator-api/operations/v0/scan-proxy/registry/allocation-instruction/v1/get-allocation-factory.js +1 -0
  86. package/build/src/clients/validator-api/operations/v0/scan-proxy/registry/allocation-instruction/v1/get-allocation-factory.js.map +1 -1
  87. package/build/src/clients/validator-api/operations/v0/scan-proxy/registry/allocations/v1/get-allocation-cancel-context.d.ts.map +1 -1
  88. package/build/src/clients/validator-api/operations/v0/scan-proxy/registry/allocations/v1/get-allocation-cancel-context.js +1 -0
  89. package/build/src/clients/validator-api/operations/v0/scan-proxy/registry/allocations/v1/get-allocation-cancel-context.js.map +1 -1
  90. package/build/src/clients/validator-api/operations/v0/scan-proxy/registry/allocations/v1/get-allocation-transfer-context.d.ts.map +1 -1
  91. package/build/src/clients/validator-api/operations/v0/scan-proxy/registry/allocations/v1/get-allocation-transfer-context.js +1 -0
  92. package/build/src/clients/validator-api/operations/v0/scan-proxy/registry/allocations/v1/get-allocation-transfer-context.js.map +1 -1
  93. package/build/src/clients/validator-api/operations/v0/scan-proxy/registry/allocations/v1/get-allocation-withdraw-context.d.ts.map +1 -1
  94. package/build/src/clients/validator-api/operations/v0/scan-proxy/registry/allocations/v1/get-allocation-withdraw-context.js +1 -0
  95. package/build/src/clients/validator-api/operations/v0/scan-proxy/registry/allocations/v1/get-allocation-withdraw-context.js.map +1 -1
  96. package/build/src/clients/validator-api/operations/v0/scan-proxy/registry/transfer-instruction/v1/get-transfer-factory.d.ts.map +1 -1
  97. package/build/src/clients/validator-api/operations/v0/scan-proxy/registry/transfer-instruction/v1/get-transfer-factory.js +1 -0
  98. package/build/src/clients/validator-api/operations/v0/scan-proxy/registry/transfer-instruction/v1/get-transfer-factory.js.map +1 -1
  99. package/build/src/clients/validator-api/operations/v0/scan-proxy/registry/transfer-instruction/v1/get-transfer-instruction-accept-context.d.ts.map +1 -1
  100. package/build/src/clients/validator-api/operations/v0/scan-proxy/registry/transfer-instruction/v1/get-transfer-instruction-accept-context.js +1 -0
  101. package/build/src/clients/validator-api/operations/v0/scan-proxy/registry/transfer-instruction/v1/get-transfer-instruction-accept-context.js.map +1 -1
  102. package/build/src/clients/validator-api/operations/v0/scan-proxy/registry/transfer-instruction/v1/get-transfer-instruction-reject-context.d.ts.map +1 -1
  103. package/build/src/clients/validator-api/operations/v0/scan-proxy/registry/transfer-instruction/v1/get-transfer-instruction-reject-context.js +1 -0
  104. package/build/src/clients/validator-api/operations/v0/scan-proxy/registry/transfer-instruction/v1/get-transfer-instruction-reject-context.js.map +1 -1
  105. package/build/src/clients/validator-api/operations/v0/scan-proxy/registry/transfer-instruction/v1/get-transfer-instruction-withdraw-context.d.ts.map +1 -1
  106. package/build/src/clients/validator-api/operations/v0/scan-proxy/registry/transfer-instruction/v1/get-transfer-instruction-withdraw-context.js +1 -0
  107. package/build/src/clients/validator-api/operations/v0/scan-proxy/registry/transfer-instruction/v1/get-transfer-instruction-withdraw-context.js.map +1 -1
  108. package/build/src/clients/validator-api/operations/v0/wallet/buy-traffic-requests/get-status.d.ts.map +1 -1
  109. package/build/src/clients/validator-api/operations/v0/wallet/buy-traffic-requests/get-status.js +1 -0
  110. package/build/src/clients/validator-api/operations/v0/wallet/buy-traffic-requests/get-status.js.map +1 -1
  111. package/build/src/clients/validator-api/operations/v0/wallet/transfer-offers/get-status.d.ts.map +1 -1
  112. package/build/src/clients/validator-api/operations/v0/wallet/transfer-offers/get-status.js +1 -0
  113. package/build/src/clients/validator-api/operations/v0/wallet/transfer-offers/get-status.js.map +1 -1
  114. package/build/src/core/BaseClient.d.ts +5 -4
  115. package/build/src/core/BaseClient.d.ts.map +1 -1
  116. package/build/src/core/BaseClient.js +8 -8
  117. package/build/src/core/BaseClient.js.map +1 -1
  118. package/build/src/core/errors.d.ts +26 -1
  119. package/build/src/core/errors.d.ts.map +1 -1
  120. package/build/src/core/errors.js +30 -9
  121. package/build/src/core/errors.js.map +1 -1
  122. package/build/src/core/http/HttpClient.d.ts +41 -31
  123. package/build/src/core/http/HttpClient.d.ts.map +1 -1
  124. package/build/src/core/http/HttpClient.js +391 -118
  125. package/build/src/core/http/HttpClient.js.map +1 -1
  126. package/build/src/core/http/abort.d.ts +9 -0
  127. package/build/src/core/http/abort.d.ts.map +1 -0
  128. package/build/src/core/http/abort.js +54 -0
  129. package/build/src/core/http/abort.js.map +1 -0
  130. package/build/src/core/http/index.d.ts +1 -0
  131. package/build/src/core/http/index.d.ts.map +1 -1
  132. package/build/src/core/http/index.js +1 -0
  133. package/build/src/core/http/index.js.map +1 -1
  134. package/build/src/core/http/request-retry.d.ts +130 -0
  135. package/build/src/core/http/request-retry.d.ts.map +1 -0
  136. package/build/src/core/http/request-retry.js +14 -0
  137. package/build/src/core/http/request-retry.js.map +1 -0
  138. package/build/src/core/http/request-value.d.ts +5 -0
  139. package/build/src/core/http/request-value.d.ts.map +1 -0
  140. package/build/src/core/http/request-value.js +63 -0
  141. package/build/src/core/http/request-value.js.map +1 -0
  142. package/build/src/core/operations/ApiOperation.d.ts +7 -5
  143. package/build/src/core/operations/ApiOperation.d.ts.map +1 -1
  144. package/build/src/core/operations/ApiOperation.js +19 -11
  145. package/build/src/core/operations/ApiOperation.js.map +1 -1
  146. package/build/src/core/operations/ApiOperationFactory.d.ts +20 -4
  147. package/build/src/core/operations/ApiOperationFactory.d.ts.map +1 -1
  148. package/build/src/core/operations/ApiOperationFactory.js +48 -8
  149. package/build/src/core/operations/ApiOperationFactory.js.map +1 -1
  150. package/build/src/core/operations/index.d.ts +2 -0
  151. package/build/src/core/operations/index.d.ts.map +1 -1
  152. package/build/src/core/operations/index.js +2 -0
  153. package/build/src/core/operations/index.js.map +1 -1
  154. package/build/src/core/operations/operation-execute-options.d.ts +68 -0
  155. package/build/src/core/operations/operation-execute-options.d.ts.map +1 -0
  156. package/build/src/core/operations/operation-execute-options.js +36 -0
  157. package/build/src/core/operations/operation-execute-options.js.map +1 -0
  158. package/build/src/core/operations/operation-request-options.d.ts +15 -0
  159. package/build/src/core/operations/operation-request-options.d.ts.map +1 -0
  160. package/build/src/core/operations/operation-request-options.js +106 -0
  161. package/build/src/core/operations/operation-request-options.js.map +1 -0
  162. package/build/src/utils/mining/mining-rounds.d.ts +4 -3
  163. package/build/src/utils/mining/mining-rounds.d.ts.map +1 -1
  164. package/build/src/utils/mining/mining-rounds.js +4 -4
  165. package/build/src/utils/mining/mining-rounds.js.map +1 -1
  166. package/build/src/utils/token-standard/v2/allocation.d.ts.map +1 -1
  167. package/build/src/utils/token-standard/v2/allocation.js +5 -10
  168. package/build/src/utils/token-standard/v2/allocation.js.map +1 -1
  169. package/build/src/utils/token-standard/v2/holdings.d.ts +70 -0
  170. package/build/src/utils/token-standard/v2/holdings.d.ts.map +1 -0
  171. package/build/src/utils/token-standard/v2/holdings.js +458 -0
  172. package/build/src/utils/token-standard/v2/holdings.js.map +1 -0
  173. package/build/src/utils/token-standard/v2/index.d.ts +1 -0
  174. package/build/src/utils/token-standard/v2/index.d.ts.map +1 -1
  175. package/build/src/utils/token-standard/v2/index.js +1 -0
  176. package/build/src/utils/token-standard/v2/index.js.map +1 -1
  177. package/package.json +1 -1
@@ -6,7 +6,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.HttpClient = void 0;
7
7
  const axios_1 = __importDefault(require("axios"));
8
8
  const errors_1 = require("../errors");
9
- /** Handles HTTP requests with authentication, logging, and error handling */
9
+ const abort_1 = require("./abort");
10
+ const request_retry_1 = require("./request-retry");
11
+ const request_value_1 = require("./request-value");
12
+ /** Handles HTTP requests with authentication, logging, cancellation, and explicit retry safety. */
10
13
  class HttpClient {
11
14
  constructor(logger, bearerTokenProvider) {
12
15
  this.retryConfig = { maxRetries: 3, delayMs: 6000 };
@@ -14,90 +17,286 @@ class HttpClient {
14
17
  this.bearerTokenProvider = bearerTokenProvider;
15
18
  this.logger = logger;
16
19
  }
20
+ /** Configure the default retry policy used only by requests classified as semantic reads. */
17
21
  setRetryConfig(config) {
18
- this.retryConfig = config;
22
+ if (!Number.isInteger(config.maxRetries) || config.maxRetries < 0) {
23
+ throw new errors_1.ConfigurationError('HTTP maxRetries must be a non-negative integer');
24
+ }
25
+ if (!Number.isFinite(config.delayMs) || config.delayMs < 0) {
26
+ throw new errors_1.ConfigurationError('HTTP retry delayMs must be a non-negative finite number');
27
+ }
28
+ this.retryConfig = { ...config };
19
29
  }
20
- async makeGetRequest(url, config = {}, _retryCount = 0) {
21
- try {
22
- const headers = await this.buildHeaders(config);
23
- const response = await this.axiosInstance.get(url, { headers });
24
- await this.logRequestResponse(url, { method: 'GET', headers }, response.data);
25
- return response.data;
30
+ async makeGetRequest(url, config = {}, options = {}) {
31
+ return this.makeRequest('GET', url, undefined, config, options);
32
+ }
33
+ async makePostRequest(url, data, config = {}, options = {}) {
34
+ return this.makeRequest('POST', url, data, config, options);
35
+ }
36
+ async makeDeleteRequest(url, config = {}, options = {}) {
37
+ return this.makeRequest('DELETE', url, undefined, config, options);
38
+ }
39
+ async makePatchRequest(url, data, config = {}, options = {}) {
40
+ return this.makeRequest('PATCH', url, data, config, options);
41
+ }
42
+ async makeRequest(method, url, initialBody, config, options) {
43
+ // Snapshot every caller-owned option before the first asynchronous boundary. In particular, never let replacing an
44
+ // options signal or retry object while a token/hook is pending change the in-flight request contract.
45
+ const requestOptions = (0, request_retry_1.snapshotHttpRequestOptions)(options);
46
+ const requestConfig = Object.freeze({ ...config });
47
+ const { signal, retry: requestedRetry, requestSemantics: requestedSemantics, resolveReadAttemptUrl, } = requestOptions;
48
+ const requestSemantics = requestedSemantics ?? (method === 'GET' ? 'read' : 'mutation');
49
+ if (method === 'GET' && requestSemantics !== 'read') {
50
+ throw new errors_1.ConfigurationError('HTTP GET requests must use read semantics');
26
51
  }
27
- catch (error) {
28
- // Attempt up to 3 retries for transient errors
29
- if (_retryCount < this.retryConfig.maxRetries && this.isRetryableError(error)) {
30
- await this.logRequestResponse(url, { method: 'GET', retry: _retryCount + 1 }, `Retrying after error (attempt ${_retryCount + 1}/${this.retryConfig.maxRetries}): ${axios_1.default.isAxiosError(error) ? (error.response?.status ?? 'network error') : String(error)}`);
31
- await this.sleep(this.retryConfig.delayMs);
32
- return this.makeGetRequest(url, config, _retryCount + 1);
52
+ if (resolveReadAttemptUrl !== undefined && requestSemantics !== 'read') {
53
+ throw new errors_1.ConfigurationError('Endpoint failover is only supported for semantic reads');
54
+ }
55
+ const strategy = this.resolveRetryStrategy(requestSemantics, requestedRetry);
56
+ const maxAttempts = strategy.kind === 'none' ? 1 : strategy.maxAttempts;
57
+ this.validateMaxAttempts(maxAttempts);
58
+ const requiresImmutableBody = strategy.kind !== 'none';
59
+ let currentBody = this.cloneBody(initialBody);
60
+ let lastFailure;
61
+ const failedAttempts = [];
62
+ for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
63
+ let attemptContext;
64
+ let attemptUrl = url;
65
+ let identifier;
66
+ try {
67
+ (0, abort_1.throwIfAborted)(signal);
68
+ if (attempt > 1 && strategy.kind === 'derived-body') {
69
+ const priorFailure = lastFailure;
70
+ if (!priorFailure) {
71
+ throw new errors_1.ConfigurationError('Cannot derive a retry body without a failed request attempt');
72
+ }
73
+ const derivedBody = await (0, abort_1.awaitWithAbort)(async () => {
74
+ const body = await strategy.deriveBody(priorFailure.retryContext);
75
+ return body;
76
+ }, signal);
77
+ currentBody = this.cloneDerivedBody(derivedBody);
78
+ }
79
+ attemptContext = this.createAttemptContext(attempt, currentBody, failedAttempts, signal, requiresImmutableBody);
80
+ if (resolveReadAttemptUrl !== undefined) {
81
+ const urlContext = Object.freeze({
82
+ ...attemptContext,
83
+ originalUrl: url,
84
+ });
85
+ const resolvedAttemptUrl = await (0, abort_1.awaitWithAbort)(async () => resolveReadAttemptUrl(urlContext), signal);
86
+ if (typeof resolvedAttemptUrl !== 'string' || resolvedAttemptUrl.length === 0) {
87
+ throw new errors_1.ConfigurationError('Read endpoint resolver must return a non-empty URL');
88
+ }
89
+ attemptUrl = resolvedAttemptUrl;
90
+ }
91
+ const getAttemptIdentifier = strategy.kind === 'none' ? undefined : strategy.getAttemptIdentifier;
92
+ identifier = getAttemptIdentifier
93
+ ? this.redactAttemptIdentifier(await (0, abort_1.awaitWithAbort)(async () => {
94
+ const attemptIdentifier = await getAttemptIdentifier(attemptContext);
95
+ return attemptIdentifier;
96
+ }, signal))
97
+ : undefined;
98
+ const beforeAttempt = strategy.kind === 'none' ? undefined : strategy.beforeAttempt;
99
+ if (beforeAttempt) {
100
+ await (0, abort_1.awaitWithAbort)(async () => {
101
+ await beforeAttempt(attemptContext);
102
+ }, signal);
103
+ }
104
+ (0, abort_1.throwIfAborted)(signal);
105
+ }
106
+ catch (preDispatchError) {
107
+ throw this.preservePriorAmbiguity(requestSemantics, method, attemptUrl, this.normalizeCallbackError(preDispatchError), failedAttempts);
33
108
  }
34
- // Log the error response before throwing
35
- if (axios_1.default.isAxiosError(error)) {
36
- await this.logRequestResponse(url, { method: 'GET' }, error.response?.data ?? error.message);
109
+ let dispatched = false;
110
+ try {
111
+ const headers = await (0, abort_1.awaitWithAbort)(async () => {
112
+ const builtHeaders = await this.buildHeaders(requestConfig);
113
+ return builtHeaders;
114
+ }, signal);
115
+ (0, abort_1.throwIfAborted)(signal);
116
+ const requestBody = this.cloneBody(currentBody);
117
+ dispatched = true;
118
+ const response = await this.dispatchRequest(method, attemptUrl, requestBody, headers, signal);
119
+ this.observeLog(attemptUrl, method === 'GET' || method === 'DELETE' ? { method, headers } : { method, headers, data: requestBody }, response);
120
+ return response;
121
+ }
122
+ catch (rawError) {
123
+ if (this.isCanceledError(rawError) || (0, abort_1.isAbortError)(rawError)) {
124
+ const abortError = (0, abort_1.createAbortError)(signal, rawError);
125
+ if (requestSemantics === 'read' || (!dispatched && !this.hasAmbiguousAttempt(failedAttempts))) {
126
+ throw abortError;
127
+ }
128
+ if (!dispatched) {
129
+ throw this.toTerminalError(requestSemantics, method, attemptUrl, abortError, failedAttempts);
130
+ }
131
+ const canceledAttempt = Object.freeze({
132
+ attempt,
133
+ ...(identifier !== undefined ? { identifier } : {}),
134
+ errorClassification: 'ambiguous-mutation-outcome',
135
+ outcomeCertainty: 'ambiguous',
136
+ retryable: false,
137
+ });
138
+ throw this.toTerminalError(requestSemantics, method, attemptUrl, abortError, [
139
+ ...failedAttempts,
140
+ canceledAttempt,
141
+ ]);
142
+ }
143
+ const error = this.handleRequestError(rawError);
144
+ const retryable = this.isRetryableError(rawError);
145
+ const { errorClassification, outcomeCertainty } = this.classifyRequestError(requestSemantics, error, dispatched, retryable);
146
+ const summary = Object.freeze({
147
+ attempt,
148
+ ...(identifier !== undefined ? { identifier } : {}),
149
+ errorClassification,
150
+ outcomeCertainty,
151
+ retryable,
152
+ });
153
+ const retryContext = Object.freeze({
154
+ ...attemptContext,
155
+ error,
156
+ errorClassification,
157
+ outcomeCertainty,
158
+ retryable,
159
+ });
160
+ const failure = {
161
+ error,
162
+ retryContext,
163
+ summary,
164
+ };
165
+ lastFailure = failure;
166
+ this.logFailedRequest(method, attemptUrl, currentBody, rawError);
167
+ let retryRequest;
168
+ try {
169
+ retryRequest = await this.shouldRetry(strategy, failure, attempt, maxAttempts, requestSemantics, signal);
170
+ }
171
+ catch (retryHookError) {
172
+ throw this.toTerminalError(requestSemantics, method, attemptUrl, this.normalizeCallbackError(retryHookError), [...failedAttempts, summary]);
173
+ }
174
+ if (retryRequest) {
175
+ failedAttempts.push(summary);
176
+ this.logRetry(method, attemptUrl, attempt, maxAttempts, summary, rawError);
177
+ try {
178
+ await this.waitForRetry(strategy, retryContext, signal);
179
+ }
180
+ catch (backoffError) {
181
+ const normalizedBackoffError = this.normalizeCallbackError(backoffError);
182
+ throw this.toTerminalError(requestSemantics, method, attemptUrl, normalizedBackoffError, failedAttempts);
183
+ }
184
+ continue;
185
+ }
186
+ throw this.toTerminalError(requestSemantics, method, attemptUrl, error, [...failedAttempts, summary]);
37
187
  }
38
- throw this.handleRequestError(error);
39
188
  }
189
+ // The loop either returns or throws for every attempt.
190
+ throw new errors_1.ConfigurationError('HTTP retry loop completed without a response or error');
40
191
  }
41
- async makePostRequest(url, data, config = {}, _retryCount = 0) {
42
- try {
43
- const headers = await this.buildHeaders(config);
44
- const response = await this.axiosInstance.post(url, data, { headers });
45
- await this.logRequestResponse(url, { method: 'POST', headers, data }, response.data);
46
- return response.data;
47
- }
48
- catch (error) {
49
- if (_retryCount < this.retryConfig.maxRetries && this.isRetryableError(error)) {
50
- await this.logRequestResponse(url, { method: 'POST', retry: _retryCount + 1, data }, `Retrying after error (attempt ${_retryCount + 1}/${this.retryConfig.maxRetries}): ${axios_1.default.isAxiosError(error) ? (error.response?.status ?? 'network error') : String(error)}`);
51
- await this.sleep(this.retryConfig.delayMs);
52
- const retryData = this.prepareDataForRetry(data);
53
- return this.makePostRequest(url, retryData, config, _retryCount + 1);
54
- }
55
- // Log the error response before throwing
56
- if (axios_1.default.isAxiosError(error)) {
57
- await this.logRequestResponse(url, { method: 'POST', data }, error.response?.data ?? error.message);
192
+ resolveRetryStrategy(requestSemantics, retry) {
193
+ if (retry) {
194
+ if (retry.kind === 'none')
195
+ return Object.freeze({ kind: 'none' });
196
+ if (retry.kind === 'exact-body')
197
+ return Object.freeze({ ...retry });
198
+ if (typeof retry.deriveBody !== 'function') {
199
+ throw new errors_1.ConfigurationError('HTTP derived-body retry requires a deriveBody function');
58
200
  }
59
- throw this.handleRequestError(error);
201
+ return Object.freeze({ ...retry });
202
+ }
203
+ // Semantic reads retain transient retry behavior regardless of HTTP verb. Mutations must explicitly opt in because
204
+ // their server outcome may be unknown after a response is lost.
205
+ if (requestSemantics === 'read') {
206
+ return Object.freeze({
207
+ kind: 'exact-body',
208
+ maxAttempts: this.retryConfig.maxRetries + 1,
209
+ backoffMs: this.retryConfig.delayMs,
210
+ });
211
+ }
212
+ return Object.freeze({ kind: 'none' });
213
+ }
214
+ validateMaxAttempts(maxAttempts) {
215
+ if (!Number.isInteger(maxAttempts) || maxAttempts < 1) {
216
+ throw new errors_1.ConfigurationError('HTTP retry maxAttempts must be a positive integer');
217
+ }
218
+ }
219
+ async shouldRetry(strategy, failure, attempt, maxAttempts, requestSemantics, signal) {
220
+ if (strategy.kind === 'none' || attempt >= maxAttempts)
221
+ return false;
222
+ const { shouldRetry } = strategy;
223
+ if (shouldRetry) {
224
+ return (0, abort_1.awaitWithAbort)(async () => {
225
+ const retry = await shouldRetry(failure.retryContext);
226
+ return retry;
227
+ }, signal);
60
228
  }
229
+ if (requestSemantics === 'read')
230
+ return failure.summary.retryable;
231
+ return failure.summary.outcomeCertainty !== 'ambiguous' && failure.summary.retryable;
61
232
  }
62
- async makeDeleteRequest(url, config = {}, _retryCount = 0) {
233
+ async waitForRetry(strategy, context, signal) {
234
+ if (strategy.kind === 'none')
235
+ return;
236
+ const configuredBackoff = strategy.backoffMs ?? this.retryConfig.delayMs;
237
+ const delayMs = typeof configuredBackoff === 'function'
238
+ ? await (0, abort_1.awaitWithAbort)(async () => {
239
+ const backoff = await configuredBackoff(context);
240
+ return backoff;
241
+ }, signal)
242
+ : configuredBackoff;
243
+ if (!Number.isFinite(delayMs) || delayMs < 0) {
244
+ throw new errors_1.ConfigurationError('HTTP retry backoffMs must resolve to a non-negative finite number');
245
+ }
246
+ if (delayMs === 0) {
247
+ (0, abort_1.throwIfAborted)(signal);
248
+ return;
249
+ }
250
+ await this.abortableSleep(delayMs, signal);
251
+ }
252
+ createAttemptContext(attempt, body, previousAttempts, signal, immutableBody) {
253
+ const context = {
254
+ attempt,
255
+ body: immutableBody ? this.createReadonlyBody(body) : body,
256
+ previousAttempts: Object.freeze([...previousAttempts]),
257
+ ...(signal !== undefined ? { signal } : {}),
258
+ };
259
+ return Object.freeze(context);
260
+ }
261
+ createReadonlyBody(body) {
262
+ const cloned = this.cloneBody(body);
263
+ return (0, request_value_1.deepFreezeRequestValue)(cloned);
264
+ }
265
+ cloneBody(body) {
63
266
  try {
64
- const headers = await this.buildHeaders(config);
65
- const response = await this.axiosInstance.delete(url, { headers });
66
- await this.logRequestResponse(url, { method: 'DELETE', headers }, response.data);
67
- return response.data;
267
+ return (0, request_value_1.cloneRequestValue)(body);
68
268
  }
69
269
  catch (error) {
70
- if (_retryCount < this.retryConfig.maxRetries && this.isRetryableError(error)) {
71
- await this.logRequestResponse(url, { method: 'DELETE', retry: _retryCount + 1 }, `Retrying after error (attempt ${_retryCount + 1}/${this.retryConfig.maxRetries}): ${axios_1.default.isAxiosError(error) ? (error.response?.status ?? 'network error') : String(error)}`);
72
- await this.sleep(this.retryConfig.delayMs);
73
- return this.makeDeleteRequest(url, config, _retryCount + 1);
74
- }
75
- // Log the error response before throwing
76
- if (axios_1.default.isAxiosError(error)) {
77
- await this.logRequestResponse(url, { method: 'DELETE' }, error.response?.data ?? error.message);
78
- }
79
- throw this.handleRequestError(error);
270
+ throw new errors_1.ConfigurationError('HTTP request bodies must be structured-cloneable', {
271
+ cause: error instanceof Error ? error.message : 'unknown clone failure',
272
+ });
80
273
  }
81
274
  }
82
- async makePatchRequest(url, data, config = {}, _retryCount = 0) {
275
+ /** Convert a deeply readonly callback result back into an isolated mutable transport snapshot. */
276
+ cloneDerivedBody(body) {
83
277
  try {
84
- const headers = await this.buildHeaders(config);
85
- const response = await this.axiosInstance.patch(url, data, { headers });
86
- await this.logRequestResponse(url, { method: 'PATCH', headers, data }, response.data);
87
- return response.data;
278
+ return (0, request_value_1.cloneRequestValue)(body);
88
279
  }
89
280
  catch (error) {
90
- if (_retryCount < this.retryConfig.maxRetries && this.isRetryableError(error)) {
91
- await this.logRequestResponse(url, { method: 'PATCH', retry: _retryCount + 1, data }, `Retrying after error (attempt ${_retryCount + 1}/${this.retryConfig.maxRetries}): ${axios_1.default.isAxiosError(error) ? (error.response?.status ?? 'network error') : String(error)}`);
92
- await this.sleep(this.retryConfig.delayMs);
93
- const retryData = this.prepareDataForRetry(data);
94
- return this.makePatchRequest(url, retryData, config, _retryCount + 1);
95
- }
96
- // Log the error response before throwing
97
- if (axios_1.default.isAxiosError(error)) {
98
- await this.logRequestResponse(url, { method: 'PATCH', data }, error.response?.data ?? error.message);
99
- }
100
- throw this.handleRequestError(error);
281
+ throw new errors_1.ConfigurationError('Derived HTTP request bodies must be structured-cloneable', {
282
+ cause: error instanceof Error ? error.message : 'unknown clone failure',
283
+ });
284
+ }
285
+ }
286
+ async dispatchRequest(method, url, body, headers, signal) {
287
+ const axiosConfig = {
288
+ headers,
289
+ ...(signal !== undefined ? { signal } : {}),
290
+ };
291
+ switch (method) {
292
+ case 'GET':
293
+ return (await this.axiosInstance.get(url, axiosConfig)).data;
294
+ case 'POST':
295
+ return (await this.axiosInstance.post(url, body, axiosConfig)).data;
296
+ case 'DELETE':
297
+ return (await this.axiosInstance.delete(url, axiosConfig)).data;
298
+ case 'PATCH':
299
+ return (await this.axiosInstance.patch(url, body, axiosConfig)).data;
101
300
  }
102
301
  }
103
302
  async buildHeaders(config) {
@@ -119,12 +318,61 @@ class HttpClient {
119
318
  }
120
319
  return headers;
121
320
  }
122
- async logRequestResponse(url, request, response) {
123
- if (this.logger) {
124
- await this.logger.logRequestResponse(url, request, response);
321
+ /** Invoke logging as a detached observer. Logger behavior can never affect request control flow. */
322
+ observeLog(url, request, response) {
323
+ if (!this.logger)
324
+ return;
325
+ try {
326
+ const requestSnapshot = this.createLogSnapshot(request, true);
327
+ const responseSnapshot = this.createLogSnapshot(response);
328
+ void Promise.resolve(this.logger.logRequestResponse(url, requestSnapshot, responseSnapshot)).catch(() => undefined);
329
+ }
330
+ catch {
331
+ // Logging is deliberately observational, including for synchronous logger failures.
332
+ }
333
+ }
334
+ /** Isolate logger-owned values so a custom logger cannot mutate request or response control-flow state. */
335
+ createLogSnapshot(value, redactRequestHeaders = false) {
336
+ try {
337
+ const snapshot = (0, request_value_1.cloneRequestValue)(value);
338
+ if (redactRequestHeaders)
339
+ this.redactSensitiveLogHeaders(snapshot);
340
+ return (0, request_value_1.deepFreezeRequestValue)(snapshot);
341
+ }
342
+ catch {
343
+ return '[log value unavailable]';
125
344
  }
126
345
  }
346
+ /** Remove credentials from logger-owned header snapshots without changing dispatch headers. */
347
+ redactSensitiveLogHeaders(request) {
348
+ if (typeof request !== 'object' || request === null || !('headers' in request))
349
+ return;
350
+ const { headers } = request;
351
+ if (typeof headers !== 'object' || headers === null)
352
+ return;
353
+ for (const headerName of Object.keys(headers)) {
354
+ const normalizedHeaderName = headerName.toLowerCase();
355
+ if (normalizedHeaderName === 'authorization' || normalizedHeaderName === 'proxy-authorization') {
356
+ Reflect.set(headers, headerName, '[REDACTED]');
357
+ }
358
+ }
359
+ }
360
+ logFailedRequest(method, url, body, error) {
361
+ if (!axios_1.default.isAxiosError(error))
362
+ return;
363
+ const request = method === 'GET' || method === 'DELETE' ? { method } : { method, data: body };
364
+ this.observeLog(url, request, error.response?.data ?? error.message);
365
+ }
366
+ logRetry(method, url, attempt, maxAttempts, summary, error) {
367
+ this.observeLog(url, {
368
+ method,
369
+ nextAttempt: attempt + 1,
370
+ ...(summary.identifier !== undefined ? { attemptIdentifier: summary.identifier } : {}),
371
+ }, `Retrying after ${summary.errorClassification} (next attempt ${attempt + 1}/${maxAttempts}): ${axios_1.default.isAxiosError(error) ? (error.response?.status ?? 'network error') : 'request error'}`);
372
+ }
127
373
  handleRequestError(error) {
374
+ if (error instanceof errors_1.CantonError)
375
+ return error;
128
376
  if (axios_1.default.isAxiosError(error)) {
129
377
  const status = error.response?.status;
130
378
  const data = (error.response?.data ?? {});
@@ -165,10 +413,7 @@ class HttpClient {
165
413
  }
166
414
  return new errors_1.NetworkError(`Request failed: ${error instanceof Error ? error.message : String(error)}`);
167
415
  }
168
- /**
169
- * Formats a context object into a summary string for error messages. Shows up to MAX_CONTEXT_KEYS keys with truncated
170
- * values.
171
- */
416
+ /** Formats a context object into a summary string. Shows up to MAX_CONTEXT_KEYS keys with truncated values. */
172
417
  formatContextSummary(contextObj) {
173
418
  const contextKeys = Object.keys(contextObj);
174
419
  if (contextKeys.length === 0) {
@@ -186,10 +431,7 @@ class HttpClient {
186
431
  })
187
432
  .join(', ');
188
433
  }
189
- /**
190
- * Safely converts a context value to a string representation. Handles null, undefined, strings, and objects (with
191
- * circular reference protection).
192
- */
434
+ /** Safely converts a context value to a string representation. */
193
435
  stringifyContextValue(v) {
194
436
  if (typeof v === 'string') {
195
437
  return v;
@@ -201,67 +443,97 @@ class HttpClient {
201
443
  return 'undefined';
202
444
  }
203
445
  try {
204
- // JSON.stringify can return undefined for functions, Symbols, or objects with toJSON() returning undefined
205
446
  const result = JSON.stringify(v);
206
447
  return typeof result === 'string' ? result : '[Object]';
207
448
  }
208
449
  catch {
209
- // Handle circular references, BigInt, or other non-serializable values
210
450
  return '[Object]';
211
451
  }
212
452
  }
213
- /**
214
- * Determines whether a request error is retryable. Retries on:
215
- *
216
- * - HTTP 404 (transient during Canton node restarts)
217
- * - HTTP 5xx server errors
218
- * - Network errors
219
- * - Canton-specific transient errors: UNKNOWN_CONTRACT_SYNCHRONIZERS (400), SEQUENCER_BACKPRESSURE (409), HTTP 503
220
- */
453
+ /** Return true for transport failures that the SDK has historically classified as transient. */
221
454
  isRetryableError(error) {
222
455
  if (axios_1.default.isAxiosError(error)) {
223
456
  const status = error.response?.status;
224
457
  const data = (error.response?.data ?? {});
225
458
  const code = typeof data['code'] === 'string' ? data['code'] : undefined;
226
- // Retry on undefined status (network error)
227
- if (status === undefined) {
459
+ if (status === undefined || status === 404 || (status >= 500 && status < 600))
228
460
  return true;
229
- }
230
- // Retry on 404 - Canton nodes may return transient 404s during restarts
231
- if (status === 404) {
232
- return true;
233
- }
234
- // Retry on 5xx server errors
235
- if (status >= 500 && status < 600) {
461
+ if (status === 400 && code === 'UNKNOWN_CONTRACT_SYNCHRONIZERS')
236
462
  return true;
237
- }
238
- // Retry on Canton-specific transient errors
239
- if (status === 400 && code === 'UNKNOWN_CONTRACT_SYNCHRONIZERS') {
463
+ if (status === 409 && code === 'SEQUENCER_BACKPRESSURE')
240
464
  return true;
241
- }
242
- if (status === 409 && code === 'SEQUENCER_BACKPRESSURE') {
243
- return true;
244
- }
245
465
  return false;
246
466
  }
247
- // Only retry non-Axios errors that are instances of NetworkError
248
467
  return error instanceof errors_1.NetworkError;
249
468
  }
250
- /** Sleep for the specified number of milliseconds */
251
- async sleep(ms) {
252
- return new Promise((resolve) => setTimeout(resolve, ms));
469
+ classifyRequestError(requestSemantics, error, dispatched, retryable) {
470
+ if (!dispatched) {
471
+ return { errorClassification: 'pre-dispatch-failure', outcomeCertainty: 'not-dispatched' };
472
+ }
473
+ if (requestSemantics === 'read') {
474
+ return {
475
+ errorClassification: retryable ? 'transient-read-failure' : 'definite-rejection',
476
+ outcomeCertainty: 'definite',
477
+ };
478
+ }
479
+ if ((0, errors_1.isDefiniteCantonMutationRejection)(error)) {
480
+ return { errorClassification: 'definite-rejection', outcomeCertainty: 'definite' };
481
+ }
482
+ return { errorClassification: 'ambiguous-mutation-outcome', outcomeCertainty: 'ambiguous' };
483
+ }
484
+ toTerminalError(requestSemantics, method, url, error, attempts) {
485
+ if (requestSemantics === 'read' || method === 'GET' || !this.hasAmbiguousAttempt(attempts)) {
486
+ return error;
487
+ }
488
+ const attemptIdentifiers = attempts.flatMap((attempt) => attempt.identifier === undefined ? [] : [attempt.identifier]);
489
+ return new errors_1.UnknownMutationOutcomeError({
490
+ method,
491
+ endpoint: this.redactEndpoint(url),
492
+ attempts: attempts.length,
493
+ ...(attemptIdentifiers.length > 0 ? { attemptIdentifiers } : {}),
494
+ }, error);
495
+ }
496
+ preservePriorAmbiguity(requestSemantics, method, url, error, previousAttempts) {
497
+ return this.toTerminalError(requestSemantics, method, url, error, previousAttempts);
253
498
  }
254
- /**
255
- * Prepares request data for retry by updating commandId fields to avoid duplicate command rejection. If the data
256
- * contains a commandId field, appends a retry suffix with timestamp to make it unique.
257
- */
258
- prepareDataForRetry(data) {
259
- if (data && typeof data === 'object' && 'commandId' in data) {
260
- const originalCommandId = data.commandId;
261
- const retryCommandId = `${originalCommandId}-retry`;
262
- return { ...data, commandId: retryCommandId };
499
+ hasAmbiguousAttempt(attempts) {
500
+ return attempts.some((attempt) => attempt.outcomeCertainty === 'ambiguous');
501
+ }
502
+ normalizeCallbackError(error) {
503
+ return error instanceof Error ? error : new errors_1.NetworkError(`Request callback failed: ${String(error)}`);
504
+ }
505
+ redactEndpoint(url) {
506
+ try {
507
+ const parsed = new URL(url);
508
+ return `${parsed.origin}${parsed.pathname}`;
509
+ }
510
+ catch {
511
+ const queryIndex = url.search(/[?#]/u);
512
+ return queryIndex < 0 ? url : url.slice(0, queryIndex);
263
513
  }
264
- return data;
514
+ }
515
+ redactAttemptIdentifier(identifier) {
516
+ if (identifier === undefined)
517
+ return undefined;
518
+ return identifier.slice(0, HttpClient.MAX_ATTEMPT_IDENTIFIER_LENGTH);
519
+ }
520
+ isCanceledError(error) {
521
+ return axios_1.default.isCancel(error) || (axios_1.default.isAxiosError(error) && error.code === 'ERR_CANCELED');
522
+ }
523
+ async abortableSleep(ms, signal) {
524
+ (0, abort_1.throwIfAborted)(signal);
525
+ await new Promise((resolve, reject) => {
526
+ const timer = setTimeout(() => {
527
+ signal?.removeEventListener('abort', onAbort);
528
+ resolve();
529
+ }, ms);
530
+ const onAbort = () => {
531
+ clearTimeout(timer);
532
+ signal?.removeEventListener('abort', onAbort);
533
+ reject((0, abort_1.createAbortError)(signal));
534
+ };
535
+ signal?.addEventListener('abort', onAbort, { once: true });
536
+ });
265
537
  }
266
538
  }
267
539
  exports.HttpClient = HttpClient;
@@ -269,4 +541,5 @@ exports.HttpClient = HttpClient;
269
541
  HttpClient.CAUSE_TRUNCATE_LENGTH = 200;
270
542
  HttpClient.CONTEXT_VALUE_TRUNCATE_LENGTH = 50;
271
543
  HttpClient.MAX_CONTEXT_KEYS = 3;
544
+ HttpClient.MAX_ATTEMPT_IDENTIFIER_LENGTH = 200;
272
545
  //# sourceMappingURL=HttpClient.js.map