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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1,8 +1,8 @@
1
1
  "use client";
2
- import { a as useCapxulAuth, c as useCapxulIdentityOrNull, d as capxulKeys, f as useCapxulClientOrNull, i as entered, l as useCapxulSend, n as CapxulOnboardingController, o as useCapxulDestination, p as useCapxul, r as CapxulProvider, s as useCapxulIdentity, t as CapxulAuthenticationController, u as useCapxulTransitions } from "./controllers-BPEhjKX9.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-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, formatMoney, isCapxulError, isClaimed, isClaimed as isClaimed$1, isMoneyParseError, isRestoring, isRestoring as isRestoring$1, parseMoney, paymentPhase, 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
  /**
@@ -47,8 +47,9 @@ function useCapxulProfile() {
47
47
  //#region src/hooks/use-capxul-account-balance.ts
48
48
  function useCapxulAccountBalance(options) {
49
49
  const client = useCapxulClientOrNull();
50
+ const identity = useCapxulIdentityOrNull();
50
51
  return useQuery({
51
- queryKey: capxulKeys.accountBalance,
52
+ queryKey: accountKey(capxulKeys.accountBalance, identity),
52
53
  queryFn: async () => {
53
54
  return unwrapCapxulResult(await requireBootstrappedClient(client, CAPXUL_OPERATIONS.accounts.read).accounts.read());
54
55
  },
@@ -302,7 +303,7 @@ function useCapxulPayments(options) {
302
303
  const client = useCapxulClientOrNull();
303
304
  const identity = useCapxulIdentityOrNull();
304
305
  return useQuery({
305
- queryKey: capxulKeys.payments,
306
+ queryKey: accountKey(capxulKeys.payments, identity),
306
307
  queryFn: async () => {
307
308
  return unwrapCapxulResult(await requireBootstrappedClient(client, CAPXUL_OPERATIONS.payments.list).payments.list());
308
309
  },
@@ -311,8 +312,9 @@ function useCapxulPayments(options) {
311
312
  }
312
313
  function useCapxulPayment(paymentId, options) {
313
314
  const client = useCapxulClientOrNull();
315
+ const identity = useCapxulIdentityOrNull();
314
316
  return useQuery({
315
- queryKey: capxulKeys.payment(paymentId),
317
+ queryKey: accountKey(capxulKeys.payment(paymentId), identity),
316
318
  queryFn: async () => {
317
319
  if (paymentId === void 0) throw Errors.invalidInput("paymentId", "required for payments.get");
318
320
  return unwrapCapxulResult(await requireBootstrappedClient(client, CAPXUL_OPERATIONS.payments.get).payments.get(paymentId));
@@ -430,7 +432,7 @@ function useCapxulActivity(params) {
430
432
  const client = useCapxulClientOrNull();
431
433
  const identity = useCapxulIdentityOrNull();
432
434
  return useQuery({
433
- queryKey: [...capxulKeys.activity, params ?? {}],
435
+ queryKey: accountKey([...capxulKeys.activity, params ?? {}], identity),
434
436
  queryFn: async () => {
435
437
  return unwrapCapxulResult(await requireBootstrappedClient(client, CAPXUL_OPERATIONS.activity.list).activity.list(params));
436
438
  },
@@ -441,7 +443,7 @@ function useCapxulActivityDetail(reference, actor) {
441
443
  const client = useCapxulClientOrNull();
442
444
  const identity = useCapxulIdentityOrNull();
443
445
  return useQuery({
444
- queryKey: capxulKeys.activityDetail(reference, actor),
446
+ queryKey: accountKey(capxulKeys.activityDetail(reference, actor), identity),
445
447
  queryFn: async () => {
446
448
  if (reference === void 0) throw Errors.invalidInput("reference", "required for activity.get");
447
449
  return unwrapCapxulResult(await requireBootstrappedClient(client, CAPXUL_OPERATIONS.activity.get).activity.get(reference, actor === void 0 ? void 0 : { actor }));
@@ -466,7 +468,7 @@ function useCapxulHoldings(params) {
466
468
  const identity = useCapxulIdentityOrNull();
467
469
  const actor = params?.actor;
468
470
  return useQuery({
469
- queryKey: capxulKeys.holdings(actor),
471
+ queryKey: accountKey(capxulKeys.holdings(actor), identity),
470
472
  queryFn: async () => {
471
473
  return unwrapCapxulResult(await requireBootstrappedClient(client, CAPXUL_OPERATIONS.holdings.current).holdings.current(actor === void 0 ? void 0 : { actor }));
472
474
  },
@@ -477,8 +479,9 @@ function useCapxulHoldings(params) {
477
479
  //#region src/hooks/use-capxul-orgs.ts
478
480
  function useCapxulOrgs(options) {
479
481
  const client = useCapxulClientOrNull();
482
+ const identity = useCapxulIdentityOrNull();
480
483
  return useQuery({
481
- queryKey: capxulKeys.orgs,
484
+ queryKey: accountKey(capxulKeys.orgs, identity),
482
485
  queryFn: async () => {
483
486
  return unwrapCapxulResult(await requireBootstrappedClient(client, "orgs").orgs());
484
487
  },
@@ -517,9 +520,10 @@ function useCapxulOrgRoles(orgId, options) {
517
520
  //#region src/hooks/use-capxul-org-treasury.ts
518
521
  function useCapxulOrgTreasury(orgId, options) {
519
522
  const client = useCapxulClientOrNull();
523
+ const identity = useCapxulIdentityOrNull();
520
524
  const enabled = options?.enabled ?? true;
521
525
  return useQuery({
522
- queryKey: capxulKeys.orgTreasury(orgId),
526
+ queryKey: accountKey(capxulKeys.orgTreasury(orgId), identity),
523
527
  queryFn: async () => {
524
528
  if (orgId === void 0) throw Errors.invalidInput("orgId", "required for useCapxulOrgTreasury");
525
529
  return unwrapCapxulResult(await requireBootstrappedClient(client, CAPXUL_OPERATIONS.org.treasury).org(orgId).treasury());
@@ -542,22 +546,6 @@ function useCapxulCreateOrg() {
542
546
  });
543
547
  }
544
548
  //#endregion
545
- //#region src/hooks/use-capxul-invite-member.ts
546
- function useCapxulInviteMember(orgId) {
547
- const client = useCapxulClientOrNull();
548
- const queryClient = useQueryClient();
549
- return useMutation({
550
- mutationFn: async (input) => {
551
- if (orgId === void 0) throw Errors.invalidInput("orgId", "required for useCapxulInviteMember");
552
- return unwrapCapxulResult(await requireBootstrappedClient(client, CAPXUL_OPERATIONS.org.invite).org(orgId).invite(input));
553
- },
554
- onSuccess: async () => {
555
- if (orgId === void 0) return;
556
- await queryClient.invalidateQueries({ queryKey: capxulKeys.orgMembers(orgId) });
557
- }
558
- });
559
- }
560
- //#endregion
561
549
  //#region src/headless/journey/journey-state.ts
562
550
  const JOURNEY_KEY = "capxul.onboarding.journey.v1";
563
551
  const VALIDATED_OWNER_KEY = "capxul.onboarding.validated-owner.v1";
@@ -879,6 +867,8 @@ function useCapxulUploadImage() {
879
867
  }
880
868
  //#endregion
881
869
  //#region src/headless/contacts/use-contacts.ts
870
+ /** One captured snapshot, or nothing — never a fabricated empty options object. */
871
+ const carry$1 = (options) => options === void 0 ? {} : { options };
882
872
  function useContacts(input) {
883
873
  const { actor, onAdded, onFailed } = input;
884
874
  const client = useCapxulClientOrNull();
@@ -895,35 +885,40 @@ function useContacts(input) {
895
885
  const writeMutate = useMutation({
896
886
  mutationFn: async (command) => {
897
887
  const target = requireBook(book, `addressBook.${command.kind}`);
888
+ const controls = toInvocationControls(command.options);
898
889
  return unwrapCapxulResult(command.kind === "rename" ? await target.label({
899
890
  entryId: command.id,
900
891
  label: command.name
901
- }) : command.kind === "hide" ? await target.hide(command.id) : await target.unhide(command.id));
892
+ }, controls) : command.kind === "hide" ? await target.hide(command.id, controls) : await target.unhide(command.id, controls));
902
893
  },
903
894
  onSettled: invalidate,
904
- onError: onFailed
895
+ onError: (error, command) => onFailed(error, command.options)
905
896
  }).mutate;
906
897
  const [value, setValue] = useState("");
907
898
  const [addError, setAddError] = useState(null);
908
899
  const addContact = useMutation({
909
- mutationFn: async (ref) => unwrapCapxulResult(await requireBook(book, CAPXUL_OPERATIONS.addressBook.add).add({ ref })),
900
+ mutationFn: async (command) => unwrapCapxulResult(await requireBook(book, CAPXUL_OPERATIONS.addressBook.add).add({ ref: command.ref }, toInvocationControls(command.options))),
910
901
  onSettled: invalidate
911
902
  });
912
903
  const addMutate = addContact.mutate;
913
904
  const blockedReason = refusal !== null ? "org-unavailable" : value.trim() === "" ? "empty" : null;
914
905
  const submitGate = useRef(false);
