@bstage-sdk/core 0.2.0 → 0.4.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.cjs +22 -9
- package/dist/index.d.cts +15 -2
- package/dist/index.d.ts +15 -2
- package/dist/index.js +22 -9
- package/docs/API_REFERENCE.md +5 -4
- package/docs/BUILD_SYSTEM.md +1 -1
- package/docs/GETTING_STARTED.md +5 -3
- package/docs/INIT.md +1 -1
- package/docs/MIGRATION.md +31 -2
- package/docs/SDK_ARCHITECTURE.md +3 -3
- package/docs/SLOT_CATALOG.md +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -192,20 +192,32 @@ var resolveBstageFetch = (input, init) => {
|
|
|
192
192
|
return hook ? hook(input, init) : globalThis.fetch(input, init);
|
|
193
193
|
};
|
|
194
194
|
var APP_ID_PREFIX = "bsa_";
|
|
195
|
-
var
|
|
196
|
-
function warnOnSwappedCredentials(appId,
|
|
197
|
-
const
|
|
198
|
-
const
|
|
199
|
-
if (!
|
|
195
|
+
var APP_KEY_PREFIX = "bsp_";
|
|
196
|
+
function warnOnSwappedCredentials(appId, appKey) {
|
|
197
|
+
const appIdLooksLikeKey = typeof appId === "string" && appId.startsWith(APP_KEY_PREFIX);
|
|
198
|
+
const appKeyLooksLikeId = typeof appKey === "string" && appKey.startsWith(APP_ID_PREFIX);
|
|
199
|
+
if (!appIdLooksLikeKey && !appKeyLooksLikeId) return;
|
|
200
200
|
console.warn(
|
|
201
|
-
`[b.stage] appId\uC640
|
|
202
|
-
appId\uB294 "${APP_ID_PREFIX}",
|
|
201
|
+
`[b.stage] appId\uC640 appKey\uAC00 \uC11C\uB85C \uBC14\uB010 \uAC83 \uAC19\uC2B5\uB2C8\uB2E4.
|
|
202
|
+
appId\uB294 "${APP_ID_PREFIX}", appKey\uB294 "${APP_KEY_PREFIX}"\uB85C \uC2DC\uC791\uD574\uC57C \uD569\uB2C8\uB2E4.
|
|
203
203
|
\uD30C\uD2B8\uB108 \uCF58\uC194\uC5D0\uC11C \uBC1C\uAE09\uBC1B\uC740 \uAC12\uC744 \uB2E4\uC2DC \uD655\uC778\uD574 \uC8FC\uC138\uC694.`
|
|
204
204
|
);
|
|
205
205
|
}
|
|
206
|
+
function resolveAppKey(config) {
|
|
207
|
+
return config.appKey || config.appSecret || void 0;
|
|
208
|
+
}
|
|
209
|
+
function warnOnMissingAppKey(appKey) {
|
|
210
|
+
if (appKey !== void 0) return;
|
|
211
|
+
console.warn(
|
|
212
|
+
`[b.stage] appKey\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4 \u2014 X-BSTAGE-APP-KEY \uD5E4\uB354 \uC5C6\uC774 \uD638\uCD9C\uB418\uC5B4 API\uAC00 401\uC744 \uB0C5\uB2C8\uB2E4.
|
|
213
|
+
.env\uC758 VITE_BSTAGE_APP_KEY(\uD30C\uD2B8\uB108 \uCF58\uC194\uC758 APP KEY)\uB97C \uD655\uC778\uD574 \uC8FC\uC138\uC694.`
|
|
214
|
+
);
|
|
215
|
+
}
|
|
206
216
|
var BstageClient = class {
|
|
207
217
|
constructor(config) {
|
|
208
|
-
|
|
218
|
+
const appKey = resolveAppKey(config);
|
|
219
|
+
warnOnMissingAppKey(appKey);
|
|
220
|
+
warnOnSwappedCredentials(config.appId, appKey);
|
|
209
221
|
const fetchFn = config.fetch ?? resolveBstageFetch;
|
|
210
222
|
this.http = new HttpClient({
|
|
211
223
|
baseUrl: config.baseUrl ?? resolveBaseUrl(),
|
|
@@ -214,7 +226,8 @@ var BstageClient = class {
|
|
|
214
226
|
headers: {
|
|
215
227
|
"Content-Type": "application/json",
|
|
216
228
|
"X-BSTAGE-APP-ID": config.appId,
|
|
217
|
-
"
|
|
229
|
+
// 값이 없으면(UI 전용 템플릿의 빈 .env) 헤더 자체를 빼서 "undefined" 문자열이 실리지 않게 한다.
|
|
230
|
+
...appKey !== void 0 ? { "X-BSTAGE-APP-KEY": appKey } : {},
|
|
218
231
|
"X-BSTAGE-TENANT-ID": config.tenantId
|
|
219
232
|
}
|
|
220
233
|
});
|
package/dist/index.d.cts
CHANGED
|
@@ -93,8 +93,21 @@ declare class HttpTimeoutError extends Error {
|
|
|
93
93
|
interface BstageConfig {
|
|
94
94
|
/** 파트너 콘솔에서 발급받은 앱 식별자. */
|
|
95
95
|
appId: string;
|
|
96
|
-
/**
|
|
97
|
-
|
|
96
|
+
/**
|
|
97
|
+
* 파트너 콘솔에서 발급받은 앱 키(APP KEY). 게이트웨이 `X-BSTAGE-APP-KEY` 헤더로 나간다.
|
|
98
|
+
*
|
|
99
|
+
* **브라우저에 노출되는 값이다** — `VITE_` 환경변수로 번들에 평문으로 실리고 요청 헤더에서도
|
|
100
|
+
* 보인다. 비밀값이 아니며, 접근 범위는 게이트웨이가 앱 단위로 제어한다.
|
|
101
|
+
* `appKey`·`appSecret` 중 하나는 있어야 하고, 둘 다 있으면 `appKey`가 우선한다.
|
|
102
|
+
*/
|
|
103
|
+
appKey?: string;
|
|
104
|
+
/**
|
|
105
|
+
* `appKey`의 옛 이름. 값은 같다(비밀값이 아니다).
|
|
106
|
+
*
|
|
107
|
+
* @deprecated `appKey`를 쓰세요. 기존 프로젝트 호환용 별칭이며 **0.4.0**(공개 배포판 `@bstage-sdk/core`
|
|
108
|
+
* 기준)에서 제거된다. 다른 배포 라인은 제거 전 MIGRATION 문서에 먼저 고지한다. 둘 다 주면 `appKey`가 우선한다.
|
|
109
|
+
*/
|
|
110
|
+
appSecret?: string;
|
|
98
111
|
/** 템플릿을 적용할 Space(테넌트) 식별자. */
|
|
99
112
|
tenantId: string;
|
|
100
113
|
/**
|
package/dist/index.d.ts
CHANGED
|
@@ -93,8 +93,21 @@ declare class HttpTimeoutError extends Error {
|
|
|
93
93
|
interface BstageConfig {
|
|
94
94
|
/** 파트너 콘솔에서 발급받은 앱 식별자. */
|
|
95
95
|
appId: string;
|
|
96
|
-
/**
|
|
97
|
-
|
|
96
|
+
/**
|
|
97
|
+
* 파트너 콘솔에서 발급받은 앱 키(APP KEY). 게이트웨이 `X-BSTAGE-APP-KEY` 헤더로 나간다.
|
|
98
|
+
*
|
|
99
|
+
* **브라우저에 노출되는 값이다** — `VITE_` 환경변수로 번들에 평문으로 실리고 요청 헤더에서도
|
|
100
|
+
* 보인다. 비밀값이 아니며, 접근 범위는 게이트웨이가 앱 단위로 제어한다.
|
|
101
|
+
* `appKey`·`appSecret` 중 하나는 있어야 하고, 둘 다 있으면 `appKey`가 우선한다.
|
|
102
|
+
*/
|
|
103
|
+
appKey?: string;
|
|
104
|
+
/**
|
|
105
|
+
* `appKey`의 옛 이름. 값은 같다(비밀값이 아니다).
|
|
106
|
+
*
|
|
107
|
+
* @deprecated `appKey`를 쓰세요. 기존 프로젝트 호환용 별칭이며 **0.4.0**(공개 배포판 `@bstage-sdk/core`
|
|
108
|
+
* 기준)에서 제거된다. 다른 배포 라인은 제거 전 MIGRATION 문서에 먼저 고지한다. 둘 다 주면 `appKey`가 우선한다.
|
|
109
|
+
*/
|
|
110
|
+
appSecret?: string;
|
|
98
111
|
/** 템플릿을 적용할 Space(테넌트) 식별자. */
|
|
99
112
|
tenantId: string;
|
|
100
113
|
/**
|
package/dist/index.js
CHANGED
|
@@ -138,20 +138,32 @@ var resolveBstageFetch = (input, init) => {
|
|
|
138
138
|
return hook ? hook(input, init) : globalThis.fetch(input, init);
|
|
139
139
|
};
|
|
140
140
|
var APP_ID_PREFIX = "bsa_";
|
|
141
|
-
var
|
|
142
|
-
function warnOnSwappedCredentials(appId,
|
|
143
|
-
const
|
|
144
|
-
const
|
|
145
|
-
if (!
|
|
141
|
+
var APP_KEY_PREFIX = "bsp_";
|
|
142
|
+
function warnOnSwappedCredentials(appId, appKey) {
|
|
143
|
+
const appIdLooksLikeKey = typeof appId === "string" && appId.startsWith(APP_KEY_PREFIX);
|
|
144
|
+
const appKeyLooksLikeId = typeof appKey === "string" && appKey.startsWith(APP_ID_PREFIX);
|
|
145
|
+
if (!appIdLooksLikeKey && !appKeyLooksLikeId) return;
|
|
146
146
|
console.warn(
|
|
147
|
-
`[b.stage] appId\uC640
|
|
148
|
-
appId\uB294 "${APP_ID_PREFIX}",
|
|
147
|
+
`[b.stage] appId\uC640 appKey\uAC00 \uC11C\uB85C \uBC14\uB010 \uAC83 \uAC19\uC2B5\uB2C8\uB2E4.
|
|
148
|
+
appId\uB294 "${APP_ID_PREFIX}", appKey\uB294 "${APP_KEY_PREFIX}"\uB85C \uC2DC\uC791\uD574\uC57C \uD569\uB2C8\uB2E4.
|
|
149
149
|
\uD30C\uD2B8\uB108 \uCF58\uC194\uC5D0\uC11C \uBC1C\uAE09\uBC1B\uC740 \uAC12\uC744 \uB2E4\uC2DC \uD655\uC778\uD574 \uC8FC\uC138\uC694.`
|
|
150
150
|
);
|
|
151
151
|
}
|
|
152
|
+
function resolveAppKey(config) {
|
|
153
|
+
return config.appKey || config.appSecret || void 0;
|
|
154
|
+
}
|
|
155
|
+
function warnOnMissingAppKey(appKey) {
|
|
156
|
+
if (appKey !== void 0) return;
|
|
157
|
+
console.warn(
|
|
158
|
+
`[b.stage] appKey\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4 \u2014 X-BSTAGE-APP-KEY \uD5E4\uB354 \uC5C6\uC774 \uD638\uCD9C\uB418\uC5B4 API\uAC00 401\uC744 \uB0C5\uB2C8\uB2E4.
|
|
159
|
+
.env\uC758 VITE_BSTAGE_APP_KEY(\uD30C\uD2B8\uB108 \uCF58\uC194\uC758 APP KEY)\uB97C \uD655\uC778\uD574 \uC8FC\uC138\uC694.`
|
|
160
|
+
);
|
|
161
|
+
}
|
|
152
162
|
var BstageClient = class {
|
|
153
163
|
constructor(config) {
|
|
154
|
-
|
|
164
|
+
const appKey = resolveAppKey(config);
|
|
165
|
+
warnOnMissingAppKey(appKey);
|
|
166
|
+
warnOnSwappedCredentials(config.appId, appKey);
|
|
155
167
|
const fetchFn = config.fetch ?? resolveBstageFetch;
|
|
156
168
|
this.http = new HttpClient({
|
|
157
169
|
baseUrl: config.baseUrl ?? resolveBaseUrl(),
|
|
@@ -160,7 +172,8 @@ var BstageClient = class {
|
|
|
160
172
|
headers: {
|
|
161
173
|
"Content-Type": "application/json",
|
|
162
174
|
"X-BSTAGE-APP-ID": config.appId,
|
|
163
|
-
"
|
|
175
|
+
// 값이 없으면(UI 전용 템플릿의 빈 .env) 헤더 자체를 빼서 "undefined" 문자열이 실리지 않게 한다.
|
|
176
|
+
...appKey !== void 0 ? { "X-BSTAGE-APP-KEY": appKey } : {},
|
|
164
177
|
"X-BSTAGE-TENANT-ID": config.tenantId
|
|
165
178
|
}
|
|
166
179
|
});
|
package/docs/API_REFERENCE.md
CHANGED
|
@@ -101,8 +101,8 @@ Platform → Template 이벤트는 `bubbles: false, composed: false`로 해당
|
|
|
101
101
|
|
|
102
102
|
```typescript
|
|
103
103
|
const client = new BstageClient({
|
|
104
|
-
appId: '
|
|
105
|
-
|
|
104
|
+
appId: 'bsa_xxx',
|
|
105
|
+
appKey: 'bsp_xxx',
|
|
106
106
|
tenantId: 'tenant-xxx',
|
|
107
107
|
})
|
|
108
108
|
```
|
|
@@ -112,7 +112,8 @@ const client = new BstageClient({
|
|
|
112
112
|
| 옵션 | 타입 | 설명 |
|
|
113
113
|
| ----------- | --------------------------------------------- | ------------------------------------------------------------ |
|
|
114
114
|
| `appId` | `string` | 앱 식별자 |
|
|
115
|
-
| `
|
|
115
|
+
| `appKey` | `string` | 앱 키(APP KEY). `appKey`·`appSecret` 중 하나는 필요합니다. 포털이 발급하고 게이트웨이 `X-BSTAGE-APP-KEY` 헤더로 검증하는 값. **브라우저에 노출되는 값**으로 비밀값이 아닙니다 |
|
|
116
|
+
| `appSecret` | `string` (deprecated) | `appKey`의 옛 이름. 값은 같습니다. 둘 다 주면 `appKey`가 우선하며, 0.4.0(공개 배포판 `@bstage-sdk/core` 기준)에서 제거됩니다 |
|
|
116
117
|
| `tenantId` | `string` | 테넌트 식별자 |
|
|
117
118
|
| `phase` | `'dev' \| 'qa' \| 'real' \| 'sandbox'` (선택) | 환경. 현재 미사용 (향후 확장용) |
|
|
118
119
|
| `baseUrl` | `string` (선택) | base URL 직접 지정. 미지정 시 `resolveBaseUrl()`로 자동 결정 |
|
|
@@ -173,7 +174,7 @@ const post = await client.get<PostResponse>('/content/v1/boards/{boardId}/posts/
|
|
|
173
174
|
|
|
174
175
|
응답 타입(`MenuResponse` 등)은 게이트웨이 API Reference Doc을 보고 필요한 필드만 프로젝트 안에 선언해 쓰는 것을 권장합니다.
|
|
175
176
|
|
|
176
|
-
내부적으로 `HttpClient`(Fetch 기반, 인터셉터 지원)를 사용하며, 모든 요청에 `X-BSTAGE-APP-ID`/`X-BSTAGE-APP-KEY`/`X-BSTAGE-TENANT-ID` 헤더를 자동
|
|
177
|
+
내부적으로 `HttpClient`(Fetch 기반, 인터셉터 지원)를 사용하며, 모든 요청에 `X-BSTAGE-APP-ID`/`X-BSTAGE-APP-KEY`/`X-BSTAGE-TENANT-ID` 헤더를 자동 포함합니다(앱 키가 없으면 `X-BSTAGE-APP-KEY`는 생략되고 생성 시 콘솔 경고가 납니다).
|
|
177
178
|
|
|
178
179
|
---
|
|
179
180
|
|
package/docs/BUILD_SYSTEM.md
CHANGED
|
@@ -58,7 +58,7 @@ Vite 빌드라서 `import.meta.env.VITE_BSTAGE_*`는 **빌드 시점에 문자
|
|
|
58
58
|
- **대상 판정** — 두 조건을 모두 만족할 때만 점검합니다.
|
|
59
59
|
1. 번들에 BstageClient의 앱 ID 헤더(`X-BSTAGE-APP-ID`)가 있을 것. API를 쓰지 않는 UI 전용 템플릿은 `client.ts`를 스캐폴드만 하고 import하지 않으면 트리셰이킹으로 사라지므로 대상이 아닙니다.
|
|
60
60
|
2. 소스가 `import.meta.env.VITE_BSTAGE_*`를 참조할 것. cli 0.40.1 이전 스캐폴드처럼 `client.ts`에 키를 리터럴로 박은 프로젝트는 `.env`가 없는 게 정상이므로(그 전환은 마이그레이션에서 `선택`) 대상이 아닙니다. 참조한 변수만 검사합니다.
|
|
61
|
-
- **검사 항목** — 값 누락, 자리표시자(`YOUR_APP_ID` 등) 잔존, appId·
|
|
61
|
+
- **검사 항목** — 값 누락, 자리표시자(`YOUR_APP_ID` 등) 잔존, appId·appKey 뒤바뀜, 접두사(`bsa_`/`bsp_`) 불일치. 앱 키는 `VITE_BSTAGE_APP_KEY`(정본)와 옛 이름 `VITE_BSTAGE_APP_SECRET`을 한 값으로 보며, 한쪽만 있으면 빌드가 다른 이름으로도 읽히게 이어 붙인다
|
|
62
62
|
- **env 해석** — Vite의 `loadEnv`를 그대로 씁니다. `.env` 파일뿐 아니라 CI가 `process.env`로 주입한 값도 번들에 인라인되므로 파일만 읽으면 오탐이 납니다.
|
|
63
63
|
- **빌드를 실패시키지 않습니다** — 값이 비어도 번들 자체는 정상적으로 나오고, 여기서 실패시키면 기존 소비자 CI가 깨집니다. 경고는 빌드 로그 맨 끝에 출력됩니다.
|
|
64
64
|
- 어느 환경에서 발급한 키인지(QA 키를 리얼에 썼는지)는 **감지할 수 없습니다** — 키 형식에 발급 환경이 담기지 않습니다. 그 계열은 [GETTING_STARTED.md](./GETTING_STARTED.md) "9.2 배포 전 점검"의 체크리스트가 담당합니다.
|
package/docs/GETTING_STARTED.md
CHANGED
|
@@ -6,7 +6,7 @@ b.stage 3rd-party 템플릿을 개발하고 배포하는 전체 흐름을 안내
|
|
|
6
6
|
|
|
7
7
|
- **Node.js** v20 이상
|
|
8
8
|
- **npm** 또는 **pnpm**
|
|
9
|
-
- **BstageClient 인증 정보** (선택) — API 호출에 필요한 `appId`, `
|
|
9
|
+
- **BstageClient 인증 정보** (선택) — API 호출에 필요한 `appId`, `appKey`(포털의 APP KEY)를 파트너 콘솔에서 발급받아 두면 프로젝트 생성 시 자동으로 설정됩니다.
|
|
10
10
|
|
|
11
11
|
---
|
|
12
12
|
|
|
@@ -259,11 +259,13 @@ import { BstageClient } from '@bstage-sdk/core'
|
|
|
259
259
|
|
|
260
260
|
export const client = new BstageClient({
|
|
261
261
|
appId: 'bsa_xxxxx',
|
|
262
|
-
|
|
262
|
+
appKey: 'bsp_xxxxx',
|
|
263
263
|
tenantId: 'myspace',
|
|
264
264
|
})
|
|
265
265
|
```
|
|
266
266
|
|
|
267
|
+
- `appKey`는 포털 화면의 **APP KEY**, 게이트웨이 헤더 `X-BSTAGE-APP-KEY`와 같은 값입니다. `VITE_` 환경변수로 번들에 평문으로 실려 브라우저에서 보이는 값이라 **비밀값이 아닙니다** — 접근 범위는 게이트웨이가 앱 단위로 제어합니다. 옛 이름 `appSecret`(`.env`의 `VITE_BSTAGE_APP_SECRET`)은 별칭으로 당분간 동작합니다([마이그레이션 가이드](./MIGRATION.md) 참고).
|
|
268
|
+
|
|
267
269
|
- 게이트웨이 base URL은 클라이언트가 임베드된 **페이지의 origin**(`location.origin + /gw`)으로 자동 결정됩니다. `{tenant}.sandstage.in`에 배포되면 그 환경의 게이트웨이로 가므로 환경별로 다시 빌드·설정할 필요가 없습니다.
|
|
268
270
|
- 로컬 개발에서는 **dev 서버**가 `.env`의 `VITE_BSTAGE_PHASE`(→ `vite.config.ts`의 `bstageDevPlugin`)를 보고 해당 phase 게이트웨이로 프록시합니다. 이 phase는 **dev 서버 설정**이지 `BstageClient`의 옵션이 아닙니다.
|
|
269
271
|
- 특정 게이트웨이를 직접 지정하려면 `baseUrl` 옵션을 사용합니다(예: 플랫폼 임베드가 아닌 standalone 앱).
|
|
@@ -629,7 +631,7 @@ CI에서는 `BSTAGE_TOKEN`·`BSTAGE_PORTAL_URL`(또는 `VITE_BSTAGE_PHASE`)·`BS
|
|
|
629
631
|
|
|
630
632
|
| 증상 | 원인 | 조치 | 소관 |
|
|
631
633
|
| ----------------------- | ---------------------------------------- | ----------------------------------------------- | ------------ |
|
|
632
|
-
| `401 Unauthorized` | 다른 환경(QA 등)의 키로 배포 / 키 미발급 | 배포할 환경에서 발급한 appId·
|
|
634
|
+
| `401 Unauthorized` | 다른 환경(QA 등)의 키로 배포 / 키 미발급 | 배포할 환경에서 발급한 appId·appKey인지 확인 | 파트너 콘솔 |
|
|
633
635
|
| `403 Forbidden` | Space ID가 프로젝트에 미등록 | BE에 Space ID 등록 요청 | BE |
|
|
634
636
|
| `400 MISSING_ORIGIN` | 키의 Allowed Origins에 배포 도메인 없음 | 배포 도메인을 키 설정에 추가 요청 | BE (키 설정) |
|
|
635
637
|
| 템플릿이 화면에 안 뜸 | 콘솔 "커스텀 템플릿 사용" 토글이 꺼짐 | 콘솔에서 토글 확인 | 콘솔 |
|
package/docs/INIT.md
CHANGED
|
@@ -31,7 +31,7 @@ Node.js v20 이상과 npm(또는 pnpm)만 있으면 됩니다. SDK 패키지는
|
|
|
31
31
|
| 4 | 패키지 매니저 | O | `npm` 또는 `pnpm` |
|
|
32
32
|
| 5 | API 키 설정 여부 | - | "없어도 시작할 수 있어요" |
|
|
33
33
|
| 6 | APP-ID | - | API 키가 있는 경우만 |
|
|
34
|
-
| 7 |
|
|
34
|
+
| 7 | APP KEY | - | API 키가 있는 경우만 |
|
|
35
35
|
|
|
36
36
|
### 레포명 결정 로직
|
|
37
37
|
|
package/docs/MIGRATION.md
CHANGED
|
@@ -45,6 +45,35 @@
|
|
|
45
45
|
|
|
46
46
|
> **`적용` 단계는 옛 프로젝트에서 실제로 따라가 본 뒤 적는다.** 새 `bstage init` 프로젝트에서 되는 것과, 기존 프로젝트가 이 절차만 보고 도달하는 것은 다르다. 따라 했을 때 깨지는 선행 조건이 있으면 **그 조건까지 적는다** — `core 0.44.0` 항목이 그 사례다(스크립트만 넣으면 `@types/node` 부재로 `TS2688`이 난다. 스캐폴드 템플릿을 고쳐 새 프로젝트는 해결됐지만, 먼저 써둔 적용 절차를 되짚지 않아 기존 프로젝트용 지침이 깨진 채 남았다).
|
|
47
47
|
|
|
48
|
-
> **헤더 규칙**: `
|
|
48
|
+
> **헤더 규칙**: `## → {패키지} {버전}` 형식으로, 그 변경을 실제로 낸 패키지와 버전을 명시한다(예: `→ cli 0.3.0`, `→ core 0.3.0`). 한 변경이 여러 패키지를 함께 범프시켰으면 소비자가 올려야 할 패키지를 `·`로 나열한다(예: `→ core 0.3.0 · cli 0.3.0`). 소비자는 이 헤더의 패키지를 해당 버전 **이상**으로 올려야 한다. 단순 의존성 재핀(dependency bump)만 된 패키지는 헤더에 적지 않는다.
|
|
49
|
+
>
|
|
50
|
+
> **정렬**: 릴리즈 **시점 최신이 위**. 패키지가 독립 버전이라 헤더 숫자만으로는 시간순 정렬이 되지 않으므로 릴리즈 순서를 기준으로 한다.
|
|
49
51
|
|
|
50
|
-
|
|
52
|
+
---
|
|
53
|
+
## → core 0.3.0 · cli 0.3.0
|
|
54
|
+
|
|
55
|
+
### 앱 키 이름 정리(`appSecret` → `appKey`, `VITE_BSTAGE_APP_SECRET` → `VITE_BSTAGE_APP_KEY`) — `판단` · `선택`
|
|
56
|
+
|
|
57
|
+
**영향**
|
|
58
|
+
|
|
59
|
+
- **기존 프로젝트가 반드시 할 일은 없다.** `appSecret`·`VITE_BSTAGE_APP_SECRET`은 deprecated 별칭으로 그대로 동작한다. 별칭은 **0.4.0**에서 제거되므로 그 전에 옮긴다.
|
|
60
|
+
- 감지: `src/shared/client.ts`에 `appSecret:`이 있거나, `.env`·`.env.example`에 `VITE_BSTAGE_APP_SECRET`이 있는 경우.
|
|
61
|
+
|
|
62
|
+
**변경 내용**
|
|
63
|
+
|
|
64
|
+
- 같은 값을 포털 화면(APP KEY)·포털 API(`appKey`)·게이트웨이 헤더(`X-BSTAGE-APP-KEY`)는 모두 KEY라고 부르는데 SDK만 `secret`이라고 불렀다. 이 값은 `VITE_` 환경변수라 **빌드 결과물에 평문으로 실려 브라우저에서 보이는 값**이고 비밀값이 아니다 — 이름이 사실과 반대여서 "숨겨야 하는 값"으로 오해하게 했다. SDK 이름을 나머지와 맞췄다.
|
|
65
|
+
- `BstageConfig.appKey`가 정본이고 `appSecret`은 deprecated 별칭이다(둘 다 주면 `appKey` 우선). 타입 주석도 "앱 시크릿"에서 "앱 키(APP KEY) — 브라우저에 노출되는 값"으로 바꿨다.
|
|
66
|
+
- 새 `bstage init` 스캐폴드는 `.env`·`.env.example`·`client.ts`·`vite-env.d.ts`에 새 이름을 쓴다.
|
|
67
|
+
- **소스와 `.env`가 다른 이름을 써도 빌드가 이어 붙인다.** `bstage build`(프리셋)와 `bstageDevPlugin`이 `VITE_BSTAGE_APP_KEY`·`VITE_BSTAGE_APP_SECRET` 중 한쪽만 있으면 다른 이름으로도 같은 값이 읽히게 `define`한다. 그래서 옛 `client.ts`(`appSecret: import.meta.env.VITE_BSTAGE_APP_SECRET`)를 둔 채 `.env`만 새 이름으로 바꿔도, 포털의 환경변수 입력이 새 이름으로 들어와도 번들에 값이 실린다. 둘 다 있으면 각자 실제 값이 그대로 쓰인다.
|
|
68
|
+
- `bstage build`의 인증 값 점검도 같은 순서로 본다 — 소스가 읽는 이름의 값이 우선, 다른 이름은 폴백. 보고에는 소스가 실제로 읽는 이름을 쓴다.
|
|
69
|
+
- 앱 키가 아예 없으면 `X-BSTAGE-APP-KEY` 헤더에 `"undefined"` 문자열을 실던 것을 헤더 생략 + 생성 시 콘솔 경고로 바꿨다. 게이트웨이 응답(401)은 같다.
|
|
70
|
+
|
|
71
|
+
**적용**
|
|
72
|
+
|
|
73
|
+
1. (자동) `@bstage-sdk/core`·`@bstage-sdk/cli`를 `0.3.0` 이상으로 올린다.
|
|
74
|
+
2. (판단) `src/shared/client.ts`의 `appSecret: import.meta.env.VITE_BSTAGE_APP_SECRET`을 `appKey: import.meta.env.VITE_BSTAGE_APP_KEY`로 바꾼다. 리터럴 키를 박아 둔 옛 프로젝트는 키 이름만 `appSecret` → `appKey`로 바꾼다.
|
|
75
|
+
3. (판단) `.env`·`.env.example`의 `VITE_BSTAGE_APP_SECRET=`을 `VITE_BSTAGE_APP_KEY=`로 바꾼다. 값은 그대로다 — 재발급이 아니다.
|
|
76
|
+
4. (판단) `src/vite-env.d.ts`의 `readonly VITE_BSTAGE_APP_SECRET: string`을 `readonly VITE_BSTAGE_APP_KEY: string`으로 바꾼다.
|
|
77
|
+
5. CI·배포 환경에 `VITE_BSTAGE_APP_SECRET`을 주입하고 있었다면 같은 값을 `VITE_BSTAGE_APP_KEY`로 주입한다. 2~4를 한 번에 못 옮기는 기간에는 위 별칭 덕에 어느 한쪽 이름만 있어도 빌드가 통과한다.
|
|
78
|
+
|
|
79
|
+
---
|
package/docs/SDK_ARCHITECTURE.md
CHANGED
|
@@ -35,7 +35,7 @@ Shadow DOM으로 서드파티 CSS가 플랫폼 UI에 영향을 주거나 플랫
|
|
|
35
35
|
|
|
36
36
|
### 토큰 비노출
|
|
37
37
|
|
|
38
|
-
서드파티 코드는 플랫폼 사용자의 인증 토큰에 직접 접근하지 않습니다. API 호출은 BstageClient가 `appId`/`
|
|
38
|
+
서드파티 코드는 플랫폼 사용자의 인증 토큰에 직접 접근하지 않습니다. API 호출은 BstageClient가 `appId`/`appKey`로 처리합니다.
|
|
39
39
|
|
|
40
40
|
---
|
|
41
41
|
|
|
@@ -145,7 +145,7 @@ Custom Element 등록(`customElements.define`)은 BstageElement 자체가 하지
|
|
|
145
145
|
|
|
146
146
|
파트너에게 공개된 API에 접근하는 HTTP 클라이언트입니다.
|
|
147
147
|
|
|
148
|
-
**설계 의도:** 서드파티가 플랫폼 API를 호출할 때, 플랫폼 사용자의 인증 토큰 대신 `appId`/`
|
|
148
|
+
**설계 의도:** 서드파티가 플랫폼 API를 호출할 때, 플랫폼 사용자의 인증 토큰 대신 `appId`/`appKey`/`tenantId`를 사용합니다. 이로써 서드파티 코드가 사용자 토큰에 접근할 수 없으며 API 접근 범위를 앱 단위로 제어할 수 있습니다.
|
|
149
149
|
|
|
150
150
|
**base URL 결정:** `resolveBaseUrl()` 함수가 `location.origin/gw`를 반환하여 현재 호스트 기반으로 요청합니다. 로컬 개발 시에는 devVitePlugin이 이 함수를 치환해 localhost 프록시로 라우팅합니다.
|
|
151
151
|
|
|
@@ -242,7 +242,7 @@ Shadow DOM은 **스타일 격리**이지 **보안 격리**가 아닙니다. Java
|
|
|
242
242
|
| 토큰/쿠키 탈취 | 민감 정보는 HttpOnly 쿠키로만 관리 |
|
|
243
243
|
| API 남용 | API Gateway에서 rate limiting |
|
|
244
244
|
|
|
245
|
-
서드파티는 BstageClient를 통해 `appId`/`
|
|
245
|
+
서드파티는 BstageClient를 통해 `appId`/`appKey`로 API에 접근하고 플랫폼 사용자의 인증 토큰에는 직접 접근하지 않습니다.
|
|
246
246
|
|
|
247
247
|
---
|
|
248
248
|
|
package/docs/SLOT_CATALOG.md
CHANGED
|
@@ -132,7 +132,7 @@ import { createTemplate, usePlatformEvent } from '@bstage-sdk/react'
|
|
|
132
132
|
import { BstageClient } from '@bstage-sdk/core'
|
|
133
133
|
import { useState } from 'react'
|
|
134
134
|
|
|
135
|
-
const client = new BstageClient({ appId: '...',
|
|
135
|
+
const client = new BstageClient({ appId: '...', appKey: '...', tenantId: '...' })
|
|
136
136
|
|
|
137
137
|
function TicketCustomField() {
|
|
138
138
|
const [resourceId, setResourceId] = useState<string>()
|