@capxul/sdk 2.8.0 → 2.8.2

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/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
- import { A as postHogFailureObservation, B as injectedWalletSigner, C as fingerprintPaymentIntent, D as failureEvidenceProps, E as failureDetail, F as isSettingUpLifecycle, G as PAYMENT_DIRECTIONS, H as OBSERVATION_CONTEXT_HEADER, I as formatTraceparent, J as BootstrapEnvelope, K as PAYMENT_STATUSES, L as copyInvocationObservation, M as captureExceptionSync, N as version, O as observationContextProps, P as fromWei, Q as BASE_SEPOLIA_CHAIN_ID, R as readInvocationObservation, S as AuthClientPortTag, T as CAPXUL_SDK_EXPECTED_OUTCOME_EVENT, U as encodeObservationContextHeader, V as signerFailure, W as sanitizeObservationContext, X as CAPXUL_PAYMENTS_V2_ADDRESS, Y as EngineeringTelemetryBootstrapPolicy, Z as normalizeBindingEmail, _ as ClockPortTag, a as TelemetryPortTag, b as authClientPortFromPromiseAdapter, c as smartAccountErrorFromCapxul, d as wireChainId, et as destination, f as IdentityPortTag, g as ClockError, h as convexCallErrorFromCapxul, i as PostHogTelemetryLayer, j as captureException, k as observeFailedResult, l as AccountReadPortTag, m as ConvexCallPortTag, n as postHogProductTelemetry, nt as isRestoring, p as identityErrorFromCapxul, q as CAPXUL_FUNCTIONS, r as detectAuthCacheAdapter, s as SmartAccountPortTag, t as assembleCapxulClient, tt as isClaimed, u as accountReadErrorFromCapxul, v as BootstrapPortTag, w as toWei, x as AuthClientError, y as bootstrapErrorFromCapxul, z as causeChain } from "./create-capxul-client-YSN2yN8m.mjs";
2
- import { B as Errors, C as toDurationMs, D as toJwtToken, E as toEpochSeconds, F as toSessionToken, H as CHAIN_UPSTREAMS, I as decodeConvexError, L as CAPXUL_ERROR_CODES, N as toPublishableKey, O as toKycTier, P as toRoleKey, R as CapxulError, S as toCurrencyCode, T as toEpochMs, U as FAILURE_MODES, V as isCapxulError, b as toChainId, g as toAllowedOrigin, h as toAddress, k as toOrgId, m as toAccountId, o as AuthCachePortTag, p as currencySymbolFor, v as toAuthUserId, w as toEmail, x as toCountryCode } from "./InMemoryAuthCacheAdapter-FptQ2p7C.mjs";
1
+ import { $ as sanitizeObservationContext, A as observationContextProps, B as fromWei, C as AuthClientError, D as CAPXUL_SDK_EXPECTED_OUTCOME_EVENT, E as toWei, F as normalizeExceptionErrorKind, G as isSignerNotReadySignal, H as formatTraceparent, I as normalizeExceptionOperation, J as resolveFailureMode, K as openfortSignerNotReadyCode, L as projectSdkException, M as postHogFailureObservation, N as EXCEPTION_MESSAGE, O as failureDetail, P as SDK_VERSION$1, Q as encodeObservationContextHeader, R as safeExceptionLabel, S as authClientPortFromPromiseAdapter, T as fingerprintPaymentIntent, U as causeChain, V as isSettingUpLifecycle, W as injectedWalletSigner, X as readInvocationObservation, Y as copyInvocationObservation, Z as OBSERVATION_CONTEXT_HEADER, _ as convexCallErrorFromCapxul, a as AccountReadPortTag, at as CAPXUL_PAYMENTS_V2_ADDRESS, b as BootstrapPortTag, d as retryIdempotentRead, dt as isRestoring, et as PAYMENT_DIRECTIONS, f as PRODUCT_INVOCATION, g as ConvexCallPortTag, h as identityErrorFromCapxul, i as smartAccountErrorFromCapxul, it as EngineeringTelemetryBootstrapPolicy, j as observeFailedResult, k as failureEvidenceProps, l as redactTelemetryProps, lt as destination, m as IdentityPortTag, n as detectAuthCacheAdapter, nt as CAPXUL_FUNCTIONS, o as accountReadErrorFromCapxul, ot as normalizeBindingEmail, p as wireChainId, q as signerFailure, r as SmartAccountPortTag, rt as BootstrapEnvelope, s as TelemetryPortTag, st as BASE_SEPOLIA_CHAIN_ID, t as assembleCapxulClient, tt as PAYMENT_STATUSES, u as stampTelemetryEnvelope, ut as isClaimed, v as ClockError, w as AuthClientPortTag, x as bootstrapErrorFromCapxul, y as ClockPortTag, z as version } from "./create-capxul-client-BKJHefmA.mjs";
2
+ import { B as Errors, C as toDurationMs, D as toJwtToken, E as toEpochSeconds, F as toSessionToken, H as CHAIN_UPSTREAMS, I as decodeConvexError, L as CAPXUL_ERROR_CODES, N as toPublishableKey, O as toKycTier, P as toRoleKey, R as CapxulError, S as toCurrencyCode, T as toEpochMs, U as FAILURE_MODES, V as isCapxulError, b as toChainId, g as toAllowedOrigin, h as toAddress, k as toOrgId, m as toAccountId, o as AuthCachePortTag, p as currencySymbolFor, v as toAuthUserId, w as toEmail, x as toCountryCode } from "./InMemoryAuthCacheAdapter-BrEN-men.mjs";
3
3
  import { keccak256, recoverAddress, stringToHex } from "viem";
4
4
  import { Cause, Context, Data, Duration, Effect, Exit, Layer, Result, Schedule, SchemaIssue, SchemaParser, Scope, Tracer } from "effect";
5
5
  import { getFunctionName, makeFunctionReference } from "convex/server";
@@ -1302,11 +1302,13 @@ async function safeText(res) {
1302
1302
  }
1303
1303
  var HttpBootstrapAdapter = class {
1304
1304
  bootstrapBaseUrl;
1305
+ bootstrapHost;
1305
1306
  fetchImpl;
1306
1307
  observation;
1307
1308
  retry;
1308
1309
  constructor(deps) {
1309
1310
  this.bootstrapBaseUrl = deps.bootstrapBaseUrl.replace(/\/$/, "");
1311
+ this.bootstrapHost = hostOf(this.bootstrapBaseUrl);
1310
1312
  this.observation = deps.observation;
1311
1313
  this.retry = deps.retry ?? DEFAULT_RETRY;
1312
1314
  this.fetchImpl = deps.fetch ?? ((input, init) => globalThis.fetch(input, init));
@@ -1332,7 +1334,11 @@ var HttpBootstrapAdapter = class {
1332
1334
  body: JSON.stringify({ publishableKey: input.publishableKey })
1333
1335
  });
1334
1336
  },
