Skip to content

Commit e2a5cef

Browse files
authored
fix!: make smoldot discovery interface conform to smoldot interface (#2516)
1 parent d183f3d commit e2a5cef

File tree

13 files changed

+270
-202
lines changed

13 files changed

+270
-202
lines changed

.changeset/friendly-toys-remember.md

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
---
2+
"@substrate/connect": major
3+
"@substrate/smoldot-discovery": major
4+
"@substrate/smoldot-discovery-connector": minor
5+
---
6+
7+
## Breaking Changes
8+
9+
- Modified `addChain` and `addWellKnownChain` methods:
10+
- Now accept a single `options` object parameter instead of separate `jsonRpcCallback` and `databaseContent` parameters
11+
- The `jsonRpcCallback` is now passed as `options.jsonRpcCallback`
12+
- The `databaseContent` is now passed as `options.databaseContent`
13+
14+
- Removed `JsonRpcCallback` type export. Use the callback type from the `options` parameter of `addChain` and `addWellKnownChain` instead.
15+
16+
- Updated peer dependency for `@substrate/smoldot-discovery` to "^3"
17+
18+
## New Features
19+
20+
- Added new methods to the Chain interface to conform with smoldot's interface:
21+
- `nextJsonRpcResponse`: Returns a promise that resolves with the next JSON-RPC response
22+
- `jsonRpcResponses`: Returns an async iterable of JSON-RPC responses
23+
24+
## Other Changes
25+
26+
- Updated internal implementation to use Effect for streaming JSON RPC responses in a Queue.
27+
- Updated error handling to include `QueueFullError`.
28+
29+
## Migration Guide
30+
31+
Users of this package will need to update their code to use the new method signatures for `addChain` and `addWellKnownChain`, and adapt to the removed `JsonRpcCallback` type export. Please refer to the updated documentation for the new usage patterns.
32+
33+
When upgrading, ensure you're using version 3 or higher of `@substrate/smoldot-discovery` as a peer dependency.

.eslintrc.cjs

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ module.exports = {
1414
devDependencies: ["**/*.test.ts", "**/*.spec.ts", "**/*.bench.ts"],
1515
},
1616
],
17+
"@typescript-eslint/no-redeclare": "off",
1718
},
1819
env: {
1920
browser: true,

packages/connect/src/connector/index.ts

+4-12
Original file line numberDiff line numberDiff line change
@@ -53,19 +53,11 @@ export const createScClient = (config?: Config): ScClient => {
5353
: smoldotScClient(config?.embeddedNodeConfig)
5454

5555
return {
56-
async addChain(chainSpec, jsonRpcCallback, databaseContent) {
57-
return (await client).addChain(
58-
chainSpec,
59-
jsonRpcCallback,
60-
databaseContent,
61-
)
56+
async addChain(chainSpec, options) {
57+
return (await client).addChain(chainSpec, options)
6258
},
63-
async addWellKnownChain(id, jsonRpcCallback, databaseContent) {
64-
return (await client).addWellKnownChain(
65-
id,
66-
jsonRpcCallback,
67-
databaseContent,
68-
)
59+
async addWellKnownChain(id, options) {
60+
return (await client).addWellKnownChain(id, options)
6961
},
7062
}
7163
}

packages/connect/src/connector/smoldot-light.ts

+23-73
Original file line numberDiff line numberDiff line change
@@ -12,20 +12,21 @@ import {
1212
AlreadyDestroyedError,
1313
CrashError,
1414
JsonRpcDisabledError,
15-
type JsonRpcCallback,
1615
type AddChain,
1716
WellKnownChain,
17+
QueueFullError,
1818
} from "./types.js"
19+
import type { AddChainOptions } from "@substrate/smoldot-discovery/types"
1920

2021
const isBrowser = ![typeof window, typeof document].includes("undefined")
2122

22-
let QueueFullError = class {}
23+
let _QueueFullError = class {}
2324

2425
let startPromise: Promise<(options: ClientOptions) => Client> | null = null
2526
const getStart = () => {
2627
if (startPromise) return startPromise
2728
startPromise = import("smoldot").then((sm) => {
28-
QueueFullError = sm.QueueFullError
29+
_QueueFullError = sm.QueueFullError
2930
return sm.start
3031
})
3132
return startPromise
@@ -151,6 +152,7 @@ const transformErrors = (thunk: () => void) => {
151152
if (error?.name === "CrashError") throw new CrashError(error.message)
152153
if (error?.name === "AlreadyDestroyedError")
153154
throw new AlreadyDestroyedError()
155+
if (error instanceof _QueueFullError) throw new QueueFullError()
154156
throw new CrashError(
155157
e instanceof Error ? e.message : `Unexpected error ${e}`,
156158
)
@@ -197,70 +199,26 @@ export const createScClient = (config?: Config): ScClient => {
197199

198200
const internalAddChain = async (
199201
chainSpec: string,
200-
jsonRpcCallback?: (msg: string) => void,
201-
databaseContent?: string,
202-
relayChain?: SChain,
202+
options?: AddChainOptions & { relayChain?: SChain },
203203
): Promise<Chain> => {
204204
const client = await getClientAndIncRef(configOrDefault)
205205

206206
try {
207207
const internalChain = await client.addChain({
208208
chainSpec,
209-
potentialRelayChains: relayChain ? [relayChain] : undefined,
210-
disableJsonRpc: jsonRpcCallback === undefined,
211-
databaseContent,
209+
potentialRelayChains: options?.relayChain
210+
? [options.relayChain]
211+
: undefined,
212+
disableJsonRpc: options?.disableJsonRpc,
213+
databaseContent: options?.databaseContent,
212214
})
213215

214-
;(async () => {
215-
while (true) {
216-
let jsonRpcResponse
217-
try {
218-
jsonRpcResponse = await internalChain.nextJsonRpcResponse()
219-
} catch (_) {
220-
break
221-
}
222-
223-
// `nextJsonRpcResponse` throws an exception if we pass `disableJsonRpc: true` in the
224-
// config. We pass `disableJsonRpc: true` if `jsonRpcCallback` is undefined. Therefore,
225-
// this code is never reachable if `jsonRpcCallback` is undefined.
226-
try {
227-
jsonRpcCallback!(jsonRpcResponse)
228-
} catch (error) {
229-
console.error("JSON-RPC callback has thrown an exception:", error)
230-
}
231-
}
232-
})()
233-
234216
return {
235217
sendJsonRpc: (rpc) => {
236-
transformErrors(() => {
237-
try {
238-
internalChain.sendJsonRpc(rpc)
239-
} catch (error) {
240-
if (error instanceof QueueFullError) {
241-
// If the queue is full, we immediately send back a JSON-RPC response indicating
242-
// the error.
243-
try {
244-
const parsedRq = JSON.parse(rpc)
245-
jsonRpcCallback!(
246-
JSON.stringify({
247-
jsonrpc: "v2",
248-
id: parsedRq.id,
249-
error: {
250-
code: -32000,
251-
message: "JSON-RPC server is too busy",
252-
},
253-
}),
254-
)
255-
} catch (_error) {
256-
// An error here counts as a malformed JSON-RPC request, which are ignored.
257-
}
258-
} else {
259-
throw error
260-
}
261-
}
262-
})
218+
transformErrors(() => internalChain.sendJsonRpc(rpc))
263219
},
220+
nextJsonRpcResponse: () => internalChain.nextJsonRpcResponse(),
221+
jsonRpcResponses: internalChain.jsonRpcResponses,
264222
remove: () => {
265223
try {
266224
transformErrors(() => {
@@ -272,15 +230,12 @@ export const createScClient = (config?: Config): ScClient => {
272230
},
273231
addChain: (
274232
chainSpec: string,
275-
jsonRpcCallback?: JsonRpcCallback | undefined,
276-
databaseContent?: string | undefined,
233+
options?: AddChainOptions,
277234
): Promise<Chain> => {
278-
return internalAddChain(
279-
chainSpec,
280-
jsonRpcCallback,
281-
databaseContent,
282-
internalChain,
283-
)
235+
return internalAddChain(chainSpec, {
236+
...options,
237+
relayChain: internalChain,
238+
})
284239
},
285240
}
286241
} catch (error) {
@@ -289,25 +244,20 @@ export const createScClient = (config?: Config): ScClient => {
289244
}
290245
}
291246

292-
const addChain: AddChain = (chainSpec, jsonRpcCallback, databaseContent) =>
293-
internalAddChain(chainSpec, jsonRpcCallback, databaseContent)
247+
const addChain: AddChain = (chainSpec, options) =>
248+
internalAddChain(chainSpec, options)
294249

295250
const addWellKnownChain: AddWellKnownChain = async (
296251
supposedChain: WellKnownChain,
297-
jsonRpcCallback?: (msg: string) => void,
298-
databaseContent?: string,
252+
options,
299253
): Promise<Chain> => {
300254
// the following line ensures that the http request for the dynamic import
301255
// of smoldot and the request for the dynamic import of the spec
302256
// happen in parallel
303257
getClientAndIncRef(configOrDefault)
304258

305259
try {
306-
return await internalAddChain(
307-
await getSpec(supposedChain),
308-
jsonRpcCallback,
309-
databaseContent,
310-
)
260+
return await internalAddChain(await getSpec(supposedChain), options)
311261
} finally {
312262
decRef(configOrDefault)
313263
}

packages/connect/src/connector/types.ts

+5-6
Original file line numberDiff line numberDiff line change
@@ -74,10 +74,9 @@ export {
7474
AlreadyDestroyedError,
7575
CrashError,
7676
JsonRpcDisabledError,
77-
} from "@substrate/smoldot-discovery/types"
78-
export type {
79-
Chain,
80-
JsonRpcCallback,
81-
AddChain,
82-
AddWellKnownChain,
77+
QueueFullError,
78+
type Chain,
79+
type AddChain,
80+
type AddWellKnownChain,
81+
type AddChainOptions,
8382
} from "@substrate/smoldot-discovery/types"

packages/connect/src/index.ts

-7
Original file line numberDiff line numberDiff line change
@@ -95,11 +95,4 @@
9595
* @packageDocumentation
9696
*/
9797

98-
export { WellKnownChain } from "@substrate/smoldot-discovery/types"
9998
export * from "./connector/index.js"
100-
export type {
101-
JsonRpcCallback,
102-
Chain,
103-
AddChain,
104-
AddWellKnownChain,
105-
} from "./connector/index.js"

packages/smoldot-discovery-connector/package.json

+4-3
Original file line numberDiff line numberDiff line change
@@ -52,14 +52,15 @@
5252
"trailingComma": "all"
5353
},
5454
"dependencies": {
55-
"@substrate/light-client-extension-helpers": "workspace:^"
55+
"@substrate/light-client-extension-helpers": "workspace:^",
56+
"effect": "^3.10.0"
5657
},
5758
"devDependencies": {
58-
"typescript": "catalog:",
5959
"@substrate/smoldot-discovery": "workspace:^",
60+
"typescript": "catalog:",
6061
"vitest": "^2.0.5"
6162
},
6263
"peerDependencies": {
63-
"@substrate/smoldot-discovery": ">=1 && <2"
64+
"@substrate/smoldot-discovery": "^3"
6465
}
6566
}

0 commit comments

Comments
 (0)