This repository has been archived on 2026-09-13. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
BPOS-TMP-REPO/packages/utils/src/index.ts
T
JCAI Main AgentandClaude Opus 5 7cd664af3a M0: 工作区骨架 + 五道门禁 + 环境自举
- pnpm workspaces / tsconfig 双工程(node · web)/ 路径别名(§11.1)
- eslint flat(§11.3 + §12.5 可机检项)/ dependency-cruiser 基线八条 + JCAI 增补(§11.4)
- tools/module-tiers.json + gen-depcruise-tiers.ts 生成 tier-order 逐层规则
- tools/api-report.mjs 契约快照只留契约面(ADR-005)
- docker-compose(PG16)+ db/init 四 schema;种子数据五岗位五员工冻结为常量
- docs/debt.md · docs/adr/ADR-005

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KeDBx2PHLavbURXB8LJJ7m
2026-07-27 00:18:58 +08:00

30 lines
1.0 KiB
TypeScript

/**
* @utils —— 纯函数,零依赖(编码规范 §2 / §5.1:叶子包)。
* 无业务语义;有业务语义的东西不属于这里。
*/
/** 穷尽性检查:switch 漏分支时编译期报错 */
export function assertNever(value: never, message = '未覆盖的分支'): never {
throw new Error(`${message}: ${JSON.stringify(value)}`);
}
/** 类型守卫:滤掉 null / undefined */
export function isDefined<T>(value: T | null | undefined): value is T {
return value !== null && value !== undefined;
}
/** 按键分组 */
export function groupBy<T, K extends string>(items: readonly T[], key: (item: T) => K): Record<K, T[]> {
const out = {} as Record<K, T[]>;
for (const item of items) {
const k = key(item);
(out[k] ??= []).push(item);
}
return out;
}
/** 稳定排序的键值对遍历(避免 Object.keys 顺序依赖) */
export function sortedEntries<V>(record: Readonly<Record<string, V>>): Array<[string, V]> {
return Object.entries(record).sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0));
}