latchkey 0.1.0
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/.nvmrc +1 -0
- package/.pre-commit-config.yaml +22 -0
- package/.prettierignore +4 -0
- package/.prettierrc +7 -0
- package/CLAUDE.md +13 -0
- package/LICENSE +7 -0
- package/README.md +167 -0
- package/dist/scripts/cryptFile.d.ts +21 -0
- package/dist/scripts/cryptFile.d.ts.map +1 -0
- package/dist/scripts/cryptFile.js +106 -0
- package/dist/scripts/cryptFile.js.map +1 -0
- package/dist/scripts/encryptFile.d.ts +21 -0
- package/dist/scripts/encryptFile.d.ts.map +1 -0
- package/dist/scripts/encryptFile.js +101 -0
- package/dist/scripts/encryptFile.js.map +1 -0
- package/dist/scripts/recordBrowserSession.d.ts +18 -0
- package/dist/scripts/recordBrowserSession.d.ts.map +1 -0
- package/dist/scripts/recordBrowserSession.js +213 -0
- package/dist/scripts/recordBrowserSession.js.map +1 -0
- package/dist/src/apiCredentialStore.d.ts +19 -0
- package/dist/src/apiCredentialStore.d.ts.map +1 -0
- package/dist/src/apiCredentialStore.js +65 -0
- package/dist/src/apiCredentialStore.js.map +1 -0
- package/dist/src/apiCredentials.d.ts +134 -0
- package/dist/src/apiCredentials.d.ts.map +1 -0
- package/dist/src/apiCredentials.js +139 -0
- package/dist/src/apiCredentials.js.map +1 -0
- package/dist/src/browserConfig.d.ts +90 -0
- package/dist/src/browserConfig.d.ts.map +1 -0
- package/dist/src/browserConfig.js +259 -0
- package/dist/src/browserConfig.js.map +1 -0
- package/dist/src/browserState.d.ts +8 -0
- package/dist/src/browserState.d.ts.map +1 -0
- package/dist/src/browserState.js +21 -0
- package/dist/src/browserState.js.map +1 -0
- package/dist/src/cli.d.ts +6 -0
- package/dist/src/cli.d.ts.map +1 -0
- package/dist/src/cli.js +25 -0
- package/dist/src/cli.js.map +1 -0
- package/dist/src/cliCommands.d.ts +29 -0
- package/dist/src/cliCommands.d.ts.map +1 -0
- package/dist/src/cliCommands.js +264 -0
- package/dist/src/cliCommands.js.map +1 -0
- package/dist/src/config.d.ts +35 -0
- package/dist/src/config.d.ts.map +1 -0
- package/dist/src/config.js +96 -0
- package/dist/src/config.js.map +1 -0
- package/dist/src/curl.d.ts +29 -0
- package/dist/src/curl.d.ts.map +1 -0
- package/dist/src/curl.js +53 -0
- package/dist/src/curl.js.map +1 -0
- package/dist/src/encryptedStorage.d.ts +39 -0
- package/dist/src/encryptedStorage.d.ts.map +1 -0
- package/dist/src/encryptedStorage.js +128 -0
- package/dist/src/encryptedStorage.js.map +1 -0
- package/dist/src/encryption.d.ts +28 -0
- package/dist/src/encryption.d.ts.map +1 -0
- package/dist/src/encryption.js +86 -0
- package/dist/src/encryption.js.map +1 -0
- package/dist/src/index.d.ts +14 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +17 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/keychain.d.ts +33 -0
- package/dist/src/keychain.d.ts.map +1 -0
- package/dist/src/keychain.js +94 -0
- package/dist/src/keychain.js.map +1 -0
- package/dist/src/playwrightUtils.d.ts +27 -0
- package/dist/src/playwrightUtils.d.ts.map +1 -0
- package/dist/src/playwrightUtils.js +122 -0
- package/dist/src/playwrightUtils.js.map +1 -0
- package/dist/src/registry.d.ts +12 -0
- package/dist/src/registry.d.ts.map +1 -0
- package/dist/src/registry.js +30 -0
- package/dist/src/registry.js.map +1 -0
- package/dist/src/services/base.d.ts +98 -0
- package/dist/src/services/base.d.ts.map +1 -0
- package/dist/src/services/base.js +137 -0
- package/dist/src/services/base.js.map +1 -0
- package/dist/src/services/discord.d.ts +20 -0
- package/dist/src/services/discord.d.ts.map +1 -0
- package/dist/src/services/discord.js +55 -0
- package/dist/src/services/discord.js.map +1 -0
- package/dist/src/services/dropbox.d.ts +23 -0
- package/dist/src/services/dropbox.d.ts.map +1 -0
- package/dist/src/services/dropbox.js +136 -0
- package/dist/src/services/dropbox.js.map +1 -0
- package/dist/src/services/github.d.ts +23 -0
- package/dist/src/services/github.d.ts.map +1 -0
- package/dist/src/services/github.js +110 -0
- package/dist/src/services/github.js.map +1 -0
- package/dist/src/services/index.d.ts +12 -0
- package/dist/src/services/index.d.ts.map +1 -0
- package/dist/src/services/index.js +11 -0
- package/dist/src/services/index.js.map +1 -0
- package/dist/src/services/linear.d.ts +23 -0
- package/dist/src/services/linear.d.ts.map +1 -0
- package/dist/src/services/linear.js +110 -0
- package/dist/src/services/linear.js.map +1 -0
- package/dist/src/services/slack.d.ts +21 -0
- package/dist/src/services/slack.d.ts.map +1 -0
- package/dist/src/services/slack.js +67 -0
- package/dist/src/services/slack.js.map +1 -0
- package/dist/tests/apiCredentialStore.test.d.ts +2 -0
- package/dist/tests/apiCredentialStore.test.d.ts.map +1 -0
- package/dist/tests/apiCredentialStore.test.js +130 -0
- package/dist/tests/apiCredentialStore.test.js.map +1 -0
- package/dist/tests/apiCredentials.test.d.ts +2 -0
- package/dist/tests/apiCredentials.test.d.ts.map +1 -0
- package/dist/tests/apiCredentials.test.js +169 -0
- package/dist/tests/apiCredentials.test.js.map +1 -0
- package/dist/tests/cli.test.d.ts +2 -0
- package/dist/tests/cli.test.d.ts.map +1 -0
- package/dist/tests/cli.test.js +584 -0
- package/dist/tests/cli.test.js.map +1 -0
- package/dist/tests/encryptedStorage.test.d.ts +2 -0
- package/dist/tests/encryptedStorage.test.d.ts.map +1 -0
- package/dist/tests/encryptedStorage.test.js +126 -0
- package/dist/tests/encryptedStorage.test.js.map +1 -0
- package/dist/tests/encryption.test.d.ts +2 -0
- package/dist/tests/encryption.test.d.ts.map +1 -0
- package/dist/tests/encryption.test.js +121 -0
- package/dist/tests/encryption.test.js.map +1 -0
- package/dist/tests/lint.test.d.ts +2 -0
- package/dist/tests/lint.test.d.ts.map +1 -0
- package/dist/tests/lint.test.js +18 -0
- package/dist/tests/lint.test.js.map +1 -0
- package/dist/tests/registry.test.d.ts +2 -0
- package/dist/tests/registry.test.d.ts.map +1 -0
- package/dist/tests/registry.test.js +85 -0
- package/dist/tests/registry.test.js.map +1 -0
- package/dist/tests/servicesAgainstRecordings.test.d.ts +20 -0
- package/dist/tests/servicesAgainstRecordings.test.d.ts.map +1 -0
- package/dist/tests/servicesAgainstRecordings.test.js +157 -0
- package/dist/tests/servicesAgainstRecordings.test.js.map +1 -0
- package/dist/tests/typecheck.test.d.ts +2 -0
- package/dist/tests/typecheck.test.d.ts.map +1 -0
- package/dist/tests/typecheck.test.js +18 -0
- package/dist/tests/typecheck.test.js.map +1 -0
- package/docs/development.md +94 -0
- package/eslint.config.js +30 -0
- package/integrations/SKILL.md +62 -0
- package/package.json +68 -0
- package/scripts/cryptFile.ts +123 -0
- package/scripts/recordBrowserSession.ts +280 -0
- package/scripts/tsconfig.json +10 -0
- package/src/apiCredentialStore.ts +87 -0
- package/src/apiCredentials.ts +180 -0
- package/src/cli.ts +32 -0
- package/src/cliCommands.ts +321 -0
- package/src/config.ts +115 -0
- package/src/curl.ts +78 -0
- package/src/encryptedStorage.ts +161 -0
- package/src/encryption.ts +106 -0
- package/src/index.ts +65 -0
- package/src/keychain.ts +105 -0
- package/src/playwrightUtils.ts +143 -0
- package/src/registry.ts +35 -0
- package/src/services/base.ts +234 -0
- package/src/services/discord.ts +73 -0
- package/src/services/dropbox.ts +173 -0
- package/src/services/github.ts +139 -0
- package/src/services/index.ts +13 -0
- package/src/services/linear.ts +134 -0
- package/src/services/slack.ts +85 -0
- package/tests/apiCredentialStore.test.ts +162 -0
- package/tests/apiCredentials.test.ts +195 -0
- package/tests/cli.test.ts +798 -0
- package/tests/encryptedStorage.test.ts +173 -0
- package/tests/encryption.test.ts +169 -0
- package/tests/lint.test.ts +19 -0
- package/tests/registry.test.ts +103 -0
- package/tests/servicesAgainstRecordings.test.ts +230 -0
- package/tests/typecheck.test.ts +19 -0
- package/tsconfig.json +24 -0
- package/vitest.config.ts +13 -0
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
#!/usr/bin/env npx tsx
|
|
2
|
+
/**
|
|
3
|
+
* Record browser requests and responses during a login session.
|
|
4
|
+
*
|
|
5
|
+
* This script opens a browser at a service's login URL and records all HTTP
|
|
6
|
+
* requests and responses (including their headers and timing). When you close the
|
|
7
|
+
* browser, the recording is saved. This is useful for recording login flows that
|
|
8
|
+
* can be replayed later for testing credentials extraction.
|
|
9
|
+
*
|
|
10
|
+
* Usage:
|
|
11
|
+
* npx tsx scripts/recordBrowserSession.ts <service_name> [recording_name]
|
|
12
|
+
*
|
|
13
|
+
* Examples:
|
|
14
|
+
* npx tsx scripts/recordBrowserSession.ts slack
|
|
15
|
+
* npx tsx scripts/recordBrowserSession.ts discord custom_session.json
|
|
16
|
+
*/
|
|
17
|
+
import { mkdirSync, writeFileSync } from 'node:fs';
|
|
18
|
+
import { dirname, join, resolve } from 'node:path';
|
|
19
|
+
import { fileURLToPath } from 'node:url';
|
|
20
|
+
import { CONFIG } from '../src/config.js';
|
|
21
|
+
import { EncryptedStorage } from '../src/encryptedStorage.js';
|
|
22
|
+
import { withTempBrowserContext } from '../src/playwrightUtils.js';
|
|
23
|
+
import { REGISTRY } from '../src/registry.js';
|
|
24
|
+
// Get the directory of this file
|
|
25
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
26
|
+
const __dirname = dirname(__filename);
|
|
27
|
+
// Recordings directory relative to this script
|
|
28
|
+
const RECORDINGS_DIRECTORY = resolve(__dirname, 'recordings');
|
|
29
|
+
// Default recording filename
|
|
30
|
+
const DEFAULT_RECORDING_NAME = 'login_session.json';
|
|
31
|
+
class UnknownServiceError extends Error {
|
|
32
|
+
constructor(serviceName) {
|
|
33
|
+
super(`Unknown service: ${serviceName}`);
|
|
34
|
+
this.name = 'UnknownServiceError';
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
// Resource types to skip (CSS, images, fonts, multimedia)
|
|
38
|
+
const SKIPPED_RESOURCE_TYPES = new Set(['stylesheet', 'image', 'media', 'font']);
|
|
39
|
+
// Common multi-part TLDs
|
|
40
|
+
const MULTI_PART_TLDS = new Set(['co.uk', 'com.au', 'co.nz', 'co.jp', 'com.br', 'co.in']);
|
|
41
|
+
/**
|
|
42
|
+
* Extract the base domain from a URL.
|
|
43
|
+
*
|
|
44
|
+
* For example:
|
|
45
|
+
* https://discord.com/login -> discord.com
|
|
46
|
+
* https://api.discord.com/v9/users -> discord.com
|
|
47
|
+
* https://www.example.co.uk/page -> example.co.uk
|
|
48
|
+
*/
|
|
49
|
+
function extractBaseDomain(url) {
|
|
50
|
+
let hostname;
|
|
51
|
+
try {
|
|
52
|
+
hostname = new URL(url).hostname;
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
return '';
|
|
56
|
+
}
|
|
57
|
+
// Split the hostname into parts
|
|
58
|
+
const parts = hostname.split('.');
|
|
59
|
+
// Handle common multi-part TLDs (e.g., co.uk, com.au)
|
|
60
|
+
if (parts.length >= 3) {
|
|
61
|
+
const potentialTld = parts.slice(-2).join('.');
|
|
62
|
+
if (MULTI_PART_TLDS.has(potentialTld)) {
|
|
63
|
+
return parts.slice(-3).join('.');
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
if (parts.length >= 2) {
|
|
67
|
+
return parts.slice(-2).join('.');
|
|
68
|
+
}
|
|
69
|
+
return hostname;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Check if a request URL belongs to the same base domain.
|
|
73
|
+
*/
|
|
74
|
+
function isSameBaseDomain(requestUrl, baseDomain) {
|
|
75
|
+
let requestHostname;
|
|
76
|
+
try {
|
|
77
|
+
requestHostname = new URL(requestUrl).hostname;
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
return requestHostname === baseDomain || requestHostname.endsWith('.' + baseDomain);
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Handle a response and record both request and response details.
|
|
86
|
+
*/
|
|
87
|
+
async function handleResponse(response, recordedEntries, startTime, baseDomain) {
|
|
88
|
+
const request = response.request();
|
|
89
|
+
// Skip CSS, images, fonts, and multimedia
|
|
90
|
+
if (SKIPPED_RESOURCE_TYPES.has(request.resourceType())) {
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
// Skip requests to external domains
|
|
94
|
+
if (!isSameBaseDomain(request.url(), baseDomain)) {
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
if (startTime.value === 0) {
|
|
98
|
+
startTime.value = Date.now();
|
|
99
|
+
}
|
|
100
|
+
const timestampMs = Date.now() - startTime.value;
|
|
101
|
+
const requestData = {
|
|
102
|
+
timestamp_ms: timestampMs,
|
|
103
|
+
method: request.method(),
|
|
104
|
+
url: request.url(),
|
|
105
|
+
headers: await request.allHeaders(),
|
|
106
|
+
resource_type: request.resourceType(),
|
|
107
|
+
};
|
|
108
|
+
// Include POST data if present
|
|
109
|
+
try {
|
|
110
|
+
const postData = request.postData();
|
|
111
|
+
if (postData !== null) {
|
|
112
|
+
requestData.post_data = postData;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
catch {
|
|
116
|
+
// Post data not available or not decodable
|
|
117
|
+
}
|
|
118
|
+
const responseData = {
|
|
119
|
+
status: response.status(),
|
|
120
|
+
status_text: response.statusText(),
|
|
121
|
+
headers: await response.allHeaders(),
|
|
122
|
+
};
|
|
123
|
+
// Try to get response body as text (skip binary content)
|
|
124
|
+
try {
|
|
125
|
+
const body = await response.text();
|
|
126
|
+
responseData.body = body;
|
|
127
|
+
}
|
|
128
|
+
catch {
|
|
129
|
+
// Binary content or other error - skip body
|
|
130
|
+
}
|
|
131
|
+
recordedEntries.push({
|
|
132
|
+
request: requestData,
|
|
133
|
+
response: responseData,
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Record browser requests and responses during a login session.
|
|
138
|
+
*/
|
|
139
|
+
async function record(serviceName, recordingName = DEFAULT_RECORDING_NAME) {
|
|
140
|
+
const service = REGISTRY.getByName(serviceName);
|
|
141
|
+
if (service === null) {
|
|
142
|
+
throw new UnknownServiceError(serviceName);
|
|
143
|
+
}
|
|
144
|
+
const outputDirectory = join(RECORDINGS_DIRECTORY, serviceName);
|
|
145
|
+
mkdirSync(outputDirectory, { recursive: true });
|
|
146
|
+
const requestsPath = join(outputDirectory, recordingName);
|
|
147
|
+
const browserStatePath = CONFIG.browserStatePath;
|
|
148
|
+
const baseDomain = extractBaseDomain(service.loginUrl);
|
|
149
|
+
console.log(`Recording login for service: ${service.name}`);
|
|
150
|
+
console.log(`Login URL: ${service.loginUrl}`);
|
|
151
|
+
console.log(`Recording requests to: ${baseDomain} (and subdomains)`);
|
|
152
|
+
console.log(`Output directory: ${outputDirectory}`);
|
|
153
|
+
console.log(`Browser state: ${browserStatePath}`);
|
|
154
|
+
console.log("\nClose the browser window when you're done to save the recording.");
|
|
155
|
+
const recordedEntries = [];
|
|
156
|
+
const startTime = { value: 0 };
|
|
157
|
+
const encryptedStorage = new EncryptedStorage({
|
|
158
|
+
encryptionKeyOverride: CONFIG.encryptionKeyOverride,
|
|
159
|
+
serviceName: CONFIG.serviceName,
|
|
160
|
+
accountName: CONFIG.accountName,
|
|
161
|
+
});
|
|
162
|
+
await withTempBrowserContext(encryptedStorage, browserStatePath, async ({ context }) => {
|
|
163
|
+
const page = await context.newPage();
|
|
164
|
+
// Register response handler to capture all requests and responses
|
|
165
|
+
page.on('response', (response) => {
|
|
166
|
+
handleResponse(response, recordedEntries, startTime, baseDomain).catch(() => {
|
|
167
|
+
// Ignore errors in response handling
|
|
168
|
+
});
|
|
169
|
+
});
|
|
170
|
+
await page.goto(service.loginUrl);
|
|
171
|
+
// Wait for user to close the browser
|
|
172
|
+
await page.waitForEvent('close', { timeout: 0 });
|
|
173
|
+
});
|
|
174
|
+
// Save recorded entries
|
|
175
|
+
writeFileSync(requestsPath, JSON.stringify(recordedEntries, null, 2));
|
|
176
|
+
console.log('\nRecording saved successfully!');
|
|
177
|
+
console.log(` Requests file: ${requestsPath}`);
|
|
178
|
+
console.log(` Recorded ${String(recordedEntries.length)} request/response pairs`);
|
|
179
|
+
}
|
|
180
|
+
// Main entry point
|
|
181
|
+
async function main() {
|
|
182
|
+
const args = process.argv.slice(2);
|
|
183
|
+
if (args.length === 0 || args[0] === '--help' || args[0] === '-h') {
|
|
184
|
+
console.log('Usage: npx tsx scripts/recordBrowserSession.ts <service_name> [recording_name]');
|
|
185
|
+
console.log('');
|
|
186
|
+
console.log('Arguments:');
|
|
187
|
+
console.log(" service_name Name of the service to record login for (e.g., 'slack', 'discord')");
|
|
188
|
+
console.log(' recording_name Name of the recording file (default: login_session.json)');
|
|
189
|
+
console.log('');
|
|
190
|
+
console.log('Examples:');
|
|
191
|
+
console.log(' npx tsx scripts/recordBrowserSession.ts slack');
|
|
192
|
+
console.log(' npx tsx scripts/recordBrowserSession.ts discord custom_session.json');
|
|
193
|
+
process.exit(0);
|
|
194
|
+
}
|
|
195
|
+
const serviceName = args[0];
|
|
196
|
+
const recordingName = args[1] ?? DEFAULT_RECORDING_NAME;
|
|
197
|
+
try {
|
|
198
|
+
await record(serviceName, recordingName);
|
|
199
|
+
}
|
|
200
|
+
catch (error) {
|
|
201
|
+
if (error instanceof UnknownServiceError) {
|
|
202
|
+
console.error(`Error: ${error.message}`);
|
|
203
|
+
console.error('Available services:');
|
|
204
|
+
for (const service of REGISTRY.services) {
|
|
205
|
+
console.error(` - ${service.name}`);
|
|
206
|
+
}
|
|
207
|
+
process.exit(1);
|
|
208
|
+
}
|
|
209
|
+
throw error;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
void main();
|
|
213
|
+
//# sourceMappingURL=recordBrowserSession.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recordBrowserSession.js","sourceRoot":"","sources":["../../scripts/recordBrowserSession.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* API credential store for persisting and loading API credentials.
|
|
3
|
+
*/
|
|
4
|
+
import { ApiCredentials } from './apiCredentials.js';
|
|
5
|
+
import { EncryptedStorage } from './encryptedStorage.js';
|
|
6
|
+
export declare class ApiCredentialStoreError extends Error {
|
|
7
|
+
constructor(message: string);
|
|
8
|
+
}
|
|
9
|
+
export declare class ApiCredentialStore {
|
|
10
|
+
readonly path: string;
|
|
11
|
+
private readonly encryptedStorage;
|
|
12
|
+
constructor(path: string, encryptedStorage: EncryptedStorage);
|
|
13
|
+
private loadStoreData;
|
|
14
|
+
private saveStoreData;
|
|
15
|
+
get(serviceName: string): ApiCredentials | null;
|
|
16
|
+
save(serviceName: string, apiCredentials: ApiCredentials): void;
|
|
17
|
+
delete(serviceName: string): boolean;
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=apiCredentialStore.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"apiCredentialStore.d.ts","sourceRoot":"","sources":["../../src/apiCredentialStore.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EACL,cAAc,EAIf,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAEzD,qBAAa,uBAAwB,SAAQ,KAAK;gBACpC,OAAO,EAAE,MAAM;CAI5B;AAID,qBAAa,kBAAkB;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAmB;gBAExC,IAAI,EAAE,MAAM,EAAE,gBAAgB,EAAE,gBAAgB;IAK5D,OAAO,CAAC,aAAa;IAcrB,OAAO,CAAC,aAAa;IAUrB,GAAG,CAAC,WAAW,EAAE,MAAM,GAAG,cAAc,GAAG,IAAI;IAiB/C,IAAI,CAAC,WAAW,EAAE,MAAM,EAAE,cAAc,EAAE,cAAc,GAAG,IAAI;IAM/D,MAAM,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO;CASrC"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* API credential store for persisting and loading API credentials.
|
|
3
|
+
*/
|
|
4
|
+
import { ApiCredentialsSchema, deserializeCredentials, serializeCredentials, } from './apiCredentials.js';
|
|
5
|
+
export class ApiCredentialStoreError extends Error {
|
|
6
|
+
constructor(message) {
|
|
7
|
+
super(message);
|
|
8
|
+
this.name = 'ApiCredentialStoreError';
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
export class ApiCredentialStore {
|
|
12
|
+
path;
|
|
13
|
+
encryptedStorage;
|
|
14
|
+
constructor(path, encryptedStorage) {
|
|
15
|
+
this.path = path;
|
|
16
|
+
this.encryptedStorage = encryptedStorage;
|
|
17
|
+
}
|
|
18
|
+
loadStoreData() {
|
|
19
|
+
try {
|
|
20
|
+
const content = this.encryptedStorage.readFile(this.path);
|
|
21
|
+
if (content === null) {
|
|
22
|
+
return {};
|
|
23
|
+
}
|
|
24
|
+
return JSON.parse(content);
|
|
25
|
+
}
|
|
26
|
+
catch (error) {
|
|
27
|
+
throw new ApiCredentialStoreError(`Failed to read credential store: ${error instanceof Error ? error.message : String(error)}`);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
saveStoreData(data) {
|
|
31
|
+
try {
|
|
32
|
+
this.encryptedStorage.writeFile(this.path, JSON.stringify(data, null, 2));
|
|
33
|
+
}
|
|
34
|
+
catch (error) {
|
|
35
|
+
throw new ApiCredentialStoreError(`Failed to write credential store: ${error instanceof Error ? error.message : String(error)}`);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
get(serviceName) {
|
|
39
|
+
const data = this.loadStoreData();
|
|
40
|
+
const credentialData = data[serviceName];
|
|
41
|
+
if (credentialData === undefined) {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
const parseResult = ApiCredentialsSchema.safeParse(credentialData);
|
|
45
|
+
if (!parseResult.success) {
|
|
46
|
+
throw new ApiCredentialStoreError(`Invalid credential data for service ${serviceName}: ${parseResult.error.message}`);
|
|
47
|
+
}
|
|
48
|
+
return deserializeCredentials(parseResult.data);
|
|
49
|
+
}
|
|
50
|
+
save(serviceName, apiCredentials) {
|
|
51
|
+
const data = this.loadStoreData();
|
|
52
|
+
data[serviceName] = serializeCredentials(apiCredentials);
|
|
53
|
+
this.saveStoreData(data);
|
|
54
|
+
}
|
|
55
|
+
delete(serviceName) {
|
|
56
|
+
const data = this.loadStoreData();
|
|
57
|
+
if (!(serviceName in data)) {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
const { [serviceName]: _, ...rest } = data;
|
|
61
|
+
this.saveStoreData(rest);
|
|
62
|
+
return true;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=apiCredentialStore.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"apiCredentialStore.js","sourceRoot":"","sources":["../../src/apiCredentialStore.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAEL,oBAAoB,EACpB,sBAAsB,EACtB,oBAAoB,GACrB,MAAM,qBAAqB,CAAC;AAG7B,MAAM,OAAO,uBAAwB,SAAQ,KAAK;IAChD,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,yBAAyB,CAAC;IACxC,CAAC;CACF;AAID,MAAM,OAAO,kBAAkB;IACpB,IAAI,CAAS;IACL,gBAAgB,CAAmB;IAEpD,YAAY,IAAY,EAAE,gBAAkC;QAC1D,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,gBAAgB,GAAG,gBAAgB,CAAC;IAC3C,CAAC;IAEO,aAAa;QACnB,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,IAAI,CAAC,gBAAgB,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC1D,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;gBACrB,OAAO,EAAE,CAAC;YACZ,CAAC;YACD,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAc,CAAC;QAC1C,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,IAAI,uBAAuB,CAC/B,oCAAoC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAC7F,CAAC;QACJ,CAAC;IACH,CAAC;IAEO,aAAa,CAAC,IAAe;QACnC,IAAI,CAAC;YACH,IAAI,CAAC,gBAAgB,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;QAC5E,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,IAAI,uBAAuB,CAC/B,qCAAqC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAC9F,CAAC;QACJ,CAAC;IACH,CAAC;IAED,GAAG,CAAC,WAAmB;QACrB,MAAM,IAAI,GAAG,IAAI,CAAC,aAAa,EAAE,CAAC;QAClC,MAAM,cAAc,GAAG,IAAI,CAAC,WAAW,CAAC,CAAC;QACzC,IAAI,cAAc,KAAK,SAAS,EAAE,CAAC;YACjC,OAAO,IAAI,CAAC;QACd,CAAC;QAED,MAAM,WAAW,GAAG,oBAAoB,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC;QACnE,IAAI,CAAC,WAAW,CAAC,OAAO,EAAE,CAAC;YACzB,MAAM,IAAI,uBAAuB,CAC/B,uCAAuC,WAAW,KAAK,WAAW,CAAC,KAAK,CAAC,OAAO,EAAE,CACnF,CAAC;QACJ,CAAC;QAED,OAAO,sBAAsB,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;IAClD,CAAC;IAED,IAAI,CAAC,WAAmB,EAAE,cAA8B;QACtD,MAAM,IAAI,GAAG,IAAI,CAAC,aAAa,EAAE,CAAC;QAClC,IAAI,CAAC,WAAW,CAAC,GAAG,oBAAoB,CAAC,cAAc,CAAC,CAAC;QACzD,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;IAC3B,CAAC;IAED,MAAM,CAAC,WAAmB;QACxB,MAAM,IAAI,GAAG,IAAI,CAAC,aAAa,EAAE,CAAC;QAClC,IAAI,CAAC,CAAC,WAAW,IAAI,IAAI,CAAC,EAAE,CAAC;YAC3B,OAAO,KAAK,CAAC;QACf,CAAC;QACD,MAAM,EAAE,CAAC,WAAW,CAAC,EAAE,CAAC,EAAE,GAAG,IAAI,EAAE,GAAG,IAAI,CAAC;QAC3C,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;QACzB,OAAO,IAAI,CAAC;IACd,CAAC;CACF"}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* API credentials types and utilities for authentication with various services.
|
|
3
|
+
*/
|
|
4
|
+
import { z } from 'zod';
|
|
5
|
+
export declare enum ApiCredentialStatus {
|
|
6
|
+
Missing = "missing",
|
|
7
|
+
Valid = "valid",
|
|
8
|
+
Invalid = "invalid"
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Base interface for all API credentials.
|
|
12
|
+
* Each credential type must specify how to convert itself to curl arguments.
|
|
13
|
+
*/
|
|
14
|
+
export interface ApiCredentials {
|
|
15
|
+
readonly objectType: string;
|
|
16
|
+
asCurlArguments(): readonly string[];
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Bearer token authentication (Authorization: Bearer <token>).
|
|
20
|
+
*/
|
|
21
|
+
export declare const AuthorizationBearerSchema: z.ZodObject<{
|
|
22
|
+
objectType: z.ZodLiteral<"authorizationBearer">;
|
|
23
|
+
token: z.ZodString;
|
|
24
|
+
}, "strip", z.ZodTypeAny, {
|
|
25
|
+
objectType: "authorizationBearer";
|
|
26
|
+
token: string;
|
|
27
|
+
}, {
|
|
28
|
+
objectType: "authorizationBearer";
|
|
29
|
+
token: string;
|
|
30
|
+
}>;
|
|
31
|
+
export type AuthorizationBearerData = z.infer<typeof AuthorizationBearerSchema>;
|
|
32
|
+
export declare class AuthorizationBearer implements ApiCredentials {
|
|
33
|
+
readonly objectType: "authorizationBearer";
|
|
34
|
+
readonly token: string;
|
|
35
|
+
constructor(token: string);
|
|
36
|
+
asCurlArguments(): readonly string[];
|
|
37
|
+
toJSON(): AuthorizationBearerData;
|
|
38
|
+
static fromJSON(data: AuthorizationBearerData): AuthorizationBearer;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Raw authorization header (Authorization: <token>).
|
|
42
|
+
*/
|
|
43
|
+
export declare const AuthorizationBareSchema: z.ZodObject<{
|
|
44
|
+
objectType: z.ZodLiteral<"authorizationBare">;
|
|
45
|
+
token: z.ZodString;
|
|
46
|
+
}, "strip", z.ZodTypeAny, {
|
|
47
|
+
objectType: "authorizationBare";
|
|
48
|
+
token: string;
|
|
49
|
+
}, {
|
|
50
|
+
objectType: "authorizationBare";
|
|
51
|
+
token: string;
|
|
52
|
+
}>;
|
|
53
|
+
export type AuthorizationBareData = z.infer<typeof AuthorizationBareSchema>;
|
|
54
|
+
export declare class AuthorizationBare implements ApiCredentials {
|
|
55
|
+
readonly objectType: "authorizationBare";
|
|
56
|
+
readonly token: string;
|
|
57
|
+
constructor(token: string);
|
|
58
|
+
asCurlArguments(): readonly string[];
|
|
59
|
+
toJSON(): AuthorizationBareData;
|
|
60
|
+
static fromJSON(data: AuthorizationBareData): AuthorizationBare;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Slack-specific credentials (token + d cookie).
|
|
64
|
+
*/
|
|
65
|
+
export declare const SlackApiCredentialsSchema: z.ZodObject<{
|
|
66
|
+
objectType: z.ZodLiteral<"slack">;
|
|
67
|
+
token: z.ZodString;
|
|
68
|
+
dCookie: z.ZodString;
|
|
69
|
+
}, "strip", z.ZodTypeAny, {
|
|
70
|
+
objectType: "slack";
|
|
71
|
+
token: string;
|
|
72
|
+
dCookie: string;
|
|
73
|
+
}, {
|
|
74
|
+
objectType: "slack";
|
|
75
|
+
token: string;
|
|
76
|
+
dCookie: string;
|
|
77
|
+
}>;
|
|
78
|
+
export type SlackApiCredentialsData = z.infer<typeof SlackApiCredentialsSchema>;
|
|
79
|
+
export declare class SlackApiCredentials implements ApiCredentials {
|
|
80
|
+
readonly objectType: "slack";
|
|
81
|
+
readonly token: string;
|
|
82
|
+
readonly dCookie: string;
|
|
83
|
+
constructor(token: string, dCookie: string);
|
|
84
|
+
asCurlArguments(): readonly string[];
|
|
85
|
+
toJSON(): SlackApiCredentialsData;
|
|
86
|
+
static fromJSON(data: SlackApiCredentialsData): SlackApiCredentials;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Union schema for all credential types.
|
|
90
|
+
*/
|
|
91
|
+
export declare const ApiCredentialsSchema: z.ZodDiscriminatedUnion<"objectType", [z.ZodObject<{
|
|
92
|
+
objectType: z.ZodLiteral<"authorizationBearer">;
|
|
93
|
+
token: z.ZodString;
|
|
94
|
+
}, "strip", z.ZodTypeAny, {
|
|
95
|
+
objectType: "authorizationBearer";
|
|
96
|
+
token: string;
|
|
97
|
+
}, {
|
|
98
|
+
objectType: "authorizationBearer";
|
|
99
|
+
token: string;
|
|
100
|
+
}>, z.ZodObject<{
|
|
101
|
+
objectType: z.ZodLiteral<"authorizationBare">;
|
|
102
|
+
token: z.ZodString;
|
|
103
|
+
}, "strip", z.ZodTypeAny, {
|
|
104
|
+
objectType: "authorizationBare";
|
|
105
|
+
token: string;
|
|
106
|
+
}, {
|
|
107
|
+
objectType: "authorizationBare";
|
|
108
|
+
token: string;
|
|
109
|
+
}>, z.ZodObject<{
|
|
110
|
+
objectType: z.ZodLiteral<"slack">;
|
|
111
|
+
token: z.ZodString;
|
|
112
|
+
dCookie: z.ZodString;
|
|
113
|
+
}, "strip", z.ZodTypeAny, {
|
|
114
|
+
objectType: "slack";
|
|
115
|
+
token: string;
|
|
116
|
+
dCookie: string;
|
|
117
|
+
}, {
|
|
118
|
+
objectType: "slack";
|
|
119
|
+
token: string;
|
|
120
|
+
dCookie: string;
|
|
121
|
+
}>]>;
|
|
122
|
+
export type ApiCredentialsData = z.infer<typeof ApiCredentialsSchema>;
|
|
123
|
+
/**
|
|
124
|
+
* Deserialize credentials from JSON data.
|
|
125
|
+
*/
|
|
126
|
+
export declare function deserializeCredentials(data: ApiCredentialsData): ApiCredentials;
|
|
127
|
+
/**
|
|
128
|
+
* Serialize credentials to JSON data.
|
|
129
|
+
*/
|
|
130
|
+
export declare function serializeCredentials(credentials: ApiCredentials): ApiCredentialsData;
|
|
131
|
+
export declare class ApiCredentialsSerializationError extends Error {
|
|
132
|
+
constructor(message: string);
|
|
133
|
+
}
|
|
134
|
+
//# sourceMappingURL=apiCredentials.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"apiCredentials.d.ts","sourceRoot":"","sources":["../../src/apiCredentials.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,oBAAY,mBAAmB;IAC7B,OAAO,YAAY;IACnB,KAAK,UAAU;IACf,OAAO,YAAY;CACpB;AAED;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,eAAe,IAAI,SAAS,MAAM,EAAE,CAAC;CACtC;AAED;;GAEG;AACH,eAAO,MAAM,yBAAyB;;;;;;;;;EAGpC,CAAC;AAEH,MAAM,MAAM,uBAAuB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAC;AAEhF,qBAAa,mBAAoB,YAAW,cAAc;IACxD,QAAQ,CAAC,UAAU,EAAG,qBAAqB,CAAU;IACrD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;gBAEX,KAAK,EAAE,MAAM;IAIzB,eAAe,IAAI,SAAS,MAAM,EAAE;IAIpC,MAAM,IAAI,uBAAuB;IAOjC,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE,uBAAuB,GAAG,mBAAmB;CAGpE;AAED;;GAEG;AACH,eAAO,MAAM,uBAAuB;;;;;;;;;EAGlC,CAAC;AAEH,MAAM,MAAM,qBAAqB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAE5E,qBAAa,iBAAkB,YAAW,cAAc;IACtD,QAAQ,CAAC,UAAU,EAAG,mBAAmB,CAAU;IACnD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;gBAEX,KAAK,EAAE,MAAM;IAIzB,eAAe,IAAI,SAAS,MAAM,EAAE;IAIpC,MAAM,IAAI,qBAAqB;IAO/B,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE,qBAAqB,GAAG,iBAAiB;CAGhE;AAED;;GAEG;AACH,eAAO,MAAM,yBAAyB;;;;;;;;;;;;EAIpC,CAAC;AAEH,MAAM,MAAM,uBAAuB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAC;AAEhF,qBAAa,mBAAoB,YAAW,cAAc;IACxD,QAAQ,CAAC,UAAU,EAAG,OAAO,CAAU;IACvC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;gBAEb,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;IAK1C,eAAe,IAAI,SAAS,MAAM,EAAE;IAIpC,MAAM,IAAI,uBAAuB;IAQjC,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE,uBAAuB,GAAG,mBAAmB;CAGpE;AAED;;GAEG;AACH,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAI/B,CAAC;AAEH,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAEtE;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,kBAAkB,GAAG,cAAc,CAe/E;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,cAAc,GAAG,kBAAkB,CAWpF;AAED,qBAAa,gCAAiC,SAAQ,KAAK;gBAC7C,OAAO,EAAE,MAAM;CAI5B"}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* API credentials types and utilities for authentication with various services.
|
|
3
|
+
*/
|
|
4
|
+
import { z } from 'zod';
|
|
5
|
+
export var ApiCredentialStatus;
|
|
6
|
+
(function (ApiCredentialStatus) {
|
|
7
|
+
ApiCredentialStatus["Missing"] = "missing";
|
|
8
|
+
ApiCredentialStatus["Valid"] = "valid";
|
|
9
|
+
ApiCredentialStatus["Invalid"] = "invalid";
|
|
10
|
+
})(ApiCredentialStatus || (ApiCredentialStatus = {}));
|
|
11
|
+
/**
|
|
12
|
+
* Bearer token authentication (Authorization: Bearer <token>).
|
|
13
|
+
*/
|
|
14
|
+
export const AuthorizationBearerSchema = z.object({
|
|
15
|
+
objectType: z.literal('authorizationBearer'),
|
|
16
|
+
token: z.string(),
|
|
17
|
+
});
|
|
18
|
+
export class AuthorizationBearer {
|
|
19
|
+
objectType = 'authorizationBearer';
|
|
20
|
+
token;
|
|
21
|
+
constructor(token) {
|
|
22
|
+
this.token = token;
|
|
23
|
+
}
|
|
24
|
+
asCurlArguments() {
|
|
25
|
+
return ['-H', `Authorization: Bearer ${this.token}`];
|
|
26
|
+
}
|
|
27
|
+
toJSON() {
|
|
28
|
+
return {
|
|
29
|
+
objectType: this.objectType,
|
|
30
|
+
token: this.token,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
static fromJSON(data) {
|
|
34
|
+
return new AuthorizationBearer(data.token);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Raw authorization header (Authorization: <token>).
|
|
39
|
+
*/
|
|
40
|
+
export const AuthorizationBareSchema = z.object({
|
|
41
|
+
objectType: z.literal('authorizationBare'),
|
|
42
|
+
token: z.string(),
|
|
43
|
+
});
|
|
44
|
+
export class AuthorizationBare {
|
|
45
|
+
objectType = 'authorizationBare';
|
|
46
|
+
token;
|
|
47
|
+
constructor(token) {
|
|
48
|
+
this.token = token;
|
|
49
|
+
}
|
|
50
|
+
asCurlArguments() {
|
|
51
|
+
return ['-H', `Authorization: ${this.token}`];
|
|
52
|
+
}
|
|
53
|
+
toJSON() {
|
|
54
|
+
return {
|
|
55
|
+
objectType: this.objectType,
|
|
56
|
+
token: this.token,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
static fromJSON(data) {
|
|
60
|
+
return new AuthorizationBare(data.token);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Slack-specific credentials (token + d cookie).
|
|
65
|
+
*/
|
|
66
|
+
export const SlackApiCredentialsSchema = z.object({
|
|
67
|
+
objectType: z.literal('slack'),
|
|
68
|
+
token: z.string(),
|
|
69
|
+
dCookie: z.string(),
|
|
70
|
+
});
|
|
71
|
+
export class SlackApiCredentials {
|
|
72
|
+
objectType = 'slack';
|
|
73
|
+
token;
|
|
74
|
+
dCookie;
|
|
75
|
+
constructor(token, dCookie) {
|
|
76
|
+
this.token = token;
|
|
77
|
+
this.dCookie = dCookie;
|
|
78
|
+
}
|
|
79
|
+
asCurlArguments() {
|
|
80
|
+
return ['-H', `Authorization: Bearer ${this.token}`, '-H', `Cookie: d=${this.dCookie}`];
|
|
81
|
+
}
|
|
82
|
+
toJSON() {
|
|
83
|
+
return {
|
|
84
|
+
objectType: this.objectType,
|
|
85
|
+
token: this.token,
|
|
86
|
+
dCookie: this.dCookie,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
static fromJSON(data) {
|
|
90
|
+
return new SlackApiCredentials(data.token, data.dCookie);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Union schema for all credential types.
|
|
95
|
+
*/
|
|
96
|
+
export const ApiCredentialsSchema = z.discriminatedUnion('objectType', [
|
|
97
|
+
AuthorizationBearerSchema,
|
|
98
|
+
AuthorizationBareSchema,
|
|
99
|
+
SlackApiCredentialsSchema,
|
|
100
|
+
]);
|
|
101
|
+
/**
|
|
102
|
+
* Deserialize credentials from JSON data.
|
|
103
|
+
*/
|
|
104
|
+
export function deserializeCredentials(data) {
|
|
105
|
+
switch (data.objectType) {
|
|
106
|
+
case 'authorizationBearer':
|
|
107
|
+
return AuthorizationBearer.fromJSON(data);
|
|
108
|
+
case 'authorizationBare':
|
|
109
|
+
return AuthorizationBare.fromJSON(data);
|
|
110
|
+
case 'slack':
|
|
111
|
+
return SlackApiCredentials.fromJSON(data);
|
|
112
|
+
default: {
|
|
113
|
+
const exhaustiveCheck = data;
|
|
114
|
+
throw new ApiCredentialsSerializationError(`Unknown credential type: ${exhaustiveCheck.objectType}`);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Serialize credentials to JSON data.
|
|
120
|
+
*/
|
|
121
|
+
export function serializeCredentials(credentials) {
|
|
122
|
+
if (credentials instanceof AuthorizationBearer) {
|
|
123
|
+
return credentials.toJSON();
|
|
124
|
+
}
|
|
125
|
+
if (credentials instanceof AuthorizationBare) {
|
|
126
|
+
return credentials.toJSON();
|
|
127
|
+
}
|
|
128
|
+
if (credentials instanceof SlackApiCredentials) {
|
|
129
|
+
return credentials.toJSON();
|
|
130
|
+
}
|
|
131
|
+
throw new ApiCredentialsSerializationError(`Unknown credential type: ${credentials.objectType}`);
|
|
132
|
+
}
|
|
133
|
+
export class ApiCredentialsSerializationError extends Error {
|
|
134
|
+
constructor(message) {
|
|
135
|
+
super(message);
|
|
136
|
+
this.name = 'ApiCredentialsSerializationError';
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
//# sourceMappingURL=apiCredentials.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"apiCredentials.js","sourceRoot":"","sources":["../../src/apiCredentials.ts"],"names":[],"mappings":"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"}
|