refactor: ensure symlinks are relative to allow moving databases around

chore: bump version (broke things with this refactor, might as well)
This commit is contained in:
Andy Burke 2025-07-14 13:54:56 -07:00
parent 9e5cde4c2a
commit 22a8b4d03f
2 changed files with 47 additions and 9 deletions

View file

@ -1,6 +1,6 @@
{
"name": "@andyburke/fsdb",
"version": "0.9.0",
"version": "1.0.0",
"license": "MIT",
"exports": {
".": "./fsdb.ts",

View file

@ -104,7 +104,7 @@ export class FSDB_INDEXER_SYMLINKS<T> implements FSDB_INDEXER<T> {
}
const resolved_item_path = await Deno.readLink(item_file.path);
results.push(resolved_item_path);
results.push(path.normalize(path.resolve(path.dirname(item_file.path), resolved_item_path)));
++counter;
}
@ -116,6 +116,44 @@ export class FSDB_INDEXER_SYMLINKS<T> implements FSDB_INDEXER<T> {
return results;
}
private async make_symlink(target: string, symlink_path: string) {
let tolerate_existing = false;
try {
const existing_link_stat = await Deno.lstat(symlink_path);
if (!existing_link_stat.isSymlink) {
throw new Deno.errors.AlreadyExists(
`There is an existing filesystem entry (not symlink) at ${symlink_path} while attempting to point to ${target}`
);
}
const existing_link_target = await Deno.readLink(symlink_path);
const resolved_link_target = path.resolve(existing_link_target);
const resolved_target = path.resolve(target);
if (resolved_link_target !== resolved_target) {
throw new Deno.errors.AlreadyExists(
`A symlink (${symlink_path}) already exists pointing to (${resolved_link_target}), failed to create one pointing to: ${resolved_target}`
);
}
tolerate_existing = true;
} catch (error) {
if (!(error instanceof Deno.errors.NotFound)) {
throw error;
}
}
await Deno.mkdir(path.dirname(symlink_path), { recursive: true });
try {
await fs.ensureSymlink(target, symlink_path);
} catch (error) {
if (!(error instanceof Deno.errors.AlreadyExists) || !tolerate_existing) {
throw error;
}
}
}
async index(item: T, authoritative_path: string): Promise<string[]> {
if (typeof this.config.root !== 'string') {
throw new Error('root should have been set by FSDB instance');
@ -171,9 +209,8 @@ export class FSDB_INDEXER_SYMLINKS<T> implements FSDB_INDEXER<T> {
}
// create index symlink and reverse link
await Deno.mkdir(path.dirname(symlink_path), { recursive: true });
await fs.ensureSymlink(authoritative_path, symlink_path);
await fs.ensureSymlink(symlink_path, reverse_link_path);
await this.make_symlink(path.relative(path.dirname(symlink_path), authoritative_path), symlink_path);
await this.make_symlink(path.relative(path.dirname(reverse_link_path), symlink_path), reverse_link_path);
results.push(symlink_path);
}
@ -193,13 +230,14 @@ export class FSDB_INDEXER_SYMLINKS<T> implements FSDB_INDEXER<T> {
}
const index_symlink_path: string = await Deno.readLink(item_dir_reverse_link);
if (fs.existsSync(index_symlink_path)) {
await Deno.remove(index_symlink_path);
results.push(index_symlink_path);
const resolved_index_symlink_path: string = path.resolve(path.join(item_dir, index_symlink_path));
if (fs.existsSync(resolved_index_symlink_path)) {
await Deno.remove(resolved_index_symlink_path);
results.push(resolved_index_symlink_path);
}
await Deno.remove(item_dir_reverse_link);
results.push(...await cleanup_empty_directories(index_symlink_path));
results.push(...await cleanup_empty_directories(resolved_index_symlink_path));
}
return results;