@capxul/sdk 1.0.0-alpha.17 → 1.0.0-alpha.18
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 +60 -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.map +1 -1
- package/dist/index.d.mts +82 -9
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +704 -64
- package/dist/index.mjs.map +1 -1
- 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-Bchp9bqb.d.mts} +3 -4
- package/dist/safe-deployment-Bchp9bqb.d.mts.map +1 -0
- package/package.json +4 -3
- package/dist/InMemoryAuthCacheAdapter-v5W-XB5M.mjs.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
|
/**
|
|
@@ -1600,7 +1618,7 @@ async function recoverRawDigestSigner(input) {
|
|
|
1600
1618
|
}
|
|
1601
1619
|
//#endregion
|
|
1602
1620
|
//#region package.json
|
|
1603
|
-
var version = "1.0.0-alpha.
|
|
1621
|
+
var version = "1.0.0-alpha.18";
|
|
1604
1622
|
//#endregion
|
|
1605
1623
|
//#region src/ports/auth-client.ts
|
|
1606
1624
|
var AuthClientError = class extends Data.TaggedError("AuthClientError") {};
|
|
@@ -1646,6 +1664,22 @@ function resolveAuthClientUrl(authBaseUrl, path) {
|
|
|
1646
1664
|
return `${base}${path}`;
|
|
1647
1665
|
}
|
|
1648
1666
|
//#endregion
|
|
1667
|
+
//#region ../wire/src/secret-material.ts
|
|
1668
|
+
const SENSITIVE_MATERIAL_PATTERNS = [
|
|
1669
|
+
/0x[a-fA-F0-9]{40,}/u,
|
|
1670
|
+
/(?:^|[^a-fA-F0-9])[a-fA-F0-9]{64}(?:$|[^a-fA-F0-9])/u,
|
|
1671
|
+
/eyJ[A-Za-z0-9_-]*\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+/u,
|
|
1672
|
+
/(?:(?: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
|
|
1673
|
+
];
|
|
1674
|
+
/**
|
|
1675
|
+
* Reject: does the value carry any known secret material? Best effort — callers
|
|
1676
|
+
* drop the whole value on a match; a false negative is a leak, a false positive
|
|
1677
|
+
* merely omits an observation field.
|
|
1678
|
+
*/
|
|
1679
|
+
function containsSensitiveMaterial(value) {
|
|
1680
|
+
return SENSITIVE_MATERIAL_PATTERNS.some((pattern) => pattern.test(value));
|
|
1681
|
+
}
|
|
1682
|
+
//#endregion
|
|
1649
1683
|
//#region ../wire/src/observation-context.ts
|
|
1650
1684
|
/** Single bounded HTTP carrier used before a Convex action envelope exists. */
|
|
1651
1685
|
const OBSERVATION_CONTEXT_HEADER = "x-capxul-observation-context";
|
|
@@ -1679,10 +1713,6 @@ const FIELD_RULES = {
|
|
|
1679
1713
|
pattern: /^anon_[A-Za-z0-9-]+$/u
|
|
1680
1714
|
}
|
|
1681
1715
|
};
|
|
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
1716
|
/**
|
|
1687
1717
|
* Copy only the canonical allowlist and silently omit malformed/sensitive
|
|
1688
1718
|
* values. Observation metadata is best effort and may never reject a domain
|
|
@@ -1709,9 +1739,6 @@ function isSafeField(field, value) {
|
|
|
1709
1739
|
const rule = FIELD_RULES[field];
|
|
1710
1740
|
return value.length > 0 && value.length <= rule.maxLength && value === value.trim() && !value.includes("://") && !containsSensitiveMaterial(value) && rule.pattern.test(value);
|
|
1711
1741
|
}
|
|
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
1742
|
//#endregion
|
|
1716
1743
|
//#region src/internal/observation-http.ts
|
|
1717
1744
|
/** Resolve one bounded pre-auth snapshot for an outbound SDK HTTP request. */
|
|
@@ -2733,7 +2760,7 @@ const PaymentSource = Schema.Union(Schema.Struct({
|
|
|
2733
2760
|
* `hashLineItems` (payment-document-hash.ts) into the Invoice `lineItemsHash`,
|
|
2734
2761
|
* and Σ(quantity × unitMinor) MUST equal the invoice `amount`. The hash binds
|
|
2735
2762
|
* the documentHash to the exact items; the sum-check makes the items add up to
|
|
2736
|
-
* the amount due. See the
|
|
2763
|
+
* the amount due. See the financial-operations contract in `packages/wire/CONTEXT.md`.
|
|
2737
2764
|
*/
|
|
2738
2765
|
const LineItem = Schema.Struct({
|
|
2739
2766
|
description: Schema.String,
|
|
@@ -3717,7 +3744,7 @@ function fromWei(rawBalance, decimals, currency) {
|
|
|
3717
3744
|
}
|
|
3718
3745
|
//#endregion
|
|
3719
3746
|
//#region src/adapters/account-read/ConvexAccountAdapter.ts
|
|
3720
|
-
const DEFAULT_FUNCTIONS$
|
|
3747
|
+
const DEFAULT_FUNCTIONS$5 = {
|
|
3721
3748
|
readBalance: makeFunctionReference("account/actions:readBalance"),
|
|
3722
3749
|
faucetMint: makeFunctionReference("account/actions:faucetMint")
|
|
3723
3750
|
};
|
|
@@ -3726,7 +3753,7 @@ var ConvexAccountAdapter = class {
|
|
|
3726
3753
|
#fns;
|
|
3727
3754
|
constructor(deps) {
|
|
3728
3755
|
this.#convex = deps.convex;
|
|
3729
|
-
this.#fns = deps.functions ?? DEFAULT_FUNCTIONS$
|
|
3756
|
+
this.#fns = deps.functions ?? DEFAULT_FUNCTIONS$5;
|
|
3730
3757
|
}
|
|
3731
3758
|
readBalance(input) {
|
|
3732
3759
|
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 +3806,7 @@ function subAccountErrorFromCapxul(operation, error, cause = error) {
|
|
|
3779
3806
|
var SubAccountPortTag = class extends Context.Tag("@capxul/sdk/ports/SubAccountPort")() {};
|
|
3780
3807
|
//#endregion
|
|
3781
3808
|
//#region src/adapters/sub-account/ConvexSubAccountAdapter.ts
|
|
3782
|
-
const DEFAULT_FUNCTIONS$
|
|
3809
|
+
const DEFAULT_FUNCTIONS$4 = {
|
|
3783
3810
|
create: makeFunctionReference("subAccount/mutations:create"),
|
|
3784
3811
|
get: makeFunctionReference("subAccount/queries:get"),
|
|
3785
3812
|
list: makeFunctionReference("subAccount/queries:list"),
|
|
@@ -3794,7 +3821,7 @@ var ConvexSubAccountAdapter = class {
|
|
|
3794
3821
|
constructor(deps) {
|
|
3795
3822
|
this.#convex = deps.convex;
|
|
3796
3823
|
this.#chainId = deps.chainId;
|
|
3797
|
-
this.#fns = deps.functions ?? DEFAULT_FUNCTIONS$
|
|
3824
|
+
this.#fns = deps.functions ?? DEFAULT_FUNCTIONS$4;
|
|
3798
3825
|
}
|
|
3799
3826
|
create(input) {
|
|
3800
3827
|
return this.#runMutation("create", this.#fns.create, {
|
|
@@ -3878,7 +3905,7 @@ function smartAccountErrorFromCapxul(operation, error, cause = error) {
|
|
|
3878
3905
|
var SmartAccountPortTag = class extends Context.Tag("@capxul/sdk/ports/SmartAccountPort")() {};
|
|
3879
3906
|
//#endregion
|
|
3880
3907
|
//#region src/adapters/smart-account/ConvexSmartAccountAdapter.ts
|
|
3881
|
-
const DEFAULT_FUNCTIONS$
|
|
3908
|
+
const DEFAULT_FUNCTIONS$3 = {
|
|
3882
3909
|
loadByAuthUserId: makeFunctionReference("smartAccount/queries:loadByAuthUserId"),
|
|
3883
3910
|
loadBySmartAccountAddress: makeFunctionReference("smartAccount/queries:loadBySmartAccountAddress"),
|
|
3884
3911
|
provision: makeFunctionReference("smartAccount/mutations:provision"),
|
|
@@ -3890,7 +3917,7 @@ var ConvexSmartAccountAdapter = class {
|
|
|
3890
3917
|
#fns;
|
|
3891
3918
|
constructor(deps) {
|
|
3892
3919
|
this.#convex = deps.convex;
|
|
3893
|
-
this.#fns = deps.functions ?? DEFAULT_FUNCTIONS$
|
|
3920
|
+
this.#fns = deps.functions ?? DEFAULT_FUNCTIONS$3;
|
|
3894
3921
|
}
|
|
3895
3922
|
loadByAuthUserId(authUserId) {
|
|
3896
3923
|
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 +3992,424 @@ function smartAccountErrorFromUnknown(operation, cause) {
|
|
|
3965
3992
|
toAddress("0xa6b71e26c5e0845f74c812102ca7114b6a896ab2");
|
|
3966
3993
|
keccak256("0x");
|
|
3967
3994
|
//#endregion
|
|
3995
|
+
//#region src/ports/org.ts
|
|
3996
|
+
var OrgError = class extends Data.TaggedError("OrgError") {};
|
|
3997
|
+
function orgErrorFromCapxul(operation, error, cause = error) {
|
|
3998
|
+
return new OrgError({
|
|
3999
|
+
operation,
|
|
4000
|
+
publicCode: error.code,
|
|
4001
|
+
publicError: error,
|
|
4002
|
+
cause,
|
|
4003
|
+
...error.details === void 0 ? {} : { details: error.details }
|
|
4004
|
+
});
|
|
4005
|
+
}
|
|
4006
|
+
Context.Tag("@capxul/sdk/ports/OrgPort")();
|
|
4007
|
+
Context.Tag("@capxul/sdk/ports/OrgRolesDeploymentPort")();
|
|
4008
|
+
Context.Tag("@capxul/sdk/ports/OrgSpendPort")();
|
|
4009
|
+
//#endregion
|
|
4010
|
+
//#region src/adapters/org/parse.ts
|
|
4011
|
+
/**
|
|
4012
|
+
* Map a `WireOrg` + its (separately read) treasury `Account` into the branded
|
|
4013
|
+
* `OrgView`. The viewer role is projected by the authenticated backend read;
|
|
4014
|
+
* it must never be inferred from Organization ownership. Brands at the read
|
|
4015
|
+
* edge: `orgId`, `safeAddress`.
|
|
4016
|
+
*/
|
|
4017
|
+
function brandOrgView(wire, treasury, viewerRole) {
|
|
4018
|
+
return {
|
|
4019
|
+
id: toOrgId(wire.orgId),
|
|
4020
|
+
name: wire.name,
|
|
4021
|
+
handle: wire.slug,
|
|
4022
|
+
safeAddress: toAddress(wire.safeAddress.toLowerCase()),
|
|
4023
|
+
role: viewerRole,
|
|
4024
|
+
treasury
|
|
4025
|
+
};
|
|
4026
|
+
}
|
|
4027
|
+
/**
|
|
4028
|
+
* Build the Org treasury `Account` from the raw on-chain `balanceOf` integer
|
|
4029
|
+
* (the D3 RPC read). `available === balance` for a treasury with no envelope
|
|
4030
|
+
* partition yet (a fresh Org reads back $0).
|
|
4031
|
+
*/
|
|
4032
|
+
function brandOrgTreasury(input) {
|
|
4033
|
+
return {
|
|
4034
|
+
id: toAccountId(`account_${orgIdBody(input.orgId)}`),
|
|
4035
|
+
balance: input.money,
|
|
4036
|
+
available: input.money
|
|
4037
|
+
};
|
|
4038
|
+
}
|
|
4039
|
+
function brandOrgRole(wire) {
|
|
4040
|
+
return {
|
|
4041
|
+
orgId: toOrgId(wire.orgId),
|
|
4042
|
+
label: wire.label,
|
|
4043
|
+
roleKey: toRoleKey(wire.roleKey),
|
|
4044
|
+
definition: parseRoleDefinition(wire.definitionJson)
|
|
4045
|
+
};
|
|
4046
|
+
}
|
|
4047
|
+
function brandOrgMember(wire) {
|
|
4048
|
+
return {
|
|
4049
|
+
orgId: toOrgId(wire.orgId),
|
|
4050
|
+
email: toEmail(wire.email),
|
|
4051
|
+
name: wire.name,
|
|
4052
|
+
personalSafeAddress: wire.personalSafeAddress === null ? null : toAddress(wire.personalSafeAddress),
|
|
4053
|
+
role: wire.role,
|
|
4054
|
+
roleKey: wire.roleKey === null ? null : toRoleKey(wire.roleKey),
|
|
4055
|
+
status: wire.status,
|
|
4056
|
+
grantTxHash: wire.grantTxHash,
|
|
4057
|
+
revokeTxHash: wire.revokeTxHash
|
|
4058
|
+
};
|
|
4059
|
+
}
|
|
4060
|
+
function parseRoleDefinition(json) {
|
|
4061
|
+
let raw;
|
|
4062
|
+
try {
|
|
4063
|
+
const parsed = JSON.parse(json);
|
|
4064
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) throw new Error("expected object");
|
|
4065
|
+
raw = parsed;
|
|
4066
|
+
} catch (err) {
|
|
4067
|
+
throw new Error(`Invalid role definition: malformed JSON - ${err instanceof Error ? err.message : String(err)}`, { cause: err });
|
|
4068
|
+
}
|
|
4069
|
+
if (typeof raw.label !== "string" || raw.label.trim().length === 0) throw new Error("Invalid role definition: missing label");
|
|
4070
|
+
return {
|
|
4071
|
+
label: raw.label,
|
|
4072
|
+
...raw.spend === void 0 ? {} : { spend: {
|
|
4073
|
+
...raw.spend.perTx === void 0 ? {} : { perTx: parseRoleMoney(raw.spend.perTx, "perTx") },
|
|
4074
|
+
...raw.spend.perDay === void 0 ? {} : { perDay: parseRoleMoney(raw.spend.perDay, "perDay") },
|
|
4075
|
+
...raw.spend.toRecipients === void 0 ? {} : { toRecipients: parseRoleRecipients(raw.spend.toRecipients) }
|
|
4076
|
+
} },
|
|
4077
|
+
...raw.subAccounts === void 0 ? {} : { subAccounts: parseRoleSubAccounts(raw.subAccounts) },
|
|
4078
|
+
...typeof raw.canManageMembers === "boolean" ? { canManageMembers: raw.canManageMembers } : {},
|
|
4079
|
+
...typeof raw.canManageRoles === "boolean" ? { canManageRoles: raw.canManageRoles } : {}
|
|
4080
|
+
};
|
|
4081
|
+
}
|
|
4082
|
+
function parseRoleMoney(raw, field) {
|
|
4083
|
+
if (typeof raw.currency !== "string" || typeof raw.value !== "string" || !/^\d+$/.test(raw.value) || raw.decimals !== 6) throw new Error(`Invalid role money: ${field}`);
|
|
4084
|
+
return {
|
|
4085
|
+
currency: toCurrencyCode(raw.currency),
|
|
4086
|
+
value: raw.value,
|
|
4087
|
+
decimals: raw.decimals
|
|
4088
|
+
};
|
|
4089
|
+
}
|
|
4090
|
+
function parseRoleRecipients(raw) {
|
|
4091
|
+
if (raw === "anyone") return "anyone";
|
|
4092
|
+
if (!Array.isArray(raw)) throw new Error("Invalid role definition: toRecipients must be \"anyone\" or an array");
|
|
4093
|
+
return raw.map((recipient) => {
|
|
4094
|
+
if (typeof recipient !== "string") throw new Error("Invalid role definition: toRecipients must be \"anyone\" or an array");
|
|
4095
|
+
return toAddress(recipient);
|
|
4096
|
+
});
|
|
4097
|
+
}
|
|
4098
|
+
function parseRoleSubAccounts(raw) {
|
|
4099
|
+
if (raw.scope === "all") return { scope: "all" };
|
|
4100
|
+
if (!Array.isArray(raw.scope)) throw new Error("Invalid role definition: subAccounts.scope must be \"all\" or an array");
|
|
4101
|
+
return { scope: raw.scope.map((subAccountId) => {
|
|
4102
|
+
if (typeof subAccountId !== "string") throw new Error("Invalid role definition: subAccounts.scope must be \"all\" or an array");
|
|
4103
|
+
return toSubAccountId(subAccountId);
|
|
4104
|
+
}) };
|
|
4105
|
+
}
|
|
4106
|
+
/** Strip the `org_` prefix + non-alphanumerics so the body re-seeds `account_`. */
|
|
4107
|
+
function orgIdBody(orgId) {
|
|
4108
|
+
const underscore = orgId.indexOf("_");
|
|
4109
|
+
const cleaned = (underscore < 0 ? orgId : orgId.slice(underscore + 1)).replace(/[^0-9A-Za-z]/g, "");
|
|
4110
|
+
return cleaned.length > 0 ? cleaned : "0";
|
|
4111
|
+
}
|
|
4112
|
+
//#endregion
|
|
4113
|
+
//#region src/adapters/org/ConvexOrganizationAdapter.ts
|
|
4114
|
+
const DEFAULT_FUNCTIONS$2 = {
|
|
4115
|
+
listAll: makeFunctionReference("org/queries:listAll"),
|
|
4116
|
+
listRoles: makeFunctionReference("org/queries:listRolesByOrgId"),
|
|
4117
|
+
listMembers: makeFunctionReference("org/queries:listMembersByOrgId"),
|
|
4118
|
+
readTreasury: makeFunctionReference("org/actions:readTreasury"),
|
|
4119
|
+
inviteMember: makeFunctionReference("org/actions:inviteMember"),
|
|
4120
|
+
resendInvite: makeFunctionReference("org/mutations:resendInviteToken"),
|
|
4121
|
+
detectInvitations: makeFunctionReference("org/actions:detectAndAcceptPendingInvitations")
|
|
4122
|
+
};
|
|
4123
|
+
/**
|
|
4124
|
+
* Standard production Organization read adapter. It intentionally has no
|
|
4125
|
+
* deployer/RPC/test configuration: authenticated Convex actions own live chain
|
|
4126
|
+
* reads, while the lifecycle adapter owns the single sponsored bootstrap.
|
|
4127
|
+
*/
|
|
4128
|
+
var ConvexOrganizationAdapter = class {
|
|
4129
|
+
#convex;
|
|
4130
|
+
#fns;
|
|
4131
|
+
constructor(input) {
|
|
4132
|
+
this.#convex = input.convex;
|
|
4133
|
+
this.#fns = input.functions ?? DEFAULT_FUNCTIONS$2;
|
|
4134
|
+
}
|
|
4135
|
+
createOrg(_input) {
|
|
4136
|
+
return Effect.fail(orgErrorFromCapxul("createOrg", Errors.notImplemented("organizationSetup", "use onboarding.completeOrganization")));
|
|
4137
|
+
}
|
|
4138
|
+
listOrgs(_input) {
|
|
4139
|
+
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) => {
|
|
4140
|
+
const viewerRole = wire.viewerRole.trim();
|
|
4141
|
+
if (viewerRole.length === 0) throw Errors.wrongState({
|
|
4142
|
+
method: "listOrgs",
|
|
4143
|
+
currentState: "viewerRoleMissing",
|
|
4144
|
+
validStates: ["activeViewerRole"]
|
|
4145
|
+
});
|
|
4146
|
+
return brandOrgView(wire, treasury, viewerRole);
|
|
4147
|
+
})))), Effect.catchAllDefect((cause) => Effect.fail(toOrgError("listOrgs", cause))));
|
|
4148
|
+
}
|
|
4149
|
+
readTreasury(input) {
|
|
4150
|
+
return this.#readTreasuryWire(String(input.orgId)).pipe(Effect.catchAllDefect((cause) => Effect.fail(toOrgError("readTreasury", cause))));
|
|
4151
|
+
}
|
|
4152
|
+
listRoles(input) {
|
|
4153
|
+
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))));
|
|
4154
|
+
}
|
|
4155
|
+
listMembers(input) {
|
|
4156
|
+
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))));
|
|
4157
|
+
}
|
|
4158
|
+
pendingMembers(input) {
|
|
4159
|
+
return this.listMembers(input).pipe(Effect.map((members) => members.filter((member) => member.status === "pending" || member.status === "pending_safe" || member.status === "pending_grant")));
|
|
4160
|
+
}
|
|
4161
|
+
inviteMember(input) {
|
|
4162
|
+
return this.#convex.action(this.#fns.inviteMember, {
|
|
4163
|
+
orgId: input.orgId,
|
|
4164
|
+
email: input.input.email,
|
|
4165
|
+
role: input.input.role
|
|
4166
|
+
}).pipe(Effect.mapError((error) => orgErrorFromCapxul("inviteMember", error.publicError, error)), Effect.map(brandOrgMember), Effect.catchAllDefect((cause) => Effect.fail(toOrgError("inviteMember", cause))));
|
|
4167
|
+
}
|
|
4168
|
+
resendInviteToken(input) {
|
|
4169
|
+
return this.#convex.mutation(this.#fns.resendInvite, {
|
|
4170
|
+
orgId: input.orgId,
|
|
4171
|
+
email: input.email
|
|
4172
|
+
}).pipe(Effect.mapError((error) => orgErrorFromCapxul("resendInviteToken", error.publicError, error)), Effect.map(brandOrgMember), Effect.catchAllDefect((cause) => Effect.fail(toOrgError("resendInviteToken", cause))));
|
|
4173
|
+
}
|
|
4174
|
+
detectAndAcceptPendingInvitations(_input) {
|
|
4175
|
+
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))));
|
|
4176
|
+
}
|
|
4177
|
+
#readTreasuryWire(orgId) {
|
|
4178
|
+
return this.#convex.action(this.#fns.readTreasury, { orgId }).pipe(Effect.mapError((error) => orgErrorFromCapxul("readTreasury", error.publicError, error)), Effect.map((wire) => {
|
|
4179
|
+
if (wire.orgId !== orgId) throw Errors.invalidInput("orgId", "Organization treasury scope does not match");
|
|
4180
|
+
const balance = fromWei(wire.rawBalance, wire.decimals, wire.currency);
|
|
4181
|
+
const available = fromWei(wire.rawAvailableBalance, wire.decimals, wire.currency);
|
|
4182
|
+
return {
|
|
4183
|
+
...brandOrgTreasury({
|
|
4184
|
+
orgId: wire.orgId,
|
|
4185
|
+
money: balance
|
|
4186
|
+
}),
|
|
4187
|
+
available
|
|
4188
|
+
};
|
|
4189
|
+
}));
|
|
4190
|
+
}
|
|
4191
|
+
};
|
|
4192
|
+
function toOrgError(operation, cause) {
|
|
4193
|
+
if (cause instanceof CapxulError) return orgErrorFromCapxul(operation, cause);
|
|
4194
|
+
return orgErrorFromCapxul(operation, Errors.providerError("convex", operation, cause), cause);
|
|
4195
|
+
}
|
|
4196
|
+
function partialOrgTruth(operation, currentState) {
|
|
4197
|
+
return orgErrorFromCapxul(operation, Errors.wrongState({
|
|
4198
|
+
method: operation,
|
|
4199
|
+
currentState,
|
|
4200
|
+
validStates: ["completeProductionOrganizationTruth"]
|
|
4201
|
+
}));
|
|
4202
|
+
}
|
|
4203
|
+
//#endregion
|
|
4204
|
+
//#region src/adapters/org/ConvexOrganizationSetupAdapter.ts
|
|
4205
|
+
const DEFAULT_FUNCTIONS$1 = {
|
|
4206
|
+
startOrResume: makeFunctionReference("org/lifecycle:startOrResume"),
|
|
4207
|
+
prepareFounderAccount: makeFunctionReference("org/actions:prepareFounderAccount"),
|
|
4208
|
+
prepareBootstrap: makeFunctionReference("org/actions:prepareBootstrap"),
|
|
4209
|
+
submitBootstrap: makeFunctionReference("org/actions:submitBootstrap"),
|
|
4210
|
+
confirmBootstrap: makeFunctionReference("org/actions:confirmBootstrap"),
|
|
4211
|
+
recordFailure: makeFunctionReference("org/lifecycle:recordFailure"),
|
|
4212
|
+
load: makeFunctionReference("org/lifecycle:load"),
|
|
4213
|
+
retry: makeFunctionReference("org/lifecycle:retry")
|
|
4214
|
+
};
|
|
4215
|
+
/** Durable Organization setup capability composed by the standard client. */
|
|
4216
|
+
var ConvexOrganizationSetupAdapter = class {
|
|
4217
|
+
#convex;
|
|
4218
|
+
#signer;
|
|
4219
|
+
#fns;
|
|
4220
|
+
constructor(input) {
|
|
4221
|
+
this.#convex = input.convex;
|
|
4222
|
+
this.#signer = input.signer;
|
|
4223
|
+
this.#fns = input.functions ?? DEFAULT_FUNCTIONS$1;
|
|
4224
|
+
}
|
|
4225
|
+
async startOrResume(input) {
|
|
4226
|
+
const result = await runCall("startOrResume", this.#convex.mutation(this.#fns.startOrResume, input));
|
|
4227
|
+
if (!result.ok) return result;
|
|
4228
|
+
const lifecycle = parseLifecycle("startOrResume", result.value.lifecycle);
|
|
4229
|
+
if (!lifecycle.ok) return lifecycle;
|
|
4230
|
+
const orgId = parseOrgId("startOrResume", result.value.orgId);
|
|
4231
|
+
if (!orgId.ok) return orgId;
|
|
4232
|
+
if (String(orgId.value) !== String(lifecycle.value.orgId)) return fail$2(Errors.invalidInput("orgId", "Organization lifecycle scope does not match"));
|
|
4233
|
+
return {
|
|
4234
|
+
ok: true,
|
|
4235
|
+
value: {
|
|
4236
|
+
orgId: orgId.value,
|
|
4237
|
+
lifecycle: lifecycle.value
|
|
4238
|
+
}
|
|
4239
|
+
};
|
|
4240
|
+
}
|
|
4241
|
+
prepareFounderAccount(input) {
|
|
4242
|
+
return this.#lifecycleAction("prepareFounderAccount", input, () => this.#convex.action(this.#fns.prepareFounderAccount, { orgId: input.orgId }));
|
|
4243
|
+
}
|
|
4244
|
+
async authorizeAndSubmitBootstrap(input) {
|
|
4245
|
+
const cancelled = cancellation(input.signal);
|
|
4246
|
+
if (cancelled !== void 0) return cancelled;
|
|
4247
|
+
const signerAddress = await signerResult("getAddress", () => this.#signer.getAddress());
|
|
4248
|
+
if (!signerAddress.ok) return signerAddress;
|
|
4249
|
+
const prepared = await runCall("prepareBootstrap", this.#convex.action(this.#fns.prepareBootstrap, {
|
|
4250
|
+
orgId: input.orgId,
|
|
4251
|
+
signerAddress: signerAddress.value
|
|
4252
|
+
}));
|
|
4253
|
+
if (!prepared.ok) return prepared;
|
|
4254
|
+
const authority = validatePreparedAuthorities(prepared.value, signerAddress.value);
|
|
4255
|
+
if (!authority.ok) return authority;
|
|
4256
|
+
const cancelledAfterPrepare = cancellation(input.signal);
|
|
4257
|
+
if (cancelledAfterPrepare !== void 0) return cancelledAfterPrepare;
|
|
4258
|
+
const signature = await signerResult("signUserOpHash", () => this.#signer.signUserOpHash(prepared.value.digest));
|
|
4259
|
+
if (!signature.ok) return signature;
|
|
4260
|
+
const cancelledAfterSign = cancellation(input.signal);
|
|
4261
|
+
if (cancelledAfterSign !== void 0) return cancelledAfterSign;
|
|
4262
|
+
const submitted = await runCall("submitBootstrap", this.#convex.action(this.#fns.submitBootstrap, {
|
|
4263
|
+
orgId: input.orgId,
|
|
4264
|
+
signerAddress: signerAddress.value,
|
|
4265
|
+
signature: signature.value,
|
|
4266
|
+
userOp: prepared.value.userOp
|
|
4267
|
+
}));
|
|
4268
|
+
if (!submitted.ok) return submitted;
|
|
4269
|
+
return parseLifecycle("submitBootstrap", submitted.value);
|
|
4270
|
+
}
|
|
4271
|
+
confirmSubmittedBootstrap(input) {
|
|
4272
|
+
return this.#lifecycleAction("confirmBootstrap", input, () => this.#convex.action(this.#fns.confirmBootstrap, { orgId: input.orgId }));
|
|
4273
|
+
}
|
|
4274
|
+
async recordFailure(input) {
|
|
4275
|
+
const result = await runCall("recordFailure", this.#convex.mutation(this.#fns.recordFailure, {
|
|
4276
|
+
orgId: input.orgId,
|
|
4277
|
+
at: input.at,
|
|
4278
|
+
errorCode: input.error.code,
|
|
4279
|
+
retryable: input.retryable
|
|
4280
|
+
}));
|
|
4281
|
+
return result.ok ? parseLifecycle("recordFailure", result.value) : result;
|
|
4282
|
+
}
|
|
4283
|
+
async loadLifecycle(input) {
|
|
4284
|
+
const result = await runCall("loadLifecycle", this.#convex.query(this.#fns.load, input));
|
|
4285
|
+
if (!result.ok) return result;
|
|
4286
|
+
if (result.value === null) return fail$2(Errors.invalidInput("orgId", "Organization lifecycle was not found"));
|
|
4287
|
+
return parseLifecycle("loadLifecycle", result.value);
|
|
4288
|
+
}
|
|
4289
|
+
async retry(input) {
|
|
4290
|
+
const cancelled = cancellation(input.signal);
|
|
4291
|
+
if (cancelled !== void 0) return cancelled;
|
|
4292
|
+
const result = await runCall("retry", this.#convex.mutation(this.#fns.retry, { orgId: input.orgId }));
|
|
4293
|
+
return result.ok ? parseLifecycle("retry", result.value) : result;
|
|
4294
|
+
}
|
|
4295
|
+
async #lifecycleAction(operation, input, call) {
|
|
4296
|
+
const cancelled = cancellation(input.signal);
|
|
4297
|
+
if (cancelled !== void 0) return cancelled;
|
|
4298
|
+
const result = await runCall(operation, call());
|
|
4299
|
+
if (!result.ok) return result;
|
|
4300
|
+
const cancelledAfter = cancellation(input.signal);
|
|
4301
|
+
if (cancelledAfter !== void 0) return cancelledAfter;
|
|
4302
|
+
return parseLifecycle(operation, result.value);
|
|
4303
|
+
}
|
|
4304
|
+
};
|
|
4305
|
+
async function runCall(operation, effect) {
|
|
4306
|
+
try {
|
|
4307
|
+
const result = await Effect.runPromise(Effect.either(effect));
|
|
4308
|
+
return Either.isRight(result) ? {
|
|
4309
|
+
ok: true,
|
|
4310
|
+
value: result.right
|
|
4311
|
+
} : fail$2(publicError(operation, result.left));
|
|
4312
|
+
} catch (cause) {
|
|
4313
|
+
return fail$2(publicError(operation, cause));
|
|
4314
|
+
}
|
|
4315
|
+
}
|
|
4316
|
+
function publicError(operation, cause) {
|
|
4317
|
+
if (cause instanceof CapxulError) return cause;
|
|
4318
|
+
if (typeof cause === "object" && cause !== null) {
|
|
4319
|
+
const carried = cause.publicError;
|
|
4320
|
+
if (carried instanceof CapxulError) return carried;
|
|
4321
|
+
}
|
|
4322
|
+
return Errors.providerError("convex-organization", operation, cause);
|
|
4323
|
+
}
|
|
4324
|
+
async function signerResult(operation, run) {
|
|
4325
|
+
try {
|
|
4326
|
+
return {
|
|
4327
|
+
ok: true,
|
|
4328
|
+
value: await run()
|
|
4329
|
+
};
|
|
4330
|
+
} catch (cause) {
|
|
4331
|
+
return fail$2(Errors.providerError("organization-signer", operation, cause));
|
|
4332
|
+
}
|
|
4333
|
+
}
|
|
4334
|
+
function validatePreparedAuthorities(prepared, signerAddress) {
|
|
4335
|
+
const signer = signerAddress.toLowerCase();
|
|
4336
|
+
const preparedSigner = prepared.signerAddress.toLowerCase();
|
|
4337
|
+
const founder = prepared.founderPersonalAccount.toLowerCase();
|
|
4338
|
+
const organization = prepared.organizationAccountAddress.toLowerCase();
|
|
4339
|
+
const sender = prepared.userOp.sender.toLowerCase();
|
|
4340
|
+
if (preparedSigner !== signer) return fail$2(Errors.invalidInput("signerAddress", "Prepared signer does not match configured signer"));
|
|
4341
|
+
if (founder === signer || organization === signer || organization === founder) return fail$2(Errors.invalidInput("organizationAuthority", "Signer EOA, founder Account, and Organization Account must be distinct"));
|
|
4342
|
+
if (sender !== founder) return fail$2(Errors.invalidInput("userOp.sender", "Bootstrap sender must be the founder Account"));
|
|
4343
|
+
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"));
|
|
4344
|
+
return {
|
|
4345
|
+
ok: true,
|
|
4346
|
+
value: void 0
|
|
4347
|
+
};
|
|
4348
|
+
}
|
|
4349
|
+
function parseLifecycle(operation, wire) {
|
|
4350
|
+
const orgId = parseOrgId(operation, wire.orgId);
|
|
4351
|
+
if (!orgId.ok) return orgId;
|
|
4352
|
+
if (wire.status === "loading") return {
|
|
4353
|
+
ok: true,
|
|
4354
|
+
value: {
|
|
4355
|
+
status: "loading",
|
|
4356
|
+
orgId: orgId.value
|
|
4357
|
+
}
|
|
4358
|
+
};
|
|
4359
|
+
if (wire.status === "ready") return {
|
|
4360
|
+
ok: true,
|
|
4361
|
+
value: {
|
|
4362
|
+
status: "ready",
|
|
4363
|
+
orgId: orgId.value,
|
|
4364
|
+
canTransact: true
|
|
4365
|
+
}
|
|
4366
|
+
};
|
|
4367
|
+
if (wire.status === "failed") {
|
|
4368
|
+
if (!isSetupStep(wire.at)) return fail$2(Errors.invalidInput("lifecycle.at", "Unknown setup step"));
|
|
4369
|
+
return {
|
|
4370
|
+
ok: true,
|
|
4371
|
+
value: {
|
|
4372
|
+
status: "failed",
|
|
4373
|
+
orgId: orgId.value,
|
|
4374
|
+
at: wire.at,
|
|
4375
|
+
error: new CapxulError(wire.error.code, wire.error.message),
|
|
4376
|
+
retryable: wire.retryable
|
|
4377
|
+
}
|
|
4378
|
+
};
|
|
4379
|
+
}
|
|
4380
|
+
if (!isSetupStep(wire.step)) return fail$2(Errors.invalidInput("lifecycle.step", `Unknown setup step from ${operation}`));
|
|
4381
|
+
return {
|
|
4382
|
+
ok: true,
|
|
4383
|
+
value: {
|
|
4384
|
+
status: "settingUp",
|
|
4385
|
+
orgId: orgId.value,
|
|
4386
|
+
step: wire.step
|
|
4387
|
+
}
|
|
4388
|
+
};
|
|
4389
|
+
}
|
|
4390
|
+
function parseOrgId(operation, value) {
|
|
4391
|
+
try {
|
|
4392
|
+
return {
|
|
4393
|
+
ok: true,
|
|
4394
|
+
value: toOrgId(value)
|
|
4395
|
+
};
|
|
4396
|
+
} catch (cause) {
|
|
4397
|
+
return fail$2(Errors.providerError("convex-organization", operation, cause));
|
|
4398
|
+
}
|
|
4399
|
+
}
|
|
4400
|
+
function isSetupStep(value) {
|
|
4401
|
+
return value === "preparingFounderAccount" || value === "awaitingFounderAuthorization" || value === "submittingBootstrap" || value === "confirmingBootstrap";
|
|
4402
|
+
}
|
|
4403
|
+
function cancellation(signal) {
|
|
4404
|
+
return signal?.aborted ? fail$2(Errors.cancelled({ operation: "organization.setup" })) : void 0;
|
|
4405
|
+
}
|
|
4406
|
+
function fail$2(error) {
|
|
4407
|
+
return {
|
|
4408
|
+
ok: false,
|
|
4409
|
+
error
|
|
4410
|
+
};
|
|
4411
|
+
}
|
|
4412
|
+
//#endregion
|
|
3968
4413
|
//#region ../observability/src/index.ts
|
|
3969
4414
|
const PII = Schema.String.pipe(Schema.brand("PII"));
|
|
3970
4415
|
const OptionalString = Schema.optional(Schema.String);
|
|
@@ -7451,6 +7896,76 @@ function listOrgsProgram() {
|
|
|
7451
7896
|
});
|
|
7452
7897
|
}
|
|
7453
7898
|
//#endregion
|
|
7899
|
+
//#region src/client/org-lifecycle.ts
|
|
7900
|
+
function fail$1(error) {
|
|
7901
|
+
return {
|
|
7902
|
+
ok: false,
|
|
7903
|
+
error
|
|
7904
|
+
};
|
|
7905
|
+
}
|
|
7906
|
+
function validateOrganizationLifecycleScope(orgId, lifecycle) {
|
|
7907
|
+
if (String(lifecycle.orgId) !== String(orgId)) return fail$1(Errors.invalidInput("orgId", "Organization lifecycle scope does not match"));
|
|
7908
|
+
return {
|
|
7909
|
+
ok: true,
|
|
7910
|
+
value: lifecycle
|
|
7911
|
+
};
|
|
7912
|
+
}
|
|
7913
|
+
/** Advance only the steps still required by one durable Organization lane. */
|
|
7914
|
+
async function advanceOrganizationSetup(setup, orgId, initial, signal) {
|
|
7915
|
+
const scopedInitial = validateOrganizationLifecycleScope(orgId, initial);
|
|
7916
|
+
if (!scopedInitial.ok) return scopedInitial;
|
|
7917
|
+
let lifecycle = scopedInitial.value;
|
|
7918
|
+
for (let transition = 0; transition < 3; transition += 1) {
|
|
7919
|
+
if (lifecycle.status !== "settingUp") return {
|
|
7920
|
+
ok: true,
|
|
7921
|
+
value: lifecycle
|
|
7922
|
+
};
|
|
7923
|
+
const currentStep = lifecycle.step;
|
|
7924
|
+
if (signal?.aborted) {
|
|
7925
|
+
const cancelled = Errors.cancelled({ operation: "organization.setup" });
|
|
7926
|
+
return recordRetryableCancellation(setup, orgId, lifecycle.step, cancelled);
|
|
7927
|
+
}
|
|
7928
|
+
const stepInput = {
|
|
7929
|
+
orgId,
|
|
7930
|
+
...signal === void 0 ? {} : { signal }
|
|
7931
|
+
};
|
|
7932
|
+
const next = lifecycle.step === "preparingFounderAccount" ? await setup.prepareFounderAccount(stepInput) : lifecycle.step === "awaitingFounderAuthorization" || lifecycle.step === "submittingBootstrap" ? await setup.authorizeAndSubmitBootstrap(stepInput) : await setup.confirmSubmittedBootstrap(stepInput);
|
|
7933
|
+
if (!next.ok) return next.error.code === "CANCELLED" ? recordRetryableCancellation(setup, orgId, lifecycle.step, next.error) : next;
|
|
7934
|
+
const scopedNext = validateOrganizationLifecycleScope(orgId, next.value);
|
|
7935
|
+
if (!scopedNext.ok) return scopedNext;
|
|
7936
|
+
lifecycle = scopedNext.value;
|
|
7937
|
+
if (currentStep === "confirmingBootstrap") return {
|
|
7938
|
+
ok: true,
|
|
7939
|
+
value: lifecycle
|
|
7940
|
+
};
|
|
7941
|
+
}
|
|
7942
|
+
if (lifecycle.status !== "settingUp" || lifecycle.step === "confirmingBootstrap") return {
|
|
7943
|
+
ok: true,
|
|
7944
|
+
value: lifecycle
|
|
7945
|
+
};
|
|
7946
|
+
return fail$1(Errors.wrongState({
|
|
7947
|
+
method: "organization.setup",
|
|
7948
|
+
currentState: lifecycle.step,
|
|
7949
|
+
validStates: [
|
|
7950
|
+
"confirmingBootstrap",
|
|
7951
|
+
"ready",
|
|
7952
|
+
"failed"
|
|
7953
|
+
]
|
|
7954
|
+
}));
|
|
7955
|
+
}
|
|
7956
|
+
async function recordRetryableCancellation(setup, orgId, at, cancelled) {
|
|
7957
|
+
const recorded = await setup.recordFailure({
|
|
7958
|
+
orgId,
|
|
7959
|
+
at,
|
|
7960
|
+
error: cancelled,
|
|
7961
|
+
retryable: true
|
|
7962
|
+
});
|
|
7963
|
+
if (!recorded.ok) return recorded;
|
|
7964
|
+
const scopedRecorded = validateOrganizationLifecycleScope(orgId, recorded.value);
|
|
7965
|
+
if (!scopedRecorded.ok) return scopedRecorded;
|
|
7966
|
+
return fail$1(cancelled);
|
|
7967
|
+
}
|
|
7968
|
+
//#endregion
|
|
7454
7969
|
//#region src/client/org.ts
|
|
7455
7970
|
/**
|
|
7456
7971
|
* Org method surface (canon §C2/§C3, D13). S1 (#274) wires `createOrg` /
|
|
@@ -7499,6 +8014,36 @@ function makeOrgMethods(deps) {
|
|
|
7499
8014
|
},
|
|
7500
8015
|
...convexCall === void 0 ? {} : { convexCall }
|
|
7501
8016
|
}),
|
|
8017
|
+
async getLifecycle(options) {
|
|
8018
|
+
if (options?.signal?.aborted) return {
|
|
8019
|
+
ok: false,
|
|
8020
|
+
error: Errors.cancelled({ operation: "org.getLifecycle" })
|
|
8021
|
+
};
|
|
8022
|
+
if (deps.organizationSetup === void 0) return {
|
|
8023
|
+
ok: false,
|
|
8024
|
+
error: Errors.notImplemented("organizationSetup", "getLifecycle")
|
|
8025
|
+
};
|
|
8026
|
+
const loaded = await deps.organizationSetup.loadLifecycle({ orgId });
|
|
8027
|
+
if (!loaded.ok) return loaded;
|
|
8028
|
+
return validateOrganizationLifecycleScope(orgId, loaded.value);
|
|
8029
|
+
},
|
|
8030
|
+
async retrySetup(options) {
|
|
8031
|
+
if (options?.signal?.aborted) return {
|
|
8032
|
+
ok: false,
|
|
8033
|
+
error: Errors.cancelled({ operation: "org.retrySetup" })
|
|
8034
|
+
};
|
|
8035
|
+
const setup = deps.organizationSetup;
|
|
8036
|
+
if (setup === void 0) return {
|
|
8037
|
+
ok: false,
|
|
8038
|
+
error: Errors.notImplemented("organizationSetup", "retrySetup")
|
|
8039
|
+
};
|
|
8040
|
+
const retried = await setup.retry({
|
|
8041
|
+
orgId,
|
|
8042
|
+
...options?.signal === void 0 ? {} : { signal: options.signal }
|
|
8043
|
+
});
|
|
8044
|
+
if (!retried.ok) return retried;
|
|
8045
|
+
return advanceOrganizationSetup(setup, orgId, retried.value, options?.signal);
|
|
8046
|
+
},
|
|
7502
8047
|
profile: {
|
|
7503
8048
|
get(_options) {
|
|
7504
8049
|
if (_options?.signal?.aborted) return Promise.resolve({
|
|
@@ -7532,12 +8077,12 @@ function makeOrgMethods(deps) {
|
|
|
7532
8077
|
ok: false,
|
|
7533
8078
|
error: treasury.error
|
|
7534
8079
|
};
|
|
7535
|
-
const
|
|
7536
|
-
if (!
|
|
8080
|
+
const orgList = await toCapxulResult(listOrgsProgram(), layer);
|
|
8081
|
+
if (!orgList.ok) return {
|
|
7537
8082
|
ok: false,
|
|
7538
|
-
error:
|
|
8083
|
+
error: orgList.error
|
|
7539
8084
|
};
|
|
7540
|
-
const org =
|
|
8085
|
+
const org = orgList.value.find((candidate) => candidate.id === orgId);
|
|
7541
8086
|
return {
|
|
7542
8087
|
ok: true,
|
|
7543
8088
|
value: {
|
|
@@ -7725,7 +8270,6 @@ function normalizePaymentTiming(payment) {
|
|
|
7725
8270
|
}
|
|
7726
8271
|
//#endregion
|
|
7727
8272
|
//#region src/flows/onboarding.ts
|
|
7728
|
-
const DEFAULT_ORG_TEMPLATE = "Startup";
|
|
7729
8273
|
async function runCompletePersonalOnboarding(ops, input) {
|
|
7730
8274
|
const validated = validatePersonalInput(input);
|
|
7731
8275
|
if (!validated.ok) return validated;
|
|
@@ -7749,11 +8293,13 @@ async function runCompletePersonalOnboarding(ops, input) {
|
|
|
7749
8293
|
value: { lifecycle: lifecycle.value }
|
|
7750
8294
|
};
|
|
7751
8295
|
}
|
|
7752
|
-
async function runCompleteOrganizationOnboarding(ops, input) {
|
|
8296
|
+
async function runCompleteOrganizationOnboarding(ops, input, options) {
|
|
7753
8297
|
const validated = validateOrganizationInput(input);
|
|
7754
8298
|
if (!validated.ok) return validated;
|
|
7755
8299
|
const session = await ops.currentSession();
|
|
7756
8300
|
if (session === null) return fail(Errors.notAuthenticated());
|
|
8301
|
+
const setup = ops.organizationSetup;
|
|
8302
|
+
if (setup === void 0) return fail(Errors.notImplemented("organizationSetup", "completeOrganization"));
|
|
7757
8303
|
const written = await ops.completeIdentityOnboarding({
|
|
7758
8304
|
authUserId: session.authUserId,
|
|
7759
8305
|
email: session.email,
|
|
@@ -7761,22 +8307,18 @@ async function runCompleteOrganizationOnboarding(ops, input) {
|
|
|
7761
8307
|
country: validated.value.country
|
|
7762
8308
|
});
|
|
7763
8309
|
if (!written.ok) return fail(written.error);
|
|
7764
|
-
const
|
|
7765
|
-
if (!provisioned.ok) return fail(provisioned.error);
|
|
7766
|
-
const org = await ops.createOrg({
|
|
8310
|
+
const started = await setup.startOrResume({
|
|
7767
8311
|
name: validated.value.organizationName,
|
|
7768
8312
|
handle: validated.value.handle,
|
|
7769
|
-
template: DEFAULT_ORG_TEMPLATE,
|
|
7770
8313
|
country: validated.value.country
|
|
7771
8314
|
});
|
|
7772
|
-
if (!
|
|
7773
|
-
|
|
7774
|
-
|
|
7775
|
-
if (!lifecycle.ok) return fail(lifecycle.error);
|
|
8315
|
+
if (!started.ok) return fail(started.error);
|
|
8316
|
+
const lifecycle = await advanceOrganizationSetup(setup, started.value.orgId, started.value.lifecycle, options?.signal);
|
|
8317
|
+
if (!lifecycle.ok) return lifecycle;
|
|
7776
8318
|
return {
|
|
7777
8319
|
ok: true,
|
|
7778
8320
|
value: {
|
|
7779
|
-
|
|
8321
|
+
orgId: started.value.orgId,
|
|
7780
8322
|
lifecycle: lifecycle.value
|
|
7781
8323
|
}
|
|
7782
8324
|
};
|
|
@@ -7867,8 +8409,7 @@ function fail(error) {
|
|
|
7867
8409
|
* Onboarding method surface. The thin namespace over the `flows/onboarding.ts`
|
|
7868
8410
|
* choreography: it adds the `signal`-abort pre-check and delegates to the run
|
|
7869
8411
|
* functions. The `OnboardingOps` seam is supplied by `assembleCapxulClient`
|
|
7870
|
-
* (real ops
|
|
7871
|
-
* (in-memory double).
|
|
8412
|
+
* (real identity/Account ops plus Organization setup capability) or by tests.
|
|
7872
8413
|
*/
|
|
7873
8414
|
function makeOnboardingMethods(ops) {
|
|
7874
8415
|
return {
|
|
@@ -7884,7 +8425,7 @@ function makeOnboardingMethods(ops) {
|
|
|
7884
8425
|
ok: false,
|
|
7885
8426
|
error: Errors.cancelled({ operation: "onboarding.completeOrganization" })
|
|
7886
8427
|
});
|
|
7887
|
-
return runCompleteOrganizationOnboarding(ops, input);
|
|
8428
|
+
return runCompleteOrganizationOnboarding(ops, input, options);
|
|
7888
8429
|
}
|
|
7889
8430
|
};
|
|
7890
8431
|
}
|
|
@@ -7963,7 +8504,8 @@ function assembleCapxulClient(input) {
|
|
|
7963
8504
|
...selectedOrgPort === void 0 ? {} : { orgPort: selectedOrgPort },
|
|
7964
8505
|
...input.orgRolesDeploymentPort === void 0 ? {} : { orgRolesDeploymentPort: input.orgRolesDeploymentPort },
|
|
7965
8506
|
...input.orgSpendPort === void 0 ? {} : { orgSpendPort: input.orgSpendPort },
|
|
7966
|
-
convexCall: input.ports.convexCall
|
|
8507
|
+
convexCall: input.ports.convexCall,
|
|
8508
|
+
...input.organizationSetup === void 0 ? {} : { organizationSetup: input.organizationSetup }
|
|
7967
8509
|
});
|
|
7968
8510
|
if (selectedOrgPort !== void 0) detectPendingOrgInvitations = async () => {
|
|
7969
8511
|
await orgMethods.orgs.detectAndAcceptPendingInvitations();
|
|
@@ -7986,7 +8528,7 @@ function assembleCapxulClient(input) {
|
|
|
7986
8528
|
triggerProvisioning: () => account._internal.provision(),
|
|
7987
8529
|
kickProvisioning: () => kickProvisioning?.(),
|
|
7988
8530
|
readLifecycle: () => account.getLifecycle(),
|
|
7989
|
-
|
|
8531
|
+
...input.organizationSetup === void 0 ? {} : { organizationSetup: input.organizationSetup }
|
|
7990
8532
|
});
|
|
7991
8533
|
return {
|
|
7992
8534
|
auth,
|
|
@@ -8028,19 +8570,6 @@ function assembleCapxulClient(input) {
|
|
|
8028
8570
|
const SDK_VERSION$1 = version;
|
|
8029
8571
|
/** Stable PostHog event used for typed failures that are expected product outcomes. */
|
|
8030
8572
|
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
8573
|
/**
|
|
8045
8574
|
* Adapt the host's already-initialized PostHog-like client. This function does
|
|
8046
8575
|
* not import, initialize, configure, or own PostHog.
|
|
@@ -8077,7 +8606,7 @@ function fromPostHog(client, options = {}) {
|
|
|
8077
8606
|
return {
|
|
8078
8607
|
resolveContext: () => {
|
|
8079
8608
|
if (!isEnabled(options.enabled) || client === null || client === void 0) return void 0;
|
|
8080
|
-
return
|
|
8609
|
+
return resolveRawContext(options.context) ?? {};
|
|
8081
8610
|
},
|
|
8082
8611
|
captureOperationFailure,
|
|
8083
8612
|
captureException
|
|
@@ -8091,6 +8620,34 @@ function deliver(capture) {
|
|
|
8091
8620
|
ignoreDeliveryFailure(capture());
|
|
8092
8621
|
} catch {}
|
|
8093
8622
|
}
|
|
8623
|
+
/**
|
|
8624
|
+
* Fan one SDK failure out to several adapters (issue #877). The SDK's own
|
|
8625
|
+
* first-party relay and an optional host-provided adapter both observe the same
|
|
8626
|
+
* failure. Order matters for `resolveContext`: the FIRST adapter that returns a
|
|
8627
|
+
* context wins, so pass the host adapter first to keep its call-start snapshot.
|
|
8628
|
+
* Each capture is isolated — a throwing adapter never blocks its peers or the
|
|
8629
|
+
* SDK. Returns `undefined` when no adapters are present (zero observation work).
|
|
8630
|
+
*/
|
|
8631
|
+
function composeObservationAdapters(...adapters) {
|
|
8632
|
+
const present = adapters.filter((adapter) => adapter !== void 0);
|
|
8633
|
+
if (present.length === 0) return void 0;
|
|
8634
|
+
if (present.length === 1) return present[0];
|
|
8635
|
+
const fanOut = (select) => {
|
|
8636
|
+
for (const adapter of present) try {
|
|
8637
|
+
ignoreDeliveryFailure(select(adapter));
|
|
8638
|
+
} catch {}
|
|
8639
|
+
};
|
|
8640
|
+
return {
|
|
8641
|
+
resolveContext: () => {
|
|
8642
|
+
for (const adapter of present) try {
|
|
8643
|
+
const context = adapter.resolveContext?.();
|
|
8644
|
+
if (context !== void 0) return context;
|
|
8645
|
+
} catch {}
|
|
8646
|
+
},
|
|
8647
|
+
captureOperationFailure: (failure) => fanOut((adapter) => adapter.captureOperationFailure(failure)),
|
|
8648
|
+
captureException: (failure) => fanOut((adapter) => adapter.captureException(failure))
|
|
8649
|
+
};
|
|
8650
|
+
}
|
|
8094
8651
|
/** @internal Decorates public client method bundles at the owning SDK boundary. */
|
|
8095
8652
|
function observeSdkClient(client, adapter) {
|
|
8096
8653
|
if (adapter === void 0) return client;
|
|
@@ -8194,7 +8751,7 @@ function report(adapter, kind, operation, cause, invocationContext) {
|
|
|
8194
8751
|
}
|
|
8195
8752
|
function resolveAdapterContext(adapter) {
|
|
8196
8753
|
try {
|
|
8197
|
-
return
|
|
8754
|
+
return adapter.resolveContext?.();
|
|
8198
8755
|
} catch {
|
|
8199
8756
|
return;
|
|
8200
8757
|
}
|
|
@@ -8225,8 +8782,12 @@ function postHogProperties(failure, context) {
|
|
|
8225
8782
|
return properties;
|
|
8226
8783
|
}
|
|
8227
8784
|
function resolveContext(context) {
|
|
8785
|
+
return sanitizeObservationContext(resolveRawContext(context));
|
|
8786
|
+
}
|
|
8787
|
+
/** Resolve the host context closure without sanitizing — the call-start snapshot. */
|
|
8788
|
+
function resolveRawContext(context) {
|
|
8228
8789
|
try {
|
|
8229
|
-
return
|
|
8790
|
+
return typeof context === "function" ? context() : context;
|
|
8230
8791
|
} catch {
|
|
8231
8792
|
return;
|
|
8232
8793
|
}
|
|
@@ -8288,6 +8849,43 @@ function isCapxulResult(value) {
|
|
|
8288
8849
|
function isFailedResult(value) {
|
|
8289
8850
|
return isCapxulResult(value) && value.ok === false && "error" in value;
|
|
8290
8851
|
}
|
|
8852
|
+
//#endregion
|
|
8853
|
+
//#region src/observation-relay.ts
|
|
8854
|
+
/**
|
|
8855
|
+
* Build the SDK-owned first-party relay adapter. Both capture entry points send
|
|
8856
|
+
* the same redacted envelope fire-and-forget; delivery is fully isolated from
|
|
8857
|
+
* the SDK's `CapxulResult` and can never throw into the caller.
|
|
8858
|
+
*/
|
|
8859
|
+
function createCapxulFirstPartyRelay(options) {
|
|
8860
|
+
const fetchImpl = options.fetch ?? resolveGlobalFetch();
|
|
8861
|
+
const send = (failure) => {
|
|
8862
|
+
if (fetchImpl === void 0) return;
|
|
8863
|
+
try {
|
|
8864
|
+
const headers = { "content-type": "application/json" };
|
|
8865
|
+
const encodedContext = encodeObservationContextHeader(failure.context);
|
|
8866
|
+
if (encodedContext !== void 0) headers[OBSERVATION_CONTEXT_HEADER] = encodedContext;
|
|
8867
|
+
const body = JSON.stringify({
|
|
8868
|
+
operation: failure.operation,
|
|
8869
|
+
error_kind: failure.errorKind,
|
|
8870
|
+
sdk_version: failure.sdkVersion
|
|
8871
|
+
});
|
|
8872
|
+
Promise.resolve(fetchImpl(options.ingestUrl, {
|
|
8873
|
+
method: "POST",
|
|
8874
|
+
headers,
|
|
8875
|
+
body,
|
|
8876
|
+
keepalive: true
|
|
8877
|
+
})).catch(() => void 0);
|
|
8878
|
+
} catch {}
|
|
8879
|
+
};
|
|
8880
|
+
return {
|
|
8881
|
+
captureOperationFailure: send,
|
|
8882
|
+
captureException: send
|
|
8883
|
+
};
|
|
8884
|
+
}
|
|
8885
|
+
function resolveGlobalFetch() {
|
|
8886
|
+
const candidate = globalThis.fetch;
|
|
8887
|
+
return typeof candidate === "function" ? candidate : void 0;
|
|
8888
|
+
}
|
|
8291
8889
|
const SDK_VERSION = version;
|
|
8292
8890
|
const collectProductionFlowPorts = Effect.gen(function* () {
|
|
8293
8891
|
const authClient = yield* AuthClientPortTag;
|
|
@@ -8575,7 +9173,7 @@ async function createCapxulClient$1(input) {
|
|
|
8575
9173
|
...input.authBaseUrl === void 0 ? {} : { override: input.authBaseUrl }
|
|
8576
9174
|
});
|
|
8577
9175
|
let signer = input.signer;
|
|
8578
|
-
if (signer === void 0 &&
|
|
9176
|
+
if (signer === void 0 && runtime === "browser") signer = createOpenfortBrowserSignerFromBootstrap({
|
|
8579
9177
|
...adapters.value.bootstrap,
|
|
8580
9178
|
authBaseUrl: resolvedAuthBaseUrl
|
|
8581
9179
|
});
|
|
@@ -8585,23 +9183,33 @@ async function createCapxulClient$1(input) {
|
|
|
8585
9183
|
authCache: adapters.value.ports.authCache,
|
|
8586
9184
|
requirement: input.requirement ?? "none",
|
|
8587
9185
|
...signer === void 0 ? {} : { signer },
|
|
9186
|
+
orgPort: new ConvexOrganizationAdapter({ convex: adapters.value.ports.convexCall }),
|
|
9187
|
+
...signer === void 0 ? {} : { organizationSetup: new ConvexOrganizationSetupAdapter({
|
|
9188
|
+
convex: adapters.value.ports.convexCall,
|
|
9189
|
+
signer
|
|
9190
|
+
}) },
|
|
8588
9191
|
...input.signal === void 0 ? {} : { signal: input.signal },
|
|
8589
9192
|
...input.otpTtlMs === void 0 ? {} : { otpTtlMs: input.otpTtlMs },
|
|
8590
9193
|
invokeTimeoutMs: input.invokeTimeoutMs ?? 3e4
|
|
8591
9194
|
}), signer);
|
|
8592
9195
|
const upstreamClose = adapters.value.close;
|
|
9196
|
+
const value = {
|
|
9197
|
+
...client,
|
|
9198
|
+
_internal: {
|
|
9199
|
+
...client._internal,
|
|
9200
|
+
close: async () => {
|
|
9201
|
+
if ("resetSession" in (signer ?? {})) signer.resetSession();
|
|
9202
|
+
await upstreamClose();
|
|
9203
|
+
}
|
|
9204
|
+
}
|
|
9205
|
+
};
|
|
9206
|
+
const relay = runtime === "browser" ? createCapxulFirstPartyRelay({
|
|
9207
|
+
ingestUrl: deriveObserveIngestUrl(adapters.value.bootstrap),
|
|
9208
|
+
...input.fetch === void 0 ? {} : { fetch: input.fetch }
|
|
9209
|
+
}) : void 0;
|
|
8593
9210
|
return {
|
|
8594
9211
|
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)
|
|
9212
|
+
value: observeSdkClient(value, composeObservationAdapters(input.observation, relay))
|
|
8605
9213
|
};
|
|
8606
9214
|
} catch (cause) {
|
|
8607
9215
|
await adapters.value.close().catch(() => void 0);
|
|
@@ -8643,6 +9251,38 @@ function resolveInput(input) {
|
|
|
8643
9251
|
};
|
|
8644
9252
|
}
|
|
8645
9253
|
}
|
|
9254
|
+
/**
|
|
9255
|
+
* Address of the Capxul client-relay ingest endpoint (Pipe 1, issue #877). The
|
|
9256
|
+
* HTTP router that serves `/v1/client/observe` lives on the deployment's
|
|
9257
|
+
* `.convex.site` host, while the bootstrap-resolved `convexUrl` is the sibling
|
|
9258
|
+
* `.convex.cloud` (WebSocket/query) host — the same deterministic pairing
|
|
9259
|
+
* `mintQuickstartKey` inverts. For any standard Convex deployment (including
|
|
9260
|
+
* production) rewriting the suffix targets the router directly with no host
|
|
9261
|
+
* proxy, so it is the primary rule.
|
|
9262
|
+
*
|
|
9263
|
+
* `siteBaseUrl` is deliberately NOT the primary source: it is an app-configured
|
|
9264
|
+
* field that defaults to a placeholder (`https://capxul.local`, see
|
|
9265
|
+
* `credentials/applications.ts`) for registered applications, so trusting it
|
|
9266
|
+
* outright would POST to a dead host for the common case. It is only consulted
|
|
9267
|
+
* as a fallback for a custom-domain `convexUrl` (no deterministic `.convex.site`
|
|
9268
|
+
* sibling) when it carries a real, non-placeholder origin.
|
|
9269
|
+
*/
|
|
9270
|
+
function deriveObserveIngestUrl(bootstrap) {
|
|
9271
|
+
const convexUrl = bootstrap.convexUrl.replace(/\/+$/, "");
|
|
9272
|
+
if (convexUrl.endsWith(".convex.cloud")) return `${convexUrl.replace(/\.convex\.cloud$/, ".convex.site")}/v1/client/observe`;
|
|
9273
|
+
return `${(usableSiteOrigin(bootstrap.siteBaseUrl) ?? convexUrl).replace(/\/+$/, "")}/v1/client/observe`;
|
|
9274
|
+
}
|
|
9275
|
+
/** A configured `siteBaseUrl` usable as a router host, or `undefined` if it is the placeholder. */
|
|
9276
|
+
function usableSiteOrigin(siteBaseUrl) {
|
|
9277
|
+
try {
|
|
9278
|
+
const url = new URL(siteBaseUrl);
|
|
9279
|
+
if (url.protocol !== "http:" && url.protocol !== "https:") return void 0;
|
|
9280
|
+
if (url.hostname === "capxul.local") return void 0;
|
|
9281
|
+
return url.origin;
|
|
9282
|
+
} catch {
|
|
9283
|
+
return;
|
|
9284
|
+
}
|
|
9285
|
+
}
|
|
8646
9286
|
function detectRuntime() {
|
|
8647
9287
|
const globalAny = globalThis;
|
|
8648
9288
|
return globalAny.window !== void 0 || globalAny.document !== void 0 ? "browser" : "node";
|