915
- const submit = useCallback(() => {
906
+ const submit = useCallback((options) => {
916
907
  if (blockedReason !== null || submitGate.current) return;
908
+ const attempt = toInvocationOptions(options);
917
909
  submitGate.current = true;
918
910
  setAddError(null);
919
- addMutate(refFromTypedText(value), {
911
+ addMutate({
912
+ ref: refFromTypedText(value),
913
+ ...carry$1(attempt)
914
+ }, {
920
915
  onSuccess: (entry) => {
921
916
  setValue("");
922
- onAdded(toContact(entry));
917
+ onAdded(toContact(entry), attempt);
923
918
  },
924
919
  onError: (error) => {
925
920
  setAddError(error.code === "INVALID_INPUT" ? "unresolved" : "failed");
926
- onFailed(error);
921
+ onFailed(error, attempt);
927
922
  },
928
923
  onSettled: () => {
929
924
  submitGate.current = false;
@@ -944,18 +939,21 @@ function useContacts(input) {
944
939
  book,
945
940
  scope,
946
941
  refusal,
947
- rename: useCallback((id, name) => writeMutate({
942
+ rename: useCallback((id, name, options) => writeMutate({
948
943
  kind: "rename",
949
944
  id,
950
- name
945
+ name,
946
+ ...carry$1(toInvocationOptions(options))
951
947
  }), [writeMutate]),
952
- hide: useCallback((id) => writeMutate({
948
+ hide: useCallback((id, options) => writeMutate({
953
949
  kind: "hide",
954
- id
950
+ id,
951
+ ...carry$1(toInvocationOptions(options))
955
952
  }), [writeMutate]),
956
- unhide: useCallback((id) => writeMutate({
953
+ unhide: useCallback((id, options) => writeMutate({
957
954
  kind: "unhide",
958
- id
955
+ id,
956
+ ...carry$1(toInvocationOptions(options))
959
957
  }), [writeMutate]),
960
958
  add: {
961
959
  value,
@@ -973,10 +971,11 @@ function useContacts(input) {
973
971
  * query key, so React Query issues ONE request for them.
974
972
  */
975
973
  function useContactsList(engine, options) {
974
+ const identity = useCapxulIdentityOrNull();
976
975
  const includeHidden = options.show === "all";
977
976
  const { book, refusal } = engine;
978
977
  const query = useQuery({
979
- queryKey: capxulKeys.contactsList(engine.scope, includeHidden),
978
+ queryKey: accountKey(capxulKeys.contactsList(engine.scope, includeHidden), identity),
980
979
  queryFn: async () => unwrapCapxulResult(await requireBook(book, CAPXUL_OPERATIONS.addressBook.list).list({ includeHidden })),
981
980
  enabled: book !== null
982
981
  });
@@ -989,9 +988,9 @@ function useContactsList(engine, options) {
989
988
  if (entries === void 0) return [];
990
989
  return (limit === void 0 ? entries : entries.slice(0, limit)).map((entry) => Object.assign(toContact(entry), {
991
990
  initials: initialsOf(entry.label),
992
- rename: (name) => rename(entry.id, name),
993
- hide: () => hide(entry.id),
994
- unhide: () => unhide(entry.id)
991
+ rename: (name, attempt) => rename(entry.id, name, attempt),
992
+ hide: (attempt) => hide(entry.id, attempt),
993
+ unhide: (attempt) => unhide(entry.id, attempt)
995
994
  }));
996
995
  }, [
997
996
  entries,
@@ -1064,7 +1063,7 @@ function useContactsContext() {
1064
1063
  if (engine === null) throw new Error("CapxulContacts parts must be used inside <CapxulContacts>");
1065
1064
  return engine;
1066
1065
  }
1067
- function Root$6({ actor, onAdded, onFailed, children }) {
1066
+ function Root$7({ actor, onAdded, onFailed, children }) {
1068
1067
  const engine = useContacts({
1069
1068
  actor,
1070
1069
  onAdded,
@@ -1088,7 +1087,7 @@ function Add({ children }) {
1088
1087
  return /* @__PURE__ */ jsx(Fragment, { children: children(useContactsContext().add) });
1089
1088
  }
1090
1089
  /** The compound shape the app's own design system already uses (`StatusTabs`). */
1091
- const CapxulContacts = Object.assign(Root$6, {
1090
+ const CapxulContacts = Object.assign(Root$7, {
1092
1091
  Summary: Summary$3,
1093
1092
  List,
1094
1093
  Add
@@ -1126,15 +1125,8 @@ function useActivityEnabled() {
1126
1125
  }
1127
1126
  //#endregion
1128
1127
  //#region src/headless/activity/detail.ts
1129
- /**
1130
- * ONE finished document, never the array, so no consumer writes `documents[0]`:
1131
- * the payslip, else the receipt, else nothing. Those two kinds are the ones the
1132
- * Download receipt button is for. A Movement detail carries no documents.
1133
- */
1134
- function pickReceiptDocument(detail) {
1135
- if (detail === null || detail.kind !== "payment") return null;
1136
- const documents = detail.payment.documents;
1137
- 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;
1138
1130
  }
1139
1131
  /**
1140
1132
  * The root actor and the whole reference are included. A payment reference can
@@ -1165,30 +1157,80 @@ function activityDetailKey(actor, reference) {
1165
1157
  }
1166
1158
  function useActivityDetail(reference, actor) {
1167
1159
  const client = useCapxulClientOrNull();
1160
+ const identity = useCapxulIdentityOrNull();
1168
1161
  const enabled = useActivityEnabled() && reference !== void 0;
1169
1162
  const detail = useQuery({
1170
- queryKey: capxulKeys.activityDetail(reference, actor),
1171
- queryFn: async () => {
1163
+ queryKey: accountKey(capxulKeys.activityDetail(reference, actor), identity),
1164
+ queryFn: async ({ signal }) => {
1172
1165
  if (reference === void 0) throw Errors.invalidInput("reference", "required for activity.get");
1173
- 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
+ }));
1174
1170
  },
1175
1171
  enabled
1176
1172
  });
1177
- 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]);
1178
1197
  const [isOpeningDocument, setIsOpeningDocument] = useState(false);
1179
- const openReceiptDocument = useCallback(async () => {
1180
- 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);
1181
1204
  setIsOpeningDocument(true);
1182
1205
  try {
1183
- 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);
1184
1215
  } finally {
1185
- setIsOpeningDocument(false);
1216
+ pending.current.delete(controller);
1217
+ if (!controller.signal.aborted) setIsOpeningDocument(pending.current.size > 0);
1186
1218
  }
1187
1219
  }, [
1188
1220
  client,
1189
- receiptDocument,
1221
+ selectedDocument,
1190
1222
  reference
1191
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]);
1192
1234
  const [typed, setTyped] = useState(null);
1193
1235
  const annotate = useCapxulAnnotateMovement();
1194
1236
  const saveMemo = useCallback(async () => {
@@ -1213,8 +1255,15 @@ function useActivityDetail(reference, actor) {
1213
1255
  typed
1214
1256
  ]);
1215
1257
  return {
1216
- receiptDocument,
1217
- openReceiptDocument,
1258
+ ...detail.data?.kind === "payment" ? { payment: {
1259
+ status: detail.data.payment.status,
1260
+ ...detail.data.payment.execution === void 0 ? {} : { execution: detail.data.payment.execution }
1261
+ } } : {},
1262
+ documents,
1263
+ selectedDocument,
1264
+ selectDocument,
1265
+ renderDocument,
1266
+ downloadDocument,
1218
1267
  isOpeningDocument,
1219
1268
  memo: typed ?? detail.data?.annotation?.memo ?? "",
1220
1269
  setMemo: setTyped,
@@ -1227,6 +1276,7 @@ function useActivityDetail(reference, actor) {
1227
1276
  //#endregion
1228
1277
  //#region src/headless/activity/summary.ts
1229
1278
  const DAY_MS = 864e5;
1279
+ const display = (money) => money === null ? null : formatMoney(money);
1230
1280
  /**
1231
1281
  * `"last30days"` is the last 30 whole UTC days including today. Quantizing to
1232
1282
  * the UTC day keeps the cache key stable for a whole day instead of changing on
@@ -1237,57 +1287,6 @@ function resolveWindow(window, now) {
1237
1287
  if (window === "last30days") return { from: Math.floor(now / DAY_MS) * DAY_MS - 29 * DAY_MS };
1238
1288
  return window;
1239
1289
  }
1240
- /** An integer raw-unit string lifted into a major-unit decimal string. */
1241
- function majorUnits(raw, decimals) {
1242
- const negative = raw.startsWith("-");
1243
- const digits = negative ? raw.slice(1) : raw;
1244
- const sign = negative ? "-" : "";
1245
- if (decimals === 0) return `${sign}${digits}`;
1246
- const padded = digits.padStart(decimals + 1, "0");
1247
- const cut = padded.length - decimals;
1248
- return `${sign}${padded.slice(0, cut)}.${padded.slice(cut)}`;
1249
- }
1250
- function absoluteNet(total) {
1251
- const net = BigInt(total.net);
1252
- return net < 0n ? -net : net;
1253
- }
1254
- /**
1255
- * Buckets carry different `decimals`, so their raw integers are not comparable.
1256
- * Both are scaled to the wider precision before the magnitudes are ordered.
1257
- */
1258
- function compareNet(left, right) {
1259
- const width = Math.max(left.decimals, right.decimals);
1260
- const scaledLeft = absoluteNet(left) * 10n ** BigInt(width - left.decimals);
1261
- const scaledRight = absoluteNet(right) * 10n ** BigInt(width - right.decimals);
1262
- if (scaledLeft === scaledRight) return 0;
1263
- return scaledLeft < scaledRight ? -1 : 1;
1264
- }
1265
- /**
1266
- * Which currency `.Summary` shows: the bucket matching the primary holding's
1267
- * currency, else the bucket with the largest absolute `net`, breaking a tie on
1268
- * currency code ascending so the choice is stable across reloads.
1269
- *
1270
- * The primary holding is matched on `available.currency` — the consumer
1271
- * `CurrencyCode` — and NOT on `Holding.symbol`, which is the raw token ticker
1272
- * ("USDX") and can never equal a bucket's currency ("USD"). See the contract
1273
- * correction on #1449.
1274
- */
1275
- function pickBucket(totals, primaryCurrency) {
1276
- if (primaryCurrency !== null) {
1277
- const matched = totals.find((total) => total.currency === primaryCurrency);
1278
- if (matched !== void 0) return matched;
1279
- }
1280
- let best = null;
1281
- for (const total of totals) {
1282
- if (best === null) {
1283
- best = total;
1284
- continue;
1285
- }
1286
- const order = compareNet(total, best);
1287
- if (order > 0 || order === 0 && total.currency < best.currency) best = total;
1288
- }
1289
- return best;
1290
- }
1291
1290
  /**
1292
1291
  * The current UTC day number, which advances ON ITS OWN at UTC midnight.
1293
1292
  *
@@ -1322,57 +1321,49 @@ function useUtcDay(active) {
1322
1321
  }
1323
1322
  function useActivitySummary(actor, window) {
1324
1323
  const client = useCapxulClientOrNull();
1324
+ const identity = useCapxulIdentityOrNull();
1325
1325
  const enabled = useActivityEnabled();
1326
1326
  const range = resolveWindow(window, useUtcDay(window === "last30days") * DAY_MS);
1327
1327
  const summary = useQuery({
1328
- queryKey: capxulKeys.activitySummary(actor, range),
1328
+ queryKey: accountKey(capxulKeys.activitySummary(actor, range), identity),
1329
1329
  queryFn: async () => {
1330
1330
  const params = {};
1331
1331
  if (actor !== void 0) params.actor = actor;
1332
1332
  if (range !== void 0) params.window = range;
1333
- return unwrapCapxulResult(await requireBootstrappedClient(client, CAPXUL_OPERATIONS.activity.summary).activity.summary(params));
1334
- },
1335
- enabled
1336
- });
1337
- const primary = useQuery({
1338
- queryKey: [...capxulKeys.holdings(actor), "primary"],
1339
- queryFn: async () => {
1340
- 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
+ };
1341
1338
  },
1342
1339
  enabled
1343
1340
  });
1344
1341
  const totals = summary.data?.totals;
1345
- const primaryCurrency = primary.data?.available.currency ?? null;
1346
- const isLoading = summary.isLoading || primary.isLoading;
1347
- const bucket = useMemo(() => totals === void 0 || isLoading ? null : pickBucket(totals, primaryCurrency), [
1348
- isLoading,
1349
- primaryCurrency,
1350
- totals
1351
- ]);
1352
- if (bucket === null) return {
1342
+ const isLoading = summary.isLoading;
1343
+ if (summary.data === void 0 || totals === void 0 || totals.length === 0 || isLoading) return {
1353
1344
  currency: null,
1354
1345
  moneyIn: null,
1355
1346
  moneyOut: null,
1356
1347
  net: null,
1357
1348
  pending: null,
1349
+ unrated: [],
1358
1350
  count: summary.data?.count ?? 0,
1359
1351
  isLoading,
1360
- error: asCapxulError$1(summary.error)
1352
+ error: asCapxulError$1(summary.error),
1353
+ ...summary.data?.readWarning === void 0 ? {} : { readWarning: summary.data.readWarning }
1361
1354
  };
1362
- const display = (raw) => formatMoney({
1363
- currency: bucket.currency,
1364
- value: majorUnits(raw, bucket.decimals),
1365
- decimals: bucket.decimals
1366
- });
1355
+ const valuation = summary.data.valuation;
1367
1356
  return {
1368
- currency: bucket.currency,
1369
- moneyIn: display(bucket.in),
1370
- moneyOut: display(bucket.out),
1371
- net: display(bucket.net),
1372
- 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,
1373
1363
  count: summary.data?.count ?? 0,
1374
1364
  isLoading,
1375
- error: asCapxulError$1(summary.error)
1365
+ error: asCapxulError$1(summary.error),
1366
+ ...summary.data?.readWarning === void 0 ? {} : { readWarning: summary.data.readWarning }
1376
1367
  };
1377
1368
  }
1378
1369
  //#endregion
@@ -1395,7 +1386,7 @@ const SIGN = Object.freeze({
1395
1386
  self: ""
1396
1387
  });
1397
1388
  function toActivityRow(item, actor) {
1398
- const amountDisplay = formatMoney(item.amount);
1389
+ const amountDisplay = formatAssetAmount(item.amount);
1399
1390
  const signedAmountDisplay = `${SIGN[item.direction]}${amountDisplay}`;
1400
1391
  if (item.kind === "movement") return {
1401
1392
  kind: "movement",
@@ -1407,32 +1398,37 @@ function toActivityRow(item, actor) {
1407
1398
  counterpartyLabel: item.counterpartyLabel,
1408
1399
  amountDisplay,
1409
1400
  signedAmountDisplay,
1410
- currency: item.amount.currency,
1401
+ currency: assetSymbolFor(item.amount.asset),
1411
1402
  createdAt: item.createdAt,
1412
1403
  classification: item.classification,
1413
1404
  phase: MOVEMENT_PHASE,
1414
1405
  receipt: item.receipt
1415
1406
  };
1407
+ const reference = actor === void 0 ? {
1408
+ kind: "payment",
1409
+ id: item.id
1410
+ } : {
1411
+ kind: "payment",
1412
+ id: item.id,
1413
+ actor
1414
+ };
1416
1415
  return {
1417
1416
  kind: "payment",
1418
- reference: actor === void 0 ? {
1419
- kind: "payment",
1420
- id: item.id
1421
- } : {
1422
- kind: "payment",
1423
- id: item.id,
1424
- actor
1425
- },
1417
+ ...item.execution === void 0 ? {} : { execution: item.execution },
1418
+ reference,
1426
1419
  direction: item.direction,
1427
1420
  counterpartyLabel: item.counterpartyLabel,
1428
1421
  amountDisplay,
1429
1422
  signedAmountDisplay,
1430
- currency: item.amount.currency,
1423
+ currency: assetSymbolFor(item.amount.asset),
1431
1424
  createdAt: item.createdAt,
1432
1425
  ledgerKind: item.ledgerKind,
1433
1426
  paymentType: item.paymentType,
1434
1427
  status: item.status,
1435
- phase: paymentPhase(item.status),
1428
+ phase: item.execution?.notSent ? {
1429
+ ...paymentPhase(item.status),
1430
+ needsAction: item.direction !== "in"
1431
+ } : paymentPhase(item.status),
1436
1432
  receipt: item.receipt
1437
1433
  };
1438
1434
  }
@@ -1505,8 +1501,8 @@ function buildCsv(items, actor, search) {
1505
1501
  row.counterpartyLabel,
1506
1502
  row.direction,
1507
1503
  item.amount.value,
1508
- item.amount.currency,
1509
- row.kind === "payment" ? row.status : "",
1504
+ assetSymbolFor(item.amount.asset),
1505
+ row.kind === "payment" ? row.execution?.notSent ? "not_sent" : row.execution?.status === "unresolved" ? "verifying" : row.status : "",
1510
1506
  row.reference.id,
1511
1507
  row.receipt?.transactionHash ?? ""
1512
1508
  ]));
@@ -1538,6 +1534,8 @@ function clampPageSize(pageSize) {
1538
1534
  function useActivity(input) {
1539
1535
  const { actor, pageSize } = input;
1540
1536
  const client = useCapxulClientOrNull();
1537
+ const identity = useCapxulIdentityOrNull();
1538
+ const account = actingAccount(identity);
1541
1539
  const enabled = useActivityEnabled();
1542
1540
  const limit = clampPageSize(pageSize);
1543
1541
  const warned = useRef(false);
@@ -1567,21 +1565,51 @@ function useActivity(input) {
1567
1565
  if (filter !== void 0) params.filter = filter;
1568
1566
  return params;
1569
1567
  }, [actor, filter]);
1568
+ const selectedRetry = useRef(void 0);
1569
+ const takeSelectedRetry = useCallback(() => {
1570
+ const attempt = selectedRetry.current;
1571
+ selectedRetry.current = void 0;
1572
+ return toInvocationControls(attempt);
1573
+ }, []);
1570
1574
  const query = useInfiniteQuery({
1571
- queryKey: [
1575
+ queryKey: accountKey([
1572
1576
  ...capxulKeys.activity,
1573
1577
  "list",
1574
1578
  actor ?? "self",
1575
1579
  limit,
1576
1580
  filter ?? "none"
1577
- ],
1581
+ ], identity),
1578
1582
  queryFn: async ({ pageParam }) => {
1579
- return unwrapCapxulResult(await requireBootstrappedClient(client, CAPXUL_OPERATIONS.activity.list).activity.list(listParams(pageParam, limit)));
1583
+ return unwrapCapxulResult(await requireBootstrappedClient(client, CAPXUL_OPERATIONS.activity.list).activity.list(listParams(pageParam, limit), takeSelectedRetry()));
1580
1584
  },
1581
1585
  initialPageParam: void 0,
1582
1586
  getNextPageParam: (last) => last.cursor ?? void 0,
1583
1587
  enabled
1584
1588
  });
1589
+ const queryClient = useQueryClient();
1590
+ useEffect(() => {
1591
+ if (!enabled || client === null) return;
1592
+ let stop;
1593
+ let cancelled = false;
1594
+ client.activity.subscribe(() => {
1595
+ if (cancelled) return;
1596
+ queryClient.invalidateQueries({ queryKey: capxulKeys.activity });
1597
+ }, actor === void 0 ? void 0 : { actor }).then((result) => {
1598
+ if (!result.ok) return;
1599
+ if (cancelled) result.value();
1600
+ else stop = result.value;
1601
+ });
1602
+ return () => {
1603
+ cancelled = true;
1604
+ stop?.();
1605
+ };
1606
+ }, [
1607
+ account,
1608
+ actor,
1609
+ client,
1610
+ enabled,
1611
+ queryClient
1612
+ ]);
1585
1613
  const pages = query.data?.pages;
1586
1614
  const rows = useMemo(() => {
1587
1615
  return (pages?.flatMap((page) => page.items) ?? []).map((item) => toActivityRow(item, actor)).filter((row) => matchesSearch(row, searchText));
@@ -1591,6 +1619,16 @@ function useActivity(input) {
1591
1619
  searchText
1592
1620
  ]);
1593
1621
  const { fetchNextPage, hasNextPage, isFetchingNextPage, refetch } = query;
1622
+ const emptyContinuation = pages?.at(-1)?.items.length === 0 && hasNextPage && query.error === null;
1623
+ useEffect(() => {
1624
+ if (enabled && emptyContinuation && !isFetchingNextPage) fetchNextPage();
1625
+ }, [
1626
+ enabled,
1627
+ emptyContinuation,
1628
+ fetchNextPage,
1629
+ isFetchingNextPage,
1630
+ pages
1631
+ ]);
1594
1632
  const pagingRef = useRef({
1595
1633
  fetchNextPage,
1596
1634
  hasNextPage,
@@ -1624,21 +1662,25 @@ function useActivity(input) {
1624
1662
  observerRef.current = observer;
1625
1663
  }, [loadMore]);
1626
1664
  const [isExporting, setIsExporting] = useState(false);
1627
- const toCsv = useCallback(async () => {
1665
+ const toCsv = useCallback(async (options) => {
1628
1666
  setIsExporting(true);
1667
+ const controls = toInvocationControls(toInvocationOptions(options));
1629
1668
  try {
1630
1669
  const bootstrapped = requireBootstrappedClient(client, CAPXUL_OPERATIONS.activity.list);
1631
1670
  const items = [];
1671
+ let readWarning;
1632
1672
  let cursor;
1633
1673
  for (let page = 0; page < EXPORT_PAGE_CEILING; page += 1) {
1634
- const result = unwrapCapxulResult(await bootstrapped.activity.list(listParams(cursor, EXPORT_PAGE_SIZE)));
1674
+ const result = unwrapCapxulResult(await bootstrapped.activity.list(listParams(cursor, EXPORT_PAGE_SIZE), controls));
1675
+ readWarning ??= result.readWarning;
1635
1676
  items.push(...result.items);
1636
1677
  cursor = result.cursor ?? void 0;
1637
1678
  if (cursor === void 0) break;
1638
1679
  }
1639
1680
  return {
1640
1681
  ...buildCsv(items, actor, searchText),
1641
- truncated: cursor !== void 0
1682
+ truncated: cursor !== void 0,
1683
+ ...readWarning === void 0 ? {} : { readWarning }
1642
1684
  };
1643
1685
  } finally {
1644
1686
  setIsExporting(false);
@@ -1654,9 +1696,19 @@ function useActivity(input) {
1654
1696
  setDirection(void 0);
1655
1697
  setStatus([]);
1656
1698
  }, []);
1657
- const retry = useCallback(() => {
1699
+ const retry = useCallback((options) => {
1700
+ selectedRetry.current = toInvocationOptions(options);
1658
1701
  refetch();
1659
- }, [refetch]);
1702
+ queryClient.invalidateQueries({
1703
+ queryKey: capxulKeys.activitySummary(actor).slice(0, -1),
1704
+ predicate: (cached) => cached.queryKey.at(-1) === accountKey([], identity)[0]
1705
+ });
1706
+ }, [
1707
+ actor,
1708
+ identity,
1709
+ queryClient,
1710
+ refetch
1711
+ ]);
1660
1712
  return {
1661
1713
  actor,
1662
1714
  filters: {
@@ -1679,10 +1731,11 @@ function useActivity(input) {
1679
1731
  },
1680
1732
  rows: {
1681
1733
  rows,
1682
- isLoading: query.isLoading,
1734
+ isLoading: query.isLoading || rows.length === 0 && emptyContinuation,
1683
1735
  error: asCapxulError$1(query.error),
1684
1736
  retry,
1685
- observedAt: pages === void 0 ? null : pages[pages.length - 1]?.observedAt ?? null
1737
+ observedAt: pages === void 0 ? null : pages[pages.length - 1]?.observedAt ?? null,
1738
+ ...pages?.some((page) => page.readWarning === "indexer-unavailable") ? { readWarning: "indexer-unavailable" } : {}
1686
1739
  },
1687
1740
  loadMore: {
1688
1741
  hasMore: hasNextPage,
@@ -1704,7 +1757,7 @@ function useActivityContext() {
1704
1757
  if (engine === null) throw new Error("CapxulActivity parts must be used inside <CapxulActivity>");
1705
1758
  return engine;
1706
1759
  }
1707
- function Root$5({ actor, pageSize, children }) {
1760
+ function Root$6({ actor, pageSize, children }) {
1708
1761
  const engine = useActivity({
1709
1762
  ...actor === void 0 ? {} : { actor },
1710
1763
  ...pageSize === void 0 ? {} : { pageSize }
@@ -1744,17 +1797,18 @@ function DetailInstance({ actor, reference, children }) {
1744
1797
  }
1745
1798
  function Detail({ reference, children }) {
1746
1799
  const { actor } = useActivityContext();
1800
+ const identity = useCapxulIdentityOrNull();
1747
1801
  return /* @__PURE__ */ jsx(DetailInstance, {
1748
1802
  actor,
1749
1803
  reference,
1750
1804
  children
1751
- }, activityDetailKey(actor, reference));
1805
+ }, JSON.stringify(accountKey([activityDetailKey(actor, reference)], identity)));
1752
1806
  }
1753
1807
  function Export({ children }) {
1754
1808
  return /* @__PURE__ */ jsx(Fragment, { children: children(useActivityContext().csv) });
1755
1809
  }
1756
1810
  /** The compound shape the app's own design system already uses (`StatusTabs`). */
1757
- const CapxulActivity = Object.assign(Root$5, {
1811
+ const CapxulActivity = Object.assign(Root$6, {
1758
1812
  Summary: Summary$2,
1759
1813
  Filters,
1760
1814
  Search,
@@ -1771,7 +1825,7 @@ function useOrgMeReading(orgId) {
1771
1825
  const ready = client !== null && identity !== null && isClaimed$1(identity);
1772
1826
  const mayStillOpen = client === null || identity === null || isRestoring$1(identity);
1773
1827
  const query = useQuery({
1774
- queryKey: capxulKeys.orgMe(orgId),
1828
+ queryKey: capxulKeys.orgMe(orgId, actingAccount(identity)),
1775
1829
  queryFn: async () => {
1776
1830
  return unwrapCapxulResult(await requireBootstrappedClient(client, CAPXUL_OPERATIONS.org.me).org(orgId).me());
1777
1831
  },
@@ -1795,6 +1849,26 @@ function useOrgMeReading(orgId) {
1795
1849
  ]);
1796
1850
  }
1797
1851
  //#endregion
1852
+ //#region src/internal/write-names-asset.ts
1853
+ /**
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).
1858
+ *
1859
+ * A Capxul payment writer takes `PaymentMoney` — a peg currency, a value and
1860
+ * `decimals` — and settles one fixed legacy token. No `AssetId` crosses that
1861
+ * seam, so a payment composed from a named position cannot name the asset it
1862
+ * moves: a position selected as USDT authorizes a different token.
1863
+ *
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.
1867
+ */
1868
+ function writeNamesAsset() {
1869
+ return false;
1870
+ }
1871
+ //#endregion
1798
1872
  //#region src/headless/send-money/signer-status.ts
1799
1873
  const NO_SUBSCRIPTION = () => () => {};
1800
1874
  function useSignerStatus(client) {
@@ -1944,6 +2018,37 @@ function useSelectedOption(options) {
1944
2018
  }, [options]);
1945
2019
  return [options.find((option) => option.id === selectedId) ?? null, select];
1946
2020
  }
2021
+ /**
2022
+ * One Send `.Asset` option per position the Account holds, taken from
2023
+ * `account.balances` — never from `account.available`, which is the Account's
2024
+ * fiat VALUATION of those positions (ADR-0028 R5/R6). Labelling the valuation
2025
+ * was the false identity this cut removes, and it collapsed USDC+USDT into one
2026
+ * "USD" row.
2027
+ *
2028
+ * The option is keyed on the ASSET id, never the symbol and never the account:
2029
+ * several positions share one account id, and a symbol is display only
2030
+ * (ADR-0028 R3). Every row is listed, so a real multi-position Account still
2031
+ * offers its holdings.
2032
+ */
2033
+ function assetSelectionsFromAccount$1(account) {
2034
+ if (account === null) return [];
2035
+ return account.balances.map((position) => {
2036
+ return {
2037
+ option: {
2038
+ id: position.asset,
2039
+ symbol: position.symbol,
2040
+ availableDisplay: formatAssetAmount({
2041
+ asset: position.asset,
2042
+ value: position.available
2043
+ })
2044
+ },
2045
+ money: {
2046
+ asset: position.asset,
2047
+ value: position.available
2048
+ }
2049
+ };
2050
+ });
2051
+ }
1947
2052
  function useSendMoney(input) {
1948
2053
  const { actor, onSent, onFailed } = input;
1949
2054
  const client = useCapxulClientOrNull();
@@ -1956,15 +2061,12 @@ function useSendMoney(input) {
1956
2061
  const treasury = useCapxulOrgTreasury(orgId, { enabled: actor.kind === "organization" });
1957
2062
  const accountQuery = actor.kind === "personal" ? balance : treasury;
1958
2063
  const account = accountQuery.data ?? null;
1959
- const assetOptions = useMemo(() => account === null ? [] : [{
1960
- id: account.id,
1961
- symbol: account.available.currency,
1962
- availableDisplay: formatMoney(account.available, { grammar: "code" })
1963
- }], [account]);
2064
+ const assetSelections = useMemo(() => assetSelectionsFromAccount$1(account), [account]);
2065
+ const assetOptions = useMemo(() => assetSelections.map((selection) => selection.option), [assetSelections]);
1964
2066
  const [selectedAssetOption, selectAsset] = useSelectedOption(assetOptions);
1965
- const selectedAssetMoney = selectedAssetOption !== null && account !== null ? account.available : null;
2067
+ const selectedAssetMoney = useMemo(() => assetSelections.find((selection) => selection.option.id === selectedAssetOption?.id)?.money ?? null, [assetSelections, selectedAssetOption]);
1966
2068
  const [amountText, setAmountText] = useState("");
1967
- 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]);
1968
2070
  const parsedMoney = parsed !== null && !isMoneyParseError(parsed) ? parsed : null;
1969
2071
  const amountError = parsed !== null && isMoneyParseError(parsed) ? parsed.reason : null;
1970
2072
  const recipient = useRecipientResolution(client, JSON.stringify([
@@ -1976,7 +2078,7 @@ function useSendMoney(input) {
1976
2078
  const personalPay = useCapxulPay();
1977
2079
  const organizationPay = useCapxulOrganizationPay(orgId);
1978
2080
  const pay = actor.kind === "personal" ? personalPay : organizationPay;
1979
- const blockedReason = signerStatus !== "ready" ? "signer-not-ready" : actor.kind === "organization" && selectedSource === null ? "no-source" : selectedAssetOption === null ? "no-asset" : parsedMoney === null ? "amount-invalid" : resolvedReference === null ? "recipient-unresolved" : null;
2081
+ const blockedReason = signerStatus !== "ready" ? "signer-not-ready" : actor.kind === "organization" && selectedSource === null ? "no-source" : selectedAssetOption === null ? "no-asset" : parsedMoney === null ? "amount-invalid" : resolvedReference === null ? "recipient-unresolved" : writeNamesAsset() ? null : "asset-unnamed";
1980
2082
  const submitGate = useRef(false);
1981
2083
  const submit = useCallback(() => {
1982
2084
  if (blockedReason !== null || submitGate.current) return;
@@ -2063,7 +2165,7 @@ function useSendMoneyContext() {
2063
2165
  if (engine === null) throw new Error("CapxulSendMoney parts must be used inside <CapxulSendMoney>");
2064
2166
  return engine;
2065
2167
  }
2066
- function Root$4({ actor, onSent, onFailed, children }) {
2168
+ function Root$5({ actor, onSent, onFailed, children }) {
2067
2169
  const engine = useSendMoney({
2068
2170
  actor,
2069
2171
  onSent,
@@ -2090,7 +2192,7 @@ function Actions$1({ children }) {
2090
2192
  return /* @__PURE__ */ jsx(Fragment, { children: children(useSendMoneyContext().actions) });
2091
2193
  }
2092
2194
  /** The compound shape the app's own design system already uses (`StatusTabs`). */
2093
- const CapxulSendMoney = Object.assign(Root$4, {
2195
+ const CapxulSendMoney = Object.assign(Root$5, {
2094
2196
  Source,
2095
2197
  Asset: Asset$1,
2096
2198
  Amount,
@@ -2180,7 +2282,7 @@ function useOrgMemberContext() {
2180
2282
  if (state === null) throw new Error("CapxulOrgMember parts must be used inside <CapxulOrgMember>");
2181
2283
  return state;
2182
2284
  }
2183
- function Root$3({ orgId, children }) {
2285
+ function Root$4({ orgId, children }) {
2184
2286
  const state = useOrgMember(orgId);
2185
2287
  return /* @__PURE__ */ jsx(OrgMemberContext.Provider, {
2186
2288
  value: state,
@@ -2200,7 +2302,7 @@ function Standing({ children }) {
2200
2302
  return /* @__PURE__ */ jsx(Fragment, { children: children(useOrgMemberContext().standing) });
2201
2303
  }
2202
2304
  /** The compound shape the app's own design system already uses (`StatusTabs`). */
2203
- const CapxulOrgMember = Object.assign(Root$3, {
2305
+ const CapxulOrgMember = Object.assign(Root$4, {
2204
2306
  Can,
2205
2307
  Standing
2206
2308
  });
@@ -2275,6 +2377,16 @@ function decidePhase(input) {
2275
2377
  if (input.orgLaneSettled && input.destination !== null) return "redirect";
2276
2378
  return "checking";
2277
2379
  }
2380
+ /**
2381
+ * Which probe produced the surfaced error — the SAME precedence `error` itself
2382
+ * is chosen by, written once so the code and its source can never disagree.
2383
+ */
2384
+ function probeOf(session, membership, lifecycle) {
2385
+ if (session !== null) return "session";
2386
+ if (membership !== null) return "membership";
2387
+ if (lifecycle !== null) return "lifecycle";
2388
+ return null;
2389
+ }
2278
2390
  /** The engine — all the logic. NOT exported from the package barrel. */
2279
2391
  function useDashboardAccess(scope) {
2280
2392
  const client = useCapxulClientOrNull();
@@ -2311,6 +2423,7 @@ function useDashboardAccess(scope) {
2311
2423
  lifecycle.refetch
2312
2424
  ]);
2313
2425
  const error = sessionError ?? membershipError ?? lifecycleError;
2426
+ const probe = probeOf(sessionError, membershipError, lifecycleError);
2314
2427
  const phase = decidePhase({
2315
2428
  error,
2316
2429
  settled: client !== null && identity !== null && !isRestoring$1(identity) && membershipsSettled && lifecycleSettled,
@@ -2321,11 +2434,13 @@ function useDashboardAccess(scope) {
2321
2434
  return useMemo(() => ({
2322
2435
  phase,
2323
2436
  error,
2437
+ probe,
2324
2438
  destination,
2325
2439
  retry
2326
2440
  }), [
2327
2441
  phase,
2328
2442
  error,
2443
+ probe,
2329
2444
  destination,
2330
2445
  retry
2331
2446
  ]);
@@ -2347,7 +2462,7 @@ function usePart(phase) {
2347
2462
  }, [placed, phase]);
2348
2463
  return state.phase === phase ? state : null;
2349
2464
  }
2350
- function Root$2({ scope, children }) {
2465
+ function Root$3({ scope, children }) {
2351
2466
  const state = useDashboardAccess(scope);
2352
2467
  const [placed] = useState(() => /* @__PURE__ */ new Set());
2353
2468
  const value = useMemo(() => ({
@@ -2365,17 +2480,24 @@ function Root$2({ scope, children }) {
2365
2480
  });
2366
2481
  }
2367
2482
  /** Hands nothing — today's app renders `null` here, exactly as it does now. */
2368
- function Checking({ children }) {
2483
+ function Checking$1({ children }) {
2369
2484
  return usePart("checking") === null ? null : /* @__PURE__ */ jsx(Fragment, { children });
2370
2485
  }
2371
2486
  function DashboardError({ children }) {
2372
2487
  const state = usePart("error");
2373
- if (state === null || state.error === null) return null;
2488
+ if (state === null || state.error === null || state.probe === null) return null;
2374
2489
  return /* @__PURE__ */ jsx(Fragment, { children: children({
2375
2490
  error: state.error,
2491
+ probe: state.probe,
2376
2492
  retry: state.retry
2377
2493
  }) });
2378
2494
  }
2495
+ /**
2496
+ * Hands the destination and draws nothing. Reaching this part is a REQUEST to
2497
+ * navigate, never an arrival: the app may cancel or supersede it, so nothing
2498
+ * here — and nothing a consumer hangs off it — may claim a completed redirect
2499
+ * (#1963 R05-1). The destination page's own normalized pageview proves arrival.
2500
+ */
2379
2501
  function Redirect({ children }) {
2380
2502
  const state = usePart("redirect");
2381
2503
  if (state === null || state.destination === null) return null;
@@ -2385,19 +2507,14 @@ function Granted({ children }) {
2385
2507
  return usePart("granted") === null ? null : /* @__PURE__ */ jsx(Fragment, { children });
2386
2508
  }
2387
2509
  /** The compound shape the app's own design system already uses (`StatusTabs`). */
2388
- const CapxulDashboardAccess = Object.assign(Root$2, {
2389
- Checking,
2510
+ const CapxulDashboardAccess = Object.assign(Root$3, {
2511
+ Checking: Checking$1,
2390
2512
  Error: DashboardError,
2391
2513
  Redirect,
2392
2514
  Granted
2393
2515
  });
2394
2516
  //#endregion
2395
2517
  //#region src/headless/payroll/money.ts
2396
- /** `{ value: "12.34", decimals: 6 }` → `12340000n`. */
2397
- function toMinorUnits(money) {
2398
- const [whole = "0", fraction = ""] = money.value.split(".");
2399
- return BigInt(`${whole}${fraction.padEnd(money.decimals, "0")}`);
2400
- }
2401
2518
  const TYPED_AMOUNT = /^(-?)(\d+)(?:\.(\d+))?$/;
2402
2519
  /**
2403
2520
  * Σ of the employer's TYPED amounts on a saved group — a DISPLAY total.
@@ -2430,6 +2547,17 @@ function fromMinorUnits(raw, asset) {
2430
2547
  decimals: asset.decimals
2431
2548
  };
2432
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
+ }
2433
2561
  //#endregion
2434
2562
  //#region src/headless/payroll/reads.ts
2435
2563
  function toRecipientOption(entry) {
@@ -2452,8 +2580,9 @@ function toRecipientOption(entry) {
2452
2580
  */
2453
2581
  function useOrgRoster(orgId) {
2454
2582
  const client = useCapxulClientOrNull();
2583
+ const identity = useCapxulIdentityOrNull();
2455
2584
  const query = useQuery({
2456
- queryKey: capxulKeys.contactsList(orgId ?? "pending", false),
2585
+ queryKey: accountKey(capxulKeys.contactsList(orgId ?? "pending", false), identity),
2457
2586
  queryFn: async () => unwrapCapxulResult(await scopedClient(client, CAPXUL_OPERATIONS.org.addressBook.list, orgId).addressBook.list({ includeHidden: false })),
2458
2587
  enabled: client !== null && orgId !== void 0
2459
2588
  });
@@ -2474,16 +2603,18 @@ function useOrgRoster(orgId) {
2474
2603
  }
2475
2604
  function usePayrollGroups(orgId) {
2476
2605
  const client = useCapxulClientOrNull();
2606
+ const identity = useCapxulIdentityOrNull();
2477
2607
  return useQuery({
2478
- queryKey: capxulKeys.payrollGroups(orgId),
2608
+ queryKey: accountKey(capxulKeys.payrollGroups(orgId), identity),
2479
2609
  queryFn: async () => unwrapCapxulResult(await scopedClient(client, CAPXUL_OPERATIONS.org.payroll.groups.list, orgId).payroll.groups.list()),
2480
2610
  enabled: client !== null && orgId !== void 0
2481
2611
  });
2482
2612
  }
2483
2613
  function usePayrollRuns(orgId) {
2484
2614
  const client = useCapxulClientOrNull();
2615
+ const identity = useCapxulIdentityOrNull();
2485
2616
  return useQuery({
2486
- queryKey: capxulKeys.payrollRuns(orgId),
2617
+ queryKey: accountKey(capxulKeys.payrollRuns(orgId), identity),
2487
2618
  queryFn: async () => unwrapCapxulResult(await scopedClient(client, CAPXUL_OPERATIONS.org.payroll.runs, orgId).payroll.runs()),
2488
2619
  enabled: client !== null && orgId !== void 0
2489
2620
  });
@@ -2498,29 +2629,34 @@ function usePayrollRuns(orgId) {
2498
2629
  */
2499
2630
  function useOrgTerms(orgId) {
2500
2631
  const client = useCapxulClientOrNull();
2632
+ const identity = useCapxulIdentityOrNull();
2501
2633
  return useQuery({
2502
- queryKey: capxulKeys.payrollTerms(orgId),
2634
+ queryKey: accountKey(capxulKeys.payrollTerms(orgId), identity),
2503
2635
  queryFn: async () => unwrapCapxulResult(await scopedClient(client, CAPXUL_OPERATIONS.org.payroll.terms, orgId).payroll.terms()),
2504
2636
  enabled: client !== null && orgId !== void 0
2505
2637
  });
2506
2638
  }
2507
2639
  function useOrgTreasury(orgId) {
2508
2640
  const client = useCapxulClientOrNull();
2641
+ const identity = useCapxulIdentityOrNull();
2509
2642
  return useQuery({
2510
- queryKey: capxulKeys.orgTreasury(orgId),
2643
+ queryKey: accountKey(capxulKeys.orgTreasury(orgId), identity),
2511
2644
  queryFn: async () => unwrapCapxulResult(await scopedClient(client, CAPXUL_OPERATIONS.org.treasury, orgId).treasury()),
2512
2645
  enabled: client !== null && orgId !== void 0
2513
2646
  });
2514
2647
  }
2515
2648
  /**
2516
2649
  * The caller's own standing, read for its Budgets alone (ruling Q2 grades
2517
- * them). Keyed as `CapxulOrgMember` keys it, so a payroll screen inside a
2518
- * `.Can` wrapper reads `org(id).me()` once.
2650
+ * them). Keyed as `CapxulOrgMember` keys it — Organization AND acting account
2651
+ * (#2007 R19) — so a payroll screen inside a `.Can` wrapper still reads
2652
+ * `org(id).me()` once, and an account replacement is a miss for both of them
2653
+ * rather than the previous person's Budgets for both.
2519
2654
  */
2520
2655
  function useOrgMe(orgId) {
2521
2656
  const client = useCapxulClientOrNull();
2657
+ const identity = useCapxulIdentityOrNull();
2522
2658
  return useQuery({
2523
- queryKey: capxulKeys.orgMe(orgId),
2659
+ queryKey: capxulKeys.orgMe(orgId, actingAccount(identity)),
2524
2660
  queryFn: async () => unwrapCapxulResult(await scopedClient(client, CAPXUL_OPERATIONS.org.me, orgId).me()),
2525
2661
  enabled: client !== null && orgId !== void 0
2526
2662
  });
@@ -2538,7 +2674,15 @@ function scopedClient(client, method, orgId) {
2538
2674
  //#endregion
2539
2675
  //#region src/headless/payroll/use-payroll.ts
2540
2676
  /** Money reads the same way everywhere on this screen: amount then code. */
2541
- const GRAMMAR$1 = { grammar: "code" };
2677
+ const GRAMMAR = { grammar: "code" };
2678
+ /**
2679
+ * One captured snapshot, or nothing — never a fabricated empty options object,
2680
+ * and never a DOM event a screen wired straight to `onClick`.
2681
+ */
2682
+ function carry(options) {
2683
+ const attempt = toInvocationOptions(options);
2684
+ return attempt === void 0 ? {} : { options: attempt };
2685
+ }
2542
2686
  function toPayrollGroupRow(group) {
2543
2687
  const currency = new Set(group.members.map((member) => member.currency)).size === 1 ? group.members[0]?.currency : void 0;
2544
2688
  return {
@@ -2546,7 +2690,7 @@ function toPayrollGroupRow(group) {
2546
2690
  name: group.name,
2547
2691
  tone: group.tone,
2548
2692
  memberCount: group.members.length,
2549
- 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),
2550
2694
  members: group.members.map((member) => ({
2551
2695
  recipientId: member.partyId,
2552
2696
  amount: member.amount,
@@ -2578,16 +2722,17 @@ function usePayroll(orgId) {
2578
2722
  const write = useMutation({
2579
2723
  mutationFn: async (command) => {
2580
2724
  const payroll = scopedClient(client, CAPXUL_OPERATIONS.org.payroll.groups[command.kind], orgId).payroll;
2725
+ const controls = toInvocationControls(command.options);
2581
2726
  if (command.kind === "remove") {
2582
- unwrapCapxulResult(await payroll.groups.remove(command.id));
2727
+ unwrapCapxulResult(await payroll.groups.remove(command.id, controls));
2583
2728
  return null;
2584
2729
  }
2585
- return unwrapCapxulResult(await payroll.groups.save(command.input));
2730
+ return unwrapCapxulResult(await payroll.groups.save(command.input, controls));
2586
2731
  },
2587
2732
  onSettled: invalidate
2588
2733
  });
2589
2734
  const mutateAsync = write.mutateAsync;
2590
- const save = useCallback(async (input) => {
2735
+ const save = useCallback(async (input, options) => {
2591
2736
  const refusal = invalidGroup(input);
2592
2737
  if (refusal !== null) return {
2593
2738
  ok: false,
@@ -2596,7 +2741,8 @@ function usePayroll(orgId) {
2596
2741
  try {
2597
2742
  const saved = await mutateAsync({
2598
2743
  kind: "save",
2599
- input
2744
+ input,
2745
+ ...carry(options)
2600
2746
  });
2601
2747
  return saved === null ? {
2602
2748
  ok: false,
@@ -2612,11 +2758,12 @@ function usePayroll(orgId) {
2612
2758
  };
2613
2759
  }
2614
2760
  }, [mutateAsync]);
2615
- const remove = useCallback(async (id) => {
2761
+ const remove = useCallback(async (id, options) => {
2616
2762
  try {
2617
2763
  await mutateAsync({
2618
2764
  kind: "remove",
2619
- id
2765
+ id,
2766
+ ...carry(options)
2620
2767
  });
2621
2768
  return {
2622
2769
  ok: true,
@@ -2633,8 +2780,8 @@ function usePayroll(orgId) {
2633
2780
  const started = orgId !== void 0;
2634
2781
  return {
2635
2782
  summary: {
2636
- walletBalanceDisplay: treasury.data === void 0 ? null : formatMoney(treasury.data.available, GRAMMAR$1),
2637
- 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(", "),
2638
2785
  isLoading: started && (treasury.data === void 0 && treasury.error === null || runs.data === void 0 && runs.error === null),
2639
2786
  error: treasury.error ?? runs.error
2640
2787
  },
@@ -2667,7 +2814,7 @@ function usePayrollContext() {
2667
2814
  if (engine === null) throw new Error("CapxulPayroll parts must be used inside <CapxulPayroll>");
2668
2815
  return engine;
2669
2816
  }
2670
- function Root$1({ orgId, children }) {
2817
+ function Root$2({ orgId, children }) {
2671
2818
  const engine = usePayroll(orgId);
2672
2819
  return /* @__PURE__ */ jsx(PayrollContext.Provider, {
2673
2820
  value: engine,
@@ -2685,7 +2832,7 @@ function Groups({ children }) {
2685
2832
  * There is NO `.Runs` part (ruling Q6): no shipped region has the right shape
2686
2833
  * for a run list, and a part without a production caller may not ship.
2687
2834
  */
2688
- const CapxulPayroll = Object.assign(Root$1, {
2835
+ const CapxulPayroll = Object.assign(Root$2, {
2689
2836
  Summary: Summary$1,
2690
2837
  Groups
2691
2838
  });
@@ -2695,8 +2842,14 @@ function record(value) {
2695
2842
  if (typeof value !== "object" || value === null || Array.isArray(value)) throw Errors.invalidInput("payrollDraft", "Invalid run draft");
2696
2843
  return value;
2697
2844
  }
2845
+ /**
2846
+ * A draft-scoped opaque key. `/` is in the class so `asset.id` can be the
2847
+ * canonical CAIP-19 `AssetId` (`eip155:<chainId>/erc20:<address>`): the draft
2848
+ * records WHICH asset was authorized, and a symbol is not an identity
2849
+ * (ADR-0028 R3). The field never reaches the backend — only `input` is sent.
2850
+ */
2698
2851
  function identifier(value) {
2699
- if (typeof value !== "string" || !/^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u.test(value)) throw Errors.invalidInput("payrollDraft", "Invalid draft identifier");
2852
+ if (typeof value !== "string" || !/^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$/u.test(value)) throw Errors.invalidInput("payrollDraft", "Invalid draft identifier");
2700
2853
  return value;
2701
2854
  }
2702
2855
  function minorAmount(value) {
@@ -2712,8 +2865,9 @@ function decodeDraft(value, scope) {
2712
2865
  const items = input.items.map((raw) => {
2713
2866
  const item = record(raw);
2714
2867
  const to = record(item.to);
2868
+ const amount = record(item.amount);
2715
2869
  const partyId = toPartyId(identifier(item.partyId));
2716
- 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");
2717
2871
  const net = minorAmount(item.net);
2718
2872
  return {
2719
2873
  to: {
@@ -2721,12 +2875,20 @@ function decodeDraft(value, scope) {
2721
2875
  partyId
2722
2876
  },
2723
2877
  partyId,
2878
+ amount: {
2879
+ asset: requireAsset(amount.asset).assetId,
2880
+ value: amount.value
2881
+ },
2724
2882
  gross: net,
2725
2883
  net,
2884
+ currency: item.currency,
2885
+ decimals: item.decimals,
2726
2886
  adjustments: []
2727
2887
  };
2728
2888
  });
2729
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");
2730
2892
  return {
2731
2893
  version: 1,
2732
2894
  scope,
@@ -2737,6 +2899,7 @@ function decodeDraft(value, scope) {
2737
2899
  },
2738
2900
  input: {
2739
2901
  permissionId: identifier(input.permissionId),
2902
+ asset: exactAsset.assetId,
2740
2903
  period: {
2741
2904
  start: period.start,
2742
2905
  end: period.start
@@ -2940,7 +3103,6 @@ function termsSeeds(terms, asset, period) {
2940
3103
  }
2941
3104
  //#endregion
2942
3105
  //#region src/headless/payroll/use-payroll-run.ts
2943
- const GRAMMAR = { grammar: "code" };
2944
3106
  /**
2945
3107
  * The period this composer composes: an INSTANT run (ruling Q3). `authorizeRun`
2946
3108
  * stamps `start === end === runAt` at submit, and the payslip's period renders
@@ -2964,6 +3126,32 @@ const INSTANT_RUN_PERIOD = {
2964
3126
  function amountText(amounts, seeds, recipientId) {
2965
3127
  return amounts[recipientId]?.value ?? seeds.get(recipientId) ?? "";
2966
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
+ }
2967
3155
  /**
2968
3156
  * WHICH run the composer is composing, by MEANING rather than by object shape.
2969
3157
  *
@@ -2995,6 +3183,33 @@ function initialAmounts(prefill) {
2995
3183
  };
2996
3184
  return seeded;
2997
3185
  }
3186
+ /**
3187
+ * One `.Asset` option per position the treasury Account holds, taken from
3188
+ * `account.balances` — never from `account.available`, which is the Account's
3189
+ * fiat VALUATION of those positions (ADR-0028 R5/R6). The option is keyed on
3190
+ * the ASSET id: several positions share one account id, and a symbol is display
3191
+ * only (ADR-0028 R3). Every row is listed, so a real multi-position treasury
3192
+ * still offers its holdings.
3193
+ */
3194
+ function assetSelectionsFromAccount(account) {
3195
+ if (account === null) return [];
3196
+ return account.balances.map((position) => {
3197
+ return {
3198
+ option: {
3199
+ id: position.asset,
3200
+ symbol: position.symbol,
3201
+ availableDisplay: formatAssetAmount({
3202
+ asset: position.asset,
3203
+ value: position.available
3204
+ })
3205
+ },
3206
+ money: {
3207
+ asset: position.asset,
3208
+ value: position.available
3209
+ }
3210
+ };
3211
+ });
3212
+ }
2998
3213
  function usePayrollRun(input) {
2999
3214
  const { orgId, prefill, onRun, onFailed } = input;
3000
3215
  const client = useCapxulClientOrNull();
@@ -3008,17 +3223,13 @@ function usePayrollRun(input) {
3008
3223
  const terms = useOrgTerms(orgId);
3009
3224
  const [selected, setSelected] = useState(() => prefill?.recipientIds ?? []);
3010
3225
  const [amounts, setAmounts] = useState(() => initialAmounts(prefill));
3011
- const restoreCurrency = treasury.data?.available.currency;
3012
3226
  const { draftScope, currentScope, draft, draftError, restorationPending, activateDraft, restoreDraft, setDraft, recovery, draftBlocked } = useRunDraft(client, orgId, useCallback((restored) => {
3013
3227
  setSelected(restored.input.items.map((item) => item.partyId));
3014
- if (restoreCurrency !== void 0 && restoreCurrency === restored.asset.currency) setAmounts(Object.fromEntries(restored.input.items.map((item) => [item.partyId, {
3015
- value: fromMinorUnits(BigInt(item.net), {
3016
- currency: restoreCurrency,
3017
- decimals: restored.asset.decimals
3018
- }).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,
3019
3230
  prefilled: false
3020
3231
  }])));
3021
- }, [restoreCurrency]));
3232
+ }, []));
3022
3233
  const frozen = draft !== null;
3023
3234
  const composerKey = composerIdentity(orgId, prefill, draftScope, frozen);
3024
3235
  const [seededKey, setSeededKey] = useState(composerKey);
@@ -3058,11 +3269,8 @@ function usePayrollRun(input) {
3058
3269
  draftError
3059
3270
  ]);
3060
3271
  const account = treasury.data ?? null;
3061
- const assetOptions = useMemo(() => account === null ? [] : [{
3062
- id: account.id,
3063
- symbol: account.available.currency,
3064
- availableDisplay: formatMoney(account.available, GRAMMAR)
3065
- }], [account]);
3272
+ const assetSelections = useMemo(() => assetSelectionsFromAccount(account), [account]);
3273
+ const assetOptions = useMemo(() => assetSelections.map((selection) => selection.option), [assetSelections]);
3066
3274
  const [selectedAssetId, setSelectedAssetId] = useState(null);
3067
3275
  useEffect(() => {
3068
3276
  if (!assetOptions.some((option) => option.id === selectedAssetId)) setSelectedAssetId(assetOptions[0]?.id ?? null);
@@ -3071,7 +3279,7 @@ function usePayrollRun(input) {
3071
3279
  const selectAsset = useCallback((id) => {
3072
3280
  if (!submitGate.current && !frozen) setSelectedAssetId((current) => assetOptions.some((option) => option.id === id) ? id : current);
3073
3281
  }, [assetOptions, frozen]);
3074
- const assetMoney = selectedAsset !== null && account !== null ? account.available : null;
3282
+ const assetMoney = assetSelections.find((selection) => selection.option.id === selectedAsset?.id)?.money ?? null;
3075
3283
  const named = useMemo(() => new Map(roster.options.map((option) => [option.id, option])), [roster.options]);
3076
3284
  const refs = roster.refs;
3077
3285
  const payableRefs = useMemo(() => {
@@ -3083,12 +3291,19 @@ function usePayrollRun(input) {
3083
3291
  }
3084
3292
  return resolved;
3085
3293
  }, [refs, selected]);
3086
- 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]);
3087
3302
  const entries = useMemo(() => {
3088
3303
  if (assetMoney === null) return [];
3089
3304
  return selected.map((recipientId) => {
3090
3305
  const value = amountText(amounts, seeds, recipientId);
3091
- const parsed = value.trim() === "" ? null : parseMoney(value, assetMoney);
3306
+ const parsed = value.trim() === "" ? null : parseAssetAmount(value, requireAsset(assetMoney.asset));
3092
3307
  return {
3093
3308
  recipientId,
3094
3309
  name: named.get(recipientId)?.name ?? recipientId,
@@ -3096,7 +3311,7 @@ function usePayrollRun(input) {
3096
3311
  change: (text) => change(recipientId, text),
3097
3312
  error: parsed !== null && isMoneyParseError(parsed) ? parsed.reason : null,
3098
3313
  prefilled: amounts[recipientId]?.prefilled ?? seeds.has(recipientId),
3099
- currency: assetMoney.currency
3314
+ currency: requireAsset(assetMoney.asset).symbol
3100
3315
  };
3101
3316
  });
3102
3317
  }, [
@@ -3111,7 +3326,7 @@ function usePayrollRun(input) {
3111
3326
  if (assetMoney === null || selected.length === 0) return null;
3112
3327
  const money = [];
3113
3328
  for (const recipientId of selected) {
3114
- const parsed = parseMoney(amountText(amounts, seeds, recipientId), assetMoney);
3329
+ const parsed = parseAssetAmount(amountText(amounts, seeds, recipientId), requireAsset(assetMoney.asset));
3115
3330
  if (isMoneyParseError(parsed)) return null;
3116
3331
  money.push(parsed);
3117
3332
  }
@@ -3122,23 +3337,33 @@ function usePayrollRun(input) {
3122
3337
  seeds,
3123
3338
  selected
3124
3339
  ]);
3125
- const total = useMemo(() => parsedAmounts === null || assetMoney === null ? null : fromMinorUnits(parsedAmounts.reduce((sum, money) => sum + toMinorUnits(money), 0n), assetMoney), [assetMoney, parsedAmounts]);
3126
- const availableRaw = assetMoney === null ? null : toMinorUnits(assetMoney);
3127
- 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));
3128
3349
  const funds = availableRaw === null || totalRaw === null ? "unknown" : totalRaw > availableRaw ? "insufficient" : "available";
3129
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();
3130
3353
  const blockedReason = firstFalseFact({
3131
3354
  signerReady: signerStatus === "ready",
3132
3355
  ...runAuthorityFacts(draft, budgets, selectedAsset, assetMoney, funds),
3133
3356
  recipientCount: payableRefs === null ? 0 : selected.length,
3134
- amountsParse: total !== null
3357
+ amountsParse: total !== null && hasRequiredCompensation(frozen, writerNamesSelectedAsset, selected, compensationTerms),
3358
+ writeNamesAsset: writerNamesSelectedAsset
3135
3359
  });
3136
3360
  const authorize = useMutation({
3137
- mutationFn: async (candidate) => {
3361
+ mutationFn: async ({ candidate, options }) => {
3138
3362
  if (candidate.scope !== currentScope.current) throw Errors.notAuthenticated();
3139
3363
  return executeRunDraft(candidate, async (persisted, onRequestKey, onResolved) => {
3140
3364
  if (candidate.scope === currentScope.current) setDraft(persisted);
3141
3365
  return unwrapCapxulResult(await scopedClient(client, CAPXUL_OPERATIONS.org.payroll.authorizeRun, orgId).payroll.authorizeRun(persisted.input, {
3366
+ ...toInvocationControls(options),
3142
3367
  onResolved,
3143
3368
  onRequestKey: (key) => {
3144
3369
  if (candidate.scope !== currentScope.current) throw Errors.notAuthenticated();
@@ -3147,56 +3372,54 @@ function usePayrollRun(input) {
3147
3372
  }));
3148
3373
  });
3149
3374
  },
3150
- onSettled: (_run, _error, candidate) => {
3375
+ onSettled: (_run, _error, { candidate }) => {
3151
3376
  queryClient.invalidateQueries({ queryKey: capxulKeys.payrollRuns(orgId) });
3152
3377
  if (candidate.scope === currentScope.current) restoreDraft(candidate.scope);
3153
3378
  }
3154
3379
  });
3155
- const totalDisplay = total === null ? null : formatMoney(total, GRAMMAR);
3380
+ const totalDisplay = total === null ? null : formatAssetAmount(total);
3156
3381
  const authorizeMutate = authorize.mutate;
3157
- const submit = useCallback(() => {
3382
+ const submit = useCallback((options) => {
3383
+ const attempt = toInvocationOptions(options);
3158
3384
  const permissionId = budgets[0]?.id;
3159
3385
  if (blockedReason !== null || submitGate.current || draftBlocked || draftScope === null || assetMoney === null || selectedAsset === null) return;
3160
3386
  if (permissionId === void 0 || totalDisplay === null || parsedAmounts === null) return;
3161
3387
  if (payableRefs === null) return;
3162
3388
  const recipientCount = selected.length;
3163
- const items = [];
3164
- for (const [index, recipientId] of selected.entries()) {
3165
- const to = payableRefs[index];
3166
- const parsed = parsedAmounts[index];
3167
- if (to === void 0 || parsed === void 0) return;
3168
- const raw = toMinorUnits(parsed).toString();
3169
- items.push({
3170
- to: {
3171
- kind: "party",
3172
- partyId: toPartyId(recipientId)
3173
- },
3174
- partyId: recipientId,
3175
- gross: raw,
3176
- net: raw,
3177
- adjustments: []
3389
+ let candidate;
3390
+ if (frozen) candidate = draft;
3391
+ else {
3392
+ const items = freshRunItems({
3393
+ selected,
3394
+ payableRefs,
3395
+ amounts: parsedAmounts,
3396
+ compensationTerms
3178
3397
  });
3179
- }
3180
- const runAt = Date.now();
3181
- const candidate = frozen ? draft : {
3182
- version: 1,
3183
- scope: draftScope,
3184
- asset: {
3185
- id: selectedAsset.id,
3186
- currency: assetMoney.currency,
3187
- decimals: assetMoney.decimals
3188
- },
3189
- input: {
3190
- permissionId,
3191
- period: {
3192
- start: runAt,
3193
- 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
3194
3406
  },
3195
- items
3196
- }
3197
- };
3407
+ input: {
3408
+ permissionId,
3409
+ asset: selectedAsset.id,
3410
+ period: {
3411
+ start: runAt,
3412
+ end: runAt
3413
+ },
3414
+ items
3415
+ }
3416
+ };
3417
+ }
3198
3418
  submitGate.current = true;
3199
- authorizeMutate(candidate, {
3419
+ authorizeMutate({
3420
+ candidate,
3421
+ ...attempt === void 0 ? {} : { options: attempt }
3422
+ }, {
3200
3423
  onSuccess: (run) => {
3201
3424
  if (candidate.scope === currentScope.current) onRun({
3202
3425
  id: run.id,
@@ -3223,6 +3446,7 @@ function usePayrollRun(input) {
3223
3446
  selectedAsset,
3224
3447
  blockedReason,
3225
3448
  budgets,
3449
+ compensationTerms,
3226
3450
  onFailed,
3227
3451
  onRun,
3228
3452
  parsedAmounts,
@@ -3271,18 +3495,25 @@ function usePayrollRun(input) {
3271
3495
  };
3272
3496
  }
3273
3497
  function remainingBalanceDisplay(available, total, asset) {
3274
- 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;
3275
3506
  }
3276
3507
  /** Persisted replay keeps its authorized Budget and asset; a prior debit cannot block its own recovery. */
3277
3508
  function runAuthorityFacts(draft, budgets, asset, money, funds) {
3278
3509
  return {
3279
3510
  budgetCount: draft !== null && !budgets.some((budget) => budget.id === draft.input.permissionId) ? 0 : budgets.length,
3280
- 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),
3281
3512
  funds: draft?.input.requestKey === void 0 ? funds : "available"
3282
3513
  };
3283
3514
  }
3284
3515
  /**
3285
- * The seven facts, in the ONE order the contract fixes. Exactly one code comes
3516
+ * The facts, in the ONE order the contract fixes. Exactly one code comes
3286
3517
  * out, and it is always the first thing that is false — so two problems never
3287
3518
  * race to name the button.
3288
3519
  */
@@ -3293,7 +3524,8 @@ function firstFalseFact(facts) {
3293
3524
  if (!facts.hasAsset) return "no-asset";
3294
3525
  if (facts.recipientCount === 0) return "no-recipients";
3295
3526
  if (!facts.amountsParse) return "amount-invalid";
3296
- return facts.funds === "insufficient" ? "insufficient-funds" : null;
3527
+ if (facts.funds === "insufficient") return "insufficient-funds";
3528
+ return facts.writeNamesAsset ? null : "asset-unnamed";
3297
3529
  }
3298
3530
  //#endregion
3299
3531
  //#region src/headless/payroll/payroll-run.tsx
@@ -3303,7 +3535,7 @@ function usePayrollRunContext() {
3303
3535
  if (engine === null) throw new Error("CapxulPayrollRun parts must be used inside <CapxulPayrollRun>");
3304
3536
  return engine;
3305
3537
  }
3306
- function Root({ orgId, prefill, onRun, onFailed, children }) {
3538
+ function Root$1({ orgId, prefill, onRun, onFailed, children }) {
3307
3539
  const engine = usePayrollRun({
3308
3540
  orgId,
3309
3541
  prefill,
@@ -3333,7 +3565,7 @@ function Actions({ children }) {
3333
3565
  return /* @__PURE__ */ jsx(Fragment, { children: children(engine.actions) });
3334
3566
  }
3335
3567
  /** The compound shape the app's own design system already uses (`StatusTabs`). */
3336
- const CapxulPayrollRun = Object.assign(Root, {
3568
+ const CapxulPayrollRun = Object.assign(Root$1, {
3337
3569
  Recipients,
3338
3570
  Amounts,
3339
3571
  Asset,
@@ -3341,4 +3573,230 @@ const CapxulPayrollRun = Object.assign(Root, {
3341
3573
  Actions
3342
3574
  });
3343
3575
  //#endregion
3344
- export { CapxulActivity, CapxulAuthenticationController, 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 };
3576
+ //#region src/headless/client-connection/use-client-connection.ts
3577
+ function useClientConnection(input) {
3578
+ const [view, setView] = useState(null);
3579
+ const [pending, setPending] = useState(false);
3580
+ const [error, setError] = useState(null);
3581
+ const [resendReady, setResendReady] = useState(false);
3582
+ const identityRef = useRef({
3583
+ client: input.client,
3584
+ requestId: input.requestId
3585
+ });
3586
+ identityRef.current = {
3587
+ client: input.client,
3588
+ requestId: input.requestId
3589
+ };
3590
+ const belongsHere = useCallback((client, requestId) => identityRef.current.client === client && identityRef.current.requestId === requestId, []);
3591
+ const refresh = useCallback(async (signal) => {
3592
+ const { client, requestId } = identityRef.current;
3593
+ const result = await client.auth.clientRequests.read({ requestId }, signal === void 0 ? void 0 : { signal });
3594
+ if (signal?.aborted || !belongsHere(client, requestId)) return;
3595
+ if (result.ok) {
3596
+ setView(result.value);
3597
+ setError(null);
3598
+ return;
3599
+ }
3600
+ setError(result.error);
3601
+ }, [belongsHere]);
3602
+ useEffect(() => {
3603
+ setView(null);
3604
+ setError(null);
3605
+ setPending(false);
3606
+ setResendReady(false);
3607
+ const controller = new AbortController();
3608
+ refresh(controller.signal);
3609
+ return () => {
3610
+ controller.abort();
3611
+ };
3612
+ }, [
3613
+ input.client,
3614
+ input.requestId,
3615
+ refresh
3616
+ ]);
3617
+ useEffect(() => {
3618
+ if (view?.phase !== "otp_required") {
3619
+ setResendReady(false);
3620
+ return;
3621
+ }
3622
+ const remaining = view.resendAt - Date.now();
3623
+ if (remaining <= 0) {
3624
+ setResendReady(true);
3625
+ return;
3626
+ }
3627
+ setResendReady(false);
3628
+ const timer = setTimeout(() => setResendReady(true), remaining);
3629
+ return () => clearTimeout(timer);
3630
+ }, [view]);
3631
+ const run = useCallback(async (work) => {
3632
+ const { client, requestId } = identityRef.current;
3633
+ setPending(true);
3634
+ try {
3635
+ const result = await work();
3636
+ if (!belongsHere(client, requestId)) return;
3637
+ if (!result.ok) {
3638
+ setError(result.error);
3639
+ return;
3640
+ }
3641
+ await refresh();
3642
+ } finally {
3643
+ if (belongsHere(client, requestId)) setPending(false);
3644
+ }
3645
+ }, [belongsHere, refresh]);
3646
+ return useMemo(() => {
3647
+ if (error !== null && (view === null || view.phase !== "refused")) return {
3648
+ phase: "failure",
3649
+ otp: null,
3650
+ consent: null,
3651
+ complete: null,
3652
+ failure: {
3653
+ code: error.code,
3654
+ retry: () => refresh(),
3655
+ cancel: async () => {
3656
+ if (view === null) return;
3657
+ const { client, requestId } = identityRef.current;
3658
+ await run(() => client.auth.clientRequests.cancel({
3659
+ requestId,
3660
+ revision: view.revision
3661
+ }));
3662
+ }
3663
+ }
3664
+ };
3665
+ if (view === null) return {
3666
+ phase: "checking",
3667
+ otp: null,
3668
+ consent: null,
3669
+ complete: null,
3670
+ failure: null
3671
+ };
3672
+ if (view.phase === "otp_required") return {
3673
+ phase: view.phase,
3674
+ otp: {
3675
+ email: view.email,
3676
+ pending,
3677
+ canResend: resendReady,
3678
+ verifyCode: async (code) => {
3679
+ const { client, requestId } = identityRef.current;
3680
+ await run(() => client.auth.clientRequests.verifyOtp({
3681
+ requestId,
3682
+ code
3683
+ }));
3684
+ },
3685
+ resend: async () => {
3686
+ const { client, requestId } = identityRef.current;
3687
+ await run(() => client.auth.clientRequests.sendOtp({ requestId }));
3688
+ }
3689
+ },
3690
+ consent: null,
3691
+ complete: null,
3692
+ failure: null
3693
+ };
3694
+ if (view.phase === "review") return {
3695
+ phase: view.phase,
3696
+ otp: null,
3697
+ consent: {
3698
+ verifiedEmail: view.email,
3699
+ clientLabel: view.clientLabel,
3700
+ scopeCodes: view.scopes,
3701
+ purpose: view.intent.mode,
3702
+ setupState: view.setupState,
3703
+ pending,
3704
+ accept: async () => {
3705
+ const { client, requestId } = identityRef.current;
3706
+ await run(() => client.auth.clientRequests.accept({
3707
+ requestId,
3708
+ revision: view.revision
3709
+ }));
3710
+ },
3711
+ cancel: async () => {
3712
+ const { client, requestId } = identityRef.current;
3713
+ await run(() => client.auth.clientRequests.cancel({
3714
+ requestId,
3715
+ revision: view.revision
3716
+ }));
3717
+ }
3718
+ },
3719
+ complete: null,
3720
+ failure: null
3721
+ };
3722
+ if (view.phase === "connected") return {
3723
+ phase: view.phase,
3724
+ otp: null,
3725
+ consent: null,
3726
+ complete: { outcome: "connected" },
3727
+ failure: null
3728
+ };
3729
+ if (view.phase === "refused") return {
3730
+ phase: view.phase,
3731
+ otp: null,
3732
+ consent: null,
3733
+ complete: null,
3734
+ failure: {
3735
+ code: view.code,
3736
+ ...view.field === void 0 ? {} : { field: view.field },
3737
+ retry: () => refresh(),
3738
+ cancel: async () => void 0
3739
+ }
3740
+ };
3741
+ return {
3742
+ phase: view.phase,
3743
+ otp: null,
3744
+ consent: null,
3745
+ complete: null,
3746
+ failure: null
3747
+ };
3748
+ }, [
3749
+ error,
3750
+ pending,
3751
+ refresh,
3752
+ resendReady,
3753
+ run,
3754
+ view
3755
+ ]);
3756
+ }
3757
+ //#endregion
3758
+ //#region src/headless/client-connection/client-connection.tsx
3759
+ const ConnectionContext = createContext(null);
3760
+ function useConnection() {
3761
+ const engine = useContext(ConnectionContext);
3762
+ if (engine === null) throw new Error("CapxulClientConnection parts must be used inside <CapxulClientConnection>");
3763
+ return engine;
3764
+ }
3765
+ function Root({ client, requestId, children }) {
3766
+ const engine = useClientConnection({
3767
+ client,
3768
+ requestId
3769
+ });
3770
+ return /* @__PURE__ */ jsx(ConnectionContext.Provider, {
3771
+ value: engine,
3772
+ children
3773
+ });
3774
+ }
3775
+ function Checking({ children }) {
3776
+ return useConnection().phase === "checking" ? /* @__PURE__ */ jsx(Fragment, { children }) : null;
3777
+ }
3778
+ function Otp({ children }) {
3779
+ const otp = useConnection().otp;
3780
+ return otp === null ? null : /* @__PURE__ */ jsx(Fragment, { children: children(otp) });
3781
+ }
3782
+ function Consent({ children }) {
3783
+ const consent = useConnection().consent;
3784
+ return consent === null ? null : /* @__PURE__ */ jsx(Fragment, { children: children(consent) });
3785
+ }
3786
+ function Complete({ children }) {
3787
+ const complete = useConnection().complete;
3788
+ return complete === null ? null : /* @__PURE__ */ jsx(Fragment, { children: children(complete) });
3789
+ }
3790
+ function Failure({ children }) {
3791
+ const failure = useConnection().failure;
3792
+ return failure === null ? null : /* @__PURE__ */ jsx(Fragment, { children: children(failure) });
3793
+ }
3794
+ const CapxulClientConnection = Object.assign(Root, {
3795
+ Checking,
3796
+ Otp,
3797
+ Consent,
3798
+ Complete,
3799
+ Failure
3800
+ });
3801
+ //#endregion
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 };