@capxul/sdk 2.8.0 → 3.0.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/index.mjs CHANGED
@@ -1,5 +1,6 @@
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 EngineeringTelemetryBootstrapPolicy, A as normalizeExceptionErrorKind, B as isSignerNotReadySignal, C as failureDetail, D as postHogFailureObservation, E as observeFailedResult, F as fromWei, G as readInvocationObservation, H as signerFailure, I as isSettingUpLifecycle, J as sanitizeObservationContext, K as OBSERVATION_CONTEXT_HEADER, L as formatTraceparent, M as projectSdkException, N as safeExceptionLabel, O as EXCEPTION_MESSAGE, P as version, Q as BootstrapEnvelope, R as causeChain, S as CAPXUL_SDK_EXPECTED_OUTCOME_EVENT, T as observationContextProps, U as resolveFailureMode, V as openfortSignerNotReadyCode, W as copyInvocationObservation, X as PAYMENT_STATUSES, Y as PAYMENT_DIRECTIONS, Z as CAPXUL_FUNCTIONS, _ as convexCallErrorFromCapxul, a as AccountReadPortTag, b as fingerprintPaymentIntent, d as retryIdempotentRead, et as destination, f as PRODUCT_INVOCATION, g as ConvexCallPortTag, h as identityErrorFromCapxul, i as smartAccountErrorFromCapxul, j as normalizeExceptionOperation, k as SDK_VERSION$1, l as redactTelemetryProps, m as IdentityPortTag, n as detectAuthCacheAdapter, nt as isRestoring, o as accountReadErrorFromCapxul, p as wireChainId, q as encodeObservationContextHeader, r as SmartAccountPortTag, s as TelemetryPortTag, t as assembleCapxulClient, tt as isClaimed, u as stampTelemetryEnvelope, v as BootstrapPortTag, w as failureEvidenceProps, x as toWei, y as bootstrapErrorFromCapxul, z as injectedWalletSigner } from "./create-capxul-client-DwINzpXZ.mjs";
2
+ import { $ as CHAIN_UPSTREAMS, B as toOrgId, D as toAllowedOrigin, E as toAddress, F as toEmail, G as toRoleKey, I as toEpochMs, J as CAPXUL_ERROR_CODES, K as toSessionToken, L as toEpochSeconds, M as toCountryCode, N as toCurrencyCode, P as toDurationMs, Q as isCapxulError, R as toJwtToken, T as toAccountId, W as toPublishableKey, Y as CapxulError, Z as Errors, d as AuthClientError, et as FAILURE_MODES, f as AuthClientPortTag, g as BASE_SEPOLIA_CHAIN_ID, h as normalizeBindingEmail, j as toChainId, k as toAuthUserId, l as ClockPortTag, m as CAPXUL_PAYMENTS_V2_ADDRESS, q as decodeConvexError, s as AuthCachePortTag, u as authClientPortFromPromiseAdapter, w as currencySymbolFor, z as toKycTier } from "./clock-0c5NM-TY.mjs";
3
+ import { n as SystemClockLayer } from "./OAuthBearerAuthClient-BZ6tXt4m.mjs";
3
4
  import { keccak256, recoverAddress, stringToHex } from "viem";
4
5
  import { Cause, Context, Data, Duration, Effect, Exit, Layer, Result, Schedule, SchemaIssue, SchemaParser, Scope, Tracer } from "effect";
5
6
  import { getFunctionName, makeFunctionReference } from "convex/server";
@@ -1302,11 +1303,13 @@ async function safeText(res) {
1302
1303
  }
1303
1304
  var HttpBootstrapAdapter = class {
1304
1305
  bootstrapBaseUrl;
1306
+ bootstrapHost;
1305
1307
  fetchImpl;
1306
1308
  observation;
1307
1309
  retry;
1308
1310
  constructor(deps) {
1309
1311
  this.bootstrapBaseUrl = deps.bootstrapBaseUrl.replace(/\/$/, "");
1312
+ this.bootstrapHost = hostOf(this.bootstrapBaseUrl);
1310
1313
  this.observation = deps.observation;
1311
1314
  this.retry = deps.retry ?? DEFAULT_RETRY;
1312
1315
  this.fetchImpl = deps.fetch ?? ((input, init) => globalThis.fetch(input, init));
@@ -1332,7 +1335,11 @@ var HttpBootstrapAdapter = class {
1332
1335
  body: JSON.stringify({ publishableKey: input.publishableKey })
1333
1336
  });
1334
1337
  },
1335
- catch: (cause) => bootstrapErrorFromCapxul("network", Errors.networkError("bootstrap", cause))
1338
+ catch: (cause) => bootstrapErrorFromCapxul("network", Errors.networkError("bootstrap", cause, {
1339
+ provider: "bootstrap",
1340
+ failure_mode: "upstream-down",
1341
+ ...this.bootstrapHost === void 0 ? {} : { target_host: this.bootstrapHost }
1342
+ }))
1336
1343
  }).pipe(Effect.flatMap((res) => this.mapResponse(res)));
1337
1344
  }
