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.
Files changed (176) hide show
  1. package/.nvmrc +1 -0
  2. package/.pre-commit-config.yaml +22 -0
  3. package/.prettierignore +4 -0
  4. package/.prettierrc +7 -0
  5. package/CLAUDE.md +13 -0
  6. package/LICENSE +7 -0
  7. package/README.md +167 -0
  8. package/dist/scripts/cryptFile.d.ts +21 -0
  9. package/dist/scripts/cryptFile.d.ts.map +1 -0
  10. package/dist/scripts/cryptFile.js +106 -0
  11. package/dist/scripts/cryptFile.js.map +1 -0
  12. package/dist/scripts/encryptFile.d.ts +21 -0
  13. package/dist/scripts/encryptFile.d.ts.map +1 -0
  14. package/dist/scripts/encryptFile.js +101 -0
  15. package/dist/scripts/encryptFile.js.map +1 -0
  16. package/dist/scripts/recordBrowserSession.d.ts +18 -0
  17. package/dist/scripts/recordBrowserSession.d.ts.map +1 -0
  18. package/dist/scripts/recordBrowserSession.js +213 -0
  19. package/dist/scripts/recordBrowserSession.js.map +1 -0
  20. package/dist/src/apiCredentialStore.d.ts +19 -0
  21. package/dist/src/apiCredentialStore.d.ts.map +1 -0
  22. package/dist/src/apiCredentialStore.js +65 -0
  23. package/dist/src/apiCredentialStore.js.map +1 -0
  24. package/dist/src/apiCredentials.d.ts +134 -0
  25. package/dist/src/apiCredentials.d.ts.map +1 -0
  26. package/dist/src/apiCredentials.js +139 -0
  27. package/dist/src/apiCredentials.js.map +1 -0
  28. package/dist/src/browserConfig.d.ts +90 -0
  29. package/dist/src/browserConfig.d.ts.map +1 -0
  30. package/dist/src/browserConfig.js +259 -0
  31. package/dist/src/browserConfig.js.map +1 -0
  32. package/dist/src/browserState.d.ts +8 -0
  33. package/dist/src/browserState.d.ts.map +1 -0
  34. package/dist/src/browserState.js +21 -0
  35. package/dist/src/browserState.js.map +1 -0
  36. package/dist/src/cli.d.ts +6 -0
  37. package/dist/src/cli.d.ts.map +1 -0
  38. package/dist/src/cli.js +25 -0
  39. package/dist/src/cli.js.map +1 -0
  40. package/dist/src/cliCommands.d.ts +29 -0
  41. package/dist/src/cliCommands.d.ts.map +1 -0
  42. package/dist/src/cliCommands.js +264 -0
  43. package/dist/src/cliCommands.js.map +1 -0
  44. package/dist/src/config.d.ts +35 -0
  45. package/dist/src/config.d.ts.map +1 -0
  46. package/dist/src/config.js +96 -0
  47. package/dist/src/config.js.map +1 -0
  48. package/dist/src/curl.d.ts +29 -0
  49. package/dist/src/curl.d.ts.map +1 -0
  50. package/dist/src/curl.js +53 -0
  51. package/dist/src/curl.js.map +1 -0
  52. package/dist/src/encryptedStorage.d.ts +39 -0
  53. package/dist/src/encryptedStorage.d.ts.map +1 -0
  54. package/dist/src/encryptedStorage.js +128 -0
  55. package/dist/src/encryptedStorage.js.map +1 -0
  56. package/dist/src/encryption.d.ts +28 -0
  57. package/dist/src/encryption.d.ts.map +1 -0
  58. package/dist/src/encryption.js +86 -0
  59. package/dist/src/encryption.js.map +1 -0
  60. package/dist/src/index.d.ts +14 -0
  61. package/dist/src/index.d.ts.map +1 -0
  62. package/dist/src/index.js +17 -0
  63. package/dist/src/index.js.map +1 -0
  64. package/dist/src/keychain.d.ts +33 -0
  65. package/dist/src/keychain.d.ts.map +1 -0
  66. package/dist/src/keychain.js +94 -0
  67. package/dist/src/keychain.js.map +1 -0
  68. package/dist/src/playwrightUtils.d.ts +27 -0
  69. package/dist/src/playwrightUtils.d.ts.map +1 -0
  70. package/dist/src/playwrightUtils.js +122 -0
  71. package/dist/src/playwrightUtils.js.map +1 -0
  72. package/dist/src/registry.d.ts +12 -0
  73. package/dist/src/registry.d.ts.map +1 -0
  74. package/dist/src/registry.js +30 -0
  75. package/dist/src/registry.js.map +1 -0
  76. package/dist/src/services/base.d.ts +98 -0
  77. package/dist/src/services/base.d.ts.map +1 -0
  78. package/dist/src/services/base.js +137 -0
  79. package/dist/src/services/base.js.map +1 -0
  80. package/dist/src/services/discord.d.ts +20 -0
  81. package/dist/src/services/discord.d.ts.map +1 -0
  82. package/dist/src/services/discord.js +55 -0
  83. package/dist/src/services/discord.js.map +1 -0
  84. package/dist/src/services/dropbox.d.ts +23 -0
  85. package/dist/src/services/dropbox.d.ts.map +1 -0
  86. package/dist/src/services/dropbox.js +136 -0
  87. package/dist/src/services/dropbox.js.map +1 -0
  88. package/dist/src/services/github.d.ts +23 -0
  89. package/dist/src/services/github.d.ts.map +1 -0
  90. package/dist/src/services/github.js +110 -0
  91. package/dist/src/services/github.js.map +1 -0
  92. package/dist/src/services/index.d.ts +12 -0
  93. package/dist/src/services/index.d.ts.map +1 -0
  94. package/dist/src/services/index.js +11 -0
  95. package/dist/src/services/index.js.map +1 -0
  96. package/dist/src/services/linear.d.ts +23 -0
  97. package/dist/src/services/linear.d.ts.map +1 -0
  98. package/dist/src/services/linear.js +110 -0
  99. package/dist/src/services/linear.js.map +1 -0
  100. package/dist/src/services/slack.d.ts +21 -0
  101. package/dist/src/services/slack.d.ts.map +1 -0
  102. package/dist/src/services/slack.js +67 -0
  103. package/dist/src/services/slack.js.map +1 -0
  104. package/dist/tests/apiCredentialStore.test.d.ts +2 -0
  105. package/dist/tests/apiCredentialStore.test.d.ts.map +1 -0
  106. package/dist/tests/apiCredentialStore.test.js +130 -0
  107. package/dist/tests/apiCredentialStore.test.js.map +1 -0
  108. package/dist/tests/apiCredentials.test.d.ts +2 -0
  109. package/dist/tests/apiCredentials.test.d.ts.map +1 -0
  110. package/dist/tests/apiCredentials.test.js +169 -0
  111. package/dist/tests/apiCredentials.test.js.map +1 -0
  112. package/dist/tests/cli.test.d.ts +2 -0
  113. package/dist/tests/cli.test.d.ts.map +1 -0
  114. package/dist/tests/cli.test.js +584 -0
  115. package/dist/tests/cli.test.js.map +1 -0
  116. package/dist/tests/encryptedStorage.test.d.ts +2 -0
  117. package/dist/tests/encryptedStorage.test.d.ts.map +1 -0
  118. package/dist/tests/encryptedStorage.test.js +126 -0
  119. package/dist/tests/encryptedStorage.test.js.map +1 -0
  120. package/dist/tests/encryption.test.d.ts +2 -0
  121. package/dist/tests/encryption.test.d.ts.map +1 -0
  122. package/dist/tests/encryption.test.js +121 -0
  123. package/dist/tests/encryption.test.js.map +1 -0
  124. package/dist/tests/lint.test.d.ts +2 -0
  125. package/dist/tests/lint.test.d.ts.map +1 -0
  126. package/dist/tests/lint.test.js +18 -0
  127. package/dist/tests/lint.test.js.map +1 -0
  128. package/dist/tests/registry.test.d.ts +2 -0
  129. package/dist/tests/registry.test.d.ts.map +1 -0
  130. package/dist/tests/registry.test.js +85 -0
  131. package/dist/tests/registry.test.js.map +1 -0
  132. package/dist/tests/servicesAgainstRecordings.test.d.ts +20 -0
  133. package/dist/tests/servicesAgainstRecordings.test.d.ts.map +1 -0
  134. package/dist/tests/servicesAgainstRecordings.test.js +157 -0
  135. package/dist/tests/servicesAgainstRecordings.test.js.map +1 -0
  136. package/dist/tests/typecheck.test.d.ts +2 -0
  137. package/dist/tests/typecheck.test.d.ts.map +1 -0
  138. package/dist/tests/typecheck.test.js +18 -0
  139. package/dist/tests/typecheck.test.js.map +1 -0
  140. package/docs/development.md +94 -0
  141. package/eslint.config.js +30 -0
  142. package/integrations/SKILL.md +62 -0
  143. package/package.json +68 -0
  144. package/scripts/cryptFile.ts +123 -0
  145. package/scripts/recordBrowserSession.ts +280 -0
  146. package/scripts/tsconfig.json +10 -0
  147. package/src/apiCredentialStore.ts +87 -0
  148. package/src/apiCredentials.ts +180 -0
  149. package/src/cli.ts +32 -0
  150. package/src/cliCommands.ts +321 -0
  151. package/src/config.ts +115 -0
  152. package/src/curl.ts +78 -0
  153. package/src/encryptedStorage.ts +161 -0
  154. package/src/encryption.ts +106 -0
  155. package/src/index.ts +65 -0
  156. package/src/keychain.ts +105 -0
  157. package/src/playwrightUtils.ts +143 -0
  158. package/src/registry.ts +35 -0
  159. package/src/services/base.ts +234 -0
  160. package/src/services/discord.ts +73 -0
  161. package/src/services/dropbox.ts +173 -0
  162. package/src/services/github.ts +139 -0
  163. package/src/services/index.ts +13 -0
  164. package/src/services/linear.ts +134 -0
  165. package/src/services/slack.ts +85 -0
  166. package/tests/apiCredentialStore.test.ts +162 -0
  167. package/tests/apiCredentials.test.ts +195 -0
  168. package/tests/cli.test.ts +798 -0
  169. package/tests/encryptedStorage.test.ts +173 -0
  170. package/tests/encryption.test.ts +169 -0
  171. package/tests/lint.test.ts +19 -0
  172. package/tests/registry.test.ts +103 -0
  173. package/tests/servicesAgainstRecordings.test.ts +230 -0
  174. package/tests/typecheck.test.ts +19 -0
  175. package/tsconfig.json +24 -0
  176. package/vitest.config.ts +13 -0
