@capxul/sdk-react 1.0.0-alpha.21 → 1.0.0-alpha.22

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
@@ -355,6 +355,7 @@ function actorKey(actor) {
355
355
  }
356
356
  function targetKey(target) {
357
357
  if (target === void 0) return "all";
358
+ if ("self" in target) return "self";
358
359
  switch (target.kind) {
359
360
  case "handle": return `handle:${target.handle}`;
360
361
  case "email": return `email:${target.email}`;
@@ -396,6 +397,12 @@ const capxulKeys = {
396
397
  root: ["capxul"],
397
398
  session: ["capxul", "session"],
398
399
  profile: ["capxul", "profile"],
400
+ usernameAvailability: (username) => [
401
+ "capxul",
402
+ "profile",
403
+ "username-availability",
404
+ username
405
+ ],
399
406
  account: ["capxul", "account"],
400
407
  accountLifecycle: ["capxul", "accountLifecycle"],
401
408
  provisioning: ["capxul", "provisioning"],
@@ -1796,6 +1803,333 @@ function Destinations({ input, slots }) {
1796
1803
  return slots.root?.({ children }) ?? children;
1797
1804
  }
1798
1805
  //#endregion
1799
- export { AddressBook, CapxulProvider, Destinations, InsightsSummary, PayrollRoster, RequestInbox, SendMoney, capxulAccountScope, capxulOrgScope, useCapxul, useCapxulAccountBalance, useCapxulAccountFund, useCapxulAccountLifecycle, useCapxulActivity, useCapxulAddAddressBookEntry, useCapxulAddDestination, useCapxulAddPayrollRosterLine, useCapxulAddressBook, useCapxulAddressBookEntry, useCapxulApproveInboxRequest, useCapxulAssignRole, useCapxulCancelPayment, useCapxulCancelRequest, useCapxulClientOrNull, useCapxulCompleteOrganizationOnboarding, useCapxulCompletePersonalOnboarding, useCapxulCreateOrg, useCapxulCurrentUser, useCapxulDeclineInboxRequest, useCapxulDestinations, useCapxulHideAddressBookEntry, useCapxulInbox, useCapxulInsightsHistory, useCapxulInsightsSummary, useCapxulInviteMember, useCapxulIssueRequest, useCapxulLabelAddressBookEntry, useCapxulOfframpQuote, useCapxulOfframpStatus, useCapxulOrg, useCapxulOrgDeployRoles, useCapxulOrgLifecycle, useCapxulOrgMembers, useCapxulOrgRoles, useCapxulOrgTreasury, useCapxulOrganizationAccount, useCapxulOrganizationAuditLog, useCapxulOrgs, useCapxulPay, useCapxulPayment, useCapxulPayments, useCapxulPayout, useCapxulPayrollRoster, useCapxulProfile, useCapxulReconcileRequests, useCapxulRemoveDestination, useCapxulRemoveMember, useCapxulRemovePayrollRosterLine, useCapxulRequest, useCapxulRequests, useCapxulRunPayroll, useCapxulSession, useCapxulSignIn, useCapxulSignOut, useCapxulSubAccountCreate, useCapxulSubAccountDelete, useCapxulSubAccountRename, useCapxulSubAccountsList, useCapxulSwitchActingEntity, useCapxulTransfer, useCapxulUnhideAddressBookEntry, useCapxulUpdatePayrollRosterLine, useCapxulVerifyOtp, useCapxulWithdraw };
1806
+ //#region src/headless/journey/journey-state.ts
1807
+ const JOURNEY_KEY = "capxul.onboarding.journey.v1";
1808
+ const VALIDATED_OWNER_KEY = "capxul.onboarding.validated-owner.v1";
1809
+ const JOURNEY_VERSION = 1;
1810
+ const MAX_DRAFT_TEXT_LENGTH = 200;
1811
+ const MAX_ID_LENGTH = 160;
1812
+ const MAX_STORED_JOURNEY_LENGTH = 8192;
1813
+ const MAX_PAYOUT_DRAFT_ENTRIES = 10;
1814
+ /**
1815
+ * The owner whose stored journey may currently be attributed to observations.
1816
+ * Persisted in sessionStorage rather than module memory so `currentOnboarding-
1817
+ * JourneyId` is a pure function of durable state — attribution is the same
1818
+ * regardless of the order of renders, reloads, or telemetry emits. Established
1819
+ * only when an authenticated owner is (journey start, or an owner-matched load)
1820
+ * and cleared at every auth boundary or on discard.
1821
+ */
1822
+ function markOnboardingOwnerValidated(ownerId) {
1823
+ try {
1824
+ sessionStorage.setItem(VALIDATED_OWNER_KEY, ownerId);
1825
+ } catch {}
1826
+ }
1827
+ function clearValidatedOnboardingOwner() {
1828
+ try {
1829
+ sessionStorage.removeItem(VALIDATED_OWNER_KEY);
1830
+ } catch {}
1831
+ }
1832
+ function validatedOnboardingOwner() {
1833
+ try {
1834
+ return sessionStorage.getItem(VALIDATED_OWNER_KEY);
1835
+ } catch {
1836
+ return null;
1837
+ }
1838
+ }
1839
+ function startOnboardingJourney(input) {
1840
+ const journey = {
1841
+ version: JOURNEY_VERSION,
1842
+ ownerId: input.ownerId,
1843
+ journeyId: `journey_${createRandomId()}`,
1844
+ intent: input.intent,
1845
+ entryPoint: input.entryPoint,
1846
+ step: input.step ?? "profile",
1847
+ ...input.origin === void 0 ? {} : { origin: input.origin }
1848
+ };
1849
+ markOnboardingOwnerValidated(journey.ownerId);
1850
+ saveOnboardingJourney(journey);
1851
+ return journey;
1852
+ }
1853
+ function loadOnboardingJourney(expectedOwnerId) {
1854
+ try {
1855
+ const raw = sessionStorage.getItem(JOURNEY_KEY);
1856
+ if (raw === null) return null;
1857
+ if (raw.length > MAX_STORED_JOURNEY_LENGTH) return discardInvalidJourney();
1858
+ const value = JSON.parse(raw);
1859
+ if (!isValidJourney(value)) return discardInvalidJourney();
1860
+ if (expectedOwnerId !== void 0 && value.ownerId !== expectedOwnerId) return discardInvalidJourney();
1861
+ if (expectedOwnerId !== void 0) markOnboardingOwnerValidated(expectedOwnerId);
1862
+ return value;
1863
+ } catch {
1864
+ return discardInvalidJourney();
1865
+ }
1866
+ }
1867
+ function saveOnboardingJourney(journey) {
1868
+ if (!isValidJourney(journey)) {
1869
+ discardInvalidJourney();
1870
+ return;
1871
+ }
1872
+ try {
1873
+ sessionStorage.setItem(JOURNEY_KEY, JSON.stringify(journey));
1874
+ } catch {}
1875
+ }
1876
+ function currentOnboardingJourneyId() {
1877
+ const journey = loadOnboardingJourney();
1878
+ const owner = validatedOnboardingOwner();
1879
+ return journey !== null && owner !== null && journey.ownerId === owner ? journey.journeyId : void 0;
1880
+ }
1881
+ /** Stop observation attribution while no authenticated owner is established. */
1882
+ function invalidateOnboardingJourneyObservation() {
1883
+ clearValidatedOnboardingOwner();
1884
+ }
1885
+ /** Where the journey stands — the pure position apps map to their routes. */
1886
+ function onboardingJourneyPosition(journey) {
1887
+ const proof = journey.origin?.kind === "proof";
1888
+ if (journey.organizationId !== void 0) return {
1889
+ intent: journey.intent,
1890
+ step: "provisioning",
1891
+ organizationId: journey.organizationId,
1892
+ proof
1893
+ };
1894
+ return {
1895
+ intent: journey.intent,
1896
+ step: journey.step,
1897
+ proof
1898
+ };
1899
+ }
1900
+ /**
1901
+ * The interruption-recovery decision (behavior map §4): a journey that does
1902
+ * not match the ready destination the app just rendered must resume; a
1903
+ * matching one needs no recovery. Returns the journey to resume, or null.
1904
+ */
1905
+ function activeOnboardingRecovery(ownerId, renderedReadyDestination) {
1906
+ const journey = loadOnboardingJourney(ownerId);
1907
+ if (journey === null) return null;
1908
+ return (renderedReadyDestination?.kind === "personal" ? journey.intent === "personal" && journey.step === "provisioning" && journey.organizationId === void 0 : renderedReadyDestination?.kind === "organization" && journey.intent === "organization" && journey.step === "provisioning" && journey.organizationId === renderedReadyDestination.organizationId) ? null : journey;
1909
+ }
1910
+ function acknowledgeOnboardingDestination(destination, ownerId) {
1911
+ const journey = loadOnboardingJourney(ownerId);
1912
+ if (journey === null) return false;
1913
+ if (!(destination.kind === "personal" ? journey.intent === "personal" && journey.step === "provisioning" : journey.intent === "organization" && journey.step === "provisioning" && journey.organizationId === destination.organizationId)) return false;
1914
+ clearOnboardingJourney();
1915
+ return true;
1916
+ }
1917
+ function clearOnboardingJourney() {
1918
+ clearValidatedOnboardingOwner();
1919
+ try {
1920
+ sessionStorage.removeItem(JOURNEY_KEY);
1921
+ } catch {}
1922
+ }
1923
+ function isValidJourney(value) {
1924
+ if (!isRecord(value) || value.version !== JOURNEY_VERSION) return false;
1925
+ if (!hasOnlyKeys(value, [
1926
+ "version",
1927
+ "ownerId",
1928
+ "journeyId",
1929
+ "intent",
1930
+ "entryPoint",
1931
+ "origin",
1932
+ "step",
1933
+ "profile",
1934
+ "organization",
1935
+ "stableHandle",
1936
+ "organizationId"
1937
+ ])) return false;
1938
+ if (!isBoundedId(value.ownerId)) return false;
1939
+ if (!isBoundedId(value.journeyId) || !value.journeyId.startsWith("journey_")) return false;
1940
+ if (value.intent !== "personal" && value.intent !== "organization") return false;
1941
+ if (value.entryPoint !== "signup" && value.entryPoint !== "dashboard") return false;
1942
+ if (value.step !== "profile" && value.step !== "organization" && value.step !== "provisioning") return false;
1943
+ if (value.origin !== void 0 && !isOrigin(value.origin)) return false;
1944
+ if (value.profile !== void 0 && !isProfileDraft(value.profile)) return false;
1945
+ if (value.organization !== void 0 && !isOrganizationDraft(value.organization)) return false;
1946
+ if (value.stableHandle !== void 0 && !isDraftText(value.stableHandle)) return false;
1947
+ if (value.organizationId !== void 0 && !isBoundedId(value.organizationId)) return false;
1948
+ if (value.organization !== void 0 && value.stableHandle !== value.organization.handle) return false;
1949
+ if (value.stableHandle !== void 0 && value.organization === void 0) return false;
1950
+ if (value.intent === "personal" && (value.step === "organization" || value.organization !== void 0 || value.stableHandle !== void 0)) return false;
1951
+ if (value.step === "organization" && value.intent !== "organization") return false;
1952
+ if (value.organizationId !== void 0 && value.intent !== "organization") return false;
1953
+ if (value.organizationId !== void 0 && value.step !== "provisioning") return false;
1954
+ return true;
1955
+ }
1956
+ function isOrigin(value) {
1957
+ if (!isRecord(value)) return false;
1958
+ if (value.kind === "personal") return hasOnlyKeys(value, ["kind"]);
1959
+ if (value.kind === "organization") return hasOnlyKeys(value, ["kind", "organizationId"]) && isBoundedId(value.organizationId);
1960
+ return value.kind === "proof" && hasOnlyKeys(value, ["kind", "organizationId"]) && (value.organizationId === void 0 || isBoundedId(value.organizationId));
1961
+ }
1962
+ function isProfileDraft(value) {
1963
+ if (!isRecord(value) || !hasOnlyKeys(value, [
1964
+ "displayName",
1965
+ "country",
1966
+ "withdrawalAddress",
1967
+ "username",
1968
+ "payoutAddresses"
1969
+ ]) || !isDraftText(value.displayName) || !isDraftText(value.country) || !isDraftText(value.withdrawalAddress)) return false;
1970
+ if (value.username !== void 0 && !isDraftText(value.username)) return false;
1971
+ if (value.payoutAddresses !== void 0 && !isPayoutDraftList(value.payoutAddresses)) return false;
1972
+ return true;
1973
+ }
1974
+ function isPayoutDraftList(value) {
1975
+ return Array.isArray(value) && value.length <= MAX_PAYOUT_DRAFT_ENTRIES && value.every((entry) => isRecord(entry) && hasOnlyKeys(entry, ["chain", "address"]) && (entry.chain === "evm" || entry.chain === "solana" || entry.chain === "starknet") && isDraftText(entry.address));
1976
+ }
1977
+ function isOrganizationDraft(value) {
1978
+ if (!isRecord(value) || !hasOnlyKeys(value, [
1979
+ "name",
1980
+ "handle",
1981
+ "country",
1982
+ "bio",
1983
+ "size"
1984
+ ]) || !isDraftText(value.name) || !isDraftText(value.handle) || !isDraftText(value.country)) return false;
1985
+ if (value.bio !== void 0 && !isDraftText(value.bio)) return false;
1986
+ if (value.size !== void 0 && !isDraftText(value.size)) return false;
1987
+ return true;
1988
+ }
1989
+ function isDraftText(value) {
1990
+ return typeof value === "string" && value.length <= MAX_DRAFT_TEXT_LENGTH;
1991
+ }
1992
+ function isBoundedId(value) {
1993
+ return typeof value === "string" && value.length > 0 && value.length <= MAX_ID_LENGTH;
1994
+ }
1995
+ function discardInvalidJourney() {
1996
+ clearValidatedOnboardingOwner();
1997
+ try {
1998
+ sessionStorage.removeItem(JOURNEY_KEY);
1999
+ } catch {}
2000
+ return null;
2001
+ }
2002
+ function createRandomId() {
2003
+ if (typeof globalThis.crypto?.randomUUID === "function") return globalThis.crypto.randomUUID();
2004
+ return Math.random().toString(36).slice(2);
2005
+ }
2006
+ function isRecord(value) {
2007
+ return typeof value === "object" && value !== null && !Array.isArray(value);
2008
+ }
2009
+ function hasOnlyKeys(value, allowed) {
2010
+ return Object.keys(value).every((key) => allowed.includes(key));
2011
+ }
2012
+ //#endregion
2013
+ //#region src/headless/routing/resolve-post-auth-destination.ts
2014
+ function resolvePostAuthDestination(inputs) {
2015
+ if (inputs.journey !== null) return {
2016
+ kind: "journey",
2017
+ position: onboardingJourneyPosition(inputs.journey)
2018
+ };
2019
+ if (!inputs.profileOnboarded) return { kind: "selectUserType" };
2020
+ if (!inputs.accountReady) return { kind: "provisioning" };
2021
+ return {
2022
+ kind: "dashboard",
2023
+ entity: inputs.lastActingEntity ?? { kind: "personal" }
2024
+ };
2025
+ }
2026
+ //#endregion
2027
+ //#region src/internal/use-debounced-value.ts
2028
+ /** The value as of `delayMs` after its last change (initial value immediate). */
2029
+ function useDebouncedValue(value, delayMs) {
2030
+ const [debounced, setDebounced] = useState(value);
2031
+ useEffect(() => {
2032
+ const timer = setTimeout(() => setDebounced(value), delayMs);
2033
+ return () => clearTimeout(timer);
2034
+ }, [value, delayMs]);
2035
+ return debounced;
2036
+ }
2037
+ //#endregion
2038
+ //#region src/headless/onboarding/use-capxul-username-availability.ts
2039
+ /**
2040
+ * #1062: debounced availability probe for the username the user is typing.
2041
+ * Disabled until the candidate reaches the 3-character floor; a malformed or
2042
+ * reserved candidate surfaces as the query's error (INVALID_INPUT with the
2043
+ * boundary's message), not as `available: false` — only a username someone
2044
+ * else owns is "taken".
2045
+ */
2046
+ function useCapxulUsernameAvailability(username, options) {
2047
+ const client = useCapxulClientOrNull();
2048
+ const candidate = useDebouncedValue(username.trim(), options?.debounceMs ?? 300);
2049
+ return useQuery({
2050
+ queryKey: capxulKeys.usernameAvailability(candidate),
2051
+ queryFn: async () => {
2052
+ const bootstrappedClient = requireBootstrappedClient(client, "identity.usernameAvailable");
2053
+ return unwrapCapxulResult(await bootstrappedClient.identity.usernameAvailable(candidate), bootstrappedClient._internal.telemetry);
2054
+ },
2055
+ enabled: client !== null && candidate.length >= 3 && (options?.enabled ?? true)
2056
+ });
2057
+ }
2058
+ //#endregion
2059
+ //#region src/headless/onboarding/use-capxul-persist-payout-entries.ts
2060
+ const CHAIN_TO_NETWORK = {
2061
+ evm: "base-sepolia",
2062
+ solana: "solana-devnet",
2063
+ starknet: "starknet-sepolia"
2064
+ };
2065
+ /**
2066
+ * #1063: persist onboarding payout entries as SELF-owned wallet destinations.
2067
+ * Per-entry failures are collected, never thrown — payout persistence is
2068
+ * additive to the journey (identity creation is the invariant; behavior map
2069
+ * §3 J1), so callers surface `failures` as a non-blocking notice.
2070
+ */
2071
+ function useCapxulPersistPayoutEntries() {
2072
+ const client = useCapxulClientOrNull();
2073
+ const queryClient = useQueryClient();
2074
+ return useMutation({
2075
+ mutationFn: async (entries) => {
2076
+ const bootstrappedClient = requireBootstrappedClient(client, "destinations.add");
2077
+ const failures = [];
2078
+ let persisted = 0;
2079
+ for (const entry of entries) {
2080
+ const result = await bootstrappedClient.destinations.add({
2081
+ target: { self: true },
2082
+ kind: "external_account",
2083
+ payload: {
2084
+ network: CHAIN_TO_NETWORK[entry.chain],
2085
+ address: entry.address
2086
+ }
2087
+ });
2088
+ if (result.ok) persisted += 1;
2089
+ else failures.push({
2090
+ entry,
2091
+ error: result.error
2092
+ });
2093
+ }
2094
+ return {
2095
+ persisted,
2096
+ failures
2097
+ };
2098
+ },
2099
+ onSuccess: async () => {
2100
+ await queryClient.invalidateQueries({ queryKey: capxulKeys.destinationsScope });
2101
+ }
2102
+ });
2103
+ }
2104
+ //#endregion
2105
+ //#region src/headless/media/use-capxul-image-upload.ts
2106
+ /**
2107
+ * #1061 / ADR-0014: the whole upload→record sequence as one mutation —
2108
+ * upload the blob, then bind the returned storage id to the profile image or
2109
+ * the org logo. The url is the resolved serving URL (re-read from queries,
2110
+ * never persisted by consumers).
2111
+ */
2112
+ function useCapxulImageUpload() {
2113
+ const client = useCapxulClientOrNull();
2114
+ const queryClient = useQueryClient();
2115
+ return useMutation({
2116
+ mutationFn: async ({ blob, target }) => {
2117
+ const bootstrappedClient = requireBootstrappedClient(client, "media.uploadImage");
2118
+ const media = bootstrappedClient.media;
2119
+ const telemetry = bootstrappedClient._internal.telemetry;
2120
+ const uploaded = unwrapCapxulResult(await media.uploadImage(blob), telemetry);
2121
+ if (target.kind === "profile") return { url: unwrapCapxulResult(await media.setProfileImage({ storageId: uploaded.storageId }), telemetry).imageUrl };
2122
+ return { url: unwrapCapxulResult(await media.setOrgLogo({
2123
+ orgId: target.orgId,
2124
+ storageId: uploaded.storageId
2125
+ }), telemetry).logoUrl };
2126
+ },
2127
+ onSuccess: async (_value, input) => {
2128
+ await queryClient.invalidateQueries({ queryKey: input.target.kind === "profile" ? capxulKeys.profile : capxulKeys.orgs });
2129
+ }
2130
+ });
2131
+ }
2132
+ //#endregion
2133
+ export { AddressBook, CapxulProvider, Destinations, InsightsSummary, PayrollRoster, RequestInbox, SendMoney, acknowledgeOnboardingDestination, activeOnboardingRecovery, capxulAccountScope, capxulOrgScope, clearOnboardingJourney, currentOnboardingJourneyId, invalidateOnboardingJourneyObservation, loadOnboardingJourney, onboardingJourneyPosition, resolvePostAuthDestination, saveOnboardingJourney, startOnboardingJourney, useCapxul, useCapxulAccountBalance, useCapxulAccountFund, useCapxulAccountLifecycle, useCapxulActivity, useCapxulAddAddressBookEntry, useCapxulAddDestination, useCapxulAddPayrollRosterLine, useCapxulAddressBook, useCapxulAddressBookEntry, useCapxulApproveInboxRequest, useCapxulAssignRole, useCapxulCancelPayment, useCapxulCancelRequest, useCapxulClientOrNull, useCapxulCompleteOrganizationOnboarding, useCapxulCompletePersonalOnboarding, useCapxulCreateOrg, useCapxulCurrentUser, useCapxulDeclineInboxRequest, useCapxulDestinations, useCapxulHideAddressBookEntry, useCapxulImageUpload, useCapxulInbox, useCapxulInsightsHistory, useCapxulInsightsSummary, useCapxulInviteMember, useCapxulIssueRequest, useCapxulLabelAddressBookEntry, useCapxulOfframpQuote, useCapxulOfframpStatus, useCapxulOrg, useCapxulOrgDeployRoles, useCapxulOrgLifecycle, useCapxulOrgMembers, useCapxulOrgRoles, useCapxulOrgTreasury, useCapxulOrganizationAccount, useCapxulOrganizationAuditLog, useCapxulOrgs, useCapxulPay, useCapxulPayment, useCapxulPayments, useCapxulPayout, useCapxulPayrollRoster, useCapxulPersistPayoutEntries, useCapxulProfile, useCapxulReconcileRequests, useCapxulRemoveDestination, useCapxulRemoveMember, useCapxulRemovePayrollRosterLine, useCapxulRequest, useCapxulRequests, useCapxulRunPayroll, useCapxulSession, useCapxulSignIn, useCapxulSignOut, useCapxulSubAccountCreate, useCapxulSubAccountDelete, useCapxulSubAccountRename, useCapxulSubAccountsList, useCapxulSwitchActingEntity, useCapxulTransfer, useCapxulUnhideAddressBookEntry, useCapxulUpdatePayrollRosterLine, useCapxulUsernameAvailability, useCapxulVerifyOtp, useCapxulWithdraw };
1800
2134
 
1801
2135
  //# sourceMappingURL=index.mjs.map