odac 0.9.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 (213) hide show
  1. package/.editorconfig +21 -0
  2. package/.github/workflows/auto-pr-description.yml +49 -0
  3. package/.github/workflows/release.yml +32 -0
  4. package/.github/workflows/test-coverage.yml +58 -0
  5. package/.husky/pre-commit +2 -0
  6. package/.kiro/steering/code-style.md +56 -0
  7. package/.kiro/steering/product.md +20 -0
  8. package/.kiro/steering/structure.md +77 -0
  9. package/.kiro/steering/tech.md +87 -0
  10. package/.prettierrc +10 -0
  11. package/.releaserc.js +134 -0
  12. package/AGENTS.md +84 -0
  13. package/CHANGELOG.md +181 -0
  14. package/CODE_OF_CONDUCT.md +83 -0
  15. package/CONTRIBUTING.md +63 -0
  16. package/LICENSE +661 -0
  17. package/README.md +57 -0
  18. package/SECURITY.md +26 -0
  19. package/bin/candy +10 -0
  20. package/bin/candypack +10 -0
  21. package/cli/index.js +3 -0
  22. package/cli/src/Cli.js +348 -0
  23. package/cli/src/Connector.js +93 -0
  24. package/cli/src/Monitor.js +416 -0
  25. package/core/Candy.js +87 -0
  26. package/core/Commands.js +239 -0
  27. package/core/Config.js +1094 -0
  28. package/core/Lang.js +52 -0
  29. package/core/Log.js +43 -0
  30. package/core/Process.js +26 -0
  31. package/docs/backend/01-overview/01-whats-in-the-candy-box.md +9 -0
  32. package/docs/backend/01-overview/02-super-handy-helper-functions.md +9 -0
  33. package/docs/backend/01-overview/03-development-server.md +79 -0
  34. package/docs/backend/02-structure/01-typical-project-layout.md +39 -0
  35. package/docs/backend/03-config/00-configuration-overview.md +214 -0
  36. package/docs/backend/03-config/01-database-connection.md +60 -0
  37. package/docs/backend/03-config/02-static-route-mapping-optional.md +20 -0
  38. package/docs/backend/03-config/03-request-timeout.md +11 -0
  39. package/docs/backend/03-config/04-environment-variables.md +227 -0
  40. package/docs/backend/03-config/05-early-hints.md +352 -0
  41. package/docs/backend/04-routing/01-basic-page-routes.md +28 -0
  42. package/docs/backend/04-routing/02-controller-less-view-routes.md +43 -0
  43. package/docs/backend/04-routing/03-api-and-data-routes.md +20 -0
  44. package/docs/backend/04-routing/04-authentication-aware-routes.md +48 -0
  45. package/docs/backend/04-routing/05-advanced-routing.md +14 -0
  46. package/docs/backend/04-routing/06-error-pages.md +101 -0
  47. package/docs/backend/04-routing/07-cron-jobs.md +149 -0
  48. package/docs/backend/05-controllers/01-how-to-build-a-controller.md +17 -0
  49. package/docs/backend/05-controllers/02-your-trusty-candy-assistant.md +20 -0
  50. package/docs/backend/05-controllers/03-controller-classes.md +93 -0
  51. package/docs/backend/05-forms/01-custom-forms.md +395 -0
  52. package/docs/backend/05-forms/02-automatic-database-insert.md +297 -0
  53. package/docs/backend/06-request-and-response/01-the-request-object-what-is-the-user-asking-for.md +96 -0
  54. package/docs/backend/06-request-and-response/02-sending-a-response-replying-to-the-user.md +40 -0
  55. package/docs/backend/07-views/01-the-view-directory.md +73 -0
  56. package/docs/backend/07-views/02-rendering-a-view.md +179 -0
  57. package/docs/backend/07-views/03-template-syntax.md +181 -0
  58. package/docs/backend/07-views/03-variables.md +328 -0
  59. package/docs/backend/07-views/04-request-data.md +231 -0
  60. package/docs/backend/07-views/05-conditionals.md +290 -0
  61. package/docs/backend/07-views/06-loops.md +353 -0
  62. package/docs/backend/07-views/07-translations.md +358 -0
  63. package/docs/backend/07-views/08-backend-javascript.md +398 -0
  64. package/docs/backend/07-views/09-comments.md +297 -0
  65. package/docs/backend/08-database/01-database-connection.md +99 -0
  66. package/docs/backend/08-database/02-using-mysql.md +322 -0
  67. package/docs/backend/09-validation/01-the-validator-service.md +424 -0
  68. package/docs/backend/10-authentication/01-user-logins-with-authjs.md +53 -0
  69. package/docs/backend/10-authentication/02-foiling-villains-with-csrf-protection.md +55 -0
  70. package/docs/backend/10-authentication/03-register.md +134 -0
  71. package/docs/backend/10-authentication/04-candy-register-forms.md +676 -0
  72. package/docs/backend/10-authentication/05-session-management.md +159 -0
  73. package/docs/backend/10-authentication/06-candy-login-forms.md +596 -0
  74. package/docs/backend/11-mail/01-the-mail-service.md +42 -0
  75. package/docs/backend/12-streaming/01-streaming-overview.md +300 -0
  76. package/docs/backend/13-utilities/01-candy-var.md +504 -0
  77. package/docs/frontend/01-overview/01-introduction.md +146 -0
  78. package/docs/frontend/02-ajax-navigation/01-quick-start.md +608 -0
  79. package/docs/frontend/02-ajax-navigation/02-configuration.md +370 -0
  80. package/docs/frontend/02-ajax-navigation/03-advanced-usage.md +519 -0
  81. package/docs/frontend/03-forms/01-form-handling.md +420 -0
  82. package/docs/frontend/04-api-requests/01-get-post.md +443 -0
  83. package/docs/frontend/05-streaming/01-client-streaming.md +163 -0
  84. package/docs/index.json +452 -0
  85. package/docs/server/01-installation/01-quick-install.md +19 -0
  86. package/docs/server/01-installation/02-manual-installation-via-npm.md +9 -0
  87. package/docs/server/02-get-started/01-core-concepts.md +7 -0
  88. package/docs/server/02-get-started/02-basic-commands.md +57 -0
  89. package/docs/server/02-get-started/03-cli-reference.md +276 -0
  90. package/docs/server/02-get-started/04-cli-quick-reference.md +102 -0
  91. package/docs/server/03-service/01-start-a-new-service.md +57 -0
  92. package/docs/server/03-service/02-delete-a-service.md +48 -0
  93. package/docs/server/04-web/01-create-a-website.md +36 -0
  94. package/docs/server/04-web/02-list-websites.md +9 -0
  95. package/docs/server/04-web/03-delete-a-website.md +29 -0
  96. package/docs/server/05-subdomain/01-create-a-subdomain.md +32 -0
  97. package/docs/server/05-subdomain/02-list-subdomains.md +33 -0
  98. package/docs/server/05-subdomain/03-delete-a-subdomain.md +41 -0
  99. package/docs/server/06-ssl/01-renew-an-ssl-certificate.md +34 -0
  100. package/docs/server/07-mail/01-create-a-mail-account.md +23 -0
  101. package/docs/server/07-mail/02-delete-a-mail-account.md +20 -0
  102. package/docs/server/07-mail/03-list-mail-accounts.md +20 -0
  103. package/docs/server/07-mail/04-change-account-password.md +23 -0
  104. package/eslint.config.mjs +120 -0
  105. package/framework/index.js +4 -0
  106. package/framework/src/Auth.js +309 -0
  107. package/framework/src/Candy.js +81 -0
  108. package/framework/src/Config.js +79 -0
  109. package/framework/src/Env.js +60 -0
  110. package/framework/src/Lang.js +57 -0
  111. package/framework/src/Mail.js +83 -0
  112. package/framework/src/Mysql.js +575 -0
  113. package/framework/src/Request.js +301 -0
  114. package/framework/src/Route/Cron.js +128 -0
  115. package/framework/src/Route/Internal.js +439 -0
  116. package/framework/src/Route.js +455 -0
  117. package/framework/src/Server.js +15 -0
  118. package/framework/src/Stream.js +163 -0
  119. package/framework/src/Token.js +37 -0
  120. package/framework/src/Validator.js +271 -0
  121. package/framework/src/Var.js +211 -0
  122. package/framework/src/View/EarlyHints.js +190 -0
  123. package/framework/src/View/Form.js +600 -0
  124. package/framework/src/View.js +513 -0
  125. package/framework/web/candy.js +838 -0
  126. package/jest.config.js +22 -0
  127. package/locale/de-DE.json +80 -0
  128. package/locale/en-US.json +79 -0
  129. package/locale/es-ES.json +80 -0
  130. package/locale/fr-FR.json +80 -0
  131. package/locale/pt-BR.json +80 -0
  132. package/locale/ru-RU.json +80 -0
  133. package/locale/tr-TR.json +85 -0
  134. package/locale/zh-CN.json +80 -0
  135. package/package.json +86 -0
  136. package/server/index.js +5 -0
  137. package/server/src/Api.js +88 -0
  138. package/server/src/DNS.js +940 -0
  139. package/server/src/Hub.js +535 -0
  140. package/server/src/Mail.js +571 -0
  141. package/server/src/SSL.js +180 -0
  142. package/server/src/Server.js +27 -0
  143. package/server/src/Service.js +248 -0
  144. package/server/src/Subdomain.js +64 -0
  145. package/server/src/Web/Firewall.js +170 -0
  146. package/server/src/Web/Proxy.js +134 -0
  147. package/server/src/Web.js +451 -0
  148. package/server/src/mail/imap.js +1091 -0
  149. package/server/src/mail/server.js +32 -0
  150. package/server/src/mail/smtp.js +786 -0
  151. package/test/cli/Cli.test.js +36 -0
  152. package/test/core/Candy.test.js +234 -0
  153. package/test/core/Commands.test.js +538 -0
  154. package/test/core/Config.test.js +1435 -0
  155. package/test/core/Lang.test.js +250 -0
  156. package/test/core/Process.test.js +156 -0
  157. package/test/framework/Route.test.js +239 -0
  158. package/test/framework/View/EarlyHints.test.js +282 -0
  159. package/test/scripts/check-coverage.js +132 -0
  160. package/test/server/Api.test.js +647 -0
  161. package/test/server/Client.test.js +338 -0
  162. package/test/server/DNS.test.js +2050 -0
  163. package/test/server/DNS.test.js.bak +2084 -0
  164. package/test/server/Log.test.js +73 -0
  165. package/test/server/Mail.account.test_.js +460 -0
  166. package/test/server/Mail.init.test_.js +411 -0
  167. package/test/server/Mail.test_.js +1340 -0
  168. package/test/server/SSL.test_.js +1491 -0
  169. package/test/server/Server.test.js +765 -0
  170. package/test/server/Service.test_.js +1127 -0
  171. package/test/server/Subdomain.test.js +440 -0
  172. package/test/server/Web/Firewall.test.js +175 -0
  173. package/test/server/Web.test_.js +1562 -0
  174. package/test/server/__mocks__/acme-client.js +17 -0
  175. package/test/server/__mocks__/bcrypt.js +50 -0
  176. package/test/server/__mocks__/child_process.js +389 -0
  177. package/test/server/__mocks__/crypto.js +432 -0
  178. package/test/server/__mocks__/fs.js +450 -0
  179. package/test/server/__mocks__/globalCandy.js +227 -0
  180. package/test/server/__mocks__/http-proxy.js +105 -0
  181. package/test/server/__mocks__/http.js +575 -0
  182. package/test/server/__mocks__/https.js +272 -0
  183. package/test/server/__mocks__/index.js +249 -0
  184. package/test/server/__mocks__/mail/server.js +100 -0
  185. package/test/server/__mocks__/mail/smtp.js +31 -0
  186. package/test/server/__mocks__/mailparser.js +81 -0
  187. package/test/server/__mocks__/net.js +369 -0
  188. package/test/server/__mocks__/node-forge.js +328 -0
  189. package/test/server/__mocks__/os.js +320 -0
  190. package/test/server/__mocks__/path.js +291 -0
  191. package/test/server/__mocks__/selfsigned.js +8 -0
  192. package/test/server/__mocks__/server/src/mail/server.js +100 -0
  193. package/test/server/__mocks__/server/src/mail/smtp.js +31 -0
  194. package/test/server/__mocks__/smtp-server.js +106 -0
  195. package/test/server/__mocks__/sqlite3.js +394 -0
  196. package/test/server/__mocks__/testFactories.js +299 -0
  197. package/test/server/__mocks__/testHelpers.js +363 -0
  198. package/test/server/__mocks__/tls.js +229 -0
  199. package/watchdog/index.js +3 -0
  200. package/watchdog/src/Watchdog.js +156 -0
  201. package/web/config.json +5 -0
  202. package/web/controller/page/about.js +27 -0
  203. package/web/controller/page/index.js +34 -0
  204. package/web/package.json +18 -0
  205. package/web/public/assets/css/style.css +1835 -0
  206. package/web/public/assets/js/app.js +96 -0
  207. package/web/route/www.js +19 -0
  208. package/web/skeleton/main.html +22 -0
  209. package/web/view/content/about.html +65 -0
  210. package/web/view/content/home.html +205 -0
  211. package/web/view/footer/main.html +11 -0
  212. package/web/view/head/main.html +5 -0
  213. package/web/view/header/main.html +14 -0
