@sayren/mcp 0.1.5 → 0.1.6
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sayren/mcp",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.6",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"sayren-mcp": "./dist/index.mjs"
|
|
@@ -12,8 +12,8 @@
|
|
|
12
12
|
"dependencies": {
|
|
13
13
|
"@modelcontextprotocol/sdk": "^1.22.0",
|
|
14
14
|
"zod": "^4.6.5",
|
|
15
|
-
"@sayren/store-sdk": "^0.
|
|
16
|
-
"@sayren/storefront-sdk": "^0.
|
|
15
|
+
"@sayren/store-sdk": "^0.4.0",
|
|
16
|
+
"@sayren/storefront-sdk": "^0.5.0"
|
|
17
17
|
},
|
|
18
18
|
"devDependencies": {
|
|
19
19
|
"@biomejs/biome": "^2.5.14",
|
|
@@ -14,10 +14,15 @@ export async function loader({ request, context }: Route.LoaderArgs) {
|
|
|
14
14
|
const cartToken = await readCartToken(request);
|
|
15
15
|
// 비회원이 아직 장바구니를 만든 적이 없으면 서버에 물어볼 것이 없다
|
|
16
16
|
if (!accessToken && !cartToken) {
|
|
17
|
-
return {
|
|
17
|
+
return {
|
|
18
|
+
cart: { items: [], summary: { productAmount: 0, deliveryFee: 0, totalAmount: 0 } },
|
|
19
|
+
loginRequired: false,
|
|
20
|
+
};
|
|
18
21
|
}
|
|
19
22
|
const api = apiFor(request, { accessToken, cartToken });
|
|
20
|
-
|
|
23
|
+
// 회원만 주문받는 스토어면 비회원에게 주문 버튼 대신 로그인 안내를 보인다(담기는 그대로 된다)
|
|
24
|
+
const [cart, store] = await Promise.all([api.cart.get(), accessToken ? null : api.store.get()]);
|
|
25
|
+
return { cart, loginRequired: store ? !store.guestCheckout : false };
|
|
21
26
|
}
|
|
22
27
|
|
|
23
28
|
export async function action({ request, context }: Route.ActionArgs) {
|
|
@@ -49,7 +54,7 @@ export async function action({ request, context }: Route.ActionArgs) {
|
|
|
49
54
|
}
|
|
50
55
|
|
|
51
56
|
export default function CartPage({ loaderData }: Route.ComponentProps) {
|
|
52
|
-
const { cart } = loaderData;
|
|
57
|
+
const { cart, loginRequired } = loaderData;
|
|
53
58
|
|
|
54
59
|
if (!cart.items.length) {
|
|
55
60
|
return (
|
|
@@ -142,19 +147,38 @@ export default function CartPage({ loaderData }: Route.ComponentProps) {
|
|
|
142
147
|
</div>
|
|
143
148
|
</dl>
|
|
144
149
|
|
|
145
|
-
{
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
</
|
|
157
|
-
|
|
150
|
+
{loginRequired ? (
|
|
151
|
+
<div className="flex w-full flex-col gap-2 md:w-64">
|
|
152
|
+
<p className="text-center text-muted text-xs">
|
|
153
|
+
이 스토어는 회원만 주문할 수 있어요. 담은 상품은 로그인하면 그대로 이어져요
|
|
154
|
+
</p>
|
|
155
|
+
<Link
|
|
156
|
+
to="/login?redirectTo=/cart"
|
|
157
|
+
className="flex h-12 items-center justify-center rounded-md bg-ink font-semibold text-white"
|
|
158
|
+
>
|
|
159
|
+
로그인하고 주문하기
|
|
160
|
+
</Link>
|
|
161
|
+
</div>
|
|
162
|
+
) : (
|
|
163
|
+
/* 주문서는 GET으로 연다 — 진입 조건이 URL에 남아 새로고침·뒤로가기가 그대로 동작한다 */
|
|
164
|
+
<Form method="get" action="/checkout" className="w-full md:w-64">
|
|
165
|
+
{purchasable.map((item) => (
|
|
166
|
+
<input
|
|
167
|
+
key={item.cartItemId}
|
|
168
|
+
type="hidden"
|
|
169
|
+
name="cartItemId"
|
|
170
|
+
value={item.cartItemId}
|
|
171
|
+
/>
|
|
172
|
+
))}
|
|
173
|
+
<button
|
|
174
|
+
type="submit"
|
|
175
|
+
disabled={!purchasable.length}
|
|
176
|
+
className="h-12 w-full rounded-md bg-ink font-semibold text-white disabled:opacity-50"
|
|
177
|
+
>
|
|
178
|
+
주문하기
|
|
179
|
+
</button>
|
|
180
|
+
</Form>
|
|
181
|
+
)}
|
|
158
182
|
</div>
|
|
159
183
|
</div>
|
|
160
184
|
);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { ApiError } from "@sayren/storefront-sdk";
|
|
1
2
|
import { useEffect, useRef, useState } from "react";
|
|
2
3
|
import { Form, redirect, useNavigate } from "react-router";
|
|
3
4
|
import { browserApi } from "../lib/api";
|
|
@@ -15,7 +16,8 @@ export function meta() {
|
|
|
15
16
|
}
|
|
16
17
|
|
|
17
18
|
export async function loader({ request, context }: Route.LoaderArgs) {
|
|
18
|
-
const
|
|
19
|
+
const url = new URL(request.url);
|
|
20
|
+
const intent = checkoutIntentFrom(url.searchParams);
|
|
19
21
|
if (!intent) throw redirect("/cart");
|
|
20
22
|
|
|
21
23
|
const accessToken = readToken(context);
|
|
@@ -23,7 +25,15 @@ export async function loader({ request, context }: Route.LoaderArgs) {
|
|
|
23
25
|
const api = apiFor(request, { accessToken, cartToken });
|
|
24
26
|
// 주문서 세션은 진입할 때마다 새로 만든다 — 조회 API가 없고, 가격·재고를 매번 다시 계산한다
|
|
25
27
|
const [checkout, member] = await Promise.all([
|
|
26
|
-
api.checkout.create(intent)
|
|
28
|
+
api.checkout.create(intent).catch((error: unknown) => {
|
|
29
|
+
// 회원만 주문받는 스토어 — 로그인 뒤 돌아온다. 장바구니 주문은 로그인하며 장바구니가 합쳐져 항목 id가
|
|
30
|
+
// 바뀔 수 있으니 장바구니로, 바로구매는 같은 주문서로 돌아온다
|
|
31
|
+
if (error instanceof ApiError && error.code === "GUEST_CHECKOUT_DISABLED") {
|
|
32
|
+
const back = intent.cartItemIds ? "/cart" : `${url.pathname}${url.search}`;
|
|
33
|
+
throw redirect(`/login?redirectTo=${encodeURIComponent(back)}`);
|
|
34
|
+
}
|
|
35
|
+
throw error;
|
|
36
|
+
}),
|
|
27
37
|
accessToken ? api.member.me().catch(() => null) : null,
|
|
28
38
|
]);
|
|
29
39
|
return {
|