@inversealtruism/cairn-sdk 0.1.2 → 0.1.4
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/board.cjs +33 -2
- package/dist/board.d.cts +1 -1
- package/dist/board.d.ts +1 -1
- package/dist/board.js +2 -2
- package/dist/chain.cjs +29 -0
- package/dist/chain.d.cts +24 -3
- package/dist/chain.d.ts +24 -3
- package/dist/chain.js +5 -1
- package/dist/{chunk-G7FABIF4.js → chunk-BGJZF7S2.js} +11 -2
- package/dist/{chunk-6VWLWF7R.js → chunk-C452UODV.js} +31 -2
- package/dist/{chunk-OGP2W7DL.js → chunk-FSQZVNEM.js} +1 -1
- package/dist/chunk-K7SJOW3O.js +213 -0
- package/dist/chunk-MBSPHWZG.js +96 -0
- package/dist/{chunk-BZVJVL4Q.js → chunk-MVX7VQT4.js} +4 -3
- package/dist/{chunk-FNBE2FPE.js → chunk-OG4KA3LS.js} +49 -17
- package/dist/chunk-RELP23CH.js +99 -0
- package/dist/connect.cjs +145 -18
- package/dist/connect.d.cts +140 -2
- package/dist/connect.d.ts +140 -2
- package/dist/connect.js +4 -2
- package/dist/content.cjs +37 -5
- package/dist/content.d.cts +1 -1
- package/dist/content.d.ts +1 -1
- package/dist/content.js +2 -2
- package/dist/controller.cjs +355 -0
- package/dist/controller.d.cts +37 -0
- package/dist/controller.d.ts +37 -0
- package/dist/controller.js +8 -0
- package/dist/{http-D2CzfOsm.d.cts → http-CQsNL8w9.d.cts} +5 -0
- package/dist/{http-D2CzfOsm.d.ts → http-CQsNL8w9.d.ts} +5 -0
- package/dist/index.cjs +386 -48
- package/dist/index.d.cts +39 -6
- package/dist/index.d.ts +39 -6
- package/dist/index.js +75 -20
- package/dist/indexer.cjs +49 -17
- package/dist/indexer.d.cts +15 -4
- package/dist/indexer.d.ts +15 -4
- package/dist/indexer.js +1 -1
- package/dist/react.cjs +371 -0
- package/dist/react.d.cts +30 -0
- package/dist/react.d.ts +30 -0
- package/dist/react.js +23 -0
- 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 +21 -10
- package/dist/chunk-PHMU3CG5.js +0 -63
- package/dist/chunk-SMAPCKVG.js +0 -118
package/dist/index.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { F as FetchLike } from './http-
|
|
2
|
-
export { H as Http, a as HttpOptions } from './http-
|
|
1
|
+
import { F as FetchLike } from './http-CQsNL8w9.cjs';
|
|
2
|
+
export { H as Http, a as HttpOptions } from './http-CQsNL8w9.cjs';
|
|
3
3
|
import { Chain } from './chain.cjs';
|
|
4
4
|
import { BoardClient } from './board.cjs';
|
|
5
5
|
export { BoardItemContent, BoardWindow, ProposeInput, ProposeResult, RankedItem, SupportInput } from './board.cjs';
|
|
@@ -9,7 +9,8 @@ import { ContentClient } from './content.cjs';
|
|
|
9
9
|
export { ContentClientOptions, PreparedContent } from './content.cjs';
|
|
10
10
|
import { RegistryClient } from './registry.cjs';
|
|
11
11
|
import { WalletConnection, DetectOptions } from './connect.cjs';
|
|
12
|
-
export { AttestParams, CairnProvider, ConnectResult, ProposeParams, SealClaimParams, SendParams, SignInResult, TxResult, connect, detectProvider, getWallet, isInstalled } from './connect.cjs';
|
|
12
|
+
export { AttestParams, CairnProvider, ConnectResult, FillParams, ProposeParams, ProviderDetail, ProviderInfo, SealClaimParams, SendParams, SignInResult, SiwcParams, SiwcResult, TxResult, WalletCapabilities, WalletPermission, connect, detectProvider, discoverProviders, getWallet, isInstalled } from './connect.cjs';
|
|
13
|
+
export { CairnController, CairnControllerOptions, CairnState } from './controller.cjs';
|
|
13
14
|
import '@inversealtruism/csd-client';
|
|
14
15
|
import '@inversealtruism/csd-tx';
|
|
15
16
|
import '@inversealtruism/csd-codec';
|
|
@@ -17,8 +18,29 @@ import '@inversealtruism/csd-crypto';
|
|
|
17
18
|
import '@inversealtruism/csd-light';
|
|
18
19
|
import '@inversealtruism/csd-registry';
|
|
19
20
|
|
|
21
|
+
/** Stable machine-readable error codes (branch on these, not the message text). */
|
|
22
|
+
type CairnErrorCode = "NOT_INSTALLED" | "USER_REJECTED" | "WALLET_LOCKED" | "UNSUPPORTED_METHOD" | "HTTP_ERROR" | "CONTENT_VERIFICATION" | "UNKNOWN";
|
|
23
|
+
declare const SDK_VERSION = "0.1.4";
|
|
24
|
+
interface CairnErrorOptions {
|
|
25
|
+
code?: CairnErrorCode;
|
|
26
|
+
docsPath?: string;
|
|
27
|
+
cause?: unknown;
|
|
28
|
+
}
|
|
20
29
|
declare class CairnError extends Error {
|
|
21
|
-
|
|
30
|
+
/** Stable machine code for programmatic branching. */
|
|
31
|
+
readonly code: CairnErrorCode;
|
|
32
|
+
/** The human message without the appended docs/version footer. */
|
|
33
|
+
readonly shortMessage: string;
|
|
34
|
+
/** Relative docs path (under the SDK docs base), if any. */
|
|
35
|
+
readonly docsPath?: string;
|
|
36
|
+
/** The cairn-sdk version that produced this error. */
|
|
37
|
+
readonly version = "0.1.4";
|
|
38
|
+
constructor(shortMessage: string, opts?: CairnErrorOptions);
|
|
39
|
+
/**
|
|
40
|
+
* Walk the `cause` chain. With a predicate, returns the FIRST matching error (or null). Without one,
|
|
41
|
+
* returns the DEEPEST cause (the root). Mirrors viem's `BaseError.walk()`.
|
|
42
|
+
*/
|
|
43
|
+
walk(fn?: (err: unknown) => boolean): unknown;
|
|
22
44
|
}
|
|
23
45
|
/** The Cairn Wallet extension is not installed (no `window.cairn` appeared). */
|
|
24
46
|
declare class NotInstalledError extends CairnError {
|
|
@@ -32,7 +54,7 @@ declare class UserRejectedError extends CairnError {
|
|
|
32
54
|
declare class WalletLockedError extends CairnError {
|
|
33
55
|
constructor(message?: string);
|
|
34
56
|
}
|
|
35
|
-
/** The method is not exposed to dApps by the wallet boundary. */
|
|
57
|
+
/** The method is not exposed to dApps by the wallet boundary (or the wallet is too old for it). */
|
|
36
58
|
declare class UnsupportedMethodError extends CairnError {
|
|
37
59
|
constructor(message?: string);
|
|
38
60
|
}
|
|
@@ -48,6 +70,13 @@ declare class ContentVerificationError extends CairnError {
|
|
|
48
70
|
expected: string;
|
|
49
71
|
constructor(expected: string);
|
|
50
72
|
}
|
|
73
|
+
/**
|
|
74
|
+
* Map a provider error string (from the wallet's `{ ok:false, error }`) onto a typed error. The
|
|
75
|
+
* wallet's strings are stable: "rejected by user", "wallet locked", "unsupported dApp method: …".
|
|
76
|
+
*/
|
|
77
|
+
declare function mapProviderError(error: string): CairnError;
|
|
78
|
+
/** Convenience: the stable code of any error (or "UNKNOWN" for a non-CairnError). */
|
|
79
|
+
declare function errorCode(err: unknown): CairnErrorCode;
|
|
51
80
|
|
|
52
81
|
interface CairnBaseUrls {
|
|
53
82
|
/** Cairn board server (board + /api/rpc proxy + /content origin). Default https://cairn-substrate.com */
|
|
@@ -93,6 +122,10 @@ interface CairnConfig {
|
|
|
93
122
|
hash: string;
|
|
94
123
|
};
|
|
95
124
|
}
|
|
125
|
+
declare const DEFAULT_SPV_CHECKPOINT: {
|
|
126
|
+
height: number;
|
|
127
|
+
hash: string;
|
|
128
|
+
};
|
|
96
129
|
declare class Cairn {
|
|
97
130
|
readonly config: CairnConfig;
|
|
98
131
|
readonly chain: Chain;
|
|
@@ -126,4 +159,4 @@ declare class Cairn {
|
|
|
126
159
|
get connected(): boolean;
|
|
127
160
|
}
|
|
128
161
|
|
|
129
|
-
export { BoardClient, Cairn, type CairnBaseUrls, type CairnConfig, CairnError, Chain, ContentClient, ContentVerificationError, DetectOptions, FetchLike, HttpError, IndexerClient, NotInstalledError, RegistryClient, UnsupportedMethodError, UserRejectedError, WalletConnection, WalletLockedError };
|
|
162
|
+
export { BoardClient, Cairn, type CairnBaseUrls, type CairnConfig, CairnError, type CairnErrorCode, type CairnErrorOptions, Chain, ContentClient, ContentVerificationError, DEFAULT_SPV_CHECKPOINT, DetectOptions, FetchLike, HttpError, IndexerClient, NotInstalledError, RegistryClient, SDK_VERSION, UnsupportedMethodError, UserRejectedError, WalletConnection, WalletLockedError, errorCode, mapProviderError };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { F as FetchLike } from './http-
|
|
2
|
-
export { H as Http, a as HttpOptions } from './http-
|
|
1
|
+
import { F as FetchLike } from './http-CQsNL8w9.js';
|
|
2
|
+
export { H as Http, a as HttpOptions } from './http-CQsNL8w9.js';
|
|
3
3
|
import { Chain } from './chain.js';
|
|
4
4
|
import { BoardClient } from './board.js';
|
|
5
5
|
export { BoardItemContent, BoardWindow, ProposeInput, ProposeResult, RankedItem, SupportInput } from './board.js';
|
|
@@ -9,7 +9,8 @@ import { ContentClient } from './content.js';
|
|
|
9
9
|
export { ContentClientOptions, PreparedContent } from './content.js';
|
|
10
10
|
import { RegistryClient } from './registry.js';
|
|
11
11
|
import { WalletConnection, DetectOptions } from './connect.js';
|
|
12
|
-
export { AttestParams, CairnProvider, ConnectResult, ProposeParams, SealClaimParams, SendParams, SignInResult, TxResult, connect, detectProvider, getWallet, isInstalled } from './connect.js';
|
|
12
|
+
export { AttestParams, CairnProvider, ConnectResult, FillParams, ProposeParams, ProviderDetail, ProviderInfo, SealClaimParams, SendParams, SignInResult, SiwcParams, SiwcResult, TxResult, WalletCapabilities, WalletPermission, connect, detectProvider, discoverProviders, getWallet, isInstalled } from './connect.js';
|
|
13
|
+
export { CairnController, CairnControllerOptions, CairnState } from './controller.js';
|
|
13
14
|
import '@inversealtruism/csd-client';
|
|
14
15
|
import '@inversealtruism/csd-tx';
|
|
15
16
|
import '@inversealtruism/csd-codec';
|
|
@@ -17,8 +18,29 @@ import '@inversealtruism/csd-crypto';
|
|
|
17
18
|
import '@inversealtruism/csd-light';
|
|
18
19
|
import '@inversealtruism/csd-registry';
|
|
19
20
|
|
|
21
|
+
/** Stable machine-readable error codes (branch on these, not the message text). */
|
|
22
|
+
type CairnErrorCode = "NOT_INSTALLED" | "USER_REJECTED" | "WALLET_LOCKED" | "UNSUPPORTED_METHOD" | "HTTP_ERROR" | "CONTENT_VERIFICATION" | "UNKNOWN";
|
|
23
|
+
declare const SDK_VERSION = "0.1.4";
|
|
24
|
+
interface CairnErrorOptions {
|
|
25
|
+
code?: CairnErrorCode;
|
|
26
|
+
docsPath?: string;
|
|
27
|
+
cause?: unknown;
|
|
28
|
+
}
|
|
20
29
|
declare class CairnError extends Error {
|
|
21
|
-
|
|
30
|
+
/** Stable machine code for programmatic branching. */
|
|
31
|
+
readonly code: CairnErrorCode;
|
|
32
|
+
/** The human message without the appended docs/version footer. */
|
|
33
|
+
readonly shortMessage: string;
|
|
34
|
+
/** Relative docs path (under the SDK docs base), if any. */
|
|
35
|
+
readonly docsPath?: string;
|
|
36
|
+
/** The cairn-sdk version that produced this error. */
|
|
37
|
+
readonly version = "0.1.4";
|
|
38
|
+
constructor(shortMessage: string, opts?: CairnErrorOptions);
|
|
39
|
+
/**
|
|
40
|
+
* Walk the `cause` chain. With a predicate, returns the FIRST matching error (or null). Without one,
|
|
41
|
+
* returns the DEEPEST cause (the root). Mirrors viem's `BaseError.walk()`.
|
|
42
|
+
*/
|
|
43
|
+
walk(fn?: (err: unknown) => boolean): unknown;
|
|
22
44
|
}
|
|
23
45
|
/** The Cairn Wallet extension is not installed (no `window.cairn` appeared). */
|
|
24
46
|
declare class NotInstalledError extends CairnError {
|
|
@@ -32,7 +54,7 @@ declare class UserRejectedError extends CairnError {
|
|
|
32
54
|
declare class WalletLockedError extends CairnError {
|
|
33
55
|
constructor(message?: string);
|
|
34
56
|
}
|
|
35
|
-
/** The method is not exposed to dApps by the wallet boundary. */
|
|
57
|
+
/** The method is not exposed to dApps by the wallet boundary (or the wallet is too old for it). */
|
|
36
58
|
declare class UnsupportedMethodError extends CairnError {
|
|
37
59
|
constructor(message?: string);
|
|
38
60
|
}
|
|
@@ -48,6 +70,13 @@ declare class ContentVerificationError extends CairnError {
|
|
|
48
70
|
expected: string;
|
|
49
71
|
constructor(expected: string);
|
|
50
72
|
}
|
|
73
|
+
/**
|
|
74
|
+
* Map a provider error string (from the wallet's `{ ok:false, error }`) onto a typed error. The
|
|
75
|
+
* wallet's strings are stable: "rejected by user", "wallet locked", "unsupported dApp method: …".
|
|
76
|
+
*/
|
|
77
|
+
declare function mapProviderError(error: string): CairnError;
|
|
78
|
+
/** Convenience: the stable code of any error (or "UNKNOWN" for a non-CairnError). */
|
|
79
|
+
declare function errorCode(err: unknown): CairnErrorCode;
|
|
51
80
|
|
|
52
81
|
interface CairnBaseUrls {
|
|
53
82
|
/** Cairn board server (board + /api/rpc proxy + /content origin). Default https://cairn-substrate.com */
|
|
@@ -93,6 +122,10 @@ interface CairnConfig {
|
|
|
93
122
|
hash: string;
|
|
94
123
|
};
|
|
95
124
|
}
|
|
125
|
+
declare const DEFAULT_SPV_CHECKPOINT: {
|
|
126
|
+
height: number;
|
|
127
|
+
hash: string;
|
|
128
|
+
};
|
|
96
129
|
declare class Cairn {
|
|
97
130
|
readonly config: CairnConfig;
|
|
98
131
|
readonly chain: Chain;
|
|
@@ -126,4 +159,4 @@ declare class Cairn {
|
|
|
126
159
|
get connected(): boolean;
|
|
127
160
|
}
|
|
128
161
|
|
|
129
|
-
export { BoardClient, Cairn, type CairnBaseUrls, type CairnConfig, CairnError, Chain, ContentClient, ContentVerificationError, DetectOptions, FetchLike, HttpError, IndexerClient, NotInstalledError, RegistryClient, UnsupportedMethodError, UserRejectedError, WalletConnection, WalletLockedError };
|
|
162
|
+
export { BoardClient, Cairn, type CairnBaseUrls, type CairnConfig, CairnError, type CairnErrorCode, type CairnErrorOptions, Chain, ContentClient, ContentVerificationError, DEFAULT_SPV_CHECKPOINT, DetectOptions, FetchLike, HttpError, IndexerClient, NotInstalledError, RegistryClient, SDK_VERSION, UnsupportedMethodError, UserRejectedError, WalletConnection, WalletLockedError, errorCode, mapProviderError };
|
package/dist/index.js
CHANGED
|
@@ -1,41 +1,86 @@
|
|
|
1
|
+
import {
|
|
2
|
+
RegistryClient
|
|
3
|
+
} from "./chunk-BGJZF7S2.js";
|
|
1
4
|
import {
|
|
2
5
|
BoardClient
|
|
3
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-FSQZVNEM.js";
|
|
4
7
|
import {
|
|
5
8
|
Chain,
|
|
6
9
|
LightClient
|
|
7
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-C452UODV.js";
|
|
11
|
+
import {
|
|
12
|
+
ContentClient
|
|
13
|
+
} from "./chunk-MVX7VQT4.js";
|
|
14
|
+
import {
|
|
15
|
+
IndexerClient
|
|
16
|
+
} from "./chunk-OG4KA3LS.js";
|
|
17
|
+
import {
|
|
18
|
+
CairnController
|
|
19
|
+
} from "./chunk-MBSPHWZG.js";
|
|
8
20
|
import {
|
|
9
21
|
WalletConnection,
|
|
10
22
|
connect,
|
|
11
23
|
detectProvider,
|
|
24
|
+
discoverProviders,
|
|
12
25
|
getWallet,
|
|
13
26
|
isInstalled
|
|
14
|
-
} from "./chunk-
|
|
15
|
-
import {
|
|
16
|
-
ContentClient
|
|
17
|
-
} from "./chunk-BZVJVL4Q.js";
|
|
27
|
+
} from "./chunk-K7SJOW3O.js";
|
|
18
28
|
import {
|
|
19
29
|
CairnError,
|
|
20
30
|
ContentVerificationError,
|
|
21
31
|
HttpError,
|
|
22
32
|
NotInstalledError,
|
|
33
|
+
SDK_VERSION,
|
|
23
34
|
UnsupportedMethodError,
|
|
24
35
|
UserRejectedError,
|
|
25
|
-
WalletLockedError
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
} from "./chunk-FNBE2FPE.js";
|
|
30
|
-
import {
|
|
31
|
-
RegistryClient
|
|
32
|
-
} from "./chunk-G7FABIF4.js";
|
|
36
|
+
WalletLockedError,
|
|
37
|
+
errorCode,
|
|
38
|
+
mapProviderError
|
|
39
|
+
} from "./chunk-RELP23CH.js";
|
|
33
40
|
|
|
34
41
|
// src/http.ts
|
|
42
|
+
var DEFAULT_MAX_BYTES = 16 * 1024 * 1024;
|
|
43
|
+
var ERR_TEXT_CAP = 8 * 1024;
|
|
44
|
+
async function readCapped(res, maxBytes) {
|
|
45
|
+
const cl = Number(res.headers.get("content-length"));
|
|
46
|
+
if (Number.isFinite(cl) && cl > maxBytes) throw new HttpError(res.status, res.url, `response too large (content-length ${cl} > ${maxBytes})`);
|
|
47
|
+
const body = res.body;
|
|
48
|
+
if (!body || typeof body.getReader !== "function") {
|
|
49
|
+
const buf = new Uint8Array(await res.arrayBuffer());
|
|
50
|
+
if (buf.byteLength > maxBytes) throw new HttpError(res.status, res.url, `response too large (${buf.byteLength} > ${maxBytes})`);
|
|
51
|
+
return buf;
|
|
52
|
+
}
|
|
53
|
+
const reader = body.getReader();
|
|
54
|
+
const chunks = [];
|
|
55
|
+
let total = 0;
|
|
56
|
+
for (; ; ) {
|
|
57
|
+
const { done, value } = await reader.read();
|
|
58
|
+
if (done) break;
|
|
59
|
+
if (value) {
|
|
60
|
+
total += value.byteLength;
|
|
61
|
+
if (total > maxBytes) {
|
|
62
|
+
try {
|
|
63
|
+
await reader.cancel();
|
|
64
|
+
} catch {
|
|
65
|
+
}
|
|
66
|
+
throw new HttpError(res.status, res.url, `response exceeded ${maxBytes} bytes`);
|
|
67
|
+
}
|
|
68
|
+
chunks.push(value);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
const out = new Uint8Array(total);
|
|
72
|
+
let off = 0;
|
|
73
|
+
for (const c of chunks) {
|
|
74
|
+
out.set(c, off);
|
|
75
|
+
off += c.byteLength;
|
|
76
|
+
}
|
|
77
|
+
return out;
|
|
78
|
+
}
|
|
35
79
|
var Http = class {
|
|
36
80
|
baseUrl;
|
|
37
81
|
_fetch;
|
|
38
82
|
timeoutMs;
|
|
83
|
+
maxBytes;
|
|
39
84
|
headers;
|
|
40
85
|
constructor(opts) {
|
|
41
86
|
this.baseUrl = opts.baseUrl.replace(/\/+$/, "");
|
|
@@ -43,12 +88,17 @@ var Http = class {
|
|
|
43
88
|
if (!f) throw new Error("No fetch available \u2014 pass `fetch` in the SDK options.");
|
|
44
89
|
this._fetch = opts.fetch ? opts.fetch : f.bind(globalThis);
|
|
45
90
|
this.timeoutMs = opts.timeoutMs ?? 15e3;
|
|
91
|
+
this.maxBytes = opts.maxBytes ?? DEFAULT_MAX_BYTES;
|
|
46
92
|
this.headers = opts.headers ?? {};
|
|
47
93
|
}
|
|
48
94
|
/** Build an absolute URL from a path + optional query params. */
|
|
49
95
|
url(path, query) {
|
|
50
96
|
const p = path.startsWith("/") ? path : `/${path}`;
|
|
51
97
|
const u = new URL(this.baseUrl + p);
|
|
98
|
+
const basePath = new URL(this.baseUrl + "/").pathname;
|
|
99
|
+
if (basePath !== "/" && !(u.pathname + "/").startsWith(basePath)) {
|
|
100
|
+
throw new Error(`illegal path '${path}' \u2014 escapes base ${basePath}`);
|
|
101
|
+
}
|
|
52
102
|
if (query) {
|
|
53
103
|
for (const [k, v] of Object.entries(query)) {
|
|
54
104
|
if (v !== void 0 && v !== null) u.searchParams.set(k, String(v));
|
|
@@ -79,25 +129,24 @@ var Http = class {
|
|
|
79
129
|
async getJson(path, query) {
|
|
80
130
|
const res = await this.raw("GET", path, { query });
|
|
81
131
|
if (!res.ok) throw new HttpError(res.status, this.url(path, query), await safeText(res));
|
|
82
|
-
return await res.
|
|
132
|
+
return JSON.parse(new TextDecoder().decode(await readCapped(res, this.maxBytes)));
|
|
83
133
|
}
|
|
84
134
|
async postJson(path, body, headers) {
|
|
85
135
|
const res = await this.raw("POST", path, { body, headers });
|
|
86
136
|
if (!res.ok) throw new HttpError(res.status, this.url(path), await safeText(res));
|
|
87
|
-
return await res.
|
|
137
|
+
return JSON.parse(new TextDecoder().decode(await readCapped(res, this.maxBytes)));
|
|
88
138
|
}
|
|
89
139
|
/** GET raw bytes (for content retrieval). Returns null on 404. */
|
|
90
140
|
async getBytes(path) {
|
|
91
141
|
const res = await this.raw("GET", path);
|
|
92
142
|
if (res.status === 404) return null;
|
|
93
143
|
if (!res.ok) throw new HttpError(res.status, this.url(path), await safeText(res));
|
|
94
|
-
|
|
95
|
-
return { bytes: buf, headers: res.headers };
|
|
144
|
+
return { bytes: await readCapped(res, this.maxBytes), headers: res.headers };
|
|
96
145
|
}
|
|
97
146
|
};
|
|
98
147
|
async function safeText(res) {
|
|
99
148
|
try {
|
|
100
|
-
return await res
|
|
149
|
+
return new TextDecoder().decode(await readCapped(res, ERR_TEXT_CAP));
|
|
101
150
|
} catch {
|
|
102
151
|
return "";
|
|
103
152
|
}
|
|
@@ -186,21 +235,27 @@ var Cairn = class {
|
|
|
186
235
|
export {
|
|
187
236
|
BoardClient,
|
|
188
237
|
Cairn,
|
|
238
|
+
CairnController,
|
|
189
239
|
CairnError,
|
|
190
240
|
Chain,
|
|
191
241
|
ContentClient,
|
|
192
242
|
ContentVerificationError,
|
|
243
|
+
DEFAULT_SPV_CHECKPOINT,
|
|
193
244
|
Http,
|
|
194
245
|
HttpError,
|
|
195
246
|
IndexerClient,
|
|
196
247
|
NotInstalledError,
|
|
197
248
|
RegistryClient,
|
|
249
|
+
SDK_VERSION,
|
|
198
250
|
UnsupportedMethodError,
|
|
199
251
|
UserRejectedError,
|
|
200
252
|
WalletConnection,
|
|
201
253
|
WalletLockedError,
|
|
202
254
|
connect,
|
|
203
255
|
detectProvider,
|
|
256
|
+
discoverProviders,
|
|
257
|
+
errorCode,
|
|
204
258
|
getWallet,
|
|
205
|
-
isInstalled
|
|
259
|
+
isInstalled,
|
|
260
|
+
mapProviderError
|
|
206
261
|
};
|
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,6 +232,13 @@ 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;
|
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-CQsNL8w9.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-CQsNL8w9.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;
|