@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/OAuthBearerAuthClient-BZ6tXt4m.mjs +211 -0
- package/dist/OAuthBearerAuthClient-GkHnGqNi.d.mts +19 -0
- package/dist/{InMemoryAuthCacheAdapter-FptQ2p7C.mjs → clock-0c5NM-TY.mjs} +1050 -6
- package/dist/{create-capxul-client-YSN2yN8m.mjs → create-capxul-client-DwINzpXZ.mjs} +364 -1409
- package/dist/index.d.mts +3 -2
- package/dist/index.mjs +639 -43
- package/dist/node/index.d.mts +3 -2
- package/dist/node/index.mjs +3 -2
- package/dist/{observation-YhuU8yQX.d.mts → observation-CR3_haBB.d.mts} +34 -67
- package/dist/{signer-xGhV5Q7i.d.mts → signer-HY5Ju8xs.d.mts} +88 -3
- package/dist/testing/index.d.mts +1 -1
- package/dist/testing/index.mjs +2 -22
- package/package.json +5 -5
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { A as
|
|
2
|
-
import {
|
|
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/
|
|
1403
|
-
var
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
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
|
|
1419
|
-
|
|
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.#
|
|
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) =>
|
|
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.
|
|
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(
|
|
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(
|
|
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(
|
|
1951
|
-
|
|
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.
|
|
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
|
|
3345
|
+
ports,
|
|
2913
3346
|
bootstrap: bootstrapResult.value,
|
|
2914
|
-
close
|
|
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 };
|