@lhremote/core 0.5.0 → 0.7.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 (191) hide show
  1. package/README.md +39 -0
  2. package/dist/cdp/client.d.ts +5 -1
  3. package/dist/cdp/client.d.ts.map +1 -1
  4. package/dist/cdp/client.js +11 -3
  5. package/dist/cdp/client.js.map +1 -1
  6. package/dist/data/index.d.ts +1 -0
  7. package/dist/data/index.d.ts.map +1 -1
  8. package/dist/data/index.js +1 -0
  9. package/dist/data/index.js.map +1 -1
  10. package/dist/data/linkedin-reference.d.ts +44 -0
  11. package/dist/data/linkedin-reference.d.ts.map +1 -0
  12. package/dist/data/linkedin-reference.js +316 -0
  13. package/dist/data/linkedin-reference.js.map +1 -0
  14. package/dist/data/linkedin-reference.test.d.ts +2 -0
  15. package/dist/data/linkedin-reference.test.d.ts.map +1 -0
  16. package/dist/data/linkedin-reference.test.js +124 -0
  17. package/dist/data/linkedin-reference.test.js.map +1 -0
  18. package/dist/db/index.d.ts +1 -1
  19. package/dist/db/index.d.ts.map +1 -1
  20. package/dist/db/index.js +1 -1
  21. package/dist/db/index.js.map +1 -1
  22. package/dist/db/repositories/collection-list.d.ts +69 -0
  23. package/dist/db/repositories/collection-list.d.ts.map +1 -0
  24. package/dist/db/repositories/collection-list.integration.test.d.ts +2 -0
  25. package/dist/db/repositories/collection-list.integration.test.d.ts.map +1 -0
  26. package/dist/db/repositories/collection-list.integration.test.js +124 -0
  27. package/dist/db/repositories/collection-list.integration.test.js.map +1 -0
  28. package/dist/db/repositories/collection-list.js +161 -0
  29. package/dist/db/repositories/collection-list.js.map +1 -0
  30. package/dist/db/repositories/index.d.ts +1 -0
  31. package/dist/db/repositories/index.d.ts.map +1 -1
  32. package/dist/db/repositories/index.js +1 -0
  33. package/dist/db/repositories/index.js.map +1 -1
  34. package/dist/db/testing/create-fixture.js +17 -0
  35. package/dist/db/testing/create-fixture.js.map +1 -1
  36. package/dist/index.d.ts +5 -5
  37. package/dist/index.d.ts.map +1 -1
  38. package/dist/index.js +10 -4
  39. package/dist/index.js.map +1 -1
  40. package/dist/operations/add-people-to-collection.d.ts +22 -0
  41. package/dist/operations/add-people-to-collection.d.ts.map +1 -0
  42. package/dist/operations/add-people-to-collection.js +27 -0
  43. package/dist/operations/add-people-to-collection.js.map +1 -0
  44. package/dist/operations/add-people-to-collection.test.d.ts +2 -0
  45. package/dist/operations/add-people-to-collection.test.d.ts.map +1 -0
  46. package/dist/operations/add-people-to-collection.test.js +69 -0
  47. package/dist/operations/add-people-to-collection.test.js.map +1 -0
  48. package/dist/operations/build-linkedin-url.d.ts +39 -0
  49. package/dist/operations/build-linkedin-url.d.ts.map +1 -0
  50. package/dist/operations/build-linkedin-url.js +72 -0
  51. package/dist/operations/build-linkedin-url.js.map +1 -0
  52. package/dist/operations/collect-people.d.ts +40 -0
  53. package/dist/operations/collect-people.d.ts.map +1 -0
  54. package/dist/operations/collect-people.js +55 -0
  55. package/dist/operations/collect-people.js.map +1 -0
  56. package/dist/operations/collect-people.test.d.ts +2 -0
  57. package/dist/operations/collect-people.test.d.ts.map +1 -0
  58. package/dist/operations/collect-people.test.js +156 -0
  59. package/dist/operations/collect-people.test.js.map +1 -0
  60. package/dist/operations/create-collection.d.ts +20 -0
  61. package/dist/operations/create-collection.d.ts.map +1 -0
  62. package/dist/operations/create-collection.js +26 -0
  63. package/dist/operations/create-collection.js.map +1 -0
  64. package/dist/operations/create-collection.test.d.ts +2 -0
  65. package/dist/operations/create-collection.test.d.ts.map +1 -0
  66. package/dist/operations/create-collection.test.js +72 -0
  67. package/dist/operations/create-collection.test.js.map +1 -0
  68. package/dist/operations/delete-collection.d.ts +20 -0
  69. package/dist/operations/delete-collection.d.ts.map +1 -0
  70. package/dist/operations/delete-collection.js +26 -0
  71. package/dist/operations/delete-collection.js.map +1 -0
  72. package/dist/operations/delete-collection.test.d.ts +2 -0
  73. package/dist/operations/delete-collection.test.d.ts.map +1 -0
  74. package/dist/operations/delete-collection.test.js +61 -0
  75. package/dist/operations/delete-collection.test.js.map +1 -0
  76. package/dist/operations/import-people-from-collection.d.ts +30 -0
  77. package/dist/operations/import-people-from-collection.d.ts.map +1 -0
  78. package/dist/operations/import-people-from-collection.js +65 -0
  79. package/dist/operations/import-people-from-collection.js.map +1 -0
  80. package/dist/operations/import-people-from-collection.test.d.ts +2 -0
  81. package/dist/operations/import-people-from-collection.test.d.ts.map +1 -0
  82. package/dist/operations/import-people-from-collection.test.js +170 -0
  83. package/dist/operations/import-people-from-collection.test.js.map +1 -0
  84. package/dist/operations/index.d.ts +9 -0
  85. package/dist/operations/index.d.ts.map +1 -1
  86. package/dist/operations/index.js +12 -0
  87. package/dist/operations/index.js.map +1 -1
  88. package/dist/operations/list-collections.d.ts +18 -0
  89. package/dist/operations/list-collections.d.ts.map +1 -0
  90. package/dist/operations/list-collections.js +22 -0
  91. package/dist/operations/list-collections.js.map +1 -0
  92. package/dist/operations/list-collections.test.d.ts +2 -0
  93. package/dist/operations/list-collections.test.d.ts.map +1 -0
  94. package/dist/operations/list-collections.test.js +73 -0
  95. package/dist/operations/list-collections.test.js.map +1 -0
  96. package/dist/operations/remove-people-from-collection.d.ts +21 -0
  97. package/dist/operations/remove-people-from-collection.d.ts.map +1 -0
  98. package/dist/operations/remove-people-from-collection.js +26 -0
  99. package/dist/operations/remove-people-from-collection.js.map +1 -0
  100. package/dist/operations/remove-people-from-collection.test.d.ts +2 -0
  101. package/dist/operations/remove-people-from-collection.test.d.ts.map +1 -0
  102. package/dist/operations/remove-people-from-collection.test.js +68 -0
  103. package/dist/operations/remove-people-from-collection.test.js.map +1 -0
  104. package/dist/operations/resolve-linkedin-entity.d.ts +33 -0
  105. package/dist/operations/resolve-linkedin-entity.d.ts.map +1 -0
  106. package/dist/operations/resolve-linkedin-entity.js +198 -0
  107. package/dist/operations/resolve-linkedin-entity.js.map +1 -0
  108. package/dist/services/app.d.ts.map +1 -1
  109. package/dist/services/app.js +37 -12
  110. package/dist/services/app.js.map +1 -1
  111. package/dist/services/app.test.js +57 -0
  112. package/dist/services/app.test.js.map +1 -1
  113. package/dist/services/boolean-expression.d.ts +29 -0
  114. package/dist/services/boolean-expression.d.ts.map +1 -0
  115. package/dist/services/boolean-expression.js +79 -0
  116. package/dist/services/boolean-expression.js.map +1 -0
  117. package/dist/services/boolean-expression.test.d.ts +2 -0
  118. package/dist/services/boolean-expression.test.d.ts.map +1 -0
  119. package/dist/services/boolean-expression.test.js +64 -0
  120. package/dist/services/boolean-expression.test.js.map +1 -0
  121. package/dist/services/collection.d.ts +84 -0
  122. package/dist/services/collection.d.ts.map +1 -0
  123. package/dist/services/collection.js +154 -0
  124. package/dist/services/collection.js.map +1 -0
  125. package/dist/services/collection.test.d.ts +2 -0
  126. package/dist/services/collection.test.d.ts.map +1 -0
  127. package/dist/services/collection.test.js +219 -0
  128. package/dist/services/collection.test.js.map +1 -0
  129. package/dist/services/errors.d.ts +27 -0
  130. package/dist/services/errors.d.ts.map +1 -1
  131. package/dist/services/errors.js +38 -0
  132. package/dist/services/errors.js.map +1 -1
  133. package/dist/services/index.d.ts +7 -1
  134. package/dist/services/index.d.ts.map +1 -1
  135. package/dist/services/index.js +7 -1
  136. package/dist/services/index.js.map +1 -1
  137. package/dist/services/instance.d.ts +12 -0
  138. package/dist/services/instance.d.ts.map +1 -1
  139. package/dist/services/instance.js +28 -0
  140. package/dist/services/instance.js.map +1 -1
  141. package/dist/services/instance.test.js +31 -0
  142. package/dist/services/instance.test.js.map +1 -1
  143. package/dist/services/launcher.d.ts +16 -0
  144. package/dist/services/launcher.d.ts.map +1 -1
  145. package/dist/services/launcher.js +63 -10
  146. package/dist/services/launcher.js.map +1 -1
  147. package/dist/services/launcher.test.js +109 -42
  148. package/dist/services/launcher.test.js.map +1 -1
  149. package/dist/services/sn-url-builder.d.ts +21 -0
  150. package/dist/services/sn-url-builder.d.ts.map +1 -0
  151. package/dist/services/sn-url-builder.js +132 -0
  152. package/dist/services/sn-url-builder.js.map +1 -0
  153. package/dist/services/sn-url-builder.test.d.ts +2 -0
  154. package/dist/services/sn-url-builder.test.d.ts.map +1 -0
  155. package/dist/services/sn-url-builder.test.js +130 -0
  156. package/dist/services/sn-url-builder.test.js.map +1 -0
  157. package/dist/services/source-type-registry.d.ts +19 -0
  158. package/dist/services/source-type-registry.d.ts.map +1 -0
  159. package/dist/services/source-type-registry.js +66 -0
  160. package/dist/services/source-type-registry.js.map +1 -0
  161. package/dist/services/source-type-registry.test.d.ts +2 -0
  162. package/dist/services/source-type-registry.test.d.ts.map +1 -0
  163. package/dist/services/source-type-registry.test.js +124 -0
  164. package/dist/services/source-type-registry.test.js.map +1 -0
  165. package/dist/services/url-builder.d.ts +12 -0
  166. package/dist/services/url-builder.d.ts.map +1 -0
  167. package/dist/services/url-builder.js +100 -0
  168. package/dist/services/url-builder.js.map +1 -0
  169. package/dist/services/url-builder.test.d.ts +2 -0
  170. package/dist/services/url-builder.test.d.ts.map +1 -0
  171. package/dist/services/url-builder.test.js +79 -0
  172. package/dist/services/url-builder.test.js.map +1 -0
  173. package/dist/services/url-templates.d.ts +41 -0
  174. package/dist/services/url-templates.d.ts.map +1 -0
  175. package/dist/services/url-templates.js +118 -0
  176. package/dist/services/url-templates.js.map +1 -0
  177. package/dist/services/url-templates.test.d.ts +2 -0
  178. package/dist/services/url-templates.test.d.ts.map +1 -0
  179. package/dist/services/url-templates.test.js +159 -0
  180. package/dist/services/url-templates.test.js.map +1 -0
  181. package/dist/types/collection.d.ts +12 -0
  182. package/dist/types/collection.d.ts.map +1 -0
  183. package/dist/types/collection.js +4 -0
  184. package/dist/types/collection.js.map +1 -0
  185. package/dist/types/index.d.ts +2 -0
  186. package/dist/types/index.d.ts.map +1 -1
  187. package/dist/types/linkedin-url.d.ts +109 -0
  188. package/dist/types/linkedin-url.d.ts.map +1 -0
  189. package/dist/types/linkedin-url.js +4 -0
  190. package/dist/types/linkedin-url.js.map +1 -0
  191. package/package.json +1 -1
