@lhremote/core 0.0.0 → 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 (218) hide show
  1. package/LICENSE +661 -0
  2. package/dist/cdp/client.d.ts +100 -0
  3. package/dist/cdp/client.d.ts.map +1 -0
  4. package/dist/cdp/client.integration.test.d.ts +2 -0
  5. package/dist/cdp/client.integration.test.d.ts.map +1 -0
  6. package/dist/cdp/client.integration.test.js +70 -0
  7. package/dist/cdp/client.integration.test.js.map +1 -0
  8. package/dist/cdp/client.js +286 -0
  9. package/dist/cdp/client.js.map +1 -0
  10. package/dist/cdp/client.test.d.ts +2 -0
  11. package/dist/cdp/client.test.d.ts.map +1 -0
  12. package/dist/cdp/client.test.js +269 -0
  13. package/dist/cdp/client.test.js.map +1 -0
  14. package/dist/cdp/discovery.d.ts +14 -0
  15. package/dist/cdp/discovery.d.ts.map +1 -0
  16. package/dist/cdp/discovery.integration.test.d.ts +2 -0
  17. package/dist/cdp/discovery.integration.test.d.ts.map +1 -0
  18. package/dist/cdp/discovery.integration.test.js +25 -0
  19. package/dist/cdp/discovery.integration.test.js.map +1 -0
  20. package/dist/cdp/discovery.js +31 -0
  21. package/dist/cdp/discovery.js.map +1 -0
  22. package/dist/cdp/discovery.test.d.ts +2 -0
  23. package/dist/cdp/discovery.test.d.ts.map +1 -0
  24. package/dist/cdp/discovery.test.js +54 -0
  25. package/dist/cdp/discovery.test.js.map +1 -0
  26. package/dist/cdp/errors.d.ts +28 -0
  27. package/dist/cdp/errors.d.ts.map +1 -0
  28. package/dist/cdp/errors.js +40 -0
  29. package/dist/cdp/errors.js.map +1 -0
  30. package/dist/cdp/errors.test.d.ts +2 -0
  31. package/dist/cdp/errors.test.d.ts.map +1 -0
  32. package/dist/cdp/errors.test.js +35 -0
  33. package/dist/cdp/errors.test.js.map +1 -0
  34. package/dist/cdp/index.d.ts +5 -0
  35. package/dist/cdp/index.d.ts.map +1 -0
  36. package/dist/cdp/index.js +5 -0
  37. package/dist/cdp/index.js.map +1 -0
  38. package/dist/cdp/instance-discovery.d.ts +30 -0
  39. package/dist/cdp/instance-discovery.d.ts.map +1 -0
  40. package/dist/cdp/instance-discovery.integration.test.d.ts +2 -0
  41. package/dist/cdp/instance-discovery.integration.test.d.ts.map +1 -0
  42. package/dist/cdp/instance-discovery.integration.test.js +34 -0
  43. package/dist/cdp/instance-discovery.integration.test.js.map +1 -0
  44. package/dist/cdp/instance-discovery.js +130 -0
  45. package/dist/cdp/instance-discovery.js.map +1 -0
  46. package/dist/cdp/instance-discovery.test.d.ts +2 -0
  47. package/dist/cdp/instance-discovery.test.d.ts.map +1 -0
  48. package/dist/cdp/instance-discovery.test.js +110 -0
  49. package/dist/cdp/instance-discovery.test.js.map +1 -0
  50. package/dist/cdp/testing/launch-chromium.d.ts +24 -0
  51. package/dist/cdp/testing/launch-chromium.d.ts.map +1 -0
  52. package/dist/cdp/testing/launch-chromium.js +90 -0
  53. package/dist/cdp/testing/launch-chromium.js.map +1 -0
  54. package/dist/db/client.d.ts +13 -0
  55. package/dist/db/client.d.ts.map +1 -0
  56. package/dist/db/client.integration.test.d.ts +2 -0
  57. package/dist/db/client.integration.test.d.ts.map +1 -0
  58. package/dist/db/client.integration.test.js +59 -0
  59. package/dist/db/client.integration.test.js.map +1 -0
  60. package/dist/db/client.js +17 -0
  61. package/dist/db/client.js.map +1 -0
  62. package/dist/db/client.test.d.ts +2 -0
  63. package/dist/db/client.test.d.ts.map +1 -0
  64. package/dist/db/client.test.js +46 -0
  65. package/dist/db/client.test.js.map +1 -0
  66. package/dist/db/discovery.d.ts +14 -0
  67. package/dist/db/discovery.d.ts.map +1 -0
  68. package/dist/db/discovery.integration.test.d.ts +2 -0
  69. package/dist/db/discovery.integration.test.d.ts.map +1 -0
  70. package/dist/db/discovery.integration.test.js +99 -0
  71. package/dist/db/discovery.integration.test.js.map +1 -0
  72. package/dist/db/discovery.js +74 -0
  73. package/dist/db/discovery.js.map +1 -0
  74. package/dist/db/discovery.test.d.ts +2 -0
  75. package/dist/db/discovery.test.d.ts.map +1 -0
  76. package/dist/db/discovery.test.js +123 -0
  77. package/dist/db/discovery.test.js.map +1 -0
  78. package/dist/db/errors.d.ts +21 -0
  79. package/dist/db/errors.d.ts.map +1 -0
  80. package/dist/db/errors.js +33 -0
  81. package/dist/db/errors.js.map +1 -0
  82. package/dist/db/errors.test.d.ts +2 -0
  83. package/dist/db/errors.test.d.ts.map +1 -0
  84. package/dist/db/errors.test.js +32 -0
  85. package/dist/db/errors.test.js.map +1 -0
  86. package/dist/db/index.d.ts +5 -0
  87. package/dist/db/index.d.ts.map +1 -0
  88. package/dist/db/index.js +5 -0
  89. package/dist/db/index.js.map +1 -0
  90. package/dist/db/repositories/index.d.ts +2 -0
  91. package/dist/db/repositories/index.d.ts.map +1 -0
  92. package/dist/db/repositories/index.js +2 -0
  93. package/dist/db/repositories/index.js.map +1 -0
  94. package/dist/db/repositories/profile.d.ts +32 -0
  95. package/dist/db/repositories/profile.d.ts.map +1 -0
  96. package/dist/db/repositories/profile.integration.test.d.ts +2 -0
  97. package/dist/db/repositories/profile.integration.test.d.ts.map +1 -0
  98. package/dist/db/repositories/profile.integration.test.js +119 -0
  99. package/dist/db/repositories/profile.integration.test.js.map +1 -0
  100. package/dist/db/repositories/profile.js +126 -0
  101. package/dist/db/repositories/profile.js.map +1 -0
  102. package/dist/db/repositories/profile.test.d.ts +2 -0
  103. package/dist/db/repositories/profile.test.d.ts.map +1 -0
  104. package/dist/db/repositories/profile.test.js +126 -0
  105. package/dist/db/repositories/profile.test.js.map +1 -0
  106. package/dist/db/testing/create-fixture.d.ts +8 -0
  107. package/dist/db/testing/create-fixture.d.ts.map +1 -0
  108. package/dist/db/testing/create-fixture.js +286 -0
  109. package/dist/db/testing/create-fixture.js.map +1 -0
  110. package/dist/db/testing/open-fixture.d.ts +14 -0
  111. package/dist/db/testing/open-fixture.d.ts.map +1 -0
  112. package/dist/db/testing/open-fixture.js +21 -0
  113. package/dist/db/testing/open-fixture.js.map +1 -0
  114. package/dist/index.d.ts +5 -1
  115. package/dist/index.d.ts.map +1 -1
  116. package/dist/index.js +7 -1
  117. package/dist/index.js.map +1 -1
  118. package/dist/services/app.d.ts +62 -0
  119. package/dist/services/app.d.ts.map +1 -0
  120. package/dist/services/app.js +198 -0
  121. package/dist/services/app.js.map +1 -0
  122. package/dist/services/app.test.d.ts +2 -0
  123. package/dist/services/app.test.d.ts.map +1 -0
  124. package/dist/services/app.test.js +265 -0
  125. package/dist/services/app.test.js.map +1 -0
  126. package/dist/services/errors.d.ts +45 -0
  127. package/dist/services/errors.d.ts.map +1 -0
  128. package/dist/services/errors.js +66 -0
  129. package/dist/services/errors.js.map +1 -0
  130. package/dist/services/errors.test.d.ts +2 -0
  131. package/dist/services/errors.test.d.ts.map +1 -0
  132. package/dist/services/errors.test.js +71 -0
  133. package/dist/services/errors.test.js.map +1 -0
  134. package/dist/services/index.d.ts +8 -0
  135. package/dist/services/index.d.ts.map +1 -0
  136. package/dist/services/index.js +8 -0
  137. package/dist/services/index.js.map +1 -0
  138. package/dist/services/instance-lifecycle.d.ts +38 -0
  139. package/dist/services/instance-lifecycle.d.ts.map +1 -0
  140. package/dist/services/instance-lifecycle.js +87 -0
  141. package/dist/services/instance-lifecycle.js.map +1 -0
  142. package/dist/services/instance-lifecycle.test.d.ts +2 -0
  143. package/dist/services/instance-lifecycle.test.d.ts.map +1 -0
  144. package/dist/services/instance-lifecycle.test.js +152 -0
  145. package/dist/services/instance-lifecycle.test.js.map +1 -0
  146. package/dist/services/instance.d.ts +50 -0
  147. package/dist/services/instance.d.ts.map +1 -0
  148. package/dist/services/instance.js +121 -0
  149. package/dist/services/instance.js.map +1 -0
  150. package/dist/services/instance.test.d.ts +2 -0
  151. package/dist/services/instance.test.d.ts.map +1 -0
  152. package/dist/services/instance.test.js +181 -0
  153. package/dist/services/instance.test.js.map +1 -0
  154. package/dist/services/launcher.d.ts +51 -0
  155. package/dist/services/launcher.d.ts.map +1 -0
  156. package/dist/services/launcher.js +147 -0
  157. package/dist/services/launcher.js.map +1 -0
  158. package/dist/services/launcher.test.d.ts +2 -0
  159. package/dist/services/launcher.test.d.ts.map +1 -0
  160. package/dist/services/launcher.test.js +126 -0
  161. package/dist/services/launcher.test.js.map +1 -0
  162. package/dist/services/profile.d.ts +44 -0
  163. package/dist/services/profile.d.ts.map +1 -0
  164. package/dist/services/profile.js +83 -0
  165. package/dist/services/profile.js.map +1 -0
  166. package/dist/services/profile.test.d.ts +2 -0
  167. package/dist/services/profile.test.d.ts.map +1 -0
  168. package/dist/services/profile.test.js +145 -0
  169. package/dist/services/profile.test.js.map +1 -0
  170. package/dist/services/status.d.ts +33 -0
  171. package/dist/services/status.d.ts.map +1 -0
  172. package/dist/services/status.js +76 -0
  173. package/dist/services/status.js.map +1 -0
  174. package/dist/services/status.test.d.ts +2 -0
  175. package/dist/services/status.test.d.ts.map +1 -0
  176. package/dist/services/status.test.js +207 -0
  177. package/dist/services/status.test.js.map +1 -0
  178. package/dist/testing/e2e-helpers.d.ts +41 -0
  179. package/dist/testing/e2e-helpers.d.ts.map +1 -0
  180. package/dist/testing/e2e-helpers.js +111 -0
  181. package/dist/testing/e2e-helpers.js.map +1 -0
  182. package/dist/types/account.d.ts +13 -0
  183. package/dist/types/account.d.ts.map +1 -0
  184. package/dist/types/account.js +2 -0
  185. package/dist/types/account.js.map +1 -0
  186. package/dist/types/account.test.d.ts +2 -0
  187. package/dist/types/account.test.d.ts.map +1 -0
  188. package/dist/types/account.test.js +24 -0
  189. package/dist/types/account.test.js.map +1 -0
  190. package/dist/types/cdp.d.ts +18 -0
  191. package/dist/types/cdp.d.ts.map +1 -0
  192. package/dist/types/cdp.js +2 -0
  193. package/dist/types/cdp.js.map +1 -0
  194. package/dist/types/cdp.test.d.ts +2 -0
  195. package/dist/types/cdp.test.d.ts.map +1 -0
  196. package/dist/types/cdp.test.js +28 -0
  197. package/dist/types/cdp.test.js.map +1 -0
  198. package/dist/types/index.d.ts +4 -0
  199. package/dist/types/index.d.ts.map +1 -0
  200. package/dist/types/index.js +2 -0
  201. package/dist/types/index.js.map +1 -0
  202. package/dist/types/instance.d.ts +36 -0
  203. package/dist/types/instance.d.ts.map +1 -0
  204. package/dist/types/instance.js +8 -0
  205. package/dist/types/instance.js.map +1 -0
  206. package/dist/types/instance.test.d.ts +2 -0
  207. package/dist/types/instance.test.d.ts.map +1 -0
  208. package/dist/types/instance.test.js +57 -0
  209. package/dist/types/instance.test.js.map +1 -0
  210. package/dist/types/profile.d.ts +50 -0
  211. package/dist/types/profile.d.ts.map +1 -0
  212. package/dist/types/profile.js +8 -0
  213. package/dist/types/profile.js.map +1 -0
  214. package/dist/types/profile.test.d.ts +2 -0
  215. package/dist/types/profile.test.d.ts.map +1 -0
  216. package/dist/types/profile.test.js +103 -0
  217. package/dist/types/profile.test.js.map +1 -0
  218. package/package.json +20 -8
