@bstage-sdk/cli 0.2.0 → 0.3.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/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
+ ---
@@ -2,14 +2,14 @@
2
2
  import { defineConfig } from "vite";
3
3
  import react from "@vitejs/plugin-react";
4
4
 
5
- // src/dev/devVitePlugin.ts
6
- import { createRequire } from "module";
7
- import { readFileSync as readFileSync2 } from "fs";
8
- import { join, dirname } from "path";
5
+ // src/vite/appKeyEnvAlias.ts
6
+ import { loadEnv } from "vite";
9
7
 
10
8
  // src/constants.ts
11
9
  import { readFileSync } from "fs";
12
10
  import { resolve } from "path";
11
+ var APP_KEY_VAR = "VITE_BSTAGE_APP_KEY";
12
+ var LEGACY_APP_KEY_VAR = "VITE_BSTAGE_APP_SECRET";
13
13
  var BSTAGE_INTERNAL_PREFIX = "/__bstage__";
14
14
  var LOGIN_PATH = `${BSTAGE_INTERNAL_PREFIX}/login`;
15
15
  var PROXY_PATH_PREFIX = "/__proxy__";
@@ -88,6 +88,31 @@ function resolveProjectTarget(root) {
88
88
  return pkg.bstage?.target === "admin" ? "admin" : "user";
89
89
  }
90
90
 
91
+ // src/vite/appKeyEnvAlias.ts
92
+ function appKeyEnvDefine(env) {
93
+ const key = env[APP_KEY_VAR]?.trim();
94
+ const legacy = env[LEGACY_APP_KEY_VAR]?.trim();
95
+ if (key && !legacy) return { [`import.meta.env.${LEGACY_APP_KEY_VAR}`]: JSON.stringify(key) };
96
+ if (legacy && !key) return { [`import.meta.env.${APP_KEY_VAR}`]: JSON.stringify(legacy) };
97
+ return {};
98
+ }
99
+ function loadProjectEnv(config, mode) {
100
+ return loadEnv(mode, config.envDir ?? config.root ?? process.cwd(), "VITE_");
101
+ }
102
+ function appKeyEnvAliasPlugin() {
103
+ return {
104
+ name: "bstage-app-key-env-alias",
105
+ config(config, { mode }) {
106
+ return { define: appKeyEnvDefine(loadProjectEnv(config, mode)) };
107
+ }
108
+ };
109
+ }
110
+
111
+ // src/dev/devVitePlugin.ts
112
+ import { createRequire } from "module";
113
+ import { readFileSync as readFileSync2 } from "fs";
114
+ import { join, dirname } from "path";
115
+
91
116
  // src/dev/loginPage.ts
