@capxul/sdk 0.1.0-alpha.8 → 0.1.0-alpha.9

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/client.cjs CHANGED
@@ -1027,7 +1027,68 @@ function createAuthClient(config = {}) {
1027
1027
  ];
1028
1028
  }
1029
1029
  }
1030
- return [null, session];
1030
+ if (!dataClient) {
1031
+ return [
1032
+ new CapxulError({
1033
+ code: "NOT_AUTHENTICATED",
1034
+ message: "Auth bootstrap requires an authenticated Convex data client."
1035
+ }),
1036
+ null
1037
+ ];
1038
+ }
1039
+ try {
1040
+ const resolution = await dataClient.mutation(
1041
+ api.authBootstrap.resolveAfterOtp,
1042
+ {
1043
+ email: session.email,
1044
+ sessionToken: session.token
1045
+ }
1046
+ );
1047
+ if (resolution.kind === "existing_member") {
1048
+ return [null, { ...resolution, session }];
1049
+ }
1050
+ return [null, { ...resolution, session }];
1051
+ } catch (cause) {
1052
+ return [fromConvexError(cause), null];
1053
+ }
1054
+ },
1055
+ completeBootstrap: async (input) => {
1056
+ const session = sessionStore.get();
1057
+ const data = dataClient ?? config.data;
1058
+ if (!session || !data) {
1059
+ return [
1060
+ new CapxulError({
1061
+ code: "INVALID_INPUT",
1062
+ message: "completeBootstrap requires the OTP-verified session that issued the bootstrap token."
1063
+ }),
1064
+ null
1065
+ ];
1066
+ }
1067
+ if (input.signerProvider.kind !== "local-private-key") {
1068
+ return [
1069
+ new CapxulError({
1070
+ code: "INVALID_INPUT",
1071
+ message: "completeBootstrap currently supports local-private-key signer providers only."
1072
+ }),
1073
+ null
1074
+ ];
1075
+ }
1076
+ try {
1077
+ const result = await data.mutation(api.authBootstrap.completeBootstrap, {
1078
+ bootstrapToken: input.bootstrapToken,
1079
+ sessionToken: session.token,
1080
+ username: input.username,
1081
+ displayName: input.displayName,
1082
+ countryCode: input.countryCode,
1083
+ signerProvider: input.signerProvider
1084
+ });
1085
+ return [null, { kind: "authenticated", session, ...result }];
1086
+ } catch (cause) {
1087
+ return [
1088
+ fromConvexError(cause),
1089
+ null
1090
+ ];
1091
+ }
1031
1092
  },
1032
1093
  getSession: async () => [null, sessionStore.get()],
1033
1094
  signOut: async () => {
@@ -2386,7 +2447,7 @@ function createAuthFlowMachine(client) {
2386
2447
  }),
