@flink-app/github-app-plugin 0.12.1-alpha.38

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 (96) hide show
  1. package/CHANGELOG.md +209 -0
  2. package/LICENSE +21 -0
  3. package/README.md +667 -0
  4. package/SECURITY.md +498 -0
  5. package/dist/GitHubAppInternalContext.d.ts +44 -0
  6. package/dist/GitHubAppInternalContext.js +2 -0
  7. package/dist/GitHubAppPlugin.d.ts +45 -0
  8. package/dist/GitHubAppPlugin.js +367 -0
  9. package/dist/GitHubAppPluginContext.d.ts +242 -0
  10. package/dist/GitHubAppPluginContext.js +2 -0
  11. package/dist/GitHubAppPluginOptions.d.ts +369 -0
  12. package/dist/GitHubAppPluginOptions.js +2 -0
  13. package/dist/handlers/InitiateInstallation.d.ts +32 -0
  14. package/dist/handlers/InitiateInstallation.js +66 -0
  15. package/dist/handlers/InstallationCallback.d.ts +42 -0
  16. package/dist/handlers/InstallationCallback.js +248 -0
  17. package/dist/handlers/UninstallHandler.d.ts +37 -0
  18. package/dist/handlers/UninstallHandler.js +153 -0
  19. package/dist/handlers/WebhookHandler.d.ts +54 -0
  20. package/dist/handlers/WebhookHandler.js +157 -0
  21. package/dist/index.d.ts +19 -0
  22. package/dist/index.js +23 -0
  23. package/dist/repos/GitHubAppSessionRepo.d.ts +24 -0
  24. package/dist/repos/GitHubAppSessionRepo.js +32 -0
  25. package/dist/repos/GitHubInstallationRepo.d.ts +53 -0
  26. package/dist/repos/GitHubInstallationRepo.js +83 -0
  27. package/dist/repos/GitHubWebhookEventRepo.d.ts +29 -0
  28. package/dist/repos/GitHubWebhookEventRepo.js +42 -0
  29. package/dist/schemas/GitHubAppSession.d.ts +13 -0
  30. package/dist/schemas/GitHubAppSession.js +2 -0
  31. package/dist/schemas/GitHubInstallation.d.ts +28 -0
  32. package/dist/schemas/GitHubInstallation.js +2 -0
  33. package/dist/schemas/InstallationCallbackRequest.d.ts +10 -0
  34. package/dist/schemas/InstallationCallbackRequest.js +2 -0
  35. package/dist/schemas/WebhookEvent.d.ts +16 -0
  36. package/dist/schemas/WebhookEvent.js +2 -0
  37. package/dist/schemas/WebhookPayload.d.ts +35 -0
  38. package/dist/schemas/WebhookPayload.js +2 -0
  39. package/dist/services/GitHubAPIClient.d.ts +143 -0
  40. package/dist/services/GitHubAPIClient.js +167 -0
  41. package/dist/services/GitHubAuthService.d.ts +85 -0
  42. package/dist/services/GitHubAuthService.js +160 -0
  43. package/dist/services/WebhookValidator.d.ts +93 -0
  44. package/dist/services/WebhookValidator.js +123 -0
  45. package/dist/utils/error-utils.d.ts +67 -0
  46. package/dist/utils/error-utils.js +121 -0
  47. package/dist/utils/jwt-utils.d.ts +35 -0
  48. package/dist/utils/jwt-utils.js +67 -0
  49. package/dist/utils/state-utils.d.ts +38 -0
  50. package/dist/utils/state-utils.js +74 -0
  51. package/dist/utils/token-cache-utils.d.ts +47 -0
  52. package/dist/utils/token-cache-utils.js +74 -0
  53. package/dist/utils/webhook-signature-utils.d.ts +22 -0
  54. package/dist/utils/webhook-signature-utils.js +57 -0
  55. package/examples/basic-installation.ts +246 -0
  56. package/examples/create-issue.ts +392 -0
  57. package/examples/error-handling.ts +396 -0
  58. package/examples/multi-event-webhook.ts +367 -0
  59. package/examples/organization-installation.ts +316 -0
  60. package/examples/repository-access.ts +480 -0
  61. package/examples/webhook-handling.ts +343 -0
  62. package/examples/with-jwt-auth.ts +319 -0
  63. package/package.json +41 -0
  64. package/spec/core-utilities.spec.ts +243 -0
  65. package/spec/handlers.spec.ts +216 -0
  66. package/spec/helpers/reporter.ts +41 -0
  67. package/spec/integration-and-security.spec.ts +483 -0
  68. package/spec/plugin-core.spec.ts +258 -0
  69. package/spec/project-setup.spec.ts +56 -0
  70. package/spec/repos-and-schemas.spec.ts +288 -0
  71. package/spec/services.spec.ts +108 -0
  72. package/spec/support/jasmine.json +7 -0
  73. package/src/GitHubAppPlugin.ts +411 -0
  74. package/src/GitHubAppPluginContext.ts +254 -0
  75. package/src/GitHubAppPluginOptions.ts +412 -0
  76. package/src/handlers/InstallationCallback.ts +292 -0
  77. package/src/handlers/WebhookHandler.ts +179 -0
  78. package/src/index.ts +29 -0
  79. package/src/repos/GitHubAppSessionRepo.ts +36 -0
  80. package/src/repos/GitHubInstallationRepo.ts +95 -0
  81. package/src/repos/GitHubWebhookEventRepo.ts +48 -0
  82. package/src/schemas/GitHubAppSession.ts +13 -0
  83. package/src/schemas/GitHubInstallation.ts +28 -0
  84. package/src/schemas/InstallationCallbackRequest.ts +10 -0
  85. package/src/schemas/WebhookEvent.ts +16 -0
  86. package/src/schemas/WebhookPayload.ts +35 -0
  87. package/src/services/GitHubAPIClient.ts +244 -0
  88. package/src/services/GitHubAuthService.ts +188 -0
  89. package/src/services/WebhookValidator.ts +159 -0
  90. package/src/utils/error-utils.ts +148 -0
  91. package/src/utils/jwt-utils.ts +64 -0
  92. package/src/utils/state-utils.ts +72 -0
  93. package/src/utils/token-cache-utils.ts +89 -0
  94. package/src/utils/webhook-signature-utils.ts +57 -0
  95. package/tsconfig.dist.json +4 -0
  96. package/tsconfig.json +24 -0
