dzql 0.5.33 → 0.6.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 (150) 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 +293 -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 +641 -0
  20. package/docs/project-setup.md +432 -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 +164 -0
  39. package/src/client/index.ts +1 -0
  40. package/src/client/ws.ts +286 -0
  41. package/src/create/.env.example +8 -0
  42. package/src/create/README.md +101 -0
  43. package/src/create/compose.yml +14 -0
  44. package/src/create/domain.ts +153 -0
  45. package/src/create/package.json +24 -0
  46. package/src/create/server.ts +18 -0
  47. package/src/create/setup.sh +11 -0
  48. package/src/create/tsconfig.json +15 -0
  49. package/src/runtime/auth.ts +39 -0
  50. package/src/runtime/db.ts +33 -0
  51. package/src/runtime/errors.ts +51 -0
  52. package/src/runtime/index.ts +98 -0
  53. package/src/runtime/js_functions.ts +63 -0
  54. package/src/runtime/manifest_loader.ts +29 -0
  55. package/src/runtime/namespace.ts +483 -0
  56. package/src/runtime/server.ts +87 -0
  57. package/src/runtime/ws.ts +197 -0
  58. package/src/shared/ir.ts +197 -0
  59. package/tests/client.test.ts +38 -0
  60. package/tests/codegen.test.ts +71 -0
  61. package/tests/compiler.test.ts +45 -0
  62. package/tests/graph_rules.test.ts +173 -0
  63. package/tests/integration/db.test.ts +174 -0
  64. package/tests/integration/e2e.test.ts +65 -0
  65. package/tests/integration/features.test.ts +922 -0
  66. package/tests/integration/full_stack.test.ts +262 -0
  67. package/tests/integration/setup.ts +45 -0
  68. package/tests/ir.test.ts +32 -0
  69. package/tests/namespace.test.ts +395 -0
  70. package/tests/permissions.test.ts +55 -0
  71. package/tests/pinia.test.ts +48 -0
  72. package/tests/realtime.test.ts +22 -0
  73. package/tests/runtime.test.ts +80 -0
  74. package/tests/subscribable_gen.test.ts +72 -0
  75. package/tests/subscribable_reactivity.test.ts +258 -0
  76. package/tests/venues_gen.test.ts +25 -0
  77. package/tsconfig.json +20 -0
  78. package/tsconfig.tsbuildinfo +1 -0
  79. package/README.md +0 -90
  80. package/bin/cli.js +0 -727
  81. package/docs/compiler/ADVANCED_FILTERS.md +0 -183
  82. package/docs/compiler/CODING_STANDARDS.md +0 -415
  83. package/docs/compiler/COMPARISON.md +0 -673
  84. package/docs/compiler/QUICKSTART.md +0 -326
  85. package/docs/compiler/README.md +0 -134
  86. package/docs/examples/README.md +0 -38
  87. package/docs/examples/blog.sql +0 -160
  88. package/docs/examples/venue-detail-simple.sql +0 -8
  89. package/docs/examples/venue-detail-subscribable.sql +0 -45
  90. package/docs/for-ai/claude-guide.md +0 -1210
  91. package/docs/getting-started/quickstart.md +0 -125
  92. package/docs/getting-started/subscriptions-quick-start.md +0 -203
  93. package/docs/getting-started/tutorial.md +0 -1104
  94. package/docs/guides/atomic-updates.md +0 -299
  95. package/docs/guides/client-stores.md +0 -730
  96. package/docs/guides/composite-primary-keys.md +0 -158
  97. package/docs/guides/custom-functions.md +0 -362
  98. package/docs/guides/drop-semantics.md +0 -554
  99. package/docs/guides/field-defaults.md +0 -240
  100. package/docs/guides/interpreter-vs-compiler.md +0 -237
  101. package/docs/guides/many-to-many.md +0 -929
  102. package/docs/guides/subscriptions.md +0 -537
  103. package/docs/reference/api.md +0 -1373
  104. package/docs/reference/client.md +0 -224
  105. package/src/client/stores/index.js +0 -8
  106. package/src/client/stores/useAppStore.js +0 -285
  107. package/src/client/stores/useWsStore.js +0 -289
  108. package/src/client/ws.js +0 -762
  109. package/src/compiler/cli/compile-example.js +0 -33
  110. package/src/compiler/cli/compile-subscribable.js +0 -43
  111. package/src/compiler/cli/debug-compile.js +0 -44
  112. package/src/compiler/cli/debug-parse.js +0 -26
  113. package/src/compiler/cli/debug-path-parser.js +0 -18
  114. package/src/compiler/cli/debug-subscribable-parser.js +0 -21
  115. package/src/compiler/cli/index.js +0 -174
  116. package/src/compiler/codegen/auth-codegen.js +0 -153
  117. package/src/compiler/codegen/drop-semantics-codegen.js +0 -553
  118. package/src/compiler/codegen/graph-rules-codegen.js +0 -450
  119. package/src/compiler/codegen/notification-codegen.js +0 -232
  120. package/src/compiler/codegen/operation-codegen.js +0 -1382
  121. package/src/compiler/codegen/permission-codegen.js +0 -318
  122. package/src/compiler/codegen/subscribable-codegen.js +0 -827
  123. package/src/compiler/compiler.js +0 -371
  124. package/src/compiler/index.js +0 -11
  125. package/src/compiler/parser/entity-parser.js +0 -440
  126. package/src/compiler/parser/path-parser.js +0 -290
  127. package/src/compiler/parser/subscribable-parser.js +0 -244
  128. package/src/database/dzql-core.sql +0 -161
  129. package/src/database/migrations/001_schema.sql +0 -60
  130. package/src/database/migrations/002_functions.sql +0 -890
  131. package/src/database/migrations/003_operations.sql +0 -1135
  132. package/src/database/migrations/004_search.sql +0 -581
  133. package/src/database/migrations/005_entities.sql +0 -730
  134. package/src/database/migrations/006_auth.sql +0 -94
  135. package/src/database/migrations/007_events.sql +0 -133
  136. package/src/database/migrations/008_hello.sql +0 -18
  137. package/src/database/migrations/008a_meta.sql +0 -172
  138. package/src/database/migrations/009_subscriptions.sql +0 -240
  139. package/src/database/migrations/010_atomic_updates.sql +0 -157
  140. package/src/database/migrations/010_fix_m2m_events.sql +0 -94
  141. package/src/index.js +0 -40
  142. package/src/server/api.js +0 -9
  143. package/src/server/db.js +0 -442
  144. package/src/server/index.js +0 -317
  145. package/src/server/logger.js +0 -259
  146. package/src/server/mcp.js +0 -594
  147. package/src/server/meta-route.js +0 -251
  148. package/src/server/namespace.js +0 -292
  149. package/src/server/subscriptions.js +0 -351
  150. 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
- }