odac 1.0.0 → 1.1.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 (61) hide show
  1. package/.github/workflows/auto-pr-description.yml +3 -1
  2. package/CHANGELOG.md +127 -0
  3. package/README.md +39 -36
  4. package/bin/odac.js +1 -31
  5. package/client/odac.js +871 -994
  6. package/docs/backend/01-overview/03-development-server.md +7 -7
  7. package/docs/backend/02-structure/01-typical-project-layout.md +1 -0
  8. package/docs/backend/03-config/00-configuration-overview.md +9 -0
  9. package/docs/backend/03-config/01-database-connection.md +1 -1
  10. package/docs/backend/04-routing/02-controller-less-view-routes.md +9 -3
  11. package/docs/backend/04-routing/09-websocket.md +29 -0
  12. package/docs/backend/05-controllers/02-your-trusty-odac-assistant.md +2 -0
  13. package/docs/backend/05-controllers/03-controller-classes.md +27 -41
  14. package/docs/backend/05-forms/01-custom-forms.md +103 -95
  15. package/docs/backend/05-forms/02-automatic-database-insert.md +21 -21
  16. package/docs/backend/07-views/02-rendering-a-view.md +1 -1
  17. package/docs/backend/07-views/03-variables.md +5 -5
  18. package/docs/backend/07-views/04-request-data.md +1 -1
  19. package/docs/backend/07-views/08-backend-javascript.md +1 -1
  20. package/docs/backend/08-database/01-getting-started.md +100 -0
  21. package/docs/backend/08-database/02-basics.md +136 -0
  22. package/docs/backend/08-database/03-advanced.md +84 -0
  23. package/docs/backend/08-database/04-migrations.md +48 -0
  24. package/docs/backend/09-validation/01-the-validator-service.md +1 -0
  25. package/docs/backend/10-authentication/03-register.md +8 -1
  26. package/docs/backend/10-authentication/04-odac-register-forms.md +46 -46
  27. package/docs/backend/10-authentication/05-session-management.md +1 -1
  28. package/docs/backend/10-authentication/06-odac-login-forms.md +48 -48
  29. package/docs/backend/10-authentication/07-magic-links.md +134 -0
  30. package/docs/backend/11-mail/01-the-mail-service.md +118 -28
  31. package/docs/backend/12-streaming/01-streaming-overview.md +2 -2
  32. package/docs/backend/13-utilities/01-odac-var.md +7 -7
  33. package/docs/backend/13-utilities/02-ipc.md +73 -0
  34. package/docs/frontend/01-overview/01-introduction.md +5 -1
  35. package/docs/frontend/02-ajax-navigation/01-quick-start.md +1 -1
  36. package/docs/index.json +16 -124
  37. package/eslint.config.mjs +5 -47
  38. package/package.json +9 -4
  39. package/src/Auth.js +362 -104
  40. package/src/Config.js +7 -2
  41. package/src/Database.js +188 -0
  42. package/src/Ipc.js +330 -0
  43. package/src/Mail.js +408 -37
  44. package/src/Odac.js +65 -9
  45. package/src/Request.js +70 -48
  46. package/src/Route/Cron.js +4 -1
  47. package/src/Route/Internal.js +214 -11
  48. package/src/Route/Middleware.js +7 -2
  49. package/src/Route.js +106 -26
  50. package/src/Server.js +80 -11
  51. package/src/Storage.js +165 -0
  52. package/src/Validator.js +94 -2
  53. package/src/View/Form.js +193 -17
  54. package/src/View.js +46 -1
  55. package/src/WebSocket.js +18 -3
  56. package/template/config.json +1 -1
  57. package/template/route/www.js +12 -10
  58. package/test/core/{Candy.test.js → Odac.test.js} +2 -2
  59. package/docs/backend/08-database/01-database-connection.md +0 -99
  60. package/docs/backend/08-database/02-using-mysql.md +0 -322
  61. package/src/Mysql.js +0 -575
@@ -1,42 +1,132 @@
1
1
  ## ✉️ The `Mail` Service
2
2
 
3
- The `Odac.Mail` service is your friendly neighborhood postal worker. It provides a super simple way to send emails using the mail server settings you've already configured in the Odac core.
3
+ The `Odac.Mail` service provides a fluent, chainable interface to send emails. **It is a zero-config service** designed to work exclusively with the **ODAC Core** server. If you are running Odac, the mail service works out of the box without any additional setup.
4
4
 
5
- #### How to Send an Email
5
+ ### How to Send an Email
6
6
 
