payload-plugin-newsletter 0.3.2 → 0.4.5

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 (180) hide show
  1. package/CHANGELOG.md +44 -1
  2. package/CLAUDE.md +31 -19
  3. package/dist/client.cjs +899 -0
  4. package/dist/client.cjs.map +1 -0
  5. package/dist/client.d.cts +52 -0
  6. package/dist/client.d.ts +52 -0
  7. package/dist/client.js +867 -0
  8. package/dist/client.js.map +1 -0
  9. package/dist/components.cjs +899 -0
  10. package/dist/components.cjs.map +1 -0
  11. package/dist/components.d.cts +4 -0
  12. package/dist/components.d.ts +4 -0
  13. package/dist/components.js +867 -0
  14. package/dist/components.js.map +1 -0
  15. package/dist/index.cjs +2004 -0
  16. package/dist/index.cjs.map +1 -0
  17. package/dist/index.d.cts +11 -0
  18. package/dist/index.d.ts +6 -5
  19. package/dist/index.js +1967 -0
  20. package/dist/index.js.map +1 -0
  21. package/dist/types.cjs +19 -0
  22. package/dist/types.cjs.map +1 -0
  23. package/dist/{types/index.d.ts → types.d.cts} +19 -17
  24. package/dist/types.d.ts +350 -0
  25. package/dist/types.js +1 -0
  26. package/dist/types.js.map +1 -0
  27. package/package.json +48 -25
  28. package/dist/.tsbuildinfo +0 -1
  29. package/dist/collections/NewsletterSettings.d.ts +0 -4
  30. package/dist/collections/NewsletterSettings.d.ts.map +0 -1
  31. package/dist/collections/Subscribers.d.ts +0 -4
  32. package/dist/collections/Subscribers.d.ts.map +0 -1
  33. package/dist/components/MagicLinkVerify.d.ts +0 -27
  34. package/dist/components/MagicLinkVerify.d.ts.map +0 -1
  35. package/dist/components/NewsletterForm.d.ts +0 -5
  36. package/dist/components/NewsletterForm.d.ts.map +0 -1
  37. package/dist/components/PreferencesForm.d.ts +0 -5
  38. package/dist/components/PreferencesForm.d.ts.map +0 -1
  39. package/dist/components/index.d.ts +0 -5
  40. package/dist/components/index.d.ts.map +0 -1
  41. package/dist/endpoints/index.d.ts +0 -4
  42. package/dist/endpoints/index.d.ts.map +0 -1
  43. package/dist/endpoints/preferences.d.ts +0 -5
  44. package/dist/endpoints/preferences.d.ts.map +0 -1
  45. package/dist/endpoints/subscribe.d.ts +0 -4
  46. package/dist/endpoints/subscribe.d.ts.map +0 -1
  47. package/dist/endpoints/unsubscribe.d.ts +0 -4
  48. package/dist/endpoints/unsubscribe.d.ts.map +0 -1
  49. package/dist/endpoints/verify-magic-link.d.ts +0 -4
  50. package/dist/endpoints/verify-magic-link.d.ts.map +0 -1
  51. package/dist/exports/client.d.ts +0 -6
  52. package/dist/exports/client.d.ts.map +0 -1
  53. package/dist/exports/components.d.ts +0 -2
  54. package/dist/exports/components.d.ts.map +0 -1
  55. package/dist/exports/types.d.ts +0 -2
  56. package/dist/exports/types.d.ts.map +0 -1
  57. package/dist/fields/newsletterScheduling.d.ts +0 -4
  58. package/dist/fields/newsletterScheduling.d.ts.map +0 -1
  59. package/dist/hooks/useNewsletterAuth.d.ts +0 -16
  60. package/dist/hooks/useNewsletterAuth.d.ts.map +0 -1
  61. package/dist/index.d.ts.map +0 -1
  62. package/dist/providers/broadcast.d.ts +0 -19
  63. package/dist/providers/broadcast.d.ts.map +0 -1
  64. package/dist/providers/index.d.ts +0 -23
  65. package/dist/providers/index.d.ts.map +0 -1
  66. package/dist/providers/resend.d.ts +0 -20
  67. package/dist/providers/resend.d.ts.map +0 -1
  68. package/dist/providers/types.d.ts +0 -46
  69. package/dist/providers/types.d.ts.map +0 -1
  70. package/dist/src/__tests__/fixtures/newsletter-settings.js +0 -41
  71. package/dist/src/__tests__/fixtures/newsletter-settings.js.map +0 -1
  72. package/dist/src/__tests__/fixtures/subscribers.js +0 -70
  73. package/dist/src/__tests__/fixtures/subscribers.js.map +0 -1
  74. package/dist/src/__tests__/integration/collections/subscriber-hooks.test.js +0 -356
  75. package/dist/src/__tests__/integration/collections/subscriber-hooks.test.js.map +0 -1
  76. package/dist/src/__tests__/integration/endpoints/preferences.test.js +0 -266
  77. package/dist/src/__tests__/integration/endpoints/preferences.test.js.map +0 -1
  78. package/dist/src/__tests__/integration/endpoints/subscribe.test.js +0 -280
  79. package/dist/src/__tests__/integration/endpoints/subscribe.test.js.map +0 -1
  80. package/dist/src/__tests__/integration/endpoints/unsubscribe.test.js +0 -187
  81. package/dist/src/__tests__/integration/endpoints/unsubscribe.test.js.map +0 -1
  82. package/dist/src/__tests__/integration/endpoints/verify-magic-link.test.js +0 -188
  83. package/dist/src/__tests__/integration/endpoints/verify-magic-link.test.js.map +0 -1
  84. package/dist/src/__tests__/mocks/email-providers.js +0 -153
  85. package/dist/src/__tests__/mocks/email-providers.js.map +0 -1
  86. package/dist/src/__tests__/mocks/payload.js +0 -244
  87. package/dist/src/__tests__/mocks/payload.js.map +0 -1
  88. package/dist/src/__tests__/security/csrf-protection.test.js +0 -309
  89. package/dist/src/__tests__/security/csrf-protection.test.js.map +0 -1
  90. package/dist/src/__tests__/security/settings-access.test.js +0 -204
  91. package/dist/src/__tests__/security/settings-access.test.js.map +0 -1
  92. package/dist/src/__tests__/security/subscriber-access.test.js +0 -210
  93. package/dist/src/__tests__/security/subscriber-access.test.js.map +0 -1
  94. package/dist/src/__tests__/security/xss-prevention.test.js +0 -305
  95. package/dist/src/__tests__/security/xss-prevention.test.js.map +0 -1
  96. package/dist/src/__tests__/setup/integration.setup.js +0 -38
  97. package/dist/src/__tests__/setup/integration.setup.js.map +0 -1
  98. package/dist/src/__tests__/setup/unit.setup.js +0 -41
  99. package/dist/src/__tests__/setup/unit.setup.js.map +0 -1
  100. package/dist/src/__tests__/unit/utils/access.test.js +0 -116
  101. package/dist/src/__tests__/unit/utils/access.test.js.map +0 -1
  102. package/dist/src/__tests__/unit/utils/jwt.test.js +0 -238
  103. package/dist/src/__tests__/unit/utils/jwt.test.js.map +0 -1
  104. package/dist/src/collections/NewsletterSettings.js +0 -390
  105. package/dist/src/collections/NewsletterSettings.js.map +0 -1
  106. package/dist/src/collections/Subscribers.js +0 -309
  107. package/dist/src/collections/Subscribers.js.map +0 -1
  108. package/dist/src/components/MagicLinkVerify.js +0 -180
  109. package/dist/src/components/MagicLinkVerify.js.map +0 -1
  110. package/dist/src/components/NewsletterForm.js +0 -326
  111. package/dist/src/components/NewsletterForm.js.map +0 -1
  112. package/dist/src/components/PreferencesForm.js +0 -524
  113. package/dist/src/components/PreferencesForm.js.map +0 -1
  114. package/dist/src/components/index.js +0 -5
  115. package/dist/src/components/index.js.map +0 -1
  116. package/dist/src/endpoints/index.js +0 -17
  117. package/dist/src/endpoints/index.js.map +0 -1
  118. package/dist/src/endpoints/preferences.js +0 -136
  119. package/dist/src/endpoints/preferences.js.map +0 -1
  120. package/dist/src/endpoints/subscribe.js +0 -151
  121. package/dist/src/endpoints/subscribe.js.map +0 -1
  122. package/dist/src/endpoints/unsubscribe.js +0 -105
  123. package/dist/src/endpoints/unsubscribe.js.map +0 -1
  124. package/dist/src/endpoints/verify-magic-link.js +0 -103
  125. package/dist/src/endpoints/verify-magic-link.js.map +0 -1
  126. package/dist/src/exports/client.js +0 -7
  127. package/dist/src/exports/client.js.map +0 -1
  128. package/dist/src/exports/components.js +0 -6
  129. package/dist/src/exports/components.js.map +0 -1
  130. package/dist/src/exports/types.js +0 -3
  131. package/dist/src/exports/types.js.map +0 -1
  132. package/dist/src/fields/newsletterScheduling.js +0 -195
  133. package/dist/src/fields/newsletterScheduling.js.map +0 -1
  134. package/dist/src/hooks/useNewsletterAuth.js +0 -112
  135. package/dist/src/hooks/useNewsletterAuth.js.map +0 -1
  136. package/dist/src/index.js +0 -130
  137. package/dist/src/index.js.map +0 -1
  138. package/dist/src/providers/broadcast.js +0 -158
  139. package/dist/src/providers/broadcast.js.map +0 -1
  140. package/dist/src/providers/index.js +0 -63
  141. package/dist/src/providers/index.js.map +0 -1
  142. package/dist/src/providers/resend.js +0 -122
  143. package/dist/src/providers/resend.js.map +0 -1
  144. package/dist/src/providers/types.js +0 -12
  145. package/dist/src/providers/types.js.map +0 -1
  146. package/dist/src/templates/BaseTemplate.js +0 -105
  147. package/dist/src/templates/BaseTemplate.js.map +0 -1
  148. package/dist/src/templates/MagicLinkTemplate.js +0 -178
  149. package/dist/src/templates/MagicLinkTemplate.js.map +0 -1
  150. package/dist/src/templates/NewsletterTemplate.js +0 -150
  151. package/dist/src/templates/NewsletterTemplate.js.map +0 -1
  152. package/dist/src/templates/WelcomeTemplate.js +0 -192
  153. package/dist/src/templates/WelcomeTemplate.js.map +0 -1
  154. package/dist/src/templates/index.js +0 -6
  155. package/dist/src/templates/index.js.map +0 -1
  156. package/dist/src/types/index.js +0 -3
  157. package/dist/src/types/index.js.map +0 -1
  158. package/dist/src/utils/access.js +0 -80
  159. package/dist/src/utils/access.js.map +0 -1
  160. package/dist/src/utils/jwt.js +0 -91
  161. package/dist/src/utils/jwt.js.map +0 -1
  162. package/dist/src/utils/validation.js +0 -74
  163. package/dist/src/utils/validation.js.map +0 -1
  164. package/dist/templates/BaseTemplate.d.ts +0 -45
  165. package/dist/templates/BaseTemplate.d.ts.map +0 -1
  166. package/dist/templates/MagicLinkTemplate.d.ts +0 -67
  167. package/dist/templates/MagicLinkTemplate.d.ts.map +0 -1
  168. package/dist/templates/NewsletterTemplate.d.ts +0 -112
  169. package/dist/templates/NewsletterTemplate.d.ts.map +0 -1
  170. package/dist/templates/WelcomeTemplate.d.ts +0 -55
  171. package/dist/templates/WelcomeTemplate.d.ts.map +0 -1
  172. package/dist/templates/index.d.ts +0 -7
  173. package/dist/templates/index.d.ts.map +0 -1
  174. package/dist/types/index.d.ts.map +0 -1
  175. package/dist/utils/access.d.ts +0 -15
  176. package/dist/utils/access.d.ts.map +0 -1
  177. package/dist/utils/jwt.d.ts +0 -32
  178. package/dist/utils/jwt.d.ts.map +0 -1
  179. package/dist/utils/validation.d.ts +0 -25
  180. package/dist/utils/validation.d.ts.map +0 -1
