@incodetech/web 2.0.0-alpha.1

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 (176) hide show
  1. package/dev/README.md +163 -0
  2. package/dev/getToken.ts +36 -0
  3. package/dev/headless.html +875 -0
  4. package/dev/index.html +366 -0
  5. package/dev/main-headless.tsx +1332 -0
  6. package/dev/main-orchestrated-flow.tsx +1158 -0
  7. package/dev/main-preact.tsx +323 -0
  8. package/dev/main-simplified.tsx +123 -0
  9. package/dev/main-web-component.tsx +256 -0
  10. package/dev/main.tsx +332 -0
  11. package/dev/manual.html +27 -0
  12. package/dev/orchestrated-flow.html +64 -0
  13. package/dev/simplified.html +64 -0
  14. package/dev/tiktok-logo.svg +7 -0
  15. package/package.json +85 -0
  16. package/src/defineCustomElement.tsx +30 -0
  17. package/src/email/email.test.tsx +368 -0
  18. package/src/email/email.tsx +255 -0
  19. package/src/email/emailInput.test.tsx +264 -0
  20. package/src/email/emailInput.tsx +85 -0
  21. package/src/email/styles.css +59 -0
  22. package/src/flow/flow.test.tsx +796 -0
  23. package/src/flow/flow.tsx +292 -0
  24. package/src/flow/flowCompleted.css +30 -0
  25. package/src/flow/flowCompleted.test.tsx +331 -0
  26. package/src/flow/flowCompleted.tsx +121 -0
  27. package/src/flow/flowInit.test.ts +264 -0
  28. package/src/flow/flowInit.ts +94 -0
  29. package/src/flow/flowStart.css +58 -0
  30. package/src/flow/flowStart.test.tsx +49 -0
  31. package/src/flow/flowStart.tsx +41 -0
  32. package/src/flow/incode-logo.svg +8 -0
  33. package/src/flow/index.ts +7 -0
  34. package/src/flow/preloadFlow.test.ts +421 -0
  35. package/src/flow/preloadFlow.ts +171 -0
  36. package/src/flow/styles.css +9 -0
  37. package/src/flow/unsupportedModule.css +21 -0
  38. package/src/flow/unsupportedModule.tsx +39 -0
  39. package/src/flow/useFlowInitialization.test.tsx +292 -0
  40. package/src/flow/useFlowInitialization.ts +128 -0
  41. package/src/flow/useModuleLoader.test.tsx +212 -0
  42. package/src/flow/useModuleLoader.ts +92 -0
  43. package/src/hooks/index.ts +1 -0
  44. package/src/hooks/useManager.test.ts +91 -0
  45. package/src/hooks/useManager.ts +40 -0
  46. package/src/i18n/index.ts +3 -0
  47. package/src/i18n/instance.ts +16 -0
  48. package/src/i18n/setup.ts +184 -0
  49. package/src/i18n/useTranslation.ts +42 -0
  50. package/src/index.ts +27 -0
  51. package/src/permissions/assets/android-dots-icon.svg +7 -0
  52. package/src/permissions/assets/android-settings-icon.svg +16 -0
  53. package/src/permissions/assets/android-toggle-icon.svg +20 -0
  54. package/src/permissions/assets/bank-card-icon.svg +14 -0
  55. package/src/permissions/assets/camera-icon.svg +12 -0
  56. package/src/permissions/assets/camera-ios.svg +53 -0
  57. package/src/permissions/assets/check-icon.svg +8 -0
  58. package/src/permissions/assets/chrome-icon.svg +43 -0
  59. package/src/permissions/assets/password-icon.svg +11 -0
  60. package/src/permissions/assets/permissions-img.svg +51 -0
  61. package/src/permissions/assets/safari-icon.svg +37 -0
  62. package/src/permissions/assets/settings-icon.svg +33 -0
  63. package/src/permissions/assets/toggle-icon.svg +19 -0
  64. package/src/permissions/assets/warning-icon.svg +6 -0
  65. package/src/permissions/boldWithArrow.css +9 -0
  66. package/src/permissions/boldWithArrow.tsx +41 -0
  67. package/src/permissions/denied.css +37 -0
  68. package/src/permissions/denied.tsx +29 -0
  69. package/src/permissions/deniedAndroid.tsx +56 -0
  70. package/src/permissions/deniedDesktop.css +9 -0
  71. package/src/permissions/deniedDesktop.tsx +64 -0
  72. package/src/permissions/deniedIOS.tsx +73 -0
  73. package/src/permissions/deniedInstructions.tsx +19 -0
  74. package/src/permissions/iconWrapper.css +9 -0
  75. package/src/permissions/iconWrapper.tsx +15 -0
  76. package/src/permissions/learnMore.css +37 -0
  77. package/src/permissions/learnMore.tsx +85 -0
  78. package/src/permissions/numberedStep.css +13 -0
  79. package/src/permissions/numberedStep.tsx +14 -0
  80. package/src/permissions/permissions.css +13 -0
  81. package/src/permissions/permissions.tsx +68 -0
  82. package/src/phone/phone.tsx +246 -0
  83. package/src/phone/phoneInput.test.tsx +275 -0
  84. package/src/phone/phoneInput.tsx +249 -0
  85. package/src/phone/styles.css +158 -0
  86. package/src/selfie/cameraButton.css +13 -0
  87. package/src/selfie/cameraButton.tsx +35 -0
  88. package/src/selfie/capture.css +57 -0
  89. package/src/selfie/capture.tsx +232 -0
  90. package/src/selfie/errorModal.tsx +218 -0
  91. package/src/selfie/errorModalContent.css +33 -0
  92. package/src/selfie/errorModalContent.tsx +44 -0
  93. package/src/selfie/faceOutline.css +5 -0
  94. package/src/selfie/faceOutline.tsx +22 -0
  95. package/src/selfie/loadingBorder.css +12 -0
  96. package/src/selfie/loadingBorder.tsx +77 -0
  97. package/src/selfie/manualCaptureButton.css +13 -0
  98. package/src/selfie/manualCaptureButton.tsx +35 -0
  99. package/src/selfie/noMoreAttemptsModal.tsx +44 -0
  100. package/src/selfie/notification.css +9 -0
  101. package/src/selfie/notification.tsx +36 -0
  102. package/src/selfie/retryErrorModal.tsx +56 -0
  103. package/src/selfie/selfie.test.tsx +458 -0
  104. package/src/selfie/selfie.tsx +83 -0
  105. package/src/selfie/selfieTutorial.json +2626 -0
  106. package/src/selfie/styles.css +1 -0
  107. package/src/selfie/tutorial.test.tsx +200 -0
  108. package/src/selfie/tutorial.tsx +43 -0
  109. package/src/setup.ts +33 -0
  110. package/src/shared/baseTutorial/baseTutorial.css +21 -0
  111. package/src/shared/baseTutorial/baseTutorial.test.tsx +184 -0
  112. package/src/shared/baseTutorial/baseTutorial.tsx +55 -0
  113. package/src/shared/baseTutorial/replaceBaseTutorial.test.ts +267 -0
  114. package/src/shared/baseTutorial/replaceBaseTutorial.ts +68 -0
  115. package/src/shared/button/button.css +55 -0
  116. package/src/shared/button/button.test.tsx +101 -0
  117. package/src/shared/button/button.tsx +47 -0
  118. package/src/shared/componentRoot/incodeComponent.tsx +12 -0
  119. package/src/shared/countries/countries.test.ts +75 -0
  120. package/src/shared/countries/countries.ts +139 -0
  121. package/src/shared/countries/index.ts +6 -0
  122. package/src/shared/icons/chevronDown.tsx +22 -0
  123. package/src/shared/icons/index.ts +2 -0
  124. package/src/shared/icons/successIcon.css +5 -0
  125. package/src/shared/icons/successIcon.test.tsx +40 -0
  126. package/src/shared/icons/successIcon.tsx +26 -0
  127. package/src/shared/loader/loadingIcon.css +28 -0
  128. package/src/shared/loader/loadingIcon.tsx +67 -0
  129. package/src/shared/lottie/lottie.tsx +108 -0
  130. package/src/shared/otpInput/otpInput.css +85 -0
  131. package/src/shared/otpInput/otpInput.test.tsx +356 -0
  132. package/src/shared/otpInput/otpInput.tsx +241 -0
  133. package/src/shared/page/incode-logo.svg +3 -0
  134. package/src/shared/page/page.css +47 -0
  135. package/src/shared/page/page.test.tsx +97 -0
  136. package/src/shared/page/page.tsx +91 -0
  137. package/src/shared/page/pageUiConfig.test.ts +112 -0
  138. package/src/shared/page/pageUiConfig.ts +64 -0
  139. package/src/shared/page/verifiedByIncode.css +5 -0
  140. package/src/shared/page/verifiedByIncode.tsx +75 -0
  141. package/src/shared/spacer/spacer.css +149 -0
  142. package/src/shared/spacer/spacer.test.tsx +143 -0
  143. package/src/shared/spacer/spacer.tsx +88 -0
  144. package/src/shared/spinner/index.ts +2 -0
  145. package/src/shared/spinner/spinner.css +28 -0
  146. package/src/shared/spinner/spinner.test.tsx +82 -0
  147. package/src/shared/spinner/spinner.tsx +65 -0
  148. package/src/shared/title/title.css +7 -0
  149. package/src/shared/title/title.tsx +12 -0
  150. package/src/shared/uiConfig/uiConfig.ts +36 -0
  151. package/src/shared/webComponent/incodeModule.ts +29 -0
  152. package/src/shared/webComponent/registerIncodeElement.ts +15 -0
  153. package/src/styles/__mocks__/fetchTheme.ts +19 -0
  154. package/src/styles/applyTheme.ts +37 -0
  155. package/src/styles/cn.test.tsx +57 -0
  156. package/src/styles/cn.tsx +21 -0
  157. package/src/styles/core.css +12 -0
  158. package/src/styles/fetchTheme.test.ts +390 -0
  159. package/src/styles/fetchTheme.ts +88 -0
  160. package/src/styles/generatePalette.ts +111 -0
  161. package/src/styles/reset.css +65 -0
  162. package/src/styles/resolveCssVariableToHex.ts +28 -0
  163. package/src/styles/tailwind.css +291 -0
  164. package/src/styles/themeTypes.ts +18 -0
  165. package/src/styles/tokens/colors.css +190 -0
  166. package/src/styles/tokens/components.css +174 -0
  167. package/src/styles/tokens/index.css +4 -0
  168. package/src/styles/tokens/primitives.css +129 -0
  169. package/src/styles/tokens/semantic.css +51 -0
  170. package/src/svg.d.ts +4 -0
  171. package/src/types/assets.d.ts +1 -0
  172. package/src/types/custom-elements.d.ts +104 -0
  173. package/tsconfig.json +22 -0
  174. package/vite.config.ts +260 -0
  175. package/vitest.config.ts +40 -0
  176. package/vitest.setup.ts +16 -0
