@longzai-intelligence/pagination 0.0.1 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/.turbo/turbo-build.log +3 -3
  2. package/.turbo/turbo-test$colon$coverage.log +6 -6
  3. package/.turbo/turbo-test.log +7 -7
  4. package/CHANGELOG.md +7 -0
  5. package/README.md +5 -1
  6. package/coverage/coverage-final.json +6 -2
  7. package/coverage/index.html +19 -4
  8. package/coverage/src/adapters/index.html +1 -16
  9. package/coverage/src/adapters/typeorm.adapter.ts.html +1 -1
  10. package/coverage/src/index.html +16 -1
  11. package/coverage/src/index.ts.html +38 -11
  12. package/coverage/src/schemas/index.html +161 -0
  13. package/coverage/src/{adapters → schemas}/index.ts.html +61 -10
  14. package/coverage/src/schemas/pagination.schemas.ts.html +343 -0
  15. package/coverage/src/schemas/result.schemas.ts.html +724 -0
  16. package/coverage/src/schemas/sort.schemas.ts.html +544 -0
  17. package/coverage/src/typeorm.ts.html +154 -0
  18. package/coverage/src/types/index.html +1 -1
  19. package/coverage/src/types/index.ts.html +69 -3
  20. package/coverage/src/types/pagination.types.ts.html +15 -138
  21. package/coverage/src/types/result.types.ts.html +11 -299
  22. package/coverage/src/types/sort.types.ts.html +16 -139
  23. package/coverage/src/types/typeorm.d.ts.html +1 -1
  24. package/coverage/src/utils/index.html +1 -1
  25. package/coverage/src/utils/index.ts.html +1 -1
  26. package/coverage/src/utils/pagination.util.ts.html +1 -1
  27. package/coverage/src/utils/validation.util.ts.html +1 -1
  28. package/dist/index-C8FaNzej.d.mts +145 -0
  29. package/dist/index-CMiyhlfm.d.cts +145 -0
  30. package/dist/index.cjs +33 -1
  31. package/dist/index.d.cts +2 -81
  32. package/dist/index.d.mts +2 -81
  33. package/dist/index.mjs +1 -1
  34. package/dist/typeorm.cjs +129 -0
  35. package/dist/typeorm.d.cts +26 -0
  36. package/dist/typeorm.d.mts +26 -0
  37. package/dist/typeorm.mjs +1 -0
  38. package/dist/utils-CV6Ovku6.mjs +1 -0
  39. package/dist/utils-D_qD2YAX.cjs +1 -0
  40. package/docs/specs/spec-20260413-typeorm-subpath-export/checklist.md +33 -0
  41. package/docs/specs/spec-20260413-typeorm-subpath-export/spec.md +104 -0
  42. package/docs/specs/spec-20260413-typeorm-subpath-export/tasks.md +48 -0
  43. package/package.json +15 -3
  44. package/src/__tests__/index.test.ts +9 -3
  45. package/src/__tests__/pagination.util.test.ts +0 -2
  46. package/src/__tests__/result.schemas.test.ts +142 -0
  47. package/src/__tests__/sort.schemas.test.ts +157 -0
  48. package/src/__tests__/typeorm.adapter.test.ts +1 -3
  49. package/src/__tests__/validation.util.test.ts +0 -2
  50. package/src/adapters/{typeorm.adapter.ts → typeorm/adapter.ts} +10 -92
  51. package/src/adapters/typeorm/index.ts +23 -0
  52. package/src/adapters/typeorm/types.ts +95 -0
  53. package/src/index.ts +18 -9
  54. package/src/schemas/index.ts +28 -0
  55. package/src/schemas/pagination.schemas.ts +86 -0
  56. package/src/schemas/result.schemas.ts +213 -0
  57. package/src/schemas/sort.schemas.ts +153 -0
  58. package/src/typeorm.ts +23 -0
  59. package/src/types/index.ts +23 -1
  60. package/src/types/pagination.types.ts +13 -54
  61. package/src/types/result.types.ts +9 -105
  62. package/src/types/sort.types.ts +14 -55
  63. package/tsconfig/.cache/app.tsbuildinfo +1 -1
  64. package/tsconfig/.cache/node.tsbuildinfo +1 -1
  65. package/tsconfig/.cache/test.tsbuildinfo +1 -1
  66. package/tsconfig/app.json +4 -1
  67. package/tsdown.config.ts +10 -3
  68. package/src/adapters/index.ts +0 -11
  69. package/tsconfig/.cache/build.tsbuildinfo +0 -1
  70. /package/src/{types → adapters/typeorm}/typeorm.d.ts +0 -0
