vibecodingmachine-core 2025.12.25-25 → 2026.1.22-1441

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 (40) hide show
  1. package/ERROR_REPORTING_API.md +212 -0
  2. package/ERROR_REPORTING_USAGE.md +380 -0
  3. package/__tests__/provider-manager-fallback.test.js +43 -0
  4. package/__tests__/provider-manager-rate-limit.test.js +61 -0
  5. package/__tests__/utils/git-branch-manager.test.js +61 -0
  6. package/package.json +1 -1
  7. package/src/beta-request.js +160 -0
  8. package/src/compliance/compliance-manager.js +5 -2
  9. package/src/database/migrations.js +135 -12
  10. package/src/database/user-database-client.js +127 -8
  11. package/src/database/user-schema.js +28 -0
  12. package/src/health-tracking/__tests__/ide-health-tracker.test.js +420 -0
  13. package/src/health-tracking/__tests__/interaction-recorder.test.js +392 -0
  14. package/src/health-tracking/errors.js +50 -0
  15. package/src/health-tracking/health-reporter.js +331 -0
  16. package/src/health-tracking/ide-health-tracker.js +446 -0
  17. package/src/health-tracking/interaction-recorder.js +161 -0
  18. package/src/health-tracking/json-storage.js +276 -0
  19. package/src/health-tracking/storage-interface.js +63 -0
  20. package/src/health-tracking/validators.js +277 -0
  21. package/src/ide-integration/applescript-manager.cjs +1087 -9
  22. package/src/ide-integration/applescript-manager.js +565 -15
  23. package/src/ide-integration/applescript-utils.js +26 -18
  24. package/src/ide-integration/provider-manager.cjs +158 -28
  25. package/src/ide-integration/quota-detector.cjs +339 -16
  26. package/src/ide-integration/quota-detector.js +6 -1
  27. package/src/index.cjs +36 -1
  28. package/src/index.js +20 -0
  29. package/src/localization/translations/en.js +15 -1
  30. package/src/localization/translations/es.js +14 -0
  31. package/src/requirement-numbering.js +164 -0
  32. package/src/sync/aws-setup.js +4 -4
  33. package/src/utils/admin-utils.js +33 -0
  34. package/src/utils/error-reporter.js +117 -0
  35. package/src/utils/git-branch-manager.js +278 -0
  36. package/src/utils/requirement-helpers.js +44 -5
  37. package/src/utils/requirements-parser.js +28 -3
  38. package/tests/health-tracking/health-reporter.test.js +329 -0
  39. package/tests/health-tracking/ide-health-tracker.test.js +368 -0
  40. package/tests/health-tracking/interaction-recorder.test.js +309 -0
