nonotify 0.1.5 → 0.1.6
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/README.md +2 -2
- package/dist/config.d.ts +1 -0
- package/dist/config.js +33 -10
- package/dist/notifier.js +19 -17
- package/package.json +1 -1
- package/src/config.ts +39 -12
- package/src/notifier.ts +25 -22
package/README.md
CHANGED
|
@@ -14,7 +14,7 @@ After install, `nnt` is available globally.
|
|
|
14
14
|
|
|
15
15
|
## Config location
|
|
16
16
|
|
|
17
|
-
- Default: `~/.nnt/
|
|
17
|
+
- Default: `~/.config/nnt/nnt.json`
|
|
18
18
|
- Override: set `NNT_CONFIG_DIR`
|
|
19
19
|
|
|
20
20
|
Example:
|
|
@@ -23,7 +23,7 @@ Example:
|
|
|
23
23
|
export NNT_CONFIG_DIR="$HOME/.config/nnt"
|
|
24
24
|
```
|
|
25
25
|
|
|
26
|
-
Config is stored as JSON in `<config-dir>/
|
|
26
|
+
Config is stored as JSON in `<config-dir>/nnt.json`.
|
|
27
27
|
|
|
28
28
|
## Add profile
|
|
29
29
|
|
package/dist/config.d.ts
CHANGED
|
@@ -11,5 +11,6 @@ export type NntConfig = {
|
|
|
11
11
|
};
|
|
12
12
|
export declare function getConfigDir(): string;
|
|
13
13
|
export declare function getConfigPath(): string;
|
|
14
|
+
export declare function getLegacyConfigPath(): string;
|
|
14
15
|
export declare function loadConfig(): Promise<NntConfig>;
|
|
15
16
|
export declare function saveConfig(config: NntConfig): Promise<void>;
|
package/dist/config.js
CHANGED
|
@@ -5,31 +5,47 @@ const DEFAULT_CONFIG = {
|
|
|
5
5
|
defaultProfile: null,
|
|
6
6
|
profiles: {},
|
|
7
7
|
};
|
|
8
|
+
const CONFIG_FILENAME = "nnt.json";
|
|
9
|
+
const DEFAULT_CONFIG_DIR = join(homedir(), ".config", "nnt");
|
|
10
|
+
const LEGACY_CONFIG_PATH = join(homedir(), ".nnt", "config");
|
|
8
11
|
export function getConfigDir() {
|
|
9
12
|
const dir = process.env.NNT_CONFIG_DIR;
|
|
10
13
|
if (dir && dir.trim() !== "") {
|
|
11
14
|
return resolve(dir);
|
|
12
15
|
}
|
|
13
|
-
return
|
|
16
|
+
return DEFAULT_CONFIG_DIR;
|
|
14
17
|
}
|
|
15
18
|
export function getConfigPath() {
|
|
16
|
-
return join(getConfigDir(),
|
|
19
|
+
return join(getConfigDir(), CONFIG_FILENAME);
|
|
20
|
+
}
|
|
21
|
+
export function getLegacyConfigPath() {
|
|
22
|
+
return LEGACY_CONFIG_PATH;
|
|
17
23
|
}
|
|
18
24
|
export async function loadConfig() {
|
|
19
25
|
const path = getConfigPath();
|
|
20
26
|
try {
|
|
21
27
|
const raw = await readFile(path, "utf8");
|
|
22
|
-
|
|
23
|
-
return {
|
|
24
|
-
defaultProfile: typeof parsed.defaultProfile === "string"
|
|
25
|
-
? parsed.defaultProfile
|
|
26
|
-
: null,
|
|
27
|
-
profiles: parsed.profiles ?? {},
|
|
28
|
-
};
|
|
28
|
+
return parseConfig(raw);
|
|
29
29
|
}
|
|
30
30
|
catch (error) {
|
|
31
31
|
if (isNodeError(error) && error.code === "ENOENT") {
|
|
32
|
-
|
|
32
|
+
try {
|
|
33
|
+
const legacyRaw = await readFile(getLegacyConfigPath(), "utf8");
|
|
34
|
+
const parsed = parseConfig(legacyRaw);
|
|
35
|
+
try {
|
|
36
|
+
await saveConfig(parsed);
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
// Best effort migration only.
|
|
40
|
+
}
|
|
41
|
+
return parsed;
|
|
42
|
+
}
|
|
43
|
+
catch (legacyError) {
|
|
44
|
+
if (isNodeError(legacyError) && legacyError.code === "ENOENT") {
|
|
45
|
+
return { ...DEFAULT_CONFIG };
|
|
46
|
+
}
|
|
47
|
+
throw legacyError;
|
|
48
|
+
}
|
|
33
49
|
}
|
|
34
50
|
throw error;
|
|
35
51
|
}
|
|
@@ -51,3 +67,10 @@ export async function saveConfig(config) {
|
|
|
51
67
|
function isNodeError(error) {
|
|
52
68
|
return typeof error === "object" && error !== null && "code" in error;
|
|
53
69
|
}
|
|
70
|
+
function parseConfig(raw) {
|
|
71
|
+
const parsed = JSON.parse(raw);
|
|
72
|
+
return {
|
|
73
|
+
defaultProfile: typeof parsed.defaultProfile === "string" ? parsed.defaultProfile : null,
|
|
74
|
+
profiles: parsed.profiles ?? {},
|
|
75
|
+
};
|
|
76
|
+
}
|
package/dist/notifier.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { readFileSync } from "node:fs";
|
|
2
|
-
import { getConfigPath } from "./config.js";
|
|
2
|
+
import { getConfigPath, getLegacyConfigPath } from "./config.js";
|
|
3
3
|
import { sendTelegramMessage } from "./telegram.js";
|
|
4
4
|
export class NotifierError extends Error {
|
|
5
5
|
constructor(message) {
|
|
@@ -23,25 +23,27 @@ export class NoProfilesConfiguredError extends NotifierError {
|
|
|
23
23
|
}
|
|
24
24
|
export class EnvConfigLoader {
|
|
25
25
|
load() {
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
catch (error) {
|
|
33
|
-
if (isNodeError(error) && error.code === "ENOENT") {
|
|
34
|
-
return {
|
|
35
|
-
profiles: [],
|
|
36
|
-
defaultProfile: null,
|
|
37
|
-
};
|
|
26
|
+
const configPaths = Array.from(new Set([getConfigPath(), getLegacyConfigPath()]));
|
|
27
|
+
for (const configPath of configPaths) {
|
|
28
|
+
try {
|
|
29
|
+
const raw = readFileSync(configPath, "utf8");
|
|
30
|
+
const rawConfig = JSON.parse(raw);
|
|
31
|
+
return normalizeEnvConfig(rawConfig);
|
|
38
32
|
}
|
|
39
|
-
|
|
40
|
-
|
|
33
|
+
catch (error) {
|
|
34
|
+
if (isNodeError(error) && error.code === "ENOENT") {
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
if (error instanceof SyntaxError) {
|
|
38
|
+
throw new NotifierError(`Invalid JSON in config file at ${configPath}: ${error.message}`);
|
|
39
|
+
}
|
|
40
|
+
throw error;
|
|
41
41
|
}
|
|
42
|
-
throw error;
|
|
43
42
|
}
|
|
44
|
-
return
|
|
43
|
+
return {
|
|
44
|
+
profiles: [],
|
|
45
|
+
defaultProfile: null,
|
|
46
|
+
};
|
|
45
47
|
}
|
|
46
48
|
}
|
|
47
49
|
export class Notifier {
|
package/package.json
CHANGED
package/src/config.ts
CHANGED
|
@@ -20,17 +20,25 @@ const DEFAULT_CONFIG: NntConfig = {
|
|
|
20
20
|
profiles: {},
|
|
21
21
|
};
|
|
22
22
|
|
|
23
|
+
const CONFIG_FILENAME = "nnt.json";
|
|
24
|
+
const DEFAULT_CONFIG_DIR = join(homedir(), ".config", "nnt");
|
|
25
|
+
const LEGACY_CONFIG_PATH = join(homedir(), ".nnt", "config");
|
|
26
|
+
|
|
23
27
|
export function getConfigDir(): string {
|
|
24
28
|
const dir = process.env.NNT_CONFIG_DIR;
|
|
25
29
|
if (dir && dir.trim() !== "") {
|
|
26
30
|
return resolve(dir);
|
|
27
31
|
}
|
|
28
32
|
|
|
29
|
-
return
|
|
33
|
+
return DEFAULT_CONFIG_DIR;
|
|
30
34
|
}
|
|
31
35
|
|
|
32
36
|
export function getConfigPath(): string {
|
|
33
|
-
return join(getConfigDir(),
|
|
37
|
+
return join(getConfigDir(), CONFIG_FILENAME);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function getLegacyConfigPath(): string {
|
|
41
|
+
return LEGACY_CONFIG_PATH;
|
|
34
42
|
}
|
|
35
43
|
|
|
36
44
|
export async function loadConfig(): Promise<NntConfig> {
|
|
@@ -38,18 +46,27 @@ export async function loadConfig(): Promise<NntConfig> {
|
|
|
38
46
|
|
|
39
47
|
try {
|
|
40
48
|
const raw = await readFile(path, "utf8");
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
return {
|
|
44
|
-
defaultProfile:
|
|
45
|
-
typeof parsed.defaultProfile === "string"
|
|
46
|
-
? parsed.defaultProfile
|
|
47
|
-
: null,
|
|
48
|
-
profiles: parsed.profiles ?? {},
|
|
49
|
-
};
|
|
49
|
+
return parseConfig(raw);
|
|
50
50
|
} catch (error) {
|
|
51
51
|
if (isNodeError(error) && error.code === "ENOENT") {
|
|
52
|
-
|
|
52
|
+
try {
|
|
53
|
+
const legacyRaw = await readFile(getLegacyConfigPath(), "utf8");
|
|
54
|
+
const parsed = parseConfig(legacyRaw);
|
|
55
|
+
|
|
56
|
+
try {
|
|
57
|
+
await saveConfig(parsed);
|
|
58
|
+
} catch {
|
|
59
|
+
// Best effort migration only.
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return parsed;
|
|
63
|
+
} catch (legacyError) {
|
|
64
|
+
if (isNodeError(legacyError) && legacyError.code === "ENOENT") {
|
|
65
|
+
return { ...DEFAULT_CONFIG };
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
throw legacyError;
|
|
69
|
+
}
|
|
53
70
|
}
|
|
54
71
|
|
|
55
72
|
throw error;
|
|
@@ -75,3 +92,13 @@ export async function saveConfig(config: NntConfig): Promise<void> {
|
|
|
75
92
|
function isNodeError(error: unknown): error is NodeJS.ErrnoException {
|
|
76
93
|
return typeof error === "object" && error !== null && "code" in error;
|
|
77
94
|
}
|
|
95
|
+
|
|
96
|
+
function parseConfig(raw: string): NntConfig {
|
|
97
|
+
const parsed = JSON.parse(raw) as Partial<NntConfig>;
|
|
98
|
+
|
|
99
|
+
return {
|
|
100
|
+
defaultProfile:
|
|
101
|
+
typeof parsed.defaultProfile === "string" ? parsed.defaultProfile : null,
|
|
102
|
+
profiles: parsed.profiles ?? {},
|
|
103
|
+
};
|
|
104
|
+
}
|
package/src/notifier.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { readFileSync } from "node:fs";
|
|
2
|
-
import { getConfigPath } from "./config.js";
|
|
2
|
+
import { getConfigPath, getLegacyConfigPath } from "./config.js";
|
|
3
3
|
import { sendTelegramMessage } from "./telegram.js";
|
|
4
4
|
|
|
5
5
|
export type NotifierProfile = {
|
|
@@ -74,31 +74,34 @@ type RawRecordProfile = {
|
|
|
74
74
|
|
|
75
75
|
export class EnvConfigLoader implements NotifierConfigLoader {
|
|
76
76
|
load(): NotifierConfig {
|
|
77
|
-
const
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
try {
|
|
82
|
-
const raw = readFileSync(configPath, "utf8");
|
|
83
|
-
rawConfig = JSON.parse(raw) as RawEnvConfig;
|
|
84
|
-
} catch (error) {
|
|
85
|
-
if (isNodeError(error) && error.code === "ENOENT") {
|
|
86
|
-
return {
|
|
87
|
-
profiles: [],
|
|
88
|
-
defaultProfile: null,
|
|
89
|
-
};
|
|
90
|
-
}
|
|
77
|
+
const configPaths = Array.from(
|
|
78
|
+
new Set([getConfigPath(), getLegacyConfigPath()]),
|
|
79
|
+
);
|
|
91
80
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
);
|
|
81
|
+
for (const configPath of configPaths) {
|
|
82
|
+
try {
|
|
83
|
+
const raw = readFileSync(configPath, "utf8");
|
|
84
|
+
const rawConfig = JSON.parse(raw) as RawEnvConfig;
|
|
85
|
+
return normalizeEnvConfig(rawConfig);
|
|
86
|
+
} catch (error) {
|
|
87
|
+
if (isNodeError(error) && error.code === "ENOENT") {
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (error instanceof SyntaxError) {
|
|
92
|
+
throw new NotifierError(
|
|
93
|
+
`Invalid JSON in config file at ${configPath}: ${error.message}`,
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
throw error;
|
|
96
98
|
}
|
|
97
|
-
|
|
98
|
-
throw error;
|
|
99
99
|
}
|
|
100
100
|
|
|
101
|
-
return
|
|
101
|
+
return {
|
|
102
|
+
profiles: [],
|
|
103
|
+
defaultProfile: null,
|
|
104
|
+
};
|
|
102
105
|
}
|
|
103
106
|
}
|
|
104
107
|
|