@capxul/sdk 1.0.0-alpha.17 → 1.0.0-alpha.19

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