@sayren/storefront-sdk 0.16.0 → 0.17.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.
@@ -1,4 +1,4 @@
1
- import { pt as ApiError, t as createStorefrontClient } from "../client-BrDY5HO3.mjs";
1
+ import { pt as ApiError, t as createStorefrontClient } from "../client-CBAgQuTc.mjs";
2
2
  //#region src/auth/index.ts
3
3
  /** 소셜 로그인 실패 — 구매자가 취소했거나(`access_denied`) state가 맞지 않는다 */
4
4
  var IdpCallbackError = class extends Error {
@@ -61,6 +61,77 @@ const myOrderItemFulfillmentSnapshotSchema = openUnion(z.discriminatedUnion("typ
61
61
  requiresShipping: z.literal(false)
62
62
  })])).describe("주문 시점 이행 스냅샷. 값이 추가될 수 있어 SDK는 모르는 유형을 `OTHER`로 읽는다");
63
63
  //#endregion
64
+ //#region src/schemas/product-options.ts
65
+ /**
66
+ * 추가 선택·직접 입력 (이슈 #59) — 상품 상세의 옵션 표시, 장바구니 담기·바로 구매 요청, 줄 스냅샷의 공통 모양.
67
+ *
68
+ * 추가 선택은 재고가 없는 옵션이다. 구매자가 조합(옵션이 없으면 상품)을 고른 뒤 옵션명마다 값 하나를 더 고르고, 값의 추가금이
69
+ * 단가에 더해진다. 직접 입력은 구매자가 글자를 적는 항목(각인 문구 등)이다. 추가 선택 값이나 직접 입력값이 다르면 장바구니에서 다른 줄이다.
70
+ */
71
+ const addonGroupViewSchema = z.object({
72
+ groupId: z.string(),
73
+ name: z.string(),
74
+ required: z.boolean().describe("꼭 골라야 하는가. false면 고르지 않아도 된다(「선택 안 함」)"),
75
+ values: z.array(z.object({
76
+ valueId: z.string(),
77
+ name: z.string(),
78
+ additionalPrice: z.number().int().describe("이 값을 고르면 단가에 더하는 금액(원, 0 이상)")
79
+ })).describe("고를 수 있는 값(셀러가 끈 값은 빠진다)")
80
+ });
81
+ const customInputViewSchema = z.object({
82
+ inputId: z.string(),
83
+ label: z.string().describe("항목명"),
84
+ placeholder: z.string().nullable().describe("입력 안내. 없으면 null"),
85
+ maxLength: z.number().int().describe("최대 글자 수"),
86
+ required: z.boolean()
87
+ });
88
+ /**
89
+ * 직접 입력값 정규화 — NFC로 모으고, 제어 문자(C0·C1·줄바꿈·줄·문단 구분자)는 공백으로, 제로폭·양방향 제어 문자는 지운 뒤
90
+ * 앞뒤 공백을 지운다. 서버가 저장하는 값이 이것이다. 빈 문자열이면 입력하지 않은 것으로 본다.
91
+ * (양방향 제어 문자를 두면 셀러 주문 화면의 각인 문구가 뒤집혀 보이게 할 수 있다.)
92
+ */
93
+ function normalizeCustomInputValue(value) {
94
+ return value.normalize("NFC").replace(/[\u0000-\u001f\u007f-\u009f\u2028\u2029]/g, " ").replace(/[\u200b-\u200f\u202a-\u202e\u2060-\u2064\u2066-\u2069\ufeff]/g, "").trim();
95
+ }
96
+ /**
97
+ * 직접 입력값의 글자 수 — 정규화한 값의 **코드 포인트 수**다(이모지 하나가 1자). 서버의 최대 글자 수 판정(`CUSTOM_INPUT_TOO_LONG`)과
98
+ * 스토어프론트·콘솔의 글자 수 표시가 이 함수 하나를 쓴다. `String.length`(UTF-16)로 세면 이모지가 2자로 세어 판정이 갈린다.
99
+ */
100
+ function customInputLength(value) {
101
+ return [...normalizeCustomInputValue(value)].length;
102
+ }
103
+ /** 담기·바로 구매 요청의 직접 입력값 한 칸. 앞뒤 공백을 지우고, 빈 값은 입력하지 않은 것으로 본다 */
104
+ const customInputEntrySchema = z.object({
105
+ inputId: z.string(),
106
+ value: z.string().max(1e3)
107
+ });
108
+ /** 고른 추가 선택 한 칸 — 옵션명(`groupId`)과 그 옵션명의 값(`valueId`) */
109
+ const addonSelectionSchema = z.object({
110
+ groupId: z.string(),
111
+ valueId: z.string()
112
+ });
113
+ /** 요청의 추가 선택·직접 입력 필드 — 담기·장바구니 수정·바로 구매가 같은 이름을 쓴다 */
114
+ const lineSelectionRequestShape = {
115
+ addons: z.array(addonSelectionSchema).max(20).optional().describe("고른 추가 선택(옵션명마다 하나, 고르지 않은 옵션명은 빼고 보낸다). 필수 옵션명을 빼면 400 `ADDON_OPTION_REQUIRED`, 없거나 꺼진 값이거나 한 옵션명에서 둘을 고르면 400 `ADDON_OPTION_NOT_FOUND`"),
116
+ customInputs: z.array(customInputEntrySchema).max(20).optional().describe("직접 입력값. 필수 항목을 비우면 400 `CUSTOM_INPUT_REQUIRED`, 최대 글자 수를 넘으면 400 `CUSTOM_INPUT_TOO_LONG`, 없는 항목이면 400 `CUSTOM_INPUT_NOT_FOUND`")
117
+ };
118
+ /** 줄의 옵션 선택 스냅샷 한 칸 — 조합 옵션은 옵션명마다 한 칸이고 추가금이 0이다(조합 추가금은 단가에 들어 있다) */
119
+ const optionSelectionViewSchema = z.object({
120
+ kind: z.string().describe("옵션명 방식. 현재 값은 `combination`·`addon`이고 값이 추가될 수 있어 문자열로 받는다"),
121
+ groupName: z.string(),
122
+ valueName: z.string(),
123
+ additionalPrice: z.number().int()
124
+ });
125
+ const customInputValueViewSchema = z.object({
126
+ label: z.string().describe("항목명"),
127
+ value: z.string().describe("구매자가 적은 값")
128
+ });
129
+ /** 응답 줄의 표시 필드 — 장바구니·주문서·주문상품이 같은 이름을 쓴다 */
130
+ const lineSelectionResponseShape = {
131
+ optionSelections: z.array(optionSelectionViewSchema).optional().default([]).describe("옵션 선택(조합 옵션 + 추가 선택). 옵션이 없으면 빈 배열. 서버는 항상 싣는다"),
132
+ customInputs: z.array(customInputValueViewSchema).optional().default([]).describe("직접 입력값(항목명과 값). 없으면 빈 배열. 서버는 항상 싣는다")
133
+ };
134
+ //#endregion
64
135
  //#region src/schemas/coupon.ts
