@capxul/sdk-react 2.0.2 → 2.1.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.
@@ -81,11 +81,10 @@ const capxulKeys = {
81
81
  "permissions"
82
82
  ],
83
83
  activity: ["capxul", "activity"],
84
- activityDetail: (kind, id) => [
84
+ activityDetail: (reference) => [
85
85
  "capxul",
86
86
  "activity",
87
- kind,
88
- id ?? "pending"
87
+ reference ?? "pending"
89
88
  ],
90
89
  holdings: ["capxul", "holdings"],
91
90
  payments: ["capxul", "payments"],
package/dist/index.d.mts CHANGED
@@ -69,7 +69,8 @@ type UseCapxulAccountFundReturn = UseMutationResult<{
69
69
  declare function useCapxulAccountFund(): UseCapxulAccountFundReturn;
70
70
  //#endregion
71
71
  //#region src/hooks/use-capxul-money.d.ts
72
- type UseCapxulPayReturn = UseMutationResult<Payment, CapxulError, PaymentsPayInput>;
72
+ type CapxulPayIntent = PaymentsPayInput;
73
+ type UseCapxulPayReturn = UseMutationResult<Payment, CapxulError, CapxulPayIntent>;
73
74
  declare function useCapxulPay(): UseCapxulPayReturn;
74
75
  type UseCapxulCreateCommitmentReturn = UseMutationResult<Payment, CapxulError, PaymentsPayInput & {
75
76
  readonly timing: Exclude<PaymentTiming, {
@@ -321,7 +322,7 @@ declare function useCapxulHoldings(): UseQueryResult<CurrentHoldings, CapxulErro
321
322
  //#endregion
322
323
  //#region src/hooks/use-capxul-orgs.d.ts
323
324
  /**
324
- * List the Orgs you belong to (canon §C1 "Org list" / §C3). Binds directly to
325
+ * List the Organizations that the current user can access. Binds directly to
325
326
  * the locked `capxul.orgs()` SDK method.
326
327
  */
327
328
  type UseCapxulOrgsReturn = UseQueryResult<readonly OrgView[], CapxulError>;
@@ -341,8 +342,8 @@ type UseCapxulOrgMembersOptions = {
341
342
  readonly enabled?: boolean;
342
343
  };
343
344
  /**
344
- * The members of an Org (canon §C1 "Members" / §C3, D8/D9). Binds directly to
345
- * the entity-scoped `capxul.org(orgId).members()` (D13). Gated by
345
+ * List the members of one Organization. Binds directly to
346
+ * `capxul.org(orgId).members()`. Gated by
346
347
  * `orgId !== undefined`; callers may additionally gate on canonical identity
347
348
  * readiness through `options.enabled`.
348
349
  */
@@ -354,9 +355,8 @@ type UseCapxulOrgRolesOptions = {
354
355
  readonly enabled?: boolean;
355
356
  };
356
357
  /**
357
- * The roles seeded on an Org (canon §C1 "Roles" / §C3, D4/D6). Binds directly
358
- * to the entity-scoped `capxul.org(orgId).roles()` (D13). Gated by
359
- * `orgId !== undefined`. RED until S2.
358
+ * List the roles for one Organization. Binds directly
359
+ * to `capxul.org(orgId).roles()`. Gated by `orgId !== undefined`.
360
360
  */
361
361
  type UseCapxulOrgRolesReturn = UseQueryResult<readonly RoleView[], CapxulError>;
362
362
  declare function useCapxulOrgRoles(orgId: OrgId | undefined, options?: UseCapxulOrgRolesOptions): UseCapxulOrgRolesReturn;
@@ -366,29 +366,26 @@ type UseCapxulOrgTreasuryOptions = {
366
366
  readonly enabled?: boolean;
367
367
  };
368
368
  /**
369
- * The Org treasury — the real M2 `Account` over the Org Safe (canon §C3, D3).
369
+ * Return the Organization treasury Account.
370
370
  * NEVER `AccountStatus` (the deploy-readiness ladder, no balance). Binds
371
- * directly to the entity-scoped `capxul.org(orgId).treasury()` (D13). Gated by
372
- * `orgId !== undefined`. RED until S1.
371
+ * Binds directly to `capxul.org(orgId).treasury()`. Gated by `orgId !== undefined`.
373
372
  */
374
373
  type UseCapxulOrgTreasuryReturn = UseQueryResult<Account, CapxulError>;
375
374
  declare function useCapxulOrgTreasury(orgId: OrgId | undefined, options?: UseCapxulOrgTreasuryOptions): UseCapxulOrgTreasuryReturn;
376
375
  //#endregion
377
376
  //#region src/hooks/use-capxul-create-org.d.ts
378
377
  /**
379
- * Create an Org (canon §C2 J1 / §C3, S1). Binds directly to the locked
378
+ * Create an Organization. Binds directly to the
380
379
  * `capxul.createOrg(input)` SDK method. On success, invalidates the org list.
381
- * RED until S1 — `mutate` rejects with `Errors.notImplemented("org","createOrg")`.
382
380
  */
383
381
  type UseCapxulCreateOrgReturn = UseMutationResult<OrgView, CapxulError, CreateOrgInput>;
384
382
  declare function useCapxulCreateOrg(): UseCapxulCreateOrgReturn;
385
383
  //#endregion
386
384
  //#region src/hooks/use-capxul-invite-member.d.ts
387
385
  /**
388
- * Invite a member to an Org by email (canon §C2 J2 virality loop / §C3, S3, D8).
389
- * Entity-scoped via the closed-over `orgId` (D13). Binds directly to
386
+ * Invite a member to an Organization by email. The hook uses the closed-over `orgId`.
387
+ * It binds directly to
390
388
  * `capxul.org(orgId).invite(input)`. On success, invalidates the member list.
391
- * RED until S3 — `mutate` rejects with `Errors.notImplemented("org","invite")`.
392
389
  */
393
390
  type UseCapxulInviteMemberReturn = UseMutationResult<MemberView, CapxulError, InviteMemberInput>;
394
391
  declare function useCapxulInviteMember(orgId: OrgId | undefined): UseCapxulInviteMemberReturn;
@@ -421,7 +418,7 @@ type OnboardingOrigin = {
421
418
  readonly kind: "organization";
422
419
  readonly organizationId: string;
423
420
  } | {
424
- readonly kind: "proof";
421
+ readonly kind: "verification";
425
422
  readonly organizationId?: string;
426
423
  };
427
424
  type OnboardingJourney = {
@@ -445,7 +442,7 @@ type OnboardingJourneyPosition = {
445
442
  readonly intent: OnboardingIntent;
446
443
  readonly step: OnboardingStep;
447
444
  readonly organizationId?: string;
448
- readonly proof: boolean;
445
+ readonly verification: boolean;
449
446
  };
450
447
  declare function startOnboardingJourney(input: {
451
448
  readonly intent: OnboardingIntent;
package/dist/index.mjs CHANGED
@@ -1,7 +1,8 @@
1
1
  "use client";
2
- import { a as useCapxulAuth, c as useCapxulIdentityOrNull, d as capxulKeys, f as useCapxulClientOrNull, i as entered, l as useCapxulSend, n as CapxulOnboardingController, o as useCapxulDestination, p as useCapxul, r as CapxulProvider, s as useCapxulIdentity, t as CapxulAuthenticationController, u as useCapxulTransitions } from "./controllers-DeXOe2mR.mjs";
2
+ import { a as useCapxulAuth, c as useCapxulIdentityOrNull, d as capxulKeys, f as useCapxulClientOrNull, i as entered, l as useCapxulSend, n as CapxulOnboardingController, o as useCapxulDestination, p as useCapxul, r as CapxulProvider, s as useCapxulIdentity, t as CapxulAuthenticationController, u as useCapxulTransitions } from "./controllers-Chqyy3HR.mjs";
3
3
  import { useEffect, useState } from "react";
4
4
  import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
5
+ import { fingerprintPaymentIntent } from "@capxul/sdk";
5
6
  //#region ../errors/src/errors.ts
6
7
  const CAPXUL_ERROR_CODES = [
7
8
  "NOT_AUTHENTICATED",
@@ -32,7 +33,7 @@ const CAPXUL_ERROR_CODES = [
32
33
  "WORK_DIED",
33
34
  "ACTOR_STOPPED"
34
35
  ];
35
- var CapxulError = class extends Error {
36
+ var CapxulError$1 = class extends Error {
36
37
  code;
37
38
  details;
38
39
  correlationId;
@@ -47,33 +48,33 @@ var CapxulError = class extends Error {
47
48
  }
48
49
  };
49
50
  const Errors = {
50
- notAuthenticated: (message, opts) => new CapxulError("NOT_AUTHENTICATED", message ?? "Not authenticated", opts?.failure_mode ? { details: { failure_mode: opts.failure_mode } } : void 0),
51
- emailDeliveryFailed: (detail) => new CapxulError("EMAIL_DELIVERY_FAILED", "Failed to send email", { details: { detail } }),
52
- profileNotFound: (authUserId) => new CapxulError("PROFILE_NOT_FOUND", `Profile not found for user ${authUserId}`, { details: { authUserId } }),
53
- smartAccountMissing: (authUserId) => new CapxulError("SMART_ACCOUNT_MISSING", "Smart account not provisioned", { details: { authUserId } }),
54
- playerNotFound: (playerId) => new CapxulError("PLAYER_NOT_FOUND", playerId ? `Openfort player ${playerId} not found` : "Openfort player not found", playerId === void 0 ? void 0 : { details: { playerId } }),
55
- accountNotFound: (accountId) => new CapxulError("ACCOUNT_NOT_FOUND", accountId ? `Openfort account ${accountId} not found` : "Openfort account not found", accountId === void 0 ? void 0 : { details: { accountId } }),
51
+ notAuthenticated: (message, opts) => new CapxulError$1("NOT_AUTHENTICATED", message ?? "Not authenticated", opts?.failure_mode ? { details: { failure_mode: opts.failure_mode } } : void 0),
52
+ emailDeliveryFailed: (detail) => new CapxulError$1("EMAIL_DELIVERY_FAILED", "Failed to send email", { details: { detail } }),
53
+ profileNotFound: (authUserId) => new CapxulError$1("PROFILE_NOT_FOUND", `Profile not found for user ${authUserId}`, { details: { authUserId } }),
54
+ smartAccountMissing: (authUserId) => new CapxulError$1("SMART_ACCOUNT_MISSING", "Smart account not provisioned", { details: { authUserId } }),
55
+ playerNotFound: (playerId) => new CapxulError$1("PLAYER_NOT_FOUND", playerId ? `Openfort player ${playerId} not found` : "Openfort player not found", playerId === void 0 ? void 0 : { details: { playerId } }),
56
+ accountNotFound: (accountId) => new CapxulError$1("ACCOUNT_NOT_FOUND", accountId ? `Openfort account ${accountId} not found` : "Openfort account not found", accountId === void 0 ? void 0 : { details: { accountId } }),
56
57
  providerError: (provider, operation, cause, opts) => {
57
58
  const details = {
58
59
  provider,
59
60
  operation
60
61
  };
61
62
  if (opts?.failure_mode) details.failure_mode = opts.failure_mode;
62
- return new CapxulError("PROVIDER_ERROR", `Provider error: ${provider} ${operation}`, {
63
+ return new CapxulError$1("PROVIDER_ERROR", `Provider error: ${provider} ${operation}`, {
63
64
  cause,
64
65
  details
65
66
  });
66
67
  },
67
- capabilityUnavailable: (provider, operation) => new CapxulError("CAPABILITY_UNAVAILABLE", "Provider capability is unavailable", { details: {
68
+ capabilityUnavailable: (provider, operation) => new CapxulError$1("CAPABILITY_UNAVAILABLE", "Provider capability is unavailable", { details: {
68
69
  provider,
69
70
  operation
70
71
  } }),
71
- invalidInput: (field, reason) => new CapxulError("INVALID_INPUT", `Invalid ${field}: ${reason}`, { details: {
72
+ invalidInput: (field, reason) => new CapxulError$1("INVALID_INPUT", `Invalid ${field}: ${reason}`, { details: {
72
73
  field,
73
74
  reason
74
75
  } }),
75
- envMissing: (name) => new CapxulError("ENV_MISSING", `Environment variable ${name} not configured`, { details: { name } }),
76
- notImplemented: (domain, method) => new CapxulError("NOT_IMPLEMENTED", `${domain}.${method} is not yet implemented. This feature is planned for a future release.`, { details: {
76
+ envMissing: (name) => new CapxulError$1("ENV_MISSING", `Environment variable ${name} not configured`, { details: { name } }),
77
+ notImplemented: (domain, method) => new CapxulError$1("NOT_IMPLEMENTED", `${domain}.${method} is not yet implemented. This feature is planned for a future release.`, { details: {
77
78
  domain,
78
79
  method
79
80
  } }),
@@ -85,7 +86,7 @@ const Errors = {
85
86
  * message string. The redacted message names the timeout budget; the
86
87
  * native `cause` carries the same information for `reportError` fidelity.
87
88
  */
88
- providerTimeout: (provider, operation, timeoutMs) => new CapxulError("PROVIDER_ERROR", `Provider error: ${provider} ${operation} (timeout exceeded ${timeoutMs}ms)`, {
89
+ providerTimeout: (provider, operation, timeoutMs) => new CapxulError$1("PROVIDER_ERROR", `Provider error: ${provider} ${operation} (timeout exceeded ${timeoutMs}ms)`, {
89
90
  details: {
90
91
  provider,
91
92
  operation,
@@ -94,14 +95,14 @@ const Errors = {
94
95
  cause: /* @__PURE__ */ new Error(`timeout: ${operation} exceeded ${timeoutMs}ms`)
95
96
  }),
96
97
  verificationRequired: (details) => {
97
- return new CapxulError("VERIFICATION_REQUIRED", "rail" in details ? `Verification is required before ${details.rail} can use ${details.currentKind}.` : `Verification tier ${details.requiredTier} is required.`, { details });
98
+ return new CapxulError$1("VERIFICATION_REQUIRED", "rail" in details ? `Verification is required before ${details.rail} can use ${details.currentKind}.` : `Verification tier ${details.requiredTier} is required.`, { details });
98
99
  },
99
- insufficientBalance: (asset, available, required) => new CapxulError("INSUFFICIENT_BALANCE", `Insufficient ${asset} balance`, { details: {
100
+ insufficientBalance: (asset, available, required) => new CapxulError$1("INSUFFICIENT_BALANCE", `Insufficient ${asset} balance`, { details: {
100
101
  asset,
101
102
  available,
102
103
  required
103
104
  } }),
104
- invalidRecipient: (reason) => new CapxulError("INVALID_RECIPIENT", `Invalid recipient: ${reason}`, { details: { reason } }),
105
+ invalidRecipient: (reason) => new CapxulError$1("INVALID_RECIPIENT", `Invalid recipient: ${reason}`, { details: { reason } }),
105
106
  /**
106
107
  * The org Zodiac Roles modifier REFUSED the spend on-chain (G4 · #547): the
107
108
  * member's role condition (per-tx cap, per-day allowance, allowed recipient,
@@ -112,7 +113,7 @@ const Errors = {
112
113
  * `disallowed_recipient` / `condition_violation`); leak-safe — no on-chain
113
114
  * identifiers ever enter the details.
114
115
  */
115
- rolePermissionDenied: (details) => new CapxulError("ROLE_PERMISSION_DENIED", `Org role denied this spend on-chain (${details.reason}).`, { details: details.operation === void 0 ? { reason: details.reason } : {
116
+ rolePermissionDenied: (details) => new CapxulError$1("ROLE_PERMISSION_DENIED", `Org role denied this spend on-chain (${details.reason}).`, { details: details.operation === void 0 ? { reason: details.reason } : {
116
117
  reason: details.reason,
117
118
  operation: details.operation
118
119
  } }),
@@ -123,28 +124,28 @@ const Errors = {
123
124
  * Roles condition violation) from an inconclusive infra failure. A confirmed
124
125
  * revert is the ONLY mode the org spend port may map to a roles denial.
125
126
  */
126
- transactionFailed: (operation, cause, extra) => new CapxulError("TRANSACTION_FAILED", `Transaction failed: ${operation}`, {
127
+ transactionFailed: (operation, cause, extra) => new CapxulError$1("TRANSACTION_FAILED", `Transaction failed: ${operation}`, {
127
128
  cause,
128
129
  details: extra?.reason === void 0 ? { operation } : {
129
130
  operation,
130
131
  reason: extra.reason
131
132
  }
132
133
  }),
133
- rateLimited: (details) => new CapxulError("RATE_LIMITED", "Rate limit exceeded", details === void 0 ? void 0 : { details: { ...details } }),
134
- networkError: (operation, cause) => new CapxulError("NETWORK_ERROR", `Network error during ${operation}`, {
134
+ rateLimited: (details) => new CapxulError$1("RATE_LIMITED", "Rate limit exceeded", details === void 0 ? void 0 : { details: { ...details } }),
135
+ networkError: (operation, cause) => new CapxulError$1("NETWORK_ERROR", `Network error during ${operation}`, {
135
136
  cause,
136
137
  details: { operation }
137
138
  }),
138
- unknown: (cause) => new CapxulError("UNKNOWN", "Unknown error", { cause }),
139
- otpExpired: (details) => new CapxulError("OTP_EXPIRED", "Verification code has expired. Request a new one.", details === void 0 ? void 0 : { details: { ...details } }),
140
- signerRejected: (details) => new CapxulError("SIGNER_REJECTED", "Signer rejected the request.", {
139
+ unknown: (cause) => new CapxulError$1("UNKNOWN", "Unknown error", { cause }),
140
+ otpExpired: (details) => new CapxulError$1("OTP_EXPIRED", "Verification code has expired. Request a new one.", details === void 0 ? void 0 : { details: { ...details } }),
141
+ signerRejected: (details) => new CapxulError$1("SIGNER_REJECTED", "Signer rejected the request.", {
141
142
  cause: details.cause,
142
143
  details: details.reason === void 0 ? { source: details.source } : {
143
144
  source: details.source,
144
145
  reason: details.reason
145
146
  }
146
147
  }),
147
- cancelled: (details) => new CapxulError("CANCELLED", "Operation was cancelled.", details === void 0 ? void 0 : { details: { ...details } }),
148
+ cancelled: (details) => new CapxulError$1("CANCELLED", "Operation was cancelled.", details === void 0 ? void 0 : { details: { ...details } }),
148
149
  /**
149
150
  * Method called from a flow state where its precondition fails (TA16). The
150
151
  * SDK's method API short-circuits with this error before driving the
@@ -153,7 +154,7 @@ const Errors = {
153
154
  * `packages/errors/CONTEXT.md`); `validStates`
154
155
  * enumerates the states the method accepts.
155
156
  */
156
- wrongState: (details) => new CapxulError("WRONG_STATE", `${details.method} called from state '${details.currentState}'; valid states: ${details.validStates.join(", ")}`, { details: {
157
+ wrongState: (details) => new CapxulError$1("WRONG_STATE", `${details.method} called from state '${details.currentState}'; valid states: ${details.validStates.join(", ")}`, { details: {
157
158
  ...details,
158
159
  validStates: [...details.validStates]
159
160
  } })
@@ -242,6 +243,137 @@ async function invalidateMoneyState(queryClient, input) {
242
243
  await Promise.all(invalidations);
243
244
  }
244
245
  //#endregion
246
+ //#region src/internal/payment-request-key.ts
247
+ const STORAGE_PREFIX = "capxul.payment.request-key.v3";
248
+ const attemptReleases = /* @__PURE__ */ new Set();
249
+ let pagehideInstalled = false;
250
+ async function storageKey(operation, intent) {
251
+ return `${STORAGE_PREFIX}:${await fingerprintPaymentIntent({
252
+ operation,
253
+ intent
254
+ })}`;
255
+ }
256
+ function paymentLockManager() {
257
+ const manager = navigator.locks;
258
+ if (manager === void 0 || typeof manager.query !== "function") throw new Error("Web Locks are required for payment request keys");
259
+ return manager;
260
+ }
261
+ function attemptLockName(storageSlot, attemptId) {
262
+ return `${storageSlot}:attempt:${attemptId}`;
263
+ }
264
+ async function heldAttemptIds(manager, storageSlot) {
265
+ const prefix = `${storageSlot}:attempt:`;
266
+ return ((await manager.query()).held ?? []).map((lock) => lock.name).filter((name) => name.startsWith(prefix)).map((name) => name.slice(prefix.length));
267
+ }
268
+ async function holdAttemptLock(name) {
269
+ const manager = paymentLockManager();
270
+ let acquired;
271
+ let release;
272
+ const ready = new Promise((resolve) => {
273
+ acquired = resolve;
274
+ });
275
+ const held = manager.request(name, async () => {
276
+ acquired?.();
277
+ await new Promise((resolve) => {
278
+ release = resolve;
279
+ });
280
+ });
281
+ await Promise.race([ready, held.then(() => Promise.reject(/* @__PURE__ */ new Error("Payment attempt lock ended before acquisition")))]);
282
+ return () => {
283
+ release?.();
284
+ held.catch(() => void 0);
285
+ };
286
+ }
287
+ function releaseAttemptOnPagehide(release) {
288
+ attemptReleases.add(release);
289
+ if (!pagehideInstalled) {
290
+ pagehideInstalled = true;
291
+ window.addEventListener("pagehide", () => {
292
+ for (const releaseAttempt of attemptReleases) releaseAttempt();
293
+ attemptReleases.clear();
294
+ });
295
+ }
296
+ return () => attemptReleases.delete(release);
297
+ }
298
+ function readState(key) {
299
+ const stored = localStorage.getItem(key);
300
+ if (stored === null) return null;
301
+ const state = JSON.parse(stored);
302
+ if (typeof state.key !== "string" || !Array.isArray(state.active) || state.active.some((attempt) => typeof attempt !== "string") || typeof state.resolved !== "boolean") throw new TypeError("Invalid payment request key state");
303
+ return state;
304
+ }
305
+ async function beginPaymentRequestKey(operation, intent) {
306
+ const storageSlot = await storageKey(operation, intent);
307
+ let releaseAttemptLock;
308
+ let forgetPagehideRelease;
309
+ try {
310
+ const attemptId = `attempt_${crypto.randomUUID()}`;
311
+ releaseAttemptLock = await holdAttemptLock(attemptLockName(storageSlot, attemptId));
312
+ forgetPagehideRelease = releaseAttemptOnPagehide(releaseAttemptLock);
313
+ const manager = paymentLockManager();
314
+ const key = await manager.request(storageSlot, async () => {
315
+ let state = readState(storageSlot) ?? {
316
+ key: `pay_${Array.from(crypto.getRandomValues(new Uint8Array(16)), (byte) => byte.toString(16).padStart(2, "0")).join("")}`,
317
+ active: [],
318
+ resolved: false
319
+ };
320
+ const registered = state.active.includes(attemptId);
321
+ const active = await heldAttemptIds(manager, storageSlot);
322
+ if (!registered && active.every((id) => id === attemptId) && state.resolved) state = {
323
+ key: `pay_${crypto.randomUUID()}`,
324
+ active: [],
325
+ resolved: false
326
+ };
327
+ state.active = active;
328
+ localStorage.setItem(storageSlot, JSON.stringify(state));
329
+ return state.key;
330
+ });
331
+ let finished = false;
332
+ return {
333
+ key,
334
+ finish: async (succeeded) => {
335
+ if (finished) return;
336
+ try {
337
+ await manager.request(storageSlot, async () => {
338
+ const state = readState(storageSlot);
339
+ if (state === null || state.key !== key) return;
340
+ const attemptIndex = state.active.indexOf(attemptId);
341
+ if (attemptIndex < 0) return;
342
+ state.active.splice(attemptIndex, 1);
343
+ state.active = (await heldAttemptIds(manager, storageSlot)).filter((id) => id !== attemptId);
344
+ state.resolved = succeeded;
345
+ if (state.active.length === 0 && state.resolved) localStorage.removeItem(storageSlot);
346
+ else localStorage.setItem(storageSlot, JSON.stringify(state));
347
+ });
348
+ finished = true;
349
+ } finally {
350
+ forgetPagehideRelease?.();
351
+ releaseAttemptLock?.();
352
+ }
353
+ }
354
+ };
355
+ } catch (cause) {
356
+ forgetPagehideRelease?.();
357
+ releaseAttemptLock?.();
358
+ throw Errors.providerError("sdk-react", "paymentRequestKey", cause);
359
+ }
360
+ }
361
+ async function withPaymentRequestKey(operation, intent, work) {
362
+ const attempt = await beginPaymentRequestKey(operation, intent);
363
+ try {
364
+ const result = await work(attempt.key);
365
+ try {
366
+ await attempt.finish(true);
367
+ } catch {}
368
+ return result;
369
+ } catch (cause) {
370
+ try {
371
+ await attempt.finish(false);
372
+ } catch {}
373
+ throw cause;
374
+ }
375
+ }
376
+ //#endregion
245
377
  //#region src/internal/reject-unresolved-actor.ts
246
378
  /**
247
379
  * Guard for mutation hooks whose variables carry an optional `actor` field
@@ -261,12 +393,22 @@ function rejectUnresolvedActor(variables, operation) {
261
393
  //#region src/hooks/use-capxul-money.ts
262
394
  function useCapxulPay() {
263
395
  const client = useCapxulClientOrNull();
396
+ const identity = useCapxulIdentityOrNull();
397
+ const actorId = identity?.phase === "authenticated" ? identity.session.authUserId : "signed-out";
398
+ const backendScope = client === null ? "pending" : `${client._internal.bootstrap.convexUrl}:${String(client._internal.bootstrap.chainId)}`;
264
399
  const queryClient = useQueryClient();
265
400
  return useMutation({
266
401
  mutationFn: async (input) => {
267
402
  rejectUnresolvedActor(input, "payments.pay");
268
403
  const bootstrappedClient = requireBootstrappedClient(client, "payments.pay");
269
- return unwrapCapxulResult(await bootstrappedClient.payments.pay(input), bootstrappedClient._internal.telemetry, "mutation");
404
+ return withPaymentRequestKey("personal-pay", {
405
+ actorId,
406
+ backendScope,
407
+ input
408
+ }, async (requestKey) => unwrapCapxulResult(await bootstrappedClient.payments.pay({
409
+ ...input,
410
+ requestKey
411
+ }), bootstrappedClient._internal.telemetry, "mutation"));
270
412
  },
271
413
  onSuccess: async (payment) => {
272
414
  await invalidateMoneyState(queryClient, {
@@ -322,13 +464,14 @@ function useCapxulRedirectPayment() {
322
464
  }
323
465
  function useCapxulPayments(options) {
324
466
  const client = useCapxulClientOrNull();
467
+ const identity = useCapxulIdentityOrNull();
325
468
  return useQuery({
326
469
  queryKey: capxulKeys.payments,
327
470
  queryFn: async () => {
328
471
  const bootstrappedClient = requireBootstrappedClient(client, "payments.list");
329
472
  return unwrapCapxulResult(await bootstrappedClient.payments.list(), bootstrappedClient._internal.telemetry);
330
473
  },
331
- enabled: client !== null && (options?.enabled ?? true)
474
+ enabled: client !== null && identity?.phase === "authenticated" && identity.account.at === "claimed" && (options?.enabled ?? true)
332
475
  });
333
476
  }
334
477
  function useCapxulPayment(paymentId, options) {
@@ -347,36 +490,56 @@ function useCapxulPayment(paymentId, options) {
347
490
  //#region src/hooks/use-capxul-organization-payments.ts
348
491
  function useCapxulOrganizationPay(orgId) {
349
492
  const client = useCapxulClientOrNull();
493
+ const backendScope = client === null ? "pending" : `${client._internal.bootstrap.convexUrl}:${String(client._internal.bootstrap.chainId)}`;
350
494
  const queryClient = useQueryClient();
351
495
  return useMutation({
352
496
  mutationFn: async (input) => {
353
497
  const bootstrapped = requireBootstrappedClient(client, "organizationPayments.pay");
354
- return unwrapCapxulResult(await bootstrapped.org(orgId).payments.pay(input), bootstrapped._internal.telemetry, "mutation");
498
+ return withPaymentRequestKey("organization-pay", {
499
+ backendScope,
500
+ orgId,
501
+ ...input
502
+ }, async (requestKey) => unwrapCapxulResult(await bootstrapped.org(orgId).payments.pay({
503
+ ...input,
504
+ requestKey
505
+ }), bootstrapped._internal.telemetry, "mutation"));
355
506
  },
356
- onSuccess: (payment) => invalidateMoneyState(queryClient, {
357
- actor: {
358
- kind: "organization",
359
- organizationId: orgId
360
- },
361
- payment
362
- })
507
+ onSuccess: async (payment) => {
508
+ return invalidateMoneyState(queryClient, {
509
+ actor: {
510
+ kind: "organization",
511
+ organizationId: orgId
512
+ },
513
+ payment
514
+ });
515
+ }
363
516
  });
364
517
  }
365
518
  function useCapxulOrganizationPayBatch(orgId) {
366
519
  const client = useCapxulClientOrNull();
520
+ const backendScope = client === null ? "pending" : `${client._internal.bootstrap.convexUrl}:${String(client._internal.bootstrap.chainId)}`;
367
521
  const queryClient = useQueryClient();
368
522
  return useMutation({
369
523
  mutationFn: async (input) => {
370
524
  const bootstrapped = requireBootstrappedClient(client, "organizationPayments.payBatch");
371
- return unwrapCapxulResult(await bootstrapped.org(orgId).payments.payBatch(input), bootstrapped._internal.telemetry, "mutation");
525
+ return withPaymentRequestKey("organization-pay-batch", {
526
+ backendScope,
527
+ orgId,
528
+ ...input
529
+ }, async (requestKey) => unwrapCapxulResult(await bootstrapped.org(orgId).payments.payBatch({
530
+ ...input,
531
+ requestKey
532
+ }), bootstrapped._internal.telemetry, "mutation"));
372
533
  },
373
- onSuccess: (payments) => Promise.all(payments.map((payment) => invalidateMoneyState(queryClient, {
374
- actor: {
375
- kind: "organization",
376
- organizationId: orgId
377
- },
378
- payment
379
- })))
534
+ onSuccess: async (payments) => {
535
+ return Promise.all(payments.map((payment) => invalidateMoneyState(queryClient, {
536
+ actor: {
537
+ kind: "organization",
538
+ organizationId: orgId
539
+ },
540
+ payment
541
+ })));
542
+ }
380
543
  });
381
544
  }
382
545
  //#endregion
@@ -439,7 +602,7 @@ function useCapxulActivityDetail(reference) {
439
602
  const client = useCapxulClientOrNull();
440
603
  const identity = useCapxulIdentityOrNull();
441
604
  return useQuery({
442
- queryKey: capxulKeys.activityDetail(reference?.kind ?? "payment", reference?.id),
605
+ queryKey: capxulKeys.activityDetail(reference),
443
606
  queryFn: async () => {
444
607
  if (reference === void 0) throw new Error("activity reference is required");
445
608
  const bootstrapped = requireBootstrappedClient(client, "activity.get");
@@ -616,7 +779,7 @@ function loadOnboardingJourney(expectedOwnerId) {
616
779
  const raw = sessionStorage.getItem(JOURNEY_KEY);
617
780
  if (raw === null) return null;
618
781
  if (raw.length > MAX_STORED_JOURNEY_LENGTH) return discardInvalidJourney();
619
- const value = JSON.parse(raw);
782
+ const value = migrateStoredJourney(JSON.parse(raw));
620
783
  if (!isValidJourney(value)) return discardInvalidJourney();
621
784
  if (expectedOwnerId !== void 0 && value.ownerId !== expectedOwnerId) return discardInvalidJourney();
622
785
  if (expectedOwnerId !== void 0) markOnboardingOwnerValidated(expectedOwnerId);
@@ -625,6 +788,20 @@ function loadOnboardingJourney(expectedOwnerId) {
625
788
  return discardInvalidJourney();
626
789
  }
627
790
  }
791
+ function migrateStoredJourney(value) {
792
+ if (!isRecord(value) || !isRecord(value.origin) || value.origin.kind !== "proof") return value;
793
+ const migrated = {
794
+ ...value,
795
+ origin: {
796
+ ...value.origin,
797
+ kind: "verification"
798
+ }
799
+ };
800
+ try {
801
+ sessionStorage.setItem(JOURNEY_KEY, JSON.stringify(migrated));
802
+ } catch {}
803
+ return migrated;
804
+ }
628
805
  function saveOnboardingJourney(journey) {
629
806
  if (!isValidJourney(journey)) {
630
807
  discardInvalidJourney();
@@ -645,17 +822,17 @@ function invalidateOnboardingJourneyObservation() {
645
822
  }
646
823
  /** Where the journey stands — the pure position apps map to their routes. */
647
824
  function onboardingJourneyPosition(journey) {
648
- const proof = journey.origin?.kind === "proof";
825
+ const verification = journey.origin?.kind === "verification";
649
826
  if (journey.organizationId !== void 0) return {
650
827
  intent: journey.intent,
651
828
  step: "provisioning",
652
829
  organizationId: journey.organizationId,
653
- proof
830
+ verification
654
831
  };
655
832
  return {
656
833
  intent: journey.intent,
657
834
  step: journey.step,
658
- proof
835
+ verification
659
836
  };
660
837
  }
661
838
  /**
@@ -718,7 +895,7 @@ function isOrigin(value) {
718
895
  if (!isRecord(value)) return false;
719
896
  if (value.kind === "personal") return hasOnlyKeys(value, ["kind"]);
720
897
  if (value.kind === "organization") return hasOnlyKeys(value, ["kind", "organizationId"]) && isBoundedId(value.organizationId);
721
- return value.kind === "proof" && hasOnlyKeys(value, ["kind", "organizationId"]) && (value.organizationId === void 0 || isBoundedId(value.organizationId));
898
+ return value.kind === "verification" && hasOnlyKeys(value, ["kind", "organizationId"]) && (value.organizationId === void 0 || isBoundedId(value.organizationId));
722
899
  }
723
900
  function isProfileDraft(value) {
724
901
  if (!isRecord(value) || !hasOnlyKeys(value, [
@@ -1,4 +1,4 @@
1
- import { n as CapxulOnboardingController, r as CapxulProvider, t as CapxulAuthenticationController } from "../controllers-DeXOe2mR.mjs";
1
+ import { n as CapxulOnboardingController, r as CapxulProvider, t as CapxulAuthenticationController } from "../controllers-Chqyy3HR.mjs";
2
2
  import "react";
3
3
  import { jsx } from "react/jsx-runtime";
4
4
  import { createCapxulTestClient } from "@capxul/sdk/testing";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@capxul/sdk-react",
3
- "version": "2.0.2",
3
+ "version": "2.1.0",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/Xelmar-tech/infrastructure.git",
@@ -26,7 +26,7 @@
26
26
  "access": "public"
27
27
  },
28
28
  "dependencies": {
29
- "@capxul/sdk": "2.0.2"
29
+ "@capxul/sdk": "2.1.0"
30
30
  },
31
31
  "devDependencies": {
32
32
  "@tanstack/react-query": "^5.66.9",
@@ -42,9 +42,9 @@
42
42
  "react-dom": "^19.2.6",
43
43
  "vite-plus": "0.1.23",
44
44
  "vitest": "npm:@voidzero-dev/vite-plus-test@0.1.23",
45
+ "@capxul/errors": "0.0.1",
45
46
  "@capxul/types": "0.1.0",
46
- "@capxul/typescript-config": "0.0.0",
47
- "@capxul/errors": "0.0.1"
47
+ "@capxul/typescript-config": "0.0.0"
48
48
  },
49
49
  "peerDependencies": {
50
50
  "@tanstack/react-query": "^5.66.9",