Skip to content

Commit 48588b2

Browse files
committed
chore: update sc provider
1 parent b132f64 commit 48588b2

File tree

4 files changed

+129
-165
lines changed

4 files changed

+129
-165
lines changed

packages/rpc-provider/package.json

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,14 @@
3535
"tslib": "^2.8.1"
3636
},
3737
"devDependencies": {
38-
"@substrate/connect": "0.8.11"
38+
"@substrate/connect": "^2.1.2"
3939
},
40-
"optionalDependencies": {
41-
"@substrate/connect": "0.8.11"
40+
"peerDependencies": {
41+
"@substrate/connect": "^2.1.2"
42+
},
43+
"peerDependenciesMeta": {
44+
"@substrate/connect": {
45+
"optional": true
46+
}
4247
}
4348
}

packages/rpc-provider/src/substrate-connect/index.spec.ts

Lines changed: 68 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ const wait = (ms: number) =>
4141
setTimeout(resolve, ms)
4242
);
4343

44-
function healthCheckerMock (): MockedHealthChecker {
44+
function healthCheckerMock(): MockedHealthChecker {
4545
let cb: (health: SmoldotHealth) => void = () => undefined;
4646
let sendJsonRpc: (request: string) => void = () => undefined;
4747
let isActive = false;
@@ -66,7 +66,7 @@ function healthCheckerMock (): MockedHealthChecker {
6666
};
6767
}
6868

69-
function healthCheckerFactory () {
69+
function healthCheckerFactory() {
7070
const _healthCheckers: MockedHealthChecker[] = [];
7171

7272
return {
@@ -82,13 +82,33 @@ function healthCheckerFactory () {
8282
};
8383
}
8484

85-
function getFakeChain (spec: string, callback: Sc.JsonRpcCallback): MockChain {
85+
function getFakeChain(spec: string): MockChain {
8686
const _receivedRequests: string[] = [];
8787
let _isTerminated = false;
8888

8989
let terminateInterceptor = Function.prototype;
9090
let sendJsonRpcInterceptor = Function.prototype;
9191

92+
const responseQueue: string[] = []
93+
94+
const nextJsonRpcResponse = async () => {
95+
if (responseQueue.length === 0) {
96+
await new Promise((resolve) => setTimeout(resolve, 0));
97+
return nextJsonRpcResponse();
98+
}
99+
100+
return responseQueue.shift()!;
101+
}
102+
103+
async function* jsonRpcResponsesGenerator(): AsyncIterableIterator<string> {
104+
while (true) {
105+
const response = await nextJsonRpcResponse();
106+
if (response) {
107+
yield response;
108+
}
109+
}
110+
}
111+
92112
return {
93113
_getLatestRequest: () => _receivedRequests[_receivedRequests.length - 1],
94114
_isTerminated: () => _isTerminated,
@@ -101,14 +121,15 @@ function getFakeChain (spec: string, callback: Sc.JsonRpcCallback): MockChain {
101121
},
102122
_spec: () => spec,
103123
_triggerCallback: (response) => {
104-
callback(
105-
typeof response === 'string'
106-
? response
107-
: stringify(response)
108-
);
124+
const message = typeof response === 'string'
125+
? response
126+
: stringify(response)
127+
responseQueue.push(message)
128+
109129
},
110-
addChain: (chainSpec, jsonRpcCallback) =>
111-
Promise.resolve(getFakeChain(chainSpec, jsonRpcCallback ?? noop)),
130+
nextJsonRpcResponse,
131+
jsonRpcResponses: jsonRpcResponsesGenerator(),
132+
addChain: (chainSpec) => Promise.resolve(getFakeChain(chainSpec)),
112133
remove: () => {
113134
terminateInterceptor();
114135
_isTerminated = true;
@@ -120,11 +141,27 @@ function getFakeChain (spec: string, callback: Sc.JsonRpcCallback): MockChain {
120141
};
121142
}
122143

123-
function getFakeClient () {
144+
function getFakeClient() {
124145
const chains: MockChain[] = [];
125146
let addChainInterceptor: Promise<void> = Promise.resolve();
126147
let addWellKnownChainInterceptor: Promise<void> = Promise.resolve();
127148

149+
const addChain: Sc.AddChain = async (chainSpec) => addChainInterceptor.then(() => {
150+
const result = getFakeChain(chainSpec);
151+
152+
chains.push(result);
153+
154+
return result;
155+
})
156+
157+
const addWellKnownChain: Sc.AddWellKnownChain = async (wellKnownChain) => addWellKnownChainInterceptor.then(() => {
158+
const result = getFakeChain(wellKnownChain);
159+
160+
chains.push(result);
161+
162+
return result;
163+
})
164+
128165
return {
129166
_chains: () => chains,
130167
_setAddChainInterceptor: (interceptor: Promise<void>) => {
@@ -133,29 +170,12 @@ function getFakeClient () {
133170
_setAddWellKnownChainInterceptor: (interceptor: Promise<void>) => {
134171
addWellKnownChainInterceptor = interceptor;
135172
},
136-
addChain: (chainSpec: string, cb: Sc.JsonRpcCallback): Promise<MockChain> =>
137-
addChainInterceptor.then(() => {
138-
const result = getFakeChain(chainSpec, cb);
139-
140-
chains.push(result);
141-
142-
return result;
143-
}),
144-
addWellKnownChain: (
145-
wellKnownChain: string,
146-
cb: Sc.JsonRpcCallback
147-
): Promise<MockChain> =>
148-
addWellKnownChainInterceptor.then(() => {
149-
const result = getFakeChain(wellKnownChain, cb);
150-
151-
chains.push(result);
152-
153-
return result;
154-
})
173+
addChain,
174+
addWellKnownChain
155175
};
156176
}
157177

158-
function connectorFactory (): MockSc {
178+
function connectorFactory(): MockSc {
159179
const clients: ReturnType<typeof getFakeClient>[] = [];
160180
const latestClient = () => clients[clients.length - 1];
161181

@@ -175,7 +195,7 @@ function connectorFactory (): MockSc {
175195
} as unknown as MockSc;
176196
}
177197

178-
function setChainSyncyingStatus (isSyncing: boolean): void {
198+
function setChainSyncyingStatus(isSyncing: boolean): void {
179199
getCurrentHealthChecker()._triggerHealthUpdate({
180200
isSyncing,
181201
peers: 1,
@@ -206,7 +226,7 @@ describe('ScProvider', () => {
206226
expect(onConnected).not.toHaveBeenCalled();
207227
setChainSyncyingStatus(false);
208228
expect(onConnected).toHaveBeenCalled();
209-
});
229+
}, 5000);
210230

211231
it('stops receiving notifications after unsubscribing', async () => {
212232
const provider = new ScProvider(mockSc, '');
@@ -220,7 +240,7 @@ describe('ScProvider', () => {
220240

221241
setChainSyncyingStatus(false);
222242
expect(onConnected).not.toHaveBeenCalled();
223-
});
243+
}, 5000);
224244

225245
it('synchronously emits connected if the Provider is already `connected`', async () => {
226246
const provider = new ScProvider(mockSc, '');
@@ -233,7 +253,7 @@ describe('ScProvider', () => {
233253

234254
provider.on('connected', onConnected);
235255
expect(onConnected).toHaveBeenCalled();
236-
});
256+
}, 5000);
237257

238258
it('emits `disconnected` once the chain goes back to syncing', async () => {
239259
const provider = new ScProvider(mockSc, '');
@@ -256,7 +276,7 @@ describe('ScProvider', () => {
256276

257277
expect(onConnected).not.toHaveBeenCalled();
258278
expect(onDisconnected).toHaveBeenCalled();
259-
});
279+
}, 5000);
260280
});
261281

262282
describe('hasSubscriptions', () => {
@@ -288,7 +308,7 @@ describe('ScProvider', () => {
288308

289309
await provider.connect(undefined, mockedHealthChecker.healthChecker);
290310
expect(chain).toBe(mockSc.latestChain());
291-
});
311+
}, 5000);
292312

293313
it('throws when trying to connect on an already connected Provider', async () => {
294314
const provider = new ScProvider(mockSc, '');
@@ -300,7 +320,7 @@ describe('ScProvider', () => {
300320
await expect(
301321
provider.connect(undefined, mockedHealthChecker.healthChecker)
302322
).rejects.toThrow(/Already connected/);
303-
});
323+
}, 5000);
304324
});
305325

306326
describe('disconnect', () => {
@@ -313,7 +333,7 @@ describe('ScProvider', () => {
313333
await provider.disconnect();
314334

315335
expect(chain._isTerminated()).toBe(true);
316-
});
336+
}, 5000);
317337

318338
// eslint-disable-next-line jest/expect-expect
319339
it('does not throw when disconnecting on an already disconnected Provider', async () => {
@@ -360,7 +380,7 @@ describe('ScProvider', () => {
360380
const response = await responsePromise;
361381

362382
expect(response).toEqual(result);
363-
});
383+
}, 5000);
364384

365385
it("rejects when the response can't be deserialized", async () => {
366386
const provider = new ScProvider(mockSc, '');
@@ -378,7 +398,7 @@ describe('ScProvider', () => {
378398
}, 0);
379399

380400
await expect(provider.send('getData', ['foo'])).rejects.toThrow();
381-
});
401+
}, 5000);
382402

383403
it('rejects when the smoldot chain has crashed', async () => {
384404
const provider = new ScProvider(mockSc, '');
@@ -397,7 +417,7 @@ describe('ScProvider', () => {
397417
provider.send('getData', ['foo'])
398418
).rejects.toThrow(/Disconnected/);
399419
expect(provider.isConnected).toBe(false);
400-
});
420+
}, 5000);
401421
});
402422

403423
describe('subscribe', () => {
@@ -466,7 +486,7 @@ describe('ScProvider', () => {
466486
});
467487
expect(cb).toHaveBeenCalledTimes(2);
468488
expect(cb).toHaveBeenLastCalledWith(null, 2);
469-
});
489+
}, 5000);
470490

471491
it('ignores subscription messages that were received before the subscription token', async () => {
472492
const provider = new ScProvider(mockSc, '');
@@ -504,7 +524,7 @@ describe('ScProvider', () => {
504524

505525
expect(token).toBe(unsubscribeToken);
506526
expect(cb).not.toHaveBeenCalled();
507-
});
527+
}, 5000);
508528

509529
it('emits the error when the message has an error', async () => {
510530
const provider = new ScProvider(mockSc, '');
@@ -545,7 +565,7 @@ describe('ScProvider', () => {
545565
expect(token).toBe(unsubscribeToken);
546566
expect(cb).toHaveBeenCalledTimes(1);
547567
expect(cb).toHaveBeenLastCalledWith(expect.any(Error), undefined);
548-
});
568+
}, 5000);
549569

550570
it('errors when subscribing to an unsupported method', async () => {
551571
const provider = new ScProvider(mockSc, '');
@@ -558,7 +578,7 @@ describe('ScProvider', () => {
558578
await expect(
559579
provider.subscribe('foo', 'bar', ['baz'], () => undefined)
560580
).rejects.toThrow(/Unsupported subscribe method: bar/);
561-
});
581+
}, 5000);
562582
});
563583