65
136
  /**
66
137
  * 스토어프론트 쿠폰 계약 (이슈 #47).
@@ -75,7 +146,8 @@ const myOrderItemFulfillmentSnapshotSchema = openUnion(z.discriminatedUnion("typ
75
146
  const couponApplicationSchema = z.object({
76
147
  issueId: z.string().min(1).optional().describe("내 쿠폰(발급 쿠폰) id — `GET /me/coupons`의 `issueId`"),
77
148
  code: z.string().trim().min(1).max(30).optional().describe("쿠폰 코드. 대소문자를 구분하지 않는다"),
78
- optionId: z.string().optional().describe("상품 쿠폰을 걸 주문서 항목의 `optionId`. 생략하면 할인이 가장 큰 항목에 건다. 주문 쿠폰에는 쓰지 않는다")
149
+ optionId: z.string().optional().describe("상품 쿠폰을 걸 주문서 항목의 `optionId`. 생략하면 할인이 가장 큰 항목에 건다. 주문 쿠폰에는 쓰지 않는다. 같은 `optionId`의 줄이 여럿이면 적용하지 못한다(`LINE_AMBIGUOUS`) — 그때는 `lineId`를 쓴다"),
150
+ lineId: z.string().optional().describe("상품 쿠폰을 걸 주문서 줄의 `lineId`(이슈 #59). 주면 `optionId`보다 우선한다. 주문 쿠폰에는 쓰지 않는다")
79
151
  }).refine((value) => (value.issueId ? 1 : 0) + (value.code ? 1 : 0) === 1, { message: "issueId와 code 중 하나만 보내 주십시오" });
80
152
  /** 한 번에 적용할 수 있는 쿠폰 수 */
