@capxul/sdk 4.2.0-rc.8 → 4.2.0-rc.9
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-hPwtSpEf.mjs → OAuthBearerAuthClient-IB2W1Fzj.mjs} +104 -5
- package/dist/capxul-source.json +3 -3
- package/dist/index.d.mts +13 -1
- package/dist/index.mjs +2 -2
- package/dist/node/index.mjs +1 -1
- package/dist/{production-Y3yqeOSE.mjs → production-216hsAKc.mjs} +498 -6
- package/dist/{production-BOMKT0n7.d.mts → production-Du4Vc7YD.d.mts} +128 -2
- package/dist/testing/index.d.mts +77 -2
- package/dist/testing/index.mjs +94 -3
- package/package.json +3 -3
|
@@ -902,6 +902,7 @@ const COUNTRY_CODE_RE = /^[A-Z]{2}$/;
|
|
|
902
902
|
const ACCOUNT_ID_RE = /^account_[0-9A-Za-z]+$/;
|
|
903
903
|
const PARTY_ID_RE = /^party_[0-9A-Za-z]+$/;
|
|
904
904
|
const APP_ID_RE = /^app_[0-7][0-9A-HJKMNP-TV-Z]{25}$/;
|
|
905
|
+
const TX_HASH_RE = /^0x[0-9a-f]{64}$/i;
|
|
905
906
|
const WEI_RE = /^[0-9]+$/;
|
|
906
907
|
Math.floor(Number.MAX_SAFE_INTEGER / 1e3);
|
|
907
908
|
function toAddress(raw) {
|
|
@@ -957,6 +958,10 @@ function toPublishableKey(raw) {
|
|
|
957
958
|
if (typeof raw !== "string" || !PUBLISHABLE_KEY_PATTERN.test(raw)) throw Errors.invalidInput("publishableKey", invalidValueReason("must match cap_pk_(test|live) plus 32 Crockford base32 chars", raw));
|
|
958
959
|
return raw;
|
|
959
960
|
}
|
|
961
|
+
function toTxHash(raw) {
|
|
962
|
+
if (typeof raw !== "string" || !TX_HASH_RE.test(raw)) throw Errors.invalidInput("txHash", invalidValueReason("must be 0x + 64 hex chars", raw));
|
|
963
|
+
return raw.toLowerCase();
|
|
964
|
+
}
|
|
960
965
|
function toEpochMs(raw) {
|
|
961
966
|
assertSafeNonNegativeInteger(raw, "epochMs");
|
|
962
967
|
return raw;
|
|
@@ -1041,23 +1046,117 @@ function invalidValueReason(prefix, raw) {
|
|
|
1041
1046
|
//#region ../config/src/tokens.ts
|
|
1042
1047
|
/** `TestUSDC` ("USDX") — Base Sepolia, 6 decimals, open `mint`. (Canon §1.) */
|
|
1043
1048
|
const USDX_ADDRESS_BASE_SEPOLIA = "0xf09fcbb6df9f8f918e58f9c8ab15a76ade862b77";
|
|
1049
|
+
const USDX_DEPLOYMENT_START_BLOCK = 39860173;
|
|
1050
|
+
/**
|
|
1051
|
+
* The ERC-20 `symbol()` the deployed `TestUSDC` contract returns
|
|
1052
|
+
* (`packages/contracts/src/TestUSDC.sol:10`). It travels with the address and
|
|
1053
|
+
* the decimals so a holdings row can never carry one token's address with
|
|
1054
|
+
* another token's display symbol. This is the display symbol, not the
|
|
1055
|
+
* currency: see `USDX_CURRENCY` below.
|
|
1056
|
+
*/
|
|
1057
|
+
const USDX_SYMBOL = "USDX";
|
|
1058
|
+
const SYNTHETIC_TEST_ASSET_SOURCE = "synthetic-test-liquidity";
|
|
1044
1059
|
const USDX_ASSET = {
|
|
1045
1060
|
assetId: assetIdFor(84532, USDX_ADDRESS_BASE_SEPOLIA),
|
|
1046
1061
|
chainId: toChainId(84532),
|
|
1047
|
-
symbol:
|
|
1062
|
+
symbol: USDX_SYMBOL,
|
|
1048
1063
|
peg: toCurrencyCode("USD"),
|
|
1049
1064
|
tokenAddress: USDX_ADDRESS_BASE_SEPOLIA,
|
|
1050
|
-
deploymentStartBlock:
|
|
1051
|
-
decimals: 6
|
|
1065
|
+
deploymentStartBlock: USDX_DEPLOYMENT_START_BLOCK,
|
|
1066
|
+
decimals: 6,
|
|
1067
|
+
source: "settlement"
|
|
1052
1068
|
};
|
|
1069
|
+
/**
|
|
1070
|
+
* The settlement tier: rows the exact-transfer path and the Movement scanner
|
|
1071
|
+
* admit, each carrying the legacy `currency` projection those readers expect.
|
|
1072
|
+
* Adding an owned test asset here would put candidate liquidity on the
|
|
1073
|
+
* money-write path, so M03's rows go to the candidate tier below instead.
|
|
1074
|
+
*/
|
|
1053
1075
|
const CONFIGURED_MONEY_ASSETS = [{
|
|
1054
1076
|
...USDX_ASSET,
|
|
1055
1077
|
currency: USDX_ASSET.peg
|
|
1056
1078
|
}];
|
|
1079
|
+
/**
|
|
1080
|
+
* The candidate tier: five owned test assets, every field read back from Base
|
|
1081
|
+
* Sepolia after the deployment that created it (chain 84532, blocks
|
|
1082
|
+
* 46,465,978–46,465,982). Real contracts, authored liquidity, no settlement
|
|
1083
|
+
* admission — so rollback is removing consumers, never relabelling a
|
|
1084
|
+
* deployed address or deleting its history.
|
|
1085
|
+
*
|
|
1086
|
+
* `symbol` is the display string the owned contract returns. A
|
|
1087
|
+
* provider-supported USDC would be a different contract at a different
|
|
1088
|
+
* address returning the same symbol, which is why the resolvers below key on
|
|
1089
|
+
* `assetId`, or on chain plus address, and never on a symbol.
|
|
1090
|
+
*/
|
|
1091
|
+
const SYNTHETIC_TEST_ASSETS = [
|
|
1092
|
+
{
|
|
1093
|
+
assetId: assetIdFor(84532, "0xb3d8566fb90f7df939f4bc08a09b19047117f549"),
|
|
1094
|
+
chainId: toChainId(84532),
|
|
1095
|
+
tokenAddress: "0xb3d8566fb90f7df939f4bc08a09b19047117f549",
|
|
1096
|
+
symbol: "USDC",
|
|
1097
|
+
decimals: 6,
|
|
1098
|
+
peg: toCurrencyCode("USD"),
|
|
1099
|
+
deploymentStartBlock: 46465978,
|
|
1100
|
+
deploymentTransaction: "0xa7fb8bb9d1fd1cc5fe2fc42fa4cdccace7742efae8ea99f091a283e5709343de",
|
|
1101
|
+
source: SYNTHETIC_TEST_ASSET_SOURCE
|
|
1102
|
+
},
|
|
1103
|
+
{
|
|
1104
|
+
assetId: assetIdFor(84532, "0x7ddcfb6aaffb9908c4c2db308b784c269eec8a01"),
|
|
1105
|
+
chainId: toChainId(84532),
|
|
1106
|
+
tokenAddress: "0x7ddcfb6aaffb9908c4c2db308b784c269eec8a01",
|
|
1107
|
+
symbol: "USDT",
|
|
1108
|
+
decimals: 6,
|
|
1109
|
+
peg: toCurrencyCode("USD"),
|
|
1110
|
+
deploymentStartBlock: 46465979,
|
|
1111
|
+
deploymentTransaction: "0xa17ad76fe8b24cdf1fbcff245c3eb022444710cf9fa7b5ee59818be654f4ae10",
|
|
1112
|
+
source: SYNTHETIC_TEST_ASSET_SOURCE
|
|
1113
|
+
},
|
|
1114
|
+
{
|
|
1115
|
+
assetId: assetIdFor(84532, "0x1d93d525f73453fe18eebf044e8a3954bfe3721e"),
|
|
1116
|
+
chainId: toChainId(84532),
|
|
1117
|
+
tokenAddress: "0x1d93d525f73453fe18eebf044e8a3954bfe3721e",
|
|
1118
|
+
symbol: "WETH",
|
|
1119
|
+
decimals: 18,
|
|
1120
|
+
peg: null,
|
|
1121
|
+
deploymentStartBlock: 46465980,
|
|
1122
|
+
deploymentTransaction: "0xcb9b2d024686fac93ec1243ccf02d3567fd531da46176115660cba2ba644b09d",
|
|
1123
|
+
source: SYNTHETIC_TEST_ASSET_SOURCE
|
|
1124
|
+
},
|
|
1125
|
+
{
|
|
1126
|
+
assetId: assetIdFor(84532, "0x8fe52c70aa9f7b6d74d8bb26eca33d87413cb624"),
|
|
1127
|
+
chainId: toChainId(84532),
|
|
1128
|
+
tokenAddress: "0x8fe52c70aa9f7b6d74d8bb26eca33d87413cb624",
|
|
1129
|
+
symbol: "WBTC",
|
|
1130
|
+
decimals: 8,
|
|
1131
|
+
peg: null,
|
|
1132
|
+
deploymentStartBlock: 46465981,
|
|
1133
|
+
deploymentTransaction: "0x728ba7b430ffd141f83d0ed5ce429d050049d7b097c936fd11e9c572d1243854",
|
|
1134
|
+
source: SYNTHETIC_TEST_ASSET_SOURCE
|
|
1135
|
+
},
|
|
1136
|
+
{
|
|
1137
|
+
assetId: assetIdFor(84532, "0x5865fe9787ac214feb5facaebaec06969ac0a9a3"),
|
|
1138
|
+
chainId: toChainId(84532),
|
|
1139
|
+
tokenAddress: "0x5865fe9787ac214feb5facaebaec06969ac0a9a3",
|
|
1140
|
+
symbol: "cNGN",
|
|
1141
|
+
decimals: 6,
|
|
1142
|
+
peg: toCurrencyCode("NGN"),
|
|
1143
|
+
deploymentStartBlock: 46465982,
|
|
1144
|
+
deploymentTransaction: "0xcedf254bcef57fc64189261a5ab1161bc4fde370e470933f627c4d8ddb5a2e26",
|
|
1145
|
+
source: SYNTHETIC_TEST_ASSET_SOURCE
|
|
1146
|
+
}
|
|
1147
|
+
];
|
|
1148
|
+
toChainId(84532);
|
|
1149
|
+
/**
|
|
1150
|
+
* Every known asset in ONE identity space. The tiers above declare admission;
|
|
1151
|
+
* this is the only list an identity resolves against, so an asset is never
|
|
1152
|
+
* two different things depending on which list a caller happened to read.
|
|
1153
|
+
*/
|
|
1154
|
+
const MONEY_ASSET_REGISTRY = [...CONFIGURED_MONEY_ASSETS, ...SYNTHETIC_TEST_ASSETS];
|
|
1057
1155
|
CONFIGURED_MONEY_ASSETS.map((asset) => asset.tokenAddress.toLowerCase());
|
|
1156
|
+
/** The registry row for `assetId` across every tier, or `null`. */
|
|
1058
1157
|
function configuredMoneyAssetById(assetId) {
|
|
1059
1158
|
const canonical = toAssetId(assetId);
|
|
1060
|
-
return
|
|
1159
|
+
return MONEY_ASSET_REGISTRY.find((asset) => asset.assetId === canonical) ?? null;
|
|
1061
1160
|
}
|
|
1062
1161
|
//#endregion
|
|
1063
1162
|
//#region ../config/src/org-payments.ts
|
|
@@ -2369,4 +2468,4 @@ function readExchange(body) {
|
|
|
2369
2468
|
};
|
|
2370
2469
|
}
|
|
2371
2470
|
//#endregion
|
|
2372
|
-
export {
|
|
2471
|
+
export { toRoleKey as $, currencySymbolFor as A, toCurrencyCode as B, ASSET_ID_RE as C, redactSecrets as Ct, WEI_RE as D, SUPPORTED_CURRENCY_CODES as E, toAssetId as F, toHandle as G, toEmail as H, toAuthUserId as I, toOrgId as J, toJwtToken as K, toBudgetId as L, toAddress as M, toAllowedOrigin as N, ZERO_BYTES32 as O, toAppId as P, toPublishableKey as Q, toChainId as R, APP_ID_RE as S, isCredentialField as St, EVM_ADDRESS_RE as T, toEpochMs as U, toDurationMs as V, toEpochSeconds as W, toPayrollGroupId as X, toPartyId as Y, toPayrollRunId as Z, normalizeBindingEmail as _, failureFingerprint as _t, parseAuthSession as a, decodeConvexError as at, configuredMoneyAssetById as b, revertSummaryText as bt, AuthCachePortTag as c, EXPECTED_OPERATION_OUTCOMES as ct, authClientPortFromPromiseAdapter as d, CHAIN_UPSTREAMS as dt, toSessionToken as et, AuthClientError as f, FAILURE_MODES as ft, CAPXUL_PAYMENTS_V2_ADDRESS as g, decodeChainCause as gt, orgRoleKeyForLabel as h, chainEvidenceLabel as ht, BrowserAuthCacheAdapter as i, HANDLE_RE as it, toAccountId as j, assetIdFor as k, SystemClockLayer as l, Errors as lt, ADMIN_ROLE_LABEL as m, chainCauseProperties as mt, readClockNow as n, toTxHash as nt, parseCachedJwt as o, CAPXUL_ERROR_CODES as ot, AuthClientPortTag as p, boundedResponseHeaders as pt, toKycTier as q, InMemoryAuthCacheAdapter as r, validateHandle as rt, AuthCacheError as s, CapxulError as st, oauthBearerAuthClient as t, toTesterKind as tt, ClockPortTag as u, isCapxulError as ut, BASE_SEPOLIA_CHAIN_ID as v, isChainUpstream as vt, BYTES32_RE as w, redactUrlSecrets as wt, ACCOUNT_ID_RE as x, containsSensitiveMaterial as xt, deriveCapxulSafeAddress as y, isFailureMode as yt, toCountryCode as z };
|
package/dist/capxul-source.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"source": {
|
|
3
|
-
"commit": "
|
|
4
|
-
"tree": "
|
|
3
|
+
"commit": "1c96a26356e8f36402e47eb4ac60bb89d6aeed7f",
|
|
4
|
+
"tree": "375f49f76377eeec331fd44ce17238d4a6f02ad4",
|
|
5
5
|
"branch": "codex/programme-rc",
|
|
6
6
|
"repository": "https://github.com/Xelmar-tech/infrastructure",
|
|
7
7
|
"lockfileSha256": "5870f53b72706d103220c1bcf733cb4fb71196aea32a9cfbdc277d73cc959264"
|
|
8
8
|
},
|
|
9
9
|
"name": "@capxul/sdk",
|
|
10
|
-
"version": "4.2.0-rc.
|
|
10
|
+
"version": "4.2.0-rc.9"
|
|
11
11
|
}
|
package/dist/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { $ as PayrollGroupId, B as ChainId, Ct as ChainCause, D as Account, Dt as RevertSummary, E as CAPXUL_PAYMENTS_V2_ADDRESS, Et as FailureMode, F as AssetId, G as EVM_ADDRESS_RE, H as CurrencyCode, I as AuthSession, J as Handle, L as AuthUserId, O as AccountId, P as AssetAmount, St as CHAIN_UPSTREAMS, Tt as FAILURE_MODES, V as CountryCode, X as OrgId, Y as Money, Z as PartyId, _ as Session, _t as CapxulErrorCode, a as Eip1193RequestProvider, at as RoleKey, c as injectedWalletSigner, ct as TesterKind, d as AccountRequirement, dt as toAddress, et as PayrollRunId, f as Eip1193Provider, ft as toCountryCode, g as Profile, gt as CapxulError, h as CapxulResult, ht as CAPXUL_ERROR_CODES, i as CapxulSigner, k as Address, l as AccountProvider, m as localPrivateKeyAccountProvider, mt as toPartyId, o as SignerStatus, p as eip1193AccountProvider, pt as toHandle, r as CapxulDigestSigner, s as SignerStatusStore, u as AccountProviderSource, v as SmartAccount, vt as CapxulErrorDetails, wt as ChainUpstream, xt as isCapxulError, yt as Errors, z as BudgetId } from "./OAuthBearerAuthClient-C-ip-z8M.mjs";
|
|
2
|
-
import { $ as OrganizationPaymentBatchInput, $n as RequestStatus, $t as Destination, A as OrgScopedMethods, An as Ref, At as AddressBookAddInput, B as AuthorizeRunOptions, Bn as SmartAccountMethods, Bt as ActivityFilter, C as CurrentUserMethods, Cn as PaymentMoney, Ct as AccountMethods, D as MemberStatus, Dn as PaymentsMethods, Dt as ActorRequest, E as InviteMemberInput, En as PaymentType, Et as ActorRelationshipMethods, F as ResendInviteTokenInput, Fn as AccountLifecycle, Ft as InboxItem, G as PayrollGroupsMethods, Gn as ActorRef, Gt as ActivityPage, H as PayrollGroup, Hn as OrgLifecycle, Ht as ActivityKind, I as RoleDefinition, In as AccountSetupStep, It as InboxMethods, J as PayrollRun, Jn as SubmittedPermissionExecution, Jt as ActivitySummary, K as PayrollMethods, Kn as CurrentHoldings, Kt as ActivityRange, L as RoleSpendCap, Ln as isSettingUpLifecycle, Lt as ActivityAnnotation, M as OrgView, Mn as TargetReference, Mt as AddressBookLabelInput, N as OrganizationAccount, Nn as TargetsMethods, Nt as AddressBookMethods, O as MemberView, On as PaymentsPayInput, Ot as ActorRequestIssueInput, P as OrganizationAuditLogItem, Pn as fingerprintPaymentIntent, Pt as InboxApproveInput, Q as PayrollTermsUnit, Qn as PaymentStatus$1, Qt as DepositInstructions, R as RoleView, Rn as CompleteProfileInput, Rt as ActivityAnnotationInput, S as CurrentUserContext, Sn as PaymentDocumentsMethods, St as ReadyAccountLifecycle, T as DetectPendingOrgInvitationsResult, Tn as PaymentTiming, Tt as ActorProfileMethods, U as PayrollGroupInput, Un as OrgSetupStep, Ut as ActivityListParams, V as PayrollEngagementTerms, Vn as AuthMethods, Vt as ActivityItem, W as PayrollGroupMember, Wn as PayoutAddress, Wt as ActivityMethods, X as PayrollRunStatus, Xn as PAYMENT_DIRECTIONS, Xt as ActivitySummaryTotal, Y as PayrollRunItemInput, Yn as InboxStatus, Yt as ActivitySummaryParams, Z as PayrollRuns, Zn as PAYMENT_STATUSES, Zt as ActorReference, _ as HoldingsMethods, _n as PaymentDirection, _t as OnboardingMethods, a as ObservationContext, an as DestinationRemoveInput, ar as IdentityEvent, at as PermissionCreateInput, b as SystemHealth, bn as PaymentDocumentRender, bt as PersonOnboarding, c as HostObservability, cn as MeMethods, cr as Readiness, ct as PermissionReplaceInput, d as postHogObservability, dn as OfframpMethods, dr as isClaimed, dt as Budget, en as DestinationAddInput, et as OrganizationPaymentInput, f as CapxulClient, fn as OfframpQuote, fr as isRestoring, ft as OrgMe, g as IdentityRuntimeSendResult, gn as PaymentActivityEvidence, gt as CompletedPersonProfile, h as IdentityRuntime, hn as Payment, ht as AccountsMethods, i as ObservationAdapter, in as DestinationRail, ir as Destination$1, it as PermissionChangeInput, j as OrgTemplate, jn as ResolvedTarget, jt as AddressBookEntry, k as OrgMethods, kn as RecipientResolution, kt as ActorRequestsMethods, l as PostHogObservabilityClient, ln as MeProfile, lr as StateLabel, lt as PermissionRevokeInput, m as IdentityProfileDetails, mn as OfframpStatus, mr as InvocationControls, mt as OrgMeOptions, nn as DestinationListInput, nt as OrganizationPaymentsMethods, o as ObservationDelivery, on as DestinationsMethods, or as IdentityState, ot as PermissionMethods, p as CreateCapxulClientInput, pn as OfframpQuoteInput, pr as IdentityTransition, pt as OrgMeMethod, q as PayrollOptions, qn as Permission, qt as ActivityReference, r as CAPXUL_SDK_EXPECTED_OUTCOME_EVENT, rn as DestinationPayload, rr as TelemetryPort, rt as PermissionAssignInput, s as SdkFailureObservation, sn as FinancialOpsMethods, sr as OrgLane, st as PermissionOptions, t as CapxulClientInput, tn as DestinationKind, tt as OrganizationPaymentItemInput, u as PostHogObservabilityOptions, un as MovementActivityEvidence, ur as destination, ut as PermissionReadResult, v as Holding, vn as PaymentDocumentKind, vt as OrganizationOnboarding, w as CreateOrgInput, wn as PaymentStatus, wt as ActorProfile, x as MediaMethods, xn as PaymentDocumentVerification, xt as PersonOnboardingInput, y as SystemMethods, yn as PaymentDocumentRef, yt as OrganizationOnboardingInput, z as AuthorizeRunInput, zn as IdentityMethods, zt as ActivityDetail } from "./production-
|
|
2
|
+
import { $ as OrganizationPaymentBatchInput, $n as RequestStatus, $t as Destination, A as OrgScopedMethods, An as Ref, At as AddressBookAddInput, B as AuthorizeRunOptions, Bn as SmartAccountMethods, Bt as ActivityFilter, C as CurrentUserMethods, Cn as PaymentMoney, Ct as AccountMethods, D as MemberStatus, Dn as PaymentsMethods, Dt as ActorRequest, E as InviteMemberInput, En as PaymentType, Et as ActorRelationshipMethods, F as ResendInviteTokenInput, Fn as AccountLifecycle, Ft as InboxItem, G as PayrollGroupsMethods, Gn as ActorRef, Gt as ActivityPage, H as PayrollGroup, Hn as OrgLifecycle, Ht as ActivityKind, I as RoleDefinition, In as AccountSetupStep, It as InboxMethods, J as PayrollRun, Jn as SubmittedPermissionExecution, Jt as ActivitySummary, K as PayrollMethods, Kn as CurrentHoldings, Kt as ActivityRange, L as RoleSpendCap, Ln as isSettingUpLifecycle, Lt as ActivityAnnotation, M as OrgView, Mn as TargetReference, Mt as AddressBookLabelInput, N as OrganizationAccount, Nn as TargetsMethods, Nt as AddressBookMethods, O as MemberView, On as PaymentsPayInput, Ot as ActorRequestIssueInput, P as OrganizationAuditLogItem, Pn as fingerprintPaymentIntent, Pt as InboxApproveInput, Q as PayrollTermsUnit, Qn as PaymentStatus$1, Qt as DepositInstructions, R as RoleView, Rn as CompleteProfileInput, Rt as ActivityAnnotationInput, S as CurrentUserContext, Sn as PaymentDocumentsMethods, St as ReadyAccountLifecycle, T as DetectPendingOrgInvitationsResult, Tn as PaymentTiming, Tt as ActorProfileMethods, U as PayrollGroupInput, Un as OrgSetupStep, Ut as ActivityListParams, V as PayrollEngagementTerms, Vn as AuthMethods, Vt as ActivityItem, W as PayrollGroupMember, Wn as PayoutAddress, Wt as ActivityMethods, X as PayrollRunStatus, Xn as PAYMENT_DIRECTIONS, Xt as ActivitySummaryTotal, Y as PayrollRunItemInput, Yn as InboxStatus, Yt as ActivitySummaryParams, Z as PayrollRuns, Zn as PAYMENT_STATUSES, Zt as ActorReference, _ as HoldingsMethods, _n as PaymentDirection, _t as OnboardingMethods, a as ObservationContext, an as DestinationRemoveInput, ar as IdentityEvent, at as PermissionCreateInput, b as SystemHealth, bn as PaymentDocumentRender, bt as PersonOnboarding, c as HostObservability, cn as MeMethods, cr as Readiness, ct as PermissionReplaceInput, d as postHogObservability, dn as OfframpMethods, dr as isClaimed, dt as Budget, en as DestinationAddInput, et as OrganizationPaymentInput, f as CapxulClient, fn as OfframpQuote, fr as isRestoring, ft as OrgMe, g as IdentityRuntimeSendResult, gn as PaymentActivityEvidence, gt as CompletedPersonProfile, h as IdentityRuntime, hn as Payment, ht as AccountsMethods, i as ObservationAdapter, in as DestinationRail, ir as Destination$1, it as PermissionChangeInput, j as OrgTemplate, jn as ResolvedTarget, jt as AddressBookEntry, k as OrgMethods, kn as RecipientResolution, kt as ActorRequestsMethods, l as PostHogObservabilityClient, ln as MeProfile, lr as StateLabel, lt as PermissionRevokeInput, m as IdentityProfileDetails, mn as OfframpStatus, mr as InvocationControls, mt as OrgMeOptions, nn as DestinationListInput, nt as OrganizationPaymentsMethods, o as ObservationDelivery, on as DestinationsMethods, or as IdentityState, ot as PermissionMethods, p as CreateCapxulClientInput, pn as OfframpQuoteInput, pr as IdentityTransition, pt as OrgMeMethod, q as PayrollOptions, qn as Permission, qt as ActivityReference, r as CAPXUL_SDK_EXPECTED_OUTCOME_EVENT, rn as DestinationPayload, rr as TelemetryPort, rt as PermissionAssignInput, s as SdkFailureObservation, sn as FinancialOpsMethods, sr as OrgLane, st as PermissionOptions, t as CapxulClientInput, tn as DestinationKind, tt as OrganizationPaymentItemInput, u as PostHogObservabilityOptions, un as MovementActivityEvidence, ur as destination, ut as PermissionReadResult, v as Holding, vn as PaymentDocumentKind, vt as OrganizationOnboarding, w as CreateOrgInput, wn as PaymentStatus, wt as ActorProfile, x as MediaMethods, xn as PaymentDocumentVerification, xt as PersonOnboardingInput, y as SystemMethods, yn as PaymentDocumentRef, yt as OrganizationOnboardingInput, z as AuthorizeRunInput, zn as IdentityMethods, zt as ActivityDetail } from "./production-Du4Vc7YD.mjs";
|
|
3
3
|
import { Hex } from "viem";
|
|
4
4
|
import { Effect, Layer } from "effect";
|
|
5
5
|
//#region ../errors/src/index.d.ts
|
|
@@ -7,6 +7,15 @@ import { Effect, Layer } from "effect";
|
|
|
7
7
|
declare const HANDLE_RE: RegExp;
|
|
8
8
|
//#endregion
|
|
9
9
|
//#region ../config/src/tokens.d.ts
|
|
10
|
+
/**
|
|
11
|
+
* Where a registry row's liquidity comes from. `settlement` is the tier the
|
|
12
|
+
* exact-transfer path and the Movement scanner read;
|
|
13
|
+
* `synthetic-test-liquidity` marks an owned fixture whose balances and rates
|
|
14
|
+
* are authored inputs, never market or provider data. Every row states its
|
|
15
|
+
* tier, so a row that reaches a caller through the registry always carries
|
|
16
|
+
* that fact with it.
|
|
17
|
+
*/
|
|
18
|
+
type AssetSource = "settlement" | "synthetic-test-liquidity";
|
|
10
19
|
/** Canonical metadata. A missing peg does not imply a missing market price. */
|
|
11
20
|
interface AssetMetadata {
|
|
12
21
|
readonly assetId: AssetId;
|
|
@@ -16,6 +25,7 @@ interface AssetMetadata {
|
|
|
16
25
|
readonly decimals: number;
|
|
17
26
|
readonly peg: CurrencyCode | null;
|
|
18
27
|
readonly deploymentStartBlock: number;
|
|
28
|
+
readonly source: AssetSource;
|
|
19
29
|
}
|
|
20
30
|
//#endregion
|
|
21
31
|
//#region ../observability/src/operations.d.ts
|
|
@@ -60,6 +70,8 @@ declare const CAPXUL_OPERATIONS: {
|
|
|
60
70
|
readonly emitAnnotationSaved: "activity.emitAnnotationSaved";
|
|
61
71
|
readonly get: "activity.get";
|
|
62
72
|
readonly list: "activity.list";
|
|
73
|
+
/** The SDK's live Activity read over the indexer's SSE stream. */
|
|
74
|
+
readonly subscribe: "activity.subscribe";
|
|
63
75
|
readonly summary: "activity.summary";
|
|
64
76
|
};
|
|
65
77
|
readonly addressBook: {
|
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { A as signerFailure, B as isRestoring, C as normalizeExceptionErrorKind, E as safeExceptionLabel, F as CAPXUL_OPERATIONS, I as isCapxulOperation, L as normalizeCapxulOperation, M as PAYMENT_DIRECTIONS, N as PAYMENT_STATUSES, O as isSettingUpLifecycle, R as destination, S as SDK_VERSION, T as projectSdkException, _ as fingerprintPaymentIntent, b as failureDetail, d as embeddedSigner, f as openfortEmbeddedSigner, g as devPrivateKeySigner, h as deriveDevPrivateKey, j as resolveFailureMode, k as injectedWalletSigner, m as openfortEmbeddedWalletPort, p as openfortEmbeddedSignerFromWallet, r as postHogObservability, t as createCapxulClient$1, w as normalizeExceptionOperation, x as EXCEPTION_MESSAGE, y as CAPXUL_SDK_EXPECTED_OUTCOME_EVENT, z as isClaimed } from "./production-
|
|
2
|
-
import { A as currencySymbolFor,
|
|
1
|
+
import { A as signerFailure, B as isRestoring, C as normalizeExceptionErrorKind, E as safeExceptionLabel, F as CAPXUL_OPERATIONS, I as isCapxulOperation, L as normalizeCapxulOperation, M as PAYMENT_DIRECTIONS, N as PAYMENT_STATUSES, O as isSettingUpLifecycle, R as destination, S as SDK_VERSION, T as projectSdkException, _ as fingerprintPaymentIntent, b as failureDetail, d as embeddedSigner, f as openfortEmbeddedSigner, g as devPrivateKeySigner, h as deriveDevPrivateKey, j as resolveFailureMode, k as injectedWalletSigner, m as openfortEmbeddedWalletPort, p as openfortEmbeddedSignerFromWallet, r as postHogObservability, t as createCapxulClient$1, w as normalizeExceptionOperation, x as EXCEPTION_MESSAGE, y as CAPXUL_SDK_EXPECTED_OUTCOME_EVENT, z as isClaimed } from "./production-216hsAKc.mjs";
|
|
2
|
+
import { A as currencySymbolFor, B as toCurrencyCode, G as toHandle, M as toAddress, T as EVM_ADDRESS_RE, Y as toPartyId, b as configuredMoneyAssetById, dt as CHAIN_UPSTREAMS, ft as FAILURE_MODES, g as CAPXUL_PAYMENTS_V2_ADDRESS, it as HANDLE_RE, k as assetIdFor, lt as Errors, ot as CAPXUL_ERROR_CODES, st as CapxulError, ut as isCapxulError, z as toCountryCode } from "./OAuthBearerAuthClient-IB2W1Fzj.mjs";
|
|
3
3
|
import { formatUnits } from "viem";
|
|
4
4
|
import { Effect } from "effect";
|
|
5
5
|
import { privateKeyToAccount } from "viem/accounts";
|
package/dist/node/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { M as toAddress, a as parseAuthSession, c as AuthCachePortTag, i as BrowserAuthCacheAdapter, o as parseCachedJwt, r as InMemoryAuthCacheAdapter, s as AuthCacheError, t as oauthBearerAuthClient } from "../OAuthBearerAuthClient-
|
|
1
|
+
import { M as toAddress, a as parseAuthSession, c as AuthCachePortTag, i as BrowserAuthCacheAdapter, o as parseCachedJwt, r as InMemoryAuthCacheAdapter, s as AuthCacheError, t as oauthBearerAuthClient } from "../OAuthBearerAuthClient-IB2W1Fzj.mjs";
|
|
2
2
|
import { Effect, FileSystem, Layer, Path } from "effect";
|
|
3
3
|
import { privateKeyToAccount } from "viem/accounts";
|
|
4
4
|
import * as os from "node:os";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $ as
|
|
1
|
+
import { $ as toRoleKey, B as toCurrencyCode, C as ASSET_ID_RE, Ct as redactSecrets, D as WEI_RE, E as SUPPORTED_CURRENCY_CODES, F as toAssetId, G as toHandle, H as toEmail, I as toAuthUserId, J as toOrgId, K as toJwtToken, L as toBudgetId, M as toAddress, N as toAllowedOrigin, O as ZERO_BYTES32, Q as toPublishableKey, R as toChainId, S as APP_ID_RE, St as isCredentialField, T as EVM_ADDRESS_RE$1, U as toEpochMs, V as toDurationMs, W as toEpochSeconds, X as toPayrollGroupId, Y as toPartyId, Z as toPayrollRunId, _ as normalizeBindingEmail, _t as failureFingerprint, at as decodeConvexError, bt as revertSummaryText, c as AuthCachePortTag, ct as EXPECTED_OPERATION_OUTCOMES, d as authClientPortFromPromiseAdapter, dt as CHAIN_UPSTREAMS, et as toSessionToken, f as AuthClientError, ft as FAILURE_MODES$1, gt as decodeChainCause, h as orgRoleKeyForLabel, ht as chainEvidenceLabel, i as BrowserAuthCacheAdapter, j as toAccountId, l as SystemClockLayer, lt as Errors, m as ADMIN_ROLE_LABEL, mt as chainCauseProperties, nt as toTxHash, ot as CAPXUL_ERROR_CODES, p as AuthClientPortTag, pt as boundedResponseHeaders, q as toKycTier, r as InMemoryAuthCacheAdapter, rt as validateHandle, st as CapxulError, tt as toTesterKind, u as ClockPortTag, ut as isCapxulError, v as BASE_SEPOLIA_CHAIN_ID, vt as isChainUpstream, w as BYTES32_RE, wt as redactUrlSecrets, x as ACCOUNT_ID_RE, xt as containsSensitiveMaterial, y as deriveCapxulSafeAddress, yt as isFailureMode, z as toCountryCode } from "./OAuthBearerAuthClient-IB2W1Fzj.mjs";
|
|
2
2
|
import { formatUnits, keccak256, parseUnits, recoverAddress, stringToHex, toBytes } from "viem";
|
|
3
3
|
import { Cause, Clock, Context, Data, Deferred, Duration, Effect, Exit, Fiber, FiberSet, Layer, Option, Queue, Ref, Result, Schedule, Schema, SchemaGetter, SchemaIssue, SchemaParser, Scope, Stream, Tracer } from "effect";
|
|
4
4
|
import { getFunctionName, makeFunctionReference } from "convex/server";
|
|
@@ -448,6 +448,8 @@ const CAPXUL_OPERATIONS = {
|
|
|
448
448
|
emitAnnotationSaved: "activity.emitAnnotationSaved",
|
|
449
449
|
get: "activity.get",
|
|
450
450
|
list: "activity.list",
|
|
451
|
+
/** The SDK's live Activity read over the indexer's SSE stream. */
|
|
452
|
+
subscribe: "activity.subscribe",
|
|
451
453
|
summary: "activity.summary"
|
|
452
454
|
},
|
|
453
455
|
addressBook: {
|
|
@@ -946,6 +948,17 @@ const AuthFailedProps = Schema.Struct({
|
|
|
946
948
|
auth_type: OptionalString,
|
|
947
949
|
reason: OptionalString
|
|
948
950
|
});
|
|
951
|
+
const IdentityRefusedProps = Schema.Struct({
|
|
952
|
+
...TelemetryEnvelopeProps,
|
|
953
|
+
/** The refused identity event tag, for example CreateOrganization. */
|
|
954
|
+
event: OptionalString,
|
|
955
|
+
/** The machine's refusal code: WRONG_STATE, SUPERSEDED or STALE_EPOCH. */
|
|
956
|
+
refusal_code: OptionalString,
|
|
957
|
+
/** The machine slot the request targeted, for example identity:org. */
|
|
958
|
+
slot: OptionalString,
|
|
959
|
+
/** The machine state at refusal, for example authenticated:claiming. */
|
|
960
|
+
state: OptionalString
|
|
961
|
+
});
|
|
949
962
|
const AuthSignedOutProps = Schema.Struct(TelemetryEnvelopeProps);
|
|
950
963
|
const ProvisioningSafeCreatedProps = Schema.Struct({
|
|
951
964
|
...TelemetryEnvelopeProps,
|
|
@@ -1396,6 +1409,10 @@ Schema.Struct({
|
|
|
1396
1409
|
name: Schema.Literal("auth_failed"),
|
|
1397
1410
|
props: AuthFailedProps
|
|
1398
1411
|
});
|
|
1412
|
+
Schema.Struct({
|
|
1413
|
+
name: Schema.Literal("identity_refused"),
|
|
1414
|
+
props: IdentityRefusedProps
|
|
1415
|
+
});
|
|
1399
1416
|
Schema.Struct({
|
|
1400
1417
|
name: Schema.Literal("auth_signed_out"),
|
|
1401
1418
|
props: AuthSignedOutProps
|
|
@@ -2214,6 +2231,8 @@ const BootstrapEnvelope = Schema.Struct({
|
|
|
2214
2231
|
shieldPublishableKey: Schema.String,
|
|
2215
2232
|
/** Which Openfort third-party auth provider the signer presents; absent means better-auth. */
|
|
2216
2233
|
openfortAuthProvider: Schema.optional(Schema.Literals(["better-auth", "oidc"])),
|
|
2234
|
+
/** Origin of the Ponder indexer the SDK reads directly; absent means no indexer reads. */
|
|
2235
|
+
indexerUrl: Schema.optional(Schema.String),
|
|
2217
2236
|
engineeringTelemetry: Schema.optional(EngineeringTelemetryBootstrapPolicy)
|
|
2218
2237
|
})
|
|
2219
2238
|
});
|
|
@@ -2288,6 +2307,7 @@ const CAPXUL_FUNCTIONS = {
|
|
|
2288
2307
|
prepareOrganizationPaymentExecution: "moneyExecution/paymentCommandActions:prepareOrganizationPaymentExecution",
|
|
2289
2308
|
submitPaymentCommandExecution: "moneyExecution/paymentCommandActions:submitPaymentCommandExecution"
|
|
2290
2309
|
},
|
|
2310
|
+
"moneyExecution/providerSendWitness": { read: "moneyExecution/providerSendWitness:read" },
|
|
2291
2311
|
media: {
|
|
2292
2312
|
generateUploadUrl: "media:generateUploadUrl",
|
|
2293
2313
|
setOrgLogo: "media:setOrgLogo",
|
|
@@ -2347,6 +2367,11 @@ const CAPXUL_FUNCTIONS = {
|
|
|
2347
2367
|
loadByAuthUserId: "smartAccount/queries:loadByAuthUserId",
|
|
2348
2368
|
loadBySmartAccountAddress: "smartAccount/queries:loadBySmartAccountAddress"
|
|
2349
2369
|
},
|
|
2370
|
+
e2eControls: {
|
|
2371
|
+
holdUserOperationReceipt: "e2eControls:holdUserOperationReceipt",
|
|
2372
|
+
isUserOperationReceiptHeld: "e2eControls:isUserOperationReceiptHeld",
|
|
2373
|
+
releaseUserOperationReceipt: "e2eControls:releaseUserOperationReceipt"
|
|
2374
|
+
},
|
|
2350
2375
|
system: { health: "system:health" }
|
|
2351
2376
|
};
|
|
2352
2377
|
//#endregion
|
|
@@ -5658,7 +5683,7 @@ const moneyExecutionContract = {
|
|
|
5658
5683
|
};
|
|
5659
5684
|
//#endregion
|
|
5660
5685
|
//#region package.json
|
|
5661
|
-
var version = "4.2.0-rc.
|
|
5686
|
+
var version = "4.2.0-rc.9";
|
|
5662
5687
|
//#endregion
|
|
5663
5688
|
//#region src/telemetry/exception-projection.ts
|
|
5664
5689
|
/** Fixed fallback for failures that have no safe message. */
|
|
@@ -5851,6 +5876,17 @@ function observeFailedResult(result, adapter, operation, origin) {
|
|
|
5851
5876
|
return result;
|
|
5852
5877
|
}
|
|
5853
5878
|
/**
|
|
5879
|
+
* @internal Reports a failure that can never become a method result.
|
|
5880
|
+
*
|
|
5881
|
+
* A reactive stream is torn down after its call already returned, so nothing
|
|
5882
|
+
* downstream observes it. `captureException` is the right kind: the caller did
|
|
5883
|
+
* not ask for the end, so it is not an expected product outcome.
|
|
5884
|
+
*/
|
|
5885
|
+
function observeStreamFailure(adapter, operation, cause) {
|
|
5886
|
+
if (adapter === void 0) return;
|
|
5887
|
+
report(adapter, "exception", operation, cause, resolveAdapterSnapshot(adapter));
|
|
5888
|
+
}
|
|
5889
|
+
/**
|
|
5854
5890
|
* One failure, one event. The identity machine reports a failed transition
|
|
5855
5891
|
* first; when the same `CapxulError` then surfaces as a public method result
|
|
5856
5892
|
* (directly or down its `cause` chain), the boundary drops that second report.
|
|
@@ -8674,7 +8710,7 @@ function isTransientBootstrapFailure(error) {
|
|
|
8674
8710
|
const status = error.details?.httpStatus;
|
|
8675
8711
|
return typeof status === "number" && isTransientHttpStatus(status);
|
|
8676
8712
|
}
|
|
8677
|
-
async function safeText(res) {
|
|
8713
|
+
async function safeText$1(res) {
|
|
8678
8714
|
try {
|
|
8679
8715
|
return await res.text();
|
|
8680
8716
|
} catch {
|
|
@@ -8748,6 +8784,7 @@ var HttpBootstrapAdapter = class {
|
|
|
8748
8784
|
openfortPublishableKey: decodedState.openfortPublishableKey,
|
|
8749
8785
|
shieldPublishableKey: decodedState.shieldPublishableKey,
|
|
8750
8786
|
...decodedState.openfortAuthProvider === void 0 ? {} : { openfortAuthProvider: decodedState.openfortAuthProvider },
|
|
8787
|
+
...decodedState.indexerUrl === void 0 ? {} : { indexerUrl: decodedState.indexerUrl },
|
|
8751
8788
|
...decodedEngineeringTelemetry !== void 0 && Result.isSuccess(decodedEngineeringTelemetry) ? { engineeringTelemetry: decodedEngineeringTelemetry.success } : {}
|
|
8752
8789
|
};
|
|
8753
8790
|
},
|
|
@@ -8756,7 +8793,7 @@ var HttpBootstrapAdapter = class {
|
|
|
8756
8793
|
return bootstrapErrorFromCapxul("malformedBody", Errors.providerError("convex", "bootstrap", cause instanceof Error ? cause : new Error(String(cause))));
|
|
8757
8794
|
}
|
|
8758
8795
|
});
|
|
8759
|
-
return Effect.promise(() => safeText(res)).pipe(Effect.flatMap((body) => {
|
|
8796
|
+
return Effect.promise(() => safeText$1(res)).pipe(Effect.flatMap((body) => {
|
|
8760
8797
|
if (res.status === 401 || body.startsWith("NOT_AUTHENTICATED")) return Effect.fail(bootstrapErrorFromCapxul("notAuthenticated", Errors.notAuthenticated()));
|
|
8761
8798
|
if (res.status === 400 || body.startsWith("INVALID_INPUT")) return Effect.fail(bootstrapErrorFromCapxul("invalidInput", Errors.invalidInput("publishableKey", "rejected by bootstrap")));
|
|
8762
8799
|
const responseBody = body.slice(0, 300);
|
|
@@ -8986,6 +9023,12 @@ function makeConnectionId() {
|
|
|
8986
9023
|
}
|
|
8987
9024
|
//#endregion
|
|
8988
9025
|
//#region src/adapters/convex-call/ConvexCallAdapter.ts
|
|
9026
|
+
/**
|
|
9027
|
+
* A socket that has not emitted `close` within this window is not going to.
|
|
9028
|
+
* ponytail: one fixed bound; `closeTimeoutMs` is the knob if a slow runtime
|
|
9029
|
+
* ever needs a different one.
|
|
9030
|
+
*/
|
|
9031
|
+
const DEFAULT_CLOSE_TIMEOUT_MS = 5e3;
|
|
8989
9032
|
/** Exact floor-first allowlist; every additional handler must migrate its validator first. */
|
|
8990
9033
|
const OBSERVED_CONVEX_ACTIONS = /* @__PURE__ */ new Set([
|
|
8991
9034
|
"payroll/actions:authorizeRun",
|
|
@@ -9054,7 +9097,9 @@ var ConvexCallAdapter = class {
|
|
|
9054
9097
|
#applicationId;
|
|
9055
9098
|
#observation;
|
|
9056
9099
|
#connectionMonitor;
|
|
9100
|
+
#closeTimeoutMs;
|
|
9057
9101
|
constructor(deps) {
|
|
9102
|
+
this.#closeTimeoutMs = deps.closeTimeoutMs ?? DEFAULT_CLOSE_TIMEOUT_MS;
|
|
9058
9103
|
this.#connectionMonitor = new ConvexConnectionMonitor();
|
|
9059
9104
|
const webSocketConstructor = this.#connectionMonitor.observedWebSocketConstructor();
|
|
9060
9105
|
this.#client = deps.client ?? new ConvexClient(deps.convexUrl, webSocketConstructor === void 0 ? {} : { webSocketConstructor });
|
|
@@ -9153,11 +9198,33 @@ var ConvexCallAdapter = class {
|
|
|
9153
9198
|
callback({ status: "loading" });
|
|
9154
9199
|
})));
|
|
9155
9200
|
}
|
|
9201
|
+
/**
|
|
9202
|
+
* Teardown always completes. `ConvexClient.close()` resolves only when the
|
|
9203
|
+
* socket emits `close`, and `WebSocketManager.close()` in the `connecting`
|
|
9204
|
+
* state arms `ws.onopen = () => ws.close()` without ever closing the socket
|
|
9205
|
+
* itself — so a handshake that stalls right after a server-initiated close
|
|
9206
|
+
* (code 1012, which is what a Convex deploy sends) leaves that promise
|
|
9207
|
+
* pending for as long as the page lives. Convex marks the client closed and
|
|
9208
|
+
* terminates the socket manager synchronously before returning it, so
|
|
9209
|
+
* abandoning it drops nothing. A rejection is dropped for the same reason —
|
|
9210
|
+
* the layer release turns it into a defect that fails the whole scope close,
|
|
9211
|
+
* and teardown has no caller who can act on it.
|
|
9212
|
+
*/
|
|
9156
9213
|
async close() {
|
|
9157
9214
|
this.#connectionMonitor.close();
|
|
9158
|
-
await this.#client.close();
|
|
9215
|
+
await settledWithin(this.#client.close(), this.#closeTimeoutMs);
|
|
9159
9216
|
}
|
|
9160
9217
|
};
|
|
9218
|
+
function settledWithin(promise, timeoutMs) {
|
|
9219
|
+
return new Promise((resolve) => {
|
|
9220
|
+
const timer = setTimeout(resolve, timeoutMs);
|
|
9221
|
+
const finish = () => {
|
|
9222
|
+
clearTimeout(timer);
|
|
9223
|
+
resolve();
|
|
9224
|
+
};
|
|
9225
|
+
promise.then(finish, finish);
|
|
9226
|
+
});
|
|
9227
|
+
}
|
|
9161
9228
|
function ConvexCallLayer(deps) {
|
|
9162
9229
|
return Layer.effect(ConvexCallPortTag, Effect.acquireRelease(Effect.sync(() => new ConvexCallAdapter(deps)), (adapter) => Effect.promise(() => adapter.close()).pipe(Effect.orDie)));
|
|
9163
9230
|
}
|
|
@@ -13338,11 +13405,28 @@ const authFailure = (record, reason, mode) => ({
|
|
|
13338
13405
|
...linkage(record)
|
|
13339
13406
|
}
|
|
13340
13407
|
});
|
|
13408
|
+
const REFUSAL_EVENTS = /* @__PURE__ */ new Set([
|
|
13409
|
+
"ClaimAccount",
|
|
13410
|
+
"RetryAccount",
|
|
13411
|
+
"CreateOrganization",
|
|
13412
|
+
"AttachOrganization",
|
|
13413
|
+
"RetryOrganization"
|
|
13414
|
+
]);
|
|
13341
13415
|
function mapIdentityProductObservation(record, state) {
|
|
13342
13416
|
if (record.machine !== "identity" || !IDENTITY_EVENT_TAG_SET.has(record.event)) return NONE;
|
|
13343
13417
|
if (record.outcome === "cancelled") return NONE;
|
|
13344
13418
|
if (record.slot === "identity:auth" && record.outcome === "refused") return authFailure(record, record.refusal_code, void 0);
|
|
13345
13419
|
if (record.slot === "identity:auth" && record.outcome === "failed") return authFailure(record, record.error_code, record.failure?.mode);
|
|
13420
|
+
if (record.outcome === "refused" && REFUSAL_EVENTS.has(record.event)) return {
|
|
13421
|
+
name: "identity_refused",
|
|
13422
|
+
props: {
|
|
13423
|
+
event: record.event,
|
|
13424
|
+
refusal_code: record.refusal_code,
|
|
13425
|
+
slot: record.slot,
|
|
13426
|
+
state: record.state,
|
|
13427
|
+
...linkage(record)
|
|
13428
|
+
}
|
|
13429
|
+
};
|
|
13346
13430
|
if (record.outcome !== "applied") return NONE;
|
|
13347
13431
|
switch (APPLIED_ACTION_BY_EVENT[record.event]) {
|
|
13348
13432
|
case "none": return NONE;
|
|
@@ -13668,6 +13752,7 @@ function assembleCapxulClient(input) {
|
|
|
13668
13752
|
identity: identityRuntime,
|
|
13669
13753
|
bootstrap: input.bootstrap,
|
|
13670
13754
|
accounts: { fund: accounts.fund },
|
|
13755
|
+
...input.ports.indexerRead === void 0 ? {} : { indexer: input.ports.indexerRead },
|
|
13671
13756
|
organizationSetup: { getProofReceipt: (orgId) => runPortEffect(input.ports.convexCall.query(organizationSetupProofReceiptQuery, { orgId })) },
|
|
13672
13757
|
telemetry: input.ports.telemetry,
|
|
13673
13758
|
captureExternalAddressAcknowledged: (address, organizationId) => {
|
|
@@ -13869,6 +13954,382 @@ function snapshotHostObservability(observability) {
|
|
|
13869
13954
|
function isPromiseLike(value) {
|
|
13870
13955
|
return (typeof value === "object" && value !== null || typeof value === "function") && "then" in value;
|
|
13871
13956
|
}
|
|
13957
|
+
//#endregion
|
|
13958
|
+
//#region src/ports/indexer-read.ts
|
|
13959
|
+
/** The indexer's own page bound. Exceeding it is a caller defect, not a refusal. */
|
|
13960
|
+
const INDEXER_MAX_LIMIT = 1e3;
|
|
13961
|
+
const LOWER_CASE_ADDRESS = /^0x[0-9a-f]{40}$/;
|
|
13962
|
+
/**
|
|
13963
|
+
* Refuse a page the indexer would refuse, before any implementation spends a
|
|
13964
|
+
* round trip on it. Every implementation calls this, so a consumer test written
|
|
13965
|
+
* against the hermetic adapter cannot pass on input production rejects. Throws
|
|
13966
|
+
* a `CapxulError`; callers convert it to an `IndexerReadError`.
|
|
13967
|
+
*/
|
|
13968
|
+
function assertIndexerPage(input) {
|
|
13969
|
+
const safe = String(input.safe).toLowerCase();
|
|
13970
|
+
if (!LOWER_CASE_ADDRESS.test(safe)) throw Errors.invalidInput("safe", "must be a lower-case EVM address");
|
|
13971
|
+
if (!Number.isSafeInteger(input.limit) || input.limit < 1 || input.limit > 1e3) throw Errors.invalidInput("limit", `must be an integer from 1 to ${INDEXER_MAX_LIMIT}`);
|
|
13972
|
+
const offset = input.offset ?? 0;
|
|
13973
|
+
if (!Number.isSafeInteger(offset) || offset < 0) throw Errors.invalidInput("offset", "must be a non-negative integer");
|
|
13974
|
+
return safe;
|
|
13975
|
+
}
|
|
13976
|
+
var IndexerReadError = class extends Data.TaggedError("IndexerReadError") {};
|
|
13977
|
+
function indexerReadErrorFromCapxul(operation, error, cause = error) {
|
|
13978
|
+
return new IndexerReadError({
|
|
13979
|
+
operation,
|
|
13980
|
+
publicCode: error.code,
|
|
13981
|
+
publicError: error,
|
|
13982
|
+
cause,
|
|
13983
|
+
...error.details === void 0 ? {} : { details: error.details }
|
|
13984
|
+
});
|
|
13985
|
+
}
|
|
13986
|
+
Context.Service()("@capxul/sdk/ports/IndexerReadPort");
|
|
13987
|
+
//#endregion
|
|
13988
|
+
//#region src/adapters/ponder/decode.ts
|
|
13989
|
+
/**
|
|
13990
|
+
* Decode the indexer's rows at the trust boundary.
|
|
13991
|
+
*
|
|
13992
|
+
* Ponder returns the raw PostgreSQL result: `integer` arrives as a number,
|
|
13993
|
+
* `numeric(78)` and `text` arrive as strings. Amounts, block numbers and
|
|
13994
|
+
* timestamps are `numeric(78)`, so they become `bigint` here and never pass
|
|
13995
|
+
* through `Number`. Every decoder throws a `CapxulError`; the adapter turns
|
|
13996
|
+
* that into a typed `IndexerReadError`.
|
|
13997
|
+
*/
|
|
13998
|
+
const HEX = /^0x[0-9a-fA-F]+$/;
|
|
13999
|
+
function field(row, key) {
|
|
14000
|
+
if (!(key in row)) throw Errors.invalidInput(key, "missing from the indexer row");
|
|
14001
|
+
return row[key];
|
|
14002
|
+
}
|
|
14003
|
+
function text(row, key) {
|
|
14004
|
+
const value = field(row, key);
|
|
14005
|
+
if (typeof value !== "string") throw Errors.invalidInput(key, "must be a string");
|
|
14006
|
+
return value;
|
|
14007
|
+
}
|
|
14008
|
+
function hex(row, key) {
|
|
14009
|
+
const value = text(row, key);
|
|
14010
|
+
if (!HEX.test(value)) throw Errors.invalidInput(key, "must be a hex string");
|
|
14011
|
+
return value.toLowerCase();
|
|
14012
|
+
}
|
|
14013
|
+
function integer(row, key) {
|
|
14014
|
+
const value = field(row, key);
|
|
14015
|
+
const parsed = typeof value === "string" ? Number(value) : value;
|
|
14016
|
+
if (typeof parsed !== "number" || !Number.isSafeInteger(parsed)) throw Errors.invalidInput(key, "must be an integer");
|
|
14017
|
+
return parsed;
|
|
14018
|
+
}
|
|
14019
|
+
/** `numeric(78)` crosses the wire as a decimal string; keep every digit. */
|
|
14020
|
+
function big(row, key) {
|
|
14021
|
+
const value = field(row, key);
|
|
14022
|
+
if (typeof value === "bigint") return value;
|
|
14023
|
+
const raw = typeof value === "number" ? String(value) : value;
|
|
14024
|
+
if (typeof raw !== "string" || !/^-?[0-9]+$/.test(raw)) throw Errors.invalidInput(key, "must be an exact integer amount");
|
|
14025
|
+
return BigInt(raw);
|
|
14026
|
+
}
|
|
14027
|
+
function asRow(value) {
|
|
14028
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) throw Errors.invalidInput("row", "must be an object");
|
|
14029
|
+
return value;
|
|
14030
|
+
}
|
|
14031
|
+
function identity(row) {
|
|
14032
|
+
return {
|
|
14033
|
+
id: text(row, "id"),
|
|
14034
|
+
chainId: toChainId(integer(row, "chain_id")),
|
|
14035
|
+
txHash: toTxHash(hex(row, "tx_hash")),
|
|
14036
|
+
transactionLogOrdinal: integer(row, "transaction_log_ordinal"),
|
|
14037
|
+
blockNumber: big(row, "block_number"),
|
|
14038
|
+
blockHash: hex(row, "block_hash"),
|
|
14039
|
+
timestamp: big(row, "timestamp")
|
|
14040
|
+
};
|
|
14041
|
+
}
|
|
14042
|
+
function decodeTransfer(value) {
|
|
14043
|
+
const row = asRow(value);
|
|
14044
|
+
return {
|
|
14045
|
+
...identity(row),
|
|
14046
|
+
assetId: toAssetId(text(row, "asset_id")),
|
|
14047
|
+
token: toAddress(hex(row, "token")),
|
|
14048
|
+
from: toAddress(hex(row, "from")),
|
|
14049
|
+
to: toAddress(hex(row, "to")),
|
|
14050
|
+
amount: big(row, "amount")
|
|
14051
|
+
};
|
|
14052
|
+
}
|
|
14053
|
+
function decodePayment(value) {
|
|
14054
|
+
const row = asRow(value);
|
|
14055
|
+
return {
|
|
14056
|
+
...identity(row),
|
|
14057
|
+
contract: toAddress(hex(row, "contract")),
|
|
14058
|
+
settlementId: hex(row, "settlement_id"),
|
|
14059
|
+
sender: toAddress(hex(row, "sender")),
|
|
14060
|
+
recipient: toAddress(hex(row, "recipient")),
|
|
14061
|
+
assetId: toAssetId(text(row, "asset_id")),
|
|
14062
|
+
token: toAddress(hex(row, "token")),
|
|
14063
|
+
amount: big(row, "amount"),
|
|
14064
|
+
kind: integer(row, "kind"),
|
|
14065
|
+
documentHash: hex(row, "document_hash")
|
|
14066
|
+
};
|
|
14067
|
+
}
|
|
14068
|
+
/** The `balance` view exposes the signed sum under the underlying column name `to`. */
|
|
14069
|
+
function decodeBalance(value) {
|
|
14070
|
+
const row = asRow(value);
|
|
14071
|
+
return {
|
|
14072
|
+
chainId: toChainId(integer(row, "chain_id")),
|
|
14073
|
+
assetId: toAssetId(text(row, "asset_id")),
|
|
14074
|
+
address: toAddress(hex(row, "to")),
|
|
14075
|
+
amount: big(row, "amount")
|
|
14076
|
+
};
|
|
14077
|
+
}
|
|
14078
|
+
const DECODERS = {
|
|
14079
|
+
transfer: decodeTransfer,
|
|
14080
|
+
payment: decodePayment,
|
|
14081
|
+
balance: decodeBalance
|
|
14082
|
+
};
|
|
14083
|
+
/** Decode one `/sql/db` or `/sql/live` payload. Ponder returns `{ rows: [...] }`. */
|
|
14084
|
+
function decodeRows(table, body) {
|
|
14085
|
+
const rows = asRow(body).rows;
|
|
14086
|
+
if (!Array.isArray(rows)) throw Errors.invalidInput("rows", "must be an array");
|
|
14087
|
+
const decode = DECODERS[table];
|
|
14088
|
+
return rows.map(decode);
|
|
14089
|
+
}
|
|
14090
|
+
/** Decode Ponder's native `/status`: `{ [chainName]: { id, block: { number, timestamp } } }`. */
|
|
14091
|
+
function decodeStatus(body) {
|
|
14092
|
+
const chains = asRow(body);
|
|
14093
|
+
return Object.entries(chains).map(([chain, value]) => {
|
|
14094
|
+
const entry = asRow(value);
|
|
14095
|
+
const block = asRow(field(entry, "block"));
|
|
14096
|
+
return {
|
|
14097
|
+
chain,
|
|
14098
|
+
chainId: toChainId(integer(entry, "id")),
|
|
14099
|
+
blockNumber: big(block, "number"),
|
|
14100
|
+
blockTimestamp: big(block, "timestamp")
|
|
14101
|
+
};
|
|
14102
|
+
});
|
|
14103
|
+
}
|
|
14104
|
+
//#endregion
|
|
14105
|
+
//#region src/adapters/ponder/statements.ts
|
|
14106
|
+
/**
|
|
14107
|
+
* The exact statements the indexer accepts.
|
|
14108
|
+
*
|
|
14109
|
+
* `apps/indexer/src/queries.ts` compiles its allowlist with `@ponder/client`
|
|
14110
|
+
* and compares `sql`, `params` and `typings` byte for byte against the caller's
|
|
14111
|
+
* statement. Anything else gets 403. The SDK therefore reproduces the compiled
|
|
14112
|
+
* text here rather than pulling Ponder and Drizzle into a published browser
|
|
14113
|
+
* bundle to regenerate three fixed statements.
|
|
14114
|
+
*
|
|
14115
|
+
* `statements.test.ts` pins the text. When the indexer schema or a query in
|
|
14116
|
+
* that allowlist changes, this file changes with it or every read fails 403.
|
|
14117
|
+
*/
|
|
14118
|
+
const HEADS = {
|
|
14119
|
+
transfer: "select \"id\", \"chain_id\", \"tx_hash\", \"transaction_log_ordinal\", \"block_number\", \"block_hash\", \"timestamp\", \"asset_id\", \"token\", \"from\", \"to\", \"amount\" from \"transfer\" where (\"transfer\".\"from\" = $1 or \"transfer\".\"to\" = $2) order by \"transfer\".\"block_number\" desc, \"transfer\".\"id\" desc",
|
|
14120
|
+
payment: "select \"id\", \"chain_id\", \"tx_hash\", \"transaction_log_ordinal\", \"block_number\", \"block_hash\", \"timestamp\", \"contract\", \"settlement_id\", \"sender\", \"recipient\", \"asset_id\", \"token\", \"amount\", \"kind\", \"document_hash\" from \"payment\" where (\"payment\".\"sender\" = $1 or \"payment\".\"recipient\" = $2) order by \"payment\".\"block_number\" desc, \"payment\".\"id\" desc",
|
|
14121
|
+
balance: "select \"chain_id\", \"asset_id\", \"to\", \"amount\" from \"balance\" where \"balance\".\"to\" = $1"
|
|
14122
|
+
};
|
|
14123
|
+
/** How many times the statement binds the Safe address. */
|
|
14124
|
+
const ACTOR_PARAMS = {
|
|
14125
|
+
transfer: 2,
|
|
14126
|
+
payment: 2,
|
|
14127
|
+
balance: 1
|
|
14128
|
+
};
|
|
14129
|
+
/**
|
|
14130
|
+
* Build the compiled query for one table and page. Throws a `CapxulError` for
|
|
14131
|
+
* a page or address the indexer would refuse, so the caller never spends a
|
|
14132
|
+
* round trip to learn it built the request wrong.
|
|
14133
|
+
*/
|
|
14134
|
+
function ponderQuery(table, input) {
|
|
14135
|
+
const safe = assertIndexerPage(input);
|
|
14136
|
+
const offset = input.offset ?? 0;
|
|
14137
|
+
const actors = ACTOR_PARAMS[table];
|
|
14138
|
+
const params = [];
|
|
14139
|
+
for (let i = 0; i < actors; i++) params.push(safe);
|
|
14140
|
+
params.push(input.limit);
|
|
14141
|
+
let sql = `${HEADS[table]} limit $${actors + 1}`;
|
|
14142
|
+
if (offset > 0) {
|
|
14143
|
+
params.push(offset);
|
|
14144
|
+
sql = `${sql} offset $${actors + 2}`;
|
|
14145
|
+
}
|
|
14146
|
+
return {
|
|
14147
|
+
sql,
|
|
14148
|
+
params,
|
|
14149
|
+
typings: params.map(() => "none")
|
|
14150
|
+
};
|
|
14151
|
+
}
|
|
14152
|
+
/**
|
|
14153
|
+
* The `?sql=` value. `@ponder/client` sends `superjson.stringify(query)`; for a
|
|
14154
|
+
* payload of strings and numbers superjson emits no `meta`, so the wire form is
|
|
14155
|
+
* exactly this envelope.
|
|
14156
|
+
*/
|
|
14157
|
+
function ponderQueryParam(query) {
|
|
14158
|
+
return JSON.stringify({ json: query });
|
|
14159
|
+
}
|
|
14160
|
+
//#endregion
|
|
14161
|
+
//#region src/adapters/ponder/sse.ts
|
|
14162
|
+
/**
|
|
14163
|
+
* The smallest SSE reader that can carry an `Authorization` header.
|
|
14164
|
+
*
|
|
14165
|
+
* `@ponder/client` opens its live query with `EventSource`, which accepts no
|
|
14166
|
+
* headers, so it cannot reach the guarded indexer at all. The indexer's CORS
|
|
14167
|
+
* policy allows exactly one request header (`Authorization`), so this reader
|
|
14168
|
+
* sends nothing else. Ponder writes one `data:` line per frame.
|
|
14169
|
+
*/
|
|
14170
|
+
async function readEventStream(body, onData, stopped) {
|
|
14171
|
+
const reader = body.getReader();
|
|
14172
|
+
const decoder = new TextDecoder();
|
|
14173
|
+
let buffer = "";
|
|
14174
|
+
try {
|
|
14175
|
+
for (;;) {
|
|
14176
|
+
const { done, value } = await reader.read();
|
|
14177
|
+
if (done || stopped()) return;
|
|
14178
|
+
buffer += decoder.decode(value, { stream: true });
|
|
14179
|
+
let split = buffer.indexOf("\n\n");
|
|
14180
|
+
while (split !== -1) {
|
|
14181
|
+
const frame = buffer.slice(0, split);
|
|
14182
|
+
buffer = buffer.slice(split + 2);
|
|
14183
|
+
const data = frameData(frame);
|
|
14184
|
+
if (data !== void 0) onData(data);
|
|
14185
|
+
if (stopped()) return;
|
|
14186
|
+
split = buffer.indexOf("\n\n");
|
|
14187
|
+
}
|
|
14188
|
+
}
|
|
14189
|
+
} finally {
|
|
14190
|
+
await reader.cancel().catch(() => void 0);
|
|
14191
|
+
}
|
|
14192
|
+
}
|
|
14193
|
+
function frameData(frame) {
|
|
14194
|
+
const lines = [];
|
|
14195
|
+
for (const line of frame.split("\n")) {
|
|
14196
|
+
if (!line.startsWith("data:")) continue;
|
|
14197
|
+
lines.push(line.slice(line.startsWith("data: ") ? 6 : 5));
|
|
14198
|
+
}
|
|
14199
|
+
return lines.length === 0 ? void 0 : lines.join("\n");
|
|
14200
|
+
}
|
|
14201
|
+
//#endregion
|
|
14202
|
+
//#region src/adapters/ponder/PonderIndexerAdapter.ts
|
|
14203
|
+
var PonderIndexerAdapter = class {
|
|
14204
|
+
#baseUrl;
|
|
14205
|
+
#token;
|
|
14206
|
+
#fetch;
|
|
14207
|
+
#observation;
|
|
14208
|
+
constructor(deps) {
|
|
14209
|
+
this.#baseUrl = deps.baseUrl.replace(/\/$/, "");
|
|
14210
|
+
this.#token = deps.token;
|
|
14211
|
+
this.#observation = deps.observation;
|
|
14212
|
+
this.#fetch = deps.fetch ?? ((input, init) => globalThis.fetch(input, init));
|
|
14213
|
+
}
|
|
14214
|
+
read(table, input) {
|
|
14215
|
+
const operation = `indexer.read.${table}`;
|
|
14216
|
+
return Effect.tryPromise({
|
|
14217
|
+
try: async () => {
|
|
14218
|
+
return decodeRows(table, await (await this.#request("db", table, input, operation)).json());
|
|
14219
|
+
},
|
|
14220
|
+
catch: (cause) => toIndexerReadError(operation, cause)
|
|
14221
|
+
});
|
|
14222
|
+
}
|
|
14223
|
+
subscribe(table, input, callback) {
|
|
14224
|
+
const operation = CAPXUL_OPERATIONS.activity.subscribe;
|
|
14225
|
+
return Effect.sync(() => {
|
|
14226
|
+
let stopped = false;
|
|
14227
|
+
const unsubscribe = () => {
|
|
14228
|
+
stopped = true;
|
|
14229
|
+
};
|
|
14230
|
+
callback({ status: "loading" });
|
|
14231
|
+
this.#stream(table, input, operation, callback, () => stopped).catch(() => void 0);
|
|
14232
|
+
return unsubscribe;
|
|
14233
|
+
});
|
|
14234
|
+
}
|
|
14235
|
+
status = Effect.tryPromise({
|
|
14236
|
+
try: async () => {
|
|
14237
|
+
const response = await this.#fetch(`${this.#baseUrl}/status`, { method: "GET" });
|
|
14238
|
+
if (!response.ok) throw httpError("indexer.status", response.status, await safeText(response));
|
|
14239
|
+
return decodeStatus(await response.json());
|
|
14240
|
+
},
|
|
14241
|
+
catch: (cause) => toIndexerReadError("indexer.status", cause)
|
|
14242
|
+
});
|
|
14243
|
+
/** One authenticated `/sql/:method` call. Throws a `CapxulError` on refusal. */
|
|
14244
|
+
async #request(method, table, input, operation) {
|
|
14245
|
+
const query = ponderQuery(table, input);
|
|
14246
|
+
const token = await this.#token({ forceRefreshToken: false });
|
|
14247
|
+
if (token === null || token === "") throw Errors.notAuthenticated();
|
|
14248
|
+
const url = `${this.#baseUrl}/sql/${method}?sql=${encodeURIComponent(ponderQueryParam(query))}`;
|
|
14249
|
+
const headers = { authorization: `Bearer ${token}` };
|
|
14250
|
+
if (method === "live") headers.accept = "text/event-stream";
|
|
14251
|
+
let response;
|
|
14252
|
+
try {
|
|
14253
|
+
response = await this.#fetch(url, {
|
|
14254
|
+
method: "GET",
|
|
14255
|
+
headers
|
|
14256
|
+
});
|
|
14257
|
+
} catch (cause) {
|
|
14258
|
+
throw Errors.networkError(operation, cause, {
|
|
14259
|
+
provider: "indexer",
|
|
14260
|
+
failure_mode: "upstream-down"
|
|
14261
|
+
});
|
|
14262
|
+
}
|
|
14263
|
+
if (!response.ok) throw httpError(operation, response.status, await safeText(response));
|
|
14264
|
+
return response;
|
|
14265
|
+
}
|
|
14266
|
+
/**
|
|
14267
|
+
* Follow the live stream until the caller unsubscribes or the indexer closes
|
|
14268
|
+
* it. The indexer closes the stream on token expiry or revoked authority
|
|
14269
|
+
* with no frame, so any end the caller did not ask for is a teardown: it
|
|
14270
|
+
* reaches `captureException` under `activity.subscribe` and the caller sees
|
|
14271
|
+
* one `error` snapshot.
|
|
14272
|
+
*/
|
|
14273
|
+
async #stream(table, input, operation, callback, stopped) {
|
|
14274
|
+
let failure;
|
|
14275
|
+
try {
|
|
14276
|
+
const response = await this.#request("live", table, input, operation);
|
|
14277
|
+
if (response.body === null) throw Errors.providerError("indexer", operation, "empty stream");
|
|
14278
|
+
await readEventStream(response.body, (data) => {
|
|
14279
|
+
try {
|
|
14280
|
+
callback({
|
|
14281
|
+
status: "ok",
|
|
14282
|
+
value: decodeRows(table, JSON.parse(data))
|
|
14283
|
+
});
|
|
14284
|
+
} catch (cause) {
|
|
14285
|
+
failure = toPublicError$1(operation, cause);
|
|
14286
|
+
}
|
|
14287
|
+
}, () => stopped() || failure !== void 0);
|
|
14288
|
+
} catch (cause) {
|
|
14289
|
+
failure = toPublicError$1(operation, cause);
|
|
14290
|
+
}
|
|
14291
|
+
if (stopped()) return;
|
|
14292
|
+
const error = failure ?? Errors.capabilityUnavailable("indexer", operation);
|
|
14293
|
+
observeStreamFailure(this.#observation, CAPXUL_OPERATIONS.activity.subscribe, error);
|
|
14294
|
+
callback({
|
|
14295
|
+
status: "error",
|
|
14296
|
+
error
|
|
14297
|
+
});
|
|
14298
|
+
}
|
|
14299
|
+
};
|
|
14300
|
+
/**
|
|
14301
|
+
* Map an indexer refusal into the shared error vocabulary.
|
|
14302
|
+
*
|
|
14303
|
+
* 401 is the expected outcome of an expired or revoked session. 403 is not:
|
|
14304
|
+
* the indexer accepts only its own compiled statements for a Safe in the
|
|
14305
|
+
* claim, so a refusal means this SDK and that deployment disagree, or the
|
|
14306
|
+
* session gained a Safe and still holds the older token. Both must be loud.
|
|
14307
|
+
*/
|
|
14308
|
+
function httpError(operation, status, body) {
|
|
14309
|
+
if (status === 401) return Errors.notAuthenticated();
|
|
14310
|
+
if (status === 429) return Errors.rateLimited({ resource: "indexer" });
|
|
14311
|
+
if (status === 503) return Errors.capabilityUnavailable("indexer", operation);
|
|
14312
|
+
return Errors.providerError("indexer", operation, body, {
|
|
14313
|
+
httpStatus: status,
|
|
14314
|
+
...status >= 500 ? { failure_mode: "upstream-down" } : {},
|
|
14315
|
+
details: { reason: status === 403 ? "scope-refused" : "http-status" }
|
|
14316
|
+
});
|
|
14317
|
+
}
|
|
14318
|
+
function toPublicError$1(operation, cause) {
|
|
14319
|
+
if (cause instanceof CapxulError) return cause;
|
|
14320
|
+
return Errors.providerError("indexer", operation, cause);
|
|
14321
|
+
}
|
|
14322
|
+
function toIndexerReadError(operation, cause) {
|
|
14323
|
+
if (cause instanceof IndexerReadError) return cause;
|
|
14324
|
+
return indexerReadErrorFromCapxul(operation, toPublicError$1(operation, cause), cause);
|
|
14325
|
+
}
|
|
14326
|
+
async function safeText(response) {
|
|
14327
|
+
try {
|
|
14328
|
+
return await response.text();
|
|
14329
|
+
} catch {
|
|
14330
|
+
return "";
|
|
14331
|
+
}
|
|
14332
|
+
}
|
|
13872
14333
|
const SDK_VERSION = version;
|
|
13873
14334
|
/**
|
|
13874
14335
|
* Project the built graph into the flat `FlowPorts` record the method bundles
|
|
@@ -14118,7 +14579,16 @@ async function createProductionAdapters(input) {
|
|
|
14118
14579
|
}));
|
|
14119
14580
|
const applicationLayer = Layer.merge(portsLayer, optionalEngineeringTelemetryLayer(bootstrapResult.value.engineeringTelemetry, resolvedInput.value.runtime));
|
|
14120
14581
|
const context = await Effect.runPromise(Layer.buildWithScope(applicationLayer, scope));
|
|
14121
|
-
const
|
|
14582
|
+
const collected = await Effect.runPromise(collectProductionFlowPorts.pipe(Effect.provide(context)));
|
|
14583
|
+
const indexerUrl = input.indexerUrl ?? bootstrapResult.value.indexerUrl;
|
|
14584
|
+
const indexerRead = productionIndexerReadPort(indexerUrl === void 0 ? input : {
|
|
14585
|
+
...input,
|
|
14586
|
+
indexerUrl
|
|
14587
|
+
}, collected, observation);
|
|
14588
|
+
const ports = indexerRead === void 0 ? collected : {
|
|
14589
|
+
...collected,
|
|
14590
|
+
indexerRead
|
|
14591
|
+
};
|
|
14122
14592
|
const stopConnectionObservation = observeProductionConvexConnection(ports.convexCall, context);
|
|
14123
14593
|
const close = idempotentClose(async () => {
|
|
14124
14594
|
try {
|
|
@@ -14450,5 +14920,27 @@ function idempotentClose(close) {
|
|
|
14450
14920
|
await close();
|
|
14451
14921
|
};
|
|
14452
14922
|
}
|
|
14923
|
+
/**
|
|
14924
|
+
* Build the Ponder read port for this client, or `undefined` when no indexer
|
|
14925
|
+
* origin was supplied. The token provider is the same
|
|
14926
|
+
* `AuthClientPort.getConvexJwt` closure `ConvexCallPort` uses, so the indexer
|
|
14927
|
+
* and Convex see one session with one scope.
|
|
14928
|
+
*/
|
|
14929
|
+
function productionIndexerReadPort(input, ports, observation) {
|
|
14930
|
+
if (input.indexerUrl === void 0 || input.indexerUrl === "") return void 0;
|
|
14931
|
+
return new PonderIndexerAdapter({
|
|
14932
|
+
baseUrl: normalizeHttpUrl("indexerUrl", input.indexerUrl),
|
|
14933
|
+
token: async ({ forceRefreshToken }) => {
|
|
14934
|
+
const token = await Effect.runPromise(Effect.result(ports.authClient.getConvexJwt({
|
|
14935
|
+
forceRefresh: forceRefreshToken,
|
|
14936
|
+
...input.signal === void 0 ? {} : { signal: input.signal }
|
|
14937
|
+
})));
|
|
14938
|
+
if (Result.isFailure(token)) return null;
|
|
14939
|
+
return String(token.success.token);
|
|
14940
|
+
},
|
|
14941
|
+
...input.fetch === void 0 ? {} : { fetch: input.fetch },
|
|
14942
|
+
...observation === void 0 ? {} : { observation }
|
|
14943
|
+
});
|
|
14944
|
+
}
|
|
14453
14945
|
//#endregion
|
|
14454
14946
|
export { signerFailure as A, isRestoring as B, normalizeExceptionErrorKind as C, fromWei as D, safeExceptionLabel as E, CAPXUL_OPERATIONS as F, isCapxulOperation as I, normalizeCapxulOperation as L, PAYMENT_DIRECTIONS as M, PAYMENT_STATUSES as N, isSettingUpLifecycle as O, redactTelemetryEvent as P, destination as R, SDK_VERSION$1 as S, projectSdkException as T, fingerprintPaymentIntent as _, smartAccountErrorFromCapxul as a, failureDetail as b, identityErrorFromCapxul as c, embeddedSigner as d, openfortEmbeddedSigner as f, devPrivateKeySigner as g, deriveDevPrivateKey as h, assembleCapxulClient as i, resolveFailureMode as j, injectedWalletSigner as k, convexCallErrorFromCapxul as l, openfortEmbeddedWalletPort as m, createCapxulClientWithSignerControls as n, accountReadErrorFromCapxul as o, openfortEmbeddedSignerFromWallet as p, postHogObservability as r, wireChainId as s, createCapxulClient as t, bootstrapErrorFromCapxul as u, toWei as v, normalizeExceptionOperation as w, EXCEPTION_MESSAGE as x, CAPXUL_SDK_EXPECTED_OUTCOME_EVENT as y, isClaimed as z };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $ as PayrollGroupId, A as AllowanceKey, B as ChainId, C as AuthCachePort, D as Account$1, Et as FailureMode, H as CurrencyCode, I as AuthSession, K as Email, L as AuthUserId, M as AnonymousDistinctId, N as AppId, Q as PaymentCommandId, R as BlockNumber, U as DocumentHash, V as CountryCode, W as DurationMs, X as OrgId, Y as Money, Z as PartyId, _ as Session$1, _t as CapxulErrorCode, at as RoleKey, b as AuthClientPort, bt as Failure, ct as TesterKind, d as AccountRequirement, et as PayrollRunId, g as Profile$1, gt as CapxulError, h as CapxulResult, i as CapxulSigner, it as PublishableKey, j as AllowedOrigin, k as Address$1, lt as TxHash, nt as PermissionId, ot as SessionToken, q as EpochMs, rt as Profile, s as SignerStatusStore, st as SmartAccount, tt as PermissionAssignmentId, u as AccountProviderSource, ut as WeiAmount, v as SmartAccount$1, vt as CapxulErrorDetails, x as CanSendOtpStatus, y as ClockPort, z as BudgetId } from "./OAuthBearerAuthClient-C-ip-z8M.mjs";
|
|
1
|
+
import { $ as PayrollGroupId, A as AllowanceKey, B as ChainId, C as AuthCachePort, D as Account$1, Et as FailureMode, F as AssetId, H as CurrencyCode, I as AuthSession, K as Email, L as AuthUserId, M as AnonymousDistinctId, N as AppId, Q as PaymentCommandId, R as BlockNumber, U as DocumentHash, V as CountryCode, W as DurationMs, X as OrgId, Y as Money, Z as PartyId, _ as Session$1, _t as CapxulErrorCode, at as RoleKey, b as AuthClientPort, bt as Failure, ct as TesterKind, d as AccountRequirement, et as PayrollRunId, g as Profile$1, gt as CapxulError, h as CapxulResult, i as CapxulSigner, it as PublishableKey, j as AllowedOrigin, k as Address$1, lt as TxHash, nt as PermissionId, ot as SessionToken, q as EpochMs, rt as Profile, s as SignerStatusStore, st as SmartAccount, tt as PermissionAssignmentId, u as AccountProviderSource, ut as WeiAmount, v as SmartAccount$1, vt as CapxulErrorDetails, x as CanSendOtpStatus, y as ClockPort, z as BudgetId } from "./OAuthBearerAuthClient-C-ip-z8M.mjs";
|
|
2
2
|
import { Hex } from "viem";
|
|
3
3
|
import { Context, Effect, Layer, Schema, Scope, Tracer } from "effect";
|
|
4
4
|
import { FunctionReference } from "convex/server";
|
|
@@ -284,7 +284,7 @@ interface TelemetryPort {
|
|
|
284
284
|
}
|
|
285
285
|
//#endregion
|
|
286
286
|
//#region ../observability/src/index.d.ts
|
|
287
|
-
declare const TELEMETRY_EVENT_NAMES: readonly ["auth_otp_requested", "auth_otp_delivered", "auth_otp_expired", "auth_verified", "auth_failed", "auth_signed_out", "onboarding_intent_selected", "onboarding_profile_submitted", "onboarding_organization_submitted", "onboarding_dashboard_reached", "provisioning_safe_created", "provisioning_safe_confirmed", "bootstrap_resolved", "bootstrap_failed", "member_activation_started", "member_activation_ready", "member_activation_failed", "organization_creation_started", "organization_creation_ready", "organization_creation_failed", "chain_upstream_fallback", "bundler_error", "receipt_pending", "operation_retried", "account_balance_read", "account_balance_failed", "faucet_requested", "faucet_confirmed", "faucet_failed", "org_create_started", "org_safe_created", "org_safe_confirmed", "org_roles_seeded", "org_created", "org_create_failed", "org_invite_sent", "org_invite_accepted", "org_invite_expired", "payment_initiated", "payment_settled", "payment_failed", "payment_verification_retry", "payment_lifecycle_transition", "quick_pay_external_address_acknowledged", "deposit_initiated", "deposit_settled", "permission_mirror_verification", "movement_scan_window", "movement_scan_checkpoint", "movement_scan_incident", "activity_annotation_saved", "ui_activity_action_completed", "contact_relationship_changed", "ui_contact_action_completed", "access_recovery_completed", "ui_access_step_completed", "org_invite_progressed", "invoice_issued", "invoice_request_transitioned", "ui_invoice_step_completed", "ui_treasury_step_completed", "ui_receive_step_completed", "payroll_group_changed", "payroll_run_transitioned", "ui_payroll_action_completed"];
|
|
287
|
+
declare const TELEMETRY_EVENT_NAMES: readonly ["auth_otp_requested", "auth_otp_delivered", "auth_otp_expired", "auth_verified", "auth_failed", "identity_refused", "auth_signed_out", "onboarding_intent_selected", "onboarding_profile_submitted", "onboarding_organization_submitted", "onboarding_dashboard_reached", "provisioning_safe_created", "provisioning_safe_confirmed", "bootstrap_resolved", "bootstrap_failed", "member_activation_started", "member_activation_ready", "member_activation_failed", "organization_creation_started", "organization_creation_ready", "organization_creation_failed", "chain_upstream_fallback", "bundler_error", "receipt_pending", "operation_retried", "account_balance_read", "account_balance_failed", "faucet_requested", "faucet_confirmed", "faucet_failed", "org_create_started", "org_safe_created", "org_safe_confirmed", "org_roles_seeded", "org_created", "org_create_failed", "org_invite_sent", "org_invite_accepted", "org_invite_expired", "payment_initiated", "payment_settled", "payment_failed", "payment_verification_retry", "payment_lifecycle_transition", "quick_pay_external_address_acknowledged", "deposit_initiated", "deposit_settled", "permission_mirror_verification", "movement_scan_window", "movement_scan_checkpoint", "movement_scan_incident", "activity_annotation_saved", "ui_activity_action_completed", "contact_relationship_changed", "ui_contact_action_completed", "access_recovery_completed", "ui_access_step_completed", "org_invite_progressed", "invoice_issued", "invoice_request_transitioned", "ui_invoice_step_completed", "ui_treasury_step_completed", "ui_receive_step_completed", "payroll_group_changed", "payroll_run_transitioned", "ui_payroll_action_completed"];
|
|
288
288
|
type TelemetryEventName = (typeof TELEMETRY_EVENT_NAMES)[number];
|
|
289
289
|
type TelemetryProps = Record<string, unknown>;
|
|
290
290
|
type TelemetryEvent = {
|
|
@@ -613,6 +613,8 @@ type BootstrapResolution = {
|
|
|
613
613
|
readonly shieldPublishableKey: string;
|
|
614
614
|
/** Openfort third-party auth provider the signer presents; absent means better-auth. */
|
|
615
615
|
readonly openfortAuthProvider?: "better-auth" | "oidc";
|
|
616
|
+
/** Origin of the Ponder indexer the SDK reads directly; absent means no indexer reads. */
|
|
617
|
+
readonly indexerUrl?: string;
|
|
616
618
|
readonly engineeringTelemetry?: EngineeringTelemetryBootstrapPolicyV1;
|
|
617
619
|
};
|
|
618
620
|
type BootstrapErrorKind = "notAuthenticated" | "network" | "provider" | "malformedBody" | "invalidInput" | "rateLimited";
|
|
@@ -906,6 +908,118 @@ interface AccountReadPort {
|
|
|
906
908
|
fundFromFaucet(input: FundFromFaucetInput): Effect.Effect<FundFromFaucetResult, AccountReadError, never>;
|
|
907
909
|
}
|
|
908
910
|
//#endregion
|
|
911
|
+
//#region src/ports/indexer-read.d.ts
|
|
912
|
+
/**
|
|
913
|
+
* IndexerReadPort is the SDK's read seam onto the Ponder read model
|
|
914
|
+
* (`apps/indexer`). Ponder holds what the chain said; Convex holds what the
|
|
915
|
+
* user meant. Nothing here is copied into Convex, nothing waits for a
|
|
916
|
+
* finality tag, and no write path may read through this port.
|
|
917
|
+
*
|
|
918
|
+
* Every read is scoped to one Safe and one page. The indexer accepts only the
|
|
919
|
+
* exact compiled statements in `apps/indexer/src/queries.ts`, so the adapter
|
|
920
|
+
* that implements this port reproduces those statements byte for byte and the
|
|
921
|
+
* service refuses anything else with 403.
|
|
922
|
+
*/
|
|
923
|
+
/** One page of rows for one Safe. `offset` defaults to 0 (the first page). */
|
|
924
|
+
interface IndexerReadInput {
|
|
925
|
+
/** The Safe the caller owns. The indexer refuses a Safe outside the session claim. */
|
|
926
|
+
readonly safe: Address$1;
|
|
927
|
+
/** 1 to 1000 inclusive — the indexer's own bound. */
|
|
928
|
+
readonly limit: number;
|
|
929
|
+
readonly offset?: number;
|
|
930
|
+
}
|
|
931
|
+
/**
|
|
932
|
+
* The chain evidence every indexed row carries. `id` is the logical event id
|
|
933
|
+
* `chainId:txHash:transactionLogOrdinal`; it survives a reorg, so it is the
|
|
934
|
+
* key a Convex intent row stitches against.
|
|
935
|
+
*/
|
|
936
|
+
interface IndexerEventIdentity {
|
|
937
|
+
readonly id: string;
|
|
938
|
+
readonly chainId: ChainId;
|
|
939
|
+
readonly txHash: TxHash;
|
|
940
|
+
readonly transactionLogOrdinal: number;
|
|
941
|
+
readonly blockNumber: bigint;
|
|
942
|
+
readonly blockHash: string;
|
|
943
|
+
/** Block timestamp in epoch seconds. */
|
|
944
|
+
readonly timestamp: bigint;
|
|
945
|
+
}
|
|
946
|
+
interface IndexerTransferRow extends IndexerEventIdentity {
|
|
947
|
+
readonly assetId: AssetId;
|
|
948
|
+
readonly token: Address$1;
|
|
949
|
+
readonly from: Address$1;
|
|
950
|
+
readonly to: Address$1;
|
|
951
|
+
/**
|
|
952
|
+
* Raw token units, exact. This is an evidence boundary, so the row never
|
|
953
|
+
* carries Money or AssetAmount: the reader owns the asset's decimals.
|
|
954
|
+
*/
|
|
955
|
+
readonly amount: bigint;
|
|
956
|
+
}
|
|
957
|
+
interface IndexerPaymentRow extends IndexerEventIdentity {
|
|
958
|
+
readonly contract: Address$1;
|
|
959
|
+
/** The protocol settlement id a Convex Payment command stitches against. */
|
|
960
|
+
readonly settlementId: string;
|
|
961
|
+
readonly sender: Address$1;
|
|
962
|
+
readonly recipient: Address$1;
|
|
963
|
+
readonly assetId: AssetId;
|
|
964
|
+
readonly token: Address$1;
|
|
965
|
+
/** Raw token units, exact. */
|
|
966
|
+
readonly amount: bigint;
|
|
967
|
+
readonly kind: number;
|
|
968
|
+
readonly documentHash: string;
|
|
969
|
+
}
|
|
970
|
+
/** A row of the indexer's signed-transfer `balance` view. */
|
|
971
|
+
interface IndexerBalanceRow {
|
|
972
|
+
readonly chainId: ChainId;
|
|
973
|
+
readonly assetId: AssetId;
|
|
974
|
+
readonly address: Address$1;
|
|
975
|
+
/** Raw token units, exact. */
|
|
976
|
+
readonly amount: bigint;
|
|
977
|
+
}
|
|
978
|
+
/** One chain's sync position, from the indexer's native `/status` route. */
|
|
979
|
+
interface IndexerChainStatus {
|
|
980
|
+
readonly chain: string;
|
|
981
|
+
readonly chainId: ChainId;
|
|
982
|
+
readonly blockNumber: bigint;
|
|
983
|
+
/** Block timestamp in epoch seconds. Its age is the freshness label. */
|
|
984
|
+
readonly blockTimestamp: bigint;
|
|
985
|
+
}
|
|
986
|
+
/**
|
|
987
|
+
* The tables this seam reads today. P04 adds the authority rows (`safe`,
|
|
988
|
+
* `safe_event`, `roles_module`) and P05 adds `commitment`: each is one more
|
|
989
|
+
* entry here plus its statement and decoder.
|
|
990
|
+
*/
|
|
991
|
+
interface IndexerRowsByTable {
|
|
992
|
+
readonly transfer: readonly IndexerTransferRow[];
|
|
993
|
+
readonly payment: readonly IndexerPaymentRow[];
|
|
994
|
+
readonly balance: readonly IndexerBalanceRow[];
|
|
995
|
+
}
|
|
996
|
+
type IndexerTable = keyof IndexerRowsByTable;
|
|
997
|
+
declare const IndexerReadError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
|
|
998
|
+
readonly _tag: "IndexerReadError";
|
|
999
|
+
} & Readonly<A>;
|
|
1000
|
+
declare class IndexerReadError extends IndexerReadError_base<{
|
|
1001
|
+
readonly operation: string;
|
|
1002
|
+
readonly publicCode: CapxulErrorCode;
|
|
1003
|
+
readonly publicError: CapxulError;
|
|
1004
|
+
readonly cause: unknown;
|
|
1005
|
+
readonly details?: CapxulErrorDetails;
|
|
1006
|
+
}> {}
|
|
1007
|
+
interface IndexerReadPort {
|
|
1008
|
+
/** One page of rows, newest block first. An empty page is a value, not an error. */
|
|
1009
|
+
read<T extends IndexerTable>(table: T, input: IndexerReadInput): Effect.Effect<IndexerRowsByTable[T], IndexerReadError>;
|
|
1010
|
+
/**
|
|
1011
|
+
* The same page over the indexer's live stream. The first snapshot is
|
|
1012
|
+
* `loading`; each later snapshot carries the whole page again. The indexer
|
|
1013
|
+
* closes the stream on token expiry or revoked authority, which arrives as
|
|
1014
|
+
* one `error` snapshot — the caller re-reads with a refreshed token.
|
|
1015
|
+
*
|
|
1016
|
+
* @internal-use — substrate reactive transport, like `ConvexCallPort.subscribe`.
|
|
1017
|
+
*/
|
|
1018
|
+
subscribe<T extends IndexerTable>(table: T, input: IndexerReadInput, callback: (snapshot: Snapshot<IndexerRowsByTable[T]>) => void): Effect.Effect<Unsubscribe, IndexerReadError>;
|
|
1019
|
+
/** Sync position per chain. Unauthenticated: a stale index is not a refusal. */
|
|
1020
|
+
readonly status: Effect.Effect<readonly IndexerChainStatus[], IndexerReadError>;
|
|
1021
|
+
}
|
|
1022
|
+
//#endregion
|
|
909
1023
|
//#region src/flows/types.d.ts
|
|
910
1024
|
/** The runtime port bundle that `assembleCapxulClient` uses. */
|
|
911
1025
|
interface FlowPorts {
|
|
@@ -918,6 +1032,11 @@ interface FlowPorts {
|
|
|
918
1032
|
readonly clock: ClockPort;
|
|
919
1033
|
readonly telemetry: TelemetryPort;
|
|
920
1034
|
readonly convexCall: ConvexCallPort;
|
|
1035
|
+
/**
|
|
1036
|
+
* The Ponder read seam (P02). Optional: it is present only when the client
|
|
1037
|
+
* was given an indexer origin, and every existing flow works without it.
|
|
1038
|
+
*/
|
|
1039
|
+
readonly indexerRead?: IndexerReadPort;
|
|
921
1040
|
}
|
|
922
1041
|
/** Legacy method input retained by the imperative auth surface. */
|
|
923
1042
|
interface ExternalSigner {
|
|
@@ -2671,6 +2790,13 @@ interface CapxulClient {
|
|
|
2671
2790
|
* NOT on the public `client.accounts` surface.
|
|
2672
2791
|
*/
|
|
2673
2792
|
readonly accounts: AccountsFaucetMethods;
|
|
2793
|
+
/**
|
|
2794
|
+
* Ponder read seam (P02). Present only when the client was built with an
|
|
2795
|
+
* indexer origin, so every reader treats it as optional. Ponder holds what
|
|
2796
|
+
* the chain said; Convex holds what the user meant. Reads here never run
|
|
2797
|
+
* inside a write path and never wait for a finality tag.
|
|
2798
|
+
*/
|
|
2799
|
+
readonly indexer?: IndexerReadPort;
|
|
2674
2800
|
/** Quarantined Reference-harness evidence; never a product Organization method. */
|
|
2675
2801
|
readonly organizationSetup: OrganizationSetupProofMethods;
|
|
2676
2802
|
/** Product telemetry projection for internal composition and test harnesses. */
|
package/dist/testing/index.d.mts
CHANGED
|
@@ -1,10 +1,85 @@
|
|
|
1
1
|
import { h as CapxulResult } from "../OAuthBearerAuthClient-C-ip-z8M.mjs";
|
|
2
|
-
import { er as TelemetryEvent, f as CapxulClient, i as ObservationAdapter, n as ProductionSignerControls, nr as TelemetryIdentifyInput, pr as IdentityTransition, t as CapxulClientInput, tr as TelemetryGroupInput } from "../production-
|
|
2
|
+
import { er as TelemetryEvent, f as CapxulClient, i as ObservationAdapter, n as ProductionSignerControls, nr as TelemetryIdentifyInput, pr as IdentityTransition, t as CapxulClientInput, tr as TelemetryGroupInput } from "../production-Du4Vc7YD.mjs";
|
|
3
3
|
import { Effect, Layer } from "effect";
|
|
4
4
|
//#region src/testing/production-client.d.ts
|
|
5
5
|
/** Create the one production client with per-client controls at its signing boundaries. */
|
|
6
6
|
declare function createCapxulProductionTestClient(input: CapxulClientInput, controls: ProductionSignerControls): Promise<CapxulResult<CapxulClient>>;
|
|
7
7
|
//#endregion
|
|
8
|
+
//#region src/testing/signer-faults.d.ts
|
|
9
|
+
/**
|
|
10
|
+
* Which provider refusal to inject. The codes are the ones the SDK's own
|
|
11
|
+
* classifiers decode (`src/signer.ts`), so a fault reaches the public error
|
|
12
|
+
* contract through the real decode path rather than a shortcut.
|
|
13
|
+
*
|
|
14
|
+
* - `missing-signer` — Openfort's typed not-ready code. `openfortSignerNotReadyCode`
|
|
15
|
+
* decodes it, so the production recovery wrapper runs its single retry (A02).
|
|
16
|
+
* - `user-rejected` — the EIP-1193 4001 result. `signerFailure` decodes it to
|
|
17
|
+
* `SIGNER_REJECTED`, an expected refusal (A08).
|
|
18
|
+
* - `unexpected` — no provider code at all, so it stays an unexpected
|
|
19
|
+
* `PROVIDER_ERROR` (A09).
|
|
20
|
+
*/
|
|
21
|
+
type SignerFaultKind = "missing-signer" | "user-rejected" | "unexpected";
|
|
22
|
+
/** The value a controlled fault throws. Named so a test can assert its origin. */
|
|
23
|
+
declare class ControlledSignerFault extends Error {
|
|
24
|
+
readonly kind: SignerFaultKind;
|
|
25
|
+
/** Provider code the SDK classifiers read; absent for `unexpected`. */
|
|
26
|
+
readonly code: string | number | undefined;
|
|
27
|
+
constructor(kind: SignerFaultKind);
|
|
28
|
+
}
|
|
29
|
+
/** Build one fault value without the harness, for a caller that writes its own control. */
|
|
30
|
+
declare function signerFaultCause(kind: SignerFaultKind): ControlledSignerFault;
|
|
31
|
+
interface SignerFaultPlan {
|
|
32
|
+
/**
|
|
33
|
+
* Fault the next embedded signature ONCE, beneath the production recovery
|
|
34
|
+
* wrapper, then delegate every later call to the real signer. The address
|
|
35
|
+
* read and the readiness cycle still run first, so the fault lands at the
|
|
36
|
+
* actual final signing boundary after the prepared-command equality checks.
|
|
37
|
+
*/
|
|
38
|
+
readonly signOnce?: SignerFaultKind;
|
|
39
|
+
/**
|
|
40
|
+
* Call the production signer's own `resetSession()` ONCE, immediately before
|
|
41
|
+
* the next signature and after the command is prepared. The delegate then
|
|
42
|
+
* re-runs the real readiness cycle, so the configure request under test is
|
|
43
|
+
* the first one the browser observes after the `reset-session` record (A03).
|
|
44
|
+
*
|
|
45
|
+
* Throws when the composed signer owns no session — a silently skipped reset
|
|
46
|
+
* would leave the run looking green while proving nothing.
|
|
47
|
+
*/
|
|
48
|
+
readonly resetSessionBeforeNextSign?: boolean;
|
|
49
|
+
}
|
|
50
|
+
/** What the seam did. JSON-serializable, so a browser test can read it back. */
|
|
51
|
+
interface SignerSeamRecord {
|
|
52
|
+
readonly boundary: "reset-session" | "embedded-sign";
|
|
53
|
+
/** 1-based count of calls at that boundary. */
|
|
54
|
+
readonly attempt: number;
|
|
55
|
+
readonly hash?: string;
|
|
56
|
+
/** Present only on the call that actually threw. */
|
|
57
|
+
readonly fault?: SignerFaultKind;
|
|
58
|
+
readonly atMs: number;
|
|
59
|
+
}
|
|
60
|
+
interface ArmedSignerSeam {
|
|
61
|
+
/** Hand these to `createCapxulProductionTestClient`. */
|
|
62
|
+
readonly controls: ProductionSignerControls;
|
|
63
|
+
/** Oldest first. Empty until the client reaches a signing boundary. */
|
|
64
|
+
readonly records: () => readonly SignerSeamRecord[];
|
|
65
|
+
/** True once every armed one-shot has fired. */
|
|
66
|
+
readonly spent: () => boolean;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Arm one-shot faults on the production embedded signer.
|
|
70
|
+
*
|
|
71
|
+
* ```ts
|
|
72
|
+
* const seam = armSignerFaults({ signOnce: "user-rejected" });
|
|
73
|
+
* const client = await createCapxulProductionTestClient(input, seam.controls);
|
|
74
|
+
* const result = await client.org(orgId).permissions.create(command);
|
|
75
|
+
* // result.error.code === "SIGNER_REJECTED"; nothing was submitted.
|
|
76
|
+
* // seam.records() names the boundary that refused.
|
|
77
|
+
* ```
|
|
78
|
+
*
|
|
79
|
+
* An empty plan installs no controls, which composes the ordinary client.
|
|
80
|
+
*/
|
|
81
|
+
declare function armSignerFaults(plan?: SignerFaultPlan): ArmedSignerSeam;
|
|
82
|
+
//#endregion
|
|
8
83
|
//#region src/testing/telemetry/RecordingTelemetryAdapter.d.ts
|
|
9
84
|
type RecordingTelemetryOperation = {
|
|
10
85
|
readonly type: "emit";
|
|
@@ -56,4 +131,4 @@ interface CapxulTestObservation {
|
|
|
56
131
|
*/
|
|
57
132
|
declare function createCapxulTestClient(options?: CreateCapxulTestClientOptions): CapxulTestClient;
|
|
58
133
|
//#endregion
|
|
59
|
-
export { CapxulTestClient, CapxulTestClock, CapxulTestObservation, CreateCapxulTestClientOptions, type ProductionSignerControls, SeedTestIdentityInput, createCapxulProductionTestClient, createCapxulTestClient };
|
|
134
|
+
export { type ArmedSignerSeam, CapxulTestClient, CapxulTestClock, CapxulTestObservation, ControlledSignerFault, CreateCapxulTestClientOptions, type ProductionSignerControls, SeedTestIdentityInput, type SignerFaultKind, type SignerFaultPlan, type SignerSeamRecord, armSignerFaults, createCapxulProductionTestClient, createCapxulTestClient, signerFaultCause };
|
package/dist/testing/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { D as fromWei, P as redactTelemetryEvent, a as smartAccountErrorFromCapxul, c as identityErrorFromCapxul, i as assembleCapxulClient, l as convexCallErrorFromCapxul, n as createCapxulClientWithSignerControls, o as accountReadErrorFromCapxul, s as wireChainId, u as bootstrapErrorFromCapxul, v as toWei } from "../production-
|
|
2
|
-
import {
|
|
1
|
+
import { D as fromWei, P as redactTelemetryEvent, a as smartAccountErrorFromCapxul, c as identityErrorFromCapxul, i as assembleCapxulClient, l as convexCallErrorFromCapxul, n as createCapxulClientWithSignerControls, o as accountReadErrorFromCapxul, s as wireChainId, u as bootstrapErrorFromCapxul, v as toWei } from "../production-216hsAKc.mjs";
|
|
2
|
+
import { G as toHandle, H as toEmail, I as toAuthUserId, K as toJwtToken, M as toAddress, N as toAllowedOrigin, P as toAppId, Q as toPublishableKey, R as toChainId, U as toEpochMs, V as toDurationMs, W as toEpochSeconds, d as authClientPortFromPromiseAdapter, et as toSessionToken, j as toAccountId, lt as Errors, n as readClockNow, q as toKycTier, r as InMemoryAuthCacheAdapter, rt as validateHandle, st as CapxulError, y as deriveCapxulSafeAddress, z as toCountryCode } from "../OAuthBearerAuthClient-IB2W1Fzj.mjs";
|
|
3
3
|
import { keccak256 } from "viem";
|
|
4
4
|
import { Effect, Result, Semaphore } from "effect";
|
|
5
5
|
import { getFunctionName } from "convex/server";
|
|
@@ -9,6 +9,97 @@ function createCapxulProductionTestClient(input, controls) {
|
|
|
9
9
|
return createCapxulClientWithSignerControls(input, controls);
|
|
10
10
|
}
|
|
11
11
|
//#endregion
|
|
12
|
+
//#region src/testing/signer-faults.ts
|
|
13
|
+
const FAULT_CODES = {
|
|
14
|
+
"missing-signer": "MISSING_SIGNER",
|
|
15
|
+
"user-rejected": 4001,
|
|
16
|
+
unexpected: void 0
|
|
17
|
+
};
|
|
18
|
+
/** The value a controlled fault throws. Named so a test can assert its origin. */
|
|
19
|
+
var ControlledSignerFault = class extends Error {
|
|
20
|
+
kind;
|
|
21
|
+
/** Provider code the SDK classifiers read; absent for `unexpected`. */
|
|
22
|
+
code;
|
|
23
|
+
constructor(kind) {
|
|
24
|
+
super(`controlled signer fault (${kind})`);
|
|
25
|
+
this.name = "ControlledSignerFault";
|
|
26
|
+
this.kind = kind;
|
|
27
|
+
this.code = FAULT_CODES[kind];
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
/** Build one fault value without the harness, for a caller that writes its own control. */
|
|
31
|
+
function signerFaultCause(kind) {
|
|
32
|
+
return new ControlledSignerFault(kind);
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Arm one-shot faults on the production embedded signer.
|
|
36
|
+
*
|
|
37
|
+
* ```ts
|
|
38
|
+
* const seam = armSignerFaults({ signOnce: "user-rejected" });
|
|
39
|
+
* const client = await createCapxulProductionTestClient(input, seam.controls);
|
|
40
|
+
* const result = await client.org(orgId).permissions.create(command);
|
|
41
|
+
* // result.error.code === "SIGNER_REJECTED"; nothing was submitted.
|
|
42
|
+
* // seam.records() names the boundary that refused.
|
|
43
|
+
* ```
|
|
44
|
+
*
|
|
45
|
+
* An empty plan installs no controls, which composes the ordinary client.
|
|
46
|
+
*/
|
|
47
|
+
function armSignerFaults(plan = {}) {
|
|
48
|
+
const records = [];
|
|
49
|
+
let pendingFault = plan.signOnce;
|
|
50
|
+
let pendingReset = plan.resetSessionBeforeNextSign === true;
|
|
51
|
+
let resetCalls = 0;
|
|
52
|
+
let embeddedCalls = 0;
|
|
53
|
+
function record(entry) {
|
|
54
|
+
records.push({
|
|
55
|
+
...entry,
|
|
56
|
+
atMs: Date.now()
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
const resetThenSign = async (hash, signer) => {
|
|
60
|
+
if (pendingReset) {
|
|
61
|
+
pendingReset = false;
|
|
62
|
+
resetCalls += 1;
|
|
63
|
+
if (signer.resetSession === void 0) throw new Error("armSignerFaults: resetSessionBeforeNextSign needs the embedded browser signer; this client composed a signer with no session");
|
|
64
|
+
signer.resetSession();
|
|
65
|
+
record({
|
|
66
|
+
boundary: "reset-session",
|
|
67
|
+
attempt: resetCalls,
|
|
68
|
+
hash
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
return signer.signUserOpHash(hash);
|
|
72
|
+
};
|
|
73
|
+
const faultThenDelegate = async (hash, delegate) => {
|
|
74
|
+
embeddedCalls += 1;
|
|
75
|
+
const fault = pendingFault;
|
|
76
|
+
if (fault === void 0) {
|
|
77
|
+
record({
|
|
78
|
+
boundary: "embedded-sign",
|
|
79
|
+
attempt: embeddedCalls,
|
|
80
|
+
hash
|
|
81
|
+
});
|
|
82
|
+
return delegate(hash);
|
|
83
|
+
}
|
|
84
|
+
pendingFault = void 0;
|
|
85
|
+
record({
|
|
86
|
+
boundary: "embedded-sign",
|
|
87
|
+
attempt: embeddedCalls,
|
|
88
|
+
hash,
|
|
89
|
+
fault
|
|
90
|
+
});
|
|
91
|
+
throw signerFaultCause(fault);
|
|
92
|
+
};
|
|
93
|
+
return {
|
|
94
|
+
controls: {
|
|
95
|
+
...plan.resetSessionBeforeNextSign === true ? { signUserOpHash: resetThenSign } : {},
|
|
96
|
+
...plan.signOnce === void 0 ? {} : { signEmbeddedUserOpHash: faultThenDelegate }
|
|
97
|
+
},
|
|
98
|
+
records: () => records.slice(),
|
|
99
|
+
spent: () => pendingFault === void 0 && !pendingReset
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
//#endregion
|
|
12
103
|
//#region src/testing/account/InMemoryAccountReadAdapter.ts
|
|
13
104
|
var InMemoryAccountReadAdapter = class {
|
|
14
105
|
#deps;
|
|
@@ -915,4 +1006,4 @@ function createCapxulTestClient(options = {}) {
|
|
|
915
1006
|
};
|
|
916
1007
|
}
|
|
917
1008
|
//#endregion
|
|
918
|
-
export { createCapxulProductionTestClient, createCapxulTestClient };
|
|
1009
|
+
export { ControlledSignerFault, armSignerFaults, createCapxulProductionTestClient, createCapxulTestClient, signerFaultCause };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@capxul/sdk",
|
|
3
|
-
"version": "4.2.0-rc.
|
|
3
|
+
"version": "4.2.0-rc.9",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "git+https://github.com/Xelmar-tech/infrastructure.git",
|
|
@@ -48,10 +48,10 @@
|
|
|
48
48
|
"vite-plus": "0.3.0",
|
|
49
49
|
"vitest": "4.1.11",
|
|
50
50
|
"@capxul/config": "0.3.0",
|
|
51
|
-
"@capxul/observability": "4.2.0-rc.
|
|
51
|
+
"@capxul/observability": "4.2.0-rc.9",
|
|
52
52
|
"@capxul/errors": "0.3.0",
|
|
53
|
-
"@capxul/types": "0.3.0",
|
|
54
53
|
"@capxul/wire": "0.7.0",
|
|
54
|
+
"@capxul/types": "0.3.0",
|
|
55
55
|
"@capxul/typescript-config": "0.0.0"
|
|
56
56
|
},
|
|
57
57
|
"_permissionlessPinReason": "permissionless.toSafeSmartAccount is pinned to 0.3.4 for live Safe deployment E2E. Counterfactual address fixtures captured 2026-05-17 in packages/backend/convex/_shared/__tests__/counterfactual.test.ts and packages/config/tests/safe.test.ts must be re-verified before upgrading.",
|