@kryshtop/bstack 1.0.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 (108) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +324 -0
  3. package/dist/api/http/BrowserStackHttpClient.d.ts +30 -0
  4. package/dist/api/http/BrowserStackHttpClient.js +111 -0
  5. package/dist/api/http/BrowserStackHttpClient.js.map +1 -0
  6. package/dist/api/http/errors.d.ts +11 -0
  7. package/dist/api/http/errors.js +31 -0
  8. package/dist/api/http/errors.js.map +1 -0
  9. package/dist/api/http/multipart.d.ts +13 -0
  10. package/dist/api/http/multipart.js +28 -0
  11. package/dist/api/http/multipart.js.map +1 -0
  12. package/dist/api/normalizers/common.d.ts +7 -0
  13. package/dist/api/normalizers/common.js +106 -0
  14. package/dist/api/normalizers/common.js.map +1 -0
  15. package/dist/api/registry/EndpointRegistry.d.ts +10 -0
  16. package/dist/api/registry/EndpointRegistry.js +34 -0
  17. package/dist/api/registry/EndpointRegistry.js.map +1 -0
  18. package/dist/api/registry/definitions.d.ts +2 -0
  19. package/dist/api/registry/definitions.js +510 -0
  20. package/dist/api/registry/definitions.js.map +1 -0
  21. package/dist/api/registry/types.d.ts +23 -0
  22. package/dist/api/registry/types.js +2 -0
  23. package/dist/api/registry/types.js.map +1 -0
  24. package/dist/auth/AuthService.d.ts +24 -0
  25. package/dist/auth/AuthService.js +100 -0
  26. package/dist/auth/AuthService.js.map +1 -0
  27. package/dist/bin.d.ts +2 -0
  28. package/dist/bin.js +4 -0
  29. package/dist/bin.js.map +1 -0
  30. package/dist/cli/context.d.ts +39 -0
  31. package/dist/cli/context.js +117 -0
  32. package/dist/cli/context.js.map +1 -0
  33. package/dist/cli/output.d.ts +29 -0
  34. package/dist/cli/output.js +143 -0
  35. package/dist/cli/output.js.map +1 -0
  36. package/dist/cli/program.d.ts +4 -0
  37. package/dist/cli/program.js +60 -0
  38. package/dist/cli/program.js.map +1 -0
  39. package/dist/cli/runCli.d.ts +1 -0
  40. package/dist/cli/runCli.js +31 -0
  41. package/dist/cli/runCli.js.map +1 -0
  42. package/dist/commands/auth.d.ts +3 -0
  43. package/dist/commands/auth.js +73 -0
  44. package/dist/commands/auth.js.map +1 -0
  45. package/dist/commands/explorer.d.ts +3 -0
  46. package/dist/commands/explorer.js +86 -0
  47. package/dist/commands/explorer.js.map +1 -0
  48. package/dist/commands/framework.d.ts +3 -0
  49. package/dist/commands/framework.js +474 -0
  50. package/dist/commands/framework.js.map +1 -0
  51. package/dist/config/paths.d.ts +5 -0
  52. package/dist/config/paths.js +22 -0
  53. package/dist/config/paths.js.map +1 -0
  54. package/dist/index.d.ts +18 -0
  55. package/dist/index.js +16 -0
  56. package/dist/index.js.map +1 -0
  57. package/dist/menus/interactiveMenu.d.ts +2 -0
  58. package/dist/menus/interactiveMenu.js +1106 -0
  59. package/dist/menus/interactiveMenu.js.map +1 -0
  60. package/dist/menus/screenModel.d.ts +12 -0
  61. package/dist/menus/screenModel.js +152 -0
  62. package/dist/menus/screenModel.js.map +1 -0
  63. package/dist/prompts/authPrompts.d.ts +10 -0
  64. package/dist/prompts/authPrompts.js +46 -0
  65. package/dist/prompts/authPrompts.js.map +1 -0
  66. package/dist/services/ResourceService.d.ts +25 -0
  67. package/dist/services/ResourceService.js +80 -0
  68. package/dist/services/ResourceService.js.map +1 -0
  69. package/dist/services/frameworkConfigs.d.ts +16 -0
  70. package/dist/services/frameworkConfigs.js +108 -0
  71. package/dist/services/frameworkConfigs.js.map +1 -0
  72. package/dist/storage/CredentialStore.d.ts +11 -0
  73. package/dist/storage/CredentialStore.js +2 -0
  74. package/dist/storage/CredentialStore.js.map +1 -0
  75. package/dist/storage/FileCredentialStores.d.ts +18 -0
  76. package/dist/storage/FileCredentialStores.js +74 -0
  77. package/dist/storage/FileCredentialStores.js.map +1 -0
  78. package/dist/storage/FileStorage.d.ts +3 -0
  79. package/dist/storage/FileStorage.js +18 -0
  80. package/dist/storage/FileStorage.js.map +1 -0
  81. package/dist/storage/KeytarCredentialStore.d.ts +9 -0
  82. package/dist/storage/KeytarCredentialStore.js +40 -0
  83. package/dist/storage/KeytarCredentialStore.js.map +1 -0
  84. package/dist/storage/SessionRepository.d.ts +28 -0
  85. package/dist/storage/SessionRepository.js +151 -0
  86. package/dist/storage/SessionRepository.js.map +1 -0
  87. package/dist/types/domain.d.ts +85 -0
  88. package/dist/types/domain.js +2 -0
  89. package/dist/types/domain.js.map +1 -0
  90. package/dist/utils/constants.d.ts +10 -0
  91. package/dist/utils/constants.js +11 -0
  92. package/dist/utils/constants.js.map +1 -0
  93. package/dist/utils/errors.d.ts +2 -0
  94. package/dist/utils/errors.js +12 -0
  95. package/dist/utils/errors.js.map +1 -0
  96. package/dist/utils/files.d.ts +4 -0
  97. package/dist/utils/files.js +28 -0
  98. package/dist/utils/files.js.map +1 -0
  99. package/dist/utils/json.d.ts +2 -0
  100. package/dist/utils/json.js +10 -0
  101. package/dist/utils/json.js.map +1 -0
  102. package/dist/utils/mask.d.ts +2 -0
  103. package/dist/utils/mask.js +10 -0
  104. package/dist/utils/mask.js.map +1 -0
  105. package/dist/utils/query.d.ts +1 -0
  106. package/dist/utils/query.js +17 -0
  107. package/dist/utils/query.js.map +1 -0
  108. package/package.json +92 -0
