@harkenapp/sdk-react-native 0.0.1-alpha.1 → 0.0.1-alpha.2

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 (278) hide show
  1. package/README.md +44 -7
  2. package/app.plugin.cjs +12 -17
  3. package/dist/__mocks__/async-storage.d.ts +16 -0
  4. package/dist/__mocks__/async-storage.d.ts.map +1 -0
  5. package/dist/__mocks__/async-storage.js +39 -0
  6. package/dist/__mocks__/async-storage.js.map +1 -0
  7. package/dist/__mocks__/expo-document-picker.d.ts +26 -0
  8. package/dist/__mocks__/expo-document-picker.d.ts.map +1 -0
  9. package/dist/__mocks__/expo-document-picker.js +25 -0
  10. package/dist/__mocks__/expo-document-picker.js.map +1 -0
  11. package/dist/__mocks__/expo-file-system.d.ts +42 -0
  12. package/dist/__mocks__/expo-file-system.d.ts.map +1 -0
  13. package/dist/__mocks__/expo-file-system.js +37 -0
  14. package/dist/__mocks__/expo-file-system.js.map +1 -0
  15. package/dist/__mocks__/expo-image-picker.d.ts +30 -0
  16. package/dist/__mocks__/expo-image-picker.d.ts.map +1 -0
  17. package/dist/__mocks__/expo-image-picker.js +30 -0
  18. package/dist/__mocks__/expo-image-picker.js.map +1 -0
  19. package/dist/__mocks__/expo-secure-store.d.ts +15 -0
  20. package/dist/__mocks__/expo-secure-store.d.ts.map +1 -0
  21. package/dist/__mocks__/expo-secure-store.js +30 -0
  22. package/dist/__mocks__/expo-secure-store.js.map +1 -0
  23. package/dist/__mocks__/react-native.d.ts +73 -0
  24. package/dist/__mocks__/react-native.d.ts.map +1 -0
  25. package/dist/__mocks__/react-native.js +45 -0
  26. package/dist/__mocks__/react-native.js.map +1 -0
  27. package/dist/api/client.d.ts +8 -8
  28. package/dist/api/client.d.ts.map +1 -1
  29. package/dist/api/client.js +17 -19
  30. package/dist/api/client.js.map +1 -1
  31. package/dist/api/client.test.d.ts +2 -0
  32. package/dist/api/client.test.d.ts.map +1 -0
  33. package/dist/api/client.test.js +417 -0
  34. package/dist/api/client.test.js.map +1 -0
  35. package/dist/api/errors.d.ts +3 -3
  36. package/dist/api/errors.d.ts.map +1 -1
  37. package/dist/api/errors.js +3 -3
  38. package/dist/api/errors.js.map +1 -1
  39. package/dist/api/errors.test.d.ts +2 -0
  40. package/dist/api/errors.test.d.ts.map +1 -0
  41. package/dist/api/errors.test.js +155 -0
  42. package/dist/api/errors.test.js.map +1 -0
  43. package/dist/api/index.d.ts +6 -6
  44. package/dist/api/index.d.ts.map +1 -1
  45. package/dist/api/index.js.map +1 -1
  46. package/dist/api/retry.d.ts +1 -1
  47. package/dist/api/retry.d.ts.map +1 -1
  48. package/dist/api/retry.js.map +1 -1
  49. package/dist/api/retry.test.d.ts +2 -0
  50. package/dist/api/retry.test.d.ts.map +1 -0
  51. package/dist/api/retry.test.js +193 -0
  52. package/dist/api/retry.test.js.map +1 -0
  53. package/dist/attachments/FeedbackSheet.d.ts +36 -13
  54. package/dist/attachments/FeedbackSheet.d.ts.map +1 -1
  55. package/dist/attachments/FeedbackSheet.js +50 -30
  56. package/dist/attachments/FeedbackSheet.js.map +1 -1
  57. package/dist/attachments/index.d.ts +2 -2
  58. package/dist/components/AttachmentGrid.d.ts +12 -4
  59. package/dist/components/AttachmentGrid.d.ts.map +1 -1
  60. package/dist/components/AttachmentGrid.js +44 -34
  61. package/dist/components/AttachmentGrid.js.map +1 -1
  62. package/dist/components/AttachmentPicker.d.ts +3 -3
  63. package/dist/components/AttachmentPicker.d.ts.map +1 -1
  64. package/dist/components/AttachmentPicker.js +34 -36
  65. package/dist/components/AttachmentPicker.js.map +1 -1
  66. package/dist/components/AttachmentPreview.d.ts +10 -4
  67. package/dist/components/AttachmentPreview.d.ts.map +1 -1
  68. package/dist/components/AttachmentPreview.js +48 -34
  69. package/dist/components/AttachmentPreview.js.map +1 -1
  70. package/dist/components/CategorySelector.d.ts +3 -3
  71. package/dist/components/CategorySelector.d.ts.map +1 -1
  72. package/dist/components/CategorySelector.js +21 -27
  73. package/dist/components/CategorySelector.js.map +1 -1
  74. package/dist/components/FeedbackForm.d.ts +3 -3
  75. package/dist/components/FeedbackForm.d.ts.map +1 -1
  76. package/dist/components/FeedbackForm.js +7 -8
  77. package/dist/components/FeedbackForm.js.map +1 -1
  78. package/dist/components/FeedbackSheet.d.ts +34 -11
  79. package/dist/components/FeedbackSheet.d.ts.map +1 -1
  80. package/dist/components/FeedbackSheet.js +46 -28
  81. package/dist/components/FeedbackSheet.js.map +1 -1
  82. package/dist/components/ThemedButton.d.ts +16 -5
  83. package/dist/components/ThemedButton.d.ts.map +1 -1
  84. package/dist/components/ThemedButton.js +38 -29
  85. package/dist/components/ThemedButton.js.map +1 -1
  86. package/dist/components/ThemedText.d.ts +3 -3
  87. package/dist/components/ThemedText.d.ts.map +1 -1
  88. package/dist/components/ThemedText.js +1 -1
  89. package/dist/components/ThemedText.js.map +1 -1
  90. package/dist/components/ThemedTextInput.d.ts +11 -2
  91. package/dist/components/ThemedTextInput.d.ts.map +1 -1
  92. package/dist/components/ThemedTextInput.js +19 -9
  93. package/dist/components/ThemedTextInput.js.map +1 -1
  94. package/dist/components/UploadStatusOverlay.d.ts +11 -3
  95. package/dist/components/UploadStatusOverlay.d.ts.map +1 -1
  96. package/dist/components/UploadStatusOverlay.js +59 -76
  97. package/dist/components/UploadStatusOverlay.js.map +1 -1
  98. package/dist/components/index.d.ts +18 -18
  99. package/dist/components/index.d.ts.map +1 -1
  100. package/dist/components/index.js.map +1 -1
  101. package/dist/context/HarkenContext.d.ts +20 -15
  102. package/dist/context/HarkenContext.d.ts.map +1 -1
  103. package/dist/context/HarkenContext.js +20 -17
  104. package/dist/context/HarkenContext.js.map +1 -1
  105. package/dist/context/index.d.ts +2 -2
  106. package/dist/domain/index.d.ts +2 -2
  107. package/dist/domain/index.d.ts.map +1 -1
  108. package/dist/domain/index.js.map +1 -1
  109. package/dist/hooks/index.d.ts +5 -5
  110. package/dist/hooks/useAnonymousId.js +1 -1
  111. package/dist/hooks/useAnonymousId.test.d.ts +2 -0
  112. package/dist/hooks/useAnonymousId.test.d.ts.map +1 -0
  113. package/dist/hooks/useAnonymousId.test.js +154 -0
  114. package/dist/hooks/useAnonymousId.test.js.map +1 -0
  115. package/dist/hooks/useAttachmentPicker.d.ts +3 -3
  116. package/dist/hooks/useAttachmentPicker.js +7 -7
  117. package/dist/hooks/useAttachmentStatus.d.ts +1 -1
  118. package/dist/hooks/useAttachmentStatus.d.ts.map +1 -1
  119. package/dist/hooks/useAttachmentStatus.js.map +1 -1
  120. package/dist/hooks/useAttachmentUpload.d.ts +2 -2
  121. package/dist/hooks/useAttachmentUpload.d.ts.map +1 -1
  122. package/dist/hooks/useAttachmentUpload.js +5 -5
  123. package/dist/hooks/useAttachmentUpload.js.map +1 -1
  124. package/dist/hooks/useAttachmentUpload.test.d.ts +2 -0
  125. package/dist/hooks/useAttachmentUpload.test.d.ts.map +1 -0
  126. package/dist/hooks/useAttachmentUpload.test.js +542 -0
  127. package/dist/hooks/useAttachmentUpload.test.js.map +1 -0
  128. package/dist/hooks/useFeedback.d.ts +4 -4
  129. package/dist/hooks/useFeedback.d.ts.map +1 -1
  130. package/dist/hooks/useFeedback.js +3 -5
  131. package/dist/hooks/useFeedback.js.map +1 -1
  132. package/dist/hooks/useFeedback.test.d.ts +2 -0
  133. package/dist/hooks/useFeedback.test.d.ts.map +1 -0
  134. package/dist/hooks/useFeedback.test.js +299 -0
  135. package/dist/hooks/useFeedback.test.js.map +1 -0
  136. package/dist/hooks/useHarkenContext.d.ts +1 -1
  137. package/dist/hooks/useHarkenContext.js +1 -1
  138. package/dist/hooks/useHarkenTheme.d.ts +27 -3
  139. package/dist/hooks/useHarkenTheme.d.ts.map +1 -1
  140. package/dist/hooks/useHarkenTheme.js +26 -2
  141. package/dist/hooks/useHarkenTheme.js.map +1 -1
  142. package/dist/index.d.ts +28 -28
  143. package/dist/index.d.ts.map +1 -1
  144. package/dist/index.js.map +1 -1
  145. package/dist/services/index.d.ts +3 -3
  146. package/dist/services/index.d.ts.map +1 -1
  147. package/dist/services/index.js.map +1 -1
  148. package/dist/services/uploadQueueService.d.ts +2 -2
  149. package/dist/services/uploadQueueService.d.ts.map +1 -1
  150. package/dist/services/uploadQueueService.js +16 -17
  151. package/dist/services/uploadQueueService.js.map +1 -1
  152. package/dist/services/uploadQueueService.test.d.ts +2 -0
  153. package/dist/services/uploadQueueService.test.d.ts.map +1 -0
  154. package/dist/services/uploadQueueService.test.js +426 -0
  155. package/dist/services/uploadQueueService.test.js.map +1 -0
  156. package/dist/services/uploadQueueStorage.d.ts +1 -1
  157. package/dist/services/uploadQueueStorage.d.ts.map +1 -1
  158. package/dist/services/uploadQueueStorage.js +4 -4
  159. package/dist/services/uploadQueueStorage.js.map +1 -1
  160. package/dist/services/uploadQueueStorage.test.d.ts +2 -0
  161. package/dist/services/uploadQueueStorage.test.d.ts.map +1 -0
  162. package/dist/services/uploadQueueStorage.test.js +200 -0
  163. package/dist/services/uploadQueueStorage.test.js.map +1 -0
  164. package/dist/storage/IdentityStore.d.ts +1 -1
  165. package/dist/storage/IdentityStore.d.ts.map +1 -1
  166. package/dist/storage/IdentityStore.js.map +1 -1
  167. package/dist/storage/IdentityStore.test.d.ts +2 -0
  168. package/dist/storage/IdentityStore.test.d.ts.map +1 -0
  169. package/dist/storage/IdentityStore.test.js +176 -0
  170. package/dist/storage/IdentityStore.test.js.map +1 -0
  171. package/dist/storage/SecureStoreAdapter.d.ts +1 -1
  172. package/dist/storage/SecureStoreAdapter.test.d.ts +2 -0
  173. package/dist/storage/SecureStoreAdapter.test.d.ts.map +1 -0
  174. package/dist/storage/SecureStoreAdapter.test.js +114 -0
  175. package/dist/storage/SecureStoreAdapter.test.js.map +1 -0
  176. package/dist/storage/defaultStorage.d.ts +1 -1
  177. package/dist/storage/defaultStorage.js +4 -4
  178. package/dist/storage/defaultStorage.test.d.ts +2 -0
  179. package/dist/storage/defaultStorage.test.d.ts.map +1 -0
  180. package/dist/storage/defaultStorage.test.js +159 -0
  181. package/dist/storage/defaultStorage.test.js.map +1 -0
  182. package/dist/storage/index.d.ts +5 -5
  183. package/dist/storage/types.js +1 -1
  184. package/dist/theme/defaults.d.ts +14 -3
  185. package/dist/theme/defaults.d.ts.map +1 -1
  186. package/dist/theme/defaults.js +58 -43
  187. package/dist/theme/defaults.js.map +1 -1
  188. package/dist/theme/index.d.ts +3 -2
  189. package/dist/theme/index.d.ts.map +1 -1
  190. package/dist/theme/index.js +4 -1
  191. package/dist/theme/index.js.map +1 -1
  192. package/dist/theme/resolver.d.ts +16 -0
  193. package/dist/theme/resolver.d.ts.map +1 -0
  194. package/dist/theme/resolver.js +375 -0
  195. package/dist/theme/resolver.js.map +1 -0
  196. package/dist/theme/resolver.test.d.ts +2 -0
  197. package/dist/theme/resolver.test.d.ts.map +1 -0
  198. package/dist/theme/resolver.test.js +344 -0
  199. package/dist/theme/resolver.test.js.map +1 -0
  200. package/dist/theme/types.d.ts +378 -5
  201. package/dist/theme/types.d.ts.map +1 -1
  202. package/dist/types/config.d.ts +4 -4
  203. package/dist/types/index.d.ts +2 -2
  204. package/dist/utils/index.d.ts +1 -1
  205. package/dist/utils/uuid.d.ts.map +1 -1
  206. package/dist/utils/uuid.js +4 -5
  207. package/dist/utils/uuid.js.map +1 -1
  208. package/dist/utils/uuid.test.d.ts +2 -0
  209. package/dist/utils/uuid.test.d.ts.map +1 -0
  210. package/dist/utils/uuid.test.js +78 -0
  211. package/dist/utils/uuid.test.js.map +1 -0
  212. package/package.json +21 -13
  213. package/src/@types/expo-file-system-legacy.d.ts +3 -3
  214. package/src/__mocks__/async-storage.ts +46 -0
  215. package/src/__mocks__/expo-document-picker.ts +41 -0
  216. package/src/__mocks__/expo-file-system.ts +62 -0
  217. package/src/__mocks__/expo-image-picker.ts +48 -0
  218. package/src/__mocks__/expo-secure-store.ts +29 -0
  219. package/src/__mocks__/react-native.ts +46 -0
  220. package/src/api/client.test.ts +515 -0
  221. package/src/api/client.ts +45 -64
  222. package/src/api/errors.test.ts +193 -0
  223. package/src/api/errors.ts +7 -11
  224. package/src/api/index.ts +6 -10
  225. package/src/api/retry.test.ts +251 -0
  226. package/src/api/retry.ts +3 -6
  227. package/src/attachments/FeedbackSheet.tsx +100 -80
  228. package/src/attachments/index.ts +2 -2
  229. package/src/components/AttachmentGrid.tsx +54 -45
  230. package/src/components/AttachmentPicker.tsx +43 -54
  231. package/src/components/AttachmentPreview.tsx +51 -47
  232. package/src/components/CategorySelector.tsx +29 -35
  233. package/src/components/FeedbackForm.tsx +23 -35
  234. package/src/components/FeedbackSheet.tsx +89 -68
  235. package/src/components/ThemedButton.tsx +49 -47
  236. package/src/components/ThemedText.tsx +7 -10
  237. package/src/components/ThemedTextInput.tsx +23 -13
  238. package/src/components/UploadStatusOverlay.tsx +66 -89
  239. package/src/components/index.ts +18 -21
  240. package/src/context/HarkenContext.tsx +29 -28
  241. package/src/context/index.ts +2 -2
  242. package/src/domain/index.ts +2 -5
  243. package/src/domain/upload-queue.ts +5 -5
  244. package/src/hooks/index.ts +5 -5
  245. package/src/hooks/useAnonymousId.test.ts +189 -0
  246. package/src/hooks/useAnonymousId.ts +3 -3
  247. package/src/hooks/useAttachmentPicker.ts +12 -12
  248. package/src/hooks/useAttachmentStatus.ts +12 -16
  249. package/src/hooks/useAttachmentUpload.test.ts +632 -0
  250. package/src/hooks/useAttachmentUpload.ts +45 -54
  251. package/src/hooks/useFeedback.test.ts +376 -0
  252. package/src/hooks/useFeedback.ts +12 -14
  253. package/src/hooks/useHarkenContext.ts +4 -4
  254. package/src/hooks/useHarkenTheme.ts +30 -6
  255. package/src/index.ts +28 -52
  256. package/src/services/index.ts +3 -9
  257. package/src/services/uploadQueueService.test.ts +489 -0
  258. package/src/services/uploadQueueService.ts +40 -56
  259. package/src/services/uploadQueueStorage.test.ts +243 -0
  260. package/src/services/uploadQueueStorage.ts +7 -9
  261. package/src/storage/IdentityStore.test.ts +173 -0
  262. package/src/storage/IdentityStore.ts +4 -5
  263. package/src/storage/SecureStoreAdapter.test.ts +147 -0
  264. package/src/storage/SecureStoreAdapter.ts +1 -1
  265. package/src/storage/defaultStorage.test.ts +159 -0
  266. package/src/storage/defaultStorage.ts +6 -6
  267. package/src/storage/index.ts +5 -5
  268. package/src/storage/types.ts +1 -1
  269. package/src/theme/defaults.ts +75 -46
  270. package/src/theme/index.ts +15 -2
  271. package/src/theme/resolver.test.ts +411 -0
  272. package/src/theme/resolver.ts +446 -0
  273. package/src/theme/types.ts +453 -15
  274. package/src/types/config.ts +4 -4
  275. package/src/types/index.ts +2 -2
  276. package/src/utils/index.ts +1 -1
  277. package/src/utils/uuid.test.ts +85 -0
  278. package/src/utils/uuid.ts +4 -7
