@tinycloud/operations 0.6.1-beta.2 → 0.6.1-beta.21
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/coverage.json +1 -0
- package/dist/artifacts.cjs +263 -56
- package/dist/artifacts.cjs.map +1 -1
- package/dist/artifacts.d.cts +54 -54
- package/dist/artifacts.d.ts +54 -54
- package/dist/artifacts.js +266 -58
- package/dist/artifacts.js.map +1 -1
- package/dist/cli-runtime.cjs +284 -69
- package/dist/cli-runtime.cjs.map +1 -1
- package/dist/cli-runtime.js +287 -71
- package/dist/cli-runtime.js.map +1 -1
- package/dist/delegation-binding.cjs +268 -58
- package/dist/delegation-binding.cjs.map +1 -1
- package/dist/delegation-binding.js +271 -60
- package/dist/delegation-binding.js.map +1 -1
- package/dist/index.cjs +284 -69
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +287 -71
- package/dist/index.js.map +1 -1
- package/dist/profile.cjs +160 -10
- package/dist/profile.cjs.map +1 -1
- package/dist/profile.js +163 -12
- package/dist/profile.js.map +1 -1
- package/dist/state.cjs +277 -58
- package/dist/state.cjs.map +1 -1
- package/dist/state.d.cts +12 -1
- package/dist/state.d.ts +12 -1
- package/dist/state.js +276 -60
- package/dist/state.js.map +1 -1
- package/package.json +4 -4
|
@@ -3759,14 +3759,18 @@ var TEST_LOCK_TURN_SETTLE_BARRIER_DIR = "TC_TEST_PROFILE_LOCK_TURN_SETTLE_BARRIE
|
|
|
3759
3759
|
var TEST_LOCK_TURN_COLLECT_BARRIER_DIR = "TC_TEST_PROFILE_LOCK_TURN_COLLECT_BARRIER_DIR";
|
|
3760
3760
|
var TEST_LOCK_LIVENESS_BARRIER_DIR = "TC_TEST_PROFILE_LOCK_LIVENESS_BARRIER_DIR";
|
|
3761
3761
|
function processWideContext(key) {
|
|
3762
|
-
var
|
|
3762
|
+
var _a5;
|
|
3763
3763
|
const registry = globalThis;
|
|
3764
|
-
return registry[
|
|
3764
|
+
return registry[_a5 = Symbol.for(key)] ?? (registry[_a5] = new import_node_async_hooks.AsyncLocalStorage());
|
|
3765
3765
|
}
|
|
3766
3766
|
var invocationStateRoot = processWideContext("tinycloud.operations.invocationStateRoot.v1");
|
|
3767
3767
|
var heldProfileLocks = processWideContext("tinycloud.operations.heldProfileLocks.v2");
|
|
3768
|
+
var pinnedProfiles = processWideContext("tinycloud.operations.pinnedProfiles.v2");
|
|
3769
|
+
var pinnedHomes = processWideContext("tinycloud.operations.pinnedHomes.v2");
|
|
3768
3770
|
var _a2;
|
|
3769
|
-
var
|
|
3771
|
+
var canonicalHomes = globalThis[_a2 = /* @__PURE__ */ Symbol.for("tinycloud.operations.canonicalHomes.v1")] ?? (globalThis[_a2] = /* @__PURE__ */ new Map());
|
|
3772
|
+
var _a3;
|
|
3773
|
+
var processInstanceId = globalThis[_a3 = /* @__PURE__ */ Symbol.for("tinycloud.operations.profileLockInstance.v1")] ?? (globalThis[_a3] = (0, import_node_crypto.randomUUID)());
|
|
3770
3774
|
var cachedProcessOrigin;
|
|
3771
3775
|
function processOrigin() {
|
|
3772
3776
|
if (cachedProcessOrigin !== void 0) return cachedProcessOrigin;
|
|
@@ -3944,9 +3948,14 @@ function tinycloudHomePath() {
|
|
|
3944
3948
|
function profilesPath() {
|
|
3945
3949
|
return (0, import_node_path.join)(tinycloudHomePath(), "profiles");
|
|
3946
3950
|
}
|
|
3947
|
-
function
|
|
3951
|
+
function namedProfilePath(profile) {
|
|
3948
3952
|
return (0, import_node_path.join)(profilesPath(), validateProfileName(profile));
|
|
3949
3953
|
}
|
|
3954
|
+
function profilePath(profile) {
|
|
3955
|
+
const named = namedProfilePath(profile);
|
|
3956
|
+
const pinned = pinnedProfiles.getStore()?.get(named);
|
|
3957
|
+
return pinned ? livePinnedPath(profile, pinned) : named;
|
|
3958
|
+
}
|
|
3950
3959
|
function profileConfigPath(profile) {
|
|
3951
3960
|
return (0, import_node_path.join)(profilePath(profile), "profile.json");
|
|
3952
3961
|
}
|
|
@@ -3960,10 +3969,12 @@ function profileStoreMetadataPath(profile, store) {
|
|
|
3960
3969
|
return `${profileStorePath(profile, store)}.metadata.json`;
|
|
3961
3970
|
}
|
|
3962
3971
|
function profileLockPath(profile) {
|
|
3963
|
-
return (0, import_node_path.join)(
|
|
3972
|
+
return (0, import_node_path.join)(namedProfilePath(profile), ".lock");
|
|
3964
3973
|
}
|
|
3965
3974
|
function profileTurnLockPath(profile) {
|
|
3966
|
-
|
|
3975
|
+
const home = tinycloudHomePath();
|
|
3976
|
+
const pinned = pinnedHomes.getStore()?.get(home);
|
|
3977
|
+
return (0, import_node_path.join)(pinned ? livePinnedPath(profile, pinned) : home, "profile-locks", validateProfileName(profile));
|
|
3967
3978
|
}
|
|
3968
3979
|
async function readJson(filePath) {
|
|
3969
3980
|
try {
|
|
@@ -3973,6 +3984,44 @@ async function readJson(filePath) {
|
|
|
3973
3984
|
throw error;
|
|
3974
3985
|
}
|
|
3975
3986
|
}
|
|
3987
|
+
async function readProfileJson(profile, filePath) {
|
|
3988
|
+
const before = process.platform !== "linux" ? await (0, import_promises.lstat)(filePath, { bigint: true }).catch((error) => {
|
|
3989
|
+
if (error.code === "ENOENT") return null;
|
|
3990
|
+
throw error;
|
|
3991
|
+
}) : null;
|
|
3992
|
+
if (process.platform !== "linux" && !before) return null;
|
|
3993
|
+
if (before && !before.isFile()) throw new ProfileSymlinkError(profile);
|
|
3994
|
+
const dev = before && exactFileId(before.dev);
|
|
3995
|
+
const ino = before && exactFileId(before.ino);
|
|
3996
|
+
try {
|
|
3997
|
+
const handle = await (0, import_promises.open)(filePath, import_node_fs.constants.O_RDONLY | (import_node_fs.constants.O_NOFOLLOW ?? 0));
|
|
3998
|
+
try {
|
|
3999
|
+
if (before) {
|
|
4000
|
+
const opened = await handle.stat({ bigint: true });
|
|
4001
|
+
if (!opened.isFile() || dev === null || ino === null || dev !== exactFileId(opened.dev) || ino !== exactFileId(opened.ino)) {
|
|
4002
|
+
throw new ProfileSymlinkError(profile, "changed while opening its file");
|
|
4003
|
+
}
|
|
4004
|
+
}
|
|
4005
|
+
const contents = await handle.readFile("utf8");
|
|
4006
|
+
if (before) {
|
|
4007
|
+
const named = await (0, import_promises.lstat)(filePath, { bigint: true }).catch((error) => {
|
|
4008
|
+
if (error.code === "ENOENT") return null;
|
|
4009
|
+
throw error;
|
|
4010
|
+
});
|
|
4011
|
+
if (!named?.isFile() || dev !== exactFileId(named.dev) || ino !== exactFileId(named.ino)) {
|
|
4012
|
+
throw new ProfileSymlinkError(profile, "changed while reading its file");
|
|
4013
|
+
}
|
|
4014
|
+
}
|
|
4015
|
+
return JSON.parse(contents);
|
|
4016
|
+
} finally {
|
|
4017
|
+
await handle.close();
|
|
4018
|
+
}
|
|
4019
|
+
} catch (error) {
|
|
4020
|
+
if (isErrno(error, "ENOENT")) return null;
|
|
4021
|
+
if (isErrno(error, "ELOOP") || isErrno(error, "ENOTDIR")) throw new ProfileSymlinkError(profile);
|
|
4022
|
+
throw error;
|
|
4023
|
+
}
|
|
4024
|
+
}
|
|
3976
4025
|
async function writeJsonAtomic(filePath, value) {
|
|
3977
4026
|
await replaceJson(filePath, value, false);
|
|
3978
4027
|
}
|
|
@@ -4006,49 +4055,203 @@ async function writeFileDurably(path, contents) {
|
|
|
4006
4055
|
}
|
|
4007
4056
|
}
|
|
4008
4057
|
async function readSession(profile) {
|
|
4009
|
-
|
|
4010
|
-
|
|
4058
|
+
return withPinnedProfileDirectory(profile, async (directory) => {
|
|
4059
|
+
if (!directory) return null;
|
|
4060
|
+
await readStoreMetadata(profile, "session");
|
|
4061
|
+
return readProfileJson(profile, sessionPath(profile));
|
|
4062
|
+
});
|
|
4011
4063
|
}
|
|
4012
4064
|
async function readAdditionalDelegations(profile) {
|
|
4013
4065
|
return (await readProfileStore(profile, "additional-delegations")).records;
|
|
4014
4066
|
}
|
|
4015
4067
|
async function readStoreMetadata(profile, store) {
|
|
4016
|
-
|
|
4017
|
-
|
|
4018
|
-
|
|
4019
|
-
|
|
4020
|
-
|
|
4021
|
-
|
|
4068
|
+
return withPinnedProfileDirectory(profile, async (directory) => {
|
|
4069
|
+
const metadata = directory ? await readProfileJson(profile, profileStoreMetadataPath(profile, store)) : null;
|
|
4070
|
+
if (metadata === null) return { formatVersion: 1 };
|
|
4071
|
+
if (typeof metadata !== "object" || metadata.formatVersion !== 1) {
|
|
4072
|
+
throw new TypeError(`Unsupported store format for "${store}".`);
|
|
4073
|
+
}
|
|
4074
|
+
return metadata;
|
|
4075
|
+
});
|
|
4022
4076
|
}
|
|
4023
4077
|
async function readProfileStore(profile, store) {
|
|
4024
|
-
|
|
4025
|
-
|
|
4026
|
-
|
|
4027
|
-
|
|
4028
|
-
|
|
4029
|
-
|
|
4030
|
-
|
|
4031
|
-
|
|
4078
|
+
return withPinnedProfileDirectory(profile, async (directory) => {
|
|
4079
|
+
if (!directory) return { formatVersion: 1, records: [] };
|
|
4080
|
+
const [metadata, raw] = await Promise.all([
|
|
4081
|
+
readStoreMetadata(profile, store),
|
|
4082
|
+
readProfileJson(profile, profileStorePath(profile, store))
|
|
4083
|
+
]);
|
|
4084
|
+
return {
|
|
4085
|
+
formatVersion: metadata.formatVersion,
|
|
4086
|
+
records: Array.isArray(raw) ? raw : []
|
|
4087
|
+
};
|
|
4088
|
+
});
|
|
4032
4089
|
}
|
|
4033
|
-
|
|
4034
|
-
|
|
4035
|
-
|
|
4036
|
-
|
|
4037
|
-
|
|
4038
|
-
|
|
4039
|
-
|
|
4040
|
-
|
|
4041
|
-
|
|
4042
|
-
|
|
4043
|
-
|
|
4044
|
-
|
|
4090
|
+
var ProfileSymlinkError = class extends Error {
|
|
4091
|
+
constructor(profile, detail = "must be a real directory, not a symlink or another file type") {
|
|
4092
|
+
super(`Profile "${profile}" ${detail}.`);
|
|
4093
|
+
this.code = "PROFILE_SYMLINK";
|
|
4094
|
+
this.name = "ProfileSymlinkError";
|
|
4095
|
+
}
|
|
4096
|
+
};
|
|
4097
|
+
function exactFileId(value) {
|
|
4098
|
+
if (typeof value === "bigint") return value >= 0n ? value : null;
|
|
4099
|
+
return Number.isSafeInteger(value) && value >= 0 ? BigInt(value) : null;
|
|
4100
|
+
}
|
|
4101
|
+
function isPinnedLive(pinned) {
|
|
4102
|
+
if (!pinned.active) return false;
|
|
4045
4103
|
try {
|
|
4046
|
-
|
|
4104
|
+
const stats = (0, import_node_fs.fstatSync)(pinned.fd, { bigint: true });
|
|
4105
|
+
return exactFileId(stats.dev) === pinned.dev && exactFileId(stats.ino) === pinned.ino;
|
|
4106
|
+
} catch {
|
|
4107
|
+
return false;
|
|
4108
|
+
}
|
|
4109
|
+
}
|
|
4110
|
+
function livePinnedPath(profile, pinned) {
|
|
4111
|
+
if (isPinnedLive(pinned)) return pinned.path;
|
|
4112
|
+
throw new ProfileSymlinkError(profile, "its pinned directory is no longer active or changed during use");
|
|
4113
|
+
}
|
|
4114
|
+
async function withPinnedProfileDirectory(profile, action, options = {}) {
|
|
4115
|
+
const named = namedProfilePath(profile);
|
|
4116
|
+
const existing = pinnedProfiles.getStore()?.get(named);
|
|
4117
|
+
if (existing) return action(livePinnedPath(profile, existing));
|
|
4118
|
+
if (process.platform !== "linux") {
|
|
4119
|
+
if (options.create) await (0, import_promises.mkdir)(named, { recursive: true, mode: PRIVATE_DIR_MODE });
|
|
4120
|
+
const stats = await (0, import_promises.lstat)(named).catch((error) => {
|
|
4121
|
+
if (error.code === "ENOENT") return null;
|
|
4122
|
+
throw error;
|
|
4123
|
+
});
|
|
4124
|
+
if (!stats) return action(null);
|
|
4125
|
+
if (!stats.isDirectory() || stats.isSymbolicLink()) throw new ProfileSymlinkError(profile);
|
|
4126
|
+
return action(named);
|
|
4127
|
+
}
|
|
4128
|
+
const home = tinycloudHomePath();
|
|
4129
|
+
const handles = [];
|
|
4130
|
+
const fdPath = (fd) => `/proc/self/fd/${fd}`;
|
|
4131
|
+
const flags = import_node_fs.constants.O_RDONLY | import_node_fs.constants.O_DIRECTORY | import_node_fs.constants.O_NOFOLLOW;
|
|
4132
|
+
try {
|
|
4133
|
+
if (options.create) await (0, import_promises.mkdir)(home, { recursive: true, mode: PRIVATE_DIR_MODE });
|
|
4134
|
+
const resolved = canonicalHomes.get(home) ?? await (0, import_promises.realpath)(home).catch((error) => {
|
|
4135
|
+
if (error.code === "ENOENT" && !options.create) return null;
|
|
4136
|
+
if (error.code === "ELOOP" || error.code === "ENOTDIR") throw new ProfileSymlinkError(profile);
|
|
4137
|
+
throw error;
|
|
4138
|
+
});
|
|
4139
|
+
if (!resolved) return await action(null);
|
|
4140
|
+
const root = canonicalHomes.get(home) ?? resolved;
|
|
4141
|
+
canonicalHomes.set(home, root);
|
|
4142
|
+
const segments = [root, "profiles", validateProfileName(profile)];
|
|
4143
|
+
for (let index = 0; index < segments.length; index++) {
|
|
4144
|
+
const path = handles.length ? (0, import_node_path.join)(fdPath(handles[handles.length - 1].fd), segments[index]) : segments[index];
|
|
4145
|
+
if (options.create && handles.length) {
|
|
4146
|
+
await (0, import_promises.mkdir)(path, { mode: PRIVATE_DIR_MODE }).catch((error) => {
|
|
4147
|
+
if (error.code !== "EEXIST") throw error;
|
|
4148
|
+
});
|
|
4149
|
+
}
|
|
4150
|
+
const handle = await (0, import_promises.open)(path, index === 1 ? flags & ~import_node_fs.constants.O_NOFOLLOW : flags).catch((error) => {
|
|
4151
|
+
if (error.code === "ENOENT" && !options.create) return null;
|
|
4152
|
+
if (error.code === "ELOOP" || error.code === "ENOTDIR") throw new ProfileSymlinkError(profile);
|
|
4153
|
+
throw error;
|
|
4154
|
+
});
|
|
4155
|
+
if (!handle) return await action(null);
|
|
4156
|
+
handles.push(handle);
|
|
4157
|
+
if (options.create) await handle.chmod(PRIVATE_DIR_MODE);
|
|
4158
|
+
}
|
|
4159
|
+
const opened = handles[2];
|
|
4160
|
+
const [actual, namedStats] = await Promise.all([opened.stat({ bigint: true }), (0, import_promises.lstat)(named, { bigint: true })]);
|
|
4161
|
+
const dev = exactFileId(actual.dev);
|
|
4162
|
+
const ino = exactFileId(actual.ino);
|
|
4163
|
+
if (!namedStats.isDirectory() || dev === null || ino === null || dev !== exactFileId(namedStats.dev) || ino !== exactFileId(namedStats.ino)) {
|
|
4164
|
+
throw new ProfileSymlinkError(profile, "changed while opening its directory");
|
|
4165
|
+
}
|
|
4166
|
+
const directory = fdPath(opened.fd);
|
|
4167
|
+
const pinned = {
|
|
4168
|
+
path: directory,
|
|
4169
|
+
fd: opened.fd,
|
|
4170
|
+
dev,
|
|
4171
|
+
ino,
|
|
4172
|
+
active: true
|
|
4173
|
+
};
|
|
4174
|
+
const homeHandle = handles[0];
|
|
4175
|
+
const homeStats = await homeHandle.stat({ bigint: true });
|
|
4176
|
+
const homeDev = exactFileId(homeStats.dev);
|
|
4177
|
+
const homeIno = exactFileId(homeStats.ino);
|
|
4178
|
+
if (homeDev === null || homeIno === null) throw new ProfileSymlinkError(profile, "has an unverified home directory identity");
|
|
4179
|
+
const homePinned = {
|
|
4180
|
+
path: fdPath(homeHandle.fd),
|
|
4181
|
+
fd: homeHandle.fd,
|
|
4182
|
+
dev: homeDev,
|
|
4183
|
+
ino: homeIno,
|
|
4184
|
+
active: true
|
|
4185
|
+
};
|
|
4186
|
+
const context = new Map(pinnedProfiles.getStore());
|
|
4187
|
+
context.set(named, pinned);
|
|
4188
|
+
const homes = new Map(pinnedHomes.getStore());
|
|
4189
|
+
homes.set(home, homePinned);
|
|
4190
|
+
try {
|
|
4191
|
+
return await pinnedHomes.run(homes, () => pinnedProfiles.run(context, () => action(directory)));
|
|
4192
|
+
} finally {
|
|
4193
|
+
pinned.active = false;
|
|
4194
|
+
homePinned.active = false;
|
|
4195
|
+
}
|
|
4047
4196
|
} finally {
|
|
4048
|
-
|
|
4049
|
-
|
|
4050
|
-
|
|
4197
|
+
for (const handle of handles.reverse()) await handle.close();
|
|
4198
|
+
}
|
|
4199
|
+
}
|
|
4200
|
+
async function assertRealProfileDirectory(profile) {
|
|
4201
|
+
const named = namedProfilePath(profile);
|
|
4202
|
+
const pinned = pinnedProfiles.getStore()?.get(named);
|
|
4203
|
+
if (pinned) {
|
|
4204
|
+
livePinnedPath(profile, pinned);
|
|
4205
|
+
return;
|
|
4051
4206
|
}
|
|
4207
|
+
const stats = await (0, import_promises.lstat)(named).catch((error) => {
|
|
4208
|
+
if (error.code === "ENOENT") return null;
|
|
4209
|
+
throw error;
|
|
4210
|
+
});
|
|
4211
|
+
if (stats && (!stats.isDirectory() || stats.isSymbolicLink())) throw new ProfileSymlinkError(profile);
|
|
4212
|
+
}
|
|
4213
|
+
async function withProfileLock(profile, action, options = {}) {
|
|
4214
|
+
const normalizedProfile = validateProfileName(profile);
|
|
4215
|
+
const lockPath = profileLockPath(normalizedProfile);
|
|
4216
|
+
const held = (heldProfileLocks.getStore() ?? []).filter((ownership) => ownership.active);
|
|
4217
|
+
if (held.some((ownership) => ownership.lockPath === lockPath)) {
|
|
4218
|
+
await assertRealProfileDirectory(normalizedProfile);
|
|
4219
|
+
return action();
|
|
4220
|
+
}
|
|
4221
|
+
const named = namedProfilePath(normalizedProfile);
|
|
4222
|
+
const inherited = pinnedProfiles.getStore()?.get(named);
|
|
4223
|
+
if (inherited && !isPinnedLive(inherited)) {
|
|
4224
|
+
const profiles = new Map(pinnedProfiles.getStore());
|
|
4225
|
+
profiles.delete(named);
|
|
4226
|
+
const homes = new Map(pinnedHomes.getStore());
|
|
4227
|
+
homes.delete(tinycloudHomePath());
|
|
4228
|
+
return pinnedHomes.run(homes, () => pinnedProfiles.run(profiles, () => withProfileLock(normalizedProfile, action, options)));
|
|
4229
|
+
}
|
|
4230
|
+
return withPinnedProfileDirectory(normalizedProfile, async (directory) => {
|
|
4231
|
+
if (!directory) throw new ProfileDeletedError(normalizedProfile);
|
|
4232
|
+
const acquired = await acquireProfileLock(normalizedProfile, options);
|
|
4233
|
+
const ownership = {
|
|
4234
|
+
lockPath,
|
|
4235
|
+
turnSlot: acquired.turnSlot,
|
|
4236
|
+
deletedWhileWaiting: acquired.deletedWhileWaiting,
|
|
4237
|
+
active: true
|
|
4238
|
+
};
|
|
4239
|
+
try {
|
|
4240
|
+
return await heldProfileLocks.run([...held, ownership], action);
|
|
4241
|
+
} finally {
|
|
4242
|
+
ownership.active = false;
|
|
4243
|
+
await acquired.release();
|
|
4244
|
+
if (ownership.deletedProfile || acquired.deletedWhileWaiting) {
|
|
4245
|
+
const current = await (0, import_promises.lstat)(namedProfilePath(normalizedProfile), { bigint: true }).catch(() => null);
|
|
4246
|
+
const original = await (0, import_promises.stat)(directory, { bigint: true }).catch(() => null);
|
|
4247
|
+
const currentDev = current && exactFileId(current.dev);
|
|
4248
|
+
const currentIno = current && exactFileId(current.ino);
|
|
4249
|
+
if (currentDev !== null && currentIno !== null && original && currentDev === exactFileId(original.dev) && currentIno === exactFileId(original.ino)) {
|
|
4250
|
+
await (0, import_promises.rmdir)(namedProfilePath(normalizedProfile)).catch(() => void 0);
|
|
4251
|
+
}
|
|
4252
|
+
}
|
|
4253
|
+
}
|
|
4254
|
+
}, { create: !options.requireProfile });
|
|
4052
4255
|
}
|
|
4053
4256
|
async function refuseWriteToDeletedProfile(profile) {
|
|
4054
4257
|
const lockPath = profileLockPath(profile);
|
|
@@ -4082,17 +4285,15 @@ async function acquireProfileLock(profile, options) {
|
|
|
4082
4285
|
retryMs: options.retryMs ?? DEFAULT_LOCK_RETRY_MS
|
|
4083
4286
|
};
|
|
4084
4287
|
const staleAfterMs = options.staleAfterMs ?? DEFAULT_STALE_LOCK_MS;
|
|
4085
|
-
const lockPath =
|
|
4288
|
+
const lockPath = (0, import_node_path.join)(profilePath(profile), ".lock");
|
|
4086
4289
|
const turnDirectory = profileTurnLockPath(profile);
|
|
4087
4290
|
const directory = profilePath(profile);
|
|
4291
|
+
await assertRealProfileDirectory(profile);
|
|
4088
4292
|
if (options.requireProfile && !await exists(profileConfigPath(profile))) throw new ProfileDeletedError(profile);
|
|
4089
4293
|
if (!options.requireProfile) await (0, import_promises.mkdir)(directory, { recursive: true, mode: PRIVATE_DIR_MODE });
|
|
4294
|
+
await assertRealProfileDirectory(profile);
|
|
4090
4295
|
await (0, import_promises.mkdir)((0, import_node_path.dirname)(turnDirectory), { recursive: true, mode: PRIVATE_DIR_MODE });
|
|
4091
|
-
|
|
4092
|
-
await (0, import_promises.chmod)(path, PRIVATE_DIR_MODE).catch((error) => {
|
|
4093
|
-
if (path !== directory || !isErrno(error, "ENOENT")) throw error;
|
|
4094
|
-
});
|
|
4095
|
-
}
|
|
4296
|
+
await (0, import_promises.chmod)(directory, PRIVATE_DIR_MODE);
|
|
4096
4297
|
const liveness = { directory: turnDirectory, held: void 0 };
|
|
4097
4298
|
const closeAcquisitionLiveness = async () => {
|
|
4098
4299
|
if (liveness.held) await closeLiveness(liveness.held);
|
|
@@ -4112,6 +4313,7 @@ async function acquireProfileLock(profile, options) {
|
|
|
4112
4313
|
}
|
|
4113
4314
|
const deletion = await readJson((0, import_node_path.join)(turnDirectory, "deleted.json")).catch(() => null);
|
|
4114
4315
|
const deletedWhileWaiting = typeof deletion?.slot === "number" && deletion.slot >= firstWaitedTurn;
|
|
4316
|
+
if (deletedWhileWaiting && !await exists(profileConfigPath(profile))) throw new ProfileDeletedError(profile);
|
|
4115
4317
|
while (true) {
|
|
4116
4318
|
const token = (0, import_node_crypto.randomUUID)();
|
|
4117
4319
|
if (await publishProfileLock(profile, lockPath, token, options.requireProfile === true, liveness.held ?? null)) {
|
|
@@ -4148,8 +4350,8 @@ async function waitOrTimeOut(profile, deadline, detail) {
|
|
|
4148
4350
|
await sleep(Math.min(deadline.retryMs, deadline.timeoutMs - (Date.now() - deadline.startedAt)));
|
|
4149
4351
|
}
|
|
4150
4352
|
var TURN = /^(?:0|[1-9][0-9]*)$/;
|
|
4151
|
-
var
|
|
4152
|
-
var relinquishedTurns = globalThis[
|
|
4353
|
+
var _a4;
|
|
4354
|
+
var relinquishedTurns = globalThis[_a4 = /* @__PURE__ */ Symbol.for("tinycloud.operations.relinquishedProfileTurns.v1")] ?? (globalThis[_a4] = /* @__PURE__ */ new Set());
|
|
4153
4355
|
var MARKER_RETRY_DELAYS_MS = [10, 50, 250];
|
|
4154
4356
|
var SETTLE_RETRY_MS = 1e3;
|
|
4155
4357
|
var SETTLE_RETRIES = 300;
|
|
@@ -4263,7 +4465,7 @@ async function takeTurn(profile, directory, latest, liveness) {
|
|
|
4263
4465
|
if (published) await markTurn(path, token, "done");
|
|
4264
4466
|
return null;
|
|
4265
4467
|
} catch (error) {
|
|
4266
|
-
if (published) settleOwnTurn(directory, slot, token);
|
|
4468
|
+
if (published) await settleOwnTurn(directory, slot, token);
|
|
4267
4469
|
throw error;
|
|
4268
4470
|
}
|
|
4269
4471
|
}
|
|
@@ -4274,17 +4476,25 @@ async function settleAbandonedTurn(directory, abandoned) {
|
|
|
4274
4476
|
}
|
|
4275
4477
|
await markTurn(path, abandoned.token, "done");
|
|
4276
4478
|
}
|
|
4277
|
-
function settleOwnTurn(directory, slot, token
|
|
4479
|
+
async function settleOwnTurn(directory, slot, token) {
|
|
4278
4480
|
relinquishedTurns.add(token);
|
|
4279
|
-
|
|
4280
|
-
|
|
4281
|
-
|
|
4282
|
-
|
|
4283
|
-
|
|
4284
|
-
|
|
4285
|
-
|
|
4286
|
-
|
|
4287
|
-
|
|
4481
|
+
const pinned = await pinDirectory(directory).catch(() => null);
|
|
4482
|
+
const path = pinned?.path(".") ?? directory;
|
|
4483
|
+
let retries = SETTLE_RETRIES;
|
|
4484
|
+
const attempt = () => {
|
|
4485
|
+
void (async () => {
|
|
4486
|
+
try {
|
|
4487
|
+
const turn = await readTurn(path, slot);
|
|
4488
|
+
if (turn !== null && turn !== "damaged" && turn.token === token && !turn.done) await settleAbandonedTurn(path, turn);
|
|
4489
|
+
relinquishedTurns.delete(token);
|
|
4490
|
+
await pinned?.close();
|
|
4491
|
+
} catch {
|
|
4492
|
+
if (retries-- > 0) setTimeout(attempt, SETTLE_RETRY_MS).unref();
|
|
4493
|
+
else await pinned?.close();
|
|
4494
|
+
}
|
|
4495
|
+
})();
|
|
4496
|
+
};
|
|
4497
|
+
attempt();
|
|
4288
4498
|
}
|
|
4289
4499
|
async function markTurn(path, token, kind) {
|
|
4290
4500
|
for (let attempt = 0; ; attempt++) {
|
|
@@ -4329,7 +4539,7 @@ async function releaseTurn(profile, turn, liveness) {
|
|
|
4329
4539
|
try {
|
|
4330
4540
|
await markTurn((0, import_node_path.join)(turn.directory, String(turn.slot)), turn.token, "done");
|
|
4331
4541
|
} catch {
|
|
4332
|
-
settleOwnTurn(turn.directory, turn.slot, turn.token);
|
|
4542
|
+
await settleOwnTurn(turn.directory, turn.slot, turn.token);
|
|
4333
4543
|
return;
|
|
4334
4544
|
}
|
|
4335
4545
|
await collectTurns(profile, turn, liveness).catch(() => void 0);
|