92
117
  function getLoginPageHtml(phase, mode = "user") {
93
118
  const tokenPath = AUTH_TOKEN_PATH_BY_MODE[mode].replace(/^\/svc/, AUTH_PATH_PREFIX);
@@ -415,14 +440,14 @@ function getLoginPageHtml(phase, mode = "user") {
415
440
  // src/dev/proxyUtils.ts
416
441
  import { request as httpsRequest } from "https";
417
442
  import { gunzipSync, brotliDecompressSync, inflateSync } from "zlib";
418
- import { loadEnv } from "vite";
443
+ import { loadEnv as loadEnv2 } from "vite";
419
444
  var CF_ACCESS_ENV_KEYS = ["VITE_CF_ACCESS_CLIENT_ID", "VITE_CF_ACCESS_CLIENT_SECRET"];
420
445
  var cachedCreds;
421
446
  var missingWarned = false;
422
447
  function resolveCfAccessCreds() {
423
448
  if (cachedCreds !== void 0) return cachedCreds;
424
449
  const [idKey, secretKey] = CF_ACCESS_ENV_KEYS;
425
- const fileEnv = loadEnv("development", process.cwd(), "");
450
+ const fileEnv = loadEnv2("development", process.cwd(), "");
426
451
  const id = process.env[idKey] || fileEnv[idKey] || "";
427
452
  const secret = process.env[secretKey] || fileEnv[secretKey] || "";
428
453
  cachedCreds = id && secret ? { id, secret } : null;
@@ -542,7 +567,7 @@ function collectBody(req, maxBytes = MAX_REQUEST_BODY_BYTES) {
542
567
  }
543
568
 
544
569
  // src/dev/resolveHosts.ts
545
- import { loadEnv as loadEnv2 } from "vite";
570
+ import { loadEnv as loadEnv3 } from "vite";
546
571
  var HOST_ENV_KEYS = {
547
572
  gateway: "VITE_BSTAGE_GATEWAY_HOST",
548
573
  auth: "VITE_BSTAGE_AUTH_HOST",
@@ -559,7 +584,7 @@ function resolveDevHosts(phase, mode, cwd = process.cwd()) {
559
584
  auth: (tenantId) => getAuthHost(mode, phase, tenantId)
560
585
  };
561
586
  }
562
- const fileEnv = loadEnv2("development", cwd, "");
587
+ const fileEnv = loadEnv3("development", cwd, "");
563
588
  const read = (key) => (process.env[key] || fileEnv[key] || "").trim();
564
589
  const authKey = authKeyFor(mode);
565
590
  const gatewayHost = read(HOST_ENV_KEYS.gateway);
@@ -582,7 +607,7 @@ function resolveDevHosts(phase, mode, cwd = process.cwd()) {
582
607
  }
583
608
 
584
609
  // src/dev/resolvePhase.ts
585
- import { loadEnv as loadEnv3 } from "vite";
610
+ import { loadEnv as loadEnv4 } from "vite";
586
611
  var PHASES = ["dev", "qa", "real", "sandbox"];
587
612
  var DEFAULT_PHASE = "sandbox";
588
613
  function isPhase(value) {
@@ -597,7 +622,7 @@ function resolvePhase(explicit, cwd = process.cwd(), env = process.env) {
597
622
  }
598
623
  return { phase: explicit, source: "flag" };
599
624
  }
600
- const fileEnv = loadEnv3("development", cwd, "");
625
+ const fileEnv = loadEnv4("development", cwd, "");
601
626
  const fromEnv = env.VITE_BSTAGE_PHASE || fileEnv.VITE_BSTAGE_PHASE || "";
602
627
  if (fromEnv) {
603
628
  if (isPhase(fromEnv)) return { phase: fromEnv, source: "env" };
@@ -1949,10 +1974,13 @@ function bstageDevPlugin(options = {}) {
1949
1974
  return {
1950
1975
  name: "bstage-dev",
1951
1976
  enforce: "pre",
1952
- config() {
1977
+ config(config, { mode: mode2 }) {
1953
1978
  const root = process.cwd();
1954
1979
  projectRequire = createRequire(join(root, "package.json"));
1955
1980
  return {
1981
+ // 앱 키 env 별칭 — 소비자 `vite.config.ts`는 이 플러그인만 얹으므로 여기서도 이어 붙인다
1982
+ // (`bstage build`는 프리셋이 맡는다). 상세는 appKeyEnvAlias.ts.
1983
+ define: appKeyEnvDefine(loadProjectEnv(config, mode2)),
1956
1984
  optimizeDeps: {
1957
1985
  exclude: ["@bstage-sdk/core", "@bstage-sdk/react"]
1958
1986
  },
@@ -2064,7 +2092,7 @@ function bstageDevPlugin(options = {}) {
2064
2092
  // src/vite/preset.ts
2065
2093
  function createViteConfig(options = {}) {
2066
2094
  return defineConfig({
2067
- plugins: [react()],
2095
+ plugins: [react(), appKeyEnvAliasPlugin()],
2068
2096
  define: {
2069
2097
  "process.env.NODE_ENV": JSON.stringify("production")
2070
2098
  },
@@ -2091,6 +2119,8 @@ function createViteConfig(options = {}) {
2091
2119
  }
2092
2120
 
2093
2121
  export {
2122
+ APP_KEY_VAR,
2123
+ LEGACY_APP_KEY_VAR,
2094
2124
  LOGIN_PATH,
2095
2125
  isPublicPhase,
2096
2126
  TRANSLATION_LANGUAGES,
@@ -2101,6 +2131,8 @@ export {
2101
2131
  PORTAL_HOSTS,
2102
2132
  PORTAL_HOST_ENV_KEY,
2103
2133
  resolveProjectTarget,
2134
+ appKeyEnvDefine,
2135
+ appKeyEnvAliasPlugin,
2104
2136
  resolvePhase,
2105
2137
  describePhase,
2106
2138
  bstageDevPlugin,
package/dist/index.js CHANGED
@@ -1,6 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
+ APP_KEY_VAR,
3
4
  DEFAULT_TRANSLATION_TIER,
5
+ LEGACY_APP_KEY_VAR,
4
6
  LOGIN_PATH,
5
7
  PORTAL_HOSTS,
6
8
  PORTAL_HOST_ENV_KEY,
@@ -14,7 +16,7 @@ import {
14
16
  isPublicPhase,
15
17
  resolvePhase,
16
18
  resolveProjectTarget
17
- } from "./chunk-FER3TEK7.js";
19
+ } from "./chunk-COUDGZIA.js";
18
20
 
19
21
  // src/index.ts
20
22
  import { readFileSync as readFileSync2 } from "fs";
@@ -157,10 +159,11 @@ __reg__.forEach(__registerTemplate__);
157
159
  // src/build/credentialCheck.ts
158
160
  var CLIENT_MARKER = "X-BSTAGE-APP-ID";
159
161
  var APP_ID_PREFIX = "bsa_";
160
- var APP_SECRET_PREFIX = "bsp_";
162
+ var APP_KEY_PREFIX = "bsp_";
161
163
  var CREDENTIAL_ENV_VARS = [
162
164
  "VITE_BSTAGE_APP_ID",
163
- "VITE_BSTAGE_APP_SECRET",
165
+ APP_KEY_VAR,
166
+ LEGACY_APP_KEY_VAR,
164
167
  "VITE_BSTAGE_TENANT_ID"
165
168
  ];
166
169
  function usesBstageClient(bundle) {
@@ -180,54 +183,77 @@ function collectReferencedVars(sources) {
180
183
  return referenced;
181
184
  }
182
185
  function checkCredentials(env2, referenced) {
183
- const issues = [];
184
- for (const varName of CREDENTIAL_ENV_VARS) {
185
- if (!referenced.has(varName)) continue;
186
- const value = env2[varName]?.trim();
186
+ const appId = referenced.has("VITE_BSTAGE_APP_ID") ? env2.VITE_BSTAGE_APP_ID?.trim() : void 0;
187
+ const appKeys = resolveAppKeys(env2, referenced);
188
+ const targets = [
189
+ ...referenced.has("VITE_BSTAGE_APP_ID") ? [{ varName: "VITE_BSTAGE_APP_ID", value: appId }] : [],
190
+ ...appKeys,
191
+ ...referenced.has("VITE_BSTAGE_TENANT_ID") ? [{ varName: "VITE_BSTAGE_TENANT_ID", value: env2.VITE_BSTAGE_TENANT_ID?.trim() }] : []
192
+ ];
193
+ const presenceIssues = targets.flatMap(({ varName, value }) => {
187
194
  if (!value) {
188
- issues.push({
189
- level: "error",
190
- varName,
191
- message: "\uAC12\uC774 \uC5C6\uC2B5\uB2C8\uB2E4 \u2014 \uBC88\uB4E4\uC5D0 undefined\uAC00 \uBC15\uD600 \uBC30\uD3EC \uD6C4 401\uC774 \uB0A9\uB2C8\uB2E4."
192
- });
193
- continue;
195
+ return [
196
+ {
197
+ level: "error",
198
+ varName,
199
+ message: "\uAC12\uC774 \uC5C6\uC2B5\uB2C8\uB2E4 \u2014 \uBC88\uB4E4\uC5D0 undefined\uAC00 \uBC15\uD600 \uBC30\uD3EC \uD6C4 401\uC774 \uB0A9\uB2C8\uB2E4."
200
+ }
201
+ ];
194
202
  }
195
203
  if (isPlaceholderValue(value)) {
196
- issues.push({
197
- level: "error",
198
- varName,
199
- message: "\uC790\uB9AC\uD45C\uC2DC\uC790\uAC00 \uADF8\uB300\uB85C\uC785\uB2C8\uB2E4 \u2014 \uD30C\uD2B8\uB108 \uCF58\uC194\uC5D0\uC11C \uBC1C\uAE09\uBC1B\uC740 \uAC12\uC73C\uB85C \uBC14\uAFB8\uC138\uC694."
200
- });
204
+ return [
205
+ {
206
+ level: "error",
207
+ varName,
208
+ message: "\uC790\uB9AC\uD45C\uC2DC\uC790\uAC00 \uADF8\uB300\uB85C\uC785\uB2C8\uB2E4 \u2014 \uD30C\uD2B8\uB108 \uCF58\uC194\uC5D0\uC11C \uBC1C\uAE09\uBC1B\uC740 \uAC12\uC73C\uB85C \uBC14\uAFB8\uC138\uC694."
209
+ }
210
+ ];
201
211
  }
202
- }
203
- const appId = referenced.has("VITE_BSTAGE_APP_ID") ? env2.VITE_BSTAGE_APP_ID?.trim() : void 0;
204
- const appSecret = referenced.has("VITE_BSTAGE_APP_SECRET") ? env2.VITE_BSTAGE_APP_SECRET?.trim() : void 0;
205
- const appIdUsable = !!appId && !isPlaceholderValue(appId);
206
- const appSecretUsable = !!appSecret && !isPlaceholderValue(appSecret);
207
- const swapped = appIdUsable && appId.startsWith(APP_SECRET_PREFIX) || appSecretUsable && appSecret.startsWith(APP_ID_PREFIX);
212
+ return [];
213
+ });
214
+ const usableAppId = appId && !isPlaceholderValue(appId) ? appId : void 0;
215
+ const usableAppKeys = appKeys.filter(
216
+ (k) => !!k.value && !isPlaceholderValue(k.value)
217
+ );
218
+ const swappedKey = usableAppKeys.find((k) => k.value.startsWith(APP_ID_PREFIX));
219
+ const swapped = usableAppId?.startsWith(APP_KEY_PREFIX) || swappedKey !== void 0;
208
220
  if (swapped) {
209
- issues.push({
210
- level: "error",
211
- varName: "VITE_BSTAGE_APP_ID",
212
- message: `APP_ID\uC640 APP_SECRET\uC774 \uC11C\uB85C \uBC14\uB010 \uAC83 \uAC19\uC2B5\uB2C8\uB2E4 \u2014 APP_ID\uB294 "${APP_ID_PREFIX}", APP_SECRET\uC740 "${APP_SECRET_PREFIX}"\uB85C \uC2DC\uC791\uD574\uC57C \uD569\uB2C8\uB2E4.`
213
- });
214
- return issues;
215
- }
216
- if (appIdUsable && !appId.startsWith(APP_ID_PREFIX)) {
217
- issues.push({
218
- level: "warn",
219
- varName: "VITE_BSTAGE_APP_ID",
220
- message: `"${APP_ID_PREFIX}"\uB85C \uC2DC\uC791\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4 \u2014 \uD30C\uD2B8\uB108 \uCF58\uC194 \uBC1C\uAE09 \uAC12\uC774 \uB9DE\uB294\uC9C0 \uD655\uC778\uD558\uC138\uC694.`
221
- });
222
- }
223
- if (appSecretUsable && !appSecret.startsWith(APP_SECRET_PREFIX)) {
224
- issues.push({
225
- level: "warn",
226
- varName: "VITE_BSTAGE_APP_SECRET",
227
- message: `"${APP_SECRET_PREFIX}"\uB85C \uC2DC\uC791\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4 \u2014 \uD30C\uD2B8\uB108 \uCF58\uC194 \uBC1C\uAE09 \uAC12\uC774 \uB9DE\uB294\uC9C0 \uD655\uC778\uD558\uC138\uC694.`
228
- });
229
- }
230
- return issues;
221
+ const keyLabel = shortName(swappedKey?.varName ?? appKeys[0]?.varName ?? APP_KEY_VAR);
222
+ return [
223
+ ...presenceIssues,
224
+ {
225
+ level: "error",
226
+ varName: "VITE_BSTAGE_APP_ID",
227
+ message: `APP_ID\uC640 ${keyLabel}\uAC00 \uC11C\uB85C \uBC14\uB010 \uAC83 \uAC19\uC2B5\uB2C8\uB2E4 \u2014 APP_ID\uB294 "${APP_ID_PREFIX}", ${keyLabel}\uB294 "${APP_KEY_PREFIX}"\uB85C \uC2DC\uC791\uD574\uC57C \uD569\uB2C8\uB2E4.`
228
+ }
229
+ ];
230
+ }
231
+ const prefixWarnings = [
232
+ ...usableAppId && !usableAppId.startsWith(APP_ID_PREFIX) ? [
233
+ {
234
+ level: "warn",
235
+ varName: "VITE_BSTAGE_APP_ID",
236
+ message: `"${APP_ID_PREFIX}"\uB85C \uC2DC\uC791\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4 \u2014 \uD30C\uD2B8\uB108 \uCF58\uC194 \uBC1C\uAE09 \uAC12\uC774 \uB9DE\uB294\uC9C0 \uD655\uC778\uD558\uC138\uC694.`
237
+ }
238
+ ] : [],
239
+ ...usableAppKeys.filter((k) => !k.value.startsWith(APP_KEY_PREFIX)).map(
240
+ (k) => ({
241
+ level: "warn",
242
+ varName: k.varName,
243
+ message: `"${APP_KEY_PREFIX}"\uB85C \uC2DC\uC791\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4 \u2014 \uD30C\uD2B8\uB108 \uCF58\uC194 \uBC1C\uAE09 \uAC12\uC774 \uB9DE\uB294\uC9C0 \uD655\uC778\uD558\uC138\uC694.`
244
+ })
245
+ )
246
+ ];
247
+ return [...presenceIssues, ...prefixWarnings];
248
+ }
249
+ function resolveAppKeys(env2, referenced) {
250
+ return [APP_KEY_VAR, LEGACY_APP_KEY_VAR].filter((varName) => referenced.has(varName)).map((varName) => {
251
+ const other = varName === APP_KEY_VAR ? LEGACY_APP_KEY_VAR : APP_KEY_VAR;
252
+ return { varName, value: env2[varName]?.trim() || env2[other]?.trim() || void 0 };
253
+ });
254
+ }
255
+ function shortName(varName) {
256
+ return varName.replace(/^VITE_BSTAGE_/, "");
231
257
  }
232
258
 
233
259
  // src/commands/build.ts
@@ -1739,7 +1765,7 @@ ${commandsSection}
1739
1765
  - \uC804\uC5ED CSS \uD30C\uC77C(\`<link>\`, \uC678\uBD80 \uC2A4\uD0C0\uC77C\uC2DC\uD2B8)\uC744 \uC0AC\uC6A9\uD558\uC9C0 \uC54A\uB294\uB2E4 \u2014 Shadow DOM \uB0B4\uBD80\uC5D0 \uC801\uC6A9\uB418\uC9C0 \uC54A\uB294\uB2E4.
1740
1766
  - **\`import './style.css'\`\uB85C \uC2A4\uD0C0\uC77C\uC744 \uB123\uC9C0 \uC54A\uB294\uB2E4 \u2014 \uBC30\uD3EC \uC0B0\uCD9C\uBB3C\uC5D0\uC11C \uC0AC\uB77C\uC9C4\uB2E4.** \uB85C\uCEEC \uAC1C\uBC1C \uD654\uBA74\uC5D0\uC11C\uB294 \uC801\uC6A9\uB418\uC5B4 \uB208\uCE58\uCC44\uAE30 \uC5B4\uB835\uB2E4. CSS \uD30C\uC77C\uC744 \uC4F0\uB824\uBA74 \`import css from './style.css?inline'\`\uC73C\uB85C \uBB38\uC790\uC5F4\uC744 \uAC00\uC838\uC640 \`createTemplate(..., { styles: css })\`\uC5D0 \uB118\uAE30\uAC70\uB098 \uCEF4\uD3EC\uB10C\uD2B8 \uC548 \`<style>{css}</style>\`\uB85C \uB80C\uB354\uD55C\uB2E4. inline \`style={{}}\`\uB3C4 \uB41C\uB2E4
1741
1767
  - UI \uC0C9\xB7\uD0C0\uC774\uD3EC\xB7\uADF8\uB9BC\uC790\uB97C hex/rgb\uB85C \uD558\uB4DC\uCF54\uB529\uD558\uC9C0 \uC54A\uB294\uB2E4 \u2014 \uC704 '\uB514\uC790\uC778' \uC139\uC158\uC758 \uB514\uC790\uC778 \uD1A0\uD070\uC744 \uC0AC\uC6A9
1742
- - \uC571\uD0A4(APP-ID \`bsa_\u2026\`\xB7Secret \`bsp_\u2026\`, \uC5B4\uB4DC\uBBFC \uAC8C\uC774\uD2B8\uC6E8\uC774 \`bsm_\u2026\`)\uB97C \uC18C\uC2A4 \uCF54\uB4DC\uC5D0 \uB9AC\uD130\uB7F4\uB85C \uB123\uC9C0 \uC54A\uB294\uB2E4 \u2014 \uC778\uC99D \uAC12\uC740 \`.env\`(\uCEE4\uBC0B \uC548 \uB428)\uC5D0\uC11C \`import.meta.env.VITE_BSTAGE_*\`\uB85C\uB9CC \uC8FC\uC785\uD55C\uB2E4. pre-commit \uD6C5\uC774 \uCEE4\uBC0B \uC804 \uAC80\uCD9C\xB7\uCC28\uB2E8\uD558\uBA70 \`--no-verify\`\uB85C \uC6B0\uD68C\uD558\uC9C0 \uC54A\uB294\uB2E4
1768
+ - \uC571\uD0A4(APP-ID \`bsa_\u2026\`\xB7APP KEY \`bsp_\u2026\`, \uC5B4\uB4DC\uBBFC \uAC8C\uC774\uD2B8\uC6E8\uC774 \`bsm_\u2026\`)\uB97C \uC18C\uC2A4 \uCF54\uB4DC\uC5D0 \uB9AC\uD130\uB7F4\uB85C \uB123\uC9C0 \uC54A\uB294\uB2E4 \u2014 \uC778\uC99D \uAC12\uC740 \`.env\`(\uCEE4\uBC0B \uC548 \uB428)\uC5D0\uC11C \`import.meta.env.VITE_BSTAGE_*\`\uB85C\uB9CC \uC8FC\uC785\uD55C\uB2E4. pre-commit \uD6C5\uC774 \uCEE4\uBC0B \uC804 \uAC80\uCD9C\xB7\uCC28\uB2E8\uD558\uBA70 \`--no-verify\`\uB85C \uC6B0\uD68C\uD558\uC9C0 \uC54A\uB294\uB2E4
1743
1769
  ${apiForbidBullet}
1744
1770
  `;
1745
1771
  }
@@ -1761,14 +1787,15 @@ function claudeMd() {
1761
1787
  }
1762
1788
 
1763
1789
  // src/templates/env.ts
1764
- function env({ phase, appId, appSecret, tenantId }) {
1790
+ function env({ phase, appId, appKey, tenantId }) {
1765
1791
  return `VITE_BSTAGE_PHASE=${phase}
1766
1792
 
1767
1793
  # \uC778\uC99D \uC815\uBCF4(\uC120\uD0DD) \u2014 BstageClient\uB85C API\uB97C \uD638\uCD9C\uD560 \uB54C\uB9CC \uD544\uC694\uD569\uB2C8\uB2E4.
1768
1794
  # API \uC5C6\uC774 UI\uB9CC \uB80C\uB354\uD558\uB294 \uD15C\uD50C\uB9BF\uC740 \uAC12\uC744 \uCC44\uC6B0\uC9C0 \uC54A\uC544\uB3C4 \uB429\uB2C8\uB2E4.
1769
1795
  # .env\uB294 \uCEE4\uBC0B\uB418\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4(.gitignore). \uAC12\uC740 b.stage \uD30C\uD2B8\uB108 \uCF58\uC194\uC5D0\uC11C \uBC1C\uAE09.
1796
+ # APP_KEY\uB294 \uD3EC\uD138\uC758 "APP KEY"\uC640 \uAC19\uC740 \uAC12\uC785\uB2C8\uB2E4. \uBE0C\uB77C\uC6B0\uC800\uC5D0 \uB178\uCD9C\uB418\uB294 \uAC12\uC774\uB77C \uBE44\uBC00\uAC12\uC774 \uC544\uB2D9\uB2C8\uB2E4.
1770
1797
  VITE_BSTAGE_APP_ID=${appId}
1771
- VITE_BSTAGE_APP_SECRET=${appSecret}
1798
+ VITE_BSTAGE_APP_KEY=${appKey}
1772
1799
  VITE_BSTAGE_TENANT_ID=${tenantId}
1773
1800
 
1774
1801
  # CF Access \uC790\uACA9\uC99D\uBA85 \u2014 dev\xB7qa phase\uB85C \uAC1C\uBC1C\uD560 \uB54C\uB9CC \uD544\uC694\uD569\uB2C8\uB2E4.
@@ -1892,9 +1919,9 @@ createTemplate(MyWidget, {
1892
1919
  VITE_BSTAGE_PHASE=dev # dev | qa | real | sandbox
1893
1920
  \`\`\`
1894
1921
 
1895
- API \uD0A4(APP-ID/Secret)\uB294 \`.env\`\uC5D0\uC11C \uC124\uC815\uD569\uB2C8\uB2E4. \`.env\`\uB294 \uCEE4\uBC0B\uB418\uC9C0 \uC54A\uC73C\uB2C8, clone \uD6C4 \`.env.example\`\uC744 \`.env\`\uB85C \uBCF5\uC0AC\uD574 \uAC12\uC744 \uCC44\uC6B0\uC138\uC694.
1922
+ API \uD0A4(APP-ID/APP KEY)\uB294 \`.env\`\uC5D0\uC11C \uC124\uC815\uD569\uB2C8\uB2E4. \`.env\`\uB294 \uCEE4\uBC0B\uB418\uC9C0 \uC54A\uC73C\uB2C8, clone \uD6C4 \`.env.example\`\uC744 \`.env\`\uB85C \uBCF5\uC0AC\uD574 \uAC12\uC744 \uCC44\uC6B0\uC138\uC694.
1896
1923
 
1897
- > **\uBCF4\uC548**: \uC571\uD0A4 \uAC12\uC744 \uC18C\uC2A4 \uCF54\uB4DC(\uC608: \`client.ts\`)\uC5D0 \uC9C1\uC811 \uB123\uC9C0 \uB9C8\uC138\uC694 \u2014 \uBC18\uB4DC\uC2DC \`.env\`\uB85C\uB9CC \uC8FC\uC785\uD569\uB2C8\uB2E4. \`bstage init\`\uC774 \uC124\uCE58\uD55C pre-commit \uD6C5\uC774 \uCEE4\uBC0B \uC804 \uD0A4 \uB9AC\uD130\uB7F4\uC744 \uCC28\uB2E8\uD558\uBA70, \`--no-verify\`\uB85C \uC6B0\uD68C\uD558\uC9C0 \uB9C8\uC138\uC694.
1924
+ > **\uADDC\uCE59**: \uC571\uD0A4 \uAC12\uC744 \uC18C\uC2A4 \uCF54\uB4DC(\uC608: \`client.ts\`)\uC5D0 \uC9C1\uC811 \uB123\uC9C0 \uB9C8\uC138\uC694 \u2014 \uBC18\uB4DC\uC2DC \`.env\`\uB85C\uB9CC \uC8FC\uC785\uD569\uB2C8\uB2E4. APP KEY\uB294 \uBE0C\uB77C\uC6B0\uC800\uC5D0 \uB178\uCD9C\uB418\uB294 \uAC12\uC774\uB77C \uBE44\uBC00\uC740 \uC544\uB2C8\uC9C0\uB9CC, \uD658\uACBD\uB9C8\uB2E4 \uAC12\uC774 \uB2E4\uB974\uACE0 \uC800\uC7A5\uC18C\uC5D0 \uBC15\uD788\uBA74 \uC7AC\uBC1C\uAE09 \uB54C \uCF54\uB4DC \uC218\uC815\uC774 \uB530\uB77C\uC635\uB2C8\uB2E4. \`bstage init\`\uC774 \uC124\uCE58\uD55C pre-commit \uD6C5\uC774 \uCEE4\uBC0B \uC804 \uD0A4 \uB9AC\uD130\uB7F4\uC744 \uCC28\uB2E8\uD558\uBA70, \`--no-verify\`\uB85C \uC6B0\uD68C\uD558\uC9C0 \uB9C8\uC138\uC694.
1898
1925
 
1899
1926
  ## SDK \uC5C5\uB370\uC774\uD2B8
1900
1927
 
@@ -2541,7 +2568,7 @@ function viteEnvDts() {
2541
2568
  interface ImportMetaEnv {
2542
2569
  readonly VITE_BSTAGE_PHASE: string
2543
2570
  readonly VITE_BSTAGE_APP_ID: string
2544
- readonly VITE_BSTAGE_APP_SECRET: string
2571
+ readonly VITE_BSTAGE_APP_KEY: string
2545
2572
  readonly VITE_BSTAGE_TENANT_ID: string
2546
2573
  }
2547
2574
 
@@ -2559,7 +2586,7 @@ function clientTs() {
2559
2586
  // clone \uD6C4 .env.example\uC744 .env\uB85C \uBCF5\uC0AC\uD574 \uAC12\uC744 \uCC44\uC6B0\uC138\uC694.
2560
2587
  export const client = new BstageClient({
2561
2588
  appId: import.meta.env.VITE_BSTAGE_APP_ID,
2562
- appSecret: import.meta.env.VITE_BSTAGE_APP_SECRET,
2589
+ appKey: import.meta.env.VITE_BSTAGE_APP_KEY,
2563
2590
  tenantId: import.meta.env.VITE_BSTAGE_TENANT_ID,
2564
2591
  })
2565
2592
  `;
@@ -2665,7 +2692,7 @@ async function initCommand(options) {
2665
2692
  let phase;
2666
2693
  let target;
2667
2694
  let appId = "";
2668
- let appSecret = "";
2695
+ let appKey = "";
2669
2696
  let tenantId = "";
2670
2697
  if (options.yes) {
2671
2698
  const targetOpt = options.target ?? "user";
@@ -2758,11 +2785,11 @@ async function initCommand(options) {
2758
2785
  message: "\uD30C\uD2B8\uB108 \uCF58\uC194\uC5D0\uC11C \uBC1C\uAE09\uBC1B\uC740 APP-ID\uB97C \uC785\uB825\uD574 \uC8FC\uC138\uC694."
2759
2786
  }) ?? "";
2760
2787
  if (p2.isCancel(appId)) return cancelled();
2761
- appSecret = await p2.text({
2762
- message: "\uD30C\uD2B8\uB108 \uCF58\uC194\uC5D0\uC11C \uBC1C\uAE09\uBC1B\uC740 Secret \uAC12\uC744 \uC785\uB825\uD574 \uC8FC\uC138\uC694."
2788
+ appKey = await p2.text({
2789
+ message: "\uD30C\uD2B8\uB108 \uCF58\uC194\uC5D0\uC11C \uBC1C\uAE09\uBC1B\uC740 APP KEY\uB97C \uC785\uB825\uD574 \uC8FC\uC138\uC694."
2763
2790
  }) ?? "";
2764
- if (p2.isCancel(appSecret)) return cancelled();
2765
- warnIfCredentialsSwapped(appId, appSecret);
2791
+ if (p2.isCancel(appKey)) return cancelled();
2792
+ warnIfCredentialsSwapped(appId, appKey);
2766
2793
  tenantId = space;
2767
2794
  }
2768
2795
  }
@@ -2816,7 +2843,7 @@ async function initCommand(options) {
2816
2843
  env({
2817
2844
  phase,
2818
2845
  appId: appId || "YOUR_APP_ID",
2819
- appSecret: appSecret || "YOUR_APP_SECRET",
2846
+ appKey: appKey || "YOUR_APP_KEY",
2820
2847
  tenantId: tenantId || "YOUR_TENANT_ID"
2821
2848
  })
2822
2849
  ],
@@ -2825,7 +2852,7 @@ async function initCommand(options) {
2825
2852
  env({
2826
2853
  phase,
2827
2854
  appId: "YOUR_APP_ID",
2828
- appSecret: "YOUR_APP_SECRET",
2855
+ appKey: "YOUR_APP_KEY",
2829
2856
  tenantId: "YOUR_TENANT_ID"
2830
2857
  })
2831
2858
  ]
@@ -2925,12 +2952,12 @@ function validateElementName(v) {
2925
2952
  }
2926
2953
  }
2927
2954
  var APP_ID_PREFIX2 = "bsa_";
2928
- var APP_SECRET_PREFIX2 = "bsp_";
2929
- function warnIfCredentialsSwapped(appId, appSecret) {
2930
- if (!appId.startsWith(APP_SECRET_PREFIX2) && !appSecret.startsWith(APP_ID_PREFIX2)) return;
2955
+ var APP_KEY_PREFIX2 = "bsp_";
2956
+ function warnIfCredentialsSwapped(appId, appKey) {
2957
+ if (!appId.startsWith(APP_KEY_PREFIX2) && !appKey.startsWith(APP_ID_PREFIX2)) return;
2931
2958
  p2.log.warn(
2932
- `APP-ID\uC640 Secret\uC774 \uC11C\uB85C \uBC14\uB010 \uAC83 \uAC19\uC2B5\uB2C8\uB2E4.
2933
- APP-ID\uB294 "${APP_ID_PREFIX2}", Secret\uC740 "${APP_SECRET_PREFIX2}"\uB85C \uC2DC\uC791\uD569\uB2C8\uB2E4.
2959
+ `APP-ID\uC640 APP KEY\uAC00 \uC11C\uB85C \uBC14\uB010 \uAC83 \uAC19\uC2B5\uB2C8\uB2E4.
2960
+ APP-ID\uB294 "${APP_ID_PREFIX2}", APP KEY\uB294 "${APP_KEY_PREFIX2}"\uB85C \uC2DC\uC791\uD569\uB2C8\uB2E4.
2934
2961
  \uD30C\uD2B8\uB108 \uCF58\uC194\uC5D0\uC11C \uBC1C\uAE09\uBC1B\uC740 \uAC12\uC744 \uB2E4\uC2DC \uD655\uC778\uD574 \uC8FC\uC138\uC694.`
2935
2962
  );
2936
2963
  }
@@ -28,6 +28,25 @@ interface BstageDevPluginOptions {
28
28
  */
29
29
  declare function bstageDevPlugin(options?: BstageDevPluginOptions): Plugin;
30
30
 
31
+ /**
32
+ * 앱 키 환경변수의 새 이름(`VITE_BSTAGE_APP_KEY`)과 옛 이름(`VITE_BSTAGE_APP_SECRET`)을 서로 이어 붙이는
33
+ * `define` 항목을 만든다.
34
+ *
35
+ * 별칭 기간에는 소스와 `.env`가 다른 이름을 쓰는 조합이 실제로 생긴다 — 기존 프로젝트의 `client.ts`는
36
+ * 옛 이름을 읽는데 포털의 환경변수 입력은 새 이름을 넣고, 반대로 새 스캐폴드에 옛 `.env`를 복사해
37
+ * 오기도 한다. Vite는 `import.meta.env.X`를 빌드 시점에 문자열로 치환하므로 이 조합은 번들에
38
+ * undefined가 박혀 배포 뒤 401로만 드러난다. 한쪽만 있으면 다른 이름으로도 같은 값이 읽히게 한다.
39
+ *
40
+ * - 둘 다 있으면 손대지 않는다(각자 실제 값). 둘 다 없으면 아무것도 정의하지 않는다(UI 전용 템플릿).
41
+ * - 값은 `JSON.stringify`로 감싼다 — define은 코드 조각 치환이라 따옴표가 없으면 식별자가 된다.
42
+ */
43
+ declare function appKeyEnvDefine(env: Record<string, string | undefined>): Record<string, string>;
44
+ /**
45
+ * `appKeyEnvDefine`을 Vite 설정에 얹는 플러그인. `bstage build`(프리셋)와 `bstageDevPlugin`(dev 서버)이
46
+ * 함께 쓴다.
47
+ */
48
+ declare function appKeyEnvAliasPlugin(): Plugin;
49
+
31
50
  interface VitePresetOptions {
32
51
  /** Entry file path (default: 'src/index.tsx') */
33
52
  entry?: string;
@@ -40,7 +59,8 @@ interface VitePresetOptions {
40
59
  *
41
60
  * `<script>` 태그로 로드 가능한 IIFE 번들(`template.js`)을 출력.
42
61
  * React를 번들에 포함(외부화하지 않음). CSS는 Shadow DOM 주입을 위해 JS에 인라인.
62
+ * 앱 키 env 별칭(`appKeyEnvAliasPlugin`)을 얹어 옛 이름·새 이름 어느 쪽 `.env`든 번들에 실리게 한다.
43
63
  */
44
64
  declare function createViteConfig(options?: VitePresetOptions): UserConfig;
45
65
 
46
- export { type BstageDevPluginOptions, type VitePresetOptions, bstageDevPlugin, createViteConfig };
66
+ export { type BstageDevPluginOptions, type VitePresetOptions, appKeyEnvAliasPlugin, appKeyEnvDefine, bstageDevPlugin, createViteConfig };
@@ -1,8 +1,12 @@
1
1
  import {
2
+ appKeyEnvAliasPlugin,
3
+ appKeyEnvDefine,
2
4
  bstageDevPlugin,
3
5
  createViteConfig
4
- } from "../chunk-FER3TEK7.js";
6
+ } from "../chunk-COUDGZIA.js";
5
7
  export {
8
+ appKeyEnvAliasPlugin,
9
+ appKeyEnvDefine,
6
10
  bstageDevPlugin,
7
11
  createViteConfig
8
12
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bstage-sdk/cli",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "bstage SDK CLI — build b.stage templates",
5
5
  "type": "module",
6
6
  "publishConfig": {
@@ -31,13 +31,13 @@
31
31
  "commander": "^12.0.0",
32
32
  "picocolors": "^1.1.1",
33
33
  "vite": "^7.0.0",
34
- "@bstage-sdk/core": "0.2.0"
34
+ "@bstage-sdk/core": "0.3.0"
35
35
  },
36
36
  "devDependencies": {
37
37
  "@types/node": "^25.5.0",
38
38
  "tsup": "^8.0.0",
39
39
  "typescript": "^5.5.0",
40
- "@bstage-sdk/design": "0.2.0"
40
+ "@bstage-sdk/design": "0.3.0"
41
41
  },
42
42
  "license": "MIT",
43
43
  "scripts": {