npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

auth-record-kit

v1.5.18

Published

微信小程序可复用登录 + 记录组件包:手机号/微信一键授权登录、登录态维护与刷新、通用数据记录 SDK 与 UI 组件。基于微信云开发,零自建服务器即可复用。

Readme

auth-record-kit

微信小程序可复用「登录 + 记录」组件包。任意小程序几行代码即可获得:

  • 📱 手机号 + 微信一键授权登录:chooseAvatar + 昵称输入获取基本信息,getPhoneNumber 解密手机号;身份用 openid 标识,无需自建账号体系。
  • 🔐 登录态维护与过期刷新:HMAC 签名 token(默认 7 天),客户端自动检测临近过期并静默刷新;过期后引导重新登录。
  • 📝 通用数据记录:任意字段结构的记录增删查,自动按用户隔离(openid)。
  • ♻️ 高复用性:登录 SDK + 记录 SDK + 两个开箱即用 UI 组件(auth-login / record-panel),基于微信云开发,无需自建服务器。

一、目录结构

auth-record-kit/
├── package.json              # npm 元数据(miniprogram 字段指向 src)
├── README.md
├── src/                      # 组件源码(前端)
│   ├── index.js              # 统一入口:init / Auth / Record
│   ├── core/client.js        # 统一客户端(配置、登录态、自动刷新、请求分发)
│   ├── auth/
│   │   ├── auth.js           # 登录 SDK
│   │   └── auth-login/       # 登录 UI 组件(弹层:头像+昵称+手机号)
│   └── record/
│       ├── record.js         # 记录 SDK
│       └── record-panel/     # 通用记录 CRUD UI 组件
└── cloud/
    └── arkit/                # 云函数(后端):登录/刷新/记录 CRUD
        ├── index.js
        └── package.json

二、后端部署(云函数 arkit)

  1. 在微信开发者工具中,把 cloud/arkit 上传并部署到你的云开发环境。
  2. 在云函数「配置 → 环境变量」中填写: | 变量 | 说明 | | --- | --- | | APP_ID | 小程序 AppId | | APP_SECRET | 小程序 AppSecret(解密手机号用) | | ARKIT_SECRET | 任意长随机串,作为 token 签名密钥(务必修改) |
  3. 在云开发控制台新建两个集合(权限均设为「仅创建者可读写」):
    • arkit_users:用户表(openid / nickName / avatarUrl / phone / createdAt)
    • arkit_records:默认记录表(openid / 业务字段 / createdAt)
    • 👉 不同业务用不同集合名即可复用同一套组件,互不干扰。

已用 wx-server-sdk 的 cloud.getWXContext().OPENID 直接拿到用户身份,不需要自己做 code2Session。


三、前端集成(两种方式任选)

方式 A:复制源码(最简单,无 npm 构建)

把整个 src/ 目录复制到你的小程序项目(例如 components/arkit/),在页面/全局 usingComponents 引用组件,在 app.js 里 require SDK。

方式 B:npm 包(推荐,已上架准备就绪)

  1. 小程序项目根目录执行 npm install auth-record-kit
  2. 微信开发者工具 → 工具 → 构建 npm(本包 package.json 已设 miniprogram: "src",构建后组件位于 miniprogram_npm/auth-record-kit/)
  3. 全局 app.js 引入 SDK:const arkit = require('auth-record-kit');
  4. 页面 usingComponents 引用组件(路径不含 /src,构建后 src 已被扁平到包根):
    {
      "usingComponents": {
        "auth-login": "auth-record-kit/auth/auth-login/index",
        "record-panel": "auth-record-kit/record/record-panel/index"
      }
    }

💡 两种方式对比:方式 A(复制源码)适合不发布、直接嵌入;方式 B(npm)适合作为可复用组件包上架,版本可迭代、消费者 npm update 即升级。

直接看可运行示例(npm 方式)

仓库内 examples/host/ 是一个最小宿主小程序,已改用 npm 引用。运行方式:

  1. 在 examples/host/ 内 npm install auth-record-kit 并执行「构建 npm」
  2. 用微信开发者工具打开 examples/host/ 编译预览 含登录弹层 + 记录面板完整接线,以及 query / order-by / fields / count / getById / batchDelete / batchAdd 交互演示(详见该目录 README)。

📦 发布上架清单(npm)

