eternal-timer 2.1.0 → 2.2.1

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.
Files changed (50) hide show
  1. package/README.md +58 -25
  2. package/dist/cjs/JSONLTimersManager.cjs +198 -0
  3. package/dist/cjs/JSONLTimersManager.d.ts +63 -0
  4. package/dist/cjs/JSONLTimersManager.d.ts.map +1 -0
  5. package/dist/cjs/JSONLTimersManager.js.map +1 -0
  6. package/dist/cjs/Log.cjs +82 -0
  7. package/dist/cjs/Log.d.ts +8 -0
  8. package/dist/cjs/Log.d.ts.map +1 -0
  9. package/dist/cjs/Log.js.map +1 -0
  10. package/dist/cjs/PlainTextTimersManager.cjs +200 -0
  11. package/dist/cjs/PlainTextTimersManager.d.ts +60 -0
  12. package/dist/cjs/PlainTextTimersManager.d.ts.map +1 -0
  13. package/dist/cjs/PlainTextTimersManager.js.map +1 -0
  14. package/dist/cjs/TimersManager.cjs +39 -0
  15. package/dist/cjs/TimersManager.d.ts +75 -0
  16. package/dist/cjs/TimersManager.d.ts.map +1 -0
  17. package/dist/cjs/TimersManager.js.map +1 -0
  18. package/dist/cjs/index.cjs +5 -279
  19. package/dist/cjs/index.d.ts +3 -72
  20. package/dist/cjs/index.d.ts.map +1 -1
  21. package/dist/cjs/index.js.map +1 -1
  22. package/dist/cjs/types.cjs +3 -0
  23. package/dist/cjs/types.d.ts +8 -0
  24. package/dist/cjs/types.d.ts.map +1 -0
  25. package/dist/cjs/types.js.map +1 -0
  26. package/dist/esm/JSONLTimersManager.d.ts +63 -0
  27. package/dist/esm/JSONLTimersManager.d.ts.map +1 -0
  28. package/dist/esm/JSONLTimersManager.js +191 -0
  29. package/dist/esm/JSONLTimersManager.js.map +1 -0
  30. package/dist/esm/Log.d.ts +8 -0
  31. package/dist/esm/Log.d.ts.map +1 -0
  32. package/dist/esm/Log.js +45 -0
  33. package/dist/esm/Log.js.map +1 -0
  34. package/dist/esm/PlainTextTimersManager.d.ts +60 -0
  35. package/dist/esm/PlainTextTimersManager.d.ts.map +1 -0
  36. package/dist/esm/PlainTextTimersManager.js +193 -0
  37. package/dist/esm/PlainTextTimersManager.js.map +1 -0
  38. package/dist/esm/TimersManager.d.ts +75 -0
  39. package/dist/esm/TimersManager.d.ts.map +1 -0
  40. package/dist/esm/TimersManager.js +32 -0
  41. package/dist/esm/TimersManager.js.map +1 -0
  42. package/dist/esm/index.d.ts +3 -72
  43. package/dist/esm/index.d.ts.map +1 -1
  44. package/dist/esm/index.js +2 -274
  45. package/dist/esm/index.js.map +1 -1
  46. package/dist/esm/types.d.ts +8 -0
  47. package/dist/esm/types.d.ts.map +1 -0
  48. package/dist/esm/types.js +2 -0
  49. package/dist/esm/types.js.map +1 -0
  50. package/package.json +12 -4
