@capxul/sdk 2.7.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/{InMemoryAuthCacheAdapter-B6PHyzCN.mjs → InMemoryAuthCacheAdapter-BrEN-men.mjs} +117 -5
- package/dist/{create-capxul-client-DNYuHQGS.mjs → create-capxul-client-BKJHefmA.mjs} +399 -437
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +641 -27
- package/dist/node/index.d.mts +1 -1
- package/dist/node/index.mjs +1 -1
- package/dist/{observation-BMW_idMZ.d.mts → observation-D-oCDlMS.d.mts} +32 -3
- package/dist/{signer-BMRxwX13.d.mts → signer-BdDgF200.d.mts} +42 -2
- package/dist/testing/index.d.mts +1 -1
- package/dist/testing/index.mjs +2 -2
- package/package.json +5 -5
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { A as
|
|
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-
|
|
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.#
|
|
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) =>
|
|
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.
|
|
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(
|
|
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(
|
|
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(
|
|
1951
|
-
|
|
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.
|
|
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
|
|
3363
|
+
ports,
|
|
2913
3364
|
bootstrap: bootstrapResult.value,
|
|
2914
|
-
close
|
|
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 };
|