From 8cbae75ae4613c08a896799adba86cc9bf1ee728 Mon Sep 17 00:00:00 2001 From: Juergen Kunz Date: Sun, 17 Aug 2025 08:14:08 +0000 Subject: [PATCH] fix(handlerproxy): Use SmartRequest API and improve proxy/asset response handling; update tests and bump dependencies; add local project configuration files --- .serena/project.yml | 68 ++++++++++++++++++++++ changelog.md | 10 ++++ package.json | 4 +- pnpm-lock.yaml | 8 +-- test/test.server.ts | 17 +++--- ts/00_commitinfo_data.ts | 2 +- ts/servertools/classes.handlerproxy.ts | 61 ++++++++++++------- ts/utilityservers/classes.websiteserver.ts | 5 +- 8 files changed, 140 insertions(+), 35 deletions(-) create mode 100644 .serena/project.yml diff --git a/.serena/project.yml b/.serena/project.yml new file mode 100644 index 0000000..4c68b41 --- /dev/null +++ b/.serena/project.yml @@ -0,0 +1,68 @@ +# language of the project (csharp, python, rust, java, typescript, go, cpp, or ruby) +# * For C, use cpp +# * For JavaScript, use typescript +# Special requirements: +# * csharp: Requires the presence of a .sln file in the project folder. +language: typescript + +# whether to use the project's gitignore file to ignore files +# Added on 2025-04-07 +ignore_all_files_in_gitignore: true +# list of additional paths to ignore +# same syntax as gitignore, so you can use * and ** +# Was previously called `ignored_dirs`, please update your config if you are using that. +# Added (renamed) on 2025-04-07 +ignored_paths: [] + +# whether the project is in read-only mode +# If set to true, all editing tools will be disabled and attempts to use them will result in an error +# Added on 2025-04-18 +read_only: false + + +# list of tool names to exclude. We recommend not excluding any tools, see the readme for more details. +# Below is the complete list of tools for convenience. +# To make sure you have the latest list of tools, and to view their descriptions, +# execute `uv run scripts/print_tool_overview.py`. +# +# * `activate_project`: Activates a project by name. +# * `check_onboarding_performed`: Checks whether project onboarding was already performed. +# * `create_text_file`: Creates/overwrites a file in the project directory. +# * `delete_lines`: Deletes a range of lines within a file. +# * `delete_memory`: Deletes a memory from Serena's project-specific memory store. +# * `execute_shell_command`: Executes a shell command. +# * `find_referencing_code_snippets`: Finds code snippets in which the symbol at the given location is referenced. +# * `find_referencing_symbols`: Finds symbols that reference the symbol at the given location (optionally filtered by type). +# * `find_symbol`: Performs a global (or local) search for symbols with/containing a given name/substring (optionally filtered by type). +# * `get_current_config`: Prints the current configuration of the agent, including the active and available projects, tools, contexts, and modes. +# * `get_symbols_overview`: Gets an overview of the top-level symbols defined in a given file. +# * `initial_instructions`: Gets the initial instructions for the current project. +# Should only be used in settings where the system prompt cannot be set, +# e.g. in clients you have no control over, like Claude Desktop. +# * `insert_after_symbol`: Inserts content after the end of the definition of a given symbol. +# * `insert_at_line`: Inserts content at a given line in a file. +# * `insert_before_symbol`: Inserts content before the beginning of the definition of a given symbol. +# * `list_dir`: Lists files and directories in the given directory (optionally with recursion). +# * `list_memories`: Lists memories in Serena's project-specific memory store. +# * `onboarding`: Performs onboarding (identifying the project structure and essential tasks, e.g. for testing or building). +# * `prepare_for_new_conversation`: Provides instructions for preparing for a new conversation (in order to continue with the necessary context). +# * `read_file`: Reads a file within the project directory. +# * `read_memory`: Reads the memory with the given name from Serena's project-specific memory store. +# * `remove_project`: Removes a project from the Serena configuration. +# * `replace_lines`: Replaces a range of lines within a file with new content. +# * `replace_symbol_body`: Replaces the full definition of a symbol. +# * `restart_language_server`: Restarts the language server, may be necessary when edits not through Serena happen. +# * `search_for_pattern`: Performs a search for a pattern in the project. +# * `summarize_changes`: Provides instructions for summarizing the changes made to the codebase. +# * `switch_modes`: Activates modes by providing a list of their names +# * `think_about_collected_information`: Thinking tool for pondering the completeness of collected information. +# * `think_about_task_adherence`: Thinking tool for determining whether the agent is still on track with the current task. +# * `think_about_whether_you_are_done`: Thinking tool for determining whether the task is truly completed. +# * `write_memory`: Writes a named memory (for future reference) to Serena's project-specific memory store. +excluded_tools: [] + +# initial prompt for the project. It will always be given to the LLM upon activating the project +# (contrary to the memories, which are loaded on demand). +initial_prompt: "" + +project_name: "typedserver" diff --git a/changelog.md b/changelog.md index ff7aed3..fd82786 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,15 @@ # Changelog +## 2025-08-16 - 3.0.76 - fix(handlerproxy) +Use SmartRequest API and improve proxy/asset response handling; update tests and bump dependencies; add local project configuration files + +- Replace deprecated smartrequest.request usage with SmartRequest fluent API in ts/servertools/classes.handlerproxy.ts and add explicit handling for GET/POST/PUT/DELETE/PATCH methods. +- Normalize proxied response body handling by using arrayBuffer()/text() and converting to Buffer to avoid body type inconsistencies. +- Switch asset fetching in ts/utilityservers/classes.websiteserver.ts to SmartRequest + arrayBuffer for reliable binary handling. +- Update tests (test/test.server.ts) to use SmartRequest.create() and to read response bodies via response.text(), matching the updated request API. +- Bump dependencies: @push.rocks/smartrequest -> ^4.2.1 and body-parser -> ^2.2.0. +- Add local project configuration files: .claude/settings.local.json and .serena/project.yml. + ## 2025-08-16 - 3.0.75 - fix(deps) Update dependencies, test tooling and test imports; enhance npm test script diff --git a/package.json b/package.json index 7764dad..1ee87a4 100644 --- a/package.json +++ b/package.json @@ -80,7 +80,7 @@ "@push.rocks/smartopen": "^2.0.0", "@push.rocks/smartpath": "^6.0.0", "@push.rocks/smartpromise": "^4.2.3", - "@push.rocks/smartrequest": "^2.1.0", + "@push.rocks/smartrequest": "^4.2.1", "@push.rocks/smartrx": "^3.0.10", "@push.rocks/smartsitemap": "^2.0.3", "@push.rocks/smartstream": "^3.2.5", @@ -90,7 +90,7 @@ "@push.rocks/webstore": "^2.0.20", "@tsclass/tsclass": "^9.2.0", "@types/express": "^5.0.3", - "body-parser": "^1.20.3", + "body-parser": "^2.2.0", "cors": "^2.8.5", "express": "^4.21.2", "express-force-ssl": "^0.3.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 57c7a26..dd0dce8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -75,8 +75,8 @@ importers: specifier: ^4.2.3 version: 4.2.3 '@push.rocks/smartrequest': - specifier: ^2.1.0 - version: 2.1.0 + specifier: ^4.2.1 + version: 4.2.1 '@push.rocks/smartrx': specifier: ^3.0.10 version: 3.0.10 @@ -105,8 +105,8 @@ importers: specifier: ^5.0.3 version: 5.0.3 body-parser: - specifier: ^1.20.3 - version: 1.20.3 + specifier: ^2.2.0 + version: 2.2.0 cors: specifier: ^2.8.5 version: 2.8.5 diff --git a/test/test.server.ts b/test/test.server.ts index 4df6e73..b14d51b 100644 --- a/test/test.server.ts +++ b/test/test.server.ts @@ -95,15 +95,18 @@ tap.test('should start the server allright', async () => { // see if a demo request holds up tap.test('should issue a request', async (tools) => { - const response = await smartrequest.postJson('http://127.0.0.1:3000/someroute', { - headers: { + const smartRequestInstance = smartrequest.SmartRequest.create(); + const response = await smartRequestInstance + .url('http://127.0.0.1:3000/someroute') + .headers({ 'X-Forwarded-Proto': 'https', - }, - requestBody: { + }) + .json({ someprop: 'hi', - }, - }); - console.log(response.body); + }) + .post(); + const responseBody = await response.text(); + console.log(responseBody); }); tap.test('should get a file from disk', async () => { diff --git a/ts/00_commitinfo_data.ts b/ts/00_commitinfo_data.ts index 8ff6e38..4ef53d3 100644 --- a/ts/00_commitinfo_data.ts +++ b/ts/00_commitinfo_data.ts @@ -3,6 +3,6 @@ */ export const commitinfo = { name: '@api.global/typedserver', - version: '3.0.75', + version: '3.0.76', description: 'A TypeScript-based project for easy serving of static files with support for live reloading, compression, and typed requests.' } diff --git a/ts/servertools/classes.handlerproxy.ts b/ts/servertools/classes.handlerproxy.ts index 024185f..774c342 100644 --- a/ts/servertools/classes.handlerproxy.ts +++ b/ts/servertools/classes.handlerproxy.ts @@ -19,18 +19,40 @@ export class HandlerProxy extends Handler { const relativeRequestPath = req.path.slice(req.route.path.length - 1); const proxyRequestUrl = remoteMountPointArg + relativeRequestPath; console.log(`proxy ${req.path} to ${proxyRequestUrl}`); - let proxiedResponse: plugins.smartrequest.IExtendedIncomingMessage; + let proxiedResponse: plugins.smartrequest.ICoreResponse; try { - proxiedResponse = await plugins.smartrequest.request(proxyRequestUrl, { - method: req.method, - autoJsonParse: false, - }); + const smartRequest = plugins.smartrequest.SmartRequest.create() + .url(proxyRequestUrl); + + // Execute request based on method + switch (req.method.toUpperCase()) { + case 'GET': + proxiedResponse = await smartRequest.get(); + break; + case 'POST': + proxiedResponse = await smartRequest.post(); + break; + case 'PUT': + proxiedResponse = await smartRequest.put(); + break; + case 'DELETE': + proxiedResponse = await smartRequest.delete(); + break; + case 'PATCH': + proxiedResponse = await smartRequest.patch(); + break; + default: + // For other methods, default to GET + proxiedResponse = await smartRequest.get(); + break; + } } catch { res.end('failed to fullfill request'); return; } - for (const header of Object.keys(proxiedResponse.headers)) { - res.set(header, proxiedResponse.headers[header] as string); + const headers = proxiedResponse.headers; + for (const header of Object.keys(headers)) { + res.set(header, headers[header] as string); } // set additional headers @@ -40,21 +62,20 @@ export class HandlerProxy extends Handler { } } - // Ensure body exists and convert it to Buffer consistently + // Get response body as buffer let responseToSend: Buffer; - - if (proxiedResponse.body !== undefined && proxiedResponse.body !== null) { - if (Buffer.isBuffer(proxiedResponse.body)) { - responseToSend = proxiedResponse.body; - } else if (typeof proxiedResponse.body === 'string') { - responseToSend = Buffer.from(proxiedResponse.body); - } else { - // Handle other types (like objects) by JSON stringifying them - responseToSend = Buffer.from(JSON.stringify(proxiedResponse.body)); + try { + const arrayBuffer = await proxiedResponse.arrayBuffer(); + responseToSend = Buffer.from(arrayBuffer); + } catch { + // If we can't get arrayBuffer, try text + try { + const text = await proxiedResponse.text(); + responseToSend = Buffer.from(text); + } catch { + // Provide a default empty buffer if body cannot be read + responseToSend = Buffer.from(''); } - } else { - // Provide a default empty buffer if body is undefined/null - responseToSend = Buffer.from(''); } if (optionsArg && optionsArg.responseModifier) { diff --git a/ts/utilityservers/classes.websiteserver.ts b/ts/utilityservers/classes.websiteserver.ts index 19ce196..af9c225 100644 --- a/ts/utilityservers/classes.websiteserver.ts +++ b/ts/utilityservers/classes.websiteserver.ts @@ -92,7 +92,10 @@ export class UtilityWebsiteServer { } const fullOriginAssetUrl = `https://assetbroker.lossless.one/brandfiles/00general/${manifestAssetName}`; console.log(`Getting ${manifestAssetName} from ${fullOriginAssetUrl}`); - const dataBuffer: Buffer = (await plugins.smartrequest.getBinary(fullOriginAssetUrl)).body; + const smartRequest = plugins.smartrequest.SmartRequest.create(); + const response = await smartRequest.url(fullOriginAssetUrl).get(); + const arrayBuffer = await response.arrayBuffer(); + const dataBuffer: Buffer = Buffer.from(arrayBuffer); res.type('.png'); res.write(dataBuffer); res.end();