# catArr Server Cloud Functions Implementation Plan > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. **Goal:** Add `catArr` initialization, reading, and manual saving to the Laf server cloud functions. **Architecture:** Keep `catArr` independent from `headArr`. Initialize it in `login`, and expose a dedicated `setCatArr` cloud function with `read` and `save` actions while preserving the existing database routing and token-validation patterns. **Tech Stack:** TypeScript, Laf cloud functions, Node.js built-in test runner. ## Global Constraints - `catArr` is an array of non-negative integer IDs. - New and legacy users default to `[1, 2, 3]` when the field is absent. - Existing `catArr` values are never overwritten during login. - The function must support both `users` and `usersAd`. - No cat-drawing or gacha behavior is included. - Do not create Git commits for this work. --- ### Task 1: Initialize catArr During Login **Files:** - Modify: `server/laf-cloud/functions/login.ts` - Create: `server/laf-cloud/tests/login-cat-arr.test.mjs` **Interfaces:** - Consumes: the existing new-user object passed to `db.collection(dbname).add(...)` - Produces: a persisted `catArr: number[]` field for newly created users - [ ] **Step 1: Write the failing source-level regression test** ```js import test from "node:test"; import assert from "node:assert/strict"; import { readFile } from "node:fs/promises"; test("login initializes new users with catArr [1, 2, 3]", async () => { const source = await readFile( new URL("../functions/login.ts", import.meta.url), "utf8" ); assert.match( source, /catArr:\s*user\?\.catArr\s*\|\|\s*\[1,\s*2,\s*3\]/ ); }); ``` - [ ] **Step 2: Run the test and verify RED** Run: ```powershell & "C:\Users\EDY\.cache\codex-runtimes\codex-primary-runtime\dependencies\node\bin\node.exe" --test server/laf-cloud/tests/login-cat-arr.test.mjs ``` Expected: FAIL because `login.ts` does not yet initialize `catArr`. - [ ] **Step 3: Add the minimal login field** Add beside `headArr` in the new-user object: ```ts catArr: user?.catArr || [1, 2, 3], ``` - [ ] **Step 4: Run the test and verify GREEN** Run the same Node test command. Expected: one passing test. ### Task 2: Add the Independent setCatArr Cloud Function **Files:** - Create: `server/laf-cloud/functions/setCatArr.ts` - Create: `server/laf-cloud/functions/setCatArr.yaml` - Create: `server/laf-cloud/tests/set-cat-arr.test.mjs` **Interfaces:** - Consumes: `{ action, uid, token, gameName?, catNum? }` from `ctx.body` - Produces for `read`: `{ code: 1, data: { catArr: number[] }, msg: "成功" }` - Produces for `save`: the same shape after persisting a unique non-negative integer ID - [ ] **Step 1: Write failing behavior tests with a Laf database stub** ```js import test from "node:test"; import assert from "node:assert/strict"; import { registerHooks } from "node:module"; import { readFile } from "node:fs/promises"; const state = { user: null, collections: [], updates: [], }; globalThis.__catArrCloudMock = { database() { return { collection(name) { state.collections.push(name); return { where() { return { async getOne() { return { data: state.user }; }, async update(payload) { state.updates.push(payload); return {}; }, }; }, }; }, }; }, }; globalThis.__catArrUtilsMock = { checkToken(received, expected) { return received === expected; }, }; registerHooks({ resolve(specifier, context, nextResolve) { if (specifier === "@lafjs/cloud") { return { url: "data:text/javascript,export default globalThis.__catArrCloudMock", shortCircuit: true, }; } if (specifier === "@/Utils") { return { url: "data:text/javascript,export default globalThis.__catArrUtilsMock", shortCircuit: true, }; } return nextResolve(specifier, context); }, }); const { default: setCatArr } = await import( new URL("../functions/setCatArr.ts", import.meta.url) ); function reset(user) { state.user = user; state.collections.length = 0; state.updates.length = 0; } test("configuration exposes the setCatArr cloud function", async () => { const yaml = await readFile( new URL("../functions/setCatArr.yaml", import.meta.url), "utf8" ); assert.match(yaml, /^name:\s*setCatArr$/m); }); test("read returns defaults for a legacy user", async () => { reset({ _id: "u1", token: "secret" }); const result = await setCatArr({ body: { action: "read", uid: "u1", token: "secret" }, }); assert.deepEqual(result.data.catArr, [1, 2, 3]); }); test("save appends and persists a new cat ID", async () => { reset({ _id: "u1", token: "secret" }); const result = await setCatArr({ body: { action: "save", uid: "u1", token: "secret", catNum: 4 }, }); assert.deepEqual(result.data.catArr, [1, 2, 3, 4]); assert.deepEqual(state.updates, [{ catArr: [1, 2, 3, 4] }]); }); test("save rejects duplicate and invalid cat IDs", async () => { reset({ _id: "u1", token: "secret", catArr: [1, 2, 3] }); const duplicate = await setCatArr({ body: { action: "save", uid: "u1", token: "secret", catNum: 2 }, }); const invalid = await setCatArr({ body: { action: "save", uid: "u1", token: "secret", catNum: -1 }, }); assert.equal(duplicate.code, 0); assert.equal(invalid.code, 0); assert.deepEqual(state.updates, []); }); test("gameName iaa routes to usersAd", async () => { reset({ _id: "u1", token: "secret", catArr: [1, 2, 3] }); await setCatArr({ body: { action: "read", uid: "u1", token: "secret", gameName: "iaa", }, }); assert.equal(state.collections[0], "usersAd"); }); test("missing user returns an error", async () => { reset(null); const result = await setCatArr({ body: { action: "read", uid: "missing", token: "secret" }, }); assert.equal(result.code, 0); }); ``` - [ ] **Step 2: Run the tests and verify RED** Run: ```powershell & "C:\Users\EDY\.cache\codex-runtimes\codex-primary-runtime\dependencies\node\bin\node.exe" --disable-warning=MODULE_TYPELESS_PACKAGE_JSON --test server/laf-cloud/tests/set-cat-arr.test.mjs ``` Expected: FAIL because `setCatArr.ts` and `setCatArr.yaml` do not exist. - [ ] **Step 3: Add the cloud-function configuration** Create `setCatArr.yaml`: ```yaml name: setCatArr desc: "" methods: - GET - POST tags: [] ``` - [ ] **Step 4: Implement the minimal cloud function** Create `setCatArr.ts`: ```ts import cloud from '@lafjs/cloud' import Utils from "@/Utils"; const db = cloud.database(); const DEFAULT_CAT_ARR = [1, 2, 3]; export default async function (ctx: FunctionContext) { const action = ctx.body.action; const uid = ctx.body.uid; const dbname = ctx.body.gameName == "iaa" ? "usersAd" : "users"; if (!action) { return { code: 0, data: null, msg: "未获取到action" }; } if (!uid) { return { code: 0, data: null, msg: "未获取到uid" }; } const res = await db.collection(dbname).where({ _id: uid }).getOne(); if (!res.data) { return { code: 0, data: null, msg: "未获取到玩家信息" }; } if (res.data.token && !Utils.checkToken(ctx.body.token, res.data.token)) { return { code: 0, data: null, msg: "token校验失败" }; } const catArr = Array.isArray(res.data.catArr) ? [...res.data.catArr] : [...DEFAULT_CAT_ARR]; switch (action) { case "save": { const catNum = Number(ctx.body.catNum); if (!Number.isInteger(catNum) || catNum < 0) { return { code: 0, data: null, msg: "猫咪id错误" }; } if (catArr.includes(catNum)) { return { code: 0, data: null, msg: "已保存猫咪" }; } catArr.push(catNum); await db.collection(dbname).where({ _id: uid }).update({ catArr }); return { code: 1, data: { catArr }, msg: "用户数据更新成功", }; } case "read": return { code: 1, data: { catArr }, msg: "成功" }; default: return { code: 400, message: "无效的操作类型,请使用 save 或 read", }; } } ``` - [ ] **Step 5: Run the behavior tests and verify GREEN** Run the same Node test command. Expected: six passing tests. ### Task 3: Run Combined Verification **Files:** - Verify: `server/laf-cloud/functions/login.ts` - Verify: `server/laf-cloud/functions/setCatArr.ts` - Verify: `server/laf-cloud/functions/setCatArr.yaml` - Verify: `server/laf-cloud/tests/login-cat-arr.test.mjs` - Verify: `server/laf-cloud/tests/set-cat-arr.test.mjs` **Interfaces:** - Consumes: completed Tasks 1 and 2 - Produces: verified server behavior for initialization, reading, and saving - [ ] **Step 1: Run all catArr tests** ```powershell & "C:\Users\EDY\.cache\codex-runtimes\codex-primary-runtime\dependencies\node\bin\node.exe" --disable-warning=MODULE_TYPELESS_PACKAGE_JSON --test server/laf-cloud/tests/*cat-arr.test.mjs ``` Expected: seven passing tests and zero failures. - [ ] **Step 2: Inspect only the scoped changes** ```powershell & "C:\Users\EDY\.cache\codex-runtimes\codex-primary-runtime\dependencies\native\git\cmd\git.exe" diff -- server/laf-cloud/functions/login.ts server/laf-cloud/functions/setCatArr.ts server/laf-cloud/functions/setCatArr.yaml server/laf-cloud/tests/login-cat-arr.test.mjs server/laf-cloud/tests/set-cat-arr.test.mjs ``` Expected: only `catArr` initialization, the independent cloud function/configuration, and their tests.