fix: ensure no crash if emhttp state configs are missing (#1514)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Added new utility functions to improve file writing reliability by
ensuring parent directories exist before writing.
* Introduced a new watch command for easier development workflow in the
shared package.

* **Bug Fixes**
* Improved startup behavior by logging warnings for missing
configuration keys instead of crashing, allowing initialization to
proceed.

* **Chores**
* Updated configuration version number and reformatted plugin list for
clarity.
* Relocated certain GraphQL schema type and enum declarations without
changing their content.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---
- To see the specific tasks where the Asana app for GitHub is being
used, see below:
  - https://app.asana.com/0/0/1210788779106748
This commit is contained in:
Pujit Mehrotra
2025-07-15 09:48:01 -04:00
committed by GitHub
parent af33e999a0
commit 1a7d35d3f6
6 changed files with 425 additions and 349 deletions

View File

@@ -0,0 +1,9 @@
# Justfile for unraid-shared
# Default recipe to run when just is called without arguments
default:
@just --list
# Watch for changes in src files and run clean + build
watch:
watchexec -r -e ts,tsx -w src -- pnpm build

View File

@@ -1,11 +1,24 @@
import { accessSync } from 'fs';
import { access } from 'fs/promises';
import { access, mkdir, writeFile } from 'fs/promises';
import { mkdirSync, writeFileSync } from 'fs';
import { F_OK } from 'node:constants';
import { dirname } from 'path';
/**
* Checks if a file exists asynchronously.
* @param path - The file path to check
* @returns Promise that resolves to true if file exists, false otherwise
*/
export const fileExists = async (path: string) =>
access(path, F_OK)
.then(() => true)
.catch(() => false);
/**
* Checks if a file exists synchronously.
* @param path - The file path to check
* @returns true if file exists, false otherwise
*/
export const fileExistsSync = (path: string) => {
try {
accessSync(path, F_OK);
@@ -14,3 +27,44 @@ export const fileExistsSync = (path: string) => {
return false;
}
};
/**
* Writes data to a file, creating parent directories if they don't exist.
*
* This function ensures the directory structure exists before writing the file,
* equivalent to `mkdir -p` followed by file writing.
*
* @param path - The file path to write to
* @param data - The data to write (string or Buffer)
* @throws {Error} If path is invalid (null, empty, or not a string)
* @throws {Error} For any file system errors (EACCES, EPERM, ENOSPC, EISDIR, etc.)
*/
export const ensureWrite = async (path: string, data: string | Buffer) => {
if (!path || typeof path !== 'string') {
throw new Error(`Invalid path provided: ${path}`);
}
await mkdir(dirname(path), { recursive: true });
return await writeFile(path, data);
};
/**
* Writes data to a file synchronously, creating parent directories if they don't exist.
*
* This function ensures the directory structure exists before writing the file,
* equivalent to `mkdir -p` followed by file writing.
*
* @param path - The file path to write to
* @param data - The data to write (string or Buffer)
* @throws {Error} If path is invalid (null, empty, or not a string)
* @throws {Error} For any file system errors (EACCES, EPERM, ENOSPC, EISDIR, etc.)
*/
export const ensureWriteSync = (path: string, data: string | Buffer) => {
if (!path || typeof path !== 'string') {
throw new Error(`Invalid path provided: ${path}`);
}
mkdirSync(dirname(path), { recursive: true });
return writeFileSync(path, data);
};