odac 1.0.1 → 1.2.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 (143) hide show
  1. package/.agent/rules/coding.md +27 -0
  2. package/.agent/rules/memory.md +33 -0
  3. package/.agent/rules/project.md +30 -0
  4. package/.agent/rules/workflow.md +16 -0
  5. package/.github/workflows/auto-pr-description.yml +3 -1
  6. package/.github/workflows/release.yml +42 -1
  7. package/.github/workflows/test-coverage.yml +6 -5
  8. package/.github/workflows/test-publish.yml +36 -0
  9. package/.husky/pre-commit +10 -0
  10. package/.husky/pre-push +13 -0
  11. package/.releaserc.js +3 -3
  12. package/CHANGELOG.md +184 -0
  13. package/README.md +53 -34
  14. package/bin/odac.js +181 -49
  15. package/client/odac.js +878 -995
  16. package/docs/backend/01-overview/03-development-server.md +39 -46
  17. package/docs/backend/02-structure/01-typical-project-layout.md +59 -25
  18. package/docs/backend/03-config/00-configuration-overview.md +15 -6
  19. package/docs/backend/03-config/01-database-connection.md +3 -3
  20. package/docs/backend/03-config/02-static-route-mapping-optional.md +1 -1
  21. package/docs/backend/03-config/03-request-timeout.md +1 -1
  22. package/docs/backend/03-config/04-environment-variables.md +4 -4
  23. package/docs/backend/03-config/05-early-hints.md +2 -2
  24. package/docs/backend/04-routing/02-controller-less-view-routes.md +9 -3
  25. package/docs/backend/04-routing/03-api-and-data-routes.md +18 -0
  26. package/docs/backend/04-routing/07-cron-jobs.md +17 -1
  27. package/docs/backend/04-routing/09-websocket.md +29 -0
  28. package/docs/backend/05-controllers/01-how-to-build-a-controller.md +48 -3
  29. package/docs/backend/05-controllers/02-your-trusty-odac-assistant.md +2 -0
  30. package/docs/backend/05-controllers/03-controller-classes.md +61 -55
  31. package/docs/backend/05-forms/01-custom-forms.md +103 -95
  32. package/docs/backend/05-forms/02-automatic-database-insert.md +21 -21
  33. package/docs/backend/06-request-and-response/01-the-request-object-what-is-the-user-asking-for.md +17 -0
  34. package/docs/backend/07-views/02-rendering-a-view.md +1 -1
  35. package/docs/backend/07-views/03-variables.md +5 -5
  36. package/docs/backend/07-views/04-request-data.md +1 -1
  37. package/docs/backend/07-views/08-backend-javascript.md +1 -1
  38. package/docs/backend/07-views/10-styling-and-tailwind.md +93 -0
  39. package/docs/backend/08-database/01-getting-started.md +100 -0
  40. package/docs/backend/08-database/02-basics.md +136 -0
  41. package/docs/backend/08-database/03-advanced.md +84 -0
  42. package/docs/backend/08-database/04-migrations.md +48 -0
  43. package/docs/backend/09-validation/01-the-validator-service.md +1 -0
  44. package/docs/backend/10-authentication/03-register.md +9 -2
  45. package/docs/backend/10-authentication/04-odac-register-forms.md +48 -48
  46. package/docs/backend/10-authentication/05-session-management.md +16 -2
  47. package/docs/backend/10-authentication/06-odac-login-forms.md +50 -50
  48. package/docs/backend/10-authentication/07-magic-links.md +134 -0
  49. package/docs/backend/11-mail/01-the-mail-service.md +118 -28
  50. package/docs/backend/12-streaming/01-streaming-overview.md +2 -2
  51. package/docs/backend/13-utilities/01-odac-var.md +7 -7
  52. package/docs/backend/13-utilities/02-ipc.md +73 -0
  53. package/docs/frontend/01-overview/01-introduction.md +5 -1
  54. package/docs/frontend/02-ajax-navigation/01-quick-start.md +1 -1
  55. package/docs/index.json +21 -125
  56. package/eslint.config.mjs +5 -47
  57. package/jest.config.js +1 -1
  58. package/package.json +16 -7
  59. package/src/Auth.js +414 -121
  60. package/src/Config.js +12 -7
  61. package/src/Database.js +188 -0
  62. package/src/Env.js +3 -1
  63. package/src/Ipc.js +337 -0
  64. package/src/Lang.js +9 -2
  65. package/src/Mail.js +408 -37
  66. package/src/Odac.js +105 -40
  67. package/src/Request.js +71 -49
  68. package/src/Route/Cron.js +62 -18
  69. package/src/Route/Internal.js +215 -12
  70. package/src/Route/Middleware.js +7 -2
  71. package/src/Route.js +372 -109
  72. package/src/Server.js +118 -12
  73. package/src/Storage.js +169 -0
  74. package/src/Token.js +6 -4
  75. package/src/Validator.js +95 -3
  76. package/src/Var.js +22 -6
  77. package/src/View/EarlyHints.js +43 -33
  78. package/src/View/Form.js +210 -28
  79. package/src/View.js +108 -7
  80. package/src/WebSocket.js +18 -3
  81. package/template/odac.json +5 -0
  82. package/template/package.json +3 -1
  83. package/template/route/www.js +12 -10
  84. package/template/view/content/home.html +3 -3
  85. package/template/view/head/main.html +2 -2
  86. package/test/Client.test.js +168 -0
  87. package/test/Config.test.js +112 -0
  88. package/test/Lang.test.js +92 -0
  89. package/test/Odac.test.js +86 -0
  90. package/test/{framework/middleware.test.js → Route/Middleware.test.js} +2 -2
  91. package/test/{framework/Route.test.js → Route.test.js} +1 -1
  92. package/test/{framework/View → View}/EarlyHints.test.js +1 -1
  93. package/test/{framework/WebSocket.test.js → WebSocket.test.js} +2 -2
  94. package/test/scripts/check-coverage.js +4 -4
  95. package/docs/backend/08-database/01-database-connection.md +0 -99
  96. package/docs/backend/08-database/02-using-mysql.md +0 -322
  97. package/src/Mysql.js +0 -575
  98. package/template/config.json +0 -5
  99. package/test/cli/Cli.test.js +0 -36
  100. package/test/core/Candy.test.js +0 -234
  101. package/test/core/Commands.test.js +0 -538
  102. package/test/core/Config.test.js +0 -1432
  103. package/test/core/Lang.test.js +0 -250
  104. package/test/core/Process.test.js +0 -156
  105. package/test/server/Api.test.js +0 -647
  106. package/test/server/DNS.test.js +0 -2050
  107. package/test/server/DNS.test.js.bak +0 -2084
  108. package/test/server/Hub.test.js +0 -497
  109. package/test/server/Log.test.js +0 -73
  110. package/test/server/Mail.account.test_.js +0 -460
  111. package/test/server/Mail.init.test_.js +0 -411
  112. package/test/server/Mail.test_.js +0 -1340
  113. package/test/server/SSL.test_.js +0 -1491
  114. package/test/server/Server.test.js +0 -765
  115. package/test/server/Service.test_.js +0 -1127
  116. package/test/server/Subdomain.test.js +0 -440
  117. package/test/server/Web/Firewall.test.js +0 -175
  118. package/test/server/Web/Proxy.test.js +0 -397
  119. package/test/server/Web.test.js +0 -1494
  120. package/test/server/__mocks__/acme-client.js +0 -17
  121. package/test/server/__mocks__/bcrypt.js +0 -50
  122. package/test/server/__mocks__/child_process.js +0 -389
  123. package/test/server/__mocks__/crypto.js +0 -432
  124. package/test/server/__mocks__/fs.js +0 -450
  125. package/test/server/__mocks__/globalOdac.js +0 -227
  126. package/test/server/__mocks__/http.js +0 -575
  127. package/test/server/__mocks__/https.js +0 -272
  128. package/test/server/__mocks__/index.js +0 -249
  129. package/test/server/__mocks__/mail/server.js +0 -100
  130. package/test/server/__mocks__/mail/smtp.js +0 -31
  131. package/test/server/__mocks__/mailparser.js +0 -81
  132. package/test/server/__mocks__/net.js +0 -369
  133. package/test/server/__mocks__/node-forge.js +0 -328
  134. package/test/server/__mocks__/os.js +0 -320
  135. package/test/server/__mocks__/path.js +0 -291
  136. package/test/server/__mocks__/selfsigned.js +0 -8
  137. package/test/server/__mocks__/server/src/mail/server.js +0 -100
  138. package/test/server/__mocks__/server/src/mail/smtp.js +0 -31
  139. package/test/server/__mocks__/smtp-server.js +0 -106
  140. package/test/server/__mocks__/sqlite3.js +0 -394
  141. package/test/server/__mocks__/testFactories.js +0 -299
  142. package/test/server/__mocks__/testHelpers.js +0 -363
  143. package/test/server/__mocks__/tls.js +0 -229
