@sayren/store-sdk 0.28.0 → 0.28.2
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 +393 -324
- package/dist/index.mjs +184 -134
- package/dist/server.d.mts +1 -1
- package/dist/server.mjs +1 -1
- package/package.json +1 -1
- package/src/client.ts +21 -21
- package/src/http.ts +1 -1
- package/src/member-delegation.ts +1 -1
- package/src/schemas/api-log.ts +7 -5
- package/src/schemas/audit.ts +5 -5
- package/src/schemas/claim.ts +4 -4
- package/src/schemas/coupon.ts +2 -2
- package/src/schemas/customer.ts +3 -3
- package/src/schemas/fulfillment.ts +9 -11
- package/src/schemas/order.ts +1 -1
- package/src/schemas/payment.ts +7 -5
- package/src/schemas/product.ts +32 -12
- package/src/schemas/promotion.ts +4 -4
- package/src/schemas/refund-policy.ts +12 -12
- package/src/schemas/saved-option-set.ts +1 -1
- package/src/schemas/scope.ts +3 -3
- package/src/schemas/shipping-policy.ts +18 -18
- package/src/schemas/store.ts +106 -11
- package/src/schemas/storefront-hosting.ts +35 -39
- package/src/schemas/subscription.ts +8 -8
- package/src/schemas/telemetry.ts +2 -2
- package/src/schemas/upload.ts +1 -1
- package/src/schemas/webhook-event.ts +1 -1
- package/src/session.ts +1 -1
|
@@ -4,7 +4,7 @@ import { RESERVED_STORE_CODES } from "./store";
|
|
|
4
4
|
/**
|
|
5
5
|
* 스토어프론트 호스팅 (이슈 #53, M1: 템플릿 발행) — 관리 API `/v1/storefront/**`의 계약이다.
|
|
6
6
|
*
|
|
7
|
-
* 셀러는 템플릿을 골라 사이트를 만들고 `{slug}.{호스팅 도메인}` 주소를 받는다.
|
|
7
|
+
* 셀러는 템플릿을 골라 사이트를 만들고 `{slug}.{호스팅 도메인}` 주소를 받는다. 상점을 만들어도 사이트는 생기지 않고
|
|
8
8
|
* `POST /v1/storefront/site`가 유일한 생성 경로다. 템플릿은 만들 때만 고르고 바꾸지 않는다. 발행은 버전 단위이고,
|
|
9
9
|
* 되돌리기는 이전 버전을 다시 발행하는 것이다(재빌드 없음). 상태의 원천은 api DB이고 Cloudflare 쪽 반영
|
|
10
10
|
* (라우팅 표·사이트 Worker)은 커밋 뒤 비동기다 — 반영 상태는 `provisioning`으로 본다.
|
|
@@ -15,13 +15,13 @@ import { RESERVED_STORE_CODES } from "./store";
|
|
|
15
15
|
* 오류 코드:
|
|
16
16
|
* - 403 `STOREFRONT_HOSTING_DISABLED` — 플랫폼 스위치가 꺼져 새 사이트를 만들 수 없다
|
|
17
17
|
* - 404 `STOREFRONT_SITE_NOT_FOUND` · `STOREFRONT_TEMPLATE_NOT_FOUND` · `STOREFRONT_VERSION_NOT_FOUND`
|
|
18
|
-
* - 409 `STOREFRONT_SITE_EXISTS` — 이미 사이트가 있다(
|
|
18
|
+
* - 409 `STOREFRONT_SITE_EXISTS` — 이미 사이트가 있다(상점당 1개)
|
|
19
19
|
* - 409 `STOREFRONT_SLUG_TAKEN` — 다른 사이트가 쓰거나 보류 중인 슬러그다
|
|
20
|
-
* - 409 `STOREFRONT_SLUG_CHANGE_LIMIT` — 슬러그는
|
|
21
|
-
* - 409 `STOREFRONT_SITE_CREATE_LIMIT` — 사이트는
|
|
20
|
+
* - 409 `STOREFRONT_SLUG_CHANGE_LIMIT` — 슬러그는 상점당 30일에 3번까지 바꾼다
|
|
21
|
+
* - 409 `STOREFRONT_SITE_CREATE_LIMIT` — 사이트는 상점당 30일에 3번까지 만든다
|
|
22
22
|
* - 409 `STOREFRONT_SITE_CHANGED` — 읽은 상태(`expected*`)와 현재 상태가 다르다. 다시 읽고 시도한다
|
|
23
23
|
* - 409 `STOREFRONT_SITE_NOT_ACTIVE` — 정지(`SUSPENDED`)된 사이트는 바꾸거나 끌 수 없다
|
|
24
|
-
* - 409 `STORE_SUSPENDED` — 정지된
|
|
24
|
+
* - 409 `STORE_SUSPENDED` — 정지된 상점에는 사이트를 만들 수 없다
|
|
25
25
|
* - 409 `STOREFRONT_VERSION_NOT_READY` — 빌드가 끝나지 않았거나 실패한 버전은 발행할 수 없다
|
|
26
26
|
* - 409 `STOREFRONT_NO_ROLLBACK_TARGET` — 되돌릴 이전 발행 버전이 없다
|
|
27
27
|
* - 409 `STOREFRONT_NOT_READY` — 공개 전환: 판매 중인 상품(샘플 제외)이 없다(이슈 #70). 상품을 등록한 뒤 공개한다
|
|
@@ -47,7 +47,7 @@ import { RESERVED_STORE_CODES } from "./store";
|
|
|
47
47
|
* - 502 `STOREFRONT_SANDBOX_UNAVAILABLE` — 편집 Sandbox 호출이 실패했다. 잠시 뒤 다시 한다
|
|
48
48
|
*
|
|
49
49
|
* 소스 내려받기·올리기(#60, `/v1/storefront/site/source/**`, `sayren` CLI):
|
|
50
|
-
* - 409 `SOURCE_CHANGED` — 작업 트리가 내려받은 뒤(`expectedBaseRef`) 바뀌었다(
|
|
50
|
+
* - 409 `SOURCE_CHANGED` — 작업 트리가 내려받은 뒤(`expectedBaseRef`) 바뀌었다(상점 플랫폼·AI 편집). `error.details.currentRef`
|
|
51
51
|
* - 409 `EDIT_SESSION_OPEN` — 편집 환경이 열려 있다. `endEditSession: true`로 끄고 진행할 수 있다
|
|
52
52
|
* - 409 `SOURCE_BLOBS_MISSING` — 올리지 않은 파일 내용이 있다(`error.details.hashes`). `POST source/blobs`로 올리고 다시 한다
|
|
53
53
|
* - 409 `DEPENDENCY_REJECTED` — 쓸 수 없는 npm 의존성이다(`error.details.rejected: [{ name, version, reason, detail }]`)
|
|
@@ -60,12 +60,12 @@ import { RESERVED_STORE_CODES } from "./store";
|
|
|
60
60
|
// ─── 슬러그 ───────────────────────────────────────────────
|
|
61
61
|
|
|
62
62
|
/**
|
|
63
|
-
* 슬러그 형식 —
|
|
63
|
+
* 슬러그 형식 — 상점 코드와 같은 DNS 라벨 규칙(소문자·숫자·하이픈 3~30자, 하이픈으로 시작·끝 불가)에
|
|
64
64
|
* 연속 하이픈 금지를 더한다. `--`는 버전 미리보기 주소(`v3--{slug}`)와 punycode(`xn--`)가 쓰는 구분자다.
|
|
65
65
|
*/
|
|
66
66
|
export const STOREFRONT_SLUG_PATTERN = /^[a-z0-9](?:[a-z0-9-]{1,28}[a-z0-9])$/;
|
|
67
67
|
|
|
68
|
-
/** 호스팅 도메인에서 플랫폼이 쓰거나 쓸 이름 —
|
|
68
|
+
/** 호스팅 도메인에서 플랫폼이 쓰거나 쓸 이름 — 상점 코드 예약어에 호스팅 전용 이름을 더한다 */
|
|
69
69
|
export const RESERVED_STOREFRONT_SLUGS: readonly string[] = [
|
|
70
70
|
...RESERVED_STORE_CODES,
|
|
71
71
|
"mcp",
|
|
@@ -94,7 +94,7 @@ export const STOREFRONT_EDIT_PREVIEW_SUFFIX = "-preview";
|
|
|
94
94
|
|
|
95
95
|
/**
|
|
96
96
|
* 공식 쇼케이스 슬러그 접두사(이슈 #72) — `demo-{템플릿 id}`는 템플릿 미리보기용 쇼케이스 사이트 주소다. 쇼케이스로 지정된
|
|
97
|
-
*
|
|
97
|
+
* 상점만 쓸 수 있다(서버가 상점 용도로 판정한다). 공식 미리보기를 흉내 내는 주소를 막는다
|
|
98
98
|
*/
|
|
99
99
|
export const STOREFRONT_SHOWCASE_SLUG_PREFIX = "demo-";
|
|
100
100
|
|
|
@@ -132,9 +132,9 @@ export const storefrontSlugSchema = z
|
|
|
132
132
|
export const STOREFRONT_SLUG_HOLD_DAYS = 90;
|
|
133
133
|
/** 슬러그 변경 한도 — 이 기간(일) 안에 */
|
|
134
134
|
export const STOREFRONT_SLUG_CHANGE_WINDOW_DAYS = 30;
|
|
135
|
-
/** 슬러그 변경 한도 — 횟수(
|
|
135
|
+
/** 슬러그 변경 한도 — 횟수(상점 단위. 끄고 다시 만들어도 이어서 센다) */
|
|
136
136
|
export const STOREFRONT_SLUG_CHANGE_LIMIT = 3;
|
|
137
|
-
/** 사이트 만들기 한도 — 같은 창(30일) 안의 횟수(
|
|
137
|
+
/** 사이트 만들기 한도 — 같은 창(30일) 안의 횟수(상점 단위). 끄고 만들기를 되풀이해 이름을 묶어 두지 못하게 한다 */
|
|
138
138
|
export const STOREFRONT_SITE_CREATE_LIMIT = 3;
|
|
139
139
|
|
|
140
140
|
// ─── 템플릿 ───────────────────────────────────────────────
|
|
@@ -146,7 +146,7 @@ export const storefrontTemplateSchema = z.object({
|
|
|
146
146
|
description: z.string().describe("템플릿 설명"),
|
|
147
147
|
thumbnailUrl: z.string().nullable().describe("썸네일 이미지 주소. 없으면 null"),
|
|
148
148
|
version: z.string().describe("현재 템플릿 버전. 새 사이트는 이 버전으로 만든다"),
|
|
149
|
-
isDefault: z.boolean().describe("기본 템플릿인가 (
|
|
149
|
+
isDefault: z.boolean().describe("기본 템플릿인가 (상점 생성 시 자동 생성에 쓴다)"),
|
|
150
150
|
tags: z
|
|
151
151
|
.array(z.string())
|
|
152
152
|
.default([])
|
|
@@ -207,7 +207,7 @@ export const storefrontVersionSchema = z.object({
|
|
|
207
207
|
.nullable()
|
|
208
208
|
.optional()
|
|
209
209
|
.describe(
|
|
210
|
-
"만든 경로 (`CONSOLE`
|
|
210
|
+
"만든 경로 (`CONSOLE` 상점 플랫폼 · `AI` AI 에이전트(MCP) · `CLI` `sayren` CLI · `API` 관리 API · `SYSTEM` 자동). 늘어날 수 있다",
|
|
211
211
|
),
|
|
212
212
|
createdAt: z.string().describe("만든 시각 (ISO 8601)"),
|
|
213
213
|
});
|
|
@@ -217,7 +217,7 @@ export type StorefrontVersion = z.infer<typeof storefrontVersionSchema>;
|
|
|
217
217
|
|
|
218
218
|
export const storefrontSiteStatusSchema = z
|
|
219
219
|
.enum(["ACTIVE", "SUSPENDED", "CLOSED"])
|
|
220
|
-
.describe("`ACTIVE` 운영 중 · `SUSPENDED` 운영자 정지 · `CLOSED` 호스팅
|
|
220
|
+
.describe("`ACTIVE` 운영 중 · `SUSPENDED` 운영자 정지 · `CLOSED` 호스팅 끔·상점 폐쇄");
|
|
221
221
|
export type StorefrontSiteStatus = z.infer<typeof storefrontSiteStatusSchema>;
|
|
222
222
|
|
|
223
223
|
export const storefrontVisibilitySchema = z
|
|
@@ -259,7 +259,7 @@ export type StorefrontProvisioning = z.infer<typeof storefrontProvisioningSchema
|
|
|
259
259
|
|
|
260
260
|
/**
|
|
261
261
|
* 편집 미리보기 주소(M2b) — 사이트마다 `{사이트 id 정규화}-preview.{호스팅 도메인}` 호스트 하나다. 호스팅 도메인의 와일드카드
|
|
262
|
-
* 인증서가 덮으므로 따로 준비할 것이 없다. 이 주소는 편집 미리보기 링크(
|
|
262
|
+
* 인증서가 덮으므로 따로 준비할 것이 없다. 이 주소는 편집 미리보기 링크(상점 플랫폼 전용)로 받은 쿠키가 있어야 열린다.
|
|
263
263
|
*/
|
|
264
264
|
export const storefrontEditPreviewSchema = z.object({
|
|
265
265
|
hostname: z
|
|
@@ -332,7 +332,7 @@ export const storefrontSlugAvailabilitySchema = z.object({
|
|
|
332
332
|
export type StorefrontSlugAvailability = z.infer<typeof storefrontSlugAvailabilitySchema>;
|
|
333
333
|
|
|
334
334
|
/**
|
|
335
|
-
* 배포 진행 — 사이트의 지금 발행 상태가 공개 주소에 반영되기까지의 단계와 기록이다(
|
|
335
|
+
* 배포 진행 — 사이트의 지금 발행 상태가 공개 주소에 반영되기까지의 단계와 기록이다(상점 플랫폼의 스토어프론트 만들기 3단계가 폴링한다).
|
|
336
336
|
* 만들기(`POST /v1/storefront/site`)는 곧바로 201로 답하고 반영은 그 뒤에 비동기로 하므로, 끝났는지는 이 조회로 본다.
|
|
337
337
|
* 템플릿 사이트는 미리 빌드한 템플릿 산출물을 올리므로 빌드 단계가 없다. 기록은 사이트 상태에서 만든 요약이다.
|
|
338
338
|
*/
|
|
@@ -379,7 +379,7 @@ export type StorefrontDeployment = z.infer<typeof storefrontDeploymentSchema>;
|
|
|
379
379
|
|
|
380
380
|
export const createStorefrontSiteRequestSchema = z.object({
|
|
381
381
|
templateId: z.string().min(1).max(64).describe("템플릿 id"),
|
|
382
|
-
slug: storefrontSlugSchema.optional().describe("주소 슬러그. 생략하면
|
|
382
|
+
slug: storefrontSlugSchema.optional().describe("주소 슬러그. 생략하면 상점 코드"),
|
|
383
383
|
});
|
|
384
384
|
export type CreateStorefrontSiteRequest = z.infer<typeof createStorefrontSiteRequestSchema>;
|
|
385
385
|
|
|
@@ -439,7 +439,7 @@ export const storefrontSlugAvailabilityQuerySchema = z.object({
|
|
|
439
439
|
});
|
|
440
440
|
|
|
441
441
|
/**
|
|
442
|
-
* 오픈 준비 중 미리보기 링크 —
|
|
442
|
+
* 오픈 준비 중 미리보기 링크 — 상점 플랫폼에서 로그인한 OWNER·ADMIN만 받는다(PAT·에이전트 불가). 링크는 5분 안에 한 번 열면
|
|
443
443
|
* 그 브라우저에 12시간짜리 미리보기 쿠키가 생기고 오픈 준비 중인 사이트의 실제 화면을 본다. 링크를 남에게 넘기면 그 사람도
|
|
444
444
|
* 볼 수 있으므로 공유하지 않는다.
|
|
445
445
|
*/
|
|
@@ -459,14 +459,14 @@ export type StorefrontPreviewLink = z.infer<typeof storefrontPreviewLinkSchema>;
|
|
|
459
459
|
// ─── 내 도메인 (M3) ───────────────────────────────────────
|
|
460
460
|
//
|
|
461
461
|
// 셀러가 가진 도메인(예: `shop.brand.com`)을 사이트 주소로 연결한다. 흐름은 추가(`PENDING_DNS`) → 셀러가 DNS에 TXT(소유 확인)·
|
|
462
|
-
// CNAME(연결)을 넣음 → 서버가 TXT를 확인하면 도메인을 이
|
|
462
|
+
// CNAME(연결)을 넣음 → 서버가 TXT를 확인하면 도메인을 이 상점에 잡고 인증서 발급을 시작한다(`PENDING_VALIDATION`) → 인증서가
|
|
463
463
|
// 발급되면 `ACTIVE`다. 확인은 서버가 주기적으로 하고 `verify`는 곧바로 한 번 더 본다. 연결된 도메인은 대표 주소로 지정할 수 있고,
|
|
464
464
|
// 대표가 아닌 주소(기본 주소 포함)는 대표 주소로 301한다.
|
|
465
465
|
//
|
|
466
|
-
// - TXT 소유 확인은 도메인마다 새 값이다. CNAME만으로는 연결하지 않는다 — 연결을 끊은 뒤 CNAME이 남은 도메인을 다른
|
|
466
|
+
// - TXT 소유 확인은 도메인마다 새 값이다. CNAME만으로는 연결하지 않는다 — 연결을 끊은 뒤 CNAME이 남은 도메인을 다른 상점이
|
|
467
467
|
// 가져가지 못하게 한다.
|
|
468
|
-
// - 소유 확인 전(`PENDING_DNS`)에는 도메인을 잡지 않는다. 같은 도메인을 여러
|
|
469
|
-
//
|
|
468
|
+
// - 소유 확인 전(`PENDING_DNS`)에는 도메인을 잡지 않는다. 같은 도메인을 여러 상점이 추가해 둘 수 있고, 먼저 TXT 확인을 통과한
|
|
469
|
+
// 상점이 잡는다. 이미 다른 상점이 잡은 도메인은 409 `STOREFRONT_DOMAIN_TAKEN`이다.
|
|
470
470
|
// - 해제하면 곧바로 연결을 지운다. 연결된 뒤 DNS가 7일 넘게 이 서비스를 가리키지 않으면 서버가 연결을 해제한다.
|
|
471
471
|
// - 결제 복귀·방문 분석·소셜 로그인 허용 목록은 연결된(`ACTIVE`) 도메인을 자동으로 더한다.
|
|
472
472
|
//
|
|
@@ -474,11 +474,11 @@ export type StorefrontPreviewLink = z.infer<typeof storefrontPreviewLinkSchema>;
|
|
|
474
474
|
// - 403 `STOREFRONT_CUSTOM_DOMAINS_DISABLED` — 플랫폼 스위치가 꺼져 도메인을 추가할 수 없다
|
|
475
475
|
// - 400 `STOREFRONT_DOMAIN_INVALID` — 형식이 틀렸거나 연결할 수 없는 도메인이다(`error.details.reason`)
|
|
476
476
|
// - 404 `STOREFRONT_DOMAIN_NOT_FOUND`
|
|
477
|
-
// - 409 `STOREFRONT_DOMAIN_EXISTS` — 이
|
|
478
|
-
// - 409 `STOREFRONT_DOMAIN_TAKEN` — 다른
|
|
479
|
-
// - 409 `STOREFRONT_DOMAIN_LIMIT` —
|
|
477
|
+
// - 409 `STOREFRONT_DOMAIN_EXISTS` — 이 상점에 이미 추가한 도메인이다
|
|
478
|
+
// - 409 `STOREFRONT_DOMAIN_TAKEN` — 다른 상점이 연결했거나 연결 중인 도메인이다
|
|
479
|
+
// - 409 `STOREFRONT_DOMAIN_LIMIT` — 상점당 도메인 수 상한(`error.details.limit`)
|
|
480
480
|
// - 409 `STOREFRONT_DOMAIN_NOT_ACTIVE` — 연결되지 않았거나 경고(`failureCode`)가 있는 도메인은 대표 주소로 지정할 수 없다
|
|
481
|
-
// - 429 `TOO_MANY_REQUESTS` — 재확인은
|
|
481
|
+
// - 429 `TOO_MANY_REQUESTS` — 재확인은 상점당 분당 20번까지다(`error.details.retryAfterSeconds`)
|
|
482
482
|
// - 400 `STOREFRONT_DOMAIN_NOT_REMOVABLE` — 기본 주소는 해제할 수 없다
|
|
483
483
|
|
|
484
484
|
/**
|
|
@@ -575,7 +575,7 @@ export const STOREFRONT_PUBLIC_SUFFIXES: readonly string[] = [
|
|
|
575
575
|
"imweb.me",
|
|
576
576
|
];
|
|
577
577
|
|
|
578
|
-
/**
|
|
578
|
+
/** 상점당 연결할 수 있는 도메인 수 기본값 — 요금제(#44) 전의 상한이다. 실제 값은 `GET …/domains`의 `limit` */
|
|
579
579
|
export const STOREFRONT_CUSTOM_DOMAIN_LIMIT_DEFAULT = 2;
|
|
580
580
|
/** DNS를 넣을 때까지 기다리는 기간(일) — 지나면 `FAILED`(`DNS_NOT_CONFIGURED`) */
|
|
581
581
|
export const STOREFRONT_DOMAIN_DNS_WAIT_DAYS = 7;
|
|
@@ -683,7 +683,7 @@ export const storefrontSiteDomainSchema = z.object({
|
|
|
683
683
|
.string()
|
|
684
684
|
.nullable()
|
|
685
685
|
.describe(
|
|
686
|
-
"실패·경고 코드. `DNS_NOT_CONFIGURED` 기한 안에 TXT를 찾지 못함 · `VALIDATION_TIMEOUT` 인증서 발급 기한 지남 · `CERTIFICATE_FAILED` 인증서 발급 실패 · `DNS_NOT_POINTING` 연결된 뒤 DNS가 이 서비스를 가리키지 않음(기한이 지나면 해제) · `DOMAIN_TAKEN` 다른
|
|
686
|
+
"실패·경고 코드. `DNS_NOT_CONFIGURED` 기한 안에 TXT를 찾지 못함 · `VALIDATION_TIMEOUT` 인증서 발급 기한 지남 · `CERTIFICATE_FAILED` 인증서 발급 실패 · `DNS_NOT_POINTING` 연결된 뒤 DNS가 이 서비스를 가리키지 않음(기한이 지나면 해제) · `DOMAIN_TAKEN` 다른 상점이 먼저 소유를 확인함 · `HOSTNAME_BLOCKED` 연결이 막힘 · `HOSTNAME_CONFLICT` 연결 준비 중 충돌(운영 확인). 늘어날 수 있다",
|
|
687
687
|
),
|
|
688
688
|
verifiedAt: z.string().nullable().describe("소유를 확인한 시각 (ISO 8601)"),
|
|
689
689
|
activatedAt: z.string().nullable().describe("연결된 시각 (ISO 8601)"),
|
|
@@ -695,11 +695,7 @@ export type StorefrontSiteDomain = z.infer<typeof storefrontSiteDomainSchema>;
|
|
|
695
695
|
/** `GET /v1/storefront/site/domains` — 사이트의 주소 목록(기본 주소 + 내 도메인)과 연결 설정 */
|
|
696
696
|
export const storefrontSiteDomainsSchema = z.object({
|
|
697
697
|
enabled: z.boolean().describe("내 도메인을 추가할 수 있는가 (플랫폼 스위치)"),
|
|
698
|
-
limit: z
|
|
699
|
-
.number()
|
|
700
|
-
.int()
|
|
701
|
-
.min(0)
|
|
702
|
-
.describe("스토어당 내 도메인 수 상한 (해제한 도메인은 세지 않는다)"),
|
|
698
|
+
limit: z.number().int().min(0).describe("상점당 내 도메인 수 상한 (해제한 도메인은 세지 않는다)"),
|
|
703
699
|
target: z.string().describe("CNAME 대상 (예: `sites.sayren.co`)"),
|
|
704
700
|
domains: z.array(storefrontSiteDomainSchema).describe("대표 주소 먼저, 그다음 추가한 순서"),
|
|
705
701
|
});
|
|
@@ -716,11 +712,11 @@ export type AddStorefrontDomainRequest = z.infer<typeof addStorefrontDomainReque
|
|
|
716
712
|
|
|
717
713
|
// ─── 편집 (M2b) ───────────────────────────────────────────
|
|
718
714
|
//
|
|
719
|
-
// 셀러(
|
|
715
|
+
// 셀러(상점 플랫폼)와 에이전트(MCP)가 사이트의 작업 트리를 고치고 버전을 만든다. 작업 트리는 사이트당 하나이고 편집 Sandbox
|
|
720
716
|
// (격리된 컨테이너)가 dev 서버로 띄운다. 편집 환경은 플랫폼 전체 동시 상한이 있고 15분 동안 활동이 없으면 잠든다(작업
|
|
721
717
|
// 트리는 보존된다). 버전 만들기는 그 시점의 작업 트리를 고정해 일회용 빌드 환경에서 규칙 검증 → 타입 검사 → 빌드를
|
|
722
718
|
// 하고, 통과하면 발행할 수 있는 `READY` 버전이 된다. 에이전트의 파일 쓰기·검사·버전 만들기는 승인 없이 되고, 발행·
|
|
723
|
-
// 되돌리기·공개 전환은 기존대로 승인이다. 편집 미리보기 링크는
|
|
719
|
+
// 되돌리기·공개 전환은 기존대로 승인이다. 편집 미리보기 링크는 상점 플랫폼 OWNER·ADMIN만 받는다(에이전트에게 주지 않는다).
|
|
724
720
|
|
|
725
721
|
/** 플랫폼 전체 동시 편집 환경 상한 */
|
|
726
722
|
export const STOREFRONT_EDIT_CAPACITY = 20;
|
|
@@ -813,7 +809,7 @@ export const storefrontEditSessionStatusSchema = z
|
|
|
813
809
|
.enum(["STARTING", "RUNNING"])
|
|
814
810
|
.describe("`STARTING` 띄우는 중 · `RUNNING` 사용 중");
|
|
815
811
|
|
|
816
|
-
/** 편집 환경을 띄우는 단계 —
|
|
812
|
+
/** 편집 환경을 띄우는 단계 — 상점 플랫폼 진행 표시용이다. 값은 늘어날 수 있다 */
|
|
817
813
|
export const STOREFRONT_EDIT_START_STAGES = ["CONTAINER", "RESTORE", "DEV_SERVER"] as const;
|
|
818
814
|
|
|
819
815
|
export const storefrontEditSessionSchema = z.object({
|
|
@@ -1252,7 +1248,7 @@ export const storefrontVersionCreatedSchema = z.object({
|
|
|
1252
1248
|
export type StorefrontVersionCreated = z.infer<typeof storefrontVersionCreatedSchema>;
|
|
1253
1249
|
|
|
1254
1250
|
/**
|
|
1255
|
-
* `POST /v1/storefront/site/editing/preview-link` — 편집 미리보기(dev 서버) 링크.
|
|
1251
|
+
* `POST /v1/storefront/site/editing/preview-link` — 편집 미리보기(dev 서버) 링크. 상점 플랫폼에서 로그인한 OWNER·ADMIN만
|
|
1256
1252
|
* 받는다(PAT·에이전트 불가). 5분 안에 열면 그 브라우저에 미리보기 호스트 전용 쿠키(12시간)가 생긴다. 편집 환경이
|
|
1257
1253
|
* 없으면 409 `EDIT_SESSION_REQUIRED`다(편집 환경이 `STARTING`이면 링크는 주고, 미리보기 화면이 기동 대기를 보여 준다).
|
|
1258
1254
|
*/
|
|
@@ -1262,7 +1258,7 @@ export type CreateStorefrontEditPreviewLinkRequest = CreateStorefrontPreviewLink
|
|
|
1262
1258
|
|
|
1263
1259
|
// ─── 소스 내려받기·올리기 (#60, `sayren` CLI) ─────────────────────────────
|
|
1264
1260
|
//
|
|
1265
|
-
// 셀러가 사이트 작업 트리를 로컬로 받아(`npx sayren pull`) 고치고 되돌려 놓는다(`npx sayren deploy`).
|
|
1261
|
+
// 셀러가 사이트 작업 트리를 로컬로 받아(`npx sayren pull`) 고치고 되돌려 놓는다(`npx sayren deploy`). 상점 플랫폼·AI 편집과 같은
|
|
1266
1262
|
// 작업 트리다. 올리는 것은 소스뿐이고 빌드는 서버가 한다(버전 만들기). 흐름:
|
|
1267
1263
|
// 1. `GET source` — 지금 작업 트리를 고정하고(`sourceRef`) 압축 파일 크기를 알린다
|
|
1268
1264
|
// 2. `GET source/archive?sourceRef=&part=` — 압축 파일(tar.gz)을 4MiB씩 받는다
|
|
@@ -1286,7 +1282,7 @@ export const storefrontSourceRefSchema = z
|
|
|
1286
1282
|
export const storefrontSourceSchema = z.object({
|
|
1287
1283
|
siteId: z.string(),
|
|
1288
1284
|
storeId: z.string(),
|
|
1289
|
-
storeCode: z.string().describe("
|
|
1285
|
+
storeCode: z.string().describe("상점 코드 — 로컬 개발 서버의 `SAYREN_STORE_CODE`"),
|
|
1290
1286
|
sourceRef: storefrontSourceRefSchema,
|
|
1291
1287
|
treeSha256: z.string().describe("트리 지문 (경로 오름차순 `{path}\\t{sha256}\\n`의 SHA-256)"),
|
|
1292
1288
|
files: z.number().int().min(0).describe("파일 수"),
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* 요금제·구독 — `GET /v1/store/subscription`(스코프 `store:r`). 구독 단위는
|
|
5
|
-
* 결제 수단·청구 금액은 싣지 않는다. 요금제 변경은
|
|
4
|
+
* 요금제·구독 — `GET /v1/store/subscription`(스코프 `store:r`). 구독 단위는 상점이다.
|
|
5
|
+
* 결제 수단·청구 금액은 싣지 않는다. 요금제 변경은 상점 플랫폼에서만 한다.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
/** 요금제 코드 — 이름(`plan.name`)이 바뀌어도 코드는 바뀌지 않는다. 값은 추가만 한다 */
|
|
@@ -58,7 +58,7 @@ export const subscriptionUsageSchema = z.object({
|
|
|
58
58
|
adminApiRequests: z
|
|
59
59
|
.number()
|
|
60
60
|
.int()
|
|
61
|
-
.describe("이번 기간 관리 API 요청 수(API 토큰·앱·에이전트 호출,
|
|
61
|
+
.describe("이번 기간 관리 API 요청 수(API 토큰·앱·에이전트 호출, 상점 플랫폼 제외)"),
|
|
62
62
|
emails: z.number().int().describe("이번 기간 초대·양도 알림 메일 수(한도 없음, 표시만)"),
|
|
63
63
|
storefrontPreviewMinutes: z.number().int().describe("이번 기간 편집 미리보기 분(끝난 세션)"),
|
|
64
64
|
storefrontBuildMinutes: z.number().int().describe("이번 기간 빌드 분(끝난 빌드)"),
|
|
@@ -89,7 +89,7 @@ export const storeSubscriptionSchema = z.object({
|
|
|
89
89
|
features: z.object({
|
|
90
90
|
adminApi: z.boolean().describe("관리 API(개인 액세스 토큰·외부 앱 토큰 호출)를 쓸 수 있다"),
|
|
91
91
|
analytics: z.boolean().describe("애널리틱스 조회를 쓸 수 있다"),
|
|
92
|
-
storeClone: z.boolean().describe("더 쓰지 않는다 —
|
|
92
|
+
storeClone: z.boolean().describe("더 쓰지 않는다 — 상점 복제 기능을 없애 항상 false"),
|
|
93
93
|
removeBadge: z.boolean().describe("더 쓰지 않는다 — 배지 키를 없애 항상 false"),
|
|
94
94
|
}),
|
|
95
95
|
usage: subscriptionUsageSchema,
|
|
@@ -123,12 +123,12 @@ export const planErrorDetailsSchema = z
|
|
|
123
123
|
used: z.number().optional(),
|
|
124
124
|
plan: responsePlanCodeSchema.optional(),
|
|
125
125
|
resetsAt: z.string().optional().describe("월 한도가 초기화되는 시각(ISO 8601)"),
|
|
126
|
-
upgradeUrl: z.string().optional().describe("요금제 화면 주소(
|
|
126
|
+
upgradeUrl: z.string().optional().describe("요금제 화면 주소(상점 플랫폼)"),
|
|
127
127
|
reason: z
|
|
128
128
|
.string()
|
|
129
129
|
.optional()
|
|
130
130
|
.describe(
|
|
131
|
-
"
|
|
131
|
+
"상점 수 한도의 이유 — `OWNED_STORES`(소유 상점 수). `FREE_STORES`는 더 쓰지 않는다",
|
|
132
132
|
),
|
|
133
133
|
})
|
|
134
134
|
.loose();
|
|
@@ -138,7 +138,7 @@ const limitOrContract = (description: string) =>
|
|
|
138
138
|
z.number().int().nullable().describe(`${description}. null이면 계약(엔터프라이즈)·한도 없음`);
|
|
139
139
|
|
|
140
140
|
/**
|
|
141
|
-
* 요금제 기능 키 값 — 요금제
|
|
141
|
+
* 요금제 기능 키 값 — 요금제 비교표·상점 플랫폼 요금제 화면이 쓴다. 키는 서버 판정의 기능 키와 같다(추가만 한다). 상위 요금제는 하위 요금제
|
|
142
142
|
* 기능을 모두 포함한다
|
|
143
143
|
*/
|
|
144
144
|
export const planEntitlementsSchema = z
|
|
@@ -146,7 +146,7 @@ export const planEntitlementsSchema = z
|
|
|
146
146
|
"features.admin_api": z
|
|
147
147
|
.boolean()
|
|
148
148
|
.describe(
|
|
149
|
-
"관리 API(개인 액세스 토큰·외부 앱 토큰 호출).
|
|
149
|
+
"관리 API(개인 액세스 토큰·외부 앱 토큰 호출). 상점 플랫폼·CLI·MCP는 모든 요금제에서 쓴다",
|
|
150
150
|
),
|
|
151
151
|
"features.analytics": z.boolean().describe("애널리틱스 화면·조회 API"),
|
|
152
152
|
"limits.ai_reads_monthly": limitOrContract("MCP 읽기 월 한도"),
|
package/src/schemas/telemetry.ts
CHANGED
|
@@ -8,7 +8,7 @@ import { z } from "zod";
|
|
|
8
8
|
* 세는 것이다. 그 숫자가 규칙과 안내를 고치는 근거가 된다.
|
|
9
9
|
*
|
|
10
10
|
* **보내지 않는 것**: 파일 경로·소스 코드·위반 상세(`detail`)·토큰·사람 식별자. 규칙 id와 개수뿐이다.
|
|
11
|
-
* 테넌트는 요청 토큰이 정한다(본문에
|
|
11
|
+
* 테넌트는 요청 토큰이 정한다(본문에 상점 id가 없다). 옵트아웃은 클라이언트의 `SAYREN_TELEMETRY=0`이다.
|
|
12
12
|
*
|
|
13
13
|
* 알 수 없는 키는 저장하지 않고 버린다(`.strict()`를 쓰지 않는다) — 새 버전 클라이언트가 필드를
|
|
14
14
|
* 하나 더 실었다고 계측 전체가 400으로 날아가면 그 버전의 숫자가 통째로 빈다.
|
|
@@ -64,7 +64,7 @@ export const mcpToolEventSchema = z.object({
|
|
|
64
64
|
results: z
|
|
65
65
|
.array(mcpRuleResultSchema)
|
|
66
66
|
.max(MCP_TELEMETRY_MAX_RESULTS)
|
|
67
|
-
// 규칙 하나당 한 줄이다. 같은 id를 여러 줄로 실으면 집계가 한
|
|
67
|
+
// 규칙 하나당 한 줄이다. 같은 id를 여러 줄로 실으면 집계가 한 상점의 한 규칙을 몇 번 세야
|
|
68
68
|
// 하는지 정할 수 없고, 상한만큼 같은 규칙을 반복해 보내는 것이 저장을 부풀리는 길이 된다.
|
|
69
69
|
.refine((results) => new Set(results.map((result) => result.ruleId)).size === results.length, {
|
|
70
70
|
message: "같은 규칙 id를 두 번 실을 수 없습니다",
|
package/src/schemas/upload.ts
CHANGED
|
@@ -28,7 +28,7 @@ export {
|
|
|
28
28
|
const envelopeFields = {
|
|
29
29
|
id: z.string().describe("이벤트 ID (`evt_…`, 테스트 전송은 `evt_test_…`). 중복 제거 키"),
|
|
30
30
|
createdAt: z.string().describe("이벤트 발생 일시 (ISO 8601)"),
|
|
31
|
-
storeId: z.string().describe("이벤트가 발생한
|
|
31
|
+
storeId: z.string().describe("이벤트가 발생한 상점 ID"),
|
|
32
32
|
sequence: z
|
|
33
33
|
.number()
|
|
34
34
|
.int()
|
package/src/session.ts
CHANGED
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
import { type MyAccount, myAccountSchema } from "./schemas/auth";
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
|
-
* SDK 세션 매니저 —
|
|
12
|
+
* SDK 세션 매니저 — 상점 플랫폼(액세스 토큰)의 저장·무음 갱신·회전을 캡슐화한다.
|
|
13
13
|
*
|
|
14
14
|
* 저장 매체(쿠키/localStorage/…)와 그 속성(Max-Age·SameSite·Secure·Path)은 **어댑터(앱) 책임**이다.
|
|
15
15
|
* SDK는 "무엇을 어떤 키로 저장할지"만 알고, "어떻게 저장할지"는 SessionStorage 어댑터에 위임한다.
|