@capxul/sdk-react 4.20.0-beta.5 → 4.20.0-beta.7

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
@@ -56,6 +56,8 @@ export function Providers({ children }: { children: React.ReactNode }) {
56
56
  engine hook stays module-scoped. Both actor paths ship: a personal actor omits
57
57
  `.Source` and spends the personal Account; an organization actor holds the
58
58
  Budget the payment is drawn from and spends the treasury Account.
59
+ The submitted amount uses the selected registry AssetId and precision. This
60
+ includes 18-decimal WETH.
59
61
  - `CapxulContacts` is the compound address-book component. One root owns the
60
62
  book behaviour and draws nothing. The `.Summary`, `.List`, and `.Add` region
61
63
  parts each hand the screen one finished slice, and a `.List` row carries its
@@ -75,6 +77,9 @@ export function Providers({ children }: { children: React.ReactNode }) {
75
77
  and an amount the caller passed in `prefill` wins over it. Both engine hooks
76
78
  stay module-scoped.
77
79
 
80
+ Activity groups use AssetId. The SDK resolves symbols and precision only for
81
+ display and CSV output. Assets with the same peg remain separate.
82
+
78
83
  Payroll confirmation restores a saved run before enabling submission. An
79
84
  uncertain run keeps its period, amounts, recipients, and SDK request key across
80
85
  close/reopen and reload. Its fields remain fixed until Core confirms success or terminal closure.
@@ -98,6 +103,16 @@ The testing harness uses the real public provider and controllers and imports
98
103
  its client factory only through `@capxul/sdk/testing`; it owns no renderer or
99
104
  parallel identity state.
100
105
 
106
+ ## Activity documents
107
+
108
+ `CapxulActivity.Detail` exposes `documents`, `selectedDocument`, and
109
+ `selectDocument({ documentHash, contentHash })`. Selection defaults to the
110
+ instruction, then the Movement Memo. `renderDocument()` returns safe semantic
111
+ HTML. `downloadDocument()` returns exact bytes for a deliberate download.
112
+ The host owns the panel and download action. Do not log these results.
113
+ Account or Organization changes clear selection and cancel pending reads.
114
+ These methods replace the receipt-only selector and action.
115
+
101
116
  ## Documentation
102
117
 
103
118
  - [Maintainer context](./CONTEXT.md)
package/dist/index.d.mts CHANGED
@@ -1,7 +1,7 @@
1
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";
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, ActorReference, AddressBookEntry, BudgetId, CapxulClient, CapxulError, CapxulResult, CapxulSigner, CreateOrgInput, CurrentHoldings, HostObservability, IdentityDestination, IdentityState, InviteMemberInput, MemberView, Money, MoneyParseErrorReason, OrgId, OrgView, OrganizationPaymentBatchInput, OrganizationPaymentInput, PartyId, Payment, PaymentDirection, 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, 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";
5
5
  //#region src/provider.d.ts
6
6
  type CapxulProviderSharedProps = {
7
7
  /** Bring your own QueryClient; otherwise the provider creates one. */
@@ -82,7 +82,7 @@ declare function useCapxulAccountBalance(options?: UseCapxulAccountBalanceOption
82
82
  //#region src/hooks/use-capxul-account-fund.d.ts
83
83
  type UseCapxulAccountFundReturn = UseMutationResult<{
84
84
  readonly txHash: string;
85
- }, CapxulError, Money>;
85
+ }, CapxulError, AssetAmount>;
86
86
  declare function useCapxulAccountFund(): UseCapxulAccountFundReturn;
87
87
  //#endregion
88
88
  //#region src/hooks/use-capxul-money.d.ts
@@ -148,7 +148,7 @@ declare function useCapxulHoldings(params?: UseCapxulHoldingsParams): UseQueryRe
148
148
  * List the Organizations that the current user can access. Binds directly to
149
149
  * the locked `capxul.orgs()` SDK method.
150
150
  */
151
- type UseCapxulOrgsReturn = UseQueryResult<readonly OrgView[], CapxulError>;
151
+ type UseCapxulOrgsReturn = UseQueryResult<readonly OrganizationAccess[], CapxulError>;
152
152
  type UseCapxulOrgsOptions = {
153
153
  /**
154
154
  * Gate the query on auth readiness. `client.orgs()` is a session-scoped
@@ -204,24 +204,6 @@ declare function useCapxulOrgTreasury(orgId: OrgId | undefined, options?: UseCap
204
204
  type UseCapxulCreateOrgReturn = UseMutationResult<OrgView, CapxulError, CreateOrgInput>;
205
205
  declare function useCapxulCreateOrg(): UseCapxulCreateOrgReturn;
206
206
  //#endregion
207
- //#region src/hooks/use-capxul-invite-member.d.ts
208
- /**
209
- * R06 (#1964): `options` is the browser attempt the caller captured before the
210
- * person pressed Send. It reaches the SDK operation unchanged, so the durable
211
- * `org_invite_sent` and the later `org_invite_progressed` carry that attempt's
212
- * correlation. Absent, the invitation keeps its own invocation context.
213
- */
214
- type UseCapxulInviteMemberInput = InviteMemberInput & {
215
- readonly options?: InvocationOptions;
216
- };
217
- /**
218
- * Invite a member to an Organization by email. The hook uses the closed-over `orgId`.
219
- * It binds directly to
220
- * `capxul.org(orgId).invite(input)`. On success, invalidates the member list.
221
- */
222
- type UseCapxulInviteMemberReturn = UseMutationResult<MemberView, CapxulError, UseCapxulInviteMemberInput>;
223
- declare function useCapxulInviteMember(orgId: OrgId | undefined): UseCapxulInviteMemberReturn;
224
- //#endregion
225
207
  //#region src/headless/journey/journey-state.d.ts
226
208
  type OnboardingIntent = "personal" | "organization";
227
209
  type OnboardingStep = "profile" | "organization" | "provisioning";
@@ -525,8 +507,8 @@ type ActivityRow = {
525
507
  */
526
508
  type SummaryWindow = "all" | "last30days" | ActivityRange;
527
509
  /**
528
- * Finished money strings for ONE currency bucket, so no consumer indexes an
529
- * array of per-currency totals. All four strings are `null` and `count` is `0`
510
+ * Finished valuation strings in the display currency. Unrated assets remain
511
+ * separate. All four strings are `null` and `count` is `0`
530
512
  * when the window holds no activity — we say there is nothing, the app says it
531
513
  * in its own words.
532
514
  */
@@ -537,6 +519,7 @@ interface ActivitySummarySlice {
537
519
  readonly moneyOut: string | null;
538
520
  readonly net: string | null;
539
521
  readonly pending: string | null;
522
+ readonly unrated: readonly ActivitySummaryTotal[];
540
523
  /** A count is not money, so it is not formatted. */
541
524
  readonly count: number;
542
525
  readonly isLoading: boolean;
@@ -600,9 +583,12 @@ interface ActivityLoadMoreSlice {
600
583
  interface ActivityDetailSlice {
601
584
  /** Current Payment state from the existing detail read; absent for Movements. */
602
585
  readonly payment?: Pick<Payment, "status" | "execution">;
603
- /** ONE finished document, never the array, so no consumer writes `documents[0]`. */
604
- readonly receiptDocument: PaymentDocumentRef | null;
605
- readonly openReceiptDocument: () => Promise<PaymentDocumentRender>;
586
+ readonly documents: readonly PaymentDocumentRef[];
587
+ readonly selectedDocument: PaymentDocumentRef | null;
588
+ readonly selectDocument: (ref: PaymentDocumentReadRef) => void;
589
+ readonly renderDocument: () => Promise<PaymentDocumentRender>;
590
+ /** Return exact bytes on a deliberate action. The host owns the download URL. */
591
+ readonly downloadDocument: () => Promise<PaymentDocumentDownload>;
606
592
  readonly isOpeningDocument: boolean;
607
593
  readonly memo: string;
608
594
  readonly setMemo: (text: string) => void;
@@ -708,7 +694,7 @@ type SentPayment = {
708
694
  /** The backend payment row. */
709
695
  readonly payment: Payment;
710
696
  /** What we parsed and sent — `.value` is the comma-stripped major-unit string. */
711
- readonly amount: Money;
697
+ readonly amount: AssetAmount;
712
698
  /** The trimmed text the person typed — the app's success toast interpolates it. */
713
699
  readonly recipient: string;
714
700
  };
@@ -741,7 +727,7 @@ interface SendMoneySourceSlice {
741
727
  readonly isPending: boolean;
742
728
  }
743
729
  interface SendMoneyAssetOption {
744
- readonly id: string;
730
+ readonly id: AssetId;
745
731
  readonly symbol: string;
746
732
  readonly availableDisplay: string;
747
733
  }
@@ -1010,7 +996,7 @@ interface PayrollRunAmountEntry {
1010
996
  /** True while the value is one we seeded and the employer has not edited. */
1011
997
  readonly prefilled: boolean;
1012
998
  /** The treasury asset's currency. Rows stay absent until this value exists. */
1013
- readonly currency: Money["currency"];
999
+ readonly currency: string;
1014
1000
  }
1015
1001
  interface PayrollRunAmountsSlice {
1016
1002
  readonly entries: readonly PayrollRunAmountEntry[];
@@ -1176,4 +1162,4 @@ declare const capxulKeys: {
1176
1162
  payment: (paymentId: string | undefined) => readonly ["capxul", "payments", string];
1177
1163
  };
1178
1164
  //#endregion
1179
- export { type AccountFailure, type AccountProgress, type ActionResult, type ActivityDetailSlice, type ActivityExportSlice, type ActivityFilterValues, type ActivityFiltersSlice, type ActivityLoadMoreSlice, type ActivityReceipt, type ActivityRow, type ActivityRowsSlice, type ActivitySearchSlice, type ActivitySummarySlice, type AuthenticatedState, type AuthenticationSlots, type CanReason, CapxulActivity, type CapxulActivityProps, type CapxulAuth, CapxulAuthenticationController, type CapxulBootstrapState, type CapxulBootstrapStatus, CapxulClientConnection, type CapxulClientConnectionProps, CapxulContacts, type CapxulContactsProps, CapxulDashboardAccess, type CapxulDashboardAccessProps, CapxulOnboardingController, CapxulOrgMember, type CapxulOrgMemberProps, CapxulPayroll, type CapxulPayrollProps, CapxulPayrollRun, type CapxulPayrollRunBlockedReason, type CapxulPayrollRunProps, CapxulProvider, type CapxulProviderProps, type CapxulSend, CapxulSendMoney, type CapxulSendMoneyBlockedReason, type CapxulSendMoneyProps, type CompleteSlice as ClientConnectionCompleteSlice, type ConsentSlice as ClientConnectionConsentSlice, type FailureSlice as ClientConnectionFailureSlice, type OtpSlice as ClientConnectionOtpSlice, type Contact, type ContactRelationship, type ContactRow, type ContactsActor, type ContactsAddBlockedReason, type ContactsAddError, type ContactsAddSlice, type ContactsListOptions, type ContactsListSlice, type ContactsRefusal, type ContactsSummarySlice, type ControllerAction, type CreateOrganizationFailure, type CreateOrganizationSubmission, type CsvExport, type DashboardAccessErrorSlice, type DashboardAccessPhase, type DashboardAccessProbe, type DashboardAccessRedirectSlice, type DashboardScope, type Destination, type FaultedState, type HandleAvailability, type ImageUploadInput, type ImageUploadTarget, type InvocationOptions, type NavigationAction, type OnboardingControllerProps, type OnboardingIntent, type OnboardingJourney, type OnboardingJourneyPosition, type OnboardingOrigin, type OnboardingStep, type OrgFailure, type OrgMemberAction, type OrgMemberCanSlice, type OrgMemberStanding, type OrgMemberStandingSlice, type OrgProgress, type OrganizationDetails, type OrganizationDraft, type OtpPendingState, type PayoutDraftChain, type PayoutDraftEntry, type PayrollGroupRow, type PayrollGroupsSlice, type PayrollRecipientOption, type PayrollRunActionsSlice, type PayrollRunAmountEntry, type PayrollRunAmountsSlice, type PayrollRunAssetSlice, type PayrollRunGroupOption, type PayrollRunPrefill, type PayrollRunRecipientsSlice, type PayrollRunStatus, type PayrollRunSummarySlice, type PayrollSummarySlice, type PendingAuthState, type ProfileDetails, type ProfileDraft, type ProfileSlotProps, type ReadyDestination, type RetryAction, type SendActor, type SendMoneyActionsSlice, type SendMoneyAmountSlice, type SendMoneyAssetError, type SendMoneyAssetOption, type SendMoneyAssetSlice, type SendMoneyRecipientSlice, type SendMoneyRecipientStatus, type SendMoneySourceError, type SendMoneySourceOption, type SendMoneySourceSlice, type SendResult, type SentPayment, type SentPayrollRun, type SignedOutState, type Slot, type SummaryWindow, type UseCapxulAccountBalanceOptions, type UseCapxulAccountBalanceReturn, type UseCapxulAccountFundReturn, type UseCapxulCreateCommitmentReturn, type UseCapxulCreateOrgReturn, type UseCapxulHandleAvailabilityReturn, type UseCapxulImageUploadReturn, type UseCapxulInviteMemberInput, type UseCapxulInviteMemberReturn, type UseCapxulOrgMembersOptions, type UseCapxulOrgMembersReturn, type UseCapxulOrgRolesOptions, type UseCapxulOrgRolesReturn, type UseCapxulOrgTreasuryOptions, type UseCapxulOrgTreasuryReturn, type UseCapxulOrgsReturn, type UseCapxulPayReturn, type UseCapxulPaymentReturn, type UseCapxulPaymentsReturn, type UseCapxulProfileReturn, type UseCapxulRedirectPaymentReturn, type UseCapxulUploadImageReturn, acknowledgeOnboardingDestination, activeOnboardingRecovery, capxulKeys, clearOnboardingJourney, currentOnboardingJourneyId, entered, invalidateOnboardingJourneyObservation, isClaimed, isRestoring, loadOnboardingJourney, onboardingJourneyPosition, saveOnboardingJourney, startOnboardingJourney, useCapxul, useCapxulAccountBalance, useCapxulAccountFund, useCapxulActivity, useCapxulActivityDetail, useCapxulAnnotateMovement, useCapxulAuth, useCapxulCancelPayment, useCapxulClaimPayment, useCapxulClientOrNull, useCapxulCreateCommitment, useCapxulCreateOrg, useCapxulDestination, useCapxulHandleAvailability, useCapxulHoldings, useCapxulIdentity, useCapxulImageUpload, useCapxulInviteMember, useCapxulOrgMembers, useCapxulOrgRoles, useCapxulOrgTreasury, useCapxulOrganizationPay, useCapxulOrganizationPayBatch, useCapxulOrgs, useCapxulPay, useCapxulPayment, useCapxulPayments, useCapxulPermission, useCapxulPermissionAssign, useCapxulPermissionChange, useCapxulPermissionCreate, useCapxulPermissionReplace, useCapxulPermissionRevoke, useCapxulPermissions, useCapxulProfile, useCapxulRedirectPayment, useCapxulSend, useCapxulTransitions, useCapxulUploadImage };
1165
+ export { type AccountFailure, type AccountProgress, type ActionResult, type ActivityDetailSlice, type ActivityExportSlice, type ActivityFilterValues, type ActivityFiltersSlice, type ActivityLoadMoreSlice, type ActivityReceipt, type ActivityRow, type ActivityRowsSlice, type ActivitySearchSlice, type ActivitySummarySlice, type AuthenticatedState, type AuthenticationSlots, type CanReason, CapxulActivity, type CapxulActivityProps, type CapxulAuth, CapxulAuthenticationController, type CapxulBootstrapState, type CapxulBootstrapStatus, CapxulClientConnection, type CapxulClientConnectionProps, CapxulContacts, type CapxulContactsProps, CapxulDashboardAccess, type CapxulDashboardAccessProps, CapxulOnboardingController, CapxulOrgMember, type CapxulOrgMemberProps, CapxulPayroll, type CapxulPayrollProps, CapxulPayrollRun, type CapxulPayrollRunBlockedReason, type CapxulPayrollRunProps, CapxulProvider, type CapxulProviderProps, type CapxulSend, CapxulSendMoney, type CapxulSendMoneyBlockedReason, type CapxulSendMoneyProps, type CompleteSlice as ClientConnectionCompleteSlice, type ConsentSlice as ClientConnectionConsentSlice, type FailureSlice as ClientConnectionFailureSlice, type OtpSlice as ClientConnectionOtpSlice, type Contact, type ContactRelationship, type ContactRow, type ContactsActor, type ContactsAddBlockedReason, type ContactsAddError, type ContactsAddSlice, type ContactsListOptions, type ContactsListSlice, type ContactsRefusal, type ContactsSummarySlice, type ControllerAction, type CreateOrganizationFailure, type CreateOrganizationSubmission, type CsvExport, type DashboardAccessErrorSlice, type DashboardAccessPhase, type DashboardAccessProbe, type DashboardAccessRedirectSlice, type DashboardScope, type Destination, type FaultedState, type HandleAvailability, type ImageUploadInput, type ImageUploadTarget, type InvocationOptions, type NavigationAction, type OnboardingControllerProps, type OnboardingIntent, type OnboardingJourney, type OnboardingJourneyPosition, type OnboardingOrigin, type OnboardingStep, type OrgFailure, type OrgMemberAction, type OrgMemberCanSlice, type OrgMemberStanding, type OrgMemberStandingSlice, type OrgProgress, type OrganizationDetails, type OrganizationDraft, type OtpPendingState, type PayoutDraftChain, type PayoutDraftEntry, type PayrollGroupRow, type PayrollGroupsSlice, type PayrollRecipientOption, type PayrollRunActionsSlice, type PayrollRunAmountEntry, type PayrollRunAmountsSlice, type PayrollRunAssetSlice, type PayrollRunGroupOption, type PayrollRunPrefill, type PayrollRunRecipientsSlice, type PayrollRunStatus, type PayrollRunSummarySlice, type PayrollSummarySlice, type PendingAuthState, type ProfileDetails, type ProfileDraft, type ProfileSlotProps, type ReadyDestination, type RetryAction, type SendActor, type SendMoneyActionsSlice, type SendMoneyAmountSlice, type SendMoneyAssetError, type SendMoneyAssetOption, type SendMoneyAssetSlice, type SendMoneyRecipientSlice, type SendMoneyRecipientStatus, type SendMoneySourceError, type SendMoneySourceOption, type SendMoneySourceSlice, type SendResult, type SentPayment, type SentPayrollRun, type SignedOutState, type Slot, type SummaryWindow, type UseCapxulAccountBalanceOptions, type UseCapxulAccountBalanceReturn, type UseCapxulAccountFundReturn, type UseCapxulCreateCommitmentReturn, type UseCapxulCreateOrgReturn, type UseCapxulHandleAvailabilityReturn, type UseCapxulImageUploadReturn, type UseCapxulOrgMembersOptions, type UseCapxulOrgMembersReturn, type UseCapxulOrgRolesOptions, type UseCapxulOrgRolesReturn, type UseCapxulOrgTreasuryOptions, type UseCapxulOrgTreasuryReturn, type UseCapxulOrgsReturn, type UseCapxulPayReturn, type UseCapxulPaymentReturn, type UseCapxulPaymentsReturn, type UseCapxulProfileReturn, type UseCapxulRedirectPaymentReturn, type UseCapxulUploadImageReturn, acknowledgeOnboardingDestination, activeOnboardingRecovery, capxulKeys, clearOnboardingJourney, currentOnboardingJourneyId, entered, invalidateOnboardingJourneyObservation, isClaimed, isRestoring, loadOnboardingJourney, onboardingJourneyPosition, saveOnboardingJourney, startOnboardingJourney, useCapxul, useCapxulAccountBalance, useCapxulAccountFund, useCapxulActivity, useCapxulActivityDetail, useCapxulAnnotateMovement, useCapxulAuth, useCapxulCancelPayment, useCapxulClaimPayment, useCapxulClientOrNull, useCapxulCreateCommitment, useCapxulCreateOrg, useCapxulDestination, useCapxulHandleAvailability, useCapxulHoldings, useCapxulIdentity, useCapxulImageUpload, useCapxulOrgMembers, useCapxulOrgRoles, useCapxulOrgTreasury, useCapxulOrganizationPay, useCapxulOrganizationPayBatch, useCapxulOrgs, useCapxulPay, useCapxulPayment, useCapxulPayments, useCapxulPermission, useCapxulPermissionAssign, useCapxulPermissionChange, useCapxulPermissionCreate, useCapxulPermissionReplace, useCapxulPermissionRevoke, useCapxulPermissions, useCapxulProfile, useCapxulRedirectPayment, useCapxulSend, useCapxulTransitions, useCapxulUploadImage };
package/dist/index.mjs CHANGED
@@ -2,7 +2,7 @@
2
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";
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
- import { CAPXUL_OPERATIONS, CapxulError, EVM_ADDRESS_RE, Errors, HANDLE_RE, PAYMENT_DIRECTIONS, PAYMENT_STATUSES, fingerprintPaymentIntent, formatAssetAmount, formatMoney, isCapxulError, isClaimed, isClaimed as isClaimed$1, isMoneyParseError, isRestoring, isRestoring as isRestoring$1, parseMoney, paymentPhase, positionPeg, resolveIdentityDestination, toEvmAddress, toPartyId } from "@capxul/sdk";
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";
6
6
  import { Fragment, jsx } from "react/jsx-runtime";
7
7
  //#region src/internal/require-bootstrapped-client.ts
8
8
  /**
@@ -546,22 +546,6 @@ function useCapxulCreateOrg() {
546
546
  });
547
547
  }
548
548
  //#endregion
549
- //#region src/hooks/use-capxul-invite-member.ts
550
- function useCapxulInviteMember(orgId) {
551
- const client = useCapxulClientOrNull();
552
- const queryClient = useQueryClient();
553
- return useMutation({
554
- mutationFn: async ({ options, ...input }) => {
555
- if (orgId === void 0) throw Errors.invalidInput("orgId", "required for useCapxulInviteMember");
556
- return unwrapCapxulResult(await requireBootstrappedClient(client, CAPXUL_OPERATIONS.org.invite).org(orgId).invite(input, toInvocationControls(options)));
557
- },
558
- onSuccess: async () => {
559
- if (orgId === void 0) return;
560
- await queryClient.invalidateQueries({ queryKey: capxulKeys.orgMembers(orgId) });
561
- }
562
- });
563
- }
564
- //#endregion
565
549
  //#region src/headless/journey/journey-state.ts
566
550
  const JOURNEY_KEY = "capxul.onboarding.journey.v1";
567
551
  const VALIDATED_OWNER_KEY = "capxul.onboarding.validated-owner.v1";
@@ -1141,15 +1125,8 @@ function useActivityEnabled() {
1141
1125
  }
1142
1126
  //#endregion
1143
1127
  //#region src/headless/activity/detail.ts
1144
- /**
1145
- * ONE finished document, never the array, so no consumer writes `documents[0]`:
1146
- * the payslip, else the receipt, else nothing. Those two kinds are the ones the
1147
- * Download receipt button is for. A Movement detail carries no documents.
1148
- */
1149
- function pickReceiptDocument(detail) {
1150
- if (detail === null || detail.kind !== "payment") return null;
1151
- const documents = detail.payment.documents;
1152
- return documents.find((document) => document.kind === "payslip") ?? documents.find((document) => document.kind === "receipt") ?? null;
1128
+ function detailDocuments(detail) {
1129
+ return detail === null ? [] : detail.kind === "payment" ? detail.payment.documents : detail.documents;
1153
1130
  }
1154
1131
  /**
1155
1132
  * The root actor and the whole reference are included. A payment reference can
@@ -1184,27 +1161,76 @@ function useActivityDetail(reference, actor) {
1184
1161
  const enabled = useActivityEnabled() && reference !== void 0;
1185
1162
  const detail = useQuery({
1186
1163
  queryKey: accountKey(capxulKeys.activityDetail(reference, actor), identity),
1187
- queryFn: async () => {
1164
+ queryFn: async ({ signal }) => {
1188
1165
  if (reference === void 0) throw Errors.invalidInput("reference", "required for activity.get");
1189
- return unwrapCapxulResult(await requireBootstrappedClient(client, CAPXUL_OPERATIONS.activity.get).activity.get(reference, actor === void 0 ? void 0 : { actor }));
1166
+ return unwrapCapxulResult(await requireBootstrappedClient(client, CAPXUL_OPERATIONS.activity.get).activity.get(reference, {
1167
+ ...actor === void 0 ? {} : { actor },
1168
+ signal
1169
+ }));
1190
1170
  },
1191
1171
  enabled
1192
1172
  });
1193
- const receiptDocument = pickReceiptDocument(detail.data ?? null);
1173
+ const documents = detailDocuments(detail.data ?? null);
1174
+ const [selected, setSelected] = useState(null);
1175
+ const selectedDocument = (selected === null ? void 0 : documents.find((doc) => doc.documentHash === selected.documentHash && doc.contentHash === selected.contentHash)) ?? documents.find((doc) => doc.role === "instruction") ?? documents.find((doc) => doc.role === "movement") ?? documents[0] ?? null;
1176
+ const pending = useRef(/* @__PURE__ */ new Set());
1177
+ const mounted = useRef(true);
1178
+ useEffect(() => {
1179
+ mounted.current = true;
1180
+ const reads = pending.current;
1181
+ return () => {
1182
+ mounted.current = false;
1183
+ for (const read of reads) read.abort();
1184
+ reads.clear();
1185
+ };
1186
+ }, []);
1187
+ const selectDocument = useCallback((ref) => {
1188
+ if (!documents.some((doc) => doc.documentHash === ref.documentHash && doc.contentHash === ref.contentHash)) throw Errors.invalidInput("documentHash", "document not found");
1189
+ for (const read of pending.current) read.abort();
1190
+ pending.current.clear();
1191
+ setIsOpeningDocument(false);
1192
+ setSelected({
1193
+ documentHash: ref.documentHash,
1194
+ contentHash: ref.contentHash
1195
+ });
1196
+ }, [documents]);
1194
1197
  const [isOpeningDocument, setIsOpeningDocument] = useState(false);
1195
- const openReceiptDocument = useCallback(async () => {
1196
- if (reference === void 0 || receiptDocument === null) throw Errors.invalidInput("receiptDocument", "no renderable document on this row");
1198
+ const readDocument = useCallback(async (download) => {
1199
+ const operation = download ? CAPXUL_OPERATIONS.paymentDocuments.get : CAPXUL_OPERATIONS.paymentDocuments.render;
1200
+ if (!mounted.current) throw Errors.cancelled({ operation });
1201
+ if (reference === void 0 || selectedDocument === null) throw Errors.invalidInput("documentHash", "no document on this row");
1202
+ const controller = new AbortController();
1203
+ pending.current.add(controller);
1197
1204
  setIsOpeningDocument(true);
1198
1205
  try {
1199
- return unwrapCapxulResult(await requireBootstrappedClient(client, CAPXUL_OPERATIONS.paymentDocuments.render).paymentDocuments.render(receiptDocument.documentHash));
1206
+ const bootstrapped = requireBootstrappedClient(client, download ? CAPXUL_OPERATIONS.paymentDocuments.get : CAPXUL_OPERATIONS.paymentDocuments.render);
1207
+ const pair = {
1208
+ documentHash: selectedDocument.documentHash,
1209
+ contentHash: selectedDocument.contentHash
1210
+ };
1211
+ const options = { signal: controller.signal };
1212
+ const result = download ? await bootstrapped.paymentDocuments.get(pair, options) : await bootstrapped.paymentDocuments.render(pair, options);
1213
+ if (controller.signal.aborted) throw Errors.cancelled({ operation });
1214
+ return unwrapCapxulResult(result);
1200
1215
  } finally {
1201
- setIsOpeningDocument(false);
1216
+ pending.current.delete(controller);
1217
+ if (!controller.signal.aborted) setIsOpeningDocument(pending.current.size > 0);
1202
1218
  }
1203
1219
  }, [
1204
1220
  client,
1205
- receiptDocument,
1221
+ selectedDocument,
1206
1222
  reference
1207
1223
  ]);
1224
+ const renderDocument = useCallback(async () => {
1225
+ const result = await readDocument(false);
1226
+ if (!("html" in result)) throw Errors.invalidInput("documentHash", "invalid document render");
1227
+ return result;
1228
+ }, [readDocument]);
1229
+ const downloadDocument = useCallback(async () => {
1230
+ const result = await readDocument(true);
1231
+ if (!("bytesBase64" in result)) throw Errors.invalidInput("documentHash", "invalid document download");
1232
+ return result;
1233
+ }, [readDocument]);
1208
1234
  const [typed, setTyped] = useState(null);
1209
1235
  const annotate = useCapxulAnnotateMovement();
1210
1236
  const saveMemo = useCallback(async () => {
@@ -1233,8 +1259,11 @@ function useActivityDetail(reference, actor) {
1233
1259
  status: detail.data.payment.status,
1234
1260
  ...detail.data.payment.execution === void 0 ? {} : { execution: detail.data.payment.execution }
1235
1261
  } } : {},
1236
- receiptDocument,
1237
- openReceiptDocument,
1262
+ documents,
1263
+ selectedDocument,
1264
+ selectDocument,
1265
+ renderDocument,
1266
+ downloadDocument,
1238
1267
  isOpeningDocument,
1239
1268
  memo: typed ?? detail.data?.annotation?.memo ?? "",
1240
1269
  setMemo: setTyped,
@@ -1247,6 +1276,7 @@ function useActivityDetail(reference, actor) {
1247
1276
  //#endregion
1248
1277
  //#region src/headless/activity/summary.ts
1249
1278
  const DAY_MS = 864e5;
1279
+ const display = (money) => money === null ? null : formatMoney(money);
1250
1280
  /**
1251
1281
  * `"last30days"` is the last 30 whole UTC days including today. Quantizing to
1252
1282
  * the UTC day keeps the cache key stable for a whole day instead of changing on
@@ -1257,52 +1287,6 @@ function resolveWindow(window, now) {
1257
1287
  if (window === "last30days") return { from: Math.floor(now / DAY_MS) * DAY_MS - 29 * DAY_MS };
1258
1288
  return window;
1259
1289
  }
1260
- /** An integer raw-unit string lifted into a major-unit decimal string. */
1261
- function majorUnits(raw, decimals) {
1262
- const negative = raw.startsWith("-");
1263
- const digits = negative ? raw.slice(1) : raw;
1264
- const sign = negative ? "-" : "";
1265
- if (decimals === 0) return `${sign}${digits}`;
1266
- const padded = digits.padStart(decimals + 1, "0");
1267
- const cut = padded.length - decimals;
1268
- return `${sign}${padded.slice(0, cut)}.${padded.slice(cut)}`;
1269
- }
1270
- function absoluteNet(total) {
1271
- const net = BigInt(total.net);
1272
- return net < 0n ? -net : net;
1273
- }
1274
- /**
1275
- * Buckets carry different `decimals`, so their raw integers are not comparable.
1276
- * Both are scaled to the wider precision before the magnitudes are ordered.
1277
- */
1278
- function compareNet(left, right) {
1279
- const width = Math.max(left.decimals, right.decimals);
1280
- const scaledLeft = absoluteNet(left) * 10n ** BigInt(width - left.decimals);
1281
- const scaledRight = absoluteNet(right) * 10n ** BigInt(width - right.decimals);
1282
- if (scaledLeft === scaledRight) return 0;
1283
- return scaledLeft < scaledRight ? -1 : 1;
1284
- }
1285
- /**
1286
- * Which currency `.Summary` shows: the bucket matching `primaryCurrency` when
1287
- * one is given, else the bucket with the largest absolute `net`, breaking a tie
1288
- * on currency code ascending so the choice is stable across reloads.
1289
- */
1290
- function pickBucket(totals, primaryCurrency) {
1291
- if (primaryCurrency !== null) {
1292
- const matched = totals.find((total) => total.currency === primaryCurrency);
1293
- if (matched !== void 0) return matched;
1294
- }
1295
- let best = null;
1296
- for (const total of totals) {
1297
- if (best === null) {
1298
- best = total;
1299
- continue;
1300
- }
1301
- const order = compareNet(total, best);
1302
- if (order > 0 || order === 0 && total.currency < best.currency) best = total;
1303
- }
1304
- return best;
1305
- }
1306
1290
  /**
1307
1291
  * The current UTC day number, which advances ON ITS OWN at UTC midnight.
1308
1292
  *
@@ -1346,42 +1330,36 @@ function useActivitySummary(actor, window) {
1346
1330
  const params = {};
1347
1331
  if (actor !== void 0) params.actor = actor;
1348
1332
  if (range !== void 0) params.window = range;
1349
- return unwrapCapxulResult(await requireBootstrappedClient(client, CAPXUL_OPERATIONS.activity.summary).activity.summary(params));
1350
- },
1351
- enabled
1352
- });
1353
- const primary = useQuery({
1354
- queryKey: accountKey([...capxulKeys.holdings(actor), "primary"], identity),
1355
- queryFn: async () => {
1356
- return unwrapCapxulResult(await requireBootstrappedClient(client, CAPXUL_OPERATIONS.holdings.primary).holdings.primary(actor === void 0 ? void 0 : { actor }));
1333
+ const result = unwrapCapxulResult(await requireBootstrappedClient(client, CAPXUL_OPERATIONS.activity.summary).activity.summary(params));
1334
+ return {
1335
+ ...result,
1336
+ valuation: valueActivitySummary(result.totals)
1337
+ };
1357
1338
  },
1358
1339
  enabled
1359
1340
  });
1360
1341
  const totals = summary.data?.totals;
1361
- const isLoading = summary.isLoading || primary.isLoading;
1362
- const bucket = useMemo(() => totals === void 0 || isLoading ? null : pickBucket(totals, null), [isLoading, totals]);
1363
- if (bucket === null) return {
1342
+ const isLoading = summary.isLoading;
1343
+ if (summary.data === void 0 || totals === void 0 || totals.length === 0 || isLoading) return {
1364
1344
  currency: null,
1365
1345
  moneyIn: null,
1366
1346
  moneyOut: null,
1367
1347
  net: null,
1368
1348
  pending: null,
1349
+ unrated: [],
1369
1350
  count: summary.data?.count ?? 0,
1370
1351
  isLoading,
1371
1352
  error: asCapxulError$1(summary.error),
1372
1353
  ...summary.data?.readWarning === void 0 ? {} : { readWarning: summary.data.readWarning }
1373
1354
  };
1374
- const display = (raw) => formatMoney({
1375
- currency: bucket.currency,
1376
- value: majorUnits(raw, bucket.decimals),
1377
- decimals: bucket.decimals
1378
- });
1355
+ const valuation = summary.data.valuation;
1379
1356
  return {
1380
- currency: bucket.currency,
1381
- moneyIn: display(bucket.in),
1382
- moneyOut: display(bucket.out),
1383
- net: display(bucket.net),
1384
- pending: display(bucket.pendingIn),
1357
+ currency: valuation.moneyIn?.currency ?? null,
1358
+ moneyIn: display(valuation.moneyIn),
1359
+ moneyOut: display(valuation.moneyOut),
1360
+ net: display(valuation.net),
1361
+ pending: display(valuation.pending),
1362
+ unrated: valuation.unrated,
1385
1363
  count: summary.data?.count ?? 0,
1386
1364
  isLoading,
1387
1365
  error: asCapxulError$1(summary.error),
@@ -1408,7 +1386,7 @@ const SIGN = Object.freeze({
1408
1386
  self: ""
1409
1387
  });
1410
1388
  function toActivityRow(item, actor) {
1411
- const amountDisplay = formatMoney(item.amount);
1389
+ const amountDisplay = formatAssetAmount(item.amount);
1412
1390
  const signedAmountDisplay = `${SIGN[item.direction]}${amountDisplay}`;
1413
1391
  if (item.kind === "movement") return {
1414
1392
  kind: "movement",
@@ -1420,7 +1398,7 @@ function toActivityRow(item, actor) {
1420
1398
  counterpartyLabel: item.counterpartyLabel,
1421
1399
  amountDisplay,
1422
1400
  signedAmountDisplay,
1423
- currency: item.amount.currency,
1401
+ currency: assetSymbolFor(item.amount.asset),
1424
1402
  createdAt: item.createdAt,
1425
1403
  classification: item.classification,
1426
1404
  phase: MOVEMENT_PHASE,
@@ -1442,7 +1420,7 @@ function toActivityRow(item, actor) {
1442
1420
  counterpartyLabel: item.counterpartyLabel,
1443
1421
  amountDisplay,
1444
1422
  signedAmountDisplay,
1445
- currency: item.amount.currency,
1423
+ currency: assetSymbolFor(item.amount.asset),
1446
1424
  createdAt: item.createdAt,
1447
1425
  ledgerKind: item.ledgerKind,
1448
1426
  paymentType: item.paymentType,
@@ -1523,7 +1501,7 @@ function buildCsv(items, actor, search) {
1523
1501
  row.counterpartyLabel,
1524
1502
  row.direction,
1525
1503
  item.amount.value,
1526
- item.amount.currency,
1504
+ assetSymbolFor(item.amount.asset),
1527
1505
  row.kind === "payment" ? row.execution?.notSent ? "not_sent" : row.execution?.status === "unresolved" ? "verifying" : row.status : "",
1528
1506
  row.reference.id,
1529
1507
  row.receipt?.transactionHash ?? ""
@@ -1873,18 +1851,19 @@ function useOrgMeReading(orgId) {
1873
1851
  //#endregion
1874
1852
  //#region src/internal/write-names-asset.ts
1875
1853
  /**
1876
- * Identity law 5 (`docs/campaigns/capabilities/treasury-cut/HANDOFF.md`): "A
1877
- * payment names an asset or inherits it from an authorized fact. Missing asset
1878
- * refuses."
1854
+ * Identity law: "A payment names an asset or inherits it from an authorized
1855
+ * fact. Missing asset refuses." The rule was recorded in the retired
1856
+ * treasury-cut handoff (removed from the current tree by the exact-asset
1857
+ * contraction; its record survives in Git history).
1879
1858
  *
1880
1859
  * A Capxul payment writer takes `PaymentMoney` — a peg currency, a value and
1881
1860
  * `decimals` — and settles one fixed legacy token. No `AssetId` crosses that
1882
1861
  * seam, so a payment composed from a named position cannot name the asset it
1883
1862
  * moves: a position selected as USDT authorizes a different token.
1884
1863
  *
1885
- * `false` until slice 02 (`02-named-pay`) makes the writer take the command's
1886
- * asset. The Send and Payroll composers read this to refuse a submission
1887
- * rather than authorize a token the person did not pick.
1864
+ * `false` until the writer takes the command's asset. The Send and Payroll
1865
+ * composers read this to refuse a submission rather than authorize a token the
1866
+ * person did not pick.
1888
1867
  */
1889
1868
  function writeNamesAsset() {
1890
1869
  return false;
@@ -2054,7 +2033,6 @@ function useSelectedOption(options) {
2054
2033
  function assetSelectionsFromAccount$1(account) {
2055
2034
  if (account === null) return [];
2056
2035
  return account.balances.map((position) => {
2057
- const peg = positionPeg(position.asset);
2058
2036
  return {
2059
2037
  option: {
2060
2038
  id: position.asset,
@@ -2064,10 +2042,9 @@ function assetSelectionsFromAccount$1(account) {
2064
2042
  value: position.available
2065
2043
  })
2066
2044
  },
2067
- money: peg === null ? null : {
2068
- currency: peg,
2069
- value: position.available,
2070
- decimals: position.decimals
2045
+ money: {
2046
+ asset: position.asset,
2047
+ value: position.available
2071
2048
  }
2072
2049
  };
2073
2050
  });
@@ -2089,7 +2066,7 @@ function useSendMoney(input) {
2089
2066
  const [selectedAssetOption, selectAsset] = useSelectedOption(assetOptions);
2090
2067
  const selectedAssetMoney = useMemo(() => assetSelections.find((selection) => selection.option.id === selectedAssetOption?.id)?.money ?? null, [assetSelections, selectedAssetOption]);
2091
2068
  const [amountText, setAmountText] = useState("");
2092
- const parsed = useMemo(() => selectedAssetMoney === null || amountText.trim() === "" ? null : parseMoney(amountText, selectedAssetMoney), [amountText, selectedAssetMoney]);
2069
+ const parsed = useMemo(() => selectedAssetMoney === null || amountText.trim() === "" ? null : parseAssetAmount(amountText, requireAsset(selectedAssetMoney.asset)), [amountText, selectedAssetMoney]);
2093
2070
  const parsedMoney = parsed !== null && !isMoneyParseError(parsed) ? parsed : null;
2094
2071
  const amountError = parsed !== null && isMoneyParseError(parsed) ? parsed.reason : null;
2095
2072
  const recipient = useRecipientResolution(client, JSON.stringify([
@@ -2538,11 +2515,6 @@ const CapxulDashboardAccess = Object.assign(Root$3, {
2538
2515
  });
2539
2516
  //#endregion
2540
2517
  //#region src/headless/payroll/money.ts
2541
- /** `{ value: "12.34", decimals: 6 }` → `12340000n`. */
2542
- function toMinorUnits(money) {
2543
- const [whole = "0", fraction = ""] = money.value.split(".");
2544
- return BigInt(`${whole}${fraction.padEnd(money.decimals, "0")}`);
2545
- }
2546
2518
  const TYPED_AMOUNT = /^(-?)(\d+)(?:\.(\d+))?$/;
2547
2519
  /**
2548
2520
  * Σ of the employer's TYPED amounts on a saved group — a DISPLAY total.
@@ -2575,6 +2547,17 @@ function fromMinorUnits(raw, asset) {
2575
2547
  decimals: asset.decimals
2576
2548
  };
2577
2549
  }
2550
+ /** Convert exact raw units to one asset quantity without a fiat denomination. */
2551
+ function assetFromMinorUnits(raw, asset) {
2552
+ const money = fromMinorUnits(raw, {
2553
+ currency: "USD",
2554
+ decimals: asset.decimals
2555
+ });
2556
+ return {
2557
+ asset: asset.asset,
2558
+ value: money.value
2559
+ };
2560
+ }
2578
2561
  //#endregion
2579
2562
  //#region src/headless/payroll/reads.ts
2580
2563
  function toRecipientOption(entry) {
@@ -2691,7 +2674,7 @@ function scopedClient(client, method, orgId) {
2691
2674
  //#endregion
2692
2675
  //#region src/headless/payroll/use-payroll.ts
2693
2676
  /** Money reads the same way everywhere on this screen: amount then code. */
2694
- const GRAMMAR$1 = { grammar: "code" };
2677
+ const GRAMMAR = { grammar: "code" };
2695
2678
  /**
2696
2679
  * One captured snapshot, or nothing — never a fabricated empty options object,
2697
2680
  * and never a DOM event a screen wired straight to `onClick`.
@@ -2707,7 +2690,7 @@ function toPayrollGroupRow(group) {
2707
2690
  name: group.name,
2708
2691
  tone: group.tone,
2709
2692
  memberCount: group.members.length,
2710
- totalDisplay: currency === void 0 ? "" : formatMoney(sumTypedAmounts(group.members.map((member) => member.amount), currency), GRAMMAR$1),
2693
+ totalDisplay: currency === void 0 ? "" : formatMoney(sumTypedAmounts(group.members.map((member) => member.amount), currency), GRAMMAR),
2711
2694
  members: group.members.map((member) => ({
2712
2695
  recipientId: member.partyId,
2713
2696
  amount: member.amount,
@@ -2797,8 +2780,8 @@ function usePayroll(orgId) {
2797
2780
  const started = orgId !== void 0;
2798
2781
  return {
2799
2782
  summary: {
2800
- walletBalanceDisplay: treasury.data === void 0 ? null : formatMoney(treasury.data.available, GRAMMAR$1),
2801
- paidThisMonthDisplay: runs.data === void 0 ? null : formatMoney(runs.data.settledThisMonth, GRAMMAR$1),
2783
+ walletBalanceDisplay: treasury.data?.available === void 0 || treasury.data.available === null ? null : formatMoney(treasury.data.available, GRAMMAR),
2784
+ paidThisMonthDisplay: runs.data === void 0 ? null : runs.data.settledThisMonth.map(formatAssetAmount).join(", "),
2802
2785
  isLoading: started && (treasury.data === void 0 && treasury.error === null || runs.data === void 0 && runs.error === null),
2803
2786
  error: treasury.error ?? runs.error
2804
2787
  },
@@ -2882,8 +2865,9 @@ function decodeDraft(value, scope) {
2882
2865
  const items = input.items.map((raw) => {
2883
2866
  const item = record(raw);
2884
2867
  const to = record(item.to);
2868
+ const amount = record(item.amount);
2885
2869
  const partyId = toPartyId(identifier(item.partyId));
2886
- if (to.kind !== "party" || to.partyId !== partyId || item.gross !== item.net || !Array.isArray(item.adjustments) || item.adjustments.length !== 0) throw Errors.invalidInput("payrollDraft", "Invalid draft recipient");
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");
2887
2871
  const net = minorAmount(item.net);
2888
2872
  return {
2889
2873
  to: {
@@ -2891,12 +2875,20 @@ function decodeDraft(value, scope) {
2891
2875
  partyId
2892
2876
  },
2893
2877
  partyId,
2878
+ amount: {
2879
+ asset: requireAsset(amount.asset).assetId,
2880
+ value: amount.value
2881
+ },
2894
2882
  gross: net,
2895
2883
  net,
2884
+ currency: item.currency,
2885
+ decimals: item.decimals,
2896
2886
  adjustments: []
2897
2887
  };
2898
2888
  });
2899
2889
  if (new Set(items.map((item) => item.partyId)).size !== items.length) throw Errors.invalidInput("payrollDraft", "Duplicate draft recipient");
2890
+ 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");
2900
2892
  return {
2901
2893
  version: 1,
2902
2894
  scope,
@@ -2907,6 +2899,7 @@ function decodeDraft(value, scope) {
2907
2899
  },
2908
2900
  input: {
2909
2901
  permissionId: identifier(input.permissionId),
2902
+ asset: exactAsset.assetId,
2910
2903
  period: {
2911
2904
  start: period.start,
2912
2905
  end: period.start
@@ -3110,7 +3103,6 @@ function termsSeeds(terms, asset, period) {
3110
3103
  }
3111
3104
  //#endregion
3112
3105
  //#region src/headless/payroll/use-payroll-run.ts
3113
- const GRAMMAR = { grammar: "code" };
3114
3106
  /**
3115
3107
  * The period this composer composes: an INSTANT run (ruling Q3). `authorizeRun`
3116
3108
  * stamps `start === end === runAt` at submit, and the payslip's period renders
@@ -3134,6 +3126,32 @@ const INSTANT_RUN_PERIOD = {
3134
3126
  function amountText(amounts, seeds, recipientId) {
3135
3127
  return amounts[recipientId]?.value ?? seeds.get(recipientId) ?? "";
3136
3128
  }
3129
+ function freshRunItems(input) {
3130
+ const items = [];
3131
+ for (const [index, recipientId] of input.selected.entries()) {
3132
+ const to = input.payableRefs[index];
3133
+ const amount = input.amounts[index];
3134
+ const terms = input.compensationTerms.get(recipientId);
3135
+ if (to === void 0 || amount === void 0 || terms === void 0) throw Errors.unknown();
3136
+ items.push({
3137
+ to: {
3138
+ kind: "party",
3139
+ partyId: toPartyId(recipientId)
3140
+ },
3141
+ partyId: recipientId,
3142
+ amount,
3143
+ gross: terms.rate,
3144
+ net: terms.rate,
3145
+ currency: terms.currency,
3146
+ decimals: terms.decimals,
3147
+ adjustments: []
3148
+ });
3149
+ }
3150
+ return items;
3151
+ }
3152
+ function hasRequiredCompensation(frozen, writerNamesSelectedAsset, selected, terms) {
3153
+ return frozen || !writerNamesSelectedAsset || selected.every((recipientId) => terms.has(recipientId));
3154
+ }
3137
3155
  /**
3138
3156
  * WHICH run the composer is composing, by MEANING rather than by object shape.
3139
3157
  *
@@ -3176,7 +3194,6 @@ function initialAmounts(prefill) {
3176
3194
  function assetSelectionsFromAccount(account) {
3177
3195
  if (account === null) return [];
3178
3196
  return account.balances.map((position) => {
3179
- const peg = positionPeg(position.asset);
3180
3197
  return {
3181
3198
  option: {
3182
3199
  id: position.asset,
@@ -3186,10 +3203,9 @@ function assetSelectionsFromAccount(account) {
3186
3203
  value: position.available
3187
3204
  })
3188
3205
  },
3189
- money: peg === null ? null : {
3190
- currency: peg,
3191
- value: position.available,
3192
- decimals: position.decimals
3206
+ money: {
3207
+ asset: position.asset,
3208
+ value: position.available
3193
3209
  }
3194
3210
  };
3195
3211
  });
@@ -3207,17 +3223,13 @@ function usePayrollRun(input) {
3207
3223
  const terms = useOrgTerms(orgId);
3208
3224
  const [selected, setSelected] = useState(() => prefill?.recipientIds ?? []);
3209
3225
  const [amounts, setAmounts] = useState(() => initialAmounts(prefill));
3210
- const restoreCurrency = treasury.data?.available.currency;
3211
3226
  const { draftScope, currentScope, draft, draftError, restorationPending, activateDraft, restoreDraft, setDraft, recovery, draftBlocked } = useRunDraft(client, orgId, useCallback((restored) => {
3212
3227
  setSelected(restored.input.items.map((item) => item.partyId));
3213
- if (restoreCurrency !== void 0 && restoreCurrency === restored.asset.currency) setAmounts(Object.fromEntries(restored.input.items.map((item) => [item.partyId, {
3214
- value: fromMinorUnits(BigInt(item.net), {
3215
- currency: restoreCurrency,
3216
- decimals: restored.asset.decimals
3217
- }).value,
3228
+ if (requireAsset(restored.input.asset).assetId === restored.input.asset) setAmounts(Object.fromEntries(restored.input.items.map((item) => [item.partyId, {
3229
+ value: item.amount.value,
3218
3230
  prefilled: false
3219
3231
  }])));
3220
- }, [restoreCurrency]));
3232
+ }, []));
3221
3233
  const frozen = draft !== null;
3222
3234
  const composerKey = composerIdentity(orgId, prefill, draftScope, frozen);
3223
3235
  const [seededKey, setSeededKey] = useState(composerKey);
@@ -3279,12 +3291,19 @@ function usePayrollRun(input) {
3279
3291
  }
3280
3292
  return resolved;
3281
3293
  }, [refs, selected]);
3282
- const seeds = useMemo(() => termsSeeds(terms.data ?? [], assetMoney, INSTANT_RUN_PERIOD), [assetMoney, terms.data]);
3294
+ const seeds = useMemo(() => {
3295
+ if (assetMoney === null) return /* @__PURE__ */ new Map();
3296
+ const metadata = requireAsset(assetMoney.asset);
3297
+ return termsSeeds(terms.data ?? [], metadata.peg === null ? null : {
3298
+ currency: metadata.peg,
3299
+ decimals: metadata.decimals
3300
+ }, INSTANT_RUN_PERIOD);
3301
+ }, [assetMoney, terms.data]);
3283
3302
  const entries = useMemo(() => {
3284
3303
  if (assetMoney === null) return [];
3285
3304
  return selected.map((recipientId) => {
3286
3305
  const value = amountText(amounts, seeds, recipientId);
3287
- const parsed = value.trim() === "" ? null : parseMoney(value, assetMoney);
3306
+ const parsed = value.trim() === "" ? null : parseAssetAmount(value, requireAsset(assetMoney.asset));
3288
3307
  return {
3289
3308
  recipientId,
3290
3309
  name: named.get(recipientId)?.name ?? recipientId,
@@ -3292,7 +3311,7 @@ function usePayrollRun(input) {
3292
3311
  change: (text) => change(recipientId, text),
3293
3312
  error: parsed !== null && isMoneyParseError(parsed) ? parsed.reason : null,
3294
3313
  prefilled: amounts[recipientId]?.prefilled ?? seeds.has(recipientId),
3295
- currency: assetMoney.currency
3314
+ currency: requireAsset(assetMoney.asset).symbol
3296
3315
  };
3297
3316
  });
3298
3317
  }, [
@@ -3307,7 +3326,7 @@ function usePayrollRun(input) {
3307
3326
  if (assetMoney === null || selected.length === 0) return null;
3308
3327
  const money = [];
3309
3328
  for (const recipientId of selected) {
3310
- const parsed = parseMoney(amountText(amounts, seeds, recipientId), assetMoney);
3329
+ const parsed = parseAssetAmount(amountText(amounts, seeds, recipientId), requireAsset(assetMoney.asset));
3311
3330
  if (isMoneyParseError(parsed)) return null;
3312
3331
  money.push(parsed);
3313
3332
  }
@@ -3318,17 +3337,25 @@ function usePayrollRun(input) {
3318
3337
  seeds,
3319
3338
  selected
3320
3339
  ]);
3321
- const total = useMemo(() => parsedAmounts === null || assetMoney === null ? null : fromMinorUnits(parsedAmounts.reduce((sum, money) => sum + toMinorUnits(money), 0n), assetMoney), [assetMoney, parsedAmounts]);
3322
- const availableRaw = assetMoney === null ? null : toMinorUnits(assetMoney);
3323
- const totalRaw = total === null ? null : toMinorUnits(total);
3340
+ const total = useMemo(() => parsedAmounts === null || assetMoney === null ? null : {
3341
+ asset: assetMoney.asset,
3342
+ value: assetFromMinorUnits(parsedAmounts.reduce((sum, amount) => sum + toRaw(amount, requireAsset(amount.asset)), 0n), {
3343
+ asset: assetMoney.asset,
3344
+ decimals: requireAsset(assetMoney.asset).decimals
3345
+ }).value
3346
+ }, [assetMoney, parsedAmounts]);
3347
+ const availableRaw = assetMoney === null ? null : toRaw(assetMoney, requireAsset(assetMoney.asset));
3348
+ const totalRaw = total === null ? null : toRaw(total, requireAsset(total.asset));
3324
3349
  const funds = availableRaw === null || totalRaw === null ? "unknown" : totalRaw > availableRaw ? "insufficient" : "available";
3325
3350
  const budgets = me.data?.budgets ?? [];
3351
+ const compensationTerms = useMemo(() => new Map((terms.data ?? []).filter((row) => row.unit === "paycheck" && /^[1-9][0-9]*$/u.test(row.rate)).map((row) => [row.partyId, row])), [terms.data]);
3352
+ const writerNamesSelectedAsset = writeNamesAsset();
3326
3353
  const blockedReason = firstFalseFact({
3327
3354
  signerReady: signerStatus === "ready",
3328
3355
  ...runAuthorityFacts(draft, budgets, selectedAsset, assetMoney, funds),
3329
3356
  recipientCount: payableRefs === null ? 0 : selected.length,
3330
- amountsParse: total !== null,
3331
- writeNamesAsset: writeNamesAsset()
3357
+ amountsParse: total !== null && hasRequiredCompensation(frozen, writerNamesSelectedAsset, selected, compensationTerms),
3358
+ writeNamesAsset: writerNamesSelectedAsset
3332
3359
  });
3333
3360
  const authorize = useMutation({
3334
3361
  mutationFn: async ({ candidate, options }) => {
@@ -3350,7 +3377,7 @@ function usePayrollRun(input) {
3350
3377
  if (candidate.scope === currentScope.current) restoreDraft(candidate.scope);
3351
3378
  }
3352
3379
  });
3353
- const totalDisplay = total === null ? null : formatMoney(total, GRAMMAR);
3380
+ const totalDisplay = total === null ? null : formatAssetAmount(total);
3354
3381
  const authorizeMutate = authorize.mutate;
3355
3382
  const submit = useCallback((options) => {
3356
3383
  const attempt = toInvocationOptions(options);
@@ -3359,41 +3386,35 @@ function usePayrollRun(input) {
3359
3386
  if (permissionId === void 0 || totalDisplay === null || parsedAmounts === null) return;
3360
3387
  if (payableRefs === null) return;
3361
3388
  const recipientCount = selected.length;
3362
- const items = [];
3363
- for (const [index, recipientId] of selected.entries()) {
3364
- const to = payableRefs[index];
3365
- const parsed = parsedAmounts[index];
3366
- if (to === void 0 || parsed === void 0) return;
3367
- const raw = toMinorUnits(parsed).toString();
3368
- items.push({
3369
- to: {
3370
- kind: "party",
3371
- partyId: toPartyId(recipientId)
3372
- },
3373
- partyId: recipientId,
3374
- gross: raw,
3375
- net: raw,
3376
- adjustments: []
3389
+ let candidate;
3390
+ if (frozen) candidate = draft;
3391
+ else {
3392
+ const items = freshRunItems({
3393
+ selected,
3394
+ payableRefs,
3395
+ amounts: parsedAmounts,
3396
+ compensationTerms
3377
3397
  });
3378
- }
3379
- const runAt = Date.now();
3380
- const candidate = frozen ? draft : {
3381
- version: 1,
3382
- scope: draftScope,
3383
- asset: {
3384
- id: selectedAsset.id,
3385
- currency: assetMoney.currency,
3386
- decimals: assetMoney.decimals
3387
- },
3388
- input: {
3389
- permissionId,
3390
- period: {
3391
- start: runAt,
3392
- end: runAt
3398
+ const runAt = Date.now();
3399
+ candidate = {
3400
+ version: 1,
3401
+ scope: draftScope,
3402
+ asset: {
3403
+ id: selectedAsset.id,
3404
+ currency: requireAsset(assetMoney.asset).symbol,
3405
+ decimals: requireAsset(assetMoney.asset).decimals
3393
3406
  },
3394
- items
3395
- }
3396
- };
3407
+ input: {
3408
+ permissionId,
3409
+ asset: selectedAsset.id,
3410
+ period: {
3411
+ start: runAt,
3412
+ end: runAt
3413
+ },
3414
+ items
3415
+ }
3416
+ };
3417
+ }
3397
3418
  submitGate.current = true;
3398
3419
  authorizeMutate({
3399
3420
  candidate,
@@ -3425,6 +3446,7 @@ function usePayrollRun(input) {
3425
3446
  selectedAsset,
3426
3447
  blockedReason,
3427
3448
  budgets,
3449
+ compensationTerms,
3428
3450
  onFailed,
3429
3451
  onRun,
3430
3452
  parsedAmounts,
@@ -3473,13 +3495,20 @@ function usePayrollRun(input) {
3473
3495
  };
3474
3496
  }
3475
3497
  function remainingBalanceDisplay(available, total, asset) {
3476
- return available === null || total === null || asset === null ? null : formatMoney(fromMinorUnits(available - total, asset), GRAMMAR);
3498
+ if (available === null || total === null || asset === null) return null;
3499
+ const remaining = available - total;
3500
+ const magnitude = remaining < 0n ? -remaining : remaining;
3501
+ const formatted = formatAssetAmount(assetFromMinorUnits(magnitude, {
3502
+ asset: asset.asset,
3503
+ decimals: requireAsset(asset.asset).decimals
3504
+ }));
3505
+ return remaining < 0n ? `-${formatted}` : formatted;
3477
3506
  }
3478
3507
  /** Persisted replay keeps its authorized Budget and asset; a prior debit cannot block its own recovery. */
3479
3508
  function runAuthorityFacts(draft, budgets, asset, money, funds) {
3480
3509
  return {
3481
3510
  budgetCount: draft !== null && !budgets.some((budget) => budget.id === draft.input.permissionId) ? 0 : budgets.length,
3482
- hasAsset: asset !== null && (draft === null || draft.asset.id === asset.id && draft.asset.currency === money?.currency && draft.asset.decimals === money?.decimals),
3511
+ hasAsset: asset !== null && (draft === null || draft.asset.id === asset.id && draft.input.asset === money?.asset),
3483
3512
  funds: draft?.input.requestKey === void 0 ? funds : "available"
3484
3513
  };
3485
3514
  }
@@ -3770,4 +3799,4 @@ const CapxulClientConnection = Object.assign(Root, {
3770
3799
  Failure
3771
3800
  });
3772
3801
  //#endregion
3773
- export { CapxulActivity, CapxulAuthenticationController, CapxulClientConnection, CapxulContacts, CapxulDashboardAccess, CapxulOnboardingController, CapxulOrgMember, CapxulPayroll, CapxulPayrollRun, CapxulProvider, CapxulSendMoney, acknowledgeOnboardingDestination, activeOnboardingRecovery, capxulKeys, clearOnboardingJourney, currentOnboardingJourneyId, entered, invalidateOnboardingJourneyObservation, isClaimed, isRestoring, loadOnboardingJourney, onboardingJourneyPosition, saveOnboardingJourney, startOnboardingJourney, useCapxul, useCapxulAccountBalance, useCapxulAccountFund, useCapxulActivity, useCapxulActivityDetail, useCapxulAnnotateMovement, useCapxulAuth, useCapxulCancelPayment, useCapxulClaimPayment, useCapxulClientOrNull, useCapxulCreateCommitment, useCapxulCreateOrg, useCapxulDestination, useCapxulHandleAvailability, useCapxulHoldings, useCapxulIdentity, useCapxulImageUpload, useCapxulInviteMember, useCapxulOrgMembers, useCapxulOrgRoles, useCapxulOrgTreasury, useCapxulOrganizationPay, useCapxulOrganizationPayBatch, useCapxulOrgs, useCapxulPay, useCapxulPayment, useCapxulPayments, useCapxulPermission, useCapxulPermissionAssign, useCapxulPermissionChange, useCapxulPermissionCreate, useCapxulPermissionReplace, useCapxulPermissionRevoke, useCapxulPermissions, useCapxulProfile, useCapxulRedirectPayment, useCapxulSend, useCapxulTransitions, useCapxulUploadImage };
3802
+ export { CapxulActivity, CapxulAuthenticationController, CapxulClientConnection, CapxulContacts, CapxulDashboardAccess, CapxulOnboardingController, CapxulOrgMember, CapxulPayroll, CapxulPayrollRun, CapxulProvider, CapxulSendMoney, acknowledgeOnboardingDestination, activeOnboardingRecovery, capxulKeys, clearOnboardingJourney, currentOnboardingJourneyId, entered, invalidateOnboardingJourneyObservation, isClaimed, isRestoring, loadOnboardingJourney, onboardingJourneyPosition, saveOnboardingJourney, startOnboardingJourney, useCapxul, useCapxulAccountBalance, useCapxulAccountFund, useCapxulActivity, useCapxulActivityDetail, useCapxulAnnotateMovement, useCapxulAuth, useCapxulCancelPayment, useCapxulClaimPayment, useCapxulClientOrNull, useCapxulCreateCommitment, useCapxulCreateOrg, useCapxulDestination, useCapxulHandleAvailability, useCapxulHoldings, useCapxulIdentity, useCapxulImageUpload, useCapxulOrgMembers, useCapxulOrgRoles, useCapxulOrgTreasury, useCapxulOrganizationPay, useCapxulOrganizationPayBatch, useCapxulOrgs, useCapxulPay, useCapxulPayment, useCapxulPayments, useCapxulPermission, useCapxulPermissionAssign, useCapxulPermissionChange, useCapxulPermissionCreate, useCapxulPermissionReplace, useCapxulPermissionRevoke, useCapxulPermissions, useCapxulProfile, useCapxulRedirectPayment, useCapxulSend, useCapxulTransitions, useCapxulUploadImage };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@capxul/sdk-react",
3
- "version": "4.20.0-beta.5",
3
+ "version": "4.20.0-beta.7",
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.5"
29
+ "@capxul/sdk": "4.20.0-beta.7"
30
30
  },
31
31
  "devDependencies": {
32
32
  "@tanstack/react-query": "^5.66.9",
@@ -43,6 +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
47
  "@capxul/errors": "0.3.0",
47
48
  "@capxul/types": "0.3.0",
48
49
  "@capxul/typescript-config": "0.0.0"