@@ -0,0 +1,432 @@
1
+ /**
2
+ * Mock implementation of the crypto module for server tests
3
+ * Provides comprehensive mocking of cryptographic operations
4
+ */
5
+
6
+ const {createMockEventEmitter} = require('./testHelpers')
7
+
8
+ // Mock random data generation
9
+ const generateMockRandomBytes = size => {
10
+ const bytes = Buffer.alloc(size)
11
+ for (let i = 0; i < size; i++) {
12
+ bytes[i] = Math.floor(Math.random() * 256)
13
+ }
14
+ return bytes
15
+ }
16
+
17
+ // Mock hash generation
18
+ const generateMockHash = (algorithm, data) => {
19
+ // Simple mock hash based on algorithm and data
20
+ const hashMap = {
21
+ md5: '5d41402abc4b2a76b9719d911017c592',
22
+ sha1: 'aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d',
23
+ sha256: '2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae',
24
+ sha512:
25
+ 'f7fbba6e0636f890e56fbbf3283e524c6fa3204ae298382d624741d0dc6638326e282c41be5e4254d8820772c5518a2c5a8c0c7f7eda19594a7eb539453e1ed7'
26
+ }
27
+
28
+ const baseHash = hashMap[algorithm] || hashMap['sha256']
29
+
30
+ // Modify hash based on data to make it somewhat unique
31
+ if (data) {
32
+ const dataStr = Buffer.isBuffer(data) ? data.toString() : String(data)
33
+ const modifier = dataStr.length % 16
34
+ return baseHash.substring(modifier) + baseHash.substring(0, modifier)
35
+ }
36
+
37
+ return baseHash
38
+ }
39
+
40
+ const createMockHash = algorithm => {
41
+ const hash = createMockEventEmitter()
42
+ let data = ''
43
+
44
+ Object.assign(hash, {
45
+ update: jest.fn((chunk, encoding = 'utf8') => {
46
+ data += Buffer.isBuffer(chunk) ? chunk.toString() : String(chunk)
47
+ return hash
48
+ }),
49
+
50
+ digest: jest.fn(encoding => {
51
+ const hashValue = generateMockHash(algorithm, data)
52
+
53
+ if (encoding === 'hex') {
54
+ return hashValue
55
+ } else if (encoding === 'base64') {
56
+ return Buffer.from(hashValue, 'hex').toString('base64')
57
+ } else if (encoding) {
58
+ return Buffer.from(hashValue, 'hex').toString(encoding)
59
+ } else {
60
+ return Buffer.from(hashValue, 'hex')
61
+ }
62
+ })
63
+ })
64
+
65
+ return hash
66
+ }
67
+
68
+ const createMockHmac = (algorithm, key) => {
69
+ const hmac = createMockEventEmitter()
70
+ let data = ''
71
+
72
+ Object.assign(hmac, {
73
+ update: jest.fn((chunk, encoding = 'utf8') => {
74
+ data += Buffer.isBuffer(chunk) ? chunk.toString() : String(chunk)
75
+ return hmac
76
+ }),
77
+
78
+ digest: jest.fn(encoding => {
79
+ // Create a mock HMAC by combining key and data
80
+ const keyStr = Buffer.isBuffer(key) ? key.toString() : String(key)
81
+ const combinedData = keyStr + data
82
+ const hashValue = generateMockHash(algorithm, combinedData)
83
+
84
+ if (encoding === 'hex') {
85
+ return hashValue
86
+ } else if (encoding === 'base64') {
87
+ return Buffer.from(hashValue, 'hex').toString('base64')
88
+ } else if (encoding) {
89
+ return Buffer.from(hashValue, 'hex').toString(encoding)
90
+ } else {
91
+ return Buffer.from(hashValue, 'hex')
92
+ }
93
+ })
94
+ })
95
+
96
+ return hmac
97
+ }
98
+
99
+ const createMockCipher = (algorithm, password) => {
100
+ const cipher = createMockEventEmitter()
101
+ let data = Buffer.alloc(0)
102
+
103
+ Object.assign(cipher, {
104
+ update: jest.fn((chunk, inputEncoding, outputEncoding) => {
105
+ const input = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk, inputEncoding || 'utf8')
106
+ data = Buffer.concat([data, input])
107
+
108
+ // Mock encryption by XORing with a simple key
109
+ const encrypted = Buffer.alloc(input.length)
110
+ const key = Buffer.from(password || 'mockkey')
111
+ for (let i = 0; i < input.length; i++) {
112
+ encrypted[i] = input[i] ^ key[i % key.length]
113
+ }
114
+
115
+ return outputEncoding ? encrypted.toString(outputEncoding) : encrypted
116
+ }),
117
+
118
+ final: jest.fn(outputEncoding => {
119
+ const final = Buffer.from('mockfinal')
120
+ return outputEncoding ? final.toString(outputEncoding) : final
121
+ }),
122
+
123
+ setAutoPadding: jest.fn((autoPadding = true) => {
124
+ cipher._autoPadding = autoPadding
125
+ return cipher
126
+ })
127
+ })
128
+
129
+ return cipher
130
+ }
131
+
132
+ const createMockDecipher = (algorithm, password) => {
133
+ const decipher = createMockEventEmitter()
134
+ let data = Buffer.alloc(0)
135
+
136
+ Object.assign(decipher, {
137
+ update: jest.fn((chunk, inputEncoding, outputEncoding) => {
138
+ const input = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk, inputEncoding || 'hex')
139
+ data = Buffer.concat([data, input])
140
+
141
+ // Mock decryption by XORing with the same key (reverse of encryption)
142
+ const decrypted = Buffer.alloc(input.length)
143
+ const key = Buffer.from(password || 'mockkey')
144
+ for (let i = 0; i < input.length; i++) {
145
+ decrypted[i] = input[i] ^ key[i % key.length]
146
+ }
147
+
148
+ return outputEncoding ? decrypted.toString(outputEncoding) : decrypted
149
+ }),
150
+
151
+ final: jest.fn(outputEncoding => {
152
+ const final = Buffer.from('mockfinal')
153
+ return outputEncoding ? final.toString(outputEncoding) : final
154
+ }),
155
+
156
+ setAutoPadding: jest.fn((autoPadding = true) => {
157
+ decipher._autoPadding = autoPadding
158
+ return decipher
159
+ })
160
+ })
161
+
162
+ return decipher
163
+ }
164
+
165
+ const crypto = {
166
+ // Hash functions
167
+ createHash: jest.fn(algorithm => {
168
+ return createMockHash(algorithm)
169
+ }),
170
+
171
+ createHmac: jest.fn((algorithm, key) => {
172
+ return createMockHmac(algorithm, key)
173
+ }),
174
+
175
+ // Cipher functions
176
+ createCipher: jest.fn((algorithm, password) => {
177
+ return createMockCipher(algorithm, password)
178
+ }),
179
+
180
+ createDecipher: jest.fn((algorithm, password) => {
181
+ return createMockDecipher(algorithm, password)
182
+ }),
183
+
184
+ createCipheriv: jest.fn((algorithm, key, iv) => {
185
+ return createMockCipher(algorithm, key)
186
+ }),
187
+
188
+ createDecipheriv: jest.fn((algorithm, key, iv) => {
189
+ return createMockDecipher(algorithm, key)
190
+ }),
191
+
192
+ // Random functions
193
+ randomBytes: jest.fn((size, callback) => {
194
+ const bytes = generateMockRandomBytes(size)
195
+
196
+ if (callback) {
197
+ setTimeout(() => callback(null, bytes), 0)
198
+ return
199
+ }
200
+
201
+ return bytes
202
+ }),
203
+
204
+ randomFillSync: jest.fn((buffer, offset, size) => {
205
+ if (!Buffer.isBuffer(buffer)) {
206
+ throw new TypeError('buffer must be a Buffer')
207
+ }
208
+
209
+ offset = offset || 0
210
+ size = size || buffer.length - offset
211
+
212
+ const randomData = generateMockRandomBytes(size)
213
+ randomData.copy(buffer, offset)
214
+
215
+ return buffer
216
+ }),
217
+
218
+ randomFill: jest.fn((buffer, offset, size, callback) => {
219
+ if (typeof offset === 'function') {
220
+ callback = offset
221
+ offset = 0
222
+ size = buffer.length
223
+ } else if (typeof size === 'function') {
224
+ callback = size
225
+ size = buffer.length - offset
226
+ }
227
+
228
+ setTimeout(() => {
229
+ try {
230
+ crypto.randomFillSync(buffer, offset, size)
231
+ callback(null, buffer)
232
+ } catch (error) {
233
+ callback(error)
234
+ }
235
+ }, 0)
236
+ }),
237
+
238
+ randomInt: jest.fn((min, max, callback) => {
239
+ if (typeof min === 'function') {
240
+ callback = min
241
+ min = 0
242
+ max = 2147483647
243
+ } else if (typeof max === 'function') {
244
+ callback = max
245
+ max = min
246
+ min = 0
247
+ }
248
+
249
+ const randomValue = Math.floor(Math.random() * (max - min)) + min
250
+
251
+ if (callback) {
252
+ setTimeout(() => callback(null, randomValue), 0)
253
+ return
254
+ }
255
+
256
+ return randomValue
257
+ }),
258
+
259
+ randomUUID: jest.fn(() => {
260
+ // Generate a mock UUID v4
261
+ const hex = '0123456789abcdef'
262
+ let uuid = ''
263
+
264
+ for (let i = 0; i < 36; i++) {
265
+ if (i === 8 || i === 13 || i === 18 || i === 23) {
266
+ uuid += '-'
267
+ } else if (i === 14) {
268
+ uuid += '4' // Version 4
269
+ } else if (i === 19) {
270
+ uuid += hex[Math.floor(Math.random() * 4) + 8] // Variant bits
271
+ } else {
272
+ uuid += hex[Math.floor(Math.random() * 16)]
273
+ }
274
+ }
275
+
276
+ return uuid
277
+ }),
278
+
279
+ // Key generation
280
+ generateKeyPairSync: jest.fn((type, options = {}) => {
281
+ const mockPrivateKey = {
282
+ asymmetricKeyType: type,
283
+ asymmetricKeySize: options.modulusLength || 2048,
284
+ export: jest.fn((options = {}) => {
285
+ if (options.format === 'pem') {
286
+ return '-----BEGIN PRIVATE KEY-----\nMockPrivateKeyData\n-----END PRIVATE KEY-----'
287
+ }
288
+ return Buffer.from('mockprivatekeydata')
289
+ })
290
+ }
291
+
292
+ const mockPublicKey = {
293
+ asymmetricKeyType: type,
294
+ asymmetricKeySize: options.modulusLength || 2048,
295
+ export: jest.fn((options = {}) => {
296
+ if (options.format === 'pem') {
297
+ return '-----BEGIN PUBLIC KEY-----\nMockPublicKeyData\n-----END PUBLIC KEY-----'
298
+ }
299
+ return Buffer.from('mockpublickeydata')
300
+ })
301
+ }
302
+
303
+ return {
304
+ privateKey: mockPrivateKey,
305
+ publicKey: mockPublicKey
306
+ }
307
+ }),
308
+
309
+ generateKeyPair: jest.fn((type, options, callback) => {
310
+ if (typeof options === 'function') {
311
+ callback = options
312
+ options = {}
313
+ }
314
+
315
+ setTimeout(() => {
316
+ try {
317
+ const keyPair = crypto.generateKeyPairSync(type, options)
318
+ callback(null, keyPair.publicKey, keyPair.privateKey)
319
+ } catch (error) {
320
+ callback(error)
321
+ }
322
+ }, 0)
323
+ }),
324
+
325
+ // Signing and verification
326
+ createSign: jest.fn(algorithm => {
327
+ const sign = createMockEventEmitter()
328
+ let data = ''
329
+
330
+ Object.assign(sign, {
331
+ update: jest.fn((chunk, encoding = 'utf8') => {
332
+ data += Buffer.isBuffer(chunk) ? chunk.toString() : String(chunk)
333
+ return sign
334
+ }),
335
+
336
+ sign: jest.fn((privateKey, outputEncoding) => {
337
+ const signature = generateMockHash('sha256', data + 'signature')
338
+
339
+ if (outputEncoding === 'hex') {
340
+ return signature
341
+ } else if (outputEncoding === 'base64') {
342
+ return Buffer.from(signature, 'hex').toString('base64')
343
+ } else if (outputEncoding) {
344
+ return Buffer.from(signature, 'hex').toString(outputEncoding)
345
+ } else {
346
+ return Buffer.from(signature, 'hex')
347
+ }
348
+ })
349
+ })
350
+
351
+ return sign
352
+ }),
353
+
354
+ createVerify: jest.fn(algorithm => {
355
+ const verify = createMockEventEmitter()
356
+ let data = ''
357
+
358
+ Object.assign(verify, {
359
+ update: jest.fn((chunk, encoding = 'utf8') => {
360
+ data += Buffer.isBuffer(chunk) ? chunk.toString() : String(chunk)
361
+ return verify
362
+ }),
363
+
364
+ verify: jest.fn((publicKey, signature, signatureEncoding) => {
365
+ // Mock verification - always return true for testing
366
+ return true
367
+ })
368
+ })
369
+
370
+ return verify
371
+ }),
372
+
373
+ // Constants
374
+ constants: {
375
+ // OpenSSL constants
376
+ OPENSSL_VERSION_NUMBER: 0x1010100f,
377
+ SSL_OP_ALL: 0x80000bff,
378
+ SSL_OP_NO_SSLv2: 0x01000000,
379
+ SSL_OP_NO_SSLv3: 0x02000000,
380
+ SSL_OP_NO_TLSv1: 0x04000000,
381
+ SSL_OP_NO_TLSv1_1: 0x10000000,
382
+ SSL_OP_NO_TLSv1_2: 0x08000000,
383
+
384
+ // RSA padding
385
+ RSA_PKCS1_PADDING: 1,
386
+ RSA_SSLV23_PADDING: 2,
387
+ RSA_NO_PADDING: 3,
388
+ RSA_PKCS1_OAEP_PADDING: 4,
389
+ RSA_X931_PADDING: 5,
390
+ RSA_PKCS1_PSS_PADDING: 6
391
+ },
392
+
393
+ // Utility functions
394
+ timingSafeEqual: jest.fn((a, b) => {
395
+ if (!Buffer.isBuffer(a) || !Buffer.isBuffer(b)) {
396
+ throw new TypeError('Arguments must be Buffers')
397
+ }
398
+
399
+ if (a.length !== b.length) {
400
+ return false
401
+ }
402
+
403
+ return a.equals(b)
404
+ }),
405
+
406
+ // Test helpers
407
+ __setMockRandomBytes: mockFn => {
408
+ crypto.randomBytes.mockImplementation(mockFn)
409
+ },
410
+
411
+ __setMockHash: (algorithm, mockHash) => {
412
+ const originalCreateHash = crypto.createHash
413
+ crypto.createHash.mockImplementation(alg => {
414
+ if (alg === algorithm) {
415
+ const hash = originalCreateHash(alg)
416
+ hash.digest.mockReturnValue(mockHash)
417
+ return hash
418
+ }
419
+ return originalCreateHash(alg)
420
+ })
421
+ },
422
+
423
+ __resetMocks: () => {
424
+ Object.values(crypto).forEach(fn => {
425
+ if (jest.isMockFunction(fn)) {
426
+ fn.mockClear()
427
+ }
428
+ })
429
+ }
430
+ }
431
+
432
+ module.exports = crypto