dzql 0.3.2 → 0.3.3

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 (2) hide show
  1. package/bin/cli.js +296 -0
  2. package/package.json +3 -2
package/bin/cli.js ADDED
@@ -0,0 +1,296 @@
1
+ #!/usr/bin/env bun
2
+
3
+ import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs';
4
+ import { resolve } from 'path';
5
+ import { DZQLCompiler } from '../src/compiler/compiler.js';
6
+
7
+ const command = process.argv[2];
8
+ const args = process.argv.slice(3);
9
+
10
+ switch (command) {
11
+ case 'create':
12
+ console.log('🚧 Create command coming soon');
13
+ console.log(`Would create project: ${args[0]}`);
14
+ break;
15
+ case 'dev':
16
+ console.log('🚧 Dev command coming soon');
17
+ break;
18
+ case 'db:up':
19
+ console.log('🚧 Database commands coming soon');
20
+ break;
21
+ case 'compile':
22
+ await runCompile(args);
23
+ break;
24
+ case '--version':
25
+ case '-v':
26
+ const pkg = await import('../package.json', { assert: { type: 'json' } });
27
+ console.log(pkg.default.version);
28
+ break;
29
+ default:
30
+ console.log(`
31
+ DZQL CLI
32
+
33
+ Usage:
34
+ dzql create <app-name> Create a new DZQL application
35
+ dzql dev Start development server
36
+ dzql db:up Start PostgreSQL database
37
+ dzql db:down Stop PostgreSQL database
38
+ dzql compile <input> Compile entity definitions to SQL
39
+ dzql --version Show version
40
+
41
+ Examples:
42
+ dzql create my-venue-app
43
+ dzql dev
44
+ dzql compile database/init_db/009_venues_domain.sql -o compiled/
45
+ `);
46
+ }
47
+
48
+ async function runCompile(args) {
49
+ const options = {
50
+ output: './compiled',
51
+ verbose: false
52
+ };
53
+
54
+ // Parse args
55
+ let inputFile = null;
56
+ for (let i = 0; i < args.length; i++) {
57
+ const arg = args[i];
58
+
59
+ if (arg === '-o' || arg === '--output') {
60
+ options.output = args[++i];
61
+ } else if (arg === '-v' || arg === '--verbose') {
62
+ options.verbose = true;
63
+ } else if (arg === '-h' || arg === '--help') {
64
+ console.log(`
65
+ DZQL Compiler - Transform entity definitions into PostgreSQL functions
66
+
67
+ Usage:
68
+ dzql compile <input-file> [options]
69
+
70
+ Options:
71
+ -o, --output <dir> Output directory (default: ./compiled)
72
+ -v, --verbose Verbose output
73
+ -h, --help Show this help message
74
+
75
+ Examples:
76
+ dzql compile entities/venues.sql
77
+ dzql compile database/init_db/009_venues_domain.sql -o compiled/
78
+ `);
79
+ return;
80
+ } else if (!inputFile) {
81
+ inputFile = arg;
82
+ }
83
+ }
84
+
85
+ if (!inputFile) {
86
+ console.error('Error: No input file specified');
87
+ console.log('Run "dzql compile --help" for usage information');
88
+ process.exit(1);
89
+ }
90
+
91
+ if (!existsSync(inputFile)) {
92
+ console.error(`Error: File not found: ${inputFile}`);
93
+ process.exit(1);
94
+ }
95
+
96
+ try {
97
+ console.log(`\nšŸ”Ø Compiling: ${inputFile}`);
98
+
99
+ // Read input file
100
+ const sqlContent = readFileSync(inputFile, 'utf-8');
101
+
102
+ // Compile
103
+ const compiler = new DZQLCompiler();
104
+ const result = compiler.compileFromSQL(sqlContent);
105
+
106
+ // Display results
107
+ console.log(`\nšŸ“Š Compilation Summary:`);
108
+ console.log(` Total entities: ${result.summary.total}`);
109
+ console.log(` Successful: ${result.summary.successful}`);
110
+ console.log(` Failed: ${result.summary.failed}`);
111
+
112
+ if (result.errors.length > 0) {
113
+ console.log(`\nāŒ Errors:`);
114
+ for (const error of result.errors) {
115
+ console.log(` - ${error.entity}: ${error.error}`);
116
+ }
117
+ }
118
+
119
+ // Write output files
120
+ if (result.results.length > 0) {
121
+ // Ensure output directory exists
122
+ if (!existsSync(options.output)) {
123
+ mkdirSync(options.output, { recursive: true });
124
+ }
125
+
126
+ console.log(`\nšŸ“ Writing compiled files to: ${options.output}`);
127
+
128
+ // Write core DZQL infrastructure
129
+ const coreSQL = `-- DZQL Core Schema and Events System
130
+
131
+ CREATE SCHEMA IF NOT EXISTS dzql;
132
+
133
+ -- Event Audit Table for real-time notifications
134
+ CREATE TABLE IF NOT EXISTS dzql.events (
135
+ event_id bigserial PRIMARY KEY,
136
+ table_name text NOT NULL,
137
+ op text NOT NULL, -- 'insert', 'update', 'delete'
138
+ pk jsonb NOT NULL, -- primary key of affected record
139
+ before jsonb, -- old values (NULL for insert)
140
+ after jsonb, -- new values (NULL for delete)
141
+ user_id int, -- who made the change
142
+ notify_users int[], -- who should be notified
143
+ at timestamptz DEFAULT now() -- when the change occurred
144
+ );
145
+
146
+ CREATE INDEX IF NOT EXISTS dzql_events_table_pk_idx ON dzql.events (table_name, pk, at);
147
+ CREATE INDEX IF NOT EXISTS dzql_events_event_id_idx ON dzql.events (event_id);
148
+
149
+ -- Event notification trigger - sends real-time notifications via pg_notify
150
+ CREATE OR REPLACE FUNCTION dzql.notify_event()
151
+ RETURNS TRIGGER LANGUAGE plpgsql AS $$
152
+ BEGIN
153
+ PERFORM pg_notify('dzql', jsonb_build_object(
154
+ 'event_id', NEW.event_id,
155
+ 'table', NEW.table_name,
156
+ 'op', NEW.op,
157
+ 'pk', NEW.pk,
158
+ 'data', COALESCE(NEW.after, NEW.before),
159
+ 'before', NEW.before,
160
+ 'after', NEW.after,
161
+ 'user_id', NEW.user_id,
162
+ 'at', NEW.at,
163
+ 'notify_users', NEW.notify_users
164
+ )::text);
165
+
166
+ RETURN NULL;
167
+ END $$;
168
+
169
+ DROP TRIGGER IF EXISTS dzql_events_notify ON dzql.events;
170
+ CREATE TRIGGER dzql_events_notify
171
+ AFTER INSERT ON dzql.events
172
+ FOR EACH ROW EXECUTE FUNCTION dzql.notify_event();
173
+ `;
174
+
175
+ writeFileSync(resolve(options.output, '000_dzql_core.sql'), coreSQL, 'utf-8');
176
+ console.log(` āœ“ 000_dzql_core.sql`);
177
+
178
+ // Extract schema SQL (everything before DZQL entity registrations)
179
+ const schemaSQL = sqlContent.split(/-- DZQL Entity Registrations|select dzql\.register_entity/i)[0].trim();
180
+ if (schemaSQL) {
181
+ writeFileSync(resolve(options.output, '001_schema.sql'), schemaSQL + '\n', 'utf-8');
182
+ console.log(` āœ“ 001_schema.sql`);
183
+ }
184
+
185
+ // Generate auth functions (required for WebSocket server)
186
+ const authSQL = `-- Authentication Functions
187
+ -- Required for DZQL WebSocket server
188
+
189
+ -- Enable pgcrypto extension for password hashing
190
+ CREATE EXTENSION IF NOT EXISTS pgcrypto;
191
+
192
+ -- Register new user
193
+ CREATE OR REPLACE FUNCTION register_user(p_email TEXT, p_password TEXT)
194
+ RETURNS JSONB
195
+ LANGUAGE plpgsql
196
+ SECURITY DEFINER
197
+ AS $$
198
+ DECLARE
199
+ user_id INT;
200
+ salt TEXT;
201
+ hash TEXT;
202
+ BEGIN
203
+ -- Generate salt and hash password
204
+ salt := gen_salt('bf', 10);
205
+ hash := crypt(p_password, salt);
206
+
207
+ -- Insert user (assumes users table has: id, email, name, password_hash)
208
+ INSERT INTO users (email, name, password_hash)
209
+ VALUES (p_email, split_part(p_email, '@', 1), hash)
210
+ RETURNING id INTO user_id;
211
+
212
+ RETURN _profile(user_id);
213
+ EXCEPTION
214
+ WHEN unique_violation THEN
215
+ RAISE EXCEPTION 'Email already exists' USING errcode = '23505';
216
+ END $$;
217
+
218
+ -- Login user
219
+ CREATE OR REPLACE FUNCTION login_user(p_email TEXT, p_password TEXT)
220
+ RETURNS JSONB
221
+ LANGUAGE plpgsql
222
+ SECURITY DEFINER
223
+ AS $$
224
+ DECLARE
225
+ user_record RECORD;
226
+ BEGIN
227
+ SELECT id, email, name, password_hash
228
+ INTO user_record
229
+ FROM users
230
+ WHERE email = p_email;
231
+
232
+ IF NOT FOUND THEN
233
+ RAISE EXCEPTION 'Invalid credentials' USING errcode = '28000';
234
+ END IF;
235
+
236
+ IF NOT (user_record.password_hash = crypt(p_password, user_record.password_hash)) THEN
237
+ RAISE EXCEPTION 'Invalid credentials' USING errcode = '28000';
238
+ END IF;
239
+
240
+ RETURN _profile(user_record.id);
241
+ END $$;
242
+
243
+ -- Get user profile (private function, called after login/register)
244
+ CREATE OR REPLACE FUNCTION _profile(p_user_id INT)
245
+ RETURNS JSONB
246
+ LANGUAGE sql
247
+ SECURITY DEFINER
248
+ AS $$
249
+ SELECT jsonb_build_object(
250
+ 'user_id', id,
251
+ 'email', email,
252
+ 'name', name,
253
+ 'created_at', created_at
254
+ )
255
+ FROM users
256
+ WHERE id = p_user_id;
257
+ $$;
258
+ `;
259
+
260
+ writeFileSync(resolve(options.output, '002_auth.sql'), authSQL, 'utf-8');
261
+ console.log(` āœ“ 002_auth.sql`);
262
+
263
+ const checksums = {};
264
+
265
+ for (const compiledResult of result.results) {
266
+ const outputFile = resolve(options.output, `${compiledResult.tableName}.sql`);
267
+
268
+ // Write SQL file
269
+ writeFileSync(outputFile, compiledResult.sql, 'utf-8');
270
+
271
+ // Store checksum
272
+ checksums[compiledResult.tableName] = {
273
+ checksum: compiledResult.checksum,
274
+ generatedAt: compiledResult.generatedAt,
275
+ compilationTime: compiledResult.compilationTime
276
+ };
277
+
278
+ console.log(` āœ“ ${compiledResult.tableName}.sql (${compiledResult.checksum.substring(0, 8)}...)`);
279
+ }
280
+
281
+ // Write checksums file
282
+ const checksumsFile = resolve(options.output, 'checksums.json');
283
+ writeFileSync(checksumsFile, JSON.stringify(checksums, null, 2), 'utf-8');
284
+
285
+ console.log(` āœ“ checksums.json`);
286
+ }
287
+
288
+ console.log(`\nāœ… Compilation complete!\n`);
289
+ } catch (error) {
290
+ console.error(`\nāŒ Compilation failed:`, error.message);
291
+ if (options.verbose) {
292
+ console.error(error.stack);
293
+ }
294
+ process.exit(1);
295
+ }
296
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dzql",
3
- "version": "0.3.2",
3
+ "version": "0.3.3",
4
4
  "description": "PostgreSQL-powered framework with zero boilerplate CRUD operations and real-time WebSocket synchronization",
5
5
  "type": "module",
6
6
  "main": "src/server/index.js",
@@ -14,6 +14,7 @@
14
14
  "./compiler": "./src/compiler/index.js"
15
15
  },
16
16
  "files": [
17
+ "bin/**/*.js",
17
18
  "src/**/*.js",
18
19
  "src/database/migrations/**/*.sql",
19
20
  "docs/**/*.md",
@@ -22,7 +23,7 @@
22
23
  ],
23
24
  "scripts": {
24
25
  "test": "bun test",
25
- "prepublishOnly": "echo 'āœ… Publishing DZQL v0.3.2...'"
26
+ "prepublishOnly": "echo 'āœ… Publishing DZQL v0.3.3...'"
26
27
  },
27
28
  "dependencies": {
28
29
  "jose": "^6.1.0",