@@ -0,0 +1,193 @@
1
+ import fs from "fs";
2
+ import { v4 as uuidv4 } from "uuid";
3
+ import readline from "readline";
4
+ import { TimersManager } from "./TimersManager.js";
5
+ import { Log } from "./Log.js";
6
+ export class PlainTextTimersManager extends TimersManager {
7
+ getDefaultFilename() {
8
+ return ".timers";
9
+ }
10
+ /**
11
+ * checkTimerfileSyntax
12
+ * @description Checks the syntax of the timer file.
13
+ * @param fileData
14
+ * @returns void
15
+ * @throws If syntax is invalid
16
+ */
17
+ async checkTimerfileSyntax(fileData) {
18
+ const throwing = () => {
19
+ throw new Error(`Timer file's syntax is wrong`);
20
+ };
21
+ const timersData = fileData
22
+ .split('\n')
23
+ .map(l => l.trim())
24
+ .filter(l => l !== "");
25
+ for (const timerData of timersData) {
26
+ const timerArray = timerData.split(/\s+/);
27
+ if (timerArray.length !== 3)
28
+ throwing();
29
+ if (timerArray[0]?.length !== 36)
30
+ throwing();
31
+ if (timerArray[1].trim() === "")
32
+ throwing();
33
+ if (timerArray[2].trim() === "")
34
+ throwing();
35
+ }
36
+ return;
37
+ }
38
+ /**
39
+ * createTimer
40
+ * @description Creates a new timer.
41
+ * @param length
42
+ * @returns Promise that resolves to the timer ID (UUID)
43
+ * @throws If length is invalid(e.g. length < 0) or file operation fails
44
+ * @example
45
+ * const manager = new PlainTextTimersManager();
46
+ * const newTimer = await manager.createTimer(5000);
47
+ * // newTimer will be id of the timer
48
+ */
49
+ async createTimer(length) {
50
+ try {
51
+ if (length < 0) {
52
+ throw new Error(`Invailed length: ${length}`);
53
+ }
54
+ const timersRaw = await fs.promises.readFile(this.timerfiledir, "utf-8");
55
+ await this.checkTimerfileSyntax(timersRaw);
56
+ length = Math.trunc(length);
57
+ // uuid, start, end
58
+ const id = uuidv4();
59
+ const now = Date.now();
60
+ const newTimerData = `${id} ${now.toString()} ${(now + length).toString()}`;
61
+ await fs.promises.appendFile(this.timerfiledir, newTimerData + "\n");
62
+ return id;
63
+ }
64
+ catch (e) {
65
+ throw new Error(`Error when creating timer: ${e}`);
66
+ }
67
+ }
68
+ /**
69
+ * removeTimer
70
+ * @description Removes a timer by ID.
71
+ * @param id ID of the timer to remove
72
+ * @returns void
73
+ * @throws If file operation fails
74
+ * @example
75
+ * await manager.removeTimer(id);
76
+ */
77
+ async removeTimer(id) {
78
+ try {
79
+ const timersRaw = await fs.promises.readFile(this.timerfiledir, "utf-8");
80
+ await this.checkTimerfileSyntax(timersRaw);
81
+ const rl = readline.createInterface({
82
+ input: fs.createReadStream(this.timerfiledir),
83
+ crlfDelay: Infinity,
84
+ });
85
+ const newTimersDataLines = [];
86
+ let found = false;
87
+ for await (const line of rl) {
88
+ if (!line.trim())
89
+ continue;
90
+ const [timerId] = line.split(" ");
91
+ if (timerId === id) {
92
+ found = true;
93
+ continue;
94
+ }
95
+ newTimersDataLines.push(line);
96
+ }
97
+ if (!found) {
98
+ throw new Error(`Timer with id ${id} not found`);
99
+ }
100
+ await fs.promises.writeFile(this.timerfiledir, newTimersDataLines.join("\n"), "utf-8");
101
+ return;
102
+ }
103
+ catch (e) {
104
+ throw new Error(`Error when removing timer: ${e}`);
105
+ }
106
+ }
107
+ /**
108
+ * checkTimers
109
+ * @description Starts monitoring expired timers asynchronously and returns immediately. The callback is invoked asynchronously when a timer expires.
110
+ * The callback is awaited before continuing.
111
+ * @param callback Function invoked when an expired timer is detected (called asynchronously)
112
+ * @param interval (number, optional): Check interval in milliseconds (default: 200ms)
113
+ * @throws If file operation fails
114
+ * @returns (NodeJS.Timeout) intervalId interval id of checkTimers
115
+ * @example
116
+ * const interval = manager.checkTimers((timer) => {
117
+ * console.log(`A timer was stopped: ${timer.id}`);
118
+ * });
119
+ */
120
+ checkTimers(callback, interval = 200) {
121
+ return setInterval(async () => {
122
+ if (this.checkLock)
123
+ return;
124
+ this.checkLock = true;
125
+ try {
126
+ const rl = readline.createInterface({
127
+ input: fs.createReadStream(this.timerfiledir),
128
+ crlfDelay: Infinity,
129
+ });
130
+ for await (const line of rl) {
131
+ if (!line.trim())
132
+ continue;
133
+ const [id, startStr, stopStr] = line.split(" ");
134
+ const timer = {
135
+ id: id,
136
+ start: Number(startStr),
137
+ stop: Number(stopStr),
138
+ };
139
+ const now = Date.now();
140
+ if (Number(timer.stop) <= now) {
141
+ await this.removeTimer(timer.id);
142
+ callback(timer).catch(async (e) => {
143
+ await Log.ensureLogger();
144
+ if (Log.loggerInstance) {
145
+ Log.loggerInstance.error(`Error in timer callback: ${e}`);
146
+ }
147
+ });
148
+ }
149
+ }
150
+ }
151
+ catch (e) {
152
+ await Log.ensureLogger();
153
+ if (Log.loggerInstance) {
154
+ Log.loggerInstance.error(`Error when checking alarm: ${e}`);
155
+ }
156
+ }
157
+ finally {
158
+ this.checkLock = false;
159
+ }
160
+ }, interval);
161
+ }
162
+ /**
163
+ * showTimers
164
+ * @description Retrieves all active timers.
165
+ * @returns Array of `Timer` objects
166
+ * @throws If file operation fails
167
+ * @example
168
+ * const timers = await manager.showTimers();
169
+ * console.log(JSON.stringify(timers))
170
+ */
171
+ async showTimers() {
172
+ try {
173
+ const timersRaw = await fs.promises.readFile(this.timerfiledir, "utf-8");
174
+ const timersData = timersRaw.split(/\r?\n/);
175
+ const timersJSON = [];
176
+ for (const timerData of timersData) {
177
+ const splitedTimerData = timerData.split(" ");
178
+ if (!timerData.trim())
179
+ continue;
180
+ timersJSON.push({
181
+ id: splitedTimerData[0],
182
+ start: Number(splitedTimerData[1]),
183
+ stop: Number(splitedTimerData[2]),
184
+ });
185
+ }
186
+ return timersJSON;
187
+ }
188
+ catch (e) {
189
+ throw new Error(`Error when showing timers: ${e}`);
190
+ }
191
+ }
192
+ }
193
+ //# sourceMappingURL=PlainTextTimersManager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PlainTextTimersManager.js","sourceRoot":"","sources":["../../src/PlainTextTimersManager.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,75 @@
1
+ import type { Timer } from "./types.js";
2
+ /**
3
+ * TimersManager
4
+ * @description
5
+ * Manages timers stored in a file.
6
+ * (This is a abstract class)
7
+ *
8
+ * - Timers are persisted in a file
9
+ * - Expired timers are detected by polling
10
+ */
11
+ export declare abstract class TimersManager {
12
+ protected readonly timerfiledir: string;
13
+ protected checkLock: boolean;
14
+ protected abstract getDefaultFilename(): string;
15
+ /**
16
+ * constructor
17
+ * @param timerfiledir(string, optional)
18
+ * If omitted, `.timers.jsonl` under the project root is used.
19
+ */
20
+ constructor(timerfiledir?: string);
21
+ /**
22
+ * checkTimerfileSyntax
23
+ * @description Checks the syntax of the timer file.
24
+ * @param fileData
25
+ * @returns void
26
+ * @throws If syntax is invalid
27
+ */
28
+ protected abstract checkTimerfileSyntax(fileData: string): Promise<void>;
29
+ /**
30
+ * createTimer
31
+ * @description Creates a new timer.
32
+ * @param length Timer duration in milliseconds
33
+ * @returns Promise that resolves to the timer ID (UUID)
34
+ * @throws If length is invalid(e.g. length < 0) or file operation fails
35
+ * @example
36
+ * const manager = new TimersManager();
37
+ * const newTimer = await manager.createTimer(5000);
38
+ * // newTimer will be id of the timer
39
+ */
40
+ abstract createTimer(length: number, title?: string, description?: string): Promise<string>;
41
+ /**
42
+ * removeTimer
43
+ * @description Removes a timer by ID.
44
+ * @param id ID of the timer to remove
45
+ * @returns void
46
+ * @throws If file operation fails
47
+ * @example
48
+ * await manager.removeTimer(id);
49
+ */
50
+ abstract removeTimer(id: string): Promise<void>;
51
+ /**
52
+ * @description Starts monitoring expired timers asynchronously and returns immediately. The callback is invoked asynchronously when a timer expires.
53
+ * The callback is awaited before continuing.
54
+ * @param callback Function invoked when an expired timer is detected (called asynchronously)
55
+ * @param interval (number, optional): Check interval in milliseconds (default: 200ms)
56
+ * @throws If file operation fails
57
+ * @returns (NodeJS.Timeout) intervalId interval id of checkTimers
58
+ * @example
59
+ * const interval = manager.checkTimers((timer) => {
60
+ * console.log(`A timer was stopped: ${timer.id}`);
61
+ * });
62
+ */
63
+ abstract checkTimers(callback: (timer: Timer) => Promise<void>, interval?: number): NodeJS.Timeout;
64
+ /**
65
+ * showTimers
66
+ * @description Retrieves all active timers.
67
+ * @returns Array of `Timer` objects
68
+ * @throws If file operation fails
69
+ * @example
70
+ * const timers = await manager.showTimers();
71
+ * console.log(JSON.stringify(timers))
72
+ */
73
+ abstract showTimers(): Promise<Timer[]>;
74
+ }
75
+ //# sourceMappingURL=TimersManager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TimersManager.d.ts","sourceRoot":"","sources":["../../src/TimersManager.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAExC;;;;;;;;GAQG;AACH,8BAAsB,aAAa;IAClC,SAAS,CAAC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IACxC,SAAS,CAAC,SAAS,EAAE,OAAO,CAAS;IAErC,SAAS,CAAC,QAAQ,CAAC,kBAAkB,IAAI,MAAM;IAE/C;;;;OAIM;gBAEL,YAAY,CAAC,EAAE,MAAM;IAYtB;;;;;;OAMG;IACH,SAAS,CAAC,QAAQ,CAAC,oBAAoB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAExE;;;;;;;;;;OAUM;aACU,WAAW,CAAC,MAAM,EAAC,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAEjG;;;;;;;;OAQM;aACU,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAEtD;;;;;;;;;;;OAWM;aACU,WAAW,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,OAAO,CAAC,IAAI,CAAC,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC,OAAO;IAEzG;;;;;;;;OAQM;aACU,UAAU,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;CAC9C"}
@@ -0,0 +1,32 @@
1
+ import path from "path";
2
+ import fs from "fs";
3
+ import searchRoot from "./searchRoot.js";
4
+ /**
5
+ * TimersManager
6
+ * @description
7
+ * Manages timers stored in a file.
8
+ * (This is a abstract class)
9
+ *
10
+ * - Timers are persisted in a file
11
+ * - Expired timers are detected by polling
12
+ */
13
+ export class TimersManager {
14
+ timerfiledir;
15
+ checkLock = false;
16
+ /**
17
+ * constructor
18
+ * @param timerfiledir(string, optional)
19
+ * If omitted, `.timers.jsonl` under the project root is used.
20
+ */
21
+ constructor(timerfiledir) {
22
+ this.timerfiledir =
23
+ timerfiledir ?? path.join(searchRoot(), this.getDefaultFilename());
24
+ try {
25
+ fs.accessSync(this.timerfiledir);
26
+ }
27
+ catch {
28
+ fs.writeFileSync(this.timerfiledir, "");
29
+ }
30
+ }
31
+ }
32
+ //# sourceMappingURL=TimersManager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TimersManager.js","sourceRoot":"","sources":["../../src/TimersManager.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,UAAU,MAAM,iBAAiB,CAAC;AAGzC;;;;;;;;GAQG;AACH,MAAM,OAAgB,aAAa;IACf,YAAY,CAAS;IAC9B,SAAS,GAAY,KAAK,CAAC;IAIrC;;;;OAIM;IACN,YACC,YAAqB;QAErB,IAAI,CAAC,YAAY;YACP,YAAY,IAAI,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,EAAE,IAAI,CAAC,kBAAkB,EAAE,CAAC,CAAC;QAE7E,IAAI,CAAC;YACJ,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QAClC,CAAC;QAAC,MAAM,CAAC;YACR,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,YAAY,EAAE,EAAE,CAAC,CAAC;QACzC,CAAC;IACF,CAAC;CA2DD"}
@@ -1,73 +1,4 @@
1
- export type Timer = {
2
- id: string;
3
- start: number;
4
- stop: number;
5
- title?: string;
6
- description?: string;
7
- };
8
- /**
9
- * TimersManager
10
- * @description
11
- * Manages timers stored in a file.
12
- * Each timer is stored as: `id start stop`.
13
- *
14
- * - Timers are persisted in a file
15
- * - Expired timers are detected by polling
16
- */
17
- export declare class TimersManager {
18
- private readonly timerfiledir;
19
- private readonly isJSONLines;
20
- private checkLock;
21
- /**
22
- * constructor
23
- * @param timerfiledir(string, optional)
24
- * If omitted, `.timers.jsonl` under the project root is used.
25
- */
26
- constructor(timerfiledir?: string);
27
- /**
28
- * createTimer
29
- * @description Creates a new timer.
30
- * @param length Timer duration in milliseconds
31
- * @returns Promise that resolves to the timer ID (UUID)
32
- * @throws If length is invalid(e.g. length < 0) or file operation fails
33
- * @example
34
- * const manager = new TimersManager();
35
- * const newTimer = await manager.createTimer(5000);
36
- * // newTimer will be id of the timer
37
- */
38
- createTimer(length: number, title?: string, description?: string): Promise<string>;
39
- /**
40
- * removeTimer
41
- * @description Removes a timer by ID.
42
- * @param id ID of the timer to remove
43
- * @returns void
44
- * @throws If file operation fails
45
- * @example
46
- * await manager.removeTimer(id);
47
- */
48
- removeTimer(id: string): Promise<void>;
49
- /**
50
- * @description Starts monitoring expired timers asynchronously and returns immediately. The callback is invoked asynchronously when a timer expires.
51
- * The callback is awaited before continuing.
52
- * @param callback Function invoked when an expired timer is detected (called asynchronously)
53
- * @param interval (number, optional): Check interval in milliseconds (default: 50ms)
54
- * @throws If file operation fails
55
- * @returns (NodeJS.Timeout) intervalId interval id of checkTimers
56
- * @example
57
- * const interval = manager.checkTimers((timer) => {
58
- * console.log(`A timer was stopped: ${timer.id}`);
59
- * });
60
- */
61
- checkTimers(callback: (timer: Timer) => Promise<void>, interval?: number): NodeJS.Timeout;
62
- /**
63
- * showTimers
64
- * @description Retrieves all active timers.
65
- * @returns Array of `Timer` objects
66
- * @throws If file operation fails
67
- * @example
68
- * const timers = await manager.showTimers();
69
- * console.log(JSON.stringify(timers))
70
- */
71
- showTimers(): Promise<Timer[]>;
72
- }
1
+ export type { Timer } from "./types.js";
2
+ export { JSONLTimersManager } from "./JSONLTimersManager.js";
3
+ export { PlainTextTimersManager } from "./PlainTextTimersManager.js";
73
4
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAKA,MAAM,MAAM,KAAK,GAAG;IAChB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAA;CACpB,CAAA;AA+BD;;;;;;;;GAQG;AACH,qBAAa,aAAa;IACzB,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;IACtC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAU;IACtC,OAAO,CAAC,SAAS,CAAkB;IAEnC;;;;OAIM;gBAEL,YAAY,CAAC,EAAE,MAAM;IActB;;;;;;;;;;OAUM;IACO,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAuC/F;;;;;;;;OAQM;IACO,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAiDnD;;;;;;;;;;;OAWM;IACC,WAAW,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,OAAO,CAAC,IAAI,CAAC,EAAE,QAAQ,GAAE,MAAW,GAAG,MAAM,CAAC,OAAO;IA+CpG;;;;;;;;OAQM;IACO,UAAU,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;CA6B3C"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAC7D,OAAO,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC"}
package/dist/esm/index.js CHANGED
@@ -1,275 +1,3 @@
1
- import fs from "fs";
2
- import path from "path";
3
- import searchRoot from "./searchRoot.js";
4
- import { v4 as uuidv4 } from "uuid";
5
- async function checkTimerfileSyntax(fileData, isJSONLines) {
6
- const throwing = () => {
7
- throw new Error(`Timer file's syntax is wrong`);
8
- };
9
- const timersData = fileData
10
- .split('\n')
11
- .map(l => l.trim())
12
- .filter(l => l !== "");
13
- if (isJSONLines) {
14
- for (const timerData of timersData) {
15
- const parsed = JSON.parse(timerData);
16
- if (!parsed.id || typeof parsed.id !== "string" || parsed.id.length !== 36)
17
- throwing();
18
- if (!parsed.start || typeof parsed.start !== "number" || parsed.start.toString().trim() === "")
19
- throwing();
20
- if (!parsed.stop || typeof parsed.stop !== "number" || parsed.stop.toString().trim() === "")
21
- throwing();
22
- if (parsed.title && typeof parsed.title !== "string")
23
- throwing();
24
- if (parsed.description && typeof parsed.description !== "string")
25
- throwing();
26
- }
27
- }
28
- else {
29
- for (const timerData of timersData) {
30
- const timerArray = timerData.split(/\s+/);
31
- if (timerArray.length !== 3)
32
- throwing();
33
- if (timerArray[0]?.length !== 36)
34
- throwing();
35
- if (timerArray[1].trim() === "")
36
- throwing();
37
- if (timerArray[2].trim() === "")
38
- throwing();
39
- }
40
- }
41
- return;
42
- }
43
- /**
44
- * TimersManager
45
- * @description
46
- * Manages timers stored in a file.
47
- * Each timer is stored as: `id start stop`.
48
- *
49
- * - Timers are persisted in a file
50
- * - Expired timers are detected by polling
51
- */
52
- export class TimersManager {
53
- timerfiledir;
54
- isJSONLines;
55
- checkLock = false;
56
- /**
57
- * constructor
58
- * @param timerfiledir(string, optional)
59
- * If omitted, `.timers.jsonl` under the project root is used.
60
- */
61
- constructor(timerfiledir) {
62
- this.timerfiledir =
63
- timerfiledir ?? path.join(searchRoot(), ".timers.jsonl");
64
- this.isJSONLines = this.timerfiledir.endsWith(".jsonl");
65
- try {
66
- fs.accessSync(this.timerfiledir);
67
- }
68
- catch {
69
- fs.writeFileSync(this.timerfiledir, "");
70
- }
71
- }
72
- /**
73
- * createTimer
74
- * @description Creates a new timer.
75
- * @param length Timer duration in milliseconds
76
- * @returns Promise that resolves to the timer ID (UUID)
77
- * @throws If length is invalid(e.g. length < 0) or file operation fails
78
- * @example
79
- * const manager = new TimersManager();
80
- * const newTimer = await manager.createTimer(5000);
81
- * // newTimer will be id of the timer
82
- */
83
- async createTimer(length, title, description) {
84
- try {
85
- if (length < 0) {
86
- throw new Error(`Invailed length: ${length}`);
87
- }
88
- if (!this.isJSONLines) {
89
- if (title || description) {
90
- // out warn
91
- }
92
- }
93
- const timersRaw = await fs.promises.readFile(this.timerfiledir, "utf-8");
94
- await checkTimerfileSyntax(timersRaw, this.isJSONLines);
95
- length = Math.trunc(length);
96
- // uuid, start, end
97
- const id = uuidv4();
98
- const now = Date.now();
99
- let newTimerData;
100
- if (this.isJSONLines) {
101
- const json = {
102
- id,
103
- start: now,
104
- stop: (now + length),
105
- ...(title !== undefined && { title }),
106
- ...(description !== undefined && { description }),
107
- };
108
- newTimerData = JSON.stringify(json, null, 0);
109
- }
110
- else {
111
- newTimerData = `${id} ${now.toString()} ${(now + length).toString()}`;
112
- }
113
- await fs.promises.appendFile(this.timerfiledir, newTimerData + "\n");
114
- return id;
115
- }
116
- catch (e) {
117
- throw new Error(`Error when creating timer: ${e}`);
118
- }
119
- }
120
- /**
121
- * removeTimer
122
- * @description Removes a timer by ID.
123
- * @param id ID of the timer to remove
124
- * @returns void
125
- * @throws If file operation fails
126
- * @example
127
- * await manager.removeTimer(id);
128
- */
129
- async removeTimer(id) {
130
- try {
131
- const timersRaw = await fs.promises.readFile(this.timerfiledir, "utf-8");
132
- await checkTimerfileSyntax(timersRaw, this.isJSONLines);
133
- let newTimersData = "";
134
- if (this.isJSONLines) {
135
- const timersData = timersRaw
136
- .split(/\r?\n/)
137
- .filter(t => t.trim())
138
- .map(line => JSON.parse(line));
139
- let found = false;
140
- for (const timerData of timersData) {
141
- if (!timerData)
142
- continue;
143
- if (timerData.id === id) {
144
- found = true;
145
- continue;
146
- }
147
- newTimersData += `${JSON.stringify(timerData, null, 0)}\n`;
148
- }
149
- if (!found) {
150
- throw new Error(`Timer with id ${id} not found`);
151
- }
152
- }
153
- else {
154
- const timersData = timersRaw.split(/\r?\n/);
155
- let found = false;
156
- for (const timerData of timersData) {
157
- if (!timerData.trim())
158
- continue;
159
- const [timerId] = timerData.split(" ");
160
- if (timerId === id) {
161
- found = true;
162
- continue;
163
- }
164
- newTimersData += timerData + "\n";
165
- }
166
- if (!found) {
167
- throw new Error(`Timer with id ${id} not found`);
168
- }
169
- }
170
- await fs.promises.writeFile(this.timerfiledir, newTimersData, "utf-8");
171
- return;
172
- }
173
- catch (e) {
174
- throw new Error(`Error when removing timer: ${e}`);
175
- }
176
- }
177
- /**
178
- * @description Starts monitoring expired timers asynchronously and returns immediately. The callback is invoked asynchronously when a timer expires.
179
- * The callback is awaited before continuing.
180
- * @param callback Function invoked when an expired timer is detected (called asynchronously)
181
- * @param interval (number, optional): Check interval in milliseconds (default: 50ms)
182
- * @throws If file operation fails
183
- * @returns (NodeJS.Timeout) intervalId interval id of checkTimers
184
- * @example
185
- * const interval = manager.checkTimers((timer) => {
186
- * console.log(`A timer was stopped: ${timer.id}`);
187
- * });
188
- */
189
- checkTimers(callback, interval = 50) {
190
- return setInterval(async () => {
191
- if (this.checkLock)
192
- return;
193
- this.checkLock = true;
194
- try {
195
- const timersDataRaw = await fs.promises.readFile(this.timerfiledir, "utf-8");
196
- const timersSet = new Set();
197
- if (this.isJSONLines) {
198
- const timersData = timersDataRaw
199
- .split(/\r?\n/)
200
- .filter(line => line.trim())
201
- .map(line => JSON.parse(line));
202
- for (const timer of timersData) {
203
- timersSet.add(timer);
204
- }
205
- }
206
- else {
207
- const timersData = timersDataRaw.split(/\r?\n/);
208
- for (const timerData of timersData) {
209
- if (!timerData.trim())
210
- continue;
211
- const [id, startStr, stopStr] = timerData.split(" ");
212
- timersSet.add({
213
- id: id,
214
- start: Number(startStr),
215
- stop: Number(stopStr),
216
- });
217
- }
218
- }
219
- const now = Date.now();
220
- for (const timer of timersSet) {
221
- if (Number(timer.stop) <= now) {
222
- await this.removeTimer(timer.id);
223
- await callback(timer);
224
- }
225
- }
226
- }
227
- catch (e) {
228
- throw new Error(`Error when checking alarm: ${e}`);
229
- }
230
- finally {
231
- this.checkLock = false;
232
- }
233
- }, interval);
234
- }
235
- /**
236
- * showTimers
237
- * @description Retrieves all active timers.
238
- * @returns Array of `Timer` objects
239
- * @throws If file operation fails
240
- * @example
241
- * const timers = await manager.showTimers();
242
- * console.log(JSON.stringify(timers))
243
- */
244
- async showTimers() {
245
- try {
246
- const timersRaw = await fs.promises.readFile(this.timerfiledir, "utf-8");
247
- if (this.isJSONLines) {
248
- const timersData = timersRaw
249
- .split(/\r?\n/)
250
- .filter(t => t.trim())
251
- .map(line => JSON.parse(line));
252
- return timersData;
253
- }
254
- else {
255
- const timersData = timersRaw.split(/\r?\n/);
256
- const timersJSON = [];
257
- for (const timerData of timersData) {
258
- const splitedTimerData = timerData.split(" ");
259
- if (!timerData.trim())
260
- continue;
261
- timersJSON.push({
262
- id: splitedTimerData[0],
263
- start: Number(splitedTimerData[1]),
264
- stop: Number(splitedTimerData[2]),
265
- });
266
- }
267
- return timersJSON;
268
- }
269
- }
270
- catch (e) {
271
- throw new Error(`Error when showing timers: ${e}`);
272
- }
273
- }
274
- }
1
+ export { JSONLTimersManager } from "./JSONLTimersManager.js";
2
+ export { PlainTextTimersManager } from "./PlainTextTimersManager.js";
275
3
  //# sourceMappingURL=index.js.map