7
- `Odac.Mail.send(to, subject, htmlBody)`
7
+ The `Odac.Mail` class uses a builder pattern. You start by instantiating it with a template name, set various properties, and finally call `send()`.
8
8
 
9
- * `to`: The email address of the person you're sending it to.
10
- * `subject`: The subject line for your email.
11
- * `htmlBody`: The main content of your email. You can even use HTML tags to make it look fancy!
9
+ #### Syntax
12
10
 
13
- Just like the database service, `send` is an `async` method, so using it with `async/await` is the way to go.
11
+ ```javascript
12
+ await Odac.Mail('template_name')
13
+ .from('sender@example.com', 'Sender Name')
14
+ .to('recipient@example.com')
15
+ .subject('Your Subject Here')
16
+ .send({
17
+ variable1: 'value1',
18
+ variable2: 'value2'
19
+ });
20
+ ```
21
+
22
+ * **Template**: The constructor takes the name of the HTML template file located in `view/mail/`.
23
+ * **Data**: The object passed to `send()` contains key-value pairs that replace `{key}` placeholders in your HTML template.
14
24
 
15
- #### Example: A Simple Contact Form
25
+ #### Example: Contact Form Controller
16
26
 
17
- Let's imagine you have a controller that handles a contact form on your website.
27
+ Here is a complete example of how to use the Mail service inside a controller:
18
28
 
19
29
  ```javascript
20
30
  module.exports = async function (Odac) {
21
- const { recipient, subject, message } = Odac.Request.post;
22
-
23
- // It's always a good idea to check your data first!
24
- if (!recipient || !subject || !message) {
25
- return { error: 'Oops! You missed a required field.' };
26
- }
27
-
28
- try {
29
- // Let's try to send the email
30
- await Odac.Mail.send(recipient, subject, `<p>${message}</p>`);
31
-
32
- // If we get here, it worked!
33
- return { success: true, message: 'Email sent successfully!' };
34
- } catch (error) {
35
- // If something went wrong...
36
- console.error('Oh no, the email failed to send:', error);
37
- return { error: 'Something went wrong while trying to send the email.' };
38
- }
31
+ const { name, email, message } = Odac.Request.post;
32
+
33
+ // 1. Validate Input
34
+ if (!name || !email || !message) {
35
+ return { error: 'Please fill in all fields.' };
36
+ }
37
+
38
+ try {
39
+ // 2. Prepare and Send Email
40
+ const result = await Odac.Mail('contact_form_notification')
41
+ .from('system@myapp.com', 'My App System')
42
+ .to('admin@myapp.com')
43
+ .subject('New Contact Form Submission')
44
+ .send({
45
+ user_name: name,
46
+ user_email: email,
47
+ user_message: message,
48
+ timestamp: new Date().toISOString()
49
+ });
50
+
51
+ // 3. Check Result
52
+ if (result) {
53
+ return { success: true, message: 'Thank you! We received your message.' };
54
+ } else {
55
+ return { error: 'Failed to send email.' };
56
+ }
57
+
58
+ } catch (e) {
59
+ console.error(e);
60
+ return { error: 'An unexpected error occurred.' };
61
+ }
39
62
  }
40
63
  ```
41
64
 
