@mx-space/api-client 2.1.1 → 2.2.0

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.
@@ -56,9 +56,8 @@ const axiosAdaptor = Object.preventExtensions({
56
56
  return $http.patch(url, data, config);
57
57
  }
58
58
  });
59
- var axios_default = axiosAdaptor;
60
59
 
61
60
  //#endregion
62
61
  exports.__toESM = __toESM;
63
62
  exports.axiosAdaptor = axiosAdaptor;
64
- exports.default = axios_default;
63
+ exports.default = axiosAdaptor;
@@ -27,7 +27,6 @@ const axiosAdaptor = Object.preventExtensions({
27
27
  return $http.patch(url, data, config);
28
28
  }
29
29
  });
30
- var axios_default = axiosAdaptor;
31
30
 
32
31
  //#endregion
33
- export { axiosAdaptor, axios_default as default };
32
+ export { axiosAdaptor, axiosAdaptor as default };
@@ -29,8 +29,7 @@ const umiAdaptor = Object.preventExtensions({
29
29
  return $http.patch(url, options);
30
30
  }
31
31
  });
32
- var umi_request_default = umiAdaptor;
33
32
 
34
33
  //#endregion
35
- exports.default = umi_request_default;
34
+ exports.default = umiAdaptor;
36
35
  exports.umiAdaptor = umiAdaptor;
@@ -27,7 +27,6 @@ const umiAdaptor = Object.preventExtensions({
27
27
  return $http.patch(url, options);
28
28
  }
29
29
  });
30
- var umi_request_default = umiAdaptor;
31
30
 
32
31
  //#endregion
33
- export { umi_request_default as default, umiAdaptor };
32
+ export { umiAdaptor as default, umiAdaptor };
package/dist/index.cjs CHANGED
@@ -201,6 +201,14 @@ var AggregateController = class {
201
201
  year
202
202
  } });
203
203
  }
204
+ getLatest(options) {
205
+ const { limit, types, combined } = options || {};
206
+ return this.proxy.latest.get({ params: {
207
+ limit,
208
+ types: types?.join(","),
209
+ combined
210
+ } });
211
+ }
204
212
  /**
205
213
  * 获取聚合数据统计
206
214
  */
@@ -414,6 +422,9 @@ var CommentController = class {
414
422
  size: size || 10
415
423
  } });
416
424
  }
425
+ getThreadReplies(rootCommentId, params = {}) {
426
+ return this.proxy.thread(rootCommentId).get({ params });
427
+ }
417
428
  /**
418
429
  * 评论
419
430
  */
@@ -517,18 +528,29 @@ var NoteController = class {
517
528
  prefer
518
529
  } });
519
530
  }
531
+ getNoteBySlugDate(year, month, day, slug, options) {
532
+ const { password, single, lang, prefer } = options || {};
533
+ return this.proxy(year.toString())(month.toString())(day.toString())(slug).get({ params: {
534
+ password,
535
+ single: single ? "1" : void 0,
536
+ lang,
537
+ prefer
538
+ } });
539
+ }
520
540
  /**
521
541
  * 日记列表分页
522
542
  */
523
543
  getList(page = 1, perPage = 10, options = {}) {
524
- const { select, sortBy, sortOrder, year } = options;
544
+ const { select, sortBy, sortOrder, year, lang, withSummary } = options;
525
545
  return this.proxy.get({ params: {
526
546
  page,
527
547
  size: perPage,
528
548
  select: select?.join(" "),
529
549
  sortBy,
530
550
  sortOrder,
531
- year
551
+ year,
552
+ lang,
553
+ withSummary: withSummary ? "1" : void 0
532
554
  } });
533
555
  }
534
556
  /**
package/dist/index.d.cts CHANGED
@@ -376,6 +376,7 @@ type NoteModel = TextBaseModel & {
376
376
  publicAt?: Date;
377
377
  password?: string | null;
378
378
  nid: number;
379
+ slug?: string;
379
380
  location?: string;
380
381
  coordinates?: Coordinate;
381
382
  topic?: TopicModel;
@@ -622,6 +623,19 @@ interface TimelineData {
622
623
  url: string;
623
624
  })[];
624
625
  }
626
+ interface LatestPostItem extends Pick<PostModel, 'id' | 'title' | 'slug' | 'created' | 'modified' | 'tags'> {
627
+ category: Pick<CategoryModel, 'name' | 'slug'> | null;
628
+ }
629
+ interface LatestNoteItem extends Pick<NoteModel, 'id' | 'title' | 'nid' | 'created' | 'modified' | 'mood' | 'weather' | 'bookmark'> {}
630
+ interface LatestData {
631
+ posts?: LatestPostItem[];
632
+ notes?: LatestNoteItem[];
633
+ }
634
+ type LatestCombinedItem = (LatestPostItem & {
635
+ type: 'post';
636
+ }) | (LatestNoteItem & {
637
+ type: 'note';
638
+ });
625
639
  interface AggregateStat {
626
640
  allComments: number;
627
641
  categories: number;
@@ -717,24 +731,44 @@ interface CommentModel extends BaseModel {
717
731
  refType: CollectionRefTypes;
718
732
  ref: string;
719
733
  state: number;
720
- commentsIndex: number;
721
734
  author: string;
722
735
  text: string;
723
736
  mail?: string;
724
737
  url?: string;
725
738
  ip?: string;
726
739
  agent?: string;
727
- key: string;
728
740
  pin?: boolean;
729
741
  avatar: string;
730
- parent?: CommentModel | string;
731
- children: CommentModel[];
742
+ parentCommentId?: string | null;
743
+ rootCommentId?: string | null;
744
+ replyCount?: number;
745
+ latestReplyAt?: string | null;
746
+ isDeleted?: boolean;
747
+ deletedAt?: string;
732
748
  isWhispers?: boolean;
733
749
  location?: string;
734
750
  source?: string;
735
751
  readerId?: string;
736
752
  editedAt?: string;
737
753
  }
754
+ interface CommentReplyWindow {
755
+ total: number;
756
+ returned: number;
757
+ threshold: number;
758
+ hasHidden: boolean;
759
+ hiddenCount: number;
760
+ nextCursor?: string;
761
+ }
762
+ interface CommentThreadItem extends CommentModel {
763
+ replies: CommentModel[];
764
+ replyWindow: CommentReplyWindow;
765
+ }
766
+ interface CommentThreadReplies {
767
+ replies: CommentModel[];
768
+ nextCursor?: string;
769
+ remaining: number;
770
+ done: boolean;
771
+ }
738
772
  interface CommentRef {
739
773
  id: string;
740
774
  categoryId?: string;
@@ -1050,6 +1084,16 @@ declare class AggregateController<ResponseWrapper> implements IController {
1050
1084
  data: TimelineData;
1051
1085
  };
1052
1086
  }>;
1087
+ getLatest(options: {
1088
+ limit?: number;
1089
+ types?: TimelineType[];
1090
+ combined: true;
1091
+ }): RequestProxyResult<LatestCombinedItem[], ResponseWrapper>;
1092
+ getLatest(options?: {
1093
+ limit?: number;
1094
+ types?: TimelineType[];
1095
+ combined?: false;
1096
+ }): RequestProxyResult<LatestData, ResponseWrapper>;
1053
1097
  /**
1054
1098
  * 获取聚合数据统计
1055
1099
  */
