From e2c23c607fcfb974954a82eadb455ddbeb0f9ccb Mon Sep 17 00:00:00 2001 From: Robert Vane Date: Sun, 28 May 2023 13:30:58 +0300 Subject: [PATCH 1/2] Add CONFIG action. Change enum names to CamelCase convention. Bump minor version. Add Changelog entry. Create "validateConfigOverride" static method on Timer object. Use "validateConfigOverride" static method to validate config override inside "setConfigOverride" method. Replace public "getState" method on Timer object with public "state" getter. Restructure logic inside the node to reduce duplication and nesting. --- CHANGELOG | 3 ++ package.json | 2 +- src/controltimer.ejs | 34 ++++++++++++--- src/controltimer.ts | 70 ++++++++++++++++--------------- src/node-config.ts | 13 ++++-- src/render-html.ts | 4 +- src/timer.ts | 98 ++++++++++++++++++++++++++------------------ 7 files changed, 141 insertions(+), 83 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index ccaede8..9ae13a1 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,6 @@ +## v0.5.0 (28.05.2023) + * Implement new CONFIG action to allow overriding configuration without starting the timer. + ## v0.4.1 (27.05.2023) * Bugfix: When the timer was in a paused state and received an override configuration, it failed to update the visible timer progress. diff --git a/package.json b/package.json index 7d8aaa6..6277ecd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "node-red-contrib-controltimer", - "version": "0.4.1", + "version": "0.5.0", "description": "A controllable Node-RED timer node. Start, Stop, Reset, Pause and Continue the timer. Use the timer as a loop or a delay.", "scripts": { "build": "scripty", diff --git a/src/controltimer.ejs b/src/controltimer.ejs index dcd8f7b..9f403f7 100644 --- a/src/controltimer.ejs +++ b/src/controltimer.ejs @@ -39,6 +39,7 @@ isResetActionEnabled: { value: <%= defaults.isResetActionEnabled %> }, isPauseActionEnabled: { value: <%= defaults.isPauseActionEnabled %> }, isContinueActionEnabled: { value: <%= defaults.isContinueActionEnabled %> }, + isConfigActionEnabled: { value: <%= defaults.isConfigActionEnabled %> }, actionPropertyNameType: { value: '<%= defaults.actionPropertyNameType %>' }, actionPropertyName: { value: '<%= defaults.actionPropertyName %>', required: true, validate: RED.validators.typedInput('actionPropertyNameType') }, startActionNameType: { value: '<%= defaults.startActionNameType %>' }, @@ -51,12 +52,14 @@ pauseActionName: { value: '<%= defaults.pauseActionName %>', required: true, validate: RED.validators.typedInput('pauseActionNameType') }, continueActionNameType: { value: '<%= defaults.continueActionNameType %>' }, continueActionName: { value: '<%= defaults.continueActionName %>', required: true, validate: RED.validators.typedInput('continueActionNameType') }, + configActionNameType: { value: '<%= defaults.configActionNameType %>' }, + configActionName: { value: '<%= defaults.configActionName %>', required: true, validate: RED.validators.typedInput('configActionNameType') }, }, inputs: 1, outputs: 2, icon: "controltimer.png", label: function () { - return this.name || `${this.timerDuration} ${this.timerDurationUnit} ${this.timerType === '<%= TIMER_TYPE.DELAY %>' ? '<%= TIMER_TYPE.DELAY %>' : '<%= TIMER_TYPE.LOOP %>'}`; + return this.name || `${this.timerDuration} ${this.timerDurationUnit} ${this.timerType === '<%= TimerType.DELAY %>' ? '<%= TimerType.DELAY %>' : '<%= TimerType.LOOP %>'}`; }, labelStyle: function () { return this.name ? 'node_label_italic' : ''; @@ -166,12 +169,14 @@ initializeNodeRedCheckbox('isResetActionEnabled'); initializeNodeRedCheckbox('isPauseActionEnabled'); initializeNodeRedCheckbox('isContinueActionEnabled'); + initializeNodeRedCheckbox('isConfigActionEnabled'); initializeNodeRedTypedTextInput('actionPropertyName', ['str']); initializeNodeRedTypedTextInput('startActionName', ['str']); initializeNodeRedTypedTextInput('stopActionName', ['str']); initializeNodeRedTypedTextInput('resetActionName', ['str']); initializeNodeRedTypedTextInput('pauseActionName', ['str']); initializeNodeRedTypedTextInput('continueActionName', ['str']); + initializeNodeRedTypedTextInput('configActionName', ['str']); } initializeInputs(); @@ -180,11 +185,11 @@ $('#node-input-timerType').change(() => { const selectedTimerType = $('#node-input-timerType').val(); - if (selectedTimerType === '<%= TIMER_TYPE.LOOP %>') { + if (selectedTimerType === '<%= TimerType.LOOP %>') { $('.loop-options').show(); } - if (selectedTimerType === '<%= TIMER_TYPE.DELAY %>') { + if (selectedTimerType === '<%= TimerType.DELAY %>') { $('.loop-options').hide(); } }); @@ -225,6 +230,7 @@ this.isStopActionEnabled = elementDefaults.isStopActionEnabled; this.isPauseActionEnabled = elementDefaults.isPauseActionEnabled; this.isContinueActionEnabled = elementDefaults.isContinueActionEnabled; + this.isConfigActionEnabled = elementDefaults.isConfigActionEnabled; this.actionPropertyNameType = elementDefaults.actionPropertyNameType; this.actionPropertyName = elementDefaults.actionPropertyName; this.startActionNameType = elementDefaults.startActionNameType; @@ -237,6 +243,8 @@ this.pauseActionName = elementDefaults.pauseActionName; this.continueActionNameType = elementDefaults.continueActionNameType; this.continueActionName = elementDefaults.continueActionName; + this.configActionNameType = elementDefaults.configActionNameType; + this.configActionName = elementDefaults.configActionName; initializeInputs(); }); @@ -289,6 +297,7 @@ this.isStopActionEnabled = $('#node-input-isStopActionEnabled').is(":checked"); this.isPauseActionEnabled = $('#node-input-isPauseActionEnabled').is(":checked"); this.isContinueActionEnabled = $('#node-input-isContinueActionEnabled').is(":checked"); + this.isConfigActionEnabled = $('#node-input-isConfigActionEnabled').is(":checked"); this.actionPropertyNameType = $('#node-input-actionPropertyNameType').val(); this.actionPropertyName = $('#node-input-actionPropertyName').val(); this.startActionNameType = $('#node-input-startActionNameType').val(); @@ -301,6 +310,8 @@ this.pauseActionName = $('#node-input-pauseActionName').val(); this.continueActionNameType = $('#node-input-continueActionNameType').val(); this.continueActionName = $('#node-input-continueActionName').val(); + this.configActionNameType = $('#node-input-configActionNameType').val(); + this.configActionName = $('#node-input-configActionName').val(); }, }); @@ -386,8 +397,8 @@
@@ -573,6 +584,13 @@ +
+ +
+ +
+
+
@@ -608,6 +626,12 @@
+ +
+ + + +
diff --git a/src/controltimer.ts b/src/controltimer.ts index 0549801..eaf1511 100644 --- a/src/controltimer.ts +++ b/src/controltimer.ts @@ -1,7 +1,7 @@ import { Node, NodeAPI, NodeMessageInFlow } from 'node-red'; import { constants, ControlTimerNodeDef, nodeName } from './node-config'; -import { STATE, Timer } from './timer'; +import { State, Timer } from './timer'; type NodeMessage = NodeMessageInFlow; @@ -27,25 +27,25 @@ module.exports = function (RED: NodeAPI): void { }); timer.on('state', ({ state, progress }) => { - if (state === STATE.IDLE) { + if (state === State.IDLE) { node.status({ fill: 'grey', shape: 'ring', text: 'Idle' }); } - if (state === STATE.RUNNING) { + if (state === State.RUNNING) { node.status({ fill: 'green', shape: 'dot', text: `Running${progress}` }); } - if (state === STATE.STOPPED) { + if (state === State.STOPPED) { node.status({ fill: 'red', shape: 'dot', text: 'Stopped' }); } - if (state === STATE.PAUSED) { + if (state === State.PAUSED) { node.status({ fill: 'yellow', shape: 'dot', text: `Paused${progress}` }); } }); - timer.on(STATE.STOPPED, () => node.send([null, getHaltedMessage()])); - timer.on(STATE.PAUSED, () => node.send([null, getHaltedMessage()])); + timer.on(State.STOPPED, () => node.send([null, getHaltedMessage()])); + timer.on(State.PAUSED, () => node.send([null, getHaltedMessage()])); timer.on('timer', () => node.send([getTriggerMessage(), null])); timer.on('loop-timeout', () => node.send([null, getMessage(config.loopTimeoutMessage)])); timer.on('loop-max-iterations', () => node.send([null, getMessage(config.loopMaxIterationsMessage)])); @@ -58,14 +58,27 @@ module.exports = function (RED: NodeAPI): void { const isPauseActionMessage = message[config.actionPropertyName] === config.pauseActionName && config.isPauseActionEnabled; const isContinueActionMessage = message[config.actionPropertyName] === config.continueActionName && config.isContinueActionEnabled; const isStopActionMessage = message[config.actionPropertyName] === config.stopActionName && config.isStopActionEnabled; - const isUnknownMessage = !(isStartActionMessage || isResetActionMessage || isPauseActionMessage || isContinueActionMessage || isStopActionMessage); + const isConfigActionMessage = message[config.actionPropertyName] === config.configActionName && config.isConfigActionEnabled; + const isUnknownMessage = !( + isStartActionMessage || + isResetActionMessage || + isPauseActionMessage || + isContinueActionMessage || + isStopActionMessage || + isConfigActionMessage + ); const timerTypeOverride = message[constants.timerTypeOverridePropertyName] ?? null; const timerDurationOverride = message[constants.timerDurationOverridePropertyName] ?? null; const timerDurationUnitOverride = message[constants.timerDurationUnitOverridePropertyName] ?? null; - const isOverrideMessage = timerTypeOverride !== null && timerDurationOverride !== null && timerDurationUnitOverride !== null; + const hasConfigOverride = timerTypeOverride !== null && timerDurationOverride !== null && timerDurationUnitOverride !== null; + + if (isConfigActionMessage && !hasConfigOverride) { + done(new Error('Config override not found or is invalid')); + return; + } - if (isStartActionMessage && isOverrideMessage) { + if (!isUnknownMessage && hasConfigOverride) { timer.setConfigOverride({ timerType: timerTypeOverride, duration: timerDurationOverride, @@ -73,29 +86,11 @@ module.exports = function (RED: NodeAPI): void { }); } - if (timer.getState() === STATE.PAUSED) { - if (isStartActionMessage && config.continueTimerOnReceivalOfStartAction) { - timer.continue(); - done(); - return; - } - } - - if (timer.getState() !== STATE.RUNNING) { - if (isStartActionMessage || (isUnknownMessage && config.startTimerOnReceivalOfUnknownMessage)) { - timer.start(); - done(); - return; - } - } - if ( - isResetActionMessage || - (isUnknownMessage && config.resetTimerOnReceivalOfUnknownMessage) || - (isStartActionMessage && config.resetTimerOnReceivalOfStartAction) || - (isStartActionMessage && isOverrideMessage) + (isStartActionMessage || (isUnknownMessage && config.startTimerOnReceivalOfUnknownMessage)) && + (timer.state === State.IDLE || timer.state === State.STOPPED) ) { - timer.reset(); + timer.start(); done(); return; } @@ -112,11 +107,22 @@ module.exports = function (RED: NodeAPI): void { return; } - if (isContinueActionMessage) { + if (isContinueActionMessage || (timer.state === State.PAUSED && isStartActionMessage && config.continueTimerOnReceivalOfStartAction)) { timer.continue(); done(); return; } + + if ( + isResetActionMessage || + (isUnknownMessage && config.resetTimerOnReceivalOfUnknownMessage) || + (isStartActionMessage && config.resetTimerOnReceivalOfStartAction) || + (isStartActionMessage && hasConfigOverride) + ) { + timer.reset(); + done(); + return; + } }); node.on('close', (done) => { diff --git a/src/node-config.ts b/src/node-config.ts index e11ea3f..5273903 100644 --- a/src/node-config.ts +++ b/src/node-config.ts @@ -1,6 +1,6 @@ import { NodeDef } from 'node-red'; -import { DurationUnit, TIMER_TYPE } from './timer'; +import { DurationUnit, TimerType } from './timer'; export const nodeName = 'controltimer'; export type ControlTimerNodeDef = NodeDef & Props; @@ -18,10 +18,11 @@ export type StopActionNameType = 'str'; export type ResetActionNameType = 'str'; export type PauseActionNameType = 'str'; export type ContinueActionNameType = 'str'; +export type ConfigActionNameType = 'str'; export interface Props { name: string; - timerType: TIMER_TYPE; + timerType: TimerType; timerDurationUnit: DurationUnit; timerDurationType: TimerDurationType; @@ -56,6 +57,7 @@ export interface Props { isResetActionEnabled: boolean; isPauseActionEnabled: boolean; isContinueActionEnabled: boolean; + isConfigActionEnabled: boolean; actionPropertyNameType: ActionPropertyNameType; actionPropertyName: string; startActionNameType: StartActionNameType; @@ -68,11 +70,13 @@ export interface Props { pauseActionName: string; continueActionNameType: ContinueActionNameType; continueActionName: string; + configActionNameType: ConfigActionNameType; + configActionName: string; } export const defaults: Props = { name: '', - timerType: TIMER_TYPE.DELAY, + timerType: TimerType.DELAY, timerDurationUnit: DurationUnit.SECOND, timerDurationType: 'num', @@ -107,6 +111,7 @@ export const defaults: Props = { isResetActionEnabled: true, isPauseActionEnabled: true, isContinueActionEnabled: true, + isConfigActionEnabled: true, actionPropertyNameType: 'str', actionPropertyName: 'payload', startActionNameType: 'str', @@ -119,6 +124,8 @@ export const defaults: Props = { pauseActionName: 'PAUSE', continueActionNameType: 'str', continueActionName: 'CONTINUE', + configActionNameType: 'str', + configActionName: 'CONFIG', }; export const constants = { diff --git a/src/render-html.ts b/src/render-html.ts index 106ed41..1d2411a 100644 --- a/src/render-html.ts +++ b/src/render-html.ts @@ -3,12 +3,12 @@ import * as fs from 'fs'; import * as path from 'path'; import { defaults, nodeName } from './node-config'; -import { DurationUnit, TIMER_TYPE } from './timer'; +import { DurationUnit, TimerType } from './timer'; (() => { const srcEjsPath = path.resolve('src/controltimer.ejs'); const ejsTemplate = fs.readFileSync(srcEjsPath, 'utf8'); - const nodeHtml = ejs.render(ejsTemplate, { defaults, nodeName, TIMER_TYPE, DurationUnit }); + const nodeHtml = ejs.render(ejsTemplate, { defaults, nodeName, TimerType, DurationUnit }); const distHtmlPath = path.resolve('dist/controltimer.html'); fs.writeFileSync(distHtmlPath, nodeHtml); })(); diff --git a/src/timer.ts b/src/timer.ts index c6ad26e..432a187 100644 --- a/src/timer.ts +++ b/src/timer.ts @@ -1,7 +1,7 @@ import { EventEmitter } from 'events'; import { clearInterval, clearTimeout } from 'timers'; -enum ACTION { +enum Action { START = 'START', STOP = 'STOP', RESET = 'RESET', @@ -9,14 +9,14 @@ enum ACTION { CONTINUE = 'CONTINUE', } -export enum STATE { +export enum State { IDLE = 'IDLE', RUNNING = 'RUNNING', STOPPED = 'STOPPED', PAUSED = 'PAUSED', } -export enum TIMER_TYPE { +export enum TimerType { LOOP = 'loop', DELAY = 'delay', } @@ -29,7 +29,7 @@ export enum DurationUnit { } interface TimerConfig { - timerType: TIMER_TYPE; + timerType: TimerType; duration: number; durationUnit: DurationUnit; isTimerProgressUpdateEnabled: boolean; @@ -40,7 +40,7 @@ interface TimerConfig { export class Timer extends EventEmitter { static defaultConfig: TimerConfig = { - timerType: TIMER_TYPE.DELAY, + timerType: TimerType.DELAY, duration: 5, durationUnit: DurationUnit.SECOND, isTimerProgressUpdateEnabled: true, @@ -49,6 +49,23 @@ export class Timer extends EventEmitter { timerLoopTimeoutUnit: DurationUnit.MILLISECOND, }; + static validateConfigOverride(configOverride: Pick) { + const { timerType, duration, durationUnit } = configOverride; + const isNil = (value: unknown) => value === undefined || value === null; + + if (isNil(timerType) || typeof timerType !== 'string' || !Object.values(TimerType).includes(timerType)) { + throw new Error('timerType is not valid'); + } + + if (isNil(duration) || !Number.isInteger(duration) || !Number.isFinite(duration) || duration <= 0) { + throw new Error('duration is not valid'); + } + + if (isNil(durationUnit) || typeof durationUnit !== 'string' || !Object.values(DurationUnit).includes(durationUnit)) { + throw new Error('durationUnit is not valid'); + } + } + static getInstance(config: TimerConfig) { return new Timer(config); } @@ -56,7 +73,7 @@ export class Timer extends EventEmitter { private config: TimerConfig = Timer.defaultConfig; private configOverride: Pick; - private currentState: STATE = STATE.IDLE; + private currentState: State = State.IDLE; private timerId: NodeJS.Timeout; private progressUpdateIntervalTimerId: NodeJS.Timeout; private stoppedTransitionToIdleTimeoutTimerId: NodeJS.Timeout; @@ -68,7 +85,7 @@ export class Timer extends EventEmitter { constructor(config: TimerConfig) { super(); this.config = config; // TODO: Validate config - this.setCurrentState(STATE.IDLE); + this.setCurrentState(State.IDLE); } // #################### @@ -76,23 +93,23 @@ export class Timer extends EventEmitter { // #################### public start() { - this.handleAction(ACTION.START); + this.handleAction(Action.START); } public stop() { - this.handleAction(ACTION.STOP); + this.handleAction(Action.STOP); } public reset() { - this.handleAction(ACTION.RESET); + this.handleAction(Action.RESET); } public pause() { - this.handleAction(ACTION.PAUSE); + this.handleAction(Action.PAUSE); } public continue() { - this.handleAction(ACTION.CONTINUE); + this.handleAction(Action.CONTINUE); } public hardReset() { @@ -101,7 +118,7 @@ export class Timer extends EventEmitter { } private softReset() { - this.currentState = STATE.IDLE; + this.currentState = State.IDLE; this.destroyTimers(); this.currentLoopIteration = 0; this.pausedTimerRunningMilliseconds = undefined; @@ -109,15 +126,16 @@ export class Timer extends EventEmitter { } public setConfigOverride(configOverride: Pick) { - this.configOverride = configOverride; // TODO: Validate configOverride + Timer.validateConfigOverride(configOverride); + this.configOverride = configOverride; this.emit('state', { state: this.currentState, - progress: this.currentState === STATE.PAUSED ? this.getPausedTimerProgress() : this.getRunningTimerProgress(), + progress: this.currentState === State.PAUSED ? this.getPausedTimerProgress() : this.getRunningTimerProgress(), }); } - public getState() { + public get state() { return this.currentState; } @@ -125,43 +143,43 @@ export class Timer extends EventEmitter { // ## Timer action handling ## // ########################### - private handleAction(action: ACTION) { - if (this.currentState === STATE.IDLE) { - if (action === ACTION.START) { + private handleAction(action: Action) { + if (this.currentState === State.IDLE) { + if (action === Action.START) { this.startTimer(); } } - if (this.currentState === STATE.RUNNING) { - if (action === ACTION.STOP) { + if (this.currentState === State.RUNNING) { + if (action === Action.STOP) { this.stopTimer(); } - if (action === ACTION.RESET) { + if (action === Action.RESET) { this.resetTimer(); } - if (action === ACTION.PAUSE) { + if (action === Action.PAUSE) { this.pauseTimer(); } } - if (this.currentState === STATE.STOPPED) { - if (action === ACTION.START) { + if (this.currentState === State.STOPPED) { + if (action === Action.START) { this.startTimer(); } } - if (this.currentState === STATE.PAUSED) { - if (action === ACTION.STOP) { + if (this.currentState === State.PAUSED) { + if (action === Action.STOP) { this.stopTimer(); } - if (action === ACTION.RESET) { + if (action === Action.RESET) { this.resetTimer(); } - if (action === ACTION.CONTINUE) { + if (action === Action.CONTINUE) { this.continueTimer(); } } @@ -174,20 +192,20 @@ export class Timer extends EventEmitter { private startTimer() { this.softReset(); this.timerId = this.createAndGetTimer(); - this.setCurrentState(STATE.RUNNING, this.getRunningTimerProgress()); + this.setCurrentState(State.RUNNING, this.getRunningTimerProgress()); this.startProgressUpdateTimer(); } private stopTimer() { this.hardReset(); - this.setCurrentState(STATE.STOPPED); + this.setCurrentState(State.STOPPED); this.startStoppedTransitionToIdleTimer(); } private resetTimer() { this.softReset(); this.timerId = this.createAndGetTimer(); - this.setCurrentState(STATE.RUNNING, this.getRunningTimerProgress()); + this.setCurrentState(State.RUNNING, this.getRunningTimerProgress()); this.startProgressUpdateTimer(); } @@ -196,18 +214,18 @@ export class Timer extends EventEmitter { const previousRunningDurationInMilliseconds = this.pausedTimerRunningMilliseconds ?? 0; this.pausedTimerRunningMilliseconds = Date.now() - this.timerStartedAtUnixTimestamp + previousRunningDurationInMilliseconds; this.timerStartedAtUnixTimestamp = undefined; - this.setCurrentState(STATE.PAUSED, this.getPausedTimerProgress()); + this.setCurrentState(State.PAUSED, this.getPausedTimerProgress()); } private continueTimer() { this.timerId = this.createAndGetTimer(this.timerDurationInMilliseconds - this.pausedTimerRunningMilliseconds); - this.setCurrentState(STATE.RUNNING, this.getRunningTimerProgress()); + this.setCurrentState(State.RUNNING, this.getRunningTimerProgress()); this.startProgressUpdateTimer(); } private finishTimer() { this.hardReset(); - this.setCurrentState(STATE.IDLE); + this.setCurrentState(State.IDLE); } // ########################### @@ -217,7 +235,7 @@ export class Timer extends EventEmitter { private createAndGetTimer(durationInMillisecondsOverride?: number) { const durationInMilliseconds = durationInMillisecondsOverride ?? this.timerDurationInMilliseconds; - if ((this.config.timerType === TIMER_TYPE.LOOP && !this.configOverride) || this.configOverride?.timerType === TIMER_TYPE.LOOP) { + if ((this.config.timerType === TimerType.LOOP && !this.configOverride) || this.configOverride?.timerType === TimerType.LOOP) { this.pausedTimerRunningMilliseconds = durationInMillisecondsOverride ? this.pausedTimerRunningMilliseconds : undefined; this.timerStartedAtUnixTimestamp = Date.now(); @@ -242,7 +260,7 @@ export class Timer extends EventEmitter { }, durationInMilliseconds); } - if ((this.config.timerType === TIMER_TYPE.DELAY && !this.configOverride) || this.configOverride?.timerType === TIMER_TYPE.DELAY) { + if ((this.config.timerType === TimerType.DELAY && !this.configOverride) || this.configOverride?.timerType === TimerType.DELAY) { this.pausedTimerRunningMilliseconds = durationInMillisecondsOverride ? this.pausedTimerRunningMilliseconds : undefined; this.timerStartedAtUnixTimestamp = Date.now(); @@ -295,7 +313,7 @@ export class Timer extends EventEmitter { } this.progressUpdateIntervalTimerId = setInterval(() => { - this.setCurrentState(STATE.RUNNING, this.getRunningTimerProgress()); + this.setCurrentState(State.RUNNING, this.getRunningTimerProgress()); }, 50); } @@ -303,7 +321,7 @@ export class Timer extends EventEmitter { clearTimeout(this.stoppedTransitionToIdleTimeoutTimerId); this.stoppedTransitionToIdleTimeoutTimerId = undefined; - if (this.currentState !== STATE.STOPPED) { + if (this.currentState !== State.STOPPED) { return; } @@ -316,7 +334,7 @@ export class Timer extends EventEmitter { // ## Other utility functions ## // ############################# - private setCurrentState(state: STATE, progress = '') { + private setCurrentState(state: State, progress = '') { this.currentState = state; this.emit('state', { state, progress }); this.emit(state); From 401c0a714269796e4cc64a8d4d6be8e9116918ef Mon Sep 17 00:00:00 2001 From: Robert Vane Date: Sun, 28 May 2023 13:32:09 +0300 Subject: [PATCH 2/2] Update package-lock.json --- package-lock.json | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3170027..232644d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "node-red-contrib-controltimer", - "version": "0.4.1", + "version": "0.5.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "node-red-contrib-controltimer", - "version": "0.4.1", + "version": "0.5.0", "license": "MIT", "devDependencies": { "@types/ejs": "^3.1.1", @@ -205,9 +205,9 @@ } }, "node_modules/@isaacs/cliui/node_modules/strip-ansi": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", - "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", "dev": true, "dependencies": { "ansi-regex": "^6.0.1" @@ -1334,9 +1334,9 @@ } }, "node_modules/boxen/node_modules/strip-ansi": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", - "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", "dev": true, "dependencies": { "ansi-regex": "^6.0.1" @@ -5949,9 +5949,9 @@ } }, "node_modules/string-width/node_modules/strip-ansi": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", - "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", "dev": true, "dependencies": { "ansi-regex": "^6.0.1"