@hamedb89/localghost 0.1.3 → 0.1.8

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/dist/index.js CHANGED
@@ -1,6 +1,100 @@
1
+ // src/activity.ts
2
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
3
+ import { homedir } from "os";
4
+ import { dirname, join } from "path";
5
+ var LOCALGHOST_ACTIVITY_VERSION = 1;
6
+ function getLocalghostActivityPath(env = process.env) {
7
+ if (env.LOCALGHOST_ACTIVITY_PATH) return env.LOCALGHOST_ACTIVITY_PATH;
8
+ const stateRoot = env.XDG_STATE_HOME || join(homedir(), ".local/state");
9
+ return join(stateRoot, "localghost", "activity.json");
10
+ }
11
+ function isProcessRunning(pid) {
12
+ if (!Number.isInteger(pid) || pid < 1) return false;
13
+ try {
14
+ process.kill(pid, 0);
15
+ return true;
16
+ } catch (error) {
17
+ const code = typeof error === "object" && error !== null && "code" in error ? error.code : void 0;
18
+ return code === "EPERM";
19
+ }
20
+ }
21
+ function emptyActivity() {
22
+ return { version: LOCALGHOST_ACTIVITY_VERSION, runs: [] };
23
+ }
24
+ function readLocalghostActivity(path = getLocalghostActivityPath()) {
25
+ if (!existsSync(path)) return emptyActivity();
26
+ try {
27
+ const parsed = JSON.parse(readFileSync(path, "utf8"));
28
+ return {
29
+ version: LOCALGHOST_ACTIVITY_VERSION,
30
+ runs: Array.isArray(parsed.runs) ? parsed.runs : []
31
+ };
32
+ } catch {
33
+ return emptyActivity();
34
+ }
35
+ }
36
+ function writeLocalghostActivity(activity, path = getLocalghostActivityPath()) {
37
+ mkdirSync(dirname(path), { recursive: true });
38
+ writeFileSync(path, `${JSON.stringify(activity, null, 2)}
39
+ `, "utf8");
40
+ return path;
41
+ }
42
+ function createRunId(input, pid) {
43
+ return `${input.projectName}:${input.mode}:${pid}:${Date.now()}`;
44
+ }
45
+ function pruneLocalghostActivity(path = getLocalghostActivityPath()) {
46
+ const activity = readLocalghostActivity(path);
47
+ const activeRuns = activity.runs.filter((run) => isProcessRunning(run.pid));
48
+ const pruned = activeRuns.length !== activity.runs.length;
49
+ if (pruned) {
50
+ writeLocalghostActivity({ version: LOCALGHOST_ACTIVITY_VERSION, runs: activeRuns }, path);
51
+ }
52
+ return {
53
+ path,
54
+ pruned,
55
+ runs: activeRuns
56
+ };
57
+ }
58
+ function listLocalghostRuns(path = getLocalghostActivityPath()) {
59
+ return pruneLocalghostActivity(path).runs;
60
+ }
61
+ function registerLocalghostRun(input, path = getLocalghostActivityPath()) {
62
+ const now = (/* @__PURE__ */ new Date()).toISOString();
63
+ const pid = input.pid ?? process.pid;
64
+ const record = {
65
+ id: input.id ?? createRunId(input, pid),
66
+ mode: input.mode,
67
+ pid,
68
+ cwd: input.cwd,
69
+ projectName: input.projectName,
70
+ startedAt: input.startedAt ?? now,
71
+ updatedAt: now,
72
+ ...input.configPath ? { configPath: input.configPath } : {},
73
+ ...input.caddyfilePath ? { caddyfilePath: input.caddyfilePath } : {},
74
+ ...input.caddyPid ? { caddyPid: input.caddyPid } : {},
75
+ ...input.childPid ? { childPid: input.childPid } : {},
76
+ ...input.childCommand ? { childCommand: input.childCommand } : {},
77
+ ...typeof input.https === "boolean" ? { https: input.https } : {},
78
+ ...input.requestedPort ? { requestedPort: input.requestedPort } : {},
79
+ ...input.port ? { port: input.port } : {},
80
+ ...typeof input.dynamicPort === "boolean" ? { dynamicPort: input.dynamicPort } : {},
81
+ entries: input.entries
82
+ };
83
+ const current = pruneLocalghostActivity(path).runs.filter((run) => run.id !== record.id);
84
+ writeLocalghostActivity({ version: LOCALGHOST_ACTIVITY_VERSION, runs: [...current, record] }, path);
85
+ return record;
86
+ }
87
+ function unregisterLocalghostRun(id, path = getLocalghostActivityPath()) {
88
+ const activity = readLocalghostActivity(path);
89
+ const runs = activity.runs.filter((run) => run.id !== id);
90
+ if (runs.length !== activity.runs.length) {
91
+ writeLocalghostActivity({ version: LOCALGHOST_ACTIVITY_VERSION, runs }, path);
92
+ }
93
+ }
94
+
1
95
  // src/config.ts