@@ -0,0 +1,66 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-only
2
+ // Copyright (C) 2026 Oleksii PELYKH
3
+ /**
4
+ * Ordered list of URL patterns for source type detection.
5
+ *
6
+ * Patterns are tested in order; the first match wins.
7
+ */
8
+ const SOURCE_TYPE_PATTERNS = [
9
+ { pattern: /\/search\/results\/people\//, sourceType: "SearchPage" },
10
+ { pattern: /\/mynetwork\/invite-connect\/connections\//, sourceType: "MyConnections" },
11
+ { pattern: /\/school\/[^/]+\/people\//, sourceType: "Alumni" },
12
+ { pattern: /\/company\/[^/]+\/people\//, sourceType: "OrganizationPeople" },
13
+ { pattern: /\/groups\/[^/]+\/members\//, sourceType: "Group" },
14
+ { pattern: /\/events\/[^/]+\/attendees\//, sourceType: "Event" },
15
+ { pattern: /\/me\/profile-views\//, sourceType: "LWVYPP" },
16
+ { pattern: /\/mynetwork\/invitation-manager\/sent\//, sourceType: "SentInvitationPage" },
17
+ { pattern: /\/me\/my-network\/followers\//, sourceType: "FollowersPage" },
18
+ { pattern: /\/me\/my-network\/following\//, sourceType: "FollowingPage" },
19
+ { pattern: /\/sales\/search\/people/, sourceType: "SNSearchPage" },
20
+ { pattern: /\/sales\/lists\/people\//, sourceType: "SNListPage" },
21
+ { pattern: /\/sales\/search\/company/, sourceType: "SNOrgsPage" },
22
+ { pattern: /\/sales\/lists\/company\//, sourceType: "SNOrgsListsPage" },
23
+ { pattern: /\/talent\/search\//, sourceType: "TSearchPage" },
24
+ { pattern: /\/talent\/projects\//, sourceType: "TProjectPage" },
25
+ { pattern: /\/recruiter\/search\//, sourceType: "RSearchPage" },
26
+ { pattern: /\/recruiter\/projects\//, sourceType: "RProjectPage" },
27
+ ];
28
+ /**
29
+ * Set of all valid source type strings for fast validation.
30
+ */
31
+ const VALID_SOURCE_TYPES = new Set(SOURCE_TYPE_PATTERNS.map((entry) => entry.sourceType));
32
+ /**
33
+ * Detect the LinkedIn source type from a URL.
34
+ *
35
+ * Tests the URL pathname against known LinkedIn page patterns and
36
+ * returns the matching source type, or `undefined` if no pattern matches.
37
+ *
38
+ * @param url - Full LinkedIn URL or pathname
39
+ * @returns The detected source type, or `undefined` for unknown URLs
40
+ */
41
+ export function detectSourceType(url) {
42
+ let pathname;
43
+ try {
44
+ pathname = new URL(url).pathname;
45
+ }
46
+ catch {
47
+ // Treat non-URL strings as raw pathnames
48
+ pathname = url;
49
+ }
50
+ for (const { pattern, sourceType } of SOURCE_TYPE_PATTERNS) {
51
+ if (pattern.test(pathname)) {
52
+ return sourceType;
53
+ }
54
+ }
55
+ return undefined;
56
+ }
57
+ /**
58
+ * Validate whether a string is a known source type.
59
+ *
60
+ * @param type - String to validate
61
+ * @returns `true` if `type` is a valid {@link SourceType}
62
+ */
63
+ export function validateSourceType(type) {
64
+ return VALID_SOURCE_TYPES.has(type);
65
+ }
66
+ //# sourceMappingURL=source-type-registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"source-type-registry.js","sourceRoot":"","sources":["../../src/services/source-type-registry.ts"],"names":[],"mappings":"AAAA,yCAAyC;AACzC,oCAAoC;AAYpC;;;;GAIG;AACH,MAAM,oBAAoB,GAAwB;IAChD,EAAE,OAAO,EAAE,6BAA6B,EAAE,UAAU,EAAE,YAAY,EAAE;IACpE,EAAE,OAAO,EAAE,4CAA4C,EAAE,UAAU,EAAE,eAAe,EAAE;IACtF,EAAE,OAAO,EAAE,2BAA2B,EAAE,UAAU,EAAE,QAAQ,EAAE;IAC9D,EAAE,OAAO,EAAE,4BAA4B,EAAE,UAAU,EAAE,oBAAoB,EAAE;IAC3E,EAAE,OAAO,EAAE,4BAA4B,EAAE,UAAU,EAAE,OAAO,EAAE;IAC9D,EAAE,OAAO,EAAE,8BAA8B,EAAE,UAAU,EAAE,OAAO,EAAE;IAChE,EAAE,OAAO,EAAE,uBAAuB,EAAE,UAAU,EAAE,QAAQ,EAAE;IAC1D,EAAE,OAAO,EAAE,yCAAyC,EAAE,UAAU,EAAE,oBAAoB,EAAE;IACxF,EAAE,OAAO,EAAE,+BAA+B,EAAE,UAAU,EAAE,eAAe,EAAE;IACzE,EAAE,OAAO,EAAE,+BAA+B,EAAE,UAAU,EAAE,eAAe,EAAE;IACzE,EAAE,OAAO,EAAE,yBAAyB,EAAE,UAAU,EAAE,cAAc,EAAE;IAClE,EAAE,OAAO,EAAE,0BAA0B,EAAE,UAAU,EAAE,YAAY,EAAE;IACjE,EAAE,OAAO,EAAE,0BAA0B,EAAE,UAAU,EAAE,YAAY,EAAE;IACjE,EAAE,OAAO,EAAE,2BAA2B,EAAE,UAAU,EAAE,iBAAiB,EAAE;IACvE,EAAE,OAAO,EAAE,oBAAoB,EAAE,UAAU,EAAE,aAAa,EAAE;IAC5D,EAAE,OAAO,EAAE,sBAAsB,EAAE,UAAU,EAAE,cAAc,EAAE;IAC/D,EAAE,OAAO,EAAE,uBAAuB,EAAE,UAAU,EAAE,aAAa,EAAE;IAC/D,EAAE,OAAO,EAAE,yBAAyB,EAAE,UAAU,EAAE,cAAc,EAAE;CACnE,CAAC;AAEF;;GAEG;AACH,MAAM,kBAAkB,GAAwB,IAAI,GAAG,CACrD,oBAAoB,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,UAAU,CAAC,CACtD,CAAC;AAEF;;;;;;;;GAQG;AACH,MAAM,UAAU,gBAAgB,CAAC,GAAW;IAC1C,IAAI,QAAgB,CAAC;IACrB,IAAI,CAAC;QACH,QAAQ,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC;IACnC,CAAC;IAAC,MAAM,CAAC;QACP,yCAAyC;QACzC,QAAQ,GAAG,GAAG,CAAC;IACjB,CAAC;IAED,KAAK,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,IAAI,oBAAoB,EAAE,CAAC;QAC3D,IAAI,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC3B,OAAO,UAAU,CAAC;QACpB,CAAC;IACH,CAAC;IAED,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,kBAAkB,CAAC,IAAY;IAC7C,OAAO,kBAAkB,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;AACtC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=source-type-registry.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"source-type-registry.test.d.ts","sourceRoot":"","sources":["../../src/services/source-type-registry.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,124 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-only
2
+ // Copyright (C) 2026 Oleksii PELYKH
3
+ import { describe, expect, it } from "vitest";
4
+ import { detectSourceType, validateSourceType } from "./source-type-registry.js";
5
+ describe("detectSourceType", () => {
6
+ describe("Free tier", () => {
7
+ it("should detect SearchPage from people search URL", () => {
8
+ expect(detectSourceType("https://www.linkedin.com/search/results/people/?keywords=engineer")).toBe("SearchPage");
9
+ });
10
+ it("should detect MyConnections from connections URL", () => {
11
+ expect(detectSourceType("https://www.linkedin.com/mynetwork/invite-connect/connections/")).toBe("MyConnections");
12
+ });
13
+ it("should detect Alumni from school people URL", () => {
14
+ expect(detectSourceType("https://www.linkedin.com/school/stanford-university/people/")).toBe("Alumni");
15
+ });
16
+ it("should detect OrganizationPeople from company people URL", () => {
17
+ expect(detectSourceType("https://www.linkedin.com/company/google/people/")).toBe("OrganizationPeople");
18
+ });
19
+ it("should detect Group from group members URL", () => {
20
+ expect(detectSourceType("https://www.linkedin.com/groups/12345/members/")).toBe("Group");
21
+ });
22
+ it("should detect Event from event attendees URL", () => {
23
+ expect(detectSourceType("https://www.linkedin.com/events/67890/attendees/")).toBe("Event");
24
+ });
25
+ it("should detect LWVYPP from profile views URL", () => {
26
+ expect(detectSourceType("https://www.linkedin.com/me/profile-views/")).toBe("LWVYPP");
27
+ });
28
+ it("should detect SentInvitationPage from sent invitations URL", () => {
29
+ expect(detectSourceType("https://www.linkedin.com/mynetwork/invitation-manager/sent/")).toBe("SentInvitationPage");
30
+ });
31
+ it("should detect FollowersPage from followers URL", () => {
32
+ expect(detectSourceType("https://www.linkedin.com/me/my-network/followers/")).toBe("FollowersPage");
33
+ });
34
+ it("should detect FollowingPage from following URL", () => {
35
+ expect(detectSourceType("https://www.linkedin.com/me/my-network/following/")).toBe("FollowingPage");
36
+ });
37
+ });
38
+ describe("Sales Navigator tier", () => {
39
+ it("should detect SNSearchPage from Sales Navigator people search URL", () => {
40
+ expect(detectSourceType("https://www.linkedin.com/sales/search/people?query=test")).toBe("SNSearchPage");
41
+ });
42
+ it("should detect SNListPage from Sales Navigator people list URL", () => {
43
+ expect(detectSourceType("https://www.linkedin.com/sales/lists/people/12345")).toBe("SNListPage");
44
+ });
45
+ it("should detect SNOrgsPage from Sales Navigator company search URL", () => {
46
+ expect(detectSourceType("https://www.linkedin.com/sales/search/company?query=test")).toBe("SNOrgsPage");
47
+ });
48
+ it("should detect SNOrgsListsPage from Sales Navigator company list URL", () => {
49
+ expect(detectSourceType("https://www.linkedin.com/sales/lists/company/12345")).toBe("SNOrgsListsPage");
50
+ });
51
+ });
52
+ describe("Recruiter tier", () => {
53
+ it("should detect TSearchPage from Talent search URL", () => {
54
+ expect(detectSourceType("https://www.linkedin.com/talent/search/?query=test")).toBe("TSearchPage");
55
+ });
56
+ it("should detect TProjectPage from Talent projects URL", () => {
57
+ expect(detectSourceType("https://www.linkedin.com/talent/projects/12345")).toBe("TProjectPage");
58
+ });
59
+ it("should detect RSearchPage from Recruiter search URL", () => {
60
+ expect(detectSourceType("https://www.linkedin.com/recruiter/search/?query=test")).toBe("RSearchPage");
61
+ });
62
+ it("should detect RProjectPage from Recruiter projects URL", () => {
63
+ expect(detectSourceType("https://www.linkedin.com/recruiter/projects/12345")).toBe("RProjectPage");
64
+ });
65
+ });
66
+ describe("edge cases", () => {
67
+ it("should return undefined for unknown URLs", () => {
68
+ expect(detectSourceType("https://www.linkedin.com/in/some-user")).toBeUndefined();
69
+ });
70
+ it("should return undefined for non-LinkedIn URLs", () => {
71
+ expect(detectSourceType("https://www.google.com/search?q=test")).toBeUndefined();
72
+ });
73
+ it("should return undefined for empty string", () => {
74
+ expect(detectSourceType("")).toBeUndefined();
75
+ });
76
+ it("should handle URLs with query parameters", () => {
77
+ expect(detectSourceType("https://www.linkedin.com/search/results/people/?keywords=test&origin=GLOBAL")).toBe("SearchPage");
78
+ });
79
+ it("should handle URLs with hash fragments", () => {
80
+ expect(detectSourceType("https://www.linkedin.com/search/results/people/#section")).toBe("SearchPage");
81
+ });
82
+ it("should handle raw pathnames without hostname", () => {
83
+ expect(detectSourceType("/search/results/people/")).toBe("SearchPage");
84
+ });
85
+ });
86
+ });
87
+ describe("validateSourceType", () => {
88
+ it("should return true for all valid source types", () => {
89
+ const validTypes = [
90
+ "SearchPage",
91
+ "MyConnections",
92
+ "Alumni",
93
+ "OrganizationPeople",
94
+ "Group",
95
+ "Event",
96
+ "LWVYPP",
97
+ "SentInvitationPage",
98
+ "FollowersPage",
99
+ "FollowingPage",
100
+ "SNSearchPage",
101
+ "SNListPage",
102
+ "SNOrgsPage",
103
+ "SNOrgsListsPage",
104
+ "TSearchPage",
105
+ "TProjectPage",
106
+ "RSearchPage",
107
+ "RProjectPage",
108
+ ];
109
+ for (const type of validTypes) {
110
+ expect(validateSourceType(type)).toBe(true);
111
+ }
112
+ });
113
+ it("should return false for unknown type strings", () => {
114
+ expect(validateSourceType("InvalidType")).toBe(false);
115
+ });
116
+ it("should return false for empty string", () => {
117
+ expect(validateSourceType("")).toBe(false);
118
+ });
119
+ it("should be case-sensitive", () => {
120
+ expect(validateSourceType("searchpage")).toBe(false);
121
+ expect(validateSourceType("SEARCHPAGE")).toBe(false);
122
+ });
123
+ });
124
+ //# sourceMappingURL=source-type-registry.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"source-type-registry.test.js","sourceRoot":"","sources":["../../src/services/source-type-registry.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ import type { BasicSearchParams, UrlBuilderResult } from "../types/linkedin-url.js";
2
+ /**
3
+ * Build a LinkedIn basic search (`/search/results/people/`) URL.
4
+ *
5
+ * Handles JSON-array encoding for faceted filters and boolean
6
+ * keyword composition.
7
+ *
8
+ * @param params - Search parameters
9
+ * @returns URL and any warnings
10
+ */
11
+ export declare function buildBasicSearchUrl(params: BasicSearchParams): UrlBuilderResult;
12
+ //# sourceMappingURL=url-builder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"url-builder.d.ts","sourceRoot":"","sources":["../../src/services/url-builder.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,iBAAiB,EAEjB,gBAAgB,EACjB,MAAM,0BAA0B,CAAC;AAwClC;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,iBAAiB,GACxB,gBAAgB,CAkElB"}
@@ -0,0 +1,100 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-only
2
+ // Copyright (C) 2026 Oleksii PELYKH
3
+ import { buildBooleanExpression } from "./boolean-expression.js";
4
+ const BASE_URL = "https://www.linkedin.com/search/results/people/";
5
+ /**
6
+ * Parameter names that are only valid for Sales Navigator searches.
7
+ * If passed to the basic search builder, a warning is emitted.
8
+ */
9
+ const SN_ONLY_PARAMS = new Set([
10
+ "seniorityLevel",
11
+ "function",
12
+ "companyHeadcount",
13
+ "companyType",
14
+ "yearsAtCurrentCompany",
15
+ "yearsAtCurrentPosition",
16
+ "yearsOfExperience",
17
+ ]);
18
+ /**
19
+ * Resolve keywords to a string.
20
+ *
21
+ * Accepts either a plain string or a structured/raw boolean expression.
22
+ */
23
+ function resolveKeywords(keywords) {
24
+ if (typeof keywords === "string")
25
+ return keywords;
26
+ return buildBooleanExpression(keywords);
27
+ }
28
+ /**
29
+ * Encode a string array as a JSON array string for LinkedIn URL params.
30
+ *
31
+ * LinkedIn uses `["value1","value2"]` encoding for faceted filters.
32
+ */
33
+ function encodeJsonArray(values) {
34
+ return JSON.stringify(values);
35
+ }
36
+ /**
37
+ * Build a LinkedIn basic search (`/search/results/people/`) URL.
38
+ *
39
+ * Handles JSON-array encoding for faceted filters and boolean
40
+ * keyword composition.
41
+ *
42
+ * @param params - Search parameters
43
+ * @returns URL and any warnings
44
+ */
45
+ export function buildBasicSearchUrl(params) {
46
+ const warnings = [];
47
+ const searchParams = new URLSearchParams();
48
+ // Keywords
49
+ if (params.keywords !== undefined) {
50
+ const keywordsStr = resolveKeywords(params.keywords);
51
+ if (keywordsStr.length > 0) {
52
+ searchParams.set("keywords", keywordsStr);
53
+ }
54
+ }
55
+ // Faceted array filters
56
+ const arrayFilters = [
57
+ {
58
+ key: "currentCompany",
59
+ urlKey: "currentCompany",
60
+ values: params.currentCompany,
61
+ },
62
+ { key: "pastCompany", urlKey: "pastCompany", values: params.pastCompany },
63
+ { key: "geoUrn", urlKey: "geoUrn", values: params.geoUrn },
64
+ { key: "industry", urlKey: "industry", values: params.industry },
65
+ { key: "school", urlKey: "schoolFilter", values: params.school },
66
+ { key: "network", urlKey: "network", values: params.network },
67
+ {
68
+ key: "profileLanguage",
69
+ urlKey: "profileLanguage",
70
+ values: params.profileLanguage,
71
+ },
72
+ {
73
+ key: "serviceCategory",
74
+ urlKey: "serviceCategory",
75
+ values: params.serviceCategory,
76
+ },
77
+ ];
78
+ for (const filter of arrayFilters) {
79
+ if (filter.values !== undefined) {
80
+ if (filter.values.length === 0) {
81
+ warnings.push(`Empty filter array for "${filter.key}" — ignored`);
82
+ continue;
83
+ }
84
+ searchParams.set(filter.urlKey, encodeJsonArray(filter.values));
85
+ }
86
+ }
87
+ // Check for SN-only params passed via extra keys
88
+ // (This is a type-level guard — the BasicSearchParams interface
89
+ // doesn't include SN-only params, but callers may pass them via
90
+ // a wider type.)
91
+ for (const key of Object.keys(params)) {
92
+ if (SN_ONLY_PARAMS.has(key)) {
93
+ warnings.push(`"${key}" is a Sales Navigator filter — not applicable to basic search`);
94
+ }
95
+ }
96
+ const qs = searchParams.toString();
97
+ const url = qs.length > 0 ? `${BASE_URL}?${qs}` : BASE_URL;
98
+ return { url, sourceType: "SearchPage", warnings };
99
+ }
100
+ //# sourceMappingURL=url-builder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"url-builder.js","sourceRoot":"","sources":["../../src/services/url-builder.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=url-builder.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"url-builder.test.d.ts","sourceRoot":"","sources":["../../src/services/url-builder.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,79 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-only
2
+ // Copyright (C) 2026 Oleksii PELYKH
3
+ import { describe, expect, it } from "vitest";
4
+ import { detectSourceType } from "./source-type-registry.js";
5
+ import { buildBasicSearchUrl } from "./url-builder.js";
6
+ describe("buildBasicSearchUrl", () => {
7
+ it("builds base URL with no params", () => {
8
+ const result = buildBasicSearchUrl({});
9
+ expect(result.url).toBe("https://www.linkedin.com/search/results/people/");
10
+ expect(result.sourceType).toBe("SearchPage");
11
+ expect(result.warnings).toHaveLength(0);
12
+ });
13
+ it("includes keywords as query param", () => {
14
+ const result = buildBasicSearchUrl({ keywords: "software engineer" });
15
+ expect(result.url).toContain("keywords=software+engineer");
16
+ });
17
+ it("accepts boolean expression as keywords", () => {
18
+ const result = buildBasicSearchUrl({
19
+ keywords: { and: ["SaaS", "B2B"] },
20
+ });
21
+ expect(result.url).toContain("keywords=SaaS+AND+B2B");
22
+ });
23
+ it("accepts raw boolean expression as keywords", () => {
24
+ const result = buildBasicSearchUrl({
25
+ keywords: { raw: 'SaaS AND "VP"' },
26
+ });
27
+ expect(result.url).toContain("keywords=SaaS+AND+");
28
+ });
29
+ it("encodes currentCompany as JSON array", () => {
30
+ const result = buildBasicSearchUrl({ currentCompany: ["1441"] });
31
+ const url = new URL(result.url);
32
+ expect(url.searchParams.get("currentCompany")).toBe('["1441"]');
33
+ });
34
+ it("encodes multiple geoUrn values", () => {
35
+ const result = buildBasicSearchUrl({
36
+ geoUrn: ["103644278", "102277331"],
37
+ });
38
+ const url = new URL(result.url);
39
+ expect(url.searchParams.get("geoUrn")).toBe('["103644278","102277331"]');
40
+ });
41
+ it("encodes network filter", () => {
42
+ const result = buildBasicSearchUrl({ network: ["F", "S"] });
43
+ const url = new URL(result.url);
44
+ expect(url.searchParams.get("network")).toBe('["F","S"]');
45
+ });
46
+ it("encodes profileLanguage filter", () => {
47
+ const result = buildBasicSearchUrl({ profileLanguage: ["en"] });
48
+ const url = new URL(result.url);
49
+ expect(url.searchParams.get("profileLanguage")).toBe('["en"]');
50
+ });
51
+ it("encodes school as schoolFilter", () => {
52
+ const result = buildBasicSearchUrl({ school: ["13596"] });
53
+ const url = new URL(result.url);
54
+ expect(url.searchParams.get("schoolFilter")).toBe('["13596"]');
55
+ });
56
+ it("warns on empty filter arrays", () => {
57
+ const result = buildBasicSearchUrl({ currentCompany: [] });
58
+ expect(result.warnings).toHaveLength(1);
59
+ expect(result.warnings[0]).toContain("Empty filter array");
60
+ });
61
+ it("combines multiple filters", () => {
62
+ const result = buildBasicSearchUrl({
63
+ keywords: "software engineer",
64
+ currentCompany: ["1441"],
65
+ geoUrn: ["103644278"],
66
+ });
67
+ const url = new URL(result.url);
68
+ expect(url.searchParams.get("keywords")).toBe("software engineer");
69
+ expect(url.searchParams.get("currentCompany")).toBe('["1441"]');
70
+ expect(url.searchParams.get("geoUrn")).toBe('["103644278"]');
71
+ });
72
+ it("output URL is detected as SearchPage", () => {
73
+ const result = buildBasicSearchUrl({
74
+ keywords: "test",
75
+ });
76
+ expect(detectSourceType(result.url)).toBe("SearchPage");
77
+ });
78
+ });
79
+ //# sourceMappingURL=url-builder.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"url-builder.test.js","sourceRoot":"","sources":["../../src/services/url-builder.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,41 @@
1
+ import type { SourceType } from "../types/collection.js";
2
+ /**
3
+ * Check whether a source type uses a fixed URL (no parameters needed).
4
+ */
5
+ export declare function isFixedUrlType(sourceType: SourceType): boolean;
6
+ /**
7
+ * Check whether a source type uses a parameterised template.
8
+ */
9
+ export declare function isParameterisedType(sourceType: SourceType): boolean;
10
+ /**
11
+ * Check whether a source type uses the basic search URL builder.
12
+ */
13
+ export declare function isSearchBuilderType(sourceType: SourceType): boolean;
14
+ /**
15
+ * Check whether a source type uses the Sales Navigator URL builder.
16
+ */
17
+ export declare function isSNSearchBuilderType(sourceType: SourceType): boolean;
18
+ /**
19
+ * Get the fixed URL for a source type.
20
+ *
21
+ * @returns The URL, or `undefined` if the source type is not a fixed URL type.
22
+ */
23
+ export declare function getFixedUrl(sourceType: SourceType): string | undefined;
24
+ /**
25
+ * Build a parameterised URL for a source type.
26
+ *
27
+ * @param sourceType - The source type to build a URL for.
28
+ * @param params - The slug or ID to substitute.
29
+ * @returns The built URL, or `undefined` if the source type is not parameterised.
30
+ */
31
+ export declare function buildParameterisedUrl(sourceType: SourceType, params: {
32
+ slug?: string;
33
+ id?: string;
34
+ }): string | undefined;
35
+ /**
36
+ * Get the required parameter type for a parameterised source type.
37
+ *
38
+ * @returns `"slug"`, `"id"`, or `undefined` if not a parameterised type.
39
+ */
40
+ export declare function getParameterType(sourceType: SourceType): "slug" | "id" | undefined;
41
+ //# sourceMappingURL=url-templates.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"url-templates.d.ts","sourceRoot":"","sources":["../../src/services/url-templates.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAqEzD;;GAEG;AACH,wBAAgB,cAAc,CAAC,UAAU,EAAE,UAAU,GAAG,OAAO,CAE9D;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,UAAU,EAAE,UAAU,GAAG,OAAO,CAEnE;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,UAAU,EAAE,UAAU,GAAG,OAAO,CAEnE;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,UAAU,EAAE,UAAU,GAAG,OAAO,CAErE;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,UAAU,EAAE,UAAU,GAAG,MAAM,GAAG,SAAS,CAEtE;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CACnC,UAAU,EAAE,UAAU,EACtB,MAAM,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE,GACrC,MAAM,GAAG,SAAS,CASpB;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAC9B,UAAU,EAAE,UAAU,GACrB,MAAM,GAAG,IAAI,GAAG,SAAS,CAE3B"}
@@ -0,0 +1,118 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-only
2
+ // Copyright (C) 2026 Oleksii PELYKH
3
+ /**
4
+ * Fixed LinkedIn URLs that require no parameters.
5
+ */
6
+ const FIXED_URLS = {
7
+ MyConnections: "https://www.linkedin.com/mynetwork/invite-connect/connections/",
8
+ LWVYPP: "https://www.linkedin.com/me/profile-views/",
9
+ SentInvitationPage: "https://www.linkedin.com/mynetwork/invitation-manager/sent/",
10
+ FollowersPage: "https://www.linkedin.com/me/my-network/followers/",
11
+ FollowingPage: "https://www.linkedin.com/me/my-network/following/",
12
+ SNOrgsPage: "https://www.linkedin.com/sales/search/company",
13
+ TSearchPage: "https://www.linkedin.com/talent/search/",
14
+ RSearchPage: "https://www.linkedin.com/recruiter/search/",
15
+ };
16
+ /**
17
+ * Parameterised URL templates.
18
+ *
19
+ * - `{slug}` is replaced with the provided slug string.
20
+ * - `{id}` is replaced with the provided ID string.
21
+ */
22
+ const PARAMETERISED_TEMPLATES = {
23
+ OrganizationPeople: {
24
+ template: "https://www.linkedin.com/company/{slug}/people/",
25
+ param: "slug",
26
+ },
27
+ Alumni: {
28
+ template: "https://www.linkedin.com/school/{slug}/people/",
29
+ param: "slug",
30
+ },
31
+ Group: {
32
+ template: "https://www.linkedin.com/groups/{id}/members/",
33
+ param: "id",
34
+ },
35
+ Event: {
36
+ template: "https://www.linkedin.com/events/{id}/attendees/",
37
+ param: "id",
38
+ },
39
+ SNListPage: {
40
+ template: "https://www.linkedin.com/sales/lists/people/{id}/",
41
+ param: "id",
42
+ },
43
+ SNOrgsListsPage: {
44
+ template: "https://www.linkedin.com/sales/lists/company/{id}/",
45
+ param: "id",
46
+ },
47
+ TProjectPage: {
48
+ template: "https://www.linkedin.com/talent/projects/{id}/",
49
+ param: "id",
50
+ },
51
+ RProjectPage: {
52
+ template: "https://www.linkedin.com/recruiter/projects/{id}/",
53
+ param: "id",
54
+ },
55
+ };
56
+ /** Source types that use the basic search URL builder. */
57
+ const SEARCH_BUILDER_TYPES = new Set(["SearchPage"]);
58
+ /** Source types that use the SN search URL builder. */
59
+ const SN_SEARCH_BUILDER_TYPES = new Set([
60
+ "SNSearchPage",
61
+ ]);
62
+ /**
63
+ * Check whether a source type uses a fixed URL (no parameters needed).
64
+ */
65
+ export function isFixedUrlType(sourceType) {
66
+ return sourceType in FIXED_URLS;
67
+ }
68
+ /**
69
+ * Check whether a source type uses a parameterised template.
70
+ */
71
+ export function isParameterisedType(sourceType) {
72
+ return sourceType in PARAMETERISED_TEMPLATES;
73
+ }
74
+ /**
75
+ * Check whether a source type uses the basic search URL builder.
76
+ */
77
+ export function isSearchBuilderType(sourceType) {
78
+ return SEARCH_BUILDER_TYPES.has(sourceType);
79
+ }
80
+ /**
81
+ * Check whether a source type uses the Sales Navigator URL builder.
82
+ */
83
+ export function isSNSearchBuilderType(sourceType) {
84
+ return SN_SEARCH_BUILDER_TYPES.has(sourceType);
85
+ }
86
+ /**
87
+ * Get the fixed URL for a source type.
88
+ *
89
+ * @returns The URL, or `undefined` if the source type is not a fixed URL type.
90
+ */
91
+ export function getFixedUrl(sourceType) {
92
+ return FIXED_URLS[sourceType];
93
+ }
94
+ /**
95
+ * Build a parameterised URL for a source type.
96
+ *
97
+ * @param sourceType - The source type to build a URL for.
98
+ * @param params - The slug or ID to substitute.
99
+ * @returns The built URL, or `undefined` if the source type is not parameterised.
100
+ */
101
+ export function buildParameterisedUrl(sourceType, params) {
102
+ const entry = PARAMETERISED_TEMPLATES[sourceType];
103
+ if (entry === undefined)
104
+ return undefined;
105
+ const value = entry.param === "slug" ? params.slug : params.id;
106
+ if (value === undefined)
107
+ return undefined;
108
+ return entry.template.replace(`{${entry.param}}`, encodeURIComponent(value));
109
+ }
110
+ /**
111
+ * Get the required parameter type for a parameterised source type.
112
+ *
113
+ * @returns `"slug"`, `"id"`, or `undefined` if not a parameterised type.
114
+ */
115
+ export function getParameterType(sourceType) {
116
+ return PARAMETERISED_TEMPLATES[sourceType]?.param;
117
+ }
118
+ //# sourceMappingURL=url-templates.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"url-templates.js","sourceRoot":"","sources":["../../src/services/url-templates.ts"],"names":[],"mappings":"AAAA,yCAAyC;AACzC,oCAAoC;AAIpC;;GAEG;AACH,MAAM,UAAU,GAAwC;IACtD,aAAa,EACX,gEAAgE;IAClE,MAAM,EAAE,4CAA4C;IACpD,kBAAkB,EAChB,6DAA6D;IAC/D,aAAa,EAAE,mDAAmD;IAClE,aAAa,EAAE,mDAAmD;IAClE,UAAU,EAAE,+CAA+C;IAC3D,WAAW,EAAE,yCAAyC;IACtD,WAAW,EAAE,4CAA4C;CAC1D,CAAC;AAEF;;;;;GAKG;AACH,MAAM,uBAAuB,GAEzB;IACF,kBAAkB,EAAE;QAClB,QAAQ,EAAE,iDAAiD;QAC3D,KAAK,EAAE,MAAM;KACd;IACD,MAAM,EAAE;QACN,QAAQ,EAAE,gDAAgD;QAC1D,KAAK,EAAE,MAAM;KACd;IACD,KAAK,EAAE;QACL,QAAQ,EAAE,+CAA+C;QACzD,KAAK,EAAE,IAAI;KACZ;IACD,KAAK,EAAE;QACL,QAAQ,EAAE,iDAAiD;QAC3D,KAAK,EAAE,IAAI;KACZ;IACD,UAAU,EAAE;QACV,QAAQ,EAAE,mDAAmD;QAC7D,KAAK,EAAE,IAAI;KACZ;IACD,eAAe,EAAE;QACf,QAAQ,EAAE,oDAAoD;QAC9D,KAAK,EAAE,IAAI;KACZ;IACD,YAAY,EAAE;QACZ,QAAQ,EAAE,gDAAgD;QAC1D,KAAK,EAAE,IAAI;KACZ;IACD,YAAY,EAAE;QACZ,QAAQ,EAAE,mDAAmD;QAC7D,KAAK,EAAE,IAAI;KACZ;CACF,CAAC;AAEF,0DAA0D;AAC1D,MAAM,oBAAoB,GAA4B,IAAI,GAAG,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC;AAE9E,uDAAuD;AACvD,MAAM,uBAAuB,GAA4B,IAAI,GAAG,CAAC;IAC/D,cAAc;CACf,CAAC,CAAC;AAEH;;GAEG;AACH,MAAM,UAAU,cAAc,CAAC,UAAsB;IACnD,OAAO,UAAU,IAAI,UAAU,CAAC;AAClC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,mBAAmB,CAAC,UAAsB;IACxD,OAAO,UAAU,IAAI,uBAAuB,CAAC;AAC/C,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,mBAAmB,CAAC,UAAsB;IACxD,OAAO,oBAAoB,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;AAC9C,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,qBAAqB,CAAC,UAAsB;IAC1D,OAAO,uBAAuB,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;AACjD,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,WAAW,CAAC,UAAsB;IAChD,OAAO,UAAU,CAAC,UAAU,CAAC,CAAC;AAChC,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,qBAAqB,CACnC,UAAsB,EACtB,MAAsC;IAEtC,MAAM,KAAK,GAAG,uBAAuB,CAAC,UAAU,CAAC,CAAC;IAClD,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IAE1C,MAAM,KAAK,GACT,KAAK,CAAC,KAAK,KAAK,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC;IACnD,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IAE1C,OAAO,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,KAAK,CAAC,KAAK,GAAG,EAAE,kBAAkB,CAAC,KAAK,CAAC,CAAC,CAAC;AAC/E,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAC9B,UAAsB;IAEtB,OAAO,uBAAuB,CAAC,UAAU,CAAC,EAAE,KAAK,CAAC;AACpD,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=url-templates.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"url-templates.test.d.ts","sourceRoot":"","sources":["../../src/services/url-templates.test.ts"],"names":[],"mappings":""}