@explorins/pers-sdk 1.2.5 → 1.3.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.
Files changed (131) hide show
  1. package/README.md +416 -0
  2. package/dist/analytics.cjs +6 -0
  3. package/dist/analytics.cjs.map +1 -1
  4. package/dist/analytics.js +2 -0
  5. package/dist/analytics.js.map +1 -1
  6. package/dist/business.cjs +6 -0
  7. package/dist/business.cjs.map +1 -1
  8. package/dist/business.js +2 -0
  9. package/dist/business.js.map +1 -1
  10. package/dist/campaign/api/campaign-api.d.ts +19 -65
  11. package/dist/campaign/api/campaign-api.d.ts.map +1 -1
  12. package/dist/campaign.cjs +55 -103
  13. package/dist/campaign.cjs.map +1 -1
  14. package/dist/campaign.js +51 -103
  15. package/dist/campaign.js.map +1 -1
  16. package/dist/chunks/base-token-service-BA81_Ouq.js +532 -0
  17. package/dist/chunks/base-token-service-BA81_Ouq.js.map +1 -0
  18. package/dist/chunks/base-token-service-BQ6uFoki.cjs +537 -0
  19. package/dist/chunks/base-token-service-BQ6uFoki.cjs.map +1 -0
  20. package/dist/chunks/jwt.function-BYiyl-z_.cjs +25 -0
  21. package/dist/chunks/jwt.function-BYiyl-z_.cjs.map +1 -0
  22. package/dist/chunks/jwt.function-d6jPtBqI.js +23 -0
  23. package/dist/chunks/jwt.function-d6jPtBqI.js.map +1 -0
  24. package/dist/chunks/pers-sdk-JC-hSYUd.js +1377 -0
  25. package/dist/chunks/pers-sdk-JC-hSYUd.js.map +1 -0
  26. package/dist/chunks/pers-sdk-_1sTi9x9.cjs +1384 -0
  27. package/dist/chunks/pers-sdk-_1sTi9x9.cjs.map +1 -0
  28. package/dist/core/auth/api/auth-api.d.ts +30 -0
  29. package/dist/core/auth/api/auth-api.d.ts.map +1 -0
  30. package/dist/core/auth/auth-constants.d.ts +33 -0
  31. package/dist/core/auth/auth-constants.d.ts.map +1 -0
  32. package/dist/core/auth/auth-errors.d.ts +8 -0
  33. package/dist/core/auth/auth-errors.d.ts.map +1 -0
  34. package/dist/core/auth/auth-provider.interface.d.ts +72 -2
  35. package/dist/core/auth/auth-provider.interface.d.ts.map +1 -1
  36. package/dist/core/auth/create-auth-provider.d.ts +3 -3
  37. package/dist/core/auth/create-auth-provider.d.ts.map +1 -1
  38. package/dist/core/auth/default-auth-provider.d.ts +71 -0
  39. package/dist/core/auth/default-auth-provider.d.ts.map +1 -0
  40. package/dist/core/auth/index.d.ts +17 -0
  41. package/dist/core/auth/index.d.ts.map +1 -0
  42. package/dist/core/auth/services/auth-service.d.ts +49 -0
  43. package/dist/core/auth/services/auth-service.d.ts.map +1 -0
  44. package/dist/core/auth/token-refresh.d.ts +91 -0
  45. package/dist/core/auth/token-refresh.d.ts.map +1 -0
  46. package/dist/core/auth/token-storage.d.ts +74 -0
  47. package/dist/core/auth/token-storage.d.ts.map +1 -0
  48. package/dist/core/errors/index.d.ts +80 -0
  49. package/dist/core/errors/index.d.ts.map +1 -0
  50. package/dist/core/index.d.ts +1 -2
  51. package/dist/core/index.d.ts.map +1 -1
  52. package/dist/core/pers-api-client.d.ts +172 -12
  53. package/dist/core/pers-api-client.d.ts.map +1 -1
  54. package/dist/core/pers-config.d.ts +36 -1
  55. package/dist/core/pers-config.d.ts.map +1 -1
  56. package/dist/core/utils/jwt.function.d.ts.map +1 -1
  57. package/dist/core.cjs +8 -500
  58. package/dist/core.cjs.map +1 -1
  59. package/dist/core.js +2 -496
  60. package/dist/core.js.map +1 -1
  61. package/dist/donation.cjs +6 -0
  62. package/dist/donation.cjs.map +1 -1
  63. package/dist/donation.js +2 -0
  64. package/dist/donation.js.map +1 -1
  65. package/dist/index.cjs +79 -4751
  66. package/dist/index.cjs.map +1 -1
  67. package/dist/index.d.ts +0 -1
  68. package/dist/index.d.ts.map +1 -1
  69. package/dist/index.js +20 -4699
  70. package/dist/index.js.map +1 -1
  71. package/dist/package.json +129 -129
  72. package/dist/payment.cjs +6 -0
  73. package/dist/payment.cjs.map +1 -1
  74. package/dist/payment.js +2 -0
  75. package/dist/payment.js.map +1 -1
  76. package/dist/pers-sdk.d.ts +49 -7
  77. package/dist/pers-sdk.d.ts.map +1 -1
  78. package/dist/redemption/api/redemption-api.d.ts +38 -77
  79. package/dist/redemption/api/redemption-api.d.ts.map +1 -1
  80. package/dist/redemption/services/redemption-service.d.ts +21 -3
  81. package/dist/redemption/services/redemption-service.d.ts.map +1 -1
  82. package/dist/redemption.cjs +84 -117
  83. package/dist/redemption.cjs.map +1 -1
  84. package/dist/redemption.js +80 -117
  85. package/dist/redemption.js.map +1 -1
  86. package/dist/shared/interfaces/pers-shared-lib.interfaces.d.ts +2 -1
  87. package/dist/shared/interfaces/pers-shared-lib.interfaces.d.ts.map +1 -1
  88. package/dist/tenant/api/tenant-api.d.ts +28 -10
  89. package/dist/tenant/api/tenant-api.d.ts.map +1 -1
  90. package/dist/tenant/index.d.ts +4 -4
  91. package/dist/tenant.cjs +46 -11
  92. package/dist/tenant.cjs.map +1 -1
  93. package/dist/tenant.js +42 -11
  94. package/dist/tenant.js.map +1 -1
  95. package/dist/token.cjs +10 -531
  96. package/dist/token.cjs.map +1 -1
  97. package/dist/token.js +2 -531
  98. package/dist/token.js.map +1 -1
  99. package/dist/transaction.cjs +4 -0
  100. package/dist/transaction.cjs.map +1 -1
  101. package/dist/transaction.js +1 -0
  102. package/dist/transaction.js.map +1 -1
  103. package/dist/user-status.cjs +6 -0
  104. package/dist/user-status.cjs.map +1 -1
  105. package/dist/user-status.js +2 -0
  106. package/dist/user-status.js.map +1 -1
  107. package/dist/user.cjs +6 -0
  108. package/dist/user.cjs.map +1 -1
  109. package/dist/user.js +2 -0
  110. package/dist/user.js.map +1 -1
  111. package/dist/web3-chain.cjs +12 -148
  112. package/dist/web3-chain.cjs.map +1 -1
  113. package/dist/web3-chain.js +7 -147
  114. package/dist/web3-chain.js.map +1 -1
  115. package/dist/web3.cjs +8 -537
  116. package/dist/web3.cjs.map +1 -1
  117. package/dist/web3.js +6 -535
  118. package/dist/web3.js.map +1 -1
  119. package/package.json +129 -129
  120. package/dist/auth-admin/api/auth-admin-api.d.ts +0 -29
  121. package/dist/auth-admin/api/auth-admin-api.d.ts.map +0 -1
  122. package/dist/auth-admin/index.d.ts +0 -27
  123. package/dist/auth-admin/index.d.ts.map +0 -1
  124. package/dist/auth-admin/services/auth-admin-service.d.ts +0 -27
  125. package/dist/auth-admin/services/auth-admin-service.d.ts.map +0 -1
  126. package/dist/auth-admin.cjs +0 -115
  127. package/dist/auth-admin.cjs.map +0 -1
  128. package/dist/auth-admin.js +0 -111
  129. package/dist/auth-admin.js.map +0 -1
  130. package/dist/core/auth/simple-auth-config.interface.d.ts +0 -15
  131. package/dist/core/auth/simple-auth-config.interface.d.ts.map +0 -1
