@lhremote/core 0.5.0 → 0.6.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 (113) hide show
  1. package/dist/db/index.d.ts +1 -1
  2. package/dist/db/index.d.ts.map +1 -1
  3. package/dist/db/index.js +1 -1
  4. package/dist/db/index.js.map +1 -1
  5. package/dist/db/repositories/collection-list.d.ts +69 -0
  6. package/dist/db/repositories/collection-list.d.ts.map +1 -0
  7. package/dist/db/repositories/collection-list.integration.test.d.ts +2 -0
  8. package/dist/db/repositories/collection-list.integration.test.d.ts.map +1 -0
  9. package/dist/db/repositories/collection-list.integration.test.js +124 -0
  10. package/dist/db/repositories/collection-list.integration.test.js.map +1 -0
  11. package/dist/db/repositories/collection-list.js +161 -0
  12. package/dist/db/repositories/collection-list.js.map +1 -0
  13. package/dist/db/repositories/index.d.ts +1 -0
  14. package/dist/db/repositories/index.d.ts.map +1 -1
  15. package/dist/db/repositories/index.js +1 -0
  16. package/dist/db/repositories/index.js.map +1 -1
  17. package/dist/db/testing/create-fixture.js +17 -0
  18. package/dist/db/testing/create-fixture.js.map +1 -1
  19. package/dist/index.d.ts +4 -4
  20. package/dist/index.d.ts.map +1 -1
  21. package/dist/index.js +7 -3
  22. package/dist/index.js.map +1 -1
  23. package/dist/operations/add-people-to-collection.d.ts +22 -0
  24. package/dist/operations/add-people-to-collection.d.ts.map +1 -0
  25. package/dist/operations/add-people-to-collection.js +27 -0
  26. package/dist/operations/add-people-to-collection.js.map +1 -0
  27. package/dist/operations/add-people-to-collection.test.d.ts +2 -0
  28. package/dist/operations/add-people-to-collection.test.d.ts.map +1 -0
  29. package/dist/operations/add-people-to-collection.test.js +69 -0
  30. package/dist/operations/add-people-to-collection.test.js.map +1 -0
  31. package/dist/operations/collect-people.d.ts +40 -0
  32. package/dist/operations/collect-people.d.ts.map +1 -0
  33. package/dist/operations/collect-people.js +55 -0
  34. package/dist/operations/collect-people.js.map +1 -0
  35. package/dist/operations/collect-people.test.d.ts +2 -0
  36. package/dist/operations/collect-people.test.d.ts.map +1 -0
  37. package/dist/operations/collect-people.test.js +156 -0
  38. package/dist/operations/collect-people.test.js.map +1 -0
  39. package/dist/operations/create-collection.d.ts +20 -0
  40. package/dist/operations/create-collection.d.ts.map +1 -0
  41. package/dist/operations/create-collection.js +26 -0
  42. package/dist/operations/create-collection.js.map +1 -0
  43. package/dist/operations/create-collection.test.d.ts +2 -0
  44. package/dist/operations/create-collection.test.d.ts.map +1 -0
  45. package/dist/operations/create-collection.test.js +72 -0
  46. package/dist/operations/create-collection.test.js.map +1 -0
  47. package/dist/operations/delete-collection.d.ts +20 -0
  48. package/dist/operations/delete-collection.d.ts.map +1 -0
  49. package/dist/operations/delete-collection.js +26 -0
  50. package/dist/operations/delete-collection.js.map +1 -0
  51. package/dist/operations/delete-collection.test.d.ts +2 -0
  52. package/dist/operations/delete-collection.test.d.ts.map +1 -0
  53. package/dist/operations/delete-collection.test.js +61 -0
  54. package/dist/operations/delete-collection.test.js.map +1 -0
  55. package/dist/operations/import-people-from-collection.d.ts +30 -0
  56. package/dist/operations/import-people-from-collection.d.ts.map +1 -0
  57. package/dist/operations/import-people-from-collection.js +65 -0
  58. package/dist/operations/import-people-from-collection.js.map +1 -0
  59. package/dist/operations/import-people-from-collection.test.d.ts +2 -0
  60. package/dist/operations/import-people-from-collection.test.d.ts.map +1 -0
  61. package/dist/operations/import-people-from-collection.test.js +170 -0
  62. package/dist/operations/import-people-from-collection.test.js.map +1 -0
  63. package/dist/operations/index.d.ts +7 -0
  64. package/dist/operations/index.d.ts.map +1 -1
  65. package/dist/operations/index.js +9 -0
  66. package/dist/operations/index.js.map +1 -1
  67. package/dist/operations/list-collections.d.ts +18 -0
  68. package/dist/operations/list-collections.d.ts.map +1 -0
  69. package/dist/operations/list-collections.js +22 -0
  70. package/dist/operations/list-collections.js.map +1 -0
  71. package/dist/operations/list-collections.test.d.ts +2 -0
  72. package/dist/operations/list-collections.test.d.ts.map +1 -0
  73. package/dist/operations/list-collections.test.js +73 -0
  74. package/dist/operations/list-collections.test.js.map +1 -0
  75. package/dist/operations/remove-people-from-collection.d.ts +21 -0
  76. package/dist/operations/remove-people-from-collection.d.ts.map +1 -0
  77. package/dist/operations/remove-people-from-collection.js +26 -0
  78. package/dist/operations/remove-people-from-collection.js.map +1 -0
  79. package/dist/operations/remove-people-from-collection.test.d.ts +2 -0
  80. package/dist/operations/remove-people-from-collection.test.d.ts.map +1 -0
  81. package/dist/operations/remove-people-from-collection.test.js +68 -0
  82. package/dist/operations/remove-people-from-collection.test.js.map +1 -0
  83. package/dist/services/collection.d.ts +84 -0
  84. package/dist/services/collection.d.ts.map +1 -0
  85. package/dist/services/collection.js +145 -0
  86. package/dist/services/collection.js.map +1 -0
  87. package/dist/services/collection.test.d.ts +2 -0
  88. package/dist/services/collection.test.d.ts.map +1 -0
  89. package/dist/services/collection.test.js +203 -0
  90. package/dist/services/collection.test.js.map +1 -0
  91. package/dist/services/errors.d.ts +16 -0
  92. package/dist/services/errors.d.ts.map +1 -1
  93. package/dist/services/errors.js +23 -0
  94. package/dist/services/errors.js.map +1 -1
  95. package/dist/services/index.d.ts +3 -1
  96. package/dist/services/index.d.ts.map +1 -1
  97. package/dist/services/index.js +3 -1
  98. package/dist/services/index.js.map +1 -1
  99. package/dist/services/source-type-registry.d.ts +19 -0
  100. package/dist/services/source-type-registry.d.ts.map +1 -0
  101. package/dist/services/source-type-registry.js +66 -0
  102. package/dist/services/source-type-registry.js.map +1 -0
  103. package/dist/services/source-type-registry.test.d.ts +2 -0
  104. package/dist/services/source-type-registry.test.d.ts.map +1 -0
  105. package/dist/services/source-type-registry.test.js +124 -0
  106. package/dist/services/source-type-registry.test.js.map +1 -0
  107. package/dist/types/collection.d.ts +12 -0
  108. package/dist/types/collection.d.ts.map +1 -0
  109. package/dist/types/collection.js +4 -0
  110. package/dist/types/collection.js.map +1 -0
  111. package/dist/types/index.d.ts +1 -0
  112. package/dist/types/index.d.ts.map +1 -1
  113. package/package.json +1 -1