@@ -0,0 +1,74 @@
1
+ import { createCipheriv, createDecipheriv, randomBytes, scryptSync } from 'node:crypto';
2
+ import { getEncryptedSessionPath, getPlainSessionPath } from '../config/paths.js';
3
+ import { asError } from '../utils/errors.js';
4
+ import { deleteFileIfExists, readTextFile, writePrivateFile } from './FileStorage.js';
5
+ export class EncryptedFileCredentialStore {
6
+ kind = 'encrypted-file';
7
+ async save(session, options) {
8
+ const masterKey = options?.masterKey;
9
+ if (!masterKey) {
10
+ throw new Error('Encrypted file storage requires a master key. Set BSTACK_MASTER_KEY or provide one interactively.');
11
+ }
12
+ const payload = encryptPayload(session, masterKey);
13
+ await writePrivateFile(getEncryptedSessionPath(), JSON.stringify(payload, null, 2));
14
+ }
15
+ async load(options) {
16
+ const encrypted = await readTextFile(getEncryptedSessionPath());
17
+ if (!encrypted) {
18
+ return null;
19
+ }
20
+ const masterKey = options?.masterKey;
21
+ if (!masterKey) {
22
+ throw new Error('Stored session is encrypted. Set BSTACK_MASTER_KEY or login again with a different storage strategy.');
23
+ }
24
+ try {
25
+ return decryptPayload(JSON.parse(encrypted), masterKey);
26
+ }
27
+ catch (error) {
28
+ throw new Error(`Unable to decrypt saved session: ${asError(error).message}`);
29
+ }
30
+ }
31
+ async clear() {
32
+ await deleteFileIfExists(getEncryptedSessionPath());
33
+ }
34
+ }
35
+ export class PlainFileCredentialStore {
36
+ kind = 'plain-file';
37
+ async save(session) {
38
+ await writePrivateFile(getPlainSessionPath(), JSON.stringify(session, null, 2));
39
+ }
40
+ async load() {
41
+ const raw = await readTextFile(getPlainSessionPath());
42
+ return raw ? JSON.parse(raw) : null;
43
+ }
44
+ async clear() {
45
+ await deleteFileIfExists(getPlainSessionPath());
46
+ }
47
+ }
48
+ function encryptPayload(payload, masterKey) {
49
+ const iv = randomBytes(12);
50
+ const salt = randomBytes(16);
51
+ const key = scryptSync(masterKey, salt, 32);
52
+ const cipher = createCipheriv('aes-256-gcm', key, iv);
53
+ const ciphertext = Buffer.concat([
54
+ cipher.update(JSON.stringify(payload), 'utf8'),
55
+ cipher.final(),
56
+ ]);
57
+ return {
58
+ iv: iv.toString('base64'),
59
+ salt: salt.toString('base64'),
60
+ authTag: cipher.getAuthTag().toString('base64'),
61
+ ciphertext: ciphertext.toString('base64'),
62
+ };
63
+ }
64
+ function decryptPayload(payload, masterKey) {
65
+ const key = scryptSync(masterKey, Buffer.from(payload.salt, 'base64'), 32);
66
+ const decipher = createDecipheriv('aes-256-gcm', key, Buffer.from(payload.iv, 'base64'));
67
+ decipher.setAuthTag(Buffer.from(payload.authTag, 'base64'));
68
+ const plaintext = Buffer.concat([
69
+ decipher.update(Buffer.from(payload.ciphertext, 'base64')),
70
+ decipher.final(),
71
+ ]);
72
+ return JSON.parse(plaintext.toString('utf8'));
73
+ }
74
+ //# sourceMappingURL=FileCredentialStores.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FileCredentialStores.js","sourceRoot":"","sources":["../../src/storage/FileCredentialStores.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ export declare function writePrivateFile(filePath: string, content: string): Promise<void>;
2
+ export declare function readTextFile(filePath: string): Promise<string | null>;
3
+ export declare function deleteFileIfExists(filePath: string): Promise<void>;
@@ -0,0 +1,18 @@
1
+ import { chmod, readFile, rm, writeFile } from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import { ensureDir, fileExists } from '../utils/files.js';
4
+ export async function writePrivateFile(filePath, content) {
5
+ await ensureDir(path.dirname(filePath));
6
+ await writeFile(filePath, content, { encoding: 'utf8', mode: 0o600 });
7
+ await chmod(filePath, 0o600);
8
+ }
9
+ export async function readTextFile(filePath) {
10
+ if (!(await fileExists(filePath))) {
11
+ return null;
12
+ }
13
+ return readFile(filePath, 'utf8');
14
+ }
15
+ export async function deleteFileIfExists(filePath) {
16
+ await rm(filePath, { force: true });
17
+ }
18
+ //# sourceMappingURL=FileStorage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FileStorage.js","sourceRoot":"","sources":["../../src/storage/FileStorage.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAClE,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAE1D,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,QAAgB,EAAE,OAAe;IACtE,MAAM,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC;IACxC,MAAM,SAAS,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IACtE,MAAM,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;AAC/B,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,QAAgB;IACjD,IAAI,CAAC,CAAC,MAAM,UAAU,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC;QAClC,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;AACpC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,QAAgB;IACvD,MAAM,EAAE,CAAC,QAAQ,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;AACtC,CAAC"}
@@ -0,0 +1,9 @@
1
+ import type { StoredSession } from '../types/domain.js';
2
+ import type { CredentialStore } from './CredentialStore.js';
3
+ export declare class KeytarCredentialStore implements CredentialStore {
4
+ readonly kind: "keychain";
5
+ isAvailable(): Promise<boolean>;
6
+ save(session: StoredSession): Promise<void>;
7
+ load(): Promise<StoredSession | null>;
8
+ clear(): Promise<void>;
9
+ }
@@ -0,0 +1,40 @@
1
+ import { KEYTAR_SERVICE } from '../utils/constants.js';
2
+ const ACCOUNT_NAME = 'bstack';
3
+ async function loadKeytar() {
4
+ try {
5
+ const module = await import('keytar');
6
+ return module.default ?? module;
7
+ }
8
+ catch {
9
+ return null;
10
+ }
11
+ }
12
+ export class KeytarCredentialStore {
13
+ kind = 'keychain';
14
+ async isAvailable() {
15
+ return (await loadKeytar()) !== null;
16
+ }
17
+ async save(session) {
18
+ const keytar = await loadKeytar();
19
+ if (!keytar) {
20
+ throw new Error('OS keychain support is unavailable on this system.');
21
+ }
22
+ await keytar.setPassword(KEYTAR_SERVICE, ACCOUNT_NAME, JSON.stringify(session));
23
+ }
24
+ async load() {
25
+ const keytar = await loadKeytar();
26
+ if (!keytar) {
27
+ return null;
28
+ }
29
+ const raw = await keytar.getPassword(KEYTAR_SERVICE, ACCOUNT_NAME);
30
+ return raw ? JSON.parse(raw) : null;
31
+ }
32
+ async clear() {
33
+ const keytar = await loadKeytar();
34
+ if (!keytar) {
35
+ return;
36
+ }
37
+ await keytar.deletePassword(KEYTAR_SERVICE, ACCOUNT_NAME);
38
+ }
39
+ }
40
+ //# sourceMappingURL=KeytarCredentialStore.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"KeytarCredentialStore.js","sourceRoot":"","sources":["../../src/storage/KeytarCredentialStore.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAIvD,MAAM,YAAY,GAAG,QAAQ,CAAC;AAQ9B,KAAK,UAAU,UAAU;IACvB,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,QAAQ,CAAC,CAAC;QACtC,OAAO,MAAM,CAAC,OAAO,IAAK,MAAkC,CAAC;IAC/D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,MAAM,OAAO,qBAAqB;IAChB,IAAI,GAAG,UAAmB,CAAC;IAEpC,KAAK,CAAC,WAAW;QACtB,OAAO,CAAC,MAAM,UAAU,EAAE,CAAC,KAAK,IAAI,CAAC;IACvC,CAAC;IAEM,KAAK,CAAC,IAAI,CAAC,OAAsB;QACtC,MAAM,MAAM,GAAG,MAAM,UAAU,EAAE,CAAC;QAClC,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,MAAM,IAAI,KAAK,CAAC,oDAAoD,CAAC,CAAC;QACxE,CAAC;QAED,MAAM,MAAM,CAAC,WAAW,CAAC,cAAc,EAAE,YAAY,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC;IAClF,CAAC;IAEM,KAAK,CAAC,IAAI;QACf,MAAM,MAAM,GAAG,MAAM,UAAU,EAAE,CAAC;QAClC,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,OAAO,IAAI,CAAC;QACd,CAAC;QAED,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,WAAW,CAAC,cAAc,EAAE,YAAY,CAAC,CAAC;QACnE,OAAO,GAAG,CAAC,CAAC,CAAE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAmB,CAAC,CAAC,CAAC,IAAI,CAAC;IACzD,CAAC;IAEM,KAAK,CAAC,KAAK;QAChB,MAAM,MAAM,GAAG,MAAM,UAAU,EAAE,CAAC;QAClC,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,OAAO;QACT,CAAC;QAED,MAAM,MAAM,CAAC,cAAc,CAAC,cAAc,EAAE,YAAY,CAAC,CAAC;IAC5D,CAAC;CACF"}
@@ -0,0 +1,28 @@
1
+ import type { LastResponseRecord, SessionStatus, StorageStrategy, StoredSession } from '../types/domain.js';
2
+ export declare class SessionRepository {
3
+ private readonly keytarStore;
4
+ private readonly encryptedFileStore;
5
+ private readonly plainFileStore;
6
+ resolveStrategy(preferred: StorageStrategy, options?: {
7
+ masterKey?: string;
8
+ allowPlainText?: boolean;
9
+ }): Promise<Exclude<StorageStrategy, 'auto'>>;
10
+ save(session: StoredSession, options?: {
11
+ masterKey?: string;
12
+ allowPlainText?: boolean;
13
+ }): Promise<void>;
14
+ load(options?: {
15
+ masterKey?: string;
16
+ }): Promise<StoredSession | null>;
17
+ clear(): Promise<void>;
18
+ getStatus(): Promise<SessionStatus>;
19
+ noteValidationSuccess(username: string): Promise<void>;
20
+ noteValidationFailure(message: string): Promise<void>;
21
+ setCurrentFramework(framework: SessionStatus['currentFramework']): Promise<void>;
22
+ noteAction(label: string): Promise<void>;
23
+ saveLastResponse(record: LastResponseRecord): Promise<void>;
24
+ getLastResponse(): Promise<LastResponseRecord | null>;
25
+ private getStore;
26
+ private readConfig;
27
+ private saveConfig;
28
+ }
@@ -0,0 +1,151 @@
1
+ import { readFile, writeFile } from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import { getConfigPath, getLastResponsePath } from '../config/paths.js';
4
+ import { ensureDir } from '../utils/files.js';
5
+ import { EncryptedFileCredentialStore, PlainFileCredentialStore } from './FileCredentialStores.js';
6
+ import { KeytarCredentialStore } from './KeytarCredentialStore.js';
7
+ export class SessionRepository {
8
+ keytarStore = new KeytarCredentialStore();
9
+ encryptedFileStore = new EncryptedFileCredentialStore();
10
+ plainFileStore = new PlainFileCredentialStore();
11
+ async resolveStrategy(preferred, options) {
12
+ if (preferred === 'keychain') {
13
+ if (await this.keytarStore.isAvailable()) {
14
+ return 'keychain';
15
+ }
16
+ throw new Error('OS keychain support is unavailable. Use encrypted-file or plain-file.');
17
+ }
18
+ if (preferred === 'encrypted-file') {
19
+ if (!options?.masterKey) {
20
+ throw new Error('Encrypted-file storage requires a master key. Set BSTACK_MASTER_KEY or choose a different storage strategy.');
21
+ }
22
+ return 'encrypted-file';
23
+ }
24
+ if (preferred === 'plain-file') {
25
+ if (!options?.allowPlainText) {
26
+ throw new Error('Plain-file storage is disabled by default. Re-run with --allow-plain-storage to confirm.');
27
+ }
28
+ return 'plain-file';
29
+ }
30
+ if (await this.keytarStore.isAvailable()) {
31
+ return 'keychain';
32
+ }
33
+ if (options?.masterKey) {
34
+ return 'encrypted-file';
35
+ }
36
+ if (options?.allowPlainText) {
37
+ return 'plain-file';
38
+ }
39
+ throw new Error('Auto storage could not resolve a secure backend. Set BSTACK_MASTER_KEY for encrypted-file storage, or use --allow-plain-storage for plain-file fallback.');
40
+ }
41
+ async save(session, options) {
42
+ const store = await this.getStore(session.storageStrategy);
43
+ await store.save(session, options);
44
+ await this.saveConfig({
45
+ storageStrategy: session.storageStrategy,
46
+ username: session.username,
47
+ updatedAt: session.updatedAt,
48
+ });
49
+ }
50
+ async load(options) {
51
+ const config = await this.readConfig();
52
+ if (!config.storageStrategy) {
53
+ return null;
54
+ }
55
+ const store = await this.getStore(config.storageStrategy);
56
+ return store.load(options);
57
+ }
58
+ async clear() {
59
+ await this.keytarStore.clear();
60
+ await this.encryptedFileStore.clear();
61
+ await this.plainFileStore.clear();
62
+ await this.saveConfig({});
63
+ }
64
+ async getStatus() {
65
+ const config = await this.readConfig();
66
+ return {
67
+ loggedIn: Boolean(config.username && config.storageStrategy),
68
+ storageStrategy: config.storageStrategy,
69
+ username: config.username,
70
+ savedAt: config.updatedAt,
71
+ authSource: config.storageStrategy,
72
+ connectionState: config.username && config.storageStrategy ? 'saved-unvalidated' : 'disconnected',
73
+ lastValidatedAt: config.lastValidatedAt,
74
+ lastValidationError: config.lastValidationError,
75
+ currentFramework: config.currentFramework,
76
+ lastActionLabel: config.lastActionLabel,
77
+ lastActionAt: config.lastActionAt,
78
+ };
79
+ }
80
+ async noteValidationSuccess(username) {
81
+ const config = await this.readConfig();
82
+ await this.saveConfig({
83
+ ...config,
84
+ username,
85
+ lastValidatedAt: new Date().toISOString(),
86
+ lastValidationError: undefined,
87
+ });
88
+ }
89
+ async noteValidationFailure(message) {
90
+ const config = await this.readConfig();
91
+ await this.saveConfig({
92
+ ...config,
93
+ lastValidationError: message,
94
+ });
95
+ }
96
+ async setCurrentFramework(framework) {
97
+ const config = await this.readConfig();
98
+ await this.saveConfig({
99
+ ...config,
100
+ currentFramework: framework,
101
+ });
102
+ }
103
+ async noteAction(label) {
104
+ const config = await this.readConfig();
105
+ await this.saveConfig({
106
+ ...config,
107
+ lastActionLabel: label,
108
+ lastActionAt: new Date().toISOString(),
109
+ });
110
+ }
111
+ async saveLastResponse(record) {
112
+ await ensureDir(path.dirname(getLastResponsePath()));
113
+ await writeFile(getLastResponsePath(), JSON.stringify(record, null, 2), 'utf8');
114
+ }
115
+ async getLastResponse() {
116
+ try {
117
+ const raw = await readFile(getLastResponsePath(), 'utf8');
118
+ return JSON.parse(raw);
119
+ }
120
+ catch {
121
+ return null;
122
+ }
123
+ }
124
+ async getStore(strategy) {
125
+ switch (strategy) {
126
+ case 'keychain':
127
+ return this.keytarStore;
128
+ case 'encrypted-file':
129
+ return this.encryptedFileStore;
130
+ case 'plain-file':
131
+ return this.plainFileStore;
132
+ }
133
+ }
134
+ async readConfig() {
135
+ try {
136
+ const raw = await readFile(getConfigPath(), 'utf8');
137
+ return JSON.parse(raw);
138
+ }
139
+ catch {
140
+ return {};
141
+ }
142
+ }
143
+ async saveConfig(config) {
144
+ await ensureDir(path.dirname(getConfigPath()));
145
+ await writeFile(getConfigPath(), JSON.stringify(config, null, 2), {
146
+ encoding: 'utf8',
147
+ mode: 0o600,
148
+ });
149
+ }
150
+ }
151
+ //# sourceMappingURL=SessionRepository.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SessionRepository.js","sourceRoot":"","sources":["../../src/storage/SessionRepository.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,85 @@
1
+ export type FrameworkKey = 'auth' | 'appium' | 'maestro' | 'espresso' | 'flutter-android' | 'flutter-ios' | 'detox-android' | 'xcuitest' | 'media';
2
+ export type ResourceKey = 'auth' | 'apps' | 'test-suites' | 'test-packages' | 'app-client' | 'builds' | 'sessions' | 'media' | 'plan';
3
+ export type OperationKey = 'validate' | 'status' | 'list' | 'list-group' | 'get' | 'upload' | 'delete' | 'run' | 'stop' | 'update-status' | 'usage';
4
+ export type StorageStrategy = 'auto' | 'keychain' | 'encrypted-file' | 'plain-file';
5
+ export type AuthSource = 'environment' | 'keychain' | 'encrypted-file' | 'plain-file';
6
+ export type ConnectionState = 'connected' | 'saved-unvalidated' | 'invalid' | 'disconnected';
7
+ export interface StoredSession {
8
+ username: string;
9
+ accessKey: string;
10
+ storageStrategy: Exclude<StorageStrategy, 'auto'>;
11
+ createdAt: string;
12
+ updatedAt: string;
13
+ }
14
+ export interface SessionStatus {
15
+ loggedIn: boolean;
16
+ storageStrategy?: Exclude<StorageStrategy, 'auto'>;
17
+ username?: string;
18
+ savedAt?: string;
19
+ authSource?: AuthSource;
20
+ connectionState?: ConnectionState;
21
+ lastValidatedAt?: string;
22
+ lastValidationError?: string;
23
+ currentFramework?: FrameworkKey;
24
+ lastActionLabel?: string;
25
+ lastActionAt?: string;
26
+ }
27
+ export interface AuthStatus {
28
+ valid: boolean;
29
+ username: string;
30
+ planName?: string;
31
+ parallelSessionsRunning?: number;
32
+ parallelSessionsMaxAllowed?: number;
33
+ queuedSessions?: number;
34
+ queuedSessionsMaxAllowed?: number;
35
+ }
36
+ export interface UploadedArtifact {
37
+ id?: string;
38
+ url?: string;
39
+ name?: string;
40
+ version?: string;
41
+ uploadedAt?: string;
42
+ expiry?: string;
43
+ customId?: string;
44
+ shareableId?: string;
45
+ framework?: string;
46
+ raw: unknown;
47
+ }
48
+ export interface BuildSummary {
49
+ id: string;
50
+ name?: string;
51
+ project?: string;
52
+ status?: string;
53
+ duration?: number | string;
54
+ startTime?: string;
55
+ browserUrl?: string;
56
+ raw: unknown;
57
+ }
58
+ export interface SessionSummary {
59
+ id: string;
60
+ name?: string;
61
+ status?: string;
62
+ device?: string;
63
+ os?: string;
64
+ osVersion?: string;
65
+ browserUrl?: string;
66
+ publicUrl?: string;
67
+ buildId?: string;
68
+ buildName?: string;
69
+ raw: unknown;
70
+ }
71
+ export interface ApiErrorShape {
72
+ statusCode?: number;
73
+ code: string;
74
+ message: string;
75
+ details?: unknown;
76
+ retryable: boolean;
77
+ }
78
+ export interface LastResponseRecord {
79
+ at: string;
80
+ command?: string;
81
+ framework?: FrameworkKey;
82
+ resource?: ResourceKey;
83
+ operation?: OperationKey;
84
+ payload: unknown;
85
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=domain.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"domain.js","sourceRoot":"","sources":["../../src/types/domain.ts"],"names":[],"mappings":""}
@@ -0,0 +1,10 @@
1
+ export declare const APP_NAME = "bstack";
2
+ export declare const KEYTAR_SERVICE = "bstack";
3
+ export declare const CONFIG_BASENAME = "config.json";
4
+ export declare const ENCRYPTED_SESSION_BASENAME = "session.enc";
5
+ export declare const PLAIN_SESSION_BASENAME = "session.json";
6
+ export declare const LAST_RESPONSE_BASENAME = "last-response.json";
7
+ export declare const DEFAULT_BASE_URL = "https://api-cloud.browserstack.com";
8
+ export declare const DEFAULT_HTTP_TIMEOUT_MS = 30000;
9
+ export declare const DEFAULT_RETRY_ATTEMPTS = 3;
10
+ export declare const MAX_EXPORT_BYTES: number;
@@ -0,0 +1,11 @@
1
+ export const APP_NAME = 'bstack';
2
+ export const KEYTAR_SERVICE = 'bstack';
3
+ export const CONFIG_BASENAME = 'config.json';
4
+ export const ENCRYPTED_SESSION_BASENAME = 'session.enc';
5
+ export const PLAIN_SESSION_BASENAME = 'session.json';
6
+ export const LAST_RESPONSE_BASENAME = 'last-response.json';
7
+ export const DEFAULT_BASE_URL = 'https://api-cloud.browserstack.com';
8
+ export const DEFAULT_HTTP_TIMEOUT_MS = 30_000;
9
+ export const DEFAULT_RETRY_ATTEMPTS = 3;
10
+ export const MAX_EXPORT_BYTES = 10 * 1024 * 1024;
11
+ //# sourceMappingURL=constants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.js","sourceRoot":"","sources":["../../src/utils/constants.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,QAAQ,GAAG,QAAQ,CAAC;AACjC,MAAM,CAAC,MAAM,cAAc,GAAG,QAAQ,CAAC;AACvC,MAAM,CAAC,MAAM,eAAe,GAAG,aAAa,CAAC;AAC7C,MAAM,CAAC,MAAM,0BAA0B,GAAG,aAAa,CAAC;AACxD,MAAM,CAAC,MAAM,sBAAsB,GAAG,cAAc,CAAC;AACrD,MAAM,CAAC,MAAM,sBAAsB,GAAG,oBAAoB,CAAC;AAC3D,MAAM,CAAC,MAAM,gBAAgB,GAAG,oCAAoC,CAAC;AACrE,MAAM,CAAC,MAAM,uBAAuB,GAAG,MAAM,CAAC;AAC9C,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,CAAC;AACxC,MAAM,CAAC,MAAM,gBAAgB,GAAG,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare function asError(error: unknown): Error;
2
+ export declare function ensure(value: unknown, message: string): asserts value;
@@ -0,0 +1,12 @@
1
+ export function asError(error) {
2
+ if (error instanceof Error) {
3
+ return error;
4
+ }
5
+ return new Error(String(error));
6
+ }
7
+ export function ensure(value, message) {
8
+ if (!value) {
9
+ throw new Error(message);
10
+ }
11
+ }
12
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/utils/errors.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,OAAO,CAAC,KAAc;IACpC,IAAI,KAAK,YAAY,KAAK,EAAE,CAAC;QAC3B,OAAO,KAAK,CAAC;IACf,CAAC;IAED,OAAO,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;AAClC,CAAC;AAED,MAAM,UAAU,MAAM,CAAC,KAAc,EAAE,OAAe;IACpD,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC;IAC3B,CAAC;AACH,CAAC"}
@@ -0,0 +1,4 @@
1
+ export declare function ensureDir(dirPath: string): Promise<void>;
2
+ export declare function fileExists(filePath: string): Promise<boolean>;
3
+ export declare function assertReadableFile(filePath: string): Promise<void>;
4
+ export declare function expandHome(inputPath: string): string;
@@ -0,0 +1,28 @@
1
+ import { access, constants, mkdir, stat } from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ export async function ensureDir(dirPath) {
4
+ await mkdir(dirPath, { recursive: true, mode: 0o700 });
5
+ }
6
+ export async function fileExists(filePath) {
7
+ try {
8
+ await access(filePath, constants.F_OK);
9
+ return true;
10
+ }
11
+ catch {
12
+ return false;
13
+ }
14
+ }
15
+ export async function assertReadableFile(filePath) {
16
+ await access(filePath, constants.R_OK);
17
+ const fileStats = await stat(filePath);
18
+ if (!fileStats.isFile()) {
19
+ throw new Error(`Path is not a regular file: ${filePath}`);
20
+ }
21
+ }
22
+ export function expandHome(inputPath) {
23
+ if (!inputPath.startsWith('~/')) {
24
+ return inputPath;
25
+ }
26
+ return path.join(process.env.HOME ?? '', inputPath.slice(2));
27
+ }
28
+ //# sourceMappingURL=files.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"files.js","sourceRoot":"","sources":["../../src/utils/files.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAClE,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,OAAe;IAC7C,MAAM,KAAK,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;AACzD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,QAAgB;IAC/C,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,QAAQ,EAAE,SAAS,CAAC,IAAI,CAAC,CAAC;QACvC,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,QAAgB;IACvD,MAAM,MAAM,CAAC,QAAQ,EAAE,SAAS,CAAC,IAAI,CAAC,CAAC;IACvC,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,CAAC;IAEvC,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,EAAE,CAAC;QACxB,MAAM,IAAI,KAAK,CAAC,+BAA+B,QAAQ,EAAE,CAAC,CAAC;IAC7D,CAAC;AACH,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,SAAiB;IAC1C,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;QAChC,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,OAAO,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,EAAE,EAAE,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;AAC/D,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare function parseJsonInput<T>(value: string | undefined, fallback: T): T;
2
+ export declare function prettyJson(value: unknown): string;
@@ -0,0 +1,10 @@
1
+ export function parseJsonInput(value, fallback) {
2
+ if (!value) {
3
+ return fallback;
4
+ }
5
+ return JSON.parse(value);
6
+ }
7
+ export function prettyJson(value) {
8
+ return JSON.stringify(value, null, 2);
9
+ }
10
+ //# sourceMappingURL=json.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"json.js","sourceRoot":"","sources":["../../src/utils/json.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,cAAc,CAAI,KAAyB,EAAE,QAAW;IACtE,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,CAAM,CAAC;AAChC,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,KAAc;IACvC,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;AACxC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare function maskSecret(secret: string, visible?: number): string;
2
+ export declare function maskBasicAuth(username: string, accessKey: string): string;
@@ -0,0 +1,10 @@
1
+ export function maskSecret(secret, visible = 4) {
2
+ if (secret.length <= visible) {
3
+ return '*'.repeat(secret.length);
4
+ }
5
+ return `${'*'.repeat(Math.max(secret.length - visible, 0))}${secret.slice(-visible)}`;
6
+ }
7
+ export function maskBasicAuth(username, accessKey) {
8
+ return `${username}:${maskSecret(accessKey)}`;
9
+ }
10
+ //# sourceMappingURL=mask.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mask.js","sourceRoot":"","sources":["../../src/utils/mask.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,UAAU,CAAC,MAAc,EAAE,OAAO,GAAG,CAAC;IACpD,IAAI,MAAM,CAAC,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,OAAO,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IACnC,CAAC;IAED,OAAO,GAAG,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,GAAG,OAAO,EAAE,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;AACxF,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,QAAgB,EAAE,SAAiB;IAC/D,OAAO,GAAG,QAAQ,IAAI,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;AAChD,CAAC"}
@@ -0,0 +1 @@
1
+ export declare function buildQuery(input: Record<string, unknown>): URLSearchParams;
@@ -0,0 +1,17 @@
1
+ export function buildQuery(input) {
2
+ const params = new URLSearchParams();
3
+ for (const [key, value] of Object.entries(input)) {
4
+ if (value === undefined || value === null || value === '') {
5
+ continue;
6
+ }
7
+ if (Array.isArray(value)) {
8
+ for (const item of value) {
9
+ params.append(key, String(item));
10
+ }
11
+ continue;
12
+ }
13
+ params.set(key, String(value));
14
+ }
15
+ return params;
16
+ }
17
+ //# sourceMappingURL=query.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"query.js","sourceRoot":"","sources":["../../src/utils/query.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,UAAU,CAAC,KAA8B;IACvD,MAAM,MAAM,GAAG,IAAI,eAAe,EAAE,CAAC;IAErC,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACjD,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,EAAE,EAAE,CAAC;YAC1D,SAAS;QACX,CAAC;QAED,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;YACzB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;gBACzB,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;YACnC,CAAC;YACD,SAAS;QACX,CAAC;QAED,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;IACjC,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC"}