From be36f41f04a5f281e09ae51c8a3176f4c3098205 Mon Sep 17 00:00:00 2001 From: ndrpp Date: Tue, 16 Dec 2025 11:36:26 +0200 Subject: [PATCH 01/16] feat: add logs to get/push config p2p handlers --- src/components/core/admin/fetchConfigHandler.ts | 6 ++++++ src/components/core/admin/pushConfigHandler.ts | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/src/components/core/admin/fetchConfigHandler.ts b/src/components/core/admin/fetchConfigHandler.ts index 7f1f005de..9a22fd9e0 100644 --- a/src/components/core/admin/fetchConfigHandler.ts +++ b/src/components/core/admin/fetchConfigHandler.ts @@ -14,7 +14,9 @@ export class FetchConfigHandler extends AdminCommandHandler { } async handle(task: AdminFetchConfigCommand): Promise { + console.log({ task, message: 'FETCH_CONFIG_P2P_COMMAND' }) const validation = await this.validate(task) + console.log({ validation, message: 'FETCH_CONFIG_P2P_COMMAND' }) if (!validation.valid) { return new Promise((resolve) => { resolve(buildInvalidParametersResponse(validation)) @@ -25,6 +27,10 @@ export class FetchConfigHandler extends AdminCommandHandler { const config = loadConfigFromFile() config.keys.privateKey = '[*** HIDDEN CONTENT ***]' + console.log({ + responseConfig: JSON.stringify(config), + message: 'FETCH_CONFIG_P2P_COMMAND' + }) return new Promise((resolve) => { resolve({ status: { httpStatus: 200 }, diff --git a/src/components/core/admin/pushConfigHandler.ts b/src/components/core/admin/pushConfigHandler.ts index 14d0ecb6d..d8255ce3b 100644 --- a/src/components/core/admin/pushConfigHandler.ts +++ b/src/components/core/admin/pushConfigHandler.ts @@ -43,12 +43,14 @@ export class PushConfigHandler extends AdminCommandHandler { } async handle(task: AdminPushConfigCommand): Promise { + console.log({ task, message: 'PUSH_CONFIG_P2P_COMMAND' }) const validation = await this.validate(task) if (!validation.valid) { return new Promise((resolve) => { resolve(buildInvalidParametersResponse(validation)) }) } + console.log({ validation, message: 'PUSH_CONFIG_P2P_COMMAND' }) try { const configPath = getConfigFilePath() @@ -62,6 +64,10 @@ export class PushConfigHandler extends AdminCommandHandler { newConfig.keys.privateKey = '[*** HIDDEN CONTENT ***]' CORE_LOGGER.logMessage('Configuration reloaded successfully') + console.log({ + responseConfig: JSON.stringify(newConfig), + message: 'PUSH_CONFIG_P2P_COMMAND' + }) return new Promise((resolve) => { resolve({ status: { httpStatus: 200 }, From 31447b4dd6d4e2bc7fe1fd0d9de61f1debb6c177 Mon Sep 17 00:00:00 2001 From: ndrpp Date: Tue, 16 Dec 2025 16:54:50 +0200 Subject: [PATCH 02/16] feat: use ERC1271 validation function for smart account calls --- src/@types/commands.ts | 1 + src/components/core/admin/adminHandler.ts | 3 ++- src/components/core/utils/nonceHandler.ts | 2 +- src/utils/auth.ts | 29 ++++++++++++++++++++--- 4 files changed, 30 insertions(+), 5 deletions(-) diff --git a/src/@types/commands.ts b/src/@types/commands.ts index c54ac14d2..723cb84c0 100644 --- a/src/@types/commands.ts +++ b/src/@types/commands.ts @@ -37,6 +37,7 @@ export interface GetP2PNetworkStatsCommand extends Command {} export interface AdminCommand extends Command { expiryTimestamp: number signature: string + address?: string } export interface AdminCollectFeesHandlerResponse { diff --git a/src/components/core/admin/adminHandler.ts b/src/components/core/admin/adminHandler.ts index c6bd50ed1..394b7c7bd 100644 --- a/src/components/core/admin/adminHandler.ts +++ b/src/components/core/admin/adminHandler.ts @@ -43,7 +43,8 @@ export abstract class AdminCommandHandler } const signatureValidation: CommonValidation = await validateAdminSignature( command.expiryTimestamp, - command.signature + command.signature, + command.address ) if (!signatureValidation.valid) { return buildInvalidRequestMessage( diff --git a/src/components/core/utils/nonceHandler.ts b/src/components/core/utils/nonceHandler.ts index 81cab8602..0f8332909 100644 --- a/src/components/core/utils/nonceHandler.ts +++ b/src/components/core/utils/nonceHandler.ts @@ -245,7 +245,7 @@ async function validateNonceAndSignature( } // Smart account validation -async function isERC1271Valid( +export async function isERC1271Valid( address: string, hash: string | Uint8Array, signature: string, diff --git a/src/utils/auth.ts b/src/utils/auth.ts index 7cc3839de..d58920557 100644 --- a/src/utils/auth.ts +++ b/src/utils/auth.ts @@ -8,13 +8,36 @@ import { getAccountsFromAccessList } from '../utils/credentials.js' import { OceanNodeConfig } from '../@types/OceanNode.js' import { LOG_LEVELS_STR } from './logging/Logger.js' import { CommonValidation } from './validators.js' +import { isERC1271Valid } from '../components/core/utils/nonceHandler.js' + export async function validateAdminSignature( expiryTimestamp: number, - signature: string + signature: string, + address?: string ): Promise { const message = expiryTimestamp.toString() - const signerAddress = ethers.verifyMessage(message, signature)?.toLowerCase() - CORE_LOGGER.logMessage(`Resolved signer address: ${signerAddress}`) + let signerAddress + + if (address) { + const config = await getConfiguration() + const firstChainId = Object.keys(config?.supportedNetworks || {})[0] + if (firstChainId) { + const provider = new ethers.JsonRpcProvider( + config.supportedNetworks[firstChainId].rpc + ) + + if (!(await isERC1271Valid(address, message, signature, provider))) { + return { valid: false, error: 'Invalid ERC1271 signature' } + } + signerAddress = address + } else { + return { valid: false, error: 'No network configured in node config' } + } + } else { + signerAddress = ethers.verifyMessage(message, signature)?.toLowerCase() + CORE_LOGGER.logMessage(`Resolved signer address: ${signerAddress}`) + } + try { const allowedAdmins: string[] = await getAdminAddresses() console.log(`Allowed admins: ${allowedAdmins}`) From bb9a1bb56c265b35a3a41bd9a9b09d33773ac4b6 Mon Sep 17 00:00:00 2001 From: ndrpp Date: Tue, 16 Dec 2025 17:58:24 +0200 Subject: [PATCH 03/16] feat: improve admin validation + add more logs for debugging --- src/components/core/utils/nonceHandler.ts | 2 + src/utils/auth.ts | 48 ++++++++++++++--------- 2 files changed, 32 insertions(+), 18 deletions(-) diff --git a/src/components/core/utils/nonceHandler.ts b/src/components/core/utils/nonceHandler.ts index 0f8332909..b05492106 100644 --- a/src/components/core/utils/nonceHandler.ts +++ b/src/components/core/utils/nonceHandler.ts @@ -258,7 +258,9 @@ export async function isERC1271Valid( provider ) const hashToUse = typeof hash === 'string' ? hash : ethers.hexlify(hash) + console.log({ hashToUse }) const result = await contract.isValidSignature(hashToUse, signature) + console.log({ result }) return result === '0x1626ba7e' // ERC-1271 magic value } catch { return false diff --git a/src/utils/auth.ts b/src/utils/auth.ts index d58920557..630108152 100644 --- a/src/utils/auth.ts +++ b/src/utils/auth.ts @@ -18,28 +18,32 @@ export async function validateAdminSignature( const message = expiryTimestamp.toString() let signerAddress - if (address) { + try { const config = await getConfiguration() - const firstChainId = Object.keys(config?.supportedNetworks || {})[0] - if (firstChainId) { - const provider = new ethers.JsonRpcProvider( - config.supportedNetworks[firstChainId].rpc - ) + if (address) { + const firstChainId = Object.keys(config?.supportedNetworks || {})[0] + if (firstChainId) { + console.log({ + chainId: firstChainId, + rpc: config.supportedNetworks[firstChainId].rpc + }) + const provider = new ethers.JsonRpcProvider( + config.supportedNetworks[firstChainId].rpc + ) - if (!(await isERC1271Valid(address, message, signature, provider))) { - return { valid: false, error: 'Invalid ERC1271 signature' } + if (!(await isERC1271Valid(address, message, signature, provider))) { + return { valid: false, error: 'Invalid ERC1271 signature' } + } + signerAddress = address + } else { + return { valid: false, error: 'No network configured in node config' } } - signerAddress = address } else { - return { valid: false, error: 'No network configured in node config' } + signerAddress = ethers.verifyMessage(message, signature)?.toLowerCase() + CORE_LOGGER.logMessage(`Resolved signer address: ${signerAddress}`) } - } else { - signerAddress = ethers.verifyMessage(message, signature)?.toLowerCase() - CORE_LOGGER.logMessage(`Resolved signer address: ${signerAddress}`) - } - try { - const allowedAdmins: string[] = await getAdminAddresses() + const allowedAdmins: string[] = await getAdminAddresses(config) console.log(`Allowed admins: ${allowedAdmins}`) if (allowedAdmins.length === 0) { @@ -71,8 +75,16 @@ export async function validateAdminSignature( } } -export async function getAdminAddresses(): Promise { - const config: OceanNodeConfig = await getConfiguration() +export async function getAdminAddresses( + existingConfig?: OceanNodeConfig +): Promise { + let config: OceanNodeConfig + if (!existingConfig) { + config = await getConfiguration() + } else { + config = existingConfig + } + const validAddresses: string[] = [] if (config.allowedAdmins && config.allowedAdmins.length > 0) { for (const admin of config.allowedAdmins) { From fa46ce73a218e7ecf356b7b956bd798638b59ca1 Mon Sep 17 00:00:00 2001 From: ndrpp Date: Wed, 17 Dec 2025 09:52:41 +0200 Subject: [PATCH 04/16] fix: hex the input message in case it's a smart account signature --- src/utils/auth.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/utils/auth.ts b/src/utils/auth.ts index 630108152..77844585e 100644 --- a/src/utils/auth.ts +++ b/src/utils/auth.ts @@ -21,6 +21,10 @@ export async function validateAdminSignature( try { const config = await getConfiguration() if (address) { + const hexMessage = ethers.solidityPackedKeccak256( + ['bytes'], + [ethers.hexlify(ethers.toUtf8Bytes(message))] + ) const firstChainId = Object.keys(config?.supportedNetworks || {})[0] if (firstChainId) { console.log({ @@ -31,7 +35,7 @@ export async function validateAdminSignature( config.supportedNetworks[firstChainId].rpc ) - if (!(await isERC1271Valid(address, message, signature, provider))) { + if (!(await isERC1271Valid(address, hexMessage, signature, provider))) { return { valid: false, error: 'Invalid ERC1271 signature' } } signerAddress = address From f06f9525aef65eb31eb3dbcee01e65d30d6a58cd Mon Sep 17 00:00:00 2001 From: ndrpp Date: Wed, 17 Dec 2025 10:46:11 +0200 Subject: [PATCH 05/16] chore: cleanup --- src/components/core/admin/fetchConfigHandler.ts | 6 ------ src/components/core/admin/pushConfigHandler.ts | 6 ------ src/components/core/utils/nonceHandler.ts | 2 -- 3 files changed, 14 deletions(-) diff --git a/src/components/core/admin/fetchConfigHandler.ts b/src/components/core/admin/fetchConfigHandler.ts index 9a22fd9e0..7f1f005de 100644 --- a/src/components/core/admin/fetchConfigHandler.ts +++ b/src/components/core/admin/fetchConfigHandler.ts @@ -14,9 +14,7 @@ export class FetchConfigHandler extends AdminCommandHandler { } async handle(task: AdminFetchConfigCommand): Promise { - console.log({ task, message: 'FETCH_CONFIG_P2P_COMMAND' }) const validation = await this.validate(task) - console.log({ validation, message: 'FETCH_CONFIG_P2P_COMMAND' }) if (!validation.valid) { return new Promise((resolve) => { resolve(buildInvalidParametersResponse(validation)) @@ -27,10 +25,6 @@ export class FetchConfigHandler extends AdminCommandHandler { const config = loadConfigFromFile() config.keys.privateKey = '[*** HIDDEN CONTENT ***]' - console.log({ - responseConfig: JSON.stringify(config), - message: 'FETCH_CONFIG_P2P_COMMAND' - }) return new Promise((resolve) => { resolve({ status: { httpStatus: 200 }, diff --git a/src/components/core/admin/pushConfigHandler.ts b/src/components/core/admin/pushConfigHandler.ts index d8255ce3b..14d0ecb6d 100644 --- a/src/components/core/admin/pushConfigHandler.ts +++ b/src/components/core/admin/pushConfigHandler.ts @@ -43,14 +43,12 @@ export class PushConfigHandler extends AdminCommandHandler { } async handle(task: AdminPushConfigCommand): Promise { - console.log({ task, message: 'PUSH_CONFIG_P2P_COMMAND' }) const validation = await this.validate(task) if (!validation.valid) { return new Promise((resolve) => { resolve(buildInvalidParametersResponse(validation)) }) } - console.log({ validation, message: 'PUSH_CONFIG_P2P_COMMAND' }) try { const configPath = getConfigFilePath() @@ -64,10 +62,6 @@ export class PushConfigHandler extends AdminCommandHandler { newConfig.keys.privateKey = '[*** HIDDEN CONTENT ***]' CORE_LOGGER.logMessage('Configuration reloaded successfully') - console.log({ - responseConfig: JSON.stringify(newConfig), - message: 'PUSH_CONFIG_P2P_COMMAND' - }) return new Promise((resolve) => { resolve({ status: { httpStatus: 200 }, diff --git a/src/components/core/utils/nonceHandler.ts b/src/components/core/utils/nonceHandler.ts index b05492106..0f8332909 100644 --- a/src/components/core/utils/nonceHandler.ts +++ b/src/components/core/utils/nonceHandler.ts @@ -258,9 +258,7 @@ export async function isERC1271Valid( provider ) const hashToUse = typeof hash === 'string' ? hash : ethers.hexlify(hash) - console.log({ hashToUse }) const result = await contract.isValidSignature(hashToUse, signature) - console.log({ result }) return result === '0x1626ba7e' // ERC-1271 magic value } catch { return false From c1c1a188fbe38415e91059bc69cdee926a245f57 Mon Sep 17 00:00:00 2001 From: ndrpp Date: Wed, 17 Dec 2025 10:46:23 +0200 Subject: [PATCH 06/16] fix: use correct hashing function for smart wallets sig validation --- src/utils/auth.ts | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/src/utils/auth.ts b/src/utils/auth.ts index 77844585e..4bf5f0adb 100644 --- a/src/utils/auth.ts +++ b/src/utils/auth.ts @@ -21,16 +21,9 @@ export async function validateAdminSignature( try { const config = await getConfiguration() if (address) { - const hexMessage = ethers.solidityPackedKeccak256( - ['bytes'], - [ethers.hexlify(ethers.toUtf8Bytes(message))] - ) - const firstChainId = Object.keys(config?.supportedNetworks || {})[0] + const hexMessage = ethers.hashMessage(message) + const firstChainId = Object.keys(config?.supportedNetworks || {})[1] if (firstChainId) { - console.log({ - chainId: firstChainId, - rpc: config.supportedNetworks[firstChainId].rpc - }) const provider = new ethers.JsonRpcProvider( config.supportedNetworks[firstChainId].rpc ) @@ -48,7 +41,6 @@ export async function validateAdminSignature( } const allowedAdmins: string[] = await getAdminAddresses(config) - console.log(`Allowed admins: ${allowedAdmins}`) if (allowedAdmins.length === 0) { const errorMsg = "Allowed admins list is empty. Please add admins' addresses." From d8eedfdde606daa5b7767f4ca258aa7a29ef96fe Mon Sep 17 00:00:00 2001 From: ndrpp Date: Wed, 17 Dec 2025 10:48:34 +0200 Subject: [PATCH 07/16] fix: use correct chainId for validation --- src/utils/auth.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/auth.ts b/src/utils/auth.ts index 4bf5f0adb..4726b8b97 100644 --- a/src/utils/auth.ts +++ b/src/utils/auth.ts @@ -22,7 +22,7 @@ export async function validateAdminSignature( const config = await getConfiguration() if (address) { const hexMessage = ethers.hashMessage(message) - const firstChainId = Object.keys(config?.supportedNetworks || {})[1] + const firstChainId = Object.keys(config?.supportedNetworks || {})[0] if (firstChainId) { const provider = new ethers.JsonRpcProvider( config.supportedNetworks[firstChainId].rpc From dbac8fe868bb111b8304ada1765415a7e160ddb6 Mon Sep 17 00:00:00 2001 From: ndrpp Date: Fri, 19 Dec 2025 14:44:04 +0200 Subject: [PATCH 08/16] feat: pass address from http handlers to p2p | update config on push --- src/OceanNode.ts | 10 ++++++++++ src/components/core/admin/pushConfigHandler.ts | 8 ++++++-- src/components/httpRoutes/adminConfig.ts | 10 ++++++---- 3 files changed, 22 insertions(+), 6 deletions(-) diff --git a/src/OceanNode.ts b/src/OceanNode.ts index 9e7846c04..80de1bd61 100644 --- a/src/OceanNode.ts +++ b/src/OceanNode.ts @@ -146,6 +146,16 @@ export class OceanNode { return this.auth } + public setConfig(config: OceanNodeConfig) { + this.config = config + if (this.config) { + this.escrow = new Escrow( + this.config.supportedNetworks, + this.config.claimDurationTimeout + ) + } + } + /** * Use this method to direct calls to the node as node cannot dial into itself * @param message command message diff --git a/src/components/core/admin/pushConfigHandler.ts b/src/components/core/admin/pushConfigHandler.ts index 14d0ecb6d..7a4839cb4 100644 --- a/src/components/core/admin/pushConfigHandler.ts +++ b/src/components/core/admin/pushConfigHandler.ts @@ -59,13 +59,17 @@ export class PushConfigHandler extends AdminCommandHandler { await this.saveConfigToFile(mergedConfig) const newConfig = await getConfiguration(true, false) - newConfig.keys.privateKey = '[*** HIDDEN CONTENT ***]' + this.getOceanNode().setConfig(newConfig) + await this.getOceanNode().addC2DEngines() + + const responseConfig = structuredClone(newConfig) + responseConfig.keys.privateKey = '[*** HIDDEN CONTENT ***]' CORE_LOGGER.logMessage('Configuration reloaded successfully') return new Promise((resolve) => { resolve({ status: { httpStatus: 200 }, - stream: new ReadableString(JSON.stringify(newConfig)) + stream: new ReadableString(JSON.stringify(responseConfig)) }) }) } catch (error) { diff --git a/src/components/httpRoutes/adminConfig.ts b/src/components/httpRoutes/adminConfig.ts index ed254c267..c00112367 100644 --- a/src/components/httpRoutes/adminConfig.ts +++ b/src/components/httpRoutes/adminConfig.ts @@ -10,12 +10,13 @@ export const adminConfigRoutes = express.Router() adminConfigRoutes.get('/api/admin/config', express.json(), async (req, res) => { try { - const { expiryTimestamp, signature } = req.body + const { expiryTimestamp, signature, address } = req.body const response = await new FetchConfigHandler(req.oceanNode).handle({ command: PROTOCOL_COMMANDS.FETCH_CONFIG, expiryTimestamp, - signature + signature, + address }) if (response.status.httpStatus === 200) { @@ -33,13 +34,14 @@ adminConfigRoutes.get('/api/admin/config', express.json(), async (req, res) => { adminConfigRoutes.post('/api/admin/config/update', express.json(), async (req, res) => { try { - const { expiryTimestamp, signature, config } = req.body + const { expiryTimestamp, signature, config, address } = req.body const response = await new PushConfigHandler(req.oceanNode).handle({ command: PROTOCOL_COMMANDS.PUSH_CONFIG, expiryTimestamp, signature, - config + config, + address }) if (response.status.httpStatus === 200) { From 1d1e92d6da2a0359c87244a259975aa1ff00620f Mon Sep 17 00:00:00 2001 From: ndrpp Date: Fri, 19 Dec 2025 15:59:15 +0200 Subject: [PATCH 09/16] feat: check if node has running jobs before updating config --- src/components/core/admin/pushConfigHandler.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/components/core/admin/pushConfigHandler.ts b/src/components/core/admin/pushConfigHandler.ts index 7a4839cb4..41b1429e3 100644 --- a/src/components/core/admin/pushConfigHandler.ts +++ b/src/components/core/admin/pushConfigHandler.ts @@ -51,6 +51,12 @@ export class PushConfigHandler extends AdminCommandHandler { } try { + const hasRunningJobs = + (await this.getOceanNode().getDatabase().c2d.getRunningJobs()).length > 0 + if (hasRunningJobs) { + throw new Error('Node has running jobs') + } + const configPath = getConfigFilePath() const configContent = await fs.promises.readFile(configPath, 'utf-8') const currentConfig = JSON.parse(configContent) From 0ea6af99e63900ca7afc2b93679a5752c6c5a3db Mon Sep 17 00:00:00 2001 From: ndrpp Date: Wed, 7 Jan 2026 11:42:37 +0200 Subject: [PATCH 10/16] test: add reject push config test when running jobs exist --- package-lock.json | 594 ++++++++++++++++-- .../core/admin/pushConfigHandler.ts | 10 +- src/test/integration/configAdmin.test.ts | 26 +- 3 files changed, 588 insertions(+), 42 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3659dcd24..183be513b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -177,6 +177,7 @@ "integrity": "sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.26.0", @@ -575,6 +576,278 @@ "node": ">=18.17" } }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.2.tgz", + "integrity": "sha512-wCIboOL2yXZym2cgm6mlA742s9QeJ8DjGVaL39dLN4rRwrOgOyYSnOaFPhKZGLb2ngj4EyfAFjsNJwPXZvseag==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.2.tgz", + "integrity": "sha512-NQhH7jFstVY5x8CKbcfa166GoV0EFkaPkCKBQkdPJFvo5u+nGXLEH/ooniLb3QI8Fk58YAx7nsPLozUWfCBOJA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.2.tgz", + "integrity": "sha512-5ZAX5xOmTligeBaeNEPnPaeEuah53Id2tX4c2CVP3JaROTH+j4fnfHCkr1PjXMd78hMst+TlkfKcW/DlTq0i4w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.2.tgz", + "integrity": "sha512-Ffcx+nnma8Sge4jzddPHCZVRvIfQ0kMsUsCMcJRHkGJ1cDmhe4SsrYIjLUKn1xpHZybmOqCWwB0zQvsjdEHtkg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.2.tgz", + "integrity": "sha512-MpM6LUVTXAzOvN4KbjzU/q5smzryuoNjlriAIx+06RpecwCkL9JpenNzpKd2YMzLJFOdPqBpuub6eVRP5IgiSA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.2.tgz", + "integrity": "sha512-5eRPrTX7wFyuWe8FqEFPG2cU0+butQQVNcT4sVipqjLYQjjh8a8+vUTfgBKM88ObB85ahsnTwF7PSIt6PG+QkA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.2.tgz", + "integrity": "sha512-mLwm4vXKiQ2UTSX4+ImyiPdiHjiZhIaE9QvC7sw0tZ6HoNMjYAqQpGyui5VRIi5sGd+uWq940gdCbY3VLvsO1w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.2.tgz", + "integrity": "sha512-6qyyn6TjayJSwGpm8J9QYYGQcRgc90nmfdUb0O7pp1s4lTY+9D0H9O02v5JqGApUyiHOtkz6+1hZNvNtEhbwRQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.2.tgz", + "integrity": "sha512-UHBRgJcmjJv5oeQF8EpTRZs/1knq6loLxTsjc3nxO9eXAPDLcWW55flrMVc97qFPbmZP31ta1AZVUKQzKTzb0g==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.2.tgz", + "integrity": "sha512-gq/sjLsOyMT19I8obBISvhoYiZIAaGF8JpeXu1u8yPv8BE5HlWYobmlsfijFIZ9hIVGYkbdFhEqC0NvM4kNO0g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.2.tgz", + "integrity": "sha512-bBYCv9obgW2cBP+2ZWfjYTU+f5cxRoGGQ5SeDbYdFCAZpYWrfjjfYwvUpP8MlKbP0nwZ5gyOU/0aUzZ5HWPuvQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.2.tgz", + "integrity": "sha512-SHNGiKtvnU2dBlM5D8CXRFdd+6etgZ9dXfaPCeJtz+37PIUlixvlIhI23L5khKXs3DIzAn9V8v+qb1TRKrgT5w==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.2.tgz", + "integrity": "sha512-hDDRlzE6rPeoj+5fsADqdUZl1OzqDYow4TB4Y/3PlKBD0ph1e6uPHzIQcv2Z65u2K0kpeByIyAjCmjn1hJgG0Q==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.2.tgz", + "integrity": "sha512-tsHu2RRSWzipmUi9UBDEzc0nLc4HtpZEI5Ba+Omms5456x5WaNuiG3u7xh5AO6sipnJ9r4cRWQB2tUjPyIkc6g==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.2.tgz", + "integrity": "sha512-k4LtpgV7NJQOml/10uPU0s4SAXGnowi5qBSjaLWMojNCUICNu7TshqHLAEbkBdAszL5TabfvQ48kK84hyFzjnw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.2.tgz", + "integrity": "sha512-GRa4IshOdvKY7M/rDpRR3gkiTNp34M0eLTaC1a08gNrh4u488aPhuZOCpkF6+2wl3zAN7L7XIpOFBhnaE3/Q8Q==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, "node_modules/@esbuild/linux-x64": { "version": "0.25.2", "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.2.tgz", @@ -592,6 +865,142 @@ "node": ">=18" } }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.2.tgz", + "integrity": "sha512-talAIBoY5M8vHc6EeI2WW9d/CkiO9MQJ0IOWX8hrLhxGbro/vBXJvaQXefW2cP0z0nQVTdQ/eNyGFV1GSKrxfw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.2.tgz", + "integrity": "sha512-voZT9Z+tpOxrvfKFyfDYPc4DO4rk06qamv1a/fkuzHpiVBMOhpjK+vBmWM8J1eiB3OLSMFYNaOaBNLXGChf5tg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.2.tgz", + "integrity": "sha512-dcXYOC6NXOqcykeDlwId9kB6OkPUxOEqU+rkrYVqJbK2hagWOMrsTGsMr8+rW02M+d5Op5NNlgMmjzecaRf7Tg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.2.tgz", + "integrity": "sha512-t/TkWwahkH0Tsgoq1Ju7QfgGhArkGLkF1uYz8nQS/PPFlXbP5YgRpqQR3ARRiC2iXoLTWFxc6DJMSK10dVXluw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.2.tgz", + "integrity": "sha512-cfZH1co2+imVdWCjd+D1gf9NjkchVhhdpgb1q5y6Hcv9TP6Zi9ZG/beI3ig8TvwT9lH9dlxLq5MQBBgwuj4xvA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.2.tgz", + "integrity": "sha512-7Loyjh+D/Nx/sOTzV8vfbB3GJuHdOQyrOryFdZvPHLf42Tk9ivBU5Aedi7iyX+x6rbn2Mh68T4qq1SDqJBQO5Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.2.tgz", + "integrity": "sha512-WRJgsz9un0nqZJ4MfhabxaD9Ft8KioqU3JMinOTvobbX6MOSUigSBlogP8QB3uxpJDsFS6yN+3FDBdqE5lg9kg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.2.tgz", + "integrity": "sha512-kM3HKb16VIXZyIeVrM1ygYmZBKybX8N4p754bw390wGO3Tf2j4L2/WYL+4suWujpgf6GBYs3jv7TyUivdd05JA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, "node_modules/@eslint-community/eslint-utils": { "version": "4.4.0", "dev": true, @@ -4403,6 +4812,7 @@ "integrity": "sha512-DhGl4xMVFGVIyMwswXeyzdL4uXD5OGILGX5N8Y+f6W7LhC1Ze2poSNrkF/fedpVDHEEZ+PHFW0vL14I+mm8K3Q==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@octokit/auth-token": "^6.0.0", "@octokit/graphql": "^9.0.3", @@ -5137,6 +5547,7 @@ "resolved": "https://registry.npmjs.org/@rdfjs/types/-/types-1.1.2.tgz", "integrity": "sha512-wqpOJK1QCbmsGNtyzYnojPU8gRDPid2JO0Q0kMtb4j65xhCK880cnKAfEOwC+dX85VJcCByQx5zOwyyfCjDJsg==", "license": "MIT", + "peer": true, "dependencies": { "@types/node": "*" } @@ -5557,6 +5968,7 @@ "node_modules/@types/rdfjs__namespace": { "version": "2.0.10", "license": "MIT", + "peer": true, "dependencies": { "@rdfjs/types": "*" } @@ -5581,7 +5993,6 @@ "node_modules/@types/rdfjs__prefix-map": { "version": "0.1.5", "license": "MIT", - "peer": true, "dependencies": { "@rdfjs/types": "*" } @@ -5597,7 +6008,6 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/@types/rdfjs__serializer-jsonld-ext/-/rdfjs__serializer-jsonld-ext-4.0.1.tgz", "integrity": "sha512-jgbQ/1kV7nESKG7SY8FJED6K4OFznr6Sz3ybF1ncpBR7TUBTuy3InpZOVRK4Wjpy2zi84iIAzJ1CIIo9NZh2Xw==", - "peer": true, "dependencies": { "@rdfjs/types": ">=1.0.0", "@types/jsonld": "*", @@ -5615,7 +6025,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/@types/rdfjs__serializer-turtle/-/rdfjs__serializer-turtle-1.1.0.tgz", "integrity": "sha512-NGHnbz5985UwS/YS6WL/FkS94B+QiVTdsfvJCqPwLmY3E7UeClw91c2KbiphZUR/uh7uwLwxeKKhV2T1gYgT5Q==", - "peer": true, "dependencies": { "@rdfjs/types": ">=1.0.0", "@types/node": "*", @@ -5794,6 +6203,7 @@ "version": "6.9.1", "dev": true, "license": "BSD-2-Clause", + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "6.9.1", "@typescript-eslint/types": "6.9.1", @@ -6104,6 +6514,7 @@ "version": "8.11.2", "dev": true, "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -6627,23 +7038,23 @@ "license": "MIT" }, "node_modules/body-parser": { - "version": "1.20.3", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", - "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", + "version": "1.20.4", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.4.tgz", + "integrity": "sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA==", "license": "MIT", "dependencies": { - "bytes": "3.1.2", + "bytes": "~3.1.2", "content-type": "~1.0.5", "debug": "2.6.9", "depd": "2.0.0", - "destroy": "1.2.0", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "on-finished": "2.4.1", - "qs": "6.13.0", - "raw-body": "2.5.2", + "destroy": "~1.2.0", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "on-finished": "~2.4.1", + "qs": "~6.14.0", + "raw-body": "~2.5.3", "type-is": "~1.6.18", - "unpipe": "1.0.0" + "unpipe": "~1.0.0" }, "engines": { "node": ">= 0.8", @@ -6659,12 +7070,41 @@ "ms": "2.0.0" } }, + "node_modules/body-parser/node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "license": "MIT", + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/body-parser/node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "license": "MIT" }, + "node_modules/body-parser/node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/bplist-parser": { "version": "0.2.0", "dev": true, @@ -6741,6 +7181,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "caniuse-lite": "^1.0.30001688", "electron-to-chromium": "^1.5.73", @@ -6802,6 +7243,15 @@ "version": "1.0.3", "license": "MIT" }, + "node_modules/buildcheck": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/buildcheck/-/buildcheck-0.0.7.tgz", + "integrity": "sha512-lHblz4ahamxpTmnsk+MNTRWsjYKv965MwOrSJyeD588rR3Jcu7swE+0wN5F+PbL5cjgu/9ObkhfzEPuofEMwLA==", + "optional": true, + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/builtins": { "version": "5.0.1", "dev": true, @@ -6826,6 +7276,8 @@ }, "node_modules/bytes": { "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", "license": "MIT", "engines": { "node": ">= 0.8" @@ -7535,6 +7987,27 @@ "node": ">= 0.10" } }, + "node_modules/cpu-features": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/cpu-features/-/cpu-features-0.0.10.tgz", + "integrity": "sha512-9IkYqtX3YHPCzoVg1Py+o9057a3i0fp7S530UWokCSaFVTc7CwXPRiOjRjBQQ18ZCNafx78YfnG+HALxtVmOGA==", + "hasInstallScript": true, + "optional": true, + "dependencies": { + "buildcheck": "~0.0.6", + "nan": "^2.19.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/cpu-features/node_modules/nan": { + "version": "2.24.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.24.0.tgz", + "integrity": "sha512-Vpf9qnVW1RaDkoNKFUvfxqAbtI8ncb8OJlqZ9wwpXzWPEsvsB1nvdUi6oYrHIkQ1Y/tMDnr1h4nczS0VB9Xykg==", + "license": "MIT", + "optional": true + }, "node_modules/crc-32": { "version": "1.2.2", "license": "Apache-2.0", @@ -8577,6 +9050,7 @@ "version": "8.52.0", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", @@ -8824,6 +9298,7 @@ "version": "2.29.0", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "array-includes": "^3.1.7", "array.prototype.findlastindex": "^1.2.3", @@ -8873,6 +9348,7 @@ "version": "15.7.0", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "builtins": "^5.0.1", "eslint-plugin-es": "^4.1.0", @@ -8984,6 +9460,7 @@ "version": "6.1.1", "dev": true, "license": "ISC", + "peer": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, @@ -8995,6 +9472,7 @@ "version": "7.33.2", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "array-includes": "^3.1.6", "array.prototype.flatmap": "^1.3.1", @@ -9562,21 +10040,6 @@ "version": "2.0.0", "license": "MIT" }, - "node_modules/express/node_modules/qs": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", - "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", - "license": "BSD-3-Clause", - "dependencies": { - "side-channel": "^1.1.0" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/exsolve": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.0.8.tgz", @@ -9959,6 +10422,21 @@ "devOptional": true, "license": "ISC" }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/function-bind": { "version": "1.1.2", "license": "MIT", @@ -10686,6 +11164,8 @@ }, "node_modules/iconv-lite": { "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", "license": "MIT", "dependencies": { "safer-buffer": ">= 2.1.2 < 3" @@ -12075,6 +12555,7 @@ "node_modules/ky": { "version": "0.33.3", "license": "MIT", + "peer": true, "engines": { "node": ">=14.16" }, @@ -14497,6 +14978,7 @@ "integrity": "sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA==", "dev": true, "license": "MIT", + "peer": true, "bin": { "prettier": "bin/prettier.cjs" }, @@ -14792,12 +15274,12 @@ } }, "node_modules/qs": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", - "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", + "version": "6.14.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz", + "integrity": "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==", "license": "BSD-3-Clause", "dependencies": { - "side-channel": "^1.0.6" + "side-channel": "^1.1.0" }, "engines": { "node": ">=0.6" @@ -14856,20 +15338,49 @@ } }, "node_modules/raw-body": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", - "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz", + "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==", "license": "MIT", "dependencies": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "unpipe": "~1.0.0" }, "engines": { "node": ">= 0.8" } }, + "node_modules/raw-body/node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "license": "MIT", + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/raw-body/node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/rc": { "version": "1.2.8", "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", @@ -16703,6 +17214,7 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -16983,6 +17495,7 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -17072,6 +17585,7 @@ "resolved": "https://registry.npmjs.org/undici/-/undici-5.29.0.tgz", "integrity": "sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg==", "license": "MIT", + "peer": true, "dependencies": { "@fastify/busboy": "^2.0.0" }, diff --git a/src/components/core/admin/pushConfigHandler.ts b/src/components/core/admin/pushConfigHandler.ts index 41b1429e3..8161d9709 100644 --- a/src/components/core/admin/pushConfigHandler.ts +++ b/src/components/core/admin/pushConfigHandler.ts @@ -54,7 +54,15 @@ export class PushConfigHandler extends AdminCommandHandler { const hasRunningJobs = (await this.getOceanNode().getDatabase().c2d.getRunningJobs()).length > 0 if (hasRunningJobs) { - throw new Error('Node has running jobs') + return new Promise((resolve) => { + resolve({ + status: { + httpStatus: 400, + error: 'Error pushing config: Node has running jobs' + }, + stream: null + }) + }) } const configPath = getConfigFilePath() diff --git a/src/test/integration/configAdmin.test.ts b/src/test/integration/configAdmin.test.ts index a444a2c27..71da66ca5 100644 --- a/src/test/integration/configAdmin.test.ts +++ b/src/test/integration/configAdmin.test.ts @@ -53,7 +53,7 @@ describe('Config Admin Endpoints Integration Tests', () => { config = await getConfiguration(true) database = await Database.init(config.dbConfig) - oceanNode = await OceanNode.getInstance(config, database) + oceanNode = OceanNode.getInstance(config, database) }) after(async () => { @@ -143,9 +143,33 @@ describe('Config Admin Endpoints Integration Tests', () => { }) describe('Push Config Tests', () => { + it('should reject push config when node has running jobs', async function () { + this.timeout(DEFAULT_TEST_TIMEOUT) + const runningJobs = await oceanNode.getDatabase().c2d.getRunningJobs() + + if (runningJobs.length > 0) { + const expiryTimestamp = Date.now() + 60000 + const signature = await getAdminSignature(expiryTimestamp) + + const newConfig = { + rateLimit: 100, + maxConnections: 200 + } + const handlerResponse = await new PushConfigHandler(oceanNode).handle({ + command: PROTOCOL_COMMANDS.PUSH_CONFIG, + expiryTimestamp, + signature, + config: newConfig + }) + + expect(handlerResponse.status.httpStatus).to.equal(400) + } + }) + it('should push config changes and reload node', async function () { this.timeout(DEFAULT_TEST_TIMEOUT) + await oceanNode.addC2DEngines() // make sure there are no running jobs const expiryTimestamp = Date.now() + 60000 const signature = await getAdminSignature(expiryTimestamp) From 882abbc4f8716bcbf4df2533b0cd0a1518caf5de Mon Sep 17 00:00:00 2001 From: ndrpp Date: Wed, 7 Jan 2026 12:06:58 +0200 Subject: [PATCH 11/16] test: add logs --- src/test/integration/configAdmin.test.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/test/integration/configAdmin.test.ts b/src/test/integration/configAdmin.test.ts index 71da66ca5..483e0e8ae 100644 --- a/src/test/integration/configAdmin.test.ts +++ b/src/test/integration/configAdmin.test.ts @@ -169,7 +169,11 @@ describe('Config Admin Endpoints Integration Tests', () => { it('should push config changes and reload node', async function () { this.timeout(DEFAULT_TEST_TIMEOUT) + const beforeJobs = await oceanNode.getDatabase().c2d.getRunningJobs() + console.log({ beforeJobs, msg: 'before restarting c2d engines' }) await oceanNode.addC2DEngines() // make sure there are no running jobs + const afterJobs = await oceanNode.getDatabase().c2d.getRunningJobs() + console.log({ afterJobs, msg: 'after restarting c2d engines' }) const expiryTimestamp = Date.now() + 60000 const signature = await getAdminSignature(expiryTimestamp) From 71bbb49f5dc828328db14da8797fa40f3937c3d2 Mon Sep 17 00:00:00 2001 From: ndrpp Date: Wed, 7 Jan 2026 12:42:01 +0200 Subject: [PATCH 12/16] test: properly stop running jobs for push config tests --- src/test/integration/configAdmin.test.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/test/integration/configAdmin.test.ts b/src/test/integration/configAdmin.test.ts index 483e0e8ae..cd4473b8e 100644 --- a/src/test/integration/configAdmin.test.ts +++ b/src/test/integration/configAdmin.test.ts @@ -169,11 +169,11 @@ describe('Config Admin Endpoints Integration Tests', () => { it('should push config changes and reload node', async function () { this.timeout(DEFAULT_TEST_TIMEOUT) - const beforeJobs = await oceanNode.getDatabase().c2d.getRunningJobs() - console.log({ beforeJobs, msg: 'before restarting c2d engines' }) - await oceanNode.addC2DEngines() // make sure there are no running jobs - const afterJobs = await oceanNode.getDatabase().c2d.getRunningJobs() - console.log({ afterJobs, msg: 'after restarting c2d engines' }) + const runningJobs = await oceanNode.getDatabase().c2d.getRunningJobs() + const engine = oceanNode.getC2DEngines().engines[0] + for (const runningJob of runningJobs) { + await engine.stopComputeJob(runningJob.jobId, runningJob.owner) + } const expiryTimestamp = Date.now() + 60000 const signature = await getAdminSignature(expiryTimestamp) From a57b05c7cffb6dcea0946b6c5638695604a832b2 Mon Sep 17 00:00:00 2001 From: ndrpp Date: Wed, 7 Jan 2026 12:59:59 +0200 Subject: [PATCH 13/16] test: change integration tests order --- .../{configAdmin.test.ts => _configAdmin.test.ts} | 5 ----- 1 file changed, 5 deletions(-) rename src/test/integration/{configAdmin.test.ts => _configAdmin.test.ts} (98%) diff --git a/src/test/integration/configAdmin.test.ts b/src/test/integration/_configAdmin.test.ts similarity index 98% rename from src/test/integration/configAdmin.test.ts rename to src/test/integration/_configAdmin.test.ts index cd4473b8e..0533b9e87 100644 --- a/src/test/integration/configAdmin.test.ts +++ b/src/test/integration/_configAdmin.test.ts @@ -169,11 +169,6 @@ describe('Config Admin Endpoints Integration Tests', () => { it('should push config changes and reload node', async function () { this.timeout(DEFAULT_TEST_TIMEOUT) - const runningJobs = await oceanNode.getDatabase().c2d.getRunningJobs() - const engine = oceanNode.getC2DEngines().engines[0] - for (const runningJob of runningJobs) { - await engine.stopComputeJob(runningJob.jobId, runningJob.owner) - } const expiryTimestamp = Date.now() + 60000 const signature = await getAdminSignature(expiryTimestamp) From 47070212f75021cf87ddbe99ea31521815db71e9 Mon Sep 17 00:00:00 2001 From: ndrpp Date: Wed, 7 Jan 2026 13:50:01 +0200 Subject: [PATCH 14/16] test: retry stopping jobs before push config handler test calls --- ...onfigAdmin.test.ts => configAdmin.test.ts} | 22 ++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) rename src/test/integration/{_configAdmin.test.ts => configAdmin.test.ts} (94%) diff --git a/src/test/integration/_configAdmin.test.ts b/src/test/integration/configAdmin.test.ts similarity index 94% rename from src/test/integration/_configAdmin.test.ts rename to src/test/integration/configAdmin.test.ts index 0533b9e87..92063f1d2 100644 --- a/src/test/integration/_configAdmin.test.ts +++ b/src/test/integration/configAdmin.test.ts @@ -16,9 +16,9 @@ import { RPCS } from '../../@types/blockchain.js' import { OceanNode } from '../../OceanNode.js' import { FetchConfigHandler } from '../../components/core/admin/fetchConfigHandler.js' import { PushConfigHandler } from '../../components/core/admin/pushConfigHandler.js' -import { streamToObject } from '../../utils/util.js' +import { sleep, streamToObject } from '../../utils/util.js' import { Readable } from 'stream' -import { expect } from 'chai' +import { expect, assert } from 'chai' describe('Config Admin Endpoints Integration Tests', () => { let config: OceanNodeConfig @@ -167,7 +167,23 @@ describe('Config Admin Endpoints Integration Tests', () => { }) it('should push config changes and reload node', async function () { - this.timeout(DEFAULT_TEST_TIMEOUT) + const runningJobs = await oceanNode.getDatabase().c2d.getRunningJobs() + const engine = oceanNode.getC2DEngines().engines[0] + for (const runningJob of runningJobs) { + await engine.stopComputeJob(runningJob.jobId, runningJob.owner) + } + + let retries = 0 + const MAX_RETRIES = 10 + do { + const latestRunningJobs = await oceanNode.getDatabase().c2d.getRunningJobs() + if (latestRunningJobs.length === 0) { + break + } + if (retries > MAX_RETRIES) assert.fail('Node still has running jobs') + retries++ + await sleep(2000) + } while (true) const expiryTimestamp = Date.now() + 60000 const signature = await getAdminSignature(expiryTimestamp) From 54ba953992bea0926c665dfa962561230672fca5 Mon Sep 17 00:00:00 2001 From: ndrpp Date: Wed, 7 Jan 2026 13:57:07 +0200 Subject: [PATCH 15/16] Revert "test: retry stopping jobs before push config handler test calls" This reverts commit 47070212f75021cf87ddbe99ea31521815db71e9. --- ...nfigAdmin.test.ts => _configAdmin.test.ts} | 22 +++---------------- 1 file changed, 3 insertions(+), 19 deletions(-) rename src/test/integration/{configAdmin.test.ts => _configAdmin.test.ts} (94%) diff --git a/src/test/integration/configAdmin.test.ts b/src/test/integration/_configAdmin.test.ts similarity index 94% rename from src/test/integration/configAdmin.test.ts rename to src/test/integration/_configAdmin.test.ts index 92063f1d2..0533b9e87 100644 --- a/src/test/integration/configAdmin.test.ts +++ b/src/test/integration/_configAdmin.test.ts @@ -16,9 +16,9 @@ import { RPCS } from '../../@types/blockchain.js' import { OceanNode } from '../../OceanNode.js' import { FetchConfigHandler } from '../../components/core/admin/fetchConfigHandler.js' import { PushConfigHandler } from '../../components/core/admin/pushConfigHandler.js' -import { sleep, streamToObject } from '../../utils/util.js' +import { streamToObject } from '../../utils/util.js' import { Readable } from 'stream' -import { expect, assert } from 'chai' +import { expect } from 'chai' describe('Config Admin Endpoints Integration Tests', () => { let config: OceanNodeConfig @@ -167,23 +167,7 @@ describe('Config Admin Endpoints Integration Tests', () => { }) it('should push config changes and reload node', async function () { - const runningJobs = await oceanNode.getDatabase().c2d.getRunningJobs() - const engine = oceanNode.getC2DEngines().engines[0] - for (const runningJob of runningJobs) { - await engine.stopComputeJob(runningJob.jobId, runningJob.owner) - } - - let retries = 0 - const MAX_RETRIES = 10 - do { - const latestRunningJobs = await oceanNode.getDatabase().c2d.getRunningJobs() - if (latestRunningJobs.length === 0) { - break - } - if (retries > MAX_RETRIES) assert.fail('Node still has running jobs') - retries++ - await sleep(2000) - } while (true) + this.timeout(DEFAULT_TEST_TIMEOUT) const expiryTimestamp = Date.now() + 60000 const signature = await getAdminSignature(expiryTimestamp) From 3d639a0bb41d0ace744e439d5e486afe72a9efc0 Mon Sep 17 00:00:00 2001 From: ndrpp Date: Thu, 8 Jan 2026 09:51:10 +0200 Subject: [PATCH 16/16] fix: remove redundant check for running jobs on push config --- .../core/admin/pushConfigHandler.ts | 14 ----------- ...onfigAdmin.test.ts => configAdmin.test.ts} | 23 ------------------- 2 files changed, 37 deletions(-) rename src/test/integration/{_configAdmin.test.ts => configAdmin.test.ts} (94%) diff --git a/src/components/core/admin/pushConfigHandler.ts b/src/components/core/admin/pushConfigHandler.ts index 8161d9709..7a4839cb4 100644 --- a/src/components/core/admin/pushConfigHandler.ts +++ b/src/components/core/admin/pushConfigHandler.ts @@ -51,20 +51,6 @@ export class PushConfigHandler extends AdminCommandHandler { } try { - const hasRunningJobs = - (await this.getOceanNode().getDatabase().c2d.getRunningJobs()).length > 0 - if (hasRunningJobs) { - return new Promise((resolve) => { - resolve({ - status: { - httpStatus: 400, - error: 'Error pushing config: Node has running jobs' - }, - stream: null - }) - }) - } - const configPath = getConfigFilePath() const configContent = await fs.promises.readFile(configPath, 'utf-8') const currentConfig = JSON.parse(configContent) diff --git a/src/test/integration/_configAdmin.test.ts b/src/test/integration/configAdmin.test.ts similarity index 94% rename from src/test/integration/_configAdmin.test.ts rename to src/test/integration/configAdmin.test.ts index 0533b9e87..e971d5508 100644 --- a/src/test/integration/_configAdmin.test.ts +++ b/src/test/integration/configAdmin.test.ts @@ -143,29 +143,6 @@ describe('Config Admin Endpoints Integration Tests', () => { }) describe('Push Config Tests', () => { - it('should reject push config when node has running jobs', async function () { - this.timeout(DEFAULT_TEST_TIMEOUT) - const runningJobs = await oceanNode.getDatabase().c2d.getRunningJobs() - - if (runningJobs.length > 0) { - const expiryTimestamp = Date.now() + 60000 - const signature = await getAdminSignature(expiryTimestamp) - - const newConfig = { - rateLimit: 100, - maxConnections: 200 - } - const handlerResponse = await new PushConfigHandler(oceanNode).handle({ - command: PROTOCOL_COMMANDS.PUSH_CONFIG, - expiryTimestamp, - signature, - config: newConfig - }) - - expect(handlerResponse.status.httpStatus).to.equal(400) - } - }) - it('should push config changes and reload node', async function () { this.timeout(DEFAULT_TEST_TIMEOUT)