@sayren/storefront-sdk 0.6.0 → 0.7.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.
- package/dist/auth/index.d.mts +22 -4
- package/dist/auth/index.mjs +42 -3
- package/dist/{client-Do9-LG5X.mjs → client-B1uXGefY.mjs} +90 -42
- package/dist/{http-CqAR0_w4.d.mts → http-BbFHVGBf.d.mts} +22 -1
- package/dist/index.d.mts +119 -3
- package/dist/index.mjs +2 -2
- package/package.json +1 -1
- package/src/auth/index.ts +49 -2
- package/src/client.ts +23 -11
- package/src/schemas/auth.ts +25 -2
- package/src/schemas/catalog.ts +15 -4
- package/src/schemas/inquiry.ts +8 -0
- package/src/schemas/member.ts +8 -0
- package/src/schemas/order.ts +9 -0
- package/src/schemas/review.ts +16 -0
- package/src/schemas/store.ts +1 -1
package/dist/auth/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as AnonymousSession,
|
|
1
|
+
import { a as AnonymousSession, d as SignupRequest, f as SocialProvider, h as WithdrawalBlockedDetails, l as LoginRequest, m as WithdrawRequest, p as TokenPair, t as ApiError, u as MemberIdentities } from "../http-BbFHVGBf.mjs";
|
|
2
2
|
//#region src/auth/index.d.ts
|
|
3
3
|
/** 소셜 로그인 시작 결과 — `codeVerifier`와 `state`는 돌아올 때까지 서버 세션(httpOnly 쿠키 등)에 보관한다 */
|
|
4
4
|
export interface IdpStart {
|
|
@@ -31,7 +31,7 @@ export interface StorefrontAuthOptions {
|
|
|
31
31
|
fetch?: typeof globalThis.fetch;
|
|
32
32
|
}
|
|
33
33
|
/**
|
|
34
|
-
* 구매자 인증 — 화면은
|
|
34
|
+
* 구매자 인증 — 화면은 스토어프론트가 그리고, 계정 저장과 인증 처리는 sayren이 한다.
|
|
35
35
|
* 어느 방식으로 로그인해도 결과는 같은 구매자 토큰(`TokenPair`)이고 `/me/*`·장바구니·주문 API에 그대로 쓴다.
|
|
36
36
|
*
|
|
37
37
|
* 토큰 보관은 앱 몫이다. 서버(loader·action)에서 호출하고 httpOnly 쿠키에 두기를 권한다.
|
|
@@ -56,7 +56,7 @@ export declare function createStorefrontAuth(options: StorefrontAuthOptions): {
|
|
|
56
56
|
/**
|
|
57
57
|
* 소셜 로그인 시작 — PKCE(S256)와 state를 만들고 공급자 로그인 주소를 받는다. 반환한 `state`·`codeVerifier`를
|
|
58
58
|
* 서버 세션에 보관하고 구매자를 `url`로 보낸다. 로그인을 마치면 `redirectUri`로 `?code=&state=`가 붙어 돌아온다.
|
|
59
|
-
* `redirectUri`는
|
|
59
|
+
* `redirectUri`는 스토어프론트 도메인 목록 안이어야 한다
|
|
60
60
|
*/
|
|
61
61
|
idp(provider: SocialProvider, options: {
|
|
62
62
|
redirectUri: string;
|
|
@@ -68,6 +68,24 @@ export declare function createStorefrontAuth(options: StorefrontAuthOptions): {
|
|
|
68
68
|
idpCallback(input: IdpCallbackInput & {
|
|
69
69
|
error?: string | null;
|
|
70
70
|
}): Promise<TokenPair>;
|
|
71
|
+
/** 로그인 수단 — 비밀번호가 있는지, 어떤 소셜 계정이 연결됐는지 */
|
|
72
|
+
identities(accessToken: string): Promise<MemberIdentities>;
|
|
73
|
+
/**
|
|
74
|
+
* 소셜 계정 연결 시작 — 로그인한 구매자에 공급자 계정을 붙인다. `idp()`처럼 `state`·`codeVerifier`를 보관하고
|
|
75
|
+
* 구매자를 `url`로 보낸다. 돌아오면 `linkIdpCallback()`으로 마무리한다. 연결은 흐름을 시작한 구매자에게만 된다
|
|
76
|
+
*/
|
|
77
|
+
linkIdp(accessToken: string, provider: SocialProvider, options: {
|
|
78
|
+
redirectUri: string;
|
|
79
|
+
}): Promise<IdpStart>;
|
|
80
|
+
/**
|
|
81
|
+
* 소셜 계정 연결 마무리 — state를 비교하고 code를 바꿔 연결한다. 다른 구매자에 이미 연결된 계정이면
|
|
82
|
+
* 409 `IDENTITY_IN_USE`, 같은 공급자의 다른 계정을 이미 연결했으면 409 `PROVIDER_ALREADY_LINKED`
|
|
83
|
+
*/
|
|
84
|
+
linkIdpCallback(accessToken: string, provider: SocialProvider, input: Omit<IdpCallbackInput, "cartToken"> & {
|
|
85
|
+
error?: string | null;
|
|
86
|
+
}): Promise<MemberIdentities>;
|
|
87
|
+
/** 소셜 계정 연결 해제 — 마지막 로그인 수단이면 409 `LAST_LOGIN_METHOD` */
|
|
88
|
+
unlinkIdp(accessToken: string, provider: SocialProvider): Promise<MemberIdentities>;
|
|
71
89
|
/** 토큰 갱신 — refreshToken은 한 번 쓰면 폐기되고 새 쌍이 온다(회전) */
|
|
72
90
|
refresh(refreshToken: string): Promise<TokenPair>;
|
|
73
91
|
/**
|
|
@@ -84,4 +102,4 @@ export declare function createStorefrontAuth(options: StorefrontAuthOptions): {
|
|
|
84
102
|
};
|
|
85
103
|
export type StorefrontAuth = ReturnType<typeof createStorefrontAuth>;
|
|
86
104
|
//#endregion
|
|
87
|
-
export { type AnonymousSession, ApiError, type LoginRequest, type SignupRequest, type SocialProvider, type TokenPair, type WithdrawRequest, type WithdrawalBlockedDetails };
|
|
105
|
+
export { type AnonymousSession, ApiError, type LoginRequest, type MemberIdentities, type SignupRequest, type SocialProvider, type TokenPair, type WithdrawRequest, type WithdrawalBlockedDetails };
|
package/dist/auth/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { gt as ApiError, t as createStorefrontClient } from "../client-B1uXGefY.mjs";
|
|
2
2
|
//#region src/auth/index.ts
|
|
3
3
|
/** 소셜 로그인 실패 — 구매자가 취소했거나(`access_denied`) state가 맞지 않는다 */
|
|
4
4
|
var IdpCallbackError = class extends Error {
|
|
@@ -22,7 +22,7 @@ async function s256(verifier) {
|
|
|
22
22
|
return base64url(new Uint8Array(digest));
|
|
23
23
|
}
|
|
24
24
|
/**
|
|
25
|
-
* 구매자 인증 — 화면은
|
|
25
|
+
* 구매자 인증 — 화면은 스토어프론트가 그리고, 계정 저장과 인증 처리는 sayren이 한다.
|
|
26
26
|
* 어느 방식으로 로그인해도 결과는 같은 구매자 토큰(`TokenPair`)이고 `/me/*`·장바구니·주문 API에 그대로 쓴다.
|
|
27
27
|
*
|
|
28
28
|
* 토큰 보관은 앱 몫이다. 서버(loader·action)에서 호출하고 httpOnly 쿠키에 두기를 권한다.
|
|
@@ -60,7 +60,7 @@ function createStorefrontAuth(options) {
|
|
|
60
60
|
/**
|
|
61
61
|
* 소셜 로그인 시작 — PKCE(S256)와 state를 만들고 공급자 로그인 주소를 받는다. 반환한 `state`·`codeVerifier`를
|
|
62
62
|
* 서버 세션에 보관하고 구매자를 `url`로 보낸다. 로그인을 마치면 `redirectUri`로 `?code=&state=`가 붙어 돌아온다.
|
|
63
|
-
* `redirectUri`는
|
|
63
|
+
* `redirectUri`는 스토어프론트 도메인 목록 안이어야 한다
|
|
64
64
|
*/
|
|
65
65
|
async idp(provider, options) {
|
|
66
66
|
const state = randomToken(24);
|
|
@@ -90,6 +90,45 @@ function createStorefrontAuth(options) {
|
|
|
90
90
|
cartToken: input.cartToken
|
|
91
91
|
});
|
|
92
92
|
},
|
|
93
|
+
/** 로그인 수단 — 비밀번호가 있는지, 어떤 소셜 계정이 연결됐는지 */
|
|
94
|
+
identities(accessToken) {
|
|
95
|
+
return client(accessToken).auth.identities();
|
|
96
|
+
},
|
|
97
|
+
/**
|
|
98
|
+
* 소셜 계정 연결 시작 — 로그인한 구매자에 공급자 계정을 붙인다. `idp()`처럼 `state`·`codeVerifier`를 보관하고
|
|
99
|
+
* 구매자를 `url`로 보낸다. 돌아오면 `linkIdpCallback()`으로 마무리한다. 연결은 흐름을 시작한 구매자에게만 된다
|
|
100
|
+
*/
|
|
101
|
+
async linkIdp(accessToken, provider, options) {
|
|
102
|
+
const state = randomToken(24);
|
|
103
|
+
const codeVerifier = randomToken(32);
|
|
104
|
+
const { url } = await client(accessToken).auth.linkIdentityAuthorize(provider, {
|
|
105
|
+
redirectUri: options.redirectUri,
|
|
106
|
+
codeChallenge: await s256(codeVerifier),
|
|
107
|
+
state
|
|
108
|
+
});
|
|
109
|
+
return {
|
|
110
|
+
url,
|
|
111
|
+
state,
|
|
112
|
+
codeVerifier
|
|
113
|
+
};
|
|
114
|
+
},
|
|
115
|
+
/**
|
|
116
|
+
* 소셜 계정 연결 마무리 — state를 비교하고 code를 바꿔 연결한다. 다른 구매자에 이미 연결된 계정이면
|
|
117
|
+
* 409 `IDENTITY_IN_USE`, 같은 공급자의 다른 계정을 이미 연결했으면 409 `PROVIDER_ALREADY_LINKED`
|
|
118
|
+
*/
|
|
119
|
+
linkIdpCallback(accessToken, provider, input) {
|
|
120
|
+
if (input.error) return Promise.reject(new IdpCallbackError(input.error));
|
|
121
|
+
if (!input.state || input.state !== input.expectedState) return Promise.reject(new IdpCallbackError("state_mismatch"));
|
|
122
|
+
if (!input.code) return Promise.reject(new IdpCallbackError("missing_code"));
|
|
123
|
+
return client(accessToken).auth.linkIdentity(provider, {
|
|
124
|
+
code: input.code,
|
|
125
|
+
codeVerifier: input.codeVerifier
|
|
126
|
+
});
|
|
127
|
+
},
|
|
128
|
+
/** 소셜 계정 연결 해제 — 마지막 로그인 수단이면 409 `LAST_LOGIN_METHOD` */
|
|
129
|
+
unlinkIdp(accessToken, provider) {
|
|
130
|
+
return client(accessToken).auth.unlinkIdentity(provider);
|
|
131
|
+
},
|
|
93
132
|
/** 토큰 갱신 — refreshToken은 한 번 쓰면 폐기되고 새 쌍이 온다(회전) */
|
|
94
133
|
refresh(refreshToken) {
|
|
95
134
|
return client().auth.refresh(refreshToken);
|
|
@@ -178,11 +178,11 @@ const socialProviderSchema = z.enum([
|
|
|
178
178
|
]);
|
|
179
179
|
/** `POST /auth/idp/{provider}/authorize` 본문 — PKCE(S256)와 state는 SDK가 만든다 */
|
|
180
180
|
const idpAuthorizeRequestSchema = z.object({
|
|
181
|
-
/** 로그인을 마치고 돌아올
|
|
181
|
+
/** 로그인을 마치고 돌아올 스토어프론트 주소. 스토어프론트 도메인 목록 안이어야 한다 */
|
|
182
182
|
redirectUri: z.url(),
|
|
183
183
|
/** base64url(SHA-256(codeVerifier)) */
|
|
184
184
|
codeChallenge: z.string().regex(/^[A-Za-z0-9_-]{43}$/),
|
|
185
|
-
/**
|
|
185
|
+
/** 스토어프론트가 만든 값 — 돌아올 때 그대로 붙어 온다. 세션에 저장해 두고 비교한다 */
|
|
186
186
|
state: z.string().min(16).max(128)
|
|
187
187
|
});
|
|
188
188
|
const idpAuthorizeResponseSchema = z.object({
|
|
@@ -195,6 +195,23 @@ const idpTokenRequestSchema = z.object({
|
|
|
195
195
|
/** 비회원 장바구니 토큰 — 주면 회원 장바구니로 합친다 */
|
|
196
196
|
cartToken: z.string().optional()
|
|
197
197
|
});
|
|
198
|
+
/** `GET /me/identities` — 구매자의 로그인 수단(비밀번호 여부와 연결된 소셜 계정) */
|
|
199
|
+
const memberIdentitiesSchema = z.object({
|
|
200
|
+
hasPassword: z.boolean(),
|
|
201
|
+
/** 셀러 자체 회원 연동(구매자 위임)으로 들어온 구매자 — 그 로그인도 로그인 수단으로 센다 */
|
|
202
|
+
hasExternalAuth: z.boolean(),
|
|
203
|
+
identities: z.array(z.object({
|
|
204
|
+
provider: socialProviderSchema,
|
|
205
|
+
/** 공급자가 알려 준 이메일(참고값). 없을 수 있다 */
|
|
206
|
+
email: z.string().nullable(),
|
|
207
|
+
linkedAt: z.string()
|
|
208
|
+
}))
|
|
209
|
+
});
|
|
210
|
+
/** `POST /me/identities/{provider}` 본문 — 연결 흐름에서 돌아온 1회용 code와 PKCE verifier */
|
|
211
|
+
const linkIdentityRequestSchema = z.object({
|
|
212
|
+
code: z.string().min(1),
|
|
213
|
+
codeVerifier: z.string().regex(/^[A-Za-z0-9._~-]{43,128}$/)
|
|
214
|
+
});
|
|
198
215
|
//#endregion
|
|
199
216
|
//#region src/schemas/cart.ts
|
|
200
217
|
const cartItemSchema = z.object({
|
|
@@ -232,6 +249,38 @@ const updateCartItemRequestSchema = z.object({
|
|
|
232
249
|
optionId: z.string().optional()
|
|
233
250
|
}).refine((value) => Object.values(value).some((v) => v !== void 0), { message: "최소 1개 필드가 필요합니다" });
|
|
234
251
|
//#endregion
|
|
252
|
+
//#region src/schemas/common.ts
|
|
253
|
+
const pageInfoSchema = z.object({
|
|
254
|
+
page: z.number().int(),
|
|
255
|
+
size: z.number().int(),
|
|
256
|
+
totalElements: z.number().int(),
|
|
257
|
+
totalPages: z.number().int()
|
|
258
|
+
});
|
|
259
|
+
function pageSchema(item) {
|
|
260
|
+
return pageInfoSchema.extend({ contents: z.array(item) });
|
|
261
|
+
}
|
|
262
|
+
const orderItemStatusSchema = z.enum([
|
|
263
|
+
"PAYMENT_WAITING",
|
|
264
|
+
"PAID",
|
|
265
|
+
"CONFIRMED",
|
|
266
|
+
"DISPATCHED",
|
|
267
|
+
"DELIVERING",
|
|
268
|
+
"DELIVERED",
|
|
269
|
+
"PURCHASE_DECIDED",
|
|
270
|
+
"CANCELED"
|
|
271
|
+
]);
|
|
272
|
+
const claimStatusSchema = z.enum([
|
|
273
|
+
"REQUESTED",
|
|
274
|
+
"HELD",
|
|
275
|
+
"COLLECTING",
|
|
276
|
+
"COLLECTED",
|
|
277
|
+
"REDELIVERING",
|
|
278
|
+
"APPROVED",
|
|
279
|
+
"REJECTED",
|
|
280
|
+
"COMPLETED",
|
|
281
|
+
"WITHDRAWN"
|
|
282
|
+
]);
|
|
283
|
+
//#endregion
|
|
235
284
|
//#region src/schemas/catalog.ts
|
|
236
285
|
const categoryNodeSchema = z.lazy(() => z.object({
|
|
237
286
|
categoryId: z.string(),
|
|
@@ -243,9 +292,9 @@ const productCardSchema = z.object({
|
|
|
243
292
|
productId: z.string(),
|
|
244
293
|
name: z.string(),
|
|
245
294
|
thumbnailUrl: z.url(),
|
|
246
|
-
salePrice: z.number().int(),
|
|
247
|
-
discountedPrice: z.number().int().nullable(),
|
|
248
|
-
discountRate: z.number().int().nullable(),
|
|
295
|
+
salePrice: z.number().int().describe("판매가. 원(KRW) 단위 정수"),
|
|
296
|
+
discountedPrice: z.number().int().nullable().describe("즉시 할인 적용가. 원 단위. 할인이 없으면 null"),
|
|
297
|
+
discountRate: z.number().int().nullable().describe("할인율(%). 할인이 없으면 null"),
|
|
249
298
|
freeShipping: z.boolean(),
|
|
250
299
|
averageRating: z.number(),
|
|
251
300
|
reviewCount: z.number().int(),
|
|
@@ -312,6 +361,8 @@ const productFacetsSchema = z.object({
|
|
|
312
361
|
max: z.number().int()
|
|
313
362
|
}).optional()
|
|
314
363
|
});
|
|
364
|
+
/** `GET /products` 응답 — 상품 카드 페이지와 검색 패싯 */
|
|
365
|
+
const productPageSchema = pageSchema(productCardSchema).extend({ facets: productFacetsSchema.optional() });
|
|
315
366
|
//#endregion
|
|
316
367
|
//#region src/schemas/checkout.ts
|
|
317
368
|
const createCheckoutRequestSchema = z.object({
|
|
@@ -420,38 +471,6 @@ const paymentStatusSchema = z.object({
|
|
|
420
471
|
expiresAt: z.string().describe("결제 요청 만료 시각")
|
|
421
472
|
});
|
|
422
473
|
//#endregion
|
|
423
|
-
//#region src/schemas/common.ts
|
|
424
|
-
const pageInfoSchema = z.object({
|
|
425
|
-
page: z.number().int(),
|
|
426
|
-
size: z.number().int(),
|
|
427
|
-
totalElements: z.number().int(),
|
|
428
|
-
totalPages: z.number().int()
|
|
429
|
-
});
|
|
430
|
-
function pageSchema(item) {
|
|
431
|
-
return pageInfoSchema.extend({ contents: z.array(item) });
|
|
432
|
-
}
|
|
433
|
-
const orderItemStatusSchema = z.enum([
|
|
434
|
-
"PAYMENT_WAITING",
|
|
435
|
-
"PAID",
|
|
436
|
-
"CONFIRMED",
|
|
437
|
-
"DISPATCHED",
|
|
438
|
-
"DELIVERING",
|
|
439
|
-
"DELIVERED",
|
|
440
|
-
"PURCHASE_DECIDED",
|
|
441
|
-
"CANCELED"
|
|
442
|
-
]);
|
|
443
|
-
const claimStatusSchema = z.enum([
|
|
444
|
-
"REQUESTED",
|
|
445
|
-
"HELD",
|
|
446
|
-
"COLLECTING",
|
|
447
|
-
"COLLECTED",
|
|
448
|
-
"REDELIVERING",
|
|
449
|
-
"APPROVED",
|
|
450
|
-
"REJECTED",
|
|
451
|
-
"COMPLETED",
|
|
452
|
-
"WITHDRAWN"
|
|
453
|
-
]);
|
|
454
|
-
//#endregion
|
|
455
474
|
//#region src/schemas/claim.ts
|
|
456
475
|
const claimTypeSchema = z.enum([
|
|
457
476
|
"CANCEL",
|
|
@@ -538,6 +557,11 @@ const createInquiryRequestSchema = z.object({
|
|
|
538
557
|
content: z.string().min(1).max(1e3),
|
|
539
558
|
secret: z.boolean().default(false)
|
|
540
559
|
});
|
|
560
|
+
/** `POST /products/{productId}/inquiries` 응답 */
|
|
561
|
+
const createInquiryResultSchema = z.object({
|
|
562
|
+
inquiryId: z.string(),
|
|
563
|
+
createdAt: z.string()
|
|
564
|
+
});
|
|
541
565
|
//#endregion
|
|
542
566
|
//#region src/schemas/member.ts
|
|
543
567
|
const memberSchema = z.object({
|
|
@@ -574,6 +598,11 @@ const memberAddressRequestSchema = z.object({
|
|
|
574
598
|
address2: z.string().optional(),
|
|
575
599
|
isDefault: z.boolean().default(false)
|
|
576
600
|
});
|
|
601
|
+
/** `POST /me/wishlist` 응답 — 이미 찜한 상품이어도 같은 응답이다 */
|
|
602
|
+
const wishlistAddResultSchema = z.object({
|
|
603
|
+
productId: z.string(),
|
|
604
|
+
wished: z.literal(true)
|
|
605
|
+
});
|
|
577
606
|
//#endregion
|
|
578
607
|
//#region src/schemas/order.ts
|
|
579
608
|
const myOrderItemSchema = z.object({
|
|
@@ -630,6 +659,12 @@ const deliveryTrackingSchema = z.object({
|
|
|
630
659
|
occurredAt: z.string()
|
|
631
660
|
}))
|
|
632
661
|
});
|
|
662
|
+
/** `POST /me/order-items/{orderItemId}/purchase-decision` 응답 */
|
|
663
|
+
const purchaseDecisionResultSchema = z.object({
|
|
664
|
+
orderItemId: z.string(),
|
|
665
|
+
status: z.literal("PURCHASE_DECIDED"),
|
|
666
|
+
decidedAt: z.string()
|
|
667
|
+
});
|
|
633
668
|
//#endregion
|
|
634
669
|
//#region src/schemas/review.ts
|
|
635
670
|
const publicReviewSchema = z.object({
|
|
@@ -672,10 +707,17 @@ const updateReviewRequestSchema = z.object({
|
|
|
672
707
|
content: z.string().min(10).max(2e3).optional(),
|
|
673
708
|
images: z.array(z.url()).max(5).optional()
|
|
674
709
|
}).refine((value) => Object.values(value).some((v) => v !== void 0), { message: "최소 1개 필드가 필요합니다" });
|
|
710
|
+
/** `GET /products/{productId}/reviews` 응답 — 리뷰 페이지와 평점 분포 */
|
|
711
|
+
const reviewPageSchema = pageSchema(publicReviewSchema).extend({ summary: reviewSummarySchema.optional() });
|
|
712
|
+
/** `PUT /me/reviews/{reviewId}` 응답 */
|
|
713
|
+
const updateReviewResultSchema = z.object({
|
|
714
|
+
reviewId: z.string(),
|
|
715
|
+
updatedAt: z.string()
|
|
716
|
+
});
|
|
675
717
|
//#endregion
|
|
676
718
|
//#region src/schemas/store.ts
|
|
677
719
|
/**
|
|
678
|
-
* 스토어 공개 설정 — `GET /store`.
|
|
720
|
+
* 스토어 공개 설정 — `GET /store`. 스토어프론트가 화면을 그릴 때 읽는 값만 담는다.
|
|
679
721
|
*/
|
|
680
722
|
const storefrontStoreSchema = z.object({
|
|
681
723
|
storeCode: z.string(),
|
|
@@ -700,8 +742,6 @@ const storefrontStoreSchema = z.object({
|
|
|
700
742
|
});
|
|
701
743
|
//#endregion
|
|
702
744
|
//#region src/client.ts
|
|
703
|
-
const productPageSchema = pageSchema(productCardSchema).extend({ facets: productFacetsSchema.optional() });
|
|
704
|
-
const reviewPageSchema = pageSchema(publicReviewSchema).extend({ summary: reviewSummarySchema.optional() });
|
|
705
745
|
function createStorefrontClient(options) {
|
|
706
746
|
const { session } = options;
|
|
707
747
|
const accessTokenSource = options.accessToken ?? options.auth?.accessToken ?? (session ? () => session.accessToken : void 0);
|
|
@@ -757,7 +797,15 @@ function createStorefrontClient(options) {
|
|
|
757
797
|
/** 소셜 로그인 마무리 — 돌아온 1회용 code + codeVerifier를 구매자 토큰으로 바꾼다 */
|
|
758
798
|
idpToken: (body) => http.request("POST", "/auth/idp/token", tokenPairSchema, { body: idpTokenRequestSchema.parse(body) }),
|
|
759
799
|
/** 구매자 탈퇴 — 로그인 상태(accessToken)에서 비밀번호로 본인 확인. 성공하면 토큰은 모두 무효 */
|
|
760
|
-
withdraw: (body) => http.requestVoid("POST", "/auth/withdraw", { body: withdrawRequestSchema.parse(body) })
|
|
800
|
+
withdraw: (body) => http.requestVoid("POST", "/auth/withdraw", { body: withdrawRequestSchema.parse(body) }),
|
|
801
|
+
/** 로그인 수단 — 비밀번호 여부와 연결된 소셜 계정(로그인 필요) */
|
|
802
|
+
identities: () => http.request("GET", "/me/identities", memberIdentitiesSchema),
|
|
803
|
+
/** 소셜 계정 연결 시작(로그인 필요) — 공급자 로그인 주소를 받는다. 본문은 `idpAuthorize`와 같다 */
|
|
804
|
+
linkIdentityAuthorize: (provider, body) => http.request("POST", path`/me/identities/${provider}/authorize`, idpAuthorizeResponseSchema, { body: idpAuthorizeRequestSchema.parse(body) }),
|
|
805
|
+
/** 소셜 계정 연결 마무리 — 돌아온 code + codeVerifier로 이 구매자에 연결한다 */
|
|
806
|
+
linkIdentity: (provider, body) => http.request("POST", path`/me/identities/${provider}`, memberIdentitiesSchema, { body: linkIdentityRequestSchema.parse(body) }),
|
|
807
|
+
/** 소셜 계정 연결 해제 — 마지막 로그인 수단이면 409 `LAST_LOGIN_METHOD` */
|
|
808
|
+
unlinkIdentity: (provider) => http.request("DELETE", path`/me/identities/${provider}`, memberIdentitiesSchema)
|
|
761
809
|
},
|
|
762
810
|
store: {
|
|
763
811
|
/** 스토어 공개 설정 — 이름·고객센터·비회원 주문 허용 여부 */
|
|
@@ -854,4 +902,4 @@ getStatus: (paymentId) => http.request("GET", path`/payments/${paymentId}`, paym
|
|
|
854
902
|
};
|
|
855
903
|
}
|
|
856
904
|
//#endregion
|
|
857
|
-
export {
|
|
905
|
+
export { pageSchema as $, createClaimResultSchema as A, paymentStatusSchema as B, publicInquirySchema as C, claimReasonSchema as D, customerInquirySchema as E, confirmPaymentResultSchema as F, productCardSchema as G, requestPaymentRequestSchema as H, createCheckoutRequestSchema as I, productPageSchema as J, productDetailSchema as K, guestInfoSchema as L, PAYMENT_STATUS_VALUES as M, checkoutSessionSchema as N, claimTypeSchema as O, confirmPaymentRequestSchema as P, pageInfoSchema as Q, paymentMethodSchema as R, createInquiryResultSchema as S, customerInquiryCategorySchema as T, shippingAddressInputSchema as U, pgParamsSchema as V, categoryNodeSchema as W, claimStatusSchema as X, productSortSchema as Y, orderItemStatusSchema as Z, memberAddressSchema as _, buildQuery as _t, publicReviewSchema as a, idpAuthorizeRequestSchema as at, wishlistAddResultSchema as b, updateReviewRequestSchema as c, linkIdentityRequestSchema as ct, deliveryTrackingSchema as d, signupRequestSchema as dt, addCartItemRequestSchema as et, myOrderItemSchema as f, socialProviderSchema as ft, memberAddressRequestSchema as g, ApiError as gt, purchaseDecisionResultSchema as h, withdrawalBlockedDetailsSchema as ht, createReviewResultSchema as i, anonymousSessionSchema as it, myClaimSchema as j, createClaimRequestSchema as k, updateReviewResultSchema as l, loginRequestSchema as lt, orderShippingAddressSchema as m, withdrawRequestSchema as mt, storefrontStoreSchema as n, cartSchema as nt, reviewPageSchema as o, idpAuthorizeResponseSchema as ot, myOrderSchema as p, tokenPairSchema as pt, productFacetsSchema as q, createReviewRequestSchema as r, updateCartItemRequestSchema as rt, reviewSummarySchema as s, idpTokenRequestSchema as st, createStorefrontClient as t, cartItemSchema as tt, writableReviewSchema as u, memberIdentitiesSchema as ut, memberSchema as v, toApiError as vt, createCustomerInquiryRequestSchema as w, createInquiryRequestSchema as x, updateProfileRequestSchema as y, unwrapData as yt, paymentParamsSchema as z };
|
|
@@ -75,6 +75,27 @@ declare const idpTokenRequestSchema: z.ZodObject<{
|
|
|
75
75
|
cartToken: z.ZodOptional<z.ZodString>;
|
|
76
76
|
}, z.core.$strip>;
|
|
77
77
|
type IdpTokenRequest = z.infer<typeof idpTokenRequestSchema>;
|
|
78
|
+
/** `GET /me/identities` — 구매자의 로그인 수단(비밀번호 여부와 연결된 소셜 계정) */
|
|
79
|
+
declare const memberIdentitiesSchema: z.ZodObject<{
|
|
80
|
+
hasPassword: z.ZodBoolean;
|
|
81
|
+
hasExternalAuth: z.ZodBoolean;
|
|
82
|
+
identities: z.ZodArray<z.ZodObject<{
|
|
83
|
+
provider: z.ZodEnum<{
|
|
84
|
+
kakao: "kakao";
|
|
85
|
+
naver: "naver";
|
|
86
|
+
google: "google";
|
|
87
|
+
}>;
|
|
88
|
+
email: z.ZodNullable<z.ZodString>;
|
|
89
|
+
linkedAt: z.ZodString;
|
|
90
|
+
}, z.core.$strip>>;
|
|
91
|
+
}, z.core.$strip>;
|
|
92
|
+
type MemberIdentities = z.infer<typeof memberIdentitiesSchema>;
|
|
93
|
+
/** `POST /me/identities/{provider}` 본문 — 연결 흐름에서 돌아온 1회용 code와 PKCE verifier */
|
|
94
|
+
declare const linkIdentityRequestSchema: z.ZodObject<{
|
|
95
|
+
code: z.ZodString;
|
|
96
|
+
codeVerifier: z.ZodString;
|
|
97
|
+
}, z.core.$strip>;
|
|
98
|
+
type LinkIdentityRequest = z.infer<typeof linkIdentityRequestSchema>;
|
|
78
99
|
//#endregion
|
|
79
100
|
//#region src/http.d.ts
|
|
80
101
|
declare class ApiError extends Error {
|
|
@@ -92,4 +113,4 @@ type TokenSource = string | null | undefined | (() => string | null | undefined)
|
|
|
92
113
|
type Query = Record<string, string | number | boolean | undefined>;
|
|
93
114
|
declare function buildQuery(query?: Query): string;
|
|
94
115
|
//#endregion
|
|
95
|
-
export {
|
|
116
|
+
export { signupRequestSchema as C, withdrawalBlockedDetailsSchema as D, withdrawRequestSchema as E, memberIdentitiesSchema as S, tokenPairSchema as T, idpAuthorizeRequestSchema as _, AnonymousSession as a, linkIdentityRequestSchema as b, LinkIdentityRequest as c, SignupRequest as d, SocialProvider as f, anonymousSessionSchema as g, WithdrawalBlockedDetails as h, buildQuery as i, LoginRequest as l, WithdrawRequest as m, Query as n, IdpAuthorizeRequest as o, TokenPair as p, TokenSource as r, IdpTokenRequest as s, ApiError as t, MemberIdentities as u, idpAuthorizeResponseSchema as v, socialProviderSchema as w, loginRequestSchema as x, idpTokenRequestSchema as y };
|
package/dist/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { C as withdrawalBlockedDetailsSchema,
|
|
1
|
+
import { C as signupRequestSchema, D as withdrawalBlockedDetailsSchema, E as withdrawRequestSchema, S as memberIdentitiesSchema, T as tokenPairSchema, _ as idpAuthorizeRequestSchema, a as AnonymousSession, b as linkIdentityRequestSchema, c as LinkIdentityRequest, d as SignupRequest, f as SocialProvider, g as anonymousSessionSchema, h as WithdrawalBlockedDetails, i as buildQuery, l as LoginRequest, m as WithdrawRequest, n as Query, o as IdpAuthorizeRequest, p as TokenPair, r as TokenSource, s as IdpTokenRequest, t as ApiError, u as MemberIdentities, v as idpAuthorizeResponseSchema, w as socialProviderSchema, x as loginRequestSchema, y as idpTokenRequestSchema } from "./http-BbFHVGBf.mjs";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
//#region src/analytics-ids.d.ts
|
|
4
4
|
/**
|
|
@@ -213,6 +213,37 @@ export declare const productFacetsSchema: z.ZodObject<{
|
|
|
213
213
|
}, z.core.$strip>>;
|
|
214
214
|
}, z.core.$strip>;
|
|
215
215
|
export type ProductFacets = z.infer<typeof productFacetsSchema>;
|
|
216
|
+
/** `GET /products` 응답 — 상품 카드 페이지와 검색 패싯 */
|
|
217
|
+
export declare const productPageSchema: z.ZodObject<{
|
|
218
|
+
page: z.ZodNumber;
|
|
219
|
+
size: z.ZodNumber;
|
|
220
|
+
totalElements: z.ZodNumber;
|
|
221
|
+
totalPages: z.ZodNumber;
|
|
222
|
+
contents: z.ZodArray<z.ZodObject<{
|
|
223
|
+
productId: z.ZodString;
|
|
224
|
+
name: z.ZodString;
|
|
225
|
+
thumbnailUrl: z.ZodURL;
|
|
226
|
+
salePrice: z.ZodNumber;
|
|
227
|
+
discountedPrice: z.ZodNullable<z.ZodNumber>;
|
|
228
|
+
discountRate: z.ZodNullable<z.ZodNumber>;
|
|
229
|
+
freeShipping: z.ZodBoolean;
|
|
230
|
+
averageRating: z.ZodNumber;
|
|
231
|
+
reviewCount: z.ZodNumber;
|
|
232
|
+
soldOut: z.ZodBoolean;
|
|
233
|
+
}, z.core.$strip>>;
|
|
234
|
+
facets: z.ZodOptional<z.ZodObject<{
|
|
235
|
+
categories: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
236
|
+
categoryId: z.ZodString;
|
|
237
|
+
name: z.ZodString;
|
|
238
|
+
count: z.ZodNumber;
|
|
239
|
+
}, z.core.$strip>>>;
|
|
240
|
+
priceRange: z.ZodOptional<z.ZodObject<{
|
|
241
|
+
min: z.ZodNumber;
|
|
242
|
+
max: z.ZodNumber;
|
|
243
|
+
}, z.core.$strip>>;
|
|
244
|
+
}, z.core.$strip>>;
|
|
245
|
+
}, z.core.$strip>;
|
|
246
|
+
export type ProductPage = z.infer<typeof productPageSchema>;
|
|
216
247
|
//#endregion
|
|
217
248
|
//#region src/schemas/checkout.d.ts
|
|
218
249
|
export declare const createCheckoutRequestSchema: z.ZodObject<{
|
|
@@ -485,6 +516,12 @@ export declare const createInquiryRequestSchema: z.ZodObject<{
|
|
|
485
516
|
secret: z.ZodDefault<z.ZodBoolean>;
|
|
486
517
|
}, z.core.$strip>;
|
|
487
518
|
export type CreateInquiryRequest = z.infer<typeof createInquiryRequestSchema>;
|
|
519
|
+
/** `POST /products/{productId}/inquiries` 응답 */
|
|
520
|
+
export declare const createInquiryResultSchema: z.ZodObject<{
|
|
521
|
+
inquiryId: z.ZodString;
|
|
522
|
+
createdAt: z.ZodString;
|
|
523
|
+
}, z.core.$strip>;
|
|
524
|
+
export type CreateInquiryResult = z.infer<typeof createInquiryResultSchema>;
|
|
488
525
|
//#endregion
|
|
489
526
|
//#region src/schemas/member.d.ts
|
|
490
527
|
export declare const memberSchema: z.ZodObject<{
|
|
@@ -524,6 +561,12 @@ export declare const memberAddressRequestSchema: z.ZodObject<{
|
|
|
524
561
|
isDefault: z.ZodDefault<z.ZodBoolean>;
|
|
525
562
|
}, z.core.$strip>;
|
|
526
563
|
export type MemberAddressRequest = z.infer<typeof memberAddressRequestSchema>;
|
|
564
|
+
/** `POST /me/wishlist` 응답 — 이미 찜한 상품이어도 같은 응답이다 */
|
|
565
|
+
export declare const wishlistAddResultSchema: z.ZodObject<{
|
|
566
|
+
productId: z.ZodString;
|
|
567
|
+
wished: z.ZodLiteral<true>;
|
|
568
|
+
}, z.core.$strip>;
|
|
569
|
+
export type WishlistAddResult = z.infer<typeof wishlistAddResultSchema>;
|
|
527
570
|
//#endregion
|
|
528
571
|
//#region src/schemas/review.d.ts
|
|
529
572
|
export declare const publicReviewSchema: z.ZodObject<{
|
|
@@ -572,6 +615,38 @@ export declare const updateReviewRequestSchema: z.ZodObject<{
|
|
|
572
615
|
images: z.ZodOptional<z.ZodArray<z.ZodURL>>;
|
|
573
616
|
}, z.core.$strip>;
|
|
574
617
|
export type UpdateReviewRequest = z.infer<typeof updateReviewRequestSchema>;
|
|
618
|
+
/** `GET /products/{productId}/reviews` 응답 — 리뷰 페이지와 평점 분포 */
|
|
619
|
+
export declare const reviewPageSchema: z.ZodObject<{
|
|
620
|
+
page: z.ZodNumber;
|
|
621
|
+
size: z.ZodNumber;
|
|
622
|
+
totalElements: z.ZodNumber;
|
|
623
|
+
totalPages: z.ZodNumber;
|
|
624
|
+
contents: z.ZodArray<z.ZodObject<{
|
|
625
|
+
reviewId: z.ZodString;
|
|
626
|
+
rating: z.ZodNumber;
|
|
627
|
+
content: z.ZodString;
|
|
628
|
+
images: z.ZodArray<z.ZodURL>;
|
|
629
|
+
optionName: z.ZodNullable<z.ZodString>;
|
|
630
|
+
writerMaskedName: z.ZodString;
|
|
631
|
+
sellerReply: z.ZodNullable<z.ZodObject<{
|
|
632
|
+
content: z.ZodString;
|
|
633
|
+
repliedAt: z.ZodString;
|
|
634
|
+
}, z.core.$strip>>;
|
|
635
|
+
createdAt: z.ZodString;
|
|
636
|
+
}, z.core.$strip>>;
|
|
637
|
+
summary: z.ZodOptional<z.ZodObject<{
|
|
638
|
+
averageRating: z.ZodNumber;
|
|
639
|
+
totalCount: z.ZodNumber;
|
|
640
|
+
distribution: z.ZodRecord<z.ZodString, z.ZodNumber>;
|
|
641
|
+
}, z.core.$strip>>;
|
|
642
|
+
}, z.core.$strip>;
|
|
643
|
+
export type ReviewPage = z.infer<typeof reviewPageSchema>;
|
|
644
|
+
/** `PUT /me/reviews/{reviewId}` 응답 */
|
|
645
|
+
export declare const updateReviewResultSchema: z.ZodObject<{
|
|
646
|
+
reviewId: z.ZodString;
|
|
647
|
+
updatedAt: z.ZodString;
|
|
648
|
+
}, z.core.$strip>;
|
|
649
|
+
export type UpdateReviewResult = z.infer<typeof updateReviewResultSchema>;
|
|
575
650
|
//#endregion
|
|
576
651
|
//#region src/session.d.ts
|
|
577
652
|
/**
|
|
@@ -713,6 +788,40 @@ export declare function createStorefrontClient(options: StorefrontClientOptions)
|
|
|
713
788
|
}>;
|
|
714
789
|
/** 구매자 탈퇴 — 로그인 상태(accessToken)에서 비밀번호로 본인 확인. 성공하면 토큰은 모두 무효 */
|
|
715
790
|
withdraw: (body: WithdrawRequest) => Promise<void>;
|
|
791
|
+
/** 로그인 수단 — 비밀번호 여부와 연결된 소셜 계정(로그인 필요) */
|
|
792
|
+
identities: () => Promise<{
|
|
793
|
+
hasPassword: boolean;
|
|
794
|
+
hasExternalAuth: boolean;
|
|
795
|
+
identities: {
|
|
796
|
+
provider: "kakao" | "naver" | "google";
|
|
797
|
+
email: string | null;
|
|
798
|
+
linkedAt: string;
|
|
799
|
+
}[];
|
|
800
|
+
}>;
|
|
801
|
+
/** 소셜 계정 연결 시작(로그인 필요) — 공급자 로그인 주소를 받는다. 본문은 `idpAuthorize`와 같다 */
|
|
802
|
+
linkIdentityAuthorize: (provider: SocialProvider, body: IdpAuthorizeRequest) => Promise<{
|
|
803
|
+
url: string;
|
|
804
|
+
}>;
|
|
805
|
+
/** 소셜 계정 연결 마무리 — 돌아온 code + codeVerifier로 이 구매자에 연결한다 */
|
|
806
|
+
linkIdentity: (provider: SocialProvider, body: LinkIdentityRequest) => Promise<{
|
|
807
|
+
hasPassword: boolean;
|
|
808
|
+
hasExternalAuth: boolean;
|
|
809
|
+
identities: {
|
|
810
|
+
provider: "kakao" | "naver" | "google";
|
|
811
|
+
email: string | null;
|
|
812
|
+
linkedAt: string;
|
|
813
|
+
}[];
|
|
814
|
+
}>;
|
|
815
|
+
/** 소셜 계정 연결 해제 — 마지막 로그인 수단이면 409 `LAST_LOGIN_METHOD` */
|
|
816
|
+
unlinkIdentity: (provider: SocialProvider) => Promise<{
|
|
817
|
+
hasPassword: boolean;
|
|
818
|
+
hasExternalAuth: boolean;
|
|
819
|
+
identities: {
|
|
820
|
+
provider: "kakao" | "naver" | "google";
|
|
821
|
+
email: string | null;
|
|
822
|
+
linkedAt: string;
|
|
823
|
+
}[];
|
|
824
|
+
}>;
|
|
716
825
|
};
|
|
717
826
|
store: {
|
|
718
827
|
/** 스토어 공개 설정 — 이름·고객센터·비회원 주문 허용 여부 */
|
|
@@ -1506,10 +1615,17 @@ export declare const deliveryTrackingSchema: z.ZodObject<{
|
|
|
1506
1615
|
}, z.core.$strip>>;
|
|
1507
1616
|
}, z.core.$strip>;
|
|
1508
1617
|
export type DeliveryTracking = z.infer<typeof deliveryTrackingSchema>;
|
|
1618
|
+
/** `POST /me/order-items/{orderItemId}/purchase-decision` 응답 */
|
|
1619
|
+
export declare const purchaseDecisionResultSchema: z.ZodObject<{
|
|
1620
|
+
orderItemId: z.ZodString;
|
|
1621
|
+
status: z.ZodLiteral<"PURCHASE_DECIDED">;
|
|
1622
|
+
decidedAt: z.ZodString;
|
|
1623
|
+
}, z.core.$strip>;
|
|
1624
|
+
export type PurchaseDecisionResult = z.infer<typeof purchaseDecisionResultSchema>;
|
|
1509
1625
|
//#endregion
|
|
1510
1626
|
//#region src/schemas/store.d.ts
|
|
1511
1627
|
/**
|
|
1512
|
-
* 스토어 공개 설정 — `GET /store`.
|
|
1628
|
+
* 스토어 공개 설정 — `GET /store`. 스토어프론트가 화면을 그릴 때 읽는 값만 담는다.
|
|
1513
1629
|
*/
|
|
1514
1630
|
export declare const storefrontStoreSchema: z.ZodObject<{
|
|
1515
1631
|
storeCode: z.ZodString;
|
|
@@ -1527,4 +1643,4 @@ export declare const storefrontStoreSchema: z.ZodObject<{
|
|
|
1527
1643
|
}, z.core.$strip>;
|
|
1528
1644
|
export type StorefrontStore = z.infer<typeof storefrontStoreSchema>;
|
|
1529
1645
|
//#endregion
|
|
1530
|
-
export { AnonymousSession, ApiError, IdpAuthorizeRequest, IdpTokenRequest, LoginRequest, type Query, type SessionStorage, SignupRequest, SocialProvider, type StorefrontClient, type StorefrontClientOptions, type StorefrontSession, type StorefrontSessionOptions, TokenPair, type TokenSource, WithdrawRequest, WithdrawalBlockedDetails, anonymousSessionSchema, buildQuery, idpAuthorizeRequestSchema, idpAuthorizeResponseSchema, idpTokenRequestSchema, loginRequestSchema, signupRequestSchema, socialProviderSchema, tokenPairSchema, withdrawRequestSchema, withdrawalBlockedDetailsSchema };
|
|
1646
|
+
export { AnonymousSession, ApiError, IdpAuthorizeRequest, IdpTokenRequest, LinkIdentityRequest, LoginRequest, MemberIdentities, type Query, type SessionStorage, SignupRequest, SocialProvider, type StorefrontClient, type StorefrontClientOptions, type StorefrontSession, type StorefrontSessionOptions, TokenPair, type TokenSource, WithdrawRequest, WithdrawalBlockedDetails, anonymousSessionSchema, buildQuery, idpAuthorizeRequestSchema, idpAuthorizeResponseSchema, idpTokenRequestSchema, linkIdentityRequestSchema, loginRequestSchema, memberIdentitiesSchema, signupRequestSchema, socialProviderSchema, tokenPairSchema, withdrawRequestSchema, withdrawalBlockedDetailsSchema };
|
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { a as ANALYTICS_VISITOR_HEADER, c as analyticsClientEventSchema, i as ANALYTICS_VISITOR_COOKIE, l as analyticsIdSchema, n as ANALYTICS_SESSION_COOKIE, o as analyticsBatchSchema, r as ANALYTICS_SESSION_HEADER, s as analyticsClientEventNameSchema, t as ANALYTICS_MAX_BATCH_BYTES, u as analyticsServerEventNameSchema } from "./analytics-CgD70OqH.mjs";
|
|
2
2
|
import { t as analyticsIdsFromCookie } from "./analytics-ids-COElZiI3.mjs";
|
|
3
|
-
import { $ as
|
|
3
|
+
import { $ as pageSchema, A as createClaimResultSchema, B as paymentStatusSchema, C as publicInquirySchema, D as claimReasonSchema, E as customerInquirySchema, F as confirmPaymentResultSchema, G as productCardSchema, H as requestPaymentRequestSchema, I as createCheckoutRequestSchema, J as productPageSchema, K as productDetailSchema, L as guestInfoSchema, M as PAYMENT_STATUS_VALUES, N as checkoutSessionSchema, O as claimTypeSchema, P as confirmPaymentRequestSchema, Q as pageInfoSchema, R as paymentMethodSchema, S as createInquiryResultSchema, T as customerInquiryCategorySchema, U as shippingAddressInputSchema, V as pgParamsSchema, W as categoryNodeSchema, X as claimStatusSchema, Y as productSortSchema, Z as orderItemStatusSchema, _ as memberAddressSchema, _t as buildQuery, a as publicReviewSchema, at as idpAuthorizeRequestSchema, b as wishlistAddResultSchema, c as updateReviewRequestSchema, ct as linkIdentityRequestSchema, d as deliveryTrackingSchema, dt as signupRequestSchema, et as addCartItemRequestSchema, f as myOrderItemSchema, ft as socialProviderSchema, g as memberAddressRequestSchema, gt as ApiError, h as purchaseDecisionResultSchema, ht as withdrawalBlockedDetailsSchema, i as createReviewResultSchema, it as anonymousSessionSchema, j as myClaimSchema, k as createClaimRequestSchema, l as updateReviewResultSchema, lt as loginRequestSchema, m as orderShippingAddressSchema, mt as withdrawRequestSchema, n as storefrontStoreSchema, nt as cartSchema, o as reviewPageSchema, ot as idpAuthorizeResponseSchema, p as myOrderSchema, pt as tokenPairSchema, q as productFacetsSchema, r as createReviewRequestSchema, rt as updateCartItemRequestSchema, s as reviewSummarySchema, st as idpTokenRequestSchema, t as createStorefrontClient, tt as cartItemSchema, u as writableReviewSchema, ut as memberIdentitiesSchema, v as memberSchema, vt as toApiError, w as createCustomerInquiryRequestSchema, x as createInquiryRequestSchema, y as updateProfileRequestSchema, yt as unwrapData, z as paymentParamsSchema } from "./client-B1uXGefY.mjs";
|
|
4
4
|
//#region src/session.ts
|
|
5
5
|
/** 세션 키 상수 — SDK가 소유한다. store-sdk와 겹치지 않는 별도 네임스페이스. */
|
|
6
6
|
const STOREFRONT_SESSION_KEYS = {
|
|
@@ -124,4 +124,4 @@ function createStorefrontSession(options) {
|
|
|
124
124
|
};
|
|
125
125
|
}
|
|
126
126
|
//#endregion
|
|
127
|
-
export { ANALYTICS_MAX_BATCH_BYTES, ANALYTICS_SESSION_COOKIE, ANALYTICS_SESSION_HEADER, ANALYTICS_VISITOR_COOKIE, ANALYTICS_VISITOR_HEADER, ApiError, PAYMENT_STATUS_VALUES, STOREFRONT_SESSION_KEYS, addCartItemRequestSchema, analyticsBatchSchema, analyticsClientEventNameSchema, analyticsClientEventSchema, analyticsIdSchema, analyticsIdsFromCookie, analyticsServerEventNameSchema, anonymousSessionSchema, buildQuery, cartItemSchema, cartSchema, categoryNodeSchema, checkoutSessionSchema, claimReasonSchema, claimStatusSchema, claimTypeSchema, confirmPaymentRequestSchema, confirmPaymentResultSchema, createCheckoutRequestSchema, createClaimRequestSchema, createClaimResultSchema, createCustomerInquiryRequestSchema, createInquiryRequestSchema, createReviewRequestSchema, createReviewResultSchema, createStorefrontClient, createStorefrontSession, customerInquiryCategorySchema, customerInquirySchema, deliveryTrackingSchema, guestInfoSchema, idpAuthorizeRequestSchema, idpAuthorizeResponseSchema, idpTokenRequestSchema, loginRequestSchema, memberAddressRequestSchema, memberAddressSchema, memberSchema, myClaimSchema, myOrderItemSchema, myOrderSchema, orderItemStatusSchema, orderShippingAddressSchema, pageInfoSchema, pageSchema, paymentMethodSchema, paymentParamsSchema, paymentStatusSchema, pgParamsSchema, productCardSchema, productDetailSchema, productFacetsSchema, productSortSchema, publicInquirySchema, publicReviewSchema, requestPaymentRequestSchema, reviewSummarySchema, shippingAddressInputSchema, signupRequestSchema, socialProviderSchema, storefrontStoreSchema, tokenPairSchema, updateCartItemRequestSchema, updateProfileRequestSchema, updateReviewRequestSchema, withdrawRequestSchema, withdrawalBlockedDetailsSchema, writableReviewSchema };
|
|
127
|
+
export { ANALYTICS_MAX_BATCH_BYTES, ANALYTICS_SESSION_COOKIE, ANALYTICS_SESSION_HEADER, ANALYTICS_VISITOR_COOKIE, ANALYTICS_VISITOR_HEADER, ApiError, PAYMENT_STATUS_VALUES, STOREFRONT_SESSION_KEYS, addCartItemRequestSchema, analyticsBatchSchema, analyticsClientEventNameSchema, analyticsClientEventSchema, analyticsIdSchema, analyticsIdsFromCookie, analyticsServerEventNameSchema, anonymousSessionSchema, buildQuery, cartItemSchema, cartSchema, categoryNodeSchema, checkoutSessionSchema, claimReasonSchema, claimStatusSchema, claimTypeSchema, confirmPaymentRequestSchema, confirmPaymentResultSchema, createCheckoutRequestSchema, createClaimRequestSchema, createClaimResultSchema, createCustomerInquiryRequestSchema, createInquiryRequestSchema, createInquiryResultSchema, createReviewRequestSchema, createReviewResultSchema, createStorefrontClient, createStorefrontSession, customerInquiryCategorySchema, customerInquirySchema, deliveryTrackingSchema, guestInfoSchema, idpAuthorizeRequestSchema, idpAuthorizeResponseSchema, idpTokenRequestSchema, linkIdentityRequestSchema, loginRequestSchema, memberAddressRequestSchema, memberAddressSchema, memberIdentitiesSchema, memberSchema, myClaimSchema, myOrderItemSchema, myOrderSchema, orderItemStatusSchema, orderShippingAddressSchema, pageInfoSchema, pageSchema, paymentMethodSchema, paymentParamsSchema, paymentStatusSchema, pgParamsSchema, productCardSchema, productDetailSchema, productFacetsSchema, productPageSchema, productSortSchema, publicInquirySchema, publicReviewSchema, purchaseDecisionResultSchema, requestPaymentRequestSchema, reviewPageSchema, reviewSummarySchema, shippingAddressInputSchema, signupRequestSchema, socialProviderSchema, storefrontStoreSchema, tokenPairSchema, updateCartItemRequestSchema, updateProfileRequestSchema, updateReviewRequestSchema, updateReviewResultSchema, wishlistAddResultSchema, withdrawRequestSchema, withdrawalBlockedDetailsSchema, writableReviewSchema };
|
package/package.json
CHANGED
package/src/auth/index.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { ApiError } from "../http";
|
|
|
3
3
|
import type {
|
|
4
4
|
AnonymousSession,
|
|
5
5
|
LoginRequest,
|
|
6
|
+
MemberIdentities,
|
|
6
7
|
SignupRequest,
|
|
7
8
|
SocialProvider,
|
|
8
9
|
TokenPair,
|
|
@@ -14,6 +15,7 @@ export { ApiError } from "../http";
|
|
|
14
15
|
export type {
|
|
15
16
|
AnonymousSession,
|
|
16
17
|
LoginRequest,
|
|
18
|
+
MemberIdentities,
|
|
17
19
|
SignupRequest,
|
|
18
20
|
SocialProvider,
|
|
19
21
|
TokenPair,
|
|
@@ -78,7 +80,7 @@ export interface StorefrontAuthOptions {
|
|
|
78
80
|
}
|
|
79
81
|
|
|
80
82
|
/**
|
|
81
|
-
* 구매자 인증 — 화면은
|
|
83
|
+
* 구매자 인증 — 화면은 스토어프론트가 그리고, 계정 저장과 인증 처리는 sayren이 한다.
|
|
82
84
|
* 어느 방식으로 로그인해도 결과는 같은 구매자 토큰(`TokenPair`)이고 `/me/*`·장바구니·주문 API에 그대로 쓴다.
|
|
83
85
|
*
|
|
84
86
|
* 토큰 보관은 앱 몫이다. 서버(loader·action)에서 호출하고 httpOnly 쿠키에 두기를 권한다.
|
|
@@ -118,7 +120,7 @@ export function createStorefrontAuth(options: StorefrontAuthOptions) {
|
|
|
118
120
|
/**
|
|
119
121
|
* 소셜 로그인 시작 — PKCE(S256)와 state를 만들고 공급자 로그인 주소를 받는다. 반환한 `state`·`codeVerifier`를
|
|
120
122
|
* 서버 세션에 보관하고 구매자를 `url`로 보낸다. 로그인을 마치면 `redirectUri`로 `?code=&state=`가 붙어 돌아온다.
|
|
121
|
-
* `redirectUri`는
|
|
123
|
+
* `redirectUri`는 스토어프론트 도메인 목록 안이어야 한다
|
|
122
124
|
*/
|
|
123
125
|
async idp(provider: SocialProvider, options: { redirectUri: string }): Promise<IdpStart> {
|
|
124
126
|
const state = randomToken(24);
|
|
@@ -146,6 +148,51 @@ export function createStorefrontAuth(options: StorefrontAuthOptions) {
|
|
|
146
148
|
cartToken: input.cartToken,
|
|
147
149
|
});
|
|
148
150
|
},
|
|
151
|
+
/** 로그인 수단 — 비밀번호가 있는지, 어떤 소셜 계정이 연결됐는지 */
|
|
152
|
+
identities(accessToken: string): Promise<MemberIdentities> {
|
|
153
|
+
return client(accessToken).auth.identities();
|
|
154
|
+
},
|
|
155
|
+
/**
|
|
156
|
+
* 소셜 계정 연결 시작 — 로그인한 구매자에 공급자 계정을 붙인다. `idp()`처럼 `state`·`codeVerifier`를 보관하고
|
|
157
|
+
* 구매자를 `url`로 보낸다. 돌아오면 `linkIdpCallback()`으로 마무리한다. 연결은 흐름을 시작한 구매자에게만 된다
|
|
158
|
+
*/
|
|
159
|
+
async linkIdp(
|
|
160
|
+
accessToken: string,
|
|
161
|
+
provider: SocialProvider,
|
|
162
|
+
options: { redirectUri: string },
|
|
163
|
+
): Promise<IdpStart> {
|
|
164
|
+
const state = randomToken(24);
|
|
165
|
+
const codeVerifier = randomToken(32);
|
|
166
|
+
const { url } = await client(accessToken).auth.linkIdentityAuthorize(provider, {
|
|
167
|
+
redirectUri: options.redirectUri,
|
|
168
|
+
codeChallenge: await s256(codeVerifier),
|
|
169
|
+
state,
|
|
170
|
+
});
|
|
171
|
+
return { url, state, codeVerifier };
|
|
172
|
+
},
|
|
173
|
+
/**
|
|
174
|
+
* 소셜 계정 연결 마무리 — state를 비교하고 code를 바꿔 연결한다. 다른 구매자에 이미 연결된 계정이면
|
|
175
|
+
* 409 `IDENTITY_IN_USE`, 같은 공급자의 다른 계정을 이미 연결했으면 409 `PROVIDER_ALREADY_LINKED`
|
|
176
|
+
*/
|
|
177
|
+
linkIdpCallback(
|
|
178
|
+
accessToken: string,
|
|
179
|
+
provider: SocialProvider,
|
|
180
|
+
input: Omit<IdpCallbackInput, "cartToken"> & { error?: string | null },
|
|
181
|
+
): Promise<MemberIdentities> {
|
|
182
|
+
if (input.error) return Promise.reject(new IdpCallbackError(input.error));
|
|
183
|
+
if (!input.state || input.state !== input.expectedState) {
|
|
184
|
+
return Promise.reject(new IdpCallbackError("state_mismatch"));
|
|
185
|
+
}
|
|
186
|
+
if (!input.code) return Promise.reject(new IdpCallbackError("missing_code"));
|
|
187
|
+
return client(accessToken).auth.linkIdentity(provider, {
|
|
188
|
+
code: input.code,
|
|
189
|
+
codeVerifier: input.codeVerifier,
|
|
190
|
+
});
|
|
191
|
+
},
|
|
192
|
+
/** 소셜 계정 연결 해제 — 마지막 로그인 수단이면 409 `LAST_LOGIN_METHOD` */
|
|
193
|
+
unlinkIdp(accessToken: string, provider: SocialProvider): Promise<MemberIdentities> {
|
|
194
|
+
return client(accessToken).auth.unlinkIdentity(provider);
|
|
195
|
+
},
|
|
149
196
|
/** 토큰 갱신 — refreshToken은 한 번 쓰면 폐기되고 새 쌍이 온다(회전) */
|
|
150
197
|
refresh(refreshToken: string): Promise<TokenPair> {
|
|
151
198
|
return client().auth.refresh(refreshToken);
|
package/src/client.ts
CHANGED
|
@@ -9,7 +9,10 @@ import {
|
|
|
9
9
|
idpAuthorizeRequestSchema,
|
|
10
10
|
idpAuthorizeResponseSchema,
|
|
11
11
|
idpTokenRequestSchema,
|
|
12
|
+
type LinkIdentityRequest,
|
|
13
|
+
linkIdentityRequestSchema,
|
|
12
14
|
loginRequestSchema,
|
|
15
|
+
memberIdentitiesSchema,
|
|
13
16
|
type SocialProvider,
|
|
14
17
|
signupRequestSchema,
|
|
15
18
|
tokenPairSchema,
|
|
@@ -28,7 +31,7 @@ import {
|
|
|
28
31
|
type ProductSearchParams,
|
|
29
32
|
productCardSchema,
|
|
30
33
|
productDetailSchema,
|
|
31
|
-
|
|
34
|
+
productPageSchema,
|
|
32
35
|
} from "./schemas/catalog";
|
|
33
36
|
import {
|
|
34
37
|
type CreateCheckoutRequest,
|
|
@@ -73,8 +76,7 @@ import {
|
|
|
73
76
|
type CreateReviewRequest,
|
|
74
77
|
createReviewRequestSchema,
|
|
75
78
|
createReviewResultSchema,
|
|
76
|
-
|
|
77
|
-
reviewSummarySchema,
|
|
79
|
+
reviewPageSchema,
|
|
78
80
|
type UpdateReviewRequest,
|
|
79
81
|
updateReviewRequestSchema,
|
|
80
82
|
writableReviewSchema,
|
|
@@ -117,14 +119,6 @@ export interface StorefrontClientOptions {
|
|
|
117
119
|
fetch?: typeof globalThis.fetch;
|
|
118
120
|
}
|
|
119
121
|
|
|
120
|
-
const productPageSchema = pageSchema(productCardSchema).extend({
|
|
121
|
-
facets: productFacetsSchema.optional(),
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
const reviewPageSchema = pageSchema(publicReviewSchema).extend({
|
|
125
|
-
summary: reviewSummarySchema.optional(),
|
|
126
|
-
});
|
|
127
|
-
|
|
128
122
|
export function createStorefrontClient(options: StorefrontClientOptions) {
|
|
129
123
|
const { session } = options;
|
|
130
124
|
|
|
@@ -203,6 +197,24 @@ export function createStorefrontClient(options: StorefrontClientOptions) {
|
|
|
203
197
|
/** 구매자 탈퇴 — 로그인 상태(accessToken)에서 비밀번호로 본인 확인. 성공하면 토큰은 모두 무효 */
|
|
204
198
|
withdraw: (body: WithdrawRequest) =>
|
|
205
199
|
http.requestVoid("POST", "/auth/withdraw", { body: withdrawRequestSchema.parse(body) }),
|
|
200
|
+
/** 로그인 수단 — 비밀번호 여부와 연결된 소셜 계정(로그인 필요) */
|
|
201
|
+
identities: () => http.request("GET", "/me/identities", memberIdentitiesSchema),
|
|
202
|
+
/** 소셜 계정 연결 시작(로그인 필요) — 공급자 로그인 주소를 받는다. 본문은 `idpAuthorize`와 같다 */
|
|
203
|
+
linkIdentityAuthorize: (provider: SocialProvider, body: IdpAuthorizeRequest) =>
|
|
204
|
+
http.request(
|
|
205
|
+
"POST",
|
|
206
|
+
path`/me/identities/${provider}/authorize`,
|
|
207
|
+
idpAuthorizeResponseSchema,
|
|
208
|
+
{ body: idpAuthorizeRequestSchema.parse(body) },
|
|
209
|
+
),
|
|
210
|
+
/** 소셜 계정 연결 마무리 — 돌아온 code + codeVerifier로 이 구매자에 연결한다 */
|
|
211
|
+
linkIdentity: (provider: SocialProvider, body: LinkIdentityRequest) =>
|
|
212
|
+
http.request("POST", path`/me/identities/${provider}`, memberIdentitiesSchema, {
|
|
213
|
+
body: linkIdentityRequestSchema.parse(body),
|
|
214
|
+
}),
|
|
215
|
+
/** 소셜 계정 연결 해제 — 마지막 로그인 수단이면 409 `LAST_LOGIN_METHOD` */
|
|
216
|
+
unlinkIdentity: (provider: SocialProvider) =>
|
|
217
|
+
http.request("DELETE", path`/me/identities/${provider}`, memberIdentitiesSchema),
|
|
206
218
|
},
|
|
207
219
|
|
|
208
220
|
store: {
|
package/src/schemas/auth.ts
CHANGED
|
@@ -88,11 +88,11 @@ export type SocialProvider = z.infer<typeof socialProviderSchema>;
|
|
|
88
88
|
|
|
89
89
|
/** `POST /auth/idp/{provider}/authorize` 본문 — PKCE(S256)와 state는 SDK가 만든다 */
|
|
90
90
|
export const idpAuthorizeRequestSchema = z.object({
|
|
91
|
-
/** 로그인을 마치고 돌아올
|
|
91
|
+
/** 로그인을 마치고 돌아올 스토어프론트 주소. 스토어프론트 도메인 목록 안이어야 한다 */
|
|
92
92
|
redirectUri: z.url(),
|
|
93
93
|
/** base64url(SHA-256(codeVerifier)) */
|
|
94
94
|
codeChallenge: z.string().regex(/^[A-Za-z0-9_-]{43}$/),
|
|
95
|
-
/**
|
|
95
|
+
/** 스토어프론트가 만든 값 — 돌아올 때 그대로 붙어 온다. 세션에 저장해 두고 비교한다 */
|
|
96
96
|
state: z.string().min(16).max(128),
|
|
97
97
|
});
|
|
98
98
|
export type IdpAuthorizeRequest = z.infer<typeof idpAuthorizeRequestSchema>;
|
|
@@ -110,3 +110,26 @@ export const idpTokenRequestSchema = z.object({
|
|
|
110
110
|
cartToken: z.string().optional(),
|
|
111
111
|
});
|
|
112
112
|
export type IdpTokenRequest = z.infer<typeof idpTokenRequestSchema>;
|
|
113
|
+
|
|
114
|
+
/** `GET /me/identities` — 구매자의 로그인 수단(비밀번호 여부와 연결된 소셜 계정) */
|
|
115
|
+
export const memberIdentitiesSchema = z.object({
|
|
116
|
+
hasPassword: z.boolean(),
|
|
117
|
+
/** 셀러 자체 회원 연동(구매자 위임)으로 들어온 구매자 — 그 로그인도 로그인 수단으로 센다 */
|
|
118
|
+
hasExternalAuth: z.boolean(),
|
|
119
|
+
identities: z.array(
|
|
120
|
+
z.object({
|
|
121
|
+
provider: socialProviderSchema,
|
|
122
|
+
/** 공급자가 알려 준 이메일(참고값). 없을 수 있다 */
|
|
123
|
+
email: z.string().nullable(),
|
|
124
|
+
linkedAt: z.string(),
|
|
125
|
+
}),
|
|
126
|
+
),
|
|
127
|
+
});
|
|
128
|
+
export type MemberIdentities = z.infer<typeof memberIdentitiesSchema>;
|
|
129
|
+
|
|
130
|
+
/** `POST /me/identities/{provider}` 본문 — 연결 흐름에서 돌아온 1회용 code와 PKCE verifier */
|
|
131
|
+
export const linkIdentityRequestSchema = z.object({
|
|
132
|
+
code: z.string().min(1),
|
|
133
|
+
codeVerifier: z.string().regex(/^[A-Za-z0-9._~-]{43,128}$/),
|
|
134
|
+
});
|
|
135
|
+
export type LinkIdentityRequest = z.infer<typeof linkIdentityRequestSchema>;
|
package/src/schemas/catalog.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import type
|
|
2
|
+
import { type PageParams, pageSchema } from "./common";
|
|
3
3
|
|
|
4
4
|
export interface CategoryNode {
|
|
5
5
|
categoryId: string;
|
|
@@ -21,9 +21,13 @@ export const productCardSchema = z.object({
|
|
|
21
21
|
productId: z.string(),
|
|
22
22
|
name: z.string(),
|
|
23
23
|
thumbnailUrl: z.url(),
|
|
24
|
-
salePrice: z.number().int(),
|
|
25
|
-
discountedPrice: z
|
|
26
|
-
|
|
24
|
+
salePrice: z.number().int().describe("판매가. 원(KRW) 단위 정수"),
|
|
25
|
+
discountedPrice: z
|
|
26
|
+
.number()
|
|
27
|
+
.int()
|
|
28
|
+
.nullable()
|
|
29
|
+
.describe("즉시 할인 적용가. 원 단위. 할인이 없으면 null"),
|
|
30
|
+
discountRate: z.number().int().nullable().describe("할인율(%). 할인이 없으면 null"),
|
|
27
31
|
freeShipping: z.boolean(),
|
|
28
32
|
averageRating: z.number(),
|
|
29
33
|
reviewCount: z.number().int(),
|
|
@@ -99,3 +103,10 @@ export const productFacetsSchema = z.object({
|
|
|
99
103
|
});
|
|
100
104
|
|
|
101
105
|
export type ProductFacets = z.infer<typeof productFacetsSchema>;
|
|
106
|
+
|
|
107
|
+
/** `GET /products` 응답 — 상품 카드 페이지와 검색 패싯 */
|
|
108
|
+
export const productPageSchema = pageSchema(productCardSchema).extend({
|
|
109
|
+
facets: productFacetsSchema.optional(),
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
export type ProductPage = z.infer<typeof productPageSchema>;
|
package/src/schemas/inquiry.ts
CHANGED
|
@@ -23,3 +23,11 @@ export const createInquiryRequestSchema = z.object({
|
|
|
23
23
|
});
|
|
24
24
|
|
|
25
25
|
export type CreateInquiryRequest = z.infer<typeof createInquiryRequestSchema>;
|
|
26
|
+
|
|
27
|
+
/** `POST /products/{productId}/inquiries` 응답 */
|
|
28
|
+
export const createInquiryResultSchema = z.object({
|
|
29
|
+
inquiryId: z.string(),
|
|
30
|
+
createdAt: z.string(),
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
export type CreateInquiryResult = z.infer<typeof createInquiryResultSchema>;
|
package/src/schemas/member.ts
CHANGED
|
@@ -49,3 +49,11 @@ export const memberAddressRequestSchema = z.object({
|
|
|
49
49
|
});
|
|
50
50
|
|
|
51
51
|
export type MemberAddressRequest = z.infer<typeof memberAddressRequestSchema>;
|
|
52
|
+
|
|
53
|
+
/** `POST /me/wishlist` 응답 — 이미 찜한 상품이어도 같은 응답이다 */
|
|
54
|
+
export const wishlistAddResultSchema = z.object({
|
|
55
|
+
productId: z.string(),
|
|
56
|
+
wished: z.literal(true),
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
export type WishlistAddResult = z.infer<typeof wishlistAddResultSchema>;
|
package/src/schemas/order.ts
CHANGED
|
@@ -74,3 +74,12 @@ export const deliveryTrackingSchema = z.object({
|
|
|
74
74
|
});
|
|
75
75
|
|
|
76
76
|
export type DeliveryTracking = z.infer<typeof deliveryTrackingSchema>;
|
|
77
|
+
|
|
78
|
+
/** `POST /me/order-items/{orderItemId}/purchase-decision` 응답 */
|
|
79
|
+
export const purchaseDecisionResultSchema = z.object({
|
|
80
|
+
orderItemId: z.string(),
|
|
81
|
+
status: z.literal("PURCHASE_DECIDED"),
|
|
82
|
+
decidedAt: z.string(),
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
export type PurchaseDecisionResult = z.infer<typeof purchaseDecisionResultSchema>;
|
package/src/schemas/review.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
import { pageSchema } from "./common";
|
|
2
3
|
|
|
3
4
|
export const publicReviewSchema = z.object({
|
|
4
5
|
reviewId: z.string(),
|
|
@@ -63,3 +64,18 @@ export const updateReviewRequestSchema = z
|
|
|
63
64
|
});
|
|
64
65
|
|
|
65
66
|
export type UpdateReviewRequest = z.infer<typeof updateReviewRequestSchema>;
|
|
67
|
+
|
|
68
|
+
/** `GET /products/{productId}/reviews` 응답 — 리뷰 페이지와 평점 분포 */
|
|
69
|
+
export const reviewPageSchema = pageSchema(publicReviewSchema).extend({
|
|
70
|
+
summary: reviewSummarySchema.optional(),
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
export type ReviewPage = z.infer<typeof reviewPageSchema>;
|
|
74
|
+
|
|
75
|
+
/** `PUT /me/reviews/{reviewId}` 응답 */
|
|
76
|
+
export const updateReviewResultSchema = z.object({
|
|
77
|
+
reviewId: z.string(),
|
|
78
|
+
updatedAt: z.string(),
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
export type UpdateReviewResult = z.infer<typeof updateReviewResultSchema>;
|