@capxul/sdk-react 4.20.0-beta.2 → 4.20.0-beta.21

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 { _ 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-DXmEHrnw.mjs";
2
+ import { _ as useCapxul, a as useCapxulAuth, c as useCapxulIdentityOrNull, d as toInvocationControls, f as toInvocationOptions, g as useCapxulClientOrNull, h as capxulKeys, i as entered, l as useCapxulSend, m as actingAccount, n as CapxulOnboardingController, o as useCapxulDestination, p as accountKey, r as CapxulProvider, s as useCapxulIdentity, t as CapxulAuthenticationController, u as useCapxulTransitions } from "./controllers-BV3e8WD3.mjs";
3
3
  import { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
4
4
  import { useInfiniteQuery, useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
5
- 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
  /**
@@ -538,7 +538,7 @@ function useCapxulCreateOrg() {
538
538
  const queryClient = useQueryClient();
539
539
  return useMutation({
540
540
  mutationFn: async (input) => {
541
- return unwrapCapxulResult(await requireBootstrappedClient(client, "createOrg").createOrg(input));
541
+ return unwrapCapxulResult(await requireBootstrappedClient(client, CAPXUL_OPERATIONS.onboarding.completeOrganization).onboarding.completeOrganization(input));
542
542
  },
543
543
  onSuccess: async () => {
544
544
  await queryClient.invalidateQueries({ queryKey: capxulKeys.orgs });
@@ -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";
@@ -1079,7 +1063,7 @@ function useContactsContext() {
1079
1063
  if (engine === null) throw new Error("CapxulContacts parts must be used inside <CapxulContacts>");
1080
1064
  return engine;
1081
1065
  }
1082
- function Root$6({ actor, onAdded, onFailed, children }) {
1066
+ function Root$7({ actor, onAdded, onFailed, children }) {
1083
1067
  const engine = useContacts({
1084
1068
  actor,
1085
1069
  onAdded,
@@ -1103,7 +1087,7 @@ function Add({ children }) {
1103
1087
  return /* @__PURE__ */ jsx(Fragment, { children: children(useContactsContext().add) });
1104
1088
  }
1105
1089
  /** The compound shape the app's own design system already uses (`StatusTabs`). */
1106
- const CapxulContacts = Object.assign(Root$6, {
1090
+ const CapxulContacts = Object.assign(Root$7, {
1107
1091
  Summary: Summary$3,
1108
1092
  List,
1109
1093
  Add
@@ -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,57 +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 the primary holding's
1287
- * currency, else the bucket with the largest absolute `net`, breaking a tie on
1288
- * currency code ascending so the choice is stable across reloads.
1289
- *
1290
- * The primary holding is matched on `available.currency` — the consumer
1291
- * `CurrencyCode` — and NOT on `Holding.symbol`, which is the raw token ticker
1292
- * ("USDX") and can never equal a bucket's currency ("USD"). See the contract
1293
- * correction on #1449.
1294
- */
1295
- function pickBucket(totals, primaryCurrency) {
1296
- if (primaryCurrency !== null) {
1297
- const matched = totals.find((total) => total.currency === primaryCurrency);
1298
- if (matched !== void 0) return matched;
1299
- }
1300
- let best = null;
1301
- for (const total of totals) {
1302
- if (best === null) {
1303
- best = total;
1304
- continue;
1305
- }
1306
- const order = compareNet(total, best);
1307
- if (order > 0 || order === 0 && total.currency < best.currency) best = total;
1308
- }
1309
- return best;
1310
- }
1311
1290
  /**
1312
1291
  * The current UTC day number, which advances ON ITS OWN at UTC midnight.
1313
1292
  *
@@ -1351,47 +1330,36 @@ function useActivitySummary(actor, window) {
1351
1330
  const params = {};
1352
1331
  if (actor !== void 0) params.actor = actor;
1353
1332
  if (range !== void 0) params.window = range;
1354
- return unwrapCapxulResult(await requireBootstrappedClient(client, CAPXUL_OPERATIONS.activity.summary).activity.summary(params));
1355
- },
1356
- enabled
1357
- });
1358
- const primary = useQuery({
1359
- queryKey: accountKey([...capxulKeys.holdings(actor), "primary"], identity),
1360
- queryFn: async () => {
1361
- 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
+ };
1362
1338
  },
1363
1339
  enabled
1364
1340
  });
1365
1341
  const totals = summary.data?.totals;
1366
- const primaryCurrency = primary.data?.available.currency ?? null;
1367
- const isLoading = summary.isLoading || primary.isLoading;
1368
- const bucket = useMemo(() => totals === void 0 || isLoading ? null : pickBucket(totals, primaryCurrency), [
1369
- isLoading,
1370
- primaryCurrency,
1371
- totals
1372
- ]);
1373
- if (bucket === null) return {
1342
+ const isLoading = summary.isLoading;
1343
+ if (summary.data === void 0 || totals === void 0 || totals.length === 0 || isLoading) return {
1374
1344
  currency: null,
1375
1345
  moneyIn: null,
1376
1346
  moneyOut: null,
1377
1347
  net: null,
1378
1348
  pending: null,
1349
+ unrated: [],
1379
1350
  count: summary.data?.count ?? 0,
1380
1351
  isLoading,
1381
1352
  error: asCapxulError$1(summary.error),
1382
1353
  ...summary.data?.readWarning === void 0 ? {} : { readWarning: summary.data.readWarning }
1383
1354
  };
1384
- const display = (raw) => formatMoney({
1385
- currency: bucket.currency,
1386
- value: majorUnits(raw, bucket.decimals),
1387
- decimals: bucket.decimals
1388
- });
1355
+ const valuation = summary.data.valuation;
1389
1356
  return {
1390
- currency: bucket.currency,
1391
- moneyIn: display(bucket.in),
1392
- moneyOut: display(bucket.out),
1393
- net: display(bucket.net),
1394
- 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,
1395
1363
  count: summary.data?.count ?? 0,
1396
1364
  isLoading,
1397
1365
  error: asCapxulError$1(summary.error),
@@ -1418,7 +1386,7 @@ const SIGN = Object.freeze({
1418
1386
  self: ""
1419
1387
  });
1420
1388
  function toActivityRow(item, actor) {
1421
- const amountDisplay = formatMoney(item.amount);
1389
+ const amountDisplay = formatAssetAmount(item.amount);
1422
1390
  const signedAmountDisplay = `${SIGN[item.direction]}${amountDisplay}`;
1423
1391
  if (item.kind === "movement") return {
1424
1392
  kind: "movement",
@@ -1430,7 +1398,7 @@ function toActivityRow(item, actor) {
1430
1398
  counterpartyLabel: item.counterpartyLabel,
1431
1399
  amountDisplay,
1432
1400
  signedAmountDisplay,
1433
- currency: item.amount.currency,
1401
+ currency: assetSymbolFor(item.amount.asset),
1434
1402
  createdAt: item.createdAt,
1435
1403
  classification: item.classification,
1436
1404
  phase: MOVEMENT_PHASE,
@@ -1452,7 +1420,7 @@ function toActivityRow(item, actor) {
1452
1420
  counterpartyLabel: item.counterpartyLabel,
1453
1421
  amountDisplay,
1454
1422
  signedAmountDisplay,
1455
- currency: item.amount.currency,
1423
+ currency: assetSymbolFor(item.amount.asset),
1456
1424
  createdAt: item.createdAt,
1457
1425
  ledgerKind: item.ledgerKind,
1458
1426
  paymentType: item.paymentType,
@@ -1533,7 +1501,7 @@ function buildCsv(items, actor, search) {
1533
1501
  row.counterpartyLabel,
1534
1502
  row.direction,
1535
1503
  item.amount.value,
1536
- item.amount.currency,
1504
+ assetSymbolFor(item.amount.asset),
1537
1505
  row.kind === "payment" ? row.execution?.notSent ? "not_sent" : row.execution?.status === "unresolved" ? "verifying" : row.status : "",
1538
1506
  row.reference.id,
1539
1507
  row.receipt?.transactionHash ?? ""
@@ -1789,7 +1757,7 @@ function useActivityContext() {
1789
1757
  if (engine === null) throw new Error("CapxulActivity parts must be used inside <CapxulActivity>");
1790
1758
  return engine;
1791
1759
  }
1792
- function Root$5({ actor, pageSize, children }) {
1760
+ function Root$6({ actor, pageSize, children }) {
1793
1761
  const engine = useActivity({
1794
1762
  ...actor === void 0 ? {} : { actor },
1795
1763
  ...pageSize === void 0 ? {} : { pageSize }
@@ -1840,7 +1808,7 @@ function Export({ children }) {
1840
1808
  return /* @__PURE__ */ jsx(Fragment, { children: children(useActivityContext().csv) });
1841
1809
  }
1842
1810
  /** The compound shape the app's own design system already uses (`StatusTabs`). */
1843
- const CapxulActivity = Object.assign(Root$5, {
1811
+ const CapxulActivity = Object.assign(Root$6, {
1844
1812
  Summary: Summary$2,
1845
1813
  Filters,
1846
1814
  Search,
@@ -1881,6 +1849,26 @@ function useOrgMeReading(orgId) {
1881
1849
  ]);
1882
1850
  }
1883
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
1884
1872
  //#region src/headless/send-money/signer-status.ts
1885
1873
  const NO_SUBSCRIPTION = () => () => {};
1886
1874
  function useSignerStatus(client) {
@@ -2030,6 +2018,37 @@ function useSelectedOption(options) {
2030
2018
  }, [options]);
2031
2019
  return [options.find((option) => option.id === selectedId) ?? null, select];
2032
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
+ }
2033
2052
  function useSendMoney(input) {
2034
2053
  const { actor, onSent, onFailed } = input;
2035
2054
  const client = useCapxulClientOrNull();
@@ -2042,15 +2061,12 @@ function useSendMoney(input) {
2042
2061
  const treasury = useCapxulOrgTreasury(orgId, { enabled: actor.kind === "organization" });
2043
2062
  const accountQuery = actor.kind === "personal" ? balance : treasury;
2044
2063
  const account = accountQuery.data ?? null;
2045
- const assetOptions = useMemo(() => account === null ? [] : [{
2046
- id: account.id,
2047
- symbol: account.available.currency,
2048
- availableDisplay: formatMoney(account.available, { grammar: "code" })
2049
- }], [account]);
2064
+ const assetSelections = useMemo(() => assetSelectionsFromAccount$1(account), [account]);
2065
+ const assetOptions = useMemo(() => assetSelections.map((selection) => selection.option), [assetSelections]);
2050
2066
  const [selectedAssetOption, selectAsset] = useSelectedOption(assetOptions);
2051
- 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]);
2052
2068
  const [amountText, setAmountText] = useState("");
2053
- 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]);
2054
2070
  const parsedMoney = parsed !== null && !isMoneyParseError(parsed) ? parsed : null;
2055
2071
  const amountError = parsed !== null && isMoneyParseError(parsed) ? parsed.reason : null;
2056
2072
  const recipient = useRecipientResolution(client, JSON.stringify([
@@ -2062,7 +2078,7 @@ function useSendMoney(input) {
2062
2078
  const personalPay = useCapxulPay();
2063
2079
  const organizationPay = useCapxulOrganizationPay(orgId);
2064
2080
  const pay = actor.kind === "personal" ? personalPay : organizationPay;
2065
- 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";
2066
2082
  const submitGate = useRef(false);
2067
2083
  const submit = useCallback(() => {
2068
2084
  if (blockedReason !== null || submitGate.current) return;
@@ -2149,7 +2165,7 @@ function useSendMoneyContext() {
2149
2165
  if (engine === null) throw new Error("CapxulSendMoney parts must be used inside <CapxulSendMoney>");
2150
2166
  return engine;
2151
2167
  }
2152
- function Root$4({ actor, onSent, onFailed, children }) {
2168
+ function Root$5({ actor, onSent, onFailed, children }) {
2153
2169
  const engine = useSendMoney({
2154
2170
  actor,
2155
2171
  onSent,
@@ -2176,7 +2192,7 @@ function Actions$1({ children }) {
2176
2192
  return /* @__PURE__ */ jsx(Fragment, { children: children(useSendMoneyContext().actions) });
2177
2193
  }
2178
2194
  /** The compound shape the app's own design system already uses (`StatusTabs`). */
2179
- const CapxulSendMoney = Object.assign(Root$4, {
2195
+ const CapxulSendMoney = Object.assign(Root$5, {
2180
2196
  Source,
2181
2197
  Asset: Asset$1,
2182
2198
  Amount,
@@ -2266,7 +2282,7 @@ function useOrgMemberContext() {
2266
2282
  if (state === null) throw new Error("CapxulOrgMember parts must be used inside <CapxulOrgMember>");
2267
2283
  return state;
2268
2284
  }
2269
- function Root$3({ orgId, children }) {
2285
+ function Root$4({ orgId, children }) {
2270
2286
  const state = useOrgMember(orgId);
2271
2287
  return /* @__PURE__ */ jsx(OrgMemberContext.Provider, {
2272
2288
  value: state,
@@ -2286,7 +2302,7 @@ function Standing({ children }) {
2286
2302
  return /* @__PURE__ */ jsx(Fragment, { children: children(useOrgMemberContext().standing) });
2287
2303
  }
2288
2304
  /** The compound shape the app's own design system already uses (`StatusTabs`). */
2289
- const CapxulOrgMember = Object.assign(Root$3, {
2305
+ const CapxulOrgMember = Object.assign(Root$4, {
2290
2306
  Can,
2291
2307
  Standing
2292
2308
  });
@@ -2326,18 +2342,14 @@ function isOrgLaneSettled(identity) {
2326
2342
  if (identity.phase !== "authenticated" || identity.account.at !== "claimed") return true;
2327
2343
  const org = identity.account.org;
2328
2344
  if (org === null) return true;
2329
- switch (org.at) {
2330
- case "ready":
2331
- case "failed": return true;
2332
- default: return false;
2333
- }
2345
+ return org.observation === "connected" && org.setup !== null;
2334
2346
  }
2335
2347
  /** Exactly the Organization asked for, and ready to be used. */
2336
2348
  function isExactOrgReady(lifecycle, orgId) {
2337
2349
  return lifecycle?.status === "ready" && String(lifecycle.orgId) === orgId;
2338
2350
  }
2339
2351
  function isLifecycleSettled(lifecycle) {
2340
- return lifecycle?.status === "ready" || lifecycle?.status === "failed";
2352
+ return lifecycle !== void 0;
2341
2353
  }
2342
2354
  function destinationOrNull(identity) {
2343
2355
  return identity === null ? null : resolveIdentityDestination(identity);
@@ -2365,19 +2377,27 @@ function decidePhase(input) {
2365
2377
  * Which probe produced the surfaced error — the SAME precedence `error` itself
2366
2378
  * is chosen by, written once so the code and its source can never disagree.
2367
2379
  */
2368
- function probeOf(session, membership, lifecycle) {
2380
+ function probeOf(session, membership, lifecycle, authority) {
2369
2381
  if (session !== null) return "session";
2370
2382
  if (membership !== null) return "membership";
2371
2383
  if (lifecycle !== null) return "lifecycle";
2384
+ if (authority !== null) return "authority";
2372
2385
  return null;
2373
2386
  }
2374
2387
  /** The engine — all the logic. NOT exported from the package barrel. */
2375
2388
  function useDashboardAccess(scope) {
2376
2389
  const client = useCapxulClientOrNull();
2390
+ const queryClient = useQueryClient();
2377
2391
  const identity = useCapxulIdentityOrNull();
2378
2392
  const destination = useMemo(() => destinationOrNull(identity), [identity]);
2379
2393
  const orgId = scope.kind === "organization" ? scope.orgId : void 0;
2380
2394
  const claimed = isClaimedOrFalse(identity);
2395
+ const scopeKey = `${orgId ?? ""}:${identity?.phase === "authenticated" ? identity.session.authUserId : ""}:${actingAccount(identity) ?? ""}`;
2396
+ const [observation, setObservation] = useState({
2397
+ scopeKey,
2398
+ status: "loading"
2399
+ });
2400
+ const [subscriptionRetry, setSubscriptionRetry] = useState(0);
2381
2401
  const sessionError = useMemo(() => identity?.phase === "faulted" ? identityError(identity) : null, [identity]);
2382
2402
  const wantsMemberships = orgId !== void 0 && claimed;
2383
2403
  const memberships = useCapxulOrgs({ enabled: wantsMemberships });
@@ -2385,33 +2405,125 @@ function useDashboardAccess(scope) {
2385
2405
  const membershipsSettled = !wantsMemberships || memberships.data !== void 0 || membershipError !== null;
2386
2406
  const hasMembership = orgId !== void 0 && (memberships.data ?? []).some((organization) => String(organization.id) === orgId);
2387
2407
  const wantsLifecycle = wantsMemberships && membershipsSettled && hasMembership;
2408
+ const setupKey = accountKey(capxulKeys.orgLifecycle(orgId), identity);
2388
2409
  const lifecycle = useQuery({
2389
- queryKey: capxulKeys.orgLifecycle(orgId),
2410
+ queryKey: setupKey,
2390
2411
  queryFn: async () => {
2391
- return unwrapCapxulResult(await requireBootstrappedClient(client, CAPXUL_OPERATIONS.org.getLifecycle).org(orgId).getLifecycle());
2412
+ return unwrapCapxulResult(await requireBootstrappedClient(client, CAPXUL_OPERATIONS.organization.setup).org(orgId).setup.get());
2392
2413
  },
2393
2414
  enabled: wantsLifecycle && client !== null
2394
2415
  });
2395
- const lifecycleError = wantsLifecycle ? lifecycle.error : null;
2396
- const lifecycleSettled = !wantsLifecycle || lifecycleError !== null || isLifecycleSettled(lifecycle.data);
2416
+ const meKey = capxulKeys.orgMe(orgId, actingAccount(identity));
2417
+ useEffect(() => {
2418
+ if (!wantsLifecycle || client === null || orgId === void 0) return;
2419
+ const controller = new AbortController();
2420
+ let closed = false;
2421
+ let stop = null;
2422
+ setObservation({
2423
+ scopeKey,
2424
+ status: "loading"
2425
+ });
2426
+ client.org(orgId).setup.subscribe((snapshot) => {
2427
+ if (closed) return;
2428
+ if (snapshot.status === "value") {
2429
+ setObservation({
2430
+ scopeKey,
2431
+ status: "value",
2432
+ value: snapshot.value
2433
+ });
2434
+ queryClient.setQueryData(setupKey, snapshot.value);
2435
+ } else if (snapshot.status === "error") setObservation({
2436
+ scopeKey,
2437
+ status: "error",
2438
+ error: snapshot.error
2439
+ });
2440
+ }, { signal: controller.signal }).then((result) => {
2441
+ if (!result.ok) {
2442
+ if (!closed) setObservation({
2443
+ scopeKey,
2444
+ status: "error",
2445
+ error: result.error
2446
+ });
2447
+ return;
2448
+ }
2449
+ stop = result.value;
2450
+ if (closed) stop();
2451
+ }).catch(() => {
2452
+ if (!closed) setObservation({
2453
+ scopeKey,
2454
+ status: "error",
2455
+ error: new CapxulError("UNKNOWN", "Organization setup subscription failed")
2456
+ });
2457
+ });
2458
+ return () => {
2459
+ closed = true;
2460
+ controller.abort();
2461
+ stop?.();
2462
+ };
2463
+ }, [
2464
+ client,
2465
+ orgId,
2466
+ wantsLifecycle,
2467
+ queryClient,
2468
+ scopeKey,
2469
+ subscriptionRetry
2470
+ ]);
2471
+ const currentSetup = observation.scopeKey === scopeKey && observation.status === "value" ? observation.value : void 0;
2472
+ const wantsAuthority = wantsLifecycle && currentSetup?.status === "ready";
2473
+ const authority = useQuery({
2474
+ queryKey: meKey,
2475
+ queryFn: async () => {
2476
+ return unwrapCapxulResult(await requireBootstrappedClient(client, CAPXUL_OPERATIONS.org.me).org(orgId).me());
2477
+ },
2478
+ enabled: wantsAuthority && client !== null
2479
+ });
2480
+ const [checkedAuthority, setCheckedAuthority] = useState(null);
2481
+ useEffect(() => {
2482
+ if (!wantsAuthority || currentSetup === void 0) return;
2483
+ let active = true;
2484
+ authority.refetch().then((result) => {
2485
+ if (active && result.isSuccess) setCheckedAuthority({
2486
+ scopeKey,
2487
+ setup: currentSetup
2488
+ });
2489
+ });
2490
+ return () => {
2491
+ active = false;
2492
+ };
2493
+ }, [
2494
+ scopeKey,
2495
+ currentSetup,
2496
+ wantsAuthority,
2497
+ authority.refetch
2498
+ ]);
2499
+ const authorityCurrent = checkedAuthority?.scopeKey === scopeKey && checkedAuthority.setup === currentSetup && authority.data !== void 0;
2500
+ const observationError = observation.scopeKey === scopeKey && observation.status === "error" ? observation.error : void 0;
2501
+ const lifecycleError = wantsLifecycle ? observationError ?? lifecycle.error : null;
2502
+ const authorityError = wantsAuthority ? authority.error : null;
2503
+ const lifecycleSettled = !wantsLifecycle || lifecycleError !== null || authorityError !== null || isLifecycleSettled(currentSetup) && (!wantsAuthority || authorityCurrent);
2397
2504
  const retry = useCallback(() => {
2398
2505
  if (sessionError !== null && client !== null) client.auth.getSession();
2399
2506
  if (membershipError !== null) memberships.refetch();
2507
+ if (observationError !== void 0) setSubscriptionRetry((attempt) => attempt + 1);
2400
2508
  if (lifecycleError !== null) lifecycle.refetch();
2509
+ if (authorityError !== null) authority.refetch();
2401
2510
  }, [
2402
2511
  client,
2403
2512
  sessionError,
2404
2513
  membershipError,
2514
+ observationError,
2405
2515
  lifecycleError,
2406
2516
  memberships.refetch,
2407
- lifecycle.refetch
2517
+ lifecycle.refetch,
2518
+ authorityError,
2519
+ authority.refetch
2408
2520
  ]);
2409
- const error = sessionError ?? membershipError ?? lifecycleError;
2410
- const probe = probeOf(sessionError, membershipError, lifecycleError);
2521
+ const error = sessionError ?? membershipError ?? lifecycleError ?? authorityError;
2522
+ const probe = probeOf(sessionError, membershipError, lifecycleError, authorityError);
2411
2523
  const phase = decidePhase({
2412
2524
  error,
2413
2525
  settled: client !== null && identity !== null && !isRestoring$1(identity) && membershipsSettled && lifecycleSettled,
2414
- granted: scope.kind === "personal" ? claimed : claimed && orgId !== void 0 && hasMembership && isExactOrgReady(lifecycle.data, orgId),
2526
+ granted: scope.kind === "personal" ? claimed : claimed && orgId !== void 0 && hasMembership && isExactOrgReady(currentSetup, orgId) && authorityCurrent,
2415
2527
  orgLaneSettled: identity !== null && isOrgLaneSettled(identity),
2416
2528
  destination
2417
2529
  });
@@ -2446,7 +2558,7 @@ function usePart(phase) {
2446
2558
  }, [placed, phase]);
2447
2559
  return state.phase === phase ? state : null;
2448
2560
  }
2449
- function Root$2({ scope, children }) {
2561
+ function Root$3({ scope, children }) {
2450
2562
  const state = useDashboardAccess(scope);
2451
2563
  const [placed] = useState(() => /* @__PURE__ */ new Set());
2452
2564
  const value = useMemo(() => ({
@@ -2464,7 +2576,7 @@ function Root$2({ scope, children }) {
2464
2576
  });
2465
2577
  }
2466
2578
  /** Hands nothing — today's app renders `null` here, exactly as it does now. */
2467
- function Checking({ children }) {
2579
+ function Checking$1({ children }) {
2468
2580
  return usePart("checking") === null ? null : /* @__PURE__ */ jsx(Fragment, { children });
2469
2581
  }
2470
2582
  function DashboardError({ children }) {
@@ -2491,19 +2603,14 @@ function Granted({ children }) {
2491
2603
  return usePart("granted") === null ? null : /* @__PURE__ */ jsx(Fragment, { children });
2492
2604
  }
2493
2605
  /** The compound shape the app's own design system already uses (`StatusTabs`). */
2494
- const CapxulDashboardAccess = Object.assign(Root$2, {
2495
- Checking,
2606
+ const CapxulDashboardAccess = Object.assign(Root$3, {
2607
+ Checking: Checking$1,
2496
2608
  Error: DashboardError,
2497
2609
  Redirect,
2498
2610
  Granted
2499
2611
  });
2500
2612
  //#endregion
2501
2613
  //#region src/headless/payroll/money.ts
2502
- /** `{ value: "12.34", decimals: 6 }` → `12340000n`. */
2503
- function toMinorUnits(money) {
2504
- const [whole = "0", fraction = ""] = money.value.split(".");
2505
- return BigInt(`${whole}${fraction.padEnd(money.decimals, "0")}`);
2506
- }
2507
2614
  const TYPED_AMOUNT = /^(-?)(\d+)(?:\.(\d+))?$/;
2508
2615
  /**
2509
2616
  * Σ of the employer's TYPED amounts on a saved group — a DISPLAY total.
@@ -2536,6 +2643,17 @@ function fromMinorUnits(raw, asset) {
2536
2643
  decimals: asset.decimals
2537
2644
  };
2538
2645
  }
2646
+ /** Convert exact raw units to one asset quantity without a fiat denomination. */
2647
+ function assetFromMinorUnits(raw, asset) {
2648
+ const money = fromMinorUnits(raw, {
2649
+ currency: "USD",
2650
+ decimals: asset.decimals
2651
+ });
2652
+ return {
2653
+ asset: asset.asset,
2654
+ value: money.value
2655
+ };
2656
+ }
2539
2657
  //#endregion
2540
2658
  //#region src/headless/payroll/reads.ts
2541
2659
  function toRecipientOption(entry) {
@@ -2652,7 +2770,7 @@ function scopedClient(client, method, orgId) {
2652
2770
  //#endregion
2653
2771
  //#region src/headless/payroll/use-payroll.ts
2654
2772
  /** Money reads the same way everywhere on this screen: amount then code. */
2655
- const GRAMMAR$1 = { grammar: "code" };
2773
+ const GRAMMAR = { grammar: "code" };
2656
2774
  /**
2657
2775
  * One captured snapshot, or nothing — never a fabricated empty options object,
2658
2776
  * and never a DOM event a screen wired straight to `onClick`.
@@ -2668,7 +2786,7 @@ function toPayrollGroupRow(group) {
2668
2786
  name: group.name,
2669
2787
  tone: group.tone,
2670
2788
  memberCount: group.members.length,
2671
- totalDisplay: currency === void 0 ? "" : formatMoney(sumTypedAmounts(group.members.map((member) => member.amount), currency), GRAMMAR$1),
2789
+ totalDisplay: currency === void 0 ? "" : formatMoney(sumTypedAmounts(group.members.map((member) => member.amount), currency), GRAMMAR),
2672
2790
  members: group.members.map((member) => ({
2673
2791
  recipientId: member.partyId,
2674
2792
  amount: member.amount,
@@ -2758,8 +2876,8 @@ function usePayroll(orgId) {
2758
2876
  const started = orgId !== void 0;
2759
2877
  return {
2760
2878
  summary: {
2761
- walletBalanceDisplay: treasury.data === void 0 ? null : formatMoney(treasury.data.available, GRAMMAR$1),
2762
- paidThisMonthDisplay: runs.data === void 0 ? null : formatMoney(runs.data.settledThisMonth, GRAMMAR$1),
2879
+ walletBalanceDisplay: treasury.data?.available === void 0 || treasury.data.available === null ? null : formatMoney(treasury.data.available, GRAMMAR),
2880
+ paidThisMonthDisplay: runs.data === void 0 ? null : runs.data.settledThisMonth.map(formatAssetAmount).join(", "),
2763
2881
  isLoading: started && (treasury.data === void 0 && treasury.error === null || runs.data === void 0 && runs.error === null),
2764
2882
  error: treasury.error ?? runs.error
2765
2883
  },
@@ -2792,7 +2910,7 @@ function usePayrollContext() {
2792
2910
  if (engine === null) throw new Error("CapxulPayroll parts must be used inside <CapxulPayroll>");
2793
2911
  return engine;
2794
2912
  }
2795
- function Root$1({ orgId, children }) {
2913
+ function Root$2({ orgId, children }) {
2796
2914
  const engine = usePayroll(orgId);
2797
2915
  return /* @__PURE__ */ jsx(PayrollContext.Provider, {
2798
2916
  value: engine,
@@ -2810,7 +2928,7 @@ function Groups({ children }) {
2810
2928
  * There is NO `.Runs` part (ruling Q6): no shipped region has the right shape
2811
2929
  * for a run list, and a part without a production caller may not ship.
2812
2930
  */
2813
- const CapxulPayroll = Object.assign(Root$1, {
2931
+ const CapxulPayroll = Object.assign(Root$2, {
2814
2932
  Summary: Summary$1,
2815
2933
  Groups
2816
2934
  });
@@ -2820,8 +2938,14 @@ function record(value) {
2820
2938
  if (typeof value !== "object" || value === null || Array.isArray(value)) throw Errors.invalidInput("payrollDraft", "Invalid run draft");
2821
2939
  return value;
2822
2940
  }
2941
+ /**
2942
+ * A draft-scoped opaque key. `/` is in the class so `asset.id` can be the
2943
+ * canonical CAIP-19 `AssetId` (`eip155:<chainId>/erc20:<address>`): the draft
2944
+ * records WHICH asset was authorized, and a symbol is not an identity
2945
+ * (ADR-0028 R3). The field never reaches the backend — only `input` is sent.
2946
+ */
2823
2947
  function identifier(value) {
2824
- if (typeof value !== "string" || !/^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u.test(value)) throw Errors.invalidInput("payrollDraft", "Invalid draft identifier");
2948
+ if (typeof value !== "string" || !/^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$/u.test(value)) throw Errors.invalidInput("payrollDraft", "Invalid draft identifier");
2825
2949
  return value;
2826
2950
  }
2827
2951
  function minorAmount(value) {
@@ -2837,21 +2961,35 @@ function decodeDraft(value, scope) {
2837
2961
  const items = input.items.map((raw) => {
2838
2962
  const item = record(raw);
2839
2963
  const to = record(item.to);
2964
+ const amount = record(item.amount);
2840
2965
  const partyId = toPartyId(identifier(item.partyId));
2841
- 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");
2966
+ if (to.kind !== "party" || to.partyId !== partyId || item.gross !== item.net || typeof amount.asset !== "string" || typeof amount.value !== "string" || "currency" in item || "decimals" in item || !Array.isArray(item.adjustments) || item.adjustments.length !== 0) throw Errors.invalidInput("payrollDraft", "Invalid draft recipient");
2842
2967
  const net = minorAmount(item.net);
2968
+ const selectedAsset = requireAsset(identifier(amount.asset));
2969
+ const selectedAmount = {
2970
+ asset: selectedAsset.assetId,
2971
+ value: amount.value
2972
+ };
2973
+ try {
2974
+ if (toRaw(selectedAmount, selectedAsset).toString() !== net) throw Errors.invalidInput("payrollDraft", "Draft payout units differ from net");
2975
+ } catch {
2976
+ throw Errors.invalidInput("payrollDraft", "Invalid draft payout amount");
2977
+ }
2843
2978
  return {
2844
2979
  to: {
2845
2980
  kind: "party",
2846
2981
  partyId
2847
2982
  },
2848
2983
  partyId,
2984
+ amount: selectedAmount,
2849
2985
  gross: net,
2850
2986
  net,
2851
2987
  adjustments: []
2852
2988
  };
2853
2989
  });
2854
2990
  if (new Set(items.map((item) => item.partyId)).size !== items.length) throw Errors.invalidInput("payrollDraft", "Duplicate draft recipient");
2991
+ const exactAsset = requireAsset(identifier(asset.id));
2992
+ if (input.asset !== exactAsset.assetId || items.some((item) => item.amount.asset !== exactAsset.assetId)) throw Errors.invalidInput("payrollDraft", "Draft item asset does not match the run asset");
2855
2993
  return {
2856
2994
  version: 1,
2857
2995
  scope,
@@ -2862,6 +3000,7 @@ function decodeDraft(value, scope) {
2862
3000
  },
2863
3001
  input: {
2864
3002
  permissionId: identifier(input.permissionId),
3003
+ asset: exactAsset.assetId,
2865
3004
  period: {
2866
3005
  start: period.start,
2867
3006
  end: period.start
@@ -3065,7 +3204,6 @@ function termsSeeds(terms, asset, period) {
3065
3204
  }
3066
3205
  //#endregion
3067
3206
  //#region src/headless/payroll/use-payroll-run.ts
3068
- const GRAMMAR = { grammar: "code" };
3069
3207
  /**
3070
3208
  * The period this composer composes: an INSTANT run (ruling Q3). `authorizeRun`
3071
3209
  * stamps `start === end === runAt` at submit, and the payslip's period renders
@@ -3089,6 +3227,31 @@ const INSTANT_RUN_PERIOD = {
3089
3227
  function amountText(amounts, seeds, recipientId) {
3090
3228
  return amounts[recipientId]?.value ?? seeds.get(recipientId) ?? "";
3091
3229
  }
3230
+ function freshRunItems(input) {
3231
+ const items = [];
3232
+ for (const [index, recipientId] of input.selected.entries()) {
3233
+ const to = input.payableRefs[index];
3234
+ const amount = input.amounts[index];
3235
+ const terms = input.compensationTerms.get(recipientId);
3236
+ if (to === void 0 || amount === void 0 || terms === void 0) throw Errors.unknown();
3237
+ const rawAmount = toRaw(amount, requireAsset(amount.asset)).toString();
3238
+ items.push({
3239
+ to: {
3240
+ kind: "party",
3241
+ partyId: toPartyId(recipientId)
3242
+ },
3243
+ partyId: recipientId,
3244
+ amount,
3245
+ gross: rawAmount,
3246
+ net: rawAmount,
3247
+ adjustments: []
3248
+ });
3249
+ }
3250
+ return items;
3251
+ }
3252
+ function hasRequiredCompensation(frozen, writerNamesSelectedAsset, selected, terms) {
3253
+ return frozen || !writerNamesSelectedAsset || selected.every((recipientId) => terms.has(recipientId));
3254
+ }
3092
3255
  /**
3093
3256
  * WHICH run the composer is composing, by MEANING rather than by object shape.
3094
3257
  *
@@ -3120,6 +3283,33 @@ function initialAmounts(prefill) {
3120
3283
  };
3121
3284
  return seeded;
3122
3285
  }
3286
+ /**
3287
+ * One `.Asset` option per position the treasury Account holds, taken from
3288
+ * `account.balances` — never from `account.available`, which is the Account's
3289
+ * fiat VALUATION of those positions (ADR-0028 R5/R6). The option is keyed on
3290
+ * the ASSET id: several positions share one account id, and a symbol is display
3291
+ * only (ADR-0028 R3). Every row is listed, so a real multi-position treasury
3292
+ * still offers its holdings.
3293
+ */
3294
+ function assetSelectionsFromAccount(account) {
3295
+ if (account === null) return [];
3296
+ return account.balances.map((position) => {
3297
+ return {
3298
+ option: {
3299
+ id: position.asset,
3300
+ symbol: position.symbol,
3301
+ availableDisplay: formatAssetAmount({
3302
+ asset: position.asset,
3303
+ value: position.available
3304
+ })
3305
+ },
3306
+ money: {
3307
+ asset: position.asset,
3308
+ value: position.available
3309
+ }
3310
+ };
3311
+ });
3312
+ }
3123
3313
  function usePayrollRun(input) {
3124
3314
  const { orgId, prefill, onRun, onFailed } = input;
3125
3315
  const client = useCapxulClientOrNull();
@@ -3133,17 +3323,13 @@ function usePayrollRun(input) {
3133
3323
  const terms = useOrgTerms(orgId);
3134
3324
  const [selected, setSelected] = useState(() => prefill?.recipientIds ?? []);
3135
3325
  const [amounts, setAmounts] = useState(() => initialAmounts(prefill));
3136
- const restoreCurrency = treasury.data?.available.currency;
3137
3326
  const { draftScope, currentScope, draft, draftError, restorationPending, activateDraft, restoreDraft, setDraft, recovery, draftBlocked } = useRunDraft(client, orgId, useCallback((restored) => {
3138
3327
  setSelected(restored.input.items.map((item) => item.partyId));
3139
- if (restoreCurrency !== void 0 && restoreCurrency === restored.asset.currency) setAmounts(Object.fromEntries(restored.input.items.map((item) => [item.partyId, {
3140
- value: fromMinorUnits(BigInt(item.net), {
3141
- currency: restoreCurrency,
3142
- decimals: restored.asset.decimals
3143
- }).value,
3328
+ if (requireAsset(restored.input.asset).assetId === restored.input.asset) setAmounts(Object.fromEntries(restored.input.items.map((item) => [item.partyId, {
3329
+ value: item.amount.value,
3144
3330
  prefilled: false
3145
3331
  }])));
3146
- }, [restoreCurrency]));
3332
+ }, []));
3147
3333
  const frozen = draft !== null;
3148
3334
  const composerKey = composerIdentity(orgId, prefill, draftScope, frozen);
3149
3335
  const [seededKey, setSeededKey] = useState(composerKey);
@@ -3183,11 +3369,8 @@ function usePayrollRun(input) {
3183
3369
  draftError
3184
3370
  ]);
3185
3371
  const account = treasury.data ?? null;
3186
- const assetOptions = useMemo(() => account === null ? [] : [{
3187
- id: account.id,
3188
- symbol: account.available.currency,
3189
- availableDisplay: formatMoney(account.available, GRAMMAR)
3190
- }], [account]);
3372
+ const assetSelections = useMemo(() => assetSelectionsFromAccount(account), [account]);
3373
+ const assetOptions = useMemo(() => assetSelections.map((selection) => selection.option), [assetSelections]);
3191
3374
  const [selectedAssetId, setSelectedAssetId] = useState(null);
3192
3375
  useEffect(() => {
3193
3376
  if (!assetOptions.some((option) => option.id === selectedAssetId)) setSelectedAssetId(assetOptions[0]?.id ?? null);
@@ -3196,7 +3379,7 @@ function usePayrollRun(input) {
3196
3379
  const selectAsset = useCallback((id) => {
3197
3380
  if (!submitGate.current && !frozen) setSelectedAssetId((current) => assetOptions.some((option) => option.id === id) ? id : current);
3198
3381
  }, [assetOptions, frozen]);
3199
- const assetMoney = selectedAsset !== null && account !== null ? account.available : null;
3382
+ const assetMoney = assetSelections.find((selection) => selection.option.id === selectedAsset?.id)?.money ?? null;
3200
3383
  const named = useMemo(() => new Map(roster.options.map((option) => [option.id, option])), [roster.options]);
3201
3384
  const refs = roster.refs;
3202
3385
  const payableRefs = useMemo(() => {
@@ -3208,12 +3391,19 @@ function usePayrollRun(input) {
3208
3391
  }
3209
3392
  return resolved;
3210
3393
  }, [refs, selected]);
3211
- const seeds = useMemo(() => termsSeeds(terms.data ?? [], assetMoney, INSTANT_RUN_PERIOD), [assetMoney, terms.data]);
3394
+ const seeds = useMemo(() => {
3395
+ if (assetMoney === null) return /* @__PURE__ */ new Map();
3396
+ const metadata = requireAsset(assetMoney.asset);
3397
+ return termsSeeds(terms.data ?? [], metadata.peg === null ? null : {
3398
+ currency: metadata.peg,
3399
+ decimals: metadata.decimals
3400
+ }, INSTANT_RUN_PERIOD);
3401
+ }, [assetMoney, terms.data]);
3212
3402
  const entries = useMemo(() => {
3213
3403
  if (assetMoney === null) return [];
3214
3404
  return selected.map((recipientId) => {
3215
3405
  const value = amountText(amounts, seeds, recipientId);
3216
- const parsed = value.trim() === "" ? null : parseMoney(value, assetMoney);
3406
+ const parsed = value.trim() === "" ? null : parseAssetAmount(value, requireAsset(assetMoney.asset));
3217
3407
  return {
3218
3408
  recipientId,
3219
3409
  name: named.get(recipientId)?.name ?? recipientId,
@@ -3221,7 +3411,7 @@ function usePayrollRun(input) {
3221
3411
  change: (text) => change(recipientId, text),
3222
3412
  error: parsed !== null && isMoneyParseError(parsed) ? parsed.reason : null,
3223
3413
  prefilled: amounts[recipientId]?.prefilled ?? seeds.has(recipientId),
3224
- currency: assetMoney.currency
3414
+ currency: requireAsset(assetMoney.asset).symbol
3225
3415
  };
3226
3416
  });
3227
3417
  }, [
@@ -3236,7 +3426,7 @@ function usePayrollRun(input) {
3236
3426
  if (assetMoney === null || selected.length === 0) return null;
3237
3427
  const money = [];
3238
3428
  for (const recipientId of selected) {
3239
- const parsed = parseMoney(amountText(amounts, seeds, recipientId), assetMoney);
3429
+ const parsed = parseAssetAmount(amountText(amounts, seeds, recipientId), requireAsset(assetMoney.asset));
3240
3430
  if (isMoneyParseError(parsed)) return null;
3241
3431
  money.push(parsed);
3242
3432
  }
@@ -3247,16 +3437,25 @@ function usePayrollRun(input) {
3247
3437
  seeds,
3248
3438
  selected
3249
3439
  ]);
3250
- const total = useMemo(() => parsedAmounts === null || assetMoney === null ? null : fromMinorUnits(parsedAmounts.reduce((sum, money) => sum + toMinorUnits(money), 0n), assetMoney), [assetMoney, parsedAmounts]);
3251
- const availableRaw = assetMoney === null ? null : toMinorUnits(assetMoney);
3252
- const totalRaw = total === null ? null : toMinorUnits(total);
3440
+ const total = useMemo(() => parsedAmounts === null || assetMoney === null ? null : {
3441
+ asset: assetMoney.asset,
3442
+ value: assetFromMinorUnits(parsedAmounts.reduce((sum, amount) => sum + toRaw(amount, requireAsset(amount.asset)), 0n), {
3443
+ asset: assetMoney.asset,
3444
+ decimals: requireAsset(assetMoney.asset).decimals
3445
+ }).value
3446
+ }, [assetMoney, parsedAmounts]);
3447
+ const availableRaw = assetMoney === null ? null : toRaw(assetMoney, requireAsset(assetMoney.asset));
3448
+ const totalRaw = total === null ? null : toRaw(total, requireAsset(total.asset));
3253
3449
  const funds = availableRaw === null || totalRaw === null ? "unknown" : totalRaw > availableRaw ? "insufficient" : "available";
3254
3450
  const budgets = me.data?.budgets ?? [];
3451
+ 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]);
3452
+ const writerNamesSelectedAsset = writeNamesAsset();
3255
3453
  const blockedReason = firstFalseFact({
3256
3454
  signerReady: signerStatus === "ready",
3257
3455
  ...runAuthorityFacts(draft, budgets, selectedAsset, assetMoney, funds),
3258
3456
  recipientCount: payableRefs === null ? 0 : selected.length,
3259
- amountsParse: total !== null
3457
+ amountsParse: total !== null && hasRequiredCompensation(frozen, writerNamesSelectedAsset, selected, compensationTerms),
3458
+ writeNamesAsset: writerNamesSelectedAsset
3260
3459
  });
3261
3460
  const authorize = useMutation({
3262
3461
  mutationFn: async ({ candidate, options }) => {
@@ -3278,7 +3477,7 @@ function usePayrollRun(input) {
3278
3477
  if (candidate.scope === currentScope.current) restoreDraft(candidate.scope);
3279
3478
  }
3280
3479
  });
3281
- const totalDisplay = total === null ? null : formatMoney(total, GRAMMAR);
3480
+ const totalDisplay = total === null ? null : formatAssetAmount(total);
3282
3481
  const authorizeMutate = authorize.mutate;
3283
3482
  const submit = useCallback((options) => {
3284
3483
  const attempt = toInvocationOptions(options);
@@ -3287,41 +3486,35 @@ function usePayrollRun(input) {
3287
3486
  if (permissionId === void 0 || totalDisplay === null || parsedAmounts === null) return;
3288
3487
  if (payableRefs === null) return;
3289
3488
  const recipientCount = selected.length;
3290
- const items = [];
3291
- for (const [index, recipientId] of selected.entries()) {
3292
- const to = payableRefs[index];
3293
- const parsed = parsedAmounts[index];
3294
- if (to === void 0 || parsed === void 0) return;
3295
- const raw = toMinorUnits(parsed).toString();
3296
- items.push({
3297
- to: {
3298
- kind: "party",
3299
- partyId: toPartyId(recipientId)
3300
- },
3301
- partyId: recipientId,
3302
- gross: raw,
3303
- net: raw,
3304
- adjustments: []
3489
+ let candidate;
3490
+ if (frozen) candidate = draft;
3491
+ else {
3492
+ const items = freshRunItems({
3493
+ selected,
3494
+ payableRefs,
3495
+ amounts: parsedAmounts,
3496
+ compensationTerms
3305
3497
  });
3306
- }
3307
- const runAt = Date.now();
3308
- const candidate = frozen ? draft : {
3309
- version: 1,
3310
- scope: draftScope,
3311
- asset: {
3312
- id: selectedAsset.id,
3313
- currency: assetMoney.currency,
3314
- decimals: assetMoney.decimals
3315
- },
3316
- input: {
3317
- permissionId,
3318
- period: {
3319
- start: runAt,
3320
- end: runAt
3498
+ const runAt = Date.now();
3499
+ candidate = {
3500
+ version: 1,
3501
+ scope: draftScope,
3502
+ asset: {
3503
+ id: selectedAsset.id,
3504
+ currency: requireAsset(assetMoney.asset).symbol,
3505
+ decimals: requireAsset(assetMoney.asset).decimals
3321
3506
  },
3322
- items
3323
- }
3324
- };
3507
+ input: {
3508
+ permissionId,
3509
+ asset: selectedAsset.id,
3510
+ period: {
3511
+ start: runAt,
3512
+ end: runAt
3513
+ },
3514
+ items
3515
+ }
3516
+ };
3517
+ }
3325
3518
  submitGate.current = true;
3326
3519
  authorizeMutate({
3327
3520
  candidate,
@@ -3353,6 +3546,7 @@ function usePayrollRun(input) {
3353
3546
  selectedAsset,
3354
3547
  blockedReason,
3355
3548
  budgets,
3549
+ compensationTerms,
3356
3550
  onFailed,
3357
3551
  onRun,
3358
3552
  parsedAmounts,
@@ -3401,18 +3595,25 @@ function usePayrollRun(input) {
3401
3595
  };
3402
3596
  }
3403
3597
  function remainingBalanceDisplay(available, total, asset) {
3404
- return available === null || total === null || asset === null ? null : formatMoney(fromMinorUnits(available - total, asset), GRAMMAR);
3598
+ if (available === null || total === null || asset === null) return null;
3599
+ const remaining = available - total;
3600
+ const magnitude = remaining < 0n ? -remaining : remaining;
3601
+ const formatted = formatAssetAmount(assetFromMinorUnits(magnitude, {
3602
+ asset: asset.asset,
3603
+ decimals: requireAsset(asset.asset).decimals
3604
+ }));
3605
+ return remaining < 0n ? `-${formatted}` : formatted;
3405
3606
  }
3406
3607
  /** Persisted replay keeps its authorized Budget and asset; a prior debit cannot block its own recovery. */
3407
3608
  function runAuthorityFacts(draft, budgets, asset, money, funds) {
3408
3609
  return {
3409
3610
  budgetCount: draft !== null && !budgets.some((budget) => budget.id === draft.input.permissionId) ? 0 : budgets.length,
3410
- hasAsset: asset !== null && (draft === null || draft.asset.id === asset.id && draft.asset.currency === money?.currency && draft.asset.decimals === money?.decimals),
3611
+ hasAsset: asset !== null && (draft === null || draft.asset.id === asset.id && draft.input.asset === money?.asset),
3411
3612
  funds: draft?.input.requestKey === void 0 ? funds : "available"
3412
3613
  };
3413
3614
  }
3414
3615
  /**
3415
- * The seven facts, in the ONE order the contract fixes. Exactly one code comes
3616
+ * The facts, in the ONE order the contract fixes. Exactly one code comes
3416
3617
  * out, and it is always the first thing that is false — so two problems never
3417
3618
  * race to name the button.
3418
3619
  */
@@ -3423,7 +3624,8 @@ function firstFalseFact(facts) {
3423
3624
  if (!facts.hasAsset) return "no-asset";
3424
3625
  if (facts.recipientCount === 0) return "no-recipients";
3425
3626
  if (!facts.amountsParse) return "amount-invalid";
3426
- return facts.funds === "insufficient" ? "insufficient-funds" : null;
3627
+ if (facts.funds === "insufficient") return "insufficient-funds";
3628
+ return facts.writeNamesAsset ? null : "asset-unnamed";
3427
3629
  }
3428
3630
  //#endregion
3429
3631
  //#region src/headless/payroll/payroll-run.tsx
@@ -3433,7 +3635,7 @@ function usePayrollRunContext() {
3433
3635
  if (engine === null) throw new Error("CapxulPayrollRun parts must be used inside <CapxulPayrollRun>");
3434
3636
  return engine;
3435
3637
  }
3436
- function Root({ orgId, prefill, onRun, onFailed, children }) {
3638
+ function Root$1({ orgId, prefill, onRun, onFailed, children }) {
3437
3639
  const engine = usePayrollRun({
3438
3640
  orgId,
3439
3641
  prefill,
@@ -3463,7 +3665,7 @@ function Actions({ children }) {
3463
3665
  return /* @__PURE__ */ jsx(Fragment, { children: children(engine.actions) });
3464
3666
  }
3465
3667
  /** The compound shape the app's own design system already uses (`StatusTabs`). */
3466
- const CapxulPayrollRun = Object.assign(Root, {
3668
+ const CapxulPayrollRun = Object.assign(Root$1, {
3467
3669
  Recipients,
3468
3670
  Amounts,
3469
3671
  Asset,
@@ -3471,4 +3673,230 @@ const CapxulPayrollRun = Object.assign(Root, {
3471
3673
  Actions
3472
3674
  });
3473
3675
  //#endregion
3474
- 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 };
3676
+ //#region src/headless/client-connection/use-client-connection.ts
3677
+ function useClientConnection(input) {
3678
+ const [view, setView] = useState(null);
3679
+ const [pending, setPending] = useState(false);
3680
+ const [error, setError] = useState(null);
3681
+ const [resendReady, setResendReady] = useState(false);
3682
+ const identityRef = useRef({
3683
+ client: input.client,
3684
+ requestId: input.requestId
3685
+ });
3686
+ identityRef.current = {
3687
+ client: input.client,
3688
+ requestId: input.requestId
3689
+ };
3690
+ const belongsHere = useCallback((client, requestId) => identityRef.current.client === client && identityRef.current.requestId === requestId, []);
3691
+ const refresh = useCallback(async (signal) => {
3692
+ const { client, requestId } = identityRef.current;
3693
+ const result = await client.auth.clientRequests.read({ requestId }, signal === void 0 ? void 0 : { signal });
3694
+ if (signal?.aborted || !belongsHere(client, requestId)) return;
3695
+ if (result.ok) {
3696
+ setView(result.value);
3697
+ setError(null);
3698
+ return;
3699
+ }
3700
+ setError(result.error);
3701
+ }, [belongsHere]);
3702
+ useEffect(() => {
3703
+ setView(null);
3704
+ setError(null);
3705
+ setPending(false);
3706
+ setResendReady(false);
3707
+ const controller = new AbortController();
3708
+ refresh(controller.signal);
3709
+ return () => {
3710
+ controller.abort();
3711
+ };
3712
+ }, [
3713
+ input.client,
3714
+ input.requestId,
3715
+ refresh
3716
+ ]);
3717
+ useEffect(() => {
3718
+ if (view?.phase !== "otp_required") {
3719
+ setResendReady(false);
3720
+ return;
3721
+ }
3722
+ const remaining = view.resendAt - Date.now();
3723
+ if (remaining <= 0) {
3724
+ setResendReady(true);
3725
+ return;
3726
+ }
3727
+ setResendReady(false);
3728
+ const timer = setTimeout(() => setResendReady(true), remaining);
3729
+ return () => clearTimeout(timer);
3730
+ }, [view]);
3731
+ const run = useCallback(async (work) => {
3732
+ const { client, requestId } = identityRef.current;
3733
+ setPending(true);
3734
+ try {
3735
+ const result = await work();
3736
+ if (!belongsHere(client, requestId)) return;
3737
+ if (!result.ok) {
3738
+ setError(result.error);
3739
+ return;
3740
+ }
3741
+ await refresh();
3742
+ } finally {
3743
+ if (belongsHere(client, requestId)) setPending(false);
3744
+ }
3745
+ }, [belongsHere, refresh]);
3746
+ return useMemo(() => {
3747
+ if (error !== null && (view === null || view.phase !== "refused")) return {
3748
+ phase: "failure",
3749
+ otp: null,
3750
+ consent: null,
3751
+ complete: null,
3752
+ failure: {
3753
+ code: error.code,
3754
+ retry: () => refresh(),
3755
+ cancel: async () => {
3756
+ if (view === null) return;
3757
+ const { client, requestId } = identityRef.current;
3758
+ await run(() => client.auth.clientRequests.cancel({
3759
+ requestId,
3760
+ revision: view.revision
3761
+ }));
3762
+ }
3763
+ }
3764
+ };
3765
+ if (view === null) return {
3766
+ phase: "checking",
3767
+ otp: null,
3768
+ consent: null,
3769
+ complete: null,
3770
+ failure: null
3771
+ };
3772
+ if (view.phase === "otp_required") return {
3773
+ phase: view.phase,
3774
+ otp: {
3775
+ email: view.email,
3776
+ pending,
3777
+ canResend: resendReady,
3778
+ verifyCode: async (code) => {
3779
+ const { client, requestId } = identityRef.current;
3780
+ await run(() => client.auth.clientRequests.verifyOtp({
3781
+ requestId,
3782
+ code
3783
+ }));
3784
+ },
3785
+ resend: async () => {
3786
+ const { client, requestId } = identityRef.current;
3787
+ await run(() => client.auth.clientRequests.sendOtp({ requestId }));
3788
+ }
3789
+ },
3790
+ consent: null,
3791
+ complete: null,
3792
+ failure: null
3793
+ };
3794
+ if (view.phase === "review") return {
3795
+ phase: view.phase,
3796
+ otp: null,
3797
+ consent: {
3798
+ verifiedEmail: view.email,
3799
+ clientLabel: view.clientLabel,
3800
+ scopeCodes: view.scopes,
3801
+ purpose: view.intent.mode,
3802
+ setupState: view.setupState,
3803
+ pending,
3804
+ accept: async () => {
3805
+ const { client, requestId } = identityRef.current;
3806
+ await run(() => client.auth.clientRequests.accept({
3807
+ requestId,
3808
+ revision: view.revision
3809
+ }));
3810
+ },
3811
+ cancel: async () => {
3812
+ const { client, requestId } = identityRef.current;
3813
+ await run(() => client.auth.clientRequests.cancel({
3814
+ requestId,
3815
+ revision: view.revision
3816
+ }));
3817
+ }
3818
+ },
3819
+ complete: null,
3820
+ failure: null
3821
+ };
3822
+ if (view.phase === "connected") return {
3823
+ phase: view.phase,
3824
+ otp: null,
3825
+ consent: null,
3826
+ complete: { outcome: "connected" },
3827
+ failure: null
3828
+ };
3829
+ if (view.phase === "refused") return {
3830
+ phase: view.phase,
3831
+ otp: null,
3832
+ consent: null,
3833
+ complete: null,
3834
+ failure: {
3835
+ code: view.code,
3836
+ ...view.field === void 0 ? {} : { field: view.field },
3837
+ retry: () => refresh(),
3838
+ cancel: async () => void 0
3839
+ }
3840
+ };
3841
+ return {
3842
+ phase: view.phase,
3843
+ otp: null,
3844
+ consent: null,
3845
+ complete: null,
3846
+ failure: null
3847
+ };
3848
+ }, [
3849
+ error,
3850
+ pending,
3851
+ refresh,
3852
+ resendReady,
3853
+ run,
3854
+ view
3855
+ ]);
3856
+ }
3857
+ //#endregion
3858
+ //#region src/headless/client-connection/client-connection.tsx
3859
+ const ConnectionContext = createContext(null);
3860
+ function useConnection() {
3861
+ const engine = useContext(ConnectionContext);
3862
+ if (engine === null) throw new Error("CapxulClientConnection parts must be used inside <CapxulClientConnection>");
3863
+ return engine;
3864
+ }
3865
+ function Root({ client, requestId, children }) {
3866
+ const engine = useClientConnection({
3867
+ client,
3868
+ requestId
3869
+ });
3870
+ return /* @__PURE__ */ jsx(ConnectionContext.Provider, {
3871
+ value: engine,
3872
+ children
3873
+ });
3874
+ }
3875
+ function Checking({ children }) {
3876
+ return useConnection().phase === "checking" ? /* @__PURE__ */ jsx(Fragment, { children }) : null;
3877
+ }
3878
+ function Otp({ children }) {
3879
+ const otp = useConnection().otp;
3880
+ return otp === null ? null : /* @__PURE__ */ jsx(Fragment, { children: children(otp) });
3881
+ }
3882
+ function Consent({ children }) {
3883
+ const consent = useConnection().consent;
3884
+ return consent === null ? null : /* @__PURE__ */ jsx(Fragment, { children: children(consent) });
3885
+ }
3886
+ function Complete({ children }) {
3887
+ const complete = useConnection().complete;
3888
+ return complete === null ? null : /* @__PURE__ */ jsx(Fragment, { children: children(complete) });
3889
+ }
3890
+ function Failure({ children }) {
3891
+ const failure = useConnection().failure;
3892
+ return failure === null ? null : /* @__PURE__ */ jsx(Fragment, { children: children(failure) });
3893
+ }
3894
+ const CapxulClientConnection = Object.assign(Root, {
3895
+ Checking,
3896
+ Otp,
3897
+ Consent,
3898
+ Complete,
3899
+ Failure
3900
+ });
3901
+ //#endregion
3902
+ 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 };