@sayren/store-sdk 0.30.8 → 0.30.9
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 +17670 -16404
- package/dist/index.mjs +1302 -460
- package/package.json +1 -1
- package/src/client.ts +57 -0
- package/src/index.ts +4 -0
- package/src/schemas/app-manifest.ts +407 -0
- package/src/schemas/apps.ts +310 -0
- package/src/schemas/misc.ts +15 -2
- package/src/schemas/payment.ts +4 -1
- package/src/schemas/scope-descriptions.ts +34 -0
- package/src/schemas/starter.ts +173 -0
- package/src/schemas/storefront-hosting.ts +78 -2
package/dist/index.mjs
CHANGED
|
@@ -618,121 +618,174 @@ const apiLogSummarySchema = z.object({
|
|
|
618
618
|
serverErrors: z.number().int().min(0).describe("구간 내 연동 요청 중 5xx 응답 수")
|
|
619
619
|
});
|
|
620
620
|
//#endregion
|
|
621
|
-
//#region src/schemas/
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
]
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
621
|
+
//#region src/schemas/scope.ts
|
|
622
|
+
/**
|
|
623
|
+
* 상점 스코프 카탈로그 (auth-spec §7) — 형식 `{리소스}:{r|rw}`, rw는 r을 함의한다.
|
|
624
|
+
*
|
|
625
|
+
* 관리 API가 요구하는 스코프와 역할별 자동 부여 스코프의 **단일 원천**이다.
|
|
626
|
+
* 서버(apps/api)가 이 상수를 그대로 가져다 역할→스코프를 산출하므로, 상점 플랫폼 권한 매트릭스와
|
|
627
|
+
* 서버 인가가 갈라지지 않는다.
|
|
628
|
+
*/
|
|
629
|
+
const scopeAccessSchema = z.enum(["r", "rw"]).describe("접근 수준. `r`=읽기, `rw`=읽기+쓰기(r을 함의)");
|
|
630
|
+
/** 리소스별로 부여 가능한 접근 수준. settlement·analytics·audit는 쓰기 API가 없어 `r`만 존재한다 */
|
|
631
|
+
const STORE_SCOPE_RESOURCES = {
|
|
632
|
+
product: ["r", "rw"],
|
|
633
|
+
category: ["r", "rw"],
|
|
634
|
+
order: ["r", "rw"],
|
|
635
|
+
claim: ["r", "rw"],
|
|
636
|
+
inquiry: ["r", "rw"],
|
|
637
|
+
review: ["r", "rw"],
|
|
638
|
+
settlement: ["r"],
|
|
639
|
+
analytics: ["r"],
|
|
640
|
+
webhook: ["r", "rw"],
|
|
641
|
+
store: ["r", "rw"],
|
|
642
|
+
member: ["r", "rw"],
|
|
643
|
+
customer: ["r", "rw"],
|
|
644
|
+
audit: ["r"],
|
|
645
|
+
promotion: ["r", "rw"],
|
|
646
|
+
storefront: ["r", "rw"],
|
|
647
|
+
flow: ["r", "rw"]
|
|
648
|
+
};
|
|
649
|
+
const storeScopeResourceSchema = z.enum(Object.keys(STORE_SCOPE_RESOURCES)).describe("스코프 리소스");
|
|
650
|
+
/** 카탈로그상 존재하는 스코프 문자열인가 (예: `settlement:rw`는 false) */
|
|
651
|
+
function isStoreScope(scope) {
|
|
652
|
+
const [resource, access, ...rest] = scope.split(":");
|
|
653
|
+
if (rest.length > 0 || !resource || !access) return false;
|
|
654
|
+
return STORE_SCOPE_RESOURCES[resource]?.includes(access) ?? false;
|
|
655
|
+
}
|
|
656
|
+
/**
|
|
657
|
+
* 역할별 자동 부여 스코프 (auth-spec §7).
|
|
658
|
+
* 계정 토큰으로 상점을 호출할 때의 스코프는 그 상점에서의 역할로 이 표에서 결정된다.
|
|
659
|
+
*/
|
|
660
|
+
const STORE_ROLE_SCOPES = {
|
|
661
|
+
OWNER: [
|
|
662
|
+
"product:rw",
|
|
663
|
+
"category:rw",
|
|
664
|
+
"order:rw",
|
|
665
|
+
"claim:rw",
|
|
666
|
+
"inquiry:rw",
|
|
667
|
+
"review:rw",
|
|
668
|
+
"settlement:r",
|
|
669
|
+
"analytics:r",
|
|
670
|
+
"webhook:rw",
|
|
671
|
+
"store:rw",
|
|
672
|
+
"member:rw",
|
|
673
|
+
"customer:rw",
|
|
674
|
+
"audit:r",
|
|
675
|
+
"promotion:rw",
|
|
676
|
+
"storefront:rw",
|
|
677
|
+
"flow:rw"
|
|
678
|
+
],
|
|
679
|
+
ADMIN: [
|
|
680
|
+
"product:rw",
|
|
681
|
+
"category:rw",
|
|
682
|
+
"order:rw",
|
|
683
|
+
"claim:rw",
|
|
684
|
+
"inquiry:rw",
|
|
685
|
+
"review:rw",
|
|
686
|
+
"analytics:r",
|
|
687
|
+
"webhook:rw",
|
|
688
|
+
"store:rw",
|
|
689
|
+
"member:rw",
|
|
690
|
+
"customer:rw",
|
|
691
|
+
"audit:r",
|
|
692
|
+
"promotion:rw",
|
|
693
|
+
"storefront:rw",
|
|
694
|
+
"flow:rw"
|
|
695
|
+
],
|
|
696
|
+
STAFF: [
|
|
697
|
+
"product:rw",
|
|
698
|
+
"category:rw",
|
|
699
|
+
"order:rw",
|
|
700
|
+
"claim:rw",
|
|
701
|
+
"inquiry:rw",
|
|
702
|
+
"review:rw",
|
|
703
|
+
"customer:r",
|
|
704
|
+
"promotion:r",
|
|
705
|
+
"flow:r"
|
|
706
|
+
]
|
|
707
|
+
};
|
|
645
708
|
//#endregion
|
|
646
|
-
//#region src/schemas/
|
|
647
|
-
const categoryProductSortSchema = z.enum([
|
|
648
|
-
"RECENT",
|
|
649
|
-
"POPULAR",
|
|
650
|
-
"PRICE_LOW",
|
|
651
|
-
"PRICE_HIGH",
|
|
652
|
-
"NAME"
|
|
653
|
-
]);
|
|
654
|
-
const categorySchema = z.object({
|
|
655
|
-
categoryId: z.string(),
|
|
656
|
-
storeId: z.string(),
|
|
657
|
-
name: z.string(),
|
|
658
|
-
parentId: z.string().nullable(),
|
|
659
|
-
path: z.string(),
|
|
660
|
-
depth: z.number().int().min(1).max(4),
|
|
661
|
-
leaf: z.boolean(),
|
|
662
|
-
sortOrder: z.number().int(),
|
|
663
|
-
visible: z.boolean(),
|
|
664
|
-
showPc: z.boolean(),
|
|
665
|
-
showMobile: z.boolean(),
|
|
666
|
-
productSort: categoryProductSortSchema,
|
|
667
|
-
metaTitle: z.string().nullable(),
|
|
668
|
-
metaDescription: z.string().nullable(),
|
|
669
|
-
metaKeywords: z.string().nullable(),
|
|
670
|
-
flowId: z.string().nullable().optional().describe("주문 흐름(이슈 #115) — 맨 아래 카테고리에만 있다. 흐름을 지정하지 않은 상품이 쓴다. null이면 기본 흐름"),
|
|
671
|
-
createdAt: z.string(),
|
|
672
|
-
updatedAt: z.string()
|
|
673
|
-
});
|
|
674
|
-
const categoryTreeNodeSchema = categorySchema.extend({ children: z.lazy(() => z.array(categoryTreeNodeSchema)) });
|
|
675
|
-
const categoryWithPathSchema = categorySchema.extend({
|
|
676
|
-
fullPath: z.string(),
|
|
677
|
-
ancestors: z.array(categorySchema),
|
|
678
|
-
childCount: z.number().int()
|
|
679
|
-
});
|
|
680
|
-
const categorySearchResultSchema = categorySchema.extend({
|
|
681
|
-
fullPath: z.string(),
|
|
682
|
-
ancestors: z.array(categorySchema)
|
|
683
|
-
});
|
|
684
|
-
const categoryAttributeSchema = z.object({
|
|
685
|
-
attributeId: z.string(),
|
|
686
|
-
name: z.string(),
|
|
687
|
-
required: z.boolean(),
|
|
688
|
-
inputType: z.enum([
|
|
689
|
-
"SELECT",
|
|
690
|
-
"MULTI_SELECT",
|
|
691
|
-
"TEXT",
|
|
692
|
-
"NUMBER"
|
|
693
|
-
]),
|
|
694
|
-
unit: z.string().nullable(),
|
|
695
|
-
allowedValues: z.array(z.string()).optional()
|
|
696
|
-
});
|
|
697
|
-
const categoryCreateRequestSchema = z.object({
|
|
698
|
-
name: z.string().min(1).max(100),
|
|
699
|
-
parentId: z.string().nullable().optional(),
|
|
700
|
-
sortOrder: z.number().int().optional(),
|
|
701
|
-
visible: z.boolean().optional(),
|
|
702
|
-
showPc: z.boolean().optional(),
|
|
703
|
-
showMobile: z.boolean().optional(),
|
|
704
|
-
productSort: categoryProductSortSchema.optional(),
|
|
705
|
-
metaTitle: z.string().max(100).nullable().optional(),
|
|
706
|
-
metaDescription: z.string().max(300).nullable().optional(),
|
|
707
|
-
metaKeywords: z.string().max(200).nullable().optional()
|
|
708
|
-
});
|
|
709
|
-
const categoryPatchRequestSchema = z.object({
|
|
710
|
-
name: z.string().min(1).max(100).optional(),
|
|
711
|
-
parentId: z.string().nullable().optional(),
|
|
712
|
-
sortOrder: z.number().int().optional(),
|
|
713
|
-
visible: z.boolean().optional(),
|
|
714
|
-
showPc: z.boolean().optional(),
|
|
715
|
-
showMobile: z.boolean().optional(),
|
|
716
|
-
productSort: categoryProductSortSchema.optional(),
|
|
717
|
-
metaTitle: z.string().max(100).nullable().optional(),
|
|
718
|
-
metaDescription: z.string().max(300).nullable().optional(),
|
|
719
|
-
metaKeywords: z.string().max(200).nullable().optional(),
|
|
720
|
-
flowId: z.string().nullable().optional().describe("주문 흐름(이슈 #115) — 게시한 흐름 id, null이면 기본 흐름. 맨 아래 카테고리에만 지정한다(400 `CATEGORY_NOT_LEAF`). 게시하지 않은 흐름은 409 `FLOW_NOT_PUBLISHED`")
|
|
721
|
-
});
|
|
722
|
-
const categoryDeleteStrategySchema = z.enum(["reparent", "cascade"]);
|
|
709
|
+
//#region src/schemas/scope-descriptions.ts
|
|
723
710
|
/**
|
|
724
|
-
*
|
|
725
|
-
*
|
|
726
|
-
* 둘 다 아니면 목록(`Category[]`)이다.
|
|
711
|
+
* 스코프를 사람이 읽는 문장 (이슈 #22) — 앱 설치 동의 화면(accounts), 상점 플랫폼 설치 앱 권한 표시, 유저 문서 스코프 레퍼런스가
|
|
712
|
+
* 같은 문장을 쓴다. 읽기(`r`)와 쓰기(`rw`)를 따로 둔다 — 동의 화면은 쓰기를 강조한다.
|
|
727
713
|
*/
|
|
728
|
-
const
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
714
|
+
const SCOPE_DESCRIPTIONS = {
|
|
715
|
+
product: {
|
|
716
|
+
r: "상품 정보를 읽습니다",
|
|
717
|
+
rw: "상품을 만들고 수정합니다"
|
|
718
|
+
},
|
|
719
|
+
category: {
|
|
720
|
+
r: "카테고리를 읽습니다",
|
|
721
|
+
rw: "카테고리를 만들고 수정합니다"
|
|
722
|
+
},
|
|
723
|
+
order: {
|
|
724
|
+
r: "주문을 읽습니다",
|
|
725
|
+
rw: "주문 상태를 바꿉니다(발송·배송완료·취소 등)"
|
|
726
|
+
},
|
|
727
|
+
claim: {
|
|
728
|
+
r: "취소·반품·교환 요청을 읽습니다",
|
|
729
|
+
rw: "취소·반품·교환을 처리합니다"
|
|
730
|
+
},
|
|
731
|
+
inquiry: {
|
|
732
|
+
r: "상품 문의를 읽습니다",
|
|
733
|
+
rw: "상품 문의에 답합니다"
|
|
734
|
+
},
|
|
735
|
+
review: {
|
|
736
|
+
r: "리뷰를 읽습니다",
|
|
737
|
+
rw: "리뷰를 관리합니다"
|
|
738
|
+
},
|
|
739
|
+
settlement: {
|
|
740
|
+
r: "매출 리포트를 읽습니다",
|
|
741
|
+
rw: "매출 리포트를 읽습니다"
|
|
742
|
+
},
|
|
743
|
+
analytics: {
|
|
744
|
+
r: "방문·구매 분석 데이터를 읽습니다",
|
|
745
|
+
rw: "방문·구매 분석 데이터를 읽습니다"
|
|
746
|
+
},
|
|
747
|
+
webhook: {
|
|
748
|
+
r: "웹훅 설정을 읽습니다",
|
|
749
|
+
rw: "웹훅을 만들고 수정합니다"
|
|
750
|
+
},
|
|
751
|
+
store: {
|
|
752
|
+
r: "상점 정보를 읽습니다",
|
|
753
|
+
rw: "상점 설정을 수정합니다"
|
|
754
|
+
},
|
|
755
|
+
member: {
|
|
756
|
+
r: "구성원 목록을 읽습니다",
|
|
757
|
+
rw: "구성원을 초대하고 역할을 바꿉니다"
|
|
758
|
+
},
|
|
759
|
+
customer: {
|
|
760
|
+
r: "고객 정보(이름·연락처·주소)를 읽습니다",
|
|
761
|
+
rw: "고객 정보를 수정합니다"
|
|
762
|
+
},
|
|
763
|
+
audit: {
|
|
764
|
+
r: "감사 로그를 읽습니다",
|
|
765
|
+
rw: "감사 로그를 읽습니다"
|
|
766
|
+
},
|
|
767
|
+
promotion: {
|
|
768
|
+
r: "쿠폰·적립금 설정을 읽습니다",
|
|
769
|
+
rw: "쿠폰을 발급하고 적립금을 지급합니다"
|
|
770
|
+
},
|
|
771
|
+
storefront: {
|
|
772
|
+
r: "스토어프론트 설정을 읽습니다",
|
|
773
|
+
rw: "스토어프론트를 편집하고 발행합니다"
|
|
774
|
+
},
|
|
775
|
+
flow: {
|
|
776
|
+
r: "주문 흐름 설정을 읽습니다",
|
|
777
|
+
rw: "주문 흐름을 만들고 수정합니다"
|
|
778
|
+
}
|
|
779
|
+
};
|
|
780
|
+
/** `{resource}:{r|rw}` → 문장. 카탈로그에 없는 스코프는 null */
|
|
781
|
+
function describeScope(scope) {
|
|
782
|
+
const [resource, access] = scope.split(":");
|
|
783
|
+
if (!resource || !(resource in STORE_SCOPE_RESOURCES)) return null;
|
|
784
|
+
const allowed = STORE_SCOPE_RESOURCES[resource];
|
|
785
|
+
if (access !== "r" && access !== "rw") return null;
|
|
786
|
+
if (!allowed.includes(access)) return null;
|
|
787
|
+
return SCOPE_DESCRIPTIONS[resource][access];
|
|
788
|
+
}
|
|
736
789
|
//#endregion
|
|
737
790
|
//#region src/schemas/commerce-number.ts
|
|
738
791
|
/**
|
|
@@ -2209,23 +2262,926 @@ const discountRequestSchema = z.object({
|
|
|
2209
2262
|
endAt: z.string().optional()
|
|
2210
2263
|
});
|
|
2211
2264
|
//#endregion
|
|
2212
|
-
//#region src/schemas/
|
|
2265
|
+
//#region src/schemas/webhook-event.ts
|
|
2266
|
+
const envelopeFields = {
|
|
2267
|
+
id: z.string().describe("이벤트 ID (`evt_…`, 테스트 전송은 `evt_test_…`). 중복 제거 키"),
|
|
2268
|
+
createdAt: z.string().describe("이벤트 발생 일시 (ISO 8601)"),
|
|
2269
|
+
storeId: z.string().describe("이벤트가 발생한 상점 ID"),
|
|
2270
|
+
sequence: z.number().int().positive().optional().describe("리소스별 순번. 같은 주문·클레임·문의·리뷰·상품의 이벤트마다 1씩 커진다. 도착 순서는 보장하지 않으므로 같은 리소스의 이벤트는 이 값으로 정렬한다. 이 필드가 생기기 전의 이벤트와 테스트 전송에는 없다")
|
|
2271
|
+
};
|
|
2272
|
+
const orderWebhookEventDataSchema = z.object({
|
|
2273
|
+
orderId: z.string().describe("주문 ID"),
|
|
2274
|
+
orderNo: z.string().nullable().optional().default(null).describe("주문번호(이슈 #107) — 숫자 14자리. 이 필드가 없는 이전 이벤트와 번호 체계 이전 주문은 null로 읽는다"),
|
|
2275
|
+
status: orderItemStatusSchema.describe("이벤트 발생 시점의 주문 대표 상태"),
|
|
2276
|
+
testPayment: z.boolean().optional().default(false).describe("테스트 결제(샌드박스 결제) 주문인가. true면 실제로 돈이 오가지 않았다. 이 필드가 없는 이전 이벤트는 false로 읽는다"),
|
|
2277
|
+
couponDiscountAmount: z.number().int().optional().default(0).describe("주문이 받은 상품·주문 쿠폰 할인 합계 (원). 즉시할인(`discountAmount`)과 별개다. 쿠폰이 없거나 이 필드가 없는 이전 이벤트는 0으로 읽는다"),
|
|
2278
|
+
pointAmount: z.number().int().optional().default(0).describe("주문에 쓴 적립금 (원, #93). 적립금을 쓰지 않았거나 이 필드가 없는 이전 이벤트는 0으로 읽는다"),
|
|
2279
|
+
orderItemId: z.string().optional().describe("이벤트를 만든 주문상품 ID. 배송 없는 상품 제공으로 보내는 `ORDER.DELIVERED`에만 실린다. 택배 배송완료와 그 밖의 주문 이벤트에는 없다"),
|
|
2280
|
+
fulfillmentSnapshot: orderItemFulfillmentSnapshotResponseSchema.optional().describe("이벤트를 만든 주문상품의 주문 시점 이행 스냅샷(주문상품 응답의 `fulfillmentSnapshot`과 같다, 이슈 #57). 배송 없는 상품의 제공 완료는 `ORDER.DELIVERED`로도 오고 `type`이 `MANUAL`이다. `orderItemId`가 있는 이벤트에만 실린다"),
|
|
2281
|
+
itemOptions: z.array(z.object({
|
|
2282
|
+
orderItemId: z.string(),
|
|
2283
|
+
optionSelections: z.array(optionSelectionSchema),
|
|
2284
|
+
customInputs: z.array(customInputValueSchema)
|
|
2285
|
+
})).optional().describe("추가 선택·직접 입력이 있는 주문상품의 주문 시점 선택값(이슈 #59, 주문상품 응답의 `optionSelections`·`customInputs`와 같다). `ORDER.PAID`에만 실리고 그런 주문상품이 없으면 없다")
|
|
2286
|
+
});
|
|
2213
2287
|
/**
|
|
2214
|
-
*
|
|
2215
|
-
*
|
|
2216
|
-
*
|
|
2217
|
-
* 유형은 만들 때 정하고 바꿀 수 없다. 직접 선택(`MANUAL`)은 셀러가 상품을 담고 순서를 정하고, 조건 자동(`AUTOMATIC`)은 조건(`rules`)을
|
|
2218
|
-
* 모두 만족하는 판매 중 상품을 조회할 때마다 계산한다(결과를 저장하지 않는다). 응답의 `type`은 값이 늘 수 있어 문자열이다.
|
|
2288
|
+
* `ORDER.FULFILLED` — 배송 없는 주문상품(`MANUAL`)을 제공했다(이슈 #48). 셀러의 제공 처리
|
|
2289
|
+
* (`POST /v1/order-items/{orderItemId}/fulfill`)로 온다. 기존 구독자를 위해 같은 순간 `ORDER.DELIVERED`도 함께 보낸다.
|
|
2219
2290
|
*/
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2291
|
+
const orderFulfilledWebhookEventDataSchema = z.object({
|
|
2292
|
+
orderId: z.string().describe("주문 ID"),
|
|
2293
|
+
orderNo: z.string().nullable().optional().default(null).describe("주문번호(이슈 #107) — 숫자 14자리. 이 필드가 없는 이전 이벤트와 번호 체계 이전 주문은 null로 읽는다"),
|
|
2294
|
+
orderItemId: z.string().describe("제공한 주문상품 ID"),
|
|
2295
|
+
status: orderItemStatusSchema.describe("이벤트 발생 시점의 주문 대표 상태"),
|
|
2296
|
+
fulfillmentSnapshot: orderItemFulfillmentSnapshotResponseSchema.describe("제공한 주문상품의 주문 시점 이행 스냅샷(주문상품 응답의 `fulfillmentSnapshot`과 같다, 이슈 #57)"),
|
|
2297
|
+
fulfillment: z.object({ mode: z.string() }).optional().describe("**Deprecated** — 항상 `{ mode: \"MANUAL\" }`이다. 자동 제공을 없앴고(이슈 #48) store-sdk 0.19 이하 호환으로만 싣는다. 게시된 연동이 store-sdk 0.20 이상으로 넘어간 뒤 다음 릴리스에서 뺀다"),
|
|
2298
|
+
fulfilledAt: z.string().describe("제공 시각 (ISO 8601). 주문상품의 `deliveredAt`과 같다"),
|
|
2299
|
+
testPayment: z.boolean().describe("테스트 결제(샌드박스 결제) 주문인가. true면 실제로 돈이 오가지 않았다")
|
|
2300
|
+
});
|
|
2301
|
+
/**
|
|
2302
|
+
* `ORDER.CANCELED` — 셀러 직권취소(`POST /v1/order-items/{orderItemId}/cancel`)로 주문상품이 취소됐다.
|
|
2303
|
+
* 수량 단위 부분 취소도 이 이벤트다(`quantity` < 주문 수량이면 주문상품 상태가 그대로다).
|
|
2304
|
+
* 구매자가 신청한 취소·반품은 `CLAIM.*` 이벤트로 온다.
|
|
2305
|
+
*/
|
|
2306
|
+
const orderCanceledWebhookEventDataSchema = z.object({
|
|
2307
|
+
orderId: z.string().describe("주문 ID"),
|
|
2308
|
+
orderNo: z.string().nullable().optional().default(null).describe("주문번호(이슈 #107) — 숫자 14자리. 이 필드가 없는 이전 이벤트와 번호 체계 이전 주문은 null로 읽는다"),
|
|
2309
|
+
orderItemId: z.string().describe("취소한 주문 상품 ID"),
|
|
2310
|
+
status: orderItemStatusSchema.describe("취소 뒤 주문 대표 상태. 모든 주문 상품이 취소됐으면 `CANCELED`다"),
|
|
2311
|
+
itemStatus: orderItemStatusSchema.describe("취소 뒤 주문 상품 상태. 전 수량이 취소됐으면 `CANCELED`, 일부만 취소했으면 원래 상태다"),
|
|
2312
|
+
reason: z.string().describe("취소 사유 코드 — 판매자 취소 요청의 `reason`(`OUT_OF_STOCK`·`PRICE_ERROR`·`DELIVERY_IMPOSSIBLE`·`ETC`). 사유가 늘어날 수 있어 문자열이다. 모르는 값은 `ETC`처럼 다룬다"),
|
|
2313
|
+
quantity: z.number().int().describe("이번에 취소한 수량"),
|
|
2314
|
+
canceledQuantity: z.number().int().describe("취소 후 누적 취소 수량"),
|
|
2315
|
+
refundAmount: z.number().int().describe("환불액 (원). 수량 비율 상품 실결제 금액(쿠폰 배분을 뺀 금액) + 배송비 환불 − 조건부 무료배송 미달 차감 + 되돌린 쿠폰 차감(`couponChargeRefundAmount`)이다. 결제사 환불 결과는 결제 내역(`GET /v1/payments/{paymentId}`)으로 확인한다"),
|
|
2316
|
+
deliveryFeeRefundAmount: z.number().int().describe("환불액에 포함된 배송비 (원)"),
|
|
2317
|
+
deliveryFeeChargeAmount: z.number().int().describe("환불액에서 뺀 배송비 (원) — 조건부 무료배송 기준 미달 차감"),
|
|
2318
|
+
couponChargeAmount: z.number().int().optional().default(0).describe("환불액에서 뺀 쿠폰 조건 미달 차감 (원). 판매자 취소는 차감하지 않으므로 늘 0이다. 이 필드가 없는 이전 이벤트는 0으로 읽는다"),
|
|
2319
|
+
couponChargeRefundAmount: z.number().int().optional().default(0).describe("환불액에 더한 금액 (원) — 앞선 환불에서 뺀 쿠폰 조건 미달 차감을 되돌린 몫이다. 전량 취소면 누적 차감이 모두 되돌아간다. 이 필드가 없는 이전 이벤트는 0으로 읽는다"),
|
|
2320
|
+
pointRefundAmount: z.number().int().optional().default(0).describe("돌려준 적립금 (원, #93) — 결제에 쓴 적립금 중 이번 취소·반품 수량의 몫이다. 현금 환불(`refundAmount`)과 별개다. 차감은 현금에서 먼저 빼고 모자란 몫만 여기서 뺀다. 적립금을 쓰지 않은 주문과 이 필드가 없는 이전 응답·이벤트는 0으로 읽는다"),
|
|
2321
|
+
testPayment: z.boolean().describe("테스트 결제(샌드박스 결제) 주문인가. true면 실제로 돈이 오가지 않았다")
|
|
2322
|
+
});
|
|
2323
|
+
const claimWebhookEventDataSchema = z.object({
|
|
2324
|
+
claimId: z.string().describe("클레임 ID"),
|
|
2325
|
+
claimNo: z.string().nullable().optional().default(null).describe("클레임번호(이슈 #107) — `C` + YYMMDD + 숫자 8자리. 이 필드가 없는 이전 이벤트와 번호 체계 이전 클레임은 null로 읽는다"),
|
|
2326
|
+
orderId: z.string().describe("주문 ID"),
|
|
2327
|
+
orderItemId: z.string().describe("클레임 대상 주문 상품 ID"),
|
|
2328
|
+
claimType: claimTypeSchema.describe("클레임 유형"),
|
|
2329
|
+
status: claimStatusSchema.describe("이벤트 발생 시점의 클레임 상태"),
|
|
2330
|
+
quantity: z.number().int().optional().default(0).describe("클레임 대상 수량. 이 필드가 없는 이전 이벤트는 0으로 읽는다"),
|
|
2331
|
+
refundAmount: z.number().int().nullable().optional().default(null).describe("환불액 (원). 배송비 환불을 포함하고 배송비 차감·추가 차감을 뺀 실제 금액이다. 환불이 확정되지 않은 접수 이벤트는 예상 환불액이고, 교환처럼 환불이 없으면 null이다"),
|
|
2332
|
+
deliveryFeeRefundAmount: z.number().int().optional().default(0).describe("환불액에 포함된 배송비 (원). 이 필드가 없는 이전 이벤트는 0으로 읽는다"),
|
|
2333
|
+
deliveryFeeChargeAmount: z.number().int().optional().default(0).describe("환불액에서 뺀 배송비 (원) — 반품·교환 배송비와 조건부 무료배송 미달 차감의 합. 셀러가 승인에 넣은 추가 차감은 `deductAmount`로 따로 온다. 이 필드가 없는 이전 이벤트는 0으로 읽는다"),
|
|
2334
|
+
deductAmount: z.number().int().optional().default(0).describe("환불액에서 뺀 추가 차감 (원) — 셀러가 클레임 승인에 넣은 금액이다(상품 훼손 등). 배송비 차감과 별개이므로 `deliveryFeeChargeAmount`와 더해서 뺀다. 접수 이벤트와 이 필드가 없는 이전 이벤트는 0이다"),
|
|
2335
|
+
couponChargeAmount: z.number().int().optional().default(0).describe("환불액에서 뺀 쿠폰 조건 미달 차감 (원) — 일부 취소·반품으로 남은 주문상품이 쿠폰 조건(최소 주문 금액 등)을 잃어 남은 주문상품이 받던 할인을 뺀 금액이다. 배송비 차감·추가 차감과 별개다. 접수 이벤트는 예상값이다. 이 필드가 없는 이전 이벤트는 0으로 읽는다"),
|
|
2336
|
+
couponChargeRefundAmount: z.number().int().optional().default(0).describe("환불액에 더한 금액 (원) — 앞선 환불에서 뺀 쿠폰 조건 미달 차감을 되돌린 몫이다. 접수 이벤트는 예상값이다. 이 필드가 없는 이전 이벤트는 0으로 읽는다"),
|
|
2337
|
+
pointRefundAmount: z.number().int().optional().default(0).describe("돌려준 적립금 (원, #93) — 결제에 쓴 적립금 중 이번 취소·반품 수량의 몫이다. 현금 환불(`refundAmount`)과 별개다. 차감은 현금에서 먼저 빼고 모자란 몫만 여기서 뺀다. 적립금을 쓰지 않은 주문과 이 필드가 없는 이전 응답·이벤트는 0으로 읽는다"),
|
|
2338
|
+
testPayment: z.boolean().optional().default(false).describe("테스트 결제(샌드박스 결제) 주문의 클레임인가. 이 필드가 없는 이전 이벤트는 false로 읽는다")
|
|
2339
|
+
});
|
|
2340
|
+
const inquiryWebhookEventDataSchema = z.object({
|
|
2341
|
+
inquiryId: z.string().describe("상품 문의 ID"),
|
|
2342
|
+
productId: z.string().describe("상품 ID")
|
|
2343
|
+
});
|
|
2344
|
+
const reviewWebhookEventDataSchema = z.object({
|
|
2345
|
+
reviewId: z.string().describe("리뷰 ID"),
|
|
2346
|
+
productId: z.string().describe("상품 ID"),
|
|
2347
|
+
rating: z.number().int().min(1).max(5).describe("별점 (1~5)")
|
|
2348
|
+
});
|
|
2349
|
+
const productOutOfStockWebhookEventDataSchema = z.object({
|
|
2350
|
+
productId: z.string().describe("상품 ID"),
|
|
2351
|
+
variantId: z.string().describe("재고가 0이 된 판매 단위(variant) ID. 주문 상품의 `optionId`와 같은 값 공간이다")
|
|
2352
|
+
});
|
|
2353
|
+
const orderWebhookEventSchema = z.object({
|
|
2354
|
+
...envelopeFields,
|
|
2355
|
+
type: z.enum([
|
|
2356
|
+
"ORDER.PAID",
|
|
2357
|
+
"ORDER.CONFIRMED",
|
|
2358
|
+
"ORDER.DISPATCHED",
|
|
2359
|
+
"ORDER.DELIVERED",
|
|
2360
|
+
"ORDER.PURCHASE_DECIDED",
|
|
2361
|
+
"ORDER.ADDRESS_CHANGE_REQUESTED"
|
|
2362
|
+
]),
|
|
2363
|
+
data: orderWebhookEventDataSchema
|
|
2364
|
+
});
|
|
2365
|
+
const orderFulfilledWebhookEventSchema = z.object({
|
|
2366
|
+
...envelopeFields,
|
|
2367
|
+
type: z.literal("ORDER.FULFILLED"),
|
|
2368
|
+
data: orderFulfilledWebhookEventDataSchema
|
|
2369
|
+
});
|
|
2370
|
+
const orderCanceledWebhookEventSchema = z.object({
|
|
2371
|
+
...envelopeFields,
|
|
2372
|
+
type: z.literal("ORDER.CANCELED"),
|
|
2373
|
+
data: orderCanceledWebhookEventDataSchema
|
|
2374
|
+
});
|
|
2375
|
+
const claimWebhookEventSchema = z.object({
|
|
2376
|
+
...envelopeFields,
|
|
2377
|
+
type: z.enum([
|
|
2378
|
+
"CLAIM.CANCEL_REQUESTED",
|
|
2379
|
+
"CLAIM.RETURN_REQUESTED",
|
|
2380
|
+
"CLAIM.EXCHANGE_REQUESTED",
|
|
2381
|
+
"CLAIM.COMPLETED"
|
|
2382
|
+
]),
|
|
2383
|
+
data: claimWebhookEventDataSchema
|
|
2384
|
+
});
|
|
2385
|
+
const inquiryWebhookEventSchema = z.object({
|
|
2386
|
+
...envelopeFields,
|
|
2387
|
+
type: z.literal("INQUIRY.CREATED"),
|
|
2388
|
+
data: inquiryWebhookEventDataSchema
|
|
2389
|
+
});
|
|
2390
|
+
const reviewWebhookEventSchema = z.object({
|
|
2391
|
+
...envelopeFields,
|
|
2392
|
+
type: z.literal("REVIEW.CREATED"),
|
|
2393
|
+
data: reviewWebhookEventDataSchema
|
|
2394
|
+
});
|
|
2395
|
+
const productOutOfStockWebhookEventSchema = z.object({
|
|
2396
|
+
...envelopeFields,
|
|
2397
|
+
type: z.literal("PRODUCT.OUT_OF_STOCK"),
|
|
2398
|
+
data: productOutOfStockWebhookEventDataSchema
|
|
2399
|
+
});
|
|
2400
|
+
/**
|
|
2401
|
+
* 플로우 알림(이슈 #115) — 상점이 플로우에 둔 알림 블록(`core.notify`, 채널 `webhook`)이 보낸다. 구매자 알림(알림톡·메일)은 이 이벤트를 받아
|
|
2402
|
+
* 상점이 보낸다(플랫폼은 구매자에게 직접 보내지 않는다). 개인정보는 싣지 않는다
|
|
2403
|
+
*/
|
|
2404
|
+
const flowNotificationWebhookEventDataSchema = z.object({
|
|
2405
|
+
orderId: z.string().describe("주문 ID"),
|
|
2406
|
+
orderItemIds: z.array(z.string()).describe("이 알림의 주문상품(묶음 진행이면 여럿)"),
|
|
2407
|
+
event: z.string().describe("상점이 블록에 둔 알림 이름(`custom.<이름>`)"),
|
|
2408
|
+
flowId: z.string(),
|
|
2409
|
+
flowVersion: z.number().int(),
|
|
2410
|
+
state: z.string().describe("알림을 보낸 전이의 도착 상태 키")
|
|
2411
|
+
});
|
|
2412
|
+
const flowNotificationWebhookEventSchema = z.object({
|
|
2413
|
+
...envelopeFields,
|
|
2414
|
+
type: z.literal("ORDER.FLOW_NOTIFICATION"),
|
|
2415
|
+
data: flowNotificationWebhookEventDataSchema
|
|
2416
|
+
});
|
|
2417
|
+
/**
|
|
2418
|
+
* 흐름 상태 변경(이슈 #115) — 주문상품의 흐름 상태(노드)가 바뀔 때마다 온다. 공개 상태(`ORDER.*`)가 같아도 상점 흐름의 단계가 바뀌면 온다
|
|
2419
|
+
* (시안 확인 → 제작중). 묶음 진행이면 `orderItemIds`가 여럿이다. 개인정보는 싣지 않는다
|
|
2420
|
+
*/
|
|
2421
|
+
const flowStateChangedWebhookEventDataSchema = z.object({
|
|
2422
|
+
orderId: z.string().describe("주문 ID"),
|
|
2423
|
+
orderItemIds: z.array(z.string()).describe("상태가 바뀐 주문상품(묶음 진행이면 여럿)"),
|
|
2424
|
+
flowId: z.string(),
|
|
2425
|
+
flowVersion: z.number().int(),
|
|
2426
|
+
from: z.string().nullable().describe("이전 상태 키. 흐름이 막 시작했으면 null"),
|
|
2427
|
+
to: z.string().describe("새 상태 키"),
|
|
2428
|
+
label: z.string().describe("새 상태의 셀러용 이름"),
|
|
2429
|
+
coreStatus: z.string().nullable().describe("새 상태의 공개 상태. 공개 상태를 바꾸지 않는 단계면 null"),
|
|
2430
|
+
changedAt: z.string().describe("바뀐 시각")
|
|
2431
|
+
});
|
|
2432
|
+
const flowStateChangedWebhookEventSchema = z.object({
|
|
2433
|
+
...envelopeFields,
|
|
2434
|
+
type: z.literal("ORDER.FLOW_STATE_CHANGED"),
|
|
2435
|
+
data: flowStateChangedWebhookEventDataSchema
|
|
2436
|
+
});
|
|
2437
|
+
/** 웹훅 요청 본문. `type`으로 판별하며 `data` 형태가 타입별로 다르다 */
|
|
2438
|
+
const webhookEventSchema = z.discriminatedUnion("type", [
|
|
2439
|
+
orderWebhookEventSchema,
|
|
2440
|
+
orderFulfilledWebhookEventSchema,
|
|
2441
|
+
orderCanceledWebhookEventSchema,
|
|
2442
|
+
claimWebhookEventSchema,
|
|
2443
|
+
inquiryWebhookEventSchema,
|
|
2444
|
+
reviewWebhookEventSchema,
|
|
2445
|
+
productOutOfStockWebhookEventSchema,
|
|
2446
|
+
flowNotificationWebhookEventSchema,
|
|
2447
|
+
flowStateChangedWebhookEventSchema
|
|
2448
|
+
]).describe("웹훅 요청 본문 (얇은 이벤트). 개인정보를 싣지 않으며 `data`의 id로 관리 API를 조회한다");
|
|
2449
|
+
/**
|
|
2450
|
+
* 알려진 웹훅 이벤트 타입 — `webhookEventSchema`의 판별 값 목록(이 SDK 버전이 아는 것).
|
|
2451
|
+
* 서버는 새 타입을 구독한 엔드포인트에만 보내지만, 구독을 더 넓힌 수신자를 위해 `parseWebhookEvent`가 모르는 타입을 따로 받는다.
|
|
2452
|
+
*/
|
|
2453
|
+
const KNOWN_WEBHOOK_EVENT_TYPES = webhookEventSchema.options.flatMap((option) => {
|
|
2454
|
+
const type = option.shape.type;
|
|
2455
|
+
return "options" in type ? [...type.options] : [type.value];
|
|
2456
|
+
});
|
|
2457
|
+
/** 이 SDK 버전이 모르는 이벤트 — 봉투만 검증하고 `data`는 그대로 둔다 */
|
|
2458
|
+
const unknownWebhookEventSchema = z.object({
|
|
2459
|
+
...envelopeFields,
|
|
2460
|
+
type: z.string().min(1),
|
|
2461
|
+
data: z.unknown()
|
|
2462
|
+
});
|
|
2463
|
+
/**
|
|
2464
|
+
* 웹훅 본문을 `type`으로 먼저 판별해 읽는다(이슈 #115 결정 25). 알려진 타입은 그 타입의 스키마로 엄격하게 읽고 틀리면 던진다 —
|
|
2465
|
+
* `z.union([known, unknown])`처럼 읽으면 알려진 타입의 파싱 실패가 `known: false`로 조용히 새어 수신자가 처리할 이벤트를 버린다.
|
|
2466
|
+
* 모르는 타입은 봉투만 검증해 `known: false`로 돌려준다(새 이벤트에 옛 SDK가 깨지지 않는다).
|
|
2467
|
+
*/
|
|
2468
|
+
function parseWebhookEvent(body) {
|
|
2469
|
+
const type = body?.type;
|
|
2470
|
+
if (typeof type === "string" && KNOWN_WEBHOOK_EVENT_TYPES.includes(type)) return {
|
|
2471
|
+
known: true,
|
|
2472
|
+
event: webhookEventSchema.parse(body)
|
|
2473
|
+
};
|
|
2474
|
+
return {
|
|
2475
|
+
known: false,
|
|
2476
|
+
event: unknownWebhookEventSchema.parse(body)
|
|
2477
|
+
};
|
|
2478
|
+
}
|
|
2479
|
+
//#endregion
|
|
2480
|
+
//#region src/schemas/app-manifest.ts
|
|
2481
|
+
/**
|
|
2482
|
+
* 서드파티 앱 매니페스트 `sayren.app.json` (이슈 #22) — CLI(`sayren app`)·api(빌드 `VERIFY`·버전 판정·동의 화면 자료)·런타임이
|
|
2483
|
+
* 같은 원천을 쓴다. 매니페스트는 정적 JSON이고 빌드 없이 읽는다.
|
|
2484
|
+
*
|
|
2485
|
+
* 기능 하나는 **capability** 하나다(`APP_CAPABILITIES`). capability는 매니페스트 조각·동의 문단·넓어짐 판정(재동의 조건)을 한 곳에 둔다.
|
|
2486
|
+
* 새 기능(예약 작업·공개 HTTP·설치 저장소·외부 런타임)은 여기에 항목 하나를 더하고, 설치·버전·정지·감사·사용량 코드는 건드리지 않는다.
|
|
2487
|
+
* 모르는 키는 거부한다(`strictObject`) — 아직 없는 capability를 쓴 매니페스트는 빌드가 분명히 실패한다.
|
|
2488
|
+
*/
|
|
2489
|
+
/** 앱 handle — 전역 유일, 앱 도메인의 라벨(`{handle}.{앱 도메인}`). 상점 코드와 같은 형식, 3~40자 */
|
|
2490
|
+
const APP_HANDLE_PATTERN = /^[a-z0-9](?:[a-z0-9-]{1,38}[a-z0-9])$/;
|
|
2491
|
+
/** handle로 쓸 수 없는 라벨 — 플랫폼 호스트·혼동되는 이름 */
|
|
2492
|
+
const APP_RESERVED_HANDLES = /* @__PURE__ */ new Set([
|
|
2493
|
+
"www",
|
|
2494
|
+
"api",
|
|
2495
|
+
"app",
|
|
2496
|
+
"apps",
|
|
2497
|
+
"admin",
|
|
2498
|
+
"accounts",
|
|
2499
|
+
"store",
|
|
2500
|
+
"mcp",
|
|
2501
|
+
"docs",
|
|
2502
|
+
"sayren",
|
|
2503
|
+
"preview",
|
|
2504
|
+
"runner",
|
|
2505
|
+
"static",
|
|
2506
|
+
"cdn"
|
|
2507
|
+
]);
|
|
2508
|
+
const appHandleSchema = z.string().regex(APP_HANDLE_PATTERN, "앱 handle은 소문자·숫자·하이픈 3~40자입니다").refine((value) => !APP_RESERVED_HANDLES.has(value) && !value.endsWith("-preview"), { message: "쓸 수 없는 handle입니다" });
|
|
2509
|
+
/**
|
|
2510
|
+
* 설치 앱에 열지 않는 스코프 — 구성원(초대로 권한 상승), 상점 설정 전반(`storefrontOrigins`로 소셜 로그인 code 가로채기), 스토어 웹훅
|
|
2511
|
+
* (앱 구독으로 대체, 제거 뒤에도 남는 문제), 스토어프론트 편집·발행, 감사 로그. 커스텀 연동은 계정 액세스 토큰(#71)이 맡는다
|
|
2512
|
+
*/
|
|
2513
|
+
const NON_INSTALLABLE_SCOPES = /* @__PURE__ */ new Set([
|
|
2514
|
+
"member:r",
|
|
2515
|
+
"member:rw",
|
|
2516
|
+
"store:rw",
|
|
2517
|
+
"webhook:r",
|
|
2518
|
+
"webhook:rw",
|
|
2519
|
+
"storefront:r",
|
|
2520
|
+
"storefront:rw",
|
|
2521
|
+
"audit:r"
|
|
2522
|
+
]);
|
|
2523
|
+
/** 앱이 요청할 수 있는 스코프 — 카탈로그 전체에서 위를 뺀 것 */
|
|
2524
|
+
const INSTALLABLE_SCOPES = Object.entries(STORE_SCOPE_RESOURCES).flatMap(([resource, accesses]) => accesses.map((access) => `${resource}:${access}`)).filter((scope) => !NON_INSTALLABLE_SCOPES.has(scope));
|
|
2525
|
+
function isInstallableScope(scope) {
|
|
2526
|
+
return isStoreScope(scope) && !NON_INSTALLABLE_SCOPES.has(scope);
|
|
2527
|
+
}
|
|
2528
|
+
const appScopeSchema = z.string().refine(isInstallableScope, { message: "앱이 요청할 수 없는 스코프입니다" });
|
|
2529
|
+
/**
|
|
2530
|
+
* 앱 생명주기 이벤트 — 웹훅 카탈로그 밖이고 앱 코드만 받는다. `APP.UNINSTALLED`·`APP.STORE_REDACT`는 설치가 끝난 뒤라 토큰 없이 온다
|
|
2531
|
+
* (`APP.STORE_REDACT`는 제거 48시간 뒤).
|
|
2532
|
+
*/
|
|
2533
|
+
const APP_LIFECYCLE_EVENT_TYPES = [
|
|
2534
|
+
"APP.INSTALLED",
|
|
2535
|
+
"APP.UNINSTALLED",
|
|
2536
|
+
"APP.SETTINGS_UPDATED",
|
|
2537
|
+
"APP.SCOPES_UPDATED",
|
|
2538
|
+
"APP.STORE_REDACT",
|
|
2539
|
+
"APP.CUSTOMER_REDACT"
|
|
2540
|
+
];
|
|
2541
|
+
/**
|
|
2542
|
+
* 이벤트를 받는 데 필요한 스코프 — 동의 스코프가 이것을 덮지 못하면 분배하지 않고, 매니페스트 `scopes`가 덮지 못하면 빌드가 실패한다.
|
|
2543
|
+
* 생명주기 이벤트는 스코프가 없다(null). 새 웹훅 이벤트를 더하면 이 표도 더해야 테스트가 통과한다
|
|
2544
|
+
*/
|
|
2545
|
+
const APP_EVENT_SCOPES = {
|
|
2546
|
+
"ORDER.PAID": "order:r",
|
|
2547
|
+
"ORDER.CONFIRMED": "order:r",
|
|
2548
|
+
"ORDER.DISPATCHED": "order:r",
|
|
2549
|
+
"ORDER.DELIVERED": "order:r",
|
|
2550
|
+
"ORDER.PURCHASE_DECIDED": "order:r",
|
|
2551
|
+
"ORDER.ADDRESS_CHANGE_REQUESTED": "order:r",
|
|
2552
|
+
"ORDER.FULFILLED": "order:r",
|
|
2553
|
+
"ORDER.CANCELED": "order:r",
|
|
2554
|
+
"ORDER.FLOW_NOTIFICATION": "order:r",
|
|
2555
|
+
"ORDER.FLOW_STATE_CHANGED": "order:r",
|
|
2556
|
+
"CLAIM.CANCEL_REQUESTED": "claim:r",
|
|
2557
|
+
"CLAIM.RETURN_REQUESTED": "claim:r",
|
|
2558
|
+
"CLAIM.EXCHANGE_REQUESTED": "claim:r",
|
|
2559
|
+
"CLAIM.COMPLETED": "claim:r",
|
|
2560
|
+
"INQUIRY.CREATED": "inquiry:r",
|
|
2561
|
+
"REVIEW.CREATED": "review:r",
|
|
2562
|
+
"PRODUCT.OUT_OF_STOCK": "product:r",
|
|
2563
|
+
"APP.INSTALLED": null,
|
|
2564
|
+
"APP.UNINSTALLED": null,
|
|
2565
|
+
"APP.SETTINGS_UPDATED": null,
|
|
2566
|
+
"APP.SCOPES_UPDATED": null,
|
|
2567
|
+
"APP.STORE_REDACT": null,
|
|
2568
|
+
"APP.CUSTOMER_REDACT": null
|
|
2569
|
+
};
|
|
2570
|
+
const APP_EVENT_TYPES = [...KNOWN_WEBHOOK_EVENT_TYPES, ...APP_LIFECYCLE_EVENT_TYPES];
|
|
2571
|
+
const appEventTypeSchema = z.enum(APP_EVENT_TYPES);
|
|
2572
|
+
/** 동의 스코프가 이벤트를 받을 수 있는가 — `rw`는 `r`을 함의한다 */
|
|
2573
|
+
function scopesCoverEvent(scopes, event) {
|
|
2574
|
+
const required = APP_EVENT_SCOPES[event];
|
|
2575
|
+
if (required === null) return true;
|
|
2576
|
+
const [resource] = required.split(":");
|
|
2577
|
+
return scopes.includes(required) || scopes.includes(`${resource}:rw`);
|
|
2578
|
+
}
|
|
2579
|
+
/**
|
|
2580
|
+
* 외부 호출 호스트 — https·443·소문자 호스트명. 와일드카드는 한 단계(`*.example.com`)만. IP·`localhost`·sayren 플랫폼 호스트는
|
|
2581
|
+
* 받지 않는다(내부 호스트 차단은 outbound Worker가 다시 본다 — 여기는 매니페스트 형식). 253자 이하, 라벨 2개 이상
|
|
2582
|
+
*/
|
|
2583
|
+
const HOST_LABEL = /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/;
|
|
2584
|
+
const TLD = /^(?:[a-z]{2,63}|xn--[a-z0-9-]{1,59})$/;
|
|
2585
|
+
const APP_NETWORK_DENIED_SUFFIXES = [
|
|
2586
|
+
"sayren.app",
|
|
2587
|
+
"sayren.co",
|
|
2588
|
+
"avarlabs.com",
|
|
2589
|
+
"localhost",
|
|
2590
|
+
"local",
|
|
2591
|
+
"internal"
|
|
2592
|
+
];
|
|
2593
|
+
function appNetworkHostProblem(raw) {
|
|
2594
|
+
const host = raw.trim();
|
|
2595
|
+
if (host !== raw || host !== host.toLowerCase()) return "호스트는 소문자이고 앞뒤 공백이 없어야 합니다";
|
|
2596
|
+
if (host.length > 253) return "호스트가 너무 깁니다";
|
|
2597
|
+
if (/^\d+\.\d+\.\d+\.\d+$/.test(host) || host.includes(":")) return "IP 주소는 쓸 수 없습니다";
|
|
2598
|
+
const labels = host.split(".");
|
|
2599
|
+
const named = labels[0] === "*" ? labels.slice(1) : labels;
|
|
2600
|
+
if (named.length < 2) return "호스트는 라벨이 둘 이상이어야 합니다";
|
|
2601
|
+
if (!named.every((label) => HOST_LABEL.test(label))) return "호스트 형식이 맞지 않습니다";
|
|
2602
|
+
if (!TLD.test(named[named.length - 1])) return "최상위 도메인 형식이 맞지 않습니다";
|
|
2603
|
+
const apex = named.join(".");
|
|
2604
|
+
for (const suffix of APP_NETWORK_DENIED_SUFFIXES) if (apex === suffix || apex.endsWith(`.${suffix}`)) return "sayren 플랫폼·내부 호스트는 쓸 수 없습니다";
|
|
2605
|
+
return null;
|
|
2606
|
+
}
|
|
2607
|
+
const appNetworkHostSchema = z.string().superRefine((value, ctx) => {
|
|
2608
|
+
const problem = appNetworkHostProblem(value);
|
|
2609
|
+
if (problem) ctx.addIssue({
|
|
2610
|
+
code: "custom",
|
|
2611
|
+
message: problem
|
|
2612
|
+
});
|
|
2613
|
+
});
|
|
2614
|
+
/** 셀러가 콘솔에서 채우는 설정 필드 — 콘솔이 폼을 그릴 수 있는 네 형식만 */
|
|
2615
|
+
const APP_SETTING_FIELD_TYPES = [
|
|
2616
|
+
"string",
|
|
2617
|
+
"number",
|
|
2618
|
+
"boolean",
|
|
2619
|
+
"select"
|
|
2620
|
+
];
|
|
2621
|
+
const APP_SETTINGS_MAX_FIELDS = 30;
|
|
2622
|
+
const APP_SETTING_KEY_PATTERN = /^[a-zA-Z][a-zA-Z0-9_]{0,39}$/;
|
|
2623
|
+
const appSettingFieldSchema = z.strictObject({
|
|
2624
|
+
type: z.enum(APP_SETTING_FIELD_TYPES),
|
|
2625
|
+
label: z.string().trim().min(1).max(60),
|
|
2626
|
+
description: z.string().trim().max(200).optional(),
|
|
2627
|
+
required: z.boolean().optional(),
|
|
2628
|
+
/** 비밀 값 — 암호화 저장, 응답에서 가린다, 에이전트·토큰으로 읽을 수 없다. `string`만 */
|
|
2629
|
+
secret: z.boolean().optional(),
|
|
2630
|
+
/** `select`의 선택지 */
|
|
2631
|
+
options: z.array(z.strictObject({
|
|
2632
|
+
value: z.string().min(1).max(60),
|
|
2633
|
+
label: z.string().trim().min(1).max(60)
|
|
2634
|
+
})).min(1).max(50).optional(),
|
|
2635
|
+
default: z.union([
|
|
2636
|
+
z.string(),
|
|
2637
|
+
z.number(),
|
|
2638
|
+
z.boolean()
|
|
2639
|
+
]).optional()
|
|
2640
|
+
});
|
|
2641
|
+
const APP_SECRET_NAME_PATTERN = /^[A-Z][A-Z0-9_]{0,63}$/;
|
|
2642
|
+
const APP_SECRETS_MAX = 20;
|
|
2643
|
+
const APP_EVENTS_MAX = 50;
|
|
2644
|
+
const APP_NETWORK_HOSTS_MAX = 20;
|
|
2645
|
+
const appManifestSchema = z.strictObject({
|
|
2646
|
+
handle: appHandleSchema,
|
|
2647
|
+
name: z.string().trim().min(1).max(60),
|
|
2648
|
+
description: z.string().trim().max(500).optional(),
|
|
2649
|
+
/** https 이미지 주소. 동의 화면·상점 플랫폼 목록에 보인다 */
|
|
2650
|
+
iconUrl: z.url().startsWith("https://").optional(),
|
|
2651
|
+
supportEmail: z.email().optional(),
|
|
2652
|
+
/** 실행 위치 — A1은 코드 업로드 앱만. 외부 런타임(`external`)은 A3에서 연다 */
|
|
2653
|
+
runtime: z.literal("code").default("code"),
|
|
2654
|
+
/** 설치 때 전체 동의하는 스코프 */
|
|
2655
|
+
scopes: z.array(appScopeSchema).max(32).default([]),
|
|
2656
|
+
network: z.strictObject({ hosts: z.array(appNetworkHostSchema).max(20) }).default({ hosts: [] }),
|
|
2657
|
+
/** 이벤트 함수 구독 */
|
|
2658
|
+
events: z.array(appEventTypeSchema).max(50).default([]),
|
|
2659
|
+
ui: z.strictObject({
|
|
2660
|
+
/** 상점 플랫폼 앱 화면(iframe) — 정적 SPA 진입 파일 */
|
|
2661
|
+
adminPage: z.strictObject({ entry: z.string().regex(/^ui\/[A-Za-z0-9._/-]+\.html$/) }) }).optional(),
|
|
2662
|
+
settings: z.record(z.string().regex(APP_SETTING_KEY_PATTERN), appSettingFieldSchema).refine((fields) => Object.keys(fields).length <= 30, { message: `설정 필드는 30개까지입니다` }).default({}),
|
|
2663
|
+
/** 개발자 비밀 값 이름 — 값은 CLI(`sayren app secret put`)로 따로 넣는다 */
|
|
2664
|
+
secrets: z.array(z.string().regex(APP_SECRET_NAME_PATTERN)).max(20).default([])
|
|
2665
|
+
});
|
|
2666
|
+
/** 소스 안 비밀 값 형식(매뉴얼 「빌드」 `SECRET_IN_SOURCE`) — 번들에 들어가면 막는다. CLI `app verify`와 api 빌드 `BUNDLE`이 같은 표를 쓴다 */
|
|
2667
|
+
const APP_SECRET_PATTERNS = [
|
|
2668
|
+
/sy_pat_[A-Za-z0-9]{8,}/,
|
|
2669
|
+
/\b(?:test|live)_sk_[A-Za-z0-9]{8,}/,
|
|
2670
|
+
/-----BEGIN (?:RSA |EC |OPENSSH )?PRIVATE KEY-----/,
|
|
2671
|
+
/\bsak_[a-z0-9]+_[0-9a-f]{32}\b/
|
|
2672
|
+
];
|
|
2673
|
+
/** 서버 번들 gzip 상한(3MiB)·화면 정적 파일 상한 — 매뉴얼 「빌드 › 한도」 */
|
|
2674
|
+
const APP_SERVER_BUNDLE_MAX_GZIP_BYTES = 3145728;
|
|
2675
|
+
const APP_UI_MAX_BYTES = 26214400;
|
|
2676
|
+
const APP_UI_MAX_FILES = 2e3;
|
|
2677
|
+
/** 스키마가 보지 못하는 필드 사이 규칙 — 빌드 `VERIFY`·CLI `app verify`가 같은 결과를 낸다. 비어 있으면 통과 */
|
|
2678
|
+
function appManifestProblems(manifest) {
|
|
2679
|
+
const problems = [];
|
|
2680
|
+
const unique = (values, path, label) => {
|
|
2681
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2682
|
+
for (const value of values) {
|
|
2683
|
+
if (seen.has(value)) problems.push({
|
|
2684
|
+
path,
|
|
2685
|
+
code: "DUPLICATE",
|
|
2686
|
+
message: `${label}이 중복됐습니다: ${value}`
|
|
2687
|
+
});
|
|
2688
|
+
seen.add(value);
|
|
2689
|
+
}
|
|
2690
|
+
};
|
|
2691
|
+
unique(manifest.scopes, "scopes", "스코프");
|
|
2692
|
+
unique(manifest.events, "events", "이벤트");
|
|
2693
|
+
unique(manifest.network.hosts, "network.hosts", "호스트");
|
|
2694
|
+
unique(manifest.secrets, "secrets", "비밀 값 이름");
|
|
2695
|
+
for (const event of manifest.events) if (!scopesCoverEvent(manifest.scopes, event)) problems.push({
|
|
2696
|
+
path: `events.${event}`,
|
|
2697
|
+
code: "EVENT_SCOPE_MISSING",
|
|
2698
|
+
message: `${event} 이벤트를 받으려면 ${APP_EVENT_SCOPES[event]} 스코프가 필요합니다`
|
|
2699
|
+
});
|
|
2700
|
+
for (const [key, field] of Object.entries(manifest.settings)) {
|
|
2701
|
+
if (field.type === "select" && !field.options) problems.push({
|
|
2702
|
+
path: `settings.${key}`,
|
|
2703
|
+
code: "SELECT_OPTIONS_REQUIRED",
|
|
2704
|
+
message: "select 필드에는 options가 필요합니다"
|
|
2705
|
+
});
|
|
2706
|
+
if (field.secret && field.type !== "string") problems.push({
|
|
2707
|
+
path: `settings.${key}`,
|
|
2708
|
+
code: "SECRET_FIELD_TYPE",
|
|
2709
|
+
message: "비밀 필드는 string만 됩니다"
|
|
2710
|
+
});
|
|
2711
|
+
if (field.default !== void 0) {
|
|
2712
|
+
const expected = field.type === "select" ? "string" : field.type;
|
|
2713
|
+
const matchesOption = field.type !== "select" || field.options?.some((o) => o.value === field.default);
|
|
2714
|
+
if (typeof field.default !== expected || !matchesOption) problems.push({
|
|
2715
|
+
path: `settings.${key}`,
|
|
2716
|
+
code: "DEFAULT_TYPE_MISMATCH",
|
|
2717
|
+
message: "default가 필드 형식과 맞지 않습니다"
|
|
2718
|
+
});
|
|
2719
|
+
}
|
|
2720
|
+
}
|
|
2721
|
+
return problems;
|
|
2722
|
+
}
|
|
2723
|
+
function appCapabilitiesOf(manifest) {
|
|
2724
|
+
const sorted = (values) => [...new Set(values)].sort();
|
|
2725
|
+
return {
|
|
2726
|
+
scopes: sorted(manifest.scopes),
|
|
2727
|
+
events: sorted(manifest.events),
|
|
2728
|
+
networkHosts: sorted(manifest.network.hosts),
|
|
2729
|
+
adminPage: Boolean(manifest.ui?.adminPage),
|
|
2730
|
+
requiredSettings: Object.entries(manifest.settings).filter(([, field]) => field.required).map(([key]) => key).sort(),
|
|
2731
|
+
secretSettings: Object.entries(manifest.settings).filter(([, field]) => field.secret).map(([key]) => key).sort()
|
|
2732
|
+
};
|
|
2733
|
+
}
|
|
2734
|
+
const grew = (previous, next) => next.some((value) => !previous.includes(value));
|
|
2735
|
+
/** 스코프 집합이 넓어졌는가 — `r`→`rw`는 넓어짐, `rw`→`r`은 아님 */
|
|
2736
|
+
function scopesWiden(previous, next) {
|
|
2737
|
+
return next.some((scope) => {
|
|
2738
|
+
if (previous.includes(scope)) return false;
|
|
2739
|
+
const [resource, access] = scope.split(":");
|
|
2740
|
+
return !(access === "r" && previous.includes(`${resource}:rw`));
|
|
2741
|
+
});
|
|
2742
|
+
}
|
|
2743
|
+
const APP_CAPABILITIES = [
|
|
2744
|
+
{
|
|
2745
|
+
key: "scopes",
|
|
2746
|
+
title: "이 앱이 할 수 있는 일",
|
|
2747
|
+
describe: (c) => c.scopes.map((scope) => describeScope(scope) ?? scope),
|
|
2748
|
+
widens: (p, n) => scopesWiden(p.scopes, n.scopes)
|
|
2749
|
+
},
|
|
2750
|
+
{
|
|
2751
|
+
key: "network",
|
|
2752
|
+
title: "이 앱이 데이터를 보내는 곳",
|
|
2753
|
+
describe: (c) => c.networkHosts,
|
|
2754
|
+
widens: (p, n) => grew(p.networkHosts, n.networkHosts)
|
|
2755
|
+
},
|
|
2756
|
+
{
|
|
2757
|
+
key: "events",
|
|
2758
|
+
title: "이 앱이 받는 알림",
|
|
2759
|
+
describe: (c) => c.events,
|
|
2760
|
+
widens: (p, n) => grew(p.events, n.events)
|
|
2761
|
+
},
|
|
2762
|
+
{
|
|
2763
|
+
key: "ui",
|
|
2764
|
+
title: "상점 플랫폼 화면",
|
|
2765
|
+
describe: (c) => c.adminPage ? ["상점 플랫폼에 이 앱의 화면을 더합니다"] : [],
|
|
2766
|
+
widens: (p, n) => !p.adminPage && n.adminPage
|
|
2767
|
+
}
|
|
2768
|
+
];
|
|
2769
|
+
/** 재동의가 필요한 capability 키 — 비어 있으면 release가 모든 설치에 곧바로 반영된다 */
|
|
2770
|
+
function appManifestWidens(previous, next) {
|
|
2771
|
+
return APP_CAPABILITIES.filter((c) => c.widens(previous, next)).map((c) => c.key);
|
|
2772
|
+
}
|
|
2773
|
+
//#endregion
|
|
2774
|
+
//#region src/schemas/apps.ts
|
|
2775
|
+
/**
|
|
2776
|
+
* 설치 앱 관리 API 계약 (이슈 #22) — `/v1/apps/installations/**`(상점이 설치한 앱), `/v1/app/installation`(설치 토큰 자기 조회).
|
|
2777
|
+
* 개발자 API(`/platform/v1/developer/**`)는 플랫폼 플레인이라 여기 두지 않는다.
|
|
2778
|
+
*/
|
|
2779
|
+
/** 배포 범위 — `PRIVATE_OWN` 개발자 조직 구성원이 OWNER·ADMIN인 상점과 개발 상점(A1) · `PRIVATE_LINK` 설치 링크(A2) · `LISTED` 공개 목록(A3) */
|
|
2780
|
+
const appDistributionSchema = z.enum([
|
|
2781
|
+
"PRIVATE_OWN",
|
|
2782
|
+
"PRIVATE_LINK",
|
|
2783
|
+
"LISTED"
|
|
2784
|
+
]);
|
|
2785
|
+
const appRuntimeSchema = z.enum(["CODE", "EXTERNAL"]);
|
|
2786
|
+
/** 앱 상태 — `SUSPENDED`는 운영자 정지(모든 설치 토큰·실행 거부, 설치 행은 그대로) */
|
|
2787
|
+
const appStatusSchema = z.enum([
|
|
2788
|
+
"ACTIVE",
|
|
2789
|
+
"SUSPENDED",
|
|
2790
|
+
"DELETED"
|
|
2791
|
+
]);
|
|
2792
|
+
const appInstallationStatusSchema = z.enum([
|
|
2793
|
+
"ACTIVE",
|
|
2794
|
+
"UNINSTALLED",
|
|
2795
|
+
"SUSPENDED"
|
|
2796
|
+
]);
|
|
2797
|
+
/** 설치 앱의 앱 정보 — 상점이 보는 범위만(개발자 비밀 값·소스·빌드는 없다) */
|
|
2798
|
+
const appInstallationAppSchema = z.object({
|
|
2799
|
+
appId: z.string(),
|
|
2800
|
+
handle: z.string().describe("앱 handle. 앱 화면 주소의 라벨"),
|
|
2801
|
+
name: z.string(),
|
|
2802
|
+
iconUrl: z.string().nullable(),
|
|
2803
|
+
description: z.string().nullable(),
|
|
2804
|
+
developerName: z.string().describe("개발자 조직 이름"),
|
|
2805
|
+
supportEmail: z.string().nullable(),
|
|
2806
|
+
distribution: appDistributionSchema,
|
|
2807
|
+
runtime: appRuntimeSchema,
|
|
2808
|
+
status: appStatusSchema
|
|
2809
|
+
});
|
|
2810
|
+
const settingsValueSchema = z.union([
|
|
2811
|
+
z.string(),
|
|
2812
|
+
z.number(),
|
|
2813
|
+
z.boolean(),
|
|
2814
|
+
z.null()
|
|
2815
|
+
]);
|
|
2816
|
+
/** 설치 설정 — 비밀 필드 값은 가려서 준다(앞 4자 + `…`). 비어 있는 필드는 null */
|
|
2817
|
+
const appInstallationSettingsSchema = z.object({
|
|
2818
|
+
fields: z.record(z.string(), appSettingFieldSchema).describe("실행 버전 매니페스트의 설정 필드"),
|
|
2819
|
+
values: z.record(z.string(), settingsValueSchema),
|
|
2820
|
+
/** 필수 설정이 모두 채워졌는가 — 아니면 이벤트 함수를 부르지 않는다 */
|
|
2821
|
+
configured: z.boolean(),
|
|
2822
|
+
updatedAt: z.string().nullable()
|
|
2823
|
+
});
|
|
2824
|
+
/** 설정 저장 — 비밀 필드를 생략하면 현재 값을 유지한다. null은 비운다 */
|
|
2825
|
+
const appInstallationSettingsUpdateSchema = z.object({ values: z.record(z.string(), settingsValueSchema) });
|
|
2826
|
+
const appInstallationSchema = z.object({
|
|
2827
|
+
installationId: z.string(),
|
|
2828
|
+
storeId: z.string(),
|
|
2829
|
+
app: appInstallationAppSchema,
|
|
2830
|
+
status: appInstallationStatusSchema,
|
|
2831
|
+
versionNumber: z.number().int().describe("실행 중인 앱 버전"),
|
|
2832
|
+
pendingVersionNumber: z.number().int().nullable().describe("재동의가 필요한 새 버전. 없으면 null"),
|
|
2833
|
+
grantedScopes: z.array(z.string()).describe("동의한 스코프"),
|
|
2834
|
+
grantedNetworkHosts: z.array(z.string()).describe("동의한 외부 호출 호스트"),
|
|
2835
|
+
events: z.array(appEventTypeSchema).describe("이 설치가 받는 이벤트"),
|
|
2836
|
+
adminPage: z.boolean().describe("상점 플랫폼 앱 화면이 있는가"),
|
|
2837
|
+
/** 설치 결과의 캐시가 아니라 실행 버전 매니페스트의 요약 */
|
|
2838
|
+
settings: appInstallationSettingsSchema,
|
|
2839
|
+
installedBy: z.object({
|
|
2840
|
+
userId: z.string(),
|
|
2841
|
+
name: z.string().nullable()
|
|
2842
|
+
}).nullable(),
|
|
2843
|
+
installedAt: z.string(),
|
|
2844
|
+
uninstalledAt: z.string().nullable(),
|
|
2845
|
+
updatedAt: z.string()
|
|
2846
|
+
});
|
|
2847
|
+
/** `POST /v1/apps/installations` — accounts 설치 동의 결과(1회용 JWT)를 낸다(상점 플랫폼 전용) */
|
|
2848
|
+
const appInstallRequestSchema = z.object({ result: z.string().min(1).describe("accounts가 발급한 설치 결과 JWT(`typ: sayren-install+jwt`)") });
|
|
2849
|
+
/** `POST /v1/apps/installations/{id}/session` — 앱 화면 iframe이 쓸 세션 토큰(5분) */
|
|
2850
|
+
const appSessionSchema = z.object({
|
|
2851
|
+
token: z.string(),
|
|
2852
|
+
expiresAt: z.string(),
|
|
2853
|
+
appOrigin: z.url().describe("iframe 주소의 origin(`https://{handle}.{앱 도메인}`)")
|
|
2854
|
+
});
|
|
2855
|
+
/** `GET /v1/app/installation` — 설치 토큰으로 보는 자기 설치 */
|
|
2856
|
+
const appInstallationSelfSchema = z.object({
|
|
2857
|
+
installationId: z.string(),
|
|
2858
|
+
appId: z.string(),
|
|
2859
|
+
storeId: z.string(),
|
|
2860
|
+
storeName: z.string(),
|
|
2861
|
+
versionNumber: z.number().int(),
|
|
2862
|
+
grantedScopes: z.array(z.string()),
|
|
2863
|
+
installedAt: z.string()
|
|
2864
|
+
});
|
|
2865
|
+
const developerOrgSchema = z.object({
|
|
2866
|
+
orgId: z.string(),
|
|
2867
|
+
name: z.string(),
|
|
2868
|
+
role: z.enum(["OWNER", "MEMBER"])
|
|
2869
|
+
});
|
|
2870
|
+
const appVersionStatusSchema = z.enum([
|
|
2871
|
+
"BUILDING",
|
|
2872
|
+
"READY",
|
|
2873
|
+
"FAILED",
|
|
2874
|
+
"REVOKED"
|
|
2875
|
+
]);
|
|
2876
|
+
/** 빌드 단계(매뉴얼 「앱 버전과 배포 › 빌드」). 로컬 번들(A1)은 `DEPENDENCIES`·`VERIFY`·`BUNDLE`·`UPLOAD`를 서버가 돈다 */
|
|
2877
|
+
const appBuildStageSchema = z.enum([
|
|
2878
|
+
"DEPENDENCIES",
|
|
2879
|
+
"VERIFY",
|
|
2880
|
+
"INSTALL",
|
|
2881
|
+
"TYPECHECK",
|
|
2882
|
+
"BUILD",
|
|
2883
|
+
"BUNDLE",
|
|
2884
|
+
"UPLOAD"
|
|
2885
|
+
]);
|
|
2886
|
+
const appBuildSchema = z.object({
|
|
2887
|
+
buildId: z.string(),
|
|
2888
|
+
status: z.enum([
|
|
2889
|
+
"QUEUED",
|
|
2890
|
+
"RUNNING",
|
|
2891
|
+
"SUCCEEDED",
|
|
2892
|
+
"FAILED",
|
|
2893
|
+
"TIMED_OUT"
|
|
2894
|
+
]),
|
|
2895
|
+
stages: z.array(z.object({
|
|
2896
|
+
stage: z.string(),
|
|
2897
|
+
ok: z.boolean(),
|
|
2898
|
+
durationMs: z.number()
|
|
2899
|
+
})),
|
|
2900
|
+
failureStage: z.string().nullable(),
|
|
2901
|
+
errorCode: z.string().nullable(),
|
|
2902
|
+
logTail: z.string().nullable(),
|
|
2903
|
+
/** 의존성 검사 결과(거절·경고). 검사 전이면 null */
|
|
2904
|
+
dependencies: z.object({
|
|
2905
|
+
rejected: z.array(z.record(z.string(), z.unknown())),
|
|
2906
|
+
warnings: z.array(z.record(z.string(), z.unknown()))
|
|
2907
|
+
}).nullable(),
|
|
2908
|
+
createdAt: z.string(),
|
|
2909
|
+
finishedAt: z.string().nullable()
|
|
2910
|
+
});
|
|
2911
|
+
const appVersionSchema = z.object({
|
|
2912
|
+
versionId: z.string(),
|
|
2913
|
+
number: z.number().int(),
|
|
2914
|
+
status: appVersionStatusSchema,
|
|
2915
|
+
current: z.boolean().describe("앱의 현재 버전(release한 버전)인가"),
|
|
2916
|
+
message: z.string().nullable(),
|
|
2917
|
+
capabilities: z.object({
|
|
2918
|
+
scopes: z.array(z.string()),
|
|
2919
|
+
events: z.array(z.string()),
|
|
2920
|
+
networkHosts: z.array(z.string()),
|
|
2921
|
+
adminPage: z.boolean(),
|
|
2922
|
+
requiredSettings: z.array(z.string()),
|
|
2923
|
+
secretSettings: z.array(z.string())
|
|
2924
|
+
}),
|
|
2925
|
+
build: appBuildSchema.nullable(),
|
|
2926
|
+
releasedAt: z.string().nullable(),
|
|
2927
|
+
revokedAt: z.string().nullable(),
|
|
2928
|
+
createdAt: z.string()
|
|
2929
|
+
});
|
|
2930
|
+
const developerAppSchema = z.object({
|
|
2931
|
+
appId: z.string(),
|
|
2932
|
+
orgId: z.string(),
|
|
2933
|
+
handle: z.string(),
|
|
2934
|
+
name: z.string(),
|
|
2935
|
+
description: z.string().nullable(),
|
|
2936
|
+
iconUrl: z.string().nullable(),
|
|
2937
|
+
supportEmail: z.string().nullable(),
|
|
2938
|
+
distribution: appDistributionSchema,
|
|
2939
|
+
runtime: appRuntimeSchema,
|
|
2940
|
+
status: appStatusSchema,
|
|
2941
|
+
suspendedReason: z.string().nullable(),
|
|
2942
|
+
currentVersionNumber: z.number().int().nullable(),
|
|
2943
|
+
installations: z.object({
|
|
2944
|
+
active: z.number().int(),
|
|
2945
|
+
pendingReconsent: z.number().int()
|
|
2946
|
+
}),
|
|
2947
|
+
createdAt: z.string(),
|
|
2948
|
+
updatedAt: z.string()
|
|
2949
|
+
});
|
|
2950
|
+
const developerAppCreateSchema = z.object({
|
|
2951
|
+
handle: appHandleSchema,
|
|
2952
|
+
name: z.string().trim().min(1).max(60),
|
|
2953
|
+
description: z.string().trim().max(500).optional()
|
|
2954
|
+
});
|
|
2955
|
+
/** `PUT /platform/v1/developer/apps/{appId}/versions` — CLI `app deploy`가 올리는 번들 */
|
|
2956
|
+
const appArtifactUploadSchema = z.object({
|
|
2957
|
+
manifest: z.unknown().describe("sayren.app.json 원문(JSON)"),
|
|
2958
|
+
packageJson: z.string().max(262144),
|
|
2959
|
+
lockfile: z.string().max(4194304).nullable(),
|
|
2960
|
+
/** 서버 번들(ESM 한 파일) base64. gzip 3MB까지 */
|
|
2961
|
+
server: z.string().min(1),
|
|
2962
|
+
ui: z.array(z.object({
|
|
2963
|
+
path: z.string().min(1).max(512),
|
|
2964
|
+
content: z.string()
|
|
2965
|
+
})).max(2e3).default([]),
|
|
2966
|
+
message: z.string().trim().max(200).optional()
|
|
2967
|
+
});
|
|
2968
|
+
const appReleaseResultSchema = z.object({
|
|
2969
|
+
version: appVersionSchema,
|
|
2970
|
+
/** release가 곧바로 적용된 설치 수 */
|
|
2971
|
+
applied: z.number().int(),
|
|
2972
|
+
/** 넓어진 범위라 재동의를 기다리는 설치 수 */
|
|
2973
|
+
pendingReconsent: z.number().int()
|
|
2974
|
+
});
|
|
2975
|
+
const appSecretSummarySchema = z.object({
|
|
2976
|
+
name: z.string(),
|
|
2977
|
+
updatedAt: z.string()
|
|
2978
|
+
});
|
|
2979
|
+
const devStoreSchema = z.object({
|
|
2980
|
+
storeId: z.string(),
|
|
2981
|
+
name: z.string(),
|
|
2982
|
+
storeCode: z.string(),
|
|
2983
|
+
status: z.enum([
|
|
2984
|
+
"ACTIVE",
|
|
2985
|
+
"SUSPENDED",
|
|
2986
|
+
"CLOSED"
|
|
2987
|
+
]),
|
|
2988
|
+
createdAt: z.string()
|
|
2989
|
+
});
|
|
2990
|
+
/** 개발자가 보는 설치 한 줄 */
|
|
2991
|
+
const developerInstallationSchema = z.object({
|
|
2992
|
+
installationId: z.string(),
|
|
2993
|
+
storeId: z.string(),
|
|
2994
|
+
storeName: z.string(),
|
|
2995
|
+
status: appInstallationStatusSchema,
|
|
2996
|
+
versionNumber: z.number().int().nullable(),
|
|
2997
|
+
pendingVersionNumber: z.number().int().nullable(),
|
|
2998
|
+
installedAt: z.string()
|
|
2999
|
+
});
|
|
3000
|
+
/** `POST /platform/v1/developer/apps/{appId}/dev-token` — `sayren app dev`가 쓰는 설치 토큰(1시간) */
|
|
3001
|
+
const appDevTokenSchema = z.object({
|
|
3002
|
+
token: z.string(),
|
|
3003
|
+
expiresAt: z.string(),
|
|
3004
|
+
installationId: z.string(),
|
|
3005
|
+
storeId: z.string()
|
|
3006
|
+
});
|
|
3007
|
+
/** 호출 기록 한 건(개발자 로그) */
|
|
3008
|
+
const appInvocationSchema = z.object({
|
|
3009
|
+
invocationId: z.string(),
|
|
3010
|
+
storeId: z.string(),
|
|
3011
|
+
storeName: z.string().nullable(),
|
|
3012
|
+
installationId: z.string(),
|
|
3013
|
+
versionNumber: z.number().int().nullable(),
|
|
3014
|
+
kind: z.enum([
|
|
3015
|
+
"event",
|
|
3016
|
+
"http",
|
|
3017
|
+
"schedule"
|
|
3018
|
+
]),
|
|
3019
|
+
eventType: z.string().nullable(),
|
|
3020
|
+
outcome: z.enum([
|
|
3021
|
+
"OK",
|
|
3022
|
+
"ERROR",
|
|
3023
|
+
"LIMIT_EXCEEDED",
|
|
3024
|
+
"REJECTED"
|
|
3025
|
+
]),
|
|
3026
|
+
errorMessage: z.string().nullable(),
|
|
3027
|
+
cpuMs: z.number().nullable(),
|
|
3028
|
+
durationMs: z.number(),
|
|
3029
|
+
/** `console.*`·`ctx.log.*` 줄(최대 100줄, 줄마다 500자) */
|
|
3030
|
+
logs: z.array(z.object({
|
|
3031
|
+
level: z.string(),
|
|
3032
|
+
message: z.string()
|
|
3033
|
+
})),
|
|
3034
|
+
createdAt: z.string()
|
|
3035
|
+
});
|
|
3036
|
+
/** 앱 배포 키(developer.sayren.app 「키 발행」) — 값은 만들 때 한 번만 보인다 */
|
|
3037
|
+
const appDeployKeySchema = z.object({
|
|
3038
|
+
keyId: z.string(),
|
|
3039
|
+
name: z.string(),
|
|
3040
|
+
prefix: z.string().describe("값 앞 12자"),
|
|
3041
|
+
createdAt: z.string(),
|
|
3042
|
+
lastUsedAt: z.string().nullable()
|
|
3043
|
+
});
|
|
3044
|
+
const appDeployKeyCreatedSchema = appDeployKeySchema.extend({
|
|
3045
|
+
/** 전체 값 — 이때만 보인다 */
|
|
3046
|
+
value: z.string() });
|
|
3047
|
+
const appDeployKeyCreateSchema = z.object({ name: z.string().trim().min(1).max(60) });
|
|
3048
|
+
/** 배포 키 값 형식 — `sak_{appId 뒤}_{32 hex}` */
|
|
3049
|
+
const APP_DEPLOY_KEY_PATTERN = /^sak_[a-z0-9]{8,40}_[0-9a-f]{32}$/;
|
|
3050
|
+
const APP_DEPLOY_KEY_PREFIX = "sak_";
|
|
3051
|
+
//#endregion
|
|
3052
|
+
//#region src/schemas/auth.ts
|
|
3053
|
+
const storeRoleSchema = z.enum([
|
|
3054
|
+
"OWNER",
|
|
3055
|
+
"ADMIN",
|
|
3056
|
+
"STAFF"
|
|
3057
|
+
]);
|
|
3058
|
+
const myStoreSchema = z.object({
|
|
3059
|
+
storeId: z.string(),
|
|
3060
|
+
storeName: z.string(),
|
|
3061
|
+
storeCode: z.string(),
|
|
3062
|
+
role: storeRoleSchema,
|
|
3063
|
+
status: z.enum([
|
|
3064
|
+
"ACTIVE",
|
|
3065
|
+
"SUSPENDED",
|
|
3066
|
+
"CLOSED"
|
|
3067
|
+
])
|
|
3068
|
+
});
|
|
3069
|
+
const myAccountSchema = z.object({
|
|
3070
|
+
userId: z.string(),
|
|
3071
|
+
email: z.email(),
|
|
3072
|
+
name: z.string(),
|
|
3073
|
+
storeCount: z.number().int(),
|
|
3074
|
+
createdAt: z.string()
|
|
3075
|
+
});
|
|
3076
|
+
//#endregion
|
|
3077
|
+
//#region src/schemas/category.ts
|
|
3078
|
+
const categoryProductSortSchema = z.enum([
|
|
3079
|
+
"RECENT",
|
|
3080
|
+
"POPULAR",
|
|
3081
|
+
"PRICE_LOW",
|
|
3082
|
+
"PRICE_HIGH",
|
|
3083
|
+
"NAME"
|
|
3084
|
+
]);
|
|
3085
|
+
const categorySchema = z.object({
|
|
3086
|
+
categoryId: z.string(),
|
|
3087
|
+
storeId: z.string(),
|
|
3088
|
+
name: z.string(),
|
|
3089
|
+
parentId: z.string().nullable(),
|
|
3090
|
+
path: z.string(),
|
|
3091
|
+
depth: z.number().int().min(1).max(4),
|
|
3092
|
+
leaf: z.boolean(),
|
|
3093
|
+
sortOrder: z.number().int(),
|
|
3094
|
+
visible: z.boolean(),
|
|
3095
|
+
showPc: z.boolean(),
|
|
3096
|
+
showMobile: z.boolean(),
|
|
3097
|
+
productSort: categoryProductSortSchema,
|
|
3098
|
+
metaTitle: z.string().nullable(),
|
|
3099
|
+
metaDescription: z.string().nullable(),
|
|
3100
|
+
metaKeywords: z.string().nullable(),
|
|
3101
|
+
flowId: z.string().nullable().optional().describe("주문 흐름(이슈 #115) — 맨 아래 카테고리에만 있다. 흐름을 지정하지 않은 상품이 쓴다. null이면 기본 흐름"),
|
|
3102
|
+
createdAt: z.string(),
|
|
3103
|
+
updatedAt: z.string()
|
|
3104
|
+
});
|
|
3105
|
+
const categoryTreeNodeSchema = categorySchema.extend({ children: z.lazy(() => z.array(categoryTreeNodeSchema)) });
|
|
3106
|
+
const categoryWithPathSchema = categorySchema.extend({
|
|
3107
|
+
fullPath: z.string(),
|
|
3108
|
+
ancestors: z.array(categorySchema),
|
|
3109
|
+
childCount: z.number().int()
|
|
3110
|
+
});
|
|
3111
|
+
const categorySearchResultSchema = categorySchema.extend({
|
|
3112
|
+
fullPath: z.string(),
|
|
3113
|
+
ancestors: z.array(categorySchema)
|
|
3114
|
+
});
|
|
3115
|
+
const categoryAttributeSchema = z.object({
|
|
3116
|
+
attributeId: z.string(),
|
|
3117
|
+
name: z.string(),
|
|
3118
|
+
required: z.boolean(),
|
|
3119
|
+
inputType: z.enum([
|
|
3120
|
+
"SELECT",
|
|
3121
|
+
"MULTI_SELECT",
|
|
3122
|
+
"TEXT",
|
|
3123
|
+
"NUMBER"
|
|
3124
|
+
]),
|
|
3125
|
+
unit: z.string().nullable(),
|
|
3126
|
+
allowedValues: z.array(z.string()).optional()
|
|
3127
|
+
});
|
|
3128
|
+
const categoryCreateRequestSchema = z.object({
|
|
3129
|
+
name: z.string().min(1).max(100),
|
|
3130
|
+
parentId: z.string().nullable().optional(),
|
|
3131
|
+
sortOrder: z.number().int().optional(),
|
|
3132
|
+
visible: z.boolean().optional(),
|
|
3133
|
+
showPc: z.boolean().optional(),
|
|
3134
|
+
showMobile: z.boolean().optional(),
|
|
3135
|
+
productSort: categoryProductSortSchema.optional(),
|
|
3136
|
+
metaTitle: z.string().max(100).nullable().optional(),
|
|
3137
|
+
metaDescription: z.string().max(300).nullable().optional(),
|
|
3138
|
+
metaKeywords: z.string().max(200).nullable().optional()
|
|
3139
|
+
});
|
|
3140
|
+
const categoryPatchRequestSchema = z.object({
|
|
3141
|
+
name: z.string().min(1).max(100).optional(),
|
|
3142
|
+
parentId: z.string().nullable().optional(),
|
|
3143
|
+
sortOrder: z.number().int().optional(),
|
|
3144
|
+
visible: z.boolean().optional(),
|
|
3145
|
+
showPc: z.boolean().optional(),
|
|
3146
|
+
showMobile: z.boolean().optional(),
|
|
3147
|
+
productSort: categoryProductSortSchema.optional(),
|
|
3148
|
+
metaTitle: z.string().max(100).nullable().optional(),
|
|
3149
|
+
metaDescription: z.string().max(300).nullable().optional(),
|
|
3150
|
+
metaKeywords: z.string().max(200).nullable().optional(),
|
|
3151
|
+
flowId: z.string().nullable().optional().describe("주문 흐름(이슈 #115) — 게시한 흐름 id, null이면 기본 흐름. 맨 아래 카테고리에만 지정한다(400 `CATEGORY_NOT_LEAF`). 게시하지 않은 흐름은 409 `FLOW_NOT_PUBLISHED`")
|
|
3152
|
+
});
|
|
3153
|
+
const categoryDeleteStrategySchema = z.enum(["reparent", "cascade"]);
|
|
3154
|
+
/**
|
|
3155
|
+
* `GET /v1/categories` 쿼리 — OpenAPI 문서용 형태(값은 쿼리 문자열). 응답 형태가 쿼리로 갈린다:
|
|
3156
|
+
* `q`가 있으면 검색 결과(`CategorySearchResult[]`), 아니면 `tree=true`일 때 트리(`CategoryTreeNode[]`),
|
|
3157
|
+
* 둘 다 아니면 목록(`Category[]`)이다.
|
|
3158
|
+
*/
|
|
3159
|
+
const categoryListQuerySchema = z.object({
|
|
3160
|
+
parentId: z.string().optional().describe("이 카테고리의 직계 자식만. 생략하면 최상위 카테고리. `flat=true`면 무시"),
|
|
3161
|
+
flat: z.enum(["true", "false"]).optional().describe("`true`면 전체 카테고리를 깊이·순서대로 평탄화"),
|
|
3162
|
+
tree: z.enum(["true", "false"]).optional().describe("`true`면 최상위부터 `children`을 채운 트리(`CategoryTreeNode[]`)를 돌려준다"),
|
|
3163
|
+
q: z.string().optional().describe("이름 검색(포함, 대소문자 무시). 주면 `fullPath`·`ancestors`가 붙은 검색 결과를 돌려준다")
|
|
3164
|
+
});
|
|
3165
|
+
/** `DELETE /v1/categories/{categoryId}` 쿼리 — OpenAPI 문서용 형태 */
|
|
3166
|
+
const categoryDeleteQuerySchema = z.object({ strategy: categoryDeleteStrategySchema.optional().describe("하위 카테고리·연결 상품 처리 방식. 생략하면 둘 중 하나라도 있을 때 409 `CATEGORY_NOT_EMPTY`다. `reparent`는 직계 자식을 삭제할 카테고리의 부모로 옮기고, `cascade`는 서브트리를 삭제하고 연결 상품을 미분류로 둔다") });
|
|
3167
|
+
//#endregion
|
|
3168
|
+
//#region src/schemas/collection.ts
|
|
3169
|
+
/**
|
|
3170
|
+
* 컬렉션 (이슈 #83) — 상품을 어디에 보여 줄지 정하는 묶음이다. 카테고리는 상품이 무엇인지를 정하고 상품당 하나지만, 컬렉션은
|
|
3171
|
+
* 다대다다. 이벤트·기획전은 기간이 있는 직접 선택 컬렉션이다. 권한은 카테고리와 같다(`category:r`·`category:rw`).
|
|
3172
|
+
*
|
|
3173
|
+
* 유형은 만들 때 정하고 바꿀 수 없다. 직접 선택(`MANUAL`)은 셀러가 상품을 담고 순서를 정하고, 조건 자동(`AUTOMATIC`)은 조건(`rules`)을
|
|
3174
|
+
* 모두 만족하는 판매 중 상품을 조회할 때마다 계산한다(결과를 저장하지 않는다). 응답의 `type`은 값이 늘 수 있어 문자열이다.
|
|
3175
|
+
*/
|
|
3176
|
+
/** 상점당 컬렉션 최대 개수 — 넘으면 409 `COLLECTION_LIMIT_EXCEEDED` */
|
|
3177
|
+
const MAX_COLLECTIONS = 200;
|
|
3178
|
+
/** 직접 선택 컬렉션 하나에 담는 상품 최대 개수 — 넘으면 409 `COLLECTION_PRODUCT_LIMIT_EXCEEDED` */
|
|
3179
|
+
const MAX_COLLECTION_PRODUCTS = 1e3;
|
|
3180
|
+
/** 상품 뒤에 붙이기(`POST /v1/collections/{id}/products`) 한 번에 보내는 상품 최대 개수 */
|
|
3181
|
+
const MAX_COLLECTION_PRODUCTS_PER_ADD = 500;
|
|
3182
|
+
/** 제목 최대 글자 수 */
|
|
3183
|
+
const MAX_COLLECTION_TITLE = 60;
|
|
3184
|
+
/** 설명 최대 글자 수 */
|
|
2229
3185
|
const MAX_COLLECTION_DESCRIPTION = 500;
|
|
2230
3186
|
/** 주소(slug) 형식 — 영문 소문자·숫자·하이픈, 첫 글자는 영문 소문자나 숫자, 64자까지 */
|
|
2231
3187
|
const COLLECTION_SLUG_PATTERN = /^[a-z0-9][a-z0-9-]{0,63}$/;
|
|
@@ -4160,8 +5116,14 @@ const webhookDeliveryErrorCodeSchema = z.enum([
|
|
|
4160
5116
|
"CONNECTION_FAILED",
|
|
4161
5117
|
"BLOCKED_ADDRESS",
|
|
4162
5118
|
"NON_2XX",
|
|
4163
|
-
"WEBHOOK_SUSPENDED"
|
|
4164
|
-
|
|
5119
|
+
"WEBHOOK_SUSPENDED",
|
|
5120
|
+
"APP_ERROR",
|
|
5121
|
+
"APP_LIMIT_EXCEEDED",
|
|
5122
|
+
"APP_REJECTED",
|
|
5123
|
+
"APP_UNCONFIGURED",
|
|
5124
|
+
"APP_INACTIVE",
|
|
5125
|
+
"APP_RUNNER_UNAVAILABLE"
|
|
5126
|
+
]).describe("전송 실패 사유. `TIMEOUT`=10초 안에 응답 없음, `CONNECTION_FAILED`=연결 실패(DNS·TLS 포함), `BLOCKED_ADDRESS`=사설·루프백 등 전송이 허용되지 않는 주소, `NON_2XX`=2xx가 아닌 응답(리다이렉트 포함), `WEBHOOK_SUSPENDED`=구독 정지로 대기 중인 전송이 종료됨. 설치 앱(#22): `APP_ERROR`=앱 코드 예외, `APP_LIMIT_EXCEEDED`=CPU·요청·시간 한도 초과, `APP_REJECTED`=앱·버전 정지 등으로 거부, `APP_UNCONFIGURED`=필수 설정이 비어 보내지 않음, `APP_INACTIVE`=설치가 제거됨, `APP_RUNNER_UNAVAILABLE`=실행 환경 호출 실패");
|
|
4165
5127
|
/**
|
|
4166
5128
|
* 실패 원인 분류의 **현재** 값. 응답 스키마(`webhookFailureReasonSchema`)는 문자열로 열려 있다 —
|
|
4167
5129
|
* 서버가 값을 더해도 이전 SDK가 목록 파싱에 실패하지 않게 한다(택배 추적 `stage`와 같은 이유).
|
|
@@ -4423,8 +5385,10 @@ const liveGateConditionCodeSchema = z.enum([
|
|
|
4423
5385
|
"SANDBOX_PAYMENT",
|
|
4424
5386
|
"SANDBOX_REFUND",
|
|
4425
5387
|
"LIVE_CONNECTION",
|
|
4426
|
-
"PLATFORM_LIVE_ENABLED"
|
|
4427
|
-
|
|
5388
|
+
"PLATFORM_LIVE_ENABLED",
|
|
5389
|
+
"NOT_TEMPLATE_STORE",
|
|
5390
|
+
"NOT_DEVELOPMENT_STORE"
|
|
5391
|
+
]).describe("라이브 개방 조건. `TEST_CONNECTION`=TEST 세트 연결 테스트 성공, `SANDBOX_PAYMENT`=현재 TEST 세트로 샌드박스 결제 1건 승인, `SANDBOX_REFUND`=그 샌드박스 결제의 환불 1건 성공, `LIVE_CONNECTION`=LIVE 세트 연결 테스트 성공, `PLATFORM_LIVE_ENABLED`=플랫폼이 실결제를 열어 둠, `NOT_TEMPLATE_STORE`=템플릿 상점이 아님(템플릿 상점에만 싣고 늘 미충족), `NOT_DEVELOPMENT_STORE`=앱 개발 상점이 아님(앱 개발 상점에만 싣고 늘 미충족)");
|
|
4428
5392
|
const liveGateConditionSchema = z.object({
|
|
4429
5393
|
code: liveGateConditionCodeSchema,
|
|
4430
5394
|
met: z.boolean().describe("충족 여부"),
|
|
@@ -5886,6 +6850,8 @@ function storefrontEditPreviewLabelOf(siteId) {
|
|
|
5886
6850
|
const storefrontSlugSchema = z.string().regex(STOREFRONT_SLUG_PATTERN, "소문자·숫자·하이픈 3~30자여야 하고 하이픈으로 시작하거나 끝날 수 없습니다").refine((slug) => !slug.includes("--"), "하이픈을 연달아 쓸 수 없습니다").refine((slug) => !isReservedStorefrontSlug(slug), "플랫폼이 쓰는 이름이라 쓸 수 없습니다").describe("주소 슬러그. `{slug}.{호스팅 도메인}`이 사이트 주소가 된다");
|
|
5887
6851
|
/** 바꾼 슬러그를 다른 사이트가 가져가지 못하게 막는 기간(일). 보류 동안 옛 주소는 새 주소로 301한다 */
|
|
5888
6852
|
const STOREFRONT_SLUG_HOLD_DAYS = 90;
|
|
6853
|
+
/** 닫은 사이트(호스팅 끄기·상점 폐쇄)의 주소 보류·보관 기간(일, 2026-10-07). 지나면 주소가 풀리고 사이트가 지워진다 */
|
|
6854
|
+
const STOREFRONT_CLOSED_SITE_HOLD_DAYS = 30;
|
|
5889
6855
|
/** 슬러그 변경 한도 — 이 기간(일) 안에 */
|
|
5890
6856
|
const STOREFRONT_SLUG_CHANGE_WINDOW_DAYS = 30;
|
|
5891
6857
|
/** 슬러그 변경 한도 — 횟수(상점 단위. 끄고 다시 만들어도 이어서 센다) */
|
|
@@ -5902,7 +6868,40 @@ const storefrontTemplateSchema = z.object({
|
|
|
5902
6868
|
isDefault: z.boolean().describe("기본 템플릿인가 (상점 생성 시 자동 생성에 쓴다)"),
|
|
5903
6869
|
tags: z.array(z.string()).default([]).describe("템플릿 특징 태그 (예: `후기 강조`). 목록 카드에 보인다. 옛 서버 응답은 빈 배열로 읽는다"),
|
|
5904
6870
|
supportsCheckout: z.boolean().default(true).describe("주문을 받는 템플릿인가. false는 가격·문의 중심의 카탈로그형이다. 옛 서버 응답은 true로 읽는다"),
|
|
5905
|
-
previewUrl: z.string().nullable().default(null).describe("
|
|
6871
|
+
previewUrl: z.string().nullable().default(null).describe("미리보기 사이트 주소. 공식 템플릿은 연결된 쇼케이스, 템플릿 상점의 템플릿은 그 상점의 사이트가 공개 중일 때만 싣고 그 밖에는 null"),
|
|
6872
|
+
source: z.enum(["CATALOG", "STORE"]).default("CATALOG").describe("`CATALOG` 공식 템플릿 · `STORE` 템플릿 상점의 템플릿(사이트를 만들 때 `copyContent`로 콘텐츠를 복사할 수 있다). 옛 서버 응답은 `CATALOG`로 읽는다")
|
|
6873
|
+
});
|
|
6874
|
+
/**
|
|
6875
|
+
* 템플릿 상점의 템플릿 정보 — 상점을 만들 때 템플릿 상점으로 고른 상점만 있다. 발행한 스토어프론트가 템플릿이 되고, 이 이름·소개·
|
|
6876
|
+
* 썸네일로 모든 셀러의 템플릿 목록에 보인다
|
|
6877
|
+
*/
|
|
6878
|
+
const storeTemplateSchema = z.object({
|
|
6879
|
+
templateId: z.string().describe("템플릿 id. 다른 상점이 사이트를 만들 때 이 값을 쓴다"),
|
|
6880
|
+
name: z.string().describe("템플릿 이름"),
|
|
6881
|
+
description: z.string().describe("템플릿 소개"),
|
|
6882
|
+
thumbnailUrl: z.string().nullable().describe("썸네일 이미지 주소. 없으면 null"),
|
|
6883
|
+
status: z.enum([
|
|
6884
|
+
"DRAFT",
|
|
6885
|
+
"ACTIVE",
|
|
6886
|
+
"RETIRED"
|
|
6887
|
+
]).describe("`DRAFT` 아직 편집한 스토어프론트를 발행하지 않아 목록에 보이지 않음 · `ACTIVE` 목록에 보임 · `RETIRED` 상점을 닫아 내려감"),
|
|
6888
|
+
version: z.string().nullable().describe("목록에 보이는 템플릿 버전. 발행한 스토어프론트 버전에서 정해진다. `DRAFT`면 null")
|
|
6889
|
+
});
|
|
6890
|
+
/**
|
|
6891
|
+
* 템플릿 상점의 고정 사업자 정보 필드 — 상점 수정에서 다른 값을 보내면 409 `TEMPLATE_STORE_BUSINESS_LOCKED`다(값은 플랫폼이 정한다)
|
|
6892
|
+
*/
|
|
6893
|
+
const TEMPLATE_STORE_LOCKED_BUSINESS_FIELDS = [
|
|
6894
|
+
"businessName",
|
|
6895
|
+
"representativeName",
|
|
6896
|
+
"businessNumber",
|
|
6897
|
+
"mailOrderSalesNumber",
|
|
6898
|
+
"customerCenterPhone",
|
|
6899
|
+
"customerCenterEmail"
|
|
6900
|
+
];
|
|
6901
|
+
const updateStoreTemplateRequestSchema = z.object({
|
|
6902
|
+
name: z.string().trim().min(1).max(40).describe("템플릿 이름(1~40자)"),
|
|
6903
|
+
description: z.string().trim().max(200).describe("템플릿 소개(200자까지)"),
|
|
6904
|
+
thumbnailUrl: z.url({ protocol: /^https$/ }).max(2048).nullable().describe("썸네일 이미지 주소(https). 이미지 업로드(`POST /v1/uploads`)로 받은 주소를 쓴다. 없으면 null")
|
|
5906
6905
|
});
|
|
5907
6906
|
/**
|
|
5908
6907
|
* 버전 상태 — 템플릿 버전(`source: TEMPLATE`)은 템플릿 산출물을 그대로 쓰므로 만들 때 곧바로 `READY`다. 편집 버전
|
|
@@ -5970,6 +6969,12 @@ const storefrontSuspensionSchema = z.object({
|
|
|
5970
6969
|
reason: z.string().nullable().describe("정지 사유. 운영자가 적은 안내이고 없으면 null"),
|
|
5971
6970
|
suspendedAt: z.string().nullable().describe("정지한 시각 (ISO 8601)")
|
|
5972
6971
|
});
|
|
6972
|
+
/** 사이트 첫 화면 스냅샷 — 발행·되돌리기로 발행 버전이 바뀌면 반영된 뒤 다시 찍는다(데스크톱 1280×800) */
|
|
6973
|
+
const storefrontSiteSnapshotSchema = z.object({
|
|
6974
|
+
url: z.string().describe("스냅샷 이미지 주소 (jpeg)"),
|
|
6975
|
+
versionId: z.string().nullable().describe("스냅샷이 보여 주는 발행 버전"),
|
|
6976
|
+
capturedAt: z.string().nullable().describe("찍은 시각 (ISO 8601)")
|
|
6977
|
+
});
|
|
5973
6978
|
const storefrontSiteSchema = z.object({
|
|
5974
6979
|
siteId: z.string(),
|
|
5975
6980
|
slug: z.string().describe("주소 슬러그"),
|
|
@@ -5985,6 +6990,7 @@ const storefrontSiteSchema = z.object({
|
|
|
5985
6990
|
editPreview: storefrontEditPreviewSchema,
|
|
5986
6991
|
slugChangesRemaining: z.number().int().min(0).describe(`최근 30일 기준 남은 슬러그 변경 횟수`),
|
|
5987
6992
|
suspension: storefrontSuspensionSchema.nullable().optional().describe("정지 정보. `status`가 `SUSPENDED`일 때만 있고 그 밖은 null"),
|
|
6993
|
+
snapshot: storefrontSiteSnapshotSchema.nullable().optional().describe("사이트 첫 화면 스냅샷. 발행한 버전이 반영되면 찍고, 아직 찍지 않았으면 null"),
|
|
5988
6994
|
createdAt: z.string(),
|
|
5989
6995
|
updatedAt: z.string()
|
|
5990
6996
|
});
|
|
@@ -6031,7 +7037,7 @@ const storefrontDeploymentLogSchema = z.object({
|
|
|
6031
7037
|
"WARN",
|
|
6032
7038
|
"ERROR"
|
|
6033
7039
|
]),
|
|
6034
|
-
code: z.string().describe("기록 종류 (`SITE_CREATED`·`ADDRESS_RESERVED`·`WAITING`·`SYNCING`·`VERSION_UPLOADED`·`SYNC_FAILED`·`RETRY_SCHEDULED`·`PUBLISHED`)"),
|
|
7040
|
+
code: z.string().describe("기록 종류 (`SITE_CREATED`·`ADDRESS_RESERVED`·`WAITING`·`SYNCING`·`VERSION_UPLOADED`·`SYNC_FAILED`·`RETRY_SCHEDULED`·`PUBLISHED`, 템플릿 상점의 템플릿은 `BUILDING`·`BUILD_FAILED`·`CONTENT_COPYING`·`CONTENT_COPIED`·`CONTENT_COPY_FAILED`)"),
|
|
6035
7041
|
message: z.string().describe("사람이 읽는 문장")
|
|
6036
7042
|
});
|
|
6037
7043
|
const storefrontDeploymentSchema = z.object({
|
|
@@ -6052,7 +7058,8 @@ const storefrontDeploymentSchema = z.object({
|
|
|
6052
7058
|
});
|
|
6053
7059
|
const createStorefrontSiteRequestSchema = z.object({
|
|
6054
7060
|
templateId: z.string().min(1).max(64).describe("템플릿 id"),
|
|
6055
|
-
slug: storefrontSlugSchema.optional().describe("주소 슬러그. 생략하면 상점 코드")
|
|
7061
|
+
slug: storefrontSlugSchema.optional().describe("주소 슬러그. 생략하면 상점 코드"),
|
|
7062
|
+
copyContent: z.boolean().optional().describe("템플릿 상점의 템플릿일 때만 — true면 템플릿 상점의 콘텐츠(레이아웃, 상품·카테고리·컬렉션·주문 흐름·배송·환불 정책)를 이 상점으로 복사한다. 쿠폰·적립금·리뷰는 복사하지 않는다. 생략하거나 false면 레이아웃 콘텐츠를 비우고 빌드한다")
|
|
6056
7063
|
});
|
|
6057
7064
|
/** @deprecated 템플릿 변경은 지원하지 않는다(항상 409 `TEMPLATE_CHANGE_NOT_SUPPORTED`). 호환을 위해 스키마만 남겼다 */
|
|
6058
7065
|
const changeStorefrontTemplateRequestSchema = z.object({
|
|
@@ -7414,6 +8421,20 @@ get: (params) => storeHttp.request("GET", "/sales-report", salesReportSchema, {
|
|
|
7414
8421
|
*/
|
|
7415
8422
|
verify: (provider, body) => storeHttp.request("POST", path`/store/payment-providers/${pgProviderSchema.parse(provider)}/verify`, paymentProviderVerificationSchema, { body: verifyPaymentProviderRequestSchema.parse(body ?? {}) })
|
|
7416
8423
|
},
|
|
8424
|
+
/** 설치 앱(#22, 매뉴얼 「앱 설치와 관리 › 관리 API」) — 설치·설정·제거·세션은 상점 플랫폼 OWNER·ADMIN만 */
|
|
8425
|
+
apps: { installations: {
|
|
8426
|
+
/** 설치 결과(accounts 동의 화면의 1회용 JWT)를 내 설치를 만든다 */
|
|
8427
|
+
install: (result) => storeHttp.request("POST", "/apps/installations", appInstallationSchema, { body: appInstallRequestSchema.parse({ result }) }),
|
|
8428
|
+
list: () => storeHttp.request("GET", "/apps/installations", z.array(appInstallationSchema)),
|
|
8429
|
+
get: (installationId) => storeHttp.request("GET", path`/apps/installations/${installationId}`, appInstallationSchema),
|
|
8430
|
+
/** 비밀 필드를 생략하면 현재 값을 유지한다. null은 비운다 */
|
|
8431
|
+
updateSettings: (installationId, body) => storeHttp.request("PUT", path`/apps/installations/${installationId}/settings`, appInstallationSchema, { body: appInstallationSettingsUpdateSchema.parse(body) }),
|
|
8432
|
+
remove: (installationId) => storeHttp.requestVoid("DELETE", path`/apps/installations/${installationId}`),
|
|
8433
|
+
/** 앱 화면 iframe에 넘길 세션 토큰(5분) */
|
|
8434
|
+
session: (installationId) => storeHttp.request("POST", path`/apps/installations/${installationId}/session`, appSessionSchema)
|
|
8435
|
+
} },
|
|
8436
|
+
/** 앱 설치 토큰 전용 — 자기 설치 */
|
|
8437
|
+
app: { installation: () => storeHttp.request("GET", "/app/installation", appInstallationSelfSchema) },
|
|
7417
8438
|
webhooks: {
|
|
7418
8439
|
list: () => storeHttp.request("GET", "/webhooks", z.array(webhookSchema)),
|
|
7419
8440
|
/** 서명 시크릿 평문은 이 응답에서만 노출된다 */
|
|
@@ -7514,6 +8535,10 @@ list: (params) => storeHttp.request("GET", "/audit-logs", auditLogPageSchema, {
|
|
|
7514
8535
|
storefront: {
|
|
7515
8536
|
/** `GET /v1/storefront/templates` — 템플릿 카탈로그 */
|
|
7516
8537
|
listTemplates: () => storeHttp.request("GET", "/storefront/templates", z.array(storefrontTemplateSchema)),
|
|
8538
|
+
/** `GET /v1/storefront/template` — 템플릿 상점의 템플릿 정보(#123). 템플릿 상점이 아니면 404 `TEMPLATE_STORE_NOT_FOUND` */
|
|
8539
|
+
getStoreTemplate: () => storeHttp.request("GET", "/storefront/template", storeTemplateSchema),
|
|
8540
|
+
/** `PUT /v1/storefront/template` — 템플릿 이름·소개·썸네일을 바꾼다(템플릿 상점만) */
|
|
8541
|
+
updateStoreTemplate: (body) => storeHttp.request("PUT", "/storefront/template", storeTemplateSchema, { body: updateStoreTemplateRequestSchema.parse(body) }),
|
|
7517
8542
|
/** `GET /v1/storefront/site` — 스위치 상태와 현재 사이트(없으면 `site: null`) */
|
|
7518
8543
|
get: () => storeHttp.request("GET", "/storefront/site", storefrontHostingSchema),
|
|
7519
8544
|
/** `GET /v1/storefront/slug-availability?slug=` — 슬러그를 쓸 수 있는가 */
|
|
@@ -7755,116 +8780,148 @@ async function exchangeAuthorizationCode(options) {
|
|
|
7755
8780
|
}
|
|
7756
8781
|
/** refresh grant (rotation — 응답의 새 refresh_token으로 반드시 교체 저장할 것) */
|
|
7757
8782
|
async function refreshUserToken(options) {
|
|
7758
|
-
return tokenRequest(options.tokenUrl, {
|
|
7759
|
-
grant_type: "refresh_token",
|
|
7760
|
-
refresh_token: options.refreshToken,
|
|
7761
|
-
client_id: options.clientId,
|
|
7762
|
-
...options.resource ? { resource: options.resource } : {}
|
|
7763
|
-
}, options.fetch);
|
|
7764
|
-
}
|
|
7765
|
-
const customerDelegationRequestSchema = z.object({
|
|
7766
|
-
uid: z.string().regex(/^[A-Za-z0-9._:@|-]{1,128}$/).optional().describe("셀러 자체 인증의 사용자 id(1~128자, 영문·숫자·`._:@|-`). 처음 보는 id면 구매자를 만든다"),
|
|
7767
|
-
email: z.email().max(254).optional().describe("uid가 없으면 이메일로 지목한다. uid와 함께 주면 처음 만들 때의 이메일이다"),
|
|
7768
|
-
name: z.string().max(50).optional().describe("처음 만들 때의 이름"),
|
|
7769
|
-
authenticatedAt: z.iso.datetime({ offset: true }).optional().describe("사용자가 셀러 서비스에서 실제로 로그인한 시각(uid 위임만). 주면 구매자 토큰의 인증 시각이 된다")
|
|
7770
|
-
}).refine((value) => Boolean(value.uid || value.email), {
|
|
7771
|
-
message: "uid 또는 email이 필요합니다",
|
|
7772
|
-
path: ["uid"]
|
|
7773
|
-
}).describe("구매자 위임 요청");
|
|
7774
|
-
const customerDelegationSchema = z.object({
|
|
7775
|
-
accessToken: z.string().describe("구매자 액세스 토큰(스토어프론트 API Bearer)"),
|
|
7776
|
-
tokenType: z.literal("Bearer"),
|
|
7777
|
-
expiresIn: z.number().int().describe("수명(초)"),
|
|
7778
|
-
memberId: z.string()
|
|
7779
|
-
}).describe("구매자 위임 결과");
|
|
7780
|
-
//#endregion
|
|
7781
|
-
//#region src/schemas/
|
|
7782
|
-
/**
|
|
7783
|
-
*
|
|
7784
|
-
*
|
|
7785
|
-
*
|
|
7786
|
-
|
|
7787
|
-
|
|
7788
|
-
|
|
7789
|
-
|
|
7790
|
-
|
|
7791
|
-
|
|
7792
|
-
|
|
7793
|
-
|
|
7794
|
-
|
|
7795
|
-
|
|
7796
|
-
|
|
7797
|
-
|
|
7798
|
-
|
|
7799
|
-
|
|
7800
|
-
|
|
7801
|
-
|
|
7802
|
-
|
|
7803
|
-
|
|
7804
|
-
|
|
7805
|
-
|
|
7806
|
-
|
|
7807
|
-
|
|
7808
|
-
}
|
|
7809
|
-
|
|
7810
|
-
/**
|
|
7811
|
-
|
|
7812
|
-
|
|
7813
|
-
|
|
7814
|
-
|
|
7815
|
-
|
|
7816
|
-
|
|
7817
|
-
|
|
7818
|
-
|
|
7819
|
-
|
|
7820
|
-
|
|
7821
|
-
|
|
7822
|
-
|
|
7823
|
-
|
|
7824
|
-
|
|
7825
|
-
|
|
7826
|
-
|
|
7827
|
-
|
|
7828
|
-
|
|
7829
|
-
|
|
7830
|
-
|
|
7831
|
-
|
|
7832
|
-
|
|
7833
|
-
|
|
7834
|
-
|
|
7835
|
-
|
|
7836
|
-
|
|
7837
|
-
|
|
7838
|
-
|
|
7839
|
-
|
|
7840
|
-
|
|
7841
|
-
|
|
7842
|
-
|
|
7843
|
-
|
|
7844
|
-
|
|
7845
|
-
|
|
7846
|
-
|
|
7847
|
-
|
|
7848
|
-
|
|
7849
|
-
|
|
7850
|
-
|
|
7851
|
-
|
|
7852
|
-
|
|
7853
|
-
|
|
7854
|
-
|
|
7855
|
-
|
|
7856
|
-
|
|
7857
|
-
|
|
7858
|
-
|
|
7859
|
-
|
|
7860
|
-
|
|
7861
|
-
|
|
7862
|
-
|
|
7863
|
-
|
|
7864
|
-
|
|
7865
|
-
|
|
7866
|
-
|
|
7867
|
-
|
|
8783
|
+
return tokenRequest(options.tokenUrl, {
|
|
8784
|
+
grant_type: "refresh_token",
|
|
8785
|
+
refresh_token: options.refreshToken,
|
|
8786
|
+
client_id: options.clientId,
|
|
8787
|
+
...options.resource ? { resource: options.resource } : {}
|
|
8788
|
+
}, options.fetch);
|
|
8789
|
+
}
|
|
8790
|
+
const customerDelegationRequestSchema = z.object({
|
|
8791
|
+
uid: z.string().regex(/^[A-Za-z0-9._:@|-]{1,128}$/).optional().describe("셀러 자체 인증의 사용자 id(1~128자, 영문·숫자·`._:@|-`). 처음 보는 id면 구매자를 만든다"),
|
|
8792
|
+
email: z.email().max(254).optional().describe("uid가 없으면 이메일로 지목한다. uid와 함께 주면 처음 만들 때의 이메일이다"),
|
|
8793
|
+
name: z.string().max(50).optional().describe("처음 만들 때의 이름"),
|
|
8794
|
+
authenticatedAt: z.iso.datetime({ offset: true }).optional().describe("사용자가 셀러 서비스에서 실제로 로그인한 시각(uid 위임만). 주면 구매자 토큰의 인증 시각이 된다")
|
|
8795
|
+
}).refine((value) => Boolean(value.uid || value.email), {
|
|
8796
|
+
message: "uid 또는 email이 필요합니다",
|
|
8797
|
+
path: ["uid"]
|
|
8798
|
+
}).describe("구매자 위임 요청");
|
|
8799
|
+
const customerDelegationSchema = z.object({
|
|
8800
|
+
accessToken: z.string().describe("구매자 액세스 토큰(스토어프론트 API Bearer)"),
|
|
8801
|
+
tokenType: z.literal("Bearer"),
|
|
8802
|
+
expiresIn: z.number().int().describe("수명(초)"),
|
|
8803
|
+
memberId: z.string()
|
|
8804
|
+
}).describe("구매자 위임 결과");
|
|
8805
|
+
//#endregion
|
|
8806
|
+
//#region src/schemas/starter.ts
|
|
8807
|
+
/**
|
|
8808
|
+
* 쇼핑몰 시작(starter, 이슈 #122) — 원격 MCP의 starter 절차가 쓰는 브랜드 브리프와 진행 상태.
|
|
8809
|
+
*
|
|
8810
|
+
* 공개 OpenAPI 문서에는 싣지 않는다(절차를 운영 테스트로 다듬는 동안 계약이 바뀔 수 있다). 소비자는 sayren MCP 도구다.
|
|
8811
|
+
*/
|
|
8812
|
+
/** 판매 품목 — 빠른 시작(#70) 답과 템플릿 카탈로그 `recommendFor`가 같은 코드를 쓴다 */
|
|
8813
|
+
const storeIndustrySchema = z.enum([
|
|
8814
|
+
"FASHION",
|
|
8815
|
+
"FOOD",
|
|
8816
|
+
"DIGITAL",
|
|
8817
|
+
"OTHER"
|
|
8818
|
+
]);
|
|
8819
|
+
/** 상품 자료 — 인터뷰의 「상품 자료」 답. 상품 등록 단계의 방법을 고른다 */
|
|
8820
|
+
const brandBriefProductSourceSchema = z.enum([
|
|
8821
|
+
"SPREADSHEET",
|
|
8822
|
+
"EXISTING_STORE",
|
|
8823
|
+
"PHOTOS",
|
|
8824
|
+
"NONE"
|
|
8825
|
+
]);
|
|
8826
|
+
const BRAND_BRIEF_MAX_REFERENCES = 5;
|
|
8827
|
+
function isHttpUrl(value) {
|
|
8828
|
+
try {
|
|
8829
|
+
const { protocol } = new URL(value);
|
|
8830
|
+
return protocol === "https:" || protocol === "http:";
|
|
8831
|
+
} catch {
|
|
8832
|
+
return false;
|
|
8833
|
+
}
|
|
8834
|
+
}
|
|
8835
|
+
/** 참고 사이트 — 이미지는 저장하지 않고 주소와 에이전트가 쓴 레이아웃 요약만 둔다 */
|
|
8836
|
+
const brandBriefReferenceSchema = z.object({
|
|
8837
|
+
url: z.string().trim().max(2048).refine(isHttpUrl, { message: "http(s) 주소여야 합니다" }).describe("참고 사이트 주소"),
|
|
8838
|
+
summary: z.string().trim().min(1).max(1e3).describe("레이아웃 특징 요약 (헤더 형태·홈 섹션 순서·상세 구성 등)")
|
|
8839
|
+
});
|
|
8840
|
+
/** 브랜드 브리프 — starter 인터뷰 결과. 이후 단계(화면 설정·상품·다듬기)의 판단 근거다 */
|
|
8841
|
+
const brandBriefSchema = z.object({
|
|
8842
|
+
industry: storeIndustrySchema.nullable().describe("판매 품목"),
|
|
8843
|
+
mood: z.string().trim().max(200).nullable().describe("쇼핑몰 분위기 (예: 깔끔한, 따뜻한)"),
|
|
8844
|
+
pointColor: z.string().regex(/^#[0-9a-fA-F]{6}$/, "#RRGGBB 형식이어야 합니다").nullable().describe("대표 색 (`#RRGGBB`)"),
|
|
8845
|
+
references: z.array(brandBriefReferenceSchema).max(5),
|
|
8846
|
+
productSource: brandBriefProductSourceSchema.nullable().describe("상품 자료"),
|
|
8847
|
+
notes: z.string().trim().max(4e3).nullable().describe("인터뷰 메모 (타깃 고객, 판매 방식, 셀러가 강조한 요구)")
|
|
8848
|
+
});
|
|
8849
|
+
/** `PUT /v1/storefront/brand-brief` — 브리프 전체를 바꾼다. 생략한 항목은 비운다 */
|
|
8850
|
+
const putBrandBriefRequestSchema = z.object({
|
|
8851
|
+
industry: storeIndustrySchema.nullable().default(null),
|
|
8852
|
+
mood: z.string().trim().max(200).nullable().default(null),
|
|
8853
|
+
pointColor: z.string().regex(/^#[0-9a-fA-F]{6}$/, "#RRGGBB 형식이어야 합니다").nullable().default(null),
|
|
8854
|
+
references: z.array(brandBriefReferenceSchema).max(5).default([]),
|
|
8855
|
+
productSource: brandBriefProductSourceSchema.nullable().default(null),
|
|
8856
|
+
notes: z.string().trim().max(4e3).nullable().default(null)
|
|
8857
|
+
});
|
|
8858
|
+
/** `GET·PUT /v1/storefront/brand-brief` 응답 — 저장한 적이 없으면 `brief`·`updatedAt`이 null이다 */
|
|
8859
|
+
const brandBriefViewSchema = z.object({
|
|
8860
|
+
brief: brandBriefSchema.nullable(),
|
|
8861
|
+
updatedAt: z.string().nullable()
|
|
8862
|
+
});
|
|
8863
|
+
/**
|
|
8864
|
+
* starter 단계 — 서버 상태로 계산한다(에이전트 보고가 아니다). 앞 단계가 끝나지 않았으면 그 단계다.
|
|
8865
|
+
* `POLISH`(다듬기·구조 감사·구매 여정 점검)부터는 로컬 검사와 셀러 확인이 판정하므로 서버는 더 나누지 않는다.
|
|
8866
|
+
*/
|
|
8867
|
+
const STARTER_STAGES = [
|
|
8868
|
+
"INTERVIEW",
|
|
8869
|
+
"SITE",
|
|
8870
|
+
"STORE_INFO",
|
|
8871
|
+
"PRODUCTS",
|
|
8872
|
+
"SELLER_TASKS",
|
|
8873
|
+
"POLISH"
|
|
8874
|
+
];
|
|
8875
|
+
const starterStageSchema = z.enum(STARTER_STAGES);
|
|
8876
|
+
/** 셀러가 상점 플랫폼에서 직접 하는 항목 — 결제는 연결 여부만 본다(테스트·라이브를 가리지 않는다) */
|
|
8877
|
+
const STARTER_SELLER_TASKS = [
|
|
8878
|
+
"PAYMENT_CONNECTED",
|
|
8879
|
+
"BUSINESS_INFO",
|
|
8880
|
+
"SHIPPING_POLICY"
|
|
8881
|
+
];
|
|
8882
|
+
const starterSellerTaskSchema = z.enum(STARTER_SELLER_TASKS);
|
|
8883
|
+
/** 상점 기본 정보 항목 — 고객센터 번호만 필수이고 로고는 권장이다(셀러가 로고가 없다고 하면 만들지 않는다) */
|
|
8884
|
+
const starterStoreInfoFieldSchema = z.enum(["LOGO", "CUSTOMER_CENTER_PHONE"]);
|
|
8885
|
+
/** `GET /v1/storefront/starter` — starter 진행 상태 */
|
|
8886
|
+
const starterStatusSchema = z.object({
|
|
8887
|
+
stage: starterStageSchema,
|
|
8888
|
+
hostingEnabled: z.boolean().describe("새 호스팅 사이트를 만들 수 있는가"),
|
|
8889
|
+
defaultSiteUrl: z.string().nullable().describe("사이트를 만들면 쓰는 기본 주소(`https://{상점 코드}.{호스팅 도메인}`). 사이트가 있으면 null"),
|
|
8890
|
+
brief: z.object({
|
|
8891
|
+
saved: z.boolean(),
|
|
8892
|
+
updatedAt: z.string().nullable()
|
|
8893
|
+
}),
|
|
8894
|
+
site: z.object({
|
|
8895
|
+
siteId: z.string(),
|
|
8896
|
+
url: z.string(),
|
|
8897
|
+
templateId: z.string(),
|
|
8898
|
+
visibility: z.enum(["COMING_SOON", "PUBLIC"]),
|
|
8899
|
+
status: z.string(),
|
|
8900
|
+
synced: z.boolean().describe("발행 상태가 공개 주소에 반영됐는가"),
|
|
8901
|
+
publishedVersion: z.object({
|
|
8902
|
+
versionId: z.string(),
|
|
8903
|
+
templateVersion: z.string(),
|
|
8904
|
+
createdVia: z.string().nullable(),
|
|
8905
|
+
publishedAt: z.string().nullable()
|
|
8906
|
+
}).nullable()
|
|
8907
|
+
}).nullable(),
|
|
8908
|
+
storeInfo: z.object({
|
|
8909
|
+
done: z.boolean().describe("필수 항목(고객센터 번호)이 모두 있는가"),
|
|
8910
|
+
missing: z.array(starterStoreInfoFieldSchema).describe("비어 있는 필수 항목"),
|
|
8911
|
+
suggested: z.array(starterStoreInfoFieldSchema).describe("비어 있지만 완료를 막지 않는 항목(로고). 셀러가 원할 때만 채운다")
|
|
8912
|
+
}),
|
|
8913
|
+
products: z.object({ realCount: z.number().int().min(0).describe("샘플이 아닌 판매 중 상품 수") }),
|
|
8914
|
+
sellerTasks: z.array(z.object({
|
|
8915
|
+
key: starterSellerTaskSchema,
|
|
8916
|
+
done: z.boolean()
|
|
8917
|
+
})),
|
|
8918
|
+
checkout: z.object({
|
|
8919
|
+
available: z.boolean(),
|
|
8920
|
+
mode: z.enum(["TEST", "LIVE"]).nullable(),
|
|
8921
|
+
reason: z.enum(["PAYMENT_NOT_CONFIGURED", "PAYMENT_NOT_LIVE"]).nullable()
|
|
8922
|
+
}),
|
|
8923
|
+
orders: z.object({ count: z.number().int().min(0).describe("주문 수 (결제 모드를 가리지 않는다)") })
|
|
8924
|
+
});
|
|
7868
8925
|
//#endregion
|
|
7869
8926
|
//#region src/schemas/telemetry.ts
|
|
7870
8927
|
/**
|
|
@@ -7904,221 +8961,6 @@ const mcpToolEventSchema = z.object({
|
|
|
7904
8961
|
results: z.array(mcpRuleResultSchema).max(32).refine((results) => new Set(results.map((result) => result.ruleId)).size === results.length, { message: "같은 규칙 id를 두 번 실을 수 없습니다" }).describe("규칙별 통과·위반. 규칙 id는 중복될 수 없고 파일 경로와 위반 상세는 싣지 않는다")
|
|
7905
8962
|
});
|
|
7906
8963
|
//#endregion
|
|
7907
|
-
//#region src/schemas/webhook-event.ts
|
|
7908
|
-
const envelopeFields = {
|
|
7909
|
-
id: z.string().describe("이벤트 ID (`evt_…`, 테스트 전송은 `evt_test_…`). 중복 제거 키"),
|
|
7910
|
-
createdAt: z.string().describe("이벤트 발생 일시 (ISO 8601)"),
|
|
7911
|
-
storeId: z.string().describe("이벤트가 발생한 상점 ID"),
|
|
7912
|
-
sequence: z.number().int().positive().optional().describe("리소스별 순번. 같은 주문·클레임·문의·리뷰·상품의 이벤트마다 1씩 커진다. 도착 순서는 보장하지 않으므로 같은 리소스의 이벤트는 이 값으로 정렬한다. 이 필드가 생기기 전의 이벤트와 테스트 전송에는 없다")
|
|
7913
|
-
};
|
|
7914
|
-
const orderWebhookEventDataSchema = z.object({
|
|
7915
|
-
orderId: z.string().describe("주문 ID"),
|
|
7916
|
-
orderNo: z.string().nullable().optional().default(null).describe("주문번호(이슈 #107) — 숫자 14자리. 이 필드가 없는 이전 이벤트와 번호 체계 이전 주문은 null로 읽는다"),
|
|
7917
|
-
status: orderItemStatusSchema.describe("이벤트 발생 시점의 주문 대표 상태"),
|
|
7918
|
-
testPayment: z.boolean().optional().default(false).describe("테스트 결제(샌드박스 결제) 주문인가. true면 실제로 돈이 오가지 않았다. 이 필드가 없는 이전 이벤트는 false로 읽는다"),
|
|
7919
|
-
couponDiscountAmount: z.number().int().optional().default(0).describe("주문이 받은 상품·주문 쿠폰 할인 합계 (원). 즉시할인(`discountAmount`)과 별개다. 쿠폰이 없거나 이 필드가 없는 이전 이벤트는 0으로 읽는다"),
|
|
7920
|
-
pointAmount: z.number().int().optional().default(0).describe("주문에 쓴 적립금 (원, #93). 적립금을 쓰지 않았거나 이 필드가 없는 이전 이벤트는 0으로 읽는다"),
|
|
7921
|
-
orderItemId: z.string().optional().describe("이벤트를 만든 주문상품 ID. 배송 없는 상품 제공으로 보내는 `ORDER.DELIVERED`에만 실린다. 택배 배송완료와 그 밖의 주문 이벤트에는 없다"),
|
|
7922
|
-
fulfillmentSnapshot: orderItemFulfillmentSnapshotResponseSchema.optional().describe("이벤트를 만든 주문상품의 주문 시점 이행 스냅샷(주문상품 응답의 `fulfillmentSnapshot`과 같다, 이슈 #57). 배송 없는 상품의 제공 완료는 `ORDER.DELIVERED`로도 오고 `type`이 `MANUAL`이다. `orderItemId`가 있는 이벤트에만 실린다"),
|
|
7923
|
-
itemOptions: z.array(z.object({
|
|
7924
|
-
orderItemId: z.string(),
|
|
7925
|
-
optionSelections: z.array(optionSelectionSchema),
|
|
7926
|
-
customInputs: z.array(customInputValueSchema)
|
|
7927
|
-
})).optional().describe("추가 선택·직접 입력이 있는 주문상품의 주문 시점 선택값(이슈 #59, 주문상품 응답의 `optionSelections`·`customInputs`와 같다). `ORDER.PAID`에만 실리고 그런 주문상품이 없으면 없다")
|
|
7928
|
-
});
|
|
7929
|
-
/**
|
|
7930
|
-
* `ORDER.FULFILLED` — 배송 없는 주문상품(`MANUAL`)을 제공했다(이슈 #48). 셀러의 제공 처리
|
|
7931
|
-
* (`POST /v1/order-items/{orderItemId}/fulfill`)로 온다. 기존 구독자를 위해 같은 순간 `ORDER.DELIVERED`도 함께 보낸다.
|
|
7932
|
-
*/
|
|
7933
|
-
const orderFulfilledWebhookEventDataSchema = z.object({
|
|
7934
|
-
orderId: z.string().describe("주문 ID"),
|
|
7935
|
-
orderNo: z.string().nullable().optional().default(null).describe("주문번호(이슈 #107) — 숫자 14자리. 이 필드가 없는 이전 이벤트와 번호 체계 이전 주문은 null로 읽는다"),
|
|
7936
|
-
orderItemId: z.string().describe("제공한 주문상품 ID"),
|
|
7937
|
-
status: orderItemStatusSchema.describe("이벤트 발생 시점의 주문 대표 상태"),
|
|
7938
|
-
fulfillmentSnapshot: orderItemFulfillmentSnapshotResponseSchema.describe("제공한 주문상품의 주문 시점 이행 스냅샷(주문상품 응답의 `fulfillmentSnapshot`과 같다, 이슈 #57)"),
|
|
7939
|
-
fulfillment: z.object({ mode: z.string() }).optional().describe("**Deprecated** — 항상 `{ mode: \"MANUAL\" }`이다. 자동 제공을 없앴고(이슈 #48) store-sdk 0.19 이하 호환으로만 싣는다. 게시된 연동이 store-sdk 0.20 이상으로 넘어간 뒤 다음 릴리스에서 뺀다"),
|
|
7940
|
-
fulfilledAt: z.string().describe("제공 시각 (ISO 8601). 주문상품의 `deliveredAt`과 같다"),
|
|
7941
|
-
testPayment: z.boolean().describe("테스트 결제(샌드박스 결제) 주문인가. true면 실제로 돈이 오가지 않았다")
|
|
7942
|
-
});
|
|
7943
|
-
/**
|
|
7944
|
-
* `ORDER.CANCELED` — 셀러 직권취소(`POST /v1/order-items/{orderItemId}/cancel`)로 주문상품이 취소됐다.
|
|
7945
|
-
* 수량 단위 부분 취소도 이 이벤트다(`quantity` < 주문 수량이면 주문상품 상태가 그대로다).
|
|
7946
|
-
* 구매자가 신청한 취소·반품은 `CLAIM.*` 이벤트로 온다.
|
|
7947
|
-
*/
|
|
7948
|
-
const orderCanceledWebhookEventDataSchema = z.object({
|
|
7949
|
-
orderId: z.string().describe("주문 ID"),
|
|
7950
|
-
orderNo: z.string().nullable().optional().default(null).describe("주문번호(이슈 #107) — 숫자 14자리. 이 필드가 없는 이전 이벤트와 번호 체계 이전 주문은 null로 읽는다"),
|
|
7951
|
-
orderItemId: z.string().describe("취소한 주문 상품 ID"),
|
|
7952
|
-
status: orderItemStatusSchema.describe("취소 뒤 주문 대표 상태. 모든 주문 상품이 취소됐으면 `CANCELED`다"),
|
|
7953
|
-
itemStatus: orderItemStatusSchema.describe("취소 뒤 주문 상품 상태. 전 수량이 취소됐으면 `CANCELED`, 일부만 취소했으면 원래 상태다"),
|
|
7954
|
-
reason: z.string().describe("취소 사유 코드 — 판매자 취소 요청의 `reason`(`OUT_OF_STOCK`·`PRICE_ERROR`·`DELIVERY_IMPOSSIBLE`·`ETC`). 사유가 늘어날 수 있어 문자열이다. 모르는 값은 `ETC`처럼 다룬다"),
|
|
7955
|
-
quantity: z.number().int().describe("이번에 취소한 수량"),
|
|
7956
|
-
canceledQuantity: z.number().int().describe("취소 후 누적 취소 수량"),
|
|
7957
|
-
refundAmount: z.number().int().describe("환불액 (원). 수량 비율 상품 실결제 금액(쿠폰 배분을 뺀 금액) + 배송비 환불 − 조건부 무료배송 미달 차감 + 되돌린 쿠폰 차감(`couponChargeRefundAmount`)이다. 결제사 환불 결과는 결제 내역(`GET /v1/payments/{paymentId}`)으로 확인한다"),
|
|
7958
|
-
deliveryFeeRefundAmount: z.number().int().describe("환불액에 포함된 배송비 (원)"),
|
|
7959
|
-
deliveryFeeChargeAmount: z.number().int().describe("환불액에서 뺀 배송비 (원) — 조건부 무료배송 기준 미달 차감"),
|
|
7960
|
-
couponChargeAmount: z.number().int().optional().default(0).describe("환불액에서 뺀 쿠폰 조건 미달 차감 (원). 판매자 취소는 차감하지 않으므로 늘 0이다. 이 필드가 없는 이전 이벤트는 0으로 읽는다"),
|
|
7961
|
-
couponChargeRefundAmount: z.number().int().optional().default(0).describe("환불액에 더한 금액 (원) — 앞선 환불에서 뺀 쿠폰 조건 미달 차감을 되돌린 몫이다. 전량 취소면 누적 차감이 모두 되돌아간다. 이 필드가 없는 이전 이벤트는 0으로 읽는다"),
|
|
7962
|
-
pointRefundAmount: z.number().int().optional().default(0).describe("돌려준 적립금 (원, #93) — 결제에 쓴 적립금 중 이번 취소·반품 수량의 몫이다. 현금 환불(`refundAmount`)과 별개다. 차감은 현금에서 먼저 빼고 모자란 몫만 여기서 뺀다. 적립금을 쓰지 않은 주문과 이 필드가 없는 이전 응답·이벤트는 0으로 읽는다"),
|
|
7963
|
-
testPayment: z.boolean().describe("테스트 결제(샌드박스 결제) 주문인가. true면 실제로 돈이 오가지 않았다")
|
|
7964
|
-
});
|
|
7965
|
-
const claimWebhookEventDataSchema = z.object({
|
|
7966
|
-
claimId: z.string().describe("클레임 ID"),
|
|
7967
|
-
claimNo: z.string().nullable().optional().default(null).describe("클레임번호(이슈 #107) — `C` + YYMMDD + 숫자 8자리. 이 필드가 없는 이전 이벤트와 번호 체계 이전 클레임은 null로 읽는다"),
|
|
7968
|
-
orderId: z.string().describe("주문 ID"),
|
|
7969
|
-
orderItemId: z.string().describe("클레임 대상 주문 상품 ID"),
|
|
7970
|
-
claimType: claimTypeSchema.describe("클레임 유형"),
|
|
7971
|
-
status: claimStatusSchema.describe("이벤트 발생 시점의 클레임 상태"),
|
|
7972
|
-
quantity: z.number().int().optional().default(0).describe("클레임 대상 수량. 이 필드가 없는 이전 이벤트는 0으로 읽는다"),
|
|
7973
|
-
refundAmount: z.number().int().nullable().optional().default(null).describe("환불액 (원). 배송비 환불을 포함하고 배송비 차감·추가 차감을 뺀 실제 금액이다. 환불이 확정되지 않은 접수 이벤트는 예상 환불액이고, 교환처럼 환불이 없으면 null이다"),
|
|
7974
|
-
deliveryFeeRefundAmount: z.number().int().optional().default(0).describe("환불액에 포함된 배송비 (원). 이 필드가 없는 이전 이벤트는 0으로 읽는다"),
|
|
7975
|
-
deliveryFeeChargeAmount: z.number().int().optional().default(0).describe("환불액에서 뺀 배송비 (원) — 반품·교환 배송비와 조건부 무료배송 미달 차감의 합. 셀러가 승인에 넣은 추가 차감은 `deductAmount`로 따로 온다. 이 필드가 없는 이전 이벤트는 0으로 읽는다"),
|
|
7976
|
-
deductAmount: z.number().int().optional().default(0).describe("환불액에서 뺀 추가 차감 (원) — 셀러가 클레임 승인에 넣은 금액이다(상품 훼손 등). 배송비 차감과 별개이므로 `deliveryFeeChargeAmount`와 더해서 뺀다. 접수 이벤트와 이 필드가 없는 이전 이벤트는 0이다"),
|
|
7977
|
-
couponChargeAmount: z.number().int().optional().default(0).describe("환불액에서 뺀 쿠폰 조건 미달 차감 (원) — 일부 취소·반품으로 남은 주문상품이 쿠폰 조건(최소 주문 금액 등)을 잃어 남은 주문상품이 받던 할인을 뺀 금액이다. 배송비 차감·추가 차감과 별개다. 접수 이벤트는 예상값이다. 이 필드가 없는 이전 이벤트는 0으로 읽는다"),
|
|
7978
|
-
couponChargeRefundAmount: z.number().int().optional().default(0).describe("환불액에 더한 금액 (원) — 앞선 환불에서 뺀 쿠폰 조건 미달 차감을 되돌린 몫이다. 접수 이벤트는 예상값이다. 이 필드가 없는 이전 이벤트는 0으로 읽는다"),
|
|
7979
|
-
pointRefundAmount: z.number().int().optional().default(0).describe("돌려준 적립금 (원, #93) — 결제에 쓴 적립금 중 이번 취소·반품 수량의 몫이다. 현금 환불(`refundAmount`)과 별개다. 차감은 현금에서 먼저 빼고 모자란 몫만 여기서 뺀다. 적립금을 쓰지 않은 주문과 이 필드가 없는 이전 응답·이벤트는 0으로 읽는다"),
|
|
7980
|
-
testPayment: z.boolean().optional().default(false).describe("테스트 결제(샌드박스 결제) 주문의 클레임인가. 이 필드가 없는 이전 이벤트는 false로 읽는다")
|
|
7981
|
-
});
|
|
7982
|
-
const inquiryWebhookEventDataSchema = z.object({
|
|
7983
|
-
inquiryId: z.string().describe("상품 문의 ID"),
|
|
7984
|
-
productId: z.string().describe("상품 ID")
|
|
7985
|
-
});
|
|
7986
|
-
const reviewWebhookEventDataSchema = z.object({
|
|
7987
|
-
reviewId: z.string().describe("리뷰 ID"),
|
|
7988
|
-
productId: z.string().describe("상품 ID"),
|
|
7989
|
-
rating: z.number().int().min(1).max(5).describe("별점 (1~5)")
|
|
7990
|
-
});
|
|
7991
|
-
const productOutOfStockWebhookEventDataSchema = z.object({
|
|
7992
|
-
productId: z.string().describe("상품 ID"),
|
|
7993
|
-
variantId: z.string().describe("재고가 0이 된 판매 단위(variant) ID. 주문 상품의 `optionId`와 같은 값 공간이다")
|
|
7994
|
-
});
|
|
7995
|
-
const orderWebhookEventSchema = z.object({
|
|
7996
|
-
...envelopeFields,
|
|
7997
|
-
type: z.enum([
|
|
7998
|
-
"ORDER.PAID",
|
|
7999
|
-
"ORDER.CONFIRMED",
|
|
8000
|
-
"ORDER.DISPATCHED",
|
|
8001
|
-
"ORDER.DELIVERED",
|
|
8002
|
-
"ORDER.PURCHASE_DECIDED",
|
|
8003
|
-
"ORDER.ADDRESS_CHANGE_REQUESTED"
|
|
8004
|
-
]),
|
|
8005
|
-
data: orderWebhookEventDataSchema
|
|
8006
|
-
});
|
|
8007
|
-
const orderFulfilledWebhookEventSchema = z.object({
|
|
8008
|
-
...envelopeFields,
|
|
8009
|
-
type: z.literal("ORDER.FULFILLED"),
|
|
8010
|
-
data: orderFulfilledWebhookEventDataSchema
|
|
8011
|
-
});
|
|
8012
|
-
const orderCanceledWebhookEventSchema = z.object({
|
|
8013
|
-
...envelopeFields,
|
|
8014
|
-
type: z.literal("ORDER.CANCELED"),
|
|
8015
|
-
data: orderCanceledWebhookEventDataSchema
|
|
8016
|
-
});
|
|
8017
|
-
const claimWebhookEventSchema = z.object({
|
|
8018
|
-
...envelopeFields,
|
|
8019
|
-
type: z.enum([
|
|
8020
|
-
"CLAIM.CANCEL_REQUESTED",
|
|
8021
|
-
"CLAIM.RETURN_REQUESTED",
|
|
8022
|
-
"CLAIM.EXCHANGE_REQUESTED",
|
|
8023
|
-
"CLAIM.COMPLETED"
|
|
8024
|
-
]),
|
|
8025
|
-
data: claimWebhookEventDataSchema
|
|
8026
|
-
});
|
|
8027
|
-
const inquiryWebhookEventSchema = z.object({
|
|
8028
|
-
...envelopeFields,
|
|
8029
|
-
type: z.literal("INQUIRY.CREATED"),
|
|
8030
|
-
data: inquiryWebhookEventDataSchema
|
|
8031
|
-
});
|
|
8032
|
-
const reviewWebhookEventSchema = z.object({
|
|
8033
|
-
...envelopeFields,
|
|
8034
|
-
type: z.literal("REVIEW.CREATED"),
|
|
8035
|
-
data: reviewWebhookEventDataSchema
|
|
8036
|
-
});
|
|
8037
|
-
const productOutOfStockWebhookEventSchema = z.object({
|
|
8038
|
-
...envelopeFields,
|
|
8039
|
-
type: z.literal("PRODUCT.OUT_OF_STOCK"),
|
|
8040
|
-
data: productOutOfStockWebhookEventDataSchema
|
|
8041
|
-
});
|
|
8042
|
-
/**
|
|
8043
|
-
* 플로우 알림(이슈 #115) — 상점이 플로우에 둔 알림 블록(`core.notify`, 채널 `webhook`)이 보낸다. 구매자 알림(알림톡·메일)은 이 이벤트를 받아
|
|
8044
|
-
* 상점이 보낸다(플랫폼은 구매자에게 직접 보내지 않는다). 개인정보는 싣지 않는다
|
|
8045
|
-
*/
|
|
8046
|
-
const flowNotificationWebhookEventDataSchema = z.object({
|
|
8047
|
-
orderId: z.string().describe("주문 ID"),
|
|
8048
|
-
orderItemIds: z.array(z.string()).describe("이 알림의 주문상품(묶음 진행이면 여럿)"),
|
|
8049
|
-
event: z.string().describe("상점이 블록에 둔 알림 이름(`custom.<이름>`)"),
|
|
8050
|
-
flowId: z.string(),
|
|
8051
|
-
flowVersion: z.number().int(),
|
|
8052
|
-
state: z.string().describe("알림을 보낸 전이의 도착 상태 키")
|
|
8053
|
-
});
|
|
8054
|
-
const flowNotificationWebhookEventSchema = z.object({
|
|
8055
|
-
...envelopeFields,
|
|
8056
|
-
type: z.literal("ORDER.FLOW_NOTIFICATION"),
|
|
8057
|
-
data: flowNotificationWebhookEventDataSchema
|
|
8058
|
-
});
|
|
8059
|
-
/**
|
|
8060
|
-
* 흐름 상태 변경(이슈 #115) — 주문상품의 흐름 상태(노드)가 바뀔 때마다 온다. 공개 상태(`ORDER.*`)가 같아도 상점 흐름의 단계가 바뀌면 온다
|
|
8061
|
-
* (시안 확인 → 제작중). 묶음 진행이면 `orderItemIds`가 여럿이다. 개인정보는 싣지 않는다
|
|
8062
|
-
*/
|
|
8063
|
-
const flowStateChangedWebhookEventDataSchema = z.object({
|
|
8064
|
-
orderId: z.string().describe("주문 ID"),
|
|
8065
|
-
orderItemIds: z.array(z.string()).describe("상태가 바뀐 주문상품(묶음 진행이면 여럿)"),
|
|
8066
|
-
flowId: z.string(),
|
|
8067
|
-
flowVersion: z.number().int(),
|
|
8068
|
-
from: z.string().nullable().describe("이전 상태 키. 흐름이 막 시작했으면 null"),
|
|
8069
|
-
to: z.string().describe("새 상태 키"),
|
|
8070
|
-
label: z.string().describe("새 상태의 셀러용 이름"),
|
|
8071
|
-
coreStatus: z.string().nullable().describe("새 상태의 공개 상태. 공개 상태를 바꾸지 않는 단계면 null"),
|
|
8072
|
-
changedAt: z.string().describe("바뀐 시각")
|
|
8073
|
-
});
|
|
8074
|
-
const flowStateChangedWebhookEventSchema = z.object({
|
|
8075
|
-
...envelopeFields,
|
|
8076
|
-
type: z.literal("ORDER.FLOW_STATE_CHANGED"),
|
|
8077
|
-
data: flowStateChangedWebhookEventDataSchema
|
|
8078
|
-
});
|
|
8079
|
-
/** 웹훅 요청 본문. `type`으로 판별하며 `data` 형태가 타입별로 다르다 */
|
|
8080
|
-
const webhookEventSchema = z.discriminatedUnion("type", [
|
|
8081
|
-
orderWebhookEventSchema,
|
|
8082
|
-
orderFulfilledWebhookEventSchema,
|
|
8083
|
-
orderCanceledWebhookEventSchema,
|
|
8084
|
-
claimWebhookEventSchema,
|
|
8085
|
-
inquiryWebhookEventSchema,
|
|
8086
|
-
reviewWebhookEventSchema,
|
|
8087
|
-
productOutOfStockWebhookEventSchema,
|
|
8088
|
-
flowNotificationWebhookEventSchema,
|
|
8089
|
-
flowStateChangedWebhookEventSchema
|
|
8090
|
-
]).describe("웹훅 요청 본문 (얇은 이벤트). 개인정보를 싣지 않으며 `data`의 id로 관리 API를 조회한다");
|
|
8091
|
-
/**
|
|
8092
|
-
* 알려진 웹훅 이벤트 타입 — `webhookEventSchema`의 판별 값 목록(이 SDK 버전이 아는 것).
|
|
8093
|
-
* 서버는 새 타입을 구독한 엔드포인트에만 보내지만, 구독을 더 넓힌 수신자를 위해 `parseWebhookEvent`가 모르는 타입을 따로 받는다.
|
|
8094
|
-
*/
|
|
8095
|
-
const KNOWN_WEBHOOK_EVENT_TYPES = webhookEventSchema.options.flatMap((option) => {
|
|
8096
|
-
const type = option.shape.type;
|
|
8097
|
-
return "options" in type ? [...type.options] : [type.value];
|
|
8098
|
-
});
|
|
8099
|
-
/** 이 SDK 버전이 모르는 이벤트 — 봉투만 검증하고 `data`는 그대로 둔다 */
|
|
8100
|
-
const unknownWebhookEventSchema = z.object({
|
|
8101
|
-
...envelopeFields,
|
|
8102
|
-
type: z.string().min(1),
|
|
8103
|
-
data: z.unknown()
|
|
8104
|
-
});
|
|
8105
|
-
/**
|
|
8106
|
-
* 웹훅 본문을 `type`으로 먼저 판별해 읽는다(이슈 #115 결정 25). 알려진 타입은 그 타입의 스키마로 엄격하게 읽고 틀리면 던진다 —
|
|
8107
|
-
* `z.union([known, unknown])`처럼 읽으면 알려진 타입의 파싱 실패가 `known: false`로 조용히 새어 수신자가 처리할 이벤트를 버린다.
|
|
8108
|
-
* 모르는 타입은 봉투만 검증해 `known: false`로 돌려준다(새 이벤트에 옛 SDK가 깨지지 않는다).
|
|
8109
|
-
*/
|
|
8110
|
-
function parseWebhookEvent(body) {
|
|
8111
|
-
const type = body?.type;
|
|
8112
|
-
if (typeof type === "string" && KNOWN_WEBHOOK_EVENT_TYPES.includes(type)) return {
|
|
8113
|
-
known: true,
|
|
8114
|
-
event: webhookEventSchema.parse(body)
|
|
8115
|
-
};
|
|
8116
|
-
return {
|
|
8117
|
-
known: false,
|
|
8118
|
-
event: unknownWebhookEventSchema.parse(body)
|
|
8119
|
-
};
|
|
8120
|
-
}
|
|
8121
|
-
//#endregion
|
|
8122
8964
|
//#region src/session.ts
|
|
8123
8965
|
/**
|
|
8124
8966
|
* 세션 키 상수 — SDK가 소유한다. 값은 apps/store의 기존 쿠키 이름(SESSION_COOKIE/REFRESH_COOKIE)과
|
|
@@ -8298,4 +9140,4 @@ function createStoreSession(options) {
|
|
|
8298
9140
|
};
|
|
8299
9141
|
}
|
|
8300
9142
|
//#endregion
|
|
8301
|
-
export { APPROVAL_ALREADY_DECIDED_CODE, APPROVAL_REQUIRED_CODE, ApiError, ApprovalPendingError, BUSINESS_INFO_MAX_LENGTH, BUSINESS_NUMBER_PATTERN, CASH_RECEIPT_STATUS_VALUES, COLLECTION_BEST_SELLING_DAYS, COLLECTION_PREVIEW_SIZE, COLLECTION_PRODUCT_SORTS, COLLECTION_SALES_SORT_DEFAULT_DAYS, COLLECTION_SLUG_PATTERN, COLLECTION_TYPES, COUPON_CODE_PATTERN, COUPON_CONDITIONS_DEFAULTS, COUPON_TARGET_ALL, COUPON_TARGET_LIST_MAX, CUSTOM_INPUT_FORBIDDEN_WORDS, CUSTOM_INPUT_TYPES, FULFILLMENT_METHODS, FULFILLMENT_METHODS_BY_TYPE, FULFILLMENT_METHOD_RULES, FULFILLMENT_RULES, KNOWN_WEBHOOK_EVENT_TYPES, MAX_ADDON_GROUPS, MAX_ANALYTICS_ALLOWED_ORIGINS, MAX_AUTO_TRANSITION_DAYS, MAX_CALENDAR_DAYS, MAX_COLLECTIONS, MAX_COLLECTION_BEST_SELLING_TOP, MAX_COLLECTION_CREATED_WITHIN_DAYS, MAX_COLLECTION_DESCRIPTION, MAX_COLLECTION_PRODUCTS, MAX_COLLECTION_PRODUCTS_PER_ADD, MAX_COLLECTION_RULES, MAX_COLLECTION_RULE_CATEGORIES, MAX_COLLECTION_TITLE, MAX_CUSTOM_INPUTS, MAX_CUSTOM_INPUT_LENGTH, MAX_OPTION_GROUPS, MAX_PICKUP_LOCATIONS, MAX_PRODUCT_IMAGES, MAX_SAVED_OPTION_SETS, MAX_SAVED_OPTION_SET_NAME, MAX_STAY_NIGHTS, MAX_VALUES_PER_GROUP, MAX_VARIANTS, MCP_TELEMETRY_MAX_RESULTS, MIN_PAYMENT_AMOUNT, NO_COUPON_SETTLEMENT, NO_SHIPPING_BUNDLE_KEY, PAYMENT_ROUTING_EXCLUSION_CODES, POINT_ADJUSTMENT_MAX, POINT_EARN_RATE_BP_MAX, POINT_ENTRY_TYPES, POINT_EXPIRY_DAYS_MAX, POINT_EXPIRY_DAYS_MIN, POINT_LOT_SOURCES, POINT_POLICY_DEFAULTS, POINT_REVIEW_REWARD_MAX, POINT_USE_UNITS, PORTONE_CHANNEL_NAME_MAX_LENGTH, PORTONE_MAX_CHANNELS, PORTONE_MIN_CHANNELS, REFUND_POLICY_DEFAULTS, REFUND_POLICY_DELIVERY_FEE_KEYS, REMOTE_AREA_ZIP_RANGES, RESERVED_STOREFRONT_SLUGS, RESERVED_STORE_CODES, SHIPPING_POLICY_DEFAULTS, STOREFRONT_BLOCKED_PATH_SEGMENTS, STOREFRONT_BUILD_STAGES, STOREFRONT_BUILD_TERMINAL_STATUSES, STOREFRONT_CHECK_TERMINAL_STATUSES, STOREFRONT_CUSTOM_DOMAIN_LIMIT_DEFAULT, STOREFRONT_DOMAIN_DNS_GRACE_DAYS, STOREFRONT_DOMAIN_DNS_WAIT_DAYS, STOREFRONT_DOMAIN_TXT_LABEL, STOREFRONT_EDIT_ACTIVITY_TIMEOUT_MINUTES, STOREFRONT_EDIT_CAPACITY, STOREFRONT_EDIT_IDLE_MINUTES, STOREFRONT_EDIT_PREVIEW_SUFFIX, STOREFRONT_EDIT_START_STAGES, STOREFRONT_FILE_BATCH_MAX, STOREFRONT_FILE_MAX_BYTES, STOREFRONT_PATH_SEGMENT_PATTERN, STOREFRONT_PLATFORM_DOMAINS, STOREFRONT_PUBLIC_SUFFIXES, STOREFRONT_SEARCH_REGEX_MAX_LENGTH, STOREFRONT_SHOWCASE_SLUG_PREFIX, STOREFRONT_SITE_CREATE_LIMIT, STOREFRONT_SLUG_CHANGE_LIMIT, STOREFRONT_SLUG_CHANGE_WINDOW_DAYS, STOREFRONT_SLUG_HOLD_DAYS, STOREFRONT_SLUG_PATTERN, STOREFRONT_SOURCE_BLOB_BATCH_MAX, STOREFRONT_SOURCE_PART_BYTES, STOREFRONT_WORKSPACE_MAX_BYTES, STOREFRONT_WORKSPACE_MAX_FILES, STORE_CODE_PATTERN, STORE_ROLE_SCOPES, STORE_SCOPE_RESOURCES, STORE_SESSION_KEYS, TOSS_WIDGET_CLIENT_KEY_PATTERN, TOSS_WIDGET_KEY_MESSAGE, TOSS_WIDGET_SECRET_KEY_PATTERN, UPLOAD_PURPOSE_LIMITS, VARIANT_COUNT_WARNING, VARIANT_STOCK_EXCLUSIVE_MESSAGE, WEBHOOK_DEFAULT_TOLERANCE_SEC, WEBHOOK_DELIVERY_ID_HEADER, WEBHOOK_EVENT_ID_HEADER, WEBHOOK_EVENT_TYPE_HEADER, WEBHOOK_FAILURE_REASONS, WEBHOOK_HEADERS, WEBHOOK_PAYLOAD_EXCERPT_MAX_BYTES, WEBHOOK_RESOURCE_TYPES, WEBHOOK_SIGNATURE_HEADER, WEBHOOK_USER_AGENT, addStorefrontDomainRequestSchema, addonGroupRequestSchema, addonGroupSchema, addonValueRequestSchema, addonValueSchema, addressChangeRequestListSchema, addressTypeSchema, agentPolicyDecisionSchema, allocateByWeight, analyticsChannelSchema, analyticsCheckoutOutcomeSchema, analyticsCheckoutSchema, analyticsDeviceRowSchema, analyticsDevicesSchema, analyticsFunnelStepSchema, analyticsOriginSchema, analyticsOverviewSchema, analyticsPagesQuerySchema, analyticsPagesSchema, analyticsProductRowSchema, analyticsProductSortSchema, analyticsProductsQuerySchema, analyticsProductsSchema, analyticsRangeQuerySchema, analyticsRealtimeSchema, analyticsSearchQuerySchema, analyticsSearchSchema, analyticsSourceGroupBySchema, analyticsSourceRowSchema, analyticsSourcesQuerySchema, analyticsSourcesSchema, analyticsTotalsSchema, apiLogErrorSchema, apiLogListQuerySchema, apiLogPageSchema, apiLogPrincipalSchema, apiLogPrincipalTypeSchema, apiLogSchema, apiLogStatusFilterSchema, apiLogSummarySchema, approvalDecisionRequestSchema, approvalListQuerySchema, approvalPageSchema, approvalRequestSchema, approvalStatusSchema, approveClaimResultSchema, auditActorSchema, auditActorTypeSchema, auditLogListQuerySchema, auditLogOutcomeSchema, auditLogPageSchema, auditLogSchema, auditLogSourceSchema, bestSellingRuleOf, buildAuthorizeUrl, buildQuery, bulkResultSchema, businessInfoPatchShape, businessInfoResponseShape, businessNumberSchema, calendarDateSchema, calendarDaySchema, calendarQuerySchema, cancelBySellerRequestSchema, cancelBySellerResultSchema, carrierSchema, cashReceiptSchema, categoryAttributeSchema, categoryCreateRequestSchema, categoryDeleteQuerySchema, categoryDeleteStrategySchema, categoryListQuerySchema, categoryPatchRequestSchema, categoryProductSortSchema, categorySchema, categorySearchResultSchema, categoryTreeNodeSchema, categoryWithPathSchema, changeStorefrontSlugRequestSchema, changeStorefrontTemplateRequestSchema, changeTypeSchema, claimDetailSchema, claimHoldResultSchema, claimListQuerySchema, claimReasonSchema, claimReturnAddressSchema, claimSchema, claimStatusSchema, claimTypeSchema, claimWebhookEventDataSchema, claimWebhookEventSchema, collectionCreateRequestSchema, collectionListQuerySchema, collectionPageSchema, collectionPatchRequestSchema, collectionPreviewRequestSchema, collectionPreviewSchema, collectionProductListQuerySchema, collectionProductSchema, collectionProductSortRequestSchema, collectionProductsAddRequestSchema, collectionProductsReplaceRequestSchema, collectionRuleCategoryIds, collectionRuleSchema, collectionRuleViewSchema, collectionRulesPredicate, collectionRulesSchema, collectionSchema, collectionStateCountsSchema, collectionStateOf, collectionStateSchema, commerceFlowCheckSchema, commerceFlowSchema, commerceFlowSummarySchema, commerceFlowVersionViewSchema, commerceNumberField, compareBySales, consentActionSchema, consentKindSchema, consentSourceSchema, couponBenefitSchema, couponConditionsInputSchema, couponConditionsSchema, couponDiscountOf, couponIssueListQuerySchema, couponIssueMethodSchema, couponIssueSchema, couponIssueStatusSchema, couponKindSchema, couponListQuerySchema, couponRedemptionListQuerySchema, couponRedemptionSchema, couponRejectReasonSchema, couponRuleSchema, couponSchema, couponStatsSchema, couponStatusSchema, couponTargetInputSchema, couponTargetSchema, createCommerceFlowRequestSchema, createCouponRequestSchema, createPkcePair, createStoreClient, createStoreSession, createStorefrontEditPreviewLinkRequestSchema, createStorefrontPreviewLinkRequestSchema, createStorefrontSiteRequestSchema, createStorefrontVersionRequestSchema, customInputLabelForbiddenWord, customInputRequestSchema, customInputSchema, customInputValueSchema, customerAddressSchema, customerConsentListQuerySchema, customerConsentSchema, customerConsentStateSchema, customerConsentsSchema, customerCouponSchema, customerDelegationRequestSchema, customerDelegationSchema, customerDetailSchema, customerInquiryCategorySchema, customerInquiryListQuerySchema, customerListQuerySchema, customerOrderSchema, customerPointsSchema, customerSortSchema, customerStatsSchema, customerSummarySchema, customerWithdrawalBlockedDetailsSchema, defaultFulfillmentMethodOf, deleteStorefrontFileRequestSchema, deliveryBundleKeyOf, deliveryFeeWaiverSchema, deliveryInfoSchema, deliveryMethodSchema, deliveryStatusSchema, deliveryTypeSchema, descendantCategoryIdsOf, disableStorefrontHostingRequestSchema, discountRequestSchema, discountSchema, dispatchRequestSchema, easyPayProviderResponseSchema, easyPayProviderSchema, eventTypeSchema, exchangeAuthorizationCode, flowActionRequestSchema, flowActionResultSchema, flowActionSchema, flowFieldValueSchema, flowInputFieldSchema, flowNodeTypesSchema, flowNotificationWebhookEventDataSchema, flowNotificationWebhookEventSchema, flowPresetDetailSchema, flowPresetSummarySchema, flowRunSchema, flowRunStepSchema, flowSecretNameSchema, flowSecretSchema, flowStateChangedWebhookEventDataSchema, flowStateChangedWebhookEventSchema, flowStateSchema, formatOrderNo, fulfillBulkRequestSchema, fulfillRequestSchema, fulfillmentMethodOf, fulfillmentMethodSchema, fulfillmentMethodsOf, fulfillmentRuleOf, fulfillmentTypeOf, fulfillmentTypeResponseSchema, fulfillmentTypeSchema, holdOrderItemRequestSchema, inquiryListQuerySchema, inquirySchema, inquiryWebhookEventDataSchema, inquiryWebhookEventSchema, inventoryModeSchema, invitationDeliveryStatusSchema, invitationListQuerySchema, invitationSchema, inviteMemberRequestSchema, isCouponTargetLine, isEditableStorefrontPath, isReservedStoreCode, isReservedStorefrontSlug, isShowcaseStorefrontSlug, isStoreScope, isWebOrigin, issueCouponRequestSchema, issueCouponResultSchema, liveGateConditionCodeSchema, liveGateConditionSchema, liveGateErrorDetailsSchema, liveGateSchema, loginProviderSchema, loginProviderStatusSchema, loginProviderTestSchema, loginSettingsSchema, maskedSecretSchema, matchesCollectionRules, mcpRuleResultSchema, mcpToolEventSchema, migrateFlowVersionRequestSchema, migrateFlowVersionResultSchema, moveFlowStateRequestSchema, moveStorefrontFileRequestSchema, myAccountSchema, myStoreSchema, nightsOf, normalizeBusinessNumber, normalizeCouponCode, normalizeStorefrontHostname, normalizeStorefrontPath, oauthTokenResponseSchema, openFulfillmentUnion, optionGroupKindSchema, optionGroupRequestSchema, optionGroupSchema, optionModelRequestSchema, optionModelSchema, optionSelectionSchema, optionSortSchema, optionValueRequestSchema, optionValueSchema, orderCanceledWebhookEventDataSchema, orderCanceledWebhookEventSchema, orderDiscountSchema, orderFlowActionResultSchema, orderFulfilledWebhookEventDataSchema, orderFulfilledWebhookEventSchema, orderItemFlowSchema, orderItemFulfillmentSchema, orderItemFulfillmentSnapshotResponseSchema, orderItemFulfillmentSnapshotSchema, orderItemListQuerySchema, orderItemMemoResultSchema, orderItemPageSchema, orderItemSchema, orderItemStatusSchema, orderListQuerySchema, orderSchema, orderSummaryPageSchema, orderSummarySchema, orderWebhookEventDataSchema, orderWebhookEventSchema, pageInfoSchema, pageQueryShape, pageSchema, parseOrderNo, parseWebhookEvent, paymentAttemptRecordSchema, paymentAttemptStatusSchema, paymentCredentialSetSchema, paymentFailureWarningSchema, paymentListQuerySchema, paymentProviderSchema, paymentProviderVerificationSchema, paymentRecordDetailSchema, paymentRecordPageSchema, paymentRecordSchema, paymentRecordStatusSchema, paymentRefundRecordSchema, paymentSettingsSchema, pgEnvironmentSchema, pgPaymentMethodSchema, pgProviderSchema, pgVerificationResultSchema, pickupLocationRequestSchema, pickupLocationSchema, pickupLocationSnapshotSchema, planCodeSchema, planEntitlementsSchema, planErrorDetailsSchema, pointAdjustmentRequestSchema, pointAdjustmentResultSchema, pointEarnOf, pointEarnRateBpSchema, pointEarningEnabled, pointEntrySchema, pointExpiresAtOf, pointHistoryQuerySchema, pointLotSchema, pointPolicyNoticeSchema, pointPolicySchema, pointRestoreExpiresAtOf, pointUseOf, pointUsePolicySchema, policyDocumentUrlSchema, policyVersionSchema, portOneChannelSchema, portOneCredentialsInputSchema, productCreateRequestSchema, productFulfillmentCreateSchema, productFulfillmentResponseSchema, productFulfillmentSchema, productFulfillmentUpdateSchema, productImageRejection, productImageUrlSchema, productListQuerySchema, productOutOfStockWebhookEventDataSchema, productOutOfStockWebhookEventSchema, productPatchRequestSchema, productRefundPolicyOverride, productRefundPolicyPatchSchema, productReplaceRequestSchema, productSchema, productSortSchema, productStatusSchema, productSummarySchema, productVariantRequestSchema, productVariantSchema, proratedPaidRefundBase, proratedPointRefund, proratedRefundBase, publicPlanSchema, publicPlansSchema, publishStorefrontVersionRequestSchema, purchaseDecisionExtendResultSchema, purchaseDecisionRequestResultSchema, purchaseEarnPolicySchema, purchaseEarnRateOf, quoteDelivery, quotePricing, rankBestSelling, refreshUserToken, refundPolicyPatchSchema, refundPolicySchema, rejectClaimRequestSchema, remoteAreaLabelOf, remoteAreaOf, remoteAreaSchema, remoteSurchargeOf, replaceStorefrontSourceRequestSchema, replayFlowRequestSchema, replayFlowResultSchema, requiresCollection, requiresShipping, resolvePointPolicy, resolveRefundPolicy, resolveShippingPolicy, resolvedFlowSchema, responsePlanCodeSchema, responsePointEntryTypeSchema, reviewListQuerySchema, reviewReportResultSchema, reviewRewardPointOf, reviewRewardPolicySchema, reviewSchema, reviewSortSchema, reviewWebhookEventDataSchema, reviewWebhookEventSchema, rollbackStorefrontRequestSchema, runStorefrontCheckRequestSchema, salesReportGroupBySchema, salesReportQuerySchema, salesReportRowSchema, salesReportSchema, salesTotalsSchema, saveFlowDraftRequestSchema, saveFlowSecretRequestSchema, saveLoginProviderRequestSchema, savedCustomInputRequestSchema, savedCustomInputSchema, savedOptionGroupRequestSchema, savedOptionGroupSchema, savedOptionSetCompositionSchema, savedOptionSetFromProductRequestSchema, savedOptionSetFromProductResultSchema, savedOptionSetListQuerySchema, savedOptionSetRequestSchema, savedOptionSetSchema, savedOptionSetSortSchema, savedOptionValueRequestSchema, savedOptionValueSchema, scopeAccessSchema, searchStorefrontFilesRequestSchema, sellerAddressListQuerySchema, sellerAddressRequestSchema, sellerAddressSchema, sellerCancelPreviewSchema, sellerCustomerInquirySchema, setCalendarRequestSchema, setStorefrontVisibilityRequestSchema, settleCouponShortfall, settlementDetailQuerySchema, settlementDetailSchema, settlementListQuerySchema, settlementStatusSchema, settlementSummarySchema, settlementWithDetailsSchema, shippingAddressSchema, shippingPolicyPatchSchema, shippingPolicySchema, simulateFlowRequestSchema, simulateFlowResultSchema, simulationStepSchema, splitRefund, stayRangeSchema, staySnapshotSchema, storeCodeSchema, storeMemberRoleUpdatedSchema, storeMemberSchema, storePointPolicySchema, storeRoleSchema, storeSchema, storeScopeResourceSchema, storeSubscriptionSchema, storefrontBuildDependenciesSchema, storefrontBuildListQuerySchema, storefrontBuildSchema, storefrontBuildStatusSchema, storefrontCheckSchema, storefrontCheckStatusSchema, storefrontCustomDomainStatusSchema, storefrontCustomHostnameProblem, storefrontDependencyFindingSchema, storefrontDeploymentLogSchema, storefrontDeploymentSchema, storefrontDeploymentStepSchema, storefrontDnsRecordSchema, storefrontDomainSchema, storefrontDomainTxtNameOf, storefrontEditPreviewLabelOf, storefrontEditPreviewSchema, storefrontEditSessionSchema, storefrontEditSessionStatusSchema, storefrontEditingSchema, storefrontFileConflictDetailsSchema, storefrontFileContentQuerySchema, storefrontFileContentSchema, storefrontFileDeleteResultSchema, storefrontFileEncodingSchema, storefrontFileEntrySchema, storefrontFileHashSchema, storefrontFileListQuerySchema, storefrontFileListSchema, storefrontFileMoveResultSchema, storefrontFilePathSchema, storefrontFileSearchResultSchema, storefrontFileWriteResultSchema, storefrontHostingSchema, storefrontPreviewLinkSchema, storefrontProvisioningSchema, storefrontRuleResultSchema, storefrontRuleSeveritySchema, storefrontSiteDomainSchema, storefrontSiteDomainsSchema, storefrontSiteSchema, storefrontSiteStatusSchema, storefrontSlugAvailabilityQuerySchema, storefrontSlugAvailabilitySchema, storefrontSlugSchema, storefrontSourceArchivePartSchema, storefrontSourceArchiveQuerySchema, storefrontSourceBlobsResultSchema, storefrontSourceModeSchema, storefrontSourceRefSchema, storefrontSourceReplacedSchema, storefrontSourceSchema, storefrontSuspensionSchema, storefrontTemplateSchema, storefrontVerifyReportSchema, storefrontVersionCreatedSchema, storefrontVersionSchema, storefrontVersionStatusSchema, storefrontVisibilitySchema, subscriptionLimitsSchema, subscriptionStatusSchema, subscriptionUsageSchema, tossPaymentsCredentialsInputSchema, trackingStageResponseSchema, trackingUpdateResultSchema, unknownWebhookEventSchema, updateCouponRequestSchema, updateLoginProviderRequestSchema, updateLoginSettingsRequestSchema, updateMemberRoleRequestSchema, updatePaymentProviderRequestSchema, updatePaymentSettingsRequestSchema, updatePointPolicyRequestSchema, updateStoreRequestSchema, uploadExtensionOf, uploadPurposeSchema, uploadRejection, uploadRequestSchema, uploadStorefrontSourceBlobsRequestSchema, uploadTicketSchema, upsertPortOneProviderRequestSchema, upsertTossPaymentsProviderRequestSchema, variantStockRequestSchema, variantStockSchema, verifyPaymentProviderRequestSchema, webhookCreatedSchema, webhookDeliveryDetailSchema, webhookDeliveryErrorCodeSchema, webhookDeliveryListQuerySchema, webhookDeliveryResourceSchema, webhookDeliverySchema, webhookDeliveryStatusSchema, webhookEventSchema, webhookFailureReasonSchema, webhookRedeliverFailedResultSchema, webhookRequestHeaderSchema, webhookRequestSchema, webhookResourceTypeSchema, webhookSchema, webhookSecretRotatedSchema, webhookStatusSchema, webhookTestRequestSchema, webhookTestResultSchema, withdrawCustomerRequestSchema, writeDryRunResultSchema, writePreviewSchema, writeStorefrontFilesRequestSchema };
|
|
9143
|
+
export { APPROVAL_ALREADY_DECIDED_CODE, APPROVAL_REQUIRED_CODE, APP_CAPABILITIES, APP_DEPLOY_KEY_PATTERN, APP_DEPLOY_KEY_PREFIX, APP_EVENTS_MAX, APP_EVENT_SCOPES, APP_EVENT_TYPES, APP_HANDLE_PATTERN, APP_LIFECYCLE_EVENT_TYPES, APP_NETWORK_DENIED_SUFFIXES, APP_NETWORK_HOSTS_MAX, APP_RESERVED_HANDLES, APP_SECRETS_MAX, APP_SECRET_NAME_PATTERN, APP_SECRET_PATTERNS, APP_SERVER_BUNDLE_MAX_GZIP_BYTES, APP_SETTINGS_MAX_FIELDS, APP_SETTING_FIELD_TYPES, APP_SETTING_KEY_PATTERN, APP_UI_MAX_BYTES, APP_UI_MAX_FILES, ApiError, ApprovalPendingError, BRAND_BRIEF_MAX_REFERENCES, BUSINESS_INFO_MAX_LENGTH, BUSINESS_NUMBER_PATTERN, CASH_RECEIPT_STATUS_VALUES, COLLECTION_BEST_SELLING_DAYS, COLLECTION_PREVIEW_SIZE, COLLECTION_PRODUCT_SORTS, COLLECTION_SALES_SORT_DEFAULT_DAYS, COLLECTION_SLUG_PATTERN, COLLECTION_TYPES, COUPON_CODE_PATTERN, COUPON_CONDITIONS_DEFAULTS, COUPON_TARGET_ALL, COUPON_TARGET_LIST_MAX, CUSTOM_INPUT_FORBIDDEN_WORDS, CUSTOM_INPUT_TYPES, FULFILLMENT_METHODS, FULFILLMENT_METHODS_BY_TYPE, FULFILLMENT_METHOD_RULES, FULFILLMENT_RULES, INSTALLABLE_SCOPES, KNOWN_WEBHOOK_EVENT_TYPES, MAX_ADDON_GROUPS, MAX_ANALYTICS_ALLOWED_ORIGINS, MAX_AUTO_TRANSITION_DAYS, MAX_CALENDAR_DAYS, MAX_COLLECTIONS, MAX_COLLECTION_BEST_SELLING_TOP, MAX_COLLECTION_CREATED_WITHIN_DAYS, MAX_COLLECTION_DESCRIPTION, MAX_COLLECTION_PRODUCTS, MAX_COLLECTION_PRODUCTS_PER_ADD, MAX_COLLECTION_RULES, MAX_COLLECTION_RULE_CATEGORIES, MAX_COLLECTION_TITLE, MAX_CUSTOM_INPUTS, MAX_CUSTOM_INPUT_LENGTH, MAX_OPTION_GROUPS, MAX_PICKUP_LOCATIONS, MAX_PRODUCT_IMAGES, MAX_SAVED_OPTION_SETS, MAX_SAVED_OPTION_SET_NAME, MAX_STAY_NIGHTS, MAX_VALUES_PER_GROUP, MAX_VARIANTS, MCP_TELEMETRY_MAX_RESULTS, MIN_PAYMENT_AMOUNT, NON_INSTALLABLE_SCOPES, NO_COUPON_SETTLEMENT, NO_SHIPPING_BUNDLE_KEY, PAYMENT_ROUTING_EXCLUSION_CODES, POINT_ADJUSTMENT_MAX, POINT_EARN_RATE_BP_MAX, POINT_ENTRY_TYPES, POINT_EXPIRY_DAYS_MAX, POINT_EXPIRY_DAYS_MIN, POINT_LOT_SOURCES, POINT_POLICY_DEFAULTS, POINT_REVIEW_REWARD_MAX, POINT_USE_UNITS, PORTONE_CHANNEL_NAME_MAX_LENGTH, PORTONE_MAX_CHANNELS, PORTONE_MIN_CHANNELS, REFUND_POLICY_DEFAULTS, REFUND_POLICY_DELIVERY_FEE_KEYS, REMOTE_AREA_ZIP_RANGES, RESERVED_STOREFRONT_SLUGS, RESERVED_STORE_CODES, SCOPE_DESCRIPTIONS, SHIPPING_POLICY_DEFAULTS, STARTER_SELLER_TASKS, STARTER_STAGES, STOREFRONT_BLOCKED_PATH_SEGMENTS, STOREFRONT_BUILD_STAGES, STOREFRONT_BUILD_TERMINAL_STATUSES, STOREFRONT_CHECK_TERMINAL_STATUSES, STOREFRONT_CLOSED_SITE_HOLD_DAYS, STOREFRONT_CUSTOM_DOMAIN_LIMIT_DEFAULT, STOREFRONT_DOMAIN_DNS_GRACE_DAYS, STOREFRONT_DOMAIN_DNS_WAIT_DAYS, STOREFRONT_DOMAIN_TXT_LABEL, STOREFRONT_EDIT_ACTIVITY_TIMEOUT_MINUTES, STOREFRONT_EDIT_CAPACITY, STOREFRONT_EDIT_IDLE_MINUTES, STOREFRONT_EDIT_PREVIEW_SUFFIX, STOREFRONT_EDIT_START_STAGES, STOREFRONT_FILE_BATCH_MAX, STOREFRONT_FILE_MAX_BYTES, STOREFRONT_PATH_SEGMENT_PATTERN, STOREFRONT_PLATFORM_DOMAINS, STOREFRONT_PUBLIC_SUFFIXES, STOREFRONT_SEARCH_REGEX_MAX_LENGTH, STOREFRONT_SHOWCASE_SLUG_PREFIX, STOREFRONT_SITE_CREATE_LIMIT, STOREFRONT_SLUG_CHANGE_LIMIT, STOREFRONT_SLUG_CHANGE_WINDOW_DAYS, STOREFRONT_SLUG_HOLD_DAYS, STOREFRONT_SLUG_PATTERN, STOREFRONT_SOURCE_BLOB_BATCH_MAX, STOREFRONT_SOURCE_PART_BYTES, STOREFRONT_WORKSPACE_MAX_BYTES, STOREFRONT_WORKSPACE_MAX_FILES, STORE_CODE_PATTERN, STORE_ROLE_SCOPES, STORE_SCOPE_RESOURCES, STORE_SESSION_KEYS, TEMPLATE_STORE_LOCKED_BUSINESS_FIELDS, TOSS_WIDGET_CLIENT_KEY_PATTERN, TOSS_WIDGET_KEY_MESSAGE, TOSS_WIDGET_SECRET_KEY_PATTERN, UPLOAD_PURPOSE_LIMITS, VARIANT_COUNT_WARNING, VARIANT_STOCK_EXCLUSIVE_MESSAGE, WEBHOOK_DEFAULT_TOLERANCE_SEC, WEBHOOK_DELIVERY_ID_HEADER, WEBHOOK_EVENT_ID_HEADER, WEBHOOK_EVENT_TYPE_HEADER, WEBHOOK_FAILURE_REASONS, WEBHOOK_HEADERS, WEBHOOK_PAYLOAD_EXCERPT_MAX_BYTES, WEBHOOK_RESOURCE_TYPES, WEBHOOK_SIGNATURE_HEADER, WEBHOOK_USER_AGENT, addStorefrontDomainRequestSchema, addonGroupRequestSchema, addonGroupSchema, addonValueRequestSchema, addonValueSchema, addressChangeRequestListSchema, addressTypeSchema, agentPolicyDecisionSchema, allocateByWeight, analyticsChannelSchema, analyticsCheckoutOutcomeSchema, analyticsCheckoutSchema, analyticsDeviceRowSchema, analyticsDevicesSchema, analyticsFunnelStepSchema, analyticsOriginSchema, analyticsOverviewSchema, analyticsPagesQuerySchema, analyticsPagesSchema, analyticsProductRowSchema, analyticsProductSortSchema, analyticsProductsQuerySchema, analyticsProductsSchema, analyticsRangeQuerySchema, analyticsRealtimeSchema, analyticsSearchQuerySchema, analyticsSearchSchema, analyticsSourceGroupBySchema, analyticsSourceRowSchema, analyticsSourcesQuerySchema, analyticsSourcesSchema, analyticsTotalsSchema, apiLogErrorSchema, apiLogListQuerySchema, apiLogPageSchema, apiLogPrincipalSchema, apiLogPrincipalTypeSchema, apiLogSchema, apiLogStatusFilterSchema, apiLogSummarySchema, appArtifactUploadSchema, appBuildSchema, appBuildStageSchema, appCapabilitiesOf, appDeployKeyCreateSchema, appDeployKeyCreatedSchema, appDeployKeySchema, appDevTokenSchema, appDistributionSchema, appEventTypeSchema, appHandleSchema, appInstallRequestSchema, appInstallationAppSchema, appInstallationSchema, appInstallationSelfSchema, appInstallationSettingsSchema, appInstallationSettingsUpdateSchema, appInstallationStatusSchema, appInvocationSchema, appManifestProblems, appManifestSchema, appManifestWidens, appNetworkHostProblem, appNetworkHostSchema, appReleaseResultSchema, appRuntimeSchema, appScopeSchema, appSecretSummarySchema, appSessionSchema, appSettingFieldSchema, appStatusSchema, appVersionSchema, appVersionStatusSchema, approvalDecisionRequestSchema, approvalListQuerySchema, approvalPageSchema, approvalRequestSchema, approvalStatusSchema, approveClaimResultSchema, auditActorSchema, auditActorTypeSchema, auditLogListQuerySchema, auditLogOutcomeSchema, auditLogPageSchema, auditLogSchema, auditLogSourceSchema, bestSellingRuleOf, brandBriefProductSourceSchema, brandBriefReferenceSchema, brandBriefSchema, brandBriefViewSchema, buildAuthorizeUrl, buildQuery, bulkResultSchema, businessInfoPatchShape, businessInfoResponseShape, businessNumberSchema, calendarDateSchema, calendarDaySchema, calendarQuerySchema, cancelBySellerRequestSchema, cancelBySellerResultSchema, carrierSchema, cashReceiptSchema, categoryAttributeSchema, categoryCreateRequestSchema, categoryDeleteQuerySchema, categoryDeleteStrategySchema, categoryListQuerySchema, categoryPatchRequestSchema, categoryProductSortSchema, categorySchema, categorySearchResultSchema, categoryTreeNodeSchema, categoryWithPathSchema, changeStorefrontSlugRequestSchema, changeStorefrontTemplateRequestSchema, changeTypeSchema, claimDetailSchema, claimHoldResultSchema, claimListQuerySchema, claimReasonSchema, claimReturnAddressSchema, claimSchema, claimStatusSchema, claimTypeSchema, claimWebhookEventDataSchema, claimWebhookEventSchema, collectionCreateRequestSchema, collectionListQuerySchema, collectionPageSchema, collectionPatchRequestSchema, collectionPreviewRequestSchema, collectionPreviewSchema, collectionProductListQuerySchema, collectionProductSchema, collectionProductSortRequestSchema, collectionProductsAddRequestSchema, collectionProductsReplaceRequestSchema, collectionRuleCategoryIds, collectionRuleSchema, collectionRuleViewSchema, collectionRulesPredicate, collectionRulesSchema, collectionSchema, collectionStateCountsSchema, collectionStateOf, collectionStateSchema, commerceFlowCheckSchema, commerceFlowSchema, commerceFlowSummarySchema, commerceFlowVersionViewSchema, commerceNumberField, compareBySales, consentActionSchema, consentKindSchema, consentSourceSchema, couponBenefitSchema, couponConditionsInputSchema, couponConditionsSchema, couponDiscountOf, couponIssueListQuerySchema, couponIssueMethodSchema, couponIssueSchema, couponIssueStatusSchema, couponKindSchema, couponListQuerySchema, couponRedemptionListQuerySchema, couponRedemptionSchema, couponRejectReasonSchema, couponRuleSchema, couponSchema, couponStatsSchema, couponStatusSchema, couponTargetInputSchema, couponTargetSchema, createCommerceFlowRequestSchema, createCouponRequestSchema, createPkcePair, createStoreClient, createStoreSession, createStorefrontEditPreviewLinkRequestSchema, createStorefrontPreviewLinkRequestSchema, createStorefrontSiteRequestSchema, createStorefrontVersionRequestSchema, customInputLabelForbiddenWord, customInputRequestSchema, customInputSchema, customInputValueSchema, customerAddressSchema, customerConsentListQuerySchema, customerConsentSchema, customerConsentStateSchema, customerConsentsSchema, customerCouponSchema, customerDelegationRequestSchema, customerDelegationSchema, customerDetailSchema, customerInquiryCategorySchema, customerInquiryListQuerySchema, customerListQuerySchema, customerOrderSchema, customerPointsSchema, customerSortSchema, customerStatsSchema, customerSummarySchema, customerWithdrawalBlockedDetailsSchema, defaultFulfillmentMethodOf, deleteStorefrontFileRequestSchema, deliveryBundleKeyOf, deliveryFeeWaiverSchema, deliveryInfoSchema, deliveryMethodSchema, deliveryStatusSchema, deliveryTypeSchema, descendantCategoryIdsOf, describeScope, devStoreSchema, developerAppCreateSchema, developerAppSchema, developerInstallationSchema, developerOrgSchema, disableStorefrontHostingRequestSchema, discountRequestSchema, discountSchema, dispatchRequestSchema, easyPayProviderResponseSchema, easyPayProviderSchema, eventTypeSchema, exchangeAuthorizationCode, flowActionRequestSchema, flowActionResultSchema, flowActionSchema, flowFieldValueSchema, flowInputFieldSchema, flowNodeTypesSchema, flowNotificationWebhookEventDataSchema, flowNotificationWebhookEventSchema, flowPresetDetailSchema, flowPresetSummarySchema, flowRunSchema, flowRunStepSchema, flowSecretNameSchema, flowSecretSchema, flowStateChangedWebhookEventDataSchema, flowStateChangedWebhookEventSchema, flowStateSchema, formatOrderNo, fulfillBulkRequestSchema, fulfillRequestSchema, fulfillmentMethodOf, fulfillmentMethodSchema, fulfillmentMethodsOf, fulfillmentRuleOf, fulfillmentTypeOf, fulfillmentTypeResponseSchema, fulfillmentTypeSchema, holdOrderItemRequestSchema, inquiryListQuerySchema, inquirySchema, inquiryWebhookEventDataSchema, inquiryWebhookEventSchema, inventoryModeSchema, invitationDeliveryStatusSchema, invitationListQuerySchema, invitationSchema, inviteMemberRequestSchema, isCouponTargetLine, isEditableStorefrontPath, isInstallableScope, isReservedStoreCode, isReservedStorefrontSlug, isShowcaseStorefrontSlug, isStoreScope, isWebOrigin, issueCouponRequestSchema, issueCouponResultSchema, liveGateConditionCodeSchema, liveGateConditionSchema, liveGateErrorDetailsSchema, liveGateSchema, loginProviderSchema, loginProviderStatusSchema, loginProviderTestSchema, loginSettingsSchema, maskedSecretSchema, matchesCollectionRules, mcpRuleResultSchema, mcpToolEventSchema, migrateFlowVersionRequestSchema, migrateFlowVersionResultSchema, moveFlowStateRequestSchema, moveStorefrontFileRequestSchema, myAccountSchema, myStoreSchema, nightsOf, normalizeBusinessNumber, normalizeCouponCode, normalizeStorefrontHostname, normalizeStorefrontPath, oauthTokenResponseSchema, openFulfillmentUnion, optionGroupKindSchema, optionGroupRequestSchema, optionGroupSchema, optionModelRequestSchema, optionModelSchema, optionSelectionSchema, optionSortSchema, optionValueRequestSchema, optionValueSchema, orderCanceledWebhookEventDataSchema, orderCanceledWebhookEventSchema, orderDiscountSchema, orderFlowActionResultSchema, orderFulfilledWebhookEventDataSchema, orderFulfilledWebhookEventSchema, orderItemFlowSchema, orderItemFulfillmentSchema, orderItemFulfillmentSnapshotResponseSchema, orderItemFulfillmentSnapshotSchema, orderItemListQuerySchema, orderItemMemoResultSchema, orderItemPageSchema, orderItemSchema, orderItemStatusSchema, orderListQuerySchema, orderSchema, orderSummaryPageSchema, orderSummarySchema, orderWebhookEventDataSchema, orderWebhookEventSchema, pageInfoSchema, pageQueryShape, pageSchema, parseOrderNo, parseWebhookEvent, paymentAttemptRecordSchema, paymentAttemptStatusSchema, paymentCredentialSetSchema, paymentFailureWarningSchema, paymentListQuerySchema, paymentProviderSchema, paymentProviderVerificationSchema, paymentRecordDetailSchema, paymentRecordPageSchema, paymentRecordSchema, paymentRecordStatusSchema, paymentRefundRecordSchema, paymentSettingsSchema, pgEnvironmentSchema, pgPaymentMethodSchema, pgProviderSchema, pgVerificationResultSchema, pickupLocationRequestSchema, pickupLocationSchema, pickupLocationSnapshotSchema, planCodeSchema, planEntitlementsSchema, planErrorDetailsSchema, pointAdjustmentRequestSchema, pointAdjustmentResultSchema, pointEarnOf, pointEarnRateBpSchema, pointEarningEnabled, pointEntrySchema, pointExpiresAtOf, pointHistoryQuerySchema, pointLotSchema, pointPolicyNoticeSchema, pointPolicySchema, pointRestoreExpiresAtOf, pointUseOf, pointUsePolicySchema, policyDocumentUrlSchema, policyVersionSchema, portOneChannelSchema, portOneCredentialsInputSchema, productCreateRequestSchema, productFulfillmentCreateSchema, productFulfillmentResponseSchema, productFulfillmentSchema, productFulfillmentUpdateSchema, productImageRejection, productImageUrlSchema, productListQuerySchema, productOutOfStockWebhookEventDataSchema, productOutOfStockWebhookEventSchema, productPatchRequestSchema, productRefundPolicyOverride, productRefundPolicyPatchSchema, productReplaceRequestSchema, productSchema, productSortSchema, productStatusSchema, productSummarySchema, productVariantRequestSchema, productVariantSchema, proratedPaidRefundBase, proratedPointRefund, proratedRefundBase, publicPlanSchema, publicPlansSchema, publishStorefrontVersionRequestSchema, purchaseDecisionExtendResultSchema, purchaseDecisionRequestResultSchema, purchaseEarnPolicySchema, purchaseEarnRateOf, putBrandBriefRequestSchema, quoteDelivery, quotePricing, rankBestSelling, refreshUserToken, refundPolicyPatchSchema, refundPolicySchema, rejectClaimRequestSchema, remoteAreaLabelOf, remoteAreaOf, remoteAreaSchema, remoteSurchargeOf, replaceStorefrontSourceRequestSchema, replayFlowRequestSchema, replayFlowResultSchema, requiresCollection, requiresShipping, resolvePointPolicy, resolveRefundPolicy, resolveShippingPolicy, resolvedFlowSchema, responsePlanCodeSchema, responsePointEntryTypeSchema, reviewListQuerySchema, reviewReportResultSchema, reviewRewardPointOf, reviewRewardPolicySchema, reviewSchema, reviewSortSchema, reviewWebhookEventDataSchema, reviewWebhookEventSchema, rollbackStorefrontRequestSchema, runStorefrontCheckRequestSchema, salesReportGroupBySchema, salesReportQuerySchema, salesReportRowSchema, salesReportSchema, salesTotalsSchema, saveFlowDraftRequestSchema, saveFlowSecretRequestSchema, saveLoginProviderRequestSchema, savedCustomInputRequestSchema, savedCustomInputSchema, savedOptionGroupRequestSchema, savedOptionGroupSchema, savedOptionSetCompositionSchema, savedOptionSetFromProductRequestSchema, savedOptionSetFromProductResultSchema, savedOptionSetListQuerySchema, savedOptionSetRequestSchema, savedOptionSetSchema, savedOptionSetSortSchema, savedOptionValueRequestSchema, savedOptionValueSchema, scopeAccessSchema, scopesCoverEvent, searchStorefrontFilesRequestSchema, sellerAddressListQuerySchema, sellerAddressRequestSchema, sellerAddressSchema, sellerCancelPreviewSchema, sellerCustomerInquirySchema, setCalendarRequestSchema, setStorefrontVisibilityRequestSchema, settleCouponShortfall, settlementDetailQuerySchema, settlementDetailSchema, settlementListQuerySchema, settlementStatusSchema, settlementSummarySchema, settlementWithDetailsSchema, shippingAddressSchema, shippingPolicyPatchSchema, shippingPolicySchema, simulateFlowRequestSchema, simulateFlowResultSchema, simulationStepSchema, splitRefund, starterSellerTaskSchema, starterStageSchema, starterStatusSchema, starterStoreInfoFieldSchema, stayRangeSchema, staySnapshotSchema, storeCodeSchema, storeIndustrySchema, storeMemberRoleUpdatedSchema, storeMemberSchema, storePointPolicySchema, storeRoleSchema, storeSchema, storeScopeResourceSchema, storeSubscriptionSchema, storeTemplateSchema, storefrontBuildDependenciesSchema, storefrontBuildListQuerySchema, storefrontBuildSchema, storefrontBuildStatusSchema, storefrontCheckSchema, storefrontCheckStatusSchema, storefrontCustomDomainStatusSchema, storefrontCustomHostnameProblem, storefrontDependencyFindingSchema, storefrontDeploymentLogSchema, storefrontDeploymentSchema, storefrontDeploymentStepSchema, storefrontDnsRecordSchema, storefrontDomainSchema, storefrontDomainTxtNameOf, storefrontEditPreviewLabelOf, storefrontEditPreviewSchema, storefrontEditSessionSchema, storefrontEditSessionStatusSchema, storefrontEditingSchema, storefrontFileConflictDetailsSchema, storefrontFileContentQuerySchema, storefrontFileContentSchema, storefrontFileDeleteResultSchema, storefrontFileEncodingSchema, storefrontFileEntrySchema, storefrontFileHashSchema, storefrontFileListQuerySchema, storefrontFileListSchema, storefrontFileMoveResultSchema, storefrontFilePathSchema, storefrontFileSearchResultSchema, storefrontFileWriteResultSchema, storefrontHostingSchema, storefrontPreviewLinkSchema, storefrontProvisioningSchema, storefrontRuleResultSchema, storefrontRuleSeveritySchema, storefrontSiteDomainSchema, storefrontSiteDomainsSchema, storefrontSiteSchema, storefrontSiteSnapshotSchema, storefrontSiteStatusSchema, storefrontSlugAvailabilityQuerySchema, storefrontSlugAvailabilitySchema, storefrontSlugSchema, storefrontSourceArchivePartSchema, storefrontSourceArchiveQuerySchema, storefrontSourceBlobsResultSchema, storefrontSourceModeSchema, storefrontSourceRefSchema, storefrontSourceReplacedSchema, storefrontSourceSchema, storefrontSuspensionSchema, storefrontTemplateSchema, storefrontVerifyReportSchema, storefrontVersionCreatedSchema, storefrontVersionSchema, storefrontVersionStatusSchema, storefrontVisibilitySchema, subscriptionLimitsSchema, subscriptionStatusSchema, subscriptionUsageSchema, tossPaymentsCredentialsInputSchema, trackingStageResponseSchema, trackingUpdateResultSchema, unknownWebhookEventSchema, updateCouponRequestSchema, updateLoginProviderRequestSchema, updateLoginSettingsRequestSchema, updateMemberRoleRequestSchema, updatePaymentProviderRequestSchema, updatePaymentSettingsRequestSchema, updatePointPolicyRequestSchema, updateStoreRequestSchema, updateStoreTemplateRequestSchema, uploadExtensionOf, uploadPurposeSchema, uploadRejection, uploadRequestSchema, uploadStorefrontSourceBlobsRequestSchema, uploadTicketSchema, upsertPortOneProviderRequestSchema, upsertTossPaymentsProviderRequestSchema, variantStockRequestSchema, variantStockSchema, verifyPaymentProviderRequestSchema, webhookCreatedSchema, webhookDeliveryDetailSchema, webhookDeliveryErrorCodeSchema, webhookDeliveryListQuerySchema, webhookDeliveryResourceSchema, webhookDeliverySchema, webhookDeliveryStatusSchema, webhookEventSchema, webhookFailureReasonSchema, webhookRedeliverFailedResultSchema, webhookRequestHeaderSchema, webhookRequestSchema, webhookResourceTypeSchema, webhookSchema, webhookSecretRotatedSchema, webhookStatusSchema, webhookTestRequestSchema, webhookTestResultSchema, withdrawCustomerRequestSchema, writeDryRunResultSchema, writePreviewSchema, writeStorefrontFilesRequestSchema };
|