@trusset/sdk 0.1.0 → 0.5.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.js CHANGED
@@ -1,73 +1,56 @@
1
- "use strict";
2
- var __defProp = Object.defineProperty;
3
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
- var __getOwnPropNames = Object.getOwnPropertyNames;
5
- var __hasOwnProp = Object.prototype.hasOwnProperty;
6
- var __export = (target, all) => {
7
- for (var name in all)
8
- __defProp(target, name, { get: all[name], enumerable: true });
9
- };
10
- var __copyProps = (to, from, except, desc) => {
11
- if (from && typeof from === "object" || typeof from === "function") {
12
- for (let key of __getOwnPropNames(from))
13
- if (!__hasOwnProp.call(to, key) && key !== except)
14
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
- }
16
- return to;
17
- };
18
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
-
20
- // src/index.ts
21
- var index_exports = {};
22
- __export(index_exports, {
23
- AuthError: () => AuthError,
24
- CustomerIdLinks: () => CustomerIdLinks,
25
- CustomerIdentity: () => CustomerIdentity,
26
- CustomerManagement: () => CustomerManagement,
27
- CustomerSync: () => CustomerSync,
28
- Customers: () => Customers,
29
- NetworkError: () => NetworkError,
30
- TrussetClient: () => TrussetClient,
31
- TrussetError: () => TrussetError,
32
- ValidationError: () => ValidationError
33
- });
34
- module.exports = __toCommonJS(index_exports);
35
-
36
- // src/utils/errors.ts
37
- var TrussetError = class _TrussetError extends Error {
38
- constructor(message, code, statusCode, requestId) {
39
- super(message);
40
- this.name = "TrussetError";
41
- this.code = code;
42
- this.statusCode = statusCode;
43
- this.requestId = requestId ?? null;
44
- Object.setPrototypeOf(this, _TrussetError.prototype);
45
- }
46
- };
47
- var AuthError = class _AuthError extends TrussetError {
48
- constructor(message, requestId) {
49
- super(message, "AUTH_FAILED", 401, requestId);
50
- this.name = "AuthError";
51
- Object.setPrototypeOf(this, _AuthError.prototype);
52
- }
53
- };
54
- var ValidationError = class _ValidationError extends TrussetError {
55
- constructor(message) {
56
- super(message, "VALIDATION_ERROR", 400);
57
- this.name = "ValidationError";
58
- Object.setPrototypeOf(this, _ValidationError.prototype);
59
- }
60
- };
61
- var NetworkError = class _NetworkError extends TrussetError {
62
- constructor(message) {
63
- super(message, "NETWORK_ERROR", 0);
64
- this.name = "NetworkError";
65
- Object.setPrototypeOf(this, _NetworkError.prototype);
66
- }
67
- };
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
2
+
3
+
4
+
5
+
6
+
7
+
8
+
9
+ var _chunkACXAKW5Ejs = require('./chunk-ACXAKW5E.js');
10
+
11
+
12
+
13
+
14
+
15
+
16
+
17
+
18
+
19
+
20
+
21
+
22
+
23
+
24
+
25
+
26
+
27
+
28
+
29
+
30
+ var _chunk2KHUPARHjs = require('./chunk-2KHUPARH.js');
31
+
32
+
33
+ var _chunkGWMT6UU7js = require('./chunk-GWMT6UU7.js');
34
+
35
+
36
+ var _chunk7HFMHYRAjs = require('./chunk-7HFMHYRA.js');
37
+
38
+
39
+
40
+
41
+
42
+
43
+ var _chunkAI4ZNJLRjs = require('./chunk-AI4ZNJLR.js');
44
+
45
+ // src/version.ts
46
+ var SDK_VERSION = "0.5.0";
68
47
 
69
48
  // src/utils/http.ts
70
49
  var RETRYABLE_STATUS = /* @__PURE__ */ new Set([408, 429, 500, 502, 503, 504]);