42
- And that's all there is to it. You're now a master of email!
65
+ ### Template File
66
+
67
+ Create your HTML template in `view/mail/contact_form_notification.html`:
68
+
69
+ ```html
70
+ <h1>New Contact Message</h1>
71
+ <p><strong>From:</strong> {user_name} ({user_email})</p>
72
+ <p><strong>Time:</strong> {timestamp}</p>
73
+ <hr>
74
+ <p>{user_message}</p>
75
+ ```
76
+
77
+ ### Sending Plain Text or Raw HTML (No Template)
78
+
79
+ You can send emails **without creating a template file** by using the `.text()` or `.html()` methods directly. This is useful for simple notifications or dynamic content.
80
+
81
+ **Note:** If you provide both a template name AND manual content, the template will take precedence.
82
+
83
+ #### 1. Plain Text Email
84
+
85
+ Use the `.text()` method to send a simple, text-only email. The `Content-Type` will automatically be set to `text/plain`.
86
+
87
+ ```javascript
88
+ await Odac.Mail() // No template name required
89
+ .to('recipient@example.com')
90
+ .subject('System Alert')
91
+ .text('Server load is critical. Please check immediately.')
92
+ .send();
93
+ ```
94
+
95
+ > **Note:** Plain text does not support HTML tags like links (`<a href>`). If you write a URL (e.g., `https://odac.run`), most email clients will automatically make it clickable.
96
+
97
+ #### 2. Raw HTML Email
98
+
99
+ Use the `.html()` method to send an HTML email without a file.
100
+
101
+ ```javascript
102
+ await Odac.Mail()
103
+ .to('user@example.com')
104
+ .subject('Welcome')
105
+ .html('<h1>Welcome!</h1><p>Please <a href="https://example.com">click here</a>.</p>')
106
+ .send();
107
+ ```
108
+
109
+ If you use `.html()`, the system will automatically generate a plain-text version of your email by stripping the tags, just like it does for templates.
110
+
111
+ ### Advanced Usage
112
+
113
+ #### Custom Headers
114
+
115
+ You can inject custom headers into the email using the `header()` method:
116
+
117
+ ```javascript
118
+ .header({
119
+ 'X-Custom-Header': 'CustomValue',
120
+ 'Reply-To': 'support@example.com'
121
+ })
122
+ ```
123
+
124
+ #### Chainable Methods
125
+
126
+ * `from(email, name)`: Sets the sender.
127
+ * `to(email)`: Sets the recipient.
128
+ * `subject(text)`: Sets the subject line.
129
+ * `text(content)`: Sets the plain text body (used if no template is provided).
130
+ * `html(content)`: Sets the HTML body (used if no template is provided).
131
+ * `header(object)`: Merges custom headers.
132
+ * `send(data)`: Compiles the template with `data`, connects to the Odac Core, and sends the email payload. Returns a `Promise`.
@@ -91,7 +91,7 @@ module.exports = async (Odac) => {
91
91
  // controller/users/get/index.js
92
92
  module.exports = async (Odac) => {
93
93
  Odac.stream(async function* () {
94
- const users = await Odac.Mysql.table('users').get()
94
+ const users = await Odac.DB.users.get()
95
95
 
96
96
  for (const user of users) {
97
97
  yield user
@@ -194,7 +194,7 @@ module.exports = async (Odac) => {
194
194
  let hasMore = true
195
195
 
196
196
  while (hasMore) {
197
- const posts = await Odac.Mysql.table('posts')
197
+ const posts = await Odac.DB.posts
198
198
  .limit(10)
199
199
  .offset((page - 1) * 10)
200
200
  .get()
@@ -313,7 +313,7 @@ module.exports = async function(Odac) {
313
313
  const profileSlug = Odac.Var(username).slug()
314
314
 
315
315
  // Save user
316
- await Odac.Mysql.table('users').insert({
316
+ await Odac.DB.users.insert({
317
317
  email: email,
318
318
  username: username,
319
319
  password: hashedPassword,
@@ -332,7 +332,7 @@ module.exports = async function(Odac) {
332
332
  const password = Odac.Request.post('password')
333
333
 
334
334
  // Find user
335
- const user = await Odac.Mysql.table('users')
335
+ const user = await Odac.DB.users
336
336
  .where('email', email)
337
337
  .first()
338
338
 
@@ -369,19 +369,19 @@ module.exports = async function(Odac) {
369
369
  const slug = Odac.Var(title).slug()
370
370
 
371
371
  // Check if slug exists
372
- const exists = await Odac.Mysql.table('posts')
372
+ const exists = await Odac.DB.posts
373
373
  .where('slug', slug)
374
374
  .first()
375
375
 
376
376
  if (exists) {
377
377
  // Add timestamp to make unique
378
378
  const uniqueSlug = `${slug}-${Date.now()}`
379
- await Odac.Mysql.table('posts').insert({
379
+ await Odac.DB.posts.insert({
380
380
  title: title,
381
381
  slug: uniqueSlug
382
382
  })
383
383
  } else {
384
- await Odac.Mysql.table('posts').insert({
384
+ await Odac.DB.posts.insert({
385
385
  title: title,
386
386
  slug: slug
387
387
  })
@@ -464,7 +464,7 @@ module.exports = async function(Odac) {
464
464
  const encryptedCard = Odac.Var(creditCard).encrypt()
465
465
 
466
466
  // Save encrypted data
467
- await Odac.Mysql.table('payments').insert({
467
+ await Odac.DB.payments.insert({
468
468
  user_id: Odac.Auth.id(),
469
469
  card: encryptedCard
470
470
  })
@@ -474,7 +474,7 @@ module.exports = async function(Odac) {
474
474
 
475
475
  // Later, to retrieve and decrypt
476
476
  module.exports = async function(Odac) {
477
- const payment = await Odac.Mysql.table('payments')
477
+ const payment = await Odac.DB.payments
478
478
  .where('user_id', Odac.Auth.id())
479
479
  .first()
480
480
 
@@ -0,0 +1,73 @@
1
+ # IPC (Inter-Process Communication)
2
+
3
+ Odac provides a built-in IPC system that allows your application workers to communicate with each other, share data, and sync states. It abstracts the underlying driver, allowing you to switch between in-memory (cluster) and Redis-based communication with a simple configuration change.
4
+
5
+ ## Configuration
6
+
7
+ To configure the IPC system, update your project configuration. The default driver is `memory`.
8
+
9
+ ```javascript
10
+ // config.js
11
+ module.exports = {
12
+ // ...
13
+ database: {
14
+ // ... other dbs
15
+ redis: {
16
+ default: {
17
+ // node-redis connection options
18
+ // e.g., url: 'redis://user:pass@host:port'
19
+ }
20
+ }
21
+ },
22
+ ipc: {
23
+ driver: 'memory', // Options: 'memory' or 'redis'
24
+ redis: 'default' // The name of the redis connection to use (if driver is 'redis')
25
+ }
26
+ }
27
+ ```
28
+
29
+ ### Drivers
30
+
31
+ - **memory**: Uses Node.js `cluster` IPC. Ideal for single-server deployments. Data is stored in the Main/Primary process RAM and shared across workers via `process.send`.
32
+ - **redis**: Uses a Redis server. Ideal for multi-server/horizontal scaling deployments. Requires a configured Redis connection.
33
+
34
+ ## Usage
35
+
36
+ You can access the IPC module via `Odac.Ipc`.
37
+
38
+ ### Data Storage (Key-Value)
39
+
40
+ You can set, get, and delete values. These values are shared across all workers.
41
+
42
+ ```javascript
43
+ // Set a value (with optional TTL in seconds)
44
+ await Odac.Ipc.set('maintenance_mode', true);
45
+ await Odac.Ipc.set('temp_cache', { foo: 'bar' }, 60);
46
+
47
+ // Get a value
48
+ const isMaintenance = await Odac.Ipc.get('maintenance_mode');
49
+
50
+ // Delete a value
51
+ await Odac.Ipc.del('maintenance_mode');
52
+ ```
53
+
54
+ > [!NOTE]
55
+ > `get`, `set`, and `del` are asynchronous and return Promises.
56
+
57
+ ### Pub/Sub (Messaging)
58
+
59
+ You can publish messages to channels and subscribe to them in other workers.
60
+
61
+ ```javascript
62
+ // Subscribe to a channel
63
+ // Best practice: Subscribe in your app initialization or a Service Provider
64
+ await Odac.Ipc.subscribe('chat:global', (message) => {
65
+ console.log('New chat message:', message);
66
+ });
67
+
68
+ // Publish a message
69
+ await Odac.Ipc.publish('chat:global', { user: 'Emre', text: 'Hello World' });
70
+ ```
71
+
72
+ > [!TIP]
73
+ > When using `memory` driver, the subscription listener is registered in the current worker. When a message is published, it goes to the Main process and is then broadcasted to all subscribed workers.
@@ -131,7 +131,11 @@ odac.get('/api/users', function(data) {
131
131
  ## Other Utility Functions
132
132
 
133
133
  - **`Odac.client()`**: Returns a unique client identifier from a cookie.
134
- - **`Odac.data()`**: Returns data from the `candy_data` cookie, which is set by the backend. This data includes the current page and the initial CSRF token.
134
+ - **`Odac.data(key)`**: Returns shared data passed from the backend via `Odac.share`. You can get the full data object or a specific key:
135
+ ```javascript
136
+ let allData = odac.data();
137
+ let user = odac.data('user'); // Returns null if not exists
138
+ ```
135
139
  - **`Odac.page()`**: Returns the identifier of the current page. This is the controller name (e.g., `'user'`) or view name (e.g., `'dashboard'`) set by the backend. Use this to conditionally run code for specific pages.
136
140
  - **`Odac.storage()`**: A wrapper for `localStorage`.
137
141
  ```javascript
@@ -347,7 +347,7 @@ Odac.action({
347
347
  **Page Identifier Rules:**
348
348
  - **With controller**: Uses controller filename (e.g., `user.js` → `'user'`)
349
349
  - **With view object**: Uses `content` or `all` value (e.g., `{content: 'dashboard'}` → `'dashboard'`)
350
- - Accessible via `Odac.page()` or `document.documentElement.dataset.candyPage`
350
+ - Accessible via `Odac.page()`
351
351
 
352
352
  ## Server Variables
353
353
 
package/docs/index.json CHANGED
@@ -1,124 +1,4 @@
1
1
  {
2
- "server": [
3
- {
4
- "file": "01-installation",
5
- "title": "Installation",
6
- "children": [
7
- {
8
- "file": "01-quick-install.md",
9
- "title": "Quick Install"
10
- },
11
- {
12
- "file": "02-manual-installation-via-npm.md",
13
- "title": "Manual Installation (via NPM)"
14
- }
15
- ]
16
- },
17
- {
18
- "file": "02-get-started",
19
- "title": "Getting Started",
20
- "children": [
21
- {
22
- "file": "01-core-concepts.md",
23
- "title": "Core Concepts"
24
- },
25
- {
26
- "file": "02-basic-commands.md",
27
- "title": "Basic Commands"
28
- },
29
- {
30
- "file": "03-cli-reference.md",
31
- "title": "CLI Reference"
32
- },
33
- {
34
- "file": "04-cli-quick-reference.md",
35
- "title": "CLI Quick Reference"
36
- }
37
- ]
38
- },
39
- {
40
- "file": "03-service",
41
- "title": "Running a Service",
42
- "children": [
43
- {
44
- "file": "01-start-a-new-service.md",
45
- "title": "Start a New Service"
46
- },
47
- {
48
- "file": "02-delete-a-service.md",
49
- "title": "Delete a Service"
50
- }
51
- ]
52
- },
53
- {
54
- "file": "04-web",
55
- "title": "Managing Websites",
56
- "children": [
57
- {
58
- "file": "01-create-a-website.md",
59
- "title": "Create a Website"
60
- },
61
- {
62
- "file": "02-list-websites.md",
63
- "title": "List Websites"
64
- },
65
- {
66
- "file": "03-delete-a-website.md",
67
- "title": "Delete a Website"
68
- }
69
- ]
70
- },
71
- {
72
- "file": "05-subdomain",
73
- "title": "Managing Subdomains",
74
- "children": [
75
- {
76
- "file": "01-create-a-subdomain.md",
77
- "title": "Create a Subdomain"
78
- },
79
- {
80
- "file": "02-list-subdomains.md",
81
- "title": "List Subdomains"
82
- },
83
- {
84
- "file": "03-delete-a-subdomain.md",
85
- "title": "Delete a Subdomain"
86
- }
87
- ]
88
- },
89
- {
90
- "file": "06-ssl",
91
- "title": "Managing SSL",
92
- "children": [
93
- {
94
- "file": "01-renew-an-ssl-certificate.md",
95
- "title": "Renew an SSL Certificate"
96
- }
97
- ]
98
- },
99
- {
100
- "file": "07-mail",
101
- "title": "Managing Mail",
102
- "children": [
103
- {
104
- "file": "01-create-a-mail-account.md",
105
- "title": "Create a Mail Account"
106
- },
107
- {
108
- "file": "02-delete-a-mail-account.md",
109
- "title": "Delete a Mail Account"
110
- },
111
- {
112
- "file": "03-list-mail-accounts.md",
113
- "title": "List Mail Accounts"
114
- },
115
- {
116
- "file": "04-change-account-password.md",
117
- "title": "Change Account Password"
118
- }
119
- ]
120
- }
121
- ],
122
2
  "backend": [
123
3
  {
124
4
  "file": "01-overview",
@@ -329,12 +209,20 @@
329
209
  "title": "Database",
330
210
  "children": [
331
211
  {
332
- "file": "01-database-connection.md",
333
- "title": "Database Connection"
212
+ "file": "01-getting-started.md",
213
+ "title": "Getting Started"
214
+ },
215
+ {
216
+ "file": "02-basics.md",
217
+ "title": "Query Basics"
334
218
  },
335
219
  {
336
- "file": "02-using-mysql.md",
337
- "title": "Using MySQL"
220
+ "file": "03-advanced.md",
221
+ "title": "Advanced Queries"
222
+ },
223
+ {
224
+ "file": "04-migrations.md",
225
+ "title": "Code-First Migrations"
338
226
  }
339
227
  ]
340
228
  },
@@ -375,6 +263,10 @@
375
263
  {
376
264
  "file": "06-odac-login-forms.md",
377
265
  "title": "Odac Login Forms (Zero-Config)"
266
+ },
267
+ {
268
+ "file": "07-magic-links.md",
269
+ "title": "Magic Links"
378
270
  }
379
271
  ]
380
272
  },
package/eslint.config.mjs CHANGED
@@ -6,49 +6,7 @@ import prettierConfig from 'eslint-config-prettier'
6
6
 
7
7
  export default defineConfig([
8
8
  {
9
- files: ['core/**/*.js', 'watchdog/**/*.js', 'server/**/*.js', 'cli/**/*.js'],
10
- ignores: ['server/src/Odac.js'],
11
- languageOptions: {
12
- globals: {
13
- ...globals.node,
14
- Odac: 'readonly',
15
- __: 'readonly'
16
- },
17
- sourceType: 'script'
18
- },
19
- plugins: {
20
- js,
21
- prettier: prettierPlugin
22
- },
23
- rules: {
24
- ...js.configs.recommended.rules,
25
- ...prettierConfig.rules,
26
- 'prettier/prettier': 'error'
27
- }
28
- },
29
- {
30
- files: ['server/src/Odac.js'],
31
- languageOptions: {
32
- globals: {
33
- ...globals.node,
34
- log: 'readonly',
35
- __: 'readonly'
36
- },
37
- sourceType: 'script'
38
- },
39
- plugins: {
40
- js,
41
- prettier: prettierPlugin
42
- },
43
- rules: {
44
- ...js.configs.recommended.rules,
45
- ...prettierConfig.rules,
46
- 'prettier/prettier': 'error'
47
- }
48
- },
49
- {
50
- files: ['framework/**/*.js'],
51
- ignores: ['framework/web/**/*.js'],
9
+ files: ['src/**/*.js'],
52
10
  languageOptions: {
53
11
  globals: {
54
12
  ...globals.node,
@@ -68,7 +26,7 @@ export default defineConfig([
68
26
  }
69
27
  },
70
28
  {
71
- files: ['framework/web/**/*.js'],
29
+ files: ['client/**/*.js'],
72
30
  languageOptions: {
73
31
  globals: {...globals.browser},
74
32
  sourceType: 'module'
@@ -79,8 +37,8 @@ export default defineConfig([
79
37
  }
80
38
  },
81
39
  {
82
- files: ['web/**/*.js'],
83
- ignores: ['web/public/**/*.js'],
40
+ files: ['template/**/*.js'],
41
+ ignores: ['template/public/**/*.js'],
84
42
  languageOptions: {
85
43
  globals: {
86
44
  ...globals.node,
@@ -99,7 +57,7 @@ export default defineConfig([
99
57
  }
100
58
  },
101
59
  {
102
- files: ['web/public/**/*.js'],
60
+ files: ['template/public/**/*.js'],
103
61
  languageOptions: {
104
62
  globals: {
105
63
  ...globals.browser,
package/package.json CHANGED
@@ -7,7 +7,7 @@
7
7
  "email": "mail@emre.red",
8
8
  "url": "https://emre.red"
9
9
  },
10
- "version": "1.0.0",
10
+ "version": "1.1.0",
11
11
  "license": "MIT",
12
12
  "engines": {
13
13
  "node": ">=18.0.0"
@@ -18,12 +18,16 @@
18
18
  "main": "index.js",
19
19
  "repository": {
20
20
  "type": "git",
21
- "url": "https://github.com/odac-run/framework.git"
21
+ "url": "https://github.com/odac-run/odac.js.git"
22
22
  },
23
23
  "dependencies": {
24
24
  "axios": "^1.7.9",
25
25
  "bcrypt": "^5.1.1",
26
- "mysql2": "^3.6.5"
26
+ "knex": "^3.1.0",
27
+ "lmdb": "^3.4.4",
28
+ "mysql2": "^3.16.0",
29
+ "pg": "^8.16.3",
30
+ "redis": "^5.10.0"
27
31
  },
28
32
  "devDependencies": {
29
33
  "@eslint/js": "^9.33.0",
@@ -42,7 +46,8 @@
42
46
  "jest": "^30.0.5",
43
47
  "lint-staged": "^16.1.5",
44
48
  "prettier": "3.6.2",
45
- "semantic-release": "^25.0.2"
49
+ "semantic-release": "^25.0.2",
50
+ "sqlite3": "^5.1.7"
46
51
  },
47
52
  "scripts": {
48
53
  "lint": "eslint .",