@capxul/sdk-react 4.20.0-beta.10 → 4.20.0-beta.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -27,13 +27,13 @@ export function Providers({ children }: { children: React.ReactNode }) {
27
27
  - The provider restores Account readiness after a complete authenticated
28
28
  session. Account and Organization readiness transitions refresh active
29
29
  authenticated queries.
30
- - `CapxulAuthenticationController` and `CapxulOnboardingController` select
31
- app-owned slots and render no SDK-owned DOM. The onboarding controller keeps
32
- no Organization submission: after a reload the Core SDK reads the pending
33
- Organization from the backend and attaches it, so the app re-submits nothing.
34
- A claimed identity with no Organization and no app-held form step reports
35
- `organizationProgress` with `{ at: "loading", orgId: null }` until that
36
- attach lands.
30
+ - CapxulAuthenticationController and CapxulOnboardingController select
31
+ app-owned slots and render no SDK-owned DOM. Organization create begins a
32
+ backend setup and returns its exact ID. The Core SDK actor subscribes to
33
+ that setup. Awaiting authorization, attention, and failure reach actionable
34
+ slots; processing and rechecking reach a finite progress slot.
35
+ - CapxulDashboardAccess combines current setup with current org.me authority.
36
+ A historical ready setup does not grant access after authority is revoked.
37
37
  - Rich-data hooks such as `useCapxulProfile`, `useCapxulOrgs`, members, roles,
38
38
  treasury, and money hooks remain TanStack Query projections. Personal
39
39
  holdings and activity reads start only after the Account is claimed.
@@ -106,14 +106,8 @@ type AccountProgress = Exclude<Readiness, {
106
106
  type AccountFailure = Extract<Readiness, {
107
107
  at: "failed";
108
108
  }>;
109
- type OrgProgress = Exclude<OrgLane, {
110
- at: "failed";
111
- } | {
112
- at: "ready";
113
- }>;
114
- type OrgFailure = Extract<OrgLane, {
115
- at: "failed";
116
- }>;
109
+ type OrgProgress = OrgLane;
110
+ type OrgFailure = OrgLane;
117
111
  type ReadyDestination = Extract<IdentityDestination, {
118
112
  to: "dashboardPersonal" | "dashboardOrganization";
119
113
  }>;
@@ -228,7 +228,7 @@ async function guarded(runtime, verb, options, run) {
228
228
  }
229
229
  }
230
230
  function organizationFailure(reason, state) {
231
- const stateFailure = state.phase === "faulted" ? state.failure : state.phase === "authenticated" && state.account.at === "failed" ? state.account.failure : state.phase === "authenticated" && state.account.at === "claimed" && state.account.org?.at === "failed" ? state.account.org.failure : void 0;
231
+ const stateFailure = state.phase === "faulted" ? state.failure : state.phase === "authenticated" && state.account.at === "failed" ? state.account.failure : state.phase === "authenticated" && state.account.at === "claimed" ? state.account.org?.failure : void 0;
232
232
  if (stateFailure?.code === reason) return {
233
233
  ok: false,
234
234
  reason,
@@ -266,8 +266,7 @@ function normalizeOrganization(organization) {
266
266
  /** The Organization id when its lane is already running or finished, else null. */
267
267
  const runningOrganization = (state) => {
268
268
  if (state.phase !== "authenticated" || state.account.at !== "claimed") return null;
269
- const org = state.account.org;
270
- return org !== null && (org.at === "loading" || org.at === "settingUp" || org.at === "ready") ? org.orgId : null;
269
+ return state.account.org?.orgId ?? null;
271
270
  };
272
271
  /** A superseded session read waits at most this long for the winning read. */
273
272
  const SESSION_SETTLE_LIMIT_MS = 3e4;
@@ -488,20 +487,6 @@ function createAuth(client, clearAuthenticatedQueries) {
488
487
  ok: true,
489
488
  orgId: running
490
489
  };
491
- if (current.phase === "authenticated" && current.account.at === "claimed") {
492
- const org = current.account.org;
493
- if (org?.at === "failed" && org.orgId !== null) {
494
- if (!org.retryable) return {
495
- ok: false,
496
- reason: org.failure.code
497
- };
498
- const retried = await runtime.send({ _tag: "RetryOrganization" }, invocation);
499
- return failure(retried) ?? {
500
- ok: true,
501
- orgId: org.orgId
502
- };
503
- }
504
- }
505
490
  const prepared = await prepareOrganization(submission, invocation);
506
491
  if (!prepared.ok) return prepared;
507
492
  const opened = runningOrganization(runtime.snapshot());
@@ -509,20 +494,24 @@ function createAuth(client, clearAuthenticatedQueries) {
509
494
  ok: true,
510
495
  orgId: opened
511
496
  };
512
- const created = await runtime.send({
513
- _tag: "CreateOrganization",
514
- draft: prepared.organization
515
- }, invocation);
516
- const refused = failure(created);
517
- if (refused !== null) return refused;
518
- const state = created.state;
519
- const org = state.phase === "authenticated" && state.account.at === "claimed" ? state.account.org : null;
520
- return org?.at === "ready" && org.orgId !== null ? {
521
- ok: true,
522
- orgId: org.orgId
523
- } : {
497
+ const begun = await client.onboarding.beginOrganization(prepared.organization, invocation);
498
+ if (!begun.ok) return {
524
499
  ok: false,
525
- reason: "UNKNOWN"
500
+ reason: begun.error.code,
501
+ error: begun.error
502
+ };
503
+ const { orgId, setup } = begun.value;
504
+ if (setup.status === "awaitingAuthorization") {
505
+ const authorized = await client.org(orgId).setup.authorize(invocation);
506
+ if (!authorized.ok) return {
507
+ ok: false,
508
+ reason: authorized.error.code,
509
+ error: authorized.error
510
+ };
511
+ }
512
+ return {
513
+ ok: true,
514
+ orgId: String(orgId)
526
515
  };
527
516
  }).then((result) => {
528
517
  if (result.ok) return result;
@@ -535,8 +524,21 @@ function createAuth(client, clearAuthenticatedQueries) {
535
524
  }),
536
525
  retry: (options) => guarded(runtime, "retry", options, async (invocation) => {
537
526
  const state = runtime.snapshot();
538
- const event = state.phase === "authenticated" && state.account.at === "claimed" ? { _tag: "RetryOrganization" } : { _tag: "RetryAccount" };
539
- const result = await runtime.send(event, invocation);
527
+ const orgId = runningOrganization(state);
528
+ if (orgId !== null) {
529
+ const scoped = client.org(orgId);
530
+ const current = await scoped.setup.get(invocation);
531
+ if (!current.ok) return {
532
+ ok: false,
533
+ reason: current.error.code
534
+ };
535
+ const result = current.value.status === "awaitingAuthorization" ? await scoped.setup.authorize(invocation) : await scoped.setup.resume(invocation);
536
+ return result.ok ? { ok: true } : {
537
+ ok: false,
538
+ reason: result.error.code
539
+ };
540
+ }
541
+ const result = await runtime.send({ _tag: "RetryAccount" }, invocation);
540
542
  return failure(result) ?? { ok: true };
541
543
  }),
542
544
  resumeSubmittedOrganization: (submission, options) => guarded(runtime, "createOrganization", options, async (invocation) => {
@@ -580,7 +582,7 @@ function CapxulIdentityProvider({ client, children }) {
580
582
  const account = actingAccount(state);
581
583
  const accountChanged = previousAccount !== void 0 && account !== void 0 && account !== previousAccount;
582
584
  previousAccount = account;
583
- if (record.outcome === "applied" && (record.from !== record.to || accountChanged) && (record.to === "authenticated:claimed" || record.to === "authenticated:claimed:ready")) queryClient.invalidateQueries({
585
+ if (record.outcome === "applied" && (record.from !== record.to || accountChanged) && (record.to === "authenticated:claimed" || record.to === "authenticated:claimed:organization")) queryClient.invalidateQueries({
584
586
  queryKey: capxulKeys.root,
585
587
  ...accountChanged ? { refetchType: "none" } : {}
586
588
  });
@@ -922,15 +924,15 @@ function CapxulOnboardingController(props) {
922
924
  if (submission !== null && state.account.at === "claimed") {
923
925
  const org = state.account.org;
924
926
  if (org === null) return organizationForm(props, state, auth, submission);
925
- if (org.at === "failed") {
926
- const retry = org.retryable ? org.orgId === null ? (options) => auth.createOrganization(submission, options) : auth.retry : void 0;
927
+ if (org.observation === "error" || org.setup?.status === "failed" || org.setup?.status === "needsAttention" || org.setup?.status === "awaitingAuthorization") {
928
+ const retry = org.orgId === null ? (options) => auth.createOrganization(submission, options) : auth.retry;
927
929
  return slots.organizationFailure({
928
930
  state,
929
931
  org,
930
- ...retry === void 0 ? {} : { retry }
932
+ retry
931
933
  });
932
934
  }
933
- if (org.at !== "ready") return slots.organizationProgress({
935
+ if (org.setup?.status !== "ready" || org.observation !== "connected") return slots.organizationProgress({
934
936
  state,
935
937
  org
936
938
  });
package/dist/index.d.mts CHANGED
@@ -1,7 +1,7 @@
1
- import { A as entered, C as CreateOrganizationFailure, D as OrganizationDetails, E as InvocationOptions, F as useCapxulTransitions, M as useCapxulDestination, N as useCapxulIdentity, O as ProfileDetails, P as useCapxulSend, S as CapxulSend, T as Destination, _ as ReadyDestination, a as AuthenticationSlots, b as Slot, c as ControllerAction, d as OnboardingControllerProps, f as OrgFailure, g as ProfileSlotProps, h as PendingAuthState, i as AuthenticatedState, j as useCapxulAuth, k as SendResult, l as FaultedState, m as OtpPendingState, n as AccountProgress, o as CapxulAuthenticationController, p as OrgProgress, r as ActionResult, s as CapxulOnboardingController, t as AccountFailure, u as NavigationAction, v as RetryAction, w as CreateOrganizationSubmission, x as CapxulAuth, y as SignedOutState } from "./controllers-BWmKRFy6.mjs";
1
+ import { A as entered, C as CreateOrganizationFailure, D as OrganizationDetails, E as InvocationOptions, F as useCapxulTransitions, M as useCapxulDestination, N as useCapxulIdentity, O as ProfileDetails, P as useCapxulSend, S as CapxulSend, T as Destination, _ as ReadyDestination, a as AuthenticationSlots, b as Slot, c as ControllerAction, d as OnboardingControllerProps, f as OrgFailure, g as ProfileSlotProps, h as PendingAuthState, i as AuthenticatedState, j as useCapxulAuth, k as SendResult, l as FaultedState, m as OtpPendingState, n as AccountProgress, o as CapxulAuthenticationController, p as OrgProgress, r as ActionResult, s as CapxulOnboardingController, t as AccountFailure, u as NavigationAction, v as RetryAction, w as CreateOrganizationSubmission, x as CapxulAuth, y as SignedOutState } from "./controllers-BMAU2kKH.mjs";
2
2
  import { ReactNode, RefCallback } from "react";
3
3
  import { QueryClient, UseMutationResult, UseQueryResult } from "@tanstack/react-query";
4
- import { Account, AccountRequirement, ActivityAnnotation, ActivityAnnotationInput, ActivityDetail, ActivityKind, ActivityListParams, ActivityPage, ActivityPhase, ActivityRange, ActivityReference, ActivitySummaryTotal, ActorReference, AddressBookEntry, AssetAmount, AssetId, BudgetId, CapxulClient, CapxulError, CapxulResult, CapxulSigner, CreateOrgInput, CurrentHoldings, HostObservability, IdentityDestination, IdentityState, MemberView, Money, MoneyParseErrorReason, OrgId, OrgView, OrganizationAccess, OrganizationPaymentBatchInput, OrganizationPaymentInput, PartyId, Payment, PaymentDirection, PaymentDocumentDownload, PaymentDocumentReadRef, PaymentDocumentRef, PaymentDocumentRender, PaymentStatus, PaymentTiming, PaymentType, PaymentsPayInput, PayrollGroupId, PayrollGroupInput, PayrollRunId, PayrollRunStatus, Permission, PermissionMethods, PermissionReadResult, Profile, RoleView, SubmittedPermissionExecution, isClaimed, isRestoring } from "@capxul/sdk";
4
+ import { Account, AccountRequirement, ActivityAnnotation, ActivityAnnotationInput, ActivityDetail, ActivityKind, ActivityListParams, ActivityPage, ActivityPhase, ActivityRange, ActivityReference, ActivitySummaryTotal, ActorReference, AddressBookEntry, AssetAmount, AssetId, BudgetId, CapxulClient, CapxulError, CapxulResult, CapxulSigner, CurrentHoldings, HostObservability, IdentityDestination, IdentityState, MemberView, Money, MoneyParseErrorReason, OrgId, OrganizationAccess, OrganizationOnboarding, OrganizationOnboardingInput, OrganizationPaymentBatchInput, OrganizationPaymentInput, PartyId, Payment, PaymentDirection, PaymentDocumentDownload, PaymentDocumentReadRef, PaymentDocumentRef, PaymentDocumentRender, PaymentStatus, PaymentTiming, PaymentType, PaymentsPayInput, PayrollGroupId, PayrollGroupInput, PayrollRunId, PayrollRunStatus, Permission, PermissionMethods, PermissionReadResult, Profile, RoleView, SubmittedPermissionExecution, isClaimed, isRestoring } from "@capxul/sdk";
5
5
  //#region src/provider.d.ts
6
6
  type CapxulProviderSharedProps = {
7
7
  /** Bring your own QueryClient; otherwise the provider creates one. */
@@ -199,9 +199,9 @@ declare function useCapxulOrgTreasury(orgId: OrgId | undefined, options?: UseCap
199
199
  //#region src/hooks/use-capxul-create-org.d.ts
200
200
  /**
201
201
  * Create an Organization. Binds directly to the
202
- * `capxul.createOrg(input)` SDK method. On success, invalidates the org list.
202
+ * `onboarding.completeOrganization(input)` SDK method. On success, invalidates the org list.
203
203
  */
204
- type UseCapxulCreateOrgReturn = UseMutationResult<OrgView, CapxulError, CreateOrgInput>;
204
+ type UseCapxulCreateOrgReturn = UseMutationResult<OrganizationOnboarding, CapxulError, OrganizationOnboardingInput>;
205
205
  declare function useCapxulCreateOrg(): UseCapxulCreateOrgReturn;
206
206
  //#endregion
207
207
  //#region src/headless/journey/journey-state.d.ts
@@ -840,14 +840,14 @@ type DashboardScope = {
840
840
  };
841
841
  type DashboardAccessPhase = "checking" | "error" | "redirect" | "granted";
842
842
  /**
843
- * Which of the three probes produced the error the screen is showing. The
843
+ * Which probe produced the error the screen is showing. The
844
844
  * error code alone cannot say: a provider failure reads the same whether the
845
845
  * session read, the membership list or the lifecycle read raised it, and a
846
846
  * consumer that reports a displayed gate error has to keep those apart
847
847
  * (#1963 R05-2). It names the SOURCE only; `retry` still re-runs every failed
848
848
  * probe, exactly as before.
849
849
  */
850
- type DashboardAccessProbe = "session" | "membership" | "lifecycle";
850
+ type DashboardAccessProbe = "session" | "membership" | "lifecycle" | "authority";
851
851
  interface DashboardAccessErrorSlice {
852
852
  readonly error: CapxulError;
853
853
  /** The probe the surfaced error came from, in the frozen precedence order. */
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  "use client";
2
- import { _ as useCapxul, a as useCapxulAuth, c as useCapxulIdentityOrNull, d as toInvocationControls, f as toInvocationOptions, g as useCapxulClientOrNull, h as capxulKeys, i as entered, l as useCapxulSend, m as actingAccount, n as CapxulOnboardingController, o as useCapxulDestination, p as accountKey, r as CapxulProvider, s as useCapxulIdentity, t as CapxulAuthenticationController, u as useCapxulTransitions } from "./controllers-L1UqwWS1.mjs";
2
+ import { _ as useCapxul, a as useCapxulAuth, c as useCapxulIdentityOrNull, d as toInvocationControls, f as toInvocationOptions, g as useCapxulClientOrNull, h as capxulKeys, i as entered, l as useCapxulSend, m as actingAccount, n as CapxulOnboardingController, o as useCapxulDestination, p as accountKey, r as CapxulProvider, s as useCapxulIdentity, t as CapxulAuthenticationController, u as useCapxulTransitions } from "./controllers-BV3e8WD3.mjs";
3
3
  import { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
4
4
  import { useInfiniteQuery, useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
5
5
  import { CAPXUL_OPERATIONS, CapxulError, EVM_ADDRESS_RE, Errors, HANDLE_RE, PAYMENT_DIRECTIONS, PAYMENT_STATUSES, assetSymbolFor, fingerprintPaymentIntent, formatAssetAmount, formatMoney, isCapxulError, isClaimed, isClaimed as isClaimed$1, isMoneyParseError, isRestoring, isRestoring as isRestoring$1, parseAssetAmount, parseMoney, paymentPhase, requireAsset, resolveIdentityDestination, toEvmAddress, toPartyId, toRaw, valueActivitySummary } from "@capxul/sdk";
@@ -538,7 +538,7 @@ function useCapxulCreateOrg() {
538
538
  const queryClient = useQueryClient();
539
539
  return useMutation({
540
540
  mutationFn: async (input) => {
541
- return unwrapCapxulResult(await requireBootstrappedClient(client, "createOrg").createOrg(input));
541
+ return unwrapCapxulResult(await requireBootstrappedClient(client, CAPXUL_OPERATIONS.onboarding.completeOrganization).onboarding.completeOrganization(input));
542
542
  },
543
543
  onSuccess: async () => {
544
544
  await queryClient.invalidateQueries({ queryKey: capxulKeys.orgs });
@@ -2342,18 +2342,14 @@ function isOrgLaneSettled(identity) {
2342
2342
  if (identity.phase !== "authenticated" || identity.account.at !== "claimed") return true;
2343
2343
  const org = identity.account.org;
2344
2344
  if (org === null) return true;
2345
- switch (org.at) {
2346
- case "ready":
2347
- case "failed": return true;
2348
- default: return false;
2349
- }
2345
+ return org.observation === "connected" && org.setup !== null;
2350
2346
  }
2351
2347
  /** Exactly the Organization asked for, and ready to be used. */
2352
2348
  function isExactOrgReady(lifecycle, orgId) {
2353
2349
  return lifecycle?.status === "ready" && String(lifecycle.orgId) === orgId;
2354
2350
  }
2355
2351
  function isLifecycleSettled(lifecycle) {
2356
- return lifecycle?.status === "ready" || lifecycle?.status === "failed";
2352
+ return lifecycle !== void 0;
2357
2353
  }
2358
2354
  function destinationOrNull(identity) {
2359
2355
  return identity === null ? null : resolveIdentityDestination(identity);
@@ -2381,19 +2377,27 @@ function decidePhase(input) {
2381
2377
  * Which probe produced the surfaced error — the SAME precedence `error` itself
2382
2378
  * is chosen by, written once so the code and its source can never disagree.
2383
2379
  */
2384
- function probeOf(session, membership, lifecycle) {
2380
+ function probeOf(session, membership, lifecycle, authority) {
2385
2381
  if (session !== null) return "session";
2386
2382
  if (membership !== null) return "membership";
2387
2383
  if (lifecycle !== null) return "lifecycle";
2384
+ if (authority !== null) return "authority";
2388
2385
  return null;
2389
2386
  }
2390
2387
  /** The engine — all the logic. NOT exported from the package barrel. */
2391
2388
  function useDashboardAccess(scope) {
2392
2389
  const client = useCapxulClientOrNull();
2390
+ const queryClient = useQueryClient();
2393
2391
  const identity = useCapxulIdentityOrNull();
2394
2392
  const destination = useMemo(() => destinationOrNull(identity), [identity]);
2395
2393
  const orgId = scope.kind === "organization" ? scope.orgId : void 0;
2396
2394
  const claimed = isClaimedOrFalse(identity);
2395
+ const scopeKey = `${orgId ?? ""}:${identity?.phase === "authenticated" ? identity.session.authUserId : ""}:${actingAccount(identity) ?? ""}`;
2396
+ const [observation, setObservation] = useState({
2397
+ scopeKey,
2398
+ status: "loading"
2399
+ });
2400
+ const [subscriptionRetry, setSubscriptionRetry] = useState(0);
2397
2401
  const sessionError = useMemo(() => identity?.phase === "faulted" ? identityError(identity) : null, [identity]);
2398
2402
  const wantsMemberships = orgId !== void 0 && claimed;
2399
2403
  const memberships = useCapxulOrgs({ enabled: wantsMemberships });
@@ -2401,33 +2405,125 @@ function useDashboardAccess(scope) {
2401
2405
  const membershipsSettled = !wantsMemberships || memberships.data !== void 0 || membershipError !== null;
2402
2406
  const hasMembership = orgId !== void 0 && (memberships.data ?? []).some((organization) => String(organization.id) === orgId);
2403
2407
  const wantsLifecycle = wantsMemberships && membershipsSettled && hasMembership;
2408
+ const setupKey = accountKey(capxulKeys.orgLifecycle(orgId), identity);
2404
2409
  const lifecycle = useQuery({
2405
- queryKey: capxulKeys.orgLifecycle(orgId),
2410
+ queryKey: setupKey,
2406
2411
  queryFn: async () => {
2407
- return unwrapCapxulResult(await requireBootstrappedClient(client, CAPXUL_OPERATIONS.org.getLifecycle).org(orgId).getLifecycle());
2412
+ return unwrapCapxulResult(await requireBootstrappedClient(client, CAPXUL_OPERATIONS.organization.setup).org(orgId).setup.get());
2408
2413
  },
2409
2414
  enabled: wantsLifecycle && client !== null
2410
2415
  });
2411
- const lifecycleError = wantsLifecycle ? lifecycle.error : null;
2412
- const lifecycleSettled = !wantsLifecycle || lifecycleError !== null || isLifecycleSettled(lifecycle.data);
2416
+ const meKey = capxulKeys.orgMe(orgId, actingAccount(identity));
2417
+ useEffect(() => {
2418
+ if (!wantsLifecycle || client === null || orgId === void 0) return;
2419
+ const controller = new AbortController();
2420
+ let closed = false;
2421
+ let stop = null;
2422
+ setObservation({
2423
+ scopeKey,
2424
+ status: "loading"
2425
+ });
2426
+ client.org(orgId).setup.subscribe((snapshot) => {
2427
+ if (closed) return;
2428
+ if (snapshot.status === "value") {
2429
+ setObservation({
2430
+ scopeKey,
2431
+ status: "value",
2432
+ value: snapshot.value
2433
+ });
2434
+ queryClient.setQueryData(setupKey, snapshot.value);
2435
+ } else if (snapshot.status === "error") setObservation({
2436
+ scopeKey,
2437
+ status: "error",
2438
+ error: snapshot.error
2439
+ });
2440
+ }, { signal: controller.signal }).then((result) => {
2441
+ if (!result.ok) {
2442
+ if (!closed) setObservation({
2443
+ scopeKey,
2444
+ status: "error",
2445
+ error: result.error
2446
+ });
2447
+ return;
2448
+ }
2449
+ stop = result.value;
2450
+ if (closed) stop();
2451
+ }).catch(() => {
2452
+ if (!closed) setObservation({
2453
+ scopeKey,
2454
+ status: "error",
2455
+ error: new CapxulError("UNKNOWN", "Organization setup subscription failed")
2456
+ });
2457
+ });
2458
+ return () => {
2459
+ closed = true;
2460
+ controller.abort();
2461
+ stop?.();
2462
+ };
2463
+ }, [
2464
+ client,
2465
+ orgId,
2466
+ wantsLifecycle,
2467
+ queryClient,
2468
+ scopeKey,
2469
+ subscriptionRetry
2470
+ ]);
2471
+ const currentSetup = observation.scopeKey === scopeKey && observation.status === "value" ? observation.value : void 0;
2472
+ const wantsAuthority = wantsLifecycle && currentSetup?.status === "ready";
2473
+ const authority = useQuery({
2474
+ queryKey: meKey,
2475
+ queryFn: async () => {
2476
+ return unwrapCapxulResult(await requireBootstrappedClient(client, CAPXUL_OPERATIONS.org.me).org(orgId).me());
2477
+ },
2478
+ enabled: wantsAuthority && client !== null
2479
+ });
2480
+ const [checkedAuthority, setCheckedAuthority] = useState(null);
2481
+ useEffect(() => {
2482
+ if (!wantsAuthority || currentSetup === void 0) return;
2483
+ let active = true;
2484
+ authority.refetch().then((result) => {
2485
+ if (active && result.isSuccess) setCheckedAuthority({
2486
+ scopeKey,
2487
+ setup: currentSetup
2488
+ });
2489
+ });
2490
+ return () => {
2491
+ active = false;
2492
+ };
2493
+ }, [
2494
+ scopeKey,
2495
+ currentSetup,
2496
+ wantsAuthority,
2497
+ authority.refetch
2498
+ ]);
2499
+ const authorityCurrent = checkedAuthority?.scopeKey === scopeKey && checkedAuthority.setup === currentSetup && authority.data !== void 0;
2500
+ const observationError = observation.scopeKey === scopeKey && observation.status === "error" ? observation.error : void 0;
2501
+ const lifecycleError = wantsLifecycle ? observationError ?? lifecycle.error : null;
2502
+ const authorityError = wantsAuthority ? authority.error : null;
2503
+ const lifecycleSettled = !wantsLifecycle || lifecycleError !== null || authorityError !== null || isLifecycleSettled(currentSetup) && (!wantsAuthority || authorityCurrent);
2413
2504
  const retry = useCallback(() => {
2414
2505
  if (sessionError !== null && client !== null) client.auth.getSession();
2415
2506
  if (membershipError !== null) memberships.refetch();
2507
+ if (observationError !== void 0) setSubscriptionRetry((attempt) => attempt + 1);
2416
2508
  if (lifecycleError !== null) lifecycle.refetch();
2509
+ if (authorityError !== null) authority.refetch();
2417
2510
  }, [
2418
2511
  client,
2419
2512
  sessionError,
2420
2513
  membershipError,
2514
+ observationError,
2421
2515
  lifecycleError,
2422
2516
  memberships.refetch,
2423
- lifecycle.refetch
2517
+ lifecycle.refetch,
2518
+ authorityError,
2519
+ authority.refetch
2424
2520
  ]);
2425
- const error = sessionError ?? membershipError ?? lifecycleError;
2426
- const probe = probeOf(sessionError, membershipError, lifecycleError);
2521
+ const error = sessionError ?? membershipError ?? lifecycleError ?? authorityError;
2522
+ const probe = probeOf(sessionError, membershipError, lifecycleError, authorityError);
2427
2523
  const phase = decidePhase({
2428
2524
  error,
2429
2525
  settled: client !== null && identity !== null && !isRestoring$1(identity) && membershipsSettled && lifecycleSettled,
2430
- granted: scope.kind === "personal" ? claimed : claimed && orgId !== void 0 && hasMembership && isExactOrgReady(lifecycle.data, orgId),
2526
+ granted: scope.kind === "personal" ? claimed : claimed && orgId !== void 0 && hasMembership && isExactOrgReady(currentSetup, orgId) && authorityCurrent,
2431
2527
  orgLaneSettled: identity !== null && isOrgLaneSettled(identity),
2432
2528
  destination
2433
2529
  });
@@ -2867,28 +2963,33 @@ function decodeDraft(value, scope) {
2867
2963
  const to = record(item.to);
2868
2964
  const amount = record(item.amount);
2869
2965
  const partyId = toPartyId(identifier(item.partyId));
2870
- if (to.kind !== "party" || to.partyId !== partyId || item.gross !== item.net || typeof amount.asset !== "string" || typeof amount.value !== "string" || typeof item.currency !== "string" || typeof item.decimals !== "number" || !Number.isInteger(item.decimals) || !Array.isArray(item.adjustments) || item.adjustments.length !== 0) throw Errors.invalidInput("payrollDraft", "Invalid draft recipient");
2966
+ if (to.kind !== "party" || to.partyId !== partyId || item.gross !== item.net || typeof amount.asset !== "string" || typeof amount.value !== "string" || "currency" in item || "decimals" in item || !Array.isArray(item.adjustments) || item.adjustments.length !== 0) throw Errors.invalidInput("payrollDraft", "Invalid draft recipient");
2871
2967
  const net = minorAmount(item.net);
2968
+ const selectedAsset = requireAsset(identifier(amount.asset));
2969
+ const selectedAmount = {
2970
+ asset: selectedAsset.assetId,
2971
+ value: amount.value
2972
+ };
2973
+ try {
2974
+ if (toRaw(selectedAmount, selectedAsset).toString() !== net) throw Errors.invalidInput("payrollDraft", "Draft payout units differ from net");
2975
+ } catch {
2976
+ throw Errors.invalidInput("payrollDraft", "Invalid draft payout amount");
2977
+ }
2872
2978
  return {
2873
2979
  to: {
2874
2980
  kind: "party",
2875
2981
  partyId
2876
2982
  },
2877
2983
  partyId,
2878
- amount: {
2879
- asset: requireAsset(amount.asset).assetId,
2880
- value: amount.value
2881
- },
2984
+ amount: selectedAmount,
2882
2985
  gross: net,
2883
2986
  net,
2884
- currency: item.currency,
2885
- decimals: item.decimals,
2886
2987
  adjustments: []
2887
2988
  };
2888
2989
  });
2889
2990
  if (new Set(items.map((item) => item.partyId)).size !== items.length) throw Errors.invalidInput("payrollDraft", "Duplicate draft recipient");
2890
2991
  const exactAsset = requireAsset(identifier(asset.id));
2891
- if (items.some((item) => item.amount.asset !== exactAsset.assetId)) throw Errors.invalidInput("payrollDraft", "Draft item asset does not match the run asset");
2992
+ if (input.asset !== exactAsset.assetId || items.some((item) => item.amount.asset !== exactAsset.assetId)) throw Errors.invalidInput("payrollDraft", "Draft item asset does not match the run asset");
2892
2993
  return {
2893
2994
  version: 1,
2894
2995
  scope,
@@ -3133,6 +3234,7 @@ function freshRunItems(input) {
3133
3234
  const amount = input.amounts[index];
3134
3235
  const terms = input.compensationTerms.get(recipientId);
3135
3236
  if (to === void 0 || amount === void 0 || terms === void 0) throw Errors.unknown();
3237
+ const rawAmount = toRaw(amount, requireAsset(amount.asset)).toString();
3136
3238
  items.push({
3137
3239
  to: {
3138
3240
  kind: "party",
@@ -3140,10 +3242,8 @@ function freshRunItems(input) {
3140
3242
  },
3141
3243
  partyId: recipientId,
3142
3244
  amount,
3143
- gross: terms.rate,
3144
- net: terms.rate,
3145
- currency: terms.currency,
3146
- decimals: terms.decimals,
3245
+ gross: rawAmount,
3246
+ net: rawAmount,
3147
3247
  adjustments: []
3148
3248
  });
3149
3249
  }
@@ -1,4 +1,4 @@
1
- import { a as AuthenticationSlots, d as OnboardingControllerProps } from "../controllers-BWmKRFy6.mjs";
1
+ import { a as AuthenticationSlots, d as OnboardingControllerProps } from "../controllers-BMAU2kKH.mjs";
2
2
  import * as React from "react";
3
3
  import { ReactNode } from "react";
4
4
  import { CapxulTestClient, CapxulTestClock, CapxulTestObservation, CreateCapxulTestClientOptions, SeedTestIdentityInput, createCapxulTestClient } from "@capxul/sdk/testing";
@@ -1,4 +1,4 @@
1
- import { n as CapxulOnboardingController, r as CapxulProvider, t as CapxulAuthenticationController } from "../controllers-L1UqwWS1.mjs";
1
+ import { n as CapxulOnboardingController, r as CapxulProvider, t as CapxulAuthenticationController } from "../controllers-BV3e8WD3.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": "4.20.0-beta.10",
3
+ "version": "4.20.0-beta.12",
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": "4.20.0-beta.10"
29
+ "@capxul/sdk": "4.20.0-beta.12"
30
30
  },
31
31
  "devDependencies": {
32
32
  "@tanstack/react-query": "^5.66.9",
@@ -43,7 +43,7 @@
43
43
  "vite": "npm:@voidzero-dev/vite-plus-core@0.3.0",
44
44
  "vite-plus": "0.3.0",
45
45
  "vitest": "4.1.11",
46
- "@capxul/config": "0.4.0-beta.0",
46
+ "@capxul/config": "0.4.0-beta.1",
47
47
  "@capxul/errors": "0.3.0",
48
48
  "@capxul/types": "0.3.0",
49
49
  "@capxul/typescript-config": "0.0.0"