把本仓库作为小程序组件包上架前,逐项确认:

  • [ ] package.json 的 miniprogram 字段指向组件源码目录(已设 "src"),files 仅含 src/cloud/examples/README.md(测试不进包)
  • [ ] 组件 usingComponents 路径对外统一用包名(auth-record-kit/...,不含 /src),已在本 README「方式 B」与 examples/host 对齐
  • [ ] src/ 内仅依赖 wx.* 全局与相对 require,无 crypto/process/fs/http/Buffer 等 node 专属 API(已审计通过)
  • [ ] 云函数 cloud/arkit/ 单独部署:复制目录到云函数 arkit → npm install(含 wx-server-sdk)→ 上传;配置环境变量 APP_ID/APP_SECRET/ARKIT_SECRET
  • [ ] 云数据库新建集合 arkit_users、arkit_records(权限「仅创建者可读写」);业务集合自定义命名即可复用
  • [ ] ARKIT_SECRET 在云函数环境变量中改为自有长随机串(切勿保留默认值)
  • [ ] 发布前在真实小程序里跑一遍:npm install → 构建 npm → 真机/模拟器登录 + 增删查改全链路
  • [ ] 版本号已 bump(npm version 或手改 package.json + src/index.js 的 version),并写入下方更新日志

四、初始化

在 app.js 的 onLaunch 中初始化(只需一次):

// app.js
var arkit = require('./components/arkit/src/index.js'); // 方式 A 路径
// 若用 npm:var arkit = require('auth-record-kit');

App({
  onLaunch() {
    wx.cloud.init({ env: 'your-cloud-env-id', traceUser: true }); // 先初始化云开发
    arkit.init({ mode: 'cloudbase', env: 'your-cloud-env-id' });
  },
});

arkit.init 支持:

| 参数 | 默认 | 说明 | | --- | --- | --- | | mode | 'cloudbase' | 'cloudbase'(云函数)或 'http'(自建后端) | | env | '' | 云开发环境 ID | | cloudFn | 'arkit' | 云函数名 | | httpBase | '' | 自建后端基址(mode='http' 时必填) | | autoRefresh | true | 启动后若 token 临近过期则静默刷新 | | refreshAhead | 3600 | 距过期少于该秒数触发刷新 | | timeout | 10000 | HTTP 请求超时(毫秒),仅 mode='http' 时生效 |


五、登录(UI 组件方式)

页面 JSON:

{
  "usingComponents": {
    "auth-login": "/components/arkit/src/auth/auth-login/index"
  }
}

页面 WXML:

<auth-login visible="{{showLogin}}" bind:success="onLogin" bind:cancel="onCancel" />

页面 JS:

Page({
  data: { showLogin: false },
  onShow() {
    const arkit = require('../../components/arkit/src/index.js');
    if (!arkit.Auth.isLogin()) this.setData({ showLogin: true });
  },
  onLogin(e) {
    console.log('登录成功', e.detail.userInfo);
    this.setData({ showLogin: false });
  },
  onCancel() { this.setData({ showLogin: false }); },
});

组件内部已调用 arkit.Auth.login(userInfo, { code }) 并写入登录态;success 事件带回 { token, userInfo }。(旧版基础库返回 cloudID 时传 { cloudID } 同样兼容。)

纯 SDK 方式(自定义登录界面):

const { Auth } = require('../../components/arkit/src/index.js');
// phone 参数支持 { code }(新版 getPhoneNumber)或 { cloudID }(旧版兼容)
await Auth.login({ nickName, avatarUrl }, { code: phoneCode }); // phoneCode 来自 getPhoneNumber 事件

六、数据记录(组件方式)

页面 JSON:

{
  "usingComponents": {
    "record-panel": "/components/arkit/src/record/record-panel/index"
  }
}

页面 WXML:

<record-panel
  collection="my_notes"
  title="我的笔记"
  form-fields="{{formFields}}"
  list-fields="{{listFields}}"
  bind:change="onChanged"
/>

页面 JS:

Page({
  data: {
    formFields: [
      { key: 'title', label: '标题', type: 'text', placeholder: '一句话' },
      { key: 'content', label: '内容', type: 'textarea', placeholder: '详细记录…' },
    ],
    listFields: [
      { key: 'title', label: '标题' },
      { key: 'content', label: '内容' },
    ],
  },
});

组件会自动:登录后拉取列表、弹层新增、删除确认,全部按当前用户隔离。

record-panel 支持的属性:

| 属性 | 类型 | 默认 | 说明 | | --- | --- | --- | --- | | collection | String | arkit_records | 记录集合名(不同业务用不同集合即可复用) | | title | String | 我的记录 | 面板标题 | | form-fields | Array | [] | 表单字段 [{ key, label, type:'text'\|'textarea'\|'number', placeholder }] | | list-fields | Array | [] | 列表展示字段 [{ key, label }] | | limit | Number | 20 | 每页条数(服务端最多 100) | | can-add | Boolean | true | 是否显示「+ 记一笔」 | | can-edit | Boolean | true | 是否显示「编辑」(点卡片编辑按钮,复用表单走 updateRecord) | | can-delete | Boolean | true | 是否显示删除按钮 | | auto-load | Boolean | true | 滚动到底部自动加载更多(IntersectionObserver);false 时显示手动「加载更多」按钮 | | query | Object | {} | 服务端过滤条件,透传给 listRecords 的 query;运行时变更会自动重新加载(首屏不重复加载) | | empty-text | String | 还没有记录,点「+ 记一笔」开始 | 列表为空时的提示文案,可自定义 | | order-by | Object/Array | null | 排序 { field, dir }(dir 为 asc/desc),过服务端 safeOrderBy 白名单;缺省按 createdAt 倒序。支持复合排序:传数组 [{field,dir}, ...](最多 3 个字段,顺序即优先级,如先按 status 升序再按 createdAt 倒序)。字段名/方向非法时服务端拒绝并回退默认排序 | | fields | Array | null | 字段投影:如 ['title','status'] 让服务端只返回指定字段(省流量),过 safeFields 白名单(字段名规则同排序、最多 10 个、自动去重、始终保留 _id);非法或 null 时返回全部字段 |