package/src/api/client.ts CHANGED
@@ -1,20 +1,20 @@
1
- import type { components } from '../types/index.js';
2
- import { HarkenApiError, HarkenNetworkError } from './errors';
3
- import { withRetry } from './retry';
4
- import type { RetryConfig } from './retry';
1
+ import type { components } from "../types/index.js";
2
+ import { HarkenApiError, HarkenNetworkError } from "./errors";
3
+ import { withRetry } from "./retry";
4
+ import type { RetryConfig } from "./retry";
5
5
 
6
6
  // Re-export types from contracts for convenience
7
- type FeedbackSubmission = components['schemas']['FeedbackSubmission'];
8
- type FeedbackSubmissionResponse = components['schemas']['FeedbackSubmissionResponse'];
9
- type ErrorResponse = components['schemas']['ErrorResponse'];
7
+ type FeedbackSubmission = components["schemas"]["FeedbackSubmission"];
8
+ type FeedbackSubmissionResponse = components["schemas"]["FeedbackSubmissionResponse"];
9
+ type ErrorResponse = components["schemas"]["ErrorResponse"];
10
10
 
11
11
  // Attachment types
12
- type AttachmentPresignRequest = components['schemas']['AttachmentPresignRequest'];
13
- type AttachmentPresignResponse = components['schemas']['AttachmentPresignResponse'];
14
- type AttachmentConfirmRequest = components['schemas']['AttachmentConfirmRequest'];
15
- type AttachmentStatusResponse = components['schemas']['AttachmentStatusResponse'];
12
+ type AttachmentPresignRequest = components["schemas"]["AttachmentPresignRequest"];
13
+ type AttachmentPresignResponse = components["schemas"]["AttachmentPresignResponse"];
14
+ type AttachmentConfirmRequest = components["schemas"]["AttachmentConfirmRequest"];
15
+ type AttachmentStatusResponse = components["schemas"]["AttachmentStatusResponse"];
16
16
 
