words:
- DECI
- CENTI
+ - Celcius
- MILLI
- MILLIWATT
- Benoit
dependencies:
'@mikro-orm/better-sqlite':
specifier: ^6.6.14
- version: 6.6.14(@mikro-orm/core@6.6.16)(mariadb@3.4.5)(supports-color@7.2.0)
+ version: 6.6.14(@mikro-orm/core@6.6.17)(mariadb@3.4.5)(supports-color@7.2.0)
'@mikro-orm/core':
specifier: ^6.6.16
- version: 6.6.16
+ version: 6.6.17
'@mikro-orm/mariadb':
specifier: ^6.6.16
- version: 6.6.16(@mikro-orm/core@6.6.16)(better-sqlite3@11.10.0)(supports-color@7.2.0)
+ version: 6.6.17(@mikro-orm/core@6.6.17)(better-sqlite3@11.10.0)(supports-color@7.2.0)
'@mikro-orm/reflection':
specifier: ^6.6.16
- version: 6.6.16(@mikro-orm/core@6.6.16)
+ version: 6.6.17(@mikro-orm/core@6.6.17)
'@modelcontextprotocol/sdk':
specifier: ~1.31.0
version: 1.31.0(supports-color@7.2.0)(zod@4.6.5)
devDependencies:
'@commitlint/cli':
specifier: ^21.2.3
- version: 21.2.3(@types/node@24.19.0)(conventional-commits-parser@7.1.2)(typescript@6.0.3)
+ version: 21.2.3(@types/node@24.19.1)(conventional-commits-parser@7.1.2)(typescript@6.0.3)
'@commitlint/config-conventional':
specifier: ^21.2.3
version: 21.2.3
'@cspell/eslint-plugin':
specifier: ^10.3.6
- version: 10.3.6(eslint@10.11.0(jiti@2.6.1)(supports-color@7.2.0))
+ version: 10.3.6(eslint@10.12.0(jiti@2.6.1)(supports-color@7.2.0))
'@eslint/js':
specifier: ^10.0.1
- version: 10.0.1(eslint@10.11.0(jiti@2.6.1)(supports-color@7.2.0))
+ version: 10.0.1(eslint@10.12.0(jiti@2.6.1)(supports-color@7.2.0))
'@mikro-orm/cli':
specifier: ^6.6.16
- version: 6.6.16(better-sqlite3@11.10.0)(mariadb@3.4.5)(supports-color@7.2.0)
+ version: 6.6.17(better-sqlite3@11.10.0)(mariadb@3.4.5)(supports-color@7.2.0)
'@types/node':
specifier: ^24.19.0
- version: 24.19.0
+ version: 24.19.1
'@types/semver':
specifier: ^7.8.0
version: 7.8.0
version: 2.1.1(esbuild@0.28.2)
eslint:
specifier: ^10.11.0
- version: 10.11.0(jiti@2.6.1)(supports-color@7.2.0)
+ version: 10.12.0(jiti@2.6.1)(supports-color@7.2.0)
eslint-plugin-jsdoc:
specifier: ^65.0.1
- version: 65.0.1(eslint@10.11.0(jiti@2.6.1)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3)
+ version: 65.0.2(eslint@10.12.0(jiti@2.6.1)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3)
eslint-plugin-perfectionist:
specifier: ^5.12.1
- version: 5.12.1(eslint@10.11.0(jiti@2.6.1)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3)
+ version: 5.12.1(eslint@10.12.0(jiti@2.6.1)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3)
eslint-plugin-vue:
specifier: ^10.11.1
- version: 10.11.1(@stylistic/eslint-plugin@5.10.0(eslint@10.11.0(jiti@2.6.1)(supports-color@7.2.0)))(@typescript-eslint/parser@8.70.1(eslint@10.11.0(jiti@2.6.1)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3))(eslint@10.11.0(jiti@2.6.1)(supports-color@7.2.0))(vue-eslint-parser@10.4.1(eslint@10.11.0(jiti@2.6.1)(supports-color@7.2.0))(supports-color@7.2.0))
+ version: 10.11.1(@stylistic/eslint-plugin@5.10.0(eslint@10.12.0(jiti@2.6.1)(supports-color@7.2.0)))(@typescript-eslint/parser@8.71.0(eslint@10.12.0(jiti@2.6.1)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3))(eslint@10.12.0(jiti@2.6.1)(supports-color@7.2.0))(vue-eslint-parser@10.4.1(eslint@10.12.0(jiti@2.6.1)(supports-color@7.2.0))(supports-color@7.2.0))
husky:
specifier: ^9.1.7
version: 9.1.7
version: 17.6.0
neostandard:
specifier: ^0.14.0-next.1
- version: 0.14.0-next.1(eslint@10.11.0(jiti@2.6.1)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3)
+ version: 0.14.0-next.1(eslint@10.12.0(jiti@2.6.1)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3)
prettier:
specifier: ^3.9.9
version: 3.9.9
version: 0.35.12(supports-color@7.2.0)
ts-node:
specifier: ^10.9.2
- version: 10.9.2(@types/node@24.19.0)(typescript@6.0.3)
+ version: 10.9.2(@types/node@24.19.1)(typescript@6.0.3)
tsx:
specifier: ^4.23.15
version: 4.23.15
version: 6.0.1
vue-eslint-parser:
specifier: ^10.4.1
- version: 10.4.1(eslint@10.11.0(jiti@2.6.1)(supports-color@7.2.0))(supports-color@7.2.0)
+ version: 10.4.1(eslint@10.12.0(jiti@2.6.1)(supports-color@7.2.0))(supports-color@7.2.0)
optionalDependencies:
bufferutil:
specifier: ^4.1.0
version: 3.5.43(typescript@6.0.3)
vue-router:
specifier: ^5.3.1
- version: 5.3.1(@vue/compiler-sfc@3.5.43)(esbuild@0.28.2)(rolldown@1.2.10)(vite@8.3.1(@types/node@24.19.0)(esbuild@0.28.2)(jiti@2.6.1)(tsx@4.23.15)(yaml@2.9.1))(vue@3.5.43(typescript@6.0.3))
+ version: 5.3.1(@vue/compiler-sfc@3.5.43)(esbuild@0.28.2)(rolldown@1.2.12)(vite@8.3.2(@types/node@24.19.1)(esbuild@0.28.2)(jiti@2.6.1)(tsx@4.23.15)(yaml@2.9.1))(vue@3.5.43(typescript@6.0.3))
vue-toast-notification:
specifier: ^3.1.3
version: 3.1.3(vue@3.5.43(typescript@6.0.3))
version: 30.0.0
'@vitejs/plugin-vue':
specifier: ^6.0.9
- version: 6.0.9(vite@8.3.1(@types/node@24.19.0)(esbuild@0.28.2)(jiti@2.6.1)(tsx@4.23.15)(yaml@2.9.1))(vue@3.5.43(typescript@6.0.3))
+ version: 6.0.9(vite@8.3.2(@types/node@24.19.1)(esbuild@0.28.2)(jiti@2.6.1)(tsx@4.23.15)(yaml@2.9.1))(vue@3.5.43(typescript@6.0.3))
'@vitejs/plugin-vue-jsx':
specifier: ^5.1.6
- version: 5.1.6(supports-color@7.2.0)(vite@8.3.1(@types/node@24.19.0)(esbuild@0.28.2)(jiti@2.6.1)(tsx@4.23.15)(yaml@2.9.1))(vue@3.5.43(typescript@6.0.3))
+ version: 5.1.6(supports-color@7.2.0)(vite@8.3.2(@types/node@24.19.1)(esbuild@0.28.2)(jiti@2.6.1)(tsx@4.23.15)(yaml@2.9.1))(vue@3.5.43(typescript@6.0.3))
'@vitest/coverage-v8':
specifier: ^5.0.3
version: 5.0.3(vitest@5.0.3)
version: 30.1.1
vite:
specifier: ^8.3.1
- version: 8.3.1(@types/node@24.19.0)(esbuild@0.28.2)(jiti@2.6.1)(tsx@4.23.15)(yaml@2.9.1)
+ version: 8.3.2(@types/node@24.19.1)(esbuild@0.28.2)(jiti@2.6.1)(tsx@4.23.15)(yaml@2.9.1)
vitest:
specifier: ^5.0.3
- version: 5.0.3(@opentelemetry/api@1.9.1)(@types/node@24.19.0)(@vitest/coverage-v8@5.0.3)(jsdom@30.1.1)(vite@8.3.1(@types/node@24.19.0)(esbuild@0.28.2)(jiti@2.6.1)(tsx@4.23.15)(yaml@2.9.1))
+ version: 5.0.3(@opentelemetry/api@1.9.1)(@types/node@24.19.1)(@vitest/coverage-v8@5.0.3)(jsdom@30.1.1)(vite@8.3.2(@types/node@24.19.1)(esbuild@0.28.2)(jiti@2.6.1)(tsx@4.23.15)(yaml@2.9.1))
vue-tsc:
- specifier: ^3.3.11
- version: 3.3.11(typescript@6.0.3)
+ specifier: ^3.3.12
+ version: 3.3.12(typescript@6.0.3)
packages:
resolution: {integrity: sha512-UQFQ6SgyJ6LX42W8rHCs8KVc0JS0tzVL9ct4XYedJukskYVWTo49tNiMEK9C2HTyarbNiT/RVIRSY82vH+6sTg==}
engines: {node: '>=4'}
- '@asamuzakjp/css-color@7.0.1':
- resolution: {integrity: sha512-C9duntabagkBZ1LebM7FKmphR4Q1pBclLxVbZETQV0akkFjV0ooFxo8FlvAQyKj9F6l8rEnmidgcTlpyxFYizg==}
+ '@asamuzakjp/css-color@7.1.3':
+ resolution: {integrity: sha512-1t1U8Cm3RBl9vh3RAZhjkPaduzLSvQJRToRrxpGR/eDRW99mOREleuu+mWfiFa795bUzjwht7NgphCKTs2ianQ==}
engines: {node: ^22.22.2 || ^24.15.0 || >=26.0.0}
- '@asamuzakjp/dom-selector@9.2.1':
- resolution: {integrity: sha512-NT4s3yZLjovPpliRpTvdsdzyPjqRqiCZj9MxnarBihbaY5MbAG7DWAJcrLlhmC7xKTNCXsTELcqB8YsqpLnUFQ==}
+ '@asamuzakjp/dom-selector@9.2.4':
+ resolution: {integrity: sha512-YnVzxDxLaqL0t7Q4wfcgHZjg55Wm6y5EaO7Bhu3q9J7wq/8iw3PhsnWiOQhYekx4VexnbkgWpTT6n+QIV7kczw==}
engines: {node: ^22.22.2 || ^24.15.0 || >=26.0.0}
'@babel/code-frame@7.29.7':
'@cspell/dict-companies@3.2.13':
resolution: {integrity: sha512-mBJeYzCkiiWgNQxKg7zyQOpFPwcXO4IAzXGbOEFfcQ4Q0G5Fa7Kqd8z+gvnvSK7NPWKJaFmiApC6I0cka02qmQ==}
- '@cspell/dict-cpp@7.1.2':
- resolution: {integrity: sha512-bkR33oE+ToyD1hwSV4XeEUB9oDkizHRX03CrQfj23tX4VUVbie5vkEHzlKVhcw/Q+fO2dtV/Vz4f6O5M4a2iZQ==}
+ '@cspell/dict-cpp@7.1.3':
+ resolution: {integrity: sha512-2d2MSh+C+xzzdqjLwtt40TdXWDR/pK0q1Q9L5zC+1EPSCIZxlN7CHbhlXWpl0JqUSdy2xaWaWucwnoGznMTneA==}
'@cspell/dict-cryptocurrencies@5.0.5':
resolution: {integrity: sha512-R68hYYF/rtlE6T/dsObStzN5QZw+0aQBinAXuWCVqwdS7YZo0X33vGMfChkHaiCo3Z2+bkegqHlqxZF4TD3rUA==}
'@cspell/dict-elixir@4.0.8':
resolution: {integrity: sha512-CyfphrbMyl4Ms55Vzuj+mNmd693HjBFr9hvU+B2YbFEZprE5AG+EXLYTMRWrXbpds4AuZcvN3deM2XVB80BN/Q==}
- '@cspell/dict-en-common-misspellings@2.2.2':
- resolution: {integrity: sha512-gu0zS7nm0jgVA2EswgWOR8od8wbNYVgcGzo0pGaDqcSkKS4WhABehLOwbyI0NIEVB+ME6SmckLVGk7g+VD1C6A==}
+ '@cspell/dict-en-common-misspellings@2.2.3':
+ resolution: {integrity: sha512-rwskWjznsDSZe3ck03BGUNT3UY9MjO1e4E9LJWACFzdDjkdM7uzro2/WfKvZTBVWVnRlzdRXPrtjkUZFI2A66A==}
'@cspell/dict-en-gb-mit@3.1.29':
resolution: {integrity: sha512-mInExsCFZui0f9Vg/MToWfQZzwMfPgHX8BCYdZNdXWbYz+jITXcnvssbdUkui/DVbriZVJ74Zu9bv2jOEqTCNA==}
'@cspell/dict-fullstack@3.2.9':
resolution: {integrity: sha512-diZX+usW5aZ4/b2T0QM/H/Wl9aNMbdODa1Jq0ReBr/jazmNeWjd+PyqeVgzd1joEaHY+SAnjrf/i9CwKd2ZtWQ==}
- '@cspell/dict-gaming-terms@1.1.2':
- resolution: {integrity: sha512-9XnOvaoTBscq0xuD6KTEIkk9hhdfBkkvJAIsvw3JMcnp1214OCGW8+kako5RqQ2vTZR3Tnf3pc57o7VgkM0q1Q==}
+ '@cspell/dict-gaming-terms@1.1.3':
+ resolution: {integrity: sha512-hLGzd/f+KX5B/5YxAXuQi4DYEfu6IKudtNFO/Nima3JLXD+8Xn+d0q6VdaBxkWVhGYlzow9o+dce+S9mNtgpMg==}
'@cspell/dict-git@3.1.1':
resolution: {integrity: sha512-XvuGVYj9YEu4EzntqyhH8XlTRyy57C6j8oxkIbUQfR2UYzxZGAP77XHl7qY7eC/40r/haDwnBaccyFR6BRmUCg==}
'@cspell/dict-node@5.0.9':
resolution: {integrity: sha512-hO+ga+uYZ/WA4OtiMEyKt5rDUlUyu3nXMf8KVEeqq2msYvAPdldKBGH7lGONg6R/rPhv53Rb+0Y1SLdoK1+7wQ==}
- '@cspell/dict-npm@5.2.50':
- resolution: {integrity: sha512-JZhJb0wrWpqnzDBM1VG8f6zZPRCOo6YMxct0hFhgBnbOTIHQiqZf9S1BSXCRMmmQlAWqFXjKNDp+rf7ZiJcO9w==}
+ '@cspell/dict-npm@5.2.52':
+ resolution: {integrity: sha512-JttzSladJ4KVhfg9QiBM4os+wAKbet2F58wpbM5VJLnL8fAj3xwvBvSxdY64yrW6Y5wdSPHoTCLqXOKoyBJFnA==}
'@cspell/dict-php@4.1.3':
resolution: {integrity: sha512-41oKzl7rLVrrVZA/Gs0ESnP6KliEX7c+IwaaNmOL3PAoHasFDVEmIFeENIzuDdM12UcHT+MlLkAm3Qw/lS/ERA==}
'@cspell/dict-powershell@5.0.17':
resolution: {integrity: sha512-khOoIdBgKRk2scnr7888pf70yJ1Tr3RGdnYcAN0MJ2vtaNRKoOTCFc13gXUkNDHwEyhMI1h9oz3KsT180F2amw==}
- '@cspell/dict-public-licenses@2.0.16':
- resolution: {integrity: sha512-EQRrPvEOmwhwWezV+W7LjXbIBjiy6y/shrET6Qcpnk3XANTzfvWflf9PnJ5kId/oKWvihFy0za0AV1JHd03pSQ==}
+ '@cspell/dict-public-licenses@2.0.17':
+ resolution: {integrity: sha512-Asdumgg3oZ4ki//b2a+beiVIsNZA5thim4t4v3lTDLY/6a8VAPmTjzC3Y2t0x7ywC/bzAhi1KW/fg8JlwHDgig==}
'@cspell/dict-python@4.5.2':
resolution: {integrity: sha512-CFcTDGN/ycn45WXC5ZZ06pG09WbGKMpPm1rkMuqMP98/jwjw3iiK8c0izWRd8x027n1rOZ9kJ8f9Sd+bQ22HJg==}
'@cspell/dict-shell@1.2.0':
resolution: {integrity: sha512-PVctvT22lJ49niMiakO8xieY7ELCAzjSqhejWR7bAMb5AZ9F4WDEs+XdGMnoVHWeXq7K5rcepLPmEJb+37zzIw==}
- '@cspell/dict-software-terms@5.4.5':
- resolution: {integrity: sha512-5PgLJNdaFKeA6iklB0XXjmgNaoLJJqf54oRTD1raG/mgwojVlRnWE1G7x4QH9Gz07h3B3+lMDv1F57Xxs4jang==}
+ '@cspell/dict-software-terms@5.4.6':
+ resolution: {integrity: sha512-r4Xw367hTosVCAQ884nPWhwcy3Jc6YWQFkW1XBdxFGdzdCltzuNf9qdJL1AwiTWMbiRnifIqKaXN0A38WCVEbQ==}
'@cspell/dict-sql@2.2.1':
resolution: {integrity: sha512-qDHF8MpAYCf4pWU8NKbnVGzkoxMNrFqBHyG/dgrlic5EQiKANCLELYtGlX5auIMDLmTf1inA0eNtv74tyRJ/vg==}
resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==}
engines: {node: '>=12'}
- '@csstools/color-helpers@6.1.1':
- resolution: {integrity: sha512-gLNsunvwf3mCi5u5o46/Z/JcJMnhbHSaZ69rkgPzNM3J4s8hWwpPUQB6/tt0EDFyCiWzxANlx+2LJwpYj4zS1w==}
+ '@csstools/color-helpers@6.1.2':
+ resolution: {integrity: sha512-grhRy3OKmniaAEKXMjua5z/EODX0MSqBGjunw8+j/3HQjOnahs2AGhvEOIYVUWcU6ScApbhLhVrQTX8XqrMrow==}
engines: {node: '>=20.19.0'}
- '@csstools/css-calc@3.4.0':
- resolution: {integrity: sha512-XQKj5B7QiZcHiegCOCAzcAOJdhGgWOHbbu62h5e5mkHnn8lWcfiJhllkqWmxu5zWR9jucPHuo1iTB56P033hcg==}
+ '@csstools/css-calc@3.4.3':
+ resolution: {integrity: sha512-iex20d8CHVkyvg6B7UKV7uHnI2Bqo9g+EFfT9E0y+GvTvhZ/DwONJ+9aKb1dlqm0ZiGsL5RXjp0fCoJYnkeDjA==}
engines: {node: '>=20.19.0'}
peerDependencies:
- '@csstools/css-parser-algorithms': ^4.0.0
- '@csstools/css-tokenizer': ^4.0.0
+ '@csstools/css-parser-algorithms': ^4.0.2
+ '@csstools/css-tokenizer': ^4.0.2
- '@csstools/css-color-parser@4.2.3':
- resolution: {integrity: sha512-y4LpL+lmpuyKDiEFq2PnZUVFdAjsoB/qQJod79yLNokXyW7jewi+/WJ69EfItj8A2unWtxXnGjw6LYXgXu5ZjA==}
+ '@csstools/css-color-parser@4.2.6':
+ resolution: {integrity: sha512-iiPQ3iRWwnJkeEn6RIu6SJPr7hYrLz6XZ9s/QZl+2/LI5KQVjpl2fdmDSZKuD4xP6GMmMPgHFFXg6k1Wkz0Trg==}
engines: {node: '>=20.19.0'}
peerDependencies:
- '@csstools/css-parser-algorithms': ^4.0.0
- '@csstools/css-tokenizer': ^4.0.0
+ '@csstools/css-parser-algorithms': ^4.0.2
+ '@csstools/css-tokenizer': ^4.0.2
- '@csstools/css-parser-algorithms@4.0.0':
- resolution: {integrity: sha512-+B87qS7fIG3L5h3qwJ/IFbjoVoOe/bpOdh9hAjXbvx0o8ImEmUsGXN0inFOnk2ChCFgqkkGFQ+TpM5rbhkKe4w==}
+ '@csstools/css-parser-algorithms@4.0.2':
+ resolution: {integrity: sha512-40cSKyMvK+tq4qz6Awrlye2WGuOKt3FwPgtGg6KTfbHOWNw+Rk1rzbAtZnZ6IBhsY491HLRnDXwoyBAijmmILA==}
engines: {node: '>=20.19.0'}
peerDependencies:
- '@csstools/css-tokenizer': ^4.0.0
+ '@csstools/css-tokenizer': ^4.0.2
- '@csstools/css-syntax-patches-for-csstree@1.1.14':
- resolution: {integrity: sha512-HpbVXyrofRXpHpgkNIjU/3EWR4WJvOkO3emNK/L6X/mTJU7bGUI3AkkpoTNXznQLp0KRjLHELTGeKI5dIkI9JQ==}
+ '@csstools/css-syntax-patches-for-csstree@1.1.15':
+ resolution: {integrity: sha512-J0u7HkVl2nzSlhsiTOp4AmwcUQ3D+mGEEKfBy/7To5/y7F2OHwyLrXfrhR0SMgr4p5Lo+eaMVSeai24zUcBIxA==}
peerDependencies:
css-tree: ^3.2.1
peerDependenciesMeta:
css-tree:
optional: true
- '@csstools/css-tokenizer@4.0.1':
- resolution: {integrity: sha512-bPlN9S9O1A0euCpEWE4qnvB5YDuyYVsUTrxSgmAM1Is0j4tICHoVyOVAXfWMP/kS9ZrjvyIXWV2PmomiAXXqOw==}
+ '@csstools/css-tokenizer@4.0.2':
+ resolution: {integrity: sha512-OoKoR0f76dCY666JlcbhmVTs2drYj1GUXZTYTcbUgJjh9Nv41aFfZ21bPQTERm5+L5cBDo466NltB2lplS5GBw==}
engines: {node: '>=20.19.0'}
'@dabh/diagnostics@2.0.9':
'@noble/hashes':
optional: true
- '@hono/node-server@2.1.1':
- resolution: {integrity: sha512-ELuehkj5VCBdgEw9zs+ivkKwyzzUCSQuE96YmiPvn1ECBoZCczbFXJLeEGMTYjphP6gydh4pHMqEYPVMYUVgQg==}
+ '@hono/node-server@2.1.3':
+ resolution: {integrity: sha512-TA//nWMqPhbfdfneACk6t5a9eqbS9lABEPyKn0/xZTah3H3U2XaVg85rJFl0/Fyit0I552YDHgXGVSf3GwqbUw==}
engines: {node: '>=20'}
peerDependencies:
hono: ^4
peerDependencies:
'@mikro-orm/core': ^6.0.0
- '@mikro-orm/cli@6.6.16':
- resolution: {integrity: sha512-EsAr7A4wbmBib3LwUcnH1cJOYgGC8irBptt+0BiC7IaP+p2UsnQBvTBbM895rAk1rXiInFhqWKjBgSKZcDf4yQ==}
+ '@mikro-orm/cli@6.6.17':
+ resolution: {integrity: sha512-cQ9HXCVe1L+MXvlg3jNPVGkIgA4ngsH48GoGj3SfxJdrdB7W5JOLuu8dGQrc6HDUvuff78lkBpIy/XROm8t/fg==}
engines: {node: '>= 18.12.0'}
hasBin: true
- '@mikro-orm/core@6.6.16':
- resolution: {integrity: sha512-ym26fbhTti7Zh6oN1y74byoUoMEq1ahckiV7BZX5pHU/0OaUESiQA+Zar2Tv2ZFuZA9946XQ805F7/4NH6oDUQ==}
+ '@mikro-orm/core@6.6.17':
+ resolution: {integrity: sha512-LFl6s0U6evvhB2iqifGEAzY0Nl1DC4Ge2N6GcPoNEvtg3OC/0GjvIftyzXnpaANjo/IxEaY/ge5BgMLt1B729g==}
engines: {node: '>= 18.12.0'}
'@mikro-orm/knex@6.6.14':
mariadb:
optional: true
- '@mikro-orm/knex@6.6.16':
- resolution: {integrity: sha512-H0fYK4vSe2mtmfBDDNsOw5Z56JDYEzbaoIo24JhkzmoGna85ddwyYzYB289DMS8Ukn7qZKhfR2pn2kQDRSJiyw==}
+ '@mikro-orm/knex@6.6.17':
+ resolution: {integrity: sha512-v65plaUKRuPsmMBXl/0h6ejqhVvS0Np4bGJSxHGOuWPONNEQHvSlt1aIpddhSYtdnzCXKV2hQNVBEaj8/wkA2Q==}
engines: {node: '>= 18.12.0'}
peerDependencies:
'@mikro-orm/core': ^6.0.0
mariadb:
optional: true
- '@mikro-orm/mariadb@6.6.16':
- resolution: {integrity: sha512-4BPpR6KYzMvByp9sJg5MlXrKxoDPrFDtnYku+swzXDgcyCNZwBJisRf0hkSXd3xL7JSV5yrsg+vZY+KjKO0ECQ==}
+ '@mikro-orm/mariadb@6.6.17':
+ resolution: {integrity: sha512-JcdyFFXyMIzHgv/bTtWWGFQph4X5AdeO9f7pLNn6yDkuF4cuzgUstmCHGbs7ZLzGY5tSgxQ2bB/GbgrJwPr3zA==}
engines: {node: '>= 18.12.0'}
peerDependencies:
'@mikro-orm/core': ^6.0.0
- '@mikro-orm/reflection@6.6.16':
- resolution: {integrity: sha512-OXMhyWOag3Fyz+RyvptHKmwIPXE++5k7aFEt8JVK7YfEjqTvuB9Y7N3jQH08HQXe8VPsYs4RjAKz7DEk5E6JAA==}
+ '@mikro-orm/reflection@6.6.17':
+ resolution: {integrity: sha512-ep79EneWXx7tctmI5amZYgUT4eQv+S2/+ZGlgHU2KBP5lARNAkE1D4EaTD/hzwbt1kNDQud0TcGEgUDoaiawoA==}
engines: {node: '>= 18.12.0'}
peerDependencies:
'@mikro-orm/core': ^6.0.0
'@cfworker/json-schema':
optional: true
- '@mongodb-js/saslprep@1.5.4':
- resolution: {integrity: sha512-05UC0jQsjKAOuXQ0H9Ud9vUTJpZIg+n/FinpR30tI5I8pY2inTfPOZ5OF/cg3Ce/N9MoD1xhRCeOsJtuTbFYlw==}
+ '@mongodb-js/saslprep@1.5.5':
+ resolution: {integrity: sha512-0PV4mvjGd2Q2/viDvXuoSNzTjQngHvtuWUry1vqvcK4Q/CTeO1zEKMf9set01DMocJnogl/pyNAdvyk+Pe0kQA==}
'@nodelib/fs.scandir@2.1.5':
resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
resolution: {integrity: sha512-gLyJlPHPZYdAk1JENA9LeHejZe1Ti77/pTeFm/nMXmQH/HFZlcS/O2XJB+L8fkbrNSqhdtlvjBVjxwUYanNH5Q==}
engines: {node: '>=8.0.0'}
- '@oxc-project/types@0.151.0':
- resolution: {integrity: sha512-J1yXrIlNDZVzE3ada310xeAw7nH8yCAyLPuUIsjKatFPmfn5bS1oW+cM+QsGOtVWd5nhSpbwZWx/rue+r5Z+PA==}
+ '@oxc-project/types@0.152.0':
+ resolution: {integrity: sha512-oM/5rLBm2tPkg0iBgkH/FOeR3PCDpY19GTgAZjMFM8h9WI9VW7cLgzp6nwtarYKmovavIQZ+Fe/RKX/8C8O/Rw==}
'@parcel/watcher-android-arm64@2.6.0':
resolution: {integrity: sha512-trgpLSCKRC/huFjXX/Smh+0sWe4+YtKfktIToiMl59ghz7z+qkH6kMvNnUbLyRs9N11t8l4svSCs1+5B3rOAhA==}
'@polka/url@1.0.0-next.29':
resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==}
- '@rolldown/binding-android-arm-eabi@1.2.10':
- resolution: {integrity: sha512-bp9svZb+QurZeh+8H4BhrZkifEB0YBNvTVzNSJnJQkj4NrRwmQoDUCGP0vSN7PbvLeM7l1tK6GXL8mrTiH2myg==}
+ '@rolldown/binding-android-arm-eabi@1.2.12':
+ resolution: {integrity: sha512-dB/a1214qKfHMXCpgqR4OZT+jS4kTyEXbQGJPqzobt5EwH5rX080pxE37alt3RzvR1bf1Yz/yGqRfrYAxuPw0A==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm]
os: [android]
- '@rolldown/binding-android-arm64@1.2.10':
- resolution: {integrity: sha512-wm6Dld3RXUAZ/gRWKyUy+4W1B5CB5UeFaOzsSWJWEdxZXHH8rCYiZ5dGe6oJmhsunAPWzL7FZV+VtvmN5Ye2eA==}
+ '@rolldown/binding-android-arm64@1.2.12':
+ resolution: {integrity: sha512-7KHFgQ5VJxIHcLlrwrc3Xbds7oTNQT7Pgi9gQCJKrd2VGab/UksIOYp6VD8MzCstGxOKMgNamPwUCfxPdP1OHg==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [android]
- '@rolldown/binding-darwin-arm64@1.2.10':
- resolution: {integrity: sha512-UbEfXq/AqGNgRTV3ik+X/iR6mUxu2QdYAadwRxJWquUGnW6gDqdP1FtLtFXRow7RJx0ssRwi80XAPr4r+4DtsA==}
+ '@rolldown/binding-darwin-arm64@1.2.12':
+ resolution: {integrity: sha512-3YIhqHD96nA5SaYNRBR16HnGv4oavZvXfD/ayHM+oYZ0WD/8lBAtf6zQua4kEyAvpqrluKXl0lnOBoiNby7x9w==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [darwin]
- '@rolldown/binding-darwin-x64@1.2.10':
- resolution: {integrity: sha512-7f5h17q5KZVx/ji1vb8OTq31ch1O2I7K8NPIr44GkyWTApXMIsmhWqZfgpOH10xeauqghDAvGlZktasCkcF6Eg==}
+ '@rolldown/binding-darwin-x64@1.2.12':
+ resolution: {integrity: sha512-UuuJ35MFw4gmFOrE9pEqIV+K3syIKveph+Qc1/ljHZVdoDW4pz/JHR/eMVom+TZGl/5OOvGJOWaOCVt3ZfqhxA==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64]
os: [darwin]
- '@rolldown/binding-freebsd-x64@1.2.10':
- resolution: {integrity: sha512-ynOk/eEYhC6ZB2xCGvKrEOwE58oBy9LnrAqtkrDF9Fz1VTaNdGZTsV0VarJdhPwb+sOJTGjCLwcuyRJZ1dnMcQ==}
+ '@rolldown/binding-freebsd-x64@1.2.12':
+ resolution: {integrity: sha512-uMvssit0a4W+/7D8CbHUvG719mH3R2jwXAlh/XcPvuHTE0g++LymF88DCGNX0HM2rBOn0xrzgXktIB6fLSJBTQ==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64]
os: [freebsd]
- '@rolldown/binding-linux-arm-gnueabihf@1.2.10':
- resolution: {integrity: sha512-ERrAs185meZZhGan7a4l3RiiJK1ArSDlHdST++uvSxe+FDbR4TwUPahT/cbZJvaG6fIpDpF78surN+tX708Y4Q==}
+ '@rolldown/binding-linux-arm-gnueabihf@1.2.12':
+ resolution: {integrity: sha512-XcFu0R0xWnwzSf4IQgFH1rJIckPN1pLy2R+4r9IDB7Yfu/ys9cVqfa4pBrMHj7a3gl8mIR4nRNPg0e5IvEVs6g==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm]
os: [linux]
- '@rolldown/binding-linux-arm64-gnu@1.2.10':
- resolution: {integrity: sha512-KN7OHKD0J3jy1UzBwZWPxpwhODf9IARUIJcrH+yLYKOcmegZ8luEUM38lDP1bDVj40yP6PsSzCqOJF76vljFnQ==}
+ '@rolldown/binding-linux-arm64-gnu@1.2.12':
+ resolution: {integrity: sha512-260UrKgn8tz39ak+SMDOirKzr7V04M9dWPw5llW00SwBivCZoWcRBKV1d8cXnRkUmSZA3BdiUmBHWk7734Ulpw==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [linux]
libc: [glibc]
- '@rolldown/binding-linux-arm64-musl@1.2.10':
- resolution: {integrity: sha512-8l9wP8O+wa8zD6iw6egSfzVtu7oZVfH3hlUsMM4MwbLMhxleqeoXbZzjddyK3YyNlwLhqznq3tF7PkNJ8T/V2w==}
+ '@rolldown/binding-linux-arm64-musl@1.2.12':
+ resolution: {integrity: sha512-5YK1I9SqDkbPgc1IA8BgDl34suqUS2q0KWnBrirm0E51YjOs6eo6dV6jbQfNE/argHRSvd0QUGgtpIoYx+WWpw==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [linux]
libc: [musl]
- '@rolldown/binding-linux-ppc64-gnu@1.2.10':
- resolution: {integrity: sha512-SeXNKeQzA5kLhz/J0CH6ZP0/HJ3v1xm/0YbiYpE0kK7emfRC2OIGGIaE14xzkISEGv2aYuUSpiLiU5Gbq+OI0A==}
+ '@rolldown/binding-linux-ppc64-gnu@1.2.12':
+ resolution: {integrity: sha512-Rkcrmp7eFRg74yL5fXEU91JEWbdEPLevWwGtXpmhbjlD1StScbWTmO94Bhly+Mo+ketKYkdmM1vNUKeWSlx8cQ==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [ppc64]
os: [linux]
libc: [glibc]
- '@rolldown/binding-linux-s390x-gnu@1.2.10':
- resolution: {integrity: sha512-mtht0nR+y8/hart4175Ll15w7lY8dg7CtQ+j2FDNTsDRspOWTK/2V3l0aj9sIj7XmvqxT8Yli/wq22e7feTTWg==}
+ '@rolldown/binding-linux-s390x-gnu@1.2.12':
+ resolution: {integrity: sha512-qvK4DuAsQc2BSjlx+Xr+IzOIvvxbGZqxFwdWfG6F518Erj0GGISyQbJ6pIappnOxlNPzNHvo/L0BwB30GZ+zVw==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [s390x]
os: [linux]
libc: [glibc]
- '@rolldown/binding-linux-x64-gnu@1.2.10':
- resolution: {integrity: sha512-FSM94nGd55NYo48usCyM/nHfUKRnqc9+b0vJNuKV0oCCpIp/OGims7rO1Nv/DkFkt0S/s2rxsJ2kkS8J3HcpeA==}
+ '@rolldown/binding-linux-x64-gnu@1.2.12':
+ resolution: {integrity: sha512-Q9uLBO53Xd4QIq1WOycVQyPP1O4HhraEV2qqb3uTrnVw6QZih9duY4vNXOivL1xoUS1/z+W8eF4NMfl2a8Sdjw==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64]
os: [linux]
libc: [glibc]
- '@rolldown/binding-linux-x64-musl@1.2.10':
- resolution: {integrity: sha512-C3YxNB16myRLs7o+B+6PnQ6jBsdIS4+AE4Ah8glVGhDpEv9AOvxhZ/1duAb4B0UGczEK/lBbccksd8VI+p6zfw==}
+ '@rolldown/binding-linux-x64-musl@1.2.12':
+ resolution: {integrity: sha512-3IBxWFMjbOZskDPKv8Lf9BCnahlKuHthWkYnyIxOH/QcJrFcS4EmcenthApkwr/5+nEqZlLzeYbxeMaX7A5u4g==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64]
os: [linux]
libc: [musl]
- '@rolldown/binding-openharmony-arm64@1.2.10':
- resolution: {integrity: sha512-571TlE/F1eeTjjdjYAMMMPs1Mfv3MtX6s3+ZKVU6HiUjZ5Njc6c/qzNy/8K3zALTZnaw3JQVYrHxvNfjm43KAg==}
+ '@rolldown/binding-openharmony-arm64@1.2.12':
+ resolution: {integrity: sha512-xtX61xg4LKPkPWilZU1ynKClz5Gj4bf74LML4r3eVLWumKnGjoEr1OSHQhMdbBDoYTi+yjrujvpZe2pUnqCrrA==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [openharmony]
- '@rolldown/binding-win32-arm64-msvc@1.2.10':
- resolution: {integrity: sha512-QXW+ZWaiqs2c7Fi++D/SsW07LTPcUrncxcskJGfGNBoaLik1IU6fJymz4HsqwEO0u5Iq11yTO0B/mc4cPk7jrQ==}
+ '@rolldown/binding-win32-arm64-msvc@1.2.12':
+ resolution: {integrity: sha512-At7fPB6PCaIjzgIhEZFxuT+BBFqiQibJDT4d3PhiR3f4E7bbMZF4aKblbFfEM3sETRDd1YiQx/+U/g/B/ou5Ew==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [win32]
- '@rolldown/binding-win32-x64-msvc@1.2.10':
- resolution: {integrity: sha512-5FQFGgah17YeMtG1Yd5a+rMxQpTksyNXxRtKz06FVTaQw3RKYUJQbUoKk0/5jrXBpDo+7makNP7UHA2LQyH64A==}
+ '@rolldown/binding-win32-x64-msvc@1.2.12':
+ resolution: {integrity: sha512-WIw2haVKwjuYdXkHaoC0mF8Le71TuCBxjrdKqLbJGctbBABj+ClfmNvtbOnzpq3RokNo5+V1qhtSzJyXorsklQ==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64]
os: [win32]
'@types/minimatch@3.0.5':
resolution: {integrity: sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ==}
- '@types/node@24.13.6':
- resolution: {integrity: sha512-SGrw/h3KPFshy3OE6ZL53LMBG5vGQQ8/gIpiqz/kRZhPJ7HgwCEs8LBuNtWLa8dvGZVpSF7+Bf+c11HUrCb/yg==}
+ '@types/node@24.19.1':
+ resolution: {integrity: sha512-aS3/DG0oM05K0RIXXP+hKjinGG5IgSSVGzswZxW3O0sS3pH4/fycXundUC9XsszgKCk4gHXylTEK6hyFxVxnoQ==}
- '@types/node@24.19.0':
- resolution: {integrity: sha512-zY+5tKxXdhGh1PYI0ac+7juvEu4OI6vWtVVoj5i2m42jxAY1U+zHGt6QCyOFwykdP62sM3MJ9stoYYUw5aCWew==}
-
- '@types/node@26.6.2':
- resolution: {integrity: sha512-X1P21scMv4zGKLYqjdGjaKa7COa0RKVYYZZN/NfvLQ1JegxFhdhpZG/Lyn8AXx6CDUavKAd11v6BvfpkDByK8g==}
+ '@types/node@26.6.4':
+ resolution: {integrity: sha512-ldVPDCzj7fsaGZrLB0NuHuTvJcsNasysBAqMolr/cgxrLd1xbqxIr3XJiPnHHJUCxj5sNF1vnRj9aWnrVh5Jcg==}
'@types/parse-json@4.0.2':
resolution: {integrity: sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==}
'@types/ws@8.18.2':
resolution: {integrity: sha512-67MQl+fpWKVTT1NYdnmo3U4sc/xPo/zQBncVnI74qmQa0z/b+1g6iYqNmGCPbxO+zz2aklb08a0oHfegiVd0/w==}
- '@typescript-eslint/eslint-plugin@8.70.1':
- resolution: {integrity: sha512-nDNrUQ/4ruSNYbu749TRY7cfrzPtoLHEXSNBI8aaNY32LlZCajixqRf3FqcKC4p5Cam4VOHYx/t+i5+nKXvrqA==}
+ '@typescript-eslint/eslint-plugin@8.71.0':
+ resolution: {integrity: sha512-pqcS9c1HxZTHt7End4nXqd0s5lJrrFzrgCkKFJrsbUnaL6M3+6oBFZaslg6Gjsl3argl2DDRFROnXARaZ2e4Nw==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
- '@typescript-eslint/parser': ^8.70.1
+ '@typescript-eslint/parser': ^8.71.0
eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
typescript: '>=4.8.4 <6.1.0'
- '@typescript-eslint/parser@8.70.1':
- resolution: {integrity: sha512-nO974WLllwhSFWQXnMLj6nDGa8f0khKEz1JzpPJ1u7Vm/4X1X6ZHajpoknU4bb41vJyMB0HHVyS2GqdhWfIXZw==}
+ '@typescript-eslint/parser@8.71.0':
+ resolution: {integrity: sha512-CG4nPk1f2zc8yw4pALqHsFYH2hdo+h1T9daSp21+Hnxi9LOE3GT9hAfTKJCBXVNM2GmYs1eMEP615wPoeOgk3A==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
typescript: '>=4.8.4 <6.1.0'
- '@typescript-eslint/project-service@8.70.1':
- resolution: {integrity: sha512-62xOgboPfwc3/IgPSX/W6oQR3ZbF04194FPGUGH8HL8iLFHbt/456/8Ph1wLNUgVF+s94FlHoipBsz+v7+LMnA==}
+ '@typescript-eslint/project-service@8.71.0':
+ resolution: {integrity: sha512-aABjw5rjBacYONVPaPiWOCjJu0vEF4a25iQuodlmQYL1trtLZ0X/y+2Vzl3BKI1odM4LnwLE1oUDXYp1wzx1TQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
typescript: '>=4.8.4 <6.1.0'
- '@typescript-eslint/scope-manager@8.70.1':
- resolution: {integrity: sha512-Pa0EeSeAusQc1WbjQMac+YfenewYTBu0KjgYvkUKwhXaHUKbFog23Dm/rp0DX/6tyYOQ3Xl1a+3EcFNZynGHCw==}
+ '@typescript-eslint/scope-manager@8.71.0':
+ resolution: {integrity: sha512-gWF0BhUcnjZxSpLE8ngS/59n2SB0J3YqRxvX1+2aoRJk9hNtHSLOV+TcarFiOr5ipXm3yc1QrI4c9YZc8zyCxw==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
- '@typescript-eslint/tsconfig-utils@8.70.1':
- resolution: {integrity: sha512-jumze1fPI+sDOaM2TWGQdn39PDxTr7TZGeuyLkAbNyx2vtMT3uRnVKChN0hfht5V2TugphJzF6bYXvBcE09qqg==}
+ '@typescript-eslint/tsconfig-utils@8.71.0':
+ resolution: {integrity: sha512-Z1UlWHADEK2Mlb9NpWfDeSjqoZ5EyrOv4R3eQpbkzqn/EwaIdOpXXupEA1+0ZIOSJSZZDBHG0BrQyN8zUG6Pwg==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
typescript: '>=4.8.4 <6.1.0'
- '@typescript-eslint/type-utils@8.70.1':
- resolution: {integrity: sha512-7zKTnyvaVWqzLZHPFQtX1hVHqgkMC+WebPWakNCSyrQVbIP1AM0L0TlBZtACldIRb6PptI8Odk+jyZ5kP3B1VA==}
+ '@typescript-eslint/type-utils@8.71.0':
+ resolution: {integrity: sha512-i8uO1qbdxeKgRnS5sCRt6On3/nfo2d2DwQe3Yvjx543zLy7r8ySqRuPPiIIXAhS03U0v5NfAFx+rUgxFzKKwNw==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
typescript: '>=4.8.4 <6.1.0'
- '@typescript-eslint/types@8.70.1':
- resolution: {integrity: sha512-Dm1ypdhhrGCTyyehxElhgJ6kgk8MVCv5qXdoOVqPr1uqk42jX8KjrZqhROvdShczA8qrDoYiOWn1ykWlx2k81Q==}
+ '@typescript-eslint/types@8.71.0':
+ resolution: {integrity: sha512-cJ4OoxPGWvFnBTnSZyaU+qJzGTqPTGJY+gDchj6cRyLRdmIdt4rcsE4twj+zPfrNiWuVi38wijHzShL++Z9atQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
- '@typescript-eslint/typescript-estree@8.70.1':
- resolution: {integrity: sha512-TU8PwyGN0PQJUcE96mw8eCQ44SmxGdQlJmlWakHaHQ15eIuuvye5yNtmh/i6oS88jzXVQB71xdNkbkB/fMwL0g==}
+ '@typescript-eslint/typescript-estree@8.71.0':
+ resolution: {integrity: sha512-PEEF4G5sLLWAS5BpPrUvms4ySZkiBQQZM4z+3ReI46axK5Vqr/vXBQatJQIZZOYdGyPUAKTtsrWzpqKuU+3DEw==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
typescript: '>=4.8.4 <6.1.0'
- '@typescript-eslint/utils@8.70.1':
- resolution: {integrity: sha512-Esgul8MsnKnRLdYU2Eb2cRV9bS5HJYtKj1ByJnOzzG2M58DGdSUQ1jUuILxipqcpB2h9WLrbD5GijIWUjX/Tqw==}
+ '@typescript-eslint/utils@8.71.0':
+ resolution: {integrity: sha512-pKR/tEMVrXZG23UFKUn5BQf3zfmfk7KQceI2cGzywZ5nxM5Eu3hEJU1utjWzydtzBbcJAQhHN8iPCxobHpPcZQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
typescript: '>=4.8.4 <6.1.0'
- '@typescript-eslint/visitor-keys@8.70.1':
- resolution: {integrity: sha512-Vwj9lUIW5Xq3wQ9w6gv3R86g1hMK8f2zNOdGTAgeXUMMXFK78G9ruCjjqutHMNJc0+CH7LYRnHeUB9IT8wFmcw==}
+ '@typescript-eslint/visitor-keys@8.71.0':
+ resolution: {integrity: sha512-8eQ9R218XORK+KLosnf4bu/QsUXvUyVwTbArg7/0NMB1Pu87OJKvj4nhFblkYE8gQV73mW1dx1ptlPCkwRGa7A==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@vitejs/plugin-vue-jsx@5.1.6':
'@vitest/browser':
optional: true
- '@vitest/istanbul-lib-coverage@1.0.1':
- resolution: {integrity: sha512-k3DJZ8LhMBK9NS4SclF1ASD3OgXEWDorbIcPTRDK0/Zae6fRvu+fJRxtFdLfHsa9Y24beCdPnoNZ4LviTNstfA==}
+ '@vitest/istanbul-lib-coverage@1.0.2':
+ resolution: {integrity: sha512-9J/JMwOf9AoJhAywhrn7ScKTL38hsWQP/qPG60OtaAFcQ5OXPwKsxZFlbnuCKmZ61m8/lGgHYnFpdyQZUvG/iA==}
engines: {node: '>=22'}
- '@vitest/istanbul-lib-report@1.0.1':
- resolution: {integrity: sha512-1EOLRfsTMnyAr3+kEAsP4o9dhaDlGPpD7H5iLBBeq//YpNB1VIahkPhB+eRp9N2Dkfw8oySROjE3yf9XDeaIkQ==}
+ '@vitest/istanbul-lib-report@1.0.2':
+ resolution: {integrity: sha512-gUsfXZJbzPamoIY5TvHFiMMoXESBrUMo+xqaj+rYrWI69+EnvRlYBlP96ZnHPY4vX8kyUpgAnFUCg5wZG/HkDQ==}
engines: {node: '>=22'}
'@vitest/mocker@5.0.3':
'@vue/devtools-shared@8.2.1':
resolution: {integrity: sha512-Fkac7lUdGReh6pVOi3AYPRGe82LQqRmAfThW7RRligOAP0ZA/Z1z9XLHDM9dv34pV2HRc79DK8uKPeG2fLnA/g==}
- '@vue/language-core@3.3.11':
- resolution: {integrity: sha512-QJmpliwAVpC/OxubIByPAhNzsQPRc8/gxlN2qnVzVfIMjMDz/9RnXRFoetjz5yEgXVXyp4LqhXq3V53PjmNzFw==}
+ '@vue/language-core@3.3.12':
+ resolution: {integrity: sha512-k7uIKR0n4CBidk233y7cac9osnDQ5WRidTl6nsrNvpZ3nf8cx9iJxRVsRzVsoPRHbowT/iKgGMTS9RGov3jeIA==}
'@vue/reactivity@3.5.43':
resolution: {integrity: sha512-G/c9GyOZNI2jVaaS6OX1EF1SSFSv7H0ERqNTl4+DTFMlZmB5eVAB53aLQNam/7NL2NPtaDD7RdVrzf8uJzMuOA==}
resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
engines: {node: '>=8'}
- ansi-regex@6.3.0:
- resolution: {integrity: sha512-WpDfL7NO6j7tH88IDBNVdUJxDh9nmCteAVW9dsep846XdwF4naCBK+/tGLX3KJgcpgMRXCFlTM2hKGoK9FsdrQ==}
+ ansi-regex@6.4.0:
+ resolution: {integrity: sha512-KzTVk2tCWAHtYrvvvaP8bJKJq2pVinhLcGEQdtLIYPbmNGNyYe8QwNaTUYQp2J7/vIsUKt5QCqAfUkYyG9DkOw==}
engines: {node: '>=12'}
ansi-styles@4.3.0:
base64-js@1.5.1:
resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
- baseline-browser-mapping@2.11.25:
- resolution: {integrity: sha512-gMmEShwwq7FJqMwvfRwvCl00v4kN+KOfJqXn+f4nrufak5gNHJOksd/60Dvjuz7sI8Y5WiSFBa8FEYr+zoyqCw==}
+ baseline-browser-mapping@2.11.27:
+ resolution: {integrity: sha512-ElY12DaROGuan+lMmZ8Cvo/ZUbXPe7Enc/9VU/b1T3Kp4dwytRcNdR8DoSJN5SNJT/CuvcCA0DHDVmMOCePdRQ==}
engines: {node: '>=6.0.0'}
hasBin: true
resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==}
engines: {node: '>=8'}
- browserslist@4.29.0:
- resolution: {integrity: sha512-3GSvyjvDI4Dur1Meg2BekJquu5uF+9R9a1+5M1Mde192eZoXbeXjzgOsgqPS2V8D5wrrip0gR5Hf/GhWQ9ZzaA==}
+ browserslist@4.29.3:
+ resolution: {integrity: sha512-1R4kiYKXGViqEN0CnoDrXc1StD9niAwu+j2dukWzrD4bJgsD4lDmEp0CRbc6E/vYJIfTHwPmwyaKtVSudICdPA==}
engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
hasBin: true
resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==}
engines: {node: '>=10'}
- caniuse-lite@1.0.30001810:
- resolution: {integrity: sha512-TITQPUkaz+aVk5GL6NhOdwk1aEaNTSDPsGFWrTuhKGtjTF70jL/Oht2W4c6rXUe5fu7Ie19VIahAXHIIiWWNeg==}
+ caniuse-lite@1.0.30001814:
+ resolution: {integrity: sha512-/Uaf1lAzr59XcMpW0o96WoEfr+VXK2OX4U9AgFoiSHsVJ4HppnIFUjtYzsyDH2+tgANaQb2/oxYGwCPapN1FpA==}
- chai@6.2.2:
- resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==}
+ chai@6.3.0:
+ resolution: {integrity: sha512-XWAtwJ6OHO+tj0EKCs0Y2UamnyOxseZWltU4x2U2wh8g4AigdjwvtUjvLP2tqkA/avxHEtzxNaqGq/YGNwckKg==}
engines: {node: '>=18'}
chalk@4.1.2:
effect@3.22.2:
resolution: {integrity: sha512-fm1CQXCs7GTMwpadZxwU5rnAb2bN6mbQpknq/pXBGCzSKkosNYb9k0wsoL0TfB10rSa5xOEZJjo77u/KLXSANg==}
- electron-to-chromium@1.5.438:
- resolution: {integrity: sha512-AN9xMU1hJiT65LkCUPL1DZm5TCbOPb2Qsm5pYwFLEXp6/qj9SdT4yMiqs7Qqstwvkn1zF7l36SRGt+s6XcJ0FA==}
+ electron-to-chromium@1.5.444:
+ resolution: {integrity: sha512-5ss/uJfoDYDHT0lfJzT6FbcskIzROIOPf0BbbFkGcvDzoJU7i//9GDrwwIHQVmIsrAGiF3ihpADBRIsrEFt1rQ==}
emoji-regex@10.6.0:
resolution: {integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==}
end-of-stream@1.4.5:
resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==}
- enhanced-resolve@5.25.1:
- resolution: {integrity: sha512-nGXts5znJzmWPu+mIE9izCOzdg63oJca2mDzGWWTth7sr4aCToKcoyFVBQwN75Ij5Pf6p510EwkTqViTRzDV+w==}
+ enhanced-resolve@5.26.0:
+ resolution: {integrity: sha512-9vhedylFonb2YGogzUKX6+Ja72gOJbN1QHAqdrvqLwhdl/QWbKopzoUC9EbQNVsAns/bx/4uyqalrQAoy1IByw==}
engines: {node: '>=10.13.0'}
entities@7.0.1:
peerDependencies:
eslint: '>=8'
- eslint-plugin-jsdoc@65.0.1:
- resolution: {integrity: sha512-7IvB+ZS71WCw5tGKh6f6D5k55qtXKdrGTnIpQTBKWdpyZ5IfhGHfh0i6ArWlCJ4JEtz22YRk5Bqu1MvbOlpMKQ==}
+ eslint-plugin-jsdoc@65.0.2:
+ resolution: {integrity: sha512-zy1VuN7NNUmfXk15B5ZDAGTXBiye+JskSJZ22Qd5E3VlUluX/RHf8TXYTj8yradaKKJt+69aylhJS22TX/aeRA==}
engines: {node: ^22.22.2 || >=24.15.0}
peerDependencies:
eslint: ^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0
typescript:
optional: true
- eslint-plugin-n@18.3.0:
- resolution: {integrity: sha512-cPVguuDe6DrIPb/qUXHf8P89MaVTUmiYWwpt5gX5AILsvRIiZAxMFXcFR6QHYBksqKJpjfUBlL/RleCJUWcD7w==}
+ eslint-plugin-n@18.4.1:
+ resolution: {integrity: sha512-2sBReWOvCbzgkPri9M5SFmD5EbIwZLa1xz6B5RZsrI19WSFKpyx1sOMr+pdk8puCiXuEgCbErEHy1PuaUrc/4w==}
engines: {node: ^20.19.0 || ^22.13.0 || >=24}
peerDependencies:
eslint: '>=8.57.1'
resolution: {integrity: sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==}
engines: {node: ^20.19.0 || ^22.13.0 || >=24}
- eslint@10.11.0:
- resolution: {integrity: sha512-P7a6UEEqb9G95MYAtqkmsTbVXIYyzIfl6NGOIJk162PaahFxFyeGcrlXYFSiagECg4sEm8IseJdZBKR3rx6MsQ==}
+ eslint@10.12.0:
+ resolution: {integrity: sha512-npHjrHb2o4ehH3mE+YuxABR+0gyb0aVWCIolgJDArwCiHEIsonBm3ZKjel5b0U5+a+MbEvIyRCGCzD3eKKK2NA==}
engines: {node: ^20.19.0 || ^22.13.0 || >=24}
hasBin: true
peerDependencies:
resolution: {integrity: sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==}
engines: {node: '>= 18'}
- exsolve@1.1.1:
- resolution: {integrity: sha512-9U/jZUgjnSGyntRr6y5Muu1MJcwFl6kPu7k8qLF0IMNfLqvw0NZ4nnVDq0RVoZ0RvCyumib4Ez3KYrVfilrw+g==}
+ exsolve@1.1.3:
+ resolution: {integrity: sha512-kuzL9zherLQoqDeQaS55AiiIg1kb4KYXaSusTtgZTPlvKP2gGCI82LNmBNh5RjDVUXIcmgx+/Mx7vPwRzBbhJA==}
fast-check@3.23.2:
resolution: {integrity: sha512-h5+1OzzfCC3Ef7VbtKdcv7zsstUQwUDlYpUTvjeUsJAssPgLn7QzbboPtL5ro04Mq0rPOsMzl7q5hIbRs2wD1A==}
fast-deep-equal@3.1.3:
resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
- fast-equals@6.0.4:
- resolution: {integrity: sha512-JiBeR0k76FIahi9zAtQR7X1jgZDKLE+fs5S9ryjOGYZWZCOcm8QiwgeahkFvaboS3i6PkGBopRznHKuKhm9zpg==}
+ fast-equals@6.1.0:
+ resolution: {integrity: sha512-MDA0N5wXvmx0yR0xeOf20qVMbyaEzS2kuxhBrDrIT5w3Gap7bZ/24jtvOvbthbZFmf+7PCC8XCJKGpmhVtvbIg==}
engines: {node: '>=6.0.0'}
fast-glob@3.3.3:
fecha@4.2.3:
resolution: {integrity: sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==}
- figlet@1.11.4:
- resolution: {integrity: sha512-ZU41480OncL+NVLQrT0GVnbO0COL24sLSrzksioDgunmdJNYEUxhJJZ4Y3x1csN8XXqN5OcCZTLG8lKdquNyfQ==}
+ figlet@1.12.0:
+ resolution: {integrity: sha512-Ith3WsUi0N/EqGt+0/F6XnWhT04sohhH2tT0Dq7GdUmWbMTIoCbg2Jq9qKggmTO62ST5kRCfB311EmIMbizGyA==}
engines: {node: '>= 17.0.0'}
hasBin: true
resolution: {integrity: sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==}
engines: {node: '>=18'}
- globals@17.12.0:
- resolution: {integrity: sha512-cezEd/DTyyht9cvSSURyygXPfy04GtWO/5e6ZPvH7fCtjKz9PYOmuawphw1Ctd1f6C+5JypXfGD7ahNMXvevBA==}
+ globals@17.13.0:
+ resolution: {integrity: sha512-RwMTC61u7hrG3ZJMkZQOK4JLJrKS8QtoTii63EmWvFXHqycY9FObBJQCbsLxSO8kjKhWE5kV1GC+Vb1g3RI0Zg==}
engines: {node: '>=18'}
globby@11.1.0:
resolution: {integrity: sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==}
engines: {node: '>=0.10.0'}
- hono@4.13.8:
- resolution: {integrity: sha512-/Gng7NfoykZl2pjukW5Z6+8Yxm3BPRf86GTbQnt0SbySkvax4fyL4H3HhY1cCpBGmiW9XDRFzRV+CXK2W8QudQ==}
+ hono@4.13.12:
+ resolution: {integrity: sha512-6E2QDAc9Ick9Sq77ZrGS/dk2WUYni91aufTw6LJKpV7w8kW5/GxVUc650FOADOmlwg3K+f7Pun6XlV+pYmW6gw==}
engines: {node: '>=16.9.0'}
hookable@5.5.3:
resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==}
engines: {node: '>= 4'}
- ignore@7.0.10:
- resolution: {integrity: sha512-HpbUakT7xp5miBUywCHf36ZEuAJNklBJDDsGpUIjMzOSmM8ELSfA9Sa/QDPeNeqeoN31u+UTCkL4klCOVvRm4Q==}
+ ignore@7.0.12:
+ resolution: {integrity: sha512-/8UvqAPU9DGTI9k4mxtf49U37Isfwr8Uts96+SBHIkFxPJnHS0Ew4f00sM4Scd8V8EjM0jUNtVDdv1kPdt35lg==}
engines: {node: '>= 4'}
import-fresh@3.3.1:
resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==}
engines: {node: '>=6'}
- import-fresh@4.0.0:
- resolution: {integrity: sha512-Fpi660c7VPDM3fPKYovStd9IP1CPOikf6v/dGxJJMmHPcwYQIMJ4W7kO1avBYEpMqkCh+Dx3Ln6H7VYqgztLjw==}
+ import-fresh@4.0.1:
+ resolution: {integrity: sha512-/XFeXYgkuWgbtqPC7qPKvciXTvhtz7WMNQKQsAM1Kz+j0I5OWJDrybfiya6QtHF9oEvPkLwTHmC9+4OR/MmfIA==}
engines: {node: '>=22.15'}
import-meta-resolve@4.2.0:
peerDependencies:
fp-ts: ^2.5.0
- ip-address@10.7.2:
- resolution: {integrity: sha512-7H/2gFSIitxc0hG3nOI1glS8QLo/EHBFFLk8vEUjXY/xu0AdL8jZ9U1IzO2PUm0d2D/ofQcAifb0g6OBkt8U7w==}
+ ip-address@10.7.3:
+ resolution: {integrity: sha512-A1kdq/tSb5QjvKvAMgIoEvDBIgL7qaqVP/jkvSwYYRZ9iEzvPpopxp2wQfu3SuZRHtpHNxMn8Fs0bS+gf5Xmwg==}
engines: {node: '>= 12'}
ipaddr.js@1.9.1:
resolution: {integrity: sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA==}
hasBin: true
+ js-yaml@5.4.2:
+ resolution: {integrity: sha512-m+aqu+LwO1O6sIopafj8HUVl5aawITwZQe/yHpMCKjaWBaA/d07B/QdMb3529REftiU+RMMHL3Vlsw3hON7vWg==}
+ hasBin: true
+
jsdoc-type-pratt-parser@9.2.2:
resolution: {integrity: sha512-eFY7Rlscvv/BpY6UsVsjKwMhu1tJO/F4U/vC9g3yeRy/JqszuO1UP6Vdq+85G+3FP6fEw/sWR8a5ftPFY86csw==}
engines: {node: ^22.22.2 || >=24.15.0}
resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==}
engines: {node: '>=8.6'}
- mikro-orm@6.6.16:
- resolution: {integrity: sha512-42bvve+XSzvBpOuZeZxJsM5aW4DQnDn23E5to4cvEjUdaPX7jg1bRh+l8f/8/WJJNzG1T05V5vHSe+SHzqN87A==}
+ mikro-orm@6.6.17:
+ resolution: {integrity: sha512-AL7V8ytnDRpwUMcYwOiGNIOW7WuE8ca2hf+pw9jcpSVWwwJNu/j9K574lf0PALaJnikbzidsxmjCUrhQ0ea+sA==}
engines: {node: '>= 18.12.0'}
mime-db@1.54.0:
engines: {node: 20 || >=22}
hasBin: true
- rolldown@1.2.10:
- resolution: {integrity: sha512-OxkA08pSryMK7B3XiFA09B4OJ1xJMPgIYCBMY2xchzpqgBGsV1o0DetPAE+Sl3N3L4oCPiEzmHVSOj7iR04Zog==}
+ rolldown@1.2.12:
+ resolution: {integrity: sha512-8wafseiaG80xmXSfqidUNqZcylTlhmPZZt+za2m+js2sFZ8dTNlhIOV2WcbIPx2hgwPBJpEUGFAMZ9bgBBLTSQ==}
engines: {node: ^20.19.0 || >=22.12.0}
hasBin: true
resolution: {integrity: sha512-hpd+HLON7HdZXqYchMM/+LaTTbdK0AU3NngIJ4KVyWbY9bfQqdL9cD+4yf6dUoU2Ap4VsU0JkQi6FxAI1B2mXQ==}
engines: {node: '>= 18'}
- source-map-js@1.2.1:
- resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
+ source-map-js@1.2.2:
+ resolution: {integrity: sha512-KGj/8Y43x35aZVDtt+J4mK1hoLGHULMYfSkODJNQjNDC3oW1PqPoxMwo0pLUsWM/UEGzON/NxeHywEfNXNP3Vw==}
engines: {node: '>=0.10.0'}
sparse-bitfield@3.0.3:
resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==}
engines: {node: '>= 0.8'}
- std-env@4.2.0:
- resolution: {integrity: sha512-oCUKSupKTHX53EyjDtuZQ64pjLJ6yYCtpmEw0goYxtjG9KpbRe8KAsl2tBUGU9DyMcJ0RwJ8GqJAFzMXcXW1Rw==}
+ std-env@4.3.0:
+ resolution: {integrity: sha512-OtU/EgQ1kIm5KwqQpBC6ZEMXrZRui11w8zgfTWp8cdO9B8OaPsbA8bTHO2P+HNo1VlUTGMVBwPhydu6poeXiag==}
stdin-discarder@0.3.2:
resolution: {integrity: sha512-eCPu1qRxPVkl5605OTWF8Wz40b4Mf45NY5LQmVPQ599knfs5QhASUm9GbJ5BDMDOXgrnh0wyEdvzmL//YMlw0A==}
resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==}
engines: {node: '>=18'}
- string-width@8.2.2:
- resolution: {integrity: sha512-GaPUh5gfdrYzqeVNZvUfT23vYYxXzKYidUcnMtJg/3rxRV63EFZy3k6xfKlmfeJD0176lnUV/Usr3XcwSvFzpg==}
+ string-width@8.3.0:
+ resolution: {integrity: sha512-ZbmZM0JCihQN91dWnxoipT2KOEyHqEyfRXUyjuRhW8b/xnqPDoq4gWEVApTVa9db2wN8mmoikgFBbjh71+cGeQ==}
engines: {node: '>=20'}
string_decoder@1.3.0:
resolution: {integrity: sha512-Cc+OraorugtXNfs50hU9KS369rFXCfgGLpfCfvlc+Ud5u6VWmUQsOAa9HbTvheQdYnrdJqqv1e5oIqXppMYnSw==}
engines: {node: '>=8'}
- tinybench@6.1.4:
- resolution: {integrity: sha512-9APumHG7r4yOk4X4WlkmE71aZcv1gvin1czO3OQ1U9iJcFA5Ja/ygyb0vPOVHTthFozUYs8CLoLUlM8grb2lTQ==}
+ tinybench@6.2.0:
+ resolution: {integrity: sha512-78U2TlB2CnVenajOFzf3BKSm0J6oz5L0NV7g32LCPccvYc0lbWvys4d3uUUCS2B1N8PAf2+aekR8i1KbC3HO7Q==}
engines: {node: '>=20.0.0'}
tinyexec@1.3.1:
resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==}
engines: {node: '>=12.0.0'}
- tinyrainbow@3.1.1:
- resolution: {integrity: sha512-yau8yJdTt989Mm0Bd/236QnzEiPf2xLLTqUZRUJOo/3CB078LSwzei343DgtJVmfJKJE3TMINY1u42SQsP6mXw==}
+ tinyrainbow@3.2.0:
+ resolution: {integrity: sha512-LgO3D9yZJjApUiuUfl9iFAwrtaX4+lok3wJIqttGoKCHlWUqHqbQpnxCf82L8FjgKsh4iGo78hqJwgL8F6To2A==}
engines: {node: '>=14.0.0'}
- tldts-core@7.4.15:
- resolution: {integrity: sha512-ERuv0p98XgSzmlSLJr8vDNxX+uATGInlN97V3R+JpYWaSqn5IG3ewWgCwczk4bkOpgth/o8Nt5FOi4B4w0n/1A==}
+ tldts-core@7.4.16:
+ resolution: {integrity: sha512-MDolfaSJtlSK5Y0A1xl3277ekubZwobpBjugknDizI9O5Rm60a1m8k4ICK+MRsCDzPygT81mp3BBf5RKDlFRfA==}
- tldts@7.4.15:
- resolution: {integrity: sha512-SJVBeHOxDbNoq14CvpAoA2mLEWdbldGK8nR+yumpjY5nrlZxOflcA7p1Qj1gbTGmbu9DY9qLj/bENSWhBzjxRQ==}
+ tldts@7.4.16:
+ resolution: {integrity: sha512-QwBER5KMR86IIjpIiO7H/Z3IMJPsZ1A6RKPAqzTTgOyUQUSt9FdnKcqhTaJmkY6HVrgouZHZR0ncK5QxvmnQeg==}
hasBin: true
to-regex-range@5.0.1:
resolution: {integrity: sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA==}
engines: {node: '>= 18'}
- typescript-eslint@8.70.1:
- resolution: {integrity: sha512-AcWG7KDjZ2THNXsgwttMaGmzVi0VFRlFYfqFHYQRbDpF3owuYbuiL8c7UUrd2k8s3PoSfIQrWfrGXfcElrWLYA==}
+ typescript-eslint@8.71.0:
+ resolution: {integrity: sha512-fBdHYiqQ14RW6mOMXD14Svn82ZsCYAoQSzGRzyEjR59S5A2Krh/l7fGTOQ7iCr8gGy/mHVXtEF7s5fgjEdV0Pw==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
ufo@1.6.4:
resolution: {integrity: sha512-JFNbkD1Svwe0KvGi8GOeLcP4kAWQ609twvCdcHxq1oSL8svv39ZuSvajcD8B+5D0eL4+s1Is2D/O6KN3qcTeRA==}
- undici-types@7.18.2:
- resolution: {integrity: sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==}
-
undici-types@7.24.6:
resolution: {integrity: sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==}
- undici-types@8.11.0:
- resolution: {integrity: sha512-Ophcodxps+mlNdcccUrHXy+Pp8X7Q0ycBk3QelSNl+jBAPMh6cEB/5xs9xDL3PJVnVs2Cp//nnsJySuD9D2WFA==}
+ undici-types@8.11.2:
+ resolution: {integrity: sha512-iMVNmWZ0leK/goS6eXMizSzmm9CDWtyphwbaCms3DNLqRxDL+mMoNVcZMTyyVgXP0N+Z8neAMzDoUOUJL8veKg==}
undici-types@8.9.0:
resolution: {integrity: sha512-KTDyRTYX8sWmKXAikPHHSyc63CRPETMctyjKFupcC6OBLXT3xsN0e9aF7m+mIXutFWpUXuedtowG7iLOzp0kQg==}
- undici@8.11.0:
- resolution: {integrity: sha512-AdzuGcAkzhbha3GgCSoUBx2P0quwym6qdFNCQz/fDGZc8w9XJ741wHUbt2kTmfPz+ZIOMkySFkiY7RXmiDDC8w==}
+ undici@8.11.2:
+ resolution: {integrity: sha512-u4UB2/IrKdU6lFxumHmmo1a3fCQO5tzQllRorfoRS63txhrB7xTpSn1PftwC4qEHkOaqP95fCWW4lJzwErwzhQ==}
engines: {node: '>=22.19.0'}
unicorn-magic@0.3.0:
resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==}
engines: {node: '>= 0.8'}
- vite@8.3.1:
- resolution: {integrity: sha512-/bvH9E9tmCXRGp2uXY3WbOldqpTwFkbha/8ANaEQ6VkxhH60KyqLwgZq6lG2y+4uT55x9+9eUHMpQ7uGnOCKjA==}
+ vite@8.3.2:
+ resolution: {integrity: sha512-SQr1x6W5vVSbROg7vsyXIaxK9b0G7zsT68acdWWRmnBUsgDieLCRG+Rep9WdZgcposvv/GSnr4GUUBqB3vXq6w==}
engines: {node: ^20.19.0 || >=22.12.0}
hasBin: true
peerDependencies:
resolution: {integrity: sha512-JvONPptw3GAQGXlVV2utDcHx0BiY34FupW/kI6mZ5x06ER5DdPG/tXWMVHjTNULF5uKPOUUD0SaXg5QaubJL0A==}
engines: {node: '>=8.0.0 || >=10.0.0'}
- vscode-jsonrpc@9.0.2:
- resolution: {integrity: sha512-SbQSV9yRemARxeXw6LU5sS6Zq0e9/DgCCX5yelH263ZQWukbTk8EF8fjTrr1dziasf4GwlJbvTwFnTrnQFWZXQ==}
+ vscode-jsonrpc@9.0.3:
+ resolution: {integrity: sha512-qETYf9PCl1lVfwR+oBmMfc4vP2nqLNA35ABxorw3j06+xlKRgDVxbLgKm4Y/xPL0YWdeDjLoI4ImICIT3nB0zQ==}
engines: {node: '>=14.0.0'}
- vscode-languageserver-protocol@3.18.3:
- resolution: {integrity: sha512-DF49+WeV5py4zO5hhobp60jjsDSK0lAqA0OuKBLBvp423HPWQcCbhZz3JgyfIewsEz2f8U+X75xNIFHdiXZm2w==}
+ vscode-languageserver-protocol@3.18.4:
+ resolution: {integrity: sha512-CCXbyr99dafRJzJH/0Ryk7ly5T8XPmSR0//OuF2w4m+9JiOin7zcAT5iET8GRE7Z9taFBejb3GbM0a0mbjdbrg==}
- vscode-languageserver-textdocument@1.0.14:
- resolution: {integrity: sha512-EQyqJMi552E4ZTf46izQ4Fj6XquqxCySR3J5ZSD1SisMf6RfpeOWHxGBE8Gr6V0/3GHIGdAzDn8F8+1nTGCnoQ==}
+ vscode-languageserver-textdocument@1.0.15:
+ resolution: {integrity: sha512-Gsfr9S8LU3S6885cS+NaleasJ0n3V5y/SOTqHCifQiXWFw3G9w9/YItDfwHrlCz+dSZWWZ8ilYVMcJe7Sz7Sxw==}
- vscode-languageserver-types@3.18.3:
- resolution: {integrity: sha512-XIlzJ7Qp/jzSI1ds7/FwPAWrPeTZA7pAtlW4hdJ1J6xXWJL6dR9QYnDhJOdLzdKhUQ5Mm6mvUMw+3DcOQQasPw==}
+ vscode-languageserver-types@3.18.4:
+ resolution: {integrity: sha512-nQsacoiijvl0fRf2q28uFDrAVwRhdCa3QveiF7/CX0r18mLR5FU/1WskSsCKOjmEbtteKUnY8x8Yzxop+k0zwg==}
vscode-uri@3.2.0:
resolution: {integrity: sha512-m2gXo3bn0G1kT9InzMf07fTbqMbGtyckj3bH5ktLO+1Ssv+yiATZ4dhwaQv9UZWxJh6E9IFGnQyjgWVDWVBDrg==}
- vue-component-type-helpers@3.3.11:
- resolution: {integrity: sha512-LwcxzeliO9fkQcpJG0PoX8X5kmAhKmH9wkpDLxNabwzkQ9Zeib2YVHwFV4pcWmMLfXVfjr/dSV+DaJ3cIPgSNA==}
+ vue-component-type-helpers@3.3.12:
+ resolution: {integrity: sha512-HwCqmgTfLFskQc4P5TRnbduCMyPat2Ezu0elLWOGn0dD/6BKHP15B5bxce6aDi+E4kblPtru8amIBQzu0Di3qQ==}
vue-eslint-parser@10.4.1:
resolution: {integrity: sha512-Gk6gRDj0n/fkRa3C3l0bBheoBckUq/Rs0F/TvMWIS6nzzx67amAViMe9CkNgsP2tXyQONvGiHQESHwFtZ3aYDA==}
peerDependencies:
vue: ^3.0
- vue-tsc@3.3.11:
- resolution: {integrity: sha512-gOb0B9rtU2+f1dszwPqSH5kAieIF9ReeLhD3kSRNHv5WZZUQz/JdVXW0RTdqhNTMlQkqKzrTTviqKr/4FYZraQ==}
+ vue-tsc@3.3.12:
+ resolution: {integrity: sha512-kbpaz18O9jPVkXVHD1BEjpUXM6WhXSu+62L1sGGr6Wbzd8GbvkBQWjoj3KZ/C0aq2MZllce7UdtWzwCIYsBMGA==}
hasBin: true
peerDependencies:
typescript: '>=5.0.0'
'@arr/every@1.0.1': {}
- '@asamuzakjp/css-color@7.0.1':
+ '@asamuzakjp/css-color@7.1.3':
dependencies:
- '@csstools/css-calc': 3.4.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.1))(@csstools/css-tokenizer@4.0.1)
- '@csstools/css-color-parser': 4.2.3(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.1))(@csstools/css-tokenizer@4.0.1)
- '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.1)
- '@csstools/css-tokenizer': 4.0.1
+ '@csstools/css-calc': 3.4.3(@csstools/css-parser-algorithms@4.0.2(@csstools/css-tokenizer@4.0.2))(@csstools/css-tokenizer@4.0.2)
+ '@csstools/css-color-parser': 4.2.6(@csstools/css-parser-algorithms@4.0.2(@csstools/css-tokenizer@4.0.2))(@csstools/css-tokenizer@4.0.2)
+ '@csstools/css-parser-algorithms': 4.0.2(@csstools/css-tokenizer@4.0.2)
+ '@csstools/css-tokenizer': 4.0.2
lru-cache: 11.5.3
- '@asamuzakjp/dom-selector@9.2.1':
+ '@asamuzakjp/dom-selector@9.2.4':
dependencies:
bidi-js: 1.1.0
css-tree: 3.2.1
dependencies:
'@babel/compat-data': 7.29.7
'@babel/helper-validator-option': 7.29.7
- browserslist: 4.29.0
+ browserslist: 4.29.3
lru-cache: 5.1.1
semver: 6.3.1
'@colors/colors@1.6.1': {}
- '@commitlint/cli@21.2.3(@types/node@24.19.0)(conventional-commits-parser@7.1.2)(typescript@6.0.3)':
+ '@commitlint/cli@21.2.3(@types/node@24.19.1)(conventional-commits-parser@7.1.2)(typescript@6.0.3)':
dependencies:
'@commitlint/config-conventional': 21.2.3
'@commitlint/format': 21.2.3
'@commitlint/lint': 21.2.3
- '@commitlint/load': 21.2.3(@types/node@24.19.0)(typescript@6.0.3)
+ '@commitlint/load': 21.2.3(@types/node@24.19.1)(typescript@6.0.3)
'@commitlint/read': 21.2.3(conventional-commits-parser@7.1.2)
'@commitlint/types': 21.2.3
tinyexec: 1.3.1
'@commitlint/rules': 21.2.3
'@commitlint/types': 21.2.3
- '@commitlint/load@21.2.3(@types/node@24.19.0)(typescript@6.0.3)':
+ '@commitlint/load@21.2.3(@types/node@24.19.1)(typescript@6.0.3)':
dependencies:
'@commitlint/config-validator': 21.2.3
'@commitlint/execute-rule': 21.0.1
'@commitlint/resolve-extends': 21.2.3
'@commitlint/types': 21.2.3
cosmiconfig: 9.0.2(typescript@6.0.3)
- cosmiconfig-typescript-loader: 6.3.0(@types/node@24.19.0)(cosmiconfig@9.0.2(typescript@6.0.3))(typescript@6.0.3)
+ cosmiconfig-typescript-loader: 6.3.0(@types/node@24.19.1)(cosmiconfig@9.0.2(typescript@6.0.3))(typescript@6.0.3)
es-toolkit: 1.52.0
is-plain-obj: 4.1.0
picocolors: 1.1.1
'@cspell/dict-aws': 4.0.17
'@cspell/dict-bash': 4.2.3
'@cspell/dict-companies': 3.2.13
- '@cspell/dict-cpp': 7.1.2
+ '@cspell/dict-cpp': 7.1.3
'@cspell/dict-cryptocurrencies': 5.0.5
'@cspell/dict-csharp': 4.0.8
'@cspell/dict-css': 4.1.2
'@cspell/dict-docker': 1.1.17
'@cspell/dict-dotnet': 5.0.15
'@cspell/dict-elixir': 4.0.8
- '@cspell/dict-en-common-misspellings': 2.2.2
+ '@cspell/dict-en-common-misspellings': 2.2.3
'@cspell/dict-en-gb-mit': 3.1.29
'@cspell/dict-en_us': 4.4.40
'@cspell/dict-filetypes': 3.0.18
'@cspell/dict-fonts': 4.0.6
'@cspell/dict-fsharp': 1.1.1
'@cspell/dict-fullstack': 3.2.9
- '@cspell/dict-gaming-terms': 1.1.2
+ '@cspell/dict-gaming-terms': 1.1.3
'@cspell/dict-git': 3.1.1
'@cspell/dict-golang': 6.0.29
'@cspell/dict-google': 1.0.9
'@cspell/dict-markdown': 2.0.18(@cspell/dict-css@4.1.2)(@cspell/dict-html-symbol-entities@4.0.5)(@cspell/dict-html@4.0.16)(@cspell/dict-typescript@3.2.3)
'@cspell/dict-monkeyc': 1.1.2
'@cspell/dict-node': 5.0.9
- '@cspell/dict-npm': 5.2.50
+ '@cspell/dict-npm': 5.2.52
'@cspell/dict-php': 4.1.3
'@cspell/dict-powershell': 5.0.17
- '@cspell/dict-public-licenses': 2.0.16
+ '@cspell/dict-public-licenses': 2.0.17
'@cspell/dict-python': 4.5.2
'@cspell/dict-r': 2.1.1
'@cspell/dict-ruby': 5.1.4
'@cspell/dict-rust': 4.1.2
'@cspell/dict-scala': 5.0.11
'@cspell/dict-shell': 1.2.0
- '@cspell/dict-software-terms': 5.4.5
+ '@cspell/dict-software-terms': 5.4.6
'@cspell/dict-sql': 2.2.1
'@cspell/dict-svelte': 1.0.7
'@cspell/dict-swift': 2.0.6
'@cspell/dict-companies@3.2.13': {}
- '@cspell/dict-cpp@7.1.2': {}
+ '@cspell/dict-cpp@7.1.3': {}
'@cspell/dict-cryptocurrencies@5.0.5': {}
'@cspell/dict-elixir@4.0.8': {}
- '@cspell/dict-en-common-misspellings@2.2.2': {}
+ '@cspell/dict-en-common-misspellings@2.2.3': {}
'@cspell/dict-en-gb-mit@3.1.29': {}
'@cspell/dict-fullstack@3.2.9': {}
- '@cspell/dict-gaming-terms@1.1.2': {}
+ '@cspell/dict-gaming-terms@1.1.3': {}
'@cspell/dict-git@3.1.1': {}
'@cspell/dict-node@5.0.9': {}
- '@cspell/dict-npm@5.2.50': {}
+ '@cspell/dict-npm@5.2.52': {}
'@cspell/dict-php@4.1.3': {}
'@cspell/dict-powershell@5.0.17': {}
- '@cspell/dict-public-licenses@2.0.16': {}
+ '@cspell/dict-public-licenses@2.0.17': {}
'@cspell/dict-python@4.5.2':
dependencies:
'@cspell/dict-shell@1.2.0': {}
- '@cspell/dict-software-terms@5.4.5': {}
+ '@cspell/dict-software-terms@5.4.6': {}
'@cspell/dict-sql@2.2.1': {}
'@cspell/url': 10.3.6
import-meta-resolve: 4.2.0
- '@cspell/eslint-plugin@10.3.6(eslint@10.11.0(jiti@2.6.1)(supports-color@7.2.0))':
+ '@cspell/eslint-plugin@10.3.6(eslint@10.12.0(jiti@2.6.1)(supports-color@7.2.0))':
dependencies:
'@cspell/cspell-types': 10.3.6
'@cspell/url': 10.3.6
cspell-lib: 10.3.6
- eslint: 10.11.0(jiti@2.6.1)(supports-color@7.2.0)
+ eslint: 10.12.0(jiti@2.6.1)(supports-color@7.2.0)
synckit: 0.11.13
'@cspell/filetypes@10.3.6': {}
dependencies:
'@jridgewell/trace-mapping': 0.3.9
- '@csstools/color-helpers@6.1.1': {}
+ '@csstools/color-helpers@6.1.2': {}
- '@csstools/css-calc@3.4.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.1))(@csstools/css-tokenizer@4.0.1)':
+ '@csstools/css-calc@3.4.3(@csstools/css-parser-algorithms@4.0.2(@csstools/css-tokenizer@4.0.2))(@csstools/css-tokenizer@4.0.2)':
dependencies:
- '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.1)
- '@csstools/css-tokenizer': 4.0.1
+ '@csstools/css-parser-algorithms': 4.0.2(@csstools/css-tokenizer@4.0.2)
+ '@csstools/css-tokenizer': 4.0.2
- '@csstools/css-color-parser@4.2.3(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.1))(@csstools/css-tokenizer@4.0.1)':
+ '@csstools/css-color-parser@4.2.6(@csstools/css-parser-algorithms@4.0.2(@csstools/css-tokenizer@4.0.2))(@csstools/css-tokenizer@4.0.2)':
dependencies:
- '@csstools/color-helpers': 6.1.1
- '@csstools/css-calc': 3.4.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.1))(@csstools/css-tokenizer@4.0.1)
- '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.1)
- '@csstools/css-tokenizer': 4.0.1
+ '@csstools/color-helpers': 6.1.2
+ '@csstools/css-calc': 3.4.3(@csstools/css-parser-algorithms@4.0.2(@csstools/css-tokenizer@4.0.2))(@csstools/css-tokenizer@4.0.2)
+ '@csstools/css-parser-algorithms': 4.0.2(@csstools/css-tokenizer@4.0.2)
+ '@csstools/css-tokenizer': 4.0.2
- '@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.1)':
+ '@csstools/css-parser-algorithms@4.0.2(@csstools/css-tokenizer@4.0.2)':
dependencies:
- '@csstools/css-tokenizer': 4.0.1
+ '@csstools/css-tokenizer': 4.0.2
- '@csstools/css-syntax-patches-for-csstree@1.1.14(css-tree@3.2.1)':
+ '@csstools/css-syntax-patches-for-csstree@1.1.15(css-tree@3.2.1)':
optionalDependencies:
css-tree: 3.2.1
- '@csstools/css-tokenizer@4.0.1': {}
+ '@csstools/css-tokenizer@4.0.2': {}
'@dabh/diagnostics@2.0.9':
dependencies:
'@es-joy/jsdoccomment@0.98.0':
dependencies:
'@types/estree': 1.0.9
- '@typescript-eslint/types': 8.70.1
+ '@typescript-eslint/types': 8.71.0
comment-parser: 1.4.9
esquery: 1.7.0
jsdoc-type-pratt-parser: 9.2.2
'@esbuild/win32-x64@0.28.2':
optional: true
- '@eslint-community/eslint-utils@4.10.1(eslint@10.11.0(jiti@2.6.1)(supports-color@7.2.0))':
+ '@eslint-community/eslint-utils@4.10.1(eslint@10.12.0(jiti@2.6.1)(supports-color@7.2.0))':
dependencies:
- eslint: 10.11.0(jiti@2.6.1)(supports-color@7.2.0)
+ eslint: 10.12.0(jiti@2.6.1)(supports-color@7.2.0)
eslint-visitor-keys: 3.4.3
'@eslint-community/regexpp@4.12.2': {}
dependencies:
'@types/json-schema': 7.0.15
- '@eslint/js@10.0.1(eslint@10.11.0(jiti@2.6.1)(supports-color@7.2.0))':
+ '@eslint/js@10.0.1(eslint@10.12.0(jiti@2.6.1)(supports-color@7.2.0))':
optionalDependencies:
- eslint: 10.11.0(jiti@2.6.1)(supports-color@7.2.0)
+ eslint: 10.12.0(jiti@2.6.1)(supports-color@7.2.0)
'@eslint/object-schema@3.0.5': {}
'@exodus/bytes@1.16.0': {}
- '@hono/node-server@2.1.1(hono@4.13.8)':
+ '@hono/node-server@2.1.3(hono@4.13.12)':
dependencies:
- hono: 4.13.8
+ hono: 4.13.12
'@humanfs/core@0.19.2':
dependencies:
'@jercle/yargonaut@1.1.5':
dependencies:
chalk: 4.1.2
- figlet: 1.11.4
+ figlet: 1.12.0
parent-require: 1.0.0
'@jridgewell/gen-mapping@0.3.13':
'@keyv/serialize@1.1.1': {}
- '@mikro-orm/better-sqlite@6.6.14(@mikro-orm/core@6.6.16)(mariadb@3.4.5)(supports-color@7.2.0)':
+ '@mikro-orm/better-sqlite@6.6.14(@mikro-orm/core@6.6.17)(mariadb@3.4.5)(supports-color@7.2.0)':
dependencies:
- '@mikro-orm/core': 6.6.16
- '@mikro-orm/knex': 6.6.14(@mikro-orm/core@6.6.16)(better-sqlite3@11.10.0)(mariadb@3.4.5)(supports-color@7.2.0)
+ '@mikro-orm/core': 6.6.17
+ '@mikro-orm/knex': 6.6.14(@mikro-orm/core@6.6.17)(better-sqlite3@11.10.0)(mariadb@3.4.5)(supports-color@7.2.0)
better-sqlite3: 11.10.0
fs-extra: 11.3.3
sqlstring-sqlite: 0.1.1
- supports-color
- tedious
- '@mikro-orm/cli@6.6.16(better-sqlite3@11.10.0)(mariadb@3.4.5)(supports-color@7.2.0)':
+ '@mikro-orm/cli@6.6.17(better-sqlite3@11.10.0)(mariadb@3.4.5)(supports-color@7.2.0)':
dependencies:
'@jercle/yargonaut': 1.1.5
- '@mikro-orm/core': 6.6.16
- '@mikro-orm/knex': 6.6.16(@mikro-orm/core@6.6.16)(better-sqlite3@11.10.0)(mariadb@3.4.5)(supports-color@7.2.0)
+ '@mikro-orm/core': 6.6.17
+ '@mikro-orm/knex': 6.6.17(@mikro-orm/core@6.6.17)(better-sqlite3@11.10.0)(mariadb@3.4.5)(supports-color@7.2.0)
fs-extra: 11.3.3
tsconfig-paths: 4.2.0
yargs: 17.7.2
- supports-color
- tedious
- '@mikro-orm/core@6.6.16':
+ '@mikro-orm/core@6.6.17':
dependencies:
dataloader: 2.2.3
dotenv: 17.3.1
esprima: 4.0.1
fs-extra: 11.3.3
globby: 11.1.0
- mikro-orm: 6.6.16
+ mikro-orm: 6.6.17
reflect-metadata: 0.2.2
- '@mikro-orm/knex@6.6.14(@mikro-orm/core@6.6.16)(better-sqlite3@11.10.0)(mariadb@3.4.5)(supports-color@7.2.0)':
+ '@mikro-orm/knex@6.6.14(@mikro-orm/core@6.6.17)(better-sqlite3@11.10.0)(mariadb@3.4.5)(supports-color@7.2.0)':
dependencies:
- '@mikro-orm/core': 6.6.16
+ '@mikro-orm/core': 6.6.17
fs-extra: 11.3.3
knex: 3.2.10(better-sqlite3@11.10.0)(supports-color@7.2.0)
sqlstring: 2.3.3
- supports-color
- tedious
- '@mikro-orm/knex@6.6.16(@mikro-orm/core@6.6.16)(better-sqlite3@11.10.0)(mariadb@3.4.5)(supports-color@7.2.0)':
+ '@mikro-orm/knex@6.6.17(@mikro-orm/core@6.6.17)(better-sqlite3@11.10.0)(mariadb@3.4.5)(supports-color@7.2.0)':
dependencies:
- '@mikro-orm/core': 6.6.16
+ '@mikro-orm/core': 6.6.17
fs-extra: 11.3.3
knex: 3.2.10(better-sqlite3@11.10.0)(supports-color@7.2.0)
sqlstring: 2.3.3
- supports-color
- tedious
- '@mikro-orm/mariadb@6.6.16(@mikro-orm/core@6.6.16)(better-sqlite3@11.10.0)(supports-color@7.2.0)':
+ '@mikro-orm/mariadb@6.6.17(@mikro-orm/core@6.6.17)(better-sqlite3@11.10.0)(supports-color@7.2.0)':
dependencies:
- '@mikro-orm/core': 6.6.16
- '@mikro-orm/knex': 6.6.16(@mikro-orm/core@6.6.16)(better-sqlite3@11.10.0)(mariadb@3.4.5)(supports-color@7.2.0)
+ '@mikro-orm/core': 6.6.17
+ '@mikro-orm/knex': 6.6.17(@mikro-orm/core@6.6.17)(better-sqlite3@11.10.0)(mariadb@3.4.5)(supports-color@7.2.0)
mariadb: 3.4.5
transitivePeerDependencies:
- better-sqlite3
- supports-color
- tedious
- '@mikro-orm/reflection@6.6.16(@mikro-orm/core@6.6.16)':
+ '@mikro-orm/reflection@6.6.17(@mikro-orm/core@6.6.17)':
dependencies:
- '@mikro-orm/core': 6.6.16
+ '@mikro-orm/core': 6.6.17
globby: 11.1.0
ts-morph: 27.0.2
'@modelcontextprotocol/sdk@1.31.0(supports-color@7.2.0)(zod@4.6.5)':
dependencies:
- '@hono/node-server': 2.1.1(hono@4.13.8)
+ '@hono/node-server': 2.1.3(hono@4.13.12)
ajv: 8.20.0
ajv-formats: 3.0.1(ajv@8.20.0)
content-type: 1.0.5
eventsource-parser: 3.1.1
express: 5.2.1(supports-color@7.2.0)
express-rate-limit: 8.7.0(express@5.2.1(supports-color@7.2.0))(supports-color@7.2.0)
- hono: 4.13.8
+ hono: 4.13.12
jose: 6.2.12
json-schema-typed: 8.0.2
pkce-challenge: 5.0.1
transitivePeerDependencies:
- supports-color
- '@mongodb-js/saslprep@1.5.4':
+ '@mongodb-js/saslprep@1.5.5':
dependencies:
sparse-bitfield: 3.0.3
'@opentelemetry/api@1.9.1': {}
- '@oxc-project/types@0.151.0': {}
+ '@oxc-project/types@0.152.0': {}
'@parcel/watcher-android-arm64@2.6.0':
optional: true
'@polka/url@1.0.0-next.29': {}
- '@rolldown/binding-android-arm-eabi@1.2.10':
+ '@rolldown/binding-android-arm-eabi@1.2.12':
optional: true
- '@rolldown/binding-android-arm64@1.2.10':
+ '@rolldown/binding-android-arm64@1.2.12':
optional: true
- '@rolldown/binding-darwin-arm64@1.2.10':
+ '@rolldown/binding-darwin-arm64@1.2.12':
optional: true
- '@rolldown/binding-darwin-x64@1.2.10':
+ '@rolldown/binding-darwin-x64@1.2.12':
optional: true
- '@rolldown/binding-freebsd-x64@1.2.10':
+ '@rolldown/binding-freebsd-x64@1.2.12':
optional: true
- '@rolldown/binding-linux-arm-gnueabihf@1.2.10':
+ '@rolldown/binding-linux-arm-gnueabihf@1.2.12':
optional: true
- '@rolldown/binding-linux-arm64-gnu@1.2.10':
+ '@rolldown/binding-linux-arm64-gnu@1.2.12':
optional: true
- '@rolldown/binding-linux-arm64-musl@1.2.10':
+ '@rolldown/binding-linux-arm64-musl@1.2.12':
optional: true
- '@rolldown/binding-linux-ppc64-gnu@1.2.10':
+ '@rolldown/binding-linux-ppc64-gnu@1.2.12':
optional: true
- '@rolldown/binding-linux-s390x-gnu@1.2.10':
+ '@rolldown/binding-linux-s390x-gnu@1.2.12':
optional: true
- '@rolldown/binding-linux-x64-gnu@1.2.10':
+ '@rolldown/binding-linux-x64-gnu@1.2.12':
optional: true
- '@rolldown/binding-linux-x64-musl@1.2.10':
+ '@rolldown/binding-linux-x64-musl@1.2.12':
optional: true
- '@rolldown/binding-openharmony-arm64@1.2.10':
+ '@rolldown/binding-openharmony-arm64@1.2.12':
optional: true
- '@rolldown/binding-win32-arm64-msvc@1.2.10':
+ '@rolldown/binding-win32-arm64-msvc@1.2.12':
optional: true
- '@rolldown/binding-win32-x64-msvc@1.2.10':
+ '@rolldown/binding-win32-x64-msvc@1.2.12':
optional: true
'@rolldown/pluginutils@1.0.1': {}
'@standard-schema/spec@1.1.0': {}
- '@stylistic/eslint-plugin@5.10.0(eslint@10.11.0(jiti@2.6.1)(supports-color@7.2.0))':
+ '@stylistic/eslint-plugin@5.10.0(eslint@10.12.0(jiti@2.6.1)(supports-color@7.2.0))':
dependencies:
- '@eslint-community/eslint-utils': 4.10.1(eslint@10.11.0(jiti@2.6.1)(supports-color@7.2.0))
- '@typescript-eslint/types': 8.70.1
- eslint: 10.11.0(jiti@2.6.1)(supports-color@7.2.0)
+ '@eslint-community/eslint-utils': 4.10.1(eslint@10.12.0(jiti@2.6.1)(supports-color@7.2.0))
+ '@typescript-eslint/types': 8.71.0
+ eslint: 10.12.0(jiti@2.6.1)(supports-color@7.2.0)
eslint-visitor-keys: 4.2.1
espree: 10.4.0
estraverse: 5.3.0
'@types/jsdom@30.0.0':
dependencies:
- '@types/node': 24.13.6
+ '@types/node': 24.19.1
'@types/tough-cookie': 4.0.5
parse5: 8.0.1
- undici-types: 8.11.0
+ undici-types: 8.11.2
'@types/json-schema@7.0.15': {}
'@types/minimatch@3.0.5': {}
- '@types/node@24.13.6':
- dependencies:
- undici-types: 7.18.2
-
- '@types/node@24.19.0':
+ '@types/node@24.19.1':
dependencies:
undici-types: 7.24.6
- '@types/node@26.6.2':
+ '@types/node@26.6.4':
dependencies:
undici-types: 8.9.0
'@types/ws@8.18.2':
dependencies:
- '@types/node': 24.19.0
+ '@types/node': 24.19.1
- '@typescript-eslint/eslint-plugin@8.70.1(@typescript-eslint/parser@8.70.1(eslint@10.11.0(jiti@2.6.1)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3))(eslint@10.11.0(jiti@2.6.1)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3)':
+ '@typescript-eslint/eslint-plugin@8.71.0(@typescript-eslint/parser@8.71.0(eslint@10.12.0(jiti@2.6.1)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3))(eslint@10.12.0(jiti@2.6.1)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3)':
dependencies:
'@eslint-community/regexpp': 4.12.2
- '@typescript-eslint/parser': 8.70.1(eslint@10.11.0(jiti@2.6.1)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3)
- '@typescript-eslint/scope-manager': 8.70.1
- '@typescript-eslint/type-utils': 8.70.1(eslint@10.11.0(jiti@2.6.1)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3)
- '@typescript-eslint/utils': 8.70.1(eslint@10.11.0(jiti@2.6.1)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3)
- '@typescript-eslint/visitor-keys': 8.70.1
- eslint: 10.11.0(jiti@2.6.1)(supports-color@7.2.0)
- ignore: 7.0.10
+ '@typescript-eslint/parser': 8.71.0(eslint@10.12.0(jiti@2.6.1)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3)
+ '@typescript-eslint/scope-manager': 8.71.0
+ '@typescript-eslint/type-utils': 8.71.0(eslint@10.12.0(jiti@2.6.1)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3)
+ '@typescript-eslint/utils': 8.71.0(eslint@10.12.0(jiti@2.6.1)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3)
+ '@typescript-eslint/visitor-keys': 8.71.0
+ eslint: 10.12.0(jiti@2.6.1)(supports-color@7.2.0)
+ ignore: 7.0.12
natural-compare: 1.4.0
ts-api-utils: 2.5.0(typescript@6.0.3)
typescript: 6.0.3
transitivePeerDependencies:
- supports-color
- '@typescript-eslint/parser@8.70.1(eslint@10.11.0(jiti@2.6.1)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3)':
+ '@typescript-eslint/parser@8.71.0(eslint@10.12.0(jiti@2.6.1)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3)':
dependencies:
- '@typescript-eslint/scope-manager': 8.70.1
- '@typescript-eslint/types': 8.70.1
- '@typescript-eslint/typescript-estree': 8.70.1(supports-color@7.2.0)(typescript@6.0.3)
- '@typescript-eslint/visitor-keys': 8.70.1
+ '@typescript-eslint/scope-manager': 8.71.0
+ '@typescript-eslint/types': 8.71.0
+ '@typescript-eslint/typescript-estree': 8.71.0(supports-color@7.2.0)(typescript@6.0.3)
+ '@typescript-eslint/visitor-keys': 8.71.0
debug: 4.4.3(supports-color@7.2.0)
- eslint: 10.11.0(jiti@2.6.1)(supports-color@7.2.0)
+ eslint: 10.12.0(jiti@2.6.1)(supports-color@7.2.0)
typescript: 6.0.3
transitivePeerDependencies:
- supports-color
- '@typescript-eslint/project-service@8.70.1(supports-color@7.2.0)(typescript@5.9.3)':
+ '@typescript-eslint/project-service@8.71.0(supports-color@7.2.0)(typescript@5.9.3)':
dependencies:
- '@typescript-eslint/tsconfig-utils': 8.70.1(typescript@5.9.3)
- '@typescript-eslint/types': 8.70.1
+ '@typescript-eslint/tsconfig-utils': 8.71.0(typescript@5.9.3)
+ '@typescript-eslint/types': 8.71.0
debug: 4.4.3(supports-color@7.2.0)
typescript: 5.9.3
transitivePeerDependencies:
- supports-color
- '@typescript-eslint/project-service@8.70.1(supports-color@7.2.0)(typescript@6.0.3)':
+ '@typescript-eslint/project-service@8.71.0(supports-color@7.2.0)(typescript@6.0.3)':
dependencies:
- '@typescript-eslint/tsconfig-utils': 8.70.1(typescript@6.0.3)
- '@typescript-eslint/types': 8.70.1
+ '@typescript-eslint/tsconfig-utils': 8.71.0(typescript@6.0.3)
+ '@typescript-eslint/types': 8.71.0
debug: 4.4.3(supports-color@7.2.0)
typescript: 6.0.3
transitivePeerDependencies:
- supports-color
- '@typescript-eslint/scope-manager@8.70.1':
+ '@typescript-eslint/scope-manager@8.71.0':
dependencies:
- '@typescript-eslint/types': 8.70.1
- '@typescript-eslint/visitor-keys': 8.70.1
+ '@typescript-eslint/types': 8.71.0
+ '@typescript-eslint/visitor-keys': 8.71.0
- '@typescript-eslint/tsconfig-utils@8.70.1(typescript@5.9.3)':
+ '@typescript-eslint/tsconfig-utils@8.71.0(typescript@5.9.3)':
dependencies:
typescript: 5.9.3
- '@typescript-eslint/tsconfig-utils@8.70.1(typescript@6.0.3)':
+ '@typescript-eslint/tsconfig-utils@8.71.0(typescript@6.0.3)':
dependencies:
typescript: 6.0.3
- '@typescript-eslint/type-utils@8.70.1(eslint@10.11.0(jiti@2.6.1)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3)':
+ '@typescript-eslint/type-utils@8.71.0(eslint@10.12.0(jiti@2.6.1)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3)':
dependencies:
- '@typescript-eslint/types': 8.70.1
- '@typescript-eslint/typescript-estree': 8.70.1(supports-color@7.2.0)(typescript@6.0.3)
- '@typescript-eslint/utils': 8.70.1(eslint@10.11.0(jiti@2.6.1)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3)
+ '@typescript-eslint/types': 8.71.0
+ '@typescript-eslint/typescript-estree': 8.71.0(supports-color@7.2.0)(typescript@6.0.3)
+ '@typescript-eslint/utils': 8.71.0(eslint@10.12.0(jiti@2.6.1)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3)
debug: 4.4.3(supports-color@7.2.0)
- eslint: 10.11.0(jiti@2.6.1)(supports-color@7.2.0)
+ eslint: 10.12.0(jiti@2.6.1)(supports-color@7.2.0)
ts-api-utils: 2.5.0(typescript@6.0.3)
typescript: 6.0.3
transitivePeerDependencies:
- supports-color
- '@typescript-eslint/types@8.70.1': {}
+ '@typescript-eslint/types@8.71.0': {}
- '@typescript-eslint/typescript-estree@8.70.1(supports-color@7.2.0)(typescript@5.9.3)':
+ '@typescript-eslint/typescript-estree@8.71.0(supports-color@7.2.0)(typescript@5.9.3)':
dependencies:
- '@typescript-eslint/project-service': 8.70.1(supports-color@7.2.0)(typescript@5.9.3)
- '@typescript-eslint/tsconfig-utils': 8.70.1(typescript@5.9.3)
- '@typescript-eslint/types': 8.70.1
- '@typescript-eslint/visitor-keys': 8.70.1
+ '@typescript-eslint/project-service': 8.71.0(supports-color@7.2.0)(typescript@5.9.3)
+ '@typescript-eslint/tsconfig-utils': 8.71.0(typescript@5.9.3)
+ '@typescript-eslint/types': 8.71.0
+ '@typescript-eslint/visitor-keys': 8.71.0
debug: 4.4.3(supports-color@7.2.0)
minimatch: 10.2.6
semver: 7.8.5
transitivePeerDependencies:
- supports-color
- '@typescript-eslint/typescript-estree@8.70.1(supports-color@7.2.0)(typescript@6.0.3)':
+ '@typescript-eslint/typescript-estree@8.71.0(supports-color@7.2.0)(typescript@6.0.3)':
dependencies:
- '@typescript-eslint/project-service': 8.70.1(supports-color@7.2.0)(typescript@6.0.3)
- '@typescript-eslint/tsconfig-utils': 8.70.1(typescript@6.0.3)
- '@typescript-eslint/types': 8.70.1
- '@typescript-eslint/visitor-keys': 8.70.1
+ '@typescript-eslint/project-service': 8.71.0(supports-color@7.2.0)(typescript@6.0.3)
+ '@typescript-eslint/tsconfig-utils': 8.71.0(typescript@6.0.3)
+ '@typescript-eslint/types': 8.71.0
+ '@typescript-eslint/visitor-keys': 8.71.0
debug: 4.4.3(supports-color@7.2.0)
minimatch: 10.2.6
semver: 7.8.5
transitivePeerDependencies:
- supports-color
- '@typescript-eslint/utils@8.70.1(eslint@10.11.0(jiti@2.6.1)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3)':
+ '@typescript-eslint/utils@8.71.0(eslint@10.12.0(jiti@2.6.1)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3)':
dependencies:
- '@eslint-community/eslint-utils': 4.10.1(eslint@10.11.0(jiti@2.6.1)(supports-color@7.2.0))
- '@typescript-eslint/scope-manager': 8.70.1
- '@typescript-eslint/types': 8.70.1
- '@typescript-eslint/typescript-estree': 8.70.1(supports-color@7.2.0)(typescript@6.0.3)
- eslint: 10.11.0(jiti@2.6.1)(supports-color@7.2.0)
+ '@eslint-community/eslint-utils': 4.10.1(eslint@10.12.0(jiti@2.6.1)(supports-color@7.2.0))
+ '@typescript-eslint/scope-manager': 8.71.0
+ '@typescript-eslint/types': 8.71.0
+ '@typescript-eslint/typescript-estree': 8.71.0(supports-color@7.2.0)(typescript@6.0.3)
+ eslint: 10.12.0(jiti@2.6.1)(supports-color@7.2.0)
typescript: 6.0.3
transitivePeerDependencies:
- supports-color
- '@typescript-eslint/visitor-keys@8.70.1':
+ '@typescript-eslint/visitor-keys@8.71.0':
dependencies:
- '@typescript-eslint/types': 8.70.1
+ '@typescript-eslint/types': 8.71.0
eslint-visitor-keys: 5.0.1
- '@vitejs/plugin-vue-jsx@5.1.6(supports-color@7.2.0)(vite@8.3.1(@types/node@24.19.0)(esbuild@0.28.2)(jiti@2.6.1)(tsx@4.23.15)(yaml@2.9.1))(vue@3.5.43(typescript@6.0.3))':
+ '@vitejs/plugin-vue-jsx@5.1.6(supports-color@7.2.0)(vite@8.3.2(@types/node@24.19.1)(esbuild@0.28.2)(jiti@2.6.1)(tsx@4.23.15)(yaml@2.9.1))(vue@3.5.43(typescript@6.0.3))':
dependencies:
'@babel/core': 7.29.7(supports-color@7.2.0)
'@babel/plugin-syntax-typescript': 7.29.7(@babel/core@7.29.7(supports-color@7.2.0))
'@babel/plugin-transform-typescript': 7.29.9(@babel/core@7.29.7(supports-color@7.2.0))(supports-color@7.2.0)
'@rolldown/pluginutils': 1.0.1
'@vue/babel-plugin-jsx': 2.0.1(@babel/core@7.29.7(supports-color@7.2.0))(supports-color@7.2.0)
- vite: 8.3.1(@types/node@24.19.0)(esbuild@0.28.2)(jiti@2.6.1)(tsx@4.23.15)(yaml@2.9.1)
+ vite: 8.3.2(@types/node@24.19.1)(esbuild@0.28.2)(jiti@2.6.1)(tsx@4.23.15)(yaml@2.9.1)
vue: 3.5.43(typescript@6.0.3)
transitivePeerDependencies:
- supports-color
- '@vitejs/plugin-vue@6.0.9(vite@8.3.1(@types/node@24.19.0)(esbuild@0.28.2)(jiti@2.6.1)(tsx@4.23.15)(yaml@2.9.1))(vue@3.5.43(typescript@6.0.3))':
+ '@vitejs/plugin-vue@6.0.9(vite@8.3.2(@types/node@24.19.1)(esbuild@0.28.2)(jiti@2.6.1)(tsx@4.23.15)(yaml@2.9.1))(vue@3.5.43(typescript@6.0.3))':
dependencies:
'@rolldown/pluginutils': 1.0.1
- vite: 8.3.1(@types/node@24.19.0)(esbuild@0.28.2)(jiti@2.6.1)(tsx@4.23.15)(yaml@2.9.1)
+ vite: 8.3.2(@types/node@24.19.1)(esbuild@0.28.2)(jiti@2.6.1)(tsx@4.23.15)(yaml@2.9.1)
vue: 3.5.43(typescript@6.0.3)
'@vitest/coverage-v8@5.0.3(vitest@5.0.3)':
dependencies:
'@bcoe/v8-coverage': 1.0.2
- '@vitest/istanbul-lib-coverage': 1.0.1
- '@vitest/istanbul-lib-report': 1.0.1
+ '@vitest/istanbul-lib-coverage': 1.0.2
+ '@vitest/istanbul-lib-report': 1.0.2
ast-v8-to-istanbul: 1.0.7
magicast: 0.5.5
obug: 2.2.1
- std-env: 4.2.0
- tinyrainbow: 3.1.1
- vitest: 5.0.3(@opentelemetry/api@1.9.1)(@types/node@24.19.0)(@vitest/coverage-v8@5.0.3)(jsdom@30.1.1)(vite@8.3.1(@types/node@24.19.0)(esbuild@0.28.2)(jiti@2.6.1)(tsx@4.23.15)(yaml@2.9.1))
+ std-env: 4.3.0
+ tinyrainbow: 3.2.0
+ vitest: 5.0.3(@opentelemetry/api@1.9.1)(@types/node@24.19.1)(@vitest/coverage-v8@5.0.3)(jsdom@30.1.1)(vite@8.3.2(@types/node@24.19.1)(esbuild@0.28.2)(jiti@2.6.1)(tsx@4.23.15)(yaml@2.9.1))
- '@vitest/istanbul-lib-coverage@1.0.1': {}
+ '@vitest/istanbul-lib-coverage@1.0.2': {}
- '@vitest/istanbul-lib-report@1.0.1':
+ '@vitest/istanbul-lib-report@1.0.2':
dependencies:
- '@vitest/istanbul-lib-coverage': 1.0.1
+ '@vitest/istanbul-lib-coverage': 1.0.2
- '@vitest/mocker@5.0.3(vite@8.3.1(@types/node@24.19.0)(esbuild@0.28.2)(jiti@2.6.1)(tsx@4.23.15)(yaml@2.9.1))':
+ '@vitest/mocker@5.0.3(vite@8.3.2(@types/node@24.19.1)(esbuild@0.28.2)(jiti@2.6.1)(tsx@4.23.15)(yaml@2.9.1))':
dependencies:
'@jridgewell/trace-mapping': 0.3.31
'@vitest/spy': 5.0.3
estree-walker: 3.0.3
magic-string: 1.4.2
optionalDependencies:
- vite: 8.3.1(@types/node@24.19.0)(esbuild@0.28.2)(jiti@2.6.1)(tsx@4.23.15)(yaml@2.9.1)
+ vite: 8.3.2(@types/node@24.19.1)(esbuild@0.28.2)(jiti@2.6.1)(tsx@4.23.15)(yaml@2.9.1)
'@vitest/spy@5.0.3': {}
'@vue/shared': 3.5.43
entities: 7.0.1
estree-walker: 2.0.2
- source-map-js: 1.2.1
+ source-map-js: 1.2.2
'@vue/compiler-dom@3.5.43':
dependencies:
estree-walker: 2.0.2
magic-string: 0.30.21
postcss: 8.5.28
- source-map-js: 1.2.1
+ source-map-js: 1.2.2
'@vue/compiler-ssr@3.5.43':
dependencies:
'@vue/devtools-shared@8.2.1': {}
- '@vue/language-core@3.3.11':
+ '@vue/language-core@3.3.12':
dependencies:
'@volar/language-core': 2.4.28
'@vue/compiler-dom': 3.5.43
'@vue/compiler-dom': 3.5.43
js-beautify: 2.0.3
vue: 3.5.43(typescript@6.0.3)
- vue-component-type-helpers: 3.3.11
+ vue-component-type-helpers: 3.3.12
optionalDependencies:
'@vue/server-renderer': 3.5.43
ansi-regex@5.0.1: {}
- ansi-regex@6.3.0: {}
+ ansi-regex@6.4.0: {}
ansi-styles@4.3.0:
dependencies:
base64-js@1.5.1: {}
- baseline-browser-mapping@2.11.25: {}
+ baseline-browser-mapping@2.11.27: {}
basic-ftp@6.2.1: {}
dependencies:
fill-range: 7.1.1
- browserslist@4.29.0:
+ browserslist@4.29.3:
dependencies:
- baseline-browser-mapping: 2.11.25
- caniuse-lite: 1.0.30001810
- electron-to-chromium: 1.5.438
+ baseline-browser-mapping: 2.11.27
+ caniuse-lite: 1.0.30001814
+ electron-to-chromium: 1.5.444
node-releases: 2.0.57
- update-browserslist-db: 1.3.3(browserslist@4.29.0)
+ update-browserslist-db: 1.3.3(browserslist@4.29.3)
bson@7.3.3: {}
camelcase@6.3.0: {}
- caniuse-lite@1.0.30001810: {}
+ caniuse-lite@1.0.30001814: {}
- chai@6.2.2: {}
+ chai@6.3.0: {}
chalk@4.1.2:
dependencies:
object-assign: 4.1.1
vary: 1.1.2
- cosmiconfig-typescript-loader@6.3.0(@types/node@24.19.0)(cosmiconfig@9.0.2(typescript@6.0.3))(typescript@6.0.3):
+ cosmiconfig-typescript-loader@6.3.0(@types/node@24.19.1)(cosmiconfig@9.0.2(typescript@6.0.3))(typescript@6.0.3):
dependencies:
- '@types/node': 24.19.0
+ '@types/node': 24.19.1
cosmiconfig: 9.0.2(typescript@6.0.3)
jiti: 2.6.1
typescript: 6.0.3
'@cspell/cspell-pipe': 10.3.6
'@cspell/cspell-types': 10.3.6
cspell-trie-lib: 10.3.6(@cspell/cspell-types@10.3.6)
- fast-equals: 6.0.4
+ fast-equals: 6.1.0
cspell-glob@10.3.6:
dependencies:
cspell-trie-lib: 10.3.6(@cspell/cspell-types@10.3.6)
env-paths: 4.0.0
gensequence: 8.0.8
- import-fresh: 4.0.0
+ import-fresh: 4.0.1
resolve-from: 5.0.0
- vscode-languageserver-textdocument: 1.0.14
+ vscode-languageserver-textdocument: 1.0.15
vscode-uri: 3.2.0
xdg-basedir: 5.1.0
css-tree@3.2.1:
dependencies:
mdn-data: 2.27.1
- source-map-js: 1.2.1
+ source-map-js: 1.2.2
cssesc@3.0.0: {}
'@standard-schema/spec': 1.1.0
fast-check: 3.23.2
- electron-to-chromium@1.5.438: {}
+ electron-to-chromium@1.5.444: {}
emoji-regex@10.6.0: {}
dependencies:
once: 1.4.0
- enhanced-resolve@5.25.1:
+ enhanced-resolve@5.26.0:
dependencies:
graceful-fs: 4.2.11
tapable: 2.3.3
escape-string-regexp@5.0.0: {}
- eslint-compat-utils@0.5.1(eslint@10.11.0(jiti@2.6.1)(supports-color@7.2.0)):
+ eslint-compat-utils@0.5.1(eslint@10.12.0(jiti@2.6.1)(supports-color@7.2.0)):
dependencies:
- eslint: 10.11.0(jiti@2.6.1)(supports-color@7.2.0)
+ eslint: 10.12.0(jiti@2.6.1)(supports-color@7.2.0)
semver: 7.8.5
- eslint-plugin-es-x@7.8.0(eslint@10.11.0(jiti@2.6.1)(supports-color@7.2.0)):
+ eslint-plugin-es-x@7.8.0(eslint@10.12.0(jiti@2.6.1)(supports-color@7.2.0)):
dependencies:
- '@eslint-community/eslint-utils': 4.10.1(eslint@10.11.0(jiti@2.6.1)(supports-color@7.2.0))
+ '@eslint-community/eslint-utils': 4.10.1(eslint@10.12.0(jiti@2.6.1)(supports-color@7.2.0))
'@eslint-community/regexpp': 4.12.2
- eslint: 10.11.0(jiti@2.6.1)(supports-color@7.2.0)
- eslint-compat-utils: 0.5.1(eslint@10.11.0(jiti@2.6.1)(supports-color@7.2.0))
+ eslint: 10.12.0(jiti@2.6.1)(supports-color@7.2.0)
+ eslint-compat-utils: 0.5.1(eslint@10.12.0(jiti@2.6.1)(supports-color@7.2.0))
- eslint-plugin-jsdoc@65.0.1(eslint@10.11.0(jiti@2.6.1)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3):
+ eslint-plugin-jsdoc@65.0.2(eslint@10.12.0(jiti@2.6.1)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3):
dependencies:
'@es-joy/jsdoccomment': 0.98.0
'@es-joy/resolve.exports': 1.2.0
- '@typescript-eslint/utils': 8.70.1(eslint@10.11.0(jiti@2.6.1)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3)
+ '@typescript-eslint/utils': 8.71.0(eslint@10.12.0(jiti@2.6.1)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3)
are-docs-informative: 0.1.1
comment-parser: 1.4.9
debug: 4.4.3(supports-color@7.2.0)
escape-string-regexp: 5.0.0
- eslint: 10.11.0(jiti@2.6.1)(supports-color@7.2.0)
+ eslint: 10.12.0(jiti@2.6.1)(supports-color@7.2.0)
espree: 11.2.0
esquery: 1.7.0
html-entities: 2.6.0
transitivePeerDependencies:
- supports-color
- eslint-plugin-n@18.3.0(eslint@10.11.0(jiti@2.6.1)(supports-color@7.2.0))(typescript@6.0.3):
+ eslint-plugin-n@18.4.1(eslint@10.12.0(jiti@2.6.1)(supports-color@7.2.0))(typescript@6.0.3):
dependencies:
- '@eslint-community/eslint-utils': 4.10.1(eslint@10.11.0(jiti@2.6.1)(supports-color@7.2.0))
- enhanced-resolve: 5.25.1
- eslint: 10.11.0(jiti@2.6.1)(supports-color@7.2.0)
- eslint-plugin-es-x: 7.8.0(eslint@10.11.0(jiti@2.6.1)(supports-color@7.2.0))
+ '@eslint-community/eslint-utils': 4.10.1(eslint@10.12.0(jiti@2.6.1)(supports-color@7.2.0))
+ enhanced-resolve: 5.26.0
+ eslint: 10.12.0(jiti@2.6.1)(supports-color@7.2.0)
+ eslint-plugin-es-x: 7.8.0(eslint@10.12.0(jiti@2.6.1)(supports-color@7.2.0))
get-tsconfig: 4.14.3
globals: 15.15.0
globrex: 0.1.2
ignore: 5.3.2
+ js-yaml: 5.4.2
semver: 7.8.5
optionalDependencies:
typescript: 6.0.3
- eslint-plugin-perfectionist@5.12.1(eslint@10.11.0(jiti@2.6.1)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3):
+ eslint-plugin-perfectionist@5.12.1(eslint@10.12.0(jiti@2.6.1)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3):
dependencies:
- '@typescript-eslint/types': 8.70.1
- '@typescript-eslint/utils': 8.70.1(eslint@10.11.0(jiti@2.6.1)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3)
+ '@typescript-eslint/types': 8.71.0
+ '@typescript-eslint/utils': 8.71.0(eslint@10.12.0(jiti@2.6.1)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3)
natural-orderby: 5.0.0
optionalDependencies:
- eslint: 10.11.0(jiti@2.6.1)(supports-color@7.2.0)
+ eslint: 10.12.0(jiti@2.6.1)(supports-color@7.2.0)
transitivePeerDependencies:
- supports-color
- typescript
- eslint-plugin-promise@7.3.0(eslint@10.11.0(jiti@2.6.1)(supports-color@7.2.0)):
+ eslint-plugin-promise@7.3.0(eslint@10.12.0(jiti@2.6.1)(supports-color@7.2.0)):
dependencies:
- '@eslint-community/eslint-utils': 4.10.1(eslint@10.11.0(jiti@2.6.1)(supports-color@7.2.0))
- eslint: 10.11.0(jiti@2.6.1)(supports-color@7.2.0)
+ '@eslint-community/eslint-utils': 4.10.1(eslint@10.12.0(jiti@2.6.1)(supports-color@7.2.0))
+ eslint: 10.12.0(jiti@2.6.1)(supports-color@7.2.0)
- eslint-plugin-vue@10.11.1(@stylistic/eslint-plugin@5.10.0(eslint@10.11.0(jiti@2.6.1)(supports-color@7.2.0)))(@typescript-eslint/parser@8.70.1(eslint@10.11.0(jiti@2.6.1)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3))(eslint@10.11.0(jiti@2.6.1)(supports-color@7.2.0))(vue-eslint-parser@10.4.1(eslint@10.11.0(jiti@2.6.1)(supports-color@7.2.0))(supports-color@7.2.0)):
+ eslint-plugin-vue@10.11.1(@stylistic/eslint-plugin@5.10.0(eslint@10.12.0(jiti@2.6.1)(supports-color@7.2.0)))(@typescript-eslint/parser@8.71.0(eslint@10.12.0(jiti@2.6.1)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3))(eslint@10.12.0(jiti@2.6.1)(supports-color@7.2.0))(vue-eslint-parser@10.4.1(eslint@10.12.0(jiti@2.6.1)(supports-color@7.2.0))(supports-color@7.2.0)):
dependencies:
- '@eslint-community/eslint-utils': 4.10.1(eslint@10.11.0(jiti@2.6.1)(supports-color@7.2.0))
- eslint: 10.11.0(jiti@2.6.1)(supports-color@7.2.0)
+ '@eslint-community/eslint-utils': 4.10.1(eslint@10.12.0(jiti@2.6.1)(supports-color@7.2.0))
+ eslint: 10.12.0(jiti@2.6.1)(supports-color@7.2.0)
natural-compare: 1.4.0
nth-check: 2.1.1
postcss-selector-parser: 7.1.6
semver: 7.8.5
- vue-eslint-parser: 10.4.1(eslint@10.11.0(jiti@2.6.1)(supports-color@7.2.0))(supports-color@7.2.0)
+ vue-eslint-parser: 10.4.1(eslint@10.12.0(jiti@2.6.1)(supports-color@7.2.0))(supports-color@7.2.0)
xml-name-validator: 5.0.0
optionalDependencies:
- '@stylistic/eslint-plugin': 5.10.0(eslint@10.11.0(jiti@2.6.1)(supports-color@7.2.0))
- '@typescript-eslint/parser': 8.70.1(eslint@10.11.0(jiti@2.6.1)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3)
+ '@stylistic/eslint-plugin': 5.10.0(eslint@10.12.0(jiti@2.6.1)(supports-color@7.2.0))
+ '@typescript-eslint/parser': 8.71.0(eslint@10.12.0(jiti@2.6.1)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3)
eslint-scope@9.1.2:
dependencies:
eslint-visitor-keys@5.0.1: {}
- eslint@10.11.0(jiti@2.6.1)(supports-color@7.2.0):
+ eslint@10.12.0(jiti@2.6.1)(supports-color@7.2.0):
dependencies:
- '@eslint-community/eslint-utils': 4.10.1(eslint@10.11.0(jiti@2.6.1)(supports-color@7.2.0))
+ '@eslint-community/eslint-utils': 4.10.1(eslint@10.12.0(jiti@2.6.1)(supports-color@7.2.0))
'@eslint-community/regexpp': 4.12.2
'@eslint/config-array': 0.23.5(supports-color@7.2.0)
'@eslint/config-helpers': 0.7.0
dependencies:
debug: 4.4.3(supports-color@7.2.0)
express: 5.2.1(supports-color@7.2.0)
- ip-address: 10.7.2
+ ip-address: 10.7.3
transitivePeerDependencies:
- supports-color
transitivePeerDependencies:
- supports-color
- exsolve@1.1.1: {}
+ exsolve@1.1.3: {}
fast-check@3.23.2:
dependencies:
fast-deep-equal@3.1.3: {}
- fast-equals@6.0.4: {}
+ fast-equals@6.1.0: {}
fast-glob@3.3.3:
dependencies:
fecha@4.2.3: {}
- figlet@1.11.4:
+ figlet@1.12.0:
dependencies:
commander: 14.0.3
globals@15.15.0: {}
- globals@17.12.0: {}
+ globals@17.13.0: {}
globby@11.1.0:
dependencies:
dependencies:
parse-passwd: 1.0.0
- hono@4.13.8: {}
+ hono@4.13.12: {}
hookable@5.5.3: {}
ignore@5.3.2: {}
- ignore@7.0.10: {}
+ ignore@7.0.12: {}
import-fresh@3.3.1:
dependencies:
parent-module: 1.0.1
resolve-from: 4.0.0
- import-fresh@4.0.0: {}
+ import-fresh@4.0.1: {}
import-meta-resolve@4.2.0: {}
dependencies:
fp-ts: 2.16.11
- ip-address@10.7.2: {}
+ ip-address@10.7.3: {}
ipaddr.js@1.9.1: {}
dependencies:
argparse: 2.0.1
+ js-yaml@5.4.2:
+ dependencies:
+ argparse: 2.0.1
+
jsdoc-type-pratt-parser@9.2.2:
dependencies:
'@types/estree': 1.0.9
- '@types/node': 26.6.2
+ '@types/node': 26.6.4
jsdom@30.1.1:
dependencies:
- '@asamuzakjp/css-color': 7.0.1
- '@asamuzakjp/dom-selector': 9.2.1
+ '@asamuzakjp/css-color': 7.1.3
+ '@asamuzakjp/dom-selector': 9.2.4
'@bramus/specificity': 2.4.2
- '@csstools/css-syntax-patches-for-csstree': 1.1.14(css-tree@3.2.1)
+ '@csstools/css-syntax-patches-for-csstree': 1.1.15(css-tree@3.2.1)
'@exodus/bytes': 1.16.0
css-tree: 3.2.1
data-urls: 7.0.0
parse5: 8.0.1
saxes: 6.0.0
tough-cookie: 6.0.2
- undici: 8.11.0
+ undici: 8.11.2
w3c-xmlserializer: 6.0.0
webidl-conversions: 8.0.1
whatwg-mimetype: 5.0.0
dependencies:
'@babel/parser': 7.29.9
'@babel/types': 7.29.8
- source-map-js: 1.2.1
+ source-map-js: 1.2.2
make-error@1.3.6: {}
mariadb@3.4.5:
dependencies:
'@types/geojson': 7946.0.16
- '@types/node': 24.13.6
+ '@types/node': 24.19.1
denque: 2.1.0
iconv-lite: 0.6.3
lru-cache: 10.4.3
braces: 3.0.3
picomatch: 2.3.2
- mikro-orm@6.6.16: {}
+ mikro-orm@6.6.17: {}
mime-db@1.54.0: {}
mongodb@7.7.0:
dependencies:
- '@mongodb-js/saslprep': 1.5.4
+ '@mongodb-js/saslprep': 1.5.5
bson: 7.3.3
mongodb-connection-string-url: 7.0.2
dependencies:
content-type: 2.1.0
- neostandard@0.14.0-next.1(eslint@10.11.0(jiti@2.6.1)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3):
+ neostandard@0.14.0-next.1(eslint@10.12.0(jiti@2.6.1)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3):
dependencies:
'@humanwhocodes/gitignore-to-minimatch': 1.0.2
- '@stylistic/eslint-plugin': 5.10.0(eslint@10.11.0(jiti@2.6.1)(supports-color@7.2.0))
- eslint: 10.11.0(jiti@2.6.1)(supports-color@7.2.0)
- eslint-plugin-n: 18.3.0(eslint@10.11.0(jiti@2.6.1)(supports-color@7.2.0))(typescript@6.0.3)
- eslint-plugin-promise: 7.3.0(eslint@10.11.0(jiti@2.6.1)(supports-color@7.2.0))
+ '@stylistic/eslint-plugin': 5.10.0(eslint@10.12.0(jiti@2.6.1)(supports-color@7.2.0))
+ eslint: 10.12.0(jiti@2.6.1)(supports-color@7.2.0)
+ eslint-plugin-n: 18.4.1(eslint@10.12.0(jiti@2.6.1)(supports-color@7.2.0))(typescript@6.0.3)
+ eslint-plugin-promise: 7.3.0(eslint@10.12.0(jiti@2.6.1)(supports-color@7.2.0))
find-up: 8.0.0
- globals: 17.12.0
+ globals: 17.13.0
peowly: 2.0.1
- typescript-eslint: 8.70.1(eslint@10.11.0(jiti@2.6.1)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3)
+ typescript-eslint: 8.71.0(eslint@10.12.0(jiti@2.6.1)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3)
transitivePeerDependencies:
- supports-color
- ts-declaration-location
is-unicode-supported: 2.1.0
log-symbols: 7.0.1
stdin-discarder: 0.3.2
- string-width: 8.2.2
+ string-width: 8.3.0
p-limit@3.1.0:
dependencies:
pkg-types@2.3.3:
dependencies:
confbox: 0.3.1
- exsolve: 1.1.1
+ exsolve: 1.1.3
pathe: 2.0.3
please-upgrade-node@3.2.0:
dependencies:
nanoid: 3.3.19
picocolors: 1.1.1
- source-map-js: 1.2.1
+ source-map-js: 1.2.2
prebuild-install@7.1.3:
dependencies:
glob: 13.0.6
package-json-from-dist: 1.0.1
- rolldown@1.2.10:
+ rolldown@1.2.12:
dependencies:
- '@oxc-project/types': 0.151.0
+ '@oxc-project/types': 0.152.0
'@rolldown/pluginutils': 1.0.1
optionalDependencies:
- '@rolldown/binding-android-arm-eabi': 1.2.10
- '@rolldown/binding-android-arm64': 1.2.10
- '@rolldown/binding-darwin-arm64': 1.2.10
- '@rolldown/binding-darwin-x64': 1.2.10
- '@rolldown/binding-freebsd-x64': 1.2.10
- '@rolldown/binding-linux-arm-gnueabihf': 1.2.10
- '@rolldown/binding-linux-arm64-gnu': 1.2.10
- '@rolldown/binding-linux-arm64-musl': 1.2.10
- '@rolldown/binding-linux-ppc64-gnu': 1.2.10
- '@rolldown/binding-linux-s390x-gnu': 1.2.10
- '@rolldown/binding-linux-x64-gnu': 1.2.10
- '@rolldown/binding-linux-x64-musl': 1.2.10
- '@rolldown/binding-openharmony-arm64': 1.2.10
- '@rolldown/binding-win32-arm64-msvc': 1.2.10
- '@rolldown/binding-win32-x64-msvc': 1.2.10
+ '@rolldown/binding-android-arm-eabi': 1.2.12
+ '@rolldown/binding-android-arm64': 1.2.12
+ '@rolldown/binding-darwin-arm64': 1.2.12
+ '@rolldown/binding-darwin-x64': 1.2.12
+ '@rolldown/binding-freebsd-x64': 1.2.12
+ '@rolldown/binding-linux-arm-gnueabihf': 1.2.12
+ '@rolldown/binding-linux-arm64-gnu': 1.2.12
+ '@rolldown/binding-linux-arm64-musl': 1.2.12
+ '@rolldown/binding-linux-ppc64-gnu': 1.2.12
+ '@rolldown/binding-linux-s390x-gnu': 1.2.12
+ '@rolldown/binding-linux-x64-gnu': 1.2.12
+ '@rolldown/binding-linux-x64-musl': 1.2.12
+ '@rolldown/binding-openharmony-arm64': 1.2.12
+ '@rolldown/binding-win32-arm64-msvc': 1.2.12
+ '@rolldown/binding-win32-x64-msvc': 1.2.12
router@2.2.0(supports-color@7.2.0):
dependencies:
skott@0.35.12(supports-color@7.2.0):
dependencies:
'@parcel/watcher': 2.6.0
- '@typescript-eslint/typescript-estree': 8.70.1(supports-color@7.2.0)(typescript@5.9.3)
+ '@typescript-eslint/typescript-estree': 8.71.0(supports-color@7.2.0)(typescript@5.9.3)
commander: 11.1.0
compression: 1.8.2(supports-color@7.2.0)
depcheck: 1.4.7(supports-color@7.2.0)
smol-toml@1.9.0: {}
- source-map-js@1.2.1: {}
+ source-map-js@1.2.2: {}
sparse-bitfield@3.0.3:
dependencies:
statuses@2.0.2: {}
- std-env@4.2.0: {}
+ std-env@4.3.0: {}
stdin-discarder@0.3.2: {}
get-east-asian-width: 1.7.0
strip-ansi: 7.2.0
- string-width@8.2.2:
+ string-width@8.3.0:
dependencies:
get-east-asian-width: 1.7.0
strip-ansi: 7.2.0
strip-ansi@7.2.0:
dependencies:
- ansi-regex: 6.3.0
+ ansi-regex: 6.4.0
strip-bom@3.0.0: {}
tildify@2.0.0: {}
- tinybench@6.1.4: {}
+ tinybench@6.2.0: {}
tinyexec@1.3.1: {}
fdir: 6.5.0(picomatch@4.0.7)
picomatch: 4.0.7
- tinyrainbow@3.1.1: {}
+ tinyrainbow@3.2.0: {}
- tldts-core@7.4.15: {}
+ tldts-core@7.4.16: {}
- tldts@7.4.15:
+ tldts@7.4.16:
dependencies:
- tldts-core: 7.4.15
+ tldts-core: 7.4.16
to-regex-range@5.0.1:
dependencies:
tough-cookie@6.0.2:
dependencies:
- tldts: 7.4.15
+ tldts: 7.4.16
tr46@5.1.1:
dependencies:
'@ts-morph/common': 0.28.1
code-block-writer: 13.0.3
- ts-node@10.9.2(@types/node@24.19.0)(typescript@6.0.3):
+ ts-node@10.9.2(@types/node@24.19.1)(typescript@6.0.3):
dependencies:
'@cspotcode/source-map-support': 0.8.1
'@tsconfig/node10': 1.0.13
'@tsconfig/node12': 1.0.11
'@tsconfig/node14': 1.0.3
'@tsconfig/node16': 1.0.4
- '@types/node': 24.19.0
+ '@types/node': 24.19.1
acorn: 8.18.0
acorn-walk: 8.3.5
arg: 4.1.3
media-typer: 1.1.1
mime-types: 3.0.2
- typescript-eslint@8.70.1(eslint@10.11.0(jiti@2.6.1)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3):
+ typescript-eslint@8.71.0(eslint@10.12.0(jiti@2.6.1)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3):
dependencies:
- '@typescript-eslint/eslint-plugin': 8.70.1(@typescript-eslint/parser@8.70.1(eslint@10.11.0(jiti@2.6.1)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3))(eslint@10.11.0(jiti@2.6.1)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3)
- '@typescript-eslint/parser': 8.70.1(eslint@10.11.0(jiti@2.6.1)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3)
- '@typescript-eslint/typescript-estree': 8.70.1(supports-color@7.2.0)(typescript@6.0.3)
- '@typescript-eslint/utils': 8.70.1(eslint@10.11.0(jiti@2.6.1)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3)
- eslint: 10.11.0(jiti@2.6.1)(supports-color@7.2.0)
+ '@typescript-eslint/eslint-plugin': 8.71.0(@typescript-eslint/parser@8.71.0(eslint@10.12.0(jiti@2.6.1)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3))(eslint@10.12.0(jiti@2.6.1)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3)
+ '@typescript-eslint/parser': 8.71.0(eslint@10.12.0(jiti@2.6.1)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3)
+ '@typescript-eslint/typescript-estree': 8.71.0(supports-color@7.2.0)(typescript@6.0.3)
+ '@typescript-eslint/utils': 8.71.0(eslint@10.12.0(jiti@2.6.1)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3)
+ eslint: 10.12.0(jiti@2.6.1)(supports-color@7.2.0)
typescript: 6.0.3
transitivePeerDependencies:
- supports-color
typescript-language-server@6.0.1:
dependencies:
vscode-jsonrpc: 5.0.1
- vscode-languageserver-protocol: 3.18.3
+ vscode-languageserver-protocol: 3.18.4
typescript@5.9.3: {}
ufo@1.6.4: {}
- undici-types@7.18.2: {}
-
undici-types@7.24.6: {}
- undici-types@8.11.0: {}
+ undici-types@8.11.2: {}
undici-types@8.9.0: {}
- undici@8.11.0: {}
+ undici@8.11.2: {}
unicorn-magic@0.3.0: {}
pathe: 2.0.3
picomatch: 4.0.7
- unplugin@3.4.0(esbuild@0.28.2)(rolldown@1.2.10)(vite@8.3.1(@types/node@24.19.0)(esbuild@0.28.2)(jiti@2.6.1)(tsx@4.23.15)(yaml@2.9.1)):
+ unplugin@3.4.0(esbuild@0.28.2)(rolldown@1.2.12)(vite@8.3.2(@types/node@24.19.1)(esbuild@0.28.2)(jiti@2.6.1)(tsx@4.23.15)(yaml@2.9.1)):
dependencies:
'@jridgewell/remapping': 2.3.5
picomatch: 4.0.7
webpack-virtual-modules: 0.6.2
optionalDependencies:
esbuild: 0.28.2
- rolldown: 1.2.10
- vite: 8.3.1(@types/node@24.19.0)(esbuild@0.28.2)(jiti@2.6.1)(tsx@4.23.15)(yaml@2.9.1)
+ rolldown: 1.2.12
+ vite: 8.3.2(@types/node@24.19.1)(esbuild@0.28.2)(jiti@2.6.1)(tsx@4.23.15)(yaml@2.9.1)
- update-browserslist-db@1.3.3(browserslist@4.29.0):
+ update-browserslist-db@1.3.3(browserslist@4.29.3):
dependencies:
- browserslist: 4.29.0
+ browserslist: 4.29.3
escalade: 3.2.0
picocolors: 1.1.1
vary@1.1.2: {}
- vite@8.3.1(@types/node@24.19.0)(esbuild@0.28.2)(jiti@2.6.1)(tsx@4.23.15)(yaml@2.9.1):
+ vite@8.3.2(@types/node@24.19.1)(esbuild@0.28.2)(jiti@2.6.1)(tsx@4.23.15)(yaml@2.9.1):
dependencies:
lightningcss: 1.33.0
picomatch: 4.0.7
postcss: 8.5.28
- rolldown: 1.2.10
+ rolldown: 1.2.12
tinyglobby: 0.2.17
optionalDependencies:
- '@types/node': 24.19.0
+ '@types/node': 24.19.1
esbuild: 0.28.2
fsevents: 2.3.3
jiti: 2.6.1
tsx: 4.23.15
yaml: 2.9.1
- vitest@5.0.3(@opentelemetry/api@1.9.1)(@types/node@24.19.0)(@vitest/coverage-v8@5.0.3)(jsdom@30.1.1)(vite@8.3.1(@types/node@24.19.0)(esbuild@0.28.2)(jiti@2.6.1)(tsx@4.23.15)(yaml@2.9.1)):
+ vitest@5.0.3(@opentelemetry/api@1.9.1)(@types/node@24.19.1)(@vitest/coverage-v8@5.0.3)(jsdom@30.1.1)(vite@8.3.2(@types/node@24.19.1)(esbuild@0.28.2)(jiti@2.6.1)(tsx@4.23.15)(yaml@2.9.1)):
dependencies:
'@types/chai': 5.2.3
- '@vitest/mocker': 5.0.3(vite@8.3.1(@types/node@24.19.0)(esbuild@0.28.2)(jiti@2.6.1)(tsx@4.23.15)(yaml@2.9.1))
- chai: 6.2.2
+ '@vitest/mocker': 5.0.3(vite@8.3.2(@types/node@24.19.1)(esbuild@0.28.2)(jiti@2.6.1)(tsx@4.23.15)(yaml@2.9.1))
+ chai: 6.3.0
es-module-lexer: 2.3.2
expect-type: 1.4.0
magic-string: 1.4.2
obug: 2.2.1
picomatch: 4.0.7
- std-env: 4.2.0
- tinybench: 6.1.4
+ std-env: 4.3.0
+ tinybench: 6.2.0
tinyexec: 1.3.1
tinyglobby: 0.2.17
- vite: 8.3.1(@types/node@24.19.0)(esbuild@0.28.2)(jiti@2.6.1)(tsx@4.23.15)(yaml@2.9.1)
+ vite: 8.3.2(@types/node@24.19.1)(esbuild@0.28.2)(jiti@2.6.1)(tsx@4.23.15)(yaml@2.9.1)
why-is-node-running: 3.2.1
optionalDependencies:
'@opentelemetry/api': 1.9.1
- '@types/node': 24.19.0
+ '@types/node': 24.19.1
'@vitest/coverage-v8': 5.0.3(vitest@5.0.3)
jsdom: 30.1.1
transitivePeerDependencies:
vscode-jsonrpc@5.0.1: {}
- vscode-jsonrpc@9.0.2: {}
+ vscode-jsonrpc@9.0.3: {}
- vscode-languageserver-protocol@3.18.3:
+ vscode-languageserver-protocol@3.18.4:
dependencies:
- vscode-jsonrpc: 9.0.2
- vscode-languageserver-types: 3.18.3
+ vscode-jsonrpc: 9.0.3
+ vscode-languageserver-types: 3.18.4
- vscode-languageserver-textdocument@1.0.14: {}
+ vscode-languageserver-textdocument@1.0.15: {}
- vscode-languageserver-types@3.18.3: {}
+ vscode-languageserver-types@3.18.4: {}
vscode-uri@3.2.0: {}
- vue-component-type-helpers@3.3.11: {}
+ vue-component-type-helpers@3.3.12: {}
- vue-eslint-parser@10.4.1(eslint@10.11.0(jiti@2.6.1)(supports-color@7.2.0))(supports-color@7.2.0):
+ vue-eslint-parser@10.4.1(eslint@10.12.0(jiti@2.6.1)(supports-color@7.2.0))(supports-color@7.2.0):
dependencies:
debug: 4.4.3(supports-color@7.2.0)
- eslint: 10.11.0(jiti@2.6.1)(supports-color@7.2.0)
+ eslint: 10.12.0(jiti@2.6.1)(supports-color@7.2.0)
eslint-scope: 9.1.2
eslint-visitor-keys: 5.0.1
espree: 11.2.0
transitivePeerDependencies:
- supports-color
- vue-router@5.3.1(@vue/compiler-sfc@3.5.43)(esbuild@0.28.2)(rolldown@1.2.10)(vite@8.3.1(@types/node@24.19.0)(esbuild@0.28.2)(jiti@2.6.1)(tsx@4.23.15)(yaml@2.9.1))(vue@3.5.43(typescript@6.0.3)):
+ vue-router@5.3.1(@vue/compiler-sfc@3.5.43)(esbuild@0.28.2)(rolldown@1.2.12)(vite@8.3.2(@types/node@24.19.1)(esbuild@0.28.2)(jiti@2.6.1)(tsx@4.23.15)(yaml@2.9.1))(vue@3.5.43(typescript@6.0.3)):
dependencies:
'@vue-macros/common': 3.1.4(vue@3.5.43(typescript@6.0.3))
'@vue/devtools-api': 8.2.1
picomatch: 4.0.7
scule: 1.3.0
tinyglobby: 0.2.17
- unplugin: 3.4.0(esbuild@0.28.2)(rolldown@1.2.10)(vite@8.3.1(@types/node@24.19.0)(esbuild@0.28.2)(jiti@2.6.1)(tsx@4.23.15)(yaml@2.9.1))
+ unplugin: 3.4.0(esbuild@0.28.2)(rolldown@1.2.12)(vite@8.3.2(@types/node@24.19.1)(esbuild@0.28.2)(jiti@2.6.1)(tsx@4.23.15)(yaml@2.9.1))
unplugin-utils: 0.3.2
vue: 3.5.43(typescript@6.0.3)
optionalDependencies:
'@vue/compiler-sfc': 3.5.43
- vite: 8.3.1(@types/node@24.19.0)(esbuild@0.28.2)(jiti@2.6.1)(tsx@4.23.15)(yaml@2.9.1)
+ vite: 8.3.2(@types/node@24.19.1)(esbuild@0.28.2)(jiti@2.6.1)(tsx@4.23.15)(yaml@2.9.1)
transitivePeerDependencies:
- '@farmfe/core'
- '@rsbuild/core'
dependencies:
vue: 3.5.43(typescript@6.0.3)
- vue-tsc@3.3.11(typescript@6.0.3):
+ vue-tsc@3.3.12(typescript@6.0.3):
dependencies:
'@volar/typescript': 2.4.28(typescript@6.0.3)
- '@vue/language-core': 3.3.11
+ '@vue/language-core': 3.3.12
typescript: 6.0.3
vue@3.5.43(typescript@6.0.3):
cliui: 9.0.1
escalade: 3.2.0
get-caller-file: 2.0.5
- string-width: 8.2.2
+ string-width: 8.3.0
y18n: 5.0.8
yargs-parser: 22.0.0
type HeartbeatRequest,
type HeartbeatResponse,
type IncomingRequest,
- type IncomingRequestCommand,
+ IncomingRequestCommand,
type JsonType,
MapStringifyFormat,
MessageType,
OCPP20ReadingContextEnumType,
OCPP20RequiredVariableName,
type OCPP20TransactionEventRequest,
+ type OCPPCommandName,
OCPPVersion,
type OutgoingRequest,
PowerUnits,
ensureError,
formatDurationMilliSeconds,
formatDurationSeconds,
+ getEnumStringValue,
getErrorMessage,
getMessageTypeString,
getWebSocketCloseEventStatusString,
class TransactionEventQueuePersistenceSupersededError extends BaseError {}
export class ChargingStation extends EventEmitter {
+ private static readonly normalWebSocketCloseEventStatusCodes: ReadonlySet<number> =
+ new Set<number>([
+ WebSocketCloseEventStatusCode.CLOSE_NO_STATUS,
+ WebSocketCloseEventStatusCode.CLOSE_NORMAL,
+ ])
+
public automaticTransactionGenerator?: AutomaticTransactionGenerator
public bootNotificationRequest?: BootNotificationRequest
public bootNotificationResponse?: BootNotificationResponse
* @param command - OCPP command the statistic is recorded against.
* @param messageType - Message type of the recorded exchange.
*/
- public recordRequestStatistic (
- command: IncomingRequestCommand | RequestCommand,
- messageType: MessageType
- ): void {
+ public recordRequestStatistic (command: OCPPCommandName, messageType: MessageType): void {
if (this.stationInfo?.enableStatistics === true) {
this.performanceStatistics?.addRequestStatistic(command, messageType)
}
}
}
- private onClose (
- wsConnection: WebSocket,
- code: WebSocketCloseEventStatusCode,
- reason: Buffer
- ): void {
+ private onClose (wsConnection: WebSocket, code: number, reason: Buffer): void {
const closedByRequest = this.wsConnectionsClosedByRequest.delete(wsConnection)
if (wsConnection !== this.wsConnection) {
return
)
this.emitChargingStationEvent(ChargingStationEvents.disconnected)
this.emitChargingStationEvent(ChargingStationEvents.updated)
- switch (code) {
- // Normal close
- case WebSocketCloseEventStatusCode.CLOSE_NO_STATUS:
- case WebSocketCloseEventStatusCode.CLOSE_NORMAL:
- logger.info(
- `${this.logPrefix()} ${moduleName}.onClose: WebSocket normally closed with status '${getWebSocketCloseEventStatusString(
- code
- )}' and reason '${reason.toString()}'`
- )
- break
- // Abnormal close
- default:
- logger.error(
- `${this.logPrefix()} ${moduleName}.onClose: WebSocket abnormally closed with status '${getWebSocketCloseEventStatusString(
- code
- )}' and reason '${reason.toString()}'`
- )
- break
+ if (ChargingStation.normalWebSocketCloseEventStatusCodes.has(code)) {
+ logger.info(
+ `${this.logPrefix()} ${moduleName}.onClose: WebSocket normally closed with status '${getWebSocketCloseEventStatusString(code)}' and reason '${reason.toString()}'`
+ )
+ } else {
+ logger.error(
+ `${this.logPrefix()} ${moduleName}.onClose: WebSocket abnormally closed with status '${getWebSocketCloseEventStatusString(code)}' and reason '${reason.toString()}'`
+ )
}
// Reconnect on any close we did not request while still started: a
// server-initiated drop, or an internal close wanting a fresh connection
// eslint-disable-next-line @typescript-eslint/no-base-to-string
request = JSON.parse(data.toString()) as ErrorResponse | IncomingRequest | Response
if (Array.isArray(request)) {
- ;[messageType] = request
+ const [parsedMessageType] = request
if (
this.isStopping() &&
- messageType !== MessageType.CALL_RESULT_MESSAGE &&
- messageType !== MessageType.CALL_ERROR_MESSAGE
+ parsedMessageType !== MessageType.CALL_RESULT_MESSAGE &&
+ parsedMessageType !== MessageType.CALL_ERROR_MESSAGE
) {
return
}
- switch (messageType) {
+ switch (parsedMessageType) {
// Error Message
case MessageType.CALL_ERROR_MESSAGE:
- this.handleErrorMessage(request as ErrorResponse)
+ messageType = MessageType.CALL_ERROR_MESSAGE
+ this.handleErrorMessage(request)
break
// Incoming Message
- case MessageType.CALL_MESSAGE:
+ case MessageType.CALL_MESSAGE: {
+ messageType = MessageType.CALL_MESSAGE
if (!messageLifecycleIsCurrent()) return
+ const commandName = getEnumStringValue(IncomingRequestCommand, request[2])
+ if (commandName == null) {
+ throw new OCPPError(
+ ErrorType.NOT_IMPLEMENTED,
+ `Unknown OCPP command '${request[2]}'`,
+ OCPPConstants.UNKNOWN_OCPP_COMMAND,
+ { command: request[2] }
+ )
+ }
await this.handleIncomingMessage(
- request as IncomingRequest,
+ [parsedMessageType, request[1], commandName, request[3]],
messageLifecycleSignal,
messageLifecycleIsCurrent,
messageSourceIsCurrent
)
break
+ }
// Response Message
case MessageType.CALL_RESULT_MESSAGE:
- this.handleResponseMessage(request as Response)
+ messageType = MessageType.CALL_RESULT_MESSAGE
+ this.handleResponseMessage(request)
break
// Unknown Message
default:
- // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
- errorMsg = `Wrong message type ${messageType}`
+ errorMsg = `Wrong message type ${String(parsedMessageType)}`
logger.error(`${this.logPrefix()} ${moduleName}.onMessage: ${errorMsg}`)
throw new OCPPError(
this.stationInfo?.ocppVersion !== OCPPVersion.VERSION_16
}
return
}
- let commandName: IncomingRequestCommand | undefined
- let requestCommandName: IncomingRequestCommand | RequestCommand | undefined
+ let commandName: string | undefined
+ let requestCommandName: string | undefined
let errorCallback: ErrorCallback
const [, messageId] = request
const ocppError =
this.requests.delete(messageId)
}
break
- case MessageType.CALL_MESSAGE:
+ case MessageType.CALL_MESSAGE: {
if (!messageLifecycleIsCurrent() || !messageSourceIsCurrent()) return
- ;[, , commandName] = request as IncomingRequest
- await this.ocppRequestService.sendError(this, messageId, ocppError, commandName)
+ const [, , rawCommandName] = request
+ commandName = typeof rawCommandName === 'string' ? rawCommandName : undefined
+ await this.ocppRequestService.sendError(this, messageId, ocppError, ocppError.command)
break
+ }
}
if (!(error instanceof OCPPError)) {
logger.warn(
onCompleteCallback()
return
}
- if (isRequest) {
- ;[, messageId, commandName] = parsedMessage as OutgoingRequest
+ if (parsedMessage[0] === MessageType.CALL_MESSAGE) {
+ ;[, messageId, commandName] = parsedMessage
}
const bufferedMessageInFlight: {
entry: BufferedMessageEntry
templateFile: string
): Voltage => {
const errorMsg = `Unknown ${currentType} currentOutType in template file ${templateFile}, cannot define default voltage out`
- let defaultVoltageOut: number
+ let defaultVoltageOut: Voltage
switch (currentType) {
case CurrentType.AC:
defaultVoltageOut = Voltage.VOLTAGE_230
GetCertificateStatusEnumType,
type HeartbeatResponse,
Iso15118EVCertificateStatusEnumType,
+ isOCPP16MeterValue,
+ isOCPP20MeterValue,
type MessageEvent,
type MeterValuesRequest,
type MeterValuesResponse,
import {
Constants,
convertToInt,
+ getEnumStringValue,
getErrorMessage,
isAsyncFunction,
isEmpty,
[
BroadcastChannelProcedureName.GET_15118_EV_CERTIFICATE,
r =>
- (r as OCPP20Get15118EVCertificateResponse).status ===
+ getEnumStringValue(Iso15118EVCertificateStatusEnumType, r.status) ===
Iso15118EVCertificateStatusEnumType.Accepted,
],
[
BroadcastChannelProcedureName.GET_CERTIFICATE_STATUS,
r =>
- (r as OCPP20GetCertificateStatusResponse).status === GetCertificateStatusEnumType.Accepted,
+ getEnumStringValue(GetCertificateStatusEnumType, r.status) ===
+ GetCertificateStatusEnumType.Accepted,
],
[
BroadcastChannelProcedureName.SIGN_CERTIFICATE,
- r => (r as OCPP20SignCertificateResponse).status === GenericStatus.Accepted,
+ r => getEnumStringValue(GenericStatus, r.status) === GenericStatus.Accepted,
],
])
)
}
const requestedPublicKeyIncluded =
- requestedMeterValues?.some(meterValue =>
- isOcpp2
- ? (meterValue as OCPP20MeterValue).sampledValue.some(sampledValue =>
- isNotEmptyString(getRawSignedMeterValuePublicKey(sampledValue))
- )
- : (meterValue as OCPP16MeterValue).sampledValue.some(sampledValue =>
- isNotEmptyString(getOCPP16SignedMeterValuePublicKey(sampledValue))
- )
- ) === true
+ requestedMeterValues?.some(meterValue => {
+ if (isOcpp2 && isOCPP20MeterValue(meterValue)) {
+ return meterValue.sampledValue.some(sampledValue =>
+ isNotEmptyString(getRawSignedMeterValuePublicKey(sampledValue))
+ )
+ }
+ if (!isOcpp2 && isOCPP16MeterValue(meterValue)) {
+ return meterValue.sampledValue.some(sampledValue =>
+ isNotEmptyString(getOCPP16SignedMeterValuePublicKey(sampledValue))
+ )
+ }
+ throw new BaseError(
+ `${this.chargingStation.logPrefix()} ${moduleName}.handleMeterValues: meterValue does not match the station OCPP version`
+ )
+ }) === true
if (requestedMeterValues == null && connectorStatus?.transactionEnding === true) {
throw new BaseError(
`${this.chargingStation.logPrefix()} ${moduleName}.handleMeterValues: Transaction is ending`
// SignedData SampledValue when signing is enabled for the connector.
// OCPP 2.0.x signing is applied inline by the versioned dispatcher.
if (!isOcpp2 && transactionId != null) {
+ if (!isOCPP16MeterValue(meterValue)) {
+ throw new BaseError('Generated MeterValue does not match OCPP 1.6')
+ }
publicKeyIncluded = OCPP16ServiceUtils.appendSignedUpdatedReadings(
this.chargingStation,
connectorId,
convertToInt(transactionId),
- meterValue as OCPP16MeterValue
+ meterValue
)
} else if (isOcpp2) {
- publicKeyIncluded = (meterValue as OCPP20MeterValue).sampledValue.some(sampledValue =>
+ if (!isOCPP20MeterValue(meterValue)) {
+ throw new BaseError('Generated MeterValue does not match OCPP 2.0.x')
+ }
+ publicKeyIncluded = meterValue.sampledValue.some(sampledValue =>
isNotEmptyString(getRawSignedMeterValuePublicKey(sampledValue))
)
}
MeterValuePhase,
MeterValueUnit,
OCPP16MeterValueFormat,
+ OCPP16MeterValueMeasurand,
+ OCPP20MeasurandEnumType,
OCPPVersion,
} from '../../types/index.js'
import {
JSON.stringify([
template.phase,
template.location ?? MeterValueLocation.OUTLET,
- (template.unit as MeterValueUnit | undefined) ?? MeterValueUnit.WATT_HOUR,
+ ocppVersion === OCPPVersion.VERSION_16
+ ? (template.unit ?? MeterValueUnit.WATT_HOUR)
+ : (template.unitOfMeasure?.unit ?? template.unit ?? MeterValueUnit.WATT_HOUR),
+ ocppVersion === OCPPVersion.VERSION_16 ? 0 : (template.unitOfMeasure?.multiplier ?? 0),
context ?? template.context ?? MeterValueContext.SAMPLE_PERIODIC,
ocppVersion === OCPPVersion.VERSION_16
? (template.format ?? OCPP16MeterValueFormat.RAW)
- : canonicalizeCustomData(template.customData),
+ : [
+ canonicalizeCustomData(template.customData),
+ canonicalizeCustomData(template.unitOfMeasure?.customData),
+ ],
])
const templateFamilyKey = (
customData: template.customData,
location: template.location ?? MeterValueLocation.OUTLET,
measurand: template.measurand ?? MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER,
- unit: (template.unit as MeterValueUnit | undefined) ?? MeterValueUnit.WATT_HOUR,
+ unit: template.unitOfMeasure?.unit ?? template.unit ?? MeterValueUnit.WATT_HOUR,
+ unitCustomData: template.unitOfMeasure?.customData,
+ unitMultiplier: template.unitOfMeasure?.multiplier,
})
/**
const templates = resolveTemplates(context, connectorId, connectorStatus, evseIdOverride)
const groups = groupTemplatesByMeasurand(templates)
if (enabledMeasurands?.has(MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_INTERVAL) === true) {
+ const intervalMeasurand =
+ context.stationInfo?.ocppVersion === OCPPVersion.VERSION_16
+ ? OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_INTERVAL
+ : OCPP20MeasurandEnumType.ENERGY_ACTIVE_IMPORT_INTERVAL
const intervalTemplates = groups.get(MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_INTERVAL) ?? []
const intervalTemplateIdentities = new Set(
intervalTemplates.map(template =>
.filter(template => {
const intervalTemplate = {
...template,
- measurand: MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_INTERVAL,
- } as SampledValueTemplate
+ measurand: intervalMeasurand,
+ }
return !intervalTemplateIdentities.has(
energyIntervalFallbackIdentity(
intervalTemplate,
)
)
})
- .map(
- template =>
- ({
- ...template,
- measurand: MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_INTERVAL,
- }) as SampledValueTemplate
- )
+ .map(template => ({
+ ...template,
+ measurand: intervalMeasurand,
+ }))
if (isNotEmptyArray(intervalFallbacks)) {
groups.set(MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_INTERVAL, [
...intervalTemplates,
measurand === MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_INTERVAL
? projectDcOutputValue(context, currentType, effectiveEvseId, template, raw)
: raw
- const unitDivider = resolveMeterValueUnitDivider(
- measurand,
- template.unit as MeterValueUnit | undefined
- )
+ const unitDivider = resolveMeterValueUnitDivider(measurand, template.unit)
const scaled =
measurand === MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_INTERVAL
? truncateTransactionIntervalValue(physicalValue / unitDivider)
* Resolves the base-to-configured-unit divider for a meter-value measurand.
* @param measurand - OCPP meter-value measurand.
* @param unit - Configured output unit.
- * @returns `Constants.UNIT_DIVIDER_KILO` for the family's kilo unit, otherwise `1`.
+ * @returns The named unit's divider from the family's base unit.
*/
export const resolveMeterValueUnitDivider = (
measurand: MeterValueMeasurand,
return kiloUnit != null && kiloUnit === unit ? Constants.UNIT_DIVIDER_KILO : 1
}
+/**
+ * Resolves the scale from an emitted meter-value number to its physical base quantity.
+ * The named-unit scale and OCPP 2.0 decimal multiplier are cumulative.
+ * @param measurand - Meter-value measurand used to resolve base/kilo unit families.
+ * @param unit - Emitted unit name.
+ * @param multiplier - OCPP 2.0 decimal multiplier.
+ * @returns Scale from the emitted number to the physical base quantity.
+ */
+export const resolveMeterValueUnitScale = (
+ measurand: MeterValueMeasurand,
+ unit: string | undefined,
+ multiplier = 0
+): number => {
+ const namedUnitScale =
+ unit === MeterValueUnit.KILO_PASCAL
+ ? Constants.UNIT_DIVIDER_KILO
+ : resolveMeterValueUnitDivider(measurand, unit)
+ return namedUnitScale * 10 ** multiplier
+}
+
/**
* Resolves an OCPP line or line-to-neutral phase to its one-based line index.
* @param phase - OCPP meter-value phase.
* @param identity.location - Effective measurement location
* @param identity.measurand - Effective measurand
* @param identity.unit - Effective unit
+ * @param identity.unitCustomData - Unit descriptor custom data
+ * @param identity.unitMultiplier - Effective decimal multiplier
* @returns Stable serialized family identity
*/
export const buildSampledValueFamilyKey = (identity: {
customData: unknown
location: MeterValueLocation | undefined
measurand: MeterValueMeasurand
- unit: MeterValueUnit | undefined
+ unit: string | undefined
+ unitCustomData?: unknown
+ unitMultiplier?: number
}): string =>
JSON.stringify([
identity.context,
identity.location,
identity.unit,
+ identity.unitMultiplier ?? 0,
identity.measurand,
canonicalizeCustomData(identity.customData),
+ canonicalizeCustomData(identity.unitCustomData),
])
import { type ConnectorStatus, CurrentType, MeterValueMeasurand } from '../../types/index.js'
-import { Constants, isEmpty } from '../../utils/index.js'
+import { isEmpty } from '../../utils/index.js'
+import { resolveMeterValueUnitScale } from './MeterValueUtils.js'
export interface TransactionIntervalState {
consumed: number
: Number.parseFloat(sampledValue.value)
if (!Number.isFinite(value)) continue
const unit = sampledValue.unitOfMeasure?.unit ?? sampledValue.unit
- const unitMultiplier =
- unit === 'kWh' ? Constants.UNIT_DIVIDER_KILO : unit === 'MWh' ? 1_000_000 : 1
- const decimalMultiplier = 10 ** (sampledValue.unitOfMeasure?.multiplier ?? 0)
+ const unitScale = resolveMeterValueUnitScale(
+ MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_INTERVAL,
+ unit,
+ sampledValue.unitOfMeasure?.multiplier
+ )
const phaseMultiplier = /^L[123](?:-N)?$/.test(sampledValue.phase ?? '') ? numberOfPhases : 1
const locationMultiplier =
sampledValue.location === 'Inlet' && inletToOutputEfficiency > 0 ? inletToOutputEfficiency : 1
representedEnergyWh = Math.max(
representedEnergyWh,
- value * unitMultiplier * decimalMultiplier * phaseMultiplier * locationMultiplier
+ value * unitScale * phaseMultiplier * locationMultiplier
)
}
return representedEnergyWh
type MeterValueUnitFamily,
resolveLinePhaseIndex,
resolveMeterValueUnitDivider,
+ resolveMeterValueUnitScale,
} from './MeterValueUtils.js'
export {
captureTransactionIntervalState,
type TransactionMeterValueDelivery,
TransactionMeterValueDeliveryBarrier,
} from '../../../charging-station/meter-values/index.js'
-import { OCPPError } from '../../../exception/index.js'
+import { BaseError, OCPPError } from '../../../exception/index.js'
import {
type ChangeConfigurationRequest,
type ChangeConfigurationResponse,
type GetDiagnosticsResponse,
type IncomingRequestCommand,
type IncomingRequestHandler,
+ isOCPP16ChargingProfile,
type JsonType,
type LogConfiguration,
OCPP16AuthorizationStatus,
type OCPP16HeartbeatResponse,
OCPP16IncomingRequestCommand,
OCPP16MessageTrigger,
- type OCPP16MeterValue,
OCPP16MeterValueContext,
type OCPP16MeterValuesRequest,
type OCPP16MeterValuesResponse,
type RemoteStopTransactionRequest,
ReservationTerminationReason,
type ResetRequest,
+ ResetType,
type SetChargingProfileRequest,
type SetChargingProfileResponse,
type StatusNotificationOptions,
convertToIntOrNaN,
ensureError,
formatDurationMilliSeconds,
+ getEnumStringValue,
handleIncomingRequestError,
isEmpty,
isNotEmptyArray,
import { OCPPIncomingRequestService } from '../OCPPIncomingRequestService.js'
import { isIdTagAuthorized } from '../OCPPServiceOperations.js'
import {
+ assertMeterValueBuilderResult,
buildMeterValue,
createPayloadValidatorMap,
isConnectorIdValid,
const moduleName = 'OCPP16IncomingRequestService'
+const requireOCPP16ChargePointStatus = (status: string | undefined): OCPP16ChargePointStatus => {
+ const ocpp16Status = getEnumStringValue(OCPP16ChargePointStatus, status)
+ if (ocpp16Status == null) {
+ throw new BaseError(`Invalid OCPP 1.6 charge point status '${String(status)}'`)
+ }
+ return ocpp16Status
+}
+
+const resolveTriggeredStatusNotificationStatus = (
+ chargingStation: ChargingStation,
+ connectorId: number,
+ status: string | undefined
+): OCPP16ChargePointStatus =>
+ requireOCPP16ChargePointStatus(
+ status ??
+ (connectorId === 0
+ ? chargingStation.getConnectorStatus(0)?.availability === OCPP16AvailabilityType.Operative
+ ? OCPP16ChargePointStatus.Available
+ : OCPP16ChargePointStatus.Unavailable
+ : undefined)
+ )
+
+const requireOCPP16ChargingProfiles = (
+ chargingProfiles: ConnectorStatus['chargingProfiles']
+): OCPP16ChargingProfile[] => {
+ const profiles = chargingProfiles ?? []
+ if (!profiles.every(isOCPP16ChargingProfile)) {
+ throw new BaseError('Charging profiles do not match OCPP 1.6')
+ }
+ return profiles
+}
+
/**
* Per-station lifecycle state carried on {@link OCPP16IncomingRequestService}.
*
OCPP16RequestCommand.STATUS_NOTIFICATION,
{
connectorId,
- status: chargingStation.getConnectorStatus(connectorId)
- ?.status as OCPP16ChargePointStatus,
+ status: resolveTriggeredStatusNotificationStatus(
+ chargingStation,
+ connectorId,
+ chargingStation.getConnectorStatus(connectorId)?.status
+ ),
},
{
triggerMessage: true,
OCPP16RequestCommand.STATUS_NOTIFICATION,
{
connectorId,
- status: connectorStatus.status as OCPP16ChargePointStatus,
+ status: resolveTriggeredStatusNotificationStatus(
+ chargingStation,
+ connectorId,
+ connectorStatus.status
+ ),
},
{
triggerMessage: true,
): ChangeConfigurationResponse {
const { key, value } = commandPayload
const keyToChange = getConfigurationKey(chargingStation, key, true)
+ const standardKey = getEnumStringValue(OCPP16StandardParametersKey, keyToChange?.key)
if (keyToChange?.readonly === true) {
return OCPP16Constants.OCPP_CONFIGURATION_RESPONSE_REJECTED
} else if (keyToChange?.readonly === false) {
OCPP16StandardParametersKey.TransactionMessageRetryInterval,
OCPP16StandardParametersKey.WebSocketPingInterval,
])
- if (integerKeys.has(keyToChange.key as OCPP16StandardParametersKey)) {
+ if (standardKey != null && integerKeys.has(standardKey)) {
// convertToInt would truncate '1.5' → 1 and throw on ''/'abc'; Number() instead yields a non-integer
// float or NaN that !Number.isInteger rejects, and isNotEmptyString rejects '' (Number('') === 0 would otherwise pass).
const numValue = Number(value)
valueChanged = true
}
let triggerHeartbeatRestart = false
- if (
- (keyToChange.key as OCPP16StandardParametersKey) ===
- OCPP16StandardParametersKey.HeartBeatInterval &&
- valueChanged
- ) {
+ if (standardKey === OCPP16StandardParametersKey.HeartBeatInterval && valueChanged) {
setConfigurationKeyValue(
chargingStation,
OCPP16StandardParametersKey.HeartbeatInterval,
)
triggerHeartbeatRestart = true
}
- if (
- (keyToChange.key as OCPP16StandardParametersKey) ===
- OCPP16StandardParametersKey.HeartbeatInterval &&
- valueChanged
- ) {
+ if (standardKey === OCPP16StandardParametersKey.HeartbeatInterval && valueChanged) {
setConfigurationKeyValue(
chargingStation,
OCPP16StandardParametersKey.HeartBeatInterval,
if (triggerHeartbeatRestart) {
chargingStation.restartHeartbeat()
}
- if (
- (keyToChange.key as OCPP16StandardParametersKey) ===
- OCPP16StandardParametersKey.WebSocketPingInterval &&
- valueChanged
- ) {
+ if (standardKey === OCPP16StandardParametersKey.WebSocketPingInterval && valueChanged) {
chargingStation.restartWebSocketPing()
}
if (
- (keyToChange.key as OCPP16StandardParametersKey) ===
- OCPP16StandardParametersKey.MeterValueSampleInterval &&
+ standardKey === OCPP16StandardParametersKey.MeterValueSampleInterval &&
chargingStation.getNumberOfRunningTransactions() > 0 &&
valueChanged
) {
const clearedCP = OCPP16ServiceUtils.clearChargingProfiles(
chargingStation,
commandPayload,
- connectorStatus.chargingProfiles as OCPP16ChargingProfile[]
+ requireOCPP16ChargingProfiles(connectorStatus.chargingProfiles)
)
if (clearedCP) {
logger.debug(
const clearedConnectorCP = OCPP16ServiceUtils.clearChargingProfiles(
chargingStation,
commandPayload,
- connectorStatus.chargingProfiles as OCPP16ChargingProfile[]
+ requireOCPP16ChargingProfiles(connectorStatus.chargingProfiles)
)
if (clearedConnectorCP && !clearedCP) {
clearedCP = true
const allChargingProfiles: OCPP16ChargingProfile[] = []
for (const { connectorId: aggregatedConnectorId } of chargingStation.iterateConnectors()) {
allChargingProfiles.push(
- ...(getConnectorChargingProfiles(
- chargingStation,
- aggregatedConnectorId
- ) as OCPP16ChargingProfile[])
+ ...getConnectorChargingProfiles(chargingStation, aggregatedConnectorId).filter(
+ isOCPP16ChargingProfile
+ )
)
}
if (isEmpty(allChargingProfiles)) {
) {
return OCPP16Constants.OCPP_RESPONSE_REJECTED
}
- const chargingProfiles = getConnectorChargingProfiles(
- chargingStation,
- connectorId
- ) as OCPP16ChargingProfile[]
+ const chargingProfiles = getConnectorChargingProfiles(chargingStation, connectorId).filter(
+ isOCPP16ChargingProfile
+ )
const compositeSchedule = this.composeCompositeSchedule(
chargingStation,
chargingProfiles,
chargingStation: ChargingStation,
commandPayload: ResetRequest
): GenericResponse {
- const { type } = commandPayload
- const reason = `${type}Reset` as OCPP16StopTransactionReason
+ const resetType = getEnumStringValue(ResetType, commandPayload.type)
+ if (resetType == null) {
+ throw new OCPPError(
+ ErrorType.FORMAT_VIOLATION,
+ `Invalid Reset type '${commandPayload.type}'`,
+ OCPP16IncomingRequestCommand.RESET
+ )
+ }
+ const reason =
+ resetType === ResetType.HARD
+ ? OCPP16StopTransactionReason.HARD_RESET
+ : OCPP16StopTransactionReason.SOFT_RESET
const graceful = reason === OCPP16StopTransactionReason.SOFT_RESET
this.pendingResetActions.set(commandPayload, async lifecycleSignal => {
if (chargingStation.lifecycleAbortSignal !== lifecycleSignal || lifecycleSignal.aborted) {
await chargingStation.reset(reason, graceful)
})
logger.info(
- `${chargingStation.logPrefix()} ${moduleName}.handleRequestReset: ${type} reset request received, simulating it. The station will be back online in ${formatDurationMilliSeconds(
+ `${chargingStation.logPrefix()} ${moduleName}.handleRequestReset: ${resetType} reset request received, simulating it. The station will be back online in ${formatDurationMilliSeconds(
chargingStation.stationInfo?.resetTime ?? 0
)}`
)
target.transactionId != null
? captureTransactionIntervalState(target.connectorStatus)
: undefined
- const meterValue = buildMeterValue(
+ const builtMeterValue = buildMeterValue(
chargingStation,
target.transactionId,
0,
OCPP16MeterValueContext.TRIGGER,
false,
{ advanceEnergy: true, connectorId: target.connectorId, snapshot: true }
- ) as OCPP16MeterValue
+ )
+ assertMeterValueBuilderResult(builtMeterValue, OCPPVersion.VERSION_16)
+ const meterValue = builtMeterValue
if (!isNotEmptyArray(meterValue.sampledValue)) {
delivery?.settle(true)
for (const reservedTarget of targets) reservedTarget.delivery?.settle(true)
+import { BaseError } from '../../../exception/index.js'
import {
type ChargingStationInfo,
type MeterValueContext,
type MeterValuePhase,
type OCPP16BootNotificationRequest,
- type OCPP16MeterValueContext,
+ OCPP16MeterValueContext,
OCPP16MeterValueFormat,
OCPP16MeterValueLocation,
OCPP16MeterValueMeasurand,
+ OCPP16MeterValuePhase,
+ OCPP16MeterValueUnit,
type OCPP16SampledValue,
type OCPP16SignedMeterValue,
type SampledValueTemplate,
} from '../../../types/index.js'
+import { getEnumStringValue } from '../../../utils/index.js'
import { resolveSampledValueFields } from '../OCPPServiceUtils.js'
+const resolveOCPP16EnumValue = <T extends string>(
+ enumObject: Readonly<Record<string, T>>,
+ value: string | undefined,
+ fieldName: string,
+ providedValue: string | undefined
+): T | undefined => {
+ const resolvedValue = getEnumStringValue(enumObject, value)
+ if (providedValue != null && resolvedValue == null) {
+ throw new BaseError(`Invalid OCPP 1.6 sampled value ${fieldName} '${providedValue}'`)
+ }
+ return resolvedValue
+}
+
export const buildOCPP16BootNotificationRequest = (
stationInfo: ChargingStationInfo
): OCPP16BootNotificationRequest => ({
context?: MeterValueContext,
phase?: MeterValuePhase
): OCPP16SampledValue {
+ if (sampledValueTemplate.unitOfMeasure != null) {
+ throw new BaseError('Invalid OCPP 1.6 sampled value unitOfMeasure')
+ }
const fields = resolveSampledValueFields(sampledValueTemplate, value, context, phase)
+ const resolvedContext = resolveOCPP16EnumValue(
+ OCPP16MeterValueContext,
+ fields.context,
+ 'context',
+ context ?? sampledValueTemplate.context
+ )
+ const resolvedLocation = resolveOCPP16EnumValue(
+ OCPP16MeterValueLocation,
+ fields.location,
+ 'location',
+ sampledValueTemplate.location
+ )
+ const resolvedMeasurand = resolveOCPP16EnumValue(
+ OCPP16MeterValueMeasurand,
+ fields.measurand,
+ 'measurand',
+ sampledValueTemplate.measurand
+ )
+ const resolvedPhase = resolveOCPP16EnumValue(
+ OCPP16MeterValuePhase,
+ fields.phase,
+ 'phase',
+ phase ?? sampledValueTemplate.phase
+ )
+ const resolvedUnit = resolveOCPP16EnumValue(
+ OCPP16MeterValueUnit,
+ fields.unit,
+ 'unit',
+ sampledValueTemplate.unit
+ )
return {
- context: fields.context,
- location: fields.location,
- measurand: fields.measurand,
- ...(fields.unit != null && { unit: fields.unit }),
+ ...(resolvedContext != null && { context: resolvedContext }),
+ ...(resolvedLocation != null && { location: resolvedLocation }),
+ ...(resolvedMeasurand != null && { measurand: resolvedMeasurand }),
+ ...(resolvedUnit != null && { unit: resolvedUnit }),
value: fields.value.toString(),
- ...(fields.phase != null && { phase: fields.phase }),
- } as OCPP16SampledValue
+ ...(resolvedPhase != null && { phase: resolvedPhase }),
+ }
}
/**
isWithinInterval,
} from 'date-fns'
-import type { SigningMethodEnumType } from '../../../types/index.js'
-
import {
type ChargingStation,
getConfigurationKey,
type ConnectorStatus,
ErrorType,
type GenericResponse,
+ type MeterValue,
type MeterValuesRequest,
type MeterValuesResponse,
OCPP16AuthorizationStatus,
convertToDate,
convertToInt,
ensureError,
+ getEnumStringValue,
isNotEmptyArray,
isNotEmptyString,
logger,
import { mapOCPP16Status, OCPPAuthServiceFactory } from '../auth/index.js'
import { sendAndSetConnectorStatus } from '../OCPPConnectorStatusOperations.js'
import {
+ assertMeterValueBuilderResult,
buildEmptyMeterValue,
buildMeterValue,
createPayloadConfigs,
import { buildOCPP16SampledValue, buildSignedOCPP16SampledValue } from './OCPP16RequestBuilders.js'
const moduleName = 'OCPP16ServiceUtils'
+
+const requireOCPP16MeterValue = (meterValue: MeterValue): OCPP16MeterValue => {
+ assertMeterValueBuilderResult(meterValue, OCPPVersion.VERSION_16)
+ return meterValue
+}
+
const RFC3339_TIMESTAMP_PATTERN =
/^(\d{4})-(\d{2})-(\d{2})[Tt](\d{2}):(\d{2}):(\d{2})(?:\.(\d+))?([Zz]|([+-])(\d{2}):(\d{2}))$/
const DAYS_PER_MONTH = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] as const
transactionId
)
}
- const meterValue = buildEmptyMeterValue() as OCPP16MeterValue
+ const meterValue = requireOCPP16MeterValue(buildEmptyMeterValue())
const meterStartOutputWh = meterStart ?? 0
// Energy.Active.Import.Register measurand (default)
const sampledValueTemplate = getSampledValueTemplate(chargingStation, connectorId)
sampledValueTemplate.unit === OCPP16MeterValueUnit.KILO_WATT_HOUR
? Constants.UNIT_DIVIDER_KILO
: 1
- const meterValue = { sampledValue: [], timestamp } as OCPP16MeterValue
+ const meterValue = requireOCPP16MeterValue(buildEmptyMeterValue(timestamp))
meterValue.sampledValue.push(
buildOCPP16SampledValue(
sampledValueTemplate,
const sampleAt = Date.now()
const elapsedInterval = Math.max(elapsedIntervalCarry, sampleAt - lastSampleAt)
const intervalState = captureTransactionIntervalState(connectorStatus)
- const meterValue = buildMeterValue(
- chargingStation,
- transactionId,
- elapsedInterval
- ) as OCPP16MeterValue
+ const meterValue = requireOCPP16MeterValue(
+ buildMeterValue(chargingStation, transactionId, elapsedInterval)
+ )
lastSampleAt = sampleAt
elapsedIntervalCarry = 0
completeTransactionIntervalState(intervalState, 'default', [meterValue])
)
}
+ const rawStopReason = reason ?? requestOverrides.reason
+ const validatedStopReason = getEnumStringValue(OCPP16StopTransactionReason, rawStopReason)
+ if (rawStopReason !== undefined && validatedStopReason == null) {
+ return Promise.reject(
+ new OCPPError(
+ ErrorType.FORMAT_VIOLATION,
+ `${moduleName}.stopTransactionOnConnector: Invalid StopTransaction reason '${rawStopReason}'`,
+ RequestCommand.STOP_TRANSACTION
+ )
+ )
+ }
+
let transactionEndingOwned = true
const periodicMeterValuesWasRunning =
connectorStatus.transactionUpdatedMeterValuesSetInterval != null
timestamp,
transactionData,
transactionId,
- ...(reason != null && { reason: reason as StopTransactionRequest['reason'] }),
+ ...(validatedStopReason != null && { reason: validatedStopReason }),
})
if (
strictEndMeterValueIsSolePublicKeyCarrier &&
getConfigurationKey(chargingStation, startTxnSampledDataKey)?.value != null
? startTxnSampledDataKey
: undefined
- return buildMeterValue(
- chargingStation,
- transactionId,
- 0,
- measurandsKey,
- OCPP16MeterValueContext.TRANSACTION_BEGIN
- ) as OCPP16MeterValue
+ return requireOCPP16MeterValue(
+ buildMeterValue(
+ chargingStation,
+ transactionId,
+ 0,
+ measurandsKey,
+ OCPP16MeterValueContext.TRANSACTION_BEGIN
+ )
+ )
}
private static buildSignedSampledValue (
chargingStation.getConnectorStatus(connectorId)?.transactionEnergyActiveImportIntervalCarry
?.default ?? 0
if (intervalEnergyWh <= 0) return
- const intervalMeterValue = buildMeterValue(
- chargingStation,
- transactionId,
- 0,
- OCPP16StandardParametersKey.StopTxnSampledData,
- OCPP16MeterValueContext.TRANSACTION_END,
- false,
- {
- advanceEnergy: false,
- connectorId,
- energyIntervalWhOverride: intervalEnergyWh,
- energyRegisterWhOverride: meterStop,
- projectDcForLocation: true,
- snapshot: true,
- timestamp: meterValue.timestamp,
- }
- ) as OCPP16MeterValue
+ const intervalMeterValue = requireOCPP16MeterValue(
+ buildMeterValue(
+ chargingStation,
+ transactionId,
+ 0,
+ OCPP16StandardParametersKey.StopTxnSampledData,
+ OCPP16MeterValueContext.TRANSACTION_END,
+ false,
+ {
+ advanceEnergy: false,
+ connectorId,
+ energyIntervalWhOverride: intervalEnergyWh,
+ energyRegisterWhOverride: meterStop,
+ projectDcForLocation: true,
+ snapshot: true,
+ timestamp: meterValue.timestamp,
+ }
+ )
+ )
const intervalSamples = intervalMeterValue.sampledValue.filter(
sampledValue =>
sampledValue.measurand === OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_INTERVAL
const configuredSigningMethod = getConfigurationKey(
chargingStation,
OCPP16VendorParametersKey.SigningMethod
- )?.value as SigningMethodEnumType | undefined
+ )?.value
const prerequisiteResult = validateSigningPrerequisites(publicKeyHex, configuredSigningMethod)
if (!prerequisiteResult.enabled) {
import {
atomicWriteFile,
convertToDate,
+ getEnumStringValue,
getErrorMessage,
isEmpty,
isNotEmptyArray,
.map(dirent => dirent.name)
for (const certType of certTypes) {
- if (filterTypes != null && isNotEmptyArray(filterTypes)) {
- if (!filterTypes.includes(certType as InstallCertificateUseEnumType)) {
- continue
- }
+ const certificateType = getEnumStringValue(InstallCertificateUseEnumType, certType)
+ if (certificateType == null) continue
+ if (
+ filterTypes != null &&
+ isNotEmptyArray(filterTypes) &&
+ !filterTypes.includes(certificateType)
+ ) {
+ continue
}
const certTypeDir = join(basePath, certType)
certificateHashDataChain.push({
certificateHashData: hashData,
- certificateType: this.mapInstallTypeToGetType(
- certType as InstallCertificateUseEnumType
- ),
+ certificateType: this.mapInstallTypeToGetType(certificateType),
})
} catch {
// Skip unreadable or unparsable certificate file
import type { StopTransactionReason } from '../../../types/index.js'
+import { BaseError } from '../../../exception/index.js'
import {
BootReasonEnumType,
type ChargingStationInfo,
type MeterValueContext,
type MeterValuePhase,
+ MeterValueUnit,
OCPP16StopTransactionReason,
type OCPP20BootNotificationRequest,
+ OCPP20LocationEnumType,
OCPP20MeasurandEnumType,
+ OCPP20PhaseEnumType,
+ OCPP20ReadingContextEnumType,
OCPP20ReasonEnumType,
type OCPP20SampledValue,
OCPP20TriggerReasonEnumType,
type SampledValueTemplate,
} from '../../../types/index.js'
+import { convertToFloat, getEnumStringValue } from '../../../utils/index.js'
+import {
+ areMeterValueUnitsCompatible,
+ getMeterValueUnitFamily,
+ resolveMeterValueUnitScale,
+} from '../../meter-values/index.js'
import { resolveSampledValueFields } from '../OCPPServiceUtils.js'
import {
generateSignedMeterData,
type SignedSampledValueResult,
} from '../OCPPSignedMeterValueUtils.js'
+const requireOCPP20EnumValue = <T extends string>(
+ enumObject: Readonly<Record<string, T>>,
+ value: string | undefined,
+ fieldName: string
+): T | undefined => {
+ if (value == null) return undefined
+ const resolvedValue = getEnumStringValue(enumObject, value)
+ if (resolvedValue == null) {
+ throw new BaseError(`Invalid OCPP 2.0.x sampled value ${fieldName} '${value}'`)
+ }
+ return resolvedValue
+}
+
export const buildOCPP20BootNotificationRequest = (
stationInfo: ChargingStationInfo,
bootReason: BootReasonEnumType = BootReasonEnumType.PowerUp
/**
* Builds an OCPP 2.0.1 sampled value from a template and measurement data.
* @param sampledValueTemplate - The sampled value template to use.
- * @param value - The measured value.
+ * @param value - The measured value expressed in the template's flat/default unit.
* @param context - The reading context.
* @param phase - The phase of the measurement.
* @param signingConfig - Optional signing configuration for generating signedMeterValue.
signingConfig?: SampledValueSigningConfig
): SignedSampledValueResult<OCPP20SampledValue> {
const fields = resolveSampledValueFields(sampledValueTemplate, value, context, phase)
+ const resolvedContext = requireOCPP20EnumValue(
+ OCPP20ReadingContextEnumType,
+ fields.context,
+ 'context'
+ )
+ const resolvedLocation = requireOCPP20EnumValue(
+ OCPP20LocationEnumType,
+ fields.location,
+ 'location'
+ )
+ const resolvedMeasurand = requireOCPP20EnumValue(
+ OCPP20MeasurandEnumType,
+ fields.measurand,
+ 'measurand'
+ )
+ const resolvedPhase = requireOCPP20EnumValue(OCPP20PhaseEnumType, fields.phase, 'phase')
+ const sourceUnit = fields.unit
+ const emittedUnit = sampledValueTemplate.unitOfMeasure?.unit ?? sourceUnit
+ if (
+ sourceUnit != null &&
+ emittedUnit != null &&
+ sourceUnit !== emittedUnit &&
+ !areMeterValueUnitsCompatible(fields.measurand, sourceUnit, emittedUnit)
+ ) {
+ throw new BaseError(
+ `Cannot convert OCPP 2.0.x sampled value unit '${sourceUnit}' to '${emittedUnit}'`
+ )
+ }
+ const unitOfMeasure =
+ sampledValueTemplate.unitOfMeasure != null || sourceUnit != null
+ ? {
+ ...sampledValueTemplate.unitOfMeasure,
+ ...(sampledValueTemplate.unitOfMeasure?.unit == null &&
+ sourceUnit != null && { unit: sourceUnit }),
+ }
+ : undefined
+ const emittedScale = resolveMeterValueUnitScale(
+ fields.measurand,
+ emittedUnit,
+ unitOfMeasure?.multiplier
+ )
+ const emittedValue =
+ (convertToFloat(fields.value) * resolveMeterValueUnitScale(fields.measurand, sourceUnit)) /
+ emittedScale
const sampledValue: OCPP20SampledValue = {
...(sampledValueTemplate.customData != null && {
customData: sampledValueTemplate.customData,
}),
- context: fields.context,
- location: fields.location,
- measurand: fields.measurand,
- ...(fields.unit != null && { unitOfMeasure: { unit: fields.unit } }),
- value: fields.value,
- ...(fields.phase != null && { phase: fields.phase }),
- } as OCPP20SampledValue
+ ...(resolvedContext != null && { context: resolvedContext }),
+ ...(resolvedLocation != null && { location: resolvedLocation }),
+ ...(resolvedMeasurand != null && { measurand: resolvedMeasurand }),
+ ...(unitOfMeasure != null && { unitOfMeasure }),
+ value: emittedValue,
+ ...(resolvedPhase != null && { phase: resolvedPhase }),
+ }
let publicKeyIncluded = false
signingConfig.publicKeyWithSignedMeterValue,
signingConfig.publicKeySentInTransaction
)
+ const unitFamily = getMeterValueUnitFamily(fields.measurand, emittedUnit)
const signedMeterDataParams: SignedMeterDataParams = {
context: fields.context,
meterSerialNumber: signingConfig.meterSerialNumber,
- meterValue: fields.value,
- meterValueUnit: fields.unit,
+ meterValue: emittedValue * emittedScale,
+ meterValueUnit: getEnumStringValue(MeterValueUnit, unitFamily?.baseUnit ?? emittedUnit),
timestamp: signingConfig.timestamp ?? new Date(),
transactionId: signingConfig.transactionId,
}
recordPendingSharedEnergy,
resolveInletToOutputEfficiency,
resolveLinePhaseIndex,
+ resolveMeterValueUnitScale,
resolveRootSeed,
type TransactionMeterValueDelivery,
TransactionMeterValueDeliveryBarrier,
type ConnectorStatusEnum,
CurrentType,
ErrorType,
+ isOCPP20MeterValue,
type MeterValue,
MeterValueLocation,
OCPP20AuthorizationStatusEnumType,
} from '../auth/index.js'
import { sendPostTransactionStatus } from '../OCPPConnectorStatusOperations.js'
import {
+ assertMeterValueBuilderResult,
buildClockAlignedConnectorMeterValue,
buildMeterValue,
createPayloadConfigs,
unitFamily: MeterValueUnitFamily
): OCPP20SampledValue => {
const configuredUnit = sampledValue.unitOfMeasure?.unit
- const namedUnitMultiplier =
- configuredUnit === unitFamily.kiloUnit ? Constants.UNIT_DIVIDER_KILO : 1
- const decimalMultiplier = 10 ** (sampledValue.unitOfMeasure?.multiplier ?? 0)
+ const unitScale = resolveMeterValueUnitScale(
+ sampledValue.measurand ?? OCPP20MeasurandEnumType.ENERGY_ACTIVE_IMPORT_REGISTER,
+ configuredUnit,
+ sampledValue.unitOfMeasure?.multiplier
+ )
return {
...sampledValue,
unitOfMeasure: {
multiplier: 0,
unit: unitFamily.baseUnit,
},
- value: sampledValue.value * namedUnitMultiplier * decimalMultiplier,
+ value: sampledValue.value * unitScale,
}
}
energyRegisterWhOverride: sharedEnergyRegisterWh,
}),
}
- ) as OCPP20MeterValue
+ )
+ assertMeterValueBuilderResult(meterValue, OCPPVersion.VERSION_201)
const sharedObservationEnergyWh =
usesSharedEvseRegister &&
context !== OCPP20ReadingContextEnumType.TRANSACTION_BEGIN &&
evseId?: number
): OCPP20MeterValue[] {
const connectorStatus = chargingStation.getConnectorStatus(connectorId, evseId)
- const endedMeterValues = (connectorStatus?.transactionEndedMeterValues ??
- []) as OCPP20MeterValue[]
- const beginMeterValue = connectorStatus?.transactionBeginMeterValue as
- OCPP20MeterValue | undefined
+ const endedMeterValues = connectorStatus?.transactionEndedMeterValues ?? []
+ if (!endedMeterValues.every(isOCPP20MeterValue)) {
+ throw new BaseError('Stored transaction-ended MeterValues do not match OCPP 2.0.x')
+ }
+ const beginMeterValue = connectorStatus?.transactionBeginMeterValue
+ if (beginMeterValue != null && !isOCPP20MeterValue(beginMeterValue)) {
+ throw new BaseError('Stored transaction-begin MeterValue does not match OCPP 2.0.x')
+ }
const measurandsKey = buildConfigKey(
OCPP20ComponentName.SampledDataCtrlr,
OCPP20RequiredVariableName.TxEndedMeasurands
SetVariableStatusEnumType,
type VariableType,
} from '../../../types/index.js'
-import { Constants, convertToIntOrNaN, isEmpty, logger } from '../../../utils/index.js'
+import {
+ Constants,
+ convertToIntOrNaN,
+ getEnumStringValue,
+ isEmpty,
+ logger,
+} from '../../../utils/index.js'
import {
addConfigurationKey,
buildConfigKey,
} from './OCPP20VariableRegistry.js'
const isOCPP20ComponentName = (name: string): name is OCPP20ComponentName => {
- return Object.values(OCPP20ComponentName).includes(name as OCPP20ComponentName)
+ return getEnumStringValue(OCPP20ComponentName, name) != null
}
const isOCPP20RequiredVariableName = (name: string): name is OCPP20RequiredVariableName => {
- return Object.values(OCPP20RequiredVariableName).includes(name as OCPP20RequiredVariableName)
+ return getEnumStringValue(OCPP20RequiredVariableName, name) != null
}
const computeConfigurationKeyName = (variableMetadata: VariableMetadata): string =>
ConfigurationStatus,
DataTransferStatus,
GenericStatus,
- type IncomingRequestCommand,
MeterValueMeasurand,
- type RequestCommand,
+ type OCPPCommandName,
ReservationStatus,
TriggerMessageStatus,
+ UNKNOWN_OCPP_COMMAND,
UnlockStatus,
} from '../../types/index.js'
import { Constants } from '../../utils/index.js'
})
static readonly OCPP_WEBSOCKET_TIMEOUT_MS = 60_000
- static readonly UNKNOWN_OCPP_COMMAND = 'unknown OCPP command' as
- IncomingRequestCommand | RequestCommand
+ static readonly UNKNOWN_OCPP_COMMAND: OCPPCommandName = UNKNOWN_OCPP_COMMAND
protected constructor () {
// This is intentional
type ErrorCallback,
type ErrorResponse,
ErrorType,
- type IncomingRequestCommand,
+ IncomingRequestCommand,
type JsonType,
MessageType,
+ type OCPPCommandName,
type OCPPVersion,
type OutgoingRequest,
type PendingRequestCancellationCallback,
ensureError,
formatDurationMilliSeconds,
generateUUID,
+ getEnumStringValue,
getErrorMessage,
getMessageTypeString,
handleSendMessageError,
validatePayload,
} from './OCPPServiceUtils.js'
+const requireOCPPCommand = <T extends IncomingRequestCommand | RequestCommand>(
+ commands: Readonly<Record<string, T>>,
+ commandName: string
+): T => {
+ const command = getEnumStringValue(commands, commandName)
+ if (command == null) {
+ throw new OCPPError(ErrorType.NOT_SUPPORTED, `Unknown OCPP command '${commandName}'`)
+ }
+ return command
+}
+
interface OutgoingCallCancellationState {
readonly destructiveError: OCPPError | undefined
readonly destructiveGeneration: number
chargingStation: ChargingStation,
messageId: string,
ocppError: OCPPError,
- commandName: IncomingRequestCommand | RequestCommand
+ commandName: OCPPCommandName
): Promise<ResponseType> {
try {
return await this.internalSendMessage(
payload: T,
{ forceValidation = false }: { forceValidation?: boolean } = {}
): boolean {
+ const requestCommand = getEnumStringValue(RequestCommand, commandName)
return validatePayload(
chargingStation,
commandName,
payload,
- this.payloadValidatorFunctions.get(commandName as RequestCommand),
+ requestCommand == null ? undefined : this.payloadValidatorFunctions.get(requestCommand),
'request',
true,
forceValidation
commandName: IncomingRequestCommand | RequestCommand,
payload: T
): boolean {
+ const incomingRequestCommand = getEnumStringValue(IncomingRequestCommand, commandName)
return validatePayload(
chargingStation,
commandName,
payload,
- this.ocppResponseService.incomingRequestResponsePayloadValidateFunctions.get(
- commandName as IncomingRequestCommand
- ),
+ incomingRequestCommand == null
+ ? undefined
+ : this.ocppResponseService.incomingRequestResponsePayloadValidateFunctions.get(
+ incomingRequestCommand
+ ),
'incoming request response',
true
)
messageId: string,
messagePayload: JsonType | OCPPError,
messageType: MessageType,
- commandName: IncomingRequestCommand | RequestCommand
+ commandName: OCPPCommandName
): string {
let messageToSend: string
// Type of message
break
}
// Request
- case MessageType.CALL_MESSAGE:
- this.validateRequestPayload(chargingStation, commandName, messagePayload as JsonType)
+ case MessageType.CALL_MESSAGE: {
+ const requestCommand = requireOCPPCommand(RequestCommand, commandName)
+ this.validateRequestPayload(chargingStation, requestCommand, messagePayload as JsonType)
messageToSend = JSON.stringify([
messageType,
messageId,
- commandName as RequestCommand,
+ requestCommand,
messagePayload as JsonType,
] satisfies OutgoingRequest)
break
+ }
// Response
case MessageType.CALL_RESULT_MESSAGE:
this.validateIncomingRequestResponsePayload(
chargingStation,
- commandName,
+ requireOCPPCommand(IncomingRequestCommand, commandName),
messagePayload as JsonType
)
messageToSend = JSON.stringify([
messageId: string,
messagePayload: JsonType | OCPPError,
messageType: MessageType,
- commandName: IncomingRequestCommand | RequestCommand,
+ commandName: OCPPCommandName,
params: RequestParams,
responseTimeoutMs: number
): PendingSendOperation {
// reject an already-answered request while its handler is still running.
chargingStation.requests.delete(messageId)
this.ocppResponseService
- .responseHandler(chargingStation, commandName as RequestCommand, payload, requestPayload)
+ .responseHandler(
+ chargingStation,
+ requireOCPPCommand(RequestCommand, commandName),
+ payload,
+ requestPayload
+ )
.then(() => {
resolve(payload)
return undefined
chargingStation,
messageId,
messagePayload as JsonType,
- commandName,
+ requireOCPPCommand(RequestCommand, commandName),
responseCallback,
errorCallback,
cancelPendingSend,
chargingStation,
messageId,
messagePayload as JsonType,
- commandName,
+ requireOCPPCommand(RequestCommand, commandName),
responseCallback,
errorCallback,
cancelPendingSend,
chargingStation,
messageId,
messagePayload as JsonType,
- commandName,
+ requireOCPPCommand(RequestCommand, commandName),
responseCallback,
errorCallback,
cancelPendingSend,
messageId: string,
messagePayload: JsonType | OCPPError,
messageType: MessageType,
- commandName: IncomingRequestCommand | RequestCommand,
+ commandName: OCPPCommandName,
params?: RequestParams,
cancellationGenerationAtRequestStart?: number
): Promise<ResponseType> {
((chargingStation.inUnknownState() ||
chargingStation.inPendingState() ||
chargingStation.inRejectedState()) &&
- commandName === RequestCommand.BOOT_NOTIFICATION) ||
+ getEnumStringValue(RequestCommand, commandName) === RequestCommand.BOOT_NOTIFICATION) ||
(chargingStation.stationInfo?.ocppStrictCompliance === false &&
chargingStation.inUnknownState()) ||
chargingStation.inAcceptedState() ||
BootReasonEnumType,
OCPP20RequiredVariableName,
OCPP20VendorVariableName,
- SigningMethodEnumType,
} from '../../types/index.js'
import {
resolveInletToOutputEfficiency,
resolveLinePhaseIndex,
resolveMeterValueUnitDivider,
+ resolveMeterValueUnitScale,
resolveRootSeed,
truncateTransactionIntervalValue,
} from '../../charging-station/meter-values/index.js'
type ConfigurationKeyType,
type ConnectorStatus,
CurrentType,
+ type CustomDataType,
ErrorType,
FileType,
IncomingRequestCommand,
+ isOCPP16SampledValue,
+ isOCPP20SampledValue,
type JsonType,
type MeasurandPerPhaseSampledValueTemplates,
type MeasurandValues,
MeterValueMeasurand,
MeterValuePhase,
MeterValueUnit,
+ type OCPP16MeterValue,
OCPP16MeterValueFormat,
+ type OCPP16MeterValueUnit,
OCPP20ComponentName,
+ OCPP20LocationEnumType,
+ OCPP20MeasurandEnumType,
type OCPP20MeterValue,
OCPP20OptionalVariableName,
OCPP20PhaseEnumType,
OCPP20ReadingContextEnumType,
type OCPP20SampledValue,
+ OCPP20UnitEnumType,
+ type OCPP20UnitOfMeasure,
OCPPVersion,
RequestCommand,
type SampledValue,
convertToFloat,
convertToInt,
DCElectricUtils,
+ getEnumStringValue,
getRandomFloatFluctuatedRounded,
getRandomFloatRounded,
handleFileException,
const moduleName = 'OCPPServiceUtils'
+const LINE_PHASES = [MeterValuePhase.L1, MeterValuePhase.L2, MeterValuePhase.L3] as const
+const LINE_TO_LINE_PHASES = [
+ MeterValuePhase.L1_L2,
+ MeterValuePhase.L2_L3,
+ MeterValuePhase.L3_L1,
+] as const
+const LINE_TO_NEUTRAL_PHASES = [
+ MeterValuePhase.L1_N,
+ MeterValuePhase.L2_N,
+ MeterValuePhase.L3_N,
+] as const
+
+const toSnapshotBaselineSample = (sampledValue: SampledValue): OCPP20SampledValue => {
+ if (isOCPP20SampledValue(sampledValue)) return sampledValue
+ const context = getEnumStringValue(OCPP20ReadingContextEnumType, sampledValue.context)
+ const location = getEnumStringValue(OCPP20LocationEnumType, sampledValue.location)
+ const measurand = getEnumStringValue(OCPP20MeasurandEnumType, sampledValue.measurand)
+ const phase = getEnumStringValue(OCPP20PhaseEnumType, sampledValue.phase)
+ const unit = getEnumStringValue(OCPP20UnitEnumType, sampledValue.unit)
+ return {
+ ...(context != null && { context }),
+ ...(location != null && { location }),
+ ...(measurand != null && { measurand }),
+ ...(phase != null && { phase }),
+ ...(unit != null && { unitOfMeasure: { unit } }),
+ value: convertToFloat(sampledValue.value),
+ }
+}
+
+export function assertMeterValueBuilderResult (
+ meterValue: MeterValue,
+ version: OCPPVersion.VERSION_16
+): asserts meterValue is OCPP16MeterValue
+export function assertMeterValueBuilderResult (
+ meterValue: MeterValue,
+ version: OCPPVersion.VERSION_20 | OCPPVersion.VERSION_201
+): asserts meterValue is OCPP20MeterValue
+/**
+ * Validates the protocol representation of an internal MeterValue builder result.
+ * Empty sampled-value arrays are valid omission signals inside builders.
+ * @param meterValue - Internal builder result to validate.
+ * @param version - OCPP version expected by the caller.
+ * @throws {BaseError} When a non-empty result contains a sample from another OCPP version.
+ */
+export function assertMeterValueBuilderResult (meterValue: MeterValue, version: OCPPVersion): void {
+ const matchesVersion =
+ meterValue.sampledValue.length === 0 ||
+ (version === OCPPVersion.VERSION_16
+ ? meterValue.sampledValue.every(isOCPP16SampledValue)
+ : meterValue.sampledValue.every(isOCPP20SampledValue))
+ if (!matchesVersion) {
+ throw new BaseError(
+ `MeterValue builder returned sampled values that do not match OCPP ${version}`
+ )
+ }
+}
+
const isOCPP20FlagEnabled = (
chargingStation: ChargingStation,
component: OCPP20ComponentName,
}
/**
- * Builds an empty MeterValue with no sampled values and the current timestamp.
+ * Builds an empty MeterValue with no sampled values.
+ * @param timestamp - Meter value timestamp; defaults to the current time
* @returns Empty MeterValue object
*/
-export const buildEmptyMeterValue = (): MeterValue => ({
+export const buildEmptyMeterValue = (timestamp = new Date()): MeterValue => ({
sampledValue: [],
- timestamp: new Date(),
+ timestamp,
})
/**
const configuredSigningMethod = getConfigurationKey(
chargingStation,
buildConfigKey(OCPP20ComponentName.FiscalMetering, VendorParametersKey.SigningMethod)
- )?.value as SigningMethodEnumType | undefined
+ )?.value
const prerequisiteResult = validateSigningPrerequisites(
publicKeyHex,
// abort semantics, no `resetStationState` hook — a diagnostic side-effect
// cache freed only by GC.
const warnedInvalidMeasurands = new WeakMap<ChargingStation, Set<string>>()
-const KNOWN_MEASURANDS: ReadonlySet<string> = new Set<string>(Object.values(MeterValueMeasurand))
-
const getOrCreateWarnedMeasurands = (chargingStation: ChargingStation): Set<string> => {
let warned = warnedInvalidMeasurands.get(chargingStation)
if (warned == null) {
if (isEmpty(trimmed)) {
continue
}
- if (KNOWN_MEASURANDS.has(trimmed)) {
- enabled.add(trimmed as MeterValueMeasurand)
+ const measurand = getEnumStringValue(MeterValueMeasurand, trimmed)
+ if (measurand != null) {
+ enabled.add(measurand)
continue
}
const warned = getOrCreateWarnedMeasurands(chargingStation)
return JSON.stringify([
resolved.phase,
resolved.location,
- resolved.unit,
+ ocppVersion === OCPPVersion.VERSION_16
+ ? resolved.unit
+ : (template.unitOfMeasure?.unit ?? resolved.unit),
+ ocppVersion === OCPPVersion.VERSION_16 ? 0 : (template.unitOfMeasure?.multiplier ?? 0),
resolved.context,
ocppVersion === OCPPVersion.VERSION_16
? (template.format ?? OCPP16MeterValueFormat.RAW)
- : canonicalizeCustomData(template.customData),
+ : [
+ canonicalizeCustomData(template.customData),
+ canonicalizeCustomData(template.unitOfMeasure?.customData),
+ ],
])
}
{
...template,
measurand: MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_INTERVAL,
- } as SampledValueTemplate,
+ },
]
})
}
physicalValue /
resolveMeterValueUnitDivider(
MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_INTERVAL,
- template.unit as string | undefined
+ template.unit
)
),
context,
customData: template.customData,
location: identity.location,
measurand: identity.measurand,
- unit: identity.unit,
+ unit: template.unitOfMeasure?.unit ?? identity.unit,
+ unitCustomData: template.unitOfMeasure?.customData,
+ unitMultiplier: template.unitOfMeasure?.multiplier,
})
const family = families.get(key) ?? []
family.push(template)
).map((template): SampledValueTemplate => {
const measurand = template.measurand ?? MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
return evseId === 0 && template.location == null && isElectricalMeasurand(measurand)
- ? ({ ...template, location: MeterValueLocation.INLET } as SampledValueTemplate)
+ ? { ...template, location: MeterValueLocation.INLET }
: template
})
const templatesWithIntervalFallback =
const templatesByIdentity = new Map<string, SampledValueTemplate>()
for (const template of templatesWithIntervalFallback) {
const identity = resolveSampledValueFields(template, 0, context, template.phase)
+ const isOcpp16 = chargingStation.stationInfo?.ocppVersion === OCPPVersion.VERSION_16
const key = JSON.stringify([
- identity.measurand,
identity.phase,
- identity.location,
- identity.unit,
- chargingStation.stationInfo?.ocppVersion === OCPPVersion.VERSION_16
- ? template.format
- : canonicalizeCustomData(template.customData),
+ buildSampledValueFamilyKey({
+ context: identity.context,
+ customData: isOcpp16 ? template.format : template.customData,
+ location: identity.location,
+ measurand: identity.measurand,
+ unit: isOcpp16 ? identity.unit : (template.unitOfMeasure?.unit ?? identity.unit),
+ unitCustomData: isOcpp16 ? undefined : template.unitOfMeasure?.customData,
+ unitMultiplier: isOcpp16 ? undefined : template.unitOfMeasure?.multiplier,
+ }),
])
if (!templatesByIdentity.has(key)) templatesByIdentity.set(key, template)
}
? [...phasedPowerByLine.values()].reduce(
(total, sample) =>
total +
- sample.value * resolveMeterValueUnitDivider(measurand, sample.unitOfMeasure?.unit),
+ sample.value *
+ resolveMeterValueUnitScale(
+ measurand,
+ sample.unitOfMeasure?.unit,
+ sample.unitOfMeasure?.multiplier
+ ),
0
)
: undefined
} else if (measurand === MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER) {
if (evseId !== 0 && source != null && preferBaseline) {
rawValue =
- source.value * resolveMeterValueUnitDivider(measurand, source.unitOfMeasure?.unit)
+ source.value *
+ resolveMeterValueUnitScale(
+ measurand,
+ source.unitOfMeasure?.unit,
+ source.unitOfMeasure?.multiplier
+ )
} else if (phaseFamily === 'Aggregate') {
rawValue = energyRegister
} else if (phaseFamily === 'Line') {
) {
continue
} else if (source != null && preferBaseline) {
- rawValue = source.value * resolveMeterValueUnitDivider(measurand, source.unitOfMeasure?.unit)
+ rawValue =
+ source.value *
+ resolveMeterValueUnitScale(
+ measurand,
+ source.unitOfMeasure?.unit,
+ source.unitOfMeasure?.multiplier
+ )
if (
(measurand === MeterValueMeasurand.POWER_ACTIVE_IMPORT ||
measurand === MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_INTERVAL) &&
if (idle) {
rawValue = 0
} else {
- const divider = resolveMeterValueUnitDivider(measurand, template.unit as string | undefined)
+ const divider = resolveMeterValueUnitDivider(measurand, template.unit)
const maximumValue =
measurand === MeterValueMeasurand.POWER_ACTIVE_IMPORT
? chargingStation.getConnectorMaximumAvailablePower(connectorId, evseId) /
) {
rawValue = randomInt(template.minimumValue ?? 0, Constants.SOC_MAXIMUM_PERCENT + 1)
} else if (source != null) {
- rawValue = source.value * resolveMeterValueUnitDivider(measurand, source.unitOfMeasure?.unit)
+ rawValue =
+ source.value *
+ resolveMeterValueUnitScale(
+ measurand,
+ source.unitOfMeasure?.unit,
+ source.unitOfMeasure?.multiplier
+ )
if (
(measurand === MeterValueMeasurand.POWER_ACTIVE_IMPORT ||
measurand === MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_INTERVAL) &&
getRandomFloatFluctuatedRounded(
convertToFloat(template.value),
template.fluctuationPercent ?? Constants.DEFAULT_FLUCTUATION_PERCENT
- ) * resolveMeterValueUnitDivider(measurand, template.unit as string | undefined)
+ ) * resolveMeterValueUnitDivider(measurand, template.unit)
} else if (measurand === MeterValueMeasurand.VOLTAGE) {
const nominal =
phaseFamily === 'LineToLine'
rawValue,
preferBaseline
)
- const unitValue =
- physicalValue / resolveMeterValueUnitDivider(measurand, template.unit as string | undefined)
+ const unitValue = physicalValue / resolveMeterValueUnitDivider(measurand, template.unit)
const value =
measurand === MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_INTERVAL
? truncateTransactionIntervalValue(unitValue)
}
return applyClockAlignedVoltageControls(
chargingStation,
- expanded as OCPP20SampledValue[],
+ expanded,
buildVersionedSampledValue,
context
)
}
+const getSampledValueUnit = (
+ sampledValue: SampledValue
+): NonNullable<OCPP20UnitOfMeasure['unit']> | OCPP16MeterValueUnit | undefined =>
+ isOCPP20SampledValue(sampledValue) ? sampledValue.unitOfMeasure?.unit : sampledValue.unit
+
+const getSampledValueCustomData = (sampledValue: SampledValue): CustomDataType | undefined =>
+ isOCPP20SampledValue(sampledValue) ? sampledValue.customData : undefined
+
+const getSampledValueFormat = (sampledValue: SampledValue): OCPP16MeterValueFormat | undefined =>
+ isOCPP20SampledValue(sampledValue) ? undefined : sampledValue.format
+
const applyClockAlignedVoltageControls = (
chargingStation: ChargingStation,
- sampledValues: OCPP20SampledValue[],
+ sampledValues: SampledValue[],
buildVersionedSampledValue: BuildVersionedSampledValue,
context: MeterValueContext | undefined
-): OCPP20SampledValue[] => {
+): SampledValue[] => {
if (
chargingStation.getNumberOfPhases() !== 3 ||
chargingStation.stationInfo?.currentOutType !== CurrentType.AC
const configuredPhaseSamples = sampledValues.filter(
sample => sample.measurand === MeterValueMeasurand.VOLTAGE && sample.phase != null
)
- const automaticSamples: OCPP20SampledValue[] = []
+ const automaticSamples: SampledValue[] = []
for (const aggregateVoltage of aggregateVoltages) {
const configuredPhases = new Set(
configuredPhaseSamples
.filter(
sample =>
sample.context === aggregateVoltage.context &&
- canonicalizeCustomData(sample.customData) ===
- canonicalizeCustomData(aggregateVoltage.customData) &&
- sample.format === aggregateVoltage.format &&
+ canonicalizeCustomData(getSampledValueCustomData(sample)) ===
+ canonicalizeCustomData(getSampledValueCustomData(aggregateVoltage)) &&
+ getSampledValueFormat(sample) === getSampledValueFormat(aggregateVoltage) &&
sample.location === aggregateVoltage.location &&
- sample.unitOfMeasure?.unit === aggregateVoltage.unitOfMeasure?.unit
+ getSampledValueUnit(sample) === getSampledValueUnit(aggregateVoltage)
)
.map(sample => sample.phase)
)
- const template = {
- ...aggregateVoltage,
- unit: aggregateVoltage.unitOfMeasure?.unit,
- } as SampledValueTemplate
- const addPhase = (phase: OCPP20PhaseEnumType, value: number): void => {
+ const template: SampledValueTemplate = {
+ ...(isOCPP20SampledValue(aggregateVoltage)
+ ? {
+ customData: aggregateVoltage.customData,
+ unit: getSampledValueUnit(aggregateVoltage),
+ unitOfMeasure: aggregateVoltage.unitOfMeasure,
+ }
+ : { format: aggregateVoltage.format, unit: aggregateVoltage.unit }),
+ context: aggregateVoltage.context,
+ location: aggregateVoltage.location,
+ measurand: aggregateVoltage.measurand,
+ phase: aggregateVoltage.phase,
+ }
+ const addPhase = (phase: MeterValuePhase, value: number): void => {
if (!configuredPhases.has(phase)) {
automaticSamples.push(
- buildVersionedSampledValue(
- template,
- roundTo(value, 2),
- context,
- phase
- ) as OCPP20SampledValue
+ buildVersionedSampledValue(template, roundTo(value, 2), context, phase)
)
}
}
- addPhase(OCPP20PhaseEnumType.L1_N, aggregateVoltage.value)
- addPhase(OCPP20PhaseEnumType.L2_N, aggregateVoltage.value)
- addPhase(OCPP20PhaseEnumType.L3_N, aggregateVoltage.value)
+ const aggregateValue =
+ convertToFloat(aggregateVoltage.value) *
+ resolveMeterValueUnitScale(
+ MeterValueMeasurand.VOLTAGE,
+ isOCPP20SampledValue(aggregateVoltage)
+ ? aggregateVoltage.unitOfMeasure?.unit
+ : aggregateVoltage.unit,
+ isOCPP20SampledValue(aggregateVoltage)
+ ? aggregateVoltage.unitOfMeasure?.multiplier
+ : undefined
+ )
+ addPhase(MeterValuePhase.L1_N, aggregateValue)
+ addPhase(MeterValuePhase.L2_N, aggregateValue)
+ addPhase(MeterValuePhase.L3_N, aggregateValue)
if (chargingStation.stationInfo.phaseLineToLineVoltageMeterValues === true) {
- const lineToLineVoltage = aggregateVoltage.value * Math.sqrt(3)
- addPhase(OCPP20PhaseEnumType.L1_L2, lineToLineVoltage)
- addPhase(OCPP20PhaseEnumType.L2_L3, lineToLineVoltage)
- addPhase(OCPP20PhaseEnumType.L3_L1, lineToLineVoltage)
+ const lineToLineVoltage = aggregateValue * Math.sqrt(3)
+ addPhase(MeterValuePhase.L1_L2, lineToLineVoltage)
+ addPhase(MeterValuePhase.L2_L3, lineToLineVoltage)
+ addPhase(MeterValuePhase.L3_L1, lineToLineVoltage)
}
}
const retainedSamples =
RequestCommand.METER_VALUES
)
}
- return buildIdentifiedMeterValue(
+ const meterValue = buildIdentifiedMeterValue(
chargingStation,
{ ...identity, commitState: identity.commitState ?? true, snapshot: true },
interval,
context,
false,
OCPP20ComponentName.AlignedDataCtrlr
- ) as OCPP20MeterValue
+ )
+ assertMeterValueBuilderResult(meterValue, OCPPVersion.VERSION_201)
+ return meterValue
}
const buildIdentifiedMeterValue = (
if (snapshot && identity.sampledValueBaseline != null) {
const timestamp = identity.timestamp ?? new Date()
if (signingConfig != null) signingConfig.timestamp = timestamp
- return {
- sampledValue: expandClockAlignedSnapshotSamples(
- chargingStation,
- connectorId,
- evseId,
- identity.sampledValueBaseline,
- buildSignedVersionedSampledValue,
- measurandsKey,
- context,
- registerValuesWithoutPhases,
- identity.idle ?? true,
- identity.transactionId != null,
- identity.energyRegisterWhOverride,
- true,
- identity.sampledValueTemplates
- ),
- timestamp,
- } as OCPP20MeterValue
+ const sampledValue = expandClockAlignedSnapshotSamples(
+ chargingStation,
+ connectorId,
+ evseId,
+ identity.sampledValueBaseline,
+ buildSignedVersionedSampledValue,
+ measurandsKey,
+ context,
+ registerValuesWithoutPhases,
+ identity.idle ?? true,
+ identity.transactionId != null,
+ identity.energyRegisterWhOverride,
+ true,
+ identity.sampledValueTemplates
+ )
+ if (isOCPP20x(chargingStation.stationInfo?.ocppVersion)) {
+ if (!sampledValue.every(isOCPP20SampledValue)) {
+ throw new BaseError('Clock-aligned snapshot contains OCPP 1.6 SampledValues for OCPP 2.0.x')
+ }
+ return { sampledValue, timestamp }
+ }
+ if (!sampledValue.every(isOCPP16SampledValue)) {
+ throw new BaseError('Clock-aligned snapshot contains OCPP 2.0.x SampledValues for OCPP 1.6')
+ }
+ return { sampledValue, timestamp }
}
const connectorStatus = chargingStation.getConnectorStatus(connectorId, evseId)
const transactionBegin = context === MeterValueContext.TRANSACTION_BEGIN
coherentMeterValue.timestamp
}
if (snapshot) {
- const coherentOcpp20MeterValue = coherentMeterValue as OCPP20MeterValue
- coherentOcpp20MeterValue.sampledValue = applyClockAlignedVoltageControls(
+ const version = chargingStation.stationInfo?.ocppVersion
+ const controlledSampledValues = applyClockAlignedVoltageControls(
chargingStation,
- coherentOcpp20MeterValue.sampledValue,
+ coherentMeterValue.sampledValue,
buildSignedVersionedSampledValue,
context
)
+ const controlledMeterValue = {
+ ...coherentMeterValue,
+ sampledValue: controlledSampledValues,
+ } as MeterValue
+ if (version === OCPPVersion.VERSION_16) {
+ assertMeterValueBuilderResult(controlledMeterValue, OCPPVersion.VERSION_16)
+ } else {
+ assertMeterValueBuilderResult(controlledMeterValue, OCPPVersion.VERSION_201)
+ }
+ coherentMeterValue.sampledValue = controlledMeterValue.sampledValue
}
// Only transactional builds may flip the one-time public-key flag. A
// coherent session always carries a transactionId, but guard explicitly to
connectorId,
meterValue,
voltageMeasurand,
- `L${phase.toString()}-N` as MeterValuePhase,
+ LINE_TO_NEUTRAL_PHASES[phase - 1],
chargingStation.getVoltageOut(),
buildVersionedSampledValue,
measurandsKey,
snapshot
)
if (chargingStation.stationInfo?.phaseLineToLineVoltageMeterValues === true) {
- const nextPhase =
- (phase + 1) % chargingStation.getNumberOfPhases() !== 0
- ? ((phase + 1) % chargingStation.getNumberOfPhases()).toString()
- : chargingStation.getNumberOfPhases().toString()
- const lineToLineLabel = `L${phase.toString()}-L${nextPhase}` as MeterValuePhase
+ const lineToLineLabel = LINE_TO_LINE_PHASES[phase - 1]
// `V_LL = sqrt(3) * V_LN` in a balanced 3-phase Y system; the
// sqrt(3) factor comes from the 30-degree phase separation, not
// from the phase count itself. Emitting L-L values makes physical
`L${phase.toString()}` as keyof MeasurandPerPhaseSampledValueTemplates
]
if (phaseTemplate != null) {
- const phaseValue = `L${phase.toString()}-N` as MeterValuePhase
+ const phaseValue = LINE_TO_NEUTRAL_PHASES[phase - 1]
const phasePowerValue =
powerMeasurand.values[`L${phase.toString()}` as keyof MeasurandValues]
meterValue.sampledValue.push(
chargingStation.getNumberOfPhases() === 3 && phase <= chargingStation.getNumberOfPhases();
phase++
) {
- const phaseValue = `L${phase.toString()}` as MeterValuePhase
+ const phaseValue = LINE_PHASES[phase - 1]
meterValue.sampledValue.push(
buildVersionedSampledValue(
currentMeasurand.perPhaseTemplates[
chargingStation,
connectorId,
evseId,
- meterValue.sampledValue as OCPP20SampledValue[],
+ meterValue.sampledValue.map(toSnapshotBaselineSample),
buildSignedVersionedSampledValue,
measurandsKey,
context,
location: MeterValueLocation | undefined
measurand: MeterValueMeasurand
phase: MeterValuePhase | undefined
- unit: MeterValueUnit | undefined
+ unit: string | undefined
value: number
} => {
const sampledValueMeasurand =
location: sampledValueTemplate.location ?? getMeasurandDefaultLocation(sampledValueMeasurand),
measurand: sampledValueMeasurand,
phase: phase ?? sampledValueTemplate.phase,
- unit:
- (sampledValueTemplate.unit as MeterValueUnit | undefined) ??
- getMeasurandDefaultUnit(sampledValueMeasurand),
+ unit: sampledValueTemplate.unit ?? getMeasurandDefaultUnit(sampledValueMeasurand),
value,
}
}
type SampledValue,
SigningMethodEnumType,
} from '../../types/index.js'
+import { getEnumStringValue } from '../../utils/index.js'
import { getErrorMessage, isJsonObject, isNotEmptyString, logger } from '../../utils/index.js'
export const getOCPP16SignedMeterValuePublicKey = (
export const validateSigningPrerequisites = (
publicKeyHex: string | undefined,
- configuredSigningMethod: SigningMethodEnumType | undefined
+ configuredSigningMethod: string | undefined
): SigningPrerequisiteResult | SigningPrerequisiteSuccess => {
+ const signingMethod = getEnumStringValue(SigningMethodEnumType, configuredSigningMethod)
+ if (configuredSigningMethod !== undefined && signingMethod == null) {
+ return {
+ enabled: false,
+ reason: `SigningMethod '${configuredSigningMethod}' is invalid`,
+ }
+ }
+
if (!isNotEmptyString(publicKeyHex)) {
return { enabled: false, reason: 'Public key is not configured' }
}
}
}
- if (configuredSigningMethod != null && configuredSigningMethod !== derivedMethod) {
+ if (signingMethod != null && signingMethod !== derivedMethod) {
return {
enabled: false,
reason:
- `SigningMethod mismatch: configured '${configuredSigningMethod}' ` +
+ `SigningMethod mismatch: configured '${signingMethod}' ` +
`but public key uses '${derivedMethod}'`,
}
}
- return { enabled: true, signingMethod: configuredSigningMethod ?? derivedMethod }
+ return { enabled: true, signingMethod: signingMethod ?? derivedMethod }
}
-const PUBLIC_KEY_WITH_SIGNED_METER_VALUE_VALUES = new Set<string>(
- Object.values(PublicKeyWithSignedMeterValueEnumType)
-)
-
export const parsePublicKeyWithSignedMeterValue = (
value: string | undefined
): PublicKeyWithSignedMeterValueEnumType =>
- value != null && PUBLIC_KEY_WITH_SIGNED_METER_VALUE_VALUES.has(value)
- ? (value as PublicKeyWithSignedMeterValueEnumType)
- : PublicKeyWithSignedMeterValueEnumType.Never
+ getEnumStringValue(PublicKeyWithSignedMeterValueEnumType, value) ??
+ PublicKeyWithSignedMeterValueEnumType.Never
export const shouldIncludePublicKey = (
config: PublicKeyWithSignedMeterValueEnumType,
import {
ApplicationProtocolVersion,
MapStringifyFormat,
- type ProcedureName,
- type Protocol,
+ ProcedureName,
+ Protocol,
type ProtocolRequest,
type ProtocolResponse,
- type ProtocolVersion,
+ ProtocolVersion,
type RequestPayload,
ResponseStatus,
type UIServerConfiguration,
type UUIDv4,
} from '../../types/index.js'
-import { generateUUID, getErrorMessage, JSONStringify, logger } from '../../utils/index.js'
+import {
+ generateUUID,
+ getEnumStringValue,
+ getErrorMessage,
+ JSONStringify,
+ logger,
+} from '../../utils/index.js'
import { AbstractUIServer } from './AbstractUIServer.js'
import {
DEFAULT_COMPRESSION_THRESHOLD_BYTES,
`Malformed URL path: '${pathname}' (expected /ui/:version/:procedureName)`
)
}
- const [protocol, version, procedureName] = parts as [Protocol, ProtocolVersion, ProcedureName]
- const fullProtocol = `${protocol}${version}`
- if (!isProtocolAndVersionSupported(fullProtocol)) {
+ const [rawProtocol, rawVersion, rawProcedureName] = parts
+ const protocol = getEnumStringValue(Protocol, rawProtocol)
+ const version = getEnumStringValue(ProtocolVersion, rawVersion)
+ const procedureName = getEnumStringValue(ProcedureName, rawProcedureName)
+ const fullProtocol = `${rawProtocol}${rawVersion}`
+ if (protocol == null || version == null || !isProtocolAndVersionSupported(fullProtocol)) {
throw new BaseError(`Unsupported UI protocol version: '${fullProtocol}'`)
}
+ if (procedureName == null) {
+ throw new BaseError(`Unsupported UI procedure: '${rawProcedureName}'`)
+ }
this.registerProtocolVersionUIService(version)
req.on('error', error => {
`${this.logPrefix(moduleName, 'requestListener')} Handle HTTP request error:`,
error
)
- this.sendResponse(this.buildProtocolResponse(uuid, { status: ResponseStatus.FAILURE }))
+ this.sendResponse(
+ this.buildProtocolResponse(uuid, {
+ errorMessage: getErrorMessage(error),
+ status: ResponseStatus.FAILURE,
+ })
+ )
}
}
import type { IncomingMessage } from 'node:http'
import { Protocol, ProtocolVersion } from '../../types/index.js'
-import { isEmpty, logger, logPrefix } from '../../utils/index.js'
+import { getEnumStringValue, isEmpty, logger, logPrefix } from '../../utils/index.js'
export enum HttpMethod {
DELETE = 'DELETE',
if (protocolAndVersion == null) {
return false
}
- const [protocol, version] = protocolAndVersion
- return (
- Object.values(Protocol).includes(protocol) && Object.values(ProtocolVersion).includes(version)
- )
+ return true
}
export const getProtocolAndVersion = (
return undefined
}
const protocolIndex = protocolStr.indexOf(Protocol.UI)
- const protocol = protocolStr.substring(protocolIndex, protocolIndex + Protocol.UI.length)
- const version = protocolStr.substring(protocolIndex + Protocol.UI.length)
- if (isEmpty(protocol) || isEmpty(version)) {
+ const protocol = getEnumStringValue(
+ Protocol,
+ protocolStr.substring(protocolIndex, protocolIndex + Protocol.UI.length)
+ )
+ const version = getEnumStringValue(
+ ProtocolVersion,
+ protocolStr.substring(protocolIndex + Protocol.UI.length)
+ )
+ if (protocol == null || version == null) {
return undefined
}
- return [protocol, version] as [Protocol, ProtocolVersion]
+ return [protocol, version]
}
// Partial Copyright Jerome Benoit. 2021-2025. All Rights Reserved.
-import type { ErrorType, IncomingRequestCommand, JsonType, RequestCommand } from '../types/index.js'
+import type { ErrorType, JsonType, OCPPCommandName } from '../types/index.js'
// Direct path: the `ocpp/index.js` barrel pulls OCPP20ServiceUtils which transitively imports OCPPError, causing a TDZ cycle.
import { OCPPConstants } from '../charging-station/ocpp/OCPPConstants.js'
export class OCPPError extends BaseError {
code: ErrorType
- command: IncomingRequestCommand | RequestCommand
+ command: OCPPCommandName
details?: JsonType
public override readonly name = 'OCPPError' as const
- constructor (
- code: ErrorType,
- message: string,
- command?: IncomingRequestCommand | RequestCommand,
- details?: JsonType
- ) {
+ constructor (code: ErrorType, message: string, command?: OCPPCommandName, details?: JsonType) {
super(message)
this.code = code
import { BaseError } from '../exception/index.js'
import {
ConfigurationSection,
- type IncomingRequestCommand,
type LogConfiguration,
MapStringifyFormat,
MessageType,
- type RequestCommand,
type Statistics,
type StatisticsData,
type StorageConfiguration,
return logPrefix(' Performance statistics')
}
- public addRequestStatistic (
- command: IncomingRequestCommand | RequestCommand,
- messageType: MessageType
- ): void {
+ public addRequestStatistic (command: string, messageType: MessageType): void {
switch (messageType) {
case MessageType.CALL_ERROR_MESSAGE: {
const commandStatisticsData = this.statistics.statisticsData.get(command)
...this.statistics,
statisticsData: JSON.parse(
JSONStringify(this.statistics.statisticsData, undefined, MapStringifyFormat.object)
- ) as Map<IncomingRequestCommand | RequestCommand | string, StatisticsData>,
+ ) as Map<string, StatisticsData>,
})
}
-import type { SampledValue } from './ocpp/MeterValues.js'
+import type { JsonObject } from './JsonType.js'
+import type { OCPP16MeterValueFormat, OCPP16SampledValue } from './ocpp/1.6/MeterValues.js'
+import type { CustomDataType } from './ocpp/2.0/Common.js'
+import type {
+ OCPP20SampledValue,
+ OCPP20SignedMeterValue,
+ OCPP20UnitOfMeasure,
+} from './ocpp/2.0/MeterValues.js'
+import type {
+ MeterValueContext,
+ MeterValueLocation,
+ MeterValueMeasurand,
+ MeterValuePhase,
+} from './ocpp/MeterValues.js'
export interface MeasurandPerPhaseSampledValueTemplates {
L1?: SampledValueTemplate
L3?: SampledValueTemplate
}
-export type SampledValueTemplate = SampledValue & {
+export interface SampledValueTemplate extends JsonObject {
+ context?: MeterValueContext
+ customData?: CustomDataType
fluctuationPercent?: number
+ format?: OCPP16MeterValueFormat
+ location?: MeterValueLocation
+ measurand?: MeterValueMeasurand
minimumValue?: number
+ phase?: MeterValuePhase
+ signedMeterValue?: OCPP20SignedMeterValue
+ unit?: NonNullable<OCPP16SampledValue['unit'] | OCPP20UnitOfMeasure['unit']>
+ unitOfMeasure?: OCPP20UnitOfMeasure
+ value?: OCPP16SampledValue['value'] | OCPP20SampledValue['value']
}
import type { CircularBuffer } from 'mnemonist'
import type { WorkerData } from '../worker/index.js'
-import type { IncomingRequestCommand, RequestCommand } from './ocpp/Requests.js'
export interface Statistics extends WorkerData {
createdAt: Date
id: string
name: string
- statisticsData: Map<IncomingRequestCommand | RequestCommand | string, StatisticsData>
+ statisticsData: Map<string, StatisticsData>
updatedAt?: Date
uri: string
}
ChargingRateUnitType,
type ChargingSchedule,
type ChargingSchedulePeriod,
+ isOCPP16ChargingProfile,
RecurrencyKindType,
} from './ocpp/ChargingProfile.js'
export { type GenericResponse, GenericStatus, RegistrationStatusEnumType } from './ocpp/Common.js'
export { ErrorType } from './ocpp/ErrorType.js'
export { MessageType } from './ocpp/MessageType.js'
export {
+ isOCPP16MeterValue,
+ isOCPP16SampledValue,
+ isOCPP20MeterValue,
+ isOCPP20SampledValue,
type MeterValue,
MeterValueContext,
MeterValueLocation,
type IncomingRequestHandler,
MessageTrigger,
type MeterValuesRequest,
+ type OCPPCommand,
+ type OCPPCommandName,
type OutgoingRequest,
type PendingRequestCancellationCallback,
type PendingRequestCancellationOptions,
type ResponseType,
type StatusNotificationOptions,
type StatusNotificationRequest,
+ UNKNOWN_OCPP_COMMAND,
} from './ocpp/Requests.js'
export {
type Reservation,
KILO_WATT = 'kW',
KILO_WATT_HOUR = 'kWh',
PERCENT = 'Percent',
+ TEMP_CELCIUS = 'Celcius',
TEMP_CELSIUS = 'Celsius',
TEMP_FAHRENHEIT = 'Fahrenheit',
TEMP_KELVIN = 'K',
export type ChargingProfile = OCPP16ChargingProfile | OCPP20ChargingProfileType
+export const isOCPP16ChargingProfile = (
+ chargingProfile: ChargingProfile
+): chargingProfile is OCPP16ChargingProfile =>
+ typeof chargingProfile.chargingProfileId === 'number' &&
+ !Array.isArray(chargingProfile.chargingSchedule)
+
export type ChargingSchedule = OCPP16ChargingSchedule | OCPP20ChargingScheduleType
export type ChargingSchedulePeriod = OCPP16ChargingSchedulePeriod | OCPP20ChargingSchedulePeriodType
export type MeterValue = OCPP16MeterValue | OCPP20MeterValue
+export const isOCPP16SampledValue = (
+ sampledValue: OCPP16SampledValue | OCPP20SampledValue
+): sampledValue is OCPP16SampledValue => typeof sampledValue.value === 'string'
+
+export const isOCPP20SampledValue = (
+ sampledValue: OCPP16SampledValue | OCPP20SampledValue
+): sampledValue is OCPP20SampledValue => typeof sampledValue.value === 'number'
+
+export const isOCPP16MeterValue = (meterValue: MeterValue): meterValue is OCPP16MeterValue =>
+ meterValue.sampledValue.length === 0 || meterValue.sampledValue.every(isOCPP16SampledValue)
+
+export const isOCPP20MeterValue = (meterValue: MeterValue): meterValue is OCPP20MeterValue =>
+ meterValue.sampledValue.length > 0 && meterValue.sampledValue.every(isOCPP20SampledValue)
+
export const MeterValueUnit = {
...OCPP16MeterValueUnit,
...OCPP20UnitEnumType,
...OCPP16RequestCommand,
...OCPP20RequestCommand,
} as const
+export type OCPPCommand = IncomingRequestCommand | RequestCommand
+
+export const UNKNOWN_OCPP_COMMAND = 'unknown OCPP command'
+export type OCPPCommandName = `${OCPPCommand}` | typeof UNKNOWN_OCPP_COMMAND
+
// eslint-disable-next-line @typescript-eslint/no-redeclare
export type RequestCommand = OCPP16RequestCommand | OCPP20RequestCommand
--- /dev/null
+export const getEnumStringValue = <T extends string>(
+ enumObject: Readonly<Record<string, T>>,
+ value: unknown
+): T | undefined => {
+ for (const key in enumObject) {
+ if (Object.hasOwn(enumObject, key) && enumObject[key] === value) {
+ return enumObject[key]
+ }
+ }
+}
IncomingRequestCommand,
JsonType,
MessageType,
- RequestCommand,
+ OCPPCommandName,
} from '../types/index.js'
import { logger } from './Logger.js'
export const handleSendMessageError = (
chargingStation: ChargingStation,
- commandName: IncomingRequestCommand | RequestCommand,
+ commandName: OCPPCommandName,
messageType: MessageType,
error: Error,
params?: HandleErrorParams<EmptyObject>
export { ConfigurationValidationError, validateConfiguration } from './ConfigurationValidation.js'
export { Constants } from './Constants.js'
export { ACElectricUtils, DCElectricUtils } from './ElectricUtils.js'
+export { getEnumStringValue } from './EnumUtils.js'
export {
ensureError,
getErrorMessage,
import { standardCleanup, withMockTimers } from '../helpers/TestLifecycleHelpers.js'
import { TEST_HEARTBEAT_INTERVAL_MS, TEST_ONE_HOUR_MS } from './ChargingStationTestConstants.js'
import { cleanupChargingStation, createMockChargingStation } from './helpers/StationHelpers.js'
+import { WebSocketReadyState } from './mocks/MockWebSocket.js'
await describe('ChargingStation Configuration Management', async () => {
// ===== B02/B03 BOOT NOTIFICATION BEHAVIOR TESTS =====
assert.strictEqual(station.isWebSocketConnectionOpened(), true)
// Act - change ready state to CLOSED
- mocks.webSocket.readyState = 3 // WebSocketReadyState.CLOSED
+ mocks.webSocket.readyState = WebSocketReadyState.CLOSED
// Assert
assert.strictEqual(station.isWebSocketConnectionOpened(), false)
const mocks = result.mocks
// Act
- mocks.webSocket.readyState = 0 // WebSocketReadyState.CONNECTING
+ mocks.webSocket.readyState = WebSocketReadyState.CONNECTING
// Assert
assert.strictEqual(station.isWebSocketConnectionOpened(), false)
const mocks = result.mocks
// Act
- mocks.webSocket.readyState = 2 // WebSocketReadyState.CLOSING
+ mocks.webSocket.readyState = WebSocketReadyState.CLOSING
// Assert
assert.strictEqual(station.isWebSocketConnectionOpened(), false)
let openEventFired = false
// First close the connection to test opening
- mocks.webSocket.readyState = 3 // CLOSED
+ mocks.webSocket.readyState = WebSocketReadyState.CLOSED
// Set up listener
mocks.webSocket.on('open', () => {
// Assert
assert.strictEqual(openEventFired, true)
- assert.strictEqual(mocks.webSocket.readyState, 1) // WebSocketReadyState.OPEN
+ assert.strictEqual(mocks.webSocket.readyState, WebSocketReadyState.OPEN)
})
await it('should emit close event and set readyState via simulateClose()', () => {
// Assert
assert.strictEqual(closeCode, 1001)
- assert.strictEqual(mocks.webSocket.readyState, 3) // WebSocketReadyState.CLOSED
+ assert.strictEqual(mocks.webSocket.readyState, WebSocketReadyState.CLOSED)
})
await it('should emit error event via simulateError()', () => {
const mocks = result.mocks
// Close the WebSocket
- mocks.webSocket.readyState = 3 // WebSocketReadyState.CLOSED
+ mocks.webSocket.readyState = WebSocketReadyState.CLOSED
// Act & Assert
assert.throws(
import { OCPP20ResponseService } from '../../src/charging-station/ocpp/2.0/OCPP20ResponseService.js'
import { OCPP20ServiceUtils } from '../../src/charging-station/ocpp/2.0/OCPP20ServiceUtils.js'
import { stopRunningTransactions } from '../../src/charging-station/ocpp/OCPPServiceOperations.js'
+import { OCPPError } from '../../src/exception/index.js'
import {
AvailabilityType,
+ ErrorType,
MessageType,
OCPP16AuthorizationStatus,
OCPP16ChargePointStatus,
OCPP20TriggerReasonEnumType,
OCPPVersion,
} from '../../src/types/index.js'
-import { AsyncLock, AsyncLockType, Constants } from '../../src/utils/index.js'
+import { AsyncLock, AsyncLockType, Constants, logger } from '../../src/utils/index.js'
import {
flushMicrotasks,
setupConnectorWithTransaction,
assert.strictEqual(closeConnection.mock.callCount(), 1)
})
+ await it('should reject an unknown OCPP command before typed dispatch', async () => {
+ const sendError = mock.fn((..._args: unknown[]): Promise<void> => Promise.resolve())
+ const { station: activeStation } = createMockChargingStation({
+ ocppRequestService: { sendError },
+ ocppVersion: OCPPVersion.VERSION_20,
+ started: true,
+ })
+ station = activeStation
+ const stationLifecycle = activeStation as unknown as {
+ onMessage: (
+ data: string,
+ sourceConnection?: NonNullable<typeof activeStation.wsConnection>
+ ) => Promise<void>
+ }
+ stationLifecycle.onMessage = (
+ ChargingStation.prototype as unknown as {
+ onMessage: typeof stationLifecycle.onMessage
+ }
+ ).onMessage
+ const sourceConnection = activeStation.wsConnection
+ assert.ok(sourceConnection != null)
+
+ await stationLifecycle.onMessage.call(
+ activeStation,
+ JSON.stringify([MessageType.CALL_MESSAGE, 'unknown-command', 'UnknownCommand', {}]),
+ sourceConnection
+ )
+
+ assert.strictEqual(sendError.mock.callCount(), 1)
+ const error: unknown = sendError.mock.calls[0]?.arguments[2]
+ assert.ok(error instanceof OCPPError)
+ assert.strictEqual(error.code, ErrorType.NOT_IMPLEMENTED)
+ assert.deepStrictEqual(error.details, { command: 'UnknownCommand' })
+ })
+
+ await it('should report the parsed unknown OCPP message type', async t => {
+ const { station: activeStation } = createMockChargingStation({
+ ocppVersion: OCPPVersion.VERSION_20,
+ started: true,
+ })
+ station = activeStation
+ const errorMock = t.mock.method(logger, 'error', () => undefined)
+ const stationLifecycle = activeStation as unknown as {
+ onMessage: (
+ data: string,
+ sourceConnection?: NonNullable<typeof activeStation.wsConnection>
+ ) => Promise<void>
+ }
+ stationLifecycle.onMessage = (
+ ChargingStation.prototype as unknown as {
+ onMessage: typeof stationLifecycle.onMessage
+ }
+ ).onMessage
+ const sourceConnection = activeStation.wsConnection
+ assert.ok(sourceConnection != null)
+
+ await stationLifecycle.onMessage.call(
+ activeStation,
+ JSON.stringify([99, 'unknown-message-type', {}]),
+ sourceConnection
+ )
+
+ assert.ok(
+ errorMock.mock.calls.some(call => {
+ const loggedMessage: unknown = call.arguments[0]
+ return (
+ typeof loggedMessage === 'string' && loggedMessage.includes('Wrong message type 99')
+ )
+ })
+ )
+ })
+
await it('should drop a late CALL at the websocket boundary without buffering and accepts it after restart', async () => {
const transactionId = '00000000-0000-4000-8000-000000000027'
const requestHandler = mock.fn((...args: unknown[]): Promise<Record<string, never>> => {
import { standardCleanup } from '../helpers/TestLifecycleHelpers.js'
import { TEST_HEARTBEAT_INTERVAL_MS } from './ChargingStationTestConstants.js'
import { cleanupChargingStation, createMockChargingStation } from './helpers/StationHelpers.js'
+import { WebSocketReadyState } from './mocks/MockWebSocket.js'
await describe('ChargingStation Resilience', async () => {
await describe('Error Recovery and Resilience', async () => {
mocks.webSocket.simulateClose(1006, 'Connection lost')
// Assert - WebSocket should be in CLOSED state
- assert.strictEqual(mocks.webSocket.readyState, 3) // CLOSED
+ assert.strictEqual(mocks.webSocket.readyState, WebSocketReadyState.CLOSED)
})
await it('should track connection retry count', () => {
// Assert - Station should remain in valid state
assert.strictEqual(station.getNumberOfConnectors() > 0, true)
- assert.strictEqual(mocks.webSocket.readyState, 3) // CLOSED
+ assert.strictEqual(mocks.webSocket.readyState, WebSocketReadyState.CLOSED)
})
await it('should handle boot notification rejected state', () => {
const testMessage = '[2,"test-msg-1","BootNotification",{}]'
// Ensure WebSocket is closed
- mocks.webSocket.readyState = 3 // CLOSED
+ mocks.webSocket.readyState = WebSocketReadyState.CLOSED
// Act - Buffer a message
station.bufferMessage(testMessage)
const testMessage = '[2,"test-msg-2","Heartbeat",{}]'
// Ensure WebSocket is open
- mocks.webSocket.readyState = 1 // OPEN
+ mocks.webSocket.readyState = WebSocketReadyState.OPEN
mocks.webSocket.simulateOpen()
// Act - Send message
const msg3 = '[2,"msg-3","StatusNotification",{}]'
// Simulate offline: close the connection
- mocks.webSocket.readyState = 3 // CLOSED
+ mocks.webSocket.readyState = WebSocketReadyState.CLOSED
// Act - Buffer multiple messages
station.bufferMessage(msg1)
'[2,"m5","Cmd5",{}]',
]
- mocks.webSocket.readyState = 3 // CLOSED
+ mocks.webSocket.readyState = WebSocketReadyState.CLOSED
// Act - Buffer all messages
for (const msg of messages) {
const mocks = result.mocks
const messageCount = 100
- mocks.webSocket.readyState = 3 // CLOSED
+ mocks.webSocket.readyState = WebSocketReadyState.CLOSED
// Act - Buffer many messages
for (let i = 0; i < messageCount; i++) {
const mocks = result.mocks
const testMessage = '[2,"offline-msg","Test",{}]'
- mocks.webSocket.readyState = 3 // CLOSED
+ mocks.webSocket.readyState = WebSocketReadyState.CLOSED
// Act - Buffer message
station.bufferMessage(testMessage)
const mocks = result.mocks
const testMessage = '[2,"clear-test","Command",{}]'
- mocks.webSocket.readyState = 3 // CLOSED
+ mocks.webSocket.readyState = WebSocketReadyState.CLOSED
// Act - Buffer message
station.bufferMessage(testMessage)
// Act - Perform multiple buffer/disconnect cycles
for (let cycle = 0; cycle < cycleCount; cycle++) {
// Simulate disconnection
- mocks.webSocket.readyState = 3 // CLOSED
+ mocks.webSocket.readyState = WebSocketReadyState.CLOSED
// Buffer messages in this cycle
for (let i = 0; i < messagesPerCycle; i++) {
CurrentType,
OCPPVersion,
type Reservation,
- type SampledValueTemplate,
} from '../../src/types/index.js'
import { logger } from '../../src/utils/index.js'
import { standardCleanup } from '../helpers/TestLifecycleHelpers.js'
const connectorStatus: ConnectorStatus = {
availability: AvailabilityType.Operative,
energyActiveImportIntervalBaselines: { 'station:aligned': 42 },
- MeterValues: [{} as unknown as SampledValueTemplate],
+ MeterValues: [{}],
status: ConnectorStatusEnum.Available,
transactionEnergyActiveImportIntervalBaselines: { transaction: 21 },
transactionStarted: true,
import assert from 'node:assert/strict'
import { afterEach, describe, it } from 'node:test'
-import type { ConnectorStatus } from '../../src/types/index.js'
-
import {
captureTransactionIntervalState,
completeTransactionIntervalState,
restoreTransactionIntervalState,
truncateTransactionIntervalValue,
} from '../../src/charging-station/meter-values/index.js'
+import {
+ type ConnectorStatus,
+ MeterValueLocation,
+ MeterValueMeasurand,
+ MeterValuePhase,
+ MeterValueUnit,
+} from '../../src/types/index.js'
import { standardCleanup } from '../helpers/TestLifecycleHelpers.js'
await describe('TransactionIntervalUtils', async () => {
{
sampledValue: [
{
- measurand: 'Energy.Active.Import.Interval',
- unit: 'kWh',
+ measurand: MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_INTERVAL,
+ unit: MeterValueUnit.KILO_WATT_HOUR,
value: '0.01',
},
],
{
sampledValue: [
{
- measurand: 'Energy.Active.Import.Interval',
- phase: 'L1',
- unitOfMeasure: { unit: 'kWh' },
+ measurand: MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_INTERVAL,
+ phase: MeterValuePhase.L1,
+ unitOfMeasure: { unit: MeterValueUnit.KILO_WATT_HOUR },
value: 0.003,
},
],
{
sampledValue: [
{
- location: 'Inlet',
- measurand: 'Energy.Active.Import.Interval',
- unit: 'Wh',
+ measurand: MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_INTERVAL,
+ unitOfMeasure: { multiplier: -1, unit: MeterValueUnit.KILO_WATT_HOUR },
+ value: 0.2,
+ },
+ ],
+ },
+ 1
+ ),
+ 20
+ )
+ assert.strictEqual(
+ getRepresentedTransactionIntervalEnergyWh(
+ {
+ sampledValue: [
+ {
+ location: MeterValueLocation.INLET,
+ measurand: MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_INTERVAL,
+ unit: MeterValueUnit.WATT_HOUR,
value: 100,
},
],
{
sampledValue: [
{
- location: 'Inlet',
- measurand: 'Energy.Active.Import.Interval',
- phase: 'L1',
- unit: 'Wh',
+ location: MeterValueLocation.INLET,
+ measurand: MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_INTERVAL,
+ phase: MeterValuePhase.L1,
+ unit: MeterValueUnit.WATT_HOUR,
value: 100 / 3,
},
],
ChargingStationInfo,
ConnectorStatus,
OCPP20SampledValue,
- SampledValue,
SampledValueTemplate,
} from '../../../src/types/index.js'
resolveRootSeed,
} from '../../../src/charging-station/meter-values/index.js'
import { hashLabel } from '../../../src/charging-station/meter-values/PRNG.js'
+import { buildOCPP16SampledValue } from '../../../src/charging-station/ocpp/1.6/OCPP16RequestBuilders.js'
import { buildOCPP20SampledValue } from '../../../src/charging-station/ocpp/2.0/OCPP20RequestBuilders.js'
import {
AvailabilityType,
CurrentType,
+ isOCPP20SampledValue,
MeterValueContext,
MeterValueLocation,
MeterValueMeasurand,
MeterValuePhase,
MeterValueUnit,
+ OCPP16MeterValueFormat,
+ OCPP20MeasurandEnumType,
OCPPVersion,
+ Voltage,
} from '../../../src/types/index.js'
import { standardCleanup } from '../../helpers/TestLifecycleHelpers.js'
import { TEST_METER_VALUES_INTERVAL_MS } from '../ChargingStationTestConstants.js'
groupUnderEvse?: boolean
numberOfPhases?: number
siblingConnectorMeterValues?: SampledValueTemplate[]
- voltageOut?: number
+ voltageOut?: Voltage
} = {}
): {
connectorStatus: ConnectorStatus
stationInfo: ChargingStationInfo
} => {
const numberOfPhases = overrides.numberOfPhases ?? 1
- const voltageOut = overrides.voltageOut ?? 230
+ const voltageOut = overrides.voltageOut ?? Voltage.VOLTAGE_230
const evseMax = overrides.evseMaxPowerW ?? 22000
const stationInfo: ChargingStationInfo = {
: measurand === MeterValueMeasurand.POWER_ACTIVE_IMPORT
? MeterValueUnit.WATT
: undefined
- return (unit != null ? { measurand, unit } : { measurand }) as SampledValueTemplate
+ return unit != null ? { measurand, unit } : { measurand }
})
}
* @param value - Numeric value to be serialized.
* @returns Minimal SampledValue with stringified value.
*/
-const passThroughBuilder: BuildVersionedSampledValue = (template, value): SampledValue => {
- return {
- ...(template.measurand != null && { measurand: template.measurand }),
- ...(template.unit != null && { unit: template.unit as never }),
- value: value.toString(),
- } as SampledValue
-}
+const passThroughBuilder: BuildVersionedSampledValue = buildOCPP16SampledValue
/**
* Creates a coherent session and asserts it is defined. Encapsulates the
measurand: MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER,
unit: MeterValueUnit.WATT_HOUR,
value: 0,
- })) as SampledValueTemplate[]
+ }))
const meterValue = buildCoherentMeterValue(
context,
currentType: CurrentType.AC,
evseMaxPowerW: 7400,
numberOfPhases: 1,
- voltageOut: 230,
+ voltageOut: Voltage.VOLTAGE_230,
})
const session = createSessionOrFail(context, {
connectorId: 1,
currentType: CurrentType.AC,
evseMaxPowerW: 22000,
numberOfPhases: 3,
- voltageOut: 230,
+ voltageOut: Voltage.VOLTAGE_230,
})
const session = createSessionOrFail(context, {
connectorId: 1,
const { connectorStatus, context, sessions } = buildContext({
currentType: CurrentType.DC,
evseMaxPowerW: 50000,
- voltageOut: 400,
+ voltageOut: Voltage.VOLTAGE_400,
})
const session = createSessionOrFail(context, {
connectorId: 1,
currentType: CurrentType.DC,
evseMaxPowerW: 1000,
groupUnderEvse: true,
- voltageOut: 400,
+ voltageOut: Voltage.VOLTAGE_400,
})
const session = createSessionOrFail(context, {
connectorId: 1,
unit: MeterValueUnit.WATT_HOUR,
},
]
- ) as unknown as SampledValueTemplate[]
+ )
const meterValue = buildCoherentMeterValue(
context,
currentType: CurrentType.DC,
evseMaxPowerW: 1000,
groupUnderEvse: true,
- voltageOut: 400,
+ voltageOut: Voltage.VOLTAGE_400,
})
const session = createSessionOrFail(context, {
connectorId: 1,
unit:
measurand === MeterValueMeasurand.VOLTAGE ? MeterValueUnit.VOLT : MeterValueUnit.AMP,
}))
- ) as unknown as SampledValueTemplate[]
+ )
const meterValue = buildCoherentMeterValue(
context,
{
fluctuationPercent: 0,
measurand: MeterValueMeasurand.FREQUENCY,
- unit: MeterValueUnit.HERTZ,
value: '50',
},
{
value: '50Hz',
},
{ measurand: MeterValueMeasurand.CURRENT_EXPORT },
- ] as unknown as SampledValueTemplate[]
+ ]
const enabledMeasurands = new Set([
MeterValueMeasurand.CURRENT_EXPORT,
MeterValueMeasurand.FREQUENCY,
currentType: CurrentType.AC,
evseMaxPowerW: 22000,
numberOfPhases: 1,
- voltageOut: 230,
+ voltageOut: Voltage.VOLTAGE_230,
})
const session = createSessionOrFail(context, {
connectorId: 1,
currentType: CurrentType.AC,
evseMaxPowerW: 22000,
numberOfPhases: 1,
- voltageOut: 230,
+ voltageOut: Voltage.VOLTAGE_230,
})
const session = createSessionOrFail(context, {
connectorId: 1,
currentType: CurrentType.AC,
evseMaxPowerW: 22000,
numberOfPhases: 3,
- voltageOut: 230,
+ voltageOut: Voltage.VOLTAGE_230,
})
const session = createSessionOrFail(context, {
connectorId: 1,
const { connectorStatus, context, sessions } = buildContext({
currentType: CurrentType.DC,
evseMaxPowerW: 50000,
- voltageOut: 400,
+ voltageOut: Voltage.VOLTAGE_400,
})
const session = createSessionOrFail(context, {
connectorId: 1,
currentType: CurrentType.AC,
evseMaxPowerW: 22000,
numberOfPhases: 3,
- voltageOut: 230,
+ voltageOut: Voltage.VOLTAGE_230,
})
const session = createSessionOrFail(context, {
connectorId: 1,
sessions.set(1, session)
// Phase-preserving builder: include template.phase and template.measurand
// so per-phase emissions can be counted and cross-checked.
- const phaseBuilder: BuildVersionedSampledValue = (template, value): SampledValue =>
- ({
- ...(template.measurand != null && { measurand: template.measurand }),
- ...(template.phase != null && { phase: template.phase as never }),
- ...(template.unit != null && { unit: template.unit as never }),
- value: value.toString(),
- }) as SampledValue
+ const phaseBuilder: BuildVersionedSampledValue = buildOCPP16SampledValue
connectorStatus.MeterValues = [
{ measurand: MeterValueMeasurand.VOLTAGE, phase: MeterValuePhase.L1_N },
{ measurand: MeterValueMeasurand.VOLTAGE, phase: MeterValuePhase.L2_N },
},
{ measurand: MeterValueMeasurand.CURRENT_IMPORT, phase: MeterValuePhase.L1 },
{ measurand: MeterValueMeasurand.CURRENT_IMPORT, phase: MeterValuePhase.N },
- ] as unknown as SampledValueTemplate[]
+ ]
const mv = buildCoherentMeterValue(context, session, phaseBuilder, {
intervalMs: TEST_METER_VALUES_INTERVAL_MS,
nowMs: TEST_METER_VALUES_INTERVAL_MS,
currentType: CurrentType.AC,
evseMaxPowerW: 7400,
numberOfPhases: 1,
- voltageOut: 230,
+ voltageOut: Voltage.VOLTAGE_230,
})
const session = createSessionOrFail(context, {
connectorId: 1,
connectorStatus.MeterValues = [
{ measurand: MeterValueMeasurand.VOLTAGE, phase: MeterValuePhase.L1_L2 },
{ measurand: MeterValueMeasurand.VOLTAGE, phase: MeterValuePhase.L1_N },
- ] as unknown as SampledValueTemplate[]
+ ]
const mv = buildCoherentMeterValue(context, session, passThroughBuilder, {
intervalMs: TEST_METER_VALUES_INTERVAL_MS,
nowMs: TEST_METER_VALUES_INTERVAL_MS,
currentType: CurrentType.AC,
evseMaxPowerW: 22000,
numberOfPhases: 3,
- voltageOut: 230,
+ voltageOut: Voltage.VOLTAGE_230,
})
const session = createSessionOrFail(context, {
connectorId: 1,
phase: MeterValuePhase.L1_N,
unit: MeterValueUnit.WATT_HOUR,
},
- ] as unknown as SampledValueTemplate[]
+ ]
const mv = buildCoherentMeterValue(context, session, passThroughBuilder, {
intervalMs: 3_600_000,
nowMs: 3_600_000,
currentType: CurrentType.AC,
evseMaxPowerW: 22000,
numberOfPhases: 3,
- voltageOut: 230,
+ voltageOut: Voltage.VOLTAGE_230,
})
const session = createSessionOrFail(context, {
connectorId: 1,
phase: MeterValuePhase.L3_N,
unit: MeterValueUnit.WATT_HOUR,
},
- ] as unknown as SampledValueTemplate[]
+ ]
const mv = buildCoherentMeterValue(
context,
session,
currentType: CurrentType.AC,
evseMaxPowerW: 22000,
numberOfPhases: 3,
- voltageOut: 230,
+ voltageOut: Voltage.VOLTAGE_230,
})
const session = createSessionOrFail(context, {
connectorId: 1,
phase: MeterValuePhase.L3_N,
unit: MeterValueUnit.WATT_HOUR,
},
- ] as unknown as SampledValueTemplate[]
+ ]
const mv = buildCoherentMeterValue(context, session, passThroughBuilder, {
intervalMs: 3_600_000,
nowMs: 3_600_000,
currentType: CurrentType.AC,
evseMaxPowerW: 22000,
numberOfPhases: 3,
- voltageOut: 230,
+ voltageOut: Voltage.VOLTAGE_230,
})
const session = createSessionOrFail(context, {
connectorId: 1,
phase: MeterValuePhase.L3_N,
unit: MeterValueUnit.WATT,
},
- ] as unknown as SampledValueTemplate[]
+ ]
const mv = buildCoherentMeterValue(
context,
session,
currentType: CurrentType.AC,
evseMaxPowerW: 22000,
numberOfPhases: 3,
- voltageOut: 230,
+ voltageOut: Voltage.VOLTAGE_230,
})
const session = createSessionOrFail(context, {
connectorId: 1,
phase: MeterValuePhase.L3_N,
unit: MeterValueUnit.WATT_HOUR,
},
- ] as unknown as SampledValueTemplate[]
+ ]
const mv = buildCoherentMeterValue(
context,
session,
currentType: CurrentType.AC,
evseMaxPowerW: 22000,
numberOfPhases: 3,
- voltageOut: 230,
+ voltageOut: Voltage.VOLTAGE_230,
})
const session = createSessionOrFail(context, {
connectorId: 1,
measurand: MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER,
unit: MeterValueUnit.WATT_HOUR,
},
- ] as unknown as SampledValueTemplate[]
+ ]
const mv = buildCoherentMeterValue(
context,
session,
currentType: CurrentType.AC,
evseMaxPowerW: 22000,
numberOfPhases: 3,
- voltageOut: 230,
+ voltageOut: Voltage.VOLTAGE_230,
})
const session = createSessionOrFail(context, {
connectorId: 1,
phase: MeterValuePhase.L2_N,
unit: MeterValueUnit.WATT_HOUR,
},
- ] as unknown as SampledValueTemplate[]
+ ]
const meterValue = buildCoherentMeterValue(
context,
connectorStatus.MeterValues = [
{ phase: MeterValuePhase.L1_N },
{ context: MeterValueContext.TRANSACTION_BEGIN, phase: MeterValuePhase.L2_N },
- { format: 'SignedData', phase: MeterValuePhase.L3_N },
+ { format: OCPP16MeterValueFormat.SIGNED_DATA, phase: MeterValuePhase.L3_N },
{
location: MeterValueLocation.OUTLET,
phase: MeterValuePhase.L1_N,
customData: { vendorId: 'sensor-b' },
phase: MeterValuePhase.L2_N,
},
- ] as unknown as SampledValueTemplate[]
+ ]
const meterValue = buildCoherentMeterValue(
context,
true
)
const energySamples = meterValue.sampledValue.filter(
- sample => sample.measurand === MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
- ) as OCPP20SampledValue[]
+ (sample): sample is OCPP20SampledValue =>
+ isOCPP20SampledValue(sample) &&
+ sample.measurand === OCPP20MeasurandEnumType.ENERGY_ACTIVE_IMPORT_REGISTER
+ )
assert.deepStrictEqual(
energySamples.map(sample => [
const evseTemplate: SampledValueTemplate = {
measurand: MeterValueMeasurand.STATE_OF_CHARGE,
unit: MeterValueUnit.PERCENT,
- } as unknown as SampledValueTemplate
+ }
const { connectorStatus, context, sessions } = buildContext({
currentType: CurrentType.AC,
evseMaxPowerW: 22000,
evseMeterValues: [evseTemplate],
numberOfPhases: 3,
- voltageOut: 230,
+ voltageOut: Voltage.VOLTAGE_230,
})
const session = createSessionOrFail(context, {
connectorId: 1,
{
measurand: MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER,
unit: MeterValueUnit.WATT_HOUR,
- } as unknown as SampledValueTemplate,
+ },
]
const mv = buildCoherentMeterValue(context, session, passThroughBuilder, {
intervalMs: TEST_METER_VALUES_INTERVAL_MS,
const connectorTemplate: SampledValueTemplate = {
measurand: MeterValueMeasurand.STATE_OF_CHARGE,
unit: MeterValueUnit.PERCENT,
- } as unknown as SampledValueTemplate
+ }
// No evseMeterValues override => getEvseIdByConnectorId returns undefined.
const { connectorStatus, context, sessions } = buildContext({
currentType: CurrentType.AC,
evseMaxPowerW: 22000,
numberOfPhases: 3,
- voltageOut: 230,
+ voltageOut: Voltage.VOLTAGE_230,
})
const session = createSessionOrFail(context, {
connectorId: 1,
const connectorTemplate: SampledValueTemplate = {
measurand: MeterValueMeasurand.STATE_OF_CHARGE,
unit: MeterValueUnit.PERCENT,
- } as unknown as SampledValueTemplate
+ }
// evseMeterValues=[] => getEvseIdByConnectorId returns 1, getEvseStatus(1).MeterValues = [].
// resolveTemplates must skip the empty EVSE array and fall back to connector-level.
const { connectorStatus, context, sessions } = buildContext({
evseMaxPowerW: 22000,
evseMeterValues: [],
numberOfPhases: 3,
- voltageOut: 230,
+ voltageOut: Voltage.VOLTAGE_230,
})
const session = createSessionOrFail(context, {
connectorId: 1,
const connectorTemplate: SampledValueTemplate = {
measurand: MeterValueMeasurand.STATE_OF_CHARGE,
unit: MeterValueUnit.PERCENT,
- } as unknown as SampledValueTemplate
+ }
// groupUnderEvse=true + no evseMeterValues => getEvseIdByConnectorId returns 1,
// getEvseStatus(1).MeterValues = undefined. resolveTemplates must fall back
// to connector-level (guard: isNotEmptyArray(evseTemplates) narrows undefined
evseMaxPowerW: 22000,
groupUnderEvse: true,
numberOfPhases: 3,
- voltageOut: 230,
+ voltageOut: Voltage.VOLTAGE_230,
})
const session = createSessionOrFail(context, {
connectorId: 1,
const siblingTemplate: SampledValueTemplate = {
measurand: MeterValueMeasurand.POWER_ACTIVE_IMPORT,
unit: MeterValueUnit.WATT,
- } as unknown as SampledValueTemplate
+ }
const { connectorStatus, context, sessions } = buildContext({
currentType: CurrentType.AC,
evseMaxPowerW: 22000,
groupUnderEvse: true,
numberOfPhases: 3,
siblingConnectorMeterValues: [siblingTemplate],
- voltageOut: 230,
+ voltageOut: Voltage.VOLTAGE_230,
})
const session = createSessionOrFail(context, {
connectorId: 1,
currentType: CurrentType.AC,
evseMaxPowerW: 7400,
numberOfPhases: 1,
- voltageOut: 230,
+ voltageOut: Voltage.VOLTAGE_230,
})
const session = createSessionOrFail(context, {
connectorId: 1,
currentType: CurrentType.AC,
evseMaxPowerW: 22000,
numberOfPhases: 3,
- voltageOut: 230,
+ voltageOut: Voltage.VOLTAGE_230,
})
const session = createSessionOrFail(context, {
connectorId: 1,
currentType: CurrentType.DC,
evseMaxPowerW: 50000,
numberOfPhases: 0,
- voltageOut: 400,
+ voltageOut: Voltage.VOLTAGE_400,
})
const session = createSessionOrFail(context, {
connectorId: 1,
currentType: CurrentType.AC,
evseMaxPowerW: 7400,
numberOfPhases: 1,
- voltageOut: 230,
+ voltageOut: Voltage.VOLTAGE_230,
})
const session = createSessionOrFail(context, {
connectorId: 1,
currentType: CurrentType.AC,
evseMaxPowerW: 7400,
numberOfPhases: 1,
- voltageOut: 230,
+ voltageOut: Voltage.VOLTAGE_230,
})
const session = createSessionOrFail(context, {
connectorId: 1,
measurand: MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER,
unit: 'Wh',
value: '0',
-} as unknown as SampledValueTemplate
+}
await describe('StrategyDispatch', async () => {
let station: ChargingStation
{
measurand: MeterValueMeasurand.STATE_OF_CHARGE,
unit: 'Percent',
- } as unknown as SampledValueTemplate,
+ },
]
connectorStatus.transactionId = TEST_TRANSACTION_ID
connectorStatus.transactionEnergyActiveImportRegisterValue = 0
phase: MeterValuePhase.L3_N,
unit: MeterValueUnit.WATT_HOUR,
},
- ] as unknown as SampledValueTemplate[]
+ ]
connectorStatus.transactionId = TEST_TRANSACTION_ID
connectorStatus.transactionEnergyActiveImportRegisterValue = 6000
connectorStatus.energyActiveImportRegisterValue = 6000
const connectorStatus = station.getConnectorStatus(1)
assert.ok(connectorStatus != null)
const templates = connectorStatus.MeterValues
- connectorStatus.MeterValues = ['sensor-a', 'sensor-b'].flatMap(vendorId =>
+ connectorStatus.MeterValues = ['sensor-a', 'sensor-b'].flatMap((vendorId, index) =>
templates.map(template => ({
...template,
- customData: { vendorId },
+ unitOfMeasure: {
+ customData: { vendorId },
+ multiplier: index,
+ unit: MeterValueUnit.WATT_HOUR,
+ },
}))
)
const meterValue = buildMeterValue(
assert.strictEqual(
energySamples.length,
2,
- 'phase suppression must preserve distinct customData register families'
+ 'phase suppression must preserve distinct native unit descriptor families'
)
assert.deepStrictEqual(
energySamples.map(sample => [
- (sample as { customData?: { vendorId?: string } }).customData?.vendorId,
+ (
+ sample as {
+ unitOfMeasure?: { customData?: { vendorId?: string }; multiplier?: number }
+ }
+ ).unitOfMeasure?.customData?.vendorId,
+ (sample as { unitOfMeasure?: { multiplier?: number } }).unitOfMeasure?.multiplier,
sample.phase,
]),
[
- ['sensor-a', undefined],
- ['sensor-b', undefined],
+ ['sensor-a', 0, undefined],
+ ['sensor-b', 1, undefined],
]
)
})
import type { ResetRequest } from '../../../../src/types/index.js'
+import { OCPPError } from '../../../../src/exception/index.js'
import {
+ ErrorType,
GenericStatus,
OCPP16IncomingRequestCommand,
ResetType,
assert.strictEqual(response.status, GenericStatus.Accepted)
})
+ await it('should reject an invalid reset type before scheduling a reset', () => {
+ const { testableService } = testContext
+ const station = ResetFixtures.createStandardStation(0)
+ const resetRequest = { type: 'Invalid' } as unknown as ResetRequest
+
+ assert.throws(
+ () => testableService.handleRequestReset(station, resetRequest),
+ (error: unknown) => error instanceof OCPPError && error.code === ErrorType.FORMAT_VIOLATION
+ )
+ })
+
// @spec §5.13 — TC_024_CS: Hard reset with active transaction
await it('should handle hard reset request with active transaction', () => {
// Arrange
RequestParams,
} from '../../../../src/types/index.js'
-import { TransactionMeterValueDeliveryBarrier } from '../../../../src/charging-station/meter-values/index.js'
+import {
+ type CoherentSession,
+ TransactionMeterValueDeliveryBarrier,
+} from '../../../../src/charging-station/meter-values/index.js'
import { createTestableIncomingRequestService } from '../../../../src/charging-station/ocpp/1.6/__testable__/index.js'
import { OCPP16IncomingRequestService } from '../../../../src/charging-station/ocpp/1.6/OCPP16IncomingRequestService.js'
import { OCPP16ServiceUtils } from '../../../../src/charging-station/ocpp/1.6/OCPP16ServiceUtils.js'
CurrentType,
ErrorType,
OCPP16AuthorizationStatus,
+ OCPP16AvailabilityType,
+ OCPP16ChargePointStatus,
OCPP16DiagnosticsStatus,
OCPP16FirmwareStatus,
OCPP16IncomingRequestCommand,
OCPP16MessageTrigger,
+ OCPP16MeterValueContext,
OCPP16MeterValueFormat,
OCPP16MeterValueLocation,
OCPP16MeterValueMeasurand,
OCPP16VendorParametersKey,
OCPPVersion,
PublicKeyWithSignedMeterValueEnumType,
+ Voltage,
} from '../../../../src/types/index.js'
import { Constants, logger } from '../../../../src/utils/index.js'
import {
// Assert
assert.strictEqual(response.status, OCPP16TriggerMessageStatus.ACCEPTED)
})
+
+ await it('should send an OCPP 1.6 coherent snapshot for a MeterValues trigger', async () => {
+ // Arrange
+ const { incomingRequestService, station, testableService } = context
+ const transactionId = 100
+ setupConnectorWithTransaction(station, 1, { transactionId })
+ assert.ok(station.stationInfo != null)
+ station.stationInfo.coherentMeterValues = true
+ enableConnectorMeterValues(station, 1)
+ const session: CoherentSession = {
+ connectorId: 1,
+ currentType: CurrentType.AC,
+ numberOfPhases: 3,
+ profile: {
+ batteryCapacityWh: 40000,
+ chargingCurve: [
+ { powerFraction: 1, socPercent: 0 },
+ { powerFraction: 1, socPercent: 100 },
+ ],
+ id: 'trigger-test',
+ initialSocPercentMax: 30,
+ initialSocPercentMin: 30,
+ maxPowerW: 11000,
+ weight: 1,
+ },
+ rampUpDurationMs: 0,
+ sessionStartMs: Date.now(),
+ socPercent: 30,
+ transactionId,
+ voltageOutNominal: Voltage.VOLTAGE_230,
+ }
+ station.__injectCoherentSession(transactionId, session)
+ const connectorStatus = station.getConnectorStatus(1)
+ assert.ok(connectorStatus != null)
+ const energyBefore = connectorStatus.transactionEnergyActiveImportRegisterValue
+ const socBefore = session.socPercent
+ let sentRequest: OCPP16MeterValuesRequest | undefined
+ ;(
+ station.ocppRequestService as unknown as {
+ requestHandler: (...args: unknown[]) => Promise<unknown>
+ }
+ ).requestHandler = (...args: unknown[]) => {
+ if (args[1] === OCPP16RequestCommand.METER_VALUES) {
+ sentRequest = args[2] as OCPP16MeterValuesRequest
+ }
+ return Promise.resolve({})
+ }
+ const request: OCPP16TriggerMessageRequest = {
+ connectorId: 1,
+ requestedMessage: OCPP16MessageTrigger.MeterValues,
+ }
+
+ // Act
+ const response = testableService.handleRequestTriggerMessage(station, request)
+ incomingRequestService.emit(
+ OCPP16IncomingRequestCommand.TRIGGER_MESSAGE,
+ station,
+ request,
+ response
+ )
+ await flushMicrotasks()
+
+ // Assert
+ assert.strictEqual(response.status, OCPP16TriggerMessageStatus.ACCEPTED)
+ assert.ok(sentRequest != null)
+ assert.strictEqual(sentRequest.connectorId, 1)
+ assert.ok((sentRequest.meterValue[0]?.sampledValue.length ?? 0) > 0)
+ assert.strictEqual(
+ sentRequest.meterValue[0]?.sampledValue[0]?.context,
+ OCPP16MeterValueContext.TRIGGER
+ )
+ assert.strictEqual(typeof sentRequest.meterValue[0]?.sampledValue[0]?.value, 'string')
+ assert.strictEqual(connectorStatus.transactionEnergyActiveImportRegisterValue, energyBefore)
+ assert.strictEqual(session.socPercent, socBefore)
+ })
})
await it('should keep active transaction interval state unchanged during MeterValues admission', () => {
// Act
const response = testableService.handleRequestTriggerMessage(station, {
- requestedMessage: 'UnknownMessage' as OCPP16MessageTrigger,
+ requestedMessage: 'UnknownMessage' as unknown as OCPP16MessageTrigger,
})
// Assert
})
}
+ await it('should include connector zero with a derived valid status in a broadcast trigger', () => {
+ // Arrange
+ const stationStatus = station.getConnectorStatus(0)
+ const connectorOneStatus = station.getConnectorStatus(1)
+ const connectorTwoStatus = station.getConnectorStatus(2)
+ assert.ok(stationStatus != null)
+ assert.ok(connectorOneStatus != null)
+ assert.ok(connectorTwoStatus != null)
+ delete stationStatus.status
+ stationStatus.availability = OCPP16AvailabilityType.Operative
+ connectorOneStatus.status = OCPP16ChargePointStatus.Available
+ connectorTwoStatus.status = OCPP16ChargePointStatus.Unavailable
+ const request: OCPP16TriggerMessageRequest = {
+ requestedMessage: OCPP16MessageTrigger.StatusNotification,
+ }
+
+ // Act
+ incomingRequestServiceForListener.emit(
+ OCPP16IncomingRequestCommand.TRIGGER_MESSAGE,
+ station,
+ request,
+ { status: OCPP16TriggerMessageStatus.ACCEPTED }
+ )
+
+ // Assert
+ assert.deepStrictEqual(
+ requestHandlerMock.mock.calls.map(call => call.arguments.slice(1)),
+ [
+ [
+ OCPP16RequestCommand.STATUS_NOTIFICATION,
+ { connectorId: 0, status: OCPP16ChargePointStatus.Available },
+ { triggerMessage: true },
+ ],
+ [
+ OCPP16RequestCommand.STATUS_NOTIFICATION,
+ { connectorId: 1, status: OCPP16ChargePointStatus.Available },
+ { triggerMessage: true },
+ ],
+ [
+ OCPP16RequestCommand.STATUS_NOTIFICATION,
+ { connectorId: 2, status: OCPP16ChargePointStatus.Unavailable },
+ { triggerMessage: true },
+ ],
+ ]
+ )
+ })
+
await it('should handle requestHandler rejection gracefully', async () => {
const rejectingMock = mock.fn(async () => Promise.reject(new Error('test error')))
const { station: rejectStation } = createMockChargingStation({
const templates: SampledValueTemplate[] = [
{
measurand: MeterValueMeasurand.STATE_OF_CHARGE,
- unit: 'Percent',
- } as unknown as SampledValueTemplate,
- { measurand: MeterValueMeasurand.VOLTAGE, unit: 'V' } as unknown as SampledValueTemplate,
+ unit: OCPP16MeterValueUnit.PERCENT,
+ },
+ { measurand: MeterValueMeasurand.VOLTAGE, unit: OCPP16MeterValueUnit.VOLT },
{
measurand: MeterValueMeasurand.POWER_ACTIVE_IMPORT,
- unit: 'W',
- } as unknown as SampledValueTemplate,
- { measurand: MeterValueMeasurand.CURRENT_IMPORT, unit: 'A' } as unknown as SampledValueTemplate,
+ unit: OCPP16MeterValueUnit.WATT,
+ },
+ { measurand: MeterValueMeasurand.CURRENT_IMPORT, unit: OCPP16MeterValueUnit.AMP },
{
measurand: MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER,
unit: OCPP16MeterValueUnit.WATT_HOUR,
- } as unknown as SampledValueTemplate,
+ },
]
const connectorStatus = station.getConnectorStatus(CONNECTOR_ID)
if (connectorStatus != null) {
--- /dev/null
+/**
+ * @file Tests for OCPP16RequestBuilders
+ * @description Verifies OCPP 1.6 version-specific pure builders
+ */
+import assert from 'node:assert/strict'
+import { describe, it } from 'node:test'
+
+import { buildOCPP16SampledValue } from '../../../../src/charging-station/ocpp/1.6/OCPP16RequestBuilders.js'
+import { BaseError } from '../../../../src/exception/index.js'
+import {
+ OCPP16MeterValueMeasurand,
+ OCPP16MeterValueUnit,
+ type SampledValueTemplate,
+} from '../../../../src/types/index.js'
+
+await describe('OCPP16RequestBuilders', async () => {
+ await describe('buildOCPP16SampledValue', async () => {
+ for (const unit of [OCPP16MeterValueUnit.TEMP_CELCIUS, OCPP16MeterValueUnit.TEMP_CELSIUS]) {
+ await it(`should preserve the schema-supported ${unit} temperature unit`, () => {
+ const sampledValue = buildOCPP16SampledValue({ unit }, 21)
+
+ assert.strictEqual(sampledValue.unit, unit)
+ })
+ }
+
+ await it('should omit the OCPP 2.0-only default unit for OCPP 1.6 Frequency', () => {
+ const sampledValue = buildOCPP16SampledValue(
+ { measurand: OCPP16MeterValueMeasurand.FREQUENCY },
+ 50
+ )
+
+ assert.strictEqual(sampledValue.unit, undefined)
+ })
+
+ await it('should reject an OCPP 2.0-only unit descriptor', () => {
+ assert.throws(
+ () =>
+ buildOCPP16SampledValue(
+ {
+ unitOfMeasure: { multiplier: 3, unit: OCPP16MeterValueUnit.WATT_HOUR },
+ },
+ 1
+ ),
+ (error: unknown) =>
+ error instanceof BaseError && error.message.includes('sampled value unitOfMeasure')
+ )
+ })
+
+ for (const fieldName of ['context', 'location', 'measurand', 'phase', 'unit'] as const) {
+ await it(`should reject an invalid supplied ${fieldName}`, () => {
+ const template = { [fieldName]: 'Invalid' } as unknown as SampledValueTemplate
+
+ assert.throws(
+ () => buildOCPP16SampledValue(template, 1),
+ (error: unknown) =>
+ error instanceof BaseError && error.message.includes(`sampled value ${fieldName}`)
+ )
+ })
+ }
+ })
+})
assert.strictEqual(signedSamples[0].context, OCPP16MeterValueContext.TRANSACTION_BEGIN)
})
+ await it('should not sign when the configured signing method is invalid', () => {
+ const connectorStatus = station.getConnectorStatus(1)
+ assert.ok(connectorStatus != null)
+ connectorStatus.transactionId = 42
+ upsertConfigurationKey(station, OCPP16VendorParametersKey.SampledDataSignReadings, 'true')
+ upsertConfigurationKey(
+ station,
+ OCPP16VendorParametersKey.SampledDataSignStartedReadings,
+ 'true'
+ )
+ upsertConfigurationKey(
+ station,
+ `${OCPP16VendorParametersKey.MeterPublicKey}1`,
+ TEST_PUBLIC_KEY_HEX
+ )
+ upsertConfigurationKey(station, OCPP16VendorParametersKey.SigningMethod, 'invalid')
+
+ const meterValue = OCPP16ServiceUtils.buildTransactionBeginMeterValue(station, 1, 5000)
+
+ assert.strictEqual(
+ meterValue.sampledValue.some(
+ sampledValue => sampledValue.format === OCPP16MeterValueFormat.SIGNED_DATA
+ ),
+ false
+ )
+ })
+
await it('should project only the ordinary DC Inlet begin sample while signing the raw Outlet reading', () => {
assert.ok(station.stationInfo != null)
station.stationInfo.conversionEfficiency = 0.8
type OCPP16StopTransactionResponse,
OCPP16SupportedFeatureProfiles,
OCPP16VendorParametersKey,
+ OCPP20ReasonEnumType,
OCPPVersion,
type RequestParams,
} from '../../../../src/types/index.js'
)
}
+ for (const invalidReason of [
+ 'InvalidReason',
+ OCPP20ReasonEnumType.EnergyLimitReached,
+ ] as const) {
+ await it(`should reject unsupported reason '${invalidReason}' before mutating transaction state`, async () => {
+ const requestHandler = mock.fn(() => Promise.resolve({}))
+ const { station } = createMockChargingStation({
+ ocppRequestService: { requestHandler },
+ ocppVersion: OCPPVersion.VERSION_16,
+ })
+ setupConnectorWithTransaction(station, 1, { transactionId: 100 })
+ const connectorStatus = station.getConnectorStatus(1)
+ assert.ok(connectorStatus != null)
+
+ const requestOverrides = {
+ reason: invalidReason,
+ } as unknown as Partial<OCPP16StopTransactionRequest>
+
+ await assert.rejects(
+ OCPP16ServiceUtils.stopTransactionOnConnector(station, 1, undefined, requestOverrides),
+ (error: unknown) =>
+ error instanceof OCPPError &&
+ error.code === ErrorType.FORMAT_VIOLATION &&
+ error.message.includes('Invalid StopTransaction reason')
+ )
+
+ assert.notStrictEqual(connectorStatus.transactionEnding, true)
+ assert.strictEqual(requestHandler.mock.callCount(), 0)
+ })
+ }
+
await it('should suppress periodic MeterValues while a transaction is ending', t => {
t.mock.timers.enable({ apis: ['setInterval'] })
const requestHandler = mock.fn(() => Promise.resolve({}))
import type {
ConnectorStatus,
- EvseStatus,
OCPP20FirmwareStatusNotificationRequest,
OCPP20MeterValuesRequest,
OCPP20TransactionEventRequest,
OCPP20RequiredVariableName,
OCPP20TransactionEventEnumType,
OCPP20TriggerReasonEnumType,
+ OCPP20UnitEnumType,
OCPPVersion,
PublicKeyWithSignedMeterValueEnumType,
ReasonCodeEnumType,
connectorStatus.MeterValues = [
{
measurand: OCPP20MeasurandEnumType.ENERGY_ACTIVE_IMPORT_REGISTER,
- unit: 'Wh',
+ unit: OCPP20UnitEnumType.WATT_HOUR,
},
- ] as unknown as NonNullable<EvseStatus['MeterValues']>
+ ]
connectorStatus.energyActiveImportRegisterValue = energyValue
evseStatus.connectors.set(connectorId, connectorStatus)
return connectorStatus
connectorStatus.MeterValues = [
{
measurand: OCPP20MeasurandEnumType.ENERGY_ACTIVE_IMPORT_REGISTER,
- unit: 'Wh',
+ unit: OCPP20UnitEnumType.WATT_HOUR,
},
- ] as unknown as NonNullable<EvseStatus['MeterValues']>
+ ]
connectorStatus.energyActiveImportRegisterValue = (evseId ?? 0) * 10
}
return { mockStation, requestHandlerMock }
{
location: OCPP20LocationEnumType.Inlet,
measurand: OCPP20MeasurandEnumType.ENERGY_ACTIVE_IMPORT_INTERVAL,
- unit: 'Wh',
+ unit: OCPP20UnitEnumType.WATT_HOUR,
},
- ] as unknown as NonNullable<EvseStatus['MeterValues']>
+ ]
connectorStatus.transactionEnergyActiveImportRegisterValue = 150
connectorStatus.transactionEnergyActiveImportIntervalBaselines = { [baselineKey]: 60 }
connectorStatus.transactionEnergyActiveImportIntervalCarry = { [baselineKey]: 0 }
connectorStatus.MeterValues = [
{
measurand: OCPP20MeasurandEnumType.ENERGY_ACTIVE_IMPORT_REGISTER,
- unit: 'Wh',
+ unit: OCPP20UnitEnumType.WATT_HOUR,
},
- ] as unknown as NonNullable<EvseStatus['MeterValues']>
+ ]
connectorStatus.transactionId = `duplicate-response-${evseId.toString()}`
connectorStatus.transactionStarted = true
}
connectorStatus.MeterValues = [
{
measurand: OCPP20MeasurandEnumType.ENERGY_ACTIVE_IMPORT_INTERVAL,
- unit: 'Wh',
+ unit: OCPP20UnitEnumType.WATT_HOUR,
},
- ] as unknown as NonNullable<EvseStatus['MeterValues']>
+ ]
connectorStatus.transactionEnergyActiveImportRegisterValue = 0
connectorStatus.transactionEnergyActiveImportIntervalBaselines = { [baselineKey]: 0 }
connectorStatus.transactionEnergyActiveImportIntervalCarry = { [baselineKey]: 0 }
{
fluctuationPercent: 0,
measurand: OCPP20MeasurandEnumType.ENERGY_ACTIVE_IMPORT_INTERVAL,
- unit: 'Wh',
+ unit: OCPP20UnitEnumType.WATT_HOUR,
},
{
fluctuationPercent: 0,
measurand: OCPP20MeasurandEnumType.ENERGY_ACTIVE_IMPORT_REGISTER,
- unit: 'Wh',
+ unit: OCPP20UnitEnumType.WATT_HOUR,
},
- ] as unknown as NonNullable<EvseStatus['MeterValues']>
+ ]
connectorStatus.transactionEnergyActiveImportRegisterValue = 150
// Pin the register read ahead of the sample window so the coalesced interval energy
// reflects only the register delta, never wall-clock proration (deterministic on any host).
function seedActiveTransaction (evseId: number, transactionId: string): void {
const evseStatus = mockStation.getEvseStatus(evseId)
if (evseStatus != null) {
- evseStatus.MeterValues = [{ unit: 'Wh' }] as unknown as EvseStatus['MeterValues']
+ evseStatus.MeterValues = [{ unit: OCPP20UnitEnumType.WATT_HOUR }]
}
setupConnectorWithTransaction(mockStation, evseId, {
energyImport: 1234,
assert.ok(evseStatus != null && connectorStatus != null)
evseStatus.connectors.delete(2)
evseStatus.connectors.set(1, connectorStatus)
- evseStatus.MeterValues = [{ unit: 'Wh' }] as unknown as EvseStatus['MeterValues']
+ evseStatus.MeterValues = [{ unit: OCPP20UnitEnumType.WATT_HOUR }]
connectorStatus.transactionStarted = true
connectorStatus.transactionId = 'txn-evse-2-local-connector'
connectorStatus.transactionEnergyActiveImportRegisterValue = 1234
})
const rejectEvseStatus = rejectStation.getEvseStatus(1)
if (rejectEvseStatus != null) {
- rejectEvseStatus.MeterValues = [{ unit: 'Wh' }] as unknown as EvseStatus['MeterValues']
+ rejectEvseStatus.MeterValues = [{ unit: OCPP20UnitEnumType.WATT_HOUR }]
}
setupConnectorWithTransaction(rejectStation, 1, {
energyImport: 1234,
})
await it('should reject SetVariables on an unknown component and confirm GetVariables returns UnknownComponent', () => {
- const unknownComponent = { name: 'NonExistentComponent' as OCPP20ComponentName }
+ const unknownComponent = { name: 'NonExistentComponent' as unknown as OCPP20ComponentName }
const variableName = 'SomeVariable'
// Attempt to set a variable on a component that does not exist in the registry
measurand: MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER,
unit: 'Wh',
value: '0',
-} as unknown as SampledValueTemplate
+}
const voltageTemplate: SampledValueTemplate = {
measurand: MeterValueMeasurand.VOLTAGE,
unit: 'V',
value: '230',
-} as unknown as SampledValueTemplate
+}
await describe('OCPP 2.0 Signed Meter Values', async () => {
await describe('buildOCPP20SampledValue with signing config', async () => {
import assert from 'node:assert/strict'
import { afterEach, describe, it } from 'node:test'
-import type { StopTransactionReason } from '../../../../src/types/index.js'
-
-import { mapStopReasonToOCPP20 } from '../../../../src/charging-station/ocpp/2.0/OCPP20RequestBuilders.js'
+import {
+ buildOCPP20SampledValue,
+ mapStopReasonToOCPP20,
+} from '../../../../src/charging-station/ocpp/2.0/OCPP20RequestBuilders.js'
+import { BaseError } from '../../../../src/exception/index.js'
+import {
+ OCPP16MeterValueMeasurand,
+ OCPP16StopTransactionReason,
+ OCPP20MeasurandEnumType,
+ OCPP20UnitEnumType,
+ PublicKeyWithSignedMeterValueEnumType,
+ type SampledValueTemplate,
+} from '../../../../src/types/index.js'
import { standardCleanup } from '../../../helpers/TestLifecycleHelpers.js'
await describe('OCPP20RequestBuilders', async () => {
standardCleanup()
})
+ await describe('buildOCPP20SampledValue', async () => {
+ await it('should reject an OCPP 1.6-only measurand', () => {
+ assert.throws(
+ () => buildOCPP20SampledValue({ measurand: OCPP16MeterValueMeasurand.FAN_RPM }, 1),
+ (error: unknown) =>
+ error instanceof BaseError && error.message.includes('sampled value measurand')
+ )
+ })
+
+ await it('should reject an invalid measurand', () => {
+ const template = { measurand: 'Invalid' } as unknown as SampledValueTemplate
+
+ assert.throws(
+ () => buildOCPP20SampledValue(template, 1),
+ (error: unknown) =>
+ error instanceof BaseError && error.message.includes('sampled value measurand')
+ )
+ })
+
+ await it('should preserve a custom unit string', () => {
+ const { sampledValue } = buildOCPP20SampledValue({ unit: 'custom-unit' }, 1)
+
+ assert.deepStrictEqual(sampledValue.unitOfMeasure, { unit: 'custom-unit' })
+ })
+
+ await it('should preserve physical quantity across standardized unit multipliers', () => {
+ const { sampledValue } = buildOCPP20SampledValue(
+ {
+ measurand: OCPP20MeasurandEnumType.ENERGY_ACTIVE_IMPORT_REGISTER,
+ unit: OCPP20UnitEnumType.WATT_HOUR,
+ unitOfMeasure: { multiplier: 3, unit: OCPP20UnitEnumType.KILO_WATT_HOUR },
+ },
+ 2_500_000,
+ undefined,
+ undefined,
+ {
+ enabled: true,
+ meterSerialNumber: 'SIM-METER-001',
+ publicKeySentInTransaction: false,
+ publicKeyWithSignedMeterValue: PublicKeyWithSignedMeterValueEnumType.Never,
+ transactionId: 'tx-1',
+ }
+ )
+
+ assert.strictEqual(sampledValue.value, 2.5)
+ assert.deepStrictEqual(sampledValue.unitOfMeasure, {
+ multiplier: 3,
+ unit: OCPP20UnitEnumType.KILO_WATT_HOUR,
+ })
+ assert.strictEqual(sampledValue.value * 1_000 * 10 ** 3, 2_500_000)
+ assert.match(
+ Buffer.from(sampledValue.signedMeterValue?.signedMeterData ?? '', 'base64').toString(
+ 'utf8'
+ ),
+ /"RV":2500/u
+ )
+ })
+ })
+
await describe('mapStopReasonToOCPP20', async () => {
await it('should map Other to Other/AbnormalCondition', () => {
- const result = mapStopReasonToOCPP20('Other' as StopTransactionReason)
+ const result = mapStopReasonToOCPP20(OCPP16StopTransactionReason.OTHER)
assert.strictEqual(result.stoppedReason, 'Other')
assert.strictEqual(result.triggerReason, 'AbnormalCondition')
})
await it('should map Remote to Remote/RemoteStop', () => {
- const result = mapStopReasonToOCPP20('Remote' as StopTransactionReason)
+ const result = mapStopReasonToOCPP20(OCPP16StopTransactionReason.REMOTE)
assert.strictEqual(result.stoppedReason, 'Remote')
assert.strictEqual(result.triggerReason, 'RemoteStop')
for (const evseId of evseIds) {
const evseStatus = mockStation.getEvseStatus(evseId)
if (evseStatus != null) {
- evseStatus.MeterValues = [{ unit: 'Wh' }] as unknown as EvseStatus['MeterValues']
+ evseStatus.MeterValues = [{ unit: OCPP20UnitEnumType.WATT_HOUR }]
}
}
const seedRegister = (connectorId: number, value: number): void => {
evseStatus.MeterValues = []
connector1.MeterValues = [
{ measurand: OCPP20MeasurandEnumType.ENERGY_ACTIVE_IMPORT_REGISTER, unit: 'Wh' },
- ] as unknown as NonNullable<EvseStatus['MeterValues']>
+ ]
connector2.MeterValues = [
{ measurand: OCPP20MeasurandEnumType.ENERGY_ACTIVE_IMPORT_REGISTER, unit: 'Wh' },
{
unit: 'V',
value: '230',
},
- ] as unknown as NonNullable<EvseStatus['MeterValues']>
+ ]
upsertConfigurationKey(mockStation, ALIGNED_DATA_INTERVAL_KEY, '60')
upsertConfigurationKey(mockStation, ALIGNED_ENABLED_KEY, 'true')
upsertConfigurationKey(
measurand: OCPP20MeasurandEnumType.ENERGY_ACTIVE_IMPORT_REGISTER,
unit: 'Wh',
},
- ] as unknown as NonNullable<EvseStatus['MeterValues']>
+ ]
connector2.MeterValues = [
{
customData: { vendorId: 'sensor-b' },
measurand: OCPP20MeasurandEnumType.ENERGY_ACTIVE_IMPORT_REGISTER,
unit: 'Wh',
},
- ] as unknown as NonNullable<EvseStatus['MeterValues']>
+ ]
upsertConfigurationKey(mockStation, ALIGNED_DATA_INTERVAL_KEY, '60')
upsertConfigurationKey(mockStation, ALIGNED_ENABLED_KEY, 'true')
upsertConfigurationKey(
unit: 'V',
value: '210',
},
- ] as unknown as NonNullable<EvseStatus['MeterValues']>
+ ]
connector2.MeterValues = [
{
fluctuationPercent: 0,
unit: 'V',
value: '240',
},
- ] as unknown as NonNullable<EvseStatus['MeterValues']>
+ ]
upsertConfigurationKey(mockStation, ALIGNED_DATA_INTERVAL_KEY, '60')
upsertConfigurationKey(mockStation, ALIGNED_ENABLED_KEY, 'true')
upsertConfigurationKey(mockStation, ALIGNED_MEASURANDS_KEY, OCPP20MeasurandEnumType.VOLTAGE)
assert.ok(connector2 != null)
stationEvse.MeterValues = [
{ measurand: OCPP20MeasurandEnumType.ENERGY_REACTIVE_IMPORT_REGISTER, unit: 'varh' },
- ] as unknown as NonNullable<EvseStatus['MeterValues']>
+ ]
evseStatus.MeterValues = []
connector1.MeterValues = [
{
unit: 'varh',
value: '1000',
},
- ] as unknown as NonNullable<EvseStatus['MeterValues']>
+ ]
connector2.MeterValues = [
{
fluctuationPercent: 0,
unit: 'varh',
value: '2000',
},
- ] as unknown as NonNullable<EvseStatus['MeterValues']>
+ ]
upsertConfigurationKey(mockStation, ALIGNED_DATA_INTERVAL_KEY, '60')
upsertConfigurationKey(mockStation, ALIGNED_ENABLED_KEY, 'true')
upsertConfigurationKey(mockStation, SEND_DURING_IDLE_KEY, 'false')
unit,
value,
},
- ] as unknown as NonNullable<EvseStatus['MeterValues']>
+ ]
}
for (const evseId of [1, 2]) {
const evseStatus = mockStation.getEvseStatus(evseId)
measurand: OCPP20MeasurandEnumType.POWER_ACTIVE_IMPORT,
unit: 'W',
},
- ] as unknown as NonNullable<EvseStatus['MeterValues']>
+ ]
evse1.MeterValues = [
{
fluctuationPercent: 0,
unit: 'W',
value: '9000',
},
- ] as unknown as NonNullable<EvseStatus['MeterValues']>
+ ]
evse2.MeterValues = [
{
fluctuationPercent: 0,
unit: 'W',
value: '2000',
},
- ] as unknown as NonNullable<EvseStatus['MeterValues']>
+ ]
upsertConfigurationKey(mockStation, ALIGNED_DATA_INTERVAL_KEY, '60')
upsertConfigurationKey(mockStation, ALIGNED_ENABLED_KEY, 'true')
upsertConfigurationKey(
value: outletValue.toString(),
},
]
- ) as unknown as NonNullable<EvseStatus['MeterValues']>
+ )
stationEvse.MeterValues = sourcePairs.map(([measurand, unit]) => ({
location: OCPP20LocationEnumType.Outlet,
measurand,
unit,
- })) as unknown as NonNullable<EvseStatus['MeterValues']>
+ }))
upsertConfigurationKey(mockStation, ALIGNED_DATA_INTERVAL_KEY, '60')
upsertConfigurationKey(mockStation, ALIGNED_ENABLED_KEY, 'true')
upsertConfigurationKey(
value: '400',
},
]
- physicalEvse.MeterValues = templates as unknown as NonNullable<EvseStatus['MeterValues']>
+ physicalEvse.MeterValues = templates
stationEvse.MeterValues = templates.map(template => ({
...template,
location: OCPP20LocationEnumType.Inlet,
value: undefined,
- })) as unknown as NonNullable<EvseStatus['MeterValues']>
+ }))
upsertConfigurationKey(mockStation, ALIGNED_DATA_INTERVAL_KEY, '60')
upsertConfigurationKey(mockStation, ALIGNED_ENABLED_KEY, 'true')
upsertConfigurationKey(
unit: 'kW',
value: '1',
},
- ] as unknown as ConnectorStatus['MeterValues']
+ ]
const stationMeter = mockStation.getEvseStatus(0)
assert.ok(stationMeter != null)
stationMeter.MeterValues = [
unit: 'W',
value: '0',
},
- ] as unknown as NonNullable<EvseStatus['MeterValues']>
+ ]
upsertConfigurationKey(mockStation, ALIGNED_DATA_INTERVAL_KEY, '60')
upsertConfigurationKey(mockStation, ALIGNED_ENABLED_KEY, 'true')
upsertConfigurationKey(
unit: 'Percent',
value: '50',
},
- ] as unknown as NonNullable<EvseStatus['MeterValues']>
+ ]
stationMeter.MeterValues = []
setupConnectorWithTransaction(mockStation, 1, { transactionId: 'tx-soc' })
upsertConfigurationKey(mockStation, ALIGNED_DATA_INTERVAL_KEY, '60')
unit,
value,
},
- ] as unknown as NonNullable<EvseStatus['MeterValues']>
+ ]
}
upsertConfigurationKey(mockStation, ALIGNED_DATA_INTERVAL_KEY, '60')
upsertConfigurationKey(mockStation, ALIGNED_ENABLED_KEY, 'true')
unit,
value: value.toString(),
},
- ] as unknown as NonNullable<EvseStatus['MeterValues']>
+ ]
}
upsertConfigurationKey(mockStation, ALIGNED_DATA_INTERVAL_KEY, '60')
upsertConfigurationKey(mockStation, ALIGNED_ENABLED_KEY, 'true')
unit: 'varh',
value: '1000',
},
- ] as unknown as NonNullable<ConnectorStatus['MeterValues']>
+ ]
}
}
setupConnectorWithTransaction(mockStation, 1, { transactionId: 'tx-1' })
measurand: OCPP20MeasurandEnumType.ENERGY_ACTIVE_IMPORT_INTERVAL,
unit: 'Wh',
},
- ] as unknown as EvseStatus['MeterValues']
+ ]
evseStatus.MeterValues = [
{
fluctuationPercent: 0,
unit: 'Wh',
value: '60',
},
- ] as unknown as EvseStatus['MeterValues']
+ ]
setupConnectorWithTransaction(mockStation, 1, { transactionId: 'tx-suppressed' })
const connectorStatus = mockStation.getConnectorStatus(1, 1)
assert.ok(connectorStatus != null)
unit: 'Wh',
value: '60',
},
- ] as unknown as EvseStatus['MeterValues']
+ ]
setupConnectorWithTransaction(mockStation, 1, { transactionId: 'tx-independent-cadences' })
const connectorStatus = mockStation.getConnectorStatus(1, 1)
assert.ok(connectorStatus != null)
measurand: OCPP20MeasurandEnumType.ENERGY_ACTIVE_IMPORT_INTERVAL,
unit: 'Wh',
},
- ] as unknown as EvseStatus['MeterValues']
+ ]
evseStatus.MeterValues = [
{
fluctuationPercent: 0,
unit: 'Wh',
value: '60',
},
- ] as unknown as EvseStatus['MeterValues']
+ ]
const transactionId = `tx-station-cadence-${String(meteringPerTransaction)}`
const baselineTimestamp = Date.now()
setupConnectorWithTransaction(mockStation, 1, { transactionId })
unit: 'V',
value: '230',
},
- ] as unknown as EvseStatus['MeterValues']
+ ]
connectorStatus.energyActiveImportRegisterValue = 115
connectorStatus.energyActiveImportIntervalBaselines = {
[`station:${ALIGNED_MEASURANDS_KEY}`]: 100,
unit: 'Wh',
value: '0',
},
- ] as unknown as EvseStatus['MeterValues']
+ ]
const activeAt = new Date(60_000)
setupConnectorWithTransaction(mockStation, 1, { transactionId: 'tx-active-idle-baseline' })
connectorStatus.energyActiveImportRegisterValue = 40
unit: 'Wh',
value: '0',
},
- ] as unknown as EvseStatus['MeterValues']
+ ]
evseStatus.energyActiveImportIntervalBaseline = 80
connector1.energyActiveImportRegisterValue = 40
connector2.energyActiveImportRegisterValue = 60
unit: 'Wh',
value: '0',
},
- ] as unknown as EvseStatus['MeterValues']
+ ]
connector1.energyActiveImportRegisterValue = 10
connector2.energyActiveImportRegisterValue = 30
connector1.energyActiveImportIntervalBaselines = {
unit: OCPP20UnitEnumType.WATT_HOUR,
value: '0',
},
- ] as unknown as EvseStatus['MeterValues']
+ ]
const stationBaselineKey = `station:${ALIGNED_MEASURANDS_KEY}`
connectorStatus.energyActiveImportRegisterValue = 10
connectorStatus.energyActiveImportIntervalBaselines = {
unit: 'Wh',
value: '0',
},
- ] as unknown as EvseStatus['MeterValues']
+ ]
setupConnectorWithTransaction(mockStation, 1, { transactionId: 'tx-shared-evse-restore' })
const connector1 = mockStation.getConnectorStatus(1, 1)
const connector2 = mockStation.getConnectorStatus(2, 1)
unit: 'Wh',
value: '0',
},
- ] as unknown as EvseStatus['MeterValues']
+ ]
connectorStatus.energyActiveImportRegisterValue = 40
connectorStatus.transactionEnergyActiveImportRegisterValue = 0
connectorStatus.energyActiveImportIntervalBaselines = {
assert.ok(stationEvseStatus != null)
assert.ok(evseStatus != null)
assert.ok(connectorStatus != null)
- stationEvseStatus.MeterValues = [{ unit: 'Wh' }] as unknown as EvseStatus['MeterValues']
+ stationEvseStatus.MeterValues = [{ unit: OCPP20UnitEnumType.WATT_HOUR }]
evseStatus.MeterValues = [
{
fluctuationPercent: 0,
unit: 'Wh',
value: '0',
},
- ] as unknown as EvseStatus['MeterValues']
+ ]
connectorStatus.energyActiveImportRegisterValue = 40
connectorStatus.transactionEnergyActiveImportRegisterValue = 0
connectorStatus.energyActiveImportIntervalBaselines = {
measurand: OCPP20MeasurandEnumType.ENERGY_ACTIVE_IMPORT_INTERVAL,
unit: 'Wh',
},
- ] as unknown as EvseStatus['MeterValues']
+ ]
setupConnectorWithTransaction(mockStation, 1, { transactionId: 'tx-between-slots' })
const connectorStatus = mockStation.getConnectorStatus(1, 1)
assert.ok(connectorStatus != null)
measurand: OCPP20MeasurandEnumType.ENERGY_REACTIVE_IMPORT_INTERVAL,
unit: 'varh',
},
- ] as unknown as EvseStatus['MeterValues']
+ ]
evseStatus.MeterValues = [
{
fluctuationPercent: 0,
unit: 'varh',
value: '5',
},
- ] as unknown as EvseStatus['MeterValues']
+ ]
setupConnectorWithTransaction(mockStation, 1, { transactionId: 'tx-coalesced' })
const connectorStatus = mockStation.getConnectorStatus(1, 1)
assert.ok(connectorStatus != null)
measurand: OCPP20MeasurandEnumType.ENERGY_ACTIVE_IMPORT_INTERVAL,
unit: 'Wh',
},
- ] as unknown as EvseStatus['MeterValues']
+ ]
evseStatus.MeterValues = [
{
fluctuationPercent: 0,
unit: 'Wh',
value: '10',
},
- ] as unknown as EvseStatus['MeterValues']
+ ]
evseStatus.energyActiveImportRegisterLastUpdatedAt = new Date(0)
setupConnectorWithTransaction(mockStation, 1, { transactionId: 'tx-meter-values-retry' })
let failed = false
assert.strictEqual(energySample.value, 1234)
})
+ await it('should not sign active aligned samples when the configured signing method is invalid', async () => {
+ const { mockStation, requestHandlerMock } = alignedStation
+ upsertConfigurationKey(mockStation, ALIGNED_DATA_INTERVAL_KEY, '60')
+ upsertConfigurationKey(mockStation, ALIGNED_ENABLED_KEY, 'true')
+ upsertConfigurationKey(mockStation, SEND_DURING_IDLE_KEY, 'false')
+ upsertConfigurationKey(mockStation, SIGN_READINGS_KEY, 'true')
+ upsertConfigurationKey(mockStation, SIGN_UPDATED_READINGS_KEY, 'true')
+ upsertConfigurationKey(mockStation, FISCAL_PUBLIC_KEY, TEST_PUBLIC_KEY_HEX)
+ upsertConfigurationKey(mockStation, FISCAL_SIGNING_METHOD, 'invalid')
+ setupConnectorWithTransaction(mockStation, 1, { transactionId: 'tx-invalid-method' })
+
+ await OCPP20ServiceUtils.emitClockAlignedMeterValues(mockStation)
+
+ const samples = sentTransactionEvents(requestHandlerMock).flatMap(transactionEvent =>
+ (transactionEvent.meterValue ?? []).flatMap(meterValue => meterValue.sampledValue)
+ )
+ assert.ok(samples.length > 0)
+ assert.ok(samples.every(sample => sample.signedMeterValue == null))
+ })
+
await it('should not sign active aligned samples when SignUpdatedReadings is disabled', async () => {
const { mockStation, requestHandlerMock } = alignedStation
upsertConfigurationKey(mockStation, ALIGNED_DATA_INTERVAL_KEY, '60')
measurand: OCPP20MeasurandEnumType.ENERGY_ACTIVE_IMPORT_REGISTER,
unit: 'Wh',
})
- ) as unknown as NonNullable<EvseStatus['MeterValues']>
+ )
setupConnectorWithTransaction(mockStation, 1, { transactionId: 'tx-coherent' })
if (mockStation.stationInfo != null) mockStation.stationInfo.meteringPerTransaction = true
const coherentConnectorStatus = mockStation.getConnectorStatus(1)
unit: 'Wh',
value: '10',
},
- ] as unknown as NonNullable<EvseStatus['MeterValues']>
+ ]
upsertConfigurationKey(mockStation, ALIGNED_DATA_INTERVAL_KEY, '90')
upsertConfigurationKey(mockStation, ALIGNED_ENABLED_KEY, 'true')
upsertConfigurationKey(mockStation, SEND_DURING_IDLE_KEY, 'false')
unit: 'Wh',
value: '1000',
},
- ] as unknown as NonNullable<EvseStatus['MeterValues']>
+ ]
upsertConfigurationKey(mockStation, ALIGNED_DATA_INTERVAL_KEY, '60')
upsertConfigurationKey(mockStation, ALIGNED_ENABLED_KEY, 'true')
upsertConfigurationKey(mockStation, SEND_DURING_IDLE_KEY, 'false')
mock.method(mockStation, 'getConnectorMaximumAvailablePower', () => 3600)
evseStatus.MeterValues = [
{ measurand: OCPP20MeasurandEnumType.ENERGY_ACTIVE_IMPORT_REGISTER, unit: 'Wh' },
- ] as unknown as NonNullable<EvseStatus['MeterValues']>
+ ]
upsertConfigurationKey(mockStation, ALIGNED_DATA_INTERVAL_KEY, '60')
upsertConfigurationKey(mockStation, ALIGNED_ENABLED_KEY, 'true')
upsertConfigurationKey(mockStation, SEND_DURING_IDLE_KEY, 'false')
unit: 'V',
value: '230',
},
- ] as unknown as NonNullable<EvseStatus['MeterValues']>
+ ]
upsertConfigurationKey(mockStation, ALIGNED_DATA_INTERVAL_KEY, '60')
upsertConfigurationKey(mockStation, ALIGNED_ENABLED_KEY, 'true')
upsertConfigurationKey(mockStation, SEND_DURING_IDLE_KEY, 'false')
unit: 'V',
value: '230',
},
- ] as unknown as NonNullable<EvseStatus['MeterValues']>
+ ]
upsertConfigurationKey(mockStation, ALIGNED_DATA_INTERVAL_KEY, '60')
upsertConfigurationKey(mockStation, ALIGNED_ENABLED_KEY, 'true')
upsertConfigurationKey(mockStation, SEND_DURING_IDLE_KEY, 'false')
unit: 'Wh',
value: '1000',
},
- ] as unknown as NonNullable<EvseStatus['MeterValues']>
+ ]
upsertConfigurationKey(mockStation, ALIGNED_DATA_INTERVAL_KEY, '60')
upsertConfigurationKey(mockStation, ALIGNED_ENABLED_KEY, 'true')
setupConnectorWithTransaction(mockStation, 1, {
unit: 'Wh',
value: '1000',
},
- ] as unknown as NonNullable<EvseStatus['MeterValues']>
+ ]
upsertConfigurationKey(mockStation, ALIGNED_DATA_INTERVAL_KEY, '60')
upsertConfigurationKey(mockStation, ALIGNED_ENABLED_KEY, 'true')
upsertConfigurationKey(
measurand: OCPP20MeasurandEnumType.ENERGY_ACTIVE_IMPORT_REGISTER,
unit: 'Wh',
},
- ] as unknown as EvseStatus['MeterValues']
+ ]
stationConnectorStatus.energyActiveImportRegisterValue = 7
void OCPP20ServiceUtils.emitClockAlignedMeterValues(mockStation)
measurand: OCPP20MeasurandEnumType.POWER_ACTIVE_IMPORT,
unit: 'W',
},
- ] as unknown as EvseStatus['MeterValues']
+ ]
sourceEvse.MeterValues = [
{
fluctuationPercent: 0,
unit: 'W',
value: '800',
},
- ] as unknown as EvseStatus['MeterValues']
+ ]
setupConnectorWithTransaction(mockStation, 1, {
transactionId: `tx-station-power-${location}`,
})
measurand: OCPP20MeasurandEnumType.POWER_ACTIVE_IMPORT,
unit: 'W',
},
- ] as unknown as EvseStatus['MeterValues']
+ ]
sourceEvse.MeterValues = [
{
fluctuationPercent: 0,
unit: 'W',
value: '800',
},
- ] as unknown as EvseStatus['MeterValues']
+ ]
setupConnectorWithTransaction(mockStation, 1, {
transactionId: 'tx-station-outlet-power',
})
measurand: OCPP20MeasurandEnumType.ENERGY_ACTIVE_IMPORT_INTERVAL,
unit: 'Wh',
},
- ] as unknown as EvseStatus['MeterValues']
+ ]
sourceEvse.MeterValues = [
{
fluctuationPercent: 0,
unit: 'Wh',
value: '800',
},
- ] as unknown as EvseStatus['MeterValues']
+ ]
setupConnectorWithTransaction(mockStation, 1, { transactionId: 'tx-station-energy' })
const connectorStatus = mockStation.getConnectorStatus(1, 1)
assert.ok(connectorStatus != null)
fluctuationPercent: 0,
location: OCPP20LocationEnumType.Inlet,
measurand,
- })) as NonNullable<EvseStatus['MeterValues']>
+ }))
sourceEvse.MeterValues = [
{
fluctuationPercent: 0,
location: OCPP20LocationEnumType.Outlet,
measurand,
phase: MeterValuePhase.L1_N,
- })) as NonNullable<EvseStatus['MeterValues']>
+ }))
sourceEvse.MeterValues = [
{
fluctuationPercent: 0,
assert.ok(sourceEvse != null)
stationEvse.MeterValues = [
{ measurand: OCPP20MeasurandEnumType.POWER_ACTIVE_IMPORT, unit: 'W' },
- ] as unknown as EvseStatus['MeterValues']
+ ]
sourceEvse.MeterValues = [
MeterValuePhase.L1_N,
MeterValuePhase.L2_N,
phase,
unit: 'W',
value: '1000',
- })) as unknown as EvseStatus['MeterValues']
+ }))
setupConnectorWithTransaction(mockStation, 1, { transactionId: 'tx-phased-power' })
void OCPP20ServiceUtils.emitClockAlignedMeterValues(mockStation)
unit: 'W',
value: '9000',
},
- ] as unknown as EvseStatus['MeterValues']
+ ]
sourceEvse.MeterValues = [
{
fluctuationPercent: 0,
unit: 'W',
value: '1000',
},
- ] as unknown as EvseStatus['MeterValues']
+ ]
setupConnectorWithTransaction(mockStation, 1, { transactionId: 'tx-partial-phase' })
void OCPP20ServiceUtils.emitClockAlignedMeterValues(mockStation)
unit: 'varh',
value: '1000',
},
- ] as unknown as EvseStatus['MeterValues']
+ ]
void OCPP20ServiceUtils.emitClockAlignedMeterValues(mockStation)
assert.ok(stationEvse != null)
assert.ok(sourceEvse != null)
stationEvse.MeterValues = [
- { measurand: OCPP20MeasurandEnumType.CURRENT_IMPORT, unit: 'A' },
- ] as unknown as EvseStatus['MeterValues']
+ { measurand: OCPP20MeasurandEnumType.CURRENT_IMPORT, unit: OCPP20UnitEnumType.AMP },
+ ]
sourceEvse.MeterValues = [
MeterValuePhase.L1_N,
MeterValuePhase.L2_N,
phase,
unit: 'A',
value: '5',
- })) as unknown as EvseStatus['MeterValues']
+ }))
setupConnectorWithTransaction(mockStation, 1, { transactionId: 'tx-current' })
void OCPP20ServiceUtils.emitClockAlignedMeterValues(mockStation)
const stationEvse = mockStation.getEvseStatus(0)
assert.ok(stationEvse != null)
stationEvse.MeterValues = [
- { measurand: OCPP20MeasurandEnumType.CURRENT_IMPORT, unit: 'A' },
- ] as unknown as EvseStatus['MeterValues']
+ { measurand: OCPP20MeasurandEnumType.CURRENT_IMPORT, unit: OCPP20UnitEnumType.AMP },
+ ]
const phaseValues = [
[MeterValuePhase.L1_N, 10],
[MeterValuePhase.L2_N, 20],
unit: 'A',
value: value.toString(),
},
- ] as unknown as EvseStatus['MeterValues']
+ ]
setupConnectorWithTransaction(mockStation, evseId, {
transactionId: `tx-cross-evse-current-${evseId.toString()}`,
})
] as const
stationEvse.MeterValues = measurands.map(measurand => ({
measurand,
- })) as EvseStatus['MeterValues']
+ }))
const phaseValues = [
[MeterValuePhase.L1, 10],
[MeterValuePhase.L1_N, 20],
unit: measurand === OCPP20MeasurandEnumType.CURRENT_IMPORT ? 'A' : 'W',
value: value.toString(),
}))
- ) as unknown as EvseStatus['MeterValues']
+ )
setupConnectorWithTransaction(mockStation, 1, { transactionId: 'tx-phase-aliases' })
const connectorStatus = mockStation.getConnectorStatus(1, 1)
assert.ok(connectorStatus != null)
assert.ok(phasedEvse != null)
stationEvse.MeterValues = [
{ measurand: OCPP20MeasurandEnumType.POWER_ACTIVE_IMPORT, unit: 'W' },
- ] as unknown as EvseStatus['MeterValues']
+ ]
aggregateEvse.MeterValues = [
{
fluctuationPercent: 0,
unit: 'W',
value: '3000',
},
- ] as unknown as EvseStatus['MeterValues']
+ ]
phasedEvse.MeterValues = [
MeterValuePhase.L1_N,
MeterValuePhase.L2_N,
phase,
unit: 'W',
value: '1000',
- })) as unknown as EvseStatus['MeterValues']
+ }))
setupConnectorWithTransaction(mockStation, 1, { transactionId: 'tx-aggregate-power' })
setupConnectorWithTransaction(mockStation, 2, { transactionId: 'tx-phased-power' })
unit: 'W',
value: '2000',
},
- ] as unknown as EvseStatus['MeterValues']
+ ]
}
setupConnectorWithTransaction(mockStation, 1, { transactionId: 'tx-custom-data' })
measurand: OCPP20MeasurandEnumType.POWER_ACTIVE_IMPORT,
unit: 'W',
},
- ] as unknown as EvseStatus['MeterValues']
+ ]
sourceEvse.MeterValues = []
connector1.MeterValues = [
{
unit: 'W',
value: '1000',
},
- ] as unknown as ConnectorStatus['MeterValues']
+ ]
connector2.MeterValues = [
{
customData: (() => {
measurand: OCPP20MeasurandEnumType.ENERGY_ACTIVE_IMPORT_REGISTER,
unit: 'Wh',
})
- ) as unknown as EvseStatus['MeterValues']
+ )
connectorStatus.energyActiveImportRegisterValue = 800
upsertConfigurationKey(
mockStation,
unit: 'Wh',
value: '800',
},
- ] as unknown as EvseStatus['MeterValues']
+ ]
setupConnectorWithTransaction(mockStation, 1, { transactionId: 'tx-main-energy' })
upsertConfigurationKey(
mockStation,
value: '230',
},
{ measurand: OCPP20MeasurandEnumType.ENERGY_ACTIVE_IMPORT_REGISTER, unit: 'Wh' },
- ] as unknown as EvseStatus['MeterValues']
+ ]
upsertConfigurationKey(
mockStation,
buildConfigKey(OCPP20ComponentName.AlignedDataCtrlr, OCPP20RequiredVariableName.Measurands),
assert.ok(evseStatus != null)
evseStatus.MeterValues = [
{ measurand: OCPP20MeasurandEnumType.ENERGY_ACTIVE_IMPORT_REGISTER, unit: 'Wh' },
- ] as unknown as EvseStatus['MeterValues']
+ ]
setupConnectorWithTransaction(mockStation, 1, {
transactionId: '00000000-0000-4000-8000-000000000123',
})
unit: 'Wh',
value: '10',
},
- ] as unknown as EvseStatus['MeterValues']
+ ]
setupConnectorWithTransaction(mockStation, 1, { transactionId: 'tx-shared-energy-1' })
setupConnectorWithTransaction(mockStation, 2, { transactionId: 'tx-shared-energy-2' })
const connector1 = mockStation.getConnectorStatus(1, 1)
unit: 'Wh',
value: '60',
},
- ] as unknown as EvseStatus['MeterValues']
+ ]
upsertConfigurationKey(
mockStation,
TX_UPDATED_MEASURANDS_KEY,
unit: 'Wh',
value: '60',
},
- ] as unknown as EvseStatus['MeterValues']
+ ]
upsertConfigurationKey(
mockStation,
TX_UPDATED_MEASURANDS_KEY,
unit: 'Wh',
value: '10',
},
- ] as unknown as EvseStatus['MeterValues']
+ ]
upsertConfigurationKey(
mockStation,
TX_UPDATED_MEASURANDS_KEY,
unit: 'Wh',
value: '60',
},
- ] as unknown as EvseStatus['MeterValues']
+ ]
upsertConfigurationKey(
mockStation,
TX_UPDATED_MEASURANDS_KEY,
unit: 'Wh',
value: '60',
},
- ] as unknown as EvseStatus['MeterValues']
+ ]
upsertConfigurationKey(
mockStation,
TX_UPDATED_MEASURANDS_KEY,
measurand: OCPP20MeasurandEnumType.ENERGY_ACTIVE_IMPORT_REGISTER,
unit: 'Wh',
},
- ] as unknown as EvseStatus['MeterValues']
+ ]
upsertConfigurationKey(
mockStation,
TX_UPDATED_MEASURANDS_KEY,
measurand: OCPP20MeasurandEnumType.ENERGY_ACTIVE_IMPORT_REGISTER,
unit: 'Wh',
},
- ] as unknown as EvseStatus['MeterValues']
+ ]
upsertConfigurationKey(
mockStation,
TX_UPDATED_MEASURANDS_KEY,
measurand: OCPP20MeasurandEnumType.ENERGY_ACTIVE_IMPORT_INTERVAL,
unit: 'Wh',
},
- ] as unknown as EvseStatus['MeterValues']
+ ]
upsertConfigurationKey(
mockStation,
TX_UPDATED_MEASURANDS_KEY,
phase,
unit: 'Wh',
value: '90',
- })) as unknown as EvseStatus['MeterValues']
+ }))
upsertConfigurationKey(
mockStation,
TX_UPDATED_MEASURANDS_KEY,
measurand: OCPP20MeasurandEnumType.ENERGY_ACTIVE_IMPORT_REGISTER,
unit: 'Wh',
},
- ] as unknown as EvseStatus['MeterValues']
+ ]
upsertConfigurationKey(
mockStation,
TX_UPDATED_MEASURANDS_KEY,
measurand: OCPP20MeasurandEnumType.ENERGY_ACTIVE_IMPORT_INTERVAL,
unit: 'Wh',
},
- ] as unknown as EvseStatus['MeterValues']
+ ]
evseStatus.MeterValues = [
{
fluctuationPercent: 0,
measurand: OCPP20MeasurandEnumType.ENERGY_ACTIVE_IMPORT_INTERVAL,
unit: 'Wh',
},
- ] as unknown as EvseStatus['MeterValues']
+ ]
upsertConfigurationKey(
mockStation,
TX_UPDATED_MEASURANDS_KEY,
unit: 'Wh',
value: '10',
},
- ] as unknown as EvseStatus['MeterValues']
+ ]
upsertConfigurationKey(
mockStation,
TX_UPDATED_MEASURANDS_KEY,
unit: 'Wh',
value: '10',
},
- ] as unknown as EvseStatus['MeterValues']
+ ]
upsertConfigurationKey(
mockStation,
TX_UPDATED_MEASURANDS_KEY,
}
stationEvse.MeterValues = [
{ measurand: OCPP20MeasurandEnumType.ENERGY_ACTIVE_IMPORT_INTERVAL, unit: 'Wh' },
- ] as unknown as EvseStatus['MeterValues']
- evseStatus.MeterValues = [intervalTemplate] as unknown as EvseStatus['MeterValues']
+ ]
+ evseStatus.MeterValues = [intervalTemplate]
upsertConfigurationKey(mockStation, ALIGNED_DATA_INTERVAL_KEY, '60')
upsertConfigurationKey(
mockStation,
unit: 'Wh',
value: '10',
},
- ] as unknown as EvseStatus['MeterValues']
+ ]
upsertConfigurationKey(mockStation, ALIGNED_DATA_INTERVAL_KEY, '60')
upsertConfigurationKey(mockStation, ALIGNED_ENABLED_KEY, 'true')
upsertConfigurationKey(
unit: 'Wh',
value: '10',
},
- ] as unknown as EvseStatus['MeterValues']
+ ]
upsertConfigurationKey(
mockStation,
ALIGNED_MEASURANDS_KEY,
location: OCPP20LocationEnumType.Inlet,
measurand: OCPP20MeasurandEnumType.ENERGY_ACTIVE_IMPORT_INTERVAL,
unit: 'Wh',
- })) as unknown as EvseStatus['MeterValues']
+ }))
evseStatus.MeterValues = [
{
fluctuationPercent: 0,
unit: 'Wh',
value: '10',
})),
- ] as unknown as EvseStatus['MeterValues']
+ ]
upsertConfigurationKey(mockStation, ALIGNED_DATA_INTERVAL_KEY, '60')
upsertConfigurationKey(mockStation, ALIGNED_ENABLED_KEY, 'true')
upsertConfigurationKey(
unit: 'Wh',
value: '10',
},
- ] as unknown as EvseStatus['MeterValues']
+ ]
upsertConfigurationKey(mockStation, ALIGNED_DATA_INTERVAL_KEY, '60')
upsertConfigurationKey(mockStation, ALIGNED_ENABLED_KEY, 'true')
upsertConfigurationKey(
unit: 'Wh',
value: '10',
},
- ] as unknown as EvseStatus['MeterValues']
+ ]
upsertConfigurationKey(
mockStation,
TX_UPDATED_MEASURANDS_KEY,
measurand: OCPP20MeasurandEnumType.POWER_ACTIVE_IMPORT,
unit: 'W',
},
- ] as unknown as EvseStatus['MeterValues']
+ ]
evseStatus.MeterValues = [
{
fluctuationPercent: 0,
measurand: OCPP20MeasurandEnumType.POWER_ACTIVE_IMPORT,
unit: 'W',
},
- ] as unknown as EvseStatus['MeterValues']
+ ]
upsertConfigurationKey(mockStation, ALIGNED_DATA_INTERVAL_KEY, '60')
upsertConfigurationKey(mockStation, ALIGNED_ENABLED_KEY, 'true')
upsertConfigurationKey(
measurand: OCPP20MeasurandEnumType.ENERGY_ACTIVE_IMPORT_INTERVAL,
unit: 'Wh',
},
- ] as unknown as EvseStatus['MeterValues']
+ ]
upsertConfigurationKey(
mockStation,
TX_UPDATED_MEASURANDS_KEY,
unit: 'varh',
value: '1000',
},
- ] as unknown as EvseStatus['MeterValues']
+ ]
stationEvse.MeterValues = [
{ measurand: OCPP20MeasurandEnumType.ENERGY_REACTIVE_IMPORT_REGISTER, unit: 'varh' },
- ] as unknown as EvseStatus['MeterValues']
+ ]
setupConnectorWithTransaction(mockStation, 1, { transactionId: 'tx-shared-register-1' })
setupConnectorWithTransaction(mockStation, 2, { transactionId: 'tx-shared-register-2' })
measurand: OCPP20MeasurandEnumType.ENERGY_ACTIVE_IMPORT_REGISTER,
unit: 'Wh',
},
- ] as unknown as EvseStatus['MeterValues']
+ ]
const measurandsKey = buildConfigKey(
OCPP20ComponentName.AlignedDataCtrlr,
OCPP20RequiredVariableName.Measurands
unit: 'Wh',
value: '80',
},
- ] as unknown as EvseStatus['MeterValues']
+ ]
setupConnectorWithTransaction(mockStation, 1, { transactionId: 'tx-dc-inlet-interval' })
const connectorStatus = mockStation.getConnectorStatus(1, 1)
assert.ok(connectorStatus != null)
},
{ measurand: OCPP20MeasurandEnumType.VOLTAGE },
{ measurand: OCPP20MeasurandEnumType.ENERGY_ACTIVE_IMPORT_REGISTER, unit: 'Wh' },
- ] as unknown as NonNullable<EvseStatus['MeterValues']>
+ ]
const measurandsKey = buildConfigKey(
OCPP20ComponentName.AlignedDataCtrlr,
OCPP20RequiredVariableName.Measurands
unit: 'Hz',
value: '50',
},
- ] as unknown as NonNullable<EvseStatus['MeterValues']>
+ ]
const measurandsKey = buildConfigKey(
OCPP20ComponentName.AlignedDataCtrlr,
OCPP20RequiredVariableName.Measurands
measurand: OCPP20MeasurandEnumType.ENERGY_ACTIVE_IMPORT_INTERVAL,
phase,
unit: 'Wh',
- })) as unknown as EvseStatus['MeterValues'],
+ })),
timestamp: new Date(60_000),
},
60_000,
phase,
unit: 'Wh',
value: '90',
- })) as unknown as EvseStatus['MeterValues']
+ }))
const measurandsKey = buildConfigKey(
OCPP20ComponentName.AlignedDataCtrlr,
OCPP20RequiredVariableName.Measurands
phase,
unit: 'Wh',
value: '90',
- })) as unknown as EvseStatus['MeterValues']
+ }))
setupConnectorWithTransaction(mockStation, 1, { transactionId: 'tx-aligned-phase-interval' })
const connectorStatus = mockStation.getConnectorStatus(1, 1)
assert.ok(connectorStatus != null)
measurand: OCPP20MeasurandEnumType.ENERGY_ACTIVE_IMPORT_REGISTER,
unit: 'Wh',
},
- ] as unknown as NonNullable<EvseStatus['MeterValues']>
+ ]
setupConnectorWithTransaction(mockStation, 1, { transactionId: 'tx-phase-only' })
const measurandsKey = buildConfigKey(
OCPP20ComponentName.AlignedDataCtrlr,
value: '1000',
},
{ measurand: OCPP20MeasurandEnumType.ENERGY_ACTIVE_IMPORT_REGISTER, unit: 'Wh' },
- ] as unknown as NonNullable<EvseStatus['MeterValues']>
+ ]
connector2.MeterValues = [
{
fluctuationPercent: 0,
value: '2000',
},
{ measurand: OCPP20MeasurandEnumType.ENERGY_ACTIVE_IMPORT_REGISTER, unit: 'Wh' },
- ] as unknown as NonNullable<EvseStatus['MeterValues']>
+ ]
setupConnectorWithTransaction(mockStation, 2, { transactionId: 'tx-2' })
const measurandsKey = buildConfigKey(
OCPP20ComponentName.AlignedDataCtrlr,
unit: 'Wh',
value: '100',
},
- ] as unknown as NonNullable<EvseStatus['MeterValues']>
+ ]
connector2.MeterValues = [
{
customData: { vendorId: 'connector-2' },
unit: 'Wh',
value: '200',
},
- ] as unknown as NonNullable<EvseStatus['MeterValues']>
+ ]
setupConnectorWithTransaction(mockStation, 2, { transactionId: 'tx-2' })
upsertConfigurationKey(
mockStation,
value: '211',
},
{ measurand: OCPP20MeasurandEnumType.ENERGY_ACTIVE_IMPORT_REGISTER, unit: 'Wh' },
- ] as unknown as NonNullable<EvseStatus['MeterValues']>
+ ]
connector2.MeterValues = [
{
fluctuationPercent: 0,
value: '240',
},
{ measurand: OCPP20MeasurandEnumType.ENERGY_ACTIVE_IMPORT_REGISTER, unit: 'Wh' },
- ] as unknown as NonNullable<EvseStatus['MeterValues']>
+ ]
const measurandsKey = buildConfigKey(
OCPP20ComponentName.AlignedDataCtrlr,
OCPP20RequiredVariableName.Measurands
phase: MeterValuePhase.L2_N,
unit: 'V',
},
- ] as unknown as NonNullable<EvseStatus['MeterValues']>
+ ]
const invalidSinglePhaseMeterValue = buildClockAlignedConnectorMeterValue(
mockStation,
{ connectorId: 1, evseId: 1 },
phase: MeterValuePhase.N,
unit: 'V',
},
- ] as unknown as NonNullable<EvseStatus['MeterValues']>
+ ]
const dcMeterValue = buildClockAlignedConnectorMeterValue(
mockStation,
{ connectorId: 1, evseId: 1 },
value: '230',
},
{ measurand: OCPP20MeasurandEnumType.ENERGY_ACTIVE_IMPORT_REGISTER, unit: 'Wh' },
- ] as unknown as NonNullable<EvseStatus['MeterValues']>
+ ]
const measurandsKey = buildConfigKey(
OCPP20ComponentName.AlignedDataCtrlr,
OCPP20RequiredVariableName.Measurands
false
)
})
+
+ await it('should preserve custom unit metadata and physical values on automatic voltage phases', () => {
+ // Arrange
+ const { mockStation } = createAlignedStation({ connectorsCount: 1, evsesCount: 1 })
+ mock.method(mockStation, 'getNumberOfPhases', () => 3)
+ assert.ok(mockStation.stationInfo != null)
+ mockStation.stationInfo.currentOutType = CurrentType.AC
+ mockStation.stationInfo.mainVoltageMeterValues = false
+ const evseStatus = mockStation.getEvseStatus(1)
+ assert.ok(evseStatus != null)
+ evseStatus.MeterValues = [
+ {
+ fluctuationPercent: 0,
+ measurand: OCPP20MeasurandEnumType.VOLTAGE,
+ unit: 'custom-voltage',
+ unitOfMeasure: {
+ customData: { vendorId: 'custom-meter' },
+ multiplier: 1,
+ unit: 'custom-voltage',
+ },
+ value: '23',
+ },
+ ]
+ const measurandsKey = buildConfigKey(
+ OCPP20ComponentName.AlignedDataCtrlr,
+ OCPP20RequiredVariableName.Measurands
+ )
+ upsertConfigurationKey(mockStation, measurandsKey, OCPP20MeasurandEnumType.VOLTAGE)
+
+ // Act
+ const meterValue = buildClockAlignedConnectorMeterValue(
+ mockStation,
+ { connectorId: 1, evseId: 1 },
+ 60_000,
+ measurandsKey,
+ OCPP20ReadingContextEnumType.SAMPLE_CLOCK
+ )
+
+ // Assert
+ assert.deepStrictEqual(
+ meterValue.sampledValue.map(sample => ({
+ phase: sample.phase,
+ physicalValue: sample.value * 10,
+ unitOfMeasure: sample.unitOfMeasure,
+ })),
+ [MeterValuePhase.L1_N, MeterValuePhase.L2_N, MeterValuePhase.L3_N].map(phase => ({
+ phase,
+ physicalValue: 23,
+ unitOfMeasure: {
+ customData: { vendorId: 'custom-meter' },
+ multiplier: 1,
+ unit: 'custom-voltage',
+ },
+ }))
+ )
+ })
+
await it('should not suppress automatic voltage phases across customData identities', () => {
const { mockStation } = createAlignedStation({ connectorsCount: 1, evsesCount: 1 })
mock.method(mockStation, 'getNumberOfPhases', () => 3)
unit: 'V',
value: '231',
},
- ] as unknown as NonNullable<EvseStatus['MeterValues']>
+ ]
const measurandsKey = buildConfigKey(
OCPP20ComponentName.AlignedDataCtrlr,
OCPP20RequiredVariableName.Measurands
import { afterEach, beforeEach, describe, it, mock } from 'node:test'
import type { CoherentSession } from '../../../../src/charging-station/meter-values/index.js'
-import type { ConnectorStatus, EmptyObject, EvseStatus } from '../../../../src/types/index.js'
+import type { ConnectorStatus, EmptyObject } from '../../../../src/types/index.js'
import { ChargingStation } from '../../../../src/charging-station/ChargingStation.js'
import {
ConnectorStatusEnum,
CurrentType,
ErrorType,
+ isOCPP20MeterValue,
OCPP20ChargingStateEnumType,
OCPP20ComponentName,
OCPP20IdTokenEnumType,
OCPPVersion,
PublicKeyWithSignedMeterValueEnumType,
type RequestParams,
- type SampledValueTemplate,
SigningMethodEnumType,
Voltage,
} from '../../../../src/types/index.js'
measurand: OCPP20MeasurandEnumType.ENERGY_ACTIVE_IMPORT_INTERVAL,
unit: 'Wh',
},
- ] as unknown as EvseStatus['MeterValues']
+ ]
addConfigurationKey(
station,
intervalBaselineKey,
const transactionId = generateUUID()
let online = false
const attemptedSequenceNumbers: number[] = []
- const malformedTriggerReason = 'Bogus' as OCPP20TriggerReasonEnumType
+ const malformedTriggerReason = 'Bogus' as unknown as OCPP20TriggerReasonEnumType
let successorPayload: OCPP20TransactionEventRequest | undefined
const malformedError = new OCPPError(
ErrorType.FORMAT_VIOLATION,
measurand: OCPP20MeasurandEnumType.ENERGY_ACTIVE_IMPORT_INTERVAL,
unit: 'Wh',
},
- ] as unknown as EvseStatus['MeterValues']
+ ]
addConfigurationKey(
station,
`${OCPP20ComponentName.OCPPCommCtrlr}.${OCPP20RequiredVariableName.MessageAttempts}.TransactionEvent`,
measurand: OCPP20MeasurandEnumType.ENERGY_ACTIVE_IMPORT_REGISTER,
unit: 'Wh',
value: '0',
- } as unknown as SampledValueTemplate,
+ },
20,
undefined,
undefined,
measurand: OCPP20MeasurandEnumType.ENERGY_ACTIVE_IMPORT_REGISTER,
unit: 'Wh',
value: '0',
- } as unknown as SampledValueTemplate,
+ },
20,
undefined,
undefined,
measurand: OCPP20MeasurandEnumType.ENERGY_ACTIVE_IMPORT_REGISTER,
unit: 'Wh',
value: '0',
- } as unknown as SampledValueTemplate,
+ },
2,
undefined,
undefined,
assert.deepStrictEqual(observedIntervals, [1000, 10_000])
})
+ await it('should send a periodic event without a MeterValue for an empty selection', async t => {
+ // Arrange
+ t.mock.timers.enable({ apis: ['setInterval'] })
+ const transactionId = generateUUID()
+ setupConnectorWithTransaction(mockStation, 1, { transactionId })
+ assert.ok(mockStation.stationInfo != null)
+ mockStation.stationInfo.coherentMeterValues = true
+ const session: CoherentSession = {
+ connectorId: 1,
+ currentType: CurrentType.AC,
+ numberOfPhases: 3,
+ profile: {
+ batteryCapacityWh: 40000,
+ chargingCurve: [
+ { powerFraction: 1, socPercent: 0 },
+ { powerFraction: 1, socPercent: 100 },
+ ],
+ id: 'empty-selection-test',
+ initialSocPercentMax: 30,
+ initialSocPercentMin: 30,
+ maxPowerW: 11000,
+ weight: 1,
+ },
+ rampUpDurationMs: 0,
+ sessionStartMs: Date.now(),
+ socPercent: 30,
+ transactionId,
+ voltageOutNominal: Voltage.VOLTAGE_230,
+ }
+ mockStation.__injectCoherentSession(transactionId, session)
+ addConfigurationKey(
+ mockStation,
+ buildConfigKey(
+ OCPP20ComponentName.SampledDataCtrlr,
+ OCPP20RequiredVariableName.TxUpdatedMeasurands
+ ),
+ '',
+ undefined,
+ { overwrite: true }
+ )
+
+ // Act
+ const meterValue = OCPP20ServiceUtils.buildTransactionMeterValue(
+ mockStation,
+ 1,
+ 1,
+ transactionId,
+ 1000,
+ buildConfigKey(
+ OCPP20ComponentName.SampledDataCtrlr,
+ OCPP20RequiredVariableName.TxUpdatedMeasurands
+ )
+ )
+ assert.deepStrictEqual(meterValue.sampledValue, [])
+
+ OCPP20ServiceUtils.startUpdatedMeterValues(mockStation, 1, 1000, 1)
+ t.mock.timers.tick(1000)
+ for (let index = 0; index < 10; index++) await flushMicrotasks()
+
+ // Assert
+ assert.strictEqual(sentRequests.length, 1)
+ assert.strictEqual(
+ sentRequests[0].payload.triggerReason,
+ OCPP20TriggerReasonEnumType.MeterValuePeriodic
+ )
+ assert.strictEqual(sentRequests[0].payload.meterValue, undefined)
+ })
+
await it('should not start timer when interval is zero', () => {
const connectorId = 1
mockTracking.station.getEvseIdByConnectorId(connectorId) ?? 1
)
if (evseStatus != null) {
- evseStatus.MeterValues = [{ unit: 'Wh' }] as unknown as ConnectorStatus['MeterValues']
+ evseStatus.MeterValues = [{ unit: OCPP20UnitEnumType.WATT_HOUR }]
}
addConfigurationKey(
// Arrange
const evseStatus = station.getEvseStatus(1)
if (evseStatus != null) {
- evseStatus.MeterValues = [{ unit: 'Wh' }] as unknown as ConnectorStatus['MeterValues']
+ evseStatus.MeterValues = [{ unit: OCPP20UnitEnumType.WATT_HOUR }]
}
const transactionId = generateUUID()
unit: 'Wh',
value: '1000',
},
- ] as unknown as ConnectorStatus['MeterValues']
+ ]
addConfigurationKey(
station,
buildConfigKey(
unit: 'Wh',
value: '1000000',
},
- ] as unknown as ConnectorStatus['MeterValues']
+ ]
addConfigurationKey(
station,
buildConfigKey(
60_000,
txUpdatedMeasurandsKey,
OCPP20ReadingContextEnumType.SAMPLE_PERIODIC
- ) as OCPP20MeterValue
+ )
+ assert.ok(isOCPP20MeterValue(meterValue))
const expectedEnergyDelta = Number(
(
(station.getConnectorMaximumAvailablePower(connectorId) * sampleInterval) /
// Arrange
const evseStatus = mockTracking.station.getEvseStatus(1)
if (evseStatus != null) {
- evseStatus.MeterValues = [{ unit: 'Wh' }] as unknown as ConnectorStatus['MeterValues']
+ evseStatus.MeterValues = [{ unit: OCPP20UnitEnumType.WATT_HOUR }]
}
const transactionId = generateUUID()
unit: 'Wh',
value: '1000',
},
- ] as unknown as ConnectorStatus['MeterValues']
+ ]
const transactionId = generateUUID()
setupConnectorWithTransaction(station, 1, { transactionId })
connectorStatus.transactionEnergyActiveImportRegisterValue = 1000
await it('should bind a nested post-restart CALL to the fresh station state', async () => {
const oldState = plumbing.getOrCreateStationState(stationA)
- const outerCommand = 'OuterInheritedCall' as Parameters<
+ const outerCommand = 'OuterInheritedCall' as unknown as Parameters<
OCPP16IncomingRequestService['incomingRequestHandler']
>[2]
- const nestedCommand = 'NestedRestartedCall' as Parameters<
+ const nestedCommand = 'NestedRestartedCall' as unknown as Parameters<
OCPP16IncomingRequestService['incomingRequestHandler']
>[2]
let freshState: OCPP16StationStateShape | undefined
import { addConfigurationKey, buildConfigKey } from '../../../src/charging-station/index.js'
import {
+ assertMeterValueBuilderResult,
buildClockAlignedConnectorMeterValue,
buildMeterValue,
} from '../../../src/charging-station/ocpp/OCPPServiceUtils.js'
import {
CurrentType,
+ type MeterValue,
MeterValueContext,
MeterValueLocation,
MeterValueMeasurand,
MeterValuePhase,
MeterValueUnit,
OCPP20ComponentName,
+ OCPP20LocationEnumType,
OCPP20OptionalVariableName,
OCPP20RequiredVariableName,
OCPPVersion,
measurand: MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER,
unit: 'Wh',
value: '0',
-} as unknown as SampledValueTemplate
+}
const socTemplate: SampledValueTemplate = {
measurand: MeterValueMeasurand.STATE_OF_CHARGE,
unit: 'Percent',
value: '75',
-} as unknown as SampledValueTemplate
+}
const voltageTemplate: SampledValueTemplate = {
measurand: MeterValueMeasurand.VOLTAGE,
unit: 'V',
value: '230',
-} as unknown as SampledValueTemplate
+}
const powerTemplate: SampledValueTemplate = {
measurand: MeterValueMeasurand.POWER_ACTIVE_IMPORT,
unit: 'W',
value: '11000',
-} as unknown as SampledValueTemplate
+}
const currentTemplate: SampledValueTemplate = {
measurand: MeterValueMeasurand.CURRENT_IMPORT,
unit: 'A',
value: '16',
-} as unknown as SampledValueTemplate
+}
await describe('buildMeterValue', async () => {
let station: ChargingStation
standardCleanup()
})
+ await it('should allow empty internal results but reject mixed version samples', () => {
+ const emptyMeterValue: MeterValue = { sampledValue: [], timestamp: new Date() }
+ assert.doesNotThrow(() => {
+ assertMeterValueBuilderResult(emptyMeterValue, OCPPVersion.VERSION_201)
+ })
+
+ const mixedMeterValue = {
+ sampledValue: [{ value: '1' }, { value: 1 }],
+ timestamp: new Date(),
+ } as unknown as MeterValue
+ assert.throws(() => {
+ assertMeterValueBuilderResult(mixedMeterValue, OCPPVersion.VERSION_201)
+ }, /do not match OCPP 2\.0\.1/u)
+ })
+
await describe('OCPP 1.6', async () => {
beforeEach(() => {
const { station: s } = createMockChargingStation({
measurand: MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER,
unit: MeterValueUnit.WATT_HOUR,
},
- ] as unknown as SampledValueTemplate[]
+ ]
const meterValue = buildMeterValue(station, TEST_TRANSACTION_ID, 0)
unit: 'Wh',
value: '1000',
},
- ] as unknown as SampledValueTemplate[]
+ ]
buildMeterValue(testStation, TEST_TRANSACTION_ID_STRING, 3_600_000)
unit: MeterValueUnit.WATT_HOUR,
value: '0',
},
- ] as unknown as SampledValueTemplate[]
+ ]
addConfigurationKey(
station,
buildConfigKey(OCPP20ComponentName.AlignedDataCtrlr, OCPP20RequiredVariableName.Measurands),
assert.deepStrictEqual(values, [[125], [0]])
})
+ await it('should preserve physical energy when consuming a multiplied snapshot baseline', () => {
+ const template: SampledValueTemplate = {
+ measurand: MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER,
+ unit: MeterValueUnit.WATT_HOUR,
+ unitOfMeasure: { multiplier: 1, unit: MeterValueUnit.WATT_HOUR },
+ }
+
+ const meterValue = buildClockAlignedConnectorMeterValue(
+ station,
+ {
+ connectorId: 1,
+ evseId: 1,
+ sampledValueBaseline: [
+ {
+ location: OCPP20LocationEnumType.Outlet,
+ measurand: MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER,
+ unitOfMeasure: { multiplier: -1, unit: MeterValueUnit.KILO_WATT_HOUR },
+ value: 2,
+ },
+ ],
+ sampledValueTemplates: [template],
+ transactionId: TEST_TRANSACTION_ID_STRING,
+ },
+ 60_000,
+ undefined,
+ MeterValueContext.SAMPLE_CLOCK
+ )
+
+ assert.strictEqual(meterValue.sampledValue[0]?.value, 20)
+ assert.deepStrictEqual(meterValue.sampledValue[0]?.unitOfMeasure, {
+ multiplier: 1,
+ unit: MeterValueUnit.WATT_HOUR,
+ })
+ })
+
+ await it('should preserve native unit descriptor families during snapshot phase suppression', () => {
+ addConfigurationKey(
+ station,
+ buildConfigKey(
+ OCPP20ComponentName.SampledDataCtrlr,
+ OCPP20OptionalVariableName.RegisterValuesWithoutPhases
+ ),
+ 'true',
+ undefined,
+ { overwrite: true }
+ )
+ const templates = ['sensor-a', 'sensor-b'].map((vendorId, index) => ({
+ measurand: MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER,
+ phase: index === 0 ? MeterValuePhase.L1_N : MeterValuePhase.L2_N,
+ unit: MeterValueUnit.WATT_HOUR,
+ unitOfMeasure: {
+ customData: { vendorId },
+ multiplier: index,
+ unit: MeterValueUnit.WATT_HOUR,
+ },
+ })) as SampledValueTemplate[]
+
+ const meterValue = buildClockAlignedConnectorMeterValue(
+ station,
+ {
+ connectorId: 1,
+ energyRegisterWhOverride: 100,
+ evseId: 1,
+ sampledValueBaseline: [],
+ sampledValueTemplates: templates,
+ transactionId: TEST_TRANSACTION_ID_STRING,
+ },
+ 60_000,
+ undefined,
+ MeterValueContext.SAMPLE_CLOCK
+ )
+
+ assert.deepStrictEqual(
+ meterValue.sampledValue.map(sample => [
+ sample.unitOfMeasure?.customData?.vendorId,
+ sample.unitOfMeasure?.multiplier,
+ sample.phase,
+ sample.value,
+ ]),
+ [
+ ['sensor-a', 0, undefined, 100],
+ ['sensor-b', 1, undefined, 10],
+ ]
+ )
+ })
+
await it('should suppress register phases by effective OCPP 2.0 output identity', () => {
addConfigurationKey(
station,
assert.strictEqual(result.enabled, true)
})
+ await it('should return disabled when configured method is invalid', () => {
+ const result = validateSigningPrerequisites(TEST_PUBLIC_KEY_HEX, 'invalid-signing-method')
+
+ assert.strictEqual(result.enabled, false)
+ assert.ok((result as { reason: string }).reason.includes('invalid'))
+ })
+
+ await it('should return disabled when configured method is empty', () => {
+ const result = validateSigningPrerequisites(TEST_PUBLIC_KEY_HEX, '')
+
+ assert.strictEqual(result.enabled, false)
+ assert.ok((result as { reason: string }).reason.includes('invalid'))
+ })
+
await it('should return disabled when public key is undefined', () => {
const result = validateSigningPrerequisites(undefined, undefined)
assert.strictEqual(result.enabled, false)
await it('should throw error for unsupported version', () => {
const { station: chargingStation } = createMockChargingStation({
- stationInfo: { ocppVersion: 'VERSION_15' as OCPPVersion },
+ stationInfo: { ocppVersion: 'VERSION_15' as unknown as OCPPVersion },
})
assert.throws(() => AuthComponentFactory.createAdapter(chargingStation), {
await it('should return false for unsupported type', () => {
const identifier: Identifier = {
- // @ts-expect-error: Testing invalid type
- type: 'UNSUPPORTED_TYPE',
+ type: 'UNSUPPORTED_TYPE' as unknown as IdentifierType,
value: 'VALUE',
}
})
})
+ await it('should reject an unknown procedure with an actionable error', async () => {
+ const broadcastServer = createBroadcastServer()
+ try {
+ const res = new MockServerResponse()
+
+ broadcastServer.emitRequest(buildProcedureRequest('unknownProcedure', {}), res)
+ await awaitFinish(res)
+
+ const payload = parseHttpResponsePayload(res)
+ assert.strictEqual(payload.status, ResponseStatus.FAILURE)
+ assert.strictEqual(payload.errorMessage, "Unsupported UI procedure: 'unknownProcedure'")
+ assert.strictEqual(payload.errorStack, undefined)
+ } finally {
+ broadcastServer.stop()
+ }
+ })
+
await it('should respond immediately to a synchronous non-broadcast procedure', async () => {
const broadcastServer = createBroadcastServer()
try {
standardCleanup,
withMockTimers,
} from '../../helpers/TestLifecycleHelpers.js'
+import { WebSocketReadyState } from '../mocks/MockWebSocket.js'
import { TEST_HASH_ID, TEST_UUID, TEST_UUID_2 } from './UIServerTestConstants.js'
import {
awaitFinish,
const config = createMockUIServerConfiguration()
const server = new TestableUIWebSocketServer(config)
const ws = createMockUIWebSocket()
- ws.readyState = 0
+ ws.readyState = WebSocketReadyState.CONNECTING
server.addResponseHandler(TEST_UUID, ws)
server.sendResponse([TEST_UUID, { status: ResponseStatus.SUCCESS }])
const config = createMockUIServerConfiguration()
const server = new TestableUIWebSocketServer(config)
const ws = createMockUIWebSocket()
- ws.readyState = 1
+ ws.readyState = WebSocketReadyState.OPEN
ws.send = (): void => {
throw new Error('WebSocket send error')
}
const service = server.getUIService(ProtocolVersion['0.0.1'])
- const request = createProtocolRequest(TEST_UUID, 'UnknownProcedure' as ProcedureName, {})
+ const request = createProtocolRequest(
+ TEST_UUID,
+ 'UnknownProcedure' as unknown as ProcedureName,
+ {}
+ )
assert.notStrictEqual(service, undefined)
if (service != null) {
// procedure has no schema to check against.
const response = await dispatchUntrustedPayload(
service,
- 'UnknownProcedure' as ProcedureNameType,
+ 'UnknownProcedure' as unknown as ProcedureNameType,
{ hashIds: 'not-an-array' }
)
[[package]]
name = "ast-serialize"
-version = "0.11.2"
+version = "0.12.1"
description = "Python bindings for mypy AST serialization"
optional = false
python-versions = ">=3.7"
groups = ["dev"]
files = [
- {file = "ast_serialize-0.11.2-cp314-cp314-pyemscripten_2026_0_wasm32.whl", hash = "sha256:f6a8dfc5ab204a706f6e5d39c6f77c18c27ef084fa2081803a64a9160ce89277"},
- {file = "ast_serialize-0.11.2-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:cb073bfa15742699d408ac50f60878383b5665ae1791d1b6799ea6f08633cd77"},
- {file = "ast_serialize-0.11.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1d6ad94edbe93bf1dabc06c9f37d55b898fdabc456aa6d7ced5e23c14f795f32"},
- {file = "ast_serialize-0.11.2-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40b2801cf2221bd922d9f69d2f0ebc373c3db47207315d525b2d87fa161a2af4"},
- {file = "ast_serialize-0.11.2-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fd666cebd6ab3b3c0fd348a6202c26e18a401ee34293c3804d3472266bc146f6"},
- {file = "ast_serialize-0.11.2-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0d01f61352c96370febf6c0dbd488dee9183a731fb2702170da9163ae317cded"},
- {file = "ast_serialize-0.11.2-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a0fd40c668b0fa19b8fdb61d9e63d547e2e19cfbfe053a51ef0b6c37070298a8"},
- {file = "ast_serialize-0.11.2-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:efa819d7c14c8e4153dcd84671826331538be7cbe460383fc6386f5eea5bd234"},
- {file = "ast_serialize-0.11.2-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:a9ffa8a197a721f07a352d0be6185f5b3e6f9aaebfdb66169ed652108531ae3b"},
- {file = "ast_serialize-0.11.2-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:00119a8fb8c1dc0f1fab023f4d8071fa49e3b0208ee54d589fd463c16ab0124e"},
- {file = "ast_serialize-0.11.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:0de02520c11391a026e62987a9aa2c3c2ff01545155059ddf0c4bdf2c5ecbe9f"},
- {file = "ast_serialize-0.11.2-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:b4e4558956b6a0fb35e18fba58f7d1810b1f2c0e6b52352572cd5dfb6b4ef33a"},
- {file = "ast_serialize-0.11.2-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:6061a54f39e82a9f2cbcb9c268fc441890e4818a6636473caa4f4063254e0750"},
- {file = "ast_serialize-0.11.2-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:85fbb01e83967a126d71f679f2b9528ef0912cb0854aa1a4657314c34e255b57"},
- {file = "ast_serialize-0.11.2-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:7aaaffc32905159774a107d3cf33dad59bd41b7a0d1bc9885532186753ee7439"},
- {file = "ast_serialize-0.11.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:08eda88a0f290a36c38cab33df8bf7e35eb95bc802ca5beb2c8fcda471a7d10c"},
- {file = "ast_serialize-0.11.2-cp314-cp314t-win32.whl", hash = "sha256:76cc294246e60a914326b4ca88c6a5ea89c064906614aaf1537ce82f09e9449f"},
- {file = "ast_serialize-0.11.2-cp314-cp314t-win_amd64.whl", hash = "sha256:43b51e6ebe6549bf21416c3c78ee886147b80875a87cc6f69e303dde0d75be0b"},
- {file = "ast_serialize-0.11.2-cp314-cp314t-win_arm64.whl", hash = "sha256:8df32ad4ff7843734a6c2f067ee974f6d3109ee5a2c3e1a9d2f79347bd282a9a"},
- {file = "ast_serialize-0.11.2-cp315-abi3.abi3t-macosx_10_12_x86_64.whl", hash = "sha256:ab924ba260efd7509492f272d4e236d24564033f20c005d7c63a107c6a76fc85"},
- {file = "ast_serialize-0.11.2-cp315-abi3.abi3t-macosx_11_0_arm64.whl", hash = "sha256:a586be418eb70a9f1396cea29ddac8f4b9bf277fb73ea2340db31e218bc00f32"},
- {file = "ast_serialize-0.11.2-cp315-abi3.abi3t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8532f20916fa3189d4d785ef2a62d93c4d651ec9c5bffda66d2fc36898351f34"},
- {file = "ast_serialize-0.11.2-cp315-abi3.abi3t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ee732ae167e686d1d3c00f98d7d82b23138304694f0441b14d7ddf9c0f8a921c"},
- {file = "ast_serialize-0.11.2-cp315-abi3.abi3t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:75a1c7f46b9c19fc0ae01ca6fd076301628faa2ed7a8edbd55c6353c483946a3"},
- {file = "ast_serialize-0.11.2-cp315-abi3.abi3t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2fdf31a0bb85ea2575cc91669f005e6647d2efed491231c4dc1497bc9a5b3aa6"},
- {file = "ast_serialize-0.11.2-cp315-abi3.abi3t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b78e6fdef3b06c86ed263e1962fee5a7b9d2d158e738b212d13b2c605ee12f5"},
- {file = "ast_serialize-0.11.2-cp315-abi3.abi3t-manylinux_2_31_riscv64.whl", hash = "sha256:8d62a47714c8bc432b9fabcc29989c815c5da17327d35151f2fd0d85c2a7a5ff"},
- {file = "ast_serialize-0.11.2-cp315-abi3.abi3t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8a5ffa70e76191dcf240d3c43e20c93b3bfd26f54d89148c762d57837f5bcd2c"},
- {file = "ast_serialize-0.11.2-cp315-abi3.abi3t-musllinux_1_2_aarch64.whl", hash = "sha256:bfbe47a3a7c368f28836e78b2440a3643ac0ec4c67d9fe53588e1448f0a3d35d"},
- {file = "ast_serialize-0.11.2-cp315-abi3.abi3t-musllinux_1_2_armv7l.whl", hash = "sha256:7f1823275b246f9c7d373be6879e4eec09686948895d4ad083f4b27fd7e4da70"},
- {file = "ast_serialize-0.11.2-cp315-abi3.abi3t-musllinux_1_2_i686.whl", hash = "sha256:57c0f5cb0021a5beb1e5e4d6e840ae2f23a28909703ef4d256a144cc1ad3d437"},
- {file = "ast_serialize-0.11.2-cp315-abi3.abi3t-musllinux_1_2_ppc64le.whl", hash = "sha256:cd320a5c4f1f2742af97eea22954f776379175c5ef2504801e9a155f2ff9a4d7"},
- {file = "ast_serialize-0.11.2-cp315-abi3.abi3t-musllinux_1_2_riscv64.whl", hash = "sha256:13b13afe32e845c86a573497729e1b7ddeb26c572c78bf50ece51da23b8fad5e"},
- {file = "ast_serialize-0.11.2-cp315-abi3.abi3t-musllinux_1_2_x86_64.whl", hash = "sha256:9d80a81ec84660422579bdb8e789f656a794b48c7a1ae1261f6bd8bc1897d17d"},
- {file = "ast_serialize-0.11.2-cp315-abi3.abi3t-win32.whl", hash = "sha256:af8c003ce721b0099dd55cef4ba733500fc3054ea0cc8565d8957aaf7cccdeb4"},
- {file = "ast_serialize-0.11.2-cp315-abi3.abi3t-win_amd64.whl", hash = "sha256:554d117cb916d8032d85007c654d179efbbfd446174c048062778136a922944f"},
- {file = "ast_serialize-0.11.2-cp315-abi3.abi3t-win_arm64.whl", hash = "sha256:d60515335750d431e462af6e722bb55720a5e7827192777bddfd9c4376065a4d"},
- {file = "ast_serialize-0.11.2-cp315-cp315-pyemscripten_2026_5_wasm32.whl", hash = "sha256:89499a439955931281986e97ca4dd3c064bf0d2e0027c0017344eb86667733a1"},
- {file = "ast_serialize-0.11.2-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:daadf1c3e0224621607ffe16f1379e4bd372271ed2e1db8a67878f0bab3ef7e4"},
- {file = "ast_serialize-0.11.2-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:1844ed9a487fb3de7325c52ddb33f2918b66b65cd54d3f8d83d23785ffe99fa4"},
- {file = "ast_serialize-0.11.2-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b17869f4ba261a5fa468a753328a548f4dbaf74b4eadae9e28aff66df7f1425b"},
- {file = "ast_serialize-0.11.2-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:feb16d9c2a720e0120c58dd5d6e7b3c7c86b43249b60a3bc212bcb8fa031e2dd"},
- {file = "ast_serialize-0.11.2-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f3109fe4805384effc8d0f8e41fbf875aa8f389af91b4348c1cfb60ea6e4cb82"},
- {file = "ast_serialize-0.11.2-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:abdb3e49ba053c3486ac1263bee9f16cc9a4a8abd9f8c90bfc21e3669f3ad9d1"},
- {file = "ast_serialize-0.11.2-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a7004ba572f09be34342ccb98dcd4bad5707d3d81adc8cb4c3f685d2a2c51bbc"},
- {file = "ast_serialize-0.11.2-cp39-abi3-manylinux_2_31_riscv64.whl", hash = "sha256:59c25f47524efa052971b860e128b1add0c94ede7dd16b2962952c85c3582365"},
- {file = "ast_serialize-0.11.2-cp39-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f3a367e0e05ed2d1b747ceb07aa728a8c204cc008b589127e9bd4f40053d7575"},
- {file = "ast_serialize-0.11.2-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:00bbf1f6669f813b48925b759f7ae4591067d456d443924055cab386e7e0a719"},
- {file = "ast_serialize-0.11.2-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:ec1c20f89c3e0d83576e3c06f79375ce936266591fe0d5fd969914af3185cbaa"},
- {file = "ast_serialize-0.11.2-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:c58bb119b73657fdc5569692f316e1e25ca114bd62f7782eb527c6be438ba3a9"},
- {file = "ast_serialize-0.11.2-cp39-abi3-musllinux_1_2_ppc64le.whl", hash = "sha256:f739e0b601be7300c5697a2573d9200bd1db74b34ab111ef9537b9d5dcd7f106"},
- {file = "ast_serialize-0.11.2-cp39-abi3-musllinux_1_2_riscv64.whl", hash = "sha256:cae5addfbb54cc1d47fe947ef9138e9d83849ed1cbc72b819cf36d96a2315b07"},
- {file = "ast_serialize-0.11.2-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:2fa3be25f7f5351b1b39c9f8a52779b2dbf21199efbae564b4746422e8edca4e"},
- {file = "ast_serialize-0.11.2-cp39-abi3-win32.whl", hash = "sha256:d70556a2f9230a44c99a655774cde823f056efc34466eabfb4085f0cb1ea9f99"},
- {file = "ast_serialize-0.11.2-cp39-abi3-win_amd64.whl", hash = "sha256:b9065dd23131a23b41f5bab3bf4e9b3c350a3fe8e36e8200eded9b729fcea484"},
- {file = "ast_serialize-0.11.2-cp39-abi3-win_arm64.whl", hash = "sha256:dab599cbdcb7b45b18c41fad746645580b3a24357082b7f0e8921cd373804f27"},
- {file = "ast_serialize-0.11.2.tar.gz", hash = "sha256:976a5bd75845d22f4b52905ddf53ab669ef1b14dba7735f5512841a2ef2b5450"},
+ {file = "ast_serialize-0.12.1-cp314-cp314-pyemscripten_2026_0_wasm32.whl", hash = "sha256:e73255c9227fd74eac8a9b55c4049e8ad7b66d1f690bf827c98a86b2e594def7"},
+ {file = "ast_serialize-0.12.1-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:4655ef993e69e01bb47d2d99647de9bbb74af03938438656832cd010d95de348"},
+ {file = "ast_serialize-0.12.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:bac5a99a2c91dd823be9b8c44645694fccbb0750773cc5b27889a9f6f22fce89"},
+ {file = "ast_serialize-0.12.1-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6485e681625ed7a094221f16a7ff2ef154946112266a05cf83bde50c959ef345"},
+ {file = "ast_serialize-0.12.1-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a513bc6f60980d01767f7cbe39b17ce0373e722824a74ce28d6cea49ee3c8460"},
+ {file = "ast_serialize-0.12.1-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:82866f3523d53ffca8d2a69a750bec52908b69f728012e40959bebce2620453c"},
+ {file = "ast_serialize-0.12.1-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:243054a05a5190f5d087b5c8b16423e7f1cefa4991bac26e9c8ace18074b75b6"},
+ {file = "ast_serialize-0.12.1-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7d9fbe5a3e8acddfc2fddff3dbbc7ea0e9798b3df3428f851b8abc52a3806f31"},
+ {file = "ast_serialize-0.12.1-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:d3d516da3463071d27e64caf54d88cba25cf4ad4afcc807e0bcf67743719f03e"},
+ {file = "ast_serialize-0.12.1-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8d6711adf11136c77e3a35517de9488a5081d1012874fae99c2876b64f4daace"},
+ {file = "ast_serialize-0.12.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:cbe239bee4bd609186daf60b95b7b0f47146c7f7f55f6da83807d747d6fe753f"},
+ {file = "ast_serialize-0.12.1-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:5bbf582286c9dc6b4c544ef645dc99e4b3aa09db28892bc60344141f6926641f"},
+ {file = "ast_serialize-0.12.1-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:99e33c93efb5254a70c525b46038212371dfe5693d48eb2d0d5f17d936a263d7"},
+ {file = "ast_serialize-0.12.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:aa6c17a2b7f07e81fa8cfcc4aa7c832b3e57733853aebea113ab502f9b0963db"},
+ {file = "ast_serialize-0.12.1-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:f896fa38e0af38821e1ab1425c5dee89e359623e165765bdeae7d0eb6909e76d"},
+ {file = "ast_serialize-0.12.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:af699e81fd7ce80b8b03945826d8ea23dd36d072f10d4613402da597ba4ee9c6"},
+ {file = "ast_serialize-0.12.1-cp314-cp314t-win32.whl", hash = "sha256:10b59afc108eb285146acb23d1b5ec0fc58bb3c09cb2ab8876402df06c373c3b"},
+ {file = "ast_serialize-0.12.1-cp314-cp314t-win_amd64.whl", hash = "sha256:72e871f6995a066c1b19104f8a6b5832b1163adb9a8267c2aa4711fbb0f4d1f3"},
+ {file = "ast_serialize-0.12.1-cp314-cp314t-win_arm64.whl", hash = "sha256:3398e458047d21c9bc1b323fe5aab77c608dc9ddb65b2d44deebaff503a1f1eb"},
+ {file = "ast_serialize-0.12.1-cp315-abi3.abi3t-macosx_10_12_x86_64.whl", hash = "sha256:410233de149ab8414cb27c6fc73e9d2baa35d6f971672d540d752060d980ffb4"},
+ {file = "ast_serialize-0.12.1-cp315-abi3.abi3t-macosx_11_0_arm64.whl", hash = "sha256:b9a2310845302f1a6bd45ae8a67d5760211103a8d66410b854bfa440d107e093"},
+ {file = "ast_serialize-0.12.1-cp315-abi3.abi3t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ff65f40f49d5e1a1a043ba366081d59a4e26a9f5c1b07eb1170e172115da7ca"},
+ {file = "ast_serialize-0.12.1-cp315-abi3.abi3t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:536d783c4d91331f094e0a892221e619be5ffbe6fb6640885f14d7f226ec90ca"},
+ {file = "ast_serialize-0.12.1-cp315-abi3.abi3t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c42d2d65f388d1960c5796231eb9bf5a988c46228633eb489605c4549ad16c52"},
+ {file = "ast_serialize-0.12.1-cp315-abi3.abi3t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a5628a12acc875fe7a167910f18d101dd101c2a7b1e6c2b6f7289ffaff25805c"},
+ {file = "ast_serialize-0.12.1-cp315-abi3.abi3t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e855adfa5bb982b2e6fe09056b2d584f6dd4fce085d91a07d1155683751b6b5"},
+ {file = "ast_serialize-0.12.1-cp315-abi3.abi3t-manylinux_2_31_riscv64.whl", hash = "sha256:fafe1471e8aca6c87b4913b7b54ff97197adf702fbe28692284b929dfa62ff96"},
+ {file = "ast_serialize-0.12.1-cp315-abi3.abi3t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a5cac246474d2a703147d1605a6ac5ba0fa9e0a443cf1cf42513adf4df02686f"},
+ {file = "ast_serialize-0.12.1-cp315-abi3.abi3t-musllinux_1_2_aarch64.whl", hash = "sha256:6e25cd319fb0d7b39fcac666784ec86708ccbc78d07a698b1400cf5ed40c045b"},
+ {file = "ast_serialize-0.12.1-cp315-abi3.abi3t-musllinux_1_2_armv7l.whl", hash = "sha256:657a7354ea16ed4d29f8127ed477c6fee3915c111d135f020ca835a991438e90"},
+ {file = "ast_serialize-0.12.1-cp315-abi3.abi3t-musllinux_1_2_i686.whl", hash = "sha256:9eb9de7e59621acdb3e66984f374272b33d56d15a04763e2fd0604211e1c8303"},
+ {file = "ast_serialize-0.12.1-cp315-abi3.abi3t-musllinux_1_2_ppc64le.whl", hash = "sha256:09cc4d3103c1fc97f6845ba307af1db9cde5226bef47f8843220dde83f2276ba"},
+ {file = "ast_serialize-0.12.1-cp315-abi3.abi3t-musllinux_1_2_riscv64.whl", hash = "sha256:c9e2a592706fd791c2271ce9c8f4e38c98d3ea0b4a86b511e09a4fe3ac44ab37"},
+ {file = "ast_serialize-0.12.1-cp315-abi3.abi3t-musllinux_1_2_x86_64.whl", hash = "sha256:f4ac042e95a575432c1730ca4cb9183066a2074886a599f46c1f1b0955fb8198"},
+ {file = "ast_serialize-0.12.1-cp315-abi3.abi3t-win32.whl", hash = "sha256:b3cd105995942cc6163a229674a86161ba1305f646493efd59bd6723a357ee14"},
+ {file = "ast_serialize-0.12.1-cp315-abi3.abi3t-win_amd64.whl", hash = "sha256:a9cd24a26126088693ca054547ea0a391398a29cf1a3a2bec1009b4b6acc8b82"},
+ {file = "ast_serialize-0.12.1-cp315-abi3.abi3t-win_arm64.whl", hash = "sha256:9649cd903db0dc047906c6dd740784a2ba665d54f7e43ba31457edbce76c9493"},
+ {file = "ast_serialize-0.12.1-cp315-cp315-pyemscripten_2026_5_wasm32.whl", hash = "sha256:5ef62601db3ce5c23445132262a193075e211fb2fc87b46b7550dd351fac0976"},
+ {file = "ast_serialize-0.12.1-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:98d91cd3a6cb76a39512ee090a539d1e3206b732ad8150eb38918cffa1ddf515"},
+ {file = "ast_serialize-0.12.1-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:8a32f184ce3e4b1d0b06d642a1243281cf55b99e0323680b1b8f904029fd7700"},
+ {file = "ast_serialize-0.12.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e62126ac2be2d9340ac1b3ee7a0466a883ecbed634cff0929c88ca0b671483b7"},
+ {file = "ast_serialize-0.12.1-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0f93a70fa9826c04ea9f2c3a880f87f4cca09a828144ed5084682abd28110980"},
+ {file = "ast_serialize-0.12.1-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1858887be56a64a2aea899423dfe43787c34c75c18b0d7497de8e618d54b2790"},
+ {file = "ast_serialize-0.12.1-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:89a2bc39a820bc7785b60c53a5742b4e8dd4c1a599294e2dd68fae545883d44a"},
+ {file = "ast_serialize-0.12.1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:50a9eaedf1db4857dad7cc47dd757ed70bfcc40b89d44d516c4a2f0d5033bd76"},
+ {file = "ast_serialize-0.12.1-cp39-abi3-manylinux_2_31_riscv64.whl", hash = "sha256:c30b609e8fea426b310543126de876592236a25aa8ebd59f1e2b323dd52a4085"},
+ {file = "ast_serialize-0.12.1-cp39-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7b1ad06513022cfa1337744959255af0ef16119d2beb1e547b67f37ad9433d4a"},
+ {file = "ast_serialize-0.12.1-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:6add54b495e37ae3cf3a1f0d5eaba364814eb72e93026adc41b7791e4b0d45d3"},
+ {file = "ast_serialize-0.12.1-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:5fc136cd08001b817ad0b3e7426f50a7d2b8982dc7c6491f0af78af4c3dd8672"},
+ {file = "ast_serialize-0.12.1-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:45a9e6b700bbd973d49942668a5cdbeffa693f2e250b8a5409abd1fa9d351854"},
+ {file = "ast_serialize-0.12.1-cp39-abi3-musllinux_1_2_ppc64le.whl", hash = "sha256:a1d8267f83c613ea0a31f2518df074bd62e98a4b3a4892f6a529d74e08e02dba"},
+ {file = "ast_serialize-0.12.1-cp39-abi3-musllinux_1_2_riscv64.whl", hash = "sha256:9a0cbab9796e6ce841197feeba4008faa96b4cc7741129542fd81c882d7a4f01"},
+ {file = "ast_serialize-0.12.1-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:b4282695f1d51a3c6ef76560351bad5af880eff7d755aefea325bffb9bf68c25"},
+ {file = "ast_serialize-0.12.1-cp39-abi3-win32.whl", hash = "sha256:119d1b0cadaba4a6e475f9bbe79eecc79351e373142eabe7c353f0250d70aebb"},
+ {file = "ast_serialize-0.12.1-cp39-abi3-win_amd64.whl", hash = "sha256:3d6ed63d4fc1ec867b8cb522d58c36df0e8f05e487bea0ffd102043a37636d72"},
+ {file = "ast_serialize-0.12.1-cp39-abi3-win_arm64.whl", hash = "sha256:610a41351de68199de9a1434499083b4256c0df7658ec1cfc0a0a7b20b08d317"},
+ {file = "ast_serialize-0.12.1.tar.gz", hash = "sha256:5285a390caf1c44368ae270f037f797b91427d138b7d43cad0f1fda4c83518d9"},
]
[[package]]
[[package]]
name = "coverage"
-version = "7.16.1"
+version = "7.16.2"
description = "Code coverage measurement for Python"
optional = false
python-versions = ">=3.10"
groups = ["dev"]
files = [
- {file = "coverage-7.16.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4f12a9e27ca7b65e40a8475d27899b2d45064d9020e6a89148939e01987b5853"},
- {file = "coverage-7.16.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f590d46c30d9e4c1fda3efefe5443f4c2f6a4192c5ca2ba403653e9ebadf097"},
- {file = "coverage-7.16.1-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3df82f0a3cef4e1bcfc799436056f0b978dda319d0bfd4460c6e479b2802d98a"},
- {file = "coverage-7.16.1-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:bb462d59146656e278d1e8ed913ce374d0ba68a4e081acde1867f6d3377fc881"},
- {file = "coverage-7.16.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d7db888dd0a1df1a653cae7f99d4047430d2187a3626eda51bc847b0fd6b9b43"},
- {file = "coverage-7.16.1-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:38a7e16f061504ac2b45370bf5bf97e8250d8d3f25e37385bae884978166554b"},
- {file = "coverage-7.16.1-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:2b26b55b18e1a53e1a159dd743728c4ddbbef28ba19000a91aaff5ce023197ec"},
- {file = "coverage-7.16.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:fdb2f528b50953e29d22033b3256c396a700193c6e45b2490222ef9c333cbbf9"},
- {file = "coverage-7.16.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:3284754371dc78592aa3ae4d661d30ee20e02b2b6b0de3590a181a936d0d3b38"},
- {file = "coverage-7.16.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:802d1246c540e07486d4ee1adfa19a797e4b33529ffc371dc140644e8f27da0a"},
- {file = "coverage-7.16.1-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:6fc735d6fe6d57f803e7ba021be4dde48e43e6aff94e6954350555d5332b0594"},
- {file = "coverage-7.16.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ed5ade1bb18f62edace1bd198c66f9d4c75a8385d5fd24e87d917ea1a5958773"},
- {file = "coverage-7.16.1-cp310-cp310-win32.whl", hash = "sha256:0c309096926b119543dc16438a11ef4c80783d2f4e59ff94f7f462651a944cdc"},
- {file = "coverage-7.16.1-cp310-cp310-win_amd64.whl", hash = "sha256:7562f8067ed9360e8b9739e5703403a7686dd1b36bf0f89fc538047c54cdea90"},
- {file = "coverage-7.16.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:72e013665e25cf9d44779f01f340af26319756f9a76822b7c94ce6b1d93813da"},
- {file = "coverage-7.16.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:cb05c0ff98b56ba6969adf35556bc43bcb8d094df8bc9cb403acff53460c4e07"},
- {file = "coverage-7.16.1-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:0d0ececb32090e3fbb03e0d352b973a0485879b4de6c58daf47227b9988b99e5"},
- {file = "coverage-7.16.1-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f0ba3892d81aacf36996c52f16bca04e39af31a6c5de930b7688ab617f4a6475"},
- {file = "coverage-7.16.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a6410b75fe07d5271eaa95fc24bd0a9177ed588d9d1c10c0cf67829adb8f0567"},
- {file = "coverage-7.16.1-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:d1039cb2de093225d597109342ce1675626bd127565e80b3044f4eca07c15b2e"},
- {file = "coverage-7.16.1-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cf047bc39fde5425be2628666d0f435ed8817859111c3aacc84b32d858069f5d"},
- {file = "coverage-7.16.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2c05913d0d5badf7ac83200f35dcf9514cce5df16a7cc89e7d1d7fff0461813b"},
- {file = "coverage-7.16.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:4027bf6d7bc0a16df058ce913b69f10c5687f8e1ca668f08caa659ce101744bf"},
- {file = "coverage-7.16.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:4f48b345f831eaf4402ab6333c2dc3e2e2b5bc7b9c1b8fe12680dee3f0538f01"},
- {file = "coverage-7.16.1-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:8643baeb590726c558b2faed6cd59b0917480f9367fcc692026f1a86d824fd08"},
- {file = "coverage-7.16.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d06dcc420b570bf683cdb647cc8fe62b672d9e429ef711c3cbbb7a6880ca1572"},
- {file = "coverage-7.16.1-cp311-cp311-win32.whl", hash = "sha256:946f58aa59b08bcd6afcc6a7bd0ff54ed5eee844f32ad69fe6814836d15856a2"},
- {file = "coverage-7.16.1-cp311-cp311-win_amd64.whl", hash = "sha256:684c7ee9b4c04358fe6ac8b517ab51ec35fcd79d08ff0f105dd8bcd96885bbb7"},
- {file = "coverage-7.16.1-cp311-cp311-win_arm64.whl", hash = "sha256:1b24f79e25bcf6c73931aeca7a3dfc7595c0cb5e9364aba3fdf387a3de4b1c22"},
- {file = "coverage-7.16.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b7f2c26ce6ce0b1e0ca0d5fae96ea510e3a2e78b7207f06e76b7f2c87fa3d0af"},
- {file = "coverage-7.16.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:070acb9da788dff743a4d36fc015feee12d68f0349959017542017c79f59c21c"},
- {file = "coverage-7.16.1-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:e366587b370bc9b8b51b7b7272c610c56db5d5b4795b9e4a29d28ff2f440f809"},
- {file = "coverage-7.16.1-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:e82e10b9d290f60b63459cfb245a841aec347603997206296b93881463a93dcf"},
- {file = "coverage-7.16.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3d73bb1f85c4150ac208fb0755beb04b2e44897bad81414de9380f98dd74729f"},
- {file = "coverage-7.16.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3397b9032553d281ad6a9253b12675b65e0cc8cd7a3b0633cf48872c9eb13360"},
- {file = "coverage-7.16.1-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:77890395cf37026a5907d3ad32376aa51f41c0f163b7477fdbd4f94966cc1d08"},
- {file = "coverage-7.16.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b0944dc3bee3091039bf970d73caaf930c906013128a421bdc132e797494d941"},
- {file = "coverage-7.16.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b89d22a89d5bc05dd95b64e08295b8394aa96dc88e08f8ba210c9ebfebbe0489"},
- {file = "coverage-7.16.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:550a2a1faf7559f13d5344f12d1eb886ad87955155d7dfab2a3fe5c8ec8fe776"},
- {file = "coverage-7.16.1-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:55eb268e5b81aefac759766c9162625b06c1bedb7b77d936225bafc4f038a6f6"},
- {file = "coverage-7.16.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:65a8fc80898c9ce59f04349fe8b4849b1f9787f14e52ead990e5f849ff4727a0"},
- {file = "coverage-7.16.1-cp312-cp312-win32.whl", hash = "sha256:528a61be40977c340cf201d23b69bd6a6bab507da60e9dbda85f8b30e935d70d"},
- {file = "coverage-7.16.1-cp312-cp312-win_amd64.whl", hash = "sha256:d0f02c633630e2b74522108ee95a84ad6e1204a8016a6cca5297f335ea27147e"},
- {file = "coverage-7.16.1-cp312-cp312-win_arm64.whl", hash = "sha256:2959978f9d1d20a2c0c15d0a68baaeccf615ac1aa214cf4a05a10d6f568926c8"},
- {file = "coverage-7.16.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ee5465db6e9152a7d09f3215309326878c6aa3ac509195a369f9d264ff4bfbd9"},
- {file = "coverage-7.16.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2b8256f8b525ba233d2e4cdcdce0d6673c66fc9bf70df1fd5e67c54a74e2d245"},
- {file = "coverage-7.16.1-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:d57cc400275b9a2892e905fc893f732b21ddb95271bf96406c88e2f6367848b5"},
- {file = "coverage-7.16.1-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:6b3fd0f3435ebb7a7183b32a6062a8b755f08242ced1f3f22761d30b56b3c2a5"},
- {file = "coverage-7.16.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e5eb1762e7eb5fad34ef913e8107c7788a66f19d328e598ce95bf7217f9e5c8f"},
- {file = "coverage-7.16.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:46cd3a73e9140410de62cceb66214bce0e08fb3922b9176fbfc1522fec151b41"},
- {file = "coverage-7.16.1-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d1ba5142d68dd2cb775cbd0ac8601819298152047803c8efe4eec6d7d7aa7878"},
- {file = "coverage-7.16.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c389c6f9d1d518e1249ddcb8a7f158135644ce2c508fa6cc17b680777dad5bf2"},
- {file = "coverage-7.16.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cdc57746c7ac0ea063351b4d651c3bb4dd4fd35e64dbb8e90c10e14eb03c4080"},
- {file = "coverage-7.16.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:5597180ed7670cc94c04c65347418a467d3a43d5f0cf52fcac647f5425f42037"},
- {file = "coverage-7.16.1-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:a9647a0ac46255b8fef59a433a2161f03e5483f3a35e1cbd9dfe4600baff0c6b"},
- {file = "coverage-7.16.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e306e98186b9cd109121f3583aeb7978797ad21d948f22944c5c08845cd554d0"},
- {file = "coverage-7.16.1-cp313-cp313-win32.whl", hash = "sha256:48a78a66fcce49d7f6156524bf979c0ac633d584199717c68c6ffa949fc14e6a"},
- {file = "coverage-7.16.1-cp313-cp313-win_amd64.whl", hash = "sha256:7af03247d598a353bbbbe1b925deb735276e4d845e7197c4073dc89352b236fa"},
- {file = "coverage-7.16.1-cp313-cp313-win_arm64.whl", hash = "sha256:166adae25b05b04c9a84135912066d9c97482115af38df1a419a38aacc6b6f5d"},
- {file = "coverage-7.16.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:cc0b37fe6f5ce5f1ccc62ad4fa9b1ad201d8e9b6027fd5e0170877beee4b2d15"},
- {file = "coverage-7.16.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:6618f481053b63fc6121faf8fc676bd9b7163c2a19d9e984a2e850002c28ab57"},
- {file = "coverage-7.16.1-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:fa02d561eb1d8d2f8ba43ba6e3cef4c6c402a3b632a9460fa329fcadcd5df6a3"},
- {file = "coverage-7.16.1-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:bc5354a124799f1f87b7637bbe6f18cd4bc66a1f37f6aa2b5db40f9adad531dc"},
- {file = "coverage-7.16.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:34bafe9f4094315248573e6223e11af0ec1b25f9cbca43bf0e9a26a189ba2751"},
- {file = "coverage-7.16.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:29c4d3e32a3b5efa420a3dc627c7e570deb80ef997def52c7686a474f5edc7ab"},
- {file = "coverage-7.16.1-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f2066c447fdd0bca39a9633a082d8ce67bf9a539a203b85059a364a405dc9fe9"},
- {file = "coverage-7.16.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:fd8ac10cd2458b3c6343aac082fb9bd0e3fa806cb2c4975f2280153474b88412"},
- {file = "coverage-7.16.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:9d8c54ec32e5c102b9241f75d88ae26538b53662868ca491736611db448d9c7a"},
- {file = "coverage-7.16.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:6dd8dda3402a01a1a8fe8b753a282466f615128574a5590a9108acd07b1f8540"},
- {file = "coverage-7.16.1-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:79afa9726438912e5cddd1fe541815cea9763c92935f594835e4c432565b68a9"},
- {file = "coverage-7.16.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:3db3978211c3cead5437a80136ca0556bab8bc7828de15a762884b0598c41361"},
- {file = "coverage-7.16.1-cp314-cp314-win32.whl", hash = "sha256:49c39c7068a494f8eb427155f5682f44feee43f9b3107fd54b1e52465379c54b"},
- {file = "coverage-7.16.1-cp314-cp314-win_amd64.whl", hash = "sha256:c510dad19552d912058e4c3e3cbec3fb155dbe8d0ce0ceb7e7dbf5c5822bae0b"},
- {file = "coverage-7.16.1-cp314-cp314-win_arm64.whl", hash = "sha256:b7d4d7e6dcaf33e85f1919f03346403bdcc27437c420a78835f3805bca0ab71f"},
- {file = "coverage-7.16.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:3d0a3681c12d3e0bcdea3d9414b04087828d6c1a482802d6f7f42c37ed530152"},
- {file = "coverage-7.16.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:3f3b4469d3da3ecced775d1a8c9c5d9fc80f259e30b7b89f9fed0700d6035ecb"},
- {file = "coverage-7.16.1-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:c08ae35c1be2fe1ce4b4c628df5c6fc0dc9a87f8e5fe8e20238d249678984741"},
- {file = "coverage-7.16.1-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:8ee71a38c54bb2676bbe762b8b0943a79ccb1c2fd6a52054f66e63eda392f8c1"},
- {file = "coverage-7.16.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:76491917771f179f9772efe218c5ccc65950dbdb35f4439298d8a8dfc6ec1f72"},
- {file = "coverage-7.16.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f4aa0b0a6f81fa3deb211e643f6954e78b4376b62b9c218271236cfa757664e8"},
- {file = "coverage-7.16.1-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:756ba2d96d073c5a2a55d67fa22784763710fadbe22c41adde2d9cfa4dd78a8c"},
- {file = "coverage-7.16.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:99bf9ea435cefcefd220f8687c3ddbbf78dc2de0bd11b57c3ae9fbbdf8d5561a"},
- {file = "coverage-7.16.1-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:35cbc81f937fc402971df45c897d2df2bfb2014efcd990360032aa0a651635da"},
- {file = "coverage-7.16.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:8fae08e85b334ac6ac886002b5041396a31bcf805225bbe19847627203da99e2"},
- {file = "coverage-7.16.1-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:83362b64e215ef00b0ba33fcf13655ace6c9fdd144d5ad2ab59ac86c2daf166e"},
- {file = "coverage-7.16.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:33300f2e140ccf26af3d8152e62bff71993f9310cfc63ba7a20940b0d246a0ae"},
- {file = "coverage-7.16.1-cp314-cp314t-win32.whl", hash = "sha256:5539304fdbb2cc144df684d35a33b81145334d23e1c2367b5a923d25107f70b2"},
- {file = "coverage-7.16.1-cp314-cp314t-win_amd64.whl", hash = "sha256:715dcb72c3280c428c3a20134b87e42c29acec9669136e899ab2de69ca86218d"},
- {file = "coverage-7.16.1-cp314-cp314t-win_arm64.whl", hash = "sha256:dac8b84c03e6029d272b8249c77018db83de59ca009a9adef7c144b4a62ee5e6"},
- {file = "coverage-7.16.1-cp315-cp315-macosx_10_15_x86_64.whl", hash = "sha256:a337dc2d54c74430cd2febb8ee04f7c508ba8b3b412bf0463f077a66cfc73743"},
- {file = "coverage-7.16.1-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:3acd1d78397dead78dd1b011b5fc19cc823c190349acd549e63856dff649c80e"},
- {file = "coverage-7.16.1-cp315-cp315-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:73a32694603a34ad01d7e51a481a4023410d8099e1d0757e067945695c10f0ae"},
- {file = "coverage-7.16.1-cp315-cp315-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:fbbe8265736659a6be2e6042b6a35be13545d14b243cc1d7ecf65f90d788a370"},
- {file = "coverage-7.16.1-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4b0359eb4c62f9993e176bc8f50450fc736a6b90dbcc05bb8584e948812699ae"},
- {file = "coverage-7.16.1-cp315-cp315-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:da506e669a8a851b59e122b4b219ea70996a6296f44f3a9348a852526ff961de"},
- {file = "coverage-7.16.1-cp315-cp315-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:64a2a5985d81810ed605ff0dc4ccd6555efcb5700353825532a9a0aea65826e1"},
- {file = "coverage-7.16.1-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:66d70132b69b861805dc1ca46cdd733e54c416890e8f1371d2fd103f70b59c9c"},
- {file = "coverage-7.16.1-cp315-cp315-musllinux_1_2_i686.whl", hash = "sha256:db651a9cf325a542bc2b7b8cc8f1b2bdc6492739bae3103731b2f1c85b96cff6"},
- {file = "coverage-7.16.1-cp315-cp315-musllinux_1_2_ppc64le.whl", hash = "sha256:4184e78a4465dcda359fb403172b8951dd220929cb0984c02fabca1742fff06f"},
- {file = "coverage-7.16.1-cp315-cp315-musllinux_1_2_riscv64.whl", hash = "sha256:bc53c3f3adaa939b7a063533ffe0ae1259e7073393c618043a99a6970a87e3df"},
- {file = "coverage-7.16.1-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:b44308854ef210b9b78df9cdfd4e159513382a859f5ef8464306d14a54c2a040"},
- {file = "coverage-7.16.1-cp315-cp315-win32.whl", hash = "sha256:dccc142614d3419ed71857deb43f1d757829a4c7fce9994464b71e7e38309827"},
- {file = "coverage-7.16.1-cp315-cp315-win_amd64.whl", hash = "sha256:961fc424e9d5229a99f8f1189942d8e7f4e1519147c3af64842f944aca03914d"},
- {file = "coverage-7.16.1-cp315-cp315-win_arm64.whl", hash = "sha256:681a9488c5a234397c4f013da065aa9e53eb7af4c78f1f80c6f15e7208acb855"},
- {file = "coverage-7.16.1-cp315-cp315t-macosx_10_15_x86_64.whl", hash = "sha256:8bb09a2d19b04db1fa0e087a7ca4f12458f7e0e7364cfcd838441d86fb1c61f6"},
- {file = "coverage-7.16.1-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:2270a794600b635ca9452ce4c32e2fe81a35f9caa17ffac0eba99f14f275bd4d"},
- {file = "coverage-7.16.1-cp315-cp315t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:a4eff405b545dfcf79cf0d9d3ff750e5c5a887aa066114175193a81d249c5ee6"},
- {file = "coverage-7.16.1-cp315-cp315t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:ee1d5fc9e3bd6a217906929cc97880239a91d20dae7746f538eb0eefee705ab1"},
- {file = "coverage-7.16.1-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d4ec944947de098ad5a1738413f9364689a57067ecbc328e9de37218aa1e5cc1"},
- {file = "coverage-7.16.1-cp315-cp315t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3f73ee3956fde2d461c9e2955dd48166e4821fc8587d135e8780fb84da2a098b"},
- {file = "coverage-7.16.1-cp315-cp315t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:1ec9a4ee989c0d06ad95add0dbfdbb72b00ef53f43431ca0b612384e7878e5de"},
- {file = "coverage-7.16.1-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:7e5727b2508f817f3126d6c33327dda32fe69d15514badfcd61db8bc4209ecef"},
- {file = "coverage-7.16.1-cp315-cp315t-musllinux_1_2_i686.whl", hash = "sha256:19a3ea2f364012ef06678118fffdc92442a16bef4a5c8ad4f4019dd8f9ac8876"},
- {file = "coverage-7.16.1-cp315-cp315t-musllinux_1_2_ppc64le.whl", hash = "sha256:996c2b891b441ec2b39725ee3e8386e2f11b4894b92be225fdfd54a3eeada2c8"},
- {file = "coverage-7.16.1-cp315-cp315t-musllinux_1_2_riscv64.whl", hash = "sha256:a125fac1f6b1e88488d208a86b578e1790e3c4937f2e1568d23356141d236220"},
- {file = "coverage-7.16.1-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:b10095528b866d322d33d6bf1709b7f8cbf959f12e8cb2ba22fc59c8717866b0"},
- {file = "coverage-7.16.1-cp315-cp315t-win32.whl", hash = "sha256:531d9be377fdcc05593b974656872eb82e808ebeb42a72515e3aaeb8bb7166f5"},
- {file = "coverage-7.16.1-cp315-cp315t-win_amd64.whl", hash = "sha256:46a88f51770df7c9bc376bd57d3f86cdc7624b8e16ac4b585a655c22b7a1b4db"},
- {file = "coverage-7.16.1-cp315-cp315t-win_arm64.whl", hash = "sha256:7580432cbe1e8b762660ae5806f04f869e1c02e519836a43f8094437e561e9f0"},
- {file = "coverage-7.16.1-py3-none-any.whl", hash = "sha256:3d8bd4e58b6a5c2018d808f297905393c6c61da466a48c3f0596a76a4900ebe4"},
- {file = "coverage-7.16.1.tar.gz", hash = "sha256:f83981779bcf9dfa06fa0a8d4cb43e0faec1706328ce07aa3e7b665b4ac0f210"},
+ {file = "coverage-7.16.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:23219888477edd736b6fcaec1272d47d93b926e999641ffea7e53a1738e70b2b"},
+ {file = "coverage-7.16.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:40c0f00899fe6181ae7f434ceb200e51f5ee4b8ed10e3b5f0b605f0cae15da87"},
+ {file = "coverage-7.16.2-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:a4624f80732f6b427ac58f1f59c577a0994a12e8174b5af6a027b4b58795d4c3"},
+ {file = "coverage-7.16.2-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:191803c4996b499fcd78c2ad5e5f767dcc53cb4dc6de6d6a741b443a1821ef02"},
+ {file = "coverage-7.16.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9fd670ac43b709c575aefc25bf52d8a598a3bc5017bddfd0a179152ab06a2deb"},
+ {file = "coverage-7.16.2-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:705e5af11d34647efdc170c7840b6857c81cf74be96419a553f237e68e62cb72"},
+ {file = "coverage-7.16.2-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:8afd9bf35cc6a1f22eb3634808fa8e0b91902459c5721ef2e4461dfe771d7f08"},
+ {file = "coverage-7.16.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3f43bac1856ba269b905302778d4df433d6006489a192174ad77ac528e395032"},
+ {file = "coverage-7.16.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:f8475460aa33ee28ac896ab1156d0bb3b6c639f7f8383c2677d3359eb35f8205"},
+ {file = "coverage-7.16.2-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:d6276d78f6fca7d0ac066d5da4165c5acd07829e8305c2cb900b738fb3a75a72"},
+ {file = "coverage-7.16.2-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:736fde09ea39646d11f8e3b76bd3425c075aa4dd45f24891970bb77c14ff20f5"},
+ {file = "coverage-7.16.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c85d54e7e8a2ca932fe8399301af9b8d5907ea2a455ffaff6e7d1208db83b943"},
+ {file = "coverage-7.16.2-cp310-cp310-win32.whl", hash = "sha256:5139009b5efd2194fc168ee9362f0e191ba612ef5d29242f9269c22f9b8f80c7"},
+ {file = "coverage-7.16.2-cp310-cp310-win_amd64.whl", hash = "sha256:c3305c38a2fa21a4254f2ace7dd9ef5fc569c9a558b66e7017650b3d637fb95e"},
+ {file = "coverage-7.16.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:732d950e51f3ba4fb6209c73250f3e8924fefca42953ee04a9e65d8c02414d7d"},
+ {file = "coverage-7.16.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5dca0bb66b4c3d624ba047887bf70270030c150692d543cb501293dc38a9f4b5"},
+ {file = "coverage-7.16.2-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:af2a2a8c7c74de0559e0c368d94c8def9e16c58faaee33a0bf081057c4227e3b"},
+ {file = "coverage-7.16.2-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:db5f8394e17f877a625b257f2ba0ce8e728a499c2c1579ad66220272cd3df510"},
+ {file = "coverage-7.16.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5b3146d2317c75f70df2509066d979dadd941f7021cdf9b5db4bcd8568258e25"},
+ {file = "coverage-7.16.2-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9e1d0ced76318bab499693ff25f64faa343415187cb2e4d7befdfdd391a1cf6a"},
+ {file = "coverage-7.16.2-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:af98ad5ed9d6daaca956201e00bb429a7eb2b080426686f70a20353e0f9839f5"},
+ {file = "coverage-7.16.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d56e4d21c56d2046447733f8b118409597db48c01efe898ee9ac24e858ec2d6"},
+ {file = "coverage-7.16.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1d5d0e3b660506fb84f995814e3118a21efdc0c8eb80127da1be627d90093c17"},
+ {file = "coverage-7.16.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:17228fbca0f22976f797be94e975dcd237799c657d49551c7de1e0654d1202e9"},
+ {file = "coverage-7.16.2-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:bc0b0ac781d489304b741269857f1f8338b7a26b1b89c06c0344658001ec0035"},
+ {file = "coverage-7.16.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bf1bd822ec4e387ed245bed0d71151582cf7be9e5309bc4145eefe36083d5878"},
+ {file = "coverage-7.16.2-cp311-cp311-win32.whl", hash = "sha256:7ed238d227e23cc300c3d464babdaf9f6ddc740aa1b15a77ae96136e6a7c4516"},
+ {file = "coverage-7.16.2-cp311-cp311-win_amd64.whl", hash = "sha256:a90700f743e29aa3d75a6ff5f01953176a889c00e526194bc4d281731b88d99d"},
+ {file = "coverage-7.16.2-cp311-cp311-win_arm64.whl", hash = "sha256:a336eec40e3520d369b8a6cdabb4f596e69a8b42927ca074aa1452fed943238a"},
+ {file = "coverage-7.16.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:218d742afca2b5ad5ca759e93eddedfbcc6eadf8322f080dcefc40b7bd4e2d48"},
+ {file = "coverage-7.16.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a9a638be322a8d76a41cdb17781c7f82aaee6a66493d8ffb7e2c09ee22423d99"},
+ {file = "coverage-7.16.2-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:724bd0f1e81856b35e59fc98cf7b4e544a3cb662e4e0864dca73d4326ee9d808"},
+ {file = "coverage-7.16.2-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:5375ebd99038021b35e99dc88255022912c06565d316212f4a576e4b08d30f5d"},
+ {file = "coverage-7.16.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7a076277ca9f5750cc230f0f578ebd2620cec60255b25707361699fef6fb465c"},
+ {file = "coverage-7.16.2-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:58d4a54c6ea672afef66d49be922a2c69826c5ae1a42a9cd94f0c9c2bacdf800"},
+ {file = "coverage-7.16.2-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0dcbcfcc059117284c603ff8cb61a65872512882f84a8cf0339241f7f7c2f148"},
+ {file = "coverage-7.16.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:afdf43b72ef3876c1fe66423b91466e37877c9e81e8cec70542b7e8525b9d1b7"},
+ {file = "coverage-7.16.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:9acc7f7ec4a1b5f89bd929fde5b8a714f6fafdc6cc18725413d510aa082b47ad"},
+ {file = "coverage-7.16.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:80d3f7b48d43ee8fc5e8707a8adb43d743a5a1a85256c25a24f9d6d0e2238fa6"},
+ {file = "coverage-7.16.2-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:126d1af8804d7224421fe991ff65d3ce649081560df7a98b1a5ffff07f9923bd"},
+ {file = "coverage-7.16.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c19cd6d025c1673f22afcd22c7df8a662d779e05d8e3fa6820c22afb895b0206"},
+ {file = "coverage-7.16.2-cp312-cp312-win32.whl", hash = "sha256:152877cdc8a07264882cfcd503ba56a3ef6cba56a70e8c70f6eb8ffd7384789a"},
+ {file = "coverage-7.16.2-cp312-cp312-win_amd64.whl", hash = "sha256:e6c52d3307824ff93b39efd99e4185d557db40bd841452abfb32e5d9151ca162"},
+ {file = "coverage-7.16.2-cp312-cp312-win_arm64.whl", hash = "sha256:a678c0b6b22086ec2427359d22e37445d4a792f5fdbbc744112c7dade65cad02"},
+ {file = "coverage-7.16.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1a37c6e478cf687e1aa30a593d19c92c02fad9d122b51ab73f51b8dc7a0c0fc9"},
+ {file = "coverage-7.16.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0993d0e90858c03943d3cb152e068a20dd4707924deec84dd2230261baae3b1b"},
+ {file = "coverage-7.16.2-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:bb2fc905bbf4e6b7f40806ea79e31515abf6349594cdf0adf27c4215f0463204"},
+ {file = "coverage-7.16.2-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:4358b9c8c0125b460407f3017c6cce8156e904b32772c5630d27112f52bdbfe5"},
+ {file = "coverage-7.16.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1f15254427c9b33eedac4f198eaf9e356eb4f6214551afb43da6194a2c088ad7"},
+ {file = "coverage-7.16.2-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9a75a4704ff640e46170042eec1f984385a121227c505d5a16ad8e495f452541"},
+ {file = "coverage-7.16.2-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:14253fc7bb15749b849795a06f5d3b6d8bc3fb8a4b5ddc341faf7a89dce205fc"},
+ {file = "coverage-7.16.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:921415102a90637fcc2e3f169f61dad7699ecf690e8639fc21b813acbedc0967"},
+ {file = "coverage-7.16.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cce2bc991293f15cc4084ca116827b5900c5f34e1a54dfe83f10ab5c43162eb7"},
+ {file = "coverage-7.16.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:e1fa594c887365b69745f25a416806e61085dd07b94c9eae68a6e20730629b23"},
+ {file = "coverage-7.16.2-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:11e597173af1dc33d5f8a7332ada544199269a223af1ee1770ddd5e245ad0fe8"},
+ {file = "coverage-7.16.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:3e7f99698ba3a7d13988bdd984b7ebf13af4dbe2166dc8502eef90d77603b0a4"},
+ {file = "coverage-7.16.2-cp313-cp313-win32.whl", hash = "sha256:f80bd9f9633eafc73d0a913ba2645c96ba58bba1befc30590f7c0fbfde59d865"},
+ {file = "coverage-7.16.2-cp313-cp313-win_amd64.whl", hash = "sha256:8be099e979fc42559328a21828281b4578304191ae46ed4e80a407048a82eee6"},
+ {file = "coverage-7.16.2-cp313-cp313-win_arm64.whl", hash = "sha256:28ff850182a67d117990fa2ce5ea1032836d8c9630dae867e8bdd3bff4533b79"},
+ {file = "coverage-7.16.2-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:4ee546b9e4872ffa194bf07ac87bfa1202ebb824d0795dc1ef22f175545ca90a"},
+ {file = "coverage-7.16.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:a2fac6895eb299a2e52d7bbb8fb3903502b9da8d3f5309ceb16ec40c646b58ee"},
+ {file = "coverage-7.16.2-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:57ff3783f99d75a1e81dd56a9737eb5665e6736a5d93258ba596b6dcad8fd05b"},
+ {file = "coverage-7.16.2-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:35f37886699cb9abd29958247d718628d5bc6f39e623dff66a09e546c42a7e03"},
+ {file = "coverage-7.16.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0fd7a86fdda7cb6d616d178654bd0ad6bc0f3f33c2e478aa598500a1a9e34eda"},
+ {file = "coverage-7.16.2-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ac0f3b379c94acc2f7dce5f5f0b24d44fa1cc6a509717ef83dfee07450c2117c"},
+ {file = "coverage-7.16.2-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:7d0732c83746bc24123c581a85d9dd96b70ddb538c9076020aa1a041790361e9"},
+ {file = "coverage-7.16.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7b451c68218c150f616bc9649783ec8de76a59792c759b43aa0c9c0466a465e4"},
+ {file = "coverage-7.16.2-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:a56ac4fa5a75c7e182e8f62600cfb4aff43c5ed7356a034f3557659c3bec1d90"},
+ {file = "coverage-7.16.2-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:4cc4f73aa3fabc36e32046d6cd2971405948d8a903636508a3d3b2f9128b3a95"},
+ {file = "coverage-7.16.2-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:723dcdab91357159b722935b500ee8abc0a66c8c432e1e9fabf4cc7598952de8"},
+ {file = "coverage-7.16.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5397e21a90dde0e9c6896b77ded8f0be26b66f8b22b33aed41f6043ed95d55e6"},
+ {file = "coverage-7.16.2-cp314-cp314-win32.whl", hash = "sha256:848893e1d361448c113dc2f0913503522a6f7be231d0e38333d2a22d9698a011"},
+ {file = "coverage-7.16.2-cp314-cp314-win_amd64.whl", hash = "sha256:5a27b731c171e43dc8b5f32b76a5051dde2ec9b9366c87028f08a7088ebc2c7b"},
+ {file = "coverage-7.16.2-cp314-cp314-win_arm64.whl", hash = "sha256:1c569a9fd25505f1cd6bea90588818f90373ce90e2632e2cacf19ddbd6e14fdb"},
+ {file = "coverage-7.16.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:d93db87adb6b1c1b408dce4763314b55d76a9f589e96783a84ac9e7689e48bdf"},
+ {file = "coverage-7.16.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:aa62c85046473959c13ba9edca9dc90a77d5c1095b1ba313556314d77fe5b036"},
+ {file = "coverage-7.16.2-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:db76506aa5416081f3e8974ae0f7965c58ada0bb0ef7339ac86099588dbb20d3"},
+ {file = "coverage-7.16.2-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:a0f2285329dac10ab08f79cb11f5692c497018e6c7c511f95e6fd63a70b8f831"},
+ {file = "coverage-7.16.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:382d3346d56b0eec1b793d53a4c88799c8053f516aa3a8d7c44315696954bacf"},
+ {file = "coverage-7.16.2-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:648352b94507179d82637292e7ae8802508d95f78e2f00a705a50b6c48011681"},
+ {file = "coverage-7.16.2-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:fb2bde05838fffae1a1bf75e5d411a6cac3e4e9bb97e6640fed8cd47888b33f0"},
+ {file = "coverage-7.16.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:6a75180829efb8ae62b4aded25be6ddca1c888d138d2d82e21d93bfbd88f41cb"},
+ {file = "coverage-7.16.2-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:99704f73721e23859112072d522076e11c31744fc96b5652e5dd2018aa4359f7"},
+ {file = "coverage-7.16.2-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:29309ccc86b7f33df7db12813c299f215bbbc470ed6292d0bedd63ffae1ebf64"},
+ {file = "coverage-7.16.2-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:30c1b65d529e46569899fadca59e4a87c1faf2886923f1307ba61e654d4f3c20"},
+ {file = "coverage-7.16.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:dcf4bc2aab4e16b1c4c0c2005918f23a7dd5d7821ddae82caed9e3342dc2fcce"},
+ {file = "coverage-7.16.2-cp314-cp314t-win32.whl", hash = "sha256:a9cd3de0a5bfe7b0e21ee10e1a14e3d61bf52efc88217ab1d95d6ace6970bd46"},
+ {file = "coverage-7.16.2-cp314-cp314t-win_amd64.whl", hash = "sha256:611a44e5229a59d7483ce830160e1a0e85f700562c7a5651c7c63fb8f4eb528c"},
+ {file = "coverage-7.16.2-cp314-cp314t-win_arm64.whl", hash = "sha256:22957cef43ce038641de78ba995de7568d2d6a37c6ddbf7fa0fd7d1ae2344d91"},
+ {file = "coverage-7.16.2-cp315-cp315-macosx_10_15_x86_64.whl", hash = "sha256:414c26dfdb96aac2d570a54e03008f001e32eb2d413705365503648c6bd361d8"},
+ {file = "coverage-7.16.2-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:00d3eb96e9988c45f50cccd1f1496571ac5c1f91386ac02c4d55516eeda19a24"},
+ {file = "coverage-7.16.2-cp315-cp315-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:4dbbd1155ca46e6e0b6b89d204428c56ef6a459af21333f365d135a2820e5a09"},
+ {file = "coverage-7.16.2-cp315-cp315-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:8fc15cc8d0d06e873c00ef18e1372d605f9aaf3de27d8c24e50782e75bc8b843"},
+ {file = "coverage-7.16.2-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9c6afdd69218202bc1758c9a14b86b8cf1084f37ed2ca143e567a103772b16d1"},
+ {file = "coverage-7.16.2-cp315-cp315-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:aba5c63b7afdc749cc9eae943d5b868cba2b261a176378fa1c5a30bc8bc89982"},
+ {file = "coverage-7.16.2-cp315-cp315-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:9174f0af24e5eff248b9dbfe76ec5275a3d19d37edbc2810543f12cf97347a34"},
+ {file = "coverage-7.16.2-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:80e9fdb4c3d926b6ba721d4bf7435bdb869c3527ae7803290361d0ab73db13b6"},
+ {file = "coverage-7.16.2-cp315-cp315-musllinux_1_2_i686.whl", hash = "sha256:7b3bce4a0d05401d70b7d0d5ca783e686bc9d30e81dbd7d980d532609bf809e4"},
+ {file = "coverage-7.16.2-cp315-cp315-musllinux_1_2_ppc64le.whl", hash = "sha256:44f21e407b278efdfc1ee5e481e00518bd1d500310a30a5fbf2bcbedfef4aaf0"},
+ {file = "coverage-7.16.2-cp315-cp315-musllinux_1_2_riscv64.whl", hash = "sha256:59c3926585e1cd1f2190f4b2ac9014de1bbeaf0d5d0587b0dc6b0aa90d17896a"},
+ {file = "coverage-7.16.2-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:066429634299e14dd2d511e1e85f8f9cecc500781f6b41907c0dd6f1baea7e63"},
+ {file = "coverage-7.16.2-cp315-cp315-win32.whl", hash = "sha256:893ea9cf86cb8d2546812ac93d973aaf2ee1fb45110a873b014214fd23e3725e"},
+ {file = "coverage-7.16.2-cp315-cp315-win_amd64.whl", hash = "sha256:01c6908bc613b420c26c818fe948e1b97dfd041a53c98b01c63bd8321f5c9aae"},
+ {file = "coverage-7.16.2-cp315-cp315-win_arm64.whl", hash = "sha256:967d72c835d7a8cf0af99ec813a2d06e3db6df706402f1fe85b31b437645f495"},
+ {file = "coverage-7.16.2-cp315-cp315t-macosx_10_15_x86_64.whl", hash = "sha256:98d9c97f51b334b0adce7b964442a9af33c1a00c6ac856984cc5dc8d18f81c75"},
+ {file = "coverage-7.16.2-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:3e861f1071dcc2fec1e88bef0920f6b1eaa66a143555b4f8ab79ba2b0f30ef55"},
+ {file = "coverage-7.16.2-cp315-cp315t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:fb9d92ecfe2d5b494367c67f7446f8b75b68d8d0c8cf3bc3e6997478be25d9e2"},
+ {file = "coverage-7.16.2-cp315-cp315t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:eb57acff4a74246ae513c142d4b36e18c389c3aed8661914a53f7cd0071031b2"},
+ {file = "coverage-7.16.2-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:444889f7f66b74e4455c0a97e0e166dd41177f1dca8c0239a47cff25e05ba7e1"},
+ {file = "coverage-7.16.2-cp315-cp315t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a740ea6f083c6db7b926534d159508f80ba275ab35e722522de0d18d0f56e55f"},
+ {file = "coverage-7.16.2-cp315-cp315t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:8e209591f7c41ae4a9171335cf6156afda0b21de73b02f73f5aa95b2d5fbb08d"},
+ {file = "coverage-7.16.2-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:396bb16e04ce04efbb3df91456ae4e3da918e69ecdf67fb711b0a0fdf35ccce0"},
+ {file = "coverage-7.16.2-cp315-cp315t-musllinux_1_2_i686.whl", hash = "sha256:9cdf19874e0d247f32f03609200370343c3c7aa260b191d8c2bb251d36198283"},
+ {file = "coverage-7.16.2-cp315-cp315t-musllinux_1_2_ppc64le.whl", hash = "sha256:fd3d72233eb8b48acc94fa57d44e2d32ce8e7abed02882ccb6d855ccc4ed33ec"},
+ {file = "coverage-7.16.2-cp315-cp315t-musllinux_1_2_riscv64.whl", hash = "sha256:bb4ffe96aa663cee727659db5a2afeb38c95f8677b747d447b90d6d4874ea2c5"},
+ {file = "coverage-7.16.2-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:dba2edfb054f6d4a08df9d1637c39a5aa3865bca6617c13c86be21e45658a59c"},
+ {file = "coverage-7.16.2-cp315-cp315t-win32.whl", hash = "sha256:251aed777c47c77aba047096d4542889db089227655711dfc2b9c54ef0e15e35"},
+ {file = "coverage-7.16.2-cp315-cp315t-win_amd64.whl", hash = "sha256:2aca0bdfa9e91621d5b09d815357bf63def4fc0e9cb66da67bf2cf93f3b1a6f5"},
+ {file = "coverage-7.16.2-cp315-cp315t-win_arm64.whl", hash = "sha256:b88841e654f09732804809e435b3e005a929ffd9998b872b7b213957b8759cb8"},
+ {file = "coverage-7.16.2-py3-none-any.whl", hash = "sha256:11d28e9123a9156cb405d8d27b44256c9a58fb5decc2073a8f17862057e3aa0f"},
+ {file = "coverage-7.16.2.tar.gz", hash = "sha256:ca64d9f1f384f151b9511bec01126072acd2f313439f8ed015a22d8790aab6fa"},
]
[package.extras]
[[package]]
name = "rpds-py"
-version = "2026.6.3"
+version = "2026.9.1"
description = "Python bindings to Rust's persistent data structures (rpds)"
optional = false
python-versions = ">=3.11"
groups = ["main"]
files = [
- {file = "rpds_py-2026.6.3-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:7b689145a1485c335569bd056464f3243a29af7ed3871c7be31ad624ba239bc7"},
- {file = "rpds_py-2026.6.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:db08f45aecde626498fb3df07bcf6d2ec040af42e859a4f5040d79c200342911"},
- {file = "rpds_py-2026.6.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:acc992ab27b15f852c76755eb2ab7dce86585ddadba6fa5946e58556088845b4"},
- {file = "rpds_py-2026.6.3-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7f88d653e7b3b779d71ae7454e20dcc9b6bae903f33c269db9f2be41bda3f261"},
- {file = "rpds_py-2026.6.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e52655eaf81e32593abedaa4bfe33170c8cfedf3365ed9be6e11e07f148f0278"},
- {file = "rpds_py-2026.6.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dfcc8b909769d19db55c7cc9541eb64b9b774b1057ffffb4f1048070475bb9f9"},
- {file = "rpds_py-2026.6.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c1255b302953c86a486b81d330d5ee1d5bd937691ce271b6be0ef0e299eaab7"},
- {file = "rpds_py-2026.6.3-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:8d2294a31386bfa251d8c8a39472beee17db67d4f1a6eabea665d35c9a4461c3"},
- {file = "rpds_py-2026.6.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f8f23ead891a3b762f35ab3b04623da7056545b48aa60d59957e6789914545da"},
- {file = "rpds_py-2026.6.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:421aba32367055614287a4292b6a17f1939c9452299f7a0209c117e990b646d4"},
- {file = "rpds_py-2026.6.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1e5822dfc2f0d4ab7e745eaa6d85945069329beeccef965af3f3bb26058fcab6"},
- {file = "rpds_py-2026.6.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:83e35b57523816c8613fd0776b40cd8bb9f596b37ddd2692eb4a6bb5ab2f8c93"},
- {file = "rpds_py-2026.6.3-cp311-cp311-win32.whl", hash = "sha256:de3eceba0b683bcbb1ab93da016d0270df1f9ae7be716b40214c5dafac6ea45a"},
- {file = "rpds_py-2026.6.3-cp311-cp311-win_amd64.whl", hash = "sha256:2c54a076ca4d370980ab57bc0e31df57bbe8d41340436a90ef8b1219a3cbb127"},
- {file = "rpds_py-2026.6.3-cp311-cp311-win_arm64.whl", hash = "sha256:168c733a7112e071bb7a66460e667edfcff06c017a3c523f7a8a8e08d0140804"},
- {file = "rpds_py-2026.6.3-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a0811d33247c3d6128a3001d763f2aa056bb3425204335400ac54f89eec3a0d0"},
- {file = "rpds_py-2026.6.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:538949e262e46caa31ac01bdb3c1e8f642622922cacbabbae6a8445d9dc33eaf"},
- {file = "rpds_py-2026.6.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55927d532399c2c646100ff7feb48eaa940ad70f42cd68e1328f3ded9f81ca24"},
- {file = "rpds_py-2026.6.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f56f1695bc5c0871cbc33dc0130fcf503aab0c57dcc5a6700a4f49eba4f2652e"},
- {file = "rpds_py-2026.6.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:270b293dae9058fc9fcedab50f13cebf46fb8ed1d1d54e0521a9da5d6b211975"},
- {file = "rpds_py-2026.6.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:127565fead0a10943b282957bd5447804ff3160ad79f2ad2635e6d249e380680"},
- {file = "rpds_py-2026.6.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ecabd69db66de867690f9797f2f8fa27ba501bbc24540cbdbdc649cd15888ba6"},
- {file = "rpds_py-2026.6.3-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:58eadac9cd119677b60e1cf8ac4052f35949d71b8a9e5556efccbe82533cf22a"},
- {file = "rpds_py-2026.6.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7491ee23305ac3eb59e492b6945881f5cd77a6f731061a3f25b77fd40f9e99a4"},
- {file = "rpds_py-2026.6.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2c99f7e8ccb3dd6e3e4bfeac657a7b208c9bac8075f4b078c02d7404c34107fa"},
- {file = "rpds_py-2026.6.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:62698275682bf121181861295c9181e789030a2d516071f5b8f3c23c170cd0fc"},
- {file = "rpds_py-2026.6.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a214c993455f99a89aaeadc9b21241900037adc9d97203e374d75513c5911822"},
- {file = "rpds_py-2026.6.3-cp312-cp312-win32.whl", hash = "sha256:501f9f04a588d6a09179368c57071301445191767c64e4b52a6aa9871f1ef5ed"},
- {file = "rpds_py-2026.6.3-cp312-cp312-win_amd64.whl", hash = "sha256:2c958bf94822e9290a40aaf2a822d4bc5c88099093e3948ad6c571eca9272e5f"},
- {file = "rpds_py-2026.6.3-cp312-cp312-win_arm64.whl", hash = "sha256:22bffe6042b9bcb0822bcd1955ec00e245daf17b4344e4ed8e9551b976b63e96"},
- {file = "rpds_py-2026.6.3-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:3cfe765c1da0072636ca06628261e0ea05688e160d5c8a03e0217c3854037223"},
- {file = "rpds_py-2026.6.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f4d78253f6996be4901669ad25319f842f740eccf4d58e3c7f3dd39e6dde1d8f"},
- {file = "rpds_py-2026.6.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:54f45a148e28767bf343d33a684693c70e451c6f4c0e9904709a723fafbdfc1f"},
- {file = "rpds_py-2026.6.3-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:842e7b070435622248c7a2c44ae53fa1440e073cc3023bc919fed570884097a7"},
- {file = "rpds_py-2026.6.3-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8020133a74bd81b4572dd8e4be028a6b1ebcd70e6726edc3918008c08bee6ee6"},
- {file = "rpds_py-2026.6.3-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cdc7e35386f3847df728fbcb5e887e2d79c19e2fa1eba9e51b6621d23e3243af"},
- {file = "rpds_py-2026.6.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:acac386b453c2516111b50985d60ce46e7fadb5ea71ae7b25f4c946935bf27cf"},
- {file = "rpds_py-2026.6.3-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:425560c6fa0415f27261727bb20bd097568485e5eb0c121f1949417d1c516885"},
- {file = "rpds_py-2026.6.3-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a550fb4950a06dde3beb4721f5ad4b25bf4513784665b0a8522c792e2bd822a4"},
- {file = "rpds_py-2026.6.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4f4bca01b63096f606e095734dd56e74e175f94cfbf24ff3d63281cec61f7bb7"},
- {file = "rpds_py-2026.6.3-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ccffae9a092a00deb7efd545fe5e2c33c33b88e7c054337e9a74c179347d0b7d"},
- {file = "rpds_py-2026.6.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1cf01971c4f2c5553b772a542e4aaf191789cd331bc2cd4ff0e6e65ba49e1e97"},
- {file = "rpds_py-2026.6.3-cp313-cp313-win32.whl", hash = "sha256:8c3d1e9c15b9d51ca0391e13da1a25a0a4df3c58a37c9dc368e0736cf7f69df0"},
- {file = "rpds_py-2026.6.3-cp313-cp313-win_amd64.whl", hash = "sha256:9250a9a0a6fd4648b3f868da8d91a4c52b5811a62df58e753d50ae4454a36f80"},
- {file = "rpds_py-2026.6.3-cp313-cp313-win_arm64.whl", hash = "sha256:900a67df3fd1660b035a4761c4ce73c382ea6b35f90f9863c36c6fd8bf8b09bb"},
- {file = "rpds_py-2026.6.3-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:931908d9fc855d8f74783377822be318edb6dcb19e47169dc038f9a1bf60b06e"},
- {file = "rpds_py-2026.6.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:d7469697dce35be237db177d42e2a2ee26e6dcc5fc052078a6fefabd288c6edd"},
- {file = "rpds_py-2026.6.3-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bcfbcf66006befb9fd2aeaa9e01feaf881b4dc330a02ba07d2322b1c11be7b5d"},
- {file = "rpds_py-2026.6.3-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:847927daf4cffbd4e90e42bc890069897101edd015f956cb8721b3473372edda"},
- {file = "rpds_py-2026.6.3-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aca6c1ef08a82bfe327cc156da694660f599923e2e6665b6d81c9c2d0ac9ffc8"},
- {file = "rpds_py-2026.6.3-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ae50181a047c871561212bb97f7932a2d45fb53e947bd9b57ebad85b529cbc53"},
- {file = "rpds_py-2026.6.3-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc319e5a1de4b6913aac94bf6a2f9e847371e0a140a43dd4991db1a09bc2d504"},
- {file = "rpds_py-2026.6.3-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:e4316bf32babbed84e691e352faf967ce2f0f024174a8643c37c94a1080374fc"},
- {file = "rpds_py-2026.6.3-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8c6e5a2f750cc71c3e3b11d71661f21d6f9bc6cebc6564b1466417a1ec03ec77"},
- {file = "rpds_py-2026.6.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:4470ce197d4090875cf6affbf1f853338387428df97c4fb7b7106317b8214698"},
- {file = "rpds_py-2026.6.3-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:ea964164cc9afa72d4d9b23cc28dafae93693c0a53e0b42acbff15b22c3f9ddd"},
- {file = "rpds_py-2026.6.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:639c8929aa0afe81be836b04de888460d6bed38b9c54cfc18da8f6bfabf5af5d"},
- {file = "rpds_py-2026.6.3-cp314-cp314-win32.whl", hash = "sha256:882076c00c0a608b131187055ddc5ae29f2e7eaf870d6168980420d58528a5c8"},
- {file = "rpds_py-2026.6.3-cp314-cp314-win_amd64.whl", hash = "sha256:0be972be84cfcaf46c8c6edf690ca0f154ac17babf1f6a955a51579b34ad2dc5"},
- {file = "rpds_py-2026.6.3-cp314-cp314-win_arm64.whl", hash = "sha256:2a9c6f195058cb45335e8cc3802745c603d716eb96bc9625950c1aac71c0c703"},
- {file = "rpds_py-2026.6.3-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:f90938e92afda60266da758ee7d363447f7f0138c9559f9e1811629580582d90"},
- {file = "rpds_py-2026.6.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ec829541c45bca16e61c7ae50c20501f213605beb75d1aba91a6ee37fbbb56a4"},
- {file = "rpds_py-2026.6.3-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:afd70d95892096cdb26f15a00c45907b17817577aa8d1c76b2dcc2788391f9e9"},
- {file = "rpds_py-2026.6.3-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:29dfa0533a5d4c94d4dfa1b694fcb56c9c63aad8330ffdd816fd225d0a7a162f"},
- {file = "rpds_py-2026.6.3-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:af05d726809bff6b141be124d4c7ce998f9c9c7f30edb1f46c07aa103d540b41"},
- {file = "rpds_py-2026.6.3-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9826217f048f620d9a712672818bf231442c1b35d96b227a07eabd11b4bb6945"},
- {file = "rpds_py-2026.6.3-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:536bceea4fa4acf7e1c61da2b5786304367c816c8895be71b8f537c480b0ea1f"},
- {file = "rpds_py-2026.6.3-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:bc0011654b91cc4fb2ae701bec0a0ba1e552c0714247fa7af6c59e0ccfa3a4e1"},
- {file = "rpds_py-2026.6.3-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:539d75de9e0d536c84ff18dfeb805398e58227001ce09231a26a08b9aed1ee0e"},
- {file = "rpds_py-2026.6.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:166cf54d9f44fc6ceb53c7860258dde44a81406646de79f8ed3234fca3b6e538"},
- {file = "rpds_py-2026.6.3-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:d34c20167764fbcf927194d532dd7e0c56772f0a5f943fa5ef9e9afbba8fb9db"},
- {file = "rpds_py-2026.6.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ea7bb13b7c9a29791f87a0387ba7d3ad3a6d783d827e4d3f27b40a0ff44495e2"},
- {file = "rpds_py-2026.6.3-cp314-cp314t-win32.whl", hash = "sha256:6de4744d05bd1aa1be4ed7ea1189e3979196808008113bbbf899a460966b925e"},
- {file = "rpds_py-2026.6.3-cp314-cp314t-win_amd64.whl", hash = "sha256:c7b9a2f8f4d8e90af72571d3d495deebdd7e3c75451f5b41719aee166e940fc2"},
- {file = "rpds_py-2026.6.3-cp315-cp315-macosx_10_12_x86_64.whl", hash = "sha256:e059c5dde6452b44424bd1834557556c226b57781dee1227af23518459722b13"},
- {file = "rpds_py-2026.6.3-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:2f7c26fbc5acd2522b95d4177fe4710ffd8e9b20529e703ffbf8db4d93903f05"},
- {file = "rpds_py-2026.6.3-cp315-cp315-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a3086b538543802f84c843911242db20447de00d8752dd0efc936dbcf02218ba"},
- {file = "rpds_py-2026.6.3-cp315-cp315-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8f2e5c5ee828d42cb11760761c0af6507927bec42d0ad5458f97c9203b054617"},
- {file = "rpds_py-2026.6.3-cp315-cp315-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ed0c1e5d10cdc7135537988c74a0188da68e2f3c30813ba3744ab1e42e0480f9"},
- {file = "rpds_py-2026.6.3-cp315-cp315-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8c2642a7603ec0b16ed77da4555db3b4b472341904873788327c0b0d7b95f1bb"},
- {file = "rpds_py-2026.6.3-cp315-cp315-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8e4320744c1ffdd95a603def63344bfab2d33edeab301c5007e7de9f9f5b3885"},
- {file = "rpds_py-2026.6.3-cp315-cp315-manylinux_2_31_riscv64.whl", hash = "sha256:a9f4645593036b81bbdb36b9c8e0ea0d1c3fee968c4d59db0344c14087ef143a"},
- {file = "rpds_py-2026.6.3-cp315-cp315-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e55d236be29255554da47abe5c577637db7c24a02b8b46f0ca9524c855801868"},
- {file = "rpds_py-2026.6.3-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:24e9c5386e16669b674a69c156c8eeefcb578f3b3397b713b08e6d60f3c7b187"},
- {file = "rpds_py-2026.6.3-cp315-cp315-musllinux_1_2_i686.whl", hash = "sha256:c60924535c75f1566b6eb75b5c31a48a43fef04fa2d0d201acbad8a9969c6107"},
- {file = "rpds_py-2026.6.3-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:38a2fea2787428f811719ceb9114cb78964a3138838320c29ac39526c79c16ba"},
- {file = "rpds_py-2026.6.3-cp315-cp315-win32.whl", hash = "sha256:d483fe17f01ad64b7bf7cc38fcefff1ca9fb83f8c2b2542b68f97ffe0611b369"},
- {file = "rpds_py-2026.6.3-cp315-cp315-win_amd64.whl", hash = "sha256:67e3a721ffc5d8d2210d3671872298c4a84e4b8035cfe42ffd7cde35d772b146"},
- {file = "rpds_py-2026.6.3-cp315-cp315-win_arm64.whl", hash = "sha256:6e84adbcf4bf841aed8116a8264b9f50b4cb3e7bd89b516122e616ac56ca269e"},
- {file = "rpds_py-2026.6.3-cp315-cp315t-macosx_10_12_x86_64.whl", hash = "sha256:ae6dd8f10bd17aad820876d24caec9efdafd80a318d16c0a48edb5e136902c6b"},
- {file = "rpds_py-2026.6.3-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:bdbd97738551fca3917c1bd7188bec1920bb520104f28e7e1007f9ceb17b7690"},
- {file = "rpds_py-2026.6.3-cp315-cp315t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b95977e7211527ab0ba576e286d023389fbeeb32a6b7b771665d333c60e5342"},
- {file = "rpds_py-2026.6.3-cp315-cp315t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d15fde0e6fb0d88a60d221204873743e5d9f0b7d29165e62cd86d0413ad74ba6"},
- {file = "rpds_py-2026.6.3-cp315-cp315t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a136d453475ac0fcbda502ef1e6504bd28d6d904700915d278deeab0d00fe140"},
- {file = "rpds_py-2026.6.3-cp315-cp315t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f826877d462181e5eb1c26a0026b8d0cab05d99844ecb6d8bf3627a2ca0c0442"},
- {file = "rpds_py-2026.6.3-cp315-cp315t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:79486287de1730dbaff3dbd124d0ca4d2ef7f9d29bf2544f1f93c09b5bcbbd12"},
- {file = "rpds_py-2026.6.3-cp315-cp315t-manylinux_2_31_riscv64.whl", hash = "sha256:808345f53cb952433ca2816f1604ff3515608a81784954f38d4452acfe8e61d5"},
- {file = "rpds_py-2026.6.3-cp315-cp315t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1967debc37f64f2c4dc90a7f563aec558b471966e12adcac4e1c4240496b6ebf"},
- {file = "rpds_py-2026.6.3-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:f0840b5b17057f7fd918b76183a4b5a0635f43e14eb2ce60dce1d4ee4707ea00"},
- {file = "rpds_py-2026.6.3-cp315-cp315t-musllinux_1_2_i686.whl", hash = "sha256:faa679d19a6696fd54259ad321251ad77a13e70e03dd834daa762a44fb6196ef"},
- {file = "rpds_py-2026.6.3-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:23a439f31ccbeff1574e24889128821d1f7917470e830cf6544dced1c662262a"},
- {file = "rpds_py-2026.6.3-cp315-cp315t-win32.whl", hash = "sha256:913ca42ccad3f8cc6e292b587ae8ae49c8c823e5dce51a736252fc7c7cdfa577"},
- {file = "rpds_py-2026.6.3-cp315-cp315t-win_amd64.whl", hash = "sha256:ae3d4fe8c0b9213624fdce7279d70e3b148b682ca20719ebd193a23ebfa47324"},
- {file = "rpds_py-2026.6.3-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:4cf2d36a2357e4d07bb5a4f98801265327b48256867816cfd2ceb001e9754a8f"},
- {file = "rpds_py-2026.6.3-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:30c6dc199b24a5e3e81d50da0f00858c5bbdb2617a750395687f4339c5818171"},
- {file = "rpds_py-2026.6.3-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9891e594296ab9dada6551c8e7b387b2721f27a67eecd528412e8906247a7b90"},
- {file = "rpds_py-2026.6.3-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b5c2dc92304aa48a4a60443b548bb12f12e119d4b72f314015e67b9e1be97fca"},
- {file = "rpds_py-2026.6.3-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:127e08c0642d880cf32ca47ec2a4a77b901f7e2dd1ad9762adb13955d72ffcc9"},
- {file = "rpds_py-2026.6.3-pp311-pypy311_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8bb68f03f395eb793220b45c097bd4d8c32944393da0fad8b999efac0868fc8c"},
- {file = "rpds_py-2026.6.3-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a3450b693fde92133e9f51060568a4c31fcca76d5e53bbd611e689ca446517e9"},
- {file = "rpds_py-2026.6.3-pp311-pypy311_pp73-manylinux_2_31_riscv64.whl", hash = "sha256:5e8d07bddee435a2ff6f1920e18feff28d0bc4533e42f4bf6927fbd073312c41"},
- {file = "rpds_py-2026.6.3-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:3a83ae6c67b7676b9878378547ca8e93ed77a580037bcbcd1d32f739e1e6089c"},
- {file = "rpds_py-2026.6.3-pp311-pypy311_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:2bfd04c19ddbd6640de0b51894d764bd2758854d5b75bd102d2ef10cb9c293a9"},
- {file = "rpds_py-2026.6.3-pp311-pypy311_pp73-musllinux_1_2_i686.whl", hash = "sha256:ca6546b66be9dc4738b1b043d5ebd5488c66c578c5ff0fd0e8065313fe3afb76"},
- {file = "rpds_py-2026.6.3-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:8e65860d238379ed982fd9ba690579b5e95af2f4840f99c772816dbe573cb826"},
- {file = "rpds_py-2026.6.3.tar.gz", hash = "sha256:1cebd1337c242e4ec2293e541f712b2da849b29f48f0c293684b71c0632625d4"},
+ {file = "rpds_py-2026.9.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:2711d29b653b3bce48a63d18b9c6b53274669e6d6c4094dddeb4d9a0e45128b2"},
+ {file = "rpds_py-2026.9.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3231c4c0e521dafa5be0c9f114ee2c2ad46650836f2d72caa86801950c3e7044"},
+ {file = "rpds_py-2026.9.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e01b3c878c8641913e688edd1b3f08658c6783d29cf6b826bd3c0d1ae7a1ffaa"},
+ {file = "rpds_py-2026.9.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3e524c7874ac72884d28e16dd5b8d839fd09e0fe76b020d3fbca23212a7b8c52"},
+ {file = "rpds_py-2026.9.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:761fdae6728ceb99ab182fad2f0cc1e262f610834dc891aea1d1a2a2e634776f"},
+ {file = "rpds_py-2026.9.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6b723eb406dec5bc9ec516c73ab9c3239a3284e017f7eb89ee2b3258bd504fb7"},
+ {file = "rpds_py-2026.9.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:136a1c3fe4402b7008bc81cb62ee538481795b61a7e83df88dff3b3f02b726ff"},
+ {file = "rpds_py-2026.9.1-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:839dde845559254f34885267c6878f60d61d5205180226d976fe488d45fa128e"},
+ {file = "rpds_py-2026.9.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:56cd8b3f77d7b6812f533b662186a1f28316931166ddc00fb893b1b0db7e9888"},
+ {file = "rpds_py-2026.9.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:074a4d198bc34d9a8ea425114fc3ded6d11ec01f6a314a8db67454a5152d8834"},
+ {file = "rpds_py-2026.9.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:6beb738155fe8ab8091afdfa5a3226b21c2b1593f1e50ebb90eb25b44dbc0391"},
+ {file = "rpds_py-2026.9.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:57492a550a1d88d29d003247e5f78dd8cf04a701fac0e4c8db8745a6d2504e0a"},
+ {file = "rpds_py-2026.9.1-cp311-cp311-win32.whl", hash = "sha256:d95a354e02393eada6d7351184671aced9d4cce109dabf927cb7aa99624352a1"},
+ {file = "rpds_py-2026.9.1-cp311-cp311-win_amd64.whl", hash = "sha256:ce4d4f52e2a4324396caddbd45a97d8d7be5f42edd25d2355282a9c34f9b2f7f"},
+ {file = "rpds_py-2026.9.1-cp311-cp311-win_arm64.whl", hash = "sha256:fdcd198979b4ecffcc1beba366a7fbcf4eb41243691a82fe52ceb0b902f09c12"},
+ {file = "rpds_py-2026.9.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:50906f5aea24b5a865cbd0a589698288631d9f3a54c3a937c83aefa95a0d14af"},
+ {file = "rpds_py-2026.9.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e21c1429e205828ea886a2293a4a2c8e01f4c25d9893ca330e97a6cf73f52e7b"},
+ {file = "rpds_py-2026.9.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2693b2728bbcc48d09a981a356954b0c47c53ff25b545856f28a889ea619f69a"},
+ {file = "rpds_py-2026.9.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8601470267d938bcb7f3ab1a336100af51a4fd5b6ed030ef52461bb3ef5e7e07"},
+ {file = "rpds_py-2026.9.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3890a6aa36e6baa53d5258a2a25d3ef8b37ad165a6ab27a892d7c3e3a432cd69"},
+ {file = "rpds_py-2026.9.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6b5b393eda5ea42cca1c1a6665f2a4882b4fd5d1777e41ce0545a107fb008c9d"},
+ {file = "rpds_py-2026.9.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:addeda51556dac7c1a2f14cda62db8b621cd12afba3091d03a96c72932387eab"},
+ {file = "rpds_py-2026.9.1-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:d9edf30457d74eebfd76b045535e36f1cd89062566a128a0db2145ca042d787e"},
+ {file = "rpds_py-2026.9.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:815d26356930846a40c7bc1366e7b1b0320ab8a063e66c11298a208bed0fd237"},
+ {file = "rpds_py-2026.9.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3b5a6f40f0a1486b4b36c888123afc67acdbd9f33235927acf5ff295429a0ba3"},
+ {file = "rpds_py-2026.9.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b5b8b0753718d258fd454283fbd57e14545d3b40583fa672e27cb4f987626bcc"},
+ {file = "rpds_py-2026.9.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:46d80bc76b51a6c24f9944368c28d38b8bcbcea1da4f2f8d3ebc31a67e8c6ec6"},
+ {file = "rpds_py-2026.9.1-cp312-cp312-win32.whl", hash = "sha256:befc2d6a953e563f8a7bfd87a42c22ebf8a3e980dcb7b6a4d17b70b0e914e8a3"},
+ {file = "rpds_py-2026.9.1-cp312-cp312-win_amd64.whl", hash = "sha256:5ce8943f79c2210f7abcc28e86367b03b28d95027fd01c46d2472373ae70c86f"},
+ {file = "rpds_py-2026.9.1-cp312-cp312-win_arm64.whl", hash = "sha256:501909f2e4a1e2dee528ef766fe3c469060ebc17e54a8383d404ba07a81a6f02"},
+ {file = "rpds_py-2026.9.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:a36b70596407634ca82d4b989a3729074a008537a0522e4c8046a67c729103e9"},
+ {file = "rpds_py-2026.9.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:eba5d173f7d5708b22a93815017a4611873ed54db9f268077c0dd1ed99cfc858"},
+ {file = "rpds_py-2026.9.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:457866b85daf5034296666168b84a69e0b2e89dc4f1af102b46f6448a60b9063"},
+ {file = "rpds_py-2026.9.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a3a52a3ba86436ab3aef510fbe21512abc2ddd1993005dfe50514bd2284ef025"},
+ {file = "rpds_py-2026.9.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d7841166b7fa64c9c56404617ae4341448847482d45933b13135d26c130519e5"},
+ {file = "rpds_py-2026.9.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:926bdd3e3b5998ddf70cc64bc8cf57209571f9044542913afb673799fec77dd0"},
+ {file = "rpds_py-2026.9.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7868b85224291c6cb6759f9b5adb9745f486d226f62b16a614dd5a2a5ab2b35b"},
+ {file = "rpds_py-2026.9.1-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:3cd182d7291d29b92c521a0069d9c01ba6193628a9a105531d11b40a6d731a33"},
+ {file = "rpds_py-2026.9.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e6ea1cda8d8c688278430e4268a42f5e5da3bdd74578dfadc0820c3f1766ce83"},
+ {file = "rpds_py-2026.9.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5943980471829f6de242a20b109de3111ba6b77e3af0ffc587028ac854b05e6c"},
+ {file = "rpds_py-2026.9.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:76d3af9732d2dab69f28179b40ba2d87e2f1d5824b4a694780aa787d685e8f36"},
+ {file = "rpds_py-2026.9.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:78326f4cb4427a56ba4996c0762b63be45f06b85f086526420d2b3a66e40f84d"},
+ {file = "rpds_py-2026.9.1-cp313-cp313-win32.whl", hash = "sha256:172e47169583f46ce118cbec68e6795d0da0f4606b488b6434f8276bca0a058c"},
+ {file = "rpds_py-2026.9.1-cp313-cp313-win_amd64.whl", hash = "sha256:3e93b2cd69a9830be33e03945cd7cda940a0a8bfcfbff41d6144f0cb0d3d8bd9"},
+ {file = "rpds_py-2026.9.1-cp313-cp313-win_arm64.whl", hash = "sha256:d151e148117294133bf8af7eeace085e7e87432db15ab6adf640330298a47f6f"},
+ {file = "rpds_py-2026.9.1-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:c9d1aca01f49170fdcf5c92761b1fafe97f554b721ca4570c5949fff778f0d4b"},
+ {file = "rpds_py-2026.9.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:3f0e9ac28fc067d4d34b88ae43c48e9489455c97fee9633d851f7eeed5a05d35"},
+ {file = "rpds_py-2026.9.1-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:07deecbfce94c78473018bc7d10b337cc651d12df87a1eb2cb3e4024bc9c33d0"},
+ {file = "rpds_py-2026.9.1-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:821b2755db9194409254012f429c56643416fb96ef9be090be82ec8826b7f477"},
+ {file = "rpds_py-2026.9.1-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3c91c210ae7645626c608400e3519b4a642f837cce09ca830db3beb2e9f274d4"},
+ {file = "rpds_py-2026.9.1-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:54ac2158a6f96cfbabff0b2eedaf94b90c5ec7ca8317fcadc61e1c2b2e0ff6ef"},
+ {file = "rpds_py-2026.9.1-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eac2f5dbafd585dfe31f86a23ebf0d3ba480a9d49ebc87947267b5608d4ea0cd"},
+ {file = "rpds_py-2026.9.1-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:8aa5dda18d39b6143eb24809d158f9252c88f402749b6f1b62a506cc7d96cc35"},
+ {file = "rpds_py-2026.9.1-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5c90e7fa02e8f5de0d10c17595c568ada48c5302e749462c0ea1a4c362111a86"},
+ {file = "rpds_py-2026.9.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:e6d198bad4e49dd6732fbd636e2fc5c082f45c8cad0b4acb756b00c82c76072e"},
+ {file = "rpds_py-2026.9.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:96beca19ec79de272e8668585380ff9092c47077c1d7a1e098e00bbd921f4785"},
+ {file = "rpds_py-2026.9.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:a5cf77eb04f20b720be95265a3e00eb2a14814074255cc27069c551b2db53118"},
+ {file = "rpds_py-2026.9.1-cp314-cp314-win32.whl", hash = "sha256:a03d57b86d2a51d0a66c92177e2be154ad015f357791d306e714569999cdb4cc"},
+ {file = "rpds_py-2026.9.1-cp314-cp314-win_amd64.whl", hash = "sha256:837c6b305e26fe0f75b15c92cf3b2ba29e0ae19dc40b1c557b026cb426347d0c"},
+ {file = "rpds_py-2026.9.1-cp314-cp314-win_arm64.whl", hash = "sha256:fce4b85234a0cbad67bf8e6e1201ee815d172c9aebad75f25645bc4d834f8e31"},
+ {file = "rpds_py-2026.9.1-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:3a72c11530d71abfb66c8d7696a2f86c43e63fca8b948f1a784ac490f4ec688e"},
+ {file = "rpds_py-2026.9.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:068c37bba854ec2fe42f7365c640af11dd9895890ccbf2df5070d0c059bd7f96"},
+ {file = "rpds_py-2026.9.1-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7fca4eb6df565e2a928f1c7dad92d27db8f9df0f449e76423ed5d7e713ed445"},
+ {file = "rpds_py-2026.9.1-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c933c6678c6f116ff8af47a4c6db0868b8ace74af0343016c0ef00f00272ea69"},
+ {file = "rpds_py-2026.9.1-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:028ad274ea951dac64491b5d1e65712a4aeabfdbdb9fccf797b57bd899b0c495"},
+ {file = "rpds_py-2026.9.1-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:740d0a99cf9de0b17a3943388e9294a59becf75e7c43421f387bd3c7a9901f7c"},
+ {file = "rpds_py-2026.9.1-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0da298fb372dc192610a4b9ecbc68a0cd8b675bbbd1fc519d01b41cfd658333e"},
+ {file = "rpds_py-2026.9.1-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:eb61be926bb81567c1f48bdc8aa22b9855048dc2efd53871f9f7e6e9a5632346"},
+ {file = "rpds_py-2026.9.1-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:42e75466f83cd43f6026c81eab74246efb2bdadafb307b85700632d06c68f299"},
+ {file = "rpds_py-2026.9.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:617f59cde379b4f648a09797b7f683d04b90a46344cddab85639da5aff0f5531"},
+ {file = "rpds_py-2026.9.1-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:3edae8c5ddfdb6985d49ae9d150516e5076888879022f91a26c2de9276ce0bdb"},
+ {file = "rpds_py-2026.9.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:0f045bb053c9057720d72c56dffe30dffdc05997b2897a827b9325f0ab6623fa"},
+ {file = "rpds_py-2026.9.1-cp314-cp314t-win32.whl", hash = "sha256:bf35d0568abda97233239ce32896d3ad53fccc537832c104e30c94aa5fb93569"},
+ {file = "rpds_py-2026.9.1-cp314-cp314t-win_amd64.whl", hash = "sha256:1e8d4d79d828299bf44a55db22a9388ab967b49d17132c88eab0f4360b48da8e"},
+ {file = "rpds_py-2026.9.1-cp315-cp315-macosx_10_12_x86_64.whl", hash = "sha256:1d77b649e6f7cdf12ca5c2a98dad0ad37f9ea9b6f960408a92f0cb12bb3d04d9"},
+ {file = "rpds_py-2026.9.1-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:00ba2d8c7dd4ee537978ddf4b3fbd712bef2d8751603f7f3146b3f4287768e25"},
+ {file = "rpds_py-2026.9.1-cp315-cp315-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec450527cbf485e13c8d3602a54f428ab0432fdade0ede75efd74b735421c871"},
+ {file = "rpds_py-2026.9.1-cp315-cp315-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:306ee1850d8105b5baf977e78d45fcadd12c1a54678d614c9baf217708446e91"},
+ {file = "rpds_py-2026.9.1-cp315-cp315-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ef6b65b03247c54692ad4fd9ee97cb772781927db72e3cb05e70b3db6d1ff14f"},
+ {file = "rpds_py-2026.9.1-cp315-cp315-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a575404ebc9cf2e91edd32eaf570ec1430eb900d4f56724ba7dd4bc1fc9c176d"},
+ {file = "rpds_py-2026.9.1-cp315-cp315-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2c16ab111bc27c646ba8aa005d0527754edc538ebb636f0b1bf8e244b48d1945"},
+ {file = "rpds_py-2026.9.1-cp315-cp315-manylinux_2_31_riscv64.whl", hash = "sha256:7664419f27db41d4f1c43a78dccda7dd6e8ef2428df3ee01d0c2a07a6b071297"},
+ {file = "rpds_py-2026.9.1-cp315-cp315-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4b26b03d9d2658ee2fa234f8f4f19f38a09773fe5261028025032e26d4d35af0"},
+ {file = "rpds_py-2026.9.1-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:be3e47e2d91aa3942ff9bf4077a505226005abfc39b6f7554a91c1b9393986b9"},
+ {file = "rpds_py-2026.9.1-cp315-cp315-musllinux_1_2_i686.whl", hash = "sha256:6307a0da524939decb8ca4a3933b8ab62525794411d6984fca6726e732804af6"},
+ {file = "rpds_py-2026.9.1-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:159a7aab5c5e8b112c8830f54717ce56da1252ebdbb526f5be2df2309280b9e7"},
+ {file = "rpds_py-2026.9.1-cp315-cp315-win32.whl", hash = "sha256:dbc2673f9223d420c91145599b3ba45a8a50c207d1976908e5fb5ddb0c9b9429"},
+ {file = "rpds_py-2026.9.1-cp315-cp315-win_amd64.whl", hash = "sha256:75c38c50ab9aca840225d9a9a3810bf11d04bd5c1f186cabbb8aee56db3e9b15"},
+ {file = "rpds_py-2026.9.1-cp315-cp315-win_arm64.whl", hash = "sha256:a431156bb41865fc14cd5d79bb9d7bbed83110b0159e34e62ae30951f96c0009"},
+ {file = "rpds_py-2026.9.1-cp315-cp315t-macosx_10_12_x86_64.whl", hash = "sha256:ef0d8c843e2827d6c120ab4687e9423fb1d893db1df27b7c1506615bcb9734a0"},
+ {file = "rpds_py-2026.9.1-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:45bc6bccf78b20fd834237d18db64965d7ee68ba7f60440a26c7ab71e7b8d51a"},
+ {file = "rpds_py-2026.9.1-cp315-cp315t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d55198263bb51f557550c6ed2e6d1cb6a6fed6eb5c9120b741c5926bef8a45d"},
+ {file = "rpds_py-2026.9.1-cp315-cp315t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a8763f20692da7df39b0afdd1ba3042b004c50a45994f76c2d9a25641f7673db"},
+ {file = "rpds_py-2026.9.1-cp315-cp315t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e43d4a1f673e8a1cbd8533e809e02b4bf9d4f2280269bb640436556312121250"},
+ {file = "rpds_py-2026.9.1-cp315-cp315t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ea394a937f17a54c51239348bdbe2e3518124c8d4a8951ba04a311d3095bd18f"},
+ {file = "rpds_py-2026.9.1-cp315-cp315t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cdeaa99ce822dca76cfb1b993e9120c5ea212f2eb66d48950ad63c349668a018"},
+ {file = "rpds_py-2026.9.1-cp315-cp315t-manylinux_2_31_riscv64.whl", hash = "sha256:b4f062343e7ad3fa94f2c66e5ae667dee47ee74dd41a9057c4fbe163236a123d"},
+ {file = "rpds_py-2026.9.1-cp315-cp315t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:22ffd29a63d71fb1b81552c21f2c2b734949b7ac751a9be70675a939a900839b"},
+ {file = "rpds_py-2026.9.1-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:08dae4a4095150a7c4545a1fb40b98e1ab1744fbc2770d92c977b9dadaa49ab6"},
+ {file = "rpds_py-2026.9.1-cp315-cp315t-musllinux_1_2_i686.whl", hash = "sha256:9a0460d43603d1fd9ef59c30278531e15d78581721ddb538fa560aa7817ea4ad"},
+ {file = "rpds_py-2026.9.1-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:1c2d1f6da5128eabf34e963d7163a818846075a52568250d006c4c953b40f903"},
+ {file = "rpds_py-2026.9.1-cp315-cp315t-win32.whl", hash = "sha256:5c6ee90dee3e85e055ddfd502d611643d9b0fd94c818220bda84ec3dacd9b27b"},
+ {file = "rpds_py-2026.9.1-cp315-cp315t-win_amd64.whl", hash = "sha256:fe5ad0664ec772b02c45859041aa17655709cced7a31005817fbbbd988c25567"},
+ {file = "rpds_py-2026.9.1-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4c0d2cb595a420b34d5086db0add011e26e2c09d6a024afbac4228bf8f863a30"},
+ {file = "rpds_py-2026.9.1-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f3d6ed6a98cfd19155996605474982cc470d7601746a6439078f1a5a3fa8b050"},
+ {file = "rpds_py-2026.9.1-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8171b44a054e5c67fd748ada04187f1250bf35b95f85e52ab64bcf3331a923bb"},
+ {file = "rpds_py-2026.9.1-pp311-pypy311_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fda1d96e542c37b6c804547dbf489c129fe7c97183a76a5ec275909ba1a063df"},
+ {file = "rpds_py-2026.9.1-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:270bdcdaac5d5b6f73c5e22e7e135c7f2a50e789f71d9e241d5be8d90026e19a"},
+ {file = "rpds_py-2026.9.1-pp311-pypy311_pp73-manylinux_2_31_riscv64.whl", hash = "sha256:84a6ecc0c940169190d2c23bd969debd48c94dbc855acd60188a68d71d421608"},
+ {file = "rpds_py-2026.9.1-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ab4b2fda7c2b542f7f9d886cc6a838c5079d2b76f72e6081411faba11adde2c9"},
+ {file = "rpds_py-2026.9.1-pp311-pypy311_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:44b32a7c4f0da3d28af31c259e38ddcff096f855e205ed0671d02fcf44f1ea1c"},
+ {file = "rpds_py-2026.9.1-pp311-pypy311_pp73-musllinux_1_2_i686.whl", hash = "sha256:a3dbc5ed9514908d5046107d7b1346bde71eea61de6e0e4919c19354f97e769f"},
+ {file = "rpds_py-2026.9.1-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:6eae33003518fd4cb4f83a218d5371469dd3001aa3b87128c005b07762f7fe5e"},
+ {file = "rpds_py-2026.9.1-pp311-pypy311_pp80-macosx_10_12_x86_64.whl", hash = "sha256:0483515261947e4e8b8e1375bf7463e7eb6ccfb3d86e7b554d90cd5285f20f32"},
+ {file = "rpds_py-2026.9.1-pp311-pypy311_pp80-macosx_11_0_arm64.whl", hash = "sha256:4cfaf02209061880210819934de2f4f6aa83dc04dafe6770276acc240a56da31"},
+ {file = "rpds_py-2026.9.1-pp312-pypy312_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6cdc537c8633d7fd92a82e2e0d2ab74320a3f63d5e59fb9cf08711e08fe151c4"},
+ {file = "rpds_py-2026.9.1-pp312-pypy312_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:10e208f2425d973938afcd56e28a7c4be32e27b6a60b5d381f49fb9d8acf9759"},
+ {file = "rpds_py-2026.9.1-pp312-pypy312_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6c0dbbcc19735fe5f8b0a54c07659d154a9e69f47e15d0a6ab7299215daf62cb"},
+ {file = "rpds_py-2026.9.1-pp312-pypy312_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:684fd492fff4fead00587544e059be2bbcb6f93454f21fa2a91b66fc7508be82"},
+ {file = "rpds_py-2026.9.1-pp312-pypy312_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d1028417bb44037eb3069c1009bd7b7277212876cda22fbe565b0bca9fab6d2c"},
+ {file = "rpds_py-2026.9.1-pp312-pypy312_pp73-manylinux_2_31_riscv64.whl", hash = "sha256:492e5e428cbe126221611f47e068f01660352feec4ad18bc0f5ea9b2ae88fb14"},
+ {file = "rpds_py-2026.9.1-pp312-pypy312_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:88b5268892fde430d5531f95bc560b6efbbd67c929662c586afd729a96e7461c"},
+ {file = "rpds_py-2026.9.1-pp312-pypy312_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:01445c8d194aa032a08e944f16567672da1c62dbdbefd8b6d0693032e290cf68"},
+ {file = "rpds_py-2026.9.1-pp312-pypy312_pp73-musllinux_1_2_i686.whl", hash = "sha256:eef6a03b0b6d08d0835ccfa8ec8d1bc70525e3801387567137b50c557695e6da"},
+ {file = "rpds_py-2026.9.1-pp312-pypy312_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:6b9bf3135b4ad5981df9a73d71a35272d650a2985ae9c2746357b24d59de2448"},
+ {file = "rpds_py-2026.9.1-pp312-pypy312_pp80-macosx_10_12_x86_64.whl", hash = "sha256:56c6952a9b15047466d0c2347c446a761d4527f89976156341e68f0ce5cc08b0"},
+ {file = "rpds_py-2026.9.1-pp312-pypy312_pp80-macosx_11_0_arm64.whl", hash = "sha256:b242c27c8f836305a4a72df9cdd564386ac57b807bd252a063223331c9316b37"},
+ {file = "rpds_py-2026.9.1.tar.gz", hash = "sha256:4793ef7f78268b124b73fa933440f01d258bbae01de9fa53e9080c9ab0425a12"},
]
[[package]]
--- /dev/null
+/**
+ * @file Tests for cross-version charging-profile type guards
+ * @description Verifies that extension properties cannot change a profile's protocol classification
+ */
+import assert from 'node:assert/strict'
+import { describe, it } from 'node:test'
+
+import {
+ isOCPP16ChargingProfile,
+ OCPP20ChargingProfileKindEnumType,
+ OCPP20ChargingProfilePurposeEnumType,
+ type OCPP20ChargingProfileType,
+ OCPP20ChargingRateUnitEnumType,
+} from '../../../src/types/index.js'
+
+await describe('ChargingProfile type guards', async () => {
+ await it('should not classify an OCPP 2.0 profile with an extension chargingProfileId as OCPP 1.6', () => {
+ const chargingProfile: OCPP20ChargingProfileType = {
+ chargingProfileId: 42,
+ chargingProfileKind: OCPP20ChargingProfileKindEnumType.Absolute,
+ chargingProfilePurpose: OCPP20ChargingProfilePurposeEnumType.TxProfile,
+ chargingSchedule: [
+ {
+ chargingRateUnit: OCPP20ChargingRateUnitEnumType.W,
+ chargingSchedulePeriod: [{ limit: 11_000, startPeriod: 0 }],
+ id: 1,
+ },
+ ],
+ id: 1,
+ stackLevel: 0,
+ }
+
+ assert.strictEqual(isOCPP16ChargingProfile(chargingProfile), false)
+ })
+})
--- /dev/null
+/**
+ * @file Tests for cross-version MeterValue type guards
+ * @description Verifies that protocol-specific guards remain disjoint at runtime
+ */
+import assert from 'node:assert/strict'
+import { describe, it } from 'node:test'
+
+import {
+ isOCPP16MeterValue,
+ isOCPP20MeterValue,
+ type MeterValue,
+} from '../../../src/types/index.js'
+
+await describe('MeterValue type guards', async () => {
+ await it('should classify an empty sampled-value list only as OCPP 1.6', () => {
+ const meterValue: MeterValue = {
+ sampledValue: [],
+ timestamp: new Date(),
+ }
+
+ assert.strictEqual(isOCPP16MeterValue(meterValue), true)
+ assert.strictEqual(isOCPP20MeterValue(meterValue), false)
+ })
+})
await it('should throw for unknown configuration section', () => {
assert.throws(() => {
- Configuration.getConfigurationSection('unknown' as ConfigurationSection)
+ Configuration.getConfigurationSection('unknown' as unknown as ConfigurationSection)
}, Error)
})
})
// Test unsupported storage type
assert.throws(() => {
- getDefaultPerformanceStorageUri('unsupported' as StorageType)
+ getDefaultPerformanceStorageUri('unsupported' as unknown as StorageType)
}, Error)
})
})
import {
buildAuthorizePayload,
buildStatusNotificationPayload,
- type ChargePointStatus,
- type OCPP16ChargePointErrorCode,
+ getEnumStringValue,
+ OCPP16ChargePointErrorCode,
+ OCPP16ChargePointStatus,
+ OCPP20ConnectorStatusEnumType,
OCPPVersion,
ProcedureName,
type RequestPayload,
if (ocppVersion === OCPPVersion.VERSION_16 && options.errorCode == null) {
throw new Error('--error-code is required for OCPP 1.6 stations')
}
+ const status =
+ ocppVersion === OCPPVersion.VERSION_16
+ ? getEnumStringValue(OCPP16ChargePointStatus, options.status)
+ : getEnumStringValue(OCPP20ConnectorStatusEnumType, options.status)
+ if (status == null) {
+ throw new Error(`Unsupported connector status '${options.status}'`)
+ }
+ const errorCode =
+ options.errorCode == null
+ ? undefined
+ : getEnumStringValue(OCPP16ChargePointErrorCode, options.errorCode)
+ if (options.errorCode != null && errorCode == null) {
+ throw new Error(`Unsupported OCPP 1.6 error code '${options.errorCode}'`)
+ }
payload = {
- ...buildStatusNotificationPayload(
- options.connectorId,
- options.status as ChargePointStatus,
- ocppVersion,
- {
- errorCode: options.errorCode as OCPP16ChargePointErrorCode | undefined,
- evseId: options.evseId,
- }
- ),
+ ...buildStatusNotificationPayload(options.connectorId, status, ocppVersion, {
+ errorCode,
+ evseId: options.evseId,
+ }),
...buildHashIdsPayload(resolvedHashIds),
}
await runAction(program, ProcedureName.STATUS_NOTIFICATION, payload, undefined, config)
-import type { DataConverter, WebSocketReadyState } from './types.js'
+import type { DataConverter } from './types.js'
-import { type WebSocketLike } from './types.js'
+import { type WebSocketLike, WebSocketReadyState } from './types.js'
interface RawWebSocket {
close(code?: number, reason?: string): void
},
get readyState (): WebSocketReadyState {
- return ws.readyState
+ switch (ws.readyState) {
+ case 0:
+ return WebSocketReadyState.CONNECTING
+ case 1:
+ return WebSocketReadyState.OPEN
+ case 2:
+ return WebSocketReadyState.CLOSING
+ case 3:
+ return WebSocketReadyState.CLOSED
+ default:
+ throw new RangeError(`Unknown WebSocket ready state: ${ws.readyState.toString()}`)
+ }
},
send (data: string): void {
export * from './types/UIProtocol.js'
export * from './types/UUID.js'
export * from './utils/converters.js'
+export * from './utils/enum.js'
export * from './utils/payloadBuilders.js'
export * from './utils/UUID.js'
export * from './utils/websocket.js'
--- /dev/null
+export const getEnumStringValue = <T extends string>(
+ enumObject: Readonly<Record<string, T>>,
+ value: unknown
+): T | undefined => {
+ for (const key in enumObject) {
+ if (Object.hasOwn(enumObject, key) && enumObject[key] === value) {
+ return enumObject[key]
+ }
+ }
+}
"jsdom": "^30.1.1",
"vite": "^8.3.1",
"vitest": "^5.0.3",
- "vue-tsc": "^3.3.11"
+ "vue-tsc": "^3.3.12"
}
}
<ToggleButton
:id="'add-charging-stations'"
:key="state.renderAddChargingStations"
- :off="
- () => {
- $router.push({ name: ROUTE_NAMES.CHARGING_STATIONS }).catch(() => undefined)
- }
- "
- :on="
- () => {
- $router.push({ name: ROUTE_NAMES.ADD_CHARGING_STATIONS }).catch(() => undefined)
- }
- "
+ :off="showChargingStations"
+ :on="showAddChargingStations"
:shared="true"
>
Add Charging Stations
const $route = useRoute()
const $router = useRouter()
+const showChargingStations = (): void => {
+ $router.push({ name: ROUTE_NAMES.CHARGING_STATIONS }).catch(() => undefined)
+}
+
+const showAddChargingStations = (): void => {
+ $router.push({ name: ROUTE_NAMES.ADD_CHARGING_STATIONS }).catch(() => undefined)
+}
+
const { activeSkinId, availableSkins: skins, switchSkin } = useSkin()
const { activeThemeId, availableThemes, switchTheme } = useTheme()
</template>
<script setup lang="ts">
-import type { OCPPVersion } from 'ui-common'
-
+import { getEnumStringValue, OCPPVersion } from 'ui-common'
import { computed } from 'vue'
import { useRoute, useRouter } from 'vue-router'
)
const ocppVersion = computed(() => {
const raw = $route.query.ocppVersion
- return typeof raw === 'string' ? (raw as OCPPVersion) : undefined
+ if (raw == null) return undefined
+ if (typeof raw !== 'string') {
+ throw new Error('Invalid OCPP version query parameter')
+ }
+ const parsedVersion = getEnumStringValue(OCPPVersion, raw)
+ if (parsedVersion == null) {
+ throw new Error(`Invalid OCPP version query parameter '${raw}'`)
+ }
+ return parsedVersion
})
const toggleButtonId = computed(
<ToggleButton
v-if="connector.transactionStarted !== true"
:id="`${hashId}-${evseId ?? 0}-${connectorId}-start-transaction`"
- :off="
- () => {
- $router.push({ name: ROUTE_NAMES.CHARGING_STATIONS }).catch(() => undefined)
- }
- "
- :on="
- () => {
- $router
- .push({
- name: ROUTE_NAMES.START_TRANSACTION,
- params: { hashId, chargingStationId, connectorId },
- query: {
- ...(evseId != null ? { evseId: String(evseId) } : {}),
- ...(ocppVersion != null ? { ocppVersion } : {}),
- },
- })
- .catch(() => undefined)
- }
- "
+ :off="showChargingStations"
+ :on="showStartTransaction"
:shared="true"
@clicked="$emit('need-refresh')"
>
import type { ChargePointStatus, ConnectorStatus, OCPPVersion, Status } from 'ui-common'
import {
+ getEnumStringValue,
isOCPP20x,
OCPP16ChargePointErrorCode,
OCPP16ChargePointStatus,
const $router = useRouter()
+const showChargingStations = (): void => {
+ $router.push({ name: ROUTE_NAMES.CHARGING_STATIONS }).catch(() => undefined)
+}
+
+const showStartTransaction = (): void => {
+ $router
+ .push({
+ name: ROUTE_NAMES.START_TRANSACTION,
+ params: {
+ chargingStationId: props.chargingStationId,
+ connectorId: props.connectorId,
+ hashId: props.hashId,
+ },
+ query: {
+ ...(props.evseId != null ? { evseId: String(props.evseId) } : {}),
+ ...(props.ocppVersion != null ? { ocppVersion: props.ocppVersion } : {}),
+ },
+ })
+ .catch(() => undefined)
+}
+
const {
lockConnector,
setConnectorStatus,
const errorCodeOptions = Object.values(OCPP16ChargePointErrorCode)
const selectedStatus = ref<ChargePointStatus>(
isOCPP20x(props.ocppVersion)
- ? ((props.connector.status as OCPP20ConnectorStatusEnumType | undefined) ??
+ ? (getEnumStringValue(OCPP20ConnectorStatusEnumType, props.connector.status) ??
OCPP20ConnectorStatusEnumType.AVAILABLE)
- : ((props.connector.status as OCPP16ChargePointStatus | undefined) ??
+ : (getEnumStringValue(OCPP16ChargePointStatus, props.connector.status) ??
OCPP16ChargePointStatus.AVAILABLE)
)
const selectedErrorCode = ref<OCPP16ChargePointErrorCode>(OCPP16ChargePointErrorCode.NO_ERROR)
/>
<ToggleButton
:id="`${chargingStation.stationInfo.hashId}-set-supervision-url`"
- :off="
- () => {
- $router.push({ name: ROUTE_NAMES.CHARGING_STATIONS }).catch(() => undefined)
- }
- "
- :on="
- () => {
- $router
- .push({
- name: ROUTE_NAMES.SET_SUPERVISION_URL,
- params: {
- hashId: chargingStation.stationInfo.hashId,
- chargingStationId: chargingStation.stationInfo.chargingStationId,
- },
- })
- .catch(() => undefined)
- }
- "
+ :off="showChargingStations"
+ :on="showSupervisionUrl"
:shared="true"
@clicked="$emit('need-refresh')"
>
</ToggleButton>
<ToggleButton
:id="`${chargingStation.stationInfo.hashId}-change-configuration`"
- :off="
- () => {
- $router.push({ name: ROUTE_NAMES.CHARGING_STATIONS }).catch(() => undefined)
- }
- "
- :on="
- () => {
- $router
- .push({
- name: ROUTE_NAMES.CHANGE_CONFIGURATION,
- params: {
- hashId: chargingStation.stationInfo.hashId,
- chargingStationId: chargingStation.stationInfo.chargingStationId,
- },
- })
- .catch(() => undefined)
- }
- "
+ :off="showChargingStations"
+ :on="showChangeConfiguration"
:shared="true"
@clicked="$emit('need-refresh')"
>
</ToggleButton>
<ToggleButton
:id="`${chargingStation.stationInfo.hashId}-show-details`"
- :off="
- () => {
- $router.push({ name: ROUTE_NAMES.CHARGING_STATIONS }).catch(() => undefined)
- }
- "
- :on="
- () => {
- $router
- .push({
- name: ROUTE_NAMES.SHOW_DETAILS,
- params: {
- hashId: chargingStation.stationInfo.hashId,
- chargingStationId: chargingStation.stationInfo.chargingStationId,
- },
- })
- .catch(() => undefined)
- }
- "
+ :off="showChargingStations"
+ :on="showDetails"
:shared="true"
@clicked="$emit('need-refresh')"
>
WebSocketReadyState,
} from 'ui-common'
import { computed } from 'vue'
+import { useRouter } from 'vue-router'
import {
deleteLocalStorageByKeyPattern,
const emit = defineEmits<{ 'need-refresh': [] }>()
+const $router = useRouter()
+
+const showChargingStations = (): void => {
+ $router.push({ name: ROUTE_NAMES.CHARGING_STATIONS }).catch(() => undefined)
+}
+
+const showStationAction = (
+ name:
+ | typeof ROUTE_NAMES.CHANGE_CONFIGURATION
+ | typeof ROUTE_NAMES.SET_SUPERVISION_URL
+ | typeof ROUTE_NAMES.SHOW_DETAILS
+): void => {
+ $router
+ .push({
+ name,
+ params: {
+ chargingStationId: props.chargingStation.stationInfo.chargingStationId,
+ hashId: props.chargingStation.stationInfo.hashId,
+ },
+ })
+ .catch(() => undefined)
+}
+
+const showSupervisionUrl = (): void => {
+ showStationAction(ROUTE_NAMES.SET_SUPERVISION_URL)
+}
+
+const showChangeConfiguration = (): void => {
+ showStationAction(ROUTE_NAMES.CHANGE_CONFIGURATION)
+}
+
+const showDetails = (): void => {
+ showStationAction(ROUTE_NAMES.SHOW_DETAILS)
+}
+
const isWebSocketOpen = computed(() => props.chargingStation.wsState === WebSocketReadyState.OPEN)
const connectorEntries = computed((): ConnectorEntry[] =>
const mockRoute = ref<{
name: string
params: Record<string, string>
- query: Record<string, string>
+ query: Record<string, string | string[]>
}>({
name: 'start-transaction',
params: { chargingStationId: TEST_STATION_ID, connectorId: '1', hashId: TEST_HASH_ID },
)
})
+ for (const invalidVersion of ['invalid', [OCPPVersion.VERSION_16, OCPPVersion.VERSION_20]]) {
+ it(`should reject the present invalid OCPP version ${JSON.stringify(invalidVersion)}`, async () => {
+ mockRoute.value.query = { evseId: '1', ocppVersion: invalidVersion }
+ const wrapper = mountStartTx()
+ await wrapper.find('input[name="idtag"]').setValue('RFID-001')
+
+ await wrapper.findComponent(ButtonStub).trigger('click')
+ await flushPromises()
+
+ expect(toastMock.error).toHaveBeenCalledWith('Error at authorizing RFID tag')
+ expect(mockClient.authorize).not.toHaveBeenCalled()
+ expect(mockClient.startTransaction).not.toHaveBeenCalled()
+ })
+ }
+
+ it('should preserve the legacy OCPP 1.6 fallback when version is absent', async () => {
+ mockRoute.value.query = { evseId: '1' }
+ const wrapper = mountStartTx()
+ await wrapper.find('input[type="checkbox"]').setValue(false)
+ await wrapper.find('input[name="idtag"]').setValue('RFID-001')
+
+ await wrapper.findComponent(ButtonStub).trigger('click')
+ await flushPromises()
+
+ expect(mockClient.authorize).not.toHaveBeenCalled()
+ expect(mockClient.startTransaction).toHaveBeenCalledWith(
+ TEST_HASH_ID,
+ expect.objectContaining({ ocppVersion: undefined })
+ )
+ })
+
it('should toast success on successful transaction start', async () => {
const wrapper = mountStartTx()
const idTagInput = wrapper.find('input[name="idtag"]')