amaprice 1.0.14 → 1.0.15

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "amaprice",
3
- "version": "1.0.14",
3
+ "version": "1.0.15",
4
4
  "description": "CLI tool to scrape and track Amazon product prices",
5
5
  "main": "src/scraper.js",
6
6
  "type": "commonjs",
@@ -114,15 +114,17 @@ function isLaunchdSupported(platform = process.platform) {
114
114
  return platform === 'darwin';
115
115
  }
116
116
 
117
- function getLaunchdDomain() {
117
+ function getLaunchdDomains() {
118
118
  if (typeof process.getuid !== 'function') {
119
119
  throw new Error('launchd requires a POSIX uid');
120
120
  }
121
- return `gui/${process.getuid()}`;
121
+ const uid = process.getuid();
122
+ return [`gui/${uid}`, `user/${uid}`];
122
123
  }
123
124
 
124
- function buildServiceTarget(label) {
125
- return `${getLaunchdDomain()}/${label}`;
125
+ function buildServiceTarget(label, domain = null) {
126
+ const safeDomain = domain || getLaunchdDomains()[0];
127
+ return `${safeDomain}/${label}`;
126
128
  }
127
129
 
128
130
  async function runLaunchctl(args, { allowFailure = false } = {}) {
@@ -233,24 +235,38 @@ async function getLaunchdServiceStatus({ label }) {
233
235
  backend: 'launchd',
234
236
  label,
235
237
  plistPath,
238
+ domain: null,
236
239
  installed: false,
237
240
  loaded: false,
238
241
  running: false,
239
242
  };
240
243
  }
241
244
 
242
- const print = await runLaunchctl(['print', buildServiceTarget(label)], { allowFailure: true });
243
- const output = `${print.stdout}\n${print.stderr}`;
244
- const loaded = print.ok;
245
- const running = loaded && (/state = running/i.test(output) || /pid = \d+/i.test(output));
245
+ const domains = getLaunchdDomains();
246
+ for (const domain of domains) {
247
+ const print = await runLaunchctl(['print', buildServiceTarget(label, domain)], { allowFailure: true });
248
+ if (!print.ok) continue;
249
+ const output = `${print.stdout}\n${print.stderr}`;
250
+ const running = /state = running/i.test(output) || /pid = \d+/i.test(output);
251
+ return {
252
+ backend: 'launchd',
253
+ label,
254
+ plistPath,
255
+ domain,
256
+ installed: true,
257
+ loaded: true,
258
+ running,
259
+ };
260
+ }
246
261
 
247
262
  return {
248
263
  backend: 'launchd',
249
264
  label,
250
265
  plistPath,
266
+ domain: domains[0] || null,
251
267
  installed: true,
252
- loaded,
253
- running,
268
+ loaded: false,
269
+ running: false,
254
270
  };
255
271
  }
256
272
 
@@ -263,7 +279,6 @@ async function enableLaunchdService({
263
279
  const plistPath = getLaunchdPlistPath(label);
264
280
  const logPath = getDaemonLogPath();
265
281
  const daemonEntry = getDaemonEntryPath();
266
- const target = buildServiceTarget(label);
267
282
 
268
283
  await fs.mkdir(path.dirname(plistPath), { recursive: true });
269
284
  await fs.mkdir(path.dirname(logPath), { recursive: true });
@@ -284,33 +299,53 @@ async function enableLaunchdService({
284
299
  });
285
300
  await fs.writeFile(plistPath, plist, 'utf8');
286
301
 
287
- // If service was previously disabled via `background off`, re-enable first.
288
- await runLaunchctl(['enable', target], { allowFailure: true });
302
+ const domains = getLaunchdDomains();
303
+ const errors = [];
289
304
 
290
- let bootstrap = await runLaunchctl(['bootstrap', getLaunchdDomain(), plistPath], { allowFailure: true });
291
- if (!bootstrap.ok && !isAlreadyLoadedError(bootstrap)) {
292
- // Recover from stale loaded/disabled state by clearing then bootstrapping once more.
293
- await runLaunchctl(['bootout', target], { allowFailure: true });
294
- await runLaunchctl(['enable', target], { allowFailure: true });
295
- bootstrap = await runLaunchctl(['bootstrap', getLaunchdDomain(), plistPath], { allowFailure: true });
296
- }
297
- if (!bootstrap.ok && !isAlreadyLoadedError(bootstrap)) {
298
- throw new Error(`Could not bootstrap launchd service: ${bootstrap.stderr || bootstrap.stdout || 'unknown error'}`);
299
- }
305
+ for (const domain of domains) {
306
+ const domainTarget = buildServiceTarget(label, domain);
307
+
308
+ // Clean stale state first, then bootstrap fresh.
309
+ await runLaunchctl(['bootout', domainTarget], { allowFailure: true });
310
+ await runLaunchctl(['disable', domainTarget], { allowFailure: true });
311
+ await runLaunchctl(['enable', domainTarget], { allowFailure: true });
300
312
 
301
- await runLaunchctl(['enable', target], { allowFailure: true });
302
- const kick = await runLaunchctl(['kickstart', '-k', target], { allowFailure: true });
303
- if (!kick.ok) {
304
- await runLaunchctl(['start', label], { allowFailure: true });
313
+ let bootstrap = await runLaunchctl(['bootstrap', domain, plistPath], { allowFailure: true });
314
+ if (!bootstrap.ok && !isAlreadyLoadedError(bootstrap)) {
315
+ await runLaunchctl(['bootout', domainTarget], { allowFailure: true });
316
+ await runLaunchctl(['enable', domainTarget], { allowFailure: true });
317
+ bootstrap = await runLaunchctl(['bootstrap', domain, plistPath], { allowFailure: true });
318
+ }
319
+
320
+ if (!bootstrap.ok && !isAlreadyLoadedError(bootstrap)) {
321
+ errors.push(`${domain}: ${bootstrap.stderr || bootstrap.stdout || 'unknown error'}`);
322
+ continue;
323
+ }
324
+
325
+ await runLaunchctl(['enable', domainTarget], { allowFailure: true });
326
+ const kick = await runLaunchctl(['kickstart', '-k', domainTarget], { allowFailure: true });
327
+ if (!kick.ok) {
328
+ await runLaunchctl(['start', label], { allowFailure: true });
329
+ }
330
+
331
+ const status = await getLaunchdServiceStatus({ label });
332
+ if (status.loaded) {
333
+ return status;
334
+ }
335
+ errors.push(`${domain}: bootstrapped but service not loaded`);
305
336
  }
306
337
 
307
- return getLaunchdServiceStatus({ label });
338
+ const msg = errors.length > 0 ? errors.join(' | ') : 'unknown error';
339
+ throw new Error(`Could not bootstrap launchd service: ${msg}`);
308
340
  }
309
341
 
310
342
  async function disableLaunchdService({ label }) {
311
343
  const plistPath = getLaunchdPlistPath(label);
312
- await runLaunchctl(['bootout', buildServiceTarget(label)], { allowFailure: true });
313
- await runLaunchctl(['disable', buildServiceTarget(label)], { allowFailure: true });
344
+ for (const domain of getLaunchdDomains()) {
345
+ const target = buildServiceTarget(label, domain);
346
+ await runLaunchctl(['bootout', target], { allowFailure: true });
347
+ await runLaunchctl(['disable', target], { allowFailure: true });
348
+ }
314
349
  try {
315
350
  await fs.unlink(plistPath);
316
351
  } catch (err) {
@@ -342,7 +377,7 @@ async function ensureBackgroundOn({
342
377
  const { collector, statePath } = await ensureCollectorEnabled({
343
378
  userId,
344
379
  collectorName,
345
- status: 'active',
380
+ status: 'paused',
346
381
  });
347
382
 
348
383
  const service = await enableLaunchdService({
@@ -352,6 +387,10 @@ async function ensureBackgroundOn({
352
387
  userId,
353
388
  });
354
389
 
390
+ if (!service.loaded) {
391
+ throw new Error('launchd service did not load');
392
+ }
393
+
355
394
  await heartbeatCollector({
356
395
  collectorId: collector.id,
357
396
  status: 'active',
@@ -510,6 +549,8 @@ module.exports.__test = {
510
549
  resolvePollSeconds,
511
550
  getLaunchdLabel,
512
551
  getLaunchdPlistPath,
552
+ getLaunchdDomains,
553
+ buildServiceTarget,
513
554
  renderLaunchdPlist,
514
555
  isLaunchdSupported,
515
556
  };