odac 0.9.0 → 1.0.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 (208) hide show
  1. package/.github/workflows/auto-pr-description.yml +0 -2
  2. package/.github/workflows/codeql.yml +46 -0
  3. package/.github/workflows/release.yml +13 -6
  4. package/.github/workflows/test-coverage.yml +10 -9
  5. package/.releaserc.js +9 -6
  6. package/CHANGELOG.md +62 -150
  7. package/CODE_OF_CONDUCT.md +1 -1
  8. package/CONTRIBUTING.md +8 -8
  9. package/LICENSE +21 -661
  10. package/README.md +12 -12
  11. package/SECURITY.md +4 -4
  12. package/bin/odac.js +101 -0
  13. package/{framework/web/candy.js → client/odac.js} +310 -44
  14. package/docs/backend/01-overview/{01-whats-in-the-candy-box.md → 01-whats-in-the-odac-box.md} +4 -2
  15. package/docs/backend/01-overview/02-super-handy-helper-functions.md +29 -1
  16. package/docs/backend/01-overview/03-development-server.md +11 -11
  17. package/docs/backend/02-structure/01-typical-project-layout.md +4 -4
  18. package/docs/backend/03-config/00-configuration-overview.md +6 -6
  19. package/docs/backend/03-config/01-database-connection.md +1 -1
  20. package/docs/backend/03-config/02-static-route-mapping-optional.md +4 -4
  21. package/docs/backend/03-config/04-environment-variables.md +20 -20
  22. package/docs/backend/03-config/05-early-hints.md +4 -4
  23. package/docs/backend/04-routing/01-basic-page-routes.md +4 -4
  24. package/docs/backend/04-routing/02-controller-less-view-routes.md +5 -5
  25. package/docs/backend/04-routing/03-api-and-data-routes.md +3 -3
  26. package/docs/backend/04-routing/04-authentication-aware-routes.md +5 -5
  27. package/docs/backend/04-routing/05-advanced-routing.md +3 -3
  28. package/docs/backend/04-routing/06-error-pages.md +17 -17
  29. package/docs/backend/04-routing/07-cron-jobs.md +13 -13
  30. package/docs/backend/04-routing/08-middleware.md +214 -0
  31. package/docs/backend/04-routing/09-websocket-auth-middleware.md +292 -0
  32. package/docs/backend/04-routing/09-websocket-examples.md +381 -0
  33. package/docs/backend/04-routing/09-websocket-quick-reference.md +211 -0
  34. package/docs/backend/04-routing/09-websocket.md +298 -0
  35. package/docs/backend/05-controllers/01-how-to-build-a-controller.md +3 -3
  36. package/docs/backend/05-controllers/02-your-trusty-odac-assistant.md +41 -0
  37. package/docs/backend/05-controllers/03-controller-classes.md +19 -19
  38. package/docs/backend/05-forms/01-custom-forms.md +114 -114
  39. package/docs/backend/05-forms/02-automatic-database-insert.md +82 -82
  40. package/docs/backend/06-request-and-response/01-the-request-object-what-is-the-user-asking-for.md +26 -26
  41. package/docs/backend/06-request-and-response/02-sending-a-response-replying-to-the-user.md +10 -10
  42. package/docs/backend/07-views/01-the-view-directory.md +1 -1
  43. package/docs/backend/07-views/02-rendering-a-view.md +22 -22
  44. package/docs/backend/07-views/03-template-syntax.md +52 -52
  45. package/docs/backend/07-views/03-variables.md +84 -84
  46. package/docs/backend/07-views/04-request-data.md +57 -57
  47. package/docs/backend/07-views/05-conditionals.md +78 -78
  48. package/docs/backend/07-views/06-loops.md +114 -114
  49. package/docs/backend/07-views/07-translations.md +66 -66
  50. package/docs/backend/07-views/08-backend-javascript.md +103 -103
  51. package/docs/backend/07-views/09-comments.md +71 -71
  52. package/docs/backend/08-database/01-database-connection.md +8 -8
  53. package/docs/backend/08-database/02-using-mysql.md +49 -49
  54. package/docs/backend/09-validation/01-the-validator-service.md +38 -38
  55. package/docs/backend/10-authentication/01-user-logins-with-authjs.md +15 -15
  56. package/docs/backend/10-authentication/02-foiling-villains-with-csrf-protection.md +10 -10
  57. package/docs/backend/10-authentication/03-register.md +12 -12
  58. package/docs/backend/10-authentication/{04-candy-register-forms.md → 04-odac-register-forms.md} +141 -141
  59. package/docs/backend/10-authentication/05-session-management.md +10 -10
  60. package/docs/backend/10-authentication/{06-candy-login-forms.md → 06-odac-login-forms.md} +125 -125
  61. package/docs/backend/11-mail/01-the-mail-service.md +5 -5
  62. package/docs/backend/12-streaming/01-streaming-overview.md +96 -54
  63. package/docs/backend/13-utilities/{01-candy-var.md → 01-odac-var.md} +109 -109
  64. package/docs/frontend/01-overview/01-introduction.md +30 -30
  65. package/docs/frontend/02-ajax-navigation/01-quick-start.md +45 -45
  66. package/docs/frontend/02-ajax-navigation/02-configuration.md +14 -14
  67. package/docs/frontend/02-ajax-navigation/03-advanced-usage.md +36 -36
  68. package/docs/frontend/03-forms/01-form-handling.md +32 -32
  69. package/docs/frontend/04-api-requests/01-get-post.md +33 -33
  70. package/docs/frontend/05-streaming/01-client-streaming.md +15 -15
  71. package/docs/frontend/06-websocket/00-overview.md +76 -0
  72. package/docs/frontend/06-websocket/01-websocket-client.md +139 -0
  73. package/docs/frontend/06-websocket/02-shared-websocket.md +149 -0
  74. package/docs/index.json +49 -11
  75. package/eslint.config.mjs +6 -6
  76. package/{framework/index.js → index.js} +1 -1
  77. package/package.json +14 -39
  78. package/{framework/src → src}/Auth.js +59 -59
  79. package/{framework/src → src}/Config.js +3 -3
  80. package/{framework/src → src}/Lang.js +7 -7
  81. package/{framework/src → src}/Mail.js +5 -5
  82. package/{framework/src → src}/Mysql.js +42 -42
  83. package/src/Odac.js +112 -0
  84. package/{framework/src → src}/Request.js +38 -36
  85. package/{framework/src → src}/Route/Internal.js +116 -116
  86. package/src/Route/Middleware.js +75 -0
  87. package/src/Route.js +621 -0
  88. package/src/Server.js +22 -0
  89. package/{framework/src → src}/Stream.js +11 -3
  90. package/{framework/src → src}/Validator.js +21 -21
  91. package/{framework/src → src}/Var.js +5 -5
  92. package/{framework/src → src}/View/EarlyHints.js +1 -1
  93. package/{framework/src → src}/View/Form.js +69 -69
  94. package/{framework/src → src}/View.js +78 -81
  95. package/src/WebSocket.js +403 -0
  96. package/template/config.json +5 -0
  97. package/{web → template}/controller/page/about.js +6 -6
  98. package/{web → template}/controller/page/index.js +9 -9
  99. package/{web → template}/package.json +4 -5
  100. package/{web → template}/public/assets/css/style.css +4 -4
  101. package/{web → template}/public/assets/js/app.js +6 -6
  102. package/{web → template}/route/www.js +6 -6
  103. package/{web → template}/skeleton/main.html +1 -1
  104. package/{web → template}/view/content/about.html +5 -5
  105. package/{web → template}/view/content/home.html +12 -12
  106. package/template/view/footer/main.html +11 -0
  107. package/{web → template}/view/head/main.html +1 -1
  108. package/{web → template}/view/header/main.html +2 -2
  109. package/test/core/Candy.test.js +58 -58
  110. package/test/core/Commands.test.js +7 -7
  111. package/test/core/Config.test.js +82 -85
  112. package/test/core/Lang.test.js +2 -2
  113. package/test/core/Process.test.js +6 -6
  114. package/test/framework/Route.test.js +56 -37
  115. package/test/framework/View/EarlyHints.test.js +2 -2
  116. package/test/framework/WebSocket.test.js +100 -0
  117. package/test/framework/middleware.test.js +85 -0
  118. package/test/server/Api.test.js +31 -31
  119. package/test/server/DNS.test.js +11 -11
  120. package/test/server/Hub.test.js +497 -0
  121. package/test/server/Mail.account.test_.js +3 -3
  122. package/test/server/Mail.init.test_.js +10 -10
  123. package/test/server/Mail.test_.js +20 -20
  124. package/test/server/SSL.test_.js +54 -54
  125. package/test/server/Server.test.js +39 -39
  126. package/test/server/Service.test_.js +7 -7
  127. package/test/server/Subdomain.test.js +7 -7
  128. package/test/server/Web/Firewall.test.js +87 -87
  129. package/test/server/Web/Proxy.test.js +397 -0
  130. package/test/server/{Web.test_.js → Web.test.js} +137 -205
  131. package/test/server/__mocks__/fs.js +2 -2
  132. package/test/server/__mocks__/{globalCandy.js → globalOdac.js} +5 -5
  133. package/test/server/__mocks__/index.js +6 -6
  134. package/test/server/__mocks__/testFactories.js +1 -1
  135. package/test/server/__mocks__/testHelpers.js +7 -7
  136. package/.husky/pre-commit +0 -2
  137. package/.kiro/steering/code-style.md +0 -56
  138. package/.kiro/steering/product.md +0 -20
  139. package/.kiro/steering/structure.md +0 -77
  140. package/.kiro/steering/tech.md +0 -87
  141. package/AGENTS.md +0 -84
  142. package/bin/candy +0 -10
  143. package/bin/candypack +0 -10
  144. package/cli/index.js +0 -3
  145. package/cli/src/Cli.js +0 -348
  146. package/cli/src/Connector.js +0 -93
  147. package/cli/src/Monitor.js +0 -416
  148. package/core/Candy.js +0 -87
  149. package/core/Commands.js +0 -239
  150. package/core/Config.js +0 -1094
  151. package/core/Lang.js +0 -52
  152. package/core/Log.js +0 -43
  153. package/core/Process.js +0 -26
  154. package/docs/backend/05-controllers/02-your-trusty-candy-assistant.md +0 -20
  155. package/docs/server/01-installation/01-quick-install.md +0 -19
  156. package/docs/server/01-installation/02-manual-installation-via-npm.md +0 -9
  157. package/docs/server/02-get-started/01-core-concepts.md +0 -7
  158. package/docs/server/02-get-started/02-basic-commands.md +0 -57
  159. package/docs/server/02-get-started/03-cli-reference.md +0 -276
  160. package/docs/server/02-get-started/04-cli-quick-reference.md +0 -102
  161. package/docs/server/03-service/01-start-a-new-service.md +0 -57
  162. package/docs/server/03-service/02-delete-a-service.md +0 -48
  163. package/docs/server/04-web/01-create-a-website.md +0 -36
  164. package/docs/server/04-web/02-list-websites.md +0 -9
  165. package/docs/server/04-web/03-delete-a-website.md +0 -29
  166. package/docs/server/05-subdomain/01-create-a-subdomain.md +0 -32
  167. package/docs/server/05-subdomain/02-list-subdomains.md +0 -33
  168. package/docs/server/05-subdomain/03-delete-a-subdomain.md +0 -41
  169. package/docs/server/06-ssl/01-renew-an-ssl-certificate.md +0 -34
  170. package/docs/server/07-mail/01-create-a-mail-account.md +0 -23
  171. package/docs/server/07-mail/02-delete-a-mail-account.md +0 -20
  172. package/docs/server/07-mail/03-list-mail-accounts.md +0 -20
  173. package/docs/server/07-mail/04-change-account-password.md +0 -23
  174. package/framework/src/Candy.js +0 -81
  175. package/framework/src/Route.js +0 -455
  176. package/framework/src/Server.js +0 -15
  177. package/locale/de-DE.json +0 -80
  178. package/locale/en-US.json +0 -79
  179. package/locale/es-ES.json +0 -80
  180. package/locale/fr-FR.json +0 -80
  181. package/locale/pt-BR.json +0 -80
  182. package/locale/ru-RU.json +0 -80
  183. package/locale/tr-TR.json +0 -85
  184. package/locale/zh-CN.json +0 -80
  185. package/server/index.js +0 -5
  186. package/server/src/Api.js +0 -88
  187. package/server/src/DNS.js +0 -940
  188. package/server/src/Hub.js +0 -535
  189. package/server/src/Mail.js +0 -571
  190. package/server/src/SSL.js +0 -180
  191. package/server/src/Server.js +0 -27
  192. package/server/src/Service.js +0 -248
  193. package/server/src/Subdomain.js +0 -64
  194. package/server/src/Web/Firewall.js +0 -170
  195. package/server/src/Web/Proxy.js +0 -134
  196. package/server/src/Web.js +0 -451
  197. package/server/src/mail/imap.js +0 -1091
  198. package/server/src/mail/server.js +0 -32
  199. package/server/src/mail/smtp.js +0 -786
  200. package/test/server/Client.test.js +0 -338
  201. package/test/server/__mocks__/http-proxy.js +0 -105
  202. package/watchdog/index.js +0 -3
  203. package/watchdog/src/Watchdog.js +0 -156
  204. package/web/config.json +0 -5
  205. package/web/view/footer/main.html +0 -11
  206. /package/{framework/src → src}/Env.js +0 -0
  207. /package/{framework/src → src}/Route/Cron.js +0 -0
  208. /package/{framework/src → src}/Token.js +0 -0
