@capxul/sdk 2.3.2 → 2.4.0

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.
@@ -1291,1078 +1291,240 @@ new Map([
1291
1291
  ["setAllowance(bytes32,uint128,uint128,uint128,uint64,uint64)", "SetAllowance(bytes32,uint128,uint128,uint128,uint64,uint64)"]
1292
1292
  ].map(([functionSignature, eventSignature]) => [toFunctionSelector(functionSignature), toEventSelector(eventSignature)]));
1293
1293
  //#endregion
1294
- //#region src/surface/_shared/provisioning-telemetry.ts
1295
- async function emitProvisioningTelemetry(telemetry, smartAccount) {
1296
- if (telemetry === void 0) return;
1297
- await Effect.runPromise(telemetry.emit({
1298
- name: "provisioning_safe_created",
1299
- props: { safe_address: smartAccount.smartAccountAddress }
1300
- }).pipe(Effect.catch((cause) => Effect.sync(() => reportProvisioningTelemetryFailure("error", smartAccount, cause))), Effect.catchDefect((cause) => Effect.sync(() => reportProvisioningTelemetryFailure("defect", smartAccount, cause)))));
1301
- }
1302
- function reportProvisioningTelemetryFailure(kind, smartAccount, cause) {
1303
- if (!isProvisioningTelemetryDebugEnabled()) return;
1304
- globalThis.console?.warn?.("[capxul] provisioning telemetry dropped", {
1305
- kind,
1306
- safeAddress: smartAccount.smartAccountAddress,
1307
- cause
1308
- });
1309
- }
1310
- function isProvisioningTelemetryDebugEnabled() {
1311
- return globalThis.process?.env?.CAPXUL_DEBUG_TELEMETRY === "1";
1312
- }
1313
- //#endregion
1314
- //#region src/telemetry/get-failure-mode.ts
1294
+ //#region ../wire/src/brands.ts
1295
+ const lowercasedString = Schema.String.pipe(Schema.decodeTo(Schema.String, {
1296
+ decode: SchemaGetter.transform((value) => value.toLowerCase()),
1297
+ encode: SchemaGetter.transform((value) => value)
1298
+ }));
1299
+ const addressSchema = (name) => lowercasedString.pipe(Schema.refine((value) => EVM_ADDRESS_RE$1.test(value), { message: `${name} must be a 0x-prefixed EVM address` }));
1300
+ const bytes32BrandSchema = (name) => lowercasedString.pipe(Schema.refine((value) => BYTES32_RE.test(value), { message: `${name} must be a 0x-prefixed bytes32 value` }));
1301
+ /** Lowercase, 0x-prefixed bytes32 evidence without a public brand. */
1302
+ const Bytes32Schema = bytes32BrandSchema("value");
1303
+ const AddressSchema$1 = addressSchema("address");
1304
+ const SafeAddressSchema = addressSchema("safe address");
1305
+ const ModuleAddressSchema = addressSchema("module address");
1306
+ const TxHashSchema$1 = bytes32BrandSchema("transaction hash");
1307
+ const RoleKeySchema = bytes32BrandSchema("role key");
1308
+ const AllowanceKeySchema = bytes32BrandSchema("allowance key");
1309
+ const BlockNumberSchema$1 = Schema.Number.pipe(Schema.refine((value) => Number.isSafeInteger(value) && value >= 0, { message: "must be a non-negative safe integer" }));
1310
+ const LogIndexSchema = Schema.Number.pipe(Schema.refine((value) => Number.isSafeInteger(value) && value >= 0, { message: "must be a non-negative safe integer" }));
1311
+ const WeiAmountSchema$1 = Schema.String.pipe(Schema.refine((value) => WEI_RE.test(value), { message: "must be a non-negative integer string" }));
1312
+ const SettlementIdSchema = bytes32BrandSchema("settlementId").pipe(Schema.refine((value) => value !== ZERO_BYTES32, { message: "settlementId must not be zero" }));
1315
1313
  /**
1316
- * The five canonical {@link FailureMode} members, for runtime membership checks.
1317
- * Single source of truth tests assert against this exact set so the taxonomy
1318
- * and its guard can never drift apart.
1314
+ * `AppId` schema. Mirrors `toAppId` from `@capxul/types`:
1315
+ * `app_` + Crockford-base32 ULID (26 chars, first char in `[0-7]`).
1319
1316
  */
1320
- const FAILURE_MODES = new Set([
1321
- "auth-origin-mismatch",
1322
- "stale-openfort-cache",
1323
- "app-env-allowlist",
1324
- "no-secure-context",
1325
- "unknown"
1326
- ]);
1327
- function isFailureMode(value) {
1328
- return typeof value === "string" && FAILURE_MODES.has(value);
1329
- }
1317
+ const AppIdSchema$1 = Schema.String.pipe(Schema.refine((s) => APP_ID_RE.test(s), { message: "must be app_ plus a ULID" }));
1330
1318
  /**
1331
- * Extract the structured {@link FailureMode} from a CapxulError.
1332
- *
1333
- * Reads `details.failure_mode` and returns it only when it is one of the five
1334
- * canonical members; any other value (a legacy free string, a typo, a
1335
- * non-string) yields `undefined` so downstream telemetry never reports an
1336
- * unrecognised cause.
1319
+ * `ChainId` schema. Mirrors `toChainId`: positive safe integer.
1337
1320
  */
1338
- function getFailureMode(error) {
1339
- if (!isCapxulError(error)) return void 0;
1340
- const details = error.details;
1341
- if (details === void 0) return void 0;
1342
- return isFailureMode(details.failure_mode) ? details.failure_mode : void 0;
1343
- }
1321
+ const ChainIdSchema$1 = Schema.Number.pipe(Schema.refine((n) => Number.isSafeInteger(n) && n > 0, { message: "must be a positive safe integer" }));
1344
1322
  /**
1345
- * Resolve the canonical {@link FailureMode} for a `$exception`, guaranteeing a
1346
- * taxonomy member is always returned — never `undefined`, never a free string.
1347
- *
1348
- * Resolution order:
1349
- * 1. the structured mode on the error's `details.failure_mode` (already guarded);
1350
- * 2. a caller-supplied `contextFailureMode`, but ONLY when it passes the same
1351
- * runtime membership check — the static `FailureMode` type is erased at
1352
- * runtime, so an operation string (e.g. `"signer-get-address"`) injected via
1353
- * a JS caller or `as` cast is rejected here rather than leaking to telemetry;
1354
- * 3. `"unknown"` otherwise, so an unclassifiable error is still tagged with a
1355
- * canonical value instead of being emitted with no `failure_mode` at all.
1323
+ * `CurrencyCode` schema. Mirrors `toCurrencyCode`: currently supported
1324
+ * consumer-facing ISO-ish currency code set.
1356
1325
  */
1357
- function resolveFailureMode(error, contextFailureMode) {
1358
- return getFailureMode(error) ?? (isFailureMode(contextFailureMode) ? contextFailureMode : "unknown");
1359
- }
1360
- //#endregion
1361
- //#region src/signer.ts
1362
- const EVM_ADDRESS_HEX = /^0x[0-9a-fA-F]{40}$/;
1363
- const ECDSA_SIGNATURE_HEX = /^0x[0-9a-fA-F]{130}$/;
1364
- const SAFE_OP_DIGEST_HEX = /^0x[0-9a-fA-F]{64}$/;
1326
+ const CurrencyCodeSchema$1 = Schema.String.pipe(Schema.refine((s) => SUPPORTED_CURRENCY_CODES.includes(s), { message: "must be a supported currency code" }));
1327
+ const DOCUMENT_HASH_HEX_RE = /^[0-9a-fA-F]{64}$/;
1365
1328
  /**
1366
- * The readiness store a signer reports when it runs no readiness cycle. It
1367
- * fails CLOSED: a node key signer or an injected wallet never claims `ready`,
1368
- * so a gate that reads this never opens on a guess.
1329
+ * `DocumentHash` schema. Mirrors `toDocumentHash`: bare or 0x-prefixed bytes32,
1330
+ * normalized to lowercase 0x-prefixed form.
1369
1331
  */
1370
- const UNOBSERVABLE_SIGNER_STATUS = {
1371
- status: () => "unknown",
1372
- subscribe: () => () => void 0
1373
- };
1332
+ const DocumentHashSchema = Schema.String.pipe(Schema.decodeTo(Schema.String, {
1333
+ decode: SchemaGetter.transform((s) => {
1334
+ const stripped = s.startsWith("0x") || s.startsWith("0X") ? s.slice(2) : s;
1335
+ return DOCUMENT_HASH_HEX_RE.test(stripped) ? `0x${stripped.toLowerCase()}` : s;
1336
+ }),
1337
+ encode: SchemaGetter.transform((s) => s)
1338
+ }), Schema.refine((s) => BYTES32_RE.test(s), { message: "must be 32 bytes of hex" }));
1339
+ /** L2 orchestrated money evidence requires a nonzero Document hash. */
1340
+ const NonzeroDocumentHashSchema = DocumentHashSchema.pipe(Schema.refine((value) => value !== ZERO_BYTES32, { message: "documentHash must not be zero" }));
1374
1341
  /**
1375
- * Walk an error and its `cause` links once each. A self-referential chain
1376
- * terminates. Both signer predicates read the chain, so they read it here.
1342
+ * `SessionToken` schema. Mirrors `toSessionToken`: non-empty string.
1343
+ * Issuance source distinguishes SDK-handshake tokens from auth-session
1344
+ * tokens; the brand itself is opaque.
1377
1345
  */
1378
- function* causeChain(cause) {
1379
- const seen = /* @__PURE__ */ new Set();
1380
- let current = cause;
1381
- while (typeof current === "object" && current !== null && !seen.has(current)) {
1382
- seen.add(current);
1383
- yield current;
1384
- current = current.cause;
1385
- }
1386
- }
1387
- /** Fold a signer throw into the public error contract. */
1388
- function signerFailure(source, operation, cause) {
1389
- let failureMode;
1390
- for (const link of causeChain(cause)) {
1391
- if (link instanceof CapxulError && link.code === "SIGNER_REJECTED") return link;
1392
- failureMode = getFailureMode(link) ?? failureMode;
1393
- const error = link;
1394
- if (error.code === 4001 || error.error === "passkey_user_cancelled") return Errors.signerRejected({
1395
- source,
1396
- cause
1397
- });
1398
- }
1399
- return Errors.providerError("signer", operation, cause, failureMode === void 0 ? void 0 : { failure_mode: failureMode });
1400
- }
1346
+ const SessionTokenSchema = Schema.String.pipe(Schema.refine((s) => s.length > 0, { message: "must be a non-empty string" }));
1401
1347
  /**
1402
- * Browser `CapxulSigner` backed by an injected EIP-1193 wallet (MetaMask, etc.).
1403
- * Signs the SafeOp digest via `eth_sign`, then verifies the returned signature
1404
- * recovers the selected account against that raw digest. Wallets that prefix
1405
- * `eth_sign` payloads are rejected before the backend submits an invalid SafeOp.
1406
- * The node key signer lives in `@capxul/sdk/node` (`localPrivateKeySigner`).
1348
+ * `EpochMs` schema. Mirrors `toEpochMs`: non-negative safe integer.
1407
1349
  */
1408
- function injectedWalletSigner(provider) {
1409
- const resolveAddress = async () => {
1410
- const accounts = await provider.request({ method: "eth_requestAccounts" });
1411
- const first = Array.isArray(accounts) ? accounts[0] : void 0;
1412
- if (typeof first !== "string") throw new Error("injectedWalletSigner: wallet returned no accounts");
1413
- if (!EVM_ADDRESS_HEX.test(first)) throw new Error("injectedWalletSigner: wallet returned invalid address format");
1414
- return toAddress(first);
1415
- };
1416
- return {
1417
- source: "injected-eip1193",
1418
- getAddress: resolveAddress,
1419
- async signUserOpHash(hash) {
1420
- if (!SAFE_OP_DIGEST_HEX.test(hash)) throw new Error("injectedWalletSigner: SafeOp digest must be a 0x-prefixed 32-byte hex");
1421
- const address = await resolveAddress();
1422
- let signature;
1423
- try {
1424
- signature = await provider.request({
1425
- method: "eth_sign",
1426
- params: [address, hash]
1427
- });
1428
- } catch (cause) {
1429
- const detail = cause instanceof Error ? cause.message : String(cause);
1430
- throw new Error(`injectedWalletSigner: eth_sign failed; enable raw-hash signing for deployment (${detail})`, { cause });
1431
- }
1432
- if (typeof signature !== "string") throw new Error("injectedWalletSigner: wallet returned a non-string signature");
1433
- if (!ECDSA_SIGNATURE_HEX.test(signature)) throw new Error("injectedWalletSigner: wallet returned invalid signature format");
1434
- if ((await recoverRawDigestSigner({
1435
- hash,
1436
- signature
1437
- })).toLowerCase() !== address.toLowerCase()) throw new Error("injectedWalletSigner: wallet signature did not recover the selected account for the raw SafeOp digest; use a raw-hash-capable wallet or @capxul/sdk/node localPrivateKeySigner for deployed flows");
1438
- return signature;
1439
- }
1440
- };
1441
- }
1442
- async function recoverRawDigestSigner(input) {
1350
+ const EpochMsSchema$1 = Schema.Number.pipe(Schema.refine((n) => Number.isSafeInteger(n) && n >= 0, { message: "must be a non-negative safe integer" }));
1351
+ /**
1352
+ * `DurationMs` schema. Mirrors `toDurationMs`: non-negative safe integer.
1353
+ */
1354
+ const DurationMsSchema$1 = Schema.Number.pipe(Schema.refine((n) => Number.isSafeInteger(n) && n >= 0, { message: "must be a non-negative safe integer" }));
1355
+ //#endregion
1356
+ //#region ../wire/src/bootstrap.ts
1357
+ const PUBLIC_POSTHOG_PROJECT_TOKEN = /^phc_[A-Za-z0-9_-]{1,191}$/u;
1358
+ const PostHogIngestOrigin = Schema.String.pipe(Schema.refine((value) => {
1443
1359
  try {
1444
- return toAddress(await recoverAddress(input));
1445
- } catch (cause) {
1446
- const detail = cause instanceof Error ? cause.message : String(cause);
1447
- throw new Error(`injectedWalletSigner: could not verify raw SafeOp digest signature (${detail})`, { cause });
1360
+ const url = new URL(value);
1361
+ return url.protocol === "https:" && url.username.length === 0 && url.password.length === 0 && url.pathname === "/" && url.search.length === 0 && url.hash.length === 0 && (url.hostname === "posthog.com" || url.hostname.endsWith(".posthog.com"));
1362
+ } catch {
1363
+ return false;
1448
1364
  }
1449
- }
1365
+ }, { message: "must be a credential-free PostHog HTTPS ingest origin" }));
1366
+ const EngineeringTelemetryBootstrapPolicy = Schema.Struct({
1367
+ host: PostHogIngestOrigin,
1368
+ projectToken: Schema.String.pipe(Schema.refine((value) => PUBLIC_POSTHOG_PROJECT_TOKEN.test(value), { message: "must be a public PostHog project token" })),
1369
+ capxulEnv: Schema.Union([
1370
+ Schema.Literal("development"),
1371
+ Schema.Literal("staging"),
1372
+ Schema.Literal("production"),
1373
+ Schema.Literal("local")
1374
+ ])
1375
+ });
1376
+ /**
1377
+ * `BootstrapEnvelope` v1.
1378
+ *
1379
+ * - `protocol`: discriminator that lets future protocols coexist on the
1380
+ * same endpoint without a wire-shape conflict.
1381
+ * - `version`: numeric version inside the protocol. Unknown versions MUST
1382
+ * fail decode with an INVALID_INPUT-class `CapxulError` at the SDK seam.
1383
+ * - `state`: the resolved bootstrap payload. Field shape matches the
1384
+ * `BootstrapResolution` port; the SDK consumer can pass `state`
1385
+ * directly (after `normalizeRuntimeUrl` on the two URL fields) into
1386
+ * the port without re-branding.
1387
+ */
1388
+ const BootstrapEnvelope = Schema.Struct({
1389
+ protocol: Schema.Literal("capxul.bootstrap"),
1390
+ version: Schema.Literal(1),
1391
+ state: Schema.Struct({
1392
+ applicationId: AppIdSchema$1,
1393
+ chainId: ChainIdSchema$1,
1394
+ sessionToken: SessionTokenSchema,
1395
+ issuedAt: EpochMsSchema$1,
1396
+ expiresIn: DurationMsSchema$1,
1397
+ authBaseUrl: Schema.String,
1398
+ convexUrl: Schema.String,
1399
+ siteBaseUrl: Schema.String,
1400
+ openfortPublishableKey: Schema.String,
1401
+ shieldPublishableKey: Schema.String,
1402
+ engineeringTelemetry: Schema.optional(EngineeringTelemetryBootstrapPolicy)
1403
+ })
1404
+ });
1450
1405
  //#endregion
1451
- //#region src/internal/invocation-observation.ts
1452
- const INVOCATION_OBSERVATION = Symbol("capxul.invocation-observation");
1453
- const FAILURE_INVOCATION_SNAPSHOT = Symbol("capxul.failure-invocation-snapshot");
1454
- /** @internal Attach one immutable, non-wire invocation snapshot to an SDK-owned object. */
1455
- function attachInvocationObservation(target, source) {
1456
- const snapshot = Object.freeze(source.context === void 0 ? { active: source.active } : {
1457
- active: source.active,
1458
- context: Object.freeze({ ...source.context })
1459
- });
1460
- Object.defineProperty(target, INVOCATION_OBSERVATION, {
1461
- configurable: false,
1462
- enumerable: false,
1463
- value: snapshot,
1464
- writable: false
1465
- });
1466
- return target;
1467
- }
1468
- /** @internal Read the snapshot without exposing its symbol or adding a wire field. */
1469
- function readInvocationObservation(source) {
1470
- if (typeof source !== "object" || source === null) return void 0;
1471
- return source[INVOCATION_OBSERVATION];
1472
- }
1473
- /** @internal Carry a snapshot across an SDK adapter projection without resolving again. */
1474
- function copyInvocationObservation(source, target) {
1475
- const snapshot = readInvocationObservation(source);
1476
- return snapshot === void 0 ? target : attachInvocationObservation(target, snapshot);
1477
- }
1478
- /** @internal Carry the public call-start delivery decision with its failure envelope. */
1479
- function markFailureInvocationSnapshot(failure, snapshot) {
1480
- Object.defineProperty(failure, FAILURE_INVOCATION_SNAPSHOT, {
1481
- configurable: false,
1482
- enumerable: false,
1483
- value: Object.freeze(snapshot),
1484
- writable: false
1485
- });
1486
- return failure;
1487
- }
1488
- /** @internal Read the call-start delivery decision; undefined means a direct adapter call. */
1489
- function readFailureInvocationSnapshot(failure) {
1490
- if (typeof failure !== "object" || failure === null) return void 0;
1491
- const snapshot = failure[FAILURE_INVOCATION_SNAPSHOT];
1492
- return typeof snapshot === "object" && snapshot !== null && "active" in snapshot ? snapshot : void 0;
1493
- }
1494
- //#endregion
1495
- //#region src/domain/machine/telemetry.ts
1496
- const definedEntries = (values) => Object.fromEntries(Object.entries(values).filter((entry) => entry[1] !== void 0));
1497
- const SAFE_ENGINEERING_ID = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u;
1498
- const safeEngineeringIdentifier = (value) => value !== void 0 && SAFE_ENGINEERING_ID.test(value) ? value : void 0;
1499
- const portFailureOutcome = (code) => code === "CANCELLED" ? "cancelled" : "failed";
1500
- function formatTraceparent(span) {
1501
- const traceId = span.traceId.toLowerCase();
1502
- const spanId = span.spanId.toLowerCase();
1503
- if (!/^[0-9a-f]{32}$/u.test(traceId) || !/^[0-9a-f]{16}$/u.test(spanId)) return void 0;
1504
- if (/^0+$/u.test(traceId) || /^0+$/u.test(spanId)) return void 0;
1505
- return `00-${traceId}-${spanId}-${span.sampled ? "01" : "00"}`;
1506
- }
1507
- /** Canonical, bounded fields for the one wide engineering log owned by P3. */
1508
- const transitionLogFields = (record) => {
1509
- const { correlation_id, journey_id, ...canonical } = record;
1510
- const safeCorrelation = safeEngineeringIdentifier(correlation_id);
1511
- const safeJourney = safeEngineeringIdentifier(journey_id);
1512
- return definedEntries({
1513
- ...canonical,
1514
- ...safeCorrelation === void 0 ? {} : { correlation_id: safeCorrelation },
1515
- ...safeJourney === void 0 ? {} : { journey_id: safeJourney }
1516
- });
1517
- };
1518
- /** The shell span owns transition/refusal classification and no other seam's fields. */
1519
- const transitionSpanFields = (record) => {
1520
- if (record.outcome === "applied") return {
1521
- machine: record.machine,
1522
- from: record.from,
1523
- event: record.event,
1524
- to: record.to
1525
- };
1526
- return {
1527
- machine: record.machine,
1528
- state: record.state,
1529
- event: record.event,
1530
- ...record.outcome === "refused" ? { refused: record.refusal_code } : {}
1531
- };
1532
- };
1406
+ //#region ../wire/src/functions.ts
1533
1407
  /**
1534
- * Emit engineering telemetry as an isolated side effect. Exporter/logger defects
1535
- * can never change the actor's transition, reply, or P3 observer cardinality.
1408
+ * Backend function paths, keyed by Convex module path then export name.
1409
+ *
1410
+ * Invariant (gate-enforced): `CAPXUL_FUNCTIONS[m][e] === \`${m}:${e}\``.
1536
1411
  */