@@ -0,0 +1,26 @@
1
+ import { T as PaginationParams, m as PaginatedResult } from "./index-CMiyhlfm.cjs";
2
+ import { FindManyOptions, FindOptionsOrder, ObjectLiteral, Repository, SelectQueryBuilder } from "typeorm";
3
+
4
+ //#region src/adapters/typeorm/types.d.ts
5
+ type EntityMapper<TEntity, TDto> = (entity: TEntity) => TDto | Promise<TDto>;
6
+ type TypeOrmPaginateOptionsWithMapper<TEntity extends ObjectLiteral, TDto> = {
7
+ mapper: EntityMapper<TEntity, TDto>;
8
+ order?: FindOptionsOrder<TEntity>;
9
+ where?: FindManyOptions<TEntity>['where'];
10
+ relations?: FindManyOptions<TEntity>['relations'];
11
+ select?: FindManyOptions<TEntity>['select'];
12
+ };
13
+ type TypeOrmPaginateOptionsNoMapper<TEntity extends ObjectLiteral> = {
14
+ order?: FindOptionsOrder<TEntity>;
15
+ where?: FindManyOptions<TEntity>['where'];
16
+ relations?: FindManyOptions<TEntity>['relations'];
17
+ select?: FindManyOptions<TEntity>['select'];
18
+ };
19
+ type TypeOrmPaginateOptions<TEntity extends ObjectLiteral, TDto = TEntity> = TypeOrmPaginateOptionsWithMapper<TEntity, TDto> | TypeOrmPaginateOptionsNoMapper<TEntity>;
20
+ //#endregion
21
+ //#region src/adapters/typeorm/adapter.d.ts
22
+ declare function paginateWithRepository<TEntity extends ObjectLiteral, TDto = TEntity>(repository: Repository<TEntity>, params: PaginationParams, options?: TypeOrmPaginateOptions<TEntity, TDto>): Promise<PaginatedResult<TEntity | TDto>>;
23
+ declare function paginateWithQueryBuilder<TEntity extends ObjectLiteral, TDto = TEntity>(queryBuilder: SelectQueryBuilder<TEntity>, params: PaginationParams, mapper?: EntityMapper<TEntity, TDto>): Promise<PaginatedResult<TEntity | TDto>>;
24
+ declare function createPaginationQueryBuilder<TEntity extends ObjectLiteral>(repository: Repository<TEntity>, alias?: string): SelectQueryBuilder<TEntity>;
25
+ //#endregion
26
+ export { type EntityMapper, type TypeOrmPaginateOptions, createPaginationQueryBuilder, paginateWithQueryBuilder, paginateWithRepository };
@@ -0,0 +1,26 @@
1
+ import { T as PaginationParams, m as PaginatedResult } from "./index-C8FaNzej.mjs";
2
+ import { FindManyOptions, FindOptionsOrder, ObjectLiteral, Repository, SelectQueryBuilder } from "typeorm";
3
+
4
+ //#region src/adapters/typeorm/types.d.ts
5
+ type EntityMapper<TEntity, TDto> = (entity: TEntity) => TDto | Promise<TDto>;
6
+ type TypeOrmPaginateOptionsWithMapper<TEntity extends ObjectLiteral, TDto> = {
7
+ mapper: EntityMapper<TEntity, TDto>;
8
+ order?: FindOptionsOrder<TEntity>;
9
+ where?: FindManyOptions<TEntity>['where'];
10
+ relations?: FindManyOptions<TEntity>['relations'];
11
+ select?: FindManyOptions<TEntity>['select'];
12
+ };
13
+ type TypeOrmPaginateOptionsNoMapper<TEntity extends ObjectLiteral> = {
14
+ order?: FindOptionsOrder<TEntity>;
15
+ where?: FindManyOptions<TEntity>['where'];
16
+ relations?: FindManyOptions<TEntity>['relations'];
17
+ select?: FindManyOptions<TEntity>['select'];
18
+ };
19
+ type TypeOrmPaginateOptions<TEntity extends ObjectLiteral, TDto = TEntity> = TypeOrmPaginateOptionsWithMapper<TEntity, TDto> | TypeOrmPaginateOptionsNoMapper<TEntity>;
20
+ //#endregion
21
+ //#region src/adapters/typeorm/adapter.d.ts
22
+ declare function paginateWithRepository<TEntity extends ObjectLiteral, TDto = TEntity>(repository: Repository<TEntity>, params: PaginationParams, options?: TypeOrmPaginateOptions<TEntity, TDto>): Promise<PaginatedResult<TEntity | TDto>>;
23
+ declare function paginateWithQueryBuilder<TEntity extends ObjectLiteral, TDto = TEntity>(queryBuilder: SelectQueryBuilder<TEntity>, params: PaginationParams, mapper?: EntityMapper<TEntity, TDto>): Promise<PaginatedResult<TEntity | TDto>>;
24
+ declare function createPaginationQueryBuilder<TEntity extends ObjectLiteral>(repository: Repository<TEntity>, alias?: string): SelectQueryBuilder<TEntity>;
25
+ //#endregion
26
+ export { type EntityMapper, type TypeOrmPaginateOptions, createPaginationQueryBuilder, paginateWithQueryBuilder, paginateWithRepository };
@@ -0,0 +1 @@
1
+ import{c as e,d as t,p as n,s as r,u as i}from"./utils-CV6Ovku6.mjs";function a(e){return e!==void 0&&`mapper`in e}function o(n,r,a,o){let s=e(r,o);return{items:n,total:r,page:a,pageSize:o,totalPages:s,hasNextPage:i(a,s),hasPreviousPage:t(a)}}async function s(e,t,i){let{page:s,pageSize:c}=n(t),l={skip:r(s,c),take:c,where:i?.where,relations:i?.relations,select:i?.select,order:i?.order??{createdAt:`DESC`}},[u,d]=await e.findAndCount(l);return a(i)?o(await Promise.all(u.map(i.mapper)),d,s,c):o(u,d,s,c)}async function c(e,t,i){let{page:a,pageSize:s}=n(t),c=r(a,s);e.skip(c).take(s);let[l,u]=await e.getManyAndCount();return o(i?await Promise.all(l.map(i)):l,u,a,s)}function l(e,t){return e.createQueryBuilder(t??e.metadata.name.toLowerCase())}export{l as createPaginationQueryBuilder,c as paginateWithQueryBuilder,s as paginateWithRepository};
@@ -0,0 +1 @@
1
+ import{z as e}from"zod";const t=e.object({page:e.literal(1),pageSize:e.literal(20),maxPageSize:e.literal(100),minPageSize:e.literal(1)}),n=e.object({page:e.number().int().positive().optional(),pageSize:e.number().int().positive().optional()}),r=e.object({limit:e.number().int().positive().optional(),offset:e.number().int().nonnegative().optional()}),i=e.object({cursor:e.string().optional(),limit:e.number().int().positive().optional()}),a={page:1,pageSize:20,maxPageSize:100,minPageSize:1};function o(t){return e.object({items:e.array(t),total:e.number().int().nonnegative(),page:e.number().int().positive(),pageSize:e.number().int().positive(),totalPages:e.number().int().nonnegative(),hasNextPage:e.boolean(),hasPreviousPage:e.boolean()})}function s(t){return e.object({items:e.array(t),total:e.number().int().nonnegative(),limit:e.number().int().nonnegative(),offset:e.number().int().nonnegative(),hasMore:e.boolean()})}function c(t){return e.object({items:e.array(t),nextCursor:e.string().nullable(),previousCursor:e.string().nullable(),hasMore:e.boolean()})}const l=e.enum([`asc`,`desc`]);function u(t){return e.object({sortBy:e.enum(t).optional(),sortOrder:l.optional()})}const d=e.object({sortBy:e.string().optional(),sortOrder:l.optional()});function f(t){return e.object({field:e.enum(t),order:l})}const p=e.object({field:e.string(),order:l});function m(t){return e.object({sort:e.array(f(t)).optional()})}const h=e.object({sort:e.array(p).optional()});function g(e){return{page:Math.max(1,Math.floor(e?.page??a.page)),pageSize:Math.min(a.maxPageSize,Math.max(a.minPageSize,Math.floor(e?.pageSize??a.pageSize)))}}function _(e){return{limit:Math.min(a.maxPageSize,Math.max(a.minPageSize,Math.floor(e?.limit??a.pageSize))),offset:Math.max(0,Math.floor(e?.offset??0))}}function v(e,t){return(e-1)*t}function y(e,t){return t<=0?0:Math.ceil(e/t)}function b(e,t){return e<t}function x(e){return e>1}function S(e,t){let n=Math.max(1,t);return{page:Math.floor(e/n)+1,pageSize:n}}function C(e,t){let n=Math.max(1,e),r=Math.max(1,t);return{offset:(n-1)*r,limit:r}}function w(e,t,n,r){let i=y(t,r);return{items:e,total:t,page:n,pageSize:r,totalPages:i,hasNextPage:b(n,i),hasPreviousPage:x(n)}}function T(e){return typeof e==`object`&&!!e}function E(e){return typeof e==`number`&&Number.isInteger(e)&&e>=1}function D(e){return typeof e==`number`&&Number.isInteger(e)&&e>=a.minPageSize&&e<=a.maxPageSize}function O(e){return typeof e==`number`&&Number.isInteger(e)&&e>=0}function k(e){return typeof e==`number`&&Number.isInteger(e)&&e>=a.minPageSize&&e<=a.maxPageSize}function A(e){if(!T(e))return!1;let t=e.page,n=e.pageSize;return(t===void 0||E(t))&&(n===void 0||D(n))}function j(e){if(!T(e))return!1;let t=e.limit,n=e.offset;return(t===void 0||k(t))&&(n===void 0||O(n))}export{n as A,c as C,i as D,a as E,r as O,u as S,o as T,p as _,D as a,m as b,y as c,x as d,_ as f,h as g,C as h,E as i,t as k,w as l,S as m,O as n,A as o,g as p,j as r,v as s,k as t,b as u,l as v,s as w,f as x,d as y};
@@ -0,0 +1 @@
1
+ let e=require(`zod`);const t=e.z.object({page:e.z.literal(1),pageSize:e.z.literal(20),maxPageSize:e.z.literal(100),minPageSize:e.z.literal(1)}),n=e.z.object({page:e.z.number().int().positive().optional(),pageSize:e.z.number().int().positive().optional()}),r=e.z.object({limit:e.z.number().int().positive().optional(),offset:e.z.number().int().nonnegative().optional()}),i=e.z.object({cursor:e.z.string().optional(),limit:e.z.number().int().positive().optional()}),a={page:1,pageSize:20,maxPageSize:100,minPageSize:1};function o(t){return e.z.object({items:e.z.array(t),total:e.z.number().int().nonnegative(),page:e.z.number().int().positive(),pageSize:e.z.number().int().positive(),totalPages:e.z.number().int().nonnegative(),hasNextPage:e.z.boolean(),hasPreviousPage:e.z.boolean()})}function s(t){return e.z.object({items:e.z.array(t),total:e.z.number().int().nonnegative(),limit:e.z.number().int().nonnegative(),offset:e.z.number().int().nonnegative(),hasMore:e.z.boolean()})}function c(t){return e.z.object({items:e.z.array(t),nextCursor:e.z.string().nullable(),previousCursor:e.z.string().nullable(),hasMore:e.z.boolean()})}const l=e.z.enum([`asc`,`desc`]);function u(t){return e.z.object({sortBy:e.z.enum(t).optional(),sortOrder:l.optional()})}const d=e.z.object({sortBy:e.z.string().optional(),sortOrder:l.optional()});function f(t){return e.z.object({field:e.z.enum(t),order:l})}const p=e.z.object({field:e.z.string(),order:l});function m(t){return e.z.object({sort:e.z.array(f(t)).optional()})}const h=e.z.object({sort:e.z.array(p).optional()});function g(e){return{page:Math.max(1,Math.floor(e?.page??a.page)),pageSize:Math.min(a.maxPageSize,Math.max(a.minPageSize,Math.floor(e?.pageSize??a.pageSize)))}}function _(e){return{limit:Math.min(a.maxPageSize,Math.max(a.minPageSize,Math.floor(e?.limit??a.pageSize))),offset:Math.max(0,Math.floor(e?.offset??0))}}function v(e,t){return(e-1)*t}function y(e,t){return t<=0?0:Math.ceil(e/t)}function b(e,t){return e<t}function x(e){return e>1}function S(e,t){let n=Math.max(1,t);return{page:Math.floor(e/n)+1,pageSize:n}}function C(e,t){let n=Math.max(1,e),r=Math.max(1,t);return{offset:(n-1)*r,limit:r}}function w(e,t,n,r){let i=y(t,r);return{items:e,total:t,page:n,pageSize:r,totalPages:i,hasNextPage:b(n,i),hasPreviousPage:x(n)}}function T(e){return typeof e==`object`&&!!e}function E(e){return typeof e==`number`&&Number.isInteger(e)&&e>=1}function D(e){return typeof e==`number`&&Number.isInteger(e)&&e>=a.minPageSize&&e<=a.maxPageSize}function O(e){return typeof e==`number`&&Number.isInteger(e)&&e>=0}function k(e){return typeof e==`number`&&Number.isInteger(e)&&e>=a.minPageSize&&e<=a.maxPageSize}function A(e){if(!T(e))return!1;let t=e.page,n=e.pageSize;return(t===void 0||E(t))&&(n===void 0||D(n))}function j(e){if(!T(e))return!1;let t=e.limit,n=e.offset;return(t===void 0||k(t))&&(n===void 0||O(n))}Object.defineProperty(exports,`A`,{enumerable:!0,get:function(){return n}}),Object.defineProperty(exports,`C`,{enumerable:!0,get:function(){return c}}),Object.defineProperty(exports,`D`,{enumerable:!0,get:function(){return i}}),Object.defineProperty(exports,`E`,{enumerable:!0,get:function(){return a}}),Object.defineProperty(exports,`O`,{enumerable:!0,get:function(){return r}}),Object.defineProperty(exports,`S`,{enumerable:!0,get:function(){return u}}),Object.defineProperty(exports,`T`,{enumerable:!0,get:function(){return o}}),Object.defineProperty(exports,`_`,{enumerable:!0,get:function(){return p}}),Object.defineProperty(exports,`a`,{enumerable:!0,get:function(){return D}}),Object.defineProperty(exports,`b`,{enumerable:!0,get:function(){return m}}),Object.defineProperty(exports,`c`,{enumerable:!0,get:function(){return y}}),Object.defineProperty(exports,`d`,{enumerable:!0,get:function(){return x}}),Object.defineProperty(exports,`f`,{enumerable:!0,get:function(){return _}}),Object.defineProperty(exports,`g`,{enumerable:!0,get:function(){return h}}),Object.defineProperty(exports,`h`,{enumerable:!0,get:function(){return C}}),Object.defineProperty(exports,`i`,{enumerable:!0,get:function(){return E}}),Object.defineProperty(exports,`k`,{enumerable:!0,get:function(){return t}}),Object.defineProperty(exports,`l`,{enumerable:!0,get:function(){return w}}),Object.defineProperty(exports,`m`,{enumerable:!0,get:function(){return S}}),Object.defineProperty(exports,`n`,{enumerable:!0,get:function(){return O}}),Object.defineProperty(exports,`o`,{enumerable:!0,get:function(){return A}}),Object.defineProperty(exports,`p`,{enumerable:!0,get:function(){return g}}),Object.defineProperty(exports,`r`,{enumerable:!0,get:function(){return j}}),Object.defineProperty(exports,`s`,{enumerable:!0,get:function(){return v}}),Object.defineProperty(exports,`t`,{enumerable:!0,get:function(){return k}}),Object.defineProperty(exports,`u`,{enumerable:!0,get:function(){return b}}),Object.defineProperty(exports,`v`,{enumerable:!0,get:function(){return l}}),Object.defineProperty(exports,`w`,{enumerable:!0,get:function(){return s}}),Object.defineProperty(exports,`x`,{enumerable:!0,get:function(){return f}}),Object.defineProperty(exports,`y`,{enumerable:!0,get:function(){return d}});
@@ -0,0 +1,33 @@
1
+ # 检查清单
2
+
3
+ ## 代码变更
4
+
5
+ - [ ] `src/typeorm.ts` 文件已创建
6
+ - [ ] `src/index.ts` 已移除 TypeORM 相关导出
7
+ - [ ] `src/adapters/index.ts` 已删除
8
+ - [ ] `src/adapters/typeorm.adapter.ts` 保留不变
9
+
10
+ ## 配置变更
11
+
12
+ - [ ] `package.json` exports 字段已添加 `./typeorm`
13
+ - [ ] `tsdown.config.ts` 已配置多入口
14
+
15
+ ## 文档变更
16
+
17
+ - [ ] `README.md` 已更新使用示例
18
+ - [ ] 子路径导入说明已添加
19
+ - [ ] `/docs/user-guides/pagination-user-guide-v1.md` 已更新
20
+ - [ ] TypeORM 适配器导入路径已修改为子路径方式
21
+
22
+ ## 测试验证
23
+
24
+ - [ ] 单元测试通过
25
+ - [ ] 类型检查通过
26
+ - [ ] 构建成功
27
+ - [ ] Lint 检查通过
28
+
29
+ ## 导出验证
30
+
31
+ - [ ] 主入口 `@longzai-intelligence/pagination` 只导出核心功能
32
+ - [ ] 子路径 `@longzai-intelligence/pagination/typeorm` 导出 TypeORM 适配器
33
+ - [ ] 类型定义正确生成
@@ -0,0 +1,104 @@
1
+ # TypeORM 适配器子路径导出规格
2
+
3
+ ## 背景
4
+
5
+ 当前 `@longzai-intelligence/pagination` 包将 TypeORM 适配器直接从主入口导出,这导致:
6
+
7
+ 1. 主入口 `index.ts` 被 TypeORM 特定代码污染
8
+ 2. 不使用 TypeORM 的用户也会引入 TypeORM 相关类型
9
+ 3. 违反了可选依赖的最佳实践
10
+
11
+ ## 目标
12
+
13
+ 将 TypeORM 适配器改为子路径导出,实现:
14
+
15
+ 1. 主入口保持纯净,只导出分页核心功能
16
+ 2. TypeORM 适配器通过 `@longzai-intelligence/pagination/typeorm` 单独导入
17
+ 3. 用户按需导入,减少不必要的依赖感知
18
+
19
+ ## 导出结构变更
20
+
21
+ ### 变更前
22
+
23
+ ```typescript
24
+ // 主入口 index.ts 同时导出核心功能和 TypeORM 适配器
25
+ export { paginateWithRepository, paginateWithQueryBuilder } from './adapters';
26
+ ```
27
+
28
+ ### 变更后
29
+
30
+ ```typescript
31
+ // 主入口 index.ts - 只导出核心功能
32
+ export { normalizePagination, calculateOffset, ... } from './utils';
33
+ export type { PaginationParams, PaginatedResult, ... } from './types';
34
+
35
+ // 新增 typeorm.ts 入口 - 单独导出 TypeORM 适配器
36
+ export { paginateWithRepository, paginateWithQueryBuilder, createPaginationQueryBuilder } from './adapters/typeorm.adapter';
37
+ export type { EntityMapper, TypeOrmPaginateOptions } from './adapters/typeorm.adapter';
38
+ ```
39
+
40
+ ## package.json exports 配置
41
+
42
+ ```json
43
+ {
44
+ "exports": {
45
+ ".": {
46
+ "types": {
47
+ "import": "./dist/index.d.mts",
48
+ "require": "./dist/index.d.cts"
49
+ },
50
+ "import": "./dist/index.mjs",
51
+ "require": "./dist/index.cjs"
52
+ },
53
+ "./typeorm": {
54
+ "types": {
55
+ "import": "./dist/typeorm.d.mts",
56
+ "require": "./dist/typeorm.d.cts"
57
+ },
58
+ "import": "./dist/typeorm.mjs",
59
+ "require": "./dist/typeorm.cjs"
60
+ }
61
+ }
62
+ }
63
+ ```
64
+
65
+ ## 使用方式变更
66
+
67
+ ### 变更前
68
+
69
+ ```typescript
70
+ import { paginateWithRepository, type PaginationParams } from '@longzai-intelligence/pagination';
71
+ ```
72
+
73
+ ### 变更后
74
+
75
+ ```typescript
76
+ // 核心功能 - 从主入口导入
77
+ import { type PaginationParams, type PaginatedResult } from '@longzai-intelligence/pagination';
78
+
79
+ // TypeORM 适配器 - 从子路径导入
80
+ import { paginateWithRepository } from '@longzai-intelligence/pagination/typeorm';
81
+ ```
82
+
83
+ ## 文件变更清单
84
+
85
+ | 文件 | 操作 | 说明 |
86
+ |------|------|------|
87
+ | `src/index.ts` | 修改 | 移除 TypeORM 适配器导出 |
88
+ | `src/typeorm.ts` | 新增 | TypeORM 适配器独立入口 |
89
+ | `src/adapters/index.ts` | 删除 | 不再需要适配器聚合入口 |
90
+ | `package.json` | 修改 | 添加 `./typeorm` 子路径导出 |
91
+ | `tsdown.config.ts` | 修改 | 添加 typeorm 入口配置 |
92
+ | `README.md` | 修改 | 更新使用文档 |
93
+ | `docs/user-guides/pagination-user-guide-v1.md` | 修改 | 更新 TypeORM 适配器导入路径 |
94
+
95
+ ## 影响范围
96
+
97
+ 1. 现有使用 `@longzai-intelligence/pagination` 导入 TypeORM 适配器的代码需要修改导入路径
98
+ 2. 仅使用核心功能的代码无需修改
99
+
100
+ ## 风险评估
101
+
102
+ - **破坏性变更**: 是,导入路径变更
103
+ - **向后兼容**: 否,需要用户更新导入路径
104
+ - **迁移成本**: 低,仅需修改导入语句
@@ -0,0 +1,48 @@
1
+ # 任务列表
2
+
3
+ ## 1. 创建 TypeORM 独立入口文件
4
+
5
+ - [ ] 创建 `src/typeorm.ts` 文件
6
+ - [ ] 从 `./adapters/typeorm.adapter` 导出所有 TypeORM 相关功能和类型
7
+
8
+ ## 2. 修改主入口文件
9
+
10
+ - [ ] 从 `src/index.ts` 移除 TypeORM 适配器相关导出
11
+ - [ ] 确保核心功能导出完整
12
+
13
+ ## 3. 清理适配器目录
14
+
15
+ - [ ] 删除 `src/adapters/index.ts` 文件(不再需要)
16
+ - [ ] 保留 `src/adapters/typeorm.adapter.ts` 文件
17
+
18
+ ## 4. 更新 package.json
19
+
20
+ - [ ] 在 `exports` 字段添加 `./typeorm` 子路径配置
21
+ - [ ] 确保类型定义文件路径正确
22
+
23
+ ## 5. 更新构建配置
24
+
25
+ - [ ] 修改 `tsdown.config.ts` 添加多入口配置
26
+ - [ ] 确保 typeorm 入口正确构建
27
+
28
+ ## 6. 更新文档
29
+
30
+ - [ ] 更新 `README.md` 使用示例
31
+ - [ ] 添加子路径导入说明
32
+
33
+ ## 7. 更新用户指南
34
+
35
+ - [ ] 更新 `/docs/user-guides/pagination-user-guide-v1.md`
36
+ - [ ] 修改 TypeORM 适配器导入路径为子路径方式
37
+ - [ ] 更新所有相关代码示例
38
+
39
+ ## 8. 更新测试
40
+
41
+ - [ ] 检查并更新测试文件中的导入路径
42
+ - [ ] 确保测试通过
43
+
44
+ ## 9. 验证构建
45
+
46
+ - [ ] 运行 `bun run build` 验证构建成功
47
+ - [ ] 运行 `bun run typecheck` 验证类型正确
48
+ - [ ] 运行 `bun run lint:fix` 验证代码规范
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@longzai-intelligence/pagination",
3
- "version": "0.0.1",
3
+ "version": "0.0.2",
4
4
  "type": "module",
