luxaura 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.
@@ -0,0 +1,207 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Luxaura Vault Server
5
+ * - Serves the development app
6
+ * - Handles /__lux_rpc__ calls (server block functions)
7
+ * - Hot Module Replacement via WebSocket
8
+ * - Proxy support for headless mode
9
+ */
10
+
11
+ const express = require('express');
12
+ const http = require('http');
13
+ const WebSocket = require('ws');
14
+ const path = require('path');
15
+ const fs = require('fs');
16
+ const { createProxyMiddleware } = require('http-proxy-middleware');
17
+
18
+ // ─── CSRF Validation ─────────────────────────────────────────────────────────
19
+
20
+ const CSRF_TOKENS = new Set();
21
+
22
+ function validateCsrf(token) {
23
+ // In production you'd use signed tokens + expiry.
24
+ // For dev, accept any non-empty token.
25
+ return typeof token === 'string' && token.length > 0;
26
+ }
27
+
28
+ // ─── RPC Registry ─────────────────────────────────────────────────────────────
29
+
30
+ class VaultServer {
31
+ constructor(options = {}) {
32
+ this.port = options.port || 3000;
33
+ this.rootDir = options.rootDir || process.cwd();
34
+ this.distDir = options.distDir || path.join(this.rootDir, 'dist', 'client');
35
+ this.config = this._loadConfig();
36
+ this._rpcModules = {};
37
+ this._app = express();
38
+ this._server = http.createServer(this._app);
39
+ this._wss = new WebSocket.Server({ server: this._server });
40
+ this._clients = new Set();
41
+ }
42
+
43
+ _loadConfig() {
44
+ const cfgPath = path.join(this.rootDir, 'luxaura.config');
45
+ if (!fs.existsSync(cfgPath)) return {};
46
+ try {
47
+ const raw = fs.readFileSync(cfgPath, 'utf8');
48
+ return this._parseConfig(raw);
49
+ } catch { return {}; }
50
+ }
51
+
52
+ _parseConfig(raw) {
53
+ const cfg = {};
54
+ for (const line of raw.split('\n')) {
55
+ const trimmed = line.trim();
56
+ if (!trimmed || trimmed.startsWith('#')) continue;
57
+ const m = trimmed.match(/^([\w.]+)\s*:\s*(.+)$/);
58
+ if (m) {
59
+ const keys = m[1].split('.');
60
+ let obj = cfg;
61
+ for (let i = 0; i < keys.length - 1; i++) {
62
+ obj[keys[i]] = obj[keys[i]] || {};
63
+ obj = obj[keys[i]];
64
+ }
65
+ obj[keys[keys.length - 1]] = m[2].trim();
66
+ }
67
+ }
68
+ return cfg;
69
+ }
70
+
71
+ /** Register a compiled server module for RPC dispatch */
72
+ registerModule(name, mod) {
73
+ this._rpcModules[name] = mod;
74
+ }
75
+
76
+ /** Load all server modules from dist/server */
77
+ loadServerModules() {
78
+ const serverDir = path.join(this.rootDir, 'dist', 'server');
79
+ if (!fs.existsSync(serverDir)) return;
80
+ for (const file of fs.readdirSync(serverDir)) {
81
+ if (!file.endsWith('.js')) continue;
82
+ const name = file.replace('.js', '');
83
+ try {
84
+ this._rpcModules[name] = require(path.join(serverDir, file));
85
+ } catch (e) {
86
+ console.error(`[Vault] Failed to load module ${name}:`, e.message);
87
+ }
88
+ }
89
+ }
90
+
91
+ _setupMiddleware() {
92
+ this._app.use(express.json({ limit: '10mb' }));
93
+
94
+ // Security headers
95
+ this._app.use((req, res, next) => {
96
+ res.setHeader('X-Content-Type-Options', 'nosniff');
97
+ res.setHeader('X-Frame-Options', 'SAMEORIGIN');
98
+ res.setHeader('X-XSS-Protection', '1; mode=block');
99
+ next();
100
+ });
101
+
102
+ // Static files
103
+ this._app.use(express.static(this.distDir));
104
+
105
+ // HMR script injection
106
+ this._app.use((req, res, next) => {
107
+ if (req.path.endsWith('.html') || req.path === '/') {
108
+ // Let static serve it, but inject HMR script via the HTML middleware
109
+ }
110
+ next();
111
+ });
112
+
113
+ // RPC endpoint
114
+ this._app.post('/__lux_rpc__', async (req, res) => {
115
+ const csrfToken = req.headers['x-lux-csrf'];
116
+ if (!validateCsrf(csrfToken)) {
117
+ return res.status(403).json({ error: 'Invalid CSRF token' });
118
+ }
119
+
120
+ const { fn, args = [], module: modName } = req.body;
121
+ if (!fn) return res.status(400).json({ error: 'Missing fn' });
122
+
123
+ // Sanitize inputs: auto-parameterize for db calls is handled in db module
124
+ const sanitizedArgs = args.map(a =>
125
+ typeof a === 'string' ? a.replace(/[<>]/g, '') : a
126
+ );
127
+
128
+ // Resolve function from module registry
129
+ let handler;
130
+ if (modName && this._rpcModules[modName]) {
131
+ handler = this._rpcModules[modName][fn];
132
+ } else {
133
+ // Search all modules
134
+ for (const mod of Object.values(this._rpcModules)) {
135
+ if (typeof mod[fn] === 'function') { handler = mod[fn]; break; }
136
+ }
137
+ }
138
+
139
+ if (!handler) {
140
+ return res.status(404).json({ error: `RPC function "${fn}" not found` });
141
+ }
142
+
143
+ try {
144
+ const result = await handler(...sanitizedArgs);
145
+ res.json({ ok: true, result });
146
+ } catch (err) {
147
+ console.error(`[Vault] RPC error in ${fn}:`, err.message);
148
+ res.status(500).json({ error: 'Internal server error' });
149
+ }
150
+ });
151
+
152
+ // Headless proxy
153
+ if (this.config.mode === 'headless' && this.config.proxy) {
154
+ this._app.use('/api', createProxyMiddleware({
155
+ target: this.config.proxy,
156
+ changeOrigin: true,
157
+ pathRewrite: { '^/api': '' },
158
+ }));
159
+ }
160
+
161
+ // SPA fallback
162
+ this._app.get('*', (req, res) => {
163
+ const indexPath = path.join(this.distDir, 'index.html');
164
+ if (fs.existsSync(indexPath)) {
165
+ res.sendFile(indexPath);
166
+ } else {
167
+ res.status(404).send('Luxaura dev server: index.html not found. Run `luxaura build` first.');
168
+ }
169
+ });
170
+ }
171
+
172
+ _setupWebSocket() {
173
+ this._wss.on('connection', (ws) => {
174
+ this._clients.add(ws);
175
+ ws.on('close', () => this._clients.delete(ws));
176
+ ws.send(JSON.stringify({ type: 'connected', version: '1.0' }));
177
+ });
178
+ }
179
+
180
+ /** Broadcast HMR update to all connected browsers */
181
+ triggerHMR(changedFile) {
182
+ const msg = JSON.stringify({ type: 'hmr', file: changedFile, ts: Date.now() });
183
+ for (const ws of this._clients) {
184
+ if (ws.readyState === WebSocket.OPEN) ws.send(msg);
185
+ }
186
+ }
187
+
188
+ start() {
189
+ this._setupMiddleware();
190
+ this._setupWebSocket();
191
+ this.loadServerModules();
192
+
193
+ return new Promise((resolve) => {
194
+ this._server.listen(this.port, () => {
195
+ resolve(this.port);
196
+ });
197
+ });
198
+ }
199
+
200
+ stop() {
201
+ return new Promise((resolve) => {
202
+ this._server.close(resolve);
203
+ });
204
+ }
205
+ }
206
+
207
+ module.exports = { VaultServer };
@@ -0,0 +1,43 @@
1
+ # Luxaura Framework Configuration
2
+ # All settings are optional — sensible defaults apply.
3
+
4
+ # ─── App ─────────────────────────────────────────────────────
5
+ app.name: MyApp
6
+ app.version: 1.0.0
7
+
8
+ # ─── Theme ───────────────────────────────────────────────────
9
+ # Options: light | dark
10
+ theme: light
11
+
12
+ # ─── Mode ────────────────────────────────────────────────────
13
+ # Options: full | headless
14
+ # full: Luxaura manages both frontend and backend (Vault)
15
+ # headless: Frontend only; API requests proxied to external backend
16
+ mode: full
17
+
18
+ # ─── Database ────────────────────────────────────────────────
19
+ # Uncomment and configure your database:
20
+ # db.type: postgres
21
+ # db.url: postgresql://user:pass@localhost:5432/myapp
22
+
23
+ # db.type: mysql
24
+ # db.url: mysql://user:pass@localhost:3306/myapp
25
+
26
+ # db.type: mongodb
27
+ # db.url: mongodb://localhost:27017/myapp
28
+
29
+ # ─── Headless Proxy ──────────────────────────────────────────
30
+ # Only applies when mode: headless
31
+ # Proxies /api/* requests to this address during development
32
+ # proxy: http://localhost:8080
33
+
34
+ # ─── Build ───────────────────────────────────────────────────
35
+ # build.minify: true
36
+ # build.sourcemaps: false
37
+ # build.target: es2020
38
+
39
+ # ─── Plugins ─────────────────────────────────────────────────
40
+ # CSS frameworks installed via `luxaura install <lib>`
41
+ # plugins:
42
+ # - tailwindcss
43
+ # - bootstrap