@bstage-sdk/cli 0.8.7 → 0.9.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.
Files changed (2) hide show
  1. package/dist/index.js +280 -74
  2. package/package.json +4 -4
package/dist/index.js CHANGED
@@ -20,8 +20,8 @@ import {
20
20
  } from "./chunk-MSXSJHUT.js";
21
21
 
22
22
  // src/index.ts
23
- import { readFileSync as readFileSync5 } from "fs";
24
- import { dirname as dirname10, join as join17 } from "path";
23
+ import { readFileSync as readFileSync6 } from "fs";
24
+ import { dirname as dirname11, join as join19 } from "path";
25
25
  import { fileURLToPath as fileURLToPath2 } from "url";
26
26
  import { Command } from "commander";
27
27
 
@@ -1130,7 +1130,7 @@ async function aiDoctorCommand(o, deps = {}) {
1130
1130
  }
1131
1131
 
1132
1132
  // src/commands/build.ts
1133
- import { resolve as resolve2, join as join6, relative as relative2, sep as sep3 } from "path";
1133
+ import { resolve as resolve2, join as join8, relative as relative2, sep as sep3 } from "path";
1134
1134
  import { readFile as readFile2, access, readdir, rename, mkdir, rm } from "fs/promises";
1135
1135
  import { build as viteBuild, loadEnv } from "vite";
1136
1136
  import pc7 from "picocolors";
@@ -1261,6 +1261,181 @@ __reg__.forEach(__registerTemplate__);
1261
1261
  };
1262
1262
  }
1263
1263
 
