@evervault/react-native 2.0.0

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.
Files changed (177) hide show
  1. package/README.md +63 -0
  2. package/android/app/build/generated/source/codegen/java/com/facebook/fbreact/specs/NativeEvervaultSpec.java +60 -0
  3. package/android/app/build/generated/source/codegen/java/com/facebook/fbreact/specs/NativeRNCWebViewModuleSpec.java +42 -0
  4. package/android/app/build/generated/source/codegen/java/com/facebook/react/viewmanagers/RNCWebViewManagerDelegate.java +294 -0
  5. package/android/app/build/generated/source/codegen/java/com/facebook/react/viewmanagers/RNCWebViewManagerInterface.java +104 -0
  6. package/android/app/build/generated/source/codegen/jni/CMakeLists.txt +36 -0
  7. package/android/app/build/generated/source/codegen/jni/NativeEvervaultSpec-generated.cpp +62 -0
  8. package/android/app/build/generated/source/codegen/jni/NativeEvervaultSpec.h +31 -0
  9. package/android/app/build/generated/source/codegen/jni/RNCWebViewSpec-generated.cpp +38 -0
  10. package/android/app/build/generated/source/codegen/jni/RNCWebViewSpec.h +31 -0
  11. package/android/app/build/generated/source/codegen/jni/react/renderer/components/NativeEvervaultSpec/NativeEvervaultSpecJSI-generated.cpp +68 -0
  12. package/android/app/build/generated/source/codegen/jni/react/renderer/components/NativeEvervaultSpec/NativeEvervaultSpecJSI.h +112 -0
  13. package/android/app/build/generated/source/codegen/jni/react/renderer/components/RNCWebViewSpec/ComponentDescriptors.cpp +22 -0
  14. package/android/app/build/generated/source/codegen/jni/react/renderer/components/RNCWebViewSpec/ComponentDescriptors.h +24 -0
  15. package/android/app/build/generated/source/codegen/jni/react/renderer/components/RNCWebViewSpec/EventEmitters.cpp +241 -0
  16. package/android/app/build/generated/source/codegen/jni/react/renderer/components/RNCWebViewSpec/EventEmitters.h +263 -0
  17. package/android/app/build/generated/source/codegen/jni/react/renderer/components/RNCWebViewSpec/Props.cpp +99 -0
  18. package/android/app/build/generated/source/codegen/jni/react/renderer/components/RNCWebViewSpec/Props.h +488 -0
  19. package/android/app/build/generated/source/codegen/jni/react/renderer/components/RNCWebViewSpec/RNCWebViewSpecJSI-generated.cpp +35 -0
  20. package/android/app/build/generated/source/codegen/jni/react/renderer/components/RNCWebViewSpec/RNCWebViewSpecJSI.h +76 -0
  21. package/android/app/build/generated/source/codegen/jni/react/renderer/components/RNCWebViewSpec/ShadowNodes.cpp +17 -0
  22. package/android/app/build/generated/source/codegen/jni/react/renderer/components/RNCWebViewSpec/ShadowNodes.h +32 -0
  23. package/android/app/build/generated/source/codegen/jni/react/renderer/components/RNCWebViewSpec/States.cpp +16 -0
  24. package/android/app/build/generated/source/codegen/jni/react/renderer/components/RNCWebViewSpec/States.h +29 -0
  25. package/android/build.gradle +32 -0
  26. package/android/src/main/java/com/nativeevervault/EvervaultModule.kt +114 -0
  27. package/android/src/main/java/com/nativeevervault/EvervaultPackage.kt +29 -0
  28. package/build/Card/Cvc.d.ts +5 -0
  29. package/build/Card/Cvc.d.ts.map +1 -0
  30. package/build/Card/Cvc.test.d.ts +2 -0
  31. package/build/Card/Cvc.test.d.ts.map +1 -0
  32. package/build/Card/Expiry.d.ts +5 -0
  33. package/build/Card/Expiry.d.ts.map +1 -0
  34. package/build/Card/Holder.d.ts +5 -0
  35. package/build/Card/Holder.d.ts.map +1 -0
  36. package/build/Card/Number.d.ts +5 -0
  37. package/build/Card/Number.d.ts.map +1 -0
  38. package/build/Card/Number.test.d.ts +2 -0
  39. package/build/Card/Number.test.d.ts.map +1 -0
  40. package/build/Card/Root.d.ts +36 -0
  41. package/build/Card/Root.d.ts.map +1 -0
  42. package/build/Card/Root.test.d.ts +2 -0
  43. package/build/Card/Root.test.d.ts.map +1 -0
  44. package/build/Card/index.d.ts +23 -0
  45. package/build/Card/index.d.ts.map +1 -0
  46. package/build/Card/schema.d.ts +30 -0
  47. package/build/Card/schema.d.ts.map +1 -0
  48. package/build/Card/types.d.ts +31 -0
  49. package/build/Card/types.d.ts.map +1 -0
  50. package/build/Card/utils.d.ts +17 -0
  51. package/build/Card/utils.d.ts.map +1 -0
  52. package/build/Card/utils.test.d.ts +2 -0
  53. package/build/Card/utils.test.d.ts.map +1 -0
  54. package/build/EvervaultProvider.d.ts +7 -0
  55. package/build/EvervaultProvider.d.ts.map +1 -0
  56. package/build/EvervaultProvider.test.d.ts +2 -0
  57. package/build/EvervaultProvider.test.d.ts.map +1 -0
  58. package/build/Input.d.ts +14 -0
  59. package/build/Input.d.ts.map +1 -0
  60. package/build/Input.test.d.ts +2 -0
  61. package/build/Input.test.d.ts.map +1 -0
  62. package/build/ThreeDSecure/Frame.d.ts +6 -0
  63. package/build/ThreeDSecure/Frame.d.ts.map +1 -0
  64. package/build/ThreeDSecure/Frame.test.d.ts +2 -0
  65. package/build/ThreeDSecure/Frame.test.d.ts.map +1 -0
  66. package/build/ThreeDSecure/Root.d.ts +10 -0
  67. package/build/ThreeDSecure/Root.d.ts.map +1 -0
  68. package/build/ThreeDSecure/Root.test.d.ts +2 -0
  69. package/build/ThreeDSecure/Root.test.d.ts.map +1 -0
  70. package/build/ThreeDSecure/config.d.ts +3 -0
  71. package/build/ThreeDSecure/config.d.ts.map +1 -0
  72. package/build/ThreeDSecure/context.d.ts +3 -0
  73. package/build/ThreeDSecure/context.d.ts.map +1 -0
  74. package/build/ThreeDSecure/index.d.ts +10 -0
  75. package/build/ThreeDSecure/index.d.ts.map +1 -0
  76. package/build/ThreeDSecure/session.d.ts +6 -0
  77. package/build/ThreeDSecure/session.d.ts.map +1 -0
  78. package/build/ThreeDSecure/session.test.d.ts +2 -0
  79. package/build/ThreeDSecure/session.test.d.ts.map +1 -0
  80. package/build/ThreeDSecure/types.d.ts +57 -0
  81. package/build/ThreeDSecure/types.d.ts.map +1 -0
  82. package/build/ThreeDSecure/useThreeDSecure.d.ts +3 -0
  83. package/build/ThreeDSecure/useThreeDSecure.d.ts.map +1 -0
  84. package/build/ThreeDSecure/useThreeDSecure.test.d.ts +2 -0
  85. package/build/ThreeDSecure/useThreeDSecure.test.d.ts.map +1 -0
  86. package/build/__mocks__/NativeEvervault.d.ts +4 -0
  87. package/build/__mocks__/NativeEvervault.d.ts.map +1 -0
  88. package/build/__mocks__/react-native-webview.d.ts +3 -0
  89. package/build/__mocks__/react-native-webview.d.ts.map +1 -0
  90. package/build/context.d.ts +9 -0
  91. package/build/context.d.ts.map +1 -0
  92. package/build/generated/ios/FBReactNativeSpec/FBReactNativeSpec-generated.mm +2321 -0
  93. package/build/generated/ios/FBReactNativeSpec/FBReactNativeSpec.h +2761 -0
  94. package/build/generated/ios/FBReactNativeSpecJSI-generated.cpp +2923 -0
  95. package/build/generated/ios/FBReactNativeSpecJSI.h +7718 -0
  96. package/build/generated/ios/NativeEvervaultSpec/NativeEvervaultSpec-generated.mm +74 -0
  97. package/build/generated/ios/NativeEvervaultSpec/NativeEvervaultSpec.h +80 -0
  98. package/build/generated/ios/NativeEvervaultSpecJSI-generated.cpp +68 -0
  99. package/build/generated/ios/NativeEvervaultSpecJSI.h +112 -0
  100. package/build/generated/ios/RCTModulesConformingToProtocolsProvider.h +18 -0
  101. package/build/generated/ios/RCTModulesConformingToProtocolsProvider.mm +33 -0
  102. package/build/generated/ios/RNCWebViewSpec/RNCWebViewSpec-generated.mm +46 -0
  103. package/build/generated/ios/RNCWebViewSpec/RNCWebViewSpec.h +62 -0
  104. package/build/generated/ios/RNCWebViewSpecJSI-generated.cpp +35 -0
  105. package/build/generated/ios/RNCWebViewSpecJSI.h +76 -0
  106. package/build/generated/ios/react/renderer/components/RNCWebViewSpec/ComponentDescriptors.cpp +22 -0
  107. package/build/generated/ios/react/renderer/components/RNCWebViewSpec/ComponentDescriptors.h +24 -0
  108. package/build/generated/ios/react/renderer/components/RNCWebViewSpec/EventEmitters.cpp +241 -0
  109. package/build/generated/ios/react/renderer/components/RNCWebViewSpec/EventEmitters.h +263 -0
  110. package/build/generated/ios/react/renderer/components/RNCWebViewSpec/Props.cpp +99 -0
  111. package/build/generated/ios/react/renderer/components/RNCWebViewSpec/Props.h +488 -0
  112. package/build/generated/ios/react/renderer/components/RNCWebViewSpec/RCTComponentViewHelpers.h +218 -0
  113. package/build/generated/ios/react/renderer/components/RNCWebViewSpec/ShadowNodes.cpp +17 -0
  114. package/build/generated/ios/react/renderer/components/RNCWebViewSpec/ShadowNodes.h +32 -0
  115. package/build/generated/ios/react/renderer/components/RNCWebViewSpec/States.cpp +16 -0
  116. package/build/generated/ios/react/renderer/components/RNCWebViewSpec/States.h +29 -0
  117. package/build/index.cjs.js +7732 -0
  118. package/build/index.cjs.js.map +1 -0
  119. package/build/index.d.ts +9 -0
  120. package/build/index.d.ts.map +1 -0
  121. package/build/index.esm.js +7702 -0
  122. package/build/sdk.d.ts +9 -0
  123. package/build/sdk.d.ts.map +1 -0
  124. package/build/sdk.test.d.ts +2 -0
  125. package/build/sdk.test.d.ts.map +1 -0
  126. package/build/specs/NativeEvervault.d.ts +59 -0
  127. package/build/specs/NativeEvervault.d.ts.map +1 -0
  128. package/build/useEvervault.d.ts +2 -0
  129. package/build/useEvervault.d.ts.map +1 -0
  130. package/build/useEvervault.test.d.ts +2 -0
  131. package/build/useEvervault.test.d.ts.map +1 -0
  132. package/build/utils.d.ts +15 -0
  133. package/build/utils.d.ts.map +1 -0
  134. package/ios/NativeEvervault-Bridging-Header.h +2 -0
  135. package/ios/NativeEvervault.mm +38 -0
  136. package/ios/NativeEvervault.swift +62 -0
  137. package/native-evervault.podspec +20 -0
  138. package/package.json +85 -0
  139. package/src/Card/Cvc.test.tsx +41 -0
  140. package/src/Card/Cvc.tsx +51 -0
  141. package/src/Card/Expiry.tsx +26 -0
  142. package/src/Card/Holder.tsx +27 -0
  143. package/src/Card/Number.test.tsx +55 -0
  144. package/src/Card/Number.tsx +47 -0
  145. package/src/Card/Root.test.tsx +260 -0
  146. package/src/Card/Root.tsx +118 -0
  147. package/src/Card/index.ts +28 -0
  148. package/src/Card/schema.ts +51 -0
  149. package/src/Card/types.ts +50 -0
  150. package/src/Card/utils.test.ts +271 -0
  151. package/src/Card/utils.ts +127 -0
  152. package/src/EvervaultProvider.test.tsx +24 -0
  153. package/src/EvervaultProvider.tsx +43 -0
  154. package/src/Input.test.tsx +138 -0
  155. package/src/Input.tsx +136 -0
  156. package/src/ThreeDSecure/Frame.test.tsx +87 -0
  157. package/src/ThreeDSecure/Frame.tsx +50 -0
  158. package/src/ThreeDSecure/Root.test.tsx +67 -0
  159. package/src/ThreeDSecure/Root.tsx +23 -0
  160. package/src/ThreeDSecure/config.ts +3 -0
  161. package/src/ThreeDSecure/context.ts +6 -0
  162. package/src/ThreeDSecure/index.ts +17 -0
  163. package/src/ThreeDSecure/session.test.ts +329 -0
  164. package/src/ThreeDSecure/session.ts +132 -0
  165. package/src/ThreeDSecure/types.ts +67 -0
  166. package/src/ThreeDSecure/useThreeDSecure.test.tsx +133 -0
  167. package/src/ThreeDSecure/useThreeDSecure.ts +47 -0
  168. package/src/__mocks__/NativeEvervault.ts +13 -0
  169. package/src/__mocks__/react-native-webview.tsx +6 -0
  170. package/src/context.ts +14 -0
  171. package/src/index.ts +21 -0
  172. package/src/sdk.test.ts +122 -0
  173. package/src/sdk.ts +71 -0
  174. package/src/specs/NativeEvervault.ts +67 -0
  175. package/src/useEvervault.test.tsx +31 -0
  176. package/src/useEvervault.ts +14 -0
  177. package/src/utils.ts +41 -0