外部方法:宿主可通过 selectComponent 拿到面板实例后调用 reload() 手动刷新列表(如外部数据变更后),getById(id) 按 _id 拉取单条记录,batchDelete(ids) 批量删除多条记录(返回实际删除条数),count(query) 按条件计数(不 fetch 数据,省流量),groupCount(field, query) 按字段分组计数(看板数据,返回 [{_id,total}],不 fetch 明细)。query / order-by / fields 任一属性在运行时变化都会触发面板自动重新加载,无需手动调用 reload()。

分页:auto-load 默认开启——用户滚动到列表底部时自动拉取下一页并追加(IntersectionObserver 监听底部哨兵元素);设为 false 则回退为手动点击「加载更多」按钮。全部加载完毕后底部显示「没有更多了」。

安全提示:业务集合名仅允许字母/数字/下划线/连字符,且禁止复用系统集合 arkit_users(已在云函数 safeCollection 中拦截),防止越权读写用户表。

纯 SDK 方式:

const { Record } = require('../../components/arkit/src/index.js');

// 新增
await Record.addRecord('my_notes', { title: 'A', content: 'B' });

// 查询(分页)
const { list, total } = await Record.listRecords('my_notes', { skip: 0, limit: 20 });

// 只取需要的字段(省流量,服务端过 safeFields 白名单)
const { list: titles } = await Record.listRecords('my_notes', { fields: ['title', 'status'], limit: 20 });

// 更新 / 删除 / 批量删除
await Record.updateRecord('my_notes', id, { content: '新的' });
await Record.deleteRecord('my_notes', id);
const removed = await Record.batchDeleteRecords('my_notes', [id1, id2]); // 一次删多条,返回删除条数

// 单条查询(按 _id 拉取自己的记录,带归属隔离)
const rec = await Record.getRecord('my_notes', id);        // 命中返回记录对象,不存在返回 null
const rec2 = await Record.getRecord('my_notes', id, { query: { status: 'done' } }); // 可选字段过滤

// 计数专用(不 fetch 记录数据,省流量;角标/进度条场景)
const total = await Record.countRecords('my_notes');                            // 全部记录数
const drafts = await Record.countRecords('my_notes', { query: { status: 'draft' } }); // 带条件计数

// 分组聚合计数(看板场景:各状态各多少条,不 fetch 任何明细)
// 返回 [{_id:'draft',total:3},{_id:'done',total:5}],groupBy 过 safeGroupField 白名单防注入
const byStatus = await Record.groupCountRecords('my_notes', { groupBy: 'status' });
const doneByStatus = await Record.groupCountRecords('my_notes', { query: { status: 'done' }, groupBy: 'status' });

// 批量新增(一次插多条,减少云函数调用次数;每项 ≤50 字段、禁嵌套、最多 20 条,自动带 openid)
const n = await Record.batchAddRecords('my_notes', [{ title: 'A' }, { title: 'B' }]); // 返回插入条数

record-panel 额外提供 getById(id) 方法:宿主通过 selectComponent 拿到实例后调用即可按 _id 拉取单条记录(同样带归属隔离,查无此条返回 null,失败弹 toast)。

record-panel 额外提供 batchDelete(ids) 方法:宿主通过 selectComponent 拿到实例后调用即可批量删除自己的多条记录(同样带 openid 归属隔离,仅删属于自己的记录,返回实际删除条数,失败弹 toast)。

record-panel 额外提供 batchAdd(items) 方法:宿主通过 selectComponent 拿到实例后调用即可批量新增自己的多条记录(同样带 openid 归属隔离,返回实际插入条数,失败弹 toast)。

record-panel 额外提供 count(query) 方法:宿主通过 selectComponent 拿到实例后调用即可按条件计数(不 fetch 数据,省流量;不传 query 时默认用面板的 query 属性,返回数字,失败弹 toast)。

