@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,99 @@
1
+ import { copyFileSync, mkdirSync, rmSync } from "node:fs";
2
+ import { homedir } from "node:os";
3
+ import { join } from "node:path";
4
+ import { tmpdir } from "node:os";
5
+ import { afterAll, beforeAll, describe, expect, it, vi } from "vitest";
6
+ import { DatabaseClient } from "./client.js";
7
+ import { DatabaseNotFoundError } from "./errors.js";
8
+ import { discoverAllDatabases, discoverDatabase } from "./discovery.js";
9
+ import { FIXTURE_PATH } from "./testing/open-fixture.js";
10
+ // Mock only homedir — all other node:fs/node:os calls stay real.
11
+ vi.mock("node:os", async (importOriginal) => {
12
+ const actual = await importOriginal();
13
+ return { ...actual, homedir: vi.fn(actual.homedir) };
14
+ });
15
+ const mockedHomedir = vi.mocked(homedir);
16
+ describe("discovery (integration)", () => {
17
+ const tmpBase = join(tmpdir(), `lhremote-discovery-int-${Date.now().toString(36)}`);
18
+ const fakeHome = tmpBase;
19
+ // Linux layout: ~/.config/linked-helper/Partitions/...
20
+ const partitionsDir = join(fakeHome, ".config", "linked-helper", "Partitions");
21
+ let dbPath111;
22
+ let dbPath222;
23
+ function createPartition(accountId) {
24
+ const dir = join(partitionsDir, `linked-helper-account-${String(accountId)}-main`);
25
+ mkdirSync(dir, { recursive: true });
26
+ const dbPath = join(dir, "lh.db");
27
+ copyFileSync(FIXTURE_PATH, dbPath);
28
+ return dbPath;
29
+ }
30
+ beforeAll(() => {
31
+ mkdirSync(partitionsDir, { recursive: true });
32
+ dbPath111 = createPartition(111);
33
+ dbPath222 = createPartition(222);
34
+ // Account 333: partition directory exists but no lh.db inside
35
+ mkdirSync(join(partitionsDir, "linked-helper-account-333-main"), { recursive: true });
36
+ // Non-matching directories that should be ignored
37
+ mkdirSync(join(partitionsDir, "some-unrelated-dir"), { recursive: true });
38
+ vi.stubGlobal("process", { ...process, platform: "linux" });
39
+ mockedHomedir.mockReturnValue(fakeHome);
40
+ });
41
+ afterAll(() => {
42
+ vi.restoreAllMocks();
43
+ rmSync(tmpBase, { recursive: true, force: true });
44
+ });
45
+ describe("discoverDatabase", () => {
46
+ it("returns the path for an existing account", () => {
47
+ const result = discoverDatabase(111);
48
+ expect(result).toBe(dbPath111);
49
+ });
50
+ it("returns the path for another existing account", () => {
51
+ const result = discoverDatabase(222);
52
+ expect(result).toBe(dbPath222);
53
+ });
54
+ it("throws DatabaseNotFoundError for a nonexistent account", () => {
55
+ expect(() => discoverDatabase(999)).toThrow(DatabaseNotFoundError);
56
+ });
57
+ it("throws DatabaseNotFoundError when partition exists but db file is missing", () => {
58
+ expect(() => discoverDatabase(333)).toThrow(DatabaseNotFoundError);
59
+ });
60
+ it("discovered database can be opened by DatabaseClient", () => {
61
+ const path = discoverDatabase(111);
62
+ const client = new DatabaseClient(path);
63
+ const row = client.db
64
+ .prepare("SELECT first_name FROM person_mini_profile WHERE person_id = 1")
65
+ .get();
66
+ expect(row?.first_name).toBe("Ada");
67
+ client.close();
68
+ });
69
+ });
70
+ describe("discoverAllDatabases", () => {
71
+ it("finds all accounts with database files", () => {
72
+ const result = discoverAllDatabases();
73
+ expect(result.size).toBe(2);
74
+ expect(result.get(111)).toBe(dbPath111);
75
+ expect(result.get(222)).toBe(dbPath222);
76
+ });
77
+ it("excludes partitions where db file is missing", () => {
78
+ const result = discoverAllDatabases();
79
+ expect(result.has(333)).toBe(false);
80
+ });
81
+ it("excludes non-partition directories", () => {
82
+ const result = discoverAllDatabases();
83
+ // Only 111 and 222, not "some-unrelated-dir"
84
+ expect(result.size).toBe(2);
85
+ });
86
+ it("all discovered paths are openable by DatabaseClient", () => {
87
+ const result = discoverAllDatabases();
88
+ for (const [, path] of result) {
89
+ const client = new DatabaseClient(path);
90
+ const row = client.db
91
+ .prepare("SELECT COUNT(*) AS cnt FROM people")
92
+ .get();
93
+ expect(row.cnt).toBeGreaterThan(0);
94
+ client.close();
95
+ }
96
+ });
97
+ });
98
+ });
99
+ //# sourceMappingURL=discovery.integration.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"discovery.integration.test.js","sourceRoot":"","sources":["../../src/db/discovery.integration.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,74 @@
1
+ import { existsSync, readdirSync } from "node:fs";
2
+ import { homedir } from "node:os";
3
+ import { join } from "node:path";
4
+ import { DatabaseNotFoundError } from "./errors.js";
5
+ const PARTITION_PREFIX = "linked-helper-account-";
6
+ const PARTITION_SUFFIX = "-main";
7
+ const DB_FILENAME = "lh.db";
8
+ /**
9
+ * Returns the platform-specific base directory where LinkedHelper
10
+ * stores its per-account database partitions.
11
+ */
12
+ function getBaseDirectory() {
13
+ switch (process.platform) {
14
+ case "darwin":
15
+ return join(homedir(), "Library", "Application Support", "linked-helper");
16
+ case "win32":
17
+ return join(process.env["APPDATA"] ?? join(homedir(), "AppData", "Roaming"), "linked-helper");
18
+ default:
19
+ // Linux and other POSIX
20
+ return join(homedir(), ".config", "linked-helper");
21
+ }
22
+ }
23
+ /**
24
+ * Builds the expected database path for a specific account.
25
+ */
26
+ function buildDbPath(baseDir, accountId) {
27
+ const partition = `${PARTITION_PREFIX}${String(accountId)}${PARTITION_SUFFIX}`;
28
+ return join(baseDir, "Partitions", partition, DB_FILENAME);
29
+ }
30
+ /**
31
+ * Resolves the database file path for a LinkedHelper account.
32
+ *
33
+ * @throws {DatabaseNotFoundError} if the database file does not exist.
34
+ */
35
+ export function discoverDatabase(accountId) {
36
+ const dbPath = buildDbPath(getBaseDirectory(), accountId);
37
+ if (!existsSync(dbPath)) {
38
+ throw new DatabaseNotFoundError(accountId);
39
+ }
40
+ return dbPath;
41
+ }
42
+ /**
43
+ * Scans the LinkedHelper partitions directory and returns a map of
44
+ * every account ID to its database file path.
45
+ *
46
+ * Only accounts whose database file actually exists are included.
47
+ */
48
+ export function discoverAllDatabases() {
49
+ const baseDir = getBaseDirectory();
50
+ const partitionsDir = join(baseDir, "Partitions");
51
+ const result = new Map();
52
+ if (!existsSync(partitionsDir)) {
53
+ return result;
54
+ }
55
+ const entries = readdirSync(partitionsDir, { withFileTypes: true });
56
+ for (const entry of entries) {
57
+ if (!entry.isDirectory())
58
+ continue;
59
+ if (!entry.name.startsWith(PARTITION_PREFIX))
60
+ continue;
61
+ if (!entry.name.endsWith(PARTITION_SUFFIX))
62
+ continue;
63
+ const idStr = entry.name.slice(PARTITION_PREFIX.length, -PARTITION_SUFFIX.length);
64
+ const accountId = Number(idStr);
65
+ if (!Number.isInteger(accountId))
66
+ continue;
67
+ const dbPath = join(partitionsDir, entry.name, DB_FILENAME);
68
+ if (existsSync(dbPath)) {
69
+ result.set(accountId, dbPath);
70
+ }
71
+ }
72
+ return result;
73
+ }
74
+ //# sourceMappingURL=discovery.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"discovery.js","sourceRoot":"","sources":["../../src/db/discovery.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAClD,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,OAAO,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAEpD,MAAM,gBAAgB,GAAG,wBAAwB,CAAC;AAClD,MAAM,gBAAgB,GAAG,OAAO,CAAC;AACjC,MAAM,WAAW,GAAG,OAAO,CAAC;AAE5B;;;GAGG;AACH,SAAS,gBAAgB;IACvB,QAAQ,OAAO,CAAC,QAAQ,EAAE,CAAC;QACzB,KAAK,QAAQ;YACX,OAAO,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,qBAAqB,EAAE,eAAe,CAAC,CAAC;QAC5E,KAAK,OAAO;YACV,OAAO,IAAI,CACT,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,SAAS,CAAC,EAC/D,eAAe,CAChB,CAAC;QACJ;YACE,wBAAwB;YACxB,OAAO,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,eAAe,CAAC,CAAC;IACvD,CAAC;AACH,CAAC;AAED;;GAEG;AACH,SAAS,WAAW,CAAC,OAAe,EAAE,SAAiB;IACrD,MAAM,SAAS,GAAG,GAAG,gBAAgB,GAAG,MAAM,CAAC,SAAS,CAAC,GAAG,gBAAgB,EAAE,CAAC;IAC/E,OAAO,IAAI,CAAC,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,WAAW,CAAC,CAAC;AAC7D,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAAC,SAAiB;IAChD,MAAM,MAAM,GAAG,WAAW,CAAC,gBAAgB,EAAE,EAAE,SAAS,CAAC,CAAC;IAC1D,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;QACxB,MAAM,IAAI,qBAAqB,CAAC,SAAS,CAAC,CAAC;IAC7C,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,oBAAoB;IAClC,MAAM,OAAO,GAAG,gBAAgB,EAAE,CAAC;IACnC,MAAM,aAAa,GAAG,IAAI,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC;IAElD,MAAM,MAAM,GAAG,IAAI,GAAG,EAAkB,CAAC;IAEzC,IAAI,CAAC,UAAU,CAAC,aAAa,CAAC,EAAE,CAAC;QAC/B,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,MAAM,OAAO,GAAG,WAAW,CAAC,aAAa,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;IACpE,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE;YAAE,SAAS;QACnC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,gBAAgB,CAAC;YAAE,SAAS;QACvD,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,gBAAgB,CAAC;YAAE,SAAS;QAErD,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,KAAK,CAC5B,gBAAgB,CAAC,MAAM,EACvB,CAAC,gBAAgB,CAAC,MAAM,CACzB,CAAC;QACF,MAAM,SAAS,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;QAChC,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,SAAS,CAAC;YAAE,SAAS;QAE3C,MAAM,MAAM,GAAG,IAAI,CAAC,aAAa,EAAE,KAAK,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC;QAC5D,IAAI,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;YACvB,MAAM,CAAC,GAAG,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;QAChC,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=discovery.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"discovery.test.d.ts","sourceRoot":"","sources":["../../src/db/discovery.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,123 @@
1
+ import { existsSync, readdirSync } from "node:fs";
2
+ import { homedir } from "node:os";
3
+ import { join } from "node:path";
4
+ import { afterEach, describe, expect, it, vi } from "vitest";
5
+ import { DatabaseNotFoundError } from "./errors.js";
6
+ import { discoverAllDatabases, discoverDatabase } from "./discovery.js";
7
+ vi.mock("node:fs", () => ({
8
+ existsSync: vi.fn(),
9
+ readdirSync: vi.fn(),
10
+ }));
11
+ vi.mock("node:os", () => ({
12
+ homedir: vi.fn(() => "/home/testuser"),
13
+ }));
14
+ const mockedExistsSync = vi.mocked(existsSync);
15
+ const mockedReaddirSync = vi.mocked(readdirSync);
16
+ const mockedHomedir = vi.mocked(homedir);
17
+ afterEach(() => {
18
+ vi.restoreAllMocks();
19
+ });
20
+ describe("discoverDatabase", () => {
21
+ it("returns the database path when the file exists (darwin)", () => {
22
+ vi.stubGlobal("process", { ...process, platform: "darwin" });
23
+ mockedHomedir.mockReturnValue("/Users/alice");
24
+ mockedExistsSync.mockReturnValue(true);
25
+ const result = discoverDatabase(42);
26
+ expect(result).toBe(join("/Users/alice/Library/Application Support/linked-helper", "Partitions", "linked-helper-account-42-main", "lh.db"));
27
+ });
28
+ it("returns the database path when the file exists (win32)", () => {
29
+ vi.stubGlobal("process", {
30
+ ...process,
31
+ platform: "win32",
32
+ env: { APPDATA: "C:\\Users\\alice\\AppData\\Roaming" },
33
+ });
34
+ mockedHomedir.mockReturnValue("C:\\Users\\alice");
35
+ mockedExistsSync.mockReturnValue(true);
36
+ const result = discoverDatabase(7);
37
+ expect(result).toBe(join("C:\\Users\\alice\\AppData\\Roaming", "linked-helper", "Partitions", "linked-helper-account-7-main", "lh.db"));
38
+ });
39
+ it("returns the database path when the file exists (linux)", () => {
40
+ vi.stubGlobal("process", { ...process, platform: "linux" });
41
+ mockedHomedir.mockReturnValue("/home/alice");
42
+ mockedExistsSync.mockReturnValue(true);
43
+ const result = discoverDatabase(100);
44
+ expect(result).toBe(join("/home/alice/.config/linked-helper", "Partitions", "linked-helper-account-100-main", "lh.db"));
45
+ });
46
+ it("throws DatabaseNotFoundError when the file does not exist", () => {
47
+ vi.stubGlobal("process", { ...process, platform: "darwin" });
48
+ mockedHomedir.mockReturnValue("/Users/alice");
49
+ mockedExistsSync.mockReturnValue(false);
50
+ expect(() => discoverDatabase(99)).toThrow(DatabaseNotFoundError);
51
+ expect(() => discoverDatabase(99)).toThrow("No database found for account 99");
52
+ });
53
+ });
54
+ describe("discoverAllDatabases", () => {
55
+ it("returns an empty map when the partitions directory does not exist", () => {
56
+ vi.stubGlobal("process", { ...process, platform: "darwin" });
57
+ mockedHomedir.mockReturnValue("/Users/alice");
58
+ mockedExistsSync.mockReturnValue(false);
59
+ const result = discoverAllDatabases();
60
+ expect(result.size).toBe(0);
61
+ });
62
+ it("discovers multiple account databases", () => {
63
+ vi.stubGlobal("process", { ...process, platform: "darwin" });
64
+ mockedHomedir.mockReturnValue("/Users/alice");
65
+ const baseDir = join("/Users/alice", "Library", "Application Support", "linked-helper", "Partitions");
66
+ mockedExistsSync.mockImplementation((p) => {
67
+ const path = String(p);
68
+ if (path === baseDir)
69
+ return true;
70
+ if (path === join(baseDir, "linked-helper-account-1-main", "lh.db"))
71
+ return true;
72
+ if (path === join(baseDir, "linked-helper-account-2-main", "lh.db"))
73
+ return true;
74
+ return false;
75
+ });
76
+ mockedReaddirSync.mockReturnValue([
77
+ makeDirent("linked-helper-account-1-main", true),
78
+ makeDirent("linked-helper-account-2-main", true),
79
+ makeDirent("some-other-dir", true),
80
+ makeDirent("not-a-directory.txt", false),
81
+ ]);
82
+ const result = discoverAllDatabases();
83
+ expect(result.size).toBe(2);
84
+ expect(result.get(1)).toBe(join(baseDir, "linked-helper-account-1-main", "lh.db"));
85
+ expect(result.get(2)).toBe(join(baseDir, "linked-helper-account-2-main", "lh.db"));
86
+ });
87
+ it("skips partitions where the db file is missing", () => {
88
+ vi.stubGlobal("process", { ...process, platform: "darwin" });
89
+ mockedHomedir.mockReturnValue("/Users/alice");
90
+ const baseDir = join("/Users/alice", "Library", "Application Support", "linked-helper", "Partitions");
91
+ mockedExistsSync.mockImplementation((p) => {
92
+ const path = String(p);
93
+ if (path === baseDir)
94
+ return true;
95
+ // Account 1 has a db, account 2 does not
96
+ if (path === join(baseDir, "linked-helper-account-1-main", "lh.db"))
97
+ return true;
98
+ return false;
99
+ });
100
+ mockedReaddirSync.mockReturnValue([
101
+ makeDirent("linked-helper-account-1-main", true),
102
+ makeDirent("linked-helper-account-2-main", true),
103
+ ]);
104
+ const result = discoverAllDatabases();
105
+ expect(result.size).toBe(1);
106
+ expect(result.has(2)).toBe(false);
107
+ });
108
+ });
109
+ function makeDirent(name, isDir) {
110
+ return {
111
+ name,
112
+ isDirectory: () => isDir,
113
+ isFile: () => !isDir,
114
+ isBlockDevice: () => false,
115
+ isCharacterDevice: () => false,
116
+ isFIFO: () => false,
117
+ isSocket: () => false,
118
+ isSymbolicLink: () => false,
119
+ parentPath: "",
120
+ path: "",
121
+ };
122
+ }
123
+ //# sourceMappingURL=discovery.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"discovery.test.js","sourceRoot":"","sources":["../../src/db/discovery.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAClD,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAE7D,OAAO,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAExE,EAAE,CAAC,IAAI,CAAC,SAAS,EAAE,GAAG,EAAE,CAAC,CAAC;IACxB,UAAU,EAAE,EAAE,CAAC,EAAE,EAAE;IACnB,WAAW,EAAE,EAAE,CAAC,EAAE,EAAE;CACrB,CAAC,CAAC,CAAC;AAEJ,EAAE,CAAC,IAAI,CAAC,SAAS,EAAE,GAAG,EAAE,CAAC,CAAC;IACxB,OAAO,EAAE,EAAE,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,gBAAgB,CAAC;CACvC,CAAC,CAAC,CAAC;AAEJ,MAAM,gBAAgB,GAAG,EAAE,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;AAC/C,MAAM,iBAAiB,GAAG,EAAE,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;AACjD,MAAM,aAAa,GAAG,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;AAEzC,SAAS,CAAC,GAAG,EAAE;IACb,EAAE,CAAC,eAAe,EAAE,CAAC;AACvB,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,kBAAkB,EAAE,GAAG,EAAE;IAChC,EAAE,CAAC,yDAAyD,EAAE,GAAG,EAAE;QACjE,EAAE,CAAC,UAAU,CAAC,SAAS,EAAE,EAAE,GAAG,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC,CAAC;QAC7D,aAAa,CAAC,eAAe,CAAC,cAAc,CAAC,CAAC;QAC9C,gBAAgB,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC;QAEvC,MAAM,MAAM,GAAG,gBAAgB,CAAC,EAAE,CAAC,CAAC;QAEpC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CACjB,IAAI,CACF,wDAAwD,EACxD,YAAY,EACZ,+BAA+B,EAC/B,OAAO,CACR,CACF,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wDAAwD,EAAE,GAAG,EAAE;QAChE,EAAE,CAAC,UAAU,CAAC,SAAS,EAAE;YACvB,GAAG,OAAO;YACV,QAAQ,EAAE,OAAO;YACjB,GAAG,EAAE,EAAE,OAAO,EAAE,oCAAoC,EAAE;SACvD,CAAC,CAAC;QACH,aAAa,CAAC,eAAe,CAAC,kBAAkB,CAAC,CAAC;QAClD,gBAAgB,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC;QAEvC,MAAM,MAAM,GAAG,gBAAgB,CAAC,CAAC,CAAC,CAAC;QAEnC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CACjB,IAAI,CACF,oCAAoC,EACpC,eAAe,EACf,YAAY,EACZ,8BAA8B,EAC9B,OAAO,CACR,CACF,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wDAAwD,EAAE,GAAG,EAAE;QAChE,EAAE,CAAC,UAAU,CAAC,SAAS,EAAE,EAAE,GAAG,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,CAAC;QAC5D,aAAa,CAAC,eAAe,CAAC,aAAa,CAAC,CAAC;QAC7C,gBAAgB,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC;QAEvC,MAAM,MAAM,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC;QAErC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CACjB,IAAI,CACF,mCAAmC,EACnC,YAAY,EACZ,gCAAgC,EAChC,OAAO,CACR,CACF,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,2DAA2D,EAAE,GAAG,EAAE;QACnE,EAAE,CAAC,UAAU,CAAC,SAAS,EAAE,EAAE,GAAG,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC,CAAC;QAC7D,aAAa,CAAC,eAAe,CAAC,cAAc,CAAC,CAAC;QAC9C,gBAAgB,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC;QAExC,MAAM,CAAC,GAAG,EAAE,CAAC,gBAAgB,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,qBAAqB,CAAC,CAAC;QAClE,MAAM,CAAC,GAAG,EAAE,CAAC,gBAAgB,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,kCAAkC,CAAC,CAAC;IACjF,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,sBAAsB,EAAE,GAAG,EAAE;IACpC,EAAE,CAAC,mEAAmE,EAAE,GAAG,EAAE;QAC3E,EAAE,CAAC,UAAU,CAAC,SAAS,EAAE,EAAE,GAAG,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC,CAAC;QAC7D,aAAa,CAAC,eAAe,CAAC,cAAc,CAAC,CAAC;QAC9C,gBAAgB,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC;QAExC,MAAM,MAAM,GAAG,oBAAoB,EAAE,CAAC;QAEtC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAC9B,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,sCAAsC,EAAE,GAAG,EAAE;QAC9C,EAAE,CAAC,UAAU,CAAC,SAAS,EAAE,EAAE,GAAG,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC,CAAC;QAC7D,aAAa,CAAC,eAAe,CAAC,cAAc,CAAC,CAAC;QAE9C,MAAM,OAAO,GAAG,IAAI,CAClB,cAAc,EACd,SAAS,EACT,qBAAqB,EACrB,eAAe,EACf,YAAY,CACb,CAAC;QAEF,gBAAgB,CAAC,kBAAkB,CAAC,CAAC,CAAC,EAAE,EAAE;YACxC,MAAM,IAAI,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;YACvB,IAAI,IAAI,KAAK,OAAO;gBAAE,OAAO,IAAI,CAAC;YAClC,IAAI,IAAI,KAAK,IAAI,CAAC,OAAO,EAAE,8BAA8B,EAAE,OAAO,CAAC;gBACjE,OAAO,IAAI,CAAC;YACd,IAAI,IAAI,KAAK,IAAI,CAAC,OAAO,EAAE,8BAA8B,EAAE,OAAO,CAAC;gBACjE,OAAO,IAAI,CAAC;YACd,OAAO,KAAK,CAAC;QACf,CAAC,CAAC,CAAC;QAEH,iBAAiB,CAAC,eAAe,CAAC;YAChC,UAAU,CAAC,8BAA8B,EAAE,IAAI,CAAC;YAChD,UAAU,CAAC,8BAA8B,EAAE,IAAI,CAAC;YAChD,UAAU,CAAC,gBAAgB,EAAE,IAAI,CAAC;YAClC,UAAU,CAAC,qBAAqB,EAAE,KAAK,CAAC;SAChC,CAAC,CAAC;QAEZ,MAAM,MAAM,GAAG,oBAAoB,EAAE,CAAC;QAEtC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAC5B,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CACxB,IAAI,CAAC,OAAO,EAAE,8BAA8B,EAAE,OAAO,CAAC,CACvD,CAAC;QACF,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CACxB,IAAI,CAAC,OAAO,EAAE,8BAA8B,EAAE,OAAO,CAAC,CACvD,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+CAA+C,EAAE,GAAG,EAAE;QACvD,EAAE,CAAC,UAAU,CAAC,SAAS,EAAE,EAAE,GAAG,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC,CAAC;QAC7D,aAAa,CAAC,eAAe,CAAC,cAAc,CAAC,CAAC;QAE9C,MAAM,OAAO,GAAG,IAAI,CAClB,cAAc,EACd,SAAS,EACT,qBAAqB,EACrB,eAAe,EACf,YAAY,CACb,CAAC;QAEF,gBAAgB,CAAC,kBAAkB,CAAC,CAAC,CAAC,EAAE,EAAE;YACxC,MAAM,IAAI,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;YACvB,IAAI,IAAI,KAAK,OAAO;gBAAE,OAAO,IAAI,CAAC;YAClC,yCAAyC;YACzC,IAAI,IAAI,KAAK,IAAI,CAAC,OAAO,EAAE,8BAA8B,EAAE,OAAO,CAAC;gBACjE,OAAO,IAAI,CAAC;YACd,OAAO,KAAK,CAAC;QACf,CAAC,CAAC,CAAC;QAEH,iBAAiB,CAAC,eAAe,CAAC;YAChC,UAAU,CAAC,8BAA8B,EAAE,IAAI,CAAC;YAChD,UAAU,CAAC,8BAA8B,EAAE,IAAI,CAAC;SACxC,CAAC,CAAC;QAEZ,MAAM,MAAM,GAAG,oBAAoB,EAAE,CAAC;QAEtC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAC5B,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACpC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,SAAS,UAAU,CAAC,IAAY,EAAE,KAAc;IAC9C,OAAO;QACL,IAAI;QACJ,WAAW,EAAE,GAAG,EAAE,CAAC,KAAK;QACxB,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC,KAAK;QACpB,aAAa,EAAE,GAAG,EAAE,CAAC,KAAK;QAC1B,iBAAiB,EAAE,GAAG,EAAE,CAAC,KAAK;QAC9B,MAAM,EAAE,GAAG,EAAE,CAAC,KAAK;QACnB,QAAQ,EAAE,GAAG,EAAE,CAAC,KAAK;QACrB,cAAc,EAAE,GAAG,EAAE,CAAC,KAAK;QAC3B,UAAU,EAAE,EAAE;QACd,IAAI,EAAE,EAAE;KACT,CAAC;AACJ,CAAC"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Base class for all database-related errors.
3
+ */
4
+ export declare class DatabaseError extends Error {
5
+ constructor(message: string, options?: ErrorOptions);
6
+ }
7
+ /**
8
+ * Thrown when a LinkedHelper database file cannot be found at the
9
+ * expected location for a given account.
10
+ */
11
+ export declare class DatabaseNotFoundError extends DatabaseError {
12
+ constructor(accountId: number);
13
+ }
14
+ /**
15
+ * Thrown when a profile lookup yields no results. The person may not
16
+ * have been extracted by LinkedHelper yet.
17
+ */
18
+ export declare class ProfileNotFoundError extends DatabaseError {
19
+ constructor(identifier: number | string);
20
+ }
21
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/db/errors.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,qBAAa,aAAc,SAAQ,KAAK;gBAC1B,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY;CAIpD;AAED;;;GAGG;AACH,qBAAa,qBAAsB,SAAQ,aAAa;gBAC1C,SAAS,EAAE,MAAM;CAI9B;AAED;;;GAGG;AACH,qBAAa,oBAAqB,SAAQ,aAAa;gBACzC,UAAU,EAAE,MAAM,GAAG,MAAM;CAUxC"}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Base class for all database-related errors.
3
+ */
4
+ export class DatabaseError extends Error {
5
+ constructor(message, options) {
6
+ super(message, options);
7
+ this.name = "DatabaseError";
8
+ }
9
+ }
10
+ /**
11
+ * Thrown when a LinkedHelper database file cannot be found at the
12
+ * expected location for a given account.
13
+ */
14
+ export class DatabaseNotFoundError extends DatabaseError {
15
+ constructor(accountId) {
16
+ super(`No database found for account ${String(accountId)}`);
17
+ this.name = "DatabaseNotFoundError";
18
+ }
19
+ }
20
+ /**
21
+ * Thrown when a profile lookup yields no results. The person may not
22
+ * have been extracted by LinkedHelper yet.
23
+ */
24
+ export class ProfileNotFoundError extends DatabaseError {
25
+ constructor(identifier) {
26
+ const detail = typeof identifier === "number"
27
+ ? `id ${String(identifier)}`
28
+ : `public ID "${identifier}"`;
29
+ super(`Profile not found for ${detail}. It may not have been extracted yet.`);
30
+ this.name = "ProfileNotFoundError";
31
+ }
32
+ }
33
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/db/errors.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,OAAO,aAAc,SAAQ,KAAK;IACtC,YAAY,OAAe,EAAE,OAAsB;QACjD,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QACxB,IAAI,CAAC,IAAI,GAAG,eAAe,CAAC;IAC9B,CAAC;CACF;AAED;;;GAGG;AACH,MAAM,OAAO,qBAAsB,SAAQ,aAAa;IACtD,YAAY,SAAiB;QAC3B,KAAK,CAAC,iCAAiC,MAAM,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;QAC5D,IAAI,CAAC,IAAI,GAAG,uBAAuB,CAAC;IACtC,CAAC;CACF;AAED;;;GAGG;AACH,MAAM,OAAO,oBAAqB,SAAQ,aAAa;IACrD,YAAY,UAA2B;QACrC,MAAM,MAAM,GACV,OAAO,UAAU,KAAK,QAAQ;YAC5B,CAAC,CAAC,MAAM,MAAM,CAAC,UAAU,CAAC,EAAE;YAC5B,CAAC,CAAC,cAAc,UAAU,GAAG,CAAC;QAClC,KAAK,CACH,yBAAyB,MAAM,uCAAuC,CACvE,CAAC;QACF,IAAI,CAAC,IAAI,GAAG,sBAAsB,CAAC;IACrC,CAAC;CACF"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=errors.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.test.d.ts","sourceRoot":"","sources":["../../src/db/errors.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,32 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { DatabaseError, DatabaseNotFoundError, ProfileNotFoundError, } from "./errors.js";
3
+ describe("DatabaseError", () => {
4
+ it("is an instance of Error", () => {
5
+ const err = new DatabaseError("test");
6
+ expect(err).toBeInstanceOf(Error);
7
+ expect(err.name).toBe("DatabaseError");
8
+ });
9
+ });
10
+ describe("DatabaseNotFoundError", () => {
11
+ it("includes the account ID in the message", () => {
12
+ const err = new DatabaseNotFoundError(42);
13
+ expect(err.message).toBe("No database found for account 42");
14
+ expect(err).toBeInstanceOf(DatabaseError);
15
+ expect(err.name).toBe("DatabaseNotFoundError");
16
+ });
17
+ });
18
+ describe("ProfileNotFoundError", () => {
19
+ it("formats numeric IDs", () => {
20
+ const err = new ProfileNotFoundError(7);
21
+ expect(err.message).toContain("id 7");
22
+ expect(err.name).toBe("ProfileNotFoundError");
23
+ });
24
+ it("formats string public IDs", () => {
25
+ const err = new ProfileNotFoundError("alice-smith");
26
+ expect(err.message).toContain('public ID "alice-smith"');
27
+ });
28
+ it("extends DatabaseError", () => {
29
+ expect(new ProfileNotFoundError(1)).toBeInstanceOf(DatabaseError);
30
+ });
31
+ });
32
+ //# sourceMappingURL=errors.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.test.js","sourceRoot":"","sources":["../../src/db/errors.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAE9C,OAAO,EACL,aAAa,EACb,qBAAqB,EACrB,oBAAoB,GACrB,MAAM,aAAa,CAAC;AAErB,QAAQ,CAAC,eAAe,EAAE,GAAG,EAAE;IAC7B,EAAE,CAAC,yBAAyB,EAAE,GAAG,EAAE;QACjC,MAAM,GAAG,GAAG,IAAI,aAAa,CAAC,MAAM,CAAC,CAAC;QACtC,MAAM,CAAC,GAAG,CAAC,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;QAClC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;IACzC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,uBAAuB,EAAE,GAAG,EAAE;IACrC,EAAE,CAAC,wCAAwC,EAAE,GAAG,EAAE;QAChD,MAAM,GAAG,GAAG,IAAI,qBAAqB,CAAC,EAAE,CAAC,CAAC;QAC1C,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,kCAAkC,CAAC,CAAC;QAC7D,MAAM,CAAC,GAAG,CAAC,CAAC,cAAc,CAAC,aAAa,CAAC,CAAC;QAC1C,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,uBAAuB,CAAC,CAAC;IACjD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,sBAAsB,EAAE,GAAG,EAAE;IACpC,EAAE,CAAC,qBAAqB,EAAE,GAAG,EAAE;QAC7B,MAAM,GAAG,GAAG,IAAI,oBAAoB,CAAC,CAAC,CAAC,CAAC;QACxC,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;QACtC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC;IAChD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,2BAA2B,EAAE,GAAG,EAAE;QACnC,MAAM,GAAG,GAAG,IAAI,oBAAoB,CAAC,aAAa,CAAC,CAAC;QACpD,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,yBAAyB,CAAC,CAAC;IAC3D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uBAAuB,EAAE,GAAG,EAAE;QAC/B,MAAM,CAAC,IAAI,oBAAoB,CAAC,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC,aAAa,CAAC,CAAC;IACpE,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,5 @@
1
+ export { DatabaseClient } from "./client.js";
2
+ export { discoverAllDatabases, discoverDatabase } from "./discovery.js";
3
+ export { DatabaseError, DatabaseNotFoundError, ProfileNotFoundError, } from "./errors.js";
4
+ export { ProfileRepository } from "./repositories/index.js";
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/db/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AACxE,OAAO,EACL,aAAa,EACb,qBAAqB,EACrB,oBAAoB,GACrB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC"}
@@ -0,0 +1,5 @@
1
+ export { DatabaseClient } from "./client.js";
2
+ export { discoverAllDatabases, discoverDatabase } from "./discovery.js";
3
+ export { DatabaseError, DatabaseNotFoundError, ProfileNotFoundError, } from "./errors.js";
4
+ export { ProfileRepository } from "./repositories/index.js";
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/db/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AACxE,OAAO,EACL,aAAa,EACb,qBAAqB,EACrB,oBAAoB,GACrB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { ProfileRepository } from "./profile.js";
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/db/repositories/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { ProfileRepository } from "./profile.js";
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/db/repositories/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC"}
@@ -0,0 +1,32 @@
1
+ import type { Profile } from "../../types/index.js";
2
+ import type { DatabaseClient } from "../client.js";
3
+ /**
4
+ * Read-only repository for assembling {@link Profile} objects from
5
+ * LinkedHelper's SQLite database.
6
+ */
7
+ export declare class ProfileRepository {
8
+ private readonly stmtPersonById;
9
+ private readonly stmtPersonByPublicId;
10
+ private readonly stmtMiniProfile;
11
+ private readonly stmtExternalIds;
12
+ private readonly stmtCurrentPosition;
13
+ private readonly stmtPositions;
14
+ private readonly stmtEducation;
15
+ private readonly stmtSkills;
16
+ private readonly stmtEmails;
17
+ constructor(client: DatabaseClient);
18
+ /**
19
+ * Looks up a profile by its internal database ID.
20
+ *
21
+ * @throws {ProfileNotFoundError} if no person exists with the given ID.
22
+ */
23
+ findById(id: number): Profile;
24
+ /**
25
+ * Looks up a profile by LinkedIn public ID (the slug from a profile URL).
26
+ *
27
+ * @throws {ProfileNotFoundError} if no person matches the public ID.
28
+ */
29
+ findByPublicId(slug: string): Profile;
30
+ private assembleProfile;
31
+ }
32
+ //# sourceMappingURL=profile.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"profile.d.ts","sourceRoot":"","sources":["../../../src/db/repositories/profile.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAOV,OAAO,EAER,MAAM,sBAAsB,CAAC;AAC9B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAwDnD;;;GAGG;AACH,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAC;IAChC,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CAAC;IACtC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAC;IACjC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAC;IACjC,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAC;IACrC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAC;IAC/B,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAC;IAC/B,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC;IAC5B,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC;gBAEhB,MAAM,EAAE,cAAc;IAoDlC;;;;OAIG;IACH,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO;IAM7B;;;;OAIG;IACH,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAMrC,OAAO,CAAC,eAAe;CA+DxB"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=profile.integration.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"profile.integration.test.d.ts","sourceRoot":"","sources":["../../../src/db/repositories/profile.integration.test.ts"],"names":[],"mappings":""}