1338
1345
  mapResponse(res) {
@@ -1388,6 +1395,13 @@ var HttpBootstrapAdapter = class {
1388
1395
  function HttpBootstrapLayer(deps) {
1389
1396
  return Layer.succeed(BootstrapPortTag, new HttpBootstrapAdapter(deps));
1390
1397
  }
1398
+ function hostOf(url) {
1399
+ try {
1400
+ return new URL(url).hostname;
1401
+ } catch {
1402
+ return;
1403
+ }
1404
+ }
1391
1405
  function normalizeRuntimeUrl(field, raw) {
1392
1406
  let parsed;
1393
1407
  try {
@@ -1399,35 +1413,195 @@ function normalizeRuntimeUrl(field, raw) {
1399
1413
  return parsed.toString().replace(/\/$/, "");
1400
1414
  }
1401
1415
  //#endregion
1402
- //#region src/adapters/clock/SystemClockAdapter.ts
1403
- var SystemClockAdapter = class {
1404
- now = Effect.try({
1405
- try: () => toEpochMs(Date.now()),
1406
- catch: (cause) => new ClockError({
1407
- operation: "now",
1408
- cause
1409
- })
1410
- });
1411
- sleep(duration) {
1412
- return Effect.callback((resume) => {
1413
- const timeout = setTimeout(() => resume(Effect.void), duration);
1414
- return Effect.sync(() => clearTimeout(timeout));
1416
+ //#region src/adapters/convex-call/convex-connection-monitor.ts
1417
+ var ConvexConnectionMonitor = class {
1418
+ #connectionId = makeConnectionId();
1419
+ #browser = new BrowserLifecycleMonitor();
1420
+ #lastClose;
1421
+ observedWebSocketConstructor() {
1422
+ const NativeWebSocket = globalThis.WebSocket;
1423
+ if (typeof globalThis.window === "undefined" || NativeWebSocket === void 0) return void 0;
1424
+ return new Proxy(NativeWebSocket, { construct: (Target, args) => {
1425
+ const socket = Reflect.construct(Target, args);
1426
+ socket.addEventListener("close", (event) => {
1427
+ this.#lastClose = this.#browser.socketClose(event);
1428
+ });
1429
+ return socket;
1430
+ } });
1431
+ }
1432
+ close() {
1433
+ this.#browser.close();
1434
+ }
1435
+ observe(client, observer) {
1436
+ const subscribe = client.subscribeToConnectionState?.bind(client);
1437
+ if (subscribe === void 0) return () => this.#browser.close();
1438
+ let previous;
1439
+ const unsubscribe = subscribe((next) => {
1440
+ const diagnostics = this.#diagnostics(previous, next);
1441
+ previous = next;
1442
+ for (const diagnostic of diagnostics) emitDiagnostic(observer, diagnostic);
1443
+ });
1444
+ if (previous === void 0) {
1445
+ const current = safeConnectionState(client);
1446
+ if (current !== void 0) {
1447
+ previous = current;
1448
+ for (const diagnostic of this.#diagnostics(void 0, current)) emitDiagnostic(observer, diagnostic);
1449
+ }
1450
+ }
1451
+ let closed = false;
1452
+ return () => {
1453
+ if (closed) return;
1454
+ closed = true;
1455
+ try {
1456
+ unsubscribe();
1457
+ } finally {
1458
+ this.#browser.close();
1459
+ }
1460
+ };
1461
+ }
1462
+ reconnectEvidence(start, end) {
1463
+ if (!connectionRestarted(start, end)) return void 0;
1464
+ return {
1465
+ kind: "connection-lost-in-flight",
1466
+ connection_id: this.#connectionId,
1467
+ start_connection_count: start.connectionCount,
1468
+ connection_count: end.connectionCount,
1469
+ ...end.connectionRetries === void 0 ? {} : { connection_retries: end.connectionRetries },
1470
+ ...closeFields(this.#lastClose)
1471
+ };
1472
+ }
1473
+ #diagnostics(previous, next) {
1474
+ const transitions = connectionTransitions(previous, next);
1475
+ if (transitions.length === 0) return [];
1476
+ const oldest = next.timeOfOldestInflightRequest;
1477
+ const oldestInflightMs = oldest instanceof Date && Number.isFinite(oldest.getTime()) ? Math.max(0, Date.now() - oldest.getTime()) : void 0;
1478
+ return transitions.map((transition) => ({
1479
+ connection_id: this.#connectionId,
1480
+ transition,
1481
+ previous_connected: previous?.isWebSocketConnected ?? false,
1482
+ connected: next.isWebSocketConnected,
1483
+ ...next.hasEverConnected === void 0 ? {} : { has_ever_connected: next.hasEverConnected },
1484
+ connection_count: next.connectionCount,
1485
+ ...next.connectionRetries === void 0 ? {} : { connection_retries: next.connectionRetries },
1486
+ ...next.hasInflightRequests === void 0 ? {} : { has_inflight_requests: next.hasInflightRequests },
1487
+ ...next.inflightActions === void 0 ? {} : { inflight_actions: next.inflightActions },
1488
+ ...next.inflightMutations === void 0 ? {} : { inflight_mutations: next.inflightMutations },
1489
+ ...oldestInflightMs === void 0 ? {} : { oldest_inflight_ms: oldestInflightMs },
1490
+ ...closeFields(this.#lastClose)
1491
+ }));
1492
+ }
1493
+ };
1494
+ function connectionRestarted(start, end) {
1495
+ if (start === void 0 || end === void 0) return false;
1496
+ const opensBeforeCancellation = start.isWebSocketConnected ? 1 : 2;
1497
+ return end.connectionCount >= start.connectionCount + opensBeforeCancellation;
1498
+ }
1499
+ function connectionTransitions(previous, next) {
1500
+ if (previous === void 0) {
1501
+ if (next.isWebSocketConnected) return (next.connectionRetries ?? 0) > 0 ? ["retrying", "connected"] : ["connected"];
1502
+ return (next.connectionRetries ?? 0) > 0 ? ["disconnected", "retrying"] : [];
1503
+ }
1504
+ const transitions = [];
1505
+ if (previous.isWebSocketConnected !== next.isWebSocketConnected) transitions.push(next.isWebSocketConnected ? "connected" : "disconnected");
1506
+ if ((next.connectionRetries ?? 0) > (previous.connectionRetries ?? 0)) transitions.push("retrying");
1507
+ return transitions;
1508
+ }
1509
+ function emitDiagnostic(observer, diagnostic) {
1510
+ try {
1511
+ observer(diagnostic);
1512
+ } catch {}
1513
+ }
1514
+ function safeConnectionState(client) {
1515
+ try {
1516
+ return client.connectionState?.();
1517
+ } catch {
1518
+ return;
1519
+ }
1520
+ }
1521
+ function closeFields(close) {
1522
+ if (close === void 0) return {};
1523
+ return {
1524
+ close_code: close.code,
1525
+ close_was_clean: close.wasClean,
1526
+ close_reason_present: close.reasonPresent,
1527
+ ...close.documentVisibility === void 0 ? {} : { document_visibility: close.documentVisibility },
1528
+ ...close.navigatorOnline === void 0 ? {} : { navigator_online: close.navigatorOnline },
1529
+ ...close.browserEvent === void 0 ? {} : { last_browser_event: close.browserEvent },
1530
+ ...close.msSinceBrowserEvent === void 0 ? {} : { ms_since_browser_event: close.msSinceBrowserEvent }
1531
+ };
1532
+ }
1533
+ var BrowserLifecycleMonitor = class {
1534
+ #listeners = [];
1535
+ #last = {};
1536
+ constructor() {
1537
+ const windowTarget = eventTarget(globalThis.window);
1538
+ const documentTarget = eventTarget(globalThis.document);
1539
+ if (windowTarget === void 0 || documentTarget === void 0) return;
1540
+ this.#listen(documentTarget, "visibilitychange", () => {
1541
+ this.#record(globalThis.document?.visibilityState === "hidden" ? "visibility-hidden" : "visibility-visible");
1415
1542
  });
1543
+ this.#listen(windowTarget, "pagehide", () => this.#record("pagehide"));
1544
+ this.#listen(windowTarget, "pageshow", () => this.#record("pageshow"));
1545
+ this.#listen(windowTarget, "online", () => this.#record("online"));
1546
+ this.#listen(windowTarget, "offline", () => this.#record("offline"));
1547
+ }
1548
+ socketClose(event) {
1549
+ const now = Date.now();
1550
+ return {
1551
+ code: event.code,
1552
+ wasClean: event.wasClean,
1553
+ reasonPresent: event.reason.length > 0,
1554
+ ...this.#last.event === void 0 ? {} : { browserEvent: this.#last.event },
1555
+ ...this.#last.at === void 0 ? {} : { msSinceBrowserEvent: Math.max(0, now - this.#last.at) },
1556
+ ...globalThis.document?.visibilityState === void 0 ? {} : { documentVisibility: globalThis.document.visibilityState },
1557
+ ...globalThis.navigator?.onLine === void 0 ? {} : { navigatorOnline: globalThis.navigator.onLine }
1558
+ };
1559
+ }
1560
+ close() {
1561
+ for (const [target, name, listener] of this.#listeners) target.removeEventListener(name, listener);
1562
+ this.#listeners.length = 0;
1563
+ }
1564
+ #listen(target, name, listener) {
1565
+ target.addEventListener(name, listener);
1566
+ this.#listeners.push([
1567
+ target,
1568
+ name,
1569
+ listener
1570
+ ]);
1571
+ }
1572
+ #record(event) {
1573
+ this.#last = {
1574
+ event,
1575
+ at: Date.now()
1576
+ };
1416
1577
  }
1417
1578
  };
1418
- function SystemClockLayer() {
1419
- return Layer.succeed(ClockPortTag, new SystemClockAdapter());
1579
+ function eventTarget(value) {
1580
+ if (typeof value !== "object" || value === null || !("addEventListener" in value) || typeof value.addEventListener !== "function" || !("removeEventListener" in value) || typeof value.removeEventListener !== "function") return;
1581
+ return value;
1582
+ }
1583
+ let fallbackConnectionId = 0;
1584
+ function makeConnectionId() {
1585
+ try {
1586
+ return `convex_${globalThis.crypto.randomUUID()}`;
1587
+ } catch {
1588
+ fallbackConnectionId += 1;
1589
+ return `convex_${Date.now().toString(36)}_${fallbackConnectionId.toString(36)}`;
1590
+ }
1420
1591
  }
1421
1592
  //#endregion
1422
1593
  //#region src/adapters/convex-call/ConvexCallAdapter.ts
1423
1594
  /** Exact floor-first allowlist; every additional handler must migrate its validator first. */
1424
1595
  const OBSERVED_CONVEX_ACTIONS = /* @__PURE__ */ new Set([
1596
+ "account/actions:readBalance",
1597
+ "holdings/actions:current",
1425
1598
  "smartAccount/actions:claim",
1426
1599
  "org/actions:prepareFounderAccount",
1427
1600
  "org/actions:prepareBootstrap",
1428
1601
  "org/actions:submitBootstrap",
1429
1602
  "org/actions:resumeBootstrapSubmission",
1430
- "org/actions:confirmBootstrap"
1603
+ "org/actions:confirmBootstrap",
1604
+ "org/actions:readTreasury"
1431
1605
  ]);
1432
1606
  const OBSERVED_CONVEX_QUERIES = /* @__PURE__ */ new Set([
1433
1607
  "identity/queries:loadByAuthUserId",
@@ -1448,8 +1622,11 @@ var ConvexCallAdapter = class {
1448
1622
  #tokenProvider;
1449
1623
  #applicationId;
1450
1624
  #observation;
1625
+ #connectionMonitor;
1451
1626
  constructor(deps) {
1452
- this.#client = deps.client ?? new ConvexClient(deps.convexUrl);
1627
+ this.#connectionMonitor = new ConvexConnectionMonitor();
1628
+ const webSocketConstructor = this.#connectionMonitor.observedWebSocketConstructor();
1629
+ this.#client = deps.client ?? new ConvexClient(deps.convexUrl, webSocketConstructor === void 0 ? {} : { webSocketConstructor });
1453
1630
  this.#tokenProvider = deps.tokenProvider;
1454
1631
  this.#applicationId = deps.applicationId;
1455
1632
  this.#observation = deps.observation;
@@ -1458,6 +1635,9 @@ var ConvexCallAdapter = class {
1458
1635
  refreshAuth() {
1459
1636
  if (this.#tokenProvider) this.#client.setAuth(this.#tokenProvider);
1460
1637
  }
1638
+ observeConnection(observer) {
1639
+ return this.#connectionMonitor.observe(this.#client, observer);
1640
+ }
1461
1641
  query(fn, args) {
1462
1642
  const path = getFunctionName(fn);
1463
1643
  return Effect.serviceOption(Tracer.ParentSpan).pipe(Effect.flatMap((parent) => {
@@ -1482,10 +1662,14 @@ var ConvexCallAdapter = class {
1482
1662
  const path = getFunctionName(fn);
1483
1663
  return Effect.serviceOption(Tracer.ParentSpan).pipe(Effect.flatMap((parent) => {
1484
1664
  const traceparent = parent._tag === "Some" ? formatTraceparent(parent.value) : void 0;
1665
+ const connectionAtStart = connectionState(this.#client);
1485
1666
  return Effect.tryPromise({
1486
1667
  try: () => this.#client.action(fn, this.#observedArgs(OBSERVED_CONVEX_ACTIONS, path, args, traceparent)),
1487
- catch: (cause) => mapToConvexCallError(path, cause)
1488
- });
1668
+ catch: (cause) => {
1669
+ const transport = this.#connectionMonitor.reconnectEvidence(connectionAtStart, connectionState(this.#client));
1670
+ return mapToConvexCallError(path, cause, transport);
1671
+ }
1672
+ }).pipe(Effect.tapError((error) => logActionTransportFailure(path, error)));
1489
1673
  }));
1490
1674
  }
1491
1675
  #observedArgs(allowlist, path, args, traceparent) {
@@ -1539,24 +1723,52 @@ var ConvexCallAdapter = class {
1539
1723
  })));
1540
1724
  }
1541
1725
  async close() {
1726
+ this.#connectionMonitor.close();
1542
1727
  await this.#client.close();
1543
1728
  }
1544
1729
  };
1545
1730
  function ConvexCallLayer(deps) {
1546
1731
  return Layer.effect(ConvexCallPortTag, Effect.acquireRelease(Effect.sync(() => new ConvexCallAdapter(deps)), (adapter) => Effect.promise(() => adapter.close()).pipe(Effect.orDie)));
1547
1732
  }
1548
- function mapToCapxulError(operation, err) {
1733
+ function mapToCapxulError(operation, err, transport) {
1549
1734
  const decoded = decodeConvexError(err);
1550
1735
  if (decoded !== null) return decoded;
1551
1736
  if (err instanceof CapxulError) return err;
1737
+ if (transport !== void 0) {
1738
+ const { kind: _kind, start_connection_count: _start, ...connection } = transport;
1739
+ return Errors.networkError(operation, err, {
1740
+ provider: "convex",
1741
+ failure_mode: "upstream-down",
1742
+ reason: "connection-lost-in-flight",
1743
+ ...connection
1744
+ });
1745
+ }
1552
1746
  if (err instanceof Error) {
1553
- if (isTransportError(err)) return Errors.networkError(operation, err);
1747
+ if (isTransportError(err)) return Errors.networkError(operation, err, { provider: "convex" });
1554
1748
  return Errors.providerError("convex", operation, err);
1555
1749
  }
1556
1750
  return Errors.providerError("convex", operation, new Error(String(err)));
1557
1751
  }
1558
- function mapToConvexCallError(operation, err) {
1559
- return convexCallErrorFromCapxul(operation, mapToCapxulError(operation, err));
1752
+ function mapToConvexCallError(operation, err, transport) {
1753
+ return convexCallErrorFromCapxul(operation, mapToCapxulError(operation, err, transport), transport);
1754
+ }
1755
+ function connectionState(client) {
1756
+ try {
1757
+ const state = client.connectionState?.();
1758
+ return typeof state?.connectionCount === "number" ? state : void 0;
1759
+ } catch {
1760
+ return;
1761
+ }
1762
+ }
1763
+ function logActionTransportFailure(operation, error) {
1764
+ if (error.transport === void 0) return Effect.void;
1765
+ const { kind, ...connection } = error.transport;
1766
+ return Effect.logWarning("convex.action.connection_lost").pipe(Effect.annotateLogs({
1767
+ operation,
1768
+ failure_mode: "upstream-down",
1769
+ reason: kind,
1770
+ ...connection
1771
+ }), Effect.catchCause(() => Effect.void));
1560
1772
  }
1561
1773
  const TRANSPORT_ERROR_CODES = /* @__PURE__ */ new Set([
1562
1774
  "EAI_AGAIN",
@@ -1647,12 +1859,14 @@ const DEFAULT_FUNCTIONS$3 = {
1647
1859
  var ConvexAccountAdapter = class {
1648
1860
  #convex;
1649
1861
  #fns;
1862
+ #telemetry;
1650
1863
  constructor(deps) {
1651
1864
  this.#convex = deps.convex;
1652
1865
  this.#fns = deps.functions ?? DEFAULT_FUNCTIONS$3;
1866
+ this.#telemetry = deps.telemetry;
1653
1867
  }
1654
1868
  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))));
1869
+ 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
1870
  }
1657
1871
  fundFromFaucet(input) {
1658
1872
  const operation = "fundFromFaucet";
@@ -1666,7 +1880,10 @@ var ConvexAccountAdapter = class {
1666
1880
  }
1667
1881
  };
1668
1882
  function ConvexAccountLayer() {
1669
- return Layer.effect(AccountReadPortTag, Effect.map(ConvexCallPortTag, (convex) => new ConvexAccountAdapter({ convex })));
1883
+ return Layer.effect(AccountReadPortTag, Effect.all([ConvexCallPortTag, TelemetryPortTag]).pipe(Effect.map(([convex, telemetry]) => new ConvexAccountAdapter({
1884
+ convex,
1885
+ telemetry
1886
+ }))));
1670
1887
  }
1671
1888
  function brandAccountEffect(operation, wire) {
1672
1889
  return Effect.try({
@@ -1901,15 +2118,17 @@ const DEFAULT_FUNCTIONS$1 = {
1901
2118
  var ConvexOrganizationAdapter = class {
1902
2119
  #convex;
1903
2120
  #fns;
2121
+ #telemetry;
1904
2122
  constructor(input) {
1905
2123
  this.#convex = input.convex;
1906
2124
  this.#fns = input.functions ?? DEFAULT_FUNCTIONS$1;
2125
+ this.#telemetry = input.telemetry;
1907
2126
  }
1908
2127
  createOrg(_input) {
1909
2128
  return Effect.fail(orgErrorFromCapxul("createOrg", Errors.notImplemented("organizationSetup", "use onboarding.completeOrganization")));
1910
2129
  }
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) => {
2130
+ listOrgs(input) {
2131
+ 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
2132
  const viewerRole = wire.viewerRole.trim();
1914
2133
  if (viewerRole.length === 0) throw Errors.wrongState({
1915
2134
  method: "listOrgs",
@@ -1920,7 +2139,7 @@ var ConvexOrganizationAdapter = class {
1920
2139
  })))), Effect.catchDefect((cause) => Effect.fail(toOrgError("listOrgs", cause))));
1921
2140
  }
1922
2141
  readTreasury(input) {
1923
- return this.#readTreasuryWire(String(input.orgId)).pipe(Effect.catchDefect((cause) => Effect.fail(toOrgError("readTreasury", cause))));
2142
+ return this.#readTreasuryWire(input).pipe(Effect.catchDefect((cause) => Effect.fail(toOrgError("readTreasury", cause))));
1924
2143
  }
1925
2144
  listRoles(input) {
1926
2145
  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 +2166,9 @@ var ConvexOrganizationAdapter = class {
1947
2166
  detectAndAcceptPendingInvitations(_input) {
1948
2167
  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
2168
  }
1950
- #readTreasuryWire(orgId) {
1951
- return this.#convex.action(this.#fns.readTreasury, { orgId }).pipe(Effect.mapError((error) => orgErrorFromCapxul("readTreasury", error.publicError, error)), Effect.map((wire) => {
2169
+ #readTreasuryWire(input) {
2170
+ const orgId = String(input.orgId);
2171
+ 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
2172
  if (wire.orgId !== orgId) throw Errors.invalidInput("orgId", "Organization treasury scope does not match");
1953
2173
  const balance = fromWei(wire.rawBalance, wire.decimals, wire.currency);
1954
2174
  const available = fromWei(wire.rawAvailableBalance, wire.decimals, wire.currency);
@@ -2239,6 +2459,90 @@ function fail(error) {
2239
2459
  };
2240
2460
  }
2241
2461
  //#endregion
2462
+ //#region src/adapters/telemetry/PostHogTelemetryAdapter.ts
2463
+ var PostHogTelemetryAdapter = class {
2464
+ #capture;
2465
+ #identify;
2466
+ #group;
2467
+ #reset;
2468
+ constructor(deps) {
2469
+ this.#capture = deps.capture;
2470
+ this.#identify = deps.identify ?? (() => void 0);
2471
+ this.#group = deps.group ?? (() => void 0);
2472
+ this.#reset = deps.reset ?? (() => void 0);
2473
+ }
2474
+ emit(event) {
2475
+ return this.#run(() => this.#capture(event.name, redactTelemetryProps(event.name, cloneProps(event.props)), event), "emit", event.name);
2476
+ }
2477
+ identify(input) {
2478
+ return this.#run(() => this.#identify(cloneIdentifyInput(input)), "identify");
2479
+ }
2480
+ group(input) {
2481
+ return this.#run(() => this.#group(cloneGroupInput(input)), "group");
2482
+ }
2483
+ reset() {
2484
+ return this.#run(() => this.#reset(), "reset");
2485
+ }
2486
+ #run(operation, operationName, eventName) {
2487
+ const diagnose = Effect.logWarning("product.telemetry.transport.dropped").pipe(Effect.annotateLogs({
2488
+ operation: operationName,
2489
+ ...eventName === void 0 ? {} : { product_event: eventName }
2490
+ }), Effect.catchCause(() => Effect.void));
2491
+ return Effect.suspend(() => {
2492
+ let pending;
2493
+ try {
2494
+ pending = operation();
2495
+ } catch {
2496
+ return diagnose;
2497
+ }
2498
+ if (pending === void 0) return Effect.void;
2499
+ const transport = Effect.tryPromise({
2500
+ try: () => pending,
2501
+ catch: () => void 0
2502
+ }).pipe(Effect.catch(() => diagnose));
2503
+ return Effect.forkDetach(transport, { startImmediately: true }).pipe(Effect.asVoid);
2504
+ });
2505
+ }
2506
+ };
2507
+ function PostHogTelemetryLayer(deps) {
2508
+ return Layer.succeed(TelemetryPortTag, new PostHogTelemetryAdapter(deps));
2509
+ }
2510
+ function cloneIdentifyInput(input) {
2511
+ const traits = input.traits === void 0 ? void 0 : cloneProps(input.traits);
2512
+ const properties = input.properties === void 0 ? void 0 : cloneProps(input.properties);
2513
+ return {
2514
+ distinctId: input.distinctId,
2515
+ ...input.anonDistinctId === void 0 ? {} : { anonDistinctId: input.anonDistinctId },
2516
+ ...traits === void 0 ? {} : { traits },
2517
+ ...properties === void 0 ? {} : { properties }
2518
+ };
2519
+ }
2520
+ function cloneGroupInput(input) {
2521
+ const properties = input.properties === void 0 ? void 0 : cloneProps(input.properties);
2522
+ return properties === void 0 ? {
2523
+ groupType: input.groupType,
2524
+ groupKey: input.groupKey
2525
+ } : {
2526
+ groupType: input.groupType,
2527
+ groupKey: input.groupKey,
2528
+ properties
2529
+ };
2530
+ }
2531
+ function cloneProps(props) {
2532
+ if (props === void 0) return void 0;
2533
+ const cloned = {};
2534
+ for (const [key, value] of Object.entries(props)) cloned[key] = cloneTelemetryValue(value);
2535
+ return cloned;
2536
+ }
2537
+ function cloneTelemetryValue(value) {
2538
+ if (Array.isArray(value)) return value.map(cloneTelemetryValue);
2539
+ if (value === null || typeof value !== "object") return value;
2540
+ if (Object.getPrototypeOf(value) !== Object.prototype) return value;
2541
+ const cloned = {};
2542
+ for (const [key, nested] of Object.entries(value)) cloned[key] = cloneTelemetryValue(nested);
2543
+ return cloned;
2544
+ }
2545
+ //#endregion
2242
2546
  //#region src/adapters/diagnostic/ConsoleDiagnosticAdapter.ts
2243
2547
  const DEFAULT_ACCOUNT_SETUP_LOG_PREFIX = "[capxul:account-setup]";
2244
2548
  var ConsoleDiagnosticAdapter = class {
@@ -2564,8 +2868,59 @@ function createOpenfortBrowserSignerFromBootstrap(bootstrap, options = {}) {
2564
2868
  embeddedWallet: openfort.embeddedWallet,
2565
2869
  ensureWalletReady: ensureOpenfortWalletReady
2566
2870
  });
2871
+ const resetReadiness = () => {
2872
+ walletReadyPromise = null;
2873
+ clearStaleOpenfortBrowserStorage(bootstrap.openfortPublishableKey);
2874
+ signer.resetAddressCache();
2875
+ setStatus("unknown");
2876
+ };
2877
+ const signUserOpHash = async (hash) => {
2878
+ try {
2879
+ return await signer.signUserOpHash(hash);
2880
+ } catch (cause) {
2881
+ const providerErrorCode = openfortSignerNotReadyCode(cause);
2882
+ if (providerErrorCode === void 0) throw cause;
2883
+ diagnostic?.trace("openfort.signerRecovery", {
2884
+ outcome: "started",
2885
+ attempt: 1,
2886
+ failure_mode: "signer-not-ready"
2887
+ });
2888
+ resetReadiness();
2889
+ try {
2890
+ await ensureOpenfortWalletReady();
2891
+ const signature = await signer.signUserOpHash(hash);
2892
+ diagnostic?.trace("openfort.signerRecovery", {
2893
+ outcome: "succeeded",
2894
+ attempt: 1,
2895
+ failure_mode: "signer-not-ready"
2896
+ });
2897
+ return signature;
2898
+ } catch (retryCause) {
2899
+ const retryFailure = signerFailure("openfort-embedded", "signUserOpHash", retryCause);
2900
+ if (retryFailure.code === "SIGNER_REJECTED") throw retryFailure;
2901
+ if (isSignerNotReadySignal(retryCause)) {
2902
+ walletReadyPromise = null;
2903
+ signer.resetAddressCache();
2904
+ setStatus("unavailable");
2905
+ }
2906
+ diagnostic?.trace("openfort.signerRecovery", {
2907
+ outcome: "failed",
2908
+ attempt: 1,
2909
+ failure_mode: isSignerNotReadySignal(retryCause) ? "signer-not-ready" : "unknown"
2910
+ });
2911
+ throw Errors.providerError("openfort", "signerRecovery", retryCause, {
2912
+ failure_mode: "signer-not-ready",
2913
+ details: {
2914
+ provider_error_source: "openfort",
2915
+ provider_error_code: providerErrorCode
2916
+ }
2917
+ });
2918
+ }
2919
+ }
2920
+ };
2567
2921
  return {
2568
2922
  ...signer,
2923
+ signUserOpHash,
2569
2924
  statusStore,
2570
2925
  ensureWalletReady: ensureOpenfortWalletReady,
2571
2926
  getAddress: async () => {
@@ -2581,12 +2936,7 @@ function createOpenfortBrowserSignerFromBootstrap(bootstrap, options = {}) {
2581
2936
  throw openfortProviderError("getAddress", cause);
2582
2937
  }
2583
2938
  },
2584
- resetSession: () => {
2585
- walletReadyPromise = null;
2586
- clearStaleOpenfortBrowserStorage(bootstrap.openfortPublishableKey);
2587
- signer.resetAddressCache();
2588
- setStatus("unknown");
2589
- }
2939
+ resetSession: resetReadiness
2590
2940
  };
2591
2941
  }
2592
2942
  function normalizeBetterAuthBaseUrl(raw) {
@@ -2594,6 +2944,73 @@ function normalizeBetterAuthBaseUrl(raw) {
2594
2944
  return trimmed.endsWith("/api/auth") ? trimmed : `${trimmed}/api/auth`;
2595
2945
  }
2596
2946
  //#endregion
2947
+ //#region src/telemetry/from-posthog.ts
2948
+ /**
2949
+ * Drop props that must never cross to a host-owned external sink. Today that is
2950
+ * the `$exception` `details` blob: `captureException` serializes
2951
+ * `CapxulError.details` (e.g. `{ asset, available, required }`, `{ name }`,
2952
+ * `{ accountId }` — errors.ts) into it, and the shared redactor has no
2953
+ * `$exception` rule, so it is stripped here at the boundary (infra#1037). The
2954
+ * safe fields (error code, operation, failure_mode, the fixed leak-safe message,
2955
+ * stack frames) are preserved.
2956
+ */
2957
+ function stripHostUnsafeProps(props) {
2958
+ if (props === void 0) return void 0;
2959
+ const { details: _details, ...safe } = props;
2960
+ return safe;
2961
+ }
2962
+ /**
2963
+ * The subset of a posthog-js client the seam calls. `identify` / `group` /
2964
+ * `reset` are optional — a host that only wants event capture can omit them.
2965
+ */
2966
+ /**
2967
+ * Adapt the host's already-initialized posthog-like client into a
2968
+ * `TelemetryPort` for the `telemetry` prop / input. This port SUPPLANTS the
2969
+ * SDK's no-op default telemetry sink (`production.ts` binds it via
2970
+ * `Layer.succeed`, not `compose` — there is no client-side success relay to
2971
+ * compose with); it is additive to Capxul's backend first-party record and
2972
+ * never owns the client.
2973
+ */
2974
+ function postHogProductTelemetry(policy, fixedSnapshot) {
2975
+ const telemetry = new PostHogTelemetryAdapter({
2976
+ capture: (name, props, source) => {
2977
+ const snapshot = readInvocationObservation(source) ?? fixedSnapshot ?? policy.snapshot();
2978
+ if (!snapshot.active || policy.client === null || policy.client === void 0) return;
2979
+ const event = stampTelemetryEnvelope({
2980
+ name,
2981
+ props
2982
+ }, {
2983
+ capxul_env: policy.capxulEnv,
2984
+ producer: "sdk"
2985
+ });
2986
+ policy.deliver(() => policy.client.capture(name, {
2987
+ ...stripHostUnsafeProps(event.props),
2988
+ ...observationContextProps(snapshot.context)
2989
+ }));
2990
+ },
2991
+ identify: (input) => {
2992
+ if (!(fixedSnapshot ?? policy.snapshot()).active || policy.client?.identify === void 0) return;
2993
+ policy.deliver(() => policy.client.identify(input.distinctId, {
2994
+ ...input.traits,
2995
+ ...input.properties
2996
+ }));
2997
+ },
2998
+ group: (input) => {
2999
+ if (!(fixedSnapshot ?? policy.snapshot()).active || policy.client?.group === void 0) return;
3000
+ policy.deliver(() => policy.client.group(input.groupType, input.groupKey, input.properties === void 0 ? void 0 : { ...input.properties }));
3001
+ },
3002
+ reset: () => {
3003
+ if (!(fixedSnapshot ?? policy.snapshot()).active || policy.client?.reset === void 0) return;
3004
+ policy.deliver(() => policy.client.reset());
3005
+ }
3006
+ });
3007
+ Object.defineProperty(telemetry, PRODUCT_INVOCATION, {
3008
+ enumerable: false,
3009
+ value: (source) => postHogProductTelemetry(policy, readInvocationObservation(source) ?? fixedSnapshot ?? policy.snapshot())
3010
+ });
3011
+ return telemetry;
3012
+ }
3013
+ //#endregion
2597
3014
  //#region src/host-observability.ts
2598
3015
  const HOST_INVOCATION = Symbol("capxul.host-observability-invocation");
2599
3016
  /** Build the SDK's one host module around an already-initialized PostHog client. */
@@ -2732,7 +3149,10 @@ function makeProductionAdapterLayerEntries(input) {
2732
3149
  },
2733
3150
  {
2734
3151
  name: "org",
2735
- layer: Layer.effect(OrgPortTag, Effect.map(ConvexCallPortTag, (convex) => new ConvexOrganizationAdapter({ convex })))
3152
+ layer: Layer.effect(OrgPortTag, Effect.all([ConvexCallPortTag, TelemetryPortTag]).pipe(Effect.map(([convex, telemetry]) => new ConvexOrganizationAdapter({
3153
+ convex,
3154
+ telemetry
3155
+ }))))
2736
3156
  }
2737
3157
  ];
2738
3158
  }
@@ -2741,10 +3161,13 @@ function mergeProductionAdapterLayers(entries) {
2741
3161
  for (const entry of entries) byName.set(entry.name, entry.layer);
2742
3162
  const authClientLayer = byName.get("authClient");
2743
3163
  const convexCallLayer = byName.get("convexCall");
3164
+ const telemetryLayer = byName.get("telemetry");
2744
3165
  const convexCallReadyLayer = convexCallLayer === void 0 || authClientLayer === void 0 ? convexCallLayer : convexCallLayer.pipe(Layer.provide(authClientLayer));
3166
+ const observedConvexCallLayer = convexCallReadyLayer === void 0 ? telemetryLayer : telemetryLayer === void 0 ? convexCallReadyLayer : Layer.merge(convexCallReadyLayer, telemetryLayer);
2745
3167
  return entries.map((entry) => {
2746
3168
  const layer = byName.get(entry.name) ?? entry.layer;
2747
3169
  if (entry.name === "convexCall") return convexCallReadyLayer ?? layer;
3170
+ if (entry.name === "accountRead" || entry.name === "org") return observedConvexCallLayer === void 0 ? layer : layer.pipe(Layer.provide(observedConvexCallLayer));
2748
3171
  if (isConvexDependentLayer(entry.name)) return convexCallReadyLayer === void 0 ? layer : layer.pipe(Layer.provide(convexCallReadyLayer));
2749
3172
  return layer;
2750
3173
  }).reduce((current, layer) => Layer.merge(current, layer), Layer.empty);
@@ -2756,7 +3179,7 @@ function productionBootstrapPortLayer(bootstrap) {
2756
3179
  return Layer.succeed(BootstrapPortTag, bootstrap);
2757
3180
  }
2758
3181
  function productionAuthClientLayer(input, refreshConvexAuthRef, resetSignerSessionRef) {
2759
- return (input.runtime === "browser" ? BetterAuthBrowserLayer({
3182
+ return (input.authClient !== void 0 ? Layer.succeed(AuthClientPortTag, input.authClient) : input.runtime === "browser" ? BetterAuthBrowserLayer({
2760
3183
  authBaseUrl: input.runtimeUrls.authBaseUrl,
2761
3184
  ...input.fetch === void 0 ? {} : { fetch: input.fetch },
2762
3185
  ...input.observation === void 0 ? {} : { observation: input.observation }
@@ -2897,6 +3320,7 @@ async function createProductionAdapters(input) {
2897
3320
  applicationId: bootstrapResult.value.applicationId,
2898
3321
  ...observation === void 0 ? {} : { observation },
2899
3322
  ...input.authCache === void 0 ? {} : { authCache: input.authCache },
3323
+ ...input.authClient === void 0 ? {} : { authClient: input.authClient },
2900
3324
  ...telemetry === void 0 ? {} : { telemetry },
2901
3325
  ...injectedClient === void 0 ? {} : { convexClient: injectedClient },
2902
3326
  ...input.fetch === void 0 ? {} : { fetch: input.fetch },
@@ -2905,13 +3329,22 @@ async function createProductionAdapters(input) {
2905
3329
  }));
2906
3330
  const applicationLayer = Layer.merge(portsLayer, optionalEngineeringTelemetryLayer(bootstrapResult.value.engineeringTelemetry, resolvedInput.value.runtime));
2907
3331
  const context = await Effect.runPromise(Layer.buildWithScope(applicationLayer, scope));
3332
+ const ports = await Effect.runPromise(collectProductionFlowPorts.pipe(Effect.provide(context)));
3333
+ const stopConnectionObservation = observeProductionConvexConnection(ports.convexCall, context);
3334
+ const close = idempotentClose(async () => {
3335
+ try {
3336
+ stopConnectionObservation();
3337
+ } finally {
3338
+ await closeScope();
3339
+ }
3340
+ });
2908
3341
  return {
2909
3342
  ok: true,
2910
3343
  value: {
2911
3344
  context,
2912
- ports: await Effect.runPromise(collectProductionFlowPorts.pipe(Effect.provide(context))),
3345
+ ports,
2913
3346
  bootstrap: bootstrapResult.value,
2914
- close: closeScope
3347
+ close
2915
3348
  }
2916
3349
  };
2917
3350
  } catch (cause) {
@@ -2922,6 +3355,18 @@ async function createProductionAdapters(input) {
2922
3355
  };
2923
3356
  }
2924
3357
  }
3358
+ function observeProductionConvexConnection(convexCall, context) {
3359
+ if (!("observeConnection" in convexCall) || typeof convexCall.observeConnection !== "function") return () => {};
3360
+ const runPromise = Effect.runPromiseWith(context);
3361
+ try {
3362
+ return convexCall.observeConnection((diagnostic) => {
3363
+ const message = diagnostic.transition === "connected" ? Effect.logInfo("convex.connection.changed") : Effect.logWarning("convex.connection.changed");
3364
+ runPromise(message.pipe(Effect.annotateLogs(diagnostic), Effect.catchCause(() => Effect.void))).catch(() => void 0);
3365
+ });
3366
+ } catch {
3367
+ return () => {};
3368
+ }
3369
+ }
2925
3370
  function optionalEngineeringTelemetryLayer(policy, runtime) {
2926
3371
  if (policy === void 0) return Layer.empty;
2927
3372
  try {
@@ -3204,4 +3649,155 @@ async function createCapxulClient(input) {
3204
3649
  return createCapxulClient$1(input);
3205
3650
  }
3206
3651
  //#endregion
3652
+ //#region src/telemetry/stack-frame-parser.ts
3653
+ /**
3654
+ * Regex for V8/Chrome stack trace frame lines.
3655
+ * Matches:
3656
+ * `at functionName (url:line:col)`
3657
+ * `at url:line:col`
3658
+ * `at async functionName (url:line:col)`
3659
+ * `at new ClassName (url:line:col)`
3660
+ */
3661
+ const V8_FRAME_RE = /^\s*at\s+(?:(.+?)\s+\()?(?:(.+?)(?::(\d+):(\d+))?|(.+?))\)?\s*$/;
3662
+ function isNonUrlName(name) {
3663
+ return name === "<anonymous>" || name.startsWith("eval") || name.startsWith("new ") || name.startsWith("async ");
3664
+ }
3665
+ /**
3666
+ * Parse a V8/Chrome-style stack trace string into PostHog `ExceptionFrame` objects.
3667
+ * Returns an empty array when `error.stack` is absent or empty.
3668
+ *
3669
+ * Handles:
3670
+ * - Standard `at functionName (url:line:col)`
3671
+ * - Bare `at url:line:col` (no function name)
3672
+ * - `at async functionName (url:line:col)`
3673
+ * - `at new ClassName (url:line:col)`
3674
+ * - Native frames: `at Array.forEach (<anonymous>)`
3675
+ */
3676
+ function parseV8StackFrames(error) {
3677
+ const stack = error.stack;
3678
+ if (stack === void 0 || stack === null || stack === "") return [];
3679
+ const lines = stack.split("\n");
3680
+ const frames = [];
3681
+ for (const line of lines) {
3682
+ const trimmed = line.trim();
3683
+ if (!trimmed.startsWith("at ")) continue;
3684
+ const match = V8_FRAME_RE.exec(trimmed);
3685
+ if (match === null) continue;
3686
+ if (match[1] !== void 0) frames.push({
3687
+ function: match[1],
3688
+ filename: match[2],
3689
+ lineno: match[3] !== void 0 ? Number(match[3]) : null,
3690
+ colno: match[4] !== void 0 ? Number(match[4]) : null
3691
+ });
3692
+ else if (match[2] !== void 0 && !isNonUrlName(match[2])) frames.push({
3693
+ function: "<anonymous>",
3694
+ filename: match[2],
3695
+ lineno: match[3] !== void 0 ? Number(match[3]) : null,
3696
+ colno: match[4] !== void 0 ? Number(match[4]) : null
3697
+ });
3698
+ else frames.push({
3699
+ function: match[1] ?? match[2] ?? match[5] ?? "<anonymous>",
3700
+ filename: match[2] ?? match[5] ?? "<anonymous>",
3701
+ lineno: match[3] !== void 0 ? Number(match[3]) : null,
3702
+ colno: match[4] !== void 0 ? Number(match[4]) : null
3703
+ });
3704
+ }
3705
+ return frames;
3706
+ }
3707
+ /** Fixed, leak-safe frame used when the error carries no parseable stack. */
3708
+ const SDK_BOUNDARY_FILENAME = "capxul-sdk-observation://boundary";
3709
+ /**
3710
+ * Build PostHog's `$exception_list` (always a single entry). Error Tracking
3711
+ * groups on `type`, so it is ALWAYS present (the CapxulError code) — the
3712
+ * previous `[{ frames }]` shape omitted it and PostHog dropped the event as
3713
+ * "missing field `type`". When the error has no parseable stack, a synthetic
3714
+ * boundary frame stands in so the event still ingests as a real Issue (#1031).
3715
+ */
3716
+ function buildExceptionList(input) {
3717
+ const frames = input.frames.length > 0 ? input.frames : [{
3718
+ filename: SDK_BOUNDARY_FILENAME,
3719
+ function: input.operation ?? "unknown",
3720
+ lineno: 1,
3721
+ colno: 1
3722
+ }];
3723
+ return [{
3724
+ type: input.type,
3725
+ value: input.value,
3726
+ mechanism: {
3727
+ handled: true,
3728
+ type: "capxul_sdk_boundary"
3729
+ },
3730
+ stacktrace: { frames }
3731
+ }];
3732
+ }
3733
+ //#endregion
3734
+ //#region src/telemetry/capture-exception.ts
3735
+ /**
3736
+ * Capture an error as a `$exception` event through the telemetry port,
3737
+ * formatted for PostHog Error Tracking.
3738
+ *
3739
+ * Parses stack traces into `$exception_list` format, extracts structured
3740
+ * metadata from CapxulError objects, and supplements with context props.
3741
+ * Fire-and-forget: telemetry defects are silently swallowed.
3742
+ * Returns `Effect<void, never>` for use in Effect pipelines; the underlying
3743
+ * adapter work is synchronous, so callers outside Effect contexts can
3744
+ * use `Effect.runSync`.
3745
+ */
3746
+ function captureException(telemetry, error, context) {
3747
+ return Effect.catchDefect(Effect.sync(() => {
3748
+ const frames = error instanceof Error ? parseV8StackFrames(error) : [];
3749
+ const capxulError = isCapxulError(error) ? error : null;
3750
+ const errorCode = normalizeExceptionErrorKind(capxulError?.code ?? context?.capxul_error_code);
3751
+ const { chain_provider_message: _providerMessage, chain_revert_args: _revertArguments, ...safeDetail } = failureDetail(error) ?? {};
3752
+ const operation = normalizeExceptionOperation(context?.operation ?? safeDetail.provider_operation);
3753
+ const failureMode = resolveFailureMode(error, context?.failure_mode);
3754
+ const detail = {
3755
+ ...safeDetail,
3756
+ failure_mode: failureMode
3757
+ };
3758
+ const properties = {
3759
+ capxul_error_code: errorCode,
3760
+ ...detail,
3761
+ error_message: EXCEPTION_MESSAGE,
3762
+ error_details: detail,
3763
+ error_cause: detail.reason ?? failureMode,
3764
+ layer: safeExceptionLabel(capxulError?.layer ?? context?.layer),
3765
+ provider: detail.provider ?? safeExceptionLabel(context?.provider)
3766
+ };
3767
+ for (const key of Object.keys(properties)) if (properties[key] === void 0) delete properties[key];
3768
+ const props = projectSdkException({
3769
+ properties,
3770
+ sdkVersion: SDK_VERSION$1,
3771
+ operation,
3772
+ errorKind: errorCode,
3773
+ failureMode,
3774
+ message: EXCEPTION_MESSAGE,
3775
+ issueDescription: detail.reason ?? failureMode,
3776
+ ...detail.chain_upstream === void 0 ? {} : { chainUpstream: detail.chain_upstream },
3777
+ ...detail.chain_method === void 0 ? {} : { chainMethod: detail.chain_method },
3778
+ exceptionList: buildExceptionList({
3779
+ type: errorCode,
3780
+ value: EXCEPTION_MESSAGE,
3781
+ operation,
3782
+ frames
3783
+ })
3784
+ });
3785
+ return telemetry.emit({
3786
+ name: "$exception",
3787
+ props
3788
+ });
3789
+ }).pipe(Effect.flatten), () => Effect.void);
3790
+ }
3791
+ /**
3792
+ * Synchronous fire-and-forget capture. Runs the Effect inline with
3793
+ * `Effect.runSync` so callers outside an Effect context can report errors
3794
+ * without awaiting. Core SDK public methods use the assembled observation
3795
+ * boundary instead.
3796
+ */
3797
+ function captureExceptionSync(telemetry, error, context) {
3798
+ try {
3799
+ Effect.runSync(captureException(telemetry, error, context));
3800
+ } catch {}
3801
+ }
3802
+ //#endregion
3207
3803
  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 };