archicore 0.2.0 → 0.2.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.
@@ -1,40 +1,45 @@
1
1
  /**
2
2
  * Authentication Service for ArchiCore
3
+ *
4
+ * Supports PostgreSQL (primary) with JSON file fallback
3
5
  */
4
6
  import { randomUUID } from 'crypto';
5
7
  import { createHash } from 'crypto';
6
8
  import { readFile, writeFile, mkdir } from 'fs/promises';
7
9
  import { join } from 'path';
8
10
  import { TIER_LIMITS } from '../../types/user.js';
11
+ import { db } from './database.js';
12
+ import { Logger } from '../../utils/logger.js';
9
13
  const DATA_DIR = '.archicore';
10
14
  const USERS_FILE = 'users.json';
11
15
  const SESSIONS_FILE = 'sessions.json';
12
16
  export class AuthService {
13
17
  static instance = null;
14
18
  dataDir;
19
+ // JSON fallback storage
15
20
  users = [];
16
21
  sessions = [];
17
- initialized = false;
22
+ jsonInitialized = false;
18
23
  constructor(dataDir = DATA_DIR) {
19
24
  this.dataDir = dataDir;
20
25
  }
21
- /**
22
- * Get singleton instance of AuthService
23
- */
24
26
  static getInstance() {
25
27
  if (!AuthService.instance) {
26
28
  AuthService.instance = new AuthService();
27
29
  }
28
30
  return AuthService.instance;
29
31
  }
30
- async ensureInitialized() {
31
- if (this.initialized)
32
+ useDatabase() {
33
+ return db.isAvailable();
34
+ }
35
+ // ========== JSON FALLBACK METHODS ==========
36
+ async ensureJsonInitialized() {
37
+ if (this.jsonInitialized)
32
38
  return;
33
39
  try {
34
40
  await mkdir(this.dataDir, { recursive: true });
35
41
  }
36
42
  catch { }
37
- // Load users
38
43
  try {
39
44
  const usersPath = join(this.dataDir, USERS_FILE);
40
45
  const data = await readFile(usersPath, 'utf-8');
@@ -43,10 +48,8 @@ export class AuthService {
43
48
  }
44
49
  catch {
45
50
  this.users = [];
46
- // Create default admin user
47
51
  await this.createDefaultAdmin();
48
52
  }
49
- // Load sessions
50
53
  try {
51
54
  const sessionsPath = join(this.dataDir, SESSIONS_FILE);
52
55
  const data = await readFile(sessionsPath, 'utf-8');
@@ -56,7 +59,7 @@ export class AuthService {
56
59
  catch {
57
60
  this.sessions = [];
58
61
  }
59
- this.initialized = true;
62
+ this.jsonInitialized = true;
60
63
  }
61
64
  async createDefaultAdmin() {
62
65
  const admin = {
@@ -81,6 +84,7 @@ export class AuthService {
81
84
  const sessionsPath = join(this.dataDir, SESSIONS_FILE);
82
85
  await writeFile(sessionsPath, JSON.stringify({ sessions: this.sessions }, null, 2));
83
86
  }
87
+ // ========== HELPER METHODS ==========
84
88
  hashPassword(password) {
85
89
  return createHash('sha256').update(password + 'archicore-salt-2024').digest('hex');
86
90
  }
@@ -99,13 +103,72 @@ export class AuthService {
99
103
  const { passwordHash, ...sanitized } = user;
100
104
  return sanitized;
101
105
  }
106
+ rowToUser(row) {
107
+ return {
108
+ id: row.id,
109
+ email: row.email,
110
+ username: row.username,
111
+ passwordHash: row.password_hash || undefined,
112
+ avatar: row.avatar || undefined,
113
+ tier: row.tier,
114
+ provider: row.provider,
115
+ providerId: row.provider_id || undefined,
116
+ createdAt: row.created_at,
117
+ lastLoginAt: row.last_login_at,
118
+ usage: {
119
+ requestsToday: row.requests_today,
120
+ fullAnalysisToday: row.full_analysis_today,
121
+ projectsToday: row.projects_today,
122
+ lastResetDate: row.usage_reset_date
123
+ }
124
+ };
125
+ }
126
+ // ========== DATABASE METHODS ==========
127
+ async dbCreateDefaultAdmin() {
128
+ const result = await db.query('SELECT id FROM users WHERE tier = $1 LIMIT 1', ['admin']);
129
+ if (result.rows.length > 0)
130
+ return;
131
+ const adminId = 'admin-' + randomUUID();
132
+ await db.query(`INSERT INTO users (id, email, username, password_hash, tier, provider)
133
+ VALUES ($1, $2, $3, $4, $5, $6)`, [adminId, 'admin@archicore.io', 'Administrator', this.hashPassword('admin123'), 'admin', 'email']);
134
+ Logger.info('Default admin user created');
135
+ }
136
+ // ========== PUBLIC API ==========
102
137
  async register(email, username, password) {
103
- await this.ensureInitialized();
104
- // Check if email already exists
138
+ if (this.useDatabase()) {
139
+ try {
140
+ // Check email exists
141
+ const emailCheck = await db.query('SELECT id FROM users WHERE LOWER(email) = LOWER($1)', [email]);
142
+ if (emailCheck.rows.length > 0) {
143
+ return { success: false, error: 'Email already registered' };
144
+ }
145
+ // Check username exists
146
+ const usernameCheck = await db.query('SELECT id FROM users WHERE LOWER(username) = LOWER($1)', [username]);
147
+ if (usernameCheck.rows.length > 0) {
148
+ return { success: false, error: 'Username already taken' };
149
+ }
150
+ const userId = 'user-' + randomUUID();
151
+ const token = this.generateToken();
152
+ await db.transaction(async (client) => {
153
+ await client.query(`INSERT INTO users (id, email, username, password_hash, tier, provider)
154
+ VALUES ($1, $2, $3, $4, $5, $6)`, [userId, email.toLowerCase(), username, this.hashPassword(password), 'free', 'email']);
155
+ await client.query(`INSERT INTO sessions (token, user_id, expires_at)
156
+ VALUES ($1, $2, $3)`, [token, userId, new Date(Date.now() + 7 * 24 * 60 * 60 * 1000)]);
157
+ });
158
+ const userResult = await db.query('SELECT * FROM users WHERE id = $1', [userId]);
159
+ const user = this.rowToUser(userResult.rows[0]);
160
+ return { success: true, token, user: this.sanitizeUser(user) };
161
+ }
162
+ catch (error) {
163
+ Logger.error('Register error:', error);
164
+ return { success: false, error: 'Registration failed' };
165
+ }
166
+ }
167
+ // JSON fallback
168
+ await this.ensureJsonInitialized();
105
169
  if (this.users.find(u => u.email.toLowerCase() === email.toLowerCase())) {
106
170
  return { success: false, error: 'Email already registered' };
107
171
  }
108
- // Check if username already exists
109
172
  if (this.users.find(u => u.username.toLowerCase() === username.toLowerCase())) {
110
173
  return { success: false, error: 'Username already taken' };
111
174
  }
@@ -122,23 +185,40 @@ export class AuthService {
122
185
  };
123
186
  this.users.push(user);
124
187
  await this.saveUsers();
125
- // Create session
126
188
  const token = this.generateToken();
127
189
  const session = {
128
190
  token,
129
191
  userId: user.id,
130
- expiresAt: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000) // 7 days
192
+ expiresAt: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000)
131
193
  };
132
194
  this.sessions.push(session);
133
195
  await this.saveSessions();
134
- return {
135
- success: true,
136
- token,
137
- user: this.sanitizeUser(user)
138
- };
196
+ return { success: true, token, user: this.sanitizeUser(user) };
139
197
  }
140
198
  async login(email, password) {
141
- await this.ensureInitialized();
199
+ if (this.useDatabase()) {
200
+ try {
201
+ const result = await db.query('SELECT * FROM users WHERE LOWER(email) = LOWER($1)', [email]);
202
+ if (result.rows.length === 0) {
203
+ return { success: false, error: 'Invalid email or password' };
204
+ }
205
+ const row = result.rows[0];
206
+ if (row.password_hash !== this.hashPassword(password)) {
207
+ return { success: false, error: 'Invalid email or password' };
208
+ }
209
+ const token = this.generateToken();
210
+ await db.query('UPDATE users SET last_login_at = NOW() WHERE id = $1', [row.id]);
211
+ await db.query('INSERT INTO sessions (token, user_id, expires_at) VALUES ($1, $2, $3)', [token, row.id, new Date(Date.now() + 7 * 24 * 60 * 60 * 1000)]);
212
+ const user = this.rowToUser(row);
213
+ return { success: true, token, user: this.sanitizeUser(user) };
214
+ }
215
+ catch (error) {
216
+ Logger.error('Login error:', error);
217
+ return { success: false, error: 'Login failed' };
218
+ }
219
+ }
220
+ // JSON fallback
221
+ await this.ensureJsonInitialized();
142
222
  const user = this.users.find(u => u.email.toLowerCase() === email.toLowerCase());
143
223
  if (!user) {
144
224
  return { success: false, error: 'Invalid email or password' };
@@ -146,10 +226,8 @@ export class AuthService {
146
226
  if (user.passwordHash !== this.hashPassword(password)) {
147
227
  return { success: false, error: 'Invalid email or password' };
148
228
  }
149
- // Update last login
150
229
  user.lastLoginAt = new Date();
151
230
  await this.saveUsers();
152
- // Create session
153
231
  const token = this.generateToken();
154
232
  const session = {
155
233
  token,
@@ -158,19 +236,44 @@ export class AuthService {
158
236
  };
159
237
  this.sessions.push(session);
160
238
  await this.saveSessions();
161
- return {
162
- success: true,
163
- token,
164
- user: this.sanitizeUser(user)
165
- };
239
+ return { success: true, token, user: this.sanitizeUser(user) };
166
240
  }
167
241
  async oauthLogin(provider, profile) {
168
- await this.ensureInitialized();
169
- // Find existing user by provider ID or email
242
+ if (this.useDatabase()) {
243
+ try {
244
+ // Find by provider ID or email
245
+ let result = await db.query(`SELECT * FROM users WHERE (provider = $1 AND provider_id = $2) OR LOWER(email) = LOWER($3) LIMIT 1`, [provider, profile.id, profile.email]);
246
+ let userId;
247
+ if (result.rows.length === 0) {
248
+ // Create new user
249
+ userId = 'user-' + randomUUID();
250
+ await db.query(`INSERT INTO users (id, email, username, avatar, tier, provider, provider_id)
251
+ VALUES ($1, $2, $3, $4, $5, $6, $7)`, [userId, profile.email.toLowerCase(), profile.name || profile.email.split('@')[0], profile.avatar, 'free', provider, profile.id]);
252
+ }
253
+ else {
254
+ // Update existing
255
+ userId = result.rows[0].id;
256
+ await db.query(`UPDATE users SET last_login_at = NOW(), avatar = COALESCE($2, avatar),
257
+ provider = CASE WHEN provider_id IS NULL THEN $3 ELSE provider END,
258
+ provider_id = CASE WHEN provider_id IS NULL THEN $4 ELSE provider_id END
259
+ WHERE id = $1`, [userId, profile.avatar, provider, profile.id]);
260
+ }
261
+ const token = this.generateToken();
262
+ await db.query('INSERT INTO sessions (token, user_id, expires_at) VALUES ($1, $2, $3)', [token, userId, new Date(Date.now() + 7 * 24 * 60 * 60 * 1000)]);
263
+ result = await db.query('SELECT * FROM users WHERE id = $1', [userId]);
264
+ const user = this.rowToUser(result.rows[0]);
265
+ return { success: true, token, user: this.sanitizeUser(user) };
266
+ }
267
+ catch (error) {
268
+ Logger.error('OAuth login error:', error);
269
+ return { success: false, error: 'OAuth login failed' };
270
+ }
271
+ }
272
+ // JSON fallback
273
+ await this.ensureJsonInitialized();
170
274
  let user = this.users.find(u => (u.provider === provider && u.providerId === profile.id) ||
171
275
  u.email.toLowerCase() === profile.email.toLowerCase());
172
276
  if (!user) {
173
- // Create new user
174
277
  user = {
175
278
  id: 'user-' + randomUUID(),
176
279
  email: profile.email.toLowerCase(),
@@ -186,7 +289,6 @@ export class AuthService {
186
289
  this.users.push(user);
187
290
  }
188
291
  else {
189
- // Update existing user
190
292
  user.lastLoginAt = new Date();
191
293
  if (profile.avatar)
192
294
  user.avatar = profile.avatar;
@@ -196,7 +298,6 @@ export class AuthService {
196
298
  }
197
299
  }
198
300
  await this.saveUsers();
199
- // Create session
200
301
  const token = this.generateToken();
201
302
  const session = {
202
303
  token,
@@ -205,37 +306,78 @@ export class AuthService {
205
306
  };
206
307
  this.sessions.push(session);
207
308
  await this.saveSessions();
208
- return {
209
- success: true,
210
- token,
211
- user: this.sanitizeUser(user)
212
- };
309
+ return { success: true, token, user: this.sanitizeUser(user) };
213
310
  }
214
311
  async validateToken(token) {
215
- await this.ensureInitialized();
312
+ if (this.useDatabase()) {
313
+ try {
314
+ const result = await db.query('SELECT * FROM sessions WHERE token = $1', [token]);
315
+ if (result.rows.length === 0)
316
+ return null;
317
+ const session = result.rows[0];
318
+ if (new Date(session.expires_at) < new Date()) {
319
+ await db.query('DELETE FROM sessions WHERE token = $1', [token]);
320
+ return null;
321
+ }
322
+ const userResult = await db.query('SELECT * FROM users WHERE id = $1', [session.user_id]);
323
+ if (userResult.rows.length === 0)
324
+ return null;
325
+ return this.rowToUser(userResult.rows[0]);
326
+ }
327
+ catch (error) {
328
+ Logger.error('Token validation error:', error);
329
+ return null;
330
+ }
331
+ }
332
+ // JSON fallback
333
+ await this.ensureJsonInitialized();
216
334
  const session = this.sessions.find(s => s.token === token);
217
335
  if (!session)
218
336
  return null;
219
337
  if (new Date(session.expiresAt) < new Date()) {
220
- // Session expired, remove it
221
338
  this.sessions = this.sessions.filter(s => s.token !== token);
222
339
  await this.saveSessions();
223
340
  return null;
224
341
  }
225
- const user = this.users.find(u => u.id === session.userId);
226
- return user || null;
342
+ return this.users.find(u => u.id === session.userId) || null;
227
343
  }
228
344
  async logout(token) {
229
- await this.ensureInitialized();
345
+ if (this.useDatabase()) {
346
+ await db.query('DELETE FROM sessions WHERE token = $1', [token]);
347
+ return;
348
+ }
349
+ await this.ensureJsonInitialized();
230
350
  this.sessions = this.sessions.filter(s => s.token !== token);
231
351
  await this.saveSessions();
232
352
  }
233
353
  async getUser(userId) {
234
- await this.ensureInitialized();
354
+ if (this.useDatabase()) {
355
+ try {
356
+ const result = await db.query('SELECT * FROM users WHERE id = $1', [userId]);
357
+ if (result.rows.length === 0)
358
+ return null;
359
+ return this.rowToUser(result.rows[0]);
360
+ }
361
+ catch (error) {
362
+ Logger.error('Get user error:', error);
363
+ return null;
364
+ }
365
+ }
366
+ await this.ensureJsonInitialized();
235
367
  return this.users.find(u => u.id === userId) || null;
236
368
  }
237
369
  async updateUserTier(userId, tier) {
238
- await this.ensureInitialized();
370
+ if (this.useDatabase()) {
371
+ try {
372
+ const result = await db.query('UPDATE users SET tier = $1 WHERE id = $2', [tier, userId]);
373
+ return (result.rowCount ?? 0) > 0;
374
+ }
375
+ catch (error) {
376
+ Logger.error('Update tier error:', error);
377
+ return false;
378
+ }
379
+ }
380
+ await this.ensureJsonInitialized();
239
381
  const user = this.users.find(u => u.id === userId);
240
382
  if (!user)
241
383
  return false;
@@ -244,12 +386,59 @@ export class AuthService {
244
386
  return true;
245
387
  }
246
388
  async checkAndUpdateUsage(userId, type) {
247
- await this.ensureInitialized();
389
+ if (this.useDatabase()) {
390
+ try {
391
+ const result = await db.query('SELECT * FROM users WHERE id = $1', [userId]);
392
+ if (result.rows.length === 0)
393
+ return { allowed: false, remaining: 0, limit: 0 };
394
+ const row = result.rows[0];
395
+ const today = new Date().toISOString().split('T')[0];
396
+ // Reset usage if new day
397
+ if (row.usage_reset_date !== today) {
398
+ await db.query(`UPDATE users SET requests_today = 0, full_analysis_today = 0, projects_today = 0, usage_reset_date = $1 WHERE id = $2`, [today, userId]);
399
+ row.requests_today = 0;
400
+ row.full_analysis_today = 0;
401
+ row.projects_today = 0;
402
+ }
403
+ const limits = TIER_LIMITS[row.tier];
404
+ let current;
405
+ let limit;
406
+ let column;
407
+ switch (type) {
408
+ case 'request':
409
+ current = row.requests_today;
410
+ limit = limits.requestsPerDay;
411
+ column = 'requests_today';
412
+ break;
413
+ case 'analysis':
414
+ current = row.full_analysis_today;
415
+ limit = limits.fullAnalysisPerDay;
416
+ column = 'full_analysis_today';
417
+ break;
418
+ case 'project':
419
+ current = row.projects_today;
420
+ limit = limits.projectsPerDay;
421
+ column = 'projects_today';
422
+ break;
423
+ }
424
+ if (current >= limit && limit !== Infinity) {
425
+ return { allowed: false, remaining: 0, limit };
426
+ }
427
+ await db.query(`UPDATE users SET ${column} = ${column} + 1 WHERE id = $1`, [userId]);
428
+ const remaining = limit === Infinity ? Infinity : limit - current - 1;
429
+ return { allowed: true, remaining, limit };
430
+ }
431
+ catch (error) {
432
+ Logger.error('Usage check error:', error);
433
+ return { allowed: false, remaining: 0, limit: 0 };
434
+ }
435
+ }
436
+ // JSON fallback
437
+ await this.ensureJsonInitialized();
248
438
  const user = this.users.find(u => u.id === userId);
249
439
  if (!user)
250
440
  return { allowed: false, remaining: 0, limit: 0 };
251
441
  const today = new Date().toISOString().split('T')[0];
252
- // Reset usage if new day
253
442
  if (user.usage.lastResetDate !== today) {
254
443
  user.usage = this.createEmptyUsage();
255
444
  }
@@ -273,7 +462,6 @@ export class AuthService {
273
462
  if (current >= limit && limit !== Infinity) {
274
463
  return { allowed: false, remaining: 0, limit };
275
464
  }
276
- // Increment usage
277
465
  switch (type) {
278
466
  case 'request':
279
467
  user.usage.requestsToday++;
@@ -290,15 +478,38 @@ export class AuthService {
290
478
  return { allowed: true, remaining, limit };
291
479
  }
292
480
  async getAllUsers() {
293
- await this.ensureInitialized();
481
+ if (this.useDatabase()) {
482
+ try {
483
+ const result = await db.query('SELECT * FROM users ORDER BY created_at DESC');
484
+ return result.rows.map(row => this.sanitizeUser(this.rowToUser(row)));
485
+ }
486
+ catch (error) {
487
+ Logger.error('Get all users error:', error);
488
+ return [];
489
+ }
490
+ }
491
+ await this.ensureJsonInitialized();
294
492
  return this.users.map(u => this.sanitizeUser(u));
295
493
  }
296
494
  async deleteUser(userId) {
297
- await this.ensureInitialized();
495
+ if (this.useDatabase()) {
496
+ try {
497
+ // Don't delete admin
498
+ const check = await db.query('SELECT tier FROM users WHERE id = $1', [userId]);
499
+ if (check.rows.length === 0 || check.rows[0].tier === 'admin')
500
+ return false;
501
+ await db.query('DELETE FROM users WHERE id = $1', [userId]);
502
+ return true;
503
+ }
504
+ catch (error) {
505
+ Logger.error('Delete user error:', error);
506
+ return false;
507
+ }
508
+ }
509
+ await this.ensureJsonInitialized();
298
510
  const index = this.users.findIndex(u => u.id === userId);
299
511
  if (index === -1)
300
512
  return false;
301
- // Don't delete admin
302
513
  if (this.users[index].tier === 'admin')
303
514
  return false;
304
515
  this.users.splice(index, 1);
@@ -308,7 +519,35 @@ export class AuthService {
308
519
  return true;
309
520
  }
310
521
  async updateUser(userId, updates) {
311
- await this.ensureInitialized();
522
+ if (this.useDatabase()) {
523
+ try {
524
+ const sets = [];
525
+ const values = [];
526
+ let paramIndex = 1;
527
+ if (updates.username) {
528
+ sets.push(`username = $${paramIndex++}`);
529
+ values.push(updates.username);
530
+ }
531
+ if (updates.email) {
532
+ sets.push(`email = $${paramIndex++}`);
533
+ values.push(updates.email);
534
+ }
535
+ if (updates.avatar) {
536
+ sets.push(`avatar = $${paramIndex++}`);
537
+ values.push(updates.avatar);
538
+ }
539
+ if (sets.length === 0)
540
+ return true;
541
+ values.push(userId);
542
+ const result = await db.query(`UPDATE users SET ${sets.join(', ')} WHERE id = $${paramIndex}`, values);
543
+ return (result.rowCount ?? 0) > 0;
544
+ }
545
+ catch (error) {
546
+ Logger.error('Update user error:', error);
547
+ return false;
548
+ }
549
+ }
550
+ await this.ensureJsonInitialized();
312
551
  const user = this.users.find(u => u.id === userId);
313
552
  if (!user)
314
553
  return false;
@@ -321,5 +560,13 @@ export class AuthService {
321
560
  await this.saveUsers();
322
561
  return true;
323
562
  }
563
+ /**
564
+ * Initialize database and create default admin if needed
565
+ */
566
+ async initDatabase() {
567
+ if (this.useDatabase()) {
568
+ await this.dbCreateDefaultAdmin();
569
+ }
570
+ }
324
571
  }
325
572
  //# sourceMappingURL=auth-service.js.map
@@ -0,0 +1,77 @@
1
+ /**
2
+ * ArchiCore Redis Cache Service
3
+ *
4
+ * Provides caching for API responses to improve performance
5
+ */
6
+ export interface CacheOptions {
7
+ ttl?: number;
8
+ prefix?: string;
9
+ }
10
+ declare class CacheService {
11
+ private client;
12
+ private enabled;
13
+ private memoryCache;
14
+ connect(): Promise<void>;
15
+ private getKey;
16
+ /**
17
+ * Get value from cache
18
+ */
19
+ get<T>(key: string, prefix?: string): Promise<T | null>;
20
+ /**
21
+ * Set value in cache
22
+ */
23
+ set(key: string, value: any, options?: CacheOptions): Promise<void>;
24
+ /**
25
+ * Delete value from cache
26
+ */
27
+ del(key: string, prefix?: string): Promise<void>;
28
+ /**
29
+ * Delete all keys matching pattern
30
+ */
31
+ delPattern(pattern: string): Promise<void>;
32
+ /**
33
+ * Clear all cache
34
+ */
35
+ flush(): Promise<void>;
36
+ /**
37
+ * Get or set cache with callback
38
+ */
39
+ getOrSet<T>(key: string, callback: () => Promise<T>, options?: CacheOptions): Promise<T>;
40
+ /**
41
+ * Check if cache is available
42
+ */
43
+ isEnabled(): boolean;
44
+ /**
45
+ * Get cache stats
46
+ */
47
+ getStats(): Promise<{
48
+ enabled: boolean;
49
+ type: string;
50
+ keys: number;
51
+ }>;
52
+ /**
53
+ * Cleanup expired memory cache entries
54
+ */
55
+ private cleanupMemoryCache;
56
+ /**
57
+ * Disconnect from Redis
58
+ */
59
+ disconnect(): Promise<void>;
60
+ }
61
+ export declare const cache: CacheService;
62
+ export declare const cacheKeys: {
63
+ project: (projectId: string) => string;
64
+ projectAnalysis: (projectId: string) => string;
65
+ projectMetrics: (projectId: string) => string;
66
+ projectSearch: (projectId: string, query: string) => string;
67
+ user: (userId: string) => string;
68
+ userProjects: (userId: string) => string;
69
+ };
70
+ export declare const cacheTTL: {
71
+ short: number;
72
+ medium: number;
73
+ long: number;
74
+ day: number;
75
+ };
76
+ export {};
77
+ //# sourceMappingURL=cache.d.ts.map