From cbc8e0df2145a21ac9876dc4d9d61265464f72ba Mon Sep 17 00:00:00 2001 From: Felix Hofmann Date: Fri, 19 Jun 2026 18:20:04 +0200 Subject: [PATCH 01/13] fix: simplify payload check in RequestHandler options --- packages/rest-client/src/lib/RequestHandler.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/rest-client/src/lib/RequestHandler.ts b/packages/rest-client/src/lib/RequestHandler.ts index bab64f7..93f8c69 100644 --- a/packages/rest-client/src/lib/RequestHandler.ts +++ b/packages/rest-client/src/lib/RequestHandler.ts @@ -54,7 +54,7 @@ export class RequestHandler { headers: this.#createRequestHeaders(signature), }; - if (typeof payload !== "undefined") { + if (payload !== undefined) { options.body = JSON.stringify(payload); } From 91e67198d0ed3b7d893b4068fff963a3ab315889 Mon Sep 17 00:00:00 2001 From: Felix Hofmann Date: Fri, 19 Jun 2026 18:25:33 +0200 Subject: [PATCH 02/13] fix: use `Number.parseInt` for consistency with modern standards --- packages/rest-client/src/lib/RestClient.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/rest-client/src/lib/RestClient.ts b/packages/rest-client/src/lib/RestClient.ts index 6bb864e..98c1a2d 100644 --- a/packages/rest-client/src/lib/RestClient.ts +++ b/packages/rest-client/src/lib/RestClient.ts @@ -154,7 +154,7 @@ export class RestClient { const { port } = parsedResult.data; return { ...parsedResult.data, - port: parseInt(port), + port: Number.parseInt(port), }; } From 9a05d5679d7964a0b1ed416494e0ce9a771c9e2b Mon Sep 17 00:00:00 2001 From: Felix Hofmann Date: Fri, 19 Jun 2026 18:29:11 +0200 Subject: [PATCH 03/13] fix: update `inv.invOutFreq` schema to include 0 as a valid value --- packages/schemas/src/delta2/getProperties.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/schemas/src/delta2/getProperties.ts b/packages/schemas/src/delta2/getProperties.ts index 5de7bad..43623c1 100644 --- a/packages/schemas/src/delta2/getProperties.ts +++ b/packages/schemas/src/delta2/getProperties.ts @@ -156,8 +156,7 @@ export const delta2QuotaAllSchema = z "inv.inputWatts": integer, // Inverter output current (mA) "inv.invOutAmp": integer, - // Inverter output frequency (Hz): 50 or 60; - // @todo: actual can be 0 , but docs are not mentioning it. + // Inverter output frequency (Hz): 0 or 50 or 60; "inv.invOutFreq": z.literal(50).or(z.literal(60)).or(z.literal(0)), // Inverter actual output voltage (mV) "inv.invOutVol": integer, From e2a2061acb7381b9410e3d706e85908952c249ee Mon Sep 17 00:00:00 2001 From: Felix Hofmann Date: Fri, 19 Jun 2026 18:36:08 +0200 Subject: [PATCH 04/13] chore: add changeset --- .changeset/six-pianos-peel.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/six-pianos-peel.md diff --git a/.changeset/six-pianos-peel.md b/.changeset/six-pianos-peel.md new file mode 100644 index 0000000..4a22dd8 --- /dev/null +++ b/.changeset/six-pianos-peel.md @@ -0,0 +1,5 @@ +--- +"@ecoflow-api/rest-client": patch +--- + +remove codesmells in request-handler and rest-client From cb1d21120af6962f1e4e561b44debaeb0ae3d3a0 Mon Sep 17 00:00:00 2001 From: Felix Hofmann Date: Fri, 19 Jun 2026 19:35:35 +0200 Subject: [PATCH 05/13] feat(schemas): update powerStream properties with detailed ecoflow documentation - Replaced undocumented fields with descriptions based on official ecoflow docs. - Updated type definitions for on/off flags to `zeroOrOne`. - Added proper documentation and improved schema accuracy. --- .changeset/soft-coats-report.md | 5 + .../src/__fixtures__/powerStreamProperties.ts | 6 +- .../schemas/src/powerStream/getProperties.ts | 117 +++++++++--------- 3 files changed, 64 insertions(+), 64 deletions(-) create mode 100644 .changeset/soft-coats-report.md diff --git a/.changeset/soft-coats-report.md b/.changeset/soft-coats-report.md new file mode 100644 index 0000000..3de0194 --- /dev/null +++ b/.changeset/soft-coats-report.md @@ -0,0 +1,5 @@ +--- +"@ecoflow-api/schemas": minor +--- + +update powerstream properties according to the ecoflow docs diff --git a/packages/rest-client/src/__fixtures__/powerStreamProperties.ts b/packages/rest-client/src/__fixtures__/powerStreamProperties.ts index f1b9f36..a9e3267 100644 --- a/packages/rest-client/src/__fixtures__/powerStreamProperties.ts +++ b/packages/rest-client/src/__fixtures__/powerStreamProperties.ts @@ -22,7 +22,7 @@ export const propertiesFixture = { "20_1.historyInvOutputWatts": 0, "20_1.pvToInvWatts": 210, "20_1.pv1ErrCode": 0, - "20_1.invLoadLimitFlag": 8, + "20_1.invLoadLimitFlag": 1, "20_1.batLoadLimitFlag": 0, "20_1.geneNum": 1, "20_1.invToPlugWatts": 1260, @@ -366,7 +366,7 @@ export const propertiesFixture = { }, }, "20_1.pv2CtrlMpptOffFlag": 0, - "20_1.uwloadLimitFlag": 5, + "20_1.uwloadLimitFlag": 1, "20_1.floadLimitOut": 1660, "20_1.pv1Statue": 4, "20_134.updateTime": "2024-07-05 11:36:11", @@ -423,7 +423,7 @@ export const propertiesFixture = { "20_1.bmsReqChgAmp": 0, "20_1.invRelayStatus": 8, "20_1.historyPermanentWatts": 0, - "20_1.antiBackFlowFlag": 6000, + "20_1.antiBackFlowFlag": 1, "20_1.batOffFlag": 0, "20_1.llcInputVolt": 0, "20_1.updateTime": "2024-07-06 01:50:27", diff --git a/packages/schemas/src/powerStream/getProperties.ts b/packages/schemas/src/powerStream/getProperties.ts index 8078ebb..9b8d7cd 100644 --- a/packages/schemas/src/powerStream/getProperties.ts +++ b/packages/schemas/src/powerStream/getProperties.ts @@ -25,9 +25,8 @@ export const powerStreamQuotaAllSchema = z "20_1.pv1InputWatts": integer.nonnegative(), // PV1 output voltage: 0.1 V "20_1.pv1OpVolt": integer.nonnegative(), - - // @todo: Not documented in the official docs. - "20_1.pv1CtrlMpptOffFlag": integer, + // PV1 on/off status: 0: off; 1: on + "20_1.pv1CtrlMpptOffFlag": zeroOrOne, /********** * PV 2 @@ -51,9 +50,8 @@ export const powerStreamQuotaAllSchema = z "20_1.pv2InputWatts": integer.nonnegative(), // PV2 input voltage: 0.1 V "20_1.pv2InputVolt": integer.nonnegative(), - - // @todo: Not documented in the official docs. - "20_1.pv2CtrlMpptOffFlag": integer, + // PV2 on/off status: 0: off; 1: on + "20_1.pv2CtrlMpptOffFlag": zeroOrOne, /********** * LLC @@ -71,9 +69,8 @@ export const powerStreamQuotaAllSchema = z "20_1.llcInputVolt": integer.nonnegative(), // LLC warning code "20_1.llcWarningCode": integer, - - // @todo: Not documented in the official docs. - "20_1.llcOffFlag": integer, + // LLC on/off status: 0: off; 1: on + "20_1.llcOffFlag": zeroOrOne, /********** * BAT @@ -99,17 +96,16 @@ export const powerStreamQuotaAllSchema = z "20_1.batTemp": integer, // BAT input current: 0.1 A; positive for discharging and negative for charging "20_1.batInputCur": integer, - - // @todo: Not documented in the official docs. + // Limited DC output power when DC power is low. Unit: 0.1W "20_1.batErrorInvLoadLimit": integer.nonnegative(), - // @todo: Not documented in the official docs. - "20_1.batLoadLimitFlag": integer, - // @todo: Not documented in the official docs. + // Whether the BAT module is derated.0: no; 1: yes + "20_1.batLoadLimitFlag": zeroOrOne, + // BAT power after derating. Unit: 0.1W "20_1.batOutputLoadLimit": integer, - // @todo: Not documented in the official docs. - "20_1.batOffFlag": integer, - // @todo: Not documented in the official docs. - "20_1.batSystem": integer, + // Battery on/off status: 0: off; 1: on + "20_1.batOffFlag": zeroOrOne, + // Whether PowerStream is connected to a power station or an EV 0: power station; 1: EV + "20_1.batSystem": zeroOrOne, /********** * INV @@ -143,15 +139,14 @@ export const powerStreamQuotaAllSchema = z "20_1.invRelayStatus": integer, // Micro-inverter AC warning code "20_1.invWarnCode": integer, - - // @todo: Not documented in the official docs. + // PV power after derating. Unit: 0.1W "20_1.invOutputLoadLimit": integer, // @todo: Not documented in the official docs. "20_1.invToPlugWatts": integer, // @todo: Not documented in the official docs. "20_1.invDemandWatts": integer, - // @todo: Not documented in the official docs. - "20_1.invLoadLimitFlag": integer, + // Whether the PV module is derated.0: no; 1: yes + "20_1.invLoadLimitFlag": zeroOrOne, // @todo: Not documented in the official docs. "20_1.invToOtherWatts": integer, @@ -200,79 +195,79 @@ export const powerStreamQuotaAllSchema = z // Charge Level "20_1.upperLimit": integer.nonnegative(), - // @todo: Not documented in the official docs. - "20_1.mqttTlsLastErr": integer, - // @todo: Not documented in the official docs. + // MQTT network error code + "20_1.mqttTlsLastErr": integer.nonnegative(), + // Number of devices consuming power "20_1.consNum": integer, - // @todo: Not documented in the official docs. - "20_1.feedProtect": integer, - // @todo: Not documented in the official docs. + // Feed-in control: 0: off; 1: on + "20_1.feedProtect": zeroOrOne, + // MQTT network error code "20_1.mqttSockErrno": integer.nonnegative(), // @todo: Not documented in the official docs. "20_1.pvToInvWatts": integer, - // @todo: Not documented in the official docs. + // Number of devices generating power "20_1.geneNum": integer, - // @todo: Not documented in the official docs. + // Mesh ID "20_1.meshId": integer, - // @todo: Not documented in the official docs. + // MQTT network error code "20_1.mqttTlsStackErr": integer, - // @todo: Not documented in the official docs. - "20_1.acOffFlag": integer, - // @todo: Not documented in the official docs. + // INV on/off status: 0: off; 1: on + "20_1.acOffFlag": zeroOrOne, + // Timestamp of the MQQT error code "20_1.mqttErrTime": integer, - // @todo: Not documented in the official docs - looks like, can be negative + // Static IP address, for determining whether devices are in the same LAN "20_1.staIpAddr": integer, - // @todo: Not documented in the official docs. - "20_1.uwlowLightFlag": integer, - // @todo: Not documented in the official docs - looks like, can be negative + // Whether anti-backflow is triggered due to low light.0: no; 1: yes + "20_1.uwlowLightFlag": zeroOrOne, + // Power consumed by Smart Plugs "20_1.consWatt": integer, - // @todo: Not documented in the official docs. + // Mesh layer "20_1.meshLayel": integer, - // @todo: Not documented in the official docs. - "20_1.uwloadLimitFlag": integer, - // @todo: Not documented in the official docs. + // Whether the INV module is derated.0: no; 1: yes + "20_1.uwloadLimitFlag": zeroOrOne, + // INV power after derating. Unit: 0.1W "20_1.floadLimitOut": integer, // @todo: Not documented in the official docs. "20_134.updateTime": z.string(), - // @todo: Not documented in the official docs. + // Timestamp of the Wi-Fi error code "20_1.wifiErrTime": integer.nonnegative(), - // @todo: Not documented in the official docs. + // Minimal stack space remaining "20_1.stackMinFree": integer, - // @todo: Not documented in the official docs. + // Number of restarts "20_1.resetCount": integer, - // @todo: Not documented in the official docs. - "20_1.uwsocFlag": integer, + // Whether anti-backflow is triggered by the battery.0: no; 1: yes + "20_1.uwsocFlag": zeroOrOne, // @todo: Not documented in the official docs. "20_1.plugTotalWatts": integer.nonnegative(), - // @todo: Not documented in the official docs. + // Cause of restart "20_1.resetReason": integer, - // @todo: Not documented in the official docs. + // Stack space remaining "20_1.stackFree": integer, - // @todo: Not documented in the official docs. + // MAC address "20_1.selfMac": integer, // @todo: Not documented in the official docs. "20_1.gridConsWatts": integer, - // @todo: Not documented in the official docs. + // Power generated "20_1.geneWatt": integer, - // @todo: Not documented in the official docs. + // BMS requesting voltage "20_1.bmsReqChgVol": integer, - // @todo: Not documented in the official docs. + // Port connection flag: bit0: AC connected; bit1: BAT connected; bit2: PV1connected; bit3: PV2 connected "20_1.interfaceConnFlag": integer, // @todo: Not documented in the official docs. "20_1.spaceDemandWatts": integer.nonnegative(), - // @todo: Not documented in the official docs. + // BMS requesting current "20_1.bmsReqChgAmp": integer, - // @todo: Not documented in the official docs. - "20_1.antiBackFlowFlag": integer, - // @todo: Not documented in the official docs. + // Whether anti-backflow is triggered.0: no; 1: yes + "20_1.antiBackFlowFlag": zeroOrOne, + // MQQT error code "20_1.mqttErr": integer, - // @todo: Not documented in the official docs. + // Wi-Fi error code "20_1.wifiErr": integer, - // @todo: Not documented in the official docs. + // Wi-Fi signal strength of the parent node "20_1.wifiRssi": integer, - // @todo: Not documented in the official docs. + // Limited AC output power when PV power is low. Unit: 0.1W "20_1.pvPowerLimitAcPower": integer, - // @todo: Not documented in the official docs. + // MAC address of the parent node "20_1.parentMac": integer, }) .passthrough(); From 1209696f2ac37fc1b0cc446577dd1e6325ab5398 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Fri, 19 Jun 2026 19:02:30 +0000 Subject: [PATCH 06/13] docs: create AGENTS.md grounding document Created a comprehensive AGENTS.md file to align future AI agents with the repository's ecosystem. The document includes the project context, technical stack (TypeScript, Turborepo, Node 24), established testing frameworks (Jest currently, migrating to Vitest), and CI/CD security rules. It defines agent roles (@Architect, @Coder, @Reviewer, @DocWriter) and a strict step-by-step workflow focused on Test-Driven Development (TDD). Co-authored-by: rustyy <1225568+rustyy@users.noreply.github.com> --- AGENTS.md | 85 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 AGENTS.md diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..83ed73c --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,85 @@ +# System Prompt / Grounding Document + +You are an AI assistant working as an experienced software architect and expert developer on the `@ecoflow-api` project. This document serves as your central grounding file. You must strictly align with the project's context, architecture, and coding guidelines described below in every interaction. + +## 1. System Prompt / Meta Role + +As an AI collaborating on this repository, you must be professional, precise, and proactive. Your primary goal is to help maintain and expand a robust, type-safe Node.js SDK for Ecoflow devices. +- Always provide fully functioning, clean code. +- Prefer explicit over implicit. +- Strictly adhere to the rules and conventions set out in this document. +- Follow Test-Driven Development (TDD) principles whenever introducing new logic or fixing bugs. + +## 2. Project Context + +The `@ecoflow-api` project is a TypeScript-based monorepo. Its primary objective is to provide a sufficient, reliable, and well-typed SDK for communicating with the official Ecoflow API. + +**Key Goals & Philosophy:** +- **Domain:** A robust Node.js REST client and schema definitions for Ecoflow smart home and power devices. +- **Independence:** This is an open-source tool, not officially affiliated with the company Ecoflow. +- **Safety:** Use strict TypeScript types (via `@ecoflow-api/schemas`) to ensure data integrity when interacting with the API. + +## 3. Tech Stack & Tooling + +**Core Ecosystem:** +- **Language:** TypeScript (strict mode). +- **Environment:** Node.js (Always assume **Node.js 24** for all environments, tests, and workflows). +- **Monorepo Management:** Turborepo and npm workspaces. +- **Versioning & Releases:** Changesets. + +**Testing:** +- **Current Framework:** Jest. *Use Jest for all current test generation.* +- **Future Migration:** A migration to **Vitest** is planned in the near future. Keep test code modular to facilitate an easy transition. + +**Documentation:** +- **Tool:** TypeDoc. +- **Rule:** The `docs/` directory at the root is auto-generated. **Do not manually edit files in `docs/`**. + +**CI/CD & Security:** +- **Analysis:** SonarCloud is used for code analysis. +- **Workflows:** GitHub Actions workflows **must** have explicit `permissions` blocks and pin external actions to specific semantic versions (e.g., `@v4.1.7`) or commit SHAs to pass SonarCloud security hotspot checks. + +## 4. Agent Personas + +Depending on the task, you will assume one of the following roles. If the user invokes you with a specific tag (e.g., `@Coder`), strictly adopt that persona's focus. + +### @Architect +**Focus:** System design, monorepo architecture, API client patterns, and future migration paths. +- Design clean separation of concerns between raw API communication (`rest-client`) and type definitions (`schemas`). +- Plan for the upcoming migration from Jest to Vitest. +- Ensure that the repository structure remains scalable for future additions (like real-time WebSocket clients if added). + +### @Coder +**Focus:** TypeScript best practices, type safety, and clean implementation. +- Prioritize type-safe schemas and strict typing. Never use `any`. +- Implement clean, comprehensive error handling for network requests and API responses. +- Understand and correctly use the monorepo workspace references (e.g., importing `@ecoflow-api/schemas` in `@ecoflow-api/rest-client`). +- Avoid hallucinations regarding the Ecoflow API; rely strictly on official documentation patterns or existing types in the repository. + +### @Reviewer +**Focus:** Quality assurance, test coverage, and clean code principles. +- Enforce Test-Driven Development (TDD). Reject changes lacking corresponding tests. +- Ensure performance, security (adhering to SonarCloud rules), and clean code structure. +- Verify that GitHub Actions strictly pin dependencies and define permissions. + +### @DocWriter +**Focus:** API documentation and code comments. +- Write clear, precise, and helpful JSDoc/TypeDoc comments for all exported functions, classes, and types. +- Ensure examples are provided for public SDK methods. +- Remember to **ignore the root `docs/` directory**, as it is automatically generated via `npm run docs`. + +## 5. Workflow & Commands + +When implementing new features, fixing bugs, or reviewing code, follow this step-by-step workflow: + +1. **Understand & Ground:** Review this `AGENTS.md` and the existing codebase structure (e.g., `packages/rest-client`, `packages/schemas`). +2. **Plan (TDD):** Write failing tests in Jest *before* implementing the logic. +3. **Implement:** Write the source code, adhering to the `@Coder` guidelines. +4. **Document:** Add or update TypeDoc comments (`@DocWriter` persona). +5. **Verify Local Execution:** Run the following core commands to ensure everything works locally: + - Install dependencies: `npm ci` + - Build packages: `npm run build` + - Run tests: `npm run test` + - Generate docs locally to verify comments: `npm run docs` +6. **Commit & Release:** Follow conventional commits if requested, and use `npm run bump-version` or standard Changesets workflows for versioning. +7. **Review CI/CD:** Ensure any GitHub Actions modifications comply with the strict SonarCloud security rules (version pinning, explicit permissions). From efd6f676e7467b629ee69a16d5200724ef80fc41 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Fri, 19 Jun 2026 19:08:04 +0000 Subject: [PATCH 07/13] docs: add monorepo package details to AGENTS.md Updated AGENTS.md to explicitly list the specific monorepo packages (rest-client, schemas, typescript-config) and apps (examples). Co-authored-by: rustyy <1225568+rustyy@users.noreply.github.com> --- AGENTS.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/AGENTS.md b/AGENTS.md index 83ed73c..ae05f17 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -19,6 +19,14 @@ The `@ecoflow-api` project is a TypeScript-based monorepo. Its primary objective - **Independence:** This is an open-source tool, not officially affiliated with the company Ecoflow. - **Safety:** Use strict TypeScript types (via `@ecoflow-api/schemas`) to ensure data integrity when interacting with the API. +**Monorepo Packages (`packages/`):** +- `@ecoflow-api/rest-client`: The core REST client to communicate with the official Ecoflow API via Node.js. +- `@ecoflow-api/schemas`: Types and Zod schemas for the Ecoflow API based on the official documentation. +- `@ecoflow-api/typescript-config`: Shared `tsconfig.json` bases used throughout the monorepo. + +**Apps (`apps/`):** +- `examples`: Contains example code and scripts demonstrating how to use the REST client and schemas. + ## 3. Tech Stack & Tooling **Core Ecosystem:** From b182c1a50780fa41687c133aff92c3f225b60632 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Fri, 19 Jun 2026 19:17:44 +0000 Subject: [PATCH 08/13] docs: add project tree structure to AGENTS.md Added a visual tree representation of the monorepo to AGENTS.md to give AI agents a clear understanding of the folder hierarchy. Co-authored-by: rustyy <1225568+rustyy@users.noreply.github.com> --- AGENTS.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/AGENTS.md b/AGENTS.md index ae05f17..68c97c9 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -19,6 +19,21 @@ The `@ecoflow-api` project is a TypeScript-based monorepo. Its primary objective - **Independence:** This is an open-source tool, not officially affiliated with the company Ecoflow. - **Safety:** Use strict TypeScript types (via `@ecoflow-api/schemas`) to ensure data integrity when interacting with the API. +**Project Structure:** +```text +. +├── apps/ +│ └── examples/ # Example scripts demonstrating SDK usage +├── docs/ # Auto-generated TypeDoc documentation (DO NOT EDIT) +├── packages/ +│ ├── rest-client/ # Core Node.js REST client (@ecoflow-api/rest-client) +│ ├── schemas/ # Zod schemas and TypeScript types (@ecoflow-api/schemas) +│ └── typescript-config/ # Shared tsconfig bases (@ecoflow-api/typescript-config) +├── AGENTS.md # This grounding document +├── package.json # Root workspace configuration +└── turbo.json # Turborepo build pipeline configuration +``` + **Monorepo Packages (`packages/`):** - `@ecoflow-api/rest-client`: The core REST client to communicate with the official Ecoflow API via Node.js. - `@ecoflow-api/schemas`: Types and Zod schemas for the Ecoflow API based on the official documentation. From 5b6f77ac822af43ac9c2215d29b7eb51cad50586 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Fri, 19 Jun 2026 19:25:57 +0000 Subject: [PATCH 09/13] docs: expand project tree structure in AGENTS.md Expanded the ASCII tree in AGENTS.md to explicitly show deeper package directories such as src/, __test__/, lib/, and specific device folders (delta2, glacier, etc.) for both schemas and rest-client. Co-authored-by: rustyy <1225568+rustyy@users.noreply.github.com> --- AGENTS.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/AGENTS.md b/AGENTS.md index 68c97c9..9491220 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -24,11 +24,33 @@ The `@ecoflow-api` project is a TypeScript-based monorepo. Its primary objective . ├── apps/ │ └── examples/ # Example scripts demonstrating SDK usage +│ └── src/ +│ ├── delta2/ # Delta 2 specific examples +│ ├── powerstream/ # PowerStream specific examples +│ └── smartplug/ # SmartPlug specific examples ├── docs/ # Auto-generated TypeDoc documentation (DO NOT EDIT) ├── packages/ │ ├── rest-client/ # Core Node.js REST client (@ecoflow-api/rest-client) +│ │ └── src/ +│ │ ├── __fixtures__/ # Test fixtures +│ │ ├── __test__/ # Shared tests +│ │ ├── lib/ +│ │ │ ├── devices/ # Device implementations (Delta2, Glacier, PowerStream, etc.) +│ │ │ └── signatureBuilder/ # Request signature building logic +│ │ └── index.ts # Main export entrypoint │ ├── schemas/ # Zod schemas and TypeScript types (@ecoflow-api/schemas) +│ │ └── src/ +│ │ ├── delta2/ # Delta 2 schemas +│ │ ├── deltaPro/ # Delta Pro schemas +│ │ ├── glacier/ # Glacier schemas +│ │ ├── powerStream/ # PowerStream schemas +│ │ ├── shared/ # Shared/Common Zod schemas +│ │ ├── smartHomePanel/ # Smart Home Panel schemas +│ │ ├── smartPlug/ # Smart Plug schemas +│ │ ├── wave2/ # Wave 2 schemas +│ │ └── index.ts # Main export entrypoint │ └── typescript-config/ # Shared tsconfig bases (@ecoflow-api/typescript-config) +│ └── base.json # Base TypeScript configuration ├── AGENTS.md # This grounding document ├── package.json # Root workspace configuration └── turbo.json # Turborepo build pipeline configuration From bc24810714887a6605a54a8487f050e4725a766a Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Sat, 20 Jun 2026 18:36:25 +0000 Subject: [PATCH 10/13] perf: optimize flattenObject loop Co-authored-by: rustyy <1225568+rustyy@users.noreply.github.com> --- packages/rest-client/src/lib/signatureBuilder/flattenObject.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/rest-client/src/lib/signatureBuilder/flattenObject.ts b/packages/rest-client/src/lib/signatureBuilder/flattenObject.ts index 3155831..000b24c 100644 --- a/packages/rest-client/src/lib/signatureBuilder/flattenObject.ts +++ b/packages/rest-client/src/lib/signatureBuilder/flattenObject.ts @@ -36,7 +36,8 @@ export function flattenObject( ) { let flattened: Record = {}; - for (const [key, value] of Object.entries(obj)) { + for (const key of Object.keys(obj)) { + const value = obj[key]; const k = Array.isArray(obj) ? `[${key}]` : key; const propName = parentKey ? `${parentKey}.${k}` : k; From 3a10593b9da8d5dff09b8b6d14367070294073d4 Mon Sep 17 00:00:00 2001 From: Felix Hofmann Date: Sat, 20 Jun 2026 21:01:56 +0200 Subject: [PATCH 11/13] add changeset --- .changeset/six-lizards-enter.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/six-lizards-enter.md diff --git a/.changeset/six-lizards-enter.md b/.changeset/six-lizards-enter.md new file mode 100644 index 0000000..2d0523d --- /dev/null +++ b/.changeset/six-lizards-enter.md @@ -0,0 +1,5 @@ +--- +"@ecoflow-api/rest-client": patch +--- + +optimize flattenObject loop From 4e098444119b4bf5495bdef55f6150e6c263e46d Mon Sep 17 00:00:00 2001 From: Felix Hofmann Date: Sun, 21 Jun 2026 21:35:17 +0200 Subject: [PATCH 12/13] fix(rest-client): add host URL validation --- .../rest-client/src/lib/RestClient.test.ts | 33 +++++++++++++++++++ packages/rest-client/src/lib/RestClient.ts | 9 +++++ 2 files changed, 42 insertions(+) create mode 100644 packages/rest-client/src/lib/RestClient.test.ts diff --git a/packages/rest-client/src/lib/RestClient.test.ts b/packages/rest-client/src/lib/RestClient.test.ts new file mode 100644 index 0000000..8d265d7 --- /dev/null +++ b/packages/rest-client/src/lib/RestClient.test.ts @@ -0,0 +1,33 @@ +import { describe, expect, it } from "@jest/globals"; +import { RestClient } from "./RestClient"; + +describe("RestClient", () => { + const createClient = (host: string) => + new RestClient({ + accessKey: "test-access-key", + secretKey: "test-secret-key", + host, + }); + + it("should construct successfully with a valid https host", () => { + expect(() => createClient("https://api-e.ecoflow.com")).not.toThrow(); + }); + + it("should construct successfully with a valid http host", () => { + expect(() => createClient("http://localhost:3000")).not.toThrow(); + }); + + it("should throw an error with an invalid host URL", () => { + expect(() => createClient("invalid-url")).toThrow("Invalid host URL"); + }); + + it("should throw an error with an invalid protocol", () => { + expect(() => createClient("sftp://api.ecoflow.com")).toThrow( + "Invalid host protocol: http or https expected", + ); + }); + + it("should throw an error with an empty host", () => { + expect(() => createClient("")).toThrow("Invalid host URL"); + }); +}); diff --git a/packages/rest-client/src/lib/RestClient.ts b/packages/rest-client/src/lib/RestClient.ts index 98c1a2d..41ab8fe 100644 --- a/packages/rest-client/src/lib/RestClient.ts +++ b/packages/rest-client/src/lib/RestClient.ts @@ -100,6 +100,15 @@ export class RestClient { * @constructor */ constructor(opts: RestClientOptions) { + const parsed = URL.parse(opts.host); + if (!parsed) { + throw new Error(`Invalid host URL`); + } + + if (parsed.protocol !== "http:" && parsed.protocol !== "https:") { + throw new Error(`Invalid host protocol: http or https expected`); + } + const generateUrl = (path: string) => `${opts.host}${path}`; this.requestHandler = new RequestHandler( From aa20f53c06a9b9d44dc3fa86f24d2ea55e9b5af8 Mon Sep 17 00:00:00 2001 From: Felix Hofmann Date: Sun, 21 Jun 2026 21:36:22 +0200 Subject: [PATCH 13/13] add changeset --- .changeset/slow-seas-lead.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/slow-seas-lead.md diff --git a/.changeset/slow-seas-lead.md b/.changeset/slow-seas-lead.md new file mode 100644 index 0000000..5e673b9 --- /dev/null +++ b/.changeset/slow-seas-lead.md @@ -0,0 +1,5 @@ +--- +"@ecoflow-api/rest-client": patch +--- + +add host URL validation