record-panel 额外提供 groupCount(field, query) 方法:宿主通过 selectComponent 拿到实例后调用即可按字段分组聚合计数(看板场景,如「各状态各多少条」),返回 [{_id,total}],不 fetch 任何明细;第 1 个参数 field 为分组字段名(过服务端 safeGroupField 白名单防注入),第 2 个 query 可选(不传时默认用面板的 query 属性),失败弹 toast 返回 []。

record-panel 的「编辑」按钮会以现有记录预填表单,submit 时自动走 updateRecord(而非新增),无需删除重建。


---

## 七、登录态与刷新机制

- 登录成功后,`{ token, exp }` 存入 `storageKey`(默认 `arkit_session`),`userInfo` 存入 `arkit_userinfo`。
- 每次 SDK 请求前若检测 token 临近过期(默认提前 1 小时),自动调用 `refresh` 换发新 token;刷新失败则清空登录态,由上层引导重新登录。
- 宿主可随时 `arkit.Auth.isLogin()` / `arkit.Auth.getUser()` / `arkit.Auth.logout()` 读取或清除状态。
- 在 `init` 时传入 `onUnauthorized(res)`:当任意业务请求返回 `401`(token 失效/被服务端判定未登录)时触发,同时**自动清空本地登录态**(避免「本地显示已登录、实际请求全 401」的假登录死循环)。典型用法是跳转登录页:

