@capxul/sdk 4.2.0-rc.1 → 4.2.0-rc.10
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-IB2W1Fzj.mjs} +110 -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-BndbwuVx.mjs} +929 -42
- package/dist/{production-CBKHzqYP.d.mts → production-Du4Vc7YD.d.mts} +152 -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 redactSecrets, D as WEI_RE, E as SUPPORTED_CURRENCY_CODES, F as toAssetId, G as toHandle, H as toEmail, I as toAuthUserId, J as toOrgId, K as toJwtToken, L as toBudgetId, M as toAddress, N as toAllowedOrigin, O as ZERO_BYTES32, Q as toPublishableKey, R as toChainId, S as APP_ID_RE, St as isCredentialField, T as EVM_ADDRESS_RE$1, U as toEpochMs, V as toDurationMs, W as toEpochSeconds, X as toPayrollGroupId, Y as toPartyId, Z as toPayrollRunId, _ as normalizeBindingEmail, _t as failureFingerprint, at as decodeConvexError, bt as revertSummaryText, c as AuthCachePortTag, ct as EXPECTED_OPERATION_OUTCOMES, d as authClientPortFromPromiseAdapter, dt as CHAIN_UPSTREAMS, et as toSessionToken, f as AuthClientError, ft as FAILURE_MODES$1, gt as decodeChainCause, h as orgRoleKeyForLabel, ht as chainEvidenceLabel, i as BrowserAuthCacheAdapter, j as toAccountId, l as SystemClockLayer, lt as Errors, m as ADMIN_ROLE_LABEL, mt as chainCauseProperties, nt as toTxHash, ot as CAPXUL_ERROR_CODES, p as AuthClientPortTag, pt as boundedResponseHeaders, q as toKycTier, r as InMemoryAuthCacheAdapter, rt as validateHandle, st as CapxulError, tt as toTesterKind, u as ClockPortTag, ut as isCapxulError, v as BASE_SEPOLIA_CHAIN_ID, vt as isChainUpstream, w as BYTES32_RE, wt as redactUrlSecrets, x as ACCOUNT_ID_RE, xt as containsSensitiveMaterial, y as deriveCapxulSafeAddress, yt as isFailureMode, z as toCountryCode } from "./OAuthBearerAuthClient-IB2W1Fzj.mjs";
|
|
2
2
|
import { formatUnits, keccak256, parseUnits, recoverAddress, stringToHex, toBytes } from "viem";
|
|
3
3
|
import { Cause, Clock, Context, Data, Deferred, Duration, Effect, Exit, Fiber, FiberSet, Layer, Option, Queue, Ref, Result, Schedule, Schema, SchemaGetter, SchemaIssue, SchemaParser, Scope, Stream, Tracer } from "effect";
|
|
4
4
|
import { getFunctionName, makeFunctionReference } from "convex/server";
|
|
@@ -448,6 +448,8 @@ const CAPXUL_OPERATIONS = {
|
|
|
448
448
|
emitAnnotationSaved: "activity.emitAnnotationSaved",
|
|
449
449
|
get: "activity.get",
|
|
450
450
|
list: "activity.list",
|
|
451
|
+
/** The SDK's live Activity read over the indexer's SSE stream. */
|
|
452
|
+
subscribe: "activity.subscribe",
|
|
451
453
|
summary: "activity.summary"
|
|
452
454
|
},
|
|
453
455
|
addressBook: {
|
|
@@ -946,6 +948,17 @@ const AuthFailedProps = Schema.Struct({
|
|
|
946
948
|
auth_type: OptionalString,
|
|
947
949
|
reason: OptionalString
|
|
948
950
|
});
|
|
951
|
+
const IdentityRefusedProps = Schema.Struct({
|
|
952
|
+
...TelemetryEnvelopeProps,
|
|
953
|
+
/** The refused identity event tag, for example CreateOrganization. */
|
|
954
|
+
event: OptionalString,
|
|
955
|
+
/** The machine's refusal code: WRONG_STATE, SUPERSEDED or STALE_EPOCH. */
|
|
956
|
+
refusal_code: OptionalString,
|
|
957
|
+
/** The machine slot the request targeted, for example identity:org. */
|
|
958
|
+
slot: OptionalString,
|
|
959
|
+
/** The machine state at refusal, for example authenticated:claiming. */
|
|
960
|
+
state: OptionalString
|
|
961
|
+
});
|
|
949
962
|
const AuthSignedOutProps = Schema.Struct(TelemetryEnvelopeProps);
|
|
950
963
|
const ProvisioningSafeCreatedProps = Schema.Struct({
|
|
951
964
|
...TelemetryEnvelopeProps,
|
|
@@ -1333,10 +1346,24 @@ 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
|
|
1340
1367
|
});
|
|
1341
1368
|
const MovementScanWindowProps = Schema.Struct({
|
|
1342
1369
|
...TelemetryEnvelopeProps,
|
|
@@ -1382,6 +1409,10 @@ Schema.Struct({
|
|
|
1382
1409
|
name: Schema.Literal("auth_failed"),
|
|
1383
1410
|
props: AuthFailedProps
|
|
1384
1411
|
});
|
|
1412
|
+
Schema.Struct({
|
|
1413
|
+
name: Schema.Literal("identity_refused"),
|
|
1414
|
+
props: IdentityRefusedProps
|
|
1415
|
+
});
|
|
1385
1416
|
Schema.Struct({
|
|
1386
1417
|
name: Schema.Literal("auth_signed_out"),
|
|
1387
1418
|
props: AuthSignedOutProps
|
|
@@ -1562,6 +1593,10 @@ Schema.Struct({
|
|
|
1562
1593
|
name: Schema.Literal("movement_scan_incident"),
|
|
1563
1594
|
props: MovementScanIncidentProps
|
|
1564
1595
|
});
|
|
1596
|
+
/** The one spelling of "whose scope did this happen in" across every surface. */
|
|
1597
|
+
const ScopeKindSchema = Schema.Literals(["account", "organization"]);
|
|
1598
|
+
const AccountKindSchema = Schema.Literals(["personalSafe", "orgTreasury"]);
|
|
1599
|
+
const OptionalReasonCode = Schema.optional(Schema.Literals(CAPXUL_ERROR_CODES));
|
|
1565
1600
|
const ActivityReferenceProps = {
|
|
1566
1601
|
reference_kind: Schema.Literals(["payment", "movement"]),
|
|
1567
1602
|
reference_id: Schema.String
|
|
@@ -1592,8 +1627,8 @@ const ActivityFilterProps = {
|
|
|
1592
1627
|
};
|
|
1593
1628
|
const ActivityActionProps = {
|
|
1594
1629
|
...TelemetryEnvelopeProps,
|
|
1595
|
-
scope_kind:
|
|
1596
|
-
reason_code:
|
|
1630
|
+
scope_kind: ScopeKindSchema,
|
|
1631
|
+
reason_code: OptionalReasonCode
|
|
1597
1632
|
};
|
|
1598
1633
|
Schema.Struct({
|
|
1599
1634
|
name: Schema.Literal("contact_relationship_changed"),
|
|
@@ -1605,7 +1640,7 @@ Schema.Struct({
|
|
|
1605
1640
|
"hidden",
|
|
1606
1641
|
"restored"
|
|
1607
1642
|
]),
|
|
1608
|
-
scope_kind:
|
|
1643
|
+
scope_kind: ScopeKindSchema,
|
|
1609
1644
|
scope_id: Schema.String,
|
|
1610
1645
|
owner_party_id: Schema.String,
|
|
1611
1646
|
party_id: Schema.String,
|
|
@@ -1621,9 +1656,9 @@ Schema.Struct({
|
|
|
1621
1656
|
});
|
|
1622
1657
|
const ContactActionProps = {
|
|
1623
1658
|
...TelemetryEnvelopeProps,
|
|
1624
|
-
scope_kind:
|
|
1659
|
+
scope_kind: ScopeKindSchema,
|
|
1625
1660
|
party_id: OptionalString,
|
|
1626
|
-
reason_code:
|
|
1661
|
+
reason_code: OptionalReasonCode
|
|
1627
1662
|
};
|
|
1628
1663
|
Schema.Struct({
|
|
1629
1664
|
name: Schema.Literal("ui_contact_action_completed"),
|
|
@@ -1650,7 +1685,7 @@ Schema.Struct({
|
|
|
1650
1685
|
props: Schema.Struct({
|
|
1651
1686
|
...TelemetryEnvelopeProps,
|
|
1652
1687
|
...ActivityReferenceProps,
|
|
1653
|
-
scope_kind:
|
|
1688
|
+
scope_kind: ScopeKindSchema,
|
|
1654
1689
|
scope_id: Schema.String,
|
|
1655
1690
|
occurred_at: Schema.Number
|
|
1656
1691
|
})
|
|
@@ -1705,6 +1740,193 @@ Schema.Struct({
|
|
|
1705
1740
|
})
|
|
1706
1741
|
])
|
|
1707
1742
|
});
|
|
1743
|
+
/**
|
|
1744
|
+
* Reliability closeout (#1960 R02) — the shape every explicit UI observation
|
|
1745
|
+
* below shares. `scope_kind` says whose surface the person was looking at,
|
|
1746
|
+
* `organization_id` is the verified Organization the producer resolved (absent
|
|
1747
|
+
* on a personal action), and `reason_code` is a catalog error code, never copy.
|
|
1748
|
+
*/
|
|
1749
|
+
const UiStepProps = {
|
|
1750
|
+
...TelemetryEnvelopeProps,
|
|
1751
|
+
scope_kind: ScopeKindSchema,
|
|
1752
|
+
organization_id: OptionalOrgId,
|
|
1753
|
+
reason_code: OptionalReasonCode
|
|
1754
|
+
};
|
|
1755
|
+
const AccessRecoveryCompletedProps = Schema.Union([Schema.Struct({
|
|
1756
|
+
...TelemetryEnvelopeProps,
|
|
1757
|
+
recovery: Schema.Literal("session_restoration"),
|
|
1758
|
+
outcome: Schema.Literals([
|
|
1759
|
+
"restored",
|
|
1760
|
+
"absent",
|
|
1761
|
+
"failed"
|
|
1762
|
+
]),
|
|
1763
|
+
reason_code: OptionalReasonCode,
|
|
1764
|
+
organization_id: OptionalOrgId
|
|
1765
|
+
}), Schema.Struct({
|
|
1766
|
+
...TelemetryEnvelopeProps,
|
|
1767
|
+
recovery: Schema.Literal("signer_readiness"),
|
|
1768
|
+
outcome: Schema.Literals(["ready", "unavailable"]),
|
|
1769
|
+
reason_code: OptionalReasonCode,
|
|
1770
|
+
organization_id: OptionalOrgId
|
|
1771
|
+
})]);
|
|
1772
|
+
Schema.Struct({
|
|
1773
|
+
name: Schema.Literal("access_recovery_completed"),
|
|
1774
|
+
props: AccessRecoveryCompletedProps
|
|
1775
|
+
});
|
|
1776
|
+
Schema.Struct({
|
|
1777
|
+
name: Schema.Literal("ui_access_step_completed"),
|
|
1778
|
+
props: Schema.Struct({
|
|
1779
|
+
...UiStepProps,
|
|
1780
|
+
step: Schema.Literals([
|
|
1781
|
+
"dashboard_granted",
|
|
1782
|
+
"redirect_completed",
|
|
1783
|
+
"gate_error_displayed",
|
|
1784
|
+
"retry_selected",
|
|
1785
|
+
"capability_refusal_displayed",
|
|
1786
|
+
"signer_refusal_displayed"
|
|
1787
|
+
]),
|
|
1788
|
+
capability: Schema.optional(Schema.Literals(["spend", "managePeople"])),
|
|
1789
|
+
probe: Schema.optional(Schema.Literals([
|
|
1790
|
+
"session",
|
|
1791
|
+
"membership",
|
|
1792
|
+
"lifecycle"
|
|
1793
|
+
])),
|
|
1794
|
+
reason: Schema.optional(Schema.Literals([
|
|
1795
|
+
"loading",
|
|
1796
|
+
"no-permission",
|
|
1797
|
+
"org-unavailable"
|
|
1798
|
+
]))
|
|
1799
|
+
})
|
|
1800
|
+
});
|
|
1801
|
+
Schema.Struct({
|
|
1802
|
+
name: Schema.Literal("org_invite_progressed"),
|
|
1803
|
+
props: Schema.Struct({
|
|
1804
|
+
...TelemetryEnvelopeProps,
|
|
1805
|
+
organization_id: OrgIdTelemetrySchema,
|
|
1806
|
+
progress: Schema.Literals([
|
|
1807
|
+
"account_ready",
|
|
1808
|
+
"permission_applied",
|
|
1809
|
+
"membership_activated",
|
|
1810
|
+
"revoked"
|
|
1811
|
+
]),
|
|
1812
|
+
/** The invited Party. The initiating actor stays on the envelope's identity. */
|
|
1813
|
+
member_party_id: Schema.String,
|
|
1814
|
+
occurred_at: Schema.Number
|
|
1815
|
+
})
|
|
1816
|
+
});
|
|
1817
|
+
Schema.Struct({
|
|
1818
|
+
name: Schema.Literal("invoice_issued"),
|
|
1819
|
+
props: Schema.Struct({
|
|
1820
|
+
...TelemetryEnvelopeProps,
|
|
1821
|
+
request_id: Schema.String,
|
|
1822
|
+
issuer_scope_kind: ScopeKindSchema,
|
|
1823
|
+
issuer_scope_id: Schema.String,
|
|
1824
|
+
payer_party_id: OptionalString,
|
|
1825
|
+
occurred_at: Schema.Number
|
|
1826
|
+
})
|
|
1827
|
+
});
|
|
1828
|
+
Schema.Struct({
|
|
1829
|
+
name: Schema.Literal("invoice_request_transitioned"),
|
|
1830
|
+
props: Schema.Struct({
|
|
1831
|
+
...TelemetryEnvelopeProps,
|
|
1832
|
+
request_id: Schema.String,
|
|
1833
|
+
transition: Schema.Literals([
|
|
1834
|
+
"approval_prepared",
|
|
1835
|
+
"declined",
|
|
1836
|
+
"cancelled"
|
|
1837
|
+
]),
|
|
1838
|
+
actor_scope_kind: ScopeKindSchema,
|
|
1839
|
+
actor_scope_id: Schema.String,
|
|
1840
|
+
occurred_at: Schema.Number
|
|
1841
|
+
})
|
|
1842
|
+
});
|
|
1843
|
+
Schema.Struct({
|
|
1844
|
+
name: Schema.Literal("ui_invoice_step_completed"),
|
|
1845
|
+
props: Schema.Struct({
|
|
1846
|
+
...UiStepProps,
|
|
1847
|
+
step: Schema.Literals([
|
|
1848
|
+
"opened",
|
|
1849
|
+
"previewed",
|
|
1850
|
+
"dismissed",
|
|
1851
|
+
"submitted",
|
|
1852
|
+
"result_displayed",
|
|
1853
|
+
"refusal_displayed",
|
|
1854
|
+
"retry_selected"
|
|
1855
|
+
]),
|
|
1856
|
+
request_id: OptionalString
|
|
1857
|
+
})
|
|
1858
|
+
});
|
|
1859
|
+
Schema.Struct({
|
|
1860
|
+
name: Schema.Literal("ui_treasury_step_completed"),
|
|
1861
|
+
props: Schema.Struct({
|
|
1862
|
+
...UiStepProps,
|
|
1863
|
+
step: Schema.Literals([
|
|
1864
|
+
"balance_displayed",
|
|
1865
|
+
"read_refusal_displayed",
|
|
1866
|
+
"retry_selected",
|
|
1867
|
+
"recovery_completed"
|
|
1868
|
+
]),
|
|
1869
|
+
account_kind: Schema.optional(AccountKindSchema)
|
|
1870
|
+
})
|
|
1871
|
+
});
|
|
1872
|
+
Schema.Struct({
|
|
1873
|
+
name: Schema.Literal("ui_receive_step_completed"),
|
|
1874
|
+
props: Schema.Struct({
|
|
1875
|
+
...UiStepProps,
|
|
1876
|
+
step: Schema.Literals([
|
|
1877
|
+
"instructions_displayed",
|
|
1878
|
+
"address_copied",
|
|
1879
|
+
"copy_failed",
|
|
1880
|
+
"refusal_displayed",
|
|
1881
|
+
"retry_selected"
|
|
1882
|
+
]),
|
|
1883
|
+
account_kind: Schema.optional(AccountKindSchema)
|
|
1884
|
+
})
|
|
1885
|
+
});
|
|
1886
|
+
Schema.Struct({
|
|
1887
|
+
name: Schema.Literal("payroll_group_changed"),
|
|
1888
|
+
props: Schema.Struct({
|
|
1889
|
+
...TelemetryEnvelopeProps,
|
|
1890
|
+
organization_id: OrgIdTelemetrySchema,
|
|
1891
|
+
change: Schema.Literals([
|
|
1892
|
+
"created",
|
|
1893
|
+
"updated",
|
|
1894
|
+
"deleted"
|
|
1895
|
+
]),
|
|
1896
|
+
group_id: Schema.String,
|
|
1897
|
+
member_count: Schema.Number,
|
|
1898
|
+
occurred_at: Schema.Number
|
|
1899
|
+
})
|
|
1900
|
+
});
|
|
1901
|
+
Schema.Struct({
|
|
1902
|
+
name: Schema.Literal("payroll_run_transitioned"),
|
|
1903
|
+
props: Schema.Struct({
|
|
1904
|
+
...TelemetryEnvelopeProps,
|
|
1905
|
+
organization_id: OrgIdTelemetrySchema,
|
|
1906
|
+
transition: Schema.Literals([
|
|
1907
|
+
"prepared",
|
|
1908
|
+
"authorized",
|
|
1909
|
+
"settled"
|
|
1910
|
+
]),
|
|
1911
|
+
run_id: Schema.String,
|
|
1912
|
+
payment_count: Schema.optional(Schema.Number),
|
|
1913
|
+
occurred_at: Schema.Number
|
|
1914
|
+
})
|
|
1915
|
+
});
|
|
1916
|
+
Schema.Struct({
|
|
1917
|
+
name: Schema.Literal("ui_payroll_action_completed"),
|
|
1918
|
+
props: Schema.Struct({
|
|
1919
|
+
...UiStepProps,
|
|
1920
|
+
action: Schema.Literals([
|
|
1921
|
+
"opened",
|
|
1922
|
+
"refusal_displayed",
|
|
1923
|
+
"recovery_completed",
|
|
1924
|
+
"cancelled",
|
|
1925
|
+
"submitted_displayed"
|
|
1926
|
+
]),
|
|
1927
|
+
run_id: OptionalString
|
|
1928
|
+
})
|
|
1929
|
+
});
|
|
1708
1930
|
function redactTelemetryEvent(event, options = {}) {
|
|
1709
1931
|
const props = redactTelemetryProps(event.name, event.props, options);
|
|
1710
1932
|
return props === void 0 ? { name: event.name } : {
|
|
@@ -2007,6 +2229,10 @@ const BootstrapEnvelope = Schema.Struct({
|
|
|
2007
2229
|
siteBaseUrl: Schema.String,
|
|
2008
2230
|
openfortPublishableKey: Schema.String,
|
|
2009
2231
|
shieldPublishableKey: Schema.String,
|
|
2232
|
+
/** Which Openfort third-party auth provider the signer presents; absent means better-auth. */
|
|
2233
|
+
openfortAuthProvider: Schema.optional(Schema.Literals(["better-auth", "oidc"])),
|
|
2234
|
+
/** Origin of the Ponder indexer the SDK reads directly; absent means no indexer reads. */
|
|
2235
|
+
indexerUrl: Schema.optional(Schema.String),
|
|
2010
2236
|
engineeringTelemetry: Schema.optional(EngineeringTelemetryBootstrapPolicy)
|
|
2011
2237
|
})
|
|
2012
2238
|
});
|
|
@@ -2081,6 +2307,7 @@ const CAPXUL_FUNCTIONS = {
|
|
|
2081
2307
|
prepareOrganizationPaymentExecution: "moneyExecution/paymentCommandActions:prepareOrganizationPaymentExecution",
|
|
2082
2308
|
submitPaymentCommandExecution: "moneyExecution/paymentCommandActions:submitPaymentCommandExecution"
|
|
2083
2309
|
},
|
|
2310
|
+
"moneyExecution/providerSendWitness": { read: "moneyExecution/providerSendWitness:read" },
|
|
2084
2311
|
media: {
|
|
2085
2312
|
generateUploadUrl: "media:generateUploadUrl",
|
|
2086
2313
|
setOrgLogo: "media:setOrgLogo",
|
|
@@ -2140,6 +2367,11 @@ const CAPXUL_FUNCTIONS = {
|
|
|
2140
2367
|
loadByAuthUserId: "smartAccount/queries:loadByAuthUserId",
|
|
2141
2368
|
loadBySmartAccountAddress: "smartAccount/queries:loadBySmartAccountAddress"
|
|
2142
2369
|
},
|
|
2370
|
+
e2eControls: {
|
|
2371
|
+
holdUserOperationReceipt: "e2eControls:holdUserOperationReceipt",
|
|
2372
|
+
isUserOperationReceiptHeld: "e2eControls:isUserOperationReceiptHeld",
|
|
2373
|
+
releaseUserOperationReceipt: "e2eControls:releaseUserOperationReceipt"
|
|
2374
|
+
},
|
|
2143
2375
|
system: { health: "system:health" }
|
|
2144
2376
|
};
|
|
2145
2377
|
//#endregion
|
|
@@ -5451,7 +5683,7 @@ const moneyExecutionContract = {
|
|
|
5451
5683
|
};
|
|
5452
5684
|
//#endregion
|
|
5453
5685
|
//#region package.json
|
|
5454
|
-
var version = "4.2.0-rc.
|
|
5686
|
+
var version = "4.2.0-rc.10";
|
|
5455
5687
|
//#endregion
|
|
5456
5688
|
//#region src/telemetry/exception-projection.ts
|
|
5457
5689
|
/** Fixed fallback for failures that have no safe message. */
|
|
@@ -5644,6 +5876,17 @@ function observeFailedResult(result, adapter, operation, origin) {
|
|
|
5644
5876
|
return result;
|
|
5645
5877
|
}
|
|
5646
5878
|
/**
|
|
5879
|
+
* @internal Reports a failure that can never become a method result.
|
|
5880
|
+
*
|
|
5881
|
+
* A reactive stream is torn down after its call already returned, so nothing
|
|
5882
|
+
* downstream observes it. `captureException` is the right kind: the caller did
|
|
5883
|
+
* not ask for the end, so it is not an expected product outcome.
|
|
5884
|
+
*/
|
|
5885
|
+
function observeStreamFailure(adapter, operation, cause) {
|
|
5886
|
+
if (adapter === void 0) return;
|
|
5887
|
+
report(adapter, "exception", operation, cause, resolveAdapterSnapshot(adapter));
|
|
5888
|
+
}
|
|
5889
|
+
/**
|
|
5647
5890
|
* One failure, one event. The identity machine reports a failed transition
|
|
5648
5891
|
* first; when the same `CapxulError` then surfaces as a public method result
|
|
5649
5892
|
* (directly or down its `cause` chain), the boundary drops that second report.
|
|
@@ -5721,6 +5964,34 @@ function ignoreDeliveryFailure(delivery) {
|
|
|
5721
5964
|
} catch {}
|
|
5722
5965
|
}
|
|
5723
5966
|
/**
|
|
5967
|
+
* Fold a caller-captured attempt identity into the host's resolved context.
|
|
5968
|
+
*
|
|
5969
|
+
* The host's `resolveContext` runs at the moment an SDK method is entered, so a
|
|
5970
|
+
* host that mints a correlation per resolve gives every operation of one user
|
|
5971
|
+
* attempt a different identity — and a user or Organization switch between the
|
|
5972
|
+
* click and the reply relabels an action that already started. When the caller
|
|
5973
|
+
* captured `correlation_id`/`journey_id` BEFORE the action started, that is the
|
|
5974
|
+
* attempt, and it replaces what the resolver returned for this one call. The
|
|
5975
|
+
* result is what the SDK span/Log, the failure envelope and the backend carrier
|
|
5976
|
+
* all read, so the three agree by construction rather than by convention.
|
|
5977
|
+
*
|
|
5978
|
+
* A caller that captured nothing gets the resolver's context unchanged, so
|
|
5979
|
+
* automatic reads keep their own invocation context.
|
|
5980
|
+
*/
|
|
5981
|
+
function withCapturedAttemptContext(context, controls) {
|
|
5982
|
+
const correlationId = controls?.correlation_id;
|
|
5983
|
+
const journeyId = controls?.journey_id;
|
|
5984
|
+
if (correlationId === void 0 && journeyId === void 0) return context;
|
|
5985
|
+
const captured = sanitizeObservationContext({
|
|
5986
|
+
...correlationId === void 0 ? {} : { correlationId },
|
|
5987
|
+
...journeyId === void 0 ? {} : { journeyId }
|
|
5988
|
+
});
|
|
5989
|
+
return captured === void 0 ? context : {
|
|
5990
|
+
...context,
|
|
5991
|
+
...captured
|
|
5992
|
+
};
|
|
5993
|
+
}
|
|
5994
|
+
/**
|
|
5724
5995
|
* Map an ALREADY-sanitized observation context to the snake_case PostHog
|
|
5725
5996
|
* property keys. Shared by the failure boundary here and the host
|
|
5726
5997
|
* success-telemetry seam (`telemetry/from-posthog.ts`) so both attach identical
|
|
@@ -7852,7 +8123,8 @@ var BetterAuthBrowserAdapter = class {
|
|
|
7852
8123
|
try {
|
|
7853
8124
|
const res = await this.fetchImpl(this.url("/api/auth/get-session"), withSignal$1({
|
|
7854
8125
|
method: "GET",
|
|
7855
|
-
credentials: "include"
|
|
8126
|
+
credentials: "include",
|
|
8127
|
+
cache: "no-store"
|
|
7856
8128
|
}, options?.signal));
|
|
7857
8129
|
if (options?.signal?.aborted) return {
|
|
7858
8130
|
ok: false,
|
|
@@ -7924,7 +8196,8 @@ var BetterAuthBrowserAdapter = class {
|
|
|
7924
8196
|
try {
|
|
7925
8197
|
const res = await this.fetchImpl(this.url("/api/auth/convex/token"), withSignal$1({
|
|
7926
8198
|
method: "GET",
|
|
7927
|
-
credentials: "include"
|
|
8199
|
+
credentials: "include",
|
|
8200
|
+
cache: "no-store"
|
|
7928
8201
|
}, options?.signal));
|
|
7929
8202
|
if (options?.signal?.aborted) return {
|
|
7930
8203
|
ok: false,
|
|
@@ -8437,7 +8710,7 @@ function isTransientBootstrapFailure(error) {
|
|
|
8437
8710
|
const status = error.details?.httpStatus;
|
|
8438
8711
|
return typeof status === "number" && isTransientHttpStatus(status);
|
|
8439
8712
|
}
|
|
8440
|
-
async function safeText(res) {
|
|
8713
|
+
async function safeText$1(res) {
|
|
8441
8714
|
try {
|
|
8442
8715
|
return await res.text();
|
|
8443
8716
|
} catch {
|
|
@@ -8510,6 +8783,8 @@ var HttpBootstrapAdapter = class {
|
|
|
8510
8783
|
siteBaseUrl: normalizeRuntimeUrl("siteBaseUrl", decodedState.siteBaseUrl),
|
|
8511
8784
|
openfortPublishableKey: decodedState.openfortPublishableKey,
|
|
8512
8785
|
shieldPublishableKey: decodedState.shieldPublishableKey,
|
|
8786
|
+
...decodedState.openfortAuthProvider === void 0 ? {} : { openfortAuthProvider: decodedState.openfortAuthProvider },
|
|
8787
|
+
...decodedState.indexerUrl === void 0 ? {} : { indexerUrl: decodedState.indexerUrl },
|
|
8513
8788
|
...decodedEngineeringTelemetry !== void 0 && Result.isSuccess(decodedEngineeringTelemetry) ? { engineeringTelemetry: decodedEngineeringTelemetry.success } : {}
|
|
8514
8789
|
};
|
|
8515
8790
|
},
|
|
@@ -8518,7 +8793,7 @@ var HttpBootstrapAdapter = class {
|
|
|
8518
8793
|
return bootstrapErrorFromCapxul("malformedBody", Errors.providerError("convex", "bootstrap", cause instanceof Error ? cause : new Error(String(cause))));
|
|
8519
8794
|
}
|
|
8520
8795
|
});
|
|
8521
|
-
return Effect.promise(() => safeText(res)).pipe(Effect.flatMap((body) => {
|
|
8796
|
+
return Effect.promise(() => safeText$1(res)).pipe(Effect.flatMap((body) => {
|
|
8522
8797
|
if (res.status === 401 || body.startsWith("NOT_AUTHENTICATED")) return Effect.fail(bootstrapErrorFromCapxul("notAuthenticated", Errors.notAuthenticated()));
|
|
8523
8798
|
if (res.status === 400 || body.startsWith("INVALID_INPUT")) return Effect.fail(bootstrapErrorFromCapxul("invalidInput", Errors.invalidInput("publishableKey", "rejected by bootstrap")));
|
|
8524
8799
|
const responseBody = body.slice(0, 300);
|
|
@@ -8748,6 +9023,12 @@ function makeConnectionId() {
|
|
|
8748
9023
|
}
|
|
8749
9024
|
//#endregion
|
|
8750
9025
|
//#region src/adapters/convex-call/ConvexCallAdapter.ts
|
|
9026
|
+
/**
|
|
9027
|
+
* A socket that has not emitted `close` within this window is not going to.
|
|
9028
|
+
* ponytail: one fixed bound; `closeTimeoutMs` is the knob if a slow runtime
|
|
9029
|
+
* ever needs a different one.
|
|
9030
|
+
*/
|
|
9031
|
+
const DEFAULT_CLOSE_TIMEOUT_MS = 5e3;
|
|
8751
9032
|
/** Exact floor-first allowlist; every additional handler must migrate its validator first. */
|
|
8752
9033
|
const OBSERVED_CONVEX_ACTIONS = /* @__PURE__ */ new Set([
|
|
8753
9034
|
"payroll/actions:authorizeRun",
|
|
@@ -8816,7 +9097,9 @@ var ConvexCallAdapter = class {
|
|
|
8816
9097
|
#applicationId;
|
|
8817
9098
|
#observation;
|
|
8818
9099
|
#connectionMonitor;
|
|
9100
|
+
#closeTimeoutMs;
|
|
8819
9101
|
constructor(deps) {
|
|
9102
|
+
this.#closeTimeoutMs = deps.closeTimeoutMs ?? DEFAULT_CLOSE_TIMEOUT_MS;
|
|
8820
9103
|
this.#connectionMonitor = new ConvexConnectionMonitor();
|
|
8821
9104
|
const webSocketConstructor = this.#connectionMonitor.observedWebSocketConstructor();
|
|
8822
9105
|
this.#client = deps.client ?? new ConvexClient(deps.convexUrl, webSocketConstructor === void 0 ? {} : { webSocketConstructor });
|
|
@@ -8915,11 +9198,33 @@ var ConvexCallAdapter = class {
|
|
|
8915
9198
|
callback({ status: "loading" });
|
|
8916
9199
|
})));
|
|
8917
9200
|
}
|
|
9201
|
+
/**
|
|
9202
|
+
* Teardown always completes. `ConvexClient.close()` resolves only when the
|
|
9203
|
+
* socket emits `close`, and `WebSocketManager.close()` in the `connecting`
|
|
9204
|
+
* state arms `ws.onopen = () => ws.close()` without ever closing the socket
|
|
9205
|
+
* itself — so a handshake that stalls right after a server-initiated close
|
|
9206
|
+
* (code 1012, which is what a Convex deploy sends) leaves that promise
|
|
9207
|
+
* pending for as long as the page lives. Convex marks the client closed and
|
|
9208
|
+
* terminates the socket manager synchronously before returning it, so
|
|
9209
|
+
* abandoning it drops nothing. A rejection is dropped for the same reason —
|
|
9210
|
+
* the layer release turns it into a defect that fails the whole scope close,
|
|
9211
|
+
* and teardown has no caller who can act on it.
|
|
9212
|
+
*/
|
|
8918
9213
|
async close() {
|
|
8919
9214
|
this.#connectionMonitor.close();
|
|
8920
|
-
await this.#client.close();
|
|
9215
|
+
await settledWithin(this.#client.close(), this.#closeTimeoutMs);
|
|
8921
9216
|
}
|
|
8922
9217
|
};
|
|
9218
|
+
function settledWithin(promise, timeoutMs) {
|
|
9219
|
+
return new Promise((resolve) => {
|
|
9220
|
+
const timer = setTimeout(resolve, timeoutMs);
|
|
9221
|
+
const finish = () => {
|
|
9222
|
+
clearTimeout(timer);
|
|
9223
|
+
resolve();
|
|
9224
|
+
};
|
|
9225
|
+
promise.then(finish, finish);
|
|
9226
|
+
});
|
|
9227
|
+
}
|
|
8923
9228
|
function ConvexCallLayer(deps) {
|
|
8924
9229
|
return Layer.effect(ConvexCallPortTag, Effect.acquireRelease(Effect.sync(() => new ConvexCallAdapter(deps)), (adapter) => Effect.promise(() => adapter.close()).pipe(Effect.orDie)));
|
|
8925
9230
|
}
|
|
@@ -9872,6 +10177,32 @@ var ConsoleDiagnosticAdapter = class {
|
|
|
9872
10177
|
}
|
|
9873
10178
|
};
|
|
9874
10179
|
//#endregion
|
|
10180
|
+
//#region src/adapters/diagnostic/EffectLogDiagnosticAdapter.ts
|
|
10181
|
+
/**
|
|
10182
|
+
* Diagnostic breadcrumbs as SDK log records. The signer's readiness cycle
|
|
10183
|
+
* traces (`openfort.*`) used to reach only the browser console; through this
|
|
10184
|
+
* adapter they run in the client's own Effect context, so the engineering
|
|
10185
|
+
* telemetry layer ships them to PostHog Logs beside `identity.transition`.
|
|
10186
|
+
* Logging never throws back into the cycle that emitted it.
|
|
10187
|
+
*/
|
|
10188
|
+
var EffectLogDiagnosticAdapter = class {
|
|
10189
|
+
#runPromise;
|
|
10190
|
+
#console;
|
|
10191
|
+
constructor(context, consoleFallback) {
|
|
10192
|
+
this.#runPromise = Effect.runPromiseWith(context);
|
|
10193
|
+
this.#console = consoleFallback;
|
|
10194
|
+
}
|
|
10195
|
+
trace(scope, detail) {
|
|
10196
|
+
this.#console?.trace(scope, detail);
|
|
10197
|
+
try {
|
|
10198
|
+
this.#runPromise(Effect.logInfo(scope).pipe(Effect.annotateLogs({
|
|
10199
|
+
...detail,
|
|
10200
|
+
diagnostic_scope: scope
|
|
10201
|
+
}), Effect.catchCause(() => Effect.void))).catch(() => void 0);
|
|
10202
|
+
} catch {}
|
|
10203
|
+
}
|
|
10204
|
+
};
|
|
10205
|
+
//#endregion
|
|
9875
10206
|
//#region src/openfort/create-openfort-browser-signer.ts
|
|
9876
10207
|
function httpStatusOf(link) {
|
|
9877
10208
|
const carrier = link;
|
|
@@ -10003,15 +10334,35 @@ function createOpenfortBrowserSignerFromBootstrap(bootstrap, options = {}) {
|
|
|
10003
10334
|
});
|
|
10004
10335
|
throw failOpenfort("configure", "no-secure-context", /* @__PURE__ */ new Error("Web Crypto unavailable: browser is not a secure context"));
|
|
10005
10336
|
}
|
|
10006
|
-
|
|
10007
|
-
|
|
10337
|
+
const authProvider = bootstrap.openfortAuthProvider ?? "better-auth";
|
|
10338
|
+
function accessTokenUrl() {
|
|
10339
|
+
return authProvider === "oidc" ? `${authBaseUrl}/convex/token` : `${authBaseUrl}/get-session`;
|
|
10008
10340
|
}
|
|
10009
10341
|
function encryptionSessionUrl() {
|
|
10010
10342
|
return `${authBaseUrl}/encryption-session`;
|
|
10011
10343
|
}
|
|
10344
|
+
let cycleStartedAt = null;
|
|
10345
|
+
let stepStartedAt = 0;
|
|
10346
|
+
let tokenCalls = 0;
|
|
10347
|
+
const sinceCycleStart = () => cycleStartedAt === null ? void 0 : Date.now() - cycleStartedAt;
|
|
10348
|
+
const step = (name, extra = {}) => {
|
|
10349
|
+
const now = Date.now();
|
|
10350
|
+
diagnostic?.trace("openfort.step", {
|
|
10351
|
+
step: name,
|
|
10352
|
+
duration_ms: now - stepStartedAt,
|
|
10353
|
+
elapsed_ms: sinceCycleStart(),
|
|
10354
|
+
...extra
|
|
10355
|
+
});
|
|
10356
|
+
stepStartedAt = now;
|
|
10357
|
+
};
|
|
10012
10358
|
async function fetchBetterAuthAccessToken() {
|
|
10359
|
+
tokenCalls += 1;
|
|
10360
|
+
const tokenStartedAt = Date.now();
|
|
10013
10361
|
try {
|
|
10014
|
-
const response = await fetch(
|
|
10362
|
+
const response = await fetch(accessTokenUrl(), {
|
|
10363
|
+
credentials: "include",
|
|
10364
|
+
cache: "no-store"
|
|
10365
|
+
});
|
|
10015
10366
|
if (!response.ok) {
|
|
10016
10367
|
diagnostic?.trace("openfort.token", {
|
|
10017
10368
|
ok: false,
|
|
@@ -10021,7 +10372,8 @@ function createOpenfortBrowserSignerFromBootstrap(bootstrap, options = {}) {
|
|
|
10021
10372
|
});
|
|
10022
10373
|
return null;
|
|
10023
10374
|
}
|
|
10024
|
-
const
|
|
10375
|
+
const body = await response.json();
|
|
10376
|
+
const token = (authProvider === "oidc" ? body.token : body.session?.token)?.trim();
|
|
10025
10377
|
if (token === void 0 || token.length === 0) {
|
|
10026
10378
|
diagnostic?.trace("openfort.token", {
|
|
10027
10379
|
ok: false,
|
|
@@ -10032,7 +10384,11 @@ function createOpenfortBrowserSignerFromBootstrap(bootstrap, options = {}) {
|
|
|
10032
10384
|
}
|
|
10033
10385
|
diagnostic?.trace("openfort.token", {
|
|
10034
10386
|
ok: true,
|
|
10035
|
-
tokenPresent: true
|
|
10387
|
+
tokenPresent: true,
|
|
10388
|
+
provider: authProvider,
|
|
10389
|
+
call: tokenCalls,
|
|
10390
|
+
duration_ms: Date.now() - tokenStartedAt,
|
|
10391
|
+
elapsed_ms: sinceCycleStart()
|
|
10036
10392
|
});
|
|
10037
10393
|
return token;
|
|
10038
10394
|
} catch (cause) {
|
|
@@ -10048,7 +10404,7 @@ function createOpenfortBrowserSignerFromBootstrap(bootstrap, options = {}) {
|
|
|
10048
10404
|
baseConfiguration: { publishableKey: bootstrap.openfortPublishableKey },
|
|
10049
10405
|
shieldConfiguration: { shieldPublishableKey: bootstrap.shieldPublishableKey },
|
|
10050
10406
|
thirdPartyAuth: {
|
|
10051
|
-
provider: ThirdPartyOAuthProvider.BETTER_AUTH,
|
|
10407
|
+
provider: authProvider === "oidc" ? ThirdPartyOAuthProvider.OIDC : ThirdPartyOAuthProvider.BETTER_AUTH,
|
|
10052
10408
|
getAccessToken: fetchBetterAuthAccessToken
|
|
10053
10409
|
}
|
|
10054
10410
|
});
|
|
@@ -10088,10 +10444,16 @@ function createOpenfortBrowserSignerFromBootstrap(bootstrap, options = {}) {
|
|
|
10088
10444
|
let walletReadyPromise = null;
|
|
10089
10445
|
function startWalletReady() {
|
|
10090
10446
|
return (async () => {
|
|
10447
|
+
const startedAt = Date.now();
|
|
10448
|
+
cycleStartedAt = startedAt;
|
|
10449
|
+
stepStartedAt = startedAt;
|
|
10450
|
+
tokenCalls = 0;
|
|
10091
10451
|
if (isInsecureBrowserContext()) failNoSecureContext();
|
|
10092
10452
|
await openfort.waitForInitialization();
|
|
10453
|
+
step("initialize");
|
|
10093
10454
|
const accessToken = await fetchBetterAuthAccessToken();
|
|
10094
10455
|
if (accessToken === null) throw openfortProviderError("token", /* @__PURE__ */ new Error("Better Auth access token unavailable for Openfort"));
|
|
10456
|
+
step("token");
|
|
10095
10457
|
let encryptionResponse;
|
|
10096
10458
|
try {
|
|
10097
10459
|
encryptionResponse = await fetch(encryptionSessionUrl(), {
|
|
@@ -10134,10 +10496,12 @@ function createOpenfortBrowserSignerFromBootstrap(bootstrap, options = {}) {
|
|
|
10134
10496
|
ok: true,
|
|
10135
10497
|
httpStatus: encryptionResponse.status
|
|
10136
10498
|
});
|
|
10499
|
+
step("encryptionSession", { httpStatus: encryptionResponse.status });
|
|
10137
10500
|
let embeddedState;
|
|
10138
10501
|
try {
|
|
10139
10502
|
embeddedState = await openfort.embeddedWallet.getEmbeddedState();
|
|
10140
10503
|
diagnostic?.trace("openfort.embeddedState", { state: embeddedState });
|
|
10504
|
+
step("embeddedState", { state: embeddedState });
|
|
10141
10505
|
} catch (cause) {
|
|
10142
10506
|
diagnostic?.trace("openfort.embeddedState", {
|
|
10143
10507
|
ok: false,
|
|
@@ -10151,6 +10515,7 @@ function createOpenfortBrowserSignerFromBootstrap(bootstrap, options = {}) {
|
|
|
10151
10515
|
try {
|
|
10152
10516
|
await configureEmbeddedWallet(encryptionBody.sessionId);
|
|
10153
10517
|
diagnostic?.trace("openfort.configure", { ok: true });
|
|
10518
|
+
step("configure");
|
|
10154
10519
|
} catch (cause) {
|
|
10155
10520
|
const error = openfortProviderError("configure", cause);
|
|
10156
10521
|
diagnostic?.trace("openfort.configure", {
|
|
@@ -10163,6 +10528,7 @@ function createOpenfortBrowserSignerFromBootstrap(bootstrap, options = {}) {
|
|
|
10163
10528
|
try {
|
|
10164
10529
|
await openfort.embeddedWallet.get();
|
|
10165
10530
|
diagnostic?.trace("openfort.get", { ok: true });
|
|
10531
|
+
step("get");
|
|
10166
10532
|
} catch (cause) {
|
|
10167
10533
|
if (!isStaleUserSignal(cause)) {
|
|
10168
10534
|
diagnostic?.trace("openfort.get", {
|
|
@@ -10176,7 +10542,13 @@ function createOpenfortBrowserSignerFromBootstrap(bootstrap, options = {}) {
|
|
|
10176
10542
|
failure_mode: "stale-openfort-cache"
|
|
10177
10543
|
});
|
|
10178
10544
|
await healStaleOpenfortCache(encryptionBody.sessionId);
|
|
10545
|
+
step("healStaleCache");
|
|
10179
10546
|
}
|
|
10547
|
+
step("walletReady", {
|
|
10548
|
+
ok: true,
|
|
10549
|
+
total_ms: Date.now() - startedAt,
|
|
10550
|
+
token_calls: tokenCalls
|
|
10551
|
+
});
|
|
10180
10552
|
})();
|
|
10181
10553
|
}
|
|
10182
10554
|
async function ensureOpenfortWalletReady() {
|
|
@@ -10259,9 +10631,13 @@ function createOpenfortBrowserSignerFromBootstrap(bootstrap, options = {}) {
|
|
|
10259
10631
|
statusStore,
|
|
10260
10632
|
ensureWalletReady: ensureOpenfortWalletReady,
|
|
10261
10633
|
getAddress: async () => {
|
|
10634
|
+
const addressStartedAt = Date.now();
|
|
10262
10635
|
try {
|
|
10263
10636
|
const address = await signer.getAddress();
|
|
10264
|
-
diagnostic?.trace("openfort.address", {
|
|
10637
|
+
diagnostic?.trace("openfort.address", {
|
|
10638
|
+
ok: true,
|
|
10639
|
+
duration_ms: Date.now() - addressStartedAt
|
|
10640
|
+
});
|
|
10265
10641
|
return address;
|
|
10266
10642
|
} catch (cause) {
|
|
10267
10643
|
diagnostic?.trace("openfort.address", {
|
|
@@ -11363,14 +11739,66 @@ function organizationWrongState(currentState, validStates) {
|
|
|
11363
11739
|
function requestCancelled(options) {
|
|
11364
11740
|
return options?.signal?.aborted === true;
|
|
11365
11741
|
}
|
|
11742
|
+
function accountInFlight(state) {
|
|
11743
|
+
return state.phase === "authenticated" && (state.account.at === "deriving" || state.account.at === "claiming");
|
|
11744
|
+
}
|
|
11745
|
+
function settledAccount(deps, options) {
|
|
11746
|
+
return new Promise((resolve) => {
|
|
11747
|
+
let done = false;
|
|
11748
|
+
let unsubscribe = null;
|
|
11749
|
+
const onAbort = () => finish({
|
|
11750
|
+
ok: false,
|
|
11751
|
+
error: Errors.cancelled({ operation: CAPXUL_OPERATIONS.onboarding.completeOrganization })
|
|
11752
|
+
});
|
|
11753
|
+
const finish = (result) => {
|
|
11754
|
+
if (done) return;
|
|
11755
|
+
done = true;
|
|
11756
|
+
unsubscribe?.();
|
|
11757
|
+
options?.signal?.removeEventListener("abort", onAbort);
|
|
11758
|
+
resolve(result);
|
|
11759
|
+
};
|
|
11760
|
+
if (options?.signal?.aborted) {
|
|
11761
|
+
onAbort();
|
|
11762
|
+
return;
|
|
11763
|
+
}
|
|
11764
|
+
options?.signal?.addEventListener("abort", onAbort, { once: true });
|
|
11765
|
+
const stop = deps.subscribeIdentity((state) => {
|
|
11766
|
+
if (!accountInFlight(state)) finish({
|
|
11767
|
+
ok: true,
|
|
11768
|
+
value: state
|
|
11769
|
+
});
|
|
11770
|
+
});
|
|
11771
|
+
if (done) {
|
|
11772
|
+
stop();
|
|
11773
|
+
return;
|
|
11774
|
+
}
|
|
11775
|
+
unsubscribe = stop;
|
|
11776
|
+
const current = deps.snapshotIdentity();
|
|
11777
|
+
if (!accountInFlight(current)) finish({
|
|
11778
|
+
ok: true,
|
|
11779
|
+
value: current
|
|
11780
|
+
});
|
|
11781
|
+
});
|
|
11782
|
+
}
|
|
11366
11783
|
async function reachClaimedAccount(deps, options) {
|
|
11367
11784
|
let state = deps.snapshotIdentity();
|
|
11785
|
+
if (accountInFlight(state)) {
|
|
11786
|
+
const settled = await settledAccount(deps, options);
|
|
11787
|
+
if (!settled.ok) return settled;
|
|
11788
|
+
state = settled.value;
|
|
11789
|
+
}
|
|
11368
11790
|
if (state.phase !== "authenticated" || state.account.at === "unknown") {
|
|
11369
11791
|
const ensured = await deps.sendIdentity({ _tag: "EnsureAccount" }, options);
|
|
11370
11792
|
if (!ensured.ok) return ensured;
|
|
11371
11793
|
state = ensured.value;
|
|
11372
11794
|
}
|
|
11373
11795
|
if (state.phase !== "authenticated") return organizationWrongState(state.phase, ["authenticated:claimed"]);
|
|
11796
|
+
if (accountInFlight(state)) {
|
|
11797
|
+
const settled = await settledAccount(deps, options);
|
|
11798
|
+
if (!settled.ok) return settled;
|
|
11799
|
+
state = settled.value;
|
|
11800
|
+
if (state.phase !== "authenticated") return organizationWrongState(state.phase, ["authenticated:claimed"]);
|
|
11801
|
+
}
|
|
11374
11802
|
if (state.account.at !== "claimed") {
|
|
11375
11803
|
const event = state.account.at === "failed" ? { _tag: "RetryAccount" } : state.account.at === "counterfactual" ? { _tag: "ClaimAccount" } : { _tag: "EnsureAccount" };
|
|
11376
11804
|
const claimed = await deps.sendIdentity(event, options);
|
|
@@ -11722,6 +12150,25 @@ const permissionContract = { read: makeFunctionReference(CAPXUL_FUNCTIONS["permi
|
|
|
11722
12150
|
function isAborted(signal) {
|
|
11723
12151
|
return signal?.aborted === true;
|
|
11724
12152
|
}
|
|
12153
|
+
/**
|
|
12154
|
+
* One owning operation per public Permission method. The compound command
|
|
12155
|
+
* (prepare, equality checks, sign, submit) keeps owning its own cancellation;
|
|
12156
|
+
* the observed runner around it is what gives the attempt its
|
|
12157
|
+
* `capxul_sdk_operation` record, its span and its failure capture, so a signer
|
|
12158
|
+
* refusal or an unexpected provider error joins to the operation like every
|
|
12159
|
+
* other public surface.
|
|
12160
|
+
*/
|
|
12161
|
+
function observed(deps, operation, options, run) {
|
|
12162
|
+
const controls = deps.invocationControls?.(options) ?? options;
|
|
12163
|
+
return runCapxulEffect(Effect.gen(function* () {
|
|
12164
|
+
if (controls?.signal?.aborted === true) return yield* Effect.fail(Errors.cancelled({ operation }));
|
|
12165
|
+
const result = yield* Effect.tryPromise({
|
|
12166
|
+
try: () => run(controls?.signal),
|
|
12167
|
+
catch: (cause) => cause instanceof CapxulError ? cause : Errors.unknown(cause)
|
|
12168
|
+
});
|
|
12169
|
+
return yield* result.ok ? Effect.succeed(result.value) : Effect.fail(result.error);
|
|
12170
|
+
}), observationOnlyControls(controls), operation, deps.runPromise);
|
|
12171
|
+
}
|
|
11725
12172
|
async function execute(deps, orgId, command, signal) {
|
|
11726
12173
|
if (isAborted(signal)) return {
|
|
11727
12174
|
ok: false,
|
|
@@ -11781,36 +12228,37 @@ async function execute(deps, orgId, command, signal) {
|
|
|
11781
12228
|
}
|
|
11782
12229
|
function makePermissionMethods(deps, orgId) {
|
|
11783
12230
|
const reads = deps.permissionFunctions ?? permissionContract;
|
|
12231
|
+
const ops = CAPXUL_OPERATIONS.org.permissions;
|
|
11784
12232
|
return {
|
|
11785
|
-
create: (input, options) => execute(deps, orgId, {
|
|
12233
|
+
create: (input, options) => observed(deps, ops.create, options, (signal) => execute(deps, orgId, {
|
|
11786
12234
|
operation: "create",
|
|
11787
12235
|
...input
|
|
11788
|
-
},
|
|
11789
|
-
change: (input, options) => execute(deps, orgId, {
|
|
12236
|
+
}, signal)),
|
|
12237
|
+
change: (input, options) => observed(deps, ops.change, options, (signal) => execute(deps, orgId, {
|
|
11790
12238
|
operation: "change",
|
|
11791
12239
|
...input
|
|
11792
|
-
},
|
|
11793
|
-
assign: (input, options) => execute(deps, orgId, {
|
|
12240
|
+
}, signal)),
|
|
12241
|
+
assign: (input, options) => observed(deps, ops.assign, options, (signal) => execute(deps, orgId, {
|
|
11794
12242
|
operation: "assign",
|
|
11795
12243
|
...input
|
|
11796
|
-
},
|
|
11797
|
-
revoke: (input, options) => execute(deps, orgId, {
|
|
12244
|
+
}, signal)),
|
|
12245
|
+
revoke: (input, options) => observed(deps, ops.revoke, options, (signal) => execute(deps, orgId, {
|
|
11798
12246
|
operation: "revoke",
|
|
11799
12247
|
...input
|
|
11800
|
-
},
|
|
11801
|
-
replace: (input, options) => execute(deps, orgId, {
|
|
12248
|
+
}, signal)),
|
|
12249
|
+
replace: (input, options) => observed(deps, ops.replace, options, (signal) => execute(deps, orgId, {
|
|
11802
12250
|
operation: "replace",
|
|
11803
12251
|
...input
|
|
11804
|
-
},
|
|
11805
|
-
list: (options) =>
|
|
11806
|
-
get:
|
|
11807
|
-
const listed = await runIfActive(
|
|
12252
|
+
}, signal)),
|
|
12253
|
+
list: (options) => observed(deps, ops.list, options, (signal) => runIfActive(signal, CAPXUL_OPERATIONS.permissions.list, () => deps.convexCall.query(reads.read, { orgId }))),
|
|
12254
|
+
get: (permissionId, options) => observed(deps, ops.get, options, async (signal) => {
|
|
12255
|
+
const listed = await runIfActive(signal, CAPXUL_OPERATIONS.permissions.get, () => deps.convexCall.query(reads.read, { orgId }));
|
|
11808
12256
|
if (!listed.ok) return listed;
|
|
11809
12257
|
return {
|
|
11810
12258
|
ok: true,
|
|
11811
12259
|
value: listed.value.permissions.find((row) => row.permissionId === permissionId) ?? null
|
|
11812
12260
|
};
|
|
11813
|
-
}
|
|
12261
|
+
})
|
|
11814
12262
|
};
|
|
11815
12263
|
}
|
|
11816
12264
|
//#endregion
|
|
@@ -12372,7 +12820,7 @@ function hermeticOrgView(input) {
|
|
|
12372
12820
|
name: input.name,
|
|
12373
12821
|
handle: input.handle,
|
|
12374
12822
|
safeAddress: toAddress(placeholder),
|
|
12375
|
-
role:
|
|
12823
|
+
role: ADMIN_ROLE_LABEL,
|
|
12376
12824
|
treasury: zeroTreasury(orgId),
|
|
12377
12825
|
bio: null,
|
|
12378
12826
|
size: null,
|
|
@@ -12618,6 +13066,13 @@ function isRetryableOrganizationSetupFailure(error) {
|
|
|
12618
13066
|
* constructed for each `org(orgId)` call. It has no shared mutable active-Org
|
|
12619
13067
|
* state. The closed-over `orgId` is its only entity reference.
|
|
12620
13068
|
*/
|
|
13069
|
+
/** The observed-runner pair a method bundle needs to own its operation records. */
|
|
13070
|
+
function observedRunnerDeps(deps) {
|
|
13071
|
+
return {
|
|
13072
|
+
...deps.runPromise === void 0 ? {} : { runPromise: deps.runPromise },
|
|
13073
|
+
...deps.invocationControls === void 0 ? {} : { invocationControls: deps.invocationControls }
|
|
13074
|
+
};
|
|
13075
|
+
}
|
|
12621
13076
|
function makeOrgMethods(deps) {
|
|
12622
13077
|
const orgDeps = {
|
|
12623
13078
|
...deps.orgPort === void 0 ? {} : { orgPort: deps.orgPort },
|
|
@@ -12785,6 +13240,7 @@ function makeOrgMethods(deps) {
|
|
|
12785
13240
|
},
|
|
12786
13241
|
permissions: convexCall === void 0 ? makeNotImplementedPermissionMethods() : makePermissionMethods({
|
|
12787
13242
|
convexCall,
|
|
13243
|
+
...observedRunnerDeps(deps),
|
|
12788
13244
|
...deps.actor === void 0 ? {} : { actor: deps.actor },
|
|
12789
13245
|
...deps.chainId === void 0 ? {} : { chainId: Number(deps.chainId) },
|
|
12790
13246
|
...deps.signer === void 0 ? {} : { signer: deps.signer }
|
|
@@ -12977,11 +13433,28 @@ const authFailure = (record, reason, mode) => ({
|
|
|
12977
13433
|
...linkage(record)
|
|
12978
13434
|
}
|
|
12979
13435
|
});
|
|
13436
|
+
const REFUSAL_EVENTS = /* @__PURE__ */ new Set([
|
|
13437
|
+
"ClaimAccount",
|
|
13438
|
+
"RetryAccount",
|
|
13439
|
+
"CreateOrganization",
|
|
13440
|
+
"AttachOrganization",
|
|
13441
|
+
"RetryOrganization"
|
|
13442
|
+
]);
|
|
12980
13443
|
function mapIdentityProductObservation(record, state) {
|
|
12981
13444
|
if (record.machine !== "identity" || !IDENTITY_EVENT_TAG_SET.has(record.event)) return NONE;
|
|
12982
13445
|
if (record.outcome === "cancelled") return NONE;
|
|
12983
13446
|
if (record.slot === "identity:auth" && record.outcome === "refused") return authFailure(record, record.refusal_code, void 0);
|
|
12984
13447
|
if (record.slot === "identity:auth" && record.outcome === "failed") return authFailure(record, record.error_code, record.failure?.mode);
|
|
13448
|
+
if (record.outcome === "refused" && REFUSAL_EVENTS.has(record.event)) return {
|
|
13449
|
+
name: "identity_refused",
|
|
13450
|
+
props: {
|
|
13451
|
+
event: record.event,
|
|
13452
|
+
refusal_code: record.refusal_code,
|
|
13453
|
+
slot: record.slot,
|
|
13454
|
+
state: record.state,
|
|
13455
|
+
...linkage(record)
|
|
13456
|
+
}
|
|
13457
|
+
};
|
|
12985
13458
|
if (record.outcome !== "applied") return NONE;
|
|
12986
13459
|
switch (APPLIED_ACTION_BY_EVENT[record.event]) {
|
|
12987
13460
|
case "none": return NONE;
|
|
@@ -13140,10 +13613,13 @@ function assembleCapxulClient(input) {
|
|
|
13140
13613
|
...input.invokeTimeoutMs === void 0 ? {} : { invokeTimeoutMs: input.invokeTimeoutMs },
|
|
13141
13614
|
afterVerifyOtp: async () => {
|
|
13142
13615
|
const browserSigner = input.signer;
|
|
13143
|
-
if (browserSigner !== void 0 && "resetSession" in browserSigner && typeof browserSigner.resetSession === "function")
|
|
13144
|
-
|
|
13145
|
-
|
|
13146
|
-
|
|
13616
|
+
if (browserSigner !== void 0 && "resetSession" in browserSigner && typeof browserSigner.resetSession === "function") {
|
|
13617
|
+
try {
|
|
13618
|
+
browserSigner.resetSession();
|
|
13619
|
+
} catch (cause) {
|
|
13620
|
+
throw signerFailure(browserSigner.source, "resetSession", cause);
|
|
13621
|
+
}
|
|
13622
|
+
if (browserSigner.statusStore?.status() === "unknown" && typeof browserSigner.ensureWalletReady === "function") browserSigner.ensureWalletReady().catch(() => {});
|
|
13147
13623
|
}
|
|
13148
13624
|
await detectPendingOrgInvitations?.();
|
|
13149
13625
|
kickProvisioning?.();
|
|
@@ -13170,6 +13646,7 @@ function assembleCapxulClient(input) {
|
|
|
13170
13646
|
ensureAccount: account._internal.ensureReady,
|
|
13171
13647
|
getLifecycle: account.getLifecycle,
|
|
13172
13648
|
snapshotIdentity: actor.snapshot,
|
|
13649
|
+
subscribeIdentity: actor.subscribe,
|
|
13173
13650
|
sendIdentity: (event, options) => runCapxulEffect(askIdentity(actor, event, options), options, CAPXUL_OPERATIONS._internal.identity.send, effectRunner.runPromise)
|
|
13174
13651
|
});
|
|
13175
13652
|
const system = makeSystemMethods({ convexCall: input.ports.convexCall });
|
|
@@ -13303,6 +13780,7 @@ function assembleCapxulClient(input) {
|
|
|
13303
13780
|
identity: identityRuntime,
|
|
13304
13781
|
bootstrap: input.bootstrap,
|
|
13305
13782
|
accounts: { fund: accounts.fund },
|
|
13783
|
+
...input.ports.indexerRead === void 0 ? {} : { indexer: input.ports.indexerRead },
|
|
13306
13784
|
organizationSetup: { getProofReceipt: (orgId) => runPortEffect(input.ports.convexCall.query(organizationSetupProofReceiptQuery, { orgId })) },
|
|
13307
13785
|
telemetry: input.ports.telemetry,
|
|
13308
13786
|
captureExternalAddressAcknowledged: (address, organizationId) => {
|
|
@@ -13361,8 +13839,10 @@ function withHostObservationControls(snapshot, input, actorId) {
|
|
|
13361
13839
|
const previous = readInvocationObservation(input);
|
|
13362
13840
|
if (previous !== void 0) return attachInvocationObservation({ ...input }, previous);
|
|
13363
13841
|
const captured = snapshot?.() ?? { active: true };
|
|
13842
|
+
const context = captured.active ? withCapturedAttemptContext(captured.context, input) : captured.context;
|
|
13364
13843
|
return attachInvocationObservation({ ...input }, {
|
|
13365
13844
|
...captured,
|
|
13845
|
+
...context === void 0 ? {} : { context },
|
|
13366
13846
|
...actorId === void 0 ? {} : { actorId }
|
|
13367
13847
|
});
|
|
13368
13848
|
}
|
|
@@ -13502,6 +13982,382 @@ function snapshotHostObservability(observability) {
|
|
|
13502
13982
|
function isPromiseLike(value) {
|
|
13503
13983
|
return (typeof value === "object" && value !== null || typeof value === "function") && "then" in value;
|
|
13504
13984
|
}
|
|
13985
|
+
//#endregion
|
|
13986
|
+
//#region src/ports/indexer-read.ts
|
|
13987
|
+
/** The indexer's own page bound. Exceeding it is a caller defect, not a refusal. */
|
|
13988
|
+
const INDEXER_MAX_LIMIT = 1e3;
|
|
13989
|
+
const LOWER_CASE_ADDRESS = /^0x[0-9a-f]{40}$/;
|
|
13990
|
+
/**
|
|
13991
|
+
* Refuse a page the indexer would refuse, before any implementation spends a
|
|
13992
|
+
* round trip on it. Every implementation calls this, so a consumer test written
|
|
13993
|
+
* against the hermetic adapter cannot pass on input production rejects. Throws
|
|
13994
|
+
* a `CapxulError`; callers convert it to an `IndexerReadError`.
|
|
13995
|
+
*/
|
|
13996
|
+
function assertIndexerPage(input) {
|
|
13997
|
+
const safe = String(input.safe).toLowerCase();
|
|
13998
|
+
if (!LOWER_CASE_ADDRESS.test(safe)) throw Errors.invalidInput("safe", "must be a lower-case EVM address");
|
|
13999
|
+
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}`);
|
|
14000
|
+
const offset = input.offset ?? 0;
|
|
14001
|
+
if (!Number.isSafeInteger(offset) || offset < 0) throw Errors.invalidInput("offset", "must be a non-negative integer");
|
|
14002
|
+
return safe;
|
|
14003
|
+
}
|
|
14004
|
+
var IndexerReadError = class extends Data.TaggedError("IndexerReadError") {};
|
|
14005
|
+
function indexerReadErrorFromCapxul(operation, error, cause = error) {
|
|
14006
|
+
return new IndexerReadError({
|
|
14007
|
+
operation,
|
|
14008
|
+
publicCode: error.code,
|
|
14009
|
+
publicError: error,
|
|
14010
|
+
cause,
|
|
14011
|
+
...error.details === void 0 ? {} : { details: error.details }
|
|
14012
|
+
});
|
|
14013
|
+
}
|
|
14014
|
+
Context.Service()("@capxul/sdk/ports/IndexerReadPort");
|
|
14015
|
+
//#endregion
|
|
14016
|
+
//#region src/adapters/ponder/decode.ts
|
|
14017
|
+
/**
|
|
14018
|
+
* Decode the indexer's rows at the trust boundary.
|
|
14019
|
+
*
|
|
14020
|
+
* Ponder returns the raw PostgreSQL result: `integer` arrives as a number,
|
|
14021
|
+
* `numeric(78)` and `text` arrive as strings. Amounts, block numbers and
|
|
14022
|
+
* timestamps are `numeric(78)`, so they become `bigint` here and never pass
|
|
14023
|
+
* through `Number`. Every decoder throws a `CapxulError`; the adapter turns
|
|
14024
|
+
* that into a typed `IndexerReadError`.
|
|
14025
|
+
*/
|
|
14026
|
+
const HEX = /^0x[0-9a-fA-F]+$/;
|
|
14027
|
+
function field(row, key) {
|
|
14028
|
+
if (!(key in row)) throw Errors.invalidInput(key, "missing from the indexer row");
|
|
14029
|
+
return row[key];
|
|
14030
|
+
}
|
|
14031
|
+
function text(row, key) {
|
|
14032
|
+
const value = field(row, key);
|
|
14033
|
+
if (typeof value !== "string") throw Errors.invalidInput(key, "must be a string");
|
|
14034
|
+
return value;
|
|
14035
|
+
}
|
|
14036
|
+
function hex(row, key) {
|
|
14037
|
+
const value = text(row, key);
|
|
14038
|
+
if (!HEX.test(value)) throw Errors.invalidInput(key, "must be a hex string");
|
|
14039
|
+
return value.toLowerCase();
|
|
14040
|
+
}
|
|
14041
|
+
function integer(row, key) {
|
|
14042
|
+
const value = field(row, key);
|
|
14043
|
+
const parsed = typeof value === "string" ? Number(value) : value;
|
|
14044
|
+
if (typeof parsed !== "number" || !Number.isSafeInteger(parsed)) throw Errors.invalidInput(key, "must be an integer");
|
|
14045
|
+
return parsed;
|
|
14046
|
+
}
|
|
14047
|
+
/** `numeric(78)` crosses the wire as a decimal string; keep every digit. */
|
|
14048
|
+
function big(row, key) {
|
|
14049
|
+
const value = field(row, key);
|
|
14050
|
+
if (typeof value === "bigint") return value;
|
|
14051
|
+
const raw = typeof value === "number" ? String(value) : value;
|
|
14052
|
+
if (typeof raw !== "string" || !/^-?[0-9]+$/.test(raw)) throw Errors.invalidInput(key, "must be an exact integer amount");
|
|
14053
|
+
return BigInt(raw);
|
|
14054
|
+
}
|
|
14055
|
+
function asRow(value) {
|
|
14056
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) throw Errors.invalidInput("row", "must be an object");
|
|
14057
|
+
return value;
|
|
14058
|
+
}
|
|
14059
|
+
function identity(row) {
|
|
14060
|
+
return {
|
|
14061
|
+
id: text(row, "id"),
|
|
14062
|
+
chainId: toChainId(integer(row, "chain_id")),
|
|
14063
|
+
txHash: toTxHash(hex(row, "tx_hash")),
|
|
14064
|
+
transactionLogOrdinal: integer(row, "transaction_log_ordinal"),
|
|
14065
|
+
blockNumber: big(row, "block_number"),
|
|
14066
|
+
blockHash: hex(row, "block_hash"),
|
|
14067
|
+
timestamp: big(row, "timestamp")
|
|
14068
|
+
};
|
|
14069
|
+
}
|
|
14070
|
+
function decodeTransfer(value) {
|
|
14071
|
+
const row = asRow(value);
|
|
14072
|
+
return {
|
|
14073
|
+
...identity(row),
|
|
14074
|
+
assetId: toAssetId(text(row, "asset_id")),
|
|
14075
|
+
token: toAddress(hex(row, "token")),
|
|
14076
|
+
from: toAddress(hex(row, "from")),
|
|
14077
|
+
to: toAddress(hex(row, "to")),
|
|
14078
|
+
amount: big(row, "amount")
|
|
14079
|
+
};
|
|
14080
|
+
}
|
|
14081
|
+
function decodePayment(value) {
|
|
14082
|
+
const row = asRow(value);
|
|
14083
|
+
return {
|
|
14084
|
+
...identity(row),
|
|
14085
|
+
contract: toAddress(hex(row, "contract")),
|
|
14086
|
+
settlementId: hex(row, "settlement_id"),
|
|
14087
|
+
sender: toAddress(hex(row, "sender")),
|
|
14088
|
+
recipient: toAddress(hex(row, "recipient")),
|
|
14089
|
+
assetId: toAssetId(text(row, "asset_id")),
|
|
14090
|
+
token: toAddress(hex(row, "token")),
|
|
14091
|
+
amount: big(row, "amount"),
|
|
14092
|
+
kind: integer(row, "kind"),
|
|
14093
|
+
documentHash: hex(row, "document_hash")
|
|
14094
|
+
};
|
|
14095
|
+
}
|
|
14096
|
+
/** The `balance` view exposes the signed sum under the underlying column name `to`. */
|
|
14097
|
+
function decodeBalance(value) {
|
|
14098
|
+
const row = asRow(value);
|
|
14099
|
+
return {
|
|
14100
|
+
chainId: toChainId(integer(row, "chain_id")),
|
|
14101
|
+
assetId: toAssetId(text(row, "asset_id")),
|
|
14102
|
+
address: toAddress(hex(row, "to")),
|
|
14103
|
+
amount: big(row, "amount")
|
|
14104
|
+
};
|
|
14105
|
+
}
|
|
14106
|
+
const DECODERS = {
|
|
14107
|
+
transfer: decodeTransfer,
|
|
14108
|
+
payment: decodePayment,
|
|
14109
|
+
balance: decodeBalance
|
|
14110
|
+
};
|
|
14111
|
+
/** Decode one `/sql/db` or `/sql/live` payload. Ponder returns `{ rows: [...] }`. */
|
|
14112
|
+
function decodeRows(table, body) {
|
|
14113
|
+
const rows = asRow(body).rows;
|
|
14114
|
+
if (!Array.isArray(rows)) throw Errors.invalidInput("rows", "must be an array");
|
|
14115
|
+
const decode = DECODERS[table];
|
|
14116
|
+
return rows.map(decode);
|
|
14117
|
+
}
|
|
14118
|
+
/** Decode Ponder's native `/status`: `{ [chainName]: { id, block: { number, timestamp } } }`. */
|
|
14119
|
+
function decodeStatus(body) {
|
|
14120
|
+
const chains = asRow(body);
|
|
14121
|
+
return Object.entries(chains).map(([chain, value]) => {
|
|
14122
|
+
const entry = asRow(value);
|
|
14123
|
+
const block = asRow(field(entry, "block"));
|
|
14124
|
+
return {
|
|
14125
|
+
chain,
|
|
14126
|
+
chainId: toChainId(integer(entry, "id")),
|
|
14127
|
+
blockNumber: big(block, "number"),
|
|
14128
|
+
blockTimestamp: big(block, "timestamp")
|
|
14129
|
+
};
|
|
14130
|
+
});
|
|
14131
|
+
}
|
|
14132
|
+
//#endregion
|
|
14133
|
+
//#region src/adapters/ponder/statements.ts
|
|
14134
|
+
/**
|
|
14135
|
+
* The exact statements the indexer accepts.
|
|
14136
|
+
*
|
|
14137
|
+
* `apps/indexer/src/queries.ts` compiles its allowlist with `@ponder/client`
|
|
14138
|
+
* and compares `sql`, `params` and `typings` byte for byte against the caller's
|
|
14139
|
+
* statement. Anything else gets 403. The SDK therefore reproduces the compiled
|
|
14140
|
+
* text here rather than pulling Ponder and Drizzle into a published browser
|
|
14141
|
+
* bundle to regenerate three fixed statements.
|
|
14142
|
+
*
|
|
14143
|
+
* `statements.test.ts` pins the text. When the indexer schema or a query in
|
|
14144
|
+
* that allowlist changes, this file changes with it or every read fails 403.
|
|
14145
|
+
*/
|
|
14146
|
+
const HEADS = {
|
|
14147
|
+
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",
|
|
14148
|
+
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",
|
|
14149
|
+
balance: "select \"chain_id\", \"asset_id\", \"to\", \"amount\" from \"balance\" where \"balance\".\"to\" = $1"
|
|
14150
|
+
};
|
|
14151
|
+
/** How many times the statement binds the Safe address. */
|
|
14152
|
+
const ACTOR_PARAMS = {
|
|
14153
|
+
transfer: 2,
|
|
14154
|
+
payment: 2,
|
|
14155
|
+
balance: 1
|
|
14156
|
+
};
|
|
14157
|
+
/**
|
|
14158
|
+
* Build the compiled query for one table and page. Throws a `CapxulError` for
|
|
14159
|
+
* a page or address the indexer would refuse, so the caller never spends a
|
|
14160
|
+
* round trip to learn it built the request wrong.
|
|
14161
|
+
*/
|
|
14162
|
+
function ponderQuery(table, input) {
|
|
14163
|
+
const safe = assertIndexerPage(input);
|
|
14164
|
+
const offset = input.offset ?? 0;
|
|
14165
|
+
const actors = ACTOR_PARAMS[table];
|
|
14166
|
+
const params = [];
|
|
14167
|
+
for (let i = 0; i < actors; i++) params.push(safe);
|
|
14168
|
+
params.push(input.limit);
|
|
14169
|
+
let sql = `${HEADS[table]} limit $${actors + 1}`;
|
|
14170
|
+
if (offset > 0) {
|
|
14171
|
+
params.push(offset);
|
|
14172
|
+
sql = `${sql} offset $${actors + 2}`;
|
|
14173
|
+
}
|
|
14174
|
+
return {
|
|
14175
|
+
sql,
|
|
14176
|
+
params,
|
|
14177
|
+
typings: params.map(() => "none")
|
|
14178
|
+
};
|
|
14179
|
+
}
|
|
14180
|
+
/**
|
|
14181
|
+
* The `?sql=` value. `@ponder/client` sends `superjson.stringify(query)`; for a
|
|
14182
|
+
* payload of strings and numbers superjson emits no `meta`, so the wire form is
|
|
14183
|
+
* exactly this envelope.
|
|
14184
|
+
*/
|
|
14185
|
+
function ponderQueryParam(query) {
|
|
14186
|
+
return JSON.stringify({ json: query });
|
|
14187
|
+
}
|
|
14188
|
+
//#endregion
|
|
14189
|
+
//#region src/adapters/ponder/sse.ts
|
|
14190
|
+
/**
|
|
14191
|
+
* The smallest SSE reader that can carry an `Authorization` header.
|
|
14192
|
+
*
|
|
14193
|
+
* `@ponder/client` opens its live query with `EventSource`, which accepts no
|
|
14194
|
+
* headers, so it cannot reach the guarded indexer at all. The indexer's CORS
|
|
14195
|
+
* policy allows exactly one request header (`Authorization`), so this reader
|
|
14196
|
+
* sends nothing else. Ponder writes one `data:` line per frame.
|
|
14197
|
+
*/
|
|
14198
|
+
async function readEventStream(body, onData, stopped) {
|
|
14199
|
+
const reader = body.getReader();
|
|
14200
|
+
const decoder = new TextDecoder();
|
|
14201
|
+
let buffer = "";
|
|
14202
|
+
try {
|
|
14203
|
+
for (;;) {
|
|
14204
|
+
const { done, value } = await reader.read();
|
|
14205
|
+
if (done || stopped()) return;
|
|
14206
|
+
buffer += decoder.decode(value, { stream: true });
|
|
14207
|
+
let split = buffer.indexOf("\n\n");
|
|
14208
|
+
while (split !== -1) {
|
|
14209
|
+
const frame = buffer.slice(0, split);
|
|
14210
|
+
buffer = buffer.slice(split + 2);
|
|
14211
|
+
const data = frameData(frame);
|
|
14212
|
+
if (data !== void 0) onData(data);
|
|
14213
|
+
if (stopped()) return;
|
|
14214
|
+
split = buffer.indexOf("\n\n");
|
|
14215
|
+
}
|
|
14216
|
+
}
|
|
14217
|
+
} finally {
|
|
14218
|
+
await reader.cancel().catch(() => void 0);
|
|
14219
|
+
}
|
|
14220
|
+
}
|
|
14221
|
+
function frameData(frame) {
|
|
14222
|
+
const lines = [];
|
|
14223
|
+
for (const line of frame.split("\n")) {
|
|
14224
|
+
if (!line.startsWith("data:")) continue;
|
|
14225
|
+
lines.push(line.slice(line.startsWith("data: ") ? 6 : 5));
|
|
14226
|
+
}
|
|
14227
|
+
return lines.length === 0 ? void 0 : lines.join("\n");
|
|
14228
|
+
}
|
|
14229
|
+
//#endregion
|
|
14230
|
+
//#region src/adapters/ponder/PonderIndexerAdapter.ts
|
|
14231
|
+
var PonderIndexerAdapter = class {
|
|
14232
|
+
#baseUrl;
|
|
14233
|
+
#token;
|
|
14234
|
+
#fetch;
|
|
14235
|
+
#observation;
|
|
14236
|
+
constructor(deps) {
|
|
14237
|
+
this.#baseUrl = deps.baseUrl.replace(/\/$/, "");
|
|
14238
|
+
this.#token = deps.token;
|
|
14239
|
+
this.#observation = deps.observation;
|
|
14240
|
+
this.#fetch = deps.fetch ?? ((input, init) => globalThis.fetch(input, init));
|
|
14241
|
+
}
|
|
14242
|
+
read(table, input) {
|
|
14243
|
+
const operation = `indexer.read.${table}`;
|
|
14244
|
+
return Effect.tryPromise({
|
|
14245
|
+
try: async () => {
|
|
14246
|
+
return decodeRows(table, await (await this.#request("db", table, input, operation)).json());
|
|
14247
|
+
},
|
|
14248
|
+
catch: (cause) => toIndexerReadError(operation, cause)
|
|
14249
|
+
});
|
|
14250
|
+
}
|
|
14251
|
+
subscribe(table, input, callback) {
|
|
14252
|
+
const operation = CAPXUL_OPERATIONS.activity.subscribe;
|
|
14253
|
+
return Effect.sync(() => {
|
|
14254
|
+
let stopped = false;
|
|
14255
|
+
const unsubscribe = () => {
|
|
14256
|
+
stopped = true;
|
|
14257
|
+
};
|
|
14258
|
+
callback({ status: "loading" });
|
|
14259
|
+
this.#stream(table, input, operation, callback, () => stopped).catch(() => void 0);
|
|
14260
|
+
return unsubscribe;
|
|
14261
|
+
});
|
|
14262
|
+
}
|
|
14263
|
+
status = Effect.tryPromise({
|
|
14264
|
+
try: async () => {
|
|
14265
|
+
const response = await this.#fetch(`${this.#baseUrl}/status`, { method: "GET" });
|
|
14266
|
+
if (!response.ok) throw httpError("indexer.status", response.status, await safeText(response));
|
|
14267
|
+
return decodeStatus(await response.json());
|
|
14268
|
+
},
|
|
14269
|
+
catch: (cause) => toIndexerReadError("indexer.status", cause)
|
|
14270
|
+
});
|
|
14271
|
+
/** One authenticated `/sql/:method` call. Throws a `CapxulError` on refusal. */
|
|
14272
|
+
async #request(method, table, input, operation) {
|
|
14273
|
+
const query = ponderQuery(table, input);
|
|
14274
|
+
const token = await this.#token({ forceRefreshToken: false });
|
|
14275
|
+
if (token === null || token === "") throw Errors.notAuthenticated();
|
|
14276
|
+
const url = `${this.#baseUrl}/sql/${method}?sql=${encodeURIComponent(ponderQueryParam(query))}`;
|
|
14277
|
+
const headers = { authorization: `Bearer ${token}` };
|
|
14278
|
+
if (method === "live") headers.accept = "text/event-stream";
|
|
14279
|
+
let response;
|
|
14280
|
+
try {
|
|
14281
|
+
response = await this.#fetch(url, {
|
|
14282
|
+
method: "GET",
|
|
14283
|
+
headers
|
|
14284
|
+
});
|
|
14285
|
+
} catch (cause) {
|
|
14286
|
+
throw Errors.networkError(operation, cause, {
|
|
14287
|
+
provider: "indexer",
|
|
14288
|
+
failure_mode: "upstream-down"
|
|
14289
|
+
});
|
|
14290
|
+
}
|
|
14291
|
+
if (!response.ok) throw httpError(operation, response.status, await safeText(response));
|
|
14292
|
+
return response;
|
|
14293
|
+
}
|
|
14294
|
+
/**
|
|
14295
|
+
* Follow the live stream until the caller unsubscribes or the indexer closes
|
|
14296
|
+
* it. The indexer closes the stream on token expiry or revoked authority
|
|
14297
|
+
* with no frame, so any end the caller did not ask for is a teardown: it
|
|
14298
|
+
* reaches `captureException` under `activity.subscribe` and the caller sees
|
|
14299
|
+
* one `error` snapshot.
|
|
14300
|
+
*/
|
|
14301
|
+
async #stream(table, input, operation, callback, stopped) {
|
|
14302
|
+
let failure;
|
|
14303
|
+
try {
|
|
14304
|
+
const response = await this.#request("live", table, input, operation);
|
|
14305
|
+
if (response.body === null) throw Errors.providerError("indexer", operation, "empty stream");
|
|
14306
|
+
await readEventStream(response.body, (data) => {
|
|
14307
|
+
try {
|
|
14308
|
+
callback({
|
|
14309
|
+
status: "ok",
|
|
14310
|
+
value: decodeRows(table, JSON.parse(data))
|
|
14311
|
+
});
|
|
14312
|
+
} catch (cause) {
|
|
14313
|
+
failure = toPublicError$1(operation, cause);
|
|
14314
|
+
}
|
|
14315
|
+
}, () => stopped() || failure !== void 0);
|
|
14316
|
+
} catch (cause) {
|
|
14317
|
+
failure = toPublicError$1(operation, cause);
|
|
14318
|
+
}
|
|
14319
|
+
if (stopped()) return;
|
|
14320
|
+
const error = failure ?? Errors.capabilityUnavailable("indexer", operation);
|
|
14321
|
+
observeStreamFailure(this.#observation, CAPXUL_OPERATIONS.activity.subscribe, error);
|
|
14322
|
+
callback({
|
|
14323
|
+
status: "error",
|
|
14324
|
+
error
|
|
14325
|
+
});
|
|
14326
|
+
}
|
|
14327
|
+
};
|
|
14328
|
+
/**
|
|
14329
|
+
* Map an indexer refusal into the shared error vocabulary.
|
|
14330
|
+
*
|
|
14331
|
+
* 401 is the expected outcome of an expired or revoked session. 403 is not:
|
|
14332
|
+
* the indexer accepts only its own compiled statements for a Safe in the
|
|
14333
|
+
* claim, so a refusal means this SDK and that deployment disagree, or the
|
|
14334
|
+
* session gained a Safe and still holds the older token. Both must be loud.
|
|
14335
|
+
*/
|
|
14336
|
+
function httpError(operation, status, body) {
|
|
14337
|
+
if (status === 401) return Errors.notAuthenticated();
|
|
14338
|
+
if (status === 429) return Errors.rateLimited({ resource: "indexer" });
|
|
14339
|
+
if (status === 503) return Errors.capabilityUnavailable("indexer", operation);
|
|
14340
|
+
return Errors.providerError("indexer", operation, body, {
|
|
14341
|
+
httpStatus: status,
|
|
14342
|
+
...status >= 500 ? { failure_mode: "upstream-down" } : {},
|
|
14343
|
+
details: { reason: status === 403 ? "scope-refused" : "http-status" }
|
|
14344
|
+
});
|
|
14345
|
+
}
|
|
14346
|
+
function toPublicError$1(operation, cause) {
|
|
14347
|
+
if (cause instanceof CapxulError) return cause;
|
|
14348
|
+
return Errors.providerError("indexer", operation, cause);
|
|
14349
|
+
}
|
|
14350
|
+
function toIndexerReadError(operation, cause) {
|
|
14351
|
+
if (cause instanceof IndexerReadError) return cause;
|
|
14352
|
+
return indexerReadErrorFromCapxul(operation, toPublicError$1(operation, cause), cause);
|
|
14353
|
+
}
|
|
14354
|
+
async function safeText(response) {
|
|
14355
|
+
try {
|
|
14356
|
+
return await response.text();
|
|
14357
|
+
} catch {
|
|
14358
|
+
return "";
|
|
14359
|
+
}
|
|
14360
|
+
}
|
|
13505
14361
|
const SDK_VERSION = version;
|
|
13506
14362
|
/**
|
|
13507
14363
|
* Project the built graph into the flat `FlowPorts` record the method bundles
|
|
@@ -13751,7 +14607,16 @@ async function createProductionAdapters(input) {
|
|
|
13751
14607
|
}));
|
|
13752
14608
|
const applicationLayer = Layer.merge(portsLayer, optionalEngineeringTelemetryLayer(bootstrapResult.value.engineeringTelemetry, resolvedInput.value.runtime));
|
|
13753
14609
|
const context = await Effect.runPromise(Layer.buildWithScope(applicationLayer, scope));
|
|
13754
|
-
const
|
|
14610
|
+
const collected = await Effect.runPromise(collectProductionFlowPorts.pipe(Effect.provide(context)));
|
|
14611
|
+
const indexerUrl = input.indexerUrl ?? bootstrapResult.value.indexerUrl;
|
|
14612
|
+
const indexerRead = productionIndexerReadPort(indexerUrl === void 0 ? input : {
|
|
14613
|
+
...input,
|
|
14614
|
+
indexerUrl
|
|
14615
|
+
}, collected, observation);
|
|
14616
|
+
const ports = indexerRead === void 0 ? collected : {
|
|
14617
|
+
...collected,
|
|
14618
|
+
indexerRead
|
|
14619
|
+
};
|
|
13755
14620
|
const stopConnectionObservation = observeProductionConvexConnection(ports.convexCall, context);
|
|
13756
14621
|
const close = idempotentClose(async () => {
|
|
13757
14622
|
try {
|
|
@@ -13878,7 +14743,7 @@ async function createCapxulClientWithSignerControls(input, controls) {
|
|
|
13878
14743
|
...adapters.value.bootstrap,
|
|
13879
14744
|
authBaseUrl: resolvedAuthBaseUrl
|
|
13880
14745
|
}, {
|
|
13881
|
-
diagnostic: new ConsoleDiagnosticAdapter(),
|
|
14746
|
+
diagnostic: new EffectLogDiagnosticAdapter(adapters.value.context, new ConsoleDiagnosticAdapter()),
|
|
13882
14747
|
signUserOpHash: controls.signEmbeddedUserOpHash
|
|
13883
14748
|
});
|
|
13884
14749
|
signer = withSignerControl(signer, controls.signUserOpHash);
|
|
@@ -14083,5 +14948,27 @@ function idempotentClose(close) {
|
|
|
14083
14948
|
await close();
|
|
14084
14949
|
};
|
|
14085
14950
|
}
|
|
14951
|
+
/**
|
|
14952
|
+
* Build the Ponder read port for this client, or `undefined` when no indexer
|
|
14953
|
+
* origin was supplied. The token provider is the same
|
|
14954
|
+
* `AuthClientPort.getConvexJwt` closure `ConvexCallPort` uses, so the indexer
|
|
14955
|
+
* and Convex see one session with one scope.
|
|
14956
|
+
*/
|
|
14957
|
+
function productionIndexerReadPort(input, ports, observation) {
|
|
14958
|
+
if (input.indexerUrl === void 0 || input.indexerUrl === "") return void 0;
|
|
14959
|
+
return new PonderIndexerAdapter({
|
|
14960
|
+
baseUrl: normalizeHttpUrl("indexerUrl", input.indexerUrl),
|
|
14961
|
+
token: async ({ forceRefreshToken }) => {
|
|
14962
|
+
const token = await Effect.runPromise(Effect.result(ports.authClient.getConvexJwt({
|
|
14963
|
+
forceRefresh: forceRefreshToken,
|
|
14964
|
+
...input.signal === void 0 ? {} : { signal: input.signal }
|
|
14965
|
+
})));
|
|
14966
|
+
if (Result.isFailure(token)) return null;
|
|
14967
|
+
return String(token.success.token);
|
|
14968
|
+
},
|
|
14969
|
+
...input.fetch === void 0 ? {} : { fetch: input.fetch },
|
|
14970
|
+
...observation === void 0 ? {} : { observation }
|
|
14971
|
+
});
|
|
14972
|
+
}
|
|
14086
14973
|
//#endregion
|
|
14087
14974
|
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 };
|