@inversealtruism/cairn-sdk 0.4.6 → 0.6.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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@inversealtruism/cairn-sdk",
3
- "version": "0.4.6",
4
- "description": "The Compute Substrate dApp kit — one cohesive toolkit to connect the Cairn Wallet, read the chain/board, publish self-certifying content, and resolve registries. Composes the published csd-* primitives.",
3
+ "version": "0.6.0",
4
+ "description": "The Compute Substrate dApp kit: one cohesive toolkit to connect the Cairn Wallet, read the chain, the indexer and .csd names, verify 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",
@@ -17,11 +17,6 @@
17
17
  "import": "./dist/connect.js",
18
18
  "require": "./dist/connect.cjs"
19
19
  },
20
- "./board": {
21
- "types": "./dist/board.d.ts",
22
- "import": "./dist/board.js",
23
- "require": "./dist/board.cjs"
24
- },
25
20
  "./indexer": {
26
21
  "types": "./dist/indexer.d.ts",
27
22
  "import": "./dist/indexer.js",
@@ -59,16 +54,16 @@
59
54
  "README.md"
60
55
  ],
61
56
  "dependencies": {
62
- "@inversealtruism/cairnx-core": "0.1.42",
63
- "@inversealtruism/csd-client": "0.1.16",
64
- "@inversealtruism/csd-codec": "0.1.16",
65
- "@inversealtruism/csd-crypto": "0.1.16",
66
- "@inversealtruism/csd-light": "0.1.21",
67
- "@inversealtruism/csd-registry": "0.1.18",
68
- "@inversealtruism/csd-tx": "0.1.19"
57
+ "@inversealtruism/cairnx-core": "0.2.0",
58
+ "@inversealtruism/csd-client": "0.1.18",
59
+ "@inversealtruism/csd-codec": "0.1.18",
60
+ "@inversealtruism/csd-crypto": "0.1.18",
61
+ "@inversealtruism/csd-light": "0.1.23",
62
+ "@inversealtruism/csd-registry": "0.1.20",
63
+ "@inversealtruism/csd-tx": "0.1.21"
69
64
  },
70
65
  "devDependencies": {
71
- "@inversealtruism/csd-siwc": "0.1.17",
66
+ "@inversealtruism/csd-siwc": "0.1.19",
72
67
  "esbuild": "^0.27.0",
73
68
  "playwright-core": "^1.49.0",
74
69
  "tsup": "^8.5.1",
@@ -95,14 +90,13 @@
95
90
  "blockchain"
96
91
  ],
97
92
  "scripts": {
98
- "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",
93
+ "build": "tsup src/index.ts src/connect.ts src/indexer.ts src/content.ts src/registry.ts src/chain.ts src/controller.ts src/react.ts --format esm,cjs --dts --clean",
99
94
  "test": "node test/run.mjs",
100
95
  "test:live": "node examples/read-only.mjs",
101
96
  "build:example": "node scripts/build-example.mjs",
102
97
  "clean": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\"",
103
98
  "test:e2e:read": "node test/e2e/e2e-read.mjs",
104
99
  "test:e2e:wallet": "xvfb-run -a node test/e2e/e2e-wallet-connect.mjs",
105
- "test:e2e:write": "node test/e2e/e2e-write.mjs",
106
100
  "test:e2e": "node test/e2e/e2e-read.mjs && xvfb-run -a node test/e2e/e2e-wallet-connect.mjs",
107
101
  "test:all": "pnpm test && pnpm test:e2e"
108
102
  }
