@capxul/sdk 1.0.0-alpha.16 → 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 +108 -18
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +928 -118
- 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 +7 -4
- 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,94 @@ 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
|
|
1683
|
+
//#region ../wire/src/observation-context.ts
|
|
1684
|
+
/** Single bounded HTTP carrier used before a Convex action envelope exists. */
|
|
1685
|
+
const OBSERVATION_CONTEXT_HEADER = "x-capxul-observation-context";
|
|
1686
|
+
const FIELD_RULES = {
|
|
1687
|
+
application: {
|
|
1688
|
+
maxLength: 64,
|
|
1689
|
+
pattern: /^[A-Za-z0-9][A-Za-z0-9._-]*$/u
|
|
1690
|
+
},
|
|
1691
|
+
applicationId: {
|
|
1692
|
+
maxLength: 30,
|
|
1693
|
+
pattern: APP_ID_RE
|
|
1694
|
+
},
|
|
1695
|
+
release: {
|
|
1696
|
+
maxLength: 128,
|
|
1697
|
+
pattern: /^[A-Za-z0-9][A-Za-z0-9._+@:/-]*$/u
|
|
1698
|
+
},
|
|
1699
|
+
sessionId: {
|
|
1700
|
+
maxLength: 128,
|
|
1701
|
+
pattern: /^[A-Za-z0-9][A-Za-z0-9._:-]*$/u
|
|
1702
|
+
},
|
|
1703
|
+
organizationId: {
|
|
1704
|
+
maxLength: 128,
|
|
1705
|
+
pattern: /^[A-Za-z0-9][A-Za-z0-9._:-]*$/u
|
|
1706
|
+
},
|
|
1707
|
+
correlationId: {
|
|
1708
|
+
maxLength: 128,
|
|
1709
|
+
pattern: /^[A-Za-z0-9][A-Za-z0-9._:-]*$/u
|
|
1710
|
+
},
|
|
1711
|
+
anonymousId: {
|
|
1712
|
+
maxLength: 128,
|
|
1713
|
+
pattern: /^anon_[A-Za-z0-9-]+$/u
|
|
1714
|
+
}
|
|
1715
|
+
};
|
|
1716
|
+
/**
|
|
1717
|
+
* Copy only the canonical allowlist and silently omit malformed/sensitive
|
|
1718
|
+
* values. Observation metadata is best effort and may never reject a domain
|
|
1719
|
+
* operation.
|
|
1720
|
+
*/
|
|
1721
|
+
function sanitizeObservationContext(input) {
|
|
1722
|
+
if (typeof input !== "object" || input === null || Array.isArray(input)) return void 0;
|
|
1723
|
+
const source = input;
|
|
1724
|
+
const sanitized = {};
|
|
1725
|
+
for (const field of Object.keys(FIELD_RULES)) {
|
|
1726
|
+
const value = source[field];
|
|
1727
|
+
if (!isSafeField(field, value)) continue;
|
|
1728
|
+
sanitized[field] = value;
|
|
1729
|
+
}
|
|
1730
|
+
return Object.keys(sanitized).length === 0 ? void 0 : sanitized;
|
|
1731
|
+
}
|
|
1732
|
+
/** Encode only the sanitized allowlist; absence stays absence. */
|
|
1733
|
+
function encodeObservationContextHeader(input) {
|
|
1734
|
+
const sanitized = sanitizeObservationContext(input);
|
|
1735
|
+
return sanitized === void 0 ? void 0 : JSON.stringify(sanitized);
|
|
1736
|
+
}
|
|
1737
|
+
function isSafeField(field, value) {
|
|
1738
|
+
if (typeof value !== "string") return false;
|
|
1739
|
+
const rule = FIELD_RULES[field];
|
|
1740
|
+
return value.length > 0 && value.length <= rule.maxLength && value === value.trim() && !value.includes("://") && !containsSensitiveMaterial(value) && rule.pattern.test(value);
|
|
1741
|
+
}
|
|
1742
|
+
//#endregion
|
|
1743
|
+
//#region src/internal/observation-http.ts
|
|
1744
|
+
/** Resolve one bounded pre-auth snapshot for an outbound SDK HTTP request. */
|
|
1745
|
+
function observationRequestHeaders(adapter) {
|
|
1746
|
+
if (adapter === void 0) return {};
|
|
1747
|
+
try {
|
|
1748
|
+
const encoded = encodeObservationContextHeader(adapter.resolveContext?.());
|
|
1749
|
+
return encoded === void 0 ? {} : { [OBSERVATION_CONTEXT_HEADER]: encoded };
|
|
1750
|
+
} catch {
|
|
1751
|
+
return {};
|
|
1752
|
+
}
|
|
1753
|
+
}
|
|
1754
|
+
//#endregion
|
|
1649
1755
|
//#region src/adapters/auth-client/BetterAuthBrowserAdapter.ts
|
|
1650
1756
|
function withSignal$1(init, signal) {
|
|
1651
1757
|
return signal === void 0 ? init : {
|
|
@@ -1703,8 +1809,10 @@ function mapFetchError$1(operation, err, signal) {
|
|
|
1703
1809
|
var BetterAuthBrowserAdapter = class {
|
|
1704
1810
|
authBaseUrl;
|
|
1705
1811
|
fetchImpl;
|
|
1812
|
+
observation;
|
|
1706
1813
|
constructor(deps) {
|
|
1707
1814
|
this.authBaseUrl = deps.authBaseUrl.replace(/\/$/, "");
|
|
1815
|
+
this.observation = deps.observation;
|
|
1708
1816
|
this.fetchImpl = deps.fetch ?? ((input, init) => globalThis.fetch(input, init));
|
|
1709
1817
|
}
|
|
1710
1818
|
url(path) {
|
|
@@ -1731,7 +1839,10 @@ var BetterAuthBrowserAdapter = class {
|
|
|
1731
1839
|
try {
|
|
1732
1840
|
const res = await this.fetchImpl(this.url("/api/auth/email-otp/send-verification-otp"), withSignal$1({
|
|
1733
1841
|
method: "POST",
|
|
1734
|
-
headers: {
|
|
1842
|
+
headers: {
|
|
1843
|
+
"Content-Type": "application/json",
|
|
1844
|
+
...observationRequestHeaders(this.observation)
|
|
1845
|
+
},
|
|
1735
1846
|
body: JSON.stringify({
|
|
1736
1847
|
email: input.email,
|
|
1737
1848
|
type: "sign-in"
|
|
@@ -2105,12 +2216,14 @@ var BetterAuthNodeAdapter = class {
|
|
|
2105
2216
|
origin;
|
|
2106
2217
|
cookieJar;
|
|
2107
2218
|
fetchImpl;
|
|
2219
|
+
observation;
|
|
2108
2220
|
constructor(deps) {
|
|
2109
2221
|
this.authBaseUrl = deps.authBaseUrl.replace(/\/$/, "");
|
|
2110
2222
|
this.host = hostFromBaseUrl(this.authBaseUrl);
|
|
2111
2223
|
this.origin = deps.origin?.replace(/\/$/, "");
|
|
2112
2224
|
this.cookieJar = deps.cookieJar ?? new CookieJar();
|
|
2113
2225
|
this.fetchImpl = deps.fetch ?? fetch;
|
|
2226
|
+
this.observation = deps.observation;
|
|
2114
2227
|
}
|
|
2115
2228
|
url(path) {
|
|
2116
2229
|
return resolveAuthClientUrl(this.authBaseUrl, path);
|
|
@@ -2145,7 +2258,8 @@ var BetterAuthNodeAdapter = class {
|
|
|
2145
2258
|
method: "POST",
|
|
2146
2259
|
headers: {
|
|
2147
2260
|
"content-type": "application/json",
|
|
2148
|
-
...this.origin ? { origin: this.origin } : {}
|
|
2261
|
+
...this.origin ? { origin: this.origin } : {},
|
|
2262
|
+
...observationRequestHeaders(this.observation)
|
|
2149
2263
|
},
|
|
2150
2264
|
body: JSON.stringify({
|
|
2151
2265
|
email: input.email,
|
|
@@ -2646,7 +2760,7 @@ const PaymentSource = Schema.Union(Schema.Struct({
|
|
|
2646
2760
|
* `hashLineItems` (payment-document-hash.ts) into the Invoice `lineItemsHash`,
|
|
2647
2761
|
* and Σ(quantity × unitMinor) MUST equal the invoice `amount`. The hash binds
|
|
2648
2762
|
* the documentHash to the exact items; the sum-check makes the items add up to
|
|
2649
|
-
* the amount due. See the
|
|
2763
|
+
* the amount due. See the financial-operations contract in `packages/wire/CONTEXT.md`.
|
|
2650
2764
|
*/
|
|
2651
2765
|
const LineItem = Schema.Struct({
|
|
2652
2766
|
description: Schema.String,
|
|
@@ -2757,8 +2871,10 @@ async function safeText(res) {
|
|
|
2757
2871
|
var HttpBootstrapAdapter = class {
|
|
2758
2872
|
bootstrapBaseUrl;
|
|
2759
2873
|
fetchImpl;
|
|
2874
|
+
observation;
|
|
2760
2875
|
constructor(deps) {
|
|
2761
2876
|
this.bootstrapBaseUrl = deps.bootstrapBaseUrl.replace(/\/$/, "");
|
|
2877
|
+
this.observation = deps.observation;
|
|
2762
2878
|
this.fetchImpl = deps.fetch ?? ((input, init) => globalThis.fetch(input, init));
|
|
2763
2879
|
}
|
|
2764
2880
|
resolve(input) {
|
|
@@ -2766,7 +2882,8 @@ var HttpBootstrapAdapter = class {
|
|
|
2766
2882
|
try: () => {
|
|
2767
2883
|
const headers = {
|
|
2768
2884
|
"content-type": "application/json",
|
|
2769
|
-
...input.origin === void 0 ? {} : { origin: input.origin }
|
|
2885
|
+
...input.origin === void 0 ? {} : { origin: input.origin },
|
|
2886
|
+
...observationRequestHeaders(this.observation)
|
|
2770
2887
|
};
|
|
2771
2888
|
return this.fetchImpl(`${this.bootstrapBaseUrl}/v1/client/bootstrap`, {
|
|
2772
2889
|
method: "POST",
|
|
@@ -2860,13 +2977,58 @@ function convexCallErrorFromCapxul(operation, error) {
|
|
|
2860
2977
|
}
|
|
2861
2978
|
var ConvexCallPortTag = class extends Context.Tag("@capxul/sdk/ports/ConvexCallPort")() {};
|
|
2862
2979
|
//#endregion
|
|
2980
|
+
//#region src/internal/invocation-observation.ts
|
|
2981
|
+
const INVOCATION_OBSERVATION = Symbol("capxul.invocation-observation");
|
|
2982
|
+
const FAILURE_INVOCATION_SNAPSHOT = Symbol("capxul.failure-invocation-snapshot");
|
|
2983
|
+
/** @internal Attach one immutable, non-wire invocation snapshot to an SDK-owned object. */
|
|
2984
|
+
function attachInvocationObservation(target, context) {
|
|
2985
|
+
const snapshot = Object.freeze(context === void 0 ? {} : { context: Object.freeze({ ...context }) });
|
|
2986
|
+
Object.defineProperty(target, INVOCATION_OBSERVATION, {
|
|
2987
|
+
configurable: false,
|
|
2988
|
+
enumerable: false,
|
|
2989
|
+
value: snapshot,
|
|
2990
|
+
writable: false
|
|
2991
|
+
});
|
|
2992
|
+
return target;
|
|
2993
|
+
}
|
|
2994
|
+
/** @internal Read the snapshot without exposing its symbol or adding a wire field. */
|
|
2995
|
+
function readInvocationObservation(source) {
|
|
2996
|
+
if (typeof source !== "object" || source === null) return void 0;
|
|
2997
|
+
return source[INVOCATION_OBSERVATION];
|
|
2998
|
+
}
|
|
2999
|
+
/** @internal Carry a snapshot across an SDK adapter projection without resolving again. */
|
|
3000
|
+
function copyInvocationObservation(source, target) {
|
|
3001
|
+
const snapshot = readInvocationObservation(source);
|
|
3002
|
+
return snapshot === void 0 ? target : attachInvocationObservation(target, snapshot.context);
|
|
3003
|
+
}
|
|
3004
|
+
/** @internal Mark a failure envelope as already resolved at the public invocation boundary. */
|
|
3005
|
+
function markFailureInvocationSnapshot(failure) {
|
|
3006
|
+
Object.defineProperty(failure, FAILURE_INVOCATION_SNAPSHOT, {
|
|
3007
|
+
configurable: false,
|
|
3008
|
+
enumerable: false,
|
|
3009
|
+
value: true,
|
|
3010
|
+
writable: false
|
|
3011
|
+
});
|
|
3012
|
+
return failure;
|
|
3013
|
+
}
|
|
3014
|
+
/** @internal Distinguish public-boundary failures from direct adapter calls. */
|
|
3015
|
+
function hasFailureInvocationSnapshot(failure) {
|
|
3016
|
+
return typeof failure === "object" && failure !== null && failure[FAILURE_INVOCATION_SNAPSHOT] === true;
|
|
3017
|
+
}
|
|
3018
|
+
//#endregion
|
|
2863
3019
|
//#region src/adapters/convex-call/ConvexCallAdapter.ts
|
|
3020
|
+
/** Exact floor-first allowlist; every additional handler must migrate its validator first. */
|
|
3021
|
+
const OBSERVED_CONVEX_ACTIONS = new Set(["subAccount/actions:transfer"]);
|
|
2864
3022
|
var ConvexCallAdapter = class {
|
|
2865
3023
|
#client;
|
|
2866
3024
|
#tokenProvider;
|
|
3025
|
+
#applicationId;
|
|
3026
|
+
#observation;
|
|
2867
3027
|
constructor(deps) {
|
|
2868
3028
|
this.#client = deps.client ?? new ConvexClient(deps.convexUrl);
|
|
2869
3029
|
this.#tokenProvider = deps.tokenProvider;
|
|
3030
|
+
this.#applicationId = deps.applicationId;
|
|
3031
|
+
this.#observation = deps.observation;
|
|
2870
3032
|
if (this.#tokenProvider) this.#client.setAuth(this.#tokenProvider);
|
|
2871
3033
|
}
|
|
2872
3034
|
refreshAuth() {
|
|
@@ -2885,10 +3047,36 @@ var ConvexCallAdapter = class {
|
|
|
2885
3047
|
});
|
|
2886
3048
|
}
|
|
2887
3049
|
action(fn, args) {
|
|
3050
|
+
const path = getFunctionName(fn);
|
|
2888
3051
|
return Effect.tryPromise({
|
|
2889
|
-
try: () => this.#client.action(fn, args),
|
|
2890
|
-
catch: (cause) => mapToConvexCallError(
|
|
3052
|
+
try: () => this.#client.action(fn, this.#observedActionArgs(path, args)),
|
|
3053
|
+
catch: (cause) => mapToConvexCallError(path, cause)
|
|
3054
|
+
});
|
|
3055
|
+
}
|
|
3056
|
+
#observedActionArgs(path, args) {
|
|
3057
|
+
if (!OBSERVED_CONVEX_ACTIONS.has(path)) return args;
|
|
3058
|
+
let hostContext;
|
|
3059
|
+
const invocationSnapshot = readInvocationObservation(args);
|
|
3060
|
+
if (invocationSnapshot !== void 0) hostContext = invocationSnapshot.context;
|
|
3061
|
+
else {
|
|
3062
|
+
const resolveContext = this.#observation?.resolveContext;
|
|
3063
|
+
if (resolveContext === void 0) return args;
|
|
3064
|
+
try {
|
|
3065
|
+
hostContext = resolveContext();
|
|
3066
|
+
} catch {
|
|
3067
|
+
return args;
|
|
3068
|
+
}
|
|
3069
|
+
}
|
|
3070
|
+
if (hostContext === void 0) return args;
|
|
3071
|
+
hostContext = sanitizeObservationContext({
|
|
3072
|
+
...hostContext,
|
|
3073
|
+
...this.#applicationId === void 0 ? {} : { applicationId: this.#applicationId }
|
|
2891
3074
|
});
|
|
3075
|
+
if (hostContext === void 0) return args;
|
|
3076
|
+
return {
|
|
3077
|
+
...args,
|
|
3078
|
+
observationContext: hostContext
|
|
3079
|
+
};
|
|
2892
3080
|
}
|
|
2893
3081
|
subscribe(fn, args, callback) {
|
|
2894
3082
|
return Effect.try({
|
|
@@ -3556,7 +3744,7 @@ function fromWei(rawBalance, decimals, currency) {
|
|
|
3556
3744
|
}
|
|
3557
3745
|
//#endregion
|
|
3558
3746
|
//#region src/adapters/account-read/ConvexAccountAdapter.ts
|
|
3559
|
-
const DEFAULT_FUNCTIONS$
|
|
3747
|
+
const DEFAULT_FUNCTIONS$5 = {
|
|
3560
3748
|
readBalance: makeFunctionReference("account/actions:readBalance"),
|
|
3561
3749
|
faucetMint: makeFunctionReference("account/actions:faucetMint")
|
|
3562
3750
|
};
|
|
@@ -3565,7 +3753,7 @@ var ConvexAccountAdapter = class {
|
|
|
3565
3753
|
#fns;
|
|
3566
3754
|
constructor(deps) {
|
|
3567
3755
|
this.#convex = deps.convex;
|
|
3568
|
-
this.#fns = deps.functions ?? DEFAULT_FUNCTIONS$
|
|
3756
|
+
this.#fns = deps.functions ?? DEFAULT_FUNCTIONS$5;
|
|
3569
3757
|
}
|
|
3570
3758
|
readBalance(input) {
|
|
3571
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))));
|
|
@@ -3618,7 +3806,7 @@ function subAccountErrorFromCapxul(operation, error, cause = error) {
|
|
|
3618
3806
|
var SubAccountPortTag = class extends Context.Tag("@capxul/sdk/ports/SubAccountPort")() {};
|
|
3619
3807
|
//#endregion
|
|
3620
3808
|
//#region src/adapters/sub-account/ConvexSubAccountAdapter.ts
|
|
3621
|
-
const DEFAULT_FUNCTIONS$
|
|
3809
|
+
const DEFAULT_FUNCTIONS$4 = {
|
|
3622
3810
|
create: makeFunctionReference("subAccount/mutations:create"),
|
|
3623
3811
|
get: makeFunctionReference("subAccount/queries:get"),
|
|
3624
3812
|
list: makeFunctionReference("subAccount/queries:list"),
|
|
@@ -3633,7 +3821,7 @@ var ConvexSubAccountAdapter = class {
|
|
|
3633
3821
|
constructor(deps) {
|
|
3634
3822
|
this.#convex = deps.convex;
|
|
3635
3823
|
this.#chainId = deps.chainId;
|
|
3636
|
-
this.#fns = deps.functions ?? DEFAULT_FUNCTIONS$
|
|
3824
|
+
this.#fns = deps.functions ?? DEFAULT_FUNCTIONS$4;
|
|
3637
3825
|
}
|
|
3638
3826
|
create(input) {
|
|
3639
3827
|
return this.#runMutation("create", this.#fns.create, {
|
|
@@ -3661,12 +3849,12 @@ var ConvexSubAccountAdapter = class {
|
|
|
3661
3849
|
const operation = "transfer";
|
|
3662
3850
|
return Effect.suspend(() => {
|
|
3663
3851
|
const rawAmount = toWei(input.amount);
|
|
3664
|
-
return this.#convex.action(this.#fns.transfer, {
|
|
3852
|
+
return this.#convex.action(this.#fns.transfer, copyInvocationObservation(input, {
|
|
3665
3853
|
chainId: this.#chainId,
|
|
3666
3854
|
from: input.from,
|
|
3667
3855
|
to: input.to,
|
|
3668
3856
|
rawAmount
|
|
3669
|
-
});
|
|
3857
|
+
}));
|
|
3670
3858
|
}).pipe(Effect.mapError((error) => subAccountErrorFromCapxul(operation, error.publicError, error)), Effect.map((wire) => brandTransferResult(wire)), Effect.catchAllDefect((cause) => Effect.fail(subAccountErrorFromUnknown(operation, cause))));
|
|
3671
3859
|
}
|
|
3672
3860
|
#runMutation(operation, ref, args) {
|
|
@@ -3717,7 +3905,7 @@ function smartAccountErrorFromCapxul(operation, error, cause = error) {
|
|
|
3717
3905
|
var SmartAccountPortTag = class extends Context.Tag("@capxul/sdk/ports/SmartAccountPort")() {};
|
|
3718
3906
|
//#endregion
|
|
3719
3907
|
//#region src/adapters/smart-account/ConvexSmartAccountAdapter.ts
|
|
3720
|
-
const DEFAULT_FUNCTIONS$
|
|
3908
|
+
const DEFAULT_FUNCTIONS$3 = {
|
|
3721
3909
|
loadByAuthUserId: makeFunctionReference("smartAccount/queries:loadByAuthUserId"),
|
|
3722
3910
|
loadBySmartAccountAddress: makeFunctionReference("smartAccount/queries:loadBySmartAccountAddress"),
|
|
3723
3911
|
provision: makeFunctionReference("smartAccount/mutations:provision"),
|
|
@@ -3729,7 +3917,7 @@ var ConvexSmartAccountAdapter = class {
|
|
|
3729
3917
|
#fns;
|
|
3730
3918
|
constructor(deps) {
|
|
3731
3919
|
this.#convex = deps.convex;
|
|
3732
|
-
this.#fns = deps.functions ?? DEFAULT_FUNCTIONS$
|
|
3920
|
+
this.#fns = deps.functions ?? DEFAULT_FUNCTIONS$3;
|
|
3733
3921
|
}
|
|
3734
3922
|
loadByAuthUserId(authUserId) {
|
|
3735
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))));
|
|
@@ -3804,6 +3992,424 @@ function smartAccountErrorFromUnknown(operation, cause) {
|
|
|
3804
3992
|
toAddress("0xa6b71e26c5e0845f74c812102ca7114b6a896ab2");
|
|
3805
3993
|
keccak256("0x");
|
|
3806
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
|
|
3807
4413
|
//#region ../observability/src/index.ts
|
|
3808
4414
|
const PII = Schema.String.pipe(Schema.brand("PII"));
|
|
3809
4415
|
const OptionalString = Schema.optional(Schema.String);
|
|
@@ -3963,6 +4569,10 @@ const TransferRequestedProps = Schema.Struct({
|
|
|
3963
4569
|
amount: OptionalWeiAmount,
|
|
3964
4570
|
direction: OptionalTransferDirection
|
|
3965
4571
|
});
|
|
4572
|
+
const TransferBackendReceivedProps = Schema.Struct({
|
|
4573
|
+
...TelemetryEnvelopeProps,
|
|
4574
|
+
direction: OptionalTransferDirection
|
|
4575
|
+
});
|
|
3966
4576
|
const TransferConfirmedProps = Schema.Struct({
|
|
3967
4577
|
...TelemetryEnvelopeProps,
|
|
3968
4578
|
amount: OptionalWeiAmount,
|
|
@@ -4179,6 +4789,10 @@ Schema.Struct({
|
|
|
4179
4789
|
name: Schema.Literal("transfer_requested"),
|
|
4180
4790
|
props: Schema.optional(TransferRequestedProps)
|
|
4181
4791
|
});
|
|
4792
|
+
Schema.Struct({
|
|
4793
|
+
name: Schema.Literal("transfer_backend_received"),
|
|
4794
|
+
props: Schema.optional(TransferBackendReceivedProps)
|
|
4795
|
+
});
|
|
4182
4796
|
Schema.Struct({
|
|
4183
4797
|
name: Schema.Literal("transfer_confirmed"),
|
|
4184
4798
|
props: Schema.optional(TransferConfirmedProps)
|
|
@@ -7282,6 +7896,76 @@ function listOrgsProgram() {
|
|
|
7282
7896
|
});
|
|
7283
7897
|
}
|
|
7284
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
|
|
7285
7969
|
//#region src/client/org.ts
|
|
7286
7970
|
/**
|
|
7287
7971
|
* Org method surface (canon §C2/§C3, D13). S1 (#274) wires `createOrg` /
|
|
@@ -7330,6 +8014,36 @@ function makeOrgMethods(deps) {
|
|
|
7330
8014
|
},
|
|
7331
8015
|
...convexCall === void 0 ? {} : { convexCall }
|
|
7332
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
|
+
},
|
|
7333
8047
|
profile: {
|
|
7334
8048
|
get(_options) {
|
|
7335
8049
|
if (_options?.signal?.aborted) return Promise.resolve({
|
|
@@ -7363,12 +8077,12 @@ function makeOrgMethods(deps) {
|
|
|
7363
8077
|
ok: false,
|
|
7364
8078
|
error: treasury.error
|
|
7365
8079
|
};
|
|
7366
|
-
const
|
|
7367
|
-
if (!
|
|
8080
|
+
const orgList = await toCapxulResult(listOrgsProgram(), layer);
|
|
8081
|
+
if (!orgList.ok) return {
|
|
7368
8082
|
ok: false,
|
|
7369
|
-
error:
|
|
8083
|
+
error: orgList.error
|
|
7370
8084
|
};
|
|
7371
|
-
const org =
|
|
8085
|
+
const org = orgList.value.find((candidate) => candidate.id === orgId);
|
|
7372
8086
|
return {
|
|
7373
8087
|
ok: true,
|
|
7374
8088
|
value: {
|
|
@@ -7556,7 +8270,6 @@ function normalizePaymentTiming(payment) {
|
|
|
7556
8270
|
}
|
|
7557
8271
|
//#endregion
|
|
7558
8272
|
//#region src/flows/onboarding.ts
|
|
7559
|
-
const DEFAULT_ORG_TEMPLATE = "Startup";
|
|
7560
8273
|
async function runCompletePersonalOnboarding(ops, input) {
|
|
7561
8274
|
const validated = validatePersonalInput(input);
|
|
7562
8275
|
if (!validated.ok) return validated;
|
|
@@ -7580,11 +8293,13 @@ async function runCompletePersonalOnboarding(ops, input) {
|
|
|
7580
8293
|
value: { lifecycle: lifecycle.value }
|
|
7581
8294
|
};
|
|
7582
8295
|
}
|
|
7583
|
-
async function runCompleteOrganizationOnboarding(ops, input) {
|
|
8296
|
+
async function runCompleteOrganizationOnboarding(ops, input, options) {
|
|
7584
8297
|
const validated = validateOrganizationInput(input);
|
|
7585
8298
|
if (!validated.ok) return validated;
|
|
7586
8299
|
const session = await ops.currentSession();
|
|
7587
8300
|
if (session === null) return fail(Errors.notAuthenticated());
|
|
8301
|
+
const setup = ops.organizationSetup;
|
|
8302
|
+
if (setup === void 0) return fail(Errors.notImplemented("organizationSetup", "completeOrganization"));
|
|
7588
8303
|
const written = await ops.completeIdentityOnboarding({
|
|
7589
8304
|
authUserId: session.authUserId,
|
|
7590
8305
|
email: session.email,
|
|
@@ -7592,22 +8307,18 @@ async function runCompleteOrganizationOnboarding(ops, input) {
|
|
|
7592
8307
|
country: validated.value.country
|
|
7593
8308
|
});
|
|
7594
8309
|
if (!written.ok) return fail(written.error);
|
|
7595
|
-
const
|
|
7596
|
-
if (!provisioned.ok) return fail(provisioned.error);
|
|
7597
|
-
const org = await ops.createOrg({
|
|
8310
|
+
const started = await setup.startOrResume({
|
|
7598
8311
|
name: validated.value.organizationName,
|
|
7599
8312
|
handle: validated.value.handle,
|
|
7600
|
-
template: DEFAULT_ORG_TEMPLATE,
|
|
7601
8313
|
country: validated.value.country
|
|
7602
8314
|
});
|
|
7603
|
-
if (!
|
|
7604
|
-
|
|
7605
|
-
|
|
7606
|
-
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;
|
|
7607
8318
|
return {
|
|
7608
8319
|
ok: true,
|
|
7609
8320
|
value: {
|
|
7610
|
-
|
|
8321
|
+
orgId: started.value.orgId,
|
|
7611
8322
|
lifecycle: lifecycle.value
|
|
7612
8323
|
}
|
|
7613
8324
|
};
|
|
@@ -7698,8 +8409,7 @@ function fail(error) {
|
|
|
7698
8409
|
* Onboarding method surface. The thin namespace over the `flows/onboarding.ts`
|
|
7699
8410
|
* choreography: it adds the `signal`-abort pre-check and delegates to the run
|
|
7700
8411
|
* functions. The `OnboardingOps` seam is supplied by `assembleCapxulClient`
|
|
7701
|
-
* (real ops
|
|
7702
|
-
* (in-memory double).
|
|
8412
|
+
* (real identity/Account ops plus Organization setup capability) or by tests.
|
|
7703
8413
|
*/
|
|
7704
8414
|
function makeOnboardingMethods(ops) {
|
|
7705
8415
|
return {
|
|
@@ -7715,7 +8425,7 @@ function makeOnboardingMethods(ops) {
|
|
|
7715
8425
|
ok: false,
|
|
7716
8426
|
error: Errors.cancelled({ operation: "onboarding.completeOrganization" })
|
|
7717
8427
|
});
|
|
7718
|
-
return runCompleteOrganizationOnboarding(ops, input);
|
|
8428
|
+
return runCompleteOrganizationOnboarding(ops, input, options);
|
|
7719
8429
|
}
|
|
7720
8430
|
};
|
|
7721
8431
|
}
|
|
@@ -7794,7 +8504,8 @@ function assembleCapxulClient(input) {
|
|
|
7794
8504
|
...selectedOrgPort === void 0 ? {} : { orgPort: selectedOrgPort },
|
|
7795
8505
|
...input.orgRolesDeploymentPort === void 0 ? {} : { orgRolesDeploymentPort: input.orgRolesDeploymentPort },
|
|
7796
8506
|
...input.orgSpendPort === void 0 ? {} : { orgSpendPort: input.orgSpendPort },
|
|
7797
|
-
convexCall: input.ports.convexCall
|
|
8507
|
+
convexCall: input.ports.convexCall,
|
|
8508
|
+
...input.organizationSetup === void 0 ? {} : { organizationSetup: input.organizationSetup }
|
|
7798
8509
|
});
|
|
7799
8510
|
if (selectedOrgPort !== void 0) detectPendingOrgInvitations = async () => {
|
|
7800
8511
|
await orgMethods.orgs.detectAndAcceptPendingInvitations();
|
|
@@ -7817,7 +8528,7 @@ function assembleCapxulClient(input) {
|
|
|
7817
8528
|
triggerProvisioning: () => account._internal.provision(),
|
|
7818
8529
|
kickProvisioning: () => kickProvisioning?.(),
|
|
7819
8530
|
readLifecycle: () => account.getLifecycle(),
|
|
7820
|
-
|
|
8531
|
+
...input.organizationSetup === void 0 ? {} : { organizationSetup: input.organizationSetup }
|
|
7821
8532
|
});
|
|
7822
8533
|
return {
|
|
7823
8534
|
auth,
|
|
@@ -7859,45 +8570,6 @@ function assembleCapxulClient(input) {
|
|
|
7859
8570
|
const SDK_VERSION$1 = version;
|
|
7860
8571
|
/** Stable PostHog event used for typed failures that are expected product outcomes. */
|
|
7861
8572
|
const CAPXUL_SDK_EXPECTED_OUTCOME_EVENT = "capxul_sdk_expected_outcome";
|
|
7862
|
-
const EXPECTED_OPERATION_OUTCOMES = new Set([
|
|
7863
|
-
"INVALID_INPUT",
|
|
7864
|
-
"NOT_AUTHENTICATED",
|
|
7865
|
-
"CANCELLED",
|
|
7866
|
-
"SIGNER_REJECTED",
|
|
7867
|
-
"VERIFICATION_REQUIRED",
|
|
7868
|
-
"INSUFFICIENT_BALANCE",
|
|
7869
|
-
"INVALID_RECIPIENT",
|
|
7870
|
-
"ROLE_PERMISSION_DENIED",
|
|
7871
|
-
"RATE_LIMITED",
|
|
7872
|
-
"OTP_EXPIRED",
|
|
7873
|
-
"WRONG_STATE"
|
|
7874
|
-
]);
|
|
7875
|
-
const CONTEXT_PROPERTY_RULES = {
|
|
7876
|
-
application: {
|
|
7877
|
-
maxLength: 64,
|
|
7878
|
-
pattern: /^[A-Za-z0-9][A-Za-z0-9._-]*$/u
|
|
7879
|
-
},
|
|
7880
|
-
release: {
|
|
7881
|
-
maxLength: 128,
|
|
7882
|
-
pattern: /^[A-Za-z0-9][A-Za-z0-9._+@:/-]*$/u
|
|
7883
|
-
},
|
|
7884
|
-
session_id: {
|
|
7885
|
-
maxLength: 128,
|
|
7886
|
-
pattern: /^[A-Za-z0-9][A-Za-z0-9._:-]*$/u
|
|
7887
|
-
},
|
|
7888
|
-
organization_id: {
|
|
7889
|
-
maxLength: 128,
|
|
7890
|
-
pattern: /^[A-Za-z0-9][A-Za-z0-9._:-]*$/u
|
|
7891
|
-
},
|
|
7892
|
-
correlation_id: {
|
|
7893
|
-
maxLength: 128,
|
|
7894
|
-
pattern: /^[A-Za-z0-9][A-Za-z0-9._:-]*$/u
|
|
7895
|
-
}
|
|
7896
|
-
};
|
|
7897
|
-
const EMBEDDED_WALLET_MATERIAL = /0x[a-fA-F0-9]{40,}/u;
|
|
7898
|
-
const RAW_PRIVATE_KEY_MATERIAL = /(?:^|[^a-fA-F0-9])[a-fA-F0-9]{64}(?:$|[^a-fA-F0-9])/u;
|
|
7899
|
-
const COMPACT_JWT = /eyJ[A-Za-z0-9_-]*\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+/u;
|
|
7900
|
-
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;
|
|
7901
8573
|
/**
|
|
7902
8574
|
* Adapt the host's already-initialized PostHog-like client. This function does
|
|
7903
8575
|
* not import, initialize, configure, or own PostHog.
|
|
@@ -7907,7 +8579,7 @@ function fromPostHog(client, options = {}) {
|
|
|
7907
8579
|
if (!isEnabled(options.enabled)) return;
|
|
7908
8580
|
if (client === null || client === void 0) return;
|
|
7909
8581
|
const safeFailure = sanitizeFailureObservation(failure);
|
|
7910
|
-
return [safeFailure, postHogProperties(safeFailure, resolveContext(options.context))];
|
|
8582
|
+
return [safeFailure, postHogProperties(safeFailure, hasFailureInvocationSnapshot(failure) ? void 0 : resolveContext(options.context))];
|
|
7911
8583
|
};
|
|
7912
8584
|
const captureOperationFailure = (failure) => {
|
|
7913
8585
|
const prepared = prepare(failure);
|
|
@@ -7932,6 +8604,10 @@ function fromPostHog(client, options = {}) {
|
|
|
7932
8604
|
deliver(() => client.captureException(safeFailure.exception, properties));
|
|
7933
8605
|
};
|
|
7934
8606
|
return {
|
|
8607
|
+
resolveContext: () => {
|
|
8608
|
+
if (!isEnabled(options.enabled) || client === null || client === void 0) return void 0;
|
|
8609
|
+
return resolveRawContext(options.context) ?? {};
|
|
8610
|
+
},
|
|
7935
8611
|
captureOperationFailure,
|
|
7936
8612
|
captureException
|
|
7937
8613
|
};
|
|
@@ -7944,6 +8620,34 @@ function deliver(capture) {
|
|
|
7944
8620
|
ignoreDeliveryFailure(capture());
|
|
7945
8621
|
} catch {}
|
|
7946
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
|
+
}
|
|
7947
8651
|
/** @internal Decorates public client method bundles at the owning SDK boundary. */
|
|
7948
8652
|
function observeSdkClient(client, adapter) {
|
|
7949
8653
|
if (adapter === void 0) return client;
|
|
@@ -7982,18 +8686,20 @@ function observeSdkClient(client, adapter) {
|
|
|
7982
8686
|
if (cached !== void 0) return cached;
|
|
7983
8687
|
const wrapped = new Proxy(callable, {
|
|
7984
8688
|
apply(currentTarget, _thisArg, args) {
|
|
8689
|
+
const invocationContext = resolveAdapterContext(adapter);
|
|
8690
|
+
const invocationArgs = carryInvocationContext(operation, args, invocationContext);
|
|
7985
8691
|
let output;
|
|
7986
8692
|
try {
|
|
7987
|
-
output = Reflect.apply(currentTarget, owner,
|
|
8693
|
+
output = Reflect.apply(currentTarget, owner, invocationArgs);
|
|
7988
8694
|
} catch (cause) {
|
|
7989
|
-
report(adapter, "exception", operation, cause);
|
|
8695
|
+
report(adapter, "exception", operation, cause, invocationContext);
|
|
7990
8696
|
throw cause;
|
|
7991
8697
|
}
|
|
7992
|
-
if (isPromiseLike(output)) return Promise.resolve(output).then((result) => processOutput(result, path), (cause) => {
|
|
7993
|
-
report(adapter, "exception", operation, cause);
|
|
8698
|
+
if (isPromiseLike(output)) return Promise.resolve(output).then((result) => processOutput(result, path, invocationContext), (cause) => {
|
|
8699
|
+
report(adapter, "exception", operation, cause, invocationContext);
|
|
7994
8700
|
throw cause;
|
|
7995
8701
|
});
|
|
7996
|
-
return processOutput(output, path);
|
|
8702
|
+
return processOutput(output, path, invocationContext);
|
|
7997
8703
|
},
|
|
7998
8704
|
get(currentTarget, property) {
|
|
7999
8705
|
const attached = Reflect.get(currentTarget, property, currentTarget);
|
|
@@ -8006,9 +8712,9 @@ function observeSdkClient(client, adapter) {
|
|
|
8006
8712
|
}
|
|
8007
8713
|
return isPlainObject(value) ? wrapObject(value, path) : value;
|
|
8008
8714
|
};
|
|
8009
|
-
const processOutput = (output, path) => {
|
|
8715
|
+
const processOutput = (output, path, invocationContext) => {
|
|
8010
8716
|
if (isFailedResult(output)) {
|
|
8011
|
-
report(adapter, "operation", path.join("."), output.error);
|
|
8717
|
+
report(adapter, "operation", path.join("."), output.error, invocationContext);
|
|
8012
8718
|
return output;
|
|
8013
8719
|
}
|
|
8014
8720
|
if (isCapxulResult(output)) return output;
|
|
@@ -8016,24 +8722,40 @@ function observeSdkClient(client, adapter) {
|
|
|
8016
8722
|
};
|
|
8017
8723
|
return wrapObject(client, []);
|
|
8018
8724
|
}
|
|
8725
|
+
function carryInvocationContext(operation, args, context) {
|
|
8726
|
+
if (operation !== "subAccounts.transfer" || !isPlainObject(args[0])) return args;
|
|
8727
|
+
return [attachInvocationObservation({ ...args[0] }, context), ...args.slice(1)];
|
|
8728
|
+
}
|
|
8019
8729
|
/** @internal Reports a factory-level typed failure without changing its identity. */
|
|
8020
8730
|
function observeFailedResult(result, adapter, operation) {
|
|
8021
|
-
if (adapter !== void 0 && isFailedResult(result)) report(adapter, "operation", operation, result.error);
|
|
8731
|
+
if (adapter !== void 0 && isFailedResult(result)) report(adapter, "operation", operation, result.error, resolveAdapterContext(adapter));
|
|
8022
8732
|
return result;
|
|
8023
8733
|
}
|
|
8024
|
-
function report(adapter, kind, operation, cause) {
|
|
8734
|
+
function report(adapter, kind, operation, cause, invocationContext) {
|
|
8025
8735
|
const operationName = normalizeOperation(operation);
|
|
8026
8736
|
const kindName = normalizeErrorKind(errorKind(cause));
|
|
8027
|
-
const
|
|
8737
|
+
const context = sanitizeObservationContext({
|
|
8738
|
+
...invocationContext,
|
|
8739
|
+
...isCapxulError(cause) && cause.correlationId !== void 0 ? { correlationId: cause.correlationId } : {}
|
|
8740
|
+
});
|
|
8741
|
+
const failure = markFailureInvocationSnapshot({
|
|
8028
8742
|
exception: syntheticException(operationName, kindName),
|
|
8029
8743
|
sdkVersion: SDK_VERSION$1,
|
|
8030
8744
|
operation: operationName,
|
|
8031
|
-
errorKind: kindName
|
|
8032
|
-
|
|
8745
|
+
errorKind: kindName,
|
|
8746
|
+
...context === void 0 ? {} : { context }
|
|
8747
|
+
});
|
|
8033
8748
|
try {
|
|
8034
8749
|
ignoreDeliveryFailure(kind === "operation" ? adapter.captureOperationFailure(failure) : adapter.captureException(failure));
|
|
8035
8750
|
} catch {}
|
|
8036
8751
|
}
|
|
8752
|
+
function resolveAdapterContext(adapter) {
|
|
8753
|
+
try {
|
|
8754
|
+
return adapter.resolveContext?.();
|
|
8755
|
+
} catch {
|
|
8756
|
+
return;
|
|
8757
|
+
}
|
|
8758
|
+
}
|
|
8037
8759
|
function ignoreDeliveryFailure(delivery) {
|
|
8038
8760
|
if (!isPromiseLike(delivery)) return;
|
|
8039
8761
|
try {
|
|
@@ -8047,23 +8769,23 @@ function postHogProperties(failure, context) {
|
|
|
8047
8769
|
error_kind: failure.errorKind,
|
|
8048
8770
|
handled: true
|
|
8049
8771
|
};
|
|
8050
|
-
|
|
8051
|
-
|
|
8052
|
-
|
|
8053
|
-
|
|
8054
|
-
|
|
8772
|
+
const merged = sanitizeObservationContext({
|
|
8773
|
+
...context,
|
|
8774
|
+
...failure.context
|
|
8775
|
+
});
|
|
8776
|
+
if (merged?.application !== void 0) properties.application = merged.application;
|
|
8777
|
+
if (merged?.release !== void 0) properties.release = merged.release;
|
|
8778
|
+
if (merged?.sessionId !== void 0) properties.session_id = merged.sessionId;
|
|
8779
|
+
if (merged?.organizationId !== void 0) properties.organization_id = merged.organizationId;
|
|
8780
|
+
if (merged?.correlationId !== void 0) properties.correlation_id = merged.correlationId;
|
|
8781
|
+
if (merged?.anonymousId !== void 0) properties.anonymous_id = merged.anonymousId;
|
|
8055
8782
|
return properties;
|
|
8056
8783
|
}
|
|
8057
|
-
function assignSafeContext(properties, key, value) {
|
|
8058
|
-
if (typeof value !== "string") return;
|
|
8059
|
-
const rule = CONTEXT_PROPERTY_RULES[key];
|
|
8060
|
-
if (value.length === 0 || value.length > rule.maxLength || value !== value.trim() || value.includes("://") || containsSensitiveContext(value) || !rule.pattern.test(value)) return;
|
|
8061
|
-
properties[key] = value;
|
|
8062
|
-
}
|
|
8063
|
-
function containsSensitiveContext(value) {
|
|
8064
|
-
return EMBEDDED_WALLET_MATERIAL.test(value) || RAW_PRIVATE_KEY_MATERIAL.test(value) || COMPACT_JWT.test(value) || KNOWN_CREDENTIAL_PREFIX.test(value);
|
|
8065
|
-
}
|
|
8066
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) {
|
|
8067
8789
|
try {
|
|
8068
8790
|
return typeof context === "function" ? context() : context;
|
|
8069
8791
|
} catch {
|
|
@@ -8089,11 +8811,13 @@ function errorKind(cause) {
|
|
|
8089
8811
|
function sanitizeFailureObservation(failure) {
|
|
8090
8812
|
const operation = normalizeOperation(failure.operation);
|
|
8091
8813
|
const kind = normalizeErrorKind(failure.errorKind);
|
|
8814
|
+
const context = sanitizeObservationContext(failure.context);
|
|
8092
8815
|
return {
|
|
8093
8816
|
exception: syntheticException(operation, kind),
|
|
8094
8817
|
sdkVersion: normalizeSdkVersion(failure.sdkVersion),
|
|
8095
8818
|
operation,
|
|
8096
|
-
errorKind: kind
|
|
8819
|
+
errorKind: kind,
|
|
8820
|
+
...context === void 0 ? {} : { context }
|
|
8097
8821
|
};
|
|
8098
8822
|
}
|
|
8099
8823
|
function normalizeSdkVersion(value) {
|
|
@@ -8125,6 +8849,43 @@ function isCapxulResult(value) {
|
|
|
8125
8849
|
function isFailedResult(value) {
|
|
8126
8850
|
return isCapxulResult(value) && value.ok === false && "error" in value;
|
|
8127
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
|
+
}
|
|
8128
8889
|
const SDK_VERSION = version;
|
|
8129
8890
|
const collectProductionFlowPorts = Effect.gen(function* () {
|
|
8130
8891
|
const authClient = yield* AuthClientPortTag;
|
|
@@ -8232,11 +8993,13 @@ function productionBootstrapPortLayer(bootstrap) {
|
|
|
8232
8993
|
function productionAuthClientLayer(input, refreshConvexAuthRef) {
|
|
8233
8994
|
return (input.runtime === "browser" ? BetterAuthBrowserLayer({
|
|
8234
8995
|
authBaseUrl: input.runtimeUrls.authBaseUrl,
|
|
8235
|
-
...input.fetch === void 0 ? {} : { fetch: input.fetch }
|
|
8996
|
+
...input.fetch === void 0 ? {} : { fetch: input.fetch },
|
|
8997
|
+
...input.observation === void 0 ? {} : { observation: input.observation }
|
|
8236
8998
|
}) : BetterAuthNodeLayer({
|
|
8237
8999
|
authBaseUrl: input.runtimeUrls.authBaseUrl,
|
|
8238
9000
|
...input.origin === void 0 ? {} : { origin: input.origin },
|
|
8239
|
-
...input.fetch === void 0 ? {} : { fetch: input.fetch }
|
|
9001
|
+
...input.fetch === void 0 ? {} : { fetch: input.fetch },
|
|
9002
|
+
...input.observation === void 0 ? {} : { observation: input.observation }
|
|
8240
9003
|
})).pipe(Layer.map((context) => {
|
|
8241
9004
|
const authClient = Context.get(context, AuthClientPortTag);
|
|
8242
9005
|
return Context.make(AuthClientPortTag, refreshConvexAuthOnSession(authClient, () => {
|
|
@@ -8255,6 +9018,8 @@ function productionAuthCacheLayer(input) {
|
|
|
8255
9018
|
function productionConvexCallLayer(input, refreshConvexAuthRef) {
|
|
8256
9019
|
return Layer.unwrapEffect(Effect.map(AuthClientPortTag, (authClient) => ConvexCallLayer({
|
|
8257
9020
|
convexUrl: input.runtimeUrls.convexUrl,
|
|
9021
|
+
...input.applicationId === void 0 ? {} : { applicationId: input.applicationId },
|
|
9022
|
+
...input.observation === void 0 ? {} : { observation: input.observation },
|
|
8258
9023
|
...input.convexClient === void 0 ? {} : { client: input.convexClient },
|
|
8259
9024
|
tokenProvider: async ({ forceRefreshToken }) => {
|
|
8260
9025
|
const tokenResult = await Effect.runPromise(Effect.either(authClient.getConvexJwt({
|
|
@@ -8286,7 +9051,8 @@ async function createProductionAdapters(input) {
|
|
|
8286
9051
|
const closeScope = idempotentClose(() => Effect.runPromise(Scope.close(scope, Exit.void)));
|
|
8287
9052
|
const bootstrapLayer = HttpBootstrapLayer({
|
|
8288
9053
|
bootstrapBaseUrl: resolvedInput.value.bootstrapBaseUrl,
|
|
8289
|
-
...input.fetch === void 0 ? {} : { fetch: input.fetch }
|
|
9054
|
+
...input.fetch === void 0 ? {} : { fetch: input.fetch },
|
|
9055
|
+
...input.observation === void 0 ? {} : { observation: input.observation }
|
|
8290
9056
|
});
|
|
8291
9057
|
try {
|
|
8292
9058
|
const bootstrapContext = await Effect.runPromise(Layer.buildWithScope(bootstrapLayer, scope));
|
|
@@ -8344,6 +9110,8 @@ async function createProductionAdapters(input) {
|
|
|
8344
9110
|
...resolvedInput.value.origin === void 0 ? {} : { origin: resolvedInput.value.origin },
|
|
8345
9111
|
runtimeUrls: runtimeUrls.value,
|
|
8346
9112
|
chainId: bootstrapResult.value.chainId,
|
|
9113
|
+
applicationId: bootstrapResult.value.applicationId,
|
|
9114
|
+
...input.observation === void 0 ? {} : { observation: input.observation },
|
|
8347
9115
|
...input.authCache === void 0 ? {} : { authCache: input.authCache },
|
|
8348
9116
|
...input.telemetry === void 0 ? {} : { telemetry: input.telemetry },
|
|
8349
9117
|
...injectedClient === void 0 ? {} : { convexClient: injectedClient },
|
|
@@ -8405,7 +9173,7 @@ async function createCapxulClient$1(input) {
|
|
|
8405
9173
|
...input.authBaseUrl === void 0 ? {} : { override: input.authBaseUrl }
|
|
8406
9174
|
});
|
|
8407
9175
|
let signer = input.signer;
|
|
8408
|
-
if (signer === void 0 &&
|
|
9176
|
+
if (signer === void 0 && runtime === "browser") signer = createOpenfortBrowserSignerFromBootstrap({
|
|
8409
9177
|
...adapters.value.bootstrap,
|
|
8410
9178
|
authBaseUrl: resolvedAuthBaseUrl
|
|
8411
9179
|
});
|
|
@@ -8415,23 +9183,33 @@ async function createCapxulClient$1(input) {
|
|
|
8415
9183
|
authCache: adapters.value.ports.authCache,
|
|
8416
9184
|
requirement: input.requirement ?? "none",
|
|
8417
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
|
+
}) },
|
|
8418
9191
|
...input.signal === void 0 ? {} : { signal: input.signal },
|
|
8419
9192
|
...input.otpTtlMs === void 0 ? {} : { otpTtlMs: input.otpTtlMs },
|
|
8420
9193
|
invokeTimeoutMs: input.invokeTimeoutMs ?? 3e4
|
|
8421
9194
|
}), signer);
|
|
8422
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;
|
|
8423
9210
|
return {
|
|
8424
9211
|
ok: true,
|
|
8425
|
-
value: observeSdkClient(
|
|
8426
|
-
...client,
|
|
8427
|
-
_internal: {
|
|
8428
|
-
...client._internal,
|
|
8429
|
-
close: async () => {
|
|
8430
|
-
if ("resetSession" in (signer ?? {})) signer.resetSession();
|
|
8431
|
-
await upstreamClose();
|
|
8432
|
-
}
|
|
8433
|
-
}
|
|
8434
|
-
}, input.observation)
|
|
9212
|
+
value: observeSdkClient(value, composeObservationAdapters(input.observation, relay))
|
|
8435
9213
|
};
|
|
8436
9214
|
} catch (cause) {
|
|
8437
9215
|
await adapters.value.close().catch(() => void 0);
|
|
@@ -8473,6 +9251,38 @@ function resolveInput(input) {
|
|
|
8473
9251
|
};
|
|
8474
9252
|
}
|
|
8475
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
|
+
}
|
|
8476
9286
|
function detectRuntime() {
|
|
8477
9287
|
const globalAny = globalThis;
|
|
8478
9288
|
return globalAny.window !== void 0 || globalAny.document !== void 0 ? "browser" : "node";
|