@@ -1,234 +0,0 @@
1
- // Mock fs before requiring any modules that might use it
2
- jest.mock('fs', () => ({
3
- promises: {
4
- writeFile: jest.fn().mockResolvedValue()
5
- },
6
- readFileSync: jest.fn().mockReturnValue('{"Status": "Status"}'),
7
- writeFileSync: jest.fn(),
8
- existsSync: jest.fn().mockReturnValue(true),
9
- mkdirSync: jest.fn()
10
- }))
11
-
12
- // Mock process.mainModule to avoid Config initialization issues
13
- process.mainModule = {path: '/mock/node_modules/odac/bin'}
14
-
15
- // Import the module to ensure global setup
16
- require('../../core/Odac.js')
17
-
18
- describe('Odac', () => {
19
- describe('global setup', () => {
20
- it('should have global Odac instance', () => {
21
- expect(global.Odac).toBeDefined()
22
- expect(global.Odac._registry).toBeInstanceOf(Map)
23
- expect(global.Odac._singletons).toBeInstanceOf(Map)
24
- })
25
-
26
- it('should have global __ function', () => {
27
- expect(global.__).toBeDefined()
28
- expect(typeof global.__).toBe('function')
29
- })
30
-
31
- it('should call Lang.get through global __ function', () => {
32
- const result = global.__('Status')
33
- expect(result).toBe('Status')
34
- })
35
-
36
- it('should not recreate global if already exists', () => {
37
- const existingCandy = global.Odac
38
-
39
- // Clear module cache and re-require
40
- delete require.cache[require.resolve('../../core/Odac.js')]
41
- require('../../core/Odac.js')
42
-
43
- expect(global.Odac).toBe(existingCandy)
44
- })
45
- })
46
-
47
- describe('Odac instance methods', () => {
48
- it('should have core method', () => {
49
- expect(typeof global.Odac.core).toBe('function')
50
- })
51
-
52
- it('should have cli method', () => {
53
- expect(typeof global.Odac.cli).toBe('function')
54
- })
55
-
56
- it('should have server method', () => {
57
- expect(typeof global.Odac.server).toBe('function')
58
- })
59
-
60
- it('should have watchdog method', () => {
61
- expect(typeof global.Odac.watchdog).toBe('function')
62
- })
63
- })
64
-
65
- describe('module loading functionality', () => {
66
- it('should load core modules', () => {
67
- const lang = global.Odac.core('Lang')
68
- expect(lang).toBeDefined()
69
- expect(global.Odac._registry.has('core:Lang')).toBe(true)
70
- })
71
-
72
- it('should return singleton instances by default', () => {
73
- const lang1 = global.Odac.core('Lang')
74
- const lang2 = global.Odac.core('Lang')
75
- expect(lang1).toBe(lang2)
76
- })
77
-
78
- it('should cache singleton instances', () => {
79
- const lang1 = global.Odac.core('Lang')
80
- const lang2 = global.Odac.core('Lang')
81
-
82
- expect(lang1).toBe(lang2)
83
- expect(global.Odac._singletons.has('core:Lang')).toBe(true)
84
- })
85
-
86
- it('should return new instances when singleton is false', () => {
87
- // Clear any existing Lang registration first to test non-singleton behavior
88
- global.Odac._registry.delete('core:Lang')
89
- global.Odac._singletons.delete('core:Lang')
90
-
91
- const lang1 = global.Odac.core('Lang', false)
92
- const lang2 = global.Odac.core('Lang', false)
93
- expect(lang1).not.toBe(lang2)
94
- })
95
-
96
- it('should call init method if module has one', () => {
97
- // Test with Config module which has init method
98
- const config = global.Odac.core('Config')
99
- expect(config).toBeDefined()
100
- })
101
-
102
- it('should retrieve Config module as singleton', () => {
103
- // Get Config instance twice
104
- const config1 = global.Odac.core('Config')
105
- const config2 = global.Odac.core('Config')
106
-
107
- // Should be the same instance (singleton behavior)
108
- expect(config1).toBe(config2)
109
- expect(config1).toBeInstanceOf(Object)
110
-
111
- // Should be registered in singleton cache
112
- expect(global.Odac._singletons.has('core:Config')).toBe(true)
113
- expect(global.Odac._singletons.get('core:Config')).toBe(config1)
114
-
115
- // Should have Config class properties
116
- expect(config1.config).toBeDefined()
117
- expect(typeof config1.force).toBe('function')
118
- expect(typeof config1.reload).toBe('function')
119
- })
120
-
121
- it('should handle modules without init method', () => {
122
- // Test with Process module which doesn't have init method
123
- const process = global.Odac.core('Process')
124
- expect(process).toBeDefined()
125
- })
126
- })
127
-
128
- describe('different module types', () => {
129
- it('should load cli modules with correct path prefix', () => {
130
- // This will fail but we can test the path construction
131
- expect(() => {
132
- global.Odac.cli('NonExistentCli')
133
- }).toThrow()
134
- })
135
-
136
- it('should load server modules with correct path prefix', () => {
137
- // This will fail but we can test the path construction
138
- expect(() => {
139
- global.Odac.server('NonExistentServer')
140
- }).toThrow()
141
- })
142
-
143
- it('should load watchdog modules with correct path prefix', () => {
144
- // This will fail but we can test the path construction
145
- expect(() => {
146
- global.Odac.watchdog('NonExistentWatchdog')
147
- }).toThrow()
148
- })
149
- })
150
-
151
- describe('registry functionality', () => {
152
- it('should maintain separate registries for different module types', () => {
153
- // Load a core module
154
- global.Odac.core('Lang')
155
-
156
- expect(global.Odac._registry.has('core:Lang')).toBe(true)
157
- expect(global.Odac._registry.size).toBeGreaterThan(0)
158
- })
159
-
160
- it('should store registry entries with correct structure', () => {
161
- global.Odac.core('Lang')
162
-
163
- const entry = global.Odac._registry.get('core:Lang')
164
- expect(entry).toBeDefined()
165
- expect(entry).toHaveProperty('value')
166
- expect(entry).toHaveProperty('singleton')
167
- })
168
-
169
- it('should handle non-singleton registry entries', () => {
170
- // Clear existing registration
171
- global.Odac._registry.delete('core:Lang')
172
- global.Odac._singletons.delete('core:Lang')
173
-
174
- global.Odac.core('Lang', false)
175
-
176
- const entry = global.Odac._registry.get('core:Lang')
177
- expect(entry.singleton).toBe(false)
178
- })
179
- })
180
-
181
- describe('instantiation behavior', () => {
182
- it('should instantiate function modules', () => {
183
- const lang = global.Odac.core('Lang')
184
- expect(lang).toBeInstanceOf(Object)
185
- })
186
-
187
- it('should handle non-function modules', () => {
188
- const config = global.Odac.core('Config')
189
- expect(config).toBeDefined()
190
- })
191
- })
192
-
193
- describe('error handling', () => {
194
- it('should handle module loading errors gracefully', () => {
195
- expect(() => {
196
- global.Odac.core('NonExistentModule')
197
- }).toThrow()
198
- })
199
-
200
- it('should throw specific error for non-existent registry entries', () => {
201
- // Create a new Odac instance to test private methods
202
- const OdacClass = global.Odac.constructor
203
- const testInstance = new OdacClass()
204
-
205
- expect(() => {
206
- // This should trigger the resolve error
207
- testInstance.core('NonExistentModule')
208
- }).toThrow()
209
- })
210
- })
211
-
212
- describe('module caching', () => {
213
- beforeEach(() => {
214
- // Clear any existing registrations for clean tests
215
- global.Odac._registry.clear()
216
- global.Odac._singletons.clear()
217
- })
218
-
219
- it('should not re-register already registered modules', () => {
220
- // Load Lang module twice
221
- const lang1 = global.Odac.core('Lang')
222
- const lang2 = global.Odac.core('Lang')
223
-
224
- expect(lang1).toBe(lang2)
225
- })
226
-
227
- it('should maintain singleton cache correctly', () => {
228
- const lang = global.Odac.core('Lang')
229
-
230
- expect(global.Odac._singletons.has('core:Lang')).toBe(true)
231
- expect(global.Odac._singletons.get('core:Lang')).toBe(lang)
232
- })
233
- })
234
- })