@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/indexer.cjs CHANGED
@@ -24,6 +24,13 @@ __export(indexer_exports, {
24
24
  });
25
25
  module.exports = __toCommonJS(indexer_exports);
26
26
  var import_csd_codec = require("@inversealtruism/csd-codec");
27
+ var HEX64 = /^0x[0-9a-fA-F]{64}$/;
28
+ var MAX_SSE_BUF = 1024 * 1024;
29
+ function isValidMerkleProof(p) {
30
+ if (!p || typeof p !== "object") return false;
31
+ const m = p;
32
+ return Number.isInteger(m.block_height) && m.block_height >= 0 && Number.isInteger(m.pos) && m.pos >= 0 && typeof m.merkle_root === "string" && HEX64.test(m.merkle_root) && Array.isArray(m.merkle) && m.merkle.every((x) => typeof x === "string" && HEX64.test(x));
33
+ }
27
34
  var IndexerClient = class {
28
35
  http;
29
36
  opts;
@@ -44,32 +51,35 @@ var IndexerClient = class {
44
51
  blockHashAtHeight(h) {
45
52
  return this.http.getJson(`/block-height/${h}`);
46
53
  }
54
+ // CAIRNSDK-CONTENT-HASH-PATH-1: encode every caller-supplied id segment. Raw interpolation let an id like
55
+ // `../../api/rpc/tip` walk `new URL` to a DIFFERENT same-origin endpoint whose body was then returned
56
+ // verbatim as the requested object (worse than the content case, which discards the walked-to response).
47
57
  block(hash) {
48
- return this.http.getJson(`/block/${hash}`);
58
+ return this.http.getJson(`/block/${encodeURIComponent(hash)}`);
49
59
  }
50
60
  blockTxids(hash) {
51
- return this.http.getJson(`/block/${hash}/txids`);
61
+ return this.http.getJson(`/block/${encodeURIComponent(hash)}/txids`);
52
62
  }
53
63
  tx(id) {
54
- return this.http.getJson(`/tx/${id}`);
64
+ return this.http.getJson(`/tx/${encodeURIComponent(id)}`);
55
65
  }
56
66
  txStatus(id) {
57
- return this.http.getJson(`/tx/${id}/status`);
67
+ return this.http.getJson(`/tx/${encodeURIComponent(id)}/status`);
58
68
  }
59
69
  txMerkleProof(id) {
60
- return this.http.getJson(`/tx/${id}/merkle-proof`);
70
+ return this.http.getJson(`/tx/${encodeURIComponent(id)}/merkle-proof`);
61
71
  }
62
72
  address(a) {
63
- return this.http.getJson(`/address/${a}`);
73
+ return this.http.getJson(`/address/${encodeURIComponent(a)}`);
64
74
  }
65
75
  addressTxs(a) {
66
- return this.http.getJson(`/address/${a}/txs`);
76
+ return this.http.getJson(`/address/${encodeURIComponent(a)}/txs`);
67
77
  }
68
78
  addressUtxo(a) {
69
- return this.http.getJson(`/address/${a}/utxo`);
79
+ return this.http.getJson(`/address/${encodeURIComponent(a)}/utxo`);
70
80
  }
71
81
  reputation(a) {
72
- return this.http.getJson(`/address/${a}/reputation`);
82
+ return this.http.getJson(`/address/${encodeURIComponent(a)}/reputation`);
73
83
  }
74
84
  domains() {
75
85
  return this.http.getJson("/domains");
@@ -78,10 +88,10 @@ var IndexerClient = class {
78
88
  return this.http.getJson(`/domain/${encodeURIComponent(d)}/proposals`);
79
89
  }
80
90
  proposal(id) {
81
- return this.http.getJson(`/proposal/${id}`);
91
+ return this.http.getJson(`/proposal/${encodeURIComponent(id)}`);
82
92
  }
83
93
  attestations(id) {
84
- return this.http.getJson(`/proposal/${id}/attestations`);
94
+ return this.http.getJson(`/proposal/${encodeURIComponent(id)}/attestations`);
85
95
  }
86
96
  registryPeers() {
87
97
  return this.http.getJson("/registry/peers");
@@ -93,7 +103,7 @@ var IndexerClient = class {
93
103
  return this.http.getJson(`/identity/${encodeURIComponent(handle)}`);
94
104
  }
95
105
  reverseIdentity(addr) {
96
- return this.http.getJson(`/address/${addr}/identity`);
106
+ return this.http.getJson(`/address/${encodeURIComponent(addr)}/identity`);
97
107
  }
98
108
  // ---- trust-minimized inclusion ----------------------------------------
99
109
  /**
@@ -102,9 +112,11 @@ var IndexerClient = class {
102
112
  * proof's root is cross-checked against the header merkle at that height →
103
113
  * trustLevel "verified-inclusion" (only as trustworthy as that header source —
104
114
  * see headerMerkleAt). Otherwise the proof is only internally consistent →
105
- * "proof-consistent" (still useful, but the indexer is trusted). The Cairn
106
- * facade wires headerMerkleAt ONLY when the node RPC is an independent origin
107
- * from the indexer, so the default same-origin setup stays at "proof-consistent".
115
+ * "proof-consistent" (still useful, but the indexer is trusted). INDEXER-DOC-STALE-1: the Cairn facade now
116
+ * wires headerMerkleAt UNCONDITIONALLY via a PoW-verifying checkpoint light client (index.ts), so the
117
+ * default reaches "verified-inclusion" when the proof root matches the PoW-verified header — what makes a
118
+ * same-origin RPC safe is the forward PoW re-verification, not an origin gate (which no longer exists).
119
+ * Fails closed: a malformed proof or a chain-disagreement returns "not-found" (the latter with equivocation:true).
108
120
  */
109
121
  async verifyInclusion(txid) {
110
122
  let proof;
@@ -113,7 +125,15 @@ var IndexerClient = class {
113
125
  } catch {
114
126
  return { included: false, trustLevel: "not-found", reason: "no merkle proof (tx not indexed)" };
115
127
  }
116
- const folds = (0, import_csd_codec.verifyMerkleProof)(txid, proof.pos, proof.merkle, proof.merkle_root);
128
+ if (!isValidMerkleProof(proof)) {
129
+ return { included: false, trustLevel: "not-found", reason: "malformed merkle proof" };
130
+ }
131
+ let folds = false;
132
+ try {
133
+ folds = (0, import_csd_codec.verifyMerkleProof)(txid, proof.pos, proof.merkle, proof.merkle_root);
134
+ } catch {
135
+ return { included: false, trustLevel: "not-found", blockHeight: proof.block_height, reason: "malformed merkle proof" };
136
+ }
117
137
  if (!folds) {
118
138
  return { included: false, trustLevel: "not-found", blockHeight: proof.block_height, reason: "merkle branch does not fold to the claimed root" };
119
139
  }
@@ -121,7 +141,7 @@ var IndexerClient = class {
121
141
  try {
122
142
  const onchain = await this.opts.headerMerkleAt(proof.block_height);
123
143
  if (norm(onchain) !== norm(proof.merkle_root)) {
124
- return { included: false, trustLevel: "not-found", blockHeight: proof.block_height, reason: "proof root != on-chain header merkle (indexer disagrees with chain)" };
144
+ return { included: false, trustLevel: "not-found", equivocation: true, blockHeight: proof.block_height, reason: "proof root != on-chain header merkle (indexer disagrees with chain)" };
125
145
  }
126
146
  return { included: true, trustLevel: "verified-inclusion", blockHeight: proof.block_height, pos: proof.pos, merkleRoot: proof.merkle_root };
127
147
  } catch {
@@ -145,6 +165,11 @@ var IndexerClient = class {
145
165
  /**
146
166
  * Subscribe over WebSocket with selective tracking. Returns a handle with
147
167
  * `.close()`. `onEvent` receives every matching IndexEvent (incl. reorgs).
168
+ *
169
+ * WS-NORECONNECT-1: unlike the SSE feeds (streamAll/streamBlocks/streamDomain, which auto-reconnect with
170
+ * backoff), this WebSocket subscription does NOT auto-reconnect — it surfaces a terminal error via
171
+ * `onError` (when provided) and stops. A consumer tracking reorgs over WS MUST re-`subscribe()` on error
172
+ * to avoid a silently-stale view; for built-in reconnect prefer the SSE `stream*` methods.
148
173
  */
149
174
  subscribe(sub, onEvent, onError) {
150
175
  const WS = this.opts.WebSocketImpl ?? globalThis.WebSocket;
@@ -207,13 +232,20 @@ var IndexerClient = class {
207
232
  buf = buf.slice(idx + 2);
208
233
  dispatchFrame(frame, handlers);
209
234
  }
235
+ if (buf.length > MAX_SSE_BUF) {
236
+ try {
237
+ await reader.cancel();
238
+ } catch {
239
+ }
240
+ throw new Error(`SSE buffer exceeded ${MAX_SSE_BUF} bytes with no frame delimiter \u2014 aborting`);
241
+ }
210
242
  }
211
243
  } catch (err) {
212
244
  if (closed) return;
213
245
  handlers.onError?.(err);
214
246
  }
215
247
  if (closed) return;
216
- await new Promise((r) => setTimeout(r, 2e3));
248
+ await new Promise((r) => setTimeout(r, 1e3 + Math.floor(Math.random() * 2e3)));
217
249
  }
218
250
  };
219
251
  void run();
@@ -1,4 +1,4 @@
1
- import { H as Http, F as FetchLike } from './http-D2CzfOsm.cjs';
1
+ import { H as Http, F as FetchLike } from './http-C9C4L4hz.cjs';
2
2
 
3
3
  interface MerkleProof {
4
4
  block_height: number;
@@ -14,6 +14,10 @@ interface InclusionResult {
14
14
  pos?: number;
15
15
  merkleRoot?: string;
16
16
  reason?: string;
17
+ /** CAIRN-SPV-3: set true when a folding proof's root conflicts with the PoW-verified on-chain header
18
+ * merkle (active indexer↔chain equivocation/tamper) — distinct from an honest tx-absence. Consumers that
19
+ * retry on `not-found` MUST check this and treat it as a detected attack, NOT a benign miss. */
20
+ equivocation?: boolean;
17
21
  }
18
22
  /** A live index event ({ kind: "block" | "proposal" | "attestation" | "reorg", ... }). */
19
23
  interface IndexEvent {
@@ -71,9 +75,11 @@ declare class IndexerClient {
71
75
  * proof's root is cross-checked against the header merkle at that height →
72
76
  * trustLevel "verified-inclusion" (only as trustworthy as that header source —
73
77
  * see headerMerkleAt). Otherwise the proof is only internally consistent →
74
- * "proof-consistent" (still useful, but the indexer is trusted). The Cairn
75
- * facade wires headerMerkleAt ONLY when the node RPC is an independent origin
76
- * from the indexer, so the default same-origin setup stays at "proof-consistent".
78
+ * "proof-consistent" (still useful, but the indexer is trusted). INDEXER-DOC-STALE-1: the Cairn facade now
79
+ * wires headerMerkleAt UNCONDITIONALLY via a PoW-verifying checkpoint light client (index.ts), so the
80
+ * default reaches "verified-inclusion" when the proof root matches the PoW-verified header — what makes a
81
+ * same-origin RPC safe is the forward PoW re-verification, not an origin gate (which no longer exists).
82
+ * Fails closed: a malformed proof or a chain-disagreement returns "not-found" (the latter with equivocation:true).
77
83
  */
78
84
  verifyInclusion(txid: string): Promise<InclusionResult>;
79
85
  /** Stream all events (blocks, proposals, attestations, reorgs) via SSE. */
@@ -85,6 +91,11 @@ declare class IndexerClient {
85
91
  /**
86
92
  * Subscribe over WebSocket with selective tracking. Returns a handle with
87
93
  * `.close()`. `onEvent` receives every matching IndexEvent (incl. reorgs).
94
+ *
95
+ * WS-NORECONNECT-1: unlike the SSE feeds (streamAll/streamBlocks/streamDomain, which auto-reconnect with
96
+ * backoff), this WebSocket subscription does NOT auto-reconnect — it surfaces a terminal error via
97
+ * `onError` (when provided) and stops. A consumer tracking reorgs over WS MUST re-`subscribe()` on error
98
+ * to avoid a silently-stale view; for built-in reconnect prefer the SSE `stream*` methods.
88
99
  */
89
100
  subscribe(sub: {
90
101
  all?: boolean;
package/dist/indexer.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { H as Http, F as FetchLike } from './http-D2CzfOsm.js';
1
+ import { H as Http, F as FetchLike } from './http-C9C4L4hz.js';
2
2
 
3
3
  interface MerkleProof {
4
4
  block_height: number;
@@ -14,6 +14,10 @@ interface InclusionResult {
14
14
  pos?: number;
15
15
  merkleRoot?: string;
16
16
  reason?: string;
17
+ /** CAIRN-SPV-3: set true when a folding proof's root conflicts with the PoW-verified on-chain header
18
+ * merkle (active indexer↔chain equivocation/tamper) — distinct from an honest tx-absence. Consumers that
19
+ * retry on `not-found` MUST check this and treat it as a detected attack, NOT a benign miss. */
20
+ equivocation?: boolean;
17
21
  }
18
22
  /** A live index event ({ kind: "block" | "proposal" | "attestation" | "reorg", ... }). */
19
23
  interface IndexEvent {
@@ -71,9 +75,11 @@ declare class IndexerClient {
71
75
  * proof's root is cross-checked against the header merkle at that height →
72
76
  * trustLevel "verified-inclusion" (only as trustworthy as that header source —
73
77
  * see headerMerkleAt). Otherwise the proof is only internally consistent →
74
- * "proof-consistent" (still useful, but the indexer is trusted). The Cairn
75
- * facade wires headerMerkleAt ONLY when the node RPC is an independent origin
76
- * from the indexer, so the default same-origin setup stays at "proof-consistent".
78
+ * "proof-consistent" (still useful, but the indexer is trusted). INDEXER-DOC-STALE-1: the Cairn facade now
79
+ * wires headerMerkleAt UNCONDITIONALLY via a PoW-verifying checkpoint light client (index.ts), so the
80
+ * default reaches "verified-inclusion" when the proof root matches the PoW-verified header — what makes a
81
+ * same-origin RPC safe is the forward PoW re-verification, not an origin gate (which no longer exists).
82
+ * Fails closed: a malformed proof or a chain-disagreement returns "not-found" (the latter with equivocation:true).
77
83
  */
78
84
  verifyInclusion(txid: string): Promise<InclusionResult>;
79
85
  /** Stream all events (blocks, proposals, attestations, reorgs) via SSE. */
@@ -85,6 +91,11 @@ declare class IndexerClient {
85
91
  /**
86
92
  * Subscribe over WebSocket with selective tracking. Returns a handle with
87
93
  * `.close()`. `onEvent` receives every matching IndexEvent (incl. reorgs).
94
+ *
95
+ * WS-NORECONNECT-1: unlike the SSE feeds (streamAll/streamBlocks/streamDomain, which auto-reconnect with
96
+ * backoff), this WebSocket subscription does NOT auto-reconnect — it surfaces a terminal error via
97
+ * `onError` (when provided) and stops. A consumer tracking reorgs over WS MUST re-`subscribe()` on error
98
+ * to avoid a silently-stale view; for built-in reconnect prefer the SSE `stream*` methods.
88
99
  */
89
100
  subscribe(sub: {
90
101
  all?: boolean;
package/dist/indexer.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  IndexerClient
3
- } from "./chunk-FNBE2FPE.js";
3
+ } from "./chunk-J5J4JEJ6.js";
4
4
  export {
5
5
  IndexerClient
6
6
  };
package/dist/react.cjs CHANGED
@@ -26,7 +26,7 @@ __export(react_exports, {
26
26
  module.exports = __toCommonJS(react_exports);
27
27
 
28
28
  // src/errors.ts
29
- var SDK_VERSION = "0.1.2";
29
+ var SDK_VERSION = "0.2.0";
30
30
  var DOCS_BASE = "https://cairn-substrate.com/docs/sdk";
31
31
  var CairnError = class extends Error {
32
32
  /** Stable machine code for programmatic branching. */
@@ -83,7 +83,12 @@ var UnsupportedMethodError = class extends CairnError {
83
83
  super(message, { code: "UNSUPPORTED_METHOD", docsPath: "/errors#unsupported-method" });
84
84
  }
85
85
  };
86
- function mapProviderError(error) {
86
+ var NATIVE_CODES = /* @__PURE__ */ new Set(["ACCOUNT_CHANGED", "FIRST_PARTY_ONLY", "RATE_LIMITED", "APPROVAL_CLOSED", "FORBIDDEN", "INTERNAL"]);
87
+ function mapProviderError(error, code) {
88
+ if (code === "USER_REJECTED" || code === "APPROVAL_CLOSED") return new UserRejectedError(error);
89
+ if (code === "WALLET_LOCKED") return new WalletLockedError(error);
90
+ if (code === "UNSUPPORTED_METHOD" || code === "UNKNOWN_KIND") return new UnsupportedMethodError(error);
91
+ if (code && NATIVE_CODES.has(code)) return new CairnError(error || code, { code });
87
92
  const e = String(error || "").toLowerCase();
88
93
  if (e.includes("rejected by user")) return new UserRejectedError(error);
89
94
  if (e.includes("wallet locked")) return new WalletLockedError(error);
@@ -136,7 +141,7 @@ function detectProvider(opts = {}) {
136
141
  }
137
142
  function unwrap(reply) {
138
143
  if (reply && reply.ok) return reply.result;
139
- throw mapProviderError(reply?.error ?? "Unknown wallet error");
144
+ throw mapProviderError(reply?.error ?? "Unknown wallet error", reply?.code);
140
145
  }
141
146
  var WalletConnection = class {
142
147
  provider;
@@ -170,6 +175,9 @@ var WalletConnection = class {
170
175
  * Passwordless sign-in: a signature over a login nonce (always prompts; structurally cannot sign a tx).
171
176
  */
172
177
  signIn() {
178
+ if (typeof this.provider.signIn !== "function") {
179
+ return Promise.reject(new UnsupportedMethodError("this wallet does not expose signIn()"));
180
+ }
173
181
  return Promise.resolve(this.provider.signIn()).then(unwrap);
174
182
  }
175
183
  /**
@@ -203,6 +211,9 @@ var WalletConnection = class {
203
211
  }
204
212
  /** Atomic fill (CairnX DvP): pay + attest in ONE tx. Always prompts with clear-signing. */
205
213
  fillOffer(params) {
214
+ if (typeof this.provider.fillOffer !== "function") {
215
+ return Promise.reject(new UnsupportedMethodError("this wallet predates fillOffer() \u2014 ask the user to update the Cairn Wallet"));
216
+ }
206
217
  return Promise.resolve(this.provider.fillOffer(params)).then(unwrap);
207
218
  }
208
219
  /** This origin's current permission grant (EIP-2255-style). Silent; [] if none / unsupported. */
@@ -295,12 +306,28 @@ var CairnController = class {
295
306
  for (const l of [...this.listeners]) l();
296
307
  }
297
308
  onAccounts = (accounts) => {
298
- if (!Array.isArray(accounts) || accounts.length === 0) this.setState({ status: "disconnected", account: null });
299
- else this.setState({ status: "connected", account: String(accounts[0]), error: null });
309
+ if (!Array.isArray(accounts) || accounts.length === 0) {
310
+ this.setState({ status: "disconnected", account: null });
311
+ return;
312
+ }
313
+ const next = String(accounts[0]);
314
+ if (this.state.status === "connected" && this.state.account && next.toLowerCase() === this.state.account.toLowerCase()) {
315
+ this.setState({ status: "connected", account: next, error: null });
316
+ } else {
317
+ this.setState({ status: "disconnected", account: null });
318
+ }
300
319
  };
301
320
  onDisconnect = () => {
302
321
  this.setState({ status: "disconnected", account: null });
303
322
  };
323
+ // Detach the provider listeners (ghost-reconnect fix). Idempotent + best-effort.
324
+ detach() {
325
+ try {
326
+ this.conn?.off?.("accountsChanged", this.onAccounts);
327
+ this.conn?.off?.("disconnect", this.onDisconnect);
328
+ } catch {
329
+ }
330
+ }
304
331
  /** Detect + connect (prompts the user the first time per origin). Resolves the connected address. */
305
332
  connect = async () => {
306
333
  this.setState({ status: "connecting", error: null });
@@ -324,6 +351,8 @@ var CairnController = class {
324
351
  await this.conn?.revokePermissions();
325
352
  } catch {
326
353
  }
354
+ this.detach();
355
+ this.conn = null;
327
356
  this.setState({ status: "disconnected", account: null, error: null });
328
357
  };
329
358
  /** Audience-bound sign-in (must connect() first). Returns the signed artifact to verify server-side. */
package/dist/react.js CHANGED
@@ -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
 
7
7
  // src/react.ts
8
8
  function createCairnHooks(React, controllerOrOpts) {
package/dist/registry.cjs CHANGED
@@ -48,11 +48,20 @@ var RegistryClient = class {
48
48
  peers() {
49
49
  return (0, import_csd_registry.discoverPeers)(this.src);
50
50
  }
51
- /** Resolve a handle → identity (GET /identity/:handle). Returns null if unresolved. */
51
+ /**
52
+ * Resolve a handle → identity (GET /identity/:handle). Returns null if unresolved.
53
+ *
54
+ * ⚠ TRUST (CAIRN-SDK-RESOLVE-VERIFIED-FOOTGUN): the returned `verified` flag is the INDEXER's assertion,
55
+ * NOT a client-side proof. A hostile/compromised indexer (or a MITM of the read proxy) can return
56
+ * `verified:true` for a wrong address. Do NOT use this directly as a payee/login target without either (a)
57
+ * recomputing from raw records via `fromRecords.name(...)`, or (b) cross-checking a second independent
58
+ * source (the wallet's namespv union cure). Treat `verified` here as a hint, not a guarantee.
59
+ */
52
60
  resolveName(handle) {
53
61
  return (0, import_csd_registry.resolveName)(this.src, handle);
54
62
  }
55
- /** Reverse-resolve an address → identity (GET /address/:addr/identity). */
63
+ /** Reverse-resolve an address → identity (GET /address/:addr/identity).
64
+ * ⚠ Same indexer-trust caveat as resolveName — `verified` is the indexer's claim, not a client proof. */
56
65
  reverseName(address) {
57
66
  return (0, import_csd_registry.reverseName)(this.src, address);
58
67
  }
@@ -1,6 +1,6 @@
1
1
  import * as _inversealtruism_csd_registry from '@inversealtruism/csd-registry';
2
2
  export { BuiltRecord, ChainRecord, DOMAINS, RankedGateway, RankedPeer, ResolveOpts, ResolvedIdentity, buildGatewayRecord, buildIdentityCommit, buildIdentityReveal, buildPeerRecord, epochOf, fromRecords, verifyGateway, verifyIdentitySig, verifyPeer } from '@inversealtruism/csd-registry';
3
- import { F as FetchLike } from './http-D2CzfOsm.cjs';
3
+ import { F as FetchLike } from './http-C9C4L4hz.cjs';
4
4
 
5
5
  interface RegistryClientOptions {
6
6
  /** Indexer base URL exposing /registry/* + /identity/* (e.g. https://cairn-substrate.com/indexer). */
@@ -14,9 +14,18 @@ declare class RegistryClient {
14
14
  gateways(): Promise<_inversealtruism_csd_registry.RankedGateway[]>;
15
15
  /** Discover ranked libp2p peers (GET /registry/peers). */
16
16
  peers(): Promise<_inversealtruism_csd_registry.RankedPeer[]>;
17
- /** Resolve a handle → identity (GET /identity/:handle). Returns null if unresolved. */
17
+ /**
18
+ * Resolve a handle → identity (GET /identity/:handle). Returns null if unresolved.
19
+ *
20
+ * ⚠ TRUST (CAIRN-SDK-RESOLVE-VERIFIED-FOOTGUN): the returned `verified` flag is the INDEXER's assertion,
21
+ * NOT a client-side proof. A hostile/compromised indexer (or a MITM of the read proxy) can return
22
+ * `verified:true` for a wrong address. Do NOT use this directly as a payee/login target without either (a)
23
+ * recomputing from raw records via `fromRecords.name(...)`, or (b) cross-checking a second independent
24
+ * source (the wallet's namespv union cure). Treat `verified` here as a hint, not a guarantee.
25
+ */
18
26
  resolveName(handle: string): Promise<_inversealtruism_csd_registry.ResolvedIdentity | null>;
19
- /** Reverse-resolve an address → identity (GET /address/:addr/identity). */
27
+ /** Reverse-resolve an address → identity (GET /address/:addr/identity).
28
+ * ⚠ Same indexer-trust caveat as resolveName — `verified` is the indexer's claim, not a client proof. */
20
29
  reverseName(address: string): Promise<_inversealtruism_csd_registry.ResolvedIdentity | null>;
21
30
  /** The deterministic, client-side resolvers (recompute from raw ChainRecord[]). */
22
31
  get fromRecords(): {
@@ -1,6 +1,6 @@
1
1
  import * as _inversealtruism_csd_registry from '@inversealtruism/csd-registry';
2
2
  export { BuiltRecord, ChainRecord, DOMAINS, RankedGateway, RankedPeer, ResolveOpts, ResolvedIdentity, buildGatewayRecord, buildIdentityCommit, buildIdentityReveal, buildPeerRecord, epochOf, fromRecords, verifyGateway, verifyIdentitySig, verifyPeer } from '@inversealtruism/csd-registry';
3
- import { F as FetchLike } from './http-D2CzfOsm.js';
3
+ import { F as FetchLike } from './http-C9C4L4hz.js';
4
4
 
5
5
  interface RegistryClientOptions {
6
6
  /** Indexer base URL exposing /registry/* + /identity/* (e.g. https://cairn-substrate.com/indexer). */
@@ -14,9 +14,18 @@ declare class RegistryClient {
14
14
  gateways(): Promise<_inversealtruism_csd_registry.RankedGateway[]>;
15
15
  /** Discover ranked libp2p peers (GET /registry/peers). */
16
16
  peers(): Promise<_inversealtruism_csd_registry.RankedPeer[]>;
17
- /** Resolve a handle → identity (GET /identity/:handle). Returns null if unresolved. */
17
+ /**
18
+ * Resolve a handle → identity (GET /identity/:handle). Returns null if unresolved.
19
+ *
20
+ * ⚠ TRUST (CAIRN-SDK-RESOLVE-VERIFIED-FOOTGUN): the returned `verified` flag is the INDEXER's assertion,
21
+ * NOT a client-side proof. A hostile/compromised indexer (or a MITM of the read proxy) can return
22
+ * `verified:true` for a wrong address. Do NOT use this directly as a payee/login target without either (a)
23
+ * recomputing from raw records via `fromRecords.name(...)`, or (b) cross-checking a second independent
24
+ * source (the wallet's namespv union cure). Treat `verified` here as a hint, not a guarantee.
25
+ */
18
26
  resolveName(handle: string): Promise<_inversealtruism_csd_registry.ResolvedIdentity | null>;
19
- /** Reverse-resolve an address → identity (GET /address/:addr/identity). */
27
+ /** Reverse-resolve an address → identity (GET /address/:addr/identity).
28
+ * ⚠ Same indexer-trust caveat as resolveName — `verified` is the indexer's claim, not a client proof. */
20
29
  reverseName(address: string): Promise<_inversealtruism_csd_registry.ResolvedIdentity | null>;
21
30
  /** The deterministic, client-side resolvers (recompute from raw ChainRecord[]). */
22
31
  get fromRecords(): {
package/dist/registry.js CHANGED
@@ -10,7 +10,7 @@ import {
10
10
  verifyGateway,
11
11
  verifyIdentitySig,
12
12
  verifyPeer
13
- } from "./chunk-G7FABIF4.js";
13
+ } from "./chunk-BGJZF7S2.js";
14
14
  export {
15
15
  DOMAINS,
16
16
  RegistryClient,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@inversealtruism/cairn-sdk",
3
- "version": "0.1.3",
4
- "description": "The Compute Substrate dApp kit — one cohesive toolkit to connect the Cairn Wallet, read the chain/board, publish self-certifying content, and resolve registries. Composes the published csd-* primitives.",
3
+ "version": "0.2.0",
4
+ "description": "The Compute Substrate dApp kit \u2014 one cohesive toolkit to connect the Cairn Wallet, read the chain/board, publish self-certifying content, and resolve registries. Composes the published csd-* primitives.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
7
7
  "module": "./dist/index.js",
@@ -58,22 +58,43 @@
58
58
  "LICENSE",
59
59
  "README.md"
60
60
  ],
61
+ "scripts": {
62
+ "build": "tsup src/index.ts src/connect.ts src/board.ts src/indexer.ts src/content.ts src/registry.ts src/chain.ts src/controller.ts src/react.ts --format esm,cjs --dts --clean",
63
+ "test": "tsx test/sdk.test.ts && tsx test/connect.test.ts && tsx test/errors.test.ts && tsx test/controller.test.ts && tsx test/react.test.ts && tsx test/hardening.test.ts && tsx test/spv-checkpoint.test.ts && tsx test/names-b10.test.ts",
64
+ "test:live": "node examples/read-only.mjs",
65
+ "build:example": "node scripts/build-example.mjs",
66
+ "clean": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\"",
67
+ "test:e2e:read": "node test/e2e/e2e-read.mjs",
68
+ "test:e2e:wallet": "xvfb-run -a node test/e2e/e2e-wallet-connect.mjs",
69
+ "test:e2e:write": "node test/e2e/e2e-write.mjs",
70
+ "test:e2e": "node test/e2e/e2e-read.mjs && xvfb-run -a node test/e2e/e2e-wallet-connect.mjs",
71
+ "test:all": "pnpm test && pnpm test:e2e",
72
+ "prepublishOnly": "npm run build && npm test"
73
+ },
61
74
  "dependencies": {
62
- "@inversealtruism/csd-client": "0.1.13",
63
- "@inversealtruism/csd-codec": "0.1.13",
64
- "@inversealtruism/csd-crypto": "0.1.13",
65
- "@inversealtruism/csd-light": "0.1.13",
66
- "@inversealtruism/csd-registry": "0.1.13",
67
- "@inversealtruism/csd-tx": "0.1.13"
75
+ "@inversealtruism/cairnx-core": "0.1.31",
76
+ "@inversealtruism/csd-client": "0.1.15",
77
+ "@inversealtruism/csd-codec": "0.1.15",
78
+ "@inversealtruism/csd-crypto": "0.1.15",
79
+ "@inversealtruism/csd-light": "0.1.15",
80
+ "@inversealtruism/csd-registry": "0.1.15",
81
+ "@inversealtruism/csd-tx": "0.1.15"
68
82
  },
69
83
  "devDependencies": {
70
84
  "esbuild": "^0.27.0",
71
85
  "playwright-core": "^1.49.0",
72
86
  "tsup": "^8.5.1",
73
87
  "tsx": "^4.19.2",
74
- "typescript": "^5.6.0"
88
+ "typescript": "^5.6.0",
89
+ "@inversealtruism/csd-siwc": "0.1.15"
75
90
  },
76
91
  "sideEffects": false,
92
+ "packageManager": "pnpm@10.32.1",
93
+ "pnpm": {
94
+ "onlyBuiltDependencies": [
95
+ "esbuild"
96
+ ]
97
+ },
77
98
  "license": "MIT",
78
99
  "publishConfig": {
79
100
  "access": "public"
@@ -91,17 +112,5 @@
91
112
  "dapp",
92
113
  "sdk",
93
114
  "blockchain"
94
- ],
95
- "scripts": {
96
- "build": "tsup src/index.ts src/connect.ts src/board.ts src/indexer.ts src/content.ts src/registry.ts src/chain.ts src/controller.ts src/react.ts --format esm,cjs --dts --clean",
97
- "test": "tsx test/sdk.test.ts && tsx test/connect.test.ts && tsx test/errors.test.ts && tsx test/controller.test.ts && tsx test/react.test.ts",
98
- "test:live": "node examples/read-only.mjs",
99
- "build:example": "node scripts/build-example.mjs",
100
- "clean": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\"",
101
- "test:e2e:read": "node test/e2e/e2e-read.mjs",
102
- "test:e2e:wallet": "xvfb-run -a node test/e2e/e2e-wallet-connect.mjs",
103
- "test:e2e:write": "node test/e2e/e2e-write.mjs",
104
- "test:e2e": "node test/e2e/e2e-read.mjs && xvfb-run -a node test/e2e/e2e-wallet-connect.mjs",
105
- "test:all": "pnpm test && pnpm test:e2e"
106
- }
107
- }
115
+ ]
116
+ }