```js
arkit.init({
  mode: 'cloudbase',
  env: 'your-cloud-env-id',
  onUnauthorized(res) {
    wx.showToast({ title: '登录已失效', icon: 'none' });
    wx.navigateTo({ url: '/pages/login/login' }); // 引导重新登录
  },
});
  • 刷新互斥:并发的 ensureLogin() / 记录请求会复用同一次刷新,避免重复签发 token。
  • 401 一次性自动重试:业务请求(非登录/刷新)若返回 401(token 刚好在服务端失效、时钟偏差等瞬时情况),SDK 会静默刷新一次并重试原请求;若刷新失败或重试仍 401,再清空本地态并触发 onUnauthorized。用户操作不会因 token 临界失效直接报错。

八、接入自建后端(mode='http')

如果你的小程序不打算用云开发,可把 arkit 云函数的逻辑原样搬到任意服务端,暴露 POST {httpBase}/arkit,请求体为 { action, token, ...data },返回 { code, ... }(code:0 成功,401 未登录)。前端只需:

arkit.init({ mode: 'http', httpBase: 'https://api.yoursite.com' });

其余 Auth / Record 调用方式完全一致——这就是「统一接口」带来的可移植性。


九、合规与安全提示

  • 手机号解密依赖 APP_SECRET,请仅存于云函数环境变量,不要下发到前端。
  • ARKIT_SECRET 必须改成只有你知道的随机串,否则他人可伪造 token。
  • 集合权限设为「仅创建者可读写」,且云函数已按 openid 强制隔离,避免越权读写他人数据。
  • 客户端无法伪造归属:云函数对写入/查询/更新的业务数据统一 stripOpenid——丢弃客户端上传的 openid/_openid/_id,一律以服务端从 token 解析的 openid 为准。即使客户端在 data/query/patch 里塞入他人 openid,也会被剥离,确保用户只能读写自己的数据。
  • 用户资料收敛:doLogin 走 sanitizeProfile——nickName 去空格截到 32 字、avatarUrl 仅放行 http(s) 且 ≤512 字符,避免脏数据/异常协议头像入库。
  • 排序字段防注入:列表排序的 orderBy 必须过 safeOrderBy 白名单——字段名仅允许「字母/下划线开头 + 字母数字下划线」、方向仅 asc/desc,拒绝 $ 操作符字段、带点路径与非法方向,防 NoSQL 注入;非法或缺失时回退默认 createdAt 倒序。支持复合排序(数组形式,最多 3 个字段,顺序即优先级),数组内任一元素非法则整体拒绝。
  • 字段投影白名单:recordList 的 fields 须过 safeFields 白名单——元素为合法字段名(同排序规则)、最多 10 个、自动去重;非法/超长/非数组则整体忽略(返回全部字段)。投影始终保留 _id,避免断更。防通过投影注入异常字段名。
  • 分组字段防注入:recordGroupCount 的 groupBy 须过 safeGroupField 白名单——仅允许单个合法字段名(同 safeFields 规则),拒绝 $ 操作符、带点路径与空串,杜绝 NoSQL 注入;非法整体拒绝。聚合以 '$'+field 为 _id 分组、_.aggregate.sum(1) 计数,仅返回分组结果,不传输任何记录明细。
  • 写入载荷护栏:doRecordAdd / doRecordUpdate 的业务数据均走 safeRecord——限制字段数(≤50)、拒绝嵌套对象/数组(保持扁平)、超长字符串截断到 2000 字,防止记录被塞爆或写入非法类型。
  • 用户昵称/头像通过 <input type="nickname"> + chooseAvatar 获取,符合微信 2022 后「不强制 getUserProfile」的规范。

十、测试

组件内置纯 Node 测试(无需微信开发者工具),基于 mock wx 全局,覆盖 token 签名校验、客户端登录态/刷新/onUnauthorized、记录 SDK 通路、集合安全校验、401 自动重试,共 116 项断言:

npm test
# 或等价
node test/run.js

每个测试文件在独立子进程中运行,避免全局 wx 互相污染;新增断言只需在对应 *.test.js 里 ok(cond, '描述'),并到 test/run.js 注册文件名即可。


十一、更新日志

v1.5.18

  • 分组聚合计数 recordGroupCount:看板类场景(如「各状态各多少条」「各分类各多少篇」)此前只能先把所有记录拉到前端再本地 reduce,既浪费流量又泄露明细。本轮新增分组聚合计数,三端打通并只返回分组结果、不 fetch 任何明细:
    • guard.js 新增 safeGroupField(groupBy):仅允许单个合法字段名(同 safeFields 规则),拒绝 $ 操作符、带点路径、空串/数字/null,杜绝 NoSQL 注入。
    • 云函数新增 doRecordGroupCount:safeCollection 校验 + safeGroupField 校验 groupBy + stripOpenid 剥离保留字段 + where({ openid, ...query }) 归属隔离;aggregate().match().group({ _id: '$'+field, total: _.aggregate.sum(1) }).end() 聚合,返回 { code:0, groups:[{_id,total}] },不传输任何记录数据。
    • SDK Record.groupCountRecords(collection, { query, groupBy }) 透传并返回分组数组,未登录拦截。
    • record-panel 新增 groupCount(field, query) 便捷方法(第 1 参分组字段名、第 2 参可选 query;不传 query 默认用面板 query 属性,失败弹 toast 返回 [])。
  • 测试增强:guard.test.js 新增 7 条 safeGroupField 断言(合法放行/$where 拒绝/带点路径拒绝/空串拒绝/数字拒绝/null 拒绝);record.test.js 新增 3 条 groupCountRecords 断言(未登录拦截、返回分组数组、groupBy 透传)。总断言数 106 → 116。

v1.5.17(上架准备)

  • 发布审计:确认 src/ 仅依赖 wx.* 与相对 require,无 crypto/process/fs/http/Buffer 等 node 专属 API;npm pack --dry-run 包体 27 文件 / 33.6 kB,测试未入包。
  • 修复 npm 接入文档 bug:usingComponents 路径由错误的 auth-record-kit/src/... 改为正确的 auth-record-kit/...(构建 npm 后 src 已被扁平到包根)。
  • 示例宿主 examples/host 改为 npm 风格:require('auth-record-kit') + 包名 usingComponents,与发布后真实用法对齐;其 README 增加「构建 npm」运行步骤。
  • 新增「📦 发布上架清单(npm)」:含 miniprogram 字段、files、云函数部署、ARKIT_SECRET 变更、真机验证等 8 项发布前核对项。

v1.5.16

  • 批量新增 recordBatchAdd:对标已有的批量删除,一次插入多条(减少云函数调用次数)。三端打通:
    • 新增 safeRecordList(items) 护栏(guard.js):非空对象数组、每项过 safeRecord(≤50 字段/禁嵌套/超长截断)、最多 20 条;任一子项非法整体拒绝。
    • 云函数 doRecordBatchAdd:safeCollection 校验 + safeRecordList 校验 + 每条强制写入服务端 openid 与 serverDate(归属不可伪造);db.collection(col).add({ data: docs }) 批量插入,返回 { code:0, inserted }。
    • SDK Record.batchAddRecords(collection, items) 透传并返回插入条数,未登录拦截。
    • record-panel 新增 batchAdd(items) 便捷方法。
  • 测试增强:guard.test.js 新增 safeRecordList 5 条断言(放行/空数组/非数组/嵌套对象/超 20 条/嵌套数组);record.test.js 新增 2 条断言(登录后返回插入条数、items 透传)。总断言数 98 → 106。

v1.5.15

  • Bug 修复(全量审计):本轮对全部核心文件做了逐行 bug 审计,修复 5 个真实 bug:
    1. [严重] 编辑数据丢失:record-panel 的 edit() 原从已启用 fields 投影的列表里取记录预填表单,未投影字段会被写成 '' 覆盖真实数据。改为编辑前用 getRecord 拉完整记录(不经过投影)再预填,提交只更新 formFields,其余字段保留。
    2. [中] stripOpenid 非对象泄漏:传入字符串/数字等非对象时原样返回,导致 Object.assign({openid}, "abc") 把字符串拆成 {'0':'a',...} 数字键污染查询条件。现一律返回 {}。
    3. [低] doRecordGet 操作符查询误判 404:query 含 {字段:{$ne:...}} 等对象值时会恒返回 404。现跳过对象/数组值键,只做等式过滤。
    4. [中] 分页失败自动重试死循环:autoLoad 下分页失败后哨兵常驻视口,IntersectionObserver 会反复触发 loadMore。现自动加载时若已 loadMoreError 则跳过(手动点"加载失败,点击重试"仍可用);load() 成功时重置 loadMoreError。
    5. [低] recordUpdate/recordDelete/recordGet 缺 id 校验:event.id 缺失会让云数据库 doc(undefined) 抛错返回 500,现前置返回 400。
  • 测试增强:guard.test.js 修订 stripOpenid 断言(纠正旧的错误断言并新增 3 条非对象回归用例)。总断言数 96 → 98。

v1.5.14

  • record-panel 运行时属性自动重载扩展:原仅 query 变更触发自动 reload,本轮将 order-by / fields 一并纳入 observers,三者任一在运行时变化都会触发面板自动重新加载(与 query 行为一致)。便于宿主做"排序下拉 / 投影切换"类交互时无需手动调 reload()。
  • examples/host 升级为可交互演示:把前几轮补齐的能力真正用起来——状态筛选(驱动 query)、排序字段+方向(驱动 order-by)、字段投影切换(驱动 fields)、计数按钮(count 显示总/筛选条数)、getById 单条查询、批量删除(逗号分隔 _id,走 batchDelete)。页面加 id="panel" 以便 selectComponent 调用外部方法;新增 status 表单/列表字段支撑筛选演示。index.js/index.wxml/index.wxss 全部重写,纯 JS 经 node --check 验证。
  • 说明:本轮为组件行为增强 + 示例升级,未新增断言(observer 属小程序运行时行为,无法在 Node 单测覆盖);测试套件仍 96 项全绿。

v1.5.13

  • 计数专用 recordCount:此前 listRecords 即便只要总数也会 fetch 全部记录数据,角标/进度条/"你有 N 条草稿"等场景浪费流量。本轮新增计数专用 API,只调 .count() 不 fetch 记录:
    • 云函数新增 doRecordCount:safeCollection 校验 + stripOpenid 剥离保留字段 + where({ openid, ...query }).count(),仅返回 { code:0, count },不传输任何记录数据。
    • SDK Record.countRecords(collection, { query }) 透传并返回数字,未登录拦截。
    • record-panel 新增 count(query) 便捷方法(不传 query 时默认用面板 query 属性,失败弹 toast)。
  • 测试增强:record.test.js 新增 3 条断言(未登录拦截、计数值返回、query 透传)。总断言数 93 → 96。

v1.5.12

  • 批量删除 recordBatchDelete:此前只能单条删(一次删一条要调多次云函数)。本轮补齐批量删除能力,三端打通:
    • guard.js 新增 safeIds(ids):数组、非空、最多 20 个、元素须为字符串(云数据库 _id 形如 24 位 hex,允许更长)、自动去重;空数组/非数组/含非字符串/超长/超 20 个 → 返回 null。
    • 云函数新增 doRecordBatchDelete:先过 safeCollection 校验集合名,再经 safeIds 校验 ids,最后以 where({ openid, _id: _.in(ids) }) 批量 remove——关键:openid 与 _id 双重条件,杜绝传入他人 _id 误删;返回 { code:0, removed }。
    • SDK Record.batchDeleteRecords(collection, ids) 透传并返回 removed;record-panel 新增 batchDelete(ids) 便捷方法(带归属隔离,失败弹 toast)。
  • 测试增强:guard.test.js 新增 6 条 safeIds 断言(合法/去重/非字符串拒绝/空数组/非数组/超 20);record.test.js 新增 3 条断言(未登录拦截、removed 返回、ids 透传)。总断言数 84 → 93。

v1.5.11

  • 列表字段投影 fields:真实列表常只需展示标题/状态等少量字段,全字段返回浪费流量。本轮新增:
    • guard.js 新增 safeFields(fields):数组元素须为合法字段名(同 safeOrderBy 规则)、最多 10 个、自动去重;空数组/非数组/含非法字段名/超 10 个 → 返回 null(不投影,返回全部字段)。
    • cloud/index.js doRecordList:合法 fields 经白名单后构建投影对象(_id 始终保留,避免断更),调用 .field(proj);非法则全字段返回。
    • SDK listRecords 透传 fields;record-panel 新增 fields 属性(数组,默认 null),load/loadMore 统一携带。
  • 测试增强:guard.test.js 新增 6 条 safeFields 断言(合法/去重/带点路径拒绝/非数组/超 10/空数组);record.test.js 新增 1 条 fields 透传断言。总断言数 77 → 84。

v1.5.10

  • 列表排序支持复合排序:原 safeOrderBy 仅支持单字段 {field, dir},真实场景常需「先按状态升序、再按时间倒序」等多字段排序。本轮改造:
    • safeOrderBy 兼容单对象与数组 [{field,dir}, ...] 两种用法,统一返回规范化数组(内部云函数逐条链式 .orderBy,顺序即优先级);最多 3 个字段,超长/空数组拒绝;数组内任一元素非法则整体拒绝,杜绝注入滥用。
    • record-panel 的 order-by 属性现支持传数组(如 [{field:'status',dir:'asc'},{field:'createdAt',dir:'desc'}]),load/loadMore 统一携带、query observer 自动重载。
    • SDK listRecords 透传数组形式 orderBy,由服务端白名单校验。
  • 测试增强:guard.test.js 重写 safeOrderBy 断言并新增 4 条复合排序用例(数组放行/$字段整体拒绝/超 3 字段拒绝/空数组拒绝);record.test.js 新增 1 条复合排序透传断言。总断言数 72 → 77。

v1.5.9

  • 补齐单条查询 recordGet:CRUD 此前缺「按 id 查单条」这一环,本轮在 SDK / 云函数 / 测试三端打通。
    • Record.getRecord(collection, id, { query }):成功返回记录对象,记录不存在或归属不符(服务端 404)返回 null,其他错误抛异常;未登录拦截。
    • 云函数新增 doRecordGet:先过 safeCollection 校验集合名,按 _id 取文档后以服务端 openid 做归属隔离;可选 query 作为 post-filter,所有键须与记录字段一致,否则当作 404(不泄露「存在但非本人」的归属信息)。
    • record-panel 新增 getById(id) 方法,供宿主通过 selectComponent 按 _id 拉取单条记录(同样带归属隔离)。
  • 测试增强:record.test.js 新增 4 条断言(未登录拦截、getRecord 成功返回、id/query 透传、404 返回 null),总断言数 68 → 72。

v1.5.3

  • 修复示例宿主 bind:change 语义混淆:examples/host 中 record-panel 的 bind:change 原复用 onLogin(显示"欢迎回来"),导致每次增删改记录后都弹欢迎提示。改为独立 onChanged 处理器,显示"已同步"。
  • npm 发布包含 examples 目录:package.json 的 files 字段新增 examples,npm 安装后可直接参考示例宿主接线方式。

v1.5.4

  • record-panel 新增 query 属性:透传给 listRecords 的 query 服务端过滤条件;运行时变更自动重新加载(首屏不重复加载)。
  • 修复 loadMore 丢失 query:翻页拉取后续页时此前未带 query,导致首页过滤、后续页不过滤的数据不一致 bug,现已统一带过滤条件。
  • record-panel 新增 empty-text 属性:自定义空列表文案(默认"还没有记录,点「+ 记一笔」开始")。
  • record-panel 新增 reload() 方法:供宿主通过 selectComponent 外部调用手动刷新列表。
  • 测试增强:record.test.js 新增 query 透传断言(总断言数 51)。

v1.5.5

  • 列表排序支持 + 注入防护:listRecords / 云函数 doRecordList 支持可选 orderBy(默认 createdAt 倒序);新增 safeOrderBy 白名单校验(字段名仅允许字母/下划线开头 + 字母数字下划线、方向仅 asc/desc),拒绝 $ 操作符字段、带点路径、非法方向,防 NoSQL 注入。
  • record-panel 新增 order-by 属性:透传排序条件,load/loadMore 统一携带,运行时变更随 query observer 自动重载。
  • 测试增强:guard.test.js 新增 6 条 safeOrderBy 断言、record.test.js 新增 orderBy 透传断言(总断言数 58)。

v1.5.6

  • init 选项校验:mode 非 cloudbase/http 时抛清晰错误;http 模式缺失 httpBase 时抛清晰错误——避免在请求时才神秘失败,提升接入体验。
  • 测试增强:client.test.js 新增 2 条 init 校验断言(总断言数 60)。

v1.5.7

  • 记录写入载荷护栏:新增 safeRecord——限制单条记录字段数(≤50)、拒绝嵌套对象/数组(保持扁平)、超长字符串截断到 2000 字,防止单条记录被塞爆或塞入非法类型;doRecordAdd 调用,非法载荷返回 400。
  • 测试增强:guard.test.js 新增 7 条 safeRecord 断言(总断言数 67)。

v1.5.8

  • 更新路径同样过 safeRecord 护栏:doRecordUpdate 的 patch 现在先经 safeRecord(字段数≤50、禁嵌套、超长截断)再写入,与 doRecordAdd 防护一致;非法补丁返回 400。
  • 测试增强:guard.test.js 新增 1 条非字符串类型(布尔/数字/小数)保留断言(总断言数 68)。

v1.5.2

  • HTTP 模式请求超时:init 新增 timeout 选项(默认 10000ms),wx.request 添加超时防止挂起;可通过 arkit.init({ timeout: 15000 }) 调整。
  • record-panel 分页失败重试:loadMore() 失败时设置 loadMoreError 标志,底部显示红色「加载失败,点击重试」(autoLoad 和手动模式均生效),用户点击即可重试加载下一页,已有列表不受影响。

v1.5.1

  • 修复 record-panel CSS 类名冲突:表单按钮行(取消/保存)原与卡片内容行共用 .rp-row 类名,导致卡片行被错误添加 margin-top 和 gap。改用独立 .rp-btn-row 类名。
  • doRecordUpdate 添加 updatedAt 时间戳:更新记录时自动写入服务端时间(db.serverDate()),客户端无法伪造;便于追踪记录最后修改时间。
  • 云函数 HTTPS 请求添加超时保护:getAccessToken 和 getPhoneNumberByCode 添加 5s 超时 + destroy,防止微信接口无响应时云函数挂起超时。

v1.5.0

  • record-panel 自动加载更多:新增 auto-load 属性(默认 true),使用 IntersectionObserver 监听列表底部哨兵元素,用户滚动到底部时自动调 loadMore() 拉取下一页——无需手动点击。设为 false 回退为手动「加载更多」按钮。全部加载完毕后显示「没有更多了」。
  • auth-login busy 卡死修复:添加 visible 属性 observer,组件隐藏时(visible: false)自动重置 busy,防止登录请求进行中被关闭后再次打开时 busy 卡死无法登录。

v1.4.3

  • token.verify 分隔符解析硬化:改用 lastIndexOf('|') 替代 split('|')[0],防止 openid 包含 | 字符时被截断(防御性加固,微信 openid 实际不含 |)。token.test.js 新增含 | 的 openid 往返断言。
  • record-panel 加载失败体验改进:首次 load() 失败时设置 loadError 标志,显示「加载失败」+「重新加载」按钮(而非仅 toast 后留空白屏),用户可一键重试。

v1.4.2

  • 云函数 doLogin 对用户资料做收敛:sanitizeProfile 将 nickName 去空格并截到 32 字、avatarUrl 仅放行 http(s) 且 ≤512 字符,避免脏数据/异常协议头像入库。guard.js 新增 sanitizeProfile 纯函数并补充单测。

v1.4.1

  • 记录 SDK 接入「401 一次性自动刷新重试」:core._withRetry 在业务请求遇瞬时 401 时静默刷新并重试,避免用户操作因 token 临界失效直接报错;刷新失败/重试仍 401 才清态并触发 onUnauthorized。_postProcess 增加 suppress401 选项以支撑重试前的临时抑制。
  • 新增 retry.test.js(4 断言)覆盖 401 自动重试通路。

v1.4.0

  • 服务端 openid 隔离加固:云函数对 recordAdd/recordList/recordUpdate 的业务数据统一 stripOpenid,丢弃客户端上传的 openid/_openid/_id,一律以服务端从 token 解析的 openid 为准——即使客户端在 data/query/patch 注入他人 openid 也会被剥离,杜绝越权读写他人数据。
  • guard.js 新增 stripOpenid 纯函数并补充单测。

v1.3.0

  • record-panel 补全 CRUD:新增「编辑」按钮(受 can-edit 控制),点按以现有记录预填表单,submit 自动走 updateRecord,无需删除重建。
  • 表单标题随新增/编辑动态切换(formTitle)。
  • record.test.js 新增 updateRecord 通路断言。

v1.2.0

  • 客户端 refresh() 增加并发互斥,多个 ensureLogin 复用同一次刷新,避免重复签发。
  • 收到 401(非登录/刷新)时自动清空本地登录态并触发 onUnauthorized,消除「假登录」死循环。
  • 云函数新增 safeCollection 集合安全校验:业务集合禁止复用 arkit_users 且名称仅允许 [A-Za-z0-9_-],防集合名注入。
  • record-panel 的 number 字段提交时自动转为数字类型。
  • 新增 guard.test.js 集合校验单测;测试套件新增并发刷新、401 清态、集合校验用例(共 29 项断言)。

v1.1.0

  • 纯函数 token.js 抽出,可独立单测。
  • 云函数 access_token 内存缓存(提前 5 分钟视为有效),避免每次登录都请求微信接口命中限频。
  • 手机号解密兼容旧版 cloudID 回退路径。
  • 客户端新增 onUnauthorized 钩子与 wx.cloud 未初始化守卫。
  • auth-login 支持 cloudID、点击遮罩关闭、登录忙等防重复提交。
  • record-panel 改用预展开 formItems 规避动态 member 绑定脆弱点,新增分页 loadMore 与 canAdd/canDelete 属性。
  • 新增 test/ 自动化测试套件。

v1.0.0

  • 基础能力:手机号 + 微信一键授权登录、HMAC 签名 token 与自动刷新、通用记录 SDK(cloudbase / http 双模式)、auth-login / record-panel 两个开箱即用 UI 组件。