Conversation
e37aec0 to
f2ab5b9
Compare
f2ab5b9 to
6a95834
Compare
Gozala
left a comment
There was a problem hiding this comment.
@vasco-santos I did a pass here, but I think I have a more general feedback that is not pinned to any specific line:
aegir bulidwill generate types indistso no.d.tsfiles are necessary (I did pushed a commit here that removes them, but feel free to revert it if you disagree).- types in
distneed to be mapped in package.json (I have created #75 that does it). - type check / build is failing with this tree (I have created #75 that addresses remaining issues)
- I think we need to distinguish between interfaces (
interface { ...}) and classes that implement those. And users of those implementations should code against interfaces not implementation classes. That enables anyone to do a different implementation of the interface, ensure with type checker compatibility and use it as drop-in replacement. - I think
declare class-es must go, if they are necessary they should be interfaces. - There are some incompatibilities regarding
MuxedStreamandDuplexIterableStreams (I had to suppress some mismatches in #75). I think we should resolve those. I don't have a complete enough understanding in this area to provide specific suggestions, but would love to have session so I can better understand it and also help resolve this. - There are bunch of places where instance fields are
null|Tbut in code used asT(I have suppressed those with@ts-ignorein #75, but I think we need a followup pull to resolve them)
|
Thanks for the reviews. On @Gozala feedback:
Yes, as mentioned on the PR desc, I kept them to test it with libp2p/js-libp2p#802
I added them to go around the type checker error of the type not being constructable. Anyway, this did not fix the issue, so I will remove them.
Yeah, I think they should all be MuxedStream, if you are talking about Pubsub. I will change them
I will have a look on these |
6f53c32 to
8fdc05d
Compare
30fe22d to
0801fc3
Compare
Gozala
left a comment
There was a problem hiding this comment.
Add few nits, but looks good to me to go with or without them.
|
|
||
| /** | ||
| * @typedef {Object} InMessage | ||
| * @property {string} [from] |
There was a problem hiding this comment.
Following code seems to suggest that from isn't optional here
https://github.com/libp2p/js-libp2p-interfaces/pull/74/files#diff-2398b30d2245a825770bfde7b95fd0b65f3ce6435c3f8cd87a632f583a5b2addR66
If so can we turn it into non optional so type checker can report the error instead of leaking it into runtime ?
There was a problem hiding this comment.
It is really dependent on the signature policy. Have a look into the validate function. It will be undefined or should exist according to that policy being used.
The same for other places like the verifySignature function, which validate only calls if it has a signature. I will add an error there to avoid potential future usages of the function though
| async attachOutboundStream (stream) { | ||
| // If an outbound stream already exists, | ||
| // gently close it | ||
| const _prevStream = this.outboundStream |
There was a problem hiding this comment.
In that case this line is no longer needed
| const _prevStream = this.outboundStream |
There was a problem hiding this comment.
We need it for the check in the end of the function to only emit stream:outbound if the connection is new. We should only emit it when the outbound stream is ready, so this should be in the end, which means we need that auxiliary constant
src/record/index.js
Outdated
| @@ -7,25 +7,31 @@ const errcode = require('err-code') | |||
| */ | |||
| class Record { | |||
There was a problem hiding this comment.
If thing does not need to be a class it's best to define it as interface / type because it could be substituted with other implementation or even just plain object that conforms the interface. Classes should really just be a one possible implementation of it. Current version implies that TS will error anywhere record instanceof Record isn't true.
| * Checks if the given value is a `MulticodecTopology` instance. | ||
| * | ||
| * @param {any} other | ||
| * @returns {other is MulticodecTopology} |
There was a problem hiding this comment.
Seeing the Either way it's just a suggestion and it's your decision to make. |
|
Ok if I take the current typedefs for transports and try to implement it like this: type TCPDial = { signal?: AbortSignal }
export class TCPTransport implements Transport<TCPDial> {
// ^^ Class 'TCPTransport' incorrectly implements interface 'Transport<TCPDial>'.
constructor(upgrader: Upgrader, ...others: any) {
return this
}
/**
* Dial a given multiaddr.
*/
dial(ma: Multiaddr, options?: TCPDial): Promise<Connection> {
throw 1
}
/**
* Create transport listeners.
*/
createListener(options: any, handler: (connection:Connection) => void): Listener {
throw 2
}
/**
* Takes a list of `Multiaddr`s and returns only valid addresses for the transport
*/
filter(multiaddrs: Multiaddr[]): Multiaddr[] {
return multiaddrs
}
}P.S.: It complains about the prototype field but if you remove prototype from interface it will still complain about Now consider what I was suggesting instead: type TCPDial = { signal?: AbortSignal }
export class TCPTransport implements Transport<TCPDial> {
constructor(_upgrader: Upgrader, ..._others: any) {
}
/**
* Dial a given multiaddr.
*/
dial(_ma: Multiaddr, _options?: TCPDial): Promise<Connection> {
throw 1
}
/**
* Create transport listeners.
*/
createListener(_options: any, _handler: (connection:Connection) => void): Listener {
throw 2
}
/**
* Takes a list of `Multiaddr`s and returns only valid addresses for the transport
*/
filter(multiaddrs: Multiaddr[]): Multiaddr[] {
return multiaddrs
}
}
export const dial = <T extends { signal: AbortSignal }>
(NetworkTransport: TransportFactory<T>, upgrader: Upgrader, ma:Multiaddr, options:T):Transport<T> => {
const transport = new NetworkTransport(upgrader)
transport.dial(ma, options)
return transport
}Unlike above example It allows implementation of the transport to claim that it implements the interface TransportFactory <DialOptions extends { signal?: AbortSignal }> {
new (upgrader: Upgrader, ...others: any): Transport<DialOptions>;
}
/**
* A libp2p transport is understood as something that offers a dial and listen interface to establish connections.
*/
export interface Transport <DialOptions extends { signal?: AbortSignal }> {
/**
* Dial a given multiaddr.
*/
dial(ma: Multiaddr, options?: DialOptions): Promise<Connection>;
/**
* Create transport listeners.
*/
createListener(options: any, handler: (connection:Connection) => void): Listener;
/**
* Takes a list of `Multiaddr`s and returns only valid addresses for the transport
*/
filter(multiaddrs: Multiaddr[]): Multiaddr[];
} |
src/transport/types.ts
Outdated
| /** | ||
| * Create transport listeners. | ||
| */ | ||
| createListener(options: any, handler: (Connection) => void): Listener; |
There was a problem hiding this comment.
looks like options here should be also made into generic rather than any.
There was a problem hiding this comment.
I can change it to unknown, but really not much more.
Taking a look at https://github.com/libp2p/js-libp2p-interfaces/tree/master/src/transport#create-a-listener there is no interface options for the listener. Each transport will have their own.
There was a problem hiding this comment.
But that also the whole point of generics to define a generic transport interface so that specific ones can specify concrete types that correspond to them just like it was in the case of dial:
interface Tansport<DialOptions extends { signal: AbortSignal }, ListenerOption> {
dial(ma: Multiaddr, options?: DialOptions): Promise<Connection>;
createListener(options: ListenerOptions, handler: (Connection) => void): Listener;
filter(multiaddrs: Multiaddr[]): Multiaddr[];
}
class TCPTransport implements Transport<TCPDialOptions, TCPListenerOptions> {
dial(ma: Multiaddr, options?: DialOptions): Promise<Connection> {
// ...
}
createListener(options: TCPListenerOptions, handler: (connection: Connection) => void): Listener {
// ...
}
// ...
}
type TCPDialOptions = {
// ...
}
type TCPListenerOptions = //...You could even go step further and make Transport have a default type e.g.:
interface Transport <DialOptions extends AbortOptions = AbortOptions, ListenerOptions = void> {
}
// Will be a shortuct for implemnets TCPTransport<AbortOptions, void>
class TCPTransport implements Transport {
} |
Ts check is just commented out in the config because it's fail if a repo doesn't have the tsconfig (looking at you @Gozala this could be better 🙏🏼) |
…factory interface and minor pubsub fixes
|
So, I did a new iteration on this with the latest feedback from @Gozala 👍 :
This should now be ready to go. |
Co-authored-by: Hugo Dias <hugomrdias@gmail.com>
There is |
Have you seen this comment #74 (comment) it contains links to the TS playground illustrating issues. IMO whole point of the interface is to be able to tell in the class implementation that it implementns some interface. Current version is not implementable (or more like it's not doing what I think assumbtion is about it). There is also a second link that illustrates how it could be split into two interfaces such that class can say it implements Transport and the other P.S.: |
|
Per libp2p/js-libp2p#802 feedback, I added two new minor commits to fix review issues |
This PR adds typedef improvements for the already available modules, as well as typescript files for the interfaces needed in libp2p core per libp2p/js-libp2p#802
On top of @Gozala 's #73 PR
Needs:
BREAKING CHANGE: Some types from connection, pubsub and topology are now fixed. Record is not a class anymore, but a typescript interface that should be implemented