@hamedb89/localghost 0.4.1 → 0.6.1
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/cli.js +395 -120
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +45 -14
- package/dist/index.js +112 -12
- package/dist/index.js.map +1 -1
- package/dist/vite.js +46 -0
- package/dist/vite.js.map +1 -1
- package/docs/localghost.1.md +26 -0
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -543,18 +543,6 @@ declare function packageRunCommand(packageManager: PackageManager, script: strin
|
|
|
543
543
|
declare function packageAddCommand(packageManager: PackageManager, packageName?: string): string;
|
|
544
544
|
declare function initLocalghost(options?: InitOptions): InitResult;
|
|
545
545
|
|
|
546
|
-
type FindAvailablePortOptions = {
|
|
547
|
-
host?: string;
|
|
548
|
-
maxAttempts?: number;
|
|
549
|
-
};
|
|
550
|
-
declare function isPortAvailable(port: number, host?: string): Promise<boolean>;
|
|
551
|
-
declare function findAvailablePort(startPort: number, options?: FindAvailablePortOptions): Promise<number>;
|
|
552
|
-
|
|
553
|
-
type ProcessSignal = NodeJS.Signals;
|
|
554
|
-
type ProcessKiller = (pid: number, signal: ProcessSignal) => void;
|
|
555
|
-
declare function signalManagedProcessPid(pid: number | undefined, signal: ProcessSignal, killProcess?: ProcessKiller): boolean;
|
|
556
|
-
declare function signalManagedProcess(child: Pick<ChildProcess, "pid" | "kill" | "killed">, signal: ProcessSignal): boolean;
|
|
557
|
-
|
|
558
546
|
declare const LOCALGHOST_REGISTRY_FILE = "registry.json";
|
|
559
547
|
declare const LOCALGHOST_REGISTRY_LOCK_FILE = "registry.lock";
|
|
560
548
|
type PortAvailabilityCheck = (port: number, host?: string) => boolean | Promise<boolean>;
|
|
@@ -599,12 +587,18 @@ type AcquireLocalghostPortOptions = {
|
|
|
599
587
|
leaseTtlMs?: number;
|
|
600
588
|
host?: string;
|
|
601
589
|
};
|
|
590
|
+
type RenewLocalghostPortOptions = {
|
|
591
|
+
projectCwd?: string;
|
|
592
|
+
instanceKey: string;
|
|
593
|
+
leaseTtlMs?: number;
|
|
594
|
+
};
|
|
602
595
|
type LocalghostRegistry = {
|
|
603
596
|
root: string;
|
|
604
597
|
registryPath: string;
|
|
605
598
|
lockPath: string;
|
|
606
599
|
ownerToken: string;
|
|
607
600
|
acquirePort(options: AcquireLocalghostPortOptions): Promise<LocalghostLease>;
|
|
601
|
+
renewPort(options: RenewLocalghostPortOptions): Promise<LocalghostLease | undefined>;
|
|
608
602
|
releasePort(options: {
|
|
609
603
|
projectCwd?: string;
|
|
610
604
|
instanceKey: string;
|
|
@@ -613,11 +607,48 @@ type LocalghostRegistry = {
|
|
|
613
607
|
prune(): Promise<{
|
|
614
608
|
removedLeases: number;
|
|
615
609
|
}>;
|
|
610
|
+
pruneTestSessions(): Promise<{
|
|
611
|
+
removedLeases: number;
|
|
612
|
+
removedAllocations: number;
|
|
613
|
+
}>;
|
|
614
|
+
reset(): Promise<void>;
|
|
616
615
|
};
|
|
617
616
|
declare function getLocalghostRegistryRoot(env?: NodeJS.ProcessEnv): string;
|
|
618
617
|
declare function canonicalizeLocalghostProjectCwd(cwd?: string): string;
|
|
619
618
|
declare function createLocalghostRegistry(options?: LocalghostRegistryOptions): LocalghostRegistry;
|
|
620
619
|
|
|
620
|
+
type LocalghostTestSessionOptions = {
|
|
621
|
+
cwd?: string;
|
|
622
|
+
instanceKey: string;
|
|
623
|
+
services: Record<string, {
|
|
624
|
+
startPort: number;
|
|
625
|
+
maxAttempts?: number;
|
|
626
|
+
host?: string;
|
|
627
|
+
}>;
|
|
628
|
+
leaseTtlMs?: number;
|
|
629
|
+
};
|
|
630
|
+
type LocalghostTestSession = {
|
|
631
|
+
instanceKey: string;
|
|
632
|
+
ports: Record<string, number>;
|
|
633
|
+
leases: LocalghostLease[];
|
|
634
|
+
renew: () => Promise<void>;
|
|
635
|
+
startHeartbeat: (intervalMs?: number) => NodeJS.Timeout;
|
|
636
|
+
release: () => Promise<void>;
|
|
637
|
+
};
|
|
638
|
+
declare function createLocalghostTestSession(options: LocalghostTestSessionOptions): Promise<LocalghostTestSession>;
|
|
639
|
+
|
|
640
|
+
type FindAvailablePortOptions = {
|
|
641
|
+
host?: string;
|
|
642
|
+
maxAttempts?: number;
|
|
643
|
+
};
|
|
644
|
+
declare function isPortAvailable(port: number, host?: string): Promise<boolean>;
|
|
645
|
+
declare function findAvailablePort(startPort: number, options?: FindAvailablePortOptions): Promise<number>;
|
|
646
|
+
|
|
647
|
+
type ProcessSignal = NodeJS.Signals;
|
|
648
|
+
type ProcessKiller = (pid: number, signal: ProcessSignal) => void;
|
|
649
|
+
declare function signalManagedProcessPid(pid: number | undefined, signal: ProcessSignal, killProcess?: ProcessKiller): boolean;
|
|
650
|
+
declare function signalManagedProcess(child: Pick<ChildProcess, "pid" | "kill" | "killed">, signal: ProcessSignal): boolean;
|
|
651
|
+
|
|
621
652
|
type DomainRoute = {
|
|
622
653
|
host: string;
|
|
623
654
|
port: number;
|
|
@@ -685,7 +716,7 @@ type CreateVercelGhostTunnelHandlerOptions = {
|
|
|
685
716
|
declare function createVercelGhostTunnelHandler(options: CreateVercelGhostTunnelHandlerOptions): (request: VercelGhostTunnelRequestLike, response: VercelGhostTunnelResponseLike) => Promise<void>;
|
|
686
717
|
|
|
687
718
|
declare const LOCALGHOST_PACKAGE_NAME = "@hamedb89/localghost";
|
|
688
|
-
declare const LOCALGHOST_VERSION = "0.
|
|
719
|
+
declare const LOCALGHOST_VERSION = "0.6.1";
|
|
689
720
|
declare const UPDATE_CHECK_CACHE_TTL_MS: number;
|
|
690
721
|
declare const UPDATE_CHECK_NOTIFY_TTL_MS: number;
|
|
691
722
|
declare const UPDATE_CHECK_TIMEOUT_MS = 900;
|
|
@@ -722,4 +753,4 @@ declare function maybeNotifyAboutUpdate(options?: {
|
|
|
722
753
|
disabled?: boolean;
|
|
723
754
|
}): Promise<void>;
|
|
724
755
|
|
|
725
|
-
export { type AcquireLocalghostPortOptions, type ActiveRelayRoute, type CaddyModeOptions, type CaddyProcessKiller, type CaddyProcessStopResult, ConfigPattern, type ConstructGhostTunnelIpUrlInput, ConstructGhostTunnelUrlInput, type CreateVercelGhostTunnelHandlerOptions, DEFAULT_GHOST_TUNNEL_IP_TRANSPORT_TTL_SECONDS, DEFAULT_GHOST_TUNNEL_RESPONSE_TTL_SECONDS, DEFAULT_GHOST_TUNNEL_TRANSPORT_QUERY_PARAM, DEFAULT_RELAY_ALLOWED_TARGET_HOSTS, DEFAULT_RELAY_BLOCKED_PORTS, DEFAULT_RELAY_LIMITS, DEFAULT_RELAY_TARGET_POLICY, type DetectedDevCommand, type DetectedDevService, DevHostEntry, type DoctorOptions, type DoctorResult, type DomainRoute, type DomainRouteOptions, type FindAvailablePortOptions, type GhostTunnelAgent, type GhostTunnelAgentOptions, GhostTunnelConfig, type GhostTunnelHttpResponse, type GhostTunnelIpTransportClaim, GhostTunnelOptions, type GhostTunnelQueuedRequest, type GhostTunnelQueuedResponse, GhostTunnelRoute, type GhostTunnelRouteHeartbeat, type GhostTunnelStore, type GhostTunnelStoreEnv, GhostTunnelTransportConfig, GhostTunnelTransportOptions, type InitOptions, type InitResult, LOCALGHOST_ACTIVITY_VERSION, LOCALGHOST_AGENT_GUIDE, LOCALGHOST_GHOST_TUNNEL_FILE, LOCALGHOST_PACKAGE_NAME, LOCALGHOST_REGISTRY_FILE, LOCALGHOST_REGISTRY_LOCK_FILE, LOCALGHOST_STATE_FILE, LOCALGHOST_VERSION, type LocalghostActivity, type LocalghostContext, type LocalghostContextOptions, type LocalghostEnvironment, type LocalghostLease, type LocalghostPackageManager, type LocalghostProjectConfig, type LocalghostProjectConfigResult, type LocalghostRegistry, type LocalghostRegistryData, type LocalghostRegistryEntry, type LocalghostRegistryOptions, type LocalghostRunMode, type LocalghostRunRecord, type LocalghostServiceOptions, type LocalghostSetupRecord, type LocalghostState, type LocalghostStateAction, type PackageManager, type PortAvailabilityCheck, ReadDevHostsOptions, type ReadGhostTunnelOptions, type RedisGhostTunnelEnvResolution, type RedisGhostTunnelStoreOptions, type RegisterLocalghostRunInput, type RegisterLocalghostSetupInput, type RelayAccessMode, type RelayLimits, type RelayLocalTarget, type RelayOfflineResponse, type RelayProtocol, type RelayRouteClaim, type RelayRouteRegistrationInput, type RelayTargetPolicy, type RemoveSystemHostsResult, type ResolveGhostTunnelIpRedirectInput, type ResolveGhostTunnelRequestInput, ResolvedDevHostsPath, type ResolvedGhostTunnelIpRedirect, type ResolvedGhostTunnelRequest, type ServeGhostTunnelLocalRequestInput, type SignedGhostTunnelIpTransportClaim, type SignedRelayRouteClaim, UPDATE_CHECK_CACHE_TTL_MS, UPDATE_CHECK_NOTIFY_TTL_MS, UPDATE_CHECK_TIMEOUT_MS, type UpdateCheckCache, type UpdateCheckResult, type UpdateSystemHostsResult, type VercelGhostTunnelRequestLike, type VercelGhostTunnelResponseLike, type WriteLocalghostStateInput, assertExactRelayHost, assertLocalDevelopment, assertRelayLocalTarget, authenticateRelayAgentToken, canonicalizeLocalghostProjectCwd, checkCaddy, checkForUpdate, compareVersions, constructGhostTunnelIpUrl, createGhostTunnelQueuedRequest, createGhostTunnelRouteHeartbeat, createLocalghostRegistry, createMemoryGhostTunnelStore, createRedisGhostTunnelStore, createRedisGhostTunnelStoreFromEnv, createRelayRouteRegistration, createVercelGhostTunnelHandler, decodeGhostTunnelBody, defineLocalghostConfig, detectDevCommand, detectDevPackageManager, detectDevServices, detectPackageManager, encodeGhostTunnelBody, findAvailablePort, findGhostTunnelEntry, formatDetectedDevCommand, formatDetectedDevServices, formatDomainRoutes, formatGhostTunnel, formatLocalghostAgentGuide, formatUpdateMessage, getCaddyfilePath, getDomainRoutes, getGhostTunnelPath, getLocalghostActivityPath, getLocalghostRegistryRoot, getLocalghostStatePath, getProductionEnvKeys, getProductionReason, getSystemHostsPath, getUpdateCheckCachePath, initLocalghost, isNewerVersion, isPortAvailable, isProcessRunning, isProductionLike, isRelayRouteActive, isUpdateCheckDisabled, listGhostTunnelEntries, listLocalghostRuns, listLocalghostSetups, markUpdateNotified, maybeNotifyAboutUpdate, packageAddCommand, packageRunCommand, patchLocalghostState, pruneLocalghostActivity, readGhostTunnelEntries, readLocalghostActivity, readLocalghostProjectConfig, readLocalghostState, redactRelayHeaders, redactRelayLogUrl, registerLocalghostRun, registerLocalghostSetup, removeManagedBlock, removeSystemHosts, renderCaddyfile, renderCompactLocalghostBanner, renderGhostTunnelRelayOfflineResponse, renderGhostTunnelRouteMissingResponse, renderHostsBlock, renderLocalghostBanner, renderRelayOfflineResponse, resolveGhostTunnelIpRedirect, resolveGhostTunnelPath, resolveGhostTunnelRequest, resolveLocalghostContext, resolveRedisGhostTunnelEnv, runCaddy, runDoctor, serveGhostTunnelLocalRequest, shouldNotifyAboutUpdate, signGhostTunnelIpTransportClaim, signRelayRouteClaim, signalManagedProcess, signalManagedProcessPid, startCaddy, startGhostTunnelAgent, stopCaddyProcesses, stripRelayForwardHeaders, trustCaddy, unregisterLocalghostRun, unregisterLocalghostSetup, updateSystemHosts, upsertManagedBlock, validateCaddyfile, verifyGhostTunnelIpTransportClaim, verifyRelayRouteClaim, writeCaddyfile, writeLocalghostActivity, writeLocalghostState };
|
|
756
|
+
export { type AcquireLocalghostPortOptions, type ActiveRelayRoute, type CaddyModeOptions, type CaddyProcessKiller, type CaddyProcessStopResult, ConfigPattern, type ConstructGhostTunnelIpUrlInput, ConstructGhostTunnelUrlInput, type CreateVercelGhostTunnelHandlerOptions, DEFAULT_GHOST_TUNNEL_IP_TRANSPORT_TTL_SECONDS, DEFAULT_GHOST_TUNNEL_RESPONSE_TTL_SECONDS, DEFAULT_GHOST_TUNNEL_TRANSPORT_QUERY_PARAM, DEFAULT_RELAY_ALLOWED_TARGET_HOSTS, DEFAULT_RELAY_BLOCKED_PORTS, DEFAULT_RELAY_LIMITS, DEFAULT_RELAY_TARGET_POLICY, type DetectedDevCommand, type DetectedDevService, DevHostEntry, type DoctorOptions, type DoctorResult, type DomainRoute, type DomainRouteOptions, type FindAvailablePortOptions, type GhostTunnelAgent, type GhostTunnelAgentOptions, GhostTunnelConfig, type GhostTunnelHttpResponse, type GhostTunnelIpTransportClaim, GhostTunnelOptions, type GhostTunnelQueuedRequest, type GhostTunnelQueuedResponse, GhostTunnelRoute, type GhostTunnelRouteHeartbeat, type GhostTunnelStore, type GhostTunnelStoreEnv, GhostTunnelTransportConfig, GhostTunnelTransportOptions, type InitOptions, type InitResult, LOCALGHOST_ACTIVITY_VERSION, LOCALGHOST_AGENT_GUIDE, LOCALGHOST_GHOST_TUNNEL_FILE, LOCALGHOST_PACKAGE_NAME, LOCALGHOST_REGISTRY_FILE, LOCALGHOST_REGISTRY_LOCK_FILE, LOCALGHOST_STATE_FILE, LOCALGHOST_VERSION, type LocalghostActivity, type LocalghostContext, type LocalghostContextOptions, type LocalghostEnvironment, type LocalghostLease, type LocalghostPackageManager, type LocalghostProjectConfig, type LocalghostProjectConfigResult, type LocalghostRegistry, type LocalghostRegistryData, type LocalghostRegistryEntry, type LocalghostRegistryOptions, type LocalghostRunMode, type LocalghostRunRecord, type LocalghostServiceOptions, type LocalghostSetupRecord, type LocalghostState, type LocalghostStateAction, type LocalghostTestSession, type LocalghostTestSessionOptions, type PackageManager, type PortAvailabilityCheck, ReadDevHostsOptions, type ReadGhostTunnelOptions, type RedisGhostTunnelEnvResolution, type RedisGhostTunnelStoreOptions, type RegisterLocalghostRunInput, type RegisterLocalghostSetupInput, type RelayAccessMode, type RelayLimits, type RelayLocalTarget, type RelayOfflineResponse, type RelayProtocol, type RelayRouteClaim, type RelayRouteRegistrationInput, type RelayTargetPolicy, type RemoveSystemHostsResult, type ResolveGhostTunnelIpRedirectInput, type ResolveGhostTunnelRequestInput, ResolvedDevHostsPath, type ResolvedGhostTunnelIpRedirect, type ResolvedGhostTunnelRequest, type ServeGhostTunnelLocalRequestInput, type SignedGhostTunnelIpTransportClaim, type SignedRelayRouteClaim, UPDATE_CHECK_CACHE_TTL_MS, UPDATE_CHECK_NOTIFY_TTL_MS, UPDATE_CHECK_TIMEOUT_MS, type UpdateCheckCache, type UpdateCheckResult, type UpdateSystemHostsResult, type VercelGhostTunnelRequestLike, type VercelGhostTunnelResponseLike, type WriteLocalghostStateInput, assertExactRelayHost, assertLocalDevelopment, assertRelayLocalTarget, authenticateRelayAgentToken, canonicalizeLocalghostProjectCwd, checkCaddy, checkForUpdate, compareVersions, constructGhostTunnelIpUrl, createGhostTunnelQueuedRequest, createGhostTunnelRouteHeartbeat, createLocalghostRegistry, createLocalghostTestSession, createMemoryGhostTunnelStore, createRedisGhostTunnelStore, createRedisGhostTunnelStoreFromEnv, createRelayRouteRegistration, createVercelGhostTunnelHandler, decodeGhostTunnelBody, defineLocalghostConfig, detectDevCommand, detectDevPackageManager, detectDevServices, detectPackageManager, encodeGhostTunnelBody, findAvailablePort, findGhostTunnelEntry, formatDetectedDevCommand, formatDetectedDevServices, formatDomainRoutes, formatGhostTunnel, formatLocalghostAgentGuide, formatUpdateMessage, getCaddyfilePath, getDomainRoutes, getGhostTunnelPath, getLocalghostActivityPath, getLocalghostRegistryRoot, getLocalghostStatePath, getProductionEnvKeys, getProductionReason, getSystemHostsPath, getUpdateCheckCachePath, initLocalghost, isNewerVersion, isPortAvailable, isProcessRunning, isProductionLike, isRelayRouteActive, isUpdateCheckDisabled, listGhostTunnelEntries, listLocalghostRuns, listLocalghostSetups, markUpdateNotified, maybeNotifyAboutUpdate, packageAddCommand, packageRunCommand, patchLocalghostState, pruneLocalghostActivity, readGhostTunnelEntries, readLocalghostActivity, readLocalghostProjectConfig, readLocalghostState, redactRelayHeaders, redactRelayLogUrl, registerLocalghostRun, registerLocalghostSetup, removeManagedBlock, removeSystemHosts, renderCaddyfile, renderCompactLocalghostBanner, renderGhostTunnelRelayOfflineResponse, renderGhostTunnelRouteMissingResponse, renderHostsBlock, renderLocalghostBanner, renderRelayOfflineResponse, resolveGhostTunnelIpRedirect, resolveGhostTunnelPath, resolveGhostTunnelRequest, resolveLocalghostContext, resolveRedisGhostTunnelEnv, runCaddy, runDoctor, serveGhostTunnelLocalRequest, shouldNotifyAboutUpdate, signGhostTunnelIpTransportClaim, signRelayRouteClaim, signalManagedProcess, signalManagedProcessPid, startCaddy, startGhostTunnelAgent, stopCaddyProcesses, stripRelayForwardHeaders, trustCaddy, unregisterLocalghostRun, unregisterLocalghostSetup, updateSystemHosts, upsertManagedBlock, validateCaddyfile, verifyGhostTunnelIpTransportClaim, verifyRelayRouteClaim, writeCaddyfile, writeLocalghostActivity, writeLocalghostState };
|
package/dist/index.js
CHANGED
|
@@ -844,11 +844,11 @@ function isStopped(signal, localSignal) {
|
|
|
844
844
|
}
|
|
845
845
|
function wait(ms, signal, localSignal) {
|
|
846
846
|
if (isStopped(signal, localSignal)) return Promise.resolve();
|
|
847
|
-
return new Promise((
|
|
848
|
-
const timeout = setTimeout(
|
|
847
|
+
return new Promise((resolve5) => {
|
|
848
|
+
const timeout = setTimeout(resolve5, ms);
|
|
849
849
|
const stop = () => {
|
|
850
850
|
clearTimeout(timeout);
|
|
851
|
-
|
|
851
|
+
resolve5();
|
|
852
852
|
};
|
|
853
853
|
signal?.addEventListener("abort", stop, { once: true });
|
|
854
854
|
localSignal.addEventListener("abort", stop, { once: true });
|
|
@@ -1696,13 +1696,13 @@ import { pathToFileURL } from "url";
|
|
|
1696
1696
|
// src/port.ts
|
|
1697
1697
|
import { createServer } from "net";
|
|
1698
1698
|
async function isPortAvailable(port, host = "127.0.0.1") {
|
|
1699
|
-
return new Promise((
|
|
1699
|
+
return new Promise((resolve5) => {
|
|
1700
1700
|
const server = createServer();
|
|
1701
1701
|
server.once("error", () => {
|
|
1702
|
-
|
|
1702
|
+
resolve5(false);
|
|
1703
1703
|
});
|
|
1704
1704
|
server.once("listening", () => {
|
|
1705
|
-
server.close(() =>
|
|
1705
|
+
server.close(() => resolve5(true));
|
|
1706
1706
|
});
|
|
1707
1707
|
server.listen(port, host);
|
|
1708
1708
|
});
|
|
@@ -1755,6 +1755,9 @@ function validRegistry(value) {
|
|
|
1755
1755
|
function pruneRegistry(registry, now, isRunning) {
|
|
1756
1756
|
registry.leases = registry.leases.filter((lease) => lease.expiresAt > now && isRunning(lease.pid));
|
|
1757
1757
|
}
|
|
1758
|
+
function isTestSessionKey(instanceKey) {
|
|
1759
|
+
return instanceKey.startsWith("test:");
|
|
1760
|
+
}
|
|
1758
1761
|
async function readJson(path) {
|
|
1759
1762
|
try {
|
|
1760
1763
|
return JSON.parse(await readFile(path, "utf8"));
|
|
@@ -1851,6 +1854,37 @@ function createLocalghostRegistry(options = {}) {
|
|
|
1851
1854
|
await releaseLock();
|
|
1852
1855
|
}
|
|
1853
1856
|
},
|
|
1857
|
+
async pruneTestSessions() {
|
|
1858
|
+
const releaseLock = await lock();
|
|
1859
|
+
try {
|
|
1860
|
+
const registry = await readRegistry();
|
|
1861
|
+
const staleTestKeys = new Set(
|
|
1862
|
+
registry.leases.filter((lease) => isTestSessionKey(lease.instanceKey) && (lease.expiresAt <= now() || !isRunning(lease.pid))).map((lease) => leaseKey(lease.projectCwd, lease.instanceKey))
|
|
1863
|
+
);
|
|
1864
|
+
const beforeLeases = registry.leases.length;
|
|
1865
|
+
registry.leases = registry.leases.filter((lease) => !staleTestKeys.has(leaseKey(lease.projectCwd, lease.instanceKey)));
|
|
1866
|
+
const activeKeys = new Set(registry.leases.map((lease) => leaseKey(lease.projectCwd, lease.instanceKey)));
|
|
1867
|
+
const beforeAllocations = registry.allocations.length;
|
|
1868
|
+
registry.allocations = registry.allocations.filter(
|
|
1869
|
+
(allocation) => !isTestSessionKey(allocation.instanceKey) || activeKeys.has(leaseKey(allocation.projectCwd, allocation.instanceKey))
|
|
1870
|
+
);
|
|
1871
|
+
await writeRegistry(registry);
|
|
1872
|
+
return {
|
|
1873
|
+
removedLeases: beforeLeases - registry.leases.length,
|
|
1874
|
+
removedAllocations: beforeAllocations - registry.allocations.length
|
|
1875
|
+
};
|
|
1876
|
+
} finally {
|
|
1877
|
+
await releaseLock();
|
|
1878
|
+
}
|
|
1879
|
+
},
|
|
1880
|
+
async reset() {
|
|
1881
|
+
const releaseLock = await lock();
|
|
1882
|
+
try {
|
|
1883
|
+
await writeRegistry({ version: 1, allocations: [], leases: [] });
|
|
1884
|
+
} finally {
|
|
1885
|
+
await releaseLock();
|
|
1886
|
+
}
|
|
1887
|
+
},
|
|
1854
1888
|
async acquirePort(acquireOptions) {
|
|
1855
1889
|
const projectCwd = canonicalizeLocalghostProjectCwd(acquireOptions.projectCwd ?? cwd);
|
|
1856
1890
|
if (!acquireOptions.instanceKey) throw new Error("instanceKey is required");
|
|
@@ -1888,6 +1922,18 @@ function createLocalghostRegistry(options = {}) {
|
|
|
1888
1922
|
return lease;
|
|
1889
1923
|
});
|
|
1890
1924
|
},
|
|
1925
|
+
async renewPort(renewOptions) {
|
|
1926
|
+
const projectCwd = canonicalizeLocalghostProjectCwd(renewOptions.projectCwd ?? cwd);
|
|
1927
|
+
return withLock(async (registry) => {
|
|
1928
|
+
const lease = registry.leases.find(
|
|
1929
|
+
(candidate) => candidate.projectCwd === projectCwd && candidate.instanceKey === renewOptions.instanceKey && candidate.ownerToken === ownerToken
|
|
1930
|
+
);
|
|
1931
|
+
if (!lease || lease.expiresAt <= now() || !isRunning(lease.pid)) return void 0;
|
|
1932
|
+
lease.expiresAt = now() + (renewOptions.leaseTtlMs ?? 30 * 60 * 1e3);
|
|
1933
|
+
await writeRegistry(registry);
|
|
1934
|
+
return lease;
|
|
1935
|
+
});
|
|
1936
|
+
},
|
|
1891
1937
|
async releasePort(releaseOptions) {
|
|
1892
1938
|
const projectCwd = canonicalizeLocalghostProjectCwd(releaseOptions.projectCwd ?? cwd);
|
|
1893
1939
|
return withLock(async (registry) => {
|
|
@@ -2429,7 +2475,7 @@ async function removeSystemHosts(projectName) {
|
|
|
2429
2475
|
|
|
2430
2476
|
// src/init.ts
|
|
2431
2477
|
import { existsSync as existsSync5, readFileSync as readFileSync6, writeFileSync as writeFileSync4 } from "fs";
|
|
2432
|
-
import { join as join8 } from "path";
|
|
2478
|
+
import { dirname as dirname4, join as join8, resolve as resolve4 } from "path";
|
|
2433
2479
|
function detectPackageManager(cwd = process.cwd()) {
|
|
2434
2480
|
if (existsSync5(join8(cwd, "pnpm-lock.yaml"))) return "pnpm";
|
|
2435
2481
|
if (existsSync5(join8(cwd, "yarn.lock"))) return "yarn";
|
|
@@ -2561,9 +2607,62 @@ function initLocalghost(options = {}) {
|
|
|
2561
2607
|
};
|
|
2562
2608
|
}
|
|
2563
2609
|
|
|
2610
|
+
// src/test-session.ts
|
|
2611
|
+
async function createLocalghostTestSession(options) {
|
|
2612
|
+
if (!options.instanceKey) throw new Error("instanceKey is required");
|
|
2613
|
+
const registry = createLocalghostRegistry(options.cwd ? { cwd: options.cwd } : {});
|
|
2614
|
+
const leases = [];
|
|
2615
|
+
const ports = {};
|
|
2616
|
+
const leaseTtlMs = options.leaseTtlMs ?? 30 * 60 * 1e3;
|
|
2617
|
+
if (!Number.isFinite(leaseTtlMs) || leaseTtlMs < 1e3) throw new Error("leaseTtlMs must be at least 1000 milliseconds.");
|
|
2618
|
+
try {
|
|
2619
|
+
for (const [name, service] of Object.entries(options.services)) {
|
|
2620
|
+
const lease = await registry.acquirePort({
|
|
2621
|
+
...options.cwd ? { projectCwd: options.cwd } : {},
|
|
2622
|
+
instanceKey: `test:${options.instanceKey}:${name}`,
|
|
2623
|
+
startPort: service.startPort,
|
|
2624
|
+
...service.maxAttempts !== void 0 ? { maxAttempts: service.maxAttempts } : {},
|
|
2625
|
+
...service.host !== void 0 ? { host: service.host } : {},
|
|
2626
|
+
leaseTtlMs,
|
|
2627
|
+
reservedPorts: Object.values(ports)
|
|
2628
|
+
});
|
|
2629
|
+
leases.push(lease);
|
|
2630
|
+
ports[name] = lease.port;
|
|
2631
|
+
}
|
|
2632
|
+
} catch (error) {
|
|
2633
|
+
await Promise.all(leases.map((lease) => registry.releasePort({ projectCwd: lease.projectCwd, instanceKey: lease.instanceKey })));
|
|
2634
|
+
throw error;
|
|
2635
|
+
}
|
|
2636
|
+
let released = false;
|
|
2637
|
+
const renew = async () => {
|
|
2638
|
+
if (released) return;
|
|
2639
|
+
await Promise.all(leases.map(async (lease) => {
|
|
2640
|
+
const renewed = await registry.renewPort({ projectCwd: lease.projectCwd, instanceKey: lease.instanceKey, leaseTtlMs });
|
|
2641
|
+
if (!renewed) throw new Error(`Localghost test lease expired: ${lease.instanceKey}`);
|
|
2642
|
+
}));
|
|
2643
|
+
};
|
|
2644
|
+
return {
|
|
2645
|
+
instanceKey: options.instanceKey,
|
|
2646
|
+
ports,
|
|
2647
|
+
leases,
|
|
2648
|
+
renew,
|
|
2649
|
+
startHeartbeat(intervalMs = Math.max(1e3, Math.floor(leaseTtlMs / 3))) {
|
|
2650
|
+
if (!Number.isFinite(intervalMs) || intervalMs < 1e3) throw new Error("Heartbeat interval must be at least 1000 milliseconds.");
|
|
2651
|
+
const timer = setInterval(() => void renew().catch(() => void 0), intervalMs);
|
|
2652
|
+
timer.unref();
|
|
2653
|
+
return timer;
|
|
2654
|
+
},
|
|
2655
|
+
async release() {
|
|
2656
|
+
if (released) return;
|
|
2657
|
+
released = true;
|
|
2658
|
+
await Promise.all(leases.map((lease) => registry.releasePort({ projectCwd: lease.projectCwd, instanceKey: lease.instanceKey })));
|
|
2659
|
+
}
|
|
2660
|
+
};
|
|
2661
|
+
}
|
|
2662
|
+
|
|
2564
2663
|
// src/process.ts
|
|
2565
2664
|
function signalManagedProcessPid(pid, signal, killProcess = (value, processSignal) => process.kill(value, processSignal)) {
|
|
2566
|
-
if (typeof pid !== "number" || !Number.isInteger(pid) || pid
|
|
2665
|
+
if (typeof pid !== "number" || !Number.isInteger(pid) || pid <= 1 || pid === process.pid) return false;
|
|
2567
2666
|
try {
|
|
2568
2667
|
killProcess(process.platform === "win32" ? pid : -pid, signal);
|
|
2569
2668
|
return true;
|
|
@@ -2796,7 +2895,7 @@ async function waitForTunnelResponse(input) {
|
|
|
2796
2895
|
await input.store.cleanup(input.requestId);
|
|
2797
2896
|
return response;
|
|
2798
2897
|
}
|
|
2799
|
-
await new Promise((
|
|
2898
|
+
await new Promise((resolve5) => setTimeout(resolve5, input.pollIntervalMs));
|
|
2800
2899
|
}
|
|
2801
2900
|
return null;
|
|
2802
2901
|
}
|
|
@@ -2897,9 +2996,9 @@ function createVercelGhostTunnelHandler(options) {
|
|
|
2897
2996
|
// src/update-check.ts
|
|
2898
2997
|
import { existsSync as existsSync7, mkdirSync as mkdirSync3, readFileSync as readFileSync7, writeFileSync as writeFileSync5 } from "fs";
|
|
2899
2998
|
import { homedir as homedir3 } from "os";
|
|
2900
|
-
import { dirname as
|
|
2999
|
+
import { dirname as dirname5, join as join10 } from "path";
|
|
2901
3000
|
var LOCALGHOST_PACKAGE_NAME = "@hamedb89/localghost";
|
|
2902
|
-
var LOCALGHOST_VERSION = "0.
|
|
3001
|
+
var LOCALGHOST_VERSION = "0.6.1";
|
|
2903
3002
|
var UPDATE_CHECK_CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
2904
3003
|
var UPDATE_CHECK_NOTIFY_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
2905
3004
|
var UPDATE_CHECK_TIMEOUT_MS = 900;
|
|
@@ -2924,7 +3023,7 @@ function readCache(path = getUpdateCheckCachePath()) {
|
|
|
2924
3023
|
}
|
|
2925
3024
|
function writeCache(cache, path = getUpdateCheckCachePath()) {
|
|
2926
3025
|
try {
|
|
2927
|
-
mkdirSync3(
|
|
3026
|
+
mkdirSync3(dirname5(path), { recursive: true });
|
|
2928
3027
|
writeFileSync5(path, `${JSON.stringify(cache, null, 2)}
|
|
2929
3028
|
`, "utf8");
|
|
2930
3029
|
} catch {
|
|
@@ -3097,6 +3196,7 @@ export {
|
|
|
3097
3196
|
createGhostTunnelQueuedRequest,
|
|
3098
3197
|
createGhostTunnelRouteHeartbeat,
|
|
3099
3198
|
createLocalghostRegistry,
|
|
3199
|
+
createLocalghostTestSession,
|
|
3100
3200
|
createMemoryGhostTunnelStore,
|
|
3101
3201
|
createRedisGhostTunnelStore,
|
|
3102
3202
|
createRedisGhostTunnelStoreFromEnv,
|