@@ -0,0 +1,27 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-only
2
+ // Copyright (C) 2026 Oleksii PELYKH
3
+ import { resolveAccount } from "../services/account-resolution.js";
4
+ import { withDatabase } from "../services/instance-context.js";
5
+ import { CollectionListRepository } from "../db/index.js";
6
+ import { DEFAULT_CDP_PORT } from "../constants.js";
7
+ /**
8
+ * Add people to a collection by person IDs.
9
+ */
10
+ export async function addPeopleToCollection(input) {
11
+ const cdpPort = input.cdpPort ?? DEFAULT_CDP_PORT;
12
+ const accountId = await resolveAccount(cdpPort, {
13
+ ...(input.cdpHost !== undefined && { host: input.cdpHost }),
14
+ ...(input.allowRemote !== undefined && { allowRemote: input.allowRemote }),
15
+ });
16
+ return withDatabase(accountId, ({ db }) => {
17
+ const repo = new CollectionListRepository(db);
18
+ const added = repo.addPeople(input.collectionId, input.personIds);
19
+ return {
20
+ success: true,
21
+ collectionId: input.collectionId,
22
+ added,
23
+ alreadyInCollection: input.personIds.length - added,
24
+ };
25
+ }, { readOnly: false });
26
+ }
27
+ //# sourceMappingURL=add-people-to-collection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"add-people-to-collection.js","sourceRoot":"","sources":["../../src/operations/add-people-to-collection.ts"],"names":[],"mappings":"AAAA,yCAAyC;AACzC,oCAAoC;AAEpC,OAAO,EAAE,cAAc,EAAE,MAAM,mCAAmC,CAAC;AACnE,OAAO,EAAE,YAAY,EAAE,MAAM,iCAAiC,CAAC;AAC/D,OAAO,EAAE,wBAAwB,EAAE,MAAM,gBAAgB,CAAC;AAC1D,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAqBnD;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB,CACzC,KAAiC;IAEjC,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,IAAI,gBAAgB,CAAC;IAElD,MAAM,SAAS,GAAG,MAAM,cAAc,CAAC,OAAO,EAAE;QAC9C,GAAG,CAAC,KAAK,CAAC,OAAO,KAAK,SAAS,IAAI,EAAE,IAAI,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC;QAC3D,GAAG,CAAC,KAAK,CAAC,WAAW,KAAK,SAAS,IAAI,EAAE,WAAW,EAAE,KAAK,CAAC,WAAW,EAAE,CAAC;KAC3E,CAAC,CAAC;IAEH,OAAO,YAAY,CAAC,SAAS,EAAE,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE;QACxC,MAAM,IAAI,GAAG,IAAI,wBAAwB,CAAC,EAAE,CAAC,CAAC;QAC9C,MAAM,KAAK,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,YAAY,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;QAClE,OAAO;YACL,OAAO,EAAE,IAAa;YACtB,YAAY,EAAE,KAAK,CAAC,YAAY;YAChC,KAAK;YACL,mBAAmB,EAAE,KAAK,CAAC,SAAS,CAAC,MAAM,GAAG,KAAK;SACpD,CAAC;IACJ,CAAC,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,CAAC;AAC1B,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=add-people-to-collection.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"add-people-to-collection.test.d.ts","sourceRoot":"","sources":["../../src/operations/add-people-to-collection.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,69 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-only
2
+ // Copyright (C) 2026 Oleksii PELYKH
3
+ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
4
+ vi.mock("../services/account-resolution.js", () => ({
5
+ resolveAccount: vi.fn(),
6
+ }));
7
+ vi.mock("../services/instance-context.js", () => ({
8
+ withDatabase: vi.fn(),
9
+ }));
10
+ vi.mock("../db/index.js", () => ({
11
+ CollectionListRepository: vi.fn(),
12
+ }));
13
+ import { resolveAccount } from "../services/account-resolution.js";
14
+ import { withDatabase } from "../services/instance-context.js";
15
+ import { CollectionListRepository } from "../db/index.js";
16
+ import { addPeopleToCollection } from "./add-people-to-collection.js";
17
+ function setupMocks(added = 2) {
18
+ vi.mocked(resolveAccount).mockResolvedValue(1);
19
+ vi.mocked(withDatabase).mockImplementation(async (_accountId, callback) => callback({ accountId: 1, db: {} }));
20
+ vi.mocked(CollectionListRepository).mockImplementation(function () {
21
+ return {
22
+ addPeople: vi.fn().mockReturnValue(added),
23
+ };
24
+ });
25
+ }
26
+ describe("addPeopleToCollection", () => {
27
+ beforeEach(() => {
28
+ vi.clearAllMocks();
29
+ });
30
+ afterEach(() => {
31
+ vi.restoreAllMocks();
32
+ });
33
+ it("returns added count and alreadyInCollection", async () => {
34
+ setupMocks(2);
35
+ const result = await addPeopleToCollection({
36
+ collectionId: 10,
37
+ personIds: [1, 2, 3],
38
+ cdpPort: 9222,
39
+ });
40
+ expect(result.success).toBe(true);
41
+ expect(result.collectionId).toBe(10);
42
+ expect(result.added).toBe(2);
43
+ expect(result.alreadyInCollection).toBe(1);
44
+ });
45
+ it("passes collectionId and personIds to repository", async () => {
46
+ setupMocks();
47
+ await addPeopleToCollection({
48
+ collectionId: 10,
49
+ personIds: [1, 2],
50
+ cdpPort: 9222,
51
+ });
52
+ const mockResult = vi.mocked(CollectionListRepository).mock.results[0];
53
+ expect(mockResult.value.addPeople).toHaveBeenCalledWith(10, [1, 2]);
54
+ });
55
+ it("opens database in write mode", async () => {
56
+ setupMocks();
57
+ await addPeopleToCollection({
58
+ collectionId: 10,
59
+ personIds: [1],
60
+ cdpPort: 9222,
61
+ });
62
+ expect(withDatabase).toHaveBeenCalledWith(1, expect.any(Function), { readOnly: false });
63
+ });
64
+ it("propagates resolveAccount errors", async () => {
65
+ vi.mocked(resolveAccount).mockRejectedValue(new Error("connection refused"));
66
+ await expect(addPeopleToCollection({ collectionId: 10, personIds: [1], cdpPort: 9222 })).rejects.toThrow("connection refused");
67
+ });
68
+ });
69
+ //# sourceMappingURL=add-people-to-collection.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"add-people-to-collection.test.js","sourceRoot":"","sources":["../../src/operations/add-people-to-collection.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,40 @@
1
+ import type { SourceType } from "../types/index.js";
2
+ import type { ConnectionOptions } from "./types.js";
3
+ /**
4
+ * Input for the collect-people operation.
5
+ */
6
+ export interface CollectPeopleInput extends ConnectionOptions {
7
+ /** LinkedIn page URL to collect people from. */
8
+ readonly sourceUrl: string;
9
+ /** Campaign to collect people into. */
10
+ readonly campaignId: number;
11
+ /** Maximum number of profiles to collect. */
12
+ readonly limit?: number;
13
+ /** Maximum number of pages to process. */
14
+ readonly maxPages?: number;
15
+ /** Number of results per page. */
16
+ readonly pageSize?: number;
17
+ /** Explicit source type to bypass URL detection. */
18
+ readonly sourceType?: string;
19
+ }
20
+ /**
21
+ * Output from the collect-people operation.
22
+ */
23
+ export interface CollectPeopleOutput {
24
+ readonly success: true;
25
+ readonly campaignId: number;
26
+ readonly sourceType: SourceType;
27
+ }
28
+ /**
29
+ * Orchestrate people collection from a LinkedIn page into a campaign.
30
+ *
31
+ * Detects the source type from the URL (or uses an explicit override),
32
+ * then initiates collection via {@link CollectionService}. Returns
33
+ * immediately — the caller should poll `campaign-status` to monitor
34
+ * progress.
35
+ *
36
+ * @throws {CollectionError} if the source type is unknown or invalid.
37
+ * @throws {CollectionBusyError} if the instance is not idle.
38
+ */
39
+ export declare function collectPeople(input: CollectPeopleInput): Promise<CollectPeopleOutput>;
40
+ //# sourceMappingURL=collect-people.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"collect-people.d.ts","sourceRoot":"","sources":["../../src/operations/collect-people.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAOpD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAEpD;;GAEG;AACH,MAAM,WAAW,kBAAmB,SAAQ,iBAAiB;IAC3D,gDAAgD;IAChD,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,uCAAuC;IACvC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,6CAA6C;IAC7C,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,0CAA0C;IAC1C,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,kCAAkC;IAClC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,oDAAoD;IACpD,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,OAAO,EAAE,IAAI,CAAC;IACvB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAC;CACjC;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,aAAa,CACjC,KAAK,EAAE,kBAAkB,GACxB,OAAO,CAAC,mBAAmB,CAAC,CAyC9B"}
@@ -0,0 +1,55 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-only
2
+ // Copyright (C) 2026 Oleksii PELYKH
3
+ import { resolveAccount } from "../services/account-resolution.js";
4
+ import { withInstanceDatabase } from "../services/instance-context.js";
5
+ import { CollectionService } from "../services/collection.js";
6
+ import { CollectionError } from "../services/errors.js";
7
+ import { detectSourceType, validateSourceType } from "../services/source-type-registry.js";
8
+ import { DEFAULT_CDP_PORT } from "../constants.js";
9
+ /**
10
+ * Orchestrate people collection from a LinkedIn page into a campaign.
11
+ *
12
+ * Detects the source type from the URL (or uses an explicit override),
13
+ * then initiates collection via {@link CollectionService}. Returns
14
+ * immediately — the caller should poll `campaign-status` to monitor
15
+ * progress.
16
+ *
17
+ * @throws {CollectionError} if the source type is unknown or invalid.
18
+ * @throws {CollectionBusyError} if the instance is not idle.
19
+ */
20
+ export async function collectPeople(input) {
21
+ const cdpPort = input.cdpPort ?? DEFAULT_CDP_PORT;
22
+ // Resolve source type: explicit override or URL detection
23
+ let sourceType;
24
+ if (input.sourceType !== undefined) {
25
+ if (!validateSourceType(input.sourceType)) {
26
+ throw new CollectionError(`Invalid source type: ${input.sourceType}`);
27
+ }
28
+ sourceType = input.sourceType;
29
+ }
30
+ else {
31
+ const detected = detectSourceType(input.sourceUrl);
32
+ if (!detected) {
33
+ throw new CollectionError(`Unrecognized source URL: ${input.sourceUrl} — cannot determine LinkedIn page type`);
34
+ }
35
+ sourceType = detected;
36
+ }
37
+ const accountId = await resolveAccount(cdpPort, {
38
+ ...(input.cdpHost !== undefined && { host: input.cdpHost }),
39
+ ...(input.allowRemote !== undefined && { allowRemote: input.allowRemote }),
40
+ });
41
+ return withInstanceDatabase(cdpPort, accountId, async ({ instance }) => {
42
+ const collectionService = new CollectionService(instance);
43
+ await collectionService.collect(input.sourceUrl, input.campaignId, {
44
+ ...(input.limit !== undefined && { limit: input.limit }),
45
+ ...(input.maxPages !== undefined && { maxPages: input.maxPages }),
46
+ ...(input.pageSize !== undefined && { pageSize: input.pageSize }),
47
+ });
48
+ return {
49
+ success: true,
50
+ campaignId: input.campaignId,
51
+ sourceType,
52
+ };
53
+ });
54
+ }
55
+ //# sourceMappingURL=collect-people.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"collect-people.js","sourceRoot":"","sources":["../../src/operations/collect-people.ts"],"names":[],"mappings":"AAAA,yCAAyC;AACzC,oCAAoC;AAGpC,OAAO,EAAE,cAAc,EAAE,MAAM,mCAAmC,CAAC;AACnE,OAAO,EAAE,oBAAoB,EAAE,MAAM,iCAAiC,CAAC;AACvE,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,qCAAqC,CAAC;AAC3F,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AA8BnD;;;;;;;;;;GAUG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,KAAyB;IAEzB,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,IAAI,gBAAgB,CAAC;IAElD,0DAA0D;IAC1D,IAAI,UAAsB,CAAC;IAC3B,IAAI,KAAK,CAAC,UAAU,KAAK,SAAS,EAAE,CAAC;QACnC,IAAI,CAAC,kBAAkB,CAAC,KAAK,CAAC,UAAU,CAAC,EAAE,CAAC;YAC1C,MAAM,IAAI,eAAe,CACvB,wBAAwB,KAAK,CAAC,UAAU,EAAE,CAC3C,CAAC;QACJ,CAAC;QACD,UAAU,GAAG,KAAK,CAAC,UAAU,CAAC;IAChC,CAAC;SAAM,CAAC;QACN,MAAM,QAAQ,GAAG,gBAAgB,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;QACnD,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,MAAM,IAAI,eAAe,CACvB,4BAA4B,KAAK,CAAC,SAAS,wCAAwC,CACpF,CAAC;QACJ,CAAC;QACD,UAAU,GAAG,QAAQ,CAAC;IACxB,CAAC;IAED,MAAM,SAAS,GAAG,MAAM,cAAc,CAAC,OAAO,EAAE;QAC9C,GAAG,CAAC,KAAK,CAAC,OAAO,KAAK,SAAS,IAAI,EAAE,IAAI,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC;QAC3D,GAAG,CAAC,KAAK,CAAC,WAAW,KAAK,SAAS,IAAI,EAAE,WAAW,EAAE,KAAK,CAAC,WAAW,EAAE,CAAC;KAC3E,CAAC,CAAC;IAEH,OAAO,oBAAoB,CAAC,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE;QACrE,MAAM,iBAAiB,GAAG,IAAI,iBAAiB,CAAC,QAAQ,CAAC,CAAC;QAC1D,MAAM,iBAAiB,CAAC,OAAO,CAAC,KAAK,CAAC,SAAS,EAAE,KAAK,CAAC,UAAU,EAAE;YACjE,GAAG,CAAC,KAAK,CAAC,KAAK,KAAK,SAAS,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,CAAC;YACxD,GAAG,CAAC,KAAK,CAAC,QAAQ,KAAK,SAAS,IAAI,EAAE,QAAQ,EAAE,KAAK,CAAC,QAAQ,EAAE,CAAC;YACjE,GAAG,CAAC,KAAK,CAAC,QAAQ,KAAK,SAAS,IAAI,EAAE,QAAQ,EAAE,KAAK,CAAC,QAAQ,EAAE,CAAC;SAClE,CAAC,CAAC;QAEH,OAAO;YACL,OAAO,EAAE,IAAa;YACtB,UAAU,EAAE,KAAK,CAAC,UAAU;YAC5B,UAAU;SACX,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=collect-people.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"collect-people.test.d.ts","sourceRoot":"","sources":["../../src/operations/collect-people.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,156 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-only
2
+ // Copyright (C) 2026 Oleksii PELYKH
3
+ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
4
+ vi.mock("../services/account-resolution.js", () => ({
5
+ resolveAccount: vi.fn(),
6
+ }));
7
+ vi.mock("../services/instance-context.js", () => ({
8
+ withInstanceDatabase: vi.fn(),
9
+ }));
10
+ vi.mock("../services/collection.js", () => ({
11
+ CollectionService: vi.fn(),
12
+ }));
13
+ import { resolveAccount } from "../services/account-resolution.js";
14
+ import { withInstanceDatabase } from "../services/instance-context.js";
15
+ import { CollectionService } from "../services/collection.js";
16
+ import { CollectionError } from "../services/errors.js";
17
+ import { collectPeople } from "./collect-people.js";
18
+ function setupMocks() {
19
+ vi.mocked(resolveAccount).mockResolvedValue(1);
20
+ vi.mocked(withInstanceDatabase).mockImplementation(async (_cdpPort, _accountId, callback) => callback({
21
+ accountId: 1,
22
+ instance: {},
23
+ db: {},
24
+ }));
25
+ vi.mocked(CollectionService).mockImplementation(function () {
26
+ return {
27
+ collect: vi.fn().mockResolvedValue(undefined),
28
+ };
29
+ });
30
+ }
31
+ describe("collectPeople", () => {
32
+ beforeEach(() => {
33
+ vi.clearAllMocks();
34
+ });
35
+ afterEach(() => {
36
+ vi.restoreAllMocks();
37
+ });
38
+ it("detects source type from URL and returns result", async () => {
39
+ setupMocks();
40
+ const result = await collectPeople({
41
+ sourceUrl: "https://www.linkedin.com/search/results/people/?keywords=test",
42
+ campaignId: 42,
43
+ cdpPort: 9222,
44
+ });
45
+ expect(result).toEqual({
46
+ success: true,
47
+ campaignId: 42,
48
+ sourceType: "SearchPage",
49
+ });
50
+ });
51
+ it("uses explicit sourceType when provided", async () => {
52
+ setupMocks();
53
+ const result = await collectPeople({
54
+ sourceUrl: "https://www.linkedin.com/search/results/people/",
55
+ campaignId: 42,
56
+ sourceType: "MyConnections",
57
+ cdpPort: 9222,
58
+ });
59
+ expect(result.sourceType).toBe("MyConnections");
60
+ });
61
+ it("throws CollectionError for invalid explicit sourceType", async () => {
62
+ await expect(collectPeople({
63
+ sourceUrl: "https://www.linkedin.com/search/results/people/",
64
+ campaignId: 42,
65
+ sourceType: "InvalidType",
66
+ cdpPort: 9222,
67
+ })).rejects.toThrow(CollectionError);
68
+ });
69
+ it("throws CollectionError for unrecognized URL without explicit sourceType", async () => {
70
+ await expect(collectPeople({
71
+ sourceUrl: "https://www.linkedin.com/some/unknown/page/",
72
+ campaignId: 42,
73
+ cdpPort: 9222,
74
+ })).rejects.toThrow(CollectionError);
75
+ });
76
+ it("passes collection options to CollectionService.collect", async () => {
77
+ const mockCollect = vi.fn().mockResolvedValue(undefined);
78
+ vi.mocked(resolveAccount).mockResolvedValue(1);
79
+ vi.mocked(withInstanceDatabase).mockImplementation(async (_cdpPort, _accountId, callback) => callback({
80
+ accountId: 1,
81
+ instance: {},
82
+ db: {},
83
+ }));
84
+ vi.mocked(CollectionService).mockImplementation(function () {
85
+ return { collect: mockCollect };
86
+ });
87
+ await collectPeople({
88
+ sourceUrl: "https://www.linkedin.com/search/results/people/",
89
+ campaignId: 42,
90
+ limit: 100,
91
+ maxPages: 5,
92
+ pageSize: 25,
93
+ cdpPort: 9222,
94
+ });
95
+ expect(mockCollect).toHaveBeenCalledWith("https://www.linkedin.com/search/results/people/", 42, { limit: 100, maxPages: 5, pageSize: 25 });
96
+ });
97
+ it("passes connection options to resolveAccount", async () => {
98
+ setupMocks();
99
+ await collectPeople({
100
+ sourceUrl: "https://www.linkedin.com/search/results/people/",
101
+ campaignId: 42,
102
+ cdpPort: 1234,
103
+ cdpHost: "192.168.1.1",
104
+ allowRemote: true,
105
+ });
106
+ expect(resolveAccount).toHaveBeenCalledWith(1234, {
107
+ host: "192.168.1.1",
108
+ allowRemote: true,
109
+ });
110
+ });
111
+ it("omits undefined connection options", async () => {
112
+ setupMocks();
113
+ await collectPeople({
114
+ sourceUrl: "https://www.linkedin.com/search/results/people/",
115
+ campaignId: 42,
116
+ cdpPort: 9222,
117
+ });
118
+ expect(resolveAccount).toHaveBeenCalledWith(9222, {});
119
+ });
120
+ it("propagates resolveAccount errors", async () => {
121
+ vi.mocked(resolveAccount).mockRejectedValue(new Error("connection refused"));
122
+ await expect(collectPeople({
123
+ sourceUrl: "https://www.linkedin.com/search/results/people/",
124
+ campaignId: 42,
125
+ cdpPort: 9222,
126
+ })).rejects.toThrow("connection refused");
127
+ });
128
+ it("propagates withInstanceDatabase errors", async () => {
129
+ vi.mocked(resolveAccount).mockResolvedValue(1);
130
+ vi.mocked(withInstanceDatabase).mockRejectedValue(new Error("instance not running"));
131
+ await expect(collectPeople({
132
+ sourceUrl: "https://www.linkedin.com/search/results/people/",
133
+ campaignId: 42,
134
+ cdpPort: 9222,
135
+ })).rejects.toThrow("instance not running");
136
+ });
137
+ it("propagates CollectionService errors", async () => {
138
+ vi.mocked(resolveAccount).mockResolvedValue(1);
139
+ vi.mocked(withInstanceDatabase).mockImplementation(async (_cdpPort, _accountId, callback) => callback({
140
+ accountId: 1,
141
+ instance: {},
142
+ db: {},
143
+ }));
144
+ vi.mocked(CollectionService).mockImplementation(function () {
145
+ return {
146
+ collect: vi.fn().mockRejectedValue(new CollectionError("instance is busy")),
147
+ };
148
+ });
149
+ await expect(collectPeople({
150
+ sourceUrl: "https://www.linkedin.com/search/results/people/",
151
+ campaignId: 42,
152
+ cdpPort: 9222,
153
+ })).rejects.toThrow("instance is busy");
154
+ });
155
+ });
156
+ //# sourceMappingURL=collect-people.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"collect-people.test.js","sourceRoot":"","sources":["../../src/operations/collect-people.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ import type { ConnectionOptions } from "./types.js";
2
+ /**
3
+ * Input for the create-collection operation.
4
+ */
5
+ export interface CreateCollectionInput extends ConnectionOptions {
6
+ readonly name: string;
7
+ }
8
+ /**
9
+ * Output from the create-collection operation.
10
+ */
11
+ export interface CreateCollectionOutput {
12
+ readonly success: true;
13
+ readonly collectionId: number;
14
+ readonly name: string;
15
+ }
16
+ /**
17
+ * Create a new named collection (LH List).
18
+ */
19
+ export declare function createCollection(input: CreateCollectionInput): Promise<CreateCollectionOutput>;
20
+ //# sourceMappingURL=create-collection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-collection.d.ts","sourceRoot":"","sources":["../../src/operations/create-collection.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAEpD;;GAEG;AACH,MAAM,WAAW,qBAAsB,SAAQ,iBAAiB;IAC9D,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,OAAO,EAAE,IAAI,CAAC;IACvB,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED;;GAEG;AACH,wBAAsB,gBAAgB,CACpC,KAAK,EAAE,qBAAqB,GAC3B,OAAO,CAAC,sBAAsB,CAAC,CAiBjC"}
@@ -0,0 +1,26 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-only
2
+ // Copyright (C) 2026 Oleksii PELYKH
3
+ import { resolveAccount } from "../services/account-resolution.js";
4
+ import { withDatabase } from "../services/instance-context.js";
5
+ import { CollectionListRepository } from "../db/index.js";
6
+ import { DEFAULT_CDP_PORT } from "../constants.js";
7
+ /**
8
+ * Create a new named collection (LH List).
9
+ */
10
+ export async function createCollection(input) {
11
+ const cdpPort = input.cdpPort ?? DEFAULT_CDP_PORT;
12
+ const accountId = await resolveAccount(cdpPort, {
13
+ ...(input.cdpHost !== undefined && { host: input.cdpHost }),
14
+ ...(input.allowRemote !== undefined && { allowRemote: input.allowRemote }),
15
+ });
16
+ return withDatabase(accountId, ({ accountId: acctId, db }) => {
17
+ const repo = new CollectionListRepository(db);
18
+ const collectionId = repo.createCollection(acctId, input.name);
19
+ return {
20
+ success: true,
21
+ collectionId,
22
+ name: input.name,
23
+ };
24
+ }, { readOnly: false });
25
+ }
26
+ //# sourceMappingURL=create-collection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-collection.js","sourceRoot":"","sources":["../../src/operations/create-collection.ts"],"names":[],"mappings":"AAAA,yCAAyC;AACzC,oCAAoC;AAEpC,OAAO,EAAE,cAAc,EAAE,MAAM,mCAAmC,CAAC;AACnE,OAAO,EAAE,YAAY,EAAE,MAAM,iCAAiC,CAAC;AAC/D,OAAO,EAAE,wBAAwB,EAAE,MAAM,gBAAgB,CAAC;AAC1D,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAmBnD;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,KAA4B;IAE5B,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,IAAI,gBAAgB,CAAC;IAElD,MAAM,SAAS,GAAG,MAAM,cAAc,CAAC,OAAO,EAAE;QAC9C,GAAG,CAAC,KAAK,CAAC,OAAO,KAAK,SAAS,IAAI,EAAE,IAAI,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC;QAC3D,GAAG,CAAC,KAAK,CAAC,WAAW,KAAK,SAAS,IAAI,EAAE,WAAW,EAAE,KAAK,CAAC,WAAW,EAAE,CAAC;KAC3E,CAAC,CAAC;IAEH,OAAO,YAAY,CAAC,SAAS,EAAE,CAAC,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,EAAE;QAC3D,MAAM,IAAI,GAAG,IAAI,wBAAwB,CAAC,EAAE,CAAC,CAAC;QAC9C,MAAM,YAAY,GAAG,IAAI,CAAC,gBAAgB,CAAC,MAAM,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;QAC/D,OAAO;YACL,OAAO,EAAE,IAAa;YACtB,YAAY;YACZ,IAAI,EAAE,KAAK,CAAC,IAAI;SACjB,CAAC;IACJ,CAAC,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,CAAC;AAC1B,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=create-collection.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-collection.test.d.ts","sourceRoot":"","sources":["../../src/operations/create-collection.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,72 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-only
2
+ // Copyright (C) 2026 Oleksii PELYKH
3
+ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
4
+ vi.mock("../services/account-resolution.js", () => ({
5
+ resolveAccount: vi.fn(),
6
+ }));
7
+ vi.mock("../services/instance-context.js", () => ({
8
+ withDatabase: vi.fn(),
9
+ }));
10
+ vi.mock("../db/index.js", () => ({
11
+ CollectionListRepository: vi.fn(),
12
+ }));
13
+ import { resolveAccount } from "../services/account-resolution.js";
14
+ import { withDatabase } from "../services/instance-context.js";
15
+ import { CollectionListRepository } from "../db/index.js";
16
+ import { createCollection } from "./create-collection.js";
17
+ function setupMocks() {
18
+ vi.mocked(resolveAccount).mockResolvedValue(1);
19
+ vi.mocked(withDatabase).mockImplementation(async (_accountId, callback) => callback({ accountId: 1, db: {} }));
20
+ vi.mocked(CollectionListRepository).mockImplementation(function () {
21
+ return {
22
+ createCollection: vi.fn().mockReturnValue(42),
23
+ };
24
+ });
25
+ }
26
+ describe("createCollection", () => {
27
+ beforeEach(() => {
28
+ vi.clearAllMocks();
29
+ });
30
+ afterEach(() => {
31
+ vi.restoreAllMocks();
32
+ });
33
+ it("returns created collection with ID and name", async () => {
34
+ setupMocks();
35
+ const result = await createCollection({
36
+ name: "My List",
37
+ cdpPort: 9222,
38
+ });
39
+ expect(result.success).toBe(true);
40
+ expect(result.collectionId).toBe(42);
41
+ expect(result.name).toBe("My List");
42
+ });
43
+ it("passes accountId to repository", async () => {
44
+ setupMocks();
45
+ await createCollection({ name: "Test", cdpPort: 9222 });
46
+ const mockResult = vi.mocked(CollectionListRepository).mock.results[0];
47
+ expect(mockResult.value.createCollection).toHaveBeenCalledWith(1, "Test");
48
+ });
49
+ it("opens database in write mode", async () => {
50
+ setupMocks();
51
+ await createCollection({ name: "Test", cdpPort: 9222 });
52
+ expect(withDatabase).toHaveBeenCalledWith(1, expect.any(Function), { readOnly: false });
53
+ });
54
+ it("passes connection options to resolveAccount", async () => {
55
+ setupMocks();
56
+ await createCollection({
57
+ name: "Test",
58
+ cdpPort: 1234,
59
+ cdpHost: "192.168.1.1",
60
+ allowRemote: true,
61
+ });
62
+ expect(resolveAccount).toHaveBeenCalledWith(1234, {
63
+ host: "192.168.1.1",
64
+ allowRemote: true,
65
+ });
66
+ });
67
+ it("propagates resolveAccount errors", async () => {
68
+ vi.mocked(resolveAccount).mockRejectedValue(new Error("connection refused"));
69
+ await expect(createCollection({ name: "Test", cdpPort: 9222 })).rejects.toThrow("connection refused");
70
+ });
71
+ });
72
+ //# sourceMappingURL=create-collection.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-collection.test.js","sourceRoot":"","sources":["../../src/operations/create-collection.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ import type { ConnectionOptions } from "./types.js";
2
+ /**
3
+ * Input for the delete-collection operation.
4
+ */
5
+ export interface DeleteCollectionInput extends ConnectionOptions {
6
+ readonly collectionId: number;
7
+ }
8
+ /**
9
+ * Output from the delete-collection operation.
10
+ */
11
+ export interface DeleteCollectionOutput {
12
+ readonly success: true;
13
+ readonly collectionId: number;
14
+ readonly deleted: boolean;
15
+ }
16
+ /**
17
+ * Delete a collection and all its people associations.
18
+ */
19
+ export declare function deleteCollection(input: DeleteCollectionInput): Promise<DeleteCollectionOutput>;
20
+ //# sourceMappingURL=delete-collection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"delete-collection.d.ts","sourceRoot":"","sources":["../../src/operations/delete-collection.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAEpD;;GAEG;AACH,MAAM,WAAW,qBAAsB,SAAQ,iBAAiB;IAC9D,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;CAC/B;AAED;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,OAAO,EAAE,IAAI,CAAC;IACvB,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;CAC3B;AAED;;GAEG;AACH,wBAAsB,gBAAgB,CACpC,KAAK,EAAE,qBAAqB,GAC3B,OAAO,CAAC,sBAAsB,CAAC,CAiBjC"}
@@ -0,0 +1,26 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-only
2
+ // Copyright (C) 2026 Oleksii PELYKH
3
+ import { resolveAccount } from "../services/account-resolution.js";
4
+ import { withDatabase } from "../services/instance-context.js";
5
+ import { CollectionListRepository } from "../db/index.js";
6
+ import { DEFAULT_CDP_PORT } from "../constants.js";
7
+ /**
8
+ * Delete a collection and all its people associations.
9
+ */
10
+ export async function deleteCollection(input) {
11
+ const cdpPort = input.cdpPort ?? DEFAULT_CDP_PORT;
12
+ const accountId = await resolveAccount(cdpPort, {
13
+ ...(input.cdpHost !== undefined && { host: input.cdpHost }),
14
+ ...(input.allowRemote !== undefined && { allowRemote: input.allowRemote }),
15
+ });
16
+ return withDatabase(accountId, ({ db }) => {
17
+ const repo = new CollectionListRepository(db);
18
+ const deleted = repo.deleteCollection(input.collectionId);
19
+ return {
20
+ success: true,
21
+ collectionId: input.collectionId,
22
+ deleted,
23
+ };
24
+ }, { readOnly: false });
25
+ }
26
+ //# sourceMappingURL=delete-collection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"delete-collection.js","sourceRoot":"","sources":["../../src/operations/delete-collection.ts"],"names":[],"mappings":"AAAA,yCAAyC;AACzC,oCAAoC;AAEpC,OAAO,EAAE,cAAc,EAAE,MAAM,mCAAmC,CAAC;AACnE,OAAO,EAAE,YAAY,EAAE,MAAM,iCAAiC,CAAC;AAC/D,OAAO,EAAE,wBAAwB,EAAE,MAAM,gBAAgB,CAAC;AAC1D,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAmBnD;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,KAA4B;IAE5B,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,IAAI,gBAAgB,CAAC;IAElD,MAAM,SAAS,GAAG,MAAM,cAAc,CAAC,OAAO,EAAE;QAC9C,GAAG,CAAC,KAAK,CAAC,OAAO,KAAK,SAAS,IAAI,EAAE,IAAI,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC;QAC3D,GAAG,CAAC,KAAK,CAAC,WAAW,KAAK,SAAS,IAAI,EAAE,WAAW,EAAE,KAAK,CAAC,WAAW,EAAE,CAAC;KAC3E,CAAC,CAAC;IAEH,OAAO,YAAY,CAAC,SAAS,EAAE,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE;QACxC,MAAM,IAAI,GAAG,IAAI,wBAAwB,CAAC,EAAE,CAAC,CAAC;QAC9C,MAAM,OAAO,GAAG,IAAI,CAAC,gBAAgB,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;QAC1D,OAAO;YACL,OAAO,EAAE,IAAa;YACtB,YAAY,EAAE,KAAK,CAAC,YAAY;YAChC,OAAO;SACR,CAAC;IACJ,CAAC,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,CAAC;AAC1B,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=delete-collection.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"delete-collection.test.d.ts","sourceRoot":"","sources":["../../src/operations/delete-collection.test.ts"],"names":[],"mappings":""}