@openzeppelin/ui-builder-adapter-stellar 1.1.0 → 1.1.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openzeppelin/ui-builder-adapter-stellar",
3
- "version": "1.1.0",
3
+ "version": "1.1.2",
4
4
  "description": "Stellar Adapter for UI Builder",
5
5
  "keywords": [
6
6
  "openzeppelin",
@@ -20,10 +20,64 @@ import {
20
20
  IndexerUnavailable,
21
21
  OperationFailed,
22
22
  } from '@openzeppelin/ui-builder-types';
23
- import { appConfigService, logger } from '@openzeppelin/ui-builder-utils';
23
+ import {
24
+ appConfigService,
25
+ isValidUrl,
26
+ logger,
27
+ userNetworkServiceConfigService,
28
+ } from '@openzeppelin/ui-builder-utils';
24
29
 
25
30
  const LOG_SYSTEM = 'StellarIndexerClient';
26
31
 
32
+ /**
33
+ * Extracts the user-configured indexer endpoints from UserNetworkServiceConfigService.
34
+ * Validates URLs before returning them to prevent invalid URLs from causing runtime errors.
35
+ *
36
+ * @param networkId - The network ID to get the indexer config for
37
+ * @returns The indexer endpoint config if configured and valid, undefined otherwise
38
+ */
39
+ function getUserIndexerEndpoints(networkId: string): IndexerEndpointConfig | undefined {
40
+ const svcCfg = userNetworkServiceConfigService.get(networkId, 'indexer');
41
+ if (!svcCfg || typeof svcCfg !== 'object') {
42
+ return undefined;
43
+ }
44
+
45
+ const endpoints: IndexerEndpointConfig = {};
46
+
47
+ // Check for indexerUri field (HTTP endpoint) and validate
48
+ if ('indexerUri' in svcCfg && svcCfg.indexerUri) {
49
+ const httpUrl = String(svcCfg.indexerUri).trim();
50
+ if (httpUrl && isValidUrl(httpUrl)) {
51
+ endpoints.http = httpUrl;
52
+ } else if (httpUrl) {
53
+ logger.warn(
54
+ LOG_SYSTEM,
55
+ `User-configured indexer HTTP URL for ${networkId} is invalid: ${httpUrl}. Ignoring.`
56
+ );
57
+ }
58
+ }
59
+
60
+ // Check for indexerWsUri field (WebSocket endpoint) and validate
61
+ if ('indexerWsUri' in svcCfg && svcCfg.indexerWsUri) {
62
+ const wsUrl = String(svcCfg.indexerWsUri).trim();
63
+ if (wsUrl && isValidUrl(wsUrl)) {
64
+ endpoints.ws = wsUrl;
65
+ } else if (wsUrl) {
66
+ logger.warn(
67
+ LOG_SYSTEM,
68
+ `User-configured indexer WebSocket URL for ${networkId} is invalid: ${wsUrl}. Ignoring.`
69
+ );
70
+ }
71
+ }
72
+
73
+ // Return undefined if no valid endpoints were found
74
+ if (!endpoints.http && !endpoints.ws) {
75
+ return undefined;
76
+ }
77
+
78
+ return endpoints;
79
+ }
80
+
27
81
  /**
28
82
  * GraphQL query response types for indexer
29
83
  */
@@ -162,9 +216,41 @@ export class StellarIndexerClient {
162
216
  private resolvedEndpoints: IndexerEndpointConfig | null = null;
163
217
  private availabilityChecked = false;
164
218
  private isAvailable = false;
219
+ private readonly unsubscribeFromConfigChanges: () => void;
165
220
 
166
221
  constructor(networkConfig: StellarNetworkConfig) {
167
222
  this.networkConfig = networkConfig;
223
+
224
+ // Subscribe to indexer config changes to reset cache when user updates settings
225
+ this.unsubscribeFromConfigChanges = userNetworkServiceConfigService.subscribe(
226
+ networkConfig.id,
227
+ 'indexer',
228
+ () => {
229
+ logger.info(
230
+ LOG_SYSTEM,
231
+ `User indexer config changed for ${networkConfig.id}, resetting cache`
232
+ );
233
+ this.resetCache();
234
+ }
235
+ );
236
+ }
237
+
238
+ /**
239
+ * Resets the resolved endpoints and availability cache.
240
+ * Called when user configuration changes to force re-resolution.
241
+ */
242
+ private resetCache(): void {
243
+ this.resolvedEndpoints = null;
244
+ this.availabilityChecked = false;
245
+ this.isAvailable = false;
246
+ }
247
+
248
+ /**
249
+ * Cleans up subscriptions when the client is no longer needed.
250
+ * Call this method when disposing of the client to prevent memory leaks.
251
+ */
252
+ public dispose(): void {
253
+ this.unsubscribeFromConfigChanges();
168
254
  }
169
255
 
170
256
  /**
@@ -990,10 +1076,11 @@ export class StellarIndexerClient {
990
1076
  /**
991
1077
  * Resolve indexer endpoints with config precedence
992
1078
  * Priority:
993
- * 1. Runtime override from AppConfigService
994
- * 2. Network config defaults (indexerUri/indexerWsUri)
995
- * 3. Derived from RPC (if safe pattern exists)
996
- * 4. None (returns empty object)
1079
+ * 1. User-configured indexer from UserNetworkServiceConfigService (localStorage)
1080
+ * 2. Runtime override from AppConfigService (environment/JSON config)
1081
+ * 3. Network config defaults (indexerUri/indexerWsUri)
1082
+ * 4. Derived from RPC (if safe pattern exists)
1083
+ * 5. None (returns empty object)
997
1084
  *
998
1085
  * @returns Resolved indexer endpoints
999
1086
  */
@@ -1005,7 +1092,26 @@ export class StellarIndexerClient {
1005
1092
  const networkId = this.networkConfig.id;
1006
1093
  const endpoints: IndexerEndpointConfig = {};
1007
1094
 
1008
- // Priority 1: Check AppConfigService for runtime override
1095
+ // Priority 1: Check user-configured indexer from localStorage
1096
+ const userIndexerConfig = getUserIndexerEndpoints(networkId);
1097
+ if (userIndexerConfig) {
1098
+ if (userIndexerConfig.http) {
1099
+ endpoints.http = userIndexerConfig.http;
1100
+ }
1101
+ if (userIndexerConfig.ws) {
1102
+ endpoints.ws = userIndexerConfig.ws;
1103
+ }
1104
+ if (endpoints.http || endpoints.ws) {
1105
+ logger.info(
1106
+ LOG_SYSTEM,
1107
+ `Using user-configured indexer for ${networkId}: http=${endpoints.http}, ws=${endpoints.ws}`
1108
+ );
1109
+ this.resolvedEndpoints = endpoints;
1110
+ return endpoints;
1111
+ }
1112
+ }
1113
+
1114
+ // Priority 2: Check AppConfigService for runtime override
1009
1115
  const indexerOverride = appConfigService.getIndexerEndpointOverride(networkId);
1010
1116
  if (indexerOverride) {
1011
1117
  if (typeof indexerOverride === 'string') {
@@ -1030,7 +1136,7 @@ export class StellarIndexerClient {
1030
1136
  return endpoints;
1031
1137
  }
1032
1138
 
1033
- // Priority 2: Network config defaults
1139
+ // Priority 3: Network config defaults
1034
1140
  if (this.networkConfig.indexerUri) {
1035
1141
  endpoints.http = this.networkConfig.indexerUri;
1036
1142
  logger.info(
@@ -1051,7 +1157,7 @@ export class StellarIndexerClient {
1051
1157
  return endpoints;
1052
1158
  }
1053
1159
 
1054
- // Priority 3: Derive from RPC (only if safe, known pattern exists)
1160
+ // Priority 4: Derive from RPC (only if safe, known pattern exists)
1055
1161
  // Currently DISABLED - no safe derivation pattern implemented
1056
1162
  // This would be enabled in the future when indexer/RPC relationship is well-defined
1057
1163
  logger.debug(LOG_SYSTEM, `No indexer derivation pattern available for ${networkId}`);
@@ -1075,6 +1181,7 @@ export class StellarIndexerClient {
1075
1181
  OWNERSHIP_TRANSFER_COMPLETED: 'OWNERSHIP_TRANSFER_COMPLETED',
1076
1182
  ADMIN_TRANSFER_INITIATED: 'ADMIN_TRANSFER_INITIATED',
1077
1183
  ADMIN_TRANSFER_COMPLETED: 'ADMIN_TRANSFER_COMPLETED',
1184
+ UNKNOWN: 'UNKNOWN',
1078
1185
  };
1079
1186
  return mapping[changeType];
1080
1187
  }
@@ -1318,6 +1318,22 @@ export class StellarAccessControlService implements AccessControlService {
1318
1318
  return [];
1319
1319
  }
1320
1320
  }
1321
+
1322
+ /**
1323
+ * Disposes of the service and cleans up resources.
1324
+ *
1325
+ * Cleans up the indexer client's subscriptions to prevent memory leaks.
1326
+ * Call this method when the service is no longer needed.
1327
+ *
1328
+ * Note: In typical usage where the service is application-scoped and lives
1329
+ * for the duration of the application, calling dispose is not strictly necessary.
1330
+ * However, it should be called if the service is created/destroyed dynamically
1331
+ * (e.g., in tests or when switching networks).
1332
+ */
1333
+ dispose(): void {
1334
+ this.indexerClient.dispose();
1335
+ logger.debug('StellarAccessControlService.dispose', 'Service disposed');
1336
+ }
1321
1337
  }
1322
1338
 
1323
1339
  /**
@@ -16,5 +16,5 @@ export const stellarTestnet: StellarNetworkConfig = {
16
16
  networkPassphrase: 'Test SDF Network ; September 2015',
17
17
  explorerUrl: 'https://stellar.expert/explorer/testnet',
18
18
  iconComponent: NetworkStellar,
19
- // indexerUri and indexerWsUri will be added here when stable testnet indexer endpoints are available
19
+ indexerUri: 'https://openzepplin-stellar-testnet.graphql.subquery.network',
20
20
  };