"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
  for (var name in all)
    __defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
  if (from && typeof from === "object" || typeof from === "function") {
    for (let key of __getOwnPropNames(from))
      if (!__hasOwnProp.call(to, key) && key !== except)
        __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
  }
  return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);

// src/state.ts
var state_exports = {};
__export(state_exports, {
  DEFAULT_PROFILE: () => DEFAULT_PROFILE,
  ProfileLockTimeoutError: () => ProfileLockTimeoutError,
  additionalDelegationsPath: () => additionalDelegationsPath,
  authRequestsPath: () => authRequestsPath,
  profileConfigPath: () => profileConfigPath,
  profileLockMetadataPath: () => profileLockMetadataPath,
  profileLockPath: () => profileLockPath,
  profilePath: () => profilePath,
  profileStoreMetadataPath: () => profileStoreMetadataPath,
  profileStorePath: () => profileStorePath,
  profilesPath: () => profilesPath,
  readAdditionalDelegations: () => readAdditionalDelegations,
  readAuthRequests: () => readAuthRequests,
  readJson: () => readJson,
  readProfile: () => readProfile,
  readProfileStore: () => readProfileStore,
  readSession: () => readSession,
  readStoreMetadata: () => readStoreMetadata,
  removeSession: () => removeSession,
  sessionPath: () => sessionPath,
  tinycloudConfigPath: () => tinycloudConfigPath,
  tinycloudHomePath: () => tinycloudHomePath,
  updateProfileStore: () => updateProfileStore,
  upsertProfileRecord: () => upsertProfileRecord,
  withProfileLock: () => withProfileLock,
  withTinyCloudStateRoot: () => withTinyCloudStateRoot,
  writeJsonAtomic: () => writeJsonAtomic,
  writeSession: () => writeSession
});
module.exports = __toCommonJS(state_exports);
var import_node_crypto = require("crypto");
var import_node_async_hooks = require("async_hooks");
var import_promises = require("fs/promises");
var import_node_os = require("os");
var import_node_path = require("path");
var DEFAULT_PROFILE = "default";
var DEFAULT_LOCK_TIMEOUT_MS = 2e3;
var DEFAULT_LOCK_RETRY_MS = 25;
var DEFAULT_STALE_LOCK_MS = 3e4;
var TEST_LOCK_CONTENTION_SIGNAL_PATH = "TC_TEST_PROFILE_LOCK_CONTENTION_SIGNAL_PATH";
var TEST_LOCK_RECOVERY_BARRIER_DIR = "TC_TEST_PROFILE_LOCK_RECOVERY_BARRIER_DIR";
var invocationStateRoot = new import_node_async_hooks.AsyncLocalStorage();
var ProfileLockTimeoutError = class extends Error {
  constructor(profile, timeoutMs) {
    super(`Timed out waiting for the profile lock for "${profile}" after ${timeoutMs}ms.`);
    this.code = "PROFILE_LOCK_TIMEOUT";
    this.name = "ProfileLockTimeoutError";
  }
};
function tinycloudHomePath() {
  const home = invocationStateRoot.getStore() ?? process.env.TC_HOME ?? process.env.HOME ?? process.env.USERPROFILE ?? (0, import_node_os.homedir)();
  return (0, import_node_path.join)(home, ".tinycloud");
}
function withTinyCloudStateRoot(stateRoot, action) {
  if (stateRoot === void 0) return action();
  if (!(0, import_node_path.isAbsolute)(stateRoot) || stateRoot.includes("\0")) {
    throw new TypeError("The TinyCloud state root must be an absolute path.");
  }
  return invocationStateRoot.run((0, import_node_path.resolve)(stateRoot), action);
}
function tinycloudConfigPath() {
  return (0, import_node_path.join)(tinycloudHomePath(), "config.json");
}
function profilesPath() {
  return (0, import_node_path.join)(tinycloudHomePath(), "profiles");
}
function profilePath(profile) {
  return (0, import_node_path.join)(profilesPath(), validateProfileName(profile));
}
function profileConfigPath(profile) {
  return (0, import_node_path.join)(profilePath(profile), "profile.json");
}
function sessionPath(profile) {
  return profileStorePath(profile, "session");
}
function additionalDelegationsPath(profile) {
  return profileStorePath(profile, "additional-delegations");
}
function authRequestsPath(profile) {
  return profileStorePath(profile, "auth-requests");
}
function profileStorePath(profile, store) {
  return (0, import_node_path.join)(profilePath(profile), `${store}.json`);
}
function profileStoreMetadataPath(profile, store) {
  return `${profileStorePath(profile, store)}.metadata.json`;
}
function profileLockPath(profile) {
  return (0, import_node_path.join)(profilePath(profile), ".lock");
}
function profileLockMetadataPath(profile) {
  return (0, import_node_path.join)(profileLockPath(profile), "owner.json");
}
async function readJson(filePath) {
  try {
    return JSON.parse(await (0, import_promises.readFile)(filePath, "utf8"));
  } catch (error) {
    if (isErrno(error, "ENOENT")) return null;
    throw error;
  }
}
async function writeJsonAtomic(filePath, value) {
  const directory = (0, import_node_path.dirname)(filePath);
  const temporaryPath = (0, import_node_path.join)(
    directory,
    `.${(0, import_node_path.basename)(filePath)}.${process.pid}.${(0, import_node_crypto.randomUUID)()}.tmp`
  );
  const contents = `${JSON.stringify(value, null, 2)}
`;
  await (0, import_promises.mkdir)(directory, { recursive: true });
  try {
    await (0, import_promises.writeFile)(temporaryPath, contents, "utf8");
    await (0, import_promises.rename)(temporaryPath, filePath);
  } catch (error) {
    await (0, import_promises.rm)(temporaryPath, { force: true }).catch(() => void 0);
    throw error;
  }
}
async function readProfile(profile) {
  return readJson(profileConfigPath(profile));
}
async function readSession(profile) {
  await readStoreMetadata(profile, "session");
  return readJson(sessionPath(profile));
}
async function readAdditionalDelegations(profile) {
  return (await readProfileStore(profile, "additional-delegations")).records;
}
async function readAuthRequests(profile) {
  return (await readProfileStore(profile, "auth-requests")).records;
}
async function readStoreMetadata(profile, store) {
  const metadata = await readJson(profileStoreMetadataPath(profile, store));
  if (metadata === null) return { formatVersion: 1 };
  if (typeof metadata !== "object" || metadata.formatVersion !== 1) {
    throw new TypeError(`Unsupported store format for "${store}".`);
  }
  return metadata;
}
async function readProfileStore(profile, store) {
  const [metadata, raw] = await Promise.all([
    readStoreMetadata(profile, store),
    readJson(profileStorePath(profile, store))
  ]);
  return {
    formatVersion: metadata.formatVersion,
    records: Array.isArray(raw) ? raw : []
  };
}
async function withProfileLock(profile, action, options = {}) {
  const normalizedProfile = validateProfileName(profile);
  const release = await acquireProfileLock(normalizedProfile, options);
  try {
    return await action();
  } finally {
    await release();
  }
}
async function upsertProfileRecord(profile, store, key, record, getKey, options = {}) {
  if (!key) throw new TypeError("A non-empty record key is required.");
  return withProfileLock(profile, async () => {
    const current = (await readProfileStore(profile, store)).records;
    const next = current.filter((candidate) => getKey(candidate) !== key);
    next.push(record);
    await writeJsonAtomic(profileStorePath(profile, store), next);
    await writeFormatOneMetadata(profile, store);
    return next;
  }, options);
}
async function updateProfileStore(profile, store, update, options = {}) {
  return withProfileLock(
    profile,
    () => updateProfileStoreWhileLocked(profile, store, update),
    options
  );
}
async function updateProfileStoreWhileLocked(profile, store, update) {
  const current = await readProfileStore(profile, store);
  const next = await update(current.records);
  await writeJsonAtomic(profileStorePath(profile, store), next.records);
  await writeFormatOneMetadata(profile, store);
  return next.result;
}
async function writeSession(profile, session, options = {}) {
  await withProfileLock(profile, async () => {
    await readStoreMetadata(profile, "session");
    await writeJsonAtomic(sessionPath(profile), session);
    await writeFormatOneMetadata(profile, "session");
  }, options);
}
async function removeSession(profile, options = {}) {
  await withProfileLock(profile, async () => {
    await readStoreMetadata(profile, "session");
    await (0, import_promises.rm)(sessionPath(profile), { force: true });
  }, options);
}
async function writeFormatOneMetadata(profile, store) {
  await writeJsonAtomic(profileStoreMetadataPath(profile, store), { formatVersion: 1 });
}
async function acquireProfileLock(profile, options) {
  const timeoutMs = options.timeoutMs ?? DEFAULT_LOCK_TIMEOUT_MS;
  const retryMs = options.retryMs ?? DEFAULT_LOCK_RETRY_MS;
  const staleAfterMs = options.staleAfterMs ?? DEFAULT_STALE_LOCK_MS;
  const startedAt = Date.now();
  const lockPath = profileLockPath(profile);
  await (0, import_promises.mkdir)(profilePath(profile), { recursive: true });
  while (true) {
    try {
      await (0, import_promises.mkdir)(lockPath);
      const token = (0, import_node_crypto.randomUUID)();
      try {
        await writeJsonAtomic(profileLockMetadataPath(profile), {
          pid: process.pid,
          createdAt: (/* @__PURE__ */ new Date()).toISOString(),
          token
        });
      } catch (error) {
        await (0, import_promises.rmdir)(lockPath).catch(() => void 0);
        throw error;
      }
      return async () => {
        await releaseProfileLock(lockPath, token);
      };
    } catch (error) {
      if (!isLockAlreadyHeld(error)) throw error;
      await signalTestLockContention(profile);
    }
    if (await recoverStaleLock(profile, lockPath, staleAfterMs)) continue;
    const elapsedMs = Date.now() - startedAt;
    if (elapsedMs >= timeoutMs) {
      throw new ProfileLockTimeoutError(profile, timeoutMs);
    }
    await sleep(Math.min(retryMs, timeoutMs - elapsedMs));
  }
}
async function releaseProfileLock(lockPath, token) {
  const ownerPath = (0, import_node_path.join)(lockPath, "owner.json");
  const claimPath = (0, import_node_path.join)(lockPath, `.release-${token}.json`);
  try {
    await (0, import_promises.rename)(ownerPath, claimPath);
  } catch (error) {
    if (isErrno(error, "ENOENT")) return;
    throw error;
  }
  const claimed = await readJson(claimPath).catch(() => null);
  if (claimed?.token !== token) {
    await (0, import_promises.rename)(claimPath, ownerPath).catch(() => void 0);
    return;
  }
  await (0, import_promises.rm)(claimPath, { force: true });
  await (0, import_promises.rmdir)(lockPath).catch(() => void 0);
}
async function signalTestLockContention(profile) {
  if (process.env.NODE_ENV !== "test") return;
  const configuredPath = process.env[TEST_LOCK_CONTENTION_SIGNAL_PATH];
  const configuredHome = process.env.TC_HOME;
  if (!configuredPath || !configuredHome) return;
  const home = (0, import_node_path.resolve)(configuredHome);
  const signalPath = (0, import_node_path.resolve)(configuredPath);
  const pathFromHome = (0, import_node_path.relative)(home, signalPath);
  if (pathFromHome === "" || pathFromHome === ".." || pathFromHome.startsWith(`..${process.platform === "win32" ? "\\" : "/"}`) || pathFromHome.startsWith("../") || pathFromHome.startsWith("..\\")) {
    return;
  }
  await (0, import_promises.writeFile)(signalPath, `${profile}
`, { encoding: "utf8", flag: "wx" }).catch(() => void 0);
}
async function recoverStaleLock(profile, lockPath, staleAfterMs) {
  const ownerPath = (0, import_node_path.join)(lockPath, "owner.json");
  const owner = await readJson(ownerPath).catch(() => null);
  if (!isStaleOwner(owner, staleAfterMs)) return false;
  const observedToken = typeof owner?.token === "string" && owner.token.length > 0 ? owner.token : "legacy";
  await waitForTestStaleRecoveryBarrier(profile);
  const claimPath = (0, import_node_path.join)(lockPath, `.stale-${(0, import_node_crypto.randomUUID)()}.json`);
  try {
    await (0, import_promises.rename)(ownerPath, claimPath);
  } catch (error) {
    if (isErrno(error, "ENOENT")) return false;
    return false;
  }
  const claimed = await readJson(claimPath).catch(() => null);
  const sameInstance = claimed !== null && (observedToken === "legacy" ? claimed.token === void 0 : claimed.token === observedToken);
  if (!sameInstance) {
    await (0, import_promises.rename)(claimPath, ownerPath).catch(() => void 0);
    return false;
  }
  await (0, import_promises.rm)(claimPath, { force: true });
  await (0, import_promises.rmdir)(lockPath).catch(() => void 0);
  return true;
}
async function waitForTestStaleRecoveryBarrier(profile) {
  if (process.env.NODE_ENV !== "test") return;
  const barrierDirectory = process.env[TEST_LOCK_RECOVERY_BARRIER_DIR];
  if (!barrierDirectory) return;
  await (0, import_promises.mkdir)(barrierDirectory, { recursive: true });
  const readyPath = (0, import_node_path.join)(barrierDirectory, `ready-${process.pid}-${profile}`);
  await (0, import_promises.writeFile)(readyPath, "ready\n", { encoding: "utf8", flag: "wx" }).catch(() => void 0);
  const releasePath = (0, import_node_path.join)(barrierDirectory, "release");
  while (true) {
    try {
      await (0, import_promises.readFile)(releasePath, "utf8");
      return;
    } catch {
      await sleep(1);
    }
  }
}
function isStaleOwner(owner, staleAfterMs) {
  const now = Date.now();
  const createdAt = typeof owner?.createdAt === "string" ? Date.parse(owner.createdAt) : Number.NaN;
  if (!Number.isFinite(createdAt) || now - createdAt < staleAfterMs) return false;
  if (typeof owner?.pid !== "number" || !Number.isInteger(owner.pid) || owner.pid <= 0) {
    return false;
  }
  return !isProcessAlive(owner.pid);
}
function isLockAlreadyHeld(error) {
  return isErrno(error, "EEXIST") || isErrno(error, "ENOTEMPTY");
}
function isProcessAlive(pid) {
  try {
    process.kill(pid, 0);
    return true;
  } catch (error) {
    return !isErrno(error, "ESRCH");
  }
}
function validateProfileName(profile) {
  if (!profile || profile === "." || profile === ".." || profile.includes("/") || profile.includes("\\") || profile.includes("\0")) {
    throw new TypeError("Profile names must be non-empty path segments.");
  }
  return profile;
}
function isErrno(error, code) {
  return typeof error === "object" && error !== null && "code" in error && error.code === code;
}
function sleep(milliseconds) {
  return new Promise((resolve2) => setTimeout(resolve2, milliseconds));
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
  DEFAULT_PROFILE,
  ProfileLockTimeoutError,
  additionalDelegationsPath,
  authRequestsPath,
  profileConfigPath,
  profileLockMetadataPath,
  profileLockPath,
  profilePath,
  profileStoreMetadataPath,
  profileStorePath,
  profilesPath,
  readAdditionalDelegations,
  readAuthRequests,
  readJson,
  readProfile,
  readProfileStore,
  readSession,
  readStoreMetadata,
  removeSession,
  sessionPath,
  tinycloudConfigPath,
  tinycloudHomePath,
  updateProfileStore,
  upsertProfileRecord,
  withProfileLock,
  withTinyCloudStateRoot,
  writeJsonAtomic,
  writeSession
});
//# sourceMappingURL=state.cjs.map