package/dist/index.js CHANGED
@@ -1,4700 +1,21 @@
1
- import { TransactionRole, AccountOwnerType } from '@explorins/pers-shared';
2
- import { jwtDecode } from 'jwt-decode';
3
- import Web3 from 'web3';
4
- import { FetchRequest, JsonRpcProvider } from 'ethers';
5
- import { getSmartContractInstance, getAccountTokenBalance, getTokenUri, getTokenOfOwnerByIndex } from '@explorins/web3-ts';
6
-
7
- /**
8
- * PERS SDK Configuration interfaces
9
- */
10
- /**
11
- * Default configuration values
12
- */
13
- const DEFAULT_PERS_CONFIG = {
14
- environment: 'production',
15
- apiVersion: 'v2',
16
- timeout: 30000,
17
- retries: 3
18
- };
19
- /**
20
- * Internal function to construct API root from environment
21
- * Now defaults to production and v2
22
- */
23
- function buildApiRoot(environment = 'production', version = 'v2') {
24
- const baseUrls = {
25
- development: 'https://explorins-loyalty.ngrok.io',
26
- staging: `https://dev.api.pers.ninja/${version}`,
27
- production: `https://api.pers.ninja/${version}`
28
- };
29
- return `${baseUrls[environment]}`;
30
- }
31
- /**
32
- * Merge user config with defaults
33
- */
34
- function mergeWithDefaults(config) {
35
- return {
36
- ...DEFAULT_PERS_CONFIG,
37
- ...config,
38
- environment: config.environment ?? DEFAULT_PERS_CONFIG.environment,
39
- apiVersion: config.apiVersion ?? DEFAULT_PERS_CONFIG.apiVersion,
40
- timeout: config.timeout ?? DEFAULT_PERS_CONFIG.timeout,
41
- retries: config.retries ?? DEFAULT_PERS_CONFIG.retries
42
- };
43
- }
44
-
45
- // packages/pers-sdk/src/core/pers-api-client.ts
46
- /**
47
- * PERS API Client - Core platform-agnostic client for PERS backend
48
- */
49
- class PersApiClient {
50
- constructor(httpClient, config) {
51
- this.httpClient = httpClient;
52
- this.config = config;
53
- // Merge user config with defaults (production + v2)
54
- this.mergedConfig = mergeWithDefaults(config);
55
- // Build API root from merged environment and version
56
- this.apiRoot = buildApiRoot(this.mergedConfig.environment, this.mergedConfig.apiVersion);
57
- }
58
- /**
59
- * Get request headers including auth token and project key
60
- */
61
- async getHeaders() {
62
- const headers = {
63
- 'Content-Type': 'application/json',
64
- };
65
- // Add authentication token
66
- if (this.mergedConfig.authProvider) {
67
- const token = await this.mergedConfig.authProvider.getToken();
68
- if (token) {
69
- headers['Authorization'] = `Bearer ${token}`;
70
- }
71
- }
72
- // Add project key
73
- if (this.mergedConfig.authProvider) {
74
- const projectKey = await this.mergedConfig.authProvider.getProjectKey();
75
- if (projectKey) {
76
- headers['x-project-key'] = projectKey;
77
- }
78
- }
79
- else if (this.mergedConfig.apiProjectKey) {
80
- // Fallback to config project key if no auth provider
81
- headers['x-project-key'] = this.mergedConfig.apiProjectKey;
82
- }
83
- return headers;
84
- }
85
- /**
86
- * Make a request with proper headers, auth, and error handling
87
- */
88
- async request(method, endpoint, body, options) {
89
- const { retryCount = 0, responseType = 'json' } = options || {};
90
- const url = `${this.apiRoot}${endpoint}`;
91
- // ✅ DEBUGGING: Add extensive logging for CSV endpoint
92
- const isCSVEndpoint = endpoint.includes('/export/csv');
93
- const requestOptions = {
94
- headers: await this.getHeaders(),
95
- timeout: this.mergedConfig.timeout,
96
- responseType
97
- };
98
- try {
99
- let result;
100
- switch (method) {
101
- case 'GET':
102
- result = await this.httpClient.get(url, requestOptions);
103
- break;
104
- case 'POST':
105
- result = await this.httpClient.post(url, body, requestOptions);
106
- break;
107
- case 'PUT':
108
- result = await this.httpClient.put(url, body, requestOptions);
109
- break;
110
- case 'DELETE':
111
- result = await this.httpClient.delete(url, requestOptions);
112
- break;
113
- default:
114
- throw new Error(`Unsupported HTTP method: ${method}`);
115
- }
116
- return result;
117
- }
118
- catch (error) {
119
- if (isCSVEndpoint) {
120
- console.error('❌ [PERS API CLIENT] CSV Request failed:', error);
121
- }
122
- // Handle 401 errors with automatic token refresh
123
- const apiError = error;
124
- if (apiError.status === 401 && retryCount === 0 && this.mergedConfig.authProvider?.onTokenExpired) {
125
- try {
126
- await this.mergedConfig.authProvider.onTokenExpired();
127
- // Retry once with refreshed token
128
- return this.request(method, endpoint, body, { ...options, retryCount: 1 });
129
- }
130
- catch (refreshError) {
131
- throw new PersApiError(`Authentication refresh failed: ${refreshError}`, endpoint, method, 401);
132
- }
133
- }
134
- throw new PersApiError(`PERS API request failed: ${apiError.message || error}`, endpoint, method, apiError.status);
135
- }
136
- }
137
- /**
138
- * Generic GET request
139
- */
140
- async get(endpoint, responseType) {
141
- return this.request('GET', endpoint, undefined, { responseType });
142
- }
143
- /**
144
- * Generic POST request
145
- */
146
- async post(endpoint, body) {
147
- return this.request('POST', endpoint, body);
148
- }
149
- /**
150
- * Generic PUT request
151
- */
152
- async put(endpoint, body) {
153
- return this.request('PUT', endpoint, body);
154
- }
155
- /**
156
- * Generic DELETE request
157
- */
158
- async delete(endpoint) {
159
- return this.request('DELETE', endpoint);
160
- }
161
- /**
162
- * Get current configuration (returns merged config)
163
- */
164
- getConfig() {
165
- return this.mergedConfig;
166
- }
167
- /**
168
- * Get original user configuration
169
- */
170
- getOriginalConfig() {
171
- return this.config;
172
- }
173
- }
174
- class PersApiError extends Error {
175
- constructor(message, endpoint, method, statusCode) {
176
- super(message);
177
- this.endpoint = endpoint;
178
- this.method = method;
179
- this.statusCode = statusCode;
180
- this.name = 'PersApiError';
181
- }
182
- }
183
- /**
184
- * PERS API Client - Core platform-agnostic client for PERS backend
185
- */
186
- /*import { HttpClient, RequestOptions } from './abstractions/http-client';
187
- import { PersConfig, buildApiRoot, mergeWithDefaults } from './pers-config';
188
-
189
- export class PersApiClient {
190
- private readonly apiRoot: string;
191
- private readonly mergedConfig: ReturnType<typeof mergeWithDefaults>;
192
-
193
- constructor(
194
- private httpClient: HttpClient,
195
- private config: PersConfig
196
- ) {
197
- // Merge user config with defaults (production + v2)
198
- this.mergedConfig = mergeWithDefaults(config);
199
-
200
- // Build API root from merged environment and version
201
- this.apiRoot = buildApiRoot(this.mergedConfig.environment, this.mergedConfig.apiVersion);
202
- }
203
-
204
- /**
205
- * Get request headers including auth token and project key
206
- */
207
- /*private async getHeaders(): Promise<Record<string, string>> {
208
- const headers: Record<string, string> = {
209
- 'Content-Type': 'application/json',
210
- };
211
-
212
- // Add authentication token
213
- if (this.mergedConfig.authProvider) {
214
- const token = await this.mergedConfig.authProvider.getToken();
215
- if (token) {
216
- headers['Authorization'] = `Bearer ${token}`;
217
- }
218
- }
219
-
220
- // Add project key
221
- if (this.mergedConfig.authProvider) {
222
- const projectKey = await this.mergedConfig.authProvider.getProjectKey();
223
- if (projectKey) {
224
- headers['x-project-key'] = projectKey;
225
- }
226
- } else if(this.mergedConfig.apiProjectKey) {
227
- // Fallback to config project key if no auth provider
228
- headers['x-project-key'] = this.mergedConfig.apiProjectKey;
229
- }
230
-
231
- return headers;
232
- }
233
-
234
- /**
235
- * Make a request with proper headers, auth, and error handling
236
- */
237
- /*private async request<T>(
238
- method: 'GET' | 'POST' | 'PUT' | 'DELETE',
239
- endpoint: string,
240
- body?: any,
241
- options?: { retryCount?: number }
242
- ): Promise<T> {
243
- const { retryCount = 0 } = options || {};
244
- const url = `${this.apiRoot}${endpoint}`;
245
-
246
- const requestOptions: RequestOptions = {
247
- headers: await this.getHeaders(),
248
- timeout: this.mergedConfig.timeout,
249
- };
250
-
251
- try {
252
- switch (method) {
253
- case 'GET':
254
- return await this.httpClient.get<T>(url, requestOptions);
255
- case 'POST':
256
- return await this.httpClient.post<T>(url, body, requestOptions);
257
- case 'PUT':
258
- return await this.httpClient.put<T>(url, body, requestOptions);
259
- case 'DELETE':
260
- return await this.httpClient.delete<T>(url, requestOptions);
261
- default:
262
- throw new Error(`Unsupported HTTP method: ${method}`);
263
- }
264
- } catch (error: any) {
265
- // Handle 401 errors with automatic token refresh
266
- if (error.status === 401 && retryCount === 0 && this.mergedConfig.authProvider?.onTokenExpired) {
267
- try {
268
- await this.mergedConfig.authProvider.onTokenExpired();
269
- // Retry once with refreshed token
270
- return this.request<T>(method, endpoint, body, { ...options, retryCount: 1 });
271
- } catch (refreshError) {
272
- throw new PersApiError(
273
- `Authentication refresh failed: ${refreshError}`,
274
- endpoint,
275
- method,
276
- 401
277
- );
278
- }
279
- }
280
-
281
- throw new PersApiError(
282
- `PERS API request failed: ${error.message || error}`,
283
- endpoint,
284
- method,
285
- error.status
286
- );
287
- }
288
- }
289
-
290
- /**
291
- * Generic GET request
292
- */
293
- /*async get<T>(endpoint: string): Promise<T> {
294
- return this.request<T>('GET', endpoint);
295
- }
296
-
297
- /**
298
- * Generic POST request
299
- */
300
- /*async post<T>(endpoint: string, body?: any): Promise<T> {
301
- return this.request<T>('POST', endpoint, body);
302
- }
303
-
304
- /**
305
- * Generic PUT request
306
- */
307
- /*async put<T>(endpoint: string, body?: any): Promise<T> {
308
- return this.request<T>('PUT', endpoint, body);
309
- }
310
-
311
- /**
312
- * Generic DELETE request
313
- */
314
- /*async delete<T>(endpoint: string): Promise<T> {
315
- return this.request<T>('DELETE', endpoint);
316
- }
317
-
318
- /**
319
- * Get current configuration (returns merged config)
320
- */
321
- /*getConfig(): ReturnType<typeof mergeWithDefaults> {
322
- return this.mergedConfig;
323
- }
324
-
325
- /**
326
- * Get original user configuration
327
- */
328
- /*getOriginalConfig(): PersConfig {
329
- return this.config;
330
- }
331
- }
332
-
333
- export class PersApiError extends Error {
334
- constructor(
335
- message: string,
336
- public endpoint: string,
337
- public method: string,
338
- public statusCode?: number
339
- ) {
340
- super(message);
341
- this.name = 'PersApiError';
342
- }
343
- }*/
344
-
345
- /**
346
- * Creates a platform-agnostic AuthProvider from simple configuration
347
- *
348
- * This factory function is completely platform-agnostic and can be used
349
- * across Angular, React, Vue, Node.js, or any other JavaScript environment.
350
- *
351
- * Features:
352
- * - Token caching with refresh support
353
- * - Automatic token refresh on expiration
354
- * - Configurable token providers
355
- * - Platform-independent (no localStorage assumptions)
356
- *
357
- * @param config - Simple auth configuration
358
- * @returns AuthProvider implementation
359
- */
360
- function createAuthProvider(config) {
361
- // Store current token for refresh scenarios and caching
362
- let currentToken = config.token || null;
363
- let isRefreshing = false; // Prevent concurrent refresh attempts
364
- let refreshPromise = null;
365
- return {
366
- authType: config.authType || 'user',
367
- async getToken() {
368
- // If currently refreshing, wait for it to complete
369
- if (isRefreshing && refreshPromise) {
370
- await refreshPromise;
371
- return currentToken;
372
- }
373
- // Use cached current token (updated after refresh)
374
- if (currentToken) {
375
- return currentToken;
376
- }
377
- // Custom token provider function (always fresh)
378
- if (config.tokenProvider) {
379
- const token = await config.tokenProvider();
380
- currentToken = token; // Cache for future calls
381
- return token;
382
- }
383
- // No token available
384
- return null;
385
- },
386
- async getProjectKey() {
387
- return config.projectKey || null;
388
- },
389
- async onTokenExpired() {
390
- // Prevent concurrent refresh attempts
391
- if (isRefreshing) {
392
- if (refreshPromise) {
393
- await refreshPromise;
394
- }
395
- return;
396
- }
397
- // No refresh logic provided
398
- if (!config.onTokenExpired) {
399
- console.warn('Token expired but no refresh logic provided');
400
- currentToken = null; // Clear expired token
401
- return;
402
- }
403
- // Start refresh process
404
- isRefreshing = true;
405
- refreshPromise = (async () => {
406
- try {
407
- // Execute refresh logic (should update token source)
408
- await config.onTokenExpired();
409
- // After refresh, get the new token
410
- if (config.tokenProvider) {
411
- const newToken = await config.tokenProvider();
412
- if (newToken && newToken !== currentToken) {
413
- currentToken = newToken;
414
- // Notify about successful token refresh
415
- if (config.onTokenRefreshed) {
416
- config.onTokenRefreshed(newToken);
417
- }
418
- }
419
- else {
420
- console.warn('Token refresh completed but no new token received');
421
- currentToken = null;
422
- }
423
- }
424
- else {
425
- // For static token configs, clear the token since we can't refresh
426
- console.warn('Token expired for static token config - clearing token');
427
- currentToken = null;
428
- }
429
- }
430
- catch (error) {
431
- console.error('Token refresh failed:', error);
432
- currentToken = null; // Clear token on refresh failure
433
- throw error; // Re-throw to let SDK handle the error
434
- }
435
- finally {
436
- isRefreshing = false;
437
- refreshPromise = null;
438
- }
439
- })();
440
- await refreshPromise;
441
- }
442
- };
443
- }
444
- /**
445
- * Platform-specific localStorage token provider for browsers
446
- * This is a convenience function for browser environments
447
- */
448
- /* export function createBrowserTokenProvider(tokenKey: string = 'userJwt'): () => Promise<string | null> {
449
- return async () => {
450
- if (typeof localStorage !== 'undefined') {
451
- return localStorage.getItem(tokenKey);
452
- }
453
- return null;
454
- };
455
- } */
456
- /**
457
- * Platform-specific environment variable token provider for Node.js
458
- * This is a convenience function for Node.js environments
459
- */
460
- /* export function createNodeTokenProvider(envVar: string = 'JWT_TOKEN'): () => Promise<string | null> {
461
- return async () => {
462
- if (typeof process !== 'undefined' && process.env) {
463
- return process.env[envVar] || null;
464
- }
465
- return null;
466
- };
467
- } */
468
-
469
- /**
470
- * PERS SDK - Minimal platform-agnostic client with built-in authentication
471
- * Authentication is now handled at the SDK core level for better scalability
472
- */
473
- /**
474
- * Minimal PERS SDK - API client with authentication built-in
475
- * Platform adapters provide auth providers and HTTP clients
476
- */
477
- class PersSDK {
478
- constructor(httpClient, config) {
479
- this.apiClient = new PersApiClient(httpClient, config);
480
- }
481
- /**
482
- * Get the API client for direct PERS API calls
483
- * This is the main interface - keep it simple!
484
- */
485
- api() {
486
- return this.apiClient;
487
- }
488
- /**
489
- * Quick config check
490
- */
491
- isProduction() {
492
- return this.apiClient.getConfig().environment === 'production';
493
- }
494
- }
495
- /**
496
- * Simple factory function
497
- */
498
- function createPersSDK(httpClient, config) {
499
- return new PersSDK(httpClient, config);
500
- }
501
-
502
- /**
503
- * Platform-Agnostic Business API Client
504
- *
505
- * Updated to match the actual RESTful endpoints:
506
- * - /businesses for business operations
507
- * - /business-types for business type operations (separate controller)
508
- */
509
- class BusinessApi {
510
- constructor(apiClient) {
511
- this.apiClient = apiClient;
512
- this.basePath = '/businesses';
513
- this.businessTypesPath = '/business-types'; // ✅ FIX: Separate controller
514
- }
515
- // ==========================================
516
- // 🌐 BUSINESS TYPES MANAGEMENT
517
- // ==========================================
518
- /**
519
- * Get all business types (project key required)
520
- *
521
- * Endpoint: GET /business-types
522
- * Auth: @ApiSecurity('projectKey')
523
- */
524
- async getAllBusinessTypes() {
525
- return this.apiClient.get(this.businessTypesPath); // ✅ FIX: Correct path
526
- }
527
- /**
528
- * ADMIN: Create business type
529
- *
530
- * Endpoint: POST /business-types
531
- * Auth: @TenantAdmin()
532
- */
533
- async createBusinessType(dto) {
534
- return this.apiClient.post(this.businessTypesPath, dto); // ✅ FIX: Correct path
535
- }
536
- /**
537
- * ADMIN: Update business type
538
- *
539
- * Endpoint: PUT /business-types
540
- * Auth: @TenantAdmin()
541
- */
542
- async updateBusinessType(dto) {
543
- return this.apiClient.put(this.businessTypesPath, dto); // ✅ FIX: Correct path
544
- }
545
- /**
546
- * ADMIN: Delete business type
547
- *
548
- * Endpoint: DELETE /business-types/{id}
549
- * Auth: @TenantAdmin()
550
- */
551
- async deleteBusinessType(id) {
552
- return this.apiClient.delete(`${this.businessTypesPath}/${id}`); // ✅ FIX: Correct path
553
- }
554
- // ==========================================
555
- // 🏢 BUSINESS MANAGEMENT
556
- // ==========================================
557
- /**
558
- * Get current business info (business authentication required)
559
- *
560
- * Endpoint: GET /businesses/me
561
- * Auth: @Business()
562
- */
563
- async getCurrentBusiness() {
564
- return this.apiClient.get(`${this.basePath}/me`);
565
- }
566
- /**
567
- * Get all businesses with role-based filtering
568
- *
569
- * Endpoint: GET /businesses?active={boolean}&sanitize={mode}
570
- * Auth: @ApiSecurity('projectKey') (enhanced with role-based filtering)
571
- * Note:
572
- * - Project API Key users: Active businesses only (automatically filtered)
573
- * - Admin JWT users: Full access with all query parameters
574
- */
575
- async getAllBusinesses(options) {
576
- const params = new URLSearchParams();
577
- if (options?.active !== undefined) {
578
- params.append('active', String(options.active));
579
- }
580
- if (options?.sanitize) {
581
- params.append('sanitize', options.sanitize);
582
- }
583
- const queryString = params.toString();
584
- const url = queryString ? `${this.basePath}?${queryString}` : this.basePath;
585
- return this.apiClient.get(url);
586
- }
587
- /**
588
- * Get all active businesses (convenience method)
589
- *
590
- * Endpoint: GET /businesses
591
- * Auth: @ApiSecurity('projectKey')
592
- */
593
- async getActiveBusinesses() {
594
- return this.apiClient.get(this.basePath);
595
- }
596
- // ✅ REMOVED: getAllBusinessesAdmin() - No separate /admin endpoint exists
597
- // The unified endpoint handles role-based access automatically
598
- /**
599
- * Get business by ID
600
- *
601
- * Endpoint: GET /businesses/{id}
602
- * Auth: @ApiSecurity('projectKey')
603
- */
604
- async getBusinessById(businessId) {
605
- return this.apiClient.get(`${this.basePath}/${businessId}`);
606
- }
607
- /**
608
- * Get business by account address
609
- *
610
- * Endpoint: GET /businesses/account/{accountAddress}
611
- * Auth: @ApiSecurity('projectKey')
612
- */
613
- async getBusinessByAccount(accountAddress) {
614
- return this.apiClient.get(`${this.basePath}/account/${accountAddress}`);
615
- }
616
- // ==========================================
617
- // 🔧 ADMIN OPERATIONS
618
- // ==========================================
619
- /**
620
- * ADMIN: Create business
621
- *
622
- * Endpoint: POST /businesses
623
- * Auth: @TenantAdmin()
624
- * Returns: BusinessApiKeyDTO | BusinessTokenBalancesDTO
625
- */
626
- async createBusiness(dto) {
627
- return this.apiClient.post(this.basePath, dto);
628
- }
629
- /**
630
- * ADMIN: Create business by display name (convenience method)
631
- */
632
- async createBusinessByDisplayName(displayName) {
633
- const dto = {
634
- displayName,
635
- // Add other required fields based on BusinessCreateRequestDTO structure
636
- };
637
- return this.createBusiness(dto);
638
- }
639
- /**
640
- * ADMIN: Create businesses from URL
641
- *
642
- * Endpoint: POST /businesses/bulk/url
643
- * Auth: @TenantAdmin()
644
- */
645
- async createBusinessesFromUrl(url) {
646
- return this.apiClient.post(`${this.basePath}/bulk/url`, { url });
647
- }
648
- /**
649
- * ADMIN: Update business
650
- *
651
- * Endpoint: PUT /businesses/{id}
652
- * Auth: @TenantAdmin()
653
- */
654
- async updateBusiness(id, businessData) {
655
- return this.apiClient.put(`${this.basePath}/${id}`, businessData);
656
- }
657
- /**
658
- * ADMIN: Toggle business active status
659
- *
660
- * Endpoint: PUT /businesses/{id}/status
661
- * Auth: @TenantAdmin()
662
- */
663
- async toggleBusinessActive(id, isActive) {
664
- const dto = { isActive };
665
- return this.apiClient.put(`${this.basePath}/${id}/status`, dto); // ✅ FIX: Correct endpoint
666
- }
667
- }
668
-
669
- /**
670
- * Platform-Agnostic Business Service
671
- *
672
- * Contains business logic and operations that work across platforms.
673
- * No framework dependencies - pure TypeScript business logic.
674
- *
675
- * Focuses only on actual backend capabilities.
676
- */
677
- class BusinessService {
678
- constructor(businessApi) {
679
- this.businessApi = businessApi;
680
- }
681
- /**
682
- * Get all active businesses
683
- */
684
- async getActiveBusinesses() {
685
- return this.businessApi.getActiveBusinesses();
686
- }
687
- /**
688
- * Get all business types
689
- */
690
- async getAllBusinessTypes() {
691
- return this.businessApi.getAllBusinessTypes();
692
- }
693
- /**
694
- * Get business by ID
695
- */
696
- async getBusinessById(businessId) {
697
- return this.businessApi.getBusinessById(businessId);
698
- }
699
- /**
700
- * Get business by account address
701
- */
702
- async getBusinessByAccount(accountAddress) {
703
- return this.businessApi.getBusinessByAccount(accountAddress);
704
- }
705
- /**
706
- * Get businesses by type (client-side filtering)
707
- */
708
- async getBusinessesByType(typeId) {
709
- const businesses = await this.getActiveBusinesses();
710
- return businesses.filter(business => business.businessType && business.businessType.id === parseInt(typeId));
711
- }
712
- // ==========================================
713
- // ADMIN OPERATIONS
714
- // ==========================================
715
- /**
716
- * ADMIN: Get all businesses (active and inactive)
717
- */
718
- async getAllBusinesses() {
719
- return this.businessApi.getAllBusinesses();
720
- }
721
- /**
722
- * ADMIN: Create business by display name
723
- */
724
- async createBusinessByDisplayName(displayName) {
725
- return this.businessApi.createBusinessByDisplayName(displayName);
726
- }
727
- /**
728
- * ADMIN: Update business
729
- */
730
- async updateBusiness(id, businessData) {
731
- return this.businessApi.updateBusiness(id, businessData);
732
- }
733
- /**
734
- * ADMIN: Toggle business active status
735
- */
736
- async toggleBusinessActive(id, isActive) {
737
- return this.businessApi.toggleBusinessActive(id, isActive);
738
- }
739
- }
740
-
741
- /**
742
- * @explorins/pers-sdk-business
743
- *
744
- * Platform-agnostic Business Domain SDK for PERS ecosystem
745
- * Focuses on non-admin business operations
746
- */
747
- // API Layer
748
- /**
749
- * Create a complete Business SDK instance
750
- *
751
- * @param apiClient - Configured PERS API client
752
- * @returns Business SDK with flattened structure for better DX
753
- */
754
- function createBusinessSDK(apiClient) {
755
- const businessApi = new BusinessApi(apiClient);
756
- const businessService = new BusinessService(businessApi);
757
- return {
758
- // Direct access to service methods (primary interface)
759
- getActiveBusinesses: () => businessService.getActiveBusinesses(),
760
- getAllBusinessTypes: () => businessService.getAllBusinessTypes(),
761
- getBusinessById: (businessId) => businessService.getBusinessById(businessId),
762
- getBusinessByAccount: (accountAddress) => businessService.getBusinessByAccount(accountAddress),
763
- getBusinessesByType: (typeId) => businessService.getBusinessesByType(typeId),
764
- // Admin methods
765
- getAllBusinesses: () => businessService.getAllBusinesses(),
766
- createBusinessByDisplayName: (displayName) => businessService.createBusinessByDisplayName(displayName),
767
- updateBusiness: (id, businessData) => businessService.updateBusiness(id, businessData),
768
- toggleBusinessActive: (id, isActive) => businessService.toggleBusinessActive(id, isActive),
769
- // Advanced access for edge cases
770
- api: businessApi,
771
- service: businessService
772
- };
773
- }
774
-
775
- /**
776
- * Platform-Agnostic Transaction API Client (UPDATED FOR NEW RESTful ENDPOINTS)
777
- *
778
- * Handles transaction operations using the PERS backend.
779
- * Uses @explorins/pers-shared DTOs for consistency with backend.
780
- *
781
- * MIGRATION NOTES:
782
- * - All endpoints changed from /transaction to /transactions
783
- * - Role-based paths removed (no more /auth, /admin, /business in URLs)
784
- * - New RESTful resource-based structure
785
- * - Added new client-side transaction flow methods
786
- * - Enhanced admin query capabilities
787
- */
788
- class TransactionApi {
789
- constructor(apiClient) {
790
- this.apiClient = apiClient;
791
- this.basePath = '/transactions';
792
- }
793
- /**
794
- * Get transaction by ID (public endpoint)
795
- *
796
- * UPDATED: /transaction/{id} → /transactions/{id}
797
- */
798
- async getTransactionById(transactionId) {
799
- return this.apiClient.get(`${this.basePath}/${transactionId}`);
800
- }
801
- /**
802
- * Unique method to create a transaction
803
- * @param request
804
- * @returns
805
- */
806
- async createTransaction(request) {
807
- return this.apiClient.post(`${this.basePath}`, request);
808
- // return this.apiClient.post<TransactionDTO>(`${this.basePath}/system`, request);
809
- }
810
- // ==========================================
811
- // AUTHENTICATED USER OPERATIONS
812
- // ==========================================
813
- /**
814
- * AUTH: Create authenticated user transaction
815
- *
816
- * UPDATED: /transaction/auth/transaction → /transactions/user
817
- */
818
- /* async createAuthTransaction(request: TransactionRequestDTO): Promise<TransactionRequestResponseDTO> {
819
- return this.apiClient.post<TransactionRequestResponseDTO>(`${this.basePath}`, request);
820
- } */
821
- /**
822
- * AUTH: Get user's sent transactions
823
- *
824
- * UPDATED: /transaction/auth/sender → /transactions/me/sent
825
- */
826
- async getUserSentTransactions() {
827
- const params = new URLSearchParams({
828
- timestamp: Date.now().toString()
829
- });
830
- return this.apiClient.get(`${this.basePath}/me/sent?${params.toString()}`);
831
- }
832
- /**
833
- * AUTH: Get user's received transactions
834
- *
835
- * UPDATED: /transaction/auth/recipient → /transactions/me/received
836
- */
837
- async getUserReceivedTransactions() {
838
- const params = new URLSearchParams({
839
- timestamp: Date.now().toString()
840
- });
841
- return this.apiClient.get(`${this.basePath}/me/received?${params.toString()}`);
842
- }
843
- /**
844
- * AUTH: Get user transaction history by type (backwards compatibility)
845
- *
846
- * UPDATED: Maps to appropriate /transactions/me/* endpoints
847
- */
848
- async getUserTransactionHistory(type) {
849
- const params = new URLSearchParams({
850
- timestamp: Date.now().toString()
851
- });
852
- // Map legacy type parameter to new endpoints
853
- switch (type.toLowerCase()) {
854
- case 'sender':
855
- case 'sent':
856
- return this.apiClient.get(`${this.basePath}/me/sent?${params.toString()}`);
857
- case 'recipient':
858
- case 'received':
859
- return this.apiClient.get(`${this.basePath}/me/received?${params.toString()}`);
860
- default:
861
- // Default to sent transactions for backwards compatibility
862
- return this.apiClient.get(`${this.basePath}/me/sent?${params.toString()}`);
863
- }
864
- }
865
- /**
866
- * AUTH: Prepare existing transaction for client-side signing
867
- *
868
- * NEW ENDPOINT: GET /transactions/{id}/prepare
869
- */
870
- async prepareExistingTransaction(transactionId) {
871
- return this.apiClient.get(`${this.basePath}/${transactionId}/prepare`);
872
- }
873
- /**
874
- * AUTH: Submit client-side signed transaction
875
- *
876
- * NEW ENDPOINT: POST /transactions/{id}/submit
877
- */
878
- async submitSignedTransaction(signedTxData) {
879
- return this.apiClient.post(`${this.basePath}/submit`, signedTxData);
880
- }
881
- /**
882
- * AUTH: Burn user tokens
883
- *
884
- * UPDATED: Uses new user transaction endpoint with burn-specific parameters
885
- * Note: This might need backend confirmation on burn functionality implementation
886
- */
887
- /* async burnUserTokens(request: UserBurnTokenRequestDTO): Promise<TransactionRequestResponseDTO> {
888
- // Map burn request to TransactionRequestDTO format for new endpoint
889
- const transactionRequest: TransactionRequestDTO = {
890
- ...request,
891
- // Add any specific burn transaction parameters here
892
- } as any;
893
-
894
- return this.apiClient.post<TransactionRequestResponseDTO>(`${this.basePath}`, transactionRequest);
895
- } */
896
- // ==========================================
897
- // BUSINESS OPERATIONS
898
- // ==========================================
899
- /**
900
- * BUSINESS: Create business transaction
901
- *
902
- * UPDATED: /transaction/business/transaction → /transactions/business
903
- */
904
- /* async createBusinessTransaction(request: TransactionRequestDTO): Promise<TransactionRequestResponseDTO> {
905
- return this.apiClient.post<TransactionRequestResponseDTO>(`${this.basePath}`, request);
906
- } */
907
- // ==========================================
908
- // ADMIN OPERATIONS
909
- // ==========================================
910
- /**
911
- * ADMIN: Create admin transaction
912
- *
913
- * UPDATED: /transaction/admin/transaction → /transactions/admin
914
- */
915
- /* async createAdminTransaction(request: TransactionRequestDTO): Promise<TransactionRequestResponseDTO> {
916
- // return this.apiClient.post<TransactionRequestResponseDTO>(`${this.basePath}`, request);
917
- return this.apiClient.post<TransactionDTO>(`${this.basePath}/system`, request);
918
- } */
919
- /**
920
- * AUTH: Prepare client signed transaction
921
- *
922
- * UPDATED: /transaction/auth/prepare-signing → /transactions/prepare
923
- */
924
- async prepareClientSignedTransaction(request) {
925
- return this.apiClient.post(`${this.basePath}`, request);
926
- }
927
- /**
928
- * ADMIN: Get all tenant transactions
929
- *
930
- * UPDATED: /transaction/admin → /transactions
931
- */
932
- async getTenantTransactions() {
933
- const result = await this.apiClient.get(`${this.basePath}`);
934
- // If the new endpoint returns paginated response, extract the data array
935
- if ('data' in result) {
936
- return result.data;
937
- }
938
- // Fallback for direct array response
939
- return result;
940
- }
941
- /**
942
- * ADMIN: Get paginated transactions with filtering and sorting
943
- *
944
- * UPDATED: /transaction/admin → /transactions (same endpoint, better structure)
945
- */
946
- async getPaginatedTransactions(params) {
947
- // Build query parameters
948
- const queryParams = {
949
- page: params.page.toString(),
950
- limit: params.limit.toString()
951
- };
952
- // Add sorting parameters if provided
953
- if (params.sortBy) {
954
- queryParams['sortBy'] = params.sortBy;
955
- }
956
- if (params.sortOrder) {
957
- queryParams['sortOrder'] = params.sortOrder;
958
- }
959
- // Add user-specific filtering if provided
960
- if (params.participantId) {
961
- queryParams['participantId'] = params.participantId;
962
- }
963
- // Add status filtering if provided
964
- if (params.status) {
965
- queryParams['status'] = params.status;
966
- }
967
- // Add additional filters if provided
968
- if (params.filters) {
969
- if (params.filters.startDate) {
970
- queryParams['startDate'] = params.filters.startDate;
971
- }
972
- if (params.filters.endDate) {
973
- queryParams['endDate'] = params.filters.endDate;
974
- }
975
- if (params.filters.type && params.filters.type.length > 0) {
976
- queryParams['type'] = params.filters.type.join(',');
977
- }
978
- if (params.filters.tokenType && params.filters.tokenType.length > 0) {
979
- queryParams['tokenType'] = params.filters.tokenType.join(',');
980
- }
981
- }
982
- // Build query string
983
- const queryString = Object.entries(queryParams)
984
- .map(([key, value]) => `${key}=${encodeURIComponent(value)}`)
985
- .join('&');
986
- return this.apiClient.get(`${this.basePath}?${queryString}`);
987
- }
988
- /**
989
- * ADMIN: Export transactions to CSV
990
- *
991
- * UPDATED: /transaction/admin/export/csv → /transactions/export/csv
992
- */
993
- async exportTransactionsCSV() {
994
- return this.apiClient.get(`${this.basePath}/export/csv`, 'blob');
995
- }
996
- // ==========================================
997
- // NEW ADMIN QUERY METHODS
998
- // ==========================================
999
- /**
1000
- * ADMIN: Query transactions by sender
1001
- *
1002
- * NEW ENDPOINT: POST /transactions/query-sender
1003
- */
1004
- /**
1005
- * Query transactions by sender using unified endpoint
1006
- */
1007
- async queryTransactionsBySender(accountSelector) {
1008
- // Build query parameters safely
1009
- const queryParams = {};
1010
- if (accountSelector.accountId) {
1011
- queryParams['participantId'] = accountSelector.accountId;
1012
- }
1013
- queryParams['role'] = TransactionRole.SENDER.toString();
1014
- const params = new URLSearchParams(queryParams);
1015
- return this.apiClient.get(`${this.basePath}?${params.toString()}`).then(response => response); // Extract items from paginated response
1016
- }
1017
- /**
1018
- * Query transactions by recipient using unified endpoint
1019
- */
1020
- async queryTransactionsByRecipient(accountSelector) {
1021
- // Build query parameters safely
1022
- const queryParams = {};
1023
- if (accountSelector.accountId) {
1024
- queryParams['participantId'] = accountSelector.accountId;
1025
- }
1026
- queryParams['role'] = TransactionRole.RECIPIENT.toString();
1027
- const params = new URLSearchParams(queryParams);
1028
- return this.apiClient.get(`${this.basePath}?${params.toString()}`).then(response => response); // Extract items from paginated response
1029
- }
1030
- /**
1031
- * ADMIN: Get transaction analytics
1032
- *
1033
- * NEW ENDPOINT: POST /transactions/analytics
1034
- */
1035
- async getTransactionAnalytics(analyticsRequest) {
1036
- return this.apiClient.post(`${this.basePath}/analytics`, analyticsRequest);
1037
- }
1038
- // ==========================================
1039
- // CONVENIENCE METHODS (BACKWARDS COMPATIBILITY)
1040
- // ==========================================
1041
- /**
1042
- * Convenience method: Get user sent transactions (alias)
1043
- */
1044
- async getUserSenderTransactions() {
1045
- return this.getUserSentTransactions();
1046
- }
1047
- /**
1048
- * Convenience method: Get user received transactions (alias)
1049
- */
1050
- async getUserRecipientTransactions() {
1051
- return this.getUserReceivedTransactions();
1052
- }
1053
- }
1054
-
1055
- /**
1056
- * Platform-Agnostic Transaction Service
1057
- *
1058
- * Contains transaction business logic and operations that work across platforms.
1059
- * No framework dependencies - pure TypeScript business logic.
1060
- *
1061
- * Focuses only on actual backend capabilities.
1062
- */
1063
- class TransactionService {
1064
- constructor(transactionApi) {
1065
- this.transactionApi = transactionApi;
1066
- }
1067
- /**
1068
- * Get transaction by ID
1069
- */
1070
- async getTransactionById(transactionId) {
1071
- return this.transactionApi.getTransactionById(transactionId);
1072
- }
1073
- // ==========================================
1074
- // AUTHENTICATED OPERATIONS
1075
- // ==========================================
1076
- /**
1077
- * AUTH: Create authenticated transaction
1078
- */
1079
- /* async createAuthTransaction(request: TransactionRequestDTO): Promise<TransactionRequestResponseDTO> {
1080
- return this.transactionApi.createAuthTransaction(request);
1081
- } */
1082
- async createTransaction(request) {
1083
- return this.transactionApi.createTransaction(request);
1084
- }
1085
- async submitSignedTransaction(signedTxData) {
1086
- return this.transactionApi.submitSignedTransaction(signedTxData);
1087
- }
1088
- /**
1089
- * AUTH: Get user transaction history by type
1090
- */
1091
- async getUserTransactionHistory(type) {
1092
- return this.transactionApi.getUserTransactionHistory(type);
1093
- }
1094
- /**
1095
- * AUTH: Prepare client signed transaction
1096
- */
1097
- /* async prepareClientSignedTransaction(request: TransactionRequestDTO): Promise<TransactionRequestResponseDTO> {
1098
- return this.transactionApi.prepareClientSignedTransaction(request);
1099
- } */
1100
- /**
1101
- * AUTH: Burn user tokens
1102
- */
1103
- /* async burnUserTokens(request: UserBurnTokenRequestDTO): Promise<TransactionRequestResponseDTO> {
1104
- return this.transactionApi.burnUserTokens(request);
1105
- } */
1106
- // ==========================================
1107
- // BUSINESS OPERATIONS
1108
- // ==========================================
1109
- /**
1110
- * BUSINESS: Create business transaction
1111
- */
1112
- /* async createBusinessTransaction(request: TransactionRequestDTO): Promise<TransactionRequestResponseDTO> {
1113
- return this.transactionApi.createBusinessTransaction(request);
1114
- } */
1115
- // ==========================================
1116
- // ADMIN OPERATIONS
1117
- // ==========================================
1118
- /**
1119
- * ADMIN: Create admin transaction
1120
- */
1121
- /* async createAdminTransaction(request: TransactionRequestDTO): Promise<TransactionRequestResponseDTO> {
1122
- return this.transactionApi.createAdminTransaction(request);
1123
- } */
1124
- /**
1125
- * ADMIN: Get all tenant transactions
1126
- */
1127
- async getTenantTransactions() {
1128
- return this.transactionApi.getTenantTransactions();
1129
- }
1130
- /**
1131
- * ADMIN: Get paginated transactions with filtering and sorting
1132
- */
1133
- async getPaginatedTransactions(params) {
1134
- return this.transactionApi.getPaginatedTransactions(params);
1135
- }
1136
- /**
1137
- * ADMIN: Export transactions to CSV
1138
- */
1139
- async exportTransactionsCSV() {
1140
- return this.transactionApi.exportTransactionsCSV();
1141
- }
1142
- }
1143
-
1144
- /**
1145
- * Transaction Domain Models
1146
- *
1147
- * Re-exports from @explorins/pers-shared for consistency with backend
1148
- * and to provide a single import source for transaction-related types.
1149
- */
1150
- // Transaction account types (domain-specific enum)
1151
- var TransactionAccountType;
1152
- (function (TransactionAccountType) {
1153
- // Add specific transaction account types as needed
1154
- // This should match the enum used in the infrastructure layer
1155
- })(TransactionAccountType || (TransactionAccountType = {}));
1156
-
1157
- /**
1158
- * @explorins/pers-sdk-transaction
1159
- *
1160
- * Platform-agnostic Transaction Domain SDK for PERS ecosystem
1161
- * Handles transaction operations across different authorization levels
1162
- */
1163
- // API Layer
1164
- /**
1165
- * Create a complete Transaction SDK instance
1166
- *
1167
- * @param apiClient - Configured PERS API client
1168
- * @returns Transaction SDK with flattened structure for better DX
1169
- */
1170
- function createTransactionSDK(apiClient) {
1171
- const transactionApi = new TransactionApi(apiClient);
1172
- const transactionService = new TransactionService(transactionApi);
1173
- return {
1174
- // Direct access to service methods (primary interface)
1175
- // Public methods
1176
- getTransactionById: (transactionId) => transactionService.getTransactionById(transactionId),
1177
- createTransaction: (request) => transactionService.createTransaction(request),
1178
- // Auth methods
1179
- // createAuthTransaction: (request: TransactionRequestDTO) => transactionService.createAuthTransaction(request),
1180
- getUserTransactionHistory: (type) => transactionService.getUserTransactionHistory(type),
1181
- //prepareClientSignedTransaction: (request: TransactionRequestDTO) => transactionService.prepareClientSignedTransaction(request),
1182
- // burnUserTokens: (request: UserBurnTokenRequestDTO) => transactionService.burnUserTokens(request),
1183
- // Business methods
1184
- // createBusinessTransaction: (request: TransactionRequestDTO) => transactionService.createBusinessTransaction(request),
1185
- // Admin methods
1186
- // createAdminTransaction: (request: TransactionRequestDTO) => transactionService.createAdminTransaction(request),
1187
- getTenantTransactions: () => transactionService.getTenantTransactions(),
1188
- getPaginatedTransactions: (params) => transactionService.getPaginatedTransactions(params),
1189
- exportTransactionsCSV: () => transactionService.exportTransactionsCSV(),
1190
- // Advanced access for edge cases
1191
- api: transactionApi,
1192
- service: transactionService
1193
- };
1194
- }
1195
-
1196
- /**
1197
- * Platform-Agnostic Analytics API Client
1198
- *
1199
- * Handles analytics operations using the PERS backend.
1200
- * Uses @explorins/pers-shared DTOs for consistency with backend.
1201
- */
1202
- class AnalyticsApi {
1203
- constructor(apiClient) {
1204
- this.apiClient = apiClient;
1205
- }
1206
- // ==========================================
1207
- // ADMIN OPERATIONS
1208
- // ==========================================
1209
- /**
1210
- * ADMIN: Get transaction analytics with filtering and aggregation
1211
- */
1212
- async getTransactionAnalytics(request) {
1213
- return this.apiClient.post('/transactions/analytics', request);
1214
- }
1215
- }
1216
-
1217
- /**
1218
- * Platform-Agnostic Analytics Service
1219
- *
1220
- * Contains analytics business logic and operations that work across platforms.
1221
- * No framework dependencies - pure TypeScript business logic.
1222
- *
1223
- * Focuses only on actual backend capabilities.
1224
- */
1225
- class AnalyticsService {
1226
- constructor(analyticsApi) {
1227
- this.analyticsApi = analyticsApi;
1228
- }
1229
- // ==========================================
1230
- // ADMIN OPERATIONS
1231
- // ==========================================
1232
- /**
1233
- * ADMIN: Get transaction analytics with filtering and aggregation
1234
- */
1235
- async getTransactionAnalytics(request) {
1236
- return this.analyticsApi.getTransactionAnalytics(request);
1237
- }
1238
- }
1239
-
1240
- /**
1241
- * @explorins/pers-sdk-analytics
1242
- *
1243
- * Platform-agnostic Analytics Domain SDK for PERS ecosystem
1244
- * Handles analytics operations and data aggregation
1245
- */
1246
- // API Layer
1247
- /**
1248
- * Create a complete Analytics SDK instance
1249
- *
1250
- * @param apiClient - Configured PERS API client
1251
- * @returns Analytics SDK with flattened structure for better DX
1252
- */
1253
- function createAnalyticsSDK(apiClient) {
1254
- const analyticsApi = new AnalyticsApi(apiClient);
1255
- const analyticsService = new AnalyticsService(analyticsApi);
1256
- return {
1257
- // Direct access to service methods (primary interface)
1258
- // Admin methods
1259
- getTransactionAnalytics: (request) => analyticsService.getTransactionAnalytics(request),
1260
- // Advanced access for edge cases
1261
- api: analyticsApi,
1262
- service: analyticsService
1263
- };
1264
- }
1265
-
1266
- /**
1267
- * Platform-Agnostic Auth Admin API Client
1268
- *
1269
- * Handles authentication and authorization admin operations using the PERS backend.
1270
- * Uses @explorins/pers-shared DTOs for consistency with backend.
1271
- *
1272
- * Note: Special header handling (bypass-auth-interceptor) may need to be implemented
1273
- * at the PersApiClient level or through a specialized auth client.
1274
- */
1275
- class AuthAdminApi {
1276
- constructor(apiClient) {
1277
- this.apiClient = apiClient;
1278
- this.basePath = '/auth';
1279
- }
1280
- // ==========================================
1281
- // ADMIN AUTHENTICATION OPERATIONS
1282
- // ==========================================
1283
- /**
1284
- * ADMIN: Login tenant admin with JWT
1285
- * Note: JWT handling and auth bypass headers may need special implementation
1286
- */
1287
- async loginTenantAdmin(jwt) {
1288
- const body = {
1289
- authToken: jwt,
1290
- authType: AccountOwnerType.TENANT
1291
- };
1292
- return this.apiClient.post(`${this.basePath}/token`, body);
1293
- }
1294
- async loginUser(jwt) {
1295
- const body = {
1296
- authToken: jwt,
1297
- authType: AccountOwnerType.USER
1298
- };
1299
- return this.apiClient.post(`${this.basePath}/token`, body);
1300
- }
1301
- /**
1302
- * ADMIN: Refresh access token
1303
- * Note: Bypass header handling may need special implementation
1304
- */
1305
- async refreshAccessToken(refreshToken) {
1306
- return this.apiClient.post(`${this.basePath}/refresh`, { refreshToken });
1307
- }
1308
- }
1309
-
1310
- /**
1311
- * Platform-Agnostic Auth Admin Service
1312
- *
1313
- * Contains auth admin business logic and operations that work across platforms.
1314
- * No framework dependencies - pure TypeScript business logic.
1315
- *
1316
- * Focuses only on actual backend capabilities.
1317
- */
1318
- class AuthAdminService {
1319
- constructor(authAdminApi) {
1320
- this.authAdminApi = authAdminApi;
1321
- }
1322
- // ==========================================
1323
- // ADMIN AUTHENTICATION OPERATIONS
1324
- // ==========================================
1325
- /**
1326
- * ADMIN: Login tenant admin with JWT
1327
- */
1328
- async loginTenantAdmin(jwt) {
1329
- return this.authAdminApi.loginTenantAdmin(jwt);
1330
- }
1331
- /**
1332
- * ADMIN: Login user with JWT
1333
- */
1334
- async loginUser(jwt) {
1335
- return this.authAdminApi.loginUser(jwt);
1336
- }
1337
- /**
1338
- * ADMIN: Refresh access token
1339
- */
1340
- async refreshAccessToken(refreshToken) {
1341
- return this.authAdminApi.refreshAccessToken(refreshToken);
1342
- }
1343
- }
1344
-
1345
- /**
1346
- * @explorins/pers-sdk-auth-admin
1347
- *
1348
- * Platform-agnostic Auth Admin Domain SDK for PERS ecosystem
1349
- * Handles authentication and authorization admin operations
1350
- */
1351
- // API Layer
1352
- /**
1353
- * Create a complete Auth Admin SDK instance
1354
- *
1355
- * @param apiClient - Configured PERS API client
1356
- * @returns Auth Admin SDK with flattened structure for better DX
1357
- */
1358
- function createAuthAdminSDK(apiClient) {
1359
- const authAdminApi = new AuthAdminApi(apiClient);
1360
- const authAdminService = new AuthAdminService(authAdminApi);
1361
- return {
1362
- // Direct access to service methods (primary interface)
1363
- // Admin authentication methods
1364
- loginTenantAdmin: (jwt) => authAdminService.loginTenantAdmin(jwt),
1365
- loginUser: (jwt) => authAdminService.loginUser(jwt),
1366
- refreshAccessToken: (refreshToken) => authAdminService.refreshAccessToken(refreshToken),
1367
- // Advanced access for edge cases
1368
- api: authAdminApi,
1369
- service: authAdminService
1370
- };
1371
- }
1372
-
1373
- /**
1374
- * Platform-Agnostic Campaign API Client (NEW - RESTful Design)
1375
- *
1376
- * Updated to use the new microservice-ready campaign controllers:
1377
- * - CampaignsController: Core campaign operations
1378
- * - CampaignTagsController: Tag management
1379
- * - CampaignTokensController: Token unit operations
1380
- * - CampaignTriggersController: Trigger system
1381
- * - CampaignEngagementsController: Business relationships
1382
- * - CampaignClaimsController: Claims processing
1383
- *
1384
- * Uses @explorins/pers-shared DTOs for consistency with backend.
1385
- * All endpoints updated to new RESTful patterns without role revelation.
1386
- */
1387
- class CampaignApi {
1388
- constructor(apiClient) {
1389
- this.apiClient = apiClient;
1390
- }
1391
- // ==========================================
1392
- // CORE CAMPAIGN OPERATIONS (/campaigns)
1393
- // ==========================================
1394
- /**
1395
- * PUBLIC: Get all active campaigns
1396
- * NEW: /campaigns (intelligent access detection)
1397
- */
1398
- async getActiveCampaigns() {
1399
- return this.apiClient.get('/campaigns');
1400
- }
1401
- /**
1402
- * ADMIN: Get campaigns with filtering options
1403
- * NEW: /campaigns with query parameters (admin access detected automatically)
1404
- */
1405
- async getCampaigns(options) {
1406
- let url = '/campaigns';
1407
- const params = [];
1408
- if (options) {
1409
- if (options.active !== undefined)
1410
- params.push(`active=${options.active}`);
1411
- if (options.tag)
1412
- params.push(`tag=${encodeURIComponent(options.tag)}`);
1413
- if (options.limit)
1414
- params.push(`limit=${options.limit}`);
1415
- if (options.offset)
1416
- params.push(`offset=${options.offset}`);
1417
- if (options.sort)
1418
- params.push(`sort=${options.sort}`);
1419
- if (options.order)
1420
- params.push(`order=${options.order}`);
1421
- }
1422
- if (params.length > 0) {
1423
- url += `?${params.join('&')}`;
1424
- }
1425
- return this.apiClient.get(url);
1426
- }
1427
- /**
1428
- * PUBLIC: Get campaign by ID
1429
- * NEW: /campaigns/{id}
1430
- */
1431
- async getCampaignById(id) {
1432
- return this.apiClient.get(`/campaigns/${id}`);
1433
- }
1434
- /**
1435
- * ADMIN: Create campaign
1436
- * NEW: POST /campaigns
1437
- */
1438
- async createCampaign(campaign) {
1439
- return this.apiClient.post('/campaigns', campaign);
1440
- }
1441
- /**
1442
- * ADMIN: Update campaign
1443
- * NEW: PUT /campaigns/{id}
1444
- */
1445
- async updateCampaign(campaignId, campaign) {
1446
- return this.apiClient.put(`/campaigns/${campaignId}`, campaign);
1447
- }
1448
- /**
1449
- * ADMIN: Toggle campaign active status
1450
- * NEW: PUT /campaigns/{id}/status
1451
- */
1452
- async toggleCampaignActive(campaignId) {
1453
- return this.apiClient.put(`/campaigns/${campaignId}/status`, {});
1454
- }
1455
- /**
1456
- * ADMIN: Toggle campaign testnet environment
1457
- * NEW: PUT /campaigns/{id}/environment
1458
- */
1459
- async toggleCampaignTestnet(campaignId) {
1460
- return this.apiClient.put(`/campaigns/${campaignId}/environment`, {});
1461
- }
1462
- /**
1463
- * ADMIN: Delete campaign
1464
- * NEW: DELETE /campaigns/{id}
1465
- */
1466
- async deleteCampaign(campaignId) {
1467
- return this.apiClient.delete(`/campaigns/${campaignId}`);
1468
- }
1469
- // ==========================================
1470
- // TAG MANAGEMENT (/campaign-tags)
1471
- // ==========================================
1472
- /**
1473
- * ADMIN: Get all unique campaign tags
1474
- * NEW: GET /campaign-tags
1475
- */
1476
- async getAllUniqueTags() {
1477
- return this.apiClient.get('/campaign-tags');
1478
- }
1479
- /**
1480
- * ADMIN: Update campaign tags (replace all)
1481
- * NEW: PUT /campaign-tags/{id}
1482
- */
1483
- async updateCampaignTags(campaignId, tags) {
1484
- return this.apiClient.put(`/campaign-tags/${campaignId}`, { tags });
1485
- }
1486
- /**
1487
- * ADMIN: Add tags to campaign
1488
- * NEW: POST /campaign-tags/{id}
1489
- */
1490
- async addTagsToCampaign(campaignId, tags) {
1491
- return this.apiClient.post(`/campaign-tags/${campaignId}`, { tags });
1492
- }
1493
- /**
1494
- * ADMIN: Remove tag from campaign
1495
- * NEW: DELETE /campaign-tags/{id}/{tag}
1496
- */
1497
- async removeTagFromCampaign(campaignId, tag) {
1498
- return this.apiClient.delete(`/campaign-tags/${campaignId}/${encodeURIComponent(tag)}`);
1499
- }
1500
- // ==========================================
1501
- // TOKEN MANAGEMENT (/campaign-tokens)
1502
- // ==========================================
1503
- /**
1504
- * ADMIN: Create campaign token unit
1505
- * NEW: POST /campaign-tokens/{id}
1506
- */
1507
- async createCampaignTokenUnit(campaignId, campaignTokenUnit) {
1508
- return this.apiClient.post(`/campaign-tokens/${campaignId}`, campaignTokenUnit);
1509
- }
1510
- /**
1511
- * ADMIN: Update campaign token unit
1512
- * NEW: PUT /campaign-tokens/{id}/{tokenUnitId}
1513
- */
1514
- async updateCampaignTokenUnit(campaignId, tokenUnitId, campaignTokenUnit) {
1515
- return this.apiClient.put(`/campaign-tokens/${campaignId}/${tokenUnitId}`, campaignTokenUnit);
1516
- }
1517
- /**
1518
- * ADMIN: Delete campaign token unit
1519
- * NEW: DELETE /campaign-tokens/{id}/{tokenUnitId}
1520
- */
1521
- async deleteCampaignTokenUnit(campaignId, campaignTokenUnitId) {
1522
- return this.apiClient.delete(`/campaign-tokens/${campaignId}/${campaignTokenUnitId}`);
1523
- }
1524
- // ==========================================
1525
- // TRIGGER SYSTEM (/campaign-triggers)
1526
- // ==========================================
1527
- /**
1528
- * PUBLIC: Get campaign triggers catalog
1529
- * NEW: GET /campaign-triggers
1530
- */
1531
- async getCampaignTriggers() {
1532
- return this.apiClient.get('/campaign-triggers');
1533
- }
1534
- /**
1535
- * ADMIN: Create campaign trigger
1536
- * NEW: POST /campaign-triggers
1537
- */
1538
- async createCampaignTrigger(trigger) {
1539
- return this.apiClient.post('/campaign-triggers', trigger);
1540
- }
1541
- /**
1542
- * ADMIN: Update campaign trigger
1543
- * NEW: PUT /campaign-triggers/{id}
1544
- */
1545
- async updateCampaignTrigger(triggerId, trigger) {
1546
- return this.apiClient.put(`/campaign-triggers/${triggerId}`, trigger);
1547
- }
1548
- /**
1549
- * ADMIN: Delete campaign trigger
1550
- * NEW: DELETE /campaign-triggers/{id}
1551
- */
1552
- async deleteCampaignTrigger(triggerId) {
1553
- return this.apiClient.delete(`/campaign-triggers/${triggerId}`);
1554
- }
1555
- /**
1556
- * ADMIN: Set campaign trigger
1557
- * NEW: PUT /campaign-triggers/campaigns/{id}/trigger/{triggerId}
1558
- */
1559
- async setCampaignTrigger(campaignId, triggerId) {
1560
- return this.apiClient.put(`/campaign-triggers/campaigns/${campaignId}/trigger/${triggerId}`, {});
1561
- }
1562
- /**
1563
- * ADMIN: Create trigger condition
1564
- * NEW: POST /campaign-triggers/conditions
1565
- */
1566
- async createTriggerCondition(condition) {
1567
- return this.apiClient.post('/campaign-triggers/conditions', condition);
1568
- }
1569
- /**
1570
- * ADMIN: Update trigger condition
1571
- * NEW: PUT /campaign-triggers/conditions/{id}
1572
- */
1573
- async updateTriggerCondition(conditionId, condition) {
1574
- return this.apiClient.put(`/campaign-triggers/conditions/${conditionId}`, condition);
1575
- }
1576
- /**
1577
- * ADMIN: Add/Remove condition to trigger
1578
- * NEW: PUT /campaign-triggers/{triggerId}/condition/{conditionId}
1579
- */
1580
- async addOrRemoveConditionToTrigger(triggerId, conditionId) {
1581
- return this.apiClient.put(`/campaign-triggers/${triggerId}/condition/${conditionId}`, {});
1582
- }
1583
- // ==========================================
1584
- // BUSINESS ENGAGEMENTS (/campaign-engagements)
1585
- // ==========================================
1586
- /**
1587
- * ADMIN: Add business engagement to campaign
1588
- * NEW: POST /campaign-engagements/{id}
1589
- */
1590
- async addBusinessEngagementToCampaign(campaignId, campaignBusinessEngagement) {
1591
- return this.apiClient.post(`/campaign-engagements/${campaignId}`, campaignBusinessEngagement);
1592
- }
1593
- /**
1594
- * ADMIN: Update campaign business engagement
1595
- * NEW: PUT /campaign-engagements/{id}/{businessEngagementId}
1596
- */
1597
- async updateCampaignBusinessEngagement(campaignId, businessEngagementId, campaignBusinessEngagement) {
1598
- return this.apiClient.put(`/campaign-engagements/${campaignId}/${businessEngagementId}`, campaignBusinessEngagement);
1599
- }
1600
- /**
1601
- * ADMIN: Delete campaign business engagement
1602
- * NEW: DELETE /campaign-engagements/{id}/{businessEngagementId}
1603
- */
1604
- async deleteCampaignBusinessEngagement(campaignId, businessEngagementId) {
1605
- return this.apiClient.delete(`/campaign-engagements/${campaignId}/${businessEngagementId}`);
1606
- }
1607
- // ==========================================
1608
- // CLAIMS PROCESSING (/campaign-claims)
1609
- // ==========================================
1610
- /**
1611
- * USER: Claim campaign reward
1612
- * NEW: POST /campaign-claims/user
1613
- */
1614
- async claimCampaign(request) {
1615
- return this.apiClient.post('/campaign-claims', request);
1616
- }
1617
- /**
1618
- * USER: Get claims for logged user
1619
- * NEW: GET /campaign-claims/users/me
1620
- */
1621
- async getClaimsForLoggedUser() {
1622
- return this.apiClient.get('/campaign-claims/me');
1623
- }
1624
- /**
1625
- * ADMIN: Get all campaign claims
1626
- * Updated to use unified endpoint
1627
- */
1628
- async getCampaignClaims() {
1629
- // Admin context - no parameters needed for all claims
1630
- return this.apiClient.get('/campaign-claims');
1631
- }
1632
- /**
1633
- * ADMIN: Get campaign claims by campaign ID
1634
- * Updated to use query parameters
1635
- */
1636
- async getCampaignClaimsByCampaignId(campaignId) {
1637
- return this.apiClient.get(`/campaign-claims?campaignId=${campaignId}`);
1638
- }
1639
- /**
1640
- * ADMIN: Get campaign claims by user ID
1641
- * Updated to use query parameters
1642
- */
1643
- async getCampaignClaimsByUserId(userId) {
1644
- return this.apiClient.get(`/campaign-claims?userId=${userId}`);
1645
- }
1646
- /**
1647
- * ADMIN: Get campaign claims by business ID
1648
- * Updated to use query parameters
1649
- */
1650
- async getCampaignClaimsByBusinessId(businessId) {
1651
- return this.apiClient.get(`/campaign-claims?businessId=${businessId}`);
1652
- }
1653
- /**
1654
- * ADMIN: Get campaign claims by user ID for specific campaign
1655
- * Combined filtering using query parameters
1656
- */
1657
- async getCampaignClaimsByUserAndCampaign(userId, campaignId) {
1658
- return this.apiClient.get(`/campaign-claims?userId=${userId}&campaignId=${campaignId}`);
1659
- }
1660
- /**
1661
- * ADMIN: Get campaign claims by business ID for specific campaign
1662
- * Combined filtering using query parameters
1663
- */
1664
- async getCampaignClaimsByBusinessAndCampaign(businessId, campaignId) {
1665
- return this.apiClient.get(`/campaign-claims?businessId=${businessId}&campaignId=${campaignId}`);
1666
- }
1667
- /**
1668
- * USER: Get user's own claims (all campaigns)
1669
- * Use convenience endpoint
1670
- */
1671
- async getUserClaims() {
1672
- return this.apiClient.get('/campaign-claims/me');
1673
- }
1674
- /**
1675
- * USER: Get user's claims for specific campaign
1676
- * Use convenience endpoint with query parameter
1677
- */
1678
- async getUserClaimsForCampaign(campaignId) {
1679
- return this.apiClient.get(`/campaign-claims/me?campaignId=${campaignId}`);
1680
- }
1681
- /**
1682
- * BUSINESS: Get business claims (all campaigns)
1683
- * Uses unified endpoint with business context
1684
- */
1685
- async getBusinessClaims() {
1686
- return this.apiClient.get('/campaign-claims');
1687
- }
1688
- /**
1689
- * BUSINESS: Get business claims for specific campaign
1690
- * Uses unified endpoint with business context and campaign filter
1691
- */
1692
- async getBusinessClaimsForCampaign(campaignId) {
1693
- return this.apiClient.get(`/campaign-claims?campaignId=${campaignId}`);
1694
- }
1695
- /**
1696
- * Helper: Build query string from parameters
1697
- */
1698
- buildQueryString(params) {
1699
- const validParams = Object.entries(params)
1700
- .filter(([_, value]) => value !== undefined)
1701
- .map(([key, value]) => `${key}=${encodeURIComponent(value)}`)
1702
- .join('&');
1703
- return validParams ? `?${validParams}` : '';
1704
- }
1705
- /**
1706
- * Flexible admin claims query with multiple filters
1707
- */
1708
- async getAdminClaims(filters) {
1709
- const queryString = this.buildQueryString(filters || {});
1710
- return this.apiClient.get(`/campaign-claims${queryString}`);
1711
- }
1712
- }
1713
-
1714
- /**
1715
- * Platform-Agnostic Campaign Service
1716
- *
1717
- * Contains campaign business logic and operations that work across platforms.
1718
- * No framework dependencies - pure TypeScript business logic.
1719
- *
1720
- * Focuses only on actual backend capabilities.
1721
- */
1722
- class CampaignService {
1723
- constructor(campaignApi) {
1724
- this.campaignApi = campaignApi;
1725
- }
1726
- // ==========================================
1727
- // PUBLIC OPERATIONS
1728
- // ==========================================
1729
- /**
1730
- * PUBLIC: Get all active campaigns
1731
- */
1732
- async getActiveCampaigns() {
1733
- return this.campaignApi.getActiveCampaigns();
1734
- }
1735
- /**
1736
- * PUBLIC: Get campaign by ID
1737
- */
1738
- async getCampaignById(id) {
1739
- return this.campaignApi.getCampaignById(id);
1740
- }
1741
- // ==========================================
1742
- // AUTHENTICATED OPERATIONS
1743
- // ==========================================
1744
- /**
1745
- * AUTH: Claim campaign
1746
- */
1747
- async claimCampaign(request) {
1748
- return this.campaignApi.claimCampaign(request);
1749
- }
1750
- /**
1751
- * AUTH: Get claims for logged user
1752
- */
1753
- async getClaimsForLoggedUser() {
1754
- return this.campaignApi.getClaimsForLoggedUser();
1755
- }
1756
- // ==========================================
1757
- // ADMIN OPERATIONS
1758
- // ==========================================
1759
- /**
1760
- * ADMIN: Get campaigns with optional active filter
1761
- */
1762
- async getCampaigns(active) {
1763
- return this.campaignApi.getCampaigns(active !== undefined ? { active } : undefined);
1764
- }
1765
- /**
1766
- * ADMIN: Get campaign triggers
1767
- */
1768
- async getCampaignTriggers() {
1769
- return this.campaignApi.getCampaignTriggers();
1770
- }
1771
- /**
1772
- * ADMIN: Toggle campaign active status
1773
- */
1774
- async toggleCampaignActive(campaignId) {
1775
- return this.campaignApi.toggleCampaignActive(campaignId);
1776
- }
1777
- /**
1778
- * ADMIN: Toggle campaign testnet environment
1779
- */
1780
- async toggleCampaignTestnet(campaignId) {
1781
- return this.campaignApi.toggleCampaignTestnet(campaignId);
1782
- }
1783
- /**
1784
- * ADMIN: Create campaign
1785
- */
1786
- async createCampaign(campaign) {
1787
- return this.campaignApi.createCampaign(campaign);
1788
- }
1789
- /**
1790
- * ADMIN: Set campaign trigger
1791
- */
1792
- async setCampaignTrigger(campaignId, triggerId) {
1793
- return this.campaignApi.setCampaignTrigger(campaignId, triggerId);
1794
- }
1795
- /**
1796
- * ADMIN: Update campaign
1797
- */
1798
- async updateCampaign(campaignId, campaign) {
1799
- return this.campaignApi.updateCampaign(campaignId, campaign);
1800
- }
1801
- /**
1802
- * ADMIN: Create campaign token unit
1803
- */
1804
- async createCampaignTokenUnit(campaignId, campaignTokenUnit) {
1805
- return this.campaignApi.createCampaignTokenUnit(campaignId, campaignTokenUnit);
1806
- }
1807
- /**
1808
- * ADMIN: Delete campaign token unit
1809
- */
1810
- async deleteCampaignTokenUnit(campaignId, campaignTokenUnitId) {
1811
- return this.campaignApi.deleteCampaignTokenUnit(campaignId, campaignTokenUnitId);
1812
- }
1813
- /**
1814
- * ADMIN: Add business engagement to campaign
1815
- */
1816
- async addBusinessEngagementToCampaign(campaignId, campaignBusinessEngagement) {
1817
- return this.campaignApi.addBusinessEngagementToCampaign(campaignId, campaignBusinessEngagement);
1818
- }
1819
- /**
1820
- * ADMIN: Update campaign business engagement
1821
- */
1822
- async updateCampaignBusinessEngagement(campaignId, businessEngagementId, campaignBusinessEngagement) {
1823
- return this.campaignApi.updateCampaignBusinessEngagement(campaignId, businessEngagementId, campaignBusinessEngagement);
1824
- }
1825
- async deleteCampaignBusinessEngagement(campaignId, businessEngagementId) {
1826
- return this.campaignApi.deleteCampaignBusinessEngagement(campaignId, businessEngagementId);
1827
- }
1828
- /**
1829
- * ADMIN: Get all campaign claims
1830
- */
1831
- async getCampaignClaims() {
1832
- return this.campaignApi.getCampaignClaims();
1833
- }
1834
- /**
1835
- * ADMIN: Get campaign claims by user ID
1836
- */
1837
- async getCampaignClaimsByUserId(userId) {
1838
- return this.campaignApi.getCampaignClaimsByUserId(userId);
1839
- }
1840
- /**
1841
- * ADMIN: Get campaign claims by business ID
1842
- */
1843
- async getCampaignClaimsByBusinessId(businessId) {
1844
- return this.campaignApi.getCampaignClaimsByBusinessId(businessId);
1845
- }
1846
- }
1847
-
1848
- /**
1849
- * @explorins/pers-sdk-campaign
1850
- *
1851
- * Platform-agnostic Campaign Domain SDK for PERS ecosystem
1852
- * Handles campaign operations across different authorization levels
1853
- */
1854
- // API Layer
1855
- /**
1856
- * Create a complete Campaign SDK instance
1857
- *
1858
- * @param apiClient - Configured PERS API client
1859
- * @returns Campaign SDK with flattened structure for better DX
1860
- */
1861
- function createCampaignSDK(apiClient) {
1862
- const campaignApi = new CampaignApi(apiClient);
1863
- const campaignService = new CampaignService(campaignApi);
1864
- return {
1865
- // Direct access to service methods (primary interface)
1866
- // Public methods
1867
- getActiveCampaigns: () => campaignService.getActiveCampaigns(),
1868
- getCampaignById: (id) => campaignService.getCampaignById(id),
1869
- // Auth methods
1870
- claimCampaign: (request) => campaignService.claimCampaign(request),
1871
- getClaimsForLoggedUser: () => campaignService.getClaimsForLoggedUser(),
1872
- // Admin methods
1873
- getCampaigns: (active) => campaignService.getCampaigns(active),
1874
- getCampaignTriggers: () => campaignService.getCampaignTriggers(),
1875
- toggleCampaignActive: (campaignId) => campaignService.toggleCampaignActive(campaignId),
1876
- toggleCampaignTestnet: (campaignId) => campaignService.toggleCampaignTestnet(campaignId),
1877
- createCampaign: (campaign) => campaignService.createCampaign(campaign),
1878
- setCampaignTrigger: (campaignId, triggerId) => campaignService.setCampaignTrigger(campaignId, triggerId),
1879
- updateCampaign: (campaignId, campaign) => campaignService.updateCampaign(campaignId, campaign),
1880
- createCampaignTokenUnit: (campaignId, campaignTokenUnit) => campaignService.createCampaignTokenUnit(campaignId, campaignTokenUnit),
1881
- deleteCampaignTokenUnit: (campaignId, campaignTokenUnitId) => campaignService.deleteCampaignTokenUnit(campaignId, campaignTokenUnitId),
1882
- addBusinessEngagementToCampaign: (campaignId, campaignBusinessEngagement) => campaignService.addBusinessEngagementToCampaign(campaignId, campaignBusinessEngagement),
1883
- updateCampaignBusinessEngagement: (campaignId, businessEngagementId, campaignBusinessEngagement) => campaignService.updateCampaignBusinessEngagement(campaignId, businessEngagementId, campaignBusinessEngagement),
1884
- deleteCampaignBusinessEngagement: (campaignId, businessEngagementId) => campaignService.deleteCampaignBusinessEngagement(campaignId, businessEngagementId),
1885
- getCampaignClaims: () => campaignService.getCampaignClaims(),
1886
- getCampaignClaimsByUserId: (userId) => campaignService.getCampaignClaimsByUserId(userId),
1887
- getCampaignClaimsByBusinessId: (businessId) => campaignService.getCampaignClaimsByBusinessId(businessId),
1888
- // Advanced access for edge cases
1889
- api: campaignApi,
1890
- service: campaignService
1891
- };
1892
- }
1893
-
1894
- /**
1895
- * Platform-Agnostic Donation API Client
1896
- *
1897
- * Handles donation operations using the PERS backend.
1898
- * Matches framework DonationApiService methods exactly.
1899
- */
1900
- class DonationApi {
1901
- constructor(apiClient) {
1902
- this.apiClient = apiClient;
1903
- }
1904
- // ==========================================
1905
- // PUBLIC OPERATIONS
1906
- // ==========================================
1907
- /**
1908
- * PUBLIC: Get all donation types
1909
- * ✅ ONLY method actually used by framework
1910
- */
1911
- async getAllDonationTypes() {
1912
- return this.apiClient.get('/purchases/donation-types');
1913
- }
1914
- }
1915
-
1916
- /**
1917
- * Platform-Agnostic Donation Service
1918
- *
1919
- * Contains donation business logic and operations that work across platforms.
1920
- * No framework dependencies - pure TypeScript business logic.
1921
- * Matches framework DonationApiService capabilities exactly.
1922
- */
1923
- class DonationService {
1924
- constructor(donationApi) {
1925
- this.donationApi = donationApi;
1926
- }
1927
- // ==========================================
1928
- // PUBLIC OPERATIONS
1929
- // ==========================================
1930
- /**
1931
- * PUBLIC: Get all donation types
1932
- * ✅ ONLY method actually used by framework
1933
- */
1934
- async getAllDonationTypes() {
1935
- return this.donationApi.getAllDonationTypes();
1936
- }
1937
- }
1938
-
1939
- /**
1940
- * @explorins/pers-sdk-donation
1941
- *
1942
- * Platform-agnostic Donation Domain SDK for PERS ecosystem
1943
- * Handles donation type retrieval for purchase flow integration
1944
- */
1945
- // API Layer
1946
- /**
1947
- * Create a complete Donation SDK instance
1948
- *
1949
- * @param apiClient - Configured PERS API client
1950
- * @returns Donation SDK with flattened structure for better DX
1951
- */
1952
- function createDonationSDK(apiClient) {
1953
- const donationApi = new DonationApi(apiClient);
1954
- const donationService = new DonationService(donationApi);
1955
- return {
1956
- // Direct access to service methods (primary interface)
1957
- // ✅ FRAMEWORK ALIGNED: Only method actually used by framework
1958
- // Public methods
1959
- getAllDonationTypes: () => donationService.getAllDonationTypes(),
1960
- // Advanced access for edge cases
1961
- api: donationApi,
1962
- service: donationService
1963
- };
1964
- }
1965
-
1966
- /**
1967
- * Platform-Agnostic Purchase API Client (RESTful Architecture)
1968
- *
1969
- * Handles purchase and payment operations using the PERS backend's new RESTful endpoints.
1970
- * Uses @explorins/pers-shared DTOs for consistency with backend.
1971
- *
1972
- * Migration Status: Updated to match /purchases controller (replaces /purchase endpoints)
1973
- *
1974
- * Available Access Levels:
1975
- * - PUBLIC: Project key authentication for catalog browsing and payment operations
1976
- * - USER: Requires user authentication JWT (purchase creation, history access)
1977
- * - ADMIN: Requires tenant admin privileges (not implemented in this client)
1978
- *
1979
- * Note: This SDK focuses on backend purchase operations only.
1980
- * Payment provider integrations (Stripe, etc.) should remain in infrastructure layer.
1981
- */
1982
- class PurchaseApi {
1983
- constructor(apiClient) {
1984
- this.apiClient = apiClient;
1985
- this.basePath = '/purchases';
1986
- }
1987
- // ==========================================
1988
- // PUBLIC OPERATIONS (Project Key)
1989
- // ==========================================
1990
- /**
1991
- * PUBLIC: Get purchase tokens (Intelligent Access)
1992
- *
1993
- * RESTful endpoint: GET /purchases/tokens
1994
- * Replaces: GET /purchase/token
1995
- *
1996
- * INTELLIGENT ACCESS:
1997
- * - PUBLIC (Project Key): Returns active tokens only (active parameter ignored)
1998
- * - ADMIN (Tenant Admin JWT): Returns filtered results based on active parameter
1999
- */
2000
- async getPurchaseTokens(active) {
2001
- let url = `${this.basePath}/tokens`;
2002
- if (active !== undefined) {
2003
- url += `?active=${active}`;
2004
- }
2005
- return this.apiClient.get(url);
2006
- }
2007
- /**
2008
- * PUBLIC: Get donation types
2009
- *
2010
- * RESTful endpoint: GET /purchases/donation-types
2011
- * Replaces: GET /purchase/donation/type
2012
- */
2013
- async getDonationTypes() {
2014
- return this.apiClient.get(`${this.basePath}/donation-types`);
2015
- }
2016
- // ==========================================
2017
- // PAYMENT OPERATIONS (FINANCIAL - CRITICAL)
2018
- // ==========================================
2019
- /**
2020
- * PUBLIC: Create payment intent (FINANCIAL OPERATION)
2021
- *
2022
- * RESTful endpoint: POST /purchases/payment-intents
2023
- * Replaces: POST /purchase/payment-intent
2024
- *
2025
- * CRITICAL: Handles real money operations - tenant context required
2026
- */
2027
- async createPaymentIntent(amount, currency, receiptEmail, description) {
2028
- const body = {
2029
- amount,
2030
- currency,
2031
- receiptEmail,
2032
- description
2033
- };
2034
- return this.apiClient.post(`${this.basePath}/payment-intents`, body);
2035
- }
2036
- /**
2037
- * PUBLIC: Update payment intent (FINANCIAL OPERATION)
2038
- *
2039
- * RESTful endpoint: PUT /purchases/payment-intents/{paymentIntentId}
2040
- * Replaces: PUT /purchase/payment-intent/{paymentIntentId}
2041
- *
2042
- * CRITICAL: Handles real money operations - tenant context required
2043
- */
2044
- async updatePaymentIntent(paymentIntentId, amount, currency, receiptEmail, description) {
2045
- const body = {
2046
- amount,
2047
- currency,
2048
- receiptEmail,
2049
- description
2050
- };
2051
- return this.apiClient.put(`${this.basePath}/payment-intents/${paymentIntentId}`, body);
2052
- }
2053
- /**
2054
- * PUBLIC: Cancel payment intent (FINANCIAL OPERATION)
2055
- *
2056
- * RESTful endpoint: DELETE /purchases/payment-intents/{paymentIntentId}
2057
- * Replaces: DELETE /purchase/payment-intent/{paymentIntentId}
2058
- *
2059
- * CRITICAL: Handles real money operations - tenant context required
2060
- */
2061
- async cancelPaymentIntent(paymentIntentId) {
2062
- return this.apiClient.delete(`${this.basePath}/payment-intents/${paymentIntentId}`);
2063
- }
2064
- // ==========================================
2065
- // USER OPERATIONS (JWT + Project Key)
2066
- // ==========================================
2067
- /**
2068
- * USER: Create purchase (BUSINESS CRITICAL - FINANCIAL TRANSACTION)
2069
- *
2070
- * RESTful endpoint: POST /purchases
2071
- * Replaces: POST /purchase/auth
2072
- *
2073
- * USER-ONLY: Requires user authentication JWT for purchase creation
2074
- * CRITICAL: Real financial transaction with Stripe integration
2075
- */
2076
- async createUserPurchase(paymentIntentId, amount, purchaseTokenId, donationTypeId, donationAccountAddress) {
2077
- const body = {
2078
- quantity: amount,
2079
- purchaseTokenId: purchaseTokenId || '',
2080
- donationTypeId,
2081
- donationAccountAddress,
2082
- paymentIntentId
2083
- };
2084
- return this.apiClient.post(`${this.basePath}`, body);
2085
- }
2086
- /**
2087
- * USER: Get user purchase history
2088
- *
2089
- * RESTful endpoint: GET /purchases/me/history
2090
- * Replaces: GET /purchase/auth
2091
- *
2092
- * USER-ONLY: Get authenticated user's purchase history
2093
- * FINANCIAL RECORDS: User attribution critical for compliance
2094
- */
2095
- async getUserPurchaseHistory() {
2096
- return this.apiClient.get(`${this.basePath}/me/history`);
2097
- }
2098
- // ==========================================
2099
- // CONVENIENCE METHODS (Backward Compatibility)
2100
- // ==========================================
2101
- /**
2102
- * @deprecated Use getPurchaseTokens() instead
2103
- * Backward compatibility alias for getActivePurchaseTokens
2104
- */
2105
- async getActivePurchaseTokens(active = true) {
2106
- return this.getPurchaseTokens(active);
2107
- }
2108
- /**
2109
- * @deprecated Use createUserPurchase() instead
2110
- * Backward compatibility alias for createPurchase
2111
- */
2112
- async createPurchase(paymentIntentId, amount, purchaseTokenId, donationTypeId, donationAccountAddress) {
2113
- return this.createUserPurchase(paymentIntentId, amount, purchaseTokenId, donationTypeId, donationAccountAddress);
2114
- }
2115
- /**
2116
- * @deprecated Use getUserPurchaseHistory() instead
2117
- * Backward compatibility alias for getAllUserPurchases
2118
- */
2119
- async getAllUserPurchases() {
2120
- return this.getUserPurchaseHistory();
2121
- }
2122
- }
2123
-
2124
- /**
2125
- * Platform-Agnostic Payment Service
2126
- *
2127
- * Contains payment business logic and operations that work across platforms.
2128
- * No framework dependencies - pure TypeScript business logic.
2129
- *
2130
- * Focuses only on actual backend capabilities.
2131
- * Payment provider logic (Stripe, etc.) should remain in infrastructure layer.
2132
- */
2133
- class PaymentService {
2134
- constructor(paymentApi) {
2135
- this.paymentApi = paymentApi;
2136
- }
2137
- // ==========================================
2138
- // PUBLIC OPERATIONS
2139
- // ==========================================
2140
- /**
2141
- * PUBLIC: Get active purchase tokens
2142
- */
2143
- async getActivePurchaseTokens(active = true) {
2144
- return this.paymentApi.getActivePurchaseTokens(active);
2145
- }
2146
- /**
2147
- * PUBLIC: Create payment intent
2148
- */
2149
- async createPaymentIntent(amount, currency, receiptEmail, description) {
2150
- return this.paymentApi.createPaymentIntent(amount, currency, receiptEmail, description);
2151
- }
2152
- /**
2153
- * PUBLIC: Update payment intent
2154
- */
2155
- async updatePaymentIntent(paymentIntentId, amount, currency, receiptEmail, description) {
2156
- return this.paymentApi.updatePaymentIntent(paymentIntentId, amount, currency, receiptEmail, description);
2157
- }
2158
- /**
2159
- * PUBLIC: Cancel payment intent
2160
- */
2161
- async cancelPaymentIntent(paymentIntentId) {
2162
- return this.paymentApi.cancelPaymentIntent(paymentIntentId);
2163
- }
2164
- // ==========================================
2165
- // AUTHENTICATED OPERATIONS
2166
- // ==========================================
2167
- /**
2168
- * AUTH: Create purchase
2169
- */
2170
- async createPurchase(paymentIntentId, amount, purchaseTokenId, donationTypeId, donationAccountAddress) {
2171
- return this.paymentApi.createPurchase(paymentIntentId, amount, purchaseTokenId, donationTypeId, donationAccountAddress);
2172
- }
2173
- /**
2174
- * AUTH: Get all user purchases
2175
- */
2176
- async getAllUserPurchases() {
2177
- return this.paymentApi.getAllUserPurchases();
2178
- }
2179
- }
2180
-
2181
- /**
2182
- * @explorins/pers-sdk-payment
2183
- *
2184
- * Platform-agnostic Payment Domain SDK for PERS ecosystem
2185
- * Handles payment intents, purchases, and purchase tokens
2186
- *
2187
- * Note: Payment provider integrations (Stripe, etc.) are kept separate
2188
- * in the infrastructure layer to maintain platform-agnostic principles.
2189
- */
2190
- // API Layer
2191
- /**
2192
- * Create a complete Payment SDK instance
2193
- *
2194
- * @param apiClient - Configured PERS API client
2195
- * @returns Payment SDK with flattened structure for better DX
2196
- */
2197
- function createPaymentSDK(apiClient) {
2198
- const paymentApi = new PurchaseApi(apiClient);
2199
- const paymentService = new PaymentService(paymentApi);
2200
- return {
2201
- // Direct access to service methods (primary interface)
2202
- // Public methods
2203
- getActivePurchaseTokens: (active) => paymentService.getActivePurchaseTokens(active),
2204
- // ✅ FIXED: Proper type instead of any
2205
- createPaymentIntent: (amount, currency, receiptEmail, description) => paymentService.createPaymentIntent(amount, currency, receiptEmail, description),
2206
- // ✅ FIXED: Proper type instead of any
2207
- updatePaymentIntent: (paymentIntentId, amount, currency, receiptEmail, description) => paymentService.updatePaymentIntent(paymentIntentId, amount, currency, receiptEmail, description),
2208
- cancelPaymentIntent: (paymentIntentId) => paymentService.cancelPaymentIntent(paymentIntentId),
2209
- // Auth methods
2210
- createPurchase: (paymentIntentId, amount, purchaseTokenId, donationTypeId, donationAccountAddress) => paymentService.createPurchase(paymentIntentId, amount, purchaseTokenId, donationTypeId, donationAccountAddress),
2211
- getAllUserPurchases: () => paymentService.getAllUserPurchases(),
2212
- // Advanced access for edge cases
2213
- api: paymentApi,
2214
- service: paymentService
2215
- };
2216
- }
2217
-
2218
- /**
2219
- * Platform-Agnostic Redemption API Client (UPDATED - RESTful Design)
2220
- *
2221
- * Updated to work with the new RESTful /redemptions and /redemption-redeems endpoints.
2222
- * Handles redemption operations using the PERS backend with intelligent access detection.
2223
- * Uses @explorins/pers-shared DTOs for consistency with backend.
2224
- *
2225
- * Migration Update: Updated all endpoints for unified controller pattern
2226
- * - Removed role revelation from URLs (no more /admin, /auth paths)
2227
- * - Added intelligent access detection for unified endpoints
2228
- * - Added new /redemption-redeems endpoints for redeem processing
2229
- * - Enhanced redemption process with role-based access control
2230
- */
2231
- class RedemptionApi {
2232
- constructor(apiClient) {
2233
- this.apiClient = apiClient;
2234
- this.basePath = '/redemptions';
2235
- this.redeemsPath = '/redemption-redeems';
2236
- }
2237
- // ==========================================
2238
- // PUBLIC OPERATIONS (Project Key)
2239
- // ==========================================
2240
- /**
2241
- * PUBLIC: Get redemptions (intelligent access)
2242
- *
2243
- * NEW: Intelligent endpoint that adapts based on authentication
2244
- * - Public users: Get active redemptions only
2245
- * - Admin users: Get all redemptions with optional filtering
2246
- *
2247
- * Replaces: getActiveRedemptions() + getRedemptionsAsAdmin()
2248
- */
2249
- async getRedemptions(active) {
2250
- let url = `${this.basePath}`;
2251
- if (active !== undefined) {
2252
- url += `?active=${active}`;
2253
- }
2254
- return this.apiClient.get(url);
2255
- }
2256
- /**
2257
- * PUBLIC: Get active redemptions
2258
- *
2259
- * Updated: Now uses unified endpoint (backward compatibility)
2260
- */
2261
- async getActiveRedemptions() {
2262
- return this.getRedemptions(); // Will return active only for public access
2263
- }
2264
- /**
2265
- * PUBLIC: Get redemption types
2266
- *
2267
- * Updated: /redemption/type → /redemptions/types
2268
- */
2269
- async getRedemptionTypes() {
2270
- return this.apiClient.get(`/redemption-types`);
2271
- }
2272
- /**
2273
- * PUBLIC: Get redemption by ID
2274
- *
2275
- * Updated: /redemption/:id → /redemptions/:id
2276
- */
2277
- async getRedemptionById(id) {
2278
- return this.apiClient.get(`${this.basePath}/${id}`);
2279
- }
2280
- /**
2281
- * PUBLIC: Get available supply for redemption
2282
- *
2283
- * Updated: /redemption/:id/available-supply → /redemptions/:id/supply
2284
- */
2285
- async getRedemptionAvailableSupply(id) {
2286
- return this.apiClient.get(`${this.basePath}/${id}/supply`);
2287
- }
2288
- // ==========================================
2289
- // REDEMPTION EXECUTION (NEW UNIFIED ENDPOINT)
2290
- // ==========================================
2291
- /**
2292
- * Execute redemption (unified endpoint)
2293
- *
2294
- * NEW: POST /redemption-redeems - Role-based processing
2295
- * - USER: Direct user redemption processing
2296
- * - ADMIN: Can process redemptions for any account type
2297
- * - BUSINESS: Process redemptions for customers
2298
- */
2299
- async redeemRedemption(redemptionId) {
2300
- const body = {
2301
- redemptionId: redemptionId,
2302
- };
2303
- return this.apiClient.post(this.redeemsPath, body);
2304
- }
2305
- // ==========================================
2306
- // REDEMPTION REDEEMS QUERIES (NEW ENDPOINTS)
2307
- // ==========================================
2308
- /**
2309
- * Get redemption redeems with filtering (unified endpoint)
2310
- *
2311
- * NEW: GET /redemption-redeems with query parameters
2312
- * Role-based access: Users see only their own, admins can filter by userId/businessId
2313
- */
2314
- async getRedemptionRedeems(filters) {
2315
- let url = this.redeemsPath;
2316
- const params = new URLSearchParams();
2317
- if (filters?.redemptionId)
2318
- params.append('redemptionId', filters.redemptionId);
2319
- if (filters?.userId)
2320
- params.append('userId', filters.userId);
2321
- if (filters?.businessId)
2322
- params.append('businessId', filters.businessId);
2323
- const queryString = params.toString();
2324
- if (queryString) {
2325
- url += `?${queryString}`;
2326
- }
2327
- return this.apiClient.get(url);
2328
- }
2329
- /**
2330
- * Get specific redemption redeem by ID
2331
- *
2332
- * NEW: GET /redemption-redeems/:id
2333
- */
2334
- async getRedemptionRedeemById(id) {
2335
- return this.apiClient.get(`${this.redeemsPath}/${id}`);
2336
- }
2337
- /**
2338
- * USER: Get my redemption redeems (convenience endpoint)
2339
- *
2340
- * NEW: GET /redemption-redeems/me with optional filtering
2341
- */
2342
- async getMyRedemptionRedeems(redemptionId) {
2343
- let url = `${this.redeemsPath}/me`;
2344
- if (redemptionId) {
2345
- url += `?redemptionId=${redemptionId}`;
2346
- }
2347
- return this.apiClient.get(url);
2348
- }
2349
- /**
2350
- * ADMIN: Get redemption redeems by user ID
2351
- *
2352
- * NEW: GET /redemption-redeems?userId=X
2353
- */
2354
- async getRedemptionRedeemsByUserId(userId, redemptionId) {
2355
- return this.getRedemptionRedeems({ userId, redemptionId });
2356
- }
2357
- /**
2358
- * ADMIN: Get redemption redeems by business ID
2359
- *
2360
- * NEW: GET /redemption-redeems?businessId=X
2361
- */
2362
- async getRedemptionRedeemsByBusinessId(businessId, redemptionId) {
2363
- return this.getRedemptionRedeems({ businessId, redemptionId });
2364
- }
2365
- /**
2366
- * ADMIN: Get redemption redeems by redemption ID
2367
- *
2368
- * NEW: GET /redemption-redeems?redemptionId=X
2369
- */
2370
- async getRedemptionRedeemsByRedemptionId(redemptionId) {
2371
- return this.getRedemptionRedeems({ redemptionId });
2372
- }
2373
- // ==========================================
2374
- // USER OPERATIONS (JWT + Project Key)
2375
- // ==========================================
2376
- /**
2377
- * USER: Get user redemption history
2378
- *
2379
- * Updated: Uses new convenience endpoint /redemption-redeems/me
2380
- */
2381
- async getUserRedemptionHistory() {
2382
- return this.getMyRedemptionRedeems();
2383
- }
2384
- /**
2385
- * USER: Get user redemptions (backward compatibility)
2386
- *
2387
- * Deprecated: Use getUserRedemptionHistory() instead
2388
- */
2389
- async getUserRedeems() {
2390
- return this.getUserRedemptionHistory();
2391
- }
2392
- // ==========================================
2393
- // ADMIN OPERATIONS (Tenant Admin JWT)
2394
- // ==========================================
2395
- /**
2396
- * ADMIN: Get redemptions with filtering (using intelligent endpoint)
2397
- *
2398
- * Updated: /redemption/admin → /redemptions (intelligent access detection)
2399
- * The unified endpoint will detect admin privileges and allow filtering
2400
- */
2401
- async getRedemptionsAsAdmin(active) {
2402
- return this.getRedemptions(active); // Uses intelligent endpoint
2403
- }
2404
- /**
2405
- * ADMIN: Create redemption
2406
- *
2407
- * Updated: /redemption/admin → /redemptions
2408
- */
2409
- async createRedemption(redemption) {
2410
- return this.apiClient.post(`${this.basePath}`, redemption);
2411
- }
2412
- /**
2413
- * ADMIN: Update redemption
2414
- *
2415
- * Updated: /redemption/admin/:id → /redemptions/:id
2416
- */
2417
- async updateRedemption(id, redemptionCreateRequest) {
2418
- return this.apiClient.put(`${this.basePath}/${id}`, redemptionCreateRequest);
2419
- }
2420
- /**
2421
- * ADMIN: Toggle redemption status
2422
- *
2423
- * Updated: /redemption/admin/:id/toggle-active → /redemptions/:id/status
2424
- * Following standard /status pattern used across domains
2425
- */
2426
- async toggleRedemptionStatus(redemptionId) {
2427
- return this.apiClient.put(`${this.basePath}/${redemptionId}/status`, {});
2428
- }
2429
- /**
2430
- * ADMIN: Toggle redemption active (backward compatibility)
2431
- *
2432
- * Deprecated: Use toggleRedemptionStatus() instead
2433
- */
2434
- async toggleRedemptionActive(redemptionId) {
2435
- return this.toggleRedemptionStatus(redemptionId);
2436
- }
2437
- /**
2438
- * ADMIN: Delete redemption
2439
- *
2440
- * Updated: /redemption/admin/:id → /redemptions/:id
2441
- */
2442
- async deleteRedemption(id) {
2443
- return this.apiClient.delete(`${this.basePath}/${id}`);
2444
- }
2445
- /**
2446
- * ADMIN: Create redemption type
2447
- *
2448
- * Updated: /redemption/admin/type → /redemptions/types
2449
- */
2450
- async createRedemptionType(redemptionType) {
2451
- return this.apiClient.post(`${this.basePath}/redemption-types`, redemptionType);
2452
- }
2453
- // ==========================================
2454
- // TOKEN UNIT MANAGEMENT (Admin)
2455
- // ==========================================
2456
- /**
2457
- * ADMIN: Create redemption token unit
2458
- *
2459
- * Updated: /redemption/admin/:id/token-units → /redemptions/:id/token-units
2460
- */
2461
- async createRedemptionTokenUnit(redemptionId, redemptionTokenUnit) {
2462
- return this.apiClient.post(`${this.basePath}/${redemptionId}/token-units`, redemptionTokenUnit);
2463
- }
2464
- /**
2465
- * ADMIN: Update redemption token unit
2466
- *
2467
- * Updated: /redemption/admin/:id/token-units/:tokenUnitId → /redemptions/:id/token-units/:tokenUnitId
2468
- */
2469
- async updateRedemptionTokenUnit(redemptionId, tokenUnitId, redemptionTokenUnit) {
2470
- return this.apiClient.put(`${this.basePath}/${redemptionId}/token-units/${tokenUnitId}`, redemptionTokenUnit);
2471
- }
2472
- /**
2473
- * ADMIN: Delete redemption token unit
2474
- *
2475
- * Updated: /redemption/admin/:id/token-units/:tokenUnitId → /redemptions/:id/token-units/:tokenUnitId
2476
- */
2477
- async deleteRedemptionTokenUnit(redemptionId, redemptionTokenUnitId) {
2478
- return this.apiClient.delete(`${this.basePath}/${redemptionId}/token-units/${redemptionTokenUnitId}`);
2479
- }
2480
- // ==========================================
2481
- // BACKWARD COMPATIBILITY METHODS
2482
- // ==========================================
2483
- /**
2484
- * @deprecated Use getRedemptions() instead
2485
- * Backward compatibility for old admin endpoint
2486
- */
2487
- async getRedemptionsAdmin(active) {
2488
- return this.getRedemptionsAsAdmin(active);
2489
- }
2490
- /**
2491
- * @deprecated Use redeemRedemption() instead
2492
- * Backward compatibility for old redeem method
2493
- */
2494
- async redeem(redemptionId) {
2495
- return this.redeemRedemption(redemptionId);
2496
- }
2497
- }
2498
-
2499
- /**
2500
- * Platform-Agnostic Redemption Service
2501
- *
2502
- * Contains redemption business logic and operations that work across platforms.
2503
- * No framework dependencies - pure TypeScript business logic.
2504
- *
2505
- * Focuses only on actual backend capabilities.
2506
- */
2507
- class RedemptionService {
2508
- constructor(redemptionApi) {
2509
- this.redemptionApi = redemptionApi;
2510
- }
2511
- // ==========================================
2512
- // PUBLIC OPERATIONS
2513
- // ==========================================
2514
- /**
2515
- * PUBLIC: Get active redemptions
2516
- */
2517
- async getActiveRedemptions() {
2518
- return this.redemptionApi.getActiveRedemptions();
2519
- }
2520
- /**
2521
- * PUBLIC: Get redemption types
2522
- */
2523
- async getRedemptionTypes() {
2524
- return this.redemptionApi.getRedemptionTypes();
2525
- }
2526
- // ==========================================
2527
- // AUTHENTICATED OPERATIONS
2528
- // ==========================================
2529
- /**
2530
- * AUTH: Redeem a redemption
2531
- */
2532
- async redeemRedemption(redemptionId) {
2533
- return this.redemptionApi.redeemRedemption(redemptionId);
2534
- }
2535
- /**
2536
- * AUTH: Get user redemptions
2537
- */
2538
- async getUserRedeems() {
2539
- return this.redemptionApi.getUserRedeems();
2540
- }
2541
- // ==========================================
2542
- // ADMIN OPERATIONS
2543
- // ==========================================
2544
- /**
2545
- * ADMIN: Get redemptions with optional active filter
2546
- */
2547
- async getRedemptionsAsAdmin(active) {
2548
- return this.redemptionApi.getRedemptionsAsAdmin(active);
2549
- }
2550
- /**
2551
- * ADMIN: Create redemption
2552
- */
2553
- async createRedemption(redemption) {
2554
- return this.redemptionApi.createRedemption(redemption);
2555
- }
2556
- /**
2557
- * ADMIN: Update redemption
2558
- */
2559
- async updateRedemption(id, redemptionCreateRequest) {
2560
- return this.redemptionApi.updateRedemption(id, redemptionCreateRequest); // ✅ CORRECTED: Fixed parameter
2561
- }
2562
- /**
2563
- * ADMIN: Toggle redemption active status
2564
- */
2565
- async toggleRedemptionActive(redemptionId) {
2566
- return this.redemptionApi.toggleRedemptionActive(redemptionId);
2567
- }
2568
- /**
2569
- * ADMIN: Create redemption token unit
2570
- */
2571
- async createRedemptionTokenUnit(redemptionId, redemptionTokenUnit) {
2572
- return this.redemptionApi.createRedemptionTokenUnit(redemptionId, redemptionTokenUnit);
2573
- }
2574
- /**
2575
- * ADMIN: Delete redemption token unit
2576
- */
2577
- async deleteRedemptionTokenUnit(redemptionId, redemptionTokenUnitId) {
2578
- return this.redemptionApi.deleteRedemptionTokenUnit(redemptionId, redemptionTokenUnitId);
2579
- }
2580
- }
2581
-
2582
- /**
2583
- * @explorins/pers-sdk-redemption
2584
- *
2585
- * Platform-agnostic Redemption Domain SDK for PERS ecosystem
2586
- * Handles redemption operations across different authorization levels
2587
- */
2588
- // API Layer
2589
- /**
2590
- * Create a complete Redemption SDK instance
2591
- *
2592
- * @param apiClient - Configured PERS API client
2593
- * @returns Redemption SDK with flattened structure for better DX
2594
- */
2595
- function createRedemptionSDK(apiClient) {
2596
- const redemptionApi = new RedemptionApi(apiClient);
2597
- const redemptionService = new RedemptionService(redemptionApi);
2598
- return {
2599
- // Direct access to service methods (primary interface)
2600
- // Public methods
2601
- getActiveRedemptions: () => redemptionService.getActiveRedemptions(),
2602
- getRedemptionTypes: () => redemptionService.getRedemptionTypes(),
2603
- // Auth methods
2604
- redeemRedemption: (redemptionId) => redemptionService.redeemRedemption(redemptionId),
2605
- getUserRedeems: () => redemptionService.getUserRedeems(),
2606
- // Admin methods
2607
- getRedemptionsAsAdmin: (active) => redemptionService.getRedemptionsAsAdmin(active),
2608
- createRedemption: (redemption) => redemptionService.createRedemption(redemption),
2609
- updateRedemption: (id, redemptionCreateRequest) => redemptionService.updateRedemption(id, redemptionCreateRequest),
2610
- toggleRedemptionActive: (redemptionId) => redemptionService.toggleRedemptionActive(redemptionId),
2611
- createRedemptionTokenUnit: (redemptionId, redemptionTokenUnit) => redemptionService.createRedemptionTokenUnit(redemptionId, redemptionTokenUnit),
2612
- deleteRedemptionTokenUnit: (redemptionId, redemptionTokenUnitId) => redemptionService.deleteRedemptionTokenUnit(redemptionId, redemptionTokenUnitId),
2613
- // Advanced access for edge cases
2614
- api: redemptionApi,
2615
- service: redemptionService
2616
- };
2617
- }
2618
-
2619
- /**
2620
- * Platform-Agnostic Tenant API Client
2621
- *
2622
- * Handles tenant and admin operations using the PERS backend.
2623
- * Matches framework TenantApiService methods exactly.
2624
- *
2625
- * Note: Special header handling (bypass-auth-interceptor) should be handled by PersApiClient internally
2626
- * or through endpoint-specific configuration.
2627
- */
2628
- class TenantApi {
2629
- constructor(apiClient) {
2630
- this.apiClient = apiClient;
2631
- this.basePath = '/tenants';
2632
- this.adminPath = '/admins';
2633
- }
2634
- // ==========================================
2635
- // PUBLIC OPERATIONS
2636
- // ==========================================
2637
- /**
2638
- * PUBLIC: Get tenant public information
2639
- * ✅ FIXED: Matches framework cache busting pattern exactly
2640
- */
2641
- async getRemoteTenant() {
2642
- const timestamp = Date.now().toString();
2643
- const url = `${this.basePath}/public?date=${timestamp}`;
2644
- return this.apiClient.get(url);
2645
- }
2646
- /**
2647
- * PUBLIC: Get remote login token
2648
- */
2649
- async getRemoteLoginToken() {
2650
- return this.apiClient.get(`${this.basePath}/login-token`);
2651
- }
2652
- /**
2653
- * PUBLIC: Get remote client configuration
2654
- * ✅ FIXED: Removed second parameter - PersApiClient handles bypass auth internally
2655
- * Note: The /tenants/client-config endpoint should be configured to bypass auth at the API client level
2656
- */
2657
- async getRemoteClientConfig() {
2658
- return this.apiClient.get(`${this.basePath}/client-config`);
2659
- }
2660
- // ==========================================
2661
- // ADMIN OPERATIONS
2662
- // ==========================================
2663
- /**
2664
- * ADMIN: Update tenant information
2665
- * ✅ FIXED: Uses TenantPublicDTO directly like framework
2666
- */
2667
- async updateRemoteTenant(tenantData) {
2668
- return this.apiClient.put(`${this.basePath}`, tenantData);
2669
- }
2670
- /**
2671
- * ADMIN: Get all tenant admins
2672
- */
2673
- async getAdmins() {
2674
- return this.apiClient.get(`${this.adminPath}`);
2675
- }
2676
- /**
2677
- * ADMIN: Create new admin
2678
- * ✅ FIXED: Renamed to match framework postAdmin method
2679
- */
2680
- async postAdmin(adminData) {
2681
- return this.apiClient.post(`${this.adminPath}`, adminData);
2682
- }
2683
- /**
2684
- * ADMIN: Update admin (toggle tenant association)
2685
- * ✅ FIXED: Renamed to match framework putAdmin method
2686
- */
2687
- async putAdmin(adminId, adminData) {
2688
- return this.apiClient.put(`${this.adminPath}/${adminId}/tenant`, adminData);
2689
- }
2690
- }
2691
-
2692
- /**
2693
- * Platform-Agnostic Tenant Service
2694
- *
2695
- * Contains tenant business logic and operations that work across platforms.
2696
- * No framework dependencies - pure TypeScript business logic.
2697
- * Matches framework TenantApiService capabilities exactly.
2698
- */
2699
- class TenantService {
2700
- constructor(tenantApi) {
2701
- this.tenantApi = tenantApi;
2702
- }
2703
- // ==========================================
2704
- // PUBLIC OPERATIONS
2705
- // ==========================================
2706
- /**
2707
- * PUBLIC: Get tenant public information
2708
- */
2709
- async getRemoteTenant() {
2710
- return this.tenantApi.getRemoteTenant();
2711
- }
2712
- /**
2713
- * PUBLIC: Get remote login token
2714
- */
2715
- async getRemoteLoginToken() {
2716
- return this.tenantApi.getRemoteLoginToken();
2717
- }
2718
- /**
2719
- * PUBLIC: Get remote client configuration
2720
- */
2721
- async getRemoteClientConfig() {
2722
- return this.tenantApi.getRemoteClientConfig();
2723
- }
2724
- // ==========================================
2725
- // ADMIN OPERATIONS
2726
- // ==========================================
2727
- /**
2728
- * ADMIN: Update tenant information
2729
- * ✅ FIXED: Uses TenantPublicDTO directly like framework
2730
- */
2731
- async updateRemoteTenant(tenantData) {
2732
- return this.tenantApi.updateRemoteTenant(tenantData);
2733
- }
2734
- /**
2735
- * ADMIN: Get all tenant admins
2736
- */
2737
- async getAdmins() {
2738
- return this.tenantApi.getAdmins();
2739
- }
2740
- /**
2741
- * ADMIN: Create new admin
2742
- * ✅ FIXED: Renamed to match framework postAdmin method
2743
- */
2744
- async postAdmin(adminData) {
2745
- return this.tenantApi.postAdmin(adminData);
2746
- }
2747
- /**
2748
- * ADMIN: Update admin (toggle tenant association)
2749
- * ✅ FIXED: Renamed to match framework putAdmin method
2750
- */
2751
- async putAdmin(adminId, adminData) {
2752
- return this.tenantApi.putAdmin(adminId, adminData);
2753
- }
2754
- }
2755
-
2756
- /**
2757
- * @explorins/pers-sdk-tenant
2758
- *
2759
- * Platform-agnostic Tenant Domain SDK for PERS ecosystem
2760
- * Handles tenant management and admin operations for multi-tenant architecture
2761
- */
2762
- // API Layer
2763
- /**
2764
- * Create a complete Tenant SDK instance
2765
- *
2766
- * @param apiClient - Configured PERS API client
2767
- * @returns Tenant SDK with flattened structure for better DX
2768
- */
2769
- function createTenantSDK(apiClient) {
2770
- const tenantApi = new TenantApi(apiClient);
2771
- const tenantService = new TenantService(tenantApi);
2772
- return {
2773
- // Direct access to service methods (primary interface)
2774
- // ✅ FRAMEWORK ALIGNED: Only methods actually used by framework
2775
- // Public methods
2776
- getRemoteTenant: () => tenantService.getRemoteTenant(),
2777
- getRemoteLoginToken: () => tenantService.getRemoteLoginToken(),
2778
- getRemoteClientConfig: () => tenantService.getRemoteClientConfig(),
2779
- // Admin methods - ✅ FIXED: Matches framework method names exactly
2780
- updateRemoteTenant: (tenantData) => tenantService.updateRemoteTenant(tenantData),
2781
- getAdmins: () => tenantService.getAdmins(),
2782
- postAdmin: (adminData) => tenantService.postAdmin(adminData),
2783
- putAdmin: (adminId, adminData) => tenantService.putAdmin(adminId, adminData),
2784
- // Advanced access for edge cases
2785
- api: tenantApi,
2786
- service: tenantService
2787
- };
2788
- }
2789
-
2790
- class TokenApi {
2791
- constructor(apiClient) {
2792
- this.apiClient = apiClient;
2793
- this.basePath = '/tokens';
2794
- }
2795
- // ==========================================
2796
- // PUBLIC OPERATIONS
2797
- // ==========================================
2798
- /**
2799
- * PUBLIC: Get all remote tokens
2800
- * ENHANCED: Added admin filtering capability
2801
- */
2802
- async getRemoteTokens(includeInactive = false) {
2803
- const url = includeInactive ? `${this.basePath}?active=false` : `${this.basePath}`;
2804
- return this.apiClient.get(url);
2805
- }
2806
- /**
2807
- * PUBLIC: Get all remote token types
2808
- */
2809
- async getRemoteTokenTypes() {
2810
- return this.apiClient.get(`${this.basePath}/types`);
2811
- }
2812
- /**
2813
- * PUBLIC: Get active point token (was credit token)
2814
- */
2815
- async getRemoteActiveCreditToken() {
2816
- return this.apiClient.get(`${this.basePath}/points`);
2817
- }
2818
- /**
2819
- * PUBLIC: Get reward tokens
2820
- * ENHANCED: Added admin filtering capability
2821
- */
2822
- async getRemoteRewardTokens(includeInactive = false) {
2823
- const url = includeInactive ? `${this.basePath}/rewards?active=false` : `${this.basePath}/rewards`;
2824
- return this.apiClient.get(url);
2825
- }
2826
- /**
2827
- * PUBLIC: Get stamp tokens (was status tokens)
2828
- * ENHANCED: Added admin filtering capability
2829
- */
2830
- async getRemoteStatusTokens(includeInactive = false) {
2831
- const url = includeInactive ? `${this.basePath}/stamps?active=false` : `${this.basePath}/stamps`;
2832
- return this.apiClient.get(url);
2833
- }
2834
- /**
2835
- * PUBLIC: Get token by contract address
2836
- */
2837
- async getTokenByContractAddress(contractAddress, contractTokenId) {
2838
- let url = `${this.basePath}/address/${contractAddress}`;
2839
- if (contractTokenId) {
2840
- url += `?contractTokenId=${contractTokenId}`;
2841
- }
2842
- return this.apiClient.get(url);
2843
- }
2844
- // ==========================================
2845
- // ADMIN OPERATIONS
2846
- // ==========================================
2847
- /**
2848
- * ADMIN: Create new token
2849
- */
2850
- async createToken(tokenData) {
2851
- return this.apiClient.post(`${this.basePath}`, tokenData);
2852
- }
2853
- /**
2854
- * ADMIN: Update token
2855
- */
2856
- async updateToken(tokenId, tokenData) {
2857
- return this.apiClient.put(`${this.basePath}/${tokenId}`, tokenData);
2858
- }
2859
- /**
2860
- * ADMIN: Toggle token active status
2861
- * FIXED: Now calls correct endpoint
2862
- */
2863
- async toggleTokenActive(tokenId) {
2864
- return this.apiClient.put(`${this.basePath}/${tokenId}/status`, {});
2865
- }
2866
- /**
2867
- * ADMIN: Set mainnet contract address
2868
- */
2869
- async setMainnetContract(tokenId, contractAddress, chainId) {
2870
- return this.apiClient.put(`${this.basePath}/${tokenId}/mainnet`, {
2871
- contractAddress,
2872
- chainId
2873
- });
2874
- }
2875
- /**
2876
- * ADMIN: Create token metadata
2877
- */
2878
- async createTokenMetadata(tokenId, tokenData) {
2879
- return this.apiClient.post(`${this.basePath}/${tokenId}/metadata`, tokenData);
2880
- }
2881
- /**
2882
- * ADMIN: Toggle token metadata status (separate from token status)
2883
- */
2884
- async toggleTokenMetadataStatus(metadataId) {
2885
- return this.apiClient.put(`${this.basePath}/metadata/${metadataId}/status`, {});
2886
- }
2887
- /**
2888
- * ADMIN: Create token type
2889
- */
2890
- async createTokenType(tokenType) {
2891
- return this.apiClient.post(`${this.basePath}/types`, tokenType);
2892
- }
2893
- }
2894
-
2895
- /**
2896
- * Platform-Agnostic Token Service
2897
- *
2898
- * Contains token business logic and operations that work across platforms.
2899
- * No framework dependencies - pure TypeScript business logic.
2900
- * Matches framework TokenApiService capabilities exactly.
2901
- */
2902
- class TokenService {
2903
- constructor(tokenApi) {
2904
- this.tokenApi = tokenApi;
2905
- }
2906
- // ==========================================
2907
- // PUBLIC OPERATIONS
2908
- // ==========================================
2909
- /**
2910
- * PUBLIC: Get all remote tokens
2911
- */
2912
- async getRemoteTokens() {
2913
- return this.tokenApi.getRemoteTokens();
2914
- }
2915
- /**
2916
- * PUBLIC: Get all remote token types
2917
- */
2918
- async getRemoteTokenTypes() {
2919
- return this.tokenApi.getRemoteTokenTypes();
2920
- }
2921
- /**
2922
- * PUBLIC: Get active credit token
2923
- */
2924
- async getRemoteActiveCreditToken() {
2925
- return this.tokenApi.getRemoteActiveCreditToken();
2926
- }
2927
- /**
2928
- * PUBLIC: Get reward tokens
2929
- */
2930
- async getRemoteRewardTokens() {
2931
- return this.tokenApi.getRemoteRewardTokens();
2932
- }
2933
- /**
2934
- * PUBLIC: Get status tokens
2935
- */
2936
- async getRemoteStatusTokens() {
2937
- return this.tokenApi.getRemoteStatusTokens();
2938
- }
2939
- /**
2940
- * PUBLIC: Get token by contract address
2941
- * ✅ FIXED: Matches framework parameter types exactly
2942
- */
2943
- async getTokenByContractAddress(contractAddress, contractTokenId) {
2944
- return this.tokenApi.getTokenByContractAddress(contractAddress, contractTokenId);
2945
- }
2946
- // ==========================================
2947
- // ADMIN OPERATIONS
2948
- // ==========================================
2949
- /**
2950
- * ADMIN: Create token metadata
2951
- */
2952
- async createTokenMetadata(tokenId, tokenData) {
2953
- return this.tokenApi.createTokenMetadata(tokenId, tokenData);
2954
- }
2955
- /**
2956
- * ADMIN: Toggle token active status
2957
- */
2958
- async toggleTokenActive(tokenId) {
2959
- return this.tokenApi.toggleTokenActive(tokenId);
2960
- }
2961
- /**
2962
- * ADMIN: Create new token
2963
- */
2964
- async createToken(tokenData) {
2965
- return this.tokenApi.createToken(tokenData);
2966
- }
2967
- /**
2968
- * ADMIN: Update token
2969
- */
2970
- async updateToken(tokenId, tokenData) {
2971
- return this.tokenApi.updateToken(tokenId, tokenData);
2972
- }
2973
- /**
2974
- * ADMIN: Set mainnet contract address
2975
- */
2976
- async setMainnetContract(tokenId, contractAddress, chainId) {
2977
- return this.tokenApi.setMainnetContract(tokenId, contractAddress, chainId);
2978
- }
2979
- /**
2980
- * ADMIN: Toggle token metadata status
2981
- */
2982
- async toggleTokenMetadataStatus(metadataId) {
2983
- return this.tokenApi.toggleTokenMetadataStatus(metadataId);
2984
- }
2985
- /**
2986
- * ADMIN: Create token type
2987
- */
2988
- async createTokenType(tokenType) {
2989
- return this.tokenApi.createTokenType(tokenType);
2990
- }
2991
- }
2992
-
2993
- /**
2994
- * Token SDK - Class-based Promise SDK for Token Operations
2995
- *
2996
- * Modern, performant SDK with direct method access and excellent TypeScript support.
2997
- * Optimized for bundle size, performance, and developer experience.
2998
- *
2999
- * Usage:
3000
- * const tokenSDK = new TokenSDK(apiClient);
3001
- * const tokens = await tokenSDK.getTokens();
3002
- * const creditToken = await tokenSDK.getActiveCreditToken();
3003
- */
3004
- class TokenSDK {
3005
- constructor(apiClient) {
3006
- this.tokenApi = new TokenApi(apiClient);
3007
- this.tokenService = new TokenService(this.tokenApi);
3008
- }
3009
- // ==========================================
3010
- // CONVENIENCE METHODS - Direct Promise API
3011
- // ==========================================
3012
- // Simplified method names for better developer experience
3013
- /**
3014
- * Get all tokens
3015
- * @returns Promise with all available tokens
3016
- */
3017
- async getTokens() {
3018
- return this.tokenService.getRemoteTokens();
3019
- }
3020
- /**
3021
- * Get all token types
3022
- * @returns Promise with all available token types
3023
- */
3024
- async getTokenTypes() {
3025
- return this.tokenService.getRemoteTokenTypes();
3026
- }
3027
- /**
3028
- * Get the active credit token
3029
- * @returns Promise with the current active credit token
3030
- */
3031
- async getActiveCreditToken() {
3032
- return this.tokenService.getRemoteActiveCreditToken();
3033
- }
3034
- /**
3035
- * Get all reward tokens
3036
- * @returns Promise with all reward tokens
3037
- */
3038
- async getRewardTokens() {
3039
- return this.tokenService.getRemoteRewardTokens();
3040
- }
3041
- /**
3042
- * Get all status tokens
3043
- * @returns Promise with all status tokens
3044
- */
3045
- async getStatusTokens() {
3046
- return this.tokenService.getRemoteStatusTokens();
3047
- }
3048
- /**
3049
- * Get token by contract address
3050
- * @param contractAddress - The contract address to search for
3051
- * @param contractTokenId - Optional contract token ID
3052
- * @returns Promise with the matching token
3053
- */
3054
- async getTokenByContract(contractAddress, contractTokenId = null) {
3055
- return this.tokenService.getTokenByContractAddress(contractAddress, contractTokenId);
3056
- }
3057
- // ==========================================
3058
- // ADMIN METHODS - Token Management
3059
- // ==========================================
3060
- /**
3061
- * Create token metadata
3062
- * @param tokenId - The token ID
3063
- * @param tokenData - The token storage data
3064
- * @returns Promise with the updated token
3065
- */
3066
- async createTokenMetadata(tokenId, tokenData) {
3067
- return this.tokenService.createTokenMetadata(tokenId, tokenData);
3068
- }
3069
- /**
3070
- * Toggle token active status
3071
- * @param tokenId - The token ID to toggle
3072
- * @returns Promise with the updated token
3073
- */
3074
- async toggleTokenActive(tokenId) {
3075
- return this.tokenService.toggleTokenActive(tokenId);
3076
- }
3077
- /**
3078
- * Create a new token
3079
- * @param tokenData - The token creation data
3080
- * @returns Promise with the created token
3081
- */
3082
- async createToken(tokenData) {
3083
- return this.tokenService.createToken(tokenData);
3084
- }
3085
- /**
3086
- * Update an existing token
3087
- * @param tokenId - The token ID to update
3088
- * @param tokenData - The token update data
3089
- * @returns Promise with the updated token
3090
- */
3091
- async updateToken(tokenId, tokenData) {
3092
- return this.tokenService.updateToken(tokenId, tokenData);
3093
- }
3094
- /**
3095
- * Set mainnet contract address for a token
3096
- * @param tokenId - The token ID
3097
- * @param contractAddress - The contract address
3098
- * @param chainId - The blockchain chain ID
3099
- * @returns Promise with the updated token
3100
- */
3101
- async setMainnetContract(tokenId, contractAddress, chainId) {
3102
- return this.tokenService.setMainnetContract(tokenId, contractAddress, chainId);
3103
- }
3104
- /**
3105
- * Toggle token metadata status
3106
- * @param metadataId - The metadata ID to toggle
3107
- * @returns Promise with the updated metadata
3108
- */
3109
- async toggleTokenMetadataStatus(metadataId) {
3110
- return this.tokenService.toggleTokenMetadataStatus(metadataId);
3111
- }
3112
- /**
3113
- * Create a new token type
3114
- * @param tokenType - The token type data
3115
- * @returns Promise with the created token type
3116
- */
3117
- async createTokenType(tokenType) {
3118
- return this.tokenService.createTokenType(tokenType);
3119
- }
3120
- // ==========================================
3121
- // ADVANCED ACCESS - For Complex Operations
3122
- // ==========================================
3123
- /**
3124
- * Get direct access to the token service for advanced operations
3125
- * @returns The underlying TokenService instance
3126
- */
3127
- getTokenService() {
3128
- return this.tokenService;
3129
- }
3130
- /**
3131
- * Get direct access to the token API for low-level operations
3132
- * @returns The underlying TokenApi instance
3133
- */
3134
- getTokenApi() {
3135
- return this.tokenApi;
3136
- }
3137
- // ==========================================
3138
- // FRAMEWORK COMPATIBILITY METHODS
3139
- // ==========================================
3140
- // These maintain compatibility with existing framework method names
3141
- /**
3142
- * @deprecated Use getTokens() instead
3143
- * Framework compatibility method
3144
- */
3145
- async getRemoteTokens() {
3146
- return this.getTokens();
3147
- }
3148
- /**
3149
- * @deprecated Use getTokenTypes() instead
3150
- * Framework compatibility method
3151
- */
3152
- async getRemoteTokenTypes() {
3153
- return this.getTokenTypes();
3154
- }
3155
- /**
3156
- * @deprecated Use getActiveCreditToken() instead
3157
- * Framework compatibility method
3158
- */
3159
- async getRemoteActiveCreditToken() {
3160
- return this.getActiveCreditToken();
3161
- }
3162
- /**
3163
- * @deprecated Use getRewardTokens() instead
3164
- * Framework compatibility method
3165
- */
3166
- async getRemoteRewardTokens() {
3167
- return this.getRewardTokens();
3168
- }
3169
- /**
3170
- * @deprecated Use getStatusTokens() instead
3171
- * Framework compatibility method
3172
- */
3173
- async getRemoteStatusTokens() {
3174
- return this.getStatusTokens();
3175
- }
3176
- /**
3177
- * @deprecated Use getTokenByContract() instead
3178
- * Framework compatibility method
3179
- */
3180
- async getTokenByContractAddress(contractAddress, contractTokenId) {
3181
- return this.getTokenByContract(contractAddress, contractTokenId);
3182
- }
3183
- }
3184
-
3185
- /**
3186
- * Abstract Base Token Service - Explicit Initialization Pattern
3187
- *
3188
- * Platform-agnostic token operations with Promise-based API.
3189
- * Framework services extend this and control initialization lifecycle.
3190
- *
3191
- * Benefits:
3192
- * - Explicit initialization control
3193
- * - Better error boundaries
3194
- * - Clear lifecycle management
3195
- * - Testable initialization state
3196
- * - Zero framework dependencies
3197
- */
3198
- class BaseTokenService {
3199
- constructor() {
3200
- this._isInitialized = false;
3201
- }
3202
- // ==========================================
3203
- // INITIALIZATION LIFECYCLE
3204
- // ==========================================
3205
- /**
3206
- * LIFECYCLE: Initialize token service with API client
3207
- * Must be called before using any token operations
3208
- */
3209
- initializeTokenService(apiClient) {
3210
- if (!apiClient) {
3211
- throw new Error('Cannot initialize TokenService: apiClient is null or undefined');
3212
- }
3213
- if (!this._isInitialized) {
3214
- this._tokenApi = new TokenApi(apiClient);
3215
- this._tokenBusinessService = new TokenService(this._tokenApi);
3216
- this._isInitialized = true;
3217
- }
3218
- }
3219
- /**
3220
- * LIFECYCLE: Check if token service is initialized
3221
- */
3222
- get isTokenServiceInitialized() {
3223
- return this._isInitialized;
3224
- }
3225
- /**
3226
- * INTERNAL: Get token business service (throws if not initialized)
3227
- */
3228
- get tokenBusinessService() {
3229
- if (!this._tokenBusinessService) {
3230
- throw new Error('TokenService not initialized. Call initializeTokenService(apiClient) first.');
3231
- }
3232
- return this._tokenBusinessService;
3233
- }
3234
- // ==========================================
3235
- // PUBLIC OPERATIONS
3236
- // ==========================================
3237
- /**
3238
- * PUBLIC: Get all remote tokens
3239
- */
3240
- getRemoteTokens() {
3241
- return this.tokenBusinessService.getRemoteTokens();
3242
- }
3243
- /**
3244
- * PUBLIC: Get all remote token types
3245
- */
3246
- getRemoteTokenTypes() {
3247
- return this.tokenBusinessService.getRemoteTokenTypes();
3248
- }
3249
- /**
3250
- * PUBLIC: Get active credit token
3251
- */
3252
- getRemoteActiveCreditToken() {
3253
- return this.tokenBusinessService.getRemoteActiveCreditToken();
3254
- }
3255
- /**
3256
- * PUBLIC: Get reward tokens
3257
- */
3258
- getRemoteRewardTokens() {
3259
- return this.tokenBusinessService.getRemoteRewardTokens();
3260
- }
3261
- /**
3262
- * PUBLIC: Get status tokens
3263
- */
3264
- getRemoteStatusTokens() {
3265
- return this.tokenBusinessService.getRemoteStatusTokens();
3266
- }
3267
- /**
3268
- * PUBLIC: Get token by contract address
3269
- */
3270
- getTokenByContractAddress(contractAddress, contractTokenId) {
3271
- return this.tokenBusinessService.getTokenByContractAddress(contractAddress, contractTokenId);
3272
- }
3273
- // ==========================================
3274
- // ADMIN OPERATIONS
3275
- // ==========================================
3276
- /**
3277
- * ADMIN: Create token metadata
3278
- */
3279
- createTokenMetadata(tokenId, tokenData) {
3280
- return this.tokenBusinessService.createTokenMetadata(tokenId, tokenData);
3281
- }
3282
- /**
3283
- * ADMIN: Toggle token active status
3284
- */
3285
- toggleTokenActive(tokenId) {
3286
- return this.tokenBusinessService.toggleTokenActive(tokenId);
3287
- }
3288
- /**
3289
- * ADMIN: Create new token
3290
- */
3291
- createToken(tokenData) {
3292
- return this.tokenBusinessService.createToken(tokenData);
3293
- }
3294
- /**
3295
- * ADMIN: Update token
3296
- */
3297
- updateToken(tokenId, tokenData) {
3298
- return this.tokenBusinessService.updateToken(tokenId, tokenData);
3299
- }
3300
- /**
3301
- * ADMIN: Set mainnet contract address
3302
- */
3303
- setMainnetContract(tokenId, contractAddress, chainId) {
3304
- return this.tokenBusinessService.setMainnetContract(tokenId, contractAddress, chainId);
3305
- }
3306
- /**
3307
- * ADMIN: Toggle token metadata status
3308
- */
3309
- toggleTokenMetadataStatus(metadataId) {
3310
- return this.tokenBusinessService.toggleTokenMetadataStatus(metadataId);
3311
- }
3312
- /**
3313
- * ADMIN: Create token type
3314
- */
3315
- createTokenType(tokenType) {
3316
- return this.tokenBusinessService.createTokenType(tokenType);
3317
- }
3318
- }
3319
-
3320
- /**
3321
- * Platform-Agnostic User API Client
3322
- *
3323
- * Handles user operations using the PERS backend RESTful API.
3324
- * Updated to use new /users endpoints with enhanced security and consistency.
3325
- * Maintains framework UserApiService method compatibility.
3326
- */
3327
- class UserApi {
3328
- constructor(apiClient) {
3329
- this.apiClient = apiClient;
3330
- this.basePath = '/users';
3331
- }
3332
- // ==========================================
3333
- // PUBLIC OPERATIONS
3334
- // ==========================================
3335
- /**
3336
- * PUBLIC: Get all users public profiles with optional filtering
3337
- * ✅ UPDATED: Uses new RESTful /users/public endpoint
3338
- */
3339
- async getAllUsersPublicProfiles(filter = null) {
3340
- let url = `${this.basePath}/public`;
3341
- if (filter) {
3342
- // ✅ MAINTAINED: Same parameter pattern for compatibility
3343
- const params = new URLSearchParams();
3344
- params.set('filterKey', filter.key);
3345
- params.set('filterValue', filter.value);
3346
- url += `?${params.toString()}`;
3347
- }
3348
- return this.apiClient.get(url);
3349
- }
3350
- // ==========================================
3351
- // AUTHENTICATED OPERATIONS
3352
- // ==========================================
3353
- /**
3354
- * AUTH: Get current authenticated user
3355
- * ✅ UPDATED: Uses new RESTful /users/me endpoint
3356
- */
3357
- async getRemoteUser() {
3358
- return this.apiClient.get(`${this.basePath}/me`);
3359
- }
3360
- /**
3361
- * AUTH: Update current authenticated user
3362
- * ✅ UPDATED: Uses new RESTful /users/me endpoint
3363
- */
3364
- async updateRemoteUser(updateRequest) {
3365
- return this.apiClient.put(`${this.basePath}/me`, updateRequest);
3366
- }
3367
- // ==========================================
3368
- // ADMIN OPERATIONS
3369
- // ==========================================
3370
- /**
3371
- * ADMIN: Get all remote users with query parameters
3372
- * ✅ UPDATED: Uses new RESTful /users endpoint with role-based access
3373
- * Note: Admin users get full data, non-admin users get public profiles only
3374
- */
3375
- async getAllRemoteUsers() {
3376
- // ✅ MAINTAINED: Same merge=soft parameter for compatibility
3377
- const url = `${this.basePath}?merge=soft`;
3378
- return this.apiClient.get(url);
3379
- }
3380
- /**
3381
- * ADMIN: Update user as admin
3382
- * ✅ UPDATED: Uses new RESTful /users/{id} endpoint
3383
- */
3384
- async updateUserAsAdmin(id, userData) {
3385
- return this.apiClient.put(`${this.basePath}/${id}`, userData);
3386
- }
3387
- /**
3388
- * ADMIN: Toggle user active status
3389
- * ✅ UPDATED: Uses new consistent /users/{id}/status endpoint
3390
- * Enhanced: Follows RESTful status management pattern across all domains
3391
- */
3392
- async toggleUserActiveStatusByUser(user) {
3393
- return this.apiClient.put(`${this.basePath}/${user.id}/status`, {});
3394
- }
3395
- /**
3396
- * ADMIN: Get user by unique identifier
3397
- * ✅ UPDATED: Uses new RESTful /users/{id} endpoint
3398
- */
3399
- async getUserByUniqueIdentifier(id) {
3400
- return this.apiClient.get(`${this.basePath}/${id}`);
3401
- }
3402
- }
3403
-
3404
- /**
3405
- * Platform-Agnostic User Service
3406
- *
3407
- * Contains user business logic and operations that work across platforms.
3408
- * No framework dependencies - pure TypeScript business logic.
3409
- * Matches framework UserApiService capabilities exactly.
3410
- */
3411
- class UserService {
3412
- constructor(userApi) {
3413
- this.userApi = userApi;
3414
- }
3415
- // ==========================================
3416
- // PUBLIC OPERATIONS
3417
- // ==========================================
3418
- /**
3419
- * PUBLIC: Get all users public profiles with optional filtering
3420
- * ✅ FIXED: Uses framework-compatible inline filter type
3421
- */
3422
- async getAllUsersPublicProfiles(filter = null) {
3423
- return this.userApi.getAllUsersPublicProfiles(filter);
3424
- }
3425
- // ==========================================
3426
- // AUTHENTICATED OPERATIONS
3427
- // ==========================================
3428
- /**
3429
- * AUTH: Get current authenticated user
3430
- */
3431
- async getRemoteUser() {
3432
- return this.userApi.getRemoteUser();
3433
- }
3434
- /**
3435
- * AUTH: Update current authenticated user
3436
- */
3437
- async updateRemoteUser(updateRequest) {
3438
- return this.userApi.updateRemoteUser(updateRequest);
3439
- }
3440
- // ==========================================
3441
- // ADMIN OPERATIONS
3442
- // ==========================================
3443
- /**
3444
- * ADMIN: Get all remote users
3445
- * ✅ FIXED: Matches API method signature (no parameters needed)
3446
- */
3447
- async getAllRemoteUsers() {
3448
- return this.userApi.getAllRemoteUsers();
3449
- }
3450
- /**
3451
- * ADMIN: Update user as admin
3452
- */
3453
- async updateUserAsAdmin(id, userData) {
3454
- return this.userApi.updateUserAsAdmin(id, userData);
3455
- }
3456
- async getUserByUniqueIdentifier(id) {
3457
- return this.userApi.getUserByUniqueIdentifier(id);
3458
- }
3459
- /**
3460
- * ADMIN: Toggle user active status by user object
3461
- * ✅ FIXED: Matches API method signature exactly
3462
- */
3463
- async toggleUserActiveStatusByUser(user) {
3464
- return this.userApi.toggleUserActiveStatusByUser(user);
3465
- }
3466
- }
3467
-
3468
- /**
3469
- * @explorins/pers-sdk-user
3470
- *
3471
- * Platform-agnostic User Domain SDK for PERS ecosystem
3472
- * Handles user management, profiles, and authentication operations
3473
- */
3474
- // API Layer
3475
- /**
3476
- * Create a complete User SDK instance
3477
- *
3478
- * @param apiClient - Configured PERS API client
3479
- * @returns User SDK with flattened structure for better DX
3480
- */
3481
- function createUserSDK(apiClient) {
3482
- const userApi = new UserApi(apiClient);
3483
- const userService = new UserService(userApi);
3484
- return {
3485
- // Direct access to service methods (primary interface)
3486
- // Public methods - matches framework exactly
3487
- getAllUsersPublicProfiles: (filter = null) => userService.getAllUsersPublicProfiles(filter),
3488
- // Auth methods - matches framework exactly
3489
- getRemoteUser: () => userService.getRemoteUser(),
3490
- updateRemoteUser: (updateRequest) => userService.updateRemoteUser(updateRequest),
3491
- // Admin methods - matches framework exactly
3492
- getAllRemoteUsers: () => userService.getAllRemoteUsers(),
3493
- updateUserAsAdmin: (id, userData) => userService.updateUserAsAdmin(id, userData),
3494
- toggleUserActiveStatusByUser: (user) => userService.toggleUserActiveStatusByUser(user),
3495
- getUserByUniqueIdentifier: (id) => userService.getUserByUniqueIdentifier(id),
3496
- // Advanced access for edge cases
3497
- api: userApi,
3498
- service: userService
3499
- };
3500
- }
3501
-
3502
- /**
3503
- * Platform-Agnostic User Status API Client
3504
- *
3505
- * Handles user status operations using the PERS backend.
3506
- * Matches framework UserStatusApiService methods exactly.
3507
- *
3508
- * ✅ UPDATED: All endpoints updated to new RESTful /users patterns
3509
- *
3510
- * Error handling patterns follow framework implementation:
3511
- * - getRemoteEarnedUserStatus() uses silent fallback (empty array)
3512
- * - Other operations throw errors for proper error handling
3513
- */
3514
- class UserStatusApi {
3515
- constructor(apiClient) {
3516
- this.apiClient = apiClient;
3517
- this.basePath = '/users';
3518
- }
3519
- // ==========================================
3520
- // PUBLIC OPERATIONS
3521
- // ==========================================
3522
- /**
3523
- * PUBLIC: Get remote user status types
3524
- * ✅ UPDATED: /user/status-type → /users/status-types
3525
- */
3526
- async getRemoteUserStatusTypes() {
3527
- try {
3528
- return await this.apiClient.get(`${this.basePath}/status-types`);
3529
- }
3530
- catch (error) {
3531
- console.error('Error getting user status types', error);
3532
- throw error;
3533
- }
3534
- }
3535
- // ==========================================
3536
- // AUTHENTICATED OPERATIONS
3537
- // ==========================================
3538
- /**
3539
- * AUTH: Get earned user status for authenticated user
3540
- * ✅ UPDATED: /user/auth/status → /users/me/status
3541
- * ✅ FIXED: Returns UserStatusTypeDTO[] to match framework exactly
3542
- * Note: Uses silent fallback pattern from framework - returns empty array on error
3543
- */
3544
- async getRemoteEarnedUserStatus() {
3545
- try {
3546
- return await this.apiClient.get(`${this.basePath}/me/status`);
3547
- }
3548
- catch (error) {
3549
- console.error('Error getting user status', error);
3550
- // ✅ FIXED: Silent fallback pattern from framework implementation
3551
- return [];
3552
- }
3553
- }
3554
- // ==========================================
3555
- // ADMIN OPERATIONS
3556
- // ==========================================
3557
- /**
3558
- * ADMIN: Create user status type
3559
- * ✅ UPDATED: /user/admin/status-type → /users/status-types
3560
- */
3561
- async createUserStatusType(userStatusType) {
3562
- try {
3563
- return await this.apiClient.post(`${this.basePath}/status-types`, userStatusType);
3564
- }
3565
- catch (error) {
3566
- console.error('Error creating user status type', error);
3567
- throw error;
3568
- }
3569
- }
3570
- }
3571
-
3572
- /**
3573
- * Platform-Agnostic User Status Service
3574
- *
3575
- * Contains user status business logic and operations that work across platforms.
3576
- * No framework dependencies - pure TypeScript business logic.
3577
- * Matches framework UserStatusApiService capabilities exactly.
3578
- */
3579
- class UserStatusService {
3580
- constructor(userStatusApi) {
3581
- this.userStatusApi = userStatusApi;
3582
- }
3583
- // ==========================================
3584
- // PUBLIC OPERATIONS
3585
- // ==========================================
3586
- /**
3587
- * PUBLIC: Get remote user status types
3588
- */
3589
- async getRemoteUserStatusTypes() {
3590
- return this.userStatusApi.getRemoteUserStatusTypes();
3591
- }
3592
- // ==========================================
3593
- // AUTHENTICATED OPERATIONS
3594
- // ==========================================
3595
- /**
3596
- * AUTH: Get earned user status for authenticated user
3597
- */
3598
- async getRemoteEarnedUserStatus() {
3599
- return this.userStatusApi.getRemoteEarnedUserStatus();
3600
- }
3601
- // ==========================================
3602
- // ADMIN OPERATIONS
3603
- // ==========================================
3604
- /**
3605
- * ADMIN: Create user status type
3606
- */
3607
- async createUserStatusType(userStatusType) {
3608
- return this.userStatusApi.createUserStatusType(userStatusType);
3609
- }
3610
- }
3611
-
3612
- /**
3613
- * @explorins/pers-sdk-user-status
3614
- *
3615
- * Platform-agnostic User Status Domain SDK for PERS ecosystem
3616
- * Handles user status management and type operations
3617
- */
3618
- // API Layer
3619
- /**
3620
- * Create a complete User Status SDK instance
3621
- *
3622
- * @param apiClient - Configured PERS API client
3623
- * @returns User Status SDK with flattened structure for better DX
3624
- */
3625
- function createUserStatusSDK(apiClient) {
3626
- const userStatusApi = new UserStatusApi(apiClient);
3627
- const userStatusService = new UserStatusService(userStatusApi);
3628
- return {
3629
- // Direct access to service methods (primary interface)
3630
- // ✅ FRAMEWORK ALIGNED: Only methods actually used by framework
3631
- // Public methods
3632
- getRemoteUserStatusTypes: () => userStatusService.getRemoteUserStatusTypes(),
3633
- // Auth methods
3634
- getRemoteEarnedUserStatus: () => userStatusService.getRemoteEarnedUserStatus(),
3635
- // Admin methods
3636
- createUserStatusType: (userStatusType) => userStatusService.createUserStatusType(userStatusType),
3637
- // Advanced access for edge cases
3638
- api: userStatusApi,
3639
- service: userStatusService
3640
- };
3641
- }
3642
-
3643
- /**
3644
- * Platform-Agnostic Web3 Chain API Client
3645
- *
3646
- * Handles blockchain chain operations using the PERS backend.
3647
- * Uses @explorins/web3-ts types for perfect framework compatibility.
3648
- */
3649
- class Web3ChainApi {
3650
- constructor(apiClient) {
3651
- this.apiClient = apiClient;
3652
- this.basePath = '/chains';
3653
- }
3654
- // ==========================================
3655
- // PUBLIC OPERATIONS
3656
- // ==========================================
3657
- /**
3658
- * PUBLIC: Get chain data by chain ID
3659
- * ✅ Returns ChainData exactly as framework expects from @explorins/web3-ts
3660
- */
3661
- async getChainData(chainId) {
3662
- try {
3663
- console.log('🔍 [Web3ChainApi] Fetching chain data for chainId:', chainId);
3664
- const response = await this.apiClient.get(`${this.basePath}/${chainId}`);
3665
- if (!response) {
3666
- throw new Error(`No chain data received for chainId: ${chainId}`);
3667
- }
3668
- return response;
3669
- }
3670
- catch (error) {
3671
- console.error('❌ [Web3ChainApi] Failed to get chain data:', {
3672
- chainId,
3673
- error: error instanceof Error ? error.message : String(error)
3674
- });
3675
- throw error;
3676
- }
3677
- }
3678
- }
3679
-
3680
- const isTokenExpired = (token, margin = 60) => {
3681
- // ✅ SANITIZE: Remove any words (Bearer etc) if exists and get the token
3682
- const cleanToken = token.split(' ')[1] || token;
3683
- // ✅ VALIDATION: Check if token is jwt
3684
- if (!cleanToken || cleanToken.split('.').length !== 3) {
3685
- return true; // Consider invalid tokens as expired
3686
- }
3687
- try {
3688
- // ✅ TYPE-SAFE: Decode with proper typing
3689
- const decoded = jwtDecode(cleanToken);
3690
- const currentTime = Math.floor(Date.now() / 1000);
3691
- // ✅ CONFIGURABLE: Use provided margin (default 60 seconds)
3692
- return decoded.exp < (currentTime + margin);
3693
- }
3694
- catch (error) {
3695
- console.error('[SDK JWT Utils] Error decoding JWT token:', error);
3696
- return true; // Consider unparseable tokens as expired
3697
- }
3698
- };
3699
-
3700
- class Web3ChainService {
3701
- constructor(web3ChainApi, providerService) {
3702
- this.web3ChainApi = web3ChainApi;
3703
- this.providerService = providerService;
3704
- this.web3InstanceCache = new Map();
3705
- }
3706
- async getWeb3ByChainId(chainId) {
3707
- const cached = this.web3InstanceCache.get(chainId);
3708
- // ✅ SMART CACHE: Check if cache is valid based on token expiration
3709
- if (cached && !this.checkIsTokenExpired(cached.chainData.authHeader)) {
3710
- console.log(`♻️ [Web3ChainService] Using cached Web3 instance for chain ${chainId}`);
3711
- return cached.web3Instance;
3712
- }
3713
- // ✅ CREATE WITH ERROR RECOVERY: Handle auth errors gracefully
3714
- try {
3715
- console.log(`🔧 [Web3ChainService] Creating new Web3 instance for chain ${chainId}`);
3716
- const { web3, chainData } = await this.createWeb3Instance(chainId);
3717
- // ✅ CACHE NEW INSTANCE
3718
- this.web3InstanceCache.set(chainId, {
3719
- web3Instance: web3,
3720
- chainData: chainData,
3721
- createdAt: Date.now(),
3722
- chainId
3723
- });
3724
- return web3;
3725
- }
3726
- catch (error) {
3727
- // ✅ AUTH ERROR RECOVERY: Clear cache and retry once
3728
- if (this.isAuthError(error)) {
3729
- console.warn(`🔄 [Web3ChainService] Auth error detected, clearing cache and retrying...`);
3730
- this.web3InstanceCache.delete(chainId);
3731
- // Retry once with fresh instance
3732
- const { web3, chainData } = await this.createWeb3Instance(chainId);
3733
- this.web3InstanceCache.set(chainId, {
3734
- web3Instance: web3,
3735
- chainData: chainData,
3736
- createdAt: Date.now(),
3737
- chainId
3738
- });
3739
- return web3;
3740
- }
3741
- throw error;
3742
- }
3743
- }
3744
- async getChainDataWithCache(chainId) {
3745
- const cached = this.web3InstanceCache.get(chainId);
3746
- //const now = Date.now();
3747
- if (cached && !this.checkIsTokenExpired(cached.chainData.authHeader)) {
3748
- console.log(`♻️ [Web3ChainService] Using cached ChainData for chain ${chainId}`);
3749
- return cached.chainData;
3750
- }
3751
- // If not cached, fetch fresh data
3752
- const chainData = await this.getChainDataById(chainId);
3753
- if (!chainData) {
3754
- throw new Error(`Chain data not found for chainId: ${chainId}`);
3755
- }
3756
- return chainData;
3757
- }
3758
- checkIsTokenExpired(authHeader) {
3759
- // ✅ NULL CHECK: Handle undefined case
3760
- if (!authHeader) {
3761
- return false; // No token means no expiration (public chains)
3762
- }
3763
- // ✅ FUNCTION CALL: Use imported function, not method
3764
- return isTokenExpired(authHeader);
3765
- }
3766
- async createWeb3Instance(chainId) {
3767
- const chainData = await this.getChainDataById(chainId);
3768
- if (!chainData) {
3769
- throw new Error(`Chain data not found for chainId: ${chainId}`);
3770
- }
3771
- // ✅ CRITICAL CHECK: Ensure authHeader is present for private chains
3772
- if (chainData.chainType === 'PRIVATE' && !chainData.authHeader) {
3773
- console.error('❌ [Web3ChainService] CRITICAL: Private chain missing authHeader!');
3774
- throw new Error(`Private chain ${chainId} missing authentication header`);
3775
- }
3776
- const web3 = await this.providerService.getWeb3(chainId, chainData.chainType || 'PUBLIC', chainData);
3777
- return { web3, chainData };
3778
- }
3779
- // ✅ HELPER: Type-safe error checking
3780
- isAuthError(error) {
3781
- const message = error instanceof Error ? error.message.toLowerCase() : String(error).toLowerCase();
3782
- return (message.includes('unauthorized') ||
3783
- message.includes('401') ||
3784
- message.includes('token expired') ||
3785
- message.includes('invalid token'));
3786
- }
3787
- async getChainDataById(chainId) {
3788
- try {
3789
- return await this.web3ChainApi.getChainData(chainId);
3790
- }
3791
- catch (error) {
3792
- console.error('❌ [SDK Web3ChainService] Error getting chain data for chainId:', chainId, error);
3793
- return null;
3794
- }
3795
- }
3796
- }
3797
-
3798
- // ✅ REVERT: Función síncrona como el código comentado que funciona
3799
- const getWeb3ProviderFromChainData = (chainData, timeout = 15000, customUserAgentName = '', tokenRefresher) => {
3800
- console.log(`🔧 [getWeb3FCD] Creating provider for chain ${chainData.chainId || 'unknown'}`);
3801
- let ethersProvider;
3802
- if (chainData.authHeader) {
3803
- // ✅ AUTHENTICATED: For private chains
3804
- const fetchRequest = new FetchRequest(chainData.rpcUrl);
3805
- fetchRequest.timeout = timeout;
3806
- // ✅ IMPROVED AUTH HEADER: Better handling
3807
- const authValue = chainData.authHeader.startsWith('Bearer ')
3808
- ? chainData.authHeader
3809
- : `Bearer ${chainData.authHeader}`;
3810
- fetchRequest.setHeader('Authorization', authValue);
3811
- fetchRequest.setHeader('Content-Type', 'application/json');
3812
- fetchRequest.setHeader('Accept', 'application/json');
3813
- if (customUserAgentName) {
3814
- fetchRequest.setHeader('User-Agent', customUserAgentName);
3815
- }
3816
- ethersProvider = new JsonRpcProvider(fetchRequest, undefined, {
3817
- staticNetwork: false,
3818
- polling: false,
3819
- batchMaxCount: 1, // ✅ DISABLE BATCHING: Better for private chains
3820
- });
3821
- }
3822
- else {
3823
- // ✅ PUBLIC: For public chains
3824
- ethersProvider = new JsonRpcProvider(chainData.rpcUrl, undefined, {
3825
- staticNetwork: false,
3826
- polling: false,
3827
- });
3828
- }
3829
- console.log(`✅ [getWeb3FCD] Provider created successfully`);
3830
- return {
3831
- web3Provider: null,
3832
- ethersProvider: ethersProvider,
3833
- isAuthenticated: !!chainData.authHeader,
3834
- };
3835
- };
3836
- // ✅ NEW: Async wrapper with retry for higher-level usage
3837
- const getWeb3ProviderWithRetry = async (chainData, timeout = 15000, customUserAgentName = '', tokenRefresher, retryConfig = { maxAttempts: 3, baseDelay: 1000, maxDelay: 8000 }) => {
3838
- let lastError = null;
3839
- for (let attempt = 1; attempt <= retryConfig.maxAttempts; attempt++) {
3840
- try {
3841
- console.log(`🔄 [Web3Provider] Attempt ${attempt}/${retryConfig.maxAttempts} for chain ${chainData.chainId || 'unknown'}`);
3842
- // ✅ SYNC CALL: Use the original sync function
3843
- const provider = getWeb3ProviderFromChainData(chainData, timeout, customUserAgentName, tokenRefresher);
3844
- await validateChainConnection(provider.ethersProvider, chainData.authHeader ? 'private' : 'public');
3845
- console.log(`✅ [Web3Provider] Successfully connected on attempt ${attempt}`);
3846
- return provider;
3847
- }
3848
- catch (error) {
3849
- lastError = error instanceof Error ? error : new Error(String(error));
3850
- console.warn(`⚠️ [Web3Provider] Attempt ${attempt} failed:`, lastError.message);
3851
- // ✅ NO RETRY: if auth error, no retry
3852
- if (isAuthError(lastError) && chainData.authHeader) {
3853
- console.error(`❌ [Web3Provider] Auth error, stopping retries`);
3854
- break;
3855
- }
3856
- if (attempt === retryConfig.maxAttempts)
3857
- break;
3858
- const delay = Math.min(retryConfig.baseDelay * Math.pow(2, attempt - 1), retryConfig.maxDelay);
3859
- console.log(`⏳ [Web3Provider] Retrying in ${delay}ms...`);
3860
- await sleep(delay);
3861
- }
3862
- }
3863
- throw new Error(`Failed to create Web3 provider after ${retryConfig.maxAttempts} attempts. Last error: ${lastError?.message}`);
3864
- };
3865
- async function validateChainConnection(provider, chainType) {
3866
- try {
3867
- console.log(`🔍 [Validation] Testing ${chainType} chain connection...`);
3868
- // ✅ LIGHTWEIGHT TEST: Use eth_chainId (works for both public and private)
3869
- const timeoutPromise = new Promise((_, reject) => {
3870
- setTimeout(() => reject(new Error(`${chainType} chain validation timeout`)), 3000);
3871
- });
3872
- // Try chainId first (fast, lightweight, universal)
3873
- const chainIdPromise = provider.send('eth_chainId', []);
3874
- const result = await Promise.race([chainIdPromise, timeoutPromise]);
3875
- console.log(`✅ [Validation] ${chainType} chain connection validated - Chain ID: ${result}`);
3876
- }
3877
- catch (error) {
3878
- // ✅ FALLBACK: Try net_version if chainId fails
3879
- try {
3880
- console.log(`🔄 [Validation] Trying fallback validation for ${chainType} chain...`);
3881
- const timeoutPromise = new Promise((_, reject) => {
3882
- setTimeout(() => reject(new Error(`${chainType} chain fallback validation timeout`)), 3000);
3883
- });
3884
- const versionPromise = provider.send('net_version', []);
3885
- const result = await Promise.race([versionPromise, timeoutPromise]);
3886
- console.log(`✅ [Validation] ${chainType} chain connection validated via fallback - Network Version: ${result}`);
3887
- }
3888
- catch (fallbackError) {
3889
- throw new Error(`${chainType} chain validation failed: ${error instanceof Error ? error.message : String(error)}`);
3890
- }
3891
- }
3892
- }
3893
- // ✅ HELPER: Auth error detection
3894
- function isAuthError(error) {
3895
- const message = error.message.toLowerCase();
3896
- return (message.includes('unauthorized') ||
3897
- message.includes('401') ||
3898
- message.includes('token expired') ||
3899
- message.includes('-40100'));
3900
- }
3901
- // ✅ HELPER: Sleep utility
3902
- function sleep(ms) {
3903
- return new Promise(resolve => setTimeout(resolve, ms));
3904
- }
3905
- /*
3906
- //IMPORTANT//
3907
- //This function is temporary so we install ethers just to make it work, once we delete this function we must uninstall Ethers
3908
-
3909
- import { ChainData } from "@explorins/web3-ts";
3910
- import { FetchRequest, JsonRpcProvider } from "ethers";
3911
-
3912
- export const getWeb3ProviderFromChainData = (
3913
- chainData: ChainData,
3914
- timeout = 15000,
3915
- customUserAgentName = '',
3916
- tokenRefresher?: () => Promise<string>
3917
- ) => {
3918
-
3919
- // Fixed ethers provider setup for authenticated requests
3920
- let ethersProvider: JsonRpcProvider;
3921
-
3922
- if (chainData.authHeader) {
3923
- // For authenticated requests, create a custom FetchRequest
3924
- const fetchRequest = new FetchRequest(chainData.rpcUrl);
3925
- fetchRequest.timeout = timeout;
3926
- fetchRequest.setHeader('Authorization', chainData.authHeader);
3927
- fetchRequest.setHeader('Content-Type', 'application/json');
3928
-
3929
- if (customUserAgentName) {
3930
- fetchRequest.setHeader('User-Agent', customUserAgentName);
3931
- }
3932
-
3933
- // Create provider with the configured FetchRequest
3934
- ethersProvider = new JsonRpcProvider(fetchRequest, undefined, {
3935
- staticNetwork: false,
3936
- polling: false, // Disable polling for better Lambda performance
3937
- });
3938
- } else {
3939
- // For public chains, use simple URL-based provider
3940
- ethersProvider = new JsonRpcProvider(chainData.rpcUrl, undefined, {
3941
- staticNetwork: false,
3942
- polling: false,
3943
- });
3944
- }
3945
-
3946
- return {
3947
- web3Provider: null,
3948
- ethersProvider: ethersProvider,
3949
- isAuthenticated: !!chainData.authHeader,
3950
- };
3951
- }; */
3952
-
3953
- class Web3ProviderService {
3954
- constructor() {
3955
- this._web3 = null;
3956
- this._currentChainId = null;
3957
- this._creationPromise = null;
3958
- }
3959
- async getWeb3(chainId, chainType, chainData) {
3960
- // ✅ EARLY RETURN: Reuse existing provider
3961
- if (this._web3 && this._currentChainId === chainId) {
3962
- return this._web3;
3963
- }
3964
- // ✅ PREVENT RACE CONDITION: Wait for ongoing creation
3965
- if (this._creationPromise) {
3966
- return await this._creationPromise;
3967
- }
3968
- if (!chainId)
3969
- throw new Error('ChainId not found');
3970
- if (!chainData)
3971
- throw new Error('ChainData not found');
3972
- // ✅ CREATE AND CACHE: Single promise for concurrent calls
3973
- this._creationPromise = this.createProvider(chainId, chainType, chainData);
3974
- try {
3975
- const web3Instance = await this._creationPromise;
3976
- this._web3 = web3Instance;
3977
- this._currentChainId = chainId;
3978
- return web3Instance;
3979
- }
3980
- finally {
3981
- // ✅ CLEANUP: Always reset promise after completion
3982
- this._creationPromise = null;
3983
- }
3984
- }
3985
- async createProvider(chainId, chainType, chainData) {
3986
- const provider = await getWeb3ProviderWithRetry(chainData);
3987
- return this.convertToWeb3(provider);
3988
- }
3989
- convertToWeb3(provider) {
3990
- if (provider instanceof Web3) {
3991
- return provider;
3992
- }
3993
- if (provider && typeof provider === 'object' && 'web3Provider' in provider) {
3994
- const providerObj = provider;
3995
- // If we want to user the web3Provider directly:
3996
- /*if (providerObj.web3Provider) {
3997
- return new Web3(providerObj.web3Provider as never);
3998
- }*/
3999
- if (providerObj.ethersProvider) {
4000
- const url = this.extractUrlFromEthersProvider(providerObj.ethersProvider);
4001
- const headers = this.extractHeadersFromEthersProvider(providerObj.ethersProvider);
4002
- const web3 = new Web3(url);
4003
- const currentProvider = web3.currentProvider;
4004
- if (currentProvider) {
4005
- currentProvider['url'] = url;
4006
- if (headers && Object.keys(headers).length > 0) {
4007
- currentProvider['request'] = async (payload) => {
4008
- const response = await fetch(url, {
4009
- method: 'POST',
4010
- headers: {
4011
- 'Content-Type': 'application/json',
4012
- ...headers
4013
- },
4014
- body: JSON.stringify(payload)
4015
- });
4016
- if (!response.ok) {
4017
- throw new Error(`HTTP ${response.status}: ${response.statusText}`);
4018
- }
4019
- return await response.json();
4020
- };
4021
- }
4022
- }
4023
- return web3;
4024
- }
4025
- }
4026
- return new Web3(provider);
4027
- }
4028
- extractUrlFromEthersProvider(ethersProvider) {
4029
- return ethersProvider.connection?.url ||
4030
- ethersProvider._getConnection?.()?.url ||
4031
- ethersProvider.url ||
4032
- '';
4033
- }
4034
- extractHeadersFromEthersProvider(ethersProvider) {
4035
- return ethersProvider.connection?.headers ||
4036
- ethersProvider._getConnection?.()?.headers ||
4037
- {};
4038
- }
4039
- }
4040
- /* import Web3 from "web3";
4041
- import { ChainData, ChainType, ChainTypes } from "@explorins/web3-ts";
4042
- import { PublicHttpProviderService } from "./public-http-provider.service";
4043
- import { getWeb3ProviderFromChainData } from "./getWeb3FCD.service";
4044
-
4045
-
4046
- export class Web3ProviderService {
4047
-
4048
- private _web3: Web3 | null = null;
4049
- private _currentChainId: number | null = null;
4050
-
4051
- constructor(
4052
- private readonly publicHttpProviderService: PublicHttpProviderService,
4053
- ) {
4054
- }
4055
-
4056
- public async getWeb3(chainId: number, chainType: ChainType, privateChainData: ChainData | null = null) {
4057
- if (!this._web3 || this._currentChainId !== chainId) {
4058
-
4059
- if(!chainId) throw new Error('ChainId not found')
4060
-
4061
- try {
4062
- this._currentChainId = chainId;
4063
- const provider = await this.getWeb3ByChainId(chainId, chainType, privateChainData);
4064
- this._web3 = this.convertToWeb3(provider);
4065
- } catch (error) {
4066
- console.error('Error getting web3 connection from chain id ' + chainId , error)
4067
- throw new Error('Error getting web3 connection from chain id ' + chainId)
4068
- }
4069
- }
4070
- return this._web3 as Web3;
4071
- }
4072
-
4073
- // Keep return type as 'any' to avoid TypeScript errors while still being adapted later
4074
- private getWeb3ByChainId(chainId: number, chainType: ChainType, privateChainData: ChainData | null = null): any {
4075
- // Rest of the method remains the same
4076
- if(chainType === ChainTypes.PRIVATE && privateChainData) {
4077
- //const privateProvider = this.privateChainProviderService.getProviderFromChainData(privateChainData)
4078
- const privateProvider = getWeb3ProviderFromChainData(privateChainData);
4079
-
4080
- if(!privateProvider || privateProvider instanceof Error) throw new Error('Error getting web3 provider');
4081
-
4082
-
4083
- return privateProvider;
4084
-
4085
- } else {
4086
-
4087
- const publicProvider = this.publicHttpProviderService.getProvider(chainId)
4088
- if(!publicProvider || publicProvider instanceof Error) throw new Error('Error getting web3 provider');
4089
-
4090
- return publicProvider;
4091
- }
4092
- }
4093
-
4094
- private convertToWeb3(provider: unknown): Web3 {
4095
- if (provider instanceof Web3) {
4096
- return provider as Web3;
4097
- }
4098
-
4099
- if (provider && typeof provider === 'object' && 'web3Provider' in provider) {
4100
- const providerObj = provider as {
4101
- web3Provider?: unknown;
4102
- ethersProvider?: any;
4103
- isAuthenticated?: boolean;
4104
- };
4105
-
4106
- // If we want to user the web3Provider directly:
4107
- /*if (providerObj.web3Provider) {
4108
- return new Web3(providerObj.web3Provider as never);
4109
- }*/
4110
- /*if (providerObj.ethersProvider) {
4111
-
4112
- const url = this.extractUrlFromEthersProvider(providerObj.ethersProvider);
4113
- const headers = this.extractHeadersFromEthersProvider(providerObj.ethersProvider);
4114
-
4115
- const web3 = new Web3(url);
4116
- const currentProvider = web3.currentProvider as unknown as Record<string, unknown>;
4117
-
4118
- if (currentProvider) {
4119
- currentProvider['url'] = url;
4120
-
4121
- if (headers && Object.keys(headers).length > 0) {
4122
- currentProvider['request'] = async (payload: Record<string, unknown>): Promise<Record<string, unknown>> => {
4123
- const response = await fetch(url, {
4124
- method: 'POST',
4125
- headers: {
4126
- 'Content-Type': 'application/json',
4127
- ...headers
4128
- },
4129
- body: JSON.stringify(payload)
4130
- });
4131
-
4132
- if (!response.ok) {
4133
- throw new Error(`HTTP ${response.status}: ${response.statusText}`);
4134
- }
4135
-
4136
- return await response.json() as Record<string, unknown>;
4137
- };
4138
- }
4139
- }
4140
-
4141
- return web3;
4142
- }
4143
- }
4144
-
4145
- return new Web3(provider as never);
4146
- }
4147
-
4148
-
4149
- private extractUrlFromEthersProvider(ethersProvider: any): string {
4150
- return ethersProvider.connection?.url ||
4151
- ethersProvider._getConnection?.()?.url ||
4152
- ethersProvider.url ||
4153
- '';
4154
- }
4155
-
4156
- private extractHeadersFromEthersProvider(ethersProvider: any): Record<string, string> {
4157
- return ethersProvider.connection?.headers ||
4158
- ethersProvider._getConnection?.()?.headers ||
4159
- {};
4160
- }
4161
- } */
4162
-
4163
- /**
4164
- * Web3 Chain Domain Models
4165
- *
4166
- * Minimal interface definitions matching framework usage exactly.
4167
- * Local ChainData interface to avoid external dependency issues.
4168
- */
4169
- const ChainTypes = {
4170
- PUBLIC: 'PUBLIC',
4171
- PRIVATE: 'PRIVATE'
4172
- };
4173
-
4174
- function createWeb3ChainSDK(apiClient, providerService) {
4175
- const web3ChainApi = new Web3ChainApi(apiClient);
4176
- const web3ChainService = new Web3ChainService(web3ChainApi, providerService); // ✅ DIRECT INJECTION
4177
- return {
4178
- // ✅ REPLICA: Same methods as framework
4179
- getChainDataById: (chainId) => web3ChainService.getChainDataById(chainId),
4180
- getWeb3ByChainId: (chainId) => web3ChainService.getWeb3ByChainId(chainId),
4181
- api: web3ChainApi,
4182
- service: web3ChainService
4183
- };
4184
- }
4185
-
4186
- /**
4187
- * TokenDomainService - Domain service for token operations
4188
- * Implements business logic for token balance, metadata, and collection operations
4189
- */
4190
- class TokenDomainService {
4191
- constructor(web3Api, metadataService, contractService) {
4192
- this.web3Api = web3Api;
4193
- this.metadataService = metadataService;
4194
- this.contractService = contractService;
4195
- }
4196
- async getTokenBalance(request) {
4197
- const balance = await this.web3Api.getTokenBalance({
4198
- accountAddress: request.accountAddress,
4199
- contractAddress: request.contractAddress,
4200
- abi: request.abi,
4201
- tokenId: request.tokenId,
4202
- chainId: request.chainId
4203
- });
4204
- return {
4205
- tokenId: request.tokenId,
4206
- balance,
4207
- hasBalance: balance > 0,
4208
- metadata: null
4209
- };
4210
- }
4211
- async getTokenWithMetadata(params) {
4212
- try {
4213
- const balance = await this.web3Api.getTokenBalance({
4214
- accountAddress: params.accountAddress,
4215
- contractAddress: params.contractAddress,
4216
- abi: params.abi,
4217
- tokenId: params.tokenId,
4218
- chainId: params.chainId
4219
- });
4220
- let metadata = null;
4221
- if (balance > 0) {
4222
- const tokenUri = await this.web3Api.getTokenUri({
4223
- contractAddress: params.contractAddress,
4224
- abi: params.abi,
4225
- tokenId: params.tokenId,
4226
- chainId: params.chainId
4227
- });
4228
- if (tokenUri) {
4229
- metadata = await this.metadataService.fetchAndProcessMetadata(tokenUri, params.chainId);
4230
- }
4231
- }
4232
- return {
4233
- tokenId: params.tokenId,
4234
- balance,
4235
- hasBalance: balance > 0,
4236
- metadata
4237
- };
4238
- }
4239
- catch (error) {
4240
- console.error('Error getting token with metadata:', error);
4241
- return {
4242
- tokenId: params.tokenId,
4243
- balance: 0,
4244
- hasBalance: false,
4245
- metadata: null
4246
- };
4247
- }
4248
- }
4249
- async getTokenCollection(params) {
4250
- try {
4251
- const contractAnalysis = this.contractService.analyzeContract(params.abi);
4252
- const tokens = [];
4253
- if (!contractAnalysis.hasEnumeration && !contractAnalysis.isERC1155) {
4254
- console.warn('Contract does not support enumeration, cannot retrieve full collection');
4255
- return {
4256
- accountAddress: params.accountAddress,
4257
- contractAddress: params.contractAddress,
4258
- totalBalance: 0,
4259
- tokensRetrieved: 0,
4260
- tokens: [],
4261
- note: 'Contract does not support enumeration'
4262
- };
4263
- }
4264
- else if (contractAnalysis.isERC1155) {
4265
- const tokenIdsToProcess = params.tokenIds || [];
4266
- if (tokenIdsToProcess.length > 0) {
4267
- for (const tokenId of tokenIdsToProcess) {
4268
- const tokenBalance = await this.getTokenWithMetadata({
4269
- accountAddress: params.accountAddress,
4270
- contractAddress: params.contractAddress,
4271
- abi: params.abi,
4272
- tokenId,
4273
- chainId: params.chainId
4274
- });
4275
- tokens.push(tokenBalance);
4276
- }
4277
- }
4278
- console.log('ERC-1155 User balances:', tokens);
4279
- // ERC-1155: Cannot enumerate without knowing token IDs
4280
- // Would need to use events or provide specific token IDs
4281
- console.warn('ERC-1155 collection retrieval requires specific token IDs or event analysis');
4282
- return {
4283
- accountAddress: params.accountAddress,
4284
- contractAddress: params.contractAddress,
4285
- totalBalance: 0,
4286
- tokensRetrieved: 0,
4287
- tokens: tokens,
4288
- note: 'ERC-1155 collection retrieval requires specific token IDs. Use getTokenWithMetadata() for individual tokens.'
4289
- };
4290
- }
4291
- // Handle different token standards
4292
- if (contractAnalysis.isERC721) {
4293
- // ERC-721: Get user's total balance and enumerate through tokens
4294
- const userBalance = await this.web3Api.getTokenBalance({
4295
- accountAddress: params.accountAddress,
4296
- contractAddress: params.contractAddress,
4297
- abi: params.abi,
4298
- tokenId: null, // null for ERC-721 total balance
4299
- chainId: params.chainId
4300
- });
4301
- console.log(`ERC-721 User balance for ${params.accountAddress}:`, userBalance);
4302
- if (userBalance === 0) {
4303
- return {
4304
- accountAddress: params.accountAddress,
4305
- contractAddress: params.contractAddress,
4306
- totalBalance: 0,
4307
- tokensRetrieved: 0,
4308
- tokens: []
4309
- };
4310
- }
4311
- // Enumerate through user's tokens
4312
- const maxTokens = params.maxTokens || userBalance;
4313
- const tokensToRetrieve = Math.min(maxTokens, userBalance);
4314
- for (let i = 0; i < tokensToRetrieve; i++) {
4315
- try {
4316
- const tokenId = await this.web3Api.getTokenOfOwnerByIndex({
4317
- contractAddress: params.contractAddress,
4318
- abi: params.abi,
4319
- accountAddress: params.accountAddress,
4320
- tokenIndex: i,
4321
- chainId: params.chainId
4322
- });
4323
- const tokenWithMetadata = await this.getTokenWithMetadata({
4324
- accountAddress: params.accountAddress,
4325
- contractAddress: params.contractAddress,
4326
- abi: params.abi,
4327
- tokenId,
4328
- chainId: params.chainId
4329
- });
4330
- if (tokenWithMetadata.hasBalance) {
4331
- tokens.push(tokenWithMetadata);
4332
- }
4333
- }
4334
- catch (error) {
4335
- console.warn(`Error retrieving ERC-721 token at index ${i}:`, error);
4336
- continue;
4337
- }
4338
- }
4339
- }
4340
- else {
4341
- // Unknown standard
4342
- return {
4343
- accountAddress: params.accountAddress,
4344
- contractAddress: params.contractAddress,
4345
- totalBalance: 0,
4346
- tokensRetrieved: 0,
4347
- tokens: [],
4348
- note: 'Unsupported token standard for collection retrieval'
4349
- };
4350
- }
4351
- // Calculate total balance based on retrieved tokens
4352
- let totalBalance = 0;
4353
- if (contractAnalysis.isERC721) {
4354
- // For ERC-721, total balance is the number of unique tokens owned
4355
- totalBalance = tokens.length;
4356
- }
4357
- else {
4358
- // For other standards, sum up individual token balances
4359
- totalBalance = tokens.reduce((sum, token) => sum + token.balance, 0);
4360
- }
4361
- return {
4362
- accountAddress: params.accountAddress,
4363
- contractAddress: params.contractAddress,
4364
- totalBalance,
4365
- tokensRetrieved: tokens.length,
4366
- tokens
4367
- };
4368
- }
4369
- catch (error) {
4370
- console.error('Error getting token collection:', error);
4371
- return {
4372
- accountAddress: params.accountAddress,
4373
- contractAddress: params.contractAddress,
4374
- totalBalance: 0,
4375
- tokensRetrieved: 0,
4376
- tokens: [],
4377
- note: 'Error retrieving collection'
4378
- };
4379
- }
4380
- }
4381
- async getTokenMetadata(params) {
4382
- try {
4383
- const tokenUri = await this.web3Api.getTokenUri({
4384
- contractAddress: params.contractAddress,
4385
- abi: params.abi,
4386
- tokenId: params.tokenId,
4387
- chainId: params.chainId
4388
- });
4389
- let metadata = null;
4390
- if (tokenUri) {
4391
- metadata = await this.metadataService.fetchAndProcessMetadata(tokenUri, params.chainId);
4392
- }
4393
- return {
4394
- tokenId: params.tokenId,
4395
- tokenUri,
4396
- metadata
4397
- };
4398
- }
4399
- catch (error) {
4400
- console.error('Error getting token metadata:', error);
4401
- return {
4402
- tokenId: params.tokenId,
4403
- tokenUri: null,
4404
- metadata: null
4405
- };
4406
- }
4407
- }
4408
- }
4409
-
4410
- /**
4411
- * MetadataDomainService - Clean IPFS metadata resolution
4412
- */
4413
- class MetadataDomainService {
4414
- constructor(ipfsApi) {
4415
- this.ipfsApi = ipfsApi;
4416
- }
4417
- async fetchAndProcessMetadata(tokenUri, chainId) {
4418
- return this.ipfsApi.fetchAndProcessMetadata(tokenUri, chainId);
4419
- }
4420
- async resolveIPFSUrl(url, chainId) {
4421
- return this.ipfsApi.resolveIPFSUrl(url, chainId);
4422
- }
4423
- }
4424
-
4425
- /**
4426
- * ContractDomainService - Clean contract analysis without external dependencies
4427
- */
4428
- class ContractDomainService {
4429
- constructor() { }
4430
- analyzeContract(abi) {
4431
- const methods = abi.filter(item => item.type === 'function').map(item => item.name);
4432
- // ERC-721 detection
4433
- const hasOwnerOf = methods.includes('ownerOf');
4434
- const hasTokenURI = methods.includes('tokenURI');
4435
- const hasTransferFrom = methods.includes('transferFrom');
4436
- const isERC721 = hasOwnerOf && hasTokenURI && hasTransferFrom;
4437
- // ERC-1155 detection
4438
- const hasBalanceOfBatch = methods.includes('balanceOfBatch');
4439
- const hasSafeBatchTransferFrom = methods.includes('safeBatchTransferFrom');
4440
- const hasURI = methods.includes('uri');
4441
- const isERC1155 = hasBalanceOfBatch && hasSafeBatchTransferFrom && hasURI;
4442
- return {
4443
- hasEnumeration: methods.includes('tokenByIndex') && methods.includes('totalSupply'),
4444
- hasOwnerOf,
4445
- hasBalanceOf: methods.includes('balanceOf'),
4446
- hasTokenURI,
4447
- hasTransfer: methods.includes('transfer') || methods.includes('transferFrom'),
4448
- hasApprove: methods.includes('approve'),
4449
- isERC721,
4450
- isERC1155
4451
- };
4452
- }
4453
- supportsEnumeration(abi) {
4454
- return this.analyzeContract(abi).hasEnumeration;
4455
- }
4456
- supportsMethod(abi, methodName) {
4457
- const methods = abi.filter(item => item.type === 'function').map(item => item.name);
4458
- return methods.includes(methodName);
4459
- }
4460
- }
4461
-
4462
- /**
4463
- * Web3ApplicationService - Application layer entrance point
4464
- * Orchestrates domain services and provides clean public interface
4465
- * Simplified architecture with concrete classes
4466
- */
4467
- class Web3ApplicationService {
4468
- constructor(web3Api, ipfsApi) {
4469
- // Type-safe metadata conversion methods for ERC-721/ERC-1155 standards
4470
- this.metadataMapper = {
4471
- fromERCStandard: (ercMetadata) => ({
4472
- name: ercMetadata.name || '',
4473
- description: ercMetadata.description || '',
4474
- imageUrl: ercMetadata.image || '',
4475
- externalUrl: ercMetadata.external_url,
4476
- animationUrl: ercMetadata.animation_url,
4477
- animationUrlConverted: undefined, // Will be set by IPFS conversion
4478
- attributes: ercMetadata.attributes || [],
4479
- ...ercMetadata
4480
- }),
4481
- toERCStandard: (metadata) => ({
4482
- name: metadata.name,
4483
- description: metadata.description,
4484
- image: metadata.imageUrl,
4485
- animation_url: metadata.animationUrl,
4486
- external_url: metadata.externalUrl,
4487
- attributes: metadata.attributes,
4488
- ...Object.fromEntries(Object.entries(metadata).filter(([key]) => !['name', 'description', 'imageUrl', 'animationUrl', 'externalUrl', 'attributes', 'animationUrlConverted'].includes(key)))
4489
- })
4490
- };
4491
- // Create domain services with injected infrastructure dependencies
4492
- this.contractDomainService = new ContractDomainService();
4493
- this.metadataDomainService = new MetadataDomainService(ipfsApi);
4494
- this.tokenDomainService = new TokenDomainService(web3Api, this.metadataDomainService, this.contractDomainService);
4495
- }
4496
- /**
4497
- * Get balance and metadata for a specific token
4498
- */
4499
- async getSpecificTokenBalance(request) {
4500
- if (!request.tokenId) {
4501
- return this.tokenDomainService.getTokenBalance({
4502
- accountAddress: request.accountAddress || '',
4503
- contractAddress: request.contractAddress,
4504
- abi: request.abi,
4505
- tokenId: '',
4506
- chainId: request.chainId
4507
- });
4508
- }
4509
- return this.tokenDomainService.getTokenWithMetadata({
4510
- accountAddress: request.accountAddress || '',
4511
- contractAddress: request.contractAddress,
4512
- abi: request.abi,
4513
- tokenId: request.tokenId || '',
4514
- chainId: request.chainId
4515
- });
4516
- }
4517
- /**
4518
- * Get metadata for a specific token from on-chain
4519
- */
4520
- async getTokenMetadata(request) {
4521
- const domainResult = await this.tokenDomainService.getTokenMetadata({
4522
- contractAddress: request.contractAddress,
4523
- abi: request.abi,
4524
- tokenId: request.tokenId || '',
4525
- chainId: request.chainId
4526
- });
4527
- return domainResult.metadata;
4528
- }
4529
- /**
4530
- * Retrieve entire collection of tokens with balance and metadata
4531
- */
4532
- async getTokenCollection(request) {
4533
- return this.tokenDomainService.getTokenCollection({
4534
- accountAddress: request.accountAddress || '',
4535
- contractAddress: request.contractAddress,
4536
- abi: request.abi,
4537
- chainId: request.chainId,
4538
- maxTokens: request.maxTokens,
4539
- tokenIds: request.tokenIds
4540
- });
4541
- }
4542
- /**
4543
- * Resolve IPFS URLs to HTTPS if needed
4544
- */
4545
- async resolveIPFSUrl(url, chainId) {
4546
- return this.metadataDomainService.resolveIPFSUrl(url, chainId);
4547
- }
4548
- /**
4549
- * Fetch and process metadata from URI with IPFS conversion
4550
- */
4551
- async fetchAndProcessMetadata(tokenUri, chainId) {
4552
- const domainMetadata = await this.metadataDomainService.fetchAndProcessMetadata(tokenUri, chainId);
4553
- if (!domainMetadata)
4554
- return null;
4555
- // Convert from ERC token standard to our clean interface
4556
- const cleanMetadata = this.metadataMapper.fromERCStandard(domainMetadata);
4557
- // Add IPFS conversion if needed
4558
- if (cleanMetadata.animationUrl?.startsWith('ipfs://')) {
4559
- return {
4560
- ...cleanMetadata,
4561
- animationUrlConverted: await this.resolveIPFSUrl(cleanMetadata.animationUrl, chainId)
4562
- };
4563
- }
4564
- return cleanMetadata;
4565
- }
4566
- }
4567
-
4568
- /**
4569
- * Web3InfrastructureApi - Infrastructure implementation for blockchain operations
4570
- * Uses @explorins/web3-ts for Web3 interactions
4571
- */
4572
- class Web3InfrastructureApi {
4573
- constructor(web3ChainService) {
4574
- this.web3ChainService = web3ChainService;
4575
- }
4576
- async getTokenBalance(request) {
4577
- try {
4578
- if (request.tokenId !== null)
4579
- request.tokenId = request.tokenId.toString();
4580
- const web3 = await this.web3ChainService.getWeb3ByChainId(request.chainId);
4581
- const contract = getSmartContractInstance(request.contractAddress, request.abi, web3);
4582
- return await getAccountTokenBalance(contract, request.accountAddress, request.tokenId);
4583
- }
4584
- catch (error) {
4585
- console.error(`Failed to get token balance for ${request.accountAddress} for ${request.contractAddress} and tokenId ${request.tokenId}, return 0 instead:`, error);
4586
- return 0;
4587
- }
4588
- }
4589
- async getTokenUri(request) {
4590
- try {
4591
- const web3 = await this.web3ChainService.getWeb3ByChainId(request.chainId);
4592
- const contract = getSmartContractInstance(request.contractAddress, request.abi, web3);
4593
- const tokenId = Number(request.tokenId);
4594
- const tokenUri = await getTokenUri(contract, tokenId);
4595
- return String(tokenUri);
4596
- }
4597
- catch (error) {
4598
- console.error(`Failed to get token URI for tokenId ${request.tokenId}:`, error);
4599
- throw error;
4600
- }
4601
- }
4602
- async getTokenOfOwnerByIndex(request) {
4603
- try {
4604
- const web3 = await this.web3ChainService.getWeb3ByChainId(request.chainId);
4605
- const tokenContract = getSmartContractInstance(request.contractAddress, request.abi, web3);
4606
- const tokenId = await getTokenOfOwnerByIndex(tokenContract, request.accountAddress, request.tokenIndex);
4607
- return String(tokenId);
4608
- }
4609
- catch (error) {
4610
- console.error(`Failed to get token by index ${request.tokenIndex} for ${request.accountAddress}:`, error);
4611
- throw error;
4612
- }
4613
- }
4614
- }
4615
-
4616
- /**
4617
- * IPFSInfrastructureApi - Infrastructure implementation for IPFS operations
4618
- * Uses Web3ChainService for IPFS gateway resolution
4619
- */
4620
- class IPFSInfrastructureApi {
4621
- constructor(web3ChainService) {
4622
- this.web3ChainService = web3ChainService;
4623
- this.defaultIpfsGatewayDomain = 'pers.mypinata.cloud';
4624
- }
4625
- async getIpfsGatewayDomain(chainId) {
4626
- try {
4627
- const chainData = await this.web3ChainService.getChainDataWithCache(chainId);
4628
- return chainData.ipfsGatewayDomain || this.defaultIpfsGatewayDomain;
4629
- }
4630
- catch (error) {
4631
- console.warn(`Failed to get chain data for chainId ${chainId}, using default IPFS gateway:`, error);
4632
- return this.defaultIpfsGatewayDomain;
4633
- }
4634
- }
4635
- async resolveIPFSUrl(url, chainId) {
4636
- if (url.startsWith('ipfs://')) {
4637
- const gateway = await this.getIpfsGatewayDomain(chainId);
4638
- return url.replace('ipfs://', `https://${gateway}/ipfs/`);
4639
- }
4640
- return url;
4641
- }
4642
- async fetchAndProcessMetadata(tokenUri, chainId) {
4643
- try {
4644
- const resolvedUri = await this.resolveIPFSUrl(tokenUri, chainId);
4645
- const response = await fetch(resolvedUri);
4646
- if (!response.ok) {
4647
- throw new Error(`HTTP error! status: ${response.status}`);
4648
- }
4649
- const metadata = await response.json();
4650
- // Process and return clean metadata
4651
- return {
4652
- name: metadata.name || '',
4653
- description: metadata.description || '',
4654
- image: metadata.image ? await this.resolveIPFSUrl(metadata.image, chainId) : '',
4655
- attributes: metadata.attributes || [],
4656
- animation_url: metadata.animation_url ? await this.resolveIPFSUrl(metadata.animation_url, chainId) : undefined,
4657
- external_url: metadata.external_url || undefined
4658
- };
4659
- }
4660
- catch (error) {
4661
- console.error('Error fetching metadata:', error);
4662
- return null;
4663
- }
4664
- }
4665
- async fetchFromUrl(url) {
4666
- try {
4667
- const response = await fetch(url);
4668
- if (!response.ok) {
4669
- throw new Error(`Failed to fetch from ${url}: ${response.statusText}`);
4670
- }
4671
- return await response.json();
4672
- }
4673
- catch (error) {
4674
- console.error(`Error fetching from URL ${url}:`, error);
4675
- throw error;
4676
- }
4677
- }
4678
- }
4679
-
4680
- function createWeb3SDK(apiClient) {
4681
- // TODO: FIX LATER - TEMPORARY CONSTRUCTION
4682
- const web3ProviderService = new Web3ProviderService();
4683
- const web3ChainSDK = createWeb3ChainSDK(apiClient, web3ProviderService);
4684
- // Create Web3ApplicationService - main entry point for all Web3 operations
4685
- const web3InfrastructureApi = new Web3InfrastructureApi(web3ChainSDK.service);
4686
- const ipfsInfrastructureApi = new IPFSInfrastructureApi(web3ChainSDK.service);
4687
- const web3ApplicationService = new Web3ApplicationService(web3InfrastructureApi, ipfsInfrastructureApi);
4688
- // Clean SDK - all functions route through Web3ApplicationService
4689
- return {
4690
- getTokenBalance: (request) => web3ApplicationService.getSpecificTokenBalance(request),
4691
- getTokenMetadata: (request) => web3ApplicationService.getTokenMetadata(request),
4692
- getTokenCollection: (request) => web3ApplicationService.getTokenCollection(request),
4693
- resolveIPFSUrl: (url, chainId) => web3ApplicationService.resolveIPFSUrl(url, chainId),
4694
- fetchAndProcessMetadata: (tokenUri, chainId) => web3ApplicationService.fetchAndProcessMetadata(tokenUri, chainId),
4695
- applicationService: web3ApplicationService
4696
- };
4697
- }
4698
-
4699
- export { AnalyticsApi, AnalyticsService, AuthAdminApi, AuthAdminService, BaseTokenService, BusinessApi, BusinessService, CampaignApi, CampaignService, ChainTypes, DEFAULT_PERS_CONFIG, DonationApi, DonationService, IPFSInfrastructureApi, PurchaseApi as PaymentApi, PaymentService, PersApiClient, PersApiError, PersSDK, RedemptionApi, RedemptionService, TenantApi, TenantService, TokenApi, TokenSDK, TokenService, TransactionAccountType, TransactionApi, TransactionService, UserApi, UserService, UserStatusApi, UserStatusService, Web3ApplicationService, Web3ChainApi, Web3ChainService, Web3InfrastructureApi, Web3ProviderService, buildApiRoot, createAnalyticsSDK, createAuthAdminSDK, createAuthProvider, createBusinessSDK, createCampaignSDK, createDonationSDK, createPaymentSDK, createPersSDK, createRedemptionSDK, createTenantSDK, createTransactionSDK, createUserSDK, createUserStatusSDK, createWeb3ChainSDK, createWeb3SDK, mergeWithDefaults };
1
+ export { D as DEFAULT_PERS_CONFIG, a as PersApiClient, P as PersSDK, b as buildApiRoot, c as createPersSDK, m as mergeWithDefaults } from './chunks/pers-sdk-JC-hSYUd.js';
2
+ export { BusinessApi, BusinessService, createBusinessSDK } from './business.js';
3
+ export { TransactionAccountType, TransactionApi, TransactionService, createTransactionSDK } from './transaction.js';
4
+ export { AnalyticsApi, AnalyticsService, createAnalyticsSDK } from './analytics.js';
5
+ export { CampaignApi, CampaignService, createCampaignSDK } from './campaign.js';
6
+ export { DonationApi, DonationService, createDonationSDK } from './donation.js';
7
+ export { PaymentApi, PaymentService, createPaymentSDK } from './payment.js';
8
+ export { RedemptionApi, RedemptionService, createRedemptionSDK } from './redemption.js';
9
+ export { TenantApi, TenantService, createTenantSDK } from './tenant.js';
10
+ export { B as BaseTokenService, a as TokenApi, T as TokenSDK, b as TokenService } from './chunks/base-token-service-BA81_Ouq.js';
11
+ export { AccountOwnerType } from '@explorins/pers-shared';
12
+ export { UserApi, UserService, createUserSDK } from './user.js';
13
+ export { UserStatusApi, UserStatusService, createUserStatusSDK } from './user-status.js';
14
+ export { ChainTypes, Web3ChainApi, Web3ChainService, Web3ProviderService, createWeb3ChainSDK } from './web3-chain.js';
15
+ export { IPFSInfrastructureApi, Web3ApplicationService, Web3InfrastructureApi, createWeb3SDK } from './web3.js';
16
+ import './chunks/jwt.function-d6jPtBqI.js';
17
+ import 'jwt-decode';
18
+ import 'web3';
19
+ import 'ethers';
20
+ import '@explorins/web3-ts';
4700
21
  //# sourceMappingURL=index.js.map