veryfront 0.0.56 → 0.0.58

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 (59) hide show
  1. package/dist/ai/index.js +104 -13
  2. package/dist/ai/index.js.map +3 -3
  3. package/dist/ai/workflow.js +1 -1
  4. package/dist/ai/workflow.js.map +1 -1
  5. package/dist/cli.js +183 -26
  6. package/dist/components.js +1 -1
  7. package/dist/components.js.map +1 -1
  8. package/dist/config.js +1 -1
  9. package/dist/config.js.map +1 -1
  10. package/dist/data.js +1 -1
  11. package/dist/data.js.map +1 -1
  12. package/dist/index.js +2 -4
  13. package/dist/index.js.map +2 -2
  14. package/dist/integrations/_base/files/app/api/integrations/status/route.ts +1 -1
  15. package/dist/integrations/_base/files/app/api/integrations/token-storage/route.ts +7 -3
  16. package/dist/integrations/_base/files/app/setup/page.tsx +81 -23
  17. package/dist/integrations/_base/files/lib/token-store-examples.ts +1 -1
  18. package/dist/integrations/_base/files/lib/token-store.ts +35 -9
  19. package/dist/integrations/airtable/files/app/api/auth/airtable/callback/route.ts +23 -3
  20. package/dist/integrations/asana/files/app/api/auth/asana/callback/route.ts +23 -3
  21. package/dist/integrations/bitbucket/files/app/api/auth/bitbucket/callback/route.ts +23 -3
  22. package/dist/integrations/box/files/app/api/auth/box/callback/route.ts +23 -3
  23. package/dist/integrations/calendar/files/app/api/auth/calendar/callback/route.ts +23 -3
  24. package/dist/integrations/clickup/files/app/api/auth/clickup/callback/route.ts +23 -3
  25. package/dist/integrations/confluence/files/app/api/auth/confluence/callback/route.ts +23 -3
  26. package/dist/integrations/discord/files/app/api/auth/discord/callback/route.ts +23 -3
  27. package/dist/integrations/docs-google/files/app/api/auth/docs-google/callback/route.ts +24 -4
  28. package/dist/integrations/drive/files/app/api/auth/drive/callback/route.ts +24 -4
  29. package/dist/integrations/dropbox/files/app/api/auth/dropbox/callback/route.ts +23 -3
  30. package/dist/integrations/figma/files/app/api/auth/figma/callback/route.ts +23 -3
  31. package/dist/integrations/freshdesk/files/app/api/auth/freshdesk/callback/route.ts +24 -4
  32. package/dist/integrations/github/files/app/api/auth/github/callback/route.ts +23 -3
  33. package/dist/integrations/gitlab/files/app/api/auth/gitlab/callback/route.ts +23 -3
  34. package/dist/integrations/gmail/files/app/api/auth/gmail/callback/route.ts +1 -1
  35. package/dist/integrations/gmail/files/lib/gmail-client.ts +21 -3
  36. package/dist/integrations/hubspot/files/app/api/auth/hubspot/callback/route.ts +23 -3
  37. package/dist/integrations/intercom/files/app/api/auth/intercom/callback/route.ts +24 -4
  38. package/dist/integrations/jira/files/app/api/auth/jira/callback/route.ts +23 -3
  39. package/dist/integrations/linear/files/app/api/auth/linear/callback/route.ts +23 -3
  40. package/dist/integrations/mailchimp/files/app/api/auth/mailchimp/callback/route.ts +23 -3
  41. package/dist/integrations/monday/files/app/api/auth/monday/callback/route.ts +24 -4
  42. package/dist/integrations/neon/files/app/api/auth/neon/route.ts +1 -1
  43. package/dist/integrations/notion/files/app/api/auth/notion/callback/route.ts +23 -3
  44. package/dist/integrations/onedrive/files/app/api/auth/onedrive/callback/route.ts +23 -3
  45. package/dist/integrations/outlook/files/app/api/auth/outlook/callback/route.ts +23 -3
  46. package/dist/integrations/pipedrive/files/app/api/auth/pipedrive/callback/route.ts +24 -4
  47. package/dist/integrations/quickbooks/files/app/api/auth/quickbooks/callback/route.ts +24 -4
  48. package/dist/integrations/salesforce/files/app/api/auth/salesforce/callback/route.ts +23 -3
  49. package/dist/integrations/sharepoint/files/app/api/auth/sharepoint/callback/route.ts +23 -3
  50. package/dist/integrations/sheets/files/app/api/auth/sheets/callback/route.ts +23 -3
  51. package/dist/integrations/shopify/files/app/api/auth/shopify/callback/route.ts +24 -4
  52. package/dist/integrations/slack/files/app/api/auth/slack/callback/route.ts +23 -3
  53. package/dist/integrations/teams/files/app/api/auth/teams/callback/route.ts +23 -3
  54. package/dist/integrations/trello/files/app/api/auth/trello/callback/route.ts +24 -4
  55. package/dist/integrations/twitter/files/app/api/auth/twitter/callback/route.ts +23 -3
  56. package/dist/integrations/webex/files/app/api/auth/webex/callback/route.ts +23 -3
  57. package/dist/integrations/xero/files/app/api/auth/xero/callback/route.ts +24 -4
  58. package/dist/integrations/zoom/files/app/api/auth/zoom/callback/route.ts +24 -4
  59. package/package.json +1 -1
