@sayren/mcp 0.1.2 → 0.1.4
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/index.mjs +30 -3
- package/package.json +3 -3
- package/template/.template-meta.json +2 -2
- package/template/README.md +3 -1
package/dist/index.mjs
CHANGED
|
@@ -529,6 +529,16 @@ const RULES = [
|
|
|
529
529
|
title: "팝업 메시지는 origin과 source를 모두 검증한다",
|
|
530
530
|
why: "검증 없이 받으면 아무 창이나 결제 완료를 흉내 낼 수 있다"
|
|
531
531
|
},
|
|
532
|
+
{
|
|
533
|
+
id: "analytics-start",
|
|
534
|
+
title: "방문 분석은 브라우저에서 한 번 시작한다(`@sayren/storefront-sdk/analytics`)",
|
|
535
|
+
why: "빠지면 셀러 콘솔 애널리틱스가 비어 있다. 서버 파일에서 부르면 아무것도 하지 않는다"
|
|
536
|
+
},
|
|
537
|
+
{
|
|
538
|
+
id: "analytics-ids",
|
|
539
|
+
title: "서버의 API 클라이언트에 방문 식별 쿠키를 싣는다(`analyticsIdsFromCookie`)",
|
|
540
|
+
why: "빠지면 장바구니·결제 시작·구매가 방문과 이어지지 않아 퍼널과 유입별 매출에서 빠진다"
|
|
541
|
+
},
|
|
532
542
|
{
|
|
533
543
|
id: "closed-not-failed",
|
|
534
544
|
title: "팝업이 결과 없이 닫히면 실패로 단정하지 않는다",
|
|
@@ -543,7 +553,7 @@ const CLIENT_FILE = /\.(ts|tsx)$/;
|
|
|
543
553
|
const SERVER_FILE = /\.server\.(ts|tsx)$/;
|
|
544
554
|
/**
|
|
545
555
|
* 만들어진 프로젝트의 소스를 규칙과 대조한다. 정적 검사라 모든 문제를 잡지는 못하지만,
|
|
546
|
-
*
|
|
556
|
+
* 여기 있는 것들은 실제로 반복해서 깨지는 것들이다.
|
|
547
557
|
*/
|
|
548
558
|
function verifySources(files) {
|
|
549
559
|
const findings = [];
|
|
@@ -554,7 +564,8 @@ function verifySources(files) {
|
|
|
554
564
|
const has = (needle) => cleaned.some((file) => file.content.includes(needle));
|
|
555
565
|
for (const file of cleaned) {
|
|
556
566
|
if (!CLIENT_FILE.test(file.path)) continue;
|
|
557
|
-
|
|
567
|
+
const isServer = SERVER_FILE.test(file.path) || /\/(server|\.server)\//.test(file.path);
|
|
568
|
+
if (!isServer && /process\.env\.[A-Z_]/.test(file.content)) findings.push({
|
|
558
569
|
ruleId: "server-only-env",
|
|
559
570
|
file: file.path,
|
|
560
571
|
detail: "브라우저로 갈 수 있는 파일에서 process.env를 읽는다. `*.server.ts`로 옮긴다"
|
|
@@ -586,6 +597,16 @@ function verifySources(files) {
|
|
|
586
597
|
detail: "message 리스너가 source(우리가 연 창)를 검증하지 않는다"
|
|
587
598
|
});
|
|
588
599
|
}
|
|
600
|
+
if (isServer && /createAnalytics\(/.test(file.content)) findings.push({
|
|
601
|
+
ruleId: "analytics-start",
|
|
602
|
+
file: file.path,
|
|
603
|
+
detail: "서버 파일에서 방문 분석을 시작한다. 서버에서는 아무것도 하지 않으니 브라우저(effect)에서 부른다"
|
|
604
|
+
});
|
|
605
|
+
if (isServer && /createStorefrontClient\(/.test(file.content) && !/analyticsIdsFromCookie|visitorId/.test(file.content)) findings.push({
|
|
606
|
+
ruleId: "analytics-ids",
|
|
607
|
+
file: file.path,
|
|
608
|
+
detail: "서버 API 클라이언트에 방문 식별자가 없다. `...analyticsIdsFromCookie(request.headers.get(\"cookie\"))`를 넘긴다"
|
|
609
|
+
});
|
|
589
610
|
if (/requestPayment\(/.test(file.content) && /<form\s/.test(file.content)) findings.push({
|
|
590
611
|
ruleId: "popup-client-submit",
|
|
591
612
|
file: file.path,
|
|
@@ -602,6 +623,11 @@ function verifySources(files) {
|
|
|
602
623
|
file: "(프로젝트 전체)",
|
|
603
624
|
detail: "팝업이 결과 없이 닫힌 경우 결제 상태를 조회하는 코드가 없다"
|
|
604
625
|
});
|
|
626
|
+
if (has("createStorefrontClient(") && !has("@sayren/storefront-sdk/analytics")) findings.push({
|
|
627
|
+
ruleId: "analytics-start",
|
|
628
|
+
file: "(프로젝트 전체)",
|
|
629
|
+
detail: "방문 분석을 시작하는 코드가 없다. `@sayren/storefront-sdk/analytics`의 createAnalytics를 브라우저에서 한 번 부른다"
|
|
630
|
+
});
|
|
605
631
|
if (has("createStorefrontClient(") && !has("loader")) findings.push({
|
|
606
632
|
ruleId: "loader-first",
|
|
607
633
|
file: "(프로젝트 전체)",
|
|
@@ -674,7 +700,8 @@ server.registerTool("get_scaffold_plan", {
|
|
|
674
700
|
],
|
|
675
701
|
doNotTouch: [
|
|
676
702
|
"app/lib/payment-popup.ts — 결제 팝업 규칙",
|
|
677
|
-
"app/lib/api.server.ts — 테넌트 헤더·토큰 전달·부모 origin",
|
|
703
|
+
"app/lib/api.server.ts — 테넌트 헤더·토큰 전달·부모 origin·방문 식별 쿠키",
|
|
704
|
+
"app/lib/analytics.ts — 방문 분석 시작(브라우저 한 번)과 시작 전 이벤트 큐",
|
|
678
705
|
"loader에서 데이터를 받는 구조"
|
|
679
706
|
],
|
|
680
707
|
rules: RULES,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sayren/mcp",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
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/
|
|
16
|
-
"@sayren/
|
|
15
|
+
"@sayren/store-sdk": "^0.3.0",
|
|
16
|
+
"@sayren/storefront-sdk": "^0.2.1"
|
|
17
17
|
},
|
|
18
18
|
"devDependencies": {
|
|
19
19
|
"@biomejs/biome": "^2.5.14",
|
package/template/README.md
CHANGED
|
@@ -26,7 +26,9 @@ pnpm dev
|
|
|
26
26
|
`app/lib/analytics.ts`가 브라우저에서 방문 분석을 시작한다. 페이지뷰·체류는 자동으로 세고, 상품 조회·목록 노출·검색은
|
|
27
27
|
화면에서 `useTrack`으로 남긴다. 장바구니·결제·구매는 `app/lib/api.server.ts`가 방문자 쿠키를 API 요청 헤더에 실어
|
|
28
28
|
서버가 기록한다. localhost에서는 보내지 않는다. 개발 중에 확인하려면 `SAYREN_ANALYTICS_DEBUG=1`을 준다.
|
|
29
|
-
|
|
29
|
+
동의 기본값은 `granted`(바로 수집)다. 개인정보를 저장하지 않는 1st-party 쿠키라 국내 쇼핑몰 기준으로 정했다.
|
|
30
|
+
EU 등 분석 쿠키에 사전 동의가 필요한 지역의 구매자를 받는다면 `app/lib/analytics.ts`의 `consent`를 `"pending"`으로 바꾸고,
|
|
31
|
+
쿠키 동의 배너에서 `setConsent("granted" | "denied")`를 부른다. 동의 전에는 쿠키를 만들지 않고 이벤트를 모아 두었다가 동의하면 보낸다.
|
|
30
32
|
|
|
31
33
|
## 확장 지점
|
|
32
34
|
|