suidouble 2.5.0 → 2.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/.claude/settings.local.json +7 -0
  2. package/README.md +222 -131
  3. package/index.js +0 -2
  4. package/lib/SuiCliCommands.js +18 -25
  5. package/lib/SuiCoin.js +79 -137
  6. package/lib/SuiCoins.js +41 -29
  7. package/lib/SuiCommonMethods.js +40 -3
  8. package/lib/SuiEvent.js +54 -6
  9. package/lib/SuiInBrowser.js +143 -15
  10. package/lib/SuiInBrowserAdapter.js +185 -40
  11. package/lib/SuiLocalTestValidator.js +76 -14
  12. package/lib/SuiMaster.js +335 -139
  13. package/lib/SuiMemoryObjectStorage.js +66 -73
  14. package/lib/SuiObject.js +128 -153
  15. package/lib/SuiPackage.js +292 -187
  16. package/lib/SuiPackageModule.js +176 -221
  17. package/lib/SuiPaginatedResponse.js +288 -25
  18. package/lib/SuiPseudoRandomAddress.js +29 -2
  19. package/lib/SuiTransaction.js +115 -70
  20. package/lib/SuiUtils.js +179 -127
  21. package/package.json +29 -13
  22. package/test/build_modules.test.js +41 -0
  23. package/test/coins.test.js +17 -16
  24. package/test/custom_transaction.test.js +167 -0
  25. package/test/event_listeners.test.js +171 -0
  26. package/test/failed_transaction.test.js +184 -0
  27. package/test/name_service.test.js +28 -0
  28. package/test/owned_objects.test.js +148 -0
  29. package/test/rpc.test.js +3 -6
  30. package/test/sui_in_browser.test.js +2 -2
  31. package/test/sui_master_basic.test.js +4 -5
  32. package/test/sui_master_onlocal.test.js +84 -22
  33. package/test/sui_object_properties.test.js +85 -0
  34. package/test/test_move_contracts/different_types/Move.lock +18 -0
  35. package/test/test_move_contracts/suidouble_chat/Move.lock +18 -0
  36. package/tsconfig.json +15 -0
  37. package/types/index.d.ts +15 -0
  38. package/types/lib/SuiCliCommands.d.ts +6 -0
  39. package/types/lib/SuiCoin.d.ts +183 -0
  40. package/types/lib/SuiCoins.d.ts +93 -0
  41. package/types/lib/SuiCommonMethods.d.ts +37 -0
  42. package/types/lib/SuiEvent.d.ts +95 -0
  43. package/types/lib/SuiInBrowser.d.ts +189 -0
  44. package/types/lib/SuiInBrowserAdapter.d.ts +171 -0
  45. package/types/lib/SuiLocalTestValidator.d.ts +92 -0
  46. package/types/lib/SuiMaster.d.ts +333 -0
  47. package/types/lib/SuiMemoryObjectStorage.d.ts +96 -0
  48. package/types/lib/SuiObject.d.ts +135 -0
  49. package/types/lib/SuiPackage.d.ts +233 -0
  50. package/types/lib/SuiPackageModule.d.ts +139 -0
  51. package/types/lib/SuiPaginatedResponse.d.ts +148 -0
  52. package/types/lib/SuiPseudoRandomAddress.d.ts +33 -0
  53. package/types/lib/SuiTransaction.d.ts +92 -0
  54. package/types/lib/SuiUtils.d.ts +152 -0
  55. package/types/lib/data/icons.d.ts +12 -0
  56. package/lib/SuiTestScenario.js +0 -169
  57. package/test/sui_test_scenario.test.js +0 -61
@@ -1,5 +1,15 @@
1
1
  import SuiCommonMethods from './SuiCommonMethods.js';
2
2
 