81
153
  const MAX_COUPONS_PER_CHECKOUT = 5;
@@ -96,8 +168,9 @@ const couponResultSchema = z.object({
96
168
  benefit: couponBenefitViewSchema,
97
169
  applied: z.boolean(),
98
170
  discountAmount: z.number().int().describe("이 쿠폰의 할인액. 적용하지 못했으면 0"),
99
- rejectReason: z.string().nullable().describe("적용하지 못한 사유. `MIN_ORDER_AMOUNT`(최소 주문 금액 미달)·`NO_ELIGIBLE_ITEMS`(대상 상품 없음)·`NOT_STARTED`·`EXPIRED`·`MEMBER_ONLY`(회원 전용)·`STACK_LIMIT`(같은 종류 쿠폰이 이미 적용됨)·`DUPLICATE_COUPON`·`LINE_NOT_ELIGIBLE`·`NO_DISCOUNT`·`IN_USE`(다른 결제에서 쓰는 중)·`LIMIT_REACHED`(1인 사용 한도)·`EXHAUSTED`(총 사용 한도)·`INACTIVE`(중지된 쿠폰). 값이 늘 수 있다"),
100
- optionId: z.string().nullable().describe("상품 쿠폰이 걸린 주문서 항목의 `optionId`. 그 밖은 null")
171
+ rejectReason: z.string().nullable().describe("적용하지 못한 사유. `MIN_ORDER_AMOUNT`(최소 주문 금액 미달)·`NO_ELIGIBLE_ITEMS`(대상 상품 없음)·`NOT_STARTED`·`EXPIRED`·`MEMBER_ONLY`(회원 전용)·`STACK_LIMIT`(같은 종류 쿠폰이 이미 적용됨)·`DUPLICATE_COUPON`·`LINE_NOT_ELIGIBLE`·`NO_DISCOUNT`·`LINE_AMBIGUOUS`(같은 `optionId`의 줄이 여럿, `lineId`로 지목한다)·`IN_USE`(다른 결제에서 쓰는 중)·`LIMIT_REACHED`(1인 사용 한도)·`EXHAUSTED`(총 사용 한도)·`INACTIVE`(중지된 쿠폰). 값이 늘 수 있다"),
172
+ optionId: z.string().nullable().describe("상품 쿠폰이 걸린 주문서 항목의 `optionId`. 그 밖은 null"),
173
+ lineId: z.string().nullable().optional().default(null).describe("상품 쿠폰이 걸린 주문서 줄의 `lineId`(이슈 #59). 그 밖은 null. 서버는 항상 싣는다")
101
174
  });
102
175
  /** 쿠폰을 반영한 금액 — 결제 금액 = productAmount − couponDiscountAmount + deliveryFee − deliveryDiscountAmount */
