@smartive/graphql-magic 23.4.0-next.8 → 23.4.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 (43) hide show
  1. package/CHANGELOG.md +2 -2
  2. package/dist/bin/gqm.cjs +125 -656
  3. package/dist/cjs/index.cjs +2132 -2702
  4. package/dist/esm/migrations/generate.d.ts +1 -9
  5. package/dist/esm/migrations/generate.js +33 -269
  6. package/dist/esm/migrations/generate.js.map +1 -1
  7. package/dist/esm/migrations/index.d.ts +0 -2
  8. package/dist/esm/migrations/index.js +0 -2
  9. package/dist/esm/migrations/index.js.map +1 -1
  10. package/dist/esm/models/model-definitions.d.ts +1 -4
  11. package/dist/esm/resolvers/filters.js +14 -73
  12. package/dist/esm/resolvers/filters.js.map +1 -1
  13. package/dist/esm/resolvers/selects.js +2 -33
  14. package/dist/esm/resolvers/selects.js.map +1 -1
  15. package/dist/esm/resolvers/utils.d.ts +0 -1
  16. package/dist/esm/resolvers/utils.js +0 -22
  17. package/dist/esm/resolvers/utils.js.map +1 -1
  18. package/docs/docs/3-fields.md +0 -149
  19. package/docs/docs/5-migrations.md +1 -9
  20. package/package.json +2 -2
  21. package/src/bin/gqm/gqm.ts +5 -44
  22. package/src/bin/gqm/settings.ts +0 -7
  23. package/src/bin/gqm/static-eval.ts +102 -0
  24. package/src/bin/gqm/utils.ts +0 -1
  25. package/src/migrations/generate.ts +41 -334
  26. package/src/migrations/index.ts +0 -2
  27. package/src/models/model-definitions.ts +1 -4
  28. package/src/resolvers/filters.ts +25 -81
  29. package/src/resolvers/selects.ts +5 -38
  30. package/src/resolvers/utils.ts +0 -32
  31. package/dist/esm/migrations/generate-functions.d.ts +0 -2
  32. package/dist/esm/migrations/generate-functions.js +0 -60
  33. package/dist/esm/migrations/generate-functions.js.map +0 -1
  34. package/dist/esm/migrations/types.d.ts +0 -7
  35. package/dist/esm/migrations/types.js +0 -2
  36. package/dist/esm/migrations/types.js.map +0 -1
  37. package/dist/esm/migrations/update-functions.d.ts +0 -3
  38. package/dist/esm/migrations/update-functions.js +0 -177
  39. package/dist/esm/migrations/update-functions.js.map +0 -1
  40. package/src/bin/gqm/parse-functions.ts +0 -141
  41. package/src/migrations/generate-functions.ts +0 -74
  42. package/src/migrations/types.ts +0 -7
  43. package/src/migrations/update-functions.ts +0 -221
