@capxul/sdk 1.0.0-alpha.17 → 1.0.0-alpha.19
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/README.md +64 -7
- package/dist/{InMemoryAuthCacheAdapter-v5W-XB5M.mjs → InMemoryAuthCacheAdapter-BuVZpnSx.mjs} +25 -4
- package/dist/InMemoryAuthCacheAdapter-BuVZpnSx.mjs.map +1 -0
- package/dist/{index-D0SflScT.d.mts → index-D1rNjmof.d.mts} +3 -3
- package/dist/index-D1rNjmof.d.mts.map +1 -0
- package/dist/index.d.mts +113 -12
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +862 -146
- package/dist/index.mjs.map +1 -1
- package/dist/node/index.d.mts +2 -2
- package/dist/node/index.mjs +1 -1
- package/dist/ports/safe-deployment.d.mts +1 -1
- package/dist/ports/safe-deployment.mjs.map +1 -1
- package/dist/{safe-deployment-ToMPzqwk.d.mts → safe-deployment-BID2pXZN.d.mts} +4 -5
- package/dist/safe-deployment-BID2pXZN.d.mts.map +1 -0
- package/dist/{signer-Cp2G98ZK.d.mts → signer-AXnBJuAN.d.mts} +2 -2
- package/dist/{signer-Cp2G98ZK.d.mts.map → signer-AXnBJuAN.d.mts.map} +1 -1
- package/package.json +5 -4
- package/dist/InMemoryAuthCacheAdapter-v5W-XB5M.mjs.map +0 -1
- package/dist/index-D0SflScT.d.mts.map +0 -1
- package/dist/safe-deployment-ToMPzqwk.d.mts.map +0 -1
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { A as toSessionToken, C as toEpochSeconds, D as toPublishableKey, E as toOrgId, F as isCapxulError, M as decodeConvexError, N as CapxulError, O as toPublishableKeyId, P as
|
|
2
|
-
import { formatUnits, keccak256, padHex, parseUnits, recoverAddress, stringToHex, toBytes } from "viem";
|
|
1
|
+
import { A as toSessionToken, C as toEpochSeconds, D as toPublishableKey, E as toOrgId, F as Errors, I as isCapxulError, M as decodeConvexError, N as CapxulError, O as toPublishableKeyId, P as EXPECTED_OPERATION_OUTCOMES, S as toEpochMs, T as toKycTier, _ as toChainId, b as toDurationMs, c as BYTES32_RE, d as toAccountId, f as toAddress, g as toAuthUserId, h as toAppId, j as toSubAccountId, k as toRoleKey, l as EVM_ADDRESS_RE$1, m as toAnonymousDistinctId, n as BrowserAuthCacheAdapter, o as AuthCachePortTag, p as toAllowedOrigin, s as APP_ID_RE, t as InMemoryAuthCacheAdapter, u as SUPPORTED_CURRENCY_CODES, v as toCountryCode, w as toJwtToken, x as toEmail, y as toCurrencyCode } from "./InMemoryAuthCacheAdapter-BuVZpnSx.mjs";
|
|
2
|
+
import { concat, formatUnits, keccak256, padHex, parseUnits, recoverAddress, stringToHex, toBytes, toEventSelector, toFunctionSelector } from "viem";
|
|
3
3
|
import { Context, Data, Deferred, Duration, Effect, Either, Exit, Layer, Ref, Request, Scope } from "effect";
|
|
4
4
|
import { getFunctionName, makeFunctionReference } from "convex/server";
|
|
5
5
|
import { privateKeyToAccount } from "viem/accounts";
|
|
@@ -12,6 +12,10 @@ import { Machine } from "@effect/experimental";
|
|
|
12
12
|
const USDX_ADDRESS_BASE_SEPOLIA = "0xf09fcbb6df9f8f918e58f9c8ab15a76ade862b77";
|
|
13
13
|
USDX_ADDRESS_BASE_SEPOLIA.toLowerCase();
|
|
14
14
|
//#endregion
|
|
15
|
+
//#region ../config/src/org-payments.ts
|
|
16
|
+
/** Zodiac MultiSendCallOnly module on Base Sepolia. */
|
|
17
|
+
const MULTI_SEND_CALL_ONLY = "0x9641d764fc13c8b624c04430c7356c1c7c8102e2";
|
|
18
|
+
//#endregion
|
|
15
19
|
//#region ../config/src/safe.ts
|
|
16
20
|
const BASE_SEPOLIA_CHAIN_ID = 84532;
|
|
17
21
|
/**
|
|
@@ -175,6 +179,20 @@ padHex(stringToHex("FM_DAILY"), {
|
|
|
175
179
|
size: 32,
|
|
176
180
|
dir: "right"
|
|
177
181
|
});
|
|
182
|
+
padHex(concat([MULTI_SEND_CALL_ONLY, "0x8d80ff0a"]), {
|
|
183
|
+
dir: "right",
|
|
184
|
+
size: 32
|
|
185
|
+
});
|
|
186
|
+
new Map([
|
|
187
|
+
["assignRoles(address,bytes32[],bool[])", "AssignRoles(address,bytes32[],bool[])"],
|
|
188
|
+
["allowTarget(bytes32,address,uint8)", "AllowTarget(bytes32,address,uint8)"],
|
|
189
|
+
["scopeTarget(bytes32,address)", "ScopeTarget(bytes32,address)"],
|
|
190
|
+
["revokeTarget(bytes32,address)", "RevokeTarget(bytes32,address)"],
|
|
191
|
+
["allowFunction(bytes32,address,bytes4,uint8)", "AllowFunction(bytes32,address,bytes4,uint8)"],
|
|
192
|
+
["scopeFunction(bytes32,address,bytes4,(uint8,uint8,uint8,bytes)[],uint8)", "ScopeFunction(bytes32,address,bytes4,(uint8,uint8,uint8,bytes)[],uint8)"],
|
|
193
|
+
["revokeFunction(bytes32,address,bytes4)", "RevokeFunction(bytes32,address,bytes4)"],
|
|
194
|
+
["setAllowance(bytes32,uint128,uint128,uint128,uint64,uint64)", "SetAllowance(bytes32,uint128,uint128,uint128,uint64,uint64)"]
|
|
195
|
+
].map(([functionSignature, eventSignature]) => [toFunctionSelector(functionSignature), toEventSelector(eventSignature)]));
|
|
178
196
|
//#endregion
|
|
179
197
|
//#region src/telemetry/stack-frame-parser.ts
|
|
180
198
|
/**
|
|
@@ -1240,10 +1258,10 @@ function makeAccountMethods(deps) {
|
|
|
1240
1258
|
value: await lane.retryProvisioning()
|
|
1241
1259
|
};
|
|
1242
1260
|
};
|
|
1243
|
-
const reportAndReturn = (error) => {
|
|
1261
|
+
const reportAndReturn = (error, operation = "getLifecycle") => {
|
|
1244
1262
|
if (deps.telemetry) captureExceptionSync(deps.telemetry, error, {
|
|
1245
1263
|
layer: "account",
|
|
1246
|
-
operation
|
|
1264
|
+
operation
|
|
1247
1265
|
});
|
|
1248
1266
|
return {
|
|
1249
1267
|
ok: false,
|
|
@@ -1274,6 +1292,12 @@ function makeAccountMethods(deps) {
|
|
|
1274
1292
|
};
|
|
1275
1293
|
const getLifecycle = () => resolveLifecycle();
|
|
1276
1294
|
const retrySetup = async () => {
|
|
1295
|
+
const resetSession = deps.signer?.resetSession;
|
|
1296
|
+
if (typeof resetSession === "function") try {
|
|
1297
|
+
resetSession.call(deps.signer);
|
|
1298
|
+
} catch (cause) {
|
|
1299
|
+
return reportAndReturn(cause instanceof CapxulError ? cause : Errors.providerError("openfort", "resetSession", cause, { failure_mode: "unknown" }), "retrySetup");
|
|
1300
|
+
}
|
|
1277
1301
|
await lane.retryProvisioning();
|
|
1278
1302
|
return resolveLifecycle();
|
|
1279
1303
|
};
|
|
@@ -1600,7 +1624,7 @@ async function recoverRawDigestSigner(input) {
|
|
|
1600
1624
|
}
|
|
1601
1625
|
//#endregion
|
|
1602
1626
|
//#region package.json
|
|
1603
|
-
var version = "1.0.0-alpha.
|
|
1627
|
+
var version = "1.0.0-alpha.19";
|
|
1604
1628
|
//#endregion
|
|
1605
1629
|
//#region src/ports/auth-client.ts
|
|
1606
1630
|
var AuthClientError = class extends Data.TaggedError("AuthClientError") {};
|
|
@@ -1646,6 +1670,22 @@ function resolveAuthClientUrl(authBaseUrl, path) {
|
|
|
1646
1670
|
return `${base}${path}`;
|
|
1647
1671
|
}
|
|
1648
1672
|
//#endregion
|
|
1673
|
+
//#region ../wire/src/secret-material.ts
|
|
1674
|
+
const SENSITIVE_MATERIAL_PATTERNS = [
|
|
1675
|
+
/0x[a-fA-F0-9]{40,}/u,
|
|
1676
|
+
/(?:^|[^a-fA-F0-9])[a-fA-F0-9]{64}(?:$|[^a-fA-F0-9])/u,
|
|
1677
|
+
/eyJ[A-Za-z0-9_-]*\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+/u,
|
|
1678
|
+
/(?:(?:sk|pk|rk)_(?:live|test)|(?:phc|phx|ghp|gho|ghu|ghs|ghr|whsec)_|github_pat_|xox[baprs]-|AKIA[0-9A-Z]{16}|AIza)[A-Za-z0-9_-]*/iu
|
|
1679
|
+
];
|
|
1680
|
+
/**
|
|
1681
|
+
* Reject: does the value carry any known secret material? Best effort — callers
|
|
1682
|
+
* drop the whole value on a match; a false negative is a leak, a false positive
|
|
1683
|
+
* merely omits an observation field.
|
|
1684
|
+
*/
|
|
1685
|
+
function containsSensitiveMaterial(value) {
|
|
1686
|
+
return SENSITIVE_MATERIAL_PATTERNS.some((pattern) => pattern.test(value));
|
|
1687
|
+
}
|
|
1688
|
+
//#endregion
|
|
1649
1689
|
//#region ../wire/src/observation-context.ts
|
|
1650
1690
|
/** Single bounded HTTP carrier used before a Convex action envelope exists. */
|
|
1651
1691
|
const OBSERVATION_CONTEXT_HEADER = "x-capxul-observation-context";
|
|
@@ -1679,10 +1719,6 @@ const FIELD_RULES = {
|
|
|
1679
1719
|
pattern: /^anon_[A-Za-z0-9-]+$/u
|
|
1680
1720
|
}
|
|
1681
1721
|
};
|
|
1682
|
-
const EMBEDDED_WALLET_MATERIAL = /0x[a-fA-F0-9]{40,}/u;
|
|
1683
|
-
const RAW_PRIVATE_KEY_MATERIAL = /(?:^|[^a-fA-F0-9])[a-fA-F0-9]{64}(?:$|[^a-fA-F0-9])/u;
|
|
1684
|
-
const COMPACT_JWT = /eyJ[A-Za-z0-9_-]*\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+/u;
|
|
1685
|
-
const KNOWN_CREDENTIAL_PREFIX = /(?:(?:sk|pk|rk)_(?:live|test)|(?:phc|phx|ghp|gho|ghu|ghs|ghr|whsec)_|github_pat_|xox[baprs]-|AKIA[0-9A-Z]{16}|AIza)[A-Za-z0-9_-]*/iu;
|
|
1686
1722
|
/**
|
|
1687
1723
|
* Copy only the canonical allowlist and silently omit malformed/sensitive
|
|
1688
1724
|
* values. Observation metadata is best effort and may never reject a domain
|
|
@@ -1709,9 +1745,6 @@ function isSafeField(field, value) {
|
|
|
1709
1745
|
const rule = FIELD_RULES[field];
|
|
1710
1746
|
return value.length > 0 && value.length <= rule.maxLength && value === value.trim() && !value.includes("://") && !containsSensitiveMaterial(value) && rule.pattern.test(value);
|
|
1711
1747
|
}
|
|
1712
|
-
function containsSensitiveMaterial(value) {
|
|
1713
|
-
return EMBEDDED_WALLET_MATERIAL.test(value) || RAW_PRIVATE_KEY_MATERIAL.test(value) || COMPACT_JWT.test(value) || KNOWN_CREDENTIAL_PREFIX.test(value);
|
|
1714
|
-
}
|
|
1715
1748
|
//#endregion
|
|
1716
1749
|
//#region src/internal/observation-http.ts
|
|
1717
1750
|
/** Resolve one bounded pre-auth snapshot for an outbound SDK HTTP request. */
|
|
@@ -2733,7 +2766,7 @@ const PaymentSource = Schema.Union(Schema.Struct({
|
|
|
2733
2766
|
* `hashLineItems` (payment-document-hash.ts) into the Invoice `lineItemsHash`,
|
|
2734
2767
|
* and Σ(quantity × unitMinor) MUST equal the invoice `amount`. The hash binds
|
|
2735
2768
|
* the documentHash to the exact items; the sum-check makes the items add up to
|
|
2736
|
-
* the amount due. See the
|
|
2769
|
+
* the amount due. See the financial-operations contract in `packages/wire/CONTEXT.md`.
|
|
2737
2770
|
*/
|
|
2738
2771
|
const LineItem = Schema.Struct({
|
|
2739
2772
|
description: Schema.String,
|
|
@@ -3359,24 +3392,6 @@ const envFields = {
|
|
|
3359
3392
|
"required": false,
|
|
3360
3393
|
"example": "development"
|
|
3361
3394
|
},
|
|
3362
|
-
AUTH_DEV_OTP: {
|
|
3363
|
-
"runtime": "server",
|
|
3364
|
-
"kind": "nonempty",
|
|
3365
|
-
"required": false,
|
|
3366
|
-
"example": "000000"
|
|
3367
|
-
},
|
|
3368
|
-
SENTRY_DSN: {
|
|
3369
|
-
"runtime": "server",
|
|
3370
|
-
"kind": "url",
|
|
3371
|
-
"required": false,
|
|
3372
|
-
"example": "https://public@example.ingest.sentry.io/1"
|
|
3373
|
-
},
|
|
3374
|
-
POSTHOG_API_KEY: {
|
|
3375
|
-
"runtime": "server",
|
|
3376
|
-
"kind": "nonempty",
|
|
3377
|
-
"required": false,
|
|
3378
|
-
"example": "phx_test"
|
|
3379
|
-
},
|
|
3380
3395
|
POSTHOG_PROJECT_TOKEN: {
|
|
3381
3396
|
"runtime": "server",
|
|
3382
3397
|
"kind": "nonempty",
|
|
@@ -3407,24 +3422,12 @@ const envFields = {
|
|
|
3407
3422
|
"required": false,
|
|
3408
3423
|
"example": "12345"
|
|
3409
3424
|
},
|
|
3410
|
-
POSTHOG_ENV_ID: {
|
|
3411
|
-
"runtime": "server",
|
|
3412
|
-
"kind": "nonempty",
|
|
3413
|
-
"required": false,
|
|
3414
|
-
"example": "12345"
|
|
3415
|
-
},
|
|
3416
3425
|
SENDER_EMAIL: {
|
|
3417
3426
|
"runtime": "server",
|
|
3418
3427
|
"kind": "email",
|
|
3419
3428
|
"required": false,
|
|
3420
3429
|
"example": "noreply@capxul.test"
|
|
3421
3430
|
},
|
|
3422
|
-
SHIELD_API_URL: {
|
|
3423
|
-
"runtime": "server",
|
|
3424
|
-
"kind": "url",
|
|
3425
|
-
"required": false,
|
|
3426
|
-
"example": "https://api.shield.test"
|
|
3427
|
-
},
|
|
3428
3431
|
ALCHEMY_API_KEY: {
|
|
3429
3432
|
"runtime": "server",
|
|
3430
3433
|
"kind": "nonempty",
|
|
@@ -3443,24 +3446,6 @@ const envFields = {
|
|
|
3443
3446
|
"required": true,
|
|
3444
3447
|
"example": "alchemy_hmac"
|
|
3445
3448
|
},
|
|
3446
|
-
ALCHEMY_AUTH_TOKEN: {
|
|
3447
|
-
"runtime": "server",
|
|
3448
|
-
"kind": "nonempty",
|
|
3449
|
-
"required": false,
|
|
3450
|
-
"example": "alchemy_auth"
|
|
3451
|
-
},
|
|
3452
|
-
ALCHEMY_WEBHOOK_ID: {
|
|
3453
|
-
"runtime": "server",
|
|
3454
|
-
"kind": "nonempty",
|
|
3455
|
-
"required": false,
|
|
3456
|
-
"example": "wh_123"
|
|
3457
|
-
},
|
|
3458
|
-
GOLDSKY_WEBHOOK_SECRET: {
|
|
3459
|
-
"runtime": "server",
|
|
3460
|
-
"kind": "nonempty",
|
|
3461
|
-
"required": false,
|
|
3462
|
-
"example": "goldsky_secret"
|
|
3463
|
-
},
|
|
3464
3449
|
DEPLOYER_PRIVATE_KEY: {
|
|
3465
3450
|
"runtime": "server",
|
|
3466
3451
|
"kind": "privateKey",
|
|
@@ -3717,7 +3702,7 @@ function fromWei(rawBalance, decimals, currency) {
|
|
|
3717
3702
|
}
|
|
3718
3703
|
//#endregion
|
|
3719
3704
|
//#region src/adapters/account-read/ConvexAccountAdapter.ts
|
|
3720
|
-
const DEFAULT_FUNCTIONS$
|
|
3705
|
+
const DEFAULT_FUNCTIONS$5 = {
|
|
3721
3706
|
readBalance: makeFunctionReference("account/actions:readBalance"),
|
|
3722
3707
|
faucetMint: makeFunctionReference("account/actions:faucetMint")
|
|
3723
3708
|
};
|
|
@@ -3726,7 +3711,7 @@ var ConvexAccountAdapter = class {
|
|
|
3726
3711
|
#fns;
|
|
3727
3712
|
constructor(deps) {
|
|
3728
3713
|
this.#convex = deps.convex;
|
|
3729
|
-
this.#fns = deps.functions ?? DEFAULT_FUNCTIONS$
|
|
3714
|
+
this.#fns = deps.functions ?? DEFAULT_FUNCTIONS$5;
|
|
3730
3715
|
}
|
|
3731
3716
|
readBalance(input) {
|
|
3732
3717
|
return this.#convex.action(this.#fns.readBalance, { chainId: wireChainId(input.chainId) }).pipe(Effect.mapError((error) => accountReadErrorFromCapxul("readBalance", error.publicError, error)), Effect.flatMap((wire) => brandAccountEffect("readBalance", wire)), Effect.catchAllDefect((cause) => Effect.fail(accountReadErrorFromUnknown("readBalance", cause))));
|
|
@@ -3779,7 +3764,7 @@ function subAccountErrorFromCapxul(operation, error, cause = error) {
|
|
|
3779
3764
|
var SubAccountPortTag = class extends Context.Tag("@capxul/sdk/ports/SubAccountPort")() {};
|
|
3780
3765
|
//#endregion
|
|
3781
3766
|
//#region src/adapters/sub-account/ConvexSubAccountAdapter.ts
|
|
3782
|
-
const DEFAULT_FUNCTIONS$
|
|
3767
|
+
const DEFAULT_FUNCTIONS$4 = {
|
|
3783
3768
|
create: makeFunctionReference("subAccount/mutations:create"),
|
|
3784
3769
|
get: makeFunctionReference("subAccount/queries:get"),
|
|
3785
3770
|
list: makeFunctionReference("subAccount/queries:list"),
|
|
@@ -3794,7 +3779,7 @@ var ConvexSubAccountAdapter = class {
|
|
|
3794
3779
|
constructor(deps) {
|
|
3795
3780
|
this.#convex = deps.convex;
|
|
3796
3781
|
this.#chainId = deps.chainId;
|
|
3797
|
-
this.#fns = deps.functions ?? DEFAULT_FUNCTIONS$
|
|
3782
|
+
this.#fns = deps.functions ?? DEFAULT_FUNCTIONS$4;
|
|
3798
3783
|
}
|
|
3799
3784
|
create(input) {
|
|
3800
3785
|
return this.#runMutation("create", this.#fns.create, {
|
|
@@ -3878,7 +3863,7 @@ function smartAccountErrorFromCapxul(operation, error, cause = error) {
|
|
|
3878
3863
|
var SmartAccountPortTag = class extends Context.Tag("@capxul/sdk/ports/SmartAccountPort")() {};
|
|
3879
3864
|
//#endregion
|
|
3880
3865
|
//#region src/adapters/smart-account/ConvexSmartAccountAdapter.ts
|
|
3881
|
-
const DEFAULT_FUNCTIONS$
|
|
3866
|
+
const DEFAULT_FUNCTIONS$3 = {
|
|
3882
3867
|
loadByAuthUserId: makeFunctionReference("smartAccount/queries:loadByAuthUserId"),
|
|
3883
3868
|
loadBySmartAccountAddress: makeFunctionReference("smartAccount/queries:loadBySmartAccountAddress"),
|
|
3884
3869
|
provision: makeFunctionReference("smartAccount/mutations:provision"),
|
|
@@ -3890,7 +3875,7 @@ var ConvexSmartAccountAdapter = class {
|
|
|
3890
3875
|
#fns;
|
|
3891
3876
|
constructor(deps) {
|
|
3892
3877
|
this.#convex = deps.convex;
|
|
3893
|
-
this.#fns = deps.functions ?? DEFAULT_FUNCTIONS$
|
|
3878
|
+
this.#fns = deps.functions ?? DEFAULT_FUNCTIONS$3;
|
|
3894
3879
|
}
|
|
3895
3880
|
loadByAuthUserId(authUserId) {
|
|
3896
3881
|
return this.#convex.query(this.#fns.loadByAuthUserId, { authUserId }).pipe(Effect.mapError((error) => smartAccountErrorFromCapxul("loadByAuthUserId", error.publicError, error)), Effect.flatMap((row) => brandSmartAccountEffect("loadByAuthUserId", row)), Effect.catchAllDefect((cause) => Effect.fail(smartAccountErrorFromUnknown("loadByAuthUserId", cause))));
|
|
@@ -3965,6 +3950,456 @@ function smartAccountErrorFromUnknown(operation, cause) {
|
|
|
3965
3950
|
toAddress("0xa6b71e26c5e0845f74c812102ca7114b6a896ab2");
|
|
3966
3951
|
keccak256("0x");
|
|
3967
3952
|
//#endregion
|
|
3953
|
+
//#region src/ports/org.ts
|
|
3954
|
+
var OrgError = class extends Data.TaggedError("OrgError") {};
|
|
3955
|
+
function orgErrorFromCapxul(operation, error, cause = error) {
|
|
3956
|
+
return new OrgError({
|
|
3957
|
+
operation,
|
|
3958
|
+
publicCode: error.code,
|
|
3959
|
+
publicError: error,
|
|
3960
|
+
cause,
|
|
3961
|
+
...error.details === void 0 ? {} : { details: error.details }
|
|
3962
|
+
});
|
|
3963
|
+
}
|
|
3964
|
+
Context.Tag("@capxul/sdk/ports/OrgPort")();
|
|
3965
|
+
Context.Tag("@capxul/sdk/ports/OrgRolesDeploymentPort")();
|
|
3966
|
+
Context.Tag("@capxul/sdk/ports/OrgSpendPort")();
|
|
3967
|
+
//#endregion
|
|
3968
|
+
//#region src/adapters/org/parse.ts
|
|
3969
|
+
/**
|
|
3970
|
+
* Map a `WireOrg` + its (separately read) treasury `Account` into the branded
|
|
3971
|
+
* `OrgView`. The viewer role is projected by the authenticated backend read;
|
|
3972
|
+
* it must never be inferred from Organization ownership. Brands at the read
|
|
3973
|
+
* edge: `orgId`, `safeAddress`.
|
|
3974
|
+
*/
|
|
3975
|
+
function brandOrgView(wire, treasury, viewerRole) {
|
|
3976
|
+
return {
|
|
3977
|
+
id: toOrgId(wire.orgId),
|
|
3978
|
+
name: wire.name,
|
|
3979
|
+
handle: wire.slug,
|
|
3980
|
+
safeAddress: toAddress(wire.safeAddress.toLowerCase()),
|
|
3981
|
+
role: viewerRole,
|
|
3982
|
+
treasury
|
|
3983
|
+
};
|
|
3984
|
+
}
|
|
3985
|
+
/**
|
|
3986
|
+
* Build the Org treasury `Account` from the raw on-chain `balanceOf` integer
|
|
3987
|
+
* (the D3 RPC read). `available === balance` for a treasury with no envelope
|
|
3988
|
+
* partition yet (a fresh Org reads back $0).
|
|
3989
|
+
*/
|
|
3990
|
+
function brandOrgTreasury(input) {
|
|
3991
|
+
return {
|
|
3992
|
+
id: toAccountId(`account_${orgIdBody(input.orgId)}`),
|
|
3993
|
+
balance: input.money,
|
|
3994
|
+
available: input.money
|
|
3995
|
+
};
|
|
3996
|
+
}
|
|
3997
|
+
function brandOrgRole(wire) {
|
|
3998
|
+
return {
|
|
3999
|
+
orgId: toOrgId(wire.orgId),
|
|
4000
|
+
label: wire.label,
|
|
4001
|
+
roleKey: toRoleKey(wire.roleKey),
|
|
4002
|
+
definition: parseRoleDefinition(wire.definitionJson)
|
|
4003
|
+
};
|
|
4004
|
+
}
|
|
4005
|
+
function brandOrgMember(wire) {
|
|
4006
|
+
return {
|
|
4007
|
+
orgId: toOrgId(wire.orgId),
|
|
4008
|
+
email: toEmail(wire.email),
|
|
4009
|
+
name: wire.name,
|
|
4010
|
+
personalSafeAddress: wire.personalSafeAddress === null ? null : toAddress(wire.personalSafeAddress),
|
|
4011
|
+
role: wire.role,
|
|
4012
|
+
roleKey: wire.roleKey === null ? null : toRoleKey(wire.roleKey),
|
|
4013
|
+
status: wire.status,
|
|
4014
|
+
grantTxHash: wire.grantTxHash,
|
|
4015
|
+
revokeTxHash: wire.revokeTxHash
|
|
4016
|
+
};
|
|
4017
|
+
}
|
|
4018
|
+
function parseRoleDefinition(json) {
|
|
4019
|
+
let raw;
|
|
4020
|
+
try {
|
|
4021
|
+
const parsed = JSON.parse(json);
|
|
4022
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) throw new Error("expected object");
|
|
4023
|
+
raw = parsed;
|
|
4024
|
+
} catch (err) {
|
|
4025
|
+
throw new Error(`Invalid role definition: malformed JSON - ${err instanceof Error ? err.message : String(err)}`, { cause: err });
|
|
4026
|
+
}
|
|
4027
|
+
if (typeof raw.label !== "string" || raw.label.trim().length === 0) throw new Error("Invalid role definition: missing label");
|
|
4028
|
+
return {
|
|
4029
|
+
label: raw.label,
|
|
4030
|
+
...raw.spend === void 0 ? {} : { spend: {
|
|
4031
|
+
...raw.spend.perTx === void 0 ? {} : { perTx: parseRoleMoney(raw.spend.perTx, "perTx") },
|
|
4032
|
+
...raw.spend.perDay === void 0 ? {} : { perDay: parseRoleMoney(raw.spend.perDay, "perDay") },
|
|
4033
|
+
...raw.spend.toRecipients === void 0 ? {} : { toRecipients: parseRoleRecipients(raw.spend.toRecipients) }
|
|
4034
|
+
} },
|
|
4035
|
+
...raw.subAccounts === void 0 ? {} : { subAccounts: parseRoleSubAccounts(raw.subAccounts) },
|
|
4036
|
+
...typeof raw.canManageMembers === "boolean" ? { canManageMembers: raw.canManageMembers } : {},
|
|
4037
|
+
...typeof raw.canManageRoles === "boolean" ? { canManageRoles: raw.canManageRoles } : {}
|
|
4038
|
+
};
|
|
4039
|
+
}
|
|
4040
|
+
function parseRoleMoney(raw, field) {
|
|
4041
|
+
if (typeof raw.currency !== "string" || typeof raw.value !== "string" || !/^\d+$/.test(raw.value) || raw.decimals !== 6) throw new Error(`Invalid role money: ${field}`);
|
|
4042
|
+
return {
|
|
4043
|
+
currency: toCurrencyCode(raw.currency),
|
|
4044
|
+
value: raw.value,
|
|
4045
|
+
decimals: raw.decimals
|
|
4046
|
+
};
|
|
4047
|
+
}
|
|
4048
|
+
function parseRoleRecipients(raw) {
|
|
4049
|
+
if (raw === "anyone") return "anyone";
|
|
4050
|
+
if (!Array.isArray(raw)) throw new Error("Invalid role definition: toRecipients must be \"anyone\" or an array");
|
|
4051
|
+
return raw.map((recipient) => {
|
|
4052
|
+
if (typeof recipient !== "string") throw new Error("Invalid role definition: toRecipients must be \"anyone\" or an array");
|
|
4053
|
+
return toAddress(recipient);
|
|
4054
|
+
});
|
|
4055
|
+
}
|
|
4056
|
+
function parseRoleSubAccounts(raw) {
|
|
4057
|
+
if (raw.scope === "all") return { scope: "all" };
|
|
4058
|
+
if (!Array.isArray(raw.scope)) throw new Error("Invalid role definition: subAccounts.scope must be \"all\" or an array");
|
|
4059
|
+
return { scope: raw.scope.map((subAccountId) => {
|
|
4060
|
+
if (typeof subAccountId !== "string") throw new Error("Invalid role definition: subAccounts.scope must be \"all\" or an array");
|
|
4061
|
+
return toSubAccountId(subAccountId);
|
|
4062
|
+
}) };
|
|
4063
|
+
}
|
|
4064
|
+
/** Strip the `org_` prefix + non-alphanumerics so the body re-seeds `account_`. */
|
|
4065
|
+
function orgIdBody(orgId) {
|
|
4066
|
+
const underscore = orgId.indexOf("_");
|
|
4067
|
+
const cleaned = (underscore < 0 ? orgId : orgId.slice(underscore + 1)).replace(/[^0-9A-Za-z]/g, "");
|
|
4068
|
+
return cleaned.length > 0 ? cleaned : "0";
|
|
4069
|
+
}
|
|
4070
|
+
//#endregion
|
|
4071
|
+
//#region src/adapters/org/ConvexOrganizationAdapter.ts
|
|
4072
|
+
const DEFAULT_FUNCTIONS$2 = {
|
|
4073
|
+
listAll: makeFunctionReference("org/queries:listAll"),
|
|
4074
|
+
listRoles: makeFunctionReference("org/queries:listRolesByOrgId"),
|
|
4075
|
+
listMembers: makeFunctionReference("org/queries:listMembersByOrgId"),
|
|
4076
|
+
readTreasury: makeFunctionReference("org/actions:readTreasury"),
|
|
4077
|
+
inviteMember: makeFunctionReference("org/actions:inviteMember"),
|
|
4078
|
+
resendInvite: makeFunctionReference("org/mutations:resendInviteToken"),
|
|
4079
|
+
detectInvitations: makeFunctionReference("org/actions:detectAndAcceptPendingInvitations")
|
|
4080
|
+
};
|
|
4081
|
+
/**
|
|
4082
|
+
* Standard production Organization read adapter. It intentionally has no
|
|
4083
|
+
* deployer/RPC/test configuration: authenticated Convex actions own live chain
|
|
4084
|
+
* reads, while the lifecycle adapter owns the single sponsored bootstrap.
|
|
4085
|
+
*/
|
|
4086
|
+
var ConvexOrganizationAdapter = class {
|
|
4087
|
+
#convex;
|
|
4088
|
+
#fns;
|
|
4089
|
+
constructor(input) {
|
|
4090
|
+
this.#convex = input.convex;
|
|
4091
|
+
this.#fns = input.functions ?? DEFAULT_FUNCTIONS$2;
|
|
4092
|
+
}
|
|
4093
|
+
createOrg(_input) {
|
|
4094
|
+
return Effect.fail(orgErrorFromCapxul("createOrg", Errors.notImplemented("organizationSetup", "use onboarding.completeOrganization")));
|
|
4095
|
+
}
|
|
4096
|
+
listOrgs(_input) {
|
|
4097
|
+
return this.#convex.query(this.#fns.listAll, {}).pipe(Effect.mapError((error) => orgErrorFromCapxul("listOrgs", error.publicError, error)), Effect.flatMap((wires) => Effect.forEach(wires, (wire) => this.#readTreasuryWire(wire.orgId).pipe(Effect.map((treasury) => {
|
|
4098
|
+
const viewerRole = wire.viewerRole.trim();
|
|
4099
|
+
if (viewerRole.length === 0) throw Errors.wrongState({
|
|
4100
|
+
method: "listOrgs",
|
|
4101
|
+
currentState: "viewerRoleMissing",
|
|
4102
|
+
validStates: ["activeViewerRole"]
|
|
4103
|
+
});
|
|
4104
|
+
return brandOrgView(wire, treasury, viewerRole);
|
|
4105
|
+
})))), Effect.catchAllDefect((cause) => Effect.fail(toOrgError("listOrgs", cause))));
|
|
4106
|
+
}
|
|
4107
|
+
readTreasury(input) {
|
|
4108
|
+
return this.#readTreasuryWire(String(input.orgId)).pipe(Effect.catchAllDefect((cause) => Effect.fail(toOrgError("readTreasury", cause))));
|
|
4109
|
+
}
|
|
4110
|
+
listRoles(input) {
|
|
4111
|
+
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.catchAllDefect((cause) => Effect.fail(toOrgError("listRoles", cause))));
|
|
4112
|
+
}
|
|
4113
|
+
listMembers(input) {
|
|
4114
|
+
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.catchAllDefect((cause) => Effect.fail(toOrgError("listMembers", cause))));
|
|
4115
|
+
}
|
|
4116
|
+
pendingMembers(input) {
|
|
4117
|
+
return this.listMembers(input).pipe(Effect.map((members) => members.filter((member) => member.status === "pending" || member.status === "pending_safe" || member.status === "pending_grant")));
|
|
4118
|
+
}
|
|
4119
|
+
inviteMember(input) {
|
|
4120
|
+
return this.#convex.action(this.#fns.inviteMember, {
|
|
4121
|
+
orgId: input.orgId,
|
|
4122
|
+
email: input.input.email,
|
|
4123
|
+
role: input.input.role
|
|
4124
|
+
}).pipe(Effect.mapError((error) => orgErrorFromCapxul("inviteMember", error.publicError, error)), Effect.map(brandOrgMember), Effect.catchAllDefect((cause) => Effect.fail(toOrgError("inviteMember", cause))));
|
|
4125
|
+
}
|
|
4126
|
+
resendInviteToken(input) {
|
|
4127
|
+
return this.#convex.mutation(this.#fns.resendInvite, {
|
|
4128
|
+
orgId: input.orgId,
|
|
4129
|
+
email: input.email
|
|
4130
|
+
}).pipe(Effect.mapError((error) => orgErrorFromCapxul("resendInviteToken", error.publicError, error)), Effect.map(brandOrgMember), Effect.catchAllDefect((cause) => Effect.fail(toOrgError("resendInviteToken", cause))));
|
|
4131
|
+
}
|
|
4132
|
+
detectAndAcceptPendingInvitations(_input) {
|
|
4133
|
+
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.catchAllDefect((cause) => Effect.fail(toOrgError("detectAndAcceptPendingInvitations", cause))));
|
|
4134
|
+
}
|
|
4135
|
+
#readTreasuryWire(orgId) {
|
|
4136
|
+
return this.#convex.action(this.#fns.readTreasury, { orgId }).pipe(Effect.mapError((error) => orgErrorFromCapxul("readTreasury", error.publicError, error)), Effect.map((wire) => {
|
|
4137
|
+
if (wire.orgId !== orgId) throw Errors.invalidInput("orgId", "Organization treasury scope does not match");
|
|
4138
|
+
const balance = fromWei(wire.rawBalance, wire.decimals, wire.currency);
|
|
4139
|
+
const available = fromWei(wire.rawAvailableBalance, wire.decimals, wire.currency);
|
|
4140
|
+
return {
|
|
4141
|
+
...brandOrgTreasury({
|
|
4142
|
+
orgId: wire.orgId,
|
|
4143
|
+
money: balance
|
|
4144
|
+
}),
|
|
4145
|
+
available
|
|
4146
|
+
};
|
|
4147
|
+
}));
|
|
4148
|
+
}
|
|
4149
|
+
};
|
|
4150
|
+
function toOrgError(operation, cause) {
|
|
4151
|
+
if (cause instanceof CapxulError) return orgErrorFromCapxul(operation, cause);
|
|
4152
|
+
return orgErrorFromCapxul(operation, Errors.providerError("convex", operation, cause), cause);
|
|
4153
|
+
}
|
|
4154
|
+
function partialOrgTruth(operation, currentState) {
|
|
4155
|
+
return orgErrorFromCapxul(operation, Errors.wrongState({
|
|
4156
|
+
method: operation,
|
|
4157
|
+
currentState,
|
|
4158
|
+
validStates: ["completeProductionOrganizationTruth"]
|
|
4159
|
+
}));
|
|
4160
|
+
}
|
|
4161
|
+
//#endregion
|
|
4162
|
+
//#region src/adapters/org/ConvexOrganizationSetupAdapter.ts
|
|
4163
|
+
const DEFAULT_FUNCTIONS$1 = {
|
|
4164
|
+
startOrResume: makeFunctionReference("org/lifecycle:startOrResume"),
|
|
4165
|
+
prepareFounderAccount: makeFunctionReference("org/actions:prepareFounderAccount"),
|
|
4166
|
+
prepareBootstrap: makeFunctionReference("org/actions:prepareBootstrap"),
|
|
4167
|
+
submitBootstrap: makeFunctionReference("org/actions:submitBootstrap"),
|
|
4168
|
+
resumeBootstrapSubmission: makeFunctionReference("org/actions:resumeBootstrapSubmission"),
|
|
4169
|
+
confirmBootstrap: makeFunctionReference("org/actions:confirmBootstrap"),
|
|
4170
|
+
recordFailure: makeFunctionReference("org/lifecycle:recordFailure"),
|
|
4171
|
+
load: makeFunctionReference("org/lifecycle:load"),
|
|
4172
|
+
retry: makeFunctionReference("org/lifecycle:retry")
|
|
4173
|
+
};
|
|
4174
|
+
/** Durable Organization setup capability composed by the standard client. */
|
|
4175
|
+
var ConvexOrganizationSetupAdapter = class {
|
|
4176
|
+
#convex;
|
|
4177
|
+
#signer;
|
|
4178
|
+
#fns;
|
|
4179
|
+
constructor(input) {
|
|
4180
|
+
this.#convex = input.convex;
|
|
4181
|
+
this.#signer = input.signer;
|
|
4182
|
+
this.#fns = input.functions ?? DEFAULT_FUNCTIONS$1;
|
|
4183
|
+
}
|
|
4184
|
+
async startOrResume(input) {
|
|
4185
|
+
const result = await runCall("startOrResume", this.#convex.mutation(this.#fns.startOrResume, input));
|
|
4186
|
+
if (!result.ok) return result;
|
|
4187
|
+
const lifecycle = parseLifecycle("startOrResume", result.value.lifecycle);
|
|
4188
|
+
if (!lifecycle.ok) return lifecycle;
|
|
4189
|
+
const orgId = parseOrgId("startOrResume", result.value.orgId);
|
|
4190
|
+
if (!orgId.ok) return orgId;
|
|
4191
|
+
if (String(orgId.value) !== String(lifecycle.value.orgId)) return fail$2(Errors.invalidInput("orgId", "Organization lifecycle scope does not match"));
|
|
4192
|
+
return {
|
|
4193
|
+
ok: true,
|
|
4194
|
+
value: {
|
|
4195
|
+
orgId: orgId.value,
|
|
4196
|
+
lifecycle: lifecycle.value
|
|
4197
|
+
}
|
|
4198
|
+
};
|
|
4199
|
+
}
|
|
4200
|
+
prepareFounderAccount(input) {
|
|
4201
|
+
return this.#lifecycleAction("prepareFounderAccount", input, () => this.#convex.action(this.#fns.prepareFounderAccount, { orgId: input.orgId }));
|
|
4202
|
+
}
|
|
4203
|
+
async authorizeAndSubmitBootstrap(input) {
|
|
4204
|
+
const cancelled = cancellation(input.signal);
|
|
4205
|
+
if (cancelled !== void 0) return cancelled;
|
|
4206
|
+
const signerAddress = await signerResult("getAddress", () => this.#signer.getAddress());
|
|
4207
|
+
if (!signerAddress.ok) return signerAddress;
|
|
4208
|
+
const prepared = await runCall("prepareBootstrap", this.#convex.action(this.#fns.prepareBootstrap, {
|
|
4209
|
+
orgId: input.orgId,
|
|
4210
|
+
signerAddress: signerAddress.value
|
|
4211
|
+
}));
|
|
4212
|
+
if (!prepared.ok) return prepared;
|
|
4213
|
+
const authority = validatePreparedAuthorities(prepared.value, signerAddress.value);
|
|
4214
|
+
if (!authority.ok) return authority;
|
|
4215
|
+
const cancelledAfterPrepare = cancellation(input.signal);
|
|
4216
|
+
if (cancelledAfterPrepare !== void 0) return cancelledAfterPrepare;
|
|
4217
|
+
const signature = await signerResult("signUserOpHash", () => this.#signer.signUserOpHash(prepared.value.digest));
|
|
4218
|
+
if (!signature.ok) return signature;
|
|
4219
|
+
const cancelledAfterSign = cancellation(input.signal);
|
|
4220
|
+
if (cancelledAfterSign !== void 0) return cancelledAfterSign;
|
|
4221
|
+
const submitted = await runCall("submitBootstrap", this.#convex.action(this.#fns.submitBootstrap, {
|
|
4222
|
+
orgId: input.orgId,
|
|
4223
|
+
signerAddress: signerAddress.value,
|
|
4224
|
+
signature: signature.value,
|
|
4225
|
+
userOp: prepared.value.userOp
|
|
4226
|
+
}));
|
|
4227
|
+
if (!submitted.ok) return submitted;
|
|
4228
|
+
return parseLifecycle("submitBootstrap", submitted.value);
|
|
4229
|
+
}
|
|
4230
|
+
resumeSubmittedBootstrap(input) {
|
|
4231
|
+
return this.#lifecycleAction("resumeBootstrapSubmission", input, () => this.#convex.action(this.#fns.resumeBootstrapSubmission, { orgId: input.orgId }));
|
|
4232
|
+
}
|
|
4233
|
+
confirmSubmittedBootstrap(input) {
|
|
4234
|
+
return this.#lifecycleAction("confirmBootstrap", input, () => this.#convex.action(this.#fns.confirmBootstrap, { orgId: input.orgId }));
|
|
4235
|
+
}
|
|
4236
|
+
async recordFailure(input) {
|
|
4237
|
+
const errorProvider = input.error.details?.provider;
|
|
4238
|
+
const errorOperation = input.error.details?.operation;
|
|
4239
|
+
const result = await runCall("recordFailure", this.#convex.mutation(this.#fns.recordFailure, {
|
|
4240
|
+
orgId: input.orgId,
|
|
4241
|
+
errorCode: input.error.code,
|
|
4242
|
+
...typeof errorProvider === "string" && typeof errorOperation === "string" ? {
|
|
4243
|
+
errorProvider,
|
|
4244
|
+
errorOperation
|
|
4245
|
+
} : {},
|
|
4246
|
+
retryable: input.retryable
|
|
4247
|
+
}));
|
|
4248
|
+
return result.ok ? parseLifecycle("recordFailure", result.value) : result;
|
|
4249
|
+
}
|
|
4250
|
+
async loadLifecycle(input) {
|
|
4251
|
+
const result = await runCall("loadLifecycle", this.#convex.query(this.#fns.load, input));
|
|
4252
|
+
if (!result.ok) return result;
|
|
4253
|
+
if (result.value === null) return fail$2(Errors.invalidInput("orgId", "Organization lifecycle was not found"));
|
|
4254
|
+
return parseLifecycle("loadLifecycle", result.value);
|
|
4255
|
+
}
|
|
4256
|
+
async retry(input) {
|
|
4257
|
+
const cancelled = cancellation(input.signal);
|
|
4258
|
+
if (cancelled !== void 0) return cancelled;
|
|
4259
|
+
const current = await this.loadLifecycle({ orgId: input.orgId });
|
|
4260
|
+
if (!current.ok) return current;
|
|
4261
|
+
if (current.value.status === "failed" && current.value.retryable && (current.value.at === "awaitingFounderAuthorization" || current.value.at === "submittingBootstrap")) {
|
|
4262
|
+
const reset = resetSignerSession(this.#signer);
|
|
4263
|
+
if (!reset.ok) return reset;
|
|
4264
|
+
}
|
|
4265
|
+
const result = await runCall("retry", this.#convex.mutation(this.#fns.retry, { orgId: input.orgId }));
|
|
4266
|
+
if (!result.ok) return result;
|
|
4267
|
+
return parseLifecycle("retry", result.value);
|
|
4268
|
+
}
|
|
4269
|
+
async #lifecycleAction(operation, input, call) {
|
|
4270
|
+
const cancelled = cancellation(input.signal);
|
|
4271
|
+
if (cancelled !== void 0) return cancelled;
|
|
4272
|
+
const result = await runCall(operation, call());
|
|
4273
|
+
if (!result.ok) return result;
|
|
4274
|
+
const cancelledAfter = cancellation(input.signal);
|
|
4275
|
+
if (cancelledAfter !== void 0) return cancelledAfter;
|
|
4276
|
+
return parseLifecycle(operation, result.value);
|
|
4277
|
+
}
|
|
4278
|
+
};
|
|
4279
|
+
function resetSignerSession(signer) {
|
|
4280
|
+
const resetSession = signer.resetSession;
|
|
4281
|
+
if (typeof resetSession !== "function") return {
|
|
4282
|
+
ok: true,
|
|
4283
|
+
value: void 0
|
|
4284
|
+
};
|
|
4285
|
+
try {
|
|
4286
|
+
resetSession.call(signer);
|
|
4287
|
+
return {
|
|
4288
|
+
ok: true,
|
|
4289
|
+
value: void 0
|
|
4290
|
+
};
|
|
4291
|
+
} catch (cause) {
|
|
4292
|
+
return fail$2(cause instanceof CapxulError ? cause : Errors.providerError("openfort", "resetSession", cause, { failure_mode: "unknown" }));
|
|
4293
|
+
}
|
|
4294
|
+
}
|
|
4295
|
+
async function runCall(operation, effect) {
|
|
4296
|
+
try {
|
|
4297
|
+
const result = await Effect.runPromise(Effect.either(effect));
|
|
4298
|
+
return Either.isRight(result) ? {
|
|
4299
|
+
ok: true,
|
|
4300
|
+
value: result.right
|
|
4301
|
+
} : fail$2(publicError(operation, result.left));
|
|
4302
|
+
} catch (cause) {
|
|
4303
|
+
return fail$2(publicError(operation, cause));
|
|
4304
|
+
}
|
|
4305
|
+
}
|
|
4306
|
+
function publicError(operation, cause) {
|
|
4307
|
+
if (cause instanceof CapxulError) return cause;
|
|
4308
|
+
if (typeof cause === "object" && cause !== null) {
|
|
4309
|
+
const carried = cause.publicError;
|
|
4310
|
+
if (carried instanceof CapxulError) return carried;
|
|
4311
|
+
}
|
|
4312
|
+
return Errors.providerError("convex-organization", operation, cause);
|
|
4313
|
+
}
|
|
4314
|
+
async function signerResult(operation, run) {
|
|
4315
|
+
try {
|
|
4316
|
+
return {
|
|
4317
|
+
ok: true,
|
|
4318
|
+
value: await run()
|
|
4319
|
+
};
|
|
4320
|
+
} catch (cause) {
|
|
4321
|
+
return fail$2(cause instanceof CapxulError ? cause : Errors.providerError("organization-signer", operation, cause));
|
|
4322
|
+
}
|
|
4323
|
+
}
|
|
4324
|
+
function validatePreparedAuthorities(prepared, signerAddress) {
|
|
4325
|
+
const signer = signerAddress.toLowerCase();
|
|
4326
|
+
const preparedSigner = prepared.signerAddress.toLowerCase();
|
|
4327
|
+
const founder = prepared.founderPersonalAccount.toLowerCase();
|
|
4328
|
+
const organization = prepared.organizationAccountAddress.toLowerCase();
|
|
4329
|
+
const sender = prepared.userOp.sender.toLowerCase();
|
|
4330
|
+
if (preparedSigner !== signer) return fail$2(Errors.invalidInput("signerAddress", "Prepared signer does not match configured signer"));
|
|
4331
|
+
if (founder === signer || organization === signer || organization === founder) return fail$2(Errors.invalidInput("organizationAuthority", "Signer EOA, founder Account, and Organization Account must be distinct"));
|
|
4332
|
+
if (sender !== founder) return fail$2(Errors.invalidInput("userOp.sender", "Bootstrap sender must be the founder Account"));
|
|
4333
|
+
if (!/^0x[0-9a-fA-F]{64}$/u.test(prepared.digest)) return fail$2(Errors.invalidInput("digest", "Prepared bootstrap digest must be 32-byte hex"));
|
|
4334
|
+
return {
|
|
4335
|
+
ok: true,
|
|
4336
|
+
value: void 0
|
|
4337
|
+
};
|
|
4338
|
+
}
|
|
4339
|
+
function parseLifecycle(operation, wire) {
|
|
4340
|
+
const orgId = parseOrgId(operation, wire.orgId);
|
|
4341
|
+
if (!orgId.ok) return orgId;
|
|
4342
|
+
if (wire.status === "loading") return {
|
|
4343
|
+
ok: true,
|
|
4344
|
+
value: {
|
|
4345
|
+
status: "loading",
|
|
4346
|
+
orgId: orgId.value
|
|
4347
|
+
}
|
|
4348
|
+
};
|
|
4349
|
+
if (wire.status === "ready") return {
|
|
4350
|
+
ok: true,
|
|
4351
|
+
value: {
|
|
4352
|
+
status: "ready",
|
|
4353
|
+
orgId: orgId.value,
|
|
4354
|
+
canTransact: true
|
|
4355
|
+
}
|
|
4356
|
+
};
|
|
4357
|
+
if (wire.status === "failed") {
|
|
4358
|
+
if (!isSetupStep(wire.at)) return fail$2(Errors.invalidInput("lifecycle.at", "Unknown setup step"));
|
|
4359
|
+
return {
|
|
4360
|
+
ok: true,
|
|
4361
|
+
value: {
|
|
4362
|
+
status: "failed",
|
|
4363
|
+
orgId: orgId.value,
|
|
4364
|
+
at: wire.at,
|
|
4365
|
+
error: new CapxulError(wire.error.code, wire.error.message),
|
|
4366
|
+
retryable: wire.retryable
|
|
4367
|
+
}
|
|
4368
|
+
};
|
|
4369
|
+
}
|
|
4370
|
+
if (!isSetupStep(wire.step)) return fail$2(Errors.invalidInput("lifecycle.step", `Unknown setup step from ${operation}`));
|
|
4371
|
+
return {
|
|
4372
|
+
ok: true,
|
|
4373
|
+
value: {
|
|
4374
|
+
status: "settingUp",
|
|
4375
|
+
orgId: orgId.value,
|
|
4376
|
+
step: wire.step
|
|
4377
|
+
}
|
|
4378
|
+
};
|
|
4379
|
+
}
|
|
4380
|
+
function parseOrgId(operation, value) {
|
|
4381
|
+
try {
|
|
4382
|
+
return {
|
|
4383
|
+
ok: true,
|
|
4384
|
+
value: toOrgId(value)
|
|
4385
|
+
};
|
|
4386
|
+
} catch (cause) {
|
|
4387
|
+
return fail$2(Errors.providerError("convex-organization", operation, cause));
|
|
4388
|
+
}
|
|
4389
|
+
}
|
|
4390
|
+
function isSetupStep(value) {
|
|
4391
|
+
return value === "preparingFounderAccount" || value === "awaitingFounderAuthorization" || value === "submittingBootstrap" || value === "confirmingBootstrap";
|
|
4392
|
+
}
|
|
4393
|
+
function cancellation(signal) {
|
|
4394
|
+
return signal?.aborted ? fail$2(Errors.cancelled({ operation: "organization.setup" })) : void 0;
|
|
4395
|
+
}
|
|
4396
|
+
function fail$2(error) {
|
|
4397
|
+
return {
|
|
4398
|
+
ok: false,
|
|
4399
|
+
error
|
|
4400
|
+
};
|
|
4401
|
+
}
|
|
4402
|
+
//#endregion
|
|
3968
4403
|
//#region ../observability/src/index.ts
|
|
3969
4404
|
const PII = Schema.String.pipe(Schema.brand("PII"));
|
|
3970
4405
|
const OptionalString = Schema.optional(Schema.String);
|
|
@@ -4805,6 +5240,9 @@ function transportErrorFromThrown(operation, request, cause) {
|
|
|
4805
5240
|
}
|
|
4806
5241
|
//#endregion
|
|
4807
5242
|
//#region src/openfort/create-openfort-browser-signer.ts
|
|
5243
|
+
function openfortProviderError(operation, cause) {
|
|
5244
|
+
return cause instanceof CapxulError ? cause : Errors.providerError("openfort", operation, cause, { failure_mode: "unknown" });
|
|
5245
|
+
}
|
|
4808
5246
|
/**
|
|
4809
5247
|
* True when the browser cannot perform Web Crypto — sandboxed iframes, headless
|
|
4810
5248
|
* agent browsers, or non-HTTPS origins. OpenFort's embedded-wallet `configure`
|
|
@@ -4873,21 +5311,39 @@ function createOpenfortBrowserSignerFromBootstrap(bootstrap, options = {}) {
|
|
|
4873
5311
|
return `${authBaseUrl}/encryption-session`;
|
|
4874
5312
|
}
|
|
4875
5313
|
async function fetchBetterAuthAccessToken() {
|
|
4876
|
-
|
|
4877
|
-
|
|
5314
|
+
try {
|
|
5315
|
+
const response = await fetch(betterAuthSessionUrl(), { credentials: "include" });
|
|
5316
|
+
if (!response.ok) {
|
|
5317
|
+
diagnostic?.trace("openfort.token", {
|
|
5318
|
+
ok: false,
|
|
5319
|
+
tokenPresent: false,
|
|
5320
|
+
httpStatus: response.status,
|
|
5321
|
+
failure_mode: "unknown"
|
|
5322
|
+
});
|
|
5323
|
+
return null;
|
|
5324
|
+
}
|
|
5325
|
+
const token = (await response.json()).session?.token?.trim();
|
|
5326
|
+
if (token === void 0 || token.length === 0) {
|
|
5327
|
+
diagnostic?.trace("openfort.token", {
|
|
5328
|
+
ok: false,
|
|
5329
|
+
tokenPresent: false,
|
|
5330
|
+
failure_mode: "unknown"
|
|
5331
|
+
});
|
|
5332
|
+
return null;
|
|
5333
|
+
}
|
|
5334
|
+
diagnostic?.trace("openfort.token", {
|
|
5335
|
+
ok: true,
|
|
5336
|
+
tokenPresent: true
|
|
5337
|
+
});
|
|
5338
|
+
return token;
|
|
5339
|
+
} catch (cause) {
|
|
4878
5340
|
diagnostic?.trace("openfort.token", {
|
|
5341
|
+
ok: false,
|
|
4879
5342
|
tokenPresent: false,
|
|
4880
|
-
|
|
5343
|
+
failure_mode: "unknown"
|
|
4881
5344
|
});
|
|
4882
|
-
|
|
5345
|
+
throw openfortProviderError("token", cause);
|
|
4883
5346
|
}
|
|
4884
|
-
const token = (await response.json()).session?.token?.trim();
|
|
4885
|
-
if (token === void 0 || token.length === 0) {
|
|
4886
|
-
diagnostic?.trace("openfort.token", { tokenPresent: false });
|
|
4887
|
-
return null;
|
|
4888
|
-
}
|
|
4889
|
-
diagnostic?.trace("openfort.token", { tokenPresent: true });
|
|
4890
|
-
return token;
|
|
4891
5347
|
}
|
|
4892
5348
|
const openfort = new Openfort({
|
|
4893
5349
|
baseConfiguration: { publishableKey: bootstrap.openfortPublishableKey },
|
|
@@ -4903,25 +5359,60 @@ function createOpenfortBrowserSignerFromBootstrap(bootstrap, options = {}) {
|
|
|
4903
5359
|
if (isInsecureBrowserContext()) failNoSecureContext();
|
|
4904
5360
|
await openfort.waitForInitialization();
|
|
4905
5361
|
const accessToken = await fetchBetterAuthAccessToken();
|
|
4906
|
-
if (accessToken === null) throw
|
|
4907
|
-
|
|
4908
|
-
|
|
4909
|
-
|
|
4910
|
-
|
|
4911
|
-
|
|
4912
|
-
|
|
4913
|
-
|
|
4914
|
-
|
|
4915
|
-
|
|
4916
|
-
|
|
5362
|
+
if (accessToken === null) throw openfortProviderError("token", /* @__PURE__ */ new Error("Better Auth access token unavailable for Openfort"));
|
|
5363
|
+
let encryptionResponse;
|
|
5364
|
+
try {
|
|
5365
|
+
encryptionResponse = await fetch(encryptionSessionUrl(), {
|
|
5366
|
+
method: "POST",
|
|
5367
|
+
credentials: "include",
|
|
5368
|
+
headers: {
|
|
5369
|
+
Authorization: `Bearer ${accessToken}`,
|
|
5370
|
+
"Content-Type": "application/json"
|
|
5371
|
+
},
|
|
5372
|
+
body: JSON.stringify({})
|
|
5373
|
+
});
|
|
5374
|
+
} catch (cause) {
|
|
5375
|
+
diagnostic?.trace("openfort.encryptionSession", {
|
|
5376
|
+
ok: false,
|
|
5377
|
+
failure_mode: "unknown"
|
|
5378
|
+
});
|
|
5379
|
+
throw openfortProviderError("encryptionSession", cause);
|
|
5380
|
+
}
|
|
4917
5381
|
if (!encryptionResponse.ok) {
|
|
4918
|
-
|
|
4919
|
-
|
|
5382
|
+
diagnostic?.trace("openfort.encryptionSession", {
|
|
5383
|
+
ok: false,
|
|
5384
|
+
httpStatus: encryptionResponse.status,
|
|
5385
|
+
failure_mode: "unknown"
|
|
5386
|
+
});
|
|
5387
|
+
throw Errors.providerError("openfort", "encryptionSession", /* @__PURE__ */ new Error(`Openfort encryption session failed (${encryptionResponse.status})`), { failure_mode: "unknown" });
|
|
5388
|
+
}
|
|
5389
|
+
let encryptionBody;
|
|
5390
|
+
try {
|
|
5391
|
+
encryptionBody = await encryptionResponse.json();
|
|
5392
|
+
if (typeof encryptionBody.sessionId !== "string" || encryptionBody.sessionId.length === 0) throw new Error("Openfort encryption session response missing sessionId");
|
|
5393
|
+
} catch (cause) {
|
|
5394
|
+
diagnostic?.trace("openfort.encryptionSession", {
|
|
5395
|
+
ok: false,
|
|
5396
|
+
httpStatus: encryptionResponse.status,
|
|
5397
|
+
failure_mode: "unknown"
|
|
5398
|
+
});
|
|
5399
|
+
throw openfortProviderError("encryptionSession", cause);
|
|
5400
|
+
}
|
|
5401
|
+
diagnostic?.trace("openfort.encryptionSession", {
|
|
5402
|
+
ok: true,
|
|
5403
|
+
httpStatus: encryptionResponse.status
|
|
5404
|
+
});
|
|
5405
|
+
let embeddedState;
|
|
5406
|
+
try {
|
|
5407
|
+
embeddedState = await openfort.embeddedWallet.getEmbeddedState();
|
|
5408
|
+
diagnostic?.trace("openfort.embeddedState", { state: embeddedState });
|
|
5409
|
+
} catch (cause) {
|
|
5410
|
+
diagnostic?.trace("openfort.embeddedState", {
|
|
5411
|
+
ok: false,
|
|
5412
|
+
failure_mode: "unknown"
|
|
5413
|
+
});
|
|
5414
|
+
throw openfortProviderError("embeddedState", cause);
|
|
4920
5415
|
}
|
|
4921
|
-
const encryptionBody = await encryptionResponse.json();
|
|
4922
|
-
if (typeof encryptionBody.sessionId !== "string" || encryptionBody.sessionId.length === 0) throw Errors.providerError("openfort", "encryptionSession", /* @__PURE__ */ new Error("Openfort encryption session response missing sessionId"));
|
|
4923
|
-
const embeddedState = await openfort.embeddedWallet.getEmbeddedState();
|
|
4924
|
-
diagnostic?.trace("openfort.embeddedState", { state: embeddedState });
|
|
4925
5416
|
if (embeddedState !== EmbeddedState.READY) {
|
|
4926
5417
|
clearStaleOpenfortBrowserStorage(bootstrap.openfortPublishableKey);
|
|
4927
5418
|
diagnostic?.trace("openfort.storageCleared", { beforeConfigure: true });
|
|
@@ -4938,9 +5429,9 @@ function createOpenfortBrowserSignerFromBootstrap(bootstrap, options = {}) {
|
|
|
4938
5429
|
} catch (cause) {
|
|
4939
5430
|
diagnostic?.trace("openfort.configure", {
|
|
4940
5431
|
ok: false,
|
|
4941
|
-
|
|
5432
|
+
failure_mode: "unknown"
|
|
4942
5433
|
});
|
|
4943
|
-
throw cause;
|
|
5434
|
+
throw openfortProviderError("configure", cause);
|
|
4944
5435
|
}
|
|
4945
5436
|
}
|
|
4946
5437
|
try {
|
|
@@ -4949,9 +5440,9 @@ function createOpenfortBrowserSignerFromBootstrap(bootstrap, options = {}) {
|
|
|
4949
5440
|
} catch (cause) {
|
|
4950
5441
|
diagnostic?.trace("openfort.get", {
|
|
4951
5442
|
ok: false,
|
|
4952
|
-
|
|
5443
|
+
failure_mode: "unknown"
|
|
4953
5444
|
});
|
|
4954
|
-
throw cause;
|
|
5445
|
+
throw openfortProviderError("get", cause);
|
|
4955
5446
|
}
|
|
4956
5447
|
})();
|
|
4957
5448
|
}
|
|
@@ -4970,6 +5461,19 @@ function createOpenfortBrowserSignerFromBootstrap(bootstrap, options = {}) {
|
|
|
4970
5461
|
});
|
|
4971
5462
|
return {
|
|
4972
5463
|
...signer,
|
|
5464
|
+
getAddress: async () => {
|
|
5465
|
+
try {
|
|
5466
|
+
const address = await signer.getAddress();
|
|
5467
|
+
diagnostic?.trace("openfort.address", { ok: true });
|
|
5468
|
+
return address;
|
|
5469
|
+
} catch (cause) {
|
|
5470
|
+
diagnostic?.trace("openfort.address", {
|
|
5471
|
+
ok: false,
|
|
5472
|
+
failure_mode: "unknown"
|
|
5473
|
+
});
|
|
5474
|
+
throw openfortProviderError("getAddress", cause);
|
|
5475
|
+
}
|
|
5476
|
+
},
|
|
4973
5477
|
resetSession: () => {
|
|
4974
5478
|
walletReadyPromise = null;
|
|
4975
5479
|
clearStaleOpenfortBrowserStorage(bootstrap.openfortPublishableKey);
|
|
@@ -7451,6 +7955,80 @@ function listOrgsProgram() {
|
|
|
7451
7955
|
});
|
|
7452
7956
|
}
|
|
7453
7957
|
//#endregion
|
|
7958
|
+
//#region src/client/org-lifecycle.ts
|
|
7959
|
+
function fail$1(error) {
|
|
7960
|
+
return {
|
|
7961
|
+
ok: false,
|
|
7962
|
+
error
|
|
7963
|
+
};
|
|
7964
|
+
}
|
|
7965
|
+
function validateOrganizationLifecycleScope(orgId, lifecycle) {
|
|
7966
|
+
if (String(lifecycle.orgId) !== String(orgId)) return fail$1(Errors.invalidInput("orgId", "Organization lifecycle scope does not match"));
|
|
7967
|
+
return {
|
|
7968
|
+
ok: true,
|
|
7969
|
+
value: lifecycle
|
|
7970
|
+
};
|
|
7971
|
+
}
|
|
7972
|
+
/** Advance only the steps still required by one durable Organization lane. */
|
|
7973
|
+
async function advanceOrganizationSetup(setup, orgId, initial, signal) {
|
|
7974
|
+
const scopedInitial = validateOrganizationLifecycleScope(orgId, initial);
|
|
7975
|
+
if (!scopedInitial.ok) return scopedInitial;
|
|
7976
|
+
let lifecycle = scopedInitial.value;
|
|
7977
|
+
for (let transition = 0; transition < 3; transition += 1) {
|
|
7978
|
+
if (lifecycle.status !== "settingUp") return {
|
|
7979
|
+
ok: true,
|
|
7980
|
+
value: lifecycle
|
|
7981
|
+
};
|
|
7982
|
+
const currentStep = lifecycle.step;
|
|
7983
|
+
if (signal?.aborted) return recordSetupFailure(setup, orgId, Errors.cancelled({ operation: "organization.setup" }), true);
|
|
7984
|
+
const stepInput = {
|
|
7985
|
+
orgId,
|
|
7986
|
+
...signal === void 0 ? {} : { signal }
|
|
7987
|
+
};
|
|
7988
|
+
const next = lifecycle.step === "preparingFounderAccount" ? await setup.prepareFounderAccount(stepInput) : lifecycle.step === "awaitingFounderAuthorization" ? await setup.authorizeAndSubmitBootstrap(stepInput) : lifecycle.step === "submittingBootstrap" ? await setup.resumeSubmittedBootstrap(stepInput) : await setup.confirmSubmittedBootstrap(stepInput);
|
|
7989
|
+
if (!next.ok) {
|
|
7990
|
+
if (next.error.code === "CANCELLED") return recordSetupFailure(setup, orgId, next.error, true);
|
|
7991
|
+
if (next.error.code === "PROVIDER_ERROR") return recordSetupFailure(setup, orgId, next.error, isRetryableProviderFailure(next.error));
|
|
7992
|
+
return next;
|
|
7993
|
+
}
|
|
7994
|
+
const scopedNext = validateOrganizationLifecycleScope(orgId, next.value);
|
|
7995
|
+
if (!scopedNext.ok) return scopedNext;
|
|
7996
|
+
lifecycle = scopedNext.value;
|
|
7997
|
+
if (currentStep === "confirmingBootstrap") return {
|
|
7998
|
+
ok: true,
|
|
7999
|
+
value: lifecycle
|
|
8000
|
+
};
|
|
8001
|
+
}
|
|
8002
|
+
if (lifecycle.status !== "settingUp" || lifecycle.step === "confirmingBootstrap") return {
|
|
8003
|
+
ok: true,
|
|
8004
|
+
value: lifecycle
|
|
8005
|
+
};
|
|
8006
|
+
return fail$1(Errors.wrongState({
|
|
8007
|
+
method: "organization.setup",
|
|
8008
|
+
currentState: lifecycle.step,
|
|
8009
|
+
validStates: [
|
|
8010
|
+
"confirmingBootstrap",
|
|
8011
|
+
"ready",
|
|
8012
|
+
"failed"
|
|
8013
|
+
]
|
|
8014
|
+
}));
|
|
8015
|
+
}
|
|
8016
|
+
async function recordSetupFailure(setup, orgId, error, retryable) {
|
|
8017
|
+
const recorded = await setup.recordFailure({
|
|
8018
|
+
orgId,
|
|
8019
|
+
error,
|
|
8020
|
+
retryable
|
|
8021
|
+
});
|
|
8022
|
+
if (!recorded.ok) return recorded;
|
|
8023
|
+
const scopedRecorded = validateOrganizationLifecycleScope(orgId, recorded.value);
|
|
8024
|
+
if (!scopedRecorded.ok) return scopedRecorded;
|
|
8025
|
+
return fail$1(error);
|
|
8026
|
+
}
|
|
8027
|
+
function isRetryableProviderFailure(error) {
|
|
8028
|
+
const mode = error.details?.failure_mode;
|
|
8029
|
+
return mode !== "auth-origin-mismatch" && mode !== "app-env-allowlist" && mode !== "no-secure-context";
|
|
8030
|
+
}
|
|
8031
|
+
//#endregion
|
|
7454
8032
|
//#region src/client/org.ts
|
|
7455
8033
|
/**
|
|
7456
8034
|
* Org method surface (canon §C2/§C3, D13). S1 (#274) wires `createOrg` /
|
|
@@ -7499,6 +8077,36 @@ function makeOrgMethods(deps) {
|
|
|
7499
8077
|
},
|
|
7500
8078
|
...convexCall === void 0 ? {} : { convexCall }
|
|
7501
8079
|
}),
|
|
8080
|
+
async getLifecycle(options) {
|
|
8081
|
+
if (options?.signal?.aborted) return {
|
|
8082
|
+
ok: false,
|
|
8083
|
+
error: Errors.cancelled({ operation: "org.getLifecycle" })
|
|
8084
|
+
};
|
|
8085
|
+
if (deps.organizationSetup === void 0) return {
|
|
8086
|
+
ok: false,
|
|
8087
|
+
error: Errors.notImplemented("organizationSetup", "getLifecycle")
|
|
8088
|
+
};
|
|
8089
|
+
const loaded = await deps.organizationSetup.loadLifecycle({ orgId });
|
|
8090
|
+
if (!loaded.ok) return loaded;
|
|
8091
|
+
return validateOrganizationLifecycleScope(orgId, loaded.value);
|
|
8092
|
+
},
|
|
8093
|
+
async retrySetup(options) {
|
|
8094
|
+
if (options?.signal?.aborted) return {
|
|
8095
|
+
ok: false,
|
|
8096
|
+
error: Errors.cancelled({ operation: "org.retrySetup" })
|
|
8097
|
+
};
|
|
8098
|
+
const setup = deps.organizationSetup;
|
|
8099
|
+
if (setup === void 0) return {
|
|
8100
|
+
ok: false,
|
|
8101
|
+
error: Errors.notImplemented("organizationSetup", "retrySetup")
|
|
8102
|
+
};
|
|
8103
|
+
const retried = await setup.retry({
|
|
8104
|
+
orgId,
|
|
8105
|
+
...options?.signal === void 0 ? {} : { signal: options.signal }
|
|
8106
|
+
});
|
|
8107
|
+
if (!retried.ok) return retried;
|
|
8108
|
+
return advanceOrganizationSetup(setup, orgId, retried.value, options?.signal);
|
|
8109
|
+
},
|
|
7502
8110
|
profile: {
|
|
7503
8111
|
get(_options) {
|
|
7504
8112
|
if (_options?.signal?.aborted) return Promise.resolve({
|
|
@@ -7532,12 +8140,12 @@ function makeOrgMethods(deps) {
|
|
|
7532
8140
|
ok: false,
|
|
7533
8141
|
error: treasury.error
|
|
7534
8142
|
};
|
|
7535
|
-
const
|
|
7536
|
-
if (!
|
|
8143
|
+
const orgList = await toCapxulResult(listOrgsProgram(), layer);
|
|
8144
|
+
if (!orgList.ok) return {
|
|
7537
8145
|
ok: false,
|
|
7538
|
-
error:
|
|
8146
|
+
error: orgList.error
|
|
7539
8147
|
};
|
|
7540
|
-
const org =
|
|
8148
|
+
const org = orgList.value.find((candidate) => candidate.id === orgId);
|
|
7541
8149
|
return {
|
|
7542
8150
|
ok: true,
|
|
7543
8151
|
value: {
|
|
@@ -7725,7 +8333,6 @@ function normalizePaymentTiming(payment) {
|
|
|
7725
8333
|
}
|
|
7726
8334
|
//#endregion
|
|
7727
8335
|
//#region src/flows/onboarding.ts
|
|
7728
|
-
const DEFAULT_ORG_TEMPLATE = "Startup";
|
|
7729
8336
|
async function runCompletePersonalOnboarding(ops, input) {
|
|
7730
8337
|
const validated = validatePersonalInput(input);
|
|
7731
8338
|
if (!validated.ok) return validated;
|
|
@@ -7749,11 +8356,13 @@ async function runCompletePersonalOnboarding(ops, input) {
|
|
|
7749
8356
|
value: { lifecycle: lifecycle.value }
|
|
7750
8357
|
};
|
|
7751
8358
|
}
|
|
7752
|
-
async function runCompleteOrganizationOnboarding(ops, input) {
|
|
8359
|
+
async function runCompleteOrganizationOnboarding(ops, input, options) {
|
|
7753
8360
|
const validated = validateOrganizationInput(input);
|
|
7754
8361
|
if (!validated.ok) return validated;
|
|
7755
8362
|
const session = await ops.currentSession();
|
|
7756
8363
|
if (session === null) return fail(Errors.notAuthenticated());
|
|
8364
|
+
const setup = ops.organizationSetup;
|
|
8365
|
+
if (setup === void 0) return fail(Errors.notImplemented("organizationSetup", "completeOrganization"));
|
|
7757
8366
|
const written = await ops.completeIdentityOnboarding({
|
|
7758
8367
|
authUserId: session.authUserId,
|
|
7759
8368
|
email: session.email,
|
|
@@ -7761,22 +8370,29 @@ async function runCompleteOrganizationOnboarding(ops, input) {
|
|
|
7761
8370
|
country: validated.value.country
|
|
7762
8371
|
});
|
|
7763
8372
|
if (!written.ok) return fail(written.error);
|
|
7764
|
-
const
|
|
7765
|
-
if (!provisioned.ok) return fail(provisioned.error);
|
|
7766
|
-
const org = await ops.createOrg({
|
|
8373
|
+
const started = await setup.startOrResume({
|
|
7767
8374
|
name: validated.value.organizationName,
|
|
7768
8375
|
handle: validated.value.handle,
|
|
7769
|
-
template: DEFAULT_ORG_TEMPLATE,
|
|
7770
8376
|
country: validated.value.country
|
|
7771
8377
|
});
|
|
7772
|
-
if (!
|
|
7773
|
-
|
|
7774
|
-
|
|
7775
|
-
|
|
8378
|
+
if (!started.ok) return fail(started.error);
|
|
8379
|
+
const lifecycle = await advanceOrganizationSetup(setup, started.value.orgId, started.value.lifecycle, options?.signal);
|
|
8380
|
+
if (!lifecycle.ok) {
|
|
8381
|
+
const error = lifecycle.error;
|
|
8382
|
+
return fail(new CapxulError(error.code, error.message, {
|
|
8383
|
+
cause: error.cause,
|
|
8384
|
+
details: {
|
|
8385
|
+
...error.details,
|
|
8386
|
+
orgId: started.value.orgId
|
|
8387
|
+
},
|
|
8388
|
+
...error.correlationId === void 0 ? {} : { correlationId: error.correlationId },
|
|
8389
|
+
...error.layer === void 0 ? {} : { layer: error.layer }
|
|
8390
|
+
}));
|
|
8391
|
+
}
|
|
7776
8392
|
return {
|
|
7777
8393
|
ok: true,
|
|
7778
8394
|
value: {
|
|
7779
|
-
|
|
8395
|
+
orgId: started.value.orgId,
|
|
7780
8396
|
lifecycle: lifecycle.value
|
|
7781
8397
|
}
|
|
7782
8398
|
};
|
|
@@ -7867,8 +8483,7 @@ function fail(error) {
|
|
|
7867
8483
|
* Onboarding method surface. The thin namespace over the `flows/onboarding.ts`
|
|
7868
8484
|
* choreography: it adds the `signal`-abort pre-check and delegates to the run
|
|
7869
8485
|
* functions. The `OnboardingOps` seam is supplied by `assembleCapxulClient`
|
|
7870
|
-
* (real ops
|
|
7871
|
-
* (in-memory double).
|
|
8486
|
+
* (real identity/Account ops plus Organization setup capability) or by tests.
|
|
7872
8487
|
*/
|
|
7873
8488
|
function makeOnboardingMethods(ops) {
|
|
7874
8489
|
return {
|
|
@@ -7884,7 +8499,7 @@ function makeOnboardingMethods(ops) {
|
|
|
7884
8499
|
ok: false,
|
|
7885
8500
|
error: Errors.cancelled({ operation: "onboarding.completeOrganization" })
|
|
7886
8501
|
});
|
|
7887
|
-
return runCompleteOrganizationOnboarding(ops, input);
|
|
8502
|
+
return runCompleteOrganizationOnboarding(ops, input, options);
|
|
7888
8503
|
}
|
|
7889
8504
|
};
|
|
7890
8505
|
}
|
|
@@ -7897,6 +8512,7 @@ function detectAuthCacheAdapter() {
|
|
|
7897
8512
|
}
|
|
7898
8513
|
//#endregion
|
|
7899
8514
|
//#region src/client/create-capxul-client.ts
|
|
8515
|
+
const GET_ORGANIZATION_SETUP_PROOF_RECEIPT = makeFunctionReference("org/lifecycle:getProofReceipt");
|
|
7900
8516
|
function assembleCapxulClient(input) {
|
|
7901
8517
|
const authCache = input.authCache ?? detectAuthCacheAdapter();
|
|
7902
8518
|
const layer = Layer.mergeAll(Layer.succeed(AuthClientPortTag, input.ports.authClient), Layer.succeed(ClockPortTag, input.ports.clock), Layer.succeed(TelemetryPortTag, input.ports.telemetry));
|
|
@@ -7963,7 +8579,8 @@ function assembleCapxulClient(input) {
|
|
|
7963
8579
|
...selectedOrgPort === void 0 ? {} : { orgPort: selectedOrgPort },
|
|
7964
8580
|
...input.orgRolesDeploymentPort === void 0 ? {} : { orgRolesDeploymentPort: input.orgRolesDeploymentPort },
|
|
7965
8581
|
...input.orgSpendPort === void 0 ? {} : { orgSpendPort: input.orgSpendPort },
|
|
7966
|
-
convexCall: input.ports.convexCall
|
|
8582
|
+
convexCall: input.ports.convexCall,
|
|
8583
|
+
...input.organizationSetup === void 0 ? {} : { organizationSetup: input.organizationSetup }
|
|
7967
8584
|
});
|
|
7968
8585
|
if (selectedOrgPort !== void 0) detectPendingOrgInvitations = async () => {
|
|
7969
8586
|
await orgMethods.orgs.detectAndAcceptPendingInvitations();
|
|
@@ -7986,7 +8603,7 @@ function assembleCapxulClient(input) {
|
|
|
7986
8603
|
triggerProvisioning: () => account._internal.provision(),
|
|
7987
8604
|
kickProvisioning: () => kickProvisioning?.(),
|
|
7988
8605
|
readLifecycle: () => account.getLifecycle(),
|
|
7989
|
-
|
|
8606
|
+
...input.organizationSetup === void 0 ? {} : { organizationSetup: input.organizationSetup }
|
|
7990
8607
|
});
|
|
7991
8608
|
return {
|
|
7992
8609
|
auth,
|
|
@@ -8019,6 +8636,7 @@ function assembleCapxulClient(input) {
|
|
|
8019
8636
|
},
|
|
8020
8637
|
bootstrap: input.bootstrap,
|
|
8021
8638
|
accounts: { fund: accounts.fund },
|
|
8639
|
+
organizationSetup: { getProofReceipt: (orgId) => runPortEffect(input.ports.convexCall.query(GET_ORGANIZATION_SETUP_PROOF_RECEIPT, { orgId })) },
|
|
8022
8640
|
telemetry: input.ports.telemetry
|
|
8023
8641
|
}
|
|
8024
8642
|
};
|
|
@@ -8028,19 +8646,6 @@ function assembleCapxulClient(input) {
|
|
|
8028
8646
|
const SDK_VERSION$1 = version;
|
|
8029
8647
|
/** Stable PostHog event used for typed failures that are expected product outcomes. */
|
|
8030
8648
|
const CAPXUL_SDK_EXPECTED_OUTCOME_EVENT = "capxul_sdk_expected_outcome";
|
|
8031
|
-
const EXPECTED_OPERATION_OUTCOMES = new Set([
|
|
8032
|
-
"INVALID_INPUT",
|
|
8033
|
-
"NOT_AUTHENTICATED",
|
|
8034
|
-
"CANCELLED",
|
|
8035
|
-
"SIGNER_REJECTED",
|
|
8036
|
-
"VERIFICATION_REQUIRED",
|
|
8037
|
-
"INSUFFICIENT_BALANCE",
|
|
8038
|
-
"INVALID_RECIPIENT",
|
|
8039
|
-
"ROLE_PERMISSION_DENIED",
|
|
8040
|
-
"RATE_LIMITED",
|
|
8041
|
-
"OTP_EXPIRED",
|
|
8042
|
-
"WRONG_STATE"
|
|
8043
|
-
]);
|
|
8044
8649
|
/**
|
|
8045
8650
|
* Adapt the host's already-initialized PostHog-like client. This function does
|
|
8046
8651
|
* not import, initialize, configure, or own PostHog.
|
|
@@ -8077,7 +8682,7 @@ function fromPostHog(client, options = {}) {
|
|
|
8077
8682
|
return {
|
|
8078
8683
|
resolveContext: () => {
|
|
8079
8684
|
if (!isEnabled(options.enabled) || client === null || client === void 0) return void 0;
|
|
8080
|
-
return
|
|
8685
|
+
return resolveRawContext(options.context) ?? {};
|
|
8081
8686
|
},
|
|
8082
8687
|
captureOperationFailure,
|
|
8083
8688
|
captureException
|
|
@@ -8091,6 +8696,34 @@ function deliver(capture) {
|
|
|
8091
8696
|
ignoreDeliveryFailure(capture());
|
|
8092
8697
|
} catch {}
|
|
8093
8698
|
}
|
|
8699
|
+
/**
|
|
8700
|
+
* Fan one SDK failure out to several adapters (issue #877). The SDK's own
|
|
8701
|
+
* first-party relay and an optional host-provided adapter both observe the same
|
|
8702
|
+
* failure. Order matters for `resolveContext`: the FIRST adapter that returns a
|
|
8703
|
+
* context wins, so pass the host adapter first to keep its call-start snapshot.
|
|
8704
|
+
* Each capture is isolated — a throwing adapter never blocks its peers or the
|
|
8705
|
+
* SDK. Returns `undefined` when no adapters are present (zero observation work).
|
|
8706
|
+
*/
|
|
8707
|
+
function composeObservationAdapters(...adapters) {
|
|
8708
|
+
const present = adapters.filter((adapter) => adapter !== void 0);
|
|
8709
|
+
if (present.length === 0) return void 0;
|
|
8710
|
+
if (present.length === 1) return present[0];
|
|
8711
|
+
const fanOut = (select) => {
|
|
8712
|
+
for (const adapter of present) try {
|
|
8713
|
+
ignoreDeliveryFailure(select(adapter));
|
|
8714
|
+
} catch {}
|
|
8715
|
+
};
|
|
8716
|
+
return {
|
|
8717
|
+
resolveContext: () => {
|
|
8718
|
+
for (const adapter of present) try {
|
|
8719
|
+
const context = adapter.resolveContext?.();
|
|
8720
|
+
if (context !== void 0) return context;
|
|
8721
|
+
} catch {}
|
|
8722
|
+
},
|
|
8723
|
+
captureOperationFailure: (failure) => fanOut((adapter) => adapter.captureOperationFailure(failure)),
|
|
8724
|
+
captureException: (failure) => fanOut((adapter) => adapter.captureException(failure))
|
|
8725
|
+
};
|
|
8726
|
+
}
|
|
8094
8727
|
/** @internal Decorates public client method bundles at the owning SDK boundary. */
|
|
8095
8728
|
function observeSdkClient(client, adapter) {
|
|
8096
8729
|
if (adapter === void 0) return client;
|
|
@@ -8194,7 +8827,7 @@ function report(adapter, kind, operation, cause, invocationContext) {
|
|
|
8194
8827
|
}
|
|
8195
8828
|
function resolveAdapterContext(adapter) {
|
|
8196
8829
|
try {
|
|
8197
|
-
return
|
|
8830
|
+
return adapter.resolveContext?.();
|
|
8198
8831
|
} catch {
|
|
8199
8832
|
return;
|
|
8200
8833
|
}
|
|
@@ -8225,8 +8858,12 @@ function postHogProperties(failure, context) {
|
|
|
8225
8858
|
return properties;
|
|
8226
8859
|
}
|
|
8227
8860
|
function resolveContext(context) {
|
|
8861
|
+
return sanitizeObservationContext(resolveRawContext(context));
|
|
8862
|
+
}
|
|
8863
|
+
/** Resolve the host context closure without sanitizing — the call-start snapshot. */
|
|
8864
|
+
function resolveRawContext(context) {
|
|
8228
8865
|
try {
|
|
8229
|
-
return
|
|
8866
|
+
return typeof context === "function" ? context() : context;
|
|
8230
8867
|
} catch {
|
|
8231
8868
|
return;
|
|
8232
8869
|
}
|
|
@@ -8288,6 +8925,43 @@ function isCapxulResult(value) {
|
|
|
8288
8925
|
function isFailedResult(value) {
|
|
8289
8926
|
return isCapxulResult(value) && value.ok === false && "error" in value;
|
|
8290
8927
|
}
|
|
8928
|
+
//#endregion
|
|
8929
|
+
//#region src/observation-relay.ts
|
|
8930
|
+
/**
|
|
8931
|
+
* Build the SDK-owned first-party relay adapter. Both capture entry points send
|
|
8932
|
+
* the same redacted envelope fire-and-forget; delivery is fully isolated from
|
|
8933
|
+
* the SDK's `CapxulResult` and can never throw into the caller.
|
|
8934
|
+
*/
|
|
8935
|
+
function createCapxulFirstPartyRelay(options) {
|
|
8936
|
+
const fetchImpl = options.fetch ?? resolveGlobalFetch();
|
|
8937
|
+
const send = (failure) => {
|
|
8938
|
+
if (fetchImpl === void 0) return;
|
|
8939
|
+
try {
|
|
8940
|
+
const headers = { "content-type": "application/json" };
|
|
8941
|
+
const encodedContext = encodeObservationContextHeader(failure.context);
|
|
8942
|
+
if (encodedContext !== void 0) headers[OBSERVATION_CONTEXT_HEADER] = encodedContext;
|
|
8943
|
+
const body = JSON.stringify({
|
|
8944
|
+
operation: failure.operation,
|
|
8945
|
+
error_kind: failure.errorKind,
|
|
8946
|
+
sdk_version: failure.sdkVersion
|
|
8947
|
+
});
|
|
8948
|
+
Promise.resolve(fetchImpl(options.ingestUrl, {
|
|
8949
|
+
method: "POST",
|
|
8950
|
+
headers,
|
|
8951
|
+
body,
|
|
8952
|
+
keepalive: true
|
|
8953
|
+
})).catch(() => void 0);
|
|
8954
|
+
} catch {}
|
|
8955
|
+
};
|
|
8956
|
+
return {
|
|
8957
|
+
captureOperationFailure: send,
|
|
8958
|
+
captureException: send
|
|
8959
|
+
};
|
|
8960
|
+
}
|
|
8961
|
+
function resolveGlobalFetch() {
|
|
8962
|
+
const candidate = globalThis.fetch;
|
|
8963
|
+
return typeof candidate === "function" ? candidate : void 0;
|
|
8964
|
+
}
|
|
8291
8965
|
const SDK_VERSION = version;
|
|
8292
8966
|
const collectProductionFlowPorts = Effect.gen(function* () {
|
|
8293
8967
|
const authClient = yield* AuthClientPortTag;
|
|
@@ -8575,7 +9249,7 @@ async function createCapxulClient$1(input) {
|
|
|
8575
9249
|
...input.authBaseUrl === void 0 ? {} : { override: input.authBaseUrl }
|
|
8576
9250
|
});
|
|
8577
9251
|
let signer = input.signer;
|
|
8578
|
-
if (signer === void 0 &&
|
|
9252
|
+
if (signer === void 0 && runtime === "browser") signer = createOpenfortBrowserSignerFromBootstrap({
|
|
8579
9253
|
...adapters.value.bootstrap,
|
|
8580
9254
|
authBaseUrl: resolvedAuthBaseUrl
|
|
8581
9255
|
});
|
|
@@ -8585,23 +9259,33 @@ async function createCapxulClient$1(input) {
|
|
|
8585
9259
|
authCache: adapters.value.ports.authCache,
|
|
8586
9260
|
requirement: input.requirement ?? "none",
|
|
8587
9261
|
...signer === void 0 ? {} : { signer },
|
|
9262
|
+
orgPort: new ConvexOrganizationAdapter({ convex: adapters.value.ports.convexCall }),
|
|
9263
|
+
...signer === void 0 ? {} : { organizationSetup: new ConvexOrganizationSetupAdapter({
|
|
9264
|
+
convex: adapters.value.ports.convexCall,
|
|
9265
|
+
signer
|
|
9266
|
+
}) },
|
|
8588
9267
|
...input.signal === void 0 ? {} : { signal: input.signal },
|
|
8589
9268
|
...input.otpTtlMs === void 0 ? {} : { otpTtlMs: input.otpTtlMs },
|
|
8590
9269
|
invokeTimeoutMs: input.invokeTimeoutMs ?? 3e4
|
|
8591
9270
|
}), signer);
|
|
8592
9271
|
const upstreamClose = adapters.value.close;
|
|
9272
|
+
const value = {
|
|
9273
|
+
...client,
|
|
9274
|
+
_internal: {
|
|
9275
|
+
...client._internal,
|
|
9276
|
+
close: async () => {
|
|
9277
|
+
if ("resetSession" in (signer ?? {})) signer.resetSession();
|
|
9278
|
+
await upstreamClose();
|
|
9279
|
+
}
|
|
9280
|
+
}
|
|
9281
|
+
};
|
|
9282
|
+
const relay = runtime === "browser" ? createCapxulFirstPartyRelay({
|
|
9283
|
+
ingestUrl: deriveObserveIngestUrl(adapters.value.bootstrap),
|
|
9284
|
+
...input.fetch === void 0 ? {} : { fetch: input.fetch }
|
|
9285
|
+
}) : void 0;
|
|
8593
9286
|
return {
|
|
8594
9287
|
ok: true,
|
|
8595
|
-
value: observeSdkClient(
|
|
8596
|
-
...client,
|
|
8597
|
-
_internal: {
|
|
8598
|
-
...client._internal,
|
|
8599
|
-
close: async () => {
|
|
8600
|
-
if ("resetSession" in (signer ?? {})) signer.resetSession();
|
|
8601
|
-
await upstreamClose();
|
|
8602
|
-
}
|
|
8603
|
-
}
|
|
8604
|
-
}, input.observation)
|
|
9288
|
+
value: observeSdkClient(value, composeObservationAdapters(input.observation, relay))
|
|
8605
9289
|
};
|
|
8606
9290
|
} catch (cause) {
|
|
8607
9291
|
await adapters.value.close().catch(() => void 0);
|
|
@@ -8643,6 +9327,38 @@ function resolveInput(input) {
|
|
|
8643
9327
|
};
|
|
8644
9328
|
}
|
|
8645
9329
|
}
|
|
9330
|
+
/**
|
|
9331
|
+
* Address of the Capxul client-relay ingest endpoint (Pipe 1, issue #877). The
|
|
9332
|
+
* HTTP router that serves `/v1/client/observe` lives on the deployment's
|
|
9333
|
+
* `.convex.site` host, while the bootstrap-resolved `convexUrl` is the sibling
|
|
9334
|
+
* `.convex.cloud` (WebSocket/query) host — the same deterministic pairing
|
|
9335
|
+
* `mintQuickstartKey` inverts. For any standard Convex deployment (including
|
|
9336
|
+
* production) rewriting the suffix targets the router directly with no host
|
|
9337
|
+
* proxy, so it is the primary rule.
|
|
9338
|
+
*
|
|
9339
|
+
* `siteBaseUrl` is deliberately NOT the primary source: it is an app-configured
|
|
9340
|
+
* field that defaults to a placeholder (`https://capxul.local`, see
|
|
9341
|
+
* `credentials/applications.ts`) for registered applications, so trusting it
|
|
9342
|
+
* outright would POST to a dead host for the common case. It is only consulted
|
|
9343
|
+
* as a fallback for a custom-domain `convexUrl` (no deterministic `.convex.site`
|
|
9344
|
+
* sibling) when it carries a real, non-placeholder origin.
|
|
9345
|
+
*/
|
|
9346
|
+
function deriveObserveIngestUrl(bootstrap) {
|
|
9347
|
+
const convexUrl = bootstrap.convexUrl.replace(/\/+$/, "");
|
|
9348
|
+
if (convexUrl.endsWith(".convex.cloud")) return `${convexUrl.replace(/\.convex\.cloud$/, ".convex.site")}/v1/client/observe`;
|
|
9349
|
+
return `${(usableSiteOrigin(bootstrap.siteBaseUrl) ?? convexUrl).replace(/\/+$/, "")}/v1/client/observe`;
|
|
9350
|
+
}
|
|
9351
|
+
/** A configured `siteBaseUrl` usable as a router host, or `undefined` if it is the placeholder. */
|
|
9352
|
+
function usableSiteOrigin(siteBaseUrl) {
|
|
9353
|
+
try {
|
|
9354
|
+
const url = new URL(siteBaseUrl);
|
|
9355
|
+
if (url.protocol !== "http:" && url.protocol !== "https:") return void 0;
|
|
9356
|
+
if (url.hostname === "capxul.local") return void 0;
|
|
9357
|
+
return url.origin;
|
|
9358
|
+
} catch {
|
|
9359
|
+
return;
|
|
9360
|
+
}
|
|
9361
|
+
}
|
|
8646
9362
|
function detectRuntime() {
|
|
8647
9363
|
const globalAny = globalThis;
|
|
8648
9364
|
return globalAny.window !== void 0 || globalAny.document !== void 0 ? "browser" : "node";
|