@@ -0,0 +1,260 @@
1
+ import { PropsWithChildren } from "react";
2
+ import { EvervaultProvider } from "../EvervaultProvider";
3
+ import {
4
+ act,
5
+ fireEvent,
6
+ render,
7
+ userEvent,
8
+ waitFor,
9
+ } from "@testing-library/react-native";
10
+ import { Card } from "./Root";
11
+ import { ErrorBoundary } from "../utils";
12
+ import { CardHolder } from "./Holder";
13
+ import { CardNumber } from "./Number";
14
+ import { CardExpiry } from "./Expiry";
15
+ import { CardCvc } from "./Cvc";
16
+ import { encryptedValue } from "../__mocks__/NativeEvervault";
17
+
18
+ function wrapper({ children }: PropsWithChildren) {
19
+ return (
20
+ <EvervaultProvider teamId="team_123" appId="app_123">
21
+ {children}
22
+ </EvervaultProvider>
23
+ );
24
+ }
25
+
26
+ it("fails if not wrapped in an EvervaultProvider", () => {
27
+ const onError = vi.fn();
28
+ render(
29
+ <ErrorBoundary onError={onError}>
30
+ <Card />
31
+ </ErrorBoundary>
32
+ );
33
+
34
+ expect(onError).toHaveBeenCalledWith(
35
+ new Error("`useEvervault` must be used within an `EvervaultProvider`.")
36
+ );
37
+ });
38
+
39
+ it("calls onChange when mounted", async () => {
40
+ const onChange = vi.fn();
41
+ render(<Card onChange={onChange} />, { wrapper });
42
+
43
+ await waitFor(() => {
44
+ expect(onChange).toHaveBeenCalledWith({
45
+ card: {
46
+ name: null,
47
+ brand: null,
48
+ localBrands: [],
49
+ number: null,
50
+ lastFour: null,
51
+ bin: null,
52
+ expiry: null,
53
+ cvc: null,
54
+ },
55
+ isValid: true,
56
+ isComplete: true,
57
+ errors: {},
58
+ });
59
+ });
60
+ });
61
+
62
+ it("renders card components", async () => {
63
+ const onChange = vi.fn();
64
+ const { getByTestId } = render(
65
+ <Card onChange={onChange}>
66
+ <CardHolder testID="holder" />
67
+ </Card>,
68
+ { wrapper }
69
+ );
70
+
71
+ const holder = getByTestId("holder");
72
+ expect(holder).toBeOnTheScreen();
73
+ expect(holder).toHaveProp("placeholder", "Johnny Appleseed");
74
+
75
+ const user = userEvent.setup();
76
+ await user.type(holder, "John Doe");
77
+ expect(holder).toHaveProp("value", "John Doe");
78
+
79
+ expect(onChange).toHaveBeenLastCalledWith({
80
+ card: {
81
+ name: "John Doe",
82
+ brand: null,
83
+ localBrands: [],
84
+ number: null,
85
+ lastFour: null,
86
+ bin: null,
87
+ expiry: null,
88
+ cvc: null,
89
+ },
90
+ isValid: true,
91
+ isComplete: true,
92
+ errors: {},
93
+ });
94
+ });
95
+
96
+ it("calls onChange when the user types", async () => {
97
+ const onChange = vi.fn();
98
+ const { getByTestId } = render(
99
+ <Card onChange={onChange}>
100
+ <CardHolder testID="holder" />
101
+ <CardNumber testID="number" />
102
+ <CardExpiry testID="expiry" />
103
+ <CardCvc testID="cvc" />
104
+ </Card>,
105
+ { wrapper }
106
+ );
107
+
108
+ const holder = getByTestId("holder");
109
+ const number = getByTestId("number");
110
+ const expiry = getByTestId("expiry");
111
+ const cvc = getByTestId("cvc");
112
+
113
+ const user = userEvent.setup();
114
+ await user.type(holder, "John Doe");
115
+ await user.type(number, "4242 4242 4242 4242");
116
+ await user.type(expiry, "12 / 34");
117
+ await user.type(cvc, "123");
118
+
119
+ expect(onChange).toHaveBeenLastCalledWith({
120
+ card: {
121
+ name: "John Doe",
122
+ brand: "visa",
123
+ localBrands: [],
124
+ number: encryptedValue,
125
+ lastFour: "4242",
126
+ bin: "42424242",
127
+ expiry: {
128
+ month: "12",
129
+ year: "34",
130
+ },
131
+ cvc: encryptedValue,
132
+ },
133
+ isValid: true,
134
+ isComplete: true,
135
+ errors: {},
136
+ });
137
+ });
138
+
139
+ it("resets all fields when reset is called", async () => {
140
+ const ref = { current: null as any };
141
+ const onChange = vi.fn();
142
+ const { getByTestId } = render(
143
+ <Card ref={ref} onChange={onChange}>
144
+ <CardHolder testID="holder" />
145
+ </Card>,
146
+ { wrapper }
147
+ );
148
+
149
+ const holder = getByTestId("holder");
150
+ const user = userEvent.setup();
151
+
152
+ await user.type(holder, "John Doe");
153
+ expect(holder).toHaveProp("value", "John Doe");
154
+ expect(onChange).toHaveBeenLastCalledWith({
155
+ card: {
156
+ name: "John Doe",
157
+ brand: null,
158
+ localBrands: [],
159
+ number: null,
160
+ lastFour: null,
161
+ bin: null,
162
+ expiry: null,
163
+ cvc: null,
164
+ },
165
+ isValid: true,
166
+ isComplete: true,
167
+ errors: {},
168
+ });
169
+
170
+ await act(() => ref.current?.reset());
171
+
172
+ expect(holder).toHaveProp("value", "");
173
+ expect(onChange).toHaveBeenCalledWith({
174
+ card: {
175
+ name: null,
176
+ brand: null,
177
+ localBrands: [],
178
+ number: null,
179
+ lastFour: null,
180
+ bin: null,
181
+ expiry: null,
182
+ cvc: null,
183
+ },
184
+ isValid: false,
185
+ isComplete: false,
186
+ errors: {},
187
+ });
188
+ });
189
+
190
+ it("adds Required error when input is blurred without a value", async () => {
191
+ const onChange = vi.fn();
192
+ const { getByTestId } = render(
193
+ <Card onChange={onChange}>
194
+ <CardNumber testID="number" />
195
+ </Card>,
196
+ { wrapper }
197
+ );
198
+
199
+ const number = getByTestId("number");
200
+
201
+ const user = userEvent.setup();
202
+ await user.type(number, "");
203
+ fireEvent(number, "blur");
204
+
205
+ await waitFor(() => {
206
+ expect(onChange).toHaveBeenLastCalledWith({
207
+ card: {
208
+ name: null,
209
+ brand: null,
210
+ localBrands: [],
211
+ number: null,
212
+ lastFour: null,
213
+ bin: null,
214
+ expiry: null,
215
+ cvc: null,
216
+ },
217
+ isValid: false,
218
+ isComplete: false,
219
+ errors: {
220
+ number: "Required",
221
+ },
222
+ });
223
+ });
224
+ });
225
+
226
+ it("adds Invalid error when input is blurred with an invalid value", async () => {
227
+ const onChange = vi.fn();
228
+ const { getByTestId } = render(
229
+ <Card onChange={onChange}>
230
+ <CardNumber testID="number" />
231
+ </Card>,
232
+ { wrapper }
233
+ );
234
+
235
+ const number = getByTestId("number");
236
+
237
+ const user = userEvent.setup();
238
+ await user.type(number, "4242");
239
+ fireEvent(number, "blur");
240
+
241
+ await waitFor(() => {
242
+ expect(onChange).toHaveBeenLastCalledWith({
243
+ card: {
244
+ name: null,
245
+ brand: "visa",
246
+ localBrands: [],
247
+ number: null,
248
+ lastFour: null,
249
+ bin: null,
250
+ expiry: null,
251
+ cvc: null,
252
+ },
253
+ isValid: false,
254
+ isComplete: false,
255
+ errors: {
256
+ number: "Invalid card number",
257
+ },
258
+ });
259
+ });
260
+ });
@@ -0,0 +1,118 @@
1
+ import {
2
+ forwardRef,
3
+ PropsWithChildren,
4
+ useCallback,
5
+ useEffect,
6
+ useImperativeHandle,
7
+ useMemo,
8
+ } from "react";
9
+ import { CardBrandName, CardConfig, CardPayload } from "./types";
10
+ import { DeepPartial, FormProvider, useForm } from "react-hook-form";
11
+ import { CardFormValues, getCardFormSchema } from "./schema";
12
+ import { zodResolver } from "@hookform/resolvers/zod";
13
+ import { useEvervault } from "../useEvervault";
14
+ import { formatPayload } from "./utils";
15
+
16
+ const DEFAULT_ACCEPTED_BRANDS: CardBrandName[] = [];
17
+
18
+ export interface CardProps extends PropsWithChildren, CardConfig {
19
+ /**
20
+ * The default values to use for the form.
21
+ */
22
+ defaultValues?: {
23
+ name?: string;
24
+ number?: string;
25
+ expiry?: string;
26
+ cvc?: string;
27
+ };
28
+
29
+ /**
30
+ * Triggered whenever the component's state is updated.
31
+ */
32
+ onChange?(payload: CardPayload): void;
33
+
34
+ /**
35
+ * The validation mode to use for the form.
36
+ *
37
+ * - `onChange`: Validate the form when the user changes a field.
38
+ * - `onBlur`: Validate the form when the user leaves a field.
39
+ * - `onTouched`: Validate the form when the user touches a field.
40
+ * - `all`: Validate the form when the user changes or leaves a field.
41
+ *
42
+ * @default "all"
43
+ */
44
+ validationMode?: "onChange" | "onBlur" | "onTouched" | "all";
45
+ }
46
+
47
+ export interface Card {
48
+ /**
49
+ * Resets the form to its default values and state.
50
+ */
51
+ reset(): void;
52
+ }
53
+
54
+ export const Card = forwardRef<Card, CardProps>(function Card(
55
+ {
56
+ children,
57
+ defaultValues,
58
+ onChange,
59
+ acceptedBrands = DEFAULT_ACCEPTED_BRANDS,
60
+ validationMode = "all",
61
+ },
62
+ ref
63
+ ) {
64
+ const evervault = useEvervault();
65
+
66
+ const resolver = useMemo(() => {
67
+ const schema = getCardFormSchema(acceptedBrands);
68
+ return zodResolver(schema);
69
+ }, [acceptedBrands]);
70
+
71
+ const methods = useForm<CardFormValues>({
72
+ defaultValues,
73
+ resolver,
74
+ mode: validationMode,
75
+ shouldUseNativeValidation: false,
76
+ });
77
+
78
+ useEffect(() => {
79
+ if (!onChange) return;
80
+
81
+ let abortController: AbortController | undefined;
82
+ function handleChange(values: DeepPartial<CardFormValues>) {
83
+ if (abortController) {
84
+ abortController.abort();
85
+ }
86
+
87
+ abortController = new AbortController();
88
+ const signal = abortController.signal;
89
+
90
+ requestAnimationFrame(async () => {
91
+ const payload = await formatPayload(values, {
92
+ encrypt: evervault.encrypt,
93
+ form: methods,
94
+ });
95
+ if (signal.aborted) return;
96
+ onChange?.(payload);
97
+ });
98
+ }
99
+
100
+ handleChange(methods.getValues());
101
+ const subscription = methods.watch(handleChange);
102
+ return () => subscription.unsubscribe();
103
+ }, [evervault.encrypt, onChange]);
104
+
105
+ useImperativeHandle(
106
+ ref,
107
+ useCallback(
108
+ () => ({
109
+ reset() {
110
+ methods.reset();
111
+ },
112
+ }),
113
+ []
114
+ )
115
+ );
116
+
117
+ return <FormProvider {...methods}>{children}</FormProvider>;
118
+ });
@@ -0,0 +1,28 @@
1
+ import { Card as CardRoot, type Card as CardRef } from "./Root";
2
+ import { CardHolder } from "./Holder";
3
+ import { CardExpiry } from "./Expiry";
4
+ import { CardCvc } from "./Cvc";
5
+ import { CardNumber } from "./Number";
6
+
7
+ export type { CardProps } from "./Root";
8
+ export type Card = CardRef;
9
+ export const Card = Object.assign(CardRoot, {
10
+ Holder: CardHolder,
11
+ Expiry: CardExpiry,
12
+ Cvc: CardCvc,
13
+ Number: CardNumber,
14
+ });
15
+
16
+ export type { CardHolderProps } from "./Holder";
17
+ export { CardHolder };
18
+
19
+ export type { CardExpiryProps } from "./Expiry";
20
+ export { CardExpiry };
21
+
22
+ export type { CardCvcProps } from "./Cvc";
23
+ export { CardCvc };
24
+
25
+ export type { CardNumberProps } from "./Number";
26
+ export { CardNumber };
27
+
28
+ export type { CardPayload, CardBrandName } from "./types";
@@ -0,0 +1,51 @@
1
+ import { z } from "zod";
2
+ import {
3
+ validateNumber,
4
+ validateCVC,
5
+ validateExpiry,
6
+ } from "@evervault/card-validator";
7
+ import { CardBrandName } from "./types";
8
+ import { isAcceptedBrand } from "./utils";
9
+
10
+ export function getCardFormSchema(acceptedBrands: CardBrandName[]) {
11
+ return z
12
+ .object({
13
+ name: z.string().min(1, "Missing name"),
14
+
15
+ number: z
16
+ .string()
17
+ .min(1, "Required")
18
+ .refine((value) => validateNumber(value).isValid, {
19
+ message: "Invalid card number",
20
+ }),
21
+
22
+ expiry: z
23
+ .string()
24
+ .min(1, "Required")
25
+ .refine((value) => validateExpiry(value).isValid, {
26
+ message: "Invalid expiry",
27
+ }),
28
+
29
+ cvc: z
30
+ .string()
31
+ .min(1, "Required")
32
+ .refine((value) => validateCVC(value).isValid, {
33
+ message: "Invalid CVC",
34
+ }),
35
+ })
36
+ .superRefine((value, ctx) => {
37
+ const validation = validateNumber(value.number);
38
+ if (!validation.isValid) return;
39
+
40
+ const isAccepted = isAcceptedBrand(acceptedBrands, validation);
41
+ if (!isAccepted) {
42
+ ctx.addIssue({
43
+ code: z.ZodIssueCode.custom,
44
+ message: "Brand not accepted",
45
+ path: ["number"],
46
+ });
47
+ }
48
+ });
49
+ }
50
+
51
+ export type CardFormValues = z.infer<ReturnType<typeof getCardFormSchema>>;
@@ -0,0 +1,50 @@
1
+ export const CARD_BRAND_NAMES = [
2
+ "american-express",
3
+ "visa",
4
+ "mastercard",
5
+ "discover",
6
+ "jcb",
7
+ "diners-club",
8
+ "unionpay",
9
+ "maestro",
10
+ "mir",
11
+ "elo",
12
+ "hipercard",
13
+ "hiper",
14
+ "szep",
15
+ "uatp",
16
+ ] as const;
17
+
18
+ export type CardBrandName = (typeof CARD_BRAND_NAMES)[number];
19
+
20
+ export interface CardConfig {
21
+ acceptedBrands?: CardBrandName[];
22
+ }
23
+
24
+ export type CardField = "name" | "number" | "expiry" | "cvc";
25
+
26
+ export interface CardExpiry {
27
+ month: string;
28
+ year: string;
29
+ }
30
+
31
+ export interface CardPayload {
32
+ card: {
33
+ name: string | null;
34
+ brand: CardBrandName | null;
35
+ localBrands: CardBrandName[];
36
+ number: string | null;
37
+ lastFour: string | null;
38
+ bin: string | null;
39
+ expiry: CardExpiry | null;
40
+ cvc: string | null;
41
+ };
42
+ isValid: boolean;
43
+ isComplete: boolean;
44
+ errors: {
45
+ name?: string;
46
+ number?: string;
47
+ expiry?: string;
48
+ cvc?: string;
49
+ };
50
+ }