@@ -1,141 +0,0 @@
1
- import { existsSync } from 'fs';
2
- import { IndentationText, Project } from 'ts-morph';
3
- import { ParsedFunction } from '../../migrations/types';
4
- import { staticEval } from './static-eval';
5
- import { findDeclarationInFile } from './utils';
6
-
7
- const normalizeWhitespace = (str: string): string => {
8
- return str
9
- .replace(/\s+/g, ' ')
10
- .replace(/\s*\(\s*/g, '(')
11
- .replace(/\s*\)\s*/g, ')')
12
- .replace(/\s*,\s*/g, ',')
13
- .replace(/\s*;\s*/g, ';')
14
- .trim();
15
- };
16
-
17
- const normalizeFunctionBody = (body: string): string => {
18
- return normalizeWhitespace(body);
19
- };
20
-
21
- const normalizeAggregateDefinition = (definition: string): string => {
22
- let normalized = normalizeWhitespace(definition);
23
-
24
- const initCondMatch = normalized.match(/INITCOND\s*=\s*([^,)]+)/i);
25
- if (initCondMatch) {
26
- const initCondValue = initCondMatch[1].trim();
27
- const unquoted = initCondValue.replace(/^['"]|['"]$/g, '');
28
- if (/^\d+$/.test(unquoted)) {
29
- normalized = normalized.replace(/INITCOND\s*=\s*[^,)]+/i, `INITCOND = '${unquoted}'`);
30
- }
31
- }
32
-
33
- return normalized;
34
- };
35
-
36
- const extractFunctionSignature = (definition: string, isAggregate: boolean): string | null => {
37
- if (isAggregate) {
38
- const createMatch = definition.match(/CREATE\s+(OR\s+REPLACE\s+)?AGGREGATE\s+([^(]+)\(/i);
39
- if (!createMatch) {
40
- return null;
41
- }
42
-
43
- const functionNamePart = createMatch[2].trim().replace(/^[^.]+\./, '');
44
- const argsMatch = definition.match(/CREATE\s+(OR\s+REPLACE\s+)?AGGREGATE\s+[^(]+\(([^)]*)\)/i);
45
- const args = argsMatch ? argsMatch[2].trim() : '';
46
-
47
- return `${functionNamePart}(${args})`;
48
- }
49
-
50
- const createMatch = definition.match(/CREATE\s+(OR\s+REPLACE\s+)?FUNCTION\s+([^(]+)\(/i);
51
- if (!createMatch) {
52
- return null;
53
- }
54
-
55
- const functionNamePart = createMatch[2].trim().replace(/^[^.]+\./, '');
56
- const fullArgsMatch = definition.match(
57
- /CREATE\s+(OR\s+REPLACE\s+)?FUNCTION\s+[^(]+\(([\s\S]*?)\)\s*(RETURNS|LANGUAGE|AS|STRICT|IMMUTABLE|STABLE|VOLATILE|SECURITY)/i,
58
- );
59
-
60
- if (!fullArgsMatch) {
61
- return null;
62
- }
63
-
64
- const argsSection = fullArgsMatch[2].trim();
65
- const args = argsSection
66
- .split(/\s*,\s*/)
67
- .map((arg) => {
68
- return arg.trim().replace(/\s+/g, ' ');
69
- })
70
- .join(', ');
71
-
72
- return `${functionNamePart}(${args})`;
73
- };
74
-
75
- const extractFunctionBody = (definition: string): string => {
76
- const dollarQuoteMatch = definition.match(/AS\s+\$([^$]*)\$([\s\S]*?)\$\1\$/i);
77
- if (dollarQuoteMatch) {
78
- return dollarQuoteMatch[2].trim();
79
- }
80
-
81
- const bodyMatch = definition.match(/AS\s+\$\$([\s\S]*?)\$\$/i) || definition.match(/AS\s+['"]([\s\S]*?)['"]/i);
82
- if (bodyMatch) {
83
- return bodyMatch[1].trim();
84
- }
85
-
86
- return definition;
87
- };
88
-
89
- export const parseFunctionsFile = (filePath: string): ParsedFunction[] => {
90
- if (!existsSync(filePath)) {
91
- return [];
92
- }
93
-
94
- const project = new Project({
95
- manipulationSettings: {
96
- indentationText: IndentationText.TwoSpaces,
97
- },
98
- });
99
- const sourceFile = project.addSourceFileAtPath(filePath);
100
-
101
- try {
102
- const functionsDeclaration = findDeclarationInFile(sourceFile, 'functions');
103
- const functionsArray = staticEval(functionsDeclaration, {}) as string[];
104
-
105
- if (!Array.isArray(functionsArray)) {
106
- return [];
107
- }
108
-
109
- const parsedFunctions: ParsedFunction[] = [];
110
-
111
- for (const definition of functionsArray) {
112
- if (!definition || typeof definition !== 'string') {
113
- continue;
114
- }
115
-
116
- const trimmedDefinition = definition.trim();
117
- const isAggregate = /CREATE\s+(OR\s+REPLACE\s+)?AGGREGATE/i.test(trimmedDefinition);
118
- const signature = extractFunctionSignature(trimmedDefinition, isAggregate);
119
-
120
- if (!signature) {
121
- continue;
122
- }
123
-
124
- const nameMatch = signature.match(/^([^(]+)\(/);
125
- const name = nameMatch ? nameMatch[1].trim().split('.').pop() || '' : '';
126
- const body = isAggregate ? trimmedDefinition : extractFunctionBody(trimmedDefinition);
127
-
128
- parsedFunctions.push({
129
- name,
130
- signature,
131
- body: isAggregate ? normalizeAggregateDefinition(body) : normalizeFunctionBody(body),
132
- fullDefinition: trimmedDefinition,
133
- isAggregate,
134
- });
135
- }
136
-
137
- return parsedFunctions;
138
- } catch (error) {
139
- return [];
140
- }
141
- };
@@ -1,74 +0,0 @@
1
- import { Knex } from 'knex';
2
-
3
- export const generateFunctionsFromDatabase = async (knex: Knex): Promise<string> => {
4
- const regularFunctions = await knex.raw(`
5
- SELECT
6
- pg_get_functiondef(p.oid) as definition
7
- FROM pg_proc p
8
- JOIN pg_namespace n ON p.pronamespace = n.oid
9
- WHERE n.nspname = 'public'
10
- AND NOT EXISTS (SELECT 1 FROM pg_aggregate a WHERE a.aggfnoid = p.oid)
11
- ORDER BY p.proname, pg_get_function_identity_arguments(p.oid)
12
- `);
13
-
14
- const aggregateFunctions = await knex.raw(`
15
- SELECT
16
- p.proname as name,
17
- pg_get_function_identity_arguments(p.oid) as arguments,
18
- a.aggtransfn::regproc::text as trans_func,
19
- a.aggfinalfn::regproc::text as final_func,
20
- a.agginitval as init_val,
21
- pg_catalog.format_type(a.aggtranstype, NULL) as state_type
22
- FROM pg_proc p
23
- JOIN pg_aggregate a ON p.oid = a.aggfnoid
24
- JOIN pg_namespace n ON p.pronamespace = n.oid
25
- WHERE n.nspname = 'public'
26
- ORDER BY p.proname, pg_get_function_identity_arguments(p.oid)
27
- `);
28
-
29
- const functions: string[] = [];
30
-
31
- for (const row of regularFunctions.rows || []) {
32
- if (row.definition) {
33
- functions.push(row.definition.trim());
34
- }
35
- }
36
-
37
- for (const row of aggregateFunctions.rows || []) {
38
- const name = row.name || '';
39
- const argumentsStr = row.arguments || '';
40
- const transFunc = row.trans_func || '';
41
- const finalFunc = row.final_func || '';
42
- const initVal = row.init_val;
43
- const stateType = row.state_type || '';
44
-
45
- if (!name || !transFunc || !stateType) {
46
- continue;
47
- }
48
-
49
- let aggregateDef = `CREATE AGGREGATE ${name}(${argumentsStr}) (\n`;
50
- aggregateDef += ` SFUNC = ${transFunc},\n`;
51
- aggregateDef += ` STYPE = ${stateType}`;
52
-
53
- if (finalFunc) {
54
- aggregateDef += `,\n FINALFUNC = ${finalFunc}`;
55
- }
56
-
57
- if (initVal !== null && initVal !== undefined) {
58
- const initValStr = typeof initVal === 'string' ? `'${initVal}'` : String(initVal);
59
- aggregateDef += `,\n INITCOND = ${initValStr}`;
60
- }
61
-
62
- aggregateDef += '\n);';
63
-
64
- functions.push(aggregateDef);
65
- }
66
-
67
- if (functions.length === 0) {
68
- return `export const functions: string[] = [];\n`;
69
- }
70
-
71
- const functionsArrayString = functions.map((func) => ` ${JSON.stringify(func)}`).join(',\n');
72
-
73
- return `export const functions: string[] = [\n${functionsArrayString},\n];\n`;
74
- };
@@ -1,7 +0,0 @@
1
- export type ParsedFunction = {
2
- name: string;
3
- signature: string;
4
- body: string;
5
- fullDefinition: string;
6
- isAggregate: boolean;
7
- };
@@ -1,221 +0,0 @@
1
- import { Knex } from 'knex';
2
- import { ParsedFunction } from './types';
3
-
4
- type DatabaseFunction = {
5
- name: string;
6
- signature: string;
7
- body: string;
8
- isAggregate: boolean;
9
- definition?: string;
10
- };
11
-
12
- const normalizeWhitespace = (str: string): string => {
13
- return str
14
- .replace(/\s+/g, ' ')
15
- .replace(/\s*\(\s*/g, '(')
16
- .replace(/\s*\)\s*/g, ')')
17
- .replace(/\s*,\s*/g, ',')
18
- .replace(/\s*;\s*/g, ';')
19
- .trim();
20
- };
21
-
22
- const normalizeFunctionBody = (body: string): string => {
23
- return normalizeWhitespace(body);
24
- };
25
-
26
- const extractFunctionBody = (definition: string): string => {
27
- const dollarQuoteMatch = definition.match(/AS\s+\$([^$]*)\$([\s\S]*?)\$\1\$/i);
28
- if (dollarQuoteMatch) {
29
- return dollarQuoteMatch[2].trim();
30
- }
31
-
32
- const bodyMatch = definition.match(/AS\s+\$\$([\s\S]*?)\$\$/i) || definition.match(/AS\s+['"]([\s\S]*?)['"]/i);
33
- if (bodyMatch) {
34
- return bodyMatch[1].trim();
35
- }
36
-
37
- return definition;
38
- };
39
-
40
- const getDatabaseFunctions = async (knex: Knex): Promise<DatabaseFunction[]> => {
41
- const regularFunctions = await knex.raw(`
42
- SELECT
43
- p.proname as name,
44
- pg_get_function_identity_arguments(p.oid) as arguments,
45
- pg_get_functiondef(p.oid) as definition,
46
- false as is_aggregate
47
- FROM pg_proc p
48
- JOIN pg_namespace n ON p.pronamespace = n.oid
49
- WHERE n.nspname = 'public'
50
- AND NOT EXISTS (SELECT 1 FROM pg_aggregate a WHERE a.aggfnoid = p.oid)
51
- ORDER BY p.proname, pg_get_function_identity_arguments(p.oid)
52
- `);
53
-
54
- const aggregateFunctions = await knex.raw(`
55
- SELECT
56
- p.proname as name,
57
- pg_get_function_identity_arguments(p.oid) as arguments,
58
- a.aggtransfn::regproc::text as trans_func,
59
- a.aggfinalfn::regproc::text as final_func,
60
- a.agginitval as init_val,
61
- pg_catalog.format_type(a.aggtranstype, NULL) as state_type,
62
- true as is_aggregate
63
- FROM pg_proc p
64
- JOIN pg_aggregate a ON p.oid = a.aggfnoid
65
- JOIN pg_namespace n ON p.pronamespace = n.oid
66
- WHERE n.nspname = 'public'
67
- ORDER BY p.proname, pg_get_function_identity_arguments(p.oid)
68
- `);
69
-
70
- const result: DatabaseFunction[] = [];
71
-
72
- for (const row of regularFunctions.rows || []) {
73
- const definition = row.definition || '';
74
- const name = row.name || '';
75
- const argumentsStr = row.arguments || '';
76
-
77
- if (!definition) {
78
- continue;
79
- }
80
-
81
- const signature = `${name}(${argumentsStr})`;
82
- const body = normalizeFunctionBody(extractFunctionBody(definition));
83
-
84
- result.push({
85
- name,
86
- signature,
87
- body,
88
- isAggregate: false,
89
- definition,
90
- });
91
- }
92
-
93
- for (const row of aggregateFunctions.rows || []) {
94
- const name = row.name || '';
95
- const argumentsStr = row.arguments || '';
96
- const transFunc = row.trans_func || '';
97
- const finalFunc = row.final_func || '';
98
- const initVal = row.init_val;
99
- const stateType = row.state_type || '';
100
-
101
- const signature = `${name}(${argumentsStr})`;
102
-
103
- let aggregateDef = `CREATE AGGREGATE ${name}(${argumentsStr}) (`;
104
- aggregateDef += `SFUNC = ${transFunc}, STYPE = ${stateType}`;
105
-
106
- if (finalFunc) {
107
- aggregateDef += `, FINALFUNC = ${finalFunc}`;
108
- }
109
-
110
- if (initVal !== null && initVal !== undefined) {
111
- const initValStr = typeof initVal === 'string' ? `'${initVal}'` : String(initVal);
112
- aggregateDef += `, INITCOND = ${initValStr}`;
113
- }
114
-
115
- aggregateDef += ');';
116
-
117
- result.push({
118
- name,
119
- signature,
120
- body: normalizeFunctionBody(aggregateDef),
121
- isAggregate: true,
122
- definition: aggregateDef,
123
- });
124
- }
125
-
126
- return result;
127
- };
128
-
129
- const compareFunctions = (defined: ParsedFunction, db: DatabaseFunction): { changed: boolean; diff?: string } => {
130
- const definedBody = normalizeFunctionBody(defined.body);
131
- const dbBody = normalizeFunctionBody(db.body);
132
-
133
- if (definedBody !== dbBody) {
134
- const definedPreview = definedBody.length > 200 ? `${definedBody.substring(0, 200)}...` : definedBody;
135
- const dbPreview = dbBody.length > 200 ? `${dbBody.substring(0, 200)}...` : dbBody;
136
-
137
- return {
138
- changed: true,
139
- diff: `Definition changed:\n File: ${definedPreview}\n DB: ${dbPreview}`,
140
- };
141
- }
142
-
143
- return { changed: false };
144
- };
145
-
146
- export const updateFunctions = async (knex: Knex, parsedFunctions: ParsedFunction[]): Promise<void> => {
147
- if (parsedFunctions.length === 0) {
148
- return;
149
- }
150
-
151
- const definedFunctions = parsedFunctions;
152
-
153
- const dbFunctions = await getDatabaseFunctions(knex);
154
- const dbFunctionsBySignature = new Map<string, DatabaseFunction>();
155
- for (const func of dbFunctions) {
156
- dbFunctionsBySignature.set(func.signature, func);
157
- }
158
-
159
- console.info(`Found ${definedFunctions.length} function(s) in file, ${dbFunctions.length} function(s) in database.`);
160
-
161
- let updatedCount = 0;
162
- let skippedCount = 0;
163
-
164
- for (const definedFunc of definedFunctions) {
165
- const dbFunc = dbFunctionsBySignature.get(definedFunc.signature);
166
-
167
- if (!dbFunc) {
168
- try {
169
- await knex.raw(definedFunc.fullDefinition);
170
- console.info(`✓ Created ${definedFunc.isAggregate ? 'aggregate' : 'function'}: ${definedFunc.signature}`);
171
- updatedCount++;
172
- } catch (error: any) {
173
- console.error(
174
- `✗ Failed to create ${definedFunc.isAggregate ? 'aggregate' : 'function'} ${definedFunc.signature}:`,
175
- error.message,
176
- );
177
- throw error;
178
- }
179
- } else {
180
- const comparison = compareFunctions(definedFunc, dbFunc);
181
- if (comparison.changed) {
182
- console.info(`\n⚠ ${definedFunc.isAggregate ? 'Aggregate' : 'Function'} ${definedFunc.signature} has changes:`);
183
- if (comparison.diff) {
184
- console.info(comparison.diff);
185
- }
186
- try {
187
- if (definedFunc.isAggregate) {
188
- const dropMatch = definedFunc.fullDefinition.match(/CREATE\s+(OR\s+REPLACE\s+)?AGGREGATE\s+([^(]+)\(/i);
189
- if (dropMatch) {
190
- const functionName = dropMatch[2].trim();
191
- const argsMatch = definedFunc.fullDefinition.match(/CREATE\s+(OR\s+REPLACE\s+)?AGGREGATE\s+[^(]+\(([^)]*)\)/i);
192
- const args = argsMatch ? argsMatch[2].trim() : '';
193
- await knex.raw(`DROP AGGREGATE IF EXISTS ${functionName}${args ? `(${args})` : ''}`);
194
- }
195
- }
196
- await knex.raw(definedFunc.fullDefinition);
197
- console.info(`✓ Updated ${definedFunc.isAggregate ? 'aggregate' : 'function'}: ${definedFunc.signature}\n`);
198
- updatedCount++;
199
- } catch (error: any) {
200
- console.error(
201
- `✗ Failed to update ${definedFunc.isAggregate ? 'aggregate' : 'function'} ${definedFunc.signature}:`,
202
- error.message,
203
- );
204
- throw error;
205
- }
206
- } else {
207
- console.info(
208
- `○ Skipped ${definedFunc.isAggregate ? 'aggregate' : 'function'} (unchanged): ${definedFunc.signature}`,
209
- );
210
- skippedCount++;
211
- }
212
- }
213
- }
214
-
215
- console.info(`\nSummary: ${updatedCount} updated, ${skippedCount} skipped`);
216
- if (updatedCount > 0) {
217
- console.info('Functions updated successfully.');
218
- } else {
219
- console.info('All functions are up to date.');
220
- }
221
- };