5
5
  "main": "./dist/index.cjs",
6
6
  "types": "./dist/index.d.mts",
@@ -12,6 +12,14 @@
12
12
  },
13
13
  "import": "./dist/index.mjs",
14
14
  "require": "./dist/index.cjs"
15
+ },
16
+ "./typeorm": {
17
+ "types": {
18
+ "import": "./dist/typeorm.d.mts",
19
+ "require": "./dist/typeorm.d.cts"
20
+ },
21
+ "import": "./dist/typeorm.mjs",
22
+ "require": "./dist/typeorm.cjs"
15
23
  }
16
24
  },
17
25
  "scripts": {
@@ -32,11 +40,15 @@
32
40
  "upgrade-deps": "ncu -u"
33
41
  },
34
42
  "peerDependencies": {
35
- "typeorm": "^0.3.0"
43
+ "typeorm": "^0.3.0",
44
+ "zod": "^4.0.0"
36
45
  },
37
46
  "peerDependenciesMeta": {
38
47
  "typeorm": {
39
48
  "optional": true
49
+ },
50
+ "zod": {
51
+ "optional": true
40
52
  }
41
53
  },
42
54
  "devDependencies": {
@@ -47,7 +59,7 @@
47
59
  "typescript": "^6.0.2",
48
60
  "vitest": "^4.1.4",
49
61
  "@longzai-intelligence/eslint-preset-library": "0.0.5",
50
- "@longzai-intelligence/vitest-config": "0.0.5",
62
+ "@longzai-intelligence/vitest-config": "0.0.7",
51
63
  "@longzai-intelligence/typescript-config": "0.0.3",
52
64
  "tsdown": "^0.21.7",
53
65
  "@longzai-intelligence/tsdown-config": "0.0.1"
@@ -1,7 +1,5 @@
1
1
  /**
2
- * @file index.test.ts
3
2
  * 主入口导出测试
4
- * @author Longzai Intelligence
5
3
  */
6
4
 
7
5
  import { describe, it, expect } from 'vitest';
@@ -42,9 +40,17 @@ describe('pagination 包导出', () => {
42
40
  expect(module.isValidOffsetPagination).toBeDefined();
43
41
  });
44
42
 
45
- it('应正确导出所有适配器', async () => {
43
+ it('主入口不应导出 TypeORM 适配器', async () => {
46
44
  const module = await import('@/index');
47
45
 
46
+ expect('paginateWithRepository' in module).toBe(false);
47
+ expect('paginateWithQueryBuilder' in module).toBe(false);
48
+ expect('createPaginationQueryBuilder' in module).toBe(false);
49
+ });
50
+
51
+ it('typeorm 子路径应正确导出适配器', async () => {
52
+ const module = await import('@/typeorm');
53
+
48
54
  expect(module.paginateWithRepository).toBeDefined();
49
55
  expect(module.paginateWithQueryBuilder).toBeDefined();
50
56
  expect(module.createPaginationQueryBuilder).toBeDefined();
@@ -1,7 +1,5 @@
1
1
  /**
2
- * @file pagination.util.test.ts
3
2
  * 分页计算工具函数测试
4
- * @author Longzai Intelligence
5
3
  */
6
4
 
7
5
  import { describe, it, expect } from 'vitest';
@@ -0,0 +1,142 @@
1
+ /**
2
+ * 分页结果 Schema 测试
3
+ */
4
+
5
+ import { describe, it, expect } from 'vitest';
6
+ import { z } from 'zod';
7
+ import {
8
+ createPaginatedResultSchema,
9
+ createOffsetPaginatedResultSchema,
10
+ createCursorPaginatedResultSchema,
11
+ } from '@/schemas/result.schemas';
12
+
13
+ /**
14
+ * 测试实体 Schema
15
+ */
16
+ const TestItemSchema = z.object({
17
+ id: z.number(),
18
+ name: z.string(),
19
+ });
20
+
21
+ describe('createPaginatedResultSchema', () => {
22
+ it('应正确验证有效的分页结果', () => {
23
+ const schema = createPaginatedResultSchema(TestItemSchema);
24
+ const result = schema.parse({
25
+ items: [{ id: 1, name: 'test' }],
26
+ total: 100,
27
+ page: 1,
28
+ pageSize: 20,
29
+ totalPages: 5,
30
+ hasNextPage: true,
31
+ hasPreviousPage: false,
32
+ });
33
+
34
+ expect(result.items).toHaveLength(1);
35
+ expect(result.total).toBe(100);
36
+ expect(result.page).toBe(1);
37
+ });
38
+
39
+ it('应拒绝无效的分页结果', () => {
40
+ const schema = createPaginatedResultSchema(TestItemSchema);
41
+
42
+ expect(() =>
43
+ schema.parse({
44
+ items: [{ id: 'invalid', name: 'test' }],
45
+ total: 100,
46
+ page: 1,
47
+ pageSize: 20,
48
+ totalPages: 5,
49
+ hasNextPage: true,
50
+ hasPreviousPage: false,
51
+ }),
52
+ ).toThrow();
53
+ });
54
+
55
+ it('应拒绝缺少必需字段的分页结果', () => {
56
+ const schema = createPaginatedResultSchema(TestItemSchema);
57
+
58
+ expect(() =>
59
+ schema.parse({
60
+ items: [{ id: 1, name: 'test' }],
61
+ total: 100,
62
+ }),
63
+ ).toThrow();
64
+ });
65
+ });
66
+
67
+ describe('createOffsetPaginatedResultSchema', () => {
68
+ it('应正确验证有效的 Offset 分页结果', () => {
69
+ const schema = createOffsetPaginatedResultSchema(TestItemSchema);
70
+ const result = schema.parse({
71
+ items: [{ id: 1, name: 'test' }],
72
+ total: 100,
73
+ limit: 20,
74
+ offset: 0,
75
+ hasMore: true,
76
+ });
77
+
78
+ expect(result.items).toHaveLength(1);
79
+ expect(result.total).toBe(100);
80
+ expect(result.limit).toBe(20);
81
+ expect(result.offset).toBe(0);
82
+ expect(result.hasMore).toBe(true);
83
+ });
84
+
85
+ it('应拒绝无效的 Offset 分页结果', () => {
86
+ const schema = createOffsetPaginatedResultSchema(TestItemSchema);
87
+
88
+ expect(() =>
89
+ schema.parse({
90
+ items: [{ id: 1, name: 'test' }],
91
+ total: -1,
92
+ limit: 20,
93
+ offset: 0,
94
+ hasMore: true,
95
+ }),
96
+ ).toThrow();
97
+ });
98
+ });
99
+
100
+ describe('createCursorPaginatedResultSchema', () => {
101
+ it('应正确验证有效的游标分页结果', () => {
102
+ const schema = createCursorPaginatedResultSchema(TestItemSchema);
103
+ const result = schema.parse({
104
+ items: [{ id: 1, name: 'test' }],
105
+ nextCursor: 'cursor123',
106
+ previousCursor: null,
107
+ hasMore: true,
108
+ });
109
+
110
+ expect(result.items).toHaveLength(1);
111
+ expect(result.nextCursor).toBe('cursor123');
112
+ expect(result.previousCursor).toBeNull();
113
+ expect(result.hasMore).toBe(true);
114
+ });
115
+
116
+ it('应支持 null 游标', () => {
117
+ const schema = createCursorPaginatedResultSchema(TestItemSchema);
118
+ const result = schema.parse({
119
+ items: [],
120
+ nextCursor: null,
121
+ previousCursor: null,
122
+ hasMore: false,
123
+ });
124
+
125
+ expect(result.items).toHaveLength(0);
126
+ expect(result.nextCursor).toBeNull();
127
+ expect(result.previousCursor).toBeNull();
128
+ });
129
+
130
+ it('应拒绝无效的游标分页结果', () => {
131
+ const schema = createCursorPaginatedResultSchema(TestItemSchema);
132
+
133
+ expect(() =>
134
+ schema.parse({
135
+ items: [{ id: 1, name: 'test' }],
136
+ nextCursor: 123,
137
+ previousCursor: null,
138
+ hasMore: true,
139
+ }),
140
+ ).toThrow();
141
+ });
142
+ });
@@ -0,0 +1,157 @@
1
+ /**
2
+ * 排序参数 Schema 测试
3
+ */
4
+
5
+ import { describe, it, expect } from 'vitest';
6
+ import {
7
+ SortOrderSchema,
8
+ createSortParamsSchema,
9
+ SortParamsSchema,
10
+ createSortItemSchema,
11
+ SortItemSchema,
12
+ createMultiSortParamsSchema,
13
+ MultiSortParamsSchema,
14
+ } from '@/schemas/sort.schemas';
15
+
16
+ describe('SortOrderSchema', () => {
17
+ it('应接受有效的排序方向', () => {
18
+ expect(SortOrderSchema.parse('asc')).toBe('asc');
19
+ expect(SortOrderSchema.parse('desc')).toBe('desc');
20
+ });
21
+
22
+ it('应拒绝无效的排序方向', () => {
23
+ expect(() => SortOrderSchema.parse('invalid')).toThrow();
24
+ });
25
+ });
26
+
27
+ describe('createSortParamsSchema', () => {
28
+ it('应正确验证有效的排序参数', () => {
29
+ const schema = createSortParamsSchema(['name', 'createdAt', 'updatedAt'] as const);
30
+ const result = schema.parse({
31
+ sortBy: 'name',
32
+ sortOrder: 'asc',
33
+ });
34
+
35
+ expect(result.sortBy).toBe('name');
36
+ expect(result.sortOrder).toBe('asc');
37
+ });
38
+
39
+ it('应支持可选的排序参数', () => {
40
+ const schema = createSortParamsSchema(['name', 'createdAt'] as const);
41
+ const result = schema.parse({});
42
+
43
+ expect(result.sortBy).toBeUndefined();
44
+ expect(result.sortOrder).toBeUndefined();
45
+ });
46
+
47
+ it('应拒绝无效的排序字段', () => {
48
+ const schema = createSortParamsSchema(['name', 'createdAt'] as const);
49
+
50
+ expect(() =>
51
+ schema.parse({
52
+ sortBy: 'invalid',
53
+ sortOrder: 'asc',
54
+ }),
55
+ ).toThrow();
56
+ });
57
+ });
58
+
59
+ describe('SortParamsSchema', () => {
60
+ it('应正确验证默认排序参数', () => {
61
+ const result = SortParamsSchema.parse({
62
+ sortBy: 'anyField',
63
+ sortOrder: 'desc',
64
+ });
65
+
66
+ expect(result.sortBy).toBe('anyField');
67
+ expect(result.sortOrder).toBe('desc');
68
+ });
69
+ });
70
+
71
+ describe('createSortItemSchema', () => {
72
+ it('应正确验证有效的排序项', () => {
73
+ const schema = createSortItemSchema(['name', 'createdAt'] as const);
74
+ const result = schema.parse({
75
+ field: 'name',
76
+ order: 'desc',
77
+ });
78
+
79
+ expect(result.field).toBe('name');
80
+ expect(result.order).toBe('desc');
81
+ });
82
+
83
+ it('应拒绝无效的排序字段', () => {
84
+ const schema = createSortItemSchema(['name', 'createdAt'] as const);
85
+
86
+ expect(() =>
87
+ schema.parse({
88
+ field: 'invalid',
89
+ order: 'asc',
90
+ }),
91
+ ).toThrow();
92
+ });
93
+
94
+ it('应拒绝缺少必需字段', () => {
95
+ const schema = createSortItemSchema(['name', 'createdAt'] as const);
96
+
97
+ expect(() => schema.parse({ field: 'name' })).toThrow();
98
+ });
99
+ });
100
+
101
+ describe('SortItemSchema', () => {
102
+ it('应正确验证默认排序项', () => {
103
+ const result = SortItemSchema.parse({
104
+ field: 'anyField',
105
+ order: 'asc',
106
+ });
107
+
108
+ expect(result.field).toBe('anyField');
109
+ expect(result.order).toBe('asc');
110
+ });
111
+ });
112
+
113
+ describe('createMultiSortParamsSchema', () => {
114
+ it('应正确验证有效的多字段排序参数', () => {
115
+ const schema = createMultiSortParamsSchema(['name', 'createdAt', 'updatedAt'] as const);
116
+ const result = schema.parse({
117
+ sort: [
118
+ { field: 'name', order: 'asc' },
119
+ { field: 'createdAt', order: 'desc' },
120
+ ],
121
+ });
122
+
123
+ expect(result.sort).toHaveLength(2);
124
+ expect(result.sort?.[0]?.field).toBe('name');
125
+ expect(result.sort?.[1]?.field).toBe('createdAt');
126
+ });
127
+
128
+ it('应支持可选的排序参数', () => {
129
+ const schema = createMultiSortParamsSchema(['name', 'createdAt'] as const);
130
+ const result = schema.parse({});
131
+
132
+ expect(result.sort).toBeUndefined();
133
+ });
134
+
135
+ it('应拒绝无效的排序字段', () => {
136
+ const schema = createMultiSortParamsSchema(['name', 'createdAt'] as const);
137
+
138
+ expect(() =>
139
+ schema.parse({
140
+ sort: [{ field: 'invalid', order: 'asc' }],
141
+ }),
142
+ ).toThrow();
143
+ });
144
+ });
145
+
146
+ describe('MultiSortParamsSchema', () => {
147
+ it('应正确验证默认多字段排序参数', () => {
148
+ const result = MultiSortParamsSchema.parse({
149
+ sort: [
150
+ { field: 'field1', order: 'asc' },
151
+ { field: 'field2', order: 'desc' },
152
+ ],
153
+ });
154
+
155
+ expect(result.sort).toHaveLength(2);
156
+ });
157
+ });
@@ -1,7 +1,5 @@
1
1
  /**
2
- * @file typeorm.adapter.test.ts
3
2
  * TypeORM 适配器测试
4
- * @author Longzai Intelligence
5
3
  */
6
4
 
7
5
  import { describe, it, expect, vi } from 'vitest';
@@ -9,7 +7,7 @@ import {
9
7
  paginateWithRepository,
10
8
  paginateWithQueryBuilder,
11
9
  createPaginationQueryBuilder,
12
- } from '@/adapters/typeorm.adapter';
10
+ } from '@/adapters/typeorm';
13
11
  import type { Repository, SelectQueryBuilder, FindOptionsOrder } from 'typeorm';
14
12
 
15
13
  /**
@@ -1,7 +1,5 @@
1
1
  /**
2
- * @file validation.util.test.ts
3
2
  * 分页参数验证工具函数测试
4
- * @author Longzai Intelligence
5
3
  */
6
4
 
7
5
  import { describe, it, expect } from 'vitest';