···11+MIT License
22+33+Copyright (c) 2024 Matt Foxx
44+55+Permission is hereby granted, free of charge, to any person obtaining a copy
66+of this software and associated documentation files (the "Software"), to deal
77+in the Software without restriction, including without limitation the rights
88+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
99+copies of the Software, and to permit persons to whom the Software is
1010+furnished to do so, subject to the following conditions:
1111+1212+The above copyright notice and this permission notice shall be included in all
1313+copies or substantial portions of the Software.
1414+1515+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1616+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1717+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
1818+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
1919+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2020+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
2121+SOFTWARE.
···11+import {Level, Logger, StreamEntry} from 'pino';
22+33+export type AdditionalLevels = "verbose" | "log";
44+export type AllLevels = Level | AdditionalLevels;
55+export type LogLevel = AllLevels;
66+export const logLevels: LogLevel[] = ['fatal', 'error', 'warn', 'info', 'verbose', 'debug'];
77+88+export interface LogOptions {
99+ /**
1010+ * Specify the minimum log level for all log outputs without their own level specified.
1111+ *
1212+ * Defaults to env `LOG_LEVEL` or `info` if not specified.
1313+ *
1414+ * @default 'info'
1515+ * */
1616+ level?: LogLevel
1717+ /**
1818+ * Specify the minimum log level to output to rotating files. If `false` no log files will be created.
1919+ * */
2020+ file?: LogLevel | false
2121+ /**
2222+ * Specify the minimum log level streamed to the console (or docker container)
2323+ * */
2424+ console?: LogLevel
2525+}
2626+2727+export const asLogOptions = (obj: object = {}): obj is LogOptions => {
2828+ return Object.entries(obj).every(([key, val]) => {
2929+ if (key !== 'file') {
3030+ return val === undefined || logLevels.includes(val.toLocaleLowerCase());
3131+ }
3232+ return val === undefined || val === false || logLevels.includes(val.toLocaleLowerCase());
3333+ });
3434+}
3535+3636+export type LabelledLogger = Logger<AllLevels> & {
3737+ labels?: any[]
3838+ addLabel: (value: any) => void
3939+}
4040+4141+export type AllLevelStreamEntry = StreamEntry<AllLevels>
+33
src/util.ts
···11+import pathUtil from "path";
22+import {accessSync, constants} from "fs";
33+import {ErrorWithCause} from "pony-cause";
44+55+export const fileOrDirectoryIsWriteable = (location: string) => {
66+ const pathInfo = pathUtil.parse(location);
77+ const isDir = pathInfo.ext === '';
88+ try {
99+ accessSync(location, constants.R_OK | constants.W_OK);
1010+ return true;
1111+ } catch (err: any) {
1212+ const {code} = err;
1313+ if (code === 'ENOENT') {
1414+ // file doesn't exist, see if we can write to directory in which case we are good
1515+ try {
1616+ accessSync(pathInfo.dir, constants.R_OK | constants.W_OK)
1717+ // we can write to dir
1818+ return true;
1919+ } catch (accessError: any) {
2020+ if (accessError.code === 'EACCES') {
2121+ // also can't access directory :(
2222+ throw new Error(`No ${isDir ? 'directory' : 'file'} exists at ${location} and application does not have permission to write to the parent directory`);
2323+ } else {
2424+ throw new ErrorWithCause(`No ${isDir ? 'directory' : 'file'} exists at ${location} and application is unable to access the parent directory due to a system error`, {cause: accessError});
2525+ }
2626+ }
2727+ } else if (code === 'EACCES') {
2828+ throw new Error(`${isDir ? 'Directory' : 'File'} exists at ${location} but application does not have permission to write to it.`);
2929+ } else {
3030+ throw new ErrorWithCause(`${isDir ? 'Directory' : 'File'} exists at ${location} but application is unable to access it due to a system error`, {cause: err});
3131+ }
3232+ }
3333+}