@sayren/store-sdk 0.57.0 → 0.58.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.
@@ -3,10 +3,10 @@ import { RESERVED_STORE_CODES } from "./store";
3
3
  import { strictRequest } from "./strict";
4
4
 
5
5
  /**
6
- * 스토어프론트 호스팅 (이슈 #53, M1: 템플릿 발행) — 관리 API `/v1/storefront/**`의 계약이다.
6
+ * 스토어프론트 호스팅 (이슈 #53) — 관리 API `/v1/storefront/**`의 계약이다.
7
7
  *
8
- * 셀러는 템플릿을 골라 사이트를 만들고 `{slug}.{호스팅 도메인}` 주소를 받는다. 상점을 만들어도 사이트는 생기지 않고
9
- * `POST /v1/storefront/site`가 유일한 생성 경로다. 템플릿은 만들 때만 고르고 바꾸지 않는다. 발행은 버전 단위이고,
8
+ * 셀러는 사이트를 만들고 `{slug}.{호스팅 도메인}` 주소를 받는다. 상점을 만들어도 사이트는 생기지 않고
9
+ * `POST /v1/storefront/site`가 유일한 생성 경로다. 첫 소스는 `initialSource`(빈 작업 트리·시작 코드)다. 발행은 버전 단위이고,
10
10
  * 되돌리기는 이전 버전을 다시 발행하는 것이다(재빌드 없음). 상태의 원천은 api DB이고 Cloudflare 쪽 반영
11
11
  * (라우팅 표·사이트 Worker)은 커밋 뒤 비동기다 — 반영 상태는 `provisioning`으로 본다.
12
12
  *
@@ -14,7 +14,7 @@ import { strictRequest } from "./strict";
14
14
  * 공개 전환·슬러그 변경·호스팅 끄기는 승인(`APPROVAL_REQUIRED`)이다 — 구매자가 보는 화면을 바꾸는 쓰기다.
15
15
  *
16
16
  * 오류 코드:
17
- * - 404 `STOREFRONT_SITE_NOT_FOUND` · `STOREFRONT_TEMPLATE_NOT_FOUND` · `STOREFRONT_VERSION_NOT_FOUND`
17
+ * - 404 `STOREFRONT_SITE_NOT_FOUND` · `STOREFRONT_VERSION_NOT_FOUND`
18
18
  * - 409 `STOREFRONT_SITE_EXISTS` — 이미 사이트가 있다(상점당 1개)
19
19
  * - 409 `STOREFRONT_SLUG_TAKEN` — 다른 사이트가 쓰거나 보류 중인 슬러그다
20
20
  * - 409 `STOREFRONT_SLUG_CHANGE_LIMIT` — 슬러그는 상점당 30일에 3번까지 바꾼다
@@ -25,12 +25,11 @@ import { strictRequest } from "./strict";
25
25
  * - 409 `STOREFRONT_VERSION_NOT_READY` — 빌드가 끝나지 않았거나 실패한 버전은 발행할 수 없다
26
26
  * - 409 `STOREFRONT_NO_ROLLBACK_TARGET` — 되돌릴 이전 발행 버전이 없다
27
27
  * - 409 `STOREFRONT_NOT_READY` — 공개 전환: 판매 중인 상품(샘플 제외)이 없다(이슈 #70). 상품을 등록한 뒤 공개한다
28
- * - 409 `TEMPLATE_CHANGE_NOT_SUPPORTED` — 템플릿 변경(`POST /v1/storefront/site/template`, 폐기)은 항상 이 오류다
29
28
  * - 503 `STOREFRONT_PREVIEW_NOT_CONFIGURED` — 미리보기 서명 키가 설정되지 않았다
30
29
  * - 503 `STOREFRONT_STARTER_UNAVAILABLE` — 시작 코드(`initialSource: "STARTER"`)를 아직 쓸 수 없다. 사이트를 만들지 않았다(한도도 세지 않는다). 잠시 뒤 다시 한다
31
30
  *
32
31
  * 편집(M2b, `/v1/storefront/site/editing`·`files/**`·`checks/**`·`verify`·`builds/**`, 버전 만들기):
33
- * - 403 `STOREFRONT_EDITING_DISABLED` — 편집을 쓸 수 없어 셀러 템플릿으로 사이트를 만들 수 없다(사이트 만들기)
32
+ * - 403 `STOREFRONT_EDITING_DISABLED` — 편집을 쓸 수 없어 사이트를 만들 수 없다(사이트 만들기)
34
33
  * - 503 `STOREFRONT_EDITING_NOT_CONFIGURED` — 편집 Sandbox가 설정되지 않았다
35
34
  * - 409 `EDIT_CAPACITY_FULL` — 플랫폼 전체 동시 편집 상한에 걸렸다. `error.details.retryAfterSeconds` 뒤에 다시 한다
36
35
  * - 409 `EDIT_SESSION_REQUIRED` — 편집 미리보기 링크: 편집 환경이 없다
@@ -94,16 +93,6 @@ export const RESERVED_STOREFRONT_SLUGS: readonly string[] = [
94
93
  */
