juxscript 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 (61) hide show
  1. package/README.md +292 -0
  2. package/bin/cli.js +149 -0
  3. package/lib/adapters/base-adapter.js +35 -0
  4. package/lib/adapters/index.js +33 -0
  5. package/lib/adapters/mysql-adapter.js +65 -0
  6. package/lib/adapters/postgres-adapter.js +70 -0
  7. package/lib/adapters/sqlite-adapter.js +56 -0
  8. package/lib/components/app.ts +124 -0
  9. package/lib/components/button.ts +136 -0
  10. package/lib/components/card.ts +205 -0
  11. package/lib/components/chart.ts +125 -0
  12. package/lib/components/code.ts +242 -0
  13. package/lib/components/container.ts +282 -0
  14. package/lib/components/data.ts +105 -0
  15. package/lib/components/docs-data.json +1211 -0
  16. package/lib/components/error-handler.ts +285 -0
  17. package/lib/components/footer.ts +146 -0
  18. package/lib/components/header.ts +167 -0
  19. package/lib/components/hero.ts +170 -0
  20. package/lib/components/import.ts +430 -0
  21. package/lib/components/input.ts +175 -0
  22. package/lib/components/layout.ts +113 -0
  23. package/lib/components/list.ts +392 -0
  24. package/lib/components/main.ts +111 -0
  25. package/lib/components/menu.ts +170 -0
  26. package/lib/components/modal.ts +216 -0
  27. package/lib/components/nav.ts +136 -0
  28. package/lib/components/node.ts +200 -0
  29. package/lib/components/reactivity.js +104 -0
  30. package/lib/components/script.ts +152 -0
  31. package/lib/components/sidebar.ts +168 -0
  32. package/lib/components/style.ts +129 -0
  33. package/lib/components/table.ts +279 -0
  34. package/lib/components/tabs.ts +191 -0
  35. package/lib/components/theme.ts +97 -0
  36. package/lib/components/view.ts +174 -0
  37. package/lib/jux.ts +203 -0
  38. package/lib/layouts/default.css +260 -0
  39. package/lib/layouts/default.jux +8 -0
  40. package/lib/layouts/figma.css +334 -0
  41. package/lib/layouts/figma.jux +0 -0
  42. package/lib/layouts/notion.css +258 -0
  43. package/lib/styles/base-theme.css +186 -0
  44. package/lib/styles/dark-theme.css +144 -0
  45. package/lib/styles/global.css +1131 -0
  46. package/lib/styles/light-theme.css +144 -0
  47. package/lib/styles/tokens/dark.css +86 -0
  48. package/lib/styles/tokens/light.css +86 -0
  49. package/lib/themes/dark.css +86 -0
  50. package/lib/themes/light.css +86 -0
  51. package/lib/utils/path-resolver.js +23 -0
  52. package/machinery/compiler.js +262 -0
  53. package/machinery/doc-generator.js +160 -0
  54. package/machinery/generators/css.js +128 -0
  55. package/machinery/generators/html.js +108 -0
  56. package/machinery/imports.js +155 -0
  57. package/machinery/server.js +185 -0
  58. package/machinery/validators/file-validator.js +123 -0
  59. package/machinery/watcher.js +148 -0
  60. package/package.json +58 -0
  61. package/types/globals.d.ts +16 -0