1537
- const logIdentityTransition = (record) => (record.outcome === "applied" ? Effect.logInfo("identity.transition") : record.outcome === "failed" ? Effect.logError("identity.transition") : Effect.logWarning("identity.transition")).pipe(Effect.annotateLogs(transitionLogFields(record)), Effect.andThen(record.outcome === "applied" ? Effect.void : Effect.fail({ code: record.outcome === "refused" ? record.refusal_code : record.error_code })), Effect.withSpan("identity.transition"), Effect.annotateSpans(transitionSpanFields(record)), Effect.catchCause(() => Effect.void));
1538
- //#endregion
1539
- //#region src/domain/machine/shell.ts
1540
- const INVOCATION_PARENT_SPAN = Symbol("@capxul/sdk/identity-invocation-parent-span");
1541
- function carryInvocationParentSpan(controls, parent) {
1542
- return {
1543
- ...controls,
1544
- [INVOCATION_PARENT_SPAN]: parent
1545
- };
1546
- }
1547
- function withInvocationParentSpan(effect, controls) {
1548
- const parent = controls?.[INVOCATION_PARENT_SPAN];
1549
- return parent === void 0 ? effect : effect.pipe(Effect.withParentSpan(parent));
1550
- }
1551
- var ActorFailure = class extends Error {
1552
- reason;
1553
- machine;
1554
- state;
1555
- event;
1556
- details;
1557
- _tag = "ActorFailure";
1558
- constructor(reason, machine, state, event, details) {
1559
- super(`${machine}:${state}:${event} ${reason}`);
1560
- this.reason = reason;
1561
- this.machine = machine;
1562
- this.state = state;
1563
- this.event = event;
1564
- this.details = details;
1565
- this.name = "ActorFailure";
1566
- }
1567
- };
1568
- const duration = (startedAt) => Math.max(0, Date.now() - startedAt);
1569
- const failReply = (reply, failure) => reply === null ? Effect.void : Effect.asVoid(Deferred.fail(reply, failure));
1570
- const withCarriage = (controls) => ({
1571
- ...controls.correlation_id === void 0 ? {} : { correlation_id: controls.correlation_id },
1572
- ...controls.journey_id === void 0 ? {} : { journey_id: controls.journey_id }
1573
- });
1574
- const timeoutFailure = {
1575
- code: "PROVIDER_ERROR",
1576
- message: "Identity work timed out"
1577
- };
1578
- const cancelledFailure = {
1579
- code: "CANCELLED",
1580
- message: "Identity work cancelled"
1581
- };
1582
- const abort = (signal) => signal.aborted ? Effect.fail(cancelledFailure) : Effect.callback((resume) => {
1583
- const onAbort = () => resume(Effect.fail(cancelledFailure));
1584
- signal.addEventListener("abort", onAbort, { once: true });
1585
- return Effect.sync(() => signal.removeEventListener("abort", onAbort));
1586
- });
1587
- const control = (effect, controls) => {
1588
- let controlled = effect;
1589
- const deadlineDelay = controls.deadlineMs === void 0 ? void 0 : Math.max(0, controls.deadlineMs - Date.now());
1590
- const timeoutMs = controls.timeoutMs === void 0 ? deadlineDelay : deadlineDelay === void 0 ? controls.timeoutMs : Math.min(controls.timeoutMs, deadlineDelay);
1591
- if (timeoutMs !== void 0) controlled = controlled.pipe(Effect.timeoutOrElse({
1592
- duration: `${timeoutMs} millis`,
1593
- orElse: () => Effect.fail(timeoutFailure)
1594
- }));
1595
- if (controls.signal !== void 0) controlled = Effect.raceFirst(controlled, abort(controls.signal));
1596
- return controlled;
1597
- };
1598
- const boot = (spec, options = {}) => Effect.gen(function* () {
1599
- const mailbox = yield* Queue.unbounded();
1600
- const cell = yield* Ref.make(spec.initial);
1601
- const slotEpochs = /* @__PURE__ */ new Map();
1602
- const slots = /* @__PURE__ */ new Map();
1603
- const stateObservers = /* @__PURE__ */ new Set();
1604
- const transitionObservers = /* @__PURE__ */ new Set();
1605
- let bootTransitionObserver = options.onTransition;
1606
- let stopped = false;
1607
- const defect = (cause) => {
1608
- try {
1609
- options.onDefect?.(cause);
1610
- } catch {}
1611
- };
1612
- const emit = (record) => Effect.sync(() => {
1613
- if (bootTransitionObserver !== void 0) try {
1614
- bootTransitionObserver(record);
1615
- } catch (cause) {
1616
- bootTransitionObserver = void 0;
1617
- defect(cause);
1618
- }
1619
- for (const observer of transitionObservers) try {
1620
- observer(record);
1621
- } catch (cause) {
1622
- transitionObservers.delete(observer);
1623
- defect(cause);
1624
- }
1625
- }).pipe(Effect.andThen(logIdentityTransition(record)));
1626
- const notify = (state) => {
1627
- for (const observer of stateObservers) try {
1628
- observer(state);
1629
- } catch (cause) {
1630
- stateObservers.delete(observer);
1631
- defect(cause);
1632
- }
1633
- };
1634
- const makeFailure = (state, event, reason, details) => new ActorFailure(reason, spec.machine, spec.label(state), event._tag, details);
1635
- const nonApplied = (state, event, reason, outcome, env) => {
1636
- const slot = env.origin?.slot ?? spec.slot(event);
1637
- return copyInvocationObservation(env.invocation, {
1638
- machine: spec.machine,
1639
- state: spec.label(state),
1640
- event: event._tag,
1641
- slot,
1642
- epoch: env.origin?.epoch ?? slotEpochs.get(slot) ?? 0,
1643
- outcome,
1644
- duration_ms: duration(env.startedAt),
1645
- ...withCarriage(env.invocation),
1646
- ...outcome === "refused" ? { refusal_code: reason } : { error_code: reason }
1647
- });
1648
- };
1649
- const applied = (from, to, event, slot, epoch, env) => copyInvocationObservation(env.invocation, {
1650
- machine: spec.machine,
1651
- from: spec.label(from),
1652
- event: event._tag,
1653
- to: spec.label(to),
1654
- slot,
1655
- epoch,
1656
- outcome: "applied",
1657
- duration_ms: duration(env.startedAt),
1658
- ...withCarriage(env.invocation)
1659
- });
1660
- const finish = (cursor, state) => {
1661
- const held = slots.get(cursor.slot);
1662
- if (held === void 0 || held.cursor !== cursor) return Effect.void;
1663
- slots.delete(cursor.slot);
1664
- return Effect.asVoid(Deferred.succeed(held.reply, state));
1665
- };
1666
- const recoverFailure = (state, cursor, failure) => {
1667
- const event = spec.recoverFailure?.(state, cursor.event, failure);
1668
- if (event === void 0) return Effect.succeed(state);
1669
- const outcome = spec.transition(state, event);
1670
- if ("refused" in outcome) return Effect.succeed(state);
1671
- return Ref.set(cell, outcome.next).pipe(Effect.tap(() => {
1672
- return emit(applied(state, outcome.next, event, cursor.slot, cursor.epoch, {
1673
- invocation: cursor.invocation,
1674
- startedAt: cursor.startedAt
1675
- })).pipe(Effect.andThen(Effect.sync(() => {
1676
- notify(outcome.next);
1677
- })));
1678
- }), Effect.as(outcome.next));
1679
- };
1680
- const step = (env) => Effect.gen(function* () {
1681
- const state = yield* Ref.get(cell);
1682
- if (env.origin !== null && (slotEpochs.get(env.origin.slot) ?? 0) !== env.origin.epoch) {
1683
- const held = slots.get(env.origin.slot);
1684
- if (held === void 0 || held.cursor !== env.origin) return;
1685
- slots.delete(env.origin.slot);
1686
- const event = { _tag: env.origin.event };
1687
- yield* emit(nonApplied(state, event, "STALE_EPOCH", "refused", env));
1688
- yield* failReply(held.reply, makeFailure(state, event, "STALE_EPOCH"));
1689
- return;
1690
- }
1691
- if (env.origin !== null && env.event._tag === "~lane/exit") {
1692
- const exit = env.event;
1693
- const held = slots.get(env.origin.slot);
1694
- if (held === void 0 || held.cursor !== env.origin) return;
1695
- if (exit.defect !== void 0) {
1696
- slots.delete(env.origin.slot);
1697
- defect(exit.defect);
1698
- const event = { _tag: env.origin.event };
1699
- const failedState = yield* recoverFailure(state, env.origin, {
1700
- code: "WORK_DIED",
1701
- message: "Identity work died"
1702
- });
1703
- yield* emit(nonApplied(failedState, event, "WORK_DIED", "failed", env));
1704
- yield* failReply(held.reply, makeFailure(failedState, event, "WORK_DIED"));
1705
- return;
1706
- }
1707
- if (exit.failure !== void 0) {
1708
- slots.delete(env.origin.slot);
1709
- const outcome = exit.failure.code === "CANCELLED" ? "cancelled" : "failed";
1710
- const event = { _tag: env.origin.event };
1711
- const failedState = exit.failure === timeoutFailure || exit.failure === cancelledFailure ? yield* recoverFailure(state, env.origin, exit.failure) : state;
1712
- yield* emit(nonApplied(failedState, event, exit.failure.code, outcome, env));
1713
- const details = exit.failure === timeoutFailure ? { reason: "timeout" } : void 0;
1714
- yield* failReply(held.reply, makeFailure(failedState, event, exit.failure.code, details));
1715
- return;
1716
- }
1717
- yield* finish(env.origin, state);
1718
- return;
1719
- }
1720
- const outcome = spec.transition(state, env.event);
1721
- if ("refused" in outcome) {
1722
- yield* emit(nonApplied(state, env.event, outcome.refused, "refused", env));
1723
- yield* failReply(env.reply, makeFailure(state, env.event, outcome.refused));
1724
- return;
1725
- }
1726
- const next = outcome.next;
1727
- yield* Ref.set(cell, next);
1728
- const emitApplied = (slot, epoch) => emit(applied(state, next, env.event, slot, epoch, env));
1729
- if (env.origin !== null) {
1730
- yield* emitApplied(env.origin.slot, env.origin.epoch);
1731
- notify(next);
1732
- return;
1733
- }
1734
- for (const slot of spec.invalidates?.(env.event) ?? []) {
1735
- if (!slots.has(slot)) continue;
1736
- slotEpochs.set(slot, (slotEpochs.get(slot) ?? 0) + 1);
1737
- }
1738
- const work = env.reply === null ? void 0 : spec.work?.({
1739
- state: next,
1740
- event: env.event
1741
- });
1742
- if (work === void 0 || env.reply === null) {
1743
- const slot = spec.slot(env.event);
1744
- yield* emitApplied(slot, slotEpochs.get(slot) ?? 0);
1745
- notify(next);
1746
- if (env.reply !== null) yield* Deferred.succeed(env.reply, next).pipe(Effect.asVoid);
1747
- return;
1748
- }
1749
- const previous = slots.get(work.slot);
1750
- if (previous !== void 0) {
1751
- slots.delete(work.slot);
1752
- yield* Fiber.interrupt(previous.fiber);
1753
- const previousEvent = { _tag: previous.cursor.event };
1754
- const reason = previous.cursor.epoch === (slotEpochs.get(work.slot) ?? 0) ? "SUPERSEDED" : "STALE_EPOCH";
1755
- yield* emit(nonApplied(next, previousEvent, reason, "refused", {
1756
- origin: previous.cursor,
1757
- invocation: previous.cursor.invocation,
1758
- startedAt: previous.cursor.startedAt
1759
- }));
1760
- yield* failReply(previous.reply, makeFailure(next, previousEvent, reason));
1761
- }
1762
- const epoch = (slotEpochs.get(work.slot) ?? 0) + 1;
1763
- slotEpochs.set(work.slot, epoch);
1764
- const cursor = {
1765
- slot: work.slot,
1766
- epoch,
1767
- event: env.event._tag,
1768
- startedAt: env.startedAt,
1769
- invocation: env.invocation
1770
- };
1771
- yield* emitApplied(work.slot, epoch);
1772
- notify(next);
1773
- const send = (event) => Queue.offer(mailbox, {
1774
- event,
1775
- reply: null,
1776
- origin: cursor,
1777
- invocation: env.invocation,
1778
- startedAt: env.startedAt
1779
- }).pipe(Effect.asVoid);
1780
- const complete = (event) => Queue.offer(mailbox, {
1781
- event,
1782
- reply: null,
1783
- origin: cursor,
1784
- invocation: env.invocation,
1785
- startedAt: env.startedAt
1786
- }).pipe(Effect.asVoid);
1787
- const guarded = Effect.gen(function* () {
1788
- yield* Effect.annotateCurrentSpan({
1789
- slot: work.slot,
1790
- epoch,
1791
- port: work.port
1792
- });
1793
- return yield* control(work.run(send, env.invocation), env.invocation);
1794
- }).pipe(Effect.withSpan(`identity.work.${work.slot}`)).pipe(Effect.matchEffect({
1795
- onFailure: (failure) => complete({
1796
- _tag: "~lane/exit",
1797
- failure
1798
- }),
1799
- onSuccess: () => complete({ _tag: "~lane/exit" })
1800
- }), Effect.catchDefect((cause) => complete({
1801
- _tag: "~lane/exit",
1802
- defect: cause
1803
- })));
1804
- const fiber = yield* Effect.forkChild(guarded, { startImmediately: true });
1805
- slots.set(work.slot, {
1806
- fiber,
1807
- reply: env.reply,
1808
- cursor
1809
- });
1810
- });
1811
- const loop = yield* Effect.forkScoped(Effect.forever(Queue.take(mailbox).pipe(Effect.flatMap((env) => withInvocationParentSpan(step(env), env.invocation)))));
1812
- yield* Effect.addFinalizer(() => Effect.gen(function* () {
1813
- stopped = true;
1814
- yield* Fiber.interrupt(loop);
1815
- const state = yield* Ref.get(cell);
1816
- const outstanding = [...slots.values()];
1817
- slots.clear();
1818
- for (const held of outstanding) {
1819
- yield* Fiber.interrupt(held.fiber);
1820
- const env = {
1821
- origin: held.cursor,
1822
- invocation: held.cursor.invocation,
1823
- startedAt: held.cursor.startedAt
1824
- };
1825
- const event = { _tag: held.cursor.event };
1826
- yield* emit(nonApplied(state, event, "ACTOR_STOPPED", "refused", env));
1827
- yield* failReply(held.reply, makeFailure(state, event, "ACTOR_STOPPED"));
1828
- }
1829
- stateObservers.clear();
1830
- transitionObservers.clear();
1831
- yield* Queue.shutdown(mailbox);
1832
- }));
1833
- const offer = (event, reply, invocation) => {
1834
- const state = Ref.getUnsafe(cell);
1835
- if (stopped) return withInvocationParentSpan(emit(nonApplied(state, event, "ACTOR_STOPPED", "refused", {
1836
- origin: null,
1837
- invocation,
1838
- startedAt: Date.now()
1839
- })).pipe(Effect.andThen(Effect.fail(makeFailure(state, event, "ACTOR_STOPPED")))), invocation);
1840
- const startedAt = Date.now();
1841
- return Queue.offer(mailbox, {
1842
- event,
1843
- reply,
1844
- origin: null,
1845
- invocation,
1846
- startedAt
1847
- }).pipe(Effect.asVoid);
1848
- };
1849
- return {
1850
- ask: (event, invocation = {}) => Effect.gen(function* () {
1851
- const reply = yield* Deferred.make();
1852
- yield* offer(event, reply, invocation);
1853
- return yield* Deferred.await(reply);
1854
- }),
1855
- tell: (event, invocation = {}) => offer(event, null, invocation),
1856
- snapshot: () => Ref.getUnsafe(cell),
1857
- subscribe: (observer) => {
1858
- stateObservers.add(observer);
1859
- return () => {
1860
- stateObservers.delete(observer);
1861
- };
1862
- },
1863
- subscribeTransitions: (observer) => {
1864
- transitionObservers.add(observer);
1865
- return () => {
1866
- transitionObservers.delete(observer);
1867
- };
1868
- }
1869
- };
1870
- });
1871
- //#endregion
1872
- //#region src/surface/to-capxul-result.ts
1873
- /** The one place an Effect becomes a Promise on the public surface. */
1874
- async function runProgram(program, runPromise = Effect.runPromise) {
1875
- const result = await runPromise(program.pipe(Effect.catchDefect((defect) => Effect.fail(Errors.unknown(defect))), Effect.result));
1876
- if (Result.isFailure(result)) return {
1877
- ok: false,
1878
- error: result.failure
1879
- };
1880
- return {
1881
- ok: true,
1882
- value: result.success
1883
- };
1884
- }
1885
- async function toCapxulResult(program, layer) {
1886
- return runProgram(program.pipe(Effect.provide(layer)));
1887
- }
1888
- /**
1889
- * Signal-aware bridge for the Convex-backed method bundles. The adapters fail
1890
- * with `{ publicError }` rather than a bare `CapxulError`, so the wrapper is
1891
- * unwrapped here — once — instead of at every call site.
1892
- */
1893
- async function runIfActive(signal, operation, effect, runPromise = Effect.runPromise) {
1894
- if (signal?.aborted === true) return {
1895
- ok: false,
1896
- error: Errors.cancelled({ operation })
1897
- };
1898
- const program = effect().pipe(Effect.mapError((error) => error.publicError));
1899
- return runProgram(signal === void 0 ? program : Effect.raceFirst(program, Effect.callback((resume) => {
1900
- if (signal.aborted) {
1901
- resume(Effect.fail(Errors.cancelled({ operation })));
1902
- return Effect.void;
1903
- }
1904
- const onAbort = () => resume(Effect.fail(Errors.cancelled({ operation })));
1905
- signal.addEventListener("abort", onAbort, { once: true });
1906
- return Effect.sync(() => signal.removeEventListener("abort", onAbort));
1907
- })), runPromise);
1908
- }
1909
- //#endregion
1910
- //#region src/surface/_shared/effect-actor-bridge.ts
1911
- const facadeOutcome = (value) => {
1912
- if (typeof value !== "object" || value === null || !("ok" in value) || value.ok !== false) return "succeeded";
1913
- const reason = value.reason;
1914
- if (reason === "CANCELLED") return "cancelled";
1915
- return typeof reason === "string" && EXPECTED_OPERATION_OUTCOMES.has(reason) ? "refused" : "failed";
1916
- };
1917
- const rejectedFacadeOutcome = (cause) => {
1918
- if (cause instanceof CapxulError) return cause.code === "CANCELLED" ? "cancelled" : "failed";
1919
- if (typeof cause !== "object" || cause === null) return "failed";
1920
- const candidate = cause;
1921
- return candidate.code === "CANCELLED" || candidate.reason === "CANCELLED" || candidate.error?.code === "CANCELLED" || candidate.publicError?.code === "CANCELLED" ? "cancelled" : "failed";
1922
- };
1923
- const RESOLVED_FACADE_SPAN_FAILURE = Symbol("resolved facade span failure");
1924
- /** The single facade-span bridge consumed by the renderer-neutral React facade. */
1925
- async function runIdentityFacade(verb, controls, run, runPromise = Effect.runPromise) {
1926
- const correlationId = safeEngineeringIdentifier(controls?.correlation_id);
1927
- let resolvedFailure;
1928
- return runPromise(Effect.gen(function* () {
1929
- const parent = yield* Effect.currentSpan;
1930
- return yield* Effect.tryPromise({
1931
- try: () => run(carryInvocationParentSpan(controls, parent)),
1932
- catch: (cause) => cause
1933
- });
1934
- }).pipe(Effect.tapError((cause) => Effect.annotateCurrentSpan({ outcome: rejectedFacadeOutcome(cause) })), Effect.flatMap((value) => {
1935
- const outcome = facadeOutcome(value);
1936
- return Effect.annotateCurrentSpan({ outcome }).pipe(Effect.andThen(outcome === "failed" || outcome === "cancelled" ? Effect.sync(() => {
1937
- resolvedFailure = value;
1938
- }).pipe(Effect.andThen(Effect.fail(RESOLVED_FACADE_SPAN_FAILURE))) : Effect.succeed(value)));
1939
- }), Effect.withSpan(`identity.${verb}`), Effect.annotateSpans({
1940
- verb,
1941
- ...correlationId === void 0 ? {} : { correlation_id: correlationId }
1942
- }), Effect.catch((cause) => cause === RESOLVED_FACADE_SPAN_FAILURE ? Effect.succeed(resolvedFailure) : Effect.fail(cause))));
1943
- }
1944
- /**
1945
- * Bridge an Effect whose typed failure carries `{ publicError: CapxulError }`
1946
- * into the `Promise<CapxulResult<T>>` shape the consumer-facing method bundles
1947
- * return. Applies to every port that surfaces a `publicError` (smart-account,
1948
- * identity, auth-cache, account provision/deploy).
1949
- */
1950
- async function runPortEffect(effect, controls, operation = "port", runPromise = Effect.runPromise) {
1951
- const deadlineDelay = controls?.deadlineMs === void 0 ? void 0 : Math.max(0, controls.deadlineMs - Date.now());
1952
- const timeoutMs = controls?.timeoutMs === void 0 ? deadlineDelay : deadlineDelay === void 0 ? controls.timeoutMs : Math.min(controls.timeoutMs, deadlineDelay);
1953
- const observed = (timeoutMs === void 0 ? effect : effect.pipe(Effect.timeoutOrElse({
1954
- duration: `${timeoutMs} millis`,
1955
- orElse: () => Effect.fail({ publicError: Errors.providerTimeout("sdk", operation, timeoutMs) })
1956
- }))).pipe(Effect.tap(() => Effect.annotateCurrentSpan({ outcome: "succeeded" })), Effect.tapError((failure) => {
1957
- const mode = failure.publicError.details?.failure_mode;
1958
- return Effect.annotateCurrentSpan({
1959
- outcome: portFailureOutcome(failure.publicError.code),
1960
- failure_code: failure.publicError.code,
1961
- ...typeof mode === "string" ? { mode } : {}
1962
- });
1963
- }), Effect.tapDefect(() => Effect.annotateCurrentSpan({
1964
- outcome: "failed",
1965
- failure_code: "UNKNOWN"
1966
- })), Effect.withSpan(`identity.port.${operation}`));
1967
- return runIfActive(controls?.signal, operation, () => withInvocationParentSpan(observed, controls), runPromise);
1968
- }
1969
- /** Map machine-internal failure state onto the public SDK error vocabulary. */
1970
- function publicIdentityFailure(failure) {
1971
- if (failure.error !== void 0) return failure.error;
1972
- return new CapxulError(failure.code === "WORK_DIED" ? "UNKNOWN" : failure.code, failure.message, {
1973
- ...failure.mode === void 0 ? {} : { details: { failure_mode: failure.mode } },
1974
- layer: "identity"
1975
- });
1976
- }
1977
- /** The session carried by the actor's current snapshot, or `null`. */
1978
- function sessionFromActor(actor) {
1979
- return actor.authSession();
1980
- }
1981
- //#endregion
1982
- //#region src/surface/account-deps.ts
1983
- /** The single Context tag the account atom + `account.getStatus` resolve. */
1984
- var AccountDepsTag = class extends Context.Service()("@capxul/sdk/AccountDeps") {};
1985
- /** Wrap a bundle as the `Layer<AccountDepsTag>` the React provider consumes. */
1986
- function accountDepsLayer(deps) {
1987
- return Layer.succeed(AccountDepsTag, deps);
1988
- }
1989
- /**
1990
- * `account.getStatus` as a single Effect requiring ONLY `AccountDepsTag`.
1991
- * Faithful transcription of the current public method body (`account.ts`):
1992
- * resolve the session (actor first, then the resume-path `authCache`), then
1993
- * walk the readiness ladder. The `SmartAccountPort` failure narrows to its
1994
- * `publicError`; a consumer `AccountProvider.getAddress` rejection becomes a
1995
- * `providerError` (defending the public `CapxulResult` contract).
1996
- */
1997
- const accountStatusProgram = Effect.gen(function* () {
1998
- const deps = yield* AccountDepsTag;
1999
- const session = yield* Effect.promise(() => currentSession(deps.actor, deps.authCache));
2000
- if (session === null) return { status: "notAuthenticated" };
2001
- const current = yield* deps.smartAccountPort.loadByAuthUserId(session.authUserId).pipe(Effect.mapError((failure) => failure.publicError));
2002
- if (current !== null) return statusFromAccount(current, deps.requirement);
2003
- if (deps.requirement === "none") return {
2004
- status: "accountReady",
2005
- requirement: deps.requirement,
2006
- account: null,
2007
- deployment: { status: "counterfactual" }
2008
- };
2009
- const signer = deps.signer;
2010
- if (signer === void 0) return {
2011
- status: "accountRequired",
2012
- requirement: deps.requirement,
2013
- chainId: deps.chainId
2014
- };
2015
- const signerAddress = yield* Effect.tryPromise({
2016
- try: () => signer.getAddress(),
2017
- catch: (cause) => signerFailure(signer.source, "getAddress", cause)
2018
- });
2019
- return {
2020
- status: "accountProviderReady",
2021
- requirement: deps.requirement,
2022
- chainId: deps.chainId,
2023
- source: signer.source,
2024
- signerAddress
2025
- };
2026
- });
2027
- /**
2028
- * Resume-path session resolution. Consults the actor first; if the actor has
2029
- * no session (e.g. page refresh before any sign-in event), reads
2030
- * `authCache.getSession`. `AuthCacheError` is non-fatal at this read point —
2031
- * treat as "no session" and let the consumer's `auth.signIn` path resolve.
2032
- */
2033
- async function currentSession(actor, authCache) {
2034
- const fromActor = sessionFromActor(actor);
2035
- if (fromActor !== null) return fromActor;
2036
- if (authCache === void 0) return null;
2037
- const cached = await Effect.runPromise(Effect.result(authCache.getSession));
2038
- if (Result.isSuccess(cached)) {
2039
- if (cached.success !== null && typeof actor.restoreAuthSession === "function") {
2040
- const restored = await Effect.runPromise(Effect.result(actor.restoreAuthSession(cached.success)));
2041
- if (Result.isFailure(restored)) return null;
2042
- }
2043
- return cached.success;
2044
- }
2045
- return null;
2046
- }
2047
- /** Map a backend `SmartAccount` row onto the readiness `AccountStatus`. */
2048
- function statusFromAccount(account, requirement) {
2049
- if (requirement === "deployed") {
2050
- if (account.deployedAt === null) return {
2051
- status: "accountPrepared",
2052
- requirement,
2053
- account,
2054
- deployment: { status: "counterfactual" }
2055
- };
2056
- return {
2057
- status: "accountReady",
2058
- requirement,
2059
- account,
2060
- deployment: {
2061
- status: "deployed",
2062
- deployedAt: account.deployedAt
2063
- }
2064
- };
2065
- }
2066
- return {
2067
- status: "accountReady",
2068
- requirement,
2069
- account,
2070
- deployment: account.deployedAt === null ? { status: "counterfactual" } : {
2071
- status: "deployed",
2072
- deployedAt: account.deployedAt
2073
- }
2074
- };
2075
- }
2076
- //#endregion
2077
- //#region src/surface/account-lifecycle.ts
2078
- function isActiveProvisioningPhase(phase) {
2079
- return phase.status === "wallet" || phase.status === "identity" || phase.status === "provision" || phase.status === "deploy";
2080
- }
2081
- function isRequirementMet(status, requirement) {
2082
- if (requirement === "none") return status.status !== "notAuthenticated";
2083
- if (status.status !== "accountReady") return false;
2084
- return requirement !== "deployed" || status.deployment.status === "deployed";
2085
- }
2086
- function mapProvisioningFailureStep(step) {
2087
- switch (step) {
2088
- case "wallet": return "connecting";
2089
- case "identity": return "registering";
2090
- case "provision":
2091
- case "deploy": return "activating";
2092
- }
2093
- }
2094
- function mapProvisioningPhaseToSetupStep(phase) {
2095
- switch (phase.status) {
2096
- case "idle":
2097
- case "wallet": return "connecting";
2098
- case "identity": return "registering";
2099
- case "provision":
2100
- case "deploy": return "activating";
2101
- case "ready":
2102
- case "failed": return "connecting";
2103
- }
2104
- }
2105
- function isSettingUpLifecycle(lifecycle) {
2106
- return lifecycle.status === "settingUp";
2107
- }
2108
- function readyLifecycle(status, accountId) {
2109
- return {
2110
- status: "ready",
2111
- accountId,
2112
- canTransact: status.status === "accountReady" && status.deployment.status === "deployed"
2113
- };
2114
- }
2115
- function mapAccountLifecycle(input) {
2116
- const { status, phase, requirement, accountId } = input;
2117
- if (status.status === "notAuthenticated") return { status: "loading" };
2118
- if (phase.status === "failed") return {
2119
- status: "failed",
2120
- at: mapProvisioningFailureStep(phase.at),
2121
- error: phase.error
2122
- };
2123
- if (isRequirementMet(status, requirement) || phase.status === "ready") {
2124
- if (accountId === void 0) return { status: "loading" };
2125
- return readyLifecycle(status, accountId);
2126
- }
2127
- if (isActiveProvisioningPhase(phase) || phase.status === "idle") return {
2128
- status: "settingUp",
2129
- step: mapProvisioningPhaseToSetupStep(phase)
2130
- };
2131
- return { status: "loading" };
2132
- }
2133
- //#endregion
2134
- //#region ../wire/src/brands.ts
2135
- const lowercasedString = Schema.String.pipe(Schema.decodeTo(Schema.String, {
2136
- decode: SchemaGetter.transform((value) => value.toLowerCase()),
2137
- encode: SchemaGetter.transform((value) => value)
2138
- }));
2139
- const addressSchema = (name) => lowercasedString.pipe(Schema.refine((value) => EVM_ADDRESS_RE$1.test(value), { message: `${name} must be a 0x-prefixed EVM address` }));
2140
- const bytes32BrandSchema = (name) => lowercasedString.pipe(Schema.refine((value) => BYTES32_RE.test(value), { message: `${name} must be a 0x-prefixed bytes32 value` }));
2141
- /** Lowercase, 0x-prefixed bytes32 evidence without a public brand. */
2142
- const Bytes32Schema = bytes32BrandSchema("value");
2143
- const AddressSchema$1 = addressSchema("address");
2144
- const SafeAddressSchema = addressSchema("safe address");
2145
- const ModuleAddressSchema = addressSchema("module address");
2146
- const TxHashSchema$1 = bytes32BrandSchema("transaction hash");
2147
- const RoleKeySchema = bytes32BrandSchema("role key");
2148
- const AllowanceKeySchema = bytes32BrandSchema("allowance key");
2149
- const BlockNumberSchema$1 = Schema.Number.pipe(Schema.refine((value) => Number.isSafeInteger(value) && value >= 0, { message: "must be a non-negative safe integer" }));
2150
- const LogIndexSchema = Schema.Number.pipe(Schema.refine((value) => Number.isSafeInteger(value) && value >= 0, { message: "must be a non-negative safe integer" }));
2151
- const WeiAmountSchema$1 = Schema.String.pipe(Schema.refine((value) => WEI_RE.test(value), { message: "must be a non-negative integer string" }));
2152
- const SettlementIdSchema = bytes32BrandSchema("settlementId").pipe(Schema.refine((value) => value !== ZERO_BYTES32, { message: "settlementId must not be zero" }));
2153
- /**
2154
- * `AppId` schema. Mirrors `toAppId` from `@capxul/types`:
2155
- * `app_` + Crockford-base32 ULID (26 chars, first char in `[0-7]`).
2156
- */
2157
- const AppIdSchema$1 = Schema.String.pipe(Schema.refine((s) => APP_ID_RE.test(s), { message: "must be app_ plus a ULID" }));
2158
- /**
2159
- * `ChainId` schema. Mirrors `toChainId`: positive safe integer.
2160
- */
2161
- const ChainIdSchema$1 = Schema.Number.pipe(Schema.refine((n) => Number.isSafeInteger(n) && n > 0, { message: "must be a positive safe integer" }));
2162
- /**
2163
- * `CurrencyCode` schema. Mirrors `toCurrencyCode`: currently supported
2164
- * consumer-facing ISO-ish currency code set.
2165
- */
2166
- const CurrencyCodeSchema$1 = Schema.String.pipe(Schema.refine((s) => SUPPORTED_CURRENCY_CODES.includes(s), { message: "must be a supported currency code" }));
2167
- const DOCUMENT_HASH_HEX_RE = /^[0-9a-fA-F]{64}$/;
2168
- /**
2169
- * `DocumentHash` schema. Mirrors `toDocumentHash`: bare or 0x-prefixed bytes32,
2170
- * normalized to lowercase 0x-prefixed form.
2171
- */
2172
- const DocumentHashSchema = Schema.String.pipe(Schema.decodeTo(Schema.String, {
2173
- decode: SchemaGetter.transform((s) => {
2174
- const stripped = s.startsWith("0x") || s.startsWith("0X") ? s.slice(2) : s;
2175
- return DOCUMENT_HASH_HEX_RE.test(stripped) ? `0x${stripped.toLowerCase()}` : s;
2176
- }),
2177
- encode: SchemaGetter.transform((s) => s)
2178
- }), Schema.refine((s) => BYTES32_RE.test(s), { message: "must be 32 bytes of hex" }));
2179
- /** L2 orchestrated money evidence requires a nonzero Document hash. */
2180
- const NonzeroDocumentHashSchema = DocumentHashSchema.pipe(Schema.refine((value) => value !== ZERO_BYTES32, { message: "documentHash must not be zero" }));
2181
- /**
2182
- * `SessionToken` schema. Mirrors `toSessionToken`: non-empty string.
2183
- * Issuance source distinguishes SDK-handshake tokens from auth-session
2184
- * tokens; the brand itself is opaque.
2185
- */
2186
- const SessionTokenSchema = Schema.String.pipe(Schema.refine((s) => s.length > 0, { message: "must be a non-empty string" }));
2187
- /**
2188
- * `EpochMs` schema. Mirrors `toEpochMs`: non-negative safe integer.
2189
- */
2190
- const EpochMsSchema$1 = Schema.Number.pipe(Schema.refine((n) => Number.isSafeInteger(n) && n >= 0, { message: "must be a non-negative safe integer" }));
2191
- /**
2192
- * `DurationMs` schema. Mirrors `toDurationMs`: non-negative safe integer.
2193
- */
2194
- const DurationMsSchema$1 = Schema.Number.pipe(Schema.refine((n) => Number.isSafeInteger(n) && n >= 0, { message: "must be a non-negative safe integer" }));
2195
- //#endregion
2196
- //#region ../wire/src/bootstrap.ts
2197
- const PUBLIC_POSTHOG_PROJECT_TOKEN = /^phc_[A-Za-z0-9_-]{1,191}$/u;
2198
- const PostHogIngestOrigin = Schema.String.pipe(Schema.refine((value) => {
2199
- try {
2200
- const url = new URL(value);
2201
- return url.protocol === "https:" && url.username.length === 0 && url.password.length === 0 && url.pathname === "/" && url.search.length === 0 && url.hash.length === 0 && (url.hostname === "posthog.com" || url.hostname.endsWith(".posthog.com"));
2202
- } catch {
2203
- return false;
2204
- }
2205
- }, { message: "must be a credential-free PostHog HTTPS ingest origin" }));
2206
- const EngineeringTelemetryBootstrapPolicy = Schema.Struct({
2207
- host: PostHogIngestOrigin,
2208
- projectToken: Schema.String.pipe(Schema.refine((value) => PUBLIC_POSTHOG_PROJECT_TOKEN.test(value), { message: "must be a public PostHog project token" })),
2209
- capxulEnv: Schema.Union([
2210
- Schema.Literal("development"),
2211
- Schema.Literal("staging"),
2212
- Schema.Literal("production")
2213
- ])
2214
- });
2215
- /**
2216
- * `BootstrapEnvelope` v1.
2217
- *
2218
- * - `protocol`: discriminator that lets future protocols coexist on the
2219
- * same endpoint without a wire-shape conflict.
2220
- * - `version`: numeric version inside the protocol. Unknown versions MUST
2221
- * fail decode with an INVALID_INPUT-class `CapxulError` at the SDK seam.
2222
- * - `state`: the resolved bootstrap payload. Field shape matches the
2223
- * `BootstrapResolution` port; the SDK consumer can pass `state`
2224
- * directly (after `normalizeRuntimeUrl` on the two URL fields) into
2225
- * the port without re-branding.
2226
- */
2227
- const BootstrapEnvelope = Schema.Struct({
2228
- protocol: Schema.Literal("capxul.bootstrap"),
2229
- version: Schema.Literal(1),
2230
- state: Schema.Struct({
2231
- applicationId: AppIdSchema$1,
2232
- chainId: ChainIdSchema$1,
2233
- sessionToken: SessionTokenSchema,
2234
- issuedAt: EpochMsSchema$1,
2235
- expiresIn: DurationMsSchema$1,
2236
- authBaseUrl: Schema.String,
2237
- convexUrl: Schema.String,
2238
- siteBaseUrl: Schema.String,
2239
- openfortPublishableKey: Schema.String,
2240
- shieldPublishableKey: Schema.String,
2241
- engineeringTelemetry: Schema.optional(EngineeringTelemetryBootstrapPolicy)
2242
- })
2243
- });
2244
- //#endregion
2245
- //#region ../wire/src/functions.ts
2246
- /**
2247
- * Backend function paths, keyed by Convex module path then export name.
2248
- *
2249
- * Invariant (gate-enforced): `CAPXUL_FUNCTIONS[m][e] === \`${m}:${e}\``.
2250
- */
2251
- const CAPXUL_FUNCTIONS = {
2252
- "account/actions": {
2253
- faucetMint: "account/actions:faucetMint",
2254
- readBalance: "account/actions:readBalance"
2255
- },
2256
- "financialOps/addressBook": {
2257
- add: "financialOps/addressBook:add",
2258
- get: "financialOps/addressBook:get",
2259
- hide: "financialOps/addressBook:hide",
2260
- label: "financialOps/addressBook:label",
2261
- list: "financialOps/addressBook:list",
2262
- unhide: "financialOps/addressBook:unhide"
2263
- },
2264
- "financialOps/destinations": {
2265
- add: "financialOps/destinations:add",
2266
- list: "financialOps/destinations:list",
2267
- remove: "financialOps/destinations:remove"
2268
- },
2269
- "financialOps/mutations": { createPayee: "financialOps/mutations:createPayee" },
2270
- "financialOps/queries": {
2271
- depositInstructions: "financialOps/queries:depositInstructions",
2272
- getPayee: "financialOps/queries:getPayee",
2273
- getPayment: "financialOps/queries:getPayment",
2274
- listPayments: "financialOps/queries:listPayments",
2275
- me: "financialOps/queries:me",
2276
- renderStoredDocument: "financialOps/queries:renderStoredDocument",
2277
- resolveHandle: "financialOps/queries:resolveHandle",
2278
- resolvePayee: "financialOps/queries:resolvePayee",
2279
- verifyPaymentDocument: "financialOps/queries:verifyPaymentDocument"
2280
- },
2281
- "financialOps/requestsInbox": {
2282
- approve: "financialOps/requestsInbox:approve",
2283
- cancel: "financialOps/requestsInbox:cancel",
2284
- decline: "financialOps/requestsInbox:decline",
2285
- get: "financialOps/requestsInbox:get",
2286
- inboxList: "financialOps/requestsInbox:inboxList",
2287
- issue: "financialOps/requestsInbox:issue",
2288
- list: "financialOps/requestsInbox:list"
2289
- },
2290
- "identity/mutations": {
2291
- completeOnboarding: "identity/mutations:completeOnboarding",
2292
- create: "identity/mutations:create",
2293
- update: "identity/mutations:update"
2294
- },
2295
- "identity/queries": {
2296
- loadByAuthUserId: "identity/queries:loadByAuthUserId",
2297
- usernameAvailable: "identity/queries:usernameAvailable"
2298
- },
2299
- "holdings/actions": { current: "holdings/actions:current" },
2300
- "movement/activity": {
2301
- annotate: "movement/activity:annotate",
2302
- get: "movement/activity:get",
2303
- list: "movement/activity:list"
2304
- },
2305
- "moneyExecution/actions": {
2306
- preparePermissionExecution: "moneyExecution/actions:preparePermissionExecution",
2307
- preparePaymentExecution: "moneyExecution/actions:preparePaymentExecution",
2308
- submitPermissionExecution: "moneyExecution/actions:submitPermissionExecution",
2309
- submitPaymentExecution: "moneyExecution/actions:submitPaymentExecution"
2310
- },
2311
- "moneyExecution/paymentCommandActions": {
2312
- preparePaymentLifecycleExecution: "moneyExecution/paymentCommandActions:preparePaymentLifecycleExecution",
2313
- prepareOrganizationPaymentExecution: "moneyExecution/paymentCommandActions:prepareOrganizationPaymentExecution",
2314
- submitPaymentCommandExecution: "moneyExecution/paymentCommandActions:submitPaymentCommandExecution"
2315
- },
2316
- media: {
2317
- generateUploadUrl: "media:generateUploadUrl",
2318
- setOrgLogo: "media:setOrgLogo",
2319
- setProfileImage: "media:setProfileImage"
2320
- },
2321
- "org/actions": {
2322
- confirmBootstrap: "org/actions:confirmBootstrap",
2323
- detectAndAcceptPendingInvitations: "org/actions:detectAndAcceptPendingInvitations",
2324
- inviteMember: "org/actions:inviteMember",
2325
- prepareBootstrap: "org/actions:prepareBootstrap",
2326
- prepareFounderAccount: "org/actions:prepareFounderAccount",
2327
- readTreasury: "org/actions:readTreasury",
2328
- resumeBootstrapSubmission: "org/actions:resumeBootstrapSubmission",
2329
- submitBootstrap: "org/actions:submitBootstrap"
2330
- },
2331
- "permission/actions": { verify: "permission/actions:verify" },
2332
- "permission/mutations": { command: "permission/mutations:command" },
2333
- "permission/queries": {
2334
- authorize: "permission/queries:authorize",
2335
- read: "permission/queries:read"
2336
- },
2337
- "org/lifecycle": {
2338
- getProofReceipt: "org/lifecycle:getProofReceipt",
2339
- load: "org/lifecycle:load",
2340
- recordFailure: "org/lifecycle:recordFailure",
2341
- retry: "org/lifecycle:retry",
2342
- startOrResume: "org/lifecycle:startOrResume"
2343
- },
2344
- "org/mutations": {
2345
- createOrg: "org/mutations:createOrg",
2346
- resendInviteToken: "org/mutations:resendInviteToken"
2347
- },
2348
- "org/queries": {
2349
- listAll: "org/queries:listAll",
2350
- listMembersByOrgId: "org/queries:listMembersByOrgId",
2351
- listMine: "org/queries:listMine",
2352
- listRolesByOrgId: "org/queries:listRolesByOrgId",
2353
- loadByOrgId: "org/queries:loadByOrgId",
2354
- me: "org/queries:me"
2355
- },
2356
- "smartAccount/actions": {
2357
- claim: "smartAccount/actions:claim",
2358
- confirmDeployment: "smartAccount/actions:confirmDeployment"
2359
- },
2360
- "smartAccount/mutations": { provision: "smartAccount/mutations:provision" },
2361
- "smartAccount/queries": {
2362
- loadByAuthUserId: "smartAccount/queries:loadByAuthUserId",
2363
- loadBySmartAccountAddress: "smartAccount/queries:loadBySmartAccountAddress"
2364
- },
2365
- system: { health: "system:health" }
1412
+ const CAPXUL_FUNCTIONS = {
1413
+ "account/actions": {
1414
+ faucetMint: "account/actions:faucetMint",
1415
+ readBalance: "account/actions:readBalance"
1416
+ },
1417
+ "financialOps/addressBook": {
1418
+ add: "financialOps/addressBook:add",
1419
+ get: "financialOps/addressBook:get",
1420
+ hide: "financialOps/addressBook:hide",
1421
+ label: "financialOps/addressBook:label",
1422
+ list: "financialOps/addressBook:list",
1423
+ unhide: "financialOps/addressBook:unhide"
1424
+ },
1425
+ "financialOps/destinations": {
1426
+ add: "financialOps/destinations:add",
1427
+ list: "financialOps/destinations:list",
1428
+ remove: "financialOps/destinations:remove"
1429
+ },
1430
+ "financialOps/mutations": { createPayee: "financialOps/mutations:createPayee" },
1431
+ "financialOps/queries": {
1432
+ depositInstructions: "financialOps/queries:depositInstructions",
1433
+ getPayee: "financialOps/queries:getPayee",
1434
+ getPayment: "financialOps/queries:getPayment",
1435
+ listPayments: "financialOps/queries:listPayments",
1436
+ me: "financialOps/queries:me",
1437
+ renderStoredDocument: "financialOps/queries:renderStoredDocument",
1438
+ resolveHandle: "financialOps/queries:resolveHandle",
1439
+ resolvePayee: "financialOps/queries:resolvePayee",
1440
+ verifyPaymentDocument: "financialOps/queries:verifyPaymentDocument"
1441
+ },
1442
+ "financialOps/requestsInbox": {
1443
+ approve: "financialOps/requestsInbox:approve",
1444
+ cancel: "financialOps/requestsInbox:cancel",
1445
+ decline: "financialOps/requestsInbox:decline",
1446
+ get: "financialOps/requestsInbox:get",
1447
+ inboxList: "financialOps/requestsInbox:inboxList",
1448
+ issue: "financialOps/requestsInbox:issue",
1449
+ list: "financialOps/requestsInbox:list"
1450
+ },
1451
+ "identity/mutations": {
1452
+ completeOnboarding: "identity/mutations:completeOnboarding",
1453
+ create: "identity/mutations:create",
1454
+ update: "identity/mutations:update"
1455
+ },
1456
+ "identity/queries": {
1457
+ loadByAuthUserId: "identity/queries:loadByAuthUserId",
1458
+ usernameAvailable: "identity/queries:usernameAvailable"
1459
+ },
1460
+ "holdings/actions": { current: "holdings/actions:current" },
1461
+ "movement/activity": {
1462
+ annotate: "movement/activity:annotate",
1463
+ get: "movement/activity:get",
1464
+ list: "movement/activity:list",
1465
+ summary: "movement/activity:summary"
1466
+ },
1467
+ "moneyExecution/actions": {
1468
+ preparePermissionExecution: "moneyExecution/actions:preparePermissionExecution",
1469
+ preparePaymentExecution: "moneyExecution/actions:preparePaymentExecution",
1470
+ submitPermissionExecution: "moneyExecution/actions:submitPermissionExecution",
1471
+ submitPaymentExecution: "moneyExecution/actions:submitPaymentExecution"
1472
+ },
1473
+ "moneyExecution/paymentCommandActions": {
1474
+ preparePaymentLifecycleExecution: "moneyExecution/paymentCommandActions:preparePaymentLifecycleExecution",
1475
+ prepareOrganizationPaymentExecution: "moneyExecution/paymentCommandActions:prepareOrganizationPaymentExecution",
1476
+ submitPaymentCommandExecution: "moneyExecution/paymentCommandActions:submitPaymentCommandExecution"
1477
+ },
1478
+ media: {
1479
+ generateUploadUrl: "media:generateUploadUrl",
1480
+ setOrgLogo: "media:setOrgLogo",
1481
+ setProfileImage: "media:setProfileImage"
1482
+ },
1483
+ "org/actions": {
1484
+ confirmBootstrap: "org/actions:confirmBootstrap",
1485
+ detectAndAcceptPendingInvitations: "org/actions:detectAndAcceptPendingInvitations",
1486
+ inviteMember: "org/actions:inviteMember",
1487
+ prepareBootstrap: "org/actions:prepareBootstrap",
1488
+ prepareFounderAccount: "org/actions:prepareFounderAccount",
1489
+ readTreasury: "org/actions:readTreasury",
1490
+ resumeBootstrapSubmission: "org/actions:resumeBootstrapSubmission",
1491
+ submitBootstrap: "org/actions:submitBootstrap"
1492
+ },
1493
+ "permission/actions": { verify: "permission/actions:verify" },
1494
+ "permission/mutations": { command: "permission/mutations:command" },
1495
+ "permission/queries": {
1496
+ authorize: "permission/queries:authorize",
1497
+ read: "permission/queries:read"
1498
+ },
1499
+ "org/lifecycle": {
1500
+ getProofReceipt: "org/lifecycle:getProofReceipt",
1501
+ load: "org/lifecycle:load",
1502
+ recordFailure: "org/lifecycle:recordFailure",
1503
+ retry: "org/lifecycle:retry",
1504
+ startOrResume: "org/lifecycle:startOrResume"
1505
+ },
1506
+ "org/mutations": {
1507
+ createOrg: "org/mutations:createOrg",
1508
+ resendInviteToken: "org/mutations:resendInviteToken"
1509
+ },
1510
+ "org/queries": {
1511
+ listAll: "org/queries:listAll",
1512
+ listMembersByOrgId: "org/queries:listMembersByOrgId",
1513
+ listMine: "org/queries:listMine",
1514
+ listRolesByOrgId: "org/queries:listRolesByOrgId",
1515
+ loadByOrgId: "org/queries:loadByOrgId",
1516
+ me: "org/queries:me"
1517
+ },
1518
+ "smartAccount/actions": {
1519
+ claim: "smartAccount/actions:claim",
1520
+ confirmDeployment: "smartAccount/actions:confirmDeployment"
1521
+ },
1522
+ "smartAccount/mutations": { provision: "smartAccount/mutations:provision" },
1523
+ "smartAccount/queries": {
1524
+ loadByAuthUserId: "smartAccount/queries:loadByAuthUserId",
1525
+ loadBySmartAccountAddress: "smartAccount/queries:loadBySmartAccountAddress"
1526
+ },
1527
+ system: { health: "system:health" }
2366
1528
  };
2367
1529
  //#endregion
2368
1530
  //#region ../wire/src/status.ts
@@ -3579,170 +2741,1010 @@ const PermissionResourceSchema = Schema.Union([
3579
2741
  PermissionAssignmentSchema,
3580
2742
  Schema.Array(PermissionSchema)
3581
2743
  ]);
3582
- Schema.Struct({
3583
- status: Schema.Literal("prepared"),
3584
- commandId: PaymentCommandIdSchema,
3585
- operation: PermissionOperationSchema,
3586
- expectedRevision: nonNegativeInteger("expectedRevision"),
3587
- userOpSenderSafe: SafeAddressSchema,
3588
- organizationSafe: SafeAddressSchema,
3589
- calls: Schema.Array(PermissionChainCallSchema),
3590
- value: PermissionResourceSchema
3591
- });
3592
- Schema.Struct({
3593
- orgId: nonEmpty("orgId"),
3594
- signerAddress: AddressSchema$1,
3595
- command: PermissionCommandSchema
2744
+ Schema.Struct({
2745
+ status: Schema.Literal("prepared"),
2746
+ commandId: PaymentCommandIdSchema,
2747
+ operation: PermissionOperationSchema,
2748
+ expectedRevision: nonNegativeInteger("expectedRevision"),
2749
+ userOpSenderSafe: SafeAddressSchema,
2750
+ organizationSafe: SafeAddressSchema,
2751
+ calls: Schema.Array(PermissionChainCallSchema),
2752
+ value: PermissionResourceSchema
2753
+ });
2754
+ Schema.Struct({
2755
+ orgId: nonEmpty("orgId"),
2756
+ signerAddress: AddressSchema$1,
2757
+ command: PermissionCommandSchema
2758
+ });
2759
+ Schema.Struct({
2760
+ executionId: MoneyExecutionIdSchema,
2761
+ commandId: PaymentCommandIdSchema,
2762
+ operation: PermissionOperationSchema,
2763
+ orgId: nonEmpty("orgId"),
2764
+ command: PermissionCommandSchema,
2765
+ expectedRevision: nonNegativeInteger("expectedRevision"),
2766
+ chainId: BaseSepoliaChainIdSchema,
2767
+ signerAddress: AddressSchema$1,
2768
+ userOpSenderSafe: SafeAddressSchema,
2769
+ digest: Bytes32Schema,
2770
+ value: PermissionResourceSchema
2771
+ });
2772
+ Schema.Struct({
2773
+ status: Schema.Literal("submitted"),
2774
+ executionId: MoneyExecutionIdSchema,
2775
+ commandId: PaymentCommandIdSchema,
2776
+ userOpHash: Bytes32Schema,
2777
+ value: PermissionResourceSchema
2778
+ });
2779
+ `
2780
+ .capxul-doc{--ink:#1d1d1f;--muted:#6e6e73;--line:#e7e7ea;--accent:#0a7d4b;--bg:#fff;
2781
+ font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Inter,system-ui,sans-serif;
2782
+ color:var(--ink);background:var(--bg);max-width:44rem;margin:0 auto;padding:2.75rem 3rem;
2783
+ border:1px solid var(--line);border-radius:16px;box-shadow:0 1px 2px rgba(0,0,0,.04),0 12px 32px rgba(0,0,0,.06);
2784
+ line-height:1.5;font-size:15px;overflow-wrap:anywhere;word-break:break-word}
2785
+ .capxul-doc *{box-sizing:border-box;min-width:0}
2786
+ .capxul-doc .doc-header{display:flex;flex-direction:column;gap:1.25rem;padding-bottom:1.5rem;border-bottom:1px solid var(--line);margin-bottom:1.75rem}
2787
+ .capxul-doc .doc-brand{display:flex;align-items:center;gap:.5rem;color:var(--accent);font-weight:600}
2788
+ .capxul-doc .doc-brand-mark{font-size:1.1rem}
2789
+ .capxul-doc .doc-brand-name{letter-spacing:.02em}
2790
+ .capxul-doc .doc-headline{display:flex;align-items:baseline;justify-content:space-between;gap:1rem;flex-wrap:wrap}
2791
+ .capxul-doc .doc-title{font-size:1.9rem;font-weight:700;letter-spacing:-.02em;margin:0}
2792
+ .capxul-doc .doc-badge{font-size:.7rem;font-weight:700;text-transform:uppercase;letter-spacing:.08em;
2793
+ color:var(--accent);background:rgba(10,125,75,.1);padding:.3rem .6rem;border-radius:999px;max-width:100%;text-align:right}
2794
+ .capxul-doc .doc-parties{display:grid;grid-template-columns:1fr 1fr;gap:1.25rem;margin-bottom:1.75rem}
2795
+ .capxul-doc .doc-party{display:flex;flex-direction:column;gap:.15rem}
2796
+ .capxul-doc .doc-party-label{font-size:.72rem;text-transform:uppercase;letter-spacing:.06em;color:var(--muted)}
2797
+ .capxul-doc .doc-party-name{font-weight:600}
2798
+ .capxul-doc .doc-meta{display:flex;flex-direction:column;gap:.4rem;margin-bottom:1.75rem}
2799
+ .capxul-doc .doc-meta-row{display:flex;justify-content:space-between;gap:1rem;font-size:.92rem}
2800
+ .capxul-doc .doc-meta-label{color:var(--muted);flex-shrink:0}
2801
+ .capxul-doc .doc-meta-value{font-weight:500;text-align:right}
2802
+ .capxul-doc time{color:var(--ink);font-variant-numeric:tabular-nums}
2803
+ .capxul-doc .doc-line-items{width:100%;border-collapse:collapse;margin:.5rem 0 1.5rem;font-size:.92rem}
2804
+ .capxul-doc .doc-line-items th{text-align:left;font-size:.7rem;text-transform:uppercase;letter-spacing:.05em;
2805
+ color:var(--muted);font-weight:600;padding:.5rem .25rem;border-bottom:1px solid var(--line)}
2806
+ .capxul-doc .doc-line-items td{padding:.7rem .25rem;border-bottom:1px solid var(--line)}
2807
+ .capxul-doc .doc-li-qty,.capxul-doc .doc-li-unit,.capxul-doc .doc-li-total{text-align:right;font-variant-numeric:tabular-nums;white-space:nowrap}
2808
+ .capxul-doc .doc-li-desc{width:100%}
2809
+ .capxul-doc .doc-totals{display:flex;flex-direction:column;gap:.5rem;margin-top:.5rem}
2810
+ .capxul-doc .doc-total-line{display:flex;justify-content:space-between;align-items:baseline;gap:1rem}
2811
+ .capxul-doc .doc-total-label{color:var(--muted)}
2812
+ .capxul-doc .doc-total-deduction .doc-amount-value{color:var(--muted)}
2813
+ .capxul-doc .doc-total-grand{border-top:2px solid var(--ink);margin-top:.5rem;padding-top:.75rem;font-size:1.15rem}
2814
+ .capxul-doc .doc-total-grand .doc-amount-value{font-weight:700}
2815
+ .capxul-doc .doc-amount-value{font-variant-numeric:tabular-nums;font-weight:600}
2816
+ .capxul-doc .doc-hero{text-align:center;padding:1.5rem 0 2rem}
2817
+ .capxul-doc .doc-hero-label{font-size:.72rem;text-transform:uppercase;letter-spacing:.06em;color:var(--muted)}
2818
+ .capxul-doc .doc-hero-amount{font-size:2.6rem;font-weight:700;letter-spacing:-.02em;margin-top:.35rem}
2819
+ .capxul-doc .doc-note{color:var(--ink);background:#f7f7f8;border-radius:10px;padding:.9rem 1.1rem;margin:0}
2820
+ .capxul-doc .doc-dest-address{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:.9rem}
2821
+ .capxul-doc .doc-footer{margin-top:1.75rem;padding-top:1.25rem;border-top:1px solid var(--line);color:var(--muted);font-size:.9rem}
2822
+ .capxul-doc code{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:.82rem;color:var(--muted);word-break:break-all}
2823
+ @media (max-width:540px){.capxul-doc{padding:1.75rem 1.25rem}.capxul-doc .doc-parties{grid-template-columns:1fr}}
2824
+ `.trim();
2825
+ //#endregion
2826
+ //#region ../wire/src/secret-material.ts
2827
+ const SENSITIVE_MATERIAL_PATTERNS = [
2828
+ /0x[a-fA-F0-9]{40,}/u,
2829
+ /(?:^|[^a-fA-F0-9])[a-fA-F0-9]{64}(?:$|[^a-fA-F0-9])/u,
2830
+ /eyJ[A-Za-z0-9_-]*\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+/u,
2831
+ /(?:(?: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
2832
+ ];
2833
+ /**
2834
+ * Reject: does the value carry any known secret material? Best effort — callers
2835
+ * drop the whole value on a match; a false negative is a leak, a false positive
2836
+ * merely omits an observation field.
2837
+ */
2838
+ function containsSensitiveMaterial(value) {
2839
+ return SENSITIVE_MATERIAL_PATTERNS.some((pattern) => pattern.test(value));
2840
+ }
2841
+ //#endregion
2842
+ //#region ../wire/src/observation-context.ts
2843
+ /** Single bounded HTTP carrier used before a Convex action envelope exists. */
2844
+ const OBSERVATION_CONTEXT_HEADER = "x-capxul-observation-context";
2845
+ const FIELD_RULES = {
2846
+ application: {
2847
+ maxLength: 64,
2848
+ pattern: /^[A-Za-z0-9][A-Za-z0-9._-]*$/u
2849
+ },
2850
+ applicationId: {
2851
+ maxLength: 30,
2852
+ pattern: APP_ID_RE
2853
+ },
2854
+ release: {
2855
+ maxLength: 128,
2856
+ pattern: /^[A-Za-z0-9][A-Za-z0-9._+@:/-]*$/u
2857
+ },
2858
+ sessionId: {
2859
+ maxLength: 128,
2860
+ pattern: /^[A-Za-z0-9][A-Za-z0-9._:-]*$/u
2861
+ },
2862
+ organizationId: {
2863
+ maxLength: 128,
2864
+ pattern: /^[A-Za-z0-9][A-Za-z0-9._:-]*$/u
2865
+ },
2866
+ journeyId: {
2867
+ maxLength: 128,
2868
+ pattern: /^[A-Za-z0-9][A-Za-z0-9._:-]*$/u
2869
+ },
2870
+ correlationId: {
2871
+ maxLength: 128,
2872
+ pattern: /^[A-Za-z0-9][A-Za-z0-9._:-]*$/u
2873
+ },
2874
+ anonymousId: {
2875
+ maxLength: 128,
2876
+ pattern: /^anon_[A-Za-z0-9-]+$/u
2877
+ },
2878
+ traceparent: {
2879
+ maxLength: 55,
2880
+ pattern: /^00-[0-9a-f]{32}-[0-9a-f]{16}-0[01]$/u
2881
+ }
2882
+ };
2883
+ /**
2884
+ * Copy only the canonical allowlist and silently omit malformed/sensitive
2885
+ * values. Observation metadata is best effort and may never reject a domain
2886
+ * operation.
2887
+ */
2888
+ function sanitizeObservationContext(input) {
2889
+ if (typeof input !== "object" || input === null || Array.isArray(input)) return void 0;
2890
+ const source = input;
2891
+ const sanitized = {};
2892
+ for (const field of Object.keys(FIELD_RULES)) {
2893
+ const value = source[field];
2894
+ if (!isSafeField(field, value)) continue;
2895
+ sanitized[field] = value;
2896
+ }
2897
+ return Object.keys(sanitized).length === 0 ? void 0 : sanitized;
2898
+ }
2899
+ /** Encode only the sanitized allowlist; absence stays absence. */
2900
+ function encodeObservationContextHeader(input) {
2901
+ const sanitized = sanitizeObservationContext(input);
2902
+ return sanitized === void 0 ? void 0 : JSON.stringify(sanitized);
2903
+ }
2904
+ function isSafeField(field, value) {
2905
+ if (typeof value !== "string") return false;
2906
+ const rule = FIELD_RULES[field];
2907
+ return value.length > 0 && value.length <= rule.maxLength && value === value.trim() && !value.includes("://") && !containsSensitiveMaterial(value) && rule.pattern.test(value);
2908
+ }
2909
+ //#endregion
2910
+ //#region src/surface/_shared/provisioning-telemetry.ts
2911
+ async function emitProvisioningTelemetry(telemetry, smartAccount) {
2912
+ if (telemetry === void 0) return;
2913
+ await Effect.runPromise(telemetry.emit({
2914
+ name: "provisioning_safe_created",
2915
+ props: { safe_address: smartAccount.smartAccountAddress }
2916
+ }).pipe(Effect.catch((cause) => Effect.sync(() => reportProvisioningTelemetryFailure("error", smartAccount, cause))), Effect.catchDefect((cause) => Effect.sync(() => reportProvisioningTelemetryFailure("defect", smartAccount, cause)))));
2917
+ }
2918
+ function reportProvisioningTelemetryFailure(kind, smartAccount, cause) {
2919
+ if (!isProvisioningTelemetryDebugEnabled()) return;
2920
+ globalThis.console?.warn?.("[capxul] provisioning telemetry dropped", {
2921
+ kind,
2922
+ safeAddress: smartAccount.smartAccountAddress,
2923
+ cause
2924
+ });
2925
+ }
2926
+ function isProvisioningTelemetryDebugEnabled() {
2927
+ return globalThis.process?.env?.CAPXUL_DEBUG_TELEMETRY === "1";
2928
+ }
2929
+ //#endregion
2930
+ //#region src/telemetry/get-failure-mode.ts
2931
+ /**
2932
+ * The five canonical {@link FailureMode} members, for runtime membership checks.
2933
+ * Single source of truth — tests assert against this exact set so the taxonomy
2934
+ * and its guard can never drift apart.
2935
+ */
2936
+ const FAILURE_MODES = new Set([
2937
+ "auth-origin-mismatch",
2938
+ "stale-openfort-cache",
2939
+ "app-env-allowlist",
2940
+ "no-secure-context",
2941
+ "unknown"
2942
+ ]);
2943
+ function isFailureMode(value) {
2944
+ return typeof value === "string" && FAILURE_MODES.has(value);
2945
+ }
2946
+ /**
2947
+ * Extract the structured {@link FailureMode} from a CapxulError.
2948
+ *
2949
+ * Reads `details.failure_mode` and returns it only when it is one of the five
2950
+ * canonical members; any other value (a legacy free string, a typo, a
2951
+ * non-string) yields `undefined` so downstream telemetry never reports an
2952
+ * unrecognised cause.
2953
+ */
2954
+ function getFailureMode(error) {
2955
+ if (!isCapxulError(error)) return void 0;
2956
+ const details = error.details;
2957
+ if (details === void 0) return void 0;
2958
+ return isFailureMode(details.failure_mode) ? details.failure_mode : void 0;
2959
+ }
2960
+ /**
2961
+ * Resolve the canonical {@link FailureMode} for a `$exception`, guaranteeing a
2962
+ * taxonomy member is always returned — never `undefined`, never a free string.
2963
+ *
2964
+ * Resolution order:
2965
+ * 1. the structured mode on the error's `details.failure_mode` (already guarded);
2966
+ * 2. a caller-supplied `contextFailureMode`, but ONLY when it passes the same
2967
+ * runtime membership check — the static `FailureMode` type is erased at
2968
+ * runtime, so an operation string (e.g. `"signer-get-address"`) injected via
2969
+ * a JS caller or `as` cast is rejected here rather than leaking to telemetry;
2970
+ * 3. `"unknown"` otherwise, so an unclassifiable error is still tagged with a
2971
+ * canonical value instead of being emitted with no `failure_mode` at all.
2972
+ */
2973
+ function resolveFailureMode(error, contextFailureMode) {
2974
+ return getFailureMode(error) ?? (isFailureMode(contextFailureMode) ? contextFailureMode : "unknown");
2975
+ }
2976
+ //#endregion
2977
+ //#region src/signer.ts
2978
+ const EVM_ADDRESS_HEX = /^0x[0-9a-fA-F]{40}$/;
2979
+ const ECDSA_SIGNATURE_HEX = /^0x[0-9a-fA-F]{130}$/;
2980
+ const SAFE_OP_DIGEST_HEX = /^0x[0-9a-fA-F]{64}$/;
2981
+ /**
2982
+ * The readiness store a signer reports when it runs no readiness cycle. It
2983
+ * fails CLOSED: a node key signer or an injected wallet never claims `ready`,
2984
+ * so a gate that reads this never opens on a guess.
2985
+ */
2986
+ const UNOBSERVABLE_SIGNER_STATUS = {
2987
+ status: () => "unknown",
2988
+ subscribe: () => () => void 0
2989
+ };
2990
+ /**
2991
+ * Walk an error and its `cause` links once each. A self-referential chain
2992
+ * terminates. Both signer predicates read the chain, so they read it here.
2993
+ */
2994
+ function* causeChain(cause) {
2995
+ const seen = /* @__PURE__ */ new Set();
2996
+ let current = cause;
2997
+ while (typeof current === "object" && current !== null && !seen.has(current)) {
2998
+ seen.add(current);
2999
+ yield current;
3000
+ current = current.cause;
3001
+ }
3002
+ }
3003
+ /** Fold a signer throw into the public error contract. */
3004
+ function signerFailure(source, operation, cause) {
3005
+ let failureMode;
3006
+ for (const link of causeChain(cause)) {
3007
+ if (link instanceof CapxulError && link.code === "SIGNER_REJECTED") return link;
3008
+ failureMode = getFailureMode(link) ?? failureMode;
3009
+ const error = link;
3010
+ if (error.code === 4001 || error.error === "passkey_user_cancelled") return Errors.signerRejected({
3011
+ source,
3012
+ cause
3013
+ });
3014
+ }
3015
+ return Errors.providerError("signer", operation, cause, failureMode === void 0 ? void 0 : { failure_mode: failureMode });
3016
+ }
3017
+ /**
3018
+ * Browser `CapxulSigner` backed by an injected EIP-1193 wallet (MetaMask, etc.).
3019
+ * Signs the SafeOp digest via `eth_sign`, then verifies the returned signature
3020
+ * recovers the selected account against that raw digest. Wallets that prefix
3021
+ * `eth_sign` payloads are rejected before the backend submits an invalid SafeOp.
3022
+ * The node key signer lives in `@capxul/sdk/node` (`localPrivateKeySigner`).
3023
+ */
3024
+ function injectedWalletSigner(provider) {
3025
+ const resolveAddress = async () => {
3026
+ const accounts = await provider.request({ method: "eth_requestAccounts" });
3027
+ const first = Array.isArray(accounts) ? accounts[0] : void 0;
3028
+ if (typeof first !== "string") throw new Error("injectedWalletSigner: wallet returned no accounts");
3029
+ if (!EVM_ADDRESS_HEX.test(first)) throw new Error("injectedWalletSigner: wallet returned invalid address format");
3030
+ return toAddress(first);
3031
+ };
3032
+ return {
3033
+ source: "injected-eip1193",
3034
+ getAddress: resolveAddress,
3035
+ async signUserOpHash(hash) {
3036
+ if (!SAFE_OP_DIGEST_HEX.test(hash)) throw new Error("injectedWalletSigner: SafeOp digest must be a 0x-prefixed 32-byte hex");
3037
+ const address = await resolveAddress();
3038
+ let signature;
3039
+ try {
3040
+ signature = await provider.request({
3041
+ method: "eth_sign",
3042
+ params: [address, hash]
3043
+ });
3044
+ } catch (cause) {
3045
+ const detail = cause instanceof Error ? cause.message : String(cause);
3046
+ throw new Error(`injectedWalletSigner: eth_sign failed; enable raw-hash signing for deployment (${detail})`, { cause });
3047
+ }
3048
+ if (typeof signature !== "string") throw new Error("injectedWalletSigner: wallet returned a non-string signature");
3049
+ if (!ECDSA_SIGNATURE_HEX.test(signature)) throw new Error("injectedWalletSigner: wallet returned invalid signature format");
3050
+ if ((await recoverRawDigestSigner({
3051
+ hash,
3052
+ signature
3053
+ })).toLowerCase() !== address.toLowerCase()) throw new Error("injectedWalletSigner: wallet signature did not recover the selected account for the raw SafeOp digest; use a raw-hash-capable wallet or @capxul/sdk/node localPrivateKeySigner for deployed flows");
3054
+ return signature;
3055
+ }
3056
+ };
3057
+ }
3058
+ async function recoverRawDigestSigner(input) {
3059
+ try {
3060
+ return toAddress(await recoverAddress(input));
3061
+ } catch (cause) {
3062
+ const detail = cause instanceof Error ? cause.message : String(cause);
3063
+ throw new Error(`injectedWalletSigner: could not verify raw SafeOp digest signature (${detail})`, { cause });
3064
+ }
3065
+ }
3066
+ //#endregion
3067
+ //#region src/internal/invocation-observation.ts
3068
+ const INVOCATION_OBSERVATION = Symbol("capxul.invocation-observation");
3069
+ const FAILURE_INVOCATION_SNAPSHOT = Symbol("capxul.failure-invocation-snapshot");
3070
+ /** @internal Attach one immutable, non-wire invocation snapshot to an SDK-owned object. */
3071
+ function attachInvocationObservation(target, source) {
3072
+ const snapshot = Object.freeze(source.context === void 0 ? { active: source.active } : {
3073
+ active: source.active,
3074
+ context: Object.freeze({ ...source.context })
3075
+ });
3076
+ Object.defineProperty(target, INVOCATION_OBSERVATION, {
3077
+ configurable: false,
3078
+ enumerable: false,
3079
+ value: snapshot,
3080
+ writable: false
3081
+ });
3082
+ return target;
3083
+ }
3084
+ /** @internal Read the snapshot without exposing its symbol or adding a wire field. */
3085
+ function readInvocationObservation(source) {
3086
+ if (typeof source !== "object" || source === null) return void 0;
3087
+ return source[INVOCATION_OBSERVATION];
3088
+ }
3089
+ /** @internal Carry a snapshot across an SDK adapter projection without resolving again. */
3090
+ function copyInvocationObservation(source, target) {
3091
+ const snapshot = readInvocationObservation(source);
3092
+ return snapshot === void 0 ? target : attachInvocationObservation(target, snapshot);
3093
+ }
3094
+ /** @internal Carry the public call-start delivery decision with its failure envelope. */
3095
+ function markFailureInvocationSnapshot(failure, snapshot) {
3096
+ Object.defineProperty(failure, FAILURE_INVOCATION_SNAPSHOT, {
3097
+ configurable: false,
3098
+ enumerable: false,
3099
+ value: Object.freeze(snapshot),
3100
+ writable: false
3101
+ });
3102
+ return failure;
3103
+ }
3104
+ /** @internal Read the call-start delivery decision; undefined means a direct adapter call. */
3105
+ function readFailureInvocationSnapshot(failure) {
3106
+ if (typeof failure !== "object" || failure === null) return void 0;
3107
+ const snapshot = failure[FAILURE_INVOCATION_SNAPSHOT];
3108
+ return typeof snapshot === "object" && snapshot !== null && "active" in snapshot ? snapshot : void 0;
3109
+ }
3110
+ //#endregion
3111
+ //#region src/domain/machine/telemetry.ts
3112
+ const definedEntries = (values) => Object.fromEntries(Object.entries(values).filter((entry) => entry[1] !== void 0));
3113
+ const SAFE_ENGINEERING_ID = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u;
3114
+ const safeEngineeringIdentifier = (value) => value !== void 0 && SAFE_ENGINEERING_ID.test(value) ? value : void 0;
3115
+ const portFailureOutcome = (code) => code === "CANCELLED" ? "cancelled" : "failed";
3116
+ function formatTraceparent(span) {
3117
+ const traceId = span.traceId.toLowerCase();
3118
+ const spanId = span.spanId.toLowerCase();
3119
+ if (!/^[0-9a-f]{32}$/u.test(traceId) || !/^[0-9a-f]{16}$/u.test(spanId)) return void 0;
3120
+ if (/^0+$/u.test(traceId) || /^0+$/u.test(spanId)) return void 0;
3121
+ return `00-${traceId}-${spanId}-${span.sampled ? "01" : "00"}`;
3122
+ }
3123
+ /** Canonical, bounded fields for the one wide engineering log owned by P3. */
3124
+ const transitionLogFields = (record) => {
3125
+ const { correlation_id, journey_id, ...canonical } = record;
3126
+ const safeCorrelation = safeEngineeringIdentifier(correlation_id);
3127
+ const safeJourney = safeEngineeringIdentifier(journey_id);
3128
+ return definedEntries({
3129
+ ...canonical,
3130
+ ...safeCorrelation === void 0 ? {} : { correlation_id: safeCorrelation },
3131
+ ...safeJourney === void 0 ? {} : { journey_id: safeJourney }
3132
+ });
3133
+ };
3134
+ /** The shell span owns transition/refusal classification and no other seam's fields. */
3135
+ const transitionSpanFields = (record) => {
3136
+ if (record.outcome === "applied") return {
3137
+ machine: record.machine,
3138
+ from: record.from,
3139
+ event: record.event,
3140
+ to: record.to
3141
+ };
3142
+ return {
3143
+ machine: record.machine,
3144
+ state: record.state,
3145
+ event: record.event,
3146
+ ...record.outcome === "refused" ? { refused: record.refusal_code } : {}
3147
+ };
3148
+ };
3149
+ /**
3150
+ * Emit engineering telemetry as an isolated side effect. Exporter/logger defects
3151
+ * can never change the actor's transition, reply, or P3 observer cardinality.
3152
+ */
3153
+ const logIdentityTransition = (record) => (record.outcome === "applied" ? Effect.logInfo("identity.transition") : record.outcome === "failed" ? Effect.logError("identity.transition") : Effect.logWarning("identity.transition")).pipe(Effect.annotateLogs(transitionLogFields(record)), Effect.andThen(record.outcome === "applied" ? Effect.void : Effect.fail({ code: record.outcome === "refused" ? record.refusal_code : record.error_code })), Effect.withSpan("identity.transition"), Effect.annotateSpans(transitionSpanFields(record)), Effect.catchCause(() => Effect.void));
3154
+ //#endregion
3155
+ //#region src/domain/machine/shell.ts
3156
+ const INVOCATION_PARENT_SPAN = Symbol("@capxul/sdk/identity-invocation-parent-span");
3157
+ function carryInvocationParentSpan(controls, parent) {
3158
+ return {
3159
+ ...controls,
3160
+ [INVOCATION_PARENT_SPAN]: parent
3161
+ };
3162
+ }
3163
+ function withInvocationParentSpan(effect, controls) {
3164
+ const parent = controls?.[INVOCATION_PARENT_SPAN];
3165
+ return parent === void 0 ? effect : effect.pipe(Effect.withParentSpan(parent));
3166
+ }
3167
+ var ActorFailure = class extends Error {
3168
+ reason;
3169
+ machine;
3170
+ state;
3171
+ event;
3172
+ details;
3173
+ _tag = "ActorFailure";
3174
+ constructor(reason, machine, state, event, details) {
3175
+ super(`${machine}:${state}:${event} ${reason}`);
3176
+ this.reason = reason;
3177
+ this.machine = machine;
3178
+ this.state = state;
3179
+ this.event = event;
3180
+ this.details = details;
3181
+ this.name = "ActorFailure";
3182
+ }
3183
+ };
3184
+ const duration = (startedAt) => Math.max(0, Date.now() - startedAt);
3185
+ const failReply = (reply, failure) => reply === null ? Effect.void : Effect.asVoid(Deferred.fail(reply, failure));
3186
+ const withCarriage = (controls) => ({
3187
+ ...controls.correlation_id === void 0 ? {} : { correlation_id: controls.correlation_id },
3188
+ ...controls.journey_id === void 0 ? {} : { journey_id: controls.journey_id }
3596
3189
  });
3597
- Schema.Struct({
3598
- executionId: MoneyExecutionIdSchema,
3599
- commandId: PaymentCommandIdSchema,
3600
- operation: PermissionOperationSchema,
3601
- orgId: nonEmpty("orgId"),
3602
- command: PermissionCommandSchema,
3603
- expectedRevision: nonNegativeInteger("expectedRevision"),
3604
- chainId: BaseSepoliaChainIdSchema,
3605
- signerAddress: AddressSchema$1,
3606
- userOpSenderSafe: SafeAddressSchema,
3607
- digest: Bytes32Schema,
3608
- value: PermissionResourceSchema
3190
+ const timeoutFailure = {
3191
+ code: "PROVIDER_ERROR",
3192
+ message: "Identity work timed out"
3193
+ };
3194
+ const cancelledFailure = {
3195
+ code: "CANCELLED",
3196
+ message: "Identity work cancelled"
3197
+ };
3198
+ const abort = (signal) => signal.aborted ? Effect.fail(cancelledFailure) : Effect.callback((resume) => {
3199
+ const onAbort = () => resume(Effect.fail(cancelledFailure));
3200
+ signal.addEventListener("abort", onAbort, { once: true });
3201
+ return Effect.sync(() => signal.removeEventListener("abort", onAbort));
3609
3202
  });
3610
- Schema.Struct({
3611
- status: Schema.Literal("submitted"),
3612
- executionId: MoneyExecutionIdSchema,
3613
- commandId: PaymentCommandIdSchema,
3614
- userOpHash: Bytes32Schema,
3615
- value: PermissionResourceSchema
3203
+ const control = (effect, controls) => {
3204
+ let controlled = effect;
3205
+ const deadlineDelay = controls.deadlineMs === void 0 ? void 0 : Math.max(0, controls.deadlineMs - Date.now());
3206
+ const timeoutMs = controls.timeoutMs === void 0 ? deadlineDelay : deadlineDelay === void 0 ? controls.timeoutMs : Math.min(controls.timeoutMs, deadlineDelay);
3207
+ if (timeoutMs !== void 0) controlled = controlled.pipe(Effect.timeoutOrElse({
3208
+ duration: `${timeoutMs} millis`,
3209
+ orElse: () => Effect.fail(timeoutFailure)
3210
+ }));
3211
+ if (controls.signal !== void 0) controlled = Effect.raceFirst(controlled, abort(controls.signal));
3212
+ return controlled;
3213
+ };
3214
+ const boot = (spec, options = {}) => Effect.gen(function* () {
3215
+ const mailbox = yield* Queue.unbounded();
3216
+ const cell = yield* Ref.make(spec.initial);
3217
+ const slotEpochs = /* @__PURE__ */ new Map();
3218
+ const slots = /* @__PURE__ */ new Map();
3219
+ const stateObservers = /* @__PURE__ */ new Set();
3220
+ const transitionObservers = /* @__PURE__ */ new Set();
3221
+ let bootTransitionObserver = options.onTransition;
3222
+ let stopped = false;
3223
+ const defect = (cause) => {
3224
+ try {
3225
+ options.onDefect?.(cause);
3226
+ } catch {}
3227
+ };
3228
+ const emit = (record) => Effect.sync(() => {
3229
+ if (bootTransitionObserver !== void 0) try {
3230
+ bootTransitionObserver(record);
3231
+ } catch (cause) {
3232
+ bootTransitionObserver = void 0;
3233
+ defect(cause);
3234
+ }
3235
+ for (const observer of transitionObservers) try {
3236
+ observer(record);
3237
+ } catch (cause) {
3238
+ transitionObservers.delete(observer);
3239
+ defect(cause);
3240
+ }
3241
+ }).pipe(Effect.andThen(logIdentityTransition(record)));
3242
+ const notify = (state) => {
3243
+ for (const observer of stateObservers) try {
3244
+ observer(state);
3245
+ } catch (cause) {
3246
+ stateObservers.delete(observer);
3247
+ defect(cause);
3248
+ }
3249
+ };
3250
+ const makeFailure = (state, event, reason, details) => new ActorFailure(reason, spec.machine, spec.label(state), event._tag, details);
3251
+ const nonApplied = (state, event, reason, outcome, env) => {
3252
+ const slot = env.origin?.slot ?? spec.slot(event);
3253
+ return copyInvocationObservation(env.invocation, {
3254
+ machine: spec.machine,
3255
+ state: spec.label(state),
3256
+ event: event._tag,
3257
+ slot,
3258
+ epoch: env.origin?.epoch ?? slotEpochs.get(slot) ?? 0,
3259
+ outcome,
3260
+ duration_ms: duration(env.startedAt),
3261
+ ...withCarriage(env.invocation),
3262
+ ...outcome === "refused" ? { refusal_code: reason } : { error_code: reason }
3263
+ });
3264
+ };
3265
+ const applied = (from, to, event, slot, epoch, env) => copyInvocationObservation(env.invocation, {
3266
+ machine: spec.machine,
3267
+ from: spec.label(from),
3268
+ event: event._tag,
3269
+ to: spec.label(to),
3270
+ slot,
3271
+ epoch,
3272
+ outcome: "applied",
3273
+ duration_ms: duration(env.startedAt),
3274
+ ...withCarriage(env.invocation)
3275
+ });
3276
+ const finish = (cursor, state) => {
3277
+ const held = slots.get(cursor.slot);
3278
+ if (held === void 0 || held.cursor !== cursor) return Effect.void;
3279
+ slots.delete(cursor.slot);
3280
+ return Effect.asVoid(Deferred.succeed(held.reply, state));
3281
+ };
3282
+ const recoverFailure = (state, cursor, failure) => {
3283
+ const event = spec.recoverFailure?.(state, cursor.event, failure);
3284
+ if (event === void 0) return Effect.succeed(state);
3285
+ const outcome = spec.transition(state, event);
3286
+ if ("refused" in outcome) return Effect.succeed(state);
3287
+ return Ref.set(cell, outcome.next).pipe(Effect.tap(() => {
3288
+ return emit(applied(state, outcome.next, event, cursor.slot, cursor.epoch, {
3289
+ invocation: cursor.invocation,
3290
+ startedAt: cursor.startedAt
3291
+ })).pipe(Effect.andThen(Effect.sync(() => {
3292
+ notify(outcome.next);
3293
+ })));
3294
+ }), Effect.as(outcome.next));
3295
+ };
3296
+ const step = (env) => Effect.gen(function* () {
3297
+ const state = yield* Ref.get(cell);
3298
+ if (env.origin !== null && (slotEpochs.get(env.origin.slot) ?? 0) !== env.origin.epoch) {
3299
+ const held = slots.get(env.origin.slot);
3300
+ if (held === void 0 || held.cursor !== env.origin) return;
3301
+ slots.delete(env.origin.slot);
3302
+ const event = { _tag: env.origin.event };
3303
+ yield* emit(nonApplied(state, event, "STALE_EPOCH", "refused", env));
3304
+ yield* failReply(held.reply, makeFailure(state, event, "STALE_EPOCH"));
3305
+ return;
3306
+ }
3307
+ if (env.origin !== null && env.event._tag === "~lane/exit") {
3308
+ const exit = env.event;
3309
+ const held = slots.get(env.origin.slot);
3310
+ if (held === void 0 || held.cursor !== env.origin) return;
3311
+ if (exit.defect !== void 0) {
3312
+ slots.delete(env.origin.slot);
3313
+ defect(exit.defect);
3314
+ const event = { _tag: env.origin.event };
3315
+ const failedState = yield* recoverFailure(state, env.origin, {
3316
+ code: "WORK_DIED",
3317
+ message: "Identity work died"
3318
+ });
3319
+ yield* emit(nonApplied(failedState, event, "WORK_DIED", "failed", env));
3320
+ yield* failReply(held.reply, makeFailure(failedState, event, "WORK_DIED"));
3321
+ return;
3322
+ }
3323
+ if (exit.failure !== void 0) {
3324
+ slots.delete(env.origin.slot);
3325
+ const outcome = exit.failure.code === "CANCELLED" ? "cancelled" : "failed";
3326
+ const event = { _tag: env.origin.event };
3327
+ const failedState = exit.failure === timeoutFailure || exit.failure === cancelledFailure ? yield* recoverFailure(state, env.origin, exit.failure) : state;
3328
+ yield* emit(nonApplied(failedState, event, exit.failure.code, outcome, env));
3329
+ const details = exit.failure === timeoutFailure ? { reason: "timeout" } : void 0;
3330
+ yield* failReply(held.reply, makeFailure(failedState, event, exit.failure.code, details));
3331
+ return;
3332
+ }
3333
+ yield* finish(env.origin, state);
3334
+ return;
3335
+ }
3336
+ const outcome = spec.transition(state, env.event);
3337
+ if ("refused" in outcome) {
3338
+ yield* emit(nonApplied(state, env.event, outcome.refused, "refused", env));
3339
+ yield* failReply(env.reply, makeFailure(state, env.event, outcome.refused));
3340
+ return;
3341
+ }
3342
+ const next = outcome.next;
3343
+ yield* Ref.set(cell, next);
3344
+ const emitApplied = (slot, epoch) => emit(applied(state, next, env.event, slot, epoch, env));
3345
+ if (env.origin !== null) {
3346
+ yield* emitApplied(env.origin.slot, env.origin.epoch);
3347
+ notify(next);
3348
+ return;
3349
+ }
3350
+ for (const slot of spec.invalidates?.(env.event) ?? []) {
3351
+ if (!slots.has(slot)) continue;
3352
+ slotEpochs.set(slot, (slotEpochs.get(slot) ?? 0) + 1);
3353
+ }
3354
+ const work = env.reply === null ? void 0 : spec.work?.({
3355
+ state: next,
3356
+ event: env.event
3357
+ });
3358
+ if (work === void 0 || env.reply === null) {
3359
+ const slot = spec.slot(env.event);
3360
+ yield* emitApplied(slot, slotEpochs.get(slot) ?? 0);
3361
+ notify(next);
3362
+ if (env.reply !== null) yield* Deferred.succeed(env.reply, next).pipe(Effect.asVoid);
3363
+ return;
3364
+ }
3365
+ const previous = slots.get(work.slot);
3366
+ if (previous !== void 0) {
3367
+ slots.delete(work.slot);
3368
+ yield* Fiber.interrupt(previous.fiber);
3369
+ const previousEvent = { _tag: previous.cursor.event };
3370
+ const reason = previous.cursor.epoch === (slotEpochs.get(work.slot) ?? 0) ? "SUPERSEDED" : "STALE_EPOCH";
3371
+ yield* emit(nonApplied(next, previousEvent, reason, "refused", {
3372
+ origin: previous.cursor,
3373
+ invocation: previous.cursor.invocation,
3374
+ startedAt: previous.cursor.startedAt
3375
+ }));
3376
+ yield* failReply(previous.reply, makeFailure(next, previousEvent, reason));
3377
+ }
3378
+ const epoch = (slotEpochs.get(work.slot) ?? 0) + 1;
3379
+ slotEpochs.set(work.slot, epoch);
3380
+ const cursor = {
3381
+ slot: work.slot,
3382
+ epoch,
3383
+ event: env.event._tag,
3384
+ startedAt: env.startedAt,
3385
+ invocation: env.invocation
3386
+ };
3387
+ yield* emitApplied(work.slot, epoch);
3388
+ notify(next);
3389
+ const send = (event) => Queue.offer(mailbox, {
3390
+ event,
3391
+ reply: null,
3392
+ origin: cursor,
3393
+ invocation: env.invocation,
3394
+ startedAt: env.startedAt
3395
+ }).pipe(Effect.asVoid);
3396
+ const complete = (event) => Queue.offer(mailbox, {
3397
+ event,
3398
+ reply: null,
3399
+ origin: cursor,
3400
+ invocation: env.invocation,
3401
+ startedAt: env.startedAt
3402
+ }).pipe(Effect.asVoid);
3403
+ const guarded = Effect.gen(function* () {
3404
+ yield* Effect.annotateCurrentSpan({
3405
+ slot: work.slot,
3406
+ epoch,
3407
+ port: work.port
3408
+ });
3409
+ return yield* control(work.run(send, env.invocation), env.invocation);
3410
+ }).pipe(Effect.withSpan(`identity.work.${work.slot}`)).pipe(Effect.matchEffect({
3411
+ onFailure: (failure) => complete({
3412
+ _tag: "~lane/exit",
3413
+ failure
3414
+ }),
3415
+ onSuccess: () => complete({ _tag: "~lane/exit" })
3416
+ }), Effect.catchDefect((cause) => complete({
3417
+ _tag: "~lane/exit",
3418
+ defect: cause
3419
+ })));
3420
+ const fiber = yield* Effect.forkChild(guarded, { startImmediately: true });
3421
+ slots.set(work.slot, {
3422
+ fiber,
3423
+ reply: env.reply,
3424
+ cursor
3425
+ });
3426
+ });
3427
+ const loop = yield* Effect.forkScoped(Effect.forever(Queue.take(mailbox).pipe(Effect.flatMap((env) => withInvocationParentSpan(step(env), env.invocation)))));
3428
+ yield* Effect.addFinalizer(() => Effect.gen(function* () {
3429
+ stopped = true;
3430
+ yield* Fiber.interrupt(loop);
3431
+ const state = yield* Ref.get(cell);
3432
+ const outstanding = [...slots.values()];
3433
+ slots.clear();
3434
+ for (const held of outstanding) {
3435
+ yield* Fiber.interrupt(held.fiber);
3436
+ const env = {
3437
+ origin: held.cursor,
3438
+ invocation: held.cursor.invocation,
3439
+ startedAt: held.cursor.startedAt
3440
+ };
3441
+ const event = { _tag: held.cursor.event };
3442
+ yield* emit(nonApplied(state, event, "ACTOR_STOPPED", "refused", env));
3443
+ yield* failReply(held.reply, makeFailure(state, event, "ACTOR_STOPPED"));
3444
+ }
3445
+ stateObservers.clear();
3446
+ transitionObservers.clear();
3447
+ yield* Queue.shutdown(mailbox);
3448
+ }));
3449
+ const offer = (event, reply, invocation) => {
3450
+ const state = Ref.getUnsafe(cell);
3451
+ if (stopped) return withInvocationParentSpan(emit(nonApplied(state, event, "ACTOR_STOPPED", "refused", {
3452
+ origin: null,
3453
+ invocation,
3454
+ startedAt: Date.now()
3455
+ })).pipe(Effect.andThen(Effect.fail(makeFailure(state, event, "ACTOR_STOPPED")))), invocation);
3456
+ const startedAt = Date.now();
3457
+ return Queue.offer(mailbox, {
3458
+ event,
3459
+ reply,
3460
+ origin: null,
3461
+ invocation,
3462
+ startedAt
3463
+ }).pipe(Effect.asVoid);
3464
+ };
3465
+ return {
3466
+ ask: (event, invocation = {}) => Effect.gen(function* () {
3467
+ const reply = yield* Deferred.make();
3468
+ yield* offer(event, reply, invocation);
3469
+ return yield* Deferred.await(reply);
3470
+ }),
3471
+ tell: (event, invocation = {}) => offer(event, null, invocation),
3472
+ snapshot: () => Ref.getUnsafe(cell),
3473
+ subscribe: (observer) => {
3474
+ stateObservers.add(observer);
3475
+ return () => {
3476
+ stateObservers.delete(observer);
3477
+ };
3478
+ },
3479
+ subscribeTransitions: (observer) => {
3480
+ transitionObservers.add(observer);
3481
+ return () => {
3482
+ transitionObservers.delete(observer);
3483
+ };
3484
+ }
3485
+ };
3616
3486
  });
3617
- `
3618
- .capxul-doc{--ink:#1d1d1f;--muted:#6e6e73;--line:#e7e7ea;--accent:#0a7d4b;--bg:#fff;
3619
- font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Inter,system-ui,sans-serif;
3620
- color:var(--ink);background:var(--bg);max-width:44rem;margin:0 auto;padding:2.75rem 3rem;
3621
- border:1px solid var(--line);border-radius:16px;box-shadow:0 1px 2px rgba(0,0,0,.04),0 12px 32px rgba(0,0,0,.06);
3622
- line-height:1.5;font-size:15px;overflow-wrap:anywhere;word-break:break-word}
3623
- .capxul-doc *{box-sizing:border-box;min-width:0}
3624
- .capxul-doc .doc-header{display:flex;flex-direction:column;gap:1.25rem;padding-bottom:1.5rem;border-bottom:1px solid var(--line);margin-bottom:1.75rem}
3625
- .capxul-doc .doc-brand{display:flex;align-items:center;gap:.5rem;color:var(--accent);font-weight:600}
3626
- .capxul-doc .doc-brand-mark{font-size:1.1rem}
3627
- .capxul-doc .doc-brand-name{letter-spacing:.02em}
3628
- .capxul-doc .doc-headline{display:flex;align-items:baseline;justify-content:space-between;gap:1rem;flex-wrap:wrap}
3629
- .capxul-doc .doc-title{font-size:1.9rem;font-weight:700;letter-spacing:-.02em;margin:0}
3630
- .capxul-doc .doc-badge{font-size:.7rem;font-weight:700;text-transform:uppercase;letter-spacing:.08em;
3631
- color:var(--accent);background:rgba(10,125,75,.1);padding:.3rem .6rem;border-radius:999px;max-width:100%;text-align:right}
3632
- .capxul-doc .doc-parties{display:grid;grid-template-columns:1fr 1fr;gap:1.25rem;margin-bottom:1.75rem}
3633
- .capxul-doc .doc-party{display:flex;flex-direction:column;gap:.15rem}
3634
- .capxul-doc .doc-party-label{font-size:.72rem;text-transform:uppercase;letter-spacing:.06em;color:var(--muted)}
3635
- .capxul-doc .doc-party-name{font-weight:600}
3636
- .capxul-doc .doc-meta{display:flex;flex-direction:column;gap:.4rem;margin-bottom:1.75rem}
3637
- .capxul-doc .doc-meta-row{display:flex;justify-content:space-between;gap:1rem;font-size:.92rem}
3638
- .capxul-doc .doc-meta-label{color:var(--muted);flex-shrink:0}
3639
- .capxul-doc .doc-meta-value{font-weight:500;text-align:right}
3640
- .capxul-doc time{color:var(--ink);font-variant-numeric:tabular-nums}
3641
- .capxul-doc .doc-line-items{width:100%;border-collapse:collapse;margin:.5rem 0 1.5rem;font-size:.92rem}
3642
- .capxul-doc .doc-line-items th{text-align:left;font-size:.7rem;text-transform:uppercase;letter-spacing:.05em;
3643
- color:var(--muted);font-weight:600;padding:.5rem .25rem;border-bottom:1px solid var(--line)}
3644
- .capxul-doc .doc-line-items td{padding:.7rem .25rem;border-bottom:1px solid var(--line)}
3645
- .capxul-doc .doc-li-qty,.capxul-doc .doc-li-unit,.capxul-doc .doc-li-total{text-align:right;font-variant-numeric:tabular-nums;white-space:nowrap}
3646
- .capxul-doc .doc-li-desc{width:100%}
3647
- .capxul-doc .doc-totals{display:flex;flex-direction:column;gap:.5rem;margin-top:.5rem}
3648
- .capxul-doc .doc-total-line{display:flex;justify-content:space-between;align-items:baseline;gap:1rem}
3649
- .capxul-doc .doc-total-label{color:var(--muted)}
3650
- .capxul-doc .doc-total-deduction .doc-amount-value{color:var(--muted)}
3651
- .capxul-doc .doc-total-grand{border-top:2px solid var(--ink);margin-top:.5rem;padding-top:.75rem;font-size:1.15rem}
3652
- .capxul-doc .doc-total-grand .doc-amount-value{font-weight:700}
3653
- .capxul-doc .doc-amount-value{font-variant-numeric:tabular-nums;font-weight:600}
3654
- .capxul-doc .doc-hero{text-align:center;padding:1.5rem 0 2rem}
3655
- .capxul-doc .doc-hero-label{font-size:.72rem;text-transform:uppercase;letter-spacing:.06em;color:var(--muted)}
3656
- .capxul-doc .doc-hero-amount{font-size:2.6rem;font-weight:700;letter-spacing:-.02em;margin-top:.35rem}
3657
- .capxul-doc .doc-note{color:var(--ink);background:#f7f7f8;border-radius:10px;padding:.9rem 1.1rem;margin:0}
3658
- .capxul-doc .doc-dest-address{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:.9rem}
3659
- .capxul-doc .doc-footer{margin-top:1.75rem;padding-top:1.25rem;border-top:1px solid var(--line);color:var(--muted);font-size:.9rem}
3660
- .capxul-doc code{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:.82rem;color:var(--muted);word-break:break-all}
3661
- @media (max-width:540px){.capxul-doc{padding:1.75rem 1.25rem}.capxul-doc .doc-parties{grid-template-columns:1fr}}
3662
- `.trim();
3663
3487
  //#endregion
3664
- //#region ../wire/src/secret-material.ts
3665
- const SENSITIVE_MATERIAL_PATTERNS = [
3666
- /0x[a-fA-F0-9]{40,}/u,
3667
- /(?:^|[^a-fA-F0-9])[a-fA-F0-9]{64}(?:$|[^a-fA-F0-9])/u,
3668
- /eyJ[A-Za-z0-9_-]*\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+/u,
3669
- /(?:(?: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
3670
- ];
3488
+ //#region src/surface/to-capxul-result.ts
3489
+ /** The one place an Effect becomes a Promise on the public surface. */
3490
+ async function runProgram(program, runPromise = Effect.runPromise) {
3491
+ const result = await runPromise(program.pipe(Effect.catchDefect((defect) => Effect.fail(Errors.unknown(defect))), Effect.result));
3492
+ if (Result.isFailure(result)) return {
3493
+ ok: false,
3494
+ error: result.failure
3495
+ };
3496
+ return {
3497
+ ok: true,
3498
+ value: result.success
3499
+ };
3500
+ }
3501
+ async function toCapxulResult(program, layer) {
3502
+ return runProgram(program.pipe(Effect.provide(layer)));
3503
+ }
3504
+ /**
3505
+ * Signal-aware bridge for the Convex-backed method bundles. The adapters fail
3506
+ * with `{ publicError }` rather than a bare `CapxulError`, so the wrapper is
3507
+ * unwrapped here — once — instead of at every call site.
3508
+ */
3509
+ async function runIfActive(signal, operation, effect, runPromise = Effect.runPromise) {
3510
+ if (signal?.aborted === true) return {
3511
+ ok: false,
3512
+ error: Errors.cancelled({ operation })
3513
+ };
3514
+ const program = effect().pipe(Effect.mapError((error) => error.publicError));
3515
+ return runProgram(signal === void 0 ? program : Effect.raceFirst(program, Effect.callback((resume) => {
3516
+ if (signal.aborted) {
3517
+ resume(Effect.fail(Errors.cancelled({ operation })));
3518
+ return Effect.void;
3519
+ }
3520
+ const onAbort = () => resume(Effect.fail(Errors.cancelled({ operation })));
3521
+ signal.addEventListener("abort", onAbort, { once: true });
3522
+ return Effect.sync(() => signal.removeEventListener("abort", onAbort));
3523
+ })), runPromise);
3524
+ }
3525
+ //#endregion
3526
+ //#region src/surface/_shared/effect-actor-bridge.ts
3527
+ const facadeOutcome = (value) => {
3528
+ if (typeof value !== "object" || value === null || !("ok" in value) || value.ok !== false) return "succeeded";
3529
+ const reason = value.reason;
3530
+ if (reason === "CANCELLED") return "cancelled";
3531
+ return typeof reason === "string" && EXPECTED_OPERATION_OUTCOMES.has(reason) ? "refused" : "failed";
3532
+ };
3533
+ const rejectedFacadeOutcome = (cause) => {
3534
+ if (cause instanceof CapxulError) return cause.code === "CANCELLED" ? "cancelled" : "failed";
3535
+ if (typeof cause !== "object" || cause === null) return "failed";
3536
+ const candidate = cause;
3537
+ return candidate.code === "CANCELLED" || candidate.reason === "CANCELLED" || candidate.error?.code === "CANCELLED" || candidate.publicError?.code === "CANCELLED" ? "cancelled" : "failed";
3538
+ };
3539
+ const RESOLVED_FACADE_SPAN_FAILURE = Symbol("resolved facade span failure");
3540
+ /** The single facade-span bridge consumed by the renderer-neutral React facade. */
3541
+ async function runIdentityFacade(verb, controls, run, runPromise = Effect.runPromise) {
3542
+ const correlationId = safeEngineeringIdentifier(controls?.correlation_id);
3543
+ let resolvedFailure;
3544
+ return runPromise(Effect.gen(function* () {
3545
+ const parent = yield* Effect.currentSpan;
3546
+ return yield* Effect.tryPromise({
3547
+ try: () => run(carryInvocationParentSpan(controls, parent)),
3548
+ catch: (cause) => cause
3549
+ });
3550
+ }).pipe(Effect.tapError((cause) => Effect.annotateCurrentSpan({ outcome: rejectedFacadeOutcome(cause) })), Effect.flatMap((value) => {
3551
+ const outcome = facadeOutcome(value);
3552
+ return Effect.annotateCurrentSpan({ outcome }).pipe(Effect.andThen(outcome === "failed" || outcome === "cancelled" ? Effect.sync(() => {
3553
+ resolvedFailure = value;
3554
+ }).pipe(Effect.andThen(Effect.fail(RESOLVED_FACADE_SPAN_FAILURE))) : Effect.succeed(value)));
3555
+ }), Effect.withSpan(`identity.${verb}`), Effect.annotateSpans({
3556
+ verb,
3557
+ ...correlationId === void 0 ? {} : { correlation_id: correlationId }
3558
+ }), Effect.catch((cause) => cause === RESOLVED_FACADE_SPAN_FAILURE ? Effect.succeed(resolvedFailure) : Effect.fail(cause))));
3559
+ }
3560
+ /**
3561
+ * Bridge an Effect whose typed failure carries `{ publicError: CapxulError }`
3562
+ * into the `Promise<CapxulResult<T>>` shape the consumer-facing method bundles
3563
+ * return. Applies to every port that surfaces a `publicError` (smart-account,
3564
+ * identity, auth-cache, account provision/deploy).
3565
+ */
3566
+ async function runPortEffect(effect, controls, operation = "port", runPromise = Effect.runPromise) {
3567
+ const deadlineDelay = controls?.deadlineMs === void 0 ? void 0 : Math.max(0, controls.deadlineMs - Date.now());
3568
+ const timeoutMs = controls?.timeoutMs === void 0 ? deadlineDelay : deadlineDelay === void 0 ? controls.timeoutMs : Math.min(controls.timeoutMs, deadlineDelay);
3569
+ const observed = (timeoutMs === void 0 ? effect : effect.pipe(Effect.timeoutOrElse({
3570
+ duration: `${timeoutMs} millis`,
3571
+ orElse: () => Effect.fail({ publicError: Errors.providerTimeout("sdk", operation, timeoutMs) })
3572
+ }))).pipe(Effect.tap(() => Effect.annotateCurrentSpan({ outcome: "succeeded" })), Effect.tapError((failure) => {
3573
+ const mode = failure.publicError.details?.failure_mode;
3574
+ return Effect.annotateCurrentSpan({
3575
+ outcome: portFailureOutcome(failure.publicError.code),
3576
+ failure_code: failure.publicError.code,
3577
+ ...typeof mode === "string" ? { mode } : {}
3578
+ });
3579
+ }), Effect.tapDefect(() => Effect.annotateCurrentSpan({
3580
+ outcome: "failed",
3581
+ failure_code: "UNKNOWN"
3582
+ })), Effect.withSpan(`identity.port.${operation}`));
3583
+ return runIfActive(controls?.signal, operation, () => withInvocationParentSpan(observed, controls), runPromise);
3584
+ }
3585
+ /** Map machine-internal failure state onto the public SDK error vocabulary. */
3586
+ function publicIdentityFailure(failure) {
3587
+ if (failure.error !== void 0) return failure.error;
3588
+ return new CapxulError(failure.code === "WORK_DIED" ? "UNKNOWN" : failure.code, failure.message, {
3589
+ ...failure.mode === void 0 ? {} : { details: { failure_mode: failure.mode } },
3590
+ layer: "identity"
3591
+ });
3592
+ }
3593
+ /** The session carried by the actor's current snapshot, or `null`. */
3594
+ function sessionFromActor(actor) {
3595
+ return actor.authSession();
3596
+ }
3597
+ //#endregion
3598
+ //#region src/surface/account-deps.ts
3599
+ /** The single Context tag the account atom + `account.getStatus` resolve. */
3600
+ var AccountDepsTag = class extends Context.Service()("@capxul/sdk/AccountDeps") {};
3601
+ /** Wrap a bundle as the `Layer<AccountDepsTag>` the React provider consumes. */
3602
+ function accountDepsLayer(deps) {
3603
+ return Layer.succeed(AccountDepsTag, deps);
3604
+ }
3671
3605
  /**
3672
- * Reject: does the value carry any known secret material? Best effort — callers
3673
- * drop the whole value on a match; a false negative is a leak, a false positive
3674
- * merely omits an observation field.
3606
+ * `account.getStatus` as a single Effect requiring ONLY `AccountDepsTag`.
3607
+ * Faithful transcription of the current public method body (`account.ts`):
3608
+ * resolve the session (actor first, then the resume-path `authCache`), then
3609
+ * walk the readiness ladder. The `SmartAccountPort` failure narrows to its
3610
+ * `publicError`; a consumer `AccountProvider.getAddress` rejection becomes a
3611
+ * `providerError` (defending the public `CapxulResult` contract).
3675
3612
  */
3676
- function containsSensitiveMaterial(value) {
3677
- return SENSITIVE_MATERIAL_PATTERNS.some((pattern) => pattern.test(value));
3613
+ const accountStatusProgram = Effect.gen(function* () {
3614
+ const deps = yield* AccountDepsTag;
3615
+ const session = yield* Effect.promise(() => currentSession(deps.actor, deps.authCache));
3616
+ if (session === null) return { status: "notAuthenticated" };
3617
+ const current = yield* deps.smartAccountPort.loadByAuthUserId(session.authUserId).pipe(Effect.mapError((failure) => failure.publicError));
3618
+ if (current !== null) return statusFromAccount(current, deps.requirement);
3619
+ if (deps.requirement === "none") return {
3620
+ status: "accountReady",
3621
+ requirement: deps.requirement,
3622
+ account: null,
3623
+ deployment: { status: "counterfactual" }
3624
+ };
3625
+ const signer = deps.signer;
3626
+ if (signer === void 0) return {
3627
+ status: "accountRequired",
3628
+ requirement: deps.requirement,
3629
+ chainId: deps.chainId
3630
+ };
3631
+ const signerAddress = yield* Effect.tryPromise({
3632
+ try: () => signer.getAddress(),
3633
+ catch: (cause) => signerFailure(signer.source, "getAddress", cause)
3634
+ });
3635
+ return {
3636
+ status: "accountProviderReady",
3637
+ requirement: deps.requirement,
3638
+ chainId: deps.chainId,
3639
+ source: signer.source,
3640
+ signerAddress
3641
+ };
3642
+ });
3643
+ /**
3644
+ * Resume-path session resolution. Consults the actor first; if the actor has
3645
+ * no session (e.g. page refresh before any sign-in event), reads
3646
+ * `authCache.getSession`. `AuthCacheError` is non-fatal at this read point —
3647
+ * treat as "no session" and let the consumer's `auth.signIn` path resolve.
3648
+ */
3649
+ async function currentSession(actor, authCache) {
3650
+ const fromActor = sessionFromActor(actor);
3651
+ if (fromActor !== null) return fromActor;
3652
+ if (authCache === void 0) return null;
3653
+ const cached = await Effect.runPromise(Effect.result(authCache.getSession));
3654
+ if (Result.isSuccess(cached)) {
3655
+ if (cached.success !== null && typeof actor.restoreAuthSession === "function") {
3656
+ const restored = await Effect.runPromise(Effect.result(actor.restoreAuthSession(cached.success)));
3657
+ if (Result.isFailure(restored)) return null;
3658
+ }
3659
+ return cached.success;
3660
+ }
3661
+ return null;
3662
+ }
3663
+ /** Map a backend `SmartAccount` row onto the readiness `AccountStatus`. */
3664
+ function statusFromAccount(account, requirement) {
3665
+ if (requirement === "deployed") {
3666
+ if (account.deployedAt === null) return {
3667
+ status: "accountPrepared",
3668
+ requirement,
3669
+ account,
3670
+ deployment: { status: "counterfactual" }
3671
+ };
3672
+ return {
3673
+ status: "accountReady",
3674
+ requirement,
3675
+ account,
3676
+ deployment: {
3677
+ status: "deployed",
3678
+ deployedAt: account.deployedAt
3679
+ }
3680
+ };
3681
+ }
3682
+ return {
3683
+ status: "accountReady",
3684
+ requirement,
3685
+ account,
3686
+ deployment: account.deployedAt === null ? { status: "counterfactual" } : {
3687
+ status: "deployed",
3688
+ deployedAt: account.deployedAt
3689
+ }
3690
+ };
3678
3691
  }
3679
3692
  //#endregion
3680
- //#region ../wire/src/observation-context.ts
3681
- /** Single bounded HTTP carrier used before a Convex action envelope exists. */
3682
- const OBSERVATION_CONTEXT_HEADER = "x-capxul-observation-context";
3683
- const FIELD_RULES = {
3684
- application: {
3685
- maxLength: 64,
3686
- pattern: /^[A-Za-z0-9][A-Za-z0-9._-]*$/u
3687
- },
3688
- applicationId: {
3689
- maxLength: 30,
3690
- pattern: APP_ID_RE
3691
- },
3692
- release: {
3693
- maxLength: 128,
3694
- pattern: /^[A-Za-z0-9][A-Za-z0-9._+@:/-]*$/u
3695
- },
3696
- sessionId: {
3697
- maxLength: 128,
3698
- pattern: /^[A-Za-z0-9][A-Za-z0-9._:-]*$/u
3699
- },
3700
- organizationId: {
3701
- maxLength: 128,
3702
- pattern: /^[A-Za-z0-9][A-Za-z0-9._:-]*$/u
3703
- },
3704
- journeyId: {
3705
- maxLength: 128,
3706
- pattern: /^[A-Za-z0-9][A-Za-z0-9._:-]*$/u
3707
- },
3708
- correlationId: {
3709
- maxLength: 128,
3710
- pattern: /^[A-Za-z0-9][A-Za-z0-9._:-]*$/u
3711
- },
3712
- anonymousId: {
3713
- maxLength: 128,
3714
- pattern: /^anon_[A-Za-z0-9-]+$/u
3715
- },
3716
- traceparent: {
3717
- maxLength: 55,
3718
- pattern: /^00-[0-9a-f]{32}-[0-9a-f]{16}-0[01]$/u
3693
+ //#region src/surface/account-lifecycle.ts
3694
+ function isActiveProvisioningPhase(phase) {
3695
+ return phase.status === "wallet" || phase.status === "identity" || phase.status === "provision" || phase.status === "deploy";
3696
+ }
3697
+ function isRequirementMet(status, requirement) {
3698
+ if (requirement === "none") return status.status !== "notAuthenticated";
3699
+ if (status.status !== "accountReady") return false;
3700
+ return requirement !== "deployed" || status.deployment.status === "deployed";
3701
+ }
3702
+ function mapProvisioningFailureStep(step) {
3703
+ switch (step) {
3704
+ case "wallet": return "connecting";
3705
+ case "identity": return "registering";
3706
+ case "provision":
3707
+ case "deploy": return "activating";
3719
3708
  }
3720
- };
3721
- /**
3722
- * Copy only the canonical allowlist and silently omit malformed/sensitive
3723
- * values. Observation metadata is best effort and may never reject a domain
3724
- * operation.
3725
- */
3726
- function sanitizeObservationContext(input) {
3727
- if (typeof input !== "object" || input === null || Array.isArray(input)) return void 0;
3728
- const source = input;
3729
- const sanitized = {};
3730
- for (const field of Object.keys(FIELD_RULES)) {
3731
- const value = source[field];
3732
- if (!isSafeField(field, value)) continue;
3733
- sanitized[field] = value;
3709
+ }
3710
+ function mapProvisioningPhaseToSetupStep(phase) {
3711
+ switch (phase.status) {
3712
+ case "idle":
3713
+ case "wallet": return "connecting";
3714
+ case "identity": return "registering";
3715
+ case "provision":
3716
+ case "deploy": return "activating";
3717
+ case "ready":
3718
+ case "failed": return "connecting";
3734
3719
  }
3735
- return Object.keys(sanitized).length === 0 ? void 0 : sanitized;
3736
3720
  }
3737
- /** Encode only the sanitized allowlist; absence stays absence. */
3738
- function encodeObservationContextHeader(input) {
3739
- const sanitized = sanitizeObservationContext(input);
3740
- return sanitized === void 0 ? void 0 : JSON.stringify(sanitized);
3721
+ function isSettingUpLifecycle(lifecycle) {
3722
+ return lifecycle.status === "settingUp";
3741
3723
  }
3742
- function isSafeField(field, value) {
3743
- if (typeof value !== "string") return false;
3744
- const rule = FIELD_RULES[field];
3745
- return value.length > 0 && value.length <= rule.maxLength && value === value.trim() && !value.includes("://") && !containsSensitiveMaterial(value) && rule.pattern.test(value);
3724
+ function readyLifecycle(status, accountId) {
3725
+ return {
3726
+ status: "ready",
3727
+ accountId,
3728
+ canTransact: status.status === "accountReady" && status.deployment.status === "deployed"
3729
+ };
3730
+ }
3731
+ function mapAccountLifecycle(input) {
3732
+ const { status, phase, requirement, accountId } = input;
3733
+ if (status.status === "notAuthenticated") return { status: "loading" };
3734
+ if (phase.status === "failed") return {
3735
+ status: "failed",
3736
+ at: mapProvisioningFailureStep(phase.at),
3737
+ error: phase.error
3738
+ };
3739
+ if (isRequirementMet(status, requirement) || phase.status === "ready") {
3740
+ if (accountId === void 0) return { status: "loading" };
3741
+ return readyLifecycle(status, accountId);
3742
+ }
3743
+ if (isActiveProvisioningPhase(phase) || phase.status === "idle") return {
3744
+ status: "settingUp",
3745
+ step: mapProvisioningPhaseToSetupStep(phase)
3746
+ };
3747
+ return { status: "loading" };
3746
3748
  }
3747
3749
  //#endregion
3748
3750
  //#region src/contract/actor-scope.ts
@@ -3979,7 +3981,7 @@ function mapActorRequest(request, payer) {
3979
3981
  ...resolvedPayer === void 0 ? {} : { payer: resolvedPayer },
3980
3982
  amount: request.amount,
3981
3983
  reference: request.reference,
3982
- status: mapActorRequestStatus(request.status),
3984
+ status: request.status,
3983
3985
  expiresAt: request.expiresAt ?? null
3984
3986
  };
3985
3987
  }
@@ -4037,28 +4039,17 @@ function mapApprovedInboxPayment(command, input) {
4037
4039
  };
4038
4040
  }
4039
4041
  }
4040
- function mapActorRequestStatus(status) {
4042
+ function mapInboxStatus(status) {
4041
4043
  switch (status) {
4042
4044
  case "draft":
4043
4045
  case "sent":
4044
- case "viewed":
4046
+ case "viewed": return "open";
4047
+ case "pending_settlement": return "approved";
4045
4048
  case "paid":
4046
- case "pending_settlement":
4047
- case "declined":
4048
- case "cancelled":
4049
- case "expired": return status;
4050
- default: return "sent";
4051
- }
4052
- }
4053
- function mapInboxStatus(status) {
4054
- switch (status) {
4055
- case "approved":
4056
4049
  case "declined":
4057
- case "paid":
4058
4050
  case "cancelled":
4059
4051
  case "expired": return status;
4060
- case "pending_settlement": return "approved";
4061
- default: return "open";
4052
+ default: throw Errors.invalidInput("status", "unknown request status");
4062
4053
  }
4063
4054
  }
4064
4055
  function normalizeRefForBackend$1(ref, field) {
@@ -4377,6 +4368,7 @@ const financialOpsContract = {
4377
4368
  listPayments: makeFunctionReference(CAPXUL_FUNCTIONS["financialOps/queries"].listPayments),
4378
4369
  getPayment: makeFunctionReference(CAPXUL_FUNCTIONS["financialOps/queries"].getPayment),
4379
4370
  activityList: makeFunctionReference(CAPXUL_FUNCTIONS["movement/activity"].list),
4371
+ activitySummary: makeFunctionReference(CAPXUL_FUNCTIONS["movement/activity"].summary),
4380
4372
  activityGet: makeFunctionReference(CAPXUL_FUNCTIONS["movement/activity"].get),
4381
4373
  activityAnnotate: makeFunctionReference(CAPXUL_FUNCTIONS["movement/activity"].annotate),
4382
4374
  verifyPaymentDocument: makeFunctionReference(CAPXUL_FUNCTIONS["financialOps/queries"].verifyPaymentDocument),
@@ -4628,6 +4620,40 @@ function actorReferenceToBackend(actor) {
4628
4620
  case "org": return actor;
4629
4621
  }
4630
4622
  }
4623
+ /**
4624
+ * The activity backend gives its actor a different name than the other
4625
+ * financialOps functions: `{kind: "organization", orgId}`, and not
4626
+ * `{kind: "org", orgId}`. Thus it does not use `actorReferenceToBackend`.
4627
+ *
4628
+ * The `personal` case sends NO actor. This is correct, and it does not lose
4629
+ * data. `requireActivityActor` uses the signed-in Account when the actor is
4630
+ * absent, and `canReadActor` refuses all other account identifiers
4631
+ * (`packages/backend/convex/movement/activityAuth.ts`). Thus "personal" and
4632
+ * "absent" both name the one Account that an SDK caller can read. The SDK does
4633
+ * not hold an account identifier to send.
4634
+ *
4635
+ * The `switch` is exhaustive. A new `ActorReference` variant is a compile error
4636
+ * here. The conditional expression that this function replaced removed each
4637
+ * value that was not an organization.
4638
+ */
4639
+ function activityActorField(actor) {
4640
+ if (actor === void 0) return {};
4641
+ switch (actor.kind) {
4642
+ case "personal": return {};
4643
+ case "organization": return { actor: {
4644
+ kind: "organization",
4645
+ orgId: actor.organizationId
4646
+ } };
4647
+ }
4648
+ }
4649
+ /** An absent actor and a `personal` actor both name the signed-in Account. */
4650
+ function actorScopeKey(actor) {
4651
+ return actor === void 0 || actor.kind === "personal" ? "personal" : `org:${actor.organizationId}`;
4652
+ }
4653
+ /** Two actor references name the same scope. */
4654
+ function sameActor(left, right) {
4655
+ return actorScopeKey(left) === actorScopeKey(right);
4656
+ }
4631
4657
  const memoryPaymentRequestKeys = /* @__PURE__ */ new Map();
4632
4658
  const paymentAttemptReleases = /* @__PURE__ */ new Set();
4633
4659
  let paymentAttemptPagehideInstalled = false;
@@ -5058,31 +5084,48 @@ function makeFinancialOpsMethods(deps) {
5058
5084
  },
5059
5085
  activity: {
5060
5086
  list: (params, options) => runIfActive(options?.signal, "activity.list", () => deps.convexCall.query(fns.activityList, { input: {
5061
- ...params?.actor?.kind === "organization" ? { actor: {
5062
- kind: "organization",
5063
- orgId: params.actor.organizationId
5064
- } } : {},
5087
+ ...activityActorField(params?.actor),
5065
5088
  ...params?.cursor === void 0 ? {} : { cursor: params.cursor },
5066
- ...params?.limit === void 0 ? {} : { limit: params.limit }
5089
+ ...params?.limit === void 0 ? {} : { limit: params.limit },
5090
+ ...params?.range === void 0 ? {} : { range: params.range },
5091
+ ...params?.filter === void 0 ? {} : { filter: params.filter }
5067
5092
  } })),
5068
- get: (reference, options) => runIfActive(options?.signal, "activity.get", () => deps.convexCall.query(fns.activityGet, { input: {
5069
- kind: reference.kind,
5070
- id: reference.id,
5071
- ...reference.kind === "payment" && reference.actor?.kind === "organization" ? { actor: {
5072
- kind: "organization",
5073
- orgId: reference.actor.organizationId
5074
- } } : {}
5093
+ summary: (params, options) => runIfActive(options?.signal, "activity.summary", () => deps.convexCall.query(fns.activitySummary, { input: {
5094
+ ...activityActorField(params?.actor),
5095
+ ...params?.window === void 0 ? {} : { window: params.window }
5075
5096
  } })),
5076
- annotate: (input, options) => runIfActive(options?.signal, "activity.annotate", () => deps.convexCall.mutation(fns.activityAnnotate, { input: {
5077
- ...input.actor?.kind === "organization" ? { actor: {
5078
- kind: "organization",
5079
- orgId: input.actor.organizationId
5080
- } } : {},
5081
- movementId: input.movementId,
5082
- ...input.counterpartyLabel === void 0 ? {} : { counterpartyLabel: input.counterpartyLabel },
5083
- ...input.accountingCategory === void 0 ? {} : { accountingCategory: input.accountingCategory },
5084
- ...input.memo === void 0 ? {} : { memo: input.memo }
5085
- } }))
5097
+ get: (reference, options) => {
5098
+ const actor = actorReferenceToBackend(options?.actor);
5099
+ return runIfActive(options?.signal, "activity.get", () => deps.convexCall.query(fns.activityGet, {
5100
+ input: {
5101
+ kind: reference.kind,
5102
+ id: reference.id,
5103
+ ...reference.kind === "payment" ? activityActorField(reference.actor) : {}
5104
+ },
5105
+ ...actor === void 0 ? {} : { actor }
5106
+ }));
5107
+ },
5108
+ annotate: (input, options) => {
5109
+ if (input.actor !== void 0 && input.reference.kind === "payment" && input.reference.actor !== void 0 && !sameActor(input.actor, input.reference.actor)) return Promise.resolve({
5110
+ ok: false,
5111
+ error: Errors.invalidInput("reference.actor", "must match the annotation actor")
5112
+ });
5113
+ const referenceActor = input.reference.kind === "payment" ? activityActorField(input.reference.actor).actor : void 0;
5114
+ return runIfActive(options?.signal, "activity.annotate", () => deps.convexCall.mutation(fns.activityAnnotate, { input: {
5115
+ ...activityActorField(input.actor),
5116
+ reference: input.reference.kind === "payment" && referenceActor !== void 0 ? {
5117
+ kind: "payment",
5118
+ id: input.reference.id,
5119
+ actor: referenceActor
5120
+ } : {
5121
+ kind: input.reference.kind,
5122
+ id: input.reference.id
5123
+ },
5124
+ ...input.counterpartyLabel === void 0 ? {} : { counterpartyLabel: input.counterpartyLabel },
5125
+ ...input.accountingCategory === void 0 ? {} : { accountingCategory: input.accountingCategory },
5126
+ ...input.memo === void 0 ? {} : { memo: input.memo }
5127
+ } }));
5128
+ }
5086
5129
  },
5087
5130
  offramp: {
5088
5131
  quote: (input, options) => {
@@ -5410,7 +5453,7 @@ function mapOk(result, f) {
5410
5453
  }
5411
5454
  //#endregion
5412
5455
  //#region package.json
5413
- var version = "2.3.2";
5456
+ var version = "2.4.0";
5414
5457
  //#endregion
5415
5458
  //#region src/ports/auth-client.ts
5416
5459
  var AuthClientError = class extends Data.TaggedError("AuthClientError") {};
@@ -5584,7 +5627,8 @@ const TelemetryEnvelopeProps = {
5584
5627
  "development",
5585
5628
  "staging",
5586
5629
  "production",
5587
- "unknown"
5630
+ "unknown",
5631
+ "local"
5588
5632
  ]),
5589
5633
  producer: Schema.Literals([
5590
5634
  "server",
@@ -8163,13 +8207,74 @@ const organizationPaymentExecutionUnavailable = () => Promise.resolve({
8163
8207
  //#region src/contract/holdings.ts
8164
8208
  const holdingsContract = { current: makeFunctionReference(CAPXUL_FUNCTIONS["holdings/actions"].current) };
8165
8209
  //#endregion
8210
+ //#region src/domain/money/primary-holding.ts
8211
+ const CURRENCY_BY_TOKEN_SYMBOL = new Map([["USDX", toCurrencyCode("USD")]]);
8212
+ /**
8213
+ * The designated display asset: the payable row holding the most money, or
8214
+ * `null` when no row is payable.
8215
+ *
8216
+ * A `HoldingRow` (`packages/wire/src/money.ts`) is a RAW CHAIN ROW, and three
8217
+ * of its shapes cannot become a `Holding`. Each is SKIPPED rather than
8218
+ * reported, because a row nobody can render is not a failure of the read:
8219
+ *
8220
+ * - no `decimals` — the raw balance cannot be lifted into major units;
8221
+ * - no `symbol` — the asset cannot be named;
8222
+ * - a symbol that stands for no supported `CurrencyCode` — the balance
8223
+ * cannot be stated as `Money` without inventing a currency for it.
8224
+ *
8225
+ * This replaces the app's hand pick in `formatCurrentHoldingsBalance`
8226
+ * (`dashboard-formatters.ts`): a `.find()` over a hardcoded `["USDX","USDC"]`
8227
+ * list falling back to `rows[0]`. Ties keep the earlier row, so the same
8228
+ * snapshot always yields the same answer.
8229
+ */
8230
+ function primaryHolding(rows) {
8231
+ let primary = null;
8232
+ for (const row of rows) {
8233
+ const holding = toHolding(row);
8234
+ if (holding === null) continue;
8235
+ if (primary === null || compareDecimal(holding.available.value, primary.available.value) > 0) primary = holding;
8236
+ }
8237
+ return primary;
8238
+ }
8239
+ function toHolding(row) {
8240
+ if (row.symbol === void 0 || row.decimals === void 0) return null;
8241
+ const currency = currencyForTokenSymbol(row.symbol);
8242
+ if (currency === null) return null;
8243
+ return {
8244
+ id: row.assetKind === "native" ? "native" : row.tokenAddress.toLowerCase(),
8245
+ symbol: row.symbol,
8246
+ available: fromWei(row.rawBalance, row.decimals, currency)
8247
+ };
8248
+ }
8249
+ function currencyForTokenSymbol(symbol) {
8250
+ return CURRENCY_BY_TOKEN_SYMBOL.get(symbol.toUpperCase()) ?? null;
8251
+ }
8252
+ /**
8253
+ * Order two non-negative major-unit decimal strings without converting either
8254
+ * to a JavaScript number. Rows carry different `decimals`, so the raw integers
8255
+ * are not comparable: 1999999999 at 9 decimals is LESS than 2000000 at 6.
8256
+ */
8257
+ function compareDecimal(first, second) {
8258
+ const [firstWhole = "0", firstFraction = ""] = first.split(".");
8259
+ const [secondWhole = "0", secondFraction = ""] = second.split(".");
8260
+ const width = Math.max(firstFraction.length, secondFraction.length);
8261
+ const firstScaled = BigInt(firstWhole + firstFraction.padEnd(width, "0"));
8262
+ const secondScaled = BigInt(secondWhole + secondFraction.padEnd(width, "0"));
8263
+ if (firstScaled === secondScaled) return 0;
8264
+ return firstScaled < secondScaled ? -1 : 1;
8265
+ }
8266
+ //#endregion
8166
8267
  //#region src/surface/holdings.ts
8167
8268
  function makeHoldingsMethods(deps) {
8168
8269
  const functions = deps.functions ?? holdingsContract;
8169
- return { current: (input, options) => runIfActive(options?.signal, "holdings.current", () => deps.convexCall.action(functions.current, input?.actor?.kind === "organization" ? { actor: {
8270
+ const read = (operation, actor, signal, pick) => runIfActive(signal, operation, () => deps.convexCall.action(functions.current, actor?.kind === "organization" ? { actor: {
8170
8271
  kind: "organization",
8171
- orgId: input.actor.organizationId
8172
- } } : {})) };
8272
+ orgId: actor.organizationId
8273
+ } } : {}).pipe(Effect.map(pick)));
8274
+ return {
8275
+ current: (input, options) => read("holdings.current", input?.actor, options?.signal, (snapshot) => snapshot),
8276
+ primary: (input, options) => read("holdings.primary", input?.actor, options?.signal, (snapshot) => primaryHolding(snapshot.rows))
8277
+ };
8173
8278
  }
8174
8279
  //#endregion
8175
8280
  //#region src/surface/factory.ts
@@ -8998,4 +9103,4 @@ function withHostObservation(actor, snapshot) {
8998
9103
  };
8999
9104
  }
9000
9105
  //#endregion
9001
- export { fingerprintPaymentIntent as A, formatTraceparent as B, ClockPortTag as C, AuthClientError as D, authClientPortFromPromiseAdapter as E, sanitizeObservationContext as F, signerFailure as G, readInvocationObservation as H, CAPXUL_FUNCTIONS as I, BASE_SEPOLIA_CHAIN_ID as J, CAPXUL_PAYMENTS_V2_ADDRESS as K, BootstrapEnvelope as L, fromWei as M, OBSERVATION_CONTEXT_HEADER as N, AuthClientPortTag as O, encodeObservationContextHeader as P, isRestoring as Q, EngineeringTelemetryBootstrapPolicy as R, ClockError as S, bootstrapErrorFromCapxul as T, causeChain as U, copyInvocationObservation as V, injectedWalletSigner as W, destination as X, deriveCapxulSafeAddress as Y, isClaimed as Z, wireChainId as _, observeFailedResult as a, ConvexCallPortTag as b, captureExceptionSync as c, TelemetryPortTag as d, redactTelemetryEvent as f, accountReadErrorFromCapxul as g, AccountReadPortTag as h, observationContextProps as i, toWei as j, version as k, detectAuthCacheAdapter as l, smartAccountErrorFromCapxul as m, postHogProductTelemetry as n, postHogFailureObservation as o, SmartAccountPortTag as p, normalizeBindingEmail as q, CAPXUL_SDK_EXPECTED_OUTCOME_EVENT as r, captureException as s, assembleCapxulClient as t, PostHogTelemetryLayer as u, IdentityPortTag as v, BootstrapPortTag as w, convexCallErrorFromCapxul as x, identityErrorFromCapxul as y, isSettingUpLifecycle as z };
9106
+ export { isClaimed as $, fingerprintPaymentIntent as A, OBSERVATION_CONTEXT_HEADER as B, ClockPortTag as C, AuthClientError as D, authClientPortFromPromiseAdapter as E, copyInvocationObservation as F, CAPXUL_FUNCTIONS as G, sanitizeObservationContext as H, readInvocationObservation as I, CAPXUL_PAYMENTS_V2_ADDRESS as J, BootstrapEnvelope as K, causeChain as L, fromWei as M, isSettingUpLifecycle as N, AuthClientPortTag as O, formatTraceparent as P, destination as Q, injectedWalletSigner as R, ClockError as S, bootstrapErrorFromCapxul as T, PAYMENT_DIRECTIONS as U, encodeObservationContextHeader as V, PAYMENT_STATUSES as W, BASE_SEPOLIA_CHAIN_ID as X, normalizeBindingEmail as Y, deriveCapxulSafeAddress as Z, wireChainId as _, observeFailedResult as a, ConvexCallPortTag as b, captureExceptionSync as c, TelemetryPortTag as d, isRestoring as et, redactTelemetryEvent as f, accountReadErrorFromCapxul as g, AccountReadPortTag as h, observationContextProps as i, toWei as j, version as k, detectAuthCacheAdapter as l, smartAccountErrorFromCapxul as m, postHogProductTelemetry as n, postHogFailureObservation as o, SmartAccountPortTag as p, EngineeringTelemetryBootstrapPolicy as q, CAPXUL_SDK_EXPECTED_OUTCOME_EVENT as r, captureException as s, assembleCapxulClient as t, PostHogTelemetryLayer as u, IdentityPortTag as v, BootstrapPortTag as w, convexCallErrorFromCapxul as x, identityErrorFromCapxul as y, signerFailure as z };