103
176
  const pricingAmountsSchema = z.object({
@@ -119,6 +192,7 @@ const checkoutPricingSchema = z.object({
119
192
  coupons: z.array(couponResultSchema).describe("요청한 쿠폰마다 한 줄, 요청 순서대로"),
120
193
  items: z.array(z.object({
121
194
  optionId: z.string().nullable(),
195
+ lineId: z.string().optional().describe("주문서 줄의 `lineId`(이슈 #59). 서버는 항상 싣는다"),
122
196
  couponDiscountAmount: z.number().int().describe("이 항목에 들어간 쿠폰 할인")
123
197
  })).describe("주문서 항목별 쿠폰 할인 — 주문서 `items`와 같은 순서"),
124
198
  payable: z.boolean().describe("이 금액으로 결제를 시작할 수 있는가 — 적용하지 못한 쿠폰이 있거나 결제 금액이 100원 미만이면 false")
@@ -164,7 +238,8 @@ const createCheckoutRequestSchema = z.object({
164
238
  directItem: z.object({
165
239
  productId: z.string(),
166
240
  optionId: z.string().optional(),
167
- quantity: z.number().int().min(1)
241
+ quantity: z.number().int().min(1),
242
+ ...lineSelectionRequestShape
168
243
  }).optional()
169
244
  }).refine((value) => value.cartItemIds ? !value.directItem : !!value.directItem, { message: "cartItemIds와 directItem 중 하나만 전달해야 합니다" });
170
245
  /** 지역 추가 배송비 구분 — 우편번호로 판정한다 */
@@ -195,13 +270,15 @@ const checkoutDeliverySchema = z.object({
195
270
  const checkoutSessionSchema = z.object({
196
271
  checkoutId: z.string(),
197
272
  items: z.array(z.object({
273
+ lineId: z.string().optional().describe("주문서 줄 id(이슈 #59). 같은 `optionId`가 추가 선택·직접 입력이 달라 여러 줄에 나올 수 있어, 상품 쿠폰을 걸 줄은 이 값으로 지목한다. 서버는 항상 싣는다"),
198
274
  productId: z.string(),
199
275
  productName: z.string(),
200
276
  /** variantId. 옵션 없는 상품은 default variant id */
201
277
  optionId: z.string().nullable(),
202
278
  optionName: z.string().nullable(),
279
+ ...lineSelectionResponseShape,
203
280
  quantity: z.number().int(),
204
- unitPrice: z.number().int(),
281
+ unitPrice: z.number().int().describe("단가 — 할인 반영 판매가 + 조합 추가금 + 추가 선택 추가금"),
205
282
  totalPrice: z.number().int(),
206
283
  fulfillment: lineFulfillmentSchema.describe("주문서 시점 상품 이행")
207
284
  })),
@@ -475,4 +552,4 @@ const paymentMessageSchema = z.object({
475
552
  orderId: z.string().optional()
476
553
  });
477
554
  //#endregion
478
- export { startPaymentRequestSchema as A, myCouponListQuerySchema as B, paymentStartSchema as C, residentNumberLike as D, remoteAreaSchema as E, checkoutPricingSchema as F, productCardFulfillmentSchema as G, pricingAmountsSchema as H, couponApplicationListSchema as I, productFulfillmentViewSchema as K, couponApplicationSchema as L, MIN_PAYMENT_AMOUNT as M, applicableCouponSchema as N, retryPaymentRequestSchema as O, checkoutPricingRequestSchema as P, couponBenefitViewSchema as R, paymentOptionSchema as S, pgProviderSchema as T, lineFulfillmentSchema as U, myCouponSchema as V, myOrderItemFulfillmentSnapshotSchema as W, paymentFailureCategorySchema as _, cashReceiptAvailable as a, paymentOptionKey as b, cashReceiptTypeSchema as c, createCheckoutRequestSchema as d, deliveryQuoteRequestSchema as f, guestInfoSchema as g, easyPayProviderSchema as h, availablePaymentOptionSchema as i, MAX_COUPONS_PER_CHECKOUT as j, shippingAddressInputSchema as k, checkoutDeliverySchema as l, easyPayProviderResponseSchema as m, PAYMENT_RESULT_VALUES as n, cashReceiptIdentityTypeOf as o, deliveryQuoteSchema as p, PAYMENT_STATUS_VALUES as r, cashReceiptRequestSchema as s, CASH_RECEIPT_METHODS as t, checkoutSessionSchema as u, paymentMessageSchema as v, paymentStatusSchema as w, paymentOptionListSchema as x, paymentMethodSchema as y, couponResultSchema as z };
555
+ export { lineFulfillmentSchema as $, startPaymentRequestSchema as A, myCouponListQuerySchema as B, paymentStartSchema as C, residentNumberLike as D, remoteAreaSchema as E, checkoutPricingSchema as F, customInputEntrySchema as G, pricingAmountsSchema as H, couponApplicationListSchema as I, customInputViewSchema as J, customInputLength as K, couponApplicationSchema as L, MIN_PAYMENT_AMOUNT as M, applicableCouponSchema as N, retryPaymentRequestSchema as O, checkoutPricingRequestSchema as P, optionSelectionViewSchema as Q, couponBenefitViewSchema as R, paymentOptionSchema as S, pgProviderSchema as T, addonGroupViewSchema as U, myCouponSchema as V, addonSelectionSchema as W, lineSelectionResponseShape as X, lineSelectionRequestShape as Y, normalizeCustomInputValue as Z, paymentFailureCategorySchema as _, cashReceiptAvailable as a, paymentOptionKey as b, cashReceiptTypeSchema as c, createCheckoutRequestSchema as d, myOrderItemFulfillmentSnapshotSchema as et, deliveryQuoteRequestSchema as f, guestInfoSchema as g, easyPayProviderSchema as h, availablePaymentOptionSchema as i, MAX_COUPONS_PER_CHECKOUT as j, shippingAddressInputSchema as k, checkoutDeliverySchema as l, easyPayProviderResponseSchema as m, PAYMENT_RESULT_VALUES as n, productFulfillmentViewSchema as nt, cashReceiptIdentityTypeOf as o, deliveryQuoteSchema as p, customInputValueViewSchema as q, PAYMENT_STATUS_VALUES as r, cashReceiptRequestSchema as s, CASH_RECEIPT_METHODS as t, productCardFulfillmentSchema as tt, checkoutSessionSchema as u, paymentMessageSchema as v, paymentStatusSchema as w, paymentOptionListSchema as x, paymentMethodSchema as y, couponResultSchema as z };
@@ -1,5 +1,5 @@
1
1
  import { a as ANALYTICS_VISITOR_HEADER, r as ANALYTICS_SESSION_HEADER } from "./analytics-CgD70OqH.mjs";
2
- import { A as startPaymentRequestSchema, C as paymentStartSchema, F as checkoutPricingSchema, G as productCardFulfillmentSchema, K as productFulfillmentViewSchema, N as applicableCouponSchema, O as retryPaymentRequestSchema, P as checkoutPricingRequestSchema, U as lineFulfillmentSchema, V as myCouponSchema, W as myOrderItemFulfillmentSnapshotSchema, d as createCheckoutRequestSchema, f as deliveryQuoteRequestSchema, m as easyPayProviderResponseSchema, p as deliveryQuoteSchema, u as checkoutSessionSchema, w as paymentStatusSchema, y as paymentMethodSchema } from "./checkout-DTfoCsMF.mjs";
2
+ import { $ as lineFulfillmentSchema, A as startPaymentRequestSchema, C as paymentStartSchema, F as checkoutPricingSchema, J as customInputViewSchema, N as applicableCouponSchema, O as retryPaymentRequestSchema, P as checkoutPricingRequestSchema, U as addonGroupViewSchema, V as myCouponSchema, X as lineSelectionResponseShape, Y as lineSelectionRequestShape, d as createCheckoutRequestSchema, et as myOrderItemFulfillmentSnapshotSchema, f as deliveryQuoteRequestSchema, m as easyPayProviderResponseSchema, nt as productFulfillmentViewSchema, p as deliveryQuoteSchema, tt as productCardFulfillmentSchema, u as checkoutSessionSchema, w as paymentStatusSchema, y as paymentMethodSchema } from "./checkout-C25liqdW.mjs";
3
3
  import { z } from "zod";
4
4
  //#region src/http.ts
5
5
  /** 표준 엔벨로프의 $.error 상세 */
@@ -243,6 +243,7 @@ const cartItemSchema = z.object({
243
243
  thumbnailUrl: z.url().nullable().describe("담은 상품의 대표 이미지. 없으면 null"),
244
244
  optionId: z.string().nullable(),
245
245
  optionName: z.string().nullable(),
246
+ ...lineSelectionResponseShape,
246
247
  quantity: z.number().int(),
247
248
  unitPrice: z.number().int(),
248
249
  totalPrice: z.number().int(),
@@ -266,11 +267,14 @@ const cartSchema = z.object({
266
267
  const addCartItemRequestSchema = z.object({
267
268
  productId: z.string(),
268
269
  optionId: z.string().optional(),
269
- quantity: z.number().int().min(1).max(999)
270
+ quantity: z.number().int().min(1).max(999),
271
+ ...lineSelectionRequestShape
270
272
  });
271
273
  const updateCartItemRequestSchema = z.object({
272
274
  quantity: z.number().int().min(1).max(999).optional(),
273
- optionId: z.string().optional()
275
+ optionId: z.string().optional(),
276
+ addons: lineSelectionRequestShape.addons.describe("추가 선택을 통째로 바꾼다. 생략하면 그대로 둔다"),
277
+ customInputs: lineSelectionRequestShape.customInputs.describe("직접 입력값을 통째로 바꾼다. 생략하면 그대로 둔다")
274
278
  }).refine((value) => Object.values(value).some((v) => v !== void 0), { message: "최소 1개 필드가 필요합니다" });
275
279
  //#endregion
276
280
  //#region src/schemas/common.ts
@@ -347,6 +351,8 @@ const productDetailSchema = productCardSchema.extend({
347
351
  additionalPrice: z.number().int(),
348
352
  soldOut: z.boolean()
349
353
  })),
354
+ addonGroups: z.array(addonGroupViewSchema).optional().default([]).describe("추가 선택 옵션명(이슈 #59). 배열 순서가 표시 순서다. 담기·바로 구매의 `addons`로 `{groupId, valueId}`를 보낸다. 없으면 빈 배열. 서버는 항상 싣는다"),
355
+ customInputs: z.array(customInputViewSchema).optional().default([]).describe("직접 입력 항목(이슈 #59). 배열 순서가 표시 순서다. 담기·바로 구매의 `customInputs`로 값을 보낸다. 없으면 빈 배열. 서버는 항상 싣는다"),
350
356
  fulfillment: productFulfillmentViewSchema,
351
357
  returnPeriodDays: z.number().int().describe("반품·교환을 받는 기간(수령·제공일부터 일수)"),
352
358
  categoryPath: z.array(z.object({
@@ -601,6 +607,7 @@ const myOrderItemSchema = z.object({
601
607
  productName: z.string(),
602
608
  thumbnailUrl: z.url().nullable().describe("주문 시점의 대표 이미지. 없었으면 null"),
603
609
  optionName: z.string().nullable(),
610
+ ...lineSelectionResponseShape,
604
611
  quantity: z.number().int().describe("주문 수량"),
605
612
  canceledQuantity: z.number().int().describe("취소·반품으로 환불된 누적 수량. 부분 취소가 없었으면 0이다"),
606
613
  activeQuantity: z.number().int().describe("남은 수량 (`quantity` − `canceledQuantity`). 취소·반품·교환 요청 수량의 상한이다"),