1335
- catch: (cause) => bootstrapErrorFromCapxul("network", Errors.networkError("bootstrap", cause))
1337
+ catch: (cause) => bootstrapErrorFromCapxul("network", Errors.networkError("bootstrap", cause, {
1338
+ provider: "bootstrap",
1339
+ failure_mode: "upstream-down",
1340
+ ...this.bootstrapHost === void 0 ? {} : { target_host: this.bootstrapHost }
1341
+ }))
1336
1342
  }).pipe(Effect.flatMap((res) => this.mapResponse(res)));
1337
1343
  }
1338
1344
  mapResponse(res) {
@@ -1388,6 +1394,13 @@ var HttpBootstrapAdapter = class {
1388
1394
  function HttpBootstrapLayer(deps) {
1389
1395
  return Layer.succeed(BootstrapPortTag, new HttpBootstrapAdapter(deps));
1390
1396
  }
1397
+ function hostOf(url) {
1398
+ try {
1399
+ return new URL(url).hostname;
1400
+ } catch {
1401
+ return;
1402
+ }
1403
+ }
1391
1404
  function normalizeRuntimeUrl(field, raw) {
1392
1405
  let parsed;
1393
1406
  try {
@@ -1419,15 +1432,195 @@ function SystemClockLayer() {
1419
1432
  return Layer.succeed(ClockPortTag, new SystemClockAdapter());
1420
1433
  }
1421
1434
  //#endregion
1435
+ //#region src/adapters/convex-call/convex-connection-monitor.ts
1436
+ var ConvexConnectionMonitor = class {
1437
+ #connectionId = makeConnectionId();
1438
+ #browser = new BrowserLifecycleMonitor();
1439
+ #lastClose;
1440
+ observedWebSocketConstructor() {
1441
+ const NativeWebSocket = globalThis.WebSocket;
1442
+ if (typeof globalThis.window === "undefined" || NativeWebSocket === void 0) return void 0;
1443
+ return new Proxy(NativeWebSocket, { construct: (Target, args) => {
1444
+ const socket = Reflect.construct(Target, args);
1445
+ socket.addEventListener("close", (event) => {
1446
+ this.#lastClose = this.#browser.socketClose(event);
1447
+ });
1448
+ return socket;
1449
+ } });
1450
+ }
1451
+ close() {
1452
+ this.#browser.close();
1453
+ }
1454
+ observe(client, observer) {
1455
+ const subscribe = client.subscribeToConnectionState?.bind(client);
1456
+ if (subscribe === void 0) return () => this.#browser.close();
1457
+ let previous;
1458
+ const unsubscribe = subscribe((next) => {
1459
+ const diagnostics = this.#diagnostics(previous, next);
1460
+ previous = next;
1461
+ for (const diagnostic of diagnostics) emitDiagnostic(observer, diagnostic);
1462
+ });
1463
+ if (previous === void 0) {
1464
+ const current = safeConnectionState(client);
1465
+ if (current !== void 0) {
1466
+ previous = current;
1467
+ for (const diagnostic of this.#diagnostics(void 0, current)) emitDiagnostic(observer, diagnostic);
1468
+ }
1469
+ }
1470
+ let closed = false;
1471
+ return () => {
1472
+ if (closed) return;
1473
+ closed = true;
1474
+ try {
1475
+ unsubscribe();
1476
+ } finally {
1477
+ this.#browser.close();
1478
+ }
1479
+ };
1480
+ }
1481
+ reconnectEvidence(start, end) {
1482
+ if (!connectionRestarted(start, end)) return void 0;
1483
+ return {
1484
+ kind: "connection-lost-in-flight",
1485
+ connection_id: this.#connectionId,
1486
+ start_connection_count: start.connectionCount,
1487
+ connection_count: end.connectionCount,
1488
+ ...end.connectionRetries === void 0 ? {} : { connection_retries: end.connectionRetries },
1489
+ ...closeFields(this.#lastClose)
1490
+ };
1491
+ }
1492
+ #diagnostics(previous, next) {
1493
+ const transitions = connectionTransitions(previous, next);
1494
+ if (transitions.length === 0) return [];
1495
+ const oldest = next.timeOfOldestInflightRequest;
1496
+ const oldestInflightMs = oldest instanceof Date && Number.isFinite(oldest.getTime()) ? Math.max(0, Date.now() - oldest.getTime()) : void 0;
1497
+ return transitions.map((transition) => ({
1498
+ connection_id: this.#connectionId,
1499
+ transition,
1500
+ previous_connected: previous?.isWebSocketConnected ?? false,
1501
+ connected: next.isWebSocketConnected,
1502
+ ...next.hasEverConnected === void 0 ? {} : { has_ever_connected: next.hasEverConnected },
1503
+ connection_count: next.connectionCount,
1504
+ ...next.connectionRetries === void 0 ? {} : { connection_retries: next.connectionRetries },
1505
+ ...next.hasInflightRequests === void 0 ? {} : { has_inflight_requests: next.hasInflightRequests },
1506
+ ...next.inflightActions === void 0 ? {} : { inflight_actions: next.inflightActions },
1507
+ ...next.inflightMutations === void 0 ? {} : { inflight_mutations: next.inflightMutations },
1508
+ ...oldestInflightMs === void 0 ? {} : { oldest_inflight_ms: oldestInflightMs },
1509
+ ...closeFields(this.#lastClose)
1510
+ }));
1511
+ }
1512
+ };
1513
+ function connectionRestarted(start, end) {
1514
+ if (start === void 0 || end === void 0) return false;
1515
+ const opensBeforeCancellation = start.isWebSocketConnected ? 1 : 2;
1516
+ return end.connectionCount >= start.connectionCount + opensBeforeCancellation;
1517
+ }
1518
+ function connectionTransitions(previous, next) {
1519
+ if (previous === void 0) {
1520
+ if (next.isWebSocketConnected) return (next.connectionRetries ?? 0) > 0 ? ["retrying", "connected"] : ["connected"];
1521
+ return (next.connectionRetries ?? 0) > 0 ? ["disconnected", "retrying"] : [];
1522
+ }
1523
+ const transitions = [];
1524
+ if (previous.isWebSocketConnected !== next.isWebSocketConnected) transitions.push(next.isWebSocketConnected ? "connected" : "disconnected");
1525
+ if ((next.connectionRetries ?? 0) > (previous.connectionRetries ?? 0)) transitions.push("retrying");
1526
+ return transitions;
1527
+ }
1528
+ function emitDiagnostic(observer, diagnostic) {
1529
+ try {
1530
+ observer(diagnostic);
1531
+ } catch {}
1532
+ }
1533
+ function safeConnectionState(client) {
1534
+ try {
1535
+ return client.connectionState?.();
1536
+ } catch {
1537
+ return;
1538
+ }
1539
+ }
1540
+ function closeFields(close) {
1541
+ if (close === void 0) return {};
1542
+ return {
1543
+ close_code: close.code,
1544
+ close_was_clean: close.wasClean,
1545
+ close_reason_present: close.reasonPresent,
1546
+ ...close.documentVisibility === void 0 ? {} : { document_visibility: close.documentVisibility },
1547
+ ...close.navigatorOnline === void 0 ? {} : { navigator_online: close.navigatorOnline },
1548
+ ...close.browserEvent === void 0 ? {} : { last_browser_event: close.browserEvent },
1549
+ ...close.msSinceBrowserEvent === void 0 ? {} : { ms_since_browser_event: close.msSinceBrowserEvent }
1550
+ };
1551
+ }
1552
+ var BrowserLifecycleMonitor = class {
1553
+ #listeners = [];
1554
+ #last = {};
1555
+ constructor() {
1556
+ const windowTarget = eventTarget(globalThis.window);
1557
+ const documentTarget = eventTarget(globalThis.document);
1558
+ if (windowTarget === void 0 || documentTarget === void 0) return;
1559
+ this.#listen(documentTarget, "visibilitychange", () => {
1560
+ this.#record(globalThis.document?.visibilityState === "hidden" ? "visibility-hidden" : "visibility-visible");
1561
+ });
1562
+ this.#listen(windowTarget, "pagehide", () => this.#record("pagehide"));
1563
+ this.#listen(windowTarget, "pageshow", () => this.#record("pageshow"));
1564
+ this.#listen(windowTarget, "online", () => this.#record("online"));
1565
+ this.#listen(windowTarget, "offline", () => this.#record("offline"));
1566
+ }
1567
+ socketClose(event) {
1568
+ const now = Date.now();
1569
+ return {
1570
+ code: event.code,
1571
+ wasClean: event.wasClean,
1572
+ reasonPresent: event.reason.length > 0,
1573
+ ...this.#last.event === void 0 ? {} : { browserEvent: this.#last.event },
1574
+ ...this.#last.at === void 0 ? {} : { msSinceBrowserEvent: Math.max(0, now - this.#last.at) },
1575
+ ...globalThis.document?.visibilityState === void 0 ? {} : { documentVisibility: globalThis.document.visibilityState },
1576
+ ...globalThis.navigator?.onLine === void 0 ? {} : { navigatorOnline: globalThis.navigator.onLine }
1577
+ };
1578
+ }
1579
+ close() {
1580
+ for (const [target, name, listener] of this.#listeners) target.removeEventListener(name, listener);
1581
+ this.#listeners.length = 0;
1582
+ }
1583
+ #listen(target, name, listener) {
1584
+ target.addEventListener(name, listener);
1585
+ this.#listeners.push([
1586
+ target,
1587
+ name,
1588
+ listener
1589
+ ]);
1590
+ }
1591
+ #record(event) {
1592
+ this.#last = {
1593
+ event,
1594
+ at: Date.now()
1595
+ };
1596
+ }
1597
+ };
1598
+ function eventTarget(value) {
1599
+ if (typeof value !== "object" || value === null || !("addEventListener" in value) || typeof value.addEventListener !== "function" || !("removeEventListener" in value) || typeof value.removeEventListener !== "function") return;
1600
+ return value;
1601
+ }
1602
+ let fallbackConnectionId = 0;
1603
+ function makeConnectionId() {
1604
+ try {
1605
+ return `convex_${globalThis.crypto.randomUUID()}`;
1606
+ } catch {
1607
+ fallbackConnectionId += 1;
1608
+ return `convex_${Date.now().toString(36)}_${fallbackConnectionId.toString(36)}`;
1609
+ }
1610
+ }
1611
+ //#endregion
1422
1612
  //#region src/adapters/convex-call/ConvexCallAdapter.ts
