@shin1ohno/sage 0.7.9 → 0.8.6
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/README.md +26 -8
- package/dist/cli/mcp-handler.d.ts.map +1 -1
- package/dist/cli/mcp-handler.js +141 -987
- package/dist/cli/mcp-handler.js.map +1 -1
- package/dist/config/loader.d.ts.map +1 -1
- package/dist/config/loader.js +30 -1
- package/dist/config/loader.js.map +1 -1
- package/dist/config/update-validation.d.ts +52 -0
- package/dist/config/update-validation.d.ts.map +1 -0
- package/dist/config/update-validation.js +133 -0
- package/dist/config/update-validation.js.map +1 -0
- package/dist/config/validation.d.ts +130 -0
- package/dist/config/validation.d.ts.map +1 -0
- package/dist/config/validation.js +53 -0
- package/dist/config/validation.js.map +1 -0
- package/dist/index.js +443 -1632
- package/dist/index.js.map +1 -1
- package/dist/integrations/calendar-event-creator.d.ts +2 -3
- package/dist/integrations/calendar-event-creator.d.ts.map +1 -1
- package/dist/integrations/calendar-event-creator.js +3 -4
- package/dist/integrations/calendar-event-creator.js.map +1 -1
- package/dist/integrations/calendar-event-deleter.d.ts +2 -3
- package/dist/integrations/calendar-event-deleter.d.ts.map +1 -1
- package/dist/integrations/calendar-event-deleter.js +3 -4
- package/dist/integrations/calendar-event-deleter.js.map +1 -1
- package/dist/integrations/calendar-event-response.d.ts +4 -17
- package/dist/integrations/calendar-event-response.d.ts.map +1 -1
- package/dist/integrations/calendar-event-response.js +3 -4
- package/dist/integrations/calendar-event-response.js.map +1 -1
- package/dist/integrations/calendar-service.d.ts +6 -3
- package/dist/integrations/calendar-service.d.ts.map +1 -1
- package/dist/integrations/calendar-service.js +26 -4
- package/dist/integrations/calendar-service.js.map +1 -1
- package/dist/integrations/calendar-source-manager.d.ts +302 -0
- package/dist/integrations/calendar-source-manager.d.ts.map +1 -0
- package/dist/integrations/calendar-source-manager.js +862 -0
- package/dist/integrations/calendar-source-manager.js.map +1 -0
- package/dist/integrations/google-calendar-service.d.ts +176 -0
- package/dist/integrations/google-calendar-service.d.ts.map +1 -0
- package/dist/integrations/google-calendar-service.js +745 -0
- package/dist/integrations/google-calendar-service.js.map +1 -0
- package/dist/integrations/notion-mcp.d.ts +28 -3
- package/dist/integrations/notion-mcp.d.ts.map +1 -1
- package/dist/integrations/notion-mcp.js +21 -5
- package/dist/integrations/notion-mcp.js.map +1 -1
- package/dist/integrations/reminder-manager.d.ts.map +1 -1
- package/dist/integrations/reminder-manager.js +2 -0
- package/dist/integrations/reminder-manager.js.map +1 -1
- package/dist/oauth/google-oauth-handler.d.ts +149 -0
- package/dist/oauth/google-oauth-handler.d.ts.map +1 -0
- package/dist/oauth/google-oauth-handler.js +365 -0
- package/dist/oauth/google-oauth-handler.js.map +1 -0
- package/dist/services/container.d.ts +56 -0
- package/dist/services/container.d.ts.map +1 -0
- package/dist/services/container.js +76 -0
- package/dist/services/container.js.map +1 -0
- package/dist/tools/calendar/handlers.d.ts +186 -0
- package/dist/tools/calendar/handlers.d.ts.map +1 -0
- package/dist/tools/calendar/handlers.js +525 -0
- package/dist/tools/calendar/handlers.js.map +1 -0
- package/dist/tools/calendar/index.d.ts +11 -0
- package/dist/tools/calendar/index.d.ts.map +1 -0
- package/dist/tools/calendar/index.js +10 -0
- package/dist/tools/calendar/index.js.map +1 -0
- package/dist/tools/index.d.ts +23 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +24 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/integrations/handlers.d.ts +57 -0
- package/dist/tools/integrations/handlers.d.ts.map +1 -0
- package/dist/tools/integrations/handlers.js +159 -0
- package/dist/tools/integrations/handlers.js.map +1 -0
- package/dist/tools/integrations/index.d.ts +11 -0
- package/dist/tools/integrations/index.d.ts.map +1 -0
- package/dist/tools/integrations/index.js +10 -0
- package/dist/tools/integrations/index.js.map +1 -0
- package/dist/tools/registry.d.ts +8 -0
- package/dist/tools/registry.d.ts.map +1 -0
- package/dist/tools/registry.js +10 -0
- package/dist/tools/registry.js.map +1 -0
- package/dist/tools/reminders/handlers.d.ts +61 -0
- package/dist/tools/reminders/handlers.d.ts.map +1 -0
- package/dist/tools/reminders/handlers.js +148 -0
- package/dist/tools/reminders/handlers.js.map +1 -0
- package/dist/tools/reminders/index.d.ts +11 -0
- package/dist/tools/reminders/index.d.ts.map +1 -0
- package/dist/tools/reminders/index.js +10 -0
- package/dist/tools/reminders/index.js.map +1 -0
- package/dist/tools/setup/handlers.d.ts +81 -0
- package/dist/tools/setup/handlers.d.ts.map +1 -0
- package/dist/tools/setup/handlers.js +172 -0
- package/dist/tools/setup/handlers.js.map +1 -0
- package/dist/tools/setup/index.d.ts +11 -0
- package/dist/tools/setup/index.d.ts.map +1 -0
- package/dist/tools/setup/index.js +10 -0
- package/dist/tools/setup/index.js.map +1 -0
- package/dist/tools/tasks/handlers.d.ts +95 -0
- package/dist/tools/tasks/handlers.d.ts.map +1 -0
- package/dist/tools/tasks/handlers.js +197 -0
- package/dist/tools/tasks/handlers.js.map +1 -0
- package/dist/tools/tasks/index.d.ts +11 -0
- package/dist/tools/tasks/index.d.ts.map +1 -0
- package/dist/tools/tasks/index.js +10 -0
- package/dist/tools/tasks/index.js.map +1 -0
- package/dist/tools/types.d.ts +54 -0
- package/dist/tools/types.d.ts.map +1 -0
- package/dist/tools/types.js +9 -0
- package/dist/tools/types.js.map +1 -0
- package/dist/types/calendar.d.ts +41 -0
- package/dist/types/calendar.d.ts.map +1 -0
- package/dist/types/calendar.js +18 -0
- package/dist/types/calendar.js.map +1 -0
- package/dist/types/config.d.ts +15 -0
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/config.js +21 -0
- package/dist/types/config.js.map +1 -1
- package/dist/types/google-calendar-types.d.ts +139 -0
- package/dist/types/google-calendar-types.d.ts.map +1 -0
- package/dist/types/google-calendar-types.js +46 -0
- package/dist/types/google-calendar-types.js.map +1 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +1 -0
- package/dist/types/index.js.map +1 -1
- package/dist/utils/estimation.d.ts +34 -0
- package/dist/utils/estimation.d.ts.map +1 -1
- package/dist/utils/estimation.js +38 -1
- package/dist/utils/estimation.js.map +1 -1
- package/dist/utils/mcp-response.d.ts +89 -0
- package/dist/utils/mcp-response.d.ts.map +1 -0
- package/dist/utils/mcp-response.js +103 -0
- package/dist/utils/mcp-response.js.map +1 -0
- package/dist/utils/task-splitter.d.ts +65 -4
- package/dist/utils/task-splitter.d.ts.map +1 -1
- package/dist/utils/task-splitter.js +69 -5
- package/dist/utils/task-splitter.js.map +1 -1
- package/dist/version.d.ts +2 -2
- package/dist/version.d.ts.map +1 -1
- package/dist/version.js +22 -4
- package/dist/version.js.map +1 -1
- package/package.json +4 -3
- package/dist/cli/http-server.d.ts +0 -74
- package/dist/cli/http-server.d.ts.map +0 -1
- package/dist/cli/http-server.js +0 -407
- package/dist/cli/http-server.js.map +0 -1
- package/dist/remote/remote-mcp-server.d.ts +0 -244
- package/dist/remote/remote-mcp-server.d.ts.map +0 -1
- package/dist/remote/remote-mcp-server.js +0 -507
- package/dist/remote/remote-mcp-server.js.map +0 -1
|
@@ -0,0 +1,365 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Google OAuth Handler
|
|
3
|
+
* Requirements: 1 (Google Calendar OAuth Authentication)
|
|
4
|
+
*
|
|
5
|
+
* Handles OAuth 2.0 flow for Google Calendar API integration.
|
|
6
|
+
* Uses PKCE (Proof Key for Code Exchange) with S256 method.
|
|
7
|
+
*/
|
|
8
|
+
import { google } from 'googleapis';
|
|
9
|
+
import { generateCodeVerifier, generateCodeChallenge } from './pkce.js';
|
|
10
|
+
import { createCipheriv, createDecipheriv, randomBytes, scrypt } from 'crypto';
|
|
11
|
+
import { promisify } from 'util';
|
|
12
|
+
import { readFile, writeFile, mkdir } from 'fs/promises';
|
|
13
|
+
import { join } from 'path';
|
|
14
|
+
import { homedir } from 'os';
|
|
15
|
+
const scryptAsync = promisify(scrypt);
|
|
16
|
+
/**
|
|
17
|
+
* Google Calendar API Scopes
|
|
18
|
+
*/
|
|
19
|
+
export const GOOGLE_CALENDAR_SCOPES = [
|
|
20
|
+
'https://www.googleapis.com/auth/calendar',
|
|
21
|
+
'https://www.googleapis.com/auth/calendar.readonly',
|
|
22
|
+
];
|
|
23
|
+
/**
|
|
24
|
+
* Google OAuth Handler Class
|
|
25
|
+
*
|
|
26
|
+
* Manages OAuth 2.0 authentication flow with Google Calendar API.
|
|
27
|
+
* Implements PKCE for enhanced security.
|
|
28
|
+
*/
|
|
29
|
+
export class GoogleOAuthHandler {
|
|
30
|
+
codeVerifier = null;
|
|
31
|
+
config;
|
|
32
|
+
encryptionKey;
|
|
33
|
+
tokensStoragePath;
|
|
34
|
+
constructor(config, encryptionKey, userId) {
|
|
35
|
+
this.config = config;
|
|
36
|
+
// Use provided encryption key or generate from environment
|
|
37
|
+
this.encryptionKey = encryptionKey || process.env.SAGE_ENCRYPTION_KEY || 'sage-default-encryption-key-change-me';
|
|
38
|
+
// Store tokens at ~/.sage/google_oauth_tokens_{userId}.enc
|
|
39
|
+
const sageDir = join(homedir(), '.sage');
|
|
40
|
+
const userIdSuffix = userId ? `_${userId}` : '';
|
|
41
|
+
this.tokensStoragePath = join(sageDir, `google_oauth_tokens${userIdSuffix}.enc`);
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Create OAuth2Client instance
|
|
45
|
+
*/
|
|
46
|
+
createOAuth2Client(redirectUri) {
|
|
47
|
+
return new google.auth.OAuth2(this.config.clientId, this.config.clientSecret, redirectUri || this.config.redirectUri);
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Encrypt data using AES-256-GCM
|
|
51
|
+
*/
|
|
52
|
+
async encrypt(data) {
|
|
53
|
+
// Derive key from encryption key using scrypt
|
|
54
|
+
const salt = randomBytes(16);
|
|
55
|
+
const key = (await scryptAsync(this.encryptionKey, salt, 32));
|
|
56
|
+
// Generate IV
|
|
57
|
+
const iv = randomBytes(16);
|
|
58
|
+
// Create cipher
|
|
59
|
+
const cipher = createCipheriv('aes-256-gcm', key, iv);
|
|
60
|
+
// Encrypt data
|
|
61
|
+
let encrypted = cipher.update(data, 'utf8', 'hex');
|
|
62
|
+
encrypted += cipher.final('hex');
|
|
63
|
+
// Get auth tag
|
|
64
|
+
const authTag = cipher.getAuthTag();
|
|
65
|
+
// Combine: salt:iv:authTag:encrypted
|
|
66
|
+
return `${salt.toString('hex')}:${iv.toString('hex')}:${authTag.toString('hex')}:${encrypted}`;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Decrypt data using AES-256-GCM
|
|
70
|
+
*/
|
|
71
|
+
async decrypt(encryptedData) {
|
|
72
|
+
// Split encrypted data
|
|
73
|
+
const parts = encryptedData.split(':');
|
|
74
|
+
if (parts.length !== 4) {
|
|
75
|
+
throw new Error('Invalid encrypted data format');
|
|
76
|
+
}
|
|
77
|
+
const [saltHex, ivHex, authTagHex, encrypted] = parts;
|
|
78
|
+
// Convert from hex
|
|
79
|
+
const salt = Buffer.from(saltHex, 'hex');
|
|
80
|
+
const iv = Buffer.from(ivHex, 'hex');
|
|
81
|
+
const authTag = Buffer.from(authTagHex, 'hex');
|
|
82
|
+
// Derive key from encryption key using scrypt
|
|
83
|
+
const key = (await scryptAsync(this.encryptionKey, salt, 32));
|
|
84
|
+
// Create decipher
|
|
85
|
+
const decipher = createDecipheriv('aes-256-gcm', key, iv);
|
|
86
|
+
decipher.setAuthTag(authTag);
|
|
87
|
+
// Decrypt data
|
|
88
|
+
let decrypted = decipher.update(encrypted, 'hex', 'utf8');
|
|
89
|
+
decrypted += decipher.final('utf8');
|
|
90
|
+
return decrypted;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Generate authorization URL with PKCE code_challenge
|
|
94
|
+
*
|
|
95
|
+
* Generates a code_verifier and code_challenge (S256), stores the verifier
|
|
96
|
+
* for later token exchange, and returns the authorization URL.
|
|
97
|
+
*
|
|
98
|
+
* @param redirectUri - Redirect URI for OAuth callback
|
|
99
|
+
* @returns Authorization URL for user to visit
|
|
100
|
+
*/
|
|
101
|
+
async getAuthorizationUrl(redirectUri) {
|
|
102
|
+
// Generate PKCE parameters
|
|
103
|
+
this.codeVerifier = generateCodeVerifier();
|
|
104
|
+
const codeChallenge = generateCodeChallenge(this.codeVerifier);
|
|
105
|
+
// Create OAuth2 client with redirect URI
|
|
106
|
+
const oauth2Client = this.createOAuth2Client(redirectUri);
|
|
107
|
+
// Generate authorization URL with PKCE
|
|
108
|
+
const authUrl = oauth2Client.generateAuthUrl({
|
|
109
|
+
access_type: 'offline', // Request refresh token
|
|
110
|
+
scope: GOOGLE_CALENDAR_SCOPES,
|
|
111
|
+
code_challenge: codeChallenge,
|
|
112
|
+
code_challenge_method: 'S256',
|
|
113
|
+
prompt: 'consent', // Force consent screen to get refresh token
|
|
114
|
+
});
|
|
115
|
+
return authUrl;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Exchange authorization code for tokens
|
|
119
|
+
*
|
|
120
|
+
* Exchanges the authorization code received from OAuth callback
|
|
121
|
+
* for access and refresh tokens. Uses the stored code_verifier
|
|
122
|
+
* for PKCE verification.
|
|
123
|
+
*
|
|
124
|
+
* @param code - Authorization code from OAuth callback
|
|
125
|
+
* @param redirectUri - Redirect URI (must match authorization request)
|
|
126
|
+
* @returns Google OAuth tokens
|
|
127
|
+
* @throws Error if code_verifier is not found or token exchange fails
|
|
128
|
+
*/
|
|
129
|
+
async exchangeCodeForTokens(code, redirectUri) {
|
|
130
|
+
if (!this.codeVerifier) {
|
|
131
|
+
throw new Error('code_verifier not found. Call getAuthorizationUrl() first.');
|
|
132
|
+
}
|
|
133
|
+
// Create OAuth2 client with redirect URI
|
|
134
|
+
const oauth2Client = this.createOAuth2Client(redirectUri);
|
|
135
|
+
try {
|
|
136
|
+
// Exchange code for tokens with PKCE verifier
|
|
137
|
+
const { tokens } = await oauth2Client.getToken({
|
|
138
|
+
code,
|
|
139
|
+
codeVerifier: this.codeVerifier,
|
|
140
|
+
});
|
|
141
|
+
// Clear stored code_verifier
|
|
142
|
+
this.codeVerifier = null;
|
|
143
|
+
if (!tokens.access_token || !tokens.refresh_token) {
|
|
144
|
+
throw new Error('Failed to retrieve access_token or refresh_token');
|
|
145
|
+
}
|
|
146
|
+
return {
|
|
147
|
+
accessToken: tokens.access_token,
|
|
148
|
+
refreshToken: tokens.refresh_token,
|
|
149
|
+
expiresAt: tokens.expiry_date || Date.now() + 3600 * 1000,
|
|
150
|
+
scope: tokens.scope ? tokens.scope.split(' ') : GOOGLE_CALENDAR_SCOPES,
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
catch (error) {
|
|
154
|
+
// Clear code_verifier on error
|
|
155
|
+
this.codeVerifier = null;
|
|
156
|
+
throw new Error(`Failed to exchange code for tokens: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Refresh access token using refresh token
|
|
161
|
+
*
|
|
162
|
+
* @param refreshToken - Refresh token
|
|
163
|
+
* @returns Updated Google OAuth tokens
|
|
164
|
+
* @throws Error if token refresh fails
|
|
165
|
+
*/
|
|
166
|
+
async refreshAccessToken(refreshToken) {
|
|
167
|
+
const oauth2Client = this.createOAuth2Client();
|
|
168
|
+
try {
|
|
169
|
+
// Set refresh token
|
|
170
|
+
oauth2Client.setCredentials({
|
|
171
|
+
refresh_token: refreshToken,
|
|
172
|
+
});
|
|
173
|
+
// Refresh access token
|
|
174
|
+
const { credentials } = await oauth2Client.refreshAccessToken();
|
|
175
|
+
if (!credentials.access_token) {
|
|
176
|
+
throw new Error('Failed to refresh access_token');
|
|
177
|
+
}
|
|
178
|
+
return {
|
|
179
|
+
accessToken: credentials.access_token,
|
|
180
|
+
refreshToken: credentials.refresh_token || refreshToken,
|
|
181
|
+
expiresAt: credentials.expiry_date || Date.now() + 3600 * 1000,
|
|
182
|
+
scope: credentials.scope ? credentials.scope.split(' ') : GOOGLE_CALENDAR_SCOPES,
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
catch (error) {
|
|
186
|
+
throw new Error(`Failed to refresh access token: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Revoke access token
|
|
191
|
+
*
|
|
192
|
+
* @param accessToken - Access token to revoke
|
|
193
|
+
* @throws Error if token revocation fails
|
|
194
|
+
*/
|
|
195
|
+
async revokeToken(accessToken) {
|
|
196
|
+
const oauth2Client = this.createOAuth2Client();
|
|
197
|
+
try {
|
|
198
|
+
await oauth2Client.revokeToken(accessToken);
|
|
199
|
+
}
|
|
200
|
+
catch (error) {
|
|
201
|
+
throw new Error(`Failed to revoke token: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Store tokens securely with encryption
|
|
206
|
+
*
|
|
207
|
+
* Encrypts tokens using AES-256-GCM and stores them in ~/.sage/google_oauth_tokens_{userId}.enc
|
|
208
|
+
*
|
|
209
|
+
* @param tokens - Google OAuth tokens to store
|
|
210
|
+
* @throws Error if token storage fails
|
|
211
|
+
*/
|
|
212
|
+
async storeTokens(tokens) {
|
|
213
|
+
try {
|
|
214
|
+
// Convert to stored format
|
|
215
|
+
const storedTokens = {
|
|
216
|
+
accessToken: tokens.accessToken,
|
|
217
|
+
refreshToken: tokens.refreshToken,
|
|
218
|
+
expiresAt: new Date(tokens.expiresAt).toISOString(),
|
|
219
|
+
scope: tokens.scope,
|
|
220
|
+
};
|
|
221
|
+
// Encrypt tokens
|
|
222
|
+
const tokensJson = JSON.stringify(storedTokens);
|
|
223
|
+
const encrypted = await this.encrypt(tokensJson);
|
|
224
|
+
// Ensure ~/.sage directory exists
|
|
225
|
+
const sageDir = join(homedir(), '.sage');
|
|
226
|
+
await mkdir(sageDir, { recursive: true });
|
|
227
|
+
// Write encrypted tokens to file
|
|
228
|
+
await writeFile(this.tokensStoragePath, encrypted, 'utf8');
|
|
229
|
+
}
|
|
230
|
+
catch (error) {
|
|
231
|
+
throw new Error(`Failed to store tokens: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Get stored tokens with decryption
|
|
236
|
+
*
|
|
237
|
+
* Reads and decrypts tokens from ~/.sage/google_oauth_tokens_{userId}.enc
|
|
238
|
+
*
|
|
239
|
+
* @returns Google OAuth tokens or null if not found
|
|
240
|
+
* @throws Error if token retrieval or decryption fails
|
|
241
|
+
*/
|
|
242
|
+
async getTokens() {
|
|
243
|
+
try {
|
|
244
|
+
// Read encrypted tokens from file
|
|
245
|
+
const encrypted = await readFile(this.tokensStoragePath, 'utf8');
|
|
246
|
+
// Decrypt tokens
|
|
247
|
+
const tokensJson = await this.decrypt(encrypted);
|
|
248
|
+
const storedTokens = JSON.parse(tokensJson);
|
|
249
|
+
// Convert to GoogleOAuthTokens format
|
|
250
|
+
return {
|
|
251
|
+
accessToken: storedTokens.accessToken,
|
|
252
|
+
refreshToken: storedTokens.refreshToken,
|
|
253
|
+
expiresAt: new Date(storedTokens.expiresAt).getTime(),
|
|
254
|
+
scope: storedTokens.scope,
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
catch (error) {
|
|
258
|
+
// Return null if file not found (user hasn't authenticated yet)
|
|
259
|
+
if (error.code === 'ENOENT') {
|
|
260
|
+
return null;
|
|
261
|
+
}
|
|
262
|
+
throw new Error(`Failed to get tokens: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Revoke tokens and clear local storage
|
|
267
|
+
*
|
|
268
|
+
* Calls Google's token revocation endpoint AND removes locally stored tokens
|
|
269
|
+
*
|
|
270
|
+
* @throws Error if token revocation fails
|
|
271
|
+
*/
|
|
272
|
+
async revokeTokens() {
|
|
273
|
+
try {
|
|
274
|
+
// Get current tokens
|
|
275
|
+
const tokens = await this.getTokens();
|
|
276
|
+
if (tokens) {
|
|
277
|
+
// Revoke token at Google
|
|
278
|
+
await this.revokeToken(tokens.accessToken);
|
|
279
|
+
}
|
|
280
|
+
// Clear local storage (remove file)
|
|
281
|
+
const fs = await import('fs/promises');
|
|
282
|
+
try {
|
|
283
|
+
await fs.unlink(this.tokensStoragePath);
|
|
284
|
+
}
|
|
285
|
+
catch (error) {
|
|
286
|
+
// Ignore if file doesn't exist
|
|
287
|
+
if (error.code !== 'ENOENT') {
|
|
288
|
+
throw error;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
catch (error) {
|
|
293
|
+
throw new Error(`Failed to revoke tokens: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* Validate access token
|
|
298
|
+
*
|
|
299
|
+
* Checks if the access token is valid and not expired (or expiring soon).
|
|
300
|
+
* Token is considered expiring if it expires within 5 minutes.
|
|
301
|
+
*
|
|
302
|
+
* @param tokens - Google OAuth tokens to validate
|
|
303
|
+
* @returns True if token is valid and not expiring soon
|
|
304
|
+
*/
|
|
305
|
+
async validateToken(tokens) {
|
|
306
|
+
// Check if expiresAt timestamp is in the future
|
|
307
|
+
const now = Date.now();
|
|
308
|
+
const fiveMinutesInMs = 5 * 60 * 1000;
|
|
309
|
+
// Token is valid if it expires more than 5 minutes from now
|
|
310
|
+
return tokens.expiresAt > now + fiveMinutesInMs;
|
|
311
|
+
}
|
|
312
|
+
/**
|
|
313
|
+
* Ensure valid token
|
|
314
|
+
*
|
|
315
|
+
* Gets stored tokens, validates expiry, refreshes if needed,
|
|
316
|
+
* stores updated tokens, and returns valid access token.
|
|
317
|
+
* This method should be called before each Google Calendar API call.
|
|
318
|
+
*
|
|
319
|
+
* @returns Valid access token
|
|
320
|
+
* @throws Error if no tokens found or refresh fails
|
|
321
|
+
*/
|
|
322
|
+
async ensureValidToken() {
|
|
323
|
+
// 1. Get stored tokens
|
|
324
|
+
const tokens = await this.getTokens();
|
|
325
|
+
if (!tokens) {
|
|
326
|
+
throw new Error('No stored tokens found. Please authenticate with Google Calendar first.');
|
|
327
|
+
}
|
|
328
|
+
// 2. Validate expiry
|
|
329
|
+
const isValid = await this.validateToken(tokens);
|
|
330
|
+
// 3. Refresh if needed
|
|
331
|
+
if (!isValid) {
|
|
332
|
+
try {
|
|
333
|
+
const refreshedTokens = await this.refreshAccessToken(tokens.refreshToken);
|
|
334
|
+
// 4. Store updated tokens
|
|
335
|
+
await this.storeTokens(refreshedTokens);
|
|
336
|
+
// 5. Return valid access token
|
|
337
|
+
return refreshedTokens.accessToken;
|
|
338
|
+
}
|
|
339
|
+
catch (error) {
|
|
340
|
+
throw new Error(`Failed to refresh expired token: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
// 5. Return valid access token (no refresh needed)
|
|
344
|
+
return tokens.accessToken;
|
|
345
|
+
}
|
|
346
|
+
/**
|
|
347
|
+
* Get OAuth2 client instance
|
|
348
|
+
*
|
|
349
|
+
* Returns the configured OAuth2Client for use in Google API calls.
|
|
350
|
+
*
|
|
351
|
+
* @param tokens - Google OAuth tokens
|
|
352
|
+
* @returns Configured OAuth2Client
|
|
353
|
+
*/
|
|
354
|
+
getOAuth2Client(tokens) {
|
|
355
|
+
const client = new google.auth.OAuth2(this.config.clientId, this.config.clientSecret, this.config.redirectUri);
|
|
356
|
+
client.setCredentials({
|
|
357
|
+
access_token: tokens.accessToken,
|
|
358
|
+
refresh_token: tokens.refreshToken,
|
|
359
|
+
expiry_date: tokens.expiresAt,
|
|
360
|
+
scope: tokens.scope.join(' '),
|
|
361
|
+
});
|
|
362
|
+
return client;
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
//# sourceMappingURL=google-oauth-handler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"google-oauth-handler.js","sourceRoot":"","sources":["../../src/oauth/google-oauth-handler.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAEpC,OAAO,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,MAAM,WAAW,CAAC;AACxE,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC/E,OAAO,EAAE,SAAS,EAAE,MAAM,MAAM,CAAC;AACjC,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AACzD,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAE,OAAO,EAAE,MAAM,IAAI,CAAC;AAE7B,MAAM,WAAW,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC;AA+BtC;;GAEG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG;IACpC,0CAA0C;IAC1C,mDAAmD;CACpD,CAAC;AAEF;;;;;GAKG;AACH,MAAM,OAAO,kBAAkB;IACrB,YAAY,GAAkB,IAAI,CAAC;IACnC,MAAM,CAAoB;IACjB,aAAa,CAAS;IACtB,iBAAiB,CAAS;IAE3C,YAAY,MAAyB,EAAE,aAAsB,EAAE,MAAe;QAC5E,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,2DAA2D;QAC3D,IAAI,CAAC,aAAa,GAAG,aAAa,IAAI,OAAO,CAAC,GAAG,CAAC,mBAAmB,IAAI,uCAAuC,CAAC;QACjH,2DAA2D;QAC3D,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,OAAO,CAAC,CAAC;QACzC,MAAM,YAAY,GAAG,MAAM,CAAC,CAAC,CAAC,IAAI,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAChD,IAAI,CAAC,iBAAiB,GAAG,IAAI,CAAC,OAAO,EAAE,sBAAsB,YAAY,MAAM,CAAC,CAAC;IACnF,CAAC;IAED;;OAEG;IACK,kBAAkB,CAAC,WAAoB;QAC7C,OAAO,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,CAC3B,IAAI,CAAC,MAAM,CAAC,QAAQ,EACpB,IAAI,CAAC,MAAM,CAAC,YAAY,EACxB,WAAW,IAAI,IAAI,CAAC,MAAM,CAAC,WAAW,CACvC,CAAC;IACJ,CAAC;IAED;;OAEG;IACK,KAAK,CAAC,OAAO,CAAC,IAAY;QAChC,8CAA8C;QAC9C,MAAM,IAAI,GAAG,WAAW,CAAC,EAAE,CAAC,CAAC;QAC7B,MAAM,GAAG,GAAG,CAAC,MAAM,WAAW,CAAC,IAAI,CAAC,aAAa,EAAE,IAAI,EAAE,EAAE,CAAC,CAAW,CAAC;QAExE,cAAc;QACd,MAAM,EAAE,GAAG,WAAW,CAAC,EAAE,CAAC,CAAC;QAE3B,gBAAgB;QAChB,MAAM,MAAM,GAAG,cAAc,CAAC,aAAa,EAAE,GAAG,EAAE,EAAE,CAAC,CAAC;QAEtD,eAAe;QACf,IAAI,SAAS,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC;QACnD,SAAS,IAAI,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QAEjC,eAAe;QACf,MAAM,OAAO,GAAG,MAAM,CAAC,UAAU,EAAE,CAAC;QAEpC,qCAAqC;QACrC,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,SAAS,EAAE,CAAC;IACjG,CAAC;IAED;;OAEG;IACK,KAAK,CAAC,OAAO,CAAC,aAAqB;QACzC,uBAAuB;QACvB,MAAM,KAAK,GAAG,aAAa,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACvC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACvB,MAAM,IAAI,KAAK,CAAC,+BAA+B,CAAC,CAAC;QACnD,CAAC;QAED,MAAM,CAAC,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,SAAS,CAAC,GAAG,KAAK,CAAC;QAEtD,mBAAmB;QACnB,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;QACzC,MAAM,EAAE,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;QACrC,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;QAE/C,8CAA8C;QAC9C,MAAM,GAAG,GAAG,CAAC,MAAM,WAAW,CAAC,IAAI,CAAC,aAAa,EAAE,IAAI,EAAE,EAAE,CAAC,CAAW,CAAC;QAExE,kBAAkB;QAClB,MAAM,QAAQ,GAAG,gBAAgB,CAAC,aAAa,EAAE,GAAG,EAAE,EAAE,CAAC,CAAC;QAC1D,QAAQ,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;QAE7B,eAAe;QACf,IAAI,SAAS,GAAG,QAAQ,CAAC,MAAM,CAAC,SAAS,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC;QAC1D,SAAS,IAAI,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QAEpC,OAAO,SAAS,CAAC;IACnB,CAAC;IAED;;;;;;;;OAQG;IACH,KAAK,CAAC,mBAAmB,CAAC,WAAoB;QAC5C,2BAA2B;QAC3B,IAAI,CAAC,YAAY,GAAG,oBAAoB,EAAE,CAAC;QAC3C,MAAM,aAAa,GAAG,qBAAqB,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QAE/D,yCAAyC;QACzC,MAAM,YAAY,GAAG,IAAI,CAAC,kBAAkB,CAAC,WAAW,CAAC,CAAC;QAE1D,uCAAuC;QACvC,MAAM,OAAO,GAAG,YAAY,CAAC,eAAe,CAAC;YAC3C,WAAW,EAAE,SAAS,EAAE,wBAAwB;YAChD,KAAK,EAAE,sBAAsB;YAC7B,cAAc,EAAE,aAAa;YAC7B,qBAAqB,EAAE,MAAM;YAC7B,MAAM,EAAE,SAAS,EAAE,4CAA4C;SACzD,CAAC,CAAC;QAEV,OAAO,OAAO,CAAC;IACjB,CAAC;IAED;;;;;;;;;;;OAWG;IACH,KAAK,CAAC,qBAAqB,CACzB,IAAY,EACZ,WAAoB;QAEpB,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,CAAC;YACvB,MAAM,IAAI,KAAK,CAAC,4DAA4D,CAAC,CAAC;QAChF,CAAC;QAED,yCAAyC;QACzC,MAAM,YAAY,GAAG,IAAI,CAAC,kBAAkB,CAAC,WAAW,CAAC,CAAC;QAE1D,IAAI,CAAC;YACH,8CAA8C;YAC9C,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,YAAY,CAAC,QAAQ,CAAC;gBAC7C,IAAI;gBACJ,YAAY,EAAE,IAAI,CAAC,YAAY;aAChC,CAAC,CAAC;YAEH,6BAA6B;YAC7B,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;YAEzB,IAAI,CAAC,MAAM,CAAC,YAAY,IAAI,CAAC,MAAM,CAAC,aAAa,EAAE,CAAC;gBAClD,MAAM,IAAI,KAAK,CAAC,kDAAkD,CAAC,CAAC;YACtE,CAAC;YAED,OAAO;gBACL,WAAW,EAAE,MAAM,CAAC,YAAY;gBAChC,YAAY,EAAE,MAAM,CAAC,aAAa;gBAClC,SAAS,EAAE,MAAM,CAAC,WAAW,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,GAAG,IAAI;gBACzD,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,sBAAsB;aACvE,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,+BAA+B;YAC/B,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;YACzB,MAAM,IAAI,KAAK,CACb,uCAAuC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,EAAE,CAClG,CAAC;QACJ,CAAC;IACH,CAAC;IAED;;;;;;OAMG;IACH,KAAK,CAAC,kBAAkB,CAAC,YAAoB;QAC3C,MAAM,YAAY,GAAG,IAAI,CAAC,kBAAkB,EAAE,CAAC;QAE/C,IAAI,CAAC;YACH,oBAAoB;YACpB,YAAY,CAAC,cAAc,CAAC;gBAC1B,aAAa,EAAE,YAAY;aAC5B,CAAC,CAAC;YAEH,uBAAuB;YACvB,MAAM,EAAE,WAAW,EAAE,GAAG,MAAM,YAAY,CAAC,kBAAkB,EAAE,CAAC;YAEhE,IAAI,CAAC,WAAW,CAAC,YAAY,EAAE,CAAC;gBAC9B,MAAM,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAC;YACpD,CAAC;YAED,OAAO;gBACL,WAAW,EAAE,WAAW,CAAC,YAAY;gBACrC,YAAY,EAAE,WAAW,CAAC,aAAa,IAAI,YAAY;gBACvD,SAAS,EAAE,WAAW,CAAC,WAAW,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,GAAG,IAAI;gBAC9D,KAAK,EAAE,WAAW,CAAC,KAAK,CAAC,CAAC,CAAC,WAAW,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,sBAAsB;aACjF,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,IAAI,KAAK,CACb,mCAAmC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,EAAE,CAC9F,CAAC;QACJ,CAAC;IACH,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,WAAW,CAAC,WAAmB;QACnC,MAAM,YAAY,GAAG,IAAI,CAAC,kBAAkB,EAAE,CAAC;QAE/C,IAAI,CAAC;YACH,MAAM,YAAY,CAAC,WAAW,CAAC,WAAW,CAAC,CAAC;QAC9C,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,IAAI,KAAK,CACb,2BAA2B,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,EAAE,CACtF,CAAC;QACJ,CAAC;IACH,CAAC;IAED;;;;;;;OAOG;IACH,KAAK,CAAC,WAAW,CAAC,MAAyB;QACzC,IAAI,CAAC;YACH,2BAA2B;YAC3B,MAAM,YAAY,GAAiB;gBACjC,WAAW,EAAE,MAAM,CAAC,WAAW;gBAC/B,YAAY,EAAE,MAAM,CAAC,YAAY;gBACjC,SAAS,EAAE,IAAI,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,WAAW,EAAE;gBACnD,KAAK,EAAE,MAAM,CAAC,KAAK;aACpB,CAAC;YAEF,iBAAiB;YACjB,MAAM,UAAU,GAAG,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC;YAChD,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;YAEjD,kCAAkC;YAClC,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,OAAO,CAAC,CAAC;YACzC,MAAM,KAAK,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YAE1C,iCAAiC;YACjC,MAAM,SAAS,CAAC,IAAI,CAAC,iBAAiB,EAAE,SAAS,EAAE,MAAM,CAAC,CAAC;QAC7D,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,IAAI,KAAK,CACb,2BAA2B,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,EAAE,CACtF,CAAC;QACJ,CAAC;IACH,CAAC;IAED;;;;;;;OAOG;IACH,KAAK,CAAC,SAAS;QACb,IAAI,CAAC;YACH,kCAAkC;YAClC,MAAM,SAAS,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC,iBAAiB,EAAE,MAAM,CAAC,CAAC;YAEjE,iBAAiB;YACjB,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;YACjD,MAAM,YAAY,GAAiB,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;YAE1D,sCAAsC;YACtC,OAAO;gBACL,WAAW,EAAE,YAAY,CAAC,WAAW;gBACrC,YAAY,EAAE,YAAY,CAAC,YAAY;gBACvC,SAAS,EAAE,IAAI,IAAI,CAAC,YAAY,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE;gBACrD,KAAK,EAAE,YAAY,CAAC,KAAK;aAC1B,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,gEAAgE;YAChE,IAAK,KAA+B,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;gBACvD,OAAO,IAAI,CAAC;YACd,CAAC;YACD,MAAM,IAAI,KAAK,CACb,yBAAyB,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,EAAE,CACpF,CAAC;QACJ,CAAC;IACH,CAAC;IAED;;;;;;OAMG;IACH,KAAK,CAAC,YAAY;QAChB,IAAI,CAAC;YACH,qBAAqB;YACrB,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,SAAS,EAAE,CAAC;YAEtC,IAAI,MAAM,EAAE,CAAC;gBACX,yBAAyB;gBACzB,MAAM,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;YAC7C,CAAC;YAED,oCAAoC;YACpC,MAAM,EAAE,GAAG,MAAM,MAAM,CAAC,aAAa,CAAC,CAAC;YACvC,IAAI,CAAC;gBACH,MAAM,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;YAC1C,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,+BAA+B;gBAC/B,IAAK,KAA+B,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;oBACvD,MAAM,KAAK,CAAC;gBACd,CAAC;YACH,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,IAAI,KAAK,CACb,4BAA4B,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,EAAE,CACvF,CAAC;QACJ,CAAC;IACH,CAAC;IAED;;;;;;;;OAQG;IACH,KAAK,CAAC,aAAa,CAAC,MAAyB;QAC3C,gDAAgD;QAChD,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,MAAM,eAAe,GAAG,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC;QAEtC,4DAA4D;QAC5D,OAAO,MAAM,CAAC,SAAS,GAAG,GAAG,GAAG,eAAe,CAAC;IAClD,CAAC;IAED;;;;;;;;;OASG;IACH,KAAK,CAAC,gBAAgB;QACpB,uBAAuB;QACvB,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,SAAS,EAAE,CAAC;QAEtC,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,MAAM,IAAI,KAAK,CAAC,yEAAyE,CAAC,CAAC;QAC7F,CAAC;QAED,qBAAqB;QACrB,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC;QAEjD,uBAAuB;QACvB,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,IAAI,CAAC;gBACH,MAAM,eAAe,GAAG,MAAM,IAAI,CAAC,kBAAkB,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;gBAE3E,0BAA0B;gBAC1B,MAAM,IAAI,CAAC,WAAW,CAAC,eAAe,CAAC,CAAC;gBAExC,+BAA+B;gBAC/B,OAAO,eAAe,CAAC,WAAW,CAAC;YACrC,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,MAAM,IAAI,KAAK,CACb,oCAAoC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,EAAE,CAC/F,CAAC;YACJ,CAAC;QACH,CAAC;QAED,mDAAmD;QACnD,OAAO,MAAM,CAAC,WAAW,CAAC;IAC5B,CAAC;IAED;;;;;;;OAOG;IACH,eAAe,CAAC,MAAyB;QACvC,MAAM,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,CACnC,IAAI,CAAC,MAAM,CAAC,QAAQ,EACpB,IAAI,CAAC,MAAM,CAAC,YAAY,EACxB,IAAI,CAAC,MAAM,CAAC,WAAW,CACxB,CAAC;QAEF,MAAM,CAAC,cAAc,CAAC;YACpB,YAAY,EAAE,MAAM,CAAC,WAAW;YAChC,aAAa,EAAE,MAAM,CAAC,YAAY;YAClC,WAAW,EAAE,MAAM,CAAC,SAAS;YAC7B,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC;SAC9B,CAAC,CAAC;QAEH,OAAO,MAAM,CAAC;IAChB,CAAC;CACF"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Service Container
|
|
3
|
+
*
|
|
4
|
+
* Manages service lifecycle and provides type-safe access to services.
|
|
5
|
+
* Services are lazily initialized on first access after config is loaded.
|
|
6
|
+
*/
|
|
7
|
+
import type { UserConfig } from '../types/index.js';
|
|
8
|
+
/**
|
|
9
|
+
* Service initialization status
|
|
10
|
+
*/
|
|
11
|
+
export type ServiceStatus = 'uninitialized' | 'initializing' | 'ready' | 'error';
|
|
12
|
+
/**
|
|
13
|
+
* Service container state
|
|
14
|
+
*
|
|
15
|
+
* Tracks which services have been initialized and their current status.
|
|
16
|
+
*/
|
|
17
|
+
export interface ServiceContainerState {
|
|
18
|
+
status: ServiceStatus;
|
|
19
|
+
config: UserConfig | null;
|
|
20
|
+
error?: Error;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Creates a service getter that lazily initializes the service
|
|
24
|
+
*
|
|
25
|
+
* @param initializer - Function that creates the service instance
|
|
26
|
+
* @returns Getter function that returns the service or null if not initialized
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* const getCalendarService = createLazyService(() => new CalendarService());
|
|
30
|
+
* // Later, when needed:
|
|
31
|
+
* const service = getCalendarService();
|
|
32
|
+
*/
|
|
33
|
+
export declare function createLazyService<T>(initializer: () => T): () => T | null;
|
|
34
|
+
/**
|
|
35
|
+
* Creates a service getter that requires config
|
|
36
|
+
*
|
|
37
|
+
* @param initializer - Function that creates the service with config
|
|
38
|
+
* @returns Getter function that accepts config and returns the service
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* const getReminderManager = createConfiguredService(
|
|
42
|
+
* (config) => new ReminderManager({ ... })
|
|
43
|
+
* );
|
|
44
|
+
* // Later, when config is available:
|
|
45
|
+
* const manager = getReminderManager(userConfig);
|
|
46
|
+
*/
|
|
47
|
+
export declare function createConfiguredService<T>(initializer: (config: UserConfig) => T): (config: UserConfig) => T | null;
|
|
48
|
+
/**
|
|
49
|
+
* Resets a lazy service to uninitialized state
|
|
50
|
+
*
|
|
51
|
+
* Useful for testing or when config changes require service re-initialization.
|
|
52
|
+
*
|
|
53
|
+
* @param resetter - Function that resets the service state
|
|
54
|
+
*/
|
|
55
|
+
export declare function resetLazyService(resetter: () => void): void;
|
|
56
|
+
//# sourceMappingURL=container.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"container.d.ts","sourceRoot":"","sources":["../../src/services/container.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAEpD;;GAEG;AACH,MAAM,MAAM,aAAa,GAAG,eAAe,GAAG,cAAc,GAAG,OAAO,GAAG,OAAO,CAAC;AAEjF;;;;GAIG;AACH,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,aAAa,CAAC;IACtB,MAAM,EAAE,UAAU,GAAG,IAAI,CAAC;IAC1B,KAAK,CAAC,EAAE,KAAK,CAAC;CACf;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,EACjC,WAAW,EAAE,MAAM,CAAC,GACnB,MAAM,CAAC,GAAG,IAAI,CAgBhB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,uBAAuB,CAAC,CAAC,EACvC,WAAW,EAAE,CAAC,MAAM,EAAE,UAAU,KAAK,CAAC,GACrC,CAAC,MAAM,EAAE,UAAU,KAAK,CAAC,GAAG,IAAI,CAiBlC;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,IAAI,CAE3D"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Service Container
|
|
3
|
+
*
|
|
4
|
+
* Manages service lifecycle and provides type-safe access to services.
|
|
5
|
+
* Services are lazily initialized on first access after config is loaded.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Creates a service getter that lazily initializes the service
|
|
9
|
+
*
|
|
10
|
+
* @param initializer - Function that creates the service instance
|
|
11
|
+
* @returns Getter function that returns the service or null if not initialized
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* const getCalendarService = createLazyService(() => new CalendarService());
|
|
15
|
+
* // Later, when needed:
|
|
16
|
+
* const service = getCalendarService();
|
|
17
|
+
*/
|
|
18
|
+
export function createLazyService(initializer) {
|
|
19
|
+
let instance = null;
|
|
20
|
+
let initialized = false;
|
|
21
|
+
return () => {
|
|
22
|
+
if (!initialized) {
|
|
23
|
+
try {
|
|
24
|
+
instance = initializer();
|
|
25
|
+
initialized = true;
|
|
26
|
+
}
|
|
27
|
+
catch (error) {
|
|
28
|
+
console.error('Failed to initialize service:', error);
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return instance;
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Creates a service getter that requires config
|
|
37
|
+
*
|
|
38
|
+
* @param initializer - Function that creates the service with config
|
|
39
|
+
* @returns Getter function that accepts config and returns the service
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* const getReminderManager = createConfiguredService(
|
|
43
|
+
* (config) => new ReminderManager({ ... })
|
|
44
|
+
* );
|
|
45
|
+
* // Later, when config is available:
|
|
46
|
+
* const manager = getReminderManager(userConfig);
|
|
47
|
+
*/
|
|
48
|
+
export function createConfiguredService(initializer) {
|
|
49
|
+
let instance = null;
|
|
50
|
+
let initializedWithConfig = null;
|
|
51
|
+
return (config) => {
|
|
52
|
+
// Re-initialize if config changed
|
|
53
|
+
if (!instance || initializedWithConfig !== config) {
|
|
54
|
+
try {
|
|
55
|
+
instance = initializer(config);
|
|
56
|
+
initializedWithConfig = config;
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
console.error('Failed to initialize configured service:', error);
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return instance;
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Resets a lazy service to uninitialized state
|
|
68
|
+
*
|
|
69
|
+
* Useful for testing or when config changes require service re-initialization.
|
|
70
|
+
*
|
|
71
|
+
* @param resetter - Function that resets the service state
|
|
72
|
+
*/
|
|
73
|
+
export function resetLazyService(resetter) {
|
|
74
|
+
resetter();
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=container.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"container.js","sourceRoot":"","sources":["../../src/services/container.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAoBH;;;;;;;;;;GAUG;AACH,MAAM,UAAU,iBAAiB,CAC/B,WAAoB;IAEpB,IAAI,QAAQ,GAAa,IAAI,CAAC;IAC9B,IAAI,WAAW,GAAG,KAAK,CAAC;IAExB,OAAO,GAAG,EAAE;QACV,IAAI,CAAC,WAAW,EAAE,CAAC;YACjB,IAAI,CAAC;gBACH,QAAQ,GAAG,WAAW,EAAE,CAAC;gBACzB,WAAW,GAAG,IAAI,CAAC;YACrB,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,OAAO,CAAC,KAAK,CAAC,+BAA+B,EAAE,KAAK,CAAC,CAAC;gBACtD,OAAO,IAAI,CAAC;YACd,CAAC;QACH,CAAC;QACD,OAAO,QAAQ,CAAC;IAClB,CAAC,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,uBAAuB,CACrC,WAAsC;IAEtC,IAAI,QAAQ,GAAa,IAAI,CAAC;IAC9B,IAAI,qBAAqB,GAAsB,IAAI,CAAC;IAEpD,OAAO,CAAC,MAAkB,EAAE,EAAE;QAC5B,kCAAkC;QAClC,IAAI,CAAC,QAAQ,IAAI,qBAAqB,KAAK,MAAM,EAAE,CAAC;YAClD,IAAI,CAAC;gBACH,QAAQ,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC;gBAC/B,qBAAqB,GAAG,MAAM,CAAC;YACjC,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,OAAO,CAAC,KAAK,CAAC,0CAA0C,EAAE,KAAK,CAAC,CAAC;gBACjE,OAAO,IAAI,CAAC;YACd,CAAC;QACH,CAAC;QACD,OAAO,QAAQ,CAAC;IAClB,CAAC,CAAC;AACJ,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,gBAAgB,CAAC,QAAoB;IACnD,QAAQ,EAAE,CAAC;AACb,CAAC"}
|