lsh-framework 0.5.4

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 (90) hide show
  1. package/.env.example +51 -0
  2. package/README.md +399 -0
  3. package/dist/app.js +33 -0
  4. package/dist/cicd/analytics.js +261 -0
  5. package/dist/cicd/auth.js +269 -0
  6. package/dist/cicd/cache-manager.js +172 -0
  7. package/dist/cicd/data-retention.js +305 -0
  8. package/dist/cicd/performance-monitor.js +224 -0
  9. package/dist/cicd/webhook-receiver.js +634 -0
  10. package/dist/cli.js +500 -0
  11. package/dist/commands/api.js +343 -0
  12. package/dist/commands/self.js +318 -0
  13. package/dist/commands/theme.js +257 -0
  14. package/dist/commands/zsh-import.js +240 -0
  15. package/dist/components/App.js +1 -0
  16. package/dist/components/Divider.js +29 -0
  17. package/dist/components/REPL.js +43 -0
  18. package/dist/components/Terminal.js +232 -0
  19. package/dist/components/UserInput.js +30 -0
  20. package/dist/daemon/api-server.js +315 -0
  21. package/dist/daemon/job-registry.js +554 -0
  22. package/dist/daemon/lshd.js +822 -0
  23. package/dist/daemon/monitoring-api.js +220 -0
  24. package/dist/examples/supabase-integration.js +106 -0
  25. package/dist/lib/api-error-handler.js +183 -0
  26. package/dist/lib/associative-arrays.js +285 -0
  27. package/dist/lib/base-api-server.js +290 -0
  28. package/dist/lib/base-command-registrar.js +286 -0
  29. package/dist/lib/base-job-manager.js +293 -0
  30. package/dist/lib/brace-expansion.js +160 -0
  31. package/dist/lib/builtin-commands.js +439 -0
  32. package/dist/lib/cloud-config-manager.js +347 -0
  33. package/dist/lib/command-validator.js +190 -0
  34. package/dist/lib/completion-system.js +344 -0
  35. package/dist/lib/cron-job-manager.js +364 -0
  36. package/dist/lib/daemon-client-helper.js +141 -0
  37. package/dist/lib/daemon-client.js +501 -0
  38. package/dist/lib/database-persistence.js +638 -0
  39. package/dist/lib/database-schema.js +259 -0
  40. package/dist/lib/enhanced-history-system.js +246 -0
  41. package/dist/lib/env-validator.js +265 -0
  42. package/dist/lib/executors/builtin-executor.js +52 -0
  43. package/dist/lib/extended-globbing.js +411 -0
  44. package/dist/lib/extended-parameter-expansion.js +227 -0
  45. package/dist/lib/floating-point-arithmetic.js +256 -0
  46. package/dist/lib/history-system.js +245 -0
  47. package/dist/lib/interactive-shell.js +460 -0
  48. package/dist/lib/job-builtins.js +580 -0
  49. package/dist/lib/job-manager.js +386 -0
  50. package/dist/lib/job-storage-database.js +156 -0
  51. package/dist/lib/job-storage-memory.js +73 -0
  52. package/dist/lib/logger.js +274 -0
  53. package/dist/lib/lshrc-init.js +177 -0
  54. package/dist/lib/pathname-expansion.js +216 -0
  55. package/dist/lib/prompt-system.js +328 -0
  56. package/dist/lib/script-runner.js +226 -0
  57. package/dist/lib/secrets-manager.js +193 -0
  58. package/dist/lib/shell-executor.js +2504 -0
  59. package/dist/lib/shell-parser.js +958 -0
  60. package/dist/lib/shell-types.js +6 -0
  61. package/dist/lib/shell.lib.js +40 -0
  62. package/dist/lib/supabase-client.js +58 -0
  63. package/dist/lib/theme-manager.js +476 -0
  64. package/dist/lib/variable-expansion.js +385 -0
  65. package/dist/lib/zsh-compatibility.js +658 -0
  66. package/dist/lib/zsh-import-manager.js +699 -0
  67. package/dist/lib/zsh-options.js +328 -0
  68. package/dist/pipeline/job-tracker.js +491 -0
  69. package/dist/pipeline/mcli-bridge.js +302 -0
  70. package/dist/pipeline/pipeline-service.js +1116 -0
  71. package/dist/pipeline/workflow-engine.js +867 -0
  72. package/dist/services/api/api.js +58 -0
  73. package/dist/services/api/auth.js +35 -0
  74. package/dist/services/api/config.js +7 -0
  75. package/dist/services/api/file.js +22 -0
  76. package/dist/services/cron/cron-registrar.js +235 -0
  77. package/dist/services/cron/cron.js +9 -0
  78. package/dist/services/daemon/daemon-registrar.js +565 -0
  79. package/dist/services/daemon/daemon.js +9 -0
  80. package/dist/services/lib/lib.js +86 -0
  81. package/dist/services/log-file-extractor.js +170 -0
  82. package/dist/services/secrets/secrets.js +94 -0
  83. package/dist/services/shell/shell.js +28 -0
  84. package/dist/services/supabase/supabase-registrar.js +367 -0
  85. package/dist/services/supabase/supabase.js +9 -0
  86. package/dist/services/zapier.js +16 -0
  87. package/dist/simple-api-server.js +148 -0
  88. package/dist/store/store.js +31 -0
  89. package/dist/util/lib.util.js +11 -0
  90. package/package.json +144 -0
