@capxul/sdk 4.1.3 → 4.2.0-rc.1
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/{clock-C2AUlq1V.mjs → OAuthBearerAuthClient-BrbXrndM.mjs} +525 -52
- package/dist/{signer-C0hZ6Kiy.d.mts → OAuthBearerAuthClient-C-ip-z8M.d.mts} +30 -6
- package/dist/capxul-source.json +11 -0
- package/dist/index.d.mts +53 -51
- package/dist/index.mjs +144 -3505
- package/dist/node/index.d.mts +2 -3
- package/dist/node/index.mjs +1 -2
- package/dist/{observation-Bhdq9-l6.d.mts → production-CBKHzqYP.d.mts} +97 -4
- package/dist/{create-capxul-client-CipLFLcJ.mjs → production-FgHUndeX.mjs} +4064 -185
- package/dist/testing/index.d.mts +7 -2
- package/dist/testing/index.mjs +10 -4
- package/package.json +4 -4
- package/dist/OAuthBearerAuthClient-BHvDR8_0.mjs +0 -211
- package/dist/OAuthBearerAuthClient-D-DLYlKj.d.mts +0 -19
|
@@ -1,7 +1,12 @@
|
|
|
1
|
-
import { $ as
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { makeFunctionReference } from "convex/server";
|
|
1
|
+
import { $ as toTesterKind, A as toAccountId, B as toEmail, C as BYTES32_RE, D as ZERO_BYTES32, E as WEI_RE, F as toBudgetId, G as toKycTier, H as toEpochSeconds, I as toChainId, J as toPayrollGroupId, K as toOrgId, L as toCountryCode, M as toAllowedOrigin, P as toAuthUserId, Q as toSessionToken, R as toCurrencyCode, S as ASSET_ID_RE, T as SUPPORTED_CURRENCY_CODES, U as toHandle, V as toEpochMs, W as toJwtToken, X as toPublishableKey, Y as toPayrollRunId, Z as toRoleKey, _ as BASE_SEPOLIA_CHAIN_ID, _t as revertSummaryText, at as EXPECTED_OPERATION_OUTCOMES, b as ACCOUNT_ID_RE, bt as redactSecrets, c as AuthCachePortTag, ct as CHAIN_UPSTREAMS, d as authClientPortFromPromiseAdapter, dt as chainCauseProperties, et as validateHandle, f as AuthClientError, ft as chainEvidenceLabel, g as normalizeBindingEmail, gt as isFailureMode, ht as isChainUpstream, i as BrowserAuthCacheAdapter, it as CapxulError, j as toAddress, l as SystemClockLayer, lt as FAILURE_MODES$1, m as orgRoleKeyForLabel, mt as failureFingerprint, nt as decodeConvexError, ot as Errors, p as AuthClientPortTag, pt as decodeChainCause, q as toPartyId, r as InMemoryAuthCacheAdapter, rt as CAPXUL_ERROR_CODES, st as isCapxulError, u as ClockPortTag, ut as boundedResponseHeaders, v as deriveCapxulSafeAddress, vt as containsSensitiveMaterial, w as EVM_ADDRESS_RE$1, x as APP_ID_RE, xt as redactUrlSecrets, yt as isCredentialField, z as toDurationMs } from "./OAuthBearerAuthClient-BrbXrndM.mjs";
|
|
2
|
+
import { formatUnits, keccak256, parseUnits, recoverAddress, stringToHex, toBytes } from "viem";
|
|
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
|
+
import { getFunctionName, makeFunctionReference } from "convex/server";
|
|
5
|
+
import { privateKeyToAccount } from "viem/accounts";
|
|
6
|
+
import { FetchHttpClient, Headers, HttpClient } from "effect/unstable/http";
|
|
7
|
+
import { OtlpExporter, OtlpLogger, OtlpSerialization, OtlpTracer } from "effect/unstable/observability";
|
|
8
|
+
import { ConvexClient } from "convex/browser";
|
|
9
|
+
import { AccountTypeEnum, ChainTypeEnum, EmbeddedState, Openfort, RecoveryMethod, ThirdPartyOAuthProvider } from "@openfort/openfort-js";
|
|
5
10
|
//#region src/domain/identity/model.ts
|
|
6
11
|
const WRONG = { refused: "WRONG_STATE" };
|
|
7
12
|
/** org-lifecycle.ts:173-184, lifted verbatim. */
|
|
@@ -811,11 +816,11 @@ const PublishableKeyIdSchema = Schema.String.pipe(Schema.refine((value) => value
|
|
|
811
816
|
const TxHashSchema$1 = Schema.String.pipe(Schema.refine((value) => BYTES32_RE.test(value), { message: "must be 0x + 64 hex chars" }));
|
|
812
817
|
const ACCOUNT_ID_TELEMETRY_RE = /^account_[0-9A-Za-z]+$/;
|
|
813
818
|
const WEI_AMOUNT_TELEMETRY_RE = /^[0-9]+$/;
|
|
814
|
-
const AccountIdSchema = Schema.String.pipe(Schema.refine((value) => ACCOUNT_ID_TELEMETRY_RE.test(value), { message: "must be account_ plus an alphanumeric id" }));
|
|
819
|
+
const AccountIdSchema$1 = Schema.String.pipe(Schema.refine((value) => ACCOUNT_ID_TELEMETRY_RE.test(value), { message: "must be account_ plus an alphanumeric id" }));
|
|
815
820
|
const WeiAmountSchema$1 = Schema.String.pipe(Schema.refine((value) => WEI_AMOUNT_TELEMETRY_RE.test(value), { message: "must be a non-negative integer string" }));
|
|
816
821
|
const CurrencyCodeSchema$1 = Schema.String.pipe(Schema.refine((value) => value.length > 0, { message: "must be a non-empty currency code" }));
|
|
817
822
|
const BalanceBucketSchema = Schema.Literals(["zero", "nonzero"]);
|
|
818
|
-
const OptionalAccountId = Schema.optional(AccountIdSchema);
|
|
823
|
+
const OptionalAccountId = Schema.optional(AccountIdSchema$1);
|
|
819
824
|
const OptionalWeiAmount = Schema.optional(WeiAmountSchema$1);
|
|
820
825
|
const OptionalCurrencyCode = Schema.optional(CurrencyCodeSchema$1);
|
|
821
826
|
const OptionalBalanceBucket = Schema.optional(BalanceBucketSchema);
|
|
@@ -1576,6 +1581,7 @@ const ActivityFilterProps = {
|
|
|
1576
1581
|
filter_status: Schema.optional(Schema.Array(Schema.Literals([
|
|
1577
1582
|
"pending",
|
|
1578
1583
|
"settling",
|
|
1584
|
+
"unresolved",
|
|
1579
1585
|
"pending_claim",
|
|
1580
1586
|
"scheduled",
|
|
1581
1587
|
"streaming",
|
|
@@ -1708,7 +1714,7 @@ function redactTelemetryEvent(event, options = {}) {
|
|
|
1708
1714
|
}
|
|
1709
1715
|
function redactTelemetryProps(name, props, options = {}) {
|
|
1710
1716
|
if (props === void 0) return void 0;
|
|
1711
|
-
const clone = cloneProps(props);
|
|
1717
|
+
const clone = cloneProps$1(props);
|
|
1712
1718
|
if (options.rawMode === true) return clone;
|
|
1713
1719
|
for (const key of PII_PROP_KEYS_BY_EVENT[name] ?? []) redactProperty(clone, key);
|
|
1714
1720
|
return clone;
|
|
@@ -1723,17 +1729,17 @@ function redactProperty(props, key) {
|
|
|
1723
1729
|
const value = props[key];
|
|
1724
1730
|
if (typeof value === "string") props[key] = sha256Hex(value).slice(0, 12);
|
|
1725
1731
|
}
|
|
1726
|
-
function cloneProps(props) {
|
|
1732
|
+
function cloneProps$1(props) {
|
|
1727
1733
|
const cloned = {};
|
|
1728
|
-
for (const [key, value] of Object.entries(props)) cloned[key] = cloneTelemetryValue(value);
|
|
1734
|
+
for (const [key, value] of Object.entries(props)) cloned[key] = cloneTelemetryValue$1(value);
|
|
1729
1735
|
return cloned;
|
|
1730
1736
|
}
|
|
1731
|
-
function cloneTelemetryValue(value) {
|
|
1732
|
-
if (Array.isArray(value)) return value.map(cloneTelemetryValue);
|
|
1737
|
+
function cloneTelemetryValue$1(value) {
|
|
1738
|
+
if (Array.isArray(value)) return value.map(cloneTelemetryValue$1);
|
|
1733
1739
|
if (value === null || typeof value !== "object") return value;
|
|
1734
1740
|
if (Object.getPrototypeOf(value) !== Object.prototype) return value;
|
|
1735
1741
|
const cloned = {};
|
|
1736
|
-
for (const [key, nested] of Object.entries(value)) cloned[key] = cloneTelemetryValue(nested);
|
|
1742
|
+
for (const [key, nested] of Object.entries(value)) cloned[key] = cloneTelemetryValue$1(nested);
|
|
1737
1743
|
return cloned;
|
|
1738
1744
|
}
|
|
1739
1745
|
const SHA256_INITIAL_HASH = [
|
|
@@ -1898,6 +1904,11 @@ const bytes32BrandSchema = (name) => lowercasedString.pipe(Schema.refine((value)
|
|
|
1898
1904
|
/** Lowercase, 0x-prefixed bytes32 evidence without a public brand. */
|
|
1899
1905
|
const Bytes32Schema = bytes32BrandSchema("value");
|
|
1900
1906
|
const AddressSchema = addressSchema("address");
|
|
1907
|
+
const AccountIdSchema = Schema.String.pipe(Schema.refine((value) => ACCOUNT_ID_RE.test(value), { message: "must be account_ plus an alphanumeric id" }));
|
|
1908
|
+
const AssetIdSchema = Schema.String.pipe(Schema.refine((value) => {
|
|
1909
|
+
const match = ASSET_ID_RE.exec(value);
|
|
1910
|
+
return match !== null && match[0] === value && Number.isSafeInteger(Number(match[1])) && Number(match[1]) > 0 && match[2] !== "0x0000000000000000000000000000000000000000";
|
|
1911
|
+
}, { message: "must be a canonical ERC-20 AssetId" }));
|
|
1901
1912
|
const SafeAddressSchema = addressSchema("safe address");
|
|
1902
1913
|
const ModuleAddressSchema = addressSchema("module address");
|
|
1903
1914
|
const TxHashSchema = bytes32BrandSchema("transaction hash");
|
|
@@ -3538,6 +3549,212 @@ Schema.Struct({
|
|
|
3538
3549
|
decimals: nonNegativeInteger("decimals"),
|
|
3539
3550
|
effectiveFrom: nonNegativeInteger("effectiveFrom")
|
|
3540
3551
|
});
|
|
3552
|
+
//#endregion
|
|
3553
|
+
//#region ../wire/src/payment-document-v2.ts
|
|
3554
|
+
const uint8 = Schema.Number.check(Schema.makeFilter((n) => Number.isInteger(n) && n >= 0 && n <= 255));
|
|
3555
|
+
const uint64 = Schema.Number.check(Schema.makeFilter((n) => Number.isSafeInteger(n) && n >= 0));
|
|
3556
|
+
const minorUnits$1 = Schema.String.check(Schema.makeFilter((s) => s.length <= 78 && s.trim() === s && /^(0|[1-9][0-9]*)$/.test(s) && BigInt(s) < 2n ** 256n));
|
|
3557
|
+
const reference = Schema.String.check(Schema.makeFilter((s) => s.length > 0));
|
|
3558
|
+
/** Public asset quantities use decimal major units. Registry validation resolves their scale. */
|
|
3559
|
+
const AssetAmountSchema = Schema.Struct({
|
|
3560
|
+
asset: AssetIdSchema,
|
|
3561
|
+
value: Schema.String.check(Schema.makeFilter((s) => s.length <= 512 && s.trim() === s && /^(0|[1-9][0-9]*)(\.[0-9]+)?$/.test(s))),
|
|
3562
|
+
currency: Schema.optional(Schema.Never),
|
|
3563
|
+
decimals: Schema.optional(Schema.Never)
|
|
3564
|
+
});
|
|
3565
|
+
const requestedMoney = Schema.Struct({
|
|
3566
|
+
...FinancialOpsMoney.fields,
|
|
3567
|
+
asset: Schema.optional(Schema.Never)
|
|
3568
|
+
}).check(Schema.makeFilter((amount) => amount.decimals <= 255 && amount.value.length <= 512 && amount.value.trim() === amount.value && (amount.value.split(".")[1]?.length ?? 0) <= amount.decimals, { message: "money must use exact decimal precision" }));
|
|
3569
|
+
Schema.Union([Schema.Struct({
|
|
3570
|
+
kind: Schema.Literal("money"),
|
|
3571
|
+
amount: requestedMoney
|
|
3572
|
+
}), Schema.Struct({
|
|
3573
|
+
kind: Schema.Literal("asset"),
|
|
3574
|
+
amount: AssetAmountSchema
|
|
3575
|
+
})]);
|
|
3576
|
+
Schema.Union([Schema.Struct({
|
|
3577
|
+
kind: Schema.Literal("money"),
|
|
3578
|
+
account: AccountIdSchema,
|
|
3579
|
+
currency: CurrencyCodeSchema,
|
|
3580
|
+
asset: Schema.optional(Schema.Never)
|
|
3581
|
+
}), Schema.Struct({
|
|
3582
|
+
kind: Schema.Literal("asset"),
|
|
3583
|
+
account: AccountIdSchema,
|
|
3584
|
+
asset: AssetIdSchema,
|
|
3585
|
+
currency: Schema.optional(Schema.Never)
|
|
3586
|
+
})]);
|
|
3587
|
+
Schema.Union([Schema.Struct({
|
|
3588
|
+
kind: Schema.Literal("money"),
|
|
3589
|
+
account: AccountIdSchema,
|
|
3590
|
+
amount: requestedMoney
|
|
3591
|
+
}), Schema.Struct({
|
|
3592
|
+
kind: Schema.Literal("asset"),
|
|
3593
|
+
account: AccountIdSchema,
|
|
3594
|
+
amount: AssetAmountSchema
|
|
3595
|
+
})]);
|
|
3596
|
+
/** Inactive fields are required and empty. They never carry an alternative denomination. */
|
|
3597
|
+
const AmountV2Schema = Schema.Union([Schema.Struct({
|
|
3598
|
+
denomination: Schema.Literal(0),
|
|
3599
|
+
currency: CurrencyCodeSchema,
|
|
3600
|
+
asset: Schema.Literal(""),
|
|
3601
|
+
minorUnits: minorUnits$1,
|
|
3602
|
+
decimals: uint8
|
|
3603
|
+
}), Schema.Struct({
|
|
3604
|
+
denomination: Schema.Literal(1),
|
|
3605
|
+
currency: Schema.Literal(""),
|
|
3606
|
+
asset: AssetIdSchema,
|
|
3607
|
+
minorUnits: minorUnits$1,
|
|
3608
|
+
decimals: uint8
|
|
3609
|
+
})]);
|
|
3610
|
+
const targetFields$1 = {
|
|
3611
|
+
target: reference,
|
|
3612
|
+
targetRevision: uint64
|
|
3613
|
+
};
|
|
3614
|
+
const payoutFields = {
|
|
3615
|
+
rail: Schema.Literals(["chainrails", "lightspark"]),
|
|
3616
|
+
purposeKind: Schema.Literals([
|
|
3617
|
+
0,
|
|
3618
|
+
1,
|
|
3619
|
+
2,
|
|
3620
|
+
3
|
|
3621
|
+
]),
|
|
3622
|
+
payerRef: reference,
|
|
3623
|
+
fundingAccount: reference,
|
|
3624
|
+
...targetFields$1,
|
|
3625
|
+
requestId: Schema.String,
|
|
3626
|
+
obligationHash: DocumentHashSchema,
|
|
3627
|
+
send: AmountV2Schema,
|
|
3628
|
+
receive: AmountV2Schema,
|
|
3629
|
+
feesHash: NonzeroDocumentHashSchema,
|
|
3630
|
+
releaseHash: NonzeroDocumentHashSchema
|
|
3631
|
+
};
|
|
3632
|
+
Schema.Struct(payoutFields);
|
|
3633
|
+
const InvoiceV2 = Schema.Struct({
|
|
3634
|
+
kind: Schema.Literal(1),
|
|
3635
|
+
invoiceNumber: Schema.String,
|
|
3636
|
+
payerRef: reference,
|
|
3637
|
+
payeeRef: reference,
|
|
3638
|
+
amount: AmountV2Schema,
|
|
3639
|
+
...targetFields$1,
|
|
3640
|
+
issuedAt: uint64,
|
|
3641
|
+
dueAt: uint64,
|
|
3642
|
+
lineItemsHash: DocumentHashSchema
|
|
3643
|
+
});
|
|
3644
|
+
const PayslipV2 = Schema.Struct({
|
|
3645
|
+
kind: Schema.Literal(2),
|
|
3646
|
+
employerRef: reference,
|
|
3647
|
+
employeeRef: reference,
|
|
3648
|
+
period: Schema.String,
|
|
3649
|
+
gross: AmountV2Schema,
|
|
3650
|
+
net: AmountV2Schema,
|
|
3651
|
+
...targetFields$1,
|
|
3652
|
+
issuedAt: uint64
|
|
3653
|
+
}).check(Schema.makeFilter((m) => m.gross.denomination === m.net.denomination && m.gross.currency === m.net.currency && m.gross.asset === m.net.asset && m.gross.decimals === m.net.decimals && BigInt(m.net.minorUnits) <= BigInt(m.gross.minorUnits), { message: "gross and net must use one denomination; net cannot exceed gross" }));
|
|
3654
|
+
const ReceiptV2 = Schema.Struct({
|
|
3655
|
+
kind: Schema.Literal(3),
|
|
3656
|
+
reference: Schema.String,
|
|
3657
|
+
amount: AmountV2Schema,
|
|
3658
|
+
...targetFields$1,
|
|
3659
|
+
obligationHash: DocumentHashSchema,
|
|
3660
|
+
dealHash: NonzeroDocumentHashSchema,
|
|
3661
|
+
paidAt: uint64,
|
|
3662
|
+
note: Schema.String
|
|
3663
|
+
});
|
|
3664
|
+
const PayoutV2 = Schema.Struct({
|
|
3665
|
+
kind: Schema.Literal(4),
|
|
3666
|
+
quoteId: reference,
|
|
3667
|
+
...payoutFields,
|
|
3668
|
+
expiresAt: uint64,
|
|
3669
|
+
issuedAt: uint64
|
|
3670
|
+
}).check(Schema.makeFilter((m) => m.issuedAt <= m.expiresAt, { message: "issuance must not follow the acceptance deadline" }));
|
|
3671
|
+
const common = {
|
|
3672
|
+
protocol: Schema.Literal("capxul.payment-document"),
|
|
3673
|
+
version: Schema.Literal(2),
|
|
3674
|
+
domain: Schema.Struct({
|
|
3675
|
+
name: Schema.Literal("CapxulPayments"),
|
|
3676
|
+
version: Schema.Literal("2"),
|
|
3677
|
+
chainId: ChainIdSchema,
|
|
3678
|
+
verifyingContract: Schema.Literal(PAYMENT_DOCUMENT_VERIFYING_CONTRACT)
|
|
3679
|
+
})
|
|
3680
|
+
};
|
|
3681
|
+
Schema.Union([
|
|
3682
|
+
Schema.Struct({
|
|
3683
|
+
...common,
|
|
3684
|
+
primaryType: Schema.Literal("Invoice"),
|
|
3685
|
+
message: InvoiceV2
|
|
3686
|
+
}),
|
|
3687
|
+
Schema.Struct({
|
|
3688
|
+
...common,
|
|
3689
|
+
primaryType: Schema.Literal("Payslip"),
|
|
3690
|
+
message: PayslipV2
|
|
3691
|
+
}),
|
|
3692
|
+
Schema.Struct({
|
|
3693
|
+
...common,
|
|
3694
|
+
primaryType: Schema.Literal("Receipt"),
|
|
3695
|
+
message: ReceiptV2
|
|
3696
|
+
}),
|
|
3697
|
+
Schema.Struct({
|
|
3698
|
+
...common,
|
|
3699
|
+
primaryType: Schema.Literal("Payout"),
|
|
3700
|
+
message: PayoutV2
|
|
3701
|
+
})
|
|
3702
|
+
]);
|
|
3703
|
+
const targetFields = [{
|
|
3704
|
+
name: "target",
|
|
3705
|
+
type: "string"
|
|
3706
|
+
}, {
|
|
3707
|
+
name: "targetRevision",
|
|
3708
|
+
type: "uint64"
|
|
3709
|
+
}];
|
|
3710
|
+
const termsFields = [
|
|
3711
|
+
{
|
|
3712
|
+
name: "rail",
|
|
3713
|
+
type: "string"
|
|
3714
|
+
},
|
|
3715
|
+
{
|
|
3716
|
+
name: "purposeKind",
|
|
3717
|
+
type: "uint16"
|
|
3718
|
+
},
|
|
3719
|
+
{
|
|
3720
|
+
name: "payerRef",
|
|
3721
|
+
type: "string"
|
|
3722
|
+
},
|
|
3723
|
+
{
|
|
3724
|
+
name: "fundingAccount",
|
|
3725
|
+
type: "string"
|
|
3726
|
+
},
|
|
3727
|
+
...targetFields,
|
|
3728
|
+
{
|
|
3729
|
+
name: "requestId",
|
|
3730
|
+
type: "string"
|
|
3731
|
+
},
|
|
3732
|
+
{
|
|
3733
|
+
name: "obligationHash",
|
|
3734
|
+
type: "bytes32"
|
|
3735
|
+
},
|
|
3736
|
+
{
|
|
3737
|
+
name: "send",
|
|
3738
|
+
type: "AmountV2"
|
|
3739
|
+
},
|
|
3740
|
+
{
|
|
3741
|
+
name: "receive",
|
|
3742
|
+
type: "AmountV2"
|
|
3743
|
+
},
|
|
3744
|
+
{
|
|
3745
|
+
name: "feesHash",
|
|
3746
|
+
type: "bytes32"
|
|
3747
|
+
},
|
|
3748
|
+
{
|
|
3749
|
+
name: "releaseHash",
|
|
3750
|
+
type: "bytes32"
|
|
3751
|
+
}
|
|
3752
|
+
];
|
|
3753
|
+
[...targetFields], [...targetFields], [...targetFields], [...termsFields];
|
|
3754
|
+
Schema.Array(Schema.Struct({
|
|
3755
|
+
code: Schema.Literals(["rail", "capxul"]),
|
|
3756
|
+
amount: AmountV2Schema
|
|
3757
|
+
}));
|
|
3541
3758
|
`
|
|
3542
3759
|
.capxul-doc{--ink:#1d1d1f;--muted:#6e6e73;--line:#e7e7ea;--accent:#0a7d4b;--bg:#fff;
|
|
3543
3760
|
font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Inter,system-ui,sans-serif;
|
|
@@ -3750,9 +3967,9 @@ function resolveFailureMode(error, contextFailureMode) {
|
|
|
3750
3967
|
}
|
|
3751
3968
|
//#endregion
|
|
3752
3969
|
//#region src/signer.ts
|
|
3753
|
-
const EVM_ADDRESS_HEX = /^0x[0-9a-fA-F]{40}$/;
|
|
3754
|
-
const ECDSA_SIGNATURE_HEX = /^0x[0-9a-fA-F]{130}$/;
|
|
3755
|
-
const SAFE_OP_DIGEST_HEX = /^0x[0-9a-fA-F]{64}$/;
|
|
3970
|
+
const EVM_ADDRESS_HEX$1 = /^0x[0-9a-fA-F]{40}$/;
|
|
3971
|
+
const ECDSA_SIGNATURE_HEX$1 = /^0x[0-9a-fA-F]{130}$/;
|
|
3972
|
+
const SAFE_OP_DIGEST_HEX$1 = /^0x[0-9a-fA-F]{64}$/;
|
|
3756
3973
|
/**
|
|
3757
3974
|
* The readiness store a signer reports when it runs no readiness cycle. It
|
|
3758
3975
|
* fails CLOSED: a node key signer or an injected wallet never claims `ready`,
|
|
@@ -3828,14 +4045,14 @@ function injectedWalletSigner(provider) {
|
|
|
3828
4045
|
const accounts = await provider.request({ method: "eth_requestAccounts" });
|
|
3829
4046
|
const first = Array.isArray(accounts) ? accounts[0] : void 0;
|
|
3830
4047
|
if (typeof first !== "string") throw new Error("injectedWalletSigner: wallet returned no accounts");
|
|
3831
|
-
if (!EVM_ADDRESS_HEX.test(first)) throw new Error("injectedWalletSigner: wallet returned invalid address format");
|
|
4048
|
+
if (!EVM_ADDRESS_HEX$1.test(first)) throw new Error("injectedWalletSigner: wallet returned invalid address format");
|
|
3832
4049
|
return toAddress(first);
|
|
3833
4050
|
};
|
|
3834
4051
|
return {
|
|
3835
4052
|
source: "injected-eip1193",
|
|
3836
4053
|
getAddress: resolveAddress,
|
|
3837
4054
|
async signUserOpHash(hash) {
|
|
3838
|
-
if (!SAFE_OP_DIGEST_HEX.test(hash)) throw new Error("injectedWalletSigner: SafeOp digest must be a 0x-prefixed 32-byte hex");
|
|
4055
|
+
if (!SAFE_OP_DIGEST_HEX$1.test(hash)) throw new Error("injectedWalletSigner: SafeOp digest must be a 0x-prefixed 32-byte hex");
|
|
3839
4056
|
const address = await resolveAddress();
|
|
3840
4057
|
let signature;
|
|
3841
4058
|
try {
|
|
@@ -3848,8 +4065,8 @@ function injectedWalletSigner(provider) {
|
|
|
3848
4065
|
throw new Error(`injectedWalletSigner: eth_sign failed; enable raw-hash signing for deployment (${detail})`, { cause });
|
|
3849
4066
|
}
|
|
3850
4067
|
if (typeof signature !== "string") throw new Error("injectedWalletSigner: wallet returned a non-string signature");
|
|
3851
|
-
if (!ECDSA_SIGNATURE_HEX.test(signature)) throw new Error("injectedWalletSigner: wallet returned invalid signature format");
|
|
3852
|
-
if ((await recoverRawDigestSigner({
|
|
4068
|
+
if (!ECDSA_SIGNATURE_HEX$1.test(signature)) throw new Error("injectedWalletSigner: wallet returned invalid signature format");
|
|
4069
|
+
if ((await recoverRawDigestSigner$1({
|
|
3853
4070
|
hash,
|
|
3854
4071
|
signature
|
|
3855
4072
|
})).toLowerCase() !== address.toLowerCase()) throw new Error("injectedWalletSigner: wallet signature did not recover the selected account for the raw SafeOp digest; use a raw-hash-capable wallet or @capxul/sdk/node localPrivateKeySigner for deployed flows");
|
|
@@ -3857,7 +4074,7 @@ function injectedWalletSigner(provider) {
|
|
|
3857
4074
|
}
|
|
3858
4075
|
};
|
|
3859
4076
|
}
|
|
3860
|
-
async function recoverRawDigestSigner(input) {
|
|
4077
|
+
async function recoverRawDigestSigner$1(input) {
|
|
3861
4078
|
try {
|
|
3862
4079
|
return toAddress(await recoverAddress(input));
|
|
3863
4080
|
} catch (cause) {
|
|
@@ -4358,6 +4575,7 @@ function verifiedOperationFields(attributes) {
|
|
|
4358
4575
|
/** Observe a public Effect that already uses the canonical CapxulError channel. */
|
|
4359
4576
|
async function runCapxulEffect(effect, controls, operation = CAPXUL_OPERATIONS.runtime.port, runPromise = Effect.runPromise) {
|
|
4360
4577
|
const invocation = readInvocationObservation(controls);
|
|
4578
|
+
let owningSpan;
|
|
4361
4579
|
const correlationId = safeEngineeringIdentifier(invocation?.context?.correlationId ?? controls?.correlation_id);
|
|
4362
4580
|
const context = invocation?.active === false ? void 0 : sanitizeObservationContext(invocation?.context);
|
|
4363
4581
|
const fields = {
|
|
@@ -4384,6 +4602,10 @@ async function runCapxulEffect(effect, controls, operation = CAPXUL_OPERATIONS.r
|
|
|
4384
4602
|
const failure = error instanceof CapxulError ? error : void 0;
|
|
4385
4603
|
const outcome = Exit.isSuccess(exit) ? "succeeded" : failure?.code === "CANCELLED" ? "cancelled" : failure !== void 0 && EXPECTED_OPERATION_OUTCOMES.has(failure.code) ? "refused" : "failed";
|
|
4386
4604
|
const span = yield* Effect.currentSpan.pipe(Effect.orDie);
|
|
4605
|
+
if (Exit.isFailure(exit) && invocation?.active === true) owningSpan = Object.freeze({
|
|
4606
|
+
traceId: span.traceId,
|
|
4607
|
+
spanId: span.spanId
|
|
4608
|
+
});
|
|
4387
4609
|
const completion = {
|
|
4388
4610
|
...fields,
|
|
4389
4611
|
...verifiedOperationFields(span.attributes),
|
|
@@ -4397,7 +4619,10 @@ async function runCapxulEffect(effect, controls, operation = CAPXUL_OPERATIONS.r
|
|
|
4397
4619
|
yield* (outcome === "failed" ? Effect.logError("capxul_sdk_operation") : Effect.logInfo("capxul_sdk_operation")).pipe(Effect.annotateLogs(completion), Effect.catchCause(() => Effect.void));
|
|
4398
4620
|
return yield* Exit.isSuccess(exit) ? Effect.succeed(exit.value) : Effect.failCause(exit.cause);
|
|
4399
4621
|
}).pipe(Effect.withSpan(operation)), controls));
|
|
4400
|
-
return !result.ok && invocation !== void 0 ? markFailureInvocationSnapshot(result,
|
|
4622
|
+
return !result.ok && invocation !== void 0 ? markFailureInvocationSnapshot(result, {
|
|
4623
|
+
...invocation,
|
|
4624
|
+
...owningSpan === void 0 ? {} : { owningSpan }
|
|
4625
|
+
}) : result;
|
|
4401
4626
|
}
|
|
4402
4627
|
/** Map machine-internal failure state onto the public SDK error vocabulary. */
|
|
4403
4628
|
function publicIdentityFailure(failure) {
|
|
@@ -4603,7 +4828,14 @@ function fromWei(rawBalance, decimals, currency) {
|
|
|
4603
4828
|
//#region src/surface/contacts.ts
|
|
4604
4829
|
function makeActorRelationshipMethods(deps) {
|
|
4605
4830
|
const domain = deps.actor.kind === "account" ? "account" : "org";
|
|
4606
|
-
const actor = deps.actor
|
|
4831
|
+
const actor = deps.actor.kind === "account" ? { kind: "account" } : {
|
|
4832
|
+
kind: "org",
|
|
4833
|
+
orgId: deps.actor.orgId
|
|
4834
|
+
};
|
|
4835
|
+
const observeContactScope = Effect.annotateCurrentSpan({
|
|
4836
|
+
scope_kind: actor.kind === "org" ? "organization" : "account",
|
|
4837
|
+
...actor.kind === "org" ? { verified_organization_id: actor.orgId } : {}
|
|
4838
|
+
});
|
|
4607
4839
|
const convexCall = deps.convexCall;
|
|
4608
4840
|
const profileGetOperation = domain === "account" ? CAPXUL_OPERATIONS.account.profile.get : CAPXUL_OPERATIONS.org.profile.get;
|
|
4609
4841
|
const profileDepositInstructionsOperation = domain === "account" ? CAPXUL_OPERATIONS.account.profile.depositInstructions : CAPXUL_OPERATIONS.org.profile.depositInstructions;
|
|
@@ -4616,14 +4848,14 @@ function makeActorRelationshipMethods(deps) {
|
|
|
4616
4848
|
return runPortEffect(mapReply(CAPXUL_OPERATIONS.addressBook.list, Effect.suspend(() => convexCall.query(fns.addressBookList, copyInvocationObservation(controls, {
|
|
4617
4849
|
actor,
|
|
4618
4850
|
...input?.includeHidden === void 0 ? {} : { includeHidden: input.includeHidden }
|
|
4619
|
-
}))), (entries) => entries.map(mapAddressBookEntry)), controls, CAPXUL_OPERATIONS.addressBook.list, deps.runPromise);
|
|
4851
|
+
}))), (entries) => entries.map(mapAddressBookEntry)).pipe(Effect.tap(() => observeContactScope)), controls, CAPXUL_OPERATIONS.addressBook.list, deps.runPromise);
|
|
4620
4852
|
},
|
|
4621
4853
|
get: (entryId, options) => {
|
|
4622
4854
|
const controls = deps.invocationControls?.(options) ?? options;
|
|
4623
4855
|
return runPortEffect(mapReply(CAPXUL_OPERATIONS.addressBook.get, Effect.suspend(() => convexCall.query(fns.addressBookGet, copyInvocationObservation(controls, {
|
|
4624
4856
|
actor,
|
|
4625
4857
|
partyId: entryId
|
|
4626
|
-
}))), (entry) => entry === null ? null : mapAddressBookEntry(entry)), controls, CAPXUL_OPERATIONS.addressBook.get, deps.runPromise);
|
|
4858
|
+
}))), (entry) => entry === null ? null : mapAddressBookEntry(entry)).pipe(Effect.tap(() => observeContactScope)), controls, CAPXUL_OPERATIONS.addressBook.get, deps.runPromise);
|
|
4627
4859
|
},
|
|
4628
4860
|
add: (input, options) => {
|
|
4629
4861
|
const controls = deps.invocationControls?.(options) ?? options;
|
|
@@ -4633,21 +4865,21 @@ function makeActorRelationshipMethods(deps) {
|
|
|
4633
4865
|
actor,
|
|
4634
4866
|
ref: ref.value,
|
|
4635
4867
|
...input.label === void 0 ? {} : { label: input.label }
|
|
4636
|
-
}))), mapAddressBookEntry), controls, CAPXUL_OPERATIONS.addressBook.add, deps.runPromise);
|
|
4868
|
+
}))), mapAddressBookEntry).pipe(Effect.tap(() => observeContactScope)), controls, CAPXUL_OPERATIONS.addressBook.add, deps.runPromise);
|
|
4637
4869
|
},
|
|
4638
4870
|
hide: (entryId, options) => {
|
|
4639
4871
|
const controls = deps.invocationControls?.(options) ?? options;
|
|
4640
4872
|
return runPortEffect(mapReply(CAPXUL_OPERATIONS.addressBook.hide, Effect.suspend(() => convexCall.mutation(fns.addressBookHide, copyInvocationObservation(controls, {
|
|
4641
4873
|
actor,
|
|
4642
4874
|
partyId: entryId
|
|
4643
|
-
}))), mapAddressBookEntry), controls, CAPXUL_OPERATIONS.addressBook.hide, deps.runPromise);
|
|
4875
|
+
}))), mapAddressBookEntry).pipe(Effect.tap(() => observeContactScope)), controls, CAPXUL_OPERATIONS.addressBook.hide, deps.runPromise);
|
|
4644
4876
|
},
|
|
4645
4877
|
unhide: (entryId, options) => {
|
|
4646
4878
|
const controls = deps.invocationControls?.(options) ?? options;
|
|
4647
4879
|
return runPortEffect(mapReply(CAPXUL_OPERATIONS.addressBook.unhide, Effect.suspend(() => convexCall.mutation(fns.addressBookUnhide, copyInvocationObservation(controls, {
|
|
4648
4880
|
actor,
|
|
4649
4881
|
partyId: entryId
|
|
4650
|
-
}))), mapAddressBookEntry), controls, CAPXUL_OPERATIONS.addressBook.unhide, deps.runPromise);
|
|
4882
|
+
}))), mapAddressBookEntry).pipe(Effect.tap(() => observeContactScope)), controls, CAPXUL_OPERATIONS.addressBook.unhide, deps.runPromise);
|
|
4651
4883
|
},
|
|
4652
4884
|
label: (input, options) => {
|
|
4653
4885
|
const controls = deps.invocationControls?.(options) ?? options;
|
|
@@ -4655,7 +4887,7 @@ function makeActorRelationshipMethods(deps) {
|
|
|
4655
4887
|
actor,
|
|
4656
4888
|
partyId: input.entryId,
|
|
4657
4889
|
label: input.label
|
|
4658
|
-
}))), mapAddressBookEntry), controls, CAPXUL_OPERATIONS.addressBook.label, deps.runPromise);
|
|
4890
|
+
}))), mapAddressBookEntry).pipe(Effect.tap(() => observeContactScope)), controls, CAPXUL_OPERATIONS.addressBook.label, deps.runPromise);
|
|
4659
4891
|
}
|
|
4660
4892
|
},
|
|
4661
4893
|
requests: {
|
|
@@ -5219,12 +5451,12 @@ const moneyExecutionContract = {
|
|
|
5219
5451
|
};
|
|
5220
5452
|
//#endregion
|
|
5221
5453
|
//#region package.json
|
|
5222
|
-
var version = "4.1
|
|
5454
|
+
var version = "4.2.0-rc.1";
|
|
5223
5455
|
//#endregion
|
|
5224
5456
|
//#region src/telemetry/exception-projection.ts
|
|
5225
5457
|
/** Fixed fallback for failures that have no safe message. */
|
|
5226
5458
|
const EXCEPTION_MESSAGE = "Capxul SDK operation failed";
|
|
5227
|
-
const SDK_VERSION = version;
|
|
5459
|
+
const SDK_VERSION$1 = version;
|
|
5228
5460
|
/** The one PostHog Error Tracking projection for every SDK exception emitter. */
|
|
5229
5461
|
function projectSdkException(input) {
|
|
5230
5462
|
const operation = normalizeExceptionOperation(input.operation);
|
|
@@ -5277,8 +5509,13 @@ function postHogFailureObservation(policy, fixedSnapshot) {
|
|
|
5277
5509
|
const safeFailure = sanitizeFailureObservation(failure);
|
|
5278
5510
|
const directContext = callStartSnapshot?.context ?? directSnapshot?.context;
|
|
5279
5511
|
const actorId = callStartSnapshot?.actorId;
|
|
5512
|
+
const span = callStartSnapshot?.owningSpan;
|
|
5280
5513
|
return [safeFailure, {
|
|
5281
5514
|
...postHogProperties(safeFailure, directContext),
|
|
5515
|
+
...span !== void 0 && /^(?!0+$)[0-9a-f]{32}$/u.test(span.traceId) && /^(?!0+$)[0-9a-f]{16}$/u.test(span.spanId) ? {
|
|
5516
|
+
trace_id: span.traceId,
|
|
5517
|
+
span_id: span.spanId
|
|
5518
|
+
} : {},
|
|
5282
5519
|
...actorId !== void 0 && /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u.test(actorId) ? {
|
|
5283
5520
|
actor_id: actorId,
|
|
5284
5521
|
actor_kind: "authenticated",
|
|
@@ -5366,7 +5603,7 @@ function observeSdkClient(client, adapter, snapshot) {
|
|
|
5366
5603
|
report(adapter, "exception", operation, cause, invocation);
|
|
5367
5604
|
throw cause;
|
|
5368
5605
|
}
|
|
5369
|
-
if (isPromiseLike(output)) return Promise.resolve(output).then((result) => processOutput(result, path, invocation), (cause) => {
|
|
5606
|
+
if (isPromiseLike$1(output)) return Promise.resolve(output).then((result) => processOutput(result, path, invocation), (cause) => {
|
|
5370
5607
|
report(adapter, "exception", operation, cause, invocation);
|
|
5371
5608
|
throw cause;
|
|
5372
5609
|
});
|
|
@@ -5440,7 +5677,7 @@ function report(adapter, kind, operation, cause, invocation, origin, authoritati
|
|
|
5440
5677
|
const evidence = failureEvidence(cause);
|
|
5441
5678
|
const failure = markFailureInvocationSnapshot({
|
|
5442
5679
|
exception: syntheticException(operationName, kindName, evidence.message, evidence.stack),
|
|
5443
|
-
sdkVersion: SDK_VERSION,
|
|
5680
|
+
sdkVersion: SDK_VERSION$1,
|
|
5444
5681
|
operation: operationName,
|
|
5445
5682
|
errorKind: kindName,
|
|
5446
5683
|
...context === void 0 ? {} : { context },
|
|
@@ -5449,7 +5686,8 @@ function report(adapter, kind, operation, cause, invocation, origin, authoritati
|
|
|
5449
5686
|
}, {
|
|
5450
5687
|
active: invocation.active,
|
|
5451
5688
|
...invocation.actorId === void 0 ? {} : { actorId: invocation.actorId },
|
|
5452
|
-
...context === void 0 ? {} : { context }
|
|
5689
|
+
...context === void 0 ? {} : { context },
|
|
5690
|
+
...invocation.owningSpan === void 0 ? {} : { owningSpan: invocation.owningSpan }
|
|
5453
5691
|
});
|
|
5454
5692
|
try {
|
|
5455
5693
|
ignoreDeliveryFailure(kind === "operation" ? adapter.captureOperationFailure(failure) : adapter.captureException(failure));
|
|
@@ -5477,7 +5715,7 @@ function resolveAdapterContext(adapter) {
|
|
|
5477
5715
|
}
|
|
5478
5716
|
}
|
|
5479
5717
|
function ignoreDeliveryFailure(delivery) {
|
|
5480
|
-
if (!isPromiseLike(delivery)) return;
|
|
5718
|
+
if (!isPromiseLike$1(delivery)) return;
|
|
5481
5719
|
try {
|
|
5482
5720
|
Promise.resolve(delivery).catch(() => void 0);
|
|
5483
5721
|
} catch {}
|
|
@@ -5576,8 +5814,6 @@ function evidenceText(value) {
|
|
|
5576
5814
|
if (typeof value !== "string" || value.length === 0) return void 0;
|
|
5577
5815
|
return redactSecrets(value).slice(0, MAX_EVIDENCE_TEXT);
|
|
5578
5816
|
}
|
|
5579
|
-
/** A field whose NAME says credential is masked whatever its value looks like. */
|
|
5580
|
-
const CREDENTIAL_FIELD_NAME = /(?:otp|pass(?:word|wd|phrase)?|token|secret|credential|api[_-]?key|private[_-]?key|authorization|cookie|session)/iu;
|
|
5581
5817
|
/** Copy a details object into a JSON-safe shape with every string masked. */
|
|
5582
5818
|
function evidenceValue(value, depth = 0) {
|
|
5583
5819
|
if (typeof value === "string") return evidenceText(value);
|
|
@@ -5588,7 +5824,7 @@ function evidenceValue(value, depth = 0) {
|
|
|
5588
5824
|
if (Array.isArray(value)) return value.slice(0, 50).map((item) => evidenceValue(item, depth + 1));
|
|
5589
5825
|
const copy = {};
|
|
5590
5826
|
for (const [key, nested] of Object.entries(value)) {
|
|
5591
|
-
if (
|
|
5827
|
+
if (isCredentialField(key)) {
|
|
5592
5828
|
copy[key] = "[REDACTED]";
|
|
5593
5829
|
continue;
|
|
5594
5830
|
}
|
|
@@ -5858,7 +6094,7 @@ function syntheticException(operation, kind, message = EXCEPTION_MESSAGE, stack)
|
|
|
5858
6094
|
error.stack = `${kind}: ${error.message}\n at CapxulSdkBoundary.${operation} (capxul-sdk-observation://boundary/${operation}:1:1)` + (stack === void 0 ? "" : `\nCaused by: ${stack}`);
|
|
5859
6095
|
return error;
|
|
5860
6096
|
}
|
|
5861
|
-
function isPromiseLike(value) {
|
|
6097
|
+
function isPromiseLike$1(value) {
|
|
5862
6098
|
return (typeof value === "object" || typeof value === "function") && value !== null && typeof value.then === "function";
|
|
5863
6099
|
}
|
|
5864
6100
|
function isPlainObject(value) {
|
|
@@ -6220,6 +6456,27 @@ function activityActorField(actor) {
|
|
|
6220
6456
|
} };
|
|
6221
6457
|
}
|
|
6222
6458
|
}
|
|
6459
|
+
/** Capture scope now; let the observed result boundary handle a malformed caller value. */
|
|
6460
|
+
function captureActivityInput(prepare) {
|
|
6461
|
+
try {
|
|
6462
|
+
return prepare();
|
|
6463
|
+
} catch (cause) {
|
|
6464
|
+
return Effect.die(cause);
|
|
6465
|
+
}
|
|
6466
|
+
}
|
|
6467
|
+
function observeActivityScope(actor) {
|
|
6468
|
+
const organizationId = actor?.kind === "organization" || actor?.kind === "org" ? safeEngineeringIdentifier(actor.orgId) : void 0;
|
|
6469
|
+
return Effect.annotateCurrentSpan({
|
|
6470
|
+
scope_kind: actor?.kind === "organization" || actor?.kind === "org" ? "organization" : "account",
|
|
6471
|
+
...organizationId === void 0 ? {} : { verified_organization_id: organizationId }
|
|
6472
|
+
});
|
|
6473
|
+
}
|
|
6474
|
+
function decodeActivityViewer(value) {
|
|
6475
|
+
const viewer = Option.getOrUndefined(Schema.decodeUnknownOption(ActorRefSchema)(value));
|
|
6476
|
+
if (viewer === void 0) return void 0;
|
|
6477
|
+
const id = viewer.kind === "organization" ? viewer.orgId : viewer.accountId;
|
|
6478
|
+
return safeEngineeringIdentifier(id) === void 0 ? void 0 : viewer;
|
|
6479
|
+
}
|
|
6223
6480
|
/** An absent actor and a `personal` actor both name the signed-in Account. */
|
|
6224
6481
|
function actorScopeKey(actor) {
|
|
6225
6482
|
return actor === void 0 || actor.kind === "personal" ? "personal" : `org:${actor.organizationId}`;
|
|
@@ -6414,8 +6671,9 @@ async function waitForPaymentSubmission(submission, signal, operation, onCancel
|
|
|
6414
6671
|
});
|
|
6415
6672
|
});
|
|
6416
6673
|
}
|
|
6417
|
-
function normalizeActivityDetail(
|
|
6418
|
-
if (
|
|
6674
|
+
function normalizeActivityDetail(response) {
|
|
6675
|
+
if (response === null) return null;
|
|
6676
|
+
const { viewer: _viewer, ...detail } = response;
|
|
6419
6677
|
if (detail.kind === "payment") {
|
|
6420
6678
|
if (detail.evidence === null) return detail;
|
|
6421
6679
|
return {
|
|
@@ -6692,39 +6950,58 @@ function makeFinancialOpsMethods(deps) {
|
|
|
6692
6950
|
activity: {
|
|
6693
6951
|
list: (params, options) => {
|
|
6694
6952
|
const controls = deps.invocationControls?.(options) ?? options;
|
|
6695
|
-
return runPortEffect(Effect.suspend(() => deps.convexCall.query(fns.activityList, copyInvocationObservation(controls, { input: {
|
|
6696
|
-
...
|
|
6953
|
+
return runPortEffect(captureActivityInput(() => Effect.succeed(activityActorField(params?.actor).actor)).pipe(Effect.flatMap((actor) => Effect.suspend(() => deps.convexCall.query(fns.activityList, copyInvocationObservation(controls, { input: {
|
|
6954
|
+
...actor === void 0 ? {} : { actor },
|
|
6697
6955
|
...params?.cursor === void 0 ? {} : { cursor: params.cursor },
|
|
6698
6956
|
...params?.limit === void 0 ? {} : { limit: params.limit },
|
|
6699
6957
|
...params?.range === void 0 ? {} : { range: params.range },
|
|
6700
6958
|
...params?.filter === void 0 ? {} : { filter: params.filter }
|
|
6701
|
-
} }))), controls, CAPXUL_OPERATIONS.activity.list, deps.runPromise);
|
|
6959
|
+
} }))).pipe(Effect.tap(() => observeActivityScope(actor))))), controls, CAPXUL_OPERATIONS.activity.list, deps.runPromise);
|
|
6702
6960
|
},
|
|
6703
6961
|
summary: (params, options) => {
|
|
6704
6962
|
const controls = deps.invocationControls?.(options) ?? options;
|
|
6705
|
-
return runPortEffect(Effect.suspend(() => deps.convexCall.query(fns.activitySummary, copyInvocationObservation(controls, { input: {
|
|
6706
|
-
...
|
|
6963
|
+
return runPortEffect(captureActivityInput(() => Effect.succeed(activityActorField(params?.actor).actor)).pipe(Effect.flatMap((actor) => Effect.suspend(() => deps.convexCall.query(fns.activitySummary, copyInvocationObservation(controls, { input: {
|
|
6964
|
+
...actor === void 0 ? {} : { actor },
|
|
6707
6965
|
...params?.window === void 0 ? {} : { window: params.window }
|
|
6708
|
-
} }))), controls, CAPXUL_OPERATIONS.activity.summary, deps.runPromise);
|
|
6966
|
+
} }))).pipe(Effect.tap(() => observeActivityScope(actor))))), controls, CAPXUL_OPERATIONS.activity.summary, deps.runPromise);
|
|
6709
6967
|
},
|
|
6710
6968
|
get: (reference, options) => {
|
|
6711
6969
|
const controls = deps.invocationControls?.(options) ?? options;
|
|
6712
|
-
const
|
|
6713
|
-
return runPortEffect(
|
|
6970
|
+
const requestedActor = actorReferenceToBackend(options?.actor);
|
|
6971
|
+
return runPortEffect(captureActivityInput(() => {
|
|
6972
|
+
const scope = requestedActor === void 0 ? void 0 : requestedActor.kind === "org" ? {
|
|
6973
|
+
kind: "org",
|
|
6974
|
+
orgId: requestedActor.orgId
|
|
6975
|
+
} : { kind: "account" };
|
|
6976
|
+
return Effect.succeed({
|
|
6977
|
+
actor: scope,
|
|
6978
|
+
referenceActor: reference.kind === "payment" ? activityActorField(reference.actor).actor : void 0
|
|
6979
|
+
});
|
|
6980
|
+
}).pipe(Effect.flatMap(({ actor, referenceActor }) => Effect.suspend(() => deps.convexCall.query(fns.activityGet, copyInvocationObservation(controls, {
|
|
6714
6981
|
input: {
|
|
6715
6982
|
kind: reference.kind,
|
|
6716
6983
|
id: reference.id,
|
|
6717
|
-
...
|
|
6984
|
+
...referenceActor === void 0 ? {} : { actor: referenceActor }
|
|
6718
6985
|
},
|
|
6719
6986
|
...actor === void 0 ? {} : { actor }
|
|
6720
|
-
}))).pipe(Effect.
|
|
6987
|
+
}))).pipe(Effect.flatMap((response) => {
|
|
6988
|
+
const detail = normalizeActivityDetail(response);
|
|
6989
|
+
if (response === null) return Effect.succeed(detail);
|
|
6990
|
+
const viewer = response.viewer === void 0 ? actor ?? referenceActor : decodeActivityViewer(response.viewer);
|
|
6991
|
+
return viewer === void 0 ? Effect.succeed(detail) : observeActivityScope(viewer).pipe(Effect.as(detail));
|
|
6992
|
+
})))), controls, CAPXUL_OPERATIONS.activity.get, deps.runPromise);
|
|
6721
6993
|
},
|
|
6722
6994
|
annotate: (input, options) => {
|
|
6723
6995
|
const controls = deps.invocationControls?.(options) ?? options;
|
|
6724
|
-
|
|
6725
|
-
|
|
6726
|
-
|
|
6727
|
-
|
|
6996
|
+
return runPortEffect(captureActivityInput(() => {
|
|
6997
|
+
const actor = activityActorField(input.actor).actor;
|
|
6998
|
+
if (input.actor !== void 0 && input.reference.kind === "payment" && input.reference.actor !== void 0 && !sameActor(input.actor, input.reference.actor)) return Effect.fail({ publicError: Errors.invalidInput("reference.actor", "must match the annotation actor") });
|
|
6999
|
+
return Effect.succeed({
|
|
7000
|
+
actor,
|
|
7001
|
+
referenceActor: input.reference.kind === "payment" ? activityActorField(input.reference.actor).actor : void 0
|
|
7002
|
+
});
|
|
7003
|
+
}).pipe(Effect.flatMap(({ actor, referenceActor }) => Effect.suspend(() => deps.convexCall.mutation(fns.activityAnnotate, copyInvocationObservation(controls, { input: {
|
|
7004
|
+
...actor === void 0 ? {} : { actor },
|
|
6728
7005
|
reference: input.reference.kind === "payment" && referenceActor !== void 0 ? {
|
|
6729
7006
|
kind: "payment",
|
|
6730
7007
|
id: input.reference.id,
|
|
@@ -6736,7 +7013,10 @@ function makeFinancialOpsMethods(deps) {
|
|
|
6736
7013
|
...input.counterpartyLabel === void 0 ? {} : { counterpartyLabel: input.counterpartyLabel },
|
|
6737
7014
|
...input.accountingCategory === void 0 ? {} : { accountingCategory: input.accountingCategory },
|
|
6738
7015
|
...input.memo === void 0 ? {} : { memo: input.memo }
|
|
6739
|
-
} })))
|
|
7016
|
+
} }))).pipe(Effect.tap((annotation) => {
|
|
7017
|
+
const scope = decodeActivityViewer(annotation.actor);
|
|
7018
|
+
return scope === void 0 ? Effect.void : observeActivityScope(scope);
|
|
7019
|
+
})))), controls, CAPXUL_OPERATIONS.activity.annotate, deps.runPromise);
|
|
6740
7020
|
}
|
|
6741
7021
|
},
|
|
6742
7022
|
offramp: {
|
|
@@ -7030,148 +7310,2974 @@ function mapOk(result, f) {
|
|
|
7030
7310
|
}
|
|
7031
7311
|
}
|
|
7032
7312
|
//#endregion
|
|
7033
|
-
//#region src/
|
|
7034
|
-
|
|
7035
|
-
function bootstrapErrorFromCapxul(kind, error) {
|
|
7036
|
-
return new BootstrapError({
|
|
7037
|
-
operation: "resolve",
|
|
7038
|
-
kind,
|
|
7039
|
-
publicCode: error.code,
|
|
7040
|
-
cause: error,
|
|
7041
|
-
...kind === "notAuthenticated" || error.details === void 0 ? {} : { details: error.details }
|
|
7042
|
-
});
|
|
7043
|
-
}
|
|
7044
|
-
var BootstrapPortTag = class extends Context.Service()("@capxul/sdk/ports/BootstrapPort") {};
|
|
7045
|
-
//#endregion
|
|
7046
|
-
//#region src/ports/convex-call.ts
|
|
7047
|
-
var ConvexCallError = class extends Data.TaggedError("ConvexCallError") {};
|
|
7048
|
-
function convexCallErrorFromCapxul(operation, error, transport) {
|
|
7049
|
-
return new ConvexCallError({
|
|
7050
|
-
operation,
|
|
7051
|
-
publicCode: error.code,
|
|
7052
|
-
publicError: error,
|
|
7053
|
-
cause: error,
|
|
7054
|
-
...error.details === void 0 ? {} : { details: error.details },
|
|
7055
|
-
...transport === void 0 ? {} : { transport }
|
|
7056
|
-
});
|
|
7057
|
-
}
|
|
7058
|
-
var ConvexCallPortTag = class extends Context.Service()("@capxul/sdk/ports/ConvexCallPort") {};
|
|
7059
|
-
//#endregion
|
|
7060
|
-
//#region src/ports/identity.ts
|
|
7061
|
-
var IdentityError = class extends Data.TaggedError("IdentityError") {};
|
|
7062
|
-
function identityErrorFromCapxul(operation, error, cause = error) {
|
|
7063
|
-
return new IdentityError({
|
|
7064
|
-
operation,
|
|
7065
|
-
publicCode: error.code,
|
|
7066
|
-
publicError: error,
|
|
7067
|
-
cause,
|
|
7068
|
-
...error.details === void 0 ? {} : { details: error.details }
|
|
7069
|
-
});
|
|
7070
|
-
}
|
|
7071
|
-
var IdentityPortTag = class extends Context.Service()("@capxul/sdk/ports/IdentityPort") {};
|
|
7072
|
-
//#endregion
|
|
7073
|
-
//#region src/adapters/_shared/wire.ts
|
|
7313
|
+
//#region src/dev-signer.ts
|
|
7314
|
+
const SESSION_KEY = "capxul.session";
|
|
7074
7315
|
/**
|
|
7075
|
-
*
|
|
7076
|
-
*
|
|
7077
|
-
*
|
|
7078
|
-
*
|
|
7079
|
-
*
|
|
7080
|
-
|
|
7081
|
-
|
|
7316
|
+
* Chains a `local-private-key` signer may sign on. Base Sepolia only, and this
|
|
7317
|
+
* list does not grow without an ADR: the keys are deterministic throwaways
|
|
7318
|
+
* derived from a shared seed (see `deriveDevPrivateKey`), so anyone holding
|
|
7319
|
+
* the seed holds every account. A dev key on a value-bearing chain is a
|
|
7320
|
+
* custody incident, not a config mistake.
|
|
7321
|
+
*/
|
|
7322
|
+
const DEV_KEY_ALLOWED_CHAIN_IDS = [BASE_SEPOLIA_CHAIN_ID];
|
|
7323
|
+
/**
|
|
7324
|
+
* Testnet fence for the dev-key signing lane (#1149, folds #1065; ADR-0018 P9
|
|
7325
|
+
* human/Openfort vs agent/dev-key split). Call it wherever a signer first
|
|
7326
|
+
* meets a resolved chain; it refuses before the signer can be used.
|
|
7082
7327
|
*
|
|
7083
|
-
*
|
|
7084
|
-
*
|
|
7085
|
-
* validation (e.g. "must not de-brand a placeholder address"), the change
|
|
7086
|
-
* lands here once instead of grep-and-replace across every adapter.
|
|
7328
|
+
* Only `local-private-key` signers are fenced — Openfort-embedded and injected
|
|
7329
|
+
* wallets carry their own custody and are the sanctioned human paths.
|
|
7087
7330
|
*/
|
|
7331
|
+
function assertDevKeySignerIsTestnetOnly(signer, chainId) {
|
|
7332
|
+
if (signer?.source !== "local-private-key") return {
|
|
7333
|
+
ok: true,
|
|
7334
|
+
value: void 0
|
|
7335
|
+
};
|
|
7336
|
+
if (DEV_KEY_ALLOWED_CHAIN_IDS.includes(chainId)) return {
|
|
7337
|
+
ok: true,
|
|
7338
|
+
value: void 0
|
|
7339
|
+
};
|
|
7340
|
+
return {
|
|
7341
|
+
ok: false,
|
|
7342
|
+
error: Errors.invalidInput("signer", `dev-key signer is testnet-only: chain ${chainId} is not allowed (expected ${DEV_KEY_ALLOWED_CHAIN_IDS.join(", ")})`)
|
|
7343
|
+
};
|
|
7344
|
+
}
|
|
7345
|
+
/** Deterministic dev private key for an email under a seed. Exported for probes. */
|
|
7346
|
+
function deriveDevPrivateKey(seed, email) {
|
|
7347
|
+
return keccak256(stringToHex(seed + normalizeBindingEmail(email)));
|
|
7348
|
+
}
|
|
7349
|
+
function readSessionEmail(storage) {
|
|
7350
|
+
const resolved = storage ?? globalThis.window?.localStorage;
|
|
7351
|
+
if (resolved === void 0) throw new Error("devPrivateKeySigner: no browser localStorage available and no explicit email supplied");
|
|
7352
|
+
const raw = resolved.getItem(SESSION_KEY);
|
|
7353
|
+
if (raw === null) throw new Error("devPrivateKeySigner: no cached session yet — sign in before the account lane uses the signer");
|
|
7354
|
+
let email;
|
|
7355
|
+
try {
|
|
7356
|
+
email = JSON.parse(raw).email;
|
|
7357
|
+
} catch {
|
|
7358
|
+
throw new Error("devPrivateKeySigner: cached session is not valid JSON");
|
|
7359
|
+
}
|
|
7360
|
+
if (typeof email !== "string" || email.length === 0) throw new Error("devPrivateKeySigner: cached session has no email");
|
|
7361
|
+
return email;
|
|
7362
|
+
}
|
|
7088
7363
|
/**
|
|
7089
|
-
*
|
|
7090
|
-
* `
|
|
7364
|
+
* Browser-safe dev signer. Lazy: the email (and so the key) is resolved at
|
|
7365
|
+
* each `getAddress()` / `signUserOpHash()` from the cached session, so the
|
|
7366
|
+
* same signer instance follows whichever user is signed in.
|
|
7091
7367
|
*/
|
|
7092
|
-
function
|
|
7093
|
-
|
|
7368
|
+
function devPrivateKeySigner(input) {
|
|
7369
|
+
if (input.seed.trim().length === 0) throw new Error("devPrivateKeySigner: seed must be non-empty");
|
|
7370
|
+
const accounts = /* @__PURE__ */ new Map();
|
|
7371
|
+
const resolveAccount = () => {
|
|
7372
|
+
const email = normalizeBindingEmail(input.email ?? readSessionEmail(input.storage));
|
|
7373
|
+
const cached = accounts.get(email);
|
|
7374
|
+
if (cached !== void 0) return cached;
|
|
7375
|
+
const account = privateKeyToAccount(deriveDevPrivateKey(input.seed, email));
|
|
7376
|
+
accounts.set(email, account);
|
|
7377
|
+
return account;
|
|
7378
|
+
};
|
|
7379
|
+
return {
|
|
7380
|
+
source: "local-private-key",
|
|
7381
|
+
async getAddress() {
|
|
7382
|
+
return toAddress(resolveAccount().address);
|
|
7383
|
+
},
|
|
7384
|
+
async signUserOpHash(hash) {
|
|
7385
|
+
return resolveAccount().sign({ hash });
|
|
7386
|
+
}
|
|
7387
|
+
};
|
|
7094
7388
|
}
|
|
7095
7389
|
//#endregion
|
|
7096
|
-
//#region src/
|
|
7097
|
-
|
|
7098
|
-
|
|
7099
|
-
|
|
7390
|
+
//#region src/ports/embedded-wallet.ts
|
|
7391
|
+
function openfortEmbeddedWalletPort(input) {
|
|
7392
|
+
return {
|
|
7393
|
+
async getAddress() {
|
|
7394
|
+
if (input.ensureReady !== void 0) await input.ensureReady();
|
|
7395
|
+
return (await input.embeddedWallet.get()).address;
|
|
7396
|
+
},
|
|
7397
|
+
async signRawDigest(hash) {
|
|
7398
|
+
if (input.ensureReady !== void 0) await input.ensureReady();
|
|
7399
|
+
return await input.embeddedWallet.signMessage(hash, {
|
|
7400
|
+
hashMessage: false,
|
|
7401
|
+
arrayifyMessage: false
|
|
7402
|
+
});
|
|
7403
|
+
}
|
|
7404
|
+
};
|
|
7100
7405
|
}
|
|
7101
7406
|
//#endregion
|
|
7102
|
-
//#region src/
|
|
7103
|
-
const
|
|
7104
|
-
|
|
7105
|
-
|
|
7106
|
-
|
|
7107
|
-
function
|
|
7108
|
-
return
|
|
7109
|
-
|
|
7110
|
-
|
|
7111
|
-
|
|
7112
|
-
|
|
7113
|
-
|
|
7114
|
-
|
|
7115
|
-
|
|
7116
|
-
|
|
7117
|
-
|
|
7118
|
-
|
|
7119
|
-
|
|
7120
|
-
|
|
7121
|
-
|
|
7122
|
-
|
|
7123
|
-
|
|
7124
|
-
|
|
7125
|
-
|
|
7126
|
-
|
|
7127
|
-
|
|
7128
|
-
|
|
7129
|
-
|
|
7130
|
-
|
|
7131
|
-
|
|
7132
|
-
|
|
7133
|
-
|
|
7134
|
-
|
|
7135
|
-
}
|
|
7136
|
-
})
|
|
7137
|
-
|
|
7138
|
-
|
|
7139
|
-
|
|
7140
|
-
|
|
7407
|
+
//#region src/openfort-embedded-signer.ts
|
|
7408
|
+
const EVM_ADDRESS_HEX = /^0x[0-9a-fA-F]{40}$/;
|
|
7409
|
+
const ECDSA_SIGNATURE_HEX = /^0x[0-9a-fA-F]{130}$/;
|
|
7410
|
+
const SAFE_OP_DIGEST_HEX = /^0x[0-9a-fA-F]{64}$/;
|
|
7411
|
+
/** Browser helper: wrap an initialized Openfort `embeddedWallet` API. */
|
|
7412
|
+
function openfortEmbeddedSignerFromWallet(input) {
|
|
7413
|
+
return openfortEmbeddedSigner({ wallet: openfortEmbeddedWalletPort({
|
|
7414
|
+
embeddedWallet: input.embeddedWallet,
|
|
7415
|
+
...input.ensureWalletReady === void 0 ? {} : { ensureReady: input.ensureWalletReady }
|
|
7416
|
+
}) });
|
|
7417
|
+
}
|
|
7418
|
+
function openfortEmbeddedSigner(input) {
|
|
7419
|
+
let cachedAddress = null;
|
|
7420
|
+
let addressInFlight = null;
|
|
7421
|
+
let cacheEpoch = 0;
|
|
7422
|
+
const resetAddressCache = () => {
|
|
7423
|
+
cacheEpoch += 1;
|
|
7424
|
+
cachedAddress = null;
|
|
7425
|
+
addressInFlight = null;
|
|
7426
|
+
};
|
|
7427
|
+
const resolveAddress = async () => {
|
|
7428
|
+
if (cachedAddress !== null) return cachedAddress;
|
|
7429
|
+
if (addressInFlight !== null) return addressInFlight;
|
|
7430
|
+
const epoch = cacheEpoch;
|
|
7431
|
+
addressInFlight = (async () => {
|
|
7432
|
+
try {
|
|
7433
|
+
const raw = await input.wallet.getAddress();
|
|
7434
|
+
if (!EVM_ADDRESS_HEX.test(raw)) throw new Error("openfortEmbeddedSigner: embedded wallet returned invalid address format");
|
|
7435
|
+
const address = toAddress(raw);
|
|
7436
|
+
if (epoch === cacheEpoch) cachedAddress = address;
|
|
7437
|
+
return address;
|
|
7438
|
+
} finally {
|
|
7439
|
+
if (epoch === cacheEpoch) addressInFlight = null;
|
|
7440
|
+
}
|
|
7441
|
+
})();
|
|
7442
|
+
return addressInFlight;
|
|
7443
|
+
};
|
|
7444
|
+
return {
|
|
7445
|
+
source: "openfort-embedded",
|
|
7446
|
+
getAddress: resolveAddress,
|
|
7447
|
+
resetAddressCache,
|
|
7448
|
+
async signUserOpHash(hash) {
|
|
7449
|
+
if (!SAFE_OP_DIGEST_HEX.test(hash)) throw new Error("openfortEmbeddedSigner: SafeOp digest must be a 0x-prefixed 32-byte hex");
|
|
7450
|
+
const address = await resolveAddress();
|
|
7451
|
+
let signature;
|
|
7452
|
+
try {
|
|
7453
|
+
signature = await input.wallet.signRawDigest(hash);
|
|
7454
|
+
} catch (cause) {
|
|
7455
|
+
const detail = cause instanceof Error ? cause.message : String(cause);
|
|
7456
|
+
throw new Error(`openfortEmbeddedSigner: raw digest signing failed; ensure the embedded wallet is configured (${detail})`, { cause });
|
|
7457
|
+
}
|
|
7458
|
+
if (!ECDSA_SIGNATURE_HEX.test(signature)) throw new Error("openfortEmbeddedSigner: embedded wallet returned invalid signature format");
|
|
7459
|
+
if ((await recoverRawDigestSigner({
|
|
7460
|
+
hash,
|
|
7461
|
+
signature
|
|
7462
|
+
})).toLowerCase() !== address.toLowerCase()) throw new Error("openfortEmbeddedSigner: signature did not recover the embedded wallet address for the raw SafeOp digest");
|
|
7463
|
+
return signature;
|
|
7464
|
+
}
|
|
7465
|
+
};
|
|
7141
7466
|
}
|
|
7142
|
-
|
|
7143
|
-
|
|
7144
|
-
|
|
7467
|
+
/**
|
|
7468
|
+
* Canonical name for the embedded-wallet `CapxulSigner` constructor
|
|
7469
|
+
* (backend-orchestrated-deploy.md). The embedded-wallet (passkey / Openfort)
|
|
7470
|
+
* member of the named constructor trio `localPrivateKeySigner` /
|
|
7471
|
+
* `injectedWalletSigner` / `embeddedSigner`. Takes the provider-agnostic
|
|
7472
|
+
* `OpenfortEmbeddedWalletPort` (getAddress + signRawDigest); the Openfort-API
|
|
7473
|
+
* convenience wrapper is `openfortEmbeddedSignerFromWallet`.
|
|
7474
|
+
*/
|
|
7475
|
+
const embeddedSigner = openfortEmbeddedSigner;
|
|
7476
|
+
async function recoverRawDigestSigner(input) {
|
|
7477
|
+
try {
|
|
7478
|
+
return toAddress(await recoverAddress(input));
|
|
7479
|
+
} catch (cause) {
|
|
7480
|
+
const detail = cause instanceof Error ? cause.message : String(cause);
|
|
7481
|
+
throw new Error(`openfortEmbeddedSigner: could not verify raw SafeOp digest signature (${detail})`, { cause });
|
|
7482
|
+
}
|
|
7145
7483
|
}
|
|
7146
7484
|
//#endregion
|
|
7147
|
-
//#region src/
|
|
7148
|
-
|
|
7149
|
-
|
|
7150
|
-
|
|
7151
|
-
|
|
7152
|
-
|
|
7153
|
-
|
|
7154
|
-
|
|
7155
|
-
|
|
7156
|
-
|
|
7157
|
-
|
|
7158
|
-
|
|
7485
|
+
//#region ../observability/src/engineering.ts
|
|
7486
|
+
const ENGINEERING_CAPXUL_ENVS = [
|
|
7487
|
+
"development",
|
|
7488
|
+
"staging",
|
|
7489
|
+
"production",
|
|
7490
|
+
"local"
|
|
7491
|
+
];
|
|
7492
|
+
const traceHeaderFilter = (_name) => true;
|
|
7493
|
+
const credentialHeaderPattern = Object.assign(/./u, { test: (name) => isCredentialField(name, "header") });
|
|
7494
|
+
const postHogOtlpEndpoints = (host) => {
|
|
7495
|
+
const base = host.replace(/\/+$/, "");
|
|
7496
|
+
return {
|
|
7497
|
+
logs: `${base}/i/v1/logs`,
|
|
7498
|
+
traces: `${base}/i/v1/traces`
|
|
7499
|
+
};
|
|
7500
|
+
};
|
|
7501
|
+
const ENGINEERING_CAPXUL_ENV_SET = new Set(ENGINEERING_CAPXUL_ENVS);
|
|
7502
|
+
const ENGINEERING_PRODUCERS = /* @__PURE__ */ new Set(["browser", "server"]);
|
|
7503
|
+
const PUBLIC_POSTHOG_AUTHORIZATION = /^Bearer phc_[A-Za-z0-9_-]{1,191}$/u;
|
|
7504
|
+
const SAFE_RESOURCE_VALUE = /^[A-Za-z0-9][A-Za-z0-9._+@/-]{0,127}$/u;
|
|
7505
|
+
const validateEngineeringTelemetryConfig = (config) => {
|
|
7506
|
+
let url;
|
|
7507
|
+
try {
|
|
7508
|
+
url = new URL(config.host);
|
|
7509
|
+
} catch {
|
|
7510
|
+
throw new TypeError("engineering telemetry host must be an absolute HTTPS URL");
|
|
7511
|
+
}
|
|
7512
|
+
if (url.protocol !== "https:" || url.username.length > 0 || url.password.length > 0 || url.pathname !== "/" || url.search.length > 0 || url.hash.length > 0 || !(url.hostname === "posthog.com" || url.hostname.endsWith(".posthog.com"))) throw new TypeError("engineering telemetry host must be a credential-free HTTPS origin");
|
|
7513
|
+
if (!ENGINEERING_CAPXUL_ENV_SET.has(config.capxulEnv)) throw new TypeError("engineering telemetry capxulEnv is not canonical");
|
|
7514
|
+
if (!ENGINEERING_PRODUCERS.has(config.producer)) throw new TypeError("engineering telemetry producer is not canonical");
|
|
7515
|
+
if (!SAFE_RESOURCE_VALUE.test(config.sdkVersion)) throw new TypeError("engineering telemetry sdkVersion must be a bounded safe value");
|
|
7516
|
+
if (config.serviceName !== void 0 && !SAFE_RESOURCE_VALUE.test(config.serviceName)) throw new TypeError("engineering telemetry serviceName must be a bounded safe value");
|
|
7517
|
+
const headerEntries = Object.entries(config.headers);
|
|
7518
|
+
if (headerEntries.length !== 1 || headerEntries[0]?.[0].toLowerCase() !== "authorization" || !PUBLIC_POSTHOG_AUTHORIZATION.test(headerEntries[0]?.[1] ?? "")) throw new TypeError("engineering telemetry headers must contain exactly one public PostHog authorization token");
|
|
7519
|
+
return {
|
|
7520
|
+
...config,
|
|
7521
|
+
host: url.origin,
|
|
7522
|
+
headers: Object.freeze({ authorization: headerEntries[0][1] })
|
|
7523
|
+
};
|
|
7524
|
+
};
|
|
7525
|
+
const failureText = (failure, key, limit, source) => {
|
|
7526
|
+
if (failure === null || typeof failure !== "object") return void 0;
|
|
7527
|
+
try {
|
|
7528
|
+
let owner = failure;
|
|
7529
|
+
let descriptor;
|
|
7530
|
+
for (let depth = 0; owner !== null && depth < 4; depth += 1) {
|
|
7531
|
+
descriptor = Object.getOwnPropertyDescriptor(owner, key);
|
|
7532
|
+
if (descriptor !== void 0 || key !== "name") break;
|
|
7533
|
+
owner = Object.getPrototypeOf(owner);
|
|
7534
|
+
}
|
|
7535
|
+
const value = descriptor?.value;
|
|
7536
|
+
return typeof value === "string" || key === "code" && typeof value === "number" ? redactSecrets(String(value), source).slice(0, limit) : void 0;
|
|
7537
|
+
} catch {
|
|
7538
|
+
return;
|
|
7539
|
+
}
|
|
7540
|
+
};
|
|
7541
|
+
const exportFailure = (failure, source) => {
|
|
7542
|
+
const message = failureText(failure, "message", 2048, source) ?? (typeof failure === "string" ? redactSecrets(failure, source).slice(0, 2048) : "Engineering operation failed");
|
|
7543
|
+
const code = failureText(failure, "code", 128);
|
|
7544
|
+
const error = new Error((code === void 0 ? message : `${message} [code=${code}]`).slice(0, 2048));
|
|
7545
|
+
error.name = failureText(failure, "_tag", 128) ?? failureText(failure, "name", 128) ?? "Error";
|
|
7546
|
+
error.stack = failureText(failure, "stack", 8192, source) ?? `${error.name}: ${error.message}`;
|
|
7547
|
+
return error;
|
|
7548
|
+
};
|
|
7549
|
+
const exportFailureExit = (cause, source) => Exit.failCause(Cause.fromReasons(cause.reasons.filter((reason) => !Cause.isInterruptReason(reason)).slice(0, 8).map((reason) => Cause.isFailReason(reason) ? Cause.makeFailReason(exportFailure(reason.error, source)) : Cause.makeDieReason(exportFailure(reason.defect, source)))));
|
|
7550
|
+
const BROWSER_KEEPALIVE_BYTES = 65536;
|
|
7551
|
+
let unfinishedBrowserExportBytes = 0;
|
|
7552
|
+
const hasBrowserDocument = () => typeof window !== "undefined" && typeof document !== "undefined";
|
|
7553
|
+
const browserExportClient = (client) => HttpClient.transform(client, (response, request) => Effect.acquireUseRelease(Effect.sync(() => {
|
|
7554
|
+
const bytes = "contentLength" in request.body ? request.body.contentLength : void 0;
|
|
7555
|
+
if (request.body._tag === "Stream" || request.body._tag === "FormData" || bytes === void 0 || !Number.isSafeInteger(bytes) || bytes < 0 || unfinishedBrowserExportBytes + bytes > BROWSER_KEEPALIVE_BYTES) return;
|
|
7556
|
+
unfinishedBrowserExportBytes += bytes;
|
|
7557
|
+
return bytes;
|
|
7558
|
+
}), (bytes) => response.pipe(Effect.provideService(FetchHttpClient.RequestInit, { keepalive: bytes !== void 0 }), Effect.tap((result) => bytes === void 0 ? Effect.void : Stream.runDrain(result.stream).pipe(Effect.catch((error) => error.reason._tag === "EmptyBodyError" ? Effect.void : Effect.fail(error))))), (bytes) => Effect.sync(() => {
|
|
7559
|
+
if (bytes !== void 0) unfinishedBrowserExportBytes -= bytes;
|
|
7560
|
+
})));
|
|
7561
|
+
const spanUpstream = (span) => {
|
|
7562
|
+
let current = span;
|
|
7563
|
+
while (current?._tag === "Span") {
|
|
7564
|
+
const upstream = current.attributes.get("chain.upstream");
|
|
7565
|
+
if (upstream === "alchemy" || upstream === "infura") return upstream;
|
|
7566
|
+
current = current.parent._tag === "Some" ? current.parent.value : void 0;
|
|
7567
|
+
}
|
|
7568
|
+
};
|
|
7569
|
+
/** Preserve domain exits while preventing the OTLP serializer from seeing raw causes. */
|
|
7570
|
+
const makeLeakSafeEngineeringTracer = (delegate) => Tracer.make({
|
|
7571
|
+
span(options) {
|
|
7572
|
+
const span = delegate.span(options);
|
|
7573
|
+
const wrapped = Object.create(span);
|
|
7574
|
+
Object.defineProperty(wrapped, "attribute", { value: (name, value) => {
|
|
7575
|
+
const source = spanUpstream(span);
|
|
7576
|
+
const header = /^http\.(?:request|response)\.header\.(.+)$/u.exec(name)?.[1];
|
|
7577
|
+
if (header !== void 0 && isCredentialField(header, "header")) span.attribute(name, "[REDACTED]");
|
|
7578
|
+
else if (typeof value === "string" && name === "url.query") span.attribute(name, redactUrlSecrets(`?${value}`).slice(1, 2049));
|
|
7579
|
+
else if (typeof value === "string" && name === "url.path") {
|
|
7580
|
+
const fullUrl = span.attributes.get("url.full");
|
|
7581
|
+
const path = typeof fullUrl === "string" ? /^https?:\/\/[^/]+([^?#]*)/u.exec(fullUrl)?.[1] ?? value : value;
|
|
7582
|
+
span.attribute(name, redactUrlSecrets(path, source).slice(0, 2048));
|
|
7583
|
+
} else if (typeof value === "string" && (name === "url.full" || header === "location" || header === "referer" || header === "referrer")) span.attribute(name, redactUrlSecrets(value, source).slice(0, 2048));
|
|
7584
|
+
else if (header !== void 0 && typeof value === "string") span.attribute(name, redactSecrets(value, source).slice(0, 2048));
|
|
7585
|
+
else span.attribute(name, value);
|
|
7586
|
+
} });
|
|
7587
|
+
Object.defineProperty(wrapped, "end", {
|
|
7588
|
+
configurable: false,
|
|
7589
|
+
enumerable: false,
|
|
7590
|
+
value: (endTime, exit) => {
|
|
7591
|
+
if (Exit.isFailure(exit) && !Cause.hasInterruptsOnly(exit.cause)) {
|
|
7592
|
+
const first = exit.cause.reasons.find((reason) => !Cause.isInterruptReason(reason));
|
|
7593
|
+
const code = failureText(first === void 0 ? void 0 : Cause.isFailReason(first) ? first.error : first.defect, "code", 128);
|
|
7594
|
+
if (code !== void 0) span.attribute("error.code", code);
|
|
7595
|
+
span.end(endTime, exportFailureExit(exit.cause, spanUpstream(span)));
|
|
7596
|
+
} else span.end(endTime, exit);
|
|
7597
|
+
},
|
|
7598
|
+
writable: false
|
|
7599
|
+
});
|
|
7600
|
+
return wrapped;
|
|
7601
|
+
},
|
|
7602
|
+
...delegate.context === void 0 ? {} : { context: delegate.context.bind(delegate) }
|
|
7603
|
+
});
|
|
7604
|
+
/** Shared browser/server OTLP layer. OtlpLogger merges with incumbent loggers once. */
|
|
7605
|
+
const makeEngineeringTelemetryLayer = (config) => {
|
|
7606
|
+
const validated = validateEngineeringTelemetryConfig(config);
|
|
7607
|
+
const endpoints = postHogOtlpEndpoints(validated.host);
|
|
7608
|
+
const resource = {
|
|
7609
|
+
serviceName: validated.serviceName ?? "capxul-sdk",
|
|
7610
|
+
serviceVersion: validated.sdkVersion,
|
|
7611
|
+
attributes: {
|
|
7612
|
+
capxul_env: validated.capxulEnv,
|
|
7613
|
+
producer: validated.producer,
|
|
7614
|
+
sdk_version: validated.sdkVersion
|
|
7615
|
+
}
|
|
7616
|
+
};
|
|
7617
|
+
const tracing = Layer.effect(Tracer.Tracer, OtlpTracer.make({
|
|
7618
|
+
url: endpoints.traces,
|
|
7619
|
+
headers: validated.headers,
|
|
7620
|
+
resource
|
|
7621
|
+
}).pipe(Effect.map(makeLeakSafeEngineeringTracer))).pipe(Layer.provideMerge(OtlpExporter.layerFlusher));
|
|
7622
|
+
const logging = OtlpLogger.layer({
|
|
7623
|
+
url: endpoints.logs,
|
|
7624
|
+
headers: validated.headers,
|
|
7625
|
+
resource,
|
|
7626
|
+
mergeWithExisting: true
|
|
7159
7627
|
});
|
|
7628
|
+
const headerPolicy = Layer.merge(Layer.succeed(HttpClient.TracerHeaderFilter, traceHeaderFilter), Layer.succeed(Headers.CurrentRedactedNames, [credentialHeaderPattern]));
|
|
7629
|
+
const browserLifecycle = Layer.effectDiscard(Effect.gen(function* () {
|
|
7630
|
+
if (validated.producer !== "browser" || !hasBrowserDocument()) return;
|
|
7631
|
+
const browserWindow = window;
|
|
7632
|
+
const browserDocument = document;
|
|
7633
|
+
const flusher = yield* OtlpExporter.Flusher;
|
|
7634
|
+
const run = yield* FiberSet.makeRuntime();
|
|
7635
|
+
const flush = () => {
|
|
7636
|
+
run(flusher.flush);
|
|
7637
|
+
};
|
|
7638
|
+
const visibilityChanged = () => {
|
|
7639
|
+
if (browserDocument.visibilityState === "hidden") flush();
|
|
7640
|
+
};
|
|
7641
|
+
yield* Effect.acquireRelease(Effect.sync(() => {
|
|
7642
|
+
browserDocument.addEventListener("visibilitychange", visibilityChanged);
|
|
7643
|
+
browserWindow.addEventListener("pagehide", flush);
|
|
7644
|
+
}), () => Effect.sync(() => {
|
|
7645
|
+
browserDocument.removeEventListener("visibilitychange", visibilityChanged);
|
|
7646
|
+
browserWindow.removeEventListener("pagehide", flush);
|
|
7647
|
+
}));
|
|
7648
|
+
}));
|
|
7649
|
+
const transport = Layer.effect(HttpClient.HttpClient, Effect.map(HttpClient.HttpClient, (client) => validated.producer === "browser" && hasBrowserDocument() ? browserExportClient(client) : client)).pipe(Layer.provide(FetchHttpClient.layer));
|
|
7650
|
+
return browserLifecycle.pipe(Layer.provideMerge(Layer.mergeAll(tracing, logging, headerPolicy)), Layer.provide(OtlpSerialization.layerJson), Layer.provide(transport));
|
|
7651
|
+
};
|
|
7652
|
+
//#endregion
|
|
7653
|
+
//#region src/adapters/auth-client/resolve-auth-url.ts
|
|
7654
|
+
/** Join bootstrap `authBaseUrl` with a BetterAuth route without duplicating `/api/auth`. */
|
|
7655
|
+
function resolveAuthClientUrl(authBaseUrl, path) {
|
|
7656
|
+
const base = authBaseUrl.replace(/\/$/, "");
|
|
7657
|
+
if (base.endsWith("/api/auth") && path.startsWith("/api/auth")) return `${base}${path.slice(9)}`;
|
|
7658
|
+
return `${base}${path}`;
|
|
7160
7659
|
}
|
|
7161
|
-
var AccountReadPortTag = class extends Context.Service()("@capxul/sdk/ports/AccountReadPort") {};
|
|
7162
7660
|
//#endregion
|
|
7163
|
-
//#region src/
|
|
7164
|
-
|
|
7165
|
-
function
|
|
7166
|
-
return
|
|
7167
|
-
|
|
7168
|
-
|
|
7169
|
-
|
|
7170
|
-
|
|
7171
|
-
|
|
7661
|
+
//#region src/internal/observation-http.ts
|
|
7662
|
+
/** Resolve one bounded pre-auth snapshot for an outbound SDK HTTP request. */
|
|
7663
|
+
function observationRequestHeaders(adapter, source) {
|
|
7664
|
+
if (adapter === void 0) return {};
|
|
7665
|
+
try {
|
|
7666
|
+
const invocation = readInvocationObservation(source);
|
|
7667
|
+
if (invocation !== void 0 && !invocation.active) return {};
|
|
7668
|
+
const encoded = encodeObservationContextHeader(invocation === void 0 ? adapter.resolveContext?.() : invocation.context);
|
|
7669
|
+
return encoded === void 0 ? {} : { [OBSERVATION_CONTEXT_HEADER]: encoded };
|
|
7670
|
+
} catch {
|
|
7671
|
+
return {};
|
|
7672
|
+
}
|
|
7673
|
+
}
|
|
7674
|
+
//#endregion
|
|
7675
|
+
//#region src/adapters/auth-client/BetterAuthBrowserAdapter.ts
|
|
7676
|
+
function withSignal$1(init, signal) {
|
|
7677
|
+
return signal === void 0 ? init : {
|
|
7678
|
+
...init,
|
|
7679
|
+
signal
|
|
7680
|
+
};
|
|
7681
|
+
}
|
|
7682
|
+
const DEFAULT_JWT_LIFETIME_S$1 = 900;
|
|
7683
|
+
function decodeJwtExp$1(jwt) {
|
|
7684
|
+
const parts = jwt.split(".");
|
|
7685
|
+
if (parts.length < 2 || parts[1] === void 0) return Math.floor(Date.now() / 1e3) + DEFAULT_JWT_LIFETIME_S$1;
|
|
7686
|
+
try {
|
|
7687
|
+
const raw = parts[1].replace(/\s+/g, "");
|
|
7688
|
+
const pad = "=".repeat((4 - raw.length % 4) % 4);
|
|
7689
|
+
const decoded = atob(raw.replace(/-/g, "+").replace(/_/g, "/") + pad);
|
|
7690
|
+
const payload = JSON.parse(decoded);
|
|
7691
|
+
if (typeof payload.exp === "number" && Number.isFinite(payload.exp) && payload.exp > 0) return payload.exp;
|
|
7692
|
+
} catch {}
|
|
7693
|
+
return Math.floor(Date.now() / 1e3) + DEFAULT_JWT_LIFETIME_S$1;
|
|
7694
|
+
}
|
|
7695
|
+
function authSessionFromBetterAuth$1(token, user) {
|
|
7696
|
+
return {
|
|
7697
|
+
authUserId: toAuthUserId(user.id),
|
|
7698
|
+
email: toEmail(user.email),
|
|
7699
|
+
token: toSessionToken(token),
|
|
7700
|
+
expiresAt: toEpochMs(Date.now() + 6048e5)
|
|
7701
|
+
};
|
|
7702
|
+
}
|
|
7703
|
+
async function safeJson$1(res) {
|
|
7704
|
+
const raw = await res.text();
|
|
7705
|
+
if (raw.length === 0 || raw === "null") return null;
|
|
7706
|
+
try {
|
|
7707
|
+
return JSON.parse(raw);
|
|
7708
|
+
} catch {
|
|
7709
|
+
return null;
|
|
7710
|
+
}
|
|
7711
|
+
}
|
|
7712
|
+
function mapBetterAuthError(operation, body) {
|
|
7713
|
+
if (typeof body === "object" && body !== null) {
|
|
7714
|
+
const errBody = body;
|
|
7715
|
+
const code = typeof errBody.code === "string" ? errBody.code : "";
|
|
7716
|
+
if (code === "OTP_EXPIRED") return Errors.otpExpired();
|
|
7717
|
+
if (code === "INVALID_OTP") return Errors.invalidInput("otp", errBody.message ?? "invalid OTP");
|
|
7718
|
+
if (code === "VALIDATION_ERROR" || code === "INVALID_EMAIL") return Errors.invalidInput("email", errBody.message ?? "invalid email");
|
|
7719
|
+
}
|
|
7720
|
+
return Errors.providerError("better-auth", operation, new Error(String(body)));
|
|
7721
|
+
}
|
|
7722
|
+
function isAbortError$1(err, signal) {
|
|
7723
|
+
return signal?.aborted === true || err instanceof Error && err.name === "AbortError" || typeof DOMException !== "undefined" && err instanceof DOMException && err.name === "AbortError";
|
|
7724
|
+
}
|
|
7725
|
+
function mapFetchError$1(operation, err, signal) {
|
|
7726
|
+
if (isAbortError$1(err, signal)) return Errors.cancelled({ operation });
|
|
7727
|
+
return Errors.networkError(operation, err instanceof Error ? err : new Error(String(err)));
|
|
7728
|
+
}
|
|
7729
|
+
var BetterAuthBrowserAdapter = class {
|
|
7730
|
+
authBaseUrl;
|
|
7731
|
+
fetchImpl;
|
|
7732
|
+
observation;
|
|
7733
|
+
constructor(deps) {
|
|
7734
|
+
this.authBaseUrl = deps.authBaseUrl.replace(/\/$/, "");
|
|
7735
|
+
this.observation = deps.observation;
|
|
7736
|
+
this.fetchImpl = deps.fetch ?? ((input, init) => globalThis.fetch(input, init));
|
|
7737
|
+
}
|
|
7738
|
+
url(path) {
|
|
7739
|
+
return resolveAuthClientUrl(this.authBaseUrl, path);
|
|
7740
|
+
}
|
|
7741
|
+
async canSendOtp(_input, options) {
|
|
7742
|
+
if (options?.signal?.aborted) return {
|
|
7743
|
+
ok: false,
|
|
7744
|
+
error: Errors.cancelled({ operation: "canSendOtp" })
|
|
7745
|
+
};
|
|
7746
|
+
return {
|
|
7747
|
+
ok: true,
|
|
7748
|
+
value: {
|
|
7749
|
+
allowed: true,
|
|
7750
|
+
cooldownMs: toDurationMs(0)
|
|
7751
|
+
}
|
|
7752
|
+
};
|
|
7753
|
+
}
|
|
7754
|
+
async sendOtp(input, options) {
|
|
7755
|
+
if (options?.signal?.aborted) return {
|
|
7756
|
+
ok: false,
|
|
7757
|
+
error: Errors.cancelled({ operation: "sendOtp" })
|
|
7758
|
+
};
|
|
7759
|
+
try {
|
|
7760
|
+
const res = await this.fetchImpl(this.url("/api/auth/email-otp/send-verification-otp"), withSignal$1({
|
|
7761
|
+
method: "POST",
|
|
7762
|
+
headers: {
|
|
7763
|
+
"Content-Type": "application/json",
|
|
7764
|
+
...observationRequestHeaders(this.observation, input)
|
|
7765
|
+
},
|
|
7766
|
+
body: JSON.stringify({
|
|
7767
|
+
email: input.email,
|
|
7768
|
+
type: "sign-in"
|
|
7769
|
+
}),
|
|
7770
|
+
credentials: "include"
|
|
7771
|
+
}, options?.signal));
|
|
7772
|
+
if (options?.signal?.aborted) return {
|
|
7773
|
+
ok: false,
|
|
7774
|
+
error: Errors.cancelled({ operation: "sendOtp" })
|
|
7775
|
+
};
|
|
7776
|
+
if (res.ok) return {
|
|
7777
|
+
ok: true,
|
|
7778
|
+
value: void 0
|
|
7779
|
+
};
|
|
7780
|
+
if (res.status === 429) return {
|
|
7781
|
+
ok: false,
|
|
7782
|
+
error: Errors.rateLimited({ resource: "better-auth/sendOtp" })
|
|
7783
|
+
};
|
|
7784
|
+
const body = await safeJson$1(res);
|
|
7785
|
+
if (typeof body === "object" && body !== null) {
|
|
7786
|
+
const errBody = body;
|
|
7787
|
+
if (errBody.code === "INVALID_EMAIL" || errBody.code === "VALIDATION_ERROR") return {
|
|
7788
|
+
ok: false,
|
|
7789
|
+
error: Errors.invalidInput("email", errBody.message ?? "invalid email")
|
|
7790
|
+
};
|
|
7791
|
+
}
|
|
7792
|
+
return {
|
|
7793
|
+
ok: false,
|
|
7794
|
+
error: Errors.providerError("better-auth", "sendOtp", /* @__PURE__ */ new Error(`HTTP ${res.status}`))
|
|
7795
|
+
};
|
|
7796
|
+
} catch (err) {
|
|
7797
|
+
return {
|
|
7798
|
+
ok: false,
|
|
7799
|
+
error: mapFetchError$1("sendOtp", err, options?.signal)
|
|
7800
|
+
};
|
|
7801
|
+
}
|
|
7802
|
+
}
|
|
7803
|
+
async verifyOtp(input, options) {
|
|
7804
|
+
if (options?.signal?.aborted) return {
|
|
7805
|
+
ok: false,
|
|
7806
|
+
error: Errors.cancelled({ operation: "verifyOtp" })
|
|
7807
|
+
};
|
|
7808
|
+
try {
|
|
7809
|
+
const res = await this.fetchImpl(this.url("/api/auth/sign-in/email-otp"), withSignal$1({
|
|
7810
|
+
method: "POST",
|
|
7811
|
+
headers: { "Content-Type": "application/json" },
|
|
7812
|
+
body: JSON.stringify({
|
|
7813
|
+
email: input.email,
|
|
7814
|
+
otp: input.otp
|
|
7815
|
+
}),
|
|
7816
|
+
credentials: "include"
|
|
7817
|
+
}, options?.signal));
|
|
7818
|
+
if (options?.signal?.aborted) return {
|
|
7819
|
+
ok: false,
|
|
7820
|
+
error: Errors.cancelled({ operation: "verifyOtp" })
|
|
7821
|
+
};
|
|
7822
|
+
const body = await safeJson$1(res);
|
|
7823
|
+
if (res.ok) {
|
|
7824
|
+
if (typeof body === "object" && body !== null) {
|
|
7825
|
+
const okBody = body;
|
|
7826
|
+
if (typeof okBody.token === "string" && typeof okBody.user === "object" && okBody.user !== null) return {
|
|
7827
|
+
ok: true,
|
|
7828
|
+
value: authSessionFromBetterAuth$1(okBody.token, okBody.user)
|
|
7829
|
+
};
|
|
7830
|
+
}
|
|
7831
|
+
return {
|
|
7832
|
+
ok: false,
|
|
7833
|
+
error: Errors.providerError("better-auth", "verifyOtp", /* @__PURE__ */ new Error("unexpected 200 body"))
|
|
7834
|
+
};
|
|
7835
|
+
}
|
|
7836
|
+
return {
|
|
7837
|
+
ok: false,
|
|
7838
|
+
error: mapBetterAuthError("verifyOtp", body)
|
|
7839
|
+
};
|
|
7840
|
+
} catch (err) {
|
|
7841
|
+
return {
|
|
7842
|
+
ok: false,
|
|
7843
|
+
error: mapFetchError$1("verifyOtp", err, options?.signal)
|
|
7844
|
+
};
|
|
7845
|
+
}
|
|
7846
|
+
}
|
|
7847
|
+
async getSession(options) {
|
|
7848
|
+
if (options?.signal?.aborted) return {
|
|
7849
|
+
ok: false,
|
|
7850
|
+
error: Errors.cancelled({ operation: "getSession" })
|
|
7851
|
+
};
|
|
7852
|
+
try {
|
|
7853
|
+
const res = await this.fetchImpl(this.url("/api/auth/get-session"), withSignal$1({
|
|
7854
|
+
method: "GET",
|
|
7855
|
+
credentials: "include"
|
|
7856
|
+
}, options?.signal));
|
|
7857
|
+
if (options?.signal?.aborted) return {
|
|
7858
|
+
ok: false,
|
|
7859
|
+
error: Errors.cancelled({ operation: "getSession" })
|
|
7860
|
+
};
|
|
7861
|
+
if (!res.ok) return {
|
|
7862
|
+
ok: false,
|
|
7863
|
+
error: Errors.providerError("better-auth", "getSession", /* @__PURE__ */ new Error(`HTTP ${res.status}`))
|
|
7864
|
+
};
|
|
7865
|
+
const body = await safeJson$1(res);
|
|
7866
|
+
if (body === null) return {
|
|
7867
|
+
ok: true,
|
|
7868
|
+
value: null
|
|
7869
|
+
};
|
|
7870
|
+
if (typeof body === "object" && body !== null) {
|
|
7871
|
+
const okBody = body;
|
|
7872
|
+
if (typeof okBody.user === "object" && okBody.user !== null) return {
|
|
7873
|
+
ok: true,
|
|
7874
|
+
value: authSessionFromBetterAuth$1(okBody.session?.token ?? okBody.session?.id ?? "session", okBody.user)
|
|
7875
|
+
};
|
|
7876
|
+
}
|
|
7877
|
+
return {
|
|
7878
|
+
ok: true,
|
|
7879
|
+
value: null
|
|
7880
|
+
};
|
|
7881
|
+
} catch (err) {
|
|
7882
|
+
return {
|
|
7883
|
+
ok: false,
|
|
7884
|
+
error: mapFetchError$1("getSession", err, options?.signal)
|
|
7885
|
+
};
|
|
7886
|
+
}
|
|
7887
|
+
}
|
|
7888
|
+
async signOut(options) {
|
|
7889
|
+
if (options?.signal?.aborted) return {
|
|
7890
|
+
ok: false,
|
|
7891
|
+
error: Errors.cancelled({ operation: "signOut" })
|
|
7892
|
+
};
|
|
7893
|
+
try {
|
|
7894
|
+
const res = await this.fetchImpl(this.url("/api/auth/sign-out"), withSignal$1({
|
|
7895
|
+
method: "POST",
|
|
7896
|
+
headers: { "Content-Type": "application/json" },
|
|
7897
|
+
body: "{}",
|
|
7898
|
+
credentials: "include"
|
|
7899
|
+
}, options?.signal));
|
|
7900
|
+
if (options?.signal?.aborted) return {
|
|
7901
|
+
ok: false,
|
|
7902
|
+
error: Errors.cancelled({ operation: "signOut" })
|
|
7903
|
+
};
|
|
7904
|
+
if (res.ok) return {
|
|
7905
|
+
ok: true,
|
|
7906
|
+
value: void 0
|
|
7907
|
+
};
|
|
7908
|
+
return {
|
|
7909
|
+
ok: false,
|
|
7910
|
+
error: Errors.providerError("better-auth", "signOut", /* @__PURE__ */ new Error(`HTTP ${res.status}`))
|
|
7911
|
+
};
|
|
7912
|
+
} catch (err) {
|
|
7913
|
+
return {
|
|
7914
|
+
ok: false,
|
|
7915
|
+
error: mapFetchError$1("signOut", err, options?.signal)
|
|
7916
|
+
};
|
|
7917
|
+
}
|
|
7918
|
+
}
|
|
7919
|
+
async getConvexJwt(options) {
|
|
7920
|
+
if (options?.signal?.aborted) return {
|
|
7921
|
+
ok: false,
|
|
7922
|
+
error: Errors.cancelled({ operation: "getConvexJwt" })
|
|
7923
|
+
};
|
|
7924
|
+
try {
|
|
7925
|
+
const res = await this.fetchImpl(this.url("/api/auth/convex/token"), withSignal$1({
|
|
7926
|
+
method: "GET",
|
|
7927
|
+
credentials: "include"
|
|
7928
|
+
}, options?.signal));
|
|
7929
|
+
if (options?.signal?.aborted) return {
|
|
7930
|
+
ok: false,
|
|
7931
|
+
error: Errors.cancelled({ operation: "getConvexJwt" })
|
|
7932
|
+
};
|
|
7933
|
+
if (res.status === 401) return {
|
|
7934
|
+
ok: false,
|
|
7935
|
+
error: Errors.notAuthenticated()
|
|
7936
|
+
};
|
|
7937
|
+
if (!res.ok) return {
|
|
7938
|
+
ok: false,
|
|
7939
|
+
error: Errors.providerError("better-auth", "getConvexJwt", /* @__PURE__ */ new Error(`HTTP ${res.status}`))
|
|
7940
|
+
};
|
|
7941
|
+
const body = await safeJson$1(res);
|
|
7942
|
+
if (typeof body === "object" && body !== null) {
|
|
7943
|
+
const okBody = body;
|
|
7944
|
+
if (typeof okBody.token === "string" && okBody.token.length > 0) return {
|
|
7945
|
+
ok: true,
|
|
7946
|
+
value: {
|
|
7947
|
+
token: toJwtToken(okBody.token),
|
|
7948
|
+
expEpochSeconds: toEpochSeconds(decodeJwtExp$1(okBody.token))
|
|
7949
|
+
}
|
|
7950
|
+
};
|
|
7951
|
+
}
|
|
7952
|
+
return {
|
|
7953
|
+
ok: false,
|
|
7954
|
+
error: Errors.providerError("better-auth", "getConvexJwt", /* @__PURE__ */ new Error("unexpected body"))
|
|
7955
|
+
};
|
|
7956
|
+
} catch (err) {
|
|
7957
|
+
return {
|
|
7958
|
+
ok: false,
|
|
7959
|
+
error: mapFetchError$1("getConvexJwt", err, options?.signal)
|
|
7960
|
+
};
|
|
7961
|
+
}
|
|
7962
|
+
}
|
|
7963
|
+
};
|
|
7964
|
+
function BetterAuthBrowserLayer(deps) {
|
|
7965
|
+
return Layer.succeed(AuthClientPortTag, authClientPortFromPromiseAdapter(new BetterAuthBrowserAdapter(deps)));
|
|
7966
|
+
}
|
|
7967
|
+
//#endregion
|
|
7968
|
+
//#region src/adapters/auth-client/cookie-jar.ts
|
|
7969
|
+
function parseSetCookie(raw) {
|
|
7970
|
+
const parts = raw.split(";").map((p) => p.trim());
|
|
7971
|
+
if (parts.length === 0 || parts[0] === void 0) return null;
|
|
7972
|
+
const nameValue = parts[0];
|
|
7973
|
+
const eq = nameValue.indexOf("=");
|
|
7974
|
+
if (eq < 0) return null;
|
|
7975
|
+
const name = nameValue.slice(0, eq).trim();
|
|
7976
|
+
const value = nameValue.slice(eq + 1).trim();
|
|
7977
|
+
if (name.length === 0) return null;
|
|
7978
|
+
let maxAgeSeconds = null;
|
|
7979
|
+
let expiresEpochMs = null;
|
|
7980
|
+
let path = null;
|
|
7981
|
+
let httpOnly = false;
|
|
7982
|
+
let secure = false;
|
|
7983
|
+
let sameSite = null;
|
|
7984
|
+
for (let i = 1; i < parts.length; i++) {
|
|
7985
|
+
const part = parts[i];
|
|
7986
|
+
if (part === void 0) continue;
|
|
7987
|
+
const partEq = part.indexOf("=");
|
|
7988
|
+
const key = (partEq < 0 ? part : part.slice(0, partEq)).trim().toLowerCase();
|
|
7989
|
+
const val = partEq < 0 ? "" : part.slice(partEq + 1).trim();
|
|
7990
|
+
if (key === "max-age") {
|
|
7991
|
+
const n = Number(val);
|
|
7992
|
+
if (Number.isFinite(n)) maxAgeSeconds = n;
|
|
7993
|
+
} else if (key === "expires") {
|
|
7994
|
+
const t = Date.parse(val);
|
|
7995
|
+
if (Number.isFinite(t)) expiresEpochMs = t;
|
|
7996
|
+
} else if (key === "path") path = val;
|
|
7997
|
+
else if (key === "httponly") httpOnly = true;
|
|
7998
|
+
else if (key === "secure") secure = true;
|
|
7999
|
+
else if (key === "samesite") {
|
|
8000
|
+
const lc = val.toLowerCase();
|
|
8001
|
+
if (lc === "strict" || lc === "lax" || lc === "none") sameSite = lc;
|
|
8002
|
+
}
|
|
8003
|
+
}
|
|
8004
|
+
return {
|
|
8005
|
+
name,
|
|
8006
|
+
value,
|
|
8007
|
+
maxAgeSeconds,
|
|
8008
|
+
expiresEpochMs,
|
|
8009
|
+
path,
|
|
8010
|
+
httpOnly,
|
|
8011
|
+
secure,
|
|
8012
|
+
sameSite
|
|
8013
|
+
};
|
|
8014
|
+
}
|
|
8015
|
+
function isExpired(cookie, nowEpochMs) {
|
|
8016
|
+
if (cookie.maxAgeSeconds !== null) {
|
|
8017
|
+
if (cookie.maxAgeSeconds <= 0) return true;
|
|
8018
|
+
return nowEpochMs >= cookie.storedAtEpochMs + cookie.maxAgeSeconds * 1e3;
|
|
8019
|
+
}
|
|
8020
|
+
if (cookie.expiresEpochMs !== null) return nowEpochMs >= cookie.expiresEpochMs;
|
|
8021
|
+
return false;
|
|
8022
|
+
}
|
|
8023
|
+
var CookieJar = class {
|
|
8024
|
+
store = /* @__PURE__ */ new Map();
|
|
8025
|
+
set(host, setCookieHeaders) {
|
|
8026
|
+
let perHost = this.store.get(host);
|
|
8027
|
+
const now = Date.now();
|
|
8028
|
+
for (const raw of setCookieHeaders) {
|
|
8029
|
+
const parsed = parseSetCookie(raw);
|
|
8030
|
+
if (parsed === null) continue;
|
|
8031
|
+
if (perHost === void 0) {
|
|
8032
|
+
perHost = /* @__PURE__ */ new Map();
|
|
8033
|
+
this.store.set(host, perHost);
|
|
8034
|
+
}
|
|
8035
|
+
if (parsed.maxAgeSeconds !== null && parsed.maxAgeSeconds <= 0) {
|
|
8036
|
+
perHost.delete(parsed.name);
|
|
8037
|
+
continue;
|
|
8038
|
+
}
|
|
8039
|
+
perHost.set(parsed.name, {
|
|
8040
|
+
...parsed,
|
|
8041
|
+
storedAtEpochMs: now
|
|
8042
|
+
});
|
|
8043
|
+
}
|
|
8044
|
+
if (perHost !== void 0 && perHost.size === 0) this.store.delete(host);
|
|
8045
|
+
}
|
|
8046
|
+
getCookieHeader(host) {
|
|
8047
|
+
const perHost = this.store.get(host);
|
|
8048
|
+
if (perHost === void 0 || perHost.size === 0) return null;
|
|
8049
|
+
const now = Date.now();
|
|
8050
|
+
const live = [];
|
|
8051
|
+
for (const [name, cookie] of perHost.entries()) {
|
|
8052
|
+
if (isExpired(cookie, now)) {
|
|
8053
|
+
perHost.delete(name);
|
|
8054
|
+
continue;
|
|
8055
|
+
}
|
|
8056
|
+
live.push(`${name}=${cookie.value}`);
|
|
8057
|
+
}
|
|
8058
|
+
if (live.length === 0) {
|
|
8059
|
+
this.store.delete(host);
|
|
8060
|
+
return null;
|
|
8061
|
+
}
|
|
8062
|
+
return live.join("; ");
|
|
8063
|
+
}
|
|
8064
|
+
};
|
|
8065
|
+
//#endregion
|
|
8066
|
+
//#region src/adapters/auth-client/BetterAuthNodeAdapter.ts
|
|
8067
|
+
function withSignal(init, signal) {
|
|
8068
|
+
return signal === void 0 ? init : {
|
|
8069
|
+
...init,
|
|
8070
|
+
signal
|
|
8071
|
+
};
|
|
8072
|
+
}
|
|
8073
|
+
const DEFAULT_JWT_LIFETIME_S = 900;
|
|
8074
|
+
function decodeJwtExp(jwt) {
|
|
8075
|
+
const parts = jwt.split(".");
|
|
8076
|
+
if (parts.length < 2 || parts[1] === void 0) return Math.floor(Date.now() / 1e3) + DEFAULT_JWT_LIFETIME_S;
|
|
8077
|
+
try {
|
|
8078
|
+
const raw = parts[1].replace(/\s+/g, "");
|
|
8079
|
+
const pad = "=".repeat((4 - raw.length % 4) % 4);
|
|
8080
|
+
const decoded = Buffer.from(raw.replace(/-/g, "+").replace(/_/g, "/") + pad, "base64").toString("utf-8");
|
|
8081
|
+
const payload = JSON.parse(decoded);
|
|
8082
|
+
if (typeof payload.exp === "number" && Number.isFinite(payload.exp) && payload.exp > 0) return payload.exp;
|
|
8083
|
+
} catch {}
|
|
8084
|
+
return Math.floor(Date.now() / 1e3) + DEFAULT_JWT_LIFETIME_S;
|
|
8085
|
+
}
|
|
8086
|
+
function authSessionFromBetterAuth(token, user) {
|
|
8087
|
+
return {
|
|
8088
|
+
authUserId: toAuthUserId(user.id),
|
|
8089
|
+
email: toEmail(user.email),
|
|
8090
|
+
token: toSessionToken(token),
|
|
8091
|
+
expiresAt: toEpochMs(Date.now() + 6048e5)
|
|
8092
|
+
};
|
|
8093
|
+
}
|
|
8094
|
+
async function safeJson(res) {
|
|
8095
|
+
const raw = await res.text();
|
|
8096
|
+
if (raw.length === 0 || raw === "null") return null;
|
|
8097
|
+
try {
|
|
8098
|
+
return JSON.parse(raw);
|
|
8099
|
+
} catch {
|
|
8100
|
+
return null;
|
|
8101
|
+
}
|
|
8102
|
+
}
|
|
8103
|
+
function hostFromBaseUrl(baseUrl) {
|
|
8104
|
+
try {
|
|
8105
|
+
return new URL(baseUrl).host;
|
|
8106
|
+
} catch {
|
|
8107
|
+
return baseUrl;
|
|
8108
|
+
}
|
|
8109
|
+
}
|
|
8110
|
+
function originFromBaseUrl(baseUrl) {
|
|
8111
|
+
try {
|
|
8112
|
+
return new URL(baseUrl).origin;
|
|
8113
|
+
} catch {
|
|
8114
|
+
return;
|
|
8115
|
+
}
|
|
8116
|
+
}
|
|
8117
|
+
function setCookiesFromResponse(jar, host, res) {
|
|
8118
|
+
const ext = res.headers;
|
|
8119
|
+
let setCookies = [];
|
|
8120
|
+
if (typeof ext.getSetCookie === "function") setCookies = ext.getSetCookie();
|
|
8121
|
+
else res.headers.forEach((value, key) => {
|
|
8122
|
+
if (key.toLowerCase() === "set-cookie") setCookies.push(value);
|
|
7172
8123
|
});
|
|
8124
|
+
if (setCookies.length > 0) jar.set(host, setCookies);
|
|
8125
|
+
}
|
|
8126
|
+
function isAbortError(err, signal) {
|
|
8127
|
+
return signal?.aborted === true || err instanceof Error && err.name === "AbortError" || typeof DOMException !== "undefined" && err instanceof DOMException && err.name === "AbortError";
|
|
8128
|
+
}
|
|
8129
|
+
function mapFetchError(operation, err, signal) {
|
|
8130
|
+
if (isAbortError(err, signal)) return Errors.cancelled({ operation });
|
|
8131
|
+
return Errors.networkError(operation, err instanceof Error ? err : new Error(String(err)));
|
|
8132
|
+
}
|
|
8133
|
+
var BetterAuthNodeAdapter = class {
|
|
8134
|
+
authBaseUrl;
|
|
8135
|
+
host;
|
|
8136
|
+
origin;
|
|
8137
|
+
cookieJar;
|
|
8138
|
+
fetchImpl;
|
|
8139
|
+
observation;
|
|
8140
|
+
constructor(deps) {
|
|
8141
|
+
this.authBaseUrl = deps.authBaseUrl.replace(/\/$/, "");
|
|
8142
|
+
this.host = hostFromBaseUrl(this.authBaseUrl);
|
|
8143
|
+
this.origin = deps.origin?.replace(/\/$/, "");
|
|
8144
|
+
this.cookieJar = deps.cookieJar ?? new CookieJar();
|
|
8145
|
+
this.fetchImpl = deps.fetch ?? fetch;
|
|
8146
|
+
this.observation = deps.observation;
|
|
8147
|
+
}
|
|
8148
|
+
url(path) {
|
|
8149
|
+
return resolveAuthClientUrl(this.authBaseUrl, path);
|
|
8150
|
+
}
|
|
8151
|
+
headersWithCookie(extra = {}) {
|
|
8152
|
+
const cookie = this.cookieJar.getCookieHeader(this.host);
|
|
8153
|
+
return cookie !== null ? {
|
|
8154
|
+
...extra,
|
|
8155
|
+
cookie
|
|
8156
|
+
} : { ...extra };
|
|
8157
|
+
}
|
|
8158
|
+
async canSendOtp(_input, options) {
|
|
8159
|
+
if (options?.signal?.aborted) return {
|
|
8160
|
+
ok: false,
|
|
8161
|
+
error: Errors.cancelled({ operation: "canSendOtp" })
|
|
8162
|
+
};
|
|
8163
|
+
return {
|
|
8164
|
+
ok: true,
|
|
8165
|
+
value: {
|
|
8166
|
+
allowed: true,
|
|
8167
|
+
cooldownMs: toDurationMs(0)
|
|
8168
|
+
}
|
|
8169
|
+
};
|
|
8170
|
+
}
|
|
8171
|
+
async sendOtp(input, options) {
|
|
8172
|
+
if (options?.signal?.aborted) return {
|
|
8173
|
+
ok: false,
|
|
8174
|
+
error: Errors.cancelled({ operation: "sendOtp" })
|
|
8175
|
+
};
|
|
8176
|
+
try {
|
|
8177
|
+
const res = await this.fetchImpl(this.url("/api/auth/email-otp/send-verification-otp"), withSignal({
|
|
8178
|
+
method: "POST",
|
|
8179
|
+
headers: {
|
|
8180
|
+
"content-type": "application/json",
|
|
8181
|
+
...this.origin ? { origin: this.origin } : {},
|
|
8182
|
+
...observationRequestHeaders(this.observation, input)
|
|
8183
|
+
},
|
|
8184
|
+
body: JSON.stringify({
|
|
8185
|
+
email: input.email,
|
|
8186
|
+
type: "sign-in"
|
|
8187
|
+
})
|
|
8188
|
+
}, options?.signal));
|
|
8189
|
+
if (options?.signal?.aborted) return {
|
|
8190
|
+
ok: false,
|
|
8191
|
+
error: Errors.cancelled({ operation: "sendOtp" })
|
|
8192
|
+
};
|
|
8193
|
+
if (res.ok) return {
|
|
8194
|
+
ok: true,
|
|
8195
|
+
value: void 0
|
|
8196
|
+
};
|
|
8197
|
+
if (res.status === 429) return {
|
|
8198
|
+
ok: false,
|
|
8199
|
+
error: Errors.rateLimited({ resource: "better-auth/sendOtp" })
|
|
8200
|
+
};
|
|
8201
|
+
const body = await safeJson(res);
|
|
8202
|
+
if (typeof body === "object" && body !== null) {
|
|
8203
|
+
const errBody = body;
|
|
8204
|
+
if (errBody.code === "INVALID_EMAIL" || errBody.code === "VALIDATION_ERROR") return {
|
|
8205
|
+
ok: false,
|
|
8206
|
+
error: Errors.invalidInput("email", errBody.message ?? "invalid email")
|
|
8207
|
+
};
|
|
8208
|
+
}
|
|
8209
|
+
return {
|
|
8210
|
+
ok: false,
|
|
8211
|
+
error: Errors.providerError("better-auth", "sendOtp", /* @__PURE__ */ new Error(`HTTP ${res.status}`))
|
|
8212
|
+
};
|
|
8213
|
+
} catch (err) {
|
|
8214
|
+
return {
|
|
8215
|
+
ok: false,
|
|
8216
|
+
error: mapFetchError("sendOtp", err, options?.signal)
|
|
8217
|
+
};
|
|
8218
|
+
}
|
|
8219
|
+
}
|
|
8220
|
+
async verifyOtp(input, options) {
|
|
8221
|
+
if (options?.signal?.aborted) return {
|
|
8222
|
+
ok: false,
|
|
8223
|
+
error: Errors.cancelled({ operation: "verifyOtp" })
|
|
8224
|
+
};
|
|
8225
|
+
try {
|
|
8226
|
+
const res = await this.fetchImpl(this.url("/api/auth/sign-in/email-otp"), withSignal({
|
|
8227
|
+
method: "POST",
|
|
8228
|
+
headers: {
|
|
8229
|
+
"content-type": "application/json",
|
|
8230
|
+
...this.origin ? { origin: this.origin } : {}
|
|
8231
|
+
},
|
|
8232
|
+
body: JSON.stringify({
|
|
8233
|
+
email: input.email,
|
|
8234
|
+
otp: input.otp
|
|
8235
|
+
})
|
|
8236
|
+
}, options?.signal));
|
|
8237
|
+
if (options?.signal?.aborted) return {
|
|
8238
|
+
ok: false,
|
|
8239
|
+
error: Errors.cancelled({ operation: "verifyOtp" })
|
|
8240
|
+
};
|
|
8241
|
+
setCookiesFromResponse(this.cookieJar, this.host, res);
|
|
8242
|
+
const body = await safeJson(res);
|
|
8243
|
+
if (res.ok && typeof body === "object" && body !== null) {
|
|
8244
|
+
const okBody = body;
|
|
8245
|
+
if (typeof okBody.token === "string" && typeof okBody.user === "object" && okBody.user !== null) return {
|
|
8246
|
+
ok: true,
|
|
8247
|
+
value: authSessionFromBetterAuth(okBody.token, okBody.user)
|
|
8248
|
+
};
|
|
8249
|
+
}
|
|
8250
|
+
if (!res.ok) {
|
|
8251
|
+
if (typeof body === "object" && body !== null) {
|
|
8252
|
+
const errBody = body;
|
|
8253
|
+
if (errBody.code === "OTP_EXPIRED") return {
|
|
8254
|
+
ok: false,
|
|
8255
|
+
error: Errors.otpExpired()
|
|
8256
|
+
};
|
|
8257
|
+
if (errBody.code === "INVALID_OTP") return {
|
|
8258
|
+
ok: false,
|
|
8259
|
+
error: Errors.invalidInput("otp", errBody.message ?? "invalid OTP")
|
|
8260
|
+
};
|
|
8261
|
+
}
|
|
8262
|
+
}
|
|
8263
|
+
return {
|
|
8264
|
+
ok: false,
|
|
8265
|
+
error: Errors.providerError("better-auth", "verifyOtp", /* @__PURE__ */ new Error(`HTTP ${res.status}`))
|
|
8266
|
+
};
|
|
8267
|
+
} catch (err) {
|
|
8268
|
+
return {
|
|
8269
|
+
ok: false,
|
|
8270
|
+
error: mapFetchError("verifyOtp", err, options?.signal)
|
|
8271
|
+
};
|
|
8272
|
+
}
|
|
8273
|
+
}
|
|
8274
|
+
async getSession(options) {
|
|
8275
|
+
if (options?.signal?.aborted) return {
|
|
8276
|
+
ok: false,
|
|
8277
|
+
error: Errors.cancelled({ operation: "getSession" })
|
|
8278
|
+
};
|
|
8279
|
+
try {
|
|
8280
|
+
const res = await this.fetchImpl(this.url("/api/auth/get-session"), withSignal({
|
|
8281
|
+
method: "GET",
|
|
8282
|
+
headers: this.headersWithCookie()
|
|
8283
|
+
}, options?.signal));
|
|
8284
|
+
if (options?.signal?.aborted) return {
|
|
8285
|
+
ok: false,
|
|
8286
|
+
error: Errors.cancelled({ operation: "getSession" })
|
|
8287
|
+
};
|
|
8288
|
+
if (!res.ok) return {
|
|
8289
|
+
ok: false,
|
|
8290
|
+
error: Errors.providerError("better-auth", "getSession", /* @__PURE__ */ new Error(`HTTP ${res.status}`))
|
|
8291
|
+
};
|
|
8292
|
+
const body = await safeJson(res);
|
|
8293
|
+
if (body === null) return {
|
|
8294
|
+
ok: true,
|
|
8295
|
+
value: null
|
|
8296
|
+
};
|
|
8297
|
+
if (typeof body === "object" && body !== null) {
|
|
8298
|
+
const okBody = body;
|
|
8299
|
+
if (typeof okBody.user === "object" && okBody.user !== null) return {
|
|
8300
|
+
ok: true,
|
|
8301
|
+
value: authSessionFromBetterAuth(okBody.session?.token ?? okBody.session?.id ?? "session", okBody.user)
|
|
8302
|
+
};
|
|
8303
|
+
}
|
|
8304
|
+
return {
|
|
8305
|
+
ok: true,
|
|
8306
|
+
value: null
|
|
8307
|
+
};
|
|
8308
|
+
} catch (err) {
|
|
8309
|
+
return {
|
|
8310
|
+
ok: false,
|
|
8311
|
+
error: mapFetchError("getSession", err, options?.signal)
|
|
8312
|
+
};
|
|
8313
|
+
}
|
|
8314
|
+
}
|
|
8315
|
+
async signOut(options) {
|
|
8316
|
+
if (options?.signal?.aborted) return {
|
|
8317
|
+
ok: false,
|
|
8318
|
+
error: Errors.cancelled({ operation: "signOut" })
|
|
8319
|
+
};
|
|
8320
|
+
try {
|
|
8321
|
+
const signOutOrigin = originFromBaseUrl(this.authBaseUrl) ?? this.origin;
|
|
8322
|
+
const res = await this.fetchImpl(this.url("/api/auth/sign-out"), withSignal({
|
|
8323
|
+
method: "POST",
|
|
8324
|
+
headers: this.headersWithCookie({
|
|
8325
|
+
"content-type": "application/json",
|
|
8326
|
+
...signOutOrigin ? { origin: signOutOrigin } : {}
|
|
8327
|
+
}),
|
|
8328
|
+
body: "{}"
|
|
8329
|
+
}, options?.signal));
|
|
8330
|
+
if (options?.signal?.aborted) return {
|
|
8331
|
+
ok: false,
|
|
8332
|
+
error: Errors.cancelled({ operation: "signOut" })
|
|
8333
|
+
};
|
|
8334
|
+
setCookiesFromResponse(this.cookieJar, this.host, res);
|
|
8335
|
+
if (res.ok) return {
|
|
8336
|
+
ok: true,
|
|
8337
|
+
value: void 0
|
|
8338
|
+
};
|
|
8339
|
+
return {
|
|
8340
|
+
ok: false,
|
|
8341
|
+
error: Errors.providerError("better-auth", "signOut", /* @__PURE__ */ new Error(`HTTP ${res.status}`))
|
|
8342
|
+
};
|
|
8343
|
+
} catch (err) {
|
|
8344
|
+
return {
|
|
8345
|
+
ok: false,
|
|
8346
|
+
error: mapFetchError("signOut", err, options?.signal)
|
|
8347
|
+
};
|
|
8348
|
+
}
|
|
8349
|
+
}
|
|
8350
|
+
async getConvexJwt(options) {
|
|
8351
|
+
if (options?.signal?.aborted) return {
|
|
8352
|
+
ok: false,
|
|
8353
|
+
error: Errors.cancelled({ operation: "getConvexJwt" })
|
|
8354
|
+
};
|
|
8355
|
+
try {
|
|
8356
|
+
const res = await this.fetchImpl(this.url("/api/auth/convex/token"), withSignal({
|
|
8357
|
+
method: "GET",
|
|
8358
|
+
headers: this.headersWithCookie()
|
|
8359
|
+
}, options?.signal));
|
|
8360
|
+
if (options?.signal?.aborted) return {
|
|
8361
|
+
ok: false,
|
|
8362
|
+
error: Errors.cancelled({ operation: "getConvexJwt" })
|
|
8363
|
+
};
|
|
8364
|
+
if (res.status === 401) return {
|
|
8365
|
+
ok: false,
|
|
8366
|
+
error: Errors.notAuthenticated()
|
|
8367
|
+
};
|
|
8368
|
+
if (!res.ok) return {
|
|
8369
|
+
ok: false,
|
|
8370
|
+
error: Errors.providerError("better-auth", "getConvexJwt", /* @__PURE__ */ new Error(`HTTP ${res.status}`))
|
|
8371
|
+
};
|
|
8372
|
+
const body = await safeJson(res);
|
|
8373
|
+
if (typeof body === "object" && body !== null) {
|
|
8374
|
+
const okBody = body;
|
|
8375
|
+
if (typeof okBody.token === "string" && okBody.token.length > 0) return {
|
|
8376
|
+
ok: true,
|
|
8377
|
+
value: {
|
|
8378
|
+
token: toJwtToken(okBody.token),
|
|
8379
|
+
expEpochSeconds: toEpochSeconds(decodeJwtExp(okBody.token))
|
|
8380
|
+
}
|
|
8381
|
+
};
|
|
8382
|
+
}
|
|
8383
|
+
return {
|
|
8384
|
+
ok: false,
|
|
8385
|
+
error: Errors.providerError("better-auth", "getConvexJwt", /* @__PURE__ */ new Error("unexpected body"))
|
|
8386
|
+
};
|
|
8387
|
+
} catch (err) {
|
|
8388
|
+
return {
|
|
8389
|
+
ok: false,
|
|
8390
|
+
error: mapFetchError("getConvexJwt", err, options?.signal)
|
|
8391
|
+
};
|
|
8392
|
+
}
|
|
8393
|
+
}
|
|
8394
|
+
};
|
|
8395
|
+
function BetterAuthNodeLayer(deps) {
|
|
8396
|
+
return Layer.succeed(AuthClientPortTag, authClientPortFromPromiseAdapter(new BetterAuthNodeAdapter(deps)));
|
|
8397
|
+
}
|
|
8398
|
+
//#endregion
|
|
8399
|
+
//#region src/ports/bootstrap.ts
|
|
8400
|
+
var BootstrapError = class extends Data.TaggedError("BootstrapError") {};
|
|
8401
|
+
function bootstrapErrorFromCapxul(kind, error) {
|
|
8402
|
+
return new BootstrapError({
|
|
8403
|
+
operation: "resolve",
|
|
8404
|
+
kind,
|
|
8405
|
+
publicCode: error.code,
|
|
8406
|
+
cause: error,
|
|
8407
|
+
...kind === "notAuthenticated" || error.details === void 0 ? {} : { details: error.details }
|
|
8408
|
+
});
|
|
8409
|
+
}
|
|
8410
|
+
var BootstrapPortTag = class extends Context.Service()("@capxul/sdk/ports/BootstrapPort") {};
|
|
8411
|
+
//#endregion
|
|
8412
|
+
//#region src/adapters/bootstrap/HttpBootstrapAdapter.ts
|
|
8413
|
+
const DEFAULT_RETRY = {
|
|
8414
|
+
attempts: 2,
|
|
8415
|
+
baseDelayMs: 400
|
|
8416
|
+
};
|
|
8417
|
+
/** Request-side statuses a proxy or backend returns while momentarily unable to serve. */
|
|
8418
|
+
const TRANSIENT_HTTP_STATUSES = /* @__PURE__ */ new Set([
|
|
8419
|
+
408,
|
|
8420
|
+
425,
|
|
8421
|
+
429
|
|
8422
|
+
]);
|
|
8423
|
+
/** Every 5xx is a server-side condition worth one more try; the client sent nothing wrong. */
|
|
8424
|
+
function isTransientHttpStatus(status) {
|
|
8425
|
+
return status >= 500 || TRANSIENT_HTTP_STATUSES.has(status);
|
|
8426
|
+
}
|
|
8427
|
+
/**
|
|
8428
|
+
* A transient HTTP status is worth one more try. The bootstrap call crosses
|
|
8429
|
+
* the host's own proxy before it reaches Convex, and every observed failure
|
|
8430
|
+
* of that hop cleared on a retry seconds later. A network rejection (offline,
|
|
8431
|
+
* DNS, CORS) is not retried: it does not clear in a second, and the caller
|
|
8432
|
+
* surfaces it as `NETWORK_ERROR` at once. Auth and input rejections are
|
|
8433
|
+
* deterministic and never retried.
|
|
8434
|
+
*/
|
|
8435
|
+
function isTransientBootstrapFailure(error) {
|
|
8436
|
+
if (error.kind !== "provider") return false;
|
|
8437
|
+
const status = error.details?.httpStatus;
|
|
8438
|
+
return typeof status === "number" && isTransientHttpStatus(status);
|
|
8439
|
+
}
|
|
8440
|
+
async function safeText(res) {
|
|
8441
|
+
try {
|
|
8442
|
+
return await res.text();
|
|
8443
|
+
} catch {
|
|
8444
|
+
return "";
|
|
8445
|
+
}
|
|
8446
|
+
}
|
|
8447
|
+
var HttpBootstrapAdapter = class {
|
|
8448
|
+
bootstrapBaseUrl;
|
|
8449
|
+
bootstrapHost;
|
|
8450
|
+
fetchImpl;
|
|
8451
|
+
observation;
|
|
8452
|
+
retry;
|
|
8453
|
+
constructor(deps) {
|
|
8454
|
+
this.bootstrapBaseUrl = deps.bootstrapBaseUrl.replace(/\/$/, "");
|
|
8455
|
+
this.bootstrapHost = hostOf(this.bootstrapBaseUrl);
|
|
8456
|
+
this.observation = deps.observation;
|
|
8457
|
+
this.retry = deps.retry ?? DEFAULT_RETRY;
|
|
8458
|
+
this.fetchImpl = deps.fetch ?? ((input, init) => globalThis.fetch(input, init));
|
|
8459
|
+
}
|
|
8460
|
+
resolve(input) {
|
|
8461
|
+
return this.attempt(input).pipe(Effect.retry({
|
|
8462
|
+
times: this.retry.attempts,
|
|
8463
|
+
while: isTransientBootstrapFailure,
|
|
8464
|
+
schedule: Schedule.exponential(Duration.millis(this.retry.baseDelayMs))
|
|
8465
|
+
}));
|
|
8466
|
+
}
|
|
8467
|
+
attempt(input) {
|
|
8468
|
+
return Effect.tryPromise({
|
|
8469
|
+
try: () => {
|
|
8470
|
+
const headers = {
|
|
8471
|
+
"content-type": "application/json",
|
|
8472
|
+
...input.origin === void 0 ? {} : { origin: input.origin },
|
|
8473
|
+
...observationRequestHeaders(this.observation)
|
|
8474
|
+
};
|
|
8475
|
+
return this.fetchImpl(`${this.bootstrapBaseUrl}/v1/client/bootstrap`, {
|
|
8476
|
+
method: "POST",
|
|
8477
|
+
headers,
|
|
8478
|
+
body: JSON.stringify({ publishableKey: input.publishableKey })
|
|
8479
|
+
});
|
|
8480
|
+
},
|
|
8481
|
+
catch: (cause) => bootstrapErrorFromCapxul("network", Errors.networkError("bootstrap", cause, {
|
|
8482
|
+
provider: "bootstrap",
|
|
8483
|
+
failure_mode: "upstream-down",
|
|
8484
|
+
...this.bootstrapHost === void 0 ? {} : { target_host: this.bootstrapHost }
|
|
8485
|
+
}))
|
|
8486
|
+
}).pipe(Effect.flatMap((res) => this.mapResponse(res)));
|
|
8487
|
+
}
|
|
8488
|
+
mapResponse(res) {
|
|
8489
|
+
if (res.ok) return Effect.tryPromise({
|
|
8490
|
+
try: async () => {
|
|
8491
|
+
const body = await res.json();
|
|
8492
|
+
const state = typeof body === "object" && body !== null && "state" in body && typeof body.state === "object" && body.state !== null ? body.state : void 0;
|
|
8493
|
+
const rawEngineeringTelemetry = state?.engineeringTelemetry;
|
|
8494
|
+
const baseBody = state === void 0 || rawEngineeringTelemetry === void 0 ? body : {
|
|
8495
|
+
...body,
|
|
8496
|
+
state: Object.fromEntries(Object.entries(state).filter(([key]) => key !== "engineeringTelemetry"))
|
|
8497
|
+
};
|
|
8498
|
+
const decoded = SchemaParser.decodeUnknownResult(BootstrapEnvelope)(baseBody);
|
|
8499
|
+
if (Result.isFailure(decoded)) throw Errors.invalidInput("bootstrapEnvelope", SchemaIssue.makeFormatterDefault()(decoded.failure));
|
|
8500
|
+
const { state: decodedState } = decoded.success;
|
|
8501
|
+
const decodedEngineeringTelemetry = rawEngineeringTelemetry === void 0 ? void 0 : SchemaParser.decodeUnknownResult(EngineeringTelemetryBootstrapPolicy)(rawEngineeringTelemetry, { onExcessProperty: "error" });
|
|
8502
|
+
return {
|
|
8503
|
+
applicationId: decodedState.applicationId,
|
|
8504
|
+
chainId: decodedState.chainId,
|
|
8505
|
+
sessionToken: decodedState.sessionToken,
|
|
8506
|
+
issuedAt: decodedState.issuedAt,
|
|
8507
|
+
expiresIn: decodedState.expiresIn,
|
|
8508
|
+
authBaseUrl: normalizeRuntimeUrl("authBaseUrl", decodedState.authBaseUrl),
|
|
8509
|
+
convexUrl: normalizeRuntimeUrl("convexUrl", decodedState.convexUrl),
|
|
8510
|
+
siteBaseUrl: normalizeRuntimeUrl("siteBaseUrl", decodedState.siteBaseUrl),
|
|
8511
|
+
openfortPublishableKey: decodedState.openfortPublishableKey,
|
|
8512
|
+
shieldPublishableKey: decodedState.shieldPublishableKey,
|
|
8513
|
+
...decodedEngineeringTelemetry !== void 0 && Result.isSuccess(decodedEngineeringTelemetry) ? { engineeringTelemetry: decodedEngineeringTelemetry.success } : {}
|
|
8514
|
+
};
|
|
8515
|
+
},
|
|
8516
|
+
catch: (cause) => {
|
|
8517
|
+
if (cause instanceof CapxulError && cause.code === "INVALID_INPUT") return bootstrapErrorFromCapxul("invalidInput", cause);
|
|
8518
|
+
return bootstrapErrorFromCapxul("malformedBody", Errors.providerError("convex", "bootstrap", cause instanceof Error ? cause : new Error(String(cause))));
|
|
8519
|
+
}
|
|
8520
|
+
});
|
|
8521
|
+
return Effect.promise(() => safeText(res)).pipe(Effect.flatMap((body) => {
|
|
8522
|
+
if (res.status === 401 || body.startsWith("NOT_AUTHENTICATED")) return Effect.fail(bootstrapErrorFromCapxul("notAuthenticated", Errors.notAuthenticated()));
|
|
8523
|
+
if (res.status === 400 || body.startsWith("INVALID_INPUT")) return Effect.fail(bootstrapErrorFromCapxul("invalidInput", Errors.invalidInput("publishableKey", "rejected by bootstrap")));
|
|
8524
|
+
const responseBody = body.slice(0, 300);
|
|
8525
|
+
const edgeError = res.headers?.get?.("x-vercel-error") ?? void 0;
|
|
8526
|
+
const edgeRequestId = res.headers?.get?.("x-vercel-id") ?? void 0;
|
|
8527
|
+
return Effect.fail(bootstrapErrorFromCapxul("provider", Errors.providerError("convex", "bootstrap", /* @__PURE__ */ new Error(`HTTP ${res.status}${edgeError === void 0 ? "" : ` ${edgeError}`}`), {
|
|
8528
|
+
httpStatus: res.status,
|
|
8529
|
+
details: {
|
|
8530
|
+
...responseBody.length === 0 ? {} : { responseBody },
|
|
8531
|
+
...edgeError === void 0 ? {} : { edgeError },
|
|
8532
|
+
...edgeRequestId === void 0 ? {} : { edgeRequestId }
|
|
8533
|
+
}
|
|
8534
|
+
})));
|
|
8535
|
+
}));
|
|
8536
|
+
}
|
|
8537
|
+
};
|
|
8538
|
+
function HttpBootstrapLayer(deps) {
|
|
8539
|
+
return Layer.succeed(BootstrapPortTag, new HttpBootstrapAdapter(deps));
|
|
8540
|
+
}
|
|
8541
|
+
function hostOf(url) {
|
|
8542
|
+
try {
|
|
8543
|
+
return new URL(url).hostname;
|
|
8544
|
+
} catch {
|
|
8545
|
+
return;
|
|
8546
|
+
}
|
|
8547
|
+
}
|
|
8548
|
+
function normalizeRuntimeUrl(field, raw) {
|
|
8549
|
+
let parsed;
|
|
8550
|
+
try {
|
|
8551
|
+
parsed = new URL(raw);
|
|
8552
|
+
} catch {
|
|
8553
|
+
throw Errors.invalidInput(field, "must be an http or https URL");
|
|
8554
|
+
}
|
|
8555
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") throw Errors.invalidInput(field, "must be an http or https URL");
|
|
8556
|
+
return parsed.toString().replace(/\/$/, "");
|
|
8557
|
+
}
|
|
8558
|
+
//#endregion
|
|
8559
|
+
//#region src/ports/convex-call.ts
|
|
8560
|
+
var ConvexCallError = class extends Data.TaggedError("ConvexCallError") {};
|
|
8561
|
+
function convexCallErrorFromCapxul(operation, error, transport) {
|
|
8562
|
+
return new ConvexCallError({
|
|
8563
|
+
operation,
|
|
8564
|
+
publicCode: error.code,
|
|
8565
|
+
publicError: error,
|
|
8566
|
+
cause: error,
|
|
8567
|
+
...error.details === void 0 ? {} : { details: error.details },
|
|
8568
|
+
...transport === void 0 ? {} : { transport }
|
|
8569
|
+
});
|
|
8570
|
+
}
|
|
8571
|
+
var ConvexCallPortTag = class extends Context.Service()("@capxul/sdk/ports/ConvexCallPort") {};
|
|
8572
|
+
//#endregion
|
|
8573
|
+
//#region src/adapters/convex-call/convex-connection-monitor.ts
|
|
8574
|
+
var ConvexConnectionMonitor = class {
|
|
8575
|
+
#connectionId = makeConnectionId();
|
|
8576
|
+
#browser = new BrowserLifecycleMonitor();
|
|
8577
|
+
#lastClose;
|
|
8578
|
+
observedWebSocketConstructor() {
|
|
8579
|
+
const NativeWebSocket = globalThis.WebSocket;
|
|
8580
|
+
if (typeof globalThis.window === "undefined" || NativeWebSocket === void 0) return void 0;
|
|
8581
|
+
return new Proxy(NativeWebSocket, { construct: (Target, args) => {
|
|
8582
|
+
const socket = Reflect.construct(Target, args);
|
|
8583
|
+
socket.addEventListener("close", (event) => {
|
|
8584
|
+
this.#lastClose = this.#browser.socketClose(event);
|
|
8585
|
+
});
|
|
8586
|
+
return socket;
|
|
8587
|
+
} });
|
|
8588
|
+
}
|
|
8589
|
+
close() {
|
|
8590
|
+
this.#browser.close();
|
|
8591
|
+
}
|
|
8592
|
+
observe(client, observer) {
|
|
8593
|
+
const subscribe = client.subscribeToConnectionState?.bind(client);
|
|
8594
|
+
if (subscribe === void 0) return () => this.#browser.close();
|
|
8595
|
+
let previous;
|
|
8596
|
+
const unsubscribe = subscribe((next) => {
|
|
8597
|
+
const diagnostics = this.#diagnostics(previous, next);
|
|
8598
|
+
previous = next;
|
|
8599
|
+
for (const diagnostic of diagnostics) emitDiagnostic(observer, diagnostic);
|
|
8600
|
+
});
|
|
8601
|
+
if (previous === void 0) {
|
|
8602
|
+
const current = safeConnectionState(client);
|
|
8603
|
+
if (current !== void 0) {
|
|
8604
|
+
previous = current;
|
|
8605
|
+
for (const diagnostic of this.#diagnostics(void 0, current)) emitDiagnostic(observer, diagnostic);
|
|
8606
|
+
}
|
|
8607
|
+
}
|
|
8608
|
+
let closed = false;
|
|
8609
|
+
return () => {
|
|
8610
|
+
if (closed) return;
|
|
8611
|
+
closed = true;
|
|
8612
|
+
try {
|
|
8613
|
+
unsubscribe();
|
|
8614
|
+
} finally {
|
|
8615
|
+
this.#browser.close();
|
|
8616
|
+
}
|
|
8617
|
+
};
|
|
8618
|
+
}
|
|
8619
|
+
reconnectEvidence(start, end) {
|
|
8620
|
+
if (!connectionRestarted(start, end)) return void 0;
|
|
8621
|
+
return {
|
|
8622
|
+
kind: "connection-lost-in-flight",
|
|
8623
|
+
connection_id: this.#connectionId,
|
|
8624
|
+
start_connection_count: start.connectionCount,
|
|
8625
|
+
connection_count: end.connectionCount,
|
|
8626
|
+
...end.connectionRetries === void 0 ? {} : { connection_retries: end.connectionRetries },
|
|
8627
|
+
...closeFields(this.#lastClose)
|
|
8628
|
+
};
|
|
8629
|
+
}
|
|
8630
|
+
#diagnostics(previous, next) {
|
|
8631
|
+
const transitions = connectionTransitions(previous, next);
|
|
8632
|
+
if (transitions.length === 0) return [];
|
|
8633
|
+
const oldest = next.timeOfOldestInflightRequest;
|
|
8634
|
+
const oldestInflightMs = oldest instanceof Date && Number.isFinite(oldest.getTime()) ? Math.max(0, Date.now() - oldest.getTime()) : void 0;
|
|
8635
|
+
return transitions.map((transition) => ({
|
|
8636
|
+
connection_id: this.#connectionId,
|
|
8637
|
+
transition,
|
|
8638
|
+
previous_connected: previous?.isWebSocketConnected ?? false,
|
|
8639
|
+
connected: next.isWebSocketConnected,
|
|
8640
|
+
...next.hasEverConnected === void 0 ? {} : { has_ever_connected: next.hasEverConnected },
|
|
8641
|
+
connection_count: next.connectionCount,
|
|
8642
|
+
...next.connectionRetries === void 0 ? {} : { connection_retries: next.connectionRetries },
|
|
8643
|
+
...next.hasInflightRequests === void 0 ? {} : { has_inflight_requests: next.hasInflightRequests },
|
|
8644
|
+
...next.inflightActions === void 0 ? {} : { inflight_actions: next.inflightActions },
|
|
8645
|
+
...next.inflightMutations === void 0 ? {} : { inflight_mutations: next.inflightMutations },
|
|
8646
|
+
...oldestInflightMs === void 0 ? {} : { oldest_inflight_ms: oldestInflightMs },
|
|
8647
|
+
...closeFields(this.#lastClose)
|
|
8648
|
+
}));
|
|
8649
|
+
}
|
|
8650
|
+
};
|
|
8651
|
+
function connectionRestarted(start, end) {
|
|
8652
|
+
if (start === void 0 || end === void 0) return false;
|
|
8653
|
+
const opensBeforeCancellation = start.isWebSocketConnected ? 1 : 2;
|
|
8654
|
+
return end.connectionCount >= start.connectionCount + opensBeforeCancellation;
|
|
8655
|
+
}
|
|
8656
|
+
function connectionTransitions(previous, next) {
|
|
8657
|
+
if (previous === void 0) {
|
|
8658
|
+
if (next.isWebSocketConnected) return (next.connectionRetries ?? 0) > 0 ? ["retrying", "connected"] : ["connected"];
|
|
8659
|
+
return (next.connectionRetries ?? 0) > 0 ? ["disconnected", "retrying"] : [];
|
|
8660
|
+
}
|
|
8661
|
+
const transitions = [];
|
|
8662
|
+
if (previous.isWebSocketConnected !== next.isWebSocketConnected) transitions.push(next.isWebSocketConnected ? "connected" : "disconnected");
|
|
8663
|
+
if ((next.connectionRetries ?? 0) > (previous.connectionRetries ?? 0)) transitions.push("retrying");
|
|
8664
|
+
return transitions;
|
|
8665
|
+
}
|
|
8666
|
+
function emitDiagnostic(observer, diagnostic) {
|
|
8667
|
+
try {
|
|
8668
|
+
observer(diagnostic);
|
|
8669
|
+
} catch {}
|
|
8670
|
+
}
|
|
8671
|
+
function safeConnectionState(client) {
|
|
8672
|
+
try {
|
|
8673
|
+
return client.connectionState?.();
|
|
8674
|
+
} catch {
|
|
8675
|
+
return;
|
|
8676
|
+
}
|
|
8677
|
+
}
|
|
8678
|
+
function closeFields(close) {
|
|
8679
|
+
if (close === void 0) return {};
|
|
8680
|
+
return {
|
|
8681
|
+
close_code: close.code,
|
|
8682
|
+
close_was_clean: close.wasClean,
|
|
8683
|
+
close_reason_present: close.reasonPresent,
|
|
8684
|
+
...close.documentVisibility === void 0 ? {} : { document_visibility: close.documentVisibility },
|
|
8685
|
+
...close.navigatorOnline === void 0 ? {} : { navigator_online: close.navigatorOnline },
|
|
8686
|
+
...close.browserEvent === void 0 ? {} : { last_browser_event: close.browserEvent },
|
|
8687
|
+
...close.msSinceBrowserEvent === void 0 ? {} : { ms_since_browser_event: close.msSinceBrowserEvent }
|
|
8688
|
+
};
|
|
8689
|
+
}
|
|
8690
|
+
var BrowserLifecycleMonitor = class {
|
|
8691
|
+
#listeners = [];
|
|
8692
|
+
#last = {};
|
|
8693
|
+
constructor() {
|
|
8694
|
+
const windowTarget = eventTarget(globalThis.window);
|
|
8695
|
+
const documentTarget = eventTarget(globalThis.document);
|
|
8696
|
+
if (windowTarget === void 0 || documentTarget === void 0) return;
|
|
8697
|
+
this.#listen(documentTarget, "visibilitychange", () => {
|
|
8698
|
+
this.#record(globalThis.document?.visibilityState === "hidden" ? "visibility-hidden" : "visibility-visible");
|
|
8699
|
+
});
|
|
8700
|
+
this.#listen(windowTarget, "pagehide", () => this.#record("pagehide"));
|
|
8701
|
+
this.#listen(windowTarget, "pageshow", () => this.#record("pageshow"));
|
|
8702
|
+
this.#listen(windowTarget, "online", () => this.#record("online"));
|
|
8703
|
+
this.#listen(windowTarget, "offline", () => this.#record("offline"));
|
|
8704
|
+
}
|
|
8705
|
+
socketClose(event) {
|
|
8706
|
+
const now = Date.now();
|
|
8707
|
+
return {
|
|
8708
|
+
code: event.code,
|
|
8709
|
+
wasClean: event.wasClean,
|
|
8710
|
+
reasonPresent: event.reason.length > 0,
|
|
8711
|
+
...this.#last.event === void 0 ? {} : { browserEvent: this.#last.event },
|
|
8712
|
+
...this.#last.at === void 0 ? {} : { msSinceBrowserEvent: Math.max(0, now - this.#last.at) },
|
|
8713
|
+
...globalThis.document?.visibilityState === void 0 ? {} : { documentVisibility: globalThis.document.visibilityState },
|
|
8714
|
+
...globalThis.navigator?.onLine === void 0 ? {} : { navigatorOnline: globalThis.navigator.onLine }
|
|
8715
|
+
};
|
|
8716
|
+
}
|
|
8717
|
+
close() {
|
|
8718
|
+
for (const [target, name, listener] of this.#listeners) target.removeEventListener(name, listener);
|
|
8719
|
+
this.#listeners.length = 0;
|
|
8720
|
+
}
|
|
8721
|
+
#listen(target, name, listener) {
|
|
8722
|
+
target.addEventListener(name, listener);
|
|
8723
|
+
this.#listeners.push([
|
|
8724
|
+
target,
|
|
8725
|
+
name,
|
|
8726
|
+
listener
|
|
8727
|
+
]);
|
|
8728
|
+
}
|
|
8729
|
+
#record(event) {
|
|
8730
|
+
this.#last = {
|
|
8731
|
+
event,
|
|
8732
|
+
at: Date.now()
|
|
8733
|
+
};
|
|
8734
|
+
}
|
|
8735
|
+
};
|
|
8736
|
+
function eventTarget(value) {
|
|
8737
|
+
if (typeof value !== "object" || value === null || !("addEventListener" in value) || typeof value.addEventListener !== "function" || !("removeEventListener" in value) || typeof value.removeEventListener !== "function") return;
|
|
8738
|
+
return value;
|
|
8739
|
+
}
|
|
8740
|
+
let fallbackConnectionId = 0;
|
|
8741
|
+
function makeConnectionId() {
|
|
8742
|
+
try {
|
|
8743
|
+
return `convex_${globalThis.crypto.randomUUID()}`;
|
|
8744
|
+
} catch {
|
|
8745
|
+
fallbackConnectionId += 1;
|
|
8746
|
+
return `convex_${Date.now().toString(36)}_${fallbackConnectionId.toString(36)}`;
|
|
8747
|
+
}
|
|
8748
|
+
}
|
|
8749
|
+
//#endregion
|
|
8750
|
+
//#region src/adapters/convex-call/ConvexCallAdapter.ts
|
|
8751
|
+
/** Exact floor-first allowlist; every additional handler must migrate its validator first. */
|
|
8752
|
+
const OBSERVED_CONVEX_ACTIONS = /* @__PURE__ */ new Set([
|
|
8753
|
+
"payroll/actions:authorizeRun",
|
|
8754
|
+
"account/actions:readBalance",
|
|
8755
|
+
"holdings/actions:current",
|
|
8756
|
+
"smartAccount/actions:claim",
|
|
8757
|
+
"org/actions:prepareFounderAccount",
|
|
8758
|
+
"org/actions:prepareBootstrap",
|
|
8759
|
+
"org/actions:submitBootstrap",
|
|
8760
|
+
"org/actions:resumeBootstrapSubmission",
|
|
8761
|
+
"org/actions:confirmBootstrap",
|
|
8762
|
+
"org/actions:readTreasury",
|
|
8763
|
+
"moneyExecution/actions:preparePaymentExecution",
|
|
8764
|
+
"moneyExecution/actions:abandonPaymentExecution",
|
|
8765
|
+
"moneyExecution/actions:submitPaymentExecution",
|
|
8766
|
+
"moneyExecution/paymentCommandActions:preparePaymentLifecycleExecution",
|
|
8767
|
+
"moneyExecution/paymentCommandActions:prepareOrganizationPaymentExecution",
|
|
8768
|
+
"moneyExecution/paymentCommandActions:submitPaymentCommandExecution"
|
|
8769
|
+
]);
|
|
8770
|
+
const OBSERVED_CONVEX_QUERIES = /* @__PURE__ */ new Set([
|
|
8771
|
+
"movement/activity:list",
|
|
8772
|
+
"movement/activity:summary",
|
|
8773
|
+
"movement/activity:get",
|
|
8774
|
+
"financialOps/queries:depositInstructions",
|
|
8775
|
+
"financialOps/destinations:list",
|
|
8776
|
+
"financialOps/queries:verifyPaymentDocument",
|
|
8777
|
+
"financialOps/queries:renderStoredDocument",
|
|
8778
|
+
"financialOps/addressBook:list",
|
|
8779
|
+
"financialOps/addressBook:get",
|
|
8780
|
+
"financialOps/requestsInbox:list",
|
|
8781
|
+
"financialOps/requestsInbox:get",
|
|
8782
|
+
"financialOps/requestsInbox:inboxList",
|
|
8783
|
+
"payroll/queries:runs",
|
|
8784
|
+
"payroll/queries:groups",
|
|
8785
|
+
"payroll/queries:terms",
|
|
8786
|
+
"financialOps/queries:resolveRecipient",
|
|
8787
|
+
"identity/queries:loadByAuthUserId",
|
|
8788
|
+
"smartAccount/queries:loadByAuthUserId",
|
|
8789
|
+
"org/lifecycle:load"
|
|
8790
|
+
]);
|
|
8791
|
+
const OBSERVED_CONVEX_MUTATIONS = /* @__PURE__ */ new Set([
|
|
8792
|
+
"movement/activity:annotate",
|
|
8793
|
+
"financialOps/destinations:add",
|
|
8794
|
+
"financialOps/destinations:remove",
|
|
8795
|
+
"financialOps/addressBook:add",
|
|
8796
|
+
"financialOps/addressBook:hide",
|
|
8797
|
+
"financialOps/addressBook:unhide",
|
|
8798
|
+
"financialOps/addressBook:label",
|
|
8799
|
+
"financialOps/requestsInbox:issue",
|
|
8800
|
+
"financialOps/requestsInbox:cancel",
|
|
8801
|
+
"financialOps/requestsInbox:approve",
|
|
8802
|
+
"financialOps/requestsInbox:decline",
|
|
8803
|
+
"payroll/mutations:saveGroup",
|
|
8804
|
+
"payroll/mutations:removeGroup",
|
|
8805
|
+
"identity/mutations:create",
|
|
8806
|
+
"identity/mutations:update",
|
|
8807
|
+
"identity/mutations:completeOnboarding",
|
|
8808
|
+
"smartAccount/mutations:provision",
|
|
8809
|
+
"org/lifecycle:startOrResume",
|
|
8810
|
+
"org/lifecycle:recordFailure",
|
|
8811
|
+
"org/lifecycle:retry"
|
|
8812
|
+
]);
|
|
8813
|
+
var ConvexCallAdapter = class {
|
|
8814
|
+
#client;
|
|
8815
|
+
#tokenProvider;
|
|
8816
|
+
#applicationId;
|
|
8817
|
+
#observation;
|
|
8818
|
+
#connectionMonitor;
|
|
8819
|
+
constructor(deps) {
|
|
8820
|
+
this.#connectionMonitor = new ConvexConnectionMonitor();
|
|
8821
|
+
const webSocketConstructor = this.#connectionMonitor.observedWebSocketConstructor();
|
|
8822
|
+
this.#client = deps.client ?? new ConvexClient(deps.convexUrl, webSocketConstructor === void 0 ? {} : { webSocketConstructor });
|
|
8823
|
+
this.#tokenProvider = deps.tokenProvider;
|
|
8824
|
+
this.#applicationId = deps.applicationId;
|
|
8825
|
+
this.#observation = deps.observation;
|
|
8826
|
+
if (this.#tokenProvider) this.#client.setAuth(this.#tokenProvider);
|
|
8827
|
+
}
|
|
8828
|
+
refreshAuth() {
|
|
8829
|
+
if (this.#tokenProvider) this.#client.setAuth(this.#tokenProvider);
|
|
8830
|
+
}
|
|
8831
|
+
observeConnection(observer) {
|
|
8832
|
+
return this.#connectionMonitor.observe(this.#client, observer);
|
|
8833
|
+
}
|
|
8834
|
+
query(fn, args) {
|
|
8835
|
+
const path = getFunctionName(fn);
|
|
8836
|
+
return Effect.serviceOption(Tracer.ParentSpan).pipe(Effect.flatMap((parent) => {
|
|
8837
|
+
const traceparent = parent._tag === "Some" ? formatTraceparent(parent.value) : void 0;
|
|
8838
|
+
return Effect.tryPromise({
|
|
8839
|
+
try: () => this.#client.query(fn, this.#observedArgs(OBSERVED_CONVEX_QUERIES, path, args, traceparent)),
|
|
8840
|
+
catch: (cause) => mapToConvexCallError(path, cause)
|
|
8841
|
+
});
|
|
8842
|
+
}));
|
|
8843
|
+
}
|
|
8844
|
+
mutation(fn, args) {
|
|
8845
|
+
const path = getFunctionName(fn);
|
|
8846
|
+
return Effect.serviceOption(Tracer.ParentSpan).pipe(Effect.flatMap((parent) => {
|
|
8847
|
+
const traceparent = parent._tag === "Some" ? formatTraceparent(parent.value) : void 0;
|
|
8848
|
+
return Effect.tryPromise({
|
|
8849
|
+
try: () => this.#client.mutation(fn, this.#observedArgs(OBSERVED_CONVEX_MUTATIONS, path, args, traceparent)),
|
|
8850
|
+
catch: (cause) => mapToConvexCallError(path, cause)
|
|
8851
|
+
});
|
|
8852
|
+
}));
|
|
8853
|
+
}
|
|
8854
|
+
action(fn, args) {
|
|
8855
|
+
const path = getFunctionName(fn);
|
|
8856
|
+
return Effect.serviceOption(Tracer.ParentSpan).pipe(Effect.flatMap((parent) => {
|
|
8857
|
+
const traceparent = parent._tag === "Some" ? formatTraceparent(parent.value) : void 0;
|
|
8858
|
+
const connectionAtStart = connectionState(this.#client);
|
|
8859
|
+
return Effect.tryPromise({
|
|
8860
|
+
try: () => this.#client.action(fn, this.#observedArgs(OBSERVED_CONVEX_ACTIONS, path, args, traceparent)),
|
|
8861
|
+
catch: (cause) => {
|
|
8862
|
+
const transport = this.#connectionMonitor.reconnectEvidence(connectionAtStart, connectionState(this.#client));
|
|
8863
|
+
return mapToConvexCallError(path, cause, transport);
|
|
8864
|
+
}
|
|
8865
|
+
}).pipe(Effect.tapError((error) => logActionTransportFailure(path, error)));
|
|
8866
|
+
}));
|
|
8867
|
+
}
|
|
8868
|
+
#observedArgs(allowlist, path, args, traceparent) {
|
|
8869
|
+
if (!allowlist.has(path)) return args;
|
|
8870
|
+
const carriedContext = sanitizeObservationContext(args.observationContext);
|
|
8871
|
+
let hostContext;
|
|
8872
|
+
const invocationSnapshot = readInvocationObservation(args);
|
|
8873
|
+
if (invocationSnapshot !== void 0) hostContext = invocationSnapshot.active ? invocationSnapshot.context : void 0;
|
|
8874
|
+
else {
|
|
8875
|
+
const resolveContext = this.#observation?.resolveContext;
|
|
8876
|
+
if (resolveContext !== void 0) try {
|
|
8877
|
+
hostContext = resolveContext();
|
|
8878
|
+
} catch {
|
|
8879
|
+
hostContext = void 0;
|
|
8880
|
+
}
|
|
8881
|
+
}
|
|
8882
|
+
if (hostContext === void 0 && carriedContext === void 0 && traceparent === void 0) return args;
|
|
8883
|
+
hostContext = sanitizeObservationContext({
|
|
8884
|
+
...hostContext,
|
|
8885
|
+
...carriedContext,
|
|
8886
|
+
...this.#applicationId === void 0 ? {} : { applicationId: this.#applicationId },
|
|
8887
|
+
...traceparent === void 0 ? {} : { traceparent }
|
|
8888
|
+
});
|
|
8889
|
+
if (hostContext === void 0) return args;
|
|
8890
|
+
return {
|
|
8891
|
+
...args,
|
|
8892
|
+
observationContext: hostContext
|
|
8893
|
+
};
|
|
8894
|
+
}
|
|
8895
|
+
subscribe(fn, args, callback) {
|
|
8896
|
+
return Effect.try({
|
|
8897
|
+
try: () => {
|
|
8898
|
+
const path = getFunctionName(fn);
|
|
8899
|
+
const unsubscribe = this.#client.onUpdate(fn, args, (value) => callback({
|
|
8900
|
+
status: "ok",
|
|
8901
|
+
value
|
|
8902
|
+
}), (err) => callback({
|
|
8903
|
+
status: "error",
|
|
8904
|
+
error: mapToCapxulError(path, err)
|
|
8905
|
+
}));
|
|
8906
|
+
let active = true;
|
|
8907
|
+
return () => {
|
|
8908
|
+
if (!active) return;
|
|
8909
|
+
active = false;
|
|
8910
|
+
unsubscribe();
|
|
8911
|
+
};
|
|
8912
|
+
},
|
|
8913
|
+
catch: (cause) => mapToConvexCallError(getFunctionName(fn), cause)
|
|
8914
|
+
}).pipe(Effect.tap(() => Effect.sync(() => {
|
|
8915
|
+
callback({ status: "loading" });
|
|
8916
|
+
})));
|
|
8917
|
+
}
|
|
8918
|
+
async close() {
|
|
8919
|
+
this.#connectionMonitor.close();
|
|
8920
|
+
await this.#client.close();
|
|
8921
|
+
}
|
|
8922
|
+
};
|
|
8923
|
+
function ConvexCallLayer(deps) {
|
|
8924
|
+
return Layer.effect(ConvexCallPortTag, Effect.acquireRelease(Effect.sync(() => new ConvexCallAdapter(deps)), (adapter) => Effect.promise(() => adapter.close()).pipe(Effect.orDie)));
|
|
8925
|
+
}
|
|
8926
|
+
function mapToCapxulError(operation, err, transport) {
|
|
8927
|
+
const decoded = decodeConvexError(err);
|
|
8928
|
+
if (decoded !== null) return decoded;
|
|
8929
|
+
if (err instanceof CapxulError) return err;
|
|
8930
|
+
if (transport !== void 0) {
|
|
8931
|
+
const { kind: _kind, start_connection_count: _start, ...connection } = transport;
|
|
8932
|
+
return Errors.networkError(operation, err, {
|
|
8933
|
+
provider: "convex",
|
|
8934
|
+
failure_mode: "upstream-down",
|
|
8935
|
+
reason: "connection-lost-in-flight",
|
|
8936
|
+
...connection
|
|
8937
|
+
});
|
|
8938
|
+
}
|
|
8939
|
+
if (err instanceof Error) {
|
|
8940
|
+
if (isTransportError(err)) return Errors.networkError(operation, err, { provider: "convex" });
|
|
8941
|
+
return Errors.providerError("convex", operation, err);
|
|
8942
|
+
}
|
|
8943
|
+
return Errors.providerError("convex", operation, new Error(String(err)));
|
|
8944
|
+
}
|
|
8945
|
+
function mapToConvexCallError(operation, err, transport) {
|
|
8946
|
+
return convexCallErrorFromCapxul(operation, mapToCapxulError(operation, err, transport), transport);
|
|
8947
|
+
}
|
|
8948
|
+
function connectionState(client) {
|
|
8949
|
+
try {
|
|
8950
|
+
const state = client.connectionState?.();
|
|
8951
|
+
return typeof state?.connectionCount === "number" ? state : void 0;
|
|
8952
|
+
} catch {
|
|
8953
|
+
return;
|
|
8954
|
+
}
|
|
8955
|
+
}
|
|
8956
|
+
function logActionTransportFailure(operation, error) {
|
|
8957
|
+
if (error.transport === void 0) return Effect.void;
|
|
8958
|
+
const { kind, ...connection } = error.transport;
|
|
8959
|
+
return Effect.logWarning("convex.action.connection_lost").pipe(Effect.annotateLogs({
|
|
8960
|
+
operation,
|
|
8961
|
+
failure_mode: "upstream-down",
|
|
8962
|
+
reason: kind,
|
|
8963
|
+
...connection
|
|
8964
|
+
}), Effect.catchCause(() => Effect.void));
|
|
8965
|
+
}
|
|
8966
|
+
const TRANSPORT_ERROR_CODES = /* @__PURE__ */ new Set([
|
|
8967
|
+
"EAI_AGAIN",
|
|
8968
|
+
"ECONNREFUSED",
|
|
8969
|
+
"ECONNRESET",
|
|
8970
|
+
"ENOTFOUND",
|
|
8971
|
+
"EPIPE",
|
|
8972
|
+
"ETIMEDOUT",
|
|
8973
|
+
"UND_ERR_CONNECT_TIMEOUT",
|
|
8974
|
+
"UND_ERR_HEADERS_TIMEOUT",
|
|
8975
|
+
"UND_ERR_SOCKET"
|
|
8976
|
+
]);
|
|
8977
|
+
function isTransportError(err) {
|
|
8978
|
+
const seen = /* @__PURE__ */ new Set();
|
|
8979
|
+
let current = err;
|
|
8980
|
+
while (current !== void 0 && !seen.has(current)) {
|
|
8981
|
+
seen.add(current);
|
|
8982
|
+
const code = current.code;
|
|
8983
|
+
if (current.name === "FetchError" || current.name === "NetworkError" || typeof code === "string" && TRANSPORT_ERROR_CODES.has(code)) return true;
|
|
8984
|
+
current = current.cause instanceof Error ? current.cause : void 0;
|
|
8985
|
+
}
|
|
8986
|
+
return false;
|
|
8987
|
+
}
|
|
8988
|
+
//#endregion
|
|
8989
|
+
//#region src/ports/identity.ts
|
|
8990
|
+
/** The chain families a payout address may name (#1955). */
|
|
8991
|
+
const PAYOUT_CHAINS = [
|
|
8992
|
+
"evm",
|
|
8993
|
+
"solana",
|
|
8994
|
+
"starknet"
|
|
8995
|
+
];
|
|
8996
|
+
var IdentityError = class extends Data.TaggedError("IdentityError") {};
|
|
8997
|
+
function identityErrorFromCapxul(operation, error, cause = error) {
|
|
8998
|
+
return new IdentityError({
|
|
8999
|
+
operation,
|
|
9000
|
+
publicCode: error.code,
|
|
9001
|
+
publicError: error,
|
|
9002
|
+
cause,
|
|
9003
|
+
...error.details === void 0 ? {} : { details: error.details }
|
|
9004
|
+
});
|
|
9005
|
+
}
|
|
9006
|
+
var IdentityPortTag = class extends Context.Service()("@capxul/sdk/ports/IdentityPort") {};
|
|
9007
|
+
//#endregion
|
|
9008
|
+
//#region src/adapters/identity/ConvexIdentityAdapter.ts
|
|
9009
|
+
const identityLoadByAuthUserIdQuery = makeFunctionReference(CAPXUL_FUNCTIONS["identity/queries"].loadByAuthUserId);
|
|
9010
|
+
const identityCreateMutation = makeFunctionReference(CAPXUL_FUNCTIONS["identity/mutations"].create);
|
|
9011
|
+
const identityUpdateMutation = makeFunctionReference(CAPXUL_FUNCTIONS["identity/mutations"].update);
|
|
9012
|
+
const identityCompleteOnboardingMutation = makeFunctionReference(CAPXUL_FUNCTIONS["identity/mutations"].completeOnboarding);
|
|
9013
|
+
var ConvexIdentityAdapter = class {
|
|
9014
|
+
#convex;
|
|
9015
|
+
constructor(deps) {
|
|
9016
|
+
this.#convex = deps.convex;
|
|
9017
|
+
}
|
|
9018
|
+
loadByAuthUserId(authUserId) {
|
|
9019
|
+
return this.#convex.query(identityLoadByAuthUserIdQuery, { authUserId }).pipe(Effect.mapError((error) => identityErrorFromCapxul("loadByAuthUserId", error.publicError, error)), Effect.flatMap((row) => Effect.try({
|
|
9020
|
+
try: () => row === null ? null : brandProfile(row),
|
|
9021
|
+
catch: (cause) => identityErrorFromUnknown("loadByAuthUserId", cause)
|
|
9022
|
+
})), Effect.catchDefect((cause) => Effect.fail(identityErrorFromUnknown("loadByAuthUserId", cause))));
|
|
9023
|
+
}
|
|
9024
|
+
create(input) {
|
|
9025
|
+
return this.#convex.mutation(identityCreateMutation, input).pipe(Effect.mapError((error) => identityErrorFromCapxul("create", error.publicError, error)), Effect.flatMap((row) => Effect.try({
|
|
9026
|
+
try: () => brandProfile(row),
|
|
9027
|
+
catch: (cause) => identityErrorFromUnknown("create", cause)
|
|
9028
|
+
})), Effect.catchDefect((cause) => Effect.fail(identityErrorFromUnknown("create", cause))));
|
|
9029
|
+
}
|
|
9030
|
+
update(input) {
|
|
9031
|
+
return this.#convex.mutation(identityUpdateMutation, input).pipe(Effect.mapError((error) => identityErrorFromCapxul("update", error.publicError, error)), Effect.flatMap((row) => Effect.try({
|
|
9032
|
+
try: () => brandProfile(row),
|
|
9033
|
+
catch: (cause) => identityErrorFromUnknown("update", cause)
|
|
9034
|
+
})), Effect.catchDefect((cause) => Effect.fail(identityErrorFromUnknown("update", cause))));
|
|
9035
|
+
}
|
|
9036
|
+
completeOnboarding(input) {
|
|
9037
|
+
return this.#convex.mutation(identityCompleteOnboardingMutation, input).pipe(Effect.mapError((error) => identityErrorFromCapxul("completeOnboarding", error.publicError, error)), Effect.flatMap((row) => Effect.try({
|
|
9038
|
+
try: () => brandProfile(row),
|
|
9039
|
+
catch: (cause) => identityErrorFromUnknown("completeOnboarding", cause)
|
|
9040
|
+
})), Effect.catchDefect((cause) => Effect.fail(identityErrorFromUnknown("completeOnboarding", cause))));
|
|
9041
|
+
}
|
|
9042
|
+
};
|
|
9043
|
+
function ConvexIdentityLayer() {
|
|
9044
|
+
return Layer.effect(IdentityPortTag, Effect.map(ConvexCallPortTag, (convex) => new ConvexIdentityAdapter({ convex })));
|
|
9045
|
+
}
|
|
9046
|
+
function brandProfile(raw) {
|
|
9047
|
+
return {
|
|
9048
|
+
authUserId: toAuthUserId(raw.authUserId),
|
|
9049
|
+
...raw.testerKind === void 0 ? {} : { testerKind: toTesterKind(raw.testerKind) },
|
|
9050
|
+
email: toEmail(raw.email),
|
|
9051
|
+
displayName: raw.displayName,
|
|
9052
|
+
country: raw.country === null ? null : toCountryCode(raw.country),
|
|
9053
|
+
onboarded: raw.onboarded ?? false,
|
|
9054
|
+
withdrawalAddress: raw.withdrawalAddress === null || raw.withdrawalAddress === void 0 ? null : toAddress(raw.withdrawalAddress),
|
|
9055
|
+
handle: raw.handle === null || raw.handle === void 0 ? null : toHandle(raw.handle),
|
|
9056
|
+
imageUrl: raw.imageUrl ?? null,
|
|
9057
|
+
kycTier: toKycTier(raw.kycTier),
|
|
9058
|
+
createdAt: toEpochMs(raw.createdAt),
|
|
9059
|
+
updatedAt: toEpochMs(raw.updatedAt)
|
|
9060
|
+
};
|
|
9061
|
+
}
|
|
9062
|
+
function identityErrorFromUnknown(operation, cause) {
|
|
9063
|
+
if (cause instanceof CapxulError) return identityErrorFromCapxul(operation, cause);
|
|
9064
|
+
return identityErrorFromCapxul(operation, Errors.providerError("convex", operation, cause), cause);
|
|
9065
|
+
}
|
|
9066
|
+
//#endregion
|
|
9067
|
+
//#region src/adapters/_shared/wire.ts
|
|
9068
|
+
/**
|
|
9069
|
+
* Documented brand-erasure helpers for the wire boundary.
|
|
9070
|
+
*
|
|
9071
|
+
* Branded primitives (`Address`, `ChainId`) carry compile-time tags that
|
|
9072
|
+
* vanish at runtime. When sending values across a wire — Convex function
|
|
9073
|
+
* args, viem's template-literal-typed parameters, raw JSON — we have to
|
|
9074
|
+
* remove the brand at the type level so the wire-side type system accepts
|
|
9075
|
+
* the value.
|
|
9076
|
+
*
|
|
9077
|
+
* Using these helpers (rather than ad-hoc `as number` / `as \`0x${string}\``
|
|
9078
|
+
* casts) localizes every de-branding site. If we ever need to centralize
|
|
9079
|
+
* validation (e.g. "must not de-brand a placeholder address"), the change
|
|
9080
|
+
* lands here once instead of grep-and-replace across every adapter.
|
|
9081
|
+
*/
|
|
9082
|
+
/**
|
|
9083
|
+
* De-brand a `ChainId` for the wire. The runtime representation is already
|
|
9084
|
+
* `number` — the cast removes the compile-time brand only.
|
|
9085
|
+
*/
|
|
9086
|
+
function wireChainId(chainId) {
|
|
9087
|
+
return chainId;
|
|
9088
|
+
}
|
|
9089
|
+
//#endregion
|
|
9090
|
+
//#region src/telemetry/invocation.ts
|
|
9091
|
+
const PRODUCT_INVOCATION = Symbol("capxul.product-telemetry-invocation");
|
|
9092
|
+
function bindProductTelemetryInvocation(telemetry, source) {
|
|
9093
|
+
return telemetry[PRODUCT_INVOCATION]?.(source) ?? telemetry;
|
|
9094
|
+
}
|
|
9095
|
+
//#endregion
|
|
9096
|
+
//#region src/adapters/convex-call/retry-idempotent-read.ts
|
|
9097
|
+
const MAX_RETRIES = 2;
|
|
9098
|
+
function isConnectionLoss(error) {
|
|
9099
|
+
return error.publicCode === "NETWORK_ERROR" && error.publicError.mode === "upstream-down" && error.publicError.details?.reason === "connection-lost-in-flight";
|
|
9100
|
+
}
|
|
9101
|
+
function retryIdempotentRead(effect, operation, telemetry, invocationSource) {
|
|
9102
|
+
return Effect.suspend(() => {
|
|
9103
|
+
let attempt = 0;
|
|
9104
|
+
const invocationTelemetry = telemetry === void 0 ? void 0 : bindProductTelemetryInvocation(telemetry, invocationSource);
|
|
9105
|
+
return effect.pipe(Effect.tapError((error) => {
|
|
9106
|
+
if (!isConnectionLoss(error) || attempt >= MAX_RETRIES) return Effect.void;
|
|
9107
|
+
attempt += 1;
|
|
9108
|
+
const eventFields = {
|
|
9109
|
+
operation,
|
|
9110
|
+
failure_mode: "upstream-down",
|
|
9111
|
+
reason: "connection-lost-in-flight",
|
|
9112
|
+
attempt,
|
|
9113
|
+
delay_ms: 0,
|
|
9114
|
+
...error.transport === void 0 ? {} : {
|
|
9115
|
+
connection_id: error.transport.connection_id,
|
|
9116
|
+
connection_count: error.transport.connection_count
|
|
9117
|
+
}
|
|
9118
|
+
};
|
|
9119
|
+
const transport = error.transport;
|
|
9120
|
+
const connection = transport === void 0 ? void 0 : omitTransportKind(transport);
|
|
9121
|
+
const log = Effect.logInfo("capxul.operation.retried").pipe(Effect.annotateLogs(connection === void 0 ? eventFields : {
|
|
9122
|
+
...eventFields,
|
|
9123
|
+
...connection
|
|
9124
|
+
}), Effect.catchCause(() => Effect.void));
|
|
9125
|
+
if (invocationTelemetry === void 0) return log;
|
|
9126
|
+
return log.pipe(Effect.andThen(invocationTelemetry.emit({
|
|
9127
|
+
name: "operation_retried",
|
|
9128
|
+
props: eventFields
|
|
9129
|
+
})), Effect.catchCause(() => Effect.void));
|
|
9130
|
+
}), Effect.retry({
|
|
9131
|
+
times: MAX_RETRIES,
|
|
9132
|
+
while: isConnectionLoss
|
|
9133
|
+
}));
|
|
9134
|
+
});
|
|
9135
|
+
}
|
|
9136
|
+
function omitTransportKind(transport) {
|
|
9137
|
+
const { kind: _kind, ...connection } = transport;
|
|
9138
|
+
return connection;
|
|
9139
|
+
}
|
|
9140
|
+
//#endregion
|
|
9141
|
+
//#region src/ports/telemetry.ts
|
|
9142
|
+
var TelemetryPortTag = class extends Context.Service()("@capxul/sdk/ports/TelemetryPort") {};
|
|
9143
|
+
//#endregion
|
|
9144
|
+
//#region src/ports/account-read.ts
|
|
9145
|
+
var AccountReadError = class extends Data.TaggedError("AccountReadError") {};
|
|
9146
|
+
function accountReadErrorFromCapxul(operation, error, cause = error) {
|
|
9147
|
+
return new AccountReadError({
|
|
9148
|
+
operation,
|
|
9149
|
+
publicCode: error.code,
|
|
9150
|
+
publicError: error,
|
|
9151
|
+
cause,
|
|
9152
|
+
...error.details === void 0 ? {} : { details: error.details }
|
|
9153
|
+
});
|
|
9154
|
+
}
|
|
9155
|
+
var AccountReadPortTag = class extends Context.Service()("@capxul/sdk/ports/AccountReadPort") {};
|
|
9156
|
+
//#endregion
|
|
9157
|
+
//#region src/adapters/account-read/ConvexAccountAdapter.ts
|
|
9158
|
+
const DEFAULT_FUNCTIONS$3 = {
|
|
9159
|
+
readBalance: makeFunctionReference(CAPXUL_FUNCTIONS["account/actions"].readBalance),
|
|
9160
|
+
faucetMint: makeFunctionReference(CAPXUL_FUNCTIONS["account/actions"].faucetMint)
|
|
9161
|
+
};
|
|
9162
|
+
var ConvexAccountAdapter = class {
|
|
9163
|
+
#convex;
|
|
9164
|
+
#fns;
|
|
9165
|
+
#telemetry;
|
|
9166
|
+
constructor(deps) {
|
|
9167
|
+
this.#convex = deps.convex;
|
|
9168
|
+
this.#fns = deps.functions ?? DEFAULT_FUNCTIONS$3;
|
|
9169
|
+
this.#telemetry = deps.telemetry;
|
|
9170
|
+
}
|
|
9171
|
+
readBalance(input) {
|
|
9172
|
+
return retryIdempotentRead(this.#convex.action(this.#fns.readBalance, copyInvocationObservation(input, { chainId: wireChainId(input.chainId) })), CAPXUL_OPERATIONS.accounts.read, this.#telemetry, input).pipe(Effect.mapError((error) => accountReadErrorFromCapxul("readBalance", error.publicError, error)), Effect.flatMap((wire) => brandAccountEffect("readBalance", wire)), Effect.catchDefect((cause) => Effect.fail(accountReadErrorFromUnknown("readBalance", cause))));
|
|
9173
|
+
}
|
|
9174
|
+
fundFromFaucet(input) {
|
|
9175
|
+
const operation = "fundFromFaucet";
|
|
9176
|
+
return Effect.suspend(() => {
|
|
9177
|
+
const rawAmount = toWei(input.amount);
|
|
9178
|
+
return this.#convex.action(this.#fns.faucetMint, {
|
|
9179
|
+
chainId: wireChainId(input.chainId),
|
|
9180
|
+
rawAmount
|
|
9181
|
+
});
|
|
9182
|
+
}).pipe(Effect.mapError((error) => accountReadErrorFromCapxul(operation, error.publicError, error)), Effect.map((wire) => ({ txHash: wire.txHash })), Effect.catchDefect((cause) => Effect.fail(accountReadErrorFromUnknown(operation, cause))));
|
|
9183
|
+
}
|
|
9184
|
+
};
|
|
9185
|
+
function ConvexAccountLayer() {
|
|
9186
|
+
return Layer.effect(AccountReadPortTag, Effect.all([ConvexCallPortTag, TelemetryPortTag]).pipe(Effect.map(([convex, telemetry]) => new ConvexAccountAdapter({
|
|
9187
|
+
convex,
|
|
9188
|
+
telemetry
|
|
9189
|
+
}))));
|
|
9190
|
+
}
|
|
9191
|
+
function brandAccountEffect(operation, wire) {
|
|
9192
|
+
return Effect.try({
|
|
9193
|
+
try: () => brandAccount(wire),
|
|
9194
|
+
catch: (cause) => accountReadErrorFromUnknown(operation, cause)
|
|
9195
|
+
});
|
|
9196
|
+
}
|
|
9197
|
+
function brandAccount(wire) {
|
|
9198
|
+
const balance = fromWei(wire.rawBalance, wire.decimals, wire.currency);
|
|
9199
|
+
const available = fromWei(wire.rawAvailableBalance ?? wire.rawBalance, wire.decimals, wire.currency);
|
|
9200
|
+
return {
|
|
9201
|
+
id: toAccountId(wire.accountId),
|
|
9202
|
+
balance,
|
|
9203
|
+
available
|
|
9204
|
+
};
|
|
9205
|
+
}
|
|
9206
|
+
function accountReadErrorFromUnknown(operation, cause) {
|
|
9207
|
+
if (cause instanceof CapxulError) return accountReadErrorFromCapxul(operation, cause);
|
|
9208
|
+
return accountReadErrorFromCapxul(operation, Errors.providerError("convex", operation, cause), cause);
|
|
9209
|
+
}
|
|
9210
|
+
//#endregion
|
|
9211
|
+
//#region src/ports/smart-account.ts
|
|
9212
|
+
var SmartAccountError = class extends Data.TaggedError("SmartAccountError") {};
|
|
9213
|
+
function smartAccountErrorFromCapxul(operation, error, cause = error) {
|
|
9214
|
+
return new SmartAccountError({
|
|
9215
|
+
operation,
|
|
9216
|
+
publicCode: error.code,
|
|
9217
|
+
publicError: error,
|
|
9218
|
+
cause,
|
|
9219
|
+
...error.details === void 0 ? {} : { details: error.details }
|
|
9220
|
+
});
|
|
9221
|
+
}
|
|
9222
|
+
var SmartAccountPortTag = class extends Context.Service()("@capxul/sdk/ports/SmartAccountPort") {};
|
|
9223
|
+
//#endregion
|
|
9224
|
+
//#region src/adapters/smart-account/ConvexSmartAccountAdapter.ts
|
|
9225
|
+
const DEFAULT_FUNCTIONS$2 = {
|
|
9226
|
+
loadByAuthUserId: makeFunctionReference(CAPXUL_FUNCTIONS["smartAccount/queries"].loadByAuthUserId),
|
|
9227
|
+
loadBySmartAccountAddress: makeFunctionReference(CAPXUL_FUNCTIONS["smartAccount/queries"].loadBySmartAccountAddress),
|
|
9228
|
+
provision: makeFunctionReference(CAPXUL_FUNCTIONS["smartAccount/mutations"].provision),
|
|
9229
|
+
confirmDeployment: makeFunctionReference(CAPXUL_FUNCTIONS["smartAccount/actions"].confirmDeployment),
|
|
9230
|
+
claim: makeFunctionReference(CAPXUL_FUNCTIONS["smartAccount/actions"].claim)
|
|
9231
|
+
};
|
|
9232
|
+
var ConvexSmartAccountAdapter = class {
|
|
9233
|
+
#convex;
|
|
9234
|
+
#fns;
|
|
9235
|
+
constructor(deps) {
|
|
9236
|
+
this.#convex = deps.convex;
|
|
9237
|
+
this.#fns = deps.functions ?? DEFAULT_FUNCTIONS$2;
|
|
9238
|
+
}
|
|
9239
|
+
loadByAuthUserId(authUserId) {
|
|
9240
|
+
return this.#convex.query(this.#fns.loadByAuthUserId, { authUserId }).pipe(Effect.mapError((error) => smartAccountErrorFromCapxul("loadByAuthUserId", error.publicError, error)), Effect.flatMap((row) => brandSmartAccountEffect("loadByAuthUserId", row)), Effect.catchDefect((cause) => Effect.fail(smartAccountErrorFromUnknown("loadByAuthUserId", cause))));
|
|
9241
|
+
}
|
|
9242
|
+
loadBySmartAccountAddress(address) {
|
|
9243
|
+
return this.#convex.query(this.#fns.loadBySmartAccountAddress, { address }).pipe(Effect.mapError((error) => smartAccountErrorFromCapxul("loadBySmartAccountAddress", error.publicError, error)), Effect.flatMap((row) => brandSmartAccountEffect("loadBySmartAccountAddress", row)), Effect.catchDefect((cause) => Effect.fail(smartAccountErrorFromUnknown("loadBySmartAccountAddress", cause))));
|
|
9244
|
+
}
|
|
9245
|
+
provision(input) {
|
|
9246
|
+
return this.#convex.mutation(this.#fns.provision, { chainId: wireChainId(input.chainId) }).pipe(Effect.mapError((error) => smartAccountErrorFromCapxul("provision", error.publicError, error)), Effect.flatMap((row) => Effect.try({
|
|
9247
|
+
try: () => brandProvisionedSmartAccount(input.authUserId, row),
|
|
9248
|
+
catch: (cause) => smartAccountErrorFromUnknown("provision", cause)
|
|
9249
|
+
})), Effect.catchDefect((cause) => Effect.fail(smartAccountErrorFromUnknown("provision", cause))));
|
|
9250
|
+
}
|
|
9251
|
+
confirmDeployment(input) {
|
|
9252
|
+
const evidence = {
|
|
9253
|
+
chainId: wireChainId(input.evidence.chainId),
|
|
9254
|
+
signerAddress: input.evidence.signerAddress,
|
|
9255
|
+
safeAddress: input.evidence.safeAddress,
|
|
9256
|
+
...input.evidence.userOpHash === void 0 ? {} : { userOpHash: input.evidence.userOpHash },
|
|
9257
|
+
...input.evidence.txHash === void 0 ? {} : { txHash: input.evidence.txHash },
|
|
9258
|
+
...input.evidence.blockNumber === void 0 ? {} : { blockNumber: input.evidence.blockNumber }
|
|
9259
|
+
};
|
|
9260
|
+
return this.#convex.action(this.#fns.confirmDeployment, {
|
|
9261
|
+
chainId: wireChainId(input.chainId),
|
|
9262
|
+
safeAddress: input.safeAddress,
|
|
9263
|
+
evidence
|
|
9264
|
+
}).pipe(Effect.mapError((error) => smartAccountErrorFromCapxul("confirmDeployment", error.publicError, error)), Effect.flatMap((row) => Effect.try({
|
|
9265
|
+
try: () => brandProvisionedSmartAccount(input.authUserId, row),
|
|
9266
|
+
catch: (cause) => smartAccountErrorFromUnknown("confirmDeployment", cause)
|
|
9267
|
+
})), Effect.catchDefect((cause) => Effect.fail(smartAccountErrorFromUnknown("confirmDeployment", cause))));
|
|
9268
|
+
}
|
|
9269
|
+
claim(input) {
|
|
9270
|
+
return this.#convex.action(this.#fns.claim, copyInvocationObservation(input, {
|
|
9271
|
+
chainId: wireChainId(input.chainId),
|
|
9272
|
+
signerAddress: input.signerAddress
|
|
9273
|
+
})).pipe(Effect.mapError((error) => smartAccountErrorFromCapxul("claim", error.publicError, error)), Effect.flatMap((row) => Effect.try({
|
|
9274
|
+
try: () => brandProvisionedSmartAccount(input.authUserId, row),
|
|
9275
|
+
catch: (cause) => smartAccountErrorFromUnknown("claim", cause)
|
|
9276
|
+
})), Effect.catchDefect((cause) => Effect.fail(smartAccountErrorFromUnknown("claim", cause))));
|
|
9277
|
+
}
|
|
9278
|
+
};
|
|
9279
|
+
function ConvexSmartAccountLayer() {
|
|
9280
|
+
return Layer.effect(SmartAccountPortTag, Effect.map(ConvexCallPortTag, (convex) => new ConvexSmartAccountAdapter({ convex })));
|
|
9281
|
+
}
|
|
9282
|
+
function brandSmartAccountEffect(operation, wire) {
|
|
9283
|
+
return Effect.try({
|
|
9284
|
+
try: () => wire === null ? null : brandNonNullSmartAccount(wire),
|
|
9285
|
+
catch: (cause) => smartAccountErrorFromUnknown(operation, cause)
|
|
9286
|
+
});
|
|
9287
|
+
}
|
|
9288
|
+
function brandNonNullSmartAccount(wire) {
|
|
9289
|
+
return {
|
|
9290
|
+
authUserId: toAuthUserId(wire.authUserId),
|
|
9291
|
+
signerAddress: wire.signerAddress === null ? null : toAddress(wire.signerAddress),
|
|
9292
|
+
smartAccountAddress: toAddress(wire.smartAccountAddress),
|
|
9293
|
+
chainId: toChainId(wire.chainId),
|
|
9294
|
+
deployedAt: wire.deployedAt === null ? null : toEpochMs(wire.deployedAt),
|
|
9295
|
+
claimedAt: wire.claimedAt === null ? null : toEpochMs(wire.claimedAt),
|
|
9296
|
+
createdAt: toEpochMs(wire.createdAt)
|
|
9297
|
+
};
|
|
9298
|
+
}
|
|
9299
|
+
function brandProvisionedSmartAccount(requestedAuthUserId, wire) {
|
|
9300
|
+
if (wire.authUserId !== String(requestedAuthUserId)) throw Errors.notAuthenticated();
|
|
9301
|
+
return brandNonNullSmartAccount(wire);
|
|
9302
|
+
}
|
|
9303
|
+
function smartAccountErrorFromUnknown(operation, cause) {
|
|
9304
|
+
if (cause instanceof CapxulError) return smartAccountErrorFromCapxul(operation, cause);
|
|
9305
|
+
return smartAccountErrorFromCapxul(operation, Errors.providerError("convex", operation, cause), cause);
|
|
9306
|
+
}
|
|
9307
|
+
//#endregion
|
|
9308
|
+
//#region src/ports/org.ts
|
|
9309
|
+
var OrgError = class extends Data.TaggedError("OrgError") {};
|
|
9310
|
+
function orgErrorFromCapxul(operation, error, cause = error) {
|
|
9311
|
+
return new OrgError({
|
|
9312
|
+
operation,
|
|
9313
|
+
publicCode: error.code,
|
|
9314
|
+
publicError: error,
|
|
9315
|
+
cause,
|
|
9316
|
+
...error.details === void 0 ? {} : { details: error.details }
|
|
9317
|
+
});
|
|
9318
|
+
}
|
|
9319
|
+
var OrgPortTag = class extends Context.Service()("@capxul/sdk/ports/OrgPort") {};
|
|
9320
|
+
//#endregion
|
|
9321
|
+
//#region src/adapters/org/parse.ts
|
|
9322
|
+
/**
|
|
9323
|
+
* Map a `WireOrg` + its (separately read) treasury `Account` into the branded
|
|
9324
|
+
* `OrgView`. The viewer role is projected by the authenticated backend read;
|
|
9325
|
+
* it must never be inferred from Organization ownership. Brands at the read
|
|
9326
|
+
* edge: `orgId`, `safeAddress`.
|
|
9327
|
+
*/
|
|
9328
|
+
function brandOrgView(wire, treasury, viewerRole) {
|
|
9329
|
+
return {
|
|
9330
|
+
id: toOrgId(wire.orgId),
|
|
9331
|
+
...wire.creationSource === void 0 ? {} : { creationSource: toTesterKind(wire.creationSource) },
|
|
9332
|
+
name: wire.name,
|
|
9333
|
+
handle: wire.handle,
|
|
9334
|
+
safeAddress: toAddress(wire.safeAddress.toLowerCase()),
|
|
9335
|
+
role: viewerRole,
|
|
9336
|
+
treasury,
|
|
9337
|
+
bio: wire.bio ?? null,
|
|
9338
|
+
size: wire.size ?? null,
|
|
9339
|
+
logoUrl: wire.logoUrl ?? null
|
|
9340
|
+
};
|
|
9341
|
+
}
|
|
9342
|
+
/**
|
|
9343
|
+
* Build the Org treasury `Account` from the raw on-chain `balanceOf` integer
|
|
9344
|
+
* (the D3 RPC read). `available === balance` for a treasury with no envelope
|
|
9345
|
+
* partition yet (a fresh Org reads back $0).
|
|
9346
|
+
*/
|
|
9347
|
+
function brandOrgTreasury(input) {
|
|
9348
|
+
return {
|
|
9349
|
+
id: toAccountId(`account_${orgIdBody(input.orgId)}`),
|
|
9350
|
+
balance: input.money,
|
|
9351
|
+
available: input.money
|
|
9352
|
+
};
|
|
9353
|
+
}
|
|
9354
|
+
function brandOrgRole(wire) {
|
|
9355
|
+
return {
|
|
9356
|
+
orgId: toOrgId(wire.orgId),
|
|
9357
|
+
label: wire.label,
|
|
9358
|
+
roleKey: toRoleKey(wire.roleKey),
|
|
9359
|
+
definition: parseRoleDefinition(wire.definitionJson)
|
|
9360
|
+
};
|
|
9361
|
+
}
|
|
9362
|
+
function brandOrgMember(wire) {
|
|
9363
|
+
return {
|
|
9364
|
+
orgId: toOrgId(wire.orgId),
|
|
9365
|
+
email: toEmail(wire.email),
|
|
9366
|
+
name: wire.name,
|
|
9367
|
+
personalSafeAddress: wire.personalSafeAddress === null ? null : toAddress(wire.personalSafeAddress),
|
|
9368
|
+
role: wire.role,
|
|
9369
|
+
roleKey: wire.roleKey === null ? null : toRoleKey(wire.roleKey),
|
|
9370
|
+
status: wire.status,
|
|
9371
|
+
grantTxHash: wire.grantTxHash,
|
|
9372
|
+
revokeTxHash: wire.revokeTxHash
|
|
9373
|
+
};
|
|
9374
|
+
}
|
|
9375
|
+
function parseRoleDefinition(json) {
|
|
9376
|
+
let raw;
|
|
9377
|
+
try {
|
|
9378
|
+
const parsed = JSON.parse(json);
|
|
9379
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) throw new Error("expected object");
|
|
9380
|
+
raw = parsed;
|
|
9381
|
+
} catch (err) {
|
|
9382
|
+
throw new Error(`Invalid role definition: malformed JSON - ${err instanceof Error ? err.message : String(err)}`, { cause: err });
|
|
9383
|
+
}
|
|
9384
|
+
if (typeof raw.label !== "string" || raw.label.trim().length === 0) throw new Error("Invalid role definition: missing label");
|
|
9385
|
+
return {
|
|
9386
|
+
label: raw.label,
|
|
9387
|
+
...raw.spend === void 0 ? {} : { spend: {
|
|
9388
|
+
...raw.spend.perTx === void 0 ? {} : { perTx: parseRoleMoney(raw.spend.perTx, "perTx") },
|
|
9389
|
+
...raw.spend.perDay === void 0 ? {} : { perDay: parseRoleMoney(raw.spend.perDay, "perDay") },
|
|
9390
|
+
...raw.spend.toRecipients === void 0 ? {} : { toRecipients: parseRoleRecipients(raw.spend.toRecipients) }
|
|
9391
|
+
} },
|
|
9392
|
+
...typeof raw.canSpend === "boolean" ? { canSpend: raw.canSpend } : {},
|
|
9393
|
+
...typeof raw.canManageMembers === "boolean" ? { canManageMembers: raw.canManageMembers } : {},
|
|
9394
|
+
...typeof raw.canManageRoles === "boolean" ? { canManageRoles: raw.canManageRoles } : {}
|
|
9395
|
+
};
|
|
9396
|
+
}
|
|
9397
|
+
function parseRoleMoney(raw, field) {
|
|
9398
|
+
if (typeof raw.currency !== "string" || typeof raw.value !== "string" || !/^\d+$/.test(raw.value) || raw.decimals !== 6) throw new Error(`Invalid role money: ${field}`);
|
|
9399
|
+
return {
|
|
9400
|
+
currency: toCurrencyCode(raw.currency),
|
|
9401
|
+
value: raw.value,
|
|
9402
|
+
decimals: raw.decimals
|
|
9403
|
+
};
|
|
9404
|
+
}
|
|
9405
|
+
function parseRoleRecipients(raw) {
|
|
9406
|
+
if (raw === "anyone") return "anyone";
|
|
9407
|
+
if (!Array.isArray(raw)) throw new Error("Invalid role definition: toRecipients must be \"anyone\" or an array");
|
|
9408
|
+
return raw.map((recipient) => {
|
|
9409
|
+
if (typeof recipient !== "string") throw new Error("Invalid role definition: toRecipients must be \"anyone\" or an array");
|
|
9410
|
+
return toAddress(recipient);
|
|
9411
|
+
});
|
|
9412
|
+
}
|
|
9413
|
+
/** Strip the `org_` prefix + non-alphanumerics so the body re-seeds `account_`. */
|
|
9414
|
+
function orgIdBody(orgId) {
|
|
9415
|
+
const underscore = orgId.indexOf("_");
|
|
9416
|
+
const cleaned = (underscore < 0 ? orgId : orgId.slice(underscore + 1)).replace(/[^0-9A-Za-z]/g, "");
|
|
9417
|
+
return cleaned.length > 0 ? cleaned : "0";
|
|
9418
|
+
}
|
|
9419
|
+
//#endregion
|
|
9420
|
+
//#region src/adapters/org/ConvexOrganizationAdapter.ts
|
|
9421
|
+
const DEFAULT_FUNCTIONS$1 = {
|
|
9422
|
+
listAll: makeFunctionReference(CAPXUL_FUNCTIONS["org/queries"].listAll),
|
|
9423
|
+
listRoles: makeFunctionReference(CAPXUL_FUNCTIONS["org/queries"].listRolesByOrgId),
|
|
9424
|
+
listMembers: makeFunctionReference(CAPXUL_FUNCTIONS["org/queries"].listMembersByOrgId),
|
|
9425
|
+
readTreasury: makeFunctionReference(CAPXUL_FUNCTIONS["org/actions"].readTreasury),
|
|
9426
|
+
inviteMember: makeFunctionReference(CAPXUL_FUNCTIONS["org/actions"].inviteMember),
|
|
9427
|
+
resendInvite: makeFunctionReference(CAPXUL_FUNCTIONS["org/mutations"].resendInviteToken),
|
|
9428
|
+
detectInvitations: makeFunctionReference(CAPXUL_FUNCTIONS["org/actions"].detectAndAcceptPendingInvitations)
|
|
9429
|
+
};
|
|
9430
|
+
/**
|
|
9431
|
+
* Standard production Organization read adapter. It intentionally has no
|
|
9432
|
+
* deployer/RPC/test configuration: authenticated Convex actions own live chain
|
|
9433
|
+
* reads, while the lifecycle adapter owns the single sponsored bootstrap.
|
|
9434
|
+
*/
|
|
9435
|
+
var ConvexOrganizationAdapter = class {
|
|
9436
|
+
#convex;
|
|
9437
|
+
#fns;
|
|
9438
|
+
#telemetry;
|
|
9439
|
+
constructor(input) {
|
|
9440
|
+
this.#convex = input.convex;
|
|
9441
|
+
this.#fns = input.functions ?? DEFAULT_FUNCTIONS$1;
|
|
9442
|
+
this.#telemetry = input.telemetry;
|
|
9443
|
+
}
|
|
9444
|
+
createOrg(_input) {
|
|
9445
|
+
return Effect.fail(orgErrorFromCapxul("createOrg", Errors.notImplemented("organizationSetup", "use onboarding.completeOrganization")));
|
|
9446
|
+
}
|
|
9447
|
+
listOrgs(input) {
|
|
9448
|
+
return this.#convex.query(this.#fns.listAll, copyInvocationObservation(input, {})).pipe(Effect.mapError((error) => orgErrorFromCapxul("listOrgs", error.publicError, error)), Effect.flatMap((wires) => Effect.forEach(wires, (wire) => this.#readTreasuryWire(copyInvocationObservation(input, { orgId: toOrgId(wire.orgId) })).pipe(Effect.map((treasury) => {
|
|
9449
|
+
const viewerRole = wire.viewerRole.trim();
|
|
9450
|
+
if (viewerRole.length === 0) throw Errors.wrongState({
|
|
9451
|
+
method: "listOrgs",
|
|
9452
|
+
currentState: "viewerRoleMissing",
|
|
9453
|
+
validStates: ["activeViewerRole"]
|
|
9454
|
+
});
|
|
9455
|
+
return brandOrgView(wire, treasury, viewerRole);
|
|
9456
|
+
})))), Effect.catchDefect((cause) => Effect.fail(toOrgError("listOrgs", cause))));
|
|
9457
|
+
}
|
|
9458
|
+
readTreasury(input) {
|
|
9459
|
+
return this.#readTreasuryWire(input).pipe(Effect.catchDefect((cause) => Effect.fail(toOrgError("readTreasury", cause))));
|
|
9460
|
+
}
|
|
9461
|
+
listRoles(input) {
|
|
9462
|
+
return this.#convex.query(this.#fns.listRoles, { orgId: input.orgId }).pipe(Effect.mapError((error) => orgErrorFromCapxul("listRoles", error.publicError, error)), Effect.flatMap((rows) => rows.length === 0 ? Effect.fail(partialOrgTruth("listRoles", "activeRoleMissing")) : Effect.succeed(rows.map(brandOrgRole))), Effect.catchDefect((cause) => Effect.fail(toOrgError("listRoles", cause))));
|
|
9463
|
+
}
|
|
9464
|
+
listMembers(input) {
|
|
9465
|
+
return this.#convex.query(this.#fns.listMembers, { orgId: input.orgId }).pipe(Effect.mapError((error) => orgErrorFromCapxul("listMembers", error.publicError, error)), Effect.flatMap((rows) => rows.length === 0 ? Effect.fail(partialOrgTruth("listMembers", "activeMemberMissing")) : Effect.succeed(rows.map(brandOrgMember))), Effect.catchDefect((cause) => Effect.fail(toOrgError("listMembers", cause))));
|
|
9466
|
+
}
|
|
9467
|
+
pendingMembers(input) {
|
|
9468
|
+
return this.listMembers(input).pipe(Effect.map((members) => members.filter((member) => member.status === "pending" || member.status === "pending_safe" || member.status === "pending_grant")));
|
|
9469
|
+
}
|
|
9470
|
+
inviteMember(input) {
|
|
9471
|
+
return this.#convex.action(this.#fns.inviteMember, {
|
|
9472
|
+
orgId: input.orgId,
|
|
9473
|
+
email: input.input.email,
|
|
9474
|
+
role: input.input.role
|
|
9475
|
+
}).pipe(Effect.mapError((error) => orgErrorFromCapxul("inviteMember", error.publicError, error)), Effect.map(brandOrgMember), Effect.catchDefect((cause) => Effect.fail(toOrgError("inviteMember", cause))));
|
|
9476
|
+
}
|
|
9477
|
+
resendInviteToken(input) {
|
|
9478
|
+
return this.#convex.mutation(this.#fns.resendInvite, {
|
|
9479
|
+
orgId: input.orgId,
|
|
9480
|
+
email: input.email
|
|
9481
|
+
}).pipe(Effect.mapError((error) => orgErrorFromCapxul("resendInviteToken", error.publicError, error)), Effect.map(brandOrgMember), Effect.catchDefect((cause) => Effect.fail(toOrgError("resendInviteToken", cause))));
|
|
9482
|
+
}
|
|
9483
|
+
detectAndAcceptPendingInvitations(_input) {
|
|
9484
|
+
return this.#convex.action(this.#fns.detectInvitations, {}).pipe(Effect.mapError((error) => orgErrorFromCapxul("detectAndAcceptPendingInvitations", error.publicError, error)), Effect.map((result) => ({ matched: result.matched.map(toOrgId) })), Effect.catchDefect((cause) => Effect.fail(toOrgError("detectAndAcceptPendingInvitations", cause))));
|
|
9485
|
+
}
|
|
9486
|
+
#readTreasuryWire(input) {
|
|
9487
|
+
const orgId = String(input.orgId);
|
|
9488
|
+
return retryIdempotentRead(this.#convex.action(this.#fns.readTreasury, copyInvocationObservation(input, { orgId })), CAPXUL_OPERATIONS.org.treasury, this.#telemetry, input).pipe(Effect.mapError((error) => orgErrorFromCapxul("readTreasury", error.publicError, error)), Effect.map((wire) => {
|
|
9489
|
+
if (wire.orgId !== orgId) throw Errors.invalidInput("orgId", "Organization treasury scope does not match");
|
|
9490
|
+
const balance = fromWei(wire.rawBalance, wire.decimals, wire.currency);
|
|
9491
|
+
const available = fromWei(wire.rawAvailableBalance, wire.decimals, wire.currency);
|
|
9492
|
+
return {
|
|
9493
|
+
...brandOrgTreasury({
|
|
9494
|
+
orgId: wire.orgId,
|
|
9495
|
+
money: balance
|
|
9496
|
+
}),
|
|
9497
|
+
available
|
|
9498
|
+
};
|
|
9499
|
+
}));
|
|
9500
|
+
}
|
|
9501
|
+
};
|
|
9502
|
+
function toOrgError(operation, cause) {
|
|
9503
|
+
if (cause instanceof CapxulError) return orgErrorFromCapxul(operation, cause);
|
|
9504
|
+
return orgErrorFromCapxul(operation, Errors.providerError("convex", operation, cause), cause);
|
|
9505
|
+
}
|
|
9506
|
+
function partialOrgTruth(operation, currentState) {
|
|
9507
|
+
return orgErrorFromCapxul(operation, Errors.wrongState({
|
|
9508
|
+
method: operation,
|
|
9509
|
+
currentState,
|
|
9510
|
+
validStates: ["completeProductionOrganizationTruth"]
|
|
9511
|
+
}));
|
|
9512
|
+
}
|
|
9513
|
+
//#endregion
|
|
9514
|
+
//#region src/adapters/org/ConvexOrganizationSetupAdapter.ts
|
|
9515
|
+
const DEFAULT_FUNCTIONS = {
|
|
9516
|
+
startOrResume: makeFunctionReference(CAPXUL_FUNCTIONS["org/lifecycle"].startOrResume),
|
|
9517
|
+
prepareFounderAccount: makeFunctionReference(CAPXUL_FUNCTIONS["org/actions"].prepareFounderAccount),
|
|
9518
|
+
prepareBootstrap: makeFunctionReference(CAPXUL_FUNCTIONS["org/actions"].prepareBootstrap),
|
|
9519
|
+
submitBootstrap: makeFunctionReference(CAPXUL_FUNCTIONS["org/actions"].submitBootstrap),
|
|
9520
|
+
resumeBootstrapSubmission: makeFunctionReference(CAPXUL_FUNCTIONS["org/actions"].resumeBootstrapSubmission),
|
|
9521
|
+
confirmBootstrap: makeFunctionReference(CAPXUL_FUNCTIONS["org/actions"].confirmBootstrap),
|
|
9522
|
+
recordFailure: makeFunctionReference(CAPXUL_FUNCTIONS["org/lifecycle"].recordFailure),
|
|
9523
|
+
load: makeFunctionReference(CAPXUL_FUNCTIONS["org/lifecycle"].load),
|
|
9524
|
+
retry: makeFunctionReference(CAPXUL_FUNCTIONS["org/lifecycle"].retry)
|
|
9525
|
+
};
|
|
9526
|
+
/** Durable Organization setup capability composed by the standard client. */
|
|
9527
|
+
var ConvexOrganizationSetupAdapter = class {
|
|
9528
|
+
#convex;
|
|
9529
|
+
#signer;
|
|
9530
|
+
#chainId;
|
|
9531
|
+
#fns;
|
|
9532
|
+
constructor(input) {
|
|
9533
|
+
this.#convex = input.convex;
|
|
9534
|
+
this.#signer = input.signer;
|
|
9535
|
+
this.#chainId = input.chainId;
|
|
9536
|
+
this.#fns = input.functions ?? DEFAULT_FUNCTIONS;
|
|
9537
|
+
}
|
|
9538
|
+
async startOrResume(input) {
|
|
9539
|
+
const result = await runCall("startOrResume", this.#convex.mutation(this.#fns.startOrResume, copyInvocationObservation(input, {
|
|
9540
|
+
...input,
|
|
9541
|
+
chainId: this.#chainId
|
|
9542
|
+
})));
|
|
9543
|
+
if (!result.ok) return result;
|
|
9544
|
+
const lifecycle = parseLifecycle("startOrResume", result.value.lifecycle);
|
|
9545
|
+
if (!lifecycle.ok) return lifecycle;
|
|
9546
|
+
const orgId = parseOrgId("startOrResume", result.value.orgId);
|
|
9547
|
+
if (!orgId.ok) return orgId;
|
|
9548
|
+
if (String(orgId.value) !== String(lifecycle.value.orgId)) return fail$1(Errors.invalidInput("orgId", "Organization lifecycle scope does not match"));
|
|
9549
|
+
return {
|
|
9550
|
+
ok: true,
|
|
9551
|
+
value: {
|
|
9552
|
+
orgId: orgId.value,
|
|
9553
|
+
lifecycle: lifecycle.value
|
|
9554
|
+
}
|
|
9555
|
+
};
|
|
9556
|
+
}
|
|
9557
|
+
prepareFounderAccount(input) {
|
|
9558
|
+
return this.#lifecycleAction("prepareFounderAccount", input, () => this.#convex.action(this.#fns.prepareFounderAccount, copyInvocationObservation(input, {
|
|
9559
|
+
orgId: input.orgId,
|
|
9560
|
+
...input.observationContext === void 0 ? {} : { observationContext: input.observationContext }
|
|
9561
|
+
})));
|
|
9562
|
+
}
|
|
9563
|
+
async authorizeAndSubmitBootstrap(input) {
|
|
9564
|
+
const cancelled = cancellation(input.signal);
|
|
9565
|
+
if (cancelled !== void 0) return cancelled;
|
|
9566
|
+
const signerAddress = await signerResult(this.#signer.source, "getAddress", () => this.#signer.getAddress());
|
|
9567
|
+
if (!signerAddress.ok) return signerAddress;
|
|
9568
|
+
const prepared = await runCall("prepareBootstrap", this.#convex.action(this.#fns.prepareBootstrap, copyInvocationObservation(input, {
|
|
9569
|
+
orgId: input.orgId,
|
|
9570
|
+
signerAddress: signerAddress.value,
|
|
9571
|
+
...input.observationContext === void 0 ? {} : { observationContext: input.observationContext }
|
|
9572
|
+
})));
|
|
9573
|
+
if (!prepared.ok) return prepared;
|
|
9574
|
+
const authority = validatePreparedAuthorities(prepared.value, signerAddress.value);
|
|
9575
|
+
if (!authority.ok) return authority;
|
|
9576
|
+
const cancelledAfterPrepare = cancellation(input.signal);
|
|
9577
|
+
if (cancelledAfterPrepare !== void 0) return cancelledAfterPrepare;
|
|
9578
|
+
const signature = await signerResult(this.#signer.source, "signUserOpHash", () => this.#signer.signUserOpHash(prepared.value.digest));
|
|
9579
|
+
if (!signature.ok) return signature;
|
|
9580
|
+
const cancelledAfterSign = cancellation(input.signal);
|
|
9581
|
+
if (cancelledAfterSign !== void 0) return cancelledAfterSign;
|
|
9582
|
+
const submitted = await runCall("submitBootstrap", this.#convex.action(this.#fns.submitBootstrap, copyInvocationObservation(input, {
|
|
9583
|
+
orgId: input.orgId,
|
|
9584
|
+
signerAddress: signerAddress.value,
|
|
9585
|
+
signature: signature.value,
|
|
9586
|
+
userOp: prepared.value.userOp,
|
|
9587
|
+
...input.observationContext === void 0 ? {} : { observationContext: input.observationContext }
|
|
9588
|
+
})));
|
|
9589
|
+
if (!submitted.ok) return submitted;
|
|
9590
|
+
return parseLifecycle("submitBootstrap", submitted.value);
|
|
9591
|
+
}
|
|
9592
|
+
resumeSubmittedBootstrap(input) {
|
|
9593
|
+
return this.#lifecycleAction("resumeBootstrapSubmission", input, () => this.#convex.action(this.#fns.resumeBootstrapSubmission, copyInvocationObservation(input, {
|
|
9594
|
+
orgId: input.orgId,
|
|
9595
|
+
...input.observationContext === void 0 ? {} : { observationContext: input.observationContext }
|
|
9596
|
+
})));
|
|
9597
|
+
}
|
|
9598
|
+
confirmSubmittedBootstrap(input) {
|
|
9599
|
+
return this.#lifecycleAction("confirmBootstrap", input, () => this.#convex.action(this.#fns.confirmBootstrap, copyInvocationObservation(input, {
|
|
9600
|
+
orgId: input.orgId,
|
|
9601
|
+
...input.observationContext === void 0 ? {} : { observationContext: input.observationContext },
|
|
9602
|
+
...input.retryDelayMs === void 0 ? {} : { retryDelayMs: input.retryDelayMs }
|
|
9603
|
+
})));
|
|
9604
|
+
}
|
|
9605
|
+
async recordFailure(input) {
|
|
9606
|
+
const errorProvider = input.error.details?.provider;
|
|
9607
|
+
const errorOperation = input.error.details?.operation;
|
|
9608
|
+
const result = await runCall("recordFailure", this.#convex.mutation(this.#fns.recordFailure, copyInvocationObservation(input, {
|
|
9609
|
+
orgId: input.orgId,
|
|
9610
|
+
errorCode: input.error.code,
|
|
9611
|
+
...typeof errorProvider === "string" && typeof errorOperation === "string" ? {
|
|
9612
|
+
errorProvider,
|
|
9613
|
+
errorOperation
|
|
9614
|
+
} : {},
|
|
9615
|
+
retryable: input.retryable,
|
|
9616
|
+
...input.observationContext === void 0 ? {} : { observationContext: input.observationContext }
|
|
9617
|
+
})));
|
|
9618
|
+
return result.ok ? parseLifecycle("recordFailure", result.value) : result;
|
|
9619
|
+
}
|
|
9620
|
+
async loadLifecycle(input) {
|
|
9621
|
+
const result = await runCall("loadLifecycle", this.#convex.query(this.#fns.load, input));
|
|
9622
|
+
if (!result.ok) return result;
|
|
9623
|
+
if (result.value === null) return fail$1(Errors.invalidInput("orgId", "Organization lifecycle was not found"));
|
|
9624
|
+
return parseLifecycle("loadLifecycle", result.value);
|
|
9625
|
+
}
|
|
9626
|
+
async retry(input) {
|
|
9627
|
+
const cancelled = cancellation(input.signal);
|
|
9628
|
+
if (cancelled !== void 0) return cancelled;
|
|
9629
|
+
const current = await this.loadLifecycle({
|
|
9630
|
+
orgId: input.orgId,
|
|
9631
|
+
...input.observationContext === void 0 ? {} : { observationContext: input.observationContext }
|
|
9632
|
+
});
|
|
9633
|
+
if (!current.ok) return current;
|
|
9634
|
+
if (current.value.status === "failed" && current.value.retryable && (current.value.at === "awaitingFounderAuthorization" || current.value.at === "submittingBootstrap")) {
|
|
9635
|
+
const reset = resetSignerSession(this.#signer);
|
|
9636
|
+
if (!reset.ok) return reset;
|
|
9637
|
+
}
|
|
9638
|
+
const result = await runCall("retry", this.#convex.mutation(this.#fns.retry, copyInvocationObservation(input, {
|
|
9639
|
+
orgId: input.orgId,
|
|
9640
|
+
...input.observationContext === void 0 ? {} : { observationContext: input.observationContext }
|
|
9641
|
+
})));
|
|
9642
|
+
if (!result.ok) return result;
|
|
9643
|
+
return parseLifecycle("retry", result.value);
|
|
9644
|
+
}
|
|
9645
|
+
async #lifecycleAction(operation, input, call) {
|
|
9646
|
+
const cancelled = cancellation(input.signal);
|
|
9647
|
+
if (cancelled !== void 0) return cancelled;
|
|
9648
|
+
const result = await runCall(operation, call());
|
|
9649
|
+
if (!result.ok) return result;
|
|
9650
|
+
const cancelledAfter = cancellation(input.signal);
|
|
9651
|
+
if (cancelledAfter !== void 0) return cancelledAfter;
|
|
9652
|
+
return parseLifecycle(operation, result.value);
|
|
9653
|
+
}
|
|
9654
|
+
};
|
|
9655
|
+
function resetSignerSession(signer) {
|
|
9656
|
+
const resetSession = signer.resetSession;
|
|
9657
|
+
if (typeof resetSession !== "function") return {
|
|
9658
|
+
ok: true,
|
|
9659
|
+
value: void 0
|
|
9660
|
+
};
|
|
9661
|
+
try {
|
|
9662
|
+
resetSession.call(signer);
|
|
9663
|
+
return {
|
|
9664
|
+
ok: true,
|
|
9665
|
+
value: void 0
|
|
9666
|
+
};
|
|
9667
|
+
} catch (cause) {
|
|
9668
|
+
return fail$1(signerFailure(signer.source, "resetSession", cause));
|
|
9669
|
+
}
|
|
9670
|
+
}
|
|
9671
|
+
async function runCall(operation, effect) {
|
|
9672
|
+
try {
|
|
9673
|
+
const result = await Effect.runPromise(Effect.result(effect));
|
|
9674
|
+
return Result.isSuccess(result) ? {
|
|
9675
|
+
ok: true,
|
|
9676
|
+
value: result.success
|
|
9677
|
+
} : fail$1(publicError(operation, result.failure));
|
|
9678
|
+
} catch (cause) {
|
|
9679
|
+
return fail$1(publicError(operation, cause));
|
|
9680
|
+
}
|
|
9681
|
+
}
|
|
9682
|
+
function publicError(operation, cause) {
|
|
9683
|
+
if (cause instanceof CapxulError) return cause;
|
|
9684
|
+
if (typeof cause === "object" && cause !== null) {
|
|
9685
|
+
const carried = cause.publicError;
|
|
9686
|
+
if (carried instanceof CapxulError) return carried;
|
|
9687
|
+
}
|
|
9688
|
+
return Errors.providerError("convex-organization", operation, cause);
|
|
9689
|
+
}
|
|
9690
|
+
async function signerResult(source, operation, run) {
|
|
9691
|
+
try {
|
|
9692
|
+
return {
|
|
9693
|
+
ok: true,
|
|
9694
|
+
value: await run()
|
|
9695
|
+
};
|
|
9696
|
+
} catch (cause) {
|
|
9697
|
+
return fail$1(signerFailure(source, operation, cause));
|
|
9698
|
+
}
|
|
9699
|
+
}
|
|
9700
|
+
function validatePreparedAuthorities(prepared, signerAddress) {
|
|
9701
|
+
const signer = signerAddress.toLowerCase();
|
|
9702
|
+
const preparedSigner = prepared.signerAddress.toLowerCase();
|
|
9703
|
+
const founder = prepared.founderPersonalAccount.toLowerCase();
|
|
9704
|
+
const organization = prepared.organizationAccountAddress.toLowerCase();
|
|
9705
|
+
const sender = prepared.userOp.sender.toLowerCase();
|
|
9706
|
+
if (preparedSigner !== signer) return fail$1(Errors.invalidInput("signerAddress", "Prepared signer does not match configured signer"));
|
|
9707
|
+
if (founder === signer || organization === signer || organization === founder) return fail$1(Errors.invalidInput("organizationAuthority", "Signer EOA, founder Account, and Organization Account must be distinct"));
|
|
9708
|
+
if (sender !== founder) return fail$1(Errors.invalidInput("userOp.sender", "Bootstrap sender must be the founder Account"));
|
|
9709
|
+
if (!/^0x[0-9a-fA-F]{64}$/u.test(prepared.digest)) return fail$1(Errors.invalidInput("digest", "Prepared bootstrap digest must be 32-byte hex"));
|
|
9710
|
+
return {
|
|
9711
|
+
ok: true,
|
|
9712
|
+
value: void 0
|
|
9713
|
+
};
|
|
9714
|
+
}
|
|
9715
|
+
function parseLifecycle(operation, wire) {
|
|
9716
|
+
const orgId = parseOrgId(operation, wire.orgId);
|
|
9717
|
+
if (!orgId.ok) return orgId;
|
|
9718
|
+
if (wire.status === "loading") return {
|
|
9719
|
+
ok: true,
|
|
9720
|
+
value: {
|
|
9721
|
+
status: "loading",
|
|
9722
|
+
orgId: orgId.value
|
|
9723
|
+
}
|
|
9724
|
+
};
|
|
9725
|
+
if (wire.status === "ready") return {
|
|
9726
|
+
ok: true,
|
|
9727
|
+
value: {
|
|
9728
|
+
status: "ready",
|
|
9729
|
+
orgId: orgId.value,
|
|
9730
|
+
canTransact: true
|
|
9731
|
+
}
|
|
9732
|
+
};
|
|
9733
|
+
if (wire.status === "failed") {
|
|
9734
|
+
if (!isSetupStep(wire.at)) return fail$1(Errors.invalidInput("lifecycle.at", "Unknown setup step"));
|
|
9735
|
+
return {
|
|
9736
|
+
ok: true,
|
|
9737
|
+
value: {
|
|
9738
|
+
status: "failed",
|
|
9739
|
+
orgId: orgId.value,
|
|
9740
|
+
at: wire.at,
|
|
9741
|
+
error: new CapxulError(wire.error.code, wire.error.message),
|
|
9742
|
+
retryable: wire.retryable
|
|
9743
|
+
}
|
|
9744
|
+
};
|
|
9745
|
+
}
|
|
9746
|
+
if (!isSetupStep(wire.step)) return fail$1(Errors.invalidInput("lifecycle.step", `Unknown setup step from ${operation}`));
|
|
9747
|
+
return {
|
|
9748
|
+
ok: true,
|
|
9749
|
+
value: {
|
|
9750
|
+
status: "settingUp",
|
|
9751
|
+
orgId: orgId.value,
|
|
9752
|
+
step: wire.step
|
|
9753
|
+
}
|
|
9754
|
+
};
|
|
9755
|
+
}
|
|
9756
|
+
function parseOrgId(operation, value) {
|
|
9757
|
+
try {
|
|
9758
|
+
return {
|
|
9759
|
+
ok: true,
|
|
9760
|
+
value: toOrgId(value)
|
|
9761
|
+
};
|
|
9762
|
+
} catch (cause) {
|
|
9763
|
+
return fail$1(Errors.providerError("convex-organization", operation, cause));
|
|
9764
|
+
}
|
|
9765
|
+
}
|
|
9766
|
+
function isSetupStep(value) {
|
|
9767
|
+
return value === "preparingFounderAccount" || value === "awaitingFounderAuthorization" || value === "submittingBootstrap" || value === "confirmingBootstrap";
|
|
9768
|
+
}
|
|
9769
|
+
function cancellation(signal) {
|
|
9770
|
+
return signal?.aborted ? fail$1(Errors.cancelled({ operation: CAPXUL_OPERATIONS.organization.setup })) : void 0;
|
|
9771
|
+
}
|
|
9772
|
+
function fail$1(error) {
|
|
9773
|
+
return {
|
|
9774
|
+
ok: false,
|
|
9775
|
+
error
|
|
9776
|
+
};
|
|
9777
|
+
}
|
|
9778
|
+
//#endregion
|
|
9779
|
+
//#region src/adapters/telemetry/PostHogTelemetryAdapter.ts
|
|
9780
|
+
var PostHogTelemetryAdapter = class {
|
|
9781
|
+
#capture;
|
|
9782
|
+
#identify;
|
|
9783
|
+
#group;
|
|
9784
|
+
#reset;
|
|
9785
|
+
constructor(deps) {
|
|
9786
|
+
this.#capture = deps.capture;
|
|
9787
|
+
this.#identify = deps.identify ?? (() => void 0);
|
|
9788
|
+
this.#group = deps.group ?? (() => void 0);
|
|
9789
|
+
this.#reset = deps.reset ?? (() => void 0);
|
|
9790
|
+
}
|
|
9791
|
+
emit(event) {
|
|
9792
|
+
return this.#run(() => this.#capture(event.name, redactTelemetryProps(event.name, cloneProps(event.props)), event), "emit", event.name);
|
|
9793
|
+
}
|
|
9794
|
+
identify(input) {
|
|
9795
|
+
return this.#run(() => this.#identify(cloneIdentifyInput(input)), "identify");
|
|
9796
|
+
}
|
|
9797
|
+
group(input) {
|
|
9798
|
+
return this.#run(() => this.#group(cloneGroupInput(input)), "group");
|
|
9799
|
+
}
|
|
9800
|
+
reset() {
|
|
9801
|
+
return this.#run(() => this.#reset(), "reset");
|
|
9802
|
+
}
|
|
9803
|
+
#run(operation, operationName, eventName) {
|
|
9804
|
+
const diagnose = Effect.logWarning("product.telemetry.transport.dropped").pipe(Effect.annotateLogs({
|
|
9805
|
+
operation: operationName,
|
|
9806
|
+
...eventName === void 0 ? {} : { product_event: eventName }
|
|
9807
|
+
}), Effect.catchCause(() => Effect.void));
|
|
9808
|
+
return Effect.suspend(() => {
|
|
9809
|
+
let pending;
|
|
9810
|
+
try {
|
|
9811
|
+
pending = operation();
|
|
9812
|
+
} catch {
|
|
9813
|
+
return diagnose;
|
|
9814
|
+
}
|
|
9815
|
+
if (pending === void 0) return Effect.void;
|
|
9816
|
+
const transport = Effect.tryPromise({
|
|
9817
|
+
try: () => pending,
|
|
9818
|
+
catch: () => void 0
|
|
9819
|
+
}).pipe(Effect.catch(() => diagnose));
|
|
9820
|
+
return Effect.forkDetach(transport, { startImmediately: true }).pipe(Effect.asVoid);
|
|
9821
|
+
});
|
|
9822
|
+
}
|
|
9823
|
+
};
|
|
9824
|
+
function PostHogTelemetryLayer(deps) {
|
|
9825
|
+
return Layer.succeed(TelemetryPortTag, new PostHogTelemetryAdapter(deps));
|
|
9826
|
+
}
|
|
9827
|
+
function cloneIdentifyInput(input) {
|
|
9828
|
+
const traits = input.traits === void 0 ? void 0 : cloneProps(input.traits);
|
|
9829
|
+
const properties = input.properties === void 0 ? void 0 : cloneProps(input.properties);
|
|
9830
|
+
return {
|
|
9831
|
+
distinctId: input.distinctId,
|
|
9832
|
+
...input.anonDistinctId === void 0 ? {} : { anonDistinctId: input.anonDistinctId },
|
|
9833
|
+
...traits === void 0 ? {} : { traits },
|
|
9834
|
+
...properties === void 0 ? {} : { properties }
|
|
9835
|
+
};
|
|
9836
|
+
}
|
|
9837
|
+
function cloneGroupInput(input) {
|
|
9838
|
+
const properties = input.properties === void 0 ? void 0 : cloneProps(input.properties);
|
|
9839
|
+
return properties === void 0 ? {
|
|
9840
|
+
groupType: input.groupType,
|
|
9841
|
+
groupKey: input.groupKey
|
|
9842
|
+
} : {
|
|
9843
|
+
groupType: input.groupType,
|
|
9844
|
+
groupKey: input.groupKey,
|
|
9845
|
+
properties
|
|
9846
|
+
};
|
|
9847
|
+
}
|
|
9848
|
+
function cloneProps(props) {
|
|
9849
|
+
if (props === void 0) return void 0;
|
|
9850
|
+
const cloned = {};
|
|
9851
|
+
for (const [key, value] of Object.entries(props)) cloned[key] = cloneTelemetryValue(value);
|
|
9852
|
+
return cloned;
|
|
9853
|
+
}
|
|
9854
|
+
function cloneTelemetryValue(value) {
|
|
9855
|
+
if (Array.isArray(value)) return value.map(cloneTelemetryValue);
|
|
9856
|
+
if (value === null || typeof value !== "object") return value;
|
|
9857
|
+
if (Object.getPrototypeOf(value) !== Object.prototype) return value;
|
|
9858
|
+
const cloned = {};
|
|
9859
|
+
for (const [key, nested] of Object.entries(value)) cloned[key] = cloneTelemetryValue(nested);
|
|
9860
|
+
return cloned;
|
|
9861
|
+
}
|
|
9862
|
+
//#endregion
|
|
9863
|
+
//#region src/adapters/diagnostic/ConsoleDiagnosticAdapter.ts
|
|
9864
|
+
const DEFAULT_ACCOUNT_SETUP_LOG_PREFIX = "[capxul:account-setup]";
|
|
9865
|
+
var ConsoleDiagnosticAdapter = class {
|
|
9866
|
+
prefix;
|
|
9867
|
+
constructor(prefix = DEFAULT_ACCOUNT_SETUP_LOG_PREFIX) {
|
|
9868
|
+
this.prefix = prefix;
|
|
9869
|
+
}
|
|
9870
|
+
trace(scope, detail) {
|
|
9871
|
+
globalThis.console?.debug?.(`${this.prefix} ${scope}`, detail);
|
|
9872
|
+
}
|
|
9873
|
+
};
|
|
9874
|
+
//#endregion
|
|
9875
|
+
//#region src/openfort/create-openfort-browser-signer.ts
|
|
9876
|
+
function httpStatusOf(link) {
|
|
9877
|
+
const carrier = link;
|
|
9878
|
+
if (typeof carrier.response?.status === "number") return carrier.response.status;
|
|
9879
|
+
if (typeof carrier.statusCode === "number") return carrier.statusCode;
|
|
9880
|
+
if (typeof carrier.status === "number") return carrier.status;
|
|
9881
|
+
}
|
|
9882
|
+
function isUnauthorizedCause(cause) {
|
|
9883
|
+
for (const link of causeChain(cause)) if (httpStatusOf(link) === 401) return true;
|
|
9884
|
+
return false;
|
|
9885
|
+
}
|
|
9886
|
+
function openfortProviderError(operation, cause) {
|
|
9887
|
+
if (cause instanceof CapxulError) return cause;
|
|
9888
|
+
const failure_mode = operation === "configure" && isUnauthorizedCause(cause) ? "unauthorized" : "unknown";
|
|
9889
|
+
return Errors.providerError("openfort", operation, cause, { failure_mode });
|
|
9890
|
+
}
|
|
9891
|
+
function openfortFailureMode(error) {
|
|
9892
|
+
return error.mode ?? "unknown";
|
|
9893
|
+
}
|
|
9894
|
+
/** Build a named PROVIDER_ERROR. The assembled Core SDK boundary reports it. */
|
|
9895
|
+
function failOpenfort(operation, failure_mode, cause) {
|
|
9896
|
+
return Errors.providerError("openfort", operation, cause, { failure_mode });
|
|
9897
|
+
}
|
|
9898
|
+
/**
|
|
9899
|
+
* True when the browser cannot perform Web Crypto — sandboxed iframes, headless
|
|
9900
|
+
* agent browsers, or non-HTTPS origins. OpenFort's embedded-wallet `configure`
|
|
9901
|
+
* silently produces no address in this state, so we detect it up front and name
|
|
9902
|
+
* it `no-secure-context` instead of letting it decay into `unknown`.
|
|
9903
|
+
*/
|
|
9904
|
+
function isInsecureBrowserContext() {
|
|
9905
|
+
return globalThis.isSecureContext === false || globalThis.crypto?.subtle === void 0;
|
|
9906
|
+
}
|
|
9907
|
+
/** Openfort SDK storage keys (`@openfort/openfort-js` StorageKeys). */
|
|
9908
|
+
const OPENFORT_BROWSER_STORAGE_KEYS = [
|
|
9909
|
+
"openfort.authentication",
|
|
9910
|
+
"openfort.account",
|
|
9911
|
+
"openfort.session",
|
|
9912
|
+
"openfort.configuration"
|
|
9913
|
+
];
|
|
9914
|
+
/**
|
|
9915
|
+
* Matches `@openfort/openfort-js` ScopedStorage.createScope — chars 8–15 of the
|
|
9916
|
+
* publishable key, prefixed onto each StorageKeys entry in localStorage.
|
|
9917
|
+
*/
|
|
9918
|
+
function openfortBrowserStorageScope(publishableKey) {
|
|
9919
|
+
const trimmed = publishableKey.trim();
|
|
9920
|
+
if (trimmed.length < 16) return;
|
|
9921
|
+
return trimmed.substring(8, 16);
|
|
9922
|
+
}
|
|
9923
|
+
/**
|
|
9924
|
+
* Drop cached Openfort auth/account state so third-party login re-runs for the
|
|
9925
|
+
* current Better Auth session. The SDK skips `authenticateThirdParty` when a
|
|
9926
|
+
* stale `userId` is already in storage, which yields 401 on `v2/accounts`.
|
|
9927
|
+
*/
|
|
9928
|
+
function clearStaleOpenfortBrowserStorage(publishableKey) {
|
|
9929
|
+
if (typeof localStorage === "undefined") return;
|
|
9930
|
+
const scope = openfortBrowserStorageScope(publishableKey);
|
|
9931
|
+
if (scope === void 0) return;
|
|
9932
|
+
for (const key of OPENFORT_BROWSER_STORAGE_KEYS) localStorage.removeItem(`${scope}.${key}`);
|
|
9933
|
+
}
|
|
9934
|
+
/**
|
|
9935
|
+
* The Openfort error code that names the stale-user class (#1435).
|
|
9936
|
+
* `getThirdPartyAuthToken` skips `authenticateThirdParty` while a `userId` sits
|
|
9937
|
+
* in scoped storage. A purged `userId` therefore pins every later call to a 401
|
|
9938
|
+
* that `extractApiError` reports as `USER_NOT_FOUND`.
|
|
9939
|
+
*
|
|
9940
|
+
* The set holds one member on purpose. Session-expiry codes (`SESSION_EXPIRED`,
|
|
9941
|
+
* `NOT_LOGGED_IN`, `INVALID_TOKEN`, `REFRESH_TOKEN_ERROR`) are a different
|
|
9942
|
+
* cause, and a bare 401 is a different cause again: `app-env-allowlist` is a
|
|
9943
|
+
* 401 by definition (`packages/errors/src/errors.ts:80-81`). Healing those and
|
|
9944
|
+
* tagging them `stale-openfort-cache` would delete the triage signal the tag
|
|
9945
|
+
* exists to carry.
|
|
9946
|
+
*/
|
|
9947
|
+
const STALE_USER_ERROR_CODES = /* @__PURE__ */ new Set(["USER_NOT_FOUND"]);
|
|
9948
|
+
/**
|
|
9949
|
+
* The one predicate that opens the heal. It reads the Openfort error code and
|
|
9950
|
+
* never the message text (ADR-0023 R4). It walks the cause chain the way
|
|
9951
|
+
* `isTransportError` walks it in the Convex transport adapter.
|
|
9952
|
+
*
|
|
9953
|
+
* Known ceiling (#1435): a 401 that carries no recognized code is NOT healed.
|
|
9954
|
+
* `extractApiError` keeps the status only on `AuthenticationError`, so such a
|
|
9955
|
+
* payload is reachable. Add the code a live payload shows; do not add a message
|
|
9956
|
+
* match, because that trades one bug class for an ADR-0023 R4 violation.
|
|
9957
|
+
*/
|
|
9958
|
+
function isStaleUserSignal(cause) {
|
|
9959
|
+
for (const link of causeChain(cause)) {
|
|
9960
|
+
const error = link;
|
|
9961
|
+
if (typeof error.error === "string" && STALE_USER_ERROR_CODES.has(error.error) || typeof error.code === "string" && STALE_USER_ERROR_CODES.has(error.code)) return true;
|
|
9962
|
+
}
|
|
9963
|
+
return false;
|
|
9964
|
+
}
|
|
9965
|
+
function createOpenfortBrowserSignerFromBootstrap(bootstrap, options = {}) {
|
|
9966
|
+
const diagnostic = options.diagnostic;
|
|
9967
|
+
const authBaseUrl = normalizeBetterAuthBaseUrl(bootstrap.authBaseUrl);
|
|
9968
|
+
let currentStatus = "unknown";
|
|
9969
|
+
const statusListeners = /* @__PURE__ */ new Set();
|
|
9970
|
+
function setStatus(next) {
|
|
9971
|
+
if (currentStatus === next) return;
|
|
9972
|
+
currentStatus = next;
|
|
9973
|
+
for (const listener of statusListeners) try {
|
|
9974
|
+
listener(next);
|
|
9975
|
+
} catch {
|
|
9976
|
+
statusListeners.delete(listener);
|
|
9977
|
+
diagnostic?.trace("openfort.signerStatusListener", {
|
|
9978
|
+
ok: false,
|
|
9979
|
+
failure_mode: "unknown"
|
|
9980
|
+
});
|
|
9981
|
+
}
|
|
9982
|
+
}
|
|
9983
|
+
const statusStore = {
|
|
9984
|
+
status: () => currentStatus,
|
|
9985
|
+
subscribe: (listener) => {
|
|
9986
|
+
statusListeners.add(listener);
|
|
9987
|
+
return () => {
|
|
9988
|
+
statusListeners.delete(listener);
|
|
9989
|
+
};
|
|
9990
|
+
}
|
|
9991
|
+
};
|
|
9992
|
+
/**
|
|
9993
|
+
* Closes the black hole: when the browser has no Web Crypto, OpenFort's
|
|
9994
|
+
* `configure` would resolve to no address and the failure would be reported
|
|
9995
|
+
* as `unknown`. Detect it before any network or wallet work. Tag it
|
|
9996
|
+
* `no-secure-context` on a PROVIDER_ERROR scoped to `configure`. Add a
|
|
9997
|
+
* DiagnosticPort breadcrumb. The assembled Core SDK boundary reports it.
|
|
9998
|
+
*/
|
|
9999
|
+
function failNoSecureContext() {
|
|
10000
|
+
diagnostic?.trace("openfort.configure", {
|
|
10001
|
+
ok: false,
|
|
10002
|
+
failure_mode: "no-secure-context"
|
|
10003
|
+
});
|
|
10004
|
+
throw failOpenfort("configure", "no-secure-context", /* @__PURE__ */ new Error("Web Crypto unavailable: browser is not a secure context"));
|
|
10005
|
+
}
|
|
10006
|
+
function betterAuthSessionUrl() {
|
|
10007
|
+
return `${authBaseUrl}/get-session`;
|
|
10008
|
+
}
|
|
10009
|
+
function encryptionSessionUrl() {
|
|
10010
|
+
return `${authBaseUrl}/encryption-session`;
|
|
10011
|
+
}
|
|
10012
|
+
async function fetchBetterAuthAccessToken() {
|
|
10013
|
+
try {
|
|
10014
|
+
const response = await fetch(betterAuthSessionUrl(), { credentials: "include" });
|
|
10015
|
+
if (!response.ok) {
|
|
10016
|
+
diagnostic?.trace("openfort.token", {
|
|
10017
|
+
ok: false,
|
|
10018
|
+
tokenPresent: false,
|
|
10019
|
+
httpStatus: response.status,
|
|
10020
|
+
failure_mode: "unknown"
|
|
10021
|
+
});
|
|
10022
|
+
return null;
|
|
10023
|
+
}
|
|
10024
|
+
const token = (await response.json()).session?.token?.trim();
|
|
10025
|
+
if (token === void 0 || token.length === 0) {
|
|
10026
|
+
diagnostic?.trace("openfort.token", {
|
|
10027
|
+
ok: false,
|
|
10028
|
+
tokenPresent: false,
|
|
10029
|
+
failure_mode: "unknown"
|
|
10030
|
+
});
|
|
10031
|
+
return null;
|
|
10032
|
+
}
|
|
10033
|
+
diagnostic?.trace("openfort.token", {
|
|
10034
|
+
ok: true,
|
|
10035
|
+
tokenPresent: true
|
|
10036
|
+
});
|
|
10037
|
+
return token;
|
|
10038
|
+
} catch (cause) {
|
|
10039
|
+
diagnostic?.trace("openfort.token", {
|
|
10040
|
+
ok: false,
|
|
10041
|
+
tokenPresent: false,
|
|
10042
|
+
failure_mode: "unknown"
|
|
10043
|
+
});
|
|
10044
|
+
throw openfortProviderError("token", cause);
|
|
10045
|
+
}
|
|
10046
|
+
}
|
|
10047
|
+
const openfort = new Openfort({
|
|
10048
|
+
baseConfiguration: { publishableKey: bootstrap.openfortPublishableKey },
|
|
10049
|
+
shieldConfiguration: { shieldPublishableKey: bootstrap.shieldPublishableKey },
|
|
10050
|
+
thirdPartyAuth: {
|
|
10051
|
+
provider: ThirdPartyOAuthProvider.BETTER_AUTH,
|
|
10052
|
+
getAccessToken: fetchBetterAuthAccessToken
|
|
10053
|
+
}
|
|
10054
|
+
});
|
|
10055
|
+
async function configureEmbeddedWallet(encryptionSession) {
|
|
10056
|
+
await openfort.embeddedWallet.configure({
|
|
10057
|
+
accountType: AccountTypeEnum.EOA,
|
|
10058
|
+
chainType: ChainTypeEnum.EVM,
|
|
10059
|
+
recoveryParams: {
|
|
10060
|
+
recoveryMethod: RecoveryMethod.AUTOMATIC,
|
|
10061
|
+
encryptionSession
|
|
10062
|
+
}
|
|
10063
|
+
});
|
|
10064
|
+
}
|
|
10065
|
+
/**
|
|
10066
|
+
* ONE heal cycle on the stale-user rejection (#1435). Clear the scoped
|
|
10067
|
+
* storage that pins the dead `userId`, run the existing configure path — it
|
|
10068
|
+
* re-runs third-party auth against the live Better Auth session now that no
|
|
10069
|
+
* `userId` is cached — and retry the read once. Exactly one cycle: a second
|
|
10070
|
+
* rejection is terminal and carries `failure_mode: "stale-openfort-cache"`.
|
|
10071
|
+
* The caller stays in `recovering` throughout; only the outcome moves it.
|
|
10072
|
+
*/
|
|
10073
|
+
async function healStaleOpenfortCache(encryptionSession) {
|
|
10074
|
+
try {
|
|
10075
|
+
clearStaleOpenfortBrowserStorage(bootstrap.openfortPublishableKey);
|
|
10076
|
+
diagnostic?.trace("openfort.storageCleared", { beforeConfigure: false });
|
|
10077
|
+
await configureEmbeddedWallet(encryptionSession);
|
|
10078
|
+
await openfort.embeddedWallet.get();
|
|
10079
|
+
} catch (cause) {
|
|
10080
|
+
diagnostic?.trace("openfort.staleCacheHeal", {
|
|
10081
|
+
ok: false,
|
|
10082
|
+
failure_mode: "stale-openfort-cache"
|
|
10083
|
+
});
|
|
10084
|
+
throw failOpenfort("get", "stale-openfort-cache", cause);
|
|
10085
|
+
}
|
|
10086
|
+
diagnostic?.trace("openfort.staleCacheHeal", { ok: true });
|
|
10087
|
+
}
|
|
10088
|
+
let walletReadyPromise = null;
|
|
10089
|
+
function startWalletReady() {
|
|
10090
|
+
return (async () => {
|
|
10091
|
+
if (isInsecureBrowserContext()) failNoSecureContext();
|
|
10092
|
+
await openfort.waitForInitialization();
|
|
10093
|
+
const accessToken = await fetchBetterAuthAccessToken();
|
|
10094
|
+
if (accessToken === null) throw openfortProviderError("token", /* @__PURE__ */ new Error("Better Auth access token unavailable for Openfort"));
|
|
10095
|
+
let encryptionResponse;
|
|
10096
|
+
try {
|
|
10097
|
+
encryptionResponse = await fetch(encryptionSessionUrl(), {
|
|
10098
|
+
method: "POST",
|
|
10099
|
+
credentials: "include",
|
|
10100
|
+
headers: {
|
|
10101
|
+
Authorization: `Bearer ${accessToken}`,
|
|
10102
|
+
"Content-Type": "application/json"
|
|
10103
|
+
},
|
|
10104
|
+
body: JSON.stringify({})
|
|
10105
|
+
});
|
|
10106
|
+
} catch (cause) {
|
|
10107
|
+
diagnostic?.trace("openfort.encryptionSession", {
|
|
10108
|
+
ok: false,
|
|
10109
|
+
failure_mode: "unknown"
|
|
10110
|
+
});
|
|
10111
|
+
throw openfortProviderError("encryptionSession", cause);
|
|
10112
|
+
}
|
|
10113
|
+
if (!encryptionResponse.ok) {
|
|
10114
|
+
diagnostic?.trace("openfort.encryptionSession", {
|
|
10115
|
+
ok: false,
|
|
10116
|
+
httpStatus: encryptionResponse.status,
|
|
10117
|
+
failure_mode: "unknown"
|
|
10118
|
+
});
|
|
10119
|
+
throw Errors.providerError("openfort", "encryptionSession", /* @__PURE__ */ new Error(`Openfort encryption session failed (${encryptionResponse.status})`), { failure_mode: "unknown" });
|
|
10120
|
+
}
|
|
10121
|
+
let encryptionBody;
|
|
10122
|
+
try {
|
|
10123
|
+
encryptionBody = await encryptionResponse.json();
|
|
10124
|
+
if (typeof encryptionBody.sessionId !== "string" || encryptionBody.sessionId.length === 0) throw new Error("Openfort encryption session response missing sessionId");
|
|
10125
|
+
} catch (cause) {
|
|
10126
|
+
diagnostic?.trace("openfort.encryptionSession", {
|
|
10127
|
+
ok: false,
|
|
10128
|
+
httpStatus: encryptionResponse.status,
|
|
10129
|
+
failure_mode: "unknown"
|
|
10130
|
+
});
|
|
10131
|
+
throw openfortProviderError("encryptionSession", cause);
|
|
10132
|
+
}
|
|
10133
|
+
diagnostic?.trace("openfort.encryptionSession", {
|
|
10134
|
+
ok: true,
|
|
10135
|
+
httpStatus: encryptionResponse.status
|
|
10136
|
+
});
|
|
10137
|
+
let embeddedState;
|
|
10138
|
+
try {
|
|
10139
|
+
embeddedState = await openfort.embeddedWallet.getEmbeddedState();
|
|
10140
|
+
diagnostic?.trace("openfort.embeddedState", { state: embeddedState });
|
|
10141
|
+
} catch (cause) {
|
|
10142
|
+
diagnostic?.trace("openfort.embeddedState", {
|
|
10143
|
+
ok: false,
|
|
10144
|
+
failure_mode: "unknown"
|
|
10145
|
+
});
|
|
10146
|
+
throw openfortProviderError("embeddedState", cause);
|
|
10147
|
+
}
|
|
10148
|
+
if (embeddedState !== EmbeddedState.READY) {
|
|
10149
|
+
clearStaleOpenfortBrowserStorage(bootstrap.openfortPublishableKey);
|
|
10150
|
+
diagnostic?.trace("openfort.storageCleared", { beforeConfigure: true });
|
|
10151
|
+
try {
|
|
10152
|
+
await configureEmbeddedWallet(encryptionBody.sessionId);
|
|
10153
|
+
diagnostic?.trace("openfort.configure", { ok: true });
|
|
10154
|
+
} catch (cause) {
|
|
10155
|
+
const error = openfortProviderError("configure", cause);
|
|
10156
|
+
diagnostic?.trace("openfort.configure", {
|
|
10157
|
+
ok: false,
|
|
10158
|
+
failure_mode: openfortFailureMode(error)
|
|
10159
|
+
});
|
|
10160
|
+
throw error;
|
|
10161
|
+
}
|
|
10162
|
+
}
|
|
10163
|
+
try {
|
|
10164
|
+
await openfort.embeddedWallet.get();
|
|
10165
|
+
diagnostic?.trace("openfort.get", { ok: true });
|
|
10166
|
+
} catch (cause) {
|
|
10167
|
+
if (!isStaleUserSignal(cause)) {
|
|
10168
|
+
diagnostic?.trace("openfort.get", {
|
|
10169
|
+
ok: false,
|
|
10170
|
+
failure_mode: "unknown"
|
|
10171
|
+
});
|
|
10172
|
+
throw openfortProviderError("get", cause);
|
|
10173
|
+
}
|
|
10174
|
+
diagnostic?.trace("openfort.get", {
|
|
10175
|
+
ok: false,
|
|
10176
|
+
failure_mode: "stale-openfort-cache"
|
|
10177
|
+
});
|
|
10178
|
+
await healStaleOpenfortCache(encryptionBody.sessionId);
|
|
10179
|
+
}
|
|
10180
|
+
})();
|
|
10181
|
+
}
|
|
10182
|
+
async function ensureOpenfortWalletReady() {
|
|
10183
|
+
let joined = walletReadyPromise;
|
|
10184
|
+
if (joined === null) {
|
|
10185
|
+
joined = startWalletReady();
|
|
10186
|
+
walletReadyPromise = joined;
|
|
10187
|
+
setStatus("recovering");
|
|
10188
|
+
}
|
|
10189
|
+
try {
|
|
10190
|
+
await joined;
|
|
10191
|
+
if (walletReadyPromise === joined) setStatus("ready");
|
|
10192
|
+
} catch (cause) {
|
|
10193
|
+
if (walletReadyPromise === joined) {
|
|
10194
|
+
walletReadyPromise = null;
|
|
10195
|
+
setStatus("unavailable");
|
|
10196
|
+
}
|
|
10197
|
+
throw cause;
|
|
10198
|
+
}
|
|
10199
|
+
}
|
|
10200
|
+
const signer = openfortEmbeddedSignerFromWallet({
|
|
10201
|
+
embeddedWallet: openfort.embeddedWallet,
|
|
10202
|
+
ensureWalletReady: ensureOpenfortWalletReady
|
|
10203
|
+
});
|
|
10204
|
+
const controlledSign = options.signUserOpHash;
|
|
10205
|
+
const signEmbeddedHash = controlledSign === void 0 ? signer.signUserOpHash : (hash) => controlledSign(hash, signer.signUserOpHash);
|
|
10206
|
+
const resetReadiness = () => {
|
|
10207
|
+
walletReadyPromise = null;
|
|
10208
|
+
clearStaleOpenfortBrowserStorage(bootstrap.openfortPublishableKey);
|
|
10209
|
+
signer.resetAddressCache();
|
|
10210
|
+
setStatus("unknown");
|
|
10211
|
+
};
|
|
10212
|
+
const signUserOpHash = async (hash) => {
|
|
10213
|
+
try {
|
|
10214
|
+
return await signEmbeddedHash(hash);
|
|
10215
|
+
} catch (cause) {
|
|
10216
|
+
const providerErrorCode = openfortSignerNotReadyCode(cause);
|
|
10217
|
+
if (providerErrorCode === void 0) throw cause;
|
|
10218
|
+
diagnostic?.trace("openfort.signerRecovery", {
|
|
10219
|
+
outcome: "started",
|
|
10220
|
+
attempt: 1,
|
|
10221
|
+
failure_mode: "signer-not-ready"
|
|
10222
|
+
});
|
|
10223
|
+
resetReadiness();
|
|
10224
|
+
try {
|
|
10225
|
+
await ensureOpenfortWalletReady();
|
|
10226
|
+
const signature = await signEmbeddedHash(hash);
|
|
10227
|
+
diagnostic?.trace("openfort.signerRecovery", {
|
|
10228
|
+
outcome: "succeeded",
|
|
10229
|
+
attempt: 1,
|
|
10230
|
+
failure_mode: "signer-not-ready"
|
|
10231
|
+
});
|
|
10232
|
+
return signature;
|
|
10233
|
+
} catch (retryCause) {
|
|
10234
|
+
const retryFailure = signerFailure("openfort-embedded", "signUserOpHash", retryCause);
|
|
10235
|
+
if (retryFailure.code === "SIGNER_REJECTED") throw retryFailure;
|
|
10236
|
+
if (isSignerNotReadySignal(retryCause)) {
|
|
10237
|
+
walletReadyPromise = null;
|
|
10238
|
+
signer.resetAddressCache();
|
|
10239
|
+
setStatus("unavailable");
|
|
10240
|
+
}
|
|
10241
|
+
diagnostic?.trace("openfort.signerRecovery", {
|
|
10242
|
+
outcome: "failed",
|
|
10243
|
+
attempt: 1,
|
|
10244
|
+
failure_mode: isSignerNotReadySignal(retryCause) ? "signer-not-ready" : "unknown"
|
|
10245
|
+
});
|
|
10246
|
+
throw Errors.providerError("openfort", "signerRecovery", retryCause, {
|
|
10247
|
+
failure_mode: "signer-not-ready",
|
|
10248
|
+
details: {
|
|
10249
|
+
provider_error_source: "openfort",
|
|
10250
|
+
provider_error_code: providerErrorCode
|
|
10251
|
+
}
|
|
10252
|
+
});
|
|
10253
|
+
}
|
|
10254
|
+
}
|
|
10255
|
+
};
|
|
10256
|
+
return {
|
|
10257
|
+
...signer,
|
|
10258
|
+
signUserOpHash,
|
|
10259
|
+
statusStore,
|
|
10260
|
+
ensureWalletReady: ensureOpenfortWalletReady,
|
|
10261
|
+
getAddress: async () => {
|
|
10262
|
+
try {
|
|
10263
|
+
const address = await signer.getAddress();
|
|
10264
|
+
diagnostic?.trace("openfort.address", { ok: true });
|
|
10265
|
+
return address;
|
|
10266
|
+
} catch (cause) {
|
|
10267
|
+
diagnostic?.trace("openfort.address", {
|
|
10268
|
+
ok: false,
|
|
10269
|
+
failure_mode: "unknown"
|
|
10270
|
+
});
|
|
10271
|
+
throw openfortProviderError("getAddress", cause);
|
|
10272
|
+
}
|
|
10273
|
+
},
|
|
10274
|
+
resetSession: resetReadiness
|
|
10275
|
+
};
|
|
10276
|
+
}
|
|
10277
|
+
function normalizeBetterAuthBaseUrl(raw) {
|
|
10278
|
+
const trimmed = raw.replace(/\/$/, "");
|
|
10279
|
+
return trimmed.endsWith("/api/auth") ? trimmed : `${trimmed}/api/auth`;
|
|
7173
10280
|
}
|
|
7174
|
-
var SmartAccountPortTag = class extends Context.Service()("@capxul/sdk/ports/SmartAccountPort") {};
|
|
7175
10281
|
//#endregion
|
|
7176
10282
|
//#region src/flows/identity.ts
|
|
7177
10283
|
const IDENTITY_SLOT = {
|
|
@@ -8060,8 +11166,52 @@ const loadIdentityProgram = Effect.gen(function* () {
|
|
|
8060
11166
|
if (authUserId === void 0) return null;
|
|
8061
11167
|
return yield* deps.identityPort.loadByAuthUserId(authUserId).pipe(Effect.catchDefect((cause) => Effect.fail(identityErrorFromCapxul("loadByAuthUserId", cause instanceof CapxulError ? cause : Errors.unknown(cause), cause))), Effect.mapError((failure) => failure.publicError));
|
|
8062
11168
|
});
|
|
8063
|
-
|
|
8064
|
-
|
|
11169
|
+
/**
|
|
11170
|
+
* THE ONE boundary grammar for those optional fields. Both `completeProfile`
|
|
11171
|
+
* entry points — the method bundle here and the identity runtime in
|
|
11172
|
+
* `create-capxul-client.ts` — run it, so a malformed value is refused the same
|
|
11173
|
+
* way at each and never reaches the port.
|
|
11174
|
+
*/
|
|
11175
|
+
function checkProfileCarriage(profile) {
|
|
11176
|
+
const carriage = {};
|
|
11177
|
+
if (profile.imageStorageId !== void 0) {
|
|
11178
|
+
if (typeof profile.imageStorageId !== "string" || profile.imageStorageId.trim().length === 0) return {
|
|
11179
|
+
ok: false,
|
|
11180
|
+
field: "imageStorageId",
|
|
11181
|
+
message: "must be a non-empty storage id"
|
|
11182
|
+
};
|
|
11183
|
+
carriage.imageStorageId = profile.imageStorageId.trim();
|
|
11184
|
+
}
|
|
11185
|
+
if (profile.payoutAddresses !== void 0) {
|
|
11186
|
+
if (!Array.isArray(profile.payoutAddresses)) return {
|
|
11187
|
+
ok: false,
|
|
11188
|
+
field: "payoutAddresses",
|
|
11189
|
+
message: "must be an array"
|
|
11190
|
+
};
|
|
11191
|
+
if (profile.payoutAddresses.length > 10) return {
|
|
11192
|
+
ok: false,
|
|
11193
|
+
field: "payoutAddresses",
|
|
11194
|
+
message: `must carry at most 10 addresses`
|
|
11195
|
+
};
|
|
11196
|
+
const entries = [];
|
|
11197
|
+
for (const entry of profile.payoutAddresses) {
|
|
11198
|
+
if (typeof entry !== "object" || entry === null || !PAYOUT_CHAINS.some((chain) => chain === entry.chain) || typeof entry.address !== "string" || entry.address.trim().length === 0) return {
|
|
11199
|
+
ok: false,
|
|
11200
|
+
field: "payoutAddresses",
|
|
11201
|
+
message: `each entry needs a chain of ${PAYOUT_CHAINS.join(", ")} and an address`
|
|
11202
|
+
};
|
|
11203
|
+
entries.push({
|
|
11204
|
+
chain: entry.chain,
|
|
11205
|
+
address: entry.address.trim()
|
|
11206
|
+
});
|
|
11207
|
+
}
|
|
11208
|
+
carriage.payoutAddresses = entries;
|
|
11209
|
+
}
|
|
11210
|
+
return {
|
|
11211
|
+
ok: true,
|
|
11212
|
+
carriage
|
|
11213
|
+
};
|
|
11214
|
+
}
|
|
8065
11215
|
function makeIdentityMethods(deps) {
|
|
8066
11216
|
const convexCall = deps.convexCall;
|
|
8067
11217
|
return {
|
|
@@ -8100,6 +11250,11 @@ function makeIdentityMethods(deps) {
|
|
|
8100
11250
|
error: Errors.invalidInput("country", "unsupported country")
|
|
8101
11251
|
};
|
|
8102
11252
|
}
|
|
11253
|
+
const carried = checkProfileCarriage(profile);
|
|
11254
|
+
if (!carried.ok) return {
|
|
11255
|
+
ok: false,
|
|
11256
|
+
error: Errors.invalidInput(carried.field, carried.message)
|
|
11257
|
+
};
|
|
8103
11258
|
const session = deps.actor?.authSession();
|
|
8104
11259
|
if (session === void 0 || session === null) return {
|
|
8105
11260
|
ok: false,
|
|
@@ -8110,7 +11265,8 @@ function makeIdentityMethods(deps) {
|
|
|
8110
11265
|
email: session.email,
|
|
8111
11266
|
displayName,
|
|
8112
11267
|
country,
|
|
8113
|
-
handle
|
|
11268
|
+
handle,
|
|
11269
|
+
...carried.carriage
|
|
8114
11270
|
}), options, CAPXUL_OPERATIONS.identity.completeProfile);
|
|
8115
11271
|
},
|
|
8116
11272
|
async handleAvailable(handle, options) {
|
|
@@ -10096,6 +13252,11 @@ function assembleCapxulClient(input) {
|
|
|
10096
13252
|
ok: false,
|
|
10097
13253
|
reason: "INVALID_INPUT"
|
|
10098
13254
|
};
|
|
13255
|
+
const carried = checkProfileCarriage(profile);
|
|
13256
|
+
if (!carried.ok) return {
|
|
13257
|
+
ok: false,
|
|
13258
|
+
reason: "INVALID_INPUT"
|
|
13259
|
+
};
|
|
10099
13260
|
const session = actor.authSession();
|
|
10100
13261
|
if (session === null) return {
|
|
10101
13262
|
ok: false,
|
|
@@ -10106,7 +13267,8 @@ function assembleCapxulClient(input) {
|
|
|
10106
13267
|
email: session.email,
|
|
10107
13268
|
displayName,
|
|
10108
13269
|
country,
|
|
10109
|
-
handle
|
|
13270
|
+
handle,
|
|
13271
|
+
...carried.carriage
|
|
10110
13272
|
}), controls, CAPXUL_OPERATIONS.identity.completeProfile, effectRunner.runPromise);
|
|
10111
13273
|
return result.ok ? { ok: true } : {
|
|
10112
13274
|
ok: false,
|
|
@@ -10205,4 +13367,721 @@ function withHostObservationControls(snapshot, input, actorId) {
|
|
|
10205
13367
|
});
|
|
10206
13368
|
}
|
|
10207
13369
|
//#endregion
|
|
10208
|
-
|
|
13370
|
+
//#region src/telemetry/from-posthog.ts
|
|
13371
|
+
/**
|
|
13372
|
+
* Drop props that must never cross to a host-owned external sink. Today that is
|
|
13373
|
+
* the `$exception` `details` blob: `captureException` serializes
|
|
13374
|
+
* `CapxulError.details` (e.g. `{ asset, available, required }`, `{ name }`,
|
|
13375
|
+
* `{ accountId }` — errors.ts) into it, and the shared redactor has no
|
|
13376
|
+
* `$exception` rule, so it is stripped here at the boundary (infra#1037). The
|
|
13377
|
+
* safe fields (error code, operation, failure_mode, the fixed leak-safe message,
|
|
13378
|
+
* stack frames) are preserved.
|
|
13379
|
+
*/
|
|
13380
|
+
function stripHostUnsafeProps(props) {
|
|
13381
|
+
if (props === void 0) return void 0;
|
|
13382
|
+
const { details: _details, ...safe } = props;
|
|
13383
|
+
return safe;
|
|
13384
|
+
}
|
|
13385
|
+
/**
|
|
13386
|
+
* The subset of a posthog-js client the seam calls. `identify` / `group` /
|
|
13387
|
+
* `reset` are optional — a host that only wants event capture can omit them.
|
|
13388
|
+
*/
|
|
13389
|
+
/**
|
|
13390
|
+
* Adapt the host's already-initialized posthog-like client into a
|
|
13391
|
+
* `TelemetryPort` for the `telemetry` prop / input. This port SUPPLANTS the
|
|
13392
|
+
* SDK's no-op default telemetry sink (`production.ts` binds it via
|
|
13393
|
+
* `Layer.succeed`, not `compose` — there is no client-side success relay to
|
|
13394
|
+
* compose with); it is additive to Capxul's backend first-party record and
|
|
13395
|
+
* never owns the client.
|
|
13396
|
+
*/
|
|
13397
|
+
function postHogProductTelemetry(policy, fixedSnapshot) {
|
|
13398
|
+
const telemetry = new PostHogTelemetryAdapter({
|
|
13399
|
+
capture: (name, props, source) => {
|
|
13400
|
+
const snapshot = readInvocationObservation(source) ?? fixedSnapshot ?? policy.snapshot();
|
|
13401
|
+
if (!snapshot.active || policy.client === null || policy.client === void 0) return;
|
|
13402
|
+
const event = stampTelemetryEnvelope({
|
|
13403
|
+
name,
|
|
13404
|
+
props
|
|
13405
|
+
}, {
|
|
13406
|
+
capxul_env: policy.capxulEnv,
|
|
13407
|
+
producer: "sdk"
|
|
13408
|
+
});
|
|
13409
|
+
policy.deliver(() => policy.client.capture(name, {
|
|
13410
|
+
...stripHostUnsafeProps(event.props),
|
|
13411
|
+
...observationContextProps(snapshot.context)
|
|
13412
|
+
}));
|
|
13413
|
+
},
|
|
13414
|
+
identify: (input) => {
|
|
13415
|
+
if (!(fixedSnapshot ?? policy.snapshot()).active || policy.client?.identify === void 0) return;
|
|
13416
|
+
policy.deliver(() => policy.client.identify(input.distinctId, {
|
|
13417
|
+
...input.traits,
|
|
13418
|
+
...input.properties
|
|
13419
|
+
}));
|
|
13420
|
+
},
|
|
13421
|
+
group: (input) => {
|
|
13422
|
+
if (!(fixedSnapshot ?? policy.snapshot()).active || policy.client?.group === void 0) return;
|
|
13423
|
+
policy.deliver(() => policy.client.group(input.groupType, input.groupKey, input.properties === void 0 ? void 0 : { ...input.properties }));
|
|
13424
|
+
},
|
|
13425
|
+
reset: () => {
|
|
13426
|
+
if (!(fixedSnapshot ?? policy.snapshot()).active || policy.client?.reset === void 0) return;
|
|
13427
|
+
policy.deliver(() => policy.client.reset());
|
|
13428
|
+
}
|
|
13429
|
+
});
|
|
13430
|
+
Object.defineProperty(telemetry, PRODUCT_INVOCATION, {
|
|
13431
|
+
enumerable: false,
|
|
13432
|
+
value: (source) => postHogProductTelemetry(policy, readInvocationObservation(source) ?? fixedSnapshot ?? policy.snapshot())
|
|
13433
|
+
});
|
|
13434
|
+
return telemetry;
|
|
13435
|
+
}
|
|
13436
|
+
//#endregion
|
|
13437
|
+
//#region src/host-observability.ts
|
|
13438
|
+
const HOST_INVOCATION = Symbol("capxul.host-observability-invocation");
|
|
13439
|
+
/** Build the SDK's one host module around an already-initialized PostHog client. */
|
|
13440
|
+
function postHogObservability(client, options = {}) {
|
|
13441
|
+
const inactive = Object.freeze({
|
|
13442
|
+
active: false,
|
|
13443
|
+
contextProps: Object.freeze({})
|
|
13444
|
+
});
|
|
13445
|
+
const snapshot = () => {
|
|
13446
|
+
if (client === null || client === void 0) return inactive;
|
|
13447
|
+
try {
|
|
13448
|
+
if (!(typeof options.enabled === "function" ? options.enabled() : options.enabled ?? true)) return inactive;
|
|
13449
|
+
const sanitized = sanitizeObservationContext(typeof options.context === "function" ? options.context() : options.context);
|
|
13450
|
+
const context = sanitized === void 0 ? void 0 : Object.freeze({ ...sanitized });
|
|
13451
|
+
return Object.freeze({
|
|
13452
|
+
active: true,
|
|
13453
|
+
...context === void 0 ? {} : { context },
|
|
13454
|
+
contextProps: Object.freeze(observationContextProps(context))
|
|
13455
|
+
});
|
|
13456
|
+
} catch {
|
|
13457
|
+
return inactive;
|
|
13458
|
+
}
|
|
13459
|
+
};
|
|
13460
|
+
const policy = {
|
|
13461
|
+
client,
|
|
13462
|
+
capxulEnv: options.capxulEnv ?? "unknown",
|
|
13463
|
+
snapshot,
|
|
13464
|
+
deliver: (capture) => {
|
|
13465
|
+
try {
|
|
13466
|
+
const delivery = capture();
|
|
13467
|
+
if (isPromiseLike(delivery)) Promise.resolve(delivery).catch(() => void 0);
|
|
13468
|
+
} catch {}
|
|
13469
|
+
}
|
|
13470
|
+
};
|
|
13471
|
+
const module = {
|
|
13472
|
+
failures: postHogFailureObservation(policy),
|
|
13473
|
+
product: postHogProductTelemetry(policy)
|
|
13474
|
+
};
|
|
13475
|
+
Object.defineProperty(module, HOST_INVOCATION, {
|
|
13476
|
+
enumerable: false,
|
|
13477
|
+
value: {
|
|
13478
|
+
bind: () => {
|
|
13479
|
+
const invocation = snapshot();
|
|
13480
|
+
return {
|
|
13481
|
+
failures: postHogFailureObservation(policy, invocation),
|
|
13482
|
+
product: postHogProductTelemetry(policy, invocation)
|
|
13483
|
+
};
|
|
13484
|
+
},
|
|
13485
|
+
snapshot
|
|
13486
|
+
}
|
|
13487
|
+
});
|
|
13488
|
+
return module;
|
|
13489
|
+
}
|
|
13490
|
+
/** @internal Bind both projections to one call-start decision/context snapshot. */
|
|
13491
|
+
function bindHostObservabilityInvocation(observability) {
|
|
13492
|
+
return observability?.[HOST_INVOCATION]?.bind() ?? observability;
|
|
13493
|
+
}
|
|
13494
|
+
/** @internal Read one call-start snapshot for actor/transition carriage. */
|
|
13495
|
+
function snapshotHostObservability(observability) {
|
|
13496
|
+
const snapshot = observability?.[HOST_INVOCATION]?.snapshot();
|
|
13497
|
+
return snapshot === void 0 ? void 0 : {
|
|
13498
|
+
active: snapshot.active,
|
|
13499
|
+
...snapshot.context === void 0 ? {} : { context: snapshot.context }
|
|
13500
|
+
};
|
|
13501
|
+
}
|
|
13502
|
+
function isPromiseLike(value) {
|
|
13503
|
+
return (typeof value === "object" && value !== null || typeof value === "function") && "then" in value;
|
|
13504
|
+
}
|
|
13505
|
+
const SDK_VERSION = version;
|
|
13506
|
+
/**
|
|
13507
|
+
* Project the built graph into the flat `FlowPorts` record the method bundles
|
|
13508
|
+
* still take. It is a VIEW of `ProductionAdapters.context`, taken from the
|
|
13509
|
+
* graph and never instead of it — the graph stays alive in the scope and is
|
|
13510
|
+
* handed to every caller.
|
|
13511
|
+
*/
|
|
13512
|
+
const collectProductionFlowPorts = Effect.gen(function* () {
|
|
13513
|
+
const authClient = yield* AuthClientPortTag;
|
|
13514
|
+
const authCache = yield* AuthCachePortTag;
|
|
13515
|
+
const bootstrap = yield* BootstrapPortTag;
|
|
13516
|
+
const clock = yield* ClockPortTag;
|
|
13517
|
+
const convexCall = yield* ConvexCallPortTag;
|
|
13518
|
+
return {
|
|
13519
|
+
authClient,
|
|
13520
|
+
authCache,
|
|
13521
|
+
identity: yield* IdentityPortTag,
|
|
13522
|
+
smartAccount: yield* SmartAccountPortTag,
|
|
13523
|
+
accountRead: yield* AccountReadPortTag,
|
|
13524
|
+
bootstrap,
|
|
13525
|
+
clock,
|
|
13526
|
+
telemetry: yield* TelemetryPortTag,
|
|
13527
|
+
convexCall
|
|
13528
|
+
};
|
|
13529
|
+
});
|
|
13530
|
+
function makeProductionAdapterLayerEntries(input) {
|
|
13531
|
+
const refreshConvexAuthRef = {
|
|
13532
|
+
current: null,
|
|
13533
|
+
pending: false
|
|
13534
|
+
};
|
|
13535
|
+
return [
|
|
13536
|
+
{
|
|
13537
|
+
name: "bootstrap",
|
|
13538
|
+
layer: productionBootstrapPortLayer(input.bootstrap)
|
|
13539
|
+
},
|
|
13540
|
+
{
|
|
13541
|
+
name: "authClient",
|
|
13542
|
+
layer: productionAuthClientLayer(input, refreshConvexAuthRef, input.resetSignerSession)
|
|
13543
|
+
},
|
|
13544
|
+
{
|
|
13545
|
+
name: "authCache",
|
|
13546
|
+
layer: productionAuthCacheLayer(input)
|
|
13547
|
+
},
|
|
13548
|
+
{
|
|
13549
|
+
name: "identity",
|
|
13550
|
+
layer: ConvexIdentityLayer()
|
|
13551
|
+
},
|
|
13552
|
+
{
|
|
13553
|
+
name: "smartAccount",
|
|
13554
|
+
layer: ConvexSmartAccountLayer()
|
|
13555
|
+
},
|
|
13556
|
+
{
|
|
13557
|
+
name: "accountRead",
|
|
13558
|
+
layer: ConvexAccountLayer()
|
|
13559
|
+
},
|
|
13560
|
+
{
|
|
13561
|
+
name: "clock",
|
|
13562
|
+
layer: SystemClockLayer()
|
|
13563
|
+
},
|
|
13564
|
+
{
|
|
13565
|
+
name: "telemetry",
|
|
13566
|
+
layer: input.telemetry === void 0 ? PostHogTelemetryLayer({ capture: () => void 0 }) : Layer.succeed(TelemetryPortTag, input.telemetry)
|
|
13567
|
+
},
|
|
13568
|
+
{
|
|
13569
|
+
name: "convexCall",
|
|
13570
|
+
layer: productionConvexCallLayer(input, refreshConvexAuthRef)
|
|
13571
|
+
},
|
|
13572
|
+
{
|
|
13573
|
+
name: "org",
|
|
13574
|
+
layer: Layer.effect(OrgPortTag, Effect.all([ConvexCallPortTag, TelemetryPortTag]).pipe(Effect.map(([convex, telemetry]) => new ConvexOrganizationAdapter({
|
|
13575
|
+
convex,
|
|
13576
|
+
telemetry
|
|
13577
|
+
}))))
|
|
13578
|
+
}
|
|
13579
|
+
];
|
|
13580
|
+
}
|
|
13581
|
+
function mergeProductionAdapterLayers(entries) {
|
|
13582
|
+
const byName = /* @__PURE__ */ new Map();
|
|
13583
|
+
for (const entry of entries) byName.set(entry.name, entry.layer);
|
|
13584
|
+
const authClientLayer = byName.get("authClient");
|
|
13585
|
+
const convexCallLayer = byName.get("convexCall");
|
|
13586
|
+
const telemetryLayer = byName.get("telemetry");
|
|
13587
|
+
const convexCallReadyLayer = convexCallLayer === void 0 || authClientLayer === void 0 ? convexCallLayer : convexCallLayer.pipe(Layer.provide(authClientLayer));
|
|
13588
|
+
const observedConvexCallLayer = convexCallReadyLayer === void 0 ? telemetryLayer : telemetryLayer === void 0 ? convexCallReadyLayer : Layer.merge(convexCallReadyLayer, telemetryLayer);
|
|
13589
|
+
return entries.map((entry) => {
|
|
13590
|
+
const layer = byName.get(entry.name) ?? entry.layer;
|
|
13591
|
+
if (entry.name === "convexCall") return convexCallReadyLayer ?? layer;
|
|
13592
|
+
if (entry.name === "accountRead" || entry.name === "org") return observedConvexCallLayer === void 0 ? layer : layer.pipe(Layer.provide(observedConvexCallLayer));
|
|
13593
|
+
if (isConvexDependentLayer(entry.name)) return convexCallReadyLayer === void 0 ? layer : layer.pipe(Layer.provide(convexCallReadyLayer));
|
|
13594
|
+
return layer;
|
|
13595
|
+
}).reduce((current, layer) => Layer.merge(current, layer), Layer.empty);
|
|
13596
|
+
}
|
|
13597
|
+
function isConvexDependentLayer(name) {
|
|
13598
|
+
return name === "identity" || name === "smartAccount" || name === "accountRead" || name === "org";
|
|
13599
|
+
}
|
|
13600
|
+
function productionBootstrapPortLayer(bootstrap) {
|
|
13601
|
+
return Layer.succeed(BootstrapPortTag, bootstrap);
|
|
13602
|
+
}
|
|
13603
|
+
function productionAuthClientLayer(input, refreshConvexAuthRef, resetSignerSessionRef) {
|
|
13604
|
+
return (input.authClient !== void 0 ? Layer.succeed(AuthClientPortTag, input.authClient) : input.runtime === "browser" ? BetterAuthBrowserLayer({
|
|
13605
|
+
authBaseUrl: input.runtimeUrls.authBaseUrl,
|
|
13606
|
+
...input.fetch === void 0 ? {} : { fetch: input.fetch },
|
|
13607
|
+
...input.observation === void 0 ? {} : { observation: input.observation }
|
|
13608
|
+
}) : BetterAuthNodeLayer({
|
|
13609
|
+
authBaseUrl: input.runtimeUrls.authBaseUrl,
|
|
13610
|
+
...input.origin === void 0 ? {} : { origin: input.origin },
|
|
13611
|
+
...input.fetch === void 0 ? {} : { fetch: input.fetch },
|
|
13612
|
+
...input.observation === void 0 ? {} : { observation: input.observation }
|
|
13613
|
+
})).pipe(Layer.flatMap((context) => {
|
|
13614
|
+
const refreshed = refreshConvexAuthOnSession(Context.get(context, AuthClientPortTag), () => {
|
|
13615
|
+
const refresh = refreshConvexAuthRef.current;
|
|
13616
|
+
if (refresh === null) {
|
|
13617
|
+
refreshConvexAuthRef.pending = true;
|
|
13618
|
+
return;
|
|
13619
|
+
}
|
|
13620
|
+
refresh();
|
|
13621
|
+
});
|
|
13622
|
+
return Layer.succeedContext(Context.make(AuthClientPortTag, resetSignerSessionRef === void 0 ? refreshed : resetSignerSessionOnSignOut(refreshed, resetSignerSessionRef)));
|
|
13623
|
+
}));
|
|
13624
|
+
}
|
|
13625
|
+
function productionAuthCacheLayer(input) {
|
|
13626
|
+
return Layer.succeed(AuthCachePortTag, input.authCache ?? detectAuthCacheAdapter());
|
|
13627
|
+
}
|
|
13628
|
+
function productionConvexCallLayer(input, refreshConvexAuthRef) {
|
|
13629
|
+
return Layer.unwrap(Effect.map(AuthClientPortTag, (authClient) => ConvexCallLayer({
|
|
13630
|
+
convexUrl: input.runtimeUrls.convexUrl,
|
|
13631
|
+
...input.applicationId === void 0 ? {} : { applicationId: input.applicationId },
|
|
13632
|
+
...input.observation === void 0 ? {} : { observation: input.observation },
|
|
13633
|
+
...input.convexClient === void 0 ? {} : { client: input.convexClient },
|
|
13634
|
+
tokenProvider: async ({ forceRefreshToken }) => {
|
|
13635
|
+
const tokenResult = await Effect.runPromise(Effect.result(authClient.getConvexJwt({
|
|
13636
|
+
forceRefresh: forceRefreshToken,
|
|
13637
|
+
...input.signal === void 0 ? {} : { signal: input.signal }
|
|
13638
|
+
})));
|
|
13639
|
+
if (Result.isFailure(tokenResult)) return null;
|
|
13640
|
+
return String(tokenResult.success.token);
|
|
13641
|
+
}
|
|
13642
|
+
}).pipe(Layer.flatMap((context) => {
|
|
13643
|
+
const convexCall = Context.get(context, ConvexCallPortTag);
|
|
13644
|
+
if (isRefreshableConvexCallPort(convexCall)) {
|
|
13645
|
+
refreshConvexAuthRef.current = () => convexCall.refreshAuth();
|
|
13646
|
+
if (refreshConvexAuthRef.pending) {
|
|
13647
|
+
refreshConvexAuthRef.pending = false;
|
|
13648
|
+
refreshConvexAuthRef.current();
|
|
13649
|
+
}
|
|
13650
|
+
}
|
|
13651
|
+
return Layer.succeedContext(context);
|
|
13652
|
+
}))));
|
|
13653
|
+
}
|
|
13654
|
+
function isRefreshableConvexCallPort(convexCall) {
|
|
13655
|
+
return "refreshAuth" in convexCall && typeof convexCall.refreshAuth === "function";
|
|
13656
|
+
}
|
|
13657
|
+
function resolveProductionObservability(input) {
|
|
13658
|
+
const observation = input.observability?.failures ?? input.observation;
|
|
13659
|
+
const telemetry = input.observability?.product ?? input.telemetry;
|
|
13660
|
+
const invocationObservability = input.invocationObservability;
|
|
13661
|
+
return {
|
|
13662
|
+
observation,
|
|
13663
|
+
telemetry,
|
|
13664
|
+
invocationObservation: invocationObservability?.failures ?? observation,
|
|
13665
|
+
invocationTelemetry: invocationObservability?.product ?? telemetry
|
|
13666
|
+
};
|
|
13667
|
+
}
|
|
13668
|
+
async function createProductionAdapters(input) {
|
|
13669
|
+
const { observation, telemetry, invocationObservation, invocationTelemetry } = resolveProductionObservability(input);
|
|
13670
|
+
const resolvedInput = resolveInput(input);
|
|
13671
|
+
if (!resolvedInput.ok) return resolvedInput;
|
|
13672
|
+
const scope = await Effect.runPromise(Scope.make());
|
|
13673
|
+
const closeScope = idempotentClose(() => Effect.runPromise(Scope.close(scope, Exit.void)));
|
|
13674
|
+
const bootstrapLayer = HttpBootstrapLayer({
|
|
13675
|
+
bootstrapBaseUrl: resolvedInput.value.bootstrapBaseUrl,
|
|
13676
|
+
...input.fetch === void 0 ? {} : { fetch: input.fetch },
|
|
13677
|
+
...invocationObservation === void 0 ? {} : { observation: invocationObservation }
|
|
13678
|
+
});
|
|
13679
|
+
try {
|
|
13680
|
+
const bootstrapContext = await Effect.runPromise(Layer.buildWithScope(bootstrapLayer, scope));
|
|
13681
|
+
const bootstrap = Context.get(bootstrapContext, BootstrapPortTag);
|
|
13682
|
+
const bootstrapResult = await runBootstrap(bootstrap.resolve({
|
|
13683
|
+
publishableKey: resolvedInput.value.publishableKey,
|
|
13684
|
+
...resolvedInput.value.origin === void 0 ? {} : { origin: resolvedInput.value.origin }
|
|
13685
|
+
}));
|
|
13686
|
+
if (!bootstrapResult.ok) {
|
|
13687
|
+
await emitBootstrapTelemetry(invocationTelemetry, {
|
|
13688
|
+
name: "bootstrap_failed",
|
|
13689
|
+
props: {
|
|
13690
|
+
...bootstrapTelemetryEnvelope(input, resolvedInput.value),
|
|
13691
|
+
reason: bootstrapResult.error.code,
|
|
13692
|
+
...failureDetail(bootstrapResult.error),
|
|
13693
|
+
...failureEvidenceProps(bootstrapResult.error)
|
|
13694
|
+
}
|
|
13695
|
+
});
|
|
13696
|
+
await closeScope().catch(() => void 0);
|
|
13697
|
+
return bootstrapResult;
|
|
13698
|
+
}
|
|
13699
|
+
const chainFence = assertDevKeySignerIsTestnetOnly(input.signer, bootstrapResult.value.chainId);
|
|
13700
|
+
if (!chainFence.ok) {
|
|
13701
|
+
await closeScope().catch(() => void 0);
|
|
13702
|
+
return chainFence;
|
|
13703
|
+
}
|
|
13704
|
+
const runtimeUrlsResult = resolveRuntimeUrls(bootstrapResult.value, resolvedInput.value.runtime, input.authBaseUrl);
|
|
13705
|
+
if (!runtimeUrlsResult.ok) {
|
|
13706
|
+
await emitBootstrapTelemetry(invocationTelemetry, {
|
|
13707
|
+
name: "bootstrap_failed",
|
|
13708
|
+
props: {
|
|
13709
|
+
...bootstrapTelemetryEnvelope(input, resolvedInput.value),
|
|
13710
|
+
applicationId: bootstrapResult.value.applicationId,
|
|
13711
|
+
reason: runtimeUrlsResult.error.code
|
|
13712
|
+
}
|
|
13713
|
+
});
|
|
13714
|
+
await closeScope().catch(() => void 0);
|
|
13715
|
+
return runtimeUrlsResult;
|
|
13716
|
+
}
|
|
13717
|
+
const runtimeUrls = runtimeUrlsResult;
|
|
13718
|
+
await emitBootstrapTelemetry(invocationTelemetry, {
|
|
13719
|
+
name: "bootstrap_resolved",
|
|
13720
|
+
props: {
|
|
13721
|
+
...bootstrapTelemetryEnvelope(input, resolvedInput.value),
|
|
13722
|
+
applicationId: bootstrapResult.value.applicationId
|
|
13723
|
+
}
|
|
13724
|
+
});
|
|
13725
|
+
let injectedClient;
|
|
13726
|
+
const convexClientFactory = input.convexClientFactory;
|
|
13727
|
+
if (convexClientFactory !== void 0) try {
|
|
13728
|
+
injectedClient = convexClientFactory(runtimeUrls.value.convexUrl);
|
|
13729
|
+
} catch (cause) {
|
|
13730
|
+
await closeScope().catch(() => void 0);
|
|
13731
|
+
return {
|
|
13732
|
+
ok: false,
|
|
13733
|
+
error: toPublicError(cause, "createProductionAdapters")
|
|
13734
|
+
};
|
|
13735
|
+
}
|
|
13736
|
+
const portsLayer = mergeProductionAdapterLayers(makeProductionAdapterLayerEntries({
|
|
13737
|
+
bootstrap,
|
|
13738
|
+
runtime: resolvedInput.value.runtime,
|
|
13739
|
+
...resolvedInput.value.origin === void 0 ? {} : { origin: resolvedInput.value.origin },
|
|
13740
|
+
runtimeUrls: runtimeUrls.value,
|
|
13741
|
+
chainId: bootstrapResult.value.chainId,
|
|
13742
|
+
applicationId: bootstrapResult.value.applicationId,
|
|
13743
|
+
...observation === void 0 ? {} : { observation },
|
|
13744
|
+
...input.authCache === void 0 ? {} : { authCache: input.authCache },
|
|
13745
|
+
...input.authClient === void 0 ? {} : { authClient: input.authClient },
|
|
13746
|
+
...telemetry === void 0 ? {} : { telemetry },
|
|
13747
|
+
...injectedClient === void 0 ? {} : { convexClient: injectedClient },
|
|
13748
|
+
...input.fetch === void 0 ? {} : { fetch: input.fetch },
|
|
13749
|
+
...input.signal === void 0 ? {} : { signal: input.signal },
|
|
13750
|
+
...input.resetSignerSession === void 0 ? {} : { resetSignerSession: input.resetSignerSession }
|
|
13751
|
+
}));
|
|
13752
|
+
const applicationLayer = Layer.merge(portsLayer, optionalEngineeringTelemetryLayer(bootstrapResult.value.engineeringTelemetry, resolvedInput.value.runtime));
|
|
13753
|
+
const context = await Effect.runPromise(Layer.buildWithScope(applicationLayer, scope));
|
|
13754
|
+
const ports = await Effect.runPromise(collectProductionFlowPorts.pipe(Effect.provide(context)));
|
|
13755
|
+
const stopConnectionObservation = observeProductionConvexConnection(ports.convexCall, context);
|
|
13756
|
+
const close = idempotentClose(async () => {
|
|
13757
|
+
try {
|
|
13758
|
+
stopConnectionObservation();
|
|
13759
|
+
} finally {
|
|
13760
|
+
await closeScope();
|
|
13761
|
+
}
|
|
13762
|
+
});
|
|
13763
|
+
return {
|
|
13764
|
+
ok: true,
|
|
13765
|
+
value: {
|
|
13766
|
+
context,
|
|
13767
|
+
ports,
|
|
13768
|
+
bootstrap: bootstrapResult.value,
|
|
13769
|
+
close
|
|
13770
|
+
}
|
|
13771
|
+
};
|
|
13772
|
+
} catch (cause) {
|
|
13773
|
+
await closeScope().catch(() => void 0);
|
|
13774
|
+
return {
|
|
13775
|
+
ok: false,
|
|
13776
|
+
error: toPublicError(cause, "createProductionAdapters")
|
|
13777
|
+
};
|
|
13778
|
+
}
|
|
13779
|
+
}
|
|
13780
|
+
function observeProductionConvexConnection(convexCall, context) {
|
|
13781
|
+
if (!("observeConnection" in convexCall) || typeof convexCall.observeConnection !== "function") return () => {};
|
|
13782
|
+
const runPromise = Effect.runPromiseWith(context);
|
|
13783
|
+
try {
|
|
13784
|
+
return convexCall.observeConnection((diagnostic) => {
|
|
13785
|
+
const message = diagnostic.transition === "connected" ? Effect.logInfo("convex.connection.changed") : Effect.logWarning("convex.connection.changed");
|
|
13786
|
+
runPromise(message.pipe(Effect.annotateLogs(diagnostic), Effect.catchCause(() => Effect.void))).catch(() => void 0);
|
|
13787
|
+
});
|
|
13788
|
+
} catch {
|
|
13789
|
+
return () => {};
|
|
13790
|
+
}
|
|
13791
|
+
}
|
|
13792
|
+
function optionalEngineeringTelemetryLayer(policy, runtime) {
|
|
13793
|
+
if (policy === void 0) return Layer.empty;
|
|
13794
|
+
try {
|
|
13795
|
+
return makeEngineeringTelemetryLayer({
|
|
13796
|
+
host: policy.host,
|
|
13797
|
+
headers: { authorization: `Bearer ${policy.projectToken}` },
|
|
13798
|
+
capxulEnv: policy.capxulEnv,
|
|
13799
|
+
producer: runtime === "browser" ? "browser" : "server",
|
|
13800
|
+
sdkVersion: SDK_VERSION
|
|
13801
|
+
});
|
|
13802
|
+
} catch {
|
|
13803
|
+
return Layer.empty;
|
|
13804
|
+
}
|
|
13805
|
+
}
|
|
13806
|
+
function refreshConvexAuthOnSession(authClient, refresh) {
|
|
13807
|
+
return {
|
|
13808
|
+
...authClient,
|
|
13809
|
+
verifyOtp: (input, options) => authClient.verifyOtp(input, options).pipe(Effect.tap(() => Effect.sync(refresh))),
|
|
13810
|
+
signOut: (options) => authClient.signOut(options).pipe(Effect.tap(() => Effect.sync(refresh)))
|
|
13811
|
+
};
|
|
13812
|
+
}
|
|
13813
|
+
/**
|
|
13814
|
+
* Signer-session reset, as a port wrapper INSIDE the Layer graph (blueprint §2:
|
|
13815
|
+
* "wrappers become Layers, not post-hoc spreads/Proxies").
|
|
13816
|
+
*
|
|
13817
|
+
* This was a spread over the assembled client — `{...client, auth: {...,
|
|
13818
|
+
* signOut}}` — applied after composition finished, so the reset lived on one
|
|
13819
|
+
* particular object rather than on the auth seam itself. Here it wraps
|
|
13820
|
+
* `AuthClientPort.signOut`, so it holds for every route to a sign-out.
|
|
13821
|
+
*
|
|
13822
|
+
* Capture the sign-out exit before reset. This preserves the old `try/finally`
|
|
13823
|
+
* order and keeps a reset failure in the typed error channel.
|
|
13824
|
+
*/
|
|
13825
|
+
function resetSignerSessionOnSignOut(authClient, resetRef) {
|
|
13826
|
+
return {
|
|
13827
|
+
...authClient,
|
|
13828
|
+
signOut: (options) => Effect.gen(function* () {
|
|
13829
|
+
const exit = yield* Effect.exit(authClient.signOut(options));
|
|
13830
|
+
yield* Effect.try({
|
|
13831
|
+
try: () => resetRef.current?.(),
|
|
13832
|
+
catch: (cause) => new AuthClientError({
|
|
13833
|
+
operation: "signOut",
|
|
13834
|
+
kind: "signer",
|
|
13835
|
+
cause
|
|
13836
|
+
})
|
|
13837
|
+
});
|
|
13838
|
+
return yield* exit;
|
|
13839
|
+
})
|
|
13840
|
+
};
|
|
13841
|
+
}
|
|
13842
|
+
async function createCapxulClient(input) {
|
|
13843
|
+
return createCapxulClientWithSignerControls(input, {});
|
|
13844
|
+
}
|
|
13845
|
+
function withSignerControl(signer, control) {
|
|
13846
|
+
if (signer === void 0 || control === void 0) return signer;
|
|
13847
|
+
return {
|
|
13848
|
+
source: signer.source,
|
|
13849
|
+
getAddress: () => signer.getAddress(),
|
|
13850
|
+
signUserOpHash: (hash) => control(hash, signer),
|
|
13851
|
+
...signer.statusStore === void 0 ? {} : { statusStore: signer.statusStore },
|
|
13852
|
+
...signer.ensureWalletReady === void 0 ? {} : { ensureWalletReady: signer.ensureWalletReady.bind(signer) },
|
|
13853
|
+
..."resetSession" in signer ? { resetSession: () => signer.resetSession() } : {}
|
|
13854
|
+
};
|
|
13855
|
+
}
|
|
13856
|
+
/** Shared production composition. The normal factory passes no controls. */
|
|
13857
|
+
async function createCapxulClientWithSignerControls(input, controls) {
|
|
13858
|
+
const invocationObservability = bindHostObservabilityInvocation(input.observability);
|
|
13859
|
+
const observation = invocationObservability?.failures ?? input.observation;
|
|
13860
|
+
const validation = validateCreateCapxulClientInput(input);
|
|
13861
|
+
if (!validation.ok) return observeFailedResult(validation, observation, "createCapxulClient");
|
|
13862
|
+
const resetSignerSession = { current: null };
|
|
13863
|
+
const adapters = await createProductionAdapters({
|
|
13864
|
+
...input,
|
|
13865
|
+
resetSignerSession,
|
|
13866
|
+
...invocationObservability === void 0 ? {} : { invocationObservability }
|
|
13867
|
+
});
|
|
13868
|
+
if (!adapters.ok) return observeFailedResult(adapters, observation, "createCapxulClient");
|
|
13869
|
+
try {
|
|
13870
|
+
const runtime = input.runtime ?? detectRuntime();
|
|
13871
|
+
const resolvedAuthBaseUrl = resolveBrowserAuthBaseUrl({
|
|
13872
|
+
bootstrapAuthBaseUrl: adapters.value.bootstrap.authBaseUrl,
|
|
13873
|
+
runtime,
|
|
13874
|
+
...input.authBaseUrl === void 0 ? {} : { override: input.authBaseUrl }
|
|
13875
|
+
});
|
|
13876
|
+
let signer = input.signer;
|
|
13877
|
+
if (signer === void 0 && runtime === "browser") signer = createOpenfortBrowserSignerFromBootstrap({
|
|
13878
|
+
...adapters.value.bootstrap,
|
|
13879
|
+
authBaseUrl: resolvedAuthBaseUrl
|
|
13880
|
+
}, {
|
|
13881
|
+
diagnostic: new ConsoleDiagnosticAdapter(),
|
|
13882
|
+
signUserOpHash: controls.signEmbeddedUserOpHash
|
|
13883
|
+
});
|
|
13884
|
+
signer = withSignerControl(signer, controls.signUserOpHash);
|
|
13885
|
+
if (signer !== void 0 && "resetSession" in signer) {
|
|
13886
|
+
const { resetSession, source } = signer;
|
|
13887
|
+
resetSignerSession.current = () => {
|
|
13888
|
+
try {
|
|
13889
|
+
resetSession();
|
|
13890
|
+
} catch (cause) {
|
|
13891
|
+
throw signerFailure(source, "resetSession", cause);
|
|
13892
|
+
}
|
|
13893
|
+
};
|
|
13894
|
+
}
|
|
13895
|
+
const client = assembleCapxulClient({
|
|
13896
|
+
ports: adapters.value.ports,
|
|
13897
|
+
bootstrap: adapters.value.bootstrap,
|
|
13898
|
+
authCache: adapters.value.ports.authCache,
|
|
13899
|
+
requirement: input.requirement ?? "none",
|
|
13900
|
+
...signer === void 0 ? {} : { signer },
|
|
13901
|
+
orgPort: Context.get(adapters.value.context, OrgPortTag),
|
|
13902
|
+
...signer === void 0 ? {} : { organizationSetup: new ConvexOrganizationSetupAdapter({
|
|
13903
|
+
convex: adapters.value.ports.convexCall,
|
|
13904
|
+
signer,
|
|
13905
|
+
chainId: adapters.value.bootstrap.chainId
|
|
13906
|
+
}) },
|
|
13907
|
+
...input.signal === void 0 ? {} : { signal: input.signal },
|
|
13908
|
+
...input.otpTtlMs === void 0 ? {} : { otpTtlMs: input.otpTtlMs },
|
|
13909
|
+
invokeTimeoutMs: input.invokeTimeoutMs ?? 3e4,
|
|
13910
|
+
...input.observability?.failures === void 0 && input.observation === void 0 ? {} : { failureObservation: input.observability?.failures ?? input.observation },
|
|
13911
|
+
...input.observability === void 0 ? {} : { hostObservationSnapshot: () => snapshotHostObservability(input.observability) },
|
|
13912
|
+
effectRunner: {
|
|
13913
|
+
runSync: Effect.runSyncWith(adapters.value.context),
|
|
13914
|
+
runPromise: Effect.runPromiseWith(adapters.value.context)
|
|
13915
|
+
}
|
|
13916
|
+
});
|
|
13917
|
+
const upstreamClose = adapters.value.close;
|
|
13918
|
+
const close = idempotentClose(async () => {
|
|
13919
|
+
try {
|
|
13920
|
+
if (signer !== void 0 && "resetSession" in signer) try {
|
|
13921
|
+
signer.resetSession();
|
|
13922
|
+
} catch (cause) {
|
|
13923
|
+
throw signerFailure(signer.source, "resetSession", cause);
|
|
13924
|
+
}
|
|
13925
|
+
} finally {
|
|
13926
|
+
await Promise.all([client._internal.close?.(), upstreamClose()]);
|
|
13927
|
+
}
|
|
13928
|
+
});
|
|
13929
|
+
return {
|
|
13930
|
+
ok: true,
|
|
13931
|
+
value: {
|
|
13932
|
+
...client,
|
|
13933
|
+
_internal: {
|
|
13934
|
+
...client._internal,
|
|
13935
|
+
close
|
|
13936
|
+
}
|
|
13937
|
+
}
|
|
13938
|
+
};
|
|
13939
|
+
} catch (cause) {
|
|
13940
|
+
await adapters.value.close().catch(() => void 0);
|
|
13941
|
+
return observeFailedResult({
|
|
13942
|
+
ok: false,
|
|
13943
|
+
error: toPublicError(cause, "createCapxulClient")
|
|
13944
|
+
}, observation, "createCapxulClient");
|
|
13945
|
+
}
|
|
13946
|
+
}
|
|
13947
|
+
function validateCreateCapxulClientInput(input) {
|
|
13948
|
+
const runtime = input.runtime ?? detectRuntime();
|
|
13949
|
+
if ((input.requirement ?? "none") === "deployed" && input.signer === void 0 && runtime !== "browser") return {
|
|
13950
|
+
ok: false,
|
|
13951
|
+
error: Errors.invalidInput("signer", "required when requirement is \"deployed\"")
|
|
13952
|
+
};
|
|
13953
|
+
return {
|
|
13954
|
+
ok: true,
|
|
13955
|
+
value: void 0
|
|
13956
|
+
};
|
|
13957
|
+
}
|
|
13958
|
+
function resolveInput(input) {
|
|
13959
|
+
try {
|
|
13960
|
+
const runtime = input.runtime ?? detectRuntime();
|
|
13961
|
+
const publishableKey = toPublishableKey(input.publishableKey);
|
|
13962
|
+
const origin = input.origin === void 0 ? runtime === "browser" ? toAllowedOrigin(derivedBrowserOrigin(runtime)) : void 0 : toAllowedOrigin(input.origin);
|
|
13963
|
+
return {
|
|
13964
|
+
ok: true,
|
|
13965
|
+
value: {
|
|
13966
|
+
publishableKey,
|
|
13967
|
+
...origin === void 0 ? {} : { origin },
|
|
13968
|
+
bootstrapBaseUrl: normalizeHttpUrl("bootstrapBaseUrl", input.bootstrapBaseUrl ?? (runtime === "browser" ? derivedBrowserOrigin(runtime) : "https://api.capxul.com")),
|
|
13969
|
+
runtime
|
|
13970
|
+
}
|
|
13971
|
+
};
|
|
13972
|
+
} catch (cause) {
|
|
13973
|
+
return {
|
|
13974
|
+
ok: false,
|
|
13975
|
+
error: toPublicError(cause, "createProductionAdapters")
|
|
13976
|
+
};
|
|
13977
|
+
}
|
|
13978
|
+
}
|
|
13979
|
+
function detectRuntime() {
|
|
13980
|
+
const globalAny = globalThis;
|
|
13981
|
+
return globalAny.window !== void 0 || globalAny.document !== void 0 ? "browser" : "node";
|
|
13982
|
+
}
|
|
13983
|
+
function derivedBrowserOrigin(runtime) {
|
|
13984
|
+
if (runtime !== "browser") throw Errors.invalidInput("origin", "required outside browser runtime");
|
|
13985
|
+
const globalAny = globalThis;
|
|
13986
|
+
if (typeof globalAny.location?.origin === "string" && globalAny.location.origin.length > 0) return globalAny.location.origin;
|
|
13987
|
+
throw Errors.invalidInput("origin", "required when browser location is unavailable");
|
|
13988
|
+
}
|
|
13989
|
+
/**
|
|
13990
|
+
* Browser local dev serves `/api/auth` via the Vite proxy on `window.location.origin`
|
|
13991
|
+
* while bootstrap returns the remote Convex site host. Openfort wallet setup reads
|
|
13992
|
+
* Better Auth cookies from `get-session` — those only attach on the same origin the
|
|
13993
|
+
* OTP flow used, so rewrite when hosts differ.
|
|
13994
|
+
*/
|
|
13995
|
+
function resolveBrowserAuthBaseUrl(input) {
|
|
13996
|
+
if (input.override !== void 0) return normalizeHttpUrl("authBaseUrl", input.override);
|
|
13997
|
+
const bootstrapUrl = normalizeHttpUrl("authBaseUrl", input.bootstrapAuthBaseUrl);
|
|
13998
|
+
if (input.runtime !== "browser") return bootstrapUrl;
|
|
13999
|
+
try {
|
|
14000
|
+
const localAuthBase = normalizeHttpUrl("authBaseUrl", `${derivedBrowserOrigin(input.runtime)}/api/auth`);
|
|
14001
|
+
const remoteAuthBase = bootstrapUrl.endsWith("/api/auth") ? bootstrapUrl : `${bootstrapUrl}/api/auth`;
|
|
14002
|
+
if (new URL(remoteAuthBase).host !== new URL(localAuthBase).host) return localAuthBase;
|
|
14003
|
+
return bootstrapUrl;
|
|
14004
|
+
} catch {
|
|
14005
|
+
return bootstrapUrl;
|
|
14006
|
+
}
|
|
14007
|
+
}
|
|
14008
|
+
function resolveRuntimeUrls(bootstrap, runtime, authBaseUrlOverride) {
|
|
14009
|
+
try {
|
|
14010
|
+
return {
|
|
14011
|
+
ok: true,
|
|
14012
|
+
value: {
|
|
14013
|
+
authBaseUrl: resolveBrowserAuthBaseUrl({
|
|
14014
|
+
bootstrapAuthBaseUrl: bootstrap.authBaseUrl,
|
|
14015
|
+
runtime,
|
|
14016
|
+
...authBaseUrlOverride === void 0 ? {} : { override: authBaseUrlOverride }
|
|
14017
|
+
}),
|
|
14018
|
+
convexUrl: normalizeHttpUrl("convexUrl", bootstrap.convexUrl)
|
|
14019
|
+
}
|
|
14020
|
+
};
|
|
14021
|
+
} catch (cause) {
|
|
14022
|
+
return {
|
|
14023
|
+
ok: false,
|
|
14024
|
+
error: toPublicError(cause, "createProductionAdapters")
|
|
14025
|
+
};
|
|
14026
|
+
}
|
|
14027
|
+
}
|
|
14028
|
+
async function runBootstrap(effect) {
|
|
14029
|
+
const result = await Effect.runPromise(Effect.result(effect));
|
|
14030
|
+
if (Result.isSuccess(result)) return {
|
|
14031
|
+
ok: true,
|
|
14032
|
+
value: result.success
|
|
14033
|
+
};
|
|
14034
|
+
return {
|
|
14035
|
+
ok: false,
|
|
14036
|
+
error: toPublicError(result.failure, "bootstrap.resolve")
|
|
14037
|
+
};
|
|
14038
|
+
}
|
|
14039
|
+
/**
|
|
14040
|
+
* Bootstrap-event props. `runtime` used to be called `capxulEnv`, which was a
|
|
14041
|
+
* name collision, not a value: it carried "browser"/"node", never an
|
|
14042
|
+
* environment. ADR-0020 A1 makes `capxul_env` the environment discriminator
|
|
14043
|
+
* every synced artifact filters on, so this field was renamed to what it
|
|
14044
|
+
* actually is. `sdk_version` follows the canon envelope spelling.
|
|
14045
|
+
*/
|
|
14046
|
+
function bootstrapTelemetryEnvelope(input, resolvedInput) {
|
|
14047
|
+
return {
|
|
14048
|
+
runtime: resolvedInput.runtime,
|
|
14049
|
+
env: input.requirement ?? "none",
|
|
14050
|
+
...resolvedInput.origin === void 0 ? {} : { origin: resolvedInput.origin },
|
|
14051
|
+
sdk_version: SDK_VERSION
|
|
14052
|
+
};
|
|
14053
|
+
}
|
|
14054
|
+
async function emitBootstrapTelemetry(telemetry, event) {
|
|
14055
|
+
if (telemetry === void 0) return;
|
|
14056
|
+
await Effect.runPromise(telemetry.emit(event).pipe(Effect.catchDefect(() => Effect.void)));
|
|
14057
|
+
}
|
|
14058
|
+
function normalizeHttpUrl(field, raw) {
|
|
14059
|
+
let parsed;
|
|
14060
|
+
try {
|
|
14061
|
+
parsed = new URL(raw);
|
|
14062
|
+
} catch {
|
|
14063
|
+
throw Errors.invalidInput(field, "must be an http or https URL");
|
|
14064
|
+
}
|
|
14065
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") throw Errors.invalidInput(field, "must be an http or https URL");
|
|
14066
|
+
return parsed.toString().replace(/\/$/, "");
|
|
14067
|
+
}
|
|
14068
|
+
function toPublicError(cause, operation) {
|
|
14069
|
+
if (cause instanceof CapxulError) return cause;
|
|
14070
|
+
if (typeof cause === "object" && cause !== null) {
|
|
14071
|
+
const publicError = cause.publicError;
|
|
14072
|
+
if (publicError instanceof CapxulError) return publicError;
|
|
14073
|
+
const nestedCause = cause.cause;
|
|
14074
|
+
if (nestedCause instanceof CapxulError) return nestedCause;
|
|
14075
|
+
}
|
|
14076
|
+
return Errors.providerError("sdk-production-adapters", operation, cause);
|
|
14077
|
+
}
|
|
14078
|
+
function idempotentClose(close) {
|
|
14079
|
+
let closed = false;
|
|
14080
|
+
return async () => {
|
|
14081
|
+
if (closed) return;
|
|
14082
|
+
closed = true;
|
|
14083
|
+
await close();
|
|
14084
|
+
};
|
|
14085
|
+
}
|
|
14086
|
+
//#endregion
|
|
14087
|
+
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 };
|