@inversealtruism/cairn-sdk 0.3.2 → 0.4.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/dist/board.cjs CHANGED
@@ -27,7 +27,7 @@ var import_csd_codec = require("@inversealtruism/csd-codec");
27
27
  var import_csd_codec2 = require("@inversealtruism/csd-codec");
28
28
 
29
29
  // src/errors.ts
30
- var SDK_VERSION = "0.3.2";
30
+ var SDK_VERSION = "0.4.0";
31
31
  var DOCS_BASE = "https://cairn-substrate.com/docs/sdk";
32
32
  var CairnError = class extends Error {
33
33
  /** Stable machine code for programmatic branching. */
package/dist/board.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  BoardClient
3
- } from "./chunk-CZWDA3A5.js";
4
- import "./chunk-FMXNJQWL.js";
3
+ } from "./chunk-2ZML7IKV.js";
4
+ import "./chunk-MREZG2HN.js";
5
5
  export {
6
6
  BoardClient
7
7
  };
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  ContentVerificationError
3
- } from "./chunk-FMXNJQWL.js";
3
+ } from "./chunk-MREZG2HN.js";
4
4
 
5
5
  // src/content.ts
6
6
  import { payloadHash, canonicalJson, verifyContentBytes } from "@inversealtruism/csd-codec";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  CairnError
3
- } from "./chunk-FMXNJQWL.js";
3
+ } from "./chunk-MREZG2HN.js";
4
4
 
5
5
  // src/board.ts
6
6
  import { payloadHash } from "@inversealtruism/csd-codec";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  getWallet
3
- } from "./chunk-BN3PFJDY.js";
3
+ } from "./chunk-RKR3JMIA.js";
4
4
 
5
5
  // src/controller.ts
6
6
  var DISCONNECTED = Object.freeze({ status: "disconnected", account: null, error: null });
@@ -1,5 +1,5 @@
1
1
  // src/errors.ts
2
- var SDK_VERSION = "0.3.2";
2
+ var SDK_VERSION = "0.4.0";
3
3
  var DOCS_BASE = "https://cairn-substrate.com/docs/sdk";
4
4
  var CairnError = class extends Error {
5
5
  /** Stable machine code for programmatic branching. */
@@ -91,7 +91,7 @@ var SubmitInFlightError = class extends CairnError {
91
91
  this.txid = txid;
92
92
  }
93
93
  };
94
- var NATIVE_CODES = /* @__PURE__ */ new Set(["ACCOUNT_CHANGED", "FIRST_PARTY_ONLY", "RATE_LIMITED", "APPROVAL_CLOSED", "FORBIDDEN", "INTERNAL"]);
94
+ var NATIVE_CODES = /* @__PURE__ */ new Set(["FIRST_PARTY_ONLY", "RATE_LIMITED", "FORBIDDEN", "INTERNAL"]);
95
95
  var NESTED_TRANSIENT = /* @__PURE__ */ new Set(["VERIFY_UNAVAILABLE", "OFFER_UNKNOWN"]);
