@capxul/sdk 4.2.0-rc.1 → 4.2.0-rc.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{OAuthBearerAuthClient-BrbXrndM.mjs → OAuthBearerAuthClient-CbU_W9Sp.mjs} +114 -5
- package/dist/capxul-source.json +3 -3
- package/dist/index.d.mts +13 -1
- package/dist/index.mjs +2 -2
- package/dist/node/index.mjs +1 -1
- package/dist/{production-FgHUndeX.mjs → production-7jr-KUvX.mjs} +1508 -63
- package/dist/{production-CBKHzqYP.d.mts → production-A1zW1JPt.d.mts} +163 -31
- package/dist/testing/index.d.mts +77 -2
- package/dist/testing/index.mjs +94 -3
- package/package.json +5 -5
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $ as PayrollGroupId, A as AllowanceKey, B as ChainId, C as AuthCachePort, D as Account$1, Et as FailureMode, H as CurrencyCode, I as AuthSession, K as Email, L as AuthUserId, M as AnonymousDistinctId, N as AppId, Q as PaymentCommandId, R as BlockNumber, U as DocumentHash, V as CountryCode, W as DurationMs, X as OrgId, Y as Money, Z as PartyId, _ as Session$1, _t as CapxulErrorCode, at as RoleKey, b as AuthClientPort, bt as Failure, ct as TesterKind, d as AccountRequirement, et as PayrollRunId, g as Profile$1, gt as CapxulError, h as CapxulResult, i as CapxulSigner, it as PublishableKey, j as AllowedOrigin, k as Address$1, lt as TxHash, nt as PermissionId, ot as SessionToken, q as EpochMs, rt as Profile, s as SignerStatusStore, st as SmartAccount, tt as PermissionAssignmentId, u as AccountProviderSource, ut as WeiAmount, v as SmartAccount$1, vt as CapxulErrorDetails, x as CanSendOtpStatus, y as ClockPort, z as BudgetId } from "./OAuthBearerAuthClient-C-ip-z8M.mjs";
|
|
1
|
+
import { $ as PayrollGroupId, A as AllowanceKey, B as ChainId, C as AuthCachePort, D as Account$1, Et as FailureMode, F as AssetId, H as CurrencyCode, I as AuthSession, K as Email, L as AuthUserId, M as AnonymousDistinctId, N as AppId, Q as PaymentCommandId, R as BlockNumber, U as DocumentHash, V as CountryCode, W as DurationMs, X as OrgId, Y as Money, Z as PartyId, _ as Session$1, _t as CapxulErrorCode, at as RoleKey, b as AuthClientPort, bt as Failure, ct as TesterKind, d as AccountRequirement, et as PayrollRunId, g as Profile$1, gt as CapxulError, h as CapxulResult, i as CapxulSigner, it as PublishableKey, j as AllowedOrigin, k as Address$1, lt as TxHash, nt as PermissionId, ot as SessionToken, q as EpochMs, rt as Profile, s as SignerStatusStore, st as SmartAccount, tt as PermissionAssignmentId, u as AccountProviderSource, ut as WeiAmount, v as SmartAccount$1, vt as CapxulErrorDetails, x as CanSendOtpStatus, y as ClockPort, z as BudgetId } from "./OAuthBearerAuthClient-C-ip-z8M.mjs";
|
|
2
2
|
import { Hex } from "viem";
|
|
3
3
|
import { Context, Effect, Layer, Schema, Scope, Tracer } from "effect";
|
|
4
4
|
import { FunctionReference } from "convex/server";
|
|
@@ -284,7 +284,7 @@ interface TelemetryPort {
|
|
|
284
284
|
}
|
|
285
285
|
//#endregion
|
|
286
286
|
//#region ../observability/src/index.d.ts
|
|
287
|
-
declare const TELEMETRY_EVENT_NAMES: readonly ["auth_otp_requested", "auth_otp_delivered", "auth_otp_expired", "auth_verified", "auth_failed", "auth_signed_out", "onboarding_intent_selected", "onboarding_profile_submitted", "onboarding_organization_submitted", "onboarding_dashboard_reached", "provisioning_safe_created", "provisioning_safe_confirmed", "bootstrap_resolved", "bootstrap_failed", "member_activation_started", "member_activation_ready", "member_activation_failed", "organization_creation_started", "organization_creation_ready", "organization_creation_failed", "chain_upstream_fallback", "bundler_error", "receipt_pending", "operation_retried", "account_balance_read", "account_balance_failed", "faucet_requested", "faucet_confirmed", "faucet_failed", "org_create_started", "org_safe_created", "org_safe_confirmed", "org_roles_seeded", "org_created", "org_create_failed", "org_invite_sent", "org_invite_accepted", "org_invite_expired", "payment_initiated", "payment_settled", "payment_failed", "payment_verification_retry", "payment_lifecycle_transition", "quick_pay_external_address_acknowledged", "deposit_initiated", "deposit_settled", "permission_mirror_verification", "movement_scan_window", "movement_scan_checkpoint", "movement_scan_incident", "activity_annotation_saved", "ui_activity_action_completed", "contact_relationship_changed", "ui_contact_action_completed"];
|
|
287
|
+
declare const TELEMETRY_EVENT_NAMES: readonly ["auth_otp_requested", "auth_otp_delivered", "auth_otp_expired", "auth_verified", "auth_failed", "identity_refused", "auth_signed_out", "onboarding_intent_selected", "onboarding_profile_submitted", "onboarding_organization_submitted", "onboarding_dashboard_reached", "provisioning_safe_created", "provisioning_safe_confirmed", "bootstrap_resolved", "bootstrap_failed", "member_activation_started", "member_activation_ready", "member_activation_failed", "organization_creation_started", "organization_creation_ready", "organization_creation_failed", "chain_upstream_fallback", "bundler_error", "receipt_pending", "operation_retried", "account_balance_read", "account_balance_failed", "faucet_requested", "faucet_confirmed", "faucet_failed", "org_create_started", "org_safe_created", "org_safe_confirmed", "org_roles_seeded", "org_created", "org_create_failed", "org_invite_sent", "org_invite_accepted", "org_invite_expired", "payment_initiated", "payment_settled", "payment_failed", "payment_verification_retry", "payment_lifecycle_transition", "quick_pay_external_address_acknowledged", "deposit_initiated", "deposit_settled", "permission_mirror_verification", "movement_scan_window", "movement_scan_checkpoint", "movement_scan_incident", "activity_annotation_saved", "ui_activity_action_completed", "contact_relationship_changed", "ui_contact_action_completed", "access_recovery_completed", "ui_access_step_completed", "org_invite_progressed", "invoice_issued", "invoice_request_transitioned", "ui_invoice_step_completed", "ui_treasury_step_completed", "ui_receive_step_completed", "payroll_group_changed", "payroll_run_transitioned", "ui_payroll_action_completed"];
|
|
288
288
|
type TelemetryEventName = (typeof TELEMETRY_EVENT_NAMES)[number];
|
|
289
289
|
type TelemetryProps = Record<string, unknown>;
|
|
290
290
|
type TelemetryEvent = {
|
|
@@ -611,6 +611,10 @@ type BootstrapResolution = {
|
|
|
611
611
|
readonly siteBaseUrl: string;
|
|
612
612
|
readonly openfortPublishableKey: string;
|
|
613
613
|
readonly shieldPublishableKey: string;
|
|
614
|
+
/** Openfort third-party auth provider the signer presents; absent means better-auth. */
|
|
615
|
+
readonly openfortAuthProvider?: "better-auth" | "oidc";
|
|
616
|
+
/** Origin of the Ponder indexer the SDK reads directly; absent means no indexer reads. */
|
|
617
|
+
readonly indexerUrl?: string;
|
|
614
618
|
readonly engineeringTelemetry?: EngineeringTelemetryBootstrapPolicyV1;
|
|
615
619
|
};
|
|
616
620
|
type BootstrapErrorKind = "notAuthenticated" | "network" | "provider" | "malformedBody" | "invalidInput" | "rateLimited";
|
|
@@ -904,6 +908,118 @@ interface AccountReadPort {
|
|
|
904
908
|
fundFromFaucet(input: FundFromFaucetInput): Effect.Effect<FundFromFaucetResult, AccountReadError, never>;
|
|
905
909
|
}
|
|
906
910
|
//#endregion
|
|
911
|
+
//#region src/ports/indexer-read.d.ts
|
|
912
|
+
/**
|
|
913
|
+
* IndexerReadPort is the SDK's read seam onto the Ponder read model
|
|
914
|
+
* (`apps/indexer`). Ponder holds what the chain said; Convex holds what the
|
|
915
|
+
* user meant. Nothing here is copied into Convex, nothing waits for a
|
|
916
|
+
* finality tag, and no write path may read through this port.
|
|
917
|
+
*
|
|
918
|
+
* Every read is scoped to one Safe and one page. The indexer accepts only the
|
|
919
|
+
* exact compiled statements in `apps/indexer/src/queries.ts`, so the adapter
|
|
920
|
+
* that implements this port reproduces those statements byte for byte and the
|
|
921
|
+
* service refuses anything else with 403.
|
|
922
|
+
*/
|
|
923
|
+
/** One page of rows for one Safe. `offset` defaults to 0 (the first page). */
|
|
924
|
+
interface IndexerReadInput {
|
|
925
|
+
/** The Safe the caller owns. The indexer refuses a Safe outside the session claim. */
|
|
926
|
+
readonly safe: Address$1;
|
|
927
|
+
/** 1 to 1000 inclusive — the indexer's own bound. */
|
|
928
|
+
readonly limit: number;
|
|
929
|
+
readonly offset?: number;
|
|
930
|
+
}
|
|
931
|
+
/**
|
|
932
|
+
* The chain evidence every indexed row carries. `id` is the logical event id
|
|
933
|
+
* `chainId:txHash:transactionLogOrdinal`; it survives a reorg, so it is the
|
|
934
|
+
* key a Convex intent row stitches against.
|
|
935
|
+
*/
|
|
936
|
+
interface IndexerEventIdentity {
|
|
937
|
+
readonly id: string;
|
|
938
|
+
readonly chainId: ChainId;
|
|
939
|
+
readonly txHash: TxHash;
|
|
940
|
+
readonly transactionLogOrdinal: number;
|
|
941
|
+
readonly blockNumber: bigint;
|
|
942
|
+
readonly blockHash: string;
|
|
943
|
+
/** Block timestamp in epoch seconds. */
|
|
944
|
+
readonly timestamp: bigint;
|
|
945
|
+
}
|
|
946
|
+
interface IndexerTransferRow extends IndexerEventIdentity {
|
|
947
|
+
readonly assetId: AssetId;
|
|
948
|
+
readonly token: Address$1;
|
|
949
|
+
readonly from: Address$1;
|
|
950
|
+
readonly to: Address$1;
|
|
951
|
+
/**
|
|
952
|
+
* Raw token units, exact. This is an evidence boundary, so the row never
|
|
953
|
+
* carries Money or AssetAmount: the reader owns the asset's decimals.
|
|
954
|
+
*/
|
|
955
|
+
readonly amount: bigint;
|
|
956
|
+
}
|
|
957
|
+
interface IndexerPaymentRow extends IndexerEventIdentity {
|
|
958
|
+
readonly contract: Address$1;
|
|
959
|
+
/** The protocol settlement id a Convex Payment command stitches against. */
|
|
960
|
+
readonly settlementId: string;
|
|
961
|
+
readonly sender: Address$1;
|
|
962
|
+
readonly recipient: Address$1;
|
|
963
|
+
readonly assetId: AssetId;
|
|
964
|
+
readonly token: Address$1;
|
|
965
|
+
/** Raw token units, exact. */
|
|
966
|
+
readonly amount: bigint;
|
|
967
|
+
readonly kind: number;
|
|
968
|
+
readonly documentHash: string;
|
|
969
|
+
}
|
|
970
|
+
/** A row of the indexer's signed-transfer `balance` view. */
|
|
971
|
+
interface IndexerBalanceRow {
|
|
972
|
+
readonly chainId: ChainId;
|
|
973
|
+
readonly assetId: AssetId;
|
|
974
|
+
readonly address: Address$1;
|
|
975
|
+
/** Raw token units, exact. */
|
|
976
|
+
readonly amount: bigint;
|
|
977
|
+
}
|
|
978
|
+
/** One chain's sync position, from the indexer's native `/status` route. */
|
|
979
|
+
interface IndexerChainStatus {
|
|
980
|
+
readonly chain: string;
|
|
981
|
+
readonly chainId: ChainId;
|
|
982
|
+
readonly blockNumber: bigint;
|
|
983
|
+
/** Block timestamp in epoch seconds. Its age is the freshness label. */
|
|
984
|
+
readonly blockTimestamp: bigint;
|
|
985
|
+
}
|
|
986
|
+
/**
|
|
987
|
+
* The tables this seam reads today. P04 adds the authority rows (`safe`,
|
|
988
|
+
* `safe_event`, `roles_module`) and P05 adds `commitment`: each is one more
|
|
989
|
+
* entry here plus its statement and decoder.
|
|
990
|
+
*/
|
|
991
|
+
interface IndexerRowsByTable {
|
|
992
|
+
readonly transfer: readonly IndexerTransferRow[];
|
|
993
|
+
readonly payment: readonly IndexerPaymentRow[];
|
|
994
|
+
readonly balance: readonly IndexerBalanceRow[];
|
|
995
|
+
}
|
|
996
|
+
type IndexerTable = keyof IndexerRowsByTable;
|
|
997
|
+
declare const IndexerReadError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
|
|
998
|
+
readonly _tag: "IndexerReadError";
|
|
999
|
+
} & Readonly<A>;
|
|
1000
|
+
declare class IndexerReadError extends IndexerReadError_base<{
|
|
1001
|
+
readonly operation: string;
|
|
1002
|
+
readonly publicCode: CapxulErrorCode;
|
|
1003
|
+
readonly publicError: CapxulError;
|
|
1004
|
+
readonly cause: unknown;
|
|
1005
|
+
readonly details?: CapxulErrorDetails;
|
|
1006
|
+
}> {}
|
|
1007
|
+
interface IndexerReadPort {
|
|
1008
|
+
/** One page of rows, newest block first. An empty page is a value, not an error. */
|
|
1009
|
+
read<T extends IndexerTable>(table: T, input: IndexerReadInput): Effect.Effect<IndexerRowsByTable[T], IndexerReadError>;
|
|
1010
|
+
/**
|
|
1011
|
+
* The same page over the indexer's live stream. The first snapshot is
|
|
1012
|
+
* `loading`; each later snapshot carries the whole page again. The indexer
|
|
1013
|
+
* closes the stream on token expiry or revoked authority, which arrives as
|
|
1014
|
+
* one `error` snapshot — the caller re-reads with a refreshed token.
|
|
1015
|
+
*
|
|
1016
|
+
* @internal-use — substrate reactive transport, like `ConvexCallPort.subscribe`.
|
|
1017
|
+
*/
|
|
1018
|
+
subscribe<T extends IndexerTable>(table: T, input: IndexerReadInput, callback: (snapshot: Snapshot<IndexerRowsByTable[T]>) => void): Effect.Effect<Unsubscribe, IndexerReadError>;
|
|
1019
|
+
/** Sync position per chain. Unauthenticated: a stale index is not a refusal. */
|
|
1020
|
+
readonly status: Effect.Effect<readonly IndexerChainStatus[], IndexerReadError>;
|
|
1021
|
+
}
|
|
1022
|
+
//#endregion
|
|
907
1023
|
//#region src/flows/types.d.ts
|
|
908
1024
|
/** The runtime port bundle that `assembleCapxulClient` uses. */
|
|
909
1025
|
interface FlowPorts {
|
|
@@ -916,6 +1032,11 @@ interface FlowPorts {
|
|
|
916
1032
|
readonly clock: ClockPort;
|
|
917
1033
|
readonly telemetry: TelemetryPort;
|
|
918
1034
|
readonly convexCall: ConvexCallPort;
|
|
1035
|
+
/**
|
|
1036
|
+
* The Ponder read seam (P02). Optional: it is present only when the client
|
|
1037
|
+
* was given an indexer origin, and every existing flow works without it.
|
|
1038
|
+
*/
|
|
1039
|
+
readonly indexerRead?: IndexerReadPort;
|
|
919
1040
|
}
|
|
920
1041
|
/** Legacy method input retained by the imperative auth surface. */
|
|
921
1042
|
interface ExternalSigner {
|
|
@@ -1551,20 +1672,30 @@ interface ActivityAnnotationInput {
|
|
|
1551
1672
|
readonly accountingCategory?: string;
|
|
1552
1673
|
readonly memo?: string;
|
|
1553
1674
|
}
|
|
1675
|
+
/**
|
|
1676
|
+
* #1960 R02: the declared option type is the `InvocationControls` these methods
|
|
1677
|
+
* have always forwarded to `deps.invocationControls`, so an Activity action can
|
|
1678
|
+
* carry the correlation its UI observation already used. `{ signal }` callers
|
|
1679
|
+
* are unaffected.
|
|
1680
|
+
*/
|
|
1554
1681
|
interface ActivityMethods {
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1682
|
+
/**
|
|
1683
|
+
* Tell the caller when the indexed page for this actor's Safe changed, over
|
|
1684
|
+
* the indexer's one live connection. The callback carries no rows: it is the
|
|
1685
|
+
* signal to re-read `list` or `summary`. Without an indexer this resolves a
|
|
1686
|
+
* no-op teardown, so a caller needs no branch.
|
|
1687
|
+
*
|
|
1688
|
+
* @internal-use — substrate reactive transport, like `ConvexCallPort.subscribe`.
|
|
1689
|
+
*/
|
|
1690
|
+
subscribe(onChange: () => void, params?: {
|
|
1691
|
+
readonly actor?: ActorReference;
|
|
1692
|
+
}, options?: InvocationControls): Promise<CapxulResult<() => void>>;
|
|
1693
|
+
list(params?: ActivityListParams, options?: InvocationControls): Promise<CapxulResult<ActivityPage>>;
|
|
1694
|
+
summary(params?: ActivitySummaryParams, options?: InvocationControls): Promise<CapxulResult<ActivitySummary>>;
|
|
1695
|
+
get(reference: ActivityReference, options?: InvocationControls & {
|
|
1563
1696
|
readonly actor?: ActorReference;
|
|
1564
1697
|
}): Promise<CapxulResult<ActivityDetail | null>>;
|
|
1565
|
-
annotate(input: ActivityAnnotationInput, options?:
|
|
1566
|
-
readonly signal?: AbortSignal;
|
|
1567
|
-
}): Promise<CapxulResult<ActivityAnnotation>>;
|
|
1698
|
+
annotate(input: ActivityAnnotationInput, options?: InvocationControls): Promise<CapxulResult<ActivityAnnotation>>;
|
|
1568
1699
|
}
|
|
1569
1700
|
interface OfframpQuoteInput {
|
|
1570
1701
|
readonly actor?: ActorReference;
|
|
@@ -1718,25 +1849,19 @@ interface AddressBookLabelInput {
|
|
|
1718
1849
|
interface AddressBookListInput {
|
|
1719
1850
|
readonly includeHidden?: boolean;
|
|
1720
1851
|
}
|
|
1852
|
+
/**
|
|
1853
|
+
* #1960 R02: the declared option type is the `InvocationControls` these methods
|
|
1854
|
+
* have always forwarded to `deps.invocationControls`. Naming it lets a caller
|
|
1855
|
+
* that captured one user attempt hand the same `correlation_id`/`journey_id` to
|
|
1856
|
+
* the operation the attempt starts. `{ signal }` callers are unaffected.
|
|
1857
|
+
*/
|
|
1721
1858
|
interface AddressBookMethods {
|
|
1722
|
-
list(input?: AddressBookListInput, options?:
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
add(input: AddressBookAddInput, options?: {
|
|
1729
|
-
readonly signal?: AbortSignal;
|
|
1730
|
-
}): Promise<CapxulResult<AddressBookEntry>>;
|
|
1731
|
-
hide(entryId: PartyId, options?: {
|
|
1732
|
-
readonly signal?: AbortSignal;
|
|
1733
|
-
}): Promise<CapxulResult<AddressBookEntry>>;
|
|
1734
|
-
unhide(entryId: PartyId, options?: {
|
|
1735
|
-
readonly signal?: AbortSignal;
|
|
1736
|
-
}): Promise<CapxulResult<AddressBookEntry>>;
|
|
1737
|
-
label(input: AddressBookLabelInput, options?: {
|
|
1738
|
-
readonly signal?: AbortSignal;
|
|
1739
|
-
}): Promise<CapxulResult<AddressBookEntry>>;
|
|
1859
|
+
list(input?: AddressBookListInput, options?: InvocationControls): Promise<CapxulResult<readonly AddressBookEntry[]>>;
|
|
1860
|
+
get(entryId: PartyId, options?: InvocationControls): Promise<CapxulResult<AddressBookEntry | null>>;
|
|
1861
|
+
add(input: AddressBookAddInput, options?: InvocationControls): Promise<CapxulResult<AddressBookEntry>>;
|
|
1862
|
+
hide(entryId: PartyId, options?: InvocationControls): Promise<CapxulResult<AddressBookEntry>>;
|
|
1863
|
+
unhide(entryId: PartyId, options?: InvocationControls): Promise<CapxulResult<AddressBookEntry>>;
|
|
1864
|
+
label(input: AddressBookLabelInput, options?: InvocationControls): Promise<CapxulResult<AddressBookEntry>>;
|
|
1740
1865
|
}
|
|
1741
1866
|
interface ActorRequest {
|
|
1742
1867
|
readonly id: string;
|
|
@@ -2676,6 +2801,13 @@ interface CapxulClient {
|
|
|
2676
2801
|
* NOT on the public `client.accounts` surface.
|
|
2677
2802
|
*/
|
|
2678
2803
|
readonly accounts: AccountsFaucetMethods;
|
|
2804
|
+
/**
|
|
2805
|
+
* Ponder read seam (P02). Present only when the client was built with an
|
|
2806
|
+
* indexer origin, so every reader treats it as optional. Ponder holds what
|
|
2807
|
+
* the chain said; Convex holds what the user meant. Reads here never run
|
|
2808
|
+
* inside a write path and never wait for a finality tag.
|
|
2809
|
+
*/
|
|
2810
|
+
readonly indexer?: IndexerReadPort;
|
|
2679
2811
|
/** Quarantined Reference-harness evidence; never a product Organization method. */
|
|
2680
2812
|
readonly organizationSetup: OrganizationSetupProofMethods;
|
|
2681
2813
|
/** Product telemetry projection for internal composition and test harnesses. */
|
package/dist/testing/index.d.mts
CHANGED
|
@@ -1,10 +1,85 @@
|
|
|
1
1
|
import { h as CapxulResult } from "../OAuthBearerAuthClient-C-ip-z8M.mjs";
|
|
2
|
-
import { er as TelemetryEvent, f as CapxulClient, i as ObservationAdapter, n as ProductionSignerControls, nr as TelemetryIdentifyInput, pr as IdentityTransition, t as CapxulClientInput, tr as TelemetryGroupInput } from "../production-
|
|
2
|
+
import { er as TelemetryEvent, f as CapxulClient, i as ObservationAdapter, n as ProductionSignerControls, nr as TelemetryIdentifyInput, pr as IdentityTransition, t as CapxulClientInput, tr as TelemetryGroupInput } from "../production-A1zW1JPt.mjs";
|
|
3
3
|
import { Effect, Layer } from "effect";
|
|
4
4
|
//#region src/testing/production-client.d.ts
|
|
5
5
|
/** Create the one production client with per-client controls at its signing boundaries. */
|
|
6
6
|
declare function createCapxulProductionTestClient(input: CapxulClientInput, controls: ProductionSignerControls): Promise<CapxulResult<CapxulClient>>;
|
|
7
7
|
//#endregion
|
|
8
|
+
//#region src/testing/signer-faults.d.ts
|
|
9
|
+
/**
|
|
10
|
+
* Which provider refusal to inject. The codes are the ones the SDK's own
|
|
11
|
+
* classifiers decode (`src/signer.ts`), so a fault reaches the public error
|
|
12
|
+
* contract through the real decode path rather than a shortcut.
|
|
13
|
+
*
|
|
14
|
+
* - `missing-signer` — Openfort's typed not-ready code. `openfortSignerNotReadyCode`
|
|
15
|
+
* decodes it, so the production recovery wrapper runs its single retry (A02).
|
|
16
|
+
* - `user-rejected` — the EIP-1193 4001 result. `signerFailure` decodes it to
|
|
17
|
+
* `SIGNER_REJECTED`, an expected refusal (A08).
|
|
18
|
+
* - `unexpected` — no provider code at all, so it stays an unexpected
|
|
19
|
+
* `PROVIDER_ERROR` (A09).
|
|
20
|
+
*/
|
|
21
|
+
type SignerFaultKind = "missing-signer" | "user-rejected" | "unexpected";
|
|
22
|
+
/** The value a controlled fault throws. Named so a test can assert its origin. */
|
|
23
|
+
declare class ControlledSignerFault extends Error {
|
|
24
|
+
readonly kind: SignerFaultKind;
|
|
25
|
+
/** Provider code the SDK classifiers read; absent for `unexpected`. */
|
|
26
|
+
readonly code: string | number | undefined;
|
|
27
|
+
constructor(kind: SignerFaultKind);
|
|
28
|
+
}
|
|
29
|
+
/** Build one fault value without the harness, for a caller that writes its own control. */
|
|
30
|
+
declare function signerFaultCause(kind: SignerFaultKind): ControlledSignerFault;
|
|
31
|
+
interface SignerFaultPlan {
|
|
32
|
+
/**
|
|
33
|
+
* Fault the next embedded signature ONCE, beneath the production recovery
|
|
34
|
+
* wrapper, then delegate every later call to the real signer. The address
|
|
35
|
+
* read and the readiness cycle still run first, so the fault lands at the
|
|
36
|
+
* actual final signing boundary after the prepared-command equality checks.
|
|
37
|
+
*/
|
|
38
|
+
readonly signOnce?: SignerFaultKind;
|
|
39
|
+
/**
|
|
40
|
+
* Call the production signer's own `resetSession()` ONCE, immediately before
|
|
41
|
+
* the next signature and after the command is prepared. The delegate then
|
|
42
|
+
* re-runs the real readiness cycle, so the configure request under test is
|
|
43
|
+
* the first one the browser observes after the `reset-session` record (A03).
|
|
44
|
+
*
|
|
45
|
+
* Throws when the composed signer owns no session — a silently skipped reset
|
|
46
|
+
* would leave the run looking green while proving nothing.
|
|
47
|
+
*/
|
|
48
|
+
readonly resetSessionBeforeNextSign?: boolean;
|
|
49
|
+
}
|
|
50
|
+
/** What the seam did. JSON-serializable, so a browser test can read it back. */
|
|
51
|
+
interface SignerSeamRecord {
|
|
52
|
+
readonly boundary: "reset-session" | "embedded-sign";
|
|
53
|
+
/** 1-based count of calls at that boundary. */
|
|
54
|
+
readonly attempt: number;
|
|
55
|
+
readonly hash?: string;
|
|
56
|
+
/** Present only on the call that actually threw. */
|
|
57
|
+
readonly fault?: SignerFaultKind;
|
|
58
|
+
readonly atMs: number;
|
|
59
|
+
}
|
|
60
|
+
interface ArmedSignerSeam {
|
|
61
|
+
/** Hand these to `createCapxulProductionTestClient`. */
|
|
62
|
+
readonly controls: ProductionSignerControls;
|
|
63
|
+
/** Oldest first. Empty until the client reaches a signing boundary. */
|
|
64
|
+
readonly records: () => readonly SignerSeamRecord[];
|
|
65
|
+
/** True once every armed one-shot has fired. */
|
|
66
|
+
readonly spent: () => boolean;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Arm one-shot faults on the production embedded signer.
|
|
70
|
+
*
|
|
71
|
+
* ```ts
|
|
72
|
+
* const seam = armSignerFaults({ signOnce: "user-rejected" });
|
|
73
|
+
* const client = await createCapxulProductionTestClient(input, seam.controls);
|
|
74
|
+
* const result = await client.org(orgId).permissions.create(command);
|
|
75
|
+
* // result.error.code === "SIGNER_REJECTED"; nothing was submitted.
|
|
76
|
+
* // seam.records() names the boundary that refused.
|
|
77
|
+
* ```
|
|
78
|
+
*
|
|
79
|
+
* An empty plan installs no controls, which composes the ordinary client.
|
|
80
|
+
*/
|
|
81
|
+
declare function armSignerFaults(plan?: SignerFaultPlan): ArmedSignerSeam;
|
|
82
|
+
//#endregion
|
|
8
83
|
//#region src/testing/telemetry/RecordingTelemetryAdapter.d.ts
|
|
9
84
|
type RecordingTelemetryOperation = {
|
|
10
85
|
readonly type: "emit";
|
|
@@ -56,4 +131,4 @@ interface CapxulTestObservation {
|
|
|
56
131
|
*/
|
|
57
132
|
declare function createCapxulTestClient(options?: CreateCapxulTestClientOptions): CapxulTestClient;
|
|
58
133
|
//#endregion
|
|
59
|
-
export { CapxulTestClient, CapxulTestClock, CapxulTestObservation, CreateCapxulTestClientOptions, type ProductionSignerControls, SeedTestIdentityInput, createCapxulProductionTestClient, createCapxulTestClient };
|
|
134
|
+
export { type ArmedSignerSeam, CapxulTestClient, CapxulTestClock, CapxulTestObservation, ControlledSignerFault, CreateCapxulTestClientOptions, type ProductionSignerControls, SeedTestIdentityInput, type SignerFaultKind, type SignerFaultPlan, type SignerSeamRecord, armSignerFaults, createCapxulProductionTestClient, createCapxulTestClient, signerFaultCause };
|
package/dist/testing/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { D as fromWei, P as redactTelemetryEvent, a as smartAccountErrorFromCapxul, c as identityErrorFromCapxul, i as assembleCapxulClient, l as convexCallErrorFromCapxul, n as createCapxulClientWithSignerControls, o as accountReadErrorFromCapxul, s as wireChainId, u as bootstrapErrorFromCapxul, v as toWei } from "../production-
|
|
2
|
-
import {
|
|
1
|
+
import { D as fromWei, P as redactTelemetryEvent, a as smartAccountErrorFromCapxul, c as identityErrorFromCapxul, i as assembleCapxulClient, l as convexCallErrorFromCapxul, n as createCapxulClientWithSignerControls, o as accountReadErrorFromCapxul, s as wireChainId, u as bootstrapErrorFromCapxul, v as toWei } from "../production-7jr-KUvX.mjs";
|
|
2
|
+
import { G as toHandle, H as toEmail, I as toAuthUserId, K as toJwtToken, M as toAddress, N as toAllowedOrigin, P as toAppId, Q as toPublishableKey, R as toChainId, U as toEpochMs, V as toDurationMs, W as toEpochSeconds, ct as CapxulError, d as authClientPortFromPromiseAdapter, et as toSessionToken, it as validateHandle, j as toAccountId, n as readClockNow, q as toKycTier, r as InMemoryAuthCacheAdapter, ut as Errors, y as deriveCapxulSafeAddress, z as toCountryCode } from "../OAuthBearerAuthClient-CbU_W9Sp.mjs";
|
|
3
3
|
import { keccak256 } from "viem";
|
|
4
4
|
import { Effect, Result, Semaphore } from "effect";
|
|
5
5
|
import { getFunctionName } from "convex/server";
|
|
@@ -9,6 +9,97 @@ function createCapxulProductionTestClient(input, controls) {
|
|
|
9
9
|
return createCapxulClientWithSignerControls(input, controls);
|
|
10
10
|
}
|
|
11
11
|
//#endregion
|
|
12
|
+
//#region src/testing/signer-faults.ts
|
|
13
|
+
const FAULT_CODES = {
|
|
14
|
+
"missing-signer": "MISSING_SIGNER",
|
|
15
|
+
"user-rejected": 4001,
|
|
16
|
+
unexpected: void 0
|
|
17
|
+
};
|
|
18
|
+
/** The value a controlled fault throws. Named so a test can assert its origin. */
|
|
19
|
+
var ControlledSignerFault = class extends Error {
|
|
20
|
+
kind;
|
|
21
|
+
/** Provider code the SDK classifiers read; absent for `unexpected`. */
|
|
22
|
+
code;
|
|
23
|
+
constructor(kind) {
|
|
24
|
+
super(`controlled signer fault (${kind})`);
|
|
25
|
+
this.name = "ControlledSignerFault";
|
|
26
|
+
this.kind = kind;
|
|
27
|
+
this.code = FAULT_CODES[kind];
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
/** Build one fault value without the harness, for a caller that writes its own control. */
|
|
31
|
+
function signerFaultCause(kind) {
|
|
32
|
+
return new ControlledSignerFault(kind);
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Arm one-shot faults on the production embedded signer.
|
|
36
|
+
*
|
|
37
|
+
* ```ts
|
|
38
|
+
* const seam = armSignerFaults({ signOnce: "user-rejected" });
|
|
39
|
+
* const client = await createCapxulProductionTestClient(input, seam.controls);
|
|
40
|
+
* const result = await client.org(orgId).permissions.create(command);
|
|
41
|
+
* // result.error.code === "SIGNER_REJECTED"; nothing was submitted.
|
|
42
|
+
* // seam.records() names the boundary that refused.
|
|
43
|
+
* ```
|
|
44
|
+
*
|
|
45
|
+
* An empty plan installs no controls, which composes the ordinary client.
|
|
46
|
+
*/
|
|
47
|
+
function armSignerFaults(plan = {}) {
|
|
48
|
+
const records = [];
|
|
49
|
+
let pendingFault = plan.signOnce;
|
|
50
|
+
let pendingReset = plan.resetSessionBeforeNextSign === true;
|
|
51
|
+
let resetCalls = 0;
|
|
52
|
+
let embeddedCalls = 0;
|
|
53
|
+
function record(entry) {
|
|
54
|
+
records.push({
|
|
55
|
+
...entry,
|
|
56
|
+
atMs: Date.now()
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
const resetThenSign = async (hash, signer) => {
|
|
60
|
+
if (pendingReset) {
|
|
61
|
+
pendingReset = false;
|
|
62
|
+
resetCalls += 1;
|
|
63
|
+
if (signer.resetSession === void 0) throw new Error("armSignerFaults: resetSessionBeforeNextSign needs the embedded browser signer; this client composed a signer with no session");
|
|
64
|
+
signer.resetSession();
|
|
65
|
+
record({
|
|
66
|
+
boundary: "reset-session",
|
|
67
|
+
attempt: resetCalls,
|
|
68
|
+
hash
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
return signer.signUserOpHash(hash);
|
|
72
|
+
};
|
|
73
|
+
const faultThenDelegate = async (hash, delegate) => {
|
|
74
|
+
embeddedCalls += 1;
|
|
75
|
+
const fault = pendingFault;
|
|
76
|
+
if (fault === void 0) {
|
|
77
|
+
record({
|
|
78
|
+
boundary: "embedded-sign",
|
|
79
|
+
attempt: embeddedCalls,
|
|
80
|
+
hash
|
|
81
|
+
});
|
|
82
|
+
return delegate(hash);
|
|
83
|
+
}
|
|
84
|
+
pendingFault = void 0;
|
|
85
|
+
record({
|
|
86
|
+
boundary: "embedded-sign",
|
|
87
|
+
attempt: embeddedCalls,
|
|
88
|
+
hash,
|
|
89
|
+
fault
|
|
90
|
+
});
|
|
91
|
+
throw signerFaultCause(fault);
|
|
92
|
+
};
|
|
93
|
+
return {
|
|
94
|
+
controls: {
|
|
95
|
+
...plan.resetSessionBeforeNextSign === true ? { signUserOpHash: resetThenSign } : {},
|
|
96
|
+
...plan.signOnce === void 0 ? {} : { signEmbeddedUserOpHash: faultThenDelegate }
|
|
97
|
+
},
|
|
98
|
+
records: () => records.slice(),
|
|
99
|
+
spent: () => pendingFault === void 0 && !pendingReset
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
//#endregion
|
|
12
103
|
//#region src/testing/account/InMemoryAccountReadAdapter.ts
|
|
13
104
|
var InMemoryAccountReadAdapter = class {
|
|
14
105
|
#deps;
|
|
@@ -915,4 +1006,4 @@ function createCapxulTestClient(options = {}) {
|
|
|
915
1006
|
};
|
|
916
1007
|
}
|
|
917
1008
|
//#endregion
|
|
918
|
-
export { createCapxulProductionTestClient, createCapxulTestClient };
|
|
1009
|
+
export { ControlledSignerFault, armSignerFaults, createCapxulProductionTestClient, createCapxulTestClient, signerFaultCause };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@capxul/sdk",
|
|
3
|
-
"version": "4.2.0-rc.
|
|
3
|
+
"version": "4.2.0-rc.11",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "git+https://github.com/Xelmar-tech/infrastructure.git",
|
|
@@ -48,11 +48,11 @@
|
|
|
48
48
|
"vite-plus": "0.3.0",
|
|
49
49
|
"vitest": "4.1.11",
|
|
50
50
|
"@capxul/config": "0.3.0",
|
|
51
|
-
"@capxul/
|
|
51
|
+
"@capxul/observability": "4.2.0-rc.11",
|
|
52
52
|
"@capxul/types": "0.3.0",
|
|
53
|
-
"@capxul/
|
|
54
|
-
"@capxul/
|
|
55
|
-
"@capxul/
|
|
53
|
+
"@capxul/typescript-config": "0.0.0",
|
|
54
|
+
"@capxul/errors": "0.3.0",
|
|
55
|
+
"@capxul/wire": "0.7.0"
|
|
56
56
|
},
|
|
57
57
|
"_permissionlessPinReason": "permissionless.toSafeSmartAccount is pinned to 0.3.4 for live Safe deployment E2E. Counterfactual address fixtures captured 2026-05-17 in packages/backend/convex/_shared/__tests__/counterfactual.test.ts and packages/config/tests/safe.test.ts must be re-verified before upgrading.",
|
|
58
58
|
"scripts": {
|