@strivacity/sdk-core 3.0.2 → 4.0.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (328) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/README.md +1873 -340
  3. package/dist/assets/oidc.cjs +2 -0
  4. package/dist/assets/oidc.cjs.map +1 -0
  5. package/dist/assets/oidc.mjs +2 -0
  6. package/dist/assets/oidc.mjs.map +1 -0
  7. package/dist/assets/server.cjs +2 -0
  8. package/dist/assets/server.cjs.map +1 -0
  9. package/dist/assets/server.mjs +2 -0
  10. package/dist/assets/server.mjs.map +1 -0
  11. package/dist/flows/base.cjs +2 -0
  12. package/dist/flows/base.cjs.map +1 -0
  13. package/dist/flows/base.d.ts +26 -0
  14. package/dist/flows/base.mjs +2 -0
  15. package/dist/flows/base.mjs.map +1 -0
  16. package/dist/flows/embedded.cjs +2 -0
  17. package/dist/flows/embedded.cjs.map +1 -0
  18. package/dist/flows/embedded.d.ts +9 -0
  19. package/dist/flows/embedded.mjs +2 -0
  20. package/dist/flows/embedded.mjs.map +1 -0
  21. package/dist/flows/index.cjs +1 -0
  22. package/dist/flows/index.d.ts +5 -0
  23. package/dist/flows/index.mjs +1 -0
  24. package/dist/flows/native.cjs +2 -0
  25. package/dist/flows/native.cjs.map +1 -0
  26. package/dist/flows/native.d.ts +9 -0
  27. package/dist/flows/native.mjs +2 -0
  28. package/dist/flows/native.mjs.map +1 -0
  29. package/dist/flows/popup.cjs +2 -0
  30. package/dist/flows/popup.cjs.map +1 -0
  31. package/dist/flows/popup.d.ts +9 -0
  32. package/dist/flows/popup.mjs +2 -0
  33. package/dist/flows/popup.mjs.map +1 -0
  34. package/dist/flows/redirect.cjs +2 -0
  35. package/dist/flows/redirect.cjs.map +1 -0
  36. package/dist/flows/redirect.d.ts +9 -0
  37. package/dist/flows/redirect.mjs +2 -0
  38. package/dist/flows/redirect.mjs.map +1 -0
  39. package/dist/handlers/embedded.cjs +2 -0
  40. package/dist/handlers/embedded.cjs.map +1 -0
  41. package/dist/handlers/embedded.d.ts +37 -0
  42. package/dist/handlers/embedded.mjs +2 -0
  43. package/dist/handlers/embedded.mjs.map +1 -0
  44. package/dist/handlers/index.cjs +1 -0
  45. package/dist/handlers/index.d.ts +2 -0
  46. package/dist/handlers/index.mjs +1 -0
  47. package/dist/handlers/native.cjs +2 -0
  48. package/dist/handlers/native.cjs.map +1 -0
  49. package/dist/handlers/native.d.ts +56 -0
  50. package/dist/handlers/native.mjs +2 -0
  51. package/dist/handlers/native.mjs.map +1 -0
  52. package/dist/handlers/popup.cjs +2 -0
  53. package/dist/handlers/popup.cjs.map +1 -0
  54. package/dist/handlers/popup.d.ts +21 -0
  55. package/dist/handlers/popup.mjs +2 -0
  56. package/dist/handlers/popup.mjs.map +1 -0
  57. package/dist/handlers/redirect.cjs +2 -0
  58. package/dist/handlers/redirect.cjs.map +1 -0
  59. package/dist/handlers/redirect.d.ts +18 -0
  60. package/dist/handlers/redirect.mjs +2 -0
  61. package/dist/handlers/redirect.mjs.map +1 -0
  62. package/dist/index.cjs +2 -2
  63. package/dist/index.cjs.map +1 -1
  64. package/dist/index.d.ts +16 -21
  65. package/dist/index.mjs +2 -2
  66. package/dist/index.mjs.map +1 -1
  67. package/dist/server/base.cjs +2 -0
  68. package/dist/server/base.cjs.map +1 -0
  69. package/dist/server/base.d.ts +15 -0
  70. package/dist/server/base.mjs +2 -0
  71. package/dist/server/base.mjs.map +1 -0
  72. package/dist/server/index.cjs +1 -0
  73. package/dist/server/index.d.ts +1 -0
  74. package/dist/server/index.mjs +1 -0
  75. package/dist/storages/cache.cjs +2 -0
  76. package/dist/storages/cache.cjs.map +1 -0
  77. package/dist/storages/cache.d.ts +11 -0
  78. package/dist/storages/cache.mjs +2 -0
  79. package/dist/storages/cache.mjs.map +1 -0
  80. package/dist/storages/index.cjs +1 -0
  81. package/dist/storages/index.d.ts +7 -0
  82. package/dist/storages/index.mjs +1 -0
  83. package/dist/storages/indexeddb.cjs +2 -0
  84. package/dist/storages/indexeddb.cjs.map +1 -0
  85. package/dist/storages/indexeddb.d.ts +10 -0
  86. package/dist/storages/indexeddb.mjs +2 -0
  87. package/dist/storages/indexeddb.mjs.map +1 -0
  88. package/dist/storages/local.cjs +2 -0
  89. package/dist/storages/local.cjs.map +1 -0
  90. package/dist/storages/local.d.ts +7 -0
  91. package/dist/storages/local.mjs +2 -0
  92. package/dist/storages/local.mjs.map +1 -0
  93. package/dist/storages/memory.cjs +2 -0
  94. package/dist/storages/memory.cjs.map +1 -0
  95. package/dist/storages/memory.d.ts +8 -0
  96. package/dist/storages/memory.mjs +2 -0
  97. package/dist/storages/memory.mjs.map +1 -0
  98. package/dist/storages/server.cjs +1 -0
  99. package/dist/storages/server.d.ts +39 -0
  100. package/dist/storages/server.mjs +1 -0
  101. package/dist/storages/session.cjs +2 -0
  102. package/dist/storages/session.cjs.map +1 -0
  103. package/dist/storages/session.d.ts +7 -0
  104. package/dist/storages/session.mjs +2 -0
  105. package/dist/storages/session.mjs.map +1 -0
  106. package/dist/storages/worker.cjs +2 -0
  107. package/dist/storages/worker.cjs.map +1 -0
  108. package/dist/storages/worker.d.ts +46 -0
  109. package/dist/storages/worker.mjs +2 -0
  110. package/dist/storages/worker.mjs.map +1 -0
  111. package/dist/types/common.cjs +0 -0
  112. package/dist/types/common.d.ts +145 -0
  113. package/dist/types/common.mjs +0 -0
  114. package/dist/types/embedded.cjs +0 -0
  115. package/dist/types/embedded.d.ts +103 -0
  116. package/dist/types/embedded.mjs +0 -0
  117. package/dist/types/index.cjs +1 -0
  118. package/dist/types/index.d.ts +7 -0
  119. package/dist/types/index.mjs +1 -0
  120. package/dist/types/native.cjs +0 -0
  121. package/dist/types/native.d.ts +385 -0
  122. package/dist/types/native.mjs +0 -0
  123. package/dist/types/oidc.cjs +2 -0
  124. package/dist/types/oidc.cjs.map +1 -0
  125. package/dist/types/oidc.d.ts +768 -0
  126. package/dist/types/oidc.mjs +2 -0
  127. package/dist/types/oidc.mjs.map +1 -0
  128. package/dist/types/popup.cjs +0 -0
  129. package/dist/types/popup.d.ts +69 -0
  130. package/dist/types/popup.mjs +0 -0
  131. package/dist/types/redirect.cjs +0 -0
  132. package/dist/types/redirect.d.ts +21 -0
  133. package/dist/types/redirect.mjs +0 -0
  134. package/dist/types/server.cjs +0 -0
  135. package/dist/types/server.d.ts +200 -0
  136. package/dist/types/server.mjs +0 -0
  137. package/dist/utils/base64url.cjs +2 -0
  138. package/dist/utils/base64url.cjs.map +1 -0
  139. package/dist/utils/base64url.d.ts +14 -0
  140. package/dist/utils/base64url.mjs +2 -0
  141. package/dist/utils/base64url.mjs.map +1 -0
  142. package/dist/utils/common.cjs +2 -0
  143. package/dist/utils/common.cjs.map +1 -0
  144. package/dist/utils/common.d.ts +23 -0
  145. package/dist/utils/common.mjs +2 -0
  146. package/dist/utils/common.mjs.map +1 -0
  147. package/dist/utils/credentials.cjs +2 -2
  148. package/dist/utils/credentials.cjs.map +1 -1
  149. package/dist/utils/credentials.d.ts +19 -3
  150. package/dist/utils/credentials.mjs +2 -2
  151. package/dist/utils/credentials.mjs.map +1 -1
  152. package/dist/utils/crypto.cjs +2 -2
  153. package/dist/utils/crypto.cjs.map +1 -1
  154. package/dist/utils/crypto.d.ts +16 -26
  155. package/dist/utils/crypto.mjs +2 -2
  156. package/dist/utils/crypto.mjs.map +1 -1
  157. package/dist/utils/dom.cjs +2 -0
  158. package/dist/utils/dom.cjs.map +1 -0
  159. package/dist/utils/dom.d.ts +7 -0
  160. package/dist/utils/dom.mjs +2 -0
  161. package/dist/utils/dom.mjs.map +1 -0
  162. package/dist/utils/errors.cjs +2 -2
  163. package/dist/utils/errors.cjs.map +1 -1
  164. package/dist/utils/errors.d.ts +62 -1
  165. package/dist/utils/errors.mjs +2 -2
  166. package/dist/utils/errors.mjs.map +1 -1
  167. package/dist/utils/httpClient.cjs +2 -0
  168. package/dist/utils/httpClient.cjs.map +1 -0
  169. package/dist/utils/httpClient.d.ts +2 -0
  170. package/dist/utils/httpClient.mjs +2 -0
  171. package/dist/utils/httpClient.mjs.map +1 -0
  172. package/dist/utils/index.cjs +1 -0
  173. package/dist/utils/index.d.ts +12 -0
  174. package/dist/utils/index.mjs +1 -0
  175. package/dist/utils/logging.cjs +2 -0
  176. package/dist/utils/logging.cjs.map +1 -0
  177. package/dist/utils/logging.d.ts +2 -0
  178. package/dist/utils/logging.mjs +2 -0
  179. package/dist/utils/logging.mjs.map +1 -0
  180. package/dist/utils/oidc.cjs +1 -0
  181. package/dist/utils/oidc.d.ts +264 -0
  182. package/dist/utils/oidc.mjs +1 -0
  183. package/dist/utils/server.cjs +1 -0
  184. package/dist/utils/server.d.ts +72 -0
  185. package/dist/utils/server.mjs +1 -0
  186. package/dist/utils/session.cjs +2 -0
  187. package/dist/utils/session.cjs.map +1 -0
  188. package/dist/utils/session.d.ts +29 -0
  189. package/dist/utils/session.mjs +2 -0
  190. package/dist/utils/session.mjs.map +1 -0
  191. package/dist/utils/state.cjs +1 -0
  192. package/dist/utils/state.d.ts +23 -0
  193. package/dist/utils/state.mjs +1 -0
  194. package/package.json +53 -1
  195. package/testing/tests/flows/base.spec.ts +1030 -0
  196. package/testing/tests/flows/embedded.spec.ts +443 -0
  197. package/testing/tests/flows/index.spec.ts +8 -0
  198. package/testing/tests/flows/native.spec.ts +487 -0
  199. package/testing/tests/flows/popup.spec.ts +400 -0
  200. package/testing/tests/flows/redirect.spec.ts +369 -0
  201. package/testing/tests/handlers/embedded.spec.ts +199 -0
  202. package/testing/tests/handlers/index.spec.ts +8 -0
  203. package/testing/tests/handlers/native.spec.ts +283 -0
  204. package/testing/tests/handlers/popup.spec.ts +206 -0
  205. package/testing/tests/handlers/redirect.spec.ts +70 -0
  206. package/testing/tests/index.spec.ts +78 -0
  207. package/testing/tests/server/base.spec.ts +846 -0
  208. package/testing/tests/server/index.spec.ts +15 -0
  209. package/testing/tests/storages/cache.spec.ts +107 -0
  210. package/testing/tests/storages/index.spec.ts +22 -0
  211. package/testing/tests/storages/indexeddb.spec.ts +113 -0
  212. package/testing/tests/storages/local.spec.ts +46 -0
  213. package/testing/tests/storages/memory.spec.ts +51 -0
  214. package/testing/tests/storages/server.spec.ts +435 -0
  215. package/testing/tests/storages/session.spec.ts +46 -0
  216. package/testing/tests/storages/worker.spec.ts +106 -0
  217. package/testing/tests/utils/common.spec.ts +77 -0
  218. package/testing/tests/utils/credentials.spec.ts +195 -0
  219. package/testing/tests/utils/dom.spec.ts +37 -0
  220. package/testing/tests/utils/index.spec.ts +71 -0
  221. package/testing/tests/utils/logging.spec.ts +82 -0
  222. package/testing/tests/utils/oidc.spec.ts +744 -0
  223. package/testing/tests/utils/server.spec.ts +235 -0
  224. package/dist/flows/BaseFlow.cjs +0 -2
  225. package/dist/flows/BaseFlow.cjs.map +0 -1
  226. package/dist/flows/BaseFlow.d.ts +0 -191
  227. package/dist/flows/BaseFlow.mjs +0 -2
  228. package/dist/flows/BaseFlow.mjs.map +0 -1
  229. package/dist/flows/EmbeddedFlow.cjs +0 -2
  230. package/dist/flows/EmbeddedFlow.cjs.map +0 -1
  231. package/dist/flows/EmbeddedFlow.d.ts +0 -26
  232. package/dist/flows/EmbeddedFlow.mjs +0 -2
  233. package/dist/flows/EmbeddedFlow.mjs.map +0 -1
  234. package/dist/flows/NativeFlow.cjs +0 -2
  235. package/dist/flows/NativeFlow.cjs.map +0 -1
  236. package/dist/flows/NativeFlow.d.ts +0 -34
  237. package/dist/flows/NativeFlow.mjs +0 -2
  238. package/dist/flows/NativeFlow.mjs.map +0 -1
  239. package/dist/flows/PopupFlow.cjs +0 -2
  240. package/dist/flows/PopupFlow.cjs.map +0 -1
  241. package/dist/flows/PopupFlow.d.ts +0 -36
  242. package/dist/flows/PopupFlow.mjs +0 -2
  243. package/dist/flows/PopupFlow.mjs.map +0 -1
  244. package/dist/flows/RedirectFlow.cjs +0 -2
  245. package/dist/flows/RedirectFlow.cjs.map +0 -1
  246. package/dist/flows/RedirectFlow.d.ts +0 -38
  247. package/dist/flows/RedirectFlow.mjs +0 -2
  248. package/dist/flows/RedirectFlow.mjs.map +0 -1
  249. package/dist/handlers/BaseFlowHandler.cjs +0 -2
  250. package/dist/handlers/BaseFlowHandler.cjs.map +0 -1
  251. package/dist/handlers/BaseFlowHandler.d.ts +0 -61
  252. package/dist/handlers/BaseFlowHandler.mjs +0 -2
  253. package/dist/handlers/BaseFlowHandler.mjs.map +0 -1
  254. package/dist/handlers/EmbeddedFlowHandler.cjs +0 -2
  255. package/dist/handlers/EmbeddedFlowHandler.cjs.map +0 -1
  256. package/dist/handlers/EmbeddedFlowHandler.d.ts +0 -28
  257. package/dist/handlers/EmbeddedFlowHandler.mjs +0 -2
  258. package/dist/handlers/EmbeddedFlowHandler.mjs.map +0 -1
  259. package/dist/handlers/NativeFlowHandler.cjs +0 -2
  260. package/dist/handlers/NativeFlowHandler.cjs.map +0 -1
  261. package/dist/handlers/NativeFlowHandler.d.ts +0 -31
  262. package/dist/handlers/NativeFlowHandler.mjs +0 -2
  263. package/dist/handlers/NativeFlowHandler.mjs.map +0 -1
  264. package/dist/storages/LocalStorage.cjs +0 -2
  265. package/dist/storages/LocalStorage.cjs.map +0 -1
  266. package/dist/storages/LocalStorage.d.ts +0 -23
  267. package/dist/storages/LocalStorage.mjs +0 -2
  268. package/dist/storages/LocalStorage.mjs.map +0 -1
  269. package/dist/storages/SessionStorage.cjs +0 -2
  270. package/dist/storages/SessionStorage.cjs.map +0 -1
  271. package/dist/storages/SessionStorage.d.ts +0 -23
  272. package/dist/storages/SessionStorage.mjs +0 -2
  273. package/dist/storages/SessionStorage.mjs.map +0 -1
  274. package/dist/types.cjs +0 -2
  275. package/dist/types.cjs.map +0 -1
  276. package/dist/types.d.ts +0 -1263
  277. package/dist/types.mjs +0 -2
  278. package/dist/types.mjs.map +0 -1
  279. package/dist/utils/HttpClient.cjs +0 -2
  280. package/dist/utils/HttpClient.cjs.map +0 -1
  281. package/dist/utils/HttpClient.d.ts +0 -4
  282. package/dist/utils/HttpClient.mjs +0 -2
  283. package/dist/utils/HttpClient.mjs.map +0 -1
  284. package/dist/utils/Logging.cjs +0 -2
  285. package/dist/utils/Logging.cjs.map +0 -1
  286. package/dist/utils/Logging.d.ts +0 -8
  287. package/dist/utils/Logging.mjs +0 -2
  288. package/dist/utils/Logging.mjs.map +0 -1
  289. package/dist/utils/Metadata.cjs +0 -2
  290. package/dist/utils/Metadata.cjs.map +0 -1
  291. package/dist/utils/Metadata.d.ts +0 -98
  292. package/dist/utils/Metadata.mjs +0 -2
  293. package/dist/utils/Metadata.mjs.map +0 -1
  294. package/dist/utils/Session.cjs +0 -2
  295. package/dist/utils/Session.cjs.map +0 -1
  296. package/dist/utils/Session.d.ts +0 -82
  297. package/dist/utils/Session.mjs +0 -2
  298. package/dist/utils/Session.mjs.map +0 -1
  299. package/dist/utils/State.cjs +0 -2
  300. package/dist/utils/State.cjs.map +0 -1
  301. package/dist/utils/State.d.ts +0 -43
  302. package/dist/utils/State.mjs +0 -2
  303. package/dist/utils/State.mjs.map +0 -1
  304. package/dist/utils/base64Url.cjs +0 -2
  305. package/dist/utils/base64Url.cjs.map +0 -1
  306. package/dist/utils/base64Url.d.ts +0 -27
  307. package/dist/utils/base64Url.mjs +0 -2
  308. package/dist/utils/base64Url.mjs.map +0 -1
  309. package/dist/utils/date.cjs +0 -2
  310. package/dist/utils/date.cjs.map +0 -1
  311. package/dist/utils/date.d.ts +0 -6
  312. package/dist/utils/date.mjs +0 -2
  313. package/dist/utils/date.mjs.map +0 -1
  314. package/dist/utils/handlers.cjs +0 -2
  315. package/dist/utils/handlers.cjs.map +0 -1
  316. package/dist/utils/handlers.d.ts +0 -35
  317. package/dist/utils/handlers.mjs +0 -2
  318. package/dist/utils/handlers.mjs.map +0 -1
  319. package/dist/utils/jwt.cjs +0 -2
  320. package/dist/utils/jwt.cjs.map +0 -1
  321. package/dist/utils/jwt.d.ts +0 -34
  322. package/dist/utils/jwt.mjs +0 -2
  323. package/dist/utils/jwt.mjs.map +0 -1
  324. package/dist/utils/object.cjs +0 -2
  325. package/dist/utils/object.cjs.map +0 -1
  326. package/dist/utils/object.d.ts +0 -1
  327. package/dist/utils/object.mjs +0 -2
  328. package/dist/utils/object.mjs.map +0 -1
