@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/dist/index.cjs +102 -7
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +114 -14
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/access-control/indexer-client.ts +115 -8
- package/src/access-control/service.ts +16 -0
- package/src/networks/testnet.ts +1 -1
package/package.json
CHANGED
|
@@ -20,10 +20,64 @@ import {
|
|
|
20
20
|
IndexerUnavailable,
|
|
21
21
|
OperationFailed,
|
|
22
22
|
} from '@openzeppelin/ui-builder-types';
|
|
23
|
-
import {
|
|
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.
|
|
994
|
-
* 2.
|
|
995
|
-
* 3.
|
|
996
|
-
* 4.
|
|
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
|
|
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
|
|
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
|
|
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
|
/**
|
package/src/networks/testnet.ts
CHANGED
|
@@ -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
|
-
|
|
19
|
+
indexerUri: 'https://openzepplin-stellar-testnet.graphql.subquery.network',
|
|
20
20
|
};
|