@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.
- package/LICENSE +661 -0
- package/dist/cdp/client.d.ts +100 -0
- package/dist/cdp/client.d.ts.map +1 -0
- package/dist/cdp/client.integration.test.d.ts +2 -0
- package/dist/cdp/client.integration.test.d.ts.map +1 -0
- package/dist/cdp/client.integration.test.js +70 -0
- package/dist/cdp/client.integration.test.js.map +1 -0
- package/dist/cdp/client.js +286 -0
- package/dist/cdp/client.js.map +1 -0
- package/dist/cdp/client.test.d.ts +2 -0
- package/dist/cdp/client.test.d.ts.map +1 -0
- package/dist/cdp/client.test.js +269 -0
- package/dist/cdp/client.test.js.map +1 -0
- package/dist/cdp/discovery.d.ts +14 -0
- package/dist/cdp/discovery.d.ts.map +1 -0
- package/dist/cdp/discovery.integration.test.d.ts +2 -0
- package/dist/cdp/discovery.integration.test.d.ts.map +1 -0
- package/dist/cdp/discovery.integration.test.js +25 -0
- package/dist/cdp/discovery.integration.test.js.map +1 -0
- package/dist/cdp/discovery.js +31 -0
- package/dist/cdp/discovery.js.map +1 -0
- package/dist/cdp/discovery.test.d.ts +2 -0
- package/dist/cdp/discovery.test.d.ts.map +1 -0
- package/dist/cdp/discovery.test.js +54 -0
- package/dist/cdp/discovery.test.js.map +1 -0
- package/dist/cdp/errors.d.ts +28 -0
- package/dist/cdp/errors.d.ts.map +1 -0
- package/dist/cdp/errors.js +40 -0
- package/dist/cdp/errors.js.map +1 -0
- package/dist/cdp/errors.test.d.ts +2 -0
- package/dist/cdp/errors.test.d.ts.map +1 -0
- package/dist/cdp/errors.test.js +35 -0
- package/dist/cdp/errors.test.js.map +1 -0
- package/dist/cdp/index.d.ts +5 -0
- package/dist/cdp/index.d.ts.map +1 -0
- package/dist/cdp/index.js +5 -0
- package/dist/cdp/index.js.map +1 -0
- package/dist/cdp/instance-discovery.d.ts +30 -0
- package/dist/cdp/instance-discovery.d.ts.map +1 -0
- package/dist/cdp/instance-discovery.integration.test.d.ts +2 -0
- package/dist/cdp/instance-discovery.integration.test.d.ts.map +1 -0
- package/dist/cdp/instance-discovery.integration.test.js +34 -0
- package/dist/cdp/instance-discovery.integration.test.js.map +1 -0
- package/dist/cdp/instance-discovery.js +130 -0
- package/dist/cdp/instance-discovery.js.map +1 -0
- package/dist/cdp/instance-discovery.test.d.ts +2 -0
- package/dist/cdp/instance-discovery.test.d.ts.map +1 -0
- package/dist/cdp/instance-discovery.test.js +110 -0
- package/dist/cdp/instance-discovery.test.js.map +1 -0
- package/dist/cdp/testing/launch-chromium.d.ts +24 -0
- package/dist/cdp/testing/launch-chromium.d.ts.map +1 -0
- package/dist/cdp/testing/launch-chromium.js +90 -0
- package/dist/cdp/testing/launch-chromium.js.map +1 -0
- package/dist/db/client.d.ts +13 -0
- package/dist/db/client.d.ts.map +1 -0
- package/dist/db/client.integration.test.d.ts +2 -0
- package/dist/db/client.integration.test.d.ts.map +1 -0
- package/dist/db/client.integration.test.js +59 -0
- package/dist/db/client.integration.test.js.map +1 -0
- package/dist/db/client.js +17 -0
- package/dist/db/client.js.map +1 -0
- package/dist/db/client.test.d.ts +2 -0
- package/dist/db/client.test.d.ts.map +1 -0
- package/dist/db/client.test.js +46 -0
- package/dist/db/client.test.js.map +1 -0
- package/dist/db/discovery.d.ts +14 -0
- package/dist/db/discovery.d.ts.map +1 -0
- package/dist/db/discovery.integration.test.d.ts +2 -0
- package/dist/db/discovery.integration.test.d.ts.map +1 -0
- package/dist/db/discovery.integration.test.js +99 -0
- package/dist/db/discovery.integration.test.js.map +1 -0
- package/dist/db/discovery.js +74 -0
- package/dist/db/discovery.js.map +1 -0
- package/dist/db/discovery.test.d.ts +2 -0
- package/dist/db/discovery.test.d.ts.map +1 -0
- package/dist/db/discovery.test.js +123 -0
- package/dist/db/discovery.test.js.map +1 -0
- package/dist/db/errors.d.ts +21 -0
- package/dist/db/errors.d.ts.map +1 -0
- package/dist/db/errors.js +33 -0
- package/dist/db/errors.js.map +1 -0
- package/dist/db/errors.test.d.ts +2 -0
- package/dist/db/errors.test.d.ts.map +1 -0
- package/dist/db/errors.test.js +32 -0
- package/dist/db/errors.test.js.map +1 -0
- package/dist/db/index.d.ts +5 -0
- package/dist/db/index.d.ts.map +1 -0
- package/dist/db/index.js +5 -0
- package/dist/db/index.js.map +1 -0
- package/dist/db/repositories/index.d.ts +2 -0
- package/dist/db/repositories/index.d.ts.map +1 -0
- package/dist/db/repositories/index.js +2 -0
- package/dist/db/repositories/index.js.map +1 -0
- package/dist/db/repositories/profile.d.ts +32 -0
- package/dist/db/repositories/profile.d.ts.map +1 -0
- package/dist/db/repositories/profile.integration.test.d.ts +2 -0
- package/dist/db/repositories/profile.integration.test.d.ts.map +1 -0
- package/dist/db/repositories/profile.integration.test.js +119 -0
- package/dist/db/repositories/profile.integration.test.js.map +1 -0
- package/dist/db/repositories/profile.js +126 -0
- package/dist/db/repositories/profile.js.map +1 -0
- package/dist/db/repositories/profile.test.d.ts +2 -0
- package/dist/db/repositories/profile.test.d.ts.map +1 -0
- package/dist/db/repositories/profile.test.js +126 -0
- package/dist/db/repositories/profile.test.js.map +1 -0
- package/dist/db/testing/create-fixture.d.ts +8 -0
- package/dist/db/testing/create-fixture.d.ts.map +1 -0
- package/dist/db/testing/create-fixture.js +286 -0
- package/dist/db/testing/create-fixture.js.map +1 -0
- package/dist/db/testing/open-fixture.d.ts +14 -0
- package/dist/db/testing/open-fixture.d.ts.map +1 -0
- package/dist/db/testing/open-fixture.js +21 -0
- package/dist/db/testing/open-fixture.js.map +1 -0
- package/dist/index.d.ts +5 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -1
- package/dist/index.js.map +1 -1
- package/dist/services/app.d.ts +62 -0
- package/dist/services/app.d.ts.map +1 -0
- package/dist/services/app.js +198 -0
- package/dist/services/app.js.map +1 -0
- package/dist/services/app.test.d.ts +2 -0
- package/dist/services/app.test.d.ts.map +1 -0
- package/dist/services/app.test.js +265 -0
- package/dist/services/app.test.js.map +1 -0
- package/dist/services/errors.d.ts +45 -0
- package/dist/services/errors.d.ts.map +1 -0
- package/dist/services/errors.js +66 -0
- package/dist/services/errors.js.map +1 -0
- package/dist/services/errors.test.d.ts +2 -0
- package/dist/services/errors.test.d.ts.map +1 -0
- package/dist/services/errors.test.js +71 -0
- package/dist/services/errors.test.js.map +1 -0
- package/dist/services/index.d.ts +8 -0
- package/dist/services/index.d.ts.map +1 -0
- package/dist/services/index.js +8 -0
- package/dist/services/index.js.map +1 -0
- package/dist/services/instance-lifecycle.d.ts +38 -0
- package/dist/services/instance-lifecycle.d.ts.map +1 -0
- package/dist/services/instance-lifecycle.js +87 -0
- package/dist/services/instance-lifecycle.js.map +1 -0
- package/dist/services/instance-lifecycle.test.d.ts +2 -0
- package/dist/services/instance-lifecycle.test.d.ts.map +1 -0
- package/dist/services/instance-lifecycle.test.js +152 -0
- package/dist/services/instance-lifecycle.test.js.map +1 -0
- package/dist/services/instance.d.ts +50 -0
- package/dist/services/instance.d.ts.map +1 -0
- package/dist/services/instance.js +121 -0
- package/dist/services/instance.js.map +1 -0
- package/dist/services/instance.test.d.ts +2 -0
- package/dist/services/instance.test.d.ts.map +1 -0
- package/dist/services/instance.test.js +181 -0
- package/dist/services/instance.test.js.map +1 -0
- package/dist/services/launcher.d.ts +51 -0
- package/dist/services/launcher.d.ts.map +1 -0
- package/dist/services/launcher.js +147 -0
- package/dist/services/launcher.js.map +1 -0
- package/dist/services/launcher.test.d.ts +2 -0
- package/dist/services/launcher.test.d.ts.map +1 -0
- package/dist/services/launcher.test.js +126 -0
- package/dist/services/launcher.test.js.map +1 -0
- package/dist/services/profile.d.ts +44 -0
- package/dist/services/profile.d.ts.map +1 -0
- package/dist/services/profile.js +83 -0
- package/dist/services/profile.js.map +1 -0
- package/dist/services/profile.test.d.ts +2 -0
- package/dist/services/profile.test.d.ts.map +1 -0
- package/dist/services/profile.test.js +145 -0
- package/dist/services/profile.test.js.map +1 -0
- package/dist/services/status.d.ts +33 -0
- package/dist/services/status.d.ts.map +1 -0
- package/dist/services/status.js +76 -0
- package/dist/services/status.js.map +1 -0
- package/dist/services/status.test.d.ts +2 -0
- package/dist/services/status.test.d.ts.map +1 -0
- package/dist/services/status.test.js +207 -0
- package/dist/services/status.test.js.map +1 -0
- package/dist/testing/e2e-helpers.d.ts +41 -0
- package/dist/testing/e2e-helpers.d.ts.map +1 -0
- package/dist/testing/e2e-helpers.js +111 -0
- package/dist/testing/e2e-helpers.js.map +1 -0
- package/dist/types/account.d.ts +13 -0
- package/dist/types/account.d.ts.map +1 -0
- package/dist/types/account.js +2 -0
- package/dist/types/account.js.map +1 -0
- package/dist/types/account.test.d.ts +2 -0
- package/dist/types/account.test.d.ts.map +1 -0
- package/dist/types/account.test.js +24 -0
- package/dist/types/account.test.js.map +1 -0
- package/dist/types/cdp.d.ts +18 -0
- package/dist/types/cdp.d.ts.map +1 -0
- package/dist/types/cdp.js +2 -0
- package/dist/types/cdp.js.map +1 -0
- package/dist/types/cdp.test.d.ts +2 -0
- package/dist/types/cdp.test.d.ts.map +1 -0
- package/dist/types/cdp.test.js +28 -0
- package/dist/types/cdp.test.js.map +1 -0
- package/dist/types/index.d.ts +4 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +2 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/instance.d.ts +36 -0
- package/dist/types/instance.d.ts.map +1 -0
- package/dist/types/instance.js +8 -0
- package/dist/types/instance.js.map +1 -0
- package/dist/types/instance.test.d.ts +2 -0
- package/dist/types/instance.test.d.ts.map +1 -0
- package/dist/types/instance.test.js +57 -0
- package/dist/types/instance.test.js.map +1 -0
- package/dist/types/profile.d.ts +50 -0
- package/dist/types/profile.d.ts.map +1 -0
- package/dist/types/profile.js +8 -0
- package/dist/types/profile.js.map +1 -0
- package/dist/types/profile.test.d.ts +2 -0
- package/dist/types/profile.test.d.ts.map +1 -0
- package/dist/types/profile.test.js +103 -0
- package/dist/types/profile.test.js.map +1 -0
- package/package.json +20 -8
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generates a test fixture SQLite database with the real LinkedHelper
|
|
3
|
+
* schema populated with synthetic (non-personal) mock data.
|
|
4
|
+
*
|
|
5
|
+
* Run: npx tsx src/db/testing/create-fixture.ts
|
|
6
|
+
*/
|
|
7
|
+
import { writeFileSync } from "node:fs";
|
|
8
|
+
import { dirname, join } from "node:path";
|
|
9
|
+
import { fileURLToPath } from "node:url";
|
|
10
|
+
import Database from "better-sqlite3";
|
|
11
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
12
|
+
const FIXTURE_PATH = join(__dirname, "fixture.db");
|
|
13
|
+
const db = new Database("");
|
|
14
|
+
// ── Schema (matches real LinkedHelper DDL) ──────────────────────────
|
|
15
|
+
db.exec(`
|
|
16
|
+
CREATE TABLE disabled_triggers(
|
|
17
|
+
id INTEGER PRIMARY KEY,
|
|
18
|
+
trigger_name TEXT,
|
|
19
|
+
UNIQUE (trigger_name)
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
CREATE TABLE people(
|
|
23
|
+
id INTEGER PRIMARY KEY,
|
|
24
|
+
original_id INTEGER,
|
|
25
|
+
created_at DATETIME DEFAULT (STRFTIME('%Y-%m-%dT%H:%M:%fZ', 'NOW')),
|
|
26
|
+
updated_at DATETIME DEFAULT (STRFTIME('%Y-%m-%dT%H:%M:%fZ', 'NOW'))
|
|
27
|
+
);
|
|
28
|
+
CREATE INDEX people_original_id_idx ON people(original_id);
|
|
29
|
+
|
|
30
|
+
CREATE TABLE person_mini_profile(
|
|
31
|
+
id INTEGER PRIMARY KEY,
|
|
32
|
+
person_id INTEGER NOT NULL,
|
|
33
|
+
first_name TEXT NOT NULL,
|
|
34
|
+
first_name_uppercase TEXT,
|
|
35
|
+
last_name TEXT,
|
|
36
|
+
last_name_uppercase TEXT,
|
|
37
|
+
headline TEXT,
|
|
38
|
+
headline_uppercase TEXT,
|
|
39
|
+
avatar TEXT,
|
|
40
|
+
first_mutual_full_name TEXT,
|
|
41
|
+
second_mutual_full_name TEXT,
|
|
42
|
+
created_at DATETIME DEFAULT (STRFTIME('%Y-%m-%dT%H:%M:%fZ', 'NOW')),
|
|
43
|
+
updated_at DATETIME DEFAULT (STRFTIME('%Y-%m-%dT%H:%M:%fZ', 'NOW')),
|
|
44
|
+
UNIQUE (person_id),
|
|
45
|
+
FOREIGN KEY(person_id) REFERENCES people(id)
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
CREATE TABLE person_external_ids(
|
|
49
|
+
id INTEGER PRIMARY KEY,
|
|
50
|
+
person_id INTEGER NOT NULL,
|
|
51
|
+
external_id TEXT NOT NULL,
|
|
52
|
+
external_id_uppercase TEXT NOT NULL,
|
|
53
|
+
type_group TEXT NOT NULL CHECK(type_group IN ('member', 'public', 'hash', 'avatar')),
|
|
54
|
+
is_member_id INTEGER,
|
|
55
|
+
created_at DATETIME DEFAULT (STRFTIME('%Y-%m-%dT%H:%M:%fZ', 'NOW')),
|
|
56
|
+
updated_at DATETIME DEFAULT (STRFTIME('%Y-%m-%dT%H:%M:%fZ', 'NOW')),
|
|
57
|
+
UNIQUE(external_id, type_group),
|
|
58
|
+
FOREIGN KEY(person_id) REFERENCES people(id)
|
|
59
|
+
);
|
|
60
|
+
CREATE INDEX person_external_ids_person_idx ON person_external_ids(person_id);
|
|
61
|
+
|
|
62
|
+
CREATE TABLE person_current_position(
|
|
63
|
+
id INTEGER PRIMARY KEY,
|
|
64
|
+
person_id INTEGER NOT NULL,
|
|
65
|
+
company TEXT,
|
|
66
|
+
company_uppercase TEXT,
|
|
67
|
+
position TEXT,
|
|
68
|
+
position_uppercase TEXT,
|
|
69
|
+
created_at DATETIME DEFAULT (STRFTIME('%Y-%m-%dT%H:%M:%fZ', 'NOW')),
|
|
70
|
+
updated_at DATETIME DEFAULT (STRFTIME('%Y-%m-%dT%H:%M:%fZ', 'NOW')),
|
|
71
|
+
UNIQUE(person_id),
|
|
72
|
+
FOREIGN KEY(person_id) REFERENCES people(id)
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
CREATE TABLE person_positions(
|
|
76
|
+
id INTEGER PRIMARY KEY,
|
|
77
|
+
person_id INTEGER NOT NULL,
|
|
78
|
+
title TEXT NOT NULL,
|
|
79
|
+
company_name TEXT NOT NULL,
|
|
80
|
+
company_id TEXT,
|
|
81
|
+
start DATETIME,
|
|
82
|
+
start_year INTEGER,
|
|
83
|
+
start_month INTEGER,
|
|
84
|
+
"end" DATETIME,
|
|
85
|
+
end_year INTEGER,
|
|
86
|
+
end_month INTEGER,
|
|
87
|
+
location_name TEXT,
|
|
88
|
+
description TEXT,
|
|
89
|
+
is_default INTEGER,
|
|
90
|
+
created_at DATETIME DEFAULT (STRFTIME('%Y-%m-%dT%H:%M:%fZ', 'NOW')),
|
|
91
|
+
updated_at DATETIME DEFAULT (STRFTIME('%Y-%m-%dT%H:%M:%fZ', 'NOW')),
|
|
92
|
+
sent_at_to_pas DATETIME,
|
|
93
|
+
actual_at DATETIME NOT NULL,
|
|
94
|
+
UNIQUE(person_id, is_default),
|
|
95
|
+
FOREIGN KEY(person_id) REFERENCES people(id),
|
|
96
|
+
CHECK(is_default IS NULL OR (is_default IS NOT NULL AND "end" IS NULL))
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
CREATE TABLE person_education(
|
|
100
|
+
id INTEGER PRIMARY KEY,
|
|
101
|
+
person_id INTEGER NOT NULL,
|
|
102
|
+
school_name TEXT NOT NULL,
|
|
103
|
+
degree_name TEXT,
|
|
104
|
+
field_of_study TEXT,
|
|
105
|
+
description TEXT,
|
|
106
|
+
start_year INTEGER,
|
|
107
|
+
start_month INTEGER,
|
|
108
|
+
end_year INTEGER,
|
|
109
|
+
end_month INTEGER,
|
|
110
|
+
created_at DATETIME DEFAULT (STRFTIME('%Y-%m-%dT%H:%M:%fZ', 'NOW')),
|
|
111
|
+
updated_at DATETIME DEFAULT (STRFTIME('%Y-%m-%dT%H:%M:%fZ', 'NOW')),
|
|
112
|
+
sent_at_to_pas DATETIME,
|
|
113
|
+
actual_at DATETIME NOT NULL,
|
|
114
|
+
FOREIGN KEY(person_id) REFERENCES people(id)
|
|
115
|
+
);
|
|
116
|
+
CREATE INDEX person_education_person_idx ON person_education(person_id);
|
|
117
|
+
|
|
118
|
+
CREATE TABLE skills(
|
|
119
|
+
id INTEGER PRIMARY KEY,
|
|
120
|
+
name TEXT NOT NULL CHECK (length(name) > 0),
|
|
121
|
+
created_at DATETIME DEFAULT (STRFTIME('%Y-%m-%dT%H:%M:%fZ', 'NOW')),
|
|
122
|
+
updated_at DATETIME DEFAULT (STRFTIME('%Y-%m-%dT%H:%M:%fZ', 'NOW')),
|
|
123
|
+
UNIQUE (name)
|
|
124
|
+
);
|
|
125
|
+
|
|
126
|
+
CREATE TABLE person_skill(
|
|
127
|
+
id INTEGER PRIMARY KEY,
|
|
128
|
+
person_id INTEGER NOT NULL,
|
|
129
|
+
skill_id INTEGER NOT NULL,
|
|
130
|
+
endorsements_count INTEGER CHECK (endorsements_count IS NULL OR typeof(endorsements_count) = 'integer' AND endorsements_count >= 0),
|
|
131
|
+
created_at DATETIME DEFAULT (STRFTIME('%Y-%m-%dT%H:%M:%fZ', 'NOW')),
|
|
132
|
+
updated_at DATETIME DEFAULT (STRFTIME('%Y-%m-%dT%H:%M:%fZ', 'NOW')),
|
|
133
|
+
sent_at_to_pas DATETIME,
|
|
134
|
+
actual_at DATETIME NOT NULL,
|
|
135
|
+
UNIQUE (person_id, skill_id),
|
|
136
|
+
FOREIGN KEY(person_id) REFERENCES people(id),
|
|
137
|
+
FOREIGN KEY(skill_id) REFERENCES skills(id)
|
|
138
|
+
);
|
|
139
|
+
|
|
140
|
+
CREATE TABLE person_email(
|
|
141
|
+
id INTEGER PRIMARY KEY,
|
|
142
|
+
person_id INTEGER NOT NULL,
|
|
143
|
+
email TEXT NOT NULL,
|
|
144
|
+
type TEXT NOT NULL CHECK (type IN ('personal', 'business')),
|
|
145
|
+
created_at DATETIME DEFAULT (STRFTIME('%Y-%m-%dT%H:%M:%fZ', 'NOW')),
|
|
146
|
+
updated_at DATETIME DEFAULT (STRFTIME('%Y-%m-%dT%H:%M:%fZ', 'NOW')),
|
|
147
|
+
sent_at_to_pas DATETIME,
|
|
148
|
+
actual_at DATETIME NOT NULL,
|
|
149
|
+
UNIQUE (person_id, email),
|
|
150
|
+
FOREIGN KEY(person_id) REFERENCES people(id)
|
|
151
|
+
);
|
|
152
|
+
`);
|
|
153
|
+
// ── Mock Data (obviously synthetic, no real PII) ────────────────────
|
|
154
|
+
const NOW = "2025-01-15T12:00:00.000Z";
|
|
155
|
+
// Person 1: full profile with all fields populated
|
|
156
|
+
db.exec(`
|
|
157
|
+
INSERT INTO people (id, original_id, created_at, updated_at)
|
|
158
|
+
VALUES (1, 1, '${NOW}', '${NOW}');
|
|
159
|
+
|
|
160
|
+
INSERT INTO person_mini_profile
|
|
161
|
+
(person_id, first_name, first_name_uppercase, last_name, last_name_uppercase,
|
|
162
|
+
headline, headline_uppercase, avatar)
|
|
163
|
+
VALUES
|
|
164
|
+
(1, 'Ada', 'ADA', 'Lovelace', 'LOVELACE',
|
|
165
|
+
'Principal Analytical Engine Programmer',
|
|
166
|
+
'PRINCIPAL ANALYTICAL ENGINE PROGRAMMER',
|
|
167
|
+
'https://example.test/avatars/ada.jpg');
|
|
168
|
+
|
|
169
|
+
INSERT INTO person_external_ids
|
|
170
|
+
(person_id, external_id, external_id_uppercase, type_group, is_member_id)
|
|
171
|
+
VALUES
|
|
172
|
+
(1, 'ada-lovelace-test', 'ADA-LOVELACE-TEST', 'public', NULL),
|
|
173
|
+
(1, '100000001', '100000001', 'member', 1),
|
|
174
|
+
(1, 'h4sh-ada-001', 'H4SH-ADA-001', 'hash', NULL);
|
|
175
|
+
|
|
176
|
+
INSERT INTO person_current_position
|
|
177
|
+
(person_id, company, company_uppercase, position, position_uppercase)
|
|
178
|
+
VALUES
|
|
179
|
+
(1, 'Babbage Industries', 'BABBAGE INDUSTRIES',
|
|
180
|
+
'Lead Programmer', 'LEAD PROGRAMMER');
|
|
181
|
+
|
|
182
|
+
INSERT INTO person_positions
|
|
183
|
+
(person_id, title, company_name, company_id, start_year, start_month,
|
|
184
|
+
is_default, actual_at)
|
|
185
|
+
VALUES
|
|
186
|
+
(1, 'Lead Programmer', 'Babbage Industries', 'C001',
|
|
187
|
+
2020, 3, 1, '${NOW}');
|
|
188
|
+
|
|
189
|
+
INSERT INTO person_positions
|
|
190
|
+
(person_id, title, company_name, company_id, start_year, start_month,
|
|
191
|
+
end_year, end_month, actual_at)
|
|
192
|
+
VALUES
|
|
193
|
+
(1, 'Junior Analyst', 'Difference Engine Co', 'C002',
|
|
194
|
+
2015, 9, 2019, 12, '${NOW}');
|
|
195
|
+
|
|
196
|
+
INSERT INTO person_education
|
|
197
|
+
(person_id, school_name, degree_name, field_of_study,
|
|
198
|
+
start_year, end_year, actual_at)
|
|
199
|
+
VALUES
|
|
200
|
+
(1, 'University of Mathematica', 'BSc', 'Mathematics',
|
|
201
|
+
2011, 2015, '${NOW}');
|
|
202
|
+
|
|
203
|
+
INSERT INTO person_education
|
|
204
|
+
(person_id, school_name, degree_name, field_of_study,
|
|
205
|
+
start_year, end_year, actual_at)
|
|
206
|
+
VALUES
|
|
207
|
+
(1, 'Royal Polytechnic', 'MSc', 'Computational Logic',
|
|
208
|
+
2015, 2017, '${NOW}');
|
|
209
|
+
|
|
210
|
+
INSERT INTO skills (id, name) VALUES (1, 'Algorithm Design');
|
|
211
|
+
INSERT INTO skills (id, name) VALUES (2, 'Mechanical Computing');
|
|
212
|
+
INSERT INTO skills (id, name) VALUES (3, 'Technical Writing');
|
|
213
|
+
|
|
214
|
+
INSERT INTO person_skill (person_id, skill_id, endorsements_count, actual_at)
|
|
215
|
+
VALUES (1, 1, 42, '${NOW}');
|
|
216
|
+
INSERT INTO person_skill (person_id, skill_id, endorsements_count, actual_at)
|
|
217
|
+
VALUES (1, 2, 17, '${NOW}');
|
|
218
|
+
|
|
219
|
+
INSERT INTO person_email (person_id, email, type, actual_at)
|
|
220
|
+
VALUES (1, 'ada@example.test', 'business', '${NOW}');
|
|
221
|
+
`);
|
|
222
|
+
// Person 2: minimal profile (only required fields)
|
|
223
|
+
db.exec(`
|
|
224
|
+
INSERT INTO people (id, original_id, created_at, updated_at)
|
|
225
|
+
VALUES (2, 2, '${NOW}', '${NOW}');
|
|
226
|
+
|
|
227
|
+
INSERT INTO person_mini_profile
|
|
228
|
+
(person_id, first_name, first_name_uppercase)
|
|
229
|
+
VALUES (2, 'Charlie', 'CHARLIE');
|
|
230
|
+
|
|
231
|
+
INSERT INTO person_external_ids
|
|
232
|
+
(person_id, external_id, external_id_uppercase, type_group)
|
|
233
|
+
VALUES (2, 'charlie-test', 'CHARLIE-TEST', 'public');
|
|
234
|
+
`);
|
|
235
|
+
// Person 3: another full profile for variety
|
|
236
|
+
db.exec(`
|
|
237
|
+
INSERT INTO people (id, original_id, created_at, updated_at)
|
|
238
|
+
VALUES (3, 3, '${NOW}', '${NOW}');
|
|
239
|
+
|
|
240
|
+
INSERT INTO person_mini_profile
|
|
241
|
+
(person_id, first_name, first_name_uppercase, last_name, last_name_uppercase,
|
|
242
|
+
headline, headline_uppercase)
|
|
243
|
+
VALUES
|
|
244
|
+
(3, 'Grace', 'GRACE', 'Hopper', 'HOPPER',
|
|
245
|
+
'Compiler Pioneer at COBOL Systems',
|
|
246
|
+
'COMPILER PIONEER AT COBOL SYSTEMS');
|
|
247
|
+
|
|
248
|
+
INSERT INTO person_external_ids
|
|
249
|
+
(person_id, external_id, external_id_uppercase, type_group, is_member_id)
|
|
250
|
+
VALUES
|
|
251
|
+
(3, 'grace-hopper-test', 'GRACE-HOPPER-TEST', 'public', NULL),
|
|
252
|
+
(3, '100000003', '100000003', 'member', 1);
|
|
253
|
+
|
|
254
|
+
INSERT INTO person_current_position
|
|
255
|
+
(person_id, company, company_uppercase, position, position_uppercase)
|
|
256
|
+
VALUES
|
|
257
|
+
(3, 'COBOL Systems Inc', 'COBOL SYSTEMS INC',
|
|
258
|
+
'Distinguished Engineer', 'DISTINGUISHED ENGINEER');
|
|
259
|
+
|
|
260
|
+
INSERT INTO person_positions
|
|
261
|
+
(person_id, title, company_name, start_year, start_month,
|
|
262
|
+
is_default, actual_at)
|
|
263
|
+
VALUES
|
|
264
|
+
(3, 'Distinguished Engineer', 'COBOL Systems Inc',
|
|
265
|
+
2018, 1, 1, '${NOW}');
|
|
266
|
+
|
|
267
|
+
INSERT INTO person_education
|
|
268
|
+
(person_id, school_name, degree_name, field_of_study,
|
|
269
|
+
start_year, end_year, actual_at)
|
|
270
|
+
VALUES
|
|
271
|
+
(3, 'Vassar College', 'BA', 'Mathematics and Physics',
|
|
272
|
+
1924, 1928, '${NOW}');
|
|
273
|
+
|
|
274
|
+
INSERT INTO person_skill (person_id, skill_id, endorsements_count, actual_at)
|
|
275
|
+
VALUES (3, 3, 99, '${NOW}');
|
|
276
|
+
|
|
277
|
+
INSERT INTO person_email (person_id, email, type, actual_at)
|
|
278
|
+
VALUES (3, 'grace@example.test', 'business', '${NOW}');
|
|
279
|
+
INSERT INTO person_email (person_id, email, type, actual_at)
|
|
280
|
+
VALUES (3, 'grace.personal@example.test', 'personal', '${NOW}');
|
|
281
|
+
`);
|
|
282
|
+
// ── Write to disk ───────────────────────────────────────────────────
|
|
283
|
+
writeFileSync(FIXTURE_PATH, db.serialize());
|
|
284
|
+
db.close();
|
|
285
|
+
console.log(`Fixture written to ${FIXTURE_PATH}`);
|
|
286
|
+
//# sourceMappingURL=create-fixture.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create-fixture.js","sourceRoot":"","sources":["../../../src/db/testing/create-fixture.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACxC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,QAAQ,MAAM,gBAAgB,CAAC;AAEtC,MAAM,SAAS,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AAC1D,MAAM,YAAY,GAAG,IAAI,CAAC,SAAS,EAAE,YAAY,CAAC,CAAC;AAEnD,MAAM,EAAE,GAAG,IAAI,QAAQ,CAAC,EAAE,CAAC,CAAC;AAE5B,uEAAuE;AAEvE,EAAE,CAAC,IAAI,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAyIP,CAAC,CAAC;AAEH,uEAAuE;AAEvE,MAAM,GAAG,GAAG,0BAA0B,CAAC;AAEvC,mDAAmD;AACnD,EAAE,CAAC,IAAI,CAAC;;mBAEW,GAAG,OAAO,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;oBA6BZ,GAAG;;;;;;;2BAOI,GAAG;;;;;;;oBAOV,GAAG;;;;;;;oBAOH,GAAG;;;;;;;uBAOA,GAAG;;uBAEH,GAAG;;;gDAGsB,GAAG;CAClD,CAAC,CAAC;AAEH,mDAAmD;AACnD,EAAE,CAAC,IAAI,CAAC;;mBAEW,GAAG,OAAO,GAAG;;;;;;;;;CAS/B,CAAC,CAAC;AAEH,6CAA6C;AAC7C,EAAE,CAAC,IAAI,CAAC;;mBAEW,GAAG,OAAO,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;oBA2BZ,GAAG;;;;;;;oBAOH,GAAG;;;uBAGA,GAAG;;;kDAGwB,GAAG;;2DAEM,GAAG;CAC7D,CAAC,CAAC;AAEH,uEAAuE;AAEvE,aAAa,CAAC,YAAY,EAAE,EAAE,CAAC,SAAS,EAAE,CAAC,CAAC;AAC5C,EAAE,CAAC,KAAK,EAAE,CAAC;AAEX,OAAO,CAAC,GAAG,CAAC,sBAAsB,YAAY,EAAE,CAAC,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import Database from "better-sqlite3";
|
|
2
|
+
/** Absolute path to the test fixture database file. */
|
|
3
|
+
export declare const FIXTURE_PATH: string;
|
|
4
|
+
/**
|
|
5
|
+
* Opens the fixture database as a writable in-memory copy.
|
|
6
|
+
*
|
|
7
|
+
* Each call returns an independent database instance so tests cannot
|
|
8
|
+
* interfere with each other. The real LinkedHelper schema (tables,
|
|
9
|
+
* indexes, CHECK constraints) and synthetic mock data are included.
|
|
10
|
+
*
|
|
11
|
+
* Call `.close()` when done.
|
|
12
|
+
*/
|
|
13
|
+
export declare function openFixture(): Database.Database;
|
|
14
|
+
//# sourceMappingURL=open-fixture.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"open-fixture.d.ts","sourceRoot":"","sources":["../../../src/db/testing/open-fixture.ts"],"names":[],"mappings":"AAGA,OAAO,QAAQ,MAAM,gBAAgB,CAAC;AAItC,uDAAuD;AACvD,eAAO,MAAM,YAAY,QAAgC,CAAC;AAE1D;;;;;;;;GAQG;AACH,wBAAgB,WAAW,IAAI,QAAQ,CAAC,QAAQ,CAG/C"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { dirname, join } from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import Database from "better-sqlite3";
|
|
5
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
6
|
+
/** Absolute path to the test fixture database file. */
|
|
7
|
+
export const FIXTURE_PATH = join(__dirname, "fixture.db");
|
|
8
|
+
/**
|
|
9
|
+
* Opens the fixture database as a writable in-memory copy.
|
|
10
|
+
*
|
|
11
|
+
* Each call returns an independent database instance so tests cannot
|
|
12
|
+
* interfere with each other. The real LinkedHelper schema (tables,
|
|
13
|
+
* indexes, CHECK constraints) and synthetic mock data are included.
|
|
14
|
+
*
|
|
15
|
+
* Call `.close()` when done.
|
|
16
|
+
*/
|
|
17
|
+
export function openFixture() {
|
|
18
|
+
const buffer = readFileSync(FIXTURE_PATH);
|
|
19
|
+
return new Database(buffer);
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=open-fixture.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"open-fixture.js","sourceRoot":"","sources":["../../../src/db/testing/open-fixture.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,QAAQ,MAAM,gBAAgB,CAAC;AAEtC,MAAM,SAAS,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AAE1D,uDAAuD;AACvD,MAAM,CAAC,MAAM,YAAY,GAAG,IAAI,CAAC,SAAS,EAAE,YAAY,CAAC,CAAC;AAE1D;;;;;;;;GAQG;AACH,MAAM,UAAU,WAAW;IACzB,MAAM,MAAM,GAAG,YAAY,CAAC,YAAY,CAAC,CAAC;IAC1C,OAAO,IAAI,QAAQ,CAAC,MAAM,CAAC,CAAC;AAC9B,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,2 +1,6 @@
|
|
|
1
|
-
export {};
|
|
1
|
+
export type { Account, CurrentPosition, Education, ExternalId, ExternalIdTypeGroup, InstanceInfo, InstanceStatus, MiniProfile, Position, Profile, Skill, StartInstanceParams, StartInstanceResult, } from "./types/index.js";
|
|
2
|
+
export { AppLaunchError, AppNotFoundError, AppService, type AppServiceOptions, ExtractionTimeoutError, extractSlug, InstanceNotRunningError, InstanceService, LauncherService, LinkedHelperNotRunningError, ProfileService, ServiceError, StartInstanceError, startInstanceWithRecovery, type StartInstanceOutcome, type VisitAndExtractOptions, checkStatus, type AccountInstanceStatus, type DatabaseStatus, type LauncherStatus, type StatusReport, waitForInstancePort, } from "./services/index.js";
|
|
3
|
+
export { DatabaseClient, discoverAllDatabases, discoverDatabase, ProfileRepository, } from "./db/index.js";
|
|
4
|
+
export { DatabaseError, DatabaseNotFoundError, ProfileNotFoundError, } from "./db/index.js";
|
|
5
|
+
export { CDPConnectionError, CDPError, CDPEvaluationError, CDPTimeoutError, discoverInstancePort, } from "./cdp/index.js";
|
|
2
6
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,YAAY,EACV,OAAO,EACP,eAAe,EACf,SAAS,EACT,UAAU,EACV,mBAAmB,EACnB,YAAY,EACZ,cAAc,EACd,WAAW,EACX,QAAQ,EACR,OAAO,EACP,KAAK,EACL,mBAAmB,EACnB,mBAAmB,GACpB,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EACL,cAAc,EACd,gBAAgB,EAChB,UAAU,EACV,KAAK,iBAAiB,EACtB,sBAAsB,EACtB,WAAW,EACX,uBAAuB,EACvB,eAAe,EACf,eAAe,EACf,2BAA2B,EAC3B,cAAc,EACd,YAAY,EACZ,kBAAkB,EAClB,yBAAyB,EACzB,KAAK,oBAAoB,EACzB,KAAK,sBAAsB,EAC3B,WAAW,EACX,KAAK,qBAAqB,EAC1B,KAAK,cAAc,EACnB,KAAK,cAAc,EACnB,KAAK,YAAY,EACjB,mBAAmB,GACpB,MAAM,qBAAqB,CAAC;AAG7B,OAAO,EACL,cAAc,EACd,oBAAoB,EACpB,gBAAgB,EAChB,iBAAiB,GAClB,MAAM,eAAe,CAAC;AAGvB,OAAO,EACL,aAAa,EACb,qBAAqB,EACrB,oBAAoB,GACrB,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,kBAAkB,EAClB,QAAQ,EACR,kBAAkB,EAClB,eAAe,EACf,oBAAoB,GACrB,MAAM,gBAAgB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,8 @@
|
|
|
1
|
-
|
|
1
|
+
// Services
|
|
2
|
+
export { AppLaunchError, AppNotFoundError, AppService, ExtractionTimeoutError, extractSlug, InstanceNotRunningError, InstanceService, LauncherService, LinkedHelperNotRunningError, ProfileService, ServiceError, StartInstanceError, startInstanceWithRecovery, checkStatus, waitForInstancePort, } from "./services/index.js";
|
|
3
|
+
// Data access
|
|
4
|
+
export { DatabaseClient, discoverAllDatabases, discoverDatabase, ProfileRepository, } from "./db/index.js";
|
|
5
|
+
// Errors (DB + CDP errors can propagate through the service layer)
|
|
6
|
+
export { DatabaseError, DatabaseNotFoundError, ProfileNotFoundError, } from "./db/index.js";
|
|
7
|
+
export { CDPConnectionError, CDPError, CDPEvaluationError, CDPTimeoutError, discoverInstancePort, } from "./cdp/index.js";
|
|
2
8
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAiBA,WAAW;AACX,OAAO,EACL,cAAc,EACd,gBAAgB,EAChB,UAAU,EAEV,sBAAsB,EACtB,WAAW,EACX,uBAAuB,EACvB,eAAe,EACf,eAAe,EACf,2BAA2B,EAC3B,cAAc,EACd,YAAY,EACZ,kBAAkB,EAClB,yBAAyB,EAGzB,WAAW,EAKX,mBAAmB,GACpB,MAAM,qBAAqB,CAAC;AAE7B,cAAc;AACd,OAAO,EACL,cAAc,EACd,oBAAoB,EACpB,gBAAgB,EAChB,iBAAiB,GAClB,MAAM,eAAe,CAAC;AAEvB,mEAAmE;AACnE,OAAO,EACL,aAAa,EACb,qBAAqB,EACrB,oBAAoB,GACrB,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,kBAAkB,EAClB,QAAQ,EACR,kBAAkB,EAClB,eAAe,EACf,oBAAoB,GACrB,MAAM,gBAAgB,CAAC"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
export interface AppServiceOptions {
|
|
2
|
+
/** Delay in ms after spawn before checking if the app is reachable (default 3000). */
|
|
3
|
+
launchProbeDelay?: number;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Manages the LinkedHelper application process lifecycle.
|
|
7
|
+
*
|
|
8
|
+
* Provides methods to launch, quit, and probe the LinkedHelper
|
|
9
|
+
* Electron application. When no explicit CDP port is provided,
|
|
10
|
+
* a free port is selected automatically at launch time.
|
|
11
|
+
*/
|
|
12
|
+
export declare class AppService {
|
|
13
|
+
private assignedPort;
|
|
14
|
+
private childProcess;
|
|
15
|
+
private readonly launchProbeDelay;
|
|
16
|
+
/**
|
|
17
|
+
* @param cdpPort - Explicit CDP port. When omitted, `launch()` will
|
|
18
|
+
* select a free port automatically via `get-port`.
|
|
19
|
+
* @param options - Additional configuration options.
|
|
20
|
+
*/
|
|
21
|
+
constructor(cdpPort?: number, options?: AppServiceOptions);
|
|
22
|
+
/**
|
|
23
|
+
* The CDP port currently in use.
|
|
24
|
+
*
|
|
25
|
+
* @throws {Error} if neither an explicit port was provided nor
|
|
26
|
+
* `launch()` has been called yet.
|
|
27
|
+
*/
|
|
28
|
+
get cdpPort(): number;
|
|
29
|
+
/**
|
|
30
|
+
* Launch the LinkedHelper application with CDP enabled.
|
|
31
|
+
*
|
|
32
|
+
* If no CDP port was specified in the constructor, a free port
|
|
33
|
+
* is selected automatically.
|
|
34
|
+
*
|
|
35
|
+
* @throws {AppNotFoundError} if the binary cannot be found.
|
|
36
|
+
* @throws {AppLaunchError} if the process fails to start.
|
|
37
|
+
*/
|
|
38
|
+
launch(): Promise<void>;
|
|
39
|
+
/**
|
|
40
|
+
* Quit the LinkedHelper application.
|
|
41
|
+
*
|
|
42
|
+
* When a child process handle is available, sends `SIGTERM` and waits
|
|
43
|
+
* for the process to exit. If it does not exit within
|
|
44
|
+
* {@link QUIT_GRACEFUL_TIMEOUT}, escalates to `SIGKILL`.
|
|
45
|
+
*
|
|
46
|
+
* When no child process handle is available (app was launched
|
|
47
|
+
* externally), attempts to close via CDP.
|
|
48
|
+
*/
|
|
49
|
+
quit(): Promise<void>;
|
|
50
|
+
/**
|
|
51
|
+
* Check whether LinkedHelper is running by probing its CDP endpoint.
|
|
52
|
+
*/
|
|
53
|
+
isRunning(): Promise<boolean>;
|
|
54
|
+
/**
|
|
55
|
+
* Locate the LinkedHelper binary for the current platform.
|
|
56
|
+
*
|
|
57
|
+
* @throws {AppNotFoundError} if the binary does not exist at the
|
|
58
|
+
* expected location.
|
|
59
|
+
*/
|
|
60
|
+
static findBinary(): string;
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=app.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../../src/services/app.ts"],"names":[],"mappings":"AAkBA,MAAM,WAAW,iBAAiB;IAChC,sFAAsF;IACtF,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED;;;;;;GAMG;AACH,qBAAa,UAAU;IACrB,OAAO,CAAC,YAAY,CAAgB;IACpC,OAAO,CAAC,YAAY,CAA6B;IACjD,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;IAE1C;;;;OAIG;gBACS,OAAO,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,iBAAiB;IAKzD;;;;;OAKG;IACH,IAAI,OAAO,IAAI,MAAM,CAKpB;IAED;;;;;;;;OAQG;IACG,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IA0C7B;;;;;;;;;OASG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAkC3B;;OAEG;IACG,SAAS,IAAI,OAAO,CAAC,OAAO,CAAC;IAYnC;;;;;OAKG;IACH,MAAM,CAAC,UAAU,IAAI,MAAM;CAW5B"}
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { accessSync, constants } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import getPort from "get-port";
|
|
5
|
+
import { discoverTargets } from "../cdp/index.js";
|
|
6
|
+
import { AppLaunchError, AppNotFoundError } from "./errors.js";
|
|
7
|
+
/** Default delay after spawn before checking if the app is reachable (ms). */
|
|
8
|
+
const DEFAULT_LAUNCH_PROBE_DELAY = 3000;
|
|
9
|
+
/** Maximum time to wait for the process to exit after SIGTERM (ms). */
|
|
10
|
+
const QUIT_GRACEFUL_TIMEOUT = 10_000;
|
|
11
|
+
/** Maximum time to wait for the process to exit after SIGKILL (ms). */
|
|
12
|
+
const QUIT_FORCE_TIMEOUT = 5_000;
|
|
13
|
+
/**
|
|
14
|
+
* Manages the LinkedHelper application process lifecycle.
|
|
15
|
+
*
|
|
16
|
+
* Provides methods to launch, quit, and probe the LinkedHelper
|
|
17
|
+
* Electron application. When no explicit CDP port is provided,
|
|
18
|
+
* a free port is selected automatically at launch time.
|
|
19
|
+
*/
|
|
20
|
+
export class AppService {
|
|
21
|
+
assignedPort;
|
|
22
|
+
childProcess = null;
|
|
23
|
+
launchProbeDelay;
|
|
24
|
+
/**
|
|
25
|
+
* @param cdpPort - Explicit CDP port. When omitted, `launch()` will
|
|
26
|
+
* select a free port automatically via `get-port`.
|
|
27
|
+
* @param options - Additional configuration options.
|
|
28
|
+
*/
|
|
29
|
+
constructor(cdpPort, options) {
|
|
30
|
+
this.assignedPort = cdpPort ?? null;
|
|
31
|
+
this.launchProbeDelay = options?.launchProbeDelay ?? DEFAULT_LAUNCH_PROBE_DELAY;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* The CDP port currently in use.
|
|
35
|
+
*
|
|
36
|
+
* @throws {Error} if neither an explicit port was provided nor
|
|
37
|
+
* `launch()` has been called yet.
|
|
38
|
+
*/
|
|
39
|
+
get cdpPort() {
|
|
40
|
+
if (this.assignedPort === null) {
|
|
41
|
+
throw new Error("CDP port not yet assigned — call launch() first or provide a port to the constructor");
|
|
42
|
+
}
|
|
43
|
+
return this.assignedPort;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Launch the LinkedHelper application with CDP enabled.
|
|
47
|
+
*
|
|
48
|
+
* If no CDP port was specified in the constructor, a free port
|
|
49
|
+
* is selected automatically.
|
|
50
|
+
*
|
|
51
|
+
* @throws {AppNotFoundError} if the binary cannot be found.
|
|
52
|
+
* @throws {AppLaunchError} if the process fails to start.
|
|
53
|
+
*/
|
|
54
|
+
async launch() {
|
|
55
|
+
if (this.assignedPort !== null && await this.isRunning()) {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
if (this.assignedPort === null) {
|
|
59
|
+
this.assignedPort = await getPort();
|
|
60
|
+
}
|
|
61
|
+
const binary = AppService.findBinary();
|
|
62
|
+
const args = [`--remote-debugging-port=${String(this.assignedPort)}`];
|
|
63
|
+
const child = spawn(binary, args, {
|
|
64
|
+
detached: true,
|
|
65
|
+
stdio: "ignore",
|
|
66
|
+
});
|
|
67
|
+
child.unref();
|
|
68
|
+
// Wait for an early error (e.g. ENOENT from spawn) before probing
|
|
69
|
+
await new Promise((resolve, reject) => {
|
|
70
|
+
const onError = (err) => {
|
|
71
|
+
cleanup();
|
|
72
|
+
reject(new AppLaunchError(`Failed to launch LinkedHelper: ${err.message}`, { cause: err }));
|
|
73
|
+
};
|
|
74
|
+
const timer = setTimeout(() => {
|
|
75
|
+
cleanup();
|
|
76
|
+
resolve();
|
|
77
|
+
}, this.launchProbeDelay);
|
|
78
|
+
function cleanup() {
|
|
79
|
+
child.removeListener("error", onError);
|
|
80
|
+
clearTimeout(timer);
|
|
81
|
+
}
|
|
82
|
+
child.on("error", onError);
|
|
83
|
+
});
|
|
84
|
+
this.childProcess = child;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Quit the LinkedHelper application.
|
|
88
|
+
*
|
|
89
|
+
* When a child process handle is available, sends `SIGTERM` and waits
|
|
90
|
+
* for the process to exit. If it does not exit within
|
|
91
|
+
* {@link QUIT_GRACEFUL_TIMEOUT}, escalates to `SIGKILL`.
|
|
92
|
+
*
|
|
93
|
+
* When no child process handle is available (app was launched
|
|
94
|
+
* externally), attempts to close via CDP.
|
|
95
|
+
*/
|
|
96
|
+
async quit() {
|
|
97
|
+
if (this.childProcess) {
|
|
98
|
+
const child = this.childProcess;
|
|
99
|
+
this.childProcess = null;
|
|
100
|
+
child.kill("SIGTERM");
|
|
101
|
+
const exited = await waitForExit(child, QUIT_GRACEFUL_TIMEOUT);
|
|
102
|
+
if (!exited) {
|
|
103
|
+
child.kill("SIGKILL");
|
|
104
|
+
await waitForExit(child, QUIT_FORCE_TIMEOUT);
|
|
105
|
+
}
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
if (this.assignedPort === null) {
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
// Fallback: close via CDP Browser.close
|
|
112
|
+
try {
|
|
113
|
+
const targets = await discoverTargets(this.assignedPort);
|
|
114
|
+
const first = targets[0];
|
|
115
|
+
if (first) {
|
|
116
|
+
await fetch(`http://127.0.0.1:${String(this.assignedPort)}/json/close/${first.id}`);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
catch {
|
|
120
|
+
// App may already be closed
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Check whether LinkedHelper is running by probing its CDP endpoint.
|
|
125
|
+
*/
|
|
126
|
+
async isRunning() {
|
|
127
|
+
if (this.assignedPort === null) {
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
130
|
+
try {
|
|
131
|
+
await discoverTargets(this.assignedPort);
|
|
132
|
+
return true;
|
|
133
|
+
}
|
|
134
|
+
catch {
|
|
135
|
+
return false;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Locate the LinkedHelper binary for the current platform.
|
|
140
|
+
*
|
|
141
|
+
* @throws {AppNotFoundError} if the binary does not exist at the
|
|
142
|
+
* expected location.
|
|
143
|
+
*/
|
|
144
|
+
static findBinary() {
|
|
145
|
+
const envPath = process.env["LINKEDHELPER_PATH"];
|
|
146
|
+
if (envPath) {
|
|
147
|
+
assertFileExists(envPath);
|
|
148
|
+
return envPath;
|
|
149
|
+
}
|
|
150
|
+
const path = getDefaultBinaryPath();
|
|
151
|
+
assertFileExists(path);
|
|
152
|
+
return path;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
function getDefaultBinaryPath() {
|
|
156
|
+
switch (process.platform) {
|
|
157
|
+
case "darwin":
|
|
158
|
+
return "/Applications/linked-helper.app/Contents/MacOS/linked-helper";
|
|
159
|
+
case "win32":
|
|
160
|
+
return join(process.env["LOCALAPPDATA"] ?? join(process.env["USERPROFILE"] ?? "C:\\Users\\Default", "AppData", "Local"), "Programs", "linked-helper", "linked-helper.exe");
|
|
161
|
+
default:
|
|
162
|
+
return "/opt/linked-helper/linked-helper";
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Wait for a child process to exit, with a timeout.
|
|
167
|
+
*
|
|
168
|
+
* @returns `true` if the process exited within the timeout, `false` otherwise.
|
|
169
|
+
*/
|
|
170
|
+
function waitForExit(child, timeout) {
|
|
171
|
+
if (child.exitCode !== null) {
|
|
172
|
+
return Promise.resolve(true);
|
|
173
|
+
}
|
|
174
|
+
return new Promise((resolve) => {
|
|
175
|
+
const timer = setTimeout(() => {
|
|
176
|
+
cleanup();
|
|
177
|
+
resolve(false);
|
|
178
|
+
}, timeout);
|
|
179
|
+
const onExit = () => {
|
|
180
|
+
cleanup();
|
|
181
|
+
resolve(true);
|
|
182
|
+
};
|
|
183
|
+
function cleanup() {
|
|
184
|
+
child.removeListener("exit", onExit);
|
|
185
|
+
clearTimeout(timer);
|
|
186
|
+
}
|
|
187
|
+
child.on("exit", onExit);
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
function assertFileExists(path) {
|
|
191
|
+
try {
|
|
192
|
+
accessSync(path, constants.X_OK);
|
|
193
|
+
}
|
|
194
|
+
catch {
|
|
195
|
+
throw new AppNotFoundError(`LinkedHelper binary not found at ${path}. Set LINKEDHELPER_PATH to override.`);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
//# sourceMappingURL=app.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app.js","sourceRoot":"","sources":["../../src/services/app.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app.test.d.ts","sourceRoot":"","sources":["../../src/services/app.test.ts"],"names":[],"mappings":""}
|