@@ -0,0 +1,61 @@
1
+ const { execSync } = require('child_process');
2
+ const fs = require('fs-extra');
3
+ const path = require('path');
4
+ const os = require('os');
5
+ const { isGitRepo, getCurrentBranch, hasUncommittedChanges } = require('../../src/utils/git-branch-manager');
6
+
7
+ describe('git-branch-manager', () => {
8
+ let tempRepoPath;
9
+
10
+ beforeEach(async () => {
11
+ tempRepoPath = path.join(os.tmpdir(), `vcm-test-repo-${Date.now()}`);
12
+ await fs.ensureDir(tempRepoPath);
13
+ });
14
+
15
+ afterEach(async () => {
16
+ await fs.remove(tempRepoPath);
17
+ });
18
+
19
+ const initRepo = () => {
20
+ execSync('git init', { cwd: tempRepoPath, stdio: 'ignore' });
21
+ execSync('git config user.email "test@example.com"', { cwd: tempRepoPath, stdio: 'ignore' });
22
+ execSync('git config user.name "Test User"', { cwd: tempRepoPath, stdio: 'ignore' });
23
+ };
24
+
25
+ test('isGitRepo identifies git repositories', () => {
26
+ expect(isGitRepo(tempRepoPath)).toBe(false);
27
+ initRepo();
28
+ expect(isGitRepo(tempRepoPath)).toBe(true);
29
+ });
30
+
31
+ test('getCurrentBranch returns the current branch name', async () => {
32
+ initRepo();
33
+ // Must commit something for HEAD to exist
34
+ await fs.writeFile(path.join(tempRepoPath, 'initial.txt'), 'init');
35
+ execSync('git add .', { cwd: tempRepoPath, stdio: 'ignore' });
36
+ execSync('git commit -m "initial commit"', { cwd: tempRepoPath, stdio: 'ignore' });
37
+
38
+ const branch = getCurrentBranch(tempRepoPath);
39
+ expect(branch).toMatch(/^(main|master)$/);
40
+ });
41
+
42
+ test('hasUncommittedChanges detects dirty repository', async () => {
43
+ initRepo();
44
+ // Should be false initially (even if empty)
45
+ expect(hasUncommittedChanges(tempRepoPath)).toBe(false);
46
+
47
+ // Create a file
48
+ await fs.writeFile(path.join(tempRepoPath, 'test.txt'), 'hello');
49
+ // Untracked file counts as uncommitted change
50
+ expect(hasUncommittedChanges(tempRepoPath)).toBe(true);
51
+
52
+ // Commit it
53
+ execSync('git add .', { cwd: tempRepoPath, stdio: 'ignore' });
54
+ execSync('git commit -m "initial commit"', { cwd: tempRepoPath, stdio: 'ignore' });
55
+ expect(hasUncommittedChanges(tempRepoPath)).toBe(false);
56
+
57
+ // Modify it
58
+ await fs.appendFile(path.join(tempRepoPath, 'test.txt'), ' world');
59
+ expect(hasUncommittedChanges(tempRepoPath)).toBe(true);
60
+ });
61
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vibecodingmachine-core",
3
- "version": "2025.12.25-0025",
3
+ "version": "2026.01.22-1441",
4
4
  "description": "Shared core logic for Vibe Coding Machine IDE integration",
5
5
  "main": "src/index.cjs",
6
6
  "module": "src/index.js",
@@ -0,0 +1,160 @@
1
+ /**
2
+ * Beta Request Management for VibeCodingMachine
3
+ *
4
+ * Handles beta access requests from web, CLI, and GUI applications
5
+ */
6
+
7
+ const crypto = require('crypto');
8
+
9
+ class BetaRequestManager {
10
+ constructor(apiBaseUrl = null) {
11
+ this.apiBaseUrl = apiBaseUrl || process.env.VIBECODINGMACHINE_API_URL || 'https://api.vibecodingmachine.com';
12
+ this.authToken = null;
13
+ }
14
+
15
+ /**
16
+ * Set authentication token for API requests
17
+ */
18
+ setAuthToken(token) {
19
+ this.authToken = token;
20
+ }
21
+
22
+ /**
23
+ * Make authenticated API request
24
+ */
25
+ async apiRequest(endpoint, options = {}) {
26
+ const url = `${this.apiBaseUrl}${endpoint}`;
27
+ const headers = {
28
+ 'Content-Type': 'application/json',
29
+ ...options.headers
30
+ };
31
+
32
+ if (this.authToken) {
33
+ headers.Authorization = `Bearer ${this.authToken}`;
34
+ }
35
+
36
+ const fetch = globalThis.fetch || require('node-fetch');
37
+ const response = await fetch(url, {
38
+ ...options,
39
+ headers
40
+ });
41
+
42
+ if (!response.ok) {
43
+ const error = await response.json().catch(() => ({ error: 'Network error' }));
44
+ throw new Error(error.error || `HTTP ${response.status}`);
45
+ }
46
+
47
+ return response.json();
48
+ }
49
+
50
+ /**
51
+ * Submit a beta access request
52
+ */
53
+ async submitBetaRequest(requestData) {
54
+ try {
55
+ const result = await this.apiRequest('/api/beta/request', {
56
+ method: 'POST',
57
+ body: JSON.stringify({
58
+ ...requestData,
59
+ requestDate: Date.now(),
60
+ status: 'REQUEST'
61
+ })
62
+ });
63
+ return result;
64
+ } catch (error) {
65
+ console.error('Beta request submission failed:', error);
66
+ throw error;
67
+ }
68
+ }
69
+
70
+ /**
71
+ * Check if user already has a pending or approved request
72
+ */
73
+ async checkExistingRequest(email) {
74
+ try {
75
+ const userId = this.generateUserId(email);
76
+ const result = await this.apiRequest(`/api/users/${userId}`);
77
+ return result.user;
78
+ } catch (error) {
79
+ // User doesn't exist or API error
80
+ return null;
81
+ }
82
+ }
83
+
84
+ /**
85
+ * Validate request data
86
+ */
87
+ validateRequestData(data) {
88
+ const errors = [];
89
+
90
+ if (!data.email || !data.email.trim()) {
91
+ errors.push('Email is required');
92
+ }
93
+
94
+ if (data.email && !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(data.email)) {
95
+ errors.push('Invalid email format');
96
+ }
97
+
98
+ if (!data.name || !data.name.trim()) {
99
+ errors.push('Name is required');
100
+ }
101
+
102
+ if (!data.language) {
103
+ errors.push('Language preference is required');
104
+ }
105
+
106
+ if (!data.appPurpose || !data.appPurpose.trim()) {
107
+ errors.push('App purpose description is required');
108
+ }
109
+
110
+ return {
111
+ isValid: errors.length === 0,
112
+ errors
113
+ };
114
+ }
115
+
116
+ /**
117
+ * Get supported languages
118
+ */
119
+ getSupportedLanguages() {
120
+ return {
121
+ 'en': 'English',
122
+ 'es': 'Spanish'
123
+ };
124
+ }
125
+
126
+ /**
127
+ * Generate user ID from email (consistent with existing system)
128
+ */
129
+ generateUserId(email) {
130
+ return crypto.createHash('sha256').update(email.toLowerCase()).digest('hex').substring(0, 16);
131
+ }
132
+
133
+ /**
134
+ * Format request data for email notification
135
+ */
136
+ formatRequestForEmail(requestData) {
137
+ const supportedLanguages = this.getSupportedLanguages();
138
+ const languageName = supportedLanguages[requestData.language] || requestData.language;
139
+
140
+ return {
141
+ recipient: 'jesse.d.olsen@gmail.com',
142
+ subject: `New Beta Request: ${requestData.name} (${requestData.email})`,
143
+ body: `
144
+ New VibeCodingMachine beta access request received:
145
+
146
+ Name: ${requestData.name}
147
+ Email: ${requestData.email}
148
+ Language: ${languageName}
149
+ App Purpose: ${requestData.appPurpose}
150
+ Other Info: ${requestData.otherInfo || 'None provided'}
151
+ Request Date: ${new Date(requestData.requestDate).toLocaleString()}
152
+ Source: ${requestData.source || 'Unknown'}
153
+
154
+ Please review and approve/reject this request in the admin panel.
155
+ `.trim()
156
+ };
157
+ }
158
+ }
159
+
160
+ module.exports = BetaRequestManager;
@@ -10,8 +10,11 @@ const fs = require('fs').promises
10
10
  const path = require('path')
11
11
 
12
12
  class ComplianceManager {
13
- constructor() {
13
+ constructor(authToken = null) {
14
14
  this.userDb = new UserDatabase()
15
+ if (authToken) {
16
+ this.userDb.setAuthToken(authToken)
17
+ }
15
18
  }
16
19
 
17
20
  /**
@@ -19,7 +22,7 @@ class ComplianceManager {
19
22
  * Returns what needs to be acknowledged
20
23
  */
21
24
  async checkComplianceStatus(userId) {
22
- const user = await this.userDb.getUser(userId)
25
+ const user = await this.userDb.getUser()
23
26
 
24
27
  if (!user) {
25
28
  throw new Error('User not found')
@@ -6,11 +6,11 @@
6
6
  */
7
7
 
8
8
  const { DynamoDBClient } = require('@aws-sdk/client-dynamodb')
9
- const {
10
- CreateTableCommand,
11
- DescribeTableCommand,
9
+ const {
10
+ CreateTableCommand,
11
+ DescribeTableCommand,
12
12
  UpdateTableCommand,
13
- ListTablesCommand
13
+ ListTablesCommand
14
14
  } = require('@aws-sdk/client-dynamodb')
15
15
 
16
16
  class DatabaseMigrations {
@@ -25,13 +25,15 @@ class DatabaseMigrations {
25
25
  */
26
26
  async runMigrations() {
27
27
  console.log('🔄 Running database migrations...')
28
-
28
+
29
29
  try {
30
30
  await this.createUsersTable()
31
31
  await this.createComputersTable()
32
32
  await this.createProjectsTable()
33
33
  await this.createActivityTable()
34
-
34
+ await this.createErrorsTable()
35
+ await this.createFeedbackTable()
36
+
35
37
  console.log('✅ All migrations completed successfully')
36
38
  } catch (error) {
37
39
  console.error('❌ Migration failed:', error)
@@ -44,7 +46,7 @@ class DatabaseMigrations {
44
46
  */
45
47
  async createUsersTable() {
46
48
  const tableName = 'vibecodingmachine-users'
47
-
49
+
48
50
  if (await this.tableExists(tableName)) {
49
51
  console.log(`📋 Table ${tableName} already exists`)
50
52
  return
@@ -94,7 +96,7 @@ class DatabaseMigrations {
94
96
  */
95
97
  async createComputersTable() {
96
98
  const tableName = 'vibecodingmachine-computers'
97
-
99
+
98
100
  if (await this.tableExists(tableName)) {
99
101
  console.log(`📋 Table ${tableName} already exists`)
100
102
  return
@@ -137,7 +139,7 @@ class DatabaseMigrations {
137
139
  */
138
140
  async createProjectsTable() {
139
141
  const tableName = 'vibecodingmachine-projects'
140
-
142
+
141
143
  if (await this.tableExists(tableName)) {
142
144
  console.log(`📋 Table ${tableName} already exists`)
143
145
  return
@@ -180,7 +182,7 @@ class DatabaseMigrations {
180
182
  */
181
183
  async createActivityTable() {
182
184
  const tableName = 'vibecodingmachine-activity'
183
-
185
+
184
186
  if (await this.tableExists(tableName)) {
185
187
  console.log(`📋 Table ${tableName} already exists`)
186
188
  return
@@ -222,6 +224,126 @@ class DatabaseMigrations {
222
224
  console.log(`✅ Created table: ${tableName}`)
223
225
  }
224
226
 
227
+ /**
228
+ * Create errors table for tracking application errors
229
+ */
230
+ async createErrorsTable() {
231
+ const tableName = 'vibecodingmachine-errors'
232
+
233
+ if (await this.tableExists(tableName)) {
234
+ console.log(`📋 Table ${tableName} already exists`)
235
+ return
236
+ }
237
+
238
+ const params = {
239
+ TableName: tableName,
240
+ KeySchema: [
241
+ { AttributeName: 'errorId', KeyType: 'HASH' }
242
+ ],
243
+ AttributeDefinitions: [
244
+ { AttributeName: 'errorId', AttributeType: 'S' }
245
+ ],
246
+ BillingMode: 'PAY_PER_REQUEST',
247
+ Tags: [
248
+ { Key: 'Application', Value: 'VibeCodingMachine' },
249
+ { Key: 'Environment', Value: process.env.NODE_ENV || 'development' }
250
+ ]
251
+ }
252
+
253
+ await this.client.send(new CreateTableCommand(params))
254
+ console.log(`✅ Created table: ${tableName}`)
255
+ }
256
+
257
+ /**
258
+ * Create feedback table for user comments
259
+ */
260
+ async createFeedbackTable() {
261
+ const tableName = 'vibecodingmachine-feedback'
262
+
263
+ if (await this.tableExists(tableName)) {
264
+ console.log(`📋 Table ${tableName} already exists`)
265
+ return
266
+ }
267
+
268
+ const params = {
269
+ TableName: tableName,
270
+ KeySchema: [
271
+ { AttributeName: 'feedbackId', KeyType: 'HASH' }
272
+ ],
273
+ AttributeDefinitions: [
274
+ { AttributeName: 'feedbackId', AttributeType: 'S' }
275
+ ],
276
+ BillingMode: 'PAY_PER_REQUEST',
277
+ Tags: [
278
+ { Key: 'Application', Value: 'VibeCodingMachine' },
279
+ { Key: 'Environment', Value: process.env.NODE_ENV || 'development' }
280
+ ]
281
+ }
282
+
283
+ await this.client.send(new CreateTableCommand(params))
284
+ console.log(`✅ Created table: ${tableName}`)
285
+ }
286
+
287
+ /**
288
+ * Create errors table for tracking application errors
289
+ */
290
+ async createErrorsTable() {
291
+ const tableName = 'vibecodingmachine-errors'
292
+
293
+ if (await this.tableExists(tableName)) {
294
+ console.log(`📋 Table ${tableName} already exists`)
295
+ return
296
+ }
297
+
298
+ const params = {
299
+ TableName: tableName,
300
+ KeySchema: [
301
+ { AttributeName: 'errorId', KeyType: 'HASH' }
302
+ ],
303
+ AttributeDefinitions: [
304
+ { AttributeName: 'errorId', AttributeType: 'S' }
305
+ ],
306
+ BillingMode: 'PAY_PER_REQUEST',
307
+ Tags: [
308
+ { Key: 'Application', Value: 'VibeCodingMachine' },
309
+ { Key: 'Environment', Value: process.env.NODE_ENV || 'development' }
310
+ ]
311
+ }
312
+
313
+ await this.client.send(new CreateTableCommand(params))
314
+ console.log(`✅ Created table: ${tableName}`)
315
+ }
316
+
317
+ /**
318
+ * Create feedback table for user comments
319
+ */
320
+ async createFeedbackTable() {
321
+ const tableName = 'vibecodingmachine-feedback'
322
+
323
+ if (await this.tableExists(tableName)) {
324
+ console.log(`📋 Table ${tableName} already exists`)
325
+ return
326
+ }
327
+
328
+ const params = {
329
+ TableName: tableName,
330
+ KeySchema: [
331
+ { AttributeName: 'feedbackId', KeyType: 'HASH' }
332
+ ],
333
+ AttributeDefinitions: [
334
+ { AttributeName: 'feedbackId', AttributeType: 'S' }
335
+ ],
336
+ BillingMode: 'PAY_PER_REQUEST',
337
+ Tags: [
338
+ { Key: 'Application', Value: 'VibeCodingMachine' },
339
+ { Key: 'Environment', Value: process.env.NODE_ENV || 'development' }
340
+ ]
341
+ }
342
+
343
+ await this.client.send(new CreateTableCommand(params))
344
+ console.log(`✅ Created table: ${tableName}`)
345
+ }
346
+
225
347
  /**
226
348
  * Check if a table exists
227
349
  */
@@ -272,10 +394,11 @@ class DatabaseMigrations {
272
394
  */
273
395
  async getAllTableStatus() {
274
396
  const tableNames = [
275
- 'vibecodingmachine-users',
276
397
  'vibecodingmachine-computers',
277
398
  'vibecodingmachine-projects',
278
- 'vibecodingmachine-activity'
399
+ 'vibecodingmachine-activity',
400
+ 'vibecodingmachine-errors',
401
+ 'vibecodingmachine-feedback'
279
402
  ]
280
403
 
281
404
  const statuses = await Promise.all(
@@ -18,9 +18,21 @@ try {
18
18
 
19
19
  class UserDatabaseClient {
20
20
  constructor(apiBaseUrl = null) {
21
- // Default to production API server, or use provided URL
22
- this.apiBaseUrl = apiBaseUrl || process.env.VIBECODINGMACHINE_API_URL || 'https://api.vibecodingmachine.com';
21
+ // Check if we're in a development workspace
22
+ const fs = require('fs');
23
+ const path = require('path');
24
+ const rootDir = path.join(__dirname, '..', '..', '..', '..');
25
+ const isDevWorkspace = fs.existsSync(path.join(rootDir, '.git'));
26
+
27
+ // Default to local dev server in development, production otherwise
28
+ if (isDevWorkspace && !apiBaseUrl && !process.env.VIBECODINGMACHINE_API_URL) {
29
+ this.apiBaseUrl = 'http://localhost:3457';
30
+ } else {
31
+ this.apiBaseUrl = apiBaseUrl || process.env.VIBECODINGMACHINE_API_URL || 'https://api.vibecodingmachine.com';
32
+ }
33
+
23
34
  this.authToken = null;
35
+ console.log(require('chalk').gray(`UserDatabaseClient: Using API at ${this.apiBaseUrl}`));
24
36
  }
25
37
 
26
38
  /**
@@ -44,14 +56,36 @@ class UserDatabaseClient {
44
56
  headers.Authorization = `Bearer ${this.authToken}`;
45
57
  }
46
58
 
47
- const response = await fetch(url, {
48
- ...options,
49
- headers
50
- });
59
+ let response;
60
+ try {
61
+ response = await fetch(url, {
62
+ ...options,
63
+ headers
64
+ });
65
+ } catch (fetchError) {
66
+ const msg = `Connection failed: ${fetchError.message}`;
67
+ console.warn(`UserDatabaseClient: ${msg} [${url}]`);
68
+ throw new Error(msg);
69
+ }
51
70
 
52
71
  if (!response.ok) {
53
- const error = await response.json().catch(() => ({ error: 'Network error' }));
54
- throw new Error(error.error || `HTTP ${response.status}`);
72
+ let errorDetail = '';
73
+ let bodyText = '';
74
+ try {
75
+ bodyText = await response.text();
76
+ try {
77
+ const errorJson = JSON.parse(bodyText);
78
+ errorDetail = errorJson.error || errorJson.message || '';
79
+ } catch (e) {
80
+ errorDetail = bodyText;
81
+ }
82
+ } catch (e) {
83
+ errorDetail = 'Could not read response body';
84
+ }
85
+
86
+ if (errorDetail.length > 200) errorDetail = errorDetail.substring(0, 200) + '...';
87
+ const errorMsg = errorDetail || `HTTP ${response.status}`;
88
+ throw new Error(errorMsg);
55
89
  }
56
90
 
57
91
  return response.json();
@@ -228,6 +262,45 @@ class UserDatabaseClient {
228
262
  throw new Error('setAdminStatus not allowed from client');
229
263
  }
230
264
 
265
+ /**
266
+ * Submit a beta access request
267
+ */
268
+ async submitBetaRequest(requestData) {
269
+ if (!this.authToken) {
270
+ console.warn('UserDatabase: No auth token, beta request submission requires authentication');
271
+ throw new Error('Authentication required for beta request');
272
+ }
273
+
274
+ try {
275
+ const result = await this.apiRequest('/api/beta/request', {
276
+ method: 'POST',
277
+ body: JSON.stringify({
278
+ ...requestData,
279
+ requestDate: Date.now(),
280
+ status: 'REQUEST'
281
+ })
282
+ });
283
+ return result;
284
+ } catch (error) {
285
+ console.warn('UserDatabase: submitBetaRequest failed:', error.message);
286
+ throw error;
287
+ }
288
+ }
289
+
290
+ /**
291
+ * Check if user already has a pending or approved request
292
+ */
293
+ async checkExistingRequest(email) {
294
+ try {
295
+ const userId = this.generateUserId(email);
296
+ const result = await this.apiRequest(`/api/users/${userId}`);
297
+ return result.user;
298
+ } catch (error) {
299
+ // User doesn't exist or API error
300
+ return null;
301
+ }
302
+ }
303
+
231
304
  /**
232
305
  * Check if user is an admin (not implemented client-side for security)
233
306
  */
@@ -235,6 +308,52 @@ class UserDatabaseClient {
235
308
  return false; // Client-side can't check admin status
236
309
  }
237
310
 
311
+ /**
312
+ * Submit user feedback to the admin database
313
+ */
314
+ async submitFeedback(feedbackData) {
315
+ try {
316
+ const response = await this.apiRequest('/api/feedback', {
317
+ method: 'POST',
318
+ body: JSON.stringify({
319
+ timestamp: Date.now(),
320
+ hostname: os.hostname(),
321
+ platform: os.platform(),
322
+ ...feedbackData
323
+ })
324
+ });
325
+ return response;
326
+ } catch (error) {
327
+ console.warn('UserDatabase: submitFeedback failed:', error.message);
328
+ throw error;
329
+ }
330
+ }
331
+
332
+ /**
333
+ * Report an error to the admin database for tracking and fixing
334
+ */
335
+ async reportError(errorData) {
336
+ try {
337
+ const errorReport = {
338
+ timestamp: Date.now(),
339
+ hostname: os.hostname(),
340
+ platform: os.platform(),
341
+ arch: os.arch(),
342
+ nodeVersion: process.version,
343
+ ...errorData
344
+ };
345
+
346
+ await this.apiRequest('/api/errors/report', {
347
+ method: 'POST',
348
+ body: JSON.stringify(errorReport)
349
+ });
350
+
351
+ return true;
352
+ } catch (error) {
353
+ console.warn('UserDatabase: reportError failed:', error.message);
354
+ return false;
355
+ }
356
+ }
238
357
  // Helper methods (same as server-side)
239
358
  generateUserId(email) {
240
359
  return crypto.createHash('sha256').update(email.toLowerCase()).digest('hex').substring(0, 16);
@@ -94,6 +94,20 @@ class UserDatabase {
94
94
  return this.apiClient.setAdminStatus(userId, isAdmin)
95
95
  }
96
96
 
97
+ /**
98
+ * Submit a beta access request
99
+ */
100
+ async submitBetaRequest(requestData) {
101
+ return this.apiClient.submitBetaRequest(requestData)
102
+ }
103
+
104
+ /**
105
+ * Check if user already has a pending or approved request
106
+ */
107
+ async checkExistingRequest(email) {
108
+ return this.apiClient.checkExistingRequest(email)
109
+ }
110
+
97
111
  /**
98
112
  * Check if user is an admin
99
113
  */
@@ -101,6 +115,20 @@ class UserDatabase {
101
115
  return this.apiClient.isAdmin(userId)
102
116
  }
103
117
 
118
+ /**
119
+ * Submit user feedback to the admin database
120
+ */
121
+ async submitFeedback(feedbackData) {
122
+ return this.apiClient.submitFeedback(feedbackData)
123
+ }
124
+
125
+ /**
126
+ * Report an error to the admin database for tracking and fixing
127
+ */
128
+ async reportError(errorData) {
129
+ return this.apiClient.reportError(errorData)
130
+ }
131
+
104
132
  // Helper methods - delegate to API client
105
133
  generateUserId(email) {
106
134
  return this.apiClient.generateUserId(email)