@ph-cms/client-sdk 0.1.42 → 0.1.44

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.
package/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # @ph-cms/client-sdk 0.1.42
1
+ # @ph-cms/client-sdk 0.1.44
2
2
 
3
3
  PH-CMS 클라이언트 SDK — 브라우저 및 React 애플리케이션을 위한 통합 클라이언트.
4
4
 
@@ -8,6 +8,7 @@ PH-CMS 클라이언트 SDK — 브라우저 및 React 애플리케이션을 위
8
8
  - Auth Provider 인터페이스 및 구현체 (`LocalAuthProvider`, `FirebaseAuthProvider`, `StaticAuthProvider`)
9
9
  - React Context 및 Hooks
10
10
  - React Query 통합
11
+ - 장소 검색 모듈 및 React hook
11
12
  - 자동 토큰 갱신 (Proactive + Reactive)
12
13
  - SSR (Server-Side Rendering) 지원 및 Cookie 연동 가능
13
14
  - Firebase ↔ PH-CMS 인증 동기화
@@ -100,6 +101,9 @@ import type {
100
101
  // Common
101
102
  PagedResponse,
102
103
  UserProfileDto,
104
+ IntegratedPlace,
105
+ PlaceSearchQuery,
106
+ PlaceSearchResponse,
103
107
  } from '@ph-cms/client-sdk';
