@inversealtruism/cairn-sdk 0.4.0 → 0.4.1

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/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.4.0";
29
+ var SDK_VERSION = "0.4.1";
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. */
@@ -213,12 +213,15 @@ var WalletConnection = class {
213
213
  // genuine wallet resolves quickly once the user acts; this only fires on a truly hung/absent response.
214
214
  call(op, label) {
215
215
  const ms = this._timeoutMs;
216
- if (!Number.isFinite(ms) || ms <= 0) return Promise.resolve(op());
216
+ const invoke = () => new Promise((resolve) => {
217
+ resolve(op());
218
+ });
219
+ if (!Number.isFinite(ms) || ms <= 0) return invoke();
217
220
  let timer;
218
221
  const timeout = new Promise((_, reject) => {
219
222
  timer = setTimeout(() => reject(new CairnError(`the wallet did not respond to ${label} within ${ms}ms`, { retryable: false })), ms);
220
223
  });
221
- return Promise.race([Promise.resolve(op()), timeout]).finally(() => clearTimeout(timer));
224
+ return Promise.race([invoke(), timeout]).finally(() => clearTimeout(timer));
222
225
  }
223
226
  /** The wallet's version string (e.g. "0.2.x"). */
224
227
  get version() {
@@ -291,8 +294,24 @@ var WalletConnection = class {
291
294
  if (typeof this.provider.fillOffer !== "function") {
292
295
  return Promise.reject(new UnsupportedMethodError("this wallet predates fillOffer() \u2014 ask the user to update the Cairn Wallet"));
293
296
  }
297
+ this.adviseFillSpvCapability();
294
298
  return this.call(() => this.provider.fillOffer(params), "fillOffer").then(unwrapWrite);
295
299
  }
300
+ // P75-5 MF-10: ONE capability advisory per connection. The wallet's on-device fill-SPV (0.2.60+) is the
301
+ // payment-grade fill boundary; when the provider SELF-REPORTS an older version, warn the integrator once
302
+ // and STILL RELAY. WARN, NEVER BLOCK: a hard version gate would decline honest fills (version strings are
303
+ // spoofable and unparseable forks exist), and the wallet itself is the enforcement point. Reads only the
304
+ // sync `provider.version`; never an awaited getCapabilities() round-trip on the fill hot path. An
305
+ // unparseable version never warns (no false alarms).
306
+ _fillSpvAdvised = false;
307
+ adviseFillSpvCapability() {
308
+ if (this._fillSpvAdvised) return;
309
+ this._fillSpvAdvised = true;
310
+ const m = /^(\d+)\.(\d+)\.(\d+)/.exec(String(this.provider?.version ?? ""));
311
+ if (!m) return;
312
+ const below = Number(m[1]) === 0 && (Number(m[2]) < 2 || Number(m[2]) === 2 && Number(m[3]) < 60);
313
+ if (below) console.warn(`[cairn-sdk] this Cairn Wallet (v${m[0]}) predates the on-device fill-SPV boundary (cairn-wallet 0.2.60+). The fill proceeds; suggest a wallet update for payment-grade offer verification.`);
314
+ }
296
315
  /** This origin's current permission grant (EIP-2255-style). Silent; [] if none / unsupported. */
297
316
  async getPermissions() {
298
317
  if (typeof this.provider.getPermissions !== "function") return [];
@@ -355,6 +374,9 @@ var DISCONNECTED = Object.freeze({ status: "disconnected", account: null, error:
355
374
  var CairnController = class {
356
375
  state = DISCONNECTED;
357
376
  conn = null;
377
+ // MF-15: connect/disconnect generation token. disconnect() bumps it; a connect() whose await straddled
378
+ // the bump must never commit "connected" over the torn-down session (a zombie null-connection session).
379
+ epoch = 0;
358
380
  listeners = /* @__PURE__ */ new Set();
359
381
  getWalletFn;
360
382
  detectOpts;
@@ -407,34 +429,43 @@ var CairnController = class {
407
429
  }
408
430
  /** Detect + connect (prompts the user the first time per origin). Resolves the connected address. */
409
431
  connect = async () => {
432
+ const epoch = this.epoch;
410
433
  this.setState({ status: "connecting", error: null });
411
434
  try {
412
- if (!this.conn) {
413
- this.conn = await this.getWalletFn(this.detectOpts);
414
- this.conn.on("accountsChanged", this.onAccounts);
415
- this.conn.on("disconnect", this.onDisconnect);
435
+ let conn = this.conn;
436
+ if (!conn) {
437
+ conn = await this.getWalletFn(this.detectOpts);
438
+ if (epoch !== this.epoch) throw new Error("connection was torn down while connecting - call connect() again");
439
+ conn.on("accountsChanged", this.onAccounts);
440
+ conn.on("disconnect", this.onDisconnect);
441
+ this.conn = conn;
416
442
  }
417
- const addr = await this.conn.connect();
443
+ const addr = await conn.connect();
444
+ if (epoch !== this.epoch) throw new Error("connection was torn down while awaiting approval - call connect() again");
418
445
  this.setState({ status: "connected", account: addr, error: null });
419
446
  return addr;
420
447
  } catch (e) {
421
- this.setState({ status: "disconnected", account: null, error: e?.message ?? String(e) });
448
+ if (epoch === this.epoch) this.setState({ status: "disconnected", account: null, error: e?.message ?? String(e) });
422
449
  throw e;
423
450
  }
424
451
  };
425
- /** Forget the connection locally AND revoke this origin's wallet-side permission (best-effort). */
452
+ /** Forget the connection locally AND revoke this origin's wallet-side permission (best-effort).
453
+ * Local teardown is IMMEDIATE and unconditional; the wallet-side revoke happens after, so a hung
454
+ * provider can never hold a torn-down session in "connected". */
426
455
  disconnect = async () => {
427
- try {
428
- await this.conn?.revokePermissions();
429
- } catch {
430
- }
456
+ this.epoch++;
457
+ const conn = this.conn;
431
458
  this.detach();
432
459
  this.conn = null;
433
460
  this.setState({ status: "disconnected", account: null, error: null });
461
+ try {
462
+ await conn?.revokePermissions();
463
+ } catch {
464
+ }
434
465
  };
435
466
  /** Audience-bound sign-in (must connect() first). Returns the signed artifact to verify server-side. */
436
467
  signInWithCsd = (params) => {
437
- if (!this.conn) return Promise.reject(new Error("call connect() before signInWithCsd()"));
468
+ if (!this.conn || this.state.status !== "connected") return Promise.reject(new Error("call connect() before signInWithCsd()"));
438
469
  return this.conn.signInWithCsd(params);
439
470
  };
440
471
  };
package/dist/react.js CHANGED
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  CairnController
3
- } from "./chunk-GTTLMWGY.js";
4
- import "./chunk-RKR3JMIA.js";
5
- import "./chunk-MREZG2HN.js";
3
+ } from "./chunk-QATE6AIP.js";
4
+ import "./chunk-PIPN6FSA.js";
5
+ import "./chunk-ERBT4DFD.js";
6
6
 
7
7
  // src/react.ts
8
8
  function createCairnHooks(React, controllerOrOpts) {
package/dist/registry.cjs CHANGED
@@ -34,19 +34,221 @@ __export(registry_exports, {
34
34
  });
35
35
  module.exports = __toCommonJS(registry_exports);
36
36
  var import_csd_registry = require("@inversealtruism/csd-registry");
37
+
38
+ // src/errors.ts
39
+ var SDK_VERSION = "0.4.1";
40
+ var DOCS_BASE = "https://cairn-substrate.com/docs/sdk";
41
+ var CairnError = class extends Error {
42
+ /** Stable machine code for programmatic branching. */
43
+ code;
44
+ /** The human message without the appended docs/version footer. */
45
+ shortMessage;
46
+ /** Relative docs path (under the SDK docs base), if any. */
47
+ docsPath;
48
+ /** The cairn-sdk version that produced this error. */
49
+ version = SDK_VERSION;
50
+ /**
51
+ * Whether a dApp may safely AUTO-retry this failure. `false` for fund-safety refusals and user-intent
52
+ * declines (the default); `true` only for provably transient infra faults where nothing was signed.
53
+ * A `SubmitInFlightError` is `false` too: reconcile its `txid`, never blind-retry.
54
+ */
55
+ retryable;
56
+ constructor(shortMessage, opts = {}) {
57
+ const docs = opts.docsPath;
58
+ super(
59
+ [shortMessage, docs ? `Docs: ${DOCS_BASE}${docs}` : "", `Version: cairn-sdk@${SDK_VERSION}`].filter(Boolean).join("\n"),
60
+ { cause: opts.cause }
61
+ );
62
+ this.name = new.target.name;
63
+ this.code = opts.code ?? "UNKNOWN";
64
+ this.shortMessage = shortMessage;
65
+ this.docsPath = docs;
66
+ this.retryable = opts.retryable ?? false;
67
+ }
68
+ /**
69
+ * Walk the `cause` chain. With a predicate, returns the FIRST matching error (or null). Without one,
70
+ * returns the DEEPEST cause (the root). Mirrors viem's `BaseError.walk()`.
71
+ */
72
+ walk(fn) {
73
+ let current = this;
74
+ let last = this;
75
+ while (current != null) {
76
+ if (fn && fn(current)) return current;
77
+ last = current;
78
+ current = current?.cause;
79
+ }
80
+ return fn ? null : last;
81
+ }
82
+ };
83
+ var HttpError = class extends CairnError {
84
+ status;
85
+ url;
86
+ body;
87
+ constructor(status, url, body) {
88
+ super(`HTTP ${status} for ${url}${body ? `: ${String(body).slice(0, 200)}` : ""}`, { code: "HTTP_ERROR" });
89
+ this.status = status;
90
+ this.url = url;
91
+ this.body = body;
92
+ }
93
+ };
94
+
95
+ // src/http.ts
96
+ var DEFAULT_MAX_BYTES = 16 * 1024 * 1024;
97
+ var ERR_TEXT_CAP = 8 * 1024;
98
+ async function readCapped(res, maxBytes) {
99
+ const cl = Number(res.headers.get("content-length"));
100
+ if (Number.isFinite(cl) && cl > maxBytes) throw new HttpError(res.status, res.url, `response too large (content-length ${cl} > ${maxBytes})`);
101
+ const body = res.body;
102
+ if (!body || typeof body.getReader !== "function") {
103
+ const buf = new Uint8Array(await res.arrayBuffer());
104
+ if (buf.byteLength > maxBytes) throw new HttpError(res.status, res.url, `response too large (${buf.byteLength} > ${maxBytes})`);
105
+ return buf;
106
+ }
107
+ const reader = body.getReader();
108
+ const chunks = [];
109
+ let total = 0;
110
+ for (; ; ) {
111
+ const { done, value } = await reader.read();
112
+ if (done) break;
113
+ if (value) {
114
+ total += value.byteLength;
115
+ if (total > maxBytes) {
116
+ try {
117
+ await reader.cancel();
118
+ } catch {
119
+ }
120
+ throw new HttpError(res.status, res.url, `response exceeded ${maxBytes} bytes`);
121
+ }
122
+ chunks.push(value);
123
+ }
124
+ }
125
+ const out = new Uint8Array(total);
126
+ let off = 0;
127
+ for (const c of chunks) {
128
+ out.set(c, off);
129
+ off += c.byteLength;
130
+ }
131
+ return out;
132
+ }
133
+ var Http = class {
134
+ baseUrl;
135
+ _fetch;
136
+ timeoutMs;
137
+ maxBytes;
138
+ headers;
139
+ retries;
140
+ constructor(opts) {
141
+ this.baseUrl = opts.baseUrl.replace(/\/+$/, "");
142
+ const f = opts.fetch ?? globalThis.fetch;
143
+ if (!f) throw new Error("No fetch available \u2014 pass `fetch` in the SDK options.");
144
+ this._fetch = opts.fetch ? opts.fetch : f.bind(globalThis);
145
+ this.timeoutMs = opts.timeoutMs ?? 15e3;
146
+ this.maxBytes = opts.maxBytes ?? DEFAULT_MAX_BYTES;
147
+ this.headers = opts.headers ?? {};
148
+ this.retries = Math.min(10, Math.max(0, Math.floor(opts.retries ?? 0)));
149
+ }
150
+ /** Build an absolute URL from a path + optional query params. */
151
+ url(path, query) {
152
+ const p = path.startsWith("/") ? path : `/${path}`;
153
+ const u = new URL(this.baseUrl + p);
154
+ const basePath = new URL(this.baseUrl + "/").pathname;
155
+ if (basePath !== "/" && !(u.pathname + "/").startsWith(basePath)) {
156
+ throw new Error(`illegal path '${path}' \u2014 escapes base ${basePath}`);
157
+ }
158
+ if (query) {
159
+ for (const [k, v] of Object.entries(query)) {
160
+ if (v !== void 0 && v !== null) u.searchParams.set(k, String(v));
161
+ }
162
+ }
163
+ return u.toString();
164
+ }
165
+ async raw(method, path, opts) {
166
+ const url = this.url(path, opts?.query);
167
+ const ctrl = new AbortController();
168
+ const timer = setTimeout(() => ctrl.abort(), this.timeoutMs);
169
+ try {
170
+ const res = await this._fetch(url, {
171
+ method,
172
+ signal: ctrl.signal,
173
+ headers: {
174
+ ...opts?.body !== void 0 ? { "content-type": "application/json" } : {},
175
+ ...this.headers,
176
+ ...opts?.headers ?? {}
177
+ },
178
+ body: opts?.body !== void 0 ? JSON.stringify(opts.body) : void 0
179
+ });
180
+ return res;
181
+ } finally {
182
+ clearTimeout(timer);
183
+ }
184
+ }
185
+ // A malformed 2xx body must surface as a TYPED HttpError, not a leaked SyntaxError (B10: dApp
186
+ // code branches on `instanceof CairnError`; a raw parse error escaped that contract).
187
+ parseJson(res, text, url) {
188
+ try {
189
+ return JSON.parse(text);
190
+ } catch (e) {
191
+ throw new HttpError(res.status, url, `invalid JSON body: ${text.slice(0, 120)}`);
192
+ }
193
+ }
194
+ async getJson(path, query) {
195
+ const url = this.url(path, query);
196
+ for (let attempt = 0; ; attempt++) {
197
+ try {
198
+ const res = await this.raw("GET", path, { query });
199
+ if (!res.ok) {
200
+ if (!(res.status >= 500 && attempt < this.retries)) throw new HttpError(res.status, url, await safeText(res));
201
+ res.body?.cancel().catch(() => {
202
+ });
203
+ } else {
204
+ return this.parseJson(res, new TextDecoder().decode(await readCapped(res, this.maxBytes)), url);
205
+ }
206
+ } catch (e) {
207
+ if (e instanceof HttpError && e.status < 500) throw e;
208
+ if (attempt >= this.retries) throw e;
209
+ }
210
+ await new Promise((r) => setTimeout(r, Math.floor(Math.random() * Math.min(4e3, 250 * 2 ** attempt))));
211
+ }
212
+ }
213
+ async postJson(path, body, headers) {
214
+ const url = this.url(path);
215
+ const res = await this.raw("POST", path, { body, headers });
216
+ if (!res.ok) throw new HttpError(res.status, url, await safeText(res));
217
+ return this.parseJson(res, new TextDecoder().decode(await readCapped(res, this.maxBytes)), url);
218
+ }
219
+ /** GET raw bytes (for content retrieval). Returns null on 404. */
220
+ async getBytes(path) {
221
+ const res = await this.raw("GET", path);
222
+ if (res.status === 404) return null;
223
+ if (!res.ok) throw new HttpError(res.status, this.url(path), await safeText(res));
224
+ return { bytes: await readCapped(res, this.maxBytes), headers: res.headers };
225
+ }
226
+ };
227
+ async function safeText(res) {
228
+ try {
229
+ return new TextDecoder().decode(await readCapped(res, ERR_TEXT_CAP));
230
+ } catch {
231
+ return "";
232
+ }
233
+ }
234
+
235
+ // src/registry.ts
37
236
  var import_csd_registry2 = require("@inversealtruism/csd-registry");
38
237
  var RegistryClient = class {
39
- src;
40
- constructor(opts) {
41
- this.src = { baseUrl: opts.baseUrl.replace(/\/+$/, ""), fetch: opts.fetch };
238
+ http;
239
+ /** Accepts the SDK's hardened Http directly (the Cairn facade path) or the legacy
240
+ * { baseUrl, fetch } options (kept for direct constructors; they now get the same
241
+ * hardened transport, built internally). */
242
+ constructor(src) {
243
+ this.http = src instanceof Http ? src : new Http({ baseUrl: src.baseUrl, fetch: src.fetch });
42
244
  }
43
245
  /** Discover ranked content/pin gateways (GET /registry/gateways). */
44
246
  gateways() {
45
- return (0, import_csd_registry.discoverGateways)(this.src);
247
+ return this.http.getJson("/registry/gateways");
46
248
  }
47
249
  /** Discover ranked libp2p peers (GET /registry/peers). */
48
250
  peers() {
49
- return (0, import_csd_registry.discoverPeers)(this.src);
251
+ return this.http.getJson("/registry/peers");
50
252
  }
51
253
  /**
52
254
  * Resolve a handle → identity (GET /identity/:handle). Returns null if unresolved.
@@ -58,12 +260,22 @@ var RegistryClient = class {
58
260
  * source (the wallet's namespv union cure). Treat `verified` here as a hint, not a guarantee.
59
261
  */
60
262
  resolveName(handle) {
61
- return (0, import_csd_registry.resolveName)(this.src, handle);
263
+ return this.getOr404Null(`/identity/${encodeURIComponent(handle)}`);
62
264
  }
63
265
  /** Reverse-resolve an address → identity (GET /address/:addr/identity).
64
266
  * ⚠ Same indexer-trust caveat as resolveName — `verified` is the indexer's claim, not a client proof. */
65
267
  reverseName(address) {
66
- return (0, import_csd_registry.reverseName)(this.src, address);
268
+ return this.getOr404Null(`/address/${encodeURIComponent(address)}/identity`);
269
+ }
270
+ // MF-12: 404 → null (the preserved csd-registry contract for both name resolvers); every other
271
+ // failure rethrows the typed HttpError/CairnError the hardened Http raises.
272
+ async getOr404Null(path) {
273
+ try {
274
+ return await this.http.getJson(path);
275
+ } catch (e) {
276
+ if (e instanceof HttpError && e.status === 404) return null;
277
+ return Promise.reject(e);
278
+ }
67
279
  }
68
280
  /** The deterministic, client-side resolvers (recompute from raw ChainRecord[]). */
69
281
  get fromRecords() {
@@ -1,6 +1,7 @@
1
1
  import * as _inversealtruism_csd_registry from '@inversealtruism/csd-registry';
2
+ import { RankedGateway, RankedPeer, ResolvedIdentity } from '@inversealtruism/csd-registry';
2
3
  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-C9C4L4hz.cjs';
4
+ import { H as Http, F as FetchLike } from './http-C9C4L4hz.cjs';
4
5
 
5
6
  interface RegistryClientOptions {
6
7
  /** Indexer base URL exposing /registry/* + /identity/* (e.g. https://cairn-substrate.com/indexer). */
@@ -8,12 +9,15 @@ interface RegistryClientOptions {
8
9
  fetch?: FetchLike;
9
10
  }
10
11
  declare class RegistryClient {
11
- private readonly src;
12
- constructor(opts: RegistryClientOptions);
12
+ private readonly http;
13
+ /** Accepts the SDK's hardened Http directly (the Cairn facade path) or the legacy
14
+ * { baseUrl, fetch } options (kept for direct constructors; they now get the same
15
+ * hardened transport, built internally). */
16
+ constructor(src: Http | RegistryClientOptions);
13
17
  /** Discover ranked content/pin gateways (GET /registry/gateways). */
14
- gateways(): Promise<_inversealtruism_csd_registry.RankedGateway[]>;
18
+ gateways(): Promise<RankedGateway[]>;
15
19
  /** Discover ranked libp2p peers (GET /registry/peers). */
16
- peers(): Promise<_inversealtruism_csd_registry.RankedPeer[]>;
20
+ peers(): Promise<RankedPeer[]>;
17
21
  /**
18
22
  * Resolve a handle → identity (GET /identity/:handle). Returns null if unresolved.
19
23
  *
@@ -23,16 +27,17 @@ declare class RegistryClient {
23
27
  * recomputing from raw records via `fromRecords.name(...)`, or (b) cross-checking a second independent
24
28
  * source (the wallet's namespv union cure). Treat `verified` here as a hint, not a guarantee.
25
29
  */
26
- resolveName(handle: string): Promise<_inversealtruism_csd_registry.ResolvedIdentity | null>;
30
+ resolveName(handle: string): Promise<ResolvedIdentity | null>;
27
31
  /** Reverse-resolve an address → identity (GET /address/:addr/identity).
28
32
  * ⚠ Same indexer-trust caveat as resolveName — `verified` is the indexer's claim, not a client proof. */
29
- reverseName(address: string): Promise<_inversealtruism_csd_registry.ResolvedIdentity | null>;
33
+ reverseName(address: string): Promise<ResolvedIdentity | null>;
34
+ private getOr404Null;
30
35
  /** The deterministic, client-side resolvers (recompute from raw ChainRecord[]). */
31
36
  get fromRecords(): {
32
- peers: (records: _inversealtruism_csd_registry.ChainRecord[], opts: _inversealtruism_csd_registry.ResolveOpts) => _inversealtruism_csd_registry.RankedPeer[];
33
- gateways: (records: _inversealtruism_csd_registry.ChainRecord[], opts: _inversealtruism_csd_registry.ResolveOpts) => _inversealtruism_csd_registry.RankedGateway[];
34
- name: (records: _inversealtruism_csd_registry.ChainRecord[], handle: string, opts: _inversealtruism_csd_registry.ResolveOpts) => _inversealtruism_csd_registry.ResolvedIdentity | null;
35
- reverse: (records: _inversealtruism_csd_registry.ChainRecord[], address: string, opts: _inversealtruism_csd_registry.ResolveOpts) => _inversealtruism_csd_registry.ResolvedIdentity | null;
37
+ peers: (records: _inversealtruism_csd_registry.ChainRecord[], opts: _inversealtruism_csd_registry.ResolveOpts) => RankedPeer[];
38
+ gateways: (records: _inversealtruism_csd_registry.ChainRecord[], opts: _inversealtruism_csd_registry.ResolveOpts) => RankedGateway[];
39
+ name: (records: _inversealtruism_csd_registry.ChainRecord[], handle: string, opts: _inversealtruism_csd_registry.ResolveOpts) => ResolvedIdentity | null;
40
+ reverse: (records: _inversealtruism_csd_registry.ChainRecord[], address: string, opts: _inversealtruism_csd_registry.ResolveOpts) => ResolvedIdentity | null;
36
41
  };
37
42
  }
38
43
 
@@ -1,6 +1,7 @@
1
1
  import * as _inversealtruism_csd_registry from '@inversealtruism/csd-registry';
2
+ import { RankedGateway, RankedPeer, ResolvedIdentity } from '@inversealtruism/csd-registry';
2
3
  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-C9C4L4hz.js';
4
+ import { H as Http, F as FetchLike } from './http-C9C4L4hz.js';
4
5
 
5
6
  interface RegistryClientOptions {
6
7
  /** Indexer base URL exposing /registry/* + /identity/* (e.g. https://cairn-substrate.com/indexer). */
@@ -8,12 +9,15 @@ interface RegistryClientOptions {
8
9
  fetch?: FetchLike;
9
10
  }
10
11
  declare class RegistryClient {
11
- private readonly src;
12
- constructor(opts: RegistryClientOptions);
12
+ private readonly http;
13
+ /** Accepts the SDK's hardened Http directly (the Cairn facade path) or the legacy
14
+ * { baseUrl, fetch } options (kept for direct constructors; they now get the same
15
+ * hardened transport, built internally). */
16
+ constructor(src: Http | RegistryClientOptions);
13
17
  /** Discover ranked content/pin gateways (GET /registry/gateways). */
14
- gateways(): Promise<_inversealtruism_csd_registry.RankedGateway[]>;
18
+ gateways(): Promise<RankedGateway[]>;
15
19
  /** Discover ranked libp2p peers (GET /registry/peers). */
16
- peers(): Promise<_inversealtruism_csd_registry.RankedPeer[]>;
20
+ peers(): Promise<RankedPeer[]>;
17
21
  /**
18
22
  * Resolve a handle → identity (GET /identity/:handle). Returns null if unresolved.
19
23
  *
@@ -23,16 +27,17 @@ declare class RegistryClient {
23
27
  * recomputing from raw records via `fromRecords.name(...)`, or (b) cross-checking a second independent
24
28
  * source (the wallet's namespv union cure). Treat `verified` here as a hint, not a guarantee.
25
29
  */
26
- resolveName(handle: string): Promise<_inversealtruism_csd_registry.ResolvedIdentity | null>;
30
+ resolveName(handle: string): Promise<ResolvedIdentity | null>;
27
31
  /** Reverse-resolve an address → identity (GET /address/:addr/identity).
28
32
  * ⚠ Same indexer-trust caveat as resolveName — `verified` is the indexer's claim, not a client proof. */
29
- reverseName(address: string): Promise<_inversealtruism_csd_registry.ResolvedIdentity | null>;
33
+ reverseName(address: string): Promise<ResolvedIdentity | null>;
34
+ private getOr404Null;
30
35
  /** The deterministic, client-side resolvers (recompute from raw ChainRecord[]). */
31
36
  get fromRecords(): {
32
- peers: (records: _inversealtruism_csd_registry.ChainRecord[], opts: _inversealtruism_csd_registry.ResolveOpts) => _inversealtruism_csd_registry.RankedPeer[];
33
- gateways: (records: _inversealtruism_csd_registry.ChainRecord[], opts: _inversealtruism_csd_registry.ResolveOpts) => _inversealtruism_csd_registry.RankedGateway[];
34
- name: (records: _inversealtruism_csd_registry.ChainRecord[], handle: string, opts: _inversealtruism_csd_registry.ResolveOpts) => _inversealtruism_csd_registry.ResolvedIdentity | null;
35
- reverse: (records: _inversealtruism_csd_registry.ChainRecord[], address: string, opts: _inversealtruism_csd_registry.ResolveOpts) => _inversealtruism_csd_registry.ResolvedIdentity | null;
37
+ peers: (records: _inversealtruism_csd_registry.ChainRecord[], opts: _inversealtruism_csd_registry.ResolveOpts) => RankedPeer[];
38
+ gateways: (records: _inversealtruism_csd_registry.ChainRecord[], opts: _inversealtruism_csd_registry.ResolveOpts) => RankedGateway[];
39
+ name: (records: _inversealtruism_csd_registry.ChainRecord[], handle: string, opts: _inversealtruism_csd_registry.ResolveOpts) => ResolvedIdentity | null;
40
+ reverse: (records: _inversealtruism_csd_registry.ChainRecord[], address: string, opts: _inversealtruism_csd_registry.ResolveOpts) => ResolvedIdentity | null;
36
41
  };
37
42
  }
38
43
 
package/dist/registry.js CHANGED
@@ -10,7 +10,8 @@ import {
10
10
  verifyGateway,
11
11
  verifyIdentitySig,
12
12
  verifyPeer
13
- } from "./chunk-BGJZF7S2.js";
13
+ } from "./chunk-7UOTIAYK.js";
14
+ import "./chunk-ERBT4DFD.js";
14
15
  export {
15
16
  DOMAINS,
16
17
  RegistryClient,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inversealtruism/cairn-sdk",
3
- "version": "0.4.0",
3
+ "version": "0.4.1",
4
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.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -59,16 +59,16 @@
59
59
  "README.md"
60
60
  ],
61
61
  "dependencies": {
62
- "@inversealtruism/cairnx-core": "0.1.40",
62
+ "@inversealtruism/cairnx-core": "0.1.41",
63
63
  "@inversealtruism/csd-client": "0.1.15",
64
- "@inversealtruism/csd-codec": "0.1.15",
64
+ "@inversealtruism/csd-codec": "0.1.16",
65
65
  "@inversealtruism/csd-crypto": "0.1.15",
66
- "@inversealtruism/csd-light": "0.1.18",
67
- "@inversealtruism/csd-registry": "0.1.16",
66
+ "@inversealtruism/csd-light": "0.1.19",
67
+ "@inversealtruism/csd-registry": "0.1.17",
68
68
  "@inversealtruism/csd-tx": "0.1.17"
69
69
  },
70
70
  "devDependencies": {
71
- "@inversealtruism/csd-siwc": "0.1.15",
71
+ "@inversealtruism/csd-siwc": "0.1.16",
72
72
  "esbuild": "^0.27.0",
73
73
  "playwright-core": "^1.49.0",
74
74
  "tsup": "^8.5.1",
@@ -1,69 +0,0 @@
1
- // src/registry.ts
2
- import {
3
- discoverPeers,
4
- discoverGateways,
5
- resolveName,
6
- reverseName,
7
- fromRecords
8
- } from "@inversealtruism/csd-registry";
9
- import {
10
- fromRecords as fromRecords2,
11
- verifyPeer as verifyPeer2,
12
- verifyGateway as verifyGateway2,
13
- verifyIdentitySig as verifyIdentitySig2,
14
- DOMAINS as DOMAINS2,
15
- buildPeerRecord,
16
- buildGatewayRecord,
17
- buildIdentityCommit,
18
- buildIdentityReveal,
19
- epochOf
20
- } from "@inversealtruism/csd-registry";
21
- var RegistryClient = class {
22
- src;
23
- constructor(opts) {
24
- this.src = { baseUrl: opts.baseUrl.replace(/\/+$/, ""), fetch: opts.fetch };
25
- }
26
- /** Discover ranked content/pin gateways (GET /registry/gateways). */
27
- gateways() {
28
- return discoverGateways(this.src);
29
- }
30
- /** Discover ranked libp2p peers (GET /registry/peers). */
31
- peers() {
32
- return discoverPeers(this.src);
33
- }
34
- /**
35
- * Resolve a handle → identity (GET /identity/:handle). Returns null if unresolved.
36
- *
37
- * ⚠ TRUST (CAIRN-SDK-RESOLVE-VERIFIED-FOOTGUN): the returned `verified` flag is the INDEXER's assertion,
38
- * NOT a client-side proof. A hostile/compromised indexer (or a MITM of the read proxy) can return
39
- * `verified:true` for a wrong address. Do NOT use this directly as a payee/login target without either (a)
40
- * recomputing from raw records via `fromRecords.name(...)`, or (b) cross-checking a second independent
41
- * source (the wallet's namespv union cure). Treat `verified` here as a hint, not a guarantee.
42
- */
43
- resolveName(handle) {
44
- return resolveName(this.src, handle);
45
- }
46
- /** Reverse-resolve an address → identity (GET /address/:addr/identity).
47
- * ⚠ Same indexer-trust caveat as resolveName — `verified` is the indexer's claim, not a client proof. */
48
- reverseName(address) {
49
- return reverseName(this.src, address);
50
- }
51
- /** The deterministic, client-side resolvers (recompute from raw ChainRecord[]). */
52
- get fromRecords() {
53
- return fromRecords;
54
- }
55
- };
56
-
57
- export {
58
- RegistryClient,
59
- fromRecords2 as fromRecords,
60
- verifyPeer2 as verifyPeer,
61
- verifyGateway2 as verifyGateway,
62
- verifyIdentitySig2 as verifyIdentitySig,
63
- DOMAINS2 as DOMAINS,
64
- buildPeerRecord,
65
- buildGatewayRecord,
66
- buildIdentityCommit,
67
- buildIdentityReveal,
68
- epochOf
69
- };