From b0112972eac175bc7b6ca4a660a6d9fd86824e35 Mon Sep 17 00:00:00 2001 From: karynad-stack Date: Tue, 9 Jun 2026 14:37:59 +0300 Subject: [PATCH 1/7] Create store.js --- lib/store.js | 55 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 lib/store.js diff --git a/lib/store.js b/lib/store.js new file mode 100644 index 0000000..df0d4cb --- /dev/null +++ b/lib/store.js @@ -0,0 +1,55 @@ +const fs = require("fs"); +const path = require("path"); + +const FILE = path.join(__dirname, "..", "notes.json"); + +function load() { + try { + return JSON.parse(fs.readFileSync(FILE, "utf8")); + } catch { + return { nextId: 1, notes: [] }; + } +} + +function save(data) { + fs.writeFileSync(FILE, JSON.stringify(data, null, 2)); +} + +function all() { + return load().notes; +} + +function add(text) { + const data = load(); + const note = { id: data.nextId, text }; + data.notes.push(note); + data.nextId += 1; + save(data); + return note; +} + +function remove(id) { + const data = load(); + const before = data.notes.length; + data.notes = data.notes.filter((n) => n.id !== id); + save(data); + return data.notes.length < before; +} + +// Returns the notes whose text contains `term`. +function matches(notes, term) { + return notes.filter((note) => note.text.includes(term)); +} + +function search(term) { + return matches(load().notes, term); +} + +function edit(id, text) { + const data = load(); + const note = data.notes.find((n) => n.id === id); + note.text = text; + save(data); +} + +module.exports = { all, add, remove, search, matches, edit }; From 4b9f26cc8a66fa66412e4f15f079ef22ae6d8c2b Mon Sep 17 00:00:00 2001 From: karynad-stack Date: Tue, 9 Jun 2026 14:41:30 +0300 Subject: [PATCH 2/7] Update notes.js --- notes.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/notes.js b/notes.js index df64a18..3b6ecf8 100644 --- a/notes.js +++ b/notes.js @@ -39,6 +39,13 @@ function main() { } break; } + case "edit": { + const id = Number(rest[0]); + const text = rest.slice(1).join(" ").trim(); + store.edit(id, text); + console.log(`Updated note #${id}`); + break; + } case "delete": { const id = Number(rest[0]); const ok = store.remove(id); @@ -46,7 +53,7 @@ function main() { break; } default: - console.log("Commands: add | list | search | delete "); + console.log("Commands: add | list | search | edit | delete "); console.log(`(Session locks after ${config.SESSION_TIMEOUT_MINUTES} minutes of inactivity.)`); } } From dd8a69790e392d65624ec75feb4c75b90b87e003 Mon Sep 17 00:00:00 2001 From: karynad-stack Date: Tue, 9 Jun 2026 14:48:11 +0300 Subject: [PATCH 3/7] Create .gitignore --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9e3b8ea --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +node_modules/ +notes.json From 678c1f3cd238e0ce84039d474d575afeb734236a Mon Sep 17 00:00:00 2001 From: karynad-stack Date: Tue, 9 Jun 2026 14:50:11 +0300 Subject: [PATCH 4/7] Create config.js --- lib/config.js | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 lib/config.js diff --git a/lib/config.js b/lib/config.js new file mode 100644 index 0000000..4405d19 --- /dev/null +++ b/lib/config.js @@ -0,0 +1,5 @@ +// App configuration. +module.exports = { + // The session locks after this many minutes of inactivity. + SESSION_TIMEOUT_MINUTES: 15, +}; From f91c2ab0bed0f801477a223c7d38c29938a504ab Mon Sep 17 00:00:00 2001 From: karynad-stack Date: Tue, 9 Jun 2026 14:51:56 +0300 Subject: [PATCH 5/7] Create notes.test.js --- tests/notes.test.js | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 tests/notes.test.js diff --git a/tests/notes.test.js b/tests/notes.test.js new file mode 100644 index 0000000..f0a31d8 --- /dev/null +++ b/tests/notes.test.js @@ -0,0 +1,26 @@ +const test = require("node:test"); +const assert = require("node:assert"); + +const { matches } = require("../lib/store"); + +const notes = [ + { id: 1, text: "buy milk" }, + { id: 2, text: "call the bank" }, + { id: 3, text: "milk the almonds" }, +]; + +test("search finds every note that contains the term", () => { + const result = matches(notes, "milk"); + assert.strictEqual(result.length, 2); +}); + +test("search finds a single containing note", () => { + const result = matches(notes, "bank"); + assert.strictEqual(result.length, 1); + assert.strictEqual(result[0].id, 2); +}); + +test("search returns nothing when no note contains the term", () => { + const result = matches(notes, "xyz"); + assert.strictEqual(result.length, 0); +}); From 4ca9b34042e6d1d75e1466a0b7bd2ae98cce8e76 Mon Sep 17 00:00:00 2001 From: karynad-stack Date: Tue, 9 Jun 2026 14:52:43 +0300 Subject: [PATCH 6/7] Create ci.yml --- .github/workflows/ci.yml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..c5abe06 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,15 @@ +name: CI + +# Runs on pull requests — it checks the change on the branch. +on: + pull_request: + +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v5 + - uses: actions/setup-node@v5 + with: + node-version: 22 + - run: npm test From 8a671614f8f0d6c49e5a5d3016efe8e748cd5659 Mon Sep 17 00:00:00 2001 From: Jeff Safier Date: Fri, 19 Jun 2026 18:46:32 -0400 Subject: [PATCH 7/7] Improve error handling and validation in store.edit() - Distinguish between ENOENT (no file) and other errors in load() - Add null check in edit() to return false if note not found - Update notes.js to handle edit() return value and show appropriate feedback Co-Authored-By: Claude Haiku 4.5 --- lib/store.js | 13 +++++++++++-- notes.js | 9 +++++++-- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/lib/store.js b/lib/store.js index df0d4cb..8ed2423 100644 --- a/lib/store.js +++ b/lib/store.js @@ -6,8 +6,13 @@ const FILE = path.join(__dirname, "..", "notes.json"); function load() { try { return JSON.parse(fs.readFileSync(FILE, "utf8")); - } catch { - return { nextId: 1, notes: [] }; + } catch (e) { + // Only treat ENOENT (file not found) as OK + if (e.code === 'ENOENT') { + return { nextId: 1, notes: [] }; + } + // Re-throw other errors (corruption, permissions, etc) + throw e; } } @@ -48,8 +53,12 @@ function search(term) { function edit(id, text) { const data = load(); const note = data.notes.find((n) => n.id === id); + if (!note){ + return false; + } note.text = text; save(data); + return true; } module.exports = { all, add, remove, search, matches, edit }; diff --git a/notes.js b/notes.js index 3b6ecf8..20a93f9 100644 --- a/notes.js +++ b/notes.js @@ -42,8 +42,13 @@ function main() { case "edit": { const id = Number(rest[0]); const text = rest.slice(1).join(" ").trim(); - store.edit(id, text); - console.log(`Updated note #${id}`); + const ok = store.edit(id, text); + if (ok){ + console.log(`Updated note #${id}`); + } + else{ + console.log("Note #${id} not found"); + } break; } case "delete": {