@sayren/store-sdk 0.29.7 → 0.30.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.
@@ -380,6 +380,25 @@ export const productOutOfStockWebhookEventSchema = z.object({
380
380
  data: productOutOfStockWebhookEventDataSchema,
381
381
  });
382
382
 
383
+ /**
384
+ * 플로우 알림(이슈 #115) — 상점이 플로우에 둔 알림 블록(`core.notify`, 채널 `webhook`)이 보낸다. 구매자 알림(알림톡·메일)은 이 이벤트를 받아
385
+ * 상점이 보낸다(플랫폼은 구매자에게 직접 보내지 않는다). 개인정보는 싣지 않는다
386
+ */
387
+ export const flowNotificationWebhookEventDataSchema = z.object({
388
+ orderId: z.string().describe("주문 ID"),
389
+ orderItemIds: z.array(z.string()).describe("이 알림의 주문상품(묶음 진행이면 여럿)"),
390
+ event: z.string().describe("상점이 블록에 둔 알림 이름(`custom.<이름>`)"),
391
+ flowId: z.string(),
392
+ flowVersion: z.number().int(),
393
+ state: z.string().describe("알림을 보낸 전이의 도착 상태 키"),
394
+ });
395
+
396
+ export const flowNotificationWebhookEventSchema = z.object({
397
+ ...envelopeFields,
398
+ type: z.literal("ORDER.FLOW_NOTIFICATION"),
399
+ data: flowNotificationWebhookEventDataSchema,
400
+ });
401
+
383
402
  /** 웹훅 요청 본문. `type`으로 판별하며 `data` 형태가 타입별로 다르다 */
384
403
  export const webhookEventSchema = z
385
404
  .discriminatedUnion("type", [
@@ -390,6 +409,7 @@ export const webhookEventSchema = z
390
409
  inquiryWebhookEventSchema,
391
410
  reviewWebhookEventSchema,
392
411
  productOutOfStockWebhookEventSchema,
412
+ flowNotificationWebhookEventSchema,
393
413
  ])
394
414
  .describe(
395
415
  "웹훅 요청 본문 (얇은 이벤트). 개인정보를 싣지 않으며 `data`의 id로 관리 API를 조회한다",
@@ -406,3 +426,40 @@ export type WebhookEventOf<T extends WebhookEventType> = WebhookEvent extends in
406
426
  : never
407
427
  : never
408
428
  : never;
429
+
430
+ /**
431
+ * 알려진 웹훅 이벤트 타입 — `webhookEventSchema`의 판별 값 목록(이 SDK 버전이 아는 것).
432
+ * 서버는 새 타입을 구독한 엔드포인트에만 보내지만, 구독을 더 넓힌 수신자를 위해 `parseWebhookEvent`가 모르는 타입을 따로 받는다.
433
+ */
434
+ export const KNOWN_WEBHOOK_EVENT_TYPES: readonly WebhookEventType[] =
435
+ webhookEventSchema.options.flatMap((option) => {
436
+ const type = option.shape.type;
437
+ return "options" in type
438
+ ? [...(type.options as WebhookEventType[])]
439
+ : [type.value as WebhookEventType];
440
+ });
441
+
442
+ /** 이 SDK 버전이 모르는 이벤트 — 봉투만 검증하고 `data`는 그대로 둔다 */
443
+ export const unknownWebhookEventSchema = z.object({
444
+ ...envelopeFields,
445
+ type: z.string().min(1),
446
+ data: z.unknown(),
447
+ });
448
+ export type UnknownWebhookEvent = z.infer<typeof unknownWebhookEventSchema>;
449
+
450
+ export type ParsedWebhookEvent =
451
+ | { known: true; event: WebhookEvent }
452
+ | { known: false; event: UnknownWebhookEvent };
453
+
454
+ /**
455
+ * 웹훅 본문을 `type`으로 먼저 판별해 읽는다(이슈 #115 결정 25). 알려진 타입은 그 타입의 스키마로 엄격하게 읽고 틀리면 던진다 —
456
+ * `z.union([known, unknown])`처럼 읽으면 알려진 타입의 파싱 실패가 `known: false`로 조용히 새어 수신자가 처리할 이벤트를 버린다.
457
+ * 모르는 타입은 봉투만 검증해 `known: false`로 돌려준다(새 이벤트에 옛 SDK가 깨지지 않는다).
458
+ */
459
+ export function parseWebhookEvent(body: unknown): ParsedWebhookEvent {
460
+ const type = (body as { type?: unknown } | null)?.type;
461
+ if (typeof type === "string" && (KNOWN_WEBHOOK_EVENT_TYPES as readonly string[]).includes(type)) {
462
+ return { known: true, event: webhookEventSchema.parse(body) };
463
+ }
464
+ return { known: false, event: unknownWebhookEventSchema.parse(body) };
465
+ }