@inversealtruism/cairn-sdk 0.1.3 → 0.2.0

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 (46) hide show
  1. package/README.md +16 -16
  2. package/dist/board.cjs +1 -1
  3. package/dist/board.d.cts +1 -1
  4. package/dist/board.d.ts +1 -1
  5. package/dist/board.js +2 -2
  6. package/dist/chain.cjs +1 -1
  7. package/dist/chain.d.cts +1 -1
  8. package/dist/chain.d.ts +1 -1
  9. package/dist/chain.js +1 -1
  10. package/dist/{chunk-G7FABIF4.js → chunk-BGJZF7S2.js} +11 -2
  11. package/dist/{chunk-5KAUQH72.js → chunk-C452UODV.js} +1 -1
  12. package/dist/{chunk-FNBE2FPE.js → chunk-J5J4JEJ6.js} +50 -18
  13. package/dist/{chunk-UMGWVOOT.js → chunk-JGXD3WIC.js} +7 -2
  14. package/dist/{chunk-ZAE2BIH5.js → chunk-PYREGAB3.js} +21 -3
  15. package/dist/{chunk-645CYBVN.js → chunk-RYZJDZLJ.js} +1 -1
  16. package/dist/{chunk-OSXS26RA.js → chunk-SIUUY6FZ.js} +4 -3
  17. package/dist/{chunk-G4OZKZSJ.js → chunk-UIEOBVFI.js} +8 -2
  18. package/dist/connect.cjs +14 -3
  19. package/dist/connect.d.cts +7 -0
  20. package/dist/connect.d.ts +7 -0
  21. package/dist/connect.js +2 -2
  22. package/dist/content.cjs +4 -3
  23. package/dist/content.d.cts +1 -1
  24. package/dist/content.d.ts +1 -1
  25. package/dist/content.js +2 -2
  26. package/dist/controller.cjs +34 -5
  27. package/dist/controller.d.cts +1 -0
  28. package/dist/controller.d.ts +1 -0
  29. package/dist/controller.js +3 -3
  30. package/dist/{http-D2CzfOsm.d.cts → http-C9C4L4hz.d.cts} +11 -0
  31. package/dist/{http-D2CzfOsm.d.ts → http-C9C4L4hz.d.ts} +11 -0
  32. package/dist/index.cjs +272 -37
  33. package/dist/index.d.cts +99 -11
  34. package/dist/index.d.ts +99 -11
  35. package/dist/index.js +179 -17
  36. package/dist/indexer.cjs +50 -18
  37. package/dist/indexer.d.cts +15 -4
  38. package/dist/indexer.d.ts +15 -4
  39. package/dist/indexer.js +1 -1
  40. package/dist/react.cjs +34 -5
  41. package/dist/react.js +3 -3
  42. package/dist/registry.cjs +11 -2
  43. package/dist/registry.d.cts +12 -3
  44. package/dist/registry.d.ts +12 -3
  45. package/dist/registry.js +1 -1
  46. package/package.json +32 -23
package/dist/content.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  ContentClient
3
- } from "./chunk-OSXS26RA.js";
4
- import "./chunk-UMGWVOOT.js";
3
+ } from "./chunk-SIUUY6FZ.js";
4
+ import "./chunk-JGXD3WIC.js";
5
5
  export {
6
6
  ContentClient
7
7
  };