2
- import { existsSync, readFileSync, readdirSync } from "fs";
3
- import { basename, join, resolve } from "path";
96
+ import { existsSync as existsSync2, readFileSync as readFileSync2, readdirSync } from "fs";
97
+ import { basename, join as join2, resolve } from "path";
4
98
 
5
99
  // src/parse.ts
6
100
  var HOST_PATTERN = /^(?=.{1,253}$)(?!-)[a-z0-9-]+(\.[a-z0-9-]+)*\.?$/i;
@@ -68,7 +162,7 @@ function resolveDevHostsPath(options = {}) {
68
162
  const searchedFiles = getConfigFileCandidates(options);
69
163
  for (const fileName2 of searchedFiles) {
70
164
  const path = resolve(cwd, fileName2);
71
- if (existsSync(path)) {
165
+ if (existsSync2(path)) {
72
166
  return {
73
167
  path,
74
168
  fileName: basename(fileName2),
@@ -104,11 +198,11 @@ function readDevHosts(options = {}) {
104
198
  `Missing Localghost config in ${cwd}. Looked for ${formatSearchedFiles(resolvedPath.searchedFiles, resolvedPath.configPattern)}. Run \`localghost init\` or pass --config/--config-pattern.`
105
199
  );
106
200
  }
107
- return parseDevHosts(readFileSync(resolvedPath.path, "utf8"), resolvedPath.fileName);
201
+ return parseDevHosts(readFileSync2(resolvedPath.path, "utf8"), resolvedPath.fileName);
108
202
  }
109
203
  function getProjectName(cwd = process.cwd()) {
110
204
  try {
111
- const pkg = JSON.parse(readFileSync(join(cwd, "package.json"), "utf8"));
205
+ const pkg = JSON.parse(readFileSync2(join2(cwd, "package.json"), "utf8"));
112
206
  const name = typeof pkg.name === "string" && pkg.name ? pkg.name : "app";
113
207
  return sanitizeProjectName(name.replace(/^@/, ""));
114
208
  } catch {
@@ -121,18 +215,18 @@ function sanitizeProjectName(value) {
121
215
  }
122
216
 
123
217
  // src/caddy.ts
124
- import { dirname as dirname2, join as join2 } from "path";
218
+ import { dirname as dirname3, join as join3 } from "path";
125
219
  import { execa } from "execa";
126
220
 
127
221
  // src/fs.ts
128
- import { mkdirSync, readFileSync as readFileSync2, writeFileSync } from "fs";
129
- import { dirname } from "path";
222
+ import { mkdirSync as mkdirSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
223
+ import { dirname as dirname2 } from "path";
130
224
  function readTextFile(path) {
131
- return readFileSync2(path, "utf8");
225
+ return readFileSync3(path, "utf8");
132
226
  }
133
227
  function writeTextFile(path, value) {
134
- mkdirSync(dirname(path), { recursive: true });
135
- writeFileSync(path, value, "utf8");
228
+ mkdirSync2(dirname2(path), { recursive: true });
229
+ writeFileSync2(path, value, "utf8");
136
230
  return path;
137
231
  }
138
232
 
@@ -147,40 +241,197 @@ function groupByPort(entries) {
147
241
  return groups;
148
242
  }
149
243
  function getCaddyfilePath(cwd = process.cwd()) {
150
- return join2(cwd, "ops/local/Caddyfile");
244
+ return join3(cwd, "ops/local/Caddyfile");
151
245
  }
152
- function renderCaddyfile(entries) {
246
+ function renderCaddyfile(entries, options = {}) {
153
247
  const groups = groupByPort(entries);
248
+ const https = options.https === true;
154
249
  const blocks = [...groups.entries()].sort(([leftPort], [rightPort]) => leftPort - rightPort).map(([port, group]) => {
155
- const hosts = group.map((entry) => entry.host).sort().join(", ");
250
+ const hosts = group.map((entry) => https ? entry.host : `http://${entry.host}`).sort().join(", ");
156
251
  return `${hosts} {
157
252
  reverse_proxy 127.0.0.1:${port}
158
253
  }`;
159
254
  });
160
- return `{
255
+ const globalOptions = https ? `{
161
256
  local_certs
162
257
  }
163
258
 
164
- ${blocks.join("\n\n")}
259
+ ` : "";
260
+ return `${globalOptions}${blocks.join("\n\n")}
165
261
  `;
166
262
  }
167
- async function writeCaddyfile(entries, cwd = process.cwd()) {
263
+ async function writeCaddyfile(entries, cwd = process.cwd(), options = {}) {
168
264
  const path = getCaddyfilePath(cwd);
169
- writeTextFile(path, renderCaddyfile(entries));
265
+ writeTextFile(path, renderCaddyfile(entries, options));
170
266
  return path;
171
267
  }
172
268
  async function validateCaddyfile(path) {
173
269
  await execa("caddy", ["validate", "--config", path], {
174
- cwd: dirname2(path),
270
+ cwd: dirname3(path),
175
271
  stdio: "inherit"
176
272
  });
177
273
  }
178
274
  async function runCaddy(path) {
179
275
  await execa("caddy", ["run", "--config", path], {
180
- cwd: dirname2(path),
276
+ cwd: dirname3(path),
181
277
  stdio: "inherit"
182
278
  });
183
279
  }
280
+ function startCaddy(path) {
281
+ return execa("caddy", ["run", "--config", path], {
282
+ cwd: dirname3(path),
283
+ stdio: "inherit"
284
+ });
285
+ }
286
+ async function trustCaddy(path) {
287
+ await execa("caddy", ["trust", "--config", path], {
288
+ cwd: dirname3(path),
289
+ stdio: "inherit"
290
+ });
291
+ }
292
+
293
+ // src/context.ts
294
+ import { existsSync as existsSync3 } from "fs";
295
+ import { pathToFileURL } from "url";
296
+
297
+ // src/port.ts
298
+ import { createServer } from "net";
299
+ async function isPortAvailable(port, host = "127.0.0.1") {
300
+ return new Promise((resolve2) => {
301
+ const server = createServer();
302
+ server.once("error", () => {
303
+ resolve2(false);
304
+ });
305
+ server.once("listening", () => {
306
+ server.close(() => resolve2(true));
307
+ });
308
+ server.listen(port, host);
309
+ });
310
+ }
311
+ async function findAvailablePort(startPort, options = {}) {
312
+ const host = options.host ?? "127.0.0.1";
313
+ const maxAttempts = options.maxAttempts ?? 50;
314
+ for (let offset = 0; offset < maxAttempts; offset += 1) {
315
+ const port = startPort + offset;
316
+ if (await isPortAvailable(port, host)) {
317
+ return port;
318
+ }
319
+ }
320
+ throw new Error(`No available port found from ${startPort} to ${startPort + maxAttempts - 1}.`);
321
+ }
322
+
323
+ // src/context.ts
324
+ var LOCALGHOST_PROJECT_CONFIG_FILES = [
325
+ "localghost.config.mjs",
326
+ "localghost.config.js",
327
+ "localghost.config.cjs"
328
+ ];
329
+ function parsePort(value) {
330
+ if (!value) return void 0;
331
+ const port = Number.parseInt(value, 10);
332
+ return Number.isInteger(port) && port > 0 && port <= 65535 ? port : void 0;
333
+ }
334
+ function envPort() {
335
+ return parsePort(process.env.LOCALGHOST_PORT) ?? parsePort(process.env.VITE_PORT);
336
+ }
337
+ function envDynamicPort() {
338
+ const value = process.env.LOCALGHOST_DYNAMIC_PORT;
339
+ if (!value) return void 0;
340
+ return ["1", "true", "yes", "on"].includes(value.toLowerCase());
341
+ }
342
+ function envHttps() {
343
+ const value = process.env.LOCALGHOST_HTTPS;
344
+ if (!value) return void 0;
345
+ return ["1", "true", "yes", "on"].includes(value.toLowerCase());
346
+ }
347
+ function readOptionsFromContext(options) {
348
+ return {
349
+ cwd: options.cwd ?? process.cwd(),
350
+ ...options.fileName ? { fileName: options.fileName } : {},
351
+ ...options.configFiles ? { configFiles: options.configFiles } : {},
352
+ ...options.configPattern ? { configPattern: options.configPattern } : {}
353
+ };
354
+ }
355
+ function withRuntimePort(entries, requestedPort, port) {
356
+ if (requestedPort === port) return entries;
357
+ const hasRequestedPort = entries.some((entry) => entry.port === requestedPort);
358
+ if (!hasRequestedPort) return entries;
359
+ return entries.map((entry) => entry.port === requestedPort ? { ...entry, port } : entry);
360
+ }
361
+ function uniqueHosts(entries) {
362
+ return [...new Set(entries.map((entry) => entry.host))];
363
+ }
364
+ function isAliasableHost(host) {
365
+ return host.includes(".") && !host.startsWith("www.") && !host.includes(":");
366
+ }
367
+ function getDefaultWwwAlias(host) {
368
+ return isAliasableHost(host) ? `www.${host}` : null;
369
+ }
370
+ function addDefaultWwwAliases(entries) {
371
+ const seen = new Set(entries.map((entry) => entry.host));
372
+ const aliases = [];
373
+ for (const entry of entries) {
374
+ const alias = getDefaultWwwAlias(entry.host);
375
+ if (alias && !seen.has(alias)) {
376
+ aliases.push({ host: alias, port: entry.port, target: `127.0.0.1:${entry.port}` });
377
+ seen.add(alias);
378
+ }
379
+ }
380
+ return [...entries, ...aliases];
381
+ }
382
+ function defined(input) {
383
+ return Object.fromEntries(Object.entries(input).filter(([, value]) => typeof value !== "undefined"));
384
+ }
385
+ async function readProjectConfig(cwd, configFile) {
386
+ if (configFile === false) return {};
387
+ const candidates = configFile ? [configFile] : LOCALGHOST_PROJECT_CONFIG_FILES;
388
+ const path = candidates.map((candidate) => resolveDevHostsPath({ cwd, fileName: candidate }).path).find((candidate) => existsSync3(candidate));
389
+ if (!path) return {};
390
+ const imported = await import(`${pathToFileURL(path).href}?localghost=${Date.now()}`);
391
+ const config = imported.default ?? imported;
392
+ return { config, path };
393
+ }
394
+ function defineLocalghostConfig(config) {
395
+ return config;
396
+ }
397
+ async function resolveLocalghostContext(options = {}) {
398
+ const cwd = options.cwd ?? process.cwd();
399
+ const projectConfig = await readProjectConfig(cwd, options.localghostConfig);
400
+ const merged = {
401
+ ...projectConfig.config,
402
+ ...defined(options)
403
+ };
404
+ const readOptions = readOptionsFromContext({ ...merged, cwd });
405
+ const resolvedPath = resolveDevHostsPath(readOptions);
406
+ const configEntries = readDevHosts(readOptions);
407
+ const requestedPort = merged.port ?? envPort() ?? configEntries[0]?.port ?? 5173;
408
+ const dynamicPort = merged.dynamicPort ?? envDynamicPort() ?? false;
409
+ const bindHost = merged.bindHost ?? "127.0.0.1";
410
+ const probeHost = typeof bindHost === "string" ? bindHost : "127.0.0.1";
411
+ const port = dynamicPort ? await findAvailablePort(requestedPort, { host: probeHost }) : requestedPort;
412
+ const wwwAlias = merged.wwwAlias ?? true;
413
+ const entries = wwwAlias ? addDefaultWwwAliases(withRuntimePort(configEntries, requestedPort, port)) : withRuntimePort(configEntries, requestedPort, port);
414
+ const hosts = uniqueHosts(entries);
415
+ const primaryHost = merged.primaryHost ?? entries.find((entry) => entry.port === port)?.host ?? hosts[0] ?? `${sanitizeProjectName(getProjectName(cwd))}.localhost`;
416
+ return {
417
+ cwd,
418
+ projectName: sanitizeProjectName(merged.project ?? getProjectName(cwd)),
419
+ readOptions,
420
+ configPath: resolvedPath.path,
421
+ configFileName: resolvedPath.fileName,
422
+ configEntries,
423
+ entries,
424
+ hosts,
425
+ requestedPort,
426
+ port,
427
+ dynamicPort,
428
+ bindHost,
429
+ primaryHost,
430
+ https: merged.https ?? envHttps() ?? false,
431
+ wwwAlias,
432
+ ...projectConfig.path ? { projectConfigPath: projectConfig.path } : {}
433
+ };
434
+ }
184
435
 
185
436
  // src/doctor.ts
186
437
  import { execa as execa2 } from "execa";
@@ -208,10 +459,34 @@ async function runDoctor() {
208
459
  };
209
460
  }
210
461
 
462
+ // src/env.ts
463
+ var PRODUCTION_ENV_KEYS = ["NODE_ENV", "VERCEL_ENV", "NETLIFY", "CF_PAGES_BRANCH", "LOCALGHOST_ENV"];
464
+ function getProductionReason(env = process.env) {
465
+ if (env.LOCALGHOST_ENV === "production") return "LOCALGHOST_ENV=production";
466
+ if (env.NODE_ENV === "production") return "NODE_ENV=production";
467
+ if (env.VERCEL_ENV === "production") return "VERCEL_ENV=production";
468
+ if (env.NETLIFY === "true" && env.CONTEXT === "production") return "NETLIFY=true and CONTEXT=production";
469
+ if (env.CF_PAGES_BRANCH && env.CF_PAGES_BRANCH === env.CF_PAGES_PRODUCTION_BRANCH) {
470
+ return "CF_PAGES_BRANCH matches CF_PAGES_PRODUCTION_BRANCH";
471
+ }
472
+ return null;
473
+ }
474
+ function isProductionLike(env = process.env) {
475
+ return getProductionReason(env) !== null;
476
+ }
477
+ function assertLocalDevelopment(command, env = process.env) {
478
+ const reason = getProductionReason(env);
479
+ if (!reason) return;
480
+ throw new Error(`Localghost only runs in local development. Refusing \`${command}\` because ${reason}.`);
481
+ }
482
+ function getProductionEnvKeys() {
483
+ return PRODUCTION_ENV_KEYS;
484
+ }
485
+
211
486
  // src/hosts-file.ts
212
- import { writeFileSync as writeFileSync2 } from "fs";
487
+ import { writeFileSync as writeFileSync3 } from "fs";
213
488
  import { tmpdir } from "os";
214
- import { join as join3 } from "path";
489
+ import { join as join4 } from "path";
215
490
  import { execa as execa3 } from "execa";
216
491
  function escapeRegExp(value) {
217
492
  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
@@ -253,8 +528,8 @@ function removeManagedBlock(existing, projectName) {
253
528
  }
254
529
  async function writeSystemHostsFile(hostsPath, next, projectName) {
255
530
  const sanitizedProjectName = sanitizeProjectName(projectName);
256
- const tempPath = join3(tmpdir(), `localghost-${sanitizedProjectName}-hosts`);
257
- writeFileSync2(tempPath, next, "utf8");
531
+ const tempPath = join4(tmpdir(), `localghost-${sanitizedProjectName}-hosts`);
532
+ writeFileSync3(tempPath, next, "utf8");
258
533
  if (process.platform === "win32") {
259
534
  throw new Error(`Windows support: run as administrator and copy ${tempPath} to ${hostsPath}.`);
260
535
  }
@@ -286,11 +561,11 @@ async function removeSystemHosts(projectName) {
286
561
  }
287
562
 
288
563
  // src/init.ts
289
- import { existsSync as existsSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
290
- import { join as join4 } from "path";
564
+ import { existsSync as existsSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync4 } from "fs";
565
+ import { join as join5 } from "path";
291
566
  function detectPackageManager(cwd = process.cwd()) {
292
- if (existsSync2(join4(cwd, "pnpm-lock.yaml"))) return "pnpm";
293
- if (existsSync2(join4(cwd, "yarn.lock"))) return "yarn";
567
+ if (existsSync4(join5(cwd, "pnpm-lock.yaml"))) return "pnpm";
568
+ if (existsSync4(join5(cwd, "yarn.lock"))) return "yarn";
294
569
  return "npm";
295
570
  }
296
571
  function packageRunCommand(packageManager, script) {
@@ -315,7 +590,7 @@ function renderConfig(options) {
315
590
  }
316
591
  function readPackageJson(path) {
317
592
  try {
318
- return JSON.parse(readFileSync3(path, "utf8"));
593
+ return JSON.parse(readFileSync4(path, "utf8"));
319
594
  } catch {
320
595
  return null;
321
596
  }
@@ -336,17 +611,25 @@ function updatePackageScripts(packageJsonPath, configFile) {
336
611
  ...scripts,
337
612
  "localghost:setup": scripts["localghost:setup"] ?? `localghost setup${configFlag}`,
338
613
  "localghost:proxy": scripts["localghost:proxy"] ?? `localghost dev${configFlag}`,
614
+ "localghost:proxy:https": scripts["localghost:proxy:https"] ?? `localghost dev${configFlag} --https`,
615
+ "localghost:run": scripts["localghost:run"] ?? `localghost run${configFlag} --`,
616
+ "localghost:ready": scripts["localghost:ready"] ?? `localghost status${configFlag} --ready`,
617
+ "localghost:trust": scripts["localghost:trust"] ?? `localghost trust${configFlag}`,
618
+ "localghost:ps": scripts["localghost:ps"] ?? "localghost ps",
339
619
  "localghost:print": scripts["localghost:print"] ?? `localghost print${configFlag}`,
340
620
  "localghost:routes": scripts["localghost:routes"] ?? `localghost routes${configFlag}`,
341
621
  "localghost:status": scripts["localghost:status"] ?? "localghost status",
622
+ "localghost:reset": scripts["localghost:reset"] ?? "localghost reset",
342
623
  "localghost:teardown": scripts["localghost:teardown"] ?? "localghost teardown",
343
624
  "localghost:doctor": scripts["localghost:doctor"] ?? "localghost doctor",
344
- "localghost:update": scripts["localghost:update"] ?? "localghost update"
625
+ "localghost:update": scripts["localghost:update"] ?? "localghost update",
626
+ "caddy:setup": scripts["caddy:setup"] ?? `localghost setup${configFlag}`,
627
+ "caddy:dev": scripts["caddy:dev"] ?? `localghost dev${configFlag}`
345
628
  };
346
629
  const changed = JSON.stringify(scripts) !== JSON.stringify(nextScripts);
347
630
  if (!changed) return false;
348
631
  pkg.scripts = nextScripts;
349
- writeFileSync3(packageJsonPath, `${JSON.stringify(pkg, null, 2)}
632
+ writeFileSync4(packageJsonPath, `${JSON.stringify(pkg, null, 2)}
350
633
  `, "utf8");
351
634
  return true;
352
635
  }
@@ -359,8 +642,8 @@ function initLocalghost(options = {}) {
359
642
  const apiPort = options.apiPort ?? 8787;
360
643
  const packageManager = options.packageManager ?? detectPackageManager(cwd);
361
644
  const configFile = options.configFile ?? LOCALGHOST_CONFIG_FILE;
362
- const configPath = join4(cwd, configFile);
363
- const configExists = existsSync2(configPath);
645
+ const configPath = join5(cwd, configFile);
646
+ const configExists = existsSync4(configPath);
364
647
  if (configExists && !options.force) {
365
648
  return {
366
649
  configPath,
@@ -370,22 +653,24 @@ function initLocalghost(options = {}) {
370
653
  nextSteps: [
371
654
  packageRunCommand(packageManager, "localghost:doctor"),
372
655
  packageRunCommand(packageManager, "localghost:setup"),
656
+ packageRunCommand(packageManager, "localghost:ready"),
373
657
  packageRunCommand(packageManager, "localghost:proxy")
374
658
  ]
375
659
  };
376
660
  }
377
661
  writeTextFile(configPath, renderConfig({ host, port, apiHost, apiPort }));
378
- const packageJsonPath = join4(cwd, "package.json");
662
+ const packageJsonPath = join5(cwd, "package.json");
379
663
  const packageJsonChanged = options.writeScripts ? updatePackageScripts(packageJsonPath, configFile) : false;
380
664
  return {
381
665
  configPath,
382
666
  configCreated: true,
383
- ...existsSync2(packageJsonPath) ? { packageJsonPath } : {},
667
+ ...existsSync4(packageJsonPath) ? { packageJsonPath } : {},
384
668
  packageJsonChanged,
385
669
  packageManager,
386
670
  nextSteps: [
387
671
  packageRunCommand(packageManager, "localghost:doctor"),
388
672
  packageRunCommand(packageManager, "localghost:setup"),
673
+ packageRunCommand(packageManager, "localghost:ready"),
389
674
  packageRunCommand(packageManager, "localghost:proxy")
390
675
  ]
391
676
  };
@@ -393,7 +678,7 @@ function initLocalghost(options = {}) {
393
678
 
394
679
  // src/routes.ts
395
680
  function getDomainRoutes(entries, options = {}) {
396
- const protocol = options.https === false ? "http" : "https";
681
+ const protocol = options.https === true ? "https" : "http";
397
682
  return [...entries].sort((left, right) => left.host.localeCompare(right.host) || left.port - right.port).map((entry) => ({
398
683
  host: entry.host,
399
684
  port: entry.port,
@@ -413,30 +698,35 @@ function formatDomainRoutes(entries, options = {}) {
413
698
  }
414
699
 
415
700
  // src/state.ts
416
- import { existsSync as existsSync3 } from "fs";
417
- import { join as join5 } from "path";
701
+ import { existsSync as existsSync5 } from "fs";
702
+ import { join as join6 } from "path";
418
703
  var LOCALGHOST_STATE_FILE = "ops/local/localghost-state.json";
419
704
  function getLocalghostStatePath(cwd = process.cwd()) {
420
- return join5(cwd, LOCALGHOST_STATE_FILE);
705
+ return join6(cwd, LOCALGHOST_STATE_FILE);
421
706
  }
422
707
  function readLocalghostState(cwd = process.cwd()) {
423
708
  const path = getLocalghostStatePath(cwd);
424
- if (!existsSync3(path)) return null;
709
+ if (!existsSync5(path)) return null;
425
710
  return JSON.parse(readTextFile(path));
426
711
  }
427
712
  function writeLocalghostState(cwd, state) {
428
713
  const path = getLocalghostStatePath(cwd);
429
- writeTextFile(path, `${JSON.stringify({ version: 1, updatedAt: (/* @__PURE__ */ new Date()).toISOString(), ...state }, null, 2)}
714
+ writeTextFile(path, `${JSON.stringify({ ...state, version: 1, updatedAt: (/* @__PURE__ */ new Date()).toISOString() }, null, 2)}
430
715
  `);
431
716
  return path;
432
717
  }
718
+ function patchLocalghostState(cwd, patch) {
719
+ const current = readLocalghostState(cwd);
720
+ if (!current) return null;
721
+ return writeLocalghostState(cwd, { ...current, ...patch });
722
+ }
433
723
 
434
724
  // src/update-check.ts
435
- import { existsSync as existsSync4, mkdirSync as mkdirSync2, readFileSync as readFileSync4, writeFileSync as writeFileSync4 } from "fs";
436
- import { homedir } from "os";
437
- import { dirname as dirname3, join as join6 } from "path";
725
+ import { existsSync as existsSync6, mkdirSync as mkdirSync3, readFileSync as readFileSync5, writeFileSync as writeFileSync5 } from "fs";
726
+ import { homedir as homedir2 } from "os";
727
+ import { dirname as dirname4, join as join7 } from "path";
438
728
  var LOCALGHOST_PACKAGE_NAME = "@hamedb89/localghost";
439
- var LOCALGHOST_VERSION = "0.1.0";
729
+ var LOCALGHOST_VERSION = "0.1.8";
440
730
  var UPDATE_CHECK_CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
441
731
  var UPDATE_CHECK_NOTIFY_TTL_MS = 24 * 60 * 60 * 1e3;
442
732
  var UPDATE_CHECK_TIMEOUT_MS = 900;
@@ -448,21 +738,21 @@ function isUpdateCheckDisabled(env = process.env) {
448
738
  }
449
739
  function getUpdateCheckCachePath(env = process.env) {
450
740
  if (env.LOCALGHOST_UPDATE_CHECK_CACHE) return env.LOCALGHOST_UPDATE_CHECK_CACHE;
451
- const cacheRoot = env.XDG_CACHE_HOME || join6(homedir(), ".cache");
452
- return join6(cacheRoot, "localghost", "update-check.json");
741
+ const cacheRoot = env.XDG_CACHE_HOME || join7(homedir2(), ".cache");
742
+ return join7(cacheRoot, "localghost", "update-check.json");
453
743
  }
454
744
  function readCache(path = getUpdateCheckCachePath()) {
455
- if (!existsSync4(path)) return null;
745
+ if (!existsSync6(path)) return null;
456
746
  try {
457
- return JSON.parse(readFileSync4(path, "utf8"));
747
+ return JSON.parse(readFileSync5(path, "utf8"));
458
748
  } catch {
459
749
  return null;
460
750
  }
461
751
  }
462
752
  function writeCache(cache, path = getUpdateCheckCachePath()) {
463
753
  try {
464
- mkdirSync2(dirname3(path), { recursive: true });
465
- writeFileSync4(path, `${JSON.stringify(cache, null, 2)}
754
+ mkdirSync3(dirname4(path), { recursive: true });
755
+ writeFileSync5(path, `${JSON.stringify(cache, null, 2)}
466
756
  `, "utf8");
467
757
  } catch {
468
758
  }
@@ -598,6 +888,7 @@ ${message}`);
598
888
  markUpdateNotified(result, cachePath);
599
889
  }
600
890
  export {
891
+ LOCALGHOST_ACTIVITY_VERSION,
601
892
  LOCALGHOST_CONFIG_FILE,
602
893
  LOCALGHOST_PACKAGE_NAME,
603
894
  LOCALGHOST_STATE_FILE,
@@ -605,10 +896,13 @@ export {
605
896
  UPDATE_CHECK_CACHE_TTL_MS,
606
897
  UPDATE_CHECK_NOTIFY_TTL_MS,
607
898
  UPDATE_CHECK_TIMEOUT_MS,
899
+ assertLocalDevelopment,
608
900
  checkCaddy,
609
901
  checkForUpdate,
610
902
  compareVersions,
903
+ defineLocalghostConfig,
611
904
  detectPackageManager,
905
+ findAvailablePort,
612
906
  findLocalMdnsHosts,
613
907
  formatDomainRoutes,
614
908
  formatUpdateMessage,
@@ -616,33 +910,49 @@ export {
616
910
  getConfigFileCandidates,
617
911
  getDevHostsPath,
618
912
  getDomainRoutes,
913
+ getLocalghostActivityPath,
619
914
  getLocalghostStatePath,
915
+ getProductionEnvKeys,
916
+ getProductionReason,
620
917
  getProjectName,
621
918
  getSystemHostsPath,
622
919
  getUpdateCheckCachePath,
623
920
  initLocalghost,
624
921
  isNewerVersion,
922
+ isPortAvailable,
923
+ isProcessRunning,
924
+ isProductionLike,
625
925
  isUpdateCheckDisabled,
926
+ listLocalghostRuns,
626
927
  markUpdateNotified,
627
928
  maybeNotifyAboutUpdate,
628
929
  packageAddCommand,
629
930
  packageRunCommand,
630
931
  parseDevHosts,
932
+ patchLocalghostState,
933
+ pruneLocalghostActivity,
631
934
  readDevHosts,
935
+ readLocalghostActivity,
632
936
  readLocalghostState,
937
+ registerLocalghostRun,
633
938
  removeManagedBlock,
634
939
  removeSystemHosts,
635
940
  renderCaddyfile,
636
941
  renderHostsBlock,
637
942
  resolveDevHostsPath,
943
+ resolveLocalghostContext,
638
944
  runCaddy,
639
945
  runDoctor,
640
946
  sanitizeProjectName,
641
947
  shouldNotifyAboutUpdate,
948
+ startCaddy,
949
+ trustCaddy,
950
+ unregisterLocalghostRun,
642
951
  updateSystemHosts,
643
952
  upsertManagedBlock,
644
953
  validateCaddyfile,
645
954
  writeCaddyfile,
955
+ writeLocalghostActivity,
646
956
  writeLocalghostState
647
957
  };
648
958
  //# sourceMappingURL=index.js.map