Skip to content

Commit 135552b

Browse files
authored
feat: add types (#106)
* feat: add types Adds ts types, fixes all tsc errors. Removes ipfs/ipfsd-ctl/ipfs-http-client dev deps as we only spin up a node to get an IPFS id which we can just hard code for testing purposes. Replaces protons with protobufjs as elsewhere in the stack. * chore: do not double-build * chore: add types directive to package.json * chore: update ubuntu version * chore: remove redundant pre publish step
1 parent a7d1aa0 commit 135552b

File tree

11 files changed

+603
-107
lines changed

11 files changed

+603
-107
lines changed

.aegir.js

Lines changed: 0 additions & 16 deletions
This file was deleted.

.travis.yml

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,16 @@
11
language: node_js
2+
dist: bionic
23
cache: npm
34
stages:
45
- check
56
- test
67
- cov
78

9+
branches:
10+
only:
11+
- master
12+
- /^release\/.*$/
13+
814
node_js:
915
- 'lts/*'
1016
- 'node'
@@ -16,7 +22,7 @@ os:
1622

1723
before_install:
1824
# modules with pre-built binaries may not have deployed versions for bleeding-edge node so this lets us fall back to building from source
19-
- npm install -g node-pre-gyp
25+
- npm install -g @mapbox/node-pre-gyp
2026

2127
script: npx nyc -s npm run test:node -- --bail
2228
after_success: npx nyc report --reporter=text-lcov > coverage.lcov && npx codecov
@@ -25,24 +31,21 @@ jobs:
2531
include:
2632
- stage: check
2733
script:
28-
- npx aegir dep-check -- -i wrtc -i electron-webrtc
34+
- npx aegir dep-check
2935
- npm run lint
3036

3137
- stage: test
3238
name: chrome
3339
addons:
3440
chrome: stable
3541
script:
36-
- npx aegir test -t browser
37-
- npx aegir test -t webworker
42+
- npx aegir test -t browser -t webworker
3843

3944
- stage: test
4045
name: firefox
4146
addons:
4247
firefox: latest
43-
script:
44-
- npx aegir test -t browser -- --browsers FirefoxHeadless
45-
- npx aegir test -t webworker -- --browsers FirefoxHeadless
48+
script: npx aegir test -t browser -t webworker -- --browser firefox
4649

4750
notifications:
4851
email: false

package.json

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,12 @@
44
"description": "ipns record definitions",
55
"leadMaintainer": "Vasco Santos <[email protected]>",
66
"main": "src/index.js",
7+
"types": "dist/src/index.d.ts",
78
"scripts": {
8-
"build": "aegir build",
9+
"prepare": "run-s prepare:*",
10+
"prepare:proto": "pbjs -t static-module -w commonjs --force-number --no-verify --no-delimited --no-create --no-beautify --no-defaults --lint eslint-disable -o src/pb/ipns.js src/pb/ipns.proto",
11+
"prepare:proto-types": "pbts -o src/pb/ipns.d.ts src/pb/ipns.js",
12+
"prepare:types": "aegir build --no-bundle",
913
"lint": "aegir lint",
1014
"release": "aegir release",
1115
"release-minor": "aegir release --type minor",
@@ -37,22 +41,22 @@
3741
"err-code": "^3.0.1",
3842
"interface-datastore": "^3.0.1",
3943
"libp2p-crypto": "^0.19.0",
40-
"multibase": "^3.0.1",
41-
"multihashes": "^3.0.1",
44+
"multibase": "^4.0.2",
45+
"multihashes": "^4.0.2",
4246
"peer-id": "^0.14.2",
43-
"protons": "^2.0.0",
47+
"protobufjs": "^6.10.2",
4448
"timestamp-nano": "^1.0.0",
4549
"uint8arrays": "^2.0.5"
4650
},
4751
"devDependencies": {
48-
"aegir": "^30.2.0",
52+
"@types/chai-string": "^1.4.2",
53+
"@types/debug": "^4.1.5",
54+
"aegir": "^31.0.4",
4955
"chai": "^4.2.0",
5056
"chai-bytes": "~0.1.2",
5157
"chai-string": "^1.5.0",
5258
"dirty-chai": "^2.0.1",
53-
"ipfs": "^0.54.2",
54-
"ipfs-http-client": "^49.0.2",
55-
"ipfsd-ctl": "^7.0.3"
59+
"npm-run-all": "^4.1.5"
5660
},
5761
"contributors": [
5862
"Vasco Santos <[email protected]>",

src/index.js

Lines changed: 85 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -12,38 +12,35 @@ const uint8ArrayToString = require('uint8arrays/to-string')
1212
const uint8ArrayConcat = require('uint8arrays/concat')
1313

1414
const debug = require('debug')
15-
const log = debug('jsipns')
16-
log.error = debug('jsipns:error')
15+
const log = Object.assign(debug('jsipns'), {
16+
error: debug('jsipns:error')
17+
})
1718

18-
const ipnsEntryProto = require('./pb/ipns.proto')
19+
const {
20+
IpnsEntry: ipnsEntryProto
21+
} = require('./pb/ipns.js')
1922
const { parseRFC3339 } = require('./utils')
2023
const ERRORS = require('./errors')
2124

22-
const ID_MULTIHASH_CODE = multihash.names.id
25+
const ID_MULTIHASH_CODE = multihash.names.identity
2326

2427
const namespace = '/ipns/'
2528

2629
/**
27-
* IPNS entry
28-
*
29-
* @typedef {Object} IpnsEntry
30-
* @property {string} value - value to be stored in the record
31-
* @property {Uint8Array} signature - signature of the record
32-
* @property {number} validityType - Type of validation being used
33-
* @property {string} validity - expiration datetime for the record in RFC3339 format
34-
* @property {number} sequence - number representing the version of the record
30+
* @typedef {import('./types').IPNSEntry} IPNSEntry
31+
* @typedef {import('libp2p-crypto').PublicKey} PublicKey
32+
* @typedef {import('libp2p-crypto').PrivateKey} PrivateKey
3533
*/
3634

3735
/**
3836
* Creates a new ipns entry and signs it with the given private key.
3937
* The ipns entry validity should follow the [RFC3339]{@link https://www.ietf.org/rfc/rfc3339.txt} with nanoseconds precision.
4038
* Note: This function does not embed the public key. If you want to do that, use `EmbedPublicKey`.
4139
*
42-
* @param {Object} privateKey - private key for signing the record.
40+
* @param {PrivateKey} privateKey - private key for signing the record.
4341
* @param {string} value - value to be stored in the record.
4442
* @param {number} seq - number representing the current version of the record.
4543
* @param {number|string} lifetime - lifetime of the record (in milliseconds).
46-
* @returns {Promise<IpnsEntry>} entry
4744
*/
4845
const create = (privateKey, value, seq, lifetime) => {
4946
// Validity in ISOString with nanoseconds precision and validity type EOL
@@ -56,17 +53,23 @@ const create = (privateKey, value, seq, lifetime) => {
5653
* Same as create(), but instead of generating a new Date, it receives the intended expiration time
5754
* WARNING: nano precision is not standard, make sure the value in seconds is 9 orders of magnitude lesser than the one provided.
5855
*
59-
* @param {Object} privateKey - private key for signing the record.
56+
* @param {PrivateKey} privateKey - private key for signing the record.
6057
* @param {string} value - value to be stored in the record.
6158
* @param {number} seq - number representing the current version of the record.
6259
* @param {string} expiration - expiration datetime for record in the [RFC3339]{@link https://www.ietf.org/rfc/rfc3339.txt} with nanoseconds precision.
63-
* @returns {Promise<IpnsEntry>} entry
6460
*/
6561
const createWithExpiration = (privateKey, value, seq, expiration) => {
6662
const validityType = ipnsEntryProto.ValidityType.EOL
6763
return _create(privateKey, value, seq, expiration, validityType)
6864
}
6965

66+
/**
67+
* @param {PrivateKey} privateKey
68+
* @param {string} value
69+
* @param {number} seq
70+
* @param {string} isoValidity
71+
* @param {number} validityType
72+
*/
7073
const _create = async (privateKey, value, seq, isoValidity, validityType) => {
7174
const signature = await sign(privateKey, value, validityType, isoValidity)
7275

@@ -85,9 +88,8 @@ const _create = async (privateKey, value, seq, isoValidity, validityType) => {
8588
/**
8689
* Validates the given ipns entry against the given public key.
8790
*
88-
* @param {Object} publicKey - public key for validating the record.
89-
* @param {IpnsEntry} entry - ipns entry record.
90-
* @returns {Promise}
91+
* @param {PublicKey} publicKey - public key for validating the record.
92+
* @param {IPNSEntry} entry - ipns entry record.
9193
*/
9294
const validate = async (publicKey, entry) => {
9395
const { value, validityType, validity } = entry
@@ -116,7 +118,7 @@ const validate = async (publicKey, entry) => {
116118
throw errCode(new Error('unrecognized validity format (not an rfc3339 format)'), ERRORS.ERR_UNRECOGNIZED_FORMAT)
117119
}
118120

119-
if (validityDate < Date.now()) {
121+
if (validityDate.getTime() < Date.now()) {
120122
log.error('record has expired')
121123
throw errCode(new Error('record has expired'), ERRORS.ERR_IPNS_EXPIRED_RECORD)
122124
}
@@ -137,9 +139,8 @@ const validate = async (publicKey, entry) => {
137139
* send this as part of the record itself. For newer ed25519 keys, the public key
138140
* can be embedded in the peerId.
139141
*
140-
* @param {Object} publicKey - public key to embed.
141-
* @param {Object} entry - ipns entry record.
142-
* @returns {IpnsEntry} entry with public key embedded
142+
* @param {PublicKey} publicKey - public key to embed.
143+
* @param {IPNSEntry} entry - ipns entry record.
143144
*/
144145
const embedPublicKey = async (publicKey, entry) => {
145146
if (!publicKey || !publicKey.bytes || !entry) {
@@ -182,9 +183,8 @@ const embedPublicKey = async (publicKey, entry) => {
182183
/**
183184
* Extracts a public key matching `pid` from the ipns record.
184185
*
185-
* @param {Object} peerId - peer identifier object.
186-
* @param {IpnsEntry} entry - ipns entry record.
187-
* @returns {Object} the public key
186+
* @param {PeerId} peerId - peer identifier object.
187+
* @param {IPNSEntry} entry - ipns entry record.
188188
*/
189189
const extractPublicKey = (peerId, entry) => {
190190
if (!entry || !peerId) {
@@ -211,15 +211,18 @@ const extractPublicKey = (peerId, entry) => {
211211
throw Object.assign(new Error('no public key is available'), { code: ERRORS.ERR_UNDEFINED_PARAMETER })
212212
}
213213

214-
// rawStdEncoding with RFC4648
214+
/**
215+
* rawStdEncoding with RFC4648
216+
*
217+
* @param {Uint8Array} key
218+
*/
215219
const rawStdEncoding = (key) => multibase.encode('base32', key).toString().slice(1).toUpperCase()
216220

217221
/**
218222
* Get key for storing the record locally.
219223
* Format: /ipns/${base32(<HASH>)}
220224
*
221225
* @param {Uint8Array} key - peer identifier object.
222-
* @returns {string}
223226
*/
224227
const getLocalKey = (key) => new Key(`/ipns/${rawStdEncoding(key)}`)
225228

@@ -228,7 +231,6 @@ const getLocalKey = (key) => new Key(`/ipns/${rawStdEncoding(key)}`)
228231
* Format: ${base32(/ipns/<HASH>)}, ${base32(/pk/<HASH>)}
229232
*
230233
* @param {Uint8Array} pid - peer identifier represented by the multihash of the public key as Uint8Array.
231-
* @returns {Object} containing the `nameKey` and the `ipnsKey`.
232234
*/
233235
const getIdKeys = (pid) => {
234236
const pkBuffer = uint8ArrayFromString('/pk/')
@@ -242,7 +244,14 @@ const getIdKeys = (pid) => {
242244
}
243245
}
244246

245-
// Sign ipns record data
247+
/**
248+
* Sign ipns record data
249+
*
250+
* @param {PrivateKey} privateKey
251+
* @param {string} value
252+
* @param {number} validityType
253+
* @param {Uint8Array | string} validity
254+
*/
246255
const sign = (privateKey, value, validityType, validity) => {
247256
try {
248257
const dataForSignature = ipnsEntryDataForSig(value, validityType, validity)
@@ -254,7 +263,11 @@ const sign = (privateKey, value, validityType, validity) => {
254263
}
255264
}
256265

257-
// Utility for getting the validity type code name of a validity
266+
/**
267+
* Utility for getting the validity type code name of a validity
268+
*
269+
* @param {number} validityType
270+
*/
258271
const getValidityType = (validityType) => {
259272
if (validityType.toString() === '0') {
260273
return 'EOL'
@@ -265,7 +278,13 @@ const getValidityType = (validityType) => {
265278
throw errCode(error, ERRORS.ERR_UNRECOGNIZED_VALIDITY)
266279
}
267280

268-
// Utility for creating the record data for being signed
281+
/**
282+
* Utility for creating the record data for being signed
283+
*
284+
* @param {string | Uint8Array} value
285+
* @param {number} validityType
286+
* @param {string | Uint8Array} validity
287+
*/
269288
const ipnsEntryDataForSig = (value, validityType, validity) => {
270289
if (!(value instanceof Uint8Array)) {
271290
value = uint8ArrayFromString(value)
@@ -280,7 +299,11 @@ const ipnsEntryDataForSig = (value, validityType, validity) => {
280299
return uint8ArrayConcat([value, validity, validityTypeBuffer])
281300
}
282301

283-
// Utility for extracting the public key from a peer-id
302+
/**
303+
* Utility for extracting the public key from a peer-id
304+
*
305+
* @param {PeerId} peerId
306+
*/
284307
const extractPublicKeyFromId = (peerId) => {
285308
const decodedId = multihash.decode(peerId.id)
286309

@@ -291,11 +314,34 @@ const extractPublicKeyFromId = (peerId) => {
291314
return crypto.keys.unmarshalPublicKey(decodedId.digest)
292315
}
293316

294-
const marshal = ipnsEntryProto.encode
317+
/**
318+
* @param {IPNSEntry} obj
319+
*/
320+
const marshal = (obj) => {
321+
return ipnsEntryProto.encode(obj).finish()
322+
}
295323

296-
const unmarshal = ipnsEntryProto.decode
324+
/**
325+
* @param {Uint8Array} buf
326+
* @returns {IPNSEntry}
327+
*/
328+
const unmarshal = (buf) => {
329+
const message = ipnsEntryProto.decode(buf)
330+
331+
// @ts-ignore
332+
return ipnsEntryProto.toObject(message, {
333+
defaults: false,
334+
arrays: true,
335+
longs: Number,
336+
objects: false
337+
})
338+
}
297339

298340
const validator = {
341+
/**
342+
* @param {Uint8Array} marshalledData
343+
* @param {Uint8Array} key
344+
*/
299345
validate: async (marshalledData, key) => {
300346
const receivedEntry = unmarshal(marshalledData)
301347
const bufferId = key.slice('/ipns/'.length)
@@ -308,6 +354,10 @@ const validator = {
308354
await validate(pubKey, receivedEntry)
309355
return true
310356
},
357+
/**
358+
* @param {Uint8Array} dataA
359+
* @param {Uint8Array} dataB
360+
*/
311361
select: (dataA, dataB) => {
312362
const entryA = unmarshal(dataA)
313363
const entryB = unmarshal(dataB)

0 commit comments

Comments
 (0)