dzql 0.5.33 → 0.6.1

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 (142) hide show
  1. package/.env.sample +28 -0
  2. package/compose.yml +28 -0
  3. package/dist/client/index.ts +1 -0
  4. package/dist/client/stores/useMyProfileStore.ts +114 -0
  5. package/dist/client/stores/useOrgDashboardStore.ts +131 -0
  6. package/dist/client/stores/useVenueDetailStore.ts +117 -0
  7. package/dist/client/ws.ts +716 -0
  8. package/dist/db/migrations/000_core.sql +92 -0
  9. package/dist/db/migrations/20251229T212912022Z_schema.sql +3020 -0
  10. package/dist/db/migrations/20251229T212912022Z_subscribables.sql +371 -0
  11. package/dist/runtime/manifest.json +1562 -0
  12. package/docs/README.md +309 -36
  13. package/docs/feature-requests/applyPatch-bug-report.md +85 -0
  14. package/docs/feature-requests/connection-ready-profile.md +57 -0
  15. package/docs/feature-requests/hidden-bug-report.md +111 -0
  16. package/docs/feature-requests/hidden-fields-subscribables.md +34 -0
  17. package/docs/feature-requests/subscribable-param-key-bug.md +38 -0
  18. package/docs/feature-requests/todo.md +146 -0
  19. package/docs/for_ai.md +653 -0
  20. package/docs/project-setup.md +456 -0
  21. package/examples/blog.ts +50 -0
  22. package/examples/invalid.ts +18 -0
  23. package/examples/venues.js +485 -0
  24. package/package.json +23 -60
  25. package/src/cli/codegen/client.ts +99 -0
  26. package/src/cli/codegen/manifest.ts +95 -0
  27. package/src/cli/codegen/pinia.ts +174 -0
  28. package/src/cli/codegen/realtime.ts +58 -0
  29. package/src/cli/codegen/sql.ts +698 -0
  30. package/src/cli/codegen/subscribable_sql.ts +547 -0
  31. package/src/cli/codegen/subscribable_store.ts +184 -0
  32. package/src/cli/codegen/types.ts +142 -0
  33. package/src/cli/compiler/analyzer.ts +52 -0
  34. package/src/cli/compiler/graph_rules.ts +251 -0
  35. package/src/cli/compiler/ir.ts +233 -0
  36. package/src/cli/compiler/loader.ts +132 -0
  37. package/src/cli/compiler/permissions.ts +227 -0
  38. package/src/cli/index.ts +166 -0
  39. package/src/client/index.ts +1 -0
  40. package/src/client/ws.ts +286 -0
  41. package/src/runtime/auth.ts +39 -0
  42. package/src/runtime/db.ts +33 -0
  43. package/src/runtime/errors.ts +51 -0
  44. package/src/runtime/index.ts +98 -0
  45. package/src/runtime/js_functions.ts +63 -0
  46. package/src/runtime/manifest_loader.ts +29 -0
  47. package/src/runtime/namespace.ts +483 -0
  48. package/src/runtime/server.ts +87 -0
  49. package/src/runtime/ws.ts +197 -0
  50. package/src/shared/ir.ts +197 -0
  51. package/tests/client.test.ts +38 -0
  52. package/tests/codegen.test.ts +71 -0
  53. package/tests/compiler.test.ts +45 -0
  54. package/tests/graph_rules.test.ts +173 -0
  55. package/tests/integration/db.test.ts +174 -0
  56. package/tests/integration/e2e.test.ts +65 -0
  57. package/tests/integration/features.test.ts +922 -0
  58. package/tests/integration/full_stack.test.ts +262 -0
  59. package/tests/integration/setup.ts +45 -0
  60. package/tests/ir.test.ts +32 -0
  61. package/tests/namespace.test.ts +395 -0
  62. package/tests/permissions.test.ts +55 -0
  63. package/tests/pinia.test.ts +48 -0
  64. package/tests/realtime.test.ts +22 -0
  65. package/tests/runtime.test.ts +80 -0
  66. package/tests/subscribable_gen.test.ts +72 -0
  67. package/tests/subscribable_reactivity.test.ts +258 -0
  68. package/tests/venues_gen.test.ts +25 -0
  69. package/tsconfig.json +20 -0
  70. package/tsconfig.tsbuildinfo +1 -0
  71. package/README.md +0 -90
  72. package/bin/cli.js +0 -727
  73. package/docs/compiler/ADVANCED_FILTERS.md +0 -183
  74. package/docs/compiler/CODING_STANDARDS.md +0 -415
  75. package/docs/compiler/COMPARISON.md +0 -673
  76. package/docs/compiler/QUICKSTART.md +0 -326
  77. package/docs/compiler/README.md +0 -134
  78. package/docs/examples/README.md +0 -38
  79. package/docs/examples/blog.sql +0 -160
  80. package/docs/examples/venue-detail-simple.sql +0 -8
  81. package/docs/examples/venue-detail-subscribable.sql +0 -45
  82. package/docs/for-ai/claude-guide.md +0 -1210
  83. package/docs/getting-started/quickstart.md +0 -125
  84. package/docs/getting-started/subscriptions-quick-start.md +0 -203
  85. package/docs/getting-started/tutorial.md +0 -1104
  86. package/docs/guides/atomic-updates.md +0 -299
  87. package/docs/guides/client-stores.md +0 -730
  88. package/docs/guides/composite-primary-keys.md +0 -158
  89. package/docs/guides/custom-functions.md +0 -362
  90. package/docs/guides/drop-semantics.md +0 -554
  91. package/docs/guides/field-defaults.md +0 -240
  92. package/docs/guides/interpreter-vs-compiler.md +0 -237
  93. package/docs/guides/many-to-many.md +0 -929
  94. package/docs/guides/subscriptions.md +0 -537
  95. package/docs/reference/api.md +0 -1373
  96. package/docs/reference/client.md +0 -224
  97. package/src/client/stores/index.js +0 -8
  98. package/src/client/stores/useAppStore.js +0 -285
  99. package/src/client/stores/useWsStore.js +0 -289
  100. package/src/client/ws.js +0 -762
  101. package/src/compiler/cli/compile-example.js +0 -33
  102. package/src/compiler/cli/compile-subscribable.js +0 -43
  103. package/src/compiler/cli/debug-compile.js +0 -44
  104. package/src/compiler/cli/debug-parse.js +0 -26
  105. package/src/compiler/cli/debug-path-parser.js +0 -18
  106. package/src/compiler/cli/debug-subscribable-parser.js +0 -21
  107. package/src/compiler/cli/index.js +0 -174
  108. package/src/compiler/codegen/auth-codegen.js +0 -153
  109. package/src/compiler/codegen/drop-semantics-codegen.js +0 -553
  110. package/src/compiler/codegen/graph-rules-codegen.js +0 -450
  111. package/src/compiler/codegen/notification-codegen.js +0 -232
  112. package/src/compiler/codegen/operation-codegen.js +0 -1382
  113. package/src/compiler/codegen/permission-codegen.js +0 -318
  114. package/src/compiler/codegen/subscribable-codegen.js +0 -827
  115. package/src/compiler/compiler.js +0 -371
  116. package/src/compiler/index.js +0 -11
  117. package/src/compiler/parser/entity-parser.js +0 -440
  118. package/src/compiler/parser/path-parser.js +0 -290
  119. package/src/compiler/parser/subscribable-parser.js +0 -244
  120. package/src/database/dzql-core.sql +0 -161
  121. package/src/database/migrations/001_schema.sql +0 -60
  122. package/src/database/migrations/002_functions.sql +0 -890
  123. package/src/database/migrations/003_operations.sql +0 -1135
  124. package/src/database/migrations/004_search.sql +0 -581
  125. package/src/database/migrations/005_entities.sql +0 -730
  126. package/src/database/migrations/006_auth.sql +0 -94
  127. package/src/database/migrations/007_events.sql +0 -133
  128. package/src/database/migrations/008_hello.sql +0 -18
  129. package/src/database/migrations/008a_meta.sql +0 -172
  130. package/src/database/migrations/009_subscriptions.sql +0 -240
  131. package/src/database/migrations/010_atomic_updates.sql +0 -157
  132. package/src/database/migrations/010_fix_m2m_events.sql +0 -94
  133. package/src/index.js +0 -40
  134. package/src/server/api.js +0 -9
  135. package/src/server/db.js +0 -442
  136. package/src/server/index.js +0 -317
  137. package/src/server/logger.js +0 -259
  138. package/src/server/mcp.js +0 -594
  139. package/src/server/meta-route.js +0 -251
  140. package/src/server/namespace.js +0 -292
  141. package/src/server/subscriptions.js +0 -351
  142. package/src/server/ws.js +0 -573