@@ -0,0 +1,129 @@
1
+ :root,
2
+ :host {
3
+ /* Color / Gray */
4
+
5
+ --primitive-color-gray-0: #ffffff;
6
+ --primitive-color-gray-0-80: rgba(255, 255, 255, 0.8);
7
+ --primitive-color-gray-50: #fcfcfd;
8
+ --primitive-color-gray-100: #ebecef;
9
+ --primitive-color-gray-200: #c6c8d2;
10
+ --primitive-color-gray-300: #a3a8b8;
11
+ --primitive-color-gray-500: #60667c;
12
+ --primitive-color-gray-700: #3a3e4b;
13
+ --primitive-color-gray-800: #262831;
14
+ --primitive-color-gray-900: #14151a;
15
+ --primitive-color-gray-900-80: rgba(20, 21, 26, 0.8);
16
+ --primitive-color-gray-1000: #000000;
17
+ --primitive-color-gray-1000-80: rgba(0, 0, 0, 0.8);
18
+
19
+ /* Color / Brand */
20
+
21
+ --primitive-color-brand-50: #e5f0ff;
22
+ --primitive-color-brand-200: #99ceff;
23
+ --primitive-color-brand-300: #66a6ff;
24
+ --primitive-color-brand-400: #3388ff;
25
+ --primitive-color-brand-500: #006aff;
26
+ --primitive-color-brand-600: #0055cc;
27
+ --primitive-color-brand-900: #21273b;
28
+
29
+ /* Color / Brand Secondary */
30
+
31
+ --primitive-color-brand-secondary-50: #f2e2fe;
32
+ --primitive-color-brand-secondary-500: #820ad1;
33
+
34
+ /* Color / Positive */
35
+
36
+ --primitive-color-positive-50: #e4fbf0;
37
+ --primitive-color-positive-600: #189f60;
38
+ --primitive-color-positive-800: #0c5030;
39
+
40
+ /* Color / Warning */
41
+
42
+ --primitive-color-warning-50: #fff7eb;
43
+ --primitive-color-warning-400: #ffb647;
44
+ --primitive-color-warning-500: #ff9900;
45
+ --primitive-color-warning-950: #523100;
46
+
47
+ /* Color / Negative */
48
+
49
+ --primitive-color-negative-50: #fff0f0;
50
+ --primitive-color-negative-500: #ff5a5f;
51
+ --primitive-color-negative-600: #e71111;
52
+ --primitive-color-negative-950: #240001;
53
+
54
+ /* Typography / Family */
55
+ --primitive-typography-family-rethink-sans: "Rethink Sans";
56
+ --primitive-typography-family-dm-sans: "DM Sans";
57
+
58
+ /* Typography / Size */
59
+
60
+ --primitive-typography-size-12: 12px;
61
+ --primitive-typography-size-14: 14px;
62
+ --primitive-typography-size-16: 16px;
63
+ --primitive-typography-size-18: 18px;
64
+ --primitive-typography-size-20: 20px;
65
+ --primitive-typography-size-24: 24px;
66
+ --primitive-typography-size-32: 32px;
67
+ --primitive-typography-size-40: 40px;
68
+ --primitive-typography-size-48: 48px;
69
+ --primitive-typography-size-80: 80px;
70
+
71
+ /* Typography / Weight */
72
+
73
+ --primitive-typography-weight-thin: 300;
74
+ --primitive-typography-weight-regular: 400;
75
+ --primitive-typography-weight-medium: 500;
76
+ --primitive-typography-weight-semibold: 600;
77
+ --primitive-typography-weight-bold: 700;
78
+ --primitive-typography-weight-extrabold: 800;
79
+
80
+ /* Typography / Letter Spacing */
81
+
82
+ --primitive-typography-letter-spacing-0: 0px;
83
+ --primitive-typography-letter-spacing-minus-0-5: -0.5px;
84
+ --primitive-typography-letter-spacing-minus-1: -1px;
85
+ --primitive-typography-letter-spacing-minus-1-5: -1.5px;
86
+
87
+ /* Border / Width */
88
+
89
+ --primitive-border-width-0: 0px;
90
+ --primitive-border-width-1: 1px;
91
+ --primitive-border-width-2: 2px;
92
+ --primitive-border-width-4: 4px;
93
+
94
+ /* Border / Radius */
95
+
96
+ --primitive-border-radius-0: 0px;
97
+ --primitive-border-radius-4: 4px;
98
+ --primitive-border-radius-8: 8px;
99
+ --primitive-border-radius-16: 16px;
100
+ --primitive-border-radius-24: 24px;
101
+ --primitive-border-radius-40: 40px;
102
+ --primitive-border-radius-64: 64px;
103
+ --primitive-border-radius-9999: 9999px;
104
+
105
+ /* Scale */
106
+
107
+ --primitive-scale-0: 0px;
108
+ --primitive-scale-2: 2px;
109
+ --primitive-scale-4: 4px;
110
+ --primitive-scale-8: 8px;
111
+ --primitive-scale-12: 12px;
112
+ --primitive-scale-16: 16px;
113
+ --primitive-scale-20: 20px;
114
+ --primitive-scale-24: 24px;
115
+ --primitive-scale-32: 32px;
116
+ --primitive-scale-40: 40px;
117
+ --primitive-scale-48: 48px;
118
+ --primitive-scale-56: 56px;
119
+ --primitive-scale-64: 64px;
120
+ --primitive-scale-72: 72px;
121
+ --primitive-scale-80: 80px;
122
+ --primitive-scale-88: 88px;
123
+ --primitive-scale-96: 96px;
124
+ --primitive-scale-104: 104px;
125
+ --primitive-scale-120: 120px;
126
+ --primitive-scale-128: 128px;
127
+ --primitive-scale-160: 160px;
128
+ --primitive-scale-200: 200px;
129
+ }
@@ -0,0 +1,51 @@
1
+ :root,
2
+ :host {
3
+ /* Border / Width */
4
+
5
+ --border-none: var(--primitive-border-width-0);
6
+ --border-regular: var(--primitive-border-width-1);
7
+ --border-bold: var(--primitive-border-width-2);
8
+
9
+ /* Border / Radius */
10
+
11
+ --border-radius-none: var(--primitive-border-radius-0);
12
+ --border-radius-x-small: var(--primitive-border-radius-4);
13
+ --border-radius-small: var(--primitive-border-radius-8);
14
+ --border-radius-medium: var(--primitive-border-radius-16);
15
+ --border-radius-large: var(--primitive-border-radius-24);
16
+ --border-radius-x-large: var(--primitive-border-radius-40);
17
+ --border-radius-xx-large: var(--primitive-border-radius-64);
18
+ --border-radius-full: var(--primitive-border-radius-9999);
19
+
20
+ /* Spacing */
21
+
22
+ --spacing-none: var(--primitive-scale-0);
23
+ --spacing-2: var(--primitive-scale-2);
24
+ --spacing-4: var(--primitive-scale-4);
25
+ --spacing-8: var(--primitive-scale-8);
26
+ --spacing-12: var(--primitive-scale-12);
27
+ --spacing-16: var(--primitive-scale-16);
28
+ --spacing-20: var(--primitive-scale-20);
29
+ --spacing-24: var(--primitive-scale-24);
30
+ --spacing-32: var(--primitive-scale-32);
31
+ --spacing-40: var(--primitive-scale-40);
32
+ --spacing-48: var(--primitive-scale-48);
33
+ --spacing-64: var(--primitive-scale-64);
34
+ --spacing-80: var(--primitive-scale-80);
35
+ --spacing-96: var(--primitive-scale-96);
36
+ --spacing-104: var(--primitive-scale-104);
37
+ --spacing-120: var(--primitive-scale-120);
38
+ --spacing-160: var(--primitive-scale-160);
39
+ --spacing-200: var(--primitive-scale-200);
40
+
41
+ /* Icon / Size */
42
+
43
+ --icon-size-xx-small: var(--primitive-scale-16);
44
+ --icon-size-x-small: var(--primitive-scale-20);
45
+ --icon-size-small: var(--primitive-scale-24);
46
+ --icon-size-medium: var(--primitive-scale-32);
47
+ --icon-size-large: var(--primitive-scale-40);
48
+ --icon-size-x-large: var(--primitive-scale-48);
49
+ --icon-size-xx-large: var(--primitive-scale-64);
50
+ --icon-size-xxx-large: var(--primitive-scale-80);
51
+ }
package/src/svg.d.ts ADDED
@@ -0,0 +1,4 @@
1
+ declare module '*.svg' {
2
+ const content: string;
3
+ export default content;
4
+ }
@@ -0,0 +1 @@
1
+ export type AssetManifest = Record<string, string>;
@@ -0,0 +1,104 @@
1
+ import type { EmailConfig } from '@incodetech/core/email';
2
+ import type { PhoneConfig } from '@incodetech/core/phone';
3
+ import type { SelfieConfig } from '@incodetech/core/selfie';
4
+ import type { IncodeFlowProps, SpinnerConfig } from '../flow/flow';
5
+ import type { FlowCompletedProps } from '../flow/flowCompleted';
6
+ import type { IncodeModuleProps } from '../shared/webComponent/incodeModule';
7
+
8
+ // =============================================================================
9
+ // Flow Components
10
+ // =============================================================================
11
+
12
+ /**
13
+ * Element type for the `<incode-flow>` web component.
14
+ *
15
+ * @example
16
+ * ```typescript
17
+ * const flow = document.createElement('incode-flow');
18
+ * flow.apiURL = 'https://api.example.com';
19
+ * flow.token = 'session-token';
20
+ * flow.onComplete = (result) => console.log(result);
21
+ * document.body.appendChild(flow);
22
+ * ```
23
+ */
24
+ export type IncodeFlowElement = HTMLElement & IncodeFlowProps;
25
+
26
+ /**
27
+ * Element type for the `<incode-flow-completed>` web component.
28
+ *
29
+ * @example
30
+ * ```typescript
31
+ * const completed = document.createElement('incode-flow-completed');
32
+ * completed.action = 'approved';
33
+ * completed.scoreStatus = 'OK';
34
+ * document.body.appendChild(completed);
35
+ * ```
36
+ */
37
+ export type FlowCompletedElement = HTMLElement & FlowCompletedProps;
38
+
39
+ // =============================================================================
40
+ // Module Components
41
+ // =============================================================================
42
+
43
+ /**
44
+ * Element type for the `<incode-phone>` web component.
45
+ *
46
+ * @example
47
+ * ```typescript
48
+ * const phone = document.createElement('incode-phone');
49
+ * phone.config = { otpVerification: true, otpExpirationInMinutes: 5 };
50
+ * phone.onFinish = () => console.log('Phone verified!');
51
+ * phone.onError = (error) => console.error(error);
52
+ * document.body.appendChild(phone);
53
+ * ```
54
+ */
55
+ export type IncodePhoneElement = HTMLElement & IncodeModuleProps<PhoneConfig>;
56
+
57
+ /**
58
+ * Element type for the `<incode-email>` web component.
59
+ *
60
+ * @example
61
+ * ```typescript
62
+ * const email = document.createElement('incode-email');
63
+ * email.config = { otpVerification: true, otpExpirationInMinutes: 5 };
64
+ * email.onFinish = () => console.log('Email verified!');
65
+ * email.onError = (error) => console.error(error);
66
+ * document.body.appendChild(email);
67
+ * ```
68
+ */
69
+ export type IncodeEmailElement = HTMLElement & IncodeModuleProps<EmailConfig>;
70
+
71
+ /**
72
+ * Element type for the `<incode-selfie>` web component.
73
+ *
74
+ * @example
75
+ * ```typescript
76
+ * const selfie = document.createElement('incode-selfie');
77
+ * selfie.config = { showTutorial: true };
78
+ * selfie.onFinish = () => console.log('Selfie captured!');
79
+ * selfie.onError = (error) => console.error(error);
80
+ * document.body.appendChild(selfie);
81
+ * ```
82
+ */
83
+ export type IncodeSelfieElement = HTMLElement & IncodeModuleProps<SelfieConfig>;
84
+
85
+ // =============================================================================
86
+ // Global Type Augmentation
87
+ // =============================================================================
88
+
89
+ declare global {
90
+ interface HTMLElementTagNameMap {
91
+ 'incode-flow': IncodeFlowElement;
92
+ 'incode-flow-completed': FlowCompletedElement;
93
+ 'incode-phone': IncodePhoneElement;
94
+ 'incode-email': IncodeEmailElement;
95
+ 'incode-selfie': IncodeSelfieElement;
96
+ }
97
+ }
98
+
99
+ // =============================================================================
100
+ // Re-exports for Consumers
101
+ // =============================================================================
102
+
103
+ export type { IncodeFlowProps, SpinnerConfig, FlowCompletedProps };
104
+ export type { IncodeModuleProps };
package/tsconfig.json ADDED
@@ -0,0 +1,22 @@
1
+ {
2
+ "extends": "../config/ts/tsconfig.strict.json",
3
+ "compilerOptions": {
4
+ "jsx": "react-jsx",
5
+ "jsxImportSource": "preact",
6
+ "outDir": "dist",
7
+ "declarationMap": false,
8
+ "resolveJsonModule": true,
9
+ "baseUrl": ".",
10
+ "paths": {
11
+ "@/*": ["src/*"],
12
+ "@incodetech/core": ["../core/src"],
13
+ "@incodetech/core/flow": ["../core/src/flow"],
14
+ "@incodetech/core/id": ["../core/src/id"],
15
+ "@incodetech/core/phone": ["../core/src/phone"],
16
+ "@incodetech/core/email": ["../core/src/email"],
17
+ "@incodetech/core/selfie": ["../core/src/selfie"],
18
+ "@incodetech/infra": ["../infra/src"]
19
+ }
20
+ },
21
+ "include": ["src", "dev", "vitest.config.ts", "vitest.setup.ts"]
22
+ }
package/vite.config.ts ADDED
@@ -0,0 +1,260 @@
1
+ import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
2
+ import { dirname, relative, resolve } from 'node:path';
3
+ import { fileURLToPath } from 'node:url';
4
+ import { preserveGlobals } from '@incodetech/config/vite/preact-globals';
5
+ import preact from '@preact/preset-vite';
6
+ import tailwindcss from '@tailwindcss/vite';
7
+ import postcss from 'postcss';
8
+ import postcssImport from 'postcss-import';
9
+ import type { Plugin } from 'vite';
10
+ import { defineConfig, loadEnv } from 'vite';
11
+ import dts from 'vite-plugin-dts';
12
+
13
+ const __dirname = dirname(fileURLToPath(import.meta.url));
14
+
15
+ const entries = {
16
+ index: resolve(__dirname, 'src/index.ts'),
17
+ 'selfie/selfie': resolve(__dirname, 'src/selfie/selfie.tsx'),
18
+ 'phone/phone': resolve(__dirname, 'src/phone/phone.tsx'),
19
+ 'email/email': resolve(__dirname, 'src/email/email.tsx'),
20
+ 'flow/flow': resolve(__dirname, 'src/flow/flow.tsx'),
21
+ 'styles/core': resolve(__dirname, 'src/styles/core.css'),
22
+ };
23
+
24
+ function makeEntryMap(libEntries: Record<string, string>) {
25
+ const map: Record<string, { dir: string; base: string }> = {};
26
+ for (const key of Object.keys(libEntries)) {
27
+ const parts = key.split('/');
28
+ const base = parts.pop() ?? key;
29
+ const dir = parts.join('/');
30
+ map[key] = { dir, base };
31
+ }
32
+ return map;
33
+ }
34
+
35
+ const entryMap = makeEntryMap(entries);
36
+
37
+ function buildTokensCSS(): Plugin {
38
+ return {
39
+ name: 'build-tokens-css',
40
+ async closeBundle() {
41
+ const tokensCssPath = resolve(__dirname, 'src/styles/tokens/index.css');
42
+ const outputPath = resolve(__dirname, 'dist/styles/tokens.css');
43
+
44
+ try {
45
+ const css = readFileSync(tokensCssPath, 'utf-8');
46
+ const result = await postcss([postcssImport()]).process(css, {
47
+ from: tokensCssPath,
48
+ to: outputPath,
49
+ });
50
+ mkdirSync(resolve(__dirname, 'dist/styles'), { recursive: true });
51
+ writeFileSync(outputPath, result.css, 'utf-8');
52
+ } catch (error) {
53
+ console.error('Error building tokens.css:', error);
54
+ }
55
+ },
56
+ };
57
+ }
58
+
59
+ function buildCoreCSS(): Plugin {
60
+ return {
61
+ name: 'build-core-css',
62
+ async closeBundle() {
63
+ const coreCssPath = resolve(__dirname, 'dist/styles/core.css');
64
+ const outputPath = resolve(__dirname, 'dist/styles/core.css');
65
+
66
+ try {
67
+ const css = readFileSync(coreCssPath, 'utf-8');
68
+ const processedCss = css.replace(/@reference\s+[^;]+;/g, '');
69
+ mkdirSync(resolve(__dirname, 'dist/styles'), { recursive: true });
70
+ writeFileSync(outputPath, processedCss, 'utf-8');
71
+ } catch (error) {
72
+ console.error('Error post-processing core.css:', error);
73
+ }
74
+ },
75
+ };
76
+ }
77
+
78
+ function useCdnForSvgs(): Plugin {
79
+ let manifest: Record<string, string> | null = null;
80
+
81
+ function loadManifest() {
82
+ if (manifest) return manifest;
83
+
84
+ try {
85
+ const manifestPath = resolve(__dirname, 'dist/asset-manifest.json');
86
+ const manifestContent = readFileSync(manifestPath, 'utf-8');
87
+ manifest = JSON.parse(manifestContent);
88
+ return manifest;
89
+ } catch (error) {
90
+ console.warn('[use-cdn-for-svgs] Could not load asset manifest:', error);
91
+ return null;
92
+ }
93
+ }
94
+
95
+ return {
96
+ name: 'use-cdn-for-svgs',
97
+ enforce: 'pre',
98
+ buildStart() {
99
+ loadManifest();
100
+ },
101
+ async resolveId(id, importer) {
102
+ if (!id.endsWith('.svg')) {
103
+ return null;
104
+ }
105
+
106
+ const cleanId = id.replace(/\?.*$/, '');
107
+ const manifestData = loadManifest();
108
+ if (!manifestData) {
109
+ return null;
110
+ }
111
+
112
+ try {
113
+ const resolved = await this.resolve(cleanId, importer, {
114
+ skipSelf: true,
115
+ });
116
+ if (!resolved || !resolved.id || typeof resolved.id !== 'string') {
117
+ return null;
118
+ }
119
+
120
+ const absolutePath = resolved.id.replace(/\?.*$/, '');
121
+ const relativePath = relative(
122
+ resolve(__dirname, 'src'),
123
+ absolutePath,
124
+ ).replace(/\\/g, '/');
125
+ const cdnUrl = manifestData[relativePath];
126
+
127
+ if (cdnUrl) {
128
+ return {
129
+ id: `\0cdn-svg:${cdnUrl}`,
130
+ external: false,
131
+ };
132
+ }
133
+ } catch {
134
+ return null;
135
+ }
136
+ return null;
137
+ },
138
+ load(id) {
139
+ if (id.startsWith('\0cdn-svg:')) {
140
+ const cdnUrl = id.replace('\0cdn-svg:', '');
141
+ return `export default ${JSON.stringify(cdnUrl)}`;
142
+ }
143
+
144
+ if (id.endsWith('.svg') && !id.includes('?')) {
145
+ const manifestData = loadManifest();
146
+ if (!manifestData) {
147
+ return null;
148
+ }
149
+
150
+ const relativePath = relative(resolve(__dirname, 'src'), id).replace(
151
+ /\\/g,
152
+ '/',
153
+ );
154
+ const cdnUrl = manifestData[relativePath];
155
+
156
+ if (cdnUrl) {
157
+ return `export default ${JSON.stringify(cdnUrl)}`;
158
+ }
159
+ }
160
+ return null;
161
+ },
162
+ };
163
+ }
164
+
165
+ const defaultAllowedHosts = ['localhost', '127.0.0.1', '::1'];
166
+
167
+ export default defineConfig(({ mode }) => {
168
+ const env = loadEnv(mode, __dirname, '');
169
+ const extraHosts = env.VITE_ALLOWED_HOSTS?.split(',').filter(Boolean) ?? [];
170
+ const allowedHosts = [...defaultAllowedHosts, ...extraHosts];
171
+
172
+ return {
173
+ plugins: [
174
+ useCdnForSvgs(),
175
+ preact(),
176
+ buildTokensCSS(),
177
+ buildCoreCSS(),
178
+ tailwindcss(),
179
+ dts({
180
+ tsconfigPath: resolve(__dirname, 'tsconfig.json'),
181
+ insertTypesEntry: true,
182
+ exclude: [
183
+ 'vitest.config.ts',
184
+ 'vitest.setup.ts',
185
+ '**/*.test.ts',
186
+ '**/*.test.tsx',
187
+ 'dev/**',
188
+ ],
189
+ include: [
190
+ resolve(__dirname, 'src/**/*.ts'),
191
+ resolve(__dirname, 'src/**/*.tsx'),
192
+ ],
193
+ copyDtsFiles: true,
194
+ rollupTypes: true,
195
+ outDir: resolve(__dirname, 'dist/types'),
196
+ }),
197
+ preserveGlobals() as Plugin,
198
+ ],
199
+ resolve: {
200
+ alias: {
201
+ '@': resolve(__dirname, 'src'),
202
+ '@incodetech/core': resolve(__dirname, '../core/src'),
203
+ },
204
+ },
205
+ root: resolve(__dirname, 'dev'),
206
+ server: {
207
+ port: 5173,
208
+ allowedHosts,
209
+ },
210
+ build: {
211
+ emptyOutDir: true,
212
+ target: 'es2021',
213
+ assetsInlineLimit: 0,
214
+ lib: {
215
+ entry: entries,
216
+ formats: ['es'],
217
+ fileName: (format, entryName) => `${entryName}.${format}.js`,
218
+ },
219
+ outDir: resolve(__dirname, 'dist'),
220
+ sourcemap: false,
221
+ cssCodeSplit: true,
222
+ cssMinify: false,
223
+ rollupOptions: {
224
+ external: [
225
+ '@incodetech/core',
226
+ '@incodetech/core/flow',
227
+ '@incodetech/core/phone',
228
+ '@incodetech/core/email',
229
+ '@incodetech/core/selfie',
230
+ ],
231
+ output: {
232
+ manualChunks: {
233
+ 'vendor-preact': ['preact', 'preact/hooks', 'preact/compat'],
234
+ },
235
+ assetFileNames: (assetInfo) => {
236
+ const names = assetInfo.names || [];
237
+ const fileName = names[0] || '';
238
+ if (fileName.endsWith('.css')) {
239
+ const cssBase = fileName.replace(/\.css$/, '');
240
+ if (
241
+ cssBase === 'styles/core' ||
242
+ cssBase.includes('styles/core')
243
+ ) {
244
+ return 'styles/core.css';
245
+ }
246
+ for (const [key, { dir, base }] of Object.entries(entryMap)) {
247
+ if (cssBase === base || key.includes(cssBase)) {
248
+ const folder = dir ? `${dir}/` : '';
249
+ return `${folder}styles.css`;
250
+ }
251
+ }
252
+ return '[name].css';
253
+ }
254
+ return 'assets/[name][extname]';
255
+ },
256
+ },
257
+ },
258
+ },
259
+ };
260
+ });
@@ -0,0 +1,40 @@
1
+ import { resolve } from 'node:path';
2
+ import { defineConfig, mergeConfig } from 'vitest/config';
3
+ import baseConfig from '../config/vitest/base';
4
+
5
+ export default mergeConfig(
6
+ baseConfig,
7
+ defineConfig({
8
+ resolve: {
9
+ alias: {
10
+ '@': resolve(__dirname, 'src'),
11
+ '@incodetech/core/selfie': resolve(__dirname, '../core/src/selfie'),
12
+ '@incodetech/core/flow': resolve(__dirname, '../core/src/flow'),
13
+ '@incodetech/core': resolve(__dirname, '../core/src'),
14
+ '@incodetech/infra': resolve(__dirname, '../infra/src'),
15
+ },
16
+ },
17
+ test: {
18
+ environment: 'jsdom',
19
+ setupFiles: ['./vitest.setup.ts'],
20
+ coverage: {
21
+ exclude: [
22
+ '**/i18n/**',
23
+ '**/index.ts',
24
+ '**/phone/phone.tsx',
25
+ '**/shared/lottie/**',
26
+ '**/styles/generatePalette.ts',
27
+ '**/styles/applyTheme.ts',
28
+ ],
29
+ thresholds: {
30
+ 'src/permissions/**': {
31
+ statements: 0,
32
+ branches: 0,
33
+ functions: 0,
34
+ lines: 0,
35
+ },
36
+ },
37
+ },
38
+ },
39
+ }),
40
+ );
@@ -0,0 +1,16 @@
1
+ import { vi } from 'vitest';
2
+
3
+ vi.mock('./src/i18n/useTranslation', () => ({
4
+ useTranslation: () => ({
5
+ t: (key: string) => key,
6
+ ready: true,
7
+ i18n: {
8
+ language: 'en',
9
+ isInitialized: true,
10
+ on: vi.fn(),
11
+ off: vi.fn(),
12
+ },
13
+ }),
14
+ }));
15
+
16
+ vi.mock('*.css', () => ({}));