564584
describe('unsubscribe', () => {
@@ -572,7 +592,7 @@ describe('ScProvider', () => {
572592
await expect(
573593
provider.unsubscribe('', '', '')
574594
).rejects.toThrow(/Unable to find active subscription/);
575-
});
595+
}, 5000);
576596
});
577597

578598
it('cleans up the stale subscriptions once it reconnects', async () => {
@@ -634,5 +654,5 @@ describe('ScProvider', () => {
634654
`{"id":2,"jsonrpc":"2.0","method":"chain_unsubscribeNewHeads","params":["${token}"]}`,
635655
'{"id":3,"jsonrpc":"2.0","method":"chain_subscribeNewHeads","params":["baz"]}'
636656
]);
637-
});
657+
}, 5000);
638658
});

packages/rpc-provider/src/substrate-connect/index.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ export class ScProvider implements ProviderInterface {
6464
#chain: Promise<ScType.Chain> | null = null;
6565
#isChainReady = false;
6666

67-
public constructor (Sc: SubstrateConnect, spec: string | ScType.WellKnownChain, sharedSandbox?: ScProvider) {
67+
public constructor (Sc: SubstrateConnect, spec: ScType.WellKnownChain | (string & {}), sharedSandbox?: ScProvider) {
6868
if (!isObject(Sc) || !isObject(Sc.WellKnownChain) || !isFunction(Sc.createScClient)) {
6969
throw new Error('Expected an @substrate/connect interface as first parameter to ScProvider');
7070
}
@@ -166,7 +166,19 @@ export class ScProvider implements ProviderInterface {
166166
? client.addWellKnownChain
167167
: client.addChain;
168168

169-
this.#chain = addChain(this.#spec as ScType.WellKnownChain, onResponse).then((chain) => {
169+
this.#chain = addChain(this.#spec as ScType.WellKnownChain).then((chain) => {
170+
// Process JSON-RPC responses
171+
void (async () => {
172+
try {
173+
for await (const response of chain.jsonRpcResponses) {
174+
onResponse(response);
175+
}
176+
} catch (error) {
177+
l.error('Error processing JSON-RPC responses:', error);
178+
this.#eventemitter.emit('error', error);
179+
}
180+
})();
181+
170182
hc.setSendJsonRpc(chain.sendJsonRpc);
171183

172184
this.#isChainReady = false;

0 commit comments

Comments
 (0)