From 4481d274012a59b55cff9a51f1ae3c89c4782126 Mon Sep 17 00:00:00 2001 From: yibo Date: Wed, 2 Sep 2026 01:41:39 +0800 Subject: [PATCH] Add JSON CRUD store for dressup dance room asset libraries (T1) --- dressup-store.js | 10 ++++++++++ store.js | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ store.test.js | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 113 insertions(+), 0 deletions(-) create mode 100644 dressup-store.js create mode 100644 store.js create mode 100644 store.test.js diff --git a/dressup-store.js b/dressup-store.js new file mode 100644 index 0000000..77418a0 --- /dev/null +++ b/dressup-store.js @@ -0,0 +1,10 @@ +// The three cold-start asset libraries for the dressup dance room +// (dev-plan.md T1; schemas: dressup-dance-room-mvcp.md M.3/M.4/M.5). +const path = require("path"); +const { createStore } = require("./store.js"); + +const characters = createStore(path.join(__dirname, "characters.json")); +const garments = createStore(path.join(__dirname, "garments.json")); +const danceRefs = createStore(path.join(__dirname, "dance-refs.json")); + +module.exports = { characters, garments, danceRefs }; diff --git a/store.js b/store.js new file mode 100644 index 0000000..38bbe31 --- /dev/null +++ b/store.js @@ -0,0 +1,51 @@ +// Generic JSON-file CRUD store (dev-plan.md T1), shared by characters.json/ +// garments.json/dance-refs.json. Same full-read/full-write pattern as +// server.js's library.json — no in-memory cache, so a process restart just +// re-reads the file. +const fs = require("fs"); +const crypto = require("crypto"); + +function createStore(filePath) { + function all() { + try { + return JSON.parse(fs.readFileSync(filePath, "utf8")); + } catch { + return []; + } + } + function save(items) { + fs.writeFileSync(filePath, JSON.stringify(items, null, 2)); + } + + return { + list() { + return all(); + }, + get(id) { + return all().find((item) => item.id === id) || null; + }, + create(data) { + const items = all(); + const item = { id: crypto.randomUUID(), createdAt: Date.now(), ...data }; + items.push(item); + save(items); + return item; + }, + update(id, patch) { + const items = all(); + const idx = items.findIndex((item) => item.id === id); + if (idx === -1) return null; + items[idx] = { ...items[idx], ...patch, id }; + save(items); + return items[idx]; + }, + remove(id) { + const items = all(); + const next = items.filter((item) => item.id !== id); + save(next); + return next.length !== items.length; + }, + }; +} + +module.exports = { createStore }; diff --git a/store.test.js b/store.test.js new file mode 100644 index 0000000..6c175f4 --- /dev/null +++ b/store.test.js @@ -0,0 +1,52 @@ +// RED/GREEN check for the JSON CRUD store shared by characters/garments/ +// dance-refs (dev-plan.md T1). Run: node --test store.test.js +const test = require("node:test"); +const assert = require("node:assert/strict"); +const fs = require("node:fs"); +const os = require("node:os"); +const path = require("node:path"); +const { createStore } = require("./store.js"); + +function tempFile() { + return path.join(os.tmpdir(), `store-test-${Date.now()}-${Math.random().toString(36).slice(2)}.json`); +} + +test("create/list/get/update/remove round-trip", () => { + const file = tempFile(); + const store = createStore(file); + + const created = store.create({ name: "test" }); + assert.equal(typeof created.id, "string"); + assert.equal(created.name, "test"); + assert.equal(typeof created.createdAt, "number"); + + assert.deepEqual(store.list(), [created]); + assert.deepEqual(store.get(created.id), created); + + const updated = store.update(created.id, { name: "updated" }); + assert.equal(updated.name, "updated"); + assert.equal(updated.id, created.id); + + assert.equal(store.remove(created.id), true); + assert.deepEqual(store.list(), []); + assert.equal(store.remove(created.id), false); + + fs.unlinkSync(file); +}); + +test("data survives a fresh store instance pointed at the same file (simulated process restart)", () => { + const file = tempFile(); + const store1 = createStore(file); + const created = store1.create({ name: "persisted" }); + + const store2 = createStore(file); // new instance, no shared in-memory state + assert.deepEqual(store2.get(created.id), created); + + fs.unlinkSync(file); +}); + +test("list() on a missing file returns an empty array, not a throw", () => { + const file = tempFile(); // never created + const store = createStore(file); + assert.deepEqual(store.list(), []); +}); -- libgit2 0.21.4