@@ -0,0 +1,638 @@
1
+ /**
2
+ * Database Persistence Layer for LSH
3
+ * Handles data synchronization with Supabase PostgreSQL
4
+ */
5
+ import { supabaseClient } from './supabase-client.js';
6
+ import * as os from 'os';
7
+ export class DatabasePersistence {
8
+ client;
9
+ userId;
10
+ sessionId;
11
+ constructor(userId) {
12
+ this.client = supabaseClient.getClient();
13
+ this.userId = userId ? this.generateUserUUID(userId) : undefined;
14
+ this.sessionId = this.generateSessionId();
15
+ }
16
+ /**
17
+ * Generate a deterministic UUID from username
18
+ */
19
+ generateUserUUID(username) {
20
+ // Create a simple UUID v5-like deterministic UUID from username
21
+ // In production, you'd use a proper UUID library
22
+ const hash = username.split('').reduce((a, b) => {
23
+ a = ((a << 5) - a) + b.charCodeAt(0);
24
+ return a & a;
25
+ }, 0);
26
+ const hex = Math.abs(hash).toString(16).padStart(8, '0');
27
+ return `${hex.substr(0, 8)}-${hex.substr(0, 4)}-4${hex.substr(1, 3)}-8${hex.substr(0, 3)}-${hex}${hex.substr(0, 4)}`;
28
+ }
29
+ /**
30
+ * Generate a unique session ID
31
+ */
32
+ generateSessionId() {
33
+ return `lsh_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
34
+ }
35
+ /**
36
+ * Initialize database schema
37
+ */
38
+ async initializeSchema() {
39
+ try {
40
+ // Note: In a real implementation, you'd need to run the SQL schema
41
+ // This would typically be done through Supabase dashboard or migrations
42
+ console.log('Database schema initialization would be handled by Supabase migrations');
43
+ return true;
44
+ }
45
+ catch (error) {
46
+ console.error('Failed to initialize database schema:', error);
47
+ return false;
48
+ }
49
+ }
50
+ /**
51
+ * Save shell history entry
52
+ */
53
+ async saveHistoryEntry(entry) {
54
+ try {
55
+ const insertData = {
56
+ ...entry,
57
+ session_id: this.sessionId,
58
+ created_at: new Date().toISOString(),
59
+ updated_at: new Date().toISOString(),
60
+ };
61
+ // Only include user_id if it's not undefined
62
+ if (this.userId !== undefined) {
63
+ insertData.user_id = this.userId;
64
+ }
65
+ const { _data, error } = await this.client
66
+ .from('shell_history')
67
+ .insert([insertData]);
68
+ if (error) {
69
+ console.error('Failed to save history entry:', error);
70
+ return false;
71
+ }
72
+ return true;
73
+ }
74
+ catch (error) {
75
+ console.error('Error saving history entry:', error);
76
+ return false;
77
+ }
78
+ }
79
+ /**
80
+ * Get shell history entries
81
+ */
82
+ async getHistoryEntries(limit = 100, offset = 0) {
83
+ try {
84
+ let query = this.client
85
+ .from('shell_history')
86
+ .select('*')
87
+ .order('timestamp', { ascending: false })
88
+ .range(offset, offset + limit - 1);
89
+ // Only filter by user_id if it's not undefined
90
+ if (this.userId !== undefined) {
91
+ query = query.eq('user_id', this.userId);
92
+ }
93
+ else {
94
+ query = query.is('user_id', null);
95
+ }
96
+ const { data, error } = await query;
97
+ if (error) {
98
+ console.error('Failed to get history entries:', error);
99
+ return [];
100
+ }
101
+ return data || [];
102
+ }
103
+ catch (error) {
104
+ console.error('Error getting history entries:', error);
105
+ return [];
106
+ }
107
+ }
108
+ /**
109
+ * Save shell job
110
+ */
111
+ async saveJob(job) {
112
+ try {
113
+ const insertData = {
114
+ ...job,
115
+ session_id: this.sessionId,
116
+ created_at: new Date().toISOString(),
117
+ updated_at: new Date().toISOString(),
118
+ };
119
+ // Only include user_id if it's not undefined
120
+ if (this.userId !== undefined) {
121
+ insertData.user_id = this.userId;
122
+ }
123
+ const { _data, error } = await this.client
124
+ .from('shell_jobs')
125
+ .insert([insertData]);
126
+ if (error) {
127
+ console.error('Failed to save job:', error);
128
+ return false;
129
+ }
130
+ return true;
131
+ }
132
+ catch (error) {
133
+ console.error('Error saving job:', error);
134
+ return false;
135
+ }
136
+ }
137
+ /**
138
+ * Update shell job status
139
+ */
140
+ async updateJobStatus(jobId, status, exitCode) {
141
+ try {
142
+ const updateData = {
143
+ status,
144
+ updated_at: new Date().toISOString(),
145
+ };
146
+ if (status === 'completed' || status === 'failed') {
147
+ updateData.completed_at = new Date().toISOString();
148
+ if (exitCode !== undefined) {
149
+ updateData.exit_code = exitCode;
150
+ }
151
+ }
152
+ let query = this.client
153
+ .from('shell_jobs')
154
+ .update(updateData)
155
+ .eq('job_id', jobId);
156
+ // Only filter by user_id if it's not undefined
157
+ if (this.userId !== undefined) {
158
+ query = query.eq('user_id', this.userId);
159
+ }
160
+ else {
161
+ query = query.is('user_id', null);
162
+ }
163
+ const { _data, error } = await query;
164
+ if (error) {
165
+ console.error('Failed to update job status:', error);
166
+ return false;
167
+ }
168
+ return true;
169
+ }
170
+ catch (error) {
171
+ console.error('Error updating job status:', error);
172
+ return false;
173
+ }
174
+ }
175
+ /**
176
+ * Get active jobs
177
+ */
178
+ async getActiveJobs() {
179
+ try {
180
+ let query = this.client
181
+ .from('shell_jobs')
182
+ .select('*')
183
+ .in('status', ['running', 'stopped', 'completed', 'failed'])
184
+ .order('created_at', { ascending: false });
185
+ // Only filter by user_id if it's not undefined
186
+ if (this.userId !== undefined) {
187
+ query = query.eq('user_id', this.userId);
188
+ }
189
+ else {
190
+ query = query.is('user_id', null);
191
+ }
192
+ const { data, error } = await query;
193
+ if (error) {
194
+ console.error('Failed to get active jobs:', error);
195
+ return [];
196
+ }
197
+ return data || [];
198
+ }
199
+ catch (error) {
200
+ console.error('Error getting active jobs:', error);
201
+ return [];
202
+ }
203
+ }
204
+ /**
205
+ * Save shell configuration
206
+ */
207
+ async saveConfiguration(config) {
208
+ try {
209
+ const upsertData = {
210
+ ...config,
211
+ created_at: new Date().toISOString(),
212
+ updated_at: new Date().toISOString(),
213
+ };
214
+ // Only include user_id if it's not undefined
215
+ if (this.userId !== undefined) {
216
+ upsertData.user_id = this.userId;
217
+ }
218
+ const { _data, error } = await this.client
219
+ .from('shell_configuration')
220
+ .upsert([upsertData], {
221
+ onConflict: 'user_id,config_key'
222
+ });
223
+ if (error) {
224
+ console.error('Failed to save configuration:', error);
225
+ return false;
226
+ }
227
+ return true;
228
+ }
229
+ catch (error) {
230
+ console.error('Error saving configuration:', error);
231
+ return false;
232
+ }
233
+ }
234
+ /**
235
+ * Get shell configuration
236
+ */
237
+ async getConfiguration(key) {
238
+ try {
239
+ let query = this.client
240
+ .from('shell_configuration')
241
+ .select('*');
242
+ // Only filter by user_id if it's not undefined
243
+ if (this.userId !== undefined) {
244
+ query = query.eq('user_id', this.userId);
245
+ }
246
+ else {
247
+ // If no user_id, get only default configurations
248
+ query = query.is('user_id', null);
249
+ }
250
+ if (key) {
251
+ query = query.eq('config_key', key);
252
+ }
253
+ const { data, error } = await query;
254
+ if (error) {
255
+ console.error('Failed to get configuration:', error);
256
+ return [];
257
+ }
258
+ return data || [];
259
+ }
260
+ catch (error) {
261
+ console.error('Error getting configuration:', error);
262
+ return [];
263
+ }
264
+ }
265
+ /**
266
+ * Save shell alias
267
+ */
268
+ async saveAlias(alias) {
269
+ try {
270
+ const upsertData = {
271
+ ...alias,
272
+ created_at: new Date().toISOString(),
273
+ updated_at: new Date().toISOString(),
274
+ };
275
+ // Only include user_id if it's not undefined
276
+ if (this.userId !== undefined) {
277
+ upsertData.user_id = this.userId;
278
+ }
279
+ const { _data, error } = await this.client
280
+ .from('shell_aliases')
281
+ .upsert([upsertData], {
282
+ onConflict: 'user_id,alias_name'
283
+ });
284
+ if (error) {
285
+ console.error('Failed to save alias:', error);
286
+ return false;
287
+ }
288
+ return true;
289
+ }
290
+ catch (error) {
291
+ console.error('Error saving alias:', error);
292
+ return false;
293
+ }
294
+ }
295
+ /**
296
+ * Get shell aliases
297
+ */
298
+ async getAliases() {
299
+ try {
300
+ let query = this.client
301
+ .from('shell_aliases')
302
+ .select('*')
303
+ .eq('is_active', true);
304
+ // Only filter by user_id if it's not undefined
305
+ if (this.userId !== undefined) {
306
+ query = query.eq('user_id', this.userId);
307
+ }
308
+ else {
309
+ query = query.is('user_id', null);
310
+ }
311
+ const { data, error } = await query;
312
+ if (error) {
313
+ console.error('Failed to get aliases:', error);
314
+ return [];
315
+ }
316
+ return data || [];
317
+ }
318
+ catch (error) {
319
+ console.error('Error getting aliases:', error);
320
+ return [];
321
+ }
322
+ }
323
+ /**
324
+ * Save shell function
325
+ */
326
+ async saveFunction(func) {
327
+ try {
328
+ const upsertData = {
329
+ ...func,
330
+ created_at: new Date().toISOString(),
331
+ updated_at: new Date().toISOString(),
332
+ };
333
+ // Only include user_id if it's not undefined
334
+ if (this.userId !== undefined) {
335
+ upsertData.user_id = this.userId;
336
+ }
337
+ const { _data, error } = await this.client
338
+ .from('shell_functions')
339
+ .upsert([upsertData], {
340
+ onConflict: 'user_id,function_name'
341
+ });
342
+ if (error) {
343
+ console.error('Failed to save function:', error);
344
+ return false;
345
+ }
346
+ return true;
347
+ }
348
+ catch (error) {
349
+ console.error('Error saving function:', error);
350
+ return false;
351
+ }
352
+ }
353
+ /**
354
+ * Get shell functions
355
+ */
356
+ async getFunctions() {
357
+ try {
358
+ let query = this.client
359
+ .from('shell_functions')
360
+ .select('*')
361
+ .eq('is_active', true);
362
+ // Only filter by user_id if it's not undefined
363
+ if (this.userId !== undefined) {
364
+ query = query.eq('user_id', this.userId);
365
+ }
366
+ else {
367
+ query = query.is('user_id', null);
368
+ }
369
+ const { data, error } = await query;
370
+ if (error) {
371
+ console.error('Failed to get functions:', error);
372
+ return [];
373
+ }
374
+ return data || [];
375
+ }
376
+ catch (error) {
377
+ console.error('Error getting functions:', error);
378
+ return [];
379
+ }
380
+ }
381
+ /**
382
+ * Start a new shell session
383
+ */
384
+ async startSession(workingDirectory, environmentVariables) {
385
+ try {
386
+ const insertData = {
387
+ session_id: this.sessionId,
388
+ hostname: os.hostname(),
389
+ working_directory: workingDirectory,
390
+ environment_variables: environmentVariables,
391
+ started_at: new Date().toISOString(),
392
+ is_active: true,
393
+ created_at: new Date().toISOString(),
394
+ updated_at: new Date().toISOString(),
395
+ };
396
+ // Only include user_id if it's not undefined
397
+ if (this.userId !== undefined) {
398
+ insertData.user_id = this.userId;
399
+ }
400
+ const { _data, error } = await this.client
401
+ .from('shell_sessions')
402
+ .insert([insertData]);
403
+ if (error) {
404
+ console.error('Failed to start session:', error);
405
+ return false;
406
+ }
407
+ return true;
408
+ }
409
+ catch (error) {
410
+ console.error('Error starting session:', error);
411
+ return false;
412
+ }
413
+ }
414
+ /**
415
+ * End the current shell session
416
+ */
417
+ async endSession() {
418
+ try {
419
+ let query = this.client
420
+ .from('shell_sessions')
421
+ .update({
422
+ ended_at: new Date().toISOString(),
423
+ is_active: false,
424
+ updated_at: new Date().toISOString(),
425
+ })
426
+ .eq('session_id', this.sessionId);
427
+ // Only filter by user_id if it's not undefined
428
+ if (this.userId !== undefined) {
429
+ query = query.eq('user_id', this.userId);
430
+ }
431
+ else {
432
+ query = query.is('user_id', null);
433
+ }
434
+ const { _data, error } = await query;
435
+ if (error) {
436
+ console.error('Failed to end session:', error);
437
+ return false;
438
+ }
439
+ return true;
440
+ }
441
+ catch (error) {
442
+ console.error('Error ending session:', error);
443
+ return false;
444
+ }
445
+ }
446
+ /**
447
+ * Test database connectivity
448
+ */
449
+ async testConnection() {
450
+ return await supabaseClient.testConnection();
451
+ }
452
+ /**
453
+ * Get session ID
454
+ */
455
+ getSessionId() {
456
+ return this.sessionId;
457
+ }
458
+ /**
459
+ * Get latest rows from all database tables
460
+ */
461
+ async getLatestRows(limit = 5) {
462
+ const result = {};
463
+ try {
464
+ // Get latest shell history entries
465
+ const historyQuery = this.client
466
+ .from('shell_history')
467
+ .select('*')
468
+ .order('created_at', { ascending: false })
469
+ .limit(limit);
470
+ if (this.userId !== undefined) {
471
+ historyQuery.eq('user_id', this.userId);
472
+ }
473
+ else {
474
+ historyQuery.is('user_id', null);
475
+ }
476
+ const { data: historyData } = await historyQuery;
477
+ result.shell_history = historyData || [];
478
+ // Get latest shell jobs
479
+ const jobsQuery = this.client
480
+ .from('shell_jobs')
481
+ .select('*')
482
+ .order('created_at', { ascending: false })
483
+ .limit(limit);
484
+ if (this.userId !== undefined) {
485
+ jobsQuery.eq('user_id', this.userId);
486
+ }
487
+ else {
488
+ jobsQuery.is('user_id', null);
489
+ }
490
+ const { data: jobsData } = await jobsQuery;
491
+ result.shell_jobs = jobsData || [];
492
+ // Get latest shell configuration
493
+ const configQuery = this.client
494
+ .from('shell_configuration')
495
+ .select('*')
496
+ .order('created_at', { ascending: false })
497
+ .limit(limit);
498
+ if (this.userId !== undefined) {
499
+ configQuery.eq('user_id', this.userId);
500
+ }
501
+ else {
502
+ configQuery.is('user_id', null);
503
+ }
504
+ const { data: configData } = await configQuery;
505
+ result.shell_configuration = configData || [];
506
+ // Get latest shell sessions
507
+ const sessionsQuery = this.client
508
+ .from('shell_sessions')
509
+ .select('*')
510
+ .order('created_at', { ascending: false })
511
+ .limit(limit);
512
+ if (this.userId !== undefined) {
513
+ sessionsQuery.eq('user_id', this.userId);
514
+ }
515
+ else {
516
+ sessionsQuery.is('user_id', null);
517
+ }
518
+ const { data: sessionsData } = await sessionsQuery;
519
+ result.shell_sessions = sessionsData || [];
520
+ // Get latest shell aliases
521
+ const aliasesQuery = this.client
522
+ .from('shell_aliases')
523
+ .select('*')
524
+ .order('created_at', { ascending: false })
525
+ .limit(limit);
526
+ if (this.userId !== undefined) {
527
+ aliasesQuery.eq('user_id', this.userId);
528
+ }
529
+ else {
530
+ aliasesQuery.is('user_id', null);
531
+ }
532
+ const { data: aliasesData } = await aliasesQuery;
533
+ result.shell_aliases = aliasesData || [];
534
+ // Get latest shell functions
535
+ const functionsQuery = this.client
536
+ .from('shell_functions')
537
+ .select('*')
538
+ .order('created_at', { ascending: false })
539
+ .limit(limit);
540
+ if (this.userId !== undefined) {
541
+ functionsQuery.eq('user_id', this.userId);
542
+ }
543
+ else {
544
+ functionsQuery.is('user_id', null);
545
+ }
546
+ const { data: functionsData } = await functionsQuery;
547
+ result.shell_functions = functionsData || [];
548
+ // Get latest shell completions
549
+ const completionsQuery = this.client
550
+ .from('shell_completions')
551
+ .select('*')
552
+ .order('created_at', { ascending: false })
553
+ .limit(limit);
554
+ if (this.userId !== undefined) {
555
+ completionsQuery.eq('user_id', this.userId);
556
+ }
557
+ else {
558
+ completionsQuery.is('user_id', null);
559
+ }
560
+ const { data: completionsData } = await completionsQuery;
561
+ result.shell_completions = completionsData || [];
562
+ // Get latest politician trading disclosures (global data, no user filtering)
563
+ const { data: tradingData } = await this.client
564
+ .from('trading_disclosures')
565
+ .select('*')
566
+ .order('created_at', { ascending: false })
567
+ .limit(limit);
568
+ result.trading_disclosures = tradingData || [];
569
+ // Get latest politicians (global data, no user filtering)
570
+ const { data: politiciansData } = await this.client
571
+ .from('politicians')
572
+ .select('*')
573
+ .order('created_at', { ascending: false })
574
+ .limit(limit);
575
+ result.politicians = politiciansData || [];
576
+ // Get latest data pull jobs (global data, no user filtering)
577
+ const { data: dataPullJobsData } = await this.client
578
+ .from('data_pull_jobs')
579
+ .select('*')
580
+ .order('created_at', { ascending: false })
581
+ .limit(limit);
582
+ result.data_pull_jobs = dataPullJobsData || [];
583
+ return result;
584
+ }
585
+ catch (error) {
586
+ console.error('Error getting latest rows:', error);
587
+ return {};
588
+ }
589
+ }
590
+ /**
591
+ * Get latest rows from a specific table
592
+ */
593
+ async getLatestRowsFromTable(tableName, limit = 5) {
594
+ try {
595
+ const validTables = [
596
+ 'shell_history',
597
+ 'shell_jobs',
598
+ 'shell_configuration',
599
+ 'shell_sessions',
600
+ 'shell_aliases',
601
+ 'shell_functions',
602
+ 'shell_completions',
603
+ 'trading_disclosures',
604
+ 'politicians',
605
+ 'data_pull_jobs'
606
+ ];
607
+ if (!validTables.includes(tableName)) {
608
+ throw new Error(`Invalid table name: ${tableName}`);
609
+ }
610
+ let query = this.client
611
+ .from(tableName)
612
+ .select('*')
613
+ .order('created_at', { ascending: false })
614
+ .limit(limit);
615
+ // Filter by user_id if available (except for politician trading tables which are global)
616
+ const globalTables = ['trading_disclosures', 'politicians', 'data_pull_jobs'];
617
+ if (!globalTables.includes(tableName)) {
618
+ if (this.userId !== undefined) {
619
+ query = query.eq('user_id', this.userId);
620
+ }
621
+ else {
622
+ query = query.is('user_id', null);
623
+ }
624
+ }
625
+ const { data, error } = await query;
626
+ if (error) {
627
+ console.error(`Failed to get latest rows from ${tableName}:`, error);
628
+ return [];
629
+ }
630
+ return data || [];
631
+ }
632
+ catch (error) {
633
+ console.error(`Error getting latest rows from ${tableName}:`, error);
634
+ return [];
635
+ }
636
+ }
637
+ }
638
+ export default DatabasePersistence;