@@ -0,0 +1,126 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
2
+ import { mkdtempSync, rmSync, readFileSync, existsSync, statSync, writeFileSync, chmodSync, } from 'node:fs';
3
+ import { join } from 'node:path';
4
+ import { tmpdir } from 'node:os';
5
+ import { EncryptedStorage, EncryptedStorageError, PathIsDirectoryError, } from '../src/encryptedStorage.js';
6
+ import { generateKey } from '../src/encryption.js';
7
+ describe('EncryptedStorage', () => {
8
+ let tempDir;
9
+ let testKey;
10
+ beforeEach(() => {
11
+ tempDir = mkdtempSync(join(tmpdir(), 'latchkey-encrypted-test-'));
12
+ testKey = generateKey();
13
+ });
14
+ afterEach(() => {
15
+ rmSync(tempDir, { recursive: true, force: true });
16
+ });
17
+ describe('with encryption key from Config', () => {
18
+ it('should encrypt data when encryptionKey is set', () => {
19
+ const filePath = join(tempDir, 'test.json.enc');
20
+ const content = '{"token": "secret-value"}';
21
+ const storage = new EncryptedStorage({
22
+ encryptionKeyOverride: testKey,
23
+ });
24
+ storage.writeFile(filePath, content);
25
+ // Verify the file is written to the exact path specified
26
+ expect(existsSync(filePath)).toBe(true);
27
+ // Verify the file is encrypted (starts with prefix)
28
+ const rawContent = readFileSync(filePath, 'utf-8');
29
+ expect(rawContent).toMatch(/^LATCHKEY_ENCRYPTED:/);
30
+ expect(rawContent).not.toContain('secret-value');
31
+ });
32
+ it('should decrypt data with the same key', () => {
33
+ const filePath = join(tempDir, 'test.json.enc');
34
+ const content = '{"token": "secret-value"}';
35
+ const storage = new EncryptedStorage({
36
+ encryptionKeyOverride: testKey,
37
+ });
38
+ storage.writeFile(filePath, content);
39
+ const retrieved = storage.readFile(filePath);
40
+ expect(retrieved).toBe(content);
41
+ });
42
+ it('should fail to decrypt with wrong key', () => {
43
+ const filePath = join(tempDir, 'test.json.enc');
44
+ const content = '{"token": "secret-value"}';
45
+ const key1 = generateKey();
46
+ const key2 = generateKey();
47
+ // Write with one key
48
+ const storageWrite = new EncryptedStorage({
49
+ encryptionKeyOverride: key1,
50
+ });
51
+ storageWrite.writeFile(filePath, content);
52
+ // Read with different key
53
+ const storageRead = new EncryptedStorage({
54
+ encryptionKeyOverride: key2,
55
+ });
56
+ expect(() => storageRead.readFile(filePath)).toThrow(EncryptedStorageError);
57
+ });
58
+ });
59
+ describe('file permissions', () => {
60
+ it('should set chmod 600 on written files', () => {
61
+ const filePath = join(tempDir, 'test.json.enc');
62
+ const content = '{"token": "secret-value"}';
63
+ const storage = new EncryptedStorage({
64
+ encryptionKeyOverride: testKey,
65
+ });
66
+ storage.writeFile(filePath, content);
67
+ // Check file permissions (600 = 0o600 = 384 in decimal)
68
+ const stats = statSync(filePath);
69
+ const permissions = stats.mode & 0o777;
70
+ expect(permissions).toBe(0o600);
71
+ });
72
+ it('should create parent directories with chmod 700', () => {
73
+ const filePath = join(tempDir, 'nested', 'deep', 'test.json.enc');
74
+ const content = '{"token": "secret-value"}';
75
+ const storage = new EncryptedStorage({
76
+ encryptionKeyOverride: testKey,
77
+ });
78
+ storage.writeFile(filePath, content);
79
+ expect(existsSync(filePath)).toBe(true);
80
+ });
81
+ it('should allow overwriting file with secure permissions', () => {
82
+ const filePath = join(tempDir, 'secure.json.enc');
83
+ const content = '{"token": "secret-value"}';
84
+ // Create a file with secure permissions
85
+ writeFileSync(filePath, 'existing content', { encoding: 'utf-8' });
86
+ chmodSync(filePath, 0o600);
87
+ const storage = new EncryptedStorage({
88
+ encryptionKeyOverride: testKey,
89
+ });
90
+ // Should not throw
91
+ storage.writeFile(filePath, content);
92
+ const retrieved = storage.readFile(filePath);
93
+ expect(retrieved).toBe(content);
94
+ });
95
+ });
96
+ describe('readFile', () => {
97
+ it('should return null for non-existent file', () => {
98
+ const filePath = join(tempDir, 'nonexistent.json.enc');
99
+ const storage = new EncryptedStorage({
100
+ encryptionKeyOverride: testKey,
101
+ });
102
+ expect(storage.readFile(filePath)).toBeNull();
103
+ });
104
+ it('should throw PathIsDirectoryError when path is a directory', () => {
105
+ const storage = new EncryptedStorage({
106
+ encryptionKeyOverride: testKey,
107
+ });
108
+ expect(() => storage.readFile(tempDir)).toThrow(PathIsDirectoryError);
109
+ expect(() => storage.readFile(tempDir)).toThrow('Path is a directory, not a file');
110
+ });
111
+ });
112
+ describe('writeFile', () => {
113
+ it('should throw PathIsDirectoryError when path is a directory', () => {
114
+ const storage = new EncryptedStorage({
115
+ encryptionKeyOverride: testKey,
116
+ });
117
+ expect(() => {
118
+ storage.writeFile(tempDir, 'content');
119
+ }).toThrow(PathIsDirectoryError);
120
+ expect(() => {
121
+ storage.writeFile(tempDir, 'content');
122
+ }).toThrow('Path is a directory, not a file');
123
+ });
124
+ });
125
+ });
126
+ //# sourceMappingURL=encryptedStorage.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"encryptedStorage.test.js","sourceRoot":"","sources":["../../tests/encryptedStorage.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=encryption.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"encryption.test.d.ts","sourceRoot":"","sources":["../../tests/encryption.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,121 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { encrypt, decrypt, generateKey, DecryptionError, EncryptionError, } from '../src/encryption.js';
3
+ describe('encryption', () => {
4
+ describe('encrypt/decrypt roundtrip', () => {
5
+ it('should encrypt and decrypt a simple string', () => {
6
+ const key = generateKey();
7
+ const plaintext = 'Hello, World!';
8
+ const encrypted = encrypt(plaintext, key);
9
+ const decrypted = decrypt(encrypted, key);
10
+ expect(decrypted).toBe(plaintext);
11
+ });
12
+ it('should encrypt and decrypt a JSON string', () => {
13
+ const key = generateKey();
14
+ const data = { token: 'xoxc-test', dCookie: 'd-value' };
15
+ const plaintext = JSON.stringify(data, null, 2);
16
+ const encrypted = encrypt(plaintext, key);
17
+ const decrypted = decrypt(encrypted, key);
18
+ expect(decrypted).toBe(plaintext);
19
+ expect(JSON.parse(decrypted)).toEqual(data);
20
+ });
21
+ it('should encrypt and decrypt unicode characters', () => {
22
+ const key = generateKey();
23
+ const plaintext = '日本語 🎉 émojis and ünïcödé';
24
+ const encrypted = encrypt(plaintext, key);
25
+ const decrypted = decrypt(encrypted, key);
26
+ expect(decrypted).toBe(plaintext);
27
+ });
28
+ it('should encrypt and decrypt empty string', () => {
29
+ const key = generateKey();
30
+ const plaintext = '';
31
+ const encrypted = encrypt(plaintext, key);
32
+ const decrypted = decrypt(encrypted, key);
33
+ expect(decrypted).toBe(plaintext);
34
+ });
35
+ it('should encrypt and decrypt long text', () => {
36
+ const key = generateKey();
37
+ const plaintext = 'a'.repeat(10000);
38
+ const encrypted = encrypt(plaintext, key);
39
+ const decrypted = decrypt(encrypted, key);
40
+ expect(decrypted).toBe(plaintext);
41
+ });
42
+ });
43
+ describe('encrypt', () => {
44
+ it('should produce different ciphertext for same plaintext (due to random IV)', () => {
45
+ const key = generateKey();
46
+ const plaintext = 'Hello, World!';
47
+ const encrypted1 = encrypt(plaintext, key);
48
+ const encrypted2 = encrypt(plaintext, key);
49
+ expect(encrypted1).not.toBe(encrypted2);
50
+ });
51
+ it('should produce base64 output', () => {
52
+ const key = generateKey();
53
+ const plaintext = 'Hello, World!';
54
+ const encrypted = encrypt(plaintext, key);
55
+ // Base64 only contains alphanumeric, +, /, and = characters
56
+ expect(encrypted).toMatch(/^[A-Za-z0-9+/]+=*$/);
57
+ });
58
+ it('should fail with invalid key length', () => {
59
+ const shortKey = Buffer.from('too-short').toString('base64');
60
+ const plaintext = 'Hello, World!';
61
+ expect(() => encrypt(plaintext, shortKey)).toThrow(EncryptionError);
62
+ });
63
+ });
64
+ describe('decrypt', () => {
65
+ it('should fail with wrong key', () => {
66
+ const key1 = generateKey();
67
+ const key2 = generateKey();
68
+ const plaintext = 'Hello, World!';
69
+ const encrypted = encrypt(plaintext, key1);
70
+ expect(() => decrypt(encrypted, key2)).toThrow(DecryptionError);
71
+ });
72
+ it('should fail with corrupted data', () => {
73
+ const key = generateKey();
74
+ const plaintext = 'Hello, World!';
75
+ const encrypted = encrypt(plaintext, key);
76
+ // Corrupt the encrypted data
77
+ const corrupted = encrypted.slice(0, -5) + 'xxxxx';
78
+ expect(() => decrypt(corrupted, key)).toThrow(DecryptionError);
79
+ });
80
+ it('should fail with too short data', () => {
81
+ const key = generateKey();
82
+ expect(() => decrypt('dG9vIHNob3J0', key)).toThrow(DecryptionError);
83
+ });
84
+ it('should fail with invalid key length', () => {
85
+ const key = generateKey();
86
+ const shortKey = Buffer.from('too-short').toString('base64');
87
+ const plaintext = 'Hello, World!';
88
+ const encrypted = encrypt(plaintext, key);
89
+ expect(() => decrypt(encrypted, shortKey)).toThrow(DecryptionError);
90
+ });
91
+ });
92
+ describe('generateKey', () => {
93
+ it('should generate a 256-bit key (44 base64 chars)', () => {
94
+ const key = generateKey();
95
+ // 32 bytes encoded as base64 = 44 characters (with padding)
96
+ expect(key.length).toBe(44);
97
+ });
98
+ it('should generate different keys each time', () => {
99
+ const key1 = generateKey();
100
+ const key2 = generateKey();
101
+ expect(key1).not.toBe(key2);
102
+ });
103
+ it('should generate valid base64 keys', () => {
104
+ const key = generateKey();
105
+ expect(key).toMatch(/^[A-Za-z0-9+/]+=*$/);
106
+ });
107
+ it('should generate keys that decode to 32 bytes', () => {
108
+ const key = generateKey();
109
+ const decoded = Buffer.from(key, 'base64');
110
+ expect(decoded.length).toBe(32);
111
+ });
112
+ it('should generate keys that work for encryption', () => {
113
+ const key = generateKey();
114
+ const plaintext = 'test data';
115
+ const encrypted = encrypt(plaintext, key);
116
+ const decrypted = decrypt(encrypted, key);
117
+ expect(decrypted).toBe(plaintext);
118
+ });
119
+ });
120
+ });
121
+ //# sourceMappingURL=encryption.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"encryption.test.js","sourceRoot":"","sources":["../../tests/encryption.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=lint.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lint.test.d.ts","sourceRoot":"","sources":["../../tests/lint.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,18 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { execSync } from 'child_process';
3
+ import { dirname, join } from 'path';
4
+ import { fileURLToPath } from 'url';
5
+ const __dirname = dirname(fileURLToPath(import.meta.url));
6
+ const projectRoot = join(__dirname, '..');
7
+ describe('Linter', () => {
8
+ it('should pass linting', () => {
9
+ expect(() => {
10
+ execSync('npm run lint', {
11
+ cwd: projectRoot,
12
+ encoding: 'utf-8',
13
+ stdio: 'pipe',
14
+ });
15
+ }).not.toThrow();
16
+ });
17
+ });
18
+ //# sourceMappingURL=lint.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lint.test.js","sourceRoot":"","sources":["../../tests/lint.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AACrC,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAC;AAEpC,MAAM,SAAS,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AAC1D,MAAM,WAAW,GAAG,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;AAE1C,QAAQ,CAAC,QAAQ,EAAE,GAAG,EAAE;IACtB,EAAE,CAAC,qBAAqB,EAAE,GAAG,EAAE;QAC7B,MAAM,CAAC,GAAG,EAAE;YACV,QAAQ,CAAC,cAAc,EAAE;gBACvB,GAAG,EAAE,WAAW;gBAChB,QAAQ,EAAE,OAAO;gBACjB,KAAK,EAAE,MAAM;aACd,CAAC,CAAC;QACL,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;IACnB,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=registry.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.test.d.ts","sourceRoot":"","sources":["../../tests/registry.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,85 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { Registry, REGISTRY } from '../src/registry.js';
3
+ import { SLACK, DISCORD, GITHUB, DROPBOX, LINEAR } from '../src/services/index.js';
4
+ describe('Registry', () => {
5
+ describe('getByName', () => {
6
+ it('should find Slack by name', () => {
7
+ expect(REGISTRY.getByName('slack')).toBe(SLACK);
8
+ });
9
+ it('should find Discord by name', () => {
10
+ expect(REGISTRY.getByName('discord')).toBe(DISCORD);
11
+ });
12
+ it('should find GitHub by name', () => {
13
+ expect(REGISTRY.getByName('github')).toBe(GITHUB);
14
+ });
15
+ it('should find Dropbox by name', () => {
16
+ expect(REGISTRY.getByName('dropbox')).toBe(DROPBOX);
17
+ });
18
+ it('should find Linear by name', () => {
19
+ expect(REGISTRY.getByName('linear')).toBe(LINEAR);
20
+ });
21
+ it('should return null for unknown service', () => {
22
+ expect(REGISTRY.getByName('unknown')).toBeNull();
23
+ });
24
+ it('should be case-sensitive', () => {
25
+ expect(REGISTRY.getByName('Slack')).toBeNull();
26
+ expect(REGISTRY.getByName('SLACK')).toBeNull();
27
+ });
28
+ });
29
+ describe('getByUrl', () => {
30
+ it('should find Slack by API URL', () => {
31
+ expect(REGISTRY.getByUrl('https://slack.com/api/auth.test')).toBe(SLACK);
32
+ expect(REGISTRY.getByUrl('https://slack.com/api/users.list')).toBe(SLACK);
33
+ });
34
+ it('should find Discord by API URL', () => {
35
+ expect(REGISTRY.getByUrl('https://discord.com/api/v9/users/@me')).toBe(DISCORD);
36
+ expect(REGISTRY.getByUrl('https://discord.com/api/guilds')).toBe(DISCORD);
37
+ });
38
+ it('should find GitHub by API URL', () => {
39
+ expect(REGISTRY.getByUrl('https://api.github.com/user')).toBe(GITHUB);
40
+ expect(REGISTRY.getByUrl('https://api.github.com/repos')).toBe(GITHUB);
41
+ });
42
+ it('should find Dropbox by API URL', () => {
43
+ expect(REGISTRY.getByUrl('https://api.dropboxapi.com/2/users/get_current_account')).toBe(DROPBOX);
44
+ expect(REGISTRY.getByUrl('https://content.dropboxapi.com/upload')).toBe(DROPBOX);
45
+ expect(REGISTRY.getByUrl('https://notify.dropboxapi.com/subscribe')).toBe(DROPBOX);
46
+ });
47
+ it('should find Linear by API URL', () => {
48
+ expect(REGISTRY.getByUrl('https://api.linear.app/graphql')).toBe(LINEAR);
49
+ });
50
+ it('should return null for unknown URL', () => {
51
+ expect(REGISTRY.getByUrl('https://example.com/api')).toBeNull();
52
+ expect(REGISTRY.getByUrl('https://google.com')).toBeNull();
53
+ });
54
+ it('should not match partial URLs', () => {
55
+ expect(REGISTRY.getByUrl('https://slack.com/')).toBeNull();
56
+ expect(REGISTRY.getByUrl('https://slack.com')).toBeNull();
57
+ });
58
+ });
59
+ describe('services', () => {
60
+ it('should contain all services', () => {
61
+ expect(REGISTRY.services).toHaveLength(5);
62
+ expect(REGISTRY.services).toContain(SLACK);
63
+ expect(REGISTRY.services).toContain(DISCORD);
64
+ expect(REGISTRY.services).toContain(GITHUB);
65
+ expect(REGISTRY.services).toContain(DROPBOX);
66
+ expect(REGISTRY.services).toContain(LINEAR);
67
+ });
68
+ });
69
+ describe('custom registry', () => {
70
+ it('should work with custom service list', () => {
71
+ const customRegistry = new Registry([SLACK, GITHUB]);
72
+ expect(customRegistry.services).toHaveLength(2);
73
+ expect(customRegistry.getByName('slack')).toBe(SLACK);
74
+ expect(customRegistry.getByName('github')).toBe(GITHUB);
75
+ expect(customRegistry.getByName('discord')).toBeNull();
76
+ });
77
+ it('should work with empty service list', () => {
78
+ const emptyRegistry = new Registry([]);
79
+ expect(emptyRegistry.services).toHaveLength(0);
80
+ expect(emptyRegistry.getByName('slack')).toBeNull();
81
+ expect(emptyRegistry.getByUrl('https://slack.com/api/test')).toBeNull();
82
+ });
83
+ });
84
+ });
85
+ //# sourceMappingURL=registry.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.test.js","sourceRoot":"","sources":["../../tests/registry.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Test login recordings against service implementations.
3
+ *
4
+ * This module validates that recorded login sessions can be used to test
5
+ * service API credential extraction logic. It discovers recordings in
6
+ * scripts/recordings/<service_name>/ and verifies that the service's
7
+ * getApiCredentialsFromResponse() method can extract valid API credentials
8
+ * from the recorded requests.
9
+ *
10
+ * The tests work by loading recorded HTTP request/response pairs from login_session.json
11
+ * and creating mock Response objects to pass to the service's API credential extraction
12
+ * method. This validates that the service can correctly identify and extract
13
+ * API credentials from outgoing browser requests.
14
+ *
15
+ * Usage:
16
+ * npm test -- tests/servicesAgainstRecordings.test.ts # Test all recordings
17
+ * npm test -- tests/servicesAgainstRecordings.test.ts -t slack # Test only Slack
18
+ */
19
+ export {};
20
+ //# sourceMappingURL=servicesAgainstRecordings.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"servicesAgainstRecordings.test.d.ts","sourceRoot":"","sources":["../../tests/servicesAgainstRecordings.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG"}
@@ -0,0 +1,157 @@
1
+ /**
2
+ * Test login recordings against service implementations.
3
+ *
4
+ * This module validates that recorded login sessions can be used to test
5
+ * service API credential extraction logic. It discovers recordings in
6
+ * scripts/recordings/<service_name>/ and verifies that the service's
7
+ * getApiCredentialsFromResponse() method can extract valid API credentials
8
+ * from the recorded requests.
9
+ *
10
+ * The tests work by loading recorded HTTP request/response pairs from login_session.json
11
+ * and creating mock Response objects to pass to the service's API credential extraction
12
+ * method. This validates that the service can correctly identify and extract
13
+ * API credentials from outgoing browser requests.
14
+ *
15
+ * Usage:
16
+ * npm test -- tests/servicesAgainstRecordings.test.ts # Test all recordings
17
+ * npm test -- tests/servicesAgainstRecordings.test.ts -t slack # Test only Slack
18
+ */
19
+ import { existsSync, readdirSync, readFileSync } from 'node:fs';
20
+ import { dirname, join, resolve } from 'node:path';
21
+ import { fileURLToPath } from 'node:url';
22
+ import { describe, it, expect } from 'vitest';
23
+ import { REGISTRY } from '../src/registry.js';
24
+ import { SimpleServiceSession } from '../src/services/base.js';
25
+ // Get the directory of this file
26
+ const __filename = fileURLToPath(import.meta.url);
27
+ const __dirname = dirname(__filename);
28
+ // Recordings directory relative to this file
29
+ const RECORDINGS_DIRECTORY = resolve(__dirname, '..', 'scripts', 'recordings');
30
+ // Default recording filename (matches recordBrowserSession.ts)
31
+ const DEFAULT_RECORDING_NAME = 'login_session.json';
32
+ // Do not test services that require special followup steps.
33
+ const BLACKLIST = new Set(['dropbox', 'github', 'linear']);
34
+ class InvalidRecordingError extends Error {
35
+ constructor(message) {
36
+ super(message);
37
+ this.name = 'InvalidRecordingError';
38
+ }
39
+ }
40
+ class ApiCredentialExtractionError extends Error {
41
+ constructor(message) {
42
+ super(message);
43
+ this.name = 'ApiCredentialExtractionError';
44
+ }
45
+ }
46
+ function loadRecordingEntries(requestsPath) {
47
+ const content = readFileSync(requestsPath, 'utf-8');
48
+ return JSON.parse(content);
49
+ }
50
+ function createMockRequest(requestData) {
51
+ const mockRequest = {
52
+ url: () => requestData.url,
53
+ method: () => requestData.method,
54
+ headers: () => requestData.headers,
55
+ allHeaders: () => Promise.resolve(requestData.headers),
56
+ resourceType: () => requestData.resource_type,
57
+ postData: () => requestData.post_data ?? null,
58
+ };
59
+ return mockRequest;
60
+ }
61
+ function createMockResponse(responseData, mockRequest) {
62
+ const body = responseData.body ?? '';
63
+ const mockResponse = {
64
+ status: () => responseData.status,
65
+ statusText: () => responseData.status_text,
66
+ headers: () => responseData.headers,
67
+ allHeaders: () => Promise.resolve(responseData.headers),
68
+ request: () => mockRequest,
69
+ text: () => Promise.resolve(body),
70
+ json: () => Promise.resolve(JSON.parse(body)),
71
+ body: () => Promise.resolve(Buffer.from(body)),
72
+ };
73
+ return mockResponse;
74
+ }
75
+ /**
76
+ * Test a service's API credential extraction using a recorded session.
77
+ *
78
+ * Loads recorded HTTP request/response pairs and tests that the service can
79
+ * extract API credentials from them using getApiCredentialsFromResponse().
80
+ */
81
+ async function testServiceWithRecording(service, recordingDirectory) {
82
+ const requestsPath = join(recordingDirectory, DEFAULT_RECORDING_NAME);
83
+ if (!existsSync(requestsPath)) {
84
+ throw new InvalidRecordingError(`Requests file not found: ${requestsPath}`);
85
+ }
86
+ const recordingEntries = loadRecordingEntries(requestsPath);
87
+ if (recordingEntries.length === 0) {
88
+ throw new InvalidRecordingError('No requests recorded');
89
+ }
90
+ const session = service.getSession();
91
+ if (!(session instanceof SimpleServiceSession)) {
92
+ throw new InvalidRecordingError(`Service ${service.name} does not use SimpleServiceSession, cannot test with recordings`);
93
+ }
94
+ // Try to extract API credentials from each recorded request/response pair
95
+ for (const entry of recordingEntries) {
96
+ const mockRequest = createMockRequest(entry.request);
97
+ const mockResponse = createMockResponse(entry.response, mockRequest);
98
+ // Call onResponse which internally calls getApiCredentialsFromResponse
99
+ session.onResponse(mockResponse);
100
+ // Give async operations time to complete (e.g., Slack reads response body)
101
+ await new Promise((resolve) => setTimeout(resolve, 10));
102
+ }
103
+ // Give additional time for any async operations to complete
104
+ await new Promise((resolve) => setTimeout(resolve, 50));
105
+ // Access the apiCredentials via the session (it's protected but accessible for testing)
106
+ const apiCredentials = session
107
+ .apiCredentials;
108
+ if (apiCredentials !== null) {
109
+ return apiCredentials;
110
+ }
111
+ throw new ApiCredentialExtractionError(`No API credentials could be extracted from ${String(recordingEntries.length)} recorded entries`);
112
+ }
113
+ function discoverRecordings() {
114
+ const recordings = [];
115
+ if (!existsSync(RECORDINGS_DIRECTORY)) {
116
+ return recordings;
117
+ }
118
+ const items = readdirSync(RECORDINGS_DIRECTORY, { withFileTypes: true });
119
+ for (const item of items) {
120
+ if (item.isDirectory() && !item.name.startsWith('.') && !BLACKLIST.has(item.name)) {
121
+ const recordingPath = join(RECORDINGS_DIRECTORY, item.name);
122
+ const requestsPath = join(recordingPath, DEFAULT_RECORDING_NAME);
123
+ if (existsSync(requestsPath)) {
124
+ recordings.push({
125
+ serviceName: item.name,
126
+ recordingPath,
127
+ });
128
+ }
129
+ }
130
+ }
131
+ return recordings.sort((a, b) => a.serviceName.localeCompare(b.serviceName));
132
+ }
133
+ // Discover recordings at module load time
134
+ const discoveredRecordings = discoverRecordings();
135
+ describe('Services Against Recordings', () => {
136
+ if (discoveredRecordings.length === 0) {
137
+ it.skip('No recordings found', () => {
138
+ // Skip if no recordings exist
139
+ });
140
+ }
141
+ for (const { serviceName, recordingPath } of discoveredRecordings) {
142
+ it(`should extract credentials from ${serviceName} recording`, async () => {
143
+ const service = REGISTRY.getByName(serviceName);
144
+ if (service === null) {
145
+ // Skip if service not found in registry
146
+ expect.fail(`Service '${serviceName}' not found in registry`);
147
+ return;
148
+ }
149
+ const apiCredentials = await testServiceWithRecording(service, recordingPath);
150
+ // Verify API credentials are valid
151
+ expect(apiCredentials).not.toBeNull();
152
+ const curlArgs = apiCredentials.asCurlArguments();
153
+ expect(curlArgs.length).toBeGreaterThan(0);
154
+ });
155
+ }
156
+ });
157
+ //# sourceMappingURL=servicesAgainstRecordings.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"servicesAgainstRecordings.test.js","sourceRoot":"","sources":["../../tests/servicesAgainstRecordings.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=typecheck.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"typecheck.test.d.ts","sourceRoot":"","sources":["../../tests/typecheck.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,18 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { execSync } from 'child_process';
3
+ import { dirname, join } from 'path';
4
+ import { fileURLToPath } from 'url';
5
+ const __dirname = dirname(fileURLToPath(import.meta.url));
6
+ const projectRoot = join(__dirname, '..');
7
+ describe('TypeScript', () => {
8
+ it('should pass type checking', () => {
9
+ expect(() => {
10
+ execSync('npm run typecheck', {
11
+ cwd: projectRoot,
12
+ encoding: 'utf-8',
13
+ stdio: 'pipe',
14
+ });
15
+ }).not.toThrow();
16
+ });
17
+ });
18
+ //# sourceMappingURL=typecheck.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"typecheck.test.js","sourceRoot":"","sources":["../../tests/typecheck.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AACrC,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAC;AAEpC,MAAM,SAAS,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AAC1D,MAAM,WAAW,GAAG,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;AAE1C,QAAQ,CAAC,YAAY,EAAE,GAAG,EAAE;IAC1B,EAAE,CAAC,2BAA2B,EAAE,GAAG,EAAE;QACnC,MAAM,CAAC,GAAG,EAAE;YACV,QAAQ,CAAC,mBAAmB,EAAE;gBAC5B,GAAG,EAAE,WAAW;gBAChB,QAAQ,EAAE,OAAO;gBACjB,KAAK,EAAE,MAAM;aACd,CAAC,CAAC;QACL,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;IACnB,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}