@autobe/ui 0.29.2 → 0.30.0-dev.20260315

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 (151) hide show
  1. package/LICENSE +661 -661
  2. package/README.md +261 -0
  3. package/lib/components/AutoBeChatMain.js +5 -5
  4. package/lib/components/AutoBeChatMain.js.map +1 -1
  5. package/lib/components/AutoBeConfigModal.js +9 -9
  6. package/lib/components/AutoBeStatusModal.js +4 -4
  7. package/lib/components/AutoBeStatusModal.js.map +1 -1
  8. package/lib/components/AutoBeUserMessageMovie.d.ts +2 -2
  9. package/lib/components/common/ChatBubble.d.ts +2 -2
  10. package/lib/components/common/openai/OpenAIContent.d.ts +2 -2
  11. package/lib/components/common/openai/OpenAIContent.js.map +1 -1
  12. package/lib/components/common/openai/OpenAIUserAudioContent.js +1 -1
  13. package/lib/components/common/openai/OpenAIUserAudioContent.js.map +1 -1
  14. package/lib/components/common/openai/OpenAIUserFileContent.js +1 -1
  15. package/lib/components/common/openai/OpenAIUserFileContent.js.map +1 -1
  16. package/lib/components/common/openai/OpenAIUserImageContent.d.ts +2 -2
  17. package/lib/components/events/AutoBeCompleteEventMovie.d.ts +2 -2
  18. package/lib/components/events/AutoBeCompleteEventMovie.js +5 -5
  19. package/lib/components/events/AutoBeCompleteEventMovie.js.map +1 -1
  20. package/lib/components/events/AutoBeCorrectEventMovie.d.ts +2 -2
  21. package/lib/components/events/AutoBeCorrectEventMovie.js +4 -4
  22. package/lib/components/events/AutoBeCorrectEventMovie.js.map +1 -1
  23. package/lib/components/events/AutoBeEventMovie.js +38 -17
  24. package/lib/components/events/AutoBeEventMovie.js.map +1 -1
  25. package/lib/components/events/AutoBeProgressEventMovie.js +73 -13
  26. package/lib/components/events/AutoBeProgressEventMovie.js.map +1 -1
  27. package/lib/components/events/AutoBeScenarioEventMovie.d.ts +2 -2
  28. package/lib/components/events/AutoBeScenarioEventMovie.js +18 -5
  29. package/lib/components/events/AutoBeScenarioEventMovie.js.map +1 -1
  30. package/lib/components/events/AutoBeStartEventMovie.d.ts +2 -2
  31. package/lib/components/events/AutoBeStartEventMovie.js +2 -2
  32. package/lib/components/events/AutoBeStartEventMovie.js.map +1 -1
  33. package/lib/components/events/AutoBeValidateEventMovie.d.ts +2 -2
  34. package/lib/components/events/AutoBeValidateEventMovie.js +3 -11
  35. package/lib/components/events/AutoBeValidateEventMovie.js.map +1 -1
  36. package/lib/components/events/groups/CorrectEventGroup.d.ts +2 -2
  37. package/lib/components/events/groups/CorrectEventGroup.js +1 -1
  38. package/lib/components/events/groups/CorrectEventGroup.js.map +1 -1
  39. package/lib/components/events/groups/ValidateEventGroup.d.ts +2 -2
  40. package/lib/components/events/groups/ValidateEventGroup.js +1 -2
  41. package/lib/components/events/groups/ValidateEventGroup.js.map +1 -1
  42. package/lib/components/events/utils/eventGrouper.js +1 -2
  43. package/lib/components/events/utils/eventGrouper.js.map +1 -1
  44. package/lib/components/upload/AutoBeChatUploadBox.d.ts +3 -4
  45. package/lib/components/upload/AutoBeChatUploadBox.js +2 -1
  46. package/lib/components/upload/AutoBeChatUploadBox.js.map +1 -1
  47. package/lib/components/upload/AutoBeChatUploadSendButton.js +1 -1
  48. package/lib/components/upload/AutoBeChatUploadSendButton.js.map +1 -1
  49. package/lib/context/AutoBeAgentContext.d.ts +1 -3
  50. package/lib/context/AutoBeAgentContext.js +0 -4
  51. package/lib/context/AutoBeAgentContext.js.map +1 -1
  52. package/lib/hooks/useSessionStorage.d.ts +4 -0
  53. package/lib/hooks/useSessionStorage.js +16 -0
  54. package/lib/hooks/useSessionStorage.js.map +1 -0
  55. package/lib/index.d.ts +1 -0
  56. package/lib/index.js +1 -0
  57. package/lib/index.js.map +1 -1
  58. package/lib/strategy/AutoBeAgentSessionStorageStrategy.d.ts +10 -0
  59. package/lib/strategy/AutoBeAgentSessionStorageStrategy.js +117 -0
  60. package/lib/strategy/AutoBeAgentSessionStorageStrategy.js.map +1 -0
  61. package/lib/structure/AutoBeListener.js +91 -23
  62. package/lib/structure/AutoBeListener.js.map +1 -1
  63. package/lib/structure/AutoBeListenerState.d.ts +3 -3
  64. package/lib/structure/AutoBeListenerState.js +4 -4
  65. package/lib/structure/AutoBeListenerState.js.map +1 -1
  66. package/lib/structure/IAutoBeAgentSessionStorageStrategy.js +1 -1
  67. package/lib/structure/IAutoBeAgentSessionStorageStrategy.js.map +1 -1
  68. package/lib/utils/AutoBeFileUploader.d.ts +2 -2
  69. package/lib/utils/AutoBeFileUploader.js.map +1 -1
  70. package/package.json +3 -4
  71. package/src/components/AutoBeAssistantMessageMovie.tsx +22 -22
  72. package/src/components/AutoBeChatMain.tsx +376 -376
  73. package/src/components/AutoBeChatSidebar.tsx +414 -414
  74. package/src/components/AutoBeConfigButton.tsx +83 -83
  75. package/src/components/AutoBeConfigModal.tsx +443 -443
  76. package/src/components/AutoBeStatusButton.tsx +75 -75
  77. package/src/components/AutoBeStatusModal.tsx +486 -484
  78. package/src/components/AutoBeUserMessageMovie.tsx +27 -27
  79. package/src/components/common/ActionButton.tsx +205 -205
  80. package/src/components/common/ActionButtonGroup.tsx +80 -80
  81. package/src/components/common/AutoBeConfigInput.tsx +185 -185
  82. package/src/components/common/ChatBubble.tsx +119 -119
  83. package/src/components/common/Collapsible.tsx +95 -95
  84. package/src/components/common/CompactSessionIndicator.tsx +73 -73
  85. package/src/components/common/CompactSessionList.tsx +82 -82
  86. package/src/components/common/index.ts +8 -8
  87. package/src/components/common/openai/OpenAIContent.tsx +53 -53
  88. package/src/components/common/openai/OpenAIUserAudioContent.tsx +70 -70
  89. package/src/components/common/openai/OpenAIUserFileContent.tsx +76 -76
  90. package/src/components/common/openai/OpenAIUserImageContent.tsx +34 -34
  91. package/src/components/common/openai/OpenAIUserTextContent.tsx +15 -15
  92. package/src/components/common/openai/index.ts +5 -5
  93. package/src/components/events/AutoBeCompleteEventMovie.tsx +402 -402
  94. package/src/components/events/AutoBeCorrectEventMovie.tsx +354 -368
  95. package/src/components/events/AutoBeEventGroupMovie.tsx +18 -18
  96. package/src/components/events/AutoBeEventMovie.tsx +158 -139
  97. package/src/components/events/AutoBeProgressEventMovie.tsx +217 -157
  98. package/src/components/events/AutoBeScenarioEventMovie.tsx +135 -95
  99. package/src/components/events/AutoBeStartEventMovie.tsx +82 -82
  100. package/src/components/events/AutoBeValidateEventMovie.tsx +249 -286
  101. package/src/components/events/README.md +300 -300
  102. package/src/components/events/common/CollapsibleEventGroup.tsx +211 -211
  103. package/src/components/events/common/EventCard.tsx +61 -61
  104. package/src/components/events/common/EventContent.tsx +31 -31
  105. package/src/components/events/common/EventHeader.tsx +85 -85
  106. package/src/components/events/common/EventIcon.tsx +82 -82
  107. package/src/components/events/common/ProgressBar.tsx +64 -64
  108. package/src/components/events/common/index.ts +13 -13
  109. package/src/components/events/groups/CorrectEventGroup.tsx +183 -183
  110. package/src/components/events/groups/ValidateEventGroup.tsx +143 -146
  111. package/src/components/events/groups/index.ts +8 -8
  112. package/src/components/events/index.ts +16 -16
  113. package/src/components/events/utils/eventGrouper.tsx +116 -117
  114. package/src/components/events/utils/index.ts +1 -1
  115. package/src/components/index.ts +13 -13
  116. package/src/components/upload/AutoBeChatUploadBox.tsx +425 -424
  117. package/src/components/upload/AutoBeChatUploadSendButton.tsx +66 -66
  118. package/src/components/upload/AutoBeFileUploadBox.tsx +123 -123
  119. package/src/components/upload/AutoBeUploadConfig.ts +5 -5
  120. package/src/components/upload/AutoBeVoiceRecoderButton.tsx +100 -100
  121. package/src/components/upload/index.ts +5 -5
  122. package/src/constant/color.ts +28 -28
  123. package/src/context/AutoBeAgentContext.tsx +245 -258
  124. package/src/context/AutoBeAgentSessionList.tsx +58 -58
  125. package/src/context/SearchParamsContext.tsx +49 -49
  126. package/src/hooks/index.ts +3 -3
  127. package/src/hooks/useEscapeKey.ts +24 -24
  128. package/src/hooks/useIsomorphicLayoutEffect.ts +8 -8
  129. package/src/hooks/useMediaQuery.ts +73 -73
  130. package/src/hooks/useSessionStorage.ts +10 -0
  131. package/src/icons/Receipt.tsx +74 -74
  132. package/src/index.ts +9 -8
  133. package/src/strategy/AutoBeAgentSessionStorageStrategy.ts +127 -0
  134. package/src/structure/AutoBeListener.ts +373 -304
  135. package/src/structure/AutoBeListenerState.ts +53 -53
  136. package/src/structure/IAutoBeAgentSessionStorageStrategy.ts +87 -87
  137. package/src/structure/IAutoBeEventGroup.ts +6 -6
  138. package/src/structure/index.ts +4 -4
  139. package/src/types/config.ts +44 -44
  140. package/src/types/index.ts +1 -1
  141. package/src/utils/AutoBeFileUploader.ts +279 -279
  142. package/src/utils/AutoBeVoiceRecorder.ts +95 -95
  143. package/src/utils/__tests__/crypto.test.ts +286 -286
  144. package/src/utils/__tests__/storage.test.ts +229 -229
  145. package/src/utils/crypto.ts +95 -95
  146. package/src/utils/index.ts +6 -6
  147. package/src/utils/number.ts +17 -17
  148. package/src/utils/storage.ts +96 -96
  149. package/src/utils/time.ts +14 -14
  150. package/tsconfig.json +9 -9
  151. package/vitest.config.ts +15 -15