@@ -0,0 +1,367 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ var __importDefault = (this && this.__importDefault) || function (mod) {
26
+ return (mod && mod.__esModule) ? mod : { "default": mod };
27
+ };
28
+ Object.defineProperty(exports, "__esModule", { value: true });
29
+ exports.githubAppPlugin = void 0;
30
+ const flink_1 = require("@flink-app/flink");
31
+ const GitHubAppSessionRepo_1 = __importDefault(require("./repos/GitHubAppSessionRepo"));
32
+ const GitHubInstallationRepo_1 = __importDefault(require("./repos/GitHubInstallationRepo"));
33
+ const GitHubWebhookEventRepo_1 = __importDefault(require("./repos/GitHubWebhookEventRepo"));
34
+ const GitHubAuthService_1 = require("./services/GitHubAuthService");
35
+ const GitHubAPIClient_1 = require("./services/GitHubAPIClient");
36
+ const WebhookValidator_1 = require("./services/WebhookValidator");
37
+ const error_utils_1 = require("./utils/error-utils");
38
+ const state_utils_1 = require("./utils/state-utils");
39
+ const InstallationCallback = __importStar(require("./handlers/InstallationCallback"));
40
+ const WebhookHandler = __importStar(require("./handlers/WebhookHandler"));
41
+ /**
42
+ * GitHub App Plugin Factory Function
43
+ *
44
+ * Creates a Flink plugin for GitHub App integration with:
45
+ * - Installation management
46
+ * - JWT-based authentication with private key signing
47
+ * - Installation access token management with automatic refresh and caching
48
+ * - Webhook integration with signature validation
49
+ * - GitHub API client wrapper
50
+ *
51
+ * @param options - GitHub App plugin configuration options
52
+ * @returns FlinkPlugin instance
53
+ *
54
+ * @example
55
+ * ```typescript
56
+ * import { githubAppPlugin } from '@flink-app/github-app-plugin';
57
+ *
58
+ * const app = new FlinkApp({
59
+ * plugins: [
60
+ * githubAppPlugin({
61
+ * appId: process.env.GITHUB_APP_ID!,
62
+ * privateKey: process.env.GITHUB_APP_PRIVATE_KEY!,
63
+ * webhookSecret: process.env.GITHUB_WEBHOOK_SECRET!,
64
+ * clientId: process.env.GITHUB_APP_CLIENT_ID!,
65
+ * clientSecret: process.env.GITHUB_APP_CLIENT_SECRET!,
66
+ * onInstallationSuccess: async ({ installationId, repositories, account }, ctx) => {
67
+ * const userId = getLoggedInUserId(req); // App-defined function
68
+ * return {
69
+ * userId,
70
+ * redirectUrl: '/dashboard/repos'
71
+ * };
72
+ * },
73
+ * onWebhookEvent: async ({ event, payload, installationId }, ctx) => {
74
+ * if (event === 'push') {
75
+ * // Process push event
76
+ * }
77
+ * }
78
+ * })
79
+ * ]
80
+ * });
81
+ * ```
82
+ */
83
+ function githubAppPlugin(options) {
84
+ // Validate required options
85
+ if (!options.appId) {
86
+ throw new Error("GitHub App Plugin: appId is required");
87
+ }
88
+ if (!options.privateKey) {
89
+ throw new Error("GitHub App Plugin: privateKey is required");
90
+ }
91
+ if (!options.webhookSecret) {
92
+ throw new Error("GitHub App Plugin: webhookSecret is required");
93
+ }
94
+ if (!options.clientId) {
95
+ throw new Error("GitHub App Plugin: clientId is required");
96
+ }
97
+ if (!options.clientSecret) {
98
+ throw new Error("GitHub App Plugin: clientSecret is required");
99
+ }
100
+ if (!options.onInstallationSuccess) {
101
+ throw new Error("GitHub App Plugin: onInstallationSuccess callback is required");
102
+ }
103
+ // Determine configuration defaults
104
+ const baseUrl = options.baseUrl || "https://api.github.com";
105
+ const tokenCacheTTL = options.tokenCacheTTL || 3300; // 55 minutes
106
+ const sessionTTL = options.sessionTTL || 600; // 10 minutes
107
+ const registerRoutes = options.registerRoutes !== false; // default true
108
+ const logWebhookEvents = options.logWebhookEvents || false; // default false
109
+ let flinkApp;
110
+ let authService;
111
+ let webhookValidator;
112
+ let sessionRepo;
113
+ let installationRepo;
114
+ let webhookEventRepo;
115
+ /**
116
+ * Plugin initialization
117
+ */
118
+ async function init(app, db) {
119
+ flink_1.log.info("Initializing GitHub App Plugin...");
120
+ flinkApp = app;
121
+ try {
122
+ if (!db) {
123
+ throw new Error("GitHub App Plugin: Database connection is required");
124
+ }
125
+ // Initialize GitHubAuthService with private key validation
126
+ // This will throw early if the private key is invalid
127
+ try {
128
+ authService = new GitHubAuthService_1.GitHubAuthService(options.appId, options.privateKey, baseUrl, tokenCacheTTL);
129
+ flink_1.log.info("GitHub App Plugin: Successfully validated private key and generated test JWT");
130
+ }
131
+ catch (error) {
132
+ flink_1.log.error("GitHub App Plugin: Failed to initialize auth service", error);
133
+ throw error;
134
+ }
135
+ // Initialize WebhookValidator
136
+ webhookValidator = new WebhookValidator_1.WebhookValidator(options.webhookSecret);
137
+ // Initialize repositories
138
+ const sessionsCollectionName = options.sessionsCollectionName || "github_app_sessions";
139
+ const installationsCollectionName = options.installationsCollectionName || "github_installations";
140
+ const webhookEventsCollectionName = options.webhookEventsCollectionName || "github_webhook_events";
141
+ sessionRepo = new GitHubAppSessionRepo_1.default(flinkApp.ctx, db, sessionsCollectionName);
142
+ installationRepo = new GitHubInstallationRepo_1.default(flinkApp.ctx, db, installationsCollectionName);
143
+ // Add repositories to FlinkApp
144
+ flinkApp.addRepo("githubAppSessionRepo", sessionRepo);
145
+ flinkApp.addRepo("githubInstallationRepo", installationRepo);
146
+ // Conditionally initialize webhook event repo if logging is enabled
147
+ if (logWebhookEvents) {
148
+ webhookEventRepo = new GitHubWebhookEventRepo_1.default(flinkApp.ctx, db, webhookEventsCollectionName);
149
+ flinkApp.addRepo("githubWebhookEventRepo", webhookEventRepo);
150
+ flink_1.log.info(`GitHub App Plugin: Webhook event logging enabled (collection: ${webhookEventsCollectionName})`);
151
+ }
152
+ // Create TTL indexes
153
+ // Sessions TTL index for automatic cleanup
154
+ await db.collection(sessionsCollectionName).createIndex({ createdAt: 1 }, { expireAfterSeconds: sessionTTL });
155
+ flink_1.log.info(`GitHub App Plugin: Created TTL index on ${sessionsCollectionName} with ${sessionTTL}s expiration`);
156
+ // Webhook events TTL index if logging enabled
157
+ if (logWebhookEvents && webhookEventsCollectionName) {
158
+ // Optional TTL for webhook events (default: 30 days)
159
+ const webhookEventTTL = 30 * 24 * 60 * 60; // 30 days in seconds
160
+ await db.collection(webhookEventsCollectionName).createIndex({ createdAt: 1 }, { expireAfterSeconds: webhookEventTTL });
161
+ flink_1.log.info(`GitHub App Plugin: Created TTL index on ${webhookEventsCollectionName} with ${webhookEventTTL}s expiration`);
162
+ }
163
+ // Conditionally register handlers (only GitHub-required handlers)
164
+ if (registerRoutes) {
165
+ flinkApp.addHandler(InstallationCallback);
166
+ flinkApp.addHandler(WebhookHandler);
167
+ flink_1.log.info("GitHub App Plugin: Registered handlers (callback and webhook)");
168
+ }
169
+ else {
170
+ flink_1.log.info("GitHub App Plugin: Skipped handler registration (routes disabled)");
171
+ }
172
+ flink_1.log.info(`GitHub App Plugin initialized successfully`);
173
+ flink_1.log.info(` - App ID: ${options.appId}`);
174
+ flink_1.log.info(` - Base URL: ${baseUrl}`);
175
+ flink_1.log.info(` - Token Cache TTL: ${tokenCacheTTL}s`);
176
+ flink_1.log.info(` - Session TTL: ${sessionTTL}s`);
177
+ flink_1.log.info(` - Routes Registered: ${registerRoutes}`);
178
+ flink_1.log.info(` - Webhook Logging: ${logWebhookEvents}`);
179
+ }
180
+ catch (error) {
181
+ flink_1.log.error("Failed to initialize GitHub App Plugin:", error);
182
+ throw error;
183
+ }
184
+ }
185
+ /**
186
+ * Initiates GitHub App installation flow
187
+ */
188
+ async function initiateInstallation(params) {
189
+ if (!sessionRepo) {
190
+ throw new Error("GitHub App Plugin: Plugin not initialized");
191
+ }
192
+ // Validate that appSlug is configured
193
+ if (!options.appSlug) {
194
+ throw new Error("GitHub App Plugin: appSlug is required for installation flow. Please set appSlug in plugin options.");
195
+ }
196
+ // Generate cryptographically secure state and session ID
197
+ const state = (0, state_utils_1.generateState)();
198
+ const sessionId = (0, state_utils_1.generateSessionId)();
199
+ // Store session for state validation in callback
200
+ await sessionRepo.create({
201
+ sessionId,
202
+ state,
203
+ userId: params.userId,
204
+ metadata: params.metadata || {},
205
+ createdAt: new Date(),
206
+ });
207
+ // Build GitHub installation URL
208
+ const installationUrl = `https://github.com/apps/${options.appSlug}/installations/new?state=${state}`;
209
+ flink_1.log.info("GitHub App installation initiated", { userId: params.userId, sessionId });
210
+ return {
211
+ redirectUrl: installationUrl,
212
+ state,
213
+ sessionId,
214
+ };
215
+ }
216
+ /**
217
+ * Uninstalls GitHub App for a user
218
+ */
219
+ async function uninstall(params) {
220
+ if (!installationRepo || !authService) {
221
+ throw new Error("GitHub App Plugin: Plugin not initialized");
222
+ }
223
+ try {
224
+ // Find installation
225
+ const installation = await installationRepo.findByUserAndInstallationId(params.userId, params.installationId);
226
+ if (!installation) {
227
+ return { success: false, error: "installation-not-found" };
228
+ }
229
+ // Verify ownership
230
+ if (installation.userId !== params.userId) {
231
+ flink_1.log.warn("User attempted to uninstall installation they don't own", {
232
+ userId: params.userId,
233
+ installationId: params.installationId,
234
+ ownerId: installation.userId,
235
+ });
236
+ return { success: false, error: "installation-not-owned" };
237
+ }
238
+ // Delete from database
239
+ await installationRepo.deleteByInstallationId(params.installationId);
240
+ // Clear token cache
241
+ authService.deleteInstallationToken(params.installationId);
242
+ flink_1.log.info("GitHub App uninstalled", { userId: params.userId, installationId: params.installationId });
243
+ return { success: true };
244
+ }
245
+ catch (error) {
246
+ flink_1.log.error("Failed to uninstall GitHub App", {
247
+ userId: params.userId,
248
+ installationId: params.installationId,
249
+ error: error.message,
250
+ });
251
+ return { success: false, error: "uninstall-failed" };
252
+ }
253
+ }
254
+ /**
255
+ * Get GitHub API client for an installation
256
+ */
257
+ async function getClient(installationId) {
258
+ if (!authService) {
259
+ throw new Error("GitHub App Plugin: Plugin not initialized");
260
+ }
261
+ return new GitHubAPIClient_1.GitHubAPIClient(installationId, authService, baseUrl);
262
+ }
263
+ /**
264
+ * Get installation for a user (returns first installation)
265
+ */
266
+ async function getInstallation(userId) {
267
+ if (!installationRepo) {
268
+ throw new Error("GitHub App Plugin: Plugin not initialized");
269
+ }
270
+ const installations = await installationRepo.findByUserId(userId);
271
+ return installations.length > 0 ? installations[0] : null;
272
+ }
273
+ /**
274
+ * Get all installations for a user
275
+ */
276
+ async function getInstallations(userId) {
277
+ if (!installationRepo) {
278
+ throw new Error("GitHub App Plugin: Plugin not initialized");
279
+ }
280
+ return installationRepo.findByUserId(userId);
281
+ }
282
+ /**
283
+ * Delete installation
284
+ */
285
+ async function deleteInstallation(userId, installationId) {
286
+ if (!installationRepo || !authService) {
287
+ throw new Error("GitHub App Plugin: Plugin not initialized");
288
+ }
289
+ // Verify user owns the installation
290
+ const installation = await installationRepo.findByUserAndInstallationId(userId, installationId);
291
+ if (!installation) {
292
+ throw (0, error_utils_1.createGitHubAppError)(error_utils_1.GitHubAppErrorCodes.INSTALLATION_NOT_OWNED, "Installation not found or not owned by user", { userId, installationId });
293
+ }
294
+ // Delete from database
295
+ await installationRepo.deleteByInstallationId(installationId);
296
+ // Clear token cache for this installation
297
+ authService.deleteInstallationToken(installationId);
298
+ flink_1.log.info(`GitHub App Plugin: Deleted installation ${installationId} for user ${userId}`);
299
+ }
300
+ /**
301
+ * Check if user has access to specific repository
302
+ */
303
+ async function hasRepositoryAccess(userId, owner, repo) {
304
+ if (!installationRepo) {
305
+ throw new Error("GitHub App Plugin: Plugin not initialized");
306
+ }
307
+ const installations = await installationRepo.findByUserId(userId);
308
+ // Check if any installation has access to the repository
309
+ for (const installation of installations) {
310
+ // Skip suspended installations
311
+ if (installation.suspendedAt) {
312
+ continue;
313
+ }
314
+ // Check repositories array
315
+ const hasAccess = installation.repositories.some((r) => r.fullName.toLowerCase() === `${owner}/${repo}`.toLowerCase());
316
+ if (hasAccess) {
317
+ return true;
318
+ }
319
+ }
320
+ return false;
321
+ }
322
+ /**
323
+ * Get installation access token (for advanced usage)
324
+ */
325
+ async function getInstallationToken(installationId) {
326
+ if (!authService) {
327
+ throw new Error("GitHub App Plugin: Plugin not initialized");
328
+ }
329
+ return authService.getInstallationToken(installationId);
330
+ }
331
+ /**
332
+ * Clear token cache
333
+ */
334
+ function clearTokenCache() {
335
+ if (!authService) {
336
+ throw new Error("GitHub App Plugin: Plugin not initialized");
337
+ }
338
+ authService.clearTokenCache();
339
+ flink_1.log.info("GitHub App Plugin: Cleared token cache");
340
+ }
341
+ /**
342
+ * Plugin context exposed via ctx.plugins.githubApp
343
+ */
344
+ const pluginCtx = {
345
+ initiateInstallation,
346
+ uninstall,
347
+ getClient,
348
+ getInstallation,
349
+ getInstallations,
350
+ deleteInstallation,
351
+ hasRepositoryAccess,
352
+ getInstallationToken,
353
+ clearTokenCache,
354
+ options: Object.freeze({ ...options }),
355
+ get authService() { return authService; },
356
+ get webhookValidator() { return webhookValidator; },
357
+ };
358
+ return {
359
+ id: "githubApp",
360
+ db: {
361
+ useHostDb: true,
362
+ },
363
+ ctx: pluginCtx,
364
+ init,
365
+ };
366
+ }
367
+ exports.githubAppPlugin = githubAppPlugin;
@@ -0,0 +1,242 @@
1
+ import { GitHubAPIClient } from "./services/GitHubAPIClient";
2
+ import GitHubInstallation from "./schemas/GitHubInstallation";
3
+ import { GitHubAppPluginOptions } from "./GitHubAppPluginOptions";
4
+ import { GitHubAuthService } from "./services/GitHubAuthService";
5
+ import { WebhookValidator } from "./services/WebhookValidator";
6
+ /**
7
+ * Public context interface exposed via ctx.plugins.githubApp
8
+ *
9
+ * Provides methods for interacting with GitHub App installations,
10
+ * managing installation access, and creating authenticated API clients.
11
+ */
12
+ export interface GitHubAppPluginContext {
13
+ githubApp: {
14
+ /**
15
+ * Initiates GitHub App installation flow
16
+ *
17
+ * Creates a session with CSRF protection and returns the installation URL.
18
+ * Call this from your own handler with your own auth logic.
19
+ *
20
+ * @param params - Installation parameters
21
+ * @param params.userId - Application user ID to link installation to
22
+ * @param params.redirectUrl - Optional URL to redirect to after installation (deprecated, use onInstallationSuccess callback)
23
+ * @param params.metadata - Optional metadata to store in session
24
+ * @returns Installation flow details
25
+ *
26
+ * @example
27
+ * ```typescript
28
+ * // In your custom handler
29
+ * export default async function InitiateGitHubInstall({ ctx }: GetHandlerParams) {
30
+ * const userId = ctx.auth.tokenData.userId;
31
+ *
32
+ * const { redirectUrl } = await ctx.plugins.githubApp.initiateInstallation({
33
+ * userId,
34
+ * metadata: { source: 'settings' }
35
+ * });
36
+ *
37
+ * return {
38
+ * status: 302,
39
+ * headers: { Location: redirectUrl }
40
+ * };
41
+ * }
42
+ * ```
43
+ */
44
+ initiateInstallation(params: {
45
+ userId: string;
46
+ redirectUrl?: string;
47
+ metadata?: Record<string, any>;
48
+ }): Promise<{
49
+ redirectUrl: string;
50
+ state: string;
51
+ sessionId: string;
52
+ }>;
53
+ /**
54
+ * Uninstalls GitHub App for a user
55
+ *
56
+ * Removes installation from database and clears cached token.
57
+ * Call this from your own handler with your own auth logic.
58
+ *
59
+ * @param params - Uninstall parameters
60
+ * @param params.userId - Application user ID requesting uninstall
61
+ * @param params.installationId - GitHub installation ID to uninstall
62
+ * @returns Uninstall result
63
+ *
64
+ * @example
65
+ * ```typescript
66
+ * // In your custom handler
67
+ * export default async function UninstallGitHub({ ctx, params }: DeleteHandlerParams) {
68
+ * const userId = ctx.auth.tokenData.userId;
69
+ *
70
+ * const result = await ctx.plugins.githubApp.uninstall({
71
+ * userId,
72
+ * installationId: params.installationId
73
+ * });
74
+ *
75
+ * if (!result.success) {
76
+ * return badRequest(result.error);
77
+ * }
78
+ *
79
+ * return { status: 204 };
80
+ * }
81
+ * ```
82
+ */
83
+ uninstall(params: {
84
+ userId: string;
85
+ installationId: number;
86
+ }): Promise<{
87
+ success: boolean;
88
+ error?: string;
89
+ }>;
90
+ /**
91
+ * Get GitHub API client for an installation
92
+ *
93
+ * Creates an authenticated API client with automatic token injection.
94
+ * The client handles token caching, refresh, and retry logic.
95
+ *
96
+ * @param installationId - GitHub installation ID
97
+ * @returns GitHub API client instance
98
+ *
99
+ * @example
100
+ * ```typescript
101
+ * const client = await ctx.plugins.githubApp.getClient(12345);
102
+ * const repos = await client.getRepositories();
103
+ * ```
104
+ */
105
+ getClient(installationId: number): Promise<GitHubAPIClient>;
106
+ /**
107
+ * Get installation for a user
108
+ *
109
+ * Returns the first installation found for the user.
110
+ * Use getInstallations() if a user may have multiple installations.
111
+ *
112
+ * @param userId - Application user ID
113
+ * @returns Installation if found, null otherwise
114
+ *
115
+ * @example
116
+ * ```typescript
117
+ * const installation = await ctx.plugins.githubApp.getInstallation('user-123');
118
+ * if (installation) {
119
+ * console.log('Installed on:', installation.accountLogin);
120
+ * }
121
+ * ```
122
+ */
123
+ getInstallation(userId: string): Promise<GitHubInstallation | null>;
124
+ /**
125
+ * Get all installations for a user
126
+ *
127
+ * Returns all GitHub App installations linked to the user.
128
+ * Users can install the app on multiple accounts (personal + organizations).
129
+ *
130
+ * @param userId - Application user ID
131
+ * @returns Array of installations
132
+ *
133
+ * @example
134
+ * ```typescript
135
+ * const installations = await ctx.plugins.githubApp.getInstallations('user-123');
136
+ * installations.forEach(installation => {
137
+ * console.log(`Installed on ${installation.accountLogin} (${installation.accountType})`);
138
+ * });
139
+ * ```
140
+ */
141
+ getInstallations(userId: string): Promise<GitHubInstallation[]>;
142
+ /**
143
+ * Delete installation
144
+ *
145
+ * Removes an installation from the database and clears its cached token.
146
+ * Verifies the user owns the installation before deletion.
147
+ *
148
+ * @param userId - Application user ID
149
+ * @param installationId - GitHub installation ID
150
+ * @throws Error if user doesn't own installation
151
+ *
152
+ * @example
153
+ * ```typescript
154
+ * await ctx.plugins.githubApp.deleteInstallation('user-123', 12345);
155
+ * ```
156
+ */
157
+ deleteInstallation(userId: string, installationId: number): Promise<void>;
158
+ /**
159
+ * Check if user has access to specific repository
160
+ *
161
+ * Verifies that the user has an installation with access to the
162
+ * specified repository. Useful for authorization checks.
163
+ *
164
+ * @param userId - Application user ID
165
+ * @param owner - Repository owner (username or org)
166
+ * @param repo - Repository name
167
+ * @returns true if user has access, false otherwise
168
+ *
169
+ * @example
170
+ * ```typescript
171
+ * const hasAccess = await ctx.plugins.githubApp.hasRepositoryAccess(
172
+ * 'user-123',
173
+ * 'facebook',
174
+ * 'react'
175
+ * );
176
+ *
177
+ * if (!hasAccess) {
178
+ * return forbidden('You do not have access to this repository');
179
+ * }
180
+ * ```
181
+ */
182
+ hasRepositoryAccess(userId: string, owner: string, repo: string): Promise<boolean>;
183
+ /**
184
+ * Get installation access token (for advanced usage)
185
+ *
186
+ * Returns a raw installation access token. Tokens are cached
187
+ * and automatically refreshed when expired.
188
+ *
189
+ * Most users should use getClient() instead, which handles
190
+ * token injection automatically.
191
+ *
192
+ * @param installationId - GitHub installation ID
193
+ * @returns Installation access token
194
+ *
195
+ * @example
196
+ * ```typescript
197
+ * const token = await ctx.plugins.githubApp.getInstallationToken(12345);
198
+ * // Make custom API call with token
199
+ * ```
200
+ */
201
+ getInstallationToken(installationId: number): Promise<string>;
202
+ /**
203
+ * Clear token cache
204
+ *
205
+ * Removes all cached installation tokens. Next API call will
206
+ * fetch fresh tokens from GitHub.
207
+ *
208
+ * Useful for testing, forcing token refresh, or plugin shutdown.
209
+ *
210
+ * @example
211
+ * ```typescript
212
+ * ctx.plugins.githubApp.clearTokenCache();
213
+ * ```
214
+ */
215
+ clearTokenCache(): void;
216
+ /**
217
+ * Plugin configuration (read-only)
218
+ *
219
+ * Provides access to plugin options for custom logic.
220
+ * Object is frozen to prevent modifications.
221
+ */
222
+ options: Readonly<GitHubAppPluginOptions>;
223
+ /**
224
+ * GitHub Authentication Service
225
+ *
226
+ * @internal
227
+ * Used internally by plugin handlers. Most applications should not need to access this directly.
228
+ *
229
+ * Provides JWT generation and installation token management.
230
+ */
231
+ authService: GitHubAuthService;
232
+ /**
233
+ * Webhook Validator
234
+ *
235
+ * @internal
236
+ * Used internally by plugin handlers. Most applications should not need to access this directly.
237
+ *
238
+ * Validates webhook signatures and parses webhook payloads.
239
+ */
240
+ webhookValidator: WebhookValidator;
241
+ };
242
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });