dzql 0.4.0 → 0.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.
- package/package.json +1 -1
- package/src/server/db.js +88 -5
- package/src/server/logger.js +17 -4
- package/src/server/namespace.js +5 -0
package/package.json
CHANGED
package/src/server/db.js
CHANGED
|
@@ -35,7 +35,10 @@ export const listen_sql = postgres(DATABASE_URL, {
|
|
|
35
35
|
onnotice: process.env.NODE_ENV === 'test' ? () => {} : undefined,
|
|
36
36
|
});
|
|
37
37
|
|
|
38
|
-
|
|
38
|
+
// Only log connection info in development
|
|
39
|
+
if (process.env.NODE_ENV !== 'production' && process.env.NODE_ENV !== 'test') {
|
|
40
|
+
dbLogger.info(`Database connected: ${DATABASE_URL.replace(/\/\/.*@/, '//***@')}`);
|
|
41
|
+
}
|
|
39
42
|
|
|
40
43
|
// Cache for function parameter metadata
|
|
41
44
|
const functionParamCache = new Map();
|
|
@@ -170,13 +173,93 @@ export async function setupListeners(callback) {
|
|
|
170
173
|
}
|
|
171
174
|
}
|
|
172
175
|
|
|
176
|
+
// Cache for mode detection (null = not checked, true = compiled, false = runtime)
|
|
177
|
+
let isCompiledMode = null;
|
|
178
|
+
|
|
179
|
+
// Auto-detect if we're in compiled or runtime mode
|
|
180
|
+
async function detectMode() {
|
|
181
|
+
if (isCompiledMode !== null) {
|
|
182
|
+
return isCompiledMode;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
try {
|
|
186
|
+
// Check if dzql.generic_exec exists
|
|
187
|
+
const result = await sql`
|
|
188
|
+
SELECT 1 FROM pg_proc
|
|
189
|
+
WHERE proname = 'generic_exec'
|
|
190
|
+
AND pronamespace = (SELECT oid FROM pg_namespace WHERE nspname = 'dzql')
|
|
191
|
+
LIMIT 1
|
|
192
|
+
`;
|
|
193
|
+
isCompiledMode = result.length === 0; // If no results, it's compiled mode
|
|
194
|
+
dbLogger.trace(isCompiledMode ? 'Detected compiled mode' : 'Detected runtime mode');
|
|
195
|
+
} catch (error) {
|
|
196
|
+
// If there's an error checking, assume runtime mode
|
|
197
|
+
isCompiledMode = false;
|
|
198
|
+
dbLogger.trace('Error detecting mode, assuming runtime mode');
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
return isCompiledMode;
|
|
202
|
+
}
|
|
203
|
+
|
|
173
204
|
// DZQL Generic Operations
|
|
174
205
|
export async function callDZQLOperation(operation, entity, args, userId) {
|
|
175
206
|
dbLogger.trace(`DZQL ${operation}.${entity} for user ${userId}`);
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
207
|
+
|
|
208
|
+
const compiled = await detectMode();
|
|
209
|
+
|
|
210
|
+
if (!compiled) {
|
|
211
|
+
// Runtime mode - use generic_exec
|
|
212
|
+
const result = await sql`
|
|
213
|
+
SELECT dzql.generic_exec(${operation}, ${entity}, ${args}, ${userId}) as result
|
|
214
|
+
`;
|
|
215
|
+
return result[0].result;
|
|
216
|
+
} else {
|
|
217
|
+
// Compiled mode - call compiled function directly
|
|
218
|
+
const compiledFunctionName = `${operation}_${entity}`;
|
|
219
|
+
|
|
220
|
+
// Different operations have different signatures:
|
|
221
|
+
// - search: search_entity(p_user_id, p_filters, p_search, p_sort, p_page, p_limit)
|
|
222
|
+
// - get: get_entity(p_user_id, p_id, p_on_date)
|
|
223
|
+
// - save: save_entity(p_user_id, p_data, p_on_date)
|
|
224
|
+
// - delete: delete_entity(p_user_id, p_id)
|
|
225
|
+
// - lookup: lookup_entity(p_user_id, p_term, p_limit)
|
|
226
|
+
|
|
227
|
+
if (operation === 'search') {
|
|
228
|
+
// Extract search parameters from args
|
|
229
|
+
const filters = args.filters || args.p_filters || {};
|
|
230
|
+
const search = args.search || null;
|
|
231
|
+
const sort = args.sort || null;
|
|
232
|
+
const page = args.page || 1;
|
|
233
|
+
const limit = args.limit || 25;
|
|
234
|
+
|
|
235
|
+
const result = await sql.unsafe(`
|
|
236
|
+
SELECT ${compiledFunctionName}($1::int, $2::jsonb, $3::text, $4::jsonb, $5::int, $6::int) as result
|
|
237
|
+
`, [userId, filters, search, sort, page, limit]);
|
|
238
|
+
return result[0].result;
|
|
239
|
+
} else if (operation === 'get') {
|
|
240
|
+
const result = await sql.unsafe(`
|
|
241
|
+
SELECT ${compiledFunctionName}($1::int, $2::int, NULL) as result
|
|
242
|
+
`, [userId, args.id]);
|
|
243
|
+
return result[0].result;
|
|
244
|
+
} else if (operation === 'save') {
|
|
245
|
+
const result = await sql.unsafe(`
|
|
246
|
+
SELECT ${compiledFunctionName}($1::int, $2::jsonb, NULL) as result
|
|
247
|
+
`, [userId, args]);
|
|
248
|
+
return result[0].result;
|
|
249
|
+
} else if (operation === 'delete') {
|
|
250
|
+
const result = await sql.unsafe(`
|
|
251
|
+
SELECT ${compiledFunctionName}($1::int, $2::int) as result
|
|
252
|
+
`, [userId, args.id]);
|
|
253
|
+
return result[0].result;
|
|
254
|
+
} else if (operation === 'lookup') {
|
|
255
|
+
const result = await sql.unsafe(`
|
|
256
|
+
SELECT ${compiledFunctionName}($1::int, $2::text, $3::int) as result
|
|
257
|
+
`, [userId, args.term || '', args.limit || 10]);
|
|
258
|
+
return result[0].result;
|
|
259
|
+
} else {
|
|
260
|
+
throw new Error(`Unknown operation: ${operation}`);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
180
263
|
}
|
|
181
264
|
|
|
182
265
|
// DZQL nested proxy factory
|
package/src/server/logger.js
CHANGED
|
@@ -12,6 +12,15 @@ const LOG_LEVELS = {
|
|
|
12
12
|
// Default log level from environment or INFO
|
|
13
13
|
const DEFAULT_LEVEL = process.env.LOG_LEVEL?.toUpperCase() || "INFO";
|
|
14
14
|
|
|
15
|
+
// Detect if running in CLI context (invokej/tasks.js)
|
|
16
|
+
const isCliContext = () => {
|
|
17
|
+
// Check if main module contains 'tasks.js' or 'invokej'
|
|
18
|
+
const mainModule = process.argv[1] || '';
|
|
19
|
+
return mainModule.includes('tasks.js') ||
|
|
20
|
+
mainModule.includes('invokej') ||
|
|
21
|
+
mainModule.includes('invj');
|
|
22
|
+
};
|
|
23
|
+
|
|
15
24
|
// Parse LOG_CATEGORIES from environment
|
|
16
25
|
// Format: "ws:debug,db:trace,auth:info" or "*:debug" for all
|
|
17
26
|
const parseCategories = () => {
|
|
@@ -20,8 +29,9 @@ const parseCategories = () => {
|
|
|
20
29
|
|
|
21
30
|
if (!envCategories) {
|
|
22
31
|
// Default settings for development vs production
|
|
23
|
-
|
|
24
|
-
|
|
32
|
+
// CLI context defaults to ERROR level unless explicitly configured
|
|
33
|
+
if (process.env.NODE_ENV === "production" || isCliContext()) {
|
|
34
|
+
categories["*"] = LOG_LEVELS.ERROR; // Only errors in production/CLI
|
|
25
35
|
} else if (process.env.NODE_ENV === "test") {
|
|
26
36
|
categories["*"] = LOG_LEVELS.ERROR;
|
|
27
37
|
} else {
|
|
@@ -220,8 +230,11 @@ export const timed = async (category, operation, fn) => {
|
|
|
220
230
|
}
|
|
221
231
|
};
|
|
222
232
|
|
|
223
|
-
// Log configuration on startup (only in development)
|
|
224
|
-
if (
|
|
233
|
+
// Log configuration on startup (only in development and when explicitly debugging)
|
|
234
|
+
// Suppress banner if LOG_CATEGORIES is not set (user doesn't care about logging config)
|
|
235
|
+
if (process.env.NODE_ENV !== "production" &&
|
|
236
|
+
process.env.NODE_ENV !== "test" &&
|
|
237
|
+
process.env.LOG_CATEGORIES) {
|
|
225
238
|
const config = getConfig();
|
|
226
239
|
console.log(colors.bright + "=== Logger Configuration ===" + colors.reset);
|
|
227
240
|
console.log("Categories:", config.categories);
|