@node-cli/timer 2.1.1 → 2.1.2
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/dist/utilities.js +12 -1
- package/dist/utilities.js.map +1 -1
- package/package.json +2 -2
package/dist/utilities.js
CHANGED
|
@@ -2,7 +2,18 @@ import { Logger, Spinner } from "@node-cli/logger";
|
|
|
2
2
|
import moment from "moment";
|
|
3
3
|
import { notify } from "./notify.js";
|
|
4
4
|
const logger = new Logger();
|
|
5
|
-
|
|
5
|
+
/**
|
|
6
|
+
* discardStdin must stay off. When it is on, ora puts stdin in raw mode, which
|
|
7
|
+
* stops the tty from turning Ctrl+C into SIGINT and delivers a bare 0x03 byte
|
|
8
|
+
* instead. stdin-discarder means to re-send the signal itself, but it attaches
|
|
9
|
+
* its reader with prependListener (which, unlike on("data"), does not start a
|
|
10
|
+
* paused stream flowing) and only calls resume() when stdin.isPaused() was true
|
|
11
|
+
* beforehand. On a fresh process.stdin, flowing is null, so isPaused() is false
|
|
12
|
+
* and the resume never happens: the byte is never read, the signal is never
|
|
13
|
+
* re-sent, and Ctrl+C is swallowed for the life of the spinner.
|
|
14
|
+
*/ const spinner = new Spinner({
|
|
15
|
+
discardStdin: false
|
|
16
|
+
});
|
|
6
17
|
const UNITS = {
|
|
7
18
|
ms: 1000,
|
|
8
19
|
s: 1000 * 1000,
|
package/dist/utilities.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/utilities.ts"],"sourcesContent":["import { Logger, Spinner } from \"@node-cli/logger\";\nimport moment, { Duration } from \"moment\";\n\nimport { notify } from \"./notify.js\";\nimport { Configuration } from \"./parse.js\";\n\nconst logger = new Logger();\nconst spinner = new Spinner();\n\nconst UNITS = {\n\tms: 1000,\n\ts: 1000 * 1000,\n\tm: 1000 * 1000 * 60,\n\th: 1000 * 1000 * 60 * 60,\n\td: 1000 * 1000 * 60 * 60 * 24,\n\tw: 1000 * 1000 * 60 * 60 * 24 * 7,\n};\n\nexport const timeToMicroseconds = (value: string, unit: string) => {\n\tconst result = UNITS[unit];\n\treturn result ? Number.parseInt(value, 10) * result : 0;\n};\n\nexport const extractDuration = (config: Configuration): Duration => {\n\tconst { parameters, showHelp } = config;\n\tlet totalMicroseconds = 0;\n\n\t/* v8 ignore next 6 */\n\tif (\n\t\tObject.entries(parameters).length === 0 ||\n\t\tObject.entries(parameters).length > 1\n\t) {\n\t\tshowHelp();\n\t}\n\n\tconst groups = parameters[\"0\"].toLowerCase().match(/[+-]?[\\d.]+[a-z]+/g);\n\n\t/* v8 ignore next 2 */\n\tif (groups === null) {\n\t\tshowHelp();\n\t} else {\n\t\tfor (const g of groups) {\n\t\t\tconst value = g.match(/[\\d.]+/g)[0];\n\t\t\tconst unit = g.match(/[a-z]+/g)[0];\n\t\t\ttotalMicroseconds += timeToMicroseconds(value, unit);\n\t\t}\n\t\treturn moment.duration(totalMicroseconds / UNITS.ms);\n\t}\n};\n\n/* v8 ignore start */\nexport class Timer {\n\tconfig: Configuration;\n\tstartTime: number;\n\ttimerDurationMilliSeconds: number;\n\tintroMessage: string;\n\ttimerDurationSeconds: number;\n\n\tconstructor(config: Configuration) {\n\t\tthis.config = config;\n\t\tconst result: Duration = extractDuration(this.config);\n\t\tthis.timerDurationMilliSeconds = result.asMilliseconds();\n\t\tthis.timerDurationSeconds = result.asSeconds();\n\t\tthis.introMessage = `${result.hours()}h ${result.minutes()}m ${result.seconds()}s`;\n\t}\n\n\tstart = () => {\n\t\tif (this.timerDurationMilliSeconds > 0) {\n\t\t\tlogger.printBox(`${this.introMessage} `, {\n\t\t\t\tpadding: 0,\n\t\t\t\tborderColor: \"cyan\",\n\t\t\t\ttitle: \"Timer Configuration\",\n\t\t\t});\n\n\t\t\tthis.startTime = Date.now();\n\t\t\tspinner.start(this.introMessage);\n\n\t\t\t// update the spinner every second.\n\t\t\tthis.printRemainingTime();\n\t\t\tconst timer = setInterval(this.printRemainingTime, 1000);\n\n\t\t\t// kill the spinner when the timer is done.\n\t\t\tsetTimeout(async () => {\n\t\t\t\tconst message = \"Time's up!\";\n\t\t\t\tclearInterval(timer);\n\t\t\t\tthis.printRemainingTime(message);\n\t\t\t\tspinner.stop();\n\n\t\t\t\t/**\n\t\t\t\t * notifying last, and never fatally: a desktop notification that cannot be\n\t\t\t\t * displayed must not take the timer down with it.\n\t\t\t\t */\n\t\t\t\tif (this.config.flags.notification) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tawait notify({\n\t\t\t\t\t\t\tmessage,\n\t\t\t\t\t\t\tsound: \"Funk\",\n\t\t\t\t\t\t\ttitle: \"Timer Notification\",\n\t\t\t\t\t\t});\n\t\t\t\t\t} catch {\n\t\t\t\t\t\t/**\n\t\t\t\t\t\t * notify is already total, so this only ever catches a future regression\n\t\t\t\t\t\t * in it. Rejecting here would be fatal: an async setTimeout callback has\n\t\t\t\t\t\t * nothing to attach a handler to.\n\t\t\t\t\t\t */\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}, this.timerDurationMilliSeconds);\n\t\t}\n\t};\n\n\tprivate printRemainingTime = (message: string | void) => {\n\t\tif (message) {\n\t\t\tspinner.text = message;\n\t\t} else {\n\t\t\tconst elapsed = (Date.now() - this.startTime) / 1000;\n\t\t\tconst duration = moment.duration(\n\t\t\t\t(this.timerDurationSeconds - elapsed) * 1000,\n\t\t\t);\n\t\t\tspinner.text = `${duration.hours()}h ${duration.minutes()}m ${duration.seconds()}s`;\n\t\t}\n\t};\n}\n/* v8 ignore stop */\n"],"names":["Logger","Spinner","moment","notify","logger","spinner","UNITS","ms","s","m","h","d","w","timeToMicroseconds","value","unit","result","Number","parseInt","extractDuration","config","parameters","showHelp","totalMicroseconds","Object","entries","length","groups","toLowerCase","match","g","duration","Timer","startTime","timerDurationMilliSeconds","introMessage","timerDurationSeconds","asMilliseconds","asSeconds","hours","minutes","seconds","start","printBox","padding","borderColor","title","Date","now","printRemainingTime","timer","setInterval","setTimeout","message","clearInterval","stop","flags","notification","sound","text","elapsed"],"mappings":"AAAA,SAASA,MAAM,EAAEC,OAAO,QAAQ,mBAAmB;AACnD,OAAOC,YAA0B,SAAS;AAE1C,SAASC,MAAM,QAAQ,cAAc;AAGrC,MAAMC,SAAS,IAAIJ;
|
|
1
|
+
{"version":3,"sources":["../src/utilities.ts"],"sourcesContent":["import { Logger, Spinner } from \"@node-cli/logger\";\nimport moment, { Duration } from \"moment\";\n\nimport { notify } from \"./notify.js\";\nimport { Configuration } from \"./parse.js\";\n\nconst logger = new Logger();\n\n/**\n * discardStdin must stay off. When it is on, ora puts stdin in raw mode, which\n * stops the tty from turning Ctrl+C into SIGINT and delivers a bare 0x03 byte\n * instead. stdin-discarder means to re-send the signal itself, but it attaches\n * its reader with prependListener (which, unlike on(\"data\"), does not start a\n * paused stream flowing) and only calls resume() when stdin.isPaused() was true\n * beforehand. On a fresh process.stdin, flowing is null, so isPaused() is false\n * and the resume never happens: the byte is never read, the signal is never\n * re-sent, and Ctrl+C is swallowed for the life of the spinner.\n */\nconst spinner = new Spinner({ discardStdin: false });\n\nconst UNITS = {\n\tms: 1000,\n\ts: 1000 * 1000,\n\tm: 1000 * 1000 * 60,\n\th: 1000 * 1000 * 60 * 60,\n\td: 1000 * 1000 * 60 * 60 * 24,\n\tw: 1000 * 1000 * 60 * 60 * 24 * 7,\n};\n\nexport const timeToMicroseconds = (value: string, unit: string) => {\n\tconst result = UNITS[unit];\n\treturn result ? Number.parseInt(value, 10) * result : 0;\n};\n\nexport const extractDuration = (config: Configuration): Duration => {\n\tconst { parameters, showHelp } = config;\n\tlet totalMicroseconds = 0;\n\n\t/* v8 ignore next 6 */\n\tif (\n\t\tObject.entries(parameters).length === 0 ||\n\t\tObject.entries(parameters).length > 1\n\t) {\n\t\tshowHelp();\n\t}\n\n\tconst groups = parameters[\"0\"].toLowerCase().match(/[+-]?[\\d.]+[a-z]+/g);\n\n\t/* v8 ignore next 2 */\n\tif (groups === null) {\n\t\tshowHelp();\n\t} else {\n\t\tfor (const g of groups) {\n\t\t\tconst value = g.match(/[\\d.]+/g)[0];\n\t\t\tconst unit = g.match(/[a-z]+/g)[0];\n\t\t\ttotalMicroseconds += timeToMicroseconds(value, unit);\n\t\t}\n\t\treturn moment.duration(totalMicroseconds / UNITS.ms);\n\t}\n};\n\n/* v8 ignore start */\nexport class Timer {\n\tconfig: Configuration;\n\tstartTime: number;\n\ttimerDurationMilliSeconds: number;\n\tintroMessage: string;\n\ttimerDurationSeconds: number;\n\n\tconstructor(config: Configuration) {\n\t\tthis.config = config;\n\t\tconst result: Duration = extractDuration(this.config);\n\t\tthis.timerDurationMilliSeconds = result.asMilliseconds();\n\t\tthis.timerDurationSeconds = result.asSeconds();\n\t\tthis.introMessage = `${result.hours()}h ${result.minutes()}m ${result.seconds()}s`;\n\t}\n\n\tstart = () => {\n\t\tif (this.timerDurationMilliSeconds > 0) {\n\t\t\tlogger.printBox(`${this.introMessage} `, {\n\t\t\t\tpadding: 0,\n\t\t\t\tborderColor: \"cyan\",\n\t\t\t\ttitle: \"Timer Configuration\",\n\t\t\t});\n\n\t\t\tthis.startTime = Date.now();\n\t\t\tspinner.start(this.introMessage);\n\n\t\t\t// update the spinner every second.\n\t\t\tthis.printRemainingTime();\n\t\t\tconst timer = setInterval(this.printRemainingTime, 1000);\n\n\t\t\t// kill the spinner when the timer is done.\n\t\t\tsetTimeout(async () => {\n\t\t\t\tconst message = \"Time's up!\";\n\t\t\t\tclearInterval(timer);\n\t\t\t\tthis.printRemainingTime(message);\n\t\t\t\tspinner.stop();\n\n\t\t\t\t/**\n\t\t\t\t * notifying last, and never fatally: a desktop notification that cannot be\n\t\t\t\t * displayed must not take the timer down with it.\n\t\t\t\t */\n\t\t\t\tif (this.config.flags.notification) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tawait notify({\n\t\t\t\t\t\t\tmessage,\n\t\t\t\t\t\t\tsound: \"Funk\",\n\t\t\t\t\t\t\ttitle: \"Timer Notification\",\n\t\t\t\t\t\t});\n\t\t\t\t\t} catch {\n\t\t\t\t\t\t/**\n\t\t\t\t\t\t * notify is already total, so this only ever catches a future regression\n\t\t\t\t\t\t * in it. Rejecting here would be fatal: an async setTimeout callback has\n\t\t\t\t\t\t * nothing to attach a handler to.\n\t\t\t\t\t\t */\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}, this.timerDurationMilliSeconds);\n\t\t}\n\t};\n\n\tprivate printRemainingTime = (message: string | void) => {\n\t\tif (message) {\n\t\t\tspinner.text = message;\n\t\t} else {\n\t\t\tconst elapsed = (Date.now() - this.startTime) / 1000;\n\t\t\tconst duration = moment.duration(\n\t\t\t\t(this.timerDurationSeconds - elapsed) * 1000,\n\t\t\t);\n\t\t\tspinner.text = `${duration.hours()}h ${duration.minutes()}m ${duration.seconds()}s`;\n\t\t}\n\t};\n}\n/* v8 ignore stop */\n"],"names":["Logger","Spinner","moment","notify","logger","spinner","discardStdin","UNITS","ms","s","m","h","d","w","timeToMicroseconds","value","unit","result","Number","parseInt","extractDuration","config","parameters","showHelp","totalMicroseconds","Object","entries","length","groups","toLowerCase","match","g","duration","Timer","startTime","timerDurationMilliSeconds","introMessage","timerDurationSeconds","asMilliseconds","asSeconds","hours","minutes","seconds","start","printBox","padding","borderColor","title","Date","now","printRemainingTime","timer","setInterval","setTimeout","message","clearInterval","stop","flags","notification","sound","text","elapsed"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@node-cli/timer",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.2",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"author": "Arno Versini",
|
|
6
6
|
"description": "Dead simpler CLI timer",
|
|
@@ -46,5 +46,5 @@
|
|
|
46
46
|
"@vitest/coverage-v8": "4.1.10",
|
|
47
47
|
"vitest": "4.1.10"
|
|
48
48
|
},
|
|
49
|
-
"gitHead": "
|
|
49
|
+
"gitHead": "9fdd995083ea81292321f20a3397122a18369b87"
|
|
50
50
|
}
|