@explorins/pers-sdk 2.1.10 → 2.1.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/business/api/business-membership-api.d.ts +2 -2
- package/dist/chunks/{index-B-g2JPVK.cjs → index-CGaKfZNU.cjs} +279 -1
- package/dist/chunks/index-CGaKfZNU.cjs.map +1 -0
- package/dist/chunks/{index-CKm_V5XE.js → index-DgTEdUgC.js} +277 -2
- package/dist/chunks/index-DgTEdUgC.js.map +1 -0
- package/dist/chunks/{pers-sdk-Bh-m0x17.js → pers-sdk-BiP7UMJ3.js} +1695 -177
- package/dist/chunks/pers-sdk-BiP7UMJ3.js.map +1 -0
- package/dist/chunks/{pers-sdk-Co-R9M1O.cjs → pers-sdk-Cv7hM1I7.cjs} +1701 -177
- package/dist/chunks/pers-sdk-Cv7hM1I7.cjs.map +1 -0
- package/dist/chunks/tenant-manager-BUiFM33X.cjs +157 -0
- package/dist/chunks/tenant-manager-BUiFM33X.cjs.map +1 -0
- package/dist/chunks/tenant-manager-Bbj0bKoo.js +155 -0
- package/dist/chunks/tenant-manager-Bbj0bKoo.js.map +1 -0
- package/dist/chunks/{transaction-request.builder-DGTxGvc3.js → transaction-request.builder-C3C19kCx.js} +23 -2
- package/dist/chunks/{transaction-request.builder-DGTxGvc3.js.map → transaction-request.builder-C3C19kCx.js.map} +1 -1
- package/dist/chunks/{transaction-request.builder-Bjxi0C9F.cjs → transaction-request.builder-CW3Wwdi3.cjs} +23 -1
- package/dist/chunks/{transaction-request.builder-Bjxi0C9F.cjs.map → transaction-request.builder-CW3Wwdi3.cjs.map} +1 -1
- package/dist/chunks/{web3-chain-service-D68-0WaW.cjs → web3-chain-service-DcLiy3m2.cjs} +7 -7
- package/dist/chunks/{web3-chain-service-D68-0WaW.cjs.map → web3-chain-service-DcLiy3m2.cjs.map} +1 -1
- package/dist/chunks/{web3-chain-service-DuvxmKSj.js → web3-chain-service-nGntR60S.js} +3 -3
- package/dist/chunks/{web3-chain-service-DuvxmKSj.js.map → web3-chain-service-nGntR60S.js.map} +1 -1
- package/dist/chunks/{web3-manager-OExwBWB7.js → web3-manager-BbJzGd0y.js} +67 -53
- package/dist/chunks/web3-manager-BbJzGd0y.js.map +1 -0
- package/dist/chunks/{web3-manager-C_cFaMCm.cjs → web3-manager-fRGFR_pM.cjs} +67 -53
- package/dist/chunks/web3-manager-fRGFR_pM.cjs.map +1 -0
- package/dist/core/events/event-emitter.d.ts.map +1 -1
- package/dist/core/pers-config.d.ts +19 -0
- package/dist/core/pers-config.d.ts.map +1 -1
- package/dist/core.cjs +17 -14
- package/dist/core.cjs.map +1 -1
- package/dist/core.js +5 -5
- package/dist/events/index.d.ts +13 -0
- package/dist/events/index.d.ts.map +1 -0
- package/dist/events/pers-events-client.d.ts +143 -0
- package/dist/events/pers-events-client.d.ts.map +1 -0
- package/dist/events/types.d.ts +37 -0
- package/dist/events/types.d.ts.map +1 -0
- package/dist/index.cjs +41 -13
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -5
- package/dist/managers/events-manager.d.ts +179 -0
- package/dist/managers/events-manager.d.ts.map +1 -0
- package/dist/managers/index.d.ts +2 -0
- package/dist/managers/index.d.ts.map +1 -1
- package/dist/managers/tenant-manager.d.ts +42 -1
- package/dist/managers/tenant-manager.d.ts.map +1 -1
- package/dist/managers/user-manager.d.ts +21 -18
- package/dist/managers/user-manager.d.ts.map +1 -1
- package/dist/managers/web3-manager.d.ts +12 -14
- package/dist/managers/web3-manager.d.ts.map +1 -1
- package/dist/managers/webhook-manager.d.ts +237 -0
- package/dist/managers/webhook-manager.d.ts.map +1 -0
- package/dist/node.cjs +4 -3
- package/dist/node.cjs.map +1 -1
- package/dist/node.js +4 -3
- package/dist/node.js.map +1 -1
- package/dist/package.json +3 -2
- package/dist/pers-sdk.d.ts +110 -1
- package/dist/pers-sdk.d.ts.map +1 -1
- package/dist/transaction/index.d.ts +1 -1
- package/dist/transaction/index.d.ts.map +1 -1
- package/dist/transaction/models/transaction-request.builder.d.ts +16 -0
- package/dist/transaction/models/transaction-request.builder.d.ts.map +1 -1
- package/dist/transaction.cjs +2 -1
- package/dist/transaction.cjs.map +1 -1
- package/dist/transaction.js +1 -1
- package/dist/user/api/user-api.d.ts +15 -2
- package/dist/user/api/user-api.d.ts.map +1 -1
- package/dist/user/services/user-service.d.ts +5 -3
- package/dist/user/services/user-service.d.ts.map +1 -1
- package/dist/user.cjs +23 -7
- package/dist/user.cjs.map +1 -1
- package/dist/user.js +23 -7
- package/dist/user.js.map +1 -1
- package/dist/web3/application/web3-application.service.d.ts +6 -3
- package/dist/web3/application/web3-application.service.d.ts.map +1 -1
- package/dist/web3/domain/services/metadata-domain.service.d.ts +4 -2
- package/dist/web3/domain/services/metadata-domain.service.d.ts.map +1 -1
- package/dist/web3/infrastructure/api/ipfs-api.d.ts +22 -9
- package/dist/web3/infrastructure/api/ipfs-api.d.ts.map +1 -1
- package/dist/web3/utils/explorer.utils.d.ts +3 -3
- package/dist/web3/utils/explorer.utils.d.ts.map +1 -1
- package/dist/web3-chain/api/web3-chain-api.d.ts +3 -2
- package/dist/web3-chain/api/web3-chain-api.d.ts.map +1 -1
- package/dist/web3-chain/index.d.ts +0 -1
- package/dist/web3-chain/index.d.ts.map +1 -1
- package/dist/web3-chain/models/index.d.ts +5 -23
- package/dist/web3-chain/models/index.d.ts.map +1 -1
- package/dist/web3-chain/services/web3-chain-service.d.ts +2 -2
- package/dist/web3-chain/services/web3-chain-service.d.ts.map +1 -1
- package/dist/web3-chain.cjs +8 -14
- package/dist/web3-chain.cjs.map +1 -1
- package/dist/web3-chain.js +3 -16
- package/dist/web3-chain.js.map +1 -1
- package/dist/web3-manager.cjs +7 -4
- package/dist/web3-manager.cjs.map +1 -1
- package/dist/web3-manager.js +7 -4
- package/dist/web3-manager.js.map +1 -1
- package/dist/web3.cjs +7 -4
- package/dist/web3.cjs.map +1 -1
- package/dist/web3.js +7 -4
- package/dist/web3.js.map +1 -1
- package/dist/webhook/api/index.d.ts +2 -0
- package/dist/webhook/api/index.d.ts.map +1 -0
- package/dist/webhook/api/webhook-api.d.ts +73 -0
- package/dist/webhook/api/webhook-api.d.ts.map +1 -0
- package/dist/webhook/index.d.ts +35 -0
- package/dist/webhook/index.d.ts.map +1 -0
- package/dist/webhook/models/index.d.ts +58 -0
- package/dist/webhook/models/index.d.ts.map +1 -0
- package/dist/webhook/services/index.d.ts +2 -0
- package/dist/webhook/services/index.d.ts.map +1 -0
- package/dist/webhook/services/webhook-service.d.ts +98 -0
- package/dist/webhook/services/webhook-service.d.ts.map +1 -0
- package/package.json +3 -2
- package/dist/chunks/index-B-g2JPVK.cjs.map +0 -1
- package/dist/chunks/index-B6-bbNnd.cjs +0 -281
- package/dist/chunks/index-B6-bbNnd.cjs.map +0 -1
- package/dist/chunks/index-CKm_V5XE.js.map +0 -1
- package/dist/chunks/index-DBLskLuH.js +0 -277
- package/dist/chunks/index-DBLskLuH.js.map +0 -1
- package/dist/chunks/pers-sdk-Bh-m0x17.js.map +0 -1
- package/dist/chunks/pers-sdk-Co-R9M1O.cjs.map +0 -1
- package/dist/chunks/web3-manager-C_cFaMCm.cjs.map +0 -1
- package/dist/chunks/web3-manager-OExwBWB7.js.map +0 -1
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import { AccountOwnerType, MembershipRole } from '@explorins/pers-shared';
|
|
2
|
-
import { i as isTokenExpired, E as ErrorUtils, A as AuthenticationError,
|
|
1
|
+
import { AccountOwnerType, MembershipRole, WebhookMethod, WebhookExecutionStatus } from '@explorins/pers-shared';
|
|
2
|
+
import { i as isTokenExpired, E as ErrorUtils, A as AuthenticationError, b as PersApiError } from './index-DgTEdUgC.js';
|
|
3
3
|
import { UserService, UserApi } from '../user.js';
|
|
4
4
|
import { createUserStatusSDK } from '../user-status.js';
|
|
5
5
|
import { a as TokenService, T as TokenApi } from './token-service-BxEO5YVN.js';
|
|
6
6
|
import { B as BusinessApi, b as BusinessService, a as BusinessMembershipApi, c as BusinessMembershipService } from './business-membership-service-CFa-TI39.js';
|
|
7
7
|
import { CampaignService, CampaignApi } from '../campaign.js';
|
|
8
8
|
import { a as RedemptionService, R as RedemptionApi } from './redemption-service-C_UTTDag.js';
|
|
9
|
-
import { a as TransactionService, T as TransactionApi } from './transaction-request.builder-
|
|
9
|
+
import { a as TransactionService, T as TransactionApi } from './transaction-request.builder-C3C19kCx.js';
|
|
10
10
|
import { a as PaymentService, P as PurchaseApi } from './payment-service-IvM6rryM.js';
|
|
11
|
-
import {
|
|
11
|
+
import { T as TenantManager } from './tenant-manager-Bbj0bKoo.js';
|
|
12
12
|
import { b as buildPaginationParams, n as normalizeToPaginated } from './pagination-utils-9vQ-Npkr.js';
|
|
13
13
|
import { a as AnalyticsService, A as AnalyticsApi } from './analytics-service-vm7B7LhS.js';
|
|
14
14
|
import { DonationService, DonationApi } from '../donation.js';
|
|
@@ -29,7 +29,8 @@ const DEFAULT_PERS_CONFIG = {
|
|
|
29
29
|
timeout: 30000,
|
|
30
30
|
retries: 3,
|
|
31
31
|
tokenRefreshMargin: 60, // Refresh tokens 60 seconds before expiry
|
|
32
|
-
backgroundRefreshThreshold: 30 // Use background refresh if >30s remaining
|
|
32
|
+
backgroundRefreshThreshold: 30, // Use background refresh if >30s remaining
|
|
33
|
+
captureWalletEvents: true // Auto-connect to wallet events after auth
|
|
33
34
|
};
|
|
34
35
|
/**
|
|
35
36
|
* Build the API root URL based on config
|
|
@@ -51,6 +52,21 @@ function buildApiRoot(environment = 'production', version = 'v2', customApiUrl)
|
|
|
51
52
|
};
|
|
52
53
|
return baseUrls[environment];
|
|
53
54
|
}
|
|
55
|
+
/**
|
|
56
|
+
* Build wallet events WebSocket URL based on config
|
|
57
|
+
*
|
|
58
|
+
* @internal
|
|
59
|
+
*/
|
|
60
|
+
function buildWalletEventsWsUrl(environment = 'production', customWsUrl) {
|
|
61
|
+
if (customWsUrl) {
|
|
62
|
+
return customWsUrl;
|
|
63
|
+
}
|
|
64
|
+
const wsUrls = {
|
|
65
|
+
staging: 'wss://events-staging.pers.ninja',
|
|
66
|
+
production: 'wss://events.pers.ninja'
|
|
67
|
+
};
|
|
68
|
+
return wsUrls[environment];
|
|
69
|
+
}
|
|
54
70
|
/**
|
|
55
71
|
* Merge user config with defaults
|
|
56
72
|
*/
|
|
@@ -1411,7 +1427,6 @@ class PersEventEmitter {
|
|
|
1411
1427
|
constructor() {
|
|
1412
1428
|
this.handlers = new Set();
|
|
1413
1429
|
this._instanceId = ++emitterInstanceCounter;
|
|
1414
|
-
console.log(`[PersEventEmitter] Instance #${this._instanceId} created`);
|
|
1415
1430
|
}
|
|
1416
1431
|
get instanceId() {
|
|
1417
1432
|
return this._instanceId;
|
|
@@ -2234,34 +2249,37 @@ class UserManager {
|
|
|
2234
2249
|
return this.userService.updateUserAsAdmin(userId, userData);
|
|
2235
2250
|
}
|
|
2236
2251
|
/**
|
|
2237
|
-
* Admin:
|
|
2252
|
+
* Admin: Set or toggle user active status
|
|
2238
2253
|
*
|
|
2239
|
-
*
|
|
2240
|
-
*
|
|
2254
|
+
* Sets the active/inactive status of a user account explicitly, or toggles
|
|
2255
|
+
* if no explicit value is provided. This is typically used for account
|
|
2256
|
+
* suspension or reactivation. Requires administrator privileges.
|
|
2241
2257
|
*
|
|
2242
|
-
* @param
|
|
2258
|
+
* @param userId - User ID to update
|
|
2259
|
+
* @param isActive - Optional explicit status. If provided, sets to this value. If omitted, toggles current status.
|
|
2243
2260
|
* @returns Promise resolving to updated user data
|
|
2244
2261
|
* @throws {PersApiError} When not authenticated as admin
|
|
2245
2262
|
*
|
|
2246
|
-
* @example
|
|
2263
|
+
* @example Explicit Status
|
|
2247
2264
|
* ```typescript
|
|
2248
|
-
* // Admin operation -
|
|
2249
|
-
*
|
|
2250
|
-
*
|
|
2251
|
-
* const updated = await sdk.users.toggleUserStatus(user);
|
|
2265
|
+
* // Admin operation - explicitly activate user
|
|
2266
|
+
* const activated = await sdk.users.setUserActiveStatus('user-123', true);
|
|
2267
|
+
* console.log('User activated:', activated.isActive); // true
|
|
2252
2268
|
*
|
|
2253
|
-
*
|
|
2254
|
-
*
|
|
2255
|
-
*
|
|
2256
|
-
*
|
|
2257
|
-
*
|
|
2258
|
-
*
|
|
2259
|
-
*
|
|
2260
|
-
*
|
|
2269
|
+
* // Explicitly deactivate user
|
|
2270
|
+
* const deactivated = await sdk.users.setUserActiveStatus('user-123', false);
|
|
2271
|
+
* console.log('User deactivated:', deactivated.isActive); // false
|
|
2272
|
+
* ```
|
|
2273
|
+
*
|
|
2274
|
+
* @example Toggle Status
|
|
2275
|
+
* ```typescript
|
|
2276
|
+
* // Admin operation - toggle current status
|
|
2277
|
+
* const toggled = await sdk.users.setUserActiveStatus('user-123');
|
|
2278
|
+
* console.log('User status toggled:', toggled.isActive);
|
|
2261
2279
|
* ```
|
|
2262
2280
|
*/
|
|
2263
|
-
async
|
|
2264
|
-
return this.userService.
|
|
2281
|
+
async setUserActiveStatus(userId, isActive) {
|
|
2282
|
+
return this.userService.setUserActiveStatus(userId, isActive);
|
|
2265
2283
|
}
|
|
2266
2284
|
/**
|
|
2267
2285
|
* Admin: Delete user (soft delete)
|
|
@@ -6377,89 +6395,6 @@ class FileManager {
|
|
|
6377
6395
|
}
|
|
6378
6396
|
}
|
|
6379
6397
|
|
|
6380
|
-
/**
|
|
6381
|
-
* Tenant Manager - Clean, high-level interface for tenant operations
|
|
6382
|
-
*
|
|
6383
|
-
* Provides a simplified API for common tenant management tasks while maintaining
|
|
6384
|
-
* access to the full tenant SDK for advanced use cases.
|
|
6385
|
-
*/
|
|
6386
|
-
class TenantManager {
|
|
6387
|
-
constructor(apiClient) {
|
|
6388
|
-
this.apiClient = apiClient;
|
|
6389
|
-
const tenantApi = new TenantApi(apiClient);
|
|
6390
|
-
this.tenantService = new TenantService(tenantApi);
|
|
6391
|
-
}
|
|
6392
|
-
/**
|
|
6393
|
-
* Get current tenant information
|
|
6394
|
-
*
|
|
6395
|
-
* @returns Promise resolving to tenant data
|
|
6396
|
-
*/
|
|
6397
|
-
async getTenantInfo() {
|
|
6398
|
-
return this.tenantService.getRemoteTenant();
|
|
6399
|
-
}
|
|
6400
|
-
/**
|
|
6401
|
-
* Get tenant login token
|
|
6402
|
-
*
|
|
6403
|
-
* @returns Promise resolving to login token
|
|
6404
|
-
*/
|
|
6405
|
-
async getLoginToken() {
|
|
6406
|
-
return this.tenantService.getRemoteLoginToken();
|
|
6407
|
-
}
|
|
6408
|
-
/**
|
|
6409
|
-
* Get tenant client configuration
|
|
6410
|
-
*
|
|
6411
|
-
* @returns Promise resolving to client config
|
|
6412
|
-
*/
|
|
6413
|
-
async getClientConfig() {
|
|
6414
|
-
return this.tenantService.getRemoteClientConfig();
|
|
6415
|
-
}
|
|
6416
|
-
/**
|
|
6417
|
-
* Admin: Update tenant data
|
|
6418
|
-
*
|
|
6419
|
-
* @param tenantData - Updated tenant data
|
|
6420
|
-
* @returns Promise resolving to updated tenant
|
|
6421
|
-
*/
|
|
6422
|
-
async updateTenant(tenantData) {
|
|
6423
|
-
return this.tenantService.updateRemoteTenant(tenantData);
|
|
6424
|
-
}
|
|
6425
|
-
/**
|
|
6426
|
-
* Admin: Get all admins
|
|
6427
|
-
*
|
|
6428
|
-
* @param options - Pagination options
|
|
6429
|
-
* @returns Promise resolving to paginated admins
|
|
6430
|
-
*/
|
|
6431
|
-
async getAdmins(options) {
|
|
6432
|
-
return this.tenantService.getAdmins(options);
|
|
6433
|
-
}
|
|
6434
|
-
/**
|
|
6435
|
-
* Admin: Create new admin
|
|
6436
|
-
*
|
|
6437
|
-
* @param adminData - Admin data
|
|
6438
|
-
* @returns Promise resolving to created admin
|
|
6439
|
-
*/
|
|
6440
|
-
async createAdmin(adminData) {
|
|
6441
|
-
return this.tenantService.postAdmin(adminData);
|
|
6442
|
-
}
|
|
6443
|
-
/**
|
|
6444
|
-
* Admin: Update existing admin
|
|
6445
|
-
*
|
|
6446
|
-
* @param adminId - ID of the admin to update
|
|
6447
|
-
* @param adminData - Updated admin data
|
|
6448
|
-
* @returns Promise resolving to updated admin
|
|
6449
|
-
*/
|
|
6450
|
-
async updateAdmin(adminId, adminData) {
|
|
6451
|
-
return this.tenantService.putAdmin(adminId, adminData);
|
|
6452
|
-
}
|
|
6453
|
-
/**
|
|
6454
|
-
* Get the tenant service for advanced operations
|
|
6455
|
-
*
|
|
6456
|
-
* @returns TenantService instance
|
|
6457
|
-
*/
|
|
6458
|
-
getTenantService() {
|
|
6459
|
-
return this.tenantService;
|
|
6460
|
-
}
|
|
6461
|
-
}
|
|
6462
|
-
|
|
6463
6398
|
/**
|
|
6464
6399
|
* Platform-Agnostic API Key API Client
|
|
6465
6400
|
*
|
|
@@ -7395,102 +7330,1513 @@ class TriggerSourceManager {
|
|
|
7395
7330
|
}
|
|
7396
7331
|
|
|
7397
7332
|
/**
|
|
7398
|
-
*
|
|
7333
|
+
* Webhook API - Low-level API client for webhook operations
|
|
7399
7334
|
*
|
|
7400
|
-
*
|
|
7401
|
-
*
|
|
7402
|
-
*
|
|
7403
|
-
*
|
|
7335
|
+
* Backend routes:
|
|
7336
|
+
* - Admin CRUD: /hooks (requires tenant auth)
|
|
7337
|
+
* - Proxy/Trigger: /hooks/:projectKey/:hookId (public, identified by projectKey)
|
|
7338
|
+
* - Callback: /hooks/:projectKey/executions/:executionId/callback (public)
|
|
7339
|
+
*
|
|
7340
|
+
* @internal Use WebhookService or WebhookManager for higher-level operations
|
|
7404
7341
|
*/
|
|
7342
|
+
class WebhookApi {
|
|
7343
|
+
constructor(apiClient) {
|
|
7344
|
+
this.apiClient = apiClient;
|
|
7345
|
+
this.basePath = '/hooks';
|
|
7346
|
+
}
|
|
7347
|
+
// ================================
|
|
7348
|
+
// ADMIN: Webhook Configuration CRUD
|
|
7349
|
+
// All require tenant auth (handled by apiClient)
|
|
7350
|
+
// ================================
|
|
7351
|
+
/**
|
|
7352
|
+
* List all webhooks (Admin)
|
|
7353
|
+
* GET /hooks
|
|
7354
|
+
*/
|
|
7355
|
+
async listWebhooks(options) {
|
|
7356
|
+
const params = new URLSearchParams();
|
|
7357
|
+
if (options?.active !== undefined)
|
|
7358
|
+
params.append('active', String(options.active));
|
|
7359
|
+
if (options?.page)
|
|
7360
|
+
params.append('page', String(options.page));
|
|
7361
|
+
if (options?.limit)
|
|
7362
|
+
params.append('limit', String(options.limit));
|
|
7363
|
+
if (options?.search)
|
|
7364
|
+
params.append('search', options.search);
|
|
7365
|
+
const queryString = params.toString();
|
|
7366
|
+
const url = queryString ? `${this.basePath}?${queryString}` : this.basePath;
|
|
7367
|
+
return this.apiClient.get(url);
|
|
7368
|
+
}
|
|
7369
|
+
/**
|
|
7370
|
+
* Get webhook by ID (Admin)
|
|
7371
|
+
* GET /hooks/:hookId
|
|
7372
|
+
*/
|
|
7373
|
+
async getWebhookById(webhookId) {
|
|
7374
|
+
return this.apiClient.get(`${this.basePath}/${webhookId}`);
|
|
7375
|
+
}
|
|
7376
|
+
/**
|
|
7377
|
+
* Create a new webhook (Admin)
|
|
7378
|
+
* POST /hooks
|
|
7379
|
+
*/
|
|
7380
|
+
async createWebhook(webhook) {
|
|
7381
|
+
return this.apiClient.post(this.basePath, webhook);
|
|
7382
|
+
}
|
|
7383
|
+
/**
|
|
7384
|
+
* Update a webhook (Admin)
|
|
7385
|
+
* PUT /hooks/:hookId
|
|
7386
|
+
*/
|
|
7387
|
+
async updateWebhook(webhookId, webhook) {
|
|
7388
|
+
return this.apiClient.put(`${this.basePath}/${webhookId}`, webhook);
|
|
7389
|
+
}
|
|
7390
|
+
/**
|
|
7391
|
+
* Delete a webhook (Admin)
|
|
7392
|
+
* DELETE /hooks/:hookId
|
|
7393
|
+
*/
|
|
7394
|
+
async deleteWebhook(webhookId) {
|
|
7395
|
+
return this.apiClient.delete(`${this.basePath}/${webhookId}`);
|
|
7396
|
+
}
|
|
7397
|
+
// ================================
|
|
7398
|
+
// Webhook Triggering via Proxy
|
|
7399
|
+
// Route: /hooks/:projectKey/:hookId
|
|
7400
|
+
// Public endpoint identified by projectKey in URL
|
|
7401
|
+
// ================================
|
|
7402
|
+
/**
|
|
7403
|
+
* Trigger a webhook programmatically
|
|
7404
|
+
*
|
|
7405
|
+
* Uses the proxy endpoint which requires projectKey in the URL path.
|
|
7406
|
+
* The projectKey is obtained from SDK config.
|
|
7407
|
+
*
|
|
7408
|
+
* ALL /hooks/:projectKey/:hookId
|
|
7409
|
+
*/
|
|
7410
|
+
async triggerWebhook(request, projectKey) {
|
|
7411
|
+
const { hookId, method = 'POST', body, queryParams, waitForCallback, callbackTimeoutMs } = request;
|
|
7412
|
+
// Build query string from queryParams
|
|
7413
|
+
const params = new URLSearchParams();
|
|
7414
|
+
if (queryParams) {
|
|
7415
|
+
Object.entries(queryParams).forEach(([key, value]) => params.append(key, value));
|
|
7416
|
+
}
|
|
7417
|
+
if (waitForCallback)
|
|
7418
|
+
params.append('waitForCallback', 'true');
|
|
7419
|
+
if (callbackTimeoutMs)
|
|
7420
|
+
params.append('callbackTimeoutMs', String(callbackTimeoutMs));
|
|
7421
|
+
const queryString = params.toString();
|
|
7422
|
+
// Use proxy path with projectKey
|
|
7423
|
+
const proxyPath = `${this.basePath}/${projectKey}/${hookId}`;
|
|
7424
|
+
const url = queryString ? `${proxyPath}?${queryString}` : proxyPath;
|
|
7425
|
+
// Use appropriate HTTP method - backend returns WebhookTriggerResponseDTO
|
|
7426
|
+
switch (method) {
|
|
7427
|
+
case 'GET':
|
|
7428
|
+
return this.apiClient.get(url);
|
|
7429
|
+
case 'PUT':
|
|
7430
|
+
return this.apiClient.put(url, body);
|
|
7431
|
+
case 'DELETE':
|
|
7432
|
+
return this.apiClient.delete(url);
|
|
7433
|
+
case 'POST':
|
|
7434
|
+
default:
|
|
7435
|
+
return this.apiClient.post(url, body);
|
|
7436
|
+
}
|
|
7437
|
+
}
|
|
7438
|
+
// ================================
|
|
7439
|
+
// Execution History (Admin)
|
|
7440
|
+
// GET /hooks/executions
|
|
7441
|
+
// ================================
|
|
7442
|
+
/**
|
|
7443
|
+
* List webhook executions (Admin)
|
|
7444
|
+
* GET /hooks/executions
|
|
7445
|
+
*/
|
|
7446
|
+
async listExecutions(options) {
|
|
7447
|
+
const params = new URLSearchParams();
|
|
7448
|
+
if (options?.webhookId)
|
|
7449
|
+
params.append('hookId', options.webhookId);
|
|
7450
|
+
if (options?.status)
|
|
7451
|
+
params.append('status', options.status);
|
|
7452
|
+
if (options?.fromDate)
|
|
7453
|
+
params.append('dateFrom', options.fromDate);
|
|
7454
|
+
if (options?.toDate)
|
|
7455
|
+
params.append('dateTo', options.toDate);
|
|
7456
|
+
if (options?.page)
|
|
7457
|
+
params.append('page', String(options.page));
|
|
7458
|
+
if (options?.limit)
|
|
7459
|
+
params.append('limit', String(options.limit));
|
|
7460
|
+
const queryString = params.toString();
|
|
7461
|
+
const url = queryString ? `${this.basePath}/executions?${queryString}` : `${this.basePath}/executions`;
|
|
7462
|
+
return this.apiClient.get(url);
|
|
7463
|
+
}
|
|
7464
|
+
/**
|
|
7465
|
+
* Get execution by ID (Admin)
|
|
7466
|
+
* Note: This endpoint doesn't exist in current backend - would need to filter by ID
|
|
7467
|
+
*/
|
|
7468
|
+
async getExecutionById(executionId) {
|
|
7469
|
+
// Filter executions by ID since direct endpoint doesn't exist
|
|
7470
|
+
const result = await this.listExecutions({ page: 1, limit: 1 });
|
|
7471
|
+
const execution = result.data.find(e => e.id === executionId);
|
|
7472
|
+
if (!execution) {
|
|
7473
|
+
throw new Error(`Execution ${executionId} not found`);
|
|
7474
|
+
}
|
|
7475
|
+
return execution;
|
|
7476
|
+
}
|
|
7477
|
+
// ================================
|
|
7478
|
+
// Callback (Public - called by external systems)
|
|
7479
|
+
// POST /hooks/:projectKey/executions/:executionId/callback
|
|
7480
|
+
// ================================
|
|
7481
|
+
/**
|
|
7482
|
+
* Send callback result (for testing or manual callback trigger)
|
|
7483
|
+
* This is normally called by external systems like n8n
|
|
7484
|
+
*
|
|
7485
|
+
* POST /hooks/:projectKey/executions/:executionId/callback
|
|
7486
|
+
*/
|
|
7487
|
+
async sendCallback(executionId, callback, projectKey) {
|
|
7488
|
+
return this.apiClient.post(`${this.basePath}/${projectKey}/executions/${executionId}/callback`, callback);
|
|
7489
|
+
}
|
|
7490
|
+
}
|
|
7491
|
+
|
|
7405
7492
|
/**
|
|
7406
|
-
*
|
|
7493
|
+
* Webhook Service - Business logic layer for webhook operations
|
|
7407
7494
|
*
|
|
7408
|
-
*
|
|
7409
|
-
*
|
|
7410
|
-
*
|
|
7411
|
-
*
|
|
7412
|
-
* - **Managers**: High-level, intuitive APIs for common operations
|
|
7413
|
-
* - **Domain Services**: Full-featured access for advanced use cases
|
|
7414
|
-
* - **API Client**: Direct REST API access for custom operations
|
|
7495
|
+
* Provides higher-level operations on top of WebhookApi including:
|
|
7496
|
+
* - Validation and error handling
|
|
7497
|
+
* - Convenience methods for common patterns
|
|
7498
|
+
* - Async workflow support with callbacks
|
|
7415
7499
|
*
|
|
7416
|
-
*
|
|
7417
|
-
* @category SDK
|
|
7500
|
+
* Note: Trigger operations require projectKey which is obtained from SDK config.
|
|
7418
7501
|
*
|
|
7419
|
-
* @
|
|
7420
|
-
*
|
|
7421
|
-
|
|
7422
|
-
|
|
7502
|
+
* @group Services
|
|
7503
|
+
* @category Webhook
|
|
7504
|
+
*/
|
|
7505
|
+
class WebhookService {
|
|
7506
|
+
constructor(webhookApi, projectKey) {
|
|
7507
|
+
this.webhookApi = webhookApi;
|
|
7508
|
+
this.projectKey = projectKey;
|
|
7509
|
+
}
|
|
7510
|
+
// ================================
|
|
7511
|
+
// Admin: Webhook Configuration
|
|
7512
|
+
// ================================
|
|
7513
|
+
/**
|
|
7514
|
+
* Get all webhooks with optional filters
|
|
7515
|
+
*/
|
|
7516
|
+
async getWebhooks(options) {
|
|
7517
|
+
return this.webhookApi.listWebhooks(options);
|
|
7518
|
+
}
|
|
7519
|
+
/**
|
|
7520
|
+
* Get active webhooks only
|
|
7521
|
+
*/
|
|
7522
|
+
async getActiveWebhooks() {
|
|
7523
|
+
return this.webhookApi.listWebhooks({ active: true });
|
|
7524
|
+
}
|
|
7525
|
+
/**
|
|
7526
|
+
* Get webhook by ID
|
|
7527
|
+
*/
|
|
7528
|
+
async getWebhookById(webhookId) {
|
|
7529
|
+
return this.webhookApi.getWebhookById(webhookId);
|
|
7530
|
+
}
|
|
7531
|
+
/**
|
|
7532
|
+
* Create a new webhook
|
|
7533
|
+
*/
|
|
7534
|
+
async createWebhook(webhook) {
|
|
7535
|
+
// Validate required fields
|
|
7536
|
+
if (!webhook.name?.trim()) {
|
|
7537
|
+
throw new Error('Webhook name is required');
|
|
7538
|
+
}
|
|
7539
|
+
if (!webhook.targetUrl?.trim()) {
|
|
7540
|
+
throw new Error('Target URL is required');
|
|
7541
|
+
}
|
|
7542
|
+
// Validate URL format
|
|
7543
|
+
try {
|
|
7544
|
+
new URL(webhook.targetUrl);
|
|
7545
|
+
}
|
|
7546
|
+
catch {
|
|
7547
|
+
throw new Error('Invalid target URL format');
|
|
7548
|
+
}
|
|
7549
|
+
return this.webhookApi.createWebhook(webhook);
|
|
7550
|
+
}
|
|
7551
|
+
/**
|
|
7552
|
+
* Update a webhook
|
|
7553
|
+
*/
|
|
7554
|
+
async updateWebhook(webhookId, webhook) {
|
|
7555
|
+
// Validate URL if provided (cast to access optional property from PartialType)
|
|
7556
|
+
const update = webhook;
|
|
7557
|
+
if (update.targetUrl) {
|
|
7558
|
+
try {
|
|
7559
|
+
new URL(update.targetUrl);
|
|
7560
|
+
}
|
|
7561
|
+
catch {
|
|
7562
|
+
throw new Error('Invalid target URL format');
|
|
7563
|
+
}
|
|
7564
|
+
}
|
|
7565
|
+
return this.webhookApi.updateWebhook(webhookId, webhook);
|
|
7566
|
+
}
|
|
7567
|
+
/**
|
|
7568
|
+
* Enable a webhook
|
|
7569
|
+
*/
|
|
7570
|
+
async enableWebhook(webhookId) {
|
|
7571
|
+
return this.webhookApi.updateWebhook(webhookId, { isActive: true });
|
|
7572
|
+
}
|
|
7573
|
+
/**
|
|
7574
|
+
* Disable a webhook
|
|
7575
|
+
*/
|
|
7576
|
+
async disableWebhook(webhookId) {
|
|
7577
|
+
return this.webhookApi.updateWebhook(webhookId, { isActive: false });
|
|
7578
|
+
}
|
|
7579
|
+
/**
|
|
7580
|
+
* Delete a webhook
|
|
7581
|
+
*/
|
|
7582
|
+
async deleteWebhook(webhookId) {
|
|
7583
|
+
return this.webhookApi.deleteWebhook(webhookId);
|
|
7584
|
+
}
|
|
7585
|
+
// ================================
|
|
7586
|
+
// Webhook Triggering (via Proxy)
|
|
7587
|
+
// Uses /hooks/:projectKey/:hookId
|
|
7588
|
+
// ================================
|
|
7589
|
+
/**
|
|
7590
|
+
* Trigger a webhook with full control over request parameters
|
|
7591
|
+
*/
|
|
7592
|
+
async triggerWebhook(request) {
|
|
7593
|
+
return this.webhookApi.triggerWebhook(request, this.projectKey);
|
|
7594
|
+
}
|
|
7595
|
+
/**
|
|
7596
|
+
* Simple POST trigger with JSON body
|
|
7597
|
+
*/
|
|
7598
|
+
async post(hookId, body) {
|
|
7599
|
+
return this.webhookApi.triggerWebhook({
|
|
7600
|
+
hookId,
|
|
7601
|
+
method: WebhookMethod.POST,
|
|
7602
|
+
body
|
|
7603
|
+
}, this.projectKey);
|
|
7604
|
+
}
|
|
7605
|
+
/**
|
|
7606
|
+
* Simple GET trigger
|
|
7607
|
+
*/
|
|
7608
|
+
async get(hookId, queryParams) {
|
|
7609
|
+
return this.webhookApi.triggerWebhook({
|
|
7610
|
+
hookId,
|
|
7611
|
+
method: WebhookMethod.GET,
|
|
7612
|
+
queryParams
|
|
7613
|
+
}, this.projectKey);
|
|
7614
|
+
}
|
|
7615
|
+
/**
|
|
7616
|
+
* Trigger and wait for async callback (for workflow systems like n8n)
|
|
7617
|
+
*
|
|
7618
|
+
* @param hookId - Webhook ID to trigger
|
|
7619
|
+
* @param body - Request body
|
|
7620
|
+
* @param timeoutMs - How long to wait for callback (default: 30000ms)
|
|
7621
|
+
*/
|
|
7622
|
+
async triggerAndWait(hookId, body, timeoutMs = 30000) {
|
|
7623
|
+
return this.webhookApi.triggerWebhook({
|
|
7624
|
+
hookId,
|
|
7625
|
+
method: WebhookMethod.POST,
|
|
7626
|
+
body,
|
|
7627
|
+
waitForCallback: true,
|
|
7628
|
+
callbackTimeoutMs: timeoutMs
|
|
7629
|
+
}, this.projectKey);
|
|
7630
|
+
}
|
|
7631
|
+
// ================================
|
|
7632
|
+
// Execution History (Admin)
|
|
7633
|
+
// ================================
|
|
7634
|
+
/**
|
|
7635
|
+
* Get execution history with filters
|
|
7636
|
+
*/
|
|
7637
|
+
async getExecutions(options) {
|
|
7638
|
+
return this.webhookApi.listExecutions(options);
|
|
7639
|
+
}
|
|
7640
|
+
/**
|
|
7641
|
+
* Get executions for a specific webhook
|
|
7642
|
+
*/
|
|
7643
|
+
async getWebhookExecutions(webhookId, options) {
|
|
7644
|
+
return this.webhookApi.listExecutions({ ...options, webhookId });
|
|
7645
|
+
}
|
|
7646
|
+
/**
|
|
7647
|
+
* Get failed executions
|
|
7648
|
+
*/
|
|
7649
|
+
async getFailedExecutions(options) {
|
|
7650
|
+
return this.webhookApi.listExecutions({ ...options, status: WebhookExecutionStatus.FAILED });
|
|
7651
|
+
}
|
|
7652
|
+
/**
|
|
7653
|
+
* Get execution details by ID
|
|
7654
|
+
*/
|
|
7655
|
+
async getExecutionById(executionId) {
|
|
7656
|
+
return this.webhookApi.getExecutionById(executionId);
|
|
7657
|
+
}
|
|
7658
|
+
// ================================
|
|
7659
|
+
// Callback (for testing/manual triggers)
|
|
7660
|
+
// ================================
|
|
7661
|
+
/**
|
|
7662
|
+
* Send a callback result for an execution
|
|
7663
|
+
* Normally this is called by external systems like n8n
|
|
7664
|
+
*/
|
|
7665
|
+
async sendCallback(executionId, callback) {
|
|
7666
|
+
return this.webhookApi.sendCallback(executionId, callback, this.projectKey);
|
|
7667
|
+
}
|
|
7668
|
+
}
|
|
7669
|
+
|
|
7670
|
+
/**
|
|
7671
|
+
* Webhook Manager - Clean, high-level interface for webhook operations
|
|
7423
7672
|
*
|
|
7424
|
-
*
|
|
7425
|
-
*
|
|
7426
|
-
*
|
|
7427
|
-
*
|
|
7428
|
-
*
|
|
7673
|
+
* Webhooks enable integration with external systems (n8n, Zapier, custom backends):
|
|
7674
|
+
* - **Admin operations**: Create, configure, and manage webhook endpoints
|
|
7675
|
+
* - **Trigger operations**: Programmatically trigger webhooks with payloads
|
|
7676
|
+
* - **Async workflows**: Wait for callbacks from workflow systems
|
|
7677
|
+
* - **Monitoring**: Track execution history
|
|
7429
7678
|
*
|
|
7430
|
-
*
|
|
7431
|
-
*
|
|
7432
|
-
*
|
|
7433
|
-
*
|
|
7679
|
+
* ## Security Model
|
|
7680
|
+
* - Admin endpoints require tenant authentication
|
|
7681
|
+
* - Trigger endpoints work with user/business/tenant auth
|
|
7682
|
+
* - Caller identity is passed to webhook target
|
|
7434
7683
|
*
|
|
7435
|
-
*
|
|
7436
|
-
*
|
|
7437
|
-
* const user = await sdk.auth.getCurrentUser();
|
|
7438
|
-
* console.log('Welcome,', user.name);
|
|
7439
|
-
* }
|
|
7440
|
-
* ```
|
|
7684
|
+
* @group Managers
|
|
7685
|
+
* @category Webhook Management
|
|
7441
7686
|
*
|
|
7442
|
-
* @example
|
|
7687
|
+
* @example Basic Webhook Operations
|
|
7443
7688
|
* ```typescript
|
|
7444
|
-
* //
|
|
7445
|
-
* const
|
|
7689
|
+
* // Admin: Create a webhook for order processing
|
|
7690
|
+
* const webhook = await sdk.webhooks.create({
|
|
7691
|
+
* name: 'Process Order',
|
|
7692
|
+
* targetUrl: 'https://n8n.example.com/webhook/orders',
|
|
7693
|
+
* method: 'POST',
|
|
7694
|
+
* headers: { 'X-Custom-Header': 'value' }
|
|
7695
|
+
* });
|
|
7446
7696
|
*
|
|
7447
|
-
* //
|
|
7448
|
-
* const
|
|
7697
|
+
* // Trigger webhook with order data
|
|
7698
|
+
* const result = await sdk.webhooks.trigger(webhook.id, {
|
|
7699
|
+
* orderId: 'order-123',
|
|
7700
|
+
* items: [{ productId: 'prod-1', quantity: 2 }]
|
|
7701
|
+
* });
|
|
7702
|
+
*
|
|
7703
|
+
* console.log('Execution ID:', result.executionId);
|
|
7449
7704
|
* ```
|
|
7450
7705
|
*
|
|
7451
|
-
* @example
|
|
7706
|
+
* @example Async Workflow with Callback
|
|
7452
7707
|
* ```typescript
|
|
7453
|
-
* //
|
|
7454
|
-
* const
|
|
7708
|
+
* // Trigger and wait for workflow completion (up to 30s)
|
|
7709
|
+
* const result = await sdk.webhooks.triggerAndWait(
|
|
7710
|
+
* 'ai-processing-webhook',
|
|
7711
|
+
* { prompt: 'Generate report for Q1' },
|
|
7712
|
+
* 30000
|
|
7713
|
+
* );
|
|
7455
7714
|
*
|
|
7456
|
-
*
|
|
7457
|
-
*
|
|
7715
|
+
* if (result.status === 'COMPLETED') {
|
|
7716
|
+
* console.log('AI Response:', result.body);
|
|
7717
|
+
* }
|
|
7458
7718
|
* ```
|
|
7459
|
-
*
|
|
7460
|
-
* @since 1.3.0 - Manager pattern architecture
|
|
7461
7719
|
*/
|
|
7462
|
-
class
|
|
7720
|
+
class WebhookManager {
|
|
7721
|
+
constructor(apiClient, projectKey, events) {
|
|
7722
|
+
this.apiClient = apiClient;
|
|
7723
|
+
this.projectKey = projectKey;
|
|
7724
|
+
this.events = events;
|
|
7725
|
+
const webhookApi = new WebhookApi(apiClient);
|
|
7726
|
+
this.webhookService = new WebhookService(webhookApi, projectKey);
|
|
7727
|
+
}
|
|
7728
|
+
// ================================
|
|
7729
|
+
// Admin: Webhook Configuration
|
|
7730
|
+
// ================================
|
|
7463
7731
|
/**
|
|
7464
|
-
*
|
|
7465
|
-
*
|
|
7466
|
-
* Initializes all domain managers and sets up the API client with the provided
|
|
7467
|
-
* HTTP client adapter and configuration.
|
|
7732
|
+
* Get all webhooks with optional filters (Admin)
|
|
7468
7733
|
*
|
|
7469
|
-
* @param
|
|
7470
|
-
* @
|
|
7734
|
+
* @param options - Filter and pagination options
|
|
7735
|
+
* @returns Promise resolving to paginated webhooks
|
|
7471
7736
|
*
|
|
7472
|
-
* @example
|
|
7737
|
+
* @example
|
|
7473
7738
|
* ```typescript
|
|
7474
|
-
*
|
|
7739
|
+
* // Get all webhooks
|
|
7740
|
+
* const webhooks = await sdk.webhooks.getAll();
|
|
7475
7741
|
*
|
|
7476
|
-
*
|
|
7477
|
-
*
|
|
7478
|
-
*
|
|
7479
|
-
*
|
|
7742
|
+
* // Get only active webhooks
|
|
7743
|
+
* const active = await sdk.webhooks.getAll({ active: true });
|
|
7744
|
+
*
|
|
7745
|
+
* // Search by name
|
|
7746
|
+
* const found = await sdk.webhooks.getAll({ search: 'order' });
|
|
7480
7747
|
* ```
|
|
7748
|
+
*/
|
|
7749
|
+
async getAll(options) {
|
|
7750
|
+
return this.webhookService.getWebhooks(options);
|
|
7751
|
+
}
|
|
7752
|
+
/**
|
|
7753
|
+
* Get active webhooks only (Admin)
|
|
7754
|
+
*/
|
|
7755
|
+
async getActive() {
|
|
7756
|
+
return this.webhookService.getActiveWebhooks();
|
|
7757
|
+
}
|
|
7758
|
+
/**
|
|
7759
|
+
* Get webhook by ID (Admin)
|
|
7481
7760
|
*
|
|
7482
|
-
* @
|
|
7483
|
-
*
|
|
7484
|
-
|
|
7761
|
+
* @param webhookId - UUID of the webhook
|
|
7762
|
+
* @returns Promise resolving to webhook details
|
|
7763
|
+
*/
|
|
7764
|
+
async getById(webhookId) {
|
|
7765
|
+
return this.webhookService.getWebhookById(webhookId);
|
|
7766
|
+
}
|
|
7767
|
+
/**
|
|
7768
|
+
* Create a new webhook (Admin)
|
|
7485
7769
|
*
|
|
7486
|
-
*
|
|
7487
|
-
*
|
|
7488
|
-
*
|
|
7489
|
-
*
|
|
7770
|
+
* @param webhook - Webhook configuration
|
|
7771
|
+
* @returns Promise resolving to created webhook
|
|
7772
|
+
*
|
|
7773
|
+
* @example
|
|
7774
|
+
* ```typescript
|
|
7775
|
+
* const webhook = await sdk.webhooks.create({
|
|
7776
|
+
* name: 'Order Notifications',
|
|
7777
|
+
* targetUrl: 'https://api.example.com/webhooks/orders',
|
|
7778
|
+
* method: 'POST',
|
|
7779
|
+
* headers: {
|
|
7780
|
+
* 'Authorization': 'Bearer secret-token'
|
|
7781
|
+
* },
|
|
7782
|
+
* rateLimitPerMinute: 60
|
|
7490
7783
|
* });
|
|
7491
7784
|
* ```
|
|
7785
|
+
*/
|
|
7786
|
+
async create(webhook) {
|
|
7787
|
+
const result = await this.webhookService.createWebhook(webhook);
|
|
7788
|
+
this.events?.emitSuccess({
|
|
7789
|
+
domain: 'webhook',
|
|
7790
|
+
type: 'WEBHOOK_CREATED',
|
|
7791
|
+
userMessage: 'Webhook created successfully',
|
|
7792
|
+
details: { webhookId: result.id, name: result.name }
|
|
7793
|
+
});
|
|
7794
|
+
return result;
|
|
7795
|
+
}
|
|
7796
|
+
/**
|
|
7797
|
+
* Update a webhook (Admin)
|
|
7492
7798
|
*
|
|
7493
|
-
* @
|
|
7799
|
+
* @param webhookId - UUID of the webhook to update
|
|
7800
|
+
* @param webhook - Updated configuration (partial)
|
|
7801
|
+
* @returns Promise resolving to updated webhook
|
|
7802
|
+
*/
|
|
7803
|
+
async update(webhookId, webhook) {
|
|
7804
|
+
const result = await this.webhookService.updateWebhook(webhookId, webhook);
|
|
7805
|
+
this.events?.emitSuccess({
|
|
7806
|
+
domain: 'webhook',
|
|
7807
|
+
type: 'WEBHOOK_UPDATED',
|
|
7808
|
+
userMessage: 'Webhook updated successfully',
|
|
7809
|
+
details: { webhookId }
|
|
7810
|
+
});
|
|
7811
|
+
return result;
|
|
7812
|
+
}
|
|
7813
|
+
/**
|
|
7814
|
+
* Enable a webhook (Admin)
|
|
7815
|
+
*/
|
|
7816
|
+
async enable(webhookId) {
|
|
7817
|
+
return this.webhookService.enableWebhook(webhookId);
|
|
7818
|
+
}
|
|
7819
|
+
/**
|
|
7820
|
+
* Disable a webhook (Admin)
|
|
7821
|
+
*/
|
|
7822
|
+
async disable(webhookId) {
|
|
7823
|
+
return this.webhookService.disableWebhook(webhookId);
|
|
7824
|
+
}
|
|
7825
|
+
/**
|
|
7826
|
+
* Delete a webhook (Admin)
|
|
7827
|
+
*
|
|
7828
|
+
* @param webhookId - UUID of the webhook to delete
|
|
7829
|
+
* @returns Promise resolving to deletion confirmation
|
|
7830
|
+
*/
|
|
7831
|
+
async delete(webhookId) {
|
|
7832
|
+
const result = await this.webhookService.deleteWebhook(webhookId);
|
|
7833
|
+
this.events?.emitSuccess({
|
|
7834
|
+
domain: 'webhook',
|
|
7835
|
+
type: 'WEBHOOK_DELETED',
|
|
7836
|
+
userMessage: 'Webhook deleted successfully',
|
|
7837
|
+
details: { webhookId }
|
|
7838
|
+
});
|
|
7839
|
+
return result;
|
|
7840
|
+
}
|
|
7841
|
+
// ================================
|
|
7842
|
+
// Webhook Triggering
|
|
7843
|
+
// ================================
|
|
7844
|
+
/**
|
|
7845
|
+
* Trigger a webhook with GET method
|
|
7846
|
+
*
|
|
7847
|
+
* Sends a GET request through the webhook proxy. Returns the full response
|
|
7848
|
+
* including execution metadata and raw data from the target.
|
|
7849
|
+
*
|
|
7850
|
+
* @param hookId - Webhook ID to trigger
|
|
7851
|
+
* @param queryParams - Optional query parameters
|
|
7852
|
+
* @returns Promise resolving to trigger response with metadata and data
|
|
7853
|
+
*
|
|
7854
|
+
* @example
|
|
7855
|
+
* ```typescript
|
|
7856
|
+
* // Fetch data from external API via webhook proxy
|
|
7857
|
+
* const result = await sdk.webhooks.get('user-directory-webhook');
|
|
7858
|
+
* console.log('Success:', result.success);
|
|
7859
|
+
* console.log('Data:', result.data); // Raw data from target
|
|
7860
|
+
* ```
|
|
7861
|
+
*/
|
|
7862
|
+
async get(hookId, queryParams) {
|
|
7863
|
+
return this.webhookService.get(hookId, queryParams);
|
|
7864
|
+
}
|
|
7865
|
+
/**
|
|
7866
|
+
* Trigger a webhook with POST method
|
|
7867
|
+
*
|
|
7868
|
+
* Sends data to the configured webhook target. The caller's identity
|
|
7869
|
+
* (user/business/tenant) is included in the request context.
|
|
7870
|
+
*
|
|
7871
|
+
* @param hookId - Webhook ID to trigger
|
|
7872
|
+
* @param body - Payload to send
|
|
7873
|
+
* @returns Promise resolving to trigger response with execution metadata
|
|
7874
|
+
*
|
|
7875
|
+
* @example
|
|
7876
|
+
* ```typescript
|
|
7877
|
+
* const result = await sdk.webhooks.trigger('order-webhook', {
|
|
7878
|
+
* orderId: 'order-123',
|
|
7879
|
+
* action: 'created',
|
|
7880
|
+
* items: [...]
|
|
7881
|
+
* });
|
|
7882
|
+
*
|
|
7883
|
+
* console.log('Success:', result.success);
|
|
7884
|
+
* console.log('Execution ID:', result.executionId);
|
|
7885
|
+
* console.log('Response:', result.data);
|
|
7886
|
+
* ```
|
|
7887
|
+
*/
|
|
7888
|
+
async trigger(hookId, body) {
|
|
7889
|
+
const result = await this.webhookService.post(hookId, body);
|
|
7890
|
+
this.events?.emitSuccess({
|
|
7891
|
+
domain: 'webhook',
|
|
7892
|
+
type: 'WEBHOOK_TRIGGERED',
|
|
7893
|
+
userMessage: 'Webhook triggered',
|
|
7894
|
+
details: { hookId, executionId: result.executionId }
|
|
7895
|
+
});
|
|
7896
|
+
return result;
|
|
7897
|
+
}
|
|
7898
|
+
/**
|
|
7899
|
+
* Trigger a webhook and wait for async callback
|
|
7900
|
+
*
|
|
7901
|
+
* Use this for workflow systems (n8n, Zapier) that need time to process
|
|
7902
|
+
* and return results via callback. The SDK will wait for the callback
|
|
7903
|
+
* up to the specified timeout.
|
|
7904
|
+
*
|
|
7905
|
+
* @param hookId - Webhook ID to trigger
|
|
7906
|
+
* @param body - Payload to send
|
|
7907
|
+
* @param timeoutMs - Max time to wait for callback (default: 30s)
|
|
7908
|
+
* @returns Promise resolving when callback received or timeout
|
|
7909
|
+
*
|
|
7910
|
+
* @example
|
|
7911
|
+
* ```typescript
|
|
7912
|
+
* // Trigger AI workflow and wait for result
|
|
7913
|
+
* const result = await sdk.webhooks.triggerAndWait(
|
|
7914
|
+
* 'ai-analysis-webhook',
|
|
7915
|
+
* { documentId: 'doc-123', analysisType: 'sentiment' },
|
|
7916
|
+
* 60000 // Wait up to 60 seconds
|
|
7917
|
+
* );
|
|
7918
|
+
*
|
|
7919
|
+
* if (result.success) {
|
|
7920
|
+
* console.log('Analysis:', result.data);
|
|
7921
|
+
* }
|
|
7922
|
+
* ```
|
|
7923
|
+
*/
|
|
7924
|
+
async triggerAndWait(hookId, body, timeoutMs = 30000) {
|
|
7925
|
+
return this.webhookService.triggerAndWait(hookId, body, timeoutMs);
|
|
7926
|
+
}
|
|
7927
|
+
// ================================
|
|
7928
|
+
// Execution History & Monitoring
|
|
7929
|
+
// ================================
|
|
7930
|
+
/**
|
|
7931
|
+
* Get webhook execution history (Admin)
|
|
7932
|
+
*
|
|
7933
|
+
* @param options - Filter and pagination options
|
|
7934
|
+
* @returns Promise resolving to paginated executions
|
|
7935
|
+
*
|
|
7936
|
+
* @example
|
|
7937
|
+
* ```typescript
|
|
7938
|
+
* // Get recent failed executions
|
|
7939
|
+
* const failed = await sdk.webhooks.getExecutions({ status: 'FAILED' });
|
|
7940
|
+
*
|
|
7941
|
+
* // Get executions for specific webhook
|
|
7942
|
+
* const executions = await sdk.webhooks.getExecutions({
|
|
7943
|
+
* webhookId: 'webhook-123',
|
|
7944
|
+
* fromDate: '2024-01-01'
|
|
7945
|
+
* });
|
|
7946
|
+
* ```
|
|
7947
|
+
*/
|
|
7948
|
+
async getExecutions(options) {
|
|
7949
|
+
return this.webhookService.getExecutions(options);
|
|
7950
|
+
}
|
|
7951
|
+
/**
|
|
7952
|
+
* Get execution details by ID (Admin)
|
|
7953
|
+
*/
|
|
7954
|
+
async getExecutionById(executionId) {
|
|
7955
|
+
return this.webhookService.getExecutionById(executionId);
|
|
7956
|
+
}
|
|
7957
|
+
/**
|
|
7958
|
+
* Get the full webhook service for advanced operations
|
|
7959
|
+
*
|
|
7960
|
+
* @returns WebhookService instance with full API access
|
|
7961
|
+
*/
|
|
7962
|
+
getWebhookService() {
|
|
7963
|
+
return this.webhookService;
|
|
7964
|
+
}
|
|
7965
|
+
}
|
|
7966
|
+
|
|
7967
|
+
/**
|
|
7968
|
+
* PERS Events Client
|
|
7969
|
+
*
|
|
7970
|
+
* Lightweight WebSocket client for real-time blockchain event streaming.
|
|
7971
|
+
* Connects to the PERS WS Relay server to receive events for user's wallets.
|
|
7972
|
+
*
|
|
7973
|
+
* ## v1.2.0 Subscription Model
|
|
7974
|
+
*
|
|
7975
|
+
* JWT is used for authentication only. After connecting, you must explicitly
|
|
7976
|
+
* subscribe to wallets (user SDK) or chains (admin dashboard).
|
|
7977
|
+
*
|
|
7978
|
+
* @example User SDK - Subscribe to specific wallets
|
|
7979
|
+
* ```typescript
|
|
7980
|
+
* const client = new PersEventsClient({
|
|
7981
|
+
* wsUrl: 'wss://events.pers.ninja',
|
|
7982
|
+
* autoReconnect: true
|
|
7983
|
+
* });
|
|
7984
|
+
*
|
|
7985
|
+
* await client.connect(jwtToken);
|
|
7986
|
+
*
|
|
7987
|
+
* // Subscribe to user's wallets
|
|
7988
|
+
* await client.subscribeWallets([
|
|
7989
|
+
* { address: '0x123...', chainId: 39123 }
|
|
7990
|
+
* ]);
|
|
7991
|
+
*
|
|
7992
|
+
* client.on('Transfer', (event) => {
|
|
7993
|
+
* console.log(`Received ${event.data.value} tokens`);
|
|
7994
|
+
* });
|
|
7995
|
+
* ```
|
|
7996
|
+
*
|
|
7997
|
+
* @example Admin Dashboard - Subscribe to all events on chains
|
|
7998
|
+
* ```typescript
|
|
7999
|
+
* await client.connect(adminJwtToken);
|
|
8000
|
+
*
|
|
8001
|
+
* // Subscribe to all events on specific chains
|
|
8002
|
+
* await client.subscribeChains([39123, 137]);
|
|
8003
|
+
*
|
|
8004
|
+
* client.on('*', (event) => {
|
|
8005
|
+
* console.log(`Chain ${event.chainId}: ${event.type}`);
|
|
8006
|
+
* });
|
|
8007
|
+
* ```
|
|
8008
|
+
*/
|
|
8009
|
+
const DEFAULT_CONFIG = {
|
|
8010
|
+
autoReconnect: true,
|
|
8011
|
+
maxReconnectAttempts: 10,
|
|
8012
|
+
reconnectDelay: 1000,
|
|
8013
|
+
connectionTimeout: 30000,
|
|
8014
|
+
debug: false,
|
|
8015
|
+
tokenRefresher: undefined,
|
|
8016
|
+
};
|
|
8017
|
+
class PersEventsClient {
|
|
8018
|
+
constructor(config) {
|
|
8019
|
+
this.ws = null;
|
|
8020
|
+
this.state = 'disconnected';
|
|
8021
|
+
this.reconnectAttempts = 0;
|
|
8022
|
+
this.reconnectTimeout = null;
|
|
8023
|
+
this.token = null;
|
|
8024
|
+
// Event handlers by type
|
|
8025
|
+
this.handlers = new Map();
|
|
8026
|
+
this.stateHandlers = new Set();
|
|
8027
|
+
// Connection info from server
|
|
8028
|
+
this.connectionInfo = null;
|
|
8029
|
+
// Current subscription state
|
|
8030
|
+
this.subscriptionState = { wallets: [], chains: [], activeChains: [] };
|
|
8031
|
+
// Pending subscription promise resolver
|
|
8032
|
+
this.pendingSubscription = null;
|
|
8033
|
+
// Subscriptions to restore on reconnect
|
|
8034
|
+
this.savedSubscriptions = { wallets: [], chains: [] };
|
|
8035
|
+
this.config = { ...DEFAULT_CONFIG, ...config };
|
|
8036
|
+
}
|
|
8037
|
+
/**
|
|
8038
|
+
* Connect to the WS relay server
|
|
8039
|
+
* @param token - JWT token for authentication (wallets no longer required in JWT)
|
|
8040
|
+
*/
|
|
8041
|
+
async connect(token) {
|
|
8042
|
+
if (this.state === 'connected' || this.state === 'connecting') {
|
|
8043
|
+
this.log('Already connected or connecting');
|
|
8044
|
+
return;
|
|
8045
|
+
}
|
|
8046
|
+
this.token = token;
|
|
8047
|
+
this.setState('connecting');
|
|
8048
|
+
return new Promise((resolve, reject) => {
|
|
8049
|
+
// Connection timeout
|
|
8050
|
+
const connectionTimeout = setTimeout(() => {
|
|
8051
|
+
this.log('Connection timeout');
|
|
8052
|
+
this.cleanup();
|
|
8053
|
+
this.setState('error');
|
|
8054
|
+
reject(new Error(`Connection timeout after ${this.config.connectionTimeout}ms`));
|
|
8055
|
+
}, this.config.connectionTimeout);
|
|
8056
|
+
const clearTimeoutAndResolve = () => {
|
|
8057
|
+
clearTimeout(connectionTimeout);
|
|
8058
|
+
resolve();
|
|
8059
|
+
};
|
|
8060
|
+
const clearTimeoutAndReject = (err) => {
|
|
8061
|
+
clearTimeout(connectionTimeout);
|
|
8062
|
+
reject(err);
|
|
8063
|
+
};
|
|
8064
|
+
try {
|
|
8065
|
+
const url = new URL(this.config.wsUrl);
|
|
8066
|
+
url.searchParams.set('token', token);
|
|
8067
|
+
this.ws = new WebSocket(url.toString());
|
|
8068
|
+
this.ws.onopen = () => {
|
|
8069
|
+
// Wait for server 'connected' message
|
|
8070
|
+
};
|
|
8071
|
+
this.ws.onmessage = (event) => {
|
|
8072
|
+
this.handleMessage(event.data, clearTimeoutAndResolve);
|
|
8073
|
+
};
|
|
8074
|
+
this.ws.onerror = (error) => {
|
|
8075
|
+
this.log('WebSocket error:', error);
|
|
8076
|
+
this.setState('error');
|
|
8077
|
+
clearTimeoutAndReject(new Error('Connection failed'));
|
|
8078
|
+
};
|
|
8079
|
+
this.ws.onclose = (event) => {
|
|
8080
|
+
this.log(`WebSocket closed: ${event.code} ${event.reason}`);
|
|
8081
|
+
// Only reject if we were still connecting
|
|
8082
|
+
if (this.state === 'connecting') {
|
|
8083
|
+
clearTimeoutAndReject(new Error(`Connection closed during handshake: ${event.code} ${event.reason}`));
|
|
8084
|
+
}
|
|
8085
|
+
this.handleDisconnect();
|
|
8086
|
+
};
|
|
8087
|
+
}
|
|
8088
|
+
catch (err) {
|
|
8089
|
+
this.log('Error creating WebSocket:', err);
|
|
8090
|
+
this.setState('error');
|
|
8091
|
+
clearTimeoutAndReject(err instanceof Error ? err : new Error(String(err)));
|
|
8092
|
+
}
|
|
8093
|
+
});
|
|
8094
|
+
}
|
|
8095
|
+
/**
|
|
8096
|
+
* Disconnect from the server
|
|
8097
|
+
*/
|
|
8098
|
+
disconnect() {
|
|
8099
|
+
this.config.autoReconnect = false; // Prevent reconnect
|
|
8100
|
+
this.cleanup();
|
|
8101
|
+
this.setState('disconnected');
|
|
8102
|
+
}
|
|
8103
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
8104
|
+
// Subscription Methods (v1.2.0)
|
|
8105
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
8106
|
+
/**
|
|
8107
|
+
* Subscribe to wallet events (User SDK)
|
|
8108
|
+
*
|
|
8109
|
+
* Receives events only for the specified wallet addresses.
|
|
8110
|
+
* Can be called multiple times to add more wallets.
|
|
8111
|
+
*
|
|
8112
|
+
* @param wallets - Array of wallet info (address + chainId)
|
|
8113
|
+
* @returns Promise that resolves when subscription is confirmed
|
|
8114
|
+
*/
|
|
8115
|
+
async subscribeWallets(wallets) {
|
|
8116
|
+
return this.sendSubscription({ type: 'subscribe', wallets });
|
|
8117
|
+
}
|
|
8118
|
+
/**
|
|
8119
|
+
* Subscribe to chain events (Admin Dashboard)
|
|
8120
|
+
*
|
|
8121
|
+
* Receives ALL events on the specified chains.
|
|
8122
|
+
* Use for admin dashboards that need to monitor all activity.
|
|
8123
|
+
*
|
|
8124
|
+
* @param chains - Array of chain IDs to subscribe to
|
|
8125
|
+
* @returns Promise that resolves when subscription is confirmed
|
|
8126
|
+
*/
|
|
8127
|
+
async subscribeChains(chains) {
|
|
8128
|
+
return this.sendSubscription({ type: 'subscribe', chains });
|
|
8129
|
+
}
|
|
8130
|
+
/**
|
|
8131
|
+
* Unsubscribe from wallet events
|
|
8132
|
+
*
|
|
8133
|
+
* @param wallets - Array of wallet info to unsubscribe from
|
|
8134
|
+
* @returns Promise that resolves when unsubscription is confirmed
|
|
8135
|
+
*/
|
|
8136
|
+
async unsubscribeWallets(wallets) {
|
|
8137
|
+
return this.sendSubscription({ type: 'unsubscribe', wallets });
|
|
8138
|
+
}
|
|
8139
|
+
/**
|
|
8140
|
+
* Unsubscribe from chain events
|
|
8141
|
+
*
|
|
8142
|
+
* @param chains - Array of chain IDs to unsubscribe from
|
|
8143
|
+
* @returns Promise that resolves when unsubscription is confirmed
|
|
8144
|
+
*/
|
|
8145
|
+
async unsubscribeChains(chains) {
|
|
8146
|
+
return this.sendSubscription({ type: 'unsubscribe', chains });
|
|
8147
|
+
}
|
|
8148
|
+
/**
|
|
8149
|
+
* Get current subscription state
|
|
8150
|
+
*/
|
|
8151
|
+
getSubscriptionState() {
|
|
8152
|
+
return { ...this.subscriptionState };
|
|
8153
|
+
}
|
|
8154
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
8155
|
+
// Event Handlers
|
|
8156
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
8157
|
+
/**
|
|
8158
|
+
* Subscribe to blockchain events
|
|
8159
|
+
* @param eventType - Event type to listen for, or '*' for all events
|
|
8160
|
+
* @param handler - Event handler function
|
|
8161
|
+
* @returns Unsubscribe function
|
|
8162
|
+
*/
|
|
8163
|
+
on(eventType, handler) {
|
|
8164
|
+
if (!this.handlers.has(eventType)) {
|
|
8165
|
+
this.handlers.set(eventType, new Set());
|
|
8166
|
+
}
|
|
8167
|
+
this.handlers.get(eventType).add(handler);
|
|
8168
|
+
return () => {
|
|
8169
|
+
this.handlers.get(eventType)?.delete(handler);
|
|
8170
|
+
};
|
|
8171
|
+
}
|
|
8172
|
+
/**
|
|
8173
|
+
* Subscribe to connection state changes
|
|
8174
|
+
*/
|
|
8175
|
+
onStateChange(handler) {
|
|
8176
|
+
this.stateHandlers.add(handler);
|
|
8177
|
+
return () => this.stateHandlers.delete(handler);
|
|
8178
|
+
}
|
|
8179
|
+
/**
|
|
8180
|
+
* Get current connection state
|
|
8181
|
+
*/
|
|
8182
|
+
getState() {
|
|
8183
|
+
return this.state;
|
|
8184
|
+
}
|
|
8185
|
+
/**
|
|
8186
|
+
* Get connection info (userId, initial wallets, activeChains)
|
|
8187
|
+
*/
|
|
8188
|
+
getConnectionInfo() {
|
|
8189
|
+
return this.connectionInfo;
|
|
8190
|
+
}
|
|
8191
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
8192
|
+
// Private methods
|
|
8193
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
8194
|
+
async sendSubscription(message) {
|
|
8195
|
+
if (this.state !== 'connected' || !this.ws) {
|
|
8196
|
+
throw new Error('Not connected to server');
|
|
8197
|
+
}
|
|
8198
|
+
// Save subscriptions for reconnect
|
|
8199
|
+
if (message.type === 'subscribe') {
|
|
8200
|
+
if (message.wallets) {
|
|
8201
|
+
this.savedSubscriptions.wallets = [
|
|
8202
|
+
...this.savedSubscriptions.wallets,
|
|
8203
|
+
...message.wallets.filter(w => !this.savedSubscriptions.wallets.some(sw => sw.address === w.address && sw.chainId === w.chainId))
|
|
8204
|
+
];
|
|
8205
|
+
}
|
|
8206
|
+
if (message.chains) {
|
|
8207
|
+
this.savedSubscriptions.chains = [
|
|
8208
|
+
...new Set([...this.savedSubscriptions.chains, ...message.chains])
|
|
8209
|
+
];
|
|
8210
|
+
}
|
|
8211
|
+
}
|
|
8212
|
+
else if (message.type === 'unsubscribe') {
|
|
8213
|
+
if (message.wallets) {
|
|
8214
|
+
this.savedSubscriptions.wallets = this.savedSubscriptions.wallets.filter(sw => !message.wallets.some(w => w.address === sw.address && w.chainId === sw.chainId));
|
|
8215
|
+
}
|
|
8216
|
+
if (message.chains) {
|
|
8217
|
+
this.savedSubscriptions.chains = this.savedSubscriptions.chains.filter(c => !message.chains.includes(c));
|
|
8218
|
+
}
|
|
8219
|
+
}
|
|
8220
|
+
return new Promise((resolve, reject) => {
|
|
8221
|
+
// Set up timeout
|
|
8222
|
+
const timeout = setTimeout(() => {
|
|
8223
|
+
this.pendingSubscription = null;
|
|
8224
|
+
reject(new Error('Subscription timeout'));
|
|
8225
|
+
}, 10000);
|
|
8226
|
+
this.pendingSubscription = {
|
|
8227
|
+
resolve: () => {
|
|
8228
|
+
clearTimeout(timeout);
|
|
8229
|
+
this.pendingSubscription = null;
|
|
8230
|
+
resolve(this.subscriptionState);
|
|
8231
|
+
},
|
|
8232
|
+
reject: (err) => {
|
|
8233
|
+
clearTimeout(timeout);
|
|
8234
|
+
this.pendingSubscription = null;
|
|
8235
|
+
reject(err);
|
|
8236
|
+
}
|
|
8237
|
+
};
|
|
8238
|
+
this.ws.send(JSON.stringify(message));
|
|
8239
|
+
this.log('Sent subscription message:', message);
|
|
8240
|
+
});
|
|
8241
|
+
}
|
|
8242
|
+
handleMessage(data, onConnected) {
|
|
8243
|
+
try {
|
|
8244
|
+
const message = JSON.parse(data);
|
|
8245
|
+
switch (message.type) {
|
|
8246
|
+
case 'connected':
|
|
8247
|
+
this.connectionInfo = message.payload;
|
|
8248
|
+
this.reconnectAttempts = 0;
|
|
8249
|
+
this.setState('connected');
|
|
8250
|
+
this.log('Connected:', message.payload);
|
|
8251
|
+
onConnected?.();
|
|
8252
|
+
// Restore subscriptions on reconnect
|
|
8253
|
+
this.restoreSubscriptions();
|
|
8254
|
+
break;
|
|
8255
|
+
case 'subscribed':
|
|
8256
|
+
this.subscriptionState = message.payload;
|
|
8257
|
+
this.log('Subscription confirmed:', message.payload);
|
|
8258
|
+
this.pendingSubscription?.resolve();
|
|
8259
|
+
break;
|
|
8260
|
+
case 'unsubscribed':
|
|
8261
|
+
this.subscriptionState = message.payload;
|
|
8262
|
+
this.log('Unsubscription confirmed:', message.payload);
|
|
8263
|
+
this.pendingSubscription?.resolve();
|
|
8264
|
+
break;
|
|
8265
|
+
case 'event':
|
|
8266
|
+
this.routeEvent(message.payload);
|
|
8267
|
+
break;
|
|
8268
|
+
case 'ping':
|
|
8269
|
+
// Respond to server ping with pong
|
|
8270
|
+
this.sendPong();
|
|
8271
|
+
break;
|
|
8272
|
+
case 'error':
|
|
8273
|
+
this.log('Server error:', message.payload);
|
|
8274
|
+
this.pendingSubscription?.reject(new Error(message.payload.message));
|
|
8275
|
+
break;
|
|
8276
|
+
}
|
|
8277
|
+
}
|
|
8278
|
+
catch (err) {
|
|
8279
|
+
this.log('Failed to parse message:', err);
|
|
8280
|
+
}
|
|
8281
|
+
}
|
|
8282
|
+
sendPong() {
|
|
8283
|
+
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
|
|
8284
|
+
this.ws.send(JSON.stringify({ type: 'pong' }));
|
|
8285
|
+
this.log('Sent pong');
|
|
8286
|
+
}
|
|
8287
|
+
}
|
|
8288
|
+
async restoreSubscriptions() {
|
|
8289
|
+
// Restore wallet subscriptions
|
|
8290
|
+
if (this.savedSubscriptions.wallets.length > 0) {
|
|
8291
|
+
this.log('Restoring wallet subscriptions:', this.savedSubscriptions.wallets);
|
|
8292
|
+
try {
|
|
8293
|
+
await this.subscribeWallets(this.savedSubscriptions.wallets);
|
|
8294
|
+
}
|
|
8295
|
+
catch (err) {
|
|
8296
|
+
this.log('Failed to restore wallet subscriptions:', err);
|
|
8297
|
+
}
|
|
8298
|
+
}
|
|
8299
|
+
// Restore chain subscriptions
|
|
8300
|
+
if (this.savedSubscriptions.chains.length > 0) {
|
|
8301
|
+
this.log('Restoring chain subscriptions:', this.savedSubscriptions.chains);
|
|
8302
|
+
try {
|
|
8303
|
+
await this.subscribeChains(this.savedSubscriptions.chains);
|
|
8304
|
+
}
|
|
8305
|
+
catch (err) {
|
|
8306
|
+
this.log('Failed to restore chain subscriptions:', err);
|
|
8307
|
+
}
|
|
8308
|
+
}
|
|
8309
|
+
}
|
|
8310
|
+
routeEvent(event) {
|
|
8311
|
+
this.log('Event received:', event.type, event);
|
|
8312
|
+
// Call specific type handlers
|
|
8313
|
+
const typeHandlers = this.handlers.get(event.type);
|
|
8314
|
+
typeHandlers?.forEach(handler => {
|
|
8315
|
+
try {
|
|
8316
|
+
handler(event);
|
|
8317
|
+
}
|
|
8318
|
+
catch (err) {
|
|
8319
|
+
console.error('[PERS-Events] Handler error:', err);
|
|
8320
|
+
}
|
|
8321
|
+
});
|
|
8322
|
+
// Call wildcard handlers
|
|
8323
|
+
const wildcardHandlers = this.handlers.get('*');
|
|
8324
|
+
wildcardHandlers?.forEach(handler => {
|
|
8325
|
+
try {
|
|
8326
|
+
handler(event);
|
|
8327
|
+
}
|
|
8328
|
+
catch (err) {
|
|
8329
|
+
console.error('[PERS-Events] Handler error:', err);
|
|
8330
|
+
}
|
|
8331
|
+
});
|
|
8332
|
+
}
|
|
8333
|
+
handleDisconnect() {
|
|
8334
|
+
this.cleanup();
|
|
8335
|
+
this.setState('disconnected');
|
|
8336
|
+
if (this.config.autoReconnect && this.token) {
|
|
8337
|
+
if (this.reconnectAttempts < this.config.maxReconnectAttempts) {
|
|
8338
|
+
this.attemptReconnect();
|
|
8339
|
+
}
|
|
8340
|
+
else {
|
|
8341
|
+
this.log(`Max reconnect attempts (${this.config.maxReconnectAttempts}) reached. Call connect() manually to retry.`);
|
|
8342
|
+
// Reset counter so manual connect() will work
|
|
8343
|
+
this.reconnectAttempts = 0;
|
|
8344
|
+
}
|
|
8345
|
+
}
|
|
8346
|
+
}
|
|
8347
|
+
attemptReconnect() {
|
|
8348
|
+
const delay = Math.min(this.config.reconnectDelay * Math.pow(2, this.reconnectAttempts), 30000 // Max 30 second delay
|
|
8349
|
+
);
|
|
8350
|
+
this.reconnectAttempts++;
|
|
8351
|
+
this.log(`🔄 Reconnecting in ${delay}ms (attempt ${this.reconnectAttempts}/${this.config.maxReconnectAttempts})`);
|
|
8352
|
+
this.setState('reconnecting');
|
|
8353
|
+
this.reconnectTimeout = setTimeout(async () => {
|
|
8354
|
+
try {
|
|
8355
|
+
// Try to get fresh token if tokenRefresher is provided
|
|
8356
|
+
let tokenToUse = this.token;
|
|
8357
|
+
if (this.config.tokenRefresher) {
|
|
8358
|
+
try {
|
|
8359
|
+
this.log('🔄 Refreshing token before reconnect...');
|
|
8360
|
+
tokenToUse = await this.config.tokenRefresher();
|
|
8361
|
+
this.token = tokenToUse; // Update stored token
|
|
8362
|
+
}
|
|
8363
|
+
catch (refreshErr) {
|
|
8364
|
+
this.log('⚠️ Token refresh failed, using existing token:', refreshErr);
|
|
8365
|
+
}
|
|
8366
|
+
}
|
|
8367
|
+
if (tokenToUse) {
|
|
8368
|
+
this.log(`🔄 Attempting reconnection...`);
|
|
8369
|
+
await this.connect(tokenToUse);
|
|
8370
|
+
}
|
|
8371
|
+
}
|
|
8372
|
+
catch (err) {
|
|
8373
|
+
this.log('❌ Reconnect failed:', err);
|
|
8374
|
+
}
|
|
8375
|
+
}, delay);
|
|
8376
|
+
}
|
|
8377
|
+
cleanup() {
|
|
8378
|
+
if (this.reconnectTimeout) {
|
|
8379
|
+
clearTimeout(this.reconnectTimeout);
|
|
8380
|
+
this.reconnectTimeout = null;
|
|
8381
|
+
}
|
|
8382
|
+
if (this.ws) {
|
|
8383
|
+
this.ws.onopen = null;
|
|
8384
|
+
this.ws.onmessage = null;
|
|
8385
|
+
this.ws.onerror = null;
|
|
8386
|
+
this.ws.onclose = null;
|
|
8387
|
+
if (this.ws.readyState === WebSocket.OPEN) {
|
|
8388
|
+
this.ws.close();
|
|
8389
|
+
}
|
|
8390
|
+
this.ws = null;
|
|
8391
|
+
}
|
|
8392
|
+
}
|
|
8393
|
+
setState(state) {
|
|
8394
|
+
if (this.state !== state) {
|
|
8395
|
+
this.state = state;
|
|
8396
|
+
this.stateHandlers.forEach(handler => handler(state));
|
|
8397
|
+
}
|
|
8398
|
+
}
|
|
8399
|
+
log(...args) {
|
|
8400
|
+
if (this.config.debug) {
|
|
8401
|
+
console.log('[PERS-Events]', ...args);
|
|
8402
|
+
}
|
|
8403
|
+
}
|
|
8404
|
+
}
|
|
8405
|
+
/**
|
|
8406
|
+
* Create a PERS Events client instance
|
|
8407
|
+
*/
|
|
8408
|
+
function createPersEventsClient(config) {
|
|
8409
|
+
return new PersEventsClient(config);
|
|
8410
|
+
}
|
|
8411
|
+
|
|
8412
|
+
/**
|
|
8413
|
+
* Wallet Events Manager - Real-time blockchain events for user's wallets
|
|
8414
|
+
*
|
|
8415
|
+
* Provides automatic connection management and event routing integrated
|
|
8416
|
+
* with the SDK's authentication flow and event system.
|
|
8417
|
+
*
|
|
8418
|
+
* Events are routed through the SDK's PersEventEmitter, so you can either:
|
|
8419
|
+
* 1. Subscribe via `sdk.walletEvents.on()` for wallet-specific handling
|
|
8420
|
+
* 2. Subscribe via `sdk.events.subscribe()` for unified event stream
|
|
8421
|
+
*
|
|
8422
|
+
* ## v1.2.0 - Subscription Model
|
|
8423
|
+
*
|
|
8424
|
+
* After connecting, you must explicitly subscribe to wallets or chains:
|
|
8425
|
+
* - User SDK: `subscribeWallets([{ address, chainId }])`
|
|
8426
|
+
* - Admin Dashboard: `subscribeChains([chainId1, chainId2])`
|
|
8427
|
+
*
|
|
8428
|
+
* For convenience, use `sdk.connectWalletEvents()` which auto-subscribes
|
|
8429
|
+
* based on auth type.
|
|
8430
|
+
*
|
|
8431
|
+
* @example Manual subscription
|
|
8432
|
+
* ```typescript
|
|
8433
|
+
* await sdk.walletEvents.connect();
|
|
8434
|
+
* await sdk.walletEvents.subscribeWallets([
|
|
8435
|
+
* { address: user.wallets[0].address, chainId: 39123 }
|
|
8436
|
+
* ]);
|
|
8437
|
+
*
|
|
8438
|
+
* sdk.walletEvents.on('Transfer', (event) => {
|
|
8439
|
+
* console.log(`Received ${event.data.value} tokens`);
|
|
8440
|
+
* });
|
|
8441
|
+
* ```
|
|
8442
|
+
*
|
|
8443
|
+
* @example Auto-subscription (recommended)
|
|
8444
|
+
* ```typescript
|
|
8445
|
+
* // Auto-subscribes based on auth type (user/business/admin)
|
|
8446
|
+
* await sdk.connectWalletEvents();
|
|
8447
|
+
* ```
|
|
8448
|
+
*/
|
|
8449
|
+
/**
|
|
8450
|
+
* Wallet Events Manager - Manages real-time blockchain event subscriptions
|
|
8451
|
+
*
|
|
8452
|
+
* Low-level WS client wrapper. For auto-subscription based on auth type,
|
|
8453
|
+
* use `sdk.connectWalletEvents()` which handles subscription automatically.
|
|
8454
|
+
*/
|
|
8455
|
+
class WalletEventsManager {
|
|
8456
|
+
constructor(apiClient, eventEmitter, config) {
|
|
8457
|
+
this.apiClient = apiClient;
|
|
8458
|
+
this.eventEmitter = eventEmitter;
|
|
8459
|
+
this.client = null;
|
|
8460
|
+
this.pendingHandlers = [];
|
|
8461
|
+
this.unsubscribes = [];
|
|
8462
|
+
this.config = {
|
|
8463
|
+
autoReconnect: true,
|
|
8464
|
+
connectionTimeout: 30000,
|
|
8465
|
+
debug: false,
|
|
8466
|
+
...config,
|
|
8467
|
+
};
|
|
8468
|
+
}
|
|
8469
|
+
/**
|
|
8470
|
+
* Connect to real-time wallet events
|
|
8471
|
+
*
|
|
8472
|
+
* Establishes WebSocket connection to the WS relay server.
|
|
8473
|
+
* After connecting, call subscribeWallets() or subscribeChains() to start
|
|
8474
|
+
* receiving events.
|
|
8475
|
+
*
|
|
8476
|
+
* For auto-subscription, use `sdk.connectWalletEvents()` instead.
|
|
8477
|
+
*/
|
|
8478
|
+
async connect() {
|
|
8479
|
+
// Already connected?
|
|
8480
|
+
if (this.client?.getState() === 'connected') {
|
|
8481
|
+
return;
|
|
8482
|
+
}
|
|
8483
|
+
const sdkConfig = this.apiClient.getConfig();
|
|
8484
|
+
const authProvider = sdkConfig.authProvider;
|
|
8485
|
+
if (!authProvider) {
|
|
8486
|
+
throw new Error('Not authenticated. Call sdk.auth.login() first.');
|
|
8487
|
+
}
|
|
8488
|
+
const token = await authProvider.getToken();
|
|
8489
|
+
if (!token) {
|
|
8490
|
+
throw new Error('No authentication token available. Call sdk.auth.login() first.');
|
|
8491
|
+
}
|
|
8492
|
+
// Resolve wsUrl: config override > SDK config > environment default
|
|
8493
|
+
const wsUrl = this.config.wsUrl
|
|
8494
|
+
|| sdkConfig.walletEventsWsUrl
|
|
8495
|
+
|| buildWalletEventsWsUrl(sdkConfig.environment);
|
|
8496
|
+
// Create token refresher that fetches fresh token from auth provider
|
|
8497
|
+
const tokenRefresher = async () => {
|
|
8498
|
+
const freshToken = await authProvider.getToken();
|
|
8499
|
+
if (!freshToken) {
|
|
8500
|
+
throw new Error('Failed to refresh token');
|
|
8501
|
+
}
|
|
8502
|
+
return freshToken;
|
|
8503
|
+
};
|
|
8504
|
+
this.client = new PersEventsClient({
|
|
8505
|
+
wsUrl,
|
|
8506
|
+
autoReconnect: this.config.autoReconnect,
|
|
8507
|
+
connectionTimeout: this.config.connectionTimeout,
|
|
8508
|
+
debug: this.config.debug,
|
|
8509
|
+
tokenRefresher,
|
|
8510
|
+
});
|
|
8511
|
+
await this.client.connect(token);
|
|
8512
|
+
// Clear previous unsubscribes on new connection (prevent memory leak)
|
|
8513
|
+
this.unsubscribes.forEach(unsub => unsub());
|
|
8514
|
+
this.unsubscribes = [];
|
|
8515
|
+
// Route all events through PersEventEmitter for unified stream
|
|
8516
|
+
// Note: Cast needed because web3-types uses string for event.type while pers-shared uses strict union
|
|
8517
|
+
this.client.on('*', (event) => this.emitToPersEvents(event));
|
|
8518
|
+
// Re-attach any handlers registered before connect
|
|
8519
|
+
for (const { type, handler } of this.pendingHandlers) {
|
|
8520
|
+
this.unsubscribes.push(this.client.on(type, handler));
|
|
8521
|
+
}
|
|
8522
|
+
this.pendingHandlers = [];
|
|
8523
|
+
}
|
|
8524
|
+
/**
|
|
8525
|
+
* Disconnect from real-time events
|
|
8526
|
+
*/
|
|
8527
|
+
disconnect() {
|
|
8528
|
+
this.unsubscribes.forEach(unsub => unsub());
|
|
8529
|
+
this.unsubscribes = [];
|
|
8530
|
+
this.client?.disconnect();
|
|
8531
|
+
this.client = null;
|
|
8532
|
+
}
|
|
8533
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
8534
|
+
// Subscription Methods (v1.2.0)
|
|
8535
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
8536
|
+
/**
|
|
8537
|
+
* Subscribe to wallet events
|
|
8538
|
+
*
|
|
8539
|
+
* Receives events only for the specified wallet addresses.
|
|
8540
|
+
* Use this for user-facing SDK where you want to monitor specific wallets.
|
|
8541
|
+
*
|
|
8542
|
+
* @param wallets - Array of wallet info (address + chainId)
|
|
8543
|
+
* @returns Promise that resolves when subscription is confirmed
|
|
8544
|
+
*
|
|
8545
|
+
* @example
|
|
8546
|
+
* ```typescript
|
|
8547
|
+
* await sdk.walletEvents.connect();
|
|
8548
|
+
* await sdk.walletEvents.subscribeWallets([
|
|
8549
|
+
* { address: '0x123...', chainId: 39123 }
|
|
8550
|
+
* ]);
|
|
8551
|
+
* ```
|
|
8552
|
+
*/
|
|
8553
|
+
async subscribeWallets(wallets) {
|
|
8554
|
+
if (!this.client) {
|
|
8555
|
+
throw new Error('Not connected. Call connect() first.');
|
|
8556
|
+
}
|
|
8557
|
+
return this.client.subscribeWallets(wallets);
|
|
8558
|
+
}
|
|
8559
|
+
/**
|
|
8560
|
+
* Subscribe to chain events (Admin Dashboard)
|
|
8561
|
+
*
|
|
8562
|
+
* Receives ALL events on the specified chains.
|
|
8563
|
+
* Use for admin dashboards that need to monitor all activity.
|
|
8564
|
+
*
|
|
8565
|
+
* @param chains - Array of chain IDs to subscribe to
|
|
8566
|
+
* @returns Promise that resolves when subscription is confirmed
|
|
8567
|
+
*
|
|
8568
|
+
* @example
|
|
8569
|
+
* ```typescript
|
|
8570
|
+
* await sdk.walletEvents.connect();
|
|
8571
|
+
* await sdk.walletEvents.subscribeChains([39123, 137]);
|
|
8572
|
+
* ```
|
|
8573
|
+
*/
|
|
8574
|
+
async subscribeChains(chains) {
|
|
8575
|
+
if (!this.client) {
|
|
8576
|
+
throw new Error('Not connected. Call connect() first.');
|
|
8577
|
+
}
|
|
8578
|
+
return this.client.subscribeChains(chains);
|
|
8579
|
+
}
|
|
8580
|
+
/**
|
|
8581
|
+
* Unsubscribe from wallet events
|
|
8582
|
+
*
|
|
8583
|
+
* @param wallets - Array of wallet info to unsubscribe from
|
|
8584
|
+
* @returns Promise that resolves when unsubscription is confirmed
|
|
8585
|
+
*/
|
|
8586
|
+
async unsubscribeWallets(wallets) {
|
|
8587
|
+
if (!this.client) {
|
|
8588
|
+
throw new Error('Not connected. Call connect() first.');
|
|
8589
|
+
}
|
|
8590
|
+
return this.client.unsubscribeWallets(wallets);
|
|
8591
|
+
}
|
|
8592
|
+
/**
|
|
8593
|
+
* Unsubscribe from chain events
|
|
8594
|
+
*
|
|
8595
|
+
* @param chains - Array of chain IDs to unsubscribe from
|
|
8596
|
+
* @returns Promise that resolves when unsubscription is confirmed
|
|
8597
|
+
*/
|
|
8598
|
+
async unsubscribeChains(chains) {
|
|
8599
|
+
if (!this.client) {
|
|
8600
|
+
throw new Error('Not connected. Call connect() first.');
|
|
8601
|
+
}
|
|
8602
|
+
return this.client.unsubscribeChains(chains);
|
|
8603
|
+
}
|
|
8604
|
+
/**
|
|
8605
|
+
* Get current subscription state
|
|
8606
|
+
*/
|
|
8607
|
+
getSubscriptionState() {
|
|
8608
|
+
return this.client?.getSubscriptionState() ?? { wallets: [], chains: [], activeChains: [] };
|
|
8609
|
+
}
|
|
8610
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
8611
|
+
// Event Handlers
|
|
8612
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
8613
|
+
/**
|
|
8614
|
+
* Subscribe to blockchain events
|
|
8615
|
+
*
|
|
8616
|
+
* @param eventType - Event type ('Transfer', 'Approval', etc.) or '*' for all
|
|
8617
|
+
* @param handler - Callback function when event is received
|
|
8618
|
+
* @returns Unsubscribe function
|
|
8619
|
+
*
|
|
8620
|
+
* @example
|
|
8621
|
+
* ```typescript
|
|
8622
|
+
* const unsub = sdk.walletEvents.on('Transfer', (event) => {
|
|
8623
|
+
* console.log(`Transfer: ${event.data.from} -> ${event.data.to}`);
|
|
8624
|
+
* });
|
|
8625
|
+
*
|
|
8626
|
+
* // Later: stop listening
|
|
8627
|
+
* unsub();
|
|
8628
|
+
* ```
|
|
8629
|
+
*/
|
|
8630
|
+
on(eventType, handler) {
|
|
8631
|
+
if (this.client) {
|
|
8632
|
+
const unsub = this.client.on(eventType, handler);
|
|
8633
|
+
this.unsubscribes.push(unsub);
|
|
8634
|
+
return unsub;
|
|
8635
|
+
}
|
|
8636
|
+
else {
|
|
8637
|
+
// Store for when connect() is called
|
|
8638
|
+
this.pendingHandlers.push({ type: eventType, handler });
|
|
8639
|
+
return () => {
|
|
8640
|
+
const idx = this.pendingHandlers.findIndex(h => h.handler === handler);
|
|
8641
|
+
if (idx !== -1)
|
|
8642
|
+
this.pendingHandlers.splice(idx, 1);
|
|
8643
|
+
};
|
|
8644
|
+
}
|
|
8645
|
+
}
|
|
8646
|
+
/**
|
|
8647
|
+
* Subscribe to connection state changes
|
|
8648
|
+
*/
|
|
8649
|
+
onStateChange(handler) {
|
|
8650
|
+
if (this.client) {
|
|
8651
|
+
return this.client.onStateChange(handler);
|
|
8652
|
+
}
|
|
8653
|
+
return () => { };
|
|
8654
|
+
}
|
|
8655
|
+
/**
|
|
8656
|
+
* Get current connection state
|
|
8657
|
+
*/
|
|
8658
|
+
getState() {
|
|
8659
|
+
return this.client?.getState() ?? 'disconnected';
|
|
8660
|
+
}
|
|
8661
|
+
/**
|
|
8662
|
+
* Check if connected
|
|
8663
|
+
*/
|
|
8664
|
+
isConnected() {
|
|
8665
|
+
return this.getState() === 'connected';
|
|
8666
|
+
}
|
|
8667
|
+
/**
|
|
8668
|
+
* Get connection info (wallets, active chains)
|
|
8669
|
+
*/
|
|
8670
|
+
getConnectionInfo() {
|
|
8671
|
+
return this.client?.getConnectionInfo();
|
|
8672
|
+
}
|
|
8673
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
8674
|
+
// Private Methods
|
|
8675
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
8676
|
+
/**
|
|
8677
|
+
* Build a descriptive message for blockchain events
|
|
8678
|
+
* Neutral tone - frontend will handle user-facing presentation
|
|
8679
|
+
*/
|
|
8680
|
+
buildUserMessage(event) {
|
|
8681
|
+
const { type, data } = event;
|
|
8682
|
+
// Use string for switch since event types may extend beyond strict BlockchainEventType union
|
|
8683
|
+
const eventType = type;
|
|
8684
|
+
const from = data.from?.toLowerCase();
|
|
8685
|
+
const to = data.to?.toLowerCase();
|
|
8686
|
+
const shortFrom = from ? `${from.slice(0, 6)}...${from.slice(-4)}` : 'unknown';
|
|
8687
|
+
const shortTo = to ? `${to.slice(0, 6)}...${to.slice(-4)}` : 'unknown';
|
|
8688
|
+
// Get user's wallets to determine direction
|
|
8689
|
+
const subscriptionState = this.getSubscriptionState();
|
|
8690
|
+
const userWallets = subscriptionState.wallets.map(w => w.address.toLowerCase());
|
|
8691
|
+
const isIncoming = to && userWallets.includes(to);
|
|
8692
|
+
const isOutgoing = from && userWallets.includes(from);
|
|
8693
|
+
const isMint = from === '0x0000000000000000000000000000000000000000';
|
|
8694
|
+
const isBurn = to === '0x0000000000000000000000000000000000000000';
|
|
8695
|
+
switch (eventType) {
|
|
8696
|
+
case 'Transfer':
|
|
8697
|
+
if (isMint) {
|
|
8698
|
+
return `Token minted to ${shortTo}`;
|
|
8699
|
+
}
|
|
8700
|
+
else if (isBurn) {
|
|
8701
|
+
return `Token burned from ${shortFrom}`;
|
|
8702
|
+
}
|
|
8703
|
+
else if (isIncoming) {
|
|
8704
|
+
return `Transfer received from ${shortFrom}`;
|
|
8705
|
+
}
|
|
8706
|
+
else if (isOutgoing) {
|
|
8707
|
+
return `Transfer sent to ${shortTo}`;
|
|
8708
|
+
}
|
|
8709
|
+
return `Transfer from ${shortFrom} to ${shortTo}`;
|
|
8710
|
+
case 'Approval':
|
|
8711
|
+
return `Approval from ${shortFrom}`;
|
|
8712
|
+
case 'SmartWalletCreated':
|
|
8713
|
+
return `Smart wallet created: ${shortTo}`;
|
|
8714
|
+
case 'TransactionExecuted':
|
|
8715
|
+
return `Transaction executed by ${shortFrom}`;
|
|
8716
|
+
case 'OwnershipTransferred':
|
|
8717
|
+
return `Ownership transferred from ${shortFrom} to ${shortTo}`;
|
|
8718
|
+
default:
|
|
8719
|
+
return `${eventType} event`;
|
|
8720
|
+
}
|
|
8721
|
+
}
|
|
8722
|
+
/**
|
|
8723
|
+
* Route blockchain event to PersEventEmitter for unified event stream
|
|
8724
|
+
*/
|
|
8725
|
+
emitToPersEvents(event) {
|
|
8726
|
+
const userMessage = this.buildUserMessage(event);
|
|
8727
|
+
this.eventEmitter.emitSuccess({
|
|
8728
|
+
type: `wallet_${event.type.toLowerCase()}`,
|
|
8729
|
+
domain: 'wallet',
|
|
8730
|
+
userMessage,
|
|
8731
|
+
details: {
|
|
8732
|
+
chainId: event.chainId,
|
|
8733
|
+
txHash: event.txHash,
|
|
8734
|
+
blockNumber: event.blockNumber,
|
|
8735
|
+
timestamp: event.timestamp,
|
|
8736
|
+
contractAddress: event.contractAddress,
|
|
8737
|
+
...event.data,
|
|
8738
|
+
},
|
|
8739
|
+
});
|
|
8740
|
+
}
|
|
8741
|
+
}
|
|
8742
|
+
|
|
8743
|
+
/**
|
|
8744
|
+
* @fileoverview PERS SDK - Platform-agnostic TypeScript SDK with High-Level Managers
|
|
8745
|
+
*
|
|
8746
|
+
* @module @explorins/pers-sdk
|
|
8747
|
+
* @version 1.6.4
|
|
8748
|
+
* @author Explorins
|
|
8749
|
+
* @license MIT
|
|
8750
|
+
*/
|
|
8751
|
+
/**
|
|
8752
|
+
* PERS SDK - Main SDK class with domain managers
|
|
8753
|
+
*
|
|
8754
|
+
* Main SDK interface providing clean, high-level managers for common operations
|
|
8755
|
+
* while maintaining full access to the underlying API client and domain services.
|
|
8756
|
+
*
|
|
8757
|
+
* The SDK follows a layered architecture:
|
|
8758
|
+
* - **Managers**: High-level, intuitive APIs for common operations
|
|
8759
|
+
* - **Domain Services**: Full-featured access for advanced use cases
|
|
8760
|
+
* - **API Client**: Direct REST API access for custom operations
|
|
8761
|
+
*
|
|
8762
|
+
* @group Core
|
|
8763
|
+
* @category SDK
|
|
8764
|
+
*
|
|
8765
|
+
* @example Basic Setup
|
|
8766
|
+
* ```typescript
|
|
8767
|
+
* import { PersSDK } from '@explorins/pers-sdk';
|
|
8768
|
+
* import { BrowserFetchClientAdapter } from '@explorins/pers-sdk/platform-adapters';
|
|
8769
|
+
*
|
|
8770
|
+
* const sdk = new PersSDK(new BrowserFetchClientAdapter(), {
|
|
8771
|
+
* environment: 'production',
|
|
8772
|
+
* apiProjectKey: 'your-project-key'
|
|
8773
|
+
* });
|
|
8774
|
+
* ```
|
|
8775
|
+
*
|
|
8776
|
+
* @example Authentication Flow
|
|
8777
|
+
* ```typescript
|
|
8778
|
+
* // Login with external JWT
|
|
8779
|
+
* await sdk.auth.loginWithToken(firebaseJWT, 'user');
|
|
8780
|
+
*
|
|
8781
|
+
* // Check authentication
|
|
8782
|
+
* if (await sdk.auth.isAuthenticated()) {
|
|
8783
|
+
* const user = await sdk.auth.getCurrentUser();
|
|
8784
|
+
* console.log('Welcome,', user.name);
|
|
8785
|
+
* }
|
|
8786
|
+
* ```
|
|
8787
|
+
*
|
|
8788
|
+
* @example Business Operations
|
|
8789
|
+
* ```typescript
|
|
8790
|
+
* // Get active businesses
|
|
8791
|
+
* const businesses = await sdk.businesses.getActiveBusinesses();
|
|
8792
|
+
*
|
|
8793
|
+
* // Get business details
|
|
8794
|
+
* const business = await sdk.businesses.getBusinessById(businessId);
|
|
8795
|
+
* ```
|
|
8796
|
+
*
|
|
8797
|
+
* @example Token Operations
|
|
8798
|
+
* ```typescript
|
|
8799
|
+
* // Get user's token balances
|
|
8800
|
+
* const tokens = await sdk.tokens.getTokens();
|
|
8801
|
+
*
|
|
8802
|
+
* // Get active credit token
|
|
8803
|
+
* const creditToken = await sdk.tokens.getActiveCreditToken();
|
|
8804
|
+
* ```
|
|
8805
|
+
*
|
|
8806
|
+
* @since 1.3.0 - Manager pattern architecture
|
|
8807
|
+
*/
|
|
8808
|
+
class PersSDK {
|
|
8809
|
+
/**
|
|
8810
|
+
* Creates a new PERS SDK instance
|
|
8811
|
+
*
|
|
8812
|
+
* Initializes all domain managers and sets up the API client with the provided
|
|
8813
|
+
* HTTP client adapter and configuration.
|
|
8814
|
+
*
|
|
8815
|
+
* @param httpClient - Platform-specific HTTP client implementation
|
|
8816
|
+
* @param config - SDK configuration options
|
|
8817
|
+
*
|
|
8818
|
+
* @example Browser Setup
|
|
8819
|
+
* ```typescript
|
|
8820
|
+
* import { BrowserFetchClientAdapter } from '@explorins/pers-sdk/platform-adapters';
|
|
8821
|
+
*
|
|
8822
|
+
* const sdk = new PersSDK(new BrowserFetchClientAdapter(), {
|
|
8823
|
+
* environment: 'production',
|
|
8824
|
+
* apiProjectKey: 'your-project-key'
|
|
8825
|
+
* });
|
|
8826
|
+
* ```
|
|
8827
|
+
*
|
|
8828
|
+
* @example Node.js Setup
|
|
8829
|
+
* ```typescript
|
|
8830
|
+
* import { NodeHttpClientAdapter } from '@explorins/pers-sdk/platform-adapters';
|
|
8831
|
+
*
|
|
8832
|
+
* const sdk = new PersSDK(new NodeHttpClientAdapter(), {
|
|
8833
|
+
* environment: 'production',
|
|
8834
|
+
* apiProjectKey: 'your-project-key',
|
|
8835
|
+
* baseUrl: 'https://api.yourpers.com'
|
|
8836
|
+
* });
|
|
8837
|
+
* ```
|
|
8838
|
+
*
|
|
8839
|
+
* @example Angular Setup
|
|
7494
8840
|
* ```typescript
|
|
7495
8841
|
* import { AngularHttpClientAdapter } from '@explorins/pers-sdk/platform-adapters';
|
|
7496
8842
|
*
|
|
@@ -7507,6 +8853,81 @@ class PersSDK {
|
|
|
7507
8853
|
// Initialize event emitter and wire to API client for error events
|
|
7508
8854
|
this._events = new PersEventEmitter();
|
|
7509
8855
|
this.apiClient.setEvents(this._events);
|
|
8856
|
+
// Auto-connect to wallet events on successful login (if enabled)
|
|
8857
|
+
this.setupWalletEventsAutoConnect();
|
|
8858
|
+
}
|
|
8859
|
+
/**
|
|
8860
|
+
* Setup auto-connect for wallet events on authentication
|
|
8861
|
+
* @internal
|
|
8862
|
+
*/
|
|
8863
|
+
setupWalletEventsAutoConnect() {
|
|
8864
|
+
const config = this.apiClient.getConfig();
|
|
8865
|
+
// Check if captureWalletEvents is enabled (default: true)
|
|
8866
|
+
if (config.captureWalletEvents === false) {
|
|
8867
|
+
return;
|
|
8868
|
+
}
|
|
8869
|
+
// Listen for login success events (both fresh login and session restoration)
|
|
8870
|
+
this._events.subscribe((event) => {
|
|
8871
|
+
if (event.level === 'success' && (event.type === 'LOGIN_SUCCESS' || event.type === 'session_restored')) {
|
|
8872
|
+
// Auto-connect and subscribe to wallet events (fire and forget, log errors)
|
|
8873
|
+
this.connectWalletEvents().catch((err) => {
|
|
8874
|
+
console.warn('[PersSDK] Failed to auto-connect wallet events:', err.message);
|
|
8875
|
+
});
|
|
8876
|
+
}
|
|
8877
|
+
// Disconnect on logout
|
|
8878
|
+
if (event.level === 'success' && event.type === 'logout_success') {
|
|
8879
|
+
this.walletEvents.disconnect();
|
|
8880
|
+
}
|
|
8881
|
+
});
|
|
8882
|
+
}
|
|
8883
|
+
/**
|
|
8884
|
+
* Connect to wallet events and auto-subscribe based on auth type
|
|
8885
|
+
*
|
|
8886
|
+
* Connects to the WebSocket relay and automatically subscribes to relevant
|
|
8887
|
+
* blockchain events based on the current authentication type:
|
|
8888
|
+
* - **USER**: Subscribes to all user's wallets
|
|
8889
|
+
* - **BUSINESS**: Subscribes to all business's wallets
|
|
8890
|
+
* - **TENANT**: Subscribes to all chains where tokens are deployed
|
|
8891
|
+
*
|
|
8892
|
+
* This method is called automatically on login when `captureWalletEvents` is enabled.
|
|
8893
|
+
* Call manually if you need to reconnect or refresh subscriptions.
|
|
8894
|
+
*
|
|
8895
|
+
* @example Manual connection
|
|
8896
|
+
* ```typescript
|
|
8897
|
+
* await sdk.connectWalletEvents();
|
|
8898
|
+
* ```
|
|
8899
|
+
*/
|
|
8900
|
+
async connectWalletEvents() {
|
|
8901
|
+
await this.walletEvents.connect();
|
|
8902
|
+
// Get authType from auth provider (where it's stored during login)
|
|
8903
|
+
const authProvider = this.apiClient.getConfig().authProvider;
|
|
8904
|
+
const authType = authProvider?.getAuthType ? await authProvider.getAuthType() : undefined;
|
|
8905
|
+
switch (authType) {
|
|
8906
|
+
case AccountOwnerType.USER: {
|
|
8907
|
+
const user = await this.users.getCurrentUser();
|
|
8908
|
+
const wallets = user.wallets?.map(w => ({ address: w.address, chainId: w.chainId })) || [];
|
|
8909
|
+
if (wallets.length > 0) {
|
|
8910
|
+
await this.walletEvents.subscribeWallets(wallets);
|
|
8911
|
+
}
|
|
8912
|
+
break;
|
|
8913
|
+
}
|
|
8914
|
+
case AccountOwnerType.BUSINESS: {
|
|
8915
|
+
const business = await this.auth.getCurrentBusiness();
|
|
8916
|
+
const wallets = business.wallets?.map(w => ({ address: w.address, chainId: w.chainId })) || [];
|
|
8917
|
+
if (wallets.length > 0) {
|
|
8918
|
+
await this.walletEvents.subscribeWallets(wallets);
|
|
8919
|
+
}
|
|
8920
|
+
break;
|
|
8921
|
+
}
|
|
8922
|
+
case AccountOwnerType.TENANT: {
|
|
8923
|
+
const tokens = await this.tokens.getTokens();
|
|
8924
|
+
const chains = [...new Set(tokens.data.map(t => t.chainId))];
|
|
8925
|
+
if (chains.length > 0) {
|
|
8926
|
+
await this.walletEvents.subscribeChains(chains);
|
|
8927
|
+
}
|
|
8928
|
+
break;
|
|
8929
|
+
}
|
|
8930
|
+
}
|
|
7510
8931
|
}
|
|
7511
8932
|
/**
|
|
7512
8933
|
* Restore user session from stored tokens
|
|
@@ -7914,6 +9335,103 @@ class PersSDK {
|
|
|
7914
9335
|
}
|
|
7915
9336
|
return this._triggerSources;
|
|
7916
9337
|
}
|
|
9338
|
+
/**
|
|
9339
|
+
* Webhook manager - High-level webhook operations
|
|
9340
|
+
*
|
|
9341
|
+
* Provides methods for creating webhooks, triggering them programmatically,
|
|
9342
|
+
* and monitoring execution history. Supports async workflows with callbacks.
|
|
9343
|
+
*
|
|
9344
|
+
* @returns WebhookManager instance
|
|
9345
|
+
*
|
|
9346
|
+
* @example Webhook Operations
|
|
9347
|
+
* ```typescript
|
|
9348
|
+
* // Admin: Create a webhook
|
|
9349
|
+
* const webhook = await sdk.webhooks.create({
|
|
9350
|
+
* name: 'Order Processing',
|
|
9351
|
+
* targetUrl: 'https://n8n.example.com/webhook/orders',
|
|
9352
|
+
* method: 'POST'
|
|
9353
|
+
* });
|
|
9354
|
+
*
|
|
9355
|
+
* // Trigger webhook with payload
|
|
9356
|
+
* const result = await sdk.webhooks.trigger(webhook.id, {
|
|
9357
|
+
* orderId: 'order-123',
|
|
9358
|
+
* action: 'created'
|
|
9359
|
+
* });
|
|
9360
|
+
*
|
|
9361
|
+
* // Trigger and wait for async workflow completion
|
|
9362
|
+
* const asyncResult = await sdk.webhooks.triggerAndWait(
|
|
9363
|
+
* 'ai-webhook',
|
|
9364
|
+
* { prompt: 'Analyze this data' },
|
|
9365
|
+
* 30000 // Wait up to 30s
|
|
9366
|
+
* );
|
|
9367
|
+
* ```
|
|
9368
|
+
*
|
|
9369
|
+
* @see {@link WebhookManager} for detailed documentation
|
|
9370
|
+
*/
|
|
9371
|
+
get webhooks() {
|
|
9372
|
+
if (!this._webhooks) {
|
|
9373
|
+
const projectKey = this.apiClient.getConfig().apiProjectKey || '';
|
|
9374
|
+
this._webhooks = new WebhookManager(this.apiClient, projectKey, this._events);
|
|
9375
|
+
}
|
|
9376
|
+
return this._webhooks;
|
|
9377
|
+
}
|
|
9378
|
+
/**
|
|
9379
|
+
* Wallet Events Manager - Real-time blockchain events for user's wallets
|
|
9380
|
+
*
|
|
9381
|
+
* Provides real-time WebSocket connection to receive blockchain events
|
|
9382
|
+
* for the user's wallets (transfers, approvals, NFT mints, etc.).
|
|
9383
|
+
*
|
|
9384
|
+
* Events are also routed through `sdk.events` for unified event handling.
|
|
9385
|
+
*
|
|
9386
|
+
* **Important:** Requires `walletEventsWsUrl` configuration and authentication.
|
|
9387
|
+
*
|
|
9388
|
+
* @returns WalletEventsManager instance
|
|
9389
|
+
*
|
|
9390
|
+
* @example Basic Usage
|
|
9391
|
+
* ```typescript
|
|
9392
|
+
* // Configure SDK with events URL
|
|
9393
|
+
* sdk.configureWalletEvents({ wsUrl: 'wss://events.pers.ninja' });
|
|
9394
|
+
*
|
|
9395
|
+
* // Connect after authentication
|
|
9396
|
+
* await sdk.auth.loginWithToken(jwt, 'user');
|
|
9397
|
+
* await sdk.walletEvents.connect();
|
|
9398
|
+
*
|
|
9399
|
+
* // Listen for token transfers
|
|
9400
|
+
* sdk.walletEvents.on('Transfer', (event) => {
|
|
9401
|
+
* if (event.data.to === myWallet) {
|
|
9402
|
+
* showNotification(`Received ${event.data.value} tokens!`);
|
|
9403
|
+
* }
|
|
9404
|
+
* });
|
|
9405
|
+
* ```
|
|
9406
|
+
*
|
|
9407
|
+
* @example Unified Event Stream
|
|
9408
|
+
* ```typescript
|
|
9409
|
+
* // Wallet events also flow through sdk.events
|
|
9410
|
+
* sdk.events.subscribe((event) => {
|
|
9411
|
+
* if (event.domain === 'wallet') {
|
|
9412
|
+
* console.log('Wallet event:', event.type);
|
|
9413
|
+
* }
|
|
9414
|
+
* });
|
|
9415
|
+
* ```
|
|
9416
|
+
*
|
|
9417
|
+
* @see {@link WalletEventsManager} for detailed documentation
|
|
9418
|
+
*/
|
|
9419
|
+
get walletEvents() {
|
|
9420
|
+
if (!this._walletEvents) {
|
|
9421
|
+
this._walletEvents = new WalletEventsManager(this.apiClient, this._events, this.walletEventsConfig);
|
|
9422
|
+
}
|
|
9423
|
+
return this._walletEvents;
|
|
9424
|
+
}
|
|
9425
|
+
/**
|
|
9426
|
+
* Configure wallet events (call before accessing walletEvents)
|
|
9427
|
+
*
|
|
9428
|
+
* @param config - Events configuration including wsUrl
|
|
9429
|
+
*/
|
|
9430
|
+
configureWalletEvents(config) {
|
|
9431
|
+
this.walletEventsConfig = config;
|
|
9432
|
+
// Reset manager so it picks up new config
|
|
9433
|
+
this._walletEvents = undefined;
|
|
9434
|
+
}
|
|
7917
9435
|
/**
|
|
7918
9436
|
* Gets the API client for direct PERS API requests
|
|
7919
9437
|
*
|
|
@@ -7963,5 +9481,5 @@ function createPersSDK(httpClient, config) {
|
|
|
7963
9481
|
return new PersSDK(httpClient, config);
|
|
7964
9482
|
}
|
|
7965
9483
|
|
|
7966
|
-
export { AuthStatus as A, BusinessManager as B, CampaignManager as C, DefaultAuthProvider as D,
|
|
7967
|
-
//# sourceMappingURL=pers-sdk-
|
|
9484
|
+
export { AuthStatus as A, BusinessManager as B, CampaignManager as C, DefaultAuthProvider as D, FileApi as E, FileManager as F, FileService as G, ApiKeyApi as H, WebhookApi as I, WebhookService as J, PersEventsClient as K, LocalStorageTokenStorage as L, MemoryTokenStorage as M, createPersEventsClient as N, PersSDK as P, RedemptionManager as R, SDK_NAME as S, TokenManager as T, UserManager as U, WebDPoPCryptoProvider as W, AuthTokenManager as a, AUTH_STORAGE_KEYS as b, createPersSDK as c, DPOP_STORAGE_KEYS as d, SDK_VERSION as e, SDK_USER_AGENT as f, PersApiClient as g, DEFAULT_PERS_CONFIG as h, buildApiRoot as i, buildWalletEventsWsUrl as j, StaticJwtAuthProvider as k, AuthApi as l, mergeWithDefaults as m, AuthService as n, DPoPManager as o, PersEventEmitter as p, AuthManager as q, UserStatusManager as r, TransactionManager as s, PurchaseManager as t, ApiKeyManager as u, AnalyticsManager as v, DonationManager as w, TriggerSourceManager as x, WebhookManager as y, WalletEventsManager as z };
|
|
9485
|
+
//# sourceMappingURL=pers-sdk-BiP7UMJ3.js.map
|