2387
2448
  verifyOtp: xstate.fromPromise(
2388
2449
  async ({ input, signal }) => {
2389
- const [error, session] = await client.auth.verifyOtp(
2450
+ const [error, result] = await client.auth.verifyOtp(
2390
2451
  {
2391
2452
  email: input.email,
2392
2453
  otp: input.code
@@ -2394,7 +2455,14 @@ function createAuthFlowMachine(client) {
2394
2455
  { signal }
2395
2456
  );
2396
2457
  if (error) throw error;
2397
- return session;
2458
+ if (result.kind === "bootstrap_required") {
2459
+ throw new CapxulError({
2460
+ code: "ACTION_REQUIRED",
2461
+ message: "OTP verified but auth bootstrap is required. Use createAuthBootstrapFlowMachine for product sign-up.",
2462
+ details: { reason: result.reason }
2463
+ });
2464
+ }
2465
+ return result.session;
2398
2466
  }
2399
2467
  ),
2400
2468
  signOut: xstate.fromPromise(async () => {
@@ -2642,6 +2710,345 @@ function emailDomain(email) {
2642
2710
  const domain = email.split("@")[1]?.trim().toLowerCase();
2643
2711
  return domain || "unknown";
2644
2712
  }
2713
+ var initialContext = {
2714
+ email: null,
2715
+ code: null,
2716
+ username: null,
2717
+ signerProvider: null,
2718
+ bootstrapToken: null,
2719
+ bootstrapReason: null,
2720
+ session: null,
2721
+ account: null,
2722
+ safe: null,
2723
+ error: null
2724
+ };
2725
+ function createAuthBootstrapFlowMachine(client) {
2726
+ return xstate.setup({
2727
+ types: {},
2728
+ actors: {
2729
+ sendOtp: xstate.fromPromise(async ({ input, signal }) => {
2730
+ const [error] = await client.auth.sendOtp(
2731
+ { email: input.email },
2732
+ { signal }
2733
+ );
2734
+ if (error) throw error;
2735
+ }),
2736
+ verifyOtp: xstate.fromPromise(
2737
+ async ({ input, signal }) => {
2738
+ const [error, result] = await client.auth.verifyOtp(
2739
+ { email: input.email, otp: input.code },
2740
+ { signal }
2741
+ );
2742
+ if (error) throw error;
2743
+ return result;
2744
+ }
2745
+ ),
2746
+ completeBootstrap: xstate.fromPromise(async ({ input }) => {
2747
+ const [error, result] = await client.auth.completeBootstrap(input);
2748
+ if (error) throw error;
2749
+ return result;
2750
+ }),
2751
+ signOut: xstate.fromPromise(async () => {
2752
+ const [error] = await client.auth.signOut();
2753
+ if (error) throw error;
2754
+ })
2755
+ },
2756
+ actions: {
2757
+ trackOtpRequested: ({ context }) => {
2758
+ if (!context.email) return;
2759
+ track("auth_otp_requested", {
2760
+ email_domain: emailDomain2(context.email)
2761
+ });
2762
+ },
2763
+ trackFailed: ({ event }) => {
2764
+ track("auth_failed", {
2765
+ auth_type: "email_otp",
2766
+ reason: errorFromEvent2(event).code
2767
+ });
2768
+ },
2769
+ trackTimeoutFailed: () => {
2770
+ track("auth_failed", {
2771
+ auth_type: "email_otp",
2772
+ reason: "timeout"
2773
+ });
2774
+ },
2775
+ trackVerified: () => {
2776
+ track("auth_verified", { auth_type: "email_otp" });
2777
+ },
2778
+ trackBootstrapRequired: ({ context }) => {
2779
+ track("auth_verified", {
2780
+ auth_type: "email_otp",
2781
+ auth_mode: context.bootstrapReason ?? "bootstrap_required"
2782
+ });
2783
+ },
2784
+ identifyAndTrack: ({ context }) => {
2785
+ if (!context.session) return;
2786
+ identify(context.session.authUserId, {
2787
+ email_domain: emailDomain2(context.session.email)
2788
+ });
2789
+ track("auth_identified", {
2790
+ email_domain: emailDomain2(context.session.email)
2791
+ });
2792
+ },
2793
+ trackSignedOut: () => {
2794
+ track("auth_signed_out");
2795
+ }
2796
+ }
2797
+ }).createMachine({
2798
+ id: "authBootstrap",
2799
+ initial: "email",
2800
+ context: initialContext,
2801
+ states: {
2802
+ email: {
2803
+ on: {
2804
+ ENTER_EMAIL: {
2805
+ actions: xstate.assign({
2806
+ email: ({ event }) => event.email,
2807
+ error: () => null
2808
+ })
2809
+ },
2810
+ REQUEST_OTP: { target: "sending_otp" }
2811
+ }
2812
+ },
2813
+ sending_otp: {
2814
+ invoke: {
2815
+ src: "sendOtp",
2816
+ input: ({ context }) => ({ email: requireEmail2(context) }),
2817
+ onDone: { target: "otp_requested", actions: "trackOtpRequested" },
2818
+ onError: {
2819
+ target: "otp_requested",
2820
+ actions: [
2821
+ xstate.assign({ error: ({ event }) => errorFromEvent2(event) }),
2822
+ "trackFailed"
2823
+ ]
2824
+ }
2825
+ },
2826
+ after: {
2827
+ [FLOW_INVOKE_TIMEOUT_MS]: {
2828
+ target: "otp_requested",
2829
+ actions: [
2830
+ xstate.assign({ error: () => timeoutError2("sending_otp") }),
2831
+ "trackTimeoutFailed"
2832
+ ]
2833
+ }
2834
+ }
2835
+ },
2836
+ otp_requested: {
2837
+ on: {
2838
+ ENTER_OTP: {
2839
+ actions: xstate.assign({
2840
+ code: ({ event }) => event.code,
2841
+ error: () => null
2842
+ })
2843
+ },
2844
+ VERIFY_OTP: { target: "verifying_otp" },
2845
+ BACK: { target: "email" },
2846
+ RESET: { target: "email", actions: xstate.assign(() => initialContext) }
2847
+ }
2848
+ },
2849
+ verifying_otp: {
2850
+ invoke: {
2851
+ src: "verifyOtp",
2852
+ input: ({ context }) => ({
2853
+ email: requireEmail2(context),
2854
+ code: requireCode(context)
2855
+ }),
2856
+ onDone: [
2857
+ {
2858
+ guard: ({ event }) => event.output.kind === "existing_member",
2859
+ target: "authenticated",
2860
+ actions: [
2861
+ xstate.assign({
2862
+ session: ({ event }) => event.output.session,
2863
+ account: ({ event }) => event.output.kind === "existing_member" ? event.output.account : null,
2864
+ username: ({ event }) => event.output.kind === "existing_member" ? event.output.username : null,
2865
+ safe: ({ event }) => event.output.kind === "existing_member" ? event.output.safe : null,
2866
+ email: () => null,
2867
+ error: () => null
2868
+ }),
2869
+ "trackVerified",
2870
+ "identifyAndTrack"
2871
+ ]
2872
+ },
2873
+ {
2874
+ target: "bootstrap_required",
2875
+ actions: [
2876
+ xstate.assign({
2877
+ session: ({ event }) => event.output.session,
2878
+ bootstrapToken: ({ event }) => event.output.kind === "bootstrap_required" ? event.output.bootstrapToken : null,
2879
+ bootstrapReason: ({ event }) => event.output.kind === "bootstrap_required" ? event.output.reason : null,
2880
+ username: ({ event }) => event.output.kind === "bootstrap_required" ? event.output.username ?? null : null,
2881
+ email: ({ event }) => event.output.kind === "bootstrap_required" ? event.output.email : null,
2882
+ error: () => null
2883
+ }),
2884
+ "trackVerified",
2885
+ "trackBootstrapRequired"
2886
+ ]
2887
+ }
2888
+ ],
2889
+ onError: {
2890
+ target: "otp_requested",
2891
+ actions: [
2892
+ xstate.assign({ error: ({ event }) => errorFromEvent2(event) }),
2893
+ "trackFailed"
2894
+ ]
2895
+ }
2896
+ },
2897
+ after: {
2898
+ [FLOW_INVOKE_TIMEOUT_MS]: {
2899
+ target: "otp_requested",
2900
+ actions: [
2901
+ xstate.assign({ error: () => timeoutError2("verifying_otp") }),
2902
+ "trackTimeoutFailed"
2903
+ ]
2904
+ }
2905
+ }
2906
+ },
2907
+ bootstrap_required: {
2908
+ on: {
2909
+ ENTER_USERNAME: {
2910
+ actions: xstate.assign({
2911
+ username: ({ event }) => event.username,
2912
+ error: () => null
2913
+ })
2914
+ },
2915
+ ENTER_SIGNER_PROVIDER: {
2916
+ actions: xstate.assign({
2917
+ signerProvider: ({ event }) => event.signerProvider,
2918
+ error: () => null
2919
+ })
2920
+ },
2921
+ COMPLETE_BOOTSTRAP: { target: "completing_bootstrap" },
2922
+ BACK: { target: "otp_requested" },
2923
+ RESET: { target: "email", actions: xstate.assign(() => initialContext) }
2924
+ }
2925
+ },
2926
+ completing_bootstrap: {
2927
+ invoke: {
2928
+ src: "completeBootstrap",
2929
+ input: ({ context }) => ({
2930
+ bootstrapToken: requireBootstrapToken(context),
2931
+ username: requireUsername(context),
2932
+ signerProvider: requireSignerProvider(context)
2933
+ }),
2934
+ onDone: {
2935
+ target: "authenticated",
2936
+ actions: [
2937
+ xstate.assign({
2938
+ session: ({ event }) => event.output.session,
2939
+ account: ({ event }) => event.output.account,
2940
+ username: ({ event }) => event.output.username,
2941
+ safe: ({ event }) => event.output.safe,
2942
+ bootstrapToken: () => null,
2943
+ bootstrapReason: () => null,
2944
+ signerProvider: () => null,
2945
+ email: () => null,
2946
+ error: () => null
2947
+ }),
2948
+ "identifyAndTrack"
2949
+ ]
2950
+ },
2951
+ onError: {
2952
+ target: "bootstrap_required",
2953
+ actions: [
2954
+ xstate.assign({ error: ({ event }) => errorFromEvent2(event) }),
2955
+ "trackFailed"
2956
+ ]
2957
+ }
2958
+ },
2959
+ after: {
2960
+ [FLOW_INVOKE_TIMEOUT_MS]: {
2961
+ target: "bootstrap_required",
2962
+ actions: [
2963
+ xstate.assign({ error: () => timeoutError2("completing_bootstrap") }),
2964
+ "trackTimeoutFailed"
2965
+ ]
2966
+ }
2967
+ }
2968
+ },
2969
+ authenticated: {
2970
+ on: {
2971
+ SIGN_OUT: { target: "signing_out" }
2972
+ }
2973
+ },
2974
+ signing_out: {
2975
+ invoke: {
2976
+ src: "signOut",
2977
+ onDone: {
2978
+ target: "email",
2979
+ actions: [
2980
+ xstate.assign(() => initialContext),
2981
+ "trackSignedOut"
2982
+ ]
2983
+ },
2984
+ onError: {
2985
+ target: "error",
2986
+ actions: xstate.assign({ error: ({ event }) => errorFromEvent2(event) })
2987
+ }
2988
+ }
2989
+ },
2990
+ error: {
2991
+ on: {
2992
+ RESET: { target: "email", actions: xstate.assign(() => initialContext) }
2993
+ }
2994
+ }
2995
+ }
2996
+ });
2997
+ }
2998
+ function requireEmail2(context) {
2999
+ if (!context.email) {
3000
+ throw Errors.invalidInput("email", "Auth bootstrap requires an email.");
3001
+ }
3002
+ return context.email;
3003
+ }
3004
+ function requireCode(context) {
3005
+ if (!context.code) {
3006
+ throw Errors.invalidInput("code", "Auth bootstrap requires an OTP code.");
3007
+ }
3008
+ return context.code;
3009
+ }
3010
+ function requireBootstrapToken(context) {
3011
+ if (!context.bootstrapToken) {
3012
+ throw Errors.invalidInput(
3013
+ "bootstrapToken",
3014
+ "Auth bootstrap requires a continuation token."
3015
+ );
3016
+ }
3017
+ return context.bootstrapToken;
3018
+ }
3019
+ function requireUsername(context) {
3020
+ if (!context.username) {
3021
+ throw Errors.invalidInput("username", "Auth bootstrap requires a username.");
3022
+ }
3023
+ return context.username;
3024
+ }
3025
+ function requireSignerProvider(context) {
3026
+ if (!context.signerProvider) {
3027
+ throw Errors.invalidInput(
3028
+ "signerProvider",
3029
+ "Auth bootstrap requires a signer provider."
3030
+ );
3031
+ }
3032
+ return context.signerProvider;
3033
+ }
3034
+ function errorFromEvent2(event) {
3035
+ const cause = typeof event === "object" && event !== null && "error" in event ? event.error : event;
3036
+ if (cause instanceof CapxulError || cause instanceof CapxulError2) {
3037
+ return cause;
3038
+ }
3039
+ return Errors.providerError("auth", "bootstrap", cause);
3040
+ }
3041
+ function timeoutError2(state) {
3042
+ return Errors.providerError(
3043
+ "auth",
3044
+ "bootstrap",
3045
+ new Error(`timeout: ${state} exceeded ${FLOW_INVOKE_TIMEOUT_MS}ms`)
3046
+ );
3047
+ }
3048
+ function emailDomain2(email) {
3049
+ const domain = email.split("@")[1]?.trim().toLowerCase();
3050
+ return domain || "unknown";
3051
+ }
2645
3052
  function createProvisioningMachine(client) {
2646
3053
  return xstate.setup({
2647
3054
  types: {},
@@ -2714,13 +3121,13 @@ function createProvisioningMachine(client) {
2714
3121
  },
2715
3122
  onError: {
2716
3123
  target: "error",
2717
- actions: xstate.assign({ error: ({ event }) => errorFromEvent2(event) })
3124
+ actions: xstate.assign({ error: ({ event }) => errorFromEvent3(event) })
2718
3125
  }
2719
3126
  },
2720
3127
  after: {
2721
3128
  [FLOW_INVOKE_TIMEOUT_MS]: {
2722
3129
  target: "error",
2723
- actions: xstate.assign({ error: () => timeoutError2() })
3130
+ actions: xstate.assign({ error: () => timeoutError3() })
2724
3131
  }
2725
3132
  }
2726
3133
  },
@@ -2738,7 +3145,7 @@ function createProvisioningMachine(client) {
2738
3145
  * this payload on its `onDone` transition and branches via guards
2739
3146
  * on `event.output.error`.
2740
3147
  */
2741
- output: ({ context }) => context.error ? { error: context.error } : context.account ? { account: context.account } : { error: timeoutError2() }
3148
+ output: ({ context }) => context.error ? { error: context.error } : context.account ? { account: context.account } : { error: timeoutError3() }
2742
3149
  });
2743
3150
  }
2744
3151
  function requireProvisionInput(context) {
@@ -2750,13 +3157,13 @@ function requireProvisionInput(context) {
2750
3157
  }
2751
3158
  return context.input;
2752
3159
  }
2753
- function errorFromEvent2(event) {
3160
+ function errorFromEvent3(event) {
2754
3161
  const cause = typeof event === "object" && event !== null && "error" in event ? event.error : event;
2755
3162
  if (cause instanceof CapxulError) return cause;
2756
3163
  if (cause instanceof CapxulError2) return cause;
2757
3164
  return Errors.providerError("provisioning", "flow", cause);
2758
3165
  }
2759
- function timeoutError2() {
3166
+ function timeoutError3() {
2760
3167
  return Errors.providerError(
2761
3168
  "provisioning",
2762
3169
  "flow",
@@ -2849,7 +3256,7 @@ function createOnboardingFlowMachine(client) {
2849
3256
  error: ({ event }) => extractChildErrorOrFallback(event)
2850
3257
  }),
2851
3258
  assignChildThrown: xstate.assign({
2852
- error: ({ event }) => errorFromEvent3(event)
3259
+ error: ({ event }) => errorFromEvent4(event)
2853
3260
  }),
2854
3261
  assignAccountFromChild: xstate.assign({
2855
3262
  account: ({ event }) => extractChildAccountOrNull(event)
@@ -3011,7 +3418,7 @@ function extractChildAccountOrNull(event) {
3011
3418
  if (output && "account" in output && output.account) return output.account;
3012
3419
  return null;
3013
3420
  }
3014
- function errorFromEvent3(event) {
3421
+ function errorFromEvent4(event) {
3015
3422
  const cause = typeof event === "object" && event !== null && "error" in event ? event.error : event;
3016
3423
  if (cause instanceof CapxulError) return cause;
3017
3424
  if (cause instanceof CapxulError2) return cause;
@@ -3043,6 +3450,7 @@ function createCapxulClient(config = {}) {
3043
3450
  const client = clientWithoutFlows;
3044
3451
  client.flows = {
3045
3452
  auth: () => createAuthFlowMachine(client),
3453
+ authBootstrap: () => createAuthBootstrapFlowMachine(client),
3046
3454
  onboarding: () => createOnboardingFlowMachine(client),
3047
3455
  provisioning: () => createProvisioningMachine(client)
3048
3456
  };
package/dist/client.d.cts CHANGED
@@ -1,6 +1,6 @@
1
1
  import 'viem';
2
2
  import 'xstate';
3
- export { f as CapxulAuthConfig, C as CapxulClient, g as CapxulConfig, h as CapxulDataClient, i as CapxulFlowFactories, j as CapxulSigningConfig, K as createCapxulClient } from './client-DDAVWtzJ.cjs';
3
+ export { l as CapxulAuthConfig, a as CapxulClient, m as CapxulConfig, n as CapxulDataClient, o as CapxulFlowFactories, p as CapxulSigningConfig, a0 as createCapxulClient } from './client-DG6ODWu6.cjs';
4
4
  import './next-action-DkrwXYay.cjs';
5
5
  import './errors-GgKrSUKp.cjs';
6
6
  import './types-hfcOE7Oi.cjs';
package/dist/client.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import 'viem';
2
2
  import 'xstate';
3
- export { f as CapxulAuthConfig, C as CapxulClient, g as CapxulConfig, h as CapxulDataClient, i as CapxulFlowFactories, j as CapxulSigningConfig, K as createCapxulClient } from './client-ByzDfG98.js';
3
+ export { l as CapxulAuthConfig, a as CapxulClient, m as CapxulConfig, n as CapxulDataClient, o as CapxulFlowFactories, p as CapxulSigningConfig, a0 as createCapxulClient } from './client-ChZrdf3R.js';
4
4
  import './next-action-DkrwXYay.js';
5
5
  import './errors-QHD5Tlok.js';
6
6
  import './types-PM4AQRLP.js';