104
108
  ```
105
109
 
@@ -107,6 +111,38 @@ import type {
107
111
 
108
112
  ---
109
113
 
114
+ ## Place Search
115
+
116
+ SDK에서 통합 장소 검색 API를 바로 호출할 수 있습니다.
117
+
118
+ ### Core Client
119
+
120
+ ```ts
121
+ const result = await client.place.search({
122
+ keyword: '강남역 맛집',
123
+ });
124
+ ```
125
+
126
+ ### React Hook
127
+
128
+ ```ts
129
+ import { usePlaceSearch } from '@ph-cms/client-sdk';
130
+
131
+ const { data, isLoading } = usePlaceSearch(
132
+ { keyword: '강남역 맛집' },
133
+ true,
134
+ );
135
+ ```
136
+
137
+ 응답의 각 장소 항목은 항상 동일한 키를 가집니다.
138
+
139
+ - `phoneNumber`: `string | null`
140
+ - `link`: `string | null`
141
+ - `photos`: `[]` 포함 항상 존재
142
+ - `reviews`: `[]` 포함 항상 존재
143
+
144
+ ---
145
+
110
146
  ## Authentication Architecture
111
147
 
112
148
  ### Overview
@@ -1679,7 +1715,7 @@ const result = await content.list({ channelUid: 'my-channel' });
1679
1715
  | 메서드 | 설명 |
1680
1716
  |---|---|
1681
1717
  | `list(query: ListContentQuery)` | 콘텐츠 목록 조회 → `PagedContentListResponse` |
1682
- | `get(uid: string)` | 단일 콘텐츠 상세 조회 → `ContentDto` |
1718
+ | `get(uid: string, params?: { withParent?: boolean; withDetail?: boolean })` | 단일 콘텐츠 상세 조회 → `ContentDto` |
1683
1719
  | `create(data: CreateContentRequest)` | 일반 콘텐츠 생성 → `ContentDto` |
1684
1720
  | `update(uid: string, data: UpdateContentRequest)` | 콘텐츠 수정 → `ContentDto` |
1685
1721
  | `delete(uid: string)` | 콘텐츠 삭제 |
@@ -1693,6 +1729,13 @@ const result = await content.list({ channelUid: 'my-channel' });
1693
1729
  | `getLikers(uid: string, params?: Partial<ListLikersQuery>)` | 콘텐츠에 좋아요 누른 사용자 목록 조회 → `PagedResponse<UserProfileDto>` |
1694
1730
  | `report(uid: string, data: ReportContentRequest)` | 콘텐츠 신고 → `ReportContentResponse` |
1695
1731
 
1732
+ 예시:
1733
+
1734
+ ```ts
1735
+ await client.content.list({ withParent: true, withDetail: true, page: 1, limit: 20 });
1736
+ await client.content.get('cnt_123', { withParent: true, withDetail: true });
1737
+ ```
1738
+
1696
1739
  ### `ChannelModule` (`client.channel`)
1697
1740
 
1698
1741
  | 메서드 | 설명 |
package/dist/client.d.ts CHANGED
@@ -4,6 +4,7 @@ import { AuthModule } from './modules/auth';
4
4
  import { ChannelModule } from './modules/channel';
5
5
  import { ContentModule } from './modules/content';
6
6
  import { MediaModule } from './modules/media';
7
+ import { PlaceModule } from './modules/place';
7
8
  import { TermsModule } from './modules/terms';
8
9
  import { UserModule } from './modules/user';
9
10
  export interface PHCMSClientConfig {
@@ -25,6 +26,7 @@ export declare class PHCMSClient {
25
26
  readonly channel: ChannelModule;
26
27
  readonly terms: TermsModule;
27
28
  readonly media: MediaModule;
29
+ readonly place: PlaceModule;
28
30
  readonly user: UserModule;
29
31
  /**
30
32
  * Whether a token refresh is currently in flight (used by the 401
package/dist/client.js CHANGED
@@ -11,6 +11,7 @@ const auth_1 = require("./modules/auth");
11
11
  const channel_1 = require("./modules/channel");
12
12
  const content_1 = require("./modules/content");
13
13
  const media_1 = require("./modules/media");
14
+ const place_1 = require("./modules/place");
14
15
  const terms_1 = require("./modules/terms");
15
16
  const user_1 = require("./modules/user");
16
17
  class PHCMSClient {
@@ -68,6 +69,7 @@ class PHCMSClient {
68
69
  this.channel = new channel_1.ChannelModule(this.axiosInstance, normalizedApiPrefix);
69
70
  this.terms = new terms_1.TermsModule(this.axiosInstance, normalizedApiPrefix);
70
71
  this.media = new media_1.MediaModule(this.axiosInstance, normalizedApiPrefix);
72
+ this.place = new place_1.PlaceModule(this.axiosInstance, normalizedApiPrefix);
71
73
  this.user = new user_1.UserModule(this.axiosInstance, normalizedApiPrefix);
72
74
  // Wire the refresh function into the auth provider so it can
73
75
  // proactively refresh tokens inside `getToken()` without needing
package/dist/core.d.ts CHANGED
@@ -8,6 +8,7 @@ export * from './modules/auth';
8
8
  export * from './modules/channel';
9
9
  export * from './modules/content';
10
10
  export * from './modules/media';
11
+ export * from './modules/place';
11
12
  export * from './modules/terms';
12
13
  export * from './modules/user';
13
14
  export * from './types';
package/dist/core.js CHANGED
@@ -27,6 +27,7 @@ __exportStar(require("./modules/auth"), exports);
27
27
  __exportStar(require("./modules/channel"), exports);
28
28
  __exportStar(require("./modules/content"), exports);
29
29
  __exportStar(require("./modules/media"), exports);
30
+ __exportStar(require("./modules/place"), exports);
30
31
  __exportStar(require("./modules/terms"), exports);
31
32
  __exportStar(require("./modules/user"), exports);
32
33
  __exportStar(require("./types"), exports);
@@ -9,6 +9,8 @@ export declare const contentKeys: {
9
9
  type?: string | undefined;
10
10
  channelUid?: string | undefined;
11
11
  channelSlug?: string | undefined;
12
+ withDetail?: boolean | undefined;
13
+ withParent?: boolean | undefined;
12
14
  tags?: string[] | undefined;
13
15
  parentUid?: string | undefined;
14
16
  authorUid?: string | undefined;
@@ -17,11 +19,17 @@ export declare const contentKeys: {
17
19
  keyword?: string | undefined;
18
20
  orderBy?: "title" | "created_at" | "updated_at" | "published_at" | "view_count" | "like_count" | undefined;
19
21
  orderDir?: "asc" | "desc" | undefined;
20
- withDetail?: boolean | undefined;
21
22
  includeDeleted?: boolean | undefined;
22
23
  }];
23
24
  details: () => readonly ["contents", "detail"];
24
- detail: (uid: string) => readonly ["contents", "detail", string];
25
+ detailPrefix: (uid: string) => readonly ["contents", "detail", string];
26
+ detail: (uid: string, params?: {
27
+ withParent?: boolean;
28
+ withDetail?: boolean;
29
+ }) => readonly ["contents", "detail", string, {
30
+ withParent?: boolean;
31
+ withDetail?: boolean;
32
+ }];
25
33
  likes: () => readonly ["contents", "like"];
26
34
  like: (uid: string) => readonly ["contents", "like", string];
27
35
  };
@@ -35,7 +43,10 @@ export declare const useContentList: (params: ListContentQuery, enabled?: boolea
35
43
  last: boolean;
36
44
  empty: boolean;
37
45
  }, Error>;
38
- export declare const useContentDetail: (uid: string) => import("@tanstack/react-query").UseQueryResult<ContentDto, Error>;
46
+ export declare const useContentDetail: (uid: string, params?: {
47
+ withParent?: boolean;
48
+ withDetail?: boolean;
49
+ }) => import("@tanstack/react-query").UseQueryResult<ContentDto, Error>;
39
50
  export declare const useIncrementView: () => import("@tanstack/react-query").UseMutationResult<void, Error, string, {
40
51
  previousDetail: ContentDto | undefined;
41
52
  uid: string;
@@ -8,7 +8,8 @@ exports.contentKeys = {
8
8
  lists: () => [...exports.contentKeys.all, 'list'],
9
9
  list: (params) => [...exports.contentKeys.lists(), params],
10
10
  details: () => [...exports.contentKeys.all, 'detail'],
11
- detail: (uid) => [...exports.contentKeys.details(), uid],
11
+ detailPrefix: (uid) => [...exports.contentKeys.details(), uid],
12
+ detail: (uid, params) => [...exports.contentKeys.details(), uid, params ?? {}],
12
13
  likes: () => [...exports.contentKeys.all, 'like'],
13
14
  like: (uid) => [...exports.contentKeys.likes(), uid],
14
15
  };
@@ -27,11 +28,11 @@ const useContentList = (params, enabled = true) => {
27
28
  });
28
29
  };
29
30
  exports.useContentList = useContentList;
30
- const useContentDetail = (uid) => {
31
+ const useContentDetail = (uid, params) => {
31
32
  const client = (0, context_1.usePHCMS)();
32
33
  return (0, react_query_1.useQuery)({
33
- queryKey: exports.contentKeys.detail(uid),
34
- queryFn: () => client.content.get(uid),
34
+ queryKey: exports.contentKeys.detail(uid, params),
35
+ queryFn: () => client.content.get(uid, params),
35
36
  enabled: !!uid,
36
37
  });
37
38
  };
@@ -43,7 +44,7 @@ const useIncrementView = () => {
43
44
  mutationFn: (uid) => client.content.incrementView(uid),
44
45
  onMutate: async (uid) => {
45
46
  // Cancel any outgoing refetches for the content detail
46
- await queryClient.cancelQueries({ queryKey: exports.contentKeys.detail(uid) });
47
+ await queryClient.cancelQueries({ queryKey: exports.contentKeys.detailPrefix(uid) });
47
48
  // Snapshot the previous detail
48
49
  const previousDetail = queryClient.getQueryData(exports.contentKeys.detail(uid));
49
50
  // Optimistically update the view count in the detail cache
@@ -99,7 +100,7 @@ const useToggleLike = () => {
99
100
  onMutate: async (uid) => {
100
101
  // Cancel any outgoing refetches so they don't overwrite our optimistic update
101
102
  await queryClient.cancelQueries({ queryKey: exports.contentKeys.like(uid) });
102
- await queryClient.cancelQueries({ queryKey: exports.contentKeys.detail(uid) });
103
+ await queryClient.cancelQueries({ queryKey: exports.contentKeys.detailPrefix(uid) });
103
104
  // Snapshot the previous values
104
105
  const previousLikeStatus = queryClient.getQueryData(exports.contentKeys.like(uid));
105
106
  const previousDetail = queryClient.getQueryData(exports.contentKeys.detail(uid));
@@ -161,6 +162,7 @@ const useUpdateContent = () => {
161
162
  mutationFn: ({ uid, data }) => client.content.update(uid, data),
162
163
  onSuccess: (data, variables) => {
163
164
  queryClient.invalidateQueries({ queryKey: exports.contentKeys.detail(variables.uid) });
165
+ queryClient.invalidateQueries({ queryKey: exports.contentKeys.detailPrefix(variables.uid) });
164
166
  queryClient.invalidateQueries({ queryKey: exports.contentKeys.lists() });
165
167
  },
166
168
  });
@@ -0,0 +1,38 @@
1
+ import { PlaceSearchQuery } from '@ph-cms/api-contract';
2
+ export declare const placeKeys: {
3
+ all: readonly ["places"];
4
+ search: (params: PlaceSearchQuery) => readonly ["places", "search", {
5
+ keyword: string;
6
+ lat?: number | undefined;
7
+ lng?: number | undefined;
8
+ }];
9
+ };
10
+ export declare const usePlaceSearch: (params: PlaceSearchQuery, enabled?: boolean) => import("@tanstack/react-query").UseQueryResult<{
11
+ items: {
12
+ id: string;
13
+ provider: "google" | "naver";
14
+ name: string;
15
+ address: string;
16
+ location: {
17
+ lat: number;
18
+ lng: number;
19
+ };
20
+ category: string;
21
+ phoneNumber: string | null;
22
+ link: string | null;
23
+ photos: {
24
+ url: string;
25
+ width: number | null;
26
+ height: number | null;
27
+ attribution: string | null;
28
+ }[];
29
+ reviews: {
30
+ date: string | null;
31
+ title: string;
32
+ author: string | null;
33
+ link: string;
34
+ snippet: string;
35
+ }[];
36
+ }[];
37
+ keyword: string;
38
+ }, Error>;
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.usePlaceSearch = exports.placeKeys = void 0;
4
+ const react_query_1 = require("@tanstack/react-query");
5
+ const context_1 = require("../context");
6
+ exports.placeKeys = {
7
+ all: ['places'],
8
+ search: (params) => [...exports.placeKeys.all, 'search', params],
9
+ };
10
+ const usePlaceSearch = (params, enabled = true) => {
11
+ const client = (0, context_1.usePHCMS)();
12
+ return (0, react_query_1.useQuery)({
13
+ queryKey: exports.placeKeys.search(params),
14
+ queryFn: () => client.place.search(params),
15
+ staleTime: 1000 * 60 * 5,
16
+ enabled: enabled && !!params.keyword,
17
+ });
18
+ };
19
+ exports.usePlaceSearch = usePlaceSearch;
package/dist/index.d.ts CHANGED
@@ -10,6 +10,7 @@ export * from './modules/auth';
10
10
  export * from './modules/channel';
11
11
  export * from './modules/content';
12
12
  export * from './modules/media';
13
+ export * from './modules/place';
13
14
  export * from './modules/terms';
14
15
  export * from './modules/user';
15
16
  export * from './context';
@@ -18,5 +19,6 @@ export * from './hooks/useContent';
18
19
  export * from './hooks/useStampTour';
19
20
  export * from './hooks/useFirebaseAuthSync';
20
21
  export * from './hooks/useMedia';
22
+ export * from './hooks/usePlace';
21
23
  export * from './hooks/useUser';
22
24
  export * from './types';
package/dist/index.js CHANGED
@@ -26,6 +26,7 @@ __exportStar(require("./modules/auth"), exports);
26
26
  __exportStar(require("./modules/channel"), exports);
27
27
  __exportStar(require("./modules/content"), exports);
28
28
  __exportStar(require("./modules/media"), exports);
29
+ __exportStar(require("./modules/place"), exports);
29
30
  __exportStar(require("./modules/terms"), exports);
30
31
  __exportStar(require("./modules/user"), exports);
31
32
  __exportStar(require("./context"), exports);
@@ -34,5 +35,6 @@ __exportStar(require("./hooks/useContent"), exports);
34
35
  __exportStar(require("./hooks/useStampTour"), exports);
35
36
  __exportStar(require("./hooks/useFirebaseAuthSync"), exports);
36
37
  __exportStar(require("./hooks/useMedia"), exports);
38
+ __exportStar(require("./hooks/usePlace"), exports);
37
39
  __exportStar(require("./hooks/useUser"), exports);
38
40
  __exportStar(require("./types"), exports);
@@ -5,7 +5,10 @@ export declare class ContentModule {
5
5
  private prefix;
6
6
  constructor(client: AxiosInstance, prefix?: string);
7
7
  list(params: ListContentQuery): Promise<PagedContentListResponse>;
8
- get(uid: string): Promise<ContentDto>;
8
+ get(uid: string, params?: {
9
+ withParent?: boolean;
10
+ withDetail?: boolean;
11
+ }): Promise<ContentDto>;
9
12
  incrementView(uid: string): Promise<void>;
10
13
  create(data: CreateContentRequest): Promise<ContentDto>;
11
14
  update(uid: string, data: UpdateContentRequest): Promise<ContentDto>;
@@ -15,10 +15,10 @@ class ContentModule {
15
15
  }
16
16
  return this.client.get(`${this.prefix}/contents`, { params });
17
17
  }
18
- async get(uid) {
18
+ async get(uid, params) {
19
19
  if (!uid)
20
20
  throw new errors_1.ValidationError("UID is required", []);
21
- return this.client.get(`${this.prefix}/contents/${uid}`);
21
+ return this.client.get(`${this.prefix}/contents/${uid}`, { params });
22
22
  }
23
23
  async incrementView(uid) {
24
24
  if (!uid)
@@ -0,0 +1,8 @@
1
+ import { PlaceSearchQuery, PlaceSearchResponse } from "@ph-cms/api-contract";
2
+ import { AxiosInstance } from "axios";
3
+ export declare class PlaceModule {
4
+ private client;
5
+ private prefix;
6
+ constructor(client: AxiosInstance, prefix?: string);
7
+ search(params: PlaceSearchQuery): Promise<PlaceSearchResponse>;
8
+ }
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PlaceModule = void 0;
4
+ const api_contract_1 = require("@ph-cms/api-contract");
5
+ const errors_1 = require("../errors");
6
+ class PlaceModule {
7
+ constructor(client, prefix = '/api') {
8
+ this.client = client;
9
+ this.prefix = prefix;
10
+ }
11
+ async search(params) {
12
+ const validation = api_contract_1.PlaceSearchQuerySchema.safeParse(params);
13
+ if (!validation.success) {
14
+ throw new errors_1.ValidationError("Invalid place search params", validation.error.errors);
15
+ }
16
+ return this.client.get(`${this.prefix}/places/search`, {
17
+ params,
18
+ });
19
+ }
20
+ }
21
+ exports.PlaceModule = PlaceModule;
package/dist/types.d.ts CHANGED
@@ -13,4 +13,5 @@ export type { PermissionPolicySetDto } from '@ph-cms/api-contract';
13
13
  export type { TermDto } from '@ph-cms/api-contract';
14
14
  export type { GeoJSON } from '@ph-cms/api-contract';
15
15
  export type { MediaUploadTicketBatchRequest, MediaUploadTicketBatchResponse, MediaUploadTicketRequest, MediaUploadTicketResponse } from '@ph-cms/api-contract';
16
+ export type { IntegratedPlace, PlaceSearchQuery, PlaceSearchResponse, } from '@ph-cms/api-contract';
16
17
  export type { PagedResponse } from '@ph-cms/api-contract';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ph-cms/client-sdk",
3
- "version": "0.1.42",
3
+ "version": "0.1.44",
4
4
  "description": "Unified PH-CMS Client SDK (React + Core)",
5
5
  "keywords": [],
6
6
  "license": "MIT",
@@ -30,7 +30,7 @@
30
30
  "LICENSE"
31
31
  ],
32
32
  "dependencies": {
33
- "@ph-cms/api-contract": "^0.1.19",
33
+ "@ph-cms/api-contract": "^0.1.21",
34
34
  "@tanstack/react-query": "^5.0.0",
35
35
  "axios": "^1.6.0",
36
36
  "zod": "^3.22.4"