@@ -0,0 +1,435 @@
1
+ import type { ServerAdapter, ServerStorage, LogoutTokenClaims } from '../../../src/types';
2
+ import { describe, test, expect, beforeEach, vi } from 'vitest';
3
+ import { createServerStateStorage, createServerMemoryStorage, createSessionIdCookieStorage, createEncryptedCookieStorage } from '../../../src/storages';
4
+ import { flushSetCookies } from '../../../src/utils';
5
+
6
+ type MockEvent = { request: Request };
7
+
8
+ const adapter: ServerAdapter<MockEvent> = {
9
+ toRequest: (event) => event.request,
10
+ };
11
+
12
+ function createMockEvent(cookies: Record<string, string> = {}): MockEvent {
13
+ const cookieHeader = Object.entries(cookies)
14
+ .map(([name, value]) => `${name}=${encodeURIComponent(value)}`)
15
+ .join('; ');
16
+ const request = new Request('https://example.com');
17
+
18
+ if (cookieHeader) {
19
+ // `Request`'s constructor strips forbidden headers like `cookie`, so it must be set directly on the built request instead.
20
+ request.headers.set('cookie', cookieHeader);
21
+ }
22
+
23
+ return { request };
24
+ }
25
+
26
+ // Reads the `Set-Cookie` headers accumulated for an event back into a plain name/value map.
27
+ function getSetCookies(event: MockEvent): Record<string, string> {
28
+ const response = flushSetCookies(event, new Response());
29
+ const cookies: Record<string, string> = {};
30
+
31
+ for (const header of response.headers.getSetCookie()) {
32
+ const [pair] = header.split(';');
33
+ const index = pair.indexOf('=');
34
+
35
+ cookies[pair.slice(0, index)] = decodeURIComponent(pair.slice(index + 1));
36
+ }
37
+
38
+ return cookies;
39
+ }
40
+
41
+ describe('createServerStateStorage', () => {
42
+ test('should return null when the key does not exist', async () => {
43
+ const storage = createServerStateStorage();
44
+
45
+ await expect(storage.get('missing')).resolves.toBeNull();
46
+ });
47
+
48
+ test('should store and retrieve a value', async () => {
49
+ const storage = createServerStateStorage();
50
+
51
+ await storage.set('key', 'value');
52
+
53
+ await expect(storage.get('key')).resolves.toBe('value');
54
+ });
55
+
56
+ test('should overwrite an existing value', async () => {
57
+ const storage = createServerStateStorage();
58
+
59
+ await storage.set('key', 'value');
60
+ await storage.set('key', 'new-value');
61
+
62
+ await expect(storage.get('key')).resolves.toBe('new-value');
63
+ });
64
+
65
+ test('should delete a stored value', async () => {
66
+ const storage = createServerStateStorage();
67
+
68
+ await storage.set('key', 'value');
69
+ await storage.delete('key');
70
+
71
+ await expect(storage.get('key')).resolves.toBeNull();
72
+ });
73
+
74
+ test('should share state across separate instances', async () => {
75
+ const storageA = createServerStateStorage();
76
+ const storageB = createServerStateStorage();
77
+
78
+ await storageA.set('key', 'value');
79
+
80
+ await expect(storageB.get('key')).resolves.toBe('value');
81
+ });
82
+ });
83
+
84
+ describe('createServerMemoryStorage', () => {
85
+ beforeEach(() => {
86
+ delete (globalThis as { sty?: unknown }).sty;
87
+ });
88
+
89
+ test('should return null when the key does not exist', async () => {
90
+ const storage = createServerMemoryStorage();
91
+
92
+ await expect(storage.get('missing')).resolves.toBeNull();
93
+ });
94
+
95
+ test('should store and retrieve a value', async () => {
96
+ const storage = createServerMemoryStorage();
97
+
98
+ await storage.set('key', 'value');
99
+
100
+ await expect(storage.get('key')).resolves.toBe('value');
101
+ });
102
+
103
+ test('should overwrite an existing value', async () => {
104
+ const storage = createServerMemoryStorage();
105
+
106
+ await storage.set('key', 'value');
107
+ await storage.set('key', 'new-value');
108
+
109
+ await expect(storage.get('key')).resolves.toBe('new-value');
110
+ });
111
+
112
+ test('should delete a stored value', async () => {
113
+ const storage = createServerMemoryStorage();
114
+
115
+ await storage.set('key', 'value');
116
+ await storage.delete('key');
117
+
118
+ await expect(storage.get('key')).resolves.toBeNull();
119
+ });
120
+
121
+ test('should reset the shared state whenever a new instance is created', async () => {
122
+ const storageA = createServerMemoryStorage();
123
+
124
+ await storageA.set('key', 'value');
125
+
126
+ createServerMemoryStorage();
127
+
128
+ await expect(storageA.get('key')).resolves.toBeNull();
129
+ });
130
+
131
+ test('should delete sessions matching the logout token sid', async () => {
132
+ const storage = createServerMemoryStorage();
133
+
134
+ await storage.set('session-a', JSON.stringify({ claims: { sid: 'sid-a' } }));
135
+ await storage.set('session-b', JSON.stringify({ claims: { sid: 'sid-b' } }));
136
+
137
+ await storage.deleteByLogoutToken!({ sid: 'sid-a' } as LogoutTokenClaims);
138
+
139
+ await expect(storage.get('session-a')).resolves.toBeNull();
140
+ await expect(storage.get('session-b')).resolves.not.toBeNull();
141
+ });
142
+
143
+ test('should delete sessions matching the logout token sub', async () => {
144
+ const storage = createServerMemoryStorage();
145
+
146
+ await storage.set('session-a', JSON.stringify({ claims: { sub: 'sub-a' } }));
147
+ await storage.set('session-b', JSON.stringify({ claims: { sub: 'sub-b' } }));
148
+
149
+ await storage.deleteByLogoutToken!({ sub: 'sub-a' } as LogoutTokenClaims);
150
+
151
+ await expect(storage.get('session-a')).resolves.toBeNull();
152
+ await expect(storage.get('session-b')).resolves.not.toBeNull();
153
+ });
154
+
155
+ test('should not delete sessions when the logout token matches nothing', async () => {
156
+ const storage = createServerMemoryStorage();
157
+
158
+ await storage.set('session-a', JSON.stringify({ claims: { sid: 'sid-a' } }));
159
+
160
+ await storage.deleteByLogoutToken!({ sid: 'other-sid' } as LogoutTokenClaims);
161
+
162
+ await expect(storage.get('session-a')).resolves.not.toBeNull();
163
+ });
164
+ });
165
+
166
+ describe('createSessionIdCookieStorage', () => {
167
+ type MockEvent = { request: Request };
168
+
169
+ function createSessionAdapter(): ServerAdapter<MockEvent> {
170
+ return { toRequest: (event) => event.request };
171
+ }
172
+
173
+ function createUnderlyingStorage(): ServerStorage<MockEvent> {
174
+ return {
175
+ get: vi.fn().mockResolvedValue(null),
176
+ set: vi.fn().mockResolvedValue(undefined),
177
+ delete: vi.fn().mockResolvedValue(undefined),
178
+ deleteByLogoutToken: vi.fn().mockResolvedValue(undefined),
179
+ };
180
+ }
181
+
182
+ test('should return null and skip the underlying storage when no session id cookie exists', async () => {
183
+ const underlying = createUnderlyingStorage();
184
+ const storage = createSessionIdCookieStorage(createSessionAdapter(), underlying);
185
+
186
+ await expect(storage.get('key', createMockEvent())).resolves.toBeNull();
187
+ expect(underlying.get).not.toHaveBeenCalled();
188
+ });
189
+
190
+ test('should generate a new session id and set a cookie on first set', async () => {
191
+ const underlying = createUnderlyingStorage();
192
+ const storage = createSessionIdCookieStorage(createSessionAdapter(), underlying);
193
+ const outgoingEvent = createMockEvent();
194
+
195
+ await storage.set('key', 'value', outgoingEvent);
196
+
197
+ const cookies = getSetCookies(outgoingEvent);
198
+
199
+ expect(cookies.key).toBeTruthy();
200
+ expect(underlying.set).toHaveBeenCalledWith(cookies.key, 'value');
201
+ });
202
+
203
+ test('should use the custom uuidGenerator when generating a session id', async () => {
204
+ const underlying = createUnderlyingStorage();
205
+ const uuidGenerator = vi.fn().mockReturnValue('custom-uuid');
206
+ const randomUUID = vi.spyOn(globalThis.crypto, 'randomUUID');
207
+ const storage = createSessionIdCookieStorage(createSessionAdapter(), underlying, { uuidGenerator });
208
+ const outgoingEvent = createMockEvent();
209
+
210
+ await storage.set('key', 'value', outgoingEvent);
211
+
212
+ expect(uuidGenerator).toHaveBeenCalledTimes(1);
213
+ expect(randomUUID).not.toHaveBeenCalled();
214
+ expect(getSetCookies(outgoingEvent).key).toBe('custom-uuid');
215
+ expect(underlying.set).toHaveBeenCalledWith('custom-uuid', 'value');
216
+
217
+ randomUUID.mockRestore();
218
+ });
219
+
220
+ test('should reuse the existing session id on subsequent set calls', async () => {
221
+ const underlying = createUnderlyingStorage();
222
+ const storage = createSessionIdCookieStorage(createSessionAdapter(), underlying);
223
+ const outgoingEvent = createMockEvent();
224
+
225
+ await storage.set('key', 'value', outgoingEvent);
226
+ const sessionId = getSetCookies(outgoingEvent).key;
227
+
228
+ const incomingEvent = createMockEvent({ key: sessionId });
229
+ await storage.set('key', 'other-value', incomingEvent);
230
+
231
+ expect(getSetCookies(incomingEvent).key).toBeUndefined();
232
+ expect(underlying.set).toHaveBeenLastCalledWith(sessionId, 'other-value');
233
+ });
234
+
235
+ test('should retrieve the value from the underlying storage via the session id cookie', async () => {
236
+ const underlying = createServerMemoryStorage();
237
+ const storage = createSessionIdCookieStorage(createSessionAdapter(), underlying);
238
+ const outgoingEvent = createMockEvent();
239
+
240
+ await storage.set('key', 'value', outgoingEvent);
241
+ const incomingEvent = createMockEvent(getSetCookies(outgoingEvent));
242
+
243
+ await expect(storage.get('key', incomingEvent)).resolves.toBe('value');
244
+ });
245
+
246
+ test('should delete the session from the underlying storage and expire the cookie', async () => {
247
+ const underlying = createUnderlyingStorage();
248
+ const storage = createSessionIdCookieStorage(createSessionAdapter(), underlying);
249
+ const outgoingEvent = createMockEvent();
250
+
251
+ await storage.set('key', 'value', outgoingEvent);
252
+ const sessionId = getSetCookies(outgoingEvent).key;
253
+
254
+ const incomingEvent = createMockEvent({ key: sessionId });
255
+ await storage.delete('key', incomingEvent);
256
+
257
+ expect(underlying.delete).toHaveBeenCalledWith(sessionId);
258
+ const [header] = flushSetCookies(incomingEvent, new Response()).headers.getSetCookie();
259
+
260
+ expect(header).toContain('key=');
261
+ expect(header).toContain('Expires=Thu, 01 Jan 1970');
262
+ });
263
+
264
+ test('should still expire the cookie when no session id exists on delete', async () => {
265
+ const underlying = createUnderlyingStorage();
266
+ const storage = createSessionIdCookieStorage(createSessionAdapter(), underlying);
267
+ const event = createMockEvent();
268
+
269
+ await storage.delete('key', event);
270
+
271
+ expect(underlying.delete).not.toHaveBeenCalled();
272
+ const [header] = flushSetCookies(event, new Response()).headers.getSetCookie();
273
+
274
+ expect(header).toContain('Expires=Thu, 01 Jan 1970');
275
+ });
276
+
277
+ test('should apply the default and custom cookie attributes', async () => {
278
+ const storage = createSessionIdCookieStorage(createSessionAdapter(), createUnderlyingStorage(), {
279
+ defaultCookieOptions: { sameSite: 'strict', path: '/custom' },
280
+ });
281
+ const outgoingEvent = createMockEvent();
282
+
283
+ await storage.set('key', 'value', outgoingEvent);
284
+
285
+ const [header] = flushSetCookies(outgoingEvent, new Response()).headers.getSetCookie();
286
+
287
+ expect(header).toContain('Path=/custom');
288
+ expect(header).toContain('SameSite=Strict');
289
+ expect(header).toContain('HttpOnly');
290
+ expect(header).toContain('Secure');
291
+ });
292
+
293
+ test('should delegate deleteByLogoutToken to the underlying storage', async () => {
294
+ const underlying = createUnderlyingStorage();
295
+ const storage = createSessionIdCookieStorage(createSessionAdapter(), underlying);
296
+ const token = { sid: 'session-id' } as LogoutTokenClaims;
297
+
298
+ await storage.deleteByLogoutToken!(token);
299
+
300
+ expect(underlying.deleteByLogoutToken).toHaveBeenCalledWith(token);
301
+ });
302
+
303
+ test('should use a pre-existing adapter.getSessionId instead of the cookie-based default', async () => {
304
+ const underlying = createUnderlyingStorage();
305
+ const adapter = createSessionAdapter();
306
+ adapter.getSessionId = vi.fn().mockResolvedValue('custom-session-id');
307
+
308
+ const storage = createSessionIdCookieStorage(adapter, underlying);
309
+
310
+ await expect(storage.get('key', createMockEvent())).resolves.toBeNull();
311
+ expect(underlying.get).toHaveBeenCalledWith('custom-session-id');
312
+ });
313
+ });
314
+
315
+ describe('createEncryptedCookieStorage', () => {
316
+ const secret = 'test-secret';
317
+
318
+ test('should return null when no event is provided', async () => {
319
+ const storage = createEncryptedCookieStorage(secret, adapter);
320
+
321
+ await expect(storage.get('key')).resolves.toBeNull();
322
+ });
323
+
324
+ test('should return null when the cookie is not present', async () => {
325
+ const storage = createEncryptedCookieStorage(secret, adapter);
326
+
327
+ await expect(storage.get('key', createMockEvent())).resolves.toBeNull();
328
+ });
329
+
330
+ test('should store and retrieve a value via cookies', async () => {
331
+ const storage = createEncryptedCookieStorage(secret, adapter);
332
+ const outgoingEvent = createMockEvent();
333
+
334
+ await storage.set('key', 'value', outgoingEvent);
335
+
336
+ const incomingEvent = createMockEvent(getSetCookies(outgoingEvent));
337
+
338
+ await expect(storage.get('key', incomingEvent)).resolves.toBe('value');
339
+ });
340
+
341
+ test('should encrypt the cookie value', async () => {
342
+ const storage = createEncryptedCookieStorage(secret, adapter);
343
+ const outgoingEvent = createMockEvent();
344
+
345
+ await storage.set('key', 'sensitive-value', outgoingEvent);
346
+
347
+ expect(getSetCookies(outgoingEvent).key).not.toContain('sensitive-value');
348
+ });
349
+
350
+ test('should apply the default cookie attributes', async () => {
351
+ const storage = createEncryptedCookieStorage(secret, adapter);
352
+ const outgoingEvent = createMockEvent();
353
+
354
+ await storage.set('key', 'value', outgoingEvent);
355
+
356
+ const [header] = flushSetCookies(outgoingEvent, new Response()).headers.getSetCookie();
357
+
358
+ expect(header).toContain('Path=/');
359
+ expect(header).toContain('HttpOnly');
360
+ expect(header).toContain('Secure');
361
+ expect(header).toContain('SameSite=Lax');
362
+ });
363
+
364
+ test('should accept custom cookie options', async () => {
365
+ const storage = createEncryptedCookieStorage(secret, adapter, {
366
+ defaultCookieOptions: { httpOnly: false, secure: false, sameSite: 'strict', path: '/custom' },
367
+ });
368
+ const outgoingEvent = createMockEvent();
369
+
370
+ await storage.set('key', 'value', outgoingEvent);
371
+
372
+ const [header] = flushSetCookies(outgoingEvent, new Response()).headers.getSetCookie();
373
+
374
+ expect(header).toContain('Path=/custom');
375
+ expect(header).toContain('SameSite=Strict');
376
+ expect(header).not.toContain('HttpOnly');
377
+ expect(header).not.toContain('Secure');
378
+ });
379
+
380
+ test('should chunk values larger than the cookie size limit', async () => {
381
+ const storage = createEncryptedCookieStorage(secret, adapter);
382
+ const outgoingEvent = createMockEvent();
383
+ const largeValue = 'a'.repeat(3000);
384
+
385
+ await storage.set('key', largeValue, outgoingEvent);
386
+
387
+ const cookies = getSetCookies(outgoingEvent);
388
+
389
+ expect(cookies.key).toBe('');
390
+ expect(cookies['key.0']).toBeDefined();
391
+ expect(cookies['key.1']).toBeDefined();
392
+
393
+ await expect(storage.get('key', createMockEvent(cookies))).resolves.toBe(largeValue);
394
+ });
395
+
396
+ test('should remove existing chunks when overwriting with a smaller value', async () => {
397
+ const storage = createEncryptedCookieStorage(secret, adapter);
398
+ const outgoingEvent = createMockEvent();
399
+ const largeValue = 'a'.repeat(3000);
400
+
401
+ await storage.set('key', largeValue, outgoingEvent);
402
+ const incomingEvent = createMockEvent(getSetCookies(outgoingEvent));
403
+
404
+ await storage.set('key', 'small-value', incomingEvent);
405
+ const cookies = getSetCookies(incomingEvent);
406
+
407
+ expect(cookies.key).toBeDefined();
408
+ expect(cookies['key.0']).toBe('');
409
+ expect(cookies['key.1']).toBe('');
410
+ });
411
+
412
+ test('should delete a stored cookie', async () => {
413
+ const storage = createEncryptedCookieStorage(secret, adapter);
414
+ const outgoingEvent = createMockEvent();
415
+
416
+ await storage.delete('key', outgoingEvent);
417
+
418
+ const [header] = flushSetCookies(outgoingEvent, new Response()).headers.getSetCookie();
419
+
420
+ expect(header).toContain('key=');
421
+ expect(header).toContain('Expires=Thu, 01 Jan 1970');
422
+ });
423
+
424
+ test('should return null after decrypting with a different secret', async () => {
425
+ const storage = createEncryptedCookieStorage(secret, adapter);
426
+ const outgoingEvent = createMockEvent();
427
+
428
+ await storage.set('key', 'value', outgoingEvent);
429
+
430
+ const otherStorage = createEncryptedCookieStorage('other-secret', adapter);
431
+ const incomingEvent = createMockEvent(getSetCookies(outgoingEvent));
432
+
433
+ await expect(otherStorage.get('key', incomingEvent)).resolves.toBeNull();
434
+ });
435
+ });
@@ -0,0 +1,46 @@
1
+ import { describe, beforeEach, test, expect } from 'vitest';
2
+ import { createSessionStorage } from '../../../src/storages';
3
+
4
+ describe('createSessionStorage', () => {
5
+ beforeEach(() => {
6
+ globalThis.sessionStorage?.clear();
7
+ });
8
+
9
+ test('should return null when the key does not exist', async () => {
10
+ const storage = createSessionStorage();
11
+
12
+ await expect(storage.get('missing')).resolves.toBeNull();
13
+ });
14
+
15
+ test('should store and retrieve a value', async () => {
16
+ const storage = createSessionStorage();
17
+
18
+ await storage.set('key', 'value');
19
+
20
+ await expect(storage.get('key')).resolves.toBe('value');
21
+ });
22
+
23
+ test('should overwrite an existing value', async () => {
24
+ const storage = createSessionStorage();
25
+
26
+ await storage.set('key', 'value');
27
+ await storage.set('key', 'new-value');
28
+
29
+ await expect(storage.get('key')).resolves.toBe('new-value');
30
+ });
31
+
32
+ test('should delete a stored value', async () => {
33
+ const storage = createSessionStorage();
34
+
35
+ await storage.set('key', 'value');
36
+ await storage.delete('key');
37
+
38
+ await expect(storage.get('key')).resolves.toBeNull();
39
+ });
40
+
41
+ test('should not throw when deleting a non-existent key', async () => {
42
+ const storage = createSessionStorage();
43
+
44
+ await expect(storage.delete('missing')).resolves.toBeUndefined();
45
+ });
46
+ });
@@ -0,0 +1,106 @@
1
+ import type { SDKStorage } from '../../../src/types';
2
+ import type { WorkerStorageRequest, WorkerStorageResponse } from '../../../src/storages/worker';
3
+ import { describe, test, expect, beforeEach, afterEach, vi } from 'vitest';
4
+ import { createWorkerStorage, handleWorkerStorageRequests } from '../../../src/storages';
5
+
6
+ // Simulates a Worker in the same thread: messages posted to one side are dispatched as `message` events on the other.
7
+ class MockWorker extends EventTarget implements Partial<Worker> {
8
+ public other!: MockWorker;
9
+
10
+ postMessage(message: unknown): void {
11
+ this.other.dispatchEvent(new MessageEvent('message', { data: message }));
12
+ }
13
+ }
14
+
15
+ function createLinkedWorkers(): [mainSide: MockWorker, workerSide: MockWorker] {
16
+ const workerSide = new MockWorker();
17
+ const mainSide = new MockWorker();
18
+
19
+ workerSide.other = mainSide;
20
+ mainSide.other = workerSide;
21
+
22
+ return [mainSide, workerSide];
23
+ }
24
+
25
+ function createMemoryLikeStorage(): SDKStorage {
26
+ const store = new Map<string, string>();
27
+
28
+ return {
29
+ get: (key) => {
30
+ return Promise.resolve(store.get(key) ?? null);
31
+ },
32
+ set: (key, value) => {
33
+ store.set(key, value);
34
+ return Promise.resolve();
35
+ },
36
+ delete: (key) => {
37
+ store.delete(key);
38
+ return Promise.resolve();
39
+ },
40
+ };
41
+ }
42
+
43
+ afterEach(() => {
44
+ vi.unstubAllGlobals();
45
+ });
46
+
47
+ describe('createWorkerStorage / handleWorkerStorageRequests', () => {
48
+ let mainSide: MockWorker;
49
+ let workerSide: MockWorker;
50
+
51
+ beforeEach(() => {
52
+ [mainSide, workerSide] = createLinkedWorkers();
53
+ vi.stubGlobal('self', workerSide);
54
+ });
55
+
56
+ test('should return null when the key does not exist', async () => {
57
+ handleWorkerStorageRequests(createMemoryLikeStorage());
58
+ const storage = createWorkerStorage(mainSide);
59
+
60
+ await expect(storage.get('missing')).resolves.toBeNull();
61
+ });
62
+
63
+ test('should store and retrieve a value', async () => {
64
+ handleWorkerStorageRequests(createMemoryLikeStorage());
65
+ const storage = createWorkerStorage(mainSide);
66
+
67
+ await storage.set('key', 'value');
68
+
69
+ await expect(storage.get('key')).resolves.toBe('value');
70
+ });
71
+
72
+ test('should delete a stored value', async () => {
73
+ handleWorkerStorageRequests(createMemoryLikeStorage());
74
+ const storage = createWorkerStorage(mainSide);
75
+
76
+ await storage.set('key', 'value');
77
+ await storage.delete('key');
78
+
79
+ await expect(storage.get('key')).resolves.toBeNull();
80
+ });
81
+
82
+ test('should reject the pending call when the underlying storage throws', async () => {
83
+ handleWorkerStorageRequests({
84
+ get: () => Promise.reject(new Error('boom')),
85
+ set: () => Promise.resolve(),
86
+ delete: () => Promise.resolve(),
87
+ });
88
+ const storage = createWorkerStorage(mainSide);
89
+
90
+ await expect(storage.get('key')).rejects.toThrow('boom');
91
+ });
92
+
93
+ test('should ignore incoming messages without a matching pending request', () => {
94
+ createWorkerStorage(mainSide);
95
+
96
+ expect(() =>
97
+ mainSide.dispatchEvent(new MessageEvent('message', { data: { requestId: 'unknown', result: 'value' } satisfies WorkerStorageResponse })),
98
+ ).not.toThrow();
99
+ });
100
+
101
+ test('should ignore malformed requests inside the worker', () => {
102
+ handleWorkerStorageRequests(createMemoryLikeStorage());
103
+
104
+ expect(() => workerSide.dispatchEvent(new MessageEvent('message', { data: {} as WorkerStorageRequest }))).not.toThrow();
105
+ });
106
+ });
@@ -0,0 +1,77 @@
1
+ import { describe, test, expect, vi, afterEach } from 'vitest';
2
+ import { timestamp, unflattenObject, buildCookieString } from '../../../src/utils/common';
3
+
4
+ describe('timestamp', () => {
5
+ afterEach(() => {
6
+ vi.useRealTimers();
7
+ });
8
+
9
+ test('returns the current time in whole seconds', () => {
10
+ vi.useFakeTimers();
11
+ vi.setSystemTime(new Date('2030-01-01T00:00:00.000Z'));
12
+
13
+ expect(timestamp()).toBe(Math.floor(new Date('2030-01-01T00:00:00.000Z').getTime() / 1000));
14
+ });
15
+ });
16
+
17
+ describe('unflattenObject', () => {
18
+ test('returns an empty object for an empty input', () => {
19
+ expect(unflattenObject({})).toEqual({});
20
+ });
21
+
22
+ test('unflattens a single-level dot-separated key', () => {
23
+ expect(unflattenObject({ 'a.b': 1 })).toEqual({ a: { b: 1 } });
24
+ });
25
+
26
+ test('unflattens multiple nested levels', () => {
27
+ expect(unflattenObject({ 'a.b.c': 1 })).toEqual({ a: { b: { c: 1 } } });
28
+ });
29
+
30
+ test('merges sibling keys under the same parent', () => {
31
+ expect(unflattenObject({ 'a.b': 1, 'a.c': 2 })).toEqual({ a: { b: 1, c: 2 } });
32
+ });
33
+
34
+ test('keeps keys without dots at the top level', () => {
35
+ expect(unflattenObject({ foo: 'bar' })).toEqual({ foo: 'bar' });
36
+ });
37
+ });
38
+
39
+ describe('buildCookieString', () => {
40
+ test('includes HttpOnly and Secure by default', () => {
41
+ expect(buildCookieString('foo', 'bar', {})).toBe('foo=bar; HttpOnly; Secure; Path=/');
42
+ });
43
+
44
+ test('URI-encodes the cookie value', () => {
45
+ expect(buildCookieString('foo', 'hello world', {})).toBe('foo=hello%20world; HttpOnly; Secure; Path=/');
46
+ });
47
+
48
+ test('omits HttpOnly when explicitly false', () => {
49
+ expect(buildCookieString('foo', 'bar', { httpOnly: false })).toBe('foo=bar; Secure; Path=/');
50
+ });
51
+
52
+ test('omits Secure when explicitly false', () => {
53
+ expect(buildCookieString('foo', 'bar', { secure: false })).toBe('foo=bar; HttpOnly; Path=/');
54
+ });
55
+
56
+ test('uses a custom path', () => {
57
+ expect(buildCookieString('foo', 'bar', { path: '/auth' })).toBe('foo=bar; HttpOnly; Secure; Path=/auth');
58
+ });
59
+
60
+ test('includes SameSite when provided', () => {
61
+ expect(buildCookieString('foo', 'bar', { sameSite: 'lax' })).toBe('foo=bar; HttpOnly; Secure; Path=/; SameSite=lax');
62
+ });
63
+
64
+ test('includes Max-Age when provided', () => {
65
+ expect(buildCookieString('foo', 'bar', { maxAge: 3600 })).toBe('foo=bar; HttpOnly; Secure; Path=/; Max-Age=3600');
66
+ });
67
+
68
+ test('includes Domain when provided', () => {
69
+ expect(buildCookieString('foo', 'bar', { domain: 'brandtegrity.io' })).toBe('foo=bar; HttpOnly; Secure; Path=/; Domain=brandtegrity.io');
70
+ });
71
+
72
+ test('combines all attributes', () => {
73
+ expect(buildCookieString('foo', 'bar', { path: '/auth', sameSite: 'strict', maxAge: 60, domain: 'brandtegrity.io' })).toBe(
74
+ 'foo=bar; HttpOnly; Secure; Path=/auth; SameSite=strict; Max-Age=60; Domain=brandtegrity.io',
75
+ );
76
+ });
77
+ });