96
96
  var NESTED_TERMINAL = /* @__PURE__ */ new Set([
97
97
  "FILL_UNSAFE",
@@ -1,11 +1,13 @@
1
1
  import {
2
+ CairnError,
2
3
  NotInstalledError,
3
4
  UnsupportedMethodError,
4
5
  mapProviderError,
5
6
  mapSubmitResultError
6
- } from "./chunk-FMXNJQWL.js";
7
+ } from "./chunk-MREZG2HN.js";
7
8
 
8
9
  // src/connect.ts
10
+ var DEFAULT_PROVIDER_TIMEOUT_MS = 10 * 6e4;
9
11
  function discoverProviders(opts = {}) {
10
12
  const timeoutMs = opts.timeoutMs ?? 1e3;
11
13
  return new Promise((resolve) => {
@@ -85,8 +87,24 @@ function unwrapWrite(reply) {
85
87
  var WalletConnection = class {
86
88
  provider;
87
89
  _addr = null;
88
- constructor(provider) {
90
+ /** B7c: per-call provider timeout (ms). GENEROUS by default so it never rejects an in-flight human
91
+ * approval; 0 disables it. Every provider call (writes especially) races against it. */
92
+ _timeoutMs;
93
+ constructor(provider, opts = {}) {
89
94
  this.provider = provider;
95
+ this._timeoutMs = opts.timeoutMs ?? DEFAULT_PROVIDER_TIMEOUT_MS;
96
+ }
97
+ // B7c: race a provider call against the per-call timeout. On timeout, throw a fail-closed CairnError
98
+ // (retryable:false: a write may already have reached the wallet, so never invite a blind auto-retry). The
99
+ // genuine wallet resolves quickly once the user acts; this only fires on a truly hung/absent response.
100
+ call(op, label) {
101
+ const ms = this._timeoutMs;
102
+ if (!Number.isFinite(ms) || ms <= 0) return Promise.resolve(op());
103
+ let timer;
104
+ const timeout = new Promise((_, reject) => {
105
+ timer = setTimeout(() => reject(new CairnError(`the wallet did not respond to ${label} within ${ms}ms`, { retryable: false })), ms);
106
+ });
107
+ return Promise.race([Promise.resolve(op()), timeout]).finally(() => clearTimeout(timer));
90
108
  }
91
109
  /** The wallet's version string (e.g. "0.2.x"). */
92
110
  get version() {
@@ -98,13 +116,13 @@ var WalletConnection = class {
98
116
  }
99
117
  /** Request connection. First time per origin prompts the user; afterwards may be silent. */
100
118
  async connect() {
101
- const { addr } = unwrap(await this.provider.connect());
119
+ const { addr } = unwrap(await this.call(() => this.provider.connect(), "connect"));
102
120
  this._addr = addr;
103
121
  return addr;
104
122
  }
105
123
  /** Get the connected address (does not force a new connection prompt if already consented). */
106
124
  async getAddress() {
107
- const { addr } = unwrap(await this.provider.getAddress());
125
+ const { addr } = unwrap(await this.call(() => this.provider.getAddress(), "getAddress"));
108
126
  this._addr = addr;
109
127
  return addr;
110
128
  }
@@ -117,7 +135,7 @@ var WalletConnection = class {
117
135
  if (typeof this.provider.signIn !== "function") {
118
136
  return Promise.reject(new UnsupportedMethodError("this wallet does not expose signIn()"));
119
137
  }
120
- return Promise.resolve(this.provider.signIn()).then(unwrap);
138
+ return this.call(() => this.provider.signIn(), "signIn").then(unwrap);
121
139
  }
122
140
  /**
123
141
  * Audience-bound "Sign in with CSD" (SIWC) — the secure, replay-resistant third-party login.
@@ -133,7 +151,7 @@ var WalletConnection = class {
133
151
  if (typeof this.provider.signInWithCsd !== "function") {
134
152
  return Promise.reject(new UnsupportedMethodError("this wallet predates Sign-in-with-CSD \u2014 ask the user to update the Cairn Wallet"));
135
153
  }
136
- return Promise.resolve(this.provider.signInWithCsd(params)).then(unwrap);
154
+ return this.call(() => this.provider.signInWithCsd(params), "signInWithCsd").then(unwrap);
137
155
  }
138
156
  /** True if this wallet supports audience-bound SIWC (safe to call `signInWithCsd`). */
139
157
  get supportsSiwc() {
@@ -143,7 +161,7 @@ var WalletConnection = class {
143
161
  async getCapabilities() {
144
162
  if (typeof this.provider.getCapabilities !== "function") return null;
145
163
  try {
146
- return await this.provider.getCapabilities();
164
+ return await this.call(() => this.provider.getCapabilities(), "getCapabilities");
147
165
  } catch {
148
166
  return null;
149
167
  }
@@ -159,24 +177,24 @@ var WalletConnection = class {
159
177
  if (typeof this.provider.fillOffer !== "function") {
160
178
  return Promise.reject(new UnsupportedMethodError("this wallet predates fillOffer() \u2014 ask the user to update the Cairn Wallet"));
161
179
  }
162
- return Promise.resolve(this.provider.fillOffer(params)).then(unwrapWrite);
180
+ return this.call(() => this.provider.fillOffer(params), "fillOffer").then(unwrapWrite);
163
181
  }
164
182
  /** This origin's current permission grant (EIP-2255-style). Silent; [] if none / unsupported. */
165
183
  async getPermissions() {
166
184
  if (typeof this.provider.getPermissions !== "function") return [];
167
- return unwrap(await this.provider.getPermissions());
185
+ return unwrap(await this.call(() => this.provider.getPermissions(), "getPermissions"));
168
186
  }
169
187
  /** Explicitly request permission to see the address (prompts, like connect). */
170
188
  requestPermissions() {
171
189
  if (typeof this.provider.requestPermissions !== "function") {
172
190
  return Promise.reject(new UnsupportedMethodError("this wallet predates requestPermissions \u2014 use connect()"));
173
191
  }
174
- return Promise.resolve(this.provider.requestPermissions()).then(unwrap);
192
+ return this.call(() => this.provider.requestPermissions(), "requestPermissions").then(unwrap);
175
193
  }
176
194
  /** Revoke THIS origin's own access (silent). Returns { revoked }. Safe no-op on older wallets. */
177
195
  async revokePermissions() {
178
196
  if (typeof this.provider.revokePermissions !== "function") return { revoked: false };
179
- return unwrap(await this.provider.revokePermissions());
197
+ return unwrap(await this.call(() => this.provider.revokePermissions(), "revokePermissions"));
180
198
  }
181
199
  /**
182
200
  * Subscribe to provider events. `accountsChanged` fires with `[]` when this site loses access
@@ -192,26 +210,26 @@ var WalletConnection = class {
192
210
  }
193
211
  /** Send CSD (always prompts with clear-signing; wallet picks inputs + returns change to itself). */
194
212
  send(params) {
195
- return Promise.resolve(this.provider.send(params)).then(unwrapWrite);
213
+ return this.call(() => this.provider.send(params), "send").then(unwrapWrite);
196
214
  }
197
215
  /** Low-level Propose (always prompts). Prefer `cairn.board.propose()` which computes the hash/uri/expiry. */
198
216
  propose(params) {
199
- return Promise.resolve(this.provider.propose(params)).then(unwrapWrite);
217
+ return this.call(() => this.provider.propose(params), "propose").then(unwrapWrite);
200
218
  }
201
219
  /** Attest/support a proposal (always prompts). */
202
220
  attest(params) {
203
- return Promise.resolve(this.provider.attest(params)).then(unwrapWrite);
221
+ return this.call(() => this.provider.attest(params), "attest").then(unwrapWrite);
204
222
  }
205
223
  /** Seal a commit-reveal claim (always prompts; salt stays inside the wallet). */
206
224
  sealClaim(params) {
207
- return Promise.resolve(this.provider.sealClaim(params)).then(unwrapWrite);
225
+ return this.call(() => this.provider.sealClaim(params), "sealClaim").then(unwrapWrite);
208
226
  }
209
227
  /** Reveal a previously sealed claim by its sealing txid (always prompts). */
210
228
  revealClaim(txid) {
211
229
  if (typeof txid !== "string" || !txid) {
212
230
  return Promise.reject(new UnsupportedMethodError("revealClaim requires the sealing txid"));
213
231
  }
214
- return Promise.resolve(this.provider.revealClaim(txid)).then(unwrapWrite);
232
+ return this.call(() => this.provider.revealClaim(txid), "revealClaim").then(unwrapWrite);
215
233
  }
216
234
  };
217
235
  async function getWallet(opts = {}) {
package/dist/connect.cjs CHANGED
@@ -30,7 +30,7 @@ __export(connect_exports, {
30
30
  module.exports = __toCommonJS(connect_exports);
31
31
 
32
32
  // src/errors.ts
33
- var SDK_VERSION = "0.3.2";
33
+ var SDK_VERSION = "0.4.0";
34
34
  var DOCS_BASE = "https://cairn-substrate.com/docs/sdk";
35
35
  var CairnError = class extends Error {
36
36
  /** Stable machine code for programmatic branching. */
@@ -104,7 +104,7 @@ var SubmitInFlightError = class extends CairnError {
104
104
  this.txid = txid;
105
105
  }
106
106
  };
107
- var NATIVE_CODES = /* @__PURE__ */ new Set(["ACCOUNT_CHANGED", "FIRST_PARTY_ONLY", "RATE_LIMITED", "APPROVAL_CLOSED", "FORBIDDEN", "INTERNAL"]);
107
+ var NATIVE_CODES = /* @__PURE__ */ new Set(["FIRST_PARTY_ONLY", "RATE_LIMITED", "FORBIDDEN", "INTERNAL"]);
108
108
  var NESTED_TRANSIENT = /* @__PURE__ */ new Set(["VERIFY_UNAVAILABLE", "OFFER_UNKNOWN"]);
109
109
  var NESTED_TERMINAL = /* @__PURE__ */ new Set([
110
110
  "FILL_UNSAFE",
@@ -148,6 +148,7 @@ function mapSubmitResultError(result) {
148
148
  }
149
149
 
150
150
  // src/connect.ts
151
+ var DEFAULT_PROVIDER_TIMEOUT_MS = 10 * 6e4;
151
152
  function discoverProviders(opts = {}) {
152
153
  const timeoutMs = opts.timeoutMs ?? 1e3;
153
154
  return new Promise((resolve) => {
@@ -227,8 +228,24 @@ function unwrapWrite(reply) {
227
228
  var WalletConnection = class {
228
229
  provider;
229
230
  _addr = null;
230
- constructor(provider) {
231
+ /** B7c: per-call provider timeout (ms). GENEROUS by default so it never rejects an in-flight human
232
+ * approval; 0 disables it. Every provider call (writes especially) races against it. */
233
+ _timeoutMs;
234
+ constructor(provider, opts = {}) {
231
235
  this.provider = provider;
236
+ this._timeoutMs = opts.timeoutMs ?? DEFAULT_PROVIDER_TIMEOUT_MS;
237
+ }
238
+ // B7c: race a provider call against the per-call timeout. On timeout, throw a fail-closed CairnError
239
+ // (retryable:false: a write may already have reached the wallet, so never invite a blind auto-retry). The
240
+ // genuine wallet resolves quickly once the user acts; this only fires on a truly hung/absent response.
241
+ call(op, label) {
242
+ const ms = this._timeoutMs;
243
+ if (!Number.isFinite(ms) || ms <= 0) return Promise.resolve(op());
244
+ let timer;
245
+ const timeout = new Promise((_, reject) => {
246
+ timer = setTimeout(() => reject(new CairnError(`the wallet did not respond to ${label} within ${ms}ms`, { retryable: false })), ms);
247
+ });
248
+ return Promise.race([Promise.resolve(op()), timeout]).finally(() => clearTimeout(timer));
232
249
  }
233
250
  /** The wallet's version string (e.g. "0.2.x"). */
234
251
  get version() {
@@ -240,13 +257,13 @@ var WalletConnection = class {
240
257
  }
241
258
  /** Request connection. First time per origin prompts the user; afterwards may be silent. */
242
259
  async connect() {
243
- const { addr } = unwrap(await this.provider.connect());
260
+ const { addr } = unwrap(await this.call(() => this.provider.connect(), "connect"));
244
261
  this._addr = addr;
245
262
  return addr;
246
263
  }
247
264
  /** Get the connected address (does not force a new connection prompt if already consented). */
248
265
  async getAddress() {
249
- const { addr } = unwrap(await this.provider.getAddress());
266
+ const { addr } = unwrap(await this.call(() => this.provider.getAddress(), "getAddress"));
250
267
  this._addr = addr;
251
268
  return addr;
252
269
  }
@@ -259,7 +276,7 @@ var WalletConnection = class {
259
276
  if (typeof this.provider.signIn !== "function") {
260
277
  return Promise.reject(new UnsupportedMethodError("this wallet does not expose signIn()"));
261
278
  }
262
- return Promise.resolve(this.provider.signIn()).then(unwrap);
279
+ return this.call(() => this.provider.signIn(), "signIn").then(unwrap);
263
280
  }
264
281
  /**
265
282
  * Audience-bound "Sign in with CSD" (SIWC) — the secure, replay-resistant third-party login.
@@ -275,7 +292,7 @@ var WalletConnection = class {
275
292
  if (typeof this.provider.signInWithCsd !== "function") {
276
293
  return Promise.reject(new UnsupportedMethodError("this wallet predates Sign-in-with-CSD \u2014 ask the user to update the Cairn Wallet"));
277
294
  }
278
- return Promise.resolve(this.provider.signInWithCsd(params)).then(unwrap);
295
+ return this.call(() => this.provider.signInWithCsd(params), "signInWithCsd").then(unwrap);
279
296
  }
280
297
  /** True if this wallet supports audience-bound SIWC (safe to call `signInWithCsd`). */
281
298
  get supportsSiwc() {
@@ -285,7 +302,7 @@ var WalletConnection = class {
285
302
  async getCapabilities() {
286
303
  if (typeof this.provider.getCapabilities !== "function") return null;
287
304
  try {
288
- return await this.provider.getCapabilities();
305
+ return await this.call(() => this.provider.getCapabilities(), "getCapabilities");
289
306
  } catch {
290
307
  return null;
291
308
  }
@@ -301,24 +318,24 @@ var WalletConnection = class {
301
318
  if (typeof this.provider.fillOffer !== "function") {
302
319
  return Promise.reject(new UnsupportedMethodError("this wallet predates fillOffer() \u2014 ask the user to update the Cairn Wallet"));
303
320
  }
304
- return Promise.resolve(this.provider.fillOffer(params)).then(unwrapWrite);
321
+ return this.call(() => this.provider.fillOffer(params), "fillOffer").then(unwrapWrite);
305
322
  }
306
323
  /** This origin's current permission grant (EIP-2255-style). Silent; [] if none / unsupported. */
307
324
  async getPermissions() {
308
325
  if (typeof this.provider.getPermissions !== "function") return [];
309
- return unwrap(await this.provider.getPermissions());
326
+ return unwrap(await this.call(() => this.provider.getPermissions(), "getPermissions"));
310
327
  }
311
328
  /** Explicitly request permission to see the address (prompts, like connect). */
312
329
  requestPermissions() {
313
330
  if (typeof this.provider.requestPermissions !== "function") {
314
331
  return Promise.reject(new UnsupportedMethodError("this wallet predates requestPermissions \u2014 use connect()"));
315
332
  }
316
- return Promise.resolve(this.provider.requestPermissions()).then(unwrap);
333
+ return this.call(() => this.provider.requestPermissions(), "requestPermissions").then(unwrap);
317
334
  }
318
335
  /** Revoke THIS origin's own access (silent). Returns { revoked }. Safe no-op on older wallets. */
319
336
  async revokePermissions() {
320
337
  if (typeof this.provider.revokePermissions !== "function") return { revoked: false };
321
- return unwrap(await this.provider.revokePermissions());
338
+ return unwrap(await this.call(() => this.provider.revokePermissions(), "revokePermissions"));
322
339
  }
323
340
  /**
324
341
  * Subscribe to provider events. `accountsChanged` fires with `[]` when this site loses access
@@ -334,26 +351,26 @@ var WalletConnection = class {
334
351
  }
335
352
  /** Send CSD (always prompts with clear-signing; wallet picks inputs + returns change to itself). */
336
353
  send(params) {
337
- return Promise.resolve(this.provider.send(params)).then(unwrapWrite);
354
+ return this.call(() => this.provider.send(params), "send").then(unwrapWrite);
338
355
  }
339
356
  /** Low-level Propose (always prompts). Prefer `cairn.board.propose()` which computes the hash/uri/expiry. */
340
357
  propose(params) {
341
- return Promise.resolve(this.provider.propose(params)).then(unwrapWrite);
358
+ return this.call(() => this.provider.propose(params), "propose").then(unwrapWrite);
342
359
  }
343
360
  /** Attest/support a proposal (always prompts). */
344
361
  attest(params) {
345
- return Promise.resolve(this.provider.attest(params)).then(unwrapWrite);
362
+ return this.call(() => this.provider.attest(params), "attest").then(unwrapWrite);
346
363
  }
347
364
  /** Seal a commit-reveal claim (always prompts; salt stays inside the wallet). */
348
365
  sealClaim(params) {
349
- return Promise.resolve(this.provider.sealClaim(params)).then(unwrapWrite);
366
+ return this.call(() => this.provider.sealClaim(params), "sealClaim").then(unwrapWrite);
350
367
  }
351
368
  /** Reveal a previously sealed claim by its sealing txid (always prompts). */
352
369
  revealClaim(txid) {
353
370
  if (typeof txid !== "string" || !txid) {
354
371
  return Promise.reject(new UnsupportedMethodError("revealClaim requires the sealing txid"));
355
372
  }
356
- return Promise.resolve(this.provider.revealClaim(txid)).then(unwrapWrite);
373
+ return this.call(() => this.provider.revealClaim(txid), "revealClaim").then(unwrapWrite);
357
374
  }
358
375
  };
359
376
  async function getWallet(opts = {}) {
@@ -208,7 +208,13 @@ declare function isInstalled(): boolean;
208
208
  declare class WalletConnection {
209
209
  readonly provider: CairnProvider;
210
210
  private _addr;
211
- constructor(provider: CairnProvider);
211
+ /** B7c: per-call provider timeout (ms). GENEROUS by default so it never rejects an in-flight human
212
+ * approval; 0 disables it. Every provider call (writes especially) races against it. */
213
+ private readonly _timeoutMs;
214
+ constructor(provider: CairnProvider, opts?: {
215
+ timeoutMs?: number;
216
+ });
217
+ private call;
212
218
  /** The wallet's version string (e.g. "0.2.x"). */
213
219
  get version(): string;
214
220
  /** The connected address, if `connect()`/`getAddress()` has resolved. */
package/dist/connect.d.ts CHANGED
@@ -208,7 +208,13 @@ declare function isInstalled(): boolean;
208
208
  declare class WalletConnection {
209
209
  readonly provider: CairnProvider;
210
210
  private _addr;
211
- constructor(provider: CairnProvider);
211
+ /** B7c: per-call provider timeout (ms). GENEROUS by default so it never rejects an in-flight human
212
+ * approval; 0 disables it. Every provider call (writes especially) races against it. */
213
+ private readonly _timeoutMs;
214
+ constructor(provider: CairnProvider, opts?: {
215
+ timeoutMs?: number;
216
+ });
217
+ private call;
212
218
  /** The wallet's version string (e.g. "0.2.x"). */
213
219
  get version(): string;
214
220
  /** The connected address, if `connect()`/`getAddress()` has resolved. */
package/dist/connect.js CHANGED
@@ -5,8 +5,8 @@ import {
5
5
  discoverProviders,
6
6
  getWallet,
7
7
  isInstalled
8
- } from "./chunk-BN3PFJDY.js";
9
- import "./chunk-FMXNJQWL.js";
8
+ } from "./chunk-RKR3JMIA.js";
9
+ import "./chunk-MREZG2HN.js";
10
10
  export {
11
11
  WalletConnection,
12
12
  connect,
package/dist/content.cjs CHANGED
@@ -26,7 +26,7 @@ module.exports = __toCommonJS(content_exports);
26
26
  var import_csd_codec = require("@inversealtruism/csd-codec");
27
27
 
28
28
  // src/errors.ts
29
- var SDK_VERSION = "0.3.2";
29
+ var SDK_VERSION = "0.4.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. */
package/dist/content.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  ContentClient
3
- } from "./chunk-JKC5K253.js";
4
- import "./chunk-FMXNJQWL.js";
3
+ } from "./chunk-2L5QNDAE.js";
4
+ import "./chunk-MREZG2HN.js";
5
5
  export {
6
6
  ContentClient
7
7
  };
@@ -25,7 +25,7 @@ __export(controller_exports, {
25
25
  module.exports = __toCommonJS(controller_exports);
26
26
 
27
27
  // src/errors.ts
28
- var SDK_VERSION = "0.3.2";
28
+ var SDK_VERSION = "0.4.0";
29
29
  var DOCS_BASE = "https://cairn-substrate.com/docs/sdk";
30
30
  var CairnError = class extends Error {
31
31
  /** Stable machine code for programmatic branching. */
@@ -99,7 +99,7 @@ var SubmitInFlightError = class extends CairnError {
99
99
  this.txid = txid;
100
100
  }
101
101
  };
102
- var NATIVE_CODES = /* @__PURE__ */ new Set(["ACCOUNT_CHANGED", "FIRST_PARTY_ONLY", "RATE_LIMITED", "APPROVAL_CLOSED", "FORBIDDEN", "INTERNAL"]);
102
+ var NATIVE_CODES = /* @__PURE__ */ new Set(["FIRST_PARTY_ONLY", "RATE_LIMITED", "FORBIDDEN", "INTERNAL"]);
103
103
  var NESTED_TRANSIENT = /* @__PURE__ */ new Set(["VERIFY_UNAVAILABLE", "OFFER_UNKNOWN"]);
104
104
  var NESTED_TERMINAL = /* @__PURE__ */ new Set([
105
105
  "FILL_UNSAFE",
@@ -143,6 +143,7 @@ function mapSubmitResultError(result) {
143
143
  }
144
144
 
145
145
  // src/connect.ts
146
+ var DEFAULT_PROVIDER_TIMEOUT_MS = 10 * 6e4;
146
147
  function detectProvider(opts = {}) {
147
148
  const timeoutMs = opts.timeoutMs ?? 3e3;
148
149
  return new Promise((resolve, reject) => {
@@ -199,8 +200,24 @@ function unwrapWrite(reply) {
199
200
  var WalletConnection = class {
200
201
  provider;
201
202
  _addr = null;
202
- constructor(provider) {
203
+ /** B7c: per-call provider timeout (ms). GENEROUS by default so it never rejects an in-flight human
204
+ * approval; 0 disables it. Every provider call (writes especially) races against it. */
205
+ _timeoutMs;
206
+ constructor(provider, opts = {}) {
203
207
  this.provider = provider;
208
+ this._timeoutMs = opts.timeoutMs ?? DEFAULT_PROVIDER_TIMEOUT_MS;
209
+ }
210
+ // B7c: race a provider call against the per-call timeout. On timeout, throw a fail-closed CairnError
211
+ // (retryable:false: a write may already have reached the wallet, so never invite a blind auto-retry). The
212
+ // genuine wallet resolves quickly once the user acts; this only fires on a truly hung/absent response.
213
+ call(op, label) {
214
+ const ms = this._timeoutMs;
215
+ if (!Number.isFinite(ms) || ms <= 0) return Promise.resolve(op());
216
+ let timer;
217
+ const timeout = new Promise((_, reject) => {
218
+ timer = setTimeout(() => reject(new CairnError(`the wallet did not respond to ${label} within ${ms}ms`, { retryable: false })), ms);
219
+ });
220
+ return Promise.race([Promise.resolve(op()), timeout]).finally(() => clearTimeout(timer));
204
221
  }
205
222
  /** The wallet's version string (e.g. "0.2.x"). */
206
223
  get version() {
@@ -212,13 +229,13 @@ var WalletConnection = class {
212
229
  }
213
230
  /** Request connection. First time per origin prompts the user; afterwards may be silent. */
214
231
  async connect() {
215
- const { addr } = unwrap(await this.provider.connect());
232
+ const { addr } = unwrap(await this.call(() => this.provider.connect(), "connect"));
216
233
  this._addr = addr;
217
234
  return addr;
218
235
  }
219
236
  /** Get the connected address (does not force a new connection prompt if already consented). */
220
237
  async getAddress() {
221
- const { addr } = unwrap(await this.provider.getAddress());
238
+ const { addr } = unwrap(await this.call(() => this.provider.getAddress(), "getAddress"));
222
239
  this._addr = addr;
223
240
  return addr;
224
241
  }
@@ -231,7 +248,7 @@ var WalletConnection = class {
231
248
  if (typeof this.provider.signIn !== "function") {
232
249
  return Promise.reject(new UnsupportedMethodError("this wallet does not expose signIn()"));
233
250
  }
234
- return Promise.resolve(this.provider.signIn()).then(unwrap);
251
+ return this.call(() => this.provider.signIn(), "signIn").then(unwrap);
235
252
  }
236
253
  /**
237
254
  * Audience-bound "Sign in with CSD" (SIWC) — the secure, replay-resistant third-party login.
@@ -247,7 +264,7 @@ var WalletConnection = class {
247
264
  if (typeof this.provider.signInWithCsd !== "function") {
248
265
  return Promise.reject(new UnsupportedMethodError("this wallet predates Sign-in-with-CSD \u2014 ask the user to update the Cairn Wallet"));
249
266
  }
250
- return Promise.resolve(this.provider.signInWithCsd(params)).then(unwrap);
267
+ return this.call(() => this.provider.signInWithCsd(params), "signInWithCsd").then(unwrap);
251
268
  }
252
269
  /** True if this wallet supports audience-bound SIWC (safe to call `signInWithCsd`). */
253
270
  get supportsSiwc() {
@@ -257,7 +274,7 @@ var WalletConnection = class {
257
274
  async getCapabilities() {
258
275
  if (typeof this.provider.getCapabilities !== "function") return null;
259
276
  try {
260
- return await this.provider.getCapabilities();
277
+ return await this.call(() => this.provider.getCapabilities(), "getCapabilities");
261
278
  } catch {
262
279
  return null;
263
280
  }
@@ -273,24 +290,24 @@ var WalletConnection = class {
273
290
  if (typeof this.provider.fillOffer !== "function") {
274
291
  return Promise.reject(new UnsupportedMethodError("this wallet predates fillOffer() \u2014 ask the user to update the Cairn Wallet"));
275
292
  }
276
- return Promise.resolve(this.provider.fillOffer(params)).then(unwrapWrite);
293
+ return this.call(() => this.provider.fillOffer(params), "fillOffer").then(unwrapWrite);
277
294
  }
278
295
  /** This origin's current permission grant (EIP-2255-style). Silent; [] if none / unsupported. */
279
296
  async getPermissions() {
280
297
  if (typeof this.provider.getPermissions !== "function") return [];
281
- return unwrap(await this.provider.getPermissions());
298
+ return unwrap(await this.call(() => this.provider.getPermissions(), "getPermissions"));
282
299
  }
283
300
  /** Explicitly request permission to see the address (prompts, like connect). */
284
301
  requestPermissions() {
285
302
  if (typeof this.provider.requestPermissions !== "function") {
286
303
  return Promise.reject(new UnsupportedMethodError("this wallet predates requestPermissions \u2014 use connect()"));
287
304
  }
288
- return Promise.resolve(this.provider.requestPermissions()).then(unwrap);
305
+ return this.call(() => this.provider.requestPermissions(), "requestPermissions").then(unwrap);
289
306
  }
290
307
  /** Revoke THIS origin's own access (silent). Returns { revoked }. Safe no-op on older wallets. */
291
308
  async revokePermissions() {
292
309
  if (typeof this.provider.revokePermissions !== "function") return { revoked: false };
293
- return unwrap(await this.provider.revokePermissions());
310
+ return unwrap(await this.call(() => this.provider.revokePermissions(), "revokePermissions"));
294
311
  }
295
312
  /**
296
313
  * Subscribe to provider events. `accountsChanged` fires with `[]` when this site loses access
@@ -306,26 +323,26 @@ var WalletConnection = class {
306
323
  }
307
324
  /** Send CSD (always prompts with clear-signing; wallet picks inputs + returns change to itself). */
308
325
  send(params) {
309
- return Promise.resolve(this.provider.send(params)).then(unwrapWrite);
326
+ return this.call(() => this.provider.send(params), "send").then(unwrapWrite);
310
327
  }
311
328
  /** Low-level Propose (always prompts). Prefer `cairn.board.propose()` which computes the hash/uri/expiry. */
312
329
  propose(params) {
313
- return Promise.resolve(this.provider.propose(params)).then(unwrapWrite);
330
+ return this.call(() => this.provider.propose(params), "propose").then(unwrapWrite);
314
331
  }
315
332
  /** Attest/support a proposal (always prompts). */
316
333
  attest(params) {
317
- return Promise.resolve(this.provider.attest(params)).then(unwrapWrite);
334
+ return this.call(() => this.provider.attest(params), "attest").then(unwrapWrite);
318
335
  }
319
336
  /** Seal a commit-reveal claim (always prompts; salt stays inside the wallet). */
320
337
  sealClaim(params) {
321
- return Promise.resolve(this.provider.sealClaim(params)).then(unwrapWrite);
338
+ return this.call(() => this.provider.sealClaim(params), "sealClaim").then(unwrapWrite);
322
339
  }
323
340
  /** Reveal a previously sealed claim by its sealing txid (always prompts). */
324
341
  revealClaim(txid) {
325
342
  if (typeof txid !== "string" || !txid) {
326
343
  return Promise.reject(new UnsupportedMethodError("revealClaim requires the sealing txid"));
327
344
  }
328
- return Promise.resolve(this.provider.revealClaim(txid)).then(unwrapWrite);
345
+ return this.call(() => this.provider.revealClaim(txid), "revealClaim").then(unwrapWrite);
329
346
  }
330
347
  };
331
348
  async function getWallet(opts = {}) {
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  CairnController
3
- } from "./chunk-AGU6K5EZ.js";
4
- import "./chunk-BN3PFJDY.js";
5
- import "./chunk-FMXNJQWL.js";
3
+ } from "./chunk-GTTLMWGY.js";
4
+ import "./chunk-RKR3JMIA.js";
5
+ import "./chunk-MREZG2HN.js";
6
6
  export {
7
7
  CairnController
8
8
  };