From ef9e3b334b977bd79231adb7e59c3875313d32dc Mon Sep 17 00:00:00 2001 From: =?utf8?q?J=C3=A9r=C3=B4me=20Benoit?= Date: Thu, 3 Aug 2023 13:26:00 +0200 Subject: [PATCH] refactor: factor out charging schedule composition code MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Signed-off-by: Jérôme Benoit --- package.json | 8 +- pnpm-lock.yaml | 98 +++---- src/charging-station/Helpers.ts | 54 +++- .../ocpp/1.6/OCPP16IncomingRequestService.ts | 275 +++--------------- .../ocpp/1.6/OCPP16ServiceUtils.ts | 118 ++++++++ src/types/ocpp/1.6/ChargingProfile.ts | 2 +- ui/web/package.json | 4 +- ui/web/pnpm-lock.yaml | 82 +++--- 8 files changed, 304 insertions(+), 337 deletions(-) diff --git a/package.json b/package.json index a8004c94..3d334e97 100644 --- a/package.json +++ b/package.json @@ -133,7 +133,7 @@ "@rollup/plugin-typescript": "^11.1.2", "@types/mocha": "^10.0.1", "@types/mochawesome": "^6.2.1", - "@types/node": "^20.4.5", + "@types/node": "^20.4.6", "@types/sinon": "^10.0.16", "@types/tar": "^6.1.5", "@types/ws": "^8.5.5", @@ -145,7 +145,7 @@ "clinic": "^13.0.0", "cross-env": "^7.0.3", "eslint": "^8.46.0", - "eslint-config-prettier": "^8.9.0", + "eslint-config-prettier": "^8.10.0", "eslint-import-resolver-typescript": "^3.5.5", "eslint-plugin-import": "^2.28.0", "eslint-plugin-jsdoc": "^46.4.5", @@ -157,11 +157,11 @@ "lint-staged": "^13.2.3", "mocha": "^10.2.0", "mochawesome": "^7.1.3", - "prettier": "^3.0.0", + "prettier": "^3.0.1", "release-it": "^16.1.3", "rimraf": "^5.0.1", "robohydra": "^0.6.9", - "rollup": "^3.27.0", + "rollup": "^3.27.1", "rollup-plugin-analyzer": "^4.0.0", "rollup-plugin-delete": "^2.0.0", "semver": "^7.5.4", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a9f15e80..d7db8897 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -102,13 +102,13 @@ devDependencies: version: 5.1.0(release-it@16.1.3) '@rollup/plugin-json': specifier: ^6.0.0 - version: 6.0.0(rollup@3.27.0) + version: 6.0.0(rollup@3.27.1) '@rollup/plugin-terser': specifier: ^0.4.3 - version: 0.4.3(rollup@3.27.0) + version: 0.4.3(rollup@3.27.1) '@rollup/plugin-typescript': specifier: ^11.1.2 - version: 11.1.2(rollup@3.27.0)(tslib@2.6.1)(typescript@5.1.6) + version: 11.1.2(rollup@3.27.1)(tslib@2.6.1)(typescript@5.1.6) '@types/mocha': specifier: ^10.0.1 version: 10.0.1 @@ -116,8 +116,8 @@ devDependencies: specifier: ^6.2.1 version: 6.2.1 '@types/node': - specifier: ^20.4.5 - version: 20.4.5 + specifier: ^20.4.6 + version: 20.4.6 '@types/sinon': specifier: ^10.0.16 version: 10.0.16 @@ -152,8 +152,8 @@ devDependencies: specifier: ^8.46.0 version: 8.46.0 eslint-config-prettier: - specifier: ^8.9.0 - version: 8.9.0(eslint@8.46.0) + specifier: ^8.10.0 + version: 8.10.0(eslint@8.46.0) eslint-import-resolver-typescript: specifier: ^3.5.5 version: 3.5.5(@typescript-eslint/parser@6.2.1)(eslint-plugin-import@2.28.0)(eslint@8.46.0) @@ -168,7 +168,7 @@ devDependencies: version: 16.0.1(eslint@8.46.0) eslint-plugin-prettier: specifier: ^5.0.0 - version: 5.0.0(eslint-config-prettier@8.9.0)(eslint@8.46.0)(prettier@3.0.0) + version: 5.0.0(eslint-config-prettier@8.10.0)(eslint@8.46.0)(prettier@3.0.1) eslint-plugin-tsdoc: specifier: ^0.2.17 version: 0.2.17 @@ -188,8 +188,8 @@ devDependencies: specifier: ^7.1.3 version: 7.1.3(mocha@10.2.0) prettier: - specifier: ^3.0.0 - version: 3.0.0 + specifier: ^3.0.1 + version: 3.0.1 release-it: specifier: ^16.1.3 version: 16.1.3 @@ -200,8 +200,8 @@ devDependencies: specifier: ^0.6.9 version: 0.6.9(bufferutil@4.0.7)(utf-8-validate@6.0.3) rollup: - specifier: ^3.27.0 - version: 3.27.0 + specifier: ^3.27.1 + version: 3.27.1 rollup-plugin-analyzer: specifier: ^4.0.0 version: 4.0.0 @@ -216,7 +216,7 @@ devDependencies: version: 15.2.0 ts-node: specifier: ^10.9.1 - version: 10.9.1(@types/node@20.4.5)(typescript@5.1.6) + version: 10.9.1(@types/node@20.4.6)(typescript@5.1.6) typescript: specifier: ^5.1.6 version: 5.1.6 @@ -524,15 +524,15 @@ packages: '@commitlint/execute-rule': 17.4.0 '@commitlint/resolve-extends': 17.6.7 '@commitlint/types': 17.4.4 - '@types/node': 20.4.5 + '@types/node': 20.4.6 chalk: 4.1.2 cosmiconfig: 8.2.0 - cosmiconfig-typescript-loader: 4.4.0(@types/node@20.4.5)(cosmiconfig@8.2.0)(ts-node@10.9.1)(typescript@5.1.6) + cosmiconfig-typescript-loader: 4.4.0(@types/node@20.4.6)(cosmiconfig@8.2.0)(ts-node@10.9.1)(typescript@5.1.6) lodash.isplainobject: 4.0.6 lodash.merge: 4.6.2 lodash.uniq: 4.5.0 resolve-from: 5.0.0 - ts-node: 10.9.1(@types/node@20.4.5)(typescript@5.1.6) + ts-node: 10.9.1(@types/node@20.4.6)(typescript@5.1.6) typescript: 5.1.6 transitivePeerDependencies: - '@swc/core' @@ -742,7 +742,7 @@ packages: '@jest/schemas': 29.6.0 '@types/istanbul-lib-coverage': 2.0.4 '@types/istanbul-reports': 3.0.1 - '@types/node': 20.4.5 + '@types/node': 20.4.6 '@types/yargs': 17.0.24 chalk: 4.1.2 dev: true @@ -1272,7 +1272,7 @@ packages: semver: 7.5.4 dev: true - /@rollup/plugin-json@6.0.0(rollup@3.27.0): + /@rollup/plugin-json@6.0.0(rollup@3.27.1): resolution: {integrity: sha512-i/4C5Jrdr1XUarRhVu27EEwjt4GObltD7c+MkCIpO2QIbojw8MUs+CCTqOphQi3Qtg1FLmYt+l+6YeoIf51J7w==} engines: {node: '>=14.0.0'} peerDependencies: @@ -1281,11 +1281,11 @@ packages: rollup: optional: true dependencies: - '@rollup/pluginutils': 5.0.2(rollup@3.27.0) - rollup: 3.27.0 + '@rollup/pluginutils': 5.0.2(rollup@3.27.1) + rollup: 3.27.1 dev: true - /@rollup/plugin-terser@0.4.3(rollup@3.27.0): + /@rollup/plugin-terser@0.4.3(rollup@3.27.1): resolution: {integrity: sha512-EF0oejTMtkyhrkwCdg0HJ0IpkcaVg1MMSf2olHb2Jp+1mnLM04OhjpJWGma4HobiDTF0WCyViWuvadyE9ch2XA==} engines: {node: '>=14.0.0'} peerDependencies: @@ -1294,13 +1294,13 @@ packages: rollup: optional: true dependencies: - rollup: 3.27.0 + rollup: 3.27.1 serialize-javascript: 6.0.1 smob: 1.4.0 terser: 5.19.2 dev: true - /@rollup/plugin-typescript@11.1.2(rollup@3.27.0)(tslib@2.6.1)(typescript@5.1.6): + /@rollup/plugin-typescript@11.1.2(rollup@3.27.1)(tslib@2.6.1)(typescript@5.1.6): resolution: {integrity: sha512-0ghSOCMcA7fl1JM+0gYRf+Q/HWyg+zg7/gDSc+fRLmlJWcW5K1I+CLRzaRhXf4Y3DRyPnnDo4M2ktw+a6JcDEg==} engines: {node: '>=14.0.0'} peerDependencies: @@ -1313,14 +1313,14 @@ packages: tslib: optional: true dependencies: - '@rollup/pluginutils': 5.0.2(rollup@3.27.0) + '@rollup/pluginutils': 5.0.2(rollup@3.27.1) resolve: 1.22.2 - rollup: 3.27.0 + rollup: 3.27.1 tslib: 2.6.1 typescript: 5.1.6 dev: true - /@rollup/pluginutils@5.0.2(rollup@3.27.0): + /@rollup/pluginutils@5.0.2(rollup@3.27.1): resolution: {integrity: sha512-pTd9rIsP92h+B6wWwFbW8RkZv4hiR/xKsqre4SIuAOaOEQRxi0lqLke9k2/7WegC85GgUs9pjmOjCUi3In4vwA==} engines: {node: '>=14.0.0'} peerDependencies: @@ -1332,7 +1332,7 @@ packages: '@types/estree': 1.0.1 estree-walker: 2.0.2 picomatch: 2.3.1 - rollup: 3.27.0 + rollup: 3.27.1 dev: true /@sinclair/typebox@0.27.8: @@ -1454,7 +1454,7 @@ packages: resolution: {integrity: sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==} dependencies: '@types/minimatch': 5.1.2 - '@types/node': 20.4.5 + '@types/node': 20.4.6 dev: true /@types/http-cache-semantics@4.0.1: @@ -1510,8 +1510,8 @@ packages: /@types/node@17.0.45: resolution: {integrity: sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==} - /@types/node@20.4.5: - resolution: {integrity: sha512-rt40Nk13II9JwQBdeYqmbn2Q6IVTA5uPhvSO+JVqdXw/6/4glI6oR9ezty/A9Hg5u7JH4OmYmuQ+XvjKm0Datg==} + /@types/node@20.4.6: + resolution: {integrity: sha512-q0RkvNgMweWWIvSMDiXhflGUKMdIxBo2M2tYM/0kEGDueQByFzK4KZAgu5YHGFNxziTlppNpTIBcqHQAxlfHdA==} /@types/normalize-package-data@2.4.1: resolution: {integrity: sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==} @@ -1546,7 +1546,7 @@ packages: /@types/tar@6.1.5: resolution: {integrity: sha512-qm2I/RlZij5RofuY7vohTpYNaYcrSQlN2MyjucQc7ZweDwaEWkdN/EeNh6e9zjK6uEm6PwjdMXkcj05BxZdX1Q==} dependencies: - '@types/node': 20.4.5 + '@types/node': 20.4.6 minipass: 4.2.8 dev: true @@ -1565,14 +1565,14 @@ packages: /@types/whatwg-url@8.2.2: resolution: {integrity: sha512-FtQu10RWgn3D9U4aazdwIE2yzphmTJREDqNdODHrbrZmmMqI0vMheC/6NE/J1Yveaj8H+ela+YwWTjq5PGmuhA==} dependencies: - '@types/node': 20.4.5 + '@types/node': 20.4.6 '@types/webidl-conversions': 7.0.0 dev: false /@types/ws@8.5.5: resolution: {integrity: sha512-lwhs8hktwxSjf9UaZ9tG5M03PGogvFaH8gUgLNbN9HKIg0dvv6q+gkSuJ8HN4/VbyxkuLzCjlN7GquQ0gUJfIg==} dependencies: - '@types/node': 20.4.5 + '@types/node': 20.4.6 dev: true /@types/yargs-parser@21.0.0: @@ -3096,7 +3096,7 @@ packages: resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} dev: true - /cosmiconfig-typescript-loader@4.4.0(@types/node@20.4.5)(cosmiconfig@8.2.0)(ts-node@10.9.1)(typescript@5.1.6): + /cosmiconfig-typescript-loader@4.4.0(@types/node@20.4.6)(cosmiconfig@8.2.0)(ts-node@10.9.1)(typescript@5.1.6): resolution: {integrity: sha512-BabizFdC3wBHhbI4kJh0VkQP9GkBfoHPydD0COMce1nJ1kJAB3F2TmJ/I7diULBKtmEWSwEbuN/KDtgnmUUVmw==} engines: {node: '>=v14.21.3'} peerDependencies: @@ -3105,9 +3105,9 @@ packages: ts-node: '>=10' typescript: '>=4' dependencies: - '@types/node': 20.4.5 + '@types/node': 20.4.6 cosmiconfig: 8.2.0 - ts-node: 10.9.1(@types/node@20.4.5)(typescript@5.1.6) + ts-node: 10.9.1(@types/node@20.4.6)(typescript@5.1.6) typescript: 5.1.6 dev: true @@ -3968,8 +3968,8 @@ packages: source-map: 0.6.1 dev: true - /eslint-config-prettier@8.9.0(eslint@8.46.0): - resolution: {integrity: sha512-+sbni7NfVXnOpnRadUA8S28AUlsZt9GjgFvABIRL9Hkn8KqNzOp+7Lw4QWtrwn20KzU3wqu1QoOj2m+7rKRqkA==} + /eslint-config-prettier@8.10.0(eslint@8.46.0): + resolution: {integrity: sha512-SM8AMJdeQqRYT9O9zguiruQZaN7+z+E4eAP9oiLNGKMtomwaB1E9dcgUD6ZAn/eQAb52USbvezbiljfZUhbJcg==} hasBin: true peerDependencies: eslint: '>=7.0.0' @@ -4125,7 +4125,7 @@ packages: semver: 7.5.4 dev: true - /eslint-plugin-prettier@5.0.0(eslint-config-prettier@8.9.0)(eslint@8.46.0)(prettier@3.0.0): + /eslint-plugin-prettier@5.0.0(eslint-config-prettier@8.10.0)(eslint@8.46.0)(prettier@3.0.1): resolution: {integrity: sha512-AgaZCVuYDXHUGxj/ZGu1u8H8CYgDY3iG6w5kUFw4AzMVXzB7VvbKgYR4nATIN+OvUrghMbiDLeimVjVY5ilq3w==} engines: {node: ^14.18.0 || >=16.0.0} peerDependencies: @@ -4140,8 +4140,8 @@ packages: optional: true dependencies: eslint: 8.46.0 - eslint-config-prettier: 8.9.0(eslint@8.46.0) - prettier: 3.0.0 + eslint-config-prettier: 8.10.0(eslint@8.46.0) + prettier: 3.0.1 prettier-linter-helpers: 1.0.0 synckit: 0.8.5 dev: true @@ -4367,7 +4367,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@jest/expect-utils': 29.6.2 - '@types/node': 20.4.5 + '@types/node': 20.4.6 jest-get-type: 29.4.3 jest-matcher-utils: 29.6.2 jest-message-util: 29.6.2 @@ -5917,7 +5917,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@jest/types': 29.6.1 - '@types/node': 20.4.5 + '@types/node': 20.4.6 chalk: 4.1.2 ci-info: 3.8.0 graceful-fs: 4.2.11 @@ -7784,8 +7784,8 @@ packages: fast-diff: 1.3.0 dev: true - /prettier@3.0.0: - resolution: {integrity: sha512-zBf5eHpwHOGPC47h0zrPyNn+eAEIdEzfywMoYn2XPi0P44Zp0tSq64rq0xAREh4auw2cJZHo9QUob+NqCQky4g==} + /prettier@3.0.1: + resolution: {integrity: sha512-fcOWSnnpCrovBsmFZIGIy9UqK2FaI7Hqax+DIO0A9UxeVoY4iweyaFjS5TavZN97Hfehph0nhsZnjlVKzEQSrQ==} engines: {node: '>=14'} hasBin: true dev: true @@ -8407,8 +8407,8 @@ packages: del: 5.1.0 dev: true - /rollup@3.27.0: - resolution: {integrity: sha512-aOltLCrYZ0FhJDm7fCqwTjIUEVjWjcydKBV/Zeid6Mn8BWgDCUBBWT5beM5ieForYNo/1ZHuGJdka26kvQ3Gzg==} + /rollup@3.27.1: + resolution: {integrity: sha512-tXNDFwOkN6C2w5Blj1g6ForKeFw6c1mDu5jxoeDO3/pmYjgt+8yvIFjKzH5FQUq70OKZBkOt0zzv0THXL7vwzQ==} engines: {node: '>=14.18.0', npm: '>=8.0.0'} hasBin: true optionalDependencies: @@ -9360,7 +9360,7 @@ packages: code-block-writer: 12.0.0 dev: false - /ts-node@10.9.1(@types/node@20.4.5)(typescript@5.1.6): + /ts-node@10.9.1(@types/node@20.4.6)(typescript@5.1.6): resolution: {integrity: sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==} hasBin: true peerDependencies: @@ -9379,7 +9379,7 @@ packages: '@tsconfig/node12': 1.0.11 '@tsconfig/node14': 1.0.3 '@tsconfig/node16': 1.0.4 - '@types/node': 20.4.5 + '@types/node': 20.4.6 acorn: 8.10.0 acorn-walk: 8.2.0 arg: 4.1.3 diff --git a/src/charging-station/Helpers.ts b/src/charging-station/Helpers.ts index b7229262..dd6bcee1 100644 --- a/src/charging-station/Helpers.ts +++ b/src/charging-station/Helpers.ts @@ -761,6 +761,15 @@ const getLimitFromChargingProfiles = ( // OCPP specifies that if startSchedule is not defined, it should be relative to start of the connector transaction chargingSchedule.startSchedule = connectorStatus?.transactionStart; } + if ( + !isNullOrUndefined(chargingSchedule?.startSchedule) && + !isDate(chargingSchedule?.startSchedule) + ) { + logger.warn( + `${logPrefix} ${moduleName}.getLimitFromChargingProfiles: Charging profile id ${chargingProfile.chargingProfileId} startSchedule property is not a Date instance. Trying to convert it to a Date instance`, + ); + chargingSchedule.startSchedule = convertToDate(chargingSchedule?.startSchedule)!; + } if ( !isNullOrUndefined(chargingSchedule?.startSchedule) && isNullOrUndefined(chargingSchedule?.duration) @@ -771,12 +780,6 @@ const getLimitFromChargingProfiles = ( // OCPP specifies that if duration is not defined, it should be infinite chargingSchedule.duration = differenceInSeconds(maxTime, chargingSchedule.startSchedule!); } - if (!isDate(chargingSchedule?.startSchedule)) { - logger.warn( - `${logPrefix} ${moduleName}.getLimitFromChargingProfiles: Charging profile id ${chargingProfile.chargingProfileId} startSchedule property is not a Date object. Trying to convert it to a Date object`, - ); - chargingSchedule.startSchedule = convertToDate(chargingSchedule?.startSchedule)!; - } if (!prepareChargingProfileKind(connectorStatus, chargingProfile, currentDate, logPrefix)) { continue; } @@ -785,7 +788,6 @@ const getLimitFromChargingProfiles = ( } // Check if the charging profile is active if ( - isValidTime(chargingSchedule?.startSchedule) && isWithinInterval(currentDate, { start: chargingSchedule.startSchedule!, end: addSeconds(chargingSchedule.startSchedule!, chargingSchedule.duration!), @@ -891,8 +893,10 @@ export const prepareChargingProfileKind = ( ); delete chargingProfile.chargingSchedule.startSchedule; } - connectorStatus?.transactionStarted && - (chargingProfile.chargingSchedule.startSchedule = connectorStatus?.transactionStart); + if (connectorStatus?.transactionStarted) { + chargingProfile.chargingSchedule.startSchedule = connectorStatus?.transactionStart; + } + // FIXME: Handle relative charging profile duration break; } return true; @@ -914,10 +918,30 @@ export const canProceedChargingProfile = ( ); return false; } - const chargingSchedule = chargingProfile.chargingSchedule; - if (isNullOrUndefined(chargingSchedule?.startSchedule)) { + if ( + isNullOrUndefined(chargingProfile.chargingSchedule.startSchedule) || + isNullOrUndefined(chargingProfile.chargingSchedule.duration) + ) { logger.error( - `${logPrefix} ${moduleName}.canProceedChargingProfile: Charging profile id ${chargingProfile.chargingProfileId} has no startSchedule defined`, + `${logPrefix} ${moduleName}.canProceedChargingProfile: Charging profile id ${chargingProfile.chargingProfileId} has no startSchedule or duration defined`, + ); + return false; + } + if ( + !isNullOrUndefined(chargingProfile.chargingSchedule.startSchedule) && + !isValidTime(chargingProfile.chargingSchedule.startSchedule) + ) { + logger.error( + `${logPrefix} ${moduleName}.canProceedChargingProfile: Charging profile id ${chargingProfile.chargingProfileId} has an invalid startSchedule date defined`, + ); + return false; + } + if ( + !isNullOrUndefined(chargingProfile.chargingSchedule.duration) && + !Number.isSafeInteger(chargingProfile.chargingSchedule.duration) + ) { + logger.error( + `${logPrefix} ${moduleName}.canProceedChargingProfile: Charging profile id ${chargingProfile.chargingProfileId} has non integer duration defined`, ); return false; } @@ -937,6 +961,12 @@ const canProceedRecurringChargingProfile = ( ); return false; } + if (isNullOrUndefined(chargingProfile.chargingSchedule.startSchedule)) { + logger.error( + `${logPrefix} ${moduleName}.canProceedRecurringChargingProfile: Recurring charging profile id ${chargingProfile.chargingProfileId} has no startSchedule defined`, + ); + return false; + } return true; }; diff --git a/src/charging-station/ocpp/1.6/OCPP16IncomingRequestService.ts b/src/charging-station/ocpp/1.6/OCPP16IncomingRequestService.ts index d24a2d4c..02154148 100644 --- a/src/charging-station/ocpp/1.6/OCPP16IncomingRequestService.ts +++ b/src/charging-station/ocpp/1.6/OCPP16IncomingRequestService.ts @@ -6,19 +6,7 @@ import { URL, fileURLToPath } from 'node:url'; import type { JSONSchemaType } from 'ajv'; import { Client, type FTPResponse } from 'basic-ftp'; -import { - addSeconds, - differenceInSeconds, - isAfter, - isBefore, - isDate, - isWithinInterval, - max, - maxTime, - min, - minTime, - secondsToMilliseconds, -} from 'date-fns'; +import { addSeconds, differenceInSeconds, isDate, maxTime, secondsToMilliseconds } from 'date-fns'; import { create } from 'tar'; import { OCPP16Constants } from './OCPP16Constants'; @@ -60,9 +48,7 @@ import { OCPP16ChargePointStatus, type OCPP16ChargingProfile, OCPP16ChargingProfilePurposeType, - OCPP16ChargingRateUnitType, type OCPP16ChargingSchedule, - type OCPP16ChargingSchedulePeriod, type OCPP16ClearCacheRequest, type OCPP16DataTransferRequest, type OCPP16DataTransferResponse, @@ -115,7 +101,6 @@ import { isNotEmptyString, isNullOrUndefined, isUndefined, - isValidTime, logger, sleep, } from '../../../utils'; @@ -700,59 +685,63 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { return OCPP16Constants.OCPP_RESPONSE_REJECTED; } const currentDate = new Date(); - const interval: Interval = { + const compositeScheduleInterval: Interval = { start: currentDate, end: addSeconds(currentDate, duration), }; // Get charging profiles sorted by connector id then stack level - const storedChargingProfiles: OCPP16ChargingProfile[] = getConnectorChargingProfiles( + const chargingProfiles: OCPP16ChargingProfile[] = getConnectorChargingProfiles( chargingStation, connectorId, ); - const chargingProfiles: OCPP16ChargingProfile[] = []; - for (const storedChargingProfile of storedChargingProfiles) { + let previousCompositeSchedule: OCPP16ChargingSchedule | undefined; + let compositeSchedule: OCPP16ChargingSchedule | undefined; + for (const chargingProfile of chargingProfiles) { if ( - isNullOrUndefined(storedChargingProfile.chargingSchedule?.startSchedule) && + isNullOrUndefined(chargingProfile.chargingSchedule?.startSchedule) && connectorStatus?.transactionStarted ) { logger.debug( `${chargingStation.logPrefix()} ${moduleName}.handleRequestGetCompositeSchedule: Charging profile id ${ - storedChargingProfile.chargingProfileId + chargingProfile.chargingProfileId } has no startSchedule defined. Trying to set it to the connector current transaction start date`, ); // OCPP specifies that if startSchedule is not defined, it should be relative to start of the connector transaction - storedChargingProfile.chargingSchedule.startSchedule = connectorStatus?.transactionStart; + chargingProfile.chargingSchedule.startSchedule = connectorStatus?.transactionStart; } if ( - !isNullOrUndefined(storedChargingProfile.chargingSchedule?.startSchedule) && - isNullOrUndefined(storedChargingProfile.chargingSchedule?.duration) + !isNullOrUndefined(chargingProfile.chargingSchedule?.startSchedule) && + !isDate(chargingProfile.chargingSchedule?.startSchedule) + ) { + logger.warn( + `${chargingStation.logPrefix()} ${moduleName}.handleRequestGetCompositeSchedule: Charging profile id ${ + chargingProfile.chargingProfileId + } startSchedule property is not a Date instance. Trying to convert it to a Date instance`, + ); + chargingProfile.chargingSchedule.startSchedule = convertToDate( + chargingProfile.chargingSchedule?.startSchedule, + )!; + } + if ( + !isNullOrUndefined(chargingProfile.chargingSchedule?.startSchedule) && + isNullOrUndefined(chargingProfile.chargingSchedule?.duration) ) { logger.debug( - `${chargingStation.logPrefix()} ${moduleName}.getLimitFromChargingProfiles: Charging profile id ${ - storedChargingProfile.chargingProfileId + `${chargingStation.logPrefix()} ${moduleName}.handleRequestGetCompositeSchedule: Charging profile id ${ + chargingProfile.chargingProfileId } has no duration defined and will be set to the maximum time allowed`, ); // OCPP specifies that if duration is not defined, it should be infinite - storedChargingProfile.chargingSchedule.duration = differenceInSeconds( + chargingProfile.chargingSchedule.duration = differenceInSeconds( maxTime, - storedChargingProfile.chargingSchedule.startSchedule!, + chargingProfile.chargingSchedule.startSchedule!, ); } - if (!isDate(storedChargingProfile.chargingSchedule?.startSchedule)) { - logger.warn( - `${chargingStation.logPrefix()} ${moduleName}.handleRequestGetCompositeSchedule: Charging profile id ${ - storedChargingProfile.chargingProfileId - } startSchedule property is not a Date object. Trying to convert it to a Date object`, - ); - storedChargingProfile.chargingSchedule.startSchedule = convertToDate( - storedChargingProfile.chargingSchedule?.startSchedule, - )!; - } if ( !prepareChargingProfileKind( connectorStatus, - storedChargingProfile, - interval.start as Date, + chargingProfile, + compositeScheduleInterval.start as Date, chargingStation.logPrefix(), ) ) { @@ -760,199 +749,29 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { } if ( !canProceedChargingProfile( - storedChargingProfile, - interval.start as Date, + chargingProfile, + compositeScheduleInterval.start as Date, chargingStation.logPrefix(), ) ) { continue; } - // Add active charging profiles into chargingProfiles array - if ( - isValidTime(storedChargingProfile.chargingSchedule?.startSchedule) && - isWithinInterval(storedChargingProfile.chargingSchedule.startSchedule!, interval) - ) { - if (isEmptyArray(chargingProfiles)) { - if ( - isAfter( - addSeconds( - storedChargingProfile.chargingSchedule.startSchedule!, - storedChargingProfile.chargingSchedule.duration!, - ), - interval.end, - ) - ) { - storedChargingProfile.chargingSchedule.chargingSchedulePeriod = - storedChargingProfile.chargingSchedule.chargingSchedulePeriod.filter( - (schedulePeriod) => - isWithinInterval( - addSeconds( - storedChargingProfile.chargingSchedule.startSchedule!, - schedulePeriod.startPeriod, - )!, - interval, - ), - ); - storedChargingProfile.chargingSchedule.duration = differenceInSeconds( - interval.end, - storedChargingProfile.chargingSchedule.startSchedule!, - ); - } - chargingProfiles.push(storedChargingProfile); - } else if (isNotEmptyArray(chargingProfiles)) { - const chargingProfilesInterval: Interval = { - start: min( - chargingProfiles.map( - (chargingProfile) => chargingProfile.chargingSchedule.startSchedule ?? maxTime, - ), - ), - end: max( - chargingProfiles.map( - (chargingProfile) => - addSeconds( - chargingProfile.chargingSchedule.startSchedule!, - chargingProfile.chargingSchedule.duration!, - ) ?? minTime, - ), - ), - }; - let addChargingProfile = false; - if ( - isBefore(interval.start, chargingProfilesInterval.start) && - isBefore( - storedChargingProfile.chargingSchedule.startSchedule!, - chargingProfilesInterval.start, - ) - ) { - // Remove charging schedule periods that are after the start of the active profiles interval - storedChargingProfile.chargingSchedule.chargingSchedulePeriod = - storedChargingProfile.chargingSchedule.chargingSchedulePeriod.filter( - (schedulePeriod) => - isWithinInterval( - addSeconds( - storedChargingProfile.chargingSchedule.startSchedule!, - schedulePeriod.startPeriod, - ), - { - start: interval.start, - end: chargingProfilesInterval.start, - }, - ), - ); - addChargingProfile = true; - } - if ( - isBefore(chargingProfilesInterval.end, interval.end) && - isAfter( - addSeconds( - storedChargingProfile.chargingSchedule.startSchedule!, - storedChargingProfile.chargingSchedule.duration!, - ), - chargingProfilesInterval.end, - ) - ) { - // Remove charging schedule periods that are before the end of the active profiles interval - storedChargingProfile.chargingSchedule.chargingSchedulePeriod = - storedChargingProfile.chargingSchedule.chargingSchedulePeriod.filter( - (schedulePeriod, index) => { - if ( - isWithinInterval( - addSeconds( - storedChargingProfile.chargingSchedule.startSchedule!, - schedulePeriod.startPeriod, - ), - { - start: chargingProfilesInterval.end, - end: interval.end, - }, - ) - ) { - return true; - } - if ( - !isWithinInterval( - addSeconds( - storedChargingProfile.chargingSchedule.startSchedule!, - schedulePeriod.startPeriod, - ), - { - start: chargingProfilesInterval.end, - end: interval.end, - }, - ) && - index < - storedChargingProfile.chargingSchedule.chargingSchedulePeriod.length - 1 && - isWithinInterval( - addSeconds( - storedChargingProfile.chargingSchedule.startSchedule!, - storedChargingProfile.chargingSchedule.chargingSchedulePeriod[index + 1] - .startPeriod, - ), - { - start: chargingProfilesInterval.end, - end: interval.end, - }, - ) - ) { - return true; - } - return false; - }, - ); - addChargingProfile = true; - } - addChargingProfile && chargingProfiles.push(storedChargingProfile); - } - } - } - const compositeScheduleStart: Date = min( - chargingProfiles.map( - (chargingProfile) => chargingProfile.chargingSchedule.startSchedule ?? maxTime, - ), - ); - const compositeScheduleDuration: number = Math.max( - ...chargingProfiles.map((chargingProfile) => - isNaN(chargingProfile.chargingSchedule.duration!) - ? -Infinity - : chargingProfile.chargingSchedule.duration!, - ), - ); - const compositeSchedulePeriods: OCPP16ChargingSchedulePeriod[] = chargingProfiles - .map((chargingProfile) => chargingProfile.chargingSchedule.chargingSchedulePeriod) - .reduce( - (accumulator, value) => - accumulator.concat(value).sort((a, b) => a.startPeriod - b.startPeriod), - [], + compositeSchedule = OCPP16ServiceUtils.composeChargingSchedules( + previousCompositeSchedule, + chargingProfile.chargingSchedule, + compositeScheduleInterval, ); - const compositeSchedule: OCPP16ChargingSchedule = { - startSchedule: compositeScheduleStart, - duration: compositeScheduleDuration, - chargingRateUnit: chargingProfiles.every( - (chargingProfile) => - chargingProfile.chargingSchedule.chargingRateUnit === OCPP16ChargingRateUnitType.AMPERE, - ) - ? OCPP16ChargingRateUnitType.AMPERE - : chargingProfiles.every( - (chargingProfile) => - chargingProfile.chargingSchedule.chargingRateUnit === OCPP16ChargingRateUnitType.WATT, - ) - ? OCPP16ChargingRateUnitType.WATT - : OCPP16ChargingRateUnitType.AMPERE, - chargingSchedulePeriod: compositeSchedulePeriods, - minChargeRate: Math.min( - ...chargingProfiles.map((chargingProfile) => - isNaN(chargingProfile.chargingSchedule.minChargeRate!) - ? Infinity - : chargingProfile.chargingSchedule.minChargeRate!, - ), - ), - }; - return { - status: GenericStatus.Accepted, - scheduleStart: compositeSchedule.startSchedule!, - connectorId, - chargingSchedule: compositeSchedule, - }; + previousCompositeSchedule = compositeSchedule; + } + if (compositeSchedule) { + return { + status: GenericStatus.Accepted, + scheduleStart: compositeSchedule.startSchedule!, + connectorId, + chargingSchedule: compositeSchedule, + }; + } + return OCPP16Constants.OCPP_RESPONSE_REJECTED; } private handleRequestClearChargingProfile( diff --git a/src/charging-station/ocpp/1.6/OCPP16ServiceUtils.ts b/src/charging-station/ocpp/1.6/OCPP16ServiceUtils.ts index 502258c2..b510051e 100644 --- a/src/charging-station/ocpp/1.6/OCPP16ServiceUtils.ts +++ b/src/charging-station/ocpp/1.6/OCPP16ServiceUtils.ts @@ -1,6 +1,14 @@ // Partial Copyright Jerome Benoit. 2021-2023. All Rights Reserved. import type { JSONSchemaType } from 'ajv'; +import { + addSeconds, + areIntervalsOverlapping, + differenceInSeconds, + isAfter, + isBefore, + isWithinInterval, +} from 'date-fns'; import { OCPP16Constants } from './OCPP16Constants'; import { @@ -25,6 +33,7 @@ import { type OCPP16ChangeAvailabilityResponse, OCPP16ChargePointStatus, type OCPP16ChargingProfile, + type OCPP16ChargingSchedule, type OCPP16IncomingRequestCommand, type OCPP16MeterValue, OCPP16MeterValueMeasurand, @@ -923,6 +932,52 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils { return clearedCP; }; + public static composeChargingSchedules = ( + chargingSchedule1: OCPP16ChargingSchedule | undefined, + chargingSchedule2: OCPP16ChargingSchedule | undefined, + targetInterval: Interval, + ): OCPP16ChargingSchedule | undefined => { + if (!chargingSchedule1 && !chargingSchedule2) { + return undefined; + } + if (chargingSchedule1 && !chargingSchedule2) { + return OCPP16ServiceUtils.composeChargingSchedule(chargingSchedule1, targetInterval); + } + if (!chargingSchedule1 && chargingSchedule2) { + return OCPP16ServiceUtils.composeChargingSchedule(chargingSchedule2, targetInterval); + } + const compositeChargingSchedule1: OCPP16ChargingSchedule | undefined = + OCPP16ServiceUtils.composeChargingSchedule(chargingSchedule1!, targetInterval); + const compositeChargingSchedule2: OCPP16ChargingSchedule | undefined = + OCPP16ServiceUtils.composeChargingSchedule(chargingSchedule2!, targetInterval); + const compositeChargingScheduleInterval1: Interval = { + start: compositeChargingSchedule1!.startSchedule!, + end: addSeconds( + compositeChargingSchedule1!.startSchedule!, + compositeChargingSchedule1!.duration!, + ), + }; + const compositeChargingScheduleInterval2: Interval = { + start: compositeChargingSchedule2!.startSchedule!, + end: addSeconds( + compositeChargingSchedule2!.startSchedule!, + compositeChargingSchedule2!.duration!, + ), + }; + if ( + !areIntervalsOverlapping( + compositeChargingScheduleInterval1, + compositeChargingScheduleInterval2, + ) + ) { + return { + ...OCPP16ServiceUtils.composeChargingSchedule(chargingSchedule1!, targetInterval)!, + ...OCPP16ServiceUtils.composeChargingSchedule(chargingSchedule2!, targetInterval)!, + }; + } + // FIXME: Handle overlapping intervals + }; + public static hasReservation = ( chargingStation: ChargingStation, connectorId: number, @@ -964,6 +1019,69 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils { ); } + private static composeChargingSchedule = ( + chargingSchedule: OCPP16ChargingSchedule, + targetInterval: Interval, + ): OCPP16ChargingSchedule | undefined => { + const chargingScheduleInterval: Interval = { + start: chargingSchedule.startSchedule!, + end: addSeconds(chargingSchedule.startSchedule!, chargingSchedule.duration!), + }; + if (areIntervalsOverlapping(chargingScheduleInterval, targetInterval)) { + chargingSchedule.chargingSchedulePeriod.sort((a, b) => a.startPeriod - b.startPeriod); + if (isBefore(chargingScheduleInterval.start, targetInterval.start)) { + return { + ...chargingSchedule, + startSchedule: targetInterval.start as Date, + duration: differenceInSeconds(chargingScheduleInterval.end, targetInterval.start as Date), + chargingSchedulePeriod: chargingSchedule.chargingSchedulePeriod.filter( + (schedulePeriod, index) => { + if ( + isWithinInterval( + addSeconds(chargingScheduleInterval.start, schedulePeriod.startPeriod)!, + targetInterval, + ) + ) { + return true; + } + if ( + index < chargingSchedule.chargingSchedulePeriod.length - 1 && + !isWithinInterval( + addSeconds(chargingScheduleInterval.start, schedulePeriod.startPeriod), + targetInterval, + ) && + isWithinInterval( + addSeconds( + chargingScheduleInterval.start, + chargingSchedule.chargingSchedulePeriod[index + 1].startPeriod, + ), + targetInterval, + ) + ) { + schedulePeriod.startPeriod = 0; + return true; + } + return false; + }, + ), + }; + } + if (isAfter(chargingScheduleInterval.end, targetInterval.end)) { + return { + ...chargingSchedule, + duration: differenceInSeconds(targetInterval.end as Date, chargingScheduleInterval.start), + chargingSchedulePeriod: chargingSchedule.chargingSchedulePeriod.filter((schedulePeriod) => + isWithinInterval( + addSeconds(chargingScheduleInterval.start, schedulePeriod.startPeriod)!, + targetInterval, + ), + ), + }; + } + return chargingSchedule; + } + }; + private static buildSampledValue( sampledValueTemplate: SampledValueTemplate, value: number, diff --git a/src/types/ocpp/1.6/ChargingProfile.ts b/src/types/ocpp/1.6/ChargingProfile.ts index 430f6c28..8a50e895 100644 --- a/src/types/ocpp/1.6/ChargingProfile.ts +++ b/src/types/ocpp/1.6/ChargingProfile.ts @@ -13,8 +13,8 @@ export interface OCPP16ChargingProfile extends JsonObject { } export interface OCPP16ChargingSchedule extends JsonObject { - duration?: number; startSchedule?: Date; + duration?: number; chargingRateUnit: OCPP16ChargingRateUnitType; chargingSchedulePeriod: OCPP16ChargingSchedulePeriod[]; minChargeRate?: number; diff --git a/ui/web/package.json b/ui/web/package.json index 4f373db6..7a98040a 100644 --- a/ui/web/package.json +++ b/ui/web/package.json @@ -38,7 +38,7 @@ "devDependencies": { "@tsconfig/node20": "^20.1.0", "@types/jsdom": "^21.1.1", - "@types/node": "^20.4.5", + "@types/node": "^20.4.6", "@typescript-eslint/eslint-plugin": "^6.2.1", "@typescript-eslint/parser": "^6.2.1", "@vitejs/plugin-vue": "^4.2.3", @@ -55,7 +55,7 @@ "eslint-plugin-import": "^2.28.0", "eslint-plugin-vue": "^9.16.1", "jsdom": "^22.1.0", - "prettier": "^3.0.0", + "prettier": "^3.0.1", "rimraf": "^5.0.1", "typescript": "~5.1.6", "vite": "^4.4.8", diff --git a/ui/web/pnpm-lock.yaml b/ui/web/pnpm-lock.yaml index e62135ce..f4ec5a17 100644 --- a/ui/web/pnpm-lock.yaml +++ b/ui/web/pnpm-lock.yaml @@ -29,8 +29,8 @@ devDependencies: specifier: ^21.1.1 version: 21.1.1 '@types/node': - specifier: ^20.4.5 - version: 20.4.5 + specifier: ^20.4.6 + version: 20.4.6 '@typescript-eslint/eslint-plugin': specifier: ^6.2.1 version: 6.2.1(@typescript-eslint/parser@6.2.1)(eslint@8.46.0)(typescript@5.1.6) @@ -48,7 +48,7 @@ devDependencies: version: 0.34.1(vitest@0.34.1) '@vue/eslint-config-prettier': specifier: ^8.0.0 - version: 8.0.0(eslint@8.46.0)(prettier@3.0.0) + version: 8.0.0(eslint@8.46.0)(prettier@3.0.1) '@vue/eslint-config-typescript': specifier: ^11.0.3 version: 11.0.3(eslint-plugin-vue@9.16.1)(eslint@8.46.0)(typescript@5.1.6) @@ -80,8 +80,8 @@ devDependencies: specifier: ^22.1.0 version: 22.1.0 prettier: - specifier: ^3.0.0 - version: 3.0.0 + specifier: ^3.0.1 + version: 3.0.1 rimraf: specifier: ^5.0.1 version: 5.0.1 @@ -90,7 +90,7 @@ devDependencies: version: 5.1.6 vite: specifier: ^4.4.8 - version: 4.4.8(@types/node@20.4.5) + version: 4.4.8(@types/node@20.4.6) vitest: specifier: ^0.34.1 version: 0.34.1(jsdom@22.1.0) @@ -785,7 +785,7 @@ packages: /@types/jsdom@21.1.1: resolution: {integrity: sha512-cZFuoVLtzKP3gmq9eNosUL1R50U+USkbLtUQ1bYVgl/lKp0FZM7Cq4aIHAL8oIvQ17uSHi7jXPtfDOdjPwBE7A==} dependencies: - '@types/node': 20.4.5 + '@types/node': 20.4.6 '@types/tough-cookie': 4.0.2 parse5: 7.1.2 dev: true @@ -798,8 +798,8 @@ packages: resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} dev: true - /@types/node@20.4.5: - resolution: {integrity: sha512-rt40Nk13II9JwQBdeYqmbn2Q6IVTA5uPhvSO+JVqdXw/6/4glI6oR9ezty/A9Hg5u7JH4OmYmuQ+XvjKm0Datg==} + /@types/node@20.4.6: + resolution: {integrity: sha512-q0RkvNgMweWWIvSMDiXhflGUKMdIxBo2M2tYM/0kEGDueQByFzK4KZAgu5YHGFNxziTlppNpTIBcqHQAxlfHdA==} dev: true /@types/semver@7.5.0: @@ -1082,7 +1082,7 @@ packages: '@babel/core': 7.22.9 '@babel/plugin-transform-typescript': 7.22.9(@babel/core@7.22.9) '@vue/babel-plugin-jsx': 1.1.5(@babel/core@7.22.9) - vite: 4.4.8(@types/node@20.4.5) + vite: 4.4.8(@types/node@20.4.6) vue: 3.3.4 transitivePeerDependencies: - supports-color @@ -1095,7 +1095,7 @@ packages: vite: ^4.0.0 vue: ^3.2.25 dependencies: - vite: 4.4.8(@types/node@20.4.5) + vite: 4.4.8(@types/node@20.4.6) vue: 3.3.4 dev: true @@ -1219,16 +1219,16 @@ packages: resolution: {integrity: sha512-o9KfBeaBmCKl10usN4crU53fYtC1r7jJwdGKjPT24t348rHxgfpZ0xL3Xm/gLUYnc0oTp8LAmrxOeLyu6tbk2Q==} dev: false - /@vue/eslint-config-prettier@8.0.0(eslint@8.46.0)(prettier@3.0.0): + /@vue/eslint-config-prettier@8.0.0(eslint@8.46.0)(prettier@3.0.1): resolution: {integrity: sha512-55dPqtC4PM/yBjhAr+yEw6+7KzzdkBuLmnhBrDfp4I48+wy+Giqqj9yUr5T2uD/BkBROjjmqnLZmXRdOx/VtQg==} peerDependencies: eslint: '>= 8.0.0' prettier: '>= 3.0.0' dependencies: eslint: 8.46.0 - eslint-config-prettier: 8.9.0(eslint@8.46.0) - eslint-plugin-prettier: 5.0.0(eslint-config-prettier@8.9.0)(eslint@8.46.0)(prettier@3.0.0) - prettier: 3.0.0 + eslint-config-prettier: 8.10.0(eslint@8.46.0) + eslint-plugin-prettier: 5.0.0(eslint-config-prettier@8.10.0)(eslint@8.46.0)(prettier@3.0.1) + prettier: 3.0.1 transitivePeerDependencies: - '@types/eslint' dev: true @@ -1518,8 +1518,8 @@ packages: engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true dependencies: - caniuse-lite: 1.0.30001518 - electron-to-chromium: 1.4.480 + caniuse-lite: 1.0.30001519 + electron-to-chromium: 1.4.482 node-releases: 2.0.13 update-browserslist-db: 1.0.11(browserslist@4.21.10) dev: true @@ -1553,8 +1553,8 @@ packages: engines: {node: '>=10'} dev: true - /caniuse-lite@1.0.30001518: - resolution: {integrity: sha512-rup09/e3I0BKjncL+FesTayKtPrdwKhUufQFd3riFw1hHg8JmIFoInYfB102cFcY/pPgGmdyl/iy+jgiDi2vdA==} + /caniuse-lite@1.0.30001519: + resolution: {integrity: sha512-0QHgqR+Jv4bxHMp8kZ1Kn8CH55OikjKJ6JmKkZYP1F3D7w+lnFXF70nG5eNfsZS89jadi5Ywy5UCSKLAglIRkg==} dev: true /chai@4.3.7: @@ -1828,8 +1828,8 @@ packages: resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} dev: false - /electron-to-chromium@1.4.480: - resolution: {integrity: sha512-IXTgg+bITkQv/FLP9FjX6f9KFCs5hQWeh5uNSKxB9mqYj/JXhHDbu+ekS43LVvbkL3eW6/oZy4+r9Om6lan1Uw==} + /electron-to-chromium@1.4.482: + resolution: {integrity: sha512-h+UqpfmEr1Qkk0zp7ej/jid7CXoq4m4QzW6wNTb0ELJ/BZCpA4wgUylBIMGCe621tnr4l5VmoHjdoSx2lbnNJA==} dev: true /emoji-regex@8.0.0: @@ -1976,8 +1976,8 @@ packages: engines: {node: '>=10'} dev: true - /eslint-config-prettier@8.9.0(eslint@8.46.0): - resolution: {integrity: sha512-+sbni7NfVXnOpnRadUA8S28AUlsZt9GjgFvABIRL9Hkn8KqNzOp+7Lw4QWtrwn20KzU3wqu1QoOj2m+7rKRqkA==} + /eslint-config-prettier@8.10.0(eslint@8.46.0): + resolution: {integrity: sha512-SM8AMJdeQqRYT9O9zguiruQZaN7+z+E4eAP9oiLNGKMtomwaB1E9dcgUD6ZAn/eQAb52USbvezbiljfZUhbJcg==} hasBin: true peerDependencies: eslint: '>=7.0.0' @@ -2090,7 +2090,7 @@ packages: - supports-color dev: true - /eslint-plugin-prettier@5.0.0(eslint-config-prettier@8.9.0)(eslint@8.46.0)(prettier@3.0.0): + /eslint-plugin-prettier@5.0.0(eslint-config-prettier@8.10.0)(eslint@8.46.0)(prettier@3.0.1): resolution: {integrity: sha512-AgaZCVuYDXHUGxj/ZGu1u8H8CYgDY3iG6w5kUFw4AzMVXzB7VvbKgYR4nATIN+OvUrghMbiDLeimVjVY5ilq3w==} engines: {node: ^14.18.0 || >=16.0.0} peerDependencies: @@ -2105,8 +2105,8 @@ packages: optional: true dependencies: eslint: 8.46.0 - eslint-config-prettier: 8.9.0(eslint@8.46.0) - prettier: 3.0.0 + eslint-config-prettier: 8.10.0(eslint@8.46.0) + prettier: 3.0.1 prettier-linter-helpers: 1.0.0 synckit: 0.8.5 dev: true @@ -3443,8 +3443,8 @@ packages: fast-diff: 1.3.0 dev: true - /prettier@3.0.0: - resolution: {integrity: sha512-zBf5eHpwHOGPC47h0zrPyNn+eAEIdEzfywMoYn2XPi0P44Zp0tSq64rq0xAREh4auw2cJZHo9QUob+NqCQky4g==} + /prettier@3.0.1: + resolution: {integrity: sha512-fcOWSnnpCrovBsmFZIGIy9UqK2FaI7Hqax+DIO0A9UxeVoY4iweyaFjS5TavZN97Hfehph0nhsZnjlVKzEQSrQ==} engines: {node: '>=14'} hasBin: true dev: true @@ -3539,8 +3539,8 @@ packages: glob: 10.3.3 dev: true - /rollup@3.27.0: - resolution: {integrity: sha512-aOltLCrYZ0FhJDm7fCqwTjIUEVjWjcydKBV/Zeid6Mn8BWgDCUBBWT5beM5ieForYNo/1ZHuGJdka26kvQ3Gzg==} + /rollup@3.27.1: + resolution: {integrity: sha512-tXNDFwOkN6C2w5Blj1g6ForKeFw6c1mDu5jxoeDO3/pmYjgt+8yvIFjKzH5FQUq70OKZBkOt0zzv0THXL7vwzQ==} engines: {node: '>=14.18.0', npm: '>=8.0.0'} hasBin: true optionalDependencies: @@ -3780,8 +3780,8 @@ packages: engines: {node: '>=8'} dev: true - /strip-literal@1.0.1: - resolution: {integrity: sha512-QZTsipNpa2Ppr6v1AmJHESqJ3Uz247MUS0OjrnnZjFAvEoWqxuyFuXn2xLgMtRnijJShAa1HL0gtJyUs7u7n3Q==} + /strip-literal@1.3.0: + resolution: {integrity: sha512-PugKzOsyXpArk0yWmUwqOZecSO0GH0bPoctLcqNDH9J04pVW3lflYE0ujElBGTloevcxF5MofAOZ7C5l2b+wLg==} dependencies: acorn: 8.10.0 dev: true @@ -4053,7 +4053,7 @@ packages: convert-source-map: 1.9.0 dev: true - /vite-node@0.34.1(@types/node@20.4.5): + /vite-node@0.34.1(@types/node@20.4.6): resolution: {integrity: sha512-odAZAL9xFMuAg8aWd7nSPT+hU8u2r9gU3LRm9QKjxBEF2rRdWpMuqkrkjvyVQEdNFiBctqr2Gg4uJYizm5Le6w==} engines: {node: '>=v14.18.0'} hasBin: true @@ -4063,7 +4063,7 @@ packages: mlly: 1.4.0 pathe: 1.1.1 picocolors: 1.0.0 - vite: 4.4.8(@types/node@20.4.5) + vite: 4.4.8(@types/node@20.4.6) transitivePeerDependencies: - '@types/node' - less @@ -4075,7 +4075,7 @@ packages: - terser dev: true - /vite@4.4.8(@types/node@20.4.5): + /vite@4.4.8(@types/node@20.4.6): resolution: {integrity: sha512-LONawOUUjxQridNWGQlNizfKH89qPigK36XhMI7COMGztz8KNY0JHim7/xDd71CZwGT4HtSRgI7Hy+RlhG0Gvg==} engines: {node: ^14.18.0 || >=16.0.0} hasBin: true @@ -4103,10 +4103,10 @@ packages: terser: optional: true dependencies: - '@types/node': 20.4.5 + '@types/node': 20.4.6 esbuild: 0.18.17 postcss: 8.4.27 - rollup: 3.27.0 + rollup: 3.27.1 optionalDependencies: fsevents: 2.3.2 dev: true @@ -4144,7 +4144,7 @@ packages: dependencies: '@types/chai': 4.3.5 '@types/chai-subset': 1.3.3 - '@types/node': 20.4.5 + '@types/node': 20.4.6 '@vitest/expect': 0.34.1 '@vitest/runner': 0.34.1 '@vitest/snapshot': 0.34.1 @@ -4161,11 +4161,11 @@ packages: pathe: 1.1.1 picocolors: 1.0.0 std-env: 3.3.3 - strip-literal: 1.0.1 + strip-literal: 1.3.0 tinybench: 2.5.0 tinypool: 0.7.0 - vite: 4.4.8(@types/node@20.4.5) - vite-node: 0.34.1(@types/node@20.4.5) + vite: 4.4.8(@types/node@20.4.6) + vite-node: 0.34.1(@types/node@20.4.6) why-is-node-running: 2.2.2 transitivePeerDependencies: - less -- 2.34.1