41 Commits
Author SHA1 Message Date
wc24 6dff7b82cc 0.9.0: 导入导出协议改成 sheet 表(多 sheet)
export 返回 { sheet 名: 行数组 },import 收同样的形状 —— 一份负载对应一个 xlsx
工作簿。默认仍是单张表(以 moduleKey 命名),模块重写 export/import 就能自定义
多 sheet(如商品/商店/销售规则一次搬完),不必再把多张表压扁成 __t/__data 两列
塞进一个 sheet。

import 取同名 sheet、没有就退回第一张:手工在 Excel 里建的表叫 Sheet1,老版本
前端发的是裸数组,两种都不该白导。
v0.9.0
2026-08-21 20:05:37 +08:00
wc24 355f808294 docs: 字段类型表照实重列——删掉不存在的 json,补上 object/array;select 的属性名是 options;补只读模块怎么注册 v0.8.1 2026-08-11 16:57:35 +08:00
wc24 064bc108d7 0.8.0: express 收紧到 ^5;契约副本改为 build 时自动同步
破坏性:peerDependencies.express 从 "^4 || ^5" 收成 "^5"。
buildShrimpRouter 的 router.post("/*path") 是 Express 5 才有的写法,
声明支持 4 一直是假的。devDeps 同步升到 express 5.1 / @types/express 5,32 个测试通过。

契约同步:新增 postbuild,把 src/IShrimpType.ts 覆盖到同级 shrimp-admin 的副本
(scripts/check-admin-contract.mjs;找不到同级仓库就跳过,不影响 CI 与消费侧 prepare)。
起因是那份副本靠人工复制,漂了一整代没人发现——停在旧的 json 类型,
缺 object/array/fields/item/parse/refs,而前端对未注册类型静默回落成 string,
漂移不报错、只悄悄渲染错。

