@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.
Files changed (6) hide show
  1. package/README.md +117 -40
  2. package/index.cjs +499 -147
  3. package/index.js +534 -152
  4. package/package.json +9 -5
  5. package/pubky.d.ts +296 -186
  6. 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().putJson(path, { hello: "world" });
38
+ await session.storage.putJson(path, { hello: "world" });
39
39
 
40
40
  // 4) Read it publicly (no auth needed)
41
- const userPk = session.info().publicKey();
42
- const addr = `${userPk.z32()}/pub/example.com/hello.json`;
43
- const json = await pubky.publicStorage().getJson(addr); // -> { hello: "world" }
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
- // PKDNS resolver (read-only)
69
- const pkdns = pubky.pkdns();
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
- // Works with both pubky:// and http(s)://
83
- const res = await client.fetch("pubky://<pubky>/pub/example.com/file.txt");
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 info = session.info();
142
- const userPk = info.publicKey(); // -> PublicKey
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(); // -> SessionStorage (absolute paths)
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()); // show to user
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(`${userPk.z32()}/pub/example.com/data.json`);
196
- await pub.getText(`${userPk.z32()}/pub/example.com/readme.txt`);
197
- await pub.getBytes(`${userPk.z32()}/pub/example.com/icon.png`); // Uint8Array
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(`${userPk.z32()}/pub/example.com/foo`); // boolean
201
- await pub.stats(`${userPk.z32()}/pub/example.com/foo`); // { content_length, content_type, etag, last_modified } | null
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(`${userPk.z32()}/pub/example.com/`, null, false, 100, false);
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 `<user>/pub/app/file.txt` (or `pubky://<user>/...`).
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 resolver = pubky.pkdns();
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().publishHomeserverIfStale();
300
+ await signer.pkdns.publishHomeserverIfStale();
261
301
  // Or force an override now:
262
- await signer.pkdns().publishHomeserverForce(/* optional override homeserver*/);
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 `PubkyJsError`:
337
+ All async methods throw a structured `PubkyError`:
270
338
 
271
339
  ```ts
272
- type PubkyJsError = {
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
- statusCode?: number; // present for HTTP server errors (4xx/5xx)
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
- if (e.name === "RequestError" && e.statusCode === 404) {
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. Run the local testnet:
384
+ 2. Install and run the local testnet:
309
385
 
310
386
  ```bash
311
- npm run testnet
387
+ cargo install pubky-testnet
388
+ pubky-testnet
312
389
  ```
313
390
 
314
391
  3. Point the SDK at testnet: