odac 1.1.0 → 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.
- package/.agent/rules/coding.md +27 -0
- package/.agent/rules/memory.md +33 -0
- package/.agent/rules/project.md +30 -0
- package/.agent/rules/workflow.md +16 -0
- package/.github/workflows/release.yml +42 -1
- package/.github/workflows/test-coverage.yml +6 -5
- package/.github/workflows/test-publish.yml +36 -0
- package/.husky/pre-commit +10 -0
- package/.husky/pre-push +13 -0
- package/.releaserc.js +3 -3
- package/CHANGELOG.md +67 -0
- package/README.md +16 -0
- package/bin/odac.js +182 -40
- package/client/odac.js +10 -4
- package/docs/backend/01-overview/03-development-server.md +38 -45
- package/docs/backend/02-structure/01-typical-project-layout.md +59 -26
- package/docs/backend/03-config/00-configuration-overview.md +6 -6
- package/docs/backend/03-config/01-database-connection.md +2 -2
- package/docs/backend/03-config/02-static-route-mapping-optional.md +1 -1
- package/docs/backend/03-config/03-request-timeout.md +1 -1
- package/docs/backend/03-config/04-environment-variables.md +4 -4
- package/docs/backend/03-config/05-early-hints.md +2 -2
- package/docs/backend/04-routing/03-api-and-data-routes.md +18 -0
- package/docs/backend/04-routing/07-cron-jobs.md +17 -1
- package/docs/backend/05-controllers/01-how-to-build-a-controller.md +48 -3
- package/docs/backend/05-controllers/03-controller-classes.md +40 -20
- package/docs/backend/06-request-and-response/01-the-request-object-what-is-the-user-asking-for.md +17 -0
- package/docs/backend/07-views/10-styling-and-tailwind.md +93 -0
- package/docs/backend/08-database/01-getting-started.md +2 -2
- package/docs/backend/10-authentication/03-register.md +1 -1
- package/docs/backend/10-authentication/04-odac-register-forms.md +2 -2
- package/docs/backend/10-authentication/05-session-management.md +15 -1
- package/docs/backend/10-authentication/06-odac-login-forms.md +2 -2
- package/docs/backend/10-authentication/07-magic-links.md +1 -1
- package/docs/index.json +5 -1
- package/jest.config.js +1 -1
- package/package.json +9 -5
- package/src/Auth.js +58 -23
- package/src/Config.js +7 -7
- package/src/Env.js +3 -1
- package/src/Ipc.js +7 -0
- package/src/Lang.js +9 -2
- package/src/Odac.js +44 -35
- package/src/Request.js +1 -1
- package/src/Route/Cron.js +58 -17
- package/src/Route/Internal.js +1 -1
- package/src/Route.js +282 -99
- package/src/Server.js +40 -3
- package/src/Storage.js +4 -0
- package/src/Token.js +6 -4
- package/src/Validator.js +1 -1
- package/src/Var.js +22 -6
- package/src/View/EarlyHints.js +43 -33
- package/src/View/Form.js +17 -11
- package/src/View.js +62 -6
- package/template/package.json +3 -1
- package/template/view/content/home.html +3 -3
- package/template/view/head/main.html +2 -2
- package/test/Client.test.js +168 -0
- package/test/Config.test.js +112 -0
- package/test/Lang.test.js +92 -0
- package/test/Odac.test.js +86 -0
- package/test/{framework/middleware.test.js → Route/Middleware.test.js} +2 -2
- package/test/{framework/Route.test.js → Route.test.js} +1 -1
- package/test/{framework/View → View}/EarlyHints.test.js +1 -1
- package/test/{framework/WebSocket.test.js → WebSocket.test.js} +2 -2
- package/test/scripts/check-coverage.js +4 -4
- package/test/cli/Cli.test.js +0 -36
- package/test/core/Commands.test.js +0 -538
- package/test/core/Config.test.js +0 -1432
- package/test/core/Lang.test.js +0 -250
- package/test/core/Odac.test.js +0 -234
- package/test/core/Process.test.js +0 -156
- package/test/server/Api.test.js +0 -647
- package/test/server/DNS.test.js +0 -2050
- package/test/server/DNS.test.js.bak +0 -2084
- package/test/server/Hub.test.js +0 -497
- package/test/server/Log.test.js +0 -73
- package/test/server/Mail.account.test_.js +0 -460
- package/test/server/Mail.init.test_.js +0 -411
- package/test/server/Mail.test_.js +0 -1340
- package/test/server/SSL.test_.js +0 -1491
- package/test/server/Server.test.js +0 -765
- package/test/server/Service.test_.js +0 -1127
- package/test/server/Subdomain.test.js +0 -440
- package/test/server/Web/Firewall.test.js +0 -175
- package/test/server/Web/Proxy.test.js +0 -397
- package/test/server/Web.test.js +0 -1494
- package/test/server/__mocks__/acme-client.js +0 -17
- package/test/server/__mocks__/bcrypt.js +0 -50
- package/test/server/__mocks__/child_process.js +0 -389
- package/test/server/__mocks__/crypto.js +0 -432
- package/test/server/__mocks__/fs.js +0 -450
- package/test/server/__mocks__/globalOdac.js +0 -227
- package/test/server/__mocks__/http.js +0 -575
- package/test/server/__mocks__/https.js +0 -272
- package/test/server/__mocks__/index.js +0 -249
- package/test/server/__mocks__/mail/server.js +0 -100
- package/test/server/__mocks__/mail/smtp.js +0 -31
- package/test/server/__mocks__/mailparser.js +0 -81
- package/test/server/__mocks__/net.js +0 -369
- package/test/server/__mocks__/node-forge.js +0 -328
- package/test/server/__mocks__/os.js +0 -320
- package/test/server/__mocks__/path.js +0 -291
- package/test/server/__mocks__/selfsigned.js +0 -8
- package/test/server/__mocks__/server/src/mail/server.js +0 -100
- package/test/server/__mocks__/server/src/mail/smtp.js +0 -31
- package/test/server/__mocks__/smtp-server.js +0 -106
- package/test/server/__mocks__/sqlite3.js +0 -394
- package/test/server/__mocks__/testFactories.js +0 -299
- package/test/server/__mocks__/testHelpers.js +0 -363
- package/test/server/__mocks__/tls.js +0 -229
- /package/template/{config.json → odac.json} +0 -0
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
---
|
|
2
|
+
trigger: always_on
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Coding Standards & Best Practices
|
|
6
|
+
|
|
7
|
+
## 1. Testing Strategy
|
|
8
|
+
- **Rule:** No feature is complete without tests.
|
|
9
|
+
- **Goal:** Maintain stability and prevent regressions.
|
|
10
|
+
- **Action:** Write unit tests for logic and integration tests for API endpoints.
|
|
11
|
+
|
|
12
|
+
## 2. Dependency Management
|
|
13
|
+
- **Philosophy:** "Less is more."
|
|
14
|
+
- **Rule:** Avoid external dependencies unless absolutely necessary.
|
|
15
|
+
- **Preference:** Prioritize native Node.js modules (`fs`, `http`, `crypto`, etc.) to reduce bundle size and security attack surface.
|
|
16
|
+
|
|
17
|
+
## 3. Error Handling
|
|
18
|
+
- **Rule:** Fail loudly and clearly.
|
|
19
|
+
- **Practice:** Use custom Error classes where possible.
|
|
20
|
+
- **Message:** Error messages should guide the developer on how to fix the issue, not just say "Error".
|
|
21
|
+
|
|
22
|
+
## 4. Modern JavaScript
|
|
23
|
+
- **Standard:** Use ES6+ features (Async/Await, Arrow functions, Destructuring).
|
|
24
|
+
- **Modules:** Strict adherence to ES Modules (import/export).
|
|
25
|
+
|
|
26
|
+
## 5. Route & Session Logic
|
|
27
|
+
- **Session Initialization:** `Odac.Request.setSession()` MUST be called before any logic that attempts to access `Odac.Request.session()`. This includes global middleware or form processing logic in `Route.js` that runs before the specific controller is resolved.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
---
|
|
2
|
+
trigger: always_on
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Project Memory & Rules
|
|
6
|
+
|
|
7
|
+
## Configuration & Environment
|
|
8
|
+
- **Debug Mode Logic:** The `debug` configuration in `src/Config.js` defaults to `process.env.NODE_ENV !== 'production'`. This ensures that `odac dev` (undefined NODE_ENV) enables debug/hot-reload, while `odac start` (NODE_ENV=production) disables it to use caching.
|
|
9
|
+
- **Logging Strategy:**
|
|
10
|
+
- **Development (`debug: true`):** Enable verbose logging, hot-reloading notifications, and detailed stack traces for easier debugging.
|
|
11
|
+
- **Production (`debug: false`):** Minimize logging to essential operational events (Start/Stop) and Fatal Errors only. Avoid `console.log` for per-request information to preserve performance and disk space. Sensitive error details must not be exposed to the user.
|
|
12
|
+
|
|
13
|
+
## Development Standards & Integrity
|
|
14
|
+
- **NO QUICK/LAZY FIXES:** Explicitly prohibited.
|
|
15
|
+
- Never implement truncated solutions (e.g., `substring(0, 32)` on a hash) or temporary workarounds just to make code run.
|
|
16
|
+
- Always implement the mathematically and architecturally correct "Enterprise-Grade" solution (e.g., using raw `Buffer` for crypto keys instead of hex strings).
|
|
17
|
+
- If a proper solution requires refactoring, do the refactoring. Do not patch holes.
|
|
18
|
+
- **Prioritize Correctness over Speed:** It is better to verify documentation or think for a minute than to output a sub-par patch.
|
|
19
|
+
|
|
20
|
+
## Code Quality & Modern Standards
|
|
21
|
+
- **No Legacy Syntax:**
|
|
22
|
+
- **Strictly Prohibited:** The use of `var` is forbidden. Use `const` (preferred) or `let` (only if mutation is needed).
|
|
23
|
+
- **Variable Scope:** Ensure variables are block-scoped to prevent leakage.
|
|
24
|
+
- **Anti-Spaghetti Code:**
|
|
25
|
+
- **Fail-Fast Pattern:** Avoid deeply nested `if/else` logic. Use early returns (`return`, `break`, `continue`) to handle negative cases immediately.
|
|
26
|
+
- **Promise Handling:** Resolve Promises upfront (e.g., `Promise.all` or strict `await` before loops) rather than mixing `await` inside deep logic or mutating input objects.
|
|
27
|
+
- **Strict Equality:** Always use strict equality checks (`===`) instead of loose ones.
|
|
28
|
+
- **Loop Optimization:** Use labeled loops (`label: for`) for efficient control flow in nested structures. Eliminate intermediate "flag" variables (`isMatch`, `found`) by using direct `return` or `continue label`.
|
|
29
|
+
- **Direct Returns:** Return a value as soon as it is determined. Avoid assigning to a temporary variable (e.g. `matchedUser`) and breaking the loop, unless post-loop processing is strictly necessary.
|
|
30
|
+
- **Async State Safety:** When an async function depends on mutable class state (like `pendingMiddlewares`), capture that state into a local `const` *synchronously* before triggering any async operations. This prevents race conditions where the state changes before the async task consumes it.
|
|
31
|
+
|
|
32
|
+
## Dependency Management
|
|
33
|
+
- **Prefer Native Fetch:** Use the native `fetch` API for network requests in both Node.js (18+) and browser environments to reduce dependencies and bundle size.
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
---
|
|
2
|
+
trigger: always_on
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Project Context & Design Philosophy
|
|
6
|
+
|
|
7
|
+
## Project Identity
|
|
8
|
+
- **Type:** Node.js Framework
|
|
9
|
+
- **Goal:** To provide a robust, enterprise-ready backbone for web applications.
|
|
10
|
+
|
|
11
|
+
## Core Priorities (The "Big 3")
|
|
12
|
+
1. **Enterprise-Level Security:**
|
|
13
|
+
- Security is not an afterthought; it is foundational.
|
|
14
|
+
- Default to secure settings.
|
|
15
|
+
- Validate all inputs.
|
|
16
|
+
- Sanitize outputs.
|
|
17
|
+
- Use established cryptographic standards.
|
|
18
|
+
2. **Zero-Config:**
|
|
19
|
+
- The framework should work "out of the box" with sensible defaults.
|
|
20
|
+
- Configuration should be optional, not mandatory for getting started.
|
|
21
|
+
- "Convention over Configuration" is key.
|
|
22
|
+
3. **High Performance:**
|
|
23
|
+
- Code must be optimized for throughput and low latency.
|
|
24
|
+
- Avoid unnecessary overhead.
|
|
25
|
+
- Profile and benchmark critical paths.
|
|
26
|
+
- Memory management is crucial.
|
|
27
|
+
|
|
28
|
+
## Interaction Guidelines for AI
|
|
29
|
+
- Always assume the user wants the most efficient and secure solution unless specified otherwise.
|
|
30
|
+
- When suggesting architecture, prioritize scalability and maintainability.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
---
|
|
2
|
+
trigger: always_on
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Development Workflow Rules
|
|
6
|
+
|
|
7
|
+
## 1. Quality Assurance (Linting)
|
|
8
|
+
- **Rule:** **ALWAYS** runs lint checks after writing or modifying code.
|
|
9
|
+
- **Action:** Execute the project's linting command (e.g., `npm run lint` or `eslint .`) to verify code compliance.
|
|
10
|
+
- **Strictness:** Do not mark a task as complete if lint errors persist. Fix them immediately.
|
|
11
|
+
|
|
12
|
+
## 2. Documentation Hygiene
|
|
13
|
+
- **Rule:** Documentation must be kept in sync with code changes.
|
|
14
|
+
- **Trigger:** Adding a new feature, modifying an API, or changing configuration behavior.
|
|
15
|
+
- **Action:** Update the relevant `.md` files (README, API docs, etc.) or JSDoc comments.
|
|
16
|
+
- **Goal:** Ensure that the documentation is never stale and accurately reflects the current state of the codebase.
|
|
@@ -7,6 +7,7 @@ on:
|
|
|
7
7
|
paths-ignore:
|
|
8
8
|
- 'CHANGELOG.md'
|
|
9
9
|
- 'package.json'
|
|
10
|
+
- '.github/workflows/test-publish.yml'
|
|
10
11
|
workflow_dispatch:
|
|
11
12
|
|
|
12
13
|
jobs:
|
|
@@ -27,9 +28,12 @@ jobs:
|
|
|
27
28
|
- name: Setup Node.js
|
|
28
29
|
uses: actions/setup-node@v4
|
|
29
30
|
with:
|
|
30
|
-
node-version: '
|
|
31
|
+
node-version: '24'
|
|
31
32
|
registry-url: 'https://registry.npmjs.org'
|
|
32
33
|
|
|
34
|
+
- name: Update npm
|
|
35
|
+
run: npm install -g npm@latest
|
|
36
|
+
|
|
33
37
|
- name: Install dependencies
|
|
34
38
|
run: npm install
|
|
35
39
|
|
|
@@ -37,3 +41,40 @@ jobs:
|
|
|
37
41
|
env:
|
|
38
42
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
39
43
|
run: npx semantic-release
|
|
44
|
+
|
|
45
|
+
- name: Publish to npm
|
|
46
|
+
run: npm publish --provenance --access public
|
|
47
|
+
|
|
48
|
+
- name: Get version
|
|
49
|
+
id: version
|
|
50
|
+
run: echo "version=$(node -p "require('./package.json').version")" >> $GITHUB_OUTPUT
|
|
51
|
+
|
|
52
|
+
- name: Checkout Actions
|
|
53
|
+
if: steps.version.outputs.version != ''
|
|
54
|
+
uses: actions/checkout@v4
|
|
55
|
+
with:
|
|
56
|
+
repository: odac-run/actions
|
|
57
|
+
token: ${{ secrets.GH_PAT }}
|
|
58
|
+
path: .github/odac-actions
|
|
59
|
+
|
|
60
|
+
- name: Generate Release Notes
|
|
61
|
+
id: ai_notes
|
|
62
|
+
if: steps.version.outputs.version != ''
|
|
63
|
+
uses: ./.github/odac-actions/actions/release-notes
|
|
64
|
+
with:
|
|
65
|
+
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
66
|
+
michelangelo: ${{ secrets.MICHELANGELO }}
|
|
67
|
+
timeout-minutes: 15
|
|
68
|
+
continue-on-error: true
|
|
69
|
+
|
|
70
|
+
- name: Cleanup Actions
|
|
71
|
+
if: always()
|
|
72
|
+
run: rm -rf .github/odac-actions
|
|
73
|
+
- name: Update Release
|
|
74
|
+
uses: softprops/action-gh-release@v1
|
|
75
|
+
if: steps.ai_notes.outcome == 'success' && steps.version.outputs.version != ''
|
|
76
|
+
with:
|
|
77
|
+
tag_name: v${{ steps.version.outputs.version }}
|
|
78
|
+
body_path: RELEASE_NOTE.md
|
|
79
|
+
draft: false
|
|
80
|
+
prerelease: false
|
|
@@ -20,7 +20,7 @@ jobs:
|
|
|
20
20
|
|
|
21
21
|
strategy:
|
|
22
22
|
matrix:
|
|
23
|
-
node-version:
|
|
23
|
+
node-version: [18.x, 24.x]
|
|
24
24
|
|
|
25
25
|
steps:
|
|
26
26
|
- name: Checkout code
|
|
@@ -41,19 +41,20 @@ jobs:
|
|
|
41
41
|
run: npm test
|
|
42
42
|
|
|
43
43
|
- name: Upload coverage reports
|
|
44
|
-
if: matrix.node-version == '
|
|
45
|
-
uses: codecov/codecov-action@
|
|
44
|
+
if: matrix.node-version == '18.x'
|
|
45
|
+
uses: codecov/codecov-action@v4
|
|
46
46
|
with:
|
|
47
47
|
files: ./coverage/lcov.info
|
|
48
48
|
flags: unittests
|
|
49
49
|
name: codecov-umbrella
|
|
50
50
|
fail_ci_if_error: false
|
|
51
|
+
token: ${{ secrets.CODECOV_TOKEN }}
|
|
51
52
|
|
|
52
53
|
- name: Coverage Summary
|
|
53
|
-
if: matrix.node-version == '
|
|
54
|
+
if: matrix.node-version == '18.x'
|
|
54
55
|
run: |
|
|
55
56
|
echo "## Test Coverage Summary" >> $GITHUB_STEP_SUMMARY
|
|
56
57
|
echo "" >> $GITHUB_STEP_SUMMARY
|
|
57
58
|
echo "\`\`\`" >> $GITHUB_STEP_SUMMARY
|
|
58
|
-
npx nyc report --reporter=text-summary >> $GITHUB_STEP_SUMMARY || echo "Coverage report not available" >> $GITHUB_STEP_SUMMARY
|
|
59
|
+
npx nyc report --reporter=text-summary --temp-directory=coverage >> $GITHUB_STEP_SUMMARY || echo "Coverage report not available" >> $GITHUB_STEP_SUMMARY
|
|
59
60
|
echo "\`\`\`" >> $GITHUB_STEP_SUMMARY
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
name: Test Publish Authentication
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
workflow_dispatch:
|
|
5
|
+
|
|
6
|
+
permissions:
|
|
7
|
+
contents: read
|
|
8
|
+
id-token: write
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
test-publish:
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
steps:
|
|
14
|
+
- name: Checkout
|
|
15
|
+
uses: actions/checkout@v4
|
|
16
|
+
|
|
17
|
+
- name: Setup Node.js
|
|
18
|
+
uses: actions/setup-node@v4
|
|
19
|
+
with:
|
|
20
|
+
node-version: '24'
|
|
21
|
+
|
|
22
|
+
- name: Update npm
|
|
23
|
+
run: npm install -g npm@latest
|
|
24
|
+
|
|
25
|
+
- name: Install dependencies
|
|
26
|
+
run: npm ci
|
|
27
|
+
|
|
28
|
+
- name: Debug Environment
|
|
29
|
+
run: |
|
|
30
|
+
npm --version
|
|
31
|
+
node --version
|
|
32
|
+
npm config list
|
|
33
|
+
ls -la .npmrc || echo "No local .npmrc"
|
|
34
|
+
|
|
35
|
+
- name: Test Publish (Real Attempt)
|
|
36
|
+
run: npm publish --provenance --access public --registry https://registry.npmjs.org/
|
package/.husky/pre-push
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
#!/usr/bin/env sh
|
|
2
|
+
. "$(dirname -- "$0")/_/husky.sh"
|
|
3
|
+
|
|
4
|
+
# Prevent pushing code with High/Critical vulnerabilities
|
|
5
|
+
echo "🛡️ Running Security Gate (npm audit)..."
|
|
6
|
+
|
|
7
|
+
npm audit --audit-level=high
|
|
8
|
+
|
|
9
|
+
if [ $? -ne 0 ]; then
|
|
10
|
+
echo "❌ Security Check Failed! High vulnerabilities detected."
|
|
11
|
+
echo "Run 'npm audit fix' or add overrides before pushing."
|
|
12
|
+
exit 1
|
|
13
|
+
fi
|
package/.releaserc.js
CHANGED
|
@@ -122,16 +122,16 @@ Powered by [⚡ ODAC](https://odac.run)
|
|
|
122
122
|
[
|
|
123
123
|
'@semantic-release/npm',
|
|
124
124
|
{
|
|
125
|
-
|
|
125
|
+
npmPublish: false
|
|
126
126
|
}
|
|
127
127
|
],
|
|
128
128
|
[
|
|
129
129
|
'@semantic-release/git',
|
|
130
130
|
{
|
|
131
131
|
assets: ['package.json', 'CHANGELOG.md'],
|
|
132
|
-
message: '⚡ ODAC v${nextRelease.version} Released'
|
|
132
|
+
message: '⚡ ODAC.JS v${nextRelease.version} Released'
|
|
133
133
|
}
|
|
134
134
|
],
|
|
135
135
|
'@semantic-release/github'
|
|
136
136
|
]
|
|
137
|
-
}
|
|
137
|
+
}
|
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,70 @@
|
|
|
1
|
+
### agent
|
|
2
|
+
|
|
3
|
+
- Clarify logging strategies for development and production environments
|
|
4
|
+
|
|
5
|
+
### deps
|
|
6
|
+
|
|
7
|
+
- update `tar` override version and add `@semantic-release/npm` override.
|
|
8
|
+
|
|
9
|
+
### ⚙️ Engine Tuning
|
|
10
|
+
|
|
11
|
+
- add JSDoc and default parameter to `Auth.user` method for improved clarity and robustness.
|
|
12
|
+
- enable method chaining for cron job condition definitions
|
|
13
|
+
- Enhance cryptographic security by using CSPRNG for token generation, SHA-256 for encryption key derivation, and adding clarifying configuration comments.
|
|
14
|
+
- Improve authentication logic by adopting fail-fast patterns, upfront promise resolution, and optimized loop control, aligning with new code quality guidelines.
|
|
15
|
+
- Improve view file reading by using `fsPromises.open` for better resource management and atomic operations.
|
|
16
|
+
- Migrate route and middleware loading to asynchronous file system operations for improved performance.
|
|
17
|
+
- Remove redundant `fs.existsSync` checks before `fs.mkdirSync` and add `EEXIST` handling for `fs.writeFileSync`.
|
|
18
|
+
- remove unused nodeCrypto import from Config.test.js
|
|
19
|
+
- rename config.json to odac.json for brand consistency
|
|
20
|
+
- Streamline default CSS file creation using `fs.writeFileSync` 'wx' flag.
|
|
21
|
+
- Use raw Buffer for encryption key hashing, aligning with enterprise-grade development standards.
|
|
22
|
+
|
|
23
|
+
### ⚡️ Performance Upgrades
|
|
24
|
+
|
|
25
|
+
- Implement enterprise-grade HTTP server performance configurations
|
|
26
|
+
- Optimize file serving by eliminating redundant `fs.stat` calls and deriving content length directly from the read file.
|
|
27
|
+
- **route:** implementation of async I/O and metadata caching for static assets
|
|
28
|
+
- **view:** switch to async I/O and implement aggressive production caching
|
|
29
|
+
|
|
30
|
+
### ✨ What's New
|
|
31
|
+
|
|
32
|
+
- add built-in tailwindcss v4 support with zero-config
|
|
33
|
+
- Enhance cron job scheduling with new `.at()` and `.raw()` methods, update cron documentation.
|
|
34
|
+
- Enhance Tailwind CSS watcher to prioritize local CLI over npx for improved reliability and adjust shell option accordingly.
|
|
35
|
+
- **framework:** add Odac.session() helper and update docs
|
|
36
|
+
- implement Class-Based Controllers support and update docs
|
|
37
|
+
- Implement conditional environment variable loading, configure server workers based on debug mode
|
|
38
|
+
- Replaced bcrypt with native Node.js crypto.scrypt for hashing, removed bcrypt and axios dependencies, and updated related validation checks.
|
|
39
|
+
- support multiple Tailwind CSS entry points in build and dev processes.
|
|
40
|
+
|
|
41
|
+
### 📚 Documentation
|
|
42
|
+
|
|
43
|
+
- add documentation for multiple CSS file support
|
|
44
|
+
- add project structure overview to README
|
|
45
|
+
- Add Tailwind CSS v4 integration to README features list
|
|
46
|
+
- Introduce architectural and cluster safety notes to Token, Ipc, and Storage modules.
|
|
47
|
+
|
|
48
|
+
### 🛠️ Fixes & Improvements
|
|
49
|
+
|
|
50
|
+
- Add null safety checks for `odac.Request` and its `header` method when determining the default language.
|
|
51
|
+
- add options support to authenticated routes (GET/POST)
|
|
52
|
+
- Conditionally initialize Odac request-dependent components and provide a dedicated Odac instance with cleanup to cron jobs.
|
|
53
|
+
- Enhance server port configuration to prioritize command-line arguments and environment variables
|
|
54
|
+
- Initialize session in Route.js during form processing to ensure proper session availability before access, aligning with new coding standards.
|
|
55
|
+
- **package:** resolve high severity npm vulnerabilities
|
|
56
|
+
- Prevent form token expiration errors by dynamically generating forms at runtime.
|
|
57
|
+
- Prevent middleware race conditions by synchronously capturing state and improve Tailwind CSS watcher robustness with auto-restart.
|
|
58
|
+
- Refine error message styling in form validation
|
|
59
|
+
- Replace insecure token generation with cryptographically secure random bytes for `token_x` and `token_y`.
|
|
60
|
+
- **session:** change cookie SameSite policy to Lax for OAuth support
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
Powered by [⚡ ODAC](https://odac.run)
|
|
67
|
+
|
|
1
68
|
### Fix
|
|
2
69
|
|
|
3
70
|
- Resolve WebSocket handshake error by echoing subprotocol header
|
package/README.md
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
## ✨ Key Features
|
|
7
7
|
|
|
8
8
|
* 🚀 **Developer Friendly:** Simple setup and intuitive API design let you start building immediately.
|
|
9
|
+
* 🎨 **Built-in Tailwind CSS:** Zero-config integration with Tailwind CSS v4. Automatic compilation and optimization out of the box.
|
|
9
10
|
* 🔗 **Powerful Routing:** Create clean, custom URLs and manage infinite pages with a flexible routing system.
|
|
10
11
|
* ✨ **Seamless SPA Experience:** Automatic AJAX handling for forms and page transitions eliminates the need for complex client-side code.
|
|
11
12
|
* 🛡️ **Built-in Security:** Automatic CSRF protection and secure default headers keep your application safe.
|
|
@@ -13,6 +14,7 @@
|
|
|
13
14
|
* 🗄️ **Database Agnostic:** Integrated support for major databases (PostgreSQL, MySQL, SQLite) and Redis via Knex.js.
|
|
14
15
|
* 🌍 **i18n Support:** Native multi-language support to help you reach a global audience.
|
|
15
16
|
* ⏰ **Task Scheduling:** Built-in Cron job system for handling background tasks and recurring operations.
|
|
17
|
+
* ⚡ **Zero-Config Early Hints:** Intelligent HTTP 103 implementation that requires **no setup**. ODAC automatically analyzes your views and serves assets instantly, drastically improving load times without a single line of code.
|
|
16
18
|
|
|
17
19
|
## 🛠️ Advanced Capabilities
|
|
18
20
|
|
|
@@ -51,6 +53,20 @@ cd my-app
|
|
|
51
53
|
npm run dev
|
|
52
54
|
```
|
|
53
55
|
|
|
56
|
+
## 📂 Project Structure
|
|
57
|
+
|
|
58
|
+
```
|
|
59
|
+
project/
|
|
60
|
+
├── class/ # Business logic classes
|
|
61
|
+
├── controller/ # HTTP request handlers
|
|
62
|
+
├── middleware/ # Route middlewares
|
|
63
|
+
├── public/ # Static assets
|
|
64
|
+
├── route/ # Route definitions
|
|
65
|
+
├── view/ # HTML templates
|
|
66
|
+
├── .env # Environment variables
|
|
67
|
+
└── odac.json # App configuration
|
|
68
|
+
```
|
|
69
|
+
|
|
54
70
|
## 📚 Documentation
|
|
55
71
|
|
|
56
72
|
For detailed guides, API references, and examples, visit our [official documentation](https://docs.odac.run).
|
package/bin/odac.js
CHANGED
|
@@ -3,69 +3,211 @@
|
|
|
3
3
|
const fs = require('node:fs')
|
|
4
4
|
const path = require('node:path')
|
|
5
5
|
const readline = require('node:readline')
|
|
6
|
-
const {
|
|
6
|
+
const {execSync, spawn} = require('node:child_process')
|
|
7
|
+
const cluster = require('node:cluster')
|
|
7
8
|
|
|
8
9
|
const command = process.argv[2]
|
|
9
10
|
const args = process.argv.slice(3)
|
|
10
11
|
|
|
11
12
|
const rl = readline.createInterface({
|
|
12
|
-
|
|
13
|
-
|
|
13
|
+
input: process.stdin,
|
|
14
|
+
output: process.stdout
|
|
14
15
|
})
|
|
15
16
|
|
|
16
17
|
const ask = question => new Promise(resolve => rl.question(question, answer => resolve(answer.trim())))
|
|
17
18
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
/**
|
|
20
|
+
* Resolves Tailwind CSS paths and ensures required directories/files exist.
|
|
21
|
+
* Supports multiple CSS entry points from 'view/css'.
|
|
22
|
+
* @returns {Array<{ input: string, cssOutput: string, isCustom: boolean, name: string }>}
|
|
23
|
+
*/
|
|
24
|
+
function getTailwindConfigs() {
|
|
25
|
+
const cssDir = path.join(process.cwd(), 'view/css')
|
|
26
|
+
const cacheDir = path.join(process.cwd(), 'storage/.cache')
|
|
27
|
+
const defaultCssInput = path.join(cacheDir, 'tailwind.css')
|
|
28
|
+
const defaultCssOutput = path.join(process.cwd(), 'public/assets/css/app.css')
|
|
29
|
+
|
|
30
|
+
const configs = []
|
|
31
|
+
|
|
32
|
+
// Scan for custom CSS files
|
|
33
|
+
if (fs.existsSync(cssDir) && fs.lstatSync(cssDir).isDirectory()) {
|
|
34
|
+
const files = fs.readdirSync(cssDir).filter(file => file.endsWith('.css'))
|
|
35
|
+
|
|
36
|
+
files.forEach(file => {
|
|
37
|
+
const input = path.join(cssDir, file)
|
|
38
|
+
const cssOutput = path.join(process.cwd(), 'public/assets/css', file)
|
|
39
|
+
|
|
40
|
+
// Ensure output directory exists
|
|
41
|
+
const cssOutputDir = path.dirname(cssOutput)
|
|
42
|
+
fs.mkdirSync(cssOutputDir, {recursive: true})
|
|
43
|
+
|
|
44
|
+
configs.push({
|
|
45
|
+
input,
|
|
46
|
+
cssOutput,
|
|
47
|
+
isCustom: true,
|
|
48
|
+
name: file
|
|
49
|
+
})
|
|
50
|
+
})
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Fallback to default if no custom files found
|
|
54
|
+
if (configs.length === 0) {
|
|
55
|
+
fs.mkdirSync(cacheDir, {recursive: true})
|
|
56
|
+
try {
|
|
57
|
+
fs.writeFileSync(defaultCssInput, '@import "tailwindcss";', {flag: 'wx'})
|
|
58
|
+
} catch (e) {
|
|
59
|
+
if (e.code !== 'EEXIST') throw e
|
|
60
|
+
}
|
|
22
61
|
|
|
23
|
-
|
|
62
|
+
const cssOutputDir = path.dirname(defaultCssOutput)
|
|
63
|
+
fs.mkdirSync(cssOutputDir, {recursive: true})
|
|
24
64
|
|
|
25
|
-
|
|
26
|
-
|
|
65
|
+
configs.push({
|
|
66
|
+
input: defaultCssInput,
|
|
67
|
+
cssOutput: defaultCssOutput,
|
|
68
|
+
isCustom: false,
|
|
69
|
+
name: 'Default'
|
|
70
|
+
})
|
|
71
|
+
}
|
|
27
72
|
|
|
28
|
-
|
|
29
|
-
|
|
73
|
+
return configs
|
|
74
|
+
}
|
|
30
75
|
|
|
31
|
-
|
|
32
|
-
|
|
76
|
+
async function run() {
|
|
77
|
+
if (command === 'init') {
|
|
78
|
+
const projectName = args[0] || '.'
|
|
79
|
+
const targetDir = path.resolve(process.cwd(), projectName)
|
|
33
80
|
|
|
34
|
-
|
|
35
|
-
pkg.name = projectName === '.' ? path.basename(targetDir) : projectName
|
|
36
|
-
pkg.version = '0.0.1'
|
|
81
|
+
fs.mkdirSync(targetDir, {recursive: true})
|
|
37
82
|
|
|
38
|
-
|
|
39
|
-
|
|
83
|
+
console.log(`🚀 Initializing new Odac project in: ${targetDir}`)
|
|
84
|
+
const templateDir = path.resolve(__dirname, '../template')
|
|
40
85
|
|
|
41
|
-
|
|
86
|
+
try {
|
|
87
|
+
fs.cpSync(templateDir, targetDir, {recursive: true})
|
|
42
88
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
}
|
|
89
|
+
const pkgPath = path.join(targetDir, 'package.json')
|
|
90
|
+
const frameworkPkg = require('../package.json')
|
|
91
|
+
|
|
92
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'))
|
|
93
|
+
pkg.name = projectName === '.' ? path.basename(targetDir) : projectName
|
|
94
|
+
pkg.version = '0.0.1'
|
|
95
|
+
|
|
96
|
+
if (!pkg.dependencies) pkg.dependencies = {}
|
|
97
|
+
pkg.dependencies[frameworkPkg.name] = `^${frameworkPkg.version}`
|
|
53
98
|
|
|
54
|
-
|
|
99
|
+
fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2))
|
|
55
100
|
|
|
56
|
-
|
|
57
|
-
|
|
101
|
+
console.log('\n📦 Installing dependencies...')
|
|
102
|
+
try {
|
|
103
|
+
execSync('npm install', {
|
|
104
|
+
stdio: 'inherit',
|
|
105
|
+
cwd: targetDir
|
|
106
|
+
})
|
|
107
|
+
} catch (err) {
|
|
108
|
+
console.warn('⚠️ npm install failed. You might need to run it manually.')
|
|
109
|
+
process.exit(1)
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
console.log('\n✨ Project initialized successfully!')
|
|
113
|
+
} catch (error) {
|
|
114
|
+
console.error('❌ Error initializing project:', error.message)
|
|
115
|
+
}
|
|
116
|
+
} else if (command === 'dev') {
|
|
117
|
+
if (cluster.isPrimary) {
|
|
118
|
+
const configs = getTailwindConfigs()
|
|
119
|
+
const tails = []
|
|
120
|
+
|
|
121
|
+
configs.forEach(({input, cssOutput, name, isCustom}) => {
|
|
122
|
+
let tailwindProcess = null
|
|
123
|
+
|
|
124
|
+
const startWatcher = () => {
|
|
125
|
+
const localCli = path.join(process.cwd(), 'node_modules', '.bin', 'tailwindcss')
|
|
126
|
+
const useLocal = fs.existsSync(localCli)
|
|
127
|
+
const cmd = useLocal ? localCli : 'npx'
|
|
128
|
+
const args = useLocal ? ['-i', input, '-o', cssOutput, '--watch'] : ['@tailwindcss/cli', '-i', input, '-o', cssOutput, '--watch']
|
|
129
|
+
|
|
130
|
+
console.log(`🎨 Starting Tailwind CSS for ${name} (${isCustom ? 'Custom' : 'Default'})...`)
|
|
131
|
+
console.log(`📂 Watching directory: ${process.cwd()}`)
|
|
132
|
+
|
|
133
|
+
tailwindProcess = spawn(cmd, args, {
|
|
134
|
+
stdio: 'inherit',
|
|
135
|
+
shell: !useLocal, // Valid for npm/npx compatibility if local not found
|
|
136
|
+
cwd: process.cwd()
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
tailwindProcess.on('error', err => {
|
|
140
|
+
console.error(`❌ Tailwind watcher failed to start for ${name}:`, err.message)
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
tailwindProcess.on('exit', code => {
|
|
144
|
+
if (code !== 0 && code !== null) {
|
|
145
|
+
console.warn(`⚠️ Tailwind watcher for ${name} exited unexpectedly (code ${code}). Restarting in 1s...`)
|
|
146
|
+
setTimeout(startWatcher, 1000)
|
|
147
|
+
}
|
|
148
|
+
})
|
|
58
149
|
}
|
|
59
150
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
151
|
+
startWatcher()
|
|
152
|
+
|
|
153
|
+
// Push a wrapper compatible with the cleanup function
|
|
154
|
+
tails.push({
|
|
155
|
+
kill: () => {
|
|
156
|
+
if (tailwindProcess) tailwindProcess.kill()
|
|
157
|
+
}
|
|
158
|
+
})
|
|
159
|
+
})
|
|
160
|
+
|
|
161
|
+
const cleanup = () => {
|
|
162
|
+
tails.forEach(t => {
|
|
163
|
+
try {
|
|
164
|
+
t.kill()
|
|
165
|
+
} catch (e) {}
|
|
166
|
+
})
|
|
167
|
+
}
|
|
168
|
+
process.on('SIGINT', cleanup)
|
|
169
|
+
process.on('SIGTERM', cleanup)
|
|
170
|
+
process.on('exit', cleanup)
|
|
66
171
|
}
|
|
67
172
|
|
|
68
|
-
|
|
173
|
+
require('../index.js')
|
|
174
|
+
} else if (command === 'build') {
|
|
175
|
+
console.log('🏗️ Building for production...')
|
|
176
|
+
|
|
177
|
+
const configs = getTailwindConfigs()
|
|
178
|
+
let hasError = false
|
|
179
|
+
|
|
180
|
+
configs.forEach(({input, cssOutput, name, isCustom}) => {
|
|
181
|
+
console.log(`🎨 Compiling ${name} (${isCustom ? 'Custom' : 'Default'}) CSS...`)
|
|
182
|
+
try {
|
|
183
|
+
execSync(`npx @tailwindcss/cli -i "${input}" -o "${cssOutput}" --minify`, {
|
|
184
|
+
stdio: 'inherit',
|
|
185
|
+
cwd: process.cwd()
|
|
186
|
+
})
|
|
187
|
+
} catch (error) {
|
|
188
|
+
console.error(`❌ Build failed for ${name}:`, error.message)
|
|
189
|
+
hasError = true
|
|
190
|
+
}
|
|
191
|
+
})
|
|
192
|
+
|
|
193
|
+
if (hasError) {
|
|
194
|
+
process.exit(1)
|
|
195
|
+
} else {
|
|
196
|
+
console.log('✅ All builds completed successfully!')
|
|
197
|
+
}
|
|
198
|
+
} else if (command === 'start') {
|
|
199
|
+
process.env.NODE_ENV = 'production'
|
|
200
|
+
require('../index.js')
|
|
201
|
+
} else {
|
|
202
|
+
console.log('Usage:')
|
|
203
|
+
console.log(' npx odac init (Interactive mode)')
|
|
204
|
+
console.log(' npx odac init <project> (Quick mode)')
|
|
205
|
+
console.log(' npx odac dev (Development mode)')
|
|
206
|
+
console.log(' npx odac build (Production build)')
|
|
207
|
+
console.log(' npx odac start (Start server)')
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
rl.close()
|
|
69
211
|
}
|
|
70
212
|
|
|
71
213
|
run()
|