@tinycloud/operations 0.4.0-beta.7 → 0.4.0-beta.9
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/artifacts.cjs +493 -6680
- package/dist/artifacts.cjs.map +1 -1
- package/dist/artifacts.d.cts +1 -1
- package/dist/artifacts.d.ts +1 -1
- package/dist/artifacts.js +476 -6661
- package/dist/artifacts.js.map +1 -1
- package/dist/cli-runtime.cjs +727 -6915
- package/dist/cli-runtime.cjs.map +1 -1
- package/dist/cli-runtime.d.cts +1 -1
- package/dist/cli-runtime.d.ts +1 -1
- package/dist/cli-runtime.js +723 -6909
- package/dist/cli-runtime.js.map +1 -1
- package/dist/{contract-xMIIMtpF.d.cts → contract-CjuEjE-M.d.cts} +1 -1
- package/dist/{contract-xMIIMtpF.d.ts → contract-CjuEjE-M.d.ts} +1 -1
- package/dist/delegation-binding.cjs +492 -6679
- package/dist/delegation-binding.cjs.map +1 -1
- package/dist/delegation-binding.js +480 -6665
- package/dist/delegation-binding.js.map +1 -1
- package/dist/index.cjs +727 -6915
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +723 -6909
- package/dist/index.js.map +1 -1
- package/dist/profile.cjs +5 -3
- package/dist/profile.cjs.map +1 -1
- package/dist/profile.d.cts +1 -1
- package/dist/profile.d.ts +1 -1
- package/dist/profile.js +7 -3
- package/dist/profile.js.map +1 -1
- package/dist/state.cjs +422 -51
- package/dist/state.cjs.map +1 -1
- package/dist/state.d.cts +38 -5
- package/dist/state.d.ts +38 -5
- package/dist/state.js +420 -51
- package/dist/state.js.map +1 -1
- package/generated/operations.json +66 -0
- package/package.json +4 -3
package/dist/state.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/state.ts"],"sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { AsyncLocalStorage } from \"node:async_hooks\";\nimport {\n chmod,\n link,\n mkdir,\n readFile,\n readdir,\n rename,\n rmdir,\n rm,\n stat,\n writeFile,\n} from \"node:fs/promises\";\nimport { homedir } from \"node:os\";\nimport { basename, dirname, isAbsolute, join, relative, resolve } from \"node:path\";\n\nconst DEFAULT_PROFILE = \"default\";\nconst DEFAULT_LOCK_TIMEOUT_MS = 2_000;\nconst DEFAULT_LOCK_RETRY_MS = 25;\nconst DEFAULT_STALE_LOCK_MS = 30_000;\nconst TEST_LOCK_CONTENTION_SIGNAL_PATH = \"TC_TEST_PROFILE_LOCK_CONTENTION_SIGNAL_PATH\";\n// Profile state holds keys, sessions and delegations: owner-only access.\nconst PRIVATE_FILE_MODE = 0o600;\nconst PRIVATE_DIR_MODE = 0o700;\nconst TEST_LOCK_RECOVERY_BARRIER_DIR = \"TC_TEST_PROFILE_LOCK_RECOVERY_BARRIER_DIR\";\nconst TEST_LOCK_OWNERLESS_BARRIER_DIR = \"TC_TEST_PROFILE_LOCK_OWNERLESS_BARRIER_DIR\";\nconst TEST_LOCK_PUBLISH_BARRIER_DIR = \"TC_TEST_PROFILE_LOCK_PUBLISH_BARRIER_DIR\";\nconst TEST_LOCK_RELEASE_BARRIER_DIR = \"TC_TEST_PROFILE_LOCK_RELEASE_BARRIER_DIR\";\nconst TEST_LOCK_CLAIM_BARRIER_DIR = \"TC_TEST_PROFILE_LOCK_CLAIM_BARRIER_DIR\";\nconst TEST_LOCK_CLAIMED_BARRIER_DIR = \"TC_TEST_PROFILE_LOCK_CLAIMED_BARRIER_DIR\";\nconst TEST_LOCK_FENCED_BARRIER_DIR = \"TC_TEST_PROFILE_LOCK_FENCED_BARRIER_DIR\";\nconst TEST_LOCK_VERIFIED_BARRIER_DIR = \"TC_TEST_PROFILE_LOCK_VERIFIED_BARRIER_DIR\";\n/** One acquisition of a profile lock; `active` is cleared before release. */\ninterface HeldProfileLock {\n readonly lockPath: string;\n active: boolean;\n}\n/**\n * Async context shared by every copy of this module in the process. Each\n * bundled operations entry point (`state`, `delegation-binding`, the root)\n * carries its own copy, so module-level context would make lock reentrancy and\n * the invocation state root stop at an entry-point boundary.\n *\n * The `.v1` in each key is the format version shared by every copy that reads\n * it. Bump it whenever the `HeldProfileLock` entry shape or the on-disk lock\n * protocol changes, so copies with different formats (two operations versions\n * in one process) stop trusting each other's held-lock entries and simply\n * wait for the lock instead. TC-633 changes the held-lock shape and adds a\n * per-process `turnsInProgress` set: whichever of TC-602 and TC-633 lands\n * second bumps these keys to `.v2` and makes that set process-wide under the\n * same version.\n */\nfunction processWideContext<T>(key: string): AsyncLocalStorage<T> {\n const registry = globalThis as unknown as Record<symbol, AsyncLocalStorage<T> | undefined>;\n return registry[Symbol.for(key)] ??= new AsyncLocalStorage<T>();\n}\nconst invocationStateRoot = processWideContext<string>(\"tinycloud.operations.invocationStateRoot.v1\");\n/** Profile lock acquisitions held by the current async call chain (see withProfileLock). */\nconst heldProfileLocks = processWideContext<readonly HeldProfileLock[]>(\"tinycloud.operations.heldProfileLocks.v1\");\n\nexport type ProfileStoreName =\n | \"session\"\n | \"additional-delegations\"\n | \"auth-requests\";\n\nexport interface StoreMetadata {\n formatVersion: number;\n}\n\nexport interface ProfileStoreContents<T> {\n formatVersion: number;\n records: T[];\n}\n\nexport interface ProfileLockOptions {\n timeoutMs?: number;\n retryMs?: number;\n staleAfterMs?: number;\n}\n\nexport class ProfileLockTimeoutError extends Error {\n readonly code = \"PROFILE_LOCK_TIMEOUT\";\n\n constructor(profile: string, timeoutMs: number) {\n super(`Timed out waiting for the profile lock for \"${profile}\" after ${timeoutMs}ms.`);\n this.name = \"ProfileLockTimeoutError\";\n }\n}\n\n/**\n * The CLI's delegated-secret path treats TC_HOME as a home directory, rather\n * than a direct TinyCloud directory. Keep the shared store on that convention.\n */\nexport function tinycloudHomePath(): string {\n const home = invocationStateRoot.getStore() ??\n process.env.TC_HOME ??\n process.env.HOME ??\n process.env.USERPROFILE ??\n homedir();\n return join(home, \".tinycloud\");\n}\n\n/** Run one operation against an isolated TinyCloud home directory. */\nexport function withTinyCloudStateRoot<T>(\n stateRoot: string | undefined,\n action: () => Promise<T>,\n): Promise<T> {\n if (stateRoot === undefined) return action();\n if (!isAbsolute(stateRoot) || stateRoot.includes(\"\\0\")) {\n throw new TypeError(\"The TinyCloud state root must be an absolute path.\");\n }\n return invocationStateRoot.run(resolve(stateRoot), action);\n}\n\nexport function tinycloudConfigPath(): string {\n return join(tinycloudHomePath(), \"config.json\");\n}\n\nexport function profilesPath(): string {\n return join(tinycloudHomePath(), \"profiles\");\n}\n\nexport function profilePath(profile: string): string {\n return join(profilesPath(), validateProfileName(profile));\n}\n\nexport function profileConfigPath(profile: string): string {\n return join(profilePath(profile), \"profile.json\");\n}\n\nexport function sessionPath(profile: string): string {\n return profileStorePath(profile, \"session\");\n}\n\nexport function additionalDelegationsPath(profile: string): string {\n return profileStorePath(profile, \"additional-delegations\");\n}\n\nexport function authRequestsPath(profile: string): string {\n return profileStorePath(profile, \"auth-requests\");\n}\n\nexport function profileStorePath(profile: string, store: ProfileStoreName): string {\n return join(profilePath(profile), `${store}.json`);\n}\n\n/** The format record is deliberately separate from each legacy JSON payload. */\nexport function profileStoreMetadataPath(profile: string, store: ProfileStoreName): string {\n return `${profileStorePath(profile, store)}.metadata.json`;\n}\n\nexport function profileLockPath(profile: string): string {\n return join(profilePath(profile), \".lock\");\n}\n\nexport function profileLockMetadataPath(profile: string): string {\n return join(profileLockPath(profile), \"owner.json\");\n}\n\n/**\n * Reads JSON without hiding malformed or inaccessible files. A missing file is\n * the only condition represented as null.\n */\nexport async function readJson<T>(filePath: string): Promise<T | null> {\n try {\n return JSON.parse(await readFile(filePath, \"utf8\")) as T;\n } catch (error) {\n if (isErrno(error, \"ENOENT\")) return null;\n throw error;\n }\n}\n\n/**\n * Writes the same pretty-printed, trailing-newline JSON shape used by the CLI,\n * but publishes it with an atomic rename from the same directory.\n */\nexport async function writeJsonAtomic(filePath: string, value: unknown): Promise<void> {\n const directory = dirname(filePath);\n const temporaryPath = join(\n directory,\n `.${basename(filePath)}.${process.pid}.${randomUUID()}.tmp`,\n );\n const contents = `${JSON.stringify(value, null, 2)}\\n`;\n\n await mkdir(directory, { recursive: true, mode: PRIVATE_DIR_MODE });\n try {\n await writeFile(temporaryPath, contents, { encoding: \"utf8\", mode: PRIVATE_FILE_MODE });\n await rename(temporaryPath, filePath);\n } catch (error) {\n await rm(temporaryPath, { force: true }).catch(() => undefined);\n throw error;\n }\n}\n\nexport async function readProfile<T extends object = Record<string, unknown>>(\n profile: string,\n): Promise<T | null> {\n return readJson<T>(profileConfigPath(profile));\n}\n\nexport async function readSession<T extends object = Record<string, unknown>>(\n profile: string,\n): Promise<T | null> {\n await readStoreMetadata(profile, \"session\");\n return readJson<T>(sessionPath(profile));\n}\n\nexport async function readAdditionalDelegations<T = Record<string, unknown>>(\n profile: string,\n): Promise<T[]> {\n return (await readProfileStore<T>(profile, \"additional-delegations\")).records;\n}\n\nexport async function readAuthRequests<T = Record<string, unknown>>(\n profile: string,\n): Promise<T[]> {\n return (await readProfileStore<T>(profile, \"auth-requests\")).records;\n}\n\nexport async function readStoreMetadata(\n profile: string,\n store: ProfileStoreName,\n): Promise<StoreMetadata> {\n const metadata = await readJson<StoreMetadata>(profileStoreMetadataPath(profile, store));\n if (metadata === null) return { formatVersion: 1 };\n if (\n typeof metadata !== \"object\" ||\n metadata.formatVersion !== 1\n ) {\n throw new TypeError(`Unsupported store format for \"${store}\".`);\n }\n return metadata;\n}\n\nexport async function readProfileStore<T>(\n profile: string,\n store: Exclude<ProfileStoreName, \"session\">,\n): Promise<ProfileStoreContents<T>> {\n const [metadata, raw] = await Promise.all([\n readStoreMetadata(profile, store),\n readJson<unknown>(profileStorePath(profile, store)),\n ]);\n return {\n formatVersion: metadata.formatVersion,\n records: Array.isArray(raw) ? raw as T[] : [],\n };\n}\n\n/**\n * Runs a small critical section under the one advisory lock shared by all\n * profile stores. Lock release verifies its ownership token before removing\n * the exact metadata instance it acquired.\n *\n * Reentrant within one async call chain: a critical section that already\n * holds a profile's lock (for example a login's compare-and-commit) can call\n * store writers that take the same lock without deadlocking. Ownership is\n * keyed by the resolved lock path (so switching state roots inside a lock\n * still takes the other root's lock) and lasts exactly as long as the\n * acquisition: it is revoked before release, so work deferred past the\n * critical section waits for the lock like any other caller.\n */\nexport async function withProfileLock<T>(\n profile: string,\n action: () => Promise<T>,\n options: ProfileLockOptions = {},\n): Promise<T> {\n const normalizedProfile = validateProfileName(profile);\n const lockPath = profileLockPath(normalizedProfile);\n const held = (heldProfileLocks.getStore() ?? []).filter((ownership) => ownership.active);\n if (held.some((ownership) => ownership.lockPath === lockPath)) return action();\n const release = await acquireProfileLock(normalizedProfile, options);\n const ownership: HeldProfileLock = { lockPath, active: true };\n try {\n return await heldProfileLocks.run([...held, ownership], action);\n } finally {\n ownership.active = false;\n await release();\n }\n}\n\n/**\n * Appends a record or replaces the existing record with the supplied explicit\n * key. The extractor is supplied by the owning store so state.ts never needs\n * to know permission-request or delegation record shapes.\n */\nexport async function upsertProfileRecord<T>(\n profile: string,\n store: Exclude<ProfileStoreName, \"session\">,\n key: string,\n record: T,\n getKey: (candidate: T) => string | undefined,\n options: ProfileLockOptions = {},\n): Promise<T[]> {\n if (!key) throw new TypeError(\"A non-empty record key is required.\");\n\n return withProfileLock(profile, async () => {\n const current = (await readProfileStore<T>(profile, store)).records;\n const next = current.filter((candidate) => getKey(candidate) !== key);\n next.push(record);\n await writeJsonAtomic(profileStorePath(profile, store), next);\n await writeFormatOneMetadata(profile, store);\n return next;\n }, options);\n}\n\n/**\n * Performs a typed read-modify-write of a legacy array store under the one\n * per-profile lock. The payload stays an array and the sibling metadata stays\n * format 1, so existing CLI readers keep their byte/layout contract.\n */\nexport async function updateProfileStore<T, Result>(\n profile: string,\n store: Exclude<ProfileStoreName, \"session\">,\n update: (\n records: readonly T[],\n ) => Promise<{ readonly records: readonly T[]; readonly result: Result }> | {\n readonly records: readonly T[];\n readonly result: Result;\n },\n options: ProfileLockOptions = {},\n): Promise<Result> {\n return withProfileLock(\n profile,\n () => updateProfileStoreWhileLocked(profile, store, update),\n options,\n );\n}\n\n/** The caller must already own this profile's lock. */\nasync function updateProfileStoreWhileLocked<T, Result>(\n profile: string,\n store: Exclude<ProfileStoreName, \"session\">,\n update: (\n records: readonly T[],\n ) => Promise<{ readonly records: readonly T[]; readonly result: Result }> | {\n readonly records: readonly T[];\n readonly result: Result;\n },\n): Promise<Result> {\n const current = await readProfileStore<T>(profile, store);\n const next = await update(current.records);\n await writeJsonAtomic(profileStorePath(profile, store), next.records);\n await writeFormatOneMetadata(profile, store);\n return next.result;\n}\n\nexport async function writeSession<T extends object>(\n profile: string,\n session: T,\n options: ProfileLockOptions = {},\n): Promise<void> {\n await withProfileLock(profile, async () => {\n await readStoreMetadata(profile, \"session\");\n await writeJsonAtomic(sessionPath(profile), session);\n await writeFormatOneMetadata(profile, \"session\");\n }, options);\n}\n\nexport async function removeSession(\n profile: string,\n options: ProfileLockOptions = {},\n): Promise<void> {\n await withProfileLock(profile, async () => {\n await readStoreMetadata(profile, \"session\");\n await rm(sessionPath(profile), { force: true });\n }, options);\n}\n\nasync function writeFormatOneMetadata(profile: string, store: ProfileStoreName): Promise<void> {\n await writeJsonAtomic(profileStoreMetadataPath(profile, store), { formatVersion: 1 });\n}\n\nasync function acquireProfileLock(\n profile: string,\n options: ProfileLockOptions,\n): Promise<() => Promise<void>> {\n const timeoutMs = options.timeoutMs ?? DEFAULT_LOCK_TIMEOUT_MS;\n const retryMs = options.retryMs ?? DEFAULT_LOCK_RETRY_MS;\n const staleAfterMs = options.staleAfterMs ?? DEFAULT_STALE_LOCK_MS;\n const startedAt = Date.now();\n const lockPath = profileLockPath(profile);\n\n const directory = profilePath(profile);\n await mkdir(directory, { recursive: true, mode: PRIVATE_DIR_MODE });\n // Every store write takes this lock: tighten the tree older releases\n // created 0775 before writing sessions or delegations into it.\n for (const path of [tinycloudHomePath(), profilesPath(), directory]) {\n await chmod(path, PRIVATE_DIR_MODE).catch((error: unknown) => {\n // A profile deletion can remove the profile directory right after the\n // mkdir above; the lock attempt below recreates it 0700.\n if (path !== directory || !isErrno(error, \"ENOENT\")) throw error;\n });\n }\n\n while (true) {\n const token = randomUUID();\n if (await publishProfileLock(profile, lockPath, token)) {\n return async () => {\n await releaseProfileLock(profile, lockPath, token);\n };\n }\n await signalTestLockContention(profile);\n\n if (await recoverStaleLock(profile, lockPath, staleAfterMs)) continue;\n\n const elapsedMs = Date.now() - startedAt;\n if (elapsedMs >= timeoutMs) {\n throw new ProfileLockTimeoutError(profile, timeoutMs);\n }\n await sleep(Math.min(retryMs, timeoutMs - elapsedMs));\n }\n}\n\n/**\n * One attempt to take the lock. Nothing here replaces an existing path:\n *\n * 1. `mkdir(.lock)` (never recursive) is exclusive, and is the primitive\n * older releases use too, so they exclude each other.\n * 2. Ownership is published by `link`ing a fully written owner file (staged\n * beside `.lock`, on the same filesystem) to `.lock/owner.json`. `link`\n * fails with EEXIST if an owner exists, and with ENOENT if the directory\n * was removed meanwhile (an aged ownerless-lock reclaim). Only a\n * successful link holds the lock; on either failure nothing is released,\n * since nothing was acquired, and the caller retries.\n *\n * A profile deletion removes the profile directory once it has released its\n * lock, so `mkdir(.lock)` can see ENOENT; the directory is recreated (0700)\n * and the caller retries within its deadline, as for a first write to a new\n * profile.\n *\n * Only one owner.json can exist at the lock path, and it is removed only by\n * its holder's release or by recovery of a dead holder, so at most one\n * process holds the lock. Returns false when the lock is not acquired.\n */\nasync function publishProfileLock(profile: string, lockPath: string, token: string): Promise<boolean> {\n try {\n await mkdir(lockPath, { mode: PRIVATE_DIR_MODE });\n } catch (error) {\n if (isErrno(error, \"EEXIST\")) return false;\n if (!isErrno(error, \"ENOENT\")) throw error;\n await mkdir(profilePath(profile), { recursive: true, mode: PRIVATE_DIR_MODE });\n return false;\n }\n await waitForTestBarrier(TEST_LOCK_PUBLISH_BARRIER_DIR, profile);\n const staged = join(dirname(lockPath), `.lock-owner-${token}.tmp`);\n const owner = { pid: process.pid, createdAt: new Date().toISOString(), token };\n try {\n await writeFile(staged, `${JSON.stringify(owner, null, 2)}\\n`, { encoding: \"utf8\", mode: PRIVATE_FILE_MODE, flag: \"wx\" });\n await link(staged, join(lockPath, \"owner.json\"));\n return true;\n } catch (error) {\n if (isErrno(error, \"EEXIST\") || isErrno(error, \"ENOENT\")) return false;\n // Do not leave an ownerless lock behind for others to wait out. rmdir\n // removes only an empty directory; anyone who created it after ours went\n // away sees ENOENT on link and retries.\n await rmdir(lockPath).catch(() => undefined);\n throw error;\n } finally {\n await rm(staged, { force: true }).catch(() => undefined);\n }\n}\n\n/**\n * Releases the lock if `.lock/owner.json` is still this acquisition's. No\n * one else can put an owner record there while it exists (owners are only\n * linked into a directory their own `mkdir` created, and recovery removes\n * only a dead holder's record), so the record read is the one unlinked.\n */\nasync function releaseProfileLock(profile: string, lockPath: string, token: string): Promise<void> {\n const ownerPath = join(lockPath, \"owner.json\");\n const owner = await readJson<{ token?: unknown }>(ownerPath).catch(() => null);\n if (owner?.token !== token) return;\n await rm(ownerPath, { force: true });\n // `.lock` is empty from here until the rmdir; a contender that removes it\n // and creates its own meanwhile is not harmed: rmdir fails on its owner\n // file, or the contender's link sees ENOENT and retries. If a recoverer's\n // claim file is still inside, rmdir fails and that recoverer removes the\n // directory when it drops its claim.\n await waitForTestBarrier(TEST_LOCK_RELEASE_BARRIER_DIR, profile);\n await rmdir(lockPath).catch(() => undefined);\n}\n\n/**\n * Gives process-level contention tests an event-driven witness that a second\n * writer has reached an already-held lock. This deliberately has no effect\n * outside an explicit test environment, and only creates a new signal file\n * beneath TC_HOME; it never changes lock acquisition or release semantics.\n */\nasync function signalTestLockContention(profile: string): Promise<void> {\n if (process.env.NODE_ENV !== \"test\") return;\n\n const configuredPath = process.env[TEST_LOCK_CONTENTION_SIGNAL_PATH];\n const configuredHome = process.env.TC_HOME;\n if (!configuredPath || !configuredHome) return;\n\n const home = resolve(configuredHome);\n const signalPath = resolve(configuredPath);\n const pathFromHome = relative(home, signalPath);\n if (\n pathFromHome === \"\" ||\n pathFromHome === \"..\" ||\n pathFromHome.startsWith(`..${process.platform === \"win32\" ? \"\\\\\" : \"/\"}`) ||\n pathFromHome.startsWith(\"../\") ||\n pathFromHome.startsWith(\"..\\\\\")\n ) {\n return;\n }\n\n await writeFile(signalPath, `${profile}\\n`, { encoding: \"utf8\", flag: \"wx\" })\n .catch(() => undefined);\n}\n\n/**\n * Reclaims a lock directory with no owner record that is older than the\n * stale threshold: one left by a crash, or by an older release. A process\n * killed while recovering (or releasing, in older releases) can leave its\n * `.stale-*` / `.release-*` claim file behind; such claim files are removed\n * first. owner.json is never touched, and the directory itself is only\n * `rmdir`ed, which succeeds only while it is empty, so a published lock is\n * never removed. If the directory was a contender's lock not yet published,\n * that contender's `link` fails with ENOENT and it retries without holding\n * anything.\n */\nasync function recoverOwnerlessLock(profile: string, lockPath: string, staleAfterMs: number): Promise<boolean> {\n try {\n const { mtimeMs } = await stat(lockPath);\n if (Date.now() - mtimeMs < staleAfterMs) return false;\n } catch {\n return false;\n }\n const checkedAt = performance.now();\n await waitForTestBarrier(TEST_LOCK_OWNERLESS_BARRIER_DIR, profile);\n try {\n const entries = await readdir(lockPath);\n if (entries.includes(\"owner.json\")) return false;\n // The age check above is only good for a while; see recoveryFenced.\n if (recoveryFenced(checkedAt, staleAfterMs)) return false;\n for (const name of entries.filter((entry) => ABANDONED_CLAIM.test(entry))) {\n await rm(join(lockPath, name), { force: true });\n }\n await rmdir(lockPath);\n } catch {\n return false;\n }\n await removeAgedOwnerFiles(lockPath, staleAfterMs);\n return true;\n}\n\n/**\n * Whether a recovery step decided at `since` (a monotonic timestamp) is too\n * old to act on. Claim-only and ownerless cleanup act only on a `.lock`\n * unchanged for `staleAfterMs`, and a recoverer's claim (or the change that\n * made a directory look abandoned) dates the directory no earlier than\n * `since`. Acting within half that window means no such cleanup can have\n * removed the directory and let a new holder in meanwhile; past it, the\n * recoverer drops only its own claim and starts the acquisition over.\n */\nfunction recoveryFenced(since: number, staleAfterMs: number): boolean {\n return performance.now() - since >= staleAfterMs / 2;\n}\n\n/** Claim files of stale recovery (and of release in older releases). */\nconst ABANDONED_CLAIM = /^\\.(?:release|stale)-[0-9a-f-]+\\.json$/;\n/** Owner files staged by publishProfileLock beside `.lock`. */\nconst STAGED_OWNER = /^\\.lock-owner-[0-9a-f-]+\\.tmp$/;\n\n/**\n * Removes owner files a crashed acquirer staged beside `.lock`. A live\n * acquirer's staged file exists only for its write and link, so only files\n * older than the stale threshold are removed. Runs after a recovery, never\n * on the uncontended path.\n */\nasync function removeAgedOwnerFiles(lockPath: string, staleAfterMs: number): Promise<void> {\n const directory = dirname(lockPath);\n const names = await readdir(directory).catch(() => []);\n for (const name of names.filter((entry) => STAGED_OWNER.test(entry))) {\n const path = join(directory, name);\n const aged = await stat(path).then(({ mtimeMs }) => Date.now() - mtimeMs >= staleAfterMs, () => false);\n if (aged) await rm(path, { force: true }).catch(() => undefined);\n }\n}\n\n/**\n * Removes a dead holder's lock. The claim is a hard link to owner.json, so\n * the owner record is never moved away: a recoverer whose observation is\n * outdated (the dead holder's lock was already recovered and a live holder\n * now owns `.lock`) sees another token, drops its link and leaves the live\n * owner record in place. Only after the claimed record is confirmed to be\n * the observed dead holder's is owner.json unlinked. Nothing can link a new\n * owner.json meanwhile: the claim keeps `.lock` non-empty, so it cannot be\n * removed and recreated.\n */\nasync function recoverStaleLock(profile: string, lockPath: string, staleAfterMs: number): Promise<boolean> {\n const ownerPath = join(lockPath, \"owner.json\");\n const owner = await readJson<{ pid?: unknown; createdAt?: unknown; token?: unknown }>(ownerPath)\n .catch(() => null);\n if (owner === null) return recoverOwnerlessLock(profile, lockPath, staleAfterMs);\n if (!isStaleOwner(owner, staleAfterMs)) return false;\n\n const observedToken = typeof owner?.token === \"string\" && owner.token.length > 0\n ? owner.token\n : \"legacy\";\n await waitForTestBarrier(TEST_LOCK_RECOVERY_BARRIER_DIR, profile);\n const claimPath = join(lockPath, `.stale-${randomUUID()}.json`);\n try {\n await link(ownerPath, claimPath);\n } catch {\n return false;\n }\n const claimedAt = performance.now();\n await waitForTestBarrier(TEST_LOCK_CLAIM_BARRIER_DIR, profile);\n\n const claimed = await readJson<{ pid?: unknown; createdAt?: unknown; token?: unknown }>(claimPath)\n .catch(() => null);\n const sameInstance = claimed !== null && (\n observedToken === \"legacy\"\n ? claimed.token === undefined\n : claimed.token === observedToken\n );\n await waitForTestBarrier(TEST_LOCK_VERIFIED_BARRIER_DIR, profile);\n if (sameInstance && recoveryFenced(claimedAt, staleAfterMs)) {\n // Paused too long: another recoverer may have finished this recovery and\n // a cleanup may have removed this claim, so owner.json may now be a live\n // holder's. Touch nothing but this claim.\n await rm(claimPath, { force: true });\n await waitForTestBarrier(TEST_LOCK_FENCED_BARRIER_DIR, profile);\n return false;\n }\n if (sameInstance) {\n await rm(ownerPath, { force: true });\n await waitForTestBarrier(TEST_LOCK_CLAIMED_BARRIER_DIR, profile);\n }\n await rm(claimPath, { force: true });\n // Empty now if the claimed holder is gone or a live holder released while\n // this claim kept `.lock` from being removed: remove it rather than leave\n // an ownerless lock to age out. rmdir fails while an owner record exists.\n await rmdir(lockPath).catch(() => undefined);\n if (!sameInstance) return false;\n await removeAgedOwnerFiles(lockPath, staleAfterMs);\n return true;\n}\n\n/** Test-only rendezvous (NODE_ENV=test and the named barrier directory set). */\nasync function waitForTestBarrier(environmentName: string, profile: string): Promise<void> {\n if (process.env.NODE_ENV !== \"test\") return;\n const barrierDirectory = process.env[environmentName];\n if (!barrierDirectory) return;\n await mkdir(barrierDirectory, { recursive: true });\n const readyPath = join(barrierDirectory, `ready-${process.pid}-${profile}`);\n await writeFile(readyPath, \"ready\\n\", { encoding: \"utf8\", flag: \"wx\" }).catch(() => undefined);\n const releasePath = join(barrierDirectory, \"release\");\n while (true) {\n try {\n await readFile(releasePath, \"utf8\");\n return;\n } catch {\n await sleep(1);\n }\n }\n}\n\nfunction isStaleOwner(\n owner: { pid?: unknown; createdAt?: unknown } | null,\n staleAfterMs: number,\n): boolean {\n const now = Date.now();\n const createdAt = typeof owner?.createdAt === \"string\"\n ? Date.parse(owner.createdAt)\n : Number.NaN;\n\n // Reclaim only a fully published lock whose owner PID is confirmed dead.\n // A lock without an owner record is handled by recoverOwnerlessLock: a\n // process holds the lock only once its owner.json is linked in.\n if (!Number.isFinite(createdAt) || now - createdAt < staleAfterMs) return false;\n if (typeof owner?.pid !== \"number\" || !Number.isInteger(owner.pid) || owner.pid <= 0) {\n return false;\n }\n return !isProcessAlive(owner.pid);\n}\n\nfunction isProcessAlive(pid: number): boolean {\n try {\n process.kill(pid, 0);\n return true;\n } catch (error) {\n return !isErrno(error, \"ESRCH\");\n }\n}\n\nfunction validateProfileName(profile: string): string {\n if (\n !profile ||\n profile === \".\" ||\n profile === \"..\" ||\n profile.includes(\"/\") ||\n profile.includes(\"\\\\\") ||\n profile.includes(\"\\0\")\n ) {\n throw new TypeError(\"Profile names must be non-empty path segments.\");\n }\n return profile;\n}\n\nfunction isErrno(error: unknown, code: string): boolean {\n return typeof error === \"object\" && error !== null && \"code\" in error &&\n (error as { code?: unknown }).code === code;\n}\n\nfunction sleep(milliseconds: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, milliseconds));\n}\n\nexport { DEFAULT_PROFILE };\n"],"mappings":"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","names":["ownership","resolve"]}
|
|
1
|
+
{"version":3,"sources":["../src/state.ts"],"sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { AsyncLocalStorage } from \"node:async_hooks\";\nimport {\n chmod,\n link,\n mkdir,\n open,\n readFile,\n readdir,\n rename,\n rmdir,\n rm,\n stat,\n unlink,\n writeFile,\n} from \"node:fs/promises\";\nimport { homedir } from \"node:os\";\nimport { basename, dirname, isAbsolute, join, relative, resolve } from \"node:path\";\n\nconst DEFAULT_PROFILE = \"default\";\nconst DEFAULT_LOCK_TIMEOUT_MS = 2_000;\nconst DEFAULT_LOCK_RETRY_MS = 25;\nconst DEFAULT_STALE_LOCK_MS = 30_000;\nconst TEST_LOCK_CONTENTION_SIGNAL_PATH = \"TC_TEST_PROFILE_LOCK_CONTENTION_SIGNAL_PATH\";\n// Profile state holds keys, sessions and delegations: owner-only access.\nconst PRIVATE_FILE_MODE = 0o600;\nconst PRIVATE_DIR_MODE = 0o700;\nconst TEST_LOCK_RECOVERY_BARRIER_DIR = \"TC_TEST_PROFILE_LOCK_RECOVERY_BARRIER_DIR\";\nconst TEST_LOCK_OWNERLESS_BARRIER_DIR = \"TC_TEST_PROFILE_LOCK_OWNERLESS_BARRIER_DIR\";\nconst TEST_LOCK_PUBLISH_BARRIER_DIR = \"TC_TEST_PROFILE_LOCK_PUBLISH_BARRIER_DIR\";\nconst TEST_LOCK_RELEASE_BARRIER_DIR = \"TC_TEST_PROFILE_LOCK_RELEASE_BARRIER_DIR\";\nconst TEST_LOCK_CLAIM_BARRIER_DIR = \"TC_TEST_PROFILE_LOCK_CLAIM_BARRIER_DIR\";\nconst TEST_LOCK_CLAIMED_BARRIER_DIR = \"TC_TEST_PROFILE_LOCK_CLAIMED_BARRIER_DIR\";\nconst TEST_LOCK_VERIFIED_BARRIER_DIR = \"TC_TEST_PROFILE_LOCK_VERIFIED_BARRIER_DIR\";\nconst TEST_LOCK_MOVED_BARRIER_DIR = \"TC_TEST_PROFILE_LOCK_MOVED_BARRIER_DIR\";\nconst TEST_LOCK_TURN_PUBLISH_BARRIER_DIR = \"TC_TEST_PROFILE_LOCK_TURN_PUBLISH_BARRIER_DIR\";\nconst TEST_LOCK_TURN_SETTLE_BARRIER_DIR = \"TC_TEST_PROFILE_LOCK_TURN_SETTLE_BARRIER_DIR\";\nconst TEST_LOCK_TURN_COLLECT_BARRIER_DIR = \"TC_TEST_PROFILE_LOCK_TURN_COLLECT_BARRIER_DIR\";\n/** One acquisition of a profile lock; `active` is cleared before release. */\ninterface HeldProfileLock {\n readonly lockPath: string;\n /** The number of the turn this acquisition holds (see acquireTurn). */\n readonly turnSlot: number;\n /** A deletion of the profile ran while this acquisition waited for the lock. */\n readonly deletedWhileWaiting: boolean;\n active: boolean;\n}\n/**\n * Async context shared by every copy of this module in the process. Each\n * bundled operations entry point (`state`, `delegation-binding`, the root)\n * carries its own copy, so module-level context would make lock reentrancy and\n * the invocation state root stop at an entry-point boundary.\n * Each key is independently versioned for the shape and meaning of the\n * value it stores. Held-lock entries gained `turnSlot` and\n * `deletedWhileWaiting` in TC-633, so `.v2` must not read TC-602's `.v1`\n * entries. The invocation root remains a string path with unchanged\n * semantics and stays `.v1`. Relinquished turn tokens use a separate\n * versioned process-wide key: only an explicitly given-up turn of this PID\n * may be settled across separately bundled entry points.\n */\nfunction processWideContext<T>(key: string): AsyncLocalStorage<T> {\n const registry = globalThis as unknown as Record<symbol, AsyncLocalStorage<T> | undefined>;\n return registry[Symbol.for(key)] ??= new AsyncLocalStorage<T>();\n}\nconst invocationStateRoot = processWideContext<string>(\"tinycloud.operations.invocationStateRoot.v1\");\n/** Profile lock acquisitions held by the current async call chain (see withProfileLock). */\nconst heldProfileLocks = processWideContext<readonly HeldProfileLock[]>(\"tinycloud.operations.heldProfileLocks.v2\");\n\nexport type ProfileStoreName =\n | \"session\"\n | \"additional-delegations\"\n | \"auth-requests\";\n\nexport interface StoreMetadata {\n formatVersion: number;\n}\n\nexport interface ProfileStoreContents<T> {\n formatVersion: number;\n records: T[];\n}\n\nexport interface ProfileLockOptions {\n timeoutMs?: number;\n retryMs?: number;\n staleAfterMs?: number;\n}\n\nexport class ProfileLockTimeoutError extends Error {\n readonly code = \"PROFILE_LOCK_TIMEOUT\";\n\n /** `detail` names the cause when it is not simply another process holding the lock. */\n constructor(profile: string, timeoutMs: number, detail?: string) {\n super(`Timed out waiting for the profile lock for \"${profile}\" after ${timeoutMs}ms.${detail ? ` ${detail}` : \"\"}`);\n this.name = \"ProfileLockTimeoutError\";\n }\n}\n\n/**\n * A store write that waited for the profile lock while a deletion of that\n * profile held it, and found no profile settings once it got the lock. The\n * write is refused rather than recreating a profile with only a session or\n * store in it.\n */\nexport class ProfileDeletedError extends Error {\n readonly code = \"PROFILE_NOT_FOUND\";\n\n constructor(profile: string) {\n super(`Profile \"${profile}\" was deleted while this change waited for its lock, so the change was not written.`);\n this.name = \"ProfileDeletedError\";\n }\n}\n\n/**\n * The CLI's delegated-secret path treats TC_HOME as a home directory, rather\n * than a direct TinyCloud directory. Keep the shared store on that convention.\n */\nexport function tinycloudHomePath(): string {\n const home = invocationStateRoot.getStore() ??\n process.env.TC_HOME ??\n process.env.HOME ??\n process.env.USERPROFILE ??\n homedir();\n return join(home, \".tinycloud\");\n}\n\n/** Run one operation against an isolated TinyCloud home directory. */\nexport function withTinyCloudStateRoot<T>(\n stateRoot: string | undefined,\n action: () => Promise<T>,\n): Promise<T> {\n if (stateRoot === undefined) return action();\n if (!isAbsolute(stateRoot) || stateRoot.includes(\"\\0\")) {\n throw new TypeError(\"The TinyCloud state root must be an absolute path.\");\n }\n return invocationStateRoot.run(resolve(stateRoot), action);\n}\n\nexport function tinycloudConfigPath(): string {\n return join(tinycloudHomePath(), \"config.json\");\n}\n\nexport function profilesPath(): string {\n return join(tinycloudHomePath(), \"profiles\");\n}\n\nexport function profilePath(profile: string): string {\n return join(profilesPath(), validateProfileName(profile));\n}\n\nexport function profileConfigPath(profile: string): string {\n return join(profilePath(profile), \"profile.json\");\n}\n\nexport function sessionPath(profile: string): string {\n return profileStorePath(profile, \"session\");\n}\n\nexport function additionalDelegationsPath(profile: string): string {\n return profileStorePath(profile, \"additional-delegations\");\n}\n\nexport function authRequestsPath(profile: string): string {\n return profileStorePath(profile, \"auth-requests\");\n}\n\nexport function profileStorePath(profile: string, store: ProfileStoreName): string {\n return join(profilePath(profile), `${store}.json`);\n}\n\n/** The format record is deliberately separate from each legacy JSON payload. */\nexport function profileStoreMetadataPath(profile: string, store: ProfileStoreName): string {\n return `${profileStorePath(profile, store)}.metadata.json`;\n}\n\nexport function profileLockPath(profile: string): string {\n return join(profilePath(profile), \".lock\");\n}\n\nexport function profileLockMetadataPath(profile: string): string {\n return join(profileLockPath(profile), \"owner.json\");\n}\n\n/**\n * The turn lock of a profile (see acquireTurn). It lives outside the profile\n * directory, so neither a profile deletion nor an older release's cleanup\n * removes it.\n */\nexport function profileTurnLockPath(profile: string): string {\n return join(tinycloudHomePath(), \"profile-locks\", validateProfileName(profile));\n}\n\n/**\n * Reads JSON without hiding malformed or inaccessible files. A missing file is\n * the only condition represented as null.\n */\nexport async function readJson<T>(filePath: string): Promise<T | null> {\n try {\n return JSON.parse(await readFile(filePath, \"utf8\")) as T;\n } catch (error) {\n if (isErrno(error, \"ENOENT\")) return null;\n throw error;\n }\n}\n\n/**\n * Writes the same pretty-printed, trailing-newline JSON shape used by the CLI,\n * but publishes it with an atomic rename from the same directory.\n */\nexport async function writeJsonAtomic(filePath: string, value: unknown): Promise<void> {\n await replaceJson(filePath, value, false);\n}\n\n/**\n * writeJsonAtomic; `durable` also flushes the data to disk before the rename,\n * so a crash or power loss leaves the old record or the new one, never a\n * truncated one (for lock records, whose damage would stop every writer).\n */\nasync function replaceJson(filePath: string, value: unknown, durable: boolean): Promise<void> {\n const directory = dirname(filePath);\n const temporaryPath = join(\n directory,\n `.${basename(filePath)}.${process.pid}.${randomUUID()}.tmp`,\n );\n const contents = `${JSON.stringify(value, null, 2)}\\n`;\n\n await mkdir(directory, { recursive: true, mode: PRIVATE_DIR_MODE });\n try {\n if (durable) await writeFileDurably(temporaryPath, contents);\n else await writeFile(temporaryPath, contents, { encoding: \"utf8\", mode: PRIVATE_FILE_MODE });\n await rename(temporaryPath, filePath);\n } catch (error) {\n await rm(temporaryPath, { force: true }).catch(() => undefined);\n throw error;\n }\n}\n\n/** Creates a file (exclusively) and, when it has contents, flushes them to disk. */\nasync function writeFileDurably(path: string, contents: string): Promise<void> {\n const handle = await open(path, \"wx\", PRIVATE_FILE_MODE);\n try {\n if (contents) {\n await handle.writeFile(contents, \"utf8\");\n await handle.datasync();\n }\n } finally {\n await handle.close();\n }\n}\n\nexport async function readProfile<T extends object = Record<string, unknown>>(\n profile: string,\n): Promise<T | null> {\n return readJson<T>(profileConfigPath(profile));\n}\n\nexport async function readSession<T extends object = Record<string, unknown>>(\n profile: string,\n): Promise<T | null> {\n await readStoreMetadata(profile, \"session\");\n return readJson<T>(sessionPath(profile));\n}\n\nexport async function readAdditionalDelegations<T = Record<string, unknown>>(\n profile: string,\n): Promise<T[]> {\n return (await readProfileStore<T>(profile, \"additional-delegations\")).records;\n}\n\nexport async function readAuthRequests<T = Record<string, unknown>>(\n profile: string,\n): Promise<T[]> {\n return (await readProfileStore<T>(profile, \"auth-requests\")).records;\n}\n\nexport async function readStoreMetadata(\n profile: string,\n store: ProfileStoreName,\n): Promise<StoreMetadata> {\n const metadata = await readJson<StoreMetadata>(profileStoreMetadataPath(profile, store));\n if (metadata === null) return { formatVersion: 1 };\n if (\n typeof metadata !== \"object\" ||\n metadata.formatVersion !== 1\n ) {\n throw new TypeError(`Unsupported store format for \"${store}\".`);\n }\n return metadata;\n}\n\nexport async function readProfileStore<T>(\n profile: string,\n store: Exclude<ProfileStoreName, \"session\">,\n): Promise<ProfileStoreContents<T>> {\n const [metadata, raw] = await Promise.all([\n readStoreMetadata(profile, store),\n readJson<unknown>(profileStorePath(profile, store)),\n ]);\n return {\n formatVersion: metadata.formatVersion,\n records: Array.isArray(raw) ? raw as T[] : [],\n };\n}\n\n/**\n * Runs a small critical section under the one lock shared by all profile\n * stores (see acquireProfileLock). Lock release verifies its ownership token\n * before removing the exact metadata instance it acquired.\n *\n * Reentrant within one async call chain: a critical section that already\n * holds a profile's lock (for example a login's compare-and-commit) can call\n * store writers that take the same lock without deadlocking. Ownership is\n * keyed by the resolved lock path (so switching state roots inside a lock\n * still takes the other root's lock) and lasts exactly as long as the\n * acquisition: it is revoked before release, so work deferred past the\n * critical section waits for the lock like any other caller.\n */\nexport async function withProfileLock<T>(\n profile: string,\n action: () => Promise<T>,\n options: ProfileLockOptions = {},\n): Promise<T> {\n const normalizedProfile = validateProfileName(profile);\n const lockPath = profileLockPath(normalizedProfile);\n const held = (heldProfileLocks.getStore() ?? []).filter((ownership) => ownership.active);\n if (held.some((ownership) => ownership.lockPath === lockPath)) return action();\n const acquired = await acquireProfileLock(normalizedProfile, options);\n const ownership: HeldProfileLock = {\n lockPath,\n turnSlot: acquired.turnSlot,\n deletedWhileWaiting: acquired.deletedWhileWaiting,\n active: true,\n };\n try {\n return await heldProfileLocks.run([...held, ownership], action);\n } finally {\n ownership.active = false;\n await acquired.release();\n // Taking the lock recreated the deleted profile's directory; remove it\n // again unless something was written into it (rmdir needs it empty).\n if (acquired.deletedWhileWaiting) await rmdir(profilePath(normalizedProfile)).catch(() => undefined);\n }\n}\n\n/**\n * Records, inside a profile deletion's critical section and after its files\n * are gone, that the profile was deleted, with the number of the deleting\n * turn. A store write whose wait overlapped that turn (it arrived before the\n * deletion released the lock, whether before or after this record) then\n * refuses (ProfileDeletedError) instead of recreating a profile with only a\n * session or store in it. The caller must hold the profile's lock.\n */\nexport async function recordProfileDeletion(profile: string): Promise<void> {\n const lockPath = profileLockPath(profile);\n const ownership = (heldProfileLocks.getStore() ?? []).find((candidate) => candidate.active && candidate.lockPath === lockPath);\n if (!ownership) throw new Error(`Recording the deletion of profile \"${profile}\" requires holding its lock.`);\n await replaceJson(join(profileTurnLockPath(profile), \"deleted.json\"), { slot: ownership.turnSlot }, true);\n}\n\n/**\n * Refuses a write that would recreate a profile deleted while this critical\n * section waited for the lock (ProfileDeletedError). Every writer of profile\n * state other than the profile settings themselves calls this inside its\n * critical section, before writing. The caller must hold the profile's lock.\n */\nexport async function refuseWriteToDeletedProfile(profile: string): Promise<void> {\n const lockPath = profileLockPath(profile);\n const ownership = (heldProfileLocks.getStore() ?? []).find((candidate) => candidate.active && candidate.lockPath === lockPath);\n if (!ownership?.deletedWhileWaiting) return;\n // Recreated meanwhile (or by this critical section): writing is fine.\n if (await exists(profileConfigPath(profile))) return;\n throw new ProfileDeletedError(profile);\n}\n\n/**\n * Appends a record or replaces the existing record with the supplied explicit\n * key. The extractor is supplied by the owning store so state.ts never needs\n * to know permission-request or delegation record shapes.\n */\nexport async function upsertProfileRecord<T>(\n profile: string,\n store: Exclude<ProfileStoreName, \"session\">,\n key: string,\n record: T,\n getKey: (candidate: T) => string | undefined,\n options: ProfileLockOptions = {},\n): Promise<T[]> {\n if (!key) throw new TypeError(\"A non-empty record key is required.\");\n\n return withProfileLock(profile, async () => {\n await refuseWriteToDeletedProfile(profile);\n const current = (await readProfileStore<T>(profile, store)).records;\n const next = current.filter((candidate) => getKey(candidate) !== key);\n next.push(record);\n await writeJsonAtomic(profileStorePath(profile, store), next);\n await writeFormatOneMetadata(profile, store);\n return next;\n }, options);\n}\n\n/**\n * Performs a typed read-modify-write of a legacy array store under the one\n * per-profile lock. The payload stays an array and the sibling metadata stays\n * format 1, so existing CLI readers keep their byte/layout contract.\n */\nexport async function updateProfileStore<T, Result>(\n profile: string,\n store: Exclude<ProfileStoreName, \"session\">,\n update: (\n records: readonly T[],\n ) => Promise<{ readonly records: readonly T[]; readonly result: Result }> | {\n readonly records: readonly T[];\n readonly result: Result;\n },\n options: ProfileLockOptions = {},\n): Promise<Result> {\n return withProfileLock(\n profile,\n () => updateProfileStoreWhileLocked(profile, store, update),\n options,\n );\n}\n\n/** The caller must already own this profile's lock. */\nasync function updateProfileStoreWhileLocked<T, Result>(\n profile: string,\n store: Exclude<ProfileStoreName, \"session\">,\n update: (\n records: readonly T[],\n ) => Promise<{ readonly records: readonly T[]; readonly result: Result }> | {\n readonly records: readonly T[];\n readonly result: Result;\n },\n): Promise<Result> {\n await refuseWriteToDeletedProfile(profile);\n const current = await readProfileStore<T>(profile, store);\n const next = await update(current.records);\n await writeJsonAtomic(profileStorePath(profile, store), next.records);\n await writeFormatOneMetadata(profile, store);\n return next.result;\n}\n\nexport async function writeSession<T extends object>(\n profile: string,\n session: T,\n options: ProfileLockOptions = {},\n): Promise<void> {\n await withProfileLock(profile, async () => {\n await refuseWriteToDeletedProfile(profile);\n await readStoreMetadata(profile, \"session\");\n await writeJsonAtomic(sessionPath(profile), session);\n await writeFormatOneMetadata(profile, \"session\");\n }, options);\n}\n\nexport async function removeSession(\n profile: string,\n options: ProfileLockOptions = {},\n): Promise<void> {\n await withProfileLock(profile, async () => {\n await readStoreMetadata(profile, \"session\");\n await rm(sessionPath(profile), { force: true });\n }, options);\n}\n\nasync function writeFormatOneMetadata(profile: string, store: ProfileStoreName): Promise<void> {\n await writeJsonAtomic(profileStoreMetadataPath(profile, store), { formatVersion: 1 });\n}\n\ninterface AcquiredProfileLock {\n readonly deletedWhileWaiting: boolean;\n readonly turnSlot: number;\n release(): Promise<void>;\n}\n\ninterface LockDeadline {\n readonly startedAt: number;\n readonly timeoutMs: number;\n readonly retryMs: number;\n}\n\n/**\n * Takes a profile's lock in two layers:\n *\n * 1. The turn lock (acquireTurn), used by this release and later ones. It\n * decides nothing from elapsed time, so a process paused anywhere, for\n * any length of time, cannot let a second holder in.\n * 2. Holding a turn, the `.lock` directory every older release uses\n * (publishProfileLock), so they keep excluding this release and it them.\n * Only one process of this release at a time acquires, recovers or\n * releases `.lock`.\n *\n * Also reports whether a profile deletion (recordProfileDeletion) ran while\n * this acquisition waited.\n */\nasync function acquireProfileLock(\n profile: string,\n options: ProfileLockOptions,\n): Promise<AcquiredProfileLock> {\n const deadline: LockDeadline = {\n startedAt: Date.now(),\n timeoutMs: options.timeoutMs ?? DEFAULT_LOCK_TIMEOUT_MS,\n retryMs: options.retryMs ?? DEFAULT_LOCK_RETRY_MS,\n };\n const staleAfterMs = options.staleAfterMs ?? DEFAULT_STALE_LOCK_MS;\n const lockPath = profileLockPath(profile);\n const turnDirectory = profileTurnLockPath(profile);\n\n const directory = profilePath(profile);\n await mkdir(directory, { recursive: true, mode: PRIVATE_DIR_MODE });\n await mkdir(dirname(turnDirectory), { recursive: true, mode: PRIVATE_DIR_MODE });\n // Every store write takes this lock: tighten the tree older releases\n // created 0775 before writing sessions or delegations into it.\n for (const path of [tinycloudHomePath(), profilesPath(), directory]) {\n await chmod(path, PRIVATE_DIR_MODE).catch((error: unknown) => {\n // A profile deletion can remove the profile directory right after the\n // mkdir above; the lock attempt below recreates it 0700.\n if (path !== directory || !isErrno(error, \"ENOENT\")) throw error;\n });\n }\n\n const { turn, firstWaitedTurn } = await acquireTurn(profile, turnDirectory, deadline);\n try {\n // An unreadable deletion record is ignored rather than failing every\n // acquisition; it is written durably, so only damage makes it so.\n const deletion = await readJson<{ slot?: unknown }>(join(turnDirectory, \"deleted.json\")).catch(() => null);\n const deletedWhileWaiting = typeof deletion?.slot === \"number\" && deletion.slot >= firstWaitedTurn;\n while (true) {\n const token = randomUUID();\n if (await publishProfileLock(profile, lockPath, token)) {\n return {\n deletedWhileWaiting,\n turnSlot: turn.slot,\n release: async () => {\n try {\n await releaseProfileLock(profile, lockPath, token);\n } finally {\n await releaseTurn(profile, turn);\n }\n },\n };\n }\n await signalTestLockContention(profile);\n if (await recoverStaleLock(profile, lockPath, staleAfterMs)) continue;\n await waitOrTimeOut(profile, deadline);\n }\n } catch (error) {\n await releaseTurn(profile, turn);\n throw error;\n }\n}\n\n/** Throws once the acquisition's time is up; `detail` explains a damaged turn lock. */\nfunction checkDeadline(profile: string, deadline: LockDeadline, detail?: string): void {\n if (Date.now() - deadline.startedAt >= deadline.timeoutMs) {\n throw new ProfileLockTimeoutError(profile, deadline.timeoutMs, detail);\n }\n}\n\n/** Sleeps one retry interval, or throws once the acquisition's time is up. */\nasync function waitOrTimeOut(profile: string, deadline: LockDeadline, detail?: string): Promise<void> {\n checkDeadline(profile, deadline, detail);\n await sleep(Math.min(deadline.retryMs, deadline.timeoutMs - (Date.now() - deadline.startedAt)));\n}\n\n/** One granted turn of the turn lock. */\ninterface Turn {\n readonly directory: string;\n readonly slot: number;\n readonly token: string;\n}\n\n/** A turn as recorded on disk. */\ninterface TurnState {\n readonly slot: number;\n readonly token: string;\n readonly pid: number;\n /** The token of the turn before it that its creator observed; null for turn 0. */\n readonly after: string | null;\n readonly held: boolean;\n readonly done: boolean;\n}\n\n/** Turn directory names: decimal turn numbers. */\nconst TURN = /^(?:0|[1-9][0-9]*)$/;\n\n/**\n * Tokens of turns this process published and then gave up (a marker write or\n * a read failed after publication): the positive evidence that lets this\n * process settle a turn of its own PID. A live process's turn that is not in\n * here is never settled by it, so a module instance that cannot see another\n * instance's set (another bundled copy of this package) waits rather than\n * settling a turn that copy still holds.\n *\n * One set per process, not per module instance: the package's entry points\n * (state, index, artifacts, cli-runtime; ESM and CJS) each bundle their own\n * copy of this module, and one process may load several. The key is\n * versioned so that a release changing what the set holds uses a new one.\n */\nconst relinquishedTurns: Set<string> =\n (globalThis as unknown as Record<symbol, Set<string> | undefined>)[Symbol.for(\"tinycloud.operations.relinquishedProfileTurns.v1\")] ??=\n new Set<string>();\n\n/** Marker writes are retried this many times, after these delays (ms), before giving up. */\nconst MARKER_RETRY_DELAYS_MS = [10, 50, 250];\n/** A given-up turn is settled in the background every second for up to five minutes. */\nconst SETTLE_RETRY_MS = 1_000;\nconst SETTLE_RETRIES = 300;\n\n/**\n * The turn lock: mutual exclusion among processes of this release that does\n * not depend on how long any of them is paused. It uses only `mkdir`,\n * `rename`, exclusive file creation, and a check that a holder's PID is gone\n * (as `.lock` recovery always has), so it needs no hard links.\n *\n * Turns are numbered directories in profileTurnLockPath(profile). Turn n has\n * `owner.json` ({ pid, createdAt, token, after }), where `after` is the token\n * of the turn n-1 its creator observed, and gains `<token>.held` when granted\n * and `<token>.done` when over. Markers name the turn's own token, so one\n * written late into a recreated directory of the same number means nothing.\n *\n * - A turn is published by renaming a fully written staging directory to\n * `n`, which fails while `n` exists (rename never replaces a non-empty\n * directory, and turn directories are never empty). The turn directory\n * itself is published the same way, with a granted, finished turn 0.\n * - A process takes turn n only after reading turn n-1 granted and done.\n * Once `n` is published it checks that turn n-1 is still the one it read\n * (same token, granted, done) and only then writes `held`; otherwise the\n * turn is void: it writes `done` alone and starts over.\n * - Turns are removed by renaming them aside: a void turn at any time, a\n * granted one only once no older turn is left and turn n+1 is granted and\n * done. The latest turn is never removed.\n *\n * Why at most one granted turn is ever not done: a removed number can be\n * published again, but turn n-1 is always removed before turn n, so a\n * process whose stale read led it to republish n finds n-1 gone or holding\n * another token, and its turn is void. A process that finds n-1 as it read\n * it published `n` for the first time, so it is the only process granted n,\n * and n-1 was done before. A turn is marked done only by its holder, or by\n * another process once the holder's PID is gone (or by its own process,\n * once it has explicitly given that turn up); a paused holder is alive.\n * A turn is voided only on definite evidence (the turn it follows is gone or\n * holds another token), never because a read failed. Nothing above is\n * decided from elapsed time.\n *\n * Returns the turn and the number of the first turn this acquisition waited\n * on: the latest turn when it first looked if that was not done yet, else\n * the next one.\n */\nasync function acquireTurn(\n profile: string,\n directory: string,\n deadline: LockDeadline,\n): Promise<{ turn: Turn; firstWaitedTurn: number }> {\n let firstWaitedTurn: number | undefined;\n // Why the turn lock looks damaged, as of the last look, for the timeout message.\n let problem: string | undefined;\n const damaged = (what: string) =>\n `The turn lock ${directory} ${what}, so no process can take it. ` +\n `If no tc or MCP process is using profile \"${profile}\", remove that directory.`;\n for (let first = true; ; first = false) {\n if (!first) checkDeadline(profile, deadline, problem);\n problem = undefined;\n const latest = await readLatestTurn(directory);\n if (latest === \"missing\") {\n await createTurnDirectory(directory);\n continue;\n }\n if (latest === \"empty\") {\n // Usually a listing that straddled a turn's publication and an older\n // turn's removal (readdir is not a snapshot); damaged if it persists.\n problem = damaged(\"has no turns\");\n await waitOrTimeOut(profile, deadline, problem);\n continue;\n }\n if (latest === null) continue;\n if (typeof latest === \"number\") {\n problem = damaged(`has an unreadable turn ${latest}`);\n await waitOrTimeOut(profile, deadline, problem);\n continue;\n }\n firstWaitedTurn ??= latest.done ? latest.slot + 1 : latest.slot;\n if (latest.held && latest.done) {\n const turn = await takeTurn(profile, directory, latest);\n if (turn !== null) return { turn, firstWaitedTurn };\n await waitOrTimeOut(profile, deadline);\n } else if (latest.done) {\n problem = damaged(`ends with turn ${latest.slot}, which was never granted`);\n await waitOrTimeOut(profile, deadline, problem);\n } else if (latest.pid === process.pid ? relinquishedTurns.has(latest.token) : !isProcessAlive(latest.pid)) {\n await waitForTestBarrier(TEST_LOCK_TURN_SETTLE_BARRIER_DIR, profile);\n await settleAbandonedTurn(directory, latest);\n relinquishedTurns.delete(latest.token);\n } else {\n await signalTestLockContention(profile);\n await waitOrTimeOut(profile, deadline);\n }\n }\n}\n\n/**\n * The highest-numbered turn: its state, its number if it is damaged, null if\n * it went away while being read.\n */\nasync function readLatestTurn(directory: string): Promise<TurnState | number | \"missing\" | \"empty\" | null> {\n let names: string[];\n try {\n names = await readdir(directory);\n } catch (error) {\n if (isErrno(error, \"ENOENT\")) return \"missing\";\n throw error;\n }\n const slots = names.filter((name) => TURN.test(name)).map(Number);\n if (slots.length === 0) return \"empty\";\n const slot = Math.max(...slots);\n const turn = await readTurn(directory, slot);\n return turn === \"damaged\" ? slot : turn;\n}\n\n/**\n * Turn `slot` as recorded now; null if it is gone; \"damaged\" if its owner\n * record is missing or unreadable (turns are renamed into place and aside\n * whole, with their owner record written durably). Any other read error is\n * thrown: it says nothing about the turn. `done` is read before `held`,\n * which every writer creates first, so a turn read as done is never misread\n * as void.\n */\nasync function readTurn(directory: string, slot: number): Promise<TurnState | \"damaged\" | null> {\n const path = join(directory, String(slot));\n let text: string;\n try {\n text = await readFile(join(path, \"owner.json\"), \"utf8\");\n } catch (error) {\n if (!isErrno(error, \"ENOENT\") && !isErrno(error, \"ENOTDIR\")) throw error;\n return await exists(path) ? \"damaged\" : null;\n }\n let owner: { pid?: unknown; token?: unknown; after?: unknown } | null;\n try {\n owner = JSON.parse(text) as typeof owner;\n } catch {\n return \"damaged\";\n }\n if (owner === null || typeof owner !== \"object\" || typeof owner.token !== \"string\" || typeof owner.pid !== \"number\") {\n return \"damaged\";\n }\n const done = await exists(join(path, `${owner.token}.done`));\n const held = await exists(join(path, `${owner.token}.held`));\n return {\n slot,\n token: owner.token,\n pid: owner.pid,\n after: typeof owner.after === \"string\" ? owner.after : null,\n held,\n done,\n };\n}\n\n/**\n * Whether turn `slot` is still the granted, finished turn with `token`. False\n * only on definite evidence: it is gone, or holds another token. Throws if it\n * cannot be read, or is damaged, so the caller leaves its own turn undecided.\n */\nasync function isFinishedTurn(directory: string, slot: number, token: string): Promise<boolean> {\n const turn = await readTurn(directory, slot);\n if (turn === \"damaged\") throw new Error(`Turn ${slot} of the turn lock ${directory} is unreadable.`);\n return turn !== null && turn.token === token && turn.held && turn.done;\n}\n\n/**\n * Publishes the turn after `latest` and claims it. Null if another process\n * published that number first, or the turn is void. If a step after\n * publication fails, the turn is handed to settleOwnTurn and the error thrown.\n */\nasync function takeTurn(profile: string, directory: string, latest: TurnState): Promise<Turn | null> {\n const token = randomUUID();\n const slot = latest.slot + 1;\n const path = join(directory, String(slot));\n const owner = { pid: process.pid, createdAt: new Date().toISOString(), token, after: latest.token };\n let published = false;\n try {\n published = await publishDirectory(\n join(directory, `.stage-${process.pid}-${token}`),\n path,\n { \"owner.json\": `${JSON.stringify(owner, null, 2)}\\n` },\n () => waitForTestBarrier(TEST_LOCK_TURN_PUBLISH_BARRIER_DIR, profile),\n );\n if (published && await isFinishedTurn(directory, latest.slot, latest.token) && await markTurn(path, token, \"held\")) {\n return { directory, slot, token };\n }\n if (published) await markTurn(path, token, \"done\");\n return null;\n } catch (error) {\n if (published) settleOwnTurn(directory, slot, token);\n throw error;\n }\n}\n\n/**\n * Finishes the turn of a process that is gone (or one this process gave up).\n * A turn its creator never got to grant is granted first if the turn before\n * it is still the one its creator read (the check the creator would have\n * made; that turn cannot have been removed, since that needs this turn\n * done), and left void only if it definitely is not.\n */\nasync function settleAbandonedTurn(directory: string, abandoned: TurnState): Promise<void> {\n const path = join(directory, String(abandoned.slot));\n if (!abandoned.held && abandoned.after !== null &&\n await isFinishedTurn(directory, abandoned.slot - 1, abandoned.after)) {\n await markTurn(path, abandoned.token, \"held\");\n }\n await markTurn(path, abandoned.token, \"done\");\n}\n\n/**\n * Settles a turn this process published but could not grant or finish (a\n * marker write or a read failed). Other processes wait on this live PID, so\n * it is retried in the background until it succeeds or the turn is settled\n * otherwise (by this process's next acquisition, or by another process once\n * this one has exited). The timer does not keep the process alive.\n */\nfunction settleOwnTurn(directory: string, slot: number, token: string, retries = SETTLE_RETRIES): void {\n relinquishedTurns.add(token);\n void (async () => {\n try {\n const turn = await readTurn(directory, slot);\n if (turn !== null && turn !== \"damaged\" && turn.token === token && !turn.done) await settleAbandonedTurn(directory, turn);\n relinquishedTurns.delete(token);\n } catch {\n // Still relinquished: this process's next acquisition settles it too.\n if (retries > 0) setTimeout(() => settleOwnTurn(directory, slot, token, retries - 1), SETTLE_RETRY_MS).unref();\n }\n })();\n}\n\n/**\n * Creates `<token>.<kind>` in a turn directory; false if the directory is\n * gone. Other failures (a full disk, a permission error) are retried a few\n * times, then thrown.\n */\nasync function markTurn(path: string, token: string, kind: \"held\" | \"done\"): Promise<boolean> {\n for (let attempt = 0; ; attempt++) {\n try {\n await writeFile(join(path, `${token}.${kind}`), \"\", { mode: PRIVATE_FILE_MODE, flag: \"wx\" });\n return true;\n } catch (error) {\n if (isErrno(error, \"EEXIST\")) return true;\n if (isErrno(error, \"ENOENT\")) return false;\n if (attempt >= MARKER_RETRY_DELAYS_MS.length) throw error;\n await sleep(MARKER_RETRY_DELAYS_MS[attempt]!);\n }\n }\n}\n\n/** Creates the turn directory with a granted, finished turn 0, unless another process did. */\nasync function createTurnDirectory(directory: string): Promise<void> {\n const token = randomUUID();\n const owner = { pid: process.pid, createdAt: new Date().toISOString(), token, after: null };\n await publishDirectory(join(dirname(directory), `.stage-${basename(directory)}-${process.pid}-${token}`), directory, {\n \"0/owner.json\": `${JSON.stringify(owner, null, 2)}\\n`,\n [`0/${token}.held`]: \"\",\n [`0/${token}.done`]: \"\",\n });\n}\n\n/**\n * Writes `files` (durably, when not empty) into a new staging directory and\n * renames it to `target`. False if `target` already exists (or the turn\n * directory is gone); the caller starts over. Nothing else removes a live\n * process's staging directory (see collectTurns), so `target` never appears\n * incomplete.\n */\nasync function publishDirectory(\n stage: string,\n target: string,\n files: Readonly<Record<string, string>>,\n beforeRename?: () => Promise<void>,\n): Promise<boolean> {\n try {\n for (const [name, contents] of Object.entries(files)) {\n await mkdir(dirname(join(stage, name)), { recursive: true, mode: PRIVATE_DIR_MODE });\n await writeFileDurably(join(stage, name), contents);\n }\n await beforeRename?.();\n await rename(stage, target);\n return true;\n } catch (error) {\n // ENOTEMPTY/EEXIST: `target` exists, though it may be gone again by now.\n // Windows reports an existing target directory as EPERM.\n if ([\"ENOTEMPTY\", \"EEXIST\", \"ENOENT\"].some((code) => isErrno(error, code)) || await exists(target)) return false;\n throw error;\n } finally {\n await rm(stage, { recursive: true, force: true }).catch(() => undefined);\n }\n}\n\n/**\n * Finishes this process's turn. If `done` cannot be written even after\n * retries, the critical section is still over and its writes stand: the turn\n * is handed to settleOwnTurn rather than failing the caller.\n */\nasync function releaseTurn(profile: string, turn: Turn): Promise<void> {\n try {\n await markTurn(join(turn.directory, String(turn.slot)), turn.token, \"done\");\n } catch {\n settleOwnTurn(turn.directory, turn.slot, turn.token);\n return;\n }\n await collectTurns(profile, turn).catch(() => undefined);\n}\n\n/**\n * Leftovers in a turn directory whose writer is gone: trash (a removed turn\n * renamed aside, garbage from then on), and staging directories and\n * deletion-record temporaries named after their writer's PID.\n */\nfunction isAbandonedTurnEntry(name: string): boolean {\n if (name.startsWith(\".trash-\")) return true;\n const writer = /^\\.(?:stage|deleted\\.json)[-.]([0-9]+)[-.]/.exec(name)?.[1];\n return writer !== undefined && !isProcessAlive(Number(writer));\n}\n\n/** A turn directory's staging directory left beside it by a process that is gone. */\nfunction isAbandonedTurnDirectoryStage(name: string): boolean {\n const writer = /^\\.stage-.+-([0-9]+)-[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/.exec(name)?.[1];\n return writer !== undefined && !isProcessAlive(Number(writer));\n}\n\n/**\n * Removes the turns before `turn`, which just finished: void turns, and\n * granted ones oldest first, each once no older turn is left and the next is\n * granted and done (see acquireTurn). A turn is renamed aside before it is\n * deleted, so its number is never an empty directory. A turn that is damaged\n * or cannot be read is left alone, and a read error stops the collection.\n * Leftovers of crashed processes go too, judged by PID, never by age:\n * removing a paused process's staging directory could publish an incomplete\n * turn.\n */\nasync function collectTurns(profile: string, turn: Turn): Promise<void> {\n for (const [directory, abandoned] of [\n [turn.directory, isAbandonedTurnEntry],\n [dirname(turn.directory), isAbandonedTurnDirectoryStage],\n ] as const) {\n for (const name of (await readdir(directory)).filter(abandoned)) {\n await rm(join(directory, name), { recursive: true, force: true }).catch(() => undefined);\n }\n }\n const older = (await readdir(turn.directory)).filter((name) => TURN.test(name)).map(Number)\n .filter((slot) => slot < turn.slot)\n .sort((left, right) => left - right);\n for (const slot of older) {\n const candidate = await readTurn(turn.directory, slot);\n if (candidate === null || candidate === \"damaged\" || !candidate.done) continue;\n if (candidate.held) {\n if (await exists(join(turn.directory, String(slot - 1)))) continue;\n const next = await readTurn(turn.directory, slot + 1);\n if (next === null || next === \"damaged\" || !next.held || !next.done) continue;\n }\n await waitForTestBarrier(TEST_LOCK_TURN_COLLECT_BARRIER_DIR, profile);\n const trash = join(turn.directory, `.trash-${randomUUID()}`);\n if (await rename(join(turn.directory, String(slot)), trash).then(() => true, () => false)) {\n await rm(trash, { recursive: true, force: true }).catch(() => undefined);\n }\n }\n}\n\n/**\n * One attempt to take `.lock`, made while holding a turn. Nothing here\n * replaces an existing path:\n *\n * 1. `mkdir(.lock)` (never recursive) is exclusive, and is the primitive\n * older releases use too, so they exclude each other.\n * 2. Ownership is published by `link`ing a fully written owner file (staged\n * beside `.lock`, on the same filesystem) to `.lock/owner.json`. `link`\n * fails with EEXIST if an owner exists, and with ENOENT if the directory\n * was removed meanwhile (an aged ownerless-lock reclaim by an older\n * release). Only a successful link holds the lock; on either failure\n * nothing is released, since nothing was acquired, and the caller retries.\n * On a filesystem without hard links (FAT/exFAT, some SMB mounts) the\n * owner file is created in place with an exclusive create instead, with\n * the same EEXIST/ENOENT outcomes. A reader may then briefly see it\n * incomplete; every release treats an owner file it cannot parse as a\n * held lock, and this release removes one only once it has aged (see\n * isAbandonedOwner).\n *\n * A profile deletion removes the profile directory once it has released its\n * lock, so `mkdir(.lock)` can see ENOENT; the directory is recreated (0700)\n * and the caller retries within its deadline, as for a first write to a new\n * profile.\n *\n * Only one owner.json can exist at the lock path, and it is removed only by\n * its holder's release or by recovery of an abandoned holder, so at most one\n * process holds the lock. Returns false when the lock is not acquired.\n */\nasync function publishProfileLock(profile: string, lockPath: string, token: string): Promise<boolean> {\n try {\n await mkdir(lockPath, { mode: PRIVATE_DIR_MODE });\n } catch (error) {\n if (isErrno(error, \"EEXIST\")) return false;\n if (!isErrno(error, \"ENOENT\")) throw error;\n await mkdir(profilePath(profile), { recursive: true, mode: PRIVATE_DIR_MODE });\n return false;\n }\n await waitForTestBarrier(TEST_LOCK_PUBLISH_BARRIER_DIR, profile);\n const ownerPath = join(lockPath, \"owner.json\");\n const staged = join(dirname(lockPath), `.lock-owner-${token}.tmp`);\n const owner = `${JSON.stringify({ pid: process.pid, createdAt: new Date().toISOString(), token }, null, 2)}\\n`;\n try {\n await writeFile(staged, owner, { encoding: \"utf8\", mode: PRIVATE_FILE_MODE, flag: \"wx\" });\n try {\n await link(staged, ownerPath);\n } catch (error) {\n if (!lacksHardLinks(error)) throw error;\n await writeFile(ownerPath, owner, { encoding: \"utf8\", mode: PRIVATE_FILE_MODE, flag: \"wx\" });\n }\n return true;\n } catch (error) {\n if (isErrno(error, \"EEXIST\") || isErrno(error, \"ENOENT\")) return false;\n // Do not leave an ownerless lock behind for others to wait out. rmdir\n // removes only an empty directory; anyone who created it after ours went\n // away sees ENOENT on link and retries.\n await rmdir(lockPath).catch(() => undefined);\n throw error;\n } finally {\n await rm(staged, { force: true }).catch(() => undefined);\n }\n}\n\n/**\n * Errors `link` reports on a filesystem without hard links: EPERM (Linux\n * FAT/exFAT), ENOTSUP/EOPNOTSUPP (macOS FAT/exFAT, SMB), ENOSYS, EISDIR\n * (libuv's code for Windows' ERROR_INVALID_FUNCTION on FAT32), and EMLINK\n * (too many links), for which the same fallback is just as correct.\n */\nconst NO_HARD_LINK_ERRORS = new Set([\"EPERM\", \"ENOTSUP\", \"EOPNOTSUPP\", \"ENOSYS\", \"EISDIR\", \"EMLINK\"]);\n\nfunction lacksHardLinks(error: unknown): boolean {\n return typeof error === \"object\" && error !== null && \"code\" in error &&\n NO_HARD_LINK_ERRORS.has(String((error as { code?: unknown }).code));\n}\n\n/**\n * Releases the lock if `.lock/owner.json` is still this acquisition's. No\n * one else can put an owner record there while it exists (owners are only\n * published into a directory their own `mkdir` created, and recovery\n * removes only an abandoned holder's record), so the record read is the one\n * unlinked.\n */\nasync function releaseProfileLock(profile: string, lockPath: string, token: string): Promise<void> {\n const ownerPath = join(lockPath, \"owner.json\");\n const owner = await readJson<{ token?: unknown }>(ownerPath).catch(() => null);\n if (owner?.token !== token) return;\n await rm(ownerPath, { force: true });\n // `.lock` is empty from here until the rmdir; a contender that removes it\n // and creates its own meanwhile is not harmed: rmdir fails on its owner\n // file, or the contender's link sees ENOENT and retries. If a recoverer's\n // claim file is still inside, rmdir fails and that recoverer removes the\n // directory when it drops its claim.\n await waitForTestBarrier(TEST_LOCK_RELEASE_BARRIER_DIR, profile);\n await rmdir(lockPath).catch(() => undefined);\n}\n\n/**\n * Gives process-level contention tests an event-driven witness that a second\n * writer has reached an already-held lock. This deliberately has no effect\n * outside an explicit test environment, and only creates a new signal file\n * beneath TC_HOME; it never changes lock acquisition or release semantics.\n */\nasync function signalTestLockContention(profile: string): Promise<void> {\n if (process.env.NODE_ENV !== \"test\") return;\n\n const configuredPath = process.env[TEST_LOCK_CONTENTION_SIGNAL_PATH];\n const configuredHome = process.env.TC_HOME;\n if (!configuredPath || !configuredHome) return;\n\n const home = resolve(configuredHome);\n const signalPath = resolve(configuredPath);\n const pathFromHome = relative(home, signalPath);\n if (\n pathFromHome === \"\" ||\n pathFromHome === \"..\" ||\n pathFromHome.startsWith(`..${process.platform === \"win32\" ? \"\\\\\" : \"/\"}`) ||\n pathFromHome.startsWith(\"../\") ||\n pathFromHome.startsWith(\"..\\\\\")\n ) {\n return;\n }\n\n await writeFile(signalPath, `${profile}\\n`, { encoding: \"utf8\", flag: \"wx\" })\n .catch(() => undefined);\n}\n\n/**\n * Reclaims a `.lock` with no owner record, while holding a turn. A\n * `.recover-*` claim may contain a live legacy owner's record moved by a\n * no-hard-links recoverer just before it crashed. Restore that record first;\n * only orphaned claims holding dead or malformed records can be removed.\n * Otherwise the directory must be older than the stale threshold: one left\n * by a crash, or by an older release, whose `.stale-*` / `.release-*` claim\n * files are removed too. owner.json is never unlinked, and the directory\n * itself is only `rmdir`ed, which succeeds only while it is empty.\n *\n * Holding a turn, no other process of this release reclaims concurrently.\n * That closes the race in which two reclaimers' age checks let the later one\n * remove a directory a 1.0.0-beta.16-or-older release had just created,\n * which holds the lock from its `mkdir`. That race remains only if a\n * 1.0.0-beta.17 … 1.0.1-beta.4 release reclaims at the same moment, or the\n * older process stays paused between its `mkdir` and its owner write for\n * longer than the stale threshold; recoveryFenced bounds how stale this\n * process's age check can be.\n */\nasync function recoverOwnerlessLock(profile: string, lockPath: string, staleAfterMs: number): Promise<boolean> {\n let aged: boolean;\n try {\n const { mtimeMs } = await stat(lockPath);\n aged = Date.now() - mtimeMs >= staleAfterMs;\n } catch {\n return false;\n }\n const checkedAt = performance.now();\n await waitForTestBarrier(TEST_LOCK_OWNERLESS_BARRIER_DIR, profile);\n try {\n const entries = await readdir(lockPath);\n if (entries.includes(\"owner.json\")) return false;\n const onlyOrphanedClaims = entries.length > 0 && entries.every((entry) => RECOVERY_CLAIM.test(entry));\n if (!onlyOrphanedClaims && (!aged || recoveryFenced(checkedAt, staleAfterMs))) return false;\n for (const name of entries.filter((entry) => RECOVERY_CLAIM.test(entry))) {\n const claimPath = join(lockPath, name);\n if (await hasLiveRecoveryOwner(claimPath)) {\n // The claim keeps the directory in place. Only the original mkdir\n // owner could publish owner.json here; never overwrite that record.\n if (!await exists(join(lockPath, \"owner.json\"))) await rename(claimPath, join(lockPath, \"owner.json\"));\n return false;\n }\n await rm(claimPath, { force: true });\n }\n for (const name of entries.filter((entry) => ABANDONED_CLAIM.test(entry))) {\n await rm(join(lockPath, name), { force: true });\n }\n await rmdir(lockPath);\n } catch {\n return false;\n }\n await removeAgedOwnerFiles(lockPath, staleAfterMs);\n return true;\n}\n\n/** A no-hard-links claim may contain a live owner's record moved before a crash. */\nasync function hasLiveRecoveryOwner(claimPath: string): Promise<boolean> {\n let contents: string;\n try {\n contents = await readFile(claimPath, \"utf8\");\n } catch (error) {\n if (isErrno(error, \"ENOENT\")) return false;\n throw error; // An I/O failure is not evidence that the holder is gone.\n }\n let owner: unknown;\n try {\n owner = JSON.parse(contents);\n } catch {\n return false;\n }\n const pid = typeof owner === \"object\" && owner !== null && \"pid\" in owner ? owner.pid : null;\n return typeof pid === \"number\" && Number.isInteger(pid) && pid > 0 && isProcessAlive(pid);\n}\n\n/**\n * Whether an ownerless-reclaim decision made at `since` (a monotonic\n * timestamp) is too old to act on. The age check is only good for a while:\n * past half the stale threshold the reclaim starts over.\n */\nfunction recoveryFenced(since: number, staleAfterMs: number): boolean {\n return performance.now() - since >= staleAfterMs / 2;\n}\n\n/** Claim files of older releases' stale recovery and (up to 1.0.0-beta.16) release. */\nconst ABANDONED_CLAIM = /^\\.(?:release|stale)-[0-9a-f-]+\\.json$/;\n/**\n * Claim files of this release's stale recovery. Releases up to 1.0.1-beta.4\n * never remove them, which is what makes this recovery safe against them; a\n * recoverer that crashes leaves one that only a release with the turn lock\n * clears (see REFERENCE.md, \"Profile lock\").\n */\nconst RECOVERY_CLAIM = /^\\.recover-[0-9a-f-]+\\.json$/;\n/** Owner files staged by publishProfileLock beside `.lock`. */\nconst STAGED_OWNER = /^\\.lock-owner-[0-9a-f-]+\\.tmp$/;\n/**\n * Removes owner files a crashed acquirer staged beside `.lock`. A live\n * acquirer's staged file exists only for its write and link, so only files\n * older than the stale threshold are removed. Runs after a recovery, never\n * on the uncontended path.\n */\nasync function removeAgedOwnerFiles(lockPath: string, staleAfterMs: number): Promise<void> {\n const directory = dirname(lockPath);\n const names = await readdir(directory).catch(() => []);\n for (const name of names.filter((entry) => STAGED_OWNER.test(entry))) {\n const path = join(directory, name);\n const aged = await stat(path).then(({ mtimeMs }) => Date.now() - mtimeMs >= staleAfterMs, () => false);\n if (aged) await rm(path, { force: true }).catch(() => undefined);\n }\n}\n\n\n/**\n * Removes an abandoned owner record (see isAbandonedOwner) and its `.lock`,\n * while holding a turn, so no other process of this release recovers,\n * reclaims or acquires `.lock` meanwhile.\n *\n * The record is claimed first: hard-linked to `.recover-<uuid>.json`, and the\n * claim compared with the exact bytes read. A record that replaced the\n * observed one is left in place. While the claim is inside `.lock`, the\n * directory cannot be removed and recreated: older releases never remove a\n * `.recover-*` claim (they clean up only their own claim names), and other\n * processes of this release are kept out by the turn lock. So no other owner\n * record can appear in it, and the unlink of owner.json can only remove the\n * claimed, dead record. A live record is never unlinked or moved. `.lock`\n * itself is removed only after this process's claim is unlinked from it,\n * which proves it is still the dead holder's directory; if the claim cannot\n * be made, `.lock` is left alone (it may be a directory a 1.0.0-beta.16-or-\n * older writer just created, which it holds from that `mkdir`). No step\n * depends on how long this process takes.\n *\n * Without hard links (where 1.0.0-beta.17 … 1.0.1-beta.4 cannot run at all),\n * the record is renamed into the claim instead, and put back if it is not\n * the observed one: one a 1.0.0-beta.16-or-older process published after\n * recovering the same lock at the same moment. The claim keeps `.lock` held\n * meanwhile, so no second writer gets in, but if that holder releases\n * between the two renames, the put-back restores a released record, and\n * `.lock` stays held until that PID is gone and the record is 30 s old.\n */\nasync function recoverStaleLock(profile: string, lockPath: string, staleAfterMs: number): Promise<boolean> {\n const ownerPath = join(lockPath, \"owner.json\");\n const observed = await readFile(ownerPath, \"utf8\").catch(() => null);\n if (observed === null) return recoverOwnerlessLock(profile, lockPath, staleAfterMs);\n if (!await isAbandonedOwner(observed, ownerPath, staleAfterMs)) return false;\n\n await waitForTestBarrier(TEST_LOCK_RECOVERY_BARRIER_DIR, profile);\n const claimPath = join(lockPath, `.recover-${randomUUID()}.json`);\n let moved = false;\n try {\n await link(ownerPath, claimPath);\n } catch (error) {\n // Gone or replaced already (another release recovered it): leave `.lock`.\n if (!lacksHardLinks(error)) return false;\n try {\n await rename(ownerPath, claimPath);\n moved = true;\n } catch {\n return false;\n }\n }\n if (moved) await waitForTestBarrier(TEST_LOCK_MOVED_BARRIER_DIR, profile);\n const sameInstance = await readFile(claimPath, \"utf8\").then((claimed) => claimed === observed, () => false);\n await waitForTestBarrier(TEST_LOCK_VERIFIED_BARRIER_DIR, profile);\n if (!sameInstance) {\n if (moved) {\n // No other owner.json can have appeared meanwhile: only a process\n // whose mkdir created this `.lock` publishes one, and it already has.\n await rename(claimPath, ownerPath).catch(() => undefined);\n } else {\n await unlink(claimPath).catch(() => undefined);\n }\n return false;\n }\n if (!moved) await rm(ownerPath, { force: true });\n await waitForTestBarrier(TEST_LOCK_CLAIM_BARRIER_DIR, profile);\n await waitForTestBarrier(TEST_LOCK_CLAIMED_BARRIER_DIR, profile);\n // Drop any claim a crashed recoverer of this release left, then this one.\n for (const name of (await readdir(lockPath).catch(() => [])).filter((entry) => RECOVERY_CLAIM.test(entry))) {\n if (join(lockPath, name) !== claimPath) await rm(join(lockPath, name), { force: true });\n }\n // Empty now, and still the dead holder's directory if this claim was in\n // it: remove it rather than leave an ownerless lock to age out.\n if (await unlink(claimPath).then(() => true, () => false)) await rmdir(lockPath).catch(() => undefined);\n await removeAgedOwnerFiles(lockPath, staleAfterMs);\n return true;\n}\n\n/**\n * Whether an owner record (its exact bytes, read from `ownerPath`) is\n * abandoned: a holder's whose PID is gone, older than the stale threshold;\n * or, if it is not a complete record, a file older than that threshold.\n * Every release publishes complete records (by link or rename) except this\n * one's no-hard-link fallback, which writes in place while holding a turn;\n * so an incomplete record seen while holding a turn was left by a crash.\n */\nasync function isAbandonedOwner(observed: string, ownerPath: string, staleAfterMs: number): Promise<boolean> {\n let owner: unknown;\n try {\n owner = JSON.parse(observed);\n } catch {\n owner = null;\n }\n if (typeof owner === \"object\" && owner !== null && \"pid\" in owner) {\n return isStaleOwner(owner as { pid?: unknown; createdAt?: unknown }, staleAfterMs);\n }\n const modifiedAt = await stat(ownerPath).then(({ mtimeMs }) => mtimeMs, () => Number.NaN);\n return Date.now() - modifiedAt >= staleAfterMs;\n}\n\n/** Test-only rendezvous (NODE_ENV=test and the named barrier directory set). */\nasync function waitForTestBarrier(environmentName: string, profile: string): Promise<void> {\n if (process.env.NODE_ENV !== \"test\") return;\n const barrierDirectory = process.env[environmentName];\n if (!barrierDirectory) return;\n await mkdir(barrierDirectory, { recursive: true });\n const readyPath = join(barrierDirectory, `ready-${process.pid}-${profile}`);\n await writeFile(readyPath, \"ready\\n\", { encoding: \"utf8\", flag: \"wx\" }).catch(() => undefined);\n const releasePath = join(barrierDirectory, \"release\");\n while (true) {\n try {\n await readFile(releasePath, \"utf8\");\n return;\n } catch {\n await sleep(1);\n }\n }\n}\n\nfunction isStaleOwner(\n owner: { pid?: unknown; createdAt?: unknown } | null,\n staleAfterMs: number,\n): boolean {\n const now = Date.now();\n const createdAt = typeof owner?.createdAt === \"string\"\n ? Date.parse(owner.createdAt)\n : Number.NaN;\n\n // Reclaim only a fully published lock whose owner PID is confirmed dead.\n // A lock without an owner record is handled by recoverOwnerlessLock: a\n // process holds the lock only once its owner.json is linked in.\n if (!Number.isFinite(createdAt) || now - createdAt < staleAfterMs) return false;\n if (typeof owner?.pid !== \"number\" || !Number.isInteger(owner.pid) || owner.pid <= 0) {\n return false;\n }\n return !isProcessAlive(owner.pid);\n}\n\n/** Whether `path` exists. Only ENOENT/ENOTDIR mean it does not; other errors are thrown. */\nasync function exists(path: string): Promise<boolean> {\n try {\n await stat(path);\n return true;\n } catch (error) {\n if (isErrno(error, \"ENOENT\") || isErrno(error, \"ENOTDIR\")) return false;\n throw error;\n }\n}\n\nfunction isProcessAlive(pid: number): boolean {\n try {\n process.kill(pid, 0);\n return true;\n } catch (error) {\n return !isErrno(error, \"ESRCH\");\n }\n}\n\nfunction validateProfileName(profile: string): string {\n if (\n !profile ||\n profile === \".\" ||\n profile === \"..\" ||\n profile.includes(\"/\") ||\n profile.includes(\"\\\\\") ||\n profile.includes(\"\\0\")\n ) {\n throw new TypeError(\"Profile names must be non-empty path segments.\");\n }\n return profile;\n}\n\nfunction isErrno(error: unknown, code: string): boolean {\n return typeof error === \"object\" && error !== null && \"code\" in error &&\n (error as { code?: unknown }).code === code;\n}\n\nfunction sleep(milliseconds: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, milliseconds));\n}\n\nexport { DEFAULT_PROFILE };\n"],"mappings":"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","names":["_a","ownership","resolve"]}
|
|
@@ -7,6 +7,8 @@
|
|
|
7
7
|
"PROFILE_NOT_FOUND",
|
|
8
8
|
"PROFILE_POSTURE_NOT_ALLOWED",
|
|
9
9
|
"PROFILE_OWNER_OPT_IN_REQUIRED",
|
|
10
|
+
"AUTH_REQUIRED",
|
|
11
|
+
"PERMISSION_DENIED",
|
|
10
12
|
"SESSION_NOT_FOUND",
|
|
11
13
|
"PERMISSION_HINT_INVALID",
|
|
12
14
|
"DELEGATION_ARTIFACT_INVALID",
|
|
@@ -586,6 +588,8 @@
|
|
|
586
588
|
"PROFILE_NOT_FOUND",
|
|
587
589
|
"PROFILE_POSTURE_NOT_ALLOWED",
|
|
588
590
|
"PROFILE_OWNER_OPT_IN_REQUIRED",
|
|
591
|
+
"AUTH_REQUIRED",
|
|
592
|
+
"PERMISSION_DENIED",
|
|
589
593
|
"SESSION_NOT_FOUND",
|
|
590
594
|
"PERMISSION_HINT_INVALID",
|
|
591
595
|
"DELEGATION_ARTIFACT_INVALID",
|
|
@@ -928,6 +932,8 @@
|
|
|
928
932
|
"PROFILE_NOT_FOUND",
|
|
929
933
|
"PROFILE_POSTURE_NOT_ALLOWED",
|
|
930
934
|
"PROFILE_OWNER_OPT_IN_REQUIRED",
|
|
935
|
+
"AUTH_REQUIRED",
|
|
936
|
+
"PERMISSION_DENIED",
|
|
931
937
|
"SESSION_NOT_FOUND",
|
|
932
938
|
"PERMISSION_HINT_INVALID",
|
|
933
939
|
"DELEGATION_ARTIFACT_INVALID",
|
|
@@ -1533,6 +1539,8 @@
|
|
|
1533
1539
|
"PROFILE_NOT_FOUND",
|
|
1534
1540
|
"PROFILE_POSTURE_NOT_ALLOWED",
|
|
1535
1541
|
"PROFILE_OWNER_OPT_IN_REQUIRED",
|
|
1542
|
+
"AUTH_REQUIRED",
|
|
1543
|
+
"PERMISSION_DENIED",
|
|
1536
1544
|
"SESSION_NOT_FOUND",
|
|
1537
1545
|
"PERMISSION_HINT_INVALID",
|
|
1538
1546
|
"DELEGATION_ARTIFACT_INVALID",
|
|
@@ -1898,6 +1906,8 @@
|
|
|
1898
1906
|
"PROFILE_NOT_FOUND",
|
|
1899
1907
|
"PROFILE_POSTURE_NOT_ALLOWED",
|
|
1900
1908
|
"PROFILE_OWNER_OPT_IN_REQUIRED",
|
|
1909
|
+
"AUTH_REQUIRED",
|
|
1910
|
+
"PERMISSION_DENIED",
|
|
1901
1911
|
"SESSION_NOT_FOUND",
|
|
1902
1912
|
"PERMISSION_HINT_INVALID",
|
|
1903
1913
|
"DELEGATION_ARTIFACT_INVALID",
|
|
@@ -2489,6 +2499,8 @@
|
|
|
2489
2499
|
"PROFILE_NOT_FOUND",
|
|
2490
2500
|
"PROFILE_POSTURE_NOT_ALLOWED",
|
|
2491
2501
|
"PROFILE_OWNER_OPT_IN_REQUIRED",
|
|
2502
|
+
"AUTH_REQUIRED",
|
|
2503
|
+
"PERMISSION_DENIED",
|
|
2492
2504
|
"SESSION_NOT_FOUND",
|
|
2493
2505
|
"PERMISSION_HINT_INVALID",
|
|
2494
2506
|
"DELEGATION_ARTIFACT_INVALID",
|
|
@@ -2839,6 +2851,8 @@
|
|
|
2839
2851
|
"PROFILE_NOT_FOUND",
|
|
2840
2852
|
"PROFILE_POSTURE_NOT_ALLOWED",
|
|
2841
2853
|
"PROFILE_OWNER_OPT_IN_REQUIRED",
|
|
2854
|
+
"AUTH_REQUIRED",
|
|
2855
|
+
"PERMISSION_DENIED",
|
|
2842
2856
|
"SESSION_NOT_FOUND",
|
|
2843
2857
|
"PERMISSION_HINT_INVALID",
|
|
2844
2858
|
"DELEGATION_ARTIFACT_INVALID",
|
|
@@ -3628,6 +3642,8 @@
|
|
|
3628
3642
|
"PROFILE_NOT_FOUND",
|
|
3629
3643
|
"PROFILE_POSTURE_NOT_ALLOWED",
|
|
3630
3644
|
"PROFILE_OWNER_OPT_IN_REQUIRED",
|
|
3645
|
+
"AUTH_REQUIRED",
|
|
3646
|
+
"PERMISSION_DENIED",
|
|
3631
3647
|
"SESSION_NOT_FOUND",
|
|
3632
3648
|
"PERMISSION_HINT_INVALID",
|
|
3633
3649
|
"DELEGATION_ARTIFACT_INVALID",
|
|
@@ -4007,6 +4023,8 @@
|
|
|
4007
4023
|
"PROFILE_NOT_FOUND",
|
|
4008
4024
|
"PROFILE_POSTURE_NOT_ALLOWED",
|
|
4009
4025
|
"PROFILE_OWNER_OPT_IN_REQUIRED",
|
|
4026
|
+
"AUTH_REQUIRED",
|
|
4027
|
+
"PERMISSION_DENIED",
|
|
4010
4028
|
"SESSION_NOT_FOUND",
|
|
4011
4029
|
"PERMISSION_HINT_INVALID",
|
|
4012
4030
|
"DELEGATION_ARTIFACT_INVALID",
|
|
@@ -4734,6 +4752,8 @@
|
|
|
4734
4752
|
"PROFILE_NOT_FOUND",
|
|
4735
4753
|
"PROFILE_POSTURE_NOT_ALLOWED",
|
|
4736
4754
|
"PROFILE_OWNER_OPT_IN_REQUIRED",
|
|
4755
|
+
"AUTH_REQUIRED",
|
|
4756
|
+
"PERMISSION_DENIED",
|
|
4737
4757
|
"SESSION_NOT_FOUND",
|
|
4738
4758
|
"PERMISSION_HINT_INVALID",
|
|
4739
4759
|
"DELEGATION_ARTIFACT_INVALID",
|
|
@@ -5188,6 +5208,8 @@
|
|
|
5188
5208
|
"PROFILE_NOT_FOUND",
|
|
5189
5209
|
"PROFILE_POSTURE_NOT_ALLOWED",
|
|
5190
5210
|
"PROFILE_OWNER_OPT_IN_REQUIRED",
|
|
5211
|
+
"AUTH_REQUIRED",
|
|
5212
|
+
"PERMISSION_DENIED",
|
|
5191
5213
|
"SESSION_NOT_FOUND",
|
|
5192
5214
|
"PERMISSION_HINT_INVALID",
|
|
5193
5215
|
"DELEGATION_ARTIFACT_INVALID",
|
|
@@ -5809,6 +5831,8 @@
|
|
|
5809
5831
|
"PROFILE_NOT_FOUND",
|
|
5810
5832
|
"PROFILE_POSTURE_NOT_ALLOWED",
|
|
5811
5833
|
"PROFILE_OWNER_OPT_IN_REQUIRED",
|
|
5834
|
+
"AUTH_REQUIRED",
|
|
5835
|
+
"PERMISSION_DENIED",
|
|
5812
5836
|
"SESSION_NOT_FOUND",
|
|
5813
5837
|
"PERMISSION_HINT_INVALID",
|
|
5814
5838
|
"DELEGATION_ARTIFACT_INVALID",
|
|
@@ -6190,6 +6214,8 @@
|
|
|
6190
6214
|
"PROFILE_NOT_FOUND",
|
|
6191
6215
|
"PROFILE_POSTURE_NOT_ALLOWED",
|
|
6192
6216
|
"PROFILE_OWNER_OPT_IN_REQUIRED",
|
|
6217
|
+
"AUTH_REQUIRED",
|
|
6218
|
+
"PERMISSION_DENIED",
|
|
6193
6219
|
"SESSION_NOT_FOUND",
|
|
6194
6220
|
"PERMISSION_HINT_INVALID",
|
|
6195
6221
|
"DELEGATION_ARTIFACT_INVALID",
|
|
@@ -6765,6 +6791,8 @@
|
|
|
6765
6791
|
"PROFILE_NOT_FOUND",
|
|
6766
6792
|
"PROFILE_POSTURE_NOT_ALLOWED",
|
|
6767
6793
|
"PROFILE_OWNER_OPT_IN_REQUIRED",
|
|
6794
|
+
"AUTH_REQUIRED",
|
|
6795
|
+
"PERMISSION_DENIED",
|
|
6768
6796
|
"SESSION_NOT_FOUND",
|
|
6769
6797
|
"PERMISSION_HINT_INVALID",
|
|
6770
6798
|
"DELEGATION_ARTIFACT_INVALID",
|
|
@@ -7083,6 +7111,8 @@
|
|
|
7083
7111
|
"PROFILE_NOT_FOUND",
|
|
7084
7112
|
"PROFILE_POSTURE_NOT_ALLOWED",
|
|
7085
7113
|
"PROFILE_OWNER_OPT_IN_REQUIRED",
|
|
7114
|
+
"AUTH_REQUIRED",
|
|
7115
|
+
"PERMISSION_DENIED",
|
|
7086
7116
|
"SESSION_NOT_FOUND",
|
|
7087
7117
|
"PERMISSION_HINT_INVALID",
|
|
7088
7118
|
"DELEGATION_ARTIFACT_INVALID",
|
|
@@ -7713,6 +7743,8 @@
|
|
|
7713
7743
|
"PROFILE_NOT_FOUND",
|
|
7714
7744
|
"PROFILE_POSTURE_NOT_ALLOWED",
|
|
7715
7745
|
"PROFILE_OWNER_OPT_IN_REQUIRED",
|
|
7746
|
+
"AUTH_REQUIRED",
|
|
7747
|
+
"PERMISSION_DENIED",
|
|
7716
7748
|
"SESSION_NOT_FOUND",
|
|
7717
7749
|
"PERMISSION_HINT_INVALID",
|
|
7718
7750
|
"DELEGATION_ARTIFACT_INVALID",
|
|
@@ -8082,6 +8114,8 @@
|
|
|
8082
8114
|
"PROFILE_NOT_FOUND",
|
|
8083
8115
|
"PROFILE_POSTURE_NOT_ALLOWED",
|
|
8084
8116
|
"PROFILE_OWNER_OPT_IN_REQUIRED",
|
|
8117
|
+
"AUTH_REQUIRED",
|
|
8118
|
+
"PERMISSION_DENIED",
|
|
8085
8119
|
"SESSION_NOT_FOUND",
|
|
8086
8120
|
"PERMISSION_HINT_INVALID",
|
|
8087
8121
|
"DELEGATION_ARTIFACT_INVALID",
|
|
@@ -8665,6 +8699,8 @@
|
|
|
8665
8699
|
"PROFILE_NOT_FOUND",
|
|
8666
8700
|
"PROFILE_POSTURE_NOT_ALLOWED",
|
|
8667
8701
|
"PROFILE_OWNER_OPT_IN_REQUIRED",
|
|
8702
|
+
"AUTH_REQUIRED",
|
|
8703
|
+
"PERMISSION_DENIED",
|
|
8668
8704
|
"SESSION_NOT_FOUND",
|
|
8669
8705
|
"PERMISSION_HINT_INVALID",
|
|
8670
8706
|
"DELEGATION_ARTIFACT_INVALID",
|
|
@@ -8995,6 +9031,8 @@
|
|
|
8995
9031
|
"PROFILE_NOT_FOUND",
|
|
8996
9032
|
"PROFILE_POSTURE_NOT_ALLOWED",
|
|
8997
9033
|
"PROFILE_OWNER_OPT_IN_REQUIRED",
|
|
9034
|
+
"AUTH_REQUIRED",
|
|
9035
|
+
"PERMISSION_DENIED",
|
|
8998
9036
|
"SESSION_NOT_FOUND",
|
|
8999
9037
|
"PERMISSION_HINT_INVALID",
|
|
9000
9038
|
"DELEGATION_ARTIFACT_INVALID",
|
|
@@ -9576,6 +9614,8 @@
|
|
|
9576
9614
|
"PROFILE_NOT_FOUND",
|
|
9577
9615
|
"PROFILE_POSTURE_NOT_ALLOWED",
|
|
9578
9616
|
"PROFILE_OWNER_OPT_IN_REQUIRED",
|
|
9617
|
+
"AUTH_REQUIRED",
|
|
9618
|
+
"PERMISSION_DENIED",
|
|
9579
9619
|
"SESSION_NOT_FOUND",
|
|
9580
9620
|
"PERMISSION_HINT_INVALID",
|
|
9581
9621
|
"DELEGATION_ARTIFACT_INVALID",
|
|
@@ -9901,6 +9941,8 @@
|
|
|
9901
9941
|
"PROFILE_NOT_FOUND",
|
|
9902
9942
|
"PROFILE_POSTURE_NOT_ALLOWED",
|
|
9903
9943
|
"PROFILE_OWNER_OPT_IN_REQUIRED",
|
|
9944
|
+
"AUTH_REQUIRED",
|
|
9945
|
+
"PERMISSION_DENIED",
|
|
9904
9946
|
"SESSION_NOT_FOUND",
|
|
9905
9947
|
"PERMISSION_HINT_INVALID",
|
|
9906
9948
|
"DELEGATION_ARTIFACT_INVALID",
|
|
@@ -10655,6 +10697,8 @@
|
|
|
10655
10697
|
"PROFILE_NOT_FOUND",
|
|
10656
10698
|
"PROFILE_POSTURE_NOT_ALLOWED",
|
|
10657
10699
|
"PROFILE_OWNER_OPT_IN_REQUIRED",
|
|
10700
|
+
"AUTH_REQUIRED",
|
|
10701
|
+
"PERMISSION_DENIED",
|
|
10658
10702
|
"SESSION_NOT_FOUND",
|
|
10659
10703
|
"PERMISSION_HINT_INVALID",
|
|
10660
10704
|
"DELEGATION_ARTIFACT_INVALID",
|
|
@@ -10999,6 +11043,8 @@
|
|
|
10999
11043
|
"PROFILE_NOT_FOUND",
|
|
11000
11044
|
"PROFILE_POSTURE_NOT_ALLOWED",
|
|
11001
11045
|
"PROFILE_OWNER_OPT_IN_REQUIRED",
|
|
11046
|
+
"AUTH_REQUIRED",
|
|
11047
|
+
"PERMISSION_DENIED",
|
|
11002
11048
|
"SESSION_NOT_FOUND",
|
|
11003
11049
|
"PERMISSION_HINT_INVALID",
|
|
11004
11050
|
"DELEGATION_ARTIFACT_INVALID",
|
|
@@ -11556,6 +11602,8 @@
|
|
|
11556
11602
|
"PROFILE_NOT_FOUND",
|
|
11557
11603
|
"PROFILE_POSTURE_NOT_ALLOWED",
|
|
11558
11604
|
"PROFILE_OWNER_OPT_IN_REQUIRED",
|
|
11605
|
+
"AUTH_REQUIRED",
|
|
11606
|
+
"PERMISSION_DENIED",
|
|
11559
11607
|
"SESSION_NOT_FOUND",
|
|
11560
11608
|
"PERMISSION_HINT_INVALID",
|
|
11561
11609
|
"DELEGATION_ARTIFACT_INVALID",
|
|
@@ -11860,6 +11908,8 @@
|
|
|
11860
11908
|
"PROFILE_NOT_FOUND",
|
|
11861
11909
|
"PROFILE_POSTURE_NOT_ALLOWED",
|
|
11862
11910
|
"PROFILE_OWNER_OPT_IN_REQUIRED",
|
|
11911
|
+
"AUTH_REQUIRED",
|
|
11912
|
+
"PERMISSION_DENIED",
|
|
11863
11913
|
"SESSION_NOT_FOUND",
|
|
11864
11914
|
"PERMISSION_HINT_INVALID",
|
|
11865
11915
|
"DELEGATION_ARTIFACT_INVALID",
|
|
@@ -12504,6 +12554,8 @@
|
|
|
12504
12554
|
"PROFILE_NOT_FOUND",
|
|
12505
12555
|
"PROFILE_POSTURE_NOT_ALLOWED",
|
|
12506
12556
|
"PROFILE_OWNER_OPT_IN_REQUIRED",
|
|
12557
|
+
"AUTH_REQUIRED",
|
|
12558
|
+
"PERMISSION_DENIED",
|
|
12507
12559
|
"SESSION_NOT_FOUND",
|
|
12508
12560
|
"PERMISSION_HINT_INVALID",
|
|
12509
12561
|
"DELEGATION_ARTIFACT_INVALID",
|
|
@@ -12845,6 +12897,8 @@
|
|
|
12845
12897
|
"PROFILE_NOT_FOUND",
|
|
12846
12898
|
"PROFILE_POSTURE_NOT_ALLOWED",
|
|
12847
12899
|
"PROFILE_OWNER_OPT_IN_REQUIRED",
|
|
12900
|
+
"AUTH_REQUIRED",
|
|
12901
|
+
"PERMISSION_DENIED",
|
|
12848
12902
|
"SESSION_NOT_FOUND",
|
|
12849
12903
|
"PERMISSION_HINT_INVALID",
|
|
12850
12904
|
"DELEGATION_ARTIFACT_INVALID",
|
|
@@ -13539,6 +13593,8 @@
|
|
|
13539
13593
|
"PROFILE_NOT_FOUND",
|
|
13540
13594
|
"PROFILE_POSTURE_NOT_ALLOWED",
|
|
13541
13595
|
"PROFILE_OWNER_OPT_IN_REQUIRED",
|
|
13596
|
+
"AUTH_REQUIRED",
|
|
13597
|
+
"PERMISSION_DENIED",
|
|
13542
13598
|
"SESSION_NOT_FOUND",
|
|
13543
13599
|
"PERMISSION_HINT_INVALID",
|
|
13544
13600
|
"DELEGATION_ARTIFACT_INVALID",
|
|
@@ -13927,6 +13983,8 @@
|
|
|
13927
13983
|
"PROFILE_NOT_FOUND",
|
|
13928
13984
|
"PROFILE_POSTURE_NOT_ALLOWED",
|
|
13929
13985
|
"PROFILE_OWNER_OPT_IN_REQUIRED",
|
|
13986
|
+
"AUTH_REQUIRED",
|
|
13987
|
+
"PERMISSION_DENIED",
|
|
13930
13988
|
"SESSION_NOT_FOUND",
|
|
13931
13989
|
"PERMISSION_HINT_INVALID",
|
|
13932
13990
|
"DELEGATION_ARTIFACT_INVALID",
|
|
@@ -14555,6 +14613,8 @@
|
|
|
14555
14613
|
"PROFILE_NOT_FOUND",
|
|
14556
14614
|
"PROFILE_POSTURE_NOT_ALLOWED",
|
|
14557
14615
|
"PROFILE_OWNER_OPT_IN_REQUIRED",
|
|
14616
|
+
"AUTH_REQUIRED",
|
|
14617
|
+
"PERMISSION_DENIED",
|
|
14558
14618
|
"SESSION_NOT_FOUND",
|
|
14559
14619
|
"PERMISSION_HINT_INVALID",
|
|
14560
14620
|
"DELEGATION_ARTIFACT_INVALID",
|
|
@@ -14929,6 +14989,8 @@
|
|
|
14929
14989
|
"PROFILE_NOT_FOUND",
|
|
14930
14990
|
"PROFILE_POSTURE_NOT_ALLOWED",
|
|
14931
14991
|
"PROFILE_OWNER_OPT_IN_REQUIRED",
|
|
14992
|
+
"AUTH_REQUIRED",
|
|
14993
|
+
"PERMISSION_DENIED",
|
|
14932
14994
|
"SESSION_NOT_FOUND",
|
|
14933
14995
|
"PERMISSION_HINT_INVALID",
|
|
14934
14996
|
"DELEGATION_ARTIFACT_INVALID",
|
|
@@ -15550,6 +15612,8 @@
|
|
|
15550
15612
|
"PROFILE_NOT_FOUND",
|
|
15551
15613
|
"PROFILE_POSTURE_NOT_ALLOWED",
|
|
15552
15614
|
"PROFILE_OWNER_OPT_IN_REQUIRED",
|
|
15615
|
+
"AUTH_REQUIRED",
|
|
15616
|
+
"PERMISSION_DENIED",
|
|
15553
15617
|
"SESSION_NOT_FOUND",
|
|
15554
15618
|
"PERMISSION_HINT_INVALID",
|
|
15555
15619
|
"DELEGATION_ARTIFACT_INVALID",
|
|
@@ -15931,6 +15995,8 @@
|
|
|
15931
15995
|
"PROFILE_NOT_FOUND",
|
|
15932
15996
|
"PROFILE_POSTURE_NOT_ALLOWED",
|
|
15933
15997
|
"PROFILE_OWNER_OPT_IN_REQUIRED",
|
|
15998
|
+
"AUTH_REQUIRED",
|
|
15999
|
+
"PERMISSION_DENIED",
|
|
15934
16000
|
"SESSION_NOT_FOUND",
|
|
15935
16001
|
"PERMISSION_HINT_INVALID",
|
|
15936
16002
|
"DELEGATION_ARTIFACT_INVALID",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tinycloud/operations",
|
|
3
|
-
"version": "0.4.0-beta.
|
|
3
|
+
"version": "0.4.0-beta.9",
|
|
4
4
|
"description": "TinyCloud shared operations contracts and runtime",
|
|
5
5
|
"author": "TinyCloud, Inc.",
|
|
6
6
|
"license": "EGPL",
|
|
@@ -64,8 +64,9 @@
|
|
|
64
64
|
"check:generated": "bun scripts/generate.ts --check"
|
|
65
65
|
},
|
|
66
66
|
"dependencies": {
|
|
67
|
-
"@tinycloud/node-sdk": "3.1.0-beta.
|
|
68
|
-
"@tinycloud/sdk-core": "3.1.0-beta.
|
|
67
|
+
"@tinycloud/node-sdk": "3.1.0-beta.6",
|
|
68
|
+
"@tinycloud/sdk-core": "3.1.0-beta.6",
|
|
69
|
+
"@tinycloud/sdk-services": "3.1.0-beta.6",
|
|
69
70
|
"node-sql-parser": "5.4.0",
|
|
70
71
|
"zod": "^3.22.0",
|
|
71
72
|
"zod-to-json-schema": "^3.22.0"
|