package/README.md ADDED
@@ -0,0 +1,292 @@
1
+ # *JUX* Authoring UI's in pure javascript.
2
+
3
+ > Build beautiful, reactive UI's using only javascript. **No markup required.**
4
+ > A clean authorship layer capable of being taught to non-developers, strong enough to be used by real developers.
5
+
6
+ >> **jux** challenges the idea that markup based systems are the most efficient way to author web applications.
7
+ ## AI friendly - **use less tokens!**
8
+
9
+
10
+ ```diff
11
+ - Markup is expensive!
12
+ Have you ever considered the energy requirements to ship chunks of HTML markup like Vue or React components?
13
+ ```
14
+
15
+ - [ ] Build a code calculator (token calculator)
16
+ > As a user, I want to be efficient with the LLM's. I want my code to be clean. I want to avoid repeating myself. I ultimately, want to write less code. Less code is better code.
17
+
18
+ ## Ecosystem friendly (NPM)
19
+
20
+ > Because it is just javascript, .jux files work with any npm package you want to throw at it with standard ESM syntax.
21
+
22
+ ## GETTING STARTED
23
+
24
+
25
+ > install
26
+ `npm i juxscript`
27
+
28
+ >> Your IDE must be setup to read .jux files as .js files. In VS Code, this is do in the `.vscode/settings.json`. Here is an example.
29
+
30
+ ```json
31
+ //.vscode/settings.json
32
+ {
33
+ "files.associations": {
34
+ "*.jux": "javascript",
35
+ "*.juxt": "javascript"
36
+ },
37
+ "javascript.validate.enable": false,
38
+ "[javascript]": {
39
+ "editor.defaultFormatter": "vscode.typescript-language-features"
40
+ }
41
+ }
42
+ ```
43
+
44
+ > building and serving the `examples` project directory.
45
+ `npm run dev`
46
+
47
+ > building only (the `examples` project directory)
48
+ `npm run build:examples`
49
+
50
+ # GOAL: Eliminating the Markup System of building UI's.
51
+ > HTML markup is the equivalent of still writing in `C` ro `C++` despite the availability of more functional levels of abstraction like `python`.
52
+ > To remove the requirement for markup, **jux** must address the utility of markup.
53
+
54
+ ## Layouts
55
+
56
+ ### Grid System
57
+ - [ ] Laying out JUX files in a rational way.
58
+ > Develop a higher-level layout javascript grammar for building layouts and palettes of style using *ZERO markup*.
59
+
60
+ - [ ] A Partial View Strategy
61
+ >>> As a dev I may need to load partial pages/components into other pages. This should avoid noisy wrappers and ideally be just javascript imports of jux objects.
62
+
63
+ ### Components
64
+
65
+ We have constructed the most helpful components for building 99% of core business/saas web applications, that present beautifully on the page and are created purely with javascript.
66
+ > https://www.shadcn-vue.com/docs/components
67
+ > Run time slots.. Example, `table.slot(columnName -> object);`
68
+
69
+ # Backend
70
+
71
+ ### Routing & Views
72
+ - [X] Subviews architecture
73
+ - [X] Nested routing support
74
+ - [X] View composition
75
+ - [X] Layout system
76
+ - [X] User views
77
+ - [X] `profile.jux` - User
78
+
79
+ ### Server Docs/Documentation.
80
+
81
+ - [ ] Should *jux* ship with a server at all? Or merely for demos.
82
+
83
+ > What is the server even doing? Is it merely a matter of understanding that JUX builder/compilation gives you a mirror of how you setup your project. It can be as flat or deep as you would like.
84
+
85
+ > *At this point*, we are speaking almost 100% towards UI interfaces.
86
+ > Servers should be 'loosely coupled.'. This allows for a standard.
87
+ >>> In the future, we could introduce an *auto api* feature, similar to *next.js* api heuristic for folders and files.
88
+ ---
89
+
90
+
91
+ [ **@jesseallenrxtrail -> Discuss.** ]
92
+
93
+ ### Chore: Commit to building more in package directory.
94
+
95
+ > We can experiment with our example project, using a simple `express` server, running. What third-party integration do we need?
96
+ - [ ] Chore: Cleanup our `machinery/server.js` to separate concerns and build our actual endpoints we need to build software here.
97
+
98
+ #### Alternative Chore: package this for `npmjs.org`, combine with our existing project, re-author frontend in *jux*.
99
+
100
+ - [ ] Alternative Chore: package this for `npmjs.org` and ship it.
101
+ - [ ] Start a brand new project.
102
+ - [ ] Build the server aspect in `flask` (already done).
103
+ - [ ] Author the frontend in **jux** instead of **vue**.
104
+
105
+ > This may require us to move our 'complilation error module', `lib/components/error-handler.ts` to the front-end for display without a 'server call' at all?
106
+ - [ ] Chore for this? This takes more analysis, discussion.
107
+
108
+ > How and what will we wire for backend and what **jux** components will we support for working with backends.
109
+ - [ X ] SQL Database (query component)
110
+ - [ ] Chore: This is currently supported. Needs revisiting and testing
111
+ > API's
112
+ - [ ] Base payload creater and consumer as a **jux** component. *jux.api*??
113
+
114
+ ---
115
+
116
+ # Jux Configs
117
+
118
+ > Jux configs.. `jux.config.js` what should this entail?
119
+ Currently it looks like this:
120
+ - [ ] Chore: **WHAT IS ACTUALLY BEING USED AND WHERE?**
121
+
122
+ ```javascript
123
+ export default {
124
+ projectRoot: __dirname,
125
+ distDir: path.join(__dirname, 'dist'),
126
+ build: {
127
+ minify: false
128
+ },
129
+
130
+ database: {
131
+ // Default connection
132
+ default: 'sqlite',
133
+
134
+ // Named connections
135
+ connections: {
136
+ sqlite: {
137
+ driver: 'sqlite',
138
+ database: path.join(__dirname, 'db', 'jux.db')
139
+ },
140
+ ... // props to feed drivers for mysql/postgres
141
+ }
142
+ },
143
+
144
+ server: {
145
+ port: process.env.PORT || 3000,
146
+ host: process.env.HOST || 'localhost'
147
+ },
148
+
149
+ };
150
+ ```
151
+ **As a user, I will want to control where JUX sends queries.**
152
+ **As a user, I may want to specify a default proxy for backend. I may also want to specify, explicitly, URL's to hit other service endpoints**
153
+ - [ ] Escape hatch (backend API proxy vs default)
154
+ - [ ] As a user, I may want to run my own server for backend requests/proxies.
155
+ - [ ] As a user, I may want to hit multiple endpoints in the same JUX file.
156
+
157
+ ---
158
+
159
+ # CORE
160
+
161
+ ### STATIC SERVING (your local file system, s3 bucket static sites, any host that allows serving etc...)
162
+ **Should JUX function, as a 100% client side app (STATIC WEBSITES)**
163
+ - [ ] Chore: Discovery... I think the answer is yes. I think what might be missing is bundling.
164
+ ![alt text](local.png)
165
+
166
+ ---
167
+ ## REACTIVITY LAYER.
168
+ ### Goal for Reactivity
169
+ > We are shooting for better than nasty amounts of javascript, not as sophisticated as vue/react.
170
+
171
+ > As a user, I want changes in state of my objects to be consumable by other objects on the page so they can paint changes and interface with each other seamlessly.
172
+ - [ ] Build a basic `v-model` strategy in pure javascript. Different than jquery, javascript etc.
173
+ - [ ] Build standard eventing systems, listeners and emitters on components where they make sense.
174
+ `
175
+ # ROADMAP
176
+
177
+ ### KEY UI PAGES
178
+ - [ ] JWT Authentication (`authJWT`) (demo `examples` only)
179
+ - [ ] Token generation (demo `examples` only)
180
+ - [ ] Token validation middleware (demo `examples` only)
181
+ - [ ] Refresh token logic (demo `examples` only)
182
+ - [ ] *Login system PAGES! Ships with `vendor` assets like lib files/layouts etc.*
183
+ - [ ] Login
184
+ - [ ] Logout
185
+ - [ ] Profile
186
+
187
+
188
+ ### Additional Features
189
+ - [ ] API documentation
190
+ - [ ] Testing setup
191
+
192
+
193
+
194
+
195
+
196
+ ### LAYOUT NOTES...
197
+ # Jux Standard Layout Regions
198
+
199
+ - [ ] CHORE : REVISIT! I think maybe we ship with our figma, notion, default etc. But then document how to build and reuse a .jux page as a layout using the `jux.layout` class.
200
+
201
+ ## The Opinionated Structure
202
+
203
+ **jux** layout pages provide a **strongly opinionated** layout structure that covers 99% of web applications. Every page includes these regions by default, even if not all are used. This eliminates layout decisions and provides consistent target containers across your entire application.
204
+
205
+ These are free to use, but you can roll your own just as easily.
206
+ You can find the documentation for each of these layouts here:
207
+
208
+ - Notion
209
+ - Jux
210
+ - Figma
211
+ - Other?
212
+
213
+
214
+ ### Sample Documenation
215
+ ```markdown
216
+ #app
217
+ ├── #appheader // Top navigation bar - your app's primary navigation
218
+ │ ├── #appheader-logo // Brand identity - logo, app name
219
+ │ ├── #appheader-nav // Primary navigation menu
220
+ │ └── #appheader-actions // User actions - profile, notifications, settings
221
+
222
+ ├── #appsubheader // Contextual navigation - changes based on current page
223
+ │ ├── #appsubheader-breadcrumbs // Where am I? - navigation path
224
+ │ ├── #appsubheader-tabs // Page-level tabs - views within the same context
225
+ │ └── #appsubheader-actions // Context-specific actions - filters, search, "New Item"
226
+
227
+ ├── #appsidebar // Left sidebar - persistent navigation or filters
228
+ │ ├── #appsidebar-header // Sidebar title or search
229
+ │ ├── #appsidebar-content // Main sidebar content - nav tree, filters
230
+ │ └── #appsidebar-footer // Sidebar actions or status
231
+
232
+ ├── #appmain // Your content lives here - the star of the show
233
+
234
+ ├── #appaside // Right sidebar - contextual help, metadata
235
+ │ ├── #appaside-header // Aside title
236
+ │ ├── #appaside-content // Properties panel, help docs, related items
237
+ │ └── #appaside-footer // Aside actions
238
+
239
+ ├── #appfooter // Bottom of the page
240
+ │ ├── #appfooter-content // Footer navigation, social links
241
+ │ └── #appfooter-legal // Copyright, terms, privacy
242
+
243
+ └── #appmodal // Overlays and dialogs
244
+ ├── #appmodal-backdrop // Click-to-close background
245
+ └── #appmodal-container
246
+ ├── #appmodal-header // Dialog title and close button
247
+ ├── #appmodal-content // Dialog body
248
+ └── #appmodal-footer // Dialog actions - Cancel, Save, etc.
249
+
250
+ ```
251
+
252
+ ## Why consider a Jux layout?
253
+
254
+ **Predictability**: Every page has the same structure. Developers know exactly where to render components.
255
+
256
+ **Flexibility**: Don't need a sidebar? Leave it empty. CSS handles visibility automatically.
257
+
258
+ **Composability**: Components can render to any region with `.render('#target-id')`.
259
+
260
+ **Convention over Configuration**: No layout decisions needed. Just start building.
261
+
262
+ ## Common Patterns
263
+
264
+ ```javascript
265
+ // Dashboard with sidebar navigation
266
+ const sideNav = jux.nav('side-nav', { /* config */ });
267
+ await sideNav.render('#appsidebar-content');
268
+
269
+ const dashboard = jux.dashboard('main-dashboard', { /* config */ });
270
+ await dashboard.render('#appmain');
271
+
272
+ // Settings page with tabs
273
+ const tabs = jux.tabs('settings-tabs', { /* config */ });
274
+ await tabs.render('#appsubheader-tabs');
275
+
276
+ const content = jux.container('settings-content', { /* config */ });
277
+ await content.render('#appmain');
278
+
279
+ // Detail view with metadata sidebar
280
+ const detail = jux.detail('item-detail', { /* config */ });
281
+ await detail.render('#appmain');
282
+
283
+ const metadata = jux.metadata('item-meta', { /* config */ });
284
+ await metadata.render('#appaside-content');
285
+
286
+ // Modal dialog
287
+ const dialog = jux.dialog('confirm-dialog', { /* config */ });
288
+ await dialog.render('#appmodal-content');
289
+
290
+ // Auto-render to default container (component chooses based on type)
291
+ const hero = jux.hero('page-hero', { /* config */ });
292
+ await hero.render(); // Automatically renders to #appmain
package/bin/cli.js ADDED
@@ -0,0 +1,149 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { compileJuxFile, copyLibToOutput, copyProjectAssets } from '../machinery/compiler.js';
4
+ import { generateDocs } from '../machinery/doc-generator.js';
5
+ import { start } from '../machinery/server.js';
6
+ import path from 'path';
7
+ import fs from 'fs';
8
+ import { fileURLToPath } from 'url';
9
+
10
+ const __filename = fileURLToPath(import.meta.url);
11
+ const __dirname = path.dirname(__filename);
12
+
13
+ const command = process.argv[2];
14
+ const projectRoot = process.cwd();
15
+ const distDir = path.join(projectRoot, 'dist');
16
+
17
+ function findJuxFiles(dir, fileList = []) {
18
+ const files = fs.readdirSync(dir);
19
+
20
+ files.forEach(file => {
21
+ const filePath = path.join(dir, file);
22
+ const stat = fs.statSync(filePath);
23
+
24
+ if (stat.isDirectory()) {
25
+ if (file !== 'node_modules' && file !== 'dist' && file !== '.git') {
26
+ findJuxFiles(filePath, fileList);
27
+ }
28
+ } else if (file.endsWith('.jux')) {
29
+ fileList.push(filePath);
30
+ }
31
+ });
32
+
33
+ return fileList;
34
+ }
35
+
36
+ async function loadConfig() {
37
+ const configPath = path.join(projectRoot, 'jux.config.js');
38
+
39
+ if (fs.existsSync(configPath)) {
40
+ try {
41
+ const configModule = await import(configPath);
42
+ return configModule.default || {};
43
+ } catch (err) {
44
+ console.warn('⚠️ Could not load jux.config.js:', err.message);
45
+ return {};
46
+ }
47
+ }
48
+
49
+ return {};
50
+ }
51
+
52
+ async function buildProject(isServe = false) {
53
+ console.log('🔨 Building JUX project...\n');
54
+
55
+ try {
56
+ if (fs.existsSync(distDir)) {
57
+ fs.rmSync(distDir, { recursive: true, force: true });
58
+ }
59
+ fs.mkdirSync(distDir, { recursive: true });
60
+
61
+ // Step 1: Generate documentation FIRST
62
+ console.log('📚 Generating documentation...');
63
+ try {
64
+ await generateDocs(projectRoot);
65
+ console.log('✅ Documentation generated\n');
66
+ } catch (error) {
67
+ console.warn('⚠️ Failed to generate docs:', error.message);
68
+ }
69
+
70
+ // Step 2: Copy lib/ to dist/lib/ (includes docs-data.json)
71
+ await copyLibToOutput(projectRoot, distDir);
72
+
73
+ // Step 3: Copy project assets (CSS, JS) from project root
74
+ await copyProjectAssets(projectRoot, distDir);
75
+
76
+ // Step 4: Find and compile project .jux files
77
+ const projectJuxFiles = findJuxFiles(projectRoot);
78
+ console.log(`Found ${projectJuxFiles.length} project .jux file(s)\n`);
79
+
80
+ for (const file of projectJuxFiles) {
81
+ try {
82
+ await compileJuxFile(file, { distDir, projectRoot, isServe });
83
+ } catch (err) {
84
+ console.error(`Error compiling ${file}:`, err.message);
85
+ }
86
+ }
87
+
88
+ // Step 5: Find and compile vendor layout .jux files
89
+ const libRoot = path.resolve(projectRoot, '../lib');
90
+ const layoutsDir = path.join(libRoot, 'layouts');
91
+
92
+ if (fs.existsSync(layoutsDir)) {
93
+ console.log('\n📐 Compiling vendor layouts...');
94
+ const vendorJuxFiles = findJuxFiles(layoutsDir);
95
+ console.log(`Found ${vendorJuxFiles.length} vendor layout(s)\n`);
96
+
97
+ for (const file of vendorJuxFiles) {
98
+ try {
99
+ const relPath = path.relative(libRoot, file);
100
+
101
+ await compileJuxFile(file, {
102
+ distDir: path.join(distDir, 'lib'),
103
+ projectRoot: libRoot,
104
+ isServe
105
+ });
106
+
107
+ console.log(` ✓ Compiled: ${relPath}`);
108
+ } catch (err) {
109
+ console.error(`Error compiling vendor layout ${file}:`, err.message);
110
+ }
111
+ }
112
+ }
113
+
114
+ console.log(`\n✅ Built ${projectJuxFiles.length} project file(s) + layouts\n`);
115
+ } catch (err) {
116
+ console.error('❌ Build error:', err.message);
117
+ process.exit(1);
118
+ }
119
+ }
120
+
121
+ (async () => {
122
+ if (command === 'build') {
123
+ await buildProject(false);
124
+ console.log(`✅ Build complete: ${distDir}`);
125
+
126
+ } else if (command === 'serve') {
127
+ // Build first
128
+ await buildProject(true); // isServe = true
129
+
130
+ // Start server with watcher
131
+ const config = await loadConfig();
132
+ await start(3000, config);
133
+
134
+ } else {
135
+ console.log(`
136
+ JUX CLI - A JavaScript UX authorship platform
137
+
138
+ Usage:
139
+ npx jux build Compile all .jux files to HTML/CSS/JS
140
+ npx jux serve [port] Start dev server with hot reload (default: 3000)
141
+ Builds automatically if dist/ doesn't exist
142
+
143
+ Examples:
144
+ npx jux build Build for production
145
+ npx jux serve Start dev server (builds if needed)
146
+ npx jux serve 8080 Start on port 8080
147
+ `);
148
+ }
149
+ })();
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Base adapter interface that all database adapters must implement
3
+ */
4
+ class BaseAdapter {
5
+ constructor(config) {
6
+ this.config = config;
7
+ this.connection = null;
8
+ }
9
+
10
+ async connect() {
11
+ throw new Error('connect() must be implemented');
12
+ }
13
+
14
+ async disconnect() {
15
+ throw new Error('disconnect() must be implemented');
16
+ }
17
+
18
+ async query(sql, params = []) {
19
+ throw new Error('query() must be implemented');
20
+ }
21
+
22
+ async execute(sql, params = []) {
23
+ throw new Error('execute() must be implemented');
24
+ }
25
+
26
+ async transaction(callback) {
27
+ throw new Error('transaction() must be implemented');
28
+ }
29
+
30
+ escape(value) {
31
+ throw new Error('escape() must be implemented');
32
+ }
33
+ }
34
+
35
+ module.exports = { BaseAdapter };
@@ -0,0 +1,33 @@
1
+ const { SQLiteAdapter } = require('./sqlite-adapter');
2
+ const { MySQLAdapter } = require('./mysql-adapter');
3
+ const { PostgresAdapter } = require('./postgres-adapter');
4
+
5
+ /**
6
+ * Adapter factory - creates the appropriate adapter based on config
7
+ */
8
+ function createAdapter(type, config) {
9
+ switch (type.toLowerCase()) {
10
+ case 'sqlite':
11
+ case 'sqlite3':
12
+ return new SQLiteAdapter(config);
13
+
14
+ case 'mysql':
15
+ case 'mariadb':
16
+ return new MySQLAdapter(config);
17
+
18
+ case 'postgres':
19
+ case 'postgresql':
20
+ case 'pg':
21
+ return new PostgresAdapter(config);
22
+
23
+ default:
24
+ throw new Error(`Unsupported database type: ${type}`);
25
+ }
26
+ }
27
+
28
+ module.exports = {
29
+ createAdapter,
30
+ SQLiteAdapter,
31
+ MySQLAdapter,
32
+ PostgresAdapter
33
+ };
@@ -0,0 +1,65 @@
1
+ const { BaseAdapter } = require('./base-adapter');
2
+ const mysql = require('mysql2/promise');
3
+
4
+ class MySQLAdapter extends BaseAdapter {
5
+ async connect() {
6
+ this.connection = await mysql.createConnection({
7
+ host: this.config.host || 'localhost',
8
+ port: this.config.port || 3306,
9
+ user: this.config.user,
10
+ password: this.config.password,
11
+ database: this.config.database
12
+ });
13
+ return this.connection;
14
+ }
15
+
16
+ async disconnect() {
17
+ if (this.connection) {
18
+ await this.connection.end();
19
+ this.connection = null;
20
+ }
21
+ }
22
+
23
+ async query(sql, params = []) {
24
+ if (!this.connection) await this.connect();
25
+
26
+ try {
27
+ const [rows] = await this.connection.execute(sql, params);
28
+ return { data: rows, rowCount: rows.length };
29
+ } catch (error) {
30
+ throw new Error(`MySQL query error: ${error.message}`);
31
+ }
32
+ }
33
+
34
+ async execute(sql, params = []) {
35
+ if (!this.connection) await this.connect();
36
+
37
+ try {
38
+ const [result] = await this.connection.execute(sql, params);
39
+ return {
40
+ rowCount: result.affectedRows,
41
+ lastInsertId: result.insertId
42
+ };
43
+ } catch (error) {
44
+ throw new Error(`MySQL execute error: ${error.message}`);
45
+ }
46
+ }
47
+
48
+ async transaction(callback) {
49
+ await this.connection.beginTransaction();
50
+ try {
51
+ const result = await callback(this);
52
+ await this.connection.commit();
53
+ return result;
54
+ } catch (error) {
55
+ await this.connection.rollback();
56
+ throw error;
57
+ }
58
+ }
59
+
60
+ escape(value) {
61
+ return mysql.escape(value);
62
+ }
63
+ }
64
+
65
+ module.exports = { MySQLAdapter };
@@ -0,0 +1,70 @@
1
+ const { BaseAdapter } = require('./base-adapter');
2
+ const { Pool } = require('pg');
3
+
4
+ class PostgresAdapter extends BaseAdapter {
5
+ async connect() {
6
+ this.connection = new Pool({
7
+ host: this.config.host || 'localhost',
8
+ port: this.config.port || 5432,
9
+ user: this.config.user,
10
+ password: this.config.password,
11
+ database: this.config.database
12
+ });
13
+ return this.connection;
14
+ }
15
+
16
+ async disconnect() {
17
+ if (this.connection) {
18
+ await this.connection.end();
19
+ this.connection = null;
20
+ }
21
+ }
22
+
23
+ async query(sql, params = []) {
24
+ if (!this.connection) await this.connect();
25
+
26
+ try {
27
+ const result = await this.connection.query(sql, params);
28
+ return { data: result.rows, rowCount: result.rowCount };
29
+ } catch (error) {
30
+ throw new Error(`Postgres query error: ${error.message}`);
31
+ }
32
+ }
33
+
34
+ async execute(sql, params = []) {
35
+ if (!this.connection) await this.connect();
36
+
37
+ try {
38
+ const result = await this.connection.query(sql, params);
39
+ return {
40
+ rowCount: result.rowCount,
41
+ lastInsertId: result.rows[0]?.id
42
+ };
43
+ } catch (error) {
44
+ throw new Error(`Postgres execute error: ${error.message}`);
45
+ }
46
+ }
47
+
48
+ async transaction(callback) {
49
+ const client = await this.connection.connect();
50
+ try {
51
+ await client.query('BEGIN');
52
+ const result = await callback(this);
53
+ await client.query('COMMIT');
54
+ return result;
55
+ } catch (error) {
56
+ await client.query('ROLLBACK');
57
+ throw error;
58
+ } finally {
59
+ client.release();
60
+ }
61
+ }
62
+
63
+ escape(value) {
64
+ if (value === null) return 'NULL';
65
+ if (typeof value === 'number') return value.toString();
66
+ return `'${value.toString().replace(/'/g, "''")}'`;
67
+ }
68
+ }
69
+
70
+ module.exports = { PostgresAdapter };
@@ -0,0 +1,56 @@
1
+ const { BaseAdapter } = require('./base-adapter');
2
+ const Database = require('better-sqlite3');
3
+
4
+ class SQLiteAdapter extends BaseAdapter {
5
+ async connect() {
6
+ this.connection = new Database(this.config.database || ':memory:');
7
+ return this.connection;
8
+ }
9
+
10
+ async disconnect() {
11
+ if (this.connection) {
12
+ this.connection.close();
13
+ this.connection = null;
14
+ }
15
+ }
16
+
17
+ async query(sql, params = []) {
18
+ if (!this.connection) await this.connect();
19
+
20
+ try {
21
+ const stmt = this.connection.prepare(sql);
22
+ const rows = stmt.all(...params);
23
+ return { data: rows, rowCount: rows.length };
24
+ } catch (error) {
25
+ throw new Error(`SQLite query error: ${error.message}`);
26
+ }
27
+ }
28
+
29
+ async execute(sql, params = []) {
30
+ if (!this.connection) await this.connect();
31
+
32
+ try {
33
+ const stmt = this.connection.prepare(sql);
34
+ const result = stmt.run(...params);
35
+ return {
36
+ rowCount: result.changes,
37
+ lastInsertId: result.lastInsertRowid
38
+ };
39
+ } catch (error) {
40
+ throw new Error(`SQLite execute error: ${error.message}`);
41
+ }
42
+ }
43
+
44
+ async transaction(callback) {
45
+ const transaction = this.connection.transaction(callback);
46
+ return transaction();
47
+ }
48
+
49
+ escape(value) {
50
+ if (value === null) return 'NULL';
51
+ if (typeof value === 'number') return value.toString();
52
+ return `'${value.toString().replace(/'/g, "''")}'`;
53
+ }
54
+ }
55
+
56
+ module.exports = { SQLiteAdapter };