@@ -5,7 +5,7 @@
5
5
  * Used by the setup guide to show which services are connected.
6
6
  */
7
7
 
8
- import { tokenStore } from "../../../../lib/token-store";
8
+ import { tokenStore } from "../../../../lib/token-store.ts";
9
9
 
10
10
  // Define available integrations - will be populated based on project config
11
11
  const INTEGRATIONS = [
@@ -18,9 +18,13 @@ export async function GET() {
18
18
  mode = "redis";
19
19
  }
20
20
 
21
- // Check if encryption is enabled
21
+ // Check if encryption key is explicitly set
22
22
  const encryptionKey = env.TOKEN_ENCRYPTION_KEY;
23
- const encrypted = typeof encryptionKey === "string" && encryptionKey.length === 64;
23
+ const hasExplicitKey = typeof encryptionKey === "string" && encryptionKey.length === 64;
24
24
 
25
- return Response.json({ mode, encrypted });
25
+ // Encryption is always enabled (auto-generated in dev if not set)
26
+ const encrypted = true;
27
+ const autoGenerated = !hasExplicitKey;
28
+
29
+ return Response.json({ mode, encrypted, autoGenerated });
26
30
  }
@@ -30,6 +30,7 @@ interface SetupGuide {
30
30
  interface TokenStorageStatus {
31
31
  mode: "memory" | "database" | "kv" | "redis" | "custom";
32
32
  encrypted: boolean;
33
+ autoGenerated?: boolean;
33
34
  }
34
35
 
35
36
  // Categories for organizing integrations
@@ -871,34 +872,91 @@ export default function SetupPage() {
871
872
  : "text-green-700 dark:text-green-300"
872
873
  }`}>
873
874
  {tokenStorage.mode === "memory" ? (
874
- <>Tokens are stored in memory and will be lost on restart. Set <code className="px-1 py-0.5 bg-amber-100 dark:bg-amber-900 rounded text-xs">DATABASE_URL</code>, <code className="px-1 py-0.5 bg-amber-100 dark:bg-amber-900 rounded text-xs">KV_REST_API_URL</code>, or <code className="px-1 py-0.5 bg-amber-100 dark:bg-amber-900 rounded text-xs">REDIS_URL</code> for production.</>
875
+ <>Tokens are stored in memory and will be lost on restart.</>
875
876
  ) : (
876
877
  <>Tokens are persisted to {tokenStorage.mode} storage.</>
877
878
  )}
878
879
  </p>
879
- <div className="mt-2 flex items-center gap-4 text-sm">
880
- <span className={`inline-flex items-center gap-1.5 ${
881
- tokenStorage.encrypted
882
- ? "text-green-600 dark:text-green-400"
883
- : "text-amber-600 dark:text-amber-400"
884
- }`}>
885
- {tokenStorage.encrypted ? (
886
- <>
887
- <svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
888
- <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z" />
889
- </svg>
890
- Encryption enabled
891
- </>
892
- ) : (
893
- <>
894
- <svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
895
- <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8 11V7a4 4 0 118 0m-4 8v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2z" />
896
- </svg>
897
- Set <code className="px-1 py-0.5 bg-amber-100 dark:bg-amber-900 rounded text-xs">TOKEN_ENCRYPTION_KEY</code> for encryption
898
- </>
899
- )}
900
- </span>
880
+
881
+ {/* Encryption Status */}
882
+ <div className="mt-2 flex items-center gap-1.5 text-sm text-green-600 dark:text-green-400">
883
+ <svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
884
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z" />
885
+ </svg>
886
+ <span>Encryption enabled {tokenStorage.autoGenerated && "(auto-generated key)"}</span>
901
887
  </div>
888
+
889
+ {/* Production Storage Options */}
890
+ {tokenStorage.mode === "memory" && (
891
+ <div className="mt-4 pt-4 border-t border-amber-200 dark:border-amber-800">
892
+ <p className="text-sm font-medium text-amber-800 dark:text-amber-200 mb-3">
893
+ For production, add one of these to your <code className="px-1 py-0.5 bg-amber-100 dark:bg-amber-900 rounded text-xs">.env</code>:
894
+ </p>
895
+ <div className="grid gap-2">
896
+ <a
897
+ href="https://upstash.com/docs/redis/overall/getstarted"
898
+ target="_blank"
899
+ rel="noopener noreferrer"
900
+ className="flex items-center justify-between p-3 bg-white dark:bg-neutral-800 rounded-lg border border-green-200 dark:border-green-700 hover:border-green-400 dark:hover:border-green-500 transition-colors group"
901
+ >
902
+ <div>
903
+ <span className="font-medium text-neutral-900 dark:text-white">Upstash</span>
904
+ <span className="text-green-600 dark:text-green-400 text-xs ml-2 font-medium">Recommended</span>
905
+ <span className="text-neutral-500 dark:text-neutral-400 text-sm ml-2">Serverless Redis, scales horizontally</span>
906
+ </div>
907
+ <code className="text-xs bg-neutral-100 dark:bg-neutral-700 px-2 py-1 rounded text-neutral-600 dark:text-neutral-300">REDIS_URL</code>
908
+ </a>
909
+ <a
910
+ href="https://docs.turso.tech/quickstart"
911
+ target="_blank"
912
+ rel="noopener noreferrer"
913
+ className="flex items-center justify-between p-3 bg-white dark:bg-neutral-800 rounded-lg border border-amber-200 dark:border-amber-700 hover:border-amber-400 dark:hover:border-amber-500 transition-colors group"
914
+ >
915
+ <div>
916
+ <span className="font-medium text-neutral-900 dark:text-white">Turso / libSQL</span>
917
+ <span className="text-neutral-500 dark:text-neutral-400 text-sm ml-2">Edge SQLite, fast reads globally</span>
918
+ </div>
919
+ <code className="text-xs bg-neutral-100 dark:bg-neutral-700 px-2 py-1 rounded text-neutral-600 dark:text-neutral-300">DATABASE_URL</code>
920
+ </a>
921
+ <a
922
+ href="https://vercel.com/docs/storage/vercel-kv/quickstart"
923
+ target="_blank"
924
+ rel="noopener noreferrer"
925
+ className="flex items-center justify-between p-3 bg-white dark:bg-neutral-800 rounded-lg border border-amber-200 dark:border-amber-700 hover:border-amber-400 dark:hover:border-amber-500 transition-colors group"
926
+ >
927
+ <div>
928
+ <span className="font-medium text-neutral-900 dark:text-white">Vercel KV</span>
929
+ <span className="text-neutral-500 dark:text-neutral-400 text-sm ml-2">Built-in if using Vercel</span>
930
+ </div>
931
+ <code className="text-xs bg-neutral-100 dark:bg-neutral-700 px-2 py-1 rounded text-neutral-600 dark:text-neutral-300">KV_REST_API_URL</code>
932
+ </a>
933
+ <a
934
+ href="https://neon.tech/docs/get-started-with-neon/connect-neon"
935
+ target="_blank"
936
+ rel="noopener noreferrer"
937
+ className="flex items-center justify-between p-3 bg-white dark:bg-neutral-800 rounded-lg border border-amber-200 dark:border-amber-700 hover:border-amber-400 dark:hover:border-amber-500 transition-colors group"
938
+ >
939
+ <div>
940
+ <span className="font-medium text-neutral-900 dark:text-white">Neon</span>
941
+ <span className="text-neutral-500 dark:text-neutral-400 text-sm ml-2">Serverless Postgres</span>
942
+ </div>
943
+ <code className="text-xs bg-neutral-100 dark:bg-neutral-700 px-2 py-1 rounded text-neutral-600 dark:text-neutral-300">DATABASE_URL</code>
944
+ </a>
945
+ <a
946
+ href="https://www.sqlite.org/index.html"
947
+ target="_blank"
948
+ rel="noopener noreferrer"
949
+ className="flex items-center justify-between p-3 bg-white dark:bg-neutral-800 rounded-lg border border-amber-200 dark:border-amber-700 hover:border-amber-400 dark:hover:border-amber-500 transition-colors group"
950
+ >
951
+ <div>
952
+ <span className="font-medium text-neutral-900 dark:text-white">SQLite</span>
953
+ <span className="text-neutral-500 dark:text-neutral-400 text-sm ml-2">Local file, single instance only</span>
954
+ </div>
955
+ <code className="text-xs bg-neutral-100 dark:bg-neutral-700 px-2 py-1 rounded text-neutral-600 dark:text-neutral-300">DATABASE_URL=file:./data.db</code>
956
+ </a>
957
+ </div>
958
+ </div>
959
+ )}
902
960
  </div>
903
961
  </div>
904
962
  </div>
@@ -13,7 +13,7 @@ import {
13
13
  decryptToken,
14
14
  type TokenStore,
15
15
  type OAuthToken,
16
- } from "./token-store";
16
+ } from "./token-store.ts";
17
17
 
18
18
  // ============================================================================
19
19
  // Vercel KV Store
@@ -172,24 +172,52 @@ export async function decryptToken(encrypted: string): Promise<OAuthToken | null
172
172
  }
173
173
  }
174
174
 
175
- /** Get encryption key from environment */
175
+ // Auto-generated encryption key storage (persists for the session)
176
+ const AUTO_KEY_STORAGE = "__veryfront_auto_encryption_key__";
177
+ // deno-lint-ignore no-explicit-any
178
+ const globalStore = globalThis as any;
179
+
180
+ /**
181
+ * Generate a cryptographically secure encryption key
182
+ * Returns a 64-character hex string (32 bytes)
183
+ */
184
+ export function generateEncryptionKey(): string {
185
+ const bytes = crypto.getRandomValues(new Uint8Array(32));
186
+ return Array.from(bytes).map(b => b.toString(16).padStart(2, '0')).join('');
187
+ }
188
+
189
+ /** Get encryption key from environment or auto-generate for development */
176
190
  function getEncryptionKey(): Uint8Array | null {
177
191
  const keyHex = typeof process !== "undefined"
178
192
  ? process.env?.TOKEN_ENCRYPTION_KEY
179
193
  // deno-lint-ignore no-explicit-any
180
194
  : (globalThis as any).Deno?.env?.get("TOKEN_ENCRYPTION_KEY");
181
195
 
182
- if (!keyHex) return null;
196
+ if (keyHex) {
197
+ // Convert hex string to Uint8Array (32 bytes = 64 hex chars)
198
+ if (keyHex.length !== 64) {
199
+ console.error("[Token Store] TOKEN_ENCRYPTION_KEY must be 64 hex characters (32 bytes)");
200
+ return null;
201
+ }
183
202
 
184
- // Convert hex string to Uint8Array (32 bytes = 64 hex chars)
185
- if (keyHex.length !== 64) {
186
- console.error("[Token Store] TOKEN_ENCRYPTION_KEY must be 64 hex characters (32 bytes)");
187
- return null;
203
+ const key = new Uint8Array(32);
204
+ for (let i = 0; i < 32; i++) {
205
+ key[i] = parseInt(keyHex.slice(i * 2, i * 2 + 2), 16);
206
+ }
207
+ return key;
188
208
  }
189
209
 
210
+ // Auto-generate key for development (persists in memory for the session)
211
+ // This ensures tokens remain encrypted even in dev mode
212
+ if (!globalStore[AUTO_KEY_STORAGE]) {
213
+ globalStore[AUTO_KEY_STORAGE] = generateEncryptionKey();
214
+ console.log("[Token Store] Auto-generated encryption key for this session");
215
+ }
216
+
217
+ const autoKey = globalStore[AUTO_KEY_STORAGE] as string;
190
218
  const key = new Uint8Array(32);
191
219
  for (let i = 0; i < 32; i++) {
192
- key[i] = parseInt(keyHex.slice(i * 2, i * 2 + 2), 16);
220
+ key[i] = parseInt(autoKey.slice(i * 2, i * 2 + 2), 16);
193
221
  }
194
222
  return key;
195
223
  }
@@ -225,8 +253,6 @@ export function isEncryptionEnabled(): boolean {
225
253
 
226
254
  // Use globalThis to share across esbuild bundles (each API route is bundled separately)
227
255
  const TOKENS_KEY = "__veryfront_oauth_tokens__";
228
- // deno-lint-ignore no-explicit-any
229
- const globalStore = globalThis as any;
230
256
  const tokens: Map<string, OAuthToken> = globalStore[TOKENS_KEY] ||= new Map<string, OAuthToken>();
231
257
 
232
258
  function getKey(userId: string, service: string): string {
@@ -1,11 +1,31 @@
1
1
  /**
2
2
  * Airtable OAuth Callback
3
+ *
4
+ * Handles the OAuth callback from Airtable and stores the tokens.
3
5
  */
4
6
 
5
7
  import { airtableConfig, createOAuthCallbackHandler, memoryTokenStore } from "veryfront/oauth";
8
+ import { tokenStore } from "../../../../../lib/token-store.ts";
9
+
10
+ // Hybrid adapter: uses framework's memoryTokenStore for state (PKCE),
11
+ // but user's tokenStore for actual token storage
12
+ const hybridTokenStore = {
13
+ // Token methods - delegate to user's tokenStore
14
+ async getTokens(serviceId: string) {
15
+ return tokenStore.getToken("current-user", serviceId);
16
+ },
17
+ async setTokens(serviceId: string, tokens: { accessToken: string; refreshToken?: string; expiresAt?: number }) {
18
+ await tokenStore.setToken("current-user", serviceId, tokens);
19
+ },
20
+ async clearTokens(serviceId: string) {
21
+ await tokenStore.revokeToken("current-user", serviceId);
22
+ },
23
+ // State methods - delegate to framework's memoryTokenStore (shared with init route)
24
+ getState: (state: string) => memoryTokenStore.getState(state),
25
+ setState: (state: { state: string; codeVerifier?: string; createdAt: number }) => memoryTokenStore.setState(state),
26
+ clearState: (state: string) => memoryTokenStore.clearState(state),
27
+ };
6
28
 
7
29
  export const GET = createOAuthCallbackHandler(airtableConfig, {
8
- tokenStore: memoryTokenStore,
9
- onSuccess: () => "/",
10
- onError: () => "/",
30
+ tokenStore: hybridTokenStore,
11
31
  });
@@ -1,11 +1,31 @@
1
1
  /**
2
2
  * Asana OAuth Callback
3
+ *
4
+ * Handles the OAuth callback from Asana and stores the tokens.
3
5
  */
4
6
 
5
7
  import { asanaConfig, createOAuthCallbackHandler, memoryTokenStore } from "veryfront/oauth";
8
+ import { tokenStore } from "../../../../../lib/token-store.ts";
9
+
10
+ // Hybrid adapter: uses framework's memoryTokenStore for state (PKCE),
11
+ // but user's tokenStore for actual token storage
12
+ const hybridTokenStore = {
13
+ // Token methods - delegate to user's tokenStore
14
+ async getTokens(serviceId: string) {
15
+ return tokenStore.getToken("current-user", serviceId);
16
+ },
17
+ async setTokens(serviceId: string, tokens: { accessToken: string; refreshToken?: string; expiresAt?: number }) {
18
+ await tokenStore.setToken("current-user", serviceId, tokens);
19
+ },
20
+ async clearTokens(serviceId: string) {
21
+ await tokenStore.revokeToken("current-user", serviceId);
22
+ },
23
+ // State methods - delegate to framework's memoryTokenStore (shared with init route)
24
+ getState: (state: string) => memoryTokenStore.getState(state),
25
+ setState: (state: { state: string; codeVerifier?: string; createdAt: number }) => memoryTokenStore.setState(state),
26
+ clearState: (state: string) => memoryTokenStore.clearState(state),
27
+ };
6
28
 
7
29
  export const GET = createOAuthCallbackHandler(asanaConfig, {
8
- tokenStore: memoryTokenStore,
9
- onSuccess: () => "/",
10
- onError: () => "/",
30
+ tokenStore: hybridTokenStore,
11
31
  });
@@ -1,11 +1,31 @@
1
1
  /**
2
2
  * Bitbucket OAuth Callback
3
+ *
4
+ * Handles the OAuth callback from Atlassian and stores the tokens.
3
5
  */
4
6
 
5
7
  import { bitbucketConfig, createOAuthCallbackHandler, memoryTokenStore } from "veryfront/oauth";
8
+ import { tokenStore } from "../../../../../lib/token-store.ts";
9
+
10
+ // Hybrid adapter: uses framework's memoryTokenStore for state (PKCE),
11
+ // but user's tokenStore for actual token storage
12
+ const hybridTokenStore = {
13
+ // Token methods - delegate to user's tokenStore
14
+ async getTokens(serviceId: string) {
15
+ return tokenStore.getToken("current-user", serviceId);
16
+ },
17
+ async setTokens(serviceId: string, tokens: { accessToken: string; refreshToken?: string; expiresAt?: number }) {
18
+ await tokenStore.setToken("current-user", serviceId, tokens);
19
+ },
20
+ async clearTokens(serviceId: string) {
21
+ await tokenStore.revokeToken("current-user", serviceId);
22
+ },
23
+ // State methods - delegate to framework's memoryTokenStore (shared with init route)
24
+ getState: (state: string) => memoryTokenStore.getState(state),
25
+ setState: (state: { state: string; codeVerifier?: string; createdAt: number }) => memoryTokenStore.setState(state),
26
+ clearState: (state: string) => memoryTokenStore.clearState(state),
27
+ };
6
28
 
7
29
  export const GET = createOAuthCallbackHandler(bitbucketConfig, {
8
- tokenStore: memoryTokenStore,
9
- onSuccess: () => "/",
10
- onError: () => "/",
30
+ tokenStore: hybridTokenStore,
11
31
  });
@@ -1,11 +1,31 @@
1
1
  /**
2
2
  * Box OAuth Callback
3
+ *
4
+ * Handles the OAuth callback from Box and stores the tokens.
3
5
  */
4
6
 
5
7
  import { boxConfig, createOAuthCallbackHandler, memoryTokenStore } from "veryfront/oauth";
8
+ import { tokenStore } from "../../../../../lib/token-store.ts";
9
+
10
+ // Hybrid adapter: uses framework's memoryTokenStore for state (PKCE),
11
+ // but user's tokenStore for actual token storage
12
+ const hybridTokenStore = {
13
+ // Token methods - delegate to user's tokenStore
14
+ async getTokens(serviceId: string) {
15
+ return tokenStore.getToken("current-user", serviceId);
16
+ },
17
+ async setTokens(serviceId: string, tokens: { accessToken: string; refreshToken?: string; expiresAt?: number }) {
18
+ await tokenStore.setToken("current-user", serviceId, tokens);
19
+ },
20
+ async clearTokens(serviceId: string) {
21
+ await tokenStore.revokeToken("current-user", serviceId);
22
+ },
23
+ // State methods - delegate to framework's memoryTokenStore (shared with init route)
24
+ getState: (state: string) => memoryTokenStore.getState(state),
25
+ setState: (state: { state: string; codeVerifier?: string; createdAt: number }) => memoryTokenStore.setState(state),
26
+ clearState: (state: string) => memoryTokenStore.clearState(state),
27
+ };
6
28
 
7
29
  export const GET = createOAuthCallbackHandler(boxConfig, {
8
- tokenStore: memoryTokenStore,
9
- onSuccess: () => "/",
10
- onError: () => "/",
30
+ tokenStore: hybridTokenStore,
11
31
  });
@@ -1,11 +1,31 @@
1
1
  /**
2
2
  * Calendar OAuth Callback
3
+ *
4
+ * Handles the OAuth callback from Google and stores the tokens.
3
5
  */
4
6
 
5
7
  import { calendarConfig, createOAuthCallbackHandler, memoryTokenStore } from "veryfront/oauth";
8
+ import { tokenStore } from "../../../../../lib/token-store.ts";
9
+
10
+ // Hybrid adapter: uses framework's memoryTokenStore for state (PKCE),
11
+ // but user's tokenStore for actual token storage
12
+ const hybridTokenStore = {
13
+ // Token methods - delegate to user's tokenStore
14
+ async getTokens(serviceId: string) {
15
+ return tokenStore.getToken("current-user", serviceId);
16
+ },
17
+ async setTokens(serviceId: string, tokens: { accessToken: string; refreshToken?: string; expiresAt?: number }) {
18
+ await tokenStore.setToken("current-user", serviceId, tokens);
19
+ },
20
+ async clearTokens(serviceId: string) {
21
+ await tokenStore.revokeToken("current-user", serviceId);
22
+ },
23
+ // State methods - delegate to framework's memoryTokenStore (shared with init route)
24
+ getState: (state: string) => memoryTokenStore.getState(state),
25
+ setState: (state: { state: string; codeVerifier?: string; createdAt: number }) => memoryTokenStore.setState(state),
26
+ clearState: (state: string) => memoryTokenStore.clearState(state),
27
+ };
6
28
 
7
29
  export const GET = createOAuthCallbackHandler(calendarConfig, {
8
- tokenStore: memoryTokenStore,
9
- onSuccess: () => "/",
10
- onError: () => "/",
30
+ tokenStore: hybridTokenStore,
11
31
  });
@@ -1,11 +1,31 @@
1
1
  /**
2
2
  * ClickUp OAuth Callback
3
+ *
4
+ * Handles the OAuth callback from ClickUp and stores the tokens.
3
5
  */
4
6
 
5
7
  import { clickupConfig, createOAuthCallbackHandler, memoryTokenStore } from "veryfront/oauth";
8
+ import { tokenStore } from "../../../../../lib/token-store.ts";
9
+
10
+ // Hybrid adapter: uses framework's memoryTokenStore for state (PKCE),
11
+ // but user's tokenStore for actual token storage
12
+ const hybridTokenStore = {
13
+ // Token methods - delegate to user's tokenStore
14
+ async getTokens(serviceId: string) {
15
+ return tokenStore.getToken("current-user", serviceId);
16
+ },
17
+ async setTokens(serviceId: string, tokens: { accessToken: string; refreshToken?: string; expiresAt?: number }) {
18
+ await tokenStore.setToken("current-user", serviceId, tokens);
19
+ },
20
+ async clearTokens(serviceId: string) {
21
+ await tokenStore.revokeToken("current-user", serviceId);
22
+ },
23
+ // State methods - delegate to framework's memoryTokenStore (shared with init route)
24
+ getState: (state: string) => memoryTokenStore.getState(state),
25
+ setState: (state: { state: string; codeVerifier?: string; createdAt: number }) => memoryTokenStore.setState(state),
26
+ clearState: (state: string) => memoryTokenStore.clearState(state),
27
+ };
6
28
 
7
29
  export const GET = createOAuthCallbackHandler(clickupConfig, {
8
- tokenStore: memoryTokenStore,
9
- onSuccess: () => "/",
10
- onError: () => "/",
30
+ tokenStore: hybridTokenStore,
11
31
  });
@@ -1,11 +1,31 @@
1
1
  /**
2
2
  * Confluence OAuth Callback
3
+ *
4
+ * Handles the OAuth callback from Atlassian and stores the tokens.
3
5
  */
4
6
 
5
7
  import { confluenceConfig, createOAuthCallbackHandler, memoryTokenStore } from "veryfront/oauth";
8
+ import { tokenStore } from "../../../../../lib/token-store.ts";
9
+
10
+ // Hybrid adapter: uses framework's memoryTokenStore for state (PKCE),
11
+ // but user's tokenStore for actual token storage
12
+ const hybridTokenStore = {
13
+ // Token methods - delegate to user's tokenStore
14
+ async getTokens(serviceId: string) {
15
+ return tokenStore.getToken("current-user", serviceId);
16
+ },
17
+ async setTokens(serviceId: string, tokens: { accessToken: string; refreshToken?: string; expiresAt?: number }) {
18
+ await tokenStore.setToken("current-user", serviceId, tokens);
19
+ },
20
+ async clearTokens(serviceId: string) {
21
+ await tokenStore.revokeToken("current-user", serviceId);
22
+ },
23
+ // State methods - delegate to framework's memoryTokenStore (shared with init route)
24
+ getState: (state: string) => memoryTokenStore.getState(state),
25
+ setState: (state: { state: string; codeVerifier?: string; createdAt: number }) => memoryTokenStore.setState(state),
26
+ clearState: (state: string) => memoryTokenStore.clearState(state),
27
+ };
6
28
 
7
29
  export const GET = createOAuthCallbackHandler(confluenceConfig, {
8
- tokenStore: memoryTokenStore,
9
- onSuccess: () => "/",
10
- onError: () => "/",
30
+ tokenStore: hybridTokenStore,
11
31
  });
@@ -1,11 +1,31 @@
1
1
  /**
2
2
  * Discord OAuth Callback
3
+ *
4
+ * Handles the OAuth callback from Discord and stores the tokens.
3
5
  */
4
6
 
5
7
  import { createOAuthCallbackHandler, discordConfig, memoryTokenStore } from "veryfront/oauth";
8
+ import { tokenStore } from "../../../../../lib/token-store.ts";
9
+
10
+ // Hybrid adapter: uses framework's memoryTokenStore for state (PKCE),
11
+ // but user's tokenStore for actual token storage
12
+ const hybridTokenStore = {
13
+ // Token methods - delegate to user's tokenStore
14
+ async getTokens(serviceId: string) {
15
+ return tokenStore.getToken("current-user", serviceId);
16
+ },
17
+ async setTokens(serviceId: string, tokens: { accessToken: string; refreshToken?: string; expiresAt?: number }) {
18
+ await tokenStore.setToken("current-user", serviceId, tokens);
19
+ },
20
+ async clearTokens(serviceId: string) {
21
+ await tokenStore.revokeToken("current-user", serviceId);
22
+ },
23
+ // State methods - delegate to framework's memoryTokenStore (shared with init route)
24
+ getState: (state: string) => memoryTokenStore.getState(state),
25
+ setState: (state: { state: string; codeVerifier?: string; createdAt: number }) => memoryTokenStore.setState(state),
26
+ clearState: (state: string) => memoryTokenStore.clearState(state),
27
+ };
6
28
 
7
29
  export const GET = createOAuthCallbackHandler(discordConfig, {
8
- tokenStore: memoryTokenStore,
9
- onSuccess: () => "/",
10
- onError: () => "/",
30
+ tokenStore: hybridTokenStore,
11
31
  });
@@ -1,11 +1,31 @@
1
1
  /**
2
2
  * Google Docs OAuth Callback
3
+ *
4
+ * Handles the OAuth callback from Google and stores the tokens.
3
5
  */
4
6
 
5
- import { createOAuthCallbackHandler, memoryTokenStore, docsGoogleConfig } from "veryfront/oauth";
7
+ import { createOAuthCallbackHandler, docsGoogleConfig, memoryTokenStore } from "veryfront/oauth";
8
+ import { tokenStore } from "../../../../../lib/token-store.ts";
9
+
10
+ // Hybrid adapter: uses framework's memoryTokenStore for state (PKCE),
11
+ // but user's tokenStore for actual token storage
12
+ const hybridTokenStore = {
13
+ // Token methods - delegate to user's tokenStore
14
+ async getTokens(serviceId: string) {
15
+ return tokenStore.getToken("current-user", serviceId);
16
+ },
17
+ async setTokens(serviceId: string, tokens: { accessToken: string; refreshToken?: string; expiresAt?: number }) {
18
+ await tokenStore.setToken("current-user", serviceId, tokens);
19
+ },
20
+ async clearTokens(serviceId: string) {
21
+ await tokenStore.revokeToken("current-user", serviceId);
22
+ },
23
+ // State methods - delegate to framework's memoryTokenStore (shared with init route)
24
+ getState: (state: string) => memoryTokenStore.getState(state),
25
+ setState: (state: { state: string; codeVerifier?: string; createdAt: number }) => memoryTokenStore.setState(state),
26
+ clearState: (state: string) => memoryTokenStore.clearState(state),
27
+ };
6
28
 
7
29
  export const GET = createOAuthCallbackHandler(docsGoogleConfig, {
8
- tokenStore: memoryTokenStore,
9
- onSuccess: () => "/",
10
- onError: () => "/",
30
+ tokenStore: hybridTokenStore,
11
31
  });
@@ -1,11 +1,31 @@
1
1
  /**
2
2
  * Google Drive OAuth Callback
3
+ *
4
+ * Handles the OAuth callback from Google and stores the tokens.
3
5
  */
4
6
 
5
- import { createOAuthCallbackHandler, memoryTokenStore, driveConfig } from "veryfront/oauth";
7
+ import { createOAuthCallbackHandler, driveConfig, memoryTokenStore } from "veryfront/oauth";
8
+ import { tokenStore } from "../../../../../lib/token-store.ts";
9
+
10
+ // Hybrid adapter: uses framework's memoryTokenStore for state (PKCE),
11
+ // but user's tokenStore for actual token storage
12
+ const hybridTokenStore = {
13
+ // Token methods - delegate to user's tokenStore
14
+ async getTokens(serviceId: string) {
15
+ return tokenStore.getToken("current-user", serviceId);
16
+ },
17
+ async setTokens(serviceId: string, tokens: { accessToken: string; refreshToken?: string; expiresAt?: number }) {
18
+ await tokenStore.setToken("current-user", serviceId, tokens);
19
+ },
20
+ async clearTokens(serviceId: string) {
21
+ await tokenStore.revokeToken("current-user", serviceId);
22
+ },
23
+ // State methods - delegate to framework's memoryTokenStore (shared with init route)
24
+ getState: (state: string) => memoryTokenStore.getState(state),
25
+ setState: (state: { state: string; codeVerifier?: string; createdAt: number }) => memoryTokenStore.setState(state),
26
+ clearState: (state: string) => memoryTokenStore.clearState(state),
27
+ };
6
28
 
7
29
  export const GET = createOAuthCallbackHandler(driveConfig, {
8
- tokenStore: memoryTokenStore,
9
- onSuccess: () => "/",
10
- onError: () => "/",
30
+ tokenStore: hybridTokenStore,
11
31
  });
@@ -1,11 +1,31 @@
1
1
  /**
2
2
  * Dropbox OAuth Callback
3
+ *
4
+ * Handles the OAuth callback from Dropbox and stores the tokens.
3
5
  */
4
6
 
5
7
  import { createOAuthCallbackHandler, dropboxConfig, memoryTokenStore } from "veryfront/oauth";
8
+ import { tokenStore } from "../../../../../lib/token-store.ts";
9
+
10
+ // Hybrid adapter: uses framework's memoryTokenStore for state (PKCE),
11
+ // but user's tokenStore for actual token storage
12
+ const hybridTokenStore = {
13
+ // Token methods - delegate to user's tokenStore
14
+ async getTokens(serviceId: string) {
15
+ return tokenStore.getToken("current-user", serviceId);
16
+ },
17
+ async setTokens(serviceId: string, tokens: { accessToken: string; refreshToken?: string; expiresAt?: number }) {
18
+ await tokenStore.setToken("current-user", serviceId, tokens);
19
+ },
20
+ async clearTokens(serviceId: string) {
21
+ await tokenStore.revokeToken("current-user", serviceId);
22
+ },
23
+ // State methods - delegate to framework's memoryTokenStore (shared with init route)
24
+ getState: (state: string) => memoryTokenStore.getState(state),
25
+ setState: (state: { state: string; codeVerifier?: string; createdAt: number }) => memoryTokenStore.setState(state),
26
+ clearState: (state: string) => memoryTokenStore.clearState(state),
27
+ };
6
28
 
7
29
  export const GET = createOAuthCallbackHandler(dropboxConfig, {
8
- tokenStore: memoryTokenStore,
9
- onSuccess: () => "/",
10
- onError: () => "/",
30
+ tokenStore: hybridTokenStore,
11
31
  });