@sayren/store-sdk 0.41.0 → 0.42.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +74 -74
- package/dist/index.mjs +156 -156
- package/dist/server.mjs +1 -1
- package/dist/{webhook-headers-D974dE4T.mjs → webhook-headers-D0E3wPpS.mjs} +80 -2
- package/package.json +1 -1
- package/src/schemas/ai.ts +39 -32
- package/src/schemas/apps.ts +18 -15
- package/src/schemas/audit.ts +6 -3
- package/src/schemas/category.ts +36 -31
- package/src/schemas/claim.ts +14 -11
- package/src/schemas/collection.ts +75 -64
- package/src/schemas/commerce-flow.ts +106 -85
- package/src/schemas/coupon.ts +87 -79
- package/src/schemas/customer-delegation.ts +30 -27
- package/src/schemas/customer.ts +9 -6
- package/src/schemas/inventory.ts +28 -23
- package/src/schemas/login.ts +17 -10
- package/src/schemas/misc.ts +6 -3
- package/src/schemas/order.ts +69 -58
- package/src/schemas/payment.ts +107 -88
- package/src/schemas/points.ts +32 -27
- package/src/schemas/product-info.ts +12 -9
- package/src/schemas/product.ts +17 -14
- package/src/schemas/saved-option-set.ts +5 -5
- package/src/schemas/starter.ts +15 -12
- package/src/schemas/stay.ts +15 -12
- package/src/schemas/store.ts +122 -111
- package/src/schemas/storefront-hosting.ts +271 -219
- package/src/schemas/strict.ts +84 -0
- package/src/schemas/upload.ts +15 -12
package/dist/server.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as WEBHOOK_HEADERS, i as WEBHOOK_EVENT_TYPE_HEADER, n as WEBHOOK_DELIVERY_ID_HEADER, o as WEBHOOK_SIGNATURE_HEADER, r as WEBHOOK_EVENT_ID_HEADER, s as WEBHOOK_USER_AGENT, t as WEBHOOK_DEFAULT_TOLERANCE_SEC } from "./webhook-headers-
|
|
1
|
+
import { a as WEBHOOK_HEADERS, i as WEBHOOK_EVENT_TYPE_HEADER, n as WEBHOOK_DELIVERY_ID_HEADER, o as WEBHOOK_SIGNATURE_HEADER, r as WEBHOOK_EVENT_ID_HEADER, s as WEBHOOK_USER_AGENT, t as WEBHOOK_DEFAULT_TOLERANCE_SEC } from "./webhook-headers-D0E3wPpS.mjs";
|
|
2
2
|
//#region src/member-delegation.ts
|
|
3
3
|
/** 위임 실패 — `error`는 api 오류 코드(엔벨로프 `error.code`, 없으면 `http_{status}`) */
|
|
4
4
|
var MemberDelegationError = class extends Error {
|
|
@@ -1,4 +1,82 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
//#region src/schemas/strict.ts
|
|
3
|
+
/**
|
|
4
|
+
* 관리 API 요청 본문을 모르는 키까지 거르는 스키마로 바꾼다(이슈 #193, 2026-10-24). `z.object`는 모르는 키를 조용히 버려서
|
|
5
|
+
* 오타·잘못된 자리의 필드(예: 옵션 값에 둔 추가금)가 400 없이 사라지고 기본값으로 저장됐다. 원본은 응답 스키마와 하위 스키마를
|
|
6
|
+
* 공유하므로 고치지 않고, 안쪽 객체까지 `catchall: never`로 복제한다. `z.looseObject`처럼 catchall을 정한 객체는 그대로 둔다.
|
|
7
|
+
* 설명·예시(전역 등록부의 메타)는 복제본에 옮겨 OpenAPI 문서가 같다
|
|
8
|
+
*/
|
|
9
|
+
function strictRequest(schema) {
|
|
10
|
+
return strictOf(schema, /* @__PURE__ */ new Map());
|
|
11
|
+
}
|
|
12
|
+
function strictOf(schema, seen) {
|
|
13
|
+
const done = seen.get(schema);
|
|
14
|
+
if (done) return done;
|
|
15
|
+
const def = schema._zod.def;
|
|
16
|
+
const clone = (patch) => {
|
|
17
|
+
const next = schema.clone({
|
|
18
|
+
...def,
|
|
19
|
+
...patch
|
|
20
|
+
});
|
|
21
|
+
const meta = z.globalRegistry.get(schema);
|
|
22
|
+
if (meta) z.globalRegistry.add(next, meta);
|
|
23
|
+
return next;
|
|
24
|
+
};
|
|
25
|
+
let next;
|
|
26
|
+
switch (def.type) {
|
|
27
|
+
case "object":
|
|
28
|
+
next = clone({
|
|
29
|
+
shape: Object.fromEntries(Object.entries(def.shape).map(([key, value]) => [key, strictOf(value, seen)])),
|
|
30
|
+
catchall: def.catchall ?? z.never()
|
|
31
|
+
});
|
|
32
|
+
break;
|
|
33
|
+
case "array":
|
|
34
|
+
next = clone({ element: strictOf(def.element, seen) });
|
|
35
|
+
break;
|
|
36
|
+
case "optional":
|
|
37
|
+
case "nullable":
|
|
38
|
+
case "default":
|
|
39
|
+
case "prefault":
|
|
40
|
+
case "nonoptional":
|
|
41
|
+
case "readonly":
|
|
42
|
+
case "catch":
|
|
43
|
+
next = clone({ innerType: strictOf(def.innerType, seen) });
|
|
44
|
+
break;
|
|
45
|
+
case "union":
|
|
46
|
+
next = clone({ options: def.options.map((option) => strictOf(option, seen)) });
|
|
47
|
+
break;
|
|
48
|
+
case "intersection":
|
|
49
|
+
next = clone({
|
|
50
|
+
left: strictOf(def.left, seen),
|
|
51
|
+
right: strictOf(def.right, seen)
|
|
52
|
+
});
|
|
53
|
+
break;
|
|
54
|
+
case "pipe":
|
|
55
|
+
next = clone({ in: strictOf(def.in, seen) });
|
|
56
|
+
break;
|
|
57
|
+
case "record":
|
|
58
|
+
next = clone({ valueType: strictOf(def.valueType, seen) });
|
|
59
|
+
break;
|
|
60
|
+
case "tuple":
|
|
61
|
+
next = clone({
|
|
62
|
+
items: def.items.map((item) => strictOf(item, seen)),
|
|
63
|
+
rest: def.rest ? strictOf(def.rest, seen) : def.rest
|
|
64
|
+
});
|
|
65
|
+
break;
|
|
66
|
+
case "lazy": {
|
|
67
|
+
let inner;
|
|
68
|
+
next = clone({ getter: () => {
|
|
69
|
+
inner ??= strictOf(def.getter(), seen);
|
|
70
|
+
return inner;
|
|
71
|
+
} });
|
|
72
|
+
break;
|
|
73
|
+
}
|
|
74
|
+
default: next = schema;
|
|
75
|
+
}
|
|
76
|
+
seen.set(schema, next);
|
|
77
|
+
return next;
|
|
78
|
+
}
|
|
79
|
+
//#endregion
|
|
2
80
|
//#region src/schemas/audit.ts
|
|
3
81
|
/**
|
|
4
82
|
* 감사 로그와 승인 요청 — `/v1/audit-logs`, `/v1/approval-requests` (이슈 #36).
|
|
@@ -155,7 +233,7 @@ const approvalPageSchema = z.object({
|
|
|
155
233
|
items: z.array(approvalRequestSchema),
|
|
156
234
|
nextCursor: z.string().nullable().describe("다음 페이지 커서. 마지막 페이지면 null")
|
|
157
235
|
});
|
|
158
|
-
const approvalDecisionRequestSchema = z.object({ note: z.string().trim().max(500).optional().describe("승인·거절 메모 (최대 500자)") });
|
|
236
|
+
const approvalDecisionRequestSchema = strictRequest(z.object({ note: z.string().trim().max(500).optional().describe("승인·거절 메모 (최대 500자)") }));
|
|
159
237
|
//#endregion
|
|
160
238
|
//#region src/http.ts
|
|
161
239
|
/** 표준 엔벨로프의 $.error 상세 */
|
|
@@ -356,4 +434,4 @@ const WEBHOOK_HEADERS = {
|
|
|
356
434
|
deliveryId: WEBHOOK_DELIVERY_ID_HEADER
|
|
357
435
|
};
|
|
358
436
|
//#endregion
|
|
359
|
-
export { auditLogPageSchema as A, approvalPageSchema as C, auditActorTypeSchema as D, auditActorSchema as E, auditLogSourceSchema as M, writeDryRunResultSchema as N, auditLogListQuerySchema as O, writePreviewSchema as P, approvalListQuerySchema as S, approvalStatusSchema as T, resolveToken as _, WEBHOOK_HEADERS as a, agentPolicyDecisionSchema as b, APPROVAL_ALREADY_DECIDED_CODE as c, ApprovalPendingError as d, SAYREN_API_BASE_URL as f, path as g, createHttp as h, WEBHOOK_EVENT_TYPE_HEADER as i, auditLogSchema as j, auditLogOutcomeSchema as k, APPROVAL_REQUIRED_CODE as l, buildQuery as m, WEBHOOK_DELIVERY_ID_HEADER as n, WEBHOOK_SIGNATURE_HEADER as o, SAYREN_API_ORIGIN as p, WEBHOOK_EVENT_ID_HEADER as r, WEBHOOK_USER_AGENT as s, WEBHOOK_DEFAULT_TOLERANCE_SEC as t, ApiError as u, toApiError as v, approvalRequestSchema as w, approvalDecisionRequestSchema as x, unwrapData as y };
|
|
437
|
+
export { auditLogPageSchema as A, approvalPageSchema as C, auditActorTypeSchema as D, auditActorSchema as E, strictRequest as F, auditLogSourceSchema as M, writeDryRunResultSchema as N, auditLogListQuerySchema as O, writePreviewSchema as P, approvalListQuerySchema as S, approvalStatusSchema as T, resolveToken as _, WEBHOOK_HEADERS as a, agentPolicyDecisionSchema as b, APPROVAL_ALREADY_DECIDED_CODE as c, ApprovalPendingError as d, SAYREN_API_BASE_URL as f, path as g, createHttp as h, WEBHOOK_EVENT_TYPE_HEADER as i, auditLogSchema as j, auditLogOutcomeSchema as k, APPROVAL_REQUIRED_CODE as l, buildQuery as m, WEBHOOK_DELIVERY_ID_HEADER as n, WEBHOOK_SIGNATURE_HEADER as o, SAYREN_API_ORIGIN as p, WEBHOOK_EVENT_ID_HEADER as r, WEBHOOK_USER_AGENT as s, WEBHOOK_DEFAULT_TOLERANCE_SEC as t, ApiError as u, toApiError as v, approvalRequestSchema as w, approvalDecisionRequestSchema as x, unwrapData as y };
|
package/package.json
CHANGED
package/src/schemas/ai.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { storefrontSlugSchema } from "./storefront-hosting";
|
|
3
|
+
import { strictRequest } from "./strict";
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* 상점 플랫폼 AI 채팅(이슈 #124) — 대화·메시지·실행과 실행 이벤트 스트림의 계약.
|
|
@@ -388,12 +389,14 @@ export const storeSetupBriefSchema = z.object({
|
|
|
388
389
|
});
|
|
389
390
|
export type StoreSetupBrief = z.infer<typeof storeSetupBriefSchema>;
|
|
390
391
|
|
|
391
|
-
export const createAiConversationRequestSchema =
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
392
|
+
export const createAiConversationRequestSchema = strictRequest(
|
|
393
|
+
z.object({
|
|
394
|
+
/** 웹사이트에서 남긴 초안(#124 M2) — 연결한 사용자만 쓴다. 주면 첫 메시지로 실행을 시작한다 */
|
|
395
|
+
draftId: z.string().max(64).optional(),
|
|
396
|
+
/** 질문 화면의 답 — `draftId`와 함께만 쓴다. 초안 앞에 붙어 첫 메시지가 된다 */
|
|
397
|
+
brief: storeSetupBriefSchema.optional(),
|
|
398
|
+
}),
|
|
399
|
+
);
|
|
397
400
|
export type CreateAiConversationRequest = z.infer<typeof createAiConversationRequestSchema>;
|
|
398
401
|
|
|
399
402
|
export const createAiConversationResponseSchema = z.object({
|
|
@@ -403,20 +406,22 @@ export const createAiConversationResponseSchema = z.object({
|
|
|
403
406
|
});
|
|
404
407
|
export type CreateAiConversationResponse = z.infer<typeof createAiConversationResponseSchema>;
|
|
405
408
|
|
|
406
|
-
export const sendAiMessageRequestSchema =
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
409
|
+
export const sendAiMessageRequestSchema = strictRequest(
|
|
410
|
+
z
|
|
411
|
+
.object({
|
|
412
|
+
text: z.string().trim().max(AI_MESSAGE_MAX_CHARS),
|
|
413
|
+
attachments: z.array(aiAttachmentSchema).max(AI_MESSAGE_MAX_ATTACHMENTS).default([]),
|
|
414
|
+
/** 선택지 버튼으로 보낸 답이면 그 값(화면 표시는 `text`) */
|
|
415
|
+
choice: z.string().max(200).optional(),
|
|
416
|
+
/** 편집 미리보기에서 고른 요소(#65) — 모델은 이 요소를 기준으로 고친다 */
|
|
417
|
+
selection: aiPreviewSelectionSchema.optional(),
|
|
418
|
+
/** 디자인 탭에서 고른 아트보드(2026-10-14) — 모델은 그 아트보드를 기준으로 고친다 */
|
|
419
|
+
designSelection: aiDesignSelectionSchema.optional(),
|
|
420
|
+
})
|
|
421
|
+
.refine((body) => body.text.length > 0 || body.attachments.length > 0, {
|
|
422
|
+
message: "내용이나 첨부가 있어야 합니다",
|
|
423
|
+
}),
|
|
424
|
+
);
|
|
420
425
|
export type SendAiMessageRequest = z.input<typeof sendAiMessageRequestSchema>;
|
|
421
426
|
|
|
422
427
|
export const sendAiMessageResponseSchema = aiStreamAccessSchema.extend({
|
|
@@ -622,18 +627,20 @@ export const STORE_DRAFT_ATTACHMENT_MAX_BYTES = 10 * 1024 * 1024;
|
|
|
622
627
|
/** 연결하지 않은 초안을 지우기까지 */
|
|
623
628
|
export const STORE_DRAFT_RETENTION_DAYS = 7;
|
|
624
629
|
|
|
625
|
-
export const createStoreDraftRequestSchema =
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
630
|
+
export const createStoreDraftRequestSchema = strictRequest(
|
|
631
|
+
z.object({
|
|
632
|
+
text: z.string().trim().min(1).max(AI_MESSAGE_MAX_CHARS),
|
|
633
|
+
attachments: z
|
|
634
|
+
.array(
|
|
635
|
+
z.object({
|
|
636
|
+
filename: z.string().trim().min(1).max(200),
|
|
637
|
+
size: z.number().int().positive().max(STORE_DRAFT_ATTACHMENT_MAX_BYTES),
|
|
638
|
+
}),
|
|
639
|
+
)
|
|
640
|
+
.max(STORE_DRAFT_MAX_ATTACHMENTS)
|
|
641
|
+
.default([]),
|
|
642
|
+
}),
|
|
643
|
+
);
|
|
637
644
|
export type CreateStoreDraftRequest = z.input<typeof createStoreDraftRequestSchema>;
|
|
638
645
|
|
|
639
646
|
export const createStoreDraftResponseSchema = z.object({
|
package/src/schemas/apps.ts
CHANGED
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
appSettingFieldSchema,
|
|
9
9
|
} from "./app-manifest";
|
|
10
10
|
import { webhookDeliveryErrorCodeSchema, webhookDeliveryStatusSchema } from "./misc";
|
|
11
|
+
import { strictRequest } from "./strict";
|
|
11
12
|
|
|
12
13
|
/**
|
|
13
14
|
* 설치 앱 관리 API 계약 (이슈 #22) — `/v1/apps/installations/**`(상점이 설치한 앱), `/v1/app/installation`(설치 토큰 자기 조회).
|
|
@@ -183,21 +184,23 @@ export type AppInstallationActivity = z.infer<typeof appInstallationActivitySche
|
|
|
183
184
|
* - `{ result }`: accounts 설치 동의 결과(1회용 JWT). 개발자 포털 [내 상점에 설치]가 이 길로 온다
|
|
184
185
|
* - `{ appId, settings }`: 상점 플랫폼 앱 목록 › 설치 화면에서 동의하고 설정까지 채워 바로 설치한다
|
|
185
186
|
*/
|
|
186
|
-
export const appInstallRequestSchema =
|
|
187
|
-
z.
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
187
|
+
export const appInstallRequestSchema = strictRequest(
|
|
188
|
+
z.union([
|
|
189
|
+
z.object({
|
|
190
|
+
result: z
|
|
191
|
+
.string()
|
|
192
|
+
.min(1)
|
|
193
|
+
.describe("accounts가 발급한 설치 결과 JWT(`typ: sayren-install+jwt`)"),
|
|
194
|
+
}),
|
|
195
|
+
z.object({
|
|
196
|
+
appId: z.string().min(1),
|
|
197
|
+
settings: z
|
|
198
|
+
.record(z.string(), settingsValueSchema)
|
|
199
|
+
.default({})
|
|
200
|
+
.describe("설치와 함께 저장할 설정. 비밀 필드도 여기서 넣는다"),
|
|
201
|
+
}),
|
|
202
|
+
]),
|
|
203
|
+
);
|
|
201
204
|
export type AppInstallRequest = z.infer<typeof appInstallRequestSchema>;
|
|
202
205
|
|
|
203
206
|
/** 앱 목록 정보 — 현재 버전이 올린 아이콘·README(없으면 매니페스트 `iconUrl`) */
|
package/src/schemas/audit.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
import { strictRequest } from "./strict";
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* 감사 로그와 승인 요청 — `/v1/audit-logs`, `/v1/approval-requests` (이슈 #36).
|
|
@@ -261,7 +262,9 @@ export const approvalPageSchema = z.object({
|
|
|
261
262
|
});
|
|
262
263
|
export type ApprovalPage = z.infer<typeof approvalPageSchema>;
|
|
263
264
|
|
|
264
|
-
export const approvalDecisionRequestSchema =
|
|
265
|
-
|
|
266
|
-
|
|
265
|
+
export const approvalDecisionRequestSchema = strictRequest(
|
|
266
|
+
z.object({
|
|
267
|
+
note: z.string().trim().max(500).optional().describe("승인·거절 메모 (최대 500자)"),
|
|
268
|
+
}),
|
|
269
|
+
);
|
|
267
270
|
export type ApprovalDecisionRequest = z.infer<typeof approvalDecisionRequestSchema>;
|
package/src/schemas/category.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
import { strictRequest } from "./strict";
|
|
2
3
|
|
|
3
4
|
export const categoryProductSortSchema = z.enum([
|
|
4
5
|
"RECENT",
|
|
@@ -70,40 +71,44 @@ export const categoryAttributeSchema = z.object({
|
|
|
70
71
|
|
|
71
72
|
export type CategoryAttribute = z.infer<typeof categoryAttributeSchema>;
|
|
72
73
|
|
|
73
|
-
export const categoryCreateRequestSchema =
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
74
|
+
export const categoryCreateRequestSchema = strictRequest(
|
|
75
|
+
z.object({
|
|
76
|
+
name: z.string().min(1).max(100),
|
|
77
|
+
parentId: z.string().nullable().optional(),
|
|
78
|
+
sortOrder: z.number().int().optional(),
|
|
79
|
+
visible: z.boolean().optional(),
|
|
80
|
+
showPc: z.boolean().optional(),
|
|
81
|
+
showMobile: z.boolean().optional(),
|
|
82
|
+
productSort: categoryProductSortSchema.optional(),
|
|
83
|
+
metaTitle: z.string().max(100).nullable().optional(),
|
|
84
|
+
metaDescription: z.string().max(300).nullable().optional(),
|
|
85
|
+
metaKeywords: z.string().max(200).nullable().optional(),
|
|
86
|
+
}),
|
|
87
|
+
);
|
|
85
88
|
|
|
86
89
|
export type CategoryCreateRequest = z.infer<typeof categoryCreateRequestSchema>;
|
|
87
90
|
|
|
88
|
-
export const categoryPatchRequestSchema =
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
91
|
+
export const categoryPatchRequestSchema = strictRequest(
|
|
92
|
+
z.object({
|
|
93
|
+
name: z.string().min(1).max(100).optional(),
|
|
94
|
+
parentId: z.string().nullable().optional(),
|
|
95
|
+
sortOrder: z.number().int().optional(),
|
|
96
|
+
visible: z.boolean().optional(),
|
|
97
|
+
showPc: z.boolean().optional(),
|
|
98
|
+
showMobile: z.boolean().optional(),
|
|
99
|
+
productSort: categoryProductSortSchema.optional(),
|
|
100
|
+
metaTitle: z.string().max(100).nullable().optional(),
|
|
101
|
+
metaDescription: z.string().max(300).nullable().optional(),
|
|
102
|
+
metaKeywords: z.string().max(200).nullable().optional(),
|
|
103
|
+
flowId: z
|
|
104
|
+
.string()
|
|
105
|
+
.nullable()
|
|
106
|
+
.optional()
|
|
107
|
+
.describe(
|
|
108
|
+
"주문 흐름(이슈 #115) — 게시한 흐름 id, null이면 기본 흐름. 맨 아래 카테고리에만 지정한다(400 `CATEGORY_NOT_LEAF`). 게시하지 않은 흐름은 409 `FLOW_NOT_PUBLISHED`",
|
|
109
|
+
),
|
|
110
|
+
}),
|
|
111
|
+
);
|
|
107
112
|
|
|
108
113
|
export type CategoryPatchRequest = z.infer<typeof categoryPatchRequestSchema>;
|
|
109
114
|
|
package/src/schemas/claim.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { commerceNumberField } from "./commerce-number";
|
|
3
3
|
import { type PageParams, pageQueryShape } from "./common";
|
|
4
|
+
import { strictRequest } from "./strict";
|
|
4
5
|
|
|
5
6
|
export const claimTypeSchema = z.enum(["CANCEL", "RETURN", "EXCHANGE"]);
|
|
6
7
|
|
|
@@ -164,17 +165,19 @@ export const claimHoldResultSchema = z.object({ claimId: z.string(), holdExpires
|
|
|
164
165
|
|
|
165
166
|
export type ClaimHoldResult = z.infer<typeof claimHoldResultSchema>;
|
|
166
167
|
|
|
167
|
-
export const rejectClaimRequestSchema =
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
168
|
+
export const rejectClaimRequestSchema = strictRequest(
|
|
169
|
+
z.object({
|
|
170
|
+
reason: z.enum([
|
|
171
|
+
"USED_PRODUCT",
|
|
172
|
+
"MISSING_COMPONENT",
|
|
173
|
+
"PERIOD_EXPIRED",
|
|
174
|
+
"BUYER_FAULT_DAMAGE",
|
|
175
|
+
"ETC",
|
|
176
|
+
]),
|
|
177
|
+
detail: z.string().min(10).max(1000),
|
|
178
|
+
evidenceImages: z.array(z.url()).max(5).optional(),
|
|
179
|
+
}),
|
|
180
|
+
);
|
|
178
181
|
|
|
179
182
|
export type RejectClaimRequest = z.infer<typeof rejectClaimRequestSchema>;
|
|
180
183
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { type PageParams, pageQueryShape, pageSchema } from "./common";
|
|
3
3
|
import { productImageUrlSchema } from "./product";
|
|
4
|
+
import { strictRequest } from "./strict";
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* 컬렉션 (이슈 #83) — 상품을 어디에 보여 줄지 정하는 묶음이다. 카테고리는 상품이 무엇인지를 정하고 상품당 하나지만, 컬렉션은
|
|
@@ -432,33 +433,35 @@ const periodRefine = (
|
|
|
432
433
|
* 직접 선택(`MANUAL`)은 `productIds`가 처음 담을 상품과 순서다. 조건 자동(`AUTOMATIC`)은 `rules`가 있어야 하고 `productIds`를 받지 않는다.
|
|
433
434
|
* `productSort`를 생략하면 직접 선택은 `MANUAL`, 조건 자동은 `LATEST`(판매량 상위 조건이 있으면 `BEST_SELLING`)다. 조건 자동은 `MANUAL`을 쓸 수 없다.
|
|
434
435
|
*/
|
|
435
|
-
export const collectionCreateRequestSchema =
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
.
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
436
|
+
export const collectionCreateRequestSchema = strictRequest(
|
|
437
|
+
z
|
|
438
|
+
.object({
|
|
439
|
+
title: z.string().trim().min(1).max(MAX_COLLECTION_TITLE).describe("제목"),
|
|
440
|
+
slug: slugField.optional(),
|
|
441
|
+
type: z
|
|
442
|
+
.enum(COLLECTION_TYPES)
|
|
443
|
+
.default("MANUAL")
|
|
444
|
+
.describe("유형. `MANUAL`(직접 선택)·`AUTOMATIC`(조건 자동). 만든 뒤 바꿀 수 없다"),
|
|
445
|
+
rules: collectionRulesSchema
|
|
446
|
+
.optional()
|
|
447
|
+
.describe("조건 자동 컬렉션의 조건(AND). 조건 자동이면 필수, 직접 선택이면 줄 수 없다"),
|
|
448
|
+
description: commonFields.description.default(""),
|
|
449
|
+
imageUrl: commonFields.imageUrl.optional(),
|
|
450
|
+
productSort: commonFields.productSort
|
|
451
|
+
.optional()
|
|
452
|
+
.describe(
|
|
453
|
+
"스토어프론트 상품 기본 정렬. 생략하면 직접 선택은 `MANUAL`, 조건 자동은 `LATEST`(판매량 상위 조건이 있으면 `BEST_SELLING`)다. 조건 자동은 `MANUAL`을 쓸 수 없다",
|
|
454
|
+
),
|
|
455
|
+
visible: commonFields.visible.default(true),
|
|
456
|
+
startsAt: commonFields.startsAt,
|
|
457
|
+
endsAt: commonFields.endsAt,
|
|
458
|
+
sortOrder: commonFields.sortOrder.optional().describe("목록 순서. 생략하면 맨 뒤"),
|
|
459
|
+
metaTitle: commonFields.metaTitle.optional(),
|
|
460
|
+
metaDescription: commonFields.metaDescription.optional(),
|
|
461
|
+
productIds: productIdsField.optional(),
|
|
462
|
+
})
|
|
463
|
+
.superRefine(periodRefine),
|
|
464
|
+
);
|
|
462
465
|
|
|
463
466
|
export type CollectionCreateRequest = z.infer<typeof collectionCreateRequestSchema>;
|
|
464
467
|
export type CollectionCreateRequestInput = z.input<typeof collectionCreateRequestSchema>;
|
|
@@ -467,45 +470,51 @@ export type CollectionCreateRequestInput = z.input<typeof collectionCreateReques
|
|
|
467
470
|
* `PATCH /v1/collections/{collectionId}` — 준 항목만 바뀐다. `type`을 주면 400 `COLLECTION_TYPE_IMMUTABLE`이다.
|
|
468
471
|
* `revision`을 주면 그 번호일 때만 바뀌고, 그 사이 다른 수정이 있었으면 409 `COLLECTION_CHANGED`다.
|
|
469
472
|
*/
|
|
470
|
-
export const collectionPatchRequestSchema =
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
473
|
+
export const collectionPatchRequestSchema = strictRequest(
|
|
474
|
+
z
|
|
475
|
+
.object({
|
|
476
|
+
title: z.string().trim().min(1).max(MAX_COLLECTION_TITLE).optional(),
|
|
477
|
+
slug: slugField.optional(),
|
|
478
|
+
type: z.string().optional().describe("바꿀 수 없다. 주면 400 `COLLECTION_TYPE_IMMUTABLE`"),
|
|
479
|
+
rules: collectionRulesSchema
|
|
480
|
+
.optional()
|
|
481
|
+
.describe(
|
|
482
|
+
"조건 자동 컬렉션의 조건을 통째로 바꾼다. 직접 선택 컬렉션에 주면 400 `COLLECTION_RULES_NOT_ALLOWED`",
|
|
483
|
+
),
|
|
484
|
+
description: commonFields.description.optional(),
|
|
485
|
+
imageUrl: commonFields.imageUrl.optional(),
|
|
486
|
+
productSort: commonFields.productSort.optional(),
|
|
487
|
+
visible: commonFields.visible.optional(),
|
|
488
|
+
startsAt: commonFields.startsAt,
|
|
489
|
+
endsAt: commonFields.endsAt,
|
|
490
|
+
sortOrder: commonFields.sortOrder.optional(),
|
|
491
|
+
metaTitle: commonFields.metaTitle.optional(),
|
|
492
|
+
metaDescription: commonFields.metaDescription.optional(),
|
|
493
|
+
revision: z.number().int().min(0).optional().describe("읽은 수정 번호"),
|
|
494
|
+
})
|
|
495
|
+
.superRefine(periodRefine),
|
|
496
|
+
);
|
|
492
497
|
|
|
493
498
|
export type CollectionPatchRequest = z.infer<typeof collectionPatchRequestSchema>;
|
|
494
499
|
|
|
495
500
|
/** `PUT /v1/collections/{collectionId}/products` — 담긴 상품을 통째로 바꾼다(순서 포함) */
|
|
496
|
-
export const collectionProductsReplaceRequestSchema =
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
501
|
+
export const collectionProductsReplaceRequestSchema = strictRequest(
|
|
502
|
+
z.object({
|
|
503
|
+
productIds: productIdsField,
|
|
504
|
+
revision: z.number().int().min(0).optional().describe("읽은 수정 번호"),
|
|
505
|
+
}),
|
|
506
|
+
);
|
|
500
507
|
|
|
501
508
|
export type CollectionProductsReplaceRequest = z.infer<
|
|
502
509
|
typeof collectionProductsReplaceRequestSchema
|
|
503
510
|
>;
|
|
504
511
|
|
|
505
512
|
/** `POST /v1/collections/{collectionId}/products` — 뒤에 붙인다. 이미 담긴 상품은 그 자리에 둔다 */
|
|
506
|
-
export const collectionProductsAddRequestSchema =
|
|
507
|
-
|
|
508
|
-
|
|
513
|
+
export const collectionProductsAddRequestSchema = strictRequest(
|
|
514
|
+
z.object({
|
|
515
|
+
productIds: z.array(z.string().min(1)).min(1).max(MAX_COLLECTION_PRODUCTS_PER_ADD),
|
|
516
|
+
}),
|
|
517
|
+
);
|
|
509
518
|
|
|
510
519
|
export type CollectionProductsAddRequest = z.infer<typeof collectionProductsAddRequestSchema>;
|
|
511
520
|
|
|
@@ -562,14 +571,16 @@ export const collectionProductListQuerySchema = z.object({
|
|
|
562
571
|
});
|
|
563
572
|
|
|
564
573
|
/** `POST /v1/collections/preview` — 저장하지 않고 조건으로 나오는 상품을 본다 */
|
|
565
|
-
export const collectionPreviewRequestSchema =
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
574
|
+
export const collectionPreviewRequestSchema = strictRequest(
|
|
575
|
+
z.object({
|
|
576
|
+
rules: collectionRulesSchema,
|
|
577
|
+
productSort: collectionProductSortRequestSchema
|
|
578
|
+
.optional()
|
|
579
|
+
.describe(
|
|
580
|
+
"미리보기 순서. 생략하면 `LATEST`(판매량 상위 조건이 있으면 `BEST_SELLING`)이고, `MANUAL`이면 `LATEST`다",
|
|
581
|
+
),
|
|
582
|
+
}),
|
|
583
|
+
);
|
|
573
584
|
|
|
574
585
|
export type CollectionPreviewRequest = z.infer<typeof collectionPreviewRequestSchema>;
|
|
575
586
|
|