50
+ var CONFIRM_RETRYABLE_STATUS = /* @__PURE__ */ new Set([408, 429, 502, 503, 504]);
51
+ var MAX_RETRY_AFTER_MS = 6e4;
52
+ var NOT_FOUND_DELAYS_MS = [3e3, 6e3, 12e3];
53
+ var EMPTY_METADATA = { requestId: "", timestamp: "" };
71
54
  var silentLogger = {
72
55
  debug() {
73
56
  },
@@ -81,28 +64,44 @@ var silentLogger = {
81
64
  var HttpClient = class {
82
65
  constructor(config) {
83
66
  this.config = {
84
- baseUrl: config.baseUrl ?? "https://api.trusset.org",
67
+ baseUrl: _nullishCoalesce(config.baseUrl, () => ( "https://api.trusset.org")),
85
68
  apiKey: config.apiKey,
86
- timeoutMs: config.timeoutMs ?? 3e4,
87
- maxRetries: config.maxRetries ?? 3,
88
- logger: config.logger ?? silentLogger
69
+ timeoutMs: _nullishCoalesce(config.timeoutMs, () => ( 3e4)),
70
+ maxRetries: _nullishCoalesce(config.maxRetries, () => ( 3)),
71
+ logger: _nullishCoalesce(config.logger, () => ( silentLogger)),
72
+ fetch: _nullishCoalesce(config.fetch, () => ( ((input, init) => fetch(input, init))))
89
73
  };
90
74
  }
91
- async get(path, params) {
92
- const url = this.buildUrl(path, params);
93
- return this.request("GET", url);
75
+ async get(path, params, options) {
76
+ const { data } = await this.requestEnvelope("GET", this.buildUrl(path, params), void 0, options);
77
+ return data;
78
+ }
79
+ /** GET that also returns the response metadata (used for list totals). */
80
+ async getWithMeta(path, params) {
81
+ return this.requestEnvelope("GET", this.buildUrl(path, params));
94
82
  }
95
- async post(path, body) {
96
- const url = this.buildUrl(path);
97
- return this.request("POST", url, body);
83
+ async getBinary(path, params, options) {
84
+ return this.requestBinary(this.buildUrl(path, params), options);
98
85
  }
99
- async put(path, body) {
100
- const url = this.buildUrl(path);
101
- return this.request("PUT", url, body);
86
+ async post(path, body, options) {
87
+ const { data } = await this.requestEnvelope("POST", this.buildUrl(path), body, options);
88
+ return data;
102
89
  }
103
- async delete(path) {
104
- const url = this.buildUrl(path);
105
- return this.request("DELETE", url);
90
+ async put(path, body, options) {
91
+ const { data } = await this.requestEnvelope("PUT", this.buildUrl(path), body, options);
92
+ return data;
93
+ }
94
+ async patch(path, body, options) {
95
+ const { data } = await this.requestEnvelope("PATCH", this.buildUrl(path), body, options);
96
+ return data;
97
+ }
98
+ async delete(path, options) {
99
+ const { data } = await this.requestEnvelope("DELETE", this.buildUrl(path), void 0, options);
100
+ return data;
101
+ }
102
+ async postForm(path, form, params, options) {
103
+ const { data } = await this.requestEnvelope("POST", this.buildUrl(path, params), form, options);
104
+ return data;
106
105
  }
107
106
  // -----------------------------------------------------------------------
108
107
  buildUrl(path, params) {
@@ -118,468 +117,174 @@ var HttpClient = class {
118
117
  }
119
118
  return url.toString();
120
119
  }
121
- async request(method, url, body) {
122
- let lastError = null;
123
- for (let attempt = 0; attempt <= this.config.maxRetries; attempt++) {
124
- if (attempt > 0) {
125
- const delay = Math.min(1e3 * Math.pow(2, attempt - 1), 1e4);
126
- this.config.logger.debug(`Retry ${attempt}/${this.config.maxRetries} after ${delay}ms`, { url });
120
+ retryClass(method, options) {
121
+ if (_optionalChain([options, 'optionalAccess', _ => _.retry])) return options.retry;
122
+ return method === "GET" ? "read" : "write";
123
+ }
124
+ // Writes are never blindly retried: identity writes are two-phase
125
+ // (prepare calldata, then confirm a mined transaction), and retrying a
126
+ // confirm after an ambiguous failure (5xx, timeout, dropped connection)
127
+ // could record the same transaction twice. 429 is the exception - the
128
+ // server rejected the request before doing any work.
129
+ canRetry(kind, status) {
130
+ if (kind === "read") return status === void 0 || RETRYABLE_STATUS.has(status);
131
+ if (kind === "confirm") return status === void 0 || CONFIRM_RETRYABLE_STATUS.has(status);
132
+ return status === 429;
133
+ }
134
+ headers(jsonBody, accept) {
135
+ const headers = {
136
+ "x-api-key": this.config.apiKey,
137
+ "Accept": accept,
138
+ "User-Agent": `@trusset/sdk/${SDK_VERSION}`
139
+ };
140
+ if (jsonBody) headers["Content-Type"] = "application/json";
141
+ return headers;
142
+ }
143
+ async send(method, url, body, timeoutMs, accept) {
144
+ const controller = new AbortController();
145
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
146
+ const form = typeof FormData !== "undefined" && body instanceof FormData;
147
+ try {
148
+ return await this.config.fetch(url, {
149
+ method,
150
+ headers: this.headers(body !== void 0 && !form, accept),
151
+ body: body === void 0 ? void 0 : form ? body : JSON.stringify(body),
152
+ signal: controller.signal
153
+ });
154
+ } finally {
155
+ clearTimeout(timer);
156
+ }
157
+ }
158
+ async requestEnvelope(method, url, body, options) {
159
+ const kind = this.retryClass(method, options);
160
+ const timeoutMs = Math.max(this.config.timeoutMs, _nullishCoalesce(_optionalChain([options, 'optionalAccess', _2 => _2.timeoutMs]), () => ( 0)));
161
+ return this.withRetries(kind, url, timeoutMs, async () => {
162
+ const res = await this.send(method, url, body, timeoutMs, "application/json");
163
+ await this.throwIfFailed(res);
164
+ let json;
165
+ try {
166
+ json = await res.json();
167
+ } catch (e) {
168
+ throw new (0, _chunkAI4ZNJLRjs.TrussetError)(
169
+ `Server returned a non-JSON response (status ${res.status})`,
170
+ "BAD_RESPONSE",
171
+ res.status
172
+ );
173
+ }
174
+ if (!json.success) {
175
+ throw toApiError(json, res, `Request failed with status ${res.status}`);
176
+ }
177
+ return { data: json.data, metadata: _nullishCoalesce(json.metadata, () => ( EMPTY_METADATA)) };
178
+ });
179
+ }
180
+ async requestBinary(url, options) {
181
+ const timeoutMs = Math.max(this.config.timeoutMs, _nullishCoalesce(_optionalChain([options, 'optionalAccess', _3 => _3.timeoutMs]), () => ( 0)));
182
+ return this.withRetries(this.retryClass("GET", options), url, timeoutMs, async () => {
183
+ const res = await this.send("GET", url, void 0, timeoutMs, "*/*");
184
+ await this.throwIfFailed(res);
185
+ const contentType = _nullishCoalesce(res.headers.get("content-type"), () => ( "application/octet-stream"));
186
+ if (contentType.includes("application/json")) {
187
+ const json = await res.json().catch(() => null);
188
+ throw toApiError(json, res, `Expected a document but the server answered with JSON (status ${res.status})`);
189
+ }
190
+ const bytes = new Uint8Array(await res.arrayBuffer());
191
+ return { bytes, contentType, filename: filenameOf(res.headers.get("content-disposition")) };
192
+ });
193
+ }
194
+ async throwIfFailed(res) {
195
+ if (res.status === 401) {
196
+ const json = await res.json().catch(() => ({}));
197
+ throw new (0, _chunkAI4ZNJLRjs.AuthError)(
198
+ _nullishCoalesce(_optionalChain([json, 'access', _4 => _4.error, 'optionalAccess', _5 => _5.message]), () => ( "Authentication failed")),
199
+ _optionalChain([json, 'access', _6 => _6.metadata, 'optionalAccess', _7 => _7.requestId])
200
+ );
201
+ }
202
+ if (res.status >= 400) {
203
+ const json = await res.json().catch(() => null);
204
+ throw toApiError(json, res, `Server returned ${res.status}`);
205
+ }
206
+ }
207
+ async withRetries(kind, url, timeoutMs, attempt) {
208
+ let retries = 0;
209
+ let notFoundRetries = 0;
210
+ let delay = 0;
211
+ while (true) {
212
+ if (delay > 0) {
213
+ this.config.logger.debug(`Retry after ${delay}ms`, { url });
127
214
  await sleep(delay);
128
215
  }
129
216
  try {
130
- const controller = new AbortController();
131
- const timer = setTimeout(() => controller.abort(), this.config.timeoutMs);
132
- const headers = {
133
- "x-api-key": this.config.apiKey,
134
- "Accept": "application/json",
135
- "User-Agent": "@trusset/sdk/0.1.0"
136
- };
137
- if (body !== void 0) {
138
- headers["Content-Type"] = "application/json";
139
- }
140
- const res = await fetch(url, {
141
- method,
142
- headers,
143
- body: body !== void 0 ? JSON.stringify(body) : void 0,
144
- signal: controller.signal
145
- });
146
- clearTimeout(timer);
147
- if (res.status === 401) {
148
- const json2 = await res.json().catch(() => ({}));
149
- throw new AuthError(
150
- json2.error?.message ?? "Authentication failed",
151
- json2.metadata?.requestId
152
- );
153
- }
154
- if (RETRYABLE_STATUS.has(res.status)) {
155
- lastError = new TrussetError(
156
- `Server returned ${res.status}`,
157
- "SERVER_ERROR",
158
- res.status
159
- );
160
- continue;
161
- }
162
- const json = await res.json();
163
- if (!json.success) {
164
- throw new TrussetError(
165
- json.error?.message ?? `Request failed with status ${res.status}`,
166
- json.error?.code ?? "REQUEST_FAILED",
167
- res.status,
168
- json.metadata?.requestId
169
- );
170
- }
171
- return json.data;
217
+ return await attempt();
172
218
  } catch (err) {
173
- if (err instanceof TrussetError) {
174
- if (!RETRYABLE_STATUS.has(err.statusCode)) throw err;
175
- lastError = err;
219
+ if (err instanceof _chunkAI4ZNJLRjs.AuthError) throw err;
220
+ if (err instanceof _chunkAI4ZNJLRjs.TrussetError && err.statusCode > 0) {
221
+ if (kind !== "read" && err.code === "TX_NOT_FOUND") {
222
+ if (notFoundRetries >= NOT_FOUND_DELAYS_MS.length) throw err;
223
+ delay = NOT_FOUND_DELAYS_MS[notFoundRetries];
224
+ notFoundRetries += 1;
225
+ continue;
226
+ }
227
+ if (!this.canRetry(kind, err.statusCode) || retries >= this.config.maxRetries) throw err;
228
+ retries += 1;
229
+ delay = err.statusCode === 429 && err.retryAfter !== null ? Math.min(err.retryAfter * 1e3, MAX_RETRY_AFTER_MS) : backoff(retries);
176
230
  continue;
177
231
  }
178
232
  if (err instanceof TypeError || err instanceof DOMException && err.name === "AbortError") {
179
- lastError = new NetworkError(
180
- err instanceof DOMException ? "Request timed out" : `Network error: ${err.message}`
233
+ const netError = new (0, _chunkAI4ZNJLRjs.NetworkError)(
234
+ err instanceof DOMException ? `Request timed out after ${timeoutMs}ms` : `Network error: ${err.message}`
181
235
  );
236
+ if (!this.canRetry(kind) || retries >= this.config.maxRetries) throw netError;
237
+ retries += 1;
238
+ delay = backoff(retries);
182
239
  continue;
183
240
  }
184
241
  throw err;
185
242
  }
186
243
  }
187
- throw lastError ?? new NetworkError("Request failed after retries");
188
244
  }
189
245
  };
190
- function sleep(ms) {
191
- return new Promise((resolve) => setTimeout(resolve, ms));
246
+ function toApiError(json, res, fallback) {
247
+ const error = _nullishCoalesce(_optionalChain([json, 'optionalAccess', _8 => _8.error]), () => ( {}));
248
+ const { code, message, details, ...rest } = error;
249
+ const metadata = _nullishCoalesce(_optionalChain([json, 'optionalAccess', _9 => _9.metadata]), () => ( {}));
250
+ return new (0, _chunkAI4ZNJLRjs.TrussetError)(
251
+ typeof message === "string" ? message : fallback,
252
+ typeof code === "string" ? code : res.status >= 500 ? "SERVER_ERROR" : "REQUEST_FAILED",
253
+ res.status,
254
+ typeof metadata.requestId === "string" ? metadata.requestId : void 0,
255
+ {
256
+ details,
257
+ data: Object.keys(rest).length ? rest : null,
258
+ retryAfter: res.status === 429 ? retryAfterOf(res, metadata) : null
259
+ }
260
+ );
192
261
  }
193
-
194
- // src/utils/validation.ts
195
- var ETH_ADDRESS_RE = /^0x[0-9a-fA-F]{40}$/;
196
- function requireString(value, field) {
197
- if (typeof value !== "string" || value.trim().length === 0) {
198
- throw new ValidationError(`${field} is required and must be a non-empty string`);
199
- }
200
- return value.trim();
262
+ function retryAfterOf(res, metadata) {
263
+ const header = res.headers.get("retry-after");
264
+ if (header !== null && /^\d+$/.test(header.trim())) return Number(header.trim());
265
+ if (typeof metadata.retryAfter === "number" && Number.isFinite(metadata.retryAfter)) return Math.max(0, metadata.retryAfter);
266
+ return null;
201
267
  }
202
- function requireAddress(value, field) {
203
- const s = requireString(value, field);
204
- if (!ETH_ADDRESS_RE.test(s)) {
205
- throw new ValidationError(`${field} must be a valid Ethereum address`);
206
- }
207
- return s;
268
+ function filenameOf(disposition) {
269
+ if (!disposition) return null;
270
+ const match = /filename="?([^";]+)"?/i.exec(disposition);
271
+ return match ? match[1] : null;
208
272
  }
209
- function optionalAddress(value, field) {
210
- if (value === void 0 || value === null || value === "") return void 0;
211
- return requireAddress(value, field);
273
+ function backoff(attempt) {
274
+ return Math.min(1e3 * Math.pow(2, attempt - 1), 1e4);
212
275
  }
213
- function requireCountry(value, field) {
214
- const s = requireString(value, field);
215
- if (s.length < 2 || s.length > 3) {
216
- throw new ValidationError(`${field} must be a 2-3 letter country code`);
217
- }
218
- return s.toUpperCase();
219
- }
220
- function clampInt(value, min, max, fallback) {
221
- if (value === void 0 || value === null) return fallback;
222
- const n = Math.floor(value);
223
- if (isNaN(n)) return fallback;
224
- return Math.max(min, Math.min(max, n));
276
+ function sleep(ms) {
277
+ return new Promise((resolve) => setTimeout(resolve, ms));
225
278
  }
226
279
 
227
- // src/modules/customers/management.ts
228
- var CustomerManagement = class {
229
- constructor(http) {
230
- this.http = http;
231
- }
232
- /**
233
- * List customers for the current instance.
234
- */
235
- async list(options = {}) {
236
- const params = {
237
- limit: clampInt(options.limit, 1, 200, 100),
238
- offset: clampInt(options.offset, 0, Number.MAX_SAFE_INTEGER, 0),
239
- includeArchived: options.includeArchived ? "true" : void 0,
240
- search: options.search || void 0
241
- };
242
- return this.http.get("/customers/api/manage", params);
243
- }
244
- /**
245
- * Get a customer by wallet address, ID, externalId, or referenceKey.
246
- */
247
- async get(identifier) {
248
- requireString(identifier, "identifier");
249
- return this.http.get(`/customers/api/manage/${encodeURIComponent(identifier)}`);
250
- }
251
- /**
252
- * Create a new customer record.
253
- */
254
- async create(input) {
255
- if (input.walletAddress) optionalAddress(input.walletAddress, "walletAddress");
256
- return this.http.post("/customers/api/manage", input);
257
- }
258
- /**
259
- * Update an existing customer.
260
- */
261
- async update(customerId, input) {
262
- requireString(customerId, "customerId");
263
- return this.http.put(`/customers/api/manage/${encodeURIComponent(customerId)}`, input);
264
- }
265
- /**
266
- * Archive (soft-delete) a customer.
267
- */
268
- async archive(customerId) {
269
- requireString(customerId, "customerId");
270
- return this.http.delete(`/customers/api/manage/${encodeURIComponent(customerId)}`);
271
- }
272
- /**
273
- * Link a wallet address to a customer created via referenceKey.
274
- */
275
- async linkWallet(input) {
276
- requireString(input.referenceKey, "referenceKey");
277
- requireAddress(input.walletAddress, "walletAddress");
278
- return this.http.post("/customers/api/manage/link-wallet", input);
279
- }
280
- /**
281
- * Batch-fetch country codes for a list of wallet addresses.
282
- */
283
- async batchGetCountries(addresses) {
284
- if (!Array.isArray(addresses) || addresses.length === 0) {
285
- return {};
286
- }
287
- return this.http.post("/customers/api/manage/countries/batch", { addresses });
288
- }
289
- // -----------------------------------------------------------------------
290
- // Convenience search helpers
291
- // -----------------------------------------------------------------------
292
- /**
293
- * Search customers by name substring.
294
- */
295
- async searchByName(name, options) {
296
- return this.list({ ...options, search: name });
297
- }
298
- /**
299
- * Find a customer by wallet address.
300
- */
301
- async searchByWallet(walletAddress) {
302
- requireAddress(walletAddress, "walletAddress");
303
- return this.get(walletAddress);
304
- }
305
- /**
306
- * Find a customer by external ID (your system's unique identifier).
307
- */
308
- async searchByExternalId(externalId) {
309
- requireString(externalId, "externalId");
310
- return this.get(externalId);
311
- }
312
- /**
313
- * Find a customer by reference key (for pre-wallet customers).
314
- */
315
- async searchByReference(referenceKey) {
316
- requireString(referenceKey, "referenceKey");
317
- return this.get(referenceKey);
318
- }
319
- };
320
-
321
- // src/modules/customers/identity.ts
322
- var CustomerIdentity = class {
323
- constructor(http) {
324
- this.http = http;
325
- }
326
- /**
327
- * Verify a customer's identity on-chain via the IdentityRegistry contract.
328
- * Requires a configured relayer on the instance.
329
- */
330
- async verify(input) {
331
- requireAddress(input.walletAddress, "walletAddress");
332
- requireCountry(input.country, "country");
333
- return this.http.post("/customers/api/identity/verify", input);
334
- }
335
- /**
336
- * Batch-verify up to 500 identities in a single transaction.
337
- * Requires a configured relayer.
338
- */
339
- async batchVerify(entries) {
340
- if (!Array.isArray(entries) || entries.length === 0 || entries.length > 500) {
341
- throw new ValidationError("Provide 1-500 entries for batch verification");
342
- }
343
- for (const e of entries) {
344
- requireAddress(e.walletAddress, "walletAddress");
345
- requireCountry(e.country, "country");
346
- }
347
- return this.http.post("/customers/api/identity/batch-verify", { entries });
348
- }
349
- /**
350
- * Revoke an identity on-chain. Requires a configured relayer.
351
- */
352
- async revoke(walletAddress) {
353
- requireAddress(walletAddress, "walletAddress");
354
- return this.http.post("/customers/api/identity/revoke", { walletAddress });
355
- }
356
- /**
357
- * Get combined on-chain and off-chain identity status.
358
- */
359
- async getStatus(walletAddress) {
360
- requireAddress(walletAddress, "walletAddress");
361
- return this.http.get(`/customers/api/identity/status/${walletAddress}`);
362
- }
363
- /**
364
- * Check whether a token transfer between two addresses is compliant.
365
- * Runs identity checks + all registered compliance modules.
366
- */
367
- async canTransfer(input) {
368
- requireAddress(input.from, "from");
369
- requireAddress(input.to, "to");
370
- return this.http.post("/customers/api/identity/can-transfer", input);
371
- }
372
- };
373
-
374
- // src/modules/customers/id-links.ts
375
- var CustomerIdLinks = class {
376
- constructor(http) {
377
- this.http = http;
378
- }
379
- /**
380
- * Create a KYC verification link to send to an investor.
381
- */
382
- async create(input) {
383
- if (!input.recipientEmail && !input.recipientWallet && !input.recipientPhone) {
384
- throw new ValidationError("At least one of recipientEmail, recipientWallet, or recipientPhone is required");
385
- }
386
- return this.http.post("/customers/api/id-links", input);
387
- }
388
- /**
389
- * List all verification links for the current instance.
390
- */
391
- async list(options = {}) {
392
- const params = {
393
- limit: clampInt(options.limit, 1, 200, 50),
394
- offset: clampInt(options.offset, 0, Number.MAX_SAFE_INTEGER, 0),
395
- status: options.status
396
- };
397
- return this.http.get("/customers/api/id-links", params);
398
- }
399
- /**
400
- * Revoke (expire) a verification link so it can no longer be used.
401
- */
402
- async revoke(linkId) {
403
- requireString(linkId, "linkId");
404
- await this.http.put(`/customers/api/id-links/${encodeURIComponent(linkId)}/revoke`);
405
- }
406
- /**
407
- * Resend a verification link via the specified channels.
408
- */
409
- async resend(linkId, channels) {
410
- requireString(linkId, "linkId");
411
- if (!Array.isArray(channels) || channels.length === 0) {
412
- throw new ValidationError("channels array is required (email and/or sms)");
413
- }
414
- return this.http.post(`/customers/api/id-links/${encodeURIComponent(linkId)}/resend`, { channels });
415
- }
416
- };
417
-
418
- // src/modules/customers/sync.ts
419
- var DEFAULT_BATCH_SIZE = 50;
420
- var MAX_BATCH_SIZE = 200;
421
- var MAX_VERIFY_BATCH = 500;
422
- var CustomerSync = class {
423
- constructor(http) {
424
- this.http = http;
425
- }
426
- /**
427
- * Import customer records from an external system.
428
- *
429
- * Each record is mapped by `externalId`. If a customer with that externalId
430
- * already exists, the `onConflict` strategy determines whether to skip or
431
- * update.
432
- *
433
- * When `verifyOnChain` is true, records that include a `walletAddress` and
434
- * `country` will be batch-verified on-chain after the database import.
435
- */
436
- async importRecords(records, options = {}) {
437
- if (!Array.isArray(records) || records.length === 0) {
438
- throw new ValidationError("records must be a non-empty array");
439
- }
440
- const batchSize = clampInt(options.batchSize, 1, MAX_BATCH_SIZE, DEFAULT_BATCH_SIZE);
441
- const onConflict = options.onConflict ?? "update";
442
- const result = { total: records.length, created: 0, updated: 0, skipped: 0, errors: [] };
443
- const verifyQueue = [];
444
- for (let i = 0; i < records.length; i += batchSize) {
445
- const batch = records.slice(i, i + batchSize);
446
- const batchResults = await Promise.allSettled(
447
- batch.map((record) => this.processRecord(record, onConflict))
448
- );
449
- for (let j = 0; j < batchResults.length; j++) {
450
- const settled = batchResults[j];
451
- const record = batch[j];
452
- if (settled.status === "fulfilled") {
453
- const outcome = settled.value;
454
- if (outcome.action === "created") result.created++;
455
- else if (outcome.action === "updated") result.updated++;
456
- else result.skipped++;
457
- if (options.verifyOnChain && record.walletAddress && record.country && outcome.action !== "skipped") {
458
- verifyQueue.push({
459
- walletAddress: record.walletAddress,
460
- country: record.country,
461
- investorType: record.investorType,
462
- softExpiryDays: options.verificationDefaults?.softExpiryDays,
463
- hardExpiryDays: options.verificationDefaults?.hardExpiryDays
464
- });
465
- }
466
- } else {
467
- const err = settled.reason;
468
- result.errors.push({
469
- externalId: record.externalId,
470
- code: err?.code ?? "UNKNOWN",
471
- message: err?.message ?? "Unknown error"
472
- });
473
- }
474
- }
475
- options.onProgress?.(Math.min(i + batchSize, records.length), records.length);
476
- }
477
- if (verifyQueue.length > 0) {
478
- await this.runBatchVerification(verifyQueue, result);
479
- }
480
- return result;
481
- }
482
- /**
483
- * Export all customers from Trusset as flat records.
484
- * Useful for syncing data back into your system.
485
- */
486
- async exportRecords(options = {}) {
487
- const allRecords = [];
488
- let offset = 0;
489
- const limit = 200;
490
- while (true) {
491
- const page = await this.http.get("/customers/api/manage", {
492
- limit,
493
- offset,
494
- includeArchived: options.includeArchived ? "true" : void 0
495
- });
496
- if (!page.customers || page.customers.length === 0) break;
497
- for (const c of page.customers) {
498
- allRecords.push({
499
- externalId: c.externalId ?? c.id,
500
- walletAddress: c.walletAddress ?? void 0,
501
- name: c.name ?? void 0,
502
- country: c.country ?? void 0,
503
- investorType: c.investorType ?? void 0,
504
- metadata: c.metadata ?? void 0
505
- });
506
- }
507
- offset += page.customers.length;
508
- if (offset >= page.total) break;
509
- }
510
- return allRecords;
511
- }
512
- // -----------------------------------------------------------------------
513
- // Internal
514
- // -----------------------------------------------------------------------
515
- async processRecord(record, onConflict) {
516
- requireString(record.externalId, "externalId");
517
- let existing = null;
518
- try {
519
- existing = await this.http.get(
520
- `/customers/api/manage/${encodeURIComponent(record.externalId)}`
521
- );
522
- } catch (err) {
523
- const code = err?.code;
524
- const statusCode = err?.statusCode;
525
- if (statusCode !== 404 && code !== "NOT_FOUND") throw err;
526
- }
527
- if (existing) {
528
- if (onConflict === "skip") {
529
- return { action: "skipped" };
530
- }
531
- await this.http.put(`/customers/api/manage/${encodeURIComponent(existing.id)}`, {
532
- name: record.name,
533
- country: record.country,
534
- investorType: record.investorType,
535
- walletAddress: record.walletAddress,
536
- metadata: record.metadata
537
- });
538
- return { action: "updated" };
539
- }
540
- await this.http.post("/customers/api/manage", {
541
- externalId: record.externalId,
542
- walletAddress: record.walletAddress,
543
- name: record.name,
544
- country: record.country,
545
- investorType: record.investorType,
546
- metadata: record.metadata
547
- });
548
- return { action: "created" };
549
- }
550
- async runBatchVerification(queue, result) {
551
- for (let i = 0; i < queue.length; i += MAX_VERIFY_BATCH) {
552
- const batch = queue.slice(i, i + MAX_VERIFY_BATCH);
553
- try {
554
- await this.http.post("/customers/api/identity/batch-verify", { entries: batch });
555
- } catch (err) {
556
- for (const entry of batch) {
557
- result.errors.push({
558
- externalId: entry.walletAddress,
559
- code: "VERIFY_FAILED",
560
- message: err?.message ?? "On-chain batch verification failed"
561
- });
562
- }
563
- }
564
- }
565
- }
566
- };
567
-
568
- // src/modules/customers/index.ts
569
- var Customers = class {
570
- constructor(http) {
571
- this.manage = new CustomerManagement(http);
572
- this.identity = new CustomerIdentity(http);
573
- this.idLinks = new CustomerIdLinks(http);
574
- this.sync = new CustomerSync(http);
575
- }
576
- };
577
-
578
280
  // src/client.ts
579
281
  var TrussetClient = class {
580
282
  constructor(config) {
581
283
  if (!config.apiKey || typeof config.apiKey !== "string" || config.apiKey.length < 20) {
582
- throw new ValidationError("A valid apiKey is required");
284
+ throw new (0, _chunkAI4ZNJLRjs.ValidationError)("A valid apiKey is required");
285
+ }
286
+ if (!config.apiKey.startsWith("trusset_")) {
287
+ throw new (0, _chunkAI4ZNJLRjs.ValidationError)("apiKey must be a Trusset instance API key (they start with 'trusset_')");
583
288
  }
584
289
  this.http = new HttpClient({
585
290
  apiKey: config.apiKey,
@@ -588,19 +293,44 @@ var TrussetClient = class {
588
293
  maxRetries: config.maxRetries,
589
294
  logger: config.logger
590
295
  });
591
- this.customers = new Customers(this.http);
296
+ const context = new (0, _chunk7HFMHYRAjs.LendingContext)(this.http);
297
+ this.customers = new (0, _chunkACXAKW5Ejs.Customers)(this.http, context);
298
+ this.lending = new (0, _chunk2KHUPARHjs.Lending)(this.http, context);
299
+ this.tokens = new (0, _chunkGWMT6UU7js.Tokens)(context);
592
300
  }
593
301
  };
594
- // Annotate the CommonJS export names for ESM import in node:
595
- 0 && (module.exports = {
596
- AuthError,
597
- CustomerIdLinks,
598
- CustomerIdentity,
599
- CustomerManagement,
600
- CustomerSync,
601
- Customers,
602
- NetworkError,
603
- TrussetClient,
604
- TrussetError,
605
- ValidationError
606
- });
302
+
303
+
304
+
305
+
306
+
307
+
308
+
309
+
310
+
311
+
312
+
313
+
314
+
315
+
316
+
317
+
318
+
319
+
320
+
321
+
322
+
323
+
324
+
325
+
326
+
327
+
328
+
329
+
330
+
331
+
332
+
333
+
334
+
335
+
336
+ exports.AuthError = _chunkAI4ZNJLRjs.AuthError; exports.ConfirmationError = _chunkAI4ZNJLRjs.ConfirmationError; exports.CustomerIdLinks = _chunkACXAKW5Ejs.CustomerIdLinks; exports.CustomerIdentity = _chunkACXAKW5Ejs.CustomerIdentity; exports.CustomerManagement = _chunkACXAKW5Ejs.CustomerManagement; exports.CustomerSync = _chunkACXAKW5Ejs.CustomerSync; exports.CustomerVerificationProfiles = _chunkACXAKW5Ejs.CustomerVerificationProfiles; exports.Customers = _chunkACXAKW5Ejs.Customers; exports.Lending = _chunk2KHUPARHjs.Lending; exports.LendingAccessLists = _chunk2KHUPARHjs.LendingAccessLists; exports.LendingAssetData = _chunk2KHUPARHjs.LendingAssetData; exports.LendingAuctions = _chunk2KHUPARHjs.LendingAuctions; exports.LendingBots = _chunk2KHUPARHjs.LendingBots; exports.LendingDocuments = _chunk2KHUPARHjs.LendingDocuments; exports.LendingFees = _chunk2KHUPARHjs.LendingFees; exports.LendingHooks = _chunk2KHUPARHjs.LendingHooks; exports.LendingInsurance = _chunk2KHUPARHjs.LendingInsurance; exports.LendingLiquidations = _chunk2KHUPARHjs.LendingLiquidations; exports.LendingLiquidity = _chunk2KHUPARHjs.LendingLiquidity; exports.LendingLoans = _chunk2KHUPARHjs.LendingLoans; exports.LendingMarkets = _chunk2KHUPARHjs.LendingMarkets; exports.LendingOperators = _chunk2KHUPARHjs.LendingOperators; exports.LendingOracle = _chunk2KHUPARHjs.LendingOracle; exports.LendingPledges = _chunk2KHUPARHjs.LendingPledges; exports.LendingRates = _chunk2KHUPARHjs.LendingRates; exports.LendingTerms = _chunk2KHUPARHjs.LendingTerms; exports.LendingVaults = _chunk2KHUPARHjs.LendingVaults; exports.NetworkError = _chunkAI4ZNJLRjs.NetworkError; exports.Tokens = _chunkGWMT6UU7js.Tokens; exports.TrussetClient = TrussetClient; exports.TrussetError = _chunkAI4ZNJLRjs.TrussetError; exports.ValidationError = _chunkAI4ZNJLRjs.ValidationError; exports.manifestToVerifyInput = _chunkACXAKW5Ejs.manifestToVerifyInput;