@@ -5,8 +5,8 @@ const os = require('os')
5
5
  jest.mock('fs')
6
6
  jest.mock('os')
7
7
 
8
- // Mock global Candy object
9
- global.Candy = {
8
+ // Mock global Odac object
9
+ global.Odac = {
10
10
  core: jest.fn(name => {
11
11
  if (name === 'Log') {
12
12
  return {
@@ -109,7 +109,7 @@ describe('Config', () => {
109
109
  global.setInterval = jest.fn().mockReturnValue({unref: jest.fn()})
110
110
 
111
111
  // Set default process.mainModule
112
- process.mainModule = {path: '/mock/node_modules/candypack/bin'}
112
+ process.mainModule = {path: '/mock/node_modules/odac/bin'}
113
113
  })
114
114
 
115
115
  afterEach(() => {
@@ -125,7 +125,7 @@ describe('Config', () => {
125
125
  config = new ConfigClass()
126
126
  config.init()
127
127
 
128
- expect(mockFs.mkdirSync).toHaveBeenCalledWith('/home/user/.candypack')
128
+ expect(mockFs.mkdirSync).toHaveBeenCalledWith('/home/user/.odac')
129
129
  })
130
130
 
131
131
  it('should handle missing config file during init', () => {
@@ -150,7 +150,7 @@ describe('Config', () => {
150
150
  config = new ConfigClass()
151
151
  config.init()
152
152
 
153
- expect(mockFs.readFileSync).toHaveBeenCalledWith('/home/user/.candypack/config.json', 'utf8')
153
+ expect(mockFs.readFileSync).toHaveBeenCalledWith('/home/user/.odac/config.json', 'utf8')
154
154
  expect(config.config.server.pid).toBe(123)
155
155
  })
156
156
 
@@ -166,7 +166,7 @@ describe('Config', () => {
166
166
  expect(config.config.server.arch).toBe('x64')
167
167
  })
168
168
 
169
- it('should setup auto-save interval when not in candypack bin', () => {
169
+ it('should setup auto-save interval when not in odac bin', () => {
170
170
  process.mainModule = {path: '/mock/project'}
171
171
  mockFs.existsSync.mockReturnValue(true)
172
172
  mockFs.readFileSync.mockReturnValue(createValidConfig())
@@ -178,8 +178,8 @@ describe('Config', () => {
178
178
  expect(global.setInterval).toHaveBeenCalledWith(expect.any(Function), 500)
179
179
  })
180
180
 
181
- it('should not setup auto-save interval when in candypack bin', () => {
182
- process.mainModule = {path: '/mock/node_modules/candypack/bin'}
181
+ it('should not setup auto-save interval when in odac bin', () => {
182
+ process.mainModule = {path: '/mock/node_modules/odac/bin'}
183
183
  mockFs.existsSync.mockReturnValue(true)
184
184
  mockFs.readFileSync.mockReturnValue(createValidConfig())
185
185
 
@@ -244,7 +244,7 @@ describe('Config', () => {
244
244
  config = new ConfigClass()
245
245
  config.init()
246
246
 
247
- expect(console.log).toHaveBeenCalledWith('Error reading config file:', '/home/user/.candypack/config.json')
247
+ expect(console.log).toHaveBeenCalledWith('Error reading config file:', '/home/user/.odac/config.json')
248
248
  })
249
249
 
250
250
  it.skip('should handle corrupted JSON file', () => {
@@ -258,7 +258,7 @@ describe('Config', () => {
258
258
  config.init()
259
259
  }).not.toThrow()
260
260
 
261
- expect(console.log).toHaveBeenCalledWith('Error parsing config file:', '/home/user/.candypack/config.json')
261
+ expect(console.log).toHaveBeenCalledWith('Error parsing config file:', '/home/user/.odac/config.json')
262
262
 
263
263
  // Should still initialize with default server config
264
264
  expect(config.config.server).toBeDefined()
@@ -363,7 +363,7 @@ describe('Config', () => {
363
363
  config.force()
364
364
 
365
365
  expect(mockFs.writeFileSync).toHaveBeenCalledWith(
366
- '/home/user/.candypack/config.json',
366
+ '/home/user/.odac/config.json',
367
367
  expect.stringContaining('"testSave": "value"'),
368
368
  'utf8'
369
369
  )
@@ -377,7 +377,7 @@ describe('Config', () => {
377
377
  jest.advanceTimersByTime(5000)
378
378
 
379
379
  expect(mockFs.writeFileSync).toHaveBeenCalledWith(
380
- '/home/user/.candypack/.bak/config.json.bak',
380
+ '/home/user/.odac/.bak/config.json.bak',
381
381
  expect.stringContaining('"testBackup": "value"'),
382
382
  'utf8'
383
383
  )
@@ -387,14 +387,14 @@ describe('Config', () => {
387
387
  config.config = {}
388
388
  config.force()
389
389
 
390
- expect(mockFs.writeFileSync).toHaveBeenCalledWith('/home/user/.candypack/config.json', '{}', 'utf8')
390
+ expect(mockFs.writeFileSync).toHaveBeenCalledWith('/home/user/.odac/config.json', '{}', 'utf8')
391
391
  })
392
392
 
393
393
  it.skip('should handle null config during save', () => {
394
394
  config.config = null
395
395
  config.force()
396
396
 
397
- expect(mockFs.writeFileSync).toHaveBeenCalledWith('/home/user/.candypack/config.json', 'null', 'utf8')
397
+ expect(mockFs.writeFileSync).toHaveBeenCalledWith('/home/user/.odac/config.json', 'null', 'utf8')
398
398
  })
399
399
 
400
400
  it('should respect changed flag when saving', () => {
@@ -415,7 +415,7 @@ describe('Config', () => {
415
415
  config.config = {a: 1}
416
416
  config.force()
417
417
 
418
- expect(mockFs.writeFileSync).toHaveBeenCalledWith('/home/user/.candypack/config.json', expect.stringContaining('"a": 1'), 'utf8')
418
+ expect(mockFs.writeFileSync).toHaveBeenCalledWith('/home/user/.odac/config.json', expect.stringContaining('"a": 1'), 'utf8')
419
419
  })
420
420
 
421
421
  it.skip('should handle writeFileSync errors during save', () => {
@@ -605,7 +605,7 @@ describe('Config', () => {
605
605
  intervalCallback()
606
606
 
607
607
  expect(mockFs.writeFileSync).toHaveBeenCalledWith(
608
- '/home/user/.candypack/config.json',
608
+ '/home/user/.odac/config.json',
609
609
  expect.stringContaining('"autoSaveTest": "value"'),
610
610
  'utf8'
611
611
  )
@@ -637,7 +637,7 @@ describe('Config', () => {
637
637
  // Should only save once despite multiple changes
638
638
  expect(mockFs.writeFileSync).toHaveBeenCalledTimes(1)
639
639
  expect(mockFs.writeFileSync).toHaveBeenCalledWith(
640
- '/home/user/.candypack/config.json',
640
+ '/home/user/.odac/config.json',
641
641
  expect.stringContaining('"change1": "value1"'),
642
642
  'utf8'
643
643
  )
@@ -678,7 +678,7 @@ describe('Config', () => {
678
678
  config.config = {a: 1}
679
679
  config.force()
680
680
 
681
- expect(mockFs.writeFileSync).toHaveBeenCalledWith('/home/user/.candypack/config.json', expect.any(String), 'utf8')
681
+ expect(mockFs.writeFileSync).toHaveBeenCalledWith('/home/user/.odac/config.json', expect.any(String), 'utf8')
682
682
  })
683
683
 
684
684
  it('should handle backup creation timeout', () => {
@@ -691,7 +691,7 @@ describe('Config', () => {
691
691
  jest.advanceTimersByTime(5000)
692
692
 
693
693
  expect(mockFs.writeFileSync).toHaveBeenCalledWith(
694
- '/home/user/.candypack/.bak/config.json.bak',
694
+ '/home/user/.odac/.bak/config.json.bak',
695
695
  expect.stringContaining('"backupTest": "value"'),
696
696
  'utf8'
697
697
  )
@@ -722,7 +722,7 @@ describe('Config', () => {
722
722
 
723
723
  config.init()
724
724
 
725
- expect(console.log).toHaveBeenCalledWith('Error reading config file:', '/home/user/.candypack/config.json')
725
+ expect(console.log).toHaveBeenCalledWith('Error reading config file:', '/home/user/.odac/config.json')
726
726
  })
727
727
  })
728
728
 
@@ -735,8 +735,8 @@ describe('Config', () => {
735
735
  describe('format detection', () => {
736
736
  it('should detect modular format when config directory exists', () => {
737
737
  mockFs.existsSync.mockImplementation(path => {
738
- if (path === '/home/user/.candypack') return true
739
- if (path === '/home/user/.candypack/config') return true
738
+ if (path === '/home/user/.odac') return true
739
+ if (path === '/home/user/.odac/config') return true
740
740
  return false
741
741
  })
742
742
  mockFs.readFileSync.mockReturnValue(JSON.stringify({server: {}}))
@@ -750,9 +750,9 @@ describe('Config', () => {
750
750
 
751
751
  it('should detect single-file format when only config.json exists', () => {
752
752
  mockFs.existsSync.mockImplementation(path => {
753
- if (path === '/home/user/.candypack') return true
754
- if (path === '/home/user/.candypack/config') return false
755
- if (path === '/home/user/.candypack/config.json') return true
753
+ if (path === '/home/user/.odac') return true
754
+ if (path === '/home/user/.odac/config') return false
755
+ if (path === '/home/user/.odac/config.json') return true
756
756
  return false
757
757
  })
758
758
  mockFs.readFileSync.mockReturnValue(createValidConfig())
@@ -766,7 +766,7 @@ describe('Config', () => {
766
766
 
767
767
  it('should detect new installation when neither exists', () => {
768
768
  mockFs.existsSync.mockImplementation(path => {
769
- if (path === '/home/user/.candypack') return false
769
+ if (path === '/home/user/.odac') return false
770
770
  return false
771
771
  })
772
772
 
@@ -782,8 +782,8 @@ describe('Config', () => {
782
782
  describe('modular loading', () => {
783
783
  it('should load all module files correctly', () => {
784
784
  mockFs.existsSync.mockImplementation(path => {
785
- if (path === '/home/user/.candypack') return true
786
- if (path === '/home/user/.candypack/config') return true
785
+ if (path === '/home/user/.odac') return true
786
+ if (path === '/home/user/.odac/config') return true
787
787
  if (path.includes('/config/server.json')) return true
788
788
  if (path.includes('/config/web.json')) return true
789
789
  return false
@@ -809,8 +809,8 @@ describe('Config', () => {
809
809
 
810
810
  it('should handle missing module files gracefully', () => {
811
811
  mockFs.existsSync.mockImplementation(path => {
812
- if (path === '/home/user/.candypack') return true
813
- if (path === '/home/user/.candypack/config') return true
812
+ if (path === '/home/user/.odac') return true
813
+ if (path === '/home/user/.odac/config') return true
814
814
  return false
815
815
  })
816
816
 
@@ -823,8 +823,8 @@ describe('Config', () => {
823
823
 
824
824
  it('should recover from corrupted module file using backup', () => {
825
825
  mockFs.existsSync.mockImplementation(path => {
826
- if (path === '/home/user/.candypack') return true
827
- if (path === '/home/user/.candypack/config') return true
826
+ if (path === '/home/user/.odac') return true
827
+ if (path === '/home/user/.odac/config') return true
828
828
  if (path.includes('/config/server.json')) return true
829
829
  if (path.includes('/.bak/server.json.bak')) return true
830
830
  return false
@@ -854,8 +854,8 @@ describe('Config', () => {
854
854
 
855
855
  it('should handle empty module file', () => {
856
856
  mockFs.existsSync.mockImplementation(path => {
857
- if (path === '/home/user/.candypack') return true
858
- if (path === '/home/user/.candypack/config') return true
857
+ if (path === '/home/user/.odac') return true
858
+ if (path === '/home/user/.odac/config') return true
859
859
  if (path.includes('/config/server.json')) return true
860
860
  return false
861
861
  })
@@ -876,9 +876,9 @@ describe('Config', () => {
876
876
  describe('migration from single-file to modular', () => {
877
877
  it('should migrate single-file config to modular format', () => {
878
878
  mockFs.existsSync.mockImplementation(path => {
879
- if (path === '/home/user/.candypack') return true
880
- if (path === '/home/user/.candypack/config.json') return true
881
- if (path === '/home/user/.candypack/config') return false
879
+ if (path === '/home/user/.odac') return true
880
+ if (path === '/home/user/.odac/config.json') return true
881
+ if (path === '/home/user/.odac/config') return false
882
882
  if (path.includes('.pre-modular')) return false
883
883
  return false
884
884
  })
@@ -891,13 +891,13 @@ describe('Config', () => {
891
891
 
892
892
  let configDirCreated = false
893
893
  mockFs.mkdirSync.mockImplementation(path => {
894
- if (path === '/home/user/.candypack/config') {
894
+ if (path === '/home/user/.odac/config') {
895
895
  configDirCreated = true
896
896
  }
897
897
  })
898
898
 
899
899
  mockFs.readFileSync.mockImplementation(path => {
900
- if (path === '/home/user/.candypack/config.json') {
900
+ if (path === '/home/user/.odac/config.json') {
901
901
  return JSON.stringify(singleFileConfig)
902
902
  }
903
903
  if (configDirCreated && path.includes('/config/')) {
@@ -913,30 +913,27 @@ describe('Config', () => {
913
913
  config = new ConfigClass()
914
914
  config.init()
915
915
 
916
- expect(mockFs.mkdirSync).toHaveBeenCalledWith('/home/user/.candypack/config', {recursive: true})
917
- expect(mockFs.copyFileSync).toHaveBeenCalledWith(
918
- '/home/user/.candypack/config.json',
919
- '/home/user/.candypack/config.json.pre-modular'
920
- )
916
+ expect(mockFs.mkdirSync).toHaveBeenCalledWith('/home/user/.odac/config', {recursive: true})
917
+ expect(mockFs.copyFileSync).toHaveBeenCalledWith('/home/user/.odac/config.json', '/home/user/.odac/config.json.pre-modular')
921
918
  })
922
919
 
923
920
  it('should rollback migration on failure', () => {
924
921
  mockFs.existsSync.mockImplementation(path => {
925
- if (path === '/home/user/.candypack') return true
926
- if (path === '/home/user/.candypack/config.json') return true
927
- if (path === '/home/user/.candypack/config') return false
922
+ if (path === '/home/user/.odac') return true
923
+ if (path === '/home/user/.odac/config.json') return true
924
+ if (path === '/home/user/.odac/config') return false
928
925
  return false
929
926
  })
930
927
 
931
928
  mockFs.readFileSync.mockReturnValue(createValidConfig())
932
929
 
933
930
  mockFs.mkdirSync.mockImplementation(path => {
934
- if (path === '/home/user/.candypack/config') {
931
+ if (path === '/home/user/.odac/config') {
935
932
  // Simulate successful directory creation
936
933
  mockFs.existsSync.mockImplementation(p => {
937
934
  if (p === path) return true
938
- if (p === '/home/user/.candypack') return true
939
- if (p === '/home/user/.candypack/config.json') return true
935
+ if (p === '/home/user/.odac') return true
936
+ if (p === '/home/user/.odac/config.json') return true
940
937
  return false
941
938
  })
942
939
  }
@@ -950,15 +947,15 @@ describe('Config', () => {
950
947
  config = new ConfigClass()
951
948
  config.init()
952
949
 
953
- expect(mockFs.rmSync).toHaveBeenCalledWith('/home/user/.candypack/config', {recursive: true, force: true})
950
+ expect(mockFs.rmSync).toHaveBeenCalledWith('/home/user/.odac/config', {recursive: true, force: true})
954
951
  expect(config.config.server).toBeDefined()
955
952
  })
956
953
 
957
954
  it('should handle migration with permission errors', () => {
958
955
  mockFs.existsSync.mockImplementation(path => {
959
- if (path === '/home/user/.candypack') return true
960
- if (path === '/home/user/.candypack/config.json') return true
961
- if (path === '/home/user/.candypack/config') return false
956
+ if (path === '/home/user/.odac') return true
957
+ if (path === '/home/user/.odac/config.json') return true
958
+ if (path === '/home/user/.odac/config') return false
962
959
  return false
963
960
  })
964
961
 
@@ -979,9 +976,9 @@ describe('Config', () => {
979
976
 
980
977
  it('should use rmSync with recursive and force options during rollback', () => {
981
978
  mockFs.existsSync.mockImplementation(path => {
982
- if (path === '/home/user/.candypack') return true
983
- if (path === '/home/user/.candypack/config.json') return true
984
- if (path === '/home/user/.candypack/config') return false
979
+ if (path === '/home/user/.odac') return true
980
+ if (path === '/home/user/.odac/config.json') return true
981
+ if (path === '/home/user/.odac/config') return false
985
982
  return false
986
983
  })
987
984
 
@@ -989,12 +986,12 @@ describe('Config', () => {
989
986
 
990
987
  let configDirCreated = false
991
988
  mockFs.mkdirSync.mockImplementation(path => {
992
- if (path === '/home/user/.candypack/config') {
989
+ if (path === '/home/user/.odac/config') {
993
990
  configDirCreated = true
994
991
  mockFs.existsSync.mockImplementation(p => {
995
992
  if (p === path) return configDirCreated
996
- if (p === '/home/user/.candypack') return true
997
- if (p === '/home/user/.candypack/config.json') return true
993
+ if (p === '/home/user/.odac') return true
994
+ if (p === '/home/user/.odac/config.json') return true
998
995
  return false
999
996
  })
1000
997
  }
@@ -1008,7 +1005,7 @@ describe('Config', () => {
1008
1005
  config = new ConfigClass()
1009
1006
  config.init()
1010
1007
 
1011
- expect(mockFs.rmSync).toHaveBeenCalledWith('/home/user/.candypack/config', {
1008
+ expect(mockFs.rmSync).toHaveBeenCalledWith('/home/user/.odac/config', {
1012
1009
  recursive: true,
1013
1010
  force: true
1014
1011
  })
@@ -1016,20 +1013,20 @@ describe('Config', () => {
1016
1013
 
1017
1014
  it('should handle rmSync errors gracefully during rollback', () => {
1018
1015
  mockFs.existsSync.mockImplementation(path => {
1019
- if (path === '/home/user/.candypack') return true
1020
- if (path === '/home/user/.candypack/config.json') return true
1021
- if (path === '/home/user/.candypack/config') return false
1016
+ if (path === '/home/user/.odac') return true
1017
+ if (path === '/home/user/.odac/config.json') return true
1018
+ if (path === '/home/user/.odac/config') return false
1022
1019
  return false
1023
1020
  })
1024
1021
 
1025
1022
  mockFs.readFileSync.mockReturnValue(createValidConfig())
1026
1023
 
1027
1024
  mockFs.mkdirSync.mockImplementation(path => {
1028
- if (path === '/home/user/.candypack/config') {
1025
+ if (path === '/home/user/.odac/config') {
1029
1026
  mockFs.existsSync.mockImplementation(p => {
1030
1027
  if (p === path) return true
1031
- if (p === '/home/user/.candypack') return true
1032
- if (p === '/home/user/.candypack/config.json') return true
1028
+ if (p === '/home/user/.odac') return true
1029
+ if (p === '/home/user/.odac/config.json') return true
1033
1030
  return false
1034
1031
  })
1035
1032
  }
@@ -1061,8 +1058,8 @@ describe('Config', () => {
1061
1058
 
1062
1059
  it('should save only changed modules', () => {
1063
1060
  mockFs.existsSync.mockImplementation(path => {
1064
- if (path === '/home/user/.candypack') return true
1065
- if (path === '/home/user/.candypack/config') return true
1061
+ if (path === '/home/user/.odac') return true
1062
+ if (path === '/home/user/.odac/config') return true
1066
1063
  return false
1067
1064
  })
1068
1065
 
@@ -1089,8 +1086,8 @@ describe('Config', () => {
1089
1086
 
1090
1087
  it('should use atomic writes for module files', () => {
1091
1088
  mockFs.existsSync.mockImplementation(path => {
1092
- if (path === '/home/user/.candypack') return true
1093
- if (path === '/home/user/.candypack/config') return true
1089
+ if (path === '/home/user/.odac') return true
1090
+ if (path === '/home/user/.odac/config') return true
1094
1091
  if (path.includes('/.bak')) return true
1095
1092
  return false
1096
1093
  })
@@ -1115,8 +1112,8 @@ describe('Config', () => {
1115
1112
 
1116
1113
  it('should create backups before overwriting module files', () => {
1117
1114
  mockFs.existsSync.mockImplementation(path => {
1118
- if (path === '/home/user/.candypack') return true
1119
- if (path === '/home/user/.candypack/config') return true
1115
+ if (path === '/home/user/.odac') return true
1116
+ if (path === '/home/user/.odac/config') return true
1120
1117
  if (path.includes('/config/server.json')) return true
1121
1118
  return false
1122
1119
  })
@@ -1138,8 +1135,8 @@ describe('Config', () => {
1138
1135
 
1139
1136
  it('should fallback to single-file on modular save failure', () => {
1140
1137
  mockFs.existsSync.mockImplementation(path => {
1141
- if (path === '/home/user/.candypack') return true
1142
- if (path === '/home/user/.candypack/config') return true
1138
+ if (path === '/home/user/.odac') return true
1139
+ if (path === '/home/user/.odac/config') return true
1143
1140
  return false
1144
1141
  })
1145
1142
 
@@ -1228,8 +1225,8 @@ describe('Config', () => {
1228
1225
 
1229
1226
  it('should write to temp file first', () => {
1230
1227
  mockFs.existsSync.mockImplementation(path => {
1231
- if (path === '/home/user/.candypack') return true
1232
- if (path === '/home/user/.candypack/config') return true
1228
+ if (path === '/home/user/.odac') return true
1229
+ if (path === '/home/user/.odac/config') return true
1233
1230
  return false
1234
1231
  })
1235
1232
 
@@ -1250,8 +1247,8 @@ describe('Config', () => {
1250
1247
 
1251
1248
  it('should cleanup temp file on write failure', () => {
1252
1249
  mockFs.existsSync.mockImplementation(path => {
1253
- if (path === '/home/user/.candypack') return true
1254
- if (path === '/home/user/.candypack/config') return true
1250
+ if (path === '/home/user/.odac') return true
1251
+ if (path === '/home/user/.odac/config') return true
1255
1252
  if (path.includes('.tmp')) return true
1256
1253
  return false
1257
1254
  })
@@ -1280,8 +1277,8 @@ describe('Config', () => {
1280
1277
 
1281
1278
  it('should handle ENOSPC error gracefully', () => {
1282
1279
  mockFs.existsSync.mockImplementation(path => {
1283
- if (path === '/home/user/.candypack') return true
1284
- if (path === '/home/user/.candypack/config') return true
1280
+ if (path === '/home/user/.odac') return true
1281
+ if (path === '/home/user/.odac/config') return true
1285
1282
  return false
1286
1283
  })
1287
1284
 
@@ -1386,8 +1383,8 @@ describe('Config', () => {
1386
1383
  describe('corruption recovery', () => {
1387
1384
  it('should create .corrupted backup when recovering from corruption', () => {
1388
1385
  mockFs.existsSync.mockImplementation(path => {
1389
- if (path === '/home/user/.candypack') return true
1390
- if (path === '/home/user/.candypack/config') return true
1386
+ if (path === '/home/user/.odac') return true
1387
+ if (path === '/home/user/.odac/config') return true
1391
1388
  if (path.includes('/config/dns.json')) return true
1392
1389
  if (path.includes('/.bak/dns.json.bak')) return true
1393
1390
  return false
@@ -1413,8 +1410,8 @@ describe('Config', () => {
1413
1410
 
1414
1411
  it('should handle both main and backup being corrupted', () => {
1415
1412
  mockFs.existsSync.mockImplementation(path => {
1416
- if (path === '/home/user/.candypack') return true
1417
- if (path === '/home/user/.candypack/config') return true
1413
+ if (path === '/home/user/.odac') return true
1414
+ if (path === '/home/user/.odac/config') return true
1418
1415
  if (path.includes('/config/mail.json')) return true
1419
1416
  if (path.includes('/.bak/mail.json.bak')) return true
1420
1417
  return false
@@ -28,9 +28,9 @@ describe('Lang', () => {
28
28
  console.error = originalConsoleError
29
29
  })
30
30
 
31
- it("should return 'CandyPack' for the 'CandyPack' key", () => {
31
+ it("should return 'Odac' for the 'Odac' key", () => {
32
32
  const lang = new Lang()
33
- expect(lang.get('CandyPack')).toBe('CandyPack')
33
+ expect(lang.get('Odac')).toBe('Odac')
34
34
  })
35
35
 
36
36
  it('should return the translation for an existing key', () => {
@@ -7,7 +7,7 @@ const findProcess = require('find-process')
7
7
  jest.mock('find-process')
8
8
  const processKillSpy = jest.spyOn(process, 'kill').mockImplementation(() => {})
9
9
 
10
- global.Candy = {
10
+ global.Odac = {
11
11
  core: () => ({
12
12
  config: {}
13
13
  })
@@ -21,7 +21,7 @@ describe('Process', () => {
21
21
  // The mock function is on the .default property of the module mock
22
22
  findProcess.default.mockClear()
23
23
  processKillSpy.mockClear()
24
- global.Candy.core = () => ({config: {}})
24
+ global.Odac.core = () => ({config: {}})
25
25
  })
26
26
 
27
27
  it('should be defined', () => {
@@ -81,7 +81,7 @@ describe('Process', () => {
81
81
  })
82
82
 
83
83
  it('should call stop for all configured pids', async () => {
84
- global.Candy.core = () => ({
84
+ global.Odac.core = () => ({
85
85
  config: {
86
86
  server: {
87
87
  watchdog: 100,
@@ -110,7 +110,7 @@ describe('Process', () => {
110
110
  })
111
111
 
112
112
  it('should handle partial configurations gracefully', async () => {
113
- global.Candy.core = () => ({
113
+ global.Odac.core = () => ({
114
114
  config: {
115
115
  server: {
116
116
  pid: 200
@@ -130,7 +130,7 @@ describe('Process', () => {
130
130
  })
131
131
 
132
132
  it('should not call stop if no pids are configured', async () => {
133
- global.Candy.core = () => ({
133
+ global.Odac.core = () => ({
134
134
  config: {
135
135
  server: {},
136
136
  websites: {},
@@ -144,7 +144,7 @@ describe('Process', () => {
144
144
  })
145
145
 
146
146
  it('should handle missing top-level config keys', async () => {
147
- global.Candy.core = () => ({
147
+ global.Odac.core = () => ({
148
148
  config: {}
149
149
  })
150
150