3
+
4
+ /**
5
+ * @typedef {import("@mysten/sui/cryptography").SignatureWithBytes} SignatureWithBytes
6
+ */
7
+
8
+ /**
9
+ * Wallet Standard feature identifiers — keys map to the string feature names used by the
10
+ * `@wallet-standard/core` registry.
11
+ * @enum {string}
12
+ */
3
13
  const Feature = {
4
14
  DISCONNECT: 'standard:disconnect',
5
15
  CONNECT: 'standard:connect',
@@ -12,25 +22,53 @@ const Feature = {
12
22
  SUI_SIGN_PERSONAL_MESSAGE: 'sui:signPersonalMessage',
13
23
  };
14
24
 
25
+ /**
26
+ * Wrapper over a single Wallet Standard adapter (e.g. Sui Wallet, Martian, Suiet).
27
+ * Abstracts away legacy `signAndExecuteTransactionBlock` vs the current
28
+ * `signAndExecuteTransaction` API, and normalises connection state changes into
29
+ * `'connected'` / `'disconnected'` events on this instance.
30
+ */
15
31
  export default class SuiInBrowserAdapter extends SuiCommonMethods {
32
+ /**
33
+ * @param {Object} [params]
34
+ * @param {Object} [params.standardAdapter] - raw Wallet Standard adapter from `@wallet-standard/core`
35
+ * @param {?string} [params.name] - display name used when no adapter is attached (e.g. "not installed" placeholder)
36
+ * @param {?string} [params.icon] - data-URI icon used when no adapter is attached
37
+ * @param {Object} [params.downloadUrls] - map of browser name → extension store URL (e.g. `{ chrome: 'https://…' }`)
38
+ * @param {boolean} [params.debug]
39
+ */
16
40
  constructor(params = {}) {
17
41
  super(params);
18
42
 
19
- this._standartAdapter = null;
20
- // params.standartAdapter || null; // instance returned from '@wallet-standard/core'
21
- if (params.standartAdapter) {
22
- this.setStandartAdapter(params.standartAdapter);
43
+ /** @type {?Object} raw Wallet Standard adapter */
44
+ this._standardAdapter = null;
45
+ if (params.standardAdapter) {
46
+ this.setStandartAdapter(params.standardAdapter);
23
47
  }
24
-
48
+
49
+ /** @type {?string} */
25
50
  this._name = params.name || null;
51
+ /** @type {?string} */
26
52
  this._icon = params.icon || null;
53
+ /** @type {Object.<string, string>} */
27
54
  this._downloadUrls = params.downloadUrls || {};
28
55
 
56
+ /** @type {?string} */
29
57
  this._connectedAddress = null;
58
+ /** @type {?string} */
30
59
  this._connectedChain = null;
60
+ /** @type {boolean} */
31
61
  this._isConnected = false;
62
+ /** @type {boolean} */
63
+ this._userDisconnected = false;
32
64
  }
33
65
 
66
+ /**
67
+ * Sign and execute a transaction. Prefers the current `sui:signAndExecuteTransaction`
68
+ * feature; falls back to the legacy `sui:signAndExecuteTransactionBlock` for older wallets.
69
+ * @param {Object} params
70
+ * @returns {Promise<*>}
71
+ */
34
72
  async signAndExecuteTransaction(params) {
35
73
  if (this.hasFeature(Feature.SUI_SIGN_AND_EXECUTE_TX)) {
36
74
  return await this.getFeature(Feature.SUI_SIGN_AND_EXECUTE_TX).signAndExecuteTransaction(params);
@@ -41,10 +79,32 @@ export default class SuiInBrowserAdapter extends SuiCommonMethods {
41
79
  }
42
80
  }
43
81
 
82
+ /**
83
+ * Sign and execute a transaction using the legacy `sui:signAndExecuteTransactionBlock` feature.
84
+ * @param {Object} params
85
+ * @returns {Promise<*>}
86
+ */
44
87
  async signAndExecuteTransactionBlock(params) {
45
88
  return await this.getFeature(Feature.SUI_SIGN_AND_EXECUTE_TX_BLOCK).signAndExecuteTransactionBlock(params);
46
89
  }
47
90
 
91
+ /**
92
+ * Sign a transaction using the current `sui:signTransaction` feature.
93
+ * @param {Object} params
94
+ * @returns {Promise<SignatureWithBytes>}
95
+ */
96
+ async signTransaction(params) {
97
+ /** @type {SignatureWithBytes} */
98
+ const resp = await this.getFeature(Feature.SUI_SIGN_TX).signTransaction(params);
99
+ return resp;
100
+ }
101
+
102
+ /**
103
+ * Sign a transaction. Prefers the current `sui:signTransaction` feature;
104
+ * falls back to the legacy `sui:signTransactionBlock` for older wallets.
105
+ * @param {Object} params
106
+ * @returns {Promise<*>}
107
+ */
48
108
  async signTransactionBlock(params) {
49
109
  if (this.hasFeature(Feature.SUI_SIGN_TX)) {
50
110
  return await this.getFeature(Feature.SUI_SIGN_TX).signTransaction(params);
@@ -55,10 +115,12 @@ export default class SuiInBrowserAdapter extends SuiCommonMethods {
55
115
  }
56
116
  }
57
117
 
58
- async signTransactionBlock(params) {
59
- return await this.getFeature(Feature.SUI_SIGN_TX_BLOCK).signTransactionBlock(params);
60
- }
61
-
118
+ /**
119
+ * Sign a personal/arbitrary message. Prefers `sui:signPersonalMessage`;
120
+ * falls back to `sui:signMessage` for older wallets.
121
+ * @param {Object} params
122
+ * @returns {Promise<*>}
123
+ */
62
124
  async signPersonalMessage(params) {
63
125
  if (this.hasFeature(Feature.SUI_SIGN_PERSONAL_MESSAGE)) {
64
126
  return await this.getFeature(Feature.SUI_SIGN_PERSONAL_MESSAGE).signPersonalMessage(params);
@@ -67,16 +129,43 @@ export default class SuiInBrowserAdapter extends SuiCommonMethods {
67
129
  }
68
130
  }
69
131
 
132
+ /**
133
+ * Alias for `signPersonalMessage`.
134
+ * @param {Object} params
135
+ * @returns {Promise<*>}
136
+ */
70
137
  async signMessage(params) {
71
138
  return await this.signPersonalMessage(params);
72
139
  }
73
140
 
141
+ /**
142
+ * Disconnect from the wallet and refresh connection state.
143
+ * Force-clears local connection state even if the wallet doesn't support
144
+ * standard:disconnect or doesn't revoke site authorization (e.g. Phantom).
145
+ * @param {Object} [params]
146
+ * @returns {Promise<void>}
147
+ */
74
148
  async disconnect(params) {
75
- const res = await this.getFeature(Feature.DISCONNECT).disconnect(params);
76
- this.connectionUpdated();
77
- return res;
149
+ try {
150
+ const feature = this.getFeature(Feature.DISCONNECT);
151
+ if (feature) {
152
+ await feature.disconnect(params);
153
+ }
154
+ } catch (e) {
155
+ console.warn('[SuiInBrowserAdapter] standard:disconnect failed:', e);
156
+ }
157
+
158
+ this._connectedAddress = null;
159
+ this._connectedChain = null;
160
+ this._isConnected = false;
161
+ this._userDisconnected = true;
162
+ this.emit('disconnected', this);
78
163
  }
79
164
 
165
+ /**
166
+ * Return the Chrome Web Store download URL, or null if not configured.
167
+ * @returns {?string}
168
+ */
80
169
  getDownloadURL() {
81
170
  if (this._downloadUrls && this._downloadUrls.chrome) {
82
171
  return this._downloadUrls.chrome;
@@ -84,26 +173,36 @@ export default class SuiInBrowserAdapter extends SuiCommonMethods {
84
173
  return null;
85
174
  }
86
175
 
176
+ /**
177
+ * True when no underlying adapter has been attached (i.e. this is a "not installed" placeholder).
178
+ * @returns {boolean}
179
+ */
87
180
  get isDefault() {
88
- if (!this._standartAdapter) {
89
- return true;
90
- }
91
- return false;
181
+ return !this._standardAdapter;
92
182
  }
93
183
 
184
+ /** @returns {?string} currently connected Sui address, or null */
94
185
  get connectedAddress() {
95
186
  return this._connectedAddress;
96
187
  }
97
188
 
189
+ /** @returns {?string} currently connected chain identifier (e.g. `'sui:mainnet'`), or null */
98
190
  get connectedChain() {
99
191
  return this._connectedChain;
100
192
  }
101
193
 
194
+ /** @returns {boolean} true if the wallet is connected and an address is available */
102
195
  get isConnected() {
103
196
  return this._isConnected;
104
197
  }
105
198
 
199
+ /**
200
+ * Request connection to the wallet. Errors are swallowed; connection state is refreshed
201
+ * via `connectionUpdated` regardless.
202
+ * @returns {Promise<void>}
203
+ */
106
204
  async connect() {
205
+ this._userDisconnected = false;
107
206
  try {
108
207
  await this.getFeature(Feature.CONNECT).connect();
109
208
  } catch (e) {
@@ -113,14 +212,21 @@ export default class SuiInBrowserAdapter extends SuiCommonMethods {
113
212
  this.connectionUpdated();
114
213
  }
115
214
 
215
+ /**
216
+ * Read the current connection state from the underlying adapter and update
217
+ * `connectedAddress`, `connectedChain`, and `isConnected`. Emits `'connected'` or
218
+ * `'disconnected'` if the state changed.
219
+ */
116
220
  connectionUpdated() {
221
+ if (this._userDisconnected) return;
222
+
117
223
  const wasConnectedAddress = ''+this._connectedAddress;
118
224
  const wasConnectedChain = ''+this._connectedChain;
119
225
 
120
226
  try {
121
- if (this._standartAdapter && this._standartAdapter.accounts && this._standartAdapter.accounts.length) {
122
- this._connectedAddress = this._standartAdapter.accounts[0].address;
123
- this._connectedChain = this._standartAdapter.accounts[0].chains[0];
227
+ if (this._standardAdapter && this._standardAdapter.accounts && this._standardAdapter.accounts.length) {
228
+ this._connectedAddress = this._standardAdapter.accounts[0].address;
229
+ this._connectedChain = this._standardAdapter.accounts[0].chains[0];
124
230
  } else {
125
231
  this._connectedAddress = null;
126
232
  this._connectedChain = null;
@@ -141,16 +247,23 @@ export default class SuiInBrowserAdapter extends SuiCommonMethods {
141
247
  }
142
248
  }
143
249
 
144
-
145
- setStandartAdapter(standartAdapter) {
146
- if (this._standartAdapter) {
250
+ /**
251
+ * Attach a Wallet Standard adapter to this instance. No-op if already attached.
252
+ * Returns false and does nothing if the adapter has no `sui:` features.
253
+ * Subscribes to adapter `'change'` events to keep connection state up to date.
254
+ *
255
+ * @param {Object} standardAdapter - raw adapter from `@wallet-standard/core`
256
+ * @returns {boolean} false if the adapter lacks Sui features, undefined on success
257
+ */
258
+ setStandartAdapter(standardAdapter) {
259
+ if (this._standardAdapter) {
147
260
  // no need to re-attach
148
261
  return true;
149
262
  }
150
263
 
151
264
  // check if it has sui: features
152
265
  let hasSuiFeatures = false;
153
- for (const key in standartAdapter.features) {
266
+ for (const key in standardAdapter.features) {
154
267
  if ((''+key).indexOf('sui:') === 0) {
155
268
  hasSuiFeatures = true;
156
269
  }
@@ -160,65 +273,97 @@ export default class SuiInBrowserAdapter extends SuiCommonMethods {
160
273
  return false;
161
274
  }
162
275
 
163
- this._standartAdapter = standartAdapter;
164
- if (!this.__standartAdapterChangeListener) {
165
- this.__standartAdapterChangeListener = (e) => {
276
+ this._standardAdapter = standardAdapter;
277
+ if (!this.__standardAdapterChangeListener) {
278
+ this.__standardAdapterChangeListener = () => {
166
279
  this.connectionUpdated();
167
280
  };
168
281
  }
169
- this.getFeature(Feature.EVENTS).on('change', this.__standartAdapterChangeListener);
282
+ this.getFeature(Feature.EVENTS).on('change', this.__standardAdapterChangeListener);
170
283
 
171
284
  this.connectionUpdated();
172
285
  }
173
286
 
287
+ /**
288
+ * True if the adapter is installed and supports the minimum required Sui features
289
+ * (`sui:signAndExecuteTransaction` or `sui:signAndExecuteTransactionBlock`, plus `standard:events`).
290
+ * @returns {boolean}
291
+ */
174
292
  get okForSui() {
175
293
  if (!this.isInstalled) {
176
294
  return false;
177
295
  }
178
296
 
179
- return this.hasFeature(Feature.SUI_SIGN_AND_EXECUTE_TX_BLOCK) && this.hasFeature(Feature.EVENTS);
297
+ return (this.hasFeature(Feature.SUI_SIGN_AND_EXECUTE_TX_BLOCK) || this.hasFeature(Feature.SUI_SIGN_AND_EXECUTE_TX)) && this.hasFeature(Feature.EVENTS);
180
298
  }
181
299
 
300
+ /** @returns {boolean} true if a Wallet Standard adapter has been attached */
182
301
  get isInstalled() {
183
- if (this._standartAdapter) {
184
- return true;
185
- }
186
- return false;
302
+ return !!this._standardAdapter;
187
303
  }
188
304
 
305
+ /**
306
+ * Feature map from the underlying adapter, or an empty object if not installed.
307
+ * Keys are Wallet Standard feature strings (e.g. `'sui:signAndExecuteTransaction'`).
308
+ * @returns {Object}
309
+ */
189
310
  get features() {
190
- if (this._standartAdapter) {
191
- return this._standartAdapter.features;
311
+ if (this._standardAdapter) {
312
+ return this._standardAdapter.features;
192
313
  }
193
314
  return {};
194
315
  }
195
316
 
317
+ /**
318
+ * Display name — from the adapter if installed, otherwise from the constructor param.
319
+ * @returns {?string}
320
+ */
196
321
  get name() {
197
- if (this._standartAdapter) {
198
- return this._standartAdapter.name;
322
+ if (this._standardAdapter) {
323
+ return this._standardAdapter.name;
199
324
  } else {
200
325
  return this._name;
201
326
  }
202
327
  }
203
328
 
329
+ /**
330
+ * Data-URI icon — from the adapter if installed, otherwise from the constructor param.
331
+ * @returns {?string}
332
+ */
204
333
  get icon() {
205
- if (this._standartAdapter) {
206
- return this._standartAdapter.icon;
334
+ if (this._standardAdapter) {
335
+ return this._standardAdapter.icon;
207
336
  } else {
208
337
  return this._icon;
209
338
  }
210
339
  }
211
340
 
341
+ /**
342
+ * Wallet version string from the adapter, or undefined if not installed.
343
+ * @returns {?string}
344
+ */
212
345
  get version() {
213
- if (this._standartAdapter) {
214
- return this._standartAdapter.version;
346
+ if (this._standardAdapter) {
347
+ return this._standardAdapter.version;
215
348
  }
216
349
  }
217
350
 
351
+ /**
352
+ * Returns true if this adapter supports the given feature.
353
+ * Accepts either a `Feature` enum key (e.g. `'SUI_SIGN_TX'`) or a raw feature string.
354
+ * @param {string} featureName
355
+ * @returns {boolean}
356
+ */
218
357
  hasFeature(featureName) {
219
358
  return (!!this.getFeature(featureName));
220
359
  }
221
360
 
361
+ /**
362
+ * Return the feature object for `featureName`, or null if not available.
363
+ * Accepts either a `Feature` enum key (e.g. `'SUI_SIGN_TX'`) or a raw feature string.
364
+ * @param {string} featureName
365
+ * @returns {?Object}
366
+ */
222
367
  getFeature(featureName) {
223
368
  const features = this.features;
224
369
 
@@ -2,29 +2,55 @@ import SuiCliCommands from "./SuiCliCommands.js";
2
2
  import SuiCommonMethods from "./SuiCommonMethods.js";
3
3
  import SuiUtils from "./SuiUtils.js";
4
4
 
5
+ /** @typedef {import("@mysten/sui/grpc").SuiGrpcClient} SuiGrpcClient */
6
+
7
+ /**
8
+ * Manages a local `sui start` test-validator process. The singleton pattern (`launch` / `stop`)
9
+ * means only one validator runs per test suite even when called from multiple test files.
10
+ *
11
+ * When `testFallbackEnabled` is true and `sui start` fails (e.g. Sui CLI not installed),
12
+ * the instance transparently switches to `sui:devnet` so tests can still run against a live node.
13
+ */
5
14
  export default class SuiLocalTestValidator extends SuiCommonMethods {
15
+ /**
16
+ * @param {Object} [params]
17
+ * @param {boolean} [params.testFallbackEnabled] - fall back to devnet if the local node can't start
18
+ * @param {?number} [params.epochDuration] - custom epoch duration in ms (only valid with `--force-regenesis`)
19
+ * @param {boolean} [params.debug]
20
+ */
6
21
  constructor(params = {}) {
7
22
  super(params);
8
23
 
24
+ /** @type {?Object} spawned child process */
9
25
  this._child = null;
26
+ /** @type {boolean} */
10
27
  this._active = false;
11
28
 
12
- this._testFallbackEnabled = false;
13
- if (params.testFallbackEnabled) {
14
- // option for unit tests to fallback to sui:dev network in case
15
- // there is no local validator installed
16
- this._testFallbackEnabled = true;
17
- }
29
+ /**
30
+ * When true, falls back to sui:devnet if the local node can't be started.
31
+ * @type {boolean}
32
+ */
33
+ this._testFallbackEnabled = !!params.testFallbackEnabled;
18
34
 
35
+ /** @type {?number} */
19
36
  this._epochDuration = params.epochDuration || null;
20
37
 
38
+ /** @type {string} */
21
39
  this._providerName = 'sui:localnet';
22
40
  }
23
41
 
42
+ /** @returns {string} active chain identifier, e.g. `'sui:localnet'` or `'sui:devnet'` after fallback */
24
43
  get providerName() {
25
44
  return this._providerName;
26
45
  }
27
46
 
47
+ /** @type {?SuiLocalTestValidator} */
48
+ static __instance = null;
49
+
50
+ /**
51
+ * gRPC client for the currently active chain.
52
+ * @returns {SuiGrpcClient}
53
+ */
28
54
  get client() {
29
55
  if (this._providerName === 'sui:localnet') {
30
56
  return SuiUtils.suiClientFor('localnet');
@@ -34,15 +60,19 @@ export default class SuiLocalTestValidator extends SuiCommonMethods {
34
60
  }
35
61
  }
36
62
 
63
+ /** @returns {boolean} true once the node is up and accepting transactions */
37
64
  get active() {
38
65
  return this._active;
39
66
  }
40
67
 
41
68
  /**
42
- * Launch a localnet sui and wait for it to become available to accept transactions.
43
- * Don't forget to .stop() it after usage.
44
- *
45
- * @returns {SuiLocalTestValidator}
69
+ * Launch the local sui node (singleton). If already running, returns the existing instance.
70
+ * Don't forget to call `.stop()` after usage.
71
+ *
72
+ * @param {Object} [params]
73
+ * @param {boolean} [params.testFallbackEnabled]
74
+ * @param {?number} [params.epochDuration]
75
+ * @returns {Promise<SuiLocalTestValidator>}
46
76
  */
47
77
  static async launch(params = {}) {
48
78
  if (SuiLocalTestValidator.__instance) {
@@ -54,7 +84,8 @@ export default class SuiLocalTestValidator extends SuiCommonMethods {
54
84
  }
55
85
 
56
86
  /**
57
- * Stop the local sui node
87
+ * Stop the singleton local sui node if running.
88
+ * @returns {Promise<void>}
58
89
  */
59
90
  static async stop() {
60
91
  if (SuiLocalTestValidator.__instance) {
@@ -62,10 +93,20 @@ export default class SuiLocalTestValidator extends SuiCommonMethods {
62
93
  }
63
94
  }
64
95
 
96
+ /**
97
+ * @param {number} port
98
+ * @returns {Promise<boolean>} true if the port accepts a TCP connection within 3 seconds
99
+ */
65
100
  async isPortThere(port) {
66
101
  return await SuiCliCommands.isPortThere(port);
67
102
  }
68
103
 
104
+ /**
105
+ * Poll `isPortThere` every 500 ms until the port is open or the timeout elapses.
106
+ * @param {number} port
107
+ * @param {number} timeout - total wait time in ms
108
+ * @returns {Promise<boolean>}
109
+ */
69
110
  async waitForPort(port, timeout) {
70
111
  this.log('waiting for port', port);
71
112
  const startedCheckingAt = (new Date()).getTime();
@@ -82,9 +123,13 @@ export default class SuiLocalTestValidator extends SuiCommonMethods {
82
123
  }
83
124
 
84
125
  /**
85
- * Launch a local sui node and wait for it to become available to accept transactions.
86
- *
87
- * @returns {SuiLocalTestValidator}
126
+ * Start `sui start --with-faucet --with-indexer --with-graphql --force-regenesis`,
127
+ * wait for the faucet (port 9123) and GraphQL (port 9125) to come up,
128
+ * then return this instance with `active === true`.
129
+ *
130
+ * If `testFallbackEnabled` is set and the spawn fails, silently switches to devnet.
131
+ *
132
+ * @returns {Promise<SuiLocalTestValidator>}
88
133
  */
89
134
  async launch() {
90
135
  if (this._active) {
@@ -98,6 +143,8 @@ export default class SuiLocalTestValidator extends SuiCommonMethods {
98
143
  const params = [];
99
144
  params.push('start');
100
145
  params.push('--with-faucet');
146
+ params.push('--with-indexer');
147
+ params.push('--with-graphql');
101
148
  params.push('--force-regenesis');
102
149
  this._child = await SuiCliCommands.spawn('sui', params, { RUST_LOG: 'off,sui_node=info' });
103
150
  } catch (e) {
@@ -120,22 +167,37 @@ export default class SuiLocalTestValidator extends SuiCommonMethods {
120
167
  this.log(`child process exited with code ${code}`);
121
168
  });
122
169
 
170
+ // @ts-ignore — process is a Node.js global; not available in TypeScript without @types/node
123
171
  process.on('exit', ()=>{
124
172
  if (this._child) {
125
173
  this._child.kill();
126
174
  }
127
175
  });
176
+ // @ts-ignore
128
177
  const cleanExit = function() { process.exit() };
178
+ // @ts-ignore
129
179
  process.on('SIGINT', cleanExit); // catch ctrl-c
180
+ // @ts-ignore
130
181
  process.on('SIGTERM', cleanExit); // catch kill
131
182
 
132
183
  this._active = await this.waitForPort(9123, 30000);
133
184
 
185
+ if (this._active) {
186
+ const graphqlUp = await this.waitForPort(9125, 30000);
187
+ if (!graphqlUp) {
188
+ this.log('warning: GraphQL port 9125 did not come up in time');
189
+ }
190
+ }
191
+
134
192
  // await this.__readyLaunchedPromise;
135
193
 
136
194
  return this;
137
195
  }
138
196
 
197
+ /**
198
+ * Kill the child `sui start` process if it is running.
199
+ * @returns {Promise<void>}
200
+ */
139
201
  async stop() {
140
202
  if (this._child) {
141
203
  await this._child.kill();