@@ -1,229 +1,229 @@
1
- import { beforeEach, describe, expect, test, vi } from "vitest";
2
-
3
- import {
4
- clearEncryptedSessionStorage,
5
- getEncryptedSessionStorage,
6
- hasEncryptedSessionStorage,
7
- removeEncryptedSessionStorage,
8
- setEncryptedSessionStorage,
9
- } from "../storage";
10
-
11
- // Mock sessionStorage for testing
12
- const mockSessionStorage = {
13
- store: new Map<string, string>(),
14
- getItem: vi.fn((key: string) => mockSessionStorage.store.get(key) || null),
15
- setItem: vi.fn((key: string, value: string) => {
16
- mockSessionStorage.store.set(key, value);
17
- }),
18
- removeItem: vi.fn((key: string) => {
19
- mockSessionStorage.store.delete(key);
20
- }),
21
- clear: vi.fn(() => {
22
- mockSessionStorage.store.clear();
23
- }),
24
- };
25
-
26
- // Mock global sessionStorage
27
- Object.defineProperty(global, "sessionStorage", {
28
- value: mockSessionStorage,
29
- writable: true,
30
- });
31
-
32
- Object.defineProperty(global, "window", {
33
- value: { sessionStorage: mockSessionStorage },
34
- writable: true,
35
- });
36
-
37
- describe("Storage Utils", () => {
38
- beforeEach(() => {
39
- mockSessionStorage.store.clear();
40
- vi.clearAllMocks();
41
- });
42
-
43
- describe("encrypted sessionStorage operations", () => {
44
- test("should store and retrieve encrypted data", () => {
45
- const key = "test_key";
46
- const value = "sk-test123456789";
47
-
48
- setEncryptedSessionStorage(key, value);
49
- const retrieved = getEncryptedSessionStorage(key);
50
-
51
- expect(retrieved).toBe(value);
52
- expect(mockSessionStorage.setItem).toHaveBeenCalledWith(
53
- key,
54
- expect.any(String),
55
- );
56
- expect(mockSessionStorage.getItem).toHaveBeenCalledWith(key);
57
- });
58
-
59
- test("should store encrypted value, not plain text", () => {
60
- const key = "test_encrypted";
61
- const value = "secret-api-key";
62
-
63
- setEncryptedSessionStorage(key, value);
64
- const storedValue = mockSessionStorage.store.get(key);
65
-
66
- expect(storedValue).not.toBe(value); // Should not store plain text
67
- expect(storedValue).toBeTruthy(); // Should store something
68
- expect(storedValue!.length).toBeGreaterThan(0); // Should have content
69
- });
70
-
71
- test("should handle empty values correctly", () => {
72
- const key = "empty_test";
73
- const value = "";
74
-
75
- setEncryptedSessionStorage(key, value);
76
- const retrieved = getEncryptedSessionStorage(key);
77
-
78
- expect(retrieved).toBe("");
79
- });
80
-
81
- test("should return empty string for non-existent keys", () => {
82
- const result = getEncryptedSessionStorage("non_existent_key");
83
- expect(result).toBe("");
84
- });
85
-
86
- test("should remove encrypted data", () => {
87
- const key = "test_remove";
88
- const value = "test-value";
89
-
90
- setEncryptedSessionStorage(key, value);
91
- expect(getEncryptedSessionStorage(key)).toBe(value);
92
-
93
- removeEncryptedSessionStorage(key);
94
- expect(getEncryptedSessionStorage(key)).toBe("");
95
- expect(mockSessionStorage.removeItem).toHaveBeenCalledWith(key);
96
- });
97
-
98
- test("should check if encrypted data exists", () => {
99
- const key = "existence_test";
100
- const value = "test-value";
101
-
102
- // Initially should not exist
103
- expect(hasEncryptedSessionStorage(key)).toBe(false);
104
-
105
- // After storing should exist
106
- setEncryptedSessionStorage(key, value);
107
- expect(hasEncryptedSessionStorage(key)).toBe(true);
108
-
109
- // After removing should not exist
110
- removeEncryptedSessionStorage(key);
111
- expect(hasEncryptedSessionStorage(key)).toBe(false);
112
- });
113
-
114
- test("should handle storage errors gracefully", () => {
115
- const originalSetItem = mockSessionStorage.setItem;
116
- mockSessionStorage.setItem = vi.fn(() => {
117
- throw new Error("Storage quota exceeded");
118
- });
119
-
120
- // Should throw error with meaningful message
121
- expect(() => {
122
- setEncryptedSessionStorage("test", "value");
123
- }).toThrow(
124
- 'Failed to store encrypted data for key "test": Storage quota exceeded',
125
- );
126
-
127
- mockSessionStorage.setItem = originalSetItem; // Restore
128
- });
129
-
130
- test("should clear all sessionStorage data", () => {
131
- // Mock setItem to not throw for this test
132
- const originalSetItem = mockSessionStorage.setItem;
133
- mockSessionStorage.setItem = vi.fn((key: string, value: string) => {
134
- mockSessionStorage.store.set(key, value);
135
- });
136
-
137
- // Store some test data
138
- setEncryptedSessionStorage("key1", "value1");
139
- setEncryptedSessionStorage("key2", "value2");
140
-
141
- expect(hasEncryptedSessionStorage("key1")).toBe(true);
142
- expect(hasEncryptedSessionStorage("key2")).toBe(true);
143
-
144
- // Clear all
145
- clearEncryptedSessionStorage();
146
-
147
- expect(hasEncryptedSessionStorage("key1")).toBe(false);
148
- expect(hasEncryptedSessionStorage("key2")).toBe(false);
149
- expect(mockSessionStorage.clear).toHaveBeenCalled();
150
-
151
- mockSessionStorage.setItem = originalSetItem; // Restore
152
- });
153
- });
154
-
155
- describe("Unicode and special data", () => {
156
- const testCases = [
157
- { key: "korean", value: "안녕하세요" },
158
- { key: "emoji", value: "🚀🔐💎✨" },
159
- { key: "mixed", value: "Hello 안녕 🚀 World!" },
160
- { key: "api_key", value: "sk-proj-1234567890abcdefghijklmnop" },
161
- { key: "json", value: '{"name":"test","value":123}' },
162
- { key: "multiline", value: "line1\nline2\nline3" },
163
- ];
164
-
165
- test.each(testCases)(
166
- "should handle $key: $value correctly",
167
- ({ key, value }) => {
168
- // Mock setItem to not throw for unicode tests
169
- const originalSetItem = mockSessionStorage.setItem;
170
- mockSessionStorage.setItem = vi.fn((key: string, value: string) => {
171
- mockSessionStorage.store.set(key, value);
172
- });
173
-
174
- setEncryptedSessionStorage(key, value);
175
- const retrieved = getEncryptedSessionStorage(key);
176
-
177
- expect(retrieved).toBe(value);
178
- expect(hasEncryptedSessionStorage(key)).toBe(true);
179
-
180
- // Verify it's actually encrypted in storage
181
- const rawStored = mockSessionStorage.store.get(key);
182
- expect(rawStored).not.toBe(value);
183
- expect(rawStored).toBeTruthy();
184
-
185
- mockSessionStorage.setItem = originalSetItem; // Restore
186
- },
187
- );
188
- });
189
-
190
- describe("Edge cases", () => {
191
- test("should handle null/undefined gracefully", () => {
192
- // These should not throw
193
- expect(() => getEncryptedSessionStorage("")).not.toThrow();
194
- expect(() => hasEncryptedSessionStorage("")).not.toThrow();
195
- expect(() => removeEncryptedSessionStorage("")).not.toThrow();
196
- });
197
-
198
- test("should handle SSR environment (no window)", () => {
199
- const originalWindow = global.window;
200
- // @ts-ignore
201
- delete global.window;
202
-
203
- // Should not throw and return safe defaults
204
- expect(() => {
205
- setEncryptedSessionStorage("test", "value");
206
- const result = getEncryptedSessionStorage("test");
207
- expect(result).toBe("");
208
- const exists = hasEncryptedSessionStorage("test");
209
- expect(exists).toBe(false);
210
- removeEncryptedSessionStorage("test");
211
- clearEncryptedSessionStorage();
212
- }).not.toThrow();
213
-
214
- global.window = originalWindow; // Restore
215
- });
216
-
217
- test("should handle corrupted storage data", () => {
218
- const key = "corrupted_test";
219
-
220
- // Manually put invalid encrypted data
221
- mockSessionStorage.store.set(key, "invalid-base64-data!");
222
-
223
- // Should throw error on corrupted data
224
- expect(() => {
225
- getEncryptedSessionStorage(key);
226
- }).toThrow('Failed to retrieve encrypted data for key "corrupted_test"');
227
- });
228
- });
229
- });
1
+ import { beforeEach, describe, expect, test, vi } from "vitest";
2
+
3
+ import {
4
+ clearEncryptedSessionStorage,
5
+ getEncryptedSessionStorage,
6
+ hasEncryptedSessionStorage,
7
+ removeEncryptedSessionStorage,
8
+ setEncryptedSessionStorage,
9
+ } from "../storage";
10
+
11
+ // Mock sessionStorage for testing
12
+ const mockSessionStorage = {
13
+ store: new Map<string, string>(),
14
+ getItem: vi.fn((key: string) => mockSessionStorage.store.get(key) || null),
15
+ setItem: vi.fn((key: string, value: string) => {
16
+ mockSessionStorage.store.set(key, value);
17
+ }),
18
+ removeItem: vi.fn((key: string) => {
19
+ mockSessionStorage.store.delete(key);
20
+ }),
21
+ clear: vi.fn(() => {
22
+ mockSessionStorage.store.clear();
23
+ }),
24
+ };
25
+
26
+ // Mock global sessionStorage
27
+ Object.defineProperty(global, "sessionStorage", {
28
+ value: mockSessionStorage,
29
+ writable: true,
30
+ });
31
+
32
+ Object.defineProperty(global, "window", {
33
+ value: { sessionStorage: mockSessionStorage },
34
+ writable: true,
35
+ });
36
+
37
+ describe("Storage Utils", () => {
38
+ beforeEach(() => {
39
+ mockSessionStorage.store.clear();
40
+ vi.clearAllMocks();
41
+ });
42
+
43
+ describe("encrypted sessionStorage operations", () => {
44
+ test("should store and retrieve encrypted data", () => {
45
+ const key = "test_key";
46
+ const value = "sk-test123456789";
47
+
48
+ setEncryptedSessionStorage(key, value);
49
+ const retrieved = getEncryptedSessionStorage(key);
50
+
51
+ expect(retrieved).toBe(value);
52
+ expect(mockSessionStorage.setItem).toHaveBeenCalledWith(
53
+ key,
54
+ expect.any(String),
55
+ );
56
+ expect(mockSessionStorage.getItem).toHaveBeenCalledWith(key);
57
+ });
58
+
59
+ test("should store encrypted value, not plain text", () => {
60
+ const key = "test_encrypted";
61
+ const value = "secret-api-key";
62
+
63
+ setEncryptedSessionStorage(key, value);
64
+ const storedValue = mockSessionStorage.store.get(key);
65
+
66
+ expect(storedValue).not.toBe(value); // Should not store plain text
67
+ expect(storedValue).toBeTruthy(); // Should store something
68
+ expect(storedValue!.length).toBeGreaterThan(0); // Should have content
69
+ });
70
+
71
+ test("should handle empty values correctly", () => {
72
+ const key = "empty_test";
73
+ const value = "";
74
+
75
+ setEncryptedSessionStorage(key, value);
76
+ const retrieved = getEncryptedSessionStorage(key);
77
+
78
+ expect(retrieved).toBe("");
79
+ });
80
+
81
+ test("should return empty string for non-existent keys", () => {
82
+ const result = getEncryptedSessionStorage("non_existent_key");
83
+ expect(result).toBe("");
84
+ });
85
+
86
+ test("should remove encrypted data", () => {
87
+ const key = "test_remove";
88
+ const value = "test-value";
89
+
90
+ setEncryptedSessionStorage(key, value);
91
+ expect(getEncryptedSessionStorage(key)).toBe(value);
92
+
93
+ removeEncryptedSessionStorage(key);
94
+ expect(getEncryptedSessionStorage(key)).toBe("");
95
+ expect(mockSessionStorage.removeItem).toHaveBeenCalledWith(key);
96
+ });
97
+
98
+ test("should check if encrypted data exists", () => {
99
+ const key = "existence_test";
100
+ const value = "test-value";
101
+
102
+ // Initially should not exist
103
+ expect(hasEncryptedSessionStorage(key)).toBe(false);
104
+
105
+ // After storing should exist
106
+ setEncryptedSessionStorage(key, value);
107
+ expect(hasEncryptedSessionStorage(key)).toBe(true);
108
+
109
+ // After removing should not exist
110
+ removeEncryptedSessionStorage(key);
111
+ expect(hasEncryptedSessionStorage(key)).toBe(false);
112
+ });
113
+
114
+ test("should handle storage errors gracefully", () => {
115
+ const originalSetItem = mockSessionStorage.setItem;
116
+ mockSessionStorage.setItem = vi.fn(() => {
117
+ throw new Error("Storage quota exceeded");
118
+ });
119
+
120
+ // Should throw error with meaningful message
121
+ expect(() => {
122
+ setEncryptedSessionStorage("test", "value");
123
+ }).toThrow(
124
+ 'Failed to store encrypted data for key "test": Storage quota exceeded',
125
+ );
126
+
127
+ mockSessionStorage.setItem = originalSetItem; // Restore
128
+ });
129
+
130
+ test("should clear all sessionStorage data", () => {
131
+ // Mock setItem to not throw for this test
132
+ const originalSetItem = mockSessionStorage.setItem;
133
+ mockSessionStorage.setItem = vi.fn((key: string, value: string) => {
134
+ mockSessionStorage.store.set(key, value);
135
+ });
136
+
137
+ // Store some test data
138
+ setEncryptedSessionStorage("key1", "value1");
139
+ setEncryptedSessionStorage("key2", "value2");
140
+
141
+ expect(hasEncryptedSessionStorage("key1")).toBe(true);
142
+ expect(hasEncryptedSessionStorage("key2")).toBe(true);
143
+
144
+ // Clear all
145
+ clearEncryptedSessionStorage();
146
+
147
+ expect(hasEncryptedSessionStorage("key1")).toBe(false);
148
+ expect(hasEncryptedSessionStorage("key2")).toBe(false);
149
+ expect(mockSessionStorage.clear).toHaveBeenCalled();
150
+
151
+ mockSessionStorage.setItem = originalSetItem; // Restore
152
+ });
153
+ });
154
+
155
+ describe("Unicode and special data", () => {
156
+ const testCases = [
157
+ { key: "korean", value: "안녕하세요" },
158
+ { key: "emoji", value: "🚀🔐💎✨" },
159
+ { key: "mixed", value: "Hello 안녕 🚀 World!" },
160
+ { key: "api_key", value: "sk-proj-1234567890abcdefghijklmnop" },
161
+ { key: "json", value: '{"name":"test","value":123}' },
162
+ { key: "multiline", value: "line1\nline2\nline3" },
163
+ ];
164
+
165
+ test.each(testCases)(
166
+ "should handle $key: $value correctly",
167
+ ({ key, value }) => {
168
+ // Mock setItem to not throw for unicode tests
169
+ const originalSetItem = mockSessionStorage.setItem;
170
+ mockSessionStorage.setItem = vi.fn((key: string, value: string) => {
171
+ mockSessionStorage.store.set(key, value);
172
+ });
173
+
174
+ setEncryptedSessionStorage(key, value);
175
+ const retrieved = getEncryptedSessionStorage(key);
176
+
177
+ expect(retrieved).toBe(value);
178
+ expect(hasEncryptedSessionStorage(key)).toBe(true);
179
+
180
+ // Verify it's actually encrypted in storage
181
+ const rawStored = mockSessionStorage.store.get(key);
182
+ expect(rawStored).not.toBe(value);
183
+ expect(rawStored).toBeTruthy();
184
+
185
+ mockSessionStorage.setItem = originalSetItem; // Restore
186
+ },
187
+ );
188
+ });
189
+
190
+ describe("Edge cases", () => {
191
+ test("should handle null/undefined gracefully", () => {
192
+ // These should not throw
193
+ expect(() => getEncryptedSessionStorage("")).not.toThrow();
194
+ expect(() => hasEncryptedSessionStorage("")).not.toThrow();
195
+ expect(() => removeEncryptedSessionStorage("")).not.toThrow();
196
+ });
197
+
198
+ test("should handle SSR environment (no window)", () => {
199
+ const originalWindow = global.window;
200
+ // @ts-ignore
201
+ delete global.window;
202
+
203
+ // Should not throw and return safe defaults
204
+ expect(() => {
205
+ setEncryptedSessionStorage("test", "value");
206
+ const result = getEncryptedSessionStorage("test");
207
+ expect(result).toBe("");
208
+ const exists = hasEncryptedSessionStorage("test");
209
+ expect(exists).toBe(false);
210
+ removeEncryptedSessionStorage("test");
211
+ clearEncryptedSessionStorage();
212
+ }).not.toThrow();
213
+
214
+ global.window = originalWindow; // Restore
215
+ });
216
+
217
+ test("should handle corrupted storage data", () => {
218
+ const key = "corrupted_test";
219
+
220
+ // Manually put invalid encrypted data
221
+ mockSessionStorage.store.set(key, "invalid-base64-data!");
222
+
223
+ // Should throw error on corrupted data
224
+ expect(() => {
225
+ getEncryptedSessionStorage(key);
226
+ }).toThrow('Failed to retrieve encrypted data for key "corrupted_test"');
227
+ });
228
+ });
229
+ });
@@ -1,95 +1,95 @@
1
- /**
2
- * Simple and reliable encryption utilities using browser built-in functions
3
- * Uses TextEncoder/TextDecoder for proper Unicode support + XOR + Base64
4
- */
5
-
6
- const ENCRYPTION_KEY = "AutoBE_Secret_2024_v3.0_Unicode"; // Unicode-safe key
7
-
8
- /**
9
- * Simple encrypt using TextEncoder for Unicode safety
10
- *
11
- * @param text - Text to encrypt
12
- * @returns Encrypted base64 string
13
- */
14
- export const encrypt = (text: string): string => {
15
- if (!text) return "";
16
-
17
- try {
18
- // Use TextEncoder for proper Unicode → UTF-8 bytes conversion
19
- const encoder = new TextEncoder();
20
- const textBytes = encoder.encode(text);
21
-
22
- // Generate simple salt
23
- const salt = Math.random().toString(36).substring(2, 10);
24
- const keyBytes = encoder.encode(ENCRYPTION_KEY + salt);
25
-
26
- // XOR encryption on bytes level
27
- const encryptedBytes = new Uint8Array(textBytes.length);
28
- for (let i = 0; i < textBytes.length; i++) {
29
- encryptedBytes[i] = textBytes[i] ^ keyBytes[i % keyBytes.length];
30
- }
31
-
32
- // Convert to hex string for safe concatenation
33
- const encryptedHex = Array.from(encryptedBytes)
34
- .map((b) => b.toString(16).padStart(2, "0"))
35
- .join("");
36
-
37
- // Combine salt + encrypted hex
38
- const combined = salt + ":" + encryptedHex;
39
-
40
- // Base64 encode the final result (handle Unicode properly)
41
- const result = btoa(unescape(encodeURIComponent(combined)));
42
- return result;
43
- } catch (error) {
44
- throw new Error(
45
- `Encryption failed: ${error instanceof Error ? error.message : String(error)}`,
46
- );
47
- }
48
- };
49
-
50
- /**
51
- * Simple decrypt using TextDecoder for Unicode safety
52
- *
53
- * @param encryptedText - Base64 encrypted string
54
- * @returns Decrypted plain text
55
- */
56
- export const decrypt = (encryptedText: string): string => {
57
- if (!encryptedText) return "";
58
-
59
- try {
60
- // Base64 decode (handle Unicode properly)
61
- const combined = decodeURIComponent(escape(atob(encryptedText)));
62
- const parts = combined.split(":");
63
-
64
- if (parts.length !== 2) {
65
- throw new Error("Invalid encrypted format: expected salt:data format");
66
- }
67
-
68
- const salt = parts[0];
69
- const encryptedHex = parts[1];
70
-
71
- // Convert hex back to bytes
72
- const encryptedBytes = new Uint8Array(
73
- encryptedHex.match(/.{2}/g)?.map((hex) => parseInt(hex, 16)) || [],
74
- );
75
-
76
- // Recreate key
77
- const encoder = new TextEncoder();
78
- const keyBytes = encoder.encode(ENCRYPTION_KEY + salt);
79
-
80
- // XOR decryption
81
- const decryptedBytes = new Uint8Array(encryptedBytes.length);
82
- for (let i = 0; i < encryptedBytes.length; i++) {
83
- decryptedBytes[i] = encryptedBytes[i] ^ keyBytes[i % keyBytes.length];
84
- }
85
-
86
- // Use TextDecoder for proper UTF-8 → Unicode conversion
87
- const decoder = new TextDecoder();
88
- const result = decoder.decode(decryptedBytes);
89
- return result;
90
- } catch (error) {
91
- throw new Error(
92
- `Decryption failed: ${error instanceof Error ? error.message : String(error)}`,
93
- );
94
- }
95
- };
1
+ /**
2
+ * Simple and reliable encryption utilities using browser built-in functions
3
+ * Uses TextEncoder/TextDecoder for proper Unicode support + XOR + Base64
4
+ */
5
+
6
+ const ENCRYPTION_KEY = "AutoBE_Secret_2024_v3.0_Unicode"; // Unicode-safe key
7
+
8
+ /**
9
+ * Simple encrypt using TextEncoder for Unicode safety
10
+ *
11
+ * @param text - Text to encrypt
12
+ * @returns Encrypted base64 string
13
+ */
14
+ export const encrypt = (text: string): string => {
15
+ if (!text) return "";
16
+
17
+ try {
18
+ // Use TextEncoder for proper Unicode → UTF-8 bytes conversion
19
+ const encoder = new TextEncoder();
20
+ const textBytes = encoder.encode(text);
21
+
22
+ // Generate simple salt
23
+ const salt = Math.random().toString(36).substring(2, 10);
24
+ const keyBytes = encoder.encode(ENCRYPTION_KEY + salt);
25
+
26
+ // XOR encryption on bytes level
27
+ const encryptedBytes = new Uint8Array(textBytes.length);
28
+ for (let i = 0; i < textBytes.length; i++) {
29
+ encryptedBytes[i] = textBytes[i] ^ keyBytes[i % keyBytes.length];
30
+ }
31
+
32
+ // Convert to hex string for safe concatenation
33
+ const encryptedHex = Array.from(encryptedBytes)
34
+ .map((b) => b.toString(16).padStart(2, "0"))
35
+ .join("");
36
+
37
+ // Combine salt + encrypted hex
38
+ const combined = salt + ":" + encryptedHex;
39
+
40
+ // Base64 encode the final result (handle Unicode properly)
41
+ const result = btoa(unescape(encodeURIComponent(combined)));
42
+ return result;
43
+ } catch (error) {
44
+ throw new Error(
45
+ `Encryption failed: ${error instanceof Error ? error.message : String(error)}`,
46
+ );
47
+ }
48
+ };
49
+
50
+ /**
51
+ * Simple decrypt using TextDecoder for Unicode safety
52
+ *
53
+ * @param encryptedText - Base64 encrypted string
54
+ * @returns Decrypted plain text
55
+ */
56
+ export const decrypt = (encryptedText: string): string => {
57
+ if (!encryptedText) return "";
58
+
59
+ try {
60
+ // Base64 decode (handle Unicode properly)
61
+ const combined = decodeURIComponent(escape(atob(encryptedText)));
62
+ const parts = combined.split(":");
63
+
64
+ if (parts.length !== 2) {
65
+ throw new Error("Invalid encrypted format: expected salt:data format");
66
+ }
67
+
68
+ const salt = parts[0];
69
+ const encryptedHex = parts[1];
70
+
71
+ // Convert hex back to bytes
72
+ const encryptedBytes = new Uint8Array(
73
+ encryptedHex.match(/.{2}/g)?.map((hex) => parseInt(hex, 16)) || [],
74
+ );
75
+
76
+ // Recreate key
77
+ const encoder = new TextEncoder();
78
+ const keyBytes = encoder.encode(ENCRYPTION_KEY + salt);
79
+
80
+ // XOR decryption
81
+ const decryptedBytes = new Uint8Array(encryptedBytes.length);
82
+ for (let i = 0; i < encryptedBytes.length; i++) {
83
+ decryptedBytes[i] = encryptedBytes[i] ^ keyBytes[i % keyBytes.length];
84
+ }
85
+
86
+ // Use TextDecoder for proper UTF-8 → Unicode conversion
87
+ const decoder = new TextDecoder();
88
+ const result = decoder.decode(decryptedBytes);
89
+ return result;
90
+ } catch (error) {
91
+ throw new Error(
92
+ `Decryption failed: ${error instanceof Error ? error.message : String(error)}`,
93
+ );
94
+ }
95
+ };
@@ -1,6 +1,6 @@
1
- export * from "./AutoBeFileUploader";
2
- export * from "./AutoBeVoiceRecorder";
3
- export * from "./time";
4
- export * from "./crypto";
5
- export * from "./storage";
6
- export * from "./number";
1
+ export * from "./AutoBeFileUploader";
2
+ export * from "./AutoBeVoiceRecorder";
3
+ export * from "./time";
4
+ export * from "./crypto";
5
+ export * from "./storage";
6
+ export * from "./number";