@sayren/store-sdk 0.41.0 → 0.43.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/index.d.mts +72 -72
- package/dist/index.mjs +160 -160
- package/dist/server.mjs +1 -1
- package/dist/{webhook-headers-D974dE4T.mjs → webhook-headers-CTez3xDY.mjs} +81 -3
- package/package.json +1 -1
- package/src/client.ts +1 -1
- package/src/http.ts +1 -1
- package/src/oauth.ts +1 -1
- package/src/schemas/ai.ts +40 -33
- package/src/schemas/analytics.ts +1 -1
- package/src/schemas/api-log.ts +1 -1
- package/src/schemas/app-manifest.ts +1 -1
- package/src/schemas/apps.ts +19 -16
- package/src/schemas/audit.ts +7 -4
- package/src/schemas/auth.ts +1 -1
- package/src/schemas/category.ts +37 -32
- package/src/schemas/claim.ts +15 -12
- package/src/schemas/collection.ts +76 -65
- package/src/schemas/commerce-flow.ts +107 -86
- package/src/schemas/commerce-number.ts +1 -1
- package/src/schemas/common.ts +1 -1
- package/src/schemas/coupon.ts +88 -80
- package/src/schemas/currency-prices.ts +1 -1
- package/src/schemas/currency.ts +1 -1
- package/src/schemas/customer-delegation.ts +31 -28
- package/src/schemas/customer.ts +10 -7
- package/src/schemas/fulfillment.ts +1 -1
- package/src/schemas/inventory.ts +29 -24
- package/src/schemas/locale.ts +1 -1
- package/src/schemas/login.ts +18 -11
- package/src/schemas/market.ts +1 -1
- package/src/schemas/misc.ts +7 -4
- package/src/schemas/order.ts +70 -59
- package/src/schemas/payment.ts +108 -89
- package/src/schemas/plans.ts +1 -1
- package/src/schemas/points.ts +33 -28
- package/src/schemas/product-info.ts +13 -10
- package/src/schemas/product-localization.ts +1 -1
- package/src/schemas/product.ts +18 -15
- package/src/schemas/promotion.ts +1 -1
- package/src/schemas/refund-policy.ts +1 -1
- package/src/schemas/saved-option-set.ts +6 -6
- package/src/schemas/scope.ts +1 -1
- package/src/schemas/shipping-policy.ts +1 -1
- package/src/schemas/starter.ts +16 -13
- package/src/schemas/stay.ts +16 -13
- package/src/schemas/store.ts +130 -114
- package/src/schemas/storefront-hosting.ts +272 -220
- package/src/schemas/strict.ts +84 -0
- package/src/schemas/subscription.ts +1 -1
- package/src/schemas/telemetry.ts +1 -1
- package/src/schemas/upload.ts +16 -13
- package/src/schemas/webhook-event.ts +1 -1
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { A as auditLogPageSchema, C as approvalPageSchema, D as auditActorTypeSchema, E as auditActorSchema, M as auditLogSourceSchema, N as writeDryRunResultSchema, O as auditLogListQuerySchema, P as writePreviewSchema, S as approvalListQuerySchema, T as approvalStatusSchema, _ as resolveToken, a as WEBHOOK_HEADERS, b as agentPolicyDecisionSchema, c as APPROVAL_ALREADY_DECIDED_CODE, d as ApprovalPendingError, f as SAYREN_API_BASE_URL, g as path, h as createHttp, i as WEBHOOK_EVENT_TYPE_HEADER, j as auditLogSchema, k as auditLogOutcomeSchema, l as APPROVAL_REQUIRED_CODE, m as buildQuery, n as WEBHOOK_DELIVERY_ID_HEADER, o as WEBHOOK_SIGNATURE_HEADER, p as SAYREN_API_ORIGIN, r as WEBHOOK_EVENT_ID_HEADER, s as WEBHOOK_USER_AGENT, t as WEBHOOK_DEFAULT_TOLERANCE_SEC, u as ApiError, v as toApiError, w as approvalRequestSchema, x as approvalDecisionRequestSchema, y as unwrapData } from "./webhook-headers-
|
|
2
|
-
import
|
|
1
|
+
import { A as auditLogPageSchema, C as approvalPageSchema, D as auditActorTypeSchema, E as auditActorSchema, F as strictRequest, M as auditLogSourceSchema, N as writeDryRunResultSchema, O as auditLogListQuerySchema, P as writePreviewSchema, S as approvalListQuerySchema, T as approvalStatusSchema, _ as resolveToken, a as WEBHOOK_HEADERS, b as agentPolicyDecisionSchema, c as APPROVAL_ALREADY_DECIDED_CODE, d as ApprovalPendingError, f as SAYREN_API_BASE_URL, g as path, h as createHttp, i as WEBHOOK_EVENT_TYPE_HEADER, j as auditLogSchema, k as auditLogOutcomeSchema, l as APPROVAL_REQUIRED_CODE, m as buildQuery, n as WEBHOOK_DELIVERY_ID_HEADER, o as WEBHOOK_SIGNATURE_HEADER, p as SAYREN_API_ORIGIN, r as WEBHOOK_EVENT_ID_HEADER, s as WEBHOOK_USER_AGENT, t as WEBHOOK_DEFAULT_TOLERANCE_SEC, u as ApiError, v as toApiError, w as approvalRequestSchema, x as approvalDecisionRequestSchema, y as unwrapData } from "./webhook-headers-CTez3xDY.mjs";
|
|
2
|
+
import * as z from "zod";
|
|
3
3
|
//#region src/schemas/auth.ts
|
|
4
4
|
const storeRoleSchema = z.enum([
|
|
5
5
|
"OWNER",
|
|
@@ -1361,7 +1361,7 @@ const businessInfoResponseShape = {
|
|
|
1361
1361
|
const autoDeliverDaysDescription = "발송 후 자동 배송완료까지의 일수(0~30, 기본 7). 발송(`DISPATCHED`)·배송중(`DELIVERING`) 주문상품을 이 기간이 지나면 배송완료(`DELIVERED`)로 바꾼다. `0`이면 자동 배송완료를 하지 않는다";
|
|
1362
1362
|
const autoConfirmDaysDescription = "배송완료 후 자동 구매확정까지의 일수(0~30, 기본 7). 배송완료 시점에 구매확정 예정일(`autoDecisionDate`)을 이 값으로 잡고, 그 날짜가 지나면 구매확정(`PURCHASE_DECIDED`)으로 바꾼다. 진행 중인 클레임이 있으면 건너뛴다. `0`이면 자동 구매확정을 하지 않는다";
|
|
1363
1363
|
/** `PATCH /v1/store` 본문 — 바꿀 필드만 보낸다 */
|
|
1364
|
-
const updateStoreRequestSchema = z.object({
|
|
1364
|
+
const updateStoreRequestSchema = strictRequest(z.object({
|
|
1365
1365
|
name: z.string().min(2).max(50).optional(),
|
|
1366
1366
|
logoUrl: z.url().optional(),
|
|
1367
1367
|
customerCenterPhone: z.string().nullable().optional().describe("고객센터 전화번호. `null`을 주면 지운다"),
|
|
@@ -1370,7 +1370,7 @@ const updateStoreRequestSchema = z.object({
|
|
|
1370
1370
|
analyticsAllowedOrigins: z.array(analyticsOriginSchema).max(20).optional().describe("@deprecated `storefrontOrigins`를 쓴다(같은 목록). 방문 분석 수집을 받을 스토어프론트 오리진(최대 20개). 비우면 모든 오리진에서 받는다. 넣으면 목록에 없는 오리진의 수집 요청은 403 `ORIGIN_NOT_ALLOWED`이고, 페이지 URL이 목록 밖인 이벤트는 버린다"),
|
|
1371
1371
|
storefrontOrigins: z.array(analyticsOriginSchema).max(20).optional().describe("스토어프론트 도메인(최대 20개) — 소셜 로그인 뒤 돌아갈 주소와 방문 분석 수집을 함께 제한한다. `analyticsAllowedOrigins`와 같은 목록이다(둘 다 주면 이 값이 이긴다)"),
|
|
1372
1372
|
guestCheckoutEnabled: z.boolean().optional().describe("비회원 주문 허용. false(회원만)면 로그인하지 않은 구매자의 주문서 생성·결제 요청이 403 `GUEST_CHECKOUT_DISABLED`다. 장바구니 담기와 이미 받은 비회원 주문의 조회·취소는 막지 않는다"),
|
|
1373
|
-
testOrderReviewsAllowed: z.boolean().optional().describe("
|
|
1373
|
+
testOrderReviewsAllowed: z.boolean().optional().describe("@deprecated 효과 없음. 테스트 결제 주문도 실주문과 같이 리뷰를 쓸 수 있다. 호환을 위해 받기만 한다"),
|
|
1374
1374
|
autoDeliverDays: z.number().int().min(0).max(30).optional().describe(autoDeliverDaysDescription),
|
|
1375
1375
|
autoConfirmDays: z.number().int().min(0).max(30).optional().describe(autoConfirmDaysDescription),
|
|
1376
1376
|
refundPolicy: refundPolicyPatchSchema.optional().describe("상점 기본 환불 정책 — 준 항목만 바꾼다. 상품별 정책(`products.refundPolicy`)이 없는 상품에 적용된다"),
|
|
@@ -1380,12 +1380,12 @@ const updateStoreRequestSchema = z.object({
|
|
|
1380
1380
|
termsUrl: policyDocumentUrlSchema.optional().describe("이용약관 문서 주소(https). 동의 시점의 값이 동의 이력에 복사된다. `null`을 주면 지운다"),
|
|
1381
1381
|
privacyUrl: policyDocumentUrlSchema.optional().describe("개인정보 수집이용 동의 문서 주소(https). `null`을 주면 지운다"),
|
|
1382
1382
|
defaultLocale: localeCodeSchema.optional().describe("상점 기본 언어(이슈 #134). 상품·카테고리 필드가 이 언어 값이고 번역이 없는 칸은 이 값으로 보인다. 바꿔도 저장된 값은 그대로다")
|
|
1383
|
-
}).refine((value) => Object.values(value).some((v) => v !== void 0), { message: "최소 1개 필드가 필요합니다" });
|
|
1383
|
+
}).refine((value) => Object.values(value).some((v) => v !== void 0), { message: "최소 1개 필드가 필요합니다" }));
|
|
1384
1384
|
const storeSchema = z.object({
|
|
1385
1385
|
storeId: z.string(),
|
|
1386
1386
|
name: z.string(),
|
|
1387
1387
|
storeCode: z.string(),
|
|
1388
|
-
storefrontUrl: z.url(),
|
|
1388
|
+
storefrontUrl: z.url().nullable().describe("스토어프론트 호스팅 사이트의 대표 주소. 사이트가 없으면 null"),
|
|
1389
1389
|
status: z.enum([
|
|
1390
1390
|
"ACTIVE",
|
|
1391
1391
|
"SUSPENDED",
|
|
@@ -1398,7 +1398,7 @@ const storeSchema = z.object({
|
|
|
1398
1398
|
analyticsAllowedOrigins: z.array(z.string()).describe("방문 분석 수집을 받는 스토어프론트 오리진. 비어 있으면 모든 오리진에서 받는다"),
|
|
1399
1399
|
storefrontOrigins: z.array(z.string()).describe("스토어프론트 도메인 — 소셜 로그인 리디렉트와 방문 분석 수집이 함께 쓴다(`analyticsAllowedOrigins`와 같은 값)"),
|
|
1400
1400
|
guestCheckoutEnabled: z.boolean().describe("비회원 주문 허용. false면 회원만 주문할 수 있다"),
|
|
1401
|
-
testOrderReviewsAllowed: z.boolean().optional().default(false).describe("테스트 결제
|
|
1401
|
+
testOrderReviewsAllowed: z.boolean().optional().default(false).describe("@deprecated 효과 없음. 테스트 결제 주문도 실주문과 같이 리뷰를 쓸 수 있다. 호환을 위해 저장한 값을 싣는다"),
|
|
1402
1402
|
autoDeliverDays: z.number().int().describe(autoDeliverDaysDescription),
|
|
1403
1403
|
autoConfirmDays: z.number().int().describe(autoConfirmDaysDescription),
|
|
1404
1404
|
refundPolicy: refundPolicySchema.describe("상점 기본 환불 정책. 상점이 정하지 않은 항목은 플랫폼 기본값으로 채워 항상 전부 싣는다"),
|
|
@@ -1457,12 +1457,12 @@ const invitationSchema = z.object({
|
|
|
1457
1457
|
acceptedAt: z.string().nullable().default(null)
|
|
1458
1458
|
});
|
|
1459
1459
|
/** `POST /v1/store/invitations` 요청 — ADMIN 초대는 OWNER만 할 수 있다 */
|
|
1460
|
-
const inviteMemberRequestSchema = z.object({
|
|
1460
|
+
const inviteMemberRequestSchema = strictRequest(z.object({
|
|
1461
1461
|
email: z.email(),
|
|
1462
1462
|
role: z.enum(["ADMIN", "STAFF"])
|
|
1463
|
-
});
|
|
1463
|
+
}));
|
|
1464
1464
|
/** `PATCH /v1/store/members/{userId}` 요청 */
|
|
1465
|
-
const updateMemberRoleRequestSchema = z.object({ role: z.enum(["ADMIN", "STAFF"]) });
|
|
1465
|
+
const updateMemberRoleRequestSchema = strictRequest(z.object({ role: z.enum(["ADMIN", "STAFF"]) }));
|
|
1466
1466
|
/** `GET /v1/store/invitations` 쿼리 — OpenAPI 문서용 형태 */
|
|
1467
1467
|
const invitationListQuerySchema = z.object({ status: invitationSchema.shape.status.optional().describe("초대 상태 필터") });
|
|
1468
1468
|
/** `PATCH /v1/store/members/{userId}` 응답 — 바뀐 역할 */
|
|
@@ -1489,7 +1489,7 @@ const sellerAddressSchema = z.object({
|
|
|
1489
1489
|
inventoryLocationId: z.string().nullable().optional().describe("이 주소의 재고 위치 ID — 출고지면 배송 주문을 이 위치 재고로 팔고, 반품지면 반품을 이 위치로 들인다. 위치를 하나만 쓰는 상점은 기본 위치다"),
|
|
1490
1490
|
createdAt: z.string()
|
|
1491
1491
|
});
|
|
1492
|
-
const sellerAddressRequestSchema = z.object({
|
|
1492
|
+
const sellerAddressRequestSchema = strictRequest(z.object({
|
|
1493
1493
|
type: addressTypeSchema,
|
|
1494
1494
|
name: z.string().min(1).max(50),
|
|
1495
1495
|
contactName: z.string().min(1),
|
|
@@ -1498,7 +1498,7 @@ const sellerAddressRequestSchema = z.object({
|
|
|
1498
1498
|
address1: z.string().min(1),
|
|
1499
1499
|
address2: z.string().optional(),
|
|
1500
1500
|
inventoryLocationId: z.string().min(1).optional().describe("재고 위치 ID. 등록에서 생략하면 기본 위치, 수정에서 생략하면 지금 연결을 유지한다")
|
|
1501
|
-
});
|
|
1501
|
+
}));
|
|
1502
1502
|
//#endregion
|
|
1503
1503
|
//#region src/schemas/storefront-hosting.ts
|
|
1504
1504
|
/**
|
|
@@ -1662,11 +1662,11 @@ const TEMPLATE_STORE_LOCKED_BUSINESS_FIELDS = [
|
|
|
1662
1662
|
"customerCenterPhone",
|
|
1663
1663
|
"customerCenterEmail"
|
|
1664
1664
|
];
|
|
1665
|
-
const updateStoreTemplateRequestSchema = z.object({
|
|
1665
|
+
const updateStoreTemplateRequestSchema = strictRequest(z.object({
|
|
1666
1666
|
name: z.string().trim().min(1).max(40).describe("템플릿 이름(1~40자)"),
|
|
1667
1667
|
description: z.string().trim().max(200).describe("템플릿 소개(200자까지)"),
|
|
1668
1668
|
thumbnailUrl: z.url({ protocol: /^https$/ }).max(2048).nullable().describe("썸네일 이미지 주소(https). 이미지 업로드(`POST /v1/uploads`)로 받은 주소를 쓴다. 없으면 null")
|
|
1669
|
-
});
|
|
1669
|
+
}));
|
|
1670
1670
|
/**
|
|
1671
1671
|
* 버전 상태 — 템플릿 버전(`source: TEMPLATE`)은 템플릿 산출물을 그대로 쓰므로 만들 때 곧바로 `READY`다. 편집 버전
|
|
1672
1672
|
* (`source: EDIT`, M2b)은 작업 트리 스냅샷을 빌드하는 동안 `BUILDING`이고, 빌드·규칙 검증을 통과하면 `READY`, 실패하면
|
|
@@ -1827,11 +1827,11 @@ const storefrontDeploymentSchema = z.object({
|
|
|
1827
1827
|
attempts: z.number().int().describe("연속 실패 횟수"),
|
|
1828
1828
|
nextAttemptAt: z.string().nullable().describe("다음 자동 시도 시각 (ISO 8601)")
|
|
1829
1829
|
});
|
|
1830
|
-
const createStorefrontSiteRequestSchema = z.object({
|
|
1830
|
+
const createStorefrontSiteRequestSchema = strictRequest(z.object({
|
|
1831
1831
|
templateId: z.string().min(1).max(64).describe("템플릿 id"),
|
|
1832
1832
|
slug: storefrontSlugSchema.optional().describe("주소 슬러그. 생략하면 상점 코드"),
|
|
1833
1833
|
copyContent: z.boolean().optional().describe("템플릿 상점의 템플릿일 때만 — true면 템플릿 상점의 콘텐츠(레이아웃, 상품·카테고리·컬렉션·주문 흐름·배송·환불 정책)를 이 상점으로 복사한다. 쿠폰·적립금·리뷰는 복사하지 않는다. 생략하거나 false면 레이아웃 콘텐츠를 비우고 빌드한다")
|
|
1834
|
-
});
|
|
1834
|
+
}));
|
|
1835
1835
|
/** @deprecated 템플릿 변경은 지원하지 않는다(항상 409 `TEMPLATE_CHANGE_NOT_SUPPORTED`). 호환을 위해 스키마만 남겼다 */
|
|
1836
1836
|
const changeStorefrontTemplateRequestSchema = z.object({
|
|
1837
1837
|
templateId: z.string().min(1).max(64).describe("바꿀 템플릿 id"),
|
|
@@ -1843,28 +1843,28 @@ const changeStorefrontTemplateRequestSchema = z.object({
|
|
|
1843
1843
|
* 지금 발행 버전이 그 값일 때만 바꾼다(화면을 본 뒤 다른 사람이 바꿨으면 409 `STOREFRONT_SITE_CHANGED`).
|
|
1844
1844
|
* 이미 그 버전이 발행돼 있으면 아무것도 바꾸지 않고 200이다(같은 요청의 재실행이 안전하다).
|
|
1845
1845
|
*/
|
|
1846
|
-
const publishStorefrontVersionRequestSchema = z.object({ expectedPublishedVersionId: z.string().nullable().optional().describe("화면에서 본 발행 버전 id (발행 전이었으면 null). 생략하면 확인하지 않는다") });
|
|
1847
|
-
const rollbackStorefrontRequestSchema = z.object({
|
|
1846
|
+
const publishStorefrontVersionRequestSchema = strictRequest(z.object({ expectedPublishedVersionId: z.string().nullable().optional().describe("화면에서 본 발행 버전 id (발행 전이었으면 null). 생략하면 확인하지 않는다") }));
|
|
1847
|
+
const rollbackStorefrontRequestSchema = strictRequest(z.object({
|
|
1848
1848
|
versionId: z.string().optional().describe("되돌릴 버전. 생략하면 지금 버전 직전에 발행했던 버전"),
|
|
1849
1849
|
expectedPublishedVersionId: z.string().nullable().optional()
|
|
1850
|
-
});
|
|
1851
|
-
const changeStorefrontSlugRequestSchema = z.object({ slug: storefrontSlugSchema });
|
|
1852
|
-
const setStorefrontVisibilityRequestSchema = z.object({
|
|
1850
|
+
}));
|
|
1851
|
+
const changeStorefrontSlugRequestSchema = strictRequest(z.object({ slug: storefrontSlugSchema }));
|
|
1852
|
+
const setStorefrontVisibilityRequestSchema = strictRequest(z.object({
|
|
1853
1853
|
visibility: storefrontVisibilitySchema,
|
|
1854
1854
|
expectedVisibility: storefrontVisibilitySchema.optional().describe("화면에서 본 공개 상태. 주면 지금 값이 같을 때만 바꾼다")
|
|
1855
|
-
});
|
|
1855
|
+
}));
|
|
1856
1856
|
/** 호스팅 끄기 — 사이트를 닫고(`CLOSED`) 슬러그를 보류한다. 확인 입력은 받지 않는다(2026-10-22) */
|
|
1857
|
-
const disableStorefrontHostingRequestSchema = z.object({ confirmSlug: z.string().max(200).optional().describe("쓰지 않는다 — 보내도 무시한다(옛 확인 입력)") });
|
|
1857
|
+
const disableStorefrontHostingRequestSchema = strictRequest(z.object({ confirmSlug: z.string().max(200).optional().describe("쓰지 않는다 — 보내도 무시한다(옛 확인 입력)") }));
|
|
1858
1858
|
const storefrontSlugAvailabilityQuerySchema = z.object({ slug: z.string().describe("확인할 슬러그") });
|
|
1859
1859
|
/**
|
|
1860
1860
|
* 버전 미리보기 링크 — 상점 플랫폼에서 로그인한 OWNER·ADMIN만 받는다(PAT·에이전트 불가). 버전마다 미리보기 전용 주소가 있고
|
|
1861
1861
|
* (오픈 준비 중이어도 실제 화면), 링크를 5분 안에 열면 그 브라우저에 그 주소 전용 12시간 쿠키가 생긴다. 사이트 주소는 오픈 전이면
|
|
1862
1862
|
* 누구에게나 「오픈 준비 중」이다. 미리보기 주소에 쿠키 없이 들어오면 상점 플랫폼 로그인과 OWNER·ADMIN 확인을 거친다.
|
|
1863
1863
|
*/
|
|
1864
|
-
const createStorefrontPreviewLinkRequestSchema = z.object({
|
|
1864
|
+
const createStorefrontPreviewLinkRequestSchema = strictRequest(z.object({
|
|
1865
1865
|
path: z.string().max(512).optional().describe("열 경로 (`/`로 시작). 생략하면 `/`"),
|
|
1866
1866
|
versionId: z.string().max(64).optional().describe("볼 버전. 생략하면 발행 버전이다. 보관 기간이 지난 버전(`previewable: false`)은 409 `STOREFRONT_VERSION_NOT_PREVIEWABLE`")
|
|
1867
|
-
});
|
|
1867
|
+
}));
|
|
1868
1868
|
const storefrontPreviewLinkSchema = z.object({
|
|
1869
1869
|
url: z.string().describe("미리보기 링크 — 5분 안에 연다"),
|
|
1870
1870
|
editorUrl: z.string().optional().describe("편집 미리보기만: AI 편집 패널이 있는 미리보기 화면 링크(5분). 사이트는 그 안에 따로 뜨고 AI 자격은 사이트 코드에 닿지 않는다"),
|
|
@@ -2050,7 +2050,7 @@ const storefrontSiteDomainsSchema = z.object({
|
|
|
2050
2050
|
target: z.string().describe("CNAME 대상 (예: `sites.sayren.co`)"),
|
|
2051
2051
|
domains: z.array(storefrontSiteDomainSchema).describe("대표 주소 먼저, 그다음 추가한 순서")
|
|
2052
2052
|
});
|
|
2053
|
-
const addStorefrontDomainRequestSchema = z.object({ hostname: z.string().min(1).max(300).describe("연결할 도메인 (예: `shop.brand.com`). 서버가 소문자·punycode로 정규화한다") });
|
|
2053
|
+
const addStorefrontDomainRequestSchema = strictRequest(z.object({ hostname: z.string().min(1).max(300).describe("연결할 도메인 (예: `shop.brand.com`). 서버가 소문자·punycode로 정규화한다") }));
|
|
2054
2054
|
/** 플랫폼 전체 동시 편집 환경 상한 */
|
|
2055
2055
|
const STOREFRONT_EDIT_CAPACITY = 20;
|
|
2056
2056
|
/** 활동이 없으면 편집 환경이 잠드는 시간(분). 작업 트리는 보존된다 */
|
|
@@ -2143,11 +2143,11 @@ const storefrontEditSessionSchema = z.object({
|
|
|
2143
2143
|
* 편집 환경이 켜져 있지 않으면 409 `EDIT_SESSION_REQUIRED`
|
|
2144
2144
|
*/
|
|
2145
2145
|
/** `POST /v1/storefront/site/editing/git/merge` — 켜진 편집 환경 작업 트리에 운영 브랜치 최신 커밋을 합친다(#138, Git 원천 사이트) */
|
|
2146
|
-
const storefrontGitMergeRequestSchema = z.object({ action: z.enum([
|
|
2146
|
+
const storefrontGitMergeRequestSchema = strictRequest(z.object({ action: z.enum([
|
|
2147
2147
|
"merge",
|
|
2148
2148
|
"continue",
|
|
2149
2149
|
"abort"
|
|
2150
|
-
]).default("merge").describe("`merge` 최신 운영 브랜치를 합친다 · `continue` 충돌 표시를 고친 뒤 합치기를 끝낸다 · `abort` 합치기를 취소하고 합치기 전으로 돌린다") });
|
|
2150
|
+
]).default("merge").describe("`merge` 최신 운영 브랜치를 합친다 · `continue` 충돌 표시를 고친 뒤 합치기를 끝낸다 · `abort` 합치기를 취소하고 합치기 전으로 돌린다") }));
|
|
2151
2151
|
const storefrontGitMergeSchema = z.object({
|
|
2152
2152
|
status: z.enum([
|
|
2153
2153
|
"MERGED",
|
|
@@ -2222,12 +2222,12 @@ const storefrontFileContentSchema = z.object({
|
|
|
2222
2222
|
* 해시가 같아야 한다. 조건을 **모두** 확인한 뒤 쓰고, 하나라도 어긋나면 아무것도 쓰지 않는다(409 `FILE_CHANGED`,
|
|
2223
2223
|
* `error.details.conflicts`).
|
|
2224
2224
|
*/
|
|
2225
|
-
const writeStorefrontFilesRequestSchema = z.object({ files: z.array(z.object({
|
|
2225
|
+
const writeStorefrontFilesRequestSchema = strictRequest(z.object({ files: z.array(z.object({
|
|
2226
2226
|
path: storefrontFilePathSchema,
|
|
2227
2227
|
content: z.string().describe("내용 (`encoding`에 따라 텍스트 또는 base64)"),
|
|
2228
2228
|
encoding: storefrontFileEncodingSchema.optional().describe("기본 `utf8`"),
|
|
2229
2229
|
expectedHash: storefrontFileHashSchema.nullable().optional().describe("쓰기 조건. 생략 = 조건 없음 · null = 새 파일 · 값 = 지금 해시가 같을 때만")
|
|
2230
|
-
})).min(1).max(50).refine((files) => new Set(files.map((file) => file.path)).size === files.length, { message: "같은 경로를 두 번 실을 수 없습니다" }) });
|
|
2230
|
+
})).min(1).max(50).refine((files) => new Set(files.map((file) => file.path)).size === files.length, { message: "같은 경로를 두 번 실을 수 없습니다" }) }));
|
|
2231
2231
|
const storefrontFileWriteResultSchema = z.object({ files: z.array(storefrontFileEntrySchema).describe("쓴 파일의 새 해시·크기 (요청 순서)") });
|
|
2232
2232
|
/** 409 `FILE_CHANGED`의 `error.details` */
|
|
2233
2233
|
const storefrontFileConflictDetailsSchema = z.object({ conflicts: z.array(z.object({
|
|
@@ -2235,21 +2235,21 @@ const storefrontFileConflictDetailsSchema = z.object({ conflicts: z.array(z.obje
|
|
|
2235
2235
|
currentHash: storefrontFileHashSchema.nullable().describe("지금 해시. 파일이 없으면 null")
|
|
2236
2236
|
})) });
|
|
2237
2237
|
/** `POST /v1/storefront/site/files/delete` — 없는 파일이면 `deleted: false`(조건을 주면 409 `FILE_CHANGED`) */
|
|
2238
|
-
const deleteStorefrontFileRequestSchema = z.object({
|
|
2238
|
+
const deleteStorefrontFileRequestSchema = strictRequest(z.object({
|
|
2239
2239
|
path: storefrontFilePathSchema,
|
|
2240
2240
|
expectedHash: storefrontFileHashSchema.optional().describe("주면 지금 해시가 같을 때만 지운다")
|
|
2241
|
-
});
|
|
2241
|
+
}));
|
|
2242
2242
|
const storefrontFileDeleteResultSchema = z.object({
|
|
2243
2243
|
path: z.string(),
|
|
2244
2244
|
deleted: z.boolean()
|
|
2245
2245
|
});
|
|
2246
2246
|
/** `POST /v1/storefront/site/files/move` — 이름 바꾸기·옮기기 */
|
|
2247
|
-
const moveStorefrontFileRequestSchema = z.object({
|
|
2247
|
+
const moveStorefrontFileRequestSchema = strictRequest(z.object({
|
|
2248
2248
|
from: storefrontFilePathSchema,
|
|
2249
2249
|
to: storefrontFilePathSchema,
|
|
2250
2250
|
expectedHash: storefrontFileHashSchema.optional().describe("주면 `from`의 지금 해시가 같을 때만"),
|
|
2251
2251
|
overwrite: z.boolean().optional().describe("`to`에 파일이 있으면 덮는가 (기본 false → 409 `FILE_EXISTS`)")
|
|
2252
|
-
});
|
|
2252
|
+
}));
|
|
2253
2253
|
const storefrontFileMoveResultSchema = z.object({
|
|
2254
2254
|
from: z.string(),
|
|
2255
2255
|
to: z.string(),
|
|
@@ -2259,7 +2259,7 @@ const storefrontFileMoveResultSchema = z.object({
|
|
|
2259
2259
|
/** `POST /v1/storefront/site/files/search` — 텍스트 파일 내용 검색(편집 환경이 없어도 된다) */
|
|
2260
2260
|
/** 정규식 검색의 패턴 길이 상한 — 편집 Sandbox가 실행 비용을 막으려고 더 좁게 받는다(sandbox-api §4.6) */
|
|
2261
2261
|
const STOREFRONT_SEARCH_REGEX_MAX_LENGTH = 100;
|
|
2262
|
-
const searchStorefrontFilesRequestSchema = z.object({
|
|
2262
|
+
const searchStorefrontFilesRequestSchema = strictRequest(z.object({
|
|
2263
2263
|
query: z.string().min(1).max(200),
|
|
2264
2264
|
regex: z.boolean().optional().describe("`query`를 정규식으로 본다 (기본 false = 글자 그대로). 정규식은 100자까지이고 중첩 수량자(`(a+)+`)·반복되는 선택(`(a|b)+`)·역참조·뒤 보기, 가변 수량자(`*`·`+`·`?`·`{n,m}`, 넓은 범위는 2로 센다) 차수 4 초과는 400 `INVALID_REQUEST`다. 차수가 클수록 긴 줄은 건너뛰고 비용이 크면 `truncated`로 끊는다"),
|
|
2265
2265
|
caseSensitive: z.boolean().optional().describe("대소문자를 가리는가 (기본 false)"),
|
|
@@ -2268,7 +2268,7 @@ const searchStorefrontFilesRequestSchema = z.object({
|
|
|
2268
2268
|
}).refine((body) => !body.regex || body.query.length <= 100, {
|
|
2269
2269
|
message: `정규식은 100자까지입니다`,
|
|
2270
2270
|
path: ["query"]
|
|
2271
|
-
});
|
|
2271
|
+
}));
|
|
2272
2272
|
const storefrontFileSearchResultSchema = z.object({
|
|
2273
2273
|
matches: z.array(z.object({
|
|
2274
2274
|
path: z.string(),
|
|
@@ -2284,11 +2284,11 @@ const storefrontFileSearchResultSchema = z.object({
|
|
|
2284
2284
|
* `GET /v1/storefront/site/checks/{checkId}`로 조회한다(권장 2~3초 간격). 사이트당 끝나지 않은 검사는 하나다 — 같은 종류가 돌고
|
|
2285
2285
|
* 있으면 그 검사를 다시 돌려주고, 다른 종류면 409 `CHECK_RUNNING`(`error.details.checkId`)이다. 출력은 꼬리만 준다.
|
|
2286
2286
|
*/
|
|
2287
|
-
const runStorefrontCheckRequestSchema = z.object({ check: z.enum([
|
|
2287
|
+
const runStorefrontCheckRequestSchema = strictRequest(z.object({ check: z.enum([
|
|
2288
2288
|
"typecheck",
|
|
2289
2289
|
"lint",
|
|
2290
2290
|
"test"
|
|
2291
|
-
]) });
|
|
2291
|
+
]) }));
|
|
2292
2292
|
const storefrontCheckStatusSchema = z.enum([
|
|
2293
2293
|
"QUEUED",
|
|
2294
2294
|
"RUNNING",
|
|
@@ -2436,10 +2436,10 @@ const storefrontBuildListQuerySchema = z.object({ limit: z.coerce.number().int()
|
|
|
2436
2436
|
* `POST /v1/storefront/site/versions` — 지금 작업 트리를 고정해 버전을 만든다(빌드 시작). 사이트당 끝나지 않은 빌드는
|
|
2437
2437
|
* 하나다(409 `STOREFRONT_BUILD_IN_PROGRESS`). 편집 환경이 없어도 된다.
|
|
2438
2438
|
*/
|
|
2439
|
-
const createStorefrontVersionRequestSchema = z.object({
|
|
2439
|
+
const createStorefrontVersionRequestSchema = strictRequest(z.object({
|
|
2440
2440
|
message: z.string().max(200).optional().describe("버전 메모 (에이전트는 바꾼 내용 요약)"),
|
|
2441
2441
|
expectedSourceRef: z.string().max(200).optional().describe("고정할 작업 트리 (`PUT /v1/storefront/site/source`의 `sourceRef`). 주면 지금 작업 트리가 이 값일 때만 버전을 만든다(다르면 409 `SOURCE_CHANGED`)")
|
|
2442
|
-
});
|
|
2442
|
+
}));
|
|
2443
2443
|
/** 버전 만들기 응답 (201) — 버전은 `BUILDING`, 빌드는 `QUEUED`로 시작한다. 결과는 `GET builds/{buildId}`로 본다 */
|
|
2444
2444
|
const storefrontVersionCreatedSchema = z.object({
|
|
2445
2445
|
version: storefrontVersionSchema,
|
|
@@ -2450,7 +2450,7 @@ const storefrontVersionCreatedSchema = z.object({
|
|
|
2450
2450
|
* 받는다(PAT·에이전트 불가). 5분 안에 열면 그 브라우저에 미리보기 호스트 전용 쿠키(12시간)가 생긴다. 편집 환경이
|
|
2451
2451
|
* 없으면 409 `EDIT_SESSION_REQUIRED`다(편집 환경이 `STARTING`이면 링크는 주고, 미리보기 화면이 기동 대기를 보여 준다).
|
|
2452
2452
|
*/
|
|
2453
|
-
const createStorefrontEditPreviewLinkRequestSchema = z.object({ path: z.string().max(512).optional().describe("열 경로 (`/`로 시작). 생략하면 `/`") });
|
|
2453
|
+
const createStorefrontEditPreviewLinkRequestSchema = strictRequest(z.object({ path: z.string().max(512).optional().describe("열 경로 (`/`로 시작). 생략하면 `/`") }));
|
|
2454
2454
|
/** 압축 파일 조각 하나의 최대 크기(바이트) */
|
|
2455
2455
|
const STOREFRONT_SOURCE_PART_BYTES = 4194304;
|
|
2456
2456
|
/** 내용 올리기 한 번의 최대 파일 수 */
|
|
@@ -2492,10 +2492,10 @@ const storefrontSourceArchivePartSchema = z.object({
|
|
|
2492
2492
|
content: z.string().describe("조각 내용 (base64)")
|
|
2493
2493
|
});
|
|
2494
2494
|
/** `POST /v1/storefront/site/source/blobs` — 파일 내용 올리기(해시 키, 다시 올려도 안전하다). 작업 트리는 바뀌지 않는다 */
|
|
2495
|
-
const uploadStorefrontSourceBlobsRequestSchema = z.object({ blobs: z.array(z.object({
|
|
2495
|
+
const uploadStorefrontSourceBlobsRequestSchema = strictRequest(z.object({ blobs: z.array(z.object({
|
|
2496
2496
|
hash: storefrontFileHashSchema,
|
|
2497
2497
|
content: z.string().describe("파일 내용 (base64, 1MiB까지)")
|
|
2498
|
-
})).min(1).max(200) });
|
|
2498
|
+
})).min(1).max(200) }));
|
|
2499
2499
|
const storefrontSourceBlobsResultSchema = z.object({
|
|
2500
2500
|
stored: z.number().int().min(0).describe("새로 올린 수"),
|
|
2501
2501
|
existing: z.number().int().min(0).describe("이미 있던 수")
|
|
@@ -2504,7 +2504,7 @@ const storefrontSourceBlobsResultSchema = z.object({
|
|
|
2504
2504
|
* `PUT /v1/storefront/site/source` — 작업 트리 전체를 이 목록으로 바꾼다(없는 경로는 지운다). 전부 아니면 전무다.
|
|
2505
2505
|
* `package.json`·`pnpm-lock.yaml`도 바꿀 수 있고 의존성은 허용 목록 안이어야 한다. `.env*`·작업 트리 밖 경로는 받지 않는다.
|
|
2506
2506
|
*/
|
|
2507
|
-
const replaceStorefrontSourceRequestSchema = z.object({
|
|
2507
|
+
const replaceStorefrontSourceRequestSchema = strictRequest(z.object({
|
|
2508
2508
|
expectedBaseRef: storefrontSourceRefSchema.optional().describe("내려받은 작업 트리. 그 뒤 작업 트리가 바뀌었으면 이 트리를 기준으로 3-way 병합하고, 같은 곳을 고쳤으면 409 `SOURCE_CHANGED`다. 생략하면 조건 없이 덮어쓴다"),
|
|
2509
2509
|
files: z.array(z.object({
|
|
2510
2510
|
path: storefrontFilePathSchema,
|
|
@@ -2514,7 +2514,7 @@ const replaceStorefrontSourceRequestSchema = z.object({
|
|
|
2514
2514
|
endEditSession: z.boolean().optional().describe("열린 편집 환경을 끄고 진행한다 (기본 false — 열려 있으면 409 `EDIT_SESSION_OPEN`)"),
|
|
2515
2515
|
packageJson: z.string().max(STOREFRONT_FILE_MAX_BYTES).optional().describe("`files`에 `package.json`이 있으면 그 내용(utf8). 서버가 의존성을 판정한다. 해시가 목록과 다르면 400 `DEPENDENCY_FILES_MISMATCH`"),
|
|
2516
2516
|
lockfile: z.string().max(STOREFRONT_FILE_MAX_BYTES).optional().describe("`files`에 `pnpm-lock.yaml`이 있으면 그 내용(utf8)")
|
|
2517
|
-
});
|
|
2517
|
+
}));
|
|
2518
2518
|
const storefrontSourceReplacedSchema = z.object({
|
|
2519
2519
|
sourceRef: storefrontSourceRefSchema.describe("새 작업 트리(버전 만들기의 `expectedSourceRef`). 다음 올리기의 기준은 `uploadedRef`다"),
|
|
2520
2520
|
previousRef: z.string().describe("바꾸기 전 작업 트리"),
|
|
@@ -2556,25 +2556,25 @@ const storefrontGitRepositorySchema = z.object({
|
|
|
2556
2556
|
defaultBranch: z.string()
|
|
2557
2557
|
});
|
|
2558
2558
|
/** `POST /v1/storefront/site/git/installations` — GitHub App 설치 화면에서 돌아온 값(`installation_id`·`code`) */
|
|
2559
|
-
const linkStorefrontGitInstallationRequestSchema = z.object({
|
|
2559
|
+
const linkStorefrontGitInstallationRequestSchema = strictRequest(z.object({
|
|
2560
2560
|
installationId: z.number().int().positive(),
|
|
2561
2561
|
code: z.string().min(1).max(200).describe("설치 화면이 돌려준 GitHub 사용자 인증 코드(한 번만 쓴다)"),
|
|
2562
2562
|
state: z.string().min(1).max(200).describe("설치 화면으로 보내기 전에 받은 연결 state(`POST …/git/installations/state`, 한 번만 쓴다)")
|
|
2563
|
-
});
|
|
2563
|
+
}));
|
|
2564
2564
|
/** `POST /v1/storefront/site/git/installations/state` — GitHub 설치 화면에 싣는 1회용 state(이 상점·사용자, 10분) */
|
|
2565
2565
|
const storefrontGitConnectStateSchema = z.object({
|
|
2566
2566
|
state: z.string(),
|
|
2567
2567
|
expiresAt: z.string()
|
|
2568
2568
|
});
|
|
2569
2569
|
/** `POST /v1/storefront/site/git/connect` */
|
|
2570
|
-
const connectStorefrontGitRequestSchema = z.object({
|
|
2570
|
+
const connectStorefrontGitRequestSchema = strictRequest(z.object({
|
|
2571
2571
|
installationId: z.number().int().positive(),
|
|
2572
2572
|
repoId: z.number().int().positive(),
|
|
2573
2573
|
productionBranch: z.string().min(1).max(200).regex(/^[A-Za-z0-9._-]+(\/[A-Za-z0-9._-]+)*$/).optional().describe("운영 배포 브랜치(생략하면 저장소 기본 브랜치)"),
|
|
2574
2574
|
rootDirectory: z.string().max(200).regex(/^$|^[A-Za-z0-9._-]+(\/[A-Za-z0-9._-]+)*$/).refine((v) => !v.split("/").some((seg) => seg === "." || seg === ".."), "상대 경로만").optional().describe("저장소 안 사이트 루트(생략하면 저장소 루트)")
|
|
2575
|
-
});
|
|
2575
|
+
}));
|
|
2576
2576
|
/** `PUT /v1/storefront/site/deploy-approval` — 승인 없이 배포(#138) */
|
|
2577
|
-
const setStorefrontDeployApprovalRequestSchema = z.object({ deployWithoutApproval: z.boolean() });
|
|
2577
|
+
const setStorefrontDeployApprovalRequestSchema = strictRequest(z.object({ deployWithoutApproval: z.boolean() }));
|
|
2578
2578
|
//#endregion
|
|
2579
2579
|
//#region src/schemas/ai.ts
|
|
2580
2580
|
/**
|
|
@@ -2945,18 +2945,18 @@ const storeSetupBriefSchema = z.object({
|
|
|
2945
2945
|
heroProduct: z.string().trim().max(500).describe("첫 화면에 크게 걸 상품 (비우면 AI가 정한다)"),
|
|
2946
2946
|
slug: storefrontSlugSchema.optional().describe("바라는 스토어프론트 주소")
|
|
2947
2947
|
});
|
|
2948
|
-
const createAiConversationRequestSchema = z.object({
|
|
2948
|
+
const createAiConversationRequestSchema = strictRequest(z.object({
|
|
2949
2949
|
/** 웹사이트에서 남긴 초안(#124 M2) — 연결한 사용자만 쓴다. 주면 첫 메시지로 실행을 시작한다 */
|
|
2950
2950
|
draftId: z.string().max(64).optional(),
|
|
2951
2951
|
/** 질문 화면의 답 — `draftId`와 함께만 쓴다. 초안 앞에 붙어 첫 메시지가 된다 */
|
|
2952
2952
|
brief: storeSetupBriefSchema.optional()
|
|
2953
|
-
});
|
|
2953
|
+
}));
|
|
2954
2954
|
const createAiConversationResponseSchema = z.object({
|
|
2955
2955
|
conversation: aiConversationSummarySchema,
|
|
2956
2956
|
/** `draftId`로 시작했으면 첫 실행 */
|
|
2957
2957
|
run: aiStreamAccessSchema.nullable()
|
|
2958
2958
|
});
|
|
2959
|
-
const sendAiMessageRequestSchema = z.object({
|
|
2959
|
+
const sendAiMessageRequestSchema = strictRequest(z.object({
|
|
2960
2960
|
text: z.string().trim().max(AI_MESSAGE_MAX_CHARS),
|
|
2961
2961
|
attachments: z.array(aiAttachmentSchema).max(5).default([]),
|
|
2962
2962
|
/** 선택지 버튼으로 보낸 답이면 그 값(화면 표시는 `text`) */
|
|
@@ -2965,7 +2965,7 @@ const sendAiMessageRequestSchema = z.object({
|
|
|
2965
2965
|
selection: aiPreviewSelectionSchema.optional(),
|
|
2966
2966
|
/** 디자인 탭에서 고른 아트보드(2026-10-14) — 모델은 그 아트보드를 기준으로 고친다 */
|
|
2967
2967
|
designSelection: aiDesignSelectionSchema.optional()
|
|
2968
|
-
}).refine((body) => body.text.length > 0 || body.attachments.length > 0, { message: "내용이나 첨부가 있어야 합니다" });
|
|
2968
|
+
}).refine((body) => body.text.length > 0 || body.attachments.length > 0, { message: "내용이나 첨부가 있어야 합니다" }));
|
|
2969
2969
|
const sendAiMessageResponseSchema = aiStreamAccessSchema.extend({ messageId: z.string() });
|
|
2970
2970
|
/**
|
|
2971
2971
|
* AI가 바꾸는 화면 영역 — 쓰기 도구가 시작되면 이벤트에 싣고, 패널이 그 화면으로 옮겨 반영된 데이터를 다시 읽는다(셀러가 AI가 하는 일을 같이 본다).
|
|
@@ -3150,13 +3150,13 @@ const STORE_DRAFT_MAX_ATTACHMENTS = 5;
|
|
|
3150
3150
|
const STORE_DRAFT_ATTACHMENT_MAX_BYTES = 10485760;
|
|
3151
3151
|
/** 연결하지 않은 초안을 지우기까지 */
|
|
3152
3152
|
const STORE_DRAFT_RETENTION_DAYS = 7;
|
|
3153
|
-
const createStoreDraftRequestSchema = z.object({
|
|
3153
|
+
const createStoreDraftRequestSchema = strictRequest(z.object({
|
|
3154
3154
|
text: z.string().trim().min(1).max(AI_MESSAGE_MAX_CHARS),
|
|
3155
3155
|
attachments: z.array(z.object({
|
|
3156
3156
|
filename: z.string().trim().min(1).max(200),
|
|
3157
3157
|
size: z.number().int().positive().max(STORE_DRAFT_ATTACHMENT_MAX_BYTES)
|
|
3158
3158
|
})).max(5).default([])
|
|
3159
|
-
});
|
|
3159
|
+
}));
|
|
3160
3160
|
const createStoreDraftResponseSchema = z.object({
|
|
3161
3161
|
draftId: z.string(),
|
|
3162
3162
|
/** 첨부마다 브라우저가 파일을 그대로 PUT할 자리(요청 순서와 같다) */
|
|
@@ -3841,7 +3841,7 @@ const claimHoldResultSchema = z.object({
|
|
|
3841
3841
|
claimId: z.string(),
|
|
3842
3842
|
holdExpiresAt: z.string()
|
|
3843
3843
|
});
|
|
3844
|
-
const rejectClaimRequestSchema = z.object({
|
|
3844
|
+
const rejectClaimRequestSchema = strictRequest(z.object({
|
|
3845
3845
|
reason: z.enum([
|
|
3846
3846
|
"USED_PRODUCT",
|
|
3847
3847
|
"MISSING_COMPONENT",
|
|
@@ -3851,7 +3851,7 @@ const rejectClaimRequestSchema = z.object({
|
|
|
3851
3851
|
]),
|
|
3852
3852
|
detail: z.string().min(10).max(1e3),
|
|
3853
3853
|
evidenceImages: z.array(z.url()).max(5).optional()
|
|
3854
|
-
});
|
|
3854
|
+
}));
|
|
3855
3855
|
const approveClaimResultSchema = z.object({
|
|
3856
3856
|
claimId: z.string(),
|
|
3857
3857
|
refundAmount: z.number().int().describe("실제 환불한 금액 — 수량 비율 상품 실결제 금액(쿠폰 배분을 뺀 금액) + 배송비 환불 − 반품 배송비 − 조건부 무료배송 미달 차감 − 쿠폰 조건 미달 차감(`couponChargeAmount`) + 되돌린 쿠폰 차감(`couponChargeRefundAmount`) − 추가 차감(`deductAmount`)"),
|
|
@@ -4034,12 +4034,12 @@ const storePointPolicySchema = z.object({
|
|
|
4034
4034
|
* `PUT /v1/store/point-policy` 본문 — 준 항목만 바꾼다(생략 = 저장된 값 유지). 리뷰 적립·구매 적립 중 하나라도 켜면 유효 기간이 있어야 한다
|
|
4035
4035
|
* (본문이나 저장된 값, 없으면 400 `POINT_EXPIRY_REQUIRED`).
|
|
4036
4036
|
*/
|
|
4037
|
-
const updatePointPolicyRequestSchema = z.object({
|
|
4037
|
+
const updatePointPolicyRequestSchema = strictRequest(z.object({
|
|
4038
4038
|
reviewReward: reviewRewardPolicySchema.optional(),
|
|
4039
4039
|
purchaseEarn: purchaseEarnPolicySchema.optional(),
|
|
4040
4040
|
use: pointUsePolicySchema.optional(),
|
|
4041
4041
|
expiryDays: expiryDays.optional()
|
|
4042
|
-
});
|
|
4042
|
+
}));
|
|
4043
4043
|
/** 응답의 원장 유형 — 값이 추가될 수 있어 문자열로 받는다 */
|
|
4044
4044
|
const responsePointEntryTypeSchema = z.union([z.enum(POINT_ENTRY_TYPES), z.string()]).describe("유형. `EARN_REVIEW` 리뷰 적립 · `REVOKE_REVIEW` 리뷰 삭제 회수 · `EARN_PURCHASE` 구매 적립 · `EXPIRE` 소멸 · `ADJUST` 셀러 지급·차감 · `OPENING` 기존 잔액 이전 · `USE_RESERVE` 결제 사용(결제 시작 때 빠짐) · `USE_RELEASE` 결제가 실패·만료되어 되돌림 · `REFUND_RESTORE` 취소·반품 환불로 반환. 값이 추가될 수 있어 문자열로 받는다");
|
|
4045
4045
|
/** 원장 항목 — 셀러 조회용(사유·행위자 포함) */
|
|
@@ -4086,14 +4086,14 @@ const POINT_ADJUSTMENT_MAX = 1e7;
|
|
|
4086
4086
|
* 지급의 소멸 시각은 `expiresAt`(생략하면 정책 유효 기간, 정책이 없으면 400 `POINT_EXPIRY_REQUIRED`), 차감은 소멸이 이른 적립금부터 뺀다
|
|
4087
4087
|
* (잔액보다 많으면 409 `POINT_BALANCE_INSUFFICIENT`).
|
|
4088
4088
|
*/
|
|
4089
|
-
const pointAdjustmentRequestSchema = z.object({
|
|
4089
|
+
const pointAdjustmentRequestSchema = strictRequest(z.object({
|
|
4090
4090
|
amount: z.number().int().min(-1e7).max(POINT_ADJUSTMENT_MAX).refine((value) => value !== 0, "0원은 조정할 수 없습니다").describe("지급은 양수, 차감은 음수(원)"),
|
|
4091
4091
|
reason: z.string().trim().min(1).max(200).describe("사유(필수). 구매자에게는 보이지 않는다"),
|
|
4092
4092
|
expiresAt: z.iso.datetime({ offset: true }).optional().describe("지급분의 소멸 시각. 생략하면 정책 유효 기간(`expiryDays`)으로 정한다. **정책 유효 기간이 없으면 지급에 필수다**(없으면 400 `POINT_EXPIRY_REQUIRED`). 차감에는 주지 않는다")
|
|
4093
4093
|
}).refine((value) => !(value.amount < 0 && value.expiresAt), {
|
|
4094
4094
|
message: "차감에는 소멸 시각을 줄 수 없습니다",
|
|
4095
4095
|
path: ["expiresAt"]
|
|
4096
|
-
});
|
|
4096
|
+
}));
|
|
4097
4097
|
const pointAdjustmentResultSchema = z.object({
|
|
4098
4098
|
entry: pointEntrySchema,
|
|
4099
4099
|
balance: z.number().int().describe("조정 뒤 잔액")
|
|
@@ -4184,7 +4184,7 @@ const productNoticeSchema = z.object({
|
|
|
4184
4184
|
values: z.record(z.string(), z.string()).describe("칸 키 → 셀러가 적은 값. 비운 칸은 없다")
|
|
4185
4185
|
});
|
|
4186
4186
|
const MAX_NOTICE_VALUE_LENGTH = 1500;
|
|
4187
|
-
const productNoticeRequestSchema = z.
|
|
4187
|
+
const productNoticeRequestSchema = z.strictObject({
|
|
4188
4188
|
schemaId: z.string().trim().min(1).max(50).describe("품목군 코드(`GET /v1/info-schemas`)"),
|
|
4189
4189
|
version: z.number().int().positive().optional().describe("품목군 버전. 생략하면 현재 버전"),
|
|
4190
4190
|
values: z.record(z.string().max(50), z.string().max(MAX_NOTICE_VALUE_LENGTH)).default({}).describe("칸 키 → 값. 출처가 `legal`인 칸은 받지 않는다(400). 빈 문자열은 비운 칸으로 저장하지 않는다. 칸 길이는 스키마 `maxLength`")
|
|
@@ -4208,13 +4208,13 @@ const productCertificationsSchema = z.object({
|
|
|
4208
4208
|
number: z.string().describe("인증 번호")
|
|
4209
4209
|
})).describe("인증서. 배열 순서가 표시 순서다")
|
|
4210
4210
|
});
|
|
4211
|
-
const productCertificationsRequestSchema = z.
|
|
4212
|
-
subjects: z.array(z.
|
|
4211
|
+
const productCertificationsRequestSchema = z.strictObject({
|
|
4212
|
+
subjects: z.array(z.strictObject({
|
|
4213
4213
|
subject: z.string().trim().min(1).max(50),
|
|
4214
4214
|
status: z.enum(CERTIFICATION_STATUSES),
|
|
4215
4215
|
exemption: z.string().trim().min(1).max(50).nullish()
|
|
4216
4216
|
})).max(20).default([]).describe("인증 대상 여부. `REQUIRED`인데 그 대상의 인증 종류(`attrs.kinds`) 인증서가 `items`에 없으면 400 `CERTIFICATION_REQUIRED`, `EXEMPT`인데 사유가 없거나 대상이 허용하지 않는 사유면 400 `CERTIFICATION_EXEMPTION_INVALID`"),
|
|
4217
|
-
items: z.array(z.
|
|
4217
|
+
items: z.array(z.strictObject({
|
|
4218
4218
|
kind: z.string().trim().min(1).max(50),
|
|
4219
4219
|
agency: z.string().trim().max(100).nullish(),
|
|
4220
4220
|
number: z.string().trim().min(1).max(100)
|
|
@@ -4236,11 +4236,11 @@ const infoSectionSchema = z.object({
|
|
|
4236
4236
|
createdAt: z.string(),
|
|
4237
4237
|
updatedAt: z.string()
|
|
4238
4238
|
});
|
|
4239
|
-
const infoSectionRequestSchema = z.object({
|
|
4239
|
+
const infoSectionRequestSchema = strictRequest(z.object({
|
|
4240
4240
|
title: z.string().trim().min(1).max(100),
|
|
4241
4241
|
body: z.string().default(""),
|
|
4242
4242
|
bodyJson: z.string().nullable().optional()
|
|
4243
|
-
});
|
|
4243
|
+
}));
|
|
4244
4244
|
/**
|
|
4245
4245
|
* 안내 섹션 번역 (이슈 #134) — 언어 하나의 제목·본문. 섹션 필드는 상점 기본 언어 값이다. 비운 칸(null)은 스토어프론트가 기본 언어 값으로 대체한다.
|
|
4246
4246
|
* 섹션을 연결한 모든 상품이 같은 번역을 보인다.
|
|
@@ -4313,14 +4313,14 @@ const calendarQuerySchema = z.object({
|
|
|
4313
4313
|
* 달력 설정 — 기간의 날짜마다 재고·가격을 덮어쓴다(없는 날은 만든다). `stock`·`price`는 준 것만 바꾸고, `price: null`은 조합 가격으로
|
|
4314
4314
|
* 되돌린다. `daysOfWeek`(0=일~6=토)를 주면 그 요일만 바꾼다
|
|
4315
4315
|
*/
|
|
4316
|
-
const setCalendarRequestSchema = z.object({
|
|
4316
|
+
const setCalendarRequestSchema = strictRequest(z.object({
|
|
4317
4317
|
variantIds: z.array(z.string()).min(1).max(50),
|
|
4318
4318
|
from: calendarDateSchema,
|
|
4319
4319
|
to: calendarDateSchema,
|
|
4320
4320
|
stock: z.number().int().min(0).max(1e5).optional(),
|
|
4321
4321
|
price: z.number().int().min(0).multipleOf(10).nullable().optional(),
|
|
4322
4322
|
daysOfWeek: z.array(z.number().int().min(0).max(6)).min(1).max(7).optional()
|
|
4323
|
-
}).refine((value) => value.stock !== void 0 || value.price !== void 0, { message: "stock이나 price 중 하나는 있어야 합니다" });
|
|
4323
|
+
}).refine((value) => value.stock !== void 0 || value.price !== void 0, { message: "stock이나 price 중 하나는 있어야 합니다" }));
|
|
4324
4324
|
//#endregion
|
|
4325
4325
|
//#region src/schemas/upload.ts
|
|
4326
4326
|
/**
|
|
@@ -4426,11 +4426,11 @@ function uploadRejection(purpose, filename, size) {
|
|
|
4426
4426
|
* 끝나면 `url`이 공개 주소가 된다. 서버가 객체 키를 정하므로 요청은 파일명만 보낸다
|
|
4427
4427
|
* (확장자만 쓰이고 이름 자체는 저장되지 않는다).
|
|
4428
4428
|
*/
|
|
4429
|
-
const uploadRequestSchema = z.object({
|
|
4429
|
+
const uploadRequestSchema = strictRequest(z.object({
|
|
4430
4430
|
filename: z.string().min(1).max(255),
|
|
4431
4431
|
purpose: uploadPurposeSchema.default("description").describe("업로드 용도. 허용 확장자와 크기 상한이 용도별로 다르다"),
|
|
4432
4432
|
size: z.number().int().positive().optional().describe("파일 바이트 크기. 주면 용도별 상한을 발급 전에 검사한다")
|
|
4433
|
-
});
|
|
4433
|
+
}));
|
|
4434
4434
|
const uploadTicketSchema = z.object({
|
|
4435
4435
|
/** 업로드 완료 후 공개될 주소 */
|
|
4436
4436
|
url: z.url(),
|
|
@@ -4611,7 +4611,7 @@ function customInputLabelForbiddenWord(label) {
|
|
|
4611
4611
|
}
|
|
4612
4612
|
/** 옵션명·값 이름 — 앞뒤 공백을 지우고 빈 값은 받지 않는다 */
|
|
4613
4613
|
const optionNameSchema = z.string().trim().min(1).max(50);
|
|
4614
|
-
const addonValueRequestSchema = z.
|
|
4614
|
+
const addonValueRequestSchema = z.strictObject({
|
|
4615
4615
|
/** 전달 시 기존 값 행을 유지(UPDATE). 생략하면 신규 발급 */
|
|
4616
4616
|
valueId: z.string().optional(),
|
|
4617
4617
|
name: optionNameSchema,
|
|
@@ -4619,14 +4619,14 @@ const addonValueRequestSchema = z.object({
|
|
|
4619
4619
|
prices: additionalCurrencyPricesSchema.optional(),
|
|
4620
4620
|
usable: z.boolean().default(true)
|
|
4621
4621
|
});
|
|
4622
|
-
const addonGroupRequestSchema = z.
|
|
4622
|
+
const addonGroupRequestSchema = z.strictObject({
|
|
4623
4623
|
/** 전달 시 기존 옵션명 행을 유지(UPDATE). 조합 옵션명의 id는 줄 수 없다(방식을 바꾸려면 새 옵션명으로 보낸다) */
|
|
4624
4624
|
groupId: z.string().optional(),
|
|
4625
4625
|
name: optionNameSchema,
|
|
4626
4626
|
required: z.boolean().default(false),
|
|
4627
4627
|
values: z.array(addonValueRequestSchema).min(1).max(100)
|
|
4628
4628
|
});
|
|
4629
|
-
const customInputRequestSchema = z.
|
|
4629
|
+
const customInputRequestSchema = z.strictObject({
|
|
4630
4630
|
/** 전달 시 기존 항목 행을 유지(UPDATE). 생략하면 신규 발급 */
|
|
4631
4631
|
inputId: z.string().optional(),
|
|
4632
4632
|
label: z.string().trim().min(1).max(30),
|
|
@@ -4754,18 +4754,18 @@ function productImageRejection(url) {
|
|
|
4754
4754
|
* (`WHERE ... is not null`)의 판정이 갈려 409 대신 500이 나간다.
|
|
4755
4755
|
*/
|
|
4756
4756
|
const codeSchema = z.string().trim().min(1).max(64);
|
|
4757
|
-
const optionValueRequestSchema = z.
|
|
4757
|
+
const optionValueRequestSchema = z.strictObject({
|
|
4758
4758
|
/** 전달 시 기존 값 행을 유지(UPDATE). 생략하면 신규 발급 */
|
|
4759
4759
|
valueId: z.string().optional(),
|
|
4760
4760
|
name: z.string().min(1).max(50)
|
|
4761
4761
|
});
|
|
4762
|
-
const optionGroupRequestSchema = z.
|
|
4762
|
+
const optionGroupRequestSchema = z.strictObject({
|
|
4763
4763
|
/** 전달 시 기존 축 행을 유지(UPDATE). 생략하면 신규 발급 */
|
|
4764
4764
|
groupId: z.string().optional(),
|
|
4765
4765
|
name: z.string().min(1).max(50),
|
|
4766
4766
|
values: z.array(optionValueRequestSchema).min(1).max(100)
|
|
4767
4767
|
});
|
|
4768
|
-
const productVariantRequestSchema = z.
|
|
4768
|
+
const productVariantRequestSchema = z.strictObject({
|
|
4769
4769
|
/** 기존 variant 유지 시 전달. 생략하면 신규 생성 */
|
|
4770
4770
|
variantId: z.string().optional(),
|
|
4771
4771
|
/** optionGroups 순서대로 각 축에서 고른 값 이름. 서버가 이름 → valueId를 해석한다 */
|
|
@@ -4787,7 +4787,7 @@ const productVariantRequestSchema = z.object({
|
|
|
4787
4787
|
attributes: z.record(z.string().regex(/^[a-z][a-zA-Z0-9_]{0,39}$/), z.string().max(100)).refine((value) => Object.keys(value).length <= 10, { message: "조합 속성은 10개까지입니다" }).optional().describe("조합 속성(이슈 #115) — 키 → 값 문자열(예: 회차 일시 `showAt`). 플로우가 `item.option.<키>`로 읽고 타입(날짜·숫자 등)은 플로우가 선언한다. 생략하면 지금 값 유지(새 조합은 없음), `{}`면 비운다")
|
|
4788
4788
|
});
|
|
4789
4789
|
/** 축 × 값 → variant 전체 교체 요청 (PUT /products/{productId}/options) */
|
|
4790
|
-
const optionModelRequestSchema = z.
|
|
4790
|
+
const optionModelRequestSchema = z.strictObject({
|
|
4791
4791
|
optionGroups: z.array(optionGroupRequestSchema).max(3).default([]),
|
|
4792
4792
|
variants: z.array(productVariantRequestSchema).min(1).max(500),
|
|
4793
4793
|
addonGroups: z.array(addonGroupRequestSchema).max(5).optional().describe("추가 선택 옵션명 전체(이슈 #59). 주면 통째로 바꾸고, 생략하면 등록은 없음·수정은 현재 값을 그대로 둔다. 끄려면 빈 배열을 보낸다"),
|
|
@@ -4847,7 +4847,7 @@ const productWriteShape = {
|
|
|
4847
4847
|
*/
|
|
4848
4848
|
descriptionJson: z.string().nullable().optional(),
|
|
4849
4849
|
images: productImagesRequestSchema.optional().describe("상품 이미지 주소 목록. 첫 장이 대표 이미지(썸네일)다"),
|
|
4850
|
-
attributes: z.array(z.
|
|
4850
|
+
attributes: z.array(z.strictObject({
|
|
4851
4851
|
attributeId: z.string(),
|
|
4852
4852
|
value: z.string()
|
|
4853
4853
|
})).optional(),
|
|
@@ -4898,12 +4898,12 @@ const productListQuerySchema = z.object({
|
|
|
4898
4898
|
sort: productSortSchema.optional().describe("정렬 (기본 createdAt.desc)"),
|
|
4899
4899
|
...pageQueryShape()
|
|
4900
4900
|
});
|
|
4901
|
-
const discountRequestSchema = z.object({
|
|
4901
|
+
const discountRequestSchema = strictRequest(z.object({
|
|
4902
4902
|
discountType: z.enum(["RATE", "AMOUNT"]),
|
|
4903
4903
|
discountValue: z.number().int().min(1),
|
|
4904
4904
|
startAt: z.string().optional(),
|
|
4905
4905
|
endAt: z.string().optional()
|
|
4906
|
-
});
|
|
4906
|
+
}));
|
|
4907
4907
|
//#endregion
|
|
4908
4908
|
//#region src/schemas/webhook-event.ts
|
|
4909
4909
|
const envelopeFields = {
|
|
@@ -5896,7 +5896,7 @@ const webhookDeliveryListQuerySchema = z.object({
|
|
|
5896
5896
|
});
|
|
5897
5897
|
/** 실패 건 일괄 재전송 응답 — `POST /webhooks/{id}/deliveries/redeliver-failed` */
|
|
5898
5898
|
const webhookRedeliverFailedResultSchema = z.object({ queued: z.number().int().min(0).describe("재전송 대기열에 올린 건수. 최근 72시간의 `FAILED` 중 아직 재전송되지 않은 건이 대상이며 최대 500건이다") });
|
|
5899
|
-
const webhookTestRequestSchema = z.object({ eventType: eventTypeSchema.describe("샘플 페이로드로 보낼 이벤트 타입") });
|
|
5899
|
+
const webhookTestRequestSchema = strictRequest(z.object({ eventType: eventTypeSchema.describe("샘플 페이로드로 보낼 이벤트 타입") }));
|
|
5900
5900
|
/** 테스트 이벤트 발송 결과 (동기 반환). 재시도·정지 판정·전송 기록이 없다 */
|
|
5901
5901
|
const webhookTestResultSchema = z.object({
|
|
5902
5902
|
responseStatusCode: z.number().int().nullable().describe("수신 서버의 HTTP 응답 코드. 연결 실패·타임아웃·차단이면 null"),
|
|
@@ -6112,10 +6112,10 @@ const appInstallationActivitySchema = z.object({
|
|
|
6112
6112
|
* - `{ result }`: accounts 설치 동의 결과(1회용 JWT). 개발자 포털 [내 상점에 설치]가 이 길로 온다
|
|
6113
6113
|
* - `{ appId, settings }`: 상점 플랫폼 앱 목록 › 설치 화면에서 동의하고 설정까지 채워 바로 설치한다
|
|
6114
6114
|
*/
|
|
6115
|
-
const appInstallRequestSchema = z.union([z.object({ result: z.string().min(1).describe("accounts가 발급한 설치 결과 JWT(`typ: sayren-install+jwt`)") }), z.object({
|
|
6115
|
+
const appInstallRequestSchema = strictRequest(z.union([z.object({ result: z.string().min(1).describe("accounts가 발급한 설치 결과 JWT(`typ: sayren-install+jwt`)") }), z.object({
|
|
6116
6116
|
appId: z.string().min(1),
|
|
6117
6117
|
settings: z.record(z.string(), settingsValueSchema).default({}).describe("설치와 함께 저장할 설정. 비밀 필드도 여기서 넣는다")
|
|
6118
|
-
})]);
|
|
6118
|
+
})]));
|
|
6119
6119
|
/** 앱 목록 정보 — 현재 버전이 올린 아이콘·README(없으면 매니페스트 `iconUrl`) */
|
|
6120
6120
|
const appListingSchema = z.object({
|
|
6121
6121
|
description: z.string().nullable(),
|
|
@@ -6479,7 +6479,7 @@ const categoryAttributeSchema = z.object({
|
|
|
6479
6479
|
unit: z.string().nullable(),
|
|
6480
6480
|
allowedValues: z.array(z.string()).optional()
|
|
6481
6481
|
});
|
|
6482
|
-
const categoryCreateRequestSchema = z.object({
|
|
6482
|
+
const categoryCreateRequestSchema = strictRequest(z.object({
|
|
6483
6483
|
name: z.string().min(1).max(100),
|
|
6484
6484
|
parentId: z.string().nullable().optional(),
|
|
6485
6485
|
sortOrder: z.number().int().optional(),
|
|
@@ -6490,8 +6490,8 @@ const categoryCreateRequestSchema = z.object({
|
|
|
6490
6490
|
metaTitle: z.string().max(100).nullable().optional(),
|
|
6491
6491
|
metaDescription: z.string().max(300).nullable().optional(),
|
|
6492
6492
|
metaKeywords: z.string().max(200).nullable().optional()
|
|
6493
|
-
});
|
|
6494
|
-
const categoryPatchRequestSchema = z.object({
|
|
6493
|
+
}));
|
|
6494
|
+
const categoryPatchRequestSchema = strictRequest(z.object({
|
|
6495
6495
|
name: z.string().min(1).max(100).optional(),
|
|
6496
6496
|
parentId: z.string().nullable().optional(),
|
|
6497
6497
|
sortOrder: z.number().int().optional(),
|
|
@@ -6503,7 +6503,7 @@ const categoryPatchRequestSchema = z.object({
|
|
|
6503
6503
|
metaDescription: z.string().max(300).nullable().optional(),
|
|
6504
6504
|
metaKeywords: z.string().max(200).nullable().optional(),
|
|
6505
6505
|
flowId: z.string().nullable().optional().describe("주문 흐름(이슈 #115) — 게시한 흐름 id, null이면 기본 흐름. 맨 아래 카테고리에만 지정한다(400 `CATEGORY_NOT_LEAF`). 게시하지 않은 흐름은 409 `FLOW_NOT_PUBLISHED`")
|
|
6506
|
-
});
|
|
6506
|
+
}));
|
|
6507
6507
|
const categoryDeleteStrategySchema = z.enum(["reparent", "cascade"]);
|
|
6508
6508
|
/**
|
|
6509
6509
|
* `GET /v1/categories` 쿼리 — OpenAPI 문서용 형태(값은 쿼리 문자열). 응답 형태가 쿼리로 갈린다:
|
|
@@ -6826,7 +6826,7 @@ const periodRefine = (body, ctx) => {
|
|
|
6826
6826
|
* 직접 선택(`MANUAL`)은 `productIds`가 처음 담을 상품과 순서다. 조건 자동(`AUTOMATIC`)은 `rules`가 있어야 하고 `productIds`를 받지 않는다.
|
|
6827
6827
|
* `productSort`를 생략하면 직접 선택은 `MANUAL`, 조건 자동은 `LATEST`(판매량 상위 조건이 있으면 `BEST_SELLING`)다. 조건 자동은 `MANUAL`을 쓸 수 없다.
|
|
6828
6828
|
*/
|
|
6829
|
-
const collectionCreateRequestSchema = z.object({
|
|
6829
|
+
const collectionCreateRequestSchema = strictRequest(z.object({
|
|
6830
6830
|
title: z.string().trim().min(1).max(60).describe("제목"),
|
|
6831
6831
|
slug: slugField.optional(),
|
|
6832
6832
|
type: z.enum(COLLECTION_TYPES).default("MANUAL").describe("유형. `MANUAL`(직접 선택)·`AUTOMATIC`(조건 자동). 만든 뒤 바꿀 수 없다"),
|
|
@@ -6841,12 +6841,12 @@ const collectionCreateRequestSchema = z.object({
|
|
|
6841
6841
|
metaTitle: commonFields.metaTitle.optional(),
|
|
6842
6842
|
metaDescription: commonFields.metaDescription.optional(),
|
|
6843
6843
|
productIds: productIdsField.optional()
|
|
6844
|
-
}).superRefine(periodRefine);
|
|
6844
|
+
}).superRefine(periodRefine));
|
|
6845
6845
|
/**
|
|
6846
6846
|
* `PATCH /v1/collections/{collectionId}` — 준 항목만 바뀐다. `type`을 주면 400 `COLLECTION_TYPE_IMMUTABLE`이다.
|
|
6847
6847
|
* `revision`을 주면 그 번호일 때만 바뀌고, 그 사이 다른 수정이 있었으면 409 `COLLECTION_CHANGED`다.
|
|
6848
6848
|
*/
|
|
6849
|
-
const collectionPatchRequestSchema = z.object({
|
|
6849
|
+
const collectionPatchRequestSchema = strictRequest(z.object({
|
|
6850
6850
|
title: z.string().trim().min(1).max(60).optional(),
|
|
6851
6851
|
slug: slugField.optional(),
|
|
6852
6852
|
type: z.string().optional().describe("바꿀 수 없다. 주면 400 `COLLECTION_TYPE_IMMUTABLE`"),
|
|
@@ -6861,14 +6861,14 @@ const collectionPatchRequestSchema = z.object({
|
|
|
6861
6861
|
metaTitle: commonFields.metaTitle.optional(),
|
|
6862
6862
|
metaDescription: commonFields.metaDescription.optional(),
|
|
6863
6863
|
revision: z.number().int().min(0).optional().describe("읽은 수정 번호")
|
|
6864
|
-
}).superRefine(periodRefine);
|
|
6864
|
+
}).superRefine(periodRefine));
|
|
6865
6865
|
/** `PUT /v1/collections/{collectionId}/products` — 담긴 상품을 통째로 바꾼다(순서 포함) */
|
|
6866
|
-
const collectionProductsReplaceRequestSchema = z.object({
|
|
6866
|
+
const collectionProductsReplaceRequestSchema = strictRequest(z.object({
|
|
6867
6867
|
productIds: productIdsField,
|
|
6868
6868
|
revision: z.number().int().min(0).optional().describe("읽은 수정 번호")
|
|
6869
|
-
});
|
|
6869
|
+
}));
|
|
6870
6870
|
/** `POST /v1/collections/{collectionId}/products` — 뒤에 붙인다. 이미 담긴 상품은 그 자리에 둔다 */
|
|
6871
|
-
const collectionProductsAddRequestSchema = z.object({ productIds: z.array(z.string().min(1)).min(1).max(500) });
|
|
6871
|
+
const collectionProductsAddRequestSchema = strictRequest(z.object({ productIds: z.array(z.string().min(1)).min(1).max(500) }));
|
|
6872
6872
|
/** 컬렉션에 담긴 상품 한 줄 */
|
|
6873
6873
|
const collectionProductSchema = z.object({
|
|
6874
6874
|
productId: z.string(),
|
|
@@ -6905,10 +6905,10 @@ const collectionProductListQuerySchema = z.object({ ...pageQueryShape({
|
|
|
6905
6905
|
maxSize: MAX_COLLECTION_PRODUCTS
|
|
6906
6906
|
}) });
|
|
6907
6907
|
/** `POST /v1/collections/preview` — 저장하지 않고 조건으로 나오는 상품을 본다 */
|
|
6908
|
-
const collectionPreviewRequestSchema = z.object({
|
|
6908
|
+
const collectionPreviewRequestSchema = strictRequest(z.object({
|
|
6909
6909
|
rules: collectionRulesSchema,
|
|
6910
6910
|
productSort: collectionProductSortRequestSchema.optional().describe("미리보기 순서. 생략하면 `LATEST`(판매량 상위 조건이 있으면 `BEST_SELLING`)이고, `MANUAL`이면 `LATEST`다")
|
|
6911
|
-
});
|
|
6911
|
+
}));
|
|
6912
6912
|
/** 미리보기 응답 — 조건을 만족하는 판매 중 상품 수와 앞 20개 */
|
|
6913
6913
|
const collectionPreviewSchema = z.object({
|
|
6914
6914
|
count: z.number().int().describe("조건을 만족하는 판매 중 상품 수(조회 시각 기준)"),
|
|
@@ -7039,10 +7039,10 @@ const orderItemFlowSchema = z.object({
|
|
|
7039
7039
|
branches: z.array(flowStateSchema).optional().describe("활성 위치가 둘 이상(병렬 가지·하위 흐름)이면 각 위치의 상태. `flowState`는 그 가운데 가장 앞 단계다")
|
|
7040
7040
|
});
|
|
7041
7041
|
/** 상태 직접 이동(#115) — 연결선을 지나지 않고 다른 상태로 옮긴다. 블록(재고·환불·송장)은 실행하지 않는다 */
|
|
7042
|
-
const moveFlowStateRequestSchema = z.object({
|
|
7042
|
+
const moveFlowStateRequestSchema = strictRequest(z.object({
|
|
7043
7043
|
to: z.string().min(1).describe("옮길 상태 키(`moveTargets[].key`)"),
|
|
7044
7044
|
reason: z.string().trim().max(200).optional().describe("옮긴 사유(실행 기록에 남는다)")
|
|
7045
|
-
});
|
|
7045
|
+
}));
|
|
7046
7046
|
const flowRunStepSchema = z.object({
|
|
7047
7047
|
step: z.number().int(),
|
|
7048
7048
|
block: z.string().describe("실행한 블록 종류"),
|
|
@@ -7138,7 +7138,7 @@ const commerceFlowSchema = commerceFlowSummarySchema.extend({
|
|
|
7138
7138
|
}))
|
|
7139
7139
|
});
|
|
7140
7140
|
/** 진행 중 주문을 활성 버전으로 옮기기(#115) — 한 번에 `limit`개까지. 남으면 다시 부른다 */
|
|
7141
|
-
const migrateFlowVersionRequestSchema = z.object({ limit: z.number().int().min(1).max(500).optional().describe("한 번에 옮길 흐름 수(기본 100)") });
|
|
7141
|
+
const migrateFlowVersionRequestSchema = strictRequest(z.object({ limit: z.number().int().min(1).max(500).optional().describe("한 번에 옮길 흐름 수(기본 100)") }));
|
|
7142
7142
|
const migrateFlowVersionResultSchema = z.object({
|
|
7143
7143
|
fromVersion: z.number().int(),
|
|
7144
7144
|
toVersion: z.number().int(),
|
|
@@ -7150,19 +7150,19 @@ const migrateFlowVersionResultSchema = z.object({
|
|
|
7150
7150
|
})).describe("이번에 옮기지 못한 흐름(그 버전에 남는다)"),
|
|
7151
7151
|
remaining: z.number().int().describe("아직 이 버전에 남은 진행 중 흐름 수(건너뛴 것 포함)")
|
|
7152
7152
|
});
|
|
7153
|
-
const createCommerceFlowRequestSchema = z.object({
|
|
7153
|
+
const createCommerceFlowRequestSchema = strictRequest(z.object({
|
|
7154
7154
|
key: z.string().regex(/^[a-z][a-z0-9-]{1,48}$/).describe("상점 안 유일한 키(소문자·숫자·하이픈)"),
|
|
7155
7155
|
name: z.string().min(1).max(60).optional(),
|
|
7156
7156
|
presetKey: z.string().optional().describe("출발한 프리셋(출처로 남는다). 없으면 빈 플로우"),
|
|
7157
7157
|
graph: flowDocumentSchema.optional().describe("편집기에서 고친 그래프(#115). 없으면 프리셋 그래프 그대로, 프리셋도 없으면 빈 흐름(결제 완료 노드 하나)이다"),
|
|
7158
7158
|
definition: flowDocumentSchema.optional().describe("흐름 설정 `requires`(progression·groupClaim·fulfillmentMethods). 없으면 프리셋의 설정"),
|
|
7159
7159
|
variables: z.record(z.string(), z.unknown()).optional().describe("쓰지 않는다(무시). 값은 그래프의 노드 설정 안에 있다(#115)")
|
|
7160
|
-
});
|
|
7161
|
-
const saveFlowDraftRequestSchema = z.object({
|
|
7160
|
+
}));
|
|
7161
|
+
const saveFlowDraftRequestSchema = strictRequest(z.object({
|
|
7162
7162
|
graph: flowDocumentSchema,
|
|
7163
7163
|
definition: flowDocumentSchema.optional().describe("흐름 설정 `requires`(progression·groupClaim·fulfillmentMethods)"),
|
|
7164
7164
|
variables: z.record(z.string(), z.unknown()).optional().describe("쓰지 않는다(무시). 값은 그래프의 노드 설정 안에 있다(#115)")
|
|
7165
|
-
});
|
|
7165
|
+
}));
|
|
7166
7166
|
const flowIssueSchema = z.object({
|
|
7167
7167
|
code: z.string(),
|
|
7168
7168
|
message: z.string(),
|
|
@@ -7251,7 +7251,7 @@ const flowDraftOperationSchema = z.discriminatedUnion("op", [
|
|
|
7251
7251
|
])).optional()
|
|
7252
7252
|
}).describe("흐름 설정(진행 단위·묶음 클레임·이행 방식) 바꾸기")
|
|
7253
7253
|
]);
|
|
7254
|
-
const applyFlowOperationsRequestSchema = z.object({ operations: z.array(flowDraftOperationSchema).min(1).describe("차례대로 적용한다. 묶음은 원자적이다 — 하나라도 형식이 틀리면 아무것도 바꾸지 않는다(400)") });
|
|
7254
|
+
const applyFlowOperationsRequestSchema = strictRequest(z.object({ operations: z.array(flowDraftOperationSchema).min(1).describe("차례대로 적용한다. 묶음은 원자적이다 — 하나라도 형식이 틀리면 아무것도 바꾸지 않는다(400)") }));
|
|
7255
7255
|
const flowOperationsResultSchema = z.object({
|
|
7256
7256
|
revision: z.number().int().describe("저장 뒤 초안 revision — 다음 조작의 If-Match"),
|
|
7257
7257
|
changed: z.object({
|
|
@@ -7282,10 +7282,10 @@ const flowSecretSchema = z.object({
|
|
|
7282
7282
|
headerNames: z.array(z.string()).describe("저장한 헤더 이름(값은 보이지 않는다)"),
|
|
7283
7283
|
updatedAt: z.string()
|
|
7284
7284
|
});
|
|
7285
|
-
const saveFlowSecretRequestSchema = z.object({
|
|
7285
|
+
const saveFlowSecretRequestSchema = strictRequest(z.object({
|
|
7286
7286
|
url: z.url().refine((value) => value.startsWith("https://"), { message: "https 주소만 받습니다" }).describe("요청 주소. https만, 사설·루프백 주소는 400 `FLOW_SECRET_URL_INVALID`"),
|
|
7287
7287
|
headers: z.record(z.string().regex(/^[A-Za-z0-9-]{1,64}$/), z.string().max(2e3)).refine((value) => Object.keys(value).length <= 10, { message: "헤더는 10개까지입니다" }).default({}).describe("함께 보낼 헤더(인증 등). 값은 암호화해 두고 다시 보여 주지 않는다")
|
|
7288
|
-
});
|
|
7288
|
+
}));
|
|
7289
7289
|
/**
|
|
7290
7290
|
* 주문 전체 행동 결과(#115) — 같은 행동을 주문의 주문상품마다 실행한다. 한 주문상품의 실패가 다른 주문상품을 되돌리지 않는다(부분 실행).
|
|
7291
7291
|
* 묶음 진행이면 한 번 실행이 묶음의 주문상품을 함께 움직인다(COMPLETED로 함께 나온다). 그 행동이 없는 주문상품은 SKIPPED다
|
|
@@ -7314,11 +7314,11 @@ const simulationEventSchema = z.object({
|
|
|
7314
7314
|
]).describe("start 결제 승인 · seller.action 셀러 버튼 · buyer.action 구매자 버튼 · event 사건 · timer 타이머 키"),
|
|
7315
7315
|
key: z.string().optional().describe("행동·사건·타이머 키(start는 없음)")
|
|
7316
7316
|
});
|
|
7317
|
-
const simulateFlowRequestSchema = z.object({
|
|
7317
|
+
const simulateFlowRequestSchema = strictRequest(z.object({
|
|
7318
7318
|
startAt: z.iso.datetime({ offset: true }).optional().describe("가상 시계 시작(기본: 지금)"),
|
|
7319
7319
|
context: z.record(z.string(), z.unknown()).optional().describe("식이 읽을 값(item·order·buyer·product). 비우면 견본 주문 값"),
|
|
7320
7320
|
steps: z.array(z.union([z.object({ event: simulationEventSchema }), z.object({ advance: z.string().regex(/^P/).describe("시간 경과(ISO 8601 기간)") })])).min(1).max(100)
|
|
7321
|
-
});
|
|
7321
|
+
}));
|
|
7322
7322
|
const simulationStepSchema = z.object({
|
|
7323
7323
|
step: z.number().int(),
|
|
7324
7324
|
input: z.record(z.string(), z.unknown()).describe("넣은 사건(`event`) 또는 시간 경과(`advance`)"),
|
|
@@ -7344,7 +7344,7 @@ const simulateFlowResultSchema = z.object({
|
|
|
7344
7344
|
errors: z.array(flowIssueSchema),
|
|
7345
7345
|
trace: z.array(simulationStepSchema)
|
|
7346
7346
|
});
|
|
7347
|
-
const replayFlowRequestSchema = z.object({ orderItemId: z.string().describe("재생할 주문상품 — 그 흐름의 실행 기록을 초안으로 다시 돌린다") });
|
|
7347
|
+
const replayFlowRequestSchema = strictRequest(z.object({ orderItemId: z.string().describe("재생할 주문상품 — 그 흐름의 실행 기록을 초안으로 다시 돌린다") }));
|
|
7348
7348
|
const replayFlowResultSchema = z.object({
|
|
7349
7349
|
orderItemId: z.string(),
|
|
7350
7350
|
compiled: z.boolean(),
|
|
@@ -7492,7 +7492,7 @@ const flowEditSessionViewSchema = z.object({
|
|
|
7492
7492
|
streamToken: z.string()
|
|
7493
7493
|
}).nullable().describe("흐름 채널 구독(SSE, `Authorization: Bearer streamToken`) — session·ops·saved·released 이벤트. 중계가 없는 환경이면 null")
|
|
7494
7494
|
});
|
|
7495
|
-
const claimFlowEditSessionRequestSchema = z.object({ takeover: z.boolean().optional().describe("다른 주체가 잡고 있어도 넘겨받는다. 앞 주체의 저장하지 않은 수정은 사라지고, AI 도우미면 그 실행이 멈춘다") });
|
|
7495
|
+
const claimFlowEditSessionRequestSchema = strictRequest(z.object({ takeover: z.boolean().optional().describe("다른 주체가 잡고 있어도 넘겨받는다. 앞 주체의 저장하지 않은 수정은 사라지고, AI 도우미면 그 실행이 멈춘다") }));
|
|
7496
7496
|
//#endregion
|
|
7497
7497
|
//#region src/schemas/promotion.ts
|
|
7498
7498
|
/**
|
|
@@ -8171,7 +8171,7 @@ const couponTargetInputSchema = z.strictObject({
|
|
|
8171
8171
|
});
|
|
8172
8172
|
const couponTimeField = (description) => z.iso.datetime({ offset: true }).nullable().optional().describe(description);
|
|
8173
8173
|
/** `POST /v1/coupons` */
|
|
8174
|
-
const createCouponRequestSchema = z.strictObject({
|
|
8174
|
+
const createCouponRequestSchema = strictRequest(z.strictObject({
|
|
8175
8175
|
name: z.string().trim().min(1).max(60).describe(descriptions.name),
|
|
8176
8176
|
description: z.string().max(300).nullable().optional().describe(descriptions.description),
|
|
8177
8177
|
kind: couponKindSchema,
|
|
@@ -8202,12 +8202,12 @@ const createCouponRequestSchema = z.strictObject({
|
|
|
8202
8202
|
if (body.issueMethod !== "CODE" && body.perMemberUseLimit != null) issue("perMemberUseLimit", "발급형 쿠폰은 발급 한 장이 한 번 쓰입니다. 1인 발급 수 한도를 사용하십시오");
|
|
8203
8203
|
if (!(body.conditions?.memberOnly ?? COUPON_CONDITIONS_DEFAULTS.memberOnly) && (body.issueMethod !== "CODE" || body.perMemberUseLimit != null)) issue("conditions", "비회원에게 여는 쿠폰은 코드 입력형이면서 1인 사용 한도가 없어야 합니다");
|
|
8204
8204
|
if (body.startsAt && body.endsAt && Date.parse(body.endsAt) <= Date.parse(body.startsAt)) issue("endsAt", "종료 시각은 시작 시각보다 뒤여야 합니다");
|
|
8205
|
-
});
|
|
8205
|
+
}));
|
|
8206
8206
|
/**
|
|
8207
8207
|
* `PATCH /v1/coupons/{couponId}` — 준 항목만 바뀐다. 발급·사용이 시작된 쿠폰(`locked`)은 이름·설명·종료일 연장·
|
|
8208
8208
|
* 한도 상향만 받고, 그 밖의 항목이 현재 값과 다르면 409 `COUPON_LOCKED`다. 종류·발급 방식은 바꿀 수 없다.
|
|
8209
8209
|
*/
|
|
8210
|
-
const updateCouponRequestSchema = z.strictObject({
|
|
8210
|
+
const updateCouponRequestSchema = strictRequest(z.strictObject({
|
|
8211
8211
|
name: z.string().trim().min(1).max(60).optional().describe(descriptions.name),
|
|
8212
8212
|
description: z.string().max(300).nullable().optional().describe(descriptions.description),
|
|
8213
8213
|
benefit: couponBenefitSchema.optional(),
|
|
@@ -8221,7 +8221,7 @@ const updateCouponRequestSchema = z.strictObject({
|
|
|
8221
8221
|
totalUseLimit: limitField(descriptions.totalUseLimit),
|
|
8222
8222
|
perMemberIssueLimit: limitField(descriptions.perMemberIssueLimit),
|
|
8223
8223
|
perMemberUseLimit: limitField(descriptions.perMemberUseLimit)
|
|
8224
|
-
});
|
|
8224
|
+
}));
|
|
8225
8225
|
/** 쿠폰 응답 */
|
|
8226
8226
|
const couponSchema = z.object({
|
|
8227
8227
|
couponId: z.string(),
|
|
@@ -8489,7 +8489,7 @@ const customerOrderSchema = z.object({
|
|
|
8489
8489
|
}))
|
|
8490
8490
|
});
|
|
8491
8491
|
/** `POST /v1/customers/{id}/withdraw` 본문 */
|
|
8492
|
-
const withdrawCustomerRequestSchema = z.object({
|
|
8492
|
+
const withdrawCustomerRequestSchema = strictRequest(z.object({
|
|
8493
8493
|
reason: z.enum([
|
|
8494
8494
|
"BUYER_REQUEST",
|
|
8495
8495
|
"DORMANT",
|
|
@@ -8497,7 +8497,7 @@ const withdrawCustomerRequestSchema = z.object({
|
|
|
8497
8497
|
"OTHER"
|
|
8498
8498
|
]).describe("처리 사유 — 구매자 요청·장기 미이용·부정 이용·기타. 감사 로그에 남는다"),
|
|
8499
8499
|
memo: z.string().max(200).optional().describe("사유 보충(선택)")
|
|
8500
|
-
});
|
|
8500
|
+
}));
|
|
8501
8501
|
/** `WITHDRAWAL_BLOCKED`의 `error.details` — 탈퇴를 막는 항목 수(스토어프론트 탈퇴와 같다) */
|
|
8502
8502
|
const customerWithdrawalBlockedDetailsSchema = z.object({
|
|
8503
8503
|
inProgressOrderItems: z.number().int(),
|
|
@@ -8638,15 +8638,15 @@ const inventoryOperationResultSchema = z.object({
|
|
|
8638
8638
|
createdAt: z.string()
|
|
8639
8639
|
});
|
|
8640
8640
|
/** 수거 완료의 재입고 지정(Q3) — 생략하면 조합은 할당 순서(교환 → 판매), 위치는 반품지에 연결된 위치 → 할당 위치다 */
|
|
8641
|
-
const claimCollectCompleteRequestSchema = z.object({ restock: z.object({
|
|
8641
|
+
const claimCollectCompleteRequestSchema = strictRequest(z.object({ restock: z.object({
|
|
8642
8642
|
optionId: z.string().min(1).optional().describe("돌아온 조합 — 그 주문상품의 할당 조합이어야 한다(아니면 400 `INVALID_RESTOCK_OPTION`)"),
|
|
8643
8643
|
inventoryLocationId: z.string().min(1).optional().describe("재입고할 재고 위치")
|
|
8644
|
-
}).optional() });
|
|
8645
|
-
const claimRedeliveryRequestSchema = z.object({
|
|
8644
|
+
}).optional() }));
|
|
8645
|
+
const claimRedeliveryRequestSchema = strictRequest(z.object({
|
|
8646
8646
|
carrierCode: z.string().min(1),
|
|
8647
8647
|
trackingNumber: z.string().min(1),
|
|
8648
8648
|
inventoryLocationId: z.string().min(1).optional().describe("교환품을 보낼 재고 위치. 생략하면 그 상품의 기준 위치")
|
|
8649
|
-
});
|
|
8649
|
+
}));
|
|
8650
8650
|
//#endregion
|
|
8651
8651
|
//#region src/schemas/login.ts
|
|
8652
8652
|
/** 소셜 로그인 공급자 */
|
|
@@ -8679,14 +8679,14 @@ const loginSettingsSchema = z.object({
|
|
|
8679
8679
|
providers: z.array(loginProviderStatusSchema)
|
|
8680
8680
|
});
|
|
8681
8681
|
/** `PATCH /v1/store/login-settings` */
|
|
8682
|
-
const updateLoginSettingsRequestSchema = z.object({ passwordLoginEnabled: z.boolean() });
|
|
8682
|
+
const updateLoginSettingsRequestSchema = strictRequest(z.object({ passwordLoginEnabled: z.boolean() }));
|
|
8683
8683
|
/** `PUT /v1/store/auth-providers/{provider}` — 저장 전에 연결 테스트를 한다 */
|
|
8684
|
-
const saveLoginProviderRequestSchema = z.object({
|
|
8684
|
+
const saveLoginProviderRequestSchema = strictRequest(z.object({
|
|
8685
8685
|
clientId: z.string().trim().min(1).max(200),
|
|
8686
8686
|
clientSecret: z.string().trim().min(1).max(500)
|
|
8687
|
-
});
|
|
8687
|
+
}));
|
|
8688
8688
|
/** `PATCH /v1/store/auth-providers/{provider}` */
|
|
8689
|
-
const updateLoginProviderRequestSchema = z.object({ enabled: z.boolean() });
|
|
8689
|
+
const updateLoginProviderRequestSchema = strictRequest(z.object({ enabled: z.boolean() }));
|
|
8690
8690
|
/** 연결 테스트 결과 */
|
|
8691
8691
|
const loginProviderTestSchema = z.object({
|
|
8692
8692
|
ok: z.boolean(),
|
|
@@ -8876,36 +8876,36 @@ const payPalCredentialsInputSchema = z.object({
|
|
|
8876
8876
|
clientSecret: secretInput().optional().describe("Secret. 비밀 값. 최초 연결 시 필수, 수정 시 생략하면 기존 값을 유지한다")
|
|
8877
8877
|
});
|
|
8878
8878
|
const upsertEnvironment = () => pgEnvironmentSchema.describe("저장할 자격 증명 세트. PG마다 `TEST`(샌드박스)·`LIVE`(실결제) 세트를 따로 둔다. 어느 세트로 결제할지는 이 값이 아니라 샌드박스 스위치(`sandboxMode`)가 정한다");
|
|
8879
|
-
const upsertTossPaymentsProviderRequestSchema = z.object({
|
|
8879
|
+
const upsertTossPaymentsProviderRequestSchema = strictRequest(z.object({
|
|
8880
8880
|
environment: upsertEnvironment(),
|
|
8881
8881
|
credentials: tossPaymentsCredentialsInputSchema
|
|
8882
|
-
});
|
|
8883
|
-
const upsertPortOneProviderRequestSchema = z.object({
|
|
8882
|
+
}));
|
|
8883
|
+
const upsertPortOneProviderRequestSchema = strictRequest(z.object({
|
|
8884
8884
|
environment: upsertEnvironment(),
|
|
8885
8885
|
credentials: portOneCredentialsInputSchema
|
|
8886
|
-
});
|
|
8887
|
-
const upsertKakaoPayProviderRequestSchema = z.object({
|
|
8886
|
+
}));
|
|
8887
|
+
const upsertKakaoPayProviderRequestSchema = strictRequest(z.object({
|
|
8888
8888
|
environment: upsertEnvironment(),
|
|
8889
8889
|
credentials: kakaoPayCredentialsInputSchema
|
|
8890
|
-
});
|
|
8891
|
-
const upsertNaverPayProviderRequestSchema = z.object({
|
|
8890
|
+
}));
|
|
8891
|
+
const upsertNaverPayProviderRequestSchema = strictRequest(z.object({
|
|
8892
8892
|
environment: upsertEnvironment(),
|
|
8893
8893
|
credentials: naverPayCredentialsInputSchema
|
|
8894
|
-
});
|
|
8895
|
-
const upsertPayPalProviderRequestSchema = z.object({
|
|
8894
|
+
}));
|
|
8895
|
+
const upsertPayPalProviderRequestSchema = strictRequest(z.object({
|
|
8896
8896
|
environment: upsertEnvironment(),
|
|
8897
8897
|
credentials: payPalCredentialsInputSchema
|
|
8898
|
-
});
|
|
8899
|
-
const updatePaymentProviderRequestSchema = z.object({
|
|
8898
|
+
}));
|
|
8899
|
+
const updatePaymentProviderRequestSchema = strictRequest(z.object({
|
|
8900
8900
|
enabled: z.boolean().optional().describe("사용 여부. 켜려면 활성 환경(샌드박스면 TEST, 아니면 LIVE) 자격 증명이 모두 저장돼 있어야 한다(`PAYMENT_PROVIDER_INCOMPLETE`)"),
|
|
8901
8901
|
sandboxMode: z.boolean().optional().describe("샌드박스 스위치. `true`면 새 결제에 TEST 세트를 쓴다(항상 허용). `false`(라이브 전환)는 라이브 개방 조건을 모두 충족해야 한다(`LIVE_GATE_NOT_MET`)"),
|
|
8902
8902
|
easyPayProviders: z.array(easyPayProviderSchema).max(easyPayProviderSchema.options.length).optional().describe("이 PG로 받는 간편결제사 — 통째로 바꾼다. PG 계약(포트원은 채널)에 추가한 간편결제만 켠다. 켠 간편결제사마다 스토어프론트 결제 옵션이 하나씩 생긴다(포트원은 간편결제를 받는 채널이 있어야 한다)")
|
|
8903
|
-
}).refine((body) => body.enabled !== void 0 || body.sandboxMode !== void 0 || body.easyPayProviders !== void 0, { message: "enabled·sandboxMode·easyPayProviders 중 하나는 보내야 합니다" });
|
|
8904
|
-
const verifyPaymentProviderRequestSchema = z.object({ environment: pgEnvironmentSchema.optional().describe("연결 테스트할 자격 증명 세트. 생략하면 활성 환경(샌드박스면 TEST, 아니면 LIVE)") });
|
|
8905
|
-
const updatePaymentSettingsRequestSchema = z.object({
|
|
8903
|
+
}).refine((body) => body.enabled !== void 0 || body.sandboxMode !== void 0 || body.easyPayProviders !== void 0, { message: "enabled·sandboxMode·easyPayProviders 중 하나는 보내야 합니다" }));
|
|
8904
|
+
const verifyPaymentProviderRequestSchema = strictRequest(z.object({ environment: pgEnvironmentSchema.optional().describe("연결 테스트할 자격 증명 세트. 생략하면 활성 환경(샌드박스면 TEST, 아니면 LIVE)") }));
|
|
8905
|
+
const updatePaymentSettingsRequestSchema = strictRequest(z.object({
|
|
8906
8906
|
priority: z.array(pgProviderSchema).min(1).max(50).optional().describe("PG 우선순위(앞이 1순위) — 스토어프론트 결제 옵션 목록의 PG 순서다. 연결된 PG 수만큼 **모두 한 번씩** 나열한다(길이 상한 50은 안전장치다). 빠지거나 중복되거나 연결되지 않은 PG가 있으면 `INVALID_PRIORITY`"),
|
|
8907
8907
|
checkoutOrigins: z.array(z.string().max(200)).max(20).optional().describe("결제 도메인 — 통째로 바꾼다. 결제를 마친 구매자가 돌아올 스토어프론트 origin(`https://shop.example.com`, https·경로 없음). 결제 시작의 `returnUrl`은 이 목록 안이어야 한다. 비어 있으면 실결제를 시작할 수 없다(테스트 결제는 localhost 허용). 같은 도메인을 PG 가맹 정보에도 등록한다. 형식이 틀리면 `INVALID_CHECKOUT_ORIGIN`")
|
|
8908
|
-
}).refine((body) => body.priority !== void 0 || body.checkoutOrigins !== void 0, { message: "priority 또는 checkoutOrigins 중 하나는 보내야 합니다" });
|
|
8908
|
+
}).refine((body) => body.priority !== void 0 || body.checkoutOrigins !== void 0, { message: "priority 또는 checkoutOrigins 중 하나는 보내야 합니다" }));
|
|
8909
8909
|
const maskedSecretSchema = z.object({
|
|
8910
8910
|
field: z.string().describe("비밀 필드 이름 (`secretKey`·`apiSecret`·`webhookSecret`)"),
|
|
8911
8911
|
configured: z.boolean().describe("값이 저장돼 있는가"),
|
|
@@ -9201,7 +9201,7 @@ const paymentListQuerySchema = z.object({
|
|
|
9201
9201
|
size: z.string().optional().describe("페이지 크기 (기본 20, 최대 100)")
|
|
9202
9202
|
});
|
|
9203
9203
|
/** 테스트모드 키 활성화 요청(2026-10-14) — `PUT /v1/store/payment-providers/{provider}/platform-test-keys` */
|
|
9204
|
-
const setPlatformTestKeysRequestSchema = z.object({ enabled: z.boolean().describe("`true`면 sayren 지정 테스트 키를 TEST 세트로 저장하고 PG를 켠다. `false`면 고정만 푼다(TEST 세트는 그대로)") });
|
|
9204
|
+
const setPlatformTestKeysRequestSchema = strictRequest(z.object({ enabled: z.boolean().describe("`true`면 sayren 지정 테스트 키를 TEST 세트로 저장하고 PG를 켠다. `false`면 고정만 푼다(TEST 세트는 그대로)") }));
|
|
9205
9205
|
/** 결제 상태 PG 한 줄 — 비밀 값·공개 키 없이 연결·사용·환경·테스트모드 키만 */
|
|
9206
9206
|
const paymentStatusProviderSchema = z.object({
|
|
9207
9207
|
provider: pgProviderResponseSchema,
|
|
@@ -9375,7 +9375,7 @@ const deliveryMethodSchema = z.enum([
|
|
|
9375
9375
|
"QUICK",
|
|
9376
9376
|
"NOTHING"
|
|
9377
9377
|
]);
|
|
9378
|
-
const dispatchRequestSchema = z.object({
|
|
9378
|
+
const dispatchRequestSchema = strictRequest(z.object({
|
|
9379
9379
|
deliveryMethod: deliveryMethodSchema,
|
|
9380
9380
|
carrierCode: z.string().optional(),
|
|
9381
9381
|
trackingNumber: z.string().optional(),
|
|
@@ -9387,7 +9387,7 @@ const dispatchRequestSchema = z.object({
|
|
|
9387
9387
|
* 잡 전체를 멈출 수 있어 입력에서 좁혔다. 검증을 통과한 값도 저장 전에 UTC ISO로 정규화한다.
|
|
9388
9388
|
*/
|
|
9389
9389
|
dispatchedAt: z.iso.datetime({ offset: true }).optional()
|
|
9390
|
-
}).refine((value) => value.deliveryMethod === "DIRECT" || value.deliveryMethod === "NOTHING" ? true : !!value.carrierCode && !!value.trackingNumber, { message: "COURIER/QUICK 발송은 carrierCode와 trackingNumber가 필수입니다" });
|
|
9390
|
+
}).refine((value) => value.deliveryMethod === "DIRECT" || value.deliveryMethod === "NOTHING" ? true : !!value.carrierCode && !!value.trackingNumber, { message: "COURIER/QUICK 발송은 carrierCode와 trackingNumber가 필수입니다" }));
|
|
9391
9391
|
const deliveryInfoSchema = z.object({
|
|
9392
9392
|
orderItemId: z.string(),
|
|
9393
9393
|
deliveryMethod: deliveryMethodSchema,
|
|
@@ -9434,7 +9434,7 @@ const carrierSchema = z.object({
|
|
|
9434
9434
|
name: z.string(),
|
|
9435
9435
|
trackingUrlTemplate: z.string()
|
|
9436
9436
|
});
|
|
9437
|
-
const holdOrderItemRequestSchema = z.object({
|
|
9437
|
+
const holdOrderItemRequestSchema = strictRequest(z.object({
|
|
9438
9438
|
reason: z.enum([
|
|
9439
9439
|
"OUT_OF_STOCK",
|
|
9440
9440
|
"CUSTOMER_REQUEST",
|
|
@@ -9443,8 +9443,8 @@ const holdOrderItemRequestSchema = z.object({
|
|
|
9443
9443
|
]),
|
|
9444
9444
|
detail: z.string().max(500).optional(),
|
|
9445
9445
|
expectedDispatchDate: z.string()
|
|
9446
|
-
});
|
|
9447
|
-
const cancelBySellerRequestSchema = z.object({
|
|
9446
|
+
}));
|
|
9447
|
+
const cancelBySellerRequestSchema = strictRequest(z.object({
|
|
9448
9448
|
reason: z.enum([
|
|
9449
9449
|
"OUT_OF_STOCK",
|
|
9450
9450
|
"PRICE_ERROR",
|
|
@@ -9454,17 +9454,17 @@ const cancelBySellerRequestSchema = z.object({
|
|
|
9454
9454
|
detail: z.string().max(500).optional(),
|
|
9455
9455
|
quantity: z.number().int().min(1).optional().describe("취소할 수량. 생략하면 남은 수량 전체다. 남은 수량보다 크면 400 `INVALID_QUANTITY`. 일부만 취소하면 주문상품은 상태를 유지한 채 `canceledQuantity`가 늘고, 전 수량이 소진되면 `CANCELED`가 된다"),
|
|
9456
9456
|
holdRefund: z.boolean().optional().describe("true면 주문만 취소하고 결제 환불은 대기로 남긴다(#115). 정산·재고·적립금 반환은 그대로 하고, PG 환불은 결제 상세의 `POST /v1/payments/refunds/{refundId}/process`로 따로 한다. 생략하면 취소와 함께 환불한다")
|
|
9457
|
-
});
|
|
9457
|
+
}));
|
|
9458
9458
|
/**
|
|
9459
9459
|
* 제공 처리 요청 (이슈 #48) — `POST /v1/order-items/{orderItemId}/fulfill`. 배송 없는 주문상품(`MANUAL`)을
|
|
9460
9460
|
* 제공 완료(`DELIVERED`)로 바꾼다.
|
|
9461
9461
|
*/
|
|
9462
|
-
const fulfillRequestSchema = z.object({ note: z.string().trim().max(1e3).optional().describe("구매자에게 보일 이용 안내(예: 예약 방법, 수령 장소). 주문 상세에 실린다") });
|
|
9462
|
+
const fulfillRequestSchema = strictRequest(z.object({ note: z.string().trim().max(1e3).optional().describe("구매자에게 보일 이용 안내(예: 예약 방법, 수령 장소). 주문 상세에 실린다") }));
|
|
9463
9463
|
/** 제공 처리 일괄 요청 — `POST /v1/order-items/fulfill` */
|
|
9464
|
-
const fulfillBulkRequestSchema = z.object({
|
|
9464
|
+
const fulfillBulkRequestSchema = strictRequest(z.object({
|
|
9465
9465
|
orderItemIds: z.array(z.string()).min(1).max(100),
|
|
9466
9466
|
note: fulfillRequestSchema.shape.note
|
|
9467
|
-
});
|
|
9467
|
+
}));
|
|
9468
9468
|
/** `POST /v1/order-items/{orderItemId}/cancel` 응답 — 셀러 직권취소 환불 결과 */
|
|
9469
9469
|
const cancelBySellerResultSchema = z.object({
|
|
9470
9470
|
orderItemId: z.string(),
|
|
@@ -9822,17 +9822,17 @@ const savedOptionSetSchema = z.object({
|
|
|
9822
9822
|
createdAt: z.string(),
|
|
9823
9823
|
updatedAt: z.string()
|
|
9824
9824
|
});
|
|
9825
|
-
const savedOptionValueRequestSchema = z.
|
|
9825
|
+
const savedOptionValueRequestSchema = z.strictObject({
|
|
9826
9826
|
name: nameSchema,
|
|
9827
9827
|
additionalPrice: z.number().int().min(0).default(0)
|
|
9828
9828
|
});
|
|
9829
|
-
const savedOptionGroupRequestSchema = z.
|
|
9829
|
+
const savedOptionGroupRequestSchema = z.strictObject({
|
|
9830
9830
|
name: nameSchema,
|
|
9831
9831
|
kind: optionGroupKindSchema,
|
|
9832
9832
|
required: z.boolean().default(false).describe("추가 선택만 쓴다. 조합 옵션명에 true를 주면 400이다"),
|
|
9833
9833
|
values: z.array(savedOptionValueRequestSchema).min(1).max(100)
|
|
9834
9834
|
});
|
|
9835
|
-
const savedCustomInputRequestSchema = z.
|
|
9835
|
+
const savedCustomInputRequestSchema = z.strictObject({
|
|
9836
9836
|
label: z.string().trim().min(1).max(30),
|
|
9837
9837
|
placeholder: z.string().trim().max(100).nullish(),
|
|
9838
9838
|
maxLength: z.number().int().min(1).max(200),
|
|
@@ -9846,7 +9846,7 @@ const savedCustomInputRequestSchema = z.object({
|
|
|
9846
9846
|
* 저장 옵션 만들기·수정 본문. 조합 옵션명은 최대 `MAX_OPTION_GROUPS`개, 추가 선택은 최대 `MAX_ADDON_GROUPS`개이고
|
|
9847
9847
|
* 옵션명과 직접 입력 항목이 모두 비면 400이다. 수정(PUT)은 통째로 바꾼다.
|
|
9848
9848
|
*/
|
|
9849
|
-
const savedOptionSetRequestSchema = z.
|
|
9849
|
+
const savedOptionSetRequestSchema = z.strictObject({
|
|
9850
9850
|
name: z.string().trim().min(1).max(50),
|
|
9851
9851
|
optionSort: optionSortSchema.default("REGISTERED"),
|
|
9852
9852
|
groups: z.array(savedOptionGroupRequestSchema).max(8).default([]),
|
|
@@ -9858,7 +9858,7 @@ const savedOptionSetRequestSchema = z.object({
|
|
|
9858
9858
|
* 조합 추가금이 값별 추가금의 합으로 되돌려지면 값별로 저장하고, 조합마다 제각각이면 추가금을 빼고
|
|
9859
9859
|
* 응답 `additionalPriceDropped: true`로 알린다. `replaceOptionSetId`를 주면 그 저장 옵션을 덮어쓴다(이름 중복의 덮어쓰기).
|
|
9860
9860
|
*/
|
|
9861
|
-
const savedOptionSetFromProductRequestSchema = z.
|
|
9861
|
+
const savedOptionSetFromProductRequestSchema = z.strictObject({
|
|
9862
9862
|
productId: z.string(),
|
|
9863
9863
|
name: z.string().trim().min(1).max(50),
|
|
9864
9864
|
includeOptions: z.boolean().default(true).describe("옵션명(조합·추가 선택)을 저장하는가"),
|
|
@@ -9976,14 +9976,14 @@ const brandBriefSchema = z.object({
|
|
|
9976
9976
|
notes: z.string().trim().max(4e3).nullable().describe("인터뷰 메모 (타깃 고객, 판매 방식, 셀러가 강조한 요구)")
|
|
9977
9977
|
});
|
|
9978
9978
|
/** `PUT /v1/storefront/brand-brief` — 브리프 전체를 바꾼다. 생략한 항목은 비운다 */
|
|
9979
|
-
const putBrandBriefRequestSchema = z.object({
|
|
9979
|
+
const putBrandBriefRequestSchema = strictRequest(z.object({
|
|
9980
9980
|
industry: storeIndustrySchema.nullable().default(null),
|
|
9981
9981
|
mood: z.string().trim().max(200).nullable().default(null),
|
|
9982
9982
|
pointColor: z.string().regex(/^#[0-9a-fA-F]{6}$/, "#RRGGBB 형식이어야 합니다").nullable().default(null),
|
|
9983
9983
|
references: z.array(brandBriefReferenceSchema).max(5).default([]),
|
|
9984
9984
|
productSource: brandBriefProductSourceSchema.nullable().default(null),
|
|
9985
9985
|
notes: z.string().trim().max(4e3).nullable().default(null)
|
|
9986
|
-
});
|
|
9986
|
+
}));
|
|
9987
9987
|
/** `GET·PUT /v1/storefront/brand-brief` 응답 — 저장한 적이 없으면 `brief`·`updatedAt`이 null이다 */
|
|
9988
9988
|
const brandBriefViewSchema = z.object({
|
|
9989
9989
|
brief: brandBriefSchema.nullable(),
|
|
@@ -11185,7 +11185,7 @@ async function refreshUserToken(options) {
|
|
|
11185
11185
|
...options.resource ? { resource: options.resource } : {}
|
|
11186
11186
|
}, options.fetch);
|
|
11187
11187
|
}
|
|
11188
|
-
const customerDelegationRequestSchema = z.object({
|
|
11188
|
+
const customerDelegationRequestSchema = strictRequest(z.object({
|
|
11189
11189
|
uid: z.string().regex(/^[A-Za-z0-9._:@|-]{1,128}$/).optional().describe("셀러 자체 인증의 사용자 id(1~128자, 영문·숫자·`._:@|-`). 처음 보는 id면 구매자를 만든다"),
|
|
11190
11190
|
email: z.email().max(254).optional().describe("uid가 없으면 이메일로 지목한다. uid와 함께 주면 처음 만들 때의 이메일이다"),
|
|
11191
11191
|
name: z.string().max(50).optional().describe("처음 만들 때의 이름"),
|
|
@@ -11193,7 +11193,7 @@ const customerDelegationRequestSchema = z.object({
|
|
|
11193
11193
|
}).refine((value) => Boolean(value.uid || value.email), {
|
|
11194
11194
|
message: "uid 또는 email이 필요합니다",
|
|
11195
11195
|
path: ["uid"]
|
|
11196
|
-
}).describe("구매자 위임 요청");
|
|
11196
|
+
}).describe("구매자 위임 요청"));
|
|
11197
11197
|
const customerDelegationSchema = z.object({
|
|
11198
11198
|
accessToken: z.string().describe("구매자 액세스 토큰(스토어프론트 API Bearer)"),
|
|
11199
11199
|
tokenType: z.literal("Bearer"),
|