package/dist/board.cjs DELETED
@@ -1,231 +0,0 @@
1
- "use strict";
2
- var __defProp = Object.defineProperty;
3
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
- var __getOwnPropNames = Object.getOwnPropertyNames;
5
- var __hasOwnProp = Object.prototype.hasOwnProperty;
6
- var __export = (target, all) => {
7
- for (var name in all)
8
- __defProp(target, name, { get: all[name], enumerable: true });
9
- };
10
- var __copyProps = (to, from, except, desc) => {
11
- if (from && typeof from === "object" || typeof from === "function") {
12
- for (let key of __getOwnPropNames(from))
13
- if (!__hasOwnProp.call(to, key) && key !== except)
14
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
- }
16
- return to;
17
- };
18
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
-
20
- // src/board.ts
21
- var board_exports = {};
22
- __export(board_exports, {
23
- BoardClient: () => BoardClient
24
- });
25
- module.exports = __toCommonJS(board_exports);
26
- var import_csd_codec = require("@inversealtruism/csd-codec");
27
- var import_csd_codec2 = require("@inversealtruism/csd-codec");
28
-
29
- // src/errors.ts
30
- var SDK_VERSION = "0.4.6";
31
- var DOCS_BASE = "https://cairn-substrate.com/docs/sdk";
32
- var CairnError = class extends Error {
33
- /** Stable machine code for programmatic branching. */
34
- code;
35
- /** The human message without the appended docs/version footer. */
36
- shortMessage;
37
- /** Relative docs path (under the SDK docs base), if any. */
38
- docsPath;
39
- /** The cairn-sdk version that produced this error. */
40
- version = SDK_VERSION;
41
- /**
42
- * Whether a dApp may safely AUTO-retry this failure. `false` for fund-safety refusals and user-intent
43
- * declines (the default); `true` only for provably transient infra faults where nothing was signed.
44
- * A `SubmitInFlightError` is `false` too: reconcile its `txid`, never blind-retry.
45
- */
46
- retryable;
47
- constructor(shortMessage, opts = {}) {
48
- const docs = opts.docsPath;
49
- super(
50
- [shortMessage, docs ? `Docs: ${DOCS_BASE}${docs}` : "", `Version: cairn-sdk@${SDK_VERSION}`].filter(Boolean).join("\n"),
51
- { cause: opts.cause }
52
- );
53
- this.name = new.target.name;
54
- this.code = opts.code ?? "UNKNOWN";
55
- this.shortMessage = shortMessage;
56
- this.docsPath = docs;
57
- this.retryable = opts.retryable ?? false;
58
- }
59
- /**
60
- * Walk the `cause` chain. With a predicate, returns the FIRST matching error (or null). Without one,
61
- * returns the DEEPEST cause (the root). Mirrors viem's `BaseError.walk()`.
62
- */
63
- walk(fn) {
64
- let current = this;
65
- let last = this;
66
- while (current != null) {
67
- if (fn && fn(current)) return current;
68
- last = current;
69
- current = current?.cause;
70
- }
71
- return fn ? null : last;
72
- }
73
- };
74
-
75
- // src/board.ts
76
- var BoardClient = class {
77
- http;
78
- wallet;
79
- constructor(http, wallet) {
80
- this.http = http;
81
- this.wallet = wallet;
82
- }
83
- requireWallet() {
84
- if (!this.wallet) {
85
- throw new CairnError("This action needs a connected wallet. Construct Cairn with a wallet, or call cairn.connect() first.");
86
- }
87
- return this.wallet;
88
- }
89
- // ---- reads (public) ----------------------------------------------------
90
- /** Ranked board for a domain + time window. */
91
- board(opts = {}) {
92
- return this.http.getJson("/api/board", { domain: opts.domain ?? "all", window: opts.window ?? "all" });
93
- }
94
- /** Alias for `board()` — the top items. */
95
- top(opts = {}) {
96
- return this.board(opts);
97
- }
98
- /** A single item with its supports + pledges + integrity flag. */
99
- item(id) {
100
- return this.http.getJson(`/api/item/${encodeURIComponent(id)}`);
101
- }
102
- /** Known + discovered domains. */
103
- domains() {
104
- return this.http.getJson("/api/domains");
105
- }
106
- /** All quests (work-graph items). */
107
- quests() {
108
- return this.http.getJson("/api/quests");
109
- }
110
- /** A single quest with claims/submissions/payouts. */
111
- quest(id) {
112
- return this.http.getJson(`/api/quest/${encodeURIComponent(id)}`);
113
- }
114
- /** An address's on-chain-derived profile + identity + reputation. */
115
- profile(addr, opts = {}) {
116
- return this.http.getJson(`/api/profile/${encodeURIComponent(addr)}`, opts.live ? { live: 1 } : void 0);
117
- }
118
- /** Reputation leaderboard (top 50). */
119
- leaderboard() {
120
- return this.http.getJson("/api/leaderboard");
121
- }
122
- /** Network dashboard snapshot. */
123
- network() {
124
- return this.http.getJson("/api/network");
125
- }
126
- /** Network time-series for charts. */
127
- networkSeries(range = "24h") {
128
- return this.http.getJson("/api/network/series", { range });
129
- }
130
- /** Recent blocks with miner/reward. */
131
- networkBlocks(limit = 15) {
132
- return this.http.getJson("/api/network/blocks", { limit });
133
- }
134
- /** Per-miner hashrate estimate. */
135
- miner(addr) {
136
- return this.http.getJson(`/api/miner/${encodeURIComponent(addr)}`);
137
- }
138
- /** Recent activity feed. */
139
- activity() {
140
- return this.http.getJson("/api/activity");
141
- }
142
- /** The gamified cairn wall. */
143
- wall() {
144
- return this.http.getJson("/api/wall");
145
- }
146
- /** Server health + chain reachability. */
147
- health() {
148
- return this.http.getJson("/api/health");
149
- }
150
- // ---- writes (wallet-signed) -------------------------------------------
151
- /**
152
- * Publish a board item: compute the canonical payload hash, have the WALLET
153
- * build/sign/submit the Propose (clear-signed approval), then register the
154
- * off-chain content. The payload hash is byte-identical to what the cairn
155
- * server verifies (csd-codec.payloadHash over { v:1, domain, title, body, links }).
156
- */
157
- async propose(input) {
158
- const wallet = this.requireWallet();
159
- const content = {
160
- v: 1,
161
- domain: input.domain,
162
- title: input.title,
163
- body: input.body ?? "",
164
- links: input.links ?? []
165
- };
166
- const ph = (0, import_csd_codec.payloadHash)(content);
167
- const uri = `cairn:v1:${ph.slice(2, 14)}`;
168
- const lifetime = input.lifetimeEpochs ?? 720;
169
- const height = await this.tipHeight();
170
- const expiresEpoch = Math.floor(height / import_csd_codec2.EPOCH_LEN) + lifetime;
171
- const fee = input.fee ?? import_csd_codec2.MIN_FEE_PROPOSE;
172
- const r = await wallet.propose({ domain: content.domain, payloadHash: ph, uri, expiresEpoch, fee });
173
- const txid = String(r.txid ?? "");
174
- if (!txid) throw new CairnError("Propose submitted but no txid was returned by the wallet.");
175
- const reg = await this.tryRegister(content, txid);
176
- return { txid, payloadHash: ph, content, registered: reg.ok, registerError: reg.error };
177
- }
178
- /**
179
- * Register off-chain content for an already-submitted proposal txid. Idempotent.
180
- * Returns { ok } — the cairn server verifies sha256(canonical) == on-chain payload_hash.
181
- */
182
- async registerContent(content, txid) {
183
- try {
184
- const res = await this.http.postJson("/api/content", {
185
- domain: content.domain,
186
- title: content.title,
187
- body: content.body,
188
- links: content.links,
189
- txid
190
- });
191
- return { ok: !!res.ok, error: res.ok ? void 0 : res.error };
192
- } catch (e) {
193
- return { ok: false, error: e instanceof Error ? e.message : String(e) };
194
- }
195
- }
196
- /** Support/attest a proposal (wallet-signed; always clear-signed). */
197
- async support(proposalId, opts = {}) {
198
- const wallet = this.requireWallet();
199
- return wallet.attest({
200
- proposalId,
201
- score: opts.score ?? 80,
202
- confidence: opts.confidence ?? 70,
203
- fee: opts.fee ?? import_csd_codec2.MIN_FEE_ATTEST
204
- });
205
- }
206
- // ---- internal ----------------------------------------------------------
207
- async tipHeight() {
208
- const tip = await this.http.getJson("/api/rpc/tip");
209
- const h = Number(tip?.height);
210
- if (!Number.isFinite(h)) throw new CairnError("Could not read chain tip height for expiry computation.");
211
- return h;
212
- }
213
- async tryRegister(content, txid) {
214
- const delays = [0, 1500, 3e3, 5e3];
215
- let last = { ok: false, error: "not attempted" };
216
- for (const d of delays) {
217
- if (d) await sleep(d);
218
- last = await this.registerContent(content, txid);
219
- if (last.ok) return last;
220
- if (!/not found yet|not found/i.test(last.error ?? "")) return last;
221
- }
222
- return last;
223
- }
224
- };
225
- function sleep(ms) {
226
- return new Promise((r) => setTimeout(r, ms));
227
- }
228
- // Annotate the CommonJS export names for ESM import in node:
229
- 0 && (module.exports = {
230
- BoardClient
231
- });
package/dist/board.d.cts DELETED
@@ -1,151 +0,0 @@
1
- import { H as Http } from './http-C9C4L4hz.cjs';
2
- import { WalletConnection, TxResult } from './connect.cjs';
3
-
4
- type BoardWindow = "24h" | "7d" | "30d" | "all";
5
- /** A ranked board item (loose — the server returns a rich object). */
6
- interface RankedItem {
7
- id: string;
8
- title: string;
9
- body: string;
10
- domain: string;
11
- proposer: string;
12
- score: number;
13
- totalWeight: number;
14
- [k: string]: unknown;
15
- }
16
- interface BoardItemContent {
17
- v: 1;
18
- domain: string;
19
- title: string;
20
- body: string;
21
- links: string[];
22
- }
23
- interface ProposeInput {
24
- domain: string;
25
- title: string;
26
- body?: string;
27
- links?: string[];
28
- /** Fee in base units (default MIN_FEE_PROPOSE = 0.25 CSD). */
29
- fee?: number;
30
- /** Lifetime in epochs before expiry (default 720 ≈ 30 days, matching cairn). */
31
- lifetimeEpochs?: number;
32
- }
33
- interface ProposeResult {
34
- txid: string;
35
- payloadHash: string;
36
- content: BoardItemContent;
37
- /** Whether the off-chain content was registered with the cairn origin yet. */
38
- registered: boolean;
39
- /** If registration is still pending, call `registerContent(content, txid)` again later. */
40
- registerError?: string;
41
- }
42
- interface SupportInput {
43
- /** 0–100 quality score (default 80). */
44
- score?: number;
45
- /** 0–100 confidence (default 70). */
46
- confidence?: number;
47
- /** Fee in base units (default MIN_FEE_ATTEST = 0.05 CSD). */
48
- fee?: number;
49
- }
50
- declare class BoardClient {
51
- private readonly http;
52
- private readonly wallet?;
53
- constructor(http: Http, wallet?: WalletConnection);
54
- private requireWallet;
55
- /** Ranked board for a domain + time window. */
56
- board(opts?: {
57
- domain?: string;
58
- window?: BoardWindow;
59
- }): Promise<{
60
- window: string;
61
- domain: string;
62
- count: number;
63
- items: RankedItem[];
64
- }>;
65
- /** Alias for `board()` — the top items. */
66
- top(opts?: {
67
- domain?: string;
68
- window?: BoardWindow;
69
- }): Promise<{
70
- window: string;
71
- domain: string;
72
- count: number;
73
- items: RankedItem[];
74
- }>;
75
- /** A single item with its supports + pledges + integrity flag. */
76
- item(id: string): Promise<{
77
- ok: boolean;
78
- item: RankedItem;
79
- supports: unknown[];
80
- pledges: unknown[];
81
- integrityOk: boolean;
82
- }>;
83
- /** Known + discovered domains. */
84
- domains(): Promise<{
85
- prefix: string;
86
- total: number;
87
- domains: unknown[];
88
- discovered: unknown[];
89
- }>;
90
- /** All quests (work-graph items). */
91
- quests(): Promise<{
92
- count: number;
93
- quests: unknown[];
94
- }>;
95
- /** A single quest with claims/submissions/payouts. */
96
- quest(id: string): Promise<{
97
- ok: boolean;
98
- quest: unknown;
99
- }>;
100
- /** An address's on-chain-derived profile + identity + reputation. */
101
- profile(addr: string, opts?: {
102
- live?: boolean;
103
- }): Promise<{
104
- ok: boolean;
105
- profile: unknown;
106
- identity: unknown;
107
- reputation: unknown;
108
- }>;
109
- /** Reputation leaderboard (top 50). */
110
- leaderboard(): Promise<{
111
- count: number;
112
- leaderboard: unknown[];
113
- }>;
114
- /** Network dashboard snapshot. */
115
- network(): Promise<unknown>;
116
- /** Network time-series for charts. */
117
- networkSeries(range?: "24h" | "1w" | "1m" | "all"): Promise<unknown>;
118
- /** Recent blocks with miner/reward. */
119
- networkBlocks(limit?: number): Promise<unknown>;
120
- /** Per-miner hashrate estimate. */
121
- miner(addr: string): Promise<unknown>;
122
- /** Recent activity feed. */
123
- activity(): Promise<{
124
- activity: unknown[];
125
- }>;
126
- /** The gamified cairn wall. */
127
- wall(): Promise<unknown>;
128
- /** Server health + chain reachability. */
129
- health(): Promise<unknown>;
130
- /**
131
- * Publish a board item: compute the canonical payload hash, have the WALLET
132
- * build/sign/submit the Propose (clear-signed approval), then register the
133
- * off-chain content. The payload hash is byte-identical to what the cairn
134
- * server verifies (csd-codec.payloadHash over { v:1, domain, title, body, links }).
135
- */
136
- propose(input: ProposeInput): Promise<ProposeResult>;
137
- /**
138
- * Register off-chain content for an already-submitted proposal txid. Idempotent.
139
- * Returns { ok } — the cairn server verifies sha256(canonical) == on-chain payload_hash.
140
- */
141
- registerContent(content: BoardItemContent, txid: string): Promise<{
142
- ok: boolean;
143
- error?: string;
144
- }>;
145
- /** Support/attest a proposal (wallet-signed; always clear-signed). */
146
- support(proposalId: string, opts?: SupportInput): Promise<TxResult>;
147
- private tipHeight;
148
- private tryRegister;
149
- }
150
-
151
- export { BoardClient, type BoardItemContent, type BoardWindow, type ProposeInput, type ProposeResult, type RankedItem, type SupportInput };
package/dist/board.d.ts DELETED
@@ -1,151 +0,0 @@
1
- import { H as Http } from './http-C9C4L4hz.js';
2
- import { WalletConnection, TxResult } from './connect.js';
3
-
4
- type BoardWindow = "24h" | "7d" | "30d" | "all";
5
- /** A ranked board item (loose — the server returns a rich object). */
6
- interface RankedItem {
7
- id: string;
8
- title: string;
9
- body: string;
10
- domain: string;
11
- proposer: string;
12
- score: number;
13
- totalWeight: number;
14
- [k: string]: unknown;
15
- }
16
- interface BoardItemContent {
17
- v: 1;
18
- domain: string;
19
- title: string;
20
- body: string;
21
- links: string[];
22
- }
23
- interface ProposeInput {
24
- domain: string;
25
- title: string;
26
- body?: string;
27
- links?: string[];
28
- /** Fee in base units (default MIN_FEE_PROPOSE = 0.25 CSD). */
29
- fee?: number;
30
- /** Lifetime in epochs before expiry (default 720 ≈ 30 days, matching cairn). */
31
- lifetimeEpochs?: number;
32
- }
33
- interface ProposeResult {
34
- txid: string;
35
- payloadHash: string;
36
- content: BoardItemContent;
37
- /** Whether the off-chain content was registered with the cairn origin yet. */
38
- registered: boolean;
39
- /** If registration is still pending, call `registerContent(content, txid)` again later. */
40
- registerError?: string;
41
- }
42
- interface SupportInput {
43
- /** 0–100 quality score (default 80). */
44
- score?: number;
45
- /** 0–100 confidence (default 70). */
46
- confidence?: number;
47
- /** Fee in base units (default MIN_FEE_ATTEST = 0.05 CSD). */
48
- fee?: number;
49
- }
50
- declare class BoardClient {
51
- private readonly http;
52
- private readonly wallet?;
53
- constructor(http: Http, wallet?: WalletConnection);
54
- private requireWallet;
55
- /** Ranked board for a domain + time window. */
56
- board(opts?: {
57
- domain?: string;
58
- window?: BoardWindow;
59
- }): Promise<{
60
- window: string;
61
- domain: string;
62
- count: number;
63
- items: RankedItem[];
64
- }>;
65
- /** Alias for `board()` — the top items. */
66
- top(opts?: {
67
- domain?: string;
68
- window?: BoardWindow;
69
- }): Promise<{
70
- window: string;
71
- domain: string;
72
- count: number;
73
- items: RankedItem[];
74
- }>;
75
- /** A single item with its supports + pledges + integrity flag. */
76
- item(id: string): Promise<{
77
- ok: boolean;
78
- item: RankedItem;
79
- supports: unknown[];
80
- pledges: unknown[];
81
- integrityOk: boolean;
82
- }>;
83
- /** Known + discovered domains. */
84
- domains(): Promise<{
85
- prefix: string;
86
- total: number;
87
- domains: unknown[];
88
- discovered: unknown[];
89
- }>;
90
- /** All quests (work-graph items). */
91
- quests(): Promise<{
92
- count: number;
93
- quests: unknown[];
94
- }>;
95
- /** A single quest with claims/submissions/payouts. */
96
- quest(id: string): Promise<{
97
- ok: boolean;
98
- quest: unknown;
99
- }>;
100
- /** An address's on-chain-derived profile + identity + reputation. */
101
- profile(addr: string, opts?: {
102
- live?: boolean;
103
- }): Promise<{
104
- ok: boolean;
105
- profile: unknown;
106
- identity: unknown;
107
- reputation: unknown;
108
- }>;
109
- /** Reputation leaderboard (top 50). */
110
- leaderboard(): Promise<{
111
- count: number;
112
- leaderboard: unknown[];
113
- }>;
114
- /** Network dashboard snapshot. */
115
- network(): Promise<unknown>;
116
- /** Network time-series for charts. */
117
- networkSeries(range?: "24h" | "1w" | "1m" | "all"): Promise<unknown>;
118
- /** Recent blocks with miner/reward. */
119
- networkBlocks(limit?: number): Promise<unknown>;
120
- /** Per-miner hashrate estimate. */
121
- miner(addr: string): Promise<unknown>;
122
- /** Recent activity feed. */
123
- activity(): Promise<{
124
- activity: unknown[];
125
- }>;
126
- /** The gamified cairn wall. */
127
- wall(): Promise<unknown>;
128
- /** Server health + chain reachability. */
129
- health(): Promise<unknown>;
130
- /**
131
- * Publish a board item: compute the canonical payload hash, have the WALLET
132
- * build/sign/submit the Propose (clear-signed approval), then register the
133
- * off-chain content. The payload hash is byte-identical to what the cairn
134
- * server verifies (csd-codec.payloadHash over { v:1, domain, title, body, links }).
135
- */
136
- propose(input: ProposeInput): Promise<ProposeResult>;
137
- /**
138
- * Register off-chain content for an already-submitted proposal txid. Idempotent.
139
- * Returns { ok } — the cairn server verifies sha256(canonical) == on-chain payload_hash.
140
- */
141
- registerContent(content: BoardItemContent, txid: string): Promise<{
142
- ok: boolean;
143
- error?: string;
144
- }>;
145
- /** Support/attest a proposal (wallet-signed; always clear-signed). */
146
- support(proposalId: string, opts?: SupportInput): Promise<TxResult>;
147
- private tipHeight;
148
- private tryRegister;
149
- }
150
-
151
- export { BoardClient, type BoardItemContent, type BoardWindow, type ProposeInput, type ProposeResult, type RankedItem, type SupportInput };
package/dist/board.js DELETED
@@ -1,7 +0,0 @@
1
- import {
2
- BoardClient
3
- } from "./chunk-E2MA75S3.js";
4
- import "./chunk-ZFVE6MU7.js";
5
- export {
6
- BoardClient
7
- };