@@ -1571,30 +1615,40 @@ declare class CommentController<ResponseWrapper> implements IController {
1571
1615
  * 获取文章的评论列表
1572
1616
  * @param refId 文章 Id
1573
1617
  */
1574
- getByRefId(refId: string, pagination?: PaginationParams): RequestProxyResult<PaginateResult<CommentModel & {
1618
+ getByRefId(refId: string, pagination?: PaginationParams): RequestProxyResult<PaginateResult<CommentThreadItem & {
1575
1619
  ref: string;
1576
1620
  }> & {
1577
1621
  readers: Record<string, ReaderModel>;
1578
1622
  }, ResponseWrapper, ResponseWrapper extends unknown ? {
1579
1623
  [key: string]: any;
1580
- data: PaginateResult<CommentModel & {
1624
+ data: PaginateResult<CommentThreadItem & {
1581
1625
  ref: string;
1582
1626
  }> & {
1583
1627
  readers: Record<string, ReaderModel>;
1584
1628
  };
1585
1629
  } : ResponseWrapper extends {
1586
- data: PaginateResult<CommentModel & {
1630
+ data: PaginateResult<CommentThreadItem & {
1587
1631
  ref: string;
1588
1632
  }> & {
1589
1633
  readers: Record<string, ReaderModel>;
1590
1634
  };
1591
1635
  } ? ResponseWrapper : Omit<ResponseWrapper, "data"> & {
1592
- data: PaginateResult<CommentModel & {
1636
+ data: PaginateResult<CommentThreadItem & {
1593
1637
  ref: string;
1594
1638
  }> & {
1595
1639
  readers: Record<string, ReaderModel>;
1596
1640
  };
1597
1641
  }>;
1642
+ getThreadReplies(rootCommentId: string, params?: PaginationParams & {
1643
+ cursor?: string;
1644
+ }): RequestProxyResult<CommentThreadReplies, ResponseWrapper, ResponseWrapper extends unknown ? {
1645
+ [key: string]: any;
1646
+ data: CommentThreadReplies;
1647
+ } : ResponseWrapper extends {
1648
+ data: CommentThreadReplies;
1649
+ } ? ResponseWrapper : Omit<ResponseWrapper, "data"> & {
1650
+ data: CommentThreadReplies;
1651
+ }>;
1598
1652
  /**
1599
1653
  * 评论
1600
1654
  */
@@ -1690,6 +1744,8 @@ type NoteListOptions = {
1690
1744
  year?: number;
1691
1745
  sortBy?: 'weather' | 'mood' | 'title' | 'created' | 'modified';
1692
1746
  sortOrder?: 1 | -1;
1747
+ lang?: string;
1748
+ withSummary?: boolean;
1693
1749
  };
1694
1750
  type NoteByNidOptions = {
1695
1751
  password?: string;
@@ -1697,13 +1753,14 @@ type NoteByNidOptions = {
1697
1753
  lang?: string;
1698
1754
  prefer?: 'lexical';
1699
1755
  };
1756
+ type NoteBySlugDateOptions = NoteByNidOptions;
1700
1757
  type NoteMiddleListOptions = {
1701
1758
  lang?: string;
1702
1759
  };
1703
1760
  type NoteTopicListOptions = SortOptions & {
1704
1761
  lang?: string;
1705
1762
  };
1706
- type NoteTimelineItem = Pick<NoteModel, 'id' | 'title' | 'nid' | 'created' | 'isPublished'> & {
1763
+ type NoteTimelineItem = Pick<NoteModel, 'id' | 'title' | 'nid' | 'slug' | 'created' | 'isPublished'> & {
1707
1764
  isTranslated?: boolean;
1708
1765
  translationMeta?: TranslationMeta;
1709
1766
  };
@@ -1744,6 +1801,7 @@ declare class NoteController<ResponseWrapper> implements IController {
1744
1801
  * @param options 可选参数:password, single, lang
1745
1802
  */
1746
1803
  getNoteByNid(nid: number, options?: NoteByNidOptions): RequestProxyResult<NoteWrappedWithLikedAndTranslationPayload, ResponseWrapper>;
1804
+ getNoteBySlugDate(year: number, month: number, day: number, slug: string, options?: NoteBySlugDateOptions): RequestProxyResult<NoteWrappedWithLikedAndTranslationPayload, ResponseWrapper>;
1747
1805
  /**
1748
1806
  * 日记列表分页
1749
1807
  */
@@ -2500,4 +2558,4 @@ declare const allControllerNames: readonly ["ai", "ack", "activity", "aggregate"
2500
2558
  */
2501
2559
  declare const camelcaseKeys: <T = any>(obj: any) => T;
2502
2560
  //#endregion
2503
- export { AIController, AIDeepReadingModel, AISummaryModel, AISummaryStreamEvent, AITranslationModel, AITranslationStreamEvent, AckController, ActivityController, ActivityPresence, AdminExtraModel, AggregateAIConfig, AggregateController, AggregateRoot, AggregateRootWithTheme, AggregateStat, AggregateTop, AggregateTopNote, AggregateTopPost, AlgoliaSearchOptionsModel, AuthUser, BackupOptionsModel, BaiduSearchOptionsModel, BaseCommentIndexModel, BaseModel, BetterAuthSession, BetterAuthSessionResult, BetterAuthSignInResult, BetterAuthUser, BetterAuthUserRole, BingSearchOptionsModel, CategoryController, CategoryEntries, CategoryModel, CategoryType, CategoryWithChildrenModel, CheckLoggedResult, CollectionRefTypes, CommentController, CommentDto, CommentModel, CommentOptionsModel, CommentRef, CommentState, Coordinate, Count, EnumPageType, type HTTPClient, IConfig, IConfigKeys, type IRequestAdapter, Image, LastYearPublication, LinkController, LinkModel, LinkState, LinkType, MailOptionsModel, ModelWithLiked, ModelWithTranslation, NoteController, type NoteMiddleListOptions, NoteModel, type NoteTimelineItem, type NoteTopicListItem, type NoteTopicListOptions, NoteWrappedPayload, NoteWrappedWithLikedAndTranslationPayload, NoteWrappedWithLikedPayload, OwnerAllowLoginResult, OwnerSessionResult, PageController, PageModel, Pager, PaginateResult, PostController, type PostListItem, type PostListOptions, PostModel, ProjectController, ProjectModel, ReaderModel, RecentActivities, RecentComment, RecentLike, RecentNote, RecentPost, RecentRecent, RecentlyAttitudeEnum, RecentlyAttitudeResultEnum, RecentlyController, RecentlyModel, RecentlyRefType, RecentlyRefTypes, RequestError, RoomOmittedNote, RoomOmittedPage, RoomOmittedPost, RoomsData, SayController, SayModel, SearchController, SeoOptionModel, ServerlessController, SnippetController, SnippetModel, SnippetType, SubscribeAllBit, SubscribeController, SubscribeNoteCreateBit, SubscribePostCreateBit, SubscribeRecentCreateBit, SubscribeSayCreateBit, SubscribeType, SubscribeTypeToBitMap, TLogin, TagModel, TextBaseModel, TextBaseModelLexical, TextBaseModelMarkdown, ThirdPartyServiceIntegrationModel, TimelineData, TimelineType, TopicController, TopicModel, TranslationMeta, Url, UrlOptionModel, UserController, UserModel, allControllerNames, allControllers, createClient, createClient as default, camelcaseKeys as simpleCamelcaseKeys };
2561
+ export { AIController, AIDeepReadingModel, AISummaryModel, AISummaryStreamEvent, AITranslationModel, AITranslationStreamEvent, AckController, ActivityController, ActivityPresence, AdminExtraModel, AggregateAIConfig, AggregateController, AggregateRoot, AggregateRootWithTheme, AggregateStat, AggregateTop, AggregateTopNote, AggregateTopPost, AlgoliaSearchOptionsModel, AuthUser, BackupOptionsModel, BaiduSearchOptionsModel, BaseCommentIndexModel, BaseModel, BetterAuthSession, BetterAuthSessionResult, BetterAuthSignInResult, BetterAuthUser, BetterAuthUserRole, BingSearchOptionsModel, CategoryController, CategoryEntries, CategoryModel, CategoryType, CategoryWithChildrenModel, CheckLoggedResult, CollectionRefTypes, CommentController, CommentDto, CommentModel, CommentOptionsModel, CommentRef, CommentReplyWindow, CommentState, CommentThreadItem, CommentThreadReplies, Coordinate, Count, EnumPageType, type HTTPClient, IConfig, IConfigKeys, type IRequestAdapter, Image, LastYearPublication, LatestCombinedItem, LatestData, LatestNoteItem, LatestPostItem, LinkController, LinkModel, LinkState, LinkType, MailOptionsModel, ModelWithLiked, ModelWithTranslation, NoteController, type NoteMiddleListOptions, NoteModel, type NoteTimelineItem, type NoteTopicListItem, type NoteTopicListOptions, NoteWrappedPayload, NoteWrappedWithLikedAndTranslationPayload, NoteWrappedWithLikedPayload, OwnerAllowLoginResult, OwnerSessionResult, PageController, PageModel, Pager, PaginateResult, PostController, type PostListItem, type PostListOptions, PostModel, ProjectController, ProjectModel, ReaderModel, RecentActivities, RecentComment, RecentLike, RecentNote, RecentPost, RecentRecent, RecentlyAttitudeEnum, RecentlyAttitudeResultEnum, RecentlyController, RecentlyModel, RecentlyRefType, RecentlyRefTypes, RequestError, RoomOmittedNote, RoomOmittedPage, RoomOmittedPost, RoomsData, SayController, SayModel, SearchController, SeoOptionModel, ServerlessController, SnippetController, SnippetModel, SnippetType, SubscribeAllBit, SubscribeController, SubscribeNoteCreateBit, SubscribePostCreateBit, SubscribeRecentCreateBit, SubscribeSayCreateBit, SubscribeType, SubscribeTypeToBitMap, TLogin, TagModel, TextBaseModel, TextBaseModelLexical, TextBaseModelMarkdown, ThirdPartyServiceIntegrationModel, TimelineData, TimelineType, TopicController, TopicModel, TranslationMeta, Url, UrlOptionModel, UserController, UserModel, allControllerNames, allControllers, createClient, createClient as default, camelcaseKeys as simpleCamelcaseKeys };
package/dist/index.d.mts CHANGED
@@ -376,6 +376,7 @@ type NoteModel = TextBaseModel & {
376
376
  publicAt?: Date;
377
377
  password?: string | null;
378
378
  nid: number;
379
+ slug?: string;
379
380
  location?: string;
380
381
  coordinates?: Coordinate;
381
382
  topic?: TopicModel;
@@ -622,6 +623,19 @@ interface TimelineData {
622
623
  url: string;
623
624
  })[];
624
625
  }
626
+ interface LatestPostItem extends Pick<PostModel, 'id' | 'title' | 'slug' | 'created' | 'modified' | 'tags'> {
627
+ category: Pick<CategoryModel, 'name' | 'slug'> | null;
628
+ }
629
+ interface LatestNoteItem extends Pick<NoteModel, 'id' | 'title' | 'nid' | 'created' | 'modified' | 'mood' | 'weather' | 'bookmark'> {}
630
+ interface LatestData {
631
+ posts?: LatestPostItem[];
632
+ notes?: LatestNoteItem[];
633
+ }
634
+ type LatestCombinedItem = (LatestPostItem & {
635
+ type: 'post';
636
+ }) | (LatestNoteItem & {
637
+ type: 'note';
638
+ });
625
639
  interface AggregateStat {
626
640
  allComments: number;
627
641
  categories: number;
@@ -717,24 +731,44 @@ interface CommentModel extends BaseModel {
717
731
  refType: CollectionRefTypes;
718
732
  ref: string;
719
733
  state: number;
720
- commentsIndex: number;
721
734
  author: string;
722
735
  text: string;
723
736
  mail?: string;
724
737
  url?: string;
725
738
  ip?: string;
726
739
  agent?: string;
727
- key: string;
728
740
  pin?: boolean;
729
741
  avatar: string;
730
- parent?: CommentModel | string;
731
- children: CommentModel[];
742
+ parentCommentId?: string | null;
743
+ rootCommentId?: string | null;
744
+ replyCount?: number;
745
+ latestReplyAt?: string | null;
746
+ isDeleted?: boolean;
747
+ deletedAt?: string;
732
748
  isWhispers?: boolean;
733
749
  location?: string;
734
750
  source?: string;
735
751
  readerId?: string;
736
752
  editedAt?: string;
737
753
  }
754
+ interface CommentReplyWindow {
755
+ total: number;
756
+ returned: number;
757
+ threshold: number;
758
+ hasHidden: boolean;
759
+ hiddenCount: number;
760
+ nextCursor?: string;
761
+ }
762
+ interface CommentThreadItem extends CommentModel {
763
+ replies: CommentModel[];
764
+ replyWindow: CommentReplyWindow;
765
+ }
766
+ interface CommentThreadReplies {
767
+ replies: CommentModel[];
768
+ nextCursor?: string;
769
+ remaining: number;
770
+ done: boolean;
771
+ }
738
772
  interface CommentRef {
739
773
  id: string;
740
774
  categoryId?: string;
@@ -1050,6 +1084,16 @@ declare class AggregateController<ResponseWrapper> implements IController {
1050
1084
  data: TimelineData;
1051
1085
  };
1052
1086
  }>;
1087
+ getLatest(options: {
1088
+ limit?: number;
1089
+ types?: TimelineType[];
1090
+ combined: true;
1091
+ }): RequestProxyResult<LatestCombinedItem[], ResponseWrapper>;
1092
+ getLatest(options?: {
1093
+ limit?: number;
1094
+ types?: TimelineType[];
1095
+ combined?: false;
1096
+ }): RequestProxyResult<LatestData, ResponseWrapper>;
1053
1097
  /**
1054
1098
  * 获取聚合数据统计
1055
1099
  */
@@ -1571,30 +1615,40 @@ declare class CommentController<ResponseWrapper> implements IController {
1571
1615
  * 获取文章的评论列表
1572
1616
  * @param refId 文章 Id
1573
1617
  */
1574
- getByRefId(refId: string, pagination?: PaginationParams): RequestProxyResult<PaginateResult<CommentModel & {
1618
+ getByRefId(refId: string, pagination?: PaginationParams): RequestProxyResult<PaginateResult<CommentThreadItem & {
1575
1619
  ref: string;
1576
1620
  }> & {
1577
1621
  readers: Record<string, ReaderModel>;
1578
1622
  }, ResponseWrapper, ResponseWrapper extends unknown ? {
1579
1623
  [key: string]: any;
1580
- data: PaginateResult<CommentModel & {
1624
+ data: PaginateResult<CommentThreadItem & {
1581
1625
  ref: string;
1582
1626
  }> & {
1583
1627
  readers: Record<string, ReaderModel>;
1584
1628
  };
1585
1629
  } : ResponseWrapper extends {
1586
- data: PaginateResult<CommentModel & {
1630
+ data: PaginateResult<CommentThreadItem & {
1587
1631
  ref: string;
1588
1632
  }> & {
1589
1633
  readers: Record<string, ReaderModel>;
1590
1634
  };
1591
1635
  } ? ResponseWrapper : Omit<ResponseWrapper, "data"> & {
1592
- data: PaginateResult<CommentModel & {
1636
+ data: PaginateResult<CommentThreadItem & {
1593
1637
  ref: string;
1594
1638
  }> & {
1595
1639
  readers: Record<string, ReaderModel>;
1596
1640
  };
1597
1641
  }>;
1642
+ getThreadReplies(rootCommentId: string, params?: PaginationParams & {
1643
+ cursor?: string;
1644
+ }): RequestProxyResult<CommentThreadReplies, ResponseWrapper, ResponseWrapper extends unknown ? {
1645
+ [key: string]: any;
1646
+ data: CommentThreadReplies;
1647
+ } : ResponseWrapper extends {
1648
+ data: CommentThreadReplies;
1649
+ } ? ResponseWrapper : Omit<ResponseWrapper, "data"> & {
1650
+ data: CommentThreadReplies;
1651
+ }>;
1598
1652
  /**
1599
1653
  * 评论
1600
1654
  */
@@ -1690,6 +1744,8 @@ type NoteListOptions = {
1690
1744
  year?: number;
1691
1745
  sortBy?: 'weather' | 'mood' | 'title' | 'created' | 'modified';
1692
1746
  sortOrder?: 1 | -1;
1747
+ lang?: string;
1748
+ withSummary?: boolean;
1693
1749
  };
1694
1750
  type NoteByNidOptions = {
1695
1751
  password?: string;
@@ -1697,13 +1753,14 @@ type NoteByNidOptions = {
1697
1753
  lang?: string;
1698
1754
  prefer?: 'lexical';
1699
1755
  };
1756
+ type NoteBySlugDateOptions = NoteByNidOptions;
1700
1757
  type NoteMiddleListOptions = {
1701
1758
  lang?: string;
1702
1759
  };
1703
1760
  type NoteTopicListOptions = SortOptions & {
1704
1761
  lang?: string;
1705
1762
  };
1706
- type NoteTimelineItem = Pick<NoteModel, 'id' | 'title' | 'nid' | 'created' | 'isPublished'> & {
1763
+ type NoteTimelineItem = Pick<NoteModel, 'id' | 'title' | 'nid' | 'slug' | 'created' | 'isPublished'> & {
1707
1764
  isTranslated?: boolean;
1708
1765
  translationMeta?: TranslationMeta;
1709
1766
  };
@@ -1744,6 +1801,7 @@ declare class NoteController<ResponseWrapper> implements IController {
1744
1801
  * @param options 可选参数:password, single, lang
1745
1802
  */
1746
1803
  getNoteByNid(nid: number, options?: NoteByNidOptions): RequestProxyResult<NoteWrappedWithLikedAndTranslationPayload, ResponseWrapper>;
1804
+ getNoteBySlugDate(year: number, month: number, day: number, slug: string, options?: NoteBySlugDateOptions): RequestProxyResult<NoteWrappedWithLikedAndTranslationPayload, ResponseWrapper>;
1747
1805
  /**
1748
1806
  * 日记列表分页
1749
1807
  */
@@ -2500,4 +2558,4 @@ declare const allControllerNames: readonly ["ai", "ack", "activity", "aggregate"
2500
2558
  */
2501
2559
  declare const camelcaseKeys: <T = any>(obj: any) => T;
2502
2560
  //#endregion
2503
- export { AIController, AIDeepReadingModel, AISummaryModel, AISummaryStreamEvent, AITranslationModel, AITranslationStreamEvent, AckController, ActivityController, ActivityPresence, AdminExtraModel, AggregateAIConfig, AggregateController, AggregateRoot, AggregateRootWithTheme, AggregateStat, AggregateTop, AggregateTopNote, AggregateTopPost, AlgoliaSearchOptionsModel, AuthUser, BackupOptionsModel, BaiduSearchOptionsModel, BaseCommentIndexModel, BaseModel, BetterAuthSession, BetterAuthSessionResult, BetterAuthSignInResult, BetterAuthUser, BetterAuthUserRole, BingSearchOptionsModel, CategoryController, CategoryEntries, CategoryModel, CategoryType, CategoryWithChildrenModel, CheckLoggedResult, CollectionRefTypes, CommentController, CommentDto, CommentModel, CommentOptionsModel, CommentRef, CommentState, Coordinate, Count, EnumPageType, type HTTPClient, IConfig, IConfigKeys, type IRequestAdapter, Image, LastYearPublication, LinkController, LinkModel, LinkState, LinkType, MailOptionsModel, ModelWithLiked, ModelWithTranslation, NoteController, type NoteMiddleListOptions, NoteModel, type NoteTimelineItem, type NoteTopicListItem, type NoteTopicListOptions, NoteWrappedPayload, NoteWrappedWithLikedAndTranslationPayload, NoteWrappedWithLikedPayload, OwnerAllowLoginResult, OwnerSessionResult, PageController, PageModel, Pager, PaginateResult, PostController, type PostListItem, type PostListOptions, PostModel, ProjectController, ProjectModel, ReaderModel, RecentActivities, RecentComment, RecentLike, RecentNote, RecentPost, RecentRecent, RecentlyAttitudeEnum, RecentlyAttitudeResultEnum, RecentlyController, RecentlyModel, RecentlyRefType, RecentlyRefTypes, RequestError, RoomOmittedNote, RoomOmittedPage, RoomOmittedPost, RoomsData, SayController, SayModel, SearchController, SeoOptionModel, ServerlessController, SnippetController, SnippetModel, SnippetType, SubscribeAllBit, SubscribeController, SubscribeNoteCreateBit, SubscribePostCreateBit, SubscribeRecentCreateBit, SubscribeSayCreateBit, SubscribeType, SubscribeTypeToBitMap, TLogin, TagModel, TextBaseModel, TextBaseModelLexical, TextBaseModelMarkdown, ThirdPartyServiceIntegrationModel, TimelineData, TimelineType, TopicController, TopicModel, TranslationMeta, Url, UrlOptionModel, UserController, UserModel, allControllerNames, allControllers, createClient, createClient as default, camelcaseKeys as simpleCamelcaseKeys };
2561
+ export { AIController, AIDeepReadingModel, AISummaryModel, AISummaryStreamEvent, AITranslationModel, AITranslationStreamEvent, AckController, ActivityController, ActivityPresence, AdminExtraModel, AggregateAIConfig, AggregateController, AggregateRoot, AggregateRootWithTheme, AggregateStat, AggregateTop, AggregateTopNote, AggregateTopPost, AlgoliaSearchOptionsModel, AuthUser, BackupOptionsModel, BaiduSearchOptionsModel, BaseCommentIndexModel, BaseModel, BetterAuthSession, BetterAuthSessionResult, BetterAuthSignInResult, BetterAuthUser, BetterAuthUserRole, BingSearchOptionsModel, CategoryController, CategoryEntries, CategoryModel, CategoryType, CategoryWithChildrenModel, CheckLoggedResult, CollectionRefTypes, CommentController, CommentDto, CommentModel, CommentOptionsModel, CommentRef, CommentReplyWindow, CommentState, CommentThreadItem, CommentThreadReplies, Coordinate, Count, EnumPageType, type HTTPClient, IConfig, IConfigKeys, type IRequestAdapter, Image, LastYearPublication, LatestCombinedItem, LatestData, LatestNoteItem, LatestPostItem, LinkController, LinkModel, LinkState, LinkType, MailOptionsModel, ModelWithLiked, ModelWithTranslation, NoteController, type NoteMiddleListOptions, NoteModel, type NoteTimelineItem, type NoteTopicListItem, type NoteTopicListOptions, NoteWrappedPayload, NoteWrappedWithLikedAndTranslationPayload, NoteWrappedWithLikedPayload, OwnerAllowLoginResult, OwnerSessionResult, PageController, PageModel, Pager, PaginateResult, PostController, type PostListItem, type PostListOptions, PostModel, ProjectController, ProjectModel, ReaderModel, RecentActivities, RecentComment, RecentLike, RecentNote, RecentPost, RecentRecent, RecentlyAttitudeEnum, RecentlyAttitudeResultEnum, RecentlyController, RecentlyModel, RecentlyRefType, RecentlyRefTypes, RequestError, RoomOmittedNote, RoomOmittedPage, RoomOmittedPost, RoomsData, SayController, SayModel, SearchController, SeoOptionModel, ServerlessController, SnippetController, SnippetModel, SnippetType, SubscribeAllBit, SubscribeController, SubscribeNoteCreateBit, SubscribePostCreateBit, SubscribeRecentCreateBit, SubscribeSayCreateBit, SubscribeType, SubscribeTypeToBitMap, TLogin, TagModel, TextBaseModel, TextBaseModelLexical, TextBaseModelMarkdown, ThirdPartyServiceIntegrationModel, TimelineData, TimelineType, TopicController, TopicModel, TranslationMeta, Url, UrlOptionModel, UserController, UserModel, allControllerNames, allControllers, createClient, createClient as default, camelcaseKeys as simpleCamelcaseKeys };
package/dist/index.mjs CHANGED
@@ -199,6 +199,14 @@ var AggregateController = class {
199
199
  year
200
200
  } });
201
201
  }
202
+ getLatest(options) {
203
+ const { limit, types, combined } = options || {};
204
+ return this.proxy.latest.get({ params: {
205
+ limit,
206
+ types: types?.join(","),
207
+ combined
208
+ } });
209
+ }
202
210
  /**
203
211
  * 获取聚合数据统计
204
212
  */
@@ -412,6 +420,9 @@ var CommentController = class {
412
420
  size: size || 10
413
421
  } });
414
422
  }
423
+ getThreadReplies(rootCommentId, params = {}) {
424
+ return this.proxy.thread(rootCommentId).get({ params });
425
+ }
415
426
  /**
416
427
  * 评论
417
428
  */
@@ -515,18 +526,29 @@ var NoteController = class {
515
526
  prefer
516
527
  } });
517
528
  }
529
+ getNoteBySlugDate(year, month, day, slug, options) {
530
+ const { password, single, lang, prefer } = options || {};
531
+ return this.proxy(year.toString())(month.toString())(day.toString())(slug).get({ params: {
532
+ password,
533
+ single: single ? "1" : void 0,
534
+ lang,
535
+ prefer
536
+ } });
537
+ }
518
538
  /**
519
539
  * 日记列表分页
520
540
  */
521
541
  getList(page = 1, perPage = 10, options = {}) {
522
- const { select, sortBy, sortOrder, year } = options;
542
+ const { select, sortBy, sortOrder, year, lang, withSummary } = options;
523
543
  return this.proxy.get({ params: {
524
544
  page,
525
545
  size: perPage,
526
546
  select: select?.join(" "),
527
547
  sortBy,
528
548
  sortOrder,
529
- year
549
+ year,
550
+ lang,
551
+ withSummary: withSummary ? "1" : void 0
530
552
  } });
531
553
  }
532
554
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mx-space/api-client",
3
- "version": "2.1.1",
3
+ "version": "2.2.0",
4
4
  "description": "A api client for mx-space server@next",
5
5
  "type": "module",
6
6
  "engines": {
@@ -56,10 +56,10 @@
56
56
  "axios": "^1.13.3",
57
57
  "camelcase-keys": "^10.0.2",
58
58
  "cors": "2.8.6",
59
- "es-toolkit": "1.44.0",
59
+ "es-toolkit": "1.45.0",
60
60
  "express": "4.21.2",
61
61
  "form-data": "4.0.5",
62
- "tsdown": "0.20.1",
62
+ "tsdown": "0.21.0-beta.2",
63
63
  "umi-request": "1.4.0",
64
64
  "vite": "^7.3.1",
65
65
  "vitest": "4.0.18"
package/readme.md CHANGED
@@ -1,133 +1,74 @@
1
- # MApi Client
1
+ # @mx-space/api-client
2
2
 
3
- 这是一个适用于 MServer v3 JS SDK,封装了常用接口请求方法以及返回类型的声明,以快速开发前端应用。
3
+ A framework-agnostic TypeScript/JavaScript SDK for the MX Space server (MServer v3). It wraps common API endpoints with typed request methods and response types for fast frontend and server-side integration.
4
4
 
5
- ## 版本兼容性
5
+ ---
6
6
 
7
- | api-client 版本 | 支持的 Server 版本 | 说明 |
8
- | --------------- | ------------------ | ----------------------------- |
9
- | v2.x | >= 10 | 基于 Better Auth 的新认证系统 |
10
- | v1.x | <= 9 | 旧版认证系统 |
7
+ ## Table of Contents
11
8
 
12
- **注意**: v2 版本与 v1 版本存在 Breaking Changes,升级时请参考下方迁移指南。
9
+ - [Requirements](#requirements)
10
+ - [Installation](#installation)
11
+ - [Quick Start](#quick-start)
12
+ - [Architecture](#architecture)
13
+ - [Adapters](#adapters)
14
+ - [Controllers](#controllers)
15
+ - [Client Options](#client-options)
16
+ - [Proxy API](#proxy-api)
17
+ - [Version Compatibility & Migration](#version-compatibility--migration)
18
+ - [Development](#development)
19
+ - [License](#license)
13
20
 
14
- ## 迁移到 v2
21
+ ---
15
22
 
16
- ### Breaking Changes
23
+ ## Requirements
17
24
 
18
- #### 1. 控制器重命名
25
+ - **Node.js** ≥ 22 (see `engines` in `package.json`)
26
+ - **MX Space server** v10+ for api-client v2.x (Better Auth); v9 or below use api-client v1.x
19
27
 
20
- `user` 控制器已重命名为 `owner`,`master` 别名已被移除:
28
+ ---
21
29
 
22
- ```diff
23
- - client.user.getMasterInfo()
24
- + client.owner.getOwnerInfo()
25
-
26
- - client.master.getMasterInfo()
27
- + client.owner.getOwnerInfo()
28
- ```
30
+ ## Installation
29
31
 
30
- #### 2. 登录接口变更
32
+ From the monorepo root (recommended):
31
33
 
32
- 登录接口从 `POST /master/login` 变更为 `POST /auth/sign-in`:
33
-
34
- ```diff
35
- - client.user.login(username, password)
36
- + client.owner.login(username, password, { rememberMe: boolean })
34
+ ```bash
35
+ pnpm add @mx-space/api-client
37
36
  ```
38
37
 
39
- v2 版本的 `login` 方法返回的数据结构也发生了变化,现在返回 `{ token, user }` 格式。
40
-
41
- #### 3. 新增 Better Auth 相关接口
42
-
43
- v2 版本新增了 Better Auth 认证相关的接口:
38
+ Or with npm:
44
39
 
45
- ```ts
46
- // 获取当前会话
47
- client.owner.getSession()
48
-
49
- // 获取 Better Auth 原生会话
50
- client.owner.getAuthSession()
51
-
52
- // 登出
53
- client.owner.logout()
54
-
55
- // 获取支持的登录方式
56
- client.owner.getAllowLoginMethods()
57
-
58
- // 获取 OAuth 提供商列表
59
- client.owner.getProviders()
60
-
61
- // 列出所有会话
62
- client.owner.listSessions()
63
-
64
- // 撤销指定会话
65
- client.owner.revokeSession(token)
66
-
67
- // 撤销所有会话
68
- client.owner.revokeSessions()
69
-
70
- // 撤销其他会话
71
- client.owner.revokeOtherSessions()
40
+ ```bash
41
+ npm install @mx-space/api-client
72
42
  ```
73
43
 
74
- ## 迁移到 v1
75
-
76
- 不再提供 camelcase-keys 的 re-export,此库不再依赖 camelcase-keys 库,如有需要可自行安装。
44
+ The package is **framework-agnostic** and does not bundle a specific HTTP client. You must provide an adapter (e.g. Axios or fetch). Install the HTTP library you use:
77
45
 
78
- ```diff
79
- - import { camelcaseKeysDeep, camelcaseKeys } from '@mx-space/api-client'
80
- + import { simpleCamelcaseKeys as camelcaseKeysDeep } from '@mx-space/api-client'
46
+ ```bash
47
+ pnpm add axios
48
+ # or use the built-in fetch adapter (no extra install)
81
49
  ```
82
50
 
83
- ## 如何使用
51
+ ---
84
52
 
85
- SDK 框架无关,不捆绑任何一个网络请求库,只需要提供适配器。你需要手动传入符合接口标准的适配器。
53
+ ## Quick Start
86
54
 
87
- 此项目提供 `axios` `umi-request` 两个适配器。
88
-
89
- `axios` 为例。
55
+ 1. **Create a client** with an endpoint and an adapter.
56
+ 2. **Inject controllers** you need (tree-shakeable).
57
+ 3. **Call methods** on the client (e.g. `client.post`, `client.note`).
90
58
 
91
59
  ```ts
92
60
  import {
93
- AggregateController,
94
- allControllers, // ...
95
- CategoryController,
96
61
  createClient,
97
- NoteController,
98
62
  PostController,
63
+ NoteController,
64
+ AggregateController,
65
+ CategoryController,
99
66
  } from '@mx-space/api-client'
100
67
  import { axiosAdaptor } from '@mx-space/api-client/adaptors/axios'
101
68
 
102
- const endpoint = 'https://api.innei.dev/v2'
69
+ const endpoint = 'https://api.example.com/v2'
103
70
  const client = createClient(axiosAdaptor)(endpoint)
104
71
 
105
- // `default` is AxiosInstance
106
- // you can do anything else on this
107
- // interceptor or re-configure
108
- const $axios = axiosAdaptor.default
109
- // re-config (optional)
110
- $axios.defaults.timeout = 10000
111
- // set interceptors (optional)
112
- $axios.interceptors.request.use(
113
- (config) => {
114
- const token = getToken()
115
- if (token) {
116
- config.headers!.Authorization = `bearer ${getToken()}`
117
- }
118
-
119
- return config
120
- },
121
- (error) => {
122
- if (__DEV__) {
123
- console.log(error.message)
124
- }
125
-
126
- return Promise.reject(error)
127
- },
128
- )
129
-
130
- // inject controller first.
131
72
  client.injectControllers([
132
73
  PostController,
133
74
  NoteController,
@@ -135,40 +76,206 @@ client.injectControllers([
135
76
  CategoryController,
136
77
  ])
137
78
 
138
- // or you can inject allControllers
139
- client.injectControllers(allControllers)
79
+ // Typed API calls
80
+ const posts = await client.post.post.getList(1, 10, { year: 2024 })
81
+ const aggregate = await client.aggregate.getAggregateData()
82
+ ```
140
83
 
141
- // then you can request `post` `note` and `aggregate` controller
84
+ **Optional: set token and interceptors** (example with Axios):
142
85
 
143
- client.post.post.getList(page, 10, { year }).then((data) => {
144
- // do anything
86
+ ```ts
87
+ const $axios = axiosAdaptor.default
88
+ $axios.defaults.timeout = 10000
89
+ $axios.interceptors.request.use((config) => {
90
+ const token = getToken()
91
+ if (token) config.headers!.Authorization = `bearer ${token}`
92
+ return config
145
93
  })
146
94
  ```
147
95
 
148
- **为什么要手动注入控制器**
96
+ ---
97
+
98
+ ## Architecture
99
+
100
+ - **Core**: `HTTPClient` in `core/client.ts` — builds a route proxy and delegates HTTP calls to an adapter.
101
+ - **Adapters**: Implement `IRequestAdapter` (get/post/put/patch/delete + optional `default` client). Responses are normalized to `{ data }`; optional `transformResponse` (e.g. camelCase) runs on `data`.
102
+ - **Controllers**: Classes that receive the client and attach methods under a name (e.g. `post`, `note`). Controllers are **injected at runtime** so you only bundle what you use.
103
+ - **Proxy**: `client.proxy` allows arbitrary path chains and HTTP methods for endpoints not modeled by a controller (e.g. `client.note.proxy.something.other('123').info.get()`).
104
+
105
+ **Response shape**: The adapter is expected to return a value with a `data` property. By default, `getDataFromResponse` uses `(res) => res.data`, and `transformResponse` converts keys to camelCase. Each returned object gets attached `$raw` (adapter response), `$request` (url, method, options), and `$serialized` (transformed data).
106
+
107
+ ---
108
+
109
+ ## Adapters
149
110
 
150
- 按需加载,可以减少打包体积 (Tree Shake)
111
+ Official adapters live under `@mx-space/api-client/adaptors/`:
151
112
 
152
- **为什么不依赖请求库**
113
+ | Adapter | Import path | Notes |
114
+ |----------------|------------------------------------------|--------------------------|
115
+ | **Axios** | `@mx-space/api-client/adaptors/axios` | Exposes `axiosAdaptor.default` (AxiosInstance). |
116
+ | **umi-request**| `@mx-space/api-client/adaptors/umi-request` | For umi-request users. |
117
+ | **Fetch** | `@mx-space/api-client/adaptors/fetch` | Uses global `fetch`; no extra dependency. |
153
118
 
154
- 可以防止项目中出现两个请求库,减少打包体积
119
+ **Custom adapter**: Implement `IRequestAdapter` from `@mx-space/api-client` (methods: `get`, `post`, `put`, `patch`, `delete`; optional `default`). See `src/adaptors/axios.ts` and `src/adaptors/umi-request.ts` for reference.
155
120
 
156
- **如果不使用 axios,应该如何编写适配器**
121
+ ---
157
122
 
158
- 参考 `src/adaptors/axios.ts` 和 `src/adaptors/umi-request.ts`
123
+ ## Controllers
159
124
 
160
- **如何使用 proxy 来访问 sdk 内未包含的请求**
125
+ Inject one or more controllers so the client exposes them (e.g. `client.post`, `client.note`). Use `allControllers` to inject everything, or list only what you need for smaller bundles.
161
126
 
162
- 如请求 `GET /notes/something/other/123456/info`,可以使用
127
+ | Controller | Client name | Purpose (high level) |
128
+ |-------------|---------------|-----------------------------|
129
+ | PostController | `post` | Blog posts |
130
+ | NoteController | `note` | Notes / private posts |
131
+ | PageController | `page` | Pages |
132
+ | CategoryController | `category` | Categories |
133
+ | AggregateController | `aggregate` | Site aggregate data |
134
+ | CommentController | `comment` | Comments |
135
+ | UserController (owner) | `owner` | Auth, session, login, OAuth |
136
+ | SayController | `say` | Says / short notes |
137
+ | LinkController | `link` | Links |
138
+ | SnippetController| `snippet` | Snippets |
139
+ | ProjectController | `project` | Projects |
140
+ | TopicController | `topic` | Topics |
141
+ | RecentlyController | `recently` | Recently items |
142
+ | SearchController | `search` | Search |
143
+ | ActivityController| `activity`| Activity |
144
+ | AIController | `ai` | AI-related endpoints |
145
+ | SubscribeController | `subscribe` | Subscriptions |
146
+ | ServerlessController | `serverless` | Serverless functions |
147
+ | AckController | `ack` | Ack |
148
+
149
+ **Example — inject all controllers:**
163
150
 
164
151
  ```ts
165
- client.note.proxy.something.other('123456').info.get()
152
+ import { createClient, allControllers } from '@mx-space/api-client'
153
+ import { axiosAdaptor } from '@mx-space/api-client/adaptors/axios'
154
+
155
+ const client = createClient(axiosAdaptor)('https://api.example.com/v2')
156
+ client.injectControllers(allControllers)
166
157
  ```
167
158
 
168
- **从 proxy 获取请求地址但不发出**
159
+ **Why inject manually?** To keep bundle size small (tree-shaking) and to avoid pulling in a specific HTTP library by default.
160
+
161
+ ---
162
+
163
+ ## Client Options
164
+
165
+ `createClient(adapter)(endpoint, options)` accepts optional second argument:
166
+
167
+ | Option | Description |
168
+ |----------------------------|-------------|
169
+ | `controllers` | Array of controller classes to inject immediately. |
170
+ | `transformResponse` | `(data) => transformed`. Default: camelCase keys. |
171
+ | `getDataFromResponse` | `(response) => data`. Default: `(res) => res.data`. |
172
+ | `getCodeMessageFromException` | `(error) => { message?, code? }` for custom error parsing. |
173
+ | `customThrowResponseError` | `(error) => Error` to throw a custom error type. |
174
+
175
+ ---
176
+
177
+ ## Proxy API
178
+
179
+ For paths not covered by a controller, use the **proxy** to build URLs and perform requests:
169
180
 
170
181
  ```ts
171
- client.note.proxy.something.other('123456').info.toString() // /notes/something/other/123456/info
182
+ // GET /notes/something/other/123456/info
183
+ const data = await client.note.proxy.something.other('123456').info.get()
184
+
185
+ // Get path only (no request)
186
+ client.note.proxy.something.other('123456').info.toString()
187
+ // => '/notes/something/other/123456/info'
188
+
189
+ // Full URL (with base endpoint)
190
+ client.note.proxy.something.other('123456').info.toString(true)
191
+ // => 'https://api.example.com/v2/notes/something/other/123456/info'
192
+ ```
193
+
194
+ ---
195
+
196
+ ## Version Compatibility & Migration
197
+
198
+ ### Compatibility
199
+
200
+ | api-client version | Server version | Notes |
201
+ |--------------------|----------------|-------|
202
+ | v2.x | ≥ 10 | Better Auth; owner API; new auth endpoints. |
203
+ | v1.x | ≤ 9 | Legacy auth. |
204
+
205
+ v2 introduces breaking changes; see migration below.
206
+
207
+ ### Migrating to v2
208
+
209
+ **1. Controller renames**
210
+
211
+ - `user` / `master` → `owner`. Use `client.owner` for auth and owner info.
212
+
213
+ ```diff
214
+ - client.user.getMasterInfo()
215
+ - client.master.getMasterInfo()
216
+ + client.owner.getOwnerInfo()
217
+ ```
218
+
219
+ **2. Login**
220
+
221
+ - Endpoint: `POST /master/login` → `POST /auth/sign-in`.
222
+ - v2 `login` returns `{ token, user }`.
223
+
224
+ ```diff
225
+ - client.user.login(username, password)
226
+ + client.owner.login(username, password, { rememberMe: boolean })
227
+ ```
228
+
229
+ **3. New auth-related APIs (v2)**
230
+
231
+ - `client.owner.getSession()`
232
+ - `client.owner.getAuthSession()`
233
+ - `client.owner.logout()`
234
+ - `client.owner.getAllowLoginMethods()`
235
+ - `client.owner.getProviders()`
236
+ - `client.owner.listSessions()`
237
+ - `client.owner.revokeSession(token)` / `revokeSessions()` / `revokeOtherSessions()`
238
+
239
+ ### Migrating from v2 to v1 (downgrade)
172
240
 
173
- client.note.proxy.something.other('123456').info.toString(true) // http://localhost:2333/notes/something/other/123456/info
241
+ - No re-export of `camelcase-keys`. Use the built-in helper or install yourself:
242
+
243
+ ```diff
244
+ - import { camelcaseKeysDeep, camelcaseKeys } from '@mx-space/api-client'
245
+ + import { simpleCamelcaseKeys as camelcaseKeysDeep } from '@mx-space/api-client'
174
246
  ```
247
+
248
+ ---
249
+
250
+ ## Development
251
+
252
+ **From repo root:**
253
+
254
+ ```bash
255
+ pnpm i
256
+ ```
257
+
258
+ **From `packages/api-client`:**
259
+
260
+ - **Build**: `pnpm run package` or `pnpm run build` (cleans `dist`, runs `tsdown`).
261
+ - **Test**: `pnpm test` (Vitest).
262
+ - **Dev (watch tests)**: `pnpm run dev`.
263
+
264
+ **Project layout (high level):**
265
+
266
+ - `core/` — client, request attachment, error type.
267
+ - `controllers/` — one class per API area; names listed in `controllers/index.ts`.
268
+ - `adaptors/` — axios, umi-request, fetch.
269
+ - `models/`, `dtos/`, `interfaces/` — types and DTOs for requests/responses.
270
+ - `utils/` — path resolution, camelCase, auto-bind.
271
+
272
+ **Exports:**
273
+
274
+ - Main: `createClient`, `RequestError`, controllers, models, DTOs, `simpleCamelcaseKeys`, `HTTPClient` type, `IRequestAdapter` type.
275
+ - Adapters: `@mx-space/api-client/adaptors/axios`, `/adaptors/umi-request`, `/adaptors/fetch`.
276
+
277
+ ---
278
+
279
+ ## License
280
+
281
+ MIT.