@@ -1,33 +0,0 @@
1
- #!/usr/bin/env bun
2
-
3
- import { readFileSync } from 'fs';
4
- import { compileSubscribablesFromSQL } from '../compiler.js';
5
-
6
- const sqlContent = readFileSync('./examples/subscribables/venue_detail_simple.sql', 'utf-8');
7
-
8
- console.log('Compiling subscribable...\n');
9
-
10
- try {
11
- const result = compileSubscribablesFromSQL(sqlContent);
12
-
13
- console.log('Summary:', result.summary);
14
-
15
- if (result.errors.length > 0) {
16
- console.log('\nErrors:');
17
- result.errors.forEach(err => {
18
- console.log(` ${err.subscribable}: ${err.error}`);
19
- });
20
- }
21
-
22
- if (result.results.length > 0) {
23
- const compiled = result.results[0];
24
- console.log(`\n✓ Compiled '${compiled.name}' successfully!`);
25
- console.log(` Checksum: ${compiled.checksum.substring(0, 16)}...`);
26
- console.log(` Time: ${compiled.compilationTime}ms`);
27
- console.log('\nGenerated SQL:\n');
28
- console.log(compiled.sql);
29
- }
30
- } catch (error) {
31
- console.error('Error:', error.message);
32
- process.exit(1);
33
- }
@@ -1,43 +0,0 @@
1
- #!/usr/bin/env bun
2
-
3
- /**
4
- * Compile subscribable and output SQL only (for piping to psql)
5
- */
6
-
7
- import { readFileSync } from 'fs';
8
- import { compileSubscribablesFromSQL } from '../compiler.js';
9
-
10
- const args = process.argv.slice(2);
11
- if (args.length === 0) {
12
- console.error('Usage: compile-subscribable.js <sql-file>');
13
- process.exit(1);
14
- }
15
-
16
- const sqlFile = args[0];
17
-
18
- try {
19
- const sqlContent = readFileSync(sqlFile, 'utf-8');
20
- const result = compileSubscribablesFromSQL(sqlContent);
21
-
22
- if (result.errors.length > 0) {
23
- console.error('Compilation errors:');
24
- result.errors.forEach(err => {
25
- console.error(` ${err.subscribable}: ${err.error}`);
26
- });
27
- process.exit(1);
28
- }
29
-
30
- if (result.results.length === 0) {
31
- console.error('No subscribables found in', sqlFile);
32
- process.exit(1);
33
- }
34
-
35
- // Output just the SQL
36
- for (const compiled of result.results) {
37
- console.log(compiled.sql);
38
- console.log(''); // Blank line between subscribables
39
- }
40
- } catch (error) {
41
- console.error('Error:', error.message);
42
- process.exit(1);
43
- }
@@ -1,44 +0,0 @@
1
- #!/usr/bin/env bun
2
-
3
- /**
4
- * Test script for subscribable compilation
5
- */
6
-
7
- import { readFileSync } from 'fs';
8
- import { compileSubscribablesFromSQL } from './src/compiler/compiler.js';
9
-
10
- // Read the example subscribable
11
- const sqlContent = readFileSync('./examples/subscribables/venue_detail_subscribable.sql', 'utf-8');
12
-
13
- console.log('Compiling subscribable...\n');
14
-
15
- try {
16
- const result = compileSubscribablesFromSQL(sqlContent);
17
-
18
- console.log('Compilation Summary:');
19
- console.log(` Total: ${result.summary.total}`);
20
- console.log(` Successful: ${result.summary.successful}`);
21
- console.log(` Failed: ${result.summary.failed}\n`);
22
-
23
- if (result.errors.length > 0) {
24
- console.log('Errors:');
25
- result.errors.forEach(err => {
26
- console.log(` - ${err.subscribable}: ${err.error}`);
27
- });
28
- console.log('');
29
- }
30
-
31
- if (result.results.length > 0) {
32
- const compiled = result.results[0];
33
- console.log(`Generated SQL for '${compiled.name}':`);
34
- console.log('='.repeat(80));
35
- console.log(compiled.sql);
36
- console.log('='.repeat(80));
37
- console.log(`\nChecksum: ${compiled.checksum}`);
38
- console.log(`Compilation time: ${compiled.compilationTime}ms`);
39
- }
40
- } catch (error) {
41
- console.error('Compilation failed:', error.message);
42
- console.error(error.stack);
43
- process.exit(1);
44
- }
@@ -1,26 +0,0 @@
1
- #!/usr/bin/env bun
2
-
3
- /**
4
- * Test script for subscribable parsing (debug)
5
- */
6
-
7
- import { readFileSync } from 'fs';
8
- import { SubscribableParser } from './src/compiler/parser/subscribable-parser.js';
9
-
10
- // Read the example subscribable
11
- const sqlContent = readFileSync('./examples/subscribables/venue_detail_subscribable.sql', 'utf-8');
12
-
13
- console.log('Parsing subscribable...\n');
14
-
15
- const parser = new SubscribableParser();
16
- const subscribables = parser.parseAllFromSQL(sqlContent);
17
-
18
- console.log('Found', subscribables.length, 'subscribables\n');
19
-
20
- for (const sub of subscribables) {
21
- console.log('Subscribable:', sub.name);
22
- console.log('Root Entity:', sub.rootEntity);
23
- console.log('Permission Paths:', JSON.stringify(sub.permissionPaths, null, 2));
24
- console.log('Param Schema:', JSON.stringify(sub.paramSchema, null, 2));
25
- console.log('Relations:', JSON.stringify(sub.relations, null, 2));
26
- }
@@ -1,18 +0,0 @@
1
- #!/usr/bin/env bun
2
-
3
- import { PathParser } from './src/compiler/parser/path-parser.js';
4
-
5
- const parser = new PathParser();
6
-
7
- const testPath = '@org_id->acts_for[org_id=$]{active}.user_id';
8
-
9
- console.log('Parsing path:', testPath);
10
- console.log('');
11
-
12
- try {
13
- const ast = parser.parse(testPath);
14
- console.log('AST:', JSON.stringify(ast, null, 2));
15
- } catch (error) {
16
- console.error('Error:', error.message);
17
- console.error(error.stack);
18
- }
@@ -1,21 +0,0 @@
1
- #!/usr/bin/env bun
2
-
3
- import { SubscribableParser } from '../parser/subscribable-parser.js';
4
- import { readFileSync } from 'fs';
5
-
6
- const sqlContent = readFileSync('./examples/subscribables/venue_detail_simple.sql', 'utf-8');
7
-
8
- console.log('Parsing subscribable...\n');
9
-
10
- const parser = new SubscribableParser();
11
- const subscribables = parser.parseAllFromSQL(sqlContent);
12
-
13
- console.log('Found:', subscribables.length, 'subscribables\n');
14
-
15
- for (const sub of subscribables) {
16
- console.log('Name:', sub.name);
17
- console.log('Root Entity:', sub.rootEntity);
18
- console.log('Permission Paths:', JSON.stringify(sub.permissionPaths, null, 2));
19
- console.log('Param Schema:', JSON.stringify(sub.paramSchema, null, 2));
20
- console.log('Relations:', JSON.stringify(sub.relations, null, 2));
21
- }
@@ -1,174 +0,0 @@
1
- #!/usr/bin/env bun
2
- /**
3
- * DZQL Compiler CLI
4
- * Command-line interface for compiling entity definitions
5
- */
6
-
7
- import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs';
8
- import { resolve, dirname, basename } from 'path';
9
- import { DZQLCompiler } from '../compiler.js';
10
-
11
- const USAGE = `
12
- DZQL Compiler - Transform entity definitions into PostgreSQL functions
13
-
14
- Usage:
15
- dzql-compile <input-file> [options]
16
-
17
- Options:
18
- -o, --output <dir> Output directory (default: ./compiled)
19
- -w, --watch Watch for changes and recompile
20
- -v, --verbose Verbose output
21
- -h, --help Show this help message
22
-
23
- Examples:
24
- dzql-compile entities/venues.sql
25
- dzql-compile database/init_db/009_venues_domain.sql -o compiled/
26
- dzql-compile entities/*.sql -o dist/compiled/
27
-
28
- Environment Variables:
29
- DZQL_COMPILER_VERBOSE Enable verbose output
30
- `;
31
-
32
- class CLI {
33
- constructor() {
34
- this.args = process.argv.slice(2);
35
- this.options = {
36
- output: './compiled',
37
- watch: false,
38
- verbose: process.env.DZQL_COMPILER_VERBOSE === 'true'
39
- };
40
- this.compiler = new DZQLCompiler();
41
- }
42
-
43
- run() {
44
- this.parseArgs();
45
-
46
- if (this.options.help || this.args.length === 0) {
47
- console.log(USAGE);
48
- process.exit(0);
49
- }
50
-
51
- const inputFile = this.args[0];
52
-
53
- if (!existsSync(inputFile)) {
54
- console.error(`Error: File not found: ${inputFile}`);
55
- process.exit(1);
56
- }
57
-
58
- this.compileFile(inputFile);
59
-
60
- if (this.options.watch) {
61
- this.watchFile(inputFile);
62
- }
63
- }
64
-
65
- parseArgs() {
66
- for (let i = 0; i < this.args.length; i++) {
67
- const arg = this.args[i];
68
-
69
- switch (arg) {
70
- case '-o':
71
- case '--output':
72
- this.options.output = this.args[++i];
73
- break;
74
-
75
- case '-w':
76
- case '--watch':
77
- this.options.watch = true;
78
- break;
79
-
80
- case '-v':
81
- case '--verbose':
82
- this.options.verbose = true;
83
- break;
84
-
85
- case '-h':
86
- case '--help':
87
- this.options.help = true;
88
- break;
89
- }
90
- }
91
- }
92
-
93
- compileFile(inputFile) {
94
- try {
95
- console.log(`\n🔨 Compiling: ${inputFile}`);
96
-
97
- // Read input file
98
- const sqlContent = readFileSync(inputFile, 'utf-8');
99
-
100
- // Compile
101
- const result = this.compiler.compileFromSQL(sqlContent);
102
-
103
- // Display results
104
- console.log(`\n📊 Compilation Summary:`);
105
- console.log(` Total entities: ${result.summary.total}`);
106
- console.log(` Successful: ${result.summary.successful}`);
107
- console.log(` Failed: ${result.summary.failed}`);
108
-
109
- if (result.errors.length > 0) {
110
- console.log(`\n❌ Errors:`);
111
- for (const error of result.errors) {
112
- console.log(` - ${error.entity}: ${error.error}`);
113
- }
114
- }
115
-
116
- // Write output files
117
- if (result.results.length > 0) {
118
- this.writeOutputFiles(result.results);
119
- }
120
-
121
- console.log(`\n✅ Compilation complete!\n`);
122
- } catch (error) {
123
- console.error(`\n❌ Compilation failed:`, error.message);
124
- if (this.options.verbose) {
125
- console.error(error.stack);
126
- }
127
- process.exit(1);
128
- }
129
- }
130
-
131
- writeOutputFiles(results) {
132
- // Ensure output directory exists
133
- if (!existsSync(this.options.output)) {
134
- mkdirSync(this.options.output, { recursive: true });
135
- }
136
-
137
- console.log(`\n📝 Writing compiled files to: ${this.options.output}`);
138
-
139
- const checksums = {};
140
-
141
- for (const result of results) {
142
- const outputFile = resolve(this.options.output, `${result.tableName}.sql`);
143
-
144
- // Write SQL file
145
- writeFileSync(outputFile, result.sql, 'utf-8');
146
-
147
- // Store checksum
148
- checksums[result.tableName] = {
149
- checksum: result.checksum,
150
- generatedAt: result.generatedAt,
151
- compilationTime: result.compilationTime
152
- };
153
-
154
- console.log(` ✓ ${result.tableName}.sql (${result.checksum.substring(0, 8)}...)`);
155
- }
156
-
157
- // Write checksums file
158
- const checksumsFile = resolve(this.options.output, 'checksums.json');
159
- writeFileSync(checksumsFile, JSON.stringify(checksums, null, 2), 'utf-8');
160
-
161
- console.log(` ✓ checksums.json`);
162
- }
163
-
164
- watchFile(inputFile) {
165
- console.log(`\n👀 Watching for changes...`);
166
-
167
- // TODO: Implement file watching
168
- console.log(` (Watch mode not yet implemented)`);
169
- }
170
- }
171
-
172
- // Run CLI
173
- const cli = new CLI();
174
- cli.run();
@@ -1,153 +0,0 @@
1
- /**
2
- * Auth Code Generator
3
- * Generates PostgreSQL functions for user authentication
4
- * Only generated when the entity is named 'users'
5
- */
6
-
7
- export class AuthCodegen {
8
- constructor(entity) {
9
- this.entity = entity;
10
- this.tableName = entity.tableName;
11
- }
12
-
13
- /**
14
- * Check if this entity should have auth functions generated
15
- * @returns {boolean}
16
- */
17
- shouldGenerate() {
18
- return this.tableName === 'users';
19
- }
20
-
21
- /**
22
- * Generate all auth functions
23
- * @returns {string} SQL for auth functions
24
- */
25
- generateAll() {
26
- if (!this.shouldGenerate()) {
27
- return '';
28
- }
29
-
30
- return [
31
- '-- Enable pgcrypto extension for password hashing',
32
- 'CREATE EXTENSION IF NOT EXISTS pgcrypto;',
33
- '',
34
- this._generateProfileFunction(),
35
- this._generateRegisterFunction(),
36
- this._generateLoginFunction()
37
- ].join('\n\n');
38
- }
39
-
40
- /**
41
- * Generate _profile function
42
- * Returns all user columns except sensitive fields
43
- * @private
44
- */
45
- _generateProfileFunction() {
46
- return `-- ============================================================================
47
- -- Auth: _profile function for ${this.tableName}
48
- -- Returns user record minus sensitive fields
49
- -- ============================================================================
50
- CREATE OR REPLACE FUNCTION _profile(p_user_id INT)
51
- RETURNS JSONB
52
- LANGUAGE SQL
53
- SECURITY DEFINER
54
- AS $$
55
- SELECT jsonb_build_object('user_id', u.id) || (to_jsonb(u.*) - 'id' - 'password_hash' - 'password' - 'secret' - 'token')
56
- FROM ${this.tableName} u
57
- WHERE id = p_user_id;
58
- $$;`;
59
- }
60
-
61
- /**
62
- * Generate register_user function
63
- * Supports optional fields via JSON parameter
64
- * @private
65
- */
66
- _generateRegisterFunction() {
67
- return `-- ============================================================================
68
- -- Auth: register_user function for ${this.tableName}
69
- -- p_options: optional JSON object with additional fields to set on the user record
70
- -- Example: register_user('test@example.com', 'password', '{"name": "Test User"}')
71
- -- ============================================================================
72
- CREATE OR REPLACE FUNCTION register_user(p_email TEXT, p_password TEXT, p_options JSON DEFAULT NULL)
73
- RETURNS JSONB
74
- LANGUAGE plpgsql
75
- SECURITY DEFINER
76
- AS $$
77
- DECLARE
78
- v_user_id INT;
79
- v_salt TEXT;
80
- v_hash TEXT;
81
- v_insert_data JSONB;
82
- BEGIN
83
- -- Generate salt and hash password
84
- v_salt := gen_salt('bf', 10);
85
- v_hash := crypt(p_password, v_salt);
86
-
87
- -- Build insert data: options fields + email + password_hash (options cannot override core fields)
88
- -- Cast p_options to JSONB for internal operations (JSON type is for API boundary convenience)
89
- v_insert_data := jsonb_build_object('email', p_email, 'password_hash', v_hash);
90
- IF p_options IS NOT NULL THEN
91
- v_insert_data := (p_options::jsonb - 'id' - 'email' - 'password_hash' - 'password') || v_insert_data;
92
- END IF;
93
-
94
- -- Dynamic INSERT from JSONB (same pattern as compiled save functions)
95
- EXECUTE (
96
- SELECT format(
97
- 'INSERT INTO ${this.tableName} (%s) VALUES (%s) RETURNING id',
98
- string_agg(quote_ident(key), ', '),
99
- string_agg(quote_nullable(value), ', ')
100
- )
101
- FROM jsonb_each_text(v_insert_data) kv(key, value)
102
- ) INTO v_user_id;
103
-
104
- RETURN _profile(v_user_id);
105
- EXCEPTION
106
- WHEN unique_violation THEN
107
- RAISE EXCEPTION 'Email already exists' USING errcode = '23505';
108
- END $$;`;
109
- }
110
-
111
- /**
112
- * Generate login_user function
113
- * @private
114
- */
115
- _generateLoginFunction() {
116
- return `-- ============================================================================
117
- -- Auth: login_user function for ${this.tableName}
118
- -- ============================================================================
119
- CREATE OR REPLACE FUNCTION login_user(p_email TEXT, p_password TEXT)
120
- RETURNS JSONB
121
- LANGUAGE plpgsql
122
- SECURITY DEFINER
123
- AS $$
124
- DECLARE
125
- v_user_record RECORD;
126
- BEGIN
127
- SELECT id, email, password_hash
128
- INTO v_user_record
129
- FROM ${this.tableName}
130
- WHERE email = p_email;
131
-
132
- IF NOT FOUND THEN
133
- RAISE EXCEPTION 'Invalid credentials' USING errcode = '28000';
134
- END IF;
135
-
136
- IF NOT (v_user_record.password_hash = crypt(p_password, v_user_record.password_hash)) THEN
137
- RAISE EXCEPTION 'Invalid credentials' USING errcode = '28000';
138
- END IF;
139
-
140
- RETURN _profile(v_user_record.id);
141
- END $$;`;
142
- }
143
- }
144
-
145
- /**
146
- * Generate auth functions for an entity (only if it's the users table)
147
- * @param {Object} entity - Entity configuration
148
- * @returns {string} SQL for auth functions (empty string if not users table)
149
- */
150
- export function generateAuthFunctions(entity) {
151
- const codegen = new AuthCodegen(entity);
152
- return codegen.generateAll();
153
- }