9.6 KiB
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
catArris an array of non-negative integer IDs.- New and legacy users default to
[1, 2, 3]when the field is absent. - Existing
catArrvalues are never overwritten during login. - The function must support both
usersandusersAd. - 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
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:
& "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:
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? }fromctx.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
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:
& "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:
name: setCatArr
desc: ""
methods:
- GET
- POST
tags: []
- Step 4: Implement the minimal cloud function
Create setCatArr.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
& "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
& "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.