├── .DS_Store
├── .github
├── dependabot.yml
└── workflows
│ ├── automerge.yml
│ ├── js-test-and-release.yml
│ ├── semantic-pull-request.yml
│ └── stale.yml
├── .gitignore
├── CHANGELOG.md
├── LICENSE
├── LICENSE-APACHE
├── LICENSE-MIT
├── README.md
├── package.json
├── src
├── index.ts
├── record.proto
├── record.ts
├── selectors.ts
├── utils.ts
└── validators.ts
├── test
├── fixtures
│ ├── go-key-records.ts
│ └── go-record.ts
├── record.spec.ts
├── selection.spec.ts
├── utils.spec.ts
└── validator.spec.ts
└── tsconfig.json
/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/libp2p/js-libp2p-record/47cff8bb50d3e26f63a5771123ed220f2bb54b1a/.DS_Store
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 | - package-ecosystem: npm
4 | directory: "/"
5 | schedule:
6 | interval: daily
7 | time: "10:00"
8 | open-pull-requests-limit: 10
9 | commit-message:
10 | prefix: "deps"
11 | prefix-development: "deps(dev)"
12 |
--------------------------------------------------------------------------------
/.github/workflows/automerge.yml:
--------------------------------------------------------------------------------
1 | # File managed by web3-bot. DO NOT EDIT.
2 | # See https://github.com/protocol/.github/ for details.
3 |
4 | name: Automerge
5 | on: [ pull_request ]
6 |
7 | jobs:
8 | automerge:
9 | uses: protocol/.github/.github/workflows/automerge.yml@master
10 | with:
11 | job: 'automerge'
12 |
--------------------------------------------------------------------------------
/.github/workflows/js-test-and-release.yml:
--------------------------------------------------------------------------------
1 | # File managed by web3-bot. DO NOT EDIT.
2 | # See https://github.com/protocol/.github/ for details.
3 |
4 | name: test & maybe release
5 | on:
6 | push:
7 | branches:
8 | - master
9 | pull_request:
10 |
11 | jobs:
12 |
13 | check:
14 | runs-on: ubuntu-latest
15 | steps:
16 | - uses: actions/checkout@v3
17 | - uses: actions/setup-node@v3
18 | with:
19 | node-version: lts/*
20 | - uses: ipfs/aegir/actions/cache-node-modules@master
21 | - run: npm run --if-present lint
22 | - run: npm run --if-present dep-check
23 |
24 | test-node:
25 | needs: check
26 | runs-on: ${{ matrix.os }}
27 | strategy:
28 | matrix:
29 | os: [windows-latest, ubuntu-latest, macos-latest]
30 | node: [lts/*]
31 | fail-fast: true
32 | steps:
33 | - uses: actions/checkout@v3
34 | - uses: actions/setup-node@v3
35 | with:
36 | node-version: ${{ matrix.node }}
37 | - uses: ipfs/aegir/actions/cache-node-modules@master
38 | - run: npm run --if-present test:node
39 | - uses: codecov/codecov-action@d9f34f8cd5cb3b3eb79b3e4b5dae3a16df499a70 # v3.1.1
40 | with:
41 | flags: node
42 |
43 | test-chrome:
44 | needs: check
45 | runs-on: ubuntu-latest
46 | steps:
47 | - uses: actions/checkout@v3
48 | - uses: actions/setup-node@v3
49 | with:
50 | node-version: lts/*
51 | - uses: ipfs/aegir/actions/cache-node-modules@master
52 | - run: npm run --if-present test:chrome
53 | - uses: codecov/codecov-action@d9f34f8cd5cb3b3eb79b3e4b5dae3a16df499a70 # v3.1.1
54 | with:
55 | flags: chrome
56 |
57 | test-chrome-webworker:
58 | needs: check
59 | runs-on: ubuntu-latest
60 | steps:
61 | - uses: actions/checkout@v3
62 | - uses: actions/setup-node@v3
63 | with:
64 | node-version: lts/*
65 | - uses: ipfs/aegir/actions/cache-node-modules@master
66 | - run: npm run --if-present test:chrome-webworker
67 | - uses: codecov/codecov-action@d9f34f8cd5cb3b3eb79b3e4b5dae3a16df499a70 # v3.1.1
68 | with:
69 | flags: chrome-webworker
70 |
71 | test-firefox:
72 | needs: check
73 | runs-on: ubuntu-latest
74 | steps:
75 | - uses: actions/checkout@v3
76 | - uses: actions/setup-node@v3
77 | with:
78 | node-version: lts/*
79 | - uses: ipfs/aegir/actions/cache-node-modules@master
80 | - run: npm run --if-present test:firefox
81 | - uses: codecov/codecov-action@d9f34f8cd5cb3b3eb79b3e4b5dae3a16df499a70 # v3.1.1
82 | with:
83 | flags: firefox
84 |
85 | test-firefox-webworker:
86 | needs: check
87 | runs-on: ubuntu-latest
88 | steps:
89 | - uses: actions/checkout@v3
90 | - uses: actions/setup-node@v3
91 | with:
92 | node-version: lts/*
93 | - uses: ipfs/aegir/actions/cache-node-modules@master
94 | - run: npm run --if-present test:firefox-webworker
95 | - uses: codecov/codecov-action@d9f34f8cd5cb3b3eb79b3e4b5dae3a16df499a70 # v3.1.1
96 | with:
97 | flags: firefox-webworker
98 |
99 | test-webkit:
100 | needs: check
101 | runs-on: ${{ matrix.os }}
102 | strategy:
103 | matrix:
104 | os: [ubuntu-latest, macos-latest]
105 | node: [lts/*]
106 | fail-fast: true
107 | steps:
108 | - uses: actions/checkout@v3
109 | - uses: actions/setup-node@v3
110 | with:
111 | node-version: lts/*
112 | - uses: ipfs/aegir/actions/cache-node-modules@master
113 | - run: npm run --if-present test:webkit
114 | - uses: codecov/codecov-action@d9f34f8cd5cb3b3eb79b3e4b5dae3a16df499a70 # v3.1.1
115 | with:
116 | flags: webkit
117 |
118 | test-webkit-webworker:
119 | needs: check
120 | runs-on: ${{ matrix.os }}
121 | strategy:
122 | matrix:
123 | os: [ubuntu-latest, macos-latest]
124 | node: [lts/*]
125 | fail-fast: true
126 | steps:
127 | - uses: actions/checkout@v3
128 | - uses: actions/setup-node@v3
129 | with:
130 | node-version: lts/*
131 | - uses: ipfs/aegir/actions/cache-node-modules@master
132 | - run: npm run --if-present test:webkit-webworker
133 | - uses: codecov/codecov-action@d9f34f8cd5cb3b3eb79b3e4b5dae3a16df499a70 # v3.1.1
134 | with:
135 | flags: webkit-webworker
136 |
137 | test-electron-main:
138 | needs: check
139 | runs-on: ubuntu-latest
140 | steps:
141 | - uses: actions/checkout@v3
142 | - uses: actions/setup-node@v3
143 | with:
144 | node-version: lts/*
145 | - uses: ipfs/aegir/actions/cache-node-modules@master
146 | - run: npx xvfb-maybe npm run --if-present test:electron-main
147 | - uses: codecov/codecov-action@d9f34f8cd5cb3b3eb79b3e4b5dae3a16df499a70 # v3.1.1
148 | with:
149 | flags: electron-main
150 |
151 | test-electron-renderer:
152 | needs: check
153 | runs-on: ubuntu-latest
154 | steps:
155 | - uses: actions/checkout@v3
156 | - uses: actions/setup-node@v3
157 | with:
158 | node-version: lts/*
159 | - uses: ipfs/aegir/actions/cache-node-modules@master
160 | - run: npx xvfb-maybe npm run --if-present test:electron-renderer
161 | - uses: codecov/codecov-action@d9f34f8cd5cb3b3eb79b3e4b5dae3a16df499a70 # v3.1.1
162 | with:
163 | flags: electron-renderer
164 |
165 | release:
166 | needs: [test-node, test-chrome, test-chrome-webworker, test-firefox, test-firefox-webworker, test-webkit, test-webkit-webworker, test-electron-main, test-electron-renderer]
167 | runs-on: ubuntu-latest
168 | if: github.event_name == 'push' && github.ref == 'refs/heads/master'
169 | steps:
170 | - uses: actions/checkout@v3
171 | with:
172 | fetch-depth: 0
173 | - uses: actions/setup-node@v3
174 | with:
175 | node-version: lts/*
176 | - uses: ipfs/aegir/actions/cache-node-modules@master
177 | - uses: ipfs/aegir/actions/docker-login@master
178 | with:
179 | docker-token: ${{ secrets.DOCKER_TOKEN }}
180 | docker-username: ${{ secrets.DOCKER_USERNAME }}
181 | - run: npm run --if-present release
182 | env:
183 | GITHUB_TOKEN: ${{ secrets.UCI_GITHUB_TOKEN || github.token }}
184 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
185 |
--------------------------------------------------------------------------------
/.github/workflows/semantic-pull-request.yml:
--------------------------------------------------------------------------------
1 | name: Semantic PR
2 |
3 | on:
4 | pull_request_target:
5 | types:
6 | - opened
7 | - edited
8 | - synchronize
9 |
10 | jobs:
11 | main:
12 | uses: pl-strflt/.github/.github/workflows/reusable-semantic-pull-request.yml@v0.3
13 |
--------------------------------------------------------------------------------
/.github/workflows/stale.yml:
--------------------------------------------------------------------------------
1 | name: Close and mark stale issue
2 |
3 | on:
4 | schedule:
5 | - cron: '0 0 * * *'
6 |
7 | permissions:
8 | issues: write
9 | pull-requests: write
10 |
11 | jobs:
12 | stale:
13 | uses: pl-strflt/.github/.github/workflows/reusable-stale-issue.yml@v0.3
14 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | dist
3 | .docs
4 | .coverage
5 | package-lock.json
6 | yarn.lock
7 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## [3.0.4](https://github.com/libp2p/js-libp2p-record/compare/v3.0.3...v3.0.4) (2023-06-15)
2 |
3 |
4 | ### Trivial Changes
5 |
6 | * Update .github/workflows/semantic-pull-request.yml [skip ci] ([afec2c9](https://github.com/libp2p/js-libp2p-record/commit/afec2c9d1685707c9cff82342099839abb6976da))
7 | * Update .github/workflows/stale.yml [skip ci] ([2d12a78](https://github.com/libp2p/js-libp2p-record/commit/2d12a789581d65181d463fa9b908280b14fc2070))
8 |
9 |
10 | ### Dependencies
11 |
12 | * **dev:** bump aegir from 38.1.8 to 39.0.10 ([#95](https://github.com/libp2p/js-libp2p-record/issues/95)) ([30e0fb5](https://github.com/libp2p/js-libp2p-record/commit/30e0fb5dfb193e3289f1aec6c29a8d194fd9aa92))
13 |
14 | ## [3.0.3](https://github.com/libp2p/js-libp2p-record/compare/v3.0.2...v3.0.3) (2023-04-04)
15 |
16 |
17 | ### Bug Fixes
18 |
19 | * correction package.json exports types path ([#87](https://github.com/libp2p/js-libp2p-record/issues/87)) ([c1e9a6d](https://github.com/libp2p/js-libp2p-record/commit/c1e9a6d402a971b3ef66484c151b9dc4627fea1b))
20 |
21 | ## [3.0.2](https://github.com/libp2p/js-libp2p-record/compare/v3.0.1...v3.0.2) (2023-03-10)
22 |
23 |
24 | ### Dependencies
25 |
26 | * bump protons-runtime from 4.0.2 to 5.0.0 ([#73](https://github.com/libp2p/js-libp2p-record/issues/73)) ([4b1b67b](https://github.com/libp2p/js-libp2p-record/commit/4b1b67bac77cb13a01ce330a4a93eb6c3dc042a5))
27 |
28 | ## [3.0.1](https://github.com/libp2p/js-libp2p-record/compare/v3.0.0...v3.0.1) (2023-03-10)
29 |
30 |
31 | ### Trivial Changes
32 |
33 | * replace err-code with CodeError ([#71](https://github.com/libp2p/js-libp2p-record/issues/71)) ([a843ae4](https://github.com/libp2p/js-libp2p-record/commit/a843ae4fbdc8c262a55f4ed87f989770d3783c5a)), closes [js-libp2p#1269](https://github.com/libp2p/js-libp2p/issues/1269)
34 | * Update .github/workflows/semantic-pull-request.yml [skip ci] ([3982918](https://github.com/libp2p/js-libp2p-record/commit/3982918a51c25bf1f803702073eaf17cc5feee9b))
35 | * Update .github/workflows/semantic-pull-request.yml [skip ci] ([79984c0](https://github.com/libp2p/js-libp2p-record/commit/79984c0ce651cb0bff634b3b8df630cf807baa59))
36 | * Update .github/workflows/semantic-pull-request.yml [skip ci] ([7ccccc7](https://github.com/libp2p/js-libp2p-record/commit/7ccccc73fb1aad74cd4b696acd8dc912199afec3))
37 |
38 |
39 | ### Dependencies
40 |
41 | * **dev:** bump aegir from 37.12.1 to 38.1.7 ([#84](https://github.com/libp2p/js-libp2p-record/issues/84)) ([4cc5935](https://github.com/libp2p/js-libp2p-record/commit/4cc593576ccda281950f30aa6c8e769baa1aeee6))
42 |
43 | ## [3.0.0](https://github.com/libp2p/js-libp2p-record/compare/v2.0.4...v3.0.0) (2023-01-06)
44 |
45 |
46 | ### ⚠ BREAKING CHANGES
47 |
48 | * update multiformats to 11.x.x (#70)
49 |
50 | ### Bug Fixes
51 |
52 | * update multiformats to 11.x.x ([#70](https://github.com/libp2p/js-libp2p-record/issues/70)) ([594fc41](https://github.com/libp2p/js-libp2p-record/commit/594fc4171ec20f4fc1fbc36c99c61eed06aeab25))
53 |
54 | ## [2.0.4](https://github.com/libp2p/js-libp2p-record/compare/v2.0.3...v2.0.4) (2022-12-16)
55 |
56 |
57 | ### Documentation
58 |
59 | * publish api docs ([#68](https://github.com/libp2p/js-libp2p-record/issues/68)) ([5a3dd41](https://github.com/libp2p/js-libp2p-record/commit/5a3dd419f13b67e27c19f3b23252937d80fc8b93))
60 |
61 | ## [2.0.3](https://github.com/libp2p/js-libp2p-record/compare/v2.0.2...v2.0.3) (2022-10-12)
62 |
63 |
64 | ### Trivial Changes
65 |
66 | * Update .github/workflows/stale.yml [skip ci] ([92044b6](https://github.com/libp2p/js-libp2p-record/commit/92044b646180e2b2d0f495d26cc54c184ad6fb7b))
67 |
68 |
69 | ### Dependencies
70 |
71 | * bump uint8arrays, protons and multiformats ([#63](https://github.com/libp2p/js-libp2p-record/issues/63)) ([9106a6a](https://github.com/libp2p/js-libp2p-record/commit/9106a6abdc71a2c94359759bbc2f61213e9a6a0b))
72 |
73 | ## [2.0.2](https://github.com/libp2p/js-libp2p-record/compare/v2.0.1...v2.0.2) (2022-08-11)
74 |
75 |
76 | ### Dependencies
77 |
78 | * update protons to 5.1.0 ([#58](https://github.com/libp2p/js-libp2p-record/issues/58)) ([24d4047](https://github.com/libp2p/js-libp2p-record/commit/24d404733aa89c28ae71abfcb51ee20b6af919cf))
79 |
80 | ## [2.0.1](https://github.com/libp2p/js-libp2p-record/compare/v2.0.0...v2.0.1) (2022-08-03)
81 |
82 |
83 | ### Trivial Changes
84 |
85 | * update project ([#53](https://github.com/libp2p/js-libp2p-record/issues/53)) ([1927144](https://github.com/libp2p/js-libp2p-record/commit/1927144ce346592f513e2f29e0b4677dd1feb468))
86 |
87 |
88 | ### Dependencies
89 |
90 | * update deps to support no-copy operations ([#55](https://github.com/libp2p/js-libp2p-record/issues/55)) ([7be8515](https://github.com/libp2p/js-libp2p-record/commit/7be8515ad87d062bbc9db20fc3134ed06b1286a9))
91 |
92 | ## [2.0.0](https://github.com/libp2p/js-libp2p-record/compare/v1.0.5...v2.0.0) (2022-06-15)
93 |
94 |
95 | ### ⚠ BREAKING CHANGES
96 |
97 | * uses new single-issue libp2p interface modules
98 |
99 | ### Features
100 |
101 | * update to latest libp2p interfaces ([#45](https://github.com/libp2p/js-libp2p-record/issues/45)) ([b5eb989](https://github.com/libp2p/js-libp2p-record/commit/b5eb9897f23ebf39e2a728672f3727222bc1159f))
102 |
103 | ### [1.0.5](https://github.com/libp2p/js-libp2p-record/compare/v1.0.4...v1.0.5) (2022-05-25)
104 |
105 |
106 | ### Trivial Changes
107 |
108 | * **deps:** bump @libp2p/interfaces from 1.3.32 to 2.0.2 ([#43](https://github.com/libp2p/js-libp2p-record/issues/43)) ([992677b](https://github.com/libp2p/js-libp2p-record/commit/992677bd6bf432b3ce894c53ac7a721e2dd44bf9))
109 |
110 | ### [1.0.4](https://github.com/libp2p/js-libp2p-record/compare/v1.0.3...v1.0.4) (2022-04-14)
111 |
112 |
113 | ### Bug Fixes
114 |
115 | * pad ns correctly ([#41](https://github.com/libp2p/js-libp2p-record/issues/41)) ([18030d9](https://github.com/libp2p/js-libp2p-record/commit/18030d9d3832a7d09dee928923909875a5780a2f))
116 |
117 | ### [1.0.3](https://github.com/libp2p/js-libp2p-record/compare/v1.0.2...v1.0.3) (2022-04-13)
118 |
119 |
120 | ### Bug Fixes
121 |
122 | * update interfaces ([#40](https://github.com/libp2p/js-libp2p-record/issues/40)) ([e2713a3](https://github.com/libp2p/js-libp2p-record/commit/e2713a3a6b5351e2dc012cf734ff1c945479920b))
123 |
124 | ### [1.0.2](https://github.com/libp2p/js-libp2p-record/compare/v1.0.1...v1.0.2) (2022-04-09)
125 |
126 |
127 | ### Bug Fixes
128 |
129 | * use protons ([#39](https://github.com/libp2p/js-libp2p-record/issues/39)) ([10b4cc2](https://github.com/libp2p/js-libp2p-record/commit/10b4cc2600e8f3bed9a2d646b68b0b2107e1caa4))
130 |
131 | ### [1.0.1](https://github.com/libp2p/js-libp2p-record/compare/v1.0.0...v1.0.1) (2022-03-24)
132 |
133 |
134 | ### Bug Fixes
135 |
136 | * export selector/validators with the same name as their prefix ([#34](https://github.com/libp2p/js-libp2p-record/issues/34)) ([4913d1f](https://github.com/libp2p/js-libp2p-record/commit/4913d1fec2ed92d4803f3497bef81142bd560a91))
137 |
138 | ## [1.0.0](https://github.com/libp2p/js-libp2p-record/compare/v0.10.6...v1.0.0) (2022-02-18)
139 |
140 |
141 | ### ⚠ BREAKING CHANGES
142 |
143 | * switch to named exports, ESM only
144 |
145 | ### Features
146 |
147 | * convert to typescript ([#32](https://github.com/libp2p/js-libp2p-record/issues/32)) ([89cc2ef](https://github.com/libp2p/js-libp2p-record/commit/89cc2ef5234835c82ea29ff54a4887d630921ae3))
148 |
149 | ## [0.10.6](https://github.com/libp2p/js-libp2p-record/compare/v0.10.5...v0.10.6) (2021-09-24)
150 |
151 |
152 | ### Bug Fixes
153 |
154 | * auto select if only one record ([#31](https://github.com/libp2p/js-libp2p-record/issues/31)) ([53bc7f2](https://github.com/libp2p/js-libp2p-record/commit/53bc7f2627a95256337033977a05df54a534f951))
155 |
156 |
157 |
158 | ## [0.10.5](https://github.com/libp2p/js-libp2p-record/compare/v0.10.4...v0.10.5) (2021-08-18)
159 |
160 |
161 |
162 | ## [0.10.4](https://github.com/libp2p/js-libp2p-record/compare/v0.10.3...v0.10.4) (2021-07-07)
163 |
164 |
165 |
166 | ## [0.10.3](https://github.com/libp2p/js-libp2p-record/compare/v0.10.2...v0.10.3) (2021-04-22)
167 |
168 |
169 | ### Bug Fixes
170 |
171 | * use dht selectors and validators from interfaces ([#28](https://github.com/libp2p/js-libp2p-record/issues/28)) ([7b211a5](https://github.com/libp2p/js-libp2p-record/commit/7b211a528675018abbc8e4674bedbdd5ab7b5eea))
172 |
173 |
174 |
175 | ## [0.10.2](https://github.com/libp2p/js-libp2p-record/compare/v0.10.1...v0.10.2) (2021-04-20)
176 |
177 |
178 | ### Bug Fixes
179 |
180 | * specify pbjs root ([#27](https://github.com/libp2p/js-libp2p-record/issues/27)) ([32ddb1d](https://github.com/libp2p/js-libp2p-record/commit/32ddb1deec71543d0ef34157b6ef2d271e8408f5))
181 |
182 |
183 |
184 | ## [0.10.1](https://github.com/libp2p/js-libp2p-record/compare/v0.10.0...v0.10.1) (2021-04-07)
185 |
186 |
187 |
188 | # [0.10.0](https://github.com/libp2p/js-libp2p-record/compare/v0.8.0...v0.10.0) (2021-02-02)
189 |
190 |
191 | ### Features
192 |
193 | * add types and update deps ([#25](https://github.com/libp2p/js-libp2p-record/issues/25)) ([e2395de](https://github.com/libp2p/js-libp2p-record/commit/e2395de924a9c71d761c6ea3f5aab2844b252591))
194 |
195 |
196 |
197 |
198 | # [0.9.0](https://github.com/libp2p/js-libp2p-record/compare/v0.8.0...v0.9.0) (2020-08-07)
199 |
200 |
201 |
202 |
203 | # [0.8.0](https://github.com/libp2p/js-libp2p-record/compare/v0.7.3...v0.8.0) (2020-07-29)
204 |
205 |
206 | ### Bug Fixes
207 |
208 | * support uint8arrays in place of node buffers ([#23](https://github.com/libp2p/js-libp2p-record/issues/23)) ([3b99ee1](https://github.com/libp2p/js-libp2p-record/commit/3b99ee1))
209 |
210 |
211 | ### BREAKING CHANGES
212 |
213 | * takes Uint8Arrays as well as Node Buffers
214 |
215 |
216 |
217 |
218 | ## [0.7.3](https://github.com/libp2p/js-libp2p-record/compare/v0.7.2...v0.7.3) (2020-04-27)
219 |
220 |
221 | ### Bug Fixes
222 |
223 | * remove buffer ([#21](https://github.com/libp2p/js-libp2p-record/issues/21)) ([80fb248](https://github.com/libp2p/js-libp2p-record/commit/80fb248))
224 |
225 |
226 |
227 |
228 | ## [0.7.2](https://github.com/libp2p/js-libp2p-record/compare/v0.7.1...v0.7.2) (2020-02-13)
229 |
230 |
231 | ### Bug Fixes
232 |
233 | * remove use of assert module ([#18](https://github.com/libp2p/js-libp2p-record/issues/18)) ([57e24a7](https://github.com/libp2p/js-libp2p-record/commit/57e24a7))
234 |
235 |
236 |
237 |
238 | ## [0.7.1](https://github.com/libp2p/js-libp2p-record/compare/v0.7.0...v0.7.1) (2020-01-03)
239 |
240 |
241 |
242 |
243 | # [0.7.0](https://github.com/libp2p/js-libp2p-record/compare/v0.6.3...v0.7.0) (2019-08-16)
244 |
245 |
246 | ### Code Refactoring
247 |
248 | * convert from callbacks to async ([#13](https://github.com/libp2p/js-libp2p-record/issues/13)) ([42eab95](https://github.com/libp2p/js-libp2p-record/commit/42eab95))
249 |
250 |
251 | ### BREAKING CHANGES
252 |
253 | * All places in the API that used callbacks are now replaced with async/await
254 |
255 |
256 |
257 |
258 | ## [0.6.3](https://github.com/libp2p/js-libp2p-record/compare/v0.6.2...v0.6.3) (2019-05-23)
259 |
260 |
261 | ### Bug Fixes
262 |
263 | * remove leftpad ([#16](https://github.com/libp2p/js-libp2p-record/issues/16)) ([4f46885](https://github.com/libp2p/js-libp2p-record/commit/4f46885))
264 |
265 |
266 |
267 |
268 | ## [0.6.2](https://github.com/libp2p/js-libp2p-record/compare/v0.6.1...v0.6.2) (2019-02-20)
269 |
270 |
271 |
272 |
273 | ## [0.6.1](https://github.com/libp2p/js-libp2p-record/compare/v0.6.0...v0.6.1) (2018-11-08)
274 |
275 |
276 |
277 |
278 | # [0.6.0](https://github.com/libp2p/js-libp2p-record/compare/v0.5.1...v0.6.0) (2018-10-18)
279 |
280 |
281 | ### Features
282 |
283 | * new record definition ([#8](https://github.com/libp2p/js-libp2p-record/issues/8)) ([10177ae](https://github.com/libp2p/js-libp2p-record/commit/10177ae))
284 |
285 |
286 | ### BREAKING CHANGES
287 |
288 | * having the libp2p-record protobuf definition compliant with go-libp2p-record. Author and signature were removed.
289 |
290 |
291 |
292 |
293 | ## [0.5.1](https://github.com/libp2p/js-libp2p-record/compare/v0.5.0...v0.5.1) (2017-09-07)
294 |
295 |
296 | ### Features
297 |
298 | * replace protocol-buffers with protons ([#5](https://github.com/libp2p/js-libp2p-record/issues/5)) ([8774a4f](https://github.com/libp2p/js-libp2p-record/commit/8774a4f))
299 |
300 |
301 |
302 |
303 | # [0.5.0](https://github.com/libp2p/js-libp2p-record/compare/v0.4.0...v0.5.0) (2017-09-03)
304 |
305 |
306 | ### Features
307 |
308 | * p2p addrs situation ([#4](https://github.com/libp2p/js-libp2p-record/issues/4)) ([bcba43c](https://github.com/libp2p/js-libp2p-record/commit/bcba43c))
309 |
310 |
311 |
312 |
313 | # [0.4.0](https://github.com/libp2p/js-libp2p-record/compare/v0.3.1...v0.4.0) (2017-07-22)
314 |
315 |
316 |
317 |
318 | ## [0.3.1](https://github.com/libp2p/js-libp2p-record/compare/v0.3.0...v0.3.1) (2017-03-29)
319 |
320 |
321 |
322 |
323 | # 0.3.0 (2017-03-29)
324 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | This project is dual licensed under MIT and Apache-2.0.
2 |
3 | MIT: https://www.opensource.org/licenses/mit
4 | Apache-2.0: https://www.apache.org/licenses/license-2.0
5 |
--------------------------------------------------------------------------------
/LICENSE-APACHE:
--------------------------------------------------------------------------------
1 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
2 |
3 | http://www.apache.org/licenses/LICENSE-2.0
4 |
5 | Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
6 |
--------------------------------------------------------------------------------
/LICENSE-MIT:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is
8 | furnished to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in
11 | all copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | THE SOFTWARE.
20 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 📁 Archived - this module has been merged into [js-libp2p](https://github.com/libp2p/js-libp2p/tree/master/packages/kad-dht)
2 |
3 | # @libp2p/record
4 |
5 | [](http://libp2p.io/)
6 | [](https://discuss.libp2p.io)
7 | [](https://codecov.io/gh/libp2p/js-libp2p-record)
8 | [](https://github.com/libp2p/js-libp2p-record/actions/workflows/js-test-and-release.yml?query=branch%3Amaster)
9 |
10 | > libp2p record implementation
11 |
12 | ## Table of contents
13 |
14 | - [Install](#install)
15 | - [Browser `
33 | ```
34 |
35 | ## Description
36 |
37 | Implementation of [go-libp2p-record](https://github.com/libp2p/go-libp2p-record) in JavaScript.
38 |
39 | ## API Docs
40 |
41 | -
42 |
43 | ## License
44 |
45 | Licensed under either of
46 |
47 | - Apache 2.0, ([LICENSE-APACHE](LICENSE-APACHE) / )
48 | - MIT ([LICENSE-MIT](LICENSE-MIT) / )
49 |
50 | ## Contribution
51 |
52 | Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.
53 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@libp2p/record",
3 | "version": "3.0.4",
4 | "description": "libp2p record implementation",
5 | "author": "Friedel Ziegelmayer ",
6 | "license": "Apache-2.0 OR MIT",
7 | "homepage": "https://github.com/libp2p/js-libp2p-record#readme",
8 | "repository": {
9 | "type": "git",
10 | "url": "git+https://github.com/libp2p/js-libp2p-record.git"
11 | },
12 | "bugs": {
13 | "url": "https://github.com/libp2p/js-libp2p-record/issues"
14 | },
15 | "keywords": [
16 | "IPFS"
17 | ],
18 | "engines": {
19 | "node": ">=16.0.0",
20 | "npm": ">=7.0.0"
21 | },
22 | "type": "module",
23 | "types": "./dist/src/index.d.ts",
24 | "typesVersions": {
25 | "*": {
26 | "*": [
27 | "*",
28 | "dist/*",
29 | "dist/src/*",
30 | "dist/src/*/index"
31 | ],
32 | "src/*": [
33 | "*",
34 | "dist/*",
35 | "dist/src/*",
36 | "dist/src/*/index"
37 | ]
38 | }
39 | },
40 | "files": [
41 | "src",
42 | "dist",
43 | "!dist/test",
44 | "!**/*.tsbuildinfo"
45 | ],
46 | "exports": {
47 | ".": {
48 | "types": "./dist/src/index.d.ts",
49 | "import": "./dist/src/index.js"
50 | },
51 | "./selectors": {
52 | "types": "./dist/src/selectors.d.ts",
53 | "import": "./dist/src/selectors.js"
54 | },
55 | "./validators": {
56 | "types": "./dist/src/validators.d.ts",
57 | "import": "./dist/src/validators.js"
58 | }
59 | },
60 | "eslintConfig": {
61 | "extends": "ipfs",
62 | "parserOptions": {
63 | "sourceType": "module"
64 | },
65 | "ignorePatterns": [
66 | "src/record.d.ts"
67 | ]
68 | },
69 | "release": {
70 | "branches": [
71 | "master"
72 | ],
73 | "plugins": [
74 | [
75 | "@semantic-release/commit-analyzer",
76 | {
77 | "preset": "conventionalcommits",
78 | "releaseRules": [
79 | {
80 | "breaking": true,
81 | "release": "major"
82 | },
83 | {
84 | "revert": true,
85 | "release": "patch"
86 | },
87 | {
88 | "type": "feat",
89 | "release": "minor"
90 | },
91 | {
92 | "type": "fix",
93 | "release": "patch"
94 | },
95 | {
96 | "type": "docs",
97 | "release": "patch"
98 | },
99 | {
100 | "type": "test",
101 | "release": "patch"
102 | },
103 | {
104 | "type": "deps",
105 | "release": "patch"
106 | },
107 | {
108 | "scope": "no-release",
109 | "release": false
110 | }
111 | ]
112 | }
113 | ],
114 | [
115 | "@semantic-release/release-notes-generator",
116 | {
117 | "preset": "conventionalcommits",
118 | "presetConfig": {
119 | "types": [
120 | {
121 | "type": "feat",
122 | "section": "Features"
123 | },
124 | {
125 | "type": "fix",
126 | "section": "Bug Fixes"
127 | },
128 | {
129 | "type": "chore",
130 | "section": "Trivial Changes"
131 | },
132 | {
133 | "type": "docs",
134 | "section": "Documentation"
135 | },
136 | {
137 | "type": "deps",
138 | "section": "Dependencies"
139 | },
140 | {
141 | "type": "test",
142 | "section": "Tests"
143 | }
144 | ]
145 | }
146 | }
147 | ],
148 | "@semantic-release/changelog",
149 | "@semantic-release/npm",
150 | "@semantic-release/github",
151 | "@semantic-release/git"
152 | ]
153 | },
154 | "scripts": {
155 | "clean": "aegir clean",
156 | "lint": "aegir lint",
157 | "dep-check": "aegir dep-check -i protons",
158 | "test": "aegir test",
159 | "test:node": "aegir test -t node",
160 | "test:chrome": "aegir test -t browser",
161 | "test:chrome-webworker": "aegir test -t webworker",
162 | "test:firefox": "aegir test -t browser -- --browser firefox",
163 | "test:firefox-webworker": "aegir test -t webworker -- --browser firefox",
164 | "build": "aegir build",
165 | "generate": "protons ./src/record.proto",
166 | "release": "aegir release",
167 | "docs": "aegir docs"
168 | },
169 | "dependencies": {
170 | "@libp2p/interface-dht": "^2.0.0",
171 | "@libp2p/interfaces": "^3.2.0",
172 | "multiformats": "^11.0.0",
173 | "protons-runtime": "^5.0.0",
174 | "uint8arraylist": "^2.1.1",
175 | "uint8arrays": "^4.0.2"
176 | },
177 | "devDependencies": {
178 | "@libp2p/crypto": "^1.0.11",
179 | "aegir": "^39.0.10",
180 | "protons": "^7.0.2"
181 | }
182 | }
183 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | import {
2 | Record
3 | } from './record.js'
4 | import * as utils from './utils.js'
5 | import type { Uint8ArrayList } from 'uint8arraylist'
6 |
7 | export class Libp2pRecord {
8 | public key: Uint8Array
9 | public value: Uint8Array
10 | public timeReceived: Date
11 |
12 | constructor (key: Uint8Array, value: Uint8Array, timeReceived: Date) {
13 | if (!(key instanceof Uint8Array)) {
14 | throw new Error('key must be a Uint8Array')
15 | }
16 |
17 | if (!(value instanceof Uint8Array)) {
18 | throw new Error('value must be a Uint8Array')
19 | }
20 |
21 | this.key = key
22 | this.value = value
23 | this.timeReceived = timeReceived
24 | }
25 |
26 | serialize (): Uint8Array {
27 | return Record.encode(this.prepareSerialize())
28 | }
29 |
30 | /**
31 | * Return the object format ready to be given to the protobuf library.
32 | */
33 | prepareSerialize (): Record {
34 | return {
35 | key: this.key,
36 | value: this.value,
37 | timeReceived: utils.toRFC3339(this.timeReceived)
38 | }
39 | }
40 |
41 | /**
42 | * Decode a protobuf encoded record
43 | */
44 | static deserialize (raw: Uint8Array | Uint8ArrayList): Libp2pRecord {
45 | const rec = Record.decode(raw)
46 |
47 | return new Libp2pRecord(rec.key, rec.value, new Date(rec.timeReceived))
48 | }
49 |
50 | /**
51 | * Create a record from the raw object returned from the protobuf library
52 | */
53 | static fromDeserialized (obj: Record): Libp2pRecord {
54 | const recvtime = utils.parseRFC3339(obj.timeReceived)
55 |
56 | if (obj.key == null) {
57 | throw new Error('key missing from deserialized object')
58 | }
59 |
60 | if (obj.value == null) {
61 | throw new Error('value missing from deserialized object')
62 | }
63 |
64 | const rec = new Libp2pRecord(
65 | obj.key, obj.value, recvtime
66 | )
67 |
68 | return rec
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/src/record.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 |
3 | // Record represents a dht record that contains a value
4 | // for a key value pair
5 | message Record {
6 | // The key that references this record
7 | bytes key = 1;
8 |
9 | // The actual value this record is storing
10 | bytes value = 2;
11 |
12 | // Note: These fields were removed from the Record message
13 | // hash of the authors public key
14 | // optional bytes author = 3;
15 | // A PKI signature for the key+value+author
16 | // optional bytes signature = 4;
17 |
18 | // Time the record was received, set by receiver
19 | string timeReceived = 5;
20 | }
21 |
--------------------------------------------------------------------------------
/src/record.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable import/export */
2 | /* eslint-disable complexity */
3 | /* eslint-disable @typescript-eslint/no-namespace */
4 | /* eslint-disable @typescript-eslint/no-unnecessary-boolean-literal-compare */
5 | /* eslint-disable @typescript-eslint/no-empty-interface */
6 |
7 | import { encodeMessage, decodeMessage, message } from 'protons-runtime'
8 | import type { Codec } from 'protons-runtime'
9 | import type { Uint8ArrayList } from 'uint8arraylist'
10 |
11 | export interface Record {
12 | key: Uint8Array
13 | value: Uint8Array
14 | timeReceived: string
15 | }
16 |
17 | export namespace Record {
18 | let _codec: Codec
19 |
20 | export const codec = (): Codec => {
21 | if (_codec == null) {
22 | _codec = message((obj, w, opts = {}) => {
23 | if (opts.lengthDelimited !== false) {
24 | w.fork()
25 | }
26 |
27 | if ((obj.key != null && obj.key.byteLength > 0)) {
28 | w.uint32(10)
29 | w.bytes(obj.key)
30 | }
31 |
32 | if ((obj.value != null && obj.value.byteLength > 0)) {
33 | w.uint32(18)
34 | w.bytes(obj.value)
35 | }
36 |
37 | if ((obj.timeReceived != null && obj.timeReceived !== '')) {
38 | w.uint32(42)
39 | w.string(obj.timeReceived)
40 | }
41 |
42 | if (opts.lengthDelimited !== false) {
43 | w.ldelim()
44 | }
45 | }, (reader, length) => {
46 | const obj: any = {
47 | key: new Uint8Array(0),
48 | value: new Uint8Array(0),
49 | timeReceived: ''
50 | }
51 |
52 | const end = length == null ? reader.len : reader.pos + length
53 |
54 | while (reader.pos < end) {
55 | const tag = reader.uint32()
56 |
57 | switch (tag >>> 3) {
58 | case 1:
59 | obj.key = reader.bytes()
60 | break
61 | case 2:
62 | obj.value = reader.bytes()
63 | break
64 | case 5:
65 | obj.timeReceived = reader.string()
66 | break
67 | default:
68 | reader.skipType(tag & 7)
69 | break
70 | }
71 | }
72 |
73 | return obj
74 | })
75 | }
76 |
77 | return _codec
78 | }
79 |
80 | export const encode = (obj: Partial): Uint8Array => {
81 | return encodeMessage(obj, Record.codec())
82 | }
83 |
84 | export const decode = (buf: Uint8Array | Uint8ArrayList): Record => {
85 | return decodeMessage(buf, Record.codec())
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/src/selectors.ts:
--------------------------------------------------------------------------------
1 | import { CodeError } from '@libp2p/interfaces/errors'
2 | import { toString as uint8ArrayToString } from 'uint8arrays/to-string'
3 | import type { Selectors } from '@libp2p/interface-dht'
4 |
5 | /**
6 | * Select the best record out of the given records
7 | */
8 | export function bestRecord (selectors: Selectors, k: Uint8Array, records: Uint8Array[]): number {
9 | if (records.length === 0) {
10 | const errMsg = 'No records given'
11 |
12 | throw new CodeError(errMsg, 'ERR_NO_RECORDS_RECEIVED')
13 | }
14 |
15 | const kStr = uint8ArrayToString(k)
16 | const parts = kStr.split('/')
17 |
18 | if (parts.length < 3) {
19 | const errMsg = 'Record key does not have a selector function'
20 |
21 | throw new CodeError(errMsg, 'ERR_NO_SELECTOR_FUNCTION_FOR_RECORD_KEY')
22 | }
23 |
24 | const selector = selectors[parts[1].toString()]
25 |
26 | if (selector == null) {
27 | const errMsg = `Unrecognized key prefix: ${parts[1]}`
28 |
29 | throw new CodeError(errMsg, 'ERR_UNRECOGNIZED_KEY_PREFIX')
30 | }
31 |
32 | if (records.length === 1) {
33 | return 0
34 | }
35 |
36 | return selector(k, records)
37 | }
38 |
39 | /**
40 | * Best record selector, for public key records.
41 | * Simply returns the first record, as all valid public key
42 | * records are equal
43 | */
44 | function publickKey (k: Uint8Array, records: Uint8Array[]): number {
45 | return 0
46 | }
47 |
48 | export const selectors: Selectors = {
49 | pk: publickKey
50 | }
51 |
--------------------------------------------------------------------------------
/src/utils.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Convert a JavaScript date into an `RFC3339Nano` formatted
3 | * string
4 | */
5 | export function toRFC3339 (time: Date): string {
6 | const year = time.getUTCFullYear()
7 | const month = String(time.getUTCMonth() + 1).padStart(2, '0')
8 | const day = String(time.getUTCDate()).padStart(2, '0')
9 | const hour = String(time.getUTCHours()).padStart(2, '0')
10 | const minute = String(time.getUTCMinutes()).padStart(2, '0')
11 | const seconds = String(time.getUTCSeconds()).padStart(2, '0')
12 | const milliseconds = time.getUTCMilliseconds()
13 | const nanoseconds = String(milliseconds * 1000 * 1000).padStart(9, '0')
14 |
15 | return `${year}-${month}-${day}T${hour}:${minute}:${seconds}.${nanoseconds}Z`
16 | }
17 |
18 | /**
19 | * Parses a date string formatted as `RFC3339Nano` into a
20 | * JavaScript Date object
21 | */
22 | export function parseRFC3339 (time: string): Date {
23 | const rfc3339Matcher = new RegExp(
24 | // 2006-01-02T
25 | '(\\d{4})-(\\d{2})-(\\d{2})T' +
26 | // 15:04:05
27 | '(\\d{2}):(\\d{2}):(\\d{2})' +
28 | // .999999999Z
29 | '\\.(\\d+)Z'
30 | )
31 | const m = String(time).trim().match(rfc3339Matcher)
32 |
33 | if (m == null) {
34 | throw new Error('Invalid format')
35 | }
36 |
37 | const year = parseInt(m[1], 10)
38 | const month = parseInt(m[2], 10) - 1
39 | const date = parseInt(m[3], 10)
40 | const hour = parseInt(m[4], 10)
41 | const minute = parseInt(m[5], 10)
42 | const second = parseInt(m[6], 10)
43 | const millisecond = parseInt(m[7].slice(0, -6), 10)
44 |
45 | return new Date(Date.UTC(year, month, date, hour, minute, second, millisecond))
46 | }
47 |
--------------------------------------------------------------------------------
/src/validators.ts:
--------------------------------------------------------------------------------
1 | import { CodeError } from '@libp2p/interfaces/errors'
2 | import { sha256 } from 'multiformats/hashes/sha2'
3 | import { equals as uint8ArrayEquals } from 'uint8arrays/equals'
4 | import { toString as uint8ArrayToString } from 'uint8arrays/to-string'
5 | import type { Libp2pRecord } from './index.js'
6 | import type { Validators } from '@libp2p/interface-dht'
7 |
8 | /**
9 | * Checks a record and ensures it is still valid.
10 | * It runs the needed validators.
11 | * If verification fails the returned Promise will reject with the error.
12 | */
13 | export async function verifyRecord (validators: Validators, record: Libp2pRecord): Promise {
14 | const key = record.key
15 | const keyString = uint8ArrayToString(key)
16 | const parts = keyString.split('/')
17 |
18 | if (parts.length < 3) {
19 | // No validator available
20 | return
21 | }
22 |
23 | const validator = validators[parts[1].toString()]
24 |
25 | if (validator == null) {
26 | const errMsg = 'Invalid record keytype'
27 |
28 | throw new CodeError(errMsg, 'ERR_INVALID_RECORD_KEY_TYPE')
29 | }
30 |
31 | await validator(key, record.value)
32 | }
33 |
34 | /**
35 | * Validator for public key records.
36 | * Verifies that the passed in record value is the PublicKey
37 | * that matches the passed in key.
38 | * If validation fails the returned Promise will reject with the error.
39 | *
40 | * @param {Uint8Array} key - A valid key is of the form `'/pk/'`
41 | * @param {Uint8Array} publicKey - The public key to validate against (protobuf encoded).
42 | */
43 | const validatePublicKeyRecord = async (key: Uint8Array, publicKey: Uint8Array): Promise => {
44 | if (!(key instanceof Uint8Array)) {
45 | throw new CodeError('"key" must be a Uint8Array', 'ERR_INVALID_RECORD_KEY_NOT_BUFFER')
46 | }
47 |
48 | if (key.byteLength < 5) {
49 | throw new CodeError('invalid public key record', 'ERR_INVALID_RECORD_KEY_TOO_SHORT')
50 | }
51 |
52 | const prefix = uint8ArrayToString(key.subarray(0, 4))
53 |
54 | if (prefix !== '/pk/') {
55 | throw new CodeError('key was not prefixed with /pk/', 'ERR_INVALID_RECORD_KEY_BAD_PREFIX')
56 | }
57 |
58 | const keyhash = key.slice(4)
59 |
60 | const publicKeyHash = await sha256.digest(publicKey)
61 |
62 | if (!uint8ArrayEquals(keyhash, publicKeyHash.bytes)) {
63 | throw new CodeError('public key does not match passed in key', 'ERR_INVALID_RECORD_HASH_MISMATCH')
64 | }
65 | }
66 |
67 | export const validators: Validators = {
68 | pk: validatePublicKeyRecord
69 | }
70 |
--------------------------------------------------------------------------------
/test/fixtures/go-key-records.ts:
--------------------------------------------------------------------------------
1 | import { base64pad } from 'multiformats/bases/base64'
2 |
3 | export const publicKey = base64pad.decode(
4 | 'MCAASXjBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQDjXAQQMal4SB2tSnX6NJIPmC69/BT8A8jc7/gDUZNkEhdhYHvc7k7S4vntV/c92nJGxNdop9fKJyevuNMuXhhHAgMBAAE='
5 | )
6 |
--------------------------------------------------------------------------------
/test/fixtures/go-record.ts:
--------------------------------------------------------------------------------
1 | import { base16 } from 'multiformats/bases/base16'
2 |
3 | // Fixtures generated using gore (https://github.com/motemen/gore)
4 | //
5 | // :import github.com/libp2p/go-libp2p-record
6 | // :import github.com/libp2p/go-libp2p-crypto
7 | //
8 | // priv, pub, err := crypto.GenerateKeyPair(crypto.RSA, 1024)
9 | //
10 | // rec, err := record.MakePutRecord(priv, "hello", []byte("world"), false)
11 | // rec2, err := recordd.MakePutRecord(priv, "hello", []byte("world"), true)
12 | //
13 | // :import github.com/gogo/protobuf/proto
14 | // enc, err := proto.Marshal(rec)
15 | // enc2, err := proto.Marshal(rec2)
16 | //
17 | // :import io/ioutil
18 | // ioutil.WriteFile("js-libp2p-record/test/fixtures/record.bin", enc, 0644)
19 | // ioutil.WriteFile("js-libp2p-record/test/fixtures/record-signed.bin", enc2, 0644)
20 | export const serialized = base16.decode(
21 | 'f0a0568656c6c6f1205776f726c641a2212201bd5175b1d4123ee29665348c60ea5cf5ac62e2e05215b97a7b9a9b0cf71d116'
22 | )
23 |
24 | export const serializedSigned = base16.decode(
25 | 'f0a0568656c6c6f1205776f726c641a2212201bd5175b1d4123ee29665348c60ea5cf5ac62e2e05215b97a7b9a9b0cf71d116228001500fe7505698b8a873ccde6f1d36a2be662d57807490d9a9959540f2645a454bf615215092e10123f6ffc4ed694711bfbb1d5ccb62f3da83cf4528ee577a96b6cf0272eef9a920bd56459993690060353b72c22b8c03ad2a33894522dac338905b201179a85cb5e2fc68ed58be96cf89beec6dc0913887dddc10f202a2a1b117'
26 | )
27 |
--------------------------------------------------------------------------------
/test/record.spec.ts:
--------------------------------------------------------------------------------
1 | /* eslint-env mocha */
2 | import { expect } from 'aegir/chai'
3 | import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'
4 | import { Libp2pRecord } from '../src/index.js'
5 | import * as fixture from './fixtures/go-record.js'
6 |
7 | const date = new Date()
8 |
9 | describe('record', () => {
10 | it('new', () => {
11 | const rec = new Libp2pRecord(
12 | uint8ArrayFromString('hello'),
13 | uint8ArrayFromString('world'),
14 | new Date()
15 | )
16 |
17 | expect(rec).to.have.property('key').eql(uint8ArrayFromString('hello'))
18 | expect(rec).to.have.property('value').eql(uint8ArrayFromString('world'))
19 | })
20 |
21 | it('serialize & deserialize', () => {
22 | const rec = new Libp2pRecord(uint8ArrayFromString('hello'), uint8ArrayFromString('world'), date)
23 | const dec = Libp2pRecord.deserialize(rec.serialize())
24 |
25 | expect(dec).to.have.property('key').eql(uint8ArrayFromString('hello'))
26 | expect(dec).to.have.property('value').eql(uint8ArrayFromString('world'))
27 | expect(dec.timeReceived).to.be.eql(date)
28 | })
29 |
30 | it('serialize & deserialize with padding', () => {
31 | // m/d/h/m/s/ms all need padding with 0s when converted to RFC3339 format
32 | const date = new Date('2022-04-03T01:04:08.078Z')
33 |
34 | const rec = new Libp2pRecord(uint8ArrayFromString('hello'), uint8ArrayFromString('world'), date)
35 | const dec = Libp2pRecord.deserialize(rec.serialize())
36 |
37 | expect(dec).to.have.property('key').eql(uint8ArrayFromString('hello'))
38 | expect(dec).to.have.property('value').eql(uint8ArrayFromString('world'))
39 | expect(dec.timeReceived).to.be.eql(date)
40 | })
41 |
42 | describe('go interop', () => {
43 | it('no signature', () => {
44 | const dec = Libp2pRecord.deserialize(fixture.serialized)
45 | expect(dec).to.have.property('key').eql(uint8ArrayFromString('hello'))
46 | expect(dec).to.have.property('value').eql(uint8ArrayFromString('world'))
47 | })
48 | })
49 | })
50 |
--------------------------------------------------------------------------------
/test/selection.spec.ts:
--------------------------------------------------------------------------------
1 | /* eslint max-nested-callbacks: ["error", 8] */
2 | /* eslint-env mocha */
3 |
4 | import { expect } from 'aegir/chai'
5 | import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'
6 | import * as selection from '../src/selectors.js'
7 | import type { Selectors } from '@libp2p/interface-dht'
8 |
9 | const records = [new Uint8Array(), uint8ArrayFromString('hello')]
10 |
11 | describe('selection', () => {
12 | describe('bestRecord', () => {
13 | it('throws no records given when no records received', () => {
14 | expect(
15 | () => selection.bestRecord({}, uint8ArrayFromString('/'), [])
16 | ).to.throw(
17 | /No records given/
18 | )
19 | })
20 |
21 | it('throws on missing selector in the record key', () => {
22 | expect(
23 | () => selection.bestRecord({}, uint8ArrayFromString('/'), records)
24 | ).to.throw(
25 | /Record key does not have a selector function/
26 | )
27 | })
28 |
29 | it('throws on unknown key prefix', () => {
30 | expect(
31 | // @ts-expect-error invalid input
32 | () => selection.bestRecord({ world () {} }, uint8ArrayFromString('/hello/'), records)
33 | ).to.throw(
34 | /Unrecognized key prefix: hello/
35 | )
36 | })
37 |
38 | it('returns the index from the matching selector', () => {
39 | const selectors: Selectors = {
40 | hello (k, recs) {
41 | expect(k).to.be.eql(uint8ArrayFromString('/hello/world'))
42 | expect(recs).to.be.eql(records)
43 |
44 | return 1
45 | }
46 | }
47 |
48 | expect(
49 | selection.bestRecord(selectors, uint8ArrayFromString('/hello/world'), records)
50 | ).to.equal(
51 | 1
52 | )
53 | })
54 | })
55 |
56 | describe('selectors', () => {
57 | it('public key', () => {
58 | expect(
59 | selection.selectors.pk(uint8ArrayFromString('/hello/world'), records)
60 | ).to.equal(
61 | 0
62 | )
63 | })
64 |
65 | it('returns the first record when there is only one to select', () => {
66 | expect(
67 | selection.selectors.pk(uint8ArrayFromString('/hello/world'), [records[0]])
68 | ).to.equal(
69 | 0
70 | )
71 | })
72 | })
73 | })
74 |
--------------------------------------------------------------------------------
/test/utils.spec.ts:
--------------------------------------------------------------------------------
1 | /* eslint-env mocha */
2 |
3 | import { expect } from 'aegir/chai'
4 | import * as utils from '../src/utils.js'
5 |
6 | const dates = [{
7 | obj: new Date(Date.UTC(2016, 0, 1, 8, 22, 33, 392)),
8 | str: '2016-01-01T08:22:33.392000000Z'
9 | }, {
10 | obj: new Date(Date.UTC(2016, 11, 30, 20, 2, 3, 392)),
11 | str: '2016-12-30T20:02:03.392000000Z'
12 | }, {
13 | obj: new Date(Date.UTC(2016, 11, 30, 20, 2, 5, 297)),
14 | str: '2016-12-30T20:02:05.297000000Z'
15 | }, {
16 | obj: new Date(Date.UTC(2012, 1, 25, 10, 10, 10, 10)),
17 | str: '2012-02-25T10:10:10.010000000Z'
18 | }]
19 |
20 | describe('utils', () => {
21 | it('toRFC3339', () => {
22 | dates.forEach((c) => {
23 | expect(utils.toRFC3339(c.obj)).to.be.eql(c.str)
24 | })
25 | })
26 |
27 | it('parseRFC3339', () => {
28 | dates.forEach((c) => {
29 | expect(utils.parseRFC3339(c.str)).to.be.eql(c.obj)
30 | })
31 | })
32 |
33 | it('to and from RFC3339', () => {
34 | dates.forEach((c) => {
35 | expect(
36 | utils.parseRFC3339(utils.toRFC3339(c.obj))
37 | ).to.be.eql(
38 | c.obj
39 | )
40 | expect(
41 | utils.toRFC3339(utils.parseRFC3339(c.str))
42 | ).to.be.eql(
43 | c.str
44 | )
45 | })
46 | })
47 | })
48 |
--------------------------------------------------------------------------------
/test/validator.spec.ts:
--------------------------------------------------------------------------------
1 | /* eslint max-nested-callbacks: ["error", 8] */
2 | /* eslint-env mocha */
3 |
4 | import { generateKeyPair, unmarshalPublicKey } from '@libp2p/crypto/keys'
5 | import { expect } from 'aegir/chai'
6 | import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'
7 | import { Libp2pRecord } from '../src/index.js'
8 | import * as validator from '../src/validators.js'
9 | import * as fixture from './fixtures/go-key-records.js'
10 | import type { Validators } from '@libp2p/interface-dht'
11 |
12 | interface Cases {
13 | valid: {
14 | publicKey: Uint8Array[]
15 | }
16 | invalid: {
17 | publicKey: Array<{
18 | data: Uint8Array
19 | code: string
20 | }>
21 | }
22 | }
23 |
24 | const generateCases = (hash: Uint8Array): Cases => {
25 | return {
26 | valid: {
27 | publicKey: [
28 | Uint8Array.of(
29 | ...uint8ArrayFromString('/pk/'),
30 | ...hash
31 | )
32 | ]
33 | },
34 | invalid: {
35 | publicKey: [{
36 | data: uint8ArrayFromString('/pk/'),
37 | code: 'ERR_INVALID_RECORD_KEY_TOO_SHORT'
38 | }, {
39 | data: Uint8Array.of(...uint8ArrayFromString('/pk/'), ...uint8ArrayFromString('random')),
40 | code: 'ERR_INVALID_RECORD_HASH_MISMATCH'
41 | }, {
42 | data: hash,
43 | code: 'ERR_INVALID_RECORD_KEY_BAD_PREFIX'
44 | }, {
45 | // @ts-expect-error invalid input
46 | data: 'not a buffer',
47 | code: 'ERR_INVALID_RECORD_KEY_NOT_BUFFER'
48 | }]
49 | }
50 | }
51 | }
52 |
53 | describe('validator', () => {
54 | let key: any
55 | let hash: Uint8Array
56 | let cases: Cases
57 |
58 | before(async () => {
59 | key = await generateKeyPair('RSA', 1024)
60 | hash = await key.public.hash()
61 | cases = generateCases(hash)
62 | })
63 |
64 | describe('verifyRecord', () => {
65 | it('calls matching validator', async () => {
66 | const k = uint8ArrayFromString('/hello/you')
67 | const rec = new Libp2pRecord(k, uint8ArrayFromString('world'), new Date())
68 |
69 | const validators: Validators = {
70 | async hello (key, value) {
71 | expect(key).to.eql(k)
72 | expect(value).to.eql(uint8ArrayFromString('world'))
73 | }
74 | }
75 | await validator.verifyRecord(validators, rec)
76 | })
77 |
78 | it('calls not matching any validator', async () => {
79 | const k = uint8ArrayFromString('/hallo/you')
80 | const rec = new Libp2pRecord(k, uint8ArrayFromString('world'), new Date())
81 |
82 | const validators: Validators = {
83 | async hello (key, value) {
84 | expect(key).to.eql(k)
85 | expect(value).to.eql(uint8ArrayFromString('world'))
86 | }
87 | }
88 | await expect(validator.verifyRecord(validators, rec))
89 | .to.eventually.rejectedWith(
90 | /Invalid record keytype/
91 | )
92 | })
93 | })
94 |
95 | describe('validators', () => {
96 | it('exports pk', () => {
97 | expect(validator.validators).to.have.keys(['pk'])
98 | })
99 |
100 | describe('public key', () => {
101 | it('exports func', () => {
102 | const pk = validator.validators.pk
103 |
104 | expect(pk).to.be.a('function')
105 | })
106 |
107 | it('does not error on valid record', async () => {
108 | return Promise.all(cases.valid.publicKey.map(async (k) => {
109 | await validator.validators.pk(k, key.public.bytes)
110 | }))
111 | })
112 |
113 | it('throws on invalid records', async () => {
114 | return Promise.all(cases.invalid.publicKey.map(async ({ data, code }) => {
115 | try {
116 | //
117 | await validator.validators.pk(data, key.public.bytes)
118 | } catch (err: any) {
119 | expect(err.code).to.eql(code)
120 | return
121 | }
122 | expect.fail('did not throw an error with code ' + code)
123 | }))
124 | })
125 | })
126 | })
127 |
128 | describe('go interop', () => {
129 | it('record with key from from go', async () => {
130 | const pubKey = unmarshalPublicKey(fixture.publicKey)
131 |
132 | const hash = await pubKey.hash()
133 | const k = Uint8Array.of(...uint8ArrayFromString('/pk/'), ...hash)
134 | await validator.validators.pk(k, pubKey.bytes)
135 | })
136 | })
137 | })
138 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "aegir/src/config/tsconfig.aegir.json",
3 | "compilerOptions": {
4 | "outDir": "dist"
5 | },
6 | "include": [
7 | "src",
8 | "test"
9 | ]
10 | }
11 |
--------------------------------------------------------------------------------