@synonymdev/pubky 0.5.4 → 0.6.0-rc.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +265 -154
- package/index.cjs +1932 -400
- package/index.js +2068 -475
- package/package.json +10 -5
- package/pubky.d.ts +818 -84
- package/pubky_bg.wasm +0 -0
package/README.md
CHANGED
|
@@ -1,13 +1,8 @@
|
|
|
1
1
|
# Pubky
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
JS/WASM SDK for [Pubky](https://github.com/pubky/pubky-core).
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
- [Install](#install)
|
|
8
|
-
- [Getting Started](#getting-started)
|
|
9
|
-
- [API](#api)
|
|
10
|
-
- [Test and Development](#test-and-development)
|
|
5
|
+
Works in browsers and Node 20+.
|
|
11
6
|
|
|
12
7
|
## Install
|
|
13
8
|
|
|
@@ -15,277 +10,393 @@ JavaScript implementation of [Pubky](https://github.com/pubky/pubky-core) client
|
|
|
15
10
|
npm install @synonymdev/pubky
|
|
16
11
|
```
|
|
17
12
|
|
|
18
|
-
|
|
13
|
+
> **Node**: requires Node v20+ (undici fetch, WebCrypto).
|
|
19
14
|
|
|
20
|
-
|
|
15
|
+
Module system + TS types: ESM and CommonJS both supported; TypeScript typings generated via tsify are included. Use `import { Pubky } from "@synonymdev/pubky"` (ESM) or `const { Pubky } = require("@synonymdev/pubky")` (CJS).
|
|
21
16
|
|
|
22
|
-
## Getting
|
|
17
|
+
## Getting Started
|
|
23
18
|
|
|
24
19
|
```js
|
|
25
|
-
import {
|
|
20
|
+
import { Pubky, PublicKey, Keypair } from "@synonymdev/pubky";
|
|
26
21
|
|
|
27
|
-
//
|
|
28
|
-
|
|
22
|
+
// Initiate a Pubky SDK facade wired for default mainnet Pkarr relays.
|
|
23
|
+
const pubky = new Pubky(); // or: const pubky = Pubky.testnet(); for localhost testnet.
|
|
29
24
|
|
|
30
|
-
//
|
|
31
|
-
|
|
25
|
+
// 1) Create random user keys and bind to a new Signer.
|
|
26
|
+
const keypair = Keypair.random();
|
|
27
|
+
const signer = pubky.signer(keypair);
|
|
32
28
|
|
|
33
|
-
//
|
|
34
|
-
|
|
35
|
-
"8pinxxgqs41n4aididenw5apqp1urfmzdztr8jt4abrkdn435ewo"
|
|
29
|
+
// 2) Sign up at a homeserver (optionally with an invite)
|
|
30
|
+
const homeserver = PublicKey.from(
|
|
31
|
+
"8pinxxgqs41n4aididenw5apqp1urfmzdztr8jt4abrkdn435ewo",
|
|
36
32
|
);
|
|
33
|
+
const signupToken = "<your-invite-code-or-null>";
|
|
34
|
+
const session = await signer.signup(homeserver, signupToken);
|
|
35
|
+
|
|
36
|
+
// 3) Write a public JSON file (session-scoped storage uses cookies automatically)
|
|
37
|
+
const path = "/pub/example.com/hello.json";
|
|
38
|
+
await session.storage.putJson(path, { hello: "world" });
|
|
39
|
+
|
|
40
|
+
// 4) Read it publicly (no auth needed)
|
|
41
|
+
const userPk = session.info.publicKey.z32();
|
|
42
|
+
const addr = `pubky${userPk}/pub/example.com/hello.json`;
|
|
43
|
+
const json = await pubky.publicStorage.getJson(addr); // -> { hello: "world" }
|
|
44
|
+
|
|
45
|
+
// 5) Authenticate on a 3rd-party app
|
|
46
|
+
const authFlow = pubky.startAuthFlow("/pub/my.app/:rw"); // require permissions to read and write into `my.app`
|
|
47
|
+
renderQr(authFlow.authorizationUrl); // show to user
|
|
48
|
+
const session = await authFlow.awaitApproval();
|
|
49
|
+
```
|
|
37
50
|
|
|
38
|
-
|
|
51
|
+
Find here [**ready-to-run examples**](https://github.com/pubky/pubky-core/tree/main/examples).
|
|
39
52
|
|
|
40
|
-
|
|
53
|
+
## API Overview
|
|
41
54
|
|
|
42
|
-
|
|
43
|
-
let url = `pubky://${publicKey.z32()}/pub/example.com/arbitrary`;
|
|
55
|
+
Use `new Pubky()` to quickly get any flow started:
|
|
44
56
|
|
|
45
|
-
|
|
46
|
-
|
|
57
|
+
```js
|
|
58
|
+
import { Pubky, Keypair } from "@synonymdev/pubky";
|
|
47
59
|
|
|
48
|
-
//
|
|
49
|
-
|
|
50
|
-
method: "PUT",
|
|
51
|
-
body: JSON.stringify({ foo: "bar" }),
|
|
52
|
-
credentials: "include",
|
|
53
|
-
});
|
|
60
|
+
// Mainnet (default relays)
|
|
61
|
+
const pubky = new Pubky();
|
|
54
62
|
|
|
55
|
-
//
|
|
56
|
-
|
|
57
|
-
|
|
63
|
+
// Local testnet wiring (Pkarr + HTTP mapping).
|
|
64
|
+
// Omit the argument for "localhost".
|
|
65
|
+
const pubkyLocal = Pubky.testnet("localhost");
|
|
58
66
|
|
|
59
|
-
|
|
60
|
-
|
|
67
|
+
// Signer (bind your keypair to a new Signer actor)
|
|
68
|
+
const signer = pubky.signer(Keypair.random());
|
|
61
69
|
|
|
62
|
-
//
|
|
63
|
-
|
|
64
|
-
|
|
70
|
+
// Pubky Auth flow (with capabilities)
|
|
71
|
+
const authFlow = pubky.startAuthFlow("/pub/my.app/:rw");
|
|
72
|
+
|
|
73
|
+
// Public storage (read-only)
|
|
74
|
+
const publicStorage = pubky.publicStorage;
|
|
65
75
|
|
|
66
|
-
|
|
76
|
+
// Pkdns resolver
|
|
77
|
+
const pkdns = pubky.getHomeserverOf(publicKey);
|
|
67
78
|
|
|
68
|
-
|
|
79
|
+
// Optional: raw HTTP client for advanced use
|
|
80
|
+
const client = pubky.client;
|
|
81
|
+
```
|
|
69
82
|
|
|
70
|
-
|
|
83
|
+
### Client (HTTP bridge)
|
|
71
84
|
|
|
72
85
|
```js
|
|
73
|
-
|
|
86
|
+
import { Client, resolvePubky } from "@synonymdev/pubky";
|
|
87
|
+
|
|
88
|
+
const client = new Client(); // or: pubky.client.fetch(); instead of constructing a client manually
|
|
89
|
+
|
|
90
|
+
// Convert the identifier into a transport URL before fetching.
|
|
91
|
+
const url = resolvePubky("pubky<pubky>/pub/example.com/file.txt");
|
|
92
|
+
const res = await client.fetch(url);
|
|
74
93
|
```
|
|
75
94
|
|
|
76
|
-
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
### Keys
|
|
77
98
|
|
|
78
99
|
```js
|
|
79
|
-
|
|
80
|
-
|
|
100
|
+
import { Keypair, PublicKey } from "@synonymdev/pubky";
|
|
101
|
+
|
|
102
|
+
const keypair = Keypair.random();
|
|
103
|
+
const pubkey = keypair.publicKey;
|
|
81
104
|
|
|
82
|
-
|
|
105
|
+
// z-base-32 roundtrip
|
|
106
|
+
const parsed = PublicKey.from(pubkey.z32());
|
|
107
|
+
```
|
|
83
108
|
|
|
84
|
-
####
|
|
109
|
+
#### Recovery file (encrypt/decrypt root secret)
|
|
85
110
|
|
|
86
111
|
```js
|
|
87
|
-
|
|
112
|
+
// Encrypt to recovery file (Uint8Array)
|
|
113
|
+
const recoveryFile = keypair.createRecoveryFile("strong passphrase");
|
|
114
|
+
|
|
115
|
+
// Decrypt back into a Keypair
|
|
116
|
+
const restored = Keypair.fromRecoveryFile(recoveryFile, "strong passphrase");
|
|
117
|
+
|
|
118
|
+
// Build a Signer from a recovered key
|
|
119
|
+
const signer = pubky.signer(restored);
|
|
88
120
|
```
|
|
89
121
|
|
|
90
122
|
- keypair: An instance of [Keypair](#keypair).
|
|
91
|
-
-
|
|
92
|
-
-
|
|
93
|
-
|
|
94
|
-
Returns:
|
|
123
|
+
- passphrase: A utf-8 string [passphrase](https://www.useapassphrase.com/).
|
|
124
|
+
- Returns: A recovery file with a spec line and an encrypted secret key.
|
|
95
125
|
|
|
96
|
-
|
|
126
|
+
---
|
|
97
127
|
|
|
98
|
-
|
|
128
|
+
### Signer & Session
|
|
99
129
|
|
|
100
130
|
```js
|
|
101
|
-
|
|
102
|
-
```
|
|
131
|
+
import { Pubky, PublicKey, Keypair } from "@synonymdev/pubky";
|
|
103
132
|
|
|
104
|
-
|
|
133
|
+
const pubky = new Pubky();
|
|
105
134
|
|
|
106
|
-
|
|
135
|
+
const keypair = Keypair.random();
|
|
136
|
+
const signer = pubky.signer(keypair);
|
|
107
137
|
|
|
108
|
-
|
|
138
|
+
const homeserver = PublicKey.from("8pinxxgq…");
|
|
139
|
+
const session = await signer.signup(homeserver, /* invite */ null);
|
|
109
140
|
|
|
110
|
-
|
|
141
|
+
const session2 = await signer.signin(); // fast, prefer this; publishes PKDNS in background
|
|
142
|
+
const session3 = await signer.signinBlocking(); // slower but safer; waits for PKDNS publish
|
|
111
143
|
|
|
112
|
-
|
|
113
|
-
await client.signout(publicKey);
|
|
144
|
+
await session.signout(); // invalidates server session
|
|
114
145
|
```
|
|
115
146
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
#### authRequest
|
|
147
|
+
**Session details**
|
|
119
148
|
|
|
120
149
|
```js
|
|
121
|
-
|
|
150
|
+
const userPk = session.info.publicKey.z32(); // -> PublicKey as z32 string
|
|
151
|
+
const caps = session.info.capabilities; // -> string[] permissions and paths
|
|
122
152
|
|
|
123
|
-
|
|
153
|
+
const storage = session.storage; // -> This User's storage API (absolute paths)
|
|
154
|
+
```
|
|
124
155
|
|
|
125
|
-
|
|
156
|
+
**Approve a pubkyauth request URL**
|
|
126
157
|
|
|
127
|
-
|
|
158
|
+
```js
|
|
159
|
+
await signer.approveAuthRequest("pubkyauth:///?caps=...&secret=...&relay=...");
|
|
128
160
|
```
|
|
129
161
|
|
|
130
|
-
|
|
131
|
-
instead request permissions (showing the user pubkyauthUrl), and await a Session after the user consenting to that request.
|
|
162
|
+
---
|
|
132
163
|
|
|
133
|
-
|
|
134
|
-
- capabilities: A list of capabilities required for the app for example `/pub/pubky.app/:rw,/pub/example.com/:r`.
|
|
164
|
+
### AuthFlow (pubkyauth)
|
|
135
165
|
|
|
136
|
-
|
|
166
|
+
End-to-end auth (3rd-party app asks a user to approve via QR/deeplink, E.g. Pubky Ring).
|
|
137
167
|
|
|
138
168
|
```js
|
|
139
|
-
|
|
140
|
-
|
|
169
|
+
import { Pubky } from "@synonymdev/pubky";
|
|
170
|
+
const pubky = new Pubky();
|
|
141
171
|
|
|
142
|
-
|
|
172
|
+
// Comma-separated capabilities string
|
|
173
|
+
const caps = "/pub/my.app/:rw,/pub/another.app/folder/:w";
|
|
143
174
|
|
|
144
|
-
|
|
145
|
-
|
|
175
|
+
// Optional relay; defaults to Synonym-hosted relay if omitted
|
|
176
|
+
const relay = "https://httprelay.pubky.app/link/"; // optional (defaults to this)
|
|
146
177
|
|
|
147
|
-
|
|
178
|
+
// Start the auth polling
|
|
179
|
+
const flow = pubky.startAuthFlow(caps, relay);
|
|
148
180
|
|
|
149
|
-
|
|
150
|
-
|
|
181
|
+
renderQr(flow.authorizationUrl); // show to user
|
|
182
|
+
|
|
183
|
+
// Blocks until the signer approves; returns a ready Session
|
|
184
|
+
const session = await flow.awaitApproval();
|
|
151
185
|
```
|
|
152
186
|
|
|
153
|
-
|
|
154
|
-
- Returns: A [Session](#session) object if signed in, or undefined if not.
|
|
187
|
+
#### Validate and normalize capabilities
|
|
155
188
|
|
|
156
|
-
|
|
189
|
+
If you accept capability strings from user input (forms, CLI arguments, etc.),
|
|
190
|
+
use `validateCapabilities` before calling `startAuthFlow`. The helper returns a
|
|
191
|
+
normalized string (ordering actions like `:rw`) and throws a structured error
|
|
192
|
+
when the input is malformed.
|
|
157
193
|
|
|
158
194
|
```js
|
|
159
|
-
|
|
195
|
+
import { Pubky, validateCapabilities } from "@synonymdev/pubky";
|
|
196
|
+
|
|
197
|
+
const pubky = new Pubky();
|
|
198
|
+
|
|
199
|
+
const rawCaps = formData.get("caps");
|
|
200
|
+
|
|
201
|
+
try {
|
|
202
|
+
const caps = validateCapabilities(rawCaps ?? "");
|
|
203
|
+
const flow = pubky.startAuthFlow(caps);
|
|
204
|
+
renderQr(flow.authorizationUrl);
|
|
205
|
+
const session = await flow.awaitApproval();
|
|
206
|
+
// ...
|
|
207
|
+
} catch (error) {
|
|
208
|
+
if (error.name === "InvalidInput") {
|
|
209
|
+
surfaceValidationError(error.message);
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
throw error;
|
|
213
|
+
}
|
|
160
214
|
```
|
|
161
215
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
- limit: Number of urls to return.
|
|
166
|
-
- Returns: A list of URLs of the files in the `url` you passed.
|
|
216
|
+
On invalid input, `validateCapabilities` throws a `PubkyError` with
|
|
217
|
+
`{ name: "InvalidInput", message: "Invalid capability entries: …" }`, so you can
|
|
218
|
+
surface precise feedback to the user.
|
|
167
219
|
|
|
168
|
-
|
|
220
|
+
#### Http Relay & reliability
|
|
169
221
|
|
|
170
|
-
|
|
222
|
+
- If you don’t specify a relay, `PubkyAuthFlow` defaults to a Synonym-hosted relay. If that relay is down, logins won’t complete.
|
|
223
|
+
- For production and larger apps, run **your own http relay** (MIT, Docker): [https://httprelay.io](https://httprelay.io).
|
|
224
|
+
The channel is derived as `base64url(hash(secret))`; the token is end-to-end encrypted with the `secret` and cannot be decrypted by the relay.
|
|
171
225
|
|
|
172
|
-
|
|
173
|
-
let keypair = Keypair.random();
|
|
174
|
-
```
|
|
226
|
+
---
|
|
175
227
|
|
|
176
|
-
|
|
228
|
+
### Storage
|
|
177
229
|
|
|
178
|
-
####
|
|
230
|
+
#### PublicStorage (read-only)
|
|
179
231
|
|
|
180
232
|
```js
|
|
181
|
-
|
|
182
|
-
```
|
|
233
|
+
const pub = pubky.publicStorage;
|
|
183
234
|
|
|
184
|
-
|
|
185
|
-
|
|
235
|
+
// Reads
|
|
236
|
+
await pub.getJson(`pubky${userPk.z32()}/pub/example.com/data.json`);
|
|
237
|
+
await pub.getText(`pubky${userPk.z32()}/pub/example.com/readme.txt`);
|
|
238
|
+
await pub.getBytes(`pubky${userPk.z32()}/pub/example.com/icon.png`); // Uint8Array
|
|
186
239
|
|
|
187
|
-
|
|
240
|
+
// Metadata
|
|
241
|
+
await pub.exists(`pubky${userPk.z32()}/pub/example.com/foo`); // boolean
|
|
242
|
+
await pub.stats(`pubky${userPk.z32()}/pub/example.com/foo`); // { content_length, content_type, etag, last_modified } | null
|
|
188
243
|
|
|
189
|
-
|
|
190
|
-
|
|
244
|
+
// List directory (addressed path "<pubky>/pub/.../") must include trailing `/`.
|
|
245
|
+
// list(addr, cursor=null|suffix|fullUrl, reverse=false, limit?, shallow=false)
|
|
246
|
+
await pub.list(`pubky${userPk.z32()}/pub/example.com/`, null, false, 100, false);
|
|
191
247
|
```
|
|
192
248
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
#### secretKey
|
|
249
|
+
#### SessionStorage (read/write; uses cookies)
|
|
196
250
|
|
|
197
251
|
```js
|
|
198
|
-
|
|
199
|
-
```
|
|
252
|
+
const s = session.storage;
|
|
200
253
|
|
|
201
|
-
|
|
254
|
+
// Writes
|
|
255
|
+
await s.putJson("/pub/example.com/data.json", { ok: true });
|
|
256
|
+
await s.putText("/pub/example.com/note.txt", "hello");
|
|
257
|
+
await s.putBytes("/pub/example.com/img.bin", new Uint8Array([1, 2, 3]));
|
|
202
258
|
|
|
203
|
-
|
|
259
|
+
// Reads
|
|
260
|
+
await s.getJson("/pub/example.com/data.json");
|
|
261
|
+
await s.getText("/pub/example.com/note.txt");
|
|
262
|
+
await s.getBytes("/pub/example.com/img.bin");
|
|
204
263
|
|
|
205
|
-
|
|
264
|
+
// Metadata
|
|
265
|
+
await s.exists("/pub/example.com/data.json");
|
|
266
|
+
await s.stats("/pub/example.com/data.json");
|
|
206
267
|
|
|
207
|
-
|
|
208
|
-
|
|
268
|
+
// Listing (session-scoped absolute dir)
|
|
269
|
+
await s.list("/pub/example.com/", null, false, 100, false);
|
|
270
|
+
|
|
271
|
+
// Delete
|
|
272
|
+
await s.delete("/pub/example.com/data.json");
|
|
209
273
|
```
|
|
210
274
|
|
|
211
|
-
|
|
212
|
-
- Returns: A new PublicKey instance.
|
|
275
|
+
Path rules:
|
|
213
276
|
|
|
214
|
-
|
|
277
|
+
- Session storage uses **absolute** paths like `"/pub/app/file.txt"`.
|
|
278
|
+
- Public storage uses **addressed** form `pubky<user>/pub/app/file.txt` (preferred) or `pubky://<user>/...`.
|
|
215
279
|
|
|
216
|
-
|
|
217
|
-
let pubky = publicKey.z32();
|
|
218
|
-
```
|
|
280
|
+
**Convention:** put your app’s public data under a domain-like folder in `/pub`, e.g. `/pub/mycoolnew.app/`.
|
|
219
281
|
|
|
220
|
-
|
|
282
|
+
---
|
|
221
283
|
|
|
222
|
-
###
|
|
284
|
+
### PKDNS (Pkarr)
|
|
223
285
|
|
|
224
|
-
|
|
286
|
+
Resolve or publish `_pubky` records.
|
|
225
287
|
|
|
226
288
|
```js
|
|
227
|
-
|
|
289
|
+
import { Pubky, PublicKey, Keypair } from "@synonymdev/pubky";
|
|
290
|
+
|
|
291
|
+
const pubky = new Pubky();
|
|
292
|
+
|
|
293
|
+
// Read-only resolver
|
|
294
|
+
const homeserver = await pubky.getHomeserverOf(PublicKey.from("<user-z32>")); // string | undefined
|
|
295
|
+
|
|
296
|
+
// With keys (signer-bound)
|
|
297
|
+
const signer = pubky.signer(Keypair.random());
|
|
298
|
+
|
|
299
|
+
// Republish if missing or stale (reuses current host unless overridden)
|
|
300
|
+
await signer.pkdns.publishHomeserverIfStale();
|
|
301
|
+
// Or force an override now:
|
|
302
|
+
await signer.pkdns.publishHomeserverForce(/* optional override homeserver*/);
|
|
303
|
+
// Resolve your own homeserver:
|
|
304
|
+
await signer.pkdns.getHomeserver();
|
|
228
305
|
```
|
|
229
306
|
|
|
230
|
-
|
|
307
|
+
## Logging
|
|
231
308
|
|
|
232
|
-
|
|
309
|
+
The SDK ships with a WASM logger that bridges Rust `log` output into the browser or Node console. Call `setLogLevel` **once at application start**, before constructing `Pubky` or other SDK actors, to choose how verbose the logs should be.
|
|
233
310
|
|
|
234
311
|
```js
|
|
235
|
-
|
|
312
|
+
import { setLogLevel } from "@synonymdev/pubky";
|
|
313
|
+
|
|
314
|
+
setLogLevel("debug"); // "error" | "warn" | "info" | "debug" | "trace"
|
|
236
315
|
```
|
|
237
316
|
|
|
238
|
-
|
|
317
|
+
If the logger is already initialized, calling `setLogLevel` again will throw. Pick the most verbose level (`"debug"` or `"trace"`) while developing to see pkarr resolution, network requests and storage operations in the console.
|
|
239
318
|
|
|
240
|
-
|
|
319
|
+
#### Resolve `pubky` identifiers into transport URLs
|
|
241
320
|
|
|
242
|
-
|
|
321
|
+
Use `resolvePubky()` when you need to feed an addressed resource into a raw HTTP client:
|
|
243
322
|
|
|
244
323
|
```js
|
|
245
|
-
|
|
324
|
+
import { resolvePubky } from "@synonymdev/pubky";
|
|
325
|
+
|
|
326
|
+
const identifier = "pubkyoperrr8wsbpr3ue9d4qj41ge1kcc6r7fdiy6o3ugjrrhi4y77rdo/pub/pubky.app/posts/0033X02JAN0SG";
|
|
327
|
+
const url = resolvePubky(identifier);
|
|
328
|
+
// -> "https://_pubky.operrr8wsbpr3ue9d4qj41ge1kcc6r7fdiy6o3ugjrrhi4y77rdo/pub/pubky.app/posts/0033X02JAN0SG"
|
|
246
329
|
```
|
|
247
330
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
331
|
+
Both `pubky<pk>/…` (preferred) and `pubky://<pk>/…` resolve to the same HTTPS endpoint.
|
|
332
|
+
|
|
333
|
+
---
|
|
334
|
+
|
|
335
|
+
## Errors
|
|
336
|
+
|
|
337
|
+
All async methods throw a structured `PubkyError`:
|
|
251
338
|
|
|
252
|
-
|
|
339
|
+
```ts
|
|
340
|
+
interface PubkyError extends Error {
|
|
341
|
+
name:
|
|
342
|
+
| "RequestError" // network/server/validation/JSON
|
|
343
|
+
| "InvalidInput"
|
|
344
|
+
| "AuthenticationError"
|
|
345
|
+
| "PkarrError"
|
|
346
|
+
| "InternalError";
|
|
347
|
+
message: string;
|
|
348
|
+
data?: unknown; // structured context when available (e.g. { statusCode: number })
|
|
349
|
+
}
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
Example:
|
|
253
353
|
|
|
254
354
|
```js
|
|
255
|
-
|
|
355
|
+
try {
|
|
356
|
+
await publicStorage.getJson(`${pk}/pub/example.com/missing.json`);
|
|
357
|
+
} catch (e) {
|
|
358
|
+
const error = e as PubkyError;
|
|
359
|
+
if (
|
|
360
|
+
error.name === "RequestError" &&
|
|
361
|
+
typeof error.data === "object" &&
|
|
362
|
+
error.data !== null &&
|
|
363
|
+
"statusCode" in error.data &&
|
|
364
|
+
typeof (error.data as { statusCode?: number }).statusCode === "number" &&
|
|
365
|
+
(error.data as { statusCode?: number }).statusCode === 404
|
|
366
|
+
) {
|
|
367
|
+
// handle not found
|
|
368
|
+
}
|
|
369
|
+
}
|
|
256
370
|
```
|
|
257
371
|
|
|
258
|
-
|
|
259
|
-
- passphrase: A utf-8 string [passphrase](https://www.useapassphrase.com/).
|
|
260
|
-
- Returns: An instance of [Keypair](#keypair).
|
|
372
|
+
---
|
|
261
373
|
|
|
262
|
-
## Test
|
|
374
|
+
## Local Test & Development
|
|
263
375
|
|
|
264
376
|
For test and development, you can run a local homeserver in a test network.
|
|
265
377
|
|
|
266
|
-
|
|
378
|
+
1. Install Rust (for wasm and testnet builds):
|
|
267
379
|
|
|
268
380
|
```bash
|
|
269
381
|
curl https://sh.rustup.rs -sSf | sh
|
|
270
382
|
```
|
|
271
383
|
|
|
272
|
-
|
|
384
|
+
2. Install and run the local testnet:
|
|
273
385
|
|
|
274
386
|
```bash
|
|
275
|
-
|
|
276
|
-
|
|
387
|
+
cargo install pubky-testnet
|
|
388
|
+
pubky-testnet
|
|
277
389
|
```
|
|
278
390
|
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
```bash
|
|
282
|
-
npm run testnet
|
|
283
|
-
```
|
|
284
|
-
|
|
285
|
-
Use the logged addresses as inputs to `Client`
|
|
391
|
+
3. Point the SDK at testnet:
|
|
286
392
|
|
|
287
393
|
```js
|
|
288
|
-
import {
|
|
394
|
+
import { Pubky } from "@synonymdev/pubky";
|
|
289
395
|
|
|
290
|
-
const
|
|
396
|
+
const pubky = Pubky.testnet(); // defaults to localhost
|
|
397
|
+
// or: const pubky = Pubky.testnet("testnet-host"); // custom host (e.g. Docker bridge)
|
|
291
398
|
```
|
|
399
|
+
|
|
400
|
+
---
|
|
401
|
+
|
|
402
|
+
MIT © Synonym
|