删掉 @contractVersion 版本轴:代码无人读取,且从 1.0.0 起从未移动,
期间契约实际改了好几处,数字全程说谎。副本新不新改由字节比对回答,
消费侧看钉的 tag 即可,版本收敛成 package version 一根轴。
v0.8.0
2026-08-10 19:42:52 +08:00
wc24 19afdf3615 fix(relation refs): buildRefs 跳过空串关联 id,避免可选 Relation 存 "" 时 $in 转 ObjectId 抛 CastError 整页 500
v0.7.11 误改在废弃的 _label/populateItems(无调用方)上:列表实际走 list()->buildRefs()。
本次回退 populateItems,改在真正活路径 collectOne。bump 0.7.12
v0.7.12
2026-07-29 15:34:13 +08:00
wc24 31b723839e fix(populate): 跳过空串关联 id,避免可选 Relation 存 "" 时 $in 转 ObjectId 抛 CastError 整页 500
bump 0.7.11
v0.7.11
2026-07-29 14:56:43 +08:00
wc24 1f48a02083 test+chore: 修 stale 测试(_label→refs 新契约)+ bump 0.7.10
crudRegistry.test 还在断言已退场的 tagIds_label,改断言整页 refs 字典。
两端版本归一到 0.7.10。
v0.7.10
2026-06-30 17:47:08 +08:00
wc24 0f2b4d6428 fix(pkg): 删掉 shrimp-server 对自身的 git 依赖(致 npm install OOM)
dependencies 误列了 shrimp-server 自引用(#v0.7.4)→ arborist 顺着自引用无限解析
依赖树、堆爆 OOM。真正依赖全在 peerDependencies,该块整体删除。含 0.7.6 的
isEmptyValue 递归判空。
v0.7.7
2026-06-30 17:22:39 +08:00
wc24 06aec7f0dc feat(required): 按类型递归判定空(tab/object/array 各自语义)
isEmptyValue(field,value) 递归: tab=没选type / object=所有子字段都空 / array=无元素或
所有元素都空(多维递归) / 标量=null|''/空数组(0、false 不算空)。required=!isEmpty 自动
对所有类型生效。validateNode: 空的复合(非必填)不下钻校验子(整个结构缺席)。
v0.7.6
2026-06-30 16:52:08 +08:00
wc24 172c49816e feat(filter): 嵌套 filterable 叶子 → 点路径过滤(object/tab)
collectFilterablePaths 递归收集标了 filterable 的字段(object 子/tab 分支子)连同点路径;
buildQuery 白名单从顶层字段扩成路径(点路径本就是 mongoose 嵌套查询,翻译逻辑不变)。
array 下钻、tab 判别值过滤留后续。
v0.7.5
2026-06-30 16:01:20 +08:00
wc24 a8cd07f851 fix(validate): 错误信息不再拼开发用的 [path],回归干净字段名+i18n
校验错只发 {t:i18nKey, params:{field:label}}(不能为空走语言包 key、字段名走 label),
不把 groups.0.title 这种路径塞进可见文案。path 仍递归传递,留给将来 inline 标红结构化携带。
v0.7.4
2026-06-30 15:17:58 +08:00
wc24 24afc42de9 feat(validate): 递归校验引擎(校验下沉到任意深度)
childrenOf(field,value): tab 选中分支 fields / object fields / array 元素套 item → 子(字段,值)对。
validateNode 递归: 每节点跑 validateField,出错即抛(带 path),再下钻复合。
接入 prepareAddItem/prepareEditItem(替原顶层 validateField)。第一版 fail-fast,
嵌套错 message 缀 [path] 定位。默认/transform 下沉、collect-all、inline 标红留后续。
v0.7.3
2026-06-30 14:49:48 +08:00
wc24 6383aade98 fix(crud): 响应统一剥掉 mongoose 版本键 __v
filterHidden 出口加剥 __v(连同 hidden 字段),list/get/add/edit 全覆盖。
前端无用的 __v 不再下发。
v0.7.2
2026-06-30 14:09:44 +08:00
wc24 d12877f846 fix(crud): add/edit 返回普通对象(toObject/lean),不再抖出 mongoose Document 内部
add 用 create 返回 Document、edit 用 findByIdAndUpdate 返回 Document,下游 processResItem
的 {...item} 会把 $__/_doc/$isNew 等内部属性展开、真数据埋进 _doc。改:add 转 toObject、
edit 加 .lean()(与 list/get 一致)。响应回归干净 POJO。
v0.7.1
2026-06-30 14:06:40 +08:00
wc24 c264ec6673 feat!: 去掉 json 类型(object 取代) + 版本统一 0.7.0
- object 字段已能声明式表达嵌套结构,freeform 的 json 类型不再需要:
  删 ShrimpFieldTypes.Json + defaultTypeHandlers[Json](reqFilter/resFilter)。
- 源码无任何 type:json 使用,安全移除。
- 前后端版本自此统一:一起推的版本用相同版本号,本批 = 0.7.0。
v0.7.0
2026-06-29 22:25:58 +08:00
wc24 f437e2e5e0 feat(composite): 新增 object/array 字段类型(refs 下钻 + 类型常量/配置)
- ShrimpFieldTypes 加 Object/Array
- IFieldConfig 加 fields(object 嵌套字段) + item(array 元素配置,递归)
- collectRefs 重构成 collectOne(单字段+值),加 object(遍历 fields)/array(遍历元素套 item)
  分支 → refs 下钻覆盖 object/array 里的嵌套 relation。
显示/编辑组件在 shrimp-admin。第一版从简:无 min/max、无排序、无递归校验引擎。
v0.6.11
2026-06-29 22:21:26 +08:00
wc24 ad5f7f18f5 refactor(export): 去掉后端可逆序列化(resLabel + export stringify),序列化交前端
export 只返回原始数据;结构化字段的字符串化交前端(生成 xlsx 时)。
删: tab resLabel、IFieldTypeHandler.resLabel、processResItem 的 _label 段、
export() 的 stringify 折叠、IFieldConfig.format/stringify。保留 tab reqFilter(import parse)
+ IFieldConfig.parse。彻底清掉 tab 显示搬前端后那个 _label 遗留。
v0.6.10
2026-06-29 21:47:08 +08:00
wc24 e04d01a80c feat(refs): buildRefs 递归下钻 tab,覆盖嵌套 relation
collectRefs 递归收集:relation 叶子直接收,tab 按 value.type 进选中分支继续下钻
(顺带覆盖 tab 套 tab)。refs 字典现含嵌套关联名(如 sellType.goods),
为 tab 显示搬前端(子组件渲染 + relation 查 refs)铺好地基。
v0.6.9
2026-06-29 21:28:32 +08:00
wc24 eb339ccc49 feat(filter): buildQuery 放行 _id 精确过滤(单项查看/relation 跳转)
filter 原本只认 filterable 字段,_id 会被丢弃。新增: key==='_id' 时始终以 eq 放行,
让 ?filter={_id} 能过滤到单条。_id 是主键精确匹配、用户已有列表权限,无安全顾虑。
v0.6.8
2026-06-29 20:40:28 +08:00
wc24 8f9dfd40e0 feat(relation): 用整页 refs 字典替代逐行 _label
list 响应新增 refs: { moduleKey: { id: 显示名 } },整页一次性按模块批量查、去重;
去掉 relation 的 populateItems→${key}_label 逐行注入。前端显示时按 id 查 refs。
tab 的 resLabel/_label 不动(留待复合那步)。这是嵌套引用的地基:collectRefs 以后加
tab 的 children() 下钻即可覆盖任意深度。
v0.6.7
2026-06-29 20:17:26 +08:00
wc24 ee35b47f69 revert(tab): 撤回 v0.6.5 嵌套 relation 解析,回到 format 纯展示
populateTabRelations / format 第二参是 tab 专属特例,太狭隘(object/array 同类问题未覆盖),
通用复合解析另当专门事做。嵌套关联名(如商品名)的展示需求改放业务侧(模块重写 list/get)。
框架只保留: format(同步纯展示) + stringify/parse(可逆往返)。
v0.6.6
2026-06-29 17:33:52 +08:00
wc24 bc54b475cf feat(tab): format 可拿到子 relation 解析名(批量,无 N+1)
tab 字段的 relation 子字段(如 sellType.goods)原只有 id,format 显示不了关联名。
新增 populateTabRelations:批量(每个关联模块一次 $in via crud.list)解析子 relation 的
labelKey 名,挂到 item.__tabRel[fieldKey][childKey],作为 format 第二参 relLabels 传入;
不污染 tab 本体(避免回灌进库),响应末尾删除 __tabRel。
v0.6.5
2026-06-29 16:56:45 +08:00
wc24 4b51d31e9f feat(tab): 拆开展示与序列化 — field.format(展示) vs stringify/parse(可逆往返)
v0.6.3 用同一个 stringify 同时扛表格展示和 export 序列化,二者诉求冲突:
展示要好看可有损,export 要可逆完整。现拆为:
- field.format → _label 展示文本(给人看,可有损),默认 JSON.stringify
- field.stringify/parse → export/import 可逆对(机器往返),默认 JSON
export 改用 stringify(可逆)而非 _label,import 的 parse 能还原。
v0.6.4
2026-06-29 16:42:11 +08:00
wc24 c5c06bee13 feat(tab): 结构化字段端到端传对象,给人看的边界走 _label/stringify
tab 字段原把对象 JSON.stringify 成字符串下发(复用 json 的 resFilter),
导致前端 FieldTab 收到字符串:表格 display 显示 '-'、编辑回退默认值。

改为:
- 库/响应/编辑全程对象(去掉 tab 的 resFilter stringify)
- 新增 resLabel 钩子 → 写入 ${key}_label,承载给人看的字符串文本
- export 边界用 _label 文本替换对象列(可读且可被 import 的 parse 还原)
- 字段级可配 stringify/parse,默认 JSON
v0.6.3
2026-06-29 15:45:58 +08:00
wc24 f94a01e0a1 fix(shrimp): 修复 multiple 字段类型校验逻辑 2026-06-15 14:34:36 +08:00
wc24 5ecbf2cc01 docs: array/object 字段类型设计草案(搁置待需求,含前后端方案) 2026-06-10 15:47:57 +08:00
wc24 9fff8c472d v0.6.2: powers 字段改自定义类型,绕过前置验证的标量类型校验 v0.6.2 2026-06-08 19:22:56 +08:00
wc24 187c6f6030 v0.6.1: 改为编译 ship,通过 npm prepare hook 自动构建
- 加 tsconfig.build.json,排除 test,rootDir 设为 src
- package.json: main/types/exports 指向 dist/main/index.{js,d.ts}
- prepare hook: 消费侧 git 安装后自动 tsc,产物不进 git
- 解决消费侧 "tsc + 裸 node" 链路 (theseus-server 等) 找不到 .js 的问题
v0.6.1
2026-06-01 19:32:48 +08:00
wc24 c99ea0e18c v0.6.0: 收单入口
- 类型契约/Express 装配/MongoShrimp 全部合并进 src/main/index.ts
- 删 src/main/expmon.ts、src/main/types.ts
- package.json exports 收成只剩 "."(和 ./package.json)
- IShrimpType.ts 头加 @contractVersion 1.0.0(供 shrimp-admin 副本对照)

未上生产,直接断掉 shrimp-server/expmon、shrimp-server/types 两个子入口。
v0.6.0
2026-05-23 22:27:36 +08:00
wc24 652b31052e docs(README): 约定按作用域分层(全局 vs 条件)
shrimp 不是单体协议而是可拼装件集合,每个件有自己的契约。
作用域 = 你用了哪个件就遵守哪个件的契约,不用不必遵守。

- 🌐 全局约定:IModuleInfo / IFieldConfig / Shrimp 树 / Power-Role / ILabel
- 🔌 条件约定:按宿主件分组(ShrimpTable / ShrimpServer / MongoShrimp /
  OperationLogModule / 默认 Field*.vue / 子模块机制)

这一层概念是 shrimp-admin 抽包前要先共识的:shrimp-admin 不是框架,
是"消费 ShrimpServer + ShrimpTable 这两套条件契约"的经典前端实现。
2026-05-23 20:47:35 +08:00
wc24 ca283c6733 v0.5.0: 重新组织目录结构,按职责拆分
- Shrimp.ts (875 行) 按类拆分到 Shrimp/(7 文件: Shrimp, ShrimpModule, ShrimpTableModule, ShrimpPower, ShrimpCrud, UserRoleModule, OperationLogModule)
- forMongo/: MongoShrimpCrud + MongoShrimp(原 ShrimpServer 的 mongo 装配部分,无 express)
- forExpress/: buildShrimpRouter + jwtAuth + types
- main/: index(0 依赖核心) + expmon(express+mongo 整合,导出 ShrimpServer) + types(纯协议)
- 入口子路径 server → expmon(express+mongo 整合方案命名更准确)
- 删除冗余的 index.ts / core.ts(合并为 main/index.ts)
v0.5.0
2026-05-23 19:44:02 +08:00
wc24 e8fc126fa6 feat: 新增 ./types 子入口,只 ship IShrimpType.ts
前端 / shrimp-admin / 任何"只要协议、不要框架运行时"的消费方装上
shrimp-server 后,import from 'shrimp-server/types' 就能拿到 types
+ ShrimpCode + ShrimpError,不会触发 Shrimp 主类 / Mongoose / express。

IShrimpType.ts 无外部 import,exports 字段把它单独切出来,vite/webpack
打包到前端时不会顺手拉走 Mongo 链。

v0.4.0
v0.4.0
2026-05-23 19:03:04 +08:00
wc24 d8e0095a28 docs: bump install snippet to v0.3.0 + 加 ts-node/tsc 配置说明
迁移 hand-server-2 时撞到两个坑,补到 README:
  - ts-node 默认 ignore node_modules,要在 tsconfig 加 "ts-node": { "ignore": ["(?!)"] }
  - tsc 默认 moduleResolution "node" 不读 exports 字段,要升 node16 或加 paths
2026-05-23 17:18:47 +08:00
wc24 416eb84db7 refactor(server): 拆分 ShrimpServer → buildShrimpRouter + tokenAuth + accessor
ShrimpServer 之前一锅端 Express routes / JWT / Mongoose / 用户字段访问。
现在拆成独立部件,消费方可以单独换掉任何一块:

  - src/server/types.ts: ITokenAuth, IUserAccessor, IShrimpRouterDeps
  - src/server/jwtAuth.ts: createJwtTokenAuth(secret | options) 默认 JWT 适配器
  - src/server/buildShrimpRouter.ts: 纯函数,不直接 import jsonwebtoken/mongoose
  - src/server/ShrimpServer.ts: 装配体,protected create* factory 保留,
    子类化扩展不破坏(hand-server-2 的 AdminApi extends ShrimpServer 仍能用)
  - src/server/index.ts: 'shrimp-server/server' entry,额外暴露低阶 API

test/buildShrimpRouter.test.ts: 用 MemoryShrimpCrud + 假 ITokenAuth 拼出
独立 Router,跑通 /config /login /me 全流程 — 保证 buildShrimpRouter 不
偷偷耦合回 ShrimpServer。

v0.3.0
v0.3.0
2026-05-23 17:10:03 +08:00
wc24 36b71bd38e feat: passwordHasher 可注入 — bcrypt 不再是 Shrimp 核心硬依赖
新增 IPasswordHasher 抽象 + bcryptHasher(默认,lazy import)+ plainHasher
(测试用)。Password 字段 reqFilter 和 UserModule.verifyPassword 都走
`Shrimp.passwordHasher`,消费方在启动期换实现就能彻底跳过 bcrypt。

  import { Shrimp, plainHasher } from "shrimp-server"
  Shrimp.passwordHasher = plainHasher

测试覆盖默认值、plain 端到端登录、自定义 hasher 被调用。
2026-05-23 16:57:27 +08:00
wc24 36326ac26b chore: 删除 examples/ — git+http install 会把它一起拖给消费方
包内自带的 Vue demo 不是 shrimp 框架的一部分,git+http 安装时
`files: [...]` 不生效,examples/(尤其 web/ Vue 子项目)会进消费方
node_modules,污染 install 体积并触发 tsc 噪音。

完整接入示例去 hand-server-2 主项目看;basic-admin 那种零依赖
demo 可以以后单独 pin 在 docs/ 或独立仓库。
2026-05-23 16:54:58 +08:00
wc24 8e4d5620b7 feat!: 拆分 core / server 入口,peer deps 全部 optional
让只用 Shrimp 核心(RPC + 模块声明 + 内存 CRUD)的项目不必装
express / mongoose / bcrypt / jsonwebtoken,显著降低 install 时间。

- src/core.ts:0 重依赖入口(Shrimp / MemoryShrimpCrud / 类型)
- src/server.ts:在 core 之上再导出 ShrimpServer + MongoShrimpCrud
- package.json:加 exports 字段 + peerDependenciesMeta.optional
- Shrimp.ts:bcrypt 改 lazy import,只有走 Password 字段才触发
- MemoryShrimpCrud.populateItems:接 crudRegistry 参数(对齐 Mongo)
- ShrimpTableModule 构造时自动登记 crudRegistry,业务无需手动 wire
- 新增 test/crudRegistry.test.ts 验证自动登记 + relation 自动 populate

BREAKING: import { ShrimpServer } from 'shrimp-server' 不再可用,
请改为 'shrimp-server/server'。
v0.2.0
2026-05-23 16:47:18 +08:00
wc24 40f311796b docs: install 指令推荐 http,补 ts 运行器要求
README 的 install 块原来只有 ssh,内网用户不一定都配了 ssh key。改成
http 优先,ssh 备选。同时补一句消费方需要 tsx/ts-node —— 仓库 ship .ts
源,装完就能 import 但 node 直接跑会炸。

下一步打 v0.1.0 tag 让这条指令真能用。
v0.1.0
2026-05-23 16:21:54 +08:00
wc24 21a56a7b2d fix: getMenuTree 默认用 module.label,而非凭空假定 i18n key
Shrimp.getMenuTree() 之前默认输出 { t: '<moduleKey>.menuManage' },
强加一个用户未必写过的 i18n key,不写翻译就显示 raw key。退化为直接
用 module.label —— 模块注册时已声明,不另起 key。

UserModule / RoleModule 同步补 label: 之前 super(parent, "user", ...)
没传 label,会落到 ShrimpTableModule 默认的 { t: moduleKey };现在显
式传 { t: "user.menuManage" } / { t: "role.menuManage" },沿用 hand-
admin 已有的翻译键,迁移零成本。

demo 里临时塞的 DemoShrimp 子类清掉,直接 new Shrimp()。
2026-05-23 15:52:27 +08:00
wc24 fea7c16d65 demo: 补 tag.vue / product.vue 页面壳
VueAdmin 按 moduleKey 自动找 @/module/<key>.vue,demo 后端注册了 tag/product
两个模块但前端只剩 user.vue/role.vue,导致路由 404。两个文件都是 <ShrimpTable
moduleKey="..." /> 一行壳,业务由框架自动渲染。
2026-05-23 15:33:35 +08:00
wc24 27a2348815 demo: 加 Vue 前端到 examples/basic-admin/web
为了端到端验证 Shrimp 框架,把 hand-admin 前端拷进 examples/basic-admin/web
作为临时演示用前端,@shrimp alias 直接指向同仓 src/,免构建。

修改要点:
- web/vite.config.ts: @shrimp 改成 ../../../src; proxy /admin-api 到 :3100
- web/tsconfig.json: 同步 @shrimp 路径; 删掉 hand-server-2 的 paths
- web/src/locales: 删掉 matter_*/zerobase 业务 key
- web/src/module: 删掉 matter_*/zerobase 业务 vue,只留 user/role/FieldRelativeTime
- examples/basic-admin/index.ts:
  - mount 到 /admin-api/* 而不是 /api/*
  - 加 /config /login /me 端点 (mock super-admin operator,固定 token)
  - DemoShrimp 子类覆盖 getMenuTree 用模块自身 label
  - MemoryShrimpCrud 的 getRelatedCrud 手动 wire 到 crudRegistry,
    populate 才能跑 (后续 70→90 改进项)

curl 已验证 /config /login /me /product/list 全通,tagIds_label 正确旁挂。
2026-05-23 15:28:10 +08:00
wc24 8548a1760a init: 从 cs-hand/hand-server-2/lib/shrimp 抽出独立仓
- 6 个框架源文件搬到 src/, 加 src/index.ts barrel 作为公共 API 入口
- package.json 标 private, main/types 指 src/index.ts (ts-node 消费方编译)
- 加 vitest + 22 条单元测试 (ILabel / MemoryShrimpCrud) 全通过
- examples/basic-admin/ 用 MemoryShrimpCrud 跑零 Mongo 依赖 demo

待办: ShrimpServer 强耦合 Mongo+JWT+Express, crudRegistry 用
.model 判断数据源, 这两个问题在 demo 中已暴露, 计划后续重构补到 90 分。
2026-05-23 15:07:55 +08:00