1423
1613
  /** Exact floor-first allowlist; every additional handler must migrate its validator first. */
1424
1614
  const OBSERVED_CONVEX_ACTIONS = /* @__PURE__ */ new Set([
1615
+ "account/actions:readBalance",
1616
+ "holdings/actions:current",
1425
1617
  "smartAccount/actions:claim",
1426
1618
  "org/actions:prepareFounderAccount",
1427
1619
  "org/actions:prepareBootstrap",
1428
1620
  "org/actions:submitBootstrap",
1429
1621
  "org/actions:resumeBootstrapSubmission",
1430
- "org/actions:confirmBootstrap"
1622
+ "org/actions:confirmBootstrap",
1623
+ "org/actions:readTreasury"
1431
1624
  ]);
1432
1625
  const OBSERVED_CONVEX_QUERIES = /* @__PURE__ */ new Set([
1433
1626
  "identity/queries:loadByAuthUserId",
@@ -1448,8 +1641,11 @@ var ConvexCallAdapter = class {
1448
1641
  #tokenProvider;
1449
1642
  #applicationId;
1450
1643
  #observation;
1644
+ #connectionMonitor;
1451
1645
  constructor(deps) {
1452
- this.#client = deps.client ?? new ConvexClient(deps.convexUrl);
1646
+ this.#connectionMonitor = new ConvexConnectionMonitor();
1647
+ const webSocketConstructor = this.#connectionMonitor.observedWebSocketConstructor();
1648
+ this.#client = deps.client ?? new ConvexClient(deps.convexUrl, webSocketConstructor === void 0 ? {} : { webSocketConstructor });
1453
1649
  this.#tokenProvider = deps.tokenProvider;
1454
1650
  this.#applicationId = deps.applicationId;
1455
1651
  this.#observation = deps.observation;
@@ -1458,6 +1654,9 @@ var ConvexCallAdapter = class {
1458
1654
  refreshAuth() {
1459
1655
  if (this.#tokenProvider) this.#client.setAuth(this.#tokenProvider);
1460
1656
  }
1657
+ observeConnection(observer) {
1658
+ return this.#connectionMonitor.observe(this.#client, observer);
1659
+ }
1461
1660
  query(fn, args) {
1462
1661
  const path = getFunctionName(fn);
1463
1662
  return Effect.serviceOption(Tracer.ParentSpan).pipe(Effect.flatMap((parent) => {
@@ -1482,10 +1681,14 @@ var ConvexCallAdapter = class {
1482
1681
  const path = getFunctionName(fn);
1483
1682
  return Effect.serviceOption(Tracer.ParentSpan).pipe(Effect.flatMap((parent) => {
1484
1683
  const traceparent = parent._tag === "Some" ? formatTraceparent(parent.value) : void 0;
1684
+ const connectionAtStart = connectionState(this.#client);
1485
1685
  return Effect.tryPromise({
1486
1686
  try: () => this.#client.action(fn, this.#observedArgs(OBSERVED_CONVEX_ACTIONS, path, args, traceparent)),
1487
- catch: (cause) => mapToConvexCallError(path, cause)
1488
- });
1687
+ catch: (cause) => {
1688
+ const transport = this.#connectionMonitor.reconnectEvidence(connectionAtStart, connectionState(this.#client));
1689
+ return mapToConvexCallError(path, cause, transport);
1690
+ }
1691
+ }).pipe(Effect.tapError((error) => logActionTransportFailure(path, error)));
1489
1692
  }));
1490
1693
  }
1491
1694
  #observedArgs(allowlist, path, args, traceparent) {
@@ -1539,24 +1742,52 @@ var ConvexCallAdapter = class {
1539
1742
  })));
1540
1743
  }
1541
1744
  async close() {
1745
+ this.#connectionMonitor.close();
1542
1746
  await this.#client.close();
1543
1747
  }
1544
1748
  };
1545
1749
  function ConvexCallLayer(deps) {
1546
1750
  return Layer.effect(ConvexCallPortTag, Effect.acquireRelease(Effect.sync(() => new ConvexCallAdapter(deps)), (adapter) => Effect.promise(() => adapter.close()).pipe(Effect.orDie)));
1547
1751
  }
1548
- function mapToCapxulError(operation, err) {
1752
+ function mapToCapxulError(operation, err, transport) {
1549
1753
  const decoded = decodeConvexError(err);
1550
1754
  if (decoded !== null) return decoded;
1551
1755
  if (err instanceof CapxulError) return err;
1756
+ if (transport !== void 0) {
1757
+ const { kind: _kind, start_connection_count: _start, ...connection } = transport;
1758
+ return Errors.networkError(operation, err, {
1759
+ provider: "convex",
1760
+ failure_mode: "upstream-down",
1761
+ reason: "connection-lost-in-flight",
1762
+ ...connection
1763
+ });
1764
+ }
1552
1765
  if (err instanceof Error) {
1553
- if (isTransportError(err)) return Errors.networkError(operation, err);
1766
+ if (isTransportError(err)) return Errors.networkError(operation, err, { provider: "convex" });
1554
1767
  return Errors.providerError("convex", operation, err);
1555
1768
  }
1556
1769
  return Errors.providerError("convex", operation, new Error(String(err)));
1557
1770
  }
1558
- function mapToConvexCallError(operation, err) {
1559
- return convexCallErrorFromCapxul(operation, mapToCapxulError(operation, err));
1771
+ function mapToConvexCallError(operation, err, transport) {
1772
+ return convexCallErrorFromCapxul(operation, mapToCapxulError(operation, err, transport), transport);
1773
+ }
1774
+ function connectionState(client) {
1775
+ try {
1776
+ const state = client.connectionState?.();
1777
+ return typeof state?.connectionCount === "number" ? state : void 0;
1778
+ } catch {
1779
+ return;
1780
+ }
1781
+ }
1782
+ function logActionTransportFailure(operation, error) {
1783
+ if (error.transport === void 0) return Effect.void;
1784
+ const { kind, ...connection } = error.transport;
1785
+ return Effect.logWarning("convex.action.connection_lost").pipe(Effect.annotateLogs({
1786
+ operation,
1787
+ failure_mode: "upstream-down",
1788
+ reason: kind,
1789
+ ...connection
1790
+ }), Effect.catchCause(() => Effect.void));
1560
1791
  }
1561
1792
  const TRANSPORT_ERROR_CODES = /* @__PURE__ */ new Set([
1562
1793
  "EAI_AGAIN",
@@ -1647,12 +1878,14 @@ const DEFAULT_FUNCTIONS$3 = {
1647
1878
  var ConvexAccountAdapter = class {
1648
1879
  #convex;
1649
1880
  #fns;
1881
+ #telemetry;
1650
1882
  constructor(deps) {
1651
1883
  this.#convex = deps.convex;
1652
1884
  this.#fns = deps.functions ?? DEFAULT_FUNCTIONS$3;
1885
+ this.#telemetry = deps.telemetry;
1653
1886
  }
1654
1887
  readBalance(input) {
1655
- return this.#convex.action(this.#fns.readBalance, { chainId: wireChainId(input.chainId) }).pipe(Effect.mapError((error) => accountReadErrorFromCapxul("readBalance", error.publicError, error)), Effect.flatMap((wire) => brandAccountEffect("readBalance", wire)), Effect.catchDefect((cause) => Effect.fail(accountReadErrorFromUnknown("readBalance", cause))));
1888
+ return retryIdempotentRead(this.#convex.action(this.#fns.readBalance, copyInvocationObservation(input, { chainId: wireChainId(input.chainId) })), "accounts.read", this.#telemetry, input).pipe(Effect.mapError((error) => accountReadErrorFromCapxul("readBalance", error.publicError, error)), Effect.flatMap((wire) => brandAccountEffect("readBalance", wire)), Effect.catchDefect((cause) => Effect.fail(accountReadErrorFromUnknown("readBalance", cause))));
1656
1889
  }
1657
1890
  fundFromFaucet(input) {
1658
1891
  const operation = "fundFromFaucet";
@@ -1666,7 +1899,10 @@ var ConvexAccountAdapter = class {
1666
1899
  }
1667
1900
  };
1668
1901
  function ConvexAccountLayer() {
1669
- return Layer.effect(AccountReadPortTag, Effect.map(ConvexCallPortTag, (convex) => new ConvexAccountAdapter({ convex })));
1902
+ return Layer.effect(AccountReadPortTag, Effect.all([ConvexCallPortTag, TelemetryPortTag]).pipe(Effect.map(([convex, telemetry]) => new ConvexAccountAdapter({
1903
+ convex,
1904
+ telemetry
1905
+ }))));
1670
1906
  }
1671
1907
  function brandAccountEffect(operation, wire) {
1672
1908
  return Effect.try({
@@ -1901,15 +2137,17 @@ const DEFAULT_FUNCTIONS$1 = {
1901
2137
  var ConvexOrganizationAdapter = class {
1902
2138
  #convex;
1903
2139
  #fns;
2140
+ #telemetry;
1904
2141
  constructor(input) {
1905
2142
  this.#convex = input.convex;
1906
2143
  this.#fns = input.functions ?? DEFAULT_FUNCTIONS$1;
2144
+ this.#telemetry = input.telemetry;
1907
2145
  }
1908
2146
  createOrg(_input) {
1909
2147
  return Effect.fail(orgErrorFromCapxul("createOrg", Errors.notImplemented("organizationSetup", "use onboarding.completeOrganization")));
1910
2148
  }
1911
- listOrgs(_input) {
1912
- return this.#convex.query(this.#fns.listAll, {}).pipe(Effect.mapError((error) => orgErrorFromCapxul("listOrgs", error.publicError, error)), Effect.flatMap((wires) => Effect.forEach(wires, (wire) => this.#readTreasuryWire(wire.orgId).pipe(Effect.map((treasury) => {
2149
+ listOrgs(input) {
2150
+ return this.#convex.query(this.#fns.listAll, copyInvocationObservation(input, {})).pipe(Effect.mapError((error) => orgErrorFromCapxul("listOrgs", error.publicError, error)), Effect.flatMap((wires) => Effect.forEach(wires, (wire) => this.#readTreasuryWire(copyInvocationObservation(input, { orgId: toOrgId(wire.orgId) })).pipe(Effect.map((treasury) => {
1913
2151
  const viewerRole = wire.viewerRole.trim();
1914
2152
  if (viewerRole.length === 0) throw Errors.wrongState({
1915
2153
  method: "listOrgs",
@@ -1920,7 +2158,7 @@ var ConvexOrganizationAdapter = class {
1920
2158
  })))), Effect.catchDefect((cause) => Effect.fail(toOrgError("listOrgs", cause))));
1921
2159
  }
1922
2160
  readTreasury(input) {
1923
- return this.#readTreasuryWire(String(input.orgId)).pipe(Effect.catchDefect((cause) => Effect.fail(toOrgError("readTreasury", cause))));
2161
+ return this.#readTreasuryWire(input).pipe(Effect.catchDefect((cause) => Effect.fail(toOrgError("readTreasury", cause))));
1924
2162
  }
1925
2163
  listRoles(input) {
1926
2164
  return this.#convex.query(this.#fns.listRoles, { orgId: input.orgId }).pipe(Effect.mapError((error) => orgErrorFromCapxul("listRoles", error.publicError, error)), Effect.flatMap((rows) => rows.length === 0 ? Effect.fail(partialOrgTruth("listRoles", "activeRoleMissing")) : Effect.succeed(rows.map(brandOrgRole))), Effect.catchDefect((cause) => Effect.fail(toOrgError("listRoles", cause))));
@@ -1947,8 +2185,9 @@ var ConvexOrganizationAdapter = class {
1947
2185
  detectAndAcceptPendingInvitations(_input) {
1948
2186
  return this.#convex.action(this.#fns.detectInvitations, {}).pipe(Effect.mapError((error) => orgErrorFromCapxul("detectAndAcceptPendingInvitations", error.publicError, error)), Effect.map((result) => ({ matched: result.matched.map(toOrgId) })), Effect.catchDefect((cause) => Effect.fail(toOrgError("detectAndAcceptPendingInvitations", cause))));
1949
2187
  }
1950
- #readTreasuryWire(orgId) {
1951
- return this.#convex.action(this.#fns.readTreasury, { orgId }).pipe(Effect.mapError((error) => orgErrorFromCapxul("readTreasury", error.publicError, error)), Effect.map((wire) => {
2188
+ #readTreasuryWire(input) {
2189
+ const orgId = String(input.orgId);
2190
+ return retryIdempotentRead(this.#convex.action(this.#fns.readTreasury, copyInvocationObservation(input, { orgId })), "org.treasury", this.#telemetry, input).pipe(Effect.mapError((error) => orgErrorFromCapxul("readTreasury", error.publicError, error)), Effect.map((wire) => {
1952
2191
  if (wire.orgId !== orgId) throw Errors.invalidInput("orgId", "Organization treasury scope does not match");
1953
2192
  const balance = fromWei(wire.rawBalance, wire.decimals, wire.currency);
1954
2193
  const available = fromWei(wire.rawAvailableBalance, wire.decimals, wire.currency);
@@ -2239,6 +2478,90 @@ function fail(error) {
2239
2478
  };
2240
2479
  }
2241
2480
  //#endregion
2481
+ //#region src/adapters/telemetry/PostHogTelemetryAdapter.ts
2482
+ var PostHogTelemetryAdapter = class {
2483
+ #capture;
2484
+ #identify;
2485
+ #group;
2486
+ #reset;
2487
+ constructor(deps) {
2488
+ this.#capture = deps.capture;
2489
+ this.#identify = deps.identify ?? (() => void 0);
2490
+ this.#group = deps.group ?? (() => void 0);
2491
+ this.#reset = deps.reset ?? (() => void 0);
2492
+ }
2493
+ emit(event) {
2494
+ return this.#run(() => this.#capture(event.name, redactTelemetryProps(event.name, cloneProps(event.props)), event), "emit", event.name);
2495
+ }
2496
+ identify(input) {
2497
+ return this.#run(() => this.#identify(cloneIdentifyInput(input)), "identify");
2498
+ }
2499
+ group(input) {
2500
+ return this.#run(() => this.#group(cloneGroupInput(input)), "group");
2501
+ }
2502
+ reset() {
2503
+ return this.#run(() => this.#reset(), "reset");
2504
+ }
2505
+ #run(operation, operationName, eventName) {
2506
+ const diagnose = Effect.logWarning("product.telemetry.transport.dropped").pipe(Effect.annotateLogs({
2507
+ operation: operationName,
2508
+ ...eventName === void 0 ? {} : { product_event: eventName }
2509
+ }), Effect.catchCause(() => Effect.void));
2510
+ return Effect.suspend(() => {
2511
+ let pending;
2512
+ try {
2513
+ pending = operation();
2514
+ } catch {
2515
+ return diagnose;
2516
+ }
2517
+ if (pending === void 0) return Effect.void;
2518
+ const transport = Effect.tryPromise({
2519
+ try: () => pending,
2520
+ catch: () => void 0
2521
+ }).pipe(Effect.catch(() => diagnose));
2522
+ return Effect.forkDetach(transport, { startImmediately: true }).pipe(Effect.asVoid);
2523
+ });
2524
+ }
2525
+ };
2526
+ function PostHogTelemetryLayer(deps) {
2527
+ return Layer.succeed(TelemetryPortTag, new PostHogTelemetryAdapter(deps));
2528
+ }
2529
+ function cloneIdentifyInput(input) {
2530
+ const traits = input.traits === void 0 ? void 0 : cloneProps(input.traits);
2531
+ const properties = input.properties === void 0 ? void 0 : cloneProps(input.properties);
2532
+ return {
2533
+ distinctId: input.distinctId,
2534
+ ...input.anonDistinctId === void 0 ? {} : { anonDistinctId: input.anonDistinctId },
2535
+ ...traits === void 0 ? {} : { traits },
2536
+ ...properties === void 0 ? {} : { properties }
2537
+ };
2538
+ }
2539
+ function cloneGroupInput(input) {
2540
+ const properties = input.properties === void 0 ? void 0 : cloneProps(input.properties);
2541
+ return properties === void 0 ? {
2542
+ groupType: input.groupType,
2543
+ groupKey: input.groupKey
2544
+ } : {
2545
+ groupType: input.groupType,
2546
+ groupKey: input.groupKey,
2547
+ properties
2548
+ };
2549
+ }
2550
+ function cloneProps(props) {
2551
+ if (props === void 0) return void 0;
2552
+ const cloned = {};
2553
+ for (const [key, value] of Object.entries(props)) cloned[key] = cloneTelemetryValue(value);
2554
+ return cloned;
2555
+ }
2556
+ function cloneTelemetryValue(value) {
2557
+ if (Array.isArray(value)) return value.map(cloneTelemetryValue);
2558
+ if (value === null || typeof value !== "object") return value;
2559
+ if (Object.getPrototypeOf(value) !== Object.prototype) return value;
2560
+ const cloned = {};
2561
+ for (const [key, nested] of Object.entries(value)) cloned[key] = cloneTelemetryValue(nested);
2562
+ return cloned;
2563
+ }
2564
+ //#endregion
2242
2565
  //#region src/adapters/diagnostic/ConsoleDiagnosticAdapter.ts
2243
2566
  const DEFAULT_ACCOUNT_SETUP_LOG_PREFIX = "[capxul:account-setup]";
2244
2567
  var ConsoleDiagnosticAdapter = class {
@@ -2564,8 +2887,59 @@ function createOpenfortBrowserSignerFromBootstrap(bootstrap, options = {}) {
2564
2887
  embeddedWallet: openfort.embeddedWallet,
2565
2888
  ensureWalletReady: ensureOpenfortWalletReady
2566
2889
  });
2890
+ const resetReadiness = () => {
2891
+ walletReadyPromise = null;
2892
+ clearStaleOpenfortBrowserStorage(bootstrap.openfortPublishableKey);
2893
+ signer.resetAddressCache();
2894
+ setStatus("unknown");
2895
+ };
2896
+ const signUserOpHash = async (hash) => {
2897
+ try {
2898
+ return await signer.signUserOpHash(hash);
2899
+ } catch (cause) {
2900
+ const providerErrorCode = openfortSignerNotReadyCode(cause);
2901
+ if (providerErrorCode === void 0) throw cause;
2902
+ diagnostic?.trace("openfort.signerRecovery", {
2903
+ outcome: "started",
2904
+ attempt: 1,
2905
+ failure_mode: "signer-not-ready"
2906
+ });
2907
+ resetReadiness();
2908
+ try {
2909
+ await ensureOpenfortWalletReady();
2910
+ const signature = await signer.signUserOpHash(hash);
2911
+ diagnostic?.trace("openfort.signerRecovery", {
2912
+ outcome: "succeeded",
2913
+ attempt: 1,
2914
+ failure_mode: "signer-not-ready"
2915
+ });
2916
+ return signature;
2917
+ } catch (retryCause) {
2918
+ const retryFailure = signerFailure("openfort-embedded", "signUserOpHash", retryCause);
2919
+ if (retryFailure.code === "SIGNER_REJECTED") throw retryFailure;
2920
+ if (isSignerNotReadySignal(retryCause)) {
2921
+ walletReadyPromise = null;
2922
+ signer.resetAddressCache();
2923
+ setStatus("unavailable");
2924
+ }
2925
+ diagnostic?.trace("openfort.signerRecovery", {
2926
+ outcome: "failed",
2927
+ attempt: 1,
2928
+ failure_mode: isSignerNotReadySignal(retryCause) ? "signer-not-ready" : "unknown"
2929
+ });
2930
+ throw Errors.providerError("openfort", "signerRecovery", retryCause, {
2931
+ failure_mode: "signer-not-ready",
2932
+ details: {
2933
+ provider_error_source: "openfort",
2934
+ provider_error_code: providerErrorCode
2935
+ }
2936
+ });
2937
+ }
2938
+ }
2939
+ };
2567
2940
  return {
2568
2941
  ...signer,
2942
+ signUserOpHash,
2569
2943
  statusStore,
2570
2944
  ensureWalletReady: ensureOpenfortWalletReady,
2571
2945
  getAddress: async () => {
@@ -2581,12 +2955,7 @@ function createOpenfortBrowserSignerFromBootstrap(bootstrap, options = {}) {
2581
2955
  throw openfortProviderError("getAddress", cause);
2582
2956
  }
2583
2957
  },
2584
- resetSession: () => {
2585
- walletReadyPromise = null;
2586
- clearStaleOpenfortBrowserStorage(bootstrap.openfortPublishableKey);
2587
- signer.resetAddressCache();
2588
- setStatus("unknown");
2589
- }
2958
+ resetSession: resetReadiness
2590
2959
  };
2591
2960
  }
2592
2961
  function normalizeBetterAuthBaseUrl(raw) {
@@ -2594,6 +2963,73 @@ function normalizeBetterAuthBaseUrl(raw) {
2594
2963
  return trimmed.endsWith("/api/auth") ? trimmed : `${trimmed}/api/auth`;
2595
2964
  }
2596
2965
  //#endregion
2966
+ //#region src/telemetry/from-posthog.ts
2967
+ /**
2968
+ * Drop props that must never cross to a host-owned external sink. Today that is
2969
+ * the `$exception` `details` blob: `captureException` serializes
2970
+ * `CapxulError.details` (e.g. `{ asset, available, required }`, `{ name }`,
2971
+ * `{ accountId }` — errors.ts) into it, and the shared redactor has no
2972
+ * `$exception` rule, so it is stripped here at the boundary (infra#1037). The
2973
+ * safe fields (error code, operation, failure_mode, the fixed leak-safe message,
2974
+ * stack frames) are preserved.
2975
+ */
2976
+ function stripHostUnsafeProps(props) {
2977
+ if (props === void 0) return void 0;
2978
+ const { details: _details, ...safe } = props;
2979
+ return safe;
2980
+ }
2981
+ /**
2982
+ * The subset of a posthog-js client the seam calls. `identify` / `group` /
2983
+ * `reset` are optional — a host that only wants event capture can omit them.
2984
+ */
2985
+ /**
2986
+ * Adapt the host's already-initialized posthog-like client into a
2987
+ * `TelemetryPort` for the `telemetry` prop / input. This port SUPPLANTS the
2988
+ * SDK's no-op default telemetry sink (`production.ts` binds it via
2989
+ * `Layer.succeed`, not `compose` — there is no client-side success relay to
2990
+ * compose with); it is additive to Capxul's backend first-party record and
2991
+ * never owns the client.
2992
+ */
2993
+ function postHogProductTelemetry(policy, fixedSnapshot) {
2994
+ const telemetry = new PostHogTelemetryAdapter({
2995
+ capture: (name, props, source) => {
2996
+ const snapshot = readInvocationObservation(source) ?? fixedSnapshot ?? policy.snapshot();
2997
+ if (!snapshot.active || policy.client === null || policy.client === void 0) return;
2998
+ const event = stampTelemetryEnvelope({
2999
+ name,
3000
+ props
3001
+ }, {
3002
+ capxul_env: policy.capxulEnv,
3003
+ producer: "sdk"
3004
+ });
3005
+ policy.deliver(() => policy.client.capture(name, {
3006
+ ...stripHostUnsafeProps(event.props),
3007
+ ...observationContextProps(snapshot.context)
3008
+ }));
3009
+ },
3010
+ identify: (input) => {
3011
+ if (!(fixedSnapshot ?? policy.snapshot()).active || policy.client?.identify === void 0) return;
3012
+ policy.deliver(() => policy.client.identify(input.distinctId, {
3013
+ ...input.traits,
3014
+ ...input.properties
3015
+ }));
3016
+ },
3017
+ group: (input) => {
3018
+ if (!(fixedSnapshot ?? policy.snapshot()).active || policy.client?.group === void 0) return;
3019
+ policy.deliver(() => policy.client.group(input.groupType, input.groupKey, input.properties === void 0 ? void 0 : { ...input.properties }));
3020
+ },
3021
+ reset: () => {
3022
+ if (!(fixedSnapshot ?? policy.snapshot()).active || policy.client?.reset === void 0) return;
3023
+ policy.deliver(() => policy.client.reset());
3024
+ }
3025
+ });
3026
+ Object.defineProperty(telemetry, PRODUCT_INVOCATION, {
3027
+ enumerable: false,
3028
+ value: (source) => postHogProductTelemetry(policy, readInvocationObservation(source) ?? fixedSnapshot ?? policy.snapshot())
3029
+ });
3030
+ return telemetry;
3031
+ }
3032
+ //#endregion
2597
3033
  //#region src/host-observability.ts
2598
3034
  const HOST_INVOCATION = Symbol("capxul.host-observability-invocation");
2599
3035
  /** Build the SDK's one host module around an already-initialized PostHog client. */
@@ -2732,7 +3168,10 @@ function makeProductionAdapterLayerEntries(input) {
2732
3168
  },
2733
3169
  {
2734
3170
  name: "org",
2735
- layer: Layer.effect(OrgPortTag, Effect.map(ConvexCallPortTag, (convex) => new ConvexOrganizationAdapter({ convex })))
3171
+ layer: Layer.effect(OrgPortTag, Effect.all([ConvexCallPortTag, TelemetryPortTag]).pipe(Effect.map(([convex, telemetry]) => new ConvexOrganizationAdapter({
3172
+ convex,
3173
+ telemetry
3174
+ }))))
2736
3175
  }
2737
3176
  ];
2738
3177
  }
@@ -2741,10 +3180,13 @@ function mergeProductionAdapterLayers(entries) {
2741
3180
  for (const entry of entries) byName.set(entry.name, entry.layer);
2742
3181
  const authClientLayer = byName.get("authClient");
2743
3182
  const convexCallLayer = byName.get("convexCall");
3183
+ const telemetryLayer = byName.get("telemetry");
2744
3184
  const convexCallReadyLayer = convexCallLayer === void 0 || authClientLayer === void 0 ? convexCallLayer : convexCallLayer.pipe(Layer.provide(authClientLayer));
3185
+ const observedConvexCallLayer = convexCallReadyLayer === void 0 ? telemetryLayer : telemetryLayer === void 0 ? convexCallReadyLayer : Layer.merge(convexCallReadyLayer, telemetryLayer);
2745
3186
  return entries.map((entry) => {
2746
3187
  const layer = byName.get(entry.name) ?? entry.layer;
2747
3188
  if (entry.name === "convexCall") return convexCallReadyLayer ?? layer;
3189
+ if (entry.name === "accountRead" || entry.name === "org") return observedConvexCallLayer === void 0 ? layer : layer.pipe(Layer.provide(observedConvexCallLayer));
2748
3190
  if (isConvexDependentLayer(entry.name)) return convexCallReadyLayer === void 0 ? layer : layer.pipe(Layer.provide(convexCallReadyLayer));
2749
3191
  return layer;
2750
3192
  }).reduce((current, layer) => Layer.merge(current, layer), Layer.empty);
@@ -2905,13 +3347,22 @@ async function createProductionAdapters(input) {
2905
3347
  }));
2906
3348
  const applicationLayer = Layer.merge(portsLayer, optionalEngineeringTelemetryLayer(bootstrapResult.value.engineeringTelemetry, resolvedInput.value.runtime));
2907
3349
  const context = await Effect.runPromise(Layer.buildWithScope(applicationLayer, scope));
3350
+ const ports = await Effect.runPromise(collectProductionFlowPorts.pipe(Effect.provide(context)));
3351
+ const stopConnectionObservation = observeProductionConvexConnection(ports.convexCall, context);
3352
+ const close = idempotentClose(async () => {
3353
+ try {
3354
+ stopConnectionObservation();
3355
+ } finally {
3356
+ await closeScope();
3357
+ }
3358
+ });
2908
3359
  return {
2909
3360
  ok: true,
2910
3361
  value: {
2911
3362
  context,
2912
- ports: await Effect.runPromise(collectProductionFlowPorts.pipe(Effect.provide(context))),
3363
+ ports,
2913
3364
  bootstrap: bootstrapResult.value,
2914
- close: closeScope
3365
+ close
2915
3366
  }
2916
3367
  };
2917
3368
  } catch (cause) {
@@ -2922,6 +3373,18 @@ async function createProductionAdapters(input) {
2922
3373
  };
2923
3374
  }
2924
3375
  }
3376
+ function observeProductionConvexConnection(convexCall, context) {
3377
+ if (!("observeConnection" in convexCall) || typeof convexCall.observeConnection !== "function") return () => {};
3378
+ const runPromise = Effect.runPromiseWith(context);
3379
+ try {
3380
+ return convexCall.observeConnection((diagnostic) => {
3381
+ const message = diagnostic.transition === "connected" ? Effect.logInfo("convex.connection.changed") : Effect.logWarning("convex.connection.changed");
3382
+ runPromise(message.pipe(Effect.annotateLogs(diagnostic), Effect.catchCause(() => Effect.void))).catch(() => void 0);
3383
+ });
3384
+ } catch {
3385
+ return () => {};
3386
+ }
3387
+ }
2925
3388
  function optionalEngineeringTelemetryLayer(policy, runtime) {
2926
3389
  if (policy === void 0) return Layer.empty;
2927
3390
  try {
@@ -3204,4 +3667,155 @@ async function createCapxulClient(input) {
3204
3667
  return createCapxulClient$1(input);
3205
3668
  }
3206
3669
  //#endregion
3670
+ //#region src/telemetry/stack-frame-parser.ts
3671
+ /**
3672
+ * Regex for V8/Chrome stack trace frame lines.
3673
+ * Matches:
3674
+ * `at functionName (url:line:col)`
3675
+ * `at url:line:col`
3676
+ * `at async functionName (url:line:col)`
3677
+ * `at new ClassName (url:line:col)`
3678
+ */
3679
+ const V8_FRAME_RE = /^\s*at\s+(?:(.+?)\s+\()?(?:(.+?)(?::(\d+):(\d+))?|(.+?))\)?\s*$/;
3680
+ function isNonUrlName(name) {
3681
+ return name === "<anonymous>" || name.startsWith("eval") || name.startsWith("new ") || name.startsWith("async ");
3682
+ }
3683
+ /**
3684
+ * Parse a V8/Chrome-style stack trace string into PostHog `ExceptionFrame` objects.
3685
+ * Returns an empty array when `error.stack` is absent or empty.
3686
+ *
3687
+ * Handles:
3688
+ * - Standard `at functionName (url:line:col)`
3689
+ * - Bare `at url:line:col` (no function name)
3690
+ * - `at async functionName (url:line:col)`
3691
+ * - `at new ClassName (url:line:col)`
3692
+ * - Native frames: `at Array.forEach (<anonymous>)`
3693
+ */
3694
+ function parseV8StackFrames(error) {
3695
+ const stack = error.stack;
3696
+ if (stack === void 0 || stack === null || stack === "") return [];
3697
+ const lines = stack.split("\n");
3698
+ const frames = [];
3699
+ for (const line of lines) {
3700
+ const trimmed = line.trim();
3701
+ if (!trimmed.startsWith("at ")) continue;
3702
+ const match = V8_FRAME_RE.exec(trimmed);
3703
+ if (match === null) continue;
3704
+ if (match[1] !== void 0) frames.push({
3705
+ function: match[1],
3706
+ filename: match[2],
3707
+ lineno: match[3] !== void 0 ? Number(match[3]) : null,
3708
+ colno: match[4] !== void 0 ? Number(match[4]) : null
3709
+ });
3710
+ else if (match[2] !== void 0 && !isNonUrlName(match[2])) frames.push({
3711
+ function: "<anonymous>",
3712
+ filename: match[2],
3713
+ lineno: match[3] !== void 0 ? Number(match[3]) : null,
3714
+ colno: match[4] !== void 0 ? Number(match[4]) : null
3715
+ });
3716
+ else frames.push({
3717
+ function: match[1] ?? match[2] ?? match[5] ?? "<anonymous>",
3718
+ filename: match[2] ?? match[5] ?? "<anonymous>",
3719
+ lineno: match[3] !== void 0 ? Number(match[3]) : null,
3720
+ colno: match[4] !== void 0 ? Number(match[4]) : null
3721
+ });
3722
+ }
3723
+ return frames;
3724
+ }
3725
+ /** Fixed, leak-safe frame used when the error carries no parseable stack. */
3726
+ const SDK_BOUNDARY_FILENAME = "capxul-sdk-observation://boundary";
3727
+ /**
3728
+ * Build PostHog's `$exception_list` (always a single entry). Error Tracking
3729
+ * groups on `type`, so it is ALWAYS present (the CapxulError code) — the
3730
+ * previous `[{ frames }]` shape omitted it and PostHog dropped the event as
3731
+ * "missing field `type`". When the error has no parseable stack, a synthetic
3732
+ * boundary frame stands in so the event still ingests as a real Issue (#1031).
3733
+ */
3734
+ function buildExceptionList(input) {
3735
+ const frames = input.frames.length > 0 ? input.frames : [{
3736
+ filename: SDK_BOUNDARY_FILENAME,
3737
+ function: input.operation ?? "unknown",
3738
+ lineno: 1,
3739
+ colno: 1
3740
+ }];
3741
+ return [{
3742
+ type: input.type,
3743
+ value: input.value,
3744
+ mechanism: {
3745
+ handled: true,
3746
+ type: "capxul_sdk_boundary"
3747
+ },
3748
+ stacktrace: { frames }
3749
+ }];
3750
+ }
3751
+ //#endregion
3752
+ //#region src/telemetry/capture-exception.ts
3753
+ /**
3754
+ * Capture an error as a `$exception` event through the telemetry port,
3755
+ * formatted for PostHog Error Tracking.
3756
+ *
3757
+ * Parses stack traces into `$exception_list` format, extracts structured
3758
+ * metadata from CapxulError objects, and supplements with context props.
3759
+ * Fire-and-forget: telemetry defects are silently swallowed.
3760
+ * Returns `Effect<void, never>` for use in Effect pipelines; the underlying
3761
+ * adapter work is synchronous, so callers outside Effect contexts can
3762
+ * use `Effect.runSync`.
3763
+ */
3764
+ function captureException(telemetry, error, context) {
3765
+ return Effect.catchDefect(Effect.sync(() => {
3766
+ const frames = error instanceof Error ? parseV8StackFrames(error) : [];
3767
+ const capxulError = isCapxulError(error) ? error : null;
3768
+ const errorCode = normalizeExceptionErrorKind(capxulError?.code ?? context?.capxul_error_code);
3769
+ const { chain_provider_message: _providerMessage, chain_revert_args: _revertArguments, ...safeDetail } = failureDetail(error) ?? {};
3770
+ const operation = normalizeExceptionOperation(context?.operation ?? safeDetail.provider_operation);
3771
+ const failureMode = resolveFailureMode(error, context?.failure_mode);
3772
+ const detail = {
3773
+ ...safeDetail,
3774
+ failure_mode: failureMode
3775
+ };
3776
+ const properties = {
3777
+ capxul_error_code: errorCode,
3778
+ ...detail,
3779
+ error_message: EXCEPTION_MESSAGE,
3780
+ error_details: detail,
3781
+ error_cause: detail.reason ?? failureMode,
3782
+ layer: safeExceptionLabel(capxulError?.layer ?? context?.layer),
3783
+ provider: detail.provider ?? safeExceptionLabel(context?.provider)
3784
+ };
3785
+ for (const key of Object.keys(properties)) if (properties[key] === void 0) delete properties[key];
3786
+ const props = projectSdkException({
3787
+ properties,
3788
+ sdkVersion: SDK_VERSION$1,
3789
+ operation,
3790
+ errorKind: errorCode,
3791
+ failureMode,
3792
+ message: EXCEPTION_MESSAGE,
3793
+ issueDescription: detail.reason ?? failureMode,
3794
+ ...detail.chain_upstream === void 0 ? {} : { chainUpstream: detail.chain_upstream },
3795
+ ...detail.chain_method === void 0 ? {} : { chainMethod: detail.chain_method },
3796
+ exceptionList: buildExceptionList({
3797
+ type: errorCode,
3798
+ value: EXCEPTION_MESSAGE,
3799
+ operation,
3800
+ frames
3801
+ })
3802
+ });
3803
+ return telemetry.emit({
3804
+ name: "$exception",
3805
+ props
3806
+ });
3807
+ }).pipe(Effect.flatten), () => Effect.void);
3808
+ }
3809
+ /**
3810
+ * Synchronous fire-and-forget capture. Runs the Effect inline with
3811
+ * `Effect.runSync` so callers outside an Effect context can report errors
3812
+ * without awaiting. Core SDK public methods use the assembled observation
3813
+ * boundary instead.
3814
+ */
3815
+ function captureExceptionSync(telemetry, error, context) {
3816
+ try {
3817
+ Effect.runSync(captureException(telemetry, error, context));
3818
+ } catch {}
3819
+ }
3820
+ //#endregion
3207
3821
  export { CAPXUL_ERROR_CODES, CAPXUL_PAYMENTS_V2_ADDRESS, CAPXUL_SDK_EXPECTED_OUTCOME_EVENT, CHAIN_UPSTREAMS, CapxulError, Errors, FAILURE_MODES, PAYMENT_DIRECTIONS, PAYMENT_STATUSES, captureException, captureExceptionSync, createCapxulClient, deriveDevPrivateKey, devPrivateKeySigner, eip1193AccountProvider, embeddedSigner, fingerprintPaymentIntent, formatMoney, inboxPhase, injectedWalletSigner, isCapxulError, isClaimed, isMoneyParseError, isRestoring, isSettingUpLifecycle, localPrivateKeyAccountProvider, openfortEmbeddedSigner, openfortEmbeddedSignerFromWallet, openfortEmbeddedWalletPort, parseMoney, paymentPhase, postHogObservability, requestPhase, destination as resolveIdentityDestination, toCountryCode, toAddress as toEvmAddress };