@@ -25,7 +25,7 @@ __export(controller_exports, {
25
25
  module.exports = __toCommonJS(controller_exports);
26
26
 
27
27
  // src/errors.ts
28
- var SDK_VERSION = "0.1.2";
28
+ var SDK_VERSION = "0.2.0";
29
29
  var DOCS_BASE = "https://cairn-substrate.com/docs/sdk";
30
30
  var CairnError = class extends Error {
31
31
  /** Stable machine code for programmatic branching. */
@@ -82,7 +82,12 @@ var UnsupportedMethodError = class extends CairnError {
82
82
  super(message, { code: "UNSUPPORTED_METHOD", docsPath: "/errors#unsupported-method" });
83
83
  }
84
84
  };
85
- function mapProviderError(error) {
85
+ var NATIVE_CODES = /* @__PURE__ */ new Set(["ACCOUNT_CHANGED", "FIRST_PARTY_ONLY", "RATE_LIMITED", "APPROVAL_CLOSED", "FORBIDDEN", "INTERNAL"]);
86
+ function mapProviderError(error, code) {
87
+ if (code === "USER_REJECTED" || code === "APPROVAL_CLOSED") return new UserRejectedError(error);
88
+ if (code === "WALLET_LOCKED") return new WalletLockedError(error);
89
+ if (code === "UNSUPPORTED_METHOD" || code === "UNKNOWN_KIND") return new UnsupportedMethodError(error);
90
+ if (code && NATIVE_CODES.has(code)) return new CairnError(error || code, { code });
86
91
  const e = String(error || "").toLowerCase();
87
92
  if (e.includes("rejected by user")) return new UserRejectedError(error);
88
93
  if (e.includes("wallet locked")) return new WalletLockedError(error);
@@ -135,7 +140,7 @@ function detectProvider(opts = {}) {
135
140
  }
136
141
  function unwrap(reply) {
137
142
  if (reply && reply.ok) return reply.result;
138
- throw mapProviderError(reply?.error ?? "Unknown wallet error");
143
+ throw mapProviderError(reply?.error ?? "Unknown wallet error", reply?.code);
139
144
  }
140
145
  var WalletConnection = class {
141
146
  provider;
@@ -169,6 +174,9 @@ var WalletConnection = class {
169
174
  * Passwordless sign-in: a signature over a login nonce (always prompts; structurally cannot sign a tx).
170
175
  */
171
176
  signIn() {
177
+ if (typeof this.provider.signIn !== "function") {
178
+ return Promise.reject(new UnsupportedMethodError("this wallet does not expose signIn()"));
179
+ }
172
180
  return Promise.resolve(this.provider.signIn()).then(unwrap);
173
181
  }
174
182
  /**
@@ -202,6 +210,9 @@ var WalletConnection = class {
202
210
  }
203
211
  /** Atomic fill (CairnX DvP): pay + attest in ONE tx. Always prompts with clear-signing. */
204
212
  fillOffer(params) {
213
+ if (typeof this.provider.fillOffer !== "function") {
214
+ return Promise.reject(new UnsupportedMethodError("this wallet predates fillOffer() \u2014 ask the user to update the Cairn Wallet"));
215
+ }
205
216
  return Promise.resolve(this.provider.fillOffer(params)).then(unwrap);
206
217
  }
207
218
  /** This origin's current permission grant (EIP-2255-style). Silent; [] if none / unsupported. */
@@ -294,12 +305,28 @@ var CairnController = class {
294
305
  for (const l of [...this.listeners]) l();
295
306
  }
296
307
  onAccounts = (accounts) => {
297
- if (!Array.isArray(accounts) || accounts.length === 0) this.setState({ status: "disconnected", account: null });
298
- else this.setState({ status: "connected", account: String(accounts[0]), error: null });
308
+ if (!Array.isArray(accounts) || accounts.length === 0) {
309
+ this.setState({ status: "disconnected", account: null });
310
+ return;
311
+ }
312
+ const next = String(accounts[0]);
313
+ if (this.state.status === "connected" && this.state.account && next.toLowerCase() === this.state.account.toLowerCase()) {
314
+ this.setState({ status: "connected", account: next, error: null });
315
+ } else {
316
+ this.setState({ status: "disconnected", account: null });
317
+ }
299
318
  };
300
319
  onDisconnect = () => {
301
320
  this.setState({ status: "disconnected", account: null });
302
321
  };
322
+ // Detach the provider listeners (ghost-reconnect fix). Idempotent + best-effort.
323
+ detach() {
324
+ try {
325
+ this.conn?.off?.("accountsChanged", this.onAccounts);
326
+ this.conn?.off?.("disconnect", this.onDisconnect);
327
+ } catch {
328
+ }
329
+ }
303
330
  /** Detect + connect (prompts the user the first time per origin). Resolves the connected address. */
304
331
  connect = async () => {
305
332
  this.setState({ status: "connecting", error: null });
@@ -323,6 +350,8 @@ var CairnController = class {
323
350
  await this.conn?.revokePermissions();
324
351
  } catch {
325
352
  }
353
+ this.detach();
354
+ this.conn = null;
326
355
  this.setState({ status: "disconnected", account: null, error: null });
327
356
  };
328
357
  /** Audience-bound sign-in (must connect() first). Returns the signed artifact to verify server-side. */
@@ -25,6 +25,7 @@ declare class CairnController {
25
25
  private setState;
26
26
  private onAccounts;
27
27
  private onDisconnect;
28
+ private detach;
28
29
  /** Detect + connect (prompts the user the first time per origin). Resolves the connected address. */
29
30
  connect: () => Promise<string>;
30
31
  /** Forget the connection locally AND revoke this origin's wallet-side permission (best-effort). */
@@ -25,6 +25,7 @@ declare class CairnController {
25
25
  private setState;
26
26
  private onAccounts;
27
27
  private onDisconnect;
28
+ private detach;
28
29
  /** Detect + connect (prompts the user the first time per origin). Resolves the connected address. */
29
30
  connect: () => Promise<string>;
30
31
  /** Forget the connection locally AND revoke this origin's wallet-side permission (best-effort). */
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  CairnController
3
- } from "./chunk-ZAE2BIH5.js";
4
- import "./chunk-G4OZKZSJ.js";
5
- import "./chunk-UMGWVOOT.js";
3
+ } from "./chunk-PYREGAB3.js";
4
+ import "./chunk-UIEOBVFI.js";
5
+ import "./chunk-JGXD3WIC.js";
6
6
  export {
7
7
  CairnController
8
8
  };
@@ -6,18 +6,29 @@ interface HttpOptions {
6
6
  fetch?: FetchLike;
7
7
  /** Per-request timeout in ms (default 15000). */
8
8
  timeoutMs?: number;
9
+ /** Max response body bytes accepted before aborting (default 16 MiB — matches the cairn proxy cap).
10
+ * CAIRNSDK-DESER-4: the SDK's sources (indexer/content) are self-classified UNTRUSTED; without a cap a
11
+ * hostile/MITM'd source can stream an unbounded body and OOM the client (browser tab / MV3 worker / bot). */
12
+ maxBytes?: number;
9
13
  /** Extra headers sent on every request. */
10
14
  headers?: Record<string, string>;
15
+ /** Opt-in retry budget for IDEMPOTENT GETs only (default 0 = today's behavior). Retries fire on
16
+ * network errors/timeouts and 5xx, never on 4xx (mirrors csd-client's posture), with full-jitter
17
+ * backoff. POSTs are never retried (they may not be idempotent). */
18
+ retries?: number;
11
19
  }
12
20
  declare class Http {
13
21
  readonly baseUrl: string;
14
22
  private readonly _fetch;
15
23
  private readonly timeoutMs;
24
+ private readonly maxBytes;
16
25
  private readonly headers;
26
+ private readonly retries;
17
27
  constructor(opts: HttpOptions);
18
28
  /** Build an absolute URL from a path + optional query params. */
19
29
  url(path: string, query?: Record<string, string | number | boolean | undefined>): string;
20
30
  private raw;
31
+ private parseJson;
21
32
  getJson<T = unknown>(path: string, query?: Record<string, string | number | boolean | undefined>): Promise<T>;
22
33
  postJson<T = unknown>(path: string, body: unknown, headers?: Record<string, string>): Promise<T>;
23
34
  /** GET raw bytes (for content retrieval). Returns null on 404. */
@@ -6,18 +6,29 @@ interface HttpOptions {
6
6
  fetch?: FetchLike;
7
7
  /** Per-request timeout in ms (default 15000). */
8
8
  timeoutMs?: number;
9
+ /** Max response body bytes accepted before aborting (default 16 MiB — matches the cairn proxy cap).
10
+ * CAIRNSDK-DESER-4: the SDK's sources (indexer/content) are self-classified UNTRUSTED; without a cap a
11
+ * hostile/MITM'd source can stream an unbounded body and OOM the client (browser tab / MV3 worker / bot). */
12
+ maxBytes?: number;
9
13
  /** Extra headers sent on every request. */
10
14
  headers?: Record<string, string>;
15
+ /** Opt-in retry budget for IDEMPOTENT GETs only (default 0 = today's behavior). Retries fire on
16
+ * network errors/timeouts and 5xx, never on 4xx (mirrors csd-client's posture), with full-jitter
17
+ * backoff. POSTs are never retried (they may not be idempotent). */
18
+ retries?: number;
11
19
  }
12
20
  declare class Http {
13
21
  readonly baseUrl: string;
14
22
  private readonly _fetch;
15
23
  private readonly timeoutMs;
24
+ private readonly maxBytes;
16
25
  private readonly headers;
26
+ private readonly retries;
17
27
  constructor(opts: HttpOptions);
18
28
  /** Build an absolute URL from a path + optional query params. */
19
29
  url(path: string, query?: Record<string, string | number | boolean | undefined>): string;
20
30
  private raw;
31
+ private parseJson;
21
32
  getJson<T = unknown>(path: string, query?: Record<string, string | number | boolean | undefined>): Promise<T>;
22
33
  postJson<T = unknown>(path: string, body: unknown, headers?: Record<string, string>): Promise<T>;
23
34
  /** GET raw bytes (for content retrieval). Returns null on 404. */