@@ -0,0 +1,147 @@
1
+ import { CDPClient, CDPConnectionError } from "../cdp/index.js";
2
+ import { LinkedHelperNotRunningError, ServiceError, StartInstanceError, } from "./errors.js";
3
+ /** Default CDP port for the LinkedHelper launcher process. */
4
+ const DEFAULT_LAUNCHER_PORT = 9222;
5
+ /**
6
+ * Controls the LinkedHelper launcher process via CDP.
7
+ *
8
+ * The launcher is the main Electron window that manages LinkedIn
9
+ * account instances. This service connects to it and provides
10
+ * methods to start/stop instances and query accounts.
11
+ */
12
+ export class LauncherService {
13
+ port;
14
+ host;
15
+ client = null;
16
+ constructor(port = DEFAULT_LAUNCHER_PORT, options) {
17
+ this.port = port;
18
+ this.host = options?.host ?? "127.0.0.1";
19
+ }
20
+ /**
21
+ * Connect to the LinkedHelper launcher via CDP.
22
+ *
23
+ * @throws {LinkedHelperNotRunningError} if the launcher is not reachable.
24
+ */
25
+ async connect() {
26
+ const client = new CDPClient(this.port, { host: this.host });
27
+ try {
28
+ await client.connect();
29
+ }
30
+ catch (error) {
31
+ if (error instanceof CDPConnectionError) {
32
+ throw new LinkedHelperNotRunningError(this.port);
33
+ }
34
+ throw error;
35
+ }
36
+ this.client = client;
37
+ }
38
+ /**
39
+ * Disconnect from the launcher.
40
+ */
41
+ disconnect() {
42
+ this.client?.disconnect();
43
+ this.client = null;
44
+ }
45
+ /**
46
+ * Start a LinkedHelper instance for the given account.
47
+ *
48
+ * @throws {StartInstanceError} if the instance fails to start.
49
+ */
50
+ async startInstance(accountId) {
51
+ const client = this.ensureConnected();
52
+ const result = await client.evaluate(`(async () => {
53
+ try {
54
+ const remote = require('@electron/remote');
55
+ const mainWindow = remote.getGlobal('mainWindow');
56
+ await mainWindow.startInstance({
57
+ linkedInAccount: { id: ${String(accountId)}, liId: ${String(accountId)} },
58
+ accountData: { id: ${String(accountId)}, liId: ${String(accountId)} },
59
+ instanceId: 1,
60
+ proxy: null,
61
+ license: null,
62
+ userId: null,
63
+ frontendSettings: {},
64
+ lhAccount: {},
65
+ zoomDefault: 0.9,
66
+ shouldBringToFront: true,
67
+ shouldStartRunningCampaigns: false,
68
+ });
69
+ return { success: true };
70
+ } catch (e) {
71
+ return { success: false, error: e.message };
72
+ }
73
+ })()`, true);
74
+ if (!result.success) {
75
+ throw new StartInstanceError(accountId, result.error);
76
+ }
77
+ }
78
+ /**
79
+ * Stop a running LinkedHelper instance.
80
+ */
81
+ async stopInstance(accountId) {
82
+ const client = this.ensureConnected();
83
+ await client.evaluate(`(async () => {
84
+ const remote = require('@electron/remote');
85
+ const mainWindow = remote.getGlobal('mainWindow');
86
+ return await mainWindow.instanceManager.stopInstance(${String(accountId)});
87
+ })()`, true);
88
+ }
89
+ /**
90
+ * Query the status of an instance for the given account.
91
+ */
92
+ async getInstanceStatus(accountId) {
93
+ const client = this.ensureConnected();
94
+ // NOTE: instanceManager.instances is always empty in the renderer process
95
+ // due to cross-process architecture (instances run as separate OS processes).
96
+ // This returns 'stopped' until a reliable IPC-based status query is implemented.
97
+ const status = await client.evaluate(`(() => {
98
+ const remote = require('@electron/remote');
99
+ const mainWindow = remote.getGlobal('mainWindow');
100
+ const im = mainWindow.instanceManager;
101
+ const instance = im.instances?.[${String(accountId)}];
102
+ return instance?.status ?? 'stopped';
103
+ })()`);
104
+ return status;
105
+ }
106
+ /**
107
+ * List all accounts configured in the LinkedHelper Electron store.
108
+ *
109
+ * Accounts are discovered from the `linkedInPasswords` store key whose
110
+ * entries use the format `userId:li:accountId`.
111
+ */
112
+ async listAccounts() {
113
+ const client = this.ensureConnected();
114
+ const accounts = await client.evaluate(`(() => {
115
+ const remote = require('@electron/remote');
116
+ const mainWindow = remote.getGlobal('mainWindow');
117
+ const store = mainWindow.electronStore;
118
+ const passwords = store.get('linkedInPasswords') ?? {};
119
+ return Object.keys(passwords)
120
+ .map(k => {
121
+ const parts = k.split(':li:');
122
+ if (parts.length !== 2) return null;
123
+ const accountId = Number(parts[1]);
124
+ if (Number.isNaN(accountId)) return null;
125
+ return {
126
+ id: accountId,
127
+ liId: accountId,
128
+ name: '',
129
+ email: undefined,
130
+ };
131
+ })
132
+ .filter(a => a !== null);
133
+ })()`);
134
+ return accounts ?? [];
135
+ }
136
+ /** Whether the service is currently connected to the launcher. */
137
+ get isConnected() {
138
+ return this.client !== null && this.client.isConnected;
139
+ }
140
+ ensureConnected() {
141
+ if (!this.client) {
142
+ throw new ServiceError("LauncherService is not connected");
143
+ }
144
+ return this.client;
145
+ }
146
+ }
147
+ //# sourceMappingURL=launcher.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"launcher.js","sourceRoot":"","sources":["../../src/services/launcher.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=launcher.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"launcher.test.d.ts","sourceRoot":"","sources":["../../src/services/launcher.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,126 @@
1
+ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
2
+ import { LinkedHelperNotRunningError, ServiceError, StartInstanceError, } from "./errors.js";
3
+ import { LauncherService } from "./launcher.js";
4
+ /**
5
+ * Shared CDPClient mocks — LauncherService creates exactly one CDPClient,
6
+ * so per-instance isolation (as in instance.test.ts) is unnecessary here.
7
+ */
8
+ const mockConnect = vi.fn();
9
+ const mockDisconnect = vi.fn();
10
+ const mockEvaluate = vi.fn();
11
+ const mockIsConnected = vi.fn().mockReturnValue(true);
12
+ vi.mock("../cdp/index.js", async (importOriginal) => {
13
+ const original = await importOriginal();
14
+ return {
15
+ CDPClient: vi.fn().mockImplementation(function () {
16
+ this.connect = mockConnect;
17
+ this.disconnect = mockDisconnect;
18
+ this.evaluate = mockEvaluate;
19
+ Object.defineProperty(this, "isConnected", {
20
+ get: mockIsConnected,
21
+ });
22
+ }),
23
+ CDPConnectionError: original.CDPConnectionError,
24
+ };
25
+ });
26
+ import { CDPConnectionError } from "../cdp/index.js";
27
+ afterEach(() => {
28
+ vi.restoreAllMocks();
29
+ });
30
+ describe("LauncherService", () => {
31
+ let service;
32
+ beforeEach(() => {
33
+ service = new LauncherService(9222);
34
+ mockConnect.mockResolvedValue(undefined);
35
+ mockEvaluate.mockResolvedValue(undefined);
36
+ });
37
+ describe("connect", () => {
38
+ it("creates a CDPClient and connects", async () => {
39
+ await service.connect();
40
+ expect(service.isConnected).toBe(true);
41
+ });
42
+ it("wraps CDPConnectionError into LinkedHelperNotRunningError", async () => {
43
+ mockConnect.mockRejectedValue(new CDPConnectionError("connection refused"));
44
+ await expect(service.connect()).rejects.toThrow(LinkedHelperNotRunningError);
45
+ });
46
+ it("re-throws non-CDP errors as-is", async () => {
47
+ mockConnect.mockRejectedValue(new TypeError("unexpected"));
48
+ await expect(service.connect()).rejects.toThrow(TypeError);
49
+ });
50
+ });
51
+ describe("disconnect", () => {
52
+ it("disconnects the CDPClient", async () => {
53
+ await service.connect();
54
+ service.disconnect();
55
+ expect(mockDisconnect).toHaveBeenCalled();
56
+ });
57
+ it("does not throw when not connected", () => {
58
+ expect(() => service.disconnect()).not.toThrow();
59
+ });
60
+ });
61
+ describe("startInstance", () => {
62
+ it("evaluates the startInstance expression", async () => {
63
+ await service.connect();
64
+ mockEvaluate.mockResolvedValue({ success: true });
65
+ await service.startInstance(42);
66
+ expect(mockEvaluate).toHaveBeenCalledWith(expect.stringContaining("startInstance"), true);
67
+ expect(mockEvaluate).toHaveBeenCalledWith(expect.stringContaining("42"), true);
68
+ });
69
+ it("throws StartInstanceError on failure", async () => {
70
+ await service.connect();
71
+ mockEvaluate.mockResolvedValue({
72
+ success: false,
73
+ error: "account is already running",
74
+ });
75
+ await expect(service.startInstance(42)).rejects.toThrow(StartInstanceError);
76
+ await expect(service.startInstance(42)).rejects.toThrow(/account is already running/);
77
+ });
78
+ it("throws ServiceError when not connected", async () => {
79
+ await expect(service.startInstance(42)).rejects.toThrow(ServiceError);
80
+ });
81
+ });
82
+ describe("stopInstance", () => {
83
+ it("evaluates the stopInstance expression", async () => {
84
+ await service.connect();
85
+ await service.stopInstance(42);
86
+ expect(mockEvaluate).toHaveBeenCalledWith(expect.stringContaining("stopInstance"), true);
87
+ });
88
+ it("throws ServiceError when not connected", async () => {
89
+ await expect(service.stopInstance(42)).rejects.toThrow(ServiceError);
90
+ });
91
+ });
92
+ describe("getInstanceStatus", () => {
93
+ it("returns the instance status", async () => {
94
+ await service.connect();
95
+ mockEvaluate.mockResolvedValue("running");
96
+ const status = await service.getInstanceStatus(42);
97
+ expect(status).toBe("running");
98
+ });
99
+ it("returns stopped when status is null", async () => {
100
+ await service.connect();
101
+ mockEvaluate.mockResolvedValue("stopped");
102
+ const status = await service.getInstanceStatus(42);
103
+ expect(status).toBe("stopped");
104
+ });
105
+ });
106
+ describe("listAccounts", () => {
107
+ it("returns parsed accounts", async () => {
108
+ await service.connect();
109
+ mockEvaluate.mockResolvedValue([
110
+ { id: 1, liId: 100, name: "Alice", email: "alice@test.com" },
111
+ { id: 2, liId: 200, name: "Bob" },
112
+ ]);
113
+ const accounts = await service.listAccounts();
114
+ expect(accounts).toHaveLength(2);
115
+ expect(accounts).toContainEqual(expect.objectContaining({ name: "Alice" }));
116
+ expect(accounts).toContainEqual(expect.objectContaining({ name: "Bob" }));
117
+ });
118
+ it("returns empty array when no accounts", async () => {
119
+ await service.connect();
120
+ mockEvaluate.mockResolvedValue(null);
121
+ const accounts = await service.listAccounts();
122
+ expect(accounts).toEqual([]);
123
+ });
124
+ });
125
+ });
126
+ //# sourceMappingURL=launcher.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"launcher.test.js","sourceRoot":"","sources":["../../src/services/launcher.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAEzE,OAAO,EACL,2BAA2B,EAC3B,YAAY,EACZ,kBAAkB,GACnB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAEhD;;;GAGG;AACH,MAAM,WAAW,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC;AAC5B,MAAM,cAAc,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC;AAC/B,MAAM,YAAY,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC;AAC7B,MAAM,eAAe,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC;AAEtD,EAAE,CAAC,IAAI,CAAC,iBAAiB,EAAE,KAAK,EAAE,cAAc,EAAE,EAAE;IAClD,MAAM,QAAQ,GAAG,MAAM,cAAc,EAAoC,CAAC;IAC1E,OAAO;QACL,SAAS,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,kBAAkB,CAAC;YACpC,IAAI,CAAC,OAAO,GAAG,WAAW,CAAC;YAC3B,IAAI,CAAC,UAAU,GAAG,cAAc,CAAC;YACjC,IAAI,CAAC,QAAQ,GAAG,YAAY,CAAC;YAC7B,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,aAAa,EAAE;gBACzC,GAAG,EAAE,eAAe;aACrB,CAAC,CAAC;QACL,CAAC,CAAC;QACF,kBAAkB,EAAE,QAAQ,CAAC,kBAAkB;KAChD,CAAC;AACJ,CAAC,CAAC,CAAC;AAEH,OAAO,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAErD,SAAS,CAAC,GAAG,EAAE;IACb,EAAE,CAAC,eAAe,EAAE,CAAC;AACvB,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,iBAAiB,EAAE,GAAG,EAAE;IAC/B,IAAI,OAAwB,CAAC;IAE7B,UAAU,CAAC,GAAG,EAAE;QACd,OAAO,GAAG,IAAI,eAAe,CAAC,IAAI,CAAC,CAAC;QACpC,WAAW,CAAC,iBAAiB,CAAC,SAAS,CAAC,CAAC;QACzC,YAAY,CAAC,iBAAiB,CAAC,SAAS,CAAC,CAAC;IAC5C,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,SAAS,EAAE,GAAG,EAAE;QACvB,EAAE,CAAC,kCAAkC,EAAE,KAAK,IAAI,EAAE;YAChD,MAAM,OAAO,CAAC,OAAO,EAAE,CAAC;YAExB,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACzC,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,2DAA2D,EAAE,KAAK,IAAI,EAAE;YACzE,WAAW,CAAC,iBAAiB,CAC3B,IAAI,kBAAkB,CAAC,oBAAoB,CAAC,CAC7C,CAAC;YAEF,MAAM,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC,OAAO,CAAC,OAAO,CAC7C,2BAA2B,CAC5B,CAAC;QACJ,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,gCAAgC,EAAE,KAAK,IAAI,EAAE;YAC9C,WAAW,CAAC,iBAAiB,CAAC,IAAI,SAAS,CAAC,YAAY,CAAC,CAAC,CAAC;YAE3D,MAAM,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QAC7D,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,YAAY,EAAE,GAAG,EAAE;QAC1B,EAAE,CAAC,2BAA2B,EAAE,KAAK,IAAI,EAAE;YACzC,MAAM,OAAO,CAAC,OAAO,EAAE,CAAC;YACxB,OAAO,CAAC,UAAU,EAAE,CAAC;YAErB,MAAM,CAAC,cAAc,CAAC,CAAC,gBAAgB,EAAE,CAAC;QAC5C,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,mCAAmC,EAAE,GAAG,EAAE;YAC3C,MAAM,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,UAAU,EAAE,CAAC,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;QACnD,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,eAAe,EAAE,GAAG,EAAE;QAC7B,EAAE,CAAC,wCAAwC,EAAE,KAAK,IAAI,EAAE;YACtD,MAAM,OAAO,CAAC,OAAO,EAAE,CAAC;YACxB,YAAY,CAAC,iBAAiB,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;YAElD,MAAM,OAAO,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC;YAEhC,MAAM,CAAC,YAAY,CAAC,CAAC,oBAAoB,CACvC,MAAM,CAAC,gBAAgB,CAAC,eAAe,CAAC,EACxC,IAAI,CACL,CAAC;YACF,MAAM,CAAC,YAAY,CAAC,CAAC,oBAAoB,CACvC,MAAM,CAAC,gBAAgB,CAAC,IAAI,CAAC,EAC7B,IAAI,CACL,CAAC;QACJ,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,sCAAsC,EAAE,KAAK,IAAI,EAAE;YACpD,MAAM,OAAO,CAAC,OAAO,EAAE,CAAC;YACxB,YAAY,CAAC,iBAAiB,CAAC;gBAC7B,OAAO,EAAE,KAAK;gBACd,KAAK,EAAE,4BAA4B;aACpC,CAAC,CAAC;YAEH,MAAM,MAAM,CAAC,OAAO,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CACrD,kBAAkB,CACnB,CAAC;YACF,MAAM,MAAM,CAAC,OAAO,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CACrD,4BAA4B,CAC7B,CAAC;QACJ,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,wCAAwC,EAAE,KAAK,IAAI,EAAE;YACtD,MAAM,MAAM,CAAC,OAAO,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;QACxE,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,cAAc,EAAE,GAAG,EAAE;QAC5B,EAAE,CAAC,uCAAuC,EAAE,KAAK,IAAI,EAAE;YACrD,MAAM,OAAO,CAAC,OAAO,EAAE,CAAC;YAExB,MAAM,OAAO,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC;YAE/B,MAAM,CAAC,YAAY,CAAC,CAAC,oBAAoB,CACvC,MAAM,CAAC,gBAAgB,CAAC,cAAc,CAAC,EACvC,IAAI,CACL,CAAC;QACJ,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,wCAAwC,EAAE,KAAK,IAAI,EAAE;YACtD,MAAM,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;QACvE,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,mBAAmB,EAAE,GAAG,EAAE;QACjC,EAAE,CAAC,6BAA6B,EAAE,KAAK,IAAI,EAAE;YAC3C,MAAM,OAAO,CAAC,OAAO,EAAE,CAAC;YACxB,YAAY,CAAC,iBAAiB,CAAC,SAAS,CAAC,CAAC;YAE1C,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,iBAAiB,CAAC,EAAE,CAAC,CAAC;YAEnD,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACjC,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,qCAAqC,EAAE,KAAK,IAAI,EAAE;YACnD,MAAM,OAAO,CAAC,OAAO,EAAE,CAAC;YACxB,YAAY,CAAC,iBAAiB,CAAC,SAAS,CAAC,CAAC;YAE1C,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,iBAAiB,CAAC,EAAE,CAAC,CAAC;YAEnD,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACjC,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,cAAc,EAAE,GAAG,EAAE;QAC5B,EAAE,CAAC,yBAAyB,EAAE,KAAK,IAAI,EAAE;YACvC,MAAM,OAAO,CAAC,OAAO,EAAE,CAAC;YACxB,YAAY,CAAC,iBAAiB,CAAC;gBAC7B,EAAE,EAAE,EAAE,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,gBAAgB,EAAE;gBAC5D,EAAE,EAAE,EAAE,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,KAAK,EAAE;aAClC,CAAC,CAAC;YAEH,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,YAAY,EAAE,CAAC;YAE9C,MAAM,CAAC,QAAQ,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;YACjC,MAAM,CAAC,QAAQ,CAAC,CAAC,cAAc,CAC7B,MAAM,CAAC,gBAAgB,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAC3C,CAAC;YACF,MAAM,CAAC,QAAQ,CAAC,CAAC,cAAc,CAC7B,MAAM,CAAC,gBAAgB,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CACzC,CAAC;QACJ,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,sCAAsC,EAAE,KAAK,IAAI,EAAE;YACpD,MAAM,OAAO,CAAC,OAAO,EAAE,CAAC;YACxB,YAAY,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC;YAErC,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,YAAY,EAAE,CAAC;YAE9C,MAAM,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QAC/B,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,44 @@
1
+ import type { Profile } from "../types/index.js";
2
+ import type { DatabaseClient } from "../db/index.js";
3
+ import type { InstanceService } from "./instance.js";
4
+ export interface VisitAndExtractOptions {
5
+ /** Interval between database polls in ms (default 1000). */
6
+ pollInterval?: number;
7
+ /** Maximum time to wait for extraction in ms (default 30000). */
8
+ pollTimeout?: number;
9
+ }
10
+ /**
11
+ * Orchestrates the visit-and-extract workflow.
12
+ *
13
+ * Navigates an instance to a LinkedIn profile, triggers extraction,
14
+ * then polls the database until the profile data appears.
15
+ */
16
+ export declare class ProfileService {
17
+ private readonly instance;
18
+ private readonly profileRepo;
19
+ constructor(instance: InstanceService, db: DatabaseClient);
20
+ /**
21
+ * Visit a LinkedIn profile and extract its data.
22
+ *
23
+ * 1. Navigate the instance to the profile URL
24
+ * 2. Wait for the page to settle
25
+ * 3. Trigger SaveCurrentProfile extraction
26
+ * 4. Poll the database until profile data appears
27
+ *
28
+ * @param profileUrl - Full LinkedIn profile URL (e.g. `https://www.linkedin.com/in/slug`)
29
+ * @returns The extracted profile data.
30
+ * @throws {ExtractionTimeoutError} if the data does not appear within the timeout.
31
+ */
32
+ visitAndExtract(profileUrl: string, options?: VisitAndExtractOptions): Promise<Profile>;
33
+ private pollForProfile;
34
+ }
35
+ /**
36
+ * Extract the public ID slug from a LinkedIn profile URL.
37
+ *
38
+ * Handles URLs like:
39
+ * - `https://www.linkedin.com/in/john-doe`
40
+ * - `https://www.linkedin.com/in/john-doe/`
41
+ * - `https://linkedin.com/in/john-doe?param=1`
42
+ */
43
+ export declare function extractSlug(profileUrl: string): string;
44
+ //# sourceMappingURL=profile.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"profile.d.ts","sourceRoot":"","sources":["../../src/services/profile.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAErD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAYrD,MAAM,WAAW,sBAAsB;IACrC,4DAA4D;IAC5D,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,iEAAiE;IACjE,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;;;GAKG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAkB;IAC3C,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAoB;gBAEpC,QAAQ,EAAE,eAAe,EAAE,EAAE,EAAE,cAAc;IAKzD;;;;;;;;;;;OAWG;IACG,eAAe,CACnB,UAAU,EAAE,MAAM,EAClB,OAAO,CAAC,EAAE,sBAAsB,GAC/B,OAAO,CAAC,OAAO,CAAC;YAYL,cAAc;CAwB7B;AAED;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAWtD"}
@@ -0,0 +1,83 @@
1
+ import { ProfileNotFoundError, ProfileRepository } from "../db/index.js";
2
+ import { ExtractionTimeoutError } from "./errors.js";
3
+ /** Default interval between database polls (ms). */
4
+ const DEFAULT_POLL_INTERVAL = 1000;
5
+ /** Default maximum time to wait for extraction (ms). */
6
+ const DEFAULT_POLL_TIMEOUT = 30_000;
7
+ /** Delay after triggering extraction before first poll (ms). */
8
+ const EXTRACTION_SETTLE_DELAY = 2000;
9
+ /**
10
+ * Orchestrates the visit-and-extract workflow.
11
+ *
12
+ * Navigates an instance to a LinkedIn profile, triggers extraction,
13
+ * then polls the database until the profile data appears.
14
+ */
15
+ export class ProfileService {
16
+ instance;
17
+ profileRepo;
18
+ constructor(instance, db) {
19
+ this.instance = instance;
20
+ this.profileRepo = new ProfileRepository(db);
21
+ }
22
+ /**
23
+ * Visit a LinkedIn profile and extract its data.
24
+ *
25
+ * 1. Navigate the instance to the profile URL
26
+ * 2. Wait for the page to settle
27
+ * 3. Trigger SaveCurrentProfile extraction
28
+ * 4. Poll the database until profile data appears
29
+ *
30
+ * @param profileUrl - Full LinkedIn profile URL (e.g. `https://www.linkedin.com/in/slug`)
31
+ * @returns The extracted profile data.
32
+ * @throws {ExtractionTimeoutError} if the data does not appear within the timeout.
33
+ */
34
+ async visitAndExtract(profileUrl, options) {
35
+ const slug = extractSlug(profileUrl);
36
+ const pollInterval = options?.pollInterval ?? DEFAULT_POLL_INTERVAL;
37
+ const pollTimeout = options?.pollTimeout ?? DEFAULT_POLL_TIMEOUT;
38
+ await this.instance.navigateToProfile(profileUrl);
39
+ await delay(EXTRACTION_SETTLE_DELAY);
40
+ await this.instance.triggerExtraction();
41
+ return this.pollForProfile(slug, pollInterval, pollTimeout, profileUrl);
42
+ }
43
+ async pollForProfile(slug, interval, timeout, profileUrl) {
44
+ const deadline = Date.now() + timeout;
45
+ while (Date.now() < deadline) {
46
+ try {
47
+ return this.profileRepo.findByPublicId(slug);
48
+ }
49
+ catch (error) {
50
+ if (!(error instanceof ProfileNotFoundError)) {
51
+ throw error;
52
+ }
53
+ }
54
+ const remaining = deadline - Date.now();
55
+ if (remaining <= 0)
56
+ break;
57
+ await delay(Math.min(interval, remaining));
58
+ }
59
+ throw new ExtractionTimeoutError(profileUrl, timeout);
60
+ }
61
+ }
62
+ /**
63
+ * Extract the public ID slug from a LinkedIn profile URL.
64
+ *
65
+ * Handles URLs like:
66
+ * - `https://www.linkedin.com/in/john-doe`
67
+ * - `https://www.linkedin.com/in/john-doe/`
68
+ * - `https://linkedin.com/in/john-doe?param=1`
69
+ */
70
+ export function extractSlug(profileUrl) {
71
+ const url = new URL(profileUrl);
72
+ const segments = url.pathname.split("/").filter(Boolean);
73
+ const inIndex = segments.indexOf("in");
74
+ const slug = inIndex !== -1 ? segments[inIndex + 1] : undefined;
75
+ if (!slug) {
76
+ throw new Error(`Invalid LinkedIn profile URL: ${profileUrl} (expected /in/{slug})`);
77
+ }
78
+ return slug;
79
+ }
80
+ function delay(ms) {
81
+ return new Promise((resolve) => setTimeout(resolve, ms));
82
+ }
83
+ //# sourceMappingURL=profile.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"profile.js","sourceRoot":"","sources":["../../src/services/profile.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=profile.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"profile.test.d.ts","sourceRoot":"","sources":["../../src/services/profile.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,145 @@
1
+ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
2
+ import { ExtractionTimeoutError } from "./errors.js";
3
+ import { extractSlug, ProfileService } from "./profile.js";
4
+ // Mock InstanceService
5
+ const mockNavigateToProfile = vi.fn();
6
+ const mockTriggerExtraction = vi.fn();
7
+ vi.mock("./instance.js", () => ({
8
+ InstanceService: vi.fn().mockImplementation(function () {
9
+ this.navigateToProfile = mockNavigateToProfile;
10
+ this.triggerExtraction = mockTriggerExtraction;
11
+ }),
12
+ }));
13
+ // Mock ProfileRepository (via db/index.js)
14
+ const mockFindByPublicId = vi.fn();
15
+ vi.mock("../db/index.js", async (importOriginal) => {
16
+ const original = await importOriginal();
17
+ return {
18
+ ProfileRepository: vi.fn().mockImplementation(function () {
19
+ this.findByPublicId = mockFindByPublicId;
20
+ }),
21
+ ProfileNotFoundError: original.ProfileNotFoundError,
22
+ };
23
+ });
24
+ import { ProfileNotFoundError } from "../db/index.js";
25
+ import { InstanceService } from "./instance.js";
26
+ const MOCK_PROFILE = {
27
+ id: 1,
28
+ miniProfile: {
29
+ firstName: "Test",
30
+ lastName: "User",
31
+ headline: "Engineer",
32
+ avatar: null,
33
+ },
34
+ externalIds: [],
35
+ currentPosition: null,
36
+ positions: [],
37
+ education: [],
38
+ skills: [],
39
+ emails: [],
40
+ };
41
+ afterEach(() => {
42
+ vi.restoreAllMocks();
43
+ });
44
+ describe("extractSlug", () => {
45
+ it("extracts slug from standard URL", () => {
46
+ expect(extractSlug("https://www.linkedin.com/in/john-doe")).toBe("john-doe");
47
+ });
48
+ it("extracts slug from URL with trailing slash", () => {
49
+ expect(extractSlug("https://www.linkedin.com/in/john-doe/")).toBe("john-doe");
50
+ });
51
+ it("extracts slug from URL with query params", () => {
52
+ expect(extractSlug("https://www.linkedin.com/in/john-doe?param=1")).toBe("john-doe");
53
+ });
54
+ it("extracts slug from URL without www", () => {
55
+ expect(extractSlug("https://linkedin.com/in/john-doe")).toBe("john-doe");
56
+ });
57
+ it("throws on URL without /in/ segment", () => {
58
+ expect(() => extractSlug("https://www.linkedin.com/company/test")).toThrow(/Invalid LinkedIn profile URL/);
59
+ });
60
+ it("throws on URL with /in/ but no slug", () => {
61
+ expect(() => extractSlug("https://www.linkedin.com/in/")).toThrow(/Invalid LinkedIn profile URL/);
62
+ });
63
+ });
64
+ describe("ProfileService", () => {
65
+ let service;
66
+ beforeEach(() => {
67
+ vi.useFakeTimers();
68
+ mockNavigateToProfile.mockResolvedValue(undefined);
69
+ mockTriggerExtraction.mockResolvedValue(undefined);
70
+ const instance = new InstanceService(9223);
71
+ service = new ProfileService(instance, {});
72
+ });
73
+ afterEach(() => {
74
+ vi.useRealTimers();
75
+ });
76
+ it("returns profile on first poll", async () => {
77
+ mockFindByPublicId.mockReturnValue(MOCK_PROFILE);
78
+ const promise = service.visitAndExtract("https://www.linkedin.com/in/test-user", { pollInterval: 100, pollTimeout: 5000 });
79
+ // Advance past settle delay
80
+ await vi.advanceTimersByTimeAsync(2000);
81
+ // Advance past first poll interval
82
+ await vi.advanceTimersByTimeAsync(100);
83
+ const result = await promise;
84
+ expect(mockNavigateToProfile).toHaveBeenCalledWith("https://www.linkedin.com/in/test-user");
85
+ expect(mockTriggerExtraction).toHaveBeenCalled();
86
+ expect(mockFindByPublicId).toHaveBeenCalledWith("test-user");
87
+ expect(result).toEqual(MOCK_PROFILE);
88
+ });
89
+ it("polls until profile appears", async () => {
90
+ let callCount = 0;
91
+ mockFindByPublicId.mockImplementation(() => {
92
+ callCount++;
93
+ if (callCount < 3) {
94
+ throw new ProfileNotFoundError("test-user");
95
+ }
96
+ return MOCK_PROFILE;
97
+ });
98
+ const promise = service.visitAndExtract("https://www.linkedin.com/in/test-user", { pollInterval: 100, pollTimeout: 5000 });
99
+ // Advance timers enough for settle delay + a few poll cycles
100
+ await vi.advanceTimersByTimeAsync(2500);
101
+ const result = await promise;
102
+ expect(result).toEqual(MOCK_PROFILE);
103
+ expect(callCount).toBeGreaterThanOrEqual(3);
104
+ });
105
+ it("throws ExtractionTimeoutError when poll times out", async () => {
106
+ mockFindByPublicId.mockImplementation(() => {
107
+ throw new ProfileNotFoundError("test-user");
108
+ });
109
+ const promise = service.visitAndExtract("https://www.linkedin.com/in/test-user", { pollInterval: 100, pollTimeout: 500 });
110
+ // Prevent unhandled rejection while advancing timers
111
+ const caughtPromise = promise.catch((e) => e);
112
+ // Advance past settle delay + poll timeout
113
+ await vi.advanceTimersByTimeAsync(3000);
114
+ const error = await caughtPromise;
115
+ expect(error).toBeInstanceOf(ExtractionTimeoutError);
116
+ });
117
+ it("re-throws non-ProfileNotFoundError errors", async () => {
118
+ mockFindByPublicId.mockImplementation(() => {
119
+ throw new Error("database locked");
120
+ });
121
+ const promise = service.visitAndExtract("https://www.linkedin.com/in/test-user", { pollInterval: 100, pollTimeout: 5000 });
122
+ // Prevent unhandled rejection while advancing timers
123
+ const caughtPromise = promise.catch((e) => e);
124
+ // Advance past settle delay so poll starts
125
+ await vi.advanceTimersByTimeAsync(2100);
126
+ const error = await caughtPromise;
127
+ expect(error).toBeInstanceOf(Error);
128
+ expect(error.message).toBe("database locked");
129
+ });
130
+ it("calls navigate before triggerExtraction", async () => {
131
+ const callOrder = [];
132
+ mockNavigateToProfile.mockImplementation(async () => {
133
+ callOrder.push("navigate");
134
+ });
135
+ mockTriggerExtraction.mockImplementation(async () => {
136
+ callOrder.push("extract");
137
+ });
138
+ mockFindByPublicId.mockReturnValue(MOCK_PROFILE);
139
+ const promise = service.visitAndExtract("https://www.linkedin.com/in/test-user", { pollInterval: 100, pollTimeout: 5000 });
140
+ await vi.advanceTimersByTimeAsync(2100);
141
+ await promise;
142
+ expect(callOrder).toEqual(["navigate", "extract"]);
143
+ });
144
+ });
145
+ //# sourceMappingURL=profile.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"profile.test.js","sourceRoot":"","sources":["../../src/services/profile.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,33 @@
1
+ /** Status of the LinkedHelper launcher process. */
2
+ export interface LauncherStatus {
3
+ reachable: boolean;
4
+ port: number;
5
+ }
6
+ /** Status of a single LinkedHelper account instance. */
7
+ export interface AccountInstanceStatus {
8
+ accountId: number;
9
+ accountName: string;
10
+ cdpPort: number | null;
11
+ }
12
+ /** Status of a single LinkedHelper database. */
13
+ export interface DatabaseStatus {
14
+ accountId: number;
15
+ path: string;
16
+ profileCount: number;
17
+ }
18
+ /** Aggregated health-check result. */
19
+ export interface StatusReport {
20
+ launcher: LauncherStatus;
21
+ instances: AccountInstanceStatus[];
22
+ databases: DatabaseStatus[];
23
+ }
24
+ /**
25
+ * Perform a health check across LinkedHelper components.
26
+ *
27
+ * The function is intentionally fault-tolerant: individual component
28
+ * failures are reported in the result rather than thrown as exceptions.
29
+ *
30
+ * @param cdpPort - The CDP port of the LinkedHelper launcher (default 9222).
31
+ */
32
+ export declare function checkStatus(cdpPort?: number): Promise<StatusReport>;
33
+ //# sourceMappingURL=status.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../src/services/status.ts"],"names":[],"mappings":"AAIA,mDAAmD;AACnD,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,OAAO,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,wDAAwD;AACxD,MAAM,WAAW,qBAAqB;IACpC,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CACxB;AAED,gDAAgD;AAChD,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,sCAAsC;AACtC,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,cAAc,CAAC;IACzB,SAAS,EAAE,qBAAqB,EAAE,CAAC;IACnC,SAAS,EAAE,cAAc,EAAE,CAAC;CAC7B;AAED;;;;;;;GAOG;AACH,wBAAsB,WAAW,CAAC,OAAO,SAAO,GAAG,OAAO,CAAC,YAAY,CAAC,CA8DvE"}