suidouble 2.5.0 → 2.17.0-1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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 +161 -16
  10. package/lib/SuiInBrowserAdapter.js +192 -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 +195 -0
  44. package/types/lib/SuiInBrowserAdapter.d.ts +173 -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,55 @@ 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;
64
+ /** @type {?string} */
65
+ this._preferredChain = null;
32
66
  }
33
67
 
68
+ /**
69
+ * Sign and execute a transaction. Prefers the current `sui:signAndExecuteTransaction`
70
+ * feature; falls back to the legacy `sui:signAndExecuteTransactionBlock` for older wallets.
71
+ * @param {Object} params
72
+ * @returns {Promise<*>}
73
+ */
34
74
  async signAndExecuteTransaction(params) {
35
75
  if (this.hasFeature(Feature.SUI_SIGN_AND_EXECUTE_TX)) {
36
76
  return await this.getFeature(Feature.SUI_SIGN_AND_EXECUTE_TX).signAndExecuteTransaction(params);
@@ -41,10 +81,32 @@ export default class SuiInBrowserAdapter extends SuiCommonMethods {
41
81
  }
42
82
  }
43
83
 
84
+ /**
85
+ * Sign and execute a transaction using the legacy `sui:signAndExecuteTransactionBlock` feature.
86
+ * @param {Object} params
87
+ * @returns {Promise<*>}
88
+ */
44
89
  async signAndExecuteTransactionBlock(params) {
45
90
  return await this.getFeature(Feature.SUI_SIGN_AND_EXECUTE_TX_BLOCK).signAndExecuteTransactionBlock(params);
46
91
  }
47
92
 
93
+ /**
94
+ * Sign a transaction using the current `sui:signTransaction` feature.
95
+ * @param {Object} params
96
+ * @returns {Promise<SignatureWithBytes>}
97
+ */
98
+ async signTransaction(params) {
99
+ /** @type {SignatureWithBytes} */
100
+ const resp = await this.getFeature(Feature.SUI_SIGN_TX).signTransaction(params);
101
+ return resp;
102
+ }
103
+
104
+ /**
105
+ * Sign a transaction. Prefers the current `sui:signTransaction` feature;
106
+ * falls back to the legacy `sui:signTransactionBlock` for older wallets.
107
+ * @param {Object} params
108
+ * @returns {Promise<*>}
109
+ */
48
110
  async signTransactionBlock(params) {
49
111
  if (this.hasFeature(Feature.SUI_SIGN_TX)) {
50
112
  return await this.getFeature(Feature.SUI_SIGN_TX).signTransaction(params);
@@ -55,10 +117,12 @@ export default class SuiInBrowserAdapter extends SuiCommonMethods {
55
117
  }
56
118
  }
57
119
 
58
- async signTransactionBlock(params) {
59
- return await this.getFeature(Feature.SUI_SIGN_TX_BLOCK).signTransactionBlock(params);
60
- }
61
-
120
+ /**
121
+ * Sign a personal/arbitrary message. Prefers `sui:signPersonalMessage`;
122
+ * falls back to `sui:signMessage` for older wallets.
123
+ * @param {Object} params
124
+ * @returns {Promise<*>}
125
+ */
62
126
  async signPersonalMessage(params) {
63
127
  if (this.hasFeature(Feature.SUI_SIGN_PERSONAL_MESSAGE)) {
64
128
  return await this.getFeature(Feature.SUI_SIGN_PERSONAL_MESSAGE).signPersonalMessage(params);
@@ -67,16 +131,43 @@ export default class SuiInBrowserAdapter extends SuiCommonMethods {
67
131
  }
68
132
  }
69
133
 
134
+ /**
135
+ * Alias for `signPersonalMessage`.
136
+ * @param {Object} params
137
+ * @returns {Promise<*>}
138
+ */
70
139
  async signMessage(params) {
71
140
  return await this.signPersonalMessage(params);
72
141
  }
73
142
 
143
+ /**
144
+ * Disconnect from the wallet and refresh connection state.
145
+ * Force-clears local connection state even if the wallet doesn't support
146
+ * standard:disconnect or doesn't revoke site authorization (e.g. Phantom).
147
+ * @param {Object} [params]
148
+ * @returns {Promise<void>}
149
+ */
74
150
  async disconnect(params) {
75
- const res = await this.getFeature(Feature.DISCONNECT).disconnect(params);
76
- this.connectionUpdated();
77
- return res;
151
+ try {
152
+ const feature = this.getFeature(Feature.DISCONNECT);
153
+ if (feature) {
154
+ await feature.disconnect(params);
155
+ }
156
+ } catch (e) {
157
+ console.warn('[SuiInBrowserAdapter] standard:disconnect failed:', e);
158
+ }
159
+
160
+ this._connectedAddress = null;
161
+ this._connectedChain = null;
162
+ this._isConnected = false;
163
+ this._userDisconnected = true;
164
+ this.emit('disconnected', this);
78
165
  }
79
166
 
167
+ /**
168
+ * Return the Chrome Web Store download URL, or null if not configured.
169
+ * @returns {?string}
170
+ */
80
171
  getDownloadURL() {
81
172
  if (this._downloadUrls && this._downloadUrls.chrome) {
82
173
  return this._downloadUrls.chrome;
@@ -84,26 +175,36 @@ export default class SuiInBrowserAdapter extends SuiCommonMethods {
84
175
  return null;
85
176
  }
86
177
 
178
+ /**
179
+ * True when no underlying adapter has been attached (i.e. this is a "not installed" placeholder).
180
+ * @returns {boolean}
181
+ */
87
182
  get isDefault() {
88
- if (!this._standartAdapter) {
89
- return true;
90
- }
91
- return false;
183
+ return !this._standardAdapter;
92
184
  }
93
185
 
186
+ /** @returns {?string} currently connected Sui address, or null */
94
187
  get connectedAddress() {
95
188
  return this._connectedAddress;
96
189
  }
97
190
 
191
+ /** @returns {?string} currently connected chain identifier (e.g. `'sui:mainnet'`), or null */
98
192
  get connectedChain() {
99
193
  return this._connectedChain;
100
194
  }
101
195
 
196
+ /** @returns {boolean} true if the wallet is connected and an address is available */
102
197
  get isConnected() {
103
198
  return this._isConnected;
104
199
  }
105
200
 
201
+ /**
202
+ * Request connection to the wallet. Errors are swallowed; connection state is refreshed
203
+ * via `connectionUpdated` regardless.
204
+ * @returns {Promise<void>}
205
+ */
106
206
  async connect() {
207
+ this._userDisconnected = false;
107
208
  try {
108
209
  await this.getFeature(Feature.CONNECT).connect();
109
210
  } catch (e) {
@@ -113,14 +214,26 @@ export default class SuiInBrowserAdapter extends SuiCommonMethods {
113
214
  this.connectionUpdated();
114
215
  }
115
216
 
217
+ /**
218
+ * Read the current connection state from the underlying adapter and update
219
+ * `connectedAddress`, `connectedChain`, and `isConnected`. Emits `'connected'` or
220
+ * `'disconnected'` if the state changed.
221
+ */
116
222
  connectionUpdated() {
223
+ if (this._userDisconnected) return;
224
+
117
225
  const wasConnectedAddress = ''+this._connectedAddress;
118
226
  const wasConnectedChain = ''+this._connectedChain;
119
227
 
120
228
  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];
229
+ if (this._standardAdapter && this._standardAdapter.accounts && this._standardAdapter.accounts.length) {
230
+ this._connectedAddress = this._standardAdapter.accounts[0].address;
231
+ const accountChains = this._standardAdapter.accounts[0].chains;
232
+ if (this._preferredChain && accountChains.includes(this._preferredChain)) {
233
+ this._connectedChain = this._preferredChain;
234
+ } else {
235
+ this._connectedChain = accountChains[0];
236
+ }
124
237
  } else {
125
238
  this._connectedAddress = null;
126
239
  this._connectedChain = null;
@@ -141,16 +254,23 @@ export default class SuiInBrowserAdapter extends SuiCommonMethods {
141
254
  }
142
255
  }
143
256
 
144
-
145
- setStandartAdapter(standartAdapter) {
146
- if (this._standartAdapter) {
257
+ /**
258
+ * Attach a Wallet Standard adapter to this instance. No-op if already attached.
259
+ * Returns false and does nothing if the adapter has no `sui:` features.
260
+ * Subscribes to adapter `'change'` events to keep connection state up to date.
261
+ *
262
+ * @param {Object} standardAdapter - raw adapter from `@wallet-standard/core`
263
+ * @returns {boolean} false if the adapter lacks Sui features, undefined on success
264
+ */
265
+ setStandartAdapter(standardAdapter) {
266
+ if (this._standardAdapter) {
147
267
  // no need to re-attach
148
268
  return true;
149
269
  }
150
270
 
151
271
  // check if it has sui: features
152
272
  let hasSuiFeatures = false;
153
- for (const key in standartAdapter.features) {
273
+ for (const key in standardAdapter.features) {
154
274
  if ((''+key).indexOf('sui:') === 0) {
155
275
  hasSuiFeatures = true;
156
276
  }
@@ -160,65 +280,97 @@ export default class SuiInBrowserAdapter extends SuiCommonMethods {
160
280
  return false;
161
281
  }
162
282
 
163
- this._standartAdapter = standartAdapter;
164
- if (!this.__standartAdapterChangeListener) {
165
- this.__standartAdapterChangeListener = (e) => {
283
+ this._standardAdapter = standardAdapter;
284
+ if (!this.__standardAdapterChangeListener) {
285
+ this.__standardAdapterChangeListener = () => {
166
286
  this.connectionUpdated();
167
287
  };
168
288
  }
169
- this.getFeature(Feature.EVENTS).on('change', this.__standartAdapterChangeListener);
289
+ this.getFeature(Feature.EVENTS).on('change', this.__standardAdapterChangeListener);
170
290
 
171
291
  this.connectionUpdated();
172
292
  }
173
293
 
294
+ /**
295
+ * True if the adapter is installed and supports the minimum required Sui features
296
+ * (`sui:signAndExecuteTransaction` or `sui:signAndExecuteTransactionBlock`, plus `standard:events`).
297
+ * @returns {boolean}
298
+ */
174
299
  get okForSui() {
175
300
  if (!this.isInstalled) {
176
301
  return false;
177
302
  }
178
303
 
179
- return this.hasFeature(Feature.SUI_SIGN_AND_EXECUTE_TX_BLOCK) && this.hasFeature(Feature.EVENTS);
304
+ return (this.hasFeature(Feature.SUI_SIGN_AND_EXECUTE_TX_BLOCK) || this.hasFeature(Feature.SUI_SIGN_AND_EXECUTE_TX)) && this.hasFeature(Feature.EVENTS);
180
305
  }
181
306
 
307
+ /** @returns {boolean} true if a Wallet Standard adapter has been attached */
182
308
  get isInstalled() {
183
- if (this._standartAdapter) {
184
- return true;
185
- }
186
- return false;
309
+ return !!this._standardAdapter;
187
310
  }
188
311
 
312
+ /**
313
+ * Feature map from the underlying adapter, or an empty object if not installed.
314
+ * Keys are Wallet Standard feature strings (e.g. `'sui:signAndExecuteTransaction'`).
315
+ * @returns {Object}
316
+ */
189
317
  get features() {
190
- if (this._standartAdapter) {
191
- return this._standartAdapter.features;
318
+ if (this._standardAdapter) {
319
+ return this._standardAdapter.features;
192
320
  }
193
321
  return {};
194
322
  }
195
323
 
324
+ /**
325
+ * Display name — from the adapter if installed, otherwise from the constructor param.
326
+ * @returns {?string}
327
+ */
196
328
  get name() {
197
- if (this._standartAdapter) {
198
- return this._standartAdapter.name;
329
+ if (this._standardAdapter) {
330
+ return this._standardAdapter.name;
199
331
  } else {
200
332
  return this._name;
201
333
  }
202
334
  }
203
335
 
336
+ /**
337
+ * Data-URI icon — from the adapter if installed, otherwise from the constructor param.
338
+ * @returns {?string}
339
+ */
204
340
  get icon() {
205
- if (this._standartAdapter) {
206
- return this._standartAdapter.icon;
341
+ if (this._standardAdapter) {
342
+ return this._standardAdapter.icon;
207
343
  } else {
208
344
  return this._icon;
209
345
  }
210
346
  }
211
347
 
348
+ /**
349
+ * Wallet version string from the adapter, or undefined if not installed.
350
+ * @returns {?string}
351
+ */
212
352
  get version() {
213
- if (this._standartAdapter) {
214
- return this._standartAdapter.version;
353
+ if (this._standardAdapter) {
354
+ return this._standardAdapter.version;
215
355
  }
216
356
  }
217
357
 
358
+ /**
359
+ * Returns true if this adapter supports the given feature.
360
+ * Accepts either a `Feature` enum key (e.g. `'SUI_SIGN_TX'`) or a raw feature string.
361
+ * @param {string} featureName
362
+ * @returns {boolean}
363
+ */
218
364
  hasFeature(featureName) {
219
365
  return (!!this.getFeature(featureName));
220
366
  }
221
367
 
368
+ /**
369
+ * Return the feature object for `featureName`, or null if not available.
370
+ * Accepts either a `Feature` enum key (e.g. `'SUI_SIGN_TX'`) or a raw feature string.
371
+ * @param {string} featureName
372
+ * @returns {?Object}
373
+ */
222
374
  getFeature(featureName) {
223
375
  const features = this.features;
224
376
 
@@ -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();