17
- const DEFAULT_API_BASE_URL = 'https://api.harken.app';
17
+ const DEFAULT_API_BASE_URL = "https://api.harken.app";
18
18
 
19
19
  export interface HarkenClientConfig {
20
20
  /** Publishable API key */
@@ -34,9 +34,9 @@ export interface HarkenClientConfig {
34
34
  */
35
35
  export class HarkenClient {
36
36
  private readonly config: Required<
37
- Pick<HarkenClientConfig, 'publishableKey' | 'baseUrl' | 'timeout'>
37
+ Pick<HarkenClientConfig, "publishableKey" | "baseUrl" | "timeout">
38
38
  > &
39
- Pick<HarkenClientConfig, 'userToken' | 'retry'>;
39
+ Pick<HarkenClientConfig, "userToken" | "retry">;
40
40
 
41
41
  constructor(config: HarkenClientConfig) {
42
42
  this.config = {
@@ -51,14 +51,13 @@ export class HarkenClient {
51
51
  /**
52
52
  * Submit feedback to the API.
53
53
  */
54
- async submitFeedback(
55
- submission: FeedbackSubmission
56
- ): Promise<FeedbackSubmissionResponse> {
54
+ async submitFeedback(submission: FeedbackSubmission): Promise<FeedbackSubmissionResponse> {
57
55
  return withRetry(
58
- () => this.request<FeedbackSubmissionResponse>('/v1/feedback', {
59
- method: 'POST',
60
- body: JSON.stringify(submission),
61
- }),
56
+ () =>
57
+ this.request<FeedbackSubmissionResponse>("/v1/feedback", {
58
+ method: "POST",
59
+ body: JSON.stringify(submission),
60
+ }),
62
61
  this.config.retry
63
62
  );
64
63
  }
@@ -71,13 +70,10 @@ export class HarkenClient {
71
70
  ): Promise<AttachmentPresignResponse> {
72
71
  return withRetry(
73
72
  () =>
74
- this.request<AttachmentPresignResponse>(
75
- '/v1/feedback/attachments/presign',
76
- {
77
- method: 'POST',
78
- body: JSON.stringify(request),
79
- }
80
- ),
73
+ this.request<AttachmentPresignResponse>("/v1/feedback/attachments/presign", {
74
+ method: "POST",
75
+ body: JSON.stringify(request),
76
+ }),
81
77
  this.config.retry
82
78
  );
83
79
  }
@@ -91,13 +87,10 @@ export class HarkenClient {
91
87
  ): Promise<AttachmentStatusResponse> {
92
88
  return withRetry(
93
89
  () =>
94
- this.request<AttachmentStatusResponse>(
95
- `/v1/feedback/attachments/${attachmentId}/confirm`,
96
- {
97
- method: 'POST',
98
- body: request ? JSON.stringify(request) : undefined,
99
- }
100
- ),
90
+ this.request<AttachmentStatusResponse>(`/v1/feedback/attachments/${attachmentId}/confirm`, {
91
+ method: "POST",
92
+ body: request ? JSON.stringify(request) : undefined,
93
+ }),
101
94
  this.config.retry
102
95
  );
103
96
  }
@@ -110,43 +103,33 @@ export class HarkenClient {
110
103
  attachmentId: string,
111
104
  error?: string
112
105
  ): Promise<AttachmentStatusResponse> {
113
- return this.request<AttachmentStatusResponse>(
114
- `/v1/feedback/attachments/${attachmentId}/fail`,
115
- {
116
- method: 'POST',
117
- body: error ? JSON.stringify({ error }) : undefined,
118
- }
119
- );
106
+ return this.request<AttachmentStatusResponse>(`/v1/feedback/attachments/${attachmentId}/fail`, {
107
+ method: "POST",
108
+ body: error ? JSON.stringify({ error }) : undefined,
109
+ });
120
110
  }
121
111
 
122
112
  /**
123
113
  * Get attachment status and download URLs.
124
114
  */
125
- async getAttachmentStatus(
126
- attachmentId: string
127
- ): Promise<AttachmentStatusResponse> {
128
- return this.request<AttachmentStatusResponse>(
129
- `/v1/feedback/attachments/${attachmentId}`
130
- );
115
+ async getAttachmentStatus(attachmentId: string): Promise<AttachmentStatusResponse> {
116
+ return this.request<AttachmentStatusResponse>(`/v1/feedback/attachments/${attachmentId}`);
131
117
  }
132
118
 
133
119
  /**
134
120
  * Make an authenticated request to the API.
135
121
  */
136
- private async request<T>(
137
- path: string,
138
- options: RequestInit = {}
139
- ): Promise<T> {
122
+ private async request<T>(path: string, options: RequestInit = {}): Promise<T> {
140
123
  const url = `${this.config.baseUrl}${path}`;
141
124
 
142
125
  const headers: Record<string, string> = {
143
- 'Content-Type': 'application/json',
144
- 'X-Publishable-Key': this.config.publishableKey,
126
+ "Content-Type": "application/json",
127
+ "X-Publishable-Key": this.config.publishableKey,
145
128
  ...(options.headers as Record<string, string>),
146
129
  };
147
130
 
148
131
  if (this.config.userToken) {
149
- headers['X-User-Token'] = this.config.userToken;
132
+ headers["X-User-Token"] = this.config.userToken;
150
133
  }
151
134
 
152
135
  // Create abort controller for timeout
@@ -165,9 +148,7 @@ export class HarkenClient {
165
148
  if (!response.ok) {
166
149
  const errorBody = await this.parseErrorResponse(response);
167
150
  // Only parse Retry-After for 429 responses
168
- const retryAfter = response.status === 429
169
- ? this.parseRetryAfter(response)
170
- : undefined;
151
+ const retryAfter = response.status === 429 ? this.parseRetryAfter(response) : undefined;
171
152
  throw new HarkenApiError(response.status, errorBody, { retryAfter });
172
153
  }
173
154
 
@@ -181,18 +162,18 @@ export class HarkenClient {
181
162
  }
182
163
 
183
164
  // Handle abort (timeout)
184
- if (error instanceof Error && error.name === 'AbortError') {
185
- throw new HarkenNetworkError('Request timed out', error);
165
+ if (error instanceof Error && error.name === "AbortError") {
166
+ throw new HarkenNetworkError("Request timed out", error);
186
167
  }
187
168
 
188
169
  // Handle other fetch errors (network issues)
189
170
  if (error instanceof TypeError) {
190
- throw new HarkenNetworkError('Network request failed', error);
171
+ throw new HarkenNetworkError("Network request failed", error);
191
172
  }
192
173
 
193
174
  // Unknown error
194
175
  throw new HarkenNetworkError(
195
- error instanceof Error ? error.message : 'Unknown error',
176
+ error instanceof Error ? error.message : "Unknown error",
196
177
  error instanceof Error ? error : undefined
197
178
  );
198
179
  }
@@ -209,7 +190,7 @@ export class HarkenClient {
209
190
  return {
210
191
  error: {
211
192
  code: `http_${response.status}`,
212
- message: response.statusText || 'Request failed',
193
+ message: response.statusText || "Request failed",
213
194
  },
214
195
  };
215
196
  }
@@ -220,7 +201,7 @@ export class HarkenClient {
220
201
  * Supports both delta-seconds and HTTP-date formats.
221
202
  */
222
203
  private parseRetryAfter(response: Response): number | undefined {
223
- const retryAfter = response.headers.get('Retry-After');
204
+ const retryAfter = response.headers.get("Retry-After");
224
205
  if (!retryAfter) {
225
206
  return undefined;
226
207
  }
@@ -0,0 +1,193 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { HarkenError, HarkenApiError, HarkenNetworkError } from "./errors";
3
+
4
+ describe("HarkenError", () => {
5
+ it("creates error with message", () => {
6
+ const error = new HarkenError("Something went wrong");
7
+ expect(error.message).toBe("Something went wrong");
8
+ });
9
+ });
10
+
11
+ describe("HarkenApiError", () => {
12
+ it("creates error with status, code, and message", () => {
13
+ const error = new HarkenApiError(400, {
14
+ error: {
15
+ code: "validation_error",
16
+ message: "Invalid input",
17
+ },
18
+ });
19
+
20
+ expect(error.status).toBe(400);
21
+ expect(error.code).toBe("validation_error");
22
+ expect(error.message).toBe("Invalid input");
23
+ });
24
+
25
+ it("includes error details when provided", () => {
26
+ const error = new HarkenApiError(400, {
27
+ error: {
28
+ code: "validation_error",
29
+ message: "Invalid input",
30
+ details: [
31
+ { field: "message", message: "Required field" },
32
+ { field: "category", message: "Invalid value" },
33
+ ],
34
+ },
35
+ });
36
+
37
+ expect(error.details).toHaveLength(2);
38
+ expect(error.details?.[0]).toEqual({
39
+ field: "message",
40
+ message: "Required field",
41
+ });
42
+ });
43
+
44
+ it("stores retryAfter when provided", () => {
45
+ const error = new HarkenApiError(
46
+ 429,
47
+ { error: { code: "rate_limited", message: "Too many requests" } },
48
+ { retryAfter: 60 }
49
+ );
50
+
51
+ expect(error.retryAfter).toBe(60);
52
+ });
53
+
54
+ describe("isValidationError", () => {
55
+ it("returns true for 400 status", () => {
56
+ const error = new HarkenApiError(400, {
57
+ error: { code: "validation_error", message: "Invalid" },
58
+ });
59
+ expect(error.isValidationError).toBe(true);
60
+ });
61
+
62
+ it("returns false for other statuses", () => {
63
+ const error = new HarkenApiError(401, {
64
+ error: { code: "unauthorized", message: "Unauthorized" },
65
+ });
66
+ expect(error.isValidationError).toBe(false);
67
+ });
68
+ });
69
+
70
+ describe("isUnauthorized", () => {
71
+ it("returns true for 401 status", () => {
72
+ const error = new HarkenApiError(401, {
73
+ error: { code: "unauthorized", message: "Unauthorized" },
74
+ });
75
+ expect(error.isUnauthorized).toBe(true);
76
+ });
77
+
78
+ it("returns false for other statuses", () => {
79
+ const error = new HarkenApiError(403, {
80
+ error: { code: "forbidden", message: "Forbidden" },
81
+ });
82
+ expect(error.isUnauthorized).toBe(false);
83
+ });
84
+ });
85
+
86
+ describe("isRateLimited", () => {
87
+ it("returns true for 429 status", () => {
88
+ const error = new HarkenApiError(429, {
89
+ error: { code: "rate_limited", message: "Too many requests" },
90
+ });
91
+ expect(error.isRateLimited).toBe(true);
92
+ });
93
+
94
+ it("returns false for other statuses", () => {
95
+ const error = new HarkenApiError(400, {
96
+ error: { code: "validation_error", message: "Invalid" },
97
+ });
98
+ expect(error.isRateLimited).toBe(false);
99
+ });
100
+ });
101
+
102
+ describe("isServerError", () => {
103
+ it("returns true for 5xx statuses", () => {
104
+ expect(
105
+ new HarkenApiError(500, {
106
+ error: { code: "internal_error", message: "Server error" },
107
+ }).isServerError
108
+ ).toBe(true);
109
+
110
+ expect(
111
+ new HarkenApiError(502, {
112
+ error: { code: "bad_gateway", message: "Bad gateway" },
113
+ }).isServerError
114
+ ).toBe(true);
115
+
116
+ expect(
117
+ new HarkenApiError(503, {
118
+ error: { code: "service_unavailable", message: "Service unavailable" },
119
+ }).isServerError
120
+ ).toBe(true);
121
+ });
122
+
123
+ it("returns false for 4xx statuses", () => {
124
+ expect(
125
+ new HarkenApiError(400, {
126
+ error: { code: "validation_error", message: "Invalid" },
127
+ }).isServerError
128
+ ).toBe(false);
129
+
130
+ expect(
131
+ new HarkenApiError(429, {
132
+ error: { code: "rate_limited", message: "Rate limited" },
133
+ }).isServerError
134
+ ).toBe(false);
135
+ });
136
+ });
137
+
138
+ describe("isRetryable", () => {
139
+ it("returns true for 429 (rate limited)", () => {
140
+ const error = new HarkenApiError(429, {
141
+ error: { code: "rate_limited", message: "Too many requests" },
142
+ });
143
+ expect(error.isRetryable).toBe(true);
144
+ });
145
+
146
+ it("returns true for 5xx (server errors)", () => {
147
+ const error500 = new HarkenApiError(500, {
148
+ error: { code: "internal_error", message: "Server error" },
149
+ });
150
+ const error503 = new HarkenApiError(503, {
151
+ error: { code: "service_unavailable", message: "Unavailable" },
152
+ });
153
+
154
+ expect(error500.isRetryable).toBe(true);
155
+ expect(error503.isRetryable).toBe(true);
156
+ });
157
+
158
+ it("returns false for 4xx (client errors except 429)", () => {
159
+ const error400 = new HarkenApiError(400, {
160
+ error: { code: "validation_error", message: "Invalid" },
161
+ });
162
+ const error401 = new HarkenApiError(401, {
163
+ error: { code: "unauthorized", message: "Unauthorized" },
164
+ });
165
+ const error404 = new HarkenApiError(404, {
166
+ error: { code: "not_found", message: "Not found" },
167
+ });
168
+
169
+ expect(error400.isRetryable).toBe(false);
170
+ expect(error401.isRetryable).toBe(false);
171
+ expect(error404.isRetryable).toBe(false);
172
+ });
173
+ });
174
+ });
175
+
176
+ describe("HarkenNetworkError", () => {
177
+ it("creates error with message", () => {
178
+ const error = new HarkenNetworkError("Network request failed");
179
+ expect(error.message).toBe("Network request failed");
180
+ });
181
+
182
+ it("stores cause when provided", () => {
183
+ const cause = new TypeError("Failed to fetch");
184
+ const error = new HarkenNetworkError("Network request failed", cause);
185
+
186
+ expect(error.cause).toBe(cause);
187
+ });
188
+
189
+ it("isRetryable always returns true", () => {
190
+ const error = new HarkenNetworkError("Network failed");
191
+ expect(error.isRetryable).toBe(true);
192
+ });
193
+ });
package/src/api/errors.ts CHANGED
@@ -1,7 +1,7 @@
1
- import type { components } from '../types/index.js';
1
+ import type { components } from "../types/index.js";
2
2
 
3
- type ErrorResponse = components['schemas']['ErrorResponse'];
4
- type ErrorDetail = components['schemas']['ErrorDetail'];
3
+ type ErrorResponse = components["schemas"]["ErrorResponse"];
4
+ type ErrorDetail = components["schemas"]["ErrorDetail"];
5
5
 
6
6
  /**
7
7
  * Base error class for Harken API errors.
@@ -9,7 +9,7 @@ type ErrorDetail = components['schemas']['ErrorDetail'];
9
9
  export class HarkenError extends Error {
10
10
  constructor(message: string) {
11
11
  super(message);
12
- this.name = 'HarkenError';
12
+ this.name = "HarkenError";
13
13
  }
14
14
  }
15
15
 
@@ -26,13 +26,9 @@ export class HarkenApiError extends HarkenError {
26
26
  /** Retry-After value in seconds (from 429 responses) */
27
27
  readonly retryAfter?: number;
28
28
 
29
- constructor(
30
- status: number,
31
- response: ErrorResponse,
32
- options?: { retryAfter?: number }
33
- ) {
29
+ constructor(status: number, response: ErrorResponse, options?: { retryAfter?: number }) {
34
30
  super(response.error.message);
35
- this.name = 'HarkenApiError';
31
+ this.name = "HarkenApiError";
36
32
  this.status = status;
37
33
  this.code = response.error.code;
38
34
  this.details = response.error.details;
@@ -73,7 +69,7 @@ export class HarkenNetworkError extends HarkenError {
73
69
 
74
70
  constructor(message: string, cause?: Error) {
75
71
  super(message);
76
- this.name = 'HarkenNetworkError';
72
+ this.name = "HarkenNetworkError";
77
73
  this.cause = cause;
78
74
  }
79
75
 
package/src/api/index.ts CHANGED
@@ -1,15 +1,11 @@
1
1
  // Client
2
- export { HarkenClient, createHarkenClient } from './client';
3
- export type { HarkenClientConfig } from './client';
2
+ export { HarkenClient, createHarkenClient } from "./client";
3
+ export type { HarkenClientConfig } from "./client";
4
4
 
5
5
  // Errors
6
- export {
7
- HarkenError,
8
- HarkenApiError,
9
- HarkenNetworkError,
10
- } from './errors';
6
+ export { HarkenError, HarkenApiError, HarkenNetworkError } from "./errors";
11
7
 
12
8
  // Retry utilities
13
- export { withRetry, calculateRetryDelay, isRetryableError } from './retry';
14
- export type { RetryConfig } from './retry';
15
- export { DEFAULT_RETRY_CONFIG } from './retry';
9
+ export { withRetry, calculateRetryDelay, isRetryableError } from "./retry";
10
+ export type { RetryConfig } from "./retry";
11
+ export { DEFAULT_RETRY_CONFIG } from "./retry";