1264
+ // src/vite/bundledPackagesPlugin.ts
1265
+ import { readFileSync as readFileSync4 } from "fs";
1266
+ import { dirname as dirname5, isAbsolute as isAbsolute2, join as join5 } from "path";
1267
+
1268
+ // src/build/manifest.ts
1269
+ import { writeFile as writeFile2 } from "fs/promises";
1270
+ import { join as join4 } from "path";
1271
+ var MANIFEST_FILE = "manifest.json";
1272
+ var MANIFEST_ENTRY = "template.js";
1273
+ var MAX_MANIFEST_BYTES = 16 * 1024;
1274
+ var PORTAL_ELEMENT_NAME_RE = /^[a-z][a-z0-9]*(-[a-z0-9]+)+$/;
1275
+ var MAX_ELEMENT_NAME_LEN = 100;
1276
+ var SEMVER_RE = /^(0|[1-9]\d{0,8})\.(0|[1-9]\d{0,8})\.(0|[1-9]\d{0,8})(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/;
1277
+ var MAX_SEMVER_LEN = 64;
1278
+ var FRAMEWORK_RE = /^[a-z][a-z0-9-]{0,31}$/;
1279
+ function parseSemver(v) {
1280
+ if (v.length === 0 || v.length > MAX_SEMVER_LEN) return null;
1281
+ const m = SEMVER_RE.exec(v);
1282
+ if (!m) return null;
1283
+ const pre = m[4] ? m[4].split(".") : [];
1284
+ if (pre.some((id) => /^\d+$/.test(id) && (id.length > 1 && id.startsWith("0") || id.length > 9)))
1285
+ return null;
1286
+ return { core: [Number(m[1]), Number(m[2]), Number(m[3])], pre };
1287
+ }
1288
+ function isValidSemver(v) {
1289
+ return parseSemver(v) !== null;
1290
+ }
1291
+ function comparePre(a, b) {
1292
+ if (a.length === 0 || b.length === 0) return Math.sign(b.length - a.length);
1293
+ for (let i = 0; i < Math.min(a.length, b.length); i++) {
1294
+ const [x, y] = [a[i], b[i]];
1295
+ if (x === y) continue;
1296
+ const [xn, yn] = [/^\d+$/.test(x), /^\d+$/.test(y)];
1297
+ if (xn && yn) return Math.sign(Number(x) - Number(y));
1298
+ if (xn !== yn) return xn ? -1 : 1;
1299
+ return x < y ? -1 : 1;
1300
+ }
1301
+ return Math.sign(a.length - b.length);
1302
+ }
1303
+ function compareSemver(a, b) {
1304
+ const [pa, pb] = [parseSemver(a), parseSemver(b)];
1305
+ if (!pa || !pb) throw new Error(`invalid semver: ${pa ? b : a}`);
1306
+ for (let i = 0; i < 3; i++) {
1307
+ if (pa.core[i] !== pb.core[i]) return Math.sign(pa.core[i] - pb.core[i]);
1308
+ }
1309
+ return comparePre(pa.pre, pb.pre);
1310
+ }
1311
+ function pickSdkVersion(versions) {
1312
+ const all = [...new Set(versions)];
1313
+ if (all.length === 0 || !all.every(isValidSemver)) return void 0;
1314
+ return [...all].sort((a, b) => compareSemver(a, b) || (a < b ? -1 : a > b ? 1 : 0))[0];
1315
+ }
1316
+ function isValidBridgeProtocol(v) {
1317
+ return typeof v === "number" && Number.isSafeInteger(v) && v >= 1;
1318
+ }
1319
+ function pickBridgeProtocol(values) {
1320
+ const all = [...values];
1321
+ if (all.length === 0 || !all.every(isValidBridgeProtocol)) return void 0;
1322
+ return Math.min(...all);
1323
+ }
1324
+ function createManifest(input) {
1325
+ const { elementName, sdkVersion } = input;
1326
+ if (elementName.length > MAX_ELEMENT_NAME_LEN || !PORTAL_ELEMENT_NAME_RE.test(elementName)) {
1327
+ return {
1328
+ ok: false,
1329
+ reason: `name ${JSON.stringify(elementName)}\uC774 \uD3EC\uD138 Custom Element \uADDC\uCE59(${PORTAL_ELEMENT_NAME_RE.source}, ${MAX_ELEMENT_NAME_LEN}\uC790 \uC774\uD558)\uC5D0 \uB9DE\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4`
1330
+ };
1331
+ }
1332
+ if (sdkVersion === void 0 || !isValidSemver(sdkVersion)) {
1333
+ return {
1334
+ ok: false,
1335
+ reason: "\uBC88\uB4E4\uC5D0\uC11C @bstage-sdk/core \uBC84\uC804\uC744 \uCC3E\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4"
1336
+ };
1337
+ }
1338
+ const explicit = input.framework?.toLowerCase();
1339
+ const framework = explicit && FRAMEWORK_RE.test(explicit) ? explicit : input.bundlesReact ? "react" : "vanilla";
1340
+ const manifest = {
1341
+ elementName,
1342
+ entry: MANIFEST_ENTRY,
1343
+ sdkVersion,
1344
+ framework,
1345
+ ...isValidBridgeProtocol(input.bridgeProtocol) ? { bridgeProtocol: input.bridgeProtocol } : {}
1346
+ };
1347
+ const json = JSON.stringify(manifest, null, 2) + "\n";
1348
+ if (Buffer.byteLength(json, "utf-8") > MAX_MANIFEST_BYTES) {
1349
+ return { ok: false, reason: `manifest\uAC00 \uC0C1\uD55C(${MAX_MANIFEST_BYTES}\uBC14\uC774\uD2B8)\uC744 \uB118\uC2B5\uB2C8\uB2E4` };
1350
+ }
1351
+ return { ok: true, manifest, json };
1352
+ }
1353
+ async function emitManifest(dir, input) {
1354
+ const result = createManifest(input);
1355
+ if (result.ok) await writeFile2(join4(dir, MANIFEST_FILE), result.json, "utf-8");
1356
+ return result;
1357
+ }
1358
+
1359
+ // src/vite/bundledPackagesPlugin.ts
1360
+ function readBridgeProtocol(pkg) {
1361
+ const bstage = pkg.bstage;
1362
+ if (typeof bstage !== "object" || bstage === null) return null;
1363
+ const value = bstage.bridgeProtocol;
1364
+ return isValidBridgeProtocol(value) ? value : null;
1365
+ }
1366
+ var UNKNOWN_VERSION = "unknown";
1367
+ var CORE_PACKAGE = "@bstage-sdk/core";
1368
+ var REACT_PACKAGE = "@bstage-sdk/react";
1369
+ var TRACKED_SCOPE = "@bstage-sdk/";
1370
+ var SDK_PATH_RE = new RegExp(
1371
+ `${TRACKED_SCOPE.slice(0, -1).replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}[/\\\\+]([a-z0-9][a-z0-9-]*)`,
1372
+ "g"
1373
+ );
1374
+ function guessSdkPackage(id) {
1375
+ const matches = [...id.matchAll(SDK_PATH_RE)];
1376
+ const last = matches[matches.length - 1];
1377
+ return last ? { name: `${TRACKED_SCOPE}${last[1]}`, version: UNKNOWN_VERSION, bridgeProtocol: null } : null;
1378
+ }
1379
+ function moduleFilePath(id) {
1380
+ if (id.startsWith("\0")) return null;
1381
+ const path = id.split("?")[0];
1382
+ return isAbsolute2(path) ? path : null;
1383
+ }
1384
+ function makePackageLookup() {
1385
+ const cache = /* @__PURE__ */ new Map();
1386
+ function readPackage(dir) {
1387
+ let raw;
1388
+ try {
1389
+ raw = readFileSync4(join5(dir, "package.json"), "utf-8");
1390
+ } catch {
1391
+ return void 0;
1392
+ }
1393
+ try {
1394
+ const pkg = JSON.parse(raw);
1395
+ if (typeof pkg.name !== "string") return void 0;
1396
+ return {
1397
+ name: pkg.name,
1398
+ version: typeof pkg.version === "string" ? pkg.version : UNKNOWN_VERSION,
1399
+ bridgeProtocol: readBridgeProtocol(pkg)
1400
+ };
1401
+ } catch {
1402
+ return null;
1403
+ }
1404
+ }
1405
+ function lookupDir(dir) {
1406
+ const cached = cache.get(dir);
1407
+ if (cached !== void 0) return cached;
1408
+ const found = readPackage(dir);
1409
+ const parent = dirname5(dir);
1410
+ const result = found !== void 0 ? found : parent === dir ? null : lookupDir(parent);
1411
+ cache.set(dir, result);
1412
+ return result;
1413
+ }
1414
+ return (file3) => lookupDir(dirname5(file3));
1415
+ }
1416
+ function createBundledPackagesPlugin() {
1417
+ let bundled = /* @__PURE__ */ new Map();
1418
+ let bridgeProtocols = /* @__PURE__ */ new Map();
1419
+ const plugin = {
1420
+ name: "bstage-bundled-packages",
1421
+ apply: "build",
1422
+ generateBundle(_options, bundle) {
1423
+ const lookup = makePackageLookup();
1424
+ const found = Object.values(bundle).flatMap((output) => output.type === "chunk" ? Object.entries(output.modules) : []).filter(([, info]) => info.renderedLength > 0).map(([id]) => {
1425
+ const file3 = moduleFilePath(id);
1426
+ return (file3 ? lookup(file3) : null) ?? guessSdkPackage(id);
1427
+ }).filter((pkg) => pkg !== null && pkg.name.startsWith(TRACKED_SCOPE));
1428
+ const names = [...new Set(found.map((p5) => p5.name))];
1429
+ const copiesOf = (name) => found.filter((p5) => p5.name === name);
1430
+ bundled = new Map(names.map((name) => [name, new Set(copiesOf(name).map((p5) => p5.version))]));
1431
+ bridgeProtocols = new Map(
1432
+ names.map((name) => [name, new Set(copiesOf(name).map((p5) => p5.bridgeProtocol))])
1433
+ );
1434
+ }
1435
+ };
1436
+ return { plugin, getBundled: () => bundled, getBridgeProtocols: () => bridgeProtocols };
1437
+ }
1438
+
1264
1439
  // src/build/credentialCheck.ts
1265
1440
  var CLIENT_MARKER = "X-BSTAGE-APP-ID";
1266
1441
  var APP_ID_PREFIX = "bsa_";
@@ -1372,13 +1547,13 @@ function ignoredLiquidNotice(files) {
1372
1547
  }
1373
1548
 
1374
1549
  // src/liquid/validate.ts
1375
- import { readdirSync as readdirSync2, readFileSync as readFileSync4 } from "fs";
1376
- import { join as join5 } from "path";
1550
+ import { readdirSync as readdirSync2, readFileSync as readFileSync5 } from "fs";
1551
+ import { join as join7 } from "path";
1377
1552
  import { LiquidError as LiquidError2 } from "liquidjs";
1378
1553
 
1379
1554
  // src/liquid/render.ts
1380
1555
  import { readFile } from "fs/promises";
1381
- import { join as join4 } from "path";
1556
+ import { join as join6 } from "path";
1382
1557
  import { Liquid, LiquidError } from "liquidjs";
1383
1558
  var LiquidPreviewError = class extends Error {
1384
1559
  constructor(message, file3, line) {
@@ -1390,7 +1565,7 @@ var LiquidPreviewError = class extends Error {
1390
1565
  };
1391
1566
  function createEngine(root) {
1392
1567
  const engine = new Liquid({
1393
- root: [join4(root, "public")],
1568
+ root: [join6(root, "public")],
1394
1569
  extname: ".liquid",
1395
1570
  strictVariables: false
1396
1571
  });
@@ -1427,15 +1602,15 @@ async function readData(dataPath) {
1427
1602
  }
1428
1603
  }
1429
1604
  async function renderTemplate(engine, root, type, name) {
1430
- const dir = join4(root, "public", type, name);
1431
- const templatePath = join4(dir, "template.liquid");
1605
+ const dir = join6(root, "public", type, name);
1606
+ const templatePath = join6(dir, "template.liquid");
1432
1607
  let source;
1433
1608
  try {
1434
1609
  source = await readFile(templatePath, "utf-8");
1435
1610
  } catch {
1436
1611
  throw new LiquidPreviewError("template.liquid\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4", templatePath);
1437
1612
  }
1438
- const data = await readData(join4(dir, "data.json"));
1613
+ const data = await readData(join6(dir, "data.json"));
1439
1614
  try {
1440
1615
  const html = await engine.parseAndRender(source, data);
1441
1616
  return { html };
@@ -1474,7 +1649,7 @@ function readEntries(dir) {
1474
1649
  }
1475
1650
  function readText(path) {
1476
1651
  try {
1477
- return readFileSync4(path, "utf-8");
1652
+ return readFileSync5(path, "utf-8");
1478
1653
  } catch {
1479
1654
  return null;
1480
1655
  }
@@ -1483,7 +1658,7 @@ function validateTemplate(engine, root, id) {
1483
1658
  const issues = [];
1484
1659
  const dir = `public/${id}`;
1485
1660
  try {
1486
- const source = readText(join5(root, dir, "template.liquid"));
1661
+ const source = readText(join7(root, dir, "template.liquid"));
1487
1662
  if (source !== null) {
1488
1663
  engine.parse(source);
1489
1664
  if (PARTIAL_TAG_RE.test(source)) {
@@ -1511,7 +1686,7 @@ function validateTemplate(engine, root, id) {
1511
1686
  message: `\uD3F4\uB354 \uC774\uB984 "${name}"\uC740 \uADDC\uCE59\uC5D0 \uB9DE\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4 \u2014 \uC18C\uBB38\uC790\uB85C \uC2DC\uC791\uD558\uACE0 \uC18C\uBB38\uC790\xB7\uC22B\uC790\xB7\uD558\uC774\uD508\uB9CC \uC4F0\uC138\uC694(CDN \uACBD\uB85C\uAC00 \uB429\uB2C8\uB2E4).`
1512
1687
  });
1513
1688
  }
1514
- const raw = readText(join5(root, dir, "data.json"));
1689
+ const raw = readText(join7(root, dir, "data.json"));
1515
1690
  if (raw !== null) {
1516
1691
  try {
1517
1692
  JSON.parse(raw);
@@ -1523,7 +1698,7 @@ function validateTemplate(engine, root, id) {
1523
1698
  });
1524
1699
  }
1525
1700
  }
1526
- if (readText(join5(root, dir, "layout.json")) !== null) {
1701
+ if (readText(join7(root, dir, "layout.json")) !== null) {
1527
1702
  issues.push({
1528
1703
  level: "warn",
1529
1704
  path: `${dir}/layout.json`,
@@ -1535,10 +1710,10 @@ function validateTemplate(engine, root, id) {
1535
1710
  function findMisnamedTemplates(root) {
1536
1711
  const issues = [];
1537
1712
  for (const surface of SURFACES) {
1538
- const surfaceDir = join5(root, "public", surface);
1713
+ const surfaceDir = join7(root, "public", surface);
1539
1714
  for (const entry of readEntries(surfaceDir)) {
1540
1715
  if (!entry.isDirectory || entry.name.startsWith(".")) continue;
1541
- const files = readEntries(join5(surfaceDir, entry.name)).filter((e) => e.isFile);
1716
+ const files = readEntries(join7(surfaceDir, entry.name)).filter((e) => e.isFile);
1542
1717
  if (files.length === 0) continue;
1543
1718
  if (files.some((f) => f.name === "template.liquid")) continue;
1544
1719
  if (files.every((f) => STRIPPED_FILES.includes(f.name))) continue;
@@ -1705,16 +1880,22 @@ async function buildEntry(ctx, entryInfo) {
1705
1880
  console.log(`
1706
1881
  Building template: ${label}`);
1707
1882
  const { plugin: metaPlugin, getMeta } = createMetaPlugin();
1708
- const outDir = join6(DIST_DIR, STAGING_DIR);
1883
+ const { plugin: bundledPlugin, getBundled, getBridgeProtocols } = createBundledPackagesPlugin();
1884
+ const outDir = join8(DIST_DIR, STAGING_DIR);
1709
1885
  const viteConfig2 = createViteConfig({ entry, outDir });
1710
1886
  await viteBuild({
1711
1887
  ...viteConfig2,
1712
1888
  configFile: false,
1713
1889
  root: cwd,
1714
1890
  logLevel: "info",
1715
- plugins: [...viteConfig2.plugins ?? [], metaPlugin, createRegisterPlugin()]
1891
+ plugins: [
1892
+ ...viteConfig2.plugins ?? [],
1893
+ metaPlugin,
1894
+ createRegisterPlugin(),
1895
+ bundledPlugin
1896
+ ]
1716
1897
  });
1717
- const bundlePath = join6(cwd, outDir, "template.js");
1898
+ const bundlePath = join8(cwd, outDir, "template.js");
1718
1899
  let bundleSize = 0;
1719
1900
  let usesClient = false;
1720
1901
  try {
@@ -1751,7 +1932,7 @@ ${pc7.red("Error")} ${e.message}`);
1751
1932
  if (previousOwner !== void 0) {
1752
1933
  console.error(
1753
1934
  `
1754
- ${pc7.red("Error")} ${label}: \uC0B0\uCD9C\uBB3C \uC704\uCE58 "${join6(DIST_DIR, outputName)}"\uAC00 ${previousOwner}\uC640 \uACB9\uCE69\uB2C8\uB2E4.
1935
+ ${pc7.red("Error")} ${label}: \uC0B0\uCD9C\uBB3C \uC704\uCE58 "${join8(DIST_DIR, outputName)}"\uAC00 ${previousOwner}\uC640 \uACB9\uCE69\uB2C8\uB2E4.
1755
1936
  \uB450 \uD15C\uD50C\uB9BF\uC774 \uAC19\uC740 \uC790\uB9AC\uB85C \uB098\uAC08 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.`
1756
1937
  );
1757
1938
  process.exit(1);
@@ -1767,11 +1948,36 @@ ${pc7.red("Error")} ${label}: name "${elementName}"\uC744 ${elementNameOwner}\uA
1767
1948
  process.exit(1);
1768
1949
  }
1769
1950
  ctx.takenElementNames.set(elementName, label);
1770
- const finalDir = outputName ? join6(cwd, DIST_DIR, outputName) : join6(cwd, DIST_DIR);
1951
+ const finalDir = outputName ? join8(cwd, DIST_DIR, outputName) : join8(cwd, DIST_DIR);
1771
1952
  await mkdir(finalDir, { recursive: true });
1772
- await rename(join6(cwd, outDir, "template.js"), join6(finalDir, "template.js"));
1953
+ await rename(join8(cwd, outDir, "template.js"), join8(finalDir, "template.js"));
1773
1954
  const output = relative2(cwd, finalDir).split(sep3).join("/");
1774
1955
  console.log(` ${output}/template.js (${formatSize(bundleSize)})`);
1956
+ const bundled = getBundled();
1957
+ const manifest = await emitManifest(finalDir, {
1958
+ elementName,
1959
+ sdkVersion: pickSdkVersion(bundled.get(CORE_PACKAGE) ?? []),
1960
+ bridgeProtocol: pickBridgeProtocol(getBridgeProtocols().get(CORE_PACKAGE) ?? []),
1961
+ framework: bundleMeta?.framework,
1962
+ bundlesReact: bundled.has(REACT_PACKAGE)
1963
+ });
1964
+ if (manifest.ok) {
1965
+ const { sdkVersion, bridgeProtocol } = manifest.manifest;
1966
+ console.log(
1967
+ ` ${output}/${MANIFEST_FILE} (sdkVersion ${sdkVersion}, bridgeProtocol ${bridgeProtocol ?? "\uC5C6\uC74C"})`
1968
+ );
1969
+ if (bridgeProtocol === void 0) {
1970
+ console.warn(
1971
+ ` ${pc7.yellow("\u26A0")} \uBC88\uB4E4\uC5D0 \uC2E4\uB9B0 bstage-core \uC911 bridgeProtocol\uC744 \uC54C \uC218 \uC5C6\uB294 \uC0AC\uBCF8\uC774 \uC788\uC2B5\uB2C8\uB2E4(\uD544\uB4DC \uC5C6\uB294 \uC61B core\xB7\uADC0\uC18D \uC2E4\uD328).
1972
+ \uC774 \uD15C\uD50C\uB9BF\uC740 \uACA9\uB9AC(iframe) \uBC30\uCE58\uB97C \uC4F8 \uC218 \uC5C6\uACE0 inline\uC73C\uB85C\uB9CC \uB3D9\uC791\uD569\uB2C8\uB2E4. SDK\uB97C \uC62C\uB9B0 \uB4A4 \uB2E4\uC2DC \uBE4C\uB4DC\uD558\uC138\uC694.`
1973
+ );
1974
+ }
1975
+ } else {
1976
+ console.warn(
1977
+ ` ${pc7.yellow("\u26A0")} ${output}/${MANIFEST_FILE}\uC744 \uB9CC\uB4E4\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4 \u2014 ${manifest.reason}.
1978
+ \uC774 \uD15C\uD50C\uB9BF\uC740 \uACA9\uB9AC(iframe) \uBC30\uCE58\uB97C \uC4F8 \uC218 \uC5C6\uACE0 inline\uC73C\uB85C\uB9CC \uB3D9\uC791\uD569\uB2C8\uB2E4.`
1979
+ );
1980
+ }
1775
1981
  return { usesClient, output };
1776
1982
  }
1777
1983
  function printCredentialReport(env2, referenced) {
@@ -1814,7 +2020,7 @@ ${pc7.red("\u2717")} \uAC80\uC99D \uC2E4\uD328 \u2014 \uC704 \uD56D\uBAA9\uC744
1814
2020
  );
1815
2021
  }
1816
2022
  async function printLegacyHint(cwd) {
1817
- if (!await exists(join6(cwd, LEGACY_ROOT))) return;
2023
+ if (!await exists(join8(cwd, LEGACY_ROOT))) return;
1818
2024
  console.error(
1819
2025
  `
1820
2026
  ${LEGACY_ROOT}/ \uC774 \uB0A8\uC544 \uC788\uC2B5\uB2C8\uB2E4. \uBE4C\uB4DC \uC0B0\uCD9C\uBB3C \uACBD\uB85C \uADDC\uCE59\uC774 \uBC14\uB00C\uBA74\uC11C
@@ -1839,7 +2045,7 @@ function printLiquidIssues(issues) {
1839
2045
  }
1840
2046
  }
1841
2047
  async function bundleAll(cwd, entries, bundleEntry) {
1842
- await rm(join6(cwd, DIST_DIR), { recursive: true, force: true });
2048
+ await rm(join8(cwd, DIST_DIR), { recursive: true, force: true });
1843
2049
  const ctx = {
1844
2050
  cwd,
1845
2051
  takenOutputs: /* @__PURE__ */ new Map(),
@@ -1852,7 +2058,7 @@ async function bundleAll(cwd, entries, bundleEntry) {
1852
2058
  for (const entry of entries) {
1853
2059
  results.push(await bundleEntry(ctx, entry));
1854
2060
  }
1855
- await rm(join6(cwd, DIST_DIR, STAGING_DIR), { recursive: true, force: true });
2061
+ await rm(join8(cwd, DIST_DIR, STAGING_DIR), { recursive: true, force: true });
1856
2062
  return results;
1857
2063
  }
1858
2064
  async function reportCredentials(cwd, results) {
@@ -2219,8 +2425,8 @@ async function waitForCompletedBuild(ctx, deps, build, json) {
2219
2425
  }
2220
2426
 
2221
2427
  // src/portal/config.ts
2222
- import { mkdir as mkdir2, readFile as readFile3, writeFile as writeFile2 } from "fs/promises";
2223
- import { dirname as dirname5, join as join7 } from "path";
2428
+ import { mkdir as mkdir2, readFile as readFile3, writeFile as writeFile3 } from "fs/promises";
2429
+ import { dirname as dirname6, join as join9 } from "path";
2224
2430
  import { loadEnv as loadEnv2 } from "vite";
2225
2431
  var LOCAL_HOSTS = /* @__PURE__ */ new Set(["localhost", "127.0.0.1", "[::1]"]);
2226
2432
  function normalizePortalUrl(url) {
@@ -2238,11 +2444,11 @@ function normalizePortalUrl(url) {
2238
2444
  return u.origin + path;
2239
2445
  }
2240
2446
  function linkPath(cwd) {
2241
- return join7(cwd, ".bstage", "project.json");
2447
+ return join9(cwd, ".bstage", "project.json");
2242
2448
  }
2243
2449
  async function saveLink(cwd, link) {
2244
- await mkdir2(dirname5(linkPath(cwd)), { recursive: true });
2245
- await writeFile2(linkPath(cwd), JSON.stringify(link, null, 2) + "\n");
2450
+ await mkdir2(dirname6(linkPath(cwd)), { recursive: true });
2451
+ await writeFile3(linkPath(cwd), JSON.stringify(link, null, 2) + "\n");
2246
2452
  }
2247
2453
  function linkFromEnv(env2) {
2248
2454
  const { BSTAGE_PORTAL_URL, BSTAGE_ORG, BSTAGE_SPACE, BSTAGE_REPO, BSTAGE_SURFACE } = env2;
@@ -2337,13 +2543,13 @@ async function resolvePortalUrl(explicit, cwd, env2 = process.env, phaseFlag, op
2337
2543
  }
2338
2544
 
2339
2545
  // src/portal/credentials.ts
2340
- import { chmod, mkdir as mkdir3, readFile as readFile4, rename as rename2, rm as rm2, writeFile as writeFile3 } from "fs/promises";
2546
+ import { chmod, mkdir as mkdir3, readFile as readFile4, rename as rename2, rm as rm2, writeFile as writeFile4 } from "fs/promises";
2341
2547
  import { homedir } from "os";
2342
- import { dirname as dirname6, join as join8 } from "path";
2548
+ import { dirname as dirname7, join as join10 } from "path";
2343
2549
  var UNKNOWN_ORG = "*";
2344
2550
  function credentialsPath(env2 = process.env) {
2345
- const base = env2.XDG_CONFIG_HOME || join8(homedir(), ".config");
2346
- return join8(base, "bstage", "credentials.json");
2551
+ const base = env2.XDG_CONFIG_HOME || join10(homedir(), ".config");
2552
+ return join10(base, "bstage", "credentials.json");
2347
2553
  }
2348
2554
  function hasEntry(bucket, org) {
2349
2555
  return Object.prototype.hasOwnProperty.call(bucket, org);
@@ -2397,10 +2603,10 @@ async function readCredentialFile(env2) {
2397
2603
  }
2398
2604
  async function writeCredentialFile(env2, data) {
2399
2605
  const p5 = credentialsPath(env2);
2400
- await mkdir3(dirname6(p5), { recursive: true, mode: 448 });
2401
- await chmod(dirname6(p5), 448);
2606
+ await mkdir3(dirname7(p5), { recursive: true, mode: 448 });
2607
+ await chmod(dirname7(p5), 448);
2402
2608
  const tmp = `${p5}.${process.pid}.${Date.now()}.tmp`;
2403
- await writeFile3(tmp, JSON.stringify(data, null, 2) + "\n", { mode: 384 });
2609
+ await writeFile4(tmp, JSON.stringify(data, null, 2) + "\n", { mode: 384 });
2404
2610
  await chmod(tmp, 384);
2405
2611
  await rename2(tmp, p5);
2406
2612
  }
@@ -2679,7 +2885,7 @@ import pc12 from "picocolors";
2679
2885
  import { existsSync as existsSync5, realpathSync as realpathSync2, statSync, watch } from "fs";
2680
2886
  import { readFile as readFile5 } from "fs/promises";
2681
2887
  import { createServer } from "http";
2682
- import { join as join9, relative as relative3, sep as sep4 } from "path";
2888
+ import { join as join11, relative as relative3, sep as sep4 } from "path";
2683
2889
 
2684
2890
  // src/liquid/routes.ts
2685
2891
  var RELOAD_PATH = "/__bstage__/reload";
@@ -2939,7 +3145,7 @@ async function sendTemplate(ctx, route2, res) {
2939
3145
  }
2940
3146
  }
2941
3147
  async function sendAsset(ctx, path, res) {
2942
- const full = join9(ctx.publicDir, path);
3148
+ const full = join11(ctx.publicDir, path);
2943
3149
  if (!isInside(ctx.publicDir, full)) return sendHtml(res, 404, notFoundPage());
2944
3150
  try {
2945
3151
  const body = await readFile5(full);
@@ -2965,7 +3171,7 @@ function subscribeReload(ctx, res) {
2965
3171
  async function route(ctx, pathname, res) {
2966
3172
  const inventory = ctx.inventory.get();
2967
3173
  const ids = inventory.templates.map((t) => t.id);
2968
- const match = resolveRoute(pathname, ids, (rel) => isFile(join9(ctx.publicDir, rel)));
3174
+ const match = resolveRoute(pathname, ids, (rel) => isFile(join11(ctx.publicDir, rel)));
2969
3175
  if (match === null) return sendHtml(res, 404, notFoundPage());
2970
3176
  switch (match.kind) {
2971
3177
  case "index":
@@ -3027,7 +3233,7 @@ async function startLiquidDevServer(options) {
3027
3233
  const { root, port, log: log3 } = options;
3028
3234
  const ctx = {
3029
3235
  root,
3030
- publicDir: join9(root, "public"),
3236
+ publicDir: join11(root, "public"),
3031
3237
  engine: createEngine(root),
3032
3238
  clients: /* @__PURE__ */ new Set(),
3033
3239
  inventory: createInventoryCache(root)
@@ -3183,7 +3389,7 @@ import { resolve as resolve3 } from "path";
3183
3389
 
3184
3390
  // src/commands/init/scaffold.ts
3185
3391
  import { chmodSync, mkdirSync as mkdirSync3, writeFileSync as writeFileSync2 } from "fs";
3186
- import { dirname as dirname7, join as join10 } from "path";
3392
+ import { dirname as dirname8, join as join12 } from "path";
3187
3393
  import { agentsMd, claudeMd, skillsFor as skillsFor2 } from "@bstage-sdk/ai-toolkit";
3188
3394
 
3189
3395
  // src/templates/env.ts
@@ -3792,7 +3998,7 @@ function rethrowWithPath(path, err) {
3792
3998
  }
3793
3999
  function writeScaffold(projectDir, scaffold) {
3794
4000
  for (const dir of scaffold.dirs) {
3795
- const full = join10(projectDir, dir);
4001
+ const full = join12(projectDir, dir);
3796
4002
  try {
3797
4003
  mkdirSync3(full, { recursive: true });
3798
4004
  } catch (err) {
@@ -3800,9 +4006,9 @@ function writeScaffold(projectDir, scaffold) {
3800
4006
  }
3801
4007
  }
3802
4008
  for (const file3 of scaffold.files) {
3803
- const full = join10(projectDir, file3.path);
4009
+ const full = join12(projectDir, file3.path);
3804
4010
  try {
3805
- mkdirSync3(dirname7(full), { recursive: true });
4011
+ mkdirSync3(dirname8(full), { recursive: true });
3806
4012
  writeFileSync2(full, file3.content, "utf-8");
3807
4013
  if (EXECUTABLE_PATHS.has(file3.path)) chmodSync(full, 493);
3808
4014
  } catch (err) {
@@ -4352,7 +4558,7 @@ async function fetchLatestVersion(packageName) {
4352
4558
 
4353
4559
  // src/commands/i18n.ts
4354
4560
  import { mkdirSync as mkdirSync4, writeFileSync as writeFileSync3 } from "fs";
4355
- import { dirname as dirname8, join as join11, resolve as resolve4 } from "path";
4561
+ import { dirname as dirname9, join as join13, resolve as resolve4 } from "path";
4356
4562
  import pc15 from "picocolors";
4357
4563
  function latestKey(phase, tier, target) {
4358
4564
  const segments = tier === DEFAULT_TRANSLATION_TIER ? [TRANSLATION_CDN_BASE_KEY, phase, target] : [TRANSLATION_CDN_BASE_KEY, phase, target, tier];
@@ -4412,14 +4618,14 @@ async function i18nPullCommand(options) {
4412
4618
  }
4413
4619
  const versionBase = `${TRANSLATION_CDN_ORIGIN}/${latest.cdnPath.replace(TRANSLATION_CDN_BUCKET_PREFIX, "")}`;
4414
4620
  console.log(pc15.dim(` version: ${latest.version}`));
4415
- const cacheDir = join11(root, ".bstage", "i18n", target);
4621
+ const cacheDir = join13(root, ".bstage", "i18n", target);
4416
4622
  mkdirSync4(cacheDir, { recursive: true });
4417
4623
  let refDict = null;
4418
4624
  const fetched = [];
4419
4625
  for (const locale of TRANSLATION_LANGUAGES) {
4420
4626
  try {
4421
4627
  const dict = await fetchJson(`${versionBase}/${locale}/translation.json`);
4422
- writeFileSync3(join11(cacheDir, `${locale}.json`), JSON.stringify(dict, null, 2));
4628
+ writeFileSync3(join13(cacheDir, `${locale}.json`), JSON.stringify(dict, null, 2));
4423
4629
  fetched.push(locale);
4424
4630
  if (locale === options.ref) refDict = dict;
4425
4631
  console.log(pc15.green(` \u2713 ${locale} (${Object.keys(dict).length})`));
@@ -4439,7 +4645,7 @@ async function i18nPullCommand(options) {
4439
4645
  return;
4440
4646
  }
4441
4647
  const outPath = resolve4(root, options.out);
4442
- mkdirSync4(dirname8(outPath), { recursive: true });
4648
+ mkdirSync4(dirname9(outPath), { recursive: true });
4443
4649
  writeFileSync3(outPath, generateTypeModule(refDict, { version: latest.version, ref: options.ref }));
4444
4650
  console.log(pc15.green(`[bstage] \uD0C0\uC785 \uC0DD\uC131: ${options.out} (${Object.keys(refDict).length} \uD0A4)`));
4445
4651
  console.log(
@@ -4571,12 +4777,12 @@ async function mapLimit(items, limit, fn) {
4571
4777
 
4572
4778
  // src/commands/doctor/report.ts
4573
4779
  import { readFile as readFile9 } from "fs/promises";
4574
- import { join as join15 } from "path";
4780
+ import { join as join17 } from "path";
4575
4781
 
4576
4782
  // src/commands/doctor/checks.ts
4577
4783
  import { existsSync as existsSync7 } from "fs";
4578
4784
  import { readFile as readFile6, readdir as readdir2 } from "fs/promises";
4579
- import { join as join12 } from "path";
4785
+ import { join as join14 } from "path";
4580
4786
  var RECOMMENDED_DEPS = [
4581
4787
  {
4582
4788
  pkg: "@bstage-sdk/design",
@@ -4638,7 +4844,7 @@ function diagnoseFiles(root, expected) {
4638
4844
  return expected.map((f) => ({
4639
4845
  path: f.path,
4640
4846
  note: f.note,
4641
- present: (f.anyOf ?? [f.path]).some((candidate) => existsSync7(join12(root, candidate)))
4847
+ present: (f.anyOf ?? [f.path]).some((candidate) => existsSync7(join14(root, candidate)))
4642
4848
  }));
4643
4849
  }
4644
4850
  function diagnoseScripts(scripts, expected) {
@@ -4652,7 +4858,7 @@ function diagnoseRecommendedDeps(deps, recommended) {
4652
4858
  return recommended.map((d) => ({ ...d, declared: d.pkg in deps }));
4653
4859
  }
4654
4860
  async function diagnosePnpmWorkspace(root) {
4655
- if (!existsSync7(join12(root, "pnpm-lock.yaml"))) {
4861
+ if (!existsSync7(join14(root, "pnpm-lock.yaml"))) {
4656
4862
  return {
4657
4863
  relevant: false,
4658
4864
  allowBuildsDeclared: false,
@@ -4662,7 +4868,7 @@ async function diagnosePnpmWorkspace(root) {
4662
4868
  }
4663
4869
  let yaml = null;
4664
4870
  try {
4665
- yaml = await readFile6(join12(root, "pnpm-workspace.yaml"), "utf-8");
4871
+ yaml = await readFile6(join14(root, "pnpm-workspace.yaml"), "utf-8");
4666
4872
  } catch {
4667
4873
  }
4668
4874
  return {
@@ -4673,7 +4879,7 @@ async function diagnosePnpmWorkspace(root) {
4673
4879
  };
4674
4880
  }
4675
4881
  async function diagnoseLayout(root) {
4676
- const legacyRoot = join12(root, "src", "templates");
4882
+ const legacyRoot = join14(root, "src", "templates");
4677
4883
  let legacyTemplates = [];
4678
4884
  let legacyPresent = false;
4679
4885
  try {
@@ -4685,14 +4891,14 @@ async function diagnoseLayout(root) {
4685
4891
  return {
4686
4892
  legacyPresent,
4687
4893
  legacyTemplates,
4688
- pagesPresent: existsSync7(join12(root, "src", "pages")),
4689
- slotsPresent: existsSync7(join12(root, "src", "slots"))
4894
+ pagesPresent: existsSync7(join14(root, "src", "pages")),
4895
+ slotsPresent: existsSync7(join14(root, "src", "slots"))
4690
4896
  };
4691
4897
  }
4692
4898
 
4693
4899
  // src/commands/doctor/migrations.ts
4694
4900
  import { readFile as readFile7 } from "fs/promises";
4695
- import { dirname as dirname9, join as join13 } from "path";
4901
+ import { dirname as dirname10, join as join15 } from "path";
4696
4902
  import { fileURLToPath } from "url";
4697
4903
  async function readApplicableMigrations(resolve5) {
4698
4904
  const md = await readBundledMigrationDoc();
@@ -4729,7 +4935,7 @@ async function selectApplicableMigrations(entries, resolve5) {
4729
4935
  for (const req of e.requirements) {
4730
4936
  const { version, relevant } = await resolve5(req.pkg);
4731
4937
  if (!relevant) continue;
4732
- if (version === null || compareSemver(version, req.version) < 0) {
4938
+ if (version === null || compareSemver2(version, req.version) < 0) {
4733
4939
  applies = true;
4734
4940
  break;
4735
4941
  }
@@ -4752,14 +4958,14 @@ function normalizeMigrationPkg(short) {
4752
4958
  return short.startsWith("@") ? short : `@bstage-sdk/${short}`;
4753
4959
  }
4754
4960
  async function readBundledMigrationDoc() {
4755
- const here = dirname9(fileURLToPath(import.meta.url));
4961
+ const here = dirname10(fileURLToPath(import.meta.url));
4756
4962
  const candidates = [
4757
- join13(here, "MIGRATION.md"),
4963
+ join15(here, "MIGRATION.md"),
4758
4964
  // 배포물: dist/ 옆 (cli/dist/MIGRATION.md)
4759
- join13(here, "..", "..", "..", "docs", "MIGRATION.md"),
4965
+ join15(here, "..", "..", "..", "docs", "MIGRATION.md"),
4760
4966
  // dev(dist): cli/dist → repo/docs
4761
4967
  // dev(src): cli/src/commands/doctor → repo/docs (모듈 분리로 한 단계 깊어졌다)
4762
- join13(here, "..", "..", "..", "..", "..", "docs", "MIGRATION.md")
4968
+ join15(here, "..", "..", "..", "..", "..", "docs", "MIGRATION.md")
4763
4969
  ];
4764
4970
  for (const path of candidates) {
4765
4971
  try {
@@ -4774,7 +4980,7 @@ function cleanRange(spec) {
4774
4980
  const m = /(\d+\.\d+\.\d+)/.exec(spec);
4775
4981
  return m ? m[1] : null;
4776
4982
  }
4777
- function compareSemver(a, b) {
4983
+ function compareSemver2(a, b) {
4778
4984
  const pa = a.split(".").map((n) => parseInt(n, 10));
4779
4985
  const pb = b.split(".").map((n) => parseInt(n, 10));
4780
4986
  for (let i = 0; i < 3; i++) {
@@ -4786,7 +4992,7 @@ function compareSemver(a, b) {
4786
4992
 
4787
4993
  // src/commands/doctor/skills.ts
4788
4994
  import { readFile as readFile8 } from "fs/promises";
4789
- import { join as join14 } from "path";
4995
+ import { join as join16 } from "path";
4790
4996
  import {
4791
4997
  VERSION as TOOLKIT_VERSION,
4792
4998
  AGENTS_MANAGED_VERSION,
@@ -4811,7 +5017,7 @@ async function diagnoseSkills(root, kind) {
4811
5017
  skillsFor4(kind).map(async (skill) => {
4812
5018
  let installed;
4813
5019
  try {
4814
- installed = await readFile8(join14(root, ".claude", "skills", skill.name, "SKILL.md"), "utf-8");
5020
+ installed = await readFile8(join16(root, ".claude", "skills", skill.name, "SKILL.md"), "utf-8");
4815
5021
  } catch {
4816
5022
  return { name: skill.name, status: "missing" };
4817
5023
  }
@@ -4829,7 +5035,7 @@ function resolveSkillKind(detected) {
4829
5035
  async function diagnoseAgentsMd(root, pkgName, kind = "sdk") {
4830
5036
  let content;
4831
5037
  try {
4832
- content = await readFile8(join14(root, "AGENTS.md"), "utf-8");
5038
+ content = await readFile8(join16(root, "AGENTS.md"), "utf-8");
4833
5039
  } catch {
4834
5040
  return { status: "missing", managedBlock: null };
4835
5041
  }
@@ -4859,7 +5065,7 @@ var CLI_PACKAGE = "@bstage-sdk/cli";
4859
5065
  async function readProjectPackage(root) {
4860
5066
  let pkgJson = null;
4861
5067
  try {
4862
- pkgJson = JSON.parse(await readFile9(join15(root, "package.json"), "utf-8"));
5068
+ pkgJson = JSON.parse(await readFile9(join17(root, "package.json"), "utf-8"));
4863
5069
  } catch {
4864
5070
  }
4865
5071
  return {
@@ -4899,7 +5105,7 @@ async function diagnoseVersions(deps, installed, isBstageProject, isLiquid) {
4899
5105
  pkg,
4900
5106
  installed: inst,
4901
5107
  latest: lat,
4902
- outdated: !!inst && !!lat && compareSemver(inst, lat) < 0
5108
+ outdated: !!inst && !!lat && compareSemver2(inst, lat) < 0
4903
5109
  };
4904
5110
  });
4905
5111
  return { versions, latestChecked };
@@ -4951,7 +5157,7 @@ async function diagnose2(root) {
4951
5157
  }
4952
5158
  async function readInstalledVersion(root, pkg) {
4953
5159
  try {
4954
- const raw = await readFile9(join15(root, "node_modules", pkg, "package.json"), "utf-8");
5160
+ const raw = await readFile9(join17(root, "node_modules", pkg, "package.json"), "utf-8");
4955
5161
  const parsed = JSON.parse(raw);
4956
5162
  return parsed.version ?? null;
4957
5163
  } catch {
@@ -6329,7 +6535,7 @@ async function skillsInstallCommand(options) {
6329
6535
 
6330
6536
  // src/commands/docs.ts
6331
6537
  import { existsSync as existsSync8, readdirSync as readdirSync3 } from "fs";
6332
- import { join as join16 } from "path";
6538
+ import { join as join18 } from "path";
6333
6539
  import pc27 from "picocolors";
6334
6540
  var DOC_ORDER = [
6335
6541
  ["GETTING_STARTED.md", "\uBE60\uB978 \uC2DC\uC791 \uAC00\uC774\uB4DC"],
@@ -6348,7 +6554,7 @@ var DOC_ORDER = [
6348
6554
  ];
6349
6555
  var CORE_DOCS_SEGMENTS = ["node_modules", "@bstage-sdk", "core", "docs"];
6350
6556
  async function docsCommand() {
6351
- const docsDir = join16(process.cwd(), ...CORE_DOCS_SEGMENTS);
6557
+ const docsDir = join18(process.cwd(), ...CORE_DOCS_SEGMENTS);
6352
6558
  if (!existsSync8(docsDir)) {
6353
6559
  console.log(pc27.yellow("SDK \uBB38\uC11C\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4."));
6354
6560
  console.log(pc27.dim(" @bstage-sdk/core\uAC00 \uC124\uCE58\uB418\uC5B4 \uC788\uB294\uC9C0 \uD655\uC778\uD558\uC138\uC694 (npm install \uD6C4 \uC7AC\uC2DC\uB3C4)."));
@@ -6476,8 +6682,8 @@ function reportStoredOnly(portalUrl, stored, json) {
6476
6682
  // src/index.ts
6477
6683
  function readVersion() {
6478
6684
  try {
6479
- const here = dirname10(fileURLToPath2(import.meta.url));
6480
- return JSON.parse(readFileSync5(join17(here, "..", "package.json"), "utf-8")).version || "0.0.0";
6685
+ const here = dirname11(fileURLToPath2(import.meta.url));
6686
+ return JSON.parse(readFileSync6(join19(here, "..", "package.json"), "utf-8")).version || "0.0.0";
6481
6687
  } catch {
6482
6688
  return "0.0.0";
6483
6689
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bstage-sdk/cli",
3
- "version": "0.8.7",
3
+ "version": "0.9.0",
4
4
  "description": "bstage SDK CLI — build b.stage templates",
5
5
  "type": "module",
6
6
  "publishConfig": {
@@ -32,14 +32,14 @@
32
32
  "liquidjs": "^10.24.0",
33
33
  "picocolors": "^1.1.1",
34
34
  "vite": "^7.0.0",
35
- "@bstage-sdk/ai-toolkit": "0.8.7",
36
- "@bstage-sdk/core": "0.8.7"
35
+ "@bstage-sdk/ai-toolkit": "0.9.0",
36
+ "@bstage-sdk/core": "0.9.0"
37
37
  },
38
38
  "devDependencies": {
39
39
  "@types/node": "^25.5.0",
40
40
  "tsup": "^8.0.0",
41
41
  "typescript": "^5.5.0",
42
- "@bstage-sdk/design": "0.8.7"
42
+ "@bstage-sdk/design": "0.9.0"
43
43
  },
44
44
  "license": "MIT",
45
45
  "scripts": {