95
94
  export const STOREFRONT_EDIT_PREVIEW_SUFFIX = "-preview";
96
95
 
97
- /**
98
- * 공식 쇼케이스 슬러그 접두사(이슈 #72) — `demo-{템플릿 id}`는 템플릿 미리보기용 쇼케이스 사이트 주소다. 쇼케이스로 지정된
99
- * 상점만 쓸 수 있다(서버가 상점 용도로 판정한다). 공식 미리보기를 흉내 내는 주소를 막는다
100
- */
101
- export const STOREFRONT_SHOWCASE_SLUG_PREFIX = "demo-";
102
-
103
- export function isShowcaseStorefrontSlug(slug: string): boolean {
104
- return slug.toLowerCase().startsWith(STOREFRONT_SHOWCASE_SLUG_PREFIX);
105
- }
106
-
107
96
  export function isReservedStorefrontSlug(slug: string): boolean {
108
97
  const lower = slug.toLowerCase();
109
98
  return (
@@ -149,94 +138,6 @@ export const STOREFRONT_SLUG_CHANGE_LIMIT = 3;
149
138
  /** 사이트 만들기 한도 — 같은 창(30일) 안의 횟수(상점 단위). 끄고 만들기를 되풀이해 이름을 묶어 두지 못하게 한다 */
150
139
  export const STOREFRONT_SITE_CREATE_LIMIT = 3;
151
140
 
152
- // ─── 템플릿 ───────────────────────────────────────────────
153
-
154
- export const storefrontTemplateSchema = z.object({
155
- templateId: z.string().describe("템플릿 id (예: `basic`)"),
156
- name: z.string().describe("템플릿 이름"),
157
- industry: z.string().describe("업종 (예: `general`·`fashion`·`food`). 목록 필터에 쓴다"),
158
- description: z.string().describe("템플릿 설명"),
159
- thumbnailUrl: z.string().nullable().describe("썸네일 이미지 주소. 없으면 null"),
160
- version: z.string().describe("현재 템플릿 버전. 새 사이트는 이 버전으로 만든다"),
161
- isDefault: z.boolean().describe("기본 템플릿인가 (상점 생성 시 자동 생성에 쓴다)"),
162
- tags: z
163
- .array(z.string())
164
- .default([])
165
- .describe(
166
- "템플릿 특징 태그 (예: `후기 강조`). 목록 카드에 보인다. 옛 서버 응답은 빈 배열로 읽는다",
167
- ),
168
- supportsCheckout: z
169
- .boolean()
170
- .default(true)
171
- .describe(
172
- "주문을 받는 템플릿인가. false는 가격·문의 중심의 카탈로그형이다. 옛 서버 응답은 true로 읽는다",
173
- ),
174
- previewUrl: z
175
- .string()
176
- .nullable()
177
- .default(null)
178
- .describe(
179
- "미리보기 사이트 주소. 공식 템플릿은 연결된 쇼케이스, 템플릿 상점의 템플릿은 그 상점의 사이트가 공개 중일 때만 싣고 그 밖에는 null",
180
- ),
181
- source: z
182
- .enum(["CATALOG", "STORE"])
183
- .default("CATALOG")
184
- .describe(
185
- "`CATALOG` 공식 템플릿 · `STORE` 템플릿 상점의 템플릿(사이트를 만들 때 `copyContent`로 콘텐츠를 복사할 수 있다). 옛 서버 응답은 `CATALOG`로 읽는다",
186
- ),
187
- });
188
- export type StorefrontTemplate = z.infer<typeof storefrontTemplateSchema>;
189
-
190
- // ─── 템플릿 상점 (#123) ────────────────────────────────────
191
-
192
- /**
193
- * 템플릿 상점의 템플릿 정보 — 상점을 만들 때 템플릿 상점으로 고른 상점만 있다. 발행한 스토어프론트가 템플릿이 되고, 이 이름·소개·
194
- * 썸네일로 모든 셀러의 템플릿 목록에 보인다
195
- */
196
- export const storeTemplateSchema = z.object({
197
- templateId: z.string().describe("템플릿 id. 다른 상점이 사이트를 만들 때 이 값을 쓴다"),
198
- name: z.string().describe("템플릿 이름"),
199
- description: z.string().describe("템플릿 소개"),
200
- thumbnailUrl: z.string().nullable().describe("썸네일 이미지 주소. 없으면 null"),
201
- status: z
202
- .enum(["DRAFT", "ACTIVE", "RETIRED"])
203
- .describe(
204
- "`DRAFT` 아직 편집한 스토어프론트를 발행하지 않아 목록에 보이지 않음 · `ACTIVE` 목록에 보임 · `RETIRED` 상점을 닫아 내려감",
205
- ),
206
- version: z
207
- .string()
208
- .nullable()
209
- .describe("목록에 보이는 템플릿 버전. 발행한 스토어프론트 버전에서 정해진다. `DRAFT`면 null"),
210
- });
211
- export type StoreTemplate = z.infer<typeof storeTemplateSchema>;
212
-
213
- /**
214
- * 템플릿 상점의 고정 사업자 정보 필드 — 상점 수정에서 다른 값을 보내면 409 `TEMPLATE_STORE_BUSINESS_LOCKED`다(값은 플랫폼이 정한다)
215
- */
216
- export const TEMPLATE_STORE_LOCKED_BUSINESS_FIELDS = [
217
- "businessName",
218
- "representativeName",
219
- "businessNumber",
220
- "mailOrderSalesNumber",
221
- "customerCenterPhone",
222
- "customerCenterEmail",
223
- ] as const;
224
-
225
- export const updateStoreTemplateRequestSchema = strictRequest(
226
- z.object({
227
- name: z.string().trim().min(1).max(40).describe("템플릿 이름(1~40자)"),
228
- description: z.string().trim().max(200).describe("템플릿 소개(200자까지)"),
229
- thumbnailUrl: z
230
- .url({ protocol: /^https$/ })
231
- .max(2048)
232
- .nullable()
233
- .describe(
234
- "썸네일 이미지 주소(https). 이미지 업로드(`POST /v1/uploads`)로 받은 주소를 쓴다. 없으면 null",
235
- ),
236
- }),
237
- );
238
- export type UpdateStoreTemplateRequest = z.infer<typeof updateStoreTemplateRequestSchema>;
239
-
240
141
  // ─── 버전 ─────────────────────────────────────────────────
241
142
 
242
143
  /**
@@ -253,8 +154,6 @@ export type StorefrontVersionStatus = z.infer<typeof storefrontVersionStatusSche
253
154
  export const storefrontVersionSchema = z.object({
254
155
  versionId: z.string(),
255
156
  number: z.number().int().min(1).describe("사이트 안에서 1부터 늘어나는 번호 (v1, v2…)"),
256
- templateId: z.string().describe("이 버전의 기준 템플릿"),
257
- templateVersion: z.string().describe("이 버전의 기준 템플릿 버전"),
258
157
  status: storefrontVersionStatusSchema,
259
158
  published: z.boolean().describe("지금 발행된 버전인가"),
260
159
  publishedAt: z
@@ -330,10 +229,6 @@ export const storefrontVisibilitySchema = z
330
229
  .describe("`COMING_SOON` 오픈 준비 중(방문자에게 준비 중 페이지) · `PUBLIC` 공개");
331
230
  export type StorefrontVisibility = z.infer<typeof storefrontVisibilitySchema>;
332
231
 
333
- export const storefrontSourceModeSchema = z
334
- .enum(["MANAGED", "CUSTOM"])
335
- .describe("`MANAGED` 템플릿 그대로 · `CUSTOM` 한 번이라도 편집함");
336
-
337
232
  export const storefrontDomainSchema = z.object({
338
233
  hostname: z.string().describe("호스트 이름 (예: `mystore.sayren.co`)"),
339
234
  kind: z.enum(["PLATFORM", "CUSTOM"]).describe("`PLATFORM` 슬러그 주소 · `CUSTOM` 내 도메인"),
@@ -406,8 +301,6 @@ export const storefrontSiteSchema = z.object({
406
301
  hostname: z.string().describe("대표 호스트 이름"),
407
302
  url: z.string().describe("대표 주소 (`https://{hostname}`)"),
408
303
  domains: z.array(storefrontDomainSchema).describe("사용 중·보류 중인 호스트 이름"),
409
- templateId: z.string().describe("지금 발행된 버전의 템플릿 (발행 전이면 만든 템플릿)"),
410
- sourceMode: storefrontSourceModeSchema,
411
304
  status: storefrontSiteStatusSchema,
412
305
  visibility: storefrontVisibilitySchema,
413
306
  publishedVersion: storefrontVersionSchema.nullable().describe("지금 발행된 버전"),
@@ -472,7 +365,7 @@ export const storefrontDeploymentLogSchema = z.object({
472
365
  code: z
473
366
  .string()
474
367
  .describe(
475
- "기록 종류 (`SITE_CREATED`·`ADDRESS_RESERVED`·`WAITING`·`SYNCING`·`VERSION_UPLOADED`·`SYNC_FAILED`·`RETRY_SCHEDULED`·`PUBLISHED`, 템플릿 상점의 템플릿·템플릿 없이 만든 사이트는 `BUILDING`·`BUILD_FAILED`, 템플릿 없이 빈 트리로 만든 사이트의 첫 업로드 전은 `WAITING_SOURCE`, 콘텐츠 복사는 `CONTENT_COPYING`·`CONTENT_COPIED`·`CONTENT_COPY_FAILED`)",
368
+ "기록 종류 (`SITE_CREATED`·`ADDRESS_RESERVED`·`WAITING`·`SYNCING`·`VERSION_UPLOADED`·`SYNC_FAILED`·`RETRY_SCHEDULED`·`PUBLISHED`, 템플릿 없이 만든 사이트는 `BUILDING`·`BUILD_FAILED`, 템플릿 없이 빈 트리로 만든 사이트의 첫 업로드 전은 `WAITING_SOURCE`)",
476
369
  ),
477
370
  message: z.string().describe("사람이 읽는 문장"),
478
371
  });
@@ -498,51 +391,22 @@ export type StorefrontDeployment = z.infer<typeof storefrontDeploymentSchema>;
498
391
 
499
392
  // ─── 요청 ─────────────────────────────────────────────────
500
393
 
501
- /** 템플릿 없이 만드는 사이트의 첫 소스(#315) — `NONE` 빈 작업 트리(CLI link) · `STARTER` sayren 시작 코드(웹 시작) */
394
+ /** 사이트의 첫 소스(#315) — `NONE` 빈 작업 트리(CLI link) · `STARTER` sayren 시작 코드(웹 시작) */
502
395
  export const storefrontInitialSourceSchema = z.enum(["NONE", "STARTER"]);
503
396
  export type StorefrontInitialSource = z.infer<typeof storefrontInitialSourceSchema>;
504
- /** 템플릿 없이 만든 사이트·버전의 응답 `templateId`·`templateVersion` — 옛 SDK가 문자열로 파싱하게 싣는 자리 값(#315 1차) */
505
- export const STOREFRONT_NO_TEMPLATE_ID = "none";
506
- export const STOREFRONT_NO_TEMPLATE_VERSION = "0.0.0";
507
397
 
508
398
  export const createStorefrontSiteRequestSchema = strictRequest(
509
399
  z.object({
510
- templateId: z
511
- .string()
512
- .min(1)
513
- .max(64)
514
- .optional()
515
- .describe(
516
- "템플릿 id. 생략하면 템플릿 없이 만든다(오픈 준비 중·발행 버전 없음, 첫 성공 빌드가 발행된다)",
517
- ),
518
400
  initialSource: storefrontInitialSourceSchema
519
401
  .optional()
520
402
  .describe(
521
- "템플릿 없이 만들 때의 첫 소스 (기본 `NONE`). `NONE`은 빈 작업 트리로 시작하고 첫 소스 업로드가 첫 소스다. `STARTER`는 sayren 시작 코드의 최신 게시 버전으로 첫 작업 트리를 만들고 빌드한다. `templateId`와 함께 쓰지 않는다",
403
+ "첫 소스 (기본 `NONE`). `NONE`은 빈 작업 트리로 시작하고 첫 소스 업로드가 첫 소스다. `STARTER`는 sayren 시작 코드의 최신 게시 버전으로 첫 작업 트리를 만들고 빌드한다",
522
404
  ),
523
405
  slug: storefrontSlugSchema.optional().describe("주소 슬러그. 생략하면 상점 코드"),
524
- copyContent: z
525
- .boolean()
526
- .optional()
527
- .describe(
528
- "템플릿 상점의 템플릿일 때만 — true면 템플릿 상점의 콘텐츠(레이아웃, 상품·카테고리·컬렉션·주문 흐름·배송·환불 정책)를 이 상점으로 복사한다. 쿠폰·적립금·리뷰는 복사하지 않는다. 생략하거나 false면 레이아웃 콘텐츠를 비우고 빌드한다",
529
- ),
530
406
  }),
531
407
  );
532
408
  export type CreateStorefrontSiteRequest = z.infer<typeof createStorefrontSiteRequestSchema>;
533
409
 
534
- /** @deprecated 템플릿 변경은 지원하지 않는다(항상 409 `TEMPLATE_CHANGE_NOT_SUPPORTED`). 호환을 위해 스키마만 남겼다 */
535
- export const changeStorefrontTemplateRequestSchema = z.object({
536
- templateId: z.string().min(1).max(64).describe("바꿀 템플릿 id"),
537
- publish: z
538
- .boolean()
539
- .optional()
540
- .describe("새 버전을 곧바로 발행하는가 (기본 true). false면 버전만 만든다"),
541
- message: z.string().max(200).optional().describe("버전 메모"),
542
- });
543
- /** @deprecated 템플릿 변경은 지원하지 않는다 */
544
- export type ChangeStorefrontTemplateRequest = z.infer<typeof changeStorefrontTemplateRequestSchema>;
545
-
546
410
  /**
547
411
  * 발행·되돌리기 — 발행은 사이트의 발행 포인터를 바꾸는 조건부 쓰기다. `expectedPublishedVersionId`를 주면
548
412
  * 지금 발행 버전이 그 값일 때만 바꾼다(화면을 본 뒤 다른 사람이 바꿨으면 409 `STOREFRONT_SITE_CHANGED`).
@@ -1120,9 +984,7 @@ export const storefrontEditingSchema = z.object({
1120
984
  "편집 미리보기 주소 (사이트의 `editPreview`와 같다)",
1121
985
  ),
1122
986
  workingTree: z.object({
1123
- modified: z
1124
- .boolean()
1125
- .describe("템플릿에서 한 번이라도 고쳤는가 (사이트 `sourceMode`가 `CUSTOM`)"),
987
+ modified: z.boolean().describe("작업 트리를 한 번이라도 고쳤는가"),
1126
988
  lastEditedAt: z.string().nullable().describe("마지막으로 파일을 바꾼 시각"),
1127
989
  }),
1128
990
  // 옛 api 응답에는 없다 — 새 SDK가 배포 전 api와 함께 쓰여도 깨지지 않게 선택 필드다