@synonymdev/pubky 0.6.0-rc.2 → 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 +117 -40
- package/index.cjs +499 -147
- package/index.js +534 -152
- package/package.json +9 -5
- package/pubky.d.ts +296 -186
- package/pubky_bg.wasm +0 -0
package/README.md
CHANGED
|
@@ -28,19 +28,24 @@ const signer = pubky.signer(keypair);
|
|
|
28
28
|
|
|
29
29
|
// 2) Sign up at a homeserver (optionally with an invite)
|
|
30
30
|
const homeserver = PublicKey.from(
|
|
31
|
-
"8pinxxgqs41n4aididenw5apqp1urfmzdztr8jt4abrkdn435ewo"
|
|
31
|
+
"8pinxxgqs41n4aididenw5apqp1urfmzdztr8jt4abrkdn435ewo",
|
|
32
32
|
);
|
|
33
33
|
const signupToken = "<your-invite-code-or-null>";
|
|
34
34
|
const session = await signer.signup(homeserver, signupToken);
|
|
35
35
|
|
|
36
36
|
// 3) Write a public JSON file (session-scoped storage uses cookies automatically)
|
|
37
37
|
const path = "/pub/example.com/hello.json";
|
|
38
|
-
await session.storage
|
|
38
|
+
await session.storage.putJson(path, { hello: "world" });
|
|
39
39
|
|
|
40
40
|
// 4) Read it publicly (no auth needed)
|
|
41
|
-
const userPk = session.info
|
|
42
|
-
const addr =
|
|
43
|
-
const json = await pubky.publicStorage
|
|
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();
|
|
44
49
|
```
|
|
45
50
|
|
|
46
51
|
Find here [**ready-to-run examples**](https://github.com/pubky/pubky-core/tree/main/examples).
|
|
@@ -62,25 +67,29 @@ const pubkyLocal = Pubky.testnet("localhost");
|
|
|
62
67
|
// Signer (bind your keypair to a new Signer actor)
|
|
63
68
|
const signer = pubky.signer(Keypair.random());
|
|
64
69
|
|
|
70
|
+
// Pubky Auth flow (with capabilities)
|
|
71
|
+
const authFlow = pubky.startAuthFlow("/pub/my.app/:rw");
|
|
72
|
+
|
|
65
73
|
// Public storage (read-only)
|
|
66
|
-
const publicStorage = pubky.publicStorage
|
|
74
|
+
const publicStorage = pubky.publicStorage;
|
|
67
75
|
|
|
68
|
-
//
|
|
69
|
-
const pkdns = pubky.
|
|
76
|
+
// Pkdns resolver
|
|
77
|
+
const pkdns = pubky.getHomeserverOf(publicKey);
|
|
70
78
|
|
|
71
79
|
// Optional: raw HTTP client for advanced use
|
|
72
|
-
const client = pubky.client
|
|
80
|
+
const client = pubky.client;
|
|
73
81
|
```
|
|
74
82
|
|
|
75
83
|
### Client (HTTP bridge)
|
|
76
84
|
|
|
77
85
|
```js
|
|
78
|
-
import { Client } from "@synonymdev/pubky";
|
|
86
|
+
import { Client, resolvePubky } from "@synonymdev/pubky";
|
|
79
87
|
|
|
80
|
-
const client = new Client(); // or: pubky.client(); instead of constructing a client manually
|
|
88
|
+
const client = new Client(); // or: pubky.client.fetch(); instead of constructing a client manually
|
|
81
89
|
|
|
82
|
-
//
|
|
83
|
-
const
|
|
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);
|
|
84
93
|
```
|
|
85
94
|
|
|
86
95
|
---
|
|
@@ -91,7 +100,7 @@ const res = await client.fetch("pubky://<pubky>/pub/example.com/file.txt");
|
|
|
91
100
|
import { Keypair, PublicKey } from "@synonymdev/pubky";
|
|
92
101
|
|
|
93
102
|
const keypair = Keypair.random();
|
|
94
|
-
const pubkey = keypair.publicKey
|
|
103
|
+
const pubkey = keypair.publicKey;
|
|
95
104
|
|
|
96
105
|
// z-base-32 roundtrip
|
|
97
106
|
const parsed = PublicKey.from(pubkey.z32());
|
|
@@ -138,11 +147,10 @@ await session.signout(); // invalidates server session
|
|
|
138
147
|
**Session details**
|
|
139
148
|
|
|
140
149
|
```js
|
|
141
|
-
const
|
|
142
|
-
const
|
|
143
|
-
const caps = info.capabilities(); // -> string[]
|
|
150
|
+
const userPk = session.info.publicKey.z32(); // -> PublicKey as z32 string
|
|
151
|
+
const caps = session.info.capabilities; // -> string[] permissions and paths
|
|
144
152
|
|
|
145
|
-
const storage = session.storage
|
|
153
|
+
const storage = session.storage; // -> This User's storage API (absolute paths)
|
|
146
154
|
```
|
|
147
155
|
|
|
148
156
|
**Approve a pubkyauth request URL**
|
|
@@ -170,12 +178,45 @@ const relay = "https://httprelay.pubky.app/link/"; // optional (defaults to this
|
|
|
170
178
|
// Start the auth polling
|
|
171
179
|
const flow = pubky.startAuthFlow(caps, relay);
|
|
172
180
|
|
|
173
|
-
renderQr(flow.authorizationUrl
|
|
181
|
+
renderQr(flow.authorizationUrl); // show to user
|
|
174
182
|
|
|
175
183
|
// Blocks until the signer approves; returns a ready Session
|
|
176
184
|
const session = await flow.awaitApproval();
|
|
177
185
|
```
|
|
178
186
|
|
|
187
|
+
#### Validate and normalize capabilities
|
|
188
|
+
|
|
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.
|
|
193
|
+
|
|
194
|
+
```js
|
|
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
|
+
}
|
|
214
|
+
```
|
|
215
|
+
|
|
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.
|
|
219
|
+
|
|
179
220
|
#### Http Relay & reliability
|
|
180
221
|
|
|
181
222
|
- If you don’t specify a relay, `PubkyAuthFlow` defaults to a Synonym-hosted relay. If that relay is down, logins won’t complete.
|
|
@@ -189,26 +230,26 @@ const session = await flow.awaitApproval();
|
|
|
189
230
|
#### PublicStorage (read-only)
|
|
190
231
|
|
|
191
232
|
```js
|
|
192
|
-
const pub = pubky.publicStorage
|
|
233
|
+
const pub = pubky.publicStorage;
|
|
193
234
|
|
|
194
235
|
// Reads
|
|
195
|
-
await pub.getJson(
|
|
196
|
-
await pub.getText(
|
|
197
|
-
await pub.getBytes(
|
|
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
|
|
198
239
|
|
|
199
240
|
// Metadata
|
|
200
|
-
await pub.exists(
|
|
201
|
-
await pub.stats(
|
|
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
|
|
202
243
|
|
|
203
244
|
// List directory (addressed path "<pubky>/pub/.../") must include trailing `/`.
|
|
204
245
|
// list(addr, cursor=null|suffix|fullUrl, reverse=false, limit?, shallow=false)
|
|
205
|
-
await pub.list(
|
|
246
|
+
await pub.list(`pubky${userPk.z32()}/pub/example.com/`, null, false, 100, false);
|
|
206
247
|
```
|
|
207
248
|
|
|
208
249
|
#### SessionStorage (read/write; uses cookies)
|
|
209
250
|
|
|
210
251
|
```js
|
|
211
|
-
const s = session.storage
|
|
252
|
+
const s = session.storage;
|
|
212
253
|
|
|
213
254
|
// Writes
|
|
214
255
|
await s.putJson("/pub/example.com/data.json", { ok: true });
|
|
@@ -234,7 +275,7 @@ await s.delete("/pub/example.com/data.json");
|
|
|
234
275
|
Path rules:
|
|
235
276
|
|
|
236
277
|
- Session storage uses **absolute** paths like `"/pub/app/file.txt"`.
|
|
237
|
-
- Public storage uses **addressed** form
|
|
278
|
+
- Public storage uses **addressed** form `pubky<user>/pub/app/file.txt` (preferred) or `pubky://<user>/...`.
|
|
238
279
|
|
|
239
280
|
**Convention:** put your app’s public data under a domain-like folder in `/pub`, e.g. `/pub/mycoolnew.app/`.
|
|
240
281
|
|
|
@@ -250,26 +291,53 @@ import { Pubky, PublicKey, Keypair } from "@synonymdev/pubky";
|
|
|
250
291
|
const pubky = new Pubky();
|
|
251
292
|
|
|
252
293
|
// Read-only resolver
|
|
253
|
-
const
|
|
254
|
-
const homeserver = await resolver.getHomeserverOf(PublicKey.from("<user-z32>")); // string | undefined
|
|
294
|
+
const homeserver = await pubky.getHomeserverOf(PublicKey.from("<user-z32>")); // string | undefined
|
|
255
295
|
|
|
256
296
|
// With keys (signer-bound)
|
|
257
297
|
const signer = pubky.signer(Keypair.random());
|
|
258
298
|
|
|
259
299
|
// Republish if missing or stale (reuses current host unless overridden)
|
|
260
|
-
await signer.pkdns
|
|
300
|
+
await signer.pkdns.publishHomeserverIfStale();
|
|
261
301
|
// Or force an override now:
|
|
262
|
-
await signer.pkdns
|
|
302
|
+
await signer.pkdns.publishHomeserverForce(/* optional override homeserver*/);
|
|
303
|
+
// Resolve your own homeserver:
|
|
304
|
+
await signer.pkdns.getHomeserver();
|
|
263
305
|
```
|
|
264
306
|
|
|
307
|
+
## Logging
|
|
308
|
+
|
|
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.
|
|
310
|
+
|
|
311
|
+
```js
|
|
312
|
+
import { setLogLevel } from "@synonymdev/pubky";
|
|
313
|
+
|
|
314
|
+
setLogLevel("debug"); // "error" | "warn" | "info" | "debug" | "trace"
|
|
315
|
+
```
|
|
316
|
+
|
|
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.
|
|
318
|
+
|
|
319
|
+
#### Resolve `pubky` identifiers into transport URLs
|
|
320
|
+
|
|
321
|
+
Use `resolvePubky()` when you need to feed an addressed resource into a raw HTTP client:
|
|
322
|
+
|
|
323
|
+
```js
|
|
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"
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
Both `pubky<pk>/…` (preferred) and `pubky://<pk>/…` resolve to the same HTTPS endpoint.
|
|
332
|
+
|
|
265
333
|
---
|
|
266
334
|
|
|
267
335
|
## Errors
|
|
268
336
|
|
|
269
|
-
All async methods throw a structured `
|
|
337
|
+
All async methods throw a structured `PubkyError`:
|
|
270
338
|
|
|
271
339
|
```ts
|
|
272
|
-
|
|
340
|
+
interface PubkyError extends Error {
|
|
273
341
|
name:
|
|
274
342
|
| "RequestError" // network/server/validation/JSON
|
|
275
343
|
| "InvalidInput"
|
|
@@ -277,8 +345,8 @@ type PubkyJsError = {
|
|
|
277
345
|
| "PkarrError"
|
|
278
346
|
| "InternalError";
|
|
279
347
|
message: string;
|
|
280
|
-
|
|
281
|
-
}
|
|
348
|
+
data?: unknown; // structured context when available (e.g. { statusCode: number })
|
|
349
|
+
}
|
|
282
350
|
```
|
|
283
351
|
|
|
284
352
|
Example:
|
|
@@ -287,7 +355,15 @@ Example:
|
|
|
287
355
|
try {
|
|
288
356
|
await publicStorage.getJson(`${pk}/pub/example.com/missing.json`);
|
|
289
357
|
} catch (e) {
|
|
290
|
-
|
|
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
|
+
) {
|
|
291
367
|
// handle not found
|
|
292
368
|
}
|
|
293
369
|
}
|
|
@@ -299,16 +375,17 @@ try {
|
|
|
299
375
|
|
|
300
376
|
For test and development, you can run a local homeserver in a test network.
|
|
301
377
|
|
|
302
|
-
1. Install Rust (for wasm builds):
|
|
378
|
+
1. Install Rust (for wasm and testnet builds):
|
|
303
379
|
|
|
304
380
|
```bash
|
|
305
381
|
curl https://sh.rustup.rs -sSf | sh
|
|
306
382
|
```
|
|
307
383
|
|
|
308
|
-
2.
|
|
384
|
+
2. Install and run the local testnet:
|
|
309
385
|
|
|
310
386
|
```bash
|
|
311
|
-
|
|
387
|
+
cargo install pubky-testnet
|
|
388
|
+
pubky-testnet
|
|
312
389
|
```
|
|
313
390
|
|
|
314
391
|
3. Point the SDK at testnet:
|