-
Notifications
You must be signed in to change notification settings - Fork 63
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix: integrate with aegir types cmd #159
Conversation
/** | ||
* Resolver for dnsaddr addresses. | ||
* | ||
* @param {Multiaddr} addr | ||
* @param {any} addr |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not a multiaddr?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
theres a TODO above explaining we can't get the Multiaddr type because of this issue microsoft/TypeScript#41672
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah right! This is not nice as we will need to update this again after, I might wait for this to be solved before getting libp2p PR updating this merged in.
But we can move forward with this module here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the only way to solve this now, is to do a bigger breaking change and default export the Multiaddr class directly. this would remove the ability to multiaddr('/ip..')
and force new Multiaddr('')
should we do this ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i can add a static method to the Multiaddr class called multiaddr
and users would only need to change from const multiaddr = require('multiaddr)
to const { multiaddr } = require('multiaddr)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would like to have the constructor exported by default to be honest. But, I would wait on @jacobheun input before moving that way as this has a impact on a large number of modules.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah having the constructor exported directly makes a bunch of things easier and documentation looks a lot better.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm in favor of exporting the constructor, we should make sure we have clear migration docs for people. We should also add a static method for creation, this would make it easy to hot swap the two and allow us to be more picky about the constructor args. Since this is in the multiformats org it might be good to be consistent with the cid update effort, multiformats/js-multiformats#23, and call it asMultiaddr
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One minor detail left, and this should be good to go
BREAKING CHANGE: Convert.toString now always a string
BREAKING CHANGE: index.js default export no longer is both callable and constructor. encapsulate and decapsulate only accept Multiaddr instances as input.
BREAKING CHANGE: module export the Multiaddr class instead of a factory function.
2d1c612
to
50719b3
Compare
@jacobheun and @vasco-santos can you please review, added the migration docs and some more small tweaks as for the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I made some small suggestions and some not so small. Given that this is breaking change, I think it would be good to use this opportunity to do more changes that otherwise would be a lot harder to justify:
- Constructor takes address in arbitrary representation, tries to figure out what is it and then turns it into
Multiaddr
if it can. I think a factory functions (static method) representation kind is lot more clear both for user and implementer (as illustrated inline) and avoids unnecessary work when types are known. If it is not unreasonable I would really love to use this opportunity to do such a change. We can still have genericMultiaddr.from
when representation is unknown. Multiaddr
has large number of utility methods, having equivalent static methods would be really nice to have because:
- They come handy in places like
addrs.map(Multiaddr.toOptions)
- Moving
multiaddrs
across realms / threads discards prototype chains. Which means static methods continue to work, but not instance methods. Which in turn means encode / decode pipeline.
I realize above two are big asks & only bringing them so we can consider / discuss given the opportunity presented by breaking change.
- run: yarn | ||
- run: yarn lint | ||
- run: yarn build | ||
- uses: gozala/typescript-error-reporter-action@v1.0.4 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- uses: gozala/typescript-error-reporter-action@v1.0.4 | |
- uses: gozala/typescript-error-reporter-action@v1.0.8 |
|
||
```js | ||
// The Multiaddr class has a factory method to help migration | ||
const { multiaddr } = require('multiaddr') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it would make more sense to have Multiaddr.parse(string)
static and make constructor @private
. That way this turns into
const { multiaddr } = require('multiaddr') | |
const { from: multiaddr } = require('multiaddr') |
And if we really want to smooth out transition we could have deprecated alias multiaddr
.
My argument for discouraging new Multiadddr
is because has tendency to grow into new Multiaddr(any)
over time.
My personal opinion is to reserve constructors for property initializations, all the computation and logic should go into dedicated functions.
Edit: Added more details in the contstructor
"test:browser": "aegir test -t browser", | ||
"test:types": "npx tsc", | ||
"test:types": "aegir ts -p check", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"test:types": "aegir ts -p check", | |
"test:types": "aegir ts -p check", | |
"prepare": "aegir ts -p types", |
function tuplesToStringTuples (tuples) { | ||
return tuples.map(tup => { | ||
const proto = protoFromTuple(tup) | ||
if (tup.length > 1) { | ||
if (tup.length > 1 && tup[1]) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Checking length is redundant
if (tup.length > 1 && tup[1]) { | |
if (tup[1]) { |
* // <Multiaddr 047f000001060fa1 - /ip4/127.0.0.1/tcp/4001> | ||
* ``` | ||
*/ | ||
constructor (addr) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we use this breaking change as an opportunity to make it more type friendly and switch to more cleaner approach:
const symbol = Symbol.for('@multiformats/js-multiaddr/multiaddr')
class Multiaddr {
/**
* Parses string encoded multiaddr.
*
* @param {string} addr
* @returns {Multiaddr}
*/
static parse(addr) {
if (addr.length > 0 && addr.charAt(0) !== '/') {
throw new Error(`multiaddr "${addr}" must start with a "/"`)
}
return new Multiaddr(codec.fromString(addr))
}
/**
* Decodedes binary encoded multiaddr.
*
* @param {Uint8Array} addr
* @returns {Multiaddr}
*/
static decode(addr) {
return new Multiaddr(codec.fromBytes(addr))
}
static empty() {
return Multiaddr.parse('')
}
/**
* @typedef {Multiaddr|string|InstanceType<typeof String>|Uint8Array|null} ToMultiaddr
* @param {ToMultiaddr} addr
* @returns {Multiaddr}
*/
static from(addr) {
if (typeof addr === 'string') {
return Multiaddr.parse(addr)
} else if (addr instanceof Uint8Array) {
return Multiaddr.decode(addr)
} else if (Multiaddr.isMultiaddr(addr)) {
return new Multiaddr(addr.bytes)
} else if (addr === null) {
return Multiaddr.empty()
} else {
throw new Error('addr must be a string, Buffer, or another Multiaddr')
}
}
/**
* @param {any} value
* @returns {value is Multiaddr}
*/
static isMultiaddr(value) {
return Boolean(value && value[symbol])
}
/**
* @private
* @param {Uint8Array} bytes
*/
constructor(bytes) {
this.bytes = bytes
}
}
Which has bunch of benifs:
- When you read
Multiadddr.parse(input)
it is clear what theinput
without any tooling or side trips. - Constructor is dead simple
- Each code path is also absolutely clear.
- You still have
.from
to take any input when you don't know what you have. - No added overhead on each instantiation when you know your types.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For what it's worth that is where multiformats/cid
ended up and feedback so far had been positive.
* ``` | ||
*/ | ||
constructor (addr) { | ||
Object.defineProperty(this, symbol, { value: true }) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we turn that this into a getter please ?
class Multiaddr {
get [symbol]() {
return true
}
}
That is what we end up doing in other places.
opts.host = parsed[2] | ||
opts.transport = parsed[3] | ||
opts.port = parseInt(parsed[4]) | ||
return opts |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we do this instead, I find it a lot easier to follow (and engines can optimize better)
toOptions() {
const [,family,host, transport, port] = this.toString().split('/')
return {
famility: family === 'ip4' ? 'ipv4' : 'ipv6',
host,
transport,
port
}
}
* Returns if something is a Multiaddr that is a name | ||
* | ||
* @param {Multiaddr} addr | ||
* @returns {boolean} isName |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
* @returns {boolean} isName | |
* @returns {boolean} |
* - how to achieve it in the browser? | ||
/** | ||
* Static factory method | ||
* |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it would make sense to deprecate it to encourage transition.
* | |
* | |
* @deprecated |
This PR does the following: (review might be easier by commit)
chore: update readme to look like the other multiformats repos
fix: update package.json and tsconfig
fix: transform multiaddr into a class
BREAKING CHANGE: index.js default export no longer is both callable and
constructor. encapsulate and decapsulate only accept Multiaddr instances as
input.
fix: remove class-is and fix types in index.js
fix: add some types to protocols
fix: add some types to convert
BREAKING CHANGE: Convert.toString now always returns a string
fix: add some types to codec.js
fix: fix resolver exports
fix: remove old docs stuff
feat: use github ci
The breaking changes should be theoretically based on the current documentation and usage but we never know if people are doing something not documented.
ping this PR #168