@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.
- package/README.md +16 -16
- package/dist/board.cjs +1 -1
- package/dist/board.d.cts +1 -1
- package/dist/board.d.ts +1 -1
- package/dist/board.js +2 -2
- package/dist/chain.cjs +1 -1
- package/dist/chain.d.cts +1 -1
- package/dist/chain.d.ts +1 -1
- package/dist/chain.js +1 -1
- package/dist/{chunk-G7FABIF4.js → chunk-BGJZF7S2.js} +11 -2
- package/dist/{chunk-5KAUQH72.js → chunk-C452UODV.js} +1 -1
- package/dist/{chunk-FNBE2FPE.js → chunk-J5J4JEJ6.js} +50 -18
- package/dist/{chunk-UMGWVOOT.js → chunk-JGXD3WIC.js} +7 -2
- package/dist/{chunk-ZAE2BIH5.js → chunk-PYREGAB3.js} +21 -3
- package/dist/{chunk-645CYBVN.js → chunk-RYZJDZLJ.js} +1 -1
- package/dist/{chunk-OSXS26RA.js → chunk-SIUUY6FZ.js} +4 -3
- package/dist/{chunk-G4OZKZSJ.js → chunk-UIEOBVFI.js} +8 -2
- package/dist/connect.cjs +14 -3
- package/dist/connect.d.cts +7 -0
- package/dist/connect.d.ts +7 -0
- package/dist/connect.js +2 -2
- package/dist/content.cjs +4 -3
- package/dist/content.d.cts +1 -1
- package/dist/content.d.ts +1 -1
- package/dist/content.js +2 -2
- package/dist/controller.cjs +34 -5
- package/dist/controller.d.cts +1 -0
- package/dist/controller.d.ts +1 -0
- package/dist/controller.js +3 -3
- package/dist/{http-D2CzfOsm.d.cts → http-C9C4L4hz.d.cts} +11 -0
- package/dist/{http-D2CzfOsm.d.ts → http-C9C4L4hz.d.ts} +11 -0
- package/dist/index.cjs +272 -37
- package/dist/index.d.cts +99 -11
- package/dist/index.d.ts +99 -11
- package/dist/index.js +179 -17
- package/dist/indexer.cjs +50 -18
- package/dist/indexer.d.cts +15 -4
- package/dist/indexer.d.ts +15 -4
- package/dist/indexer.js +1 -1
- package/dist/react.cjs +34 -5
- package/dist/react.js +3 -3
- package/dist/registry.cjs +11 -2
- package/dist/registry.d.cts +12 -3
- package/dist/registry.d.ts +12 -3
- package/dist/registry.js +1 -1
- 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).
|
|
106
|
-
*
|
|
107
|
-
*
|
|
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
|
-
|
|
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();
|
package/dist/indexer.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { H as Http, F as FetchLike } from './http-
|
|
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).
|
|
75
|
-
*
|
|
76
|
-
*
|
|
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-
|
|
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).
|
|
75
|
-
*
|
|
76
|
-
*
|
|
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
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.
|
|
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
|
-
|
|
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)
|
|
299
|
-
|
|
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-
|
|
4
|
-
import "./chunk-
|
|
5
|
-
import "./chunk-
|
|
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
|
-
/**
|
|
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
|
}
|
package/dist/registry.d.cts
CHANGED
|
@@ -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-
|
|
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
|
-
/**
|
|
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.d.ts
CHANGED
|
@@ -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-
|
|
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
|
-
/**
|
|
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
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@inversealtruism/cairn-sdk",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "The Compute Substrate dApp kit
|
|
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/
|
|
63
|
-
"@inversealtruism/csd-
|
|
64
|
-
"@inversealtruism/csd-
|
|
65
|
-
"@inversealtruism/csd-
|
|
66
|
-
"@inversealtruism/csd-
|
|
67
|
-
"@inversealtruism/csd-
|
|
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
|
-
|
|
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
|
+
}
|