@@ -1,41 +0,0 @@
1
- import '@testing-library/jest-dom';
2
- import { vi } from 'vitest';
3
- // Mock environment variables
4
- process.env.JWT_SECRET = 'test-jwt-secret';
5
- process.env.PAYLOAD_SECRET = 'test-payload-secret';
6
- // Mock console methods to reduce noise in tests
7
- global.console = {
8
- ...console,
9
- error: vi.fn(),
10
- warn: vi.fn()
11
- };
12
- // Mock window.location for browser-like tests
13
- Object.defineProperty(window, 'location', {
14
- value: {
15
- href: 'http://localhost:3000',
16
- origin: 'http://localhost:3000',
17
- search: ''
18
- },
19
- writable: true
20
- });
21
- // Mock localStorage
22
- const localStorageMock = {
23
- getItem: vi.fn(),
24
- setItem: vi.fn(),
25
- removeItem: vi.fn(),
26
- clear: vi.fn()
27
- };
28
- Object.defineProperty(window, 'localStorage', {
29
- value: localStorageMock,
30
- writable: true
31
- });
32
- // Reset mocks before each test
33
- beforeEach(()=>{
34
- vi.clearAllMocks();
35
- localStorageMock.getItem.mockReset();
36
- localStorageMock.setItem.mockReset();
37
- localStorageMock.removeItem.mockReset();
38
- localStorageMock.clear.mockReset();
39
- });
40
-
41
- //# sourceMappingURL=unit.setup.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../../../src/__tests__/setup/unit.setup.ts"],"sourcesContent":["import '@testing-library/jest-dom'\nimport { vi } from 'vitest'\n\n// Mock environment variables\nprocess.env.JWT_SECRET = 'test-jwt-secret'\nprocess.env.PAYLOAD_SECRET = 'test-payload-secret'\n\n// Mock console methods to reduce noise in tests\nglobal.console = {\n ...console,\n error: vi.fn(),\n warn: vi.fn(),\n}\n\n// Mock window.location for browser-like tests\nObject.defineProperty(window, 'location', {\n value: {\n href: 'http://localhost:3000',\n origin: 'http://localhost:3000',\n search: '',\n },\n writable: true,\n})\n\n// Mock localStorage\nconst localStorageMock = {\n getItem: vi.fn(),\n setItem: vi.fn(),\n removeItem: vi.fn(),\n clear: vi.fn(),\n}\nObject.defineProperty(window, 'localStorage', {\n value: localStorageMock,\n writable: true,\n})\n\n// Reset mocks before each test\nbeforeEach(() => {\n vi.clearAllMocks()\n localStorageMock.getItem.mockReset()\n localStorageMock.setItem.mockReset()\n localStorageMock.removeItem.mockReset()\n localStorageMock.clear.mockReset()\n})"],"names":["vi","process","env","JWT_SECRET","PAYLOAD_SECRET","global","console","error","fn","warn","Object","defineProperty","window","value","href","origin","search","writable","localStorageMock","getItem","setItem","removeItem","clear","beforeEach","clearAllMocks","mockReset"],"mappings":"AAAA,OAAO,4BAA2B;AAClC,SAASA,EAAE,QAAQ,SAAQ;AAE3B,6BAA6B;AAC7BC,QAAQC,GAAG,CAACC,UAAU,GAAG;AACzBF,QAAQC,GAAG,CAACE,cAAc,GAAG;AAE7B,gDAAgD;AAChDC,OAAOC,OAAO,GAAG;IACf,GAAGA,OAAO;IACVC,OAAOP,GAAGQ,EAAE;IACZC,MAAMT,GAAGQ,EAAE;AACb;AAEA,8CAA8C;AAC9CE,OAAOC,cAAc,CAACC,QAAQ,YAAY;IACxCC,OAAO;QACLC,MAAM;QACNC,QAAQ;QACRC,QAAQ;IACV;IACAC,UAAU;AACZ;AAEA,oBAAoB;AACpB,MAAMC,mBAAmB;IACvBC,SAASnB,GAAGQ,EAAE;IACdY,SAASpB,GAAGQ,EAAE;IACda,YAAYrB,GAAGQ,EAAE;IACjBc,OAAOtB,GAAGQ,EAAE;AACd;AACAE,OAAOC,cAAc,CAACC,QAAQ,gBAAgB;IAC5CC,OAAOK;IACPD,UAAU;AACZ;AAEA,+BAA+B;AAC/BM,WAAW;IACTvB,GAAGwB,aAAa;IAChBN,iBAAiBC,OAAO,CAACM,SAAS;IAClCP,iBAAiBE,OAAO,CAACK,SAAS;IAClCP,iBAAiBG,UAAU,CAACI,SAAS;IACrCP,iBAAiBI,KAAK,CAACG,SAAS;AAClC"}
@@ -1,116 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- import { isAdmin } from '../../../utils/access';
3
- describe('Access Control Utilities', ()=>{
4
- describe('isAdmin', ()=>{
5
- it('should return false for non-user collections', ()=>{
6
- const subscriber = {
7
- id: 'sub-123',
8
- email: 'test@example.com',
9
- collection: 'subscribers'
10
- };
11
- expect(isAdmin(subscriber)).toBe(false);
12
- });
13
- it('should return false for null or undefined user', ()=>{
14
- expect(isAdmin(null)).toBe(false);
15
- expect(isAdmin(undefined)).toBe(false);
16
- });
17
- it('should detect admin via roles array', ()=>{
18
- const user = {
19
- id: 'user-123',
20
- email: 'admin@example.com',
21
- collection: 'users',
22
- roles: [
23
- 'admin',
24
- 'editor'
25
- ]
26
- };
27
- expect(isAdmin(user)).toBe(true);
28
- });
29
- it('should detect admin via isAdmin boolean', ()=>{
30
- const user = {
31
- id: 'user-123',
32
- email: 'admin@example.com',
33
- collection: 'users',
34
- isAdmin: true
35
- };
36
- expect(isAdmin(user)).toBe(true);
37
- });
38
- it('should detect admin via role string', ()=>{
39
- const user = {
40
- id: 'user-123',
41
- email: 'admin@example.com',
42
- collection: 'users',
43
- role: 'admin'
44
- };
45
- expect(isAdmin(user)).toBe(true);
46
- });
47
- it('should detect admin via admin boolean', ()=>{
48
- const user = {
49
- id: 'user-123',
50
- email: 'admin@example.com',
51
- collection: 'users',
52
- admin: true
53
- };
54
- expect(isAdmin(user)).toBe(true);
55
- });
56
- it('should return false for non-admin users', ()=>{
57
- const user = {
58
- id: 'user-123',
59
- email: 'user@example.com',
60
- collection: 'users',
61
- roles: [
62
- 'editor',
63
- 'viewer'
64
- ],
65
- isAdmin: false,
66
- role: 'editor',
67
- admin: false
68
- };
69
- expect(isAdmin(user)).toBe(false);
70
- });
71
- it('should use custom isAdmin function when provided', ()=>{
72
- const config = {
73
- access: {
74
- isAdmin: (user)=>user?.customRole === 'super-admin'
75
- }
76
- };
77
- const regularAdmin = {
78
- id: 'user-123',
79
- email: 'admin@example.com',
80
- collection: 'users',
81
- roles: [
82
- 'admin'
83
- ]
84
- };
85
- expect(isAdmin(regularAdmin, config)).toBe(false);
86
- const customAdmin = {
87
- id: 'user-456',
88
- email: 'super@example.com',
89
- collection: 'users',
90
- customRole: 'super-admin'
91
- };
92
- expect(isAdmin(customAdmin, config)).toBe(true);
93
- });
94
- it('should handle edge cases gracefully', ()=>{
95
- // Empty user object
96
- const emptyUser = {
97
- collection: 'users'
98
- };
99
- expect(isAdmin(emptyUser)).toBe(false);
100
- // User with empty roles array
101
- const userWithEmptyRoles = {
102
- collection: 'users',
103
- roles: []
104
- };
105
- expect(isAdmin(userWithEmptyRoles)).toBe(false);
106
- // User with non-admin role string
107
- const userWithNonAdminRole = {
108
- collection: 'users',
109
- role: 'viewer'
110
- };
111
- expect(isAdmin(userWithNonAdminRole)).toBe(false);
112
- });
113
- });
114
- });
115
-
116
- //# sourceMappingURL=access.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../../../../src/__tests__/unit/utils/access.test.ts"],"sourcesContent":["import { describe, it, expect } from 'vitest'\nimport { isAdmin } from '../../../utils/access'\nimport type { NewsletterPluginConfig } from '../../../types'\n\ndescribe('Access Control Utilities', () => {\n describe('isAdmin', () => {\n it('should return false for non-user collections', () => {\n const subscriber = {\n id: 'sub-123',\n email: 'test@example.com',\n collection: 'subscribers',\n }\n expect(isAdmin(subscriber)).toBe(false)\n })\n\n it('should return false for null or undefined user', () => {\n expect(isAdmin(null)).toBe(false)\n expect(isAdmin(undefined)).toBe(false)\n })\n\n it('should detect admin via roles array', () => {\n const user = {\n id: 'user-123',\n email: 'admin@example.com',\n collection: 'users',\n roles: ['admin', 'editor'],\n }\n expect(isAdmin(user)).toBe(true)\n })\n\n it('should detect admin via isAdmin boolean', () => {\n const user = {\n id: 'user-123',\n email: 'admin@example.com',\n collection: 'users',\n isAdmin: true,\n }\n expect(isAdmin(user)).toBe(true)\n })\n\n it('should detect admin via role string', () => {\n const user = {\n id: 'user-123',\n email: 'admin@example.com',\n collection: 'users',\n role: 'admin',\n }\n expect(isAdmin(user)).toBe(true)\n })\n\n it('should detect admin via admin boolean', () => {\n const user = {\n id: 'user-123',\n email: 'admin@example.com',\n collection: 'users',\n admin: true,\n }\n expect(isAdmin(user)).toBe(true)\n })\n\n it('should return false for non-admin users', () => {\n const user = {\n id: 'user-123',\n email: 'user@example.com',\n collection: 'users',\n roles: ['editor', 'viewer'],\n isAdmin: false,\n role: 'editor',\n admin: false,\n }\n expect(isAdmin(user)).toBe(false)\n })\n\n it('should use custom isAdmin function when provided', () => {\n const config: NewsletterPluginConfig = {\n access: {\n isAdmin: (user) => user?.customRole === 'super-admin',\n },\n }\n\n const regularAdmin = {\n id: 'user-123',\n email: 'admin@example.com',\n collection: 'users',\n roles: ['admin'],\n }\n expect(isAdmin(regularAdmin, config)).toBe(false)\n\n const customAdmin = {\n id: 'user-456',\n email: 'super@example.com',\n collection: 'users',\n customRole: 'super-admin',\n }\n expect(isAdmin(customAdmin, config)).toBe(true)\n })\n\n it('should handle edge cases gracefully', () => {\n // Empty user object\n const emptyUser = { collection: 'users' }\n expect(isAdmin(emptyUser)).toBe(false)\n\n // User with empty roles array\n const userWithEmptyRoles = {\n collection: 'users',\n roles: [],\n }\n expect(isAdmin(userWithEmptyRoles)).toBe(false)\n\n // User with non-admin role string\n const userWithNonAdminRole = {\n collection: 'users',\n role: 'viewer',\n }\n expect(isAdmin(userWithNonAdminRole)).toBe(false)\n })\n })\n})"],"names":["describe","it","expect","isAdmin","subscriber","id","email","collection","toBe","undefined","user","roles","role","admin","config","access","customRole","regularAdmin","customAdmin","emptyUser","userWithEmptyRoles","userWithNonAdminRole"],"mappings":"AAAA,SAASA,QAAQ,EAAEC,EAAE,EAAEC,MAAM,QAAQ,SAAQ;AAC7C,SAASC,OAAO,QAAQ,wBAAuB;AAG/CH,SAAS,4BAA4B;IACnCA,SAAS,WAAW;QAClBC,GAAG,gDAAgD;YACjD,MAAMG,aAAa;gBACjBC,IAAI;gBACJC,OAAO;gBACPC,YAAY;YACd;YACAL,OAAOC,QAAQC,aAAaI,IAAI,CAAC;QACnC;QAEAP,GAAG,kDAAkD;YACnDC,OAAOC,QAAQ,OAAOK,IAAI,CAAC;YAC3BN,OAAOC,QAAQM,YAAYD,IAAI,CAAC;QAClC;QAEAP,GAAG,uCAAuC;YACxC,MAAMS,OAAO;gBACXL,IAAI;gBACJC,OAAO;gBACPC,YAAY;gBACZI,OAAO;oBAAC;oBAAS;iBAAS;YAC5B;YACAT,OAAOC,QAAQO,OAAOF,IAAI,CAAC;QAC7B;QAEAP,GAAG,2CAA2C;YAC5C,MAAMS,OAAO;gBACXL,IAAI;gBACJC,OAAO;gBACPC,YAAY;gBACZJ,SAAS;YACX;YACAD,OAAOC,QAAQO,OAAOF,IAAI,CAAC;QAC7B;QAEAP,GAAG,uCAAuC;YACxC,MAAMS,OAAO;gBACXL,IAAI;gBACJC,OAAO;gBACPC,YAAY;gBACZK,MAAM;YACR;YACAV,OAAOC,QAAQO,OAAOF,IAAI,CAAC;QAC7B;QAEAP,GAAG,yCAAyC;YAC1C,MAAMS,OAAO;gBACXL,IAAI;gBACJC,OAAO;gBACPC,YAAY;gBACZM,OAAO;YACT;YACAX,OAAOC,QAAQO,OAAOF,IAAI,CAAC;QAC7B;QAEAP,GAAG,2CAA2C;YAC5C,MAAMS,OAAO;gBACXL,IAAI;gBACJC,OAAO;gBACPC,YAAY;gBACZI,OAAO;oBAAC;oBAAU;iBAAS;gBAC3BR,SAAS;gBACTS,MAAM;gBACNC,OAAO;YACT;YACAX,OAAOC,QAAQO,OAAOF,IAAI,CAAC;QAC7B;QAEAP,GAAG,oDAAoD;YACrD,MAAMa,SAAiC;gBACrCC,QAAQ;oBACNZ,SAAS,CAACO,OAASA,MAAMM,eAAe;gBAC1C;YACF;YAEA,MAAMC,eAAe;gBACnBZ,IAAI;gBACJC,OAAO;gBACPC,YAAY;gBACZI,OAAO;oBAAC;iBAAQ;YAClB;YACAT,OAAOC,QAAQc,cAAcH,SAASN,IAAI,CAAC;YAE3C,MAAMU,cAAc;gBAClBb,IAAI;gBACJC,OAAO;gBACPC,YAAY;gBACZS,YAAY;YACd;YACAd,OAAOC,QAAQe,aAAaJ,SAASN,IAAI,CAAC;QAC5C;QAEAP,GAAG,uCAAuC;YACxC,oBAAoB;YACpB,MAAMkB,YAAY;gBAAEZ,YAAY;YAAQ;YACxCL,OAAOC,QAAQgB,YAAYX,IAAI,CAAC;YAEhC,8BAA8B;YAC9B,MAAMY,qBAAqB;gBACzBb,YAAY;gBACZI,OAAO,EAAE;YACX;YACAT,OAAOC,QAAQiB,qBAAqBZ,IAAI,CAAC;YAEzC,kCAAkC;YAClC,MAAMa,uBAAuB;gBAC3Bd,YAAY;gBACZK,MAAM;YACR;YACAV,OAAOC,QAAQkB,uBAAuBb,IAAI,CAAC;QAC7C;IACF;AACF"}
@@ -1,238 +0,0 @@
1
- import { describe, it, expect, beforeEach, vi } from 'vitest';
2
- import jwt from 'jsonwebtoken';
3
- import { generateMagicLinkToken, verifyMagicLinkToken, generateSessionToken, verifySessionToken, generateMagicLinkURL } from '../../../utils/jwt';
4
- // Mock jsonwebtoken
5
- vi.mock('jsonwebtoken', ()=>({
6
- default: {
7
- sign: vi.fn(),
8
- verify: vi.fn(),
9
- TokenExpiredError: class TokenExpiredError extends Error {
10
- constructor(message, expiredAt){
11
- super(message);
12
- this.name = 'TokenExpiredError';
13
- }
14
- },
15
- JsonWebTokenError: class JsonWebTokenError extends Error {
16
- constructor(message){
17
- super(message);
18
- this.name = 'JsonWebTokenError';
19
- }
20
- }
21
- }
22
- }));
23
- describe('JWT Utilities Security', ()=>{
24
- const mockSecret = 'test-secret-key';
25
- const mockConfig = {};
26
- beforeEach(()=>{
27
- process.env.JWT_SECRET = mockSecret;
28
- vi.clearAllMocks();
29
- });
30
- describe('generateMagicLinkToken', ()=>{
31
- it('should generate token with correct payload', ()=>{
32
- const mockToken = 'mock-magic-link-token';
33
- jwt.sign.mockReturnValue(mockToken);
34
- const token = generateMagicLinkToken('sub-123', 'test@example.com', mockConfig);
35
- expect(jwt.sign).toHaveBeenCalledWith({
36
- subscriberId: 'sub-123',
37
- email: 'test@example.com',
38
- type: 'magic-link'
39
- }, mockSecret, {
40
- expiresIn: '7d',
41
- issuer: 'payload-newsletter-plugin'
42
- });
43
- expect(token).toBe(mockToken);
44
- });
45
- it('should respect custom token expiration', ()=>{
46
- const customConfig = {
47
- auth: {
48
- tokenExpiration: '24h'
49
- }
50
- };
51
- generateMagicLinkToken('sub-123', 'test@example.com', customConfig);
52
- const options = jwt.sign.mock.calls[0][2];
53
- expect(options.expiresIn).toBe('24h');
54
- });
55
- it('should include correct token type', ()=>{
56
- generateMagicLinkToken('sub-123', 'test@example.com', mockConfig);
57
- const payload = jwt.sign.mock.calls[0][0];
58
- expect(payload.type).toBe('magic-link');
59
- expect(payload).not.toHaveProperty('action'); // No action field in actual implementation
60
- });
61
- });
62
- describe('verifyMagicLinkToken', ()=>{
63
- it('should verify and return valid token payload', ()=>{
64
- const mockPayload = {
65
- subscriberId: 'sub-123',
66
- email: 'test@example.com',
67
- type: 'magic-link'
68
- };
69
- jwt.verify.mockReturnValue(mockPayload);
70
- const payload = verifyMagicLinkToken('valid-token');
71
- expect(jwt.verify).toHaveBeenCalledWith('valid-token', mockSecret, {
72
- issuer: 'payload-newsletter-plugin'
73
- });
74
- expect(payload).toEqual(mockPayload);
75
- });
76
- it('should reject tokens with wrong type', ()=>{
77
- const wrongTypePayload = {
78
- subscriberId: 'sub-123',
79
- email: 'test@example.com',
80
- type: 'session'
81
- };
82
- jwt.verify.mockReturnValue(wrongTypePayload);
83
- expect(()=>verifyMagicLinkToken('wrong-type-token')).toThrow('Invalid token type');
84
- });
85
- it('should handle expired tokens', ()=>{
86
- ;
87
- jwt.verify.mockImplementation(()=>{
88
- const error = new Error('Token expired');
89
- error.name = 'TokenExpiredError';
90
- throw error;
91
- });
92
- expect(()=>verifyMagicLinkToken('expired-token')).toThrow('Magic link has expired');
93
- });
94
- it('should handle malformed tokens', ()=>{
95
- ;
96
- jwt.verify.mockImplementation(()=>{
97
- const error = new Error('Malformed token');
98
- error.name = 'JsonWebTokenError';
99
- throw error;
100
- });
101
- expect(()=>verifyMagicLinkToken('malformed-token')).toThrow('Invalid magic link token');
102
- });
103
- it('should validate required fields', ()=>{
104
- const incompletePayload = {
105
- subscriberId: 'sub-123',
106
- // Missing email
107
- type: 'magic-link'
108
- };
109
- jwt.verify.mockReturnValue(incompletePayload);
110
- // The actual implementation doesn't validate fields, just type
111
- const payload = verifyMagicLinkToken('incomplete-token');
112
- expect(payload).toEqual(incompletePayload);
113
- });
114
- });
115
- describe('generateSessionToken', ()=>{
116
- it('should generate session token with correct payload', ()=>{
117
- const mockToken = 'mock-session-token';
118
- jwt.sign.mockReturnValue(mockToken);
119
- const token = generateSessionToken('sub-123', 'test@example.com');
120
- expect(jwt.sign).toHaveBeenCalledWith({
121
- subscriberId: 'sub-123',
122
- email: 'test@example.com',
123
- type: 'session'
124
- }, mockSecret, {
125
- expiresIn: '30d',
126
- issuer: 'payload-newsletter-plugin'
127
- });
128
- expect(token).toBe(mockToken);
129
- });
130
- it('should set correct token type', ()=>{
131
- generateSessionToken('sub-123', 'test@example.com');
132
- const payload = jwt.sign.mock.calls[0][0];
133
- expect(payload.type).toBe('session');
134
- expect(payload.subscriberId).toBe('sub-123');
135
- });
136
- it('should set longer expiration for sessions', ()=>{
137
- generateSessionToken('sub-123', 'test@example.com');
138
- const options = jwt.sign.mock.calls[0][2];
139
- expect(options.expiresIn).toBe('30d');
140
- });
141
- });
142
- describe('verifySessionToken', ()=>{
143
- it('should verify and return valid session payload', ()=>{
144
- const mockPayload = {
145
- subscriberId: 'sub-123',
146
- email: 'test@example.com',
147
- type: 'session'
148
- };
149
- jwt.verify.mockReturnValue(mockPayload);
150
- const payload = verifySessionToken('valid-session');
151
- expect(jwt.verify).toHaveBeenCalledWith('valid-session', mockSecret, {
152
- issuer: 'payload-newsletter-plugin'
153
- });
154
- expect(payload).toEqual(mockPayload);
155
- });
156
- it('should reject non-session tokens', ()=>{
157
- const magicLinkPayload = {
158
- subscriberId: 'sub-123',
159
- email: 'test@example.com',
160
- type: 'magic-link'
161
- };
162
- jwt.verify.mockReturnValue(magicLinkPayload);
163
- expect(()=>verifySessionToken('magic-link-token')).toThrow('Invalid token type');
164
- });
165
- it('should handle expired session tokens', ()=>{
166
- ;
167
- jwt.verify.mockImplementation(()=>{
168
- const error = new Error('Token expired');
169
- error.name = 'TokenExpiredError';
170
- throw error;
171
- });
172
- expect(()=>verifySessionToken('expired-token')).toThrow('Session has expired');
173
- });
174
- });
175
- describe('Environment Configuration', ()=>{
176
- it('should use fallback secret if JWT_SECRET is not set', ()=>{
177
- delete process.env.JWT_SECRET;
178
- delete process.env.PAYLOAD_SECRET;
179
- // Should not throw, uses development placeholder
180
- expect(()=>generateMagicLinkToken('sub-123', 'test@example.com', mockConfig)).not.toThrow();
181
- // Check that it uses the placeholder secret
182
- expect(jwt.sign).toHaveBeenCalledWith(expect.any(Object), 'INSECURE_DEVELOPMENT_SECRET_PLEASE_SET_JWT_SECRET', expect.any(Object));
183
- });
184
- it('should use PAYLOAD_SECRET as fallback', ()=>{
185
- delete process.env.JWT_SECRET;
186
- process.env.PAYLOAD_SECRET = 'payload-secret';
187
- generateMagicLinkToken('sub-123', 'test@example.com', mockConfig);
188
- expect(jwt.sign).toHaveBeenCalledWith(expect.any(Object), 'payload-secret', expect.any(Object));
189
- });
190
- it('should not expose secret in error messages', ()=>{
191
- process.env.JWT_SECRET = 'super-secret-key-12345';
192
- jwt.verify.mockImplementation(()=>{
193
- throw new Error('invalid signature');
194
- });
195
- try {
196
- verifyMagicLinkToken('bad-token');
197
- } catch (error) {
198
- expect(error.message).not.toContain('super-secret-key-12345');
199
- }
200
- });
201
- });
202
- describe('Token Security', ()=>{
203
- it('should not allow algorithm switching attacks', ()=>{
204
- // Ensure tokens are verified with the expected algorithm
205
- const maliciousToken = 'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzdWJzY3JpYmVySWQiOiJzdWItMTIzIn0.';
206
- jwt.verify.mockImplementation(()=>{
207
- const error = new Error('invalid algorithm');
208
- error.name = 'JsonWebTokenError';
209
- throw error;
210
- });
211
- expect(()=>verifyMagicLinkToken(maliciousToken)).toThrow('Invalid magic link token');
212
- });
213
- });
214
- describe('generateMagicLinkURL', ()=>{
215
- it('should generate valid magic link URLs', ()=>{
216
- const token = 'test-token-123';
217
- const baseURL = 'https://example.com';
218
- const url = generateMagicLinkURL(token, baseURL, mockConfig);
219
- expect(url).toBe('https://example.com/newsletter/verify?token=test-token-123');
220
- });
221
- it('should use custom path from config', ()=>{
222
- const customConfig = {
223
- auth: {
224
- magicLinkPath: '/auth/magic'
225
- }
226
- };
227
- const url = generateMagicLinkURL('token-123', 'https://example.com', customConfig);
228
- expect(url).toBe('https://example.com/auth/magic?token=token-123');
229
- });
230
- it('should properly encode token in URL', ()=>{
231
- const tokenWithSpecialChars = 'token+with/special=chars';
232
- const url = generateMagicLinkURL(tokenWithSpecialChars, 'https://example.com', mockConfig);
233
- expect(url).toContain(encodeURIComponent(tokenWithSpecialChars));
234
- });
235
- });
236
- });
237
-
238
- //# sourceMappingURL=jwt.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../../../../src/__tests__/unit/utils/jwt.test.ts"],"sourcesContent":["import { describe, it, expect, beforeEach, vi } from 'vitest'\nimport jwt from 'jsonwebtoken'\nimport {\n generateMagicLinkToken,\n verifyMagicLinkToken,\n generateSessionToken,\n verifySessionToken,\n generateMagicLinkURL,\n} from '../../../utils/jwt'\nimport type { NewsletterPluginConfig } from '../../../types'\n\n// Mock jsonwebtoken\nvi.mock('jsonwebtoken', () => ({\n default: {\n sign: vi.fn(),\n verify: vi.fn(),\n TokenExpiredError: class TokenExpiredError extends Error {\n constructor(message: string, expiredAt: Date) {\n super(message)\n this.name = 'TokenExpiredError'\n }\n },\n JsonWebTokenError: class JsonWebTokenError extends Error {\n constructor(message: string) {\n super(message)\n this.name = 'JsonWebTokenError'\n }\n },\n },\n}))\n\ndescribe('JWT Utilities Security', () => {\n const mockSecret = 'test-secret-key'\n const mockConfig: NewsletterPluginConfig = {}\n \n beforeEach(() => {\n process.env.JWT_SECRET = mockSecret\n vi.clearAllMocks()\n })\n\n describe('generateMagicLinkToken', () => {\n it('should generate token with correct payload', () => {\n const mockToken = 'mock-magic-link-token'\n ;(jwt.sign as any).mockReturnValue(mockToken)\n\n const token = generateMagicLinkToken('sub-123', 'test@example.com', mockConfig)\n\n expect(jwt.sign).toHaveBeenCalledWith(\n {\n subscriberId: 'sub-123',\n email: 'test@example.com',\n type: 'magic-link',\n },\n mockSecret,\n {\n expiresIn: '7d',\n issuer: 'payload-newsletter-plugin',\n }\n )\n expect(token).toBe(mockToken)\n })\n\n it('should respect custom token expiration', () => {\n const customConfig: NewsletterPluginConfig = {\n auth: {\n tokenExpiration: '24h',\n },\n }\n \n generateMagicLinkToken('sub-123', 'test@example.com', customConfig)\n\n const options = (jwt.sign as any).mock.calls[0][2]\n expect(options.expiresIn).toBe('24h')\n })\n\n it('should include correct token type', () => {\n generateMagicLinkToken('sub-123', 'test@example.com', mockConfig)\n\n const payload = (jwt.sign as any).mock.calls[0][0]\n expect(payload.type).toBe('magic-link')\n expect(payload).not.toHaveProperty('action') // No action field in actual implementation\n })\n })\n\n describe('verifyMagicLinkToken', () => {\n it('should verify and return valid token payload', () => {\n const mockPayload = {\n subscriberId: 'sub-123',\n email: 'test@example.com',\n type: 'magic-link',\n }\n ;(jwt.verify as any).mockReturnValue(mockPayload)\n\n const payload = verifyMagicLinkToken('valid-token')\n\n expect(jwt.verify).toHaveBeenCalledWith('valid-token', mockSecret, {\n issuer: 'payload-newsletter-plugin',\n })\n expect(payload).toEqual(mockPayload)\n })\n\n it('should reject tokens with wrong type', () => {\n const wrongTypePayload = {\n subscriberId: 'sub-123',\n email: 'test@example.com',\n type: 'session', // Wrong type\n }\n ;(jwt.verify as any).mockReturnValue(wrongTypePayload)\n\n expect(() => verifyMagicLinkToken('wrong-type-token')).toThrow('Invalid token type')\n })\n\n it('should handle expired tokens', () => {\n ;(jwt.verify as any).mockImplementation(() => {\n const error = new Error('Token expired')\n error.name = 'TokenExpiredError'\n throw error\n })\n\n expect(() => verifyMagicLinkToken('expired-token')).toThrow('Magic link has expired')\n })\n\n it('should handle malformed tokens', () => {\n ;(jwt.verify as any).mockImplementation(() => {\n const error = new Error('Malformed token')\n error.name = 'JsonWebTokenError'\n throw error\n })\n\n expect(() => verifyMagicLinkToken('malformed-token')).toThrow('Invalid magic link token')\n })\n\n it('should validate required fields', () => {\n const incompletePayload = {\n subscriberId: 'sub-123',\n // Missing email\n type: 'magic-link',\n }\n ;(jwt.verify as any).mockReturnValue(incompletePayload)\n\n // The actual implementation doesn't validate fields, just type\n const payload = verifyMagicLinkToken('incomplete-token')\n expect(payload).toEqual(incompletePayload)\n })\n })\n\n describe('generateSessionToken', () => {\n it('should generate session token with correct payload', () => {\n const mockToken = 'mock-session-token'\n ;(jwt.sign as any).mockReturnValue(mockToken)\n\n const token = generateSessionToken('sub-123', 'test@example.com')\n\n expect(jwt.sign).toHaveBeenCalledWith(\n {\n subscriberId: 'sub-123',\n email: 'test@example.com',\n type: 'session',\n },\n mockSecret,\n {\n expiresIn: '30d',\n issuer: 'payload-newsletter-plugin',\n }\n )\n expect(token).toBe(mockToken)\n })\n\n it('should set correct token type', () => {\n generateSessionToken('sub-123', 'test@example.com')\n\n const payload = (jwt.sign as any).mock.calls[0][0]\n expect(payload.type).toBe('session')\n expect(payload.subscriberId).toBe('sub-123')\n })\n\n it('should set longer expiration for sessions', () => {\n generateSessionToken('sub-123', 'test@example.com')\n\n const options = (jwt.sign as any).mock.calls[0][2]\n expect(options.expiresIn).toBe('30d')\n })\n })\n\n describe('verifySessionToken', () => {\n it('should verify and return valid session payload', () => {\n const mockPayload = {\n subscriberId: 'sub-123',\n email: 'test@example.com',\n type: 'session',\n }\n ;(jwt.verify as any).mockReturnValue(mockPayload)\n\n const payload = verifySessionToken('valid-session')\n\n expect(jwt.verify).toHaveBeenCalledWith('valid-session', mockSecret, {\n issuer: 'payload-newsletter-plugin',\n })\n expect(payload).toEqual(mockPayload)\n })\n\n it('should reject non-session tokens', () => {\n const magicLinkPayload = {\n subscriberId: 'sub-123',\n email: 'test@example.com',\n type: 'magic-link', // Wrong type for session\n }\n ;(jwt.verify as any).mockReturnValue(magicLinkPayload)\n\n expect(() => verifySessionToken('magic-link-token')).toThrow('Invalid token type')\n })\n\n it('should handle expired session tokens', () => {\n ;(jwt.verify as any).mockImplementation(() => {\n const error = new Error('Token expired')\n error.name = 'TokenExpiredError'\n throw error\n })\n\n expect(() => verifySessionToken('expired-token')).toThrow('Session has expired')\n })\n })\n\n describe('Environment Configuration', () => {\n it('should use fallback secret if JWT_SECRET is not set', () => {\n delete process.env.JWT_SECRET\n delete process.env.PAYLOAD_SECRET\n\n // Should not throw, uses development placeholder\n expect(() => generateMagicLinkToken('sub-123', 'test@example.com', mockConfig)).not.toThrow()\n \n // Check that it uses the placeholder secret\n expect(jwt.sign).toHaveBeenCalledWith(\n expect.any(Object),\n 'INSECURE_DEVELOPMENT_SECRET_PLEASE_SET_JWT_SECRET',\n expect.any(Object)\n )\n })\n\n it('should use PAYLOAD_SECRET as fallback', () => {\n delete process.env.JWT_SECRET\n process.env.PAYLOAD_SECRET = 'payload-secret'\n\n generateMagicLinkToken('sub-123', 'test@example.com', mockConfig)\n\n expect(jwt.sign).toHaveBeenCalledWith(\n expect.any(Object),\n 'payload-secret',\n expect.any(Object)\n )\n })\n\n it('should not expose secret in error messages', () => {\n process.env.JWT_SECRET = 'super-secret-key-12345'\n ;(jwt.verify as any).mockImplementation(() => {\n throw new Error('invalid signature')\n })\n\n try {\n verifyMagicLinkToken('bad-token')\n } catch (error: any) {\n expect(error.message).not.toContain('super-secret-key-12345')\n }\n })\n })\n\n describe('Token Security', () => {\n it('should not allow algorithm switching attacks', () => {\n // Ensure tokens are verified with the expected algorithm\n const maliciousToken = 'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzdWJzY3JpYmVySWQiOiJzdWItMTIzIn0.'\n \n ;(jwt.verify as any).mockImplementation(() => {\n const error = new Error('invalid algorithm')\n error.name = 'JsonWebTokenError'\n throw error\n })\n\n expect(() => verifyMagicLinkToken(maliciousToken)).toThrow('Invalid magic link token')\n })\n })\n\n describe('generateMagicLinkURL', () => {\n it('should generate valid magic link URLs', () => {\n const token = 'test-token-123'\n const baseURL = 'https://example.com'\n \n const url = generateMagicLinkURL(token, baseURL, mockConfig)\n \n expect(url).toBe('https://example.com/newsletter/verify?token=test-token-123')\n })\n\n it('should use custom path from config', () => {\n const customConfig: NewsletterPluginConfig = {\n auth: {\n magicLinkPath: '/auth/magic',\n },\n }\n \n const url = generateMagicLinkURL('token-123', 'https://example.com', customConfig)\n \n expect(url).toBe('https://example.com/auth/magic?token=token-123')\n })\n\n it('should properly encode token in URL', () => {\n const tokenWithSpecialChars = 'token+with/special=chars'\n \n const url = generateMagicLinkURL(tokenWithSpecialChars, 'https://example.com', mockConfig)\n \n expect(url).toContain(encodeURIComponent(tokenWithSpecialChars))\n })\n })\n})"],"names":["describe","it","expect","beforeEach","vi","jwt","generateMagicLinkToken","verifyMagicLinkToken","generateSessionToken","verifySessionToken","generateMagicLinkURL","mock","default","sign","fn","verify","TokenExpiredError","Error","message","expiredAt","name","JsonWebTokenError","mockSecret","mockConfig","process","env","JWT_SECRET","clearAllMocks","mockToken","mockReturnValue","token","toHaveBeenCalledWith","subscriberId","email","type","expiresIn","issuer","toBe","customConfig","auth","tokenExpiration","options","calls","payload","not","toHaveProperty","mockPayload","toEqual","wrongTypePayload","toThrow","mockImplementation","error","incompletePayload","magicLinkPayload","PAYLOAD_SECRET","any","Object","toContain","maliciousToken","baseURL","url","magicLinkPath","tokenWithSpecialChars","encodeURIComponent"],"mappings":"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"}