@capxul/sdk 4.2.0-rc.1 → 4.2.0-rc.11
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-BrbXrndM.mjs → OAuthBearerAuthClient-CbU_W9Sp.mjs} +114 -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-FgHUndeX.mjs → production-7jr-KUvX.mjs} +1508 -63
- package/dist/{production-CBKHzqYP.d.mts → production-A1zW1JPt.d.mts} +163 -31
- package/dist/testing/index.d.mts +77 -2
- package/dist/testing/index.mjs +94 -3
- package/package.json +5 -5
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $ as
|
|
1
|
+
import { $ as toRoleKey, B as toCurrencyCode, C as ASSET_ID_RE, Ct as isCredentialField, 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 containsSensitiveMaterial, T as EVM_ADDRESS_RE$1, Tt as redactUrlSecrets, U as toEpochMs, V as toDurationMs, W as toEpochSeconds, X as toPayrollGroupId, Y as toPartyId, Z as toPayrollRunId, _ as normalizeBindingEmail, _t as decodeChainCause, b as configuredMoneyAssetById, bt as isFailureMode, c as AuthCachePortTag, ct as CapxulError, d as authClientPortFromPromiseAdapter, dt as isCapxulError, et as toSessionToken, f as AuthClientError, ft as CHAIN_UPSTREAMS, gt as chainEvidenceLabel, h as orgRoleKeyForLabel, ht as chainCauseProperties, i as BrowserAuthCacheAdapter, it as validateHandle, j as toAccountId, l as SystemClockLayer, lt as EXPECTED_OPERATION_OUTCOMES, m as ADMIN_ROLE_LABEL, mt as boundedResponseHeaders, nt as toTxHash, ot as decodeConvexError, p as AuthClientPortTag, pt as FAILURE_MODES$1, q as toKycTier, r as InMemoryAuthCacheAdapter, rt as toWeiAmount, st as CAPXUL_ERROR_CODES, tt as toTesterKind, u as ClockPortTag, ut as Errors, v as BASE_SEPOLIA_CHAIN_ID, vt as failureFingerprint, w as BYTES32_RE, wt as redactSecrets, x as ACCOUNT_ID_RE, xt as revertSummaryText, y as deriveCapxulSafeAddress, yt as isChainUpstream, z as toCountryCode } from "./OAuthBearerAuthClient-CbU_W9Sp.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,
|
|
@@ -1333,10 +1346,44 @@ const DepositSettledProps = Schema.Struct({
|
|
|
1333
1346
|
});
|
|
1334
1347
|
const PermissionMirrorVerificationProps = Schema.Struct({
|
|
1335
1348
|
...TelemetryEnvelopeProps,
|
|
1349
|
+
/** The RECEIPT verification outcome. What the mirror then did is separate. */
|
|
1336
1350
|
outcome: Schema.String,
|
|
1337
1351
|
reason: Schema.String,
|
|
1338
1352
|
chain_id: Schema.Number,
|
|
1339
|
-
retry_count: Schema.Number
|
|
1353
|
+
retry_count: Schema.Number,
|
|
1354
|
+
/**
|
|
1355
|
+
* #1960 R02, consumed by R03. A verified receipt is not an applied Permission:
|
|
1356
|
+
* these two optional fields let the producer say what the applying mutation
|
|
1357
|
+
* actually returned, and which command it was, without either fact rewriting
|
|
1358
|
+
* the receipt outcome above. Absent when application has not happened yet.
|
|
1359
|
+
*/
|
|
1360
|
+
application_result: Schema.optional(Schema.Literals([
|
|
1361
|
+
"applied",
|
|
1362
|
+
"replayed",
|
|
1363
|
+
"conflicting_replay",
|
|
1364
|
+
"revision_mismatch"
|
|
1365
|
+
])),
|
|
1366
|
+
command_id: OptionalString,
|
|
1367
|
+
/**
|
|
1368
|
+
* #1961 R03. The authorized stored row names the Permission it changed; the
|
|
1369
|
+
* verified receipt names the chain facts that prove it. Every field is
|
|
1370
|
+
* optional because a pending or provider-failed verification has neither an
|
|
1371
|
+
* application result nor a receipt to report.
|
|
1372
|
+
*/
|
|
1373
|
+
permission_id: OptionalString,
|
|
1374
|
+
permission_assignment_id: OptionalString,
|
|
1375
|
+
permission_operation: Schema.optional(Schema.Literals([
|
|
1376
|
+
"create",
|
|
1377
|
+
"change",
|
|
1378
|
+
"assign",
|
|
1379
|
+
"revoke",
|
|
1380
|
+
"replace"
|
|
1381
|
+
])),
|
|
1382
|
+
revision: Schema.optional(Schema.Number),
|
|
1383
|
+
user_op_hash: OptionalString,
|
|
1384
|
+
transaction_hash: OptionalString,
|
|
1385
|
+
block_number: Schema.optional(Schema.Number),
|
|
1386
|
+
log_index: Schema.optional(Schema.Number)
|
|
1340
1387
|
});
|
|
1341
1388
|
const MovementScanWindowProps = Schema.Struct({
|
|
1342
1389
|
...TelemetryEnvelopeProps,
|
|
@@ -1382,6 +1429,10 @@ Schema.Struct({
|
|
|
1382
1429
|
name: Schema.Literal("auth_failed"),
|
|
1383
1430
|
props: AuthFailedProps
|
|
1384
1431
|
});
|
|
1432
|
+
Schema.Struct({
|
|
1433
|
+
name: Schema.Literal("identity_refused"),
|
|
1434
|
+
props: IdentityRefusedProps
|
|
1435
|
+
});
|
|
1385
1436
|
Schema.Struct({
|
|
1386
1437
|
name: Schema.Literal("auth_signed_out"),
|
|
1387
1438
|
props: AuthSignedOutProps
|
|
@@ -1562,6 +1613,10 @@ Schema.Struct({
|
|
|
1562
1613
|
name: Schema.Literal("movement_scan_incident"),
|
|
1563
1614
|
props: MovementScanIncidentProps
|
|
1564
1615
|
});
|
|
1616
|
+
/** The one spelling of "whose scope did this happen in" across every surface. */
|
|
1617
|
+
const ScopeKindSchema = Schema.Literals(["account", "organization"]);
|
|
1618
|
+
const AccountKindSchema = Schema.Literals(["personalSafe", "orgTreasury"]);
|
|
1619
|
+
const OptionalReasonCode = Schema.optional(Schema.Literals(CAPXUL_ERROR_CODES));
|
|
1565
1620
|
const ActivityReferenceProps = {
|
|
1566
1621
|
reference_kind: Schema.Literals(["payment", "movement"]),
|
|
1567
1622
|
reference_id: Schema.String
|
|
@@ -1592,8 +1647,8 @@ const ActivityFilterProps = {
|
|
|
1592
1647
|
};
|
|
1593
1648
|
const ActivityActionProps = {
|
|
1594
1649
|
...TelemetryEnvelopeProps,
|
|
1595
|
-
scope_kind:
|
|
1596
|
-
reason_code:
|
|
1650
|
+
scope_kind: ScopeKindSchema,
|
|
1651
|
+
reason_code: OptionalReasonCode
|
|
1597
1652
|
};
|
|
1598
1653
|
Schema.Struct({
|
|
1599
1654
|
name: Schema.Literal("contact_relationship_changed"),
|
|
@@ -1605,7 +1660,7 @@ Schema.Struct({
|
|
|
1605
1660
|
"hidden",
|
|
1606
1661
|
"restored"
|
|
1607
1662
|
]),
|
|
1608
|
-
scope_kind:
|
|
1663
|
+
scope_kind: ScopeKindSchema,
|
|
1609
1664
|
scope_id: Schema.String,
|
|
1610
1665
|
owner_party_id: Schema.String,
|
|
1611
1666
|
party_id: Schema.String,
|
|
@@ -1621,9 +1676,9 @@ Schema.Struct({
|
|
|
1621
1676
|
});
|
|
1622
1677
|
const ContactActionProps = {
|
|
1623
1678
|
...TelemetryEnvelopeProps,
|
|
1624
|
-
scope_kind:
|
|
1679
|
+
scope_kind: ScopeKindSchema,
|
|
1625
1680
|
party_id: OptionalString,
|
|
1626
|
-
reason_code:
|
|
1681
|
+
reason_code: OptionalReasonCode
|
|
1627
1682
|
};
|
|
1628
1683
|
Schema.Struct({
|
|
1629
1684
|
name: Schema.Literal("ui_contact_action_completed"),
|
|
@@ -1650,7 +1705,7 @@ Schema.Struct({
|
|
|
1650
1705
|
props: Schema.Struct({
|
|
1651
1706
|
...TelemetryEnvelopeProps,
|
|
1652
1707
|
...ActivityReferenceProps,
|
|
1653
|
-
scope_kind:
|
|
1708
|
+
scope_kind: ScopeKindSchema,
|
|
1654
1709
|
scope_id: Schema.String,
|
|
1655
1710
|
occurred_at: Schema.Number
|
|
1656
1711
|
})
|
|
@@ -1705,6 +1760,193 @@ Schema.Struct({
|
|
|
1705
1760
|
})
|
|
1706
1761
|
])
|
|
1707
1762
|
});
|
|
1763
|
+
/**
|
|
1764
|
+
* Reliability closeout (#1960 R02) — the shape every explicit UI observation
|
|
1765
|
+
* below shares. `scope_kind` says whose surface the person was looking at,
|
|
1766
|
+
* `organization_id` is the verified Organization the producer resolved (absent
|
|
1767
|
+
* on a personal action), and `reason_code` is a catalog error code, never copy.
|
|
1768
|
+
*/
|
|
1769
|
+
const UiStepProps = {
|
|
1770
|
+
...TelemetryEnvelopeProps,
|
|
1771
|
+
scope_kind: ScopeKindSchema,
|
|
1772
|
+
organization_id: OptionalOrgId,
|
|
1773
|
+
reason_code: OptionalReasonCode
|
|
1774
|
+
};
|
|
1775
|
+
const AccessRecoveryCompletedProps = Schema.Union([Schema.Struct({
|
|
1776
|
+
...TelemetryEnvelopeProps,
|
|
1777
|
+
recovery: Schema.Literal("session_restoration"),
|
|
1778
|
+
outcome: Schema.Literals([
|
|
1779
|
+
"restored",
|
|
1780
|
+
"absent",
|
|
1781
|
+
"failed"
|
|
1782
|
+
]),
|
|
1783
|
+
reason_code: OptionalReasonCode,
|
|
1784
|
+
organization_id: OptionalOrgId
|
|
1785
|
+
}), Schema.Struct({
|
|
1786
|
+
...TelemetryEnvelopeProps,
|
|
1787
|
+
recovery: Schema.Literal("signer_readiness"),
|
|
1788
|
+
outcome: Schema.Literals(["ready", "unavailable"]),
|
|
1789
|
+
reason_code: OptionalReasonCode,
|
|
1790
|
+
organization_id: OptionalOrgId
|
|
1791
|
+
})]);
|
|
1792
|
+
Schema.Struct({
|
|
1793
|
+
name: Schema.Literal("access_recovery_completed"),
|
|
1794
|
+
props: AccessRecoveryCompletedProps
|
|
1795
|
+
});
|
|
1796
|
+
Schema.Struct({
|
|
1797
|
+
name: Schema.Literal("ui_access_step_completed"),
|
|
1798
|
+
props: Schema.Struct({
|
|
1799
|
+
...UiStepProps,
|
|
1800
|
+
step: Schema.Literals([
|
|
1801
|
+
"dashboard_granted",
|
|
1802
|
+
"redirect_completed",
|
|
1803
|
+
"gate_error_displayed",
|
|
1804
|
+
"retry_selected",
|
|
1805
|
+
"capability_refusal_displayed",
|
|
1806
|
+
"signer_refusal_displayed"
|
|
1807
|
+
]),
|
|
1808
|
+
capability: Schema.optional(Schema.Literals(["spend", "managePeople"])),
|
|
1809
|
+
probe: Schema.optional(Schema.Literals([
|
|
1810
|
+
"session",
|
|
1811
|
+
"membership",
|
|
1812
|
+
"lifecycle"
|
|
1813
|
+
])),
|
|
1814
|
+
reason: Schema.optional(Schema.Literals([
|
|
1815
|
+
"loading",
|
|
1816
|
+
"no-permission",
|
|
1817
|
+
"org-unavailable"
|
|
1818
|
+
]))
|
|
1819
|
+
})
|
|
1820
|
+
});
|
|
1821
|
+
Schema.Struct({
|
|
1822
|
+
name: Schema.Literal("org_invite_progressed"),
|
|
1823
|
+
props: Schema.Struct({
|
|
1824
|
+
...TelemetryEnvelopeProps,
|
|
1825
|
+
organization_id: OrgIdTelemetrySchema,
|
|
1826
|
+
progress: Schema.Literals([
|
|
1827
|
+
"account_ready",
|
|
1828
|
+
"permission_applied",
|
|
1829
|
+
"membership_activated",
|
|
1830
|
+
"revoked"
|
|
1831
|
+
]),
|
|
1832
|
+
/** The invited Party. The initiating actor stays on the envelope's identity. */
|
|
1833
|
+
member_party_id: Schema.String,
|
|
1834
|
+
occurred_at: Schema.Number
|
|
1835
|
+
})
|
|
1836
|
+
});
|
|
1837
|
+
Schema.Struct({
|
|
1838
|
+
name: Schema.Literal("invoice_issued"),
|
|
1839
|
+
props: Schema.Struct({
|
|
1840
|
+
...TelemetryEnvelopeProps,
|
|
1841
|
+
request_id: Schema.String,
|
|
1842
|
+
issuer_scope_kind: ScopeKindSchema,
|
|
1843
|
+
issuer_scope_id: Schema.String,
|
|
1844
|
+
payer_party_id: OptionalString,
|
|
1845
|
+
occurred_at: Schema.Number
|
|
1846
|
+
})
|
|
1847
|
+
});
|
|
1848
|
+
Schema.Struct({
|
|
1849
|
+
name: Schema.Literal("invoice_request_transitioned"),
|
|
1850
|
+
props: Schema.Struct({
|
|
1851
|
+
...TelemetryEnvelopeProps,
|
|
1852
|
+
request_id: Schema.String,
|
|
1853
|
+
transition: Schema.Literals([
|
|
1854
|
+
"approval_prepared",
|
|
1855
|
+
"declined",
|
|
1856
|
+
"cancelled"
|
|
1857
|
+
]),
|
|
1858
|
+
actor_scope_kind: ScopeKindSchema,
|
|
1859
|
+
actor_scope_id: Schema.String,
|
|
1860
|
+
occurred_at: Schema.Number
|
|
1861
|
+
})
|
|
1862
|
+
});
|
|
1863
|
+
Schema.Struct({
|
|
1864
|
+
name: Schema.Literal("ui_invoice_step_completed"),
|
|
1865
|
+
props: Schema.Struct({
|
|
1866
|
+
...UiStepProps,
|
|
1867
|
+
step: Schema.Literals([
|
|
1868
|
+
"opened",
|
|
1869
|
+
"previewed",
|
|
1870
|
+
"dismissed",
|
|
1871
|
+
"submitted",
|
|
1872
|
+
"result_displayed",
|
|
1873
|
+
"refusal_displayed",
|
|
1874
|
+
"retry_selected"
|
|
1875
|
+
]),
|
|
1876
|
+
request_id: OptionalString
|
|
1877
|
+
})
|
|
1878
|
+
});
|
|
1879
|
+
Schema.Struct({
|
|
1880
|
+
name: Schema.Literal("ui_treasury_step_completed"),
|
|
1881
|
+
props: Schema.Struct({
|
|
1882
|
+
...UiStepProps,
|
|
1883
|
+
step: Schema.Literals([
|
|
1884
|
+
"balance_displayed",
|
|
1885
|
+
"read_refusal_displayed",
|
|
1886
|
+
"retry_selected",
|
|
1887
|
+
"recovery_completed"
|
|
1888
|
+
]),
|
|
1889
|
+
account_kind: Schema.optional(AccountKindSchema)
|
|
1890
|
+
})
|
|
1891
|
+
});
|
|
1892
|
+
Schema.Struct({
|
|
1893
|
+
name: Schema.Literal("ui_receive_step_completed"),
|
|
1894
|
+
props: Schema.Struct({
|
|
1895
|
+
...UiStepProps,
|
|
1896
|
+
step: Schema.Literals([
|
|
1897
|
+
"instructions_displayed",
|
|
1898
|
+
"address_copied",
|
|
1899
|
+
"copy_failed",
|
|
1900
|
+
"refusal_displayed",
|
|
1901
|
+
"retry_selected"
|
|
1902
|
+
]),
|
|
1903
|
+
account_kind: Schema.optional(AccountKindSchema)
|
|
1904
|
+
})
|
|
1905
|
+
});
|
|
1906
|
+
Schema.Struct({
|
|
1907
|
+
name: Schema.Literal("payroll_group_changed"),
|
|
1908
|
+
props: Schema.Struct({
|
|
1909
|
+
...TelemetryEnvelopeProps,
|
|
1910
|
+
organization_id: OrgIdTelemetrySchema,
|
|
1911
|
+
change: Schema.Literals([
|
|
1912
|
+
"created",
|
|
1913
|
+
"updated",
|
|
1914
|
+
"deleted"
|
|
1915
|
+
]),
|
|
1916
|
+
group_id: Schema.String,
|
|
1917
|
+
member_count: Schema.Number,
|
|
1918
|
+
occurred_at: Schema.Number
|
|
1919
|
+
})
|
|
1920
|
+
});
|
|
1921
|
+
Schema.Struct({
|
|
1922
|
+
name: Schema.Literal("payroll_run_transitioned"),
|
|
1923
|
+
props: Schema.Struct({
|
|
1924
|
+
...TelemetryEnvelopeProps,
|
|
1925
|
+
organization_id: OrgIdTelemetrySchema,
|
|
1926
|
+
transition: Schema.Literals([
|
|
1927
|
+
"prepared",
|
|
1928
|
+
"authorized",
|
|
1929
|
+
"settled"
|
|
1930
|
+
]),
|
|
1931
|
+
run_id: Schema.String,
|
|
1932
|
+
payment_count: Schema.optional(Schema.Number),
|
|
1933
|
+
occurred_at: Schema.Number
|
|
1934
|
+
})
|
|
1935
|
+
});
|
|
1936
|
+
Schema.Struct({
|
|
1937
|
+
name: Schema.Literal("ui_payroll_action_completed"),
|
|
1938
|
+
props: Schema.Struct({
|
|
1939
|
+
...UiStepProps,
|
|
1940
|
+
action: Schema.Literals([
|
|
1941
|
+
"opened",
|
|
1942
|
+
"refusal_displayed",
|
|
1943
|
+
"recovery_completed",
|
|
1944
|
+
"cancelled",
|
|
1945
|
+
"submitted_displayed"
|
|
1946
|
+
]),
|
|
1947
|
+
run_id: OptionalString
|
|
1948
|
+
})
|
|
1949
|
+
});
|
|
1708
1950
|
function redactTelemetryEvent(event, options = {}) {
|
|
1709
1951
|
const props = redactTelemetryProps(event.name, event.props, options);
|
|
1710
1952
|
return props === void 0 ? { name: event.name } : {
|
|
@@ -2007,6 +2249,10 @@ const BootstrapEnvelope = Schema.Struct({
|
|
|
2007
2249
|
siteBaseUrl: Schema.String,
|
|
2008
2250
|
openfortPublishableKey: Schema.String,
|
|
2009
2251
|
shieldPublishableKey: Schema.String,
|
|
2252
|
+
/** Which Openfort third-party auth provider the signer presents; absent means better-auth. */
|
|
2253
|
+
openfortAuthProvider: Schema.optional(Schema.Literals(["better-auth", "oidc"])),
|
|
2254
|
+
/** Origin of the Ponder indexer the SDK reads directly; absent means no indexer reads. */
|
|
2255
|
+
indexerUrl: Schema.optional(Schema.String),
|
|
2010
2256
|
engineeringTelemetry: Schema.optional(EngineeringTelemetryBootstrapPolicy)
|
|
2011
2257
|
})
|
|
2012
2258
|
});
|
|
@@ -2069,6 +2315,7 @@ const CAPXUL_FUNCTIONS = {
|
|
|
2069
2315
|
list: "movement/activity:list",
|
|
2070
2316
|
summary: "movement/activity:summary"
|
|
2071
2317
|
},
|
|
2318
|
+
"movement/annotations": { overlay: "movement/annotations:overlay" },
|
|
2072
2319
|
"moneyExecution/actions": {
|
|
2073
2320
|
abandonPaymentExecution: "moneyExecution/actions:abandonPaymentExecution",
|
|
2074
2321
|
preparePermissionExecution: "moneyExecution/actions:preparePermissionExecution",
|
|
@@ -2081,6 +2328,7 @@ const CAPXUL_FUNCTIONS = {
|
|
|
2081
2328
|
prepareOrganizationPaymentExecution: "moneyExecution/paymentCommandActions:prepareOrganizationPaymentExecution",
|
|
2082
2329
|
submitPaymentCommandExecution: "moneyExecution/paymentCommandActions:submitPaymentCommandExecution"
|
|
2083
2330
|
},
|
|
2331
|
+
"moneyExecution/providerSendWitness": { read: "moneyExecution/providerSendWitness:read" },
|
|
2084
2332
|
media: {
|
|
2085
2333
|
generateUploadUrl: "media:generateUploadUrl",
|
|
2086
2334
|
setOrgLogo: "media:setOrgLogo",
|
|
@@ -2140,6 +2388,11 @@ const CAPXUL_FUNCTIONS = {
|
|
|
2140
2388
|
loadByAuthUserId: "smartAccount/queries:loadByAuthUserId",
|
|
2141
2389
|
loadBySmartAccountAddress: "smartAccount/queries:loadBySmartAccountAddress"
|
|
2142
2390
|
},
|
|
2391
|
+
e2eControls: {
|
|
2392
|
+
holdUserOperationReceipt: "e2eControls:holdUserOperationReceipt",
|
|
2393
|
+
isUserOperationReceiptHeld: "e2eControls:isUserOperationReceiptHeld",
|
|
2394
|
+
releaseUserOperationReceipt: "e2eControls:releaseUserOperationReceipt"
|
|
2395
|
+
},
|
|
2143
2396
|
system: { health: "system:health" }
|
|
2144
2397
|
};
|
|
2145
2398
|
//#endregion
|
|
@@ -2661,7 +2914,19 @@ const StoredPermissionCommandSchema = Schema.Struct({
|
|
|
2661
2914
|
roleKey: RoleKeySchema,
|
|
2662
2915
|
allowanceKey: Schema.NullOr(AllowanceKeySchema),
|
|
2663
2916
|
memberSafe: Schema.NullOr(SafeAddressSchema),
|
|
2664
|
-
expectedAuthorityEvents: Schema.Array(Bytes32Schema)
|
|
2917
|
+
expectedAuthorityEvents: Schema.Array(Bytes32Schema),
|
|
2918
|
+
/**
|
|
2919
|
+
* #1961 R03. The authenticated user who created the command, and the browser
|
|
2920
|
+
* linkage captured with it. Delayed verification and bounded retries read
|
|
2921
|
+
* this snapshot instead of the current viewer, so one command keeps one
|
|
2922
|
+
* origin. Both are absent on commands created before this contract.
|
|
2923
|
+
*
|
|
2924
|
+
* The context stays `unknown` on purpose: `sanitizeObservationContext` owns
|
|
2925
|
+
* the field allowlist, and every reader passes the stored value through it
|
|
2926
|
+
* rather than repeating those rules here.
|
|
2927
|
+
*/
|
|
2928
|
+
originatingAuthUserId: Schema.optional(Schema.String),
|
|
2929
|
+
originatingObservationContext: Schema.optional(Schema.Unknown)
|
|
2665
2930
|
});
|
|
2666
2931
|
Schema.Struct({
|
|
2667
2932
|
command: StoredPermissionCommandSchema,
|
|
@@ -5434,6 +5699,7 @@ const financialOpsContract = {
|
|
|
5434
5699
|
activitySummary: makeFunctionReference(CAPXUL_FUNCTIONS["movement/activity"].summary),
|
|
5435
5700
|
activityGet: makeFunctionReference(CAPXUL_FUNCTIONS["movement/activity"].get),
|
|
5436
5701
|
activityAnnotate: makeFunctionReference(CAPXUL_FUNCTIONS["movement/activity"].annotate),
|
|
5702
|
+
activityOverlay: makeFunctionReference(CAPXUL_FUNCTIONS["movement/annotations"].overlay),
|
|
5437
5703
|
verifyPaymentDocument: makeFunctionReference(CAPXUL_FUNCTIONS["financialOps/queries"].verifyPaymentDocument),
|
|
5438
5704
|
renderStoredDocument: makeFunctionReference(CAPXUL_FUNCTIONS["financialOps/queries"].renderStoredDocument)
|
|
5439
5705
|
};
|
|
@@ -5450,8 +5716,357 @@ const moneyExecutionContract = {
|
|
|
5450
5716
|
submitPaymentCommandExecution: makeFunctionReference(CAPXUL_FUNCTIONS["moneyExecution/paymentCommandActions"].submitPaymentCommandExecution)
|
|
5451
5717
|
};
|
|
5452
5718
|
//#endregion
|
|
5719
|
+
//#region src/ports/indexer-read.ts
|
|
5720
|
+
/** The indexer's own page bound. Exceeding it is a caller defect, not a refusal. */
|
|
5721
|
+
const INDEXER_MAX_LIMIT = 1e3;
|
|
5722
|
+
const LOWER_CASE_ADDRESS = /^0x[0-9a-f]{40}$/;
|
|
5723
|
+
/**
|
|
5724
|
+
* Refuse a page the indexer would refuse, before any implementation spends a
|
|
5725
|
+
* round trip on it. Every implementation calls this, so a consumer test written
|
|
5726
|
+
* against the hermetic adapter cannot pass on input production rejects. Throws
|
|
5727
|
+
* a `CapxulError`; callers convert it to an `IndexerReadError`.
|
|
5728
|
+
*/
|
|
5729
|
+
function assertIndexerPage(input) {
|
|
5730
|
+
const safe = String(input.safe).toLowerCase();
|
|
5731
|
+
if (!LOWER_CASE_ADDRESS.test(safe)) throw Errors.invalidInput("safe", "must be a lower-case EVM address");
|
|
5732
|
+
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}`);
|
|
5733
|
+
const offset = input.offset ?? 0;
|
|
5734
|
+
if (!Number.isSafeInteger(offset) || offset < 0) throw Errors.invalidInput("offset", "must be a non-negative integer");
|
|
5735
|
+
return safe;
|
|
5736
|
+
}
|
|
5737
|
+
var IndexerReadError = class extends Data.TaggedError("IndexerReadError") {};
|
|
5738
|
+
function indexerReadErrorFromCapxul(operation, error, cause = error) {
|
|
5739
|
+
return new IndexerReadError({
|
|
5740
|
+
operation,
|
|
5741
|
+
publicCode: error.code,
|
|
5742
|
+
publicError: error,
|
|
5743
|
+
cause,
|
|
5744
|
+
...error.details === void 0 ? {} : { details: error.details }
|
|
5745
|
+
});
|
|
5746
|
+
}
|
|
5747
|
+
Context.Service()("@capxul/sdk/ports/IndexerReadPort");
|
|
5748
|
+
//#endregion
|
|
5749
|
+
//#region src/surface/activity-stitch.ts
|
|
5750
|
+
/** Epoch milliseconds of the block that carried the row. */
|
|
5751
|
+
function blockMillis(row) {
|
|
5752
|
+
return Number(row.timestamp) * 1e3;
|
|
5753
|
+
}
|
|
5754
|
+
/** `0x1234…abcd` — the counterparty of an unclaimed transfer is an address. */
|
|
5755
|
+
function shortAddressLabel(address) {
|
|
5756
|
+
return address.length <= 12 ? address : `${address.slice(0, 6)}…${address.slice(-4)}`;
|
|
5757
|
+
}
|
|
5758
|
+
function directionFor(safe, transfer) {
|
|
5759
|
+
const from = transfer.from.toLowerCase();
|
|
5760
|
+
const to = transfer.to.toLowerCase();
|
|
5761
|
+
if (from === safe && to === safe) return "self";
|
|
5762
|
+
return to === safe ? "in" : "out";
|
|
5763
|
+
}
|
|
5764
|
+
/**
|
|
5765
|
+
* The Transfer a protocol Payment moved is the same money as its `payment`
|
|
5766
|
+
* row. One actor sees one item for it, so the Transfer is claimed and does not
|
|
5767
|
+
* become a second row. Another transfer in the same transaction that this
|
|
5768
|
+
* payment did not move stays unclaimed and keeps its own row.
|
|
5769
|
+
*/
|
|
5770
|
+
function claimedTransferIds(transfers, payments) {
|
|
5771
|
+
const claimed = /* @__PURE__ */ new Set();
|
|
5772
|
+
for (const payment of payments) {
|
|
5773
|
+
const match = transfers.find((transfer) => !claimed.has(transfer.id) && transfer.txHash === payment.txHash && transfer.token.toLowerCase() === payment.token.toLowerCase() && transfer.amount === payment.amount && transfer.to.toLowerCase() === payment.recipient.toLowerCase());
|
|
5774
|
+
if (match !== void 0) claimed.add(match.id);
|
|
5775
|
+
}
|
|
5776
|
+
return claimed;
|
|
5777
|
+
}
|
|
5778
|
+
function withAnnotation(item, annotation) {
|
|
5779
|
+
return annotation?.counterpartyLabel === void 0 ? item : {
|
|
5780
|
+
...item,
|
|
5781
|
+
counterpartyLabel: annotation.counterpartyLabel
|
|
5782
|
+
};
|
|
5783
|
+
}
|
|
5784
|
+
/**
|
|
5785
|
+
* Confirmation comes from the chain, never from Convex.
|
|
5786
|
+
*
|
|
5787
|
+
* A Ponder row for this Payment's settlement means settled, and its receipt is
|
|
5788
|
+
* the indexed transaction. No Ponder row and a Convex row that still says
|
|
5789
|
+
* settled means the block that carried it was dropped: the row goes back to
|
|
5790
|
+
* `settling` and its annotation, which keys on the Payment id, is untouched.
|
|
5791
|
+
*/
|
|
5792
|
+
function confirmPayment(intent, chain, hasSettlement) {
|
|
5793
|
+
if (chain !== void 0) return {
|
|
5794
|
+
...intent,
|
|
5795
|
+
status: "settled",
|
|
5796
|
+
receipt: {
|
|
5797
|
+
chainId: chain.chainId,
|
|
5798
|
+
transactionHash: chain.txHash
|
|
5799
|
+
},
|
|
5800
|
+
updatedAt: blockMillis(chain)
|
|
5801
|
+
};
|
|
5802
|
+
if (hasSettlement && intent.status === "settled") return {
|
|
5803
|
+
...intent,
|
|
5804
|
+
status: "settling",
|
|
5805
|
+
receipt: null
|
|
5806
|
+
};
|
|
5807
|
+
return intent;
|
|
5808
|
+
}
|
|
5809
|
+
/**
|
|
5810
|
+
* One unclaimed Transfer as an Activity row, or `null` when this SDK cannot
|
|
5811
|
+
* denominate its asset. The indexer serves only configured assets, so a row
|
|
5812
|
+
* the registry does not know means the SDK and that deployment disagree; it is
|
|
5813
|
+
* dropped rather than shown with a guessed currency.
|
|
5814
|
+
*/
|
|
5815
|
+
function transferItem(safe, transfer, annotation) {
|
|
5816
|
+
const asset = configuredMoneyAssetById(transfer.assetId);
|
|
5817
|
+
if (asset === null) return null;
|
|
5818
|
+
const direction = directionFor(safe, transfer);
|
|
5819
|
+
const counterparty = direction === "in" ? transfer.from : transfer.to;
|
|
5820
|
+
const at = blockMillis(transfer);
|
|
5821
|
+
return withAnnotation({
|
|
5822
|
+
kind: "movement",
|
|
5823
|
+
id: transfer.id,
|
|
5824
|
+
classification: "movement_only_outflow",
|
|
5825
|
+
amount: {
|
|
5826
|
+
currency: asset.peg ?? asset.symbol,
|
|
5827
|
+
value: formatUnits(transfer.amount, asset.decimals),
|
|
5828
|
+
decimals: asset.decimals
|
|
5829
|
+
},
|
|
5830
|
+
direction,
|
|
5831
|
+
counterpartyLabel: shortAddressLabel(counterparty),
|
|
5832
|
+
receipt: {
|
|
5833
|
+
chainId: transfer.chainId,
|
|
5834
|
+
transactionHash: transfer.txHash
|
|
5835
|
+
},
|
|
5836
|
+
createdAt: at,
|
|
5837
|
+
updatedAt: at
|
|
5838
|
+
}, annotation);
|
|
5839
|
+
}
|
|
5840
|
+
/** Newest first. A Payment precedes a Movement at one instant, as the ledger reader orders them. */
|
|
5841
|
+
function compareItems(left, right) {
|
|
5842
|
+
if (right.createdAt !== left.createdAt) return right.createdAt - left.createdAt;
|
|
5843
|
+
if (left.kind !== right.kind) return left.kind === "payment" ? -1 : 1;
|
|
5844
|
+
if (left.id === right.id) return 0;
|
|
5845
|
+
return left.id < right.id ? 1 : -1;
|
|
5846
|
+
}
|
|
5847
|
+
function stitchActivity(input) {
|
|
5848
|
+
const safe = input.safe.toLowerCase();
|
|
5849
|
+
const chainBySettlement = /* @__PURE__ */ new Map();
|
|
5850
|
+
for (const row of input.payments) chainBySettlement.set(row.settlementId.toLowerCase(), row);
|
|
5851
|
+
const chainByPaymentId = /* @__PURE__ */ new Map();
|
|
5852
|
+
const settledPaymentIds = /* @__PURE__ */ new Set();
|
|
5853
|
+
for (const link of input.links) {
|
|
5854
|
+
settledPaymentIds.add(link.paymentId);
|
|
5855
|
+
const row = chainBySettlement.get(link.settlementId.toLowerCase());
|
|
5856
|
+
if (row !== void 0) chainByPaymentId.set(link.paymentId, row);
|
|
5857
|
+
}
|
|
5858
|
+
const annotations = /* @__PURE__ */ new Map();
|
|
5859
|
+
for (const annotation of input.annotations) annotations.set(`${annotation.reference.kind}:${annotation.reference.id}`, annotation);
|
|
5860
|
+
const items = [];
|
|
5861
|
+
const claiming = [];
|
|
5862
|
+
for (const intent of input.intents) {
|
|
5863
|
+
if (intent.kind !== "payment") continue;
|
|
5864
|
+
const chain = chainByPaymentId.get(intent.id);
|
|
5865
|
+
if (chain !== void 0) claiming.push(chain);
|
|
5866
|
+
items.push(withAnnotation(confirmPayment(intent, chain, settledPaymentIds.has(intent.id)), annotations.get(`payment:${intent.id}`)));
|
|
5867
|
+
}
|
|
5868
|
+
const claimed = claimedTransferIds(input.transfers, claiming);
|
|
5869
|
+
for (const transfer of input.transfers) {
|
|
5870
|
+
if (claimed.has(transfer.id)) continue;
|
|
5871
|
+
const item = transferItem(safe, transfer, annotations.get(`movement:${transfer.id}`));
|
|
5872
|
+
if (item !== null) items.push(item);
|
|
5873
|
+
}
|
|
5874
|
+
return items.sort(compareItems);
|
|
5875
|
+
}
|
|
5876
|
+
/** The reference ids one stitched page needs its Convex overlay for. */
|
|
5877
|
+
function overlayReferences(items) {
|
|
5878
|
+
return items.map((item) => ({
|
|
5879
|
+
kind: item.kind,
|
|
5880
|
+
id: item.id
|
|
5881
|
+
}));
|
|
5882
|
+
}
|
|
5883
|
+
/**
|
|
5884
|
+
* Money in, money out and the pending share, one bucket per currency and
|
|
5885
|
+
* precision. Two assets that peg to the same currency at different precision
|
|
5886
|
+
* stay distinct buckets: their raw units are not interchangeable.
|
|
5887
|
+
*/
|
|
5888
|
+
function summarizeItems(items) {
|
|
5889
|
+
const buckets = /* @__PURE__ */ new Map();
|
|
5890
|
+
for (const item of items) {
|
|
5891
|
+
const key = `${item.amount.currency}:${String(item.amount.decimals)}`;
|
|
5892
|
+
const bucket = buckets.get(key) ?? {
|
|
5893
|
+
currency: item.amount.currency,
|
|
5894
|
+
decimals: item.amount.decimals,
|
|
5895
|
+
in: 0n,
|
|
5896
|
+
out: 0n,
|
|
5897
|
+
pendingIn: 0n
|
|
5898
|
+
};
|
|
5899
|
+
const raw = rawUnits(item.amount.value, item.amount.decimals);
|
|
5900
|
+
if (item.direction === "in") {
|
|
5901
|
+
bucket.in += raw;
|
|
5902
|
+
if (item.kind === "payment" && item.status !== "settled") bucket.pendingIn += raw;
|
|
5903
|
+
} else if (item.direction === "out") bucket.out += raw;
|
|
5904
|
+
buckets.set(key, bucket);
|
|
5905
|
+
}
|
|
5906
|
+
return [...buckets.values()].map((bucket) => ({
|
|
5907
|
+
currency: bucket.currency,
|
|
5908
|
+
decimals: bucket.decimals,
|
|
5909
|
+
in: bucket.in.toString(),
|
|
5910
|
+
out: bucket.out.toString(),
|
|
5911
|
+
net: (bucket.in - bucket.out).toString(),
|
|
5912
|
+
pendingIn: bucket.pendingIn.toString()
|
|
5913
|
+
}));
|
|
5914
|
+
}
|
|
5915
|
+
/** The inverse of `formatUnits`, kept exact: `"1.25"` at 6 decimals is `1250000n`. */
|
|
5916
|
+
function rawUnits(value, decimals) {
|
|
5917
|
+
const negative = value.startsWith("-");
|
|
5918
|
+
const [whole = "0", fraction = ""] = (negative ? value.slice(1) : value).split(".");
|
|
5919
|
+
const raw = BigInt(`${whole}${fraction.padEnd(decimals, "0").slice(0, decimals)}`);
|
|
5920
|
+
return negative ? -raw : raw;
|
|
5921
|
+
}
|
|
5922
|
+
//#endregion
|
|
5923
|
+
//#region src/surface/activity-read.ts
|
|
5924
|
+
/** How many Convex intent rows one page reads for its labels and pending rows. */
|
|
5925
|
+
const INTENT_WINDOW = 100;
|
|
5926
|
+
/** The window a summary aggregates over. The indexer's own page bound. */
|
|
5927
|
+
const SUMMARY_WINDOW = INDEXER_MAX_LIMIT;
|
|
5928
|
+
function parseCursor(value) {
|
|
5929
|
+
if (value === void 0) return 0;
|
|
5930
|
+
try {
|
|
5931
|
+
const parsed = JSON.parse(value);
|
|
5932
|
+
return Number.isSafeInteger(parsed.offset) && parsed.offset >= 0 ? parsed.offset : 0;
|
|
5933
|
+
} catch {
|
|
5934
|
+
return 0;
|
|
5935
|
+
}
|
|
5936
|
+
}
|
|
5937
|
+
function inRange(item, range) {
|
|
5938
|
+
if (range?.from !== void 0 && item.createdAt < range.from) return false;
|
|
5939
|
+
return range?.to === void 0 || item.createdAt <= range.to;
|
|
5940
|
+
}
|
|
5941
|
+
/** The caller's filter applied to the merged page. The join owns the order. */
|
|
5942
|
+
function matchesFilter(item, filter) {
|
|
5943
|
+
if (filter === void 0) return true;
|
|
5944
|
+
if (filter.kind !== void 0 && item.kind !== filter.kind) return false;
|
|
5945
|
+
if (filter.direction !== void 0 && item.direction !== filter.direction) return false;
|
|
5946
|
+
if (filter.status === void 0) return true;
|
|
5947
|
+
return item.kind === "payment" && filter.status.includes(item.status);
|
|
5948
|
+
}
|
|
5949
|
+
function overlay(deps, actor, controls, input) {
|
|
5950
|
+
return deps.convexCall.query(deps.functions.activityOverlay, copyInvocationObservation(controls, { input: {
|
|
5951
|
+
...actor === void 0 ? {} : { actor },
|
|
5952
|
+
...input.references === void 0 ? {} : { references: input.references },
|
|
5953
|
+
...input.settlementIds === void 0 ? {} : { settlementIds: input.settlementIds }
|
|
5954
|
+
} }));
|
|
5955
|
+
}
|
|
5956
|
+
function convexPage(deps, actor, controls, input) {
|
|
5957
|
+
return deps.convexCall.query(deps.functions.activityList, copyInvocationObservation(controls, { input: {
|
|
5958
|
+
...actor === void 0 ? {} : { actor },
|
|
5959
|
+
...input.cursor === void 0 ? {} : { cursor: input.cursor },
|
|
5960
|
+
...input.limit === void 0 ? {} : { limit: input.limit },
|
|
5961
|
+
...input.range === void 0 ? {} : { range: input.range }
|
|
5962
|
+
} }));
|
|
5963
|
+
}
|
|
5964
|
+
/** Both indexed tables for one Safe, or `null` when the indexer cannot answer. */
|
|
5965
|
+
function chainRows(deps, safe, limit) {
|
|
5966
|
+
const page = {
|
|
5967
|
+
safe,
|
|
5968
|
+
limit: Math.min(limit, INDEXER_MAX_LIMIT)
|
|
5969
|
+
};
|
|
5970
|
+
return Effect.all([deps.indexer.read("transfer", page), deps.indexer.read("payment", page)], { concurrency: 2 }).pipe(Effect.map(([transfers, payments]) => ({
|
|
5971
|
+
transfers,
|
|
5972
|
+
payments
|
|
5973
|
+
})), Effect.catch(() => Effect.succeed(null)));
|
|
5974
|
+
}
|
|
5975
|
+
/** The stitched, filtered rows of one window, newest first. */
|
|
5976
|
+
function stitchedWindow(deps, actor, controls, window, params) {
|
|
5977
|
+
return Effect.gen(function* () {
|
|
5978
|
+
const scope = yield* overlay(deps, actor, controls, {});
|
|
5979
|
+
const convex = yield* convexPage(deps, actor, controls, {
|
|
5980
|
+
limit: INTENT_WINDOW,
|
|
5981
|
+
...params.range === void 0 ? {} : { range: params.range }
|
|
5982
|
+
});
|
|
5983
|
+
const rows = scope.safe === null ? null : yield* chainRows(deps, scope.safe, window);
|
|
5984
|
+
if (rows === null || scope.safe === null) return {
|
|
5985
|
+
items: convex.items.filter((item) => matchesFilter(item, params.filter)),
|
|
5986
|
+
stitched: false
|
|
5987
|
+
};
|
|
5988
|
+
const decoration = yield* overlay(deps, actor, controls, {
|
|
5989
|
+
references: [...overlayReferences(convex.items), ...rows.transfers.map((row) => ({
|
|
5990
|
+
kind: "movement",
|
|
5991
|
+
id: row.id
|
|
5992
|
+
}))],
|
|
5993
|
+
settlementIds: rows.payments.map((row) => row.settlementId)
|
|
5994
|
+
});
|
|
5995
|
+
return {
|
|
5996
|
+
items: stitchActivity({
|
|
5997
|
+
safe: scope.safe,
|
|
5998
|
+
transfers: rows.transfers,
|
|
5999
|
+
payments: rows.payments,
|
|
6000
|
+
intents: convex.items,
|
|
6001
|
+
links: decoration.links,
|
|
6002
|
+
annotations: decoration.annotations
|
|
6003
|
+
}).filter((item) => inRange(item, params.range) && matchesFilter(item, params.filter)),
|
|
6004
|
+
stitched: true
|
|
6005
|
+
};
|
|
6006
|
+
});
|
|
6007
|
+
}
|
|
6008
|
+
function readActivityPage(deps, actor, controls, params) {
|
|
6009
|
+
const limit = params?.limit ?? 25;
|
|
6010
|
+
const offset = parseCursor(params?.cursor);
|
|
6011
|
+
return stitchedWindow(deps, actor, controls, offset + limit + 1, {
|
|
6012
|
+
...params?.range === void 0 ? {} : { range: params.range },
|
|
6013
|
+
...params?.filter === void 0 ? {} : { filter: params.filter }
|
|
6014
|
+
}).pipe(Effect.map(({ items, stitched }) => {
|
|
6015
|
+
const observedAt = Date.now();
|
|
6016
|
+
if (!stitched) return {
|
|
6017
|
+
items: items.slice(0, limit),
|
|
6018
|
+
cursor: null,
|
|
6019
|
+
checkpoint: observedAt,
|
|
6020
|
+
observedAt
|
|
6021
|
+
};
|
|
6022
|
+
return {
|
|
6023
|
+
items: items.slice(offset, offset + limit),
|
|
6024
|
+
cursor: items.length > offset + limit ? JSON.stringify({ offset: offset + limit }) : null,
|
|
6025
|
+
checkpoint: observedAt,
|
|
6026
|
+
observedAt
|
|
6027
|
+
};
|
|
6028
|
+
}));
|
|
6029
|
+
}
|
|
6030
|
+
function readActivitySummary(deps, actor, controls, params) {
|
|
6031
|
+
return stitchedWindow(deps, actor, controls, SUMMARY_WINDOW, params?.window === void 0 ? {} : { range: params.window }).pipe(Effect.map(({ items }) => ({
|
|
6032
|
+
window: {
|
|
6033
|
+
from: params?.window?.from ?? null,
|
|
6034
|
+
to: params?.window?.to ?? null
|
|
6035
|
+
},
|
|
6036
|
+
totals: summarizeItems(items),
|
|
6037
|
+
count: items.length,
|
|
6038
|
+
observedAt: Date.now()
|
|
6039
|
+
})));
|
|
6040
|
+
}
|
|
6041
|
+
/**
|
|
6042
|
+
* Tell the caller when the indexed page for this Safe changed.
|
|
6043
|
+
*
|
|
6044
|
+
* The transport is the indexer's one SSE connection, wired in the Ponder
|
|
6045
|
+
* adapter under `activity.subscribe`. A reactive snapshot is not a product
|
|
6046
|
+
* event, so nothing here emits telemetry: the adapter reports a teardown, and
|
|
6047
|
+
* the caller re-reads.
|
|
6048
|
+
*/
|
|
6049
|
+
function subscribeActivity(deps, actor, controls, onChange) {
|
|
6050
|
+
return Effect.gen(function* () {
|
|
6051
|
+
const scope = yield* overlay(deps, actor, controls, {});
|
|
6052
|
+
if (scope.safe === null) return () => {};
|
|
6053
|
+
const page = {
|
|
6054
|
+
safe: scope.safe,
|
|
6055
|
+
limit: INTENT_WINDOW
|
|
6056
|
+
};
|
|
6057
|
+
let delivered = false;
|
|
6058
|
+
const notify = () => {
|
|
6059
|
+
if (delivered) onChange();
|
|
6060
|
+
delivered = true;
|
|
6061
|
+
};
|
|
6062
|
+
return yield* deps.indexer.subscribe("transfer", page, (snapshot) => {
|
|
6063
|
+
if (snapshot.status === "ok") notify();
|
|
6064
|
+
});
|
|
6065
|
+
});
|
|
6066
|
+
}
|
|
6067
|
+
//#endregion
|
|
5453
6068
|
//#region package.json
|
|
5454
|
-
var version = "4.2.0-rc.
|
|
6069
|
+
var version = "4.2.0-rc.11";
|
|
5455
6070
|
//#endregion
|
|
5456
6071
|
//#region src/telemetry/exception-projection.ts
|
|
5457
6072
|
/** Fixed fallback for failures that have no safe message. */
|
|
@@ -5644,6 +6259,22 @@ function observeFailedResult(result, adapter, operation, origin) {
|
|
|
5644
6259
|
return result;
|
|
5645
6260
|
}
|
|
5646
6261
|
/**
|
|
6262
|
+
* @internal Reports a failure that can never become a method result.
|
|
6263
|
+
*
|
|
6264
|
+
* A reactive stream is torn down after its call already returned, so nothing
|
|
6265
|
+
* downstream observes it. `captureException` is the right kind: the caller did
|
|
6266
|
+
* not ask for the end, so it is not an expected product outcome.
|
|
6267
|
+
*
|
|
6268
|
+
* A caller that DID ask for the end — the session it authenticated with
|
|
6269
|
+
* expired, and the service closed the stream on purpose — passes
|
|
6270
|
+
* `kind: "operation"` instead, so the refusal lands in the expected-outcome
|
|
6271
|
+
* stream its code already declares.
|
|
6272
|
+
*/
|
|
6273
|
+
function observeStreamFailure(adapter, operation, cause, kind = "exception") {
|
|
6274
|
+
if (adapter === void 0) return;
|
|
6275
|
+
report(adapter, kind, operation, cause, resolveAdapterSnapshot(adapter));
|
|
6276
|
+
}
|
|
6277
|
+
/**
|
|
5647
6278
|
* One failure, one event. The identity machine reports a failed transition
|
|
5648
6279
|
* first; when the same `CapxulError` then surfaces as a public method result
|
|
5649
6280
|
* (directly or down its `cause` chain), the boundary drops that second report.
|
|
@@ -5721,6 +6352,34 @@ function ignoreDeliveryFailure(delivery) {
|
|
|
5721
6352
|
} catch {}
|
|
5722
6353
|
}
|
|
5723
6354
|
/**
|
|
6355
|
+
* Fold a caller-captured attempt identity into the host's resolved context.
|
|
6356
|
+
*
|
|
6357
|
+
* The host's `resolveContext` runs at the moment an SDK method is entered, so a
|
|
6358
|
+
* host that mints a correlation per resolve gives every operation of one user
|
|
6359
|
+
* attempt a different identity — and a user or Organization switch between the
|
|
6360
|
+
* click and the reply relabels an action that already started. When the caller
|
|
6361
|
+
* captured `correlation_id`/`journey_id` BEFORE the action started, that is the
|
|
6362
|
+
* attempt, and it replaces what the resolver returned for this one call. The
|
|
6363
|
+
* result is what the SDK span/Log, the failure envelope and the backend carrier
|
|
6364
|
+
* all read, so the three agree by construction rather than by convention.
|
|
6365
|
+
*
|
|
6366
|
+
* A caller that captured nothing gets the resolver's context unchanged, so
|
|
6367
|
+
* automatic reads keep their own invocation context.
|
|
6368
|
+
*/
|
|
6369
|
+
function withCapturedAttemptContext(context, controls) {
|
|
6370
|
+
const correlationId = controls?.correlation_id;
|
|
6371
|
+
const journeyId = controls?.journey_id;
|
|
6372
|
+
if (correlationId === void 0 && journeyId === void 0) return context;
|
|
6373
|
+
const captured = sanitizeObservationContext({
|
|
6374
|
+
...correlationId === void 0 ? {} : { correlationId },
|
|
6375
|
+
...journeyId === void 0 ? {} : { journeyId }
|
|
6376
|
+
});
|
|
6377
|
+
return captured === void 0 ? context : {
|
|
6378
|
+
...context,
|
|
6379
|
+
...captured
|
|
6380
|
+
};
|
|
6381
|
+
}
|
|
6382
|
+
/**
|
|
5724
6383
|
* Map an ALREADY-sanitized observation context to the snake_case PostHog
|
|
5725
6384
|
* property keys. Shared by the failure boundary here and the host
|
|
5726
6385
|
* success-telemetry seam (`telemetry/from-posthog.ts`) so both attach identical
|
|
@@ -6718,6 +7377,11 @@ function paymentExecutionIntent(input) {
|
|
|
6718
7377
|
}
|
|
6719
7378
|
function makeFinancialOpsMethods(deps) {
|
|
6720
7379
|
const fns = deps.functions ?? financialOpsContract;
|
|
7380
|
+
const indexerDeps = deps.indexerRead === void 0 ? void 0 : {
|
|
7381
|
+
indexer: deps.indexerRead,
|
|
7382
|
+
convexCall: deps.convexCall,
|
|
7383
|
+
functions: fns
|
|
7384
|
+
};
|
|
6721
7385
|
const executionFns = deps.moneyExecutionFunctions ?? moneyExecutionContract;
|
|
6722
7386
|
const requestKeyScope = deps.requestKeyScope ?? randomPaymentRequestKey();
|
|
6723
7387
|
const inFlightPersonalPayments = /* @__PURE__ */ new Map();
|
|
@@ -6948,22 +7612,26 @@ function makeFinancialOpsMethods(deps) {
|
|
|
6948
7612
|
}
|
|
6949
7613
|
},
|
|
6950
7614
|
activity: {
|
|
7615
|
+
subscribe: (onChange, params, options) => {
|
|
7616
|
+
const controls = deps.invocationControls?.(options) ?? options;
|
|
7617
|
+
return runPortEffect(captureActivityInput(() => Effect.succeed(activityActorField(params?.actor).actor)).pipe(Effect.flatMap((actor) => indexerDeps === void 0 ? Effect.succeed(() => {}) : Effect.suspend(() => subscribeActivity(indexerDeps, actor, controls, onChange)).pipe(Effect.tap(() => observeActivityScope(actor))))), controls, CAPXUL_OPERATIONS.activity.subscribe, deps.runPromise);
|
|
7618
|
+
},
|
|
6951
7619
|
list: (params, options) => {
|
|
6952
7620
|
const controls = deps.invocationControls?.(options) ?? options;
|
|
6953
|
-
return runPortEffect(captureActivityInput(() => Effect.succeed(activityActorField(params?.actor).actor)).pipe(Effect.flatMap((actor) => Effect.suspend(() => deps.convexCall.query(fns.activityList, copyInvocationObservation(controls, { input: {
|
|
7621
|
+
return runPortEffect(captureActivityInput(() => Effect.succeed(activityActorField(params?.actor).actor)).pipe(Effect.flatMap((actor) => Effect.suspend(() => indexerDeps === void 0 ? deps.convexCall.query(fns.activityList, copyInvocationObservation(controls, { input: {
|
|
6954
7622
|
...actor === void 0 ? {} : { actor },
|
|
6955
7623
|
...params?.cursor === void 0 ? {} : { cursor: params.cursor },
|
|
6956
7624
|
...params?.limit === void 0 ? {} : { limit: params.limit },
|
|
6957
7625
|
...params?.range === void 0 ? {} : { range: params.range },
|
|
6958
7626
|
...params?.filter === void 0 ? {} : { filter: params.filter }
|
|
6959
|
-
} }))).pipe(Effect.tap(() => observeActivityScope(actor))))), controls, CAPXUL_OPERATIONS.activity.list, deps.runPromise);
|
|
7627
|
+
} })) : readActivityPage(indexerDeps, actor, controls, params)).pipe(Effect.tap(() => observeActivityScope(actor))))), controls, CAPXUL_OPERATIONS.activity.list, deps.runPromise);
|
|
6960
7628
|
},
|
|
6961
7629
|
summary: (params, options) => {
|
|
6962
7630
|
const controls = deps.invocationControls?.(options) ?? options;
|
|
6963
|
-
return runPortEffect(captureActivityInput(() => Effect.succeed(activityActorField(params?.actor).actor)).pipe(Effect.flatMap((actor) => Effect.suspend(() => deps.convexCall.query(fns.activitySummary, copyInvocationObservation(controls, { input: {
|
|
7631
|
+
return runPortEffect(captureActivityInput(() => Effect.succeed(activityActorField(params?.actor).actor)).pipe(Effect.flatMap((actor) => Effect.suspend(() => indexerDeps === void 0 ? deps.convexCall.query(fns.activitySummary, copyInvocationObservation(controls, { input: {
|
|
6964
7632
|
...actor === void 0 ? {} : { actor },
|
|
6965
7633
|
...params?.window === void 0 ? {} : { window: params.window }
|
|
6966
|
-
} }))).pipe(Effect.tap(() => observeActivityScope(actor))))), controls, CAPXUL_OPERATIONS.activity.summary, deps.runPromise);
|
|
7634
|
+
} })) : readActivitySummary(indexerDeps, actor, controls, params)).pipe(Effect.tap(() => observeActivityScope(actor))))), controls, CAPXUL_OPERATIONS.activity.summary, deps.runPromise);
|
|
6967
7635
|
},
|
|
6968
7636
|
get: (reference, options) => {
|
|
6969
7637
|
const controls = deps.invocationControls?.(options) ?? options;
|
|
@@ -7852,7 +8520,8 @@ var BetterAuthBrowserAdapter = class {
|
|
|
7852
8520
|
try {
|
|
7853
8521
|
const res = await this.fetchImpl(this.url("/api/auth/get-session"), withSignal$1({
|
|
7854
8522
|
method: "GET",
|
|
7855
|
-
credentials: "include"
|
|
8523
|
+
credentials: "include",
|
|
8524
|
+
cache: "no-store"
|
|
7856
8525
|
}, options?.signal));
|
|
7857
8526
|
if (options?.signal?.aborted) return {
|
|
7858
8527
|
ok: false,
|
|
@@ -7924,7 +8593,8 @@ var BetterAuthBrowserAdapter = class {
|
|
|
7924
8593
|
try {
|
|
7925
8594
|
const res = await this.fetchImpl(this.url("/api/auth/convex/token"), withSignal$1({
|
|
7926
8595
|
method: "GET",
|
|
7927
|
-
credentials: "include"
|
|
8596
|
+
credentials: "include",
|
|
8597
|
+
cache: "no-store"
|
|
7928
8598
|
}, options?.signal));
|
|
7929
8599
|
if (options?.signal?.aborted) return {
|
|
7930
8600
|
ok: false,
|
|
@@ -8437,7 +9107,7 @@ function isTransientBootstrapFailure(error) {
|
|
|
8437
9107
|
const status = error.details?.httpStatus;
|
|
8438
9108
|
return typeof status === "number" && isTransientHttpStatus(status);
|
|
8439
9109
|
}
|
|
8440
|
-
async function safeText(res) {
|
|
9110
|
+
async function safeText$1(res) {
|
|
8441
9111
|
try {
|
|
8442
9112
|
return await res.text();
|
|
8443
9113
|
} catch {
|
|
@@ -8510,6 +9180,8 @@ var HttpBootstrapAdapter = class {
|
|
|
8510
9180
|
siteBaseUrl: normalizeRuntimeUrl("siteBaseUrl", decodedState.siteBaseUrl),
|
|
8511
9181
|
openfortPublishableKey: decodedState.openfortPublishableKey,
|
|
8512
9182
|
shieldPublishableKey: decodedState.shieldPublishableKey,
|
|
9183
|
+
...decodedState.openfortAuthProvider === void 0 ? {} : { openfortAuthProvider: decodedState.openfortAuthProvider },
|
|
9184
|
+
...decodedState.indexerUrl === void 0 ? {} : { indexerUrl: decodedState.indexerUrl },
|
|
8513
9185
|
...decodedEngineeringTelemetry !== void 0 && Result.isSuccess(decodedEngineeringTelemetry) ? { engineeringTelemetry: decodedEngineeringTelemetry.success } : {}
|
|
8514
9186
|
};
|
|
8515
9187
|
},
|
|
@@ -8518,7 +9190,7 @@ var HttpBootstrapAdapter = class {
|
|
|
8518
9190
|
return bootstrapErrorFromCapxul("malformedBody", Errors.providerError("convex", "bootstrap", cause instanceof Error ? cause : new Error(String(cause))));
|
|
8519
9191
|
}
|
|
8520
9192
|
});
|
|
8521
|
-
return Effect.promise(() => safeText(res)).pipe(Effect.flatMap((body) => {
|
|
9193
|
+
return Effect.promise(() => safeText$1(res)).pipe(Effect.flatMap((body) => {
|
|
8522
9194
|
if (res.status === 401 || body.startsWith("NOT_AUTHENTICATED")) return Effect.fail(bootstrapErrorFromCapxul("notAuthenticated", Errors.notAuthenticated()));
|
|
8523
9195
|
if (res.status === 400 || body.startsWith("INVALID_INPUT")) return Effect.fail(bootstrapErrorFromCapxul("invalidInput", Errors.invalidInput("publishableKey", "rejected by bootstrap")));
|
|
8524
9196
|
const responseBody = body.slice(0, 300);
|
|
@@ -8748,6 +9420,12 @@ function makeConnectionId() {
|
|
|
8748
9420
|
}
|
|
8749
9421
|
//#endregion
|
|
8750
9422
|
//#region src/adapters/convex-call/ConvexCallAdapter.ts
|
|
9423
|
+
/**
|
|
9424
|
+
* A socket that has not emitted `close` within this window is not going to.
|
|
9425
|
+
* ponytail: one fixed bound; `closeTimeoutMs` is the knob if a slow runtime
|
|
9426
|
+
* ever needs a different one.
|
|
9427
|
+
*/
|
|
9428
|
+
const DEFAULT_CLOSE_TIMEOUT_MS = 5e3;
|
|
8751
9429
|
/** Exact floor-first allowlist; every additional handler must migrate its validator first. */
|
|
8752
9430
|
const OBSERVED_CONVEX_ACTIONS = /* @__PURE__ */ new Set([
|
|
8753
9431
|
"payroll/actions:authorizeRun",
|
|
@@ -8760,6 +9438,9 @@ const OBSERVED_CONVEX_ACTIONS = /* @__PURE__ */ new Set([
|
|
|
8760
9438
|
"org/actions:resumeBootstrapSubmission",
|
|
8761
9439
|
"org/actions:confirmBootstrap",
|
|
8762
9440
|
"org/actions:readTreasury",
|
|
9441
|
+
"moneyExecution/actions:preparePermissionExecution",
|
|
9442
|
+
"moneyExecution/actions:submitPermissionExecution",
|
|
9443
|
+
"permission/actions:verify",
|
|
8763
9444
|
"moneyExecution/actions:preparePaymentExecution",
|
|
8764
9445
|
"moneyExecution/actions:abandonPaymentExecution",
|
|
8765
9446
|
"moneyExecution/actions:submitPaymentExecution",
|
|
@@ -8784,6 +9465,9 @@ const OBSERVED_CONVEX_QUERIES = /* @__PURE__ */ new Set([
|
|
|
8784
9465
|
"payroll/queries:groups",
|
|
8785
9466
|
"payroll/queries:terms",
|
|
8786
9467
|
"financialOps/queries:resolveRecipient",
|
|
9468
|
+
"org/queries:me",
|
|
9469
|
+
"permission/queries:read",
|
|
9470
|
+
"permission/queries:authorize",
|
|
8787
9471
|
"identity/queries:loadByAuthUserId",
|
|
8788
9472
|
"smartAccount/queries:loadByAuthUserId",
|
|
8789
9473
|
"org/lifecycle:load"
|
|
@@ -8802,6 +9486,7 @@ const OBSERVED_CONVEX_MUTATIONS = /* @__PURE__ */ new Set([
|
|
|
8802
9486
|
"financialOps/requestsInbox:decline",
|
|
8803
9487
|
"payroll/mutations:saveGroup",
|
|
8804
9488
|
"payroll/mutations:removeGroup",
|
|
9489
|
+
"permission/mutations:command",
|
|
8805
9490
|
"identity/mutations:create",
|
|
8806
9491
|
"identity/mutations:update",
|
|
8807
9492
|
"identity/mutations:completeOnboarding",
|
|
@@ -8816,7 +9501,9 @@ var ConvexCallAdapter = class {
|
|
|
8816
9501
|
#applicationId;
|
|
8817
9502
|
#observation;
|
|
8818
9503
|
#connectionMonitor;
|
|
9504
|
+
#closeTimeoutMs;
|
|
8819
9505
|
constructor(deps) {
|
|
9506
|
+
this.#closeTimeoutMs = deps.closeTimeoutMs ?? DEFAULT_CLOSE_TIMEOUT_MS;
|
|
8820
9507
|
this.#connectionMonitor = new ConvexConnectionMonitor();
|
|
8821
9508
|
const webSocketConstructor = this.#connectionMonitor.observedWebSocketConstructor();
|
|
8822
9509
|
this.#client = deps.client ?? new ConvexClient(deps.convexUrl, webSocketConstructor === void 0 ? {} : { webSocketConstructor });
|
|
@@ -8915,11 +9602,33 @@ var ConvexCallAdapter = class {
|
|
|
8915
9602
|
callback({ status: "loading" });
|
|
8916
9603
|
})));
|
|
8917
9604
|
}
|
|
9605
|
+
/**
|
|
9606
|
+
* Teardown always completes. `ConvexClient.close()` resolves only when the
|
|
9607
|
+
* socket emits `close`, and `WebSocketManager.close()` in the `connecting`
|
|
9608
|
+
* state arms `ws.onopen = () => ws.close()` without ever closing the socket
|
|
9609
|
+
* itself — so a handshake that stalls right after a server-initiated close
|
|
9610
|
+
* (code 1012, which is what a Convex deploy sends) leaves that promise
|
|
9611
|
+
* pending for as long as the page lives. Convex marks the client closed and
|
|
9612
|
+
* terminates the socket manager synchronously before returning it, so
|
|
9613
|
+
* abandoning it drops nothing. A rejection is dropped for the same reason —
|
|
9614
|
+
* the layer release turns it into a defect that fails the whole scope close,
|
|
9615
|
+
* and teardown has no caller who can act on it.
|
|
9616
|
+
*/
|
|
8918
9617
|
async close() {
|
|
8919
9618
|
this.#connectionMonitor.close();
|
|
8920
|
-
await this.#client.close();
|
|
9619
|
+
await settledWithin(this.#client.close(), this.#closeTimeoutMs);
|
|
8921
9620
|
}
|
|
8922
9621
|
};
|
|
9622
|
+
function settledWithin(promise, timeoutMs) {
|
|
9623
|
+
return new Promise((resolve) => {
|
|
9624
|
+
const timer = setTimeout(resolve, timeoutMs);
|
|
9625
|
+
const finish = () => {
|
|
9626
|
+
clearTimeout(timer);
|
|
9627
|
+
resolve();
|
|
9628
|
+
};
|
|
9629
|
+
promise.then(finish, finish);
|
|
9630
|
+
});
|
|
9631
|
+
}
|
|
8923
9632
|
function ConvexCallLayer(deps) {
|
|
8924
9633
|
return Layer.effect(ConvexCallPortTag, Effect.acquireRelease(Effect.sync(() => new ConvexCallAdapter(deps)), (adapter) => Effect.promise(() => adapter.close()).pipe(Effect.orDie)));
|
|
8925
9634
|
}
|
|
@@ -9872,6 +10581,32 @@ var ConsoleDiagnosticAdapter = class {
|
|
|
9872
10581
|
}
|
|
9873
10582
|
};
|
|
9874
10583
|
//#endregion
|
|
10584
|
+
//#region src/adapters/diagnostic/EffectLogDiagnosticAdapter.ts
|
|
10585
|
+
/**
|
|
10586
|
+
* Diagnostic breadcrumbs as SDK log records. The signer's readiness cycle
|
|
10587
|
+
* traces (`openfort.*`) used to reach only the browser console; through this
|
|
10588
|
+
* adapter they run in the client's own Effect context, so the engineering
|
|
10589
|
+
* telemetry layer ships them to PostHog Logs beside `identity.transition`.
|
|
10590
|
+
* Logging never throws back into the cycle that emitted it.
|
|
10591
|
+
*/
|
|
10592
|
+
var EffectLogDiagnosticAdapter = class {
|
|
10593
|
+
#runPromise;
|
|
10594
|
+
#console;
|
|
10595
|
+
constructor(context, consoleFallback) {
|
|
10596
|
+
this.#runPromise = Effect.runPromiseWith(context);
|
|
10597
|
+
this.#console = consoleFallback;
|
|
10598
|
+
}
|
|
10599
|
+
trace(scope, detail) {
|
|
10600
|
+
this.#console?.trace(scope, detail);
|
|
10601
|
+
try {
|
|
10602
|
+
this.#runPromise(Effect.logInfo(scope).pipe(Effect.annotateLogs({
|
|
10603
|
+
...detail,
|
|
10604
|
+
diagnostic_scope: scope
|
|
10605
|
+
}), Effect.catchCause(() => Effect.void))).catch(() => void 0);
|
|
10606
|
+
} catch {}
|
|
10607
|
+
}
|
|
10608
|
+
};
|
|
10609
|
+
//#endregion
|
|
9875
10610
|
//#region src/openfort/create-openfort-browser-signer.ts
|
|
9876
10611
|
function httpStatusOf(link) {
|
|
9877
10612
|
const carrier = link;
|
|
@@ -10003,15 +10738,35 @@ function createOpenfortBrowserSignerFromBootstrap(bootstrap, options = {}) {
|
|
|
10003
10738
|
});
|
|
10004
10739
|
throw failOpenfort("configure", "no-secure-context", /* @__PURE__ */ new Error("Web Crypto unavailable: browser is not a secure context"));
|
|
10005
10740
|
}
|
|
10006
|
-
|
|
10007
|
-
|
|
10741
|
+
const authProvider = bootstrap.openfortAuthProvider ?? "better-auth";
|
|
10742
|
+
function accessTokenUrl() {
|
|
10743
|
+
return authProvider === "oidc" ? `${authBaseUrl}/convex/token` : `${authBaseUrl}/get-session`;
|
|
10008
10744
|
}
|
|
10009
10745
|
function encryptionSessionUrl() {
|
|
10010
10746
|
return `${authBaseUrl}/encryption-session`;
|
|
10011
10747
|
}
|
|
10748
|
+
let cycleStartedAt = null;
|
|
10749
|
+
let stepStartedAt = 0;
|
|
10750
|
+
let tokenCalls = 0;
|
|
10751
|
+
const sinceCycleStart = () => cycleStartedAt === null ? void 0 : Date.now() - cycleStartedAt;
|
|
10752
|
+
const step = (name, extra = {}) => {
|
|
10753
|
+
const now = Date.now();
|
|
10754
|
+
diagnostic?.trace("openfort.step", {
|
|
10755
|
+
step: name,
|
|
10756
|
+
duration_ms: now - stepStartedAt,
|
|
10757
|
+
elapsed_ms: sinceCycleStart(),
|
|
10758
|
+
...extra
|
|
10759
|
+
});
|
|
10760
|
+
stepStartedAt = now;
|
|
10761
|
+
};
|
|
10012
10762
|
async function fetchBetterAuthAccessToken() {
|
|
10763
|
+
tokenCalls += 1;
|
|
10764
|
+
const tokenStartedAt = Date.now();
|
|
10013
10765
|
try {
|
|
10014
|
-
const response = await fetch(
|
|
10766
|
+
const response = await fetch(accessTokenUrl(), {
|
|
10767
|
+
credentials: "include",
|
|
10768
|
+
cache: "no-store"
|
|
10769
|
+
});
|
|
10015
10770
|
if (!response.ok) {
|
|
10016
10771
|
diagnostic?.trace("openfort.token", {
|
|
10017
10772
|
ok: false,
|
|
@@ -10021,7 +10776,8 @@ function createOpenfortBrowserSignerFromBootstrap(bootstrap, options = {}) {
|
|
|
10021
10776
|
});
|
|
10022
10777
|
return null;
|
|
10023
10778
|
}
|
|
10024
|
-
const
|
|
10779
|
+
const body = await response.json();
|
|
10780
|
+
const token = (authProvider === "oidc" ? body.token : body.session?.token)?.trim();
|
|
10025
10781
|
if (token === void 0 || token.length === 0) {
|
|
10026
10782
|
diagnostic?.trace("openfort.token", {
|
|
10027
10783
|
ok: false,
|
|
@@ -10032,7 +10788,11 @@ function createOpenfortBrowserSignerFromBootstrap(bootstrap, options = {}) {
|
|
|
10032
10788
|
}
|
|
10033
10789
|
diagnostic?.trace("openfort.token", {
|
|
10034
10790
|
ok: true,
|
|
10035
|
-
tokenPresent: true
|
|
10791
|
+
tokenPresent: true,
|
|
10792
|
+
provider: authProvider,
|
|
10793
|
+
call: tokenCalls,
|
|
10794
|
+
duration_ms: Date.now() - tokenStartedAt,
|
|
10795
|
+
elapsed_ms: sinceCycleStart()
|
|
10036
10796
|
});
|
|
10037
10797
|
return token;
|
|
10038
10798
|
} catch (cause) {
|
|
@@ -10048,7 +10808,7 @@ function createOpenfortBrowserSignerFromBootstrap(bootstrap, options = {}) {
|
|
|
10048
10808
|
baseConfiguration: { publishableKey: bootstrap.openfortPublishableKey },
|
|
10049
10809
|
shieldConfiguration: { shieldPublishableKey: bootstrap.shieldPublishableKey },
|
|
10050
10810
|
thirdPartyAuth: {
|
|
10051
|
-
provider: ThirdPartyOAuthProvider.BETTER_AUTH,
|
|
10811
|
+
provider: authProvider === "oidc" ? ThirdPartyOAuthProvider.OIDC : ThirdPartyOAuthProvider.BETTER_AUTH,
|
|
10052
10812
|
getAccessToken: fetchBetterAuthAccessToken
|
|
10053
10813
|
}
|
|
10054
10814
|
});
|
|
@@ -10088,10 +10848,16 @@ function createOpenfortBrowserSignerFromBootstrap(bootstrap, options = {}) {
|
|
|
10088
10848
|
let walletReadyPromise = null;
|
|
10089
10849
|
function startWalletReady() {
|
|
10090
10850
|
return (async () => {
|
|
10851
|
+
const startedAt = Date.now();
|
|
10852
|
+
cycleStartedAt = startedAt;
|
|
10853
|
+
stepStartedAt = startedAt;
|
|
10854
|
+
tokenCalls = 0;
|
|
10091
10855
|
if (isInsecureBrowserContext()) failNoSecureContext();
|
|
10092
10856
|
await openfort.waitForInitialization();
|
|
10857
|
+
step("initialize");
|
|
10093
10858
|
const accessToken = await fetchBetterAuthAccessToken();
|
|
10094
10859
|
if (accessToken === null) throw openfortProviderError("token", /* @__PURE__ */ new Error("Better Auth access token unavailable for Openfort"));
|
|
10860
|
+
step("token");
|
|
10095
10861
|
let encryptionResponse;
|
|
10096
10862
|
try {
|
|
10097
10863
|
encryptionResponse = await fetch(encryptionSessionUrl(), {
|
|
@@ -10134,10 +10900,12 @@ function createOpenfortBrowserSignerFromBootstrap(bootstrap, options = {}) {
|
|
|
10134
10900
|
ok: true,
|
|
10135
10901
|
httpStatus: encryptionResponse.status
|
|
10136
10902
|
});
|
|
10903
|
+
step("encryptionSession", { httpStatus: encryptionResponse.status });
|
|
10137
10904
|
let embeddedState;
|
|
10138
10905
|
try {
|
|
10139
10906
|
embeddedState = await openfort.embeddedWallet.getEmbeddedState();
|
|
10140
10907
|
diagnostic?.trace("openfort.embeddedState", { state: embeddedState });
|
|
10908
|
+
step("embeddedState", { state: embeddedState });
|
|
10141
10909
|
} catch (cause) {
|
|
10142
10910
|
diagnostic?.trace("openfort.embeddedState", {
|
|
10143
10911
|
ok: false,
|
|
@@ -10151,6 +10919,7 @@ function createOpenfortBrowserSignerFromBootstrap(bootstrap, options = {}) {
|
|
|
10151
10919
|
try {
|
|
10152
10920
|
await configureEmbeddedWallet(encryptionBody.sessionId);
|
|
10153
10921
|
diagnostic?.trace("openfort.configure", { ok: true });
|
|
10922
|
+
step("configure");
|
|
10154
10923
|
} catch (cause) {
|
|
10155
10924
|
const error = openfortProviderError("configure", cause);
|
|
10156
10925
|
diagnostic?.trace("openfort.configure", {
|
|
@@ -10163,6 +10932,7 @@ function createOpenfortBrowserSignerFromBootstrap(bootstrap, options = {}) {
|
|
|
10163
10932
|
try {
|
|
10164
10933
|
await openfort.embeddedWallet.get();
|
|
10165
10934
|
diagnostic?.trace("openfort.get", { ok: true });
|
|
10935
|
+
step("get");
|
|
10166
10936
|
} catch (cause) {
|
|
10167
10937
|
if (!isStaleUserSignal(cause)) {
|
|
10168
10938
|
diagnostic?.trace("openfort.get", {
|
|
@@ -10176,7 +10946,13 @@ function createOpenfortBrowserSignerFromBootstrap(bootstrap, options = {}) {
|
|
|
10176
10946
|
failure_mode: "stale-openfort-cache"
|
|
10177
10947
|
});
|
|
10178
10948
|
await healStaleOpenfortCache(encryptionBody.sessionId);
|
|
10949
|
+
step("healStaleCache");
|
|
10179
10950
|
}
|
|
10951
|
+
step("walletReady", {
|
|
10952
|
+
ok: true,
|
|
10953
|
+
total_ms: Date.now() - startedAt,
|
|
10954
|
+
token_calls: tokenCalls
|
|
10955
|
+
});
|
|
10180
10956
|
})();
|
|
10181
10957
|
}
|
|
10182
10958
|
async function ensureOpenfortWalletReady() {
|
|
@@ -10259,9 +11035,13 @@ function createOpenfortBrowserSignerFromBootstrap(bootstrap, options = {}) {
|
|
|
10259
11035
|
statusStore,
|
|
10260
11036
|
ensureWalletReady: ensureOpenfortWalletReady,
|
|
10261
11037
|
getAddress: async () => {
|
|
11038
|
+
const addressStartedAt = Date.now();
|
|
10262
11039
|
try {
|
|
10263
11040
|
const address = await signer.getAddress();
|
|
10264
|
-
diagnostic?.trace("openfort.address", {
|
|
11041
|
+
diagnostic?.trace("openfort.address", {
|
|
11042
|
+
ok: true,
|
|
11043
|
+
duration_ms: Date.now() - addressStartedAt
|
|
11044
|
+
});
|
|
10265
11045
|
return address;
|
|
10266
11046
|
} catch (cause) {
|
|
10267
11047
|
diagnostic?.trace("openfort.address", {
|
|
@@ -11363,14 +12143,66 @@ function organizationWrongState(currentState, validStates) {
|
|
|
11363
12143
|
function requestCancelled(options) {
|
|
11364
12144
|
return options?.signal?.aborted === true;
|
|
11365
12145
|
}
|
|
12146
|
+
function accountInFlight(state) {
|
|
12147
|
+
return state.phase === "authenticated" && (state.account.at === "deriving" || state.account.at === "claiming");
|
|
12148
|
+
}
|
|
12149
|
+
function settledAccount(deps, options) {
|
|
12150
|
+
return new Promise((resolve) => {
|
|
12151
|
+
let done = false;
|
|
12152
|
+
let unsubscribe = null;
|
|
12153
|
+
const onAbort = () => finish({
|
|
12154
|
+
ok: false,
|
|
12155
|
+
error: Errors.cancelled({ operation: CAPXUL_OPERATIONS.onboarding.completeOrganization })
|
|
12156
|
+
});
|
|
12157
|
+
const finish = (result) => {
|
|
12158
|
+
if (done) return;
|
|
12159
|
+
done = true;
|
|
12160
|
+
unsubscribe?.();
|
|
12161
|
+
options?.signal?.removeEventListener("abort", onAbort);
|
|
12162
|
+
resolve(result);
|
|
12163
|
+
};
|
|
12164
|
+
if (options?.signal?.aborted) {
|
|
12165
|
+
onAbort();
|
|
12166
|
+
return;
|
|
12167
|
+
}
|
|
12168
|
+
options?.signal?.addEventListener("abort", onAbort, { once: true });
|
|
12169
|
+
const stop = deps.subscribeIdentity((state) => {
|
|
12170
|
+
if (!accountInFlight(state)) finish({
|
|
12171
|
+
ok: true,
|
|
12172
|
+
value: state
|
|
12173
|
+
});
|
|
12174
|
+
});
|
|
12175
|
+
if (done) {
|
|
12176
|
+
stop();
|
|
12177
|
+
return;
|
|
12178
|
+
}
|
|
12179
|
+
unsubscribe = stop;
|
|
12180
|
+
const current = deps.snapshotIdentity();
|
|
12181
|
+
if (!accountInFlight(current)) finish({
|
|
12182
|
+
ok: true,
|
|
12183
|
+
value: current
|
|
12184
|
+
});
|
|
12185
|
+
});
|
|
12186
|
+
}
|
|
11366
12187
|
async function reachClaimedAccount(deps, options) {
|
|
11367
12188
|
let state = deps.snapshotIdentity();
|
|
12189
|
+
if (accountInFlight(state)) {
|
|
12190
|
+
const settled = await settledAccount(deps, options);
|
|
12191
|
+
if (!settled.ok) return settled;
|
|
12192
|
+
state = settled.value;
|
|
12193
|
+
}
|
|
11368
12194
|
if (state.phase !== "authenticated" || state.account.at === "unknown") {
|
|
11369
12195
|
const ensured = await deps.sendIdentity({ _tag: "EnsureAccount" }, options);
|
|
11370
12196
|
if (!ensured.ok) return ensured;
|
|
11371
12197
|
state = ensured.value;
|
|
11372
12198
|
}
|
|
11373
12199
|
if (state.phase !== "authenticated") return organizationWrongState(state.phase, ["authenticated:claimed"]);
|
|
12200
|
+
if (accountInFlight(state)) {
|
|
12201
|
+
const settled = await settledAccount(deps, options);
|
|
12202
|
+
if (!settled.ok) return settled;
|
|
12203
|
+
state = settled.value;
|
|
12204
|
+
if (state.phase !== "authenticated") return organizationWrongState(state.phase, ["authenticated:claimed"]);
|
|
12205
|
+
}
|
|
11374
12206
|
if (state.account.at !== "claimed") {
|
|
11375
12207
|
const event = state.account.at === "failed" ? { _tag: "RetryAccount" } : state.account.at === "counterfactual" ? { _tag: "ClaimAccount" } : { _tag: "EnsureAccount" };
|
|
11376
12208
|
const claimed = await deps.sendIdentity(event, options);
|
|
@@ -11711,8 +12543,33 @@ function toOrgMe(wire) {
|
|
|
11711
12543
|
observedAt: wire.observedAt
|
|
11712
12544
|
};
|
|
11713
12545
|
}
|
|
12546
|
+
/**
|
|
12547
|
+
* The read runs through the canonical observed runner, so `org.me` owns one
|
|
12548
|
+
* `capxul_sdk_operation` record and one span like every other public method.
|
|
12549
|
+
*
|
|
12550
|
+
* The call-start invocation snapshot travels on the query arguments: the
|
|
12551
|
+
* Convex adapter reads it there and sends the browser linkage with the call,
|
|
12552
|
+
* so an account switch mid-flight cannot re-resolve the actor. A successful
|
|
12553
|
+
* read proves participation in this Organization, so the owning span carries
|
|
12554
|
+
* the verified Organization and the two authority booleans the backend
|
|
12555
|
+
* actually enforces. Labels and Budget caps stay off the record.
|
|
12556
|
+
*/
|
|
11714
12557
|
function makeOrgMeMethod(deps, orgId) {
|
|
11715
|
-
return (options) =>
|
|
12558
|
+
return (options) => {
|
|
12559
|
+
const controls = deps.invocationControls?.(options) ?? options;
|
|
12560
|
+
return runCapxulEffect(Effect.gen(function* () {
|
|
12561
|
+
const wire = yield* deps.convexCall.query(orgReadContract.me, copyInvocationObservation(controls, { orgId })).pipe(Effect.mapError((failure) => failure.publicError));
|
|
12562
|
+
yield* Effect.annotateCurrentSpan({
|
|
12563
|
+
verified_organization_id: orgId,
|
|
12564
|
+
scope_kind: "organization",
|
|
12565
|
+
can_manage_people: wire.capabilities.canManagePeople,
|
|
12566
|
+
can_spend: wire.capabilities.canSpend,
|
|
12567
|
+
budget_count: wire.budgets.length,
|
|
12568
|
+
observed_at: wire.observedAt
|
|
12569
|
+
}).pipe(Effect.catchCause(() => Effect.void));
|
|
12570
|
+
return toOrgMe(wire);
|
|
12571
|
+
}), controls, CAPXUL_OPERATIONS.org.me, deps.runPromise);
|
|
12572
|
+
};
|
|
11716
12573
|
}
|
|
11717
12574
|
//#endregion
|
|
11718
12575
|
//#region src/contract/permission.ts
|
|
@@ -11722,7 +12579,34 @@ const permissionContract = { read: makeFunctionReference(CAPXUL_FUNCTIONS["permi
|
|
|
11722
12579
|
function isAborted(signal) {
|
|
11723
12580
|
return signal?.aborted === true;
|
|
11724
12581
|
}
|
|
11725
|
-
|
|
12582
|
+
/**
|
|
12583
|
+
* One owning operation per public Permission method. The compound command
|
|
12584
|
+
* (prepare, equality checks, sign, submit) keeps owning its own cancellation;
|
|
12585
|
+
* the observed runner around it is what gives the attempt its
|
|
12586
|
+
* `capxul_sdk_operation` record, its span and its failure capture, so a signer
|
|
12587
|
+
* refusal or an unexpected provider error joins to the operation like every
|
|
12588
|
+
* other public surface.
|
|
12589
|
+
*/
|
|
12590
|
+
function observed(deps, operation, options, run) {
|
|
12591
|
+
const controls = deps.invocationControls?.(options) ?? options;
|
|
12592
|
+
let verifiedOrganizationId;
|
|
12593
|
+
return runCapxulEffect(Effect.gen(function* () {
|
|
12594
|
+
if (controls?.signal?.aborted === true) return yield* Effect.fail(Errors.cancelled({ operation }));
|
|
12595
|
+
const parent = yield* Effect.currentSpan.pipe(Effect.orDie);
|
|
12596
|
+
const captured = carryInvocationParentSpan(controls, parent);
|
|
12597
|
+
const result = yield* Effect.tryPromise({
|
|
12598
|
+
try: () => run(controls?.signal, captured, (orgId) => {
|
|
12599
|
+
verifiedOrganizationId = orgId;
|
|
12600
|
+
}),
|
|
12601
|
+
catch: (cause) => cause instanceof CapxulError ? cause : Errors.unknown(cause)
|
|
12602
|
+
});
|
|
12603
|
+
return yield* result.ok ? Effect.succeed(result.value) : Effect.fail(result.error);
|
|
12604
|
+
}).pipe(Effect.ensuring(Effect.suspend(() => verifiedOrganizationId === void 0 ? Effect.void : Effect.annotateCurrentSpan({
|
|
12605
|
+
verified_organization_id: verifiedOrganizationId,
|
|
12606
|
+
scope_kind: "organization"
|
|
12607
|
+
})).pipe(Effect.catchCause(() => Effect.void)))), observationOnlyControls(controls), operation, deps.runPromise);
|
|
12608
|
+
}
|
|
12609
|
+
async function execute(deps, orgId, command, signal, controls, verifiedOrganization) {
|
|
11726
12610
|
if (isAborted(signal)) return {
|
|
11727
12611
|
ok: false,
|
|
11728
12612
|
error: Errors.cancelled({ operation: "permissions" })
|
|
@@ -11746,17 +12630,18 @@ async function execute(deps, orgId, command, signal) {
|
|
|
11746
12630
|
};
|
|
11747
12631
|
}
|
|
11748
12632
|
const executionFns = deps.executionFunctions ?? moneyExecutionContract;
|
|
11749
|
-
const prepared = await runIfActive(signal, CAPXUL_OPERATIONS.permissions.prepareExecution, () => deps.convexCall.action(executionFns.preparePermissionExecution, { input: {
|
|
12633
|
+
const prepared = await runIfActive(signal, CAPXUL_OPERATIONS.permissions.prepareExecution, () => withInvocationParentSpan(Effect.suspend(() => deps.convexCall.action(executionFns.preparePermissionExecution, copyInvocationObservation(controls, { input: {
|
|
11750
12634
|
orgId,
|
|
11751
12635
|
signerAddress,
|
|
11752
12636
|
command
|
|
11753
|
-
} }));
|
|
12637
|
+
} }))), controls), deps.runPromise);
|
|
11754
12638
|
if (!prepared.ok) return prepared;
|
|
11755
12639
|
const expectedSafe = deriveCapxulSafeAddress({ email: session.email });
|
|
11756
12640
|
if (prepared.value.chainId !== deps.chainId || prepared.value.signerAddress.toLowerCase() !== signerAddress.toLowerCase() || prepared.value.userOpSenderSafe.toLowerCase() !== expectedSafe.toLowerCase() || prepared.value.operation !== command.operation || prepared.value.orgId !== orgId || canonicalJson(prepared.value.command) !== canonicalJson(command)) return {
|
|
11757
12641
|
ok: false,
|
|
11758
12642
|
error: Errors.invalidInput("permission", "prepared authority mismatch")
|
|
11759
12643
|
};
|
|
12644
|
+
verifiedOrganization(prepared.value.orgId);
|
|
11760
12645
|
if (isAborted(signal)) return {
|
|
11761
12646
|
ok: false,
|
|
11762
12647
|
error: Errors.cancelled({ operation: "permissions" })
|
|
@@ -11774,43 +12659,46 @@ async function execute(deps, orgId, command, signal) {
|
|
|
11774
12659
|
ok: false,
|
|
11775
12660
|
error: Errors.cancelled({ operation: "permissions" })
|
|
11776
12661
|
};
|
|
11777
|
-
return runIfActive(void 0, CAPXUL_OPERATIONS.permissions.submitExecution, () => deps.convexCall.action(executionFns.submitPermissionExecution, { input: {
|
|
12662
|
+
return runIfActive(void 0, CAPXUL_OPERATIONS.permissions.submitExecution, () => withInvocationParentSpan(Effect.suspend(() => deps.convexCall.action(executionFns.submitPermissionExecution, copyInvocationObservation(controls, { input: {
|
|
11778
12663
|
executionId: prepared.value.executionId,
|
|
11779
12664
|
signature
|
|
11780
|
-
} }));
|
|
12665
|
+
} }))), controls), deps.runPromise);
|
|
11781
12666
|
}
|
|
11782
12667
|
function makePermissionMethods(deps, orgId) {
|
|
11783
12668
|
const reads = deps.permissionFunctions ?? permissionContract;
|
|
12669
|
+
const ops = CAPXUL_OPERATIONS.org.permissions;
|
|
12670
|
+
const read = (signal, operation, controls) => runIfActive(signal, operation, () => withInvocationParentSpan(Effect.suspend(() => deps.convexCall.query(reads.read, copyInvocationObservation(controls, { orgId }))), controls), deps.runPromise);
|
|
12671
|
+
const command = (operation, build, options) => observed(deps, operation, options, (signal, captured, verifiedOrganization) => execute(deps, orgId, build(), signal, captured, verifiedOrganization));
|
|
11784
12672
|
return {
|
|
11785
|
-
create: (input, options) =>
|
|
12673
|
+
create: (input, options) => command(ops.create, () => ({
|
|
11786
12674
|
operation: "create",
|
|
11787
12675
|
...input
|
|
11788
|
-
}, options
|
|
11789
|
-
change: (input, options) =>
|
|
12676
|
+
}), options),
|
|
12677
|
+
change: (input, options) => command(ops.change, () => ({
|
|
11790
12678
|
operation: "change",
|
|
11791
12679
|
...input
|
|
11792
|
-
}, options
|
|
11793
|
-
assign: (input, options) =>
|
|
12680
|
+
}), options),
|
|
12681
|
+
assign: (input, options) => command(ops.assign, () => ({
|
|
11794
12682
|
operation: "assign",
|
|
11795
12683
|
...input
|
|
11796
|
-
}, options
|
|
11797
|
-
revoke: (input, options) =>
|
|
12684
|
+
}), options),
|
|
12685
|
+
revoke: (input, options) => command(ops.revoke, () => ({
|
|
11798
12686
|
operation: "revoke",
|
|
11799
12687
|
...input
|
|
11800
|
-
}, options
|
|
11801
|
-
replace: (input, options) =>
|
|
12688
|
+
}), options),
|
|
12689
|
+
replace: (input, options) => command(ops.replace, () => ({
|
|
11802
12690
|
operation: "replace",
|
|
11803
12691
|
...input
|
|
11804
|
-
}, options
|
|
11805
|
-
list: (options) =>
|
|
11806
|
-
get:
|
|
11807
|
-
const listed = await
|
|
12692
|
+
}), options),
|
|
12693
|
+
list: (options) => observed(deps, ops.list, options, (signal, captured) => read(signal, CAPXUL_OPERATIONS.permissions.list, captured)),
|
|
12694
|
+
get: (permissionId, options) => observed(deps, ops.get, options, async (signal, captured) => {
|
|
12695
|
+
const listed = await read(signal, CAPXUL_OPERATIONS.permissions.get, captured);
|
|
11808
12696
|
if (!listed.ok) return listed;
|
|
11809
12697
|
return {
|
|
11810
12698
|
ok: true,
|
|
11811
12699
|
value: listed.value.permissions.find((row) => row.permissionId === permissionId) ?? null
|
|
11812
12700
|
};
|
|
11813
|
-
}
|
|
12701
|
+
})
|
|
11814
12702
|
};
|
|
11815
12703
|
}
|
|
11816
12704
|
//#endregion
|
|
@@ -12372,7 +13260,7 @@ function hermeticOrgView(input) {
|
|
|
12372
13260
|
name: input.name,
|
|
12373
13261
|
handle: input.handle,
|
|
12374
13262
|
safeAddress: toAddress(placeholder),
|
|
12375
|
-
role:
|
|
13263
|
+
role: ADMIN_ROLE_LABEL,
|
|
12376
13264
|
treasury: zeroTreasury(orgId),
|
|
12377
13265
|
bio: null,
|
|
12378
13266
|
size: null,
|
|
@@ -12618,6 +13506,13 @@ function isRetryableOrganizationSetupFailure(error) {
|
|
|
12618
13506
|
* constructed for each `org(orgId)` call. It has no shared mutable active-Org
|
|
12619
13507
|
* state. The closed-over `orgId` is its only entity reference.
|
|
12620
13508
|
*/
|
|
13509
|
+
/** The observed-runner pair a method bundle needs to own its operation records. */
|
|
13510
|
+
function observedRunnerDeps(deps) {
|
|
13511
|
+
return {
|
|
13512
|
+
...deps.runPromise === void 0 ? {} : { runPromise: deps.runPromise },
|
|
13513
|
+
...deps.invocationControls === void 0 ? {} : { invocationControls: deps.invocationControls }
|
|
13514
|
+
};
|
|
13515
|
+
}
|
|
12621
13516
|
function makeOrgMethods(deps) {
|
|
12622
13517
|
const orgDeps = {
|
|
12623
13518
|
...deps.orgPort === void 0 ? {} : { orgPort: deps.orgPort },
|
|
@@ -12668,7 +13563,10 @@ function makeOrgMethods(deps) {
|
|
|
12668
13563
|
},
|
|
12669
13564
|
...convexCall === void 0 ? {} : { convexCall }
|
|
12670
13565
|
}),
|
|
12671
|
-
me: convexCall === void 0 ? orgMeUnavailable : makeOrgMeMethod({
|
|
13566
|
+
me: convexCall === void 0 ? orgMeUnavailable : makeOrgMeMethod({
|
|
13567
|
+
convexCall,
|
|
13568
|
+
...observedRunnerDeps(deps)
|
|
13569
|
+
}, String(orgId)),
|
|
12672
13570
|
async getLifecycle(options) {
|
|
12673
13571
|
if (options?.signal?.aborted) return {
|
|
12674
13572
|
ok: false,
|
|
@@ -12785,6 +13683,7 @@ function makeOrgMethods(deps) {
|
|
|
12785
13683
|
},
|
|
12786
13684
|
permissions: convexCall === void 0 ? makeNotImplementedPermissionMethods() : makePermissionMethods({
|
|
12787
13685
|
convexCall,
|
|
13686
|
+
...observedRunnerDeps(deps),
|
|
12788
13687
|
...deps.actor === void 0 ? {} : { actor: deps.actor },
|
|
12789
13688
|
...deps.chainId === void 0 ? {} : { chainId: Number(deps.chainId) },
|
|
12790
13689
|
...deps.signer === void 0 ? {} : { signer: deps.signer }
|
|
@@ -12904,14 +13803,65 @@ function compareDecimal(first, second) {
|
|
|
12904
13803
|
}
|
|
12905
13804
|
//#endregion
|
|
12906
13805
|
//#region src/surface/holdings.ts
|
|
13806
|
+
/** SPEC Freshness: past this block age the read is labelled stale. Nothing refuses. */
|
|
13807
|
+
const FRESHNESS_LIMIT_SECONDS = 60;
|
|
13808
|
+
/**
|
|
13809
|
+
* The Ponder balance view for one Safe as `CurrentHoldings`.
|
|
13810
|
+
*
|
|
13811
|
+
* The view is the signed sum of every indexed transfer, so the balance IS what
|
|
13812
|
+
* the chain said — there is no snapshot to go stale behind it. `observedAt` is
|
|
13813
|
+
* the indexed block's own timestamp, which is what the last-good label reads,
|
|
13814
|
+
* and `stale` is that block's age. A stopped indexer therefore shows an old
|
|
13815
|
+
* "as of" and the stale flag; it never turns the read into a refusal.
|
|
13816
|
+
*/
|
|
13817
|
+
function indexerHoldings(indexer, convexCall, overlay, actor, controls) {
|
|
13818
|
+
return Effect.gen(function* () {
|
|
13819
|
+
const scope = yield* convexCall.query(overlay, copyInvocationObservation(controls, actor?.kind === "organization" ? { input: { actor: {
|
|
13820
|
+
kind: "organization",
|
|
13821
|
+
orgId: actor.organizationId
|
|
13822
|
+
} } } : { input: {} }));
|
|
13823
|
+
if (scope.safe === null) return null;
|
|
13824
|
+
const balances = yield* indexer.read("balance", {
|
|
13825
|
+
safe: toAddress(scope.safe),
|
|
13826
|
+
limit: INDEXER_MAX_LIMIT
|
|
13827
|
+
});
|
|
13828
|
+
const status = yield* indexer.status.pipe(Effect.catch(() => Effect.succeed([])));
|
|
13829
|
+
const rows = [];
|
|
13830
|
+
for (const balance of balances) {
|
|
13831
|
+
const asset = configuredMoneyAssetById(balance.assetId);
|
|
13832
|
+
if (asset === null || balance.amount < 0n) continue;
|
|
13833
|
+
rows.push({
|
|
13834
|
+
assetKind: "erc20",
|
|
13835
|
+
tokenAddress: toAddress(asset.tokenAddress),
|
|
13836
|
+
symbol: asset.symbol,
|
|
13837
|
+
decimals: asset.decimals,
|
|
13838
|
+
rawBalance: toWeiAmount(balance.amount.toString())
|
|
13839
|
+
});
|
|
13840
|
+
}
|
|
13841
|
+
const chainId = balances[0]?.chainId ?? status[0]?.chainId;
|
|
13842
|
+
if (chainId === void 0) return null;
|
|
13843
|
+
const head = status.find((entry) => entry.chainId === chainId);
|
|
13844
|
+
const nowSeconds = Math.floor(Date.now() / 1e3);
|
|
13845
|
+
const headSeconds = head === void 0 ? void 0 : Number(head.blockTimestamp);
|
|
13846
|
+
return {
|
|
13847
|
+
actor: scope.actor,
|
|
13848
|
+
chainId: toChainId(chainId),
|
|
13849
|
+
observedAt: headSeconds === void 0 ? Date.now() : headSeconds * 1e3,
|
|
13850
|
+
stale: headSeconds === void 0 || nowSeconds - headSeconds > FRESHNESS_LIMIT_SECONDS,
|
|
13851
|
+
rows
|
|
13852
|
+
};
|
|
13853
|
+
}).pipe(Effect.catch(() => Effect.succeed(null)));
|
|
13854
|
+
}
|
|
12907
13855
|
function makeHoldingsMethods(deps) {
|
|
12908
13856
|
const functions = deps.functions ?? holdingsContract;
|
|
13857
|
+
const overlay = (deps.financialOpsFunctions ?? financialOpsContract).activityOverlay;
|
|
12909
13858
|
const read = (operation, actor, options, pick) => {
|
|
12910
13859
|
const controls = deps.invocationControls?.(options) ?? options;
|
|
12911
|
-
|
|
13860
|
+
const convexSnapshot = retryIdempotentRead(deps.convexCall.action(functions.current, copyInvocationObservation(controls, actor?.kind === "organization" ? { actor: {
|
|
12912
13861
|
kind: "organization",
|
|
12913
13862
|
orgId: actor.organizationId
|
|
12914
|
-
} } : {})), operation, deps.telemetry, controls).pipe(Effect.
|
|
13863
|
+
} } : {})), operation, deps.telemetry, controls).pipe(Effect.mapError((error) => error.publicError));
|
|
13864
|
+
return runCapxulEffect((deps.indexerRead === void 0 ? convexSnapshot : indexerHoldings(deps.indexerRead, deps.convexCall, overlay, actor, controls).pipe(Effect.flatMap((current) => current === null ? convexSnapshot : Effect.succeed(current)))).pipe(Effect.map(pick)), controls, operation, deps.runPromise);
|
|
12915
13865
|
};
|
|
12916
13866
|
return {
|
|
12917
13867
|
current: (input, options) => read(CAPXUL_OPERATIONS.holdings.current, input?.actor, options, (snapshot) => snapshot),
|
|
@@ -12937,8 +13887,8 @@ const APPLIED_ACTION_BY_EVENT = {
|
|
|
12937
13887
|
RestoreSession: "none",
|
|
12938
13888
|
ResumeOtpEntry: "none",
|
|
12939
13889
|
Reset: "none",
|
|
12940
|
-
SessionRestored: "
|
|
12941
|
-
SessionAbsent: "
|
|
13890
|
+
SessionRestored: "restoration",
|
|
13891
|
+
SessionAbsent: "restoration",
|
|
12942
13892
|
EnsureAccount: "none",
|
|
12943
13893
|
ClaimAccount: "none",
|
|
12944
13894
|
RetryAccount: "none",
|
|
@@ -12950,8 +13900,8 @@ const APPLIED_ACTION_BY_EVENT = {
|
|
|
12950
13900
|
OtpFailed: "failed",
|
|
12951
13901
|
Verified: "verified",
|
|
12952
13902
|
VerifyFailed: "failed",
|
|
12953
|
-
SessionRead: "
|
|
12954
|
-
SessionReadFailed: "
|
|
13903
|
+
SessionRead: "restoration",
|
|
13904
|
+
SessionReadFailed: "restoration",
|
|
12955
13905
|
SignedOut: "signed-out",
|
|
12956
13906
|
SignOutFailed: "failed",
|
|
12957
13907
|
AccountAt: "none",
|
|
@@ -12977,11 +13927,71 @@ const authFailure = (record, reason, mode) => ({
|
|
|
12977
13927
|
...linkage(record)
|
|
12978
13928
|
}
|
|
12979
13929
|
});
|
|
13930
|
+
/**
|
|
13931
|
+
* The label of the restore window every client boots into. A settled session
|
|
13932
|
+
* fact that left this label is the restoration result (#1962 R04); the same
|
|
13933
|
+
* facts from a signed-out or authenticated state are the routine session probe
|
|
13934
|
+
* that `getSession()` runs, and a probe is not a restoration attempt.
|
|
13935
|
+
*/
|
|
13936
|
+
const RESTORE_WINDOW = "restoring";
|
|
13937
|
+
/**
|
|
13938
|
+
* Which result the window settled on. `SessionRestored` and `SessionAbsent` are
|
|
13939
|
+
* the cache door and say it themselves. `ReadSession` is also valid inside the
|
|
13940
|
+
* window — `getSession()` drives it on a cache miss and its `SessionRead`
|
|
13941
|
+
* completion settles the window — so that one event reports both halves and the
|
|
13942
|
+
* state it produced is what separates them.
|
|
13943
|
+
*/
|
|
13944
|
+
const restorationOutcome = (event, state) => {
|
|
13945
|
+
switch (event) {
|
|
13946
|
+
case "SessionRestored": return "restored";
|
|
13947
|
+
case "SessionAbsent": return "absent";
|
|
13948
|
+
case "SessionReadFailed": return "failed";
|
|
13949
|
+
case "SessionRead":
|
|
13950
|
+
if (state.phase === "authenticated") return "restored";
|
|
13951
|
+
return state.phase === "signed_out" ? "absent" : void 0;
|
|
13952
|
+
default: return;
|
|
13953
|
+
}
|
|
13954
|
+
};
|
|
13955
|
+
/**
|
|
13956
|
+
* The restore window's one result, or nothing when this settled session fact
|
|
13957
|
+
* belongs to a probe rather than to the window.
|
|
13958
|
+
*/
|
|
13959
|
+
const restorationObservation = (record, state) => {
|
|
13960
|
+
if (record.slot !== "identity:session" || record.from !== RESTORE_WINDOW) return NONE;
|
|
13961
|
+
const outcome = restorationOutcome(record.event, state);
|
|
13962
|
+
if (outcome === void 0) return NONE;
|
|
13963
|
+
return {
|
|
13964
|
+
name: "access_recovery_completed",
|
|
13965
|
+
props: {
|
|
13966
|
+
recovery: "session_restoration",
|
|
13967
|
+
outcome,
|
|
13968
|
+
...outcome === "failed" && state.phase === "faulted" ? { reason_code: state.failure.code } : {},
|
|
13969
|
+
...linkage(record)
|
|
13970
|
+
}
|
|
13971
|
+
};
|
|
13972
|
+
};
|
|
13973
|
+
const REFUSAL_EVENTS = /* @__PURE__ */ new Set([
|
|
13974
|
+
"ClaimAccount",
|
|
13975
|
+
"RetryAccount",
|
|
13976
|
+
"CreateOrganization",
|
|
13977
|
+
"AttachOrganization",
|
|
13978
|
+
"RetryOrganization"
|
|
13979
|
+
]);
|
|
12980
13980
|
function mapIdentityProductObservation(record, state) {
|
|
12981
13981
|
if (record.machine !== "identity" || !IDENTITY_EVENT_TAG_SET.has(record.event)) return NONE;
|
|
12982
13982
|
if (record.outcome === "cancelled") return NONE;
|
|
12983
13983
|
if (record.slot === "identity:auth" && record.outcome === "refused") return authFailure(record, record.refusal_code, void 0);
|
|
12984
13984
|
if (record.slot === "identity:auth" && record.outcome === "failed") return authFailure(record, record.error_code, record.failure?.mode);
|
|
13985
|
+
if (record.outcome === "refused" && REFUSAL_EVENTS.has(record.event)) return {
|
|
13986
|
+
name: "identity_refused",
|
|
13987
|
+
props: {
|
|
13988
|
+
event: record.event,
|
|
13989
|
+
refusal_code: record.refusal_code,
|
|
13990
|
+
slot: record.slot,
|
|
13991
|
+
state: record.state,
|
|
13992
|
+
...linkage(record)
|
|
13993
|
+
}
|
|
13994
|
+
};
|
|
12985
13995
|
if (record.outcome !== "applied") return NONE;
|
|
12986
13996
|
switch (APPLIED_ACTION_BY_EVENT[record.event]) {
|
|
12987
13997
|
case "none": return NONE;
|
|
@@ -13004,6 +14014,7 @@ function mapIdentityProductObservation(record, state) {
|
|
|
13004
14014
|
name: "auth_signed_out",
|
|
13005
14015
|
props: linkage(record)
|
|
13006
14016
|
};
|
|
14017
|
+
case "restoration": return restorationObservation(record, state);
|
|
13007
14018
|
}
|
|
13008
14019
|
}
|
|
13009
14020
|
const ignoreTransportFailure = (operation, operationName, eventName) => Effect.suspend(operation).pipe(Effect.catchCause(() => Effect.logWarning("identity.product.telemetry.dropped").pipe(Effect.annotateLogs({
|
|
@@ -13015,7 +14026,7 @@ function executeIdentityProductObservation(telemetry, record, state) {
|
|
|
13015
14026
|
if (mapped === null) return Effect.void;
|
|
13016
14027
|
copyInvocationObservation(record, mapped);
|
|
13017
14028
|
const invocationTelemetry = bindProductTelemetryInvocation(telemetry, mapped);
|
|
13018
|
-
const after = () => mapped.name === "auth_verified"
|
|
14029
|
+
const after = () => state.phase === "authenticated" && (mapped.name === "auth_verified" || mapped.name === "access_recovery_completed") ? invocationTelemetry.identify({
|
|
13019
14030
|
distinctId: state.session.authUserId,
|
|
13020
14031
|
traits: { email_domain: emailDomain(state.session.email) }
|
|
13021
14032
|
}) : mapped.name === "auth_signed_out" ? invocationTelemetry.reset() : Effect.void;
|
|
@@ -13060,6 +14071,51 @@ function startSignerReadinessOnClaim(actor, signer) {
|
|
|
13060
14071
|
browserSigner.ensureWalletReady?.().catch(() => {});
|
|
13061
14072
|
});
|
|
13062
14073
|
}
|
|
14074
|
+
/**
|
|
14075
|
+
* Report the result of each signer readiness cycle (#1962 R04).
|
|
14076
|
+
*
|
|
14077
|
+
* The readiness store already carries the cycle boundary this needs, so no
|
|
14078
|
+
* public signer hook, second identity subscriber, or render counter is added.
|
|
14079
|
+
* `ensureOpenfortWalletReady` writes `recovering` synchronously when it starts
|
|
14080
|
+
* a cycle, and only the cycle that is still the current one writes `ready` or
|
|
14081
|
+
* `unavailable` (`create-openfort-browser-signer.ts:501-521`). Callers that
|
|
14082
|
+
* join a live cycle share that single write, and a `resetSession` writes
|
|
14083
|
+
* `unknown` first, so a cycle that a reset or an account replacement outran
|
|
14084
|
+
* reports nothing.
|
|
14085
|
+
*
|
|
14086
|
+
* The host context is bound when the cycle starts, so a person who signs out
|
|
14087
|
+
* and back in while a cycle runs cannot relabel the result that began under
|
|
14088
|
+
* the previous person. A signer with no readiness cycle — a node or dev key,
|
|
14089
|
+
* and a headless client whose requirement never opens the account lane —
|
|
14090
|
+
* writes no status and reports nothing.
|
|
14091
|
+
*/
|
|
14092
|
+
function observeSignerReadinessCycles(signer, telemetry, hostObservationSnapshot, runPromise) {
|
|
14093
|
+
const statusStore = signer?.statusStore;
|
|
14094
|
+
if (statusStore === void 0 || telemetry === void 0) return () => {};
|
|
14095
|
+
let running = false;
|
|
14096
|
+
let startContext;
|
|
14097
|
+
return statusStore.subscribe((status) => {
|
|
14098
|
+
if (status === "recovering") {
|
|
14099
|
+
running = true;
|
|
14100
|
+
startContext = hostObservationSnapshot?.();
|
|
14101
|
+
return;
|
|
14102
|
+
}
|
|
14103
|
+
const cycleContext = startContext;
|
|
14104
|
+
const completesCycle = running;
|
|
14105
|
+
running = false;
|
|
14106
|
+
startContext = void 0;
|
|
14107
|
+
if (!completesCycle || status !== "ready" && status !== "unavailable") return;
|
|
14108
|
+
const event = {
|
|
14109
|
+
name: "access_recovery_completed",
|
|
14110
|
+
props: {
|
|
14111
|
+
recovery: "signer_readiness",
|
|
14112
|
+
outcome: status
|
|
14113
|
+
}
|
|
14114
|
+
};
|
|
14115
|
+
if (cycleContext !== void 0) attachInvocationObservation(event, cycleContext);
|
|
14116
|
+
runPromise(Effect.suspend(() => bindProductTelemetryInvocation(telemetry, event).emit(event)).pipe(Effect.catchCause(() => Effect.void))).catch(() => {});
|
|
14117
|
+
});
|
|
14118
|
+
}
|
|
13063
14119
|
function assembleCapxulClient(input) {
|
|
13064
14120
|
const observation = input;
|
|
13065
14121
|
const authCache = input.authCache ?? detectAuthCacheAdapter();
|
|
@@ -13089,12 +14145,14 @@ function assembleCapxulClient(input) {
|
|
|
13089
14145
|
}, failureObservation, normalizeCapxulOperation(`identity.${record.event}`), "machine");
|
|
13090
14146
|
});
|
|
13091
14147
|
const unsubscribeSignerReadiness = startSignerReadinessOnClaim(actor, input.signer);
|
|
14148
|
+
const unsubscribeReadinessObservation = observeSignerReadinessCycles(input.signer, input.ports.telemetry, observation.hostObservationSnapshot, effectRunner.runPromise);
|
|
13092
14149
|
let stopPromise = null;
|
|
13093
14150
|
const stopActor = () => {
|
|
13094
14151
|
if (stopPromise === null) {
|
|
13095
14152
|
unsubscribeProductTelemetry();
|
|
13096
14153
|
unsubscribeFailureObservation();
|
|
13097
14154
|
unsubscribeSignerReadiness();
|
|
14155
|
+
unsubscribeReadinessObservation();
|
|
13098
14156
|
stopPromise = effectRunner.runPromise(Scope.close(scope, Exit.void));
|
|
13099
14157
|
}
|
|
13100
14158
|
return stopPromise;
|
|
@@ -13111,7 +14169,8 @@ function assembleCapxulClient(input) {
|
|
|
13111
14169
|
convexCall: input.ports.convexCall,
|
|
13112
14170
|
...input.signer === void 0 ? {} : { signer: input.signer },
|
|
13113
14171
|
telemetry: input.ports.telemetry,
|
|
13114
|
-
requestKeyScope: `${input.bootstrap.convexUrl}:${String(input.bootstrap.chainId)}
|
|
14172
|
+
requestKeyScope: `${input.bootstrap.convexUrl}:${String(input.bootstrap.chainId)}`,
|
|
14173
|
+
...input.ports.indexerRead === void 0 ? {} : { indexerRead: input.ports.indexerRead }
|
|
13115
14174
|
});
|
|
13116
14175
|
const accountBundle = makeAccountMethods({
|
|
13117
14176
|
actor,
|
|
@@ -13140,10 +14199,13 @@ function assembleCapxulClient(input) {
|
|
|
13140
14199
|
...input.invokeTimeoutMs === void 0 ? {} : { invokeTimeoutMs: input.invokeTimeoutMs },
|
|
13141
14200
|
afterVerifyOtp: async () => {
|
|
13142
14201
|
const browserSigner = input.signer;
|
|
13143
|
-
if (browserSigner !== void 0 && "resetSession" in browserSigner && typeof browserSigner.resetSession === "function")
|
|
13144
|
-
|
|
13145
|
-
|
|
13146
|
-
|
|
14202
|
+
if (browserSigner !== void 0 && "resetSession" in browserSigner && typeof browserSigner.resetSession === "function") {
|
|
14203
|
+
try {
|
|
14204
|
+
browserSigner.resetSession();
|
|
14205
|
+
} catch (cause) {
|
|
14206
|
+
throw signerFailure(browserSigner.source, "resetSession", cause);
|
|
14207
|
+
}
|
|
14208
|
+
if (browserSigner.statusStore?.status() === "unknown" && typeof browserSigner.ensureWalletReady === "function") browserSigner.ensureWalletReady().catch(() => {});
|
|
13147
14209
|
}
|
|
13148
14210
|
await detectPendingOrgInvitations?.();
|
|
13149
14211
|
kickProvisioning?.();
|
|
@@ -13170,6 +14232,7 @@ function assembleCapxulClient(input) {
|
|
|
13170
14232
|
ensureAccount: account._internal.ensureReady,
|
|
13171
14233
|
getLifecycle: account.getLifecycle,
|
|
13172
14234
|
snapshotIdentity: actor.snapshot,
|
|
14235
|
+
subscribeIdentity: actor.subscribe,
|
|
13173
14236
|
sendIdentity: (event, options) => runCapxulEffect(askIdentity(actor, event, options), options, CAPXUL_OPERATIONS._internal.identity.send, effectRunner.runPromise)
|
|
13174
14237
|
});
|
|
13175
14238
|
const system = makeSystemMethods({ convexCall: input.ports.convexCall });
|
|
@@ -13178,7 +14241,8 @@ function assembleCapxulClient(input) {
|
|
|
13178
14241
|
convexCall: input.ports.convexCall,
|
|
13179
14242
|
telemetry: input.ports.telemetry,
|
|
13180
14243
|
runPromise: effectRunner.runPromise,
|
|
13181
|
-
invocationControls: (controls) => withHostObservationControls(observation.hostObservationSnapshot, controls, actor.authSession()?.authUserId)
|
|
14244
|
+
invocationControls: (controls) => withHostObservationControls(observation.hostObservationSnapshot, controls, actor.authSession()?.authUserId),
|
|
14245
|
+
...input.ports.indexerRead === void 0 ? {} : { indexerRead: input.ports.indexerRead }
|
|
13182
14246
|
});
|
|
13183
14247
|
const accounts = makeAccountsMethods({
|
|
13184
14248
|
accountReadPort: input.ports.accountRead,
|
|
@@ -13303,6 +14367,7 @@ function assembleCapxulClient(input) {
|
|
|
13303
14367
|
identity: identityRuntime,
|
|
13304
14368
|
bootstrap: input.bootstrap,
|
|
13305
14369
|
accounts: { fund: accounts.fund },
|
|
14370
|
+
...input.ports.indexerRead === void 0 ? {} : { indexer: input.ports.indexerRead },
|
|
13306
14371
|
organizationSetup: { getProofReceipt: (orgId) => runPortEffect(input.ports.convexCall.query(organizationSetupProofReceiptQuery, { orgId })) },
|
|
13307
14372
|
telemetry: input.ports.telemetry,
|
|
13308
14373
|
captureExternalAddressAcknowledged: (address, organizationId) => {
|
|
@@ -13361,8 +14426,10 @@ function withHostObservationControls(snapshot, input, actorId) {
|
|
|
13361
14426
|
const previous = readInvocationObservation(input);
|
|
13362
14427
|
if (previous !== void 0) return attachInvocationObservation({ ...input }, previous);
|
|
13363
14428
|
const captured = snapshot?.() ?? { active: true };
|
|
14429
|
+
const context = captured.active ? withCapturedAttemptContext(captured.context, input) : captured.context;
|
|
13364
14430
|
return attachInvocationObservation({ ...input }, {
|
|
13365
14431
|
...captured,
|
|
14432
|
+
...context === void 0 ? {} : { context },
|
|
13366
14433
|
...actorId === void 0 ? {} : { actorId }
|
|
13367
14434
|
});
|
|
13368
14435
|
}
|
|
@@ -13502,6 +14569,352 @@ function snapshotHostObservability(observability) {
|
|
|
13502
14569
|
function isPromiseLike(value) {
|
|
13503
14570
|
return (typeof value === "object" && value !== null || typeof value === "function") && "then" in value;
|
|
13504
14571
|
}
|
|
14572
|
+
//#endregion
|
|
14573
|
+
//#region src/adapters/ponder/decode.ts
|
|
14574
|
+
/**
|
|
14575
|
+
* Decode the indexer's rows at the trust boundary.
|
|
14576
|
+
*
|
|
14577
|
+
* Ponder returns the raw PostgreSQL result: `integer` arrives as a number,
|
|
14578
|
+
* `numeric(78)` and `text` arrive as strings. Amounts, block numbers and
|
|
14579
|
+
* timestamps are `numeric(78)`, so they become `bigint` here and never pass
|
|
14580
|
+
* through `Number`. Every decoder throws a `CapxulError`; the adapter turns
|
|
14581
|
+
* that into a typed `IndexerReadError`.
|
|
14582
|
+
*/
|
|
14583
|
+
const HEX = /^0x[0-9a-fA-F]+$/;
|
|
14584
|
+
function field(row, key) {
|
|
14585
|
+
if (!(key in row)) throw Errors.invalidInput(key, "missing from the indexer row");
|
|
14586
|
+
return row[key];
|
|
14587
|
+
}
|
|
14588
|
+
function text(row, key) {
|
|
14589
|
+
const value = field(row, key);
|
|
14590
|
+
if (typeof value !== "string") throw Errors.invalidInput(key, "must be a string");
|
|
14591
|
+
return value;
|
|
14592
|
+
}
|
|
14593
|
+
function hex(row, key) {
|
|
14594
|
+
const value = text(row, key);
|
|
14595
|
+
if (!HEX.test(value)) throw Errors.invalidInput(key, "must be a hex string");
|
|
14596
|
+
return value.toLowerCase();
|
|
14597
|
+
}
|
|
14598
|
+
function integer(row, key) {
|
|
14599
|
+
const value = field(row, key);
|
|
14600
|
+
const parsed = typeof value === "string" ? Number(value) : value;
|
|
14601
|
+
if (typeof parsed !== "number" || !Number.isSafeInteger(parsed)) throw Errors.invalidInput(key, "must be an integer");
|
|
14602
|
+
return parsed;
|
|
14603
|
+
}
|
|
14604
|
+
/** `numeric(78)` crosses the wire as a decimal string; keep every digit. */
|
|
14605
|
+
function big(row, key) {
|
|
14606
|
+
const value = field(row, key);
|
|
14607
|
+
if (typeof value === "bigint") return value;
|
|
14608
|
+
const raw = typeof value === "number" ? String(value) : value;
|
|
14609
|
+
if (typeof raw !== "string" || !/^-?[0-9]+$/.test(raw)) throw Errors.invalidInput(key, "must be an exact integer amount");
|
|
14610
|
+
return BigInt(raw);
|
|
14611
|
+
}
|
|
14612
|
+
function asRow(value) {
|
|
14613
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) throw Errors.invalidInput("row", "must be an object");
|
|
14614
|
+
return value;
|
|
14615
|
+
}
|
|
14616
|
+
function identity(row) {
|
|
14617
|
+
return {
|
|
14618
|
+
id: text(row, "id"),
|
|
14619
|
+
chainId: toChainId(integer(row, "chain_id")),
|
|
14620
|
+
txHash: toTxHash(hex(row, "tx_hash")),
|
|
14621
|
+
transactionLogOrdinal: integer(row, "transaction_log_ordinal"),
|
|
14622
|
+
blockNumber: big(row, "block_number"),
|
|
14623
|
+
blockHash: hex(row, "block_hash"),
|
|
14624
|
+
timestamp: big(row, "timestamp")
|
|
14625
|
+
};
|
|
14626
|
+
}
|
|
14627
|
+
function decodeTransfer(value) {
|
|
14628
|
+
const row = asRow(value);
|
|
14629
|
+
return {
|
|
14630
|
+
...identity(row),
|
|
14631
|
+
assetId: toAssetId(text(row, "asset_id")),
|
|
14632
|
+
token: toAddress(hex(row, "token")),
|
|
14633
|
+
from: toAddress(hex(row, "from")),
|
|
14634
|
+
to: toAddress(hex(row, "to")),
|
|
14635
|
+
amount: big(row, "amount")
|
|
14636
|
+
};
|
|
14637
|
+
}
|
|
14638
|
+
function decodePayment(value) {
|
|
14639
|
+
const row = asRow(value);
|
|
14640
|
+
return {
|
|
14641
|
+
...identity(row),
|
|
14642
|
+
contract: toAddress(hex(row, "contract")),
|
|
14643
|
+
settlementId: hex(row, "settlement_id"),
|
|
14644
|
+
sender: toAddress(hex(row, "sender")),
|
|
14645
|
+
recipient: toAddress(hex(row, "recipient")),
|
|
14646
|
+
assetId: toAssetId(text(row, "asset_id")),
|
|
14647
|
+
token: toAddress(hex(row, "token")),
|
|
14648
|
+
amount: big(row, "amount"),
|
|
14649
|
+
kind: integer(row, "kind"),
|
|
14650
|
+
documentHash: hex(row, "document_hash")
|
|
14651
|
+
};
|
|
14652
|
+
}
|
|
14653
|
+
/** The `balance` view exposes the signed sum under the underlying column name `to`. */
|
|
14654
|
+
function decodeBalance(value) {
|
|
14655
|
+
const row = asRow(value);
|
|
14656
|
+
return {
|
|
14657
|
+
chainId: toChainId(integer(row, "chain_id")),
|
|
14658
|
+
assetId: toAssetId(text(row, "asset_id")),
|
|
14659
|
+
address: toAddress(hex(row, "to")),
|
|
14660
|
+
amount: big(row, "amount")
|
|
14661
|
+
};
|
|
14662
|
+
}
|
|
14663
|
+
const DECODERS = {
|
|
14664
|
+
transfer: decodeTransfer,
|
|
14665
|
+
payment: decodePayment,
|
|
14666
|
+
balance: decodeBalance
|
|
14667
|
+
};
|
|
14668
|
+
/** Decode one `/sql/db` or `/sql/live` payload. Ponder returns `{ rows: [...] }`. */
|
|
14669
|
+
function decodeRows(table, body) {
|
|
14670
|
+
const rows = asRow(body).rows;
|
|
14671
|
+
if (!Array.isArray(rows)) throw Errors.invalidInput("rows", "must be an array");
|
|
14672
|
+
const decode = DECODERS[table];
|
|
14673
|
+
return rows.map(decode);
|
|
14674
|
+
}
|
|
14675
|
+
/** Decode Ponder's native `/status`: `{ [chainName]: { id, block: { number, timestamp } } }`. */
|
|
14676
|
+
function decodeStatus(body) {
|
|
14677
|
+
const chains = asRow(body);
|
|
14678
|
+
return Object.entries(chains).map(([chain, value]) => {
|
|
14679
|
+
const entry = asRow(value);
|
|
14680
|
+
const block = asRow(field(entry, "block"));
|
|
14681
|
+
return {
|
|
14682
|
+
chain,
|
|
14683
|
+
chainId: toChainId(integer(entry, "id")),
|
|
14684
|
+
blockNumber: big(block, "number"),
|
|
14685
|
+
blockTimestamp: big(block, "timestamp")
|
|
14686
|
+
};
|
|
14687
|
+
});
|
|
14688
|
+
}
|
|
14689
|
+
//#endregion
|
|
14690
|
+
//#region src/adapters/ponder/statements.ts
|
|
14691
|
+
/**
|
|
14692
|
+
* The exact statements the indexer accepts.
|
|
14693
|
+
*
|
|
14694
|
+
* `apps/indexer/src/queries.ts` compiles its allowlist with `@ponder/client`
|
|
14695
|
+
* and compares `sql`, `params` and `typings` byte for byte against the caller's
|
|
14696
|
+
* statement. Anything else gets 403. The SDK therefore reproduces the compiled
|
|
14697
|
+
* text here rather than pulling Ponder and Drizzle into a published browser
|
|
14698
|
+
* bundle to regenerate three fixed statements.
|
|
14699
|
+
*
|
|
14700
|
+
* `statements.test.ts` pins the text. When the indexer schema or a query in
|
|
14701
|
+
* that allowlist changes, this file changes with it or every read fails 403.
|
|
14702
|
+
*/
|
|
14703
|
+
const HEADS = {
|
|
14704
|
+
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",
|
|
14705
|
+
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",
|
|
14706
|
+
balance: "select \"chain_id\", \"asset_id\", \"to\", \"amount\" from \"balance\" where \"balance\".\"to\" = $1"
|
|
14707
|
+
};
|
|
14708
|
+
/** How many times the statement binds the Safe address. */
|
|
14709
|
+
const ACTOR_PARAMS = {
|
|
14710
|
+
transfer: 2,
|
|
14711
|
+
payment: 2,
|
|
14712
|
+
balance: 1
|
|
14713
|
+
};
|
|
14714
|
+
/**
|
|
14715
|
+
* Build the compiled query for one table and page. Throws a `CapxulError` for
|
|
14716
|
+
* a page or address the indexer would refuse, so the caller never spends a
|
|
14717
|
+
* round trip to learn it built the request wrong.
|
|
14718
|
+
*/
|
|
14719
|
+
function ponderQuery(table, input) {
|
|
14720
|
+
const safe = assertIndexerPage(input);
|
|
14721
|
+
const offset = input.offset ?? 0;
|
|
14722
|
+
const actors = ACTOR_PARAMS[table];
|
|
14723
|
+
const params = [];
|
|
14724
|
+
for (let i = 0; i < actors; i++) params.push(safe);
|
|
14725
|
+
params.push(input.limit);
|
|
14726
|
+
let sql = `${HEADS[table]} limit $${actors + 1}`;
|
|
14727
|
+
if (offset > 0) {
|
|
14728
|
+
params.push(offset);
|
|
14729
|
+
sql = `${sql} offset $${actors + 2}`;
|
|
14730
|
+
}
|
|
14731
|
+
return {
|
|
14732
|
+
sql,
|
|
14733
|
+
params,
|
|
14734
|
+
typings: params.map(() => "none")
|
|
14735
|
+
};
|
|
14736
|
+
}
|
|
14737
|
+
/**
|
|
14738
|
+
* The `?sql=` value. `@ponder/client` sends `superjson.stringify(query)`; for a
|
|
14739
|
+
* payload of strings and numbers superjson emits no `meta`, so the wire form is
|
|
14740
|
+
* exactly this envelope.
|
|
14741
|
+
*/
|
|
14742
|
+
function ponderQueryParam(query) {
|
|
14743
|
+
return JSON.stringify({ json: query });
|
|
14744
|
+
}
|
|
14745
|
+
//#endregion
|
|
14746
|
+
//#region src/adapters/ponder/sse.ts
|
|
14747
|
+
/**
|
|
14748
|
+
* The smallest SSE reader that can carry an `Authorization` header.
|
|
14749
|
+
*
|
|
14750
|
+
* `@ponder/client` opens its live query with `EventSource`, which accepts no
|
|
14751
|
+
* headers, so it cannot reach the guarded indexer at all. The indexer's CORS
|
|
14752
|
+
* policy allows exactly one request header (`Authorization`), so this reader
|
|
14753
|
+
* sends nothing else. Ponder writes one `data:` line per frame.
|
|
14754
|
+
*/
|
|
14755
|
+
async function readEventStream(body, onData, stopped) {
|
|
14756
|
+
const reader = body.getReader();
|
|
14757
|
+
const decoder = new TextDecoder();
|
|
14758
|
+
let buffer = "";
|
|
14759
|
+
try {
|
|
14760
|
+
for (;;) {
|
|
14761
|
+
const { done, value } = await reader.read();
|
|
14762
|
+
if (done || stopped()) return;
|
|
14763
|
+
buffer += decoder.decode(value, { stream: true });
|
|
14764
|
+
let split = buffer.indexOf("\n\n");
|
|
14765
|
+
while (split !== -1) {
|
|
14766
|
+
const frame = buffer.slice(0, split);
|
|
14767
|
+
buffer = buffer.slice(split + 2);
|
|
14768
|
+
const data = frameData(frame);
|
|
14769
|
+
if (data !== void 0) onData(data);
|
|
14770
|
+
if (stopped()) return;
|
|
14771
|
+
split = buffer.indexOf("\n\n");
|
|
14772
|
+
}
|
|
14773
|
+
}
|
|
14774
|
+
} finally {
|
|
14775
|
+
await reader.cancel().catch(() => void 0);
|
|
14776
|
+
}
|
|
14777
|
+
}
|
|
14778
|
+
function frameData(frame) {
|
|
14779
|
+
const lines = [];
|
|
14780
|
+
for (const line of frame.split("\n")) {
|
|
14781
|
+
if (!line.startsWith("data:")) continue;
|
|
14782
|
+
lines.push(line.slice(line.startsWith("data: ") ? 6 : 5));
|
|
14783
|
+
}
|
|
14784
|
+
return lines.length === 0 ? void 0 : lines.join("\n");
|
|
14785
|
+
}
|
|
14786
|
+
//#endregion
|
|
14787
|
+
//#region src/adapters/ponder/PonderIndexerAdapter.ts
|
|
14788
|
+
var PonderIndexerAdapter = class {
|
|
14789
|
+
#baseUrl;
|
|
14790
|
+
#token;
|
|
14791
|
+
#fetch;
|
|
14792
|
+
#observation;
|
|
14793
|
+
constructor(deps) {
|
|
14794
|
+
this.#baseUrl = deps.baseUrl.replace(/\/$/, "");
|
|
14795
|
+
this.#token = deps.token;
|
|
14796
|
+
this.#observation = deps.observation;
|
|
14797
|
+
this.#fetch = deps.fetch ?? ((input, init) => globalThis.fetch(input, init));
|
|
14798
|
+
}
|
|
14799
|
+
read(table, input) {
|
|
14800
|
+
const operation = `indexer.read.${table}`;
|
|
14801
|
+
return Effect.tryPromise({
|
|
14802
|
+
try: async () => {
|
|
14803
|
+
return decodeRows(table, await (await this.#request("db", table, input, operation)).json());
|
|
14804
|
+
},
|
|
14805
|
+
catch: (cause) => toIndexerReadError(operation, cause)
|
|
14806
|
+
});
|
|
14807
|
+
}
|
|
14808
|
+
subscribe(table, input, callback) {
|
|
14809
|
+
const operation = CAPXUL_OPERATIONS.activity.subscribe;
|
|
14810
|
+
return Effect.sync(() => {
|
|
14811
|
+
let stopped = false;
|
|
14812
|
+
const unsubscribe = () => {
|
|
14813
|
+
stopped = true;
|
|
14814
|
+
};
|
|
14815
|
+
callback({ status: "loading" });
|
|
14816
|
+
this.#stream(table, input, operation, callback, () => stopped).catch(() => void 0);
|
|
14817
|
+
return unsubscribe;
|
|
14818
|
+
});
|
|
14819
|
+
}
|
|
14820
|
+
status = Effect.tryPromise({
|
|
14821
|
+
try: async () => {
|
|
14822
|
+
const response = await this.#fetch(`${this.#baseUrl}/status`, { method: "GET" });
|
|
14823
|
+
if (!response.ok) throw httpError("indexer.status", response.status, await safeText(response));
|
|
14824
|
+
return decodeStatus(await response.json());
|
|
14825
|
+
},
|
|
14826
|
+
catch: (cause) => toIndexerReadError("indexer.status", cause)
|
|
14827
|
+
});
|
|
14828
|
+
/** One authenticated `/sql/:method` call. Throws a `CapxulError` on refusal. */
|
|
14829
|
+
async #request(method, table, input, operation) {
|
|
14830
|
+
const query = ponderQuery(table, input);
|
|
14831
|
+
const token = await this.#token({ forceRefreshToken: false });
|
|
14832
|
+
if (token === null || token === "") throw Errors.notAuthenticated();
|
|
14833
|
+
const url = `${this.#baseUrl}/sql/${method}?sql=${encodeURIComponent(ponderQueryParam(query))}`;
|
|
14834
|
+
const headers = { authorization: `Bearer ${token}` };
|
|
14835
|
+
if (method === "live") headers.accept = "text/event-stream";
|
|
14836
|
+
let response;
|
|
14837
|
+
try {
|
|
14838
|
+
response = await this.#fetch(url, {
|
|
14839
|
+
method: "GET",
|
|
14840
|
+
headers
|
|
14841
|
+
});
|
|
14842
|
+
} catch (cause) {
|
|
14843
|
+
throw Errors.networkError(operation, cause, {
|
|
14844
|
+
provider: "indexer",
|
|
14845
|
+
failure_mode: "upstream-down"
|
|
14846
|
+
});
|
|
14847
|
+
}
|
|
14848
|
+
if (!response.ok) throw httpError(operation, response.status, await safeText(response));
|
|
14849
|
+
return response;
|
|
14850
|
+
}
|
|
14851
|
+
/**
|
|
14852
|
+
* Follow the live stream until the caller unsubscribes or the indexer closes
|
|
14853
|
+
* it. The indexer closes the stream on token expiry or revoked authority
|
|
14854
|
+
* with no frame, so any end the caller did not ask for is a teardown: it
|
|
14855
|
+
* reaches `captureException` under `activity.subscribe` and the caller sees
|
|
14856
|
+
* one `error` snapshot.
|
|
14857
|
+
*/
|
|
14858
|
+
async #stream(table, input, operation, callback, stopped) {
|
|
14859
|
+
let failure;
|
|
14860
|
+
try {
|
|
14861
|
+
const response = await this.#request("live", table, input, operation);
|
|
14862
|
+
if (response.body === null) throw Errors.providerError("indexer", operation, "empty stream");
|
|
14863
|
+
await readEventStream(response.body, (data) => {
|
|
14864
|
+
try {
|
|
14865
|
+
callback({
|
|
14866
|
+
status: "ok",
|
|
14867
|
+
value: decodeRows(table, JSON.parse(data))
|
|
14868
|
+
});
|
|
14869
|
+
} catch (cause) {
|
|
14870
|
+
failure = toPublicError$1(operation, cause);
|
|
14871
|
+
}
|
|
14872
|
+
}, () => stopped() || failure !== void 0);
|
|
14873
|
+
} catch (cause) {
|
|
14874
|
+
failure = toPublicError$1(operation, cause);
|
|
14875
|
+
}
|
|
14876
|
+
if (stopped()) return;
|
|
14877
|
+
const error = failure ?? Errors.capabilityUnavailable("indexer", operation);
|
|
14878
|
+
observeStreamFailure(this.#observation, CAPXUL_OPERATIONS.activity.subscribe, error, error.code === "NOT_AUTHENTICATED" ? "operation" : "exception");
|
|
14879
|
+
callback({
|
|
14880
|
+
status: "error",
|
|
14881
|
+
error
|
|
14882
|
+
});
|
|
14883
|
+
}
|
|
14884
|
+
};
|
|
14885
|
+
/**
|
|
14886
|
+
* Map an indexer refusal into the shared error vocabulary.
|
|
14887
|
+
*
|
|
14888
|
+
* 401 is the expected outcome of an expired or revoked session. 403 is not:
|
|
14889
|
+
* the indexer accepts only its own compiled statements for a Safe in the
|
|
14890
|
+
* claim, so a refusal means this SDK and that deployment disagree, or the
|
|
14891
|
+
* session gained a Safe and still holds the older token. Both must be loud.
|
|
14892
|
+
*/
|
|
14893
|
+
function httpError(operation, status, body) {
|
|
14894
|
+
if (status === 401) return Errors.notAuthenticated();
|
|
14895
|
+
if (status === 429) return Errors.rateLimited({ resource: "indexer" });
|
|
14896
|
+
if (status === 503) return Errors.capabilityUnavailable("indexer", operation);
|
|
14897
|
+
return Errors.providerError("indexer", operation, body, {
|
|
14898
|
+
httpStatus: status,
|
|
14899
|
+
...status >= 500 ? { failure_mode: "upstream-down" } : {},
|
|
14900
|
+
details: { reason: status === 403 ? "scope-refused" : "http-status" }
|
|
14901
|
+
});
|
|
14902
|
+
}
|
|
14903
|
+
function toPublicError$1(operation, cause) {
|
|
14904
|
+
if (cause instanceof CapxulError) return cause;
|
|
14905
|
+
return Errors.providerError("indexer", operation, cause);
|
|
14906
|
+
}
|
|
14907
|
+
function toIndexerReadError(operation, cause) {
|
|
14908
|
+
if (cause instanceof IndexerReadError) return cause;
|
|
14909
|
+
return indexerReadErrorFromCapxul(operation, toPublicError$1(operation, cause), cause);
|
|
14910
|
+
}
|
|
14911
|
+
async function safeText(response) {
|
|
14912
|
+
try {
|
|
14913
|
+
return await response.text();
|
|
14914
|
+
} catch {
|
|
14915
|
+
return "";
|
|
14916
|
+
}
|
|
14917
|
+
}
|
|
13505
14918
|
const SDK_VERSION = version;
|
|
13506
14919
|
/**
|
|
13507
14920
|
* Project the built graph into the flat `FlowPorts` record the method bundles
|
|
@@ -13751,7 +15164,16 @@ async function createProductionAdapters(input) {
|
|
|
13751
15164
|
}));
|
|
13752
15165
|
const applicationLayer = Layer.merge(portsLayer, optionalEngineeringTelemetryLayer(bootstrapResult.value.engineeringTelemetry, resolvedInput.value.runtime));
|
|
13753
15166
|
const context = await Effect.runPromise(Layer.buildWithScope(applicationLayer, scope));
|
|
13754
|
-
const
|
|
15167
|
+
const collected = await Effect.runPromise(collectProductionFlowPorts.pipe(Effect.provide(context)));
|
|
15168
|
+
const indexerUrl = input.indexerUrl ?? bootstrapResult.value.indexerUrl;
|
|
15169
|
+
const indexerRead = productionIndexerReadPort(indexerUrl === void 0 ? input : {
|
|
15170
|
+
...input,
|
|
15171
|
+
indexerUrl
|
|
15172
|
+
}, collected, observation);
|
|
15173
|
+
const ports = indexerRead === void 0 ? collected : {
|
|
15174
|
+
...collected,
|
|
15175
|
+
indexerRead
|
|
15176
|
+
};
|
|
13755
15177
|
const stopConnectionObservation = observeProductionConvexConnection(ports.convexCall, context);
|
|
13756
15178
|
const close = idempotentClose(async () => {
|
|
13757
15179
|
try {
|
|
@@ -13878,7 +15300,7 @@ async function createCapxulClientWithSignerControls(input, controls) {
|
|
|
13878
15300
|
...adapters.value.bootstrap,
|
|
13879
15301
|
authBaseUrl: resolvedAuthBaseUrl
|
|
13880
15302
|
}, {
|
|
13881
|
-
diagnostic: new ConsoleDiagnosticAdapter(),
|
|
15303
|
+
diagnostic: new EffectLogDiagnosticAdapter(adapters.value.context, new ConsoleDiagnosticAdapter()),
|
|
13882
15304
|
signUserOpHash: controls.signEmbeddedUserOpHash
|
|
13883
15305
|
});
|
|
13884
15306
|
signer = withSignerControl(signer, controls.signUserOpHash);
|
|
@@ -14083,5 +15505,28 @@ function idempotentClose(close) {
|
|
|
14083
15505
|
await close();
|
|
14084
15506
|
};
|
|
14085
15507
|
}
|
|
15508
|
+
/**
|
|
15509
|
+
* Build the Ponder read port for this client, or `undefined` when no indexer
|
|
15510
|
+
* origin was supplied. The token provider is the same
|
|
15511
|
+
* `AuthClientPort.getConvexJwt` closure `ConvexCallPort` uses, so the indexer
|
|
15512
|
+
* and Convex see one session with one scope.
|
|
15513
|
+
*/
|
|
15514
|
+
function productionIndexerReadPort(input, ports, observation) {
|
|
15515
|
+
if (input.activityReadSource === "convex") return void 0;
|
|
15516
|
+
if (input.indexerUrl === void 0 || input.indexerUrl === "") return void 0;
|
|
15517
|
+
return new PonderIndexerAdapter({
|
|
15518
|
+
baseUrl: normalizeHttpUrl("indexerUrl", input.indexerUrl),
|
|
15519
|
+
token: async ({ forceRefreshToken }) => {
|
|
15520
|
+
const token = await Effect.runPromise(Effect.result(ports.authClient.getConvexJwt({
|
|
15521
|
+
forceRefresh: forceRefreshToken,
|
|
15522
|
+
...input.signal === void 0 ? {} : { signal: input.signal }
|
|
15523
|
+
})));
|
|
15524
|
+
if (Result.isFailure(token)) return null;
|
|
15525
|
+
return String(token.success.token);
|
|
15526
|
+
},
|
|
15527
|
+
...input.fetch === void 0 ? {} : { fetch: input.fetch },
|
|
15528
|
+
...observation === void 0 ? {} : { observation }
|
|
15529
|
+
});
|
|
15530
|
+
}
|
|
14086
15531
|
//#endregion
|
|
14087
15532
|
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 };
|