sayren 0.5.0 → 0.6.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.mjs +143 -5
  2. package/package.json +2 -2
package/dist/index.mjs CHANGED
@@ -2,7 +2,7 @@
2
2
  import { spawn, spawnSync } from "node:child_process";
3
3
  import { stdin, stdout } from "node:process";
4
4
  import { createInterface } from "node:readline/promises";
5
- import { APP_DEPLOY_KEY_PATTERN, APP_SECRET_PATTERNS, APP_SERVER_BUNDLE_MAX_GZIP_BYTES, APP_UI_MAX_BYTES, APP_UI_MAX_FILES, ApiError, ApprovalPendingError, STOREFRONT_BUILD_TERMINAL_STATUSES, STOREFRONT_SOURCE_BLOB_BATCH_MAX, appManifestProblems, appManifestSchema, createStoreClient, normalizeStorefrontPath } from "@sayren/store-sdk";
5
+ import { APP_DEPLOY_KEY_PATTERN, APP_README_MAX_BYTES, APP_SECRET_PATTERNS, APP_SERVER_BUNDLE_MAX_GZIP_BYTES, APP_UI_MAX_BYTES, APP_UI_MAX_FILES, ApiError, ApprovalPendingError, STOREFRONT_BUILD_TERMINAL_STATUSES, STOREFRONT_SOURCE_BLOB_BATCH_MAX, appIconProblem, appManifestProblems, appManifestSchema, createStoreClient, normalizeStorefrontPath } from "@sayren/store-sdk";
6
6
  import { chmodSync, closeSync, existsSync, lstatSync, mkdirSync, openSync, readFileSync, readdirSync, rmSync, statSync, writeFileSync } from "node:fs";
7
7
  import { createServer } from "node:http";
8
8
  import { dirname, extname, join, relative, resolve, sep } from "node:path";
@@ -1067,6 +1067,42 @@ const cli_app_handler_unlisted = ((inputs, options = {}) => {
1067
1067
  return ko_cli_app_handler_unlisted(inputs);
1068
1068
  });
1069
1069
 
1070
+ //#endregion
1071
+ //#region ../i18n/src/paraglide/messages/cli_app_icon_invalid.js
1072
+ /** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
1073
+ /** @typedef {{ path: NonNullable<unknown>, problem: NonNullable<unknown> }} Cli_App_Icon_InvalidInputs */
1074
+ const ko_cli_app_icon_invalid = (i) => {
1075
+ return `아이콘을 쓸 수 없습니다(${i?.path}): ${i?.problem}`;
1076
+ };
1077
+ /**
1078
+
1079
+ * @param {Cli_App_Icon_InvalidInputs} inputs
1080
+ * @param {{ locale?: "ko" }} options
1081
+ * @returns {LocalizedString}
1082
+ */
1083
+ const cli_app_icon_invalid = ((inputs, options = {}) => {
1084
+ experimentalStaticLocale ?? options.locale ?? getLocale();
1085
+ return ko_cli_app_icon_invalid(inputs);
1086
+ });
1087
+
1088
+ //#endregion
1089
+ //#region ../i18n/src/paraglide/messages/cli_app_icon_missing.js
1090
+ /** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
1091
+ /** @typedef {{ path: NonNullable<unknown> }} Cli_App_Icon_MissingInputs */
1092
+ const ko_cli_app_icon_missing = (i) => {
1093
+ return `매니페스트 icon의 파일이 없습니다: ${i?.path}`;
1094
+ };
1095
+ /**
1096
+
1097
+ * @param {Cli_App_Icon_MissingInputs} inputs
1098
+ * @param {{ locale?: "ko" }} options
1099
+ * @returns {LocalizedString}
1100
+ */
1101
+ const cli_app_icon_missing = ((inputs, options = {}) => {
1102
+ experimentalStaticLocale ?? options.locale ?? getLocale();
1103
+ return ko_cli_app_icon_missing(inputs);
1104
+ });
1105
+
1070
1106
  //#endregion
1071
1107
  //#region ../i18n/src/paraglide/messages/cli_app_init_done.js
1072
1108
  /** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
@@ -1108,6 +1144,24 @@ const cli_app_key_invalid = ((inputs = {}, options = {}) => {
1108
1144
  return ko_cli_app_key_invalid(inputs);
1109
1145
  });
1110
1146
 
1147
+ //#endregion
1148
+ //#region ../i18n/src/paraglide/messages/cli_app_listing_none.js
1149
+ /** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
1150
+ /** @typedef {{}} Cli_App_Listing_NoneInputs */
1151
+ const ko_cli_app_listing_none = () => {
1152
+ return `없음`;
1153
+ };
1154
+ /**
1155
+
1156
+ * @param {Cli_App_Listing_NoneInputs} inputs
1157
+ * @param {{ locale?: "ko" }} options
1158
+ * @returns {LocalizedString}
1159
+ */
1160
+ const cli_app_listing_none = ((inputs = {}, options = {}) => {
1161
+ experimentalStaticLocale ?? options.locale ?? getLocale();
1162
+ return ko_cli_app_listing_none(inputs);
1163
+ });
1164
+
1111
1165
  //#endregion
1112
1166
  //#region ../i18n/src/paraglide/messages/cli_app_logs_empty.js
1113
1167
  /** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
@@ -1199,6 +1253,42 @@ const cli_app_port_invalid = ((inputs, options = {}) => {
1199
1253
  return ko_cli_app_port_invalid(inputs);
1200
1254
  });
1201
1255
 
1256
+ //#endregion
1257
+ //#region ../i18n/src/paraglide/messages/cli_app_readme_missing.js
1258
+ /** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
1259
+ /** @typedef {{ path: NonNullable<unknown> }} Cli_App_Readme_MissingInputs */
1260
+ const ko_cli_app_readme_missing = (i) => {
1261
+ return `매니페스트 readme의 파일이 없습니다: ${i?.path}`;
1262
+ };
1263
+ /**
1264
+
1265
+ * @param {Cli_App_Readme_MissingInputs} inputs
1266
+ * @param {{ locale?: "ko" }} options
1267
+ * @returns {LocalizedString}
1268
+ */
1269
+ const cli_app_readme_missing = ((inputs, options = {}) => {
1270
+ experimentalStaticLocale ?? options.locale ?? getLocale();
1271
+ return ko_cli_app_readme_missing(inputs);
1272
+ });
1273
+
1274
+ //#endregion
1275
+ //#region ../i18n/src/paraglide/messages/cli_app_readme_too_large.js
1276
+ /** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
1277
+ /** @typedef {{ limit: NonNullable<unknown>, path: NonNullable<unknown> }} Cli_App_Readme_Too_LargeInputs */
1278
+ const ko_cli_app_readme_too_large = (i) => {
1279
+ return `README가 ${i?.limit}KB를 넘습니다: ${i?.path}`;
1280
+ };
1281
+ /**
1282
+
1283
+ * @param {Cli_App_Readme_Too_LargeInputs} inputs
1284
+ * @param {{ locale?: "ko" }} options
1285
+ * @returns {LocalizedString}
1286
+ */
1287
+ const cli_app_readme_too_large = ((inputs, options = {}) => {
1288
+ experimentalStaticLocale ?? options.locale ?? getLocale();
1289
+ return ko_cli_app_readme_too_large(inputs);
1290
+ });
1291
+
1202
1292
  //#endregion
1203
1293
  //#region ../i18n/src/paraglide/messages/cli_app_release_done.js
1204
1294
  /** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
@@ -1512,9 +1602,9 @@ const cli_app_uploading = ((inputs, options = {}) => {
1512
1602
  //#endregion
1513
1603
  //#region ../i18n/src/paraglide/messages/cli_app_verify_ok.js
1514
1604
  /** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
1515
- /** @typedef {{ events: NonNullable<unknown>, http: NonNullable<unknown>, size: NonNullable<unknown>, ui: NonNullable<unknown> }} Cli_App_Verify_OkInputs */
1605
+ /** @typedef {{ events: NonNullable<unknown>, http: NonNullable<unknown>, size: NonNullable<unknown>, ui: NonNullable<unknown>, icon: NonNullable<unknown>, readme: NonNullable<unknown> }} Cli_App_Verify_OkInputs */
1516
1606
  const ko_cli_app_verify_ok = (i) => {
1517
- return `검사 통과 — 이벤트 ${i?.events} · HTTP ${i?.http} · 서버 번들 ${i?.size}(gzip) · 화면 파일 ${i?.ui}개`;
1607
+ return `검사 통과 — 이벤트 ${i?.events} · HTTP ${i?.http} · 서버 번들 ${i?.size}(gzip) · 화면 파일 ${i?.ui}개 · 아이콘 ${i?.icon} · README ${i?.readme}`;
1518
1608
  };
1519
1609
  /**
1520
1610
 
@@ -9087,7 +9177,7 @@ async function selectStore(auth, client, io, candidates, folderStore = null) {
9087
9177
 
9088
9178
  //#endregion
9089
9179
  //#region package.json
9090
- var version = "0.5.0";
9180
+ var version = "0.6.0";
9091
9181
 
9092
9182
  //#endregion
9093
9183
  //#region src/client.ts
@@ -9305,6 +9395,42 @@ async function loadBundle(dir, server) {
9305
9395
  }
9306
9396
  };
9307
9397
  }
9398
+ function collectListing(dir, manifest) {
9399
+ let icon = null;
9400
+ if (manifest.icon) {
9401
+ const file = join(dir, manifest.icon);
9402
+ if (!existsSync(file)) throw new CliError(cli_app_icon_missing({ path: manifest.icon }));
9403
+ const bytes = readFileSync(file);
9404
+ const contentType = /\.svg$/i.test(manifest.icon) ? "image/svg+xml" : "image/png";
9405
+ const problem = appIconProblem(bytes, contentType);
9406
+ if (problem) throw new CliError(cli_app_icon_invalid({
9407
+ path: manifest.icon,
9408
+ problem
9409
+ }));
9410
+ icon = {
9411
+ path: manifest.icon,
9412
+ contentType,
9413
+ bytes
9414
+ };
9415
+ }
9416
+ const readmePath = manifest.readme ?? "README.md";
9417
+ let readme = null;
9418
+ if (existsSync(join(dir, readmePath))) {
9419
+ const text = readFileSync(join(dir, readmePath), "utf8");
9420
+ if (Buffer.byteLength(text, "utf8") > APP_README_MAX_BYTES) throw new CliError(cli_app_readme_too_large({
9421
+ path: readmePath,
9422
+ limit: String(APP_README_MAX_BYTES / 1024)
9423
+ }));
9424
+ readme = {
9425
+ path: readmePath,
9426
+ text
9427
+ };
9428
+ } else if (manifest.readme) throw new CliError(cli_app_readme_missing({ path: manifest.readme }));
9429
+ return {
9430
+ icon,
9431
+ readme
9432
+ };
9433
+ }
9308
9434
  /** 로컬 검사 — 매니페스트, 번들, 이벤트 함수 존재, 비밀 값 형식, 크기. 서버 `VERIFY`·`BUNDLE`과 같은 규칙 */
9309
9435
  async function verifyApp(dir) {
9310
9436
  const { raw, manifest } = loadManifest(dir);
@@ -9321,6 +9447,7 @@ async function verifyApp(dir) {
9321
9447
  if (extra.length) warnings.push(cli_app_handler_unlisted({ events: extra.join(", ") }));
9322
9448
  const ui = manifest.ui?.adminPage ? collectUi(dir) : [];
9323
9449
  if (manifest.ui?.adminPage && !ui.some((file) => file.path === "index.html")) throw new CliError(cli_app_ui_index_missing({ dir: UI_DIR }));
9450
+ const listing = collectListing(dir, manifest);
9324
9451
  return {
9325
9452
  manifest,
9326
9453
  raw,
@@ -9331,6 +9458,7 @@ async function verifyApp(dir) {
9331
9458
  },
9332
9459
  app,
9333
9460
  ui,
9461
+ listing,
9334
9462
  warnings
9335
9463
  };
9336
9464
  }
@@ -9416,6 +9544,7 @@ export default defineApp({
9416
9544
  });
9417
9545
  `,
9418
9546
  ".gitignore": "node_modules/\ndist/\n.env\n.sayren/\n",
9547
+ "README.md": `# ${name}\n\n이 앱이 하는 일을 한두 문장으로 씁니다.\n\n## 설정\n\n설치한 뒤 채울 설정을 설명합니다.\n`,
9419
9548
  ".env.example": `# 로컬 실행(sayren app dev)의 설정·비밀 값\n# ${SETTING_ENV_PREFIX}<설정 키>=값\n# ${SECRET_ENV_PREFIX}<비밀 값 이름>=값\n`
9420
9549
  };
9421
9550
  }
@@ -9454,7 +9583,9 @@ async function appVerify(flags, io) {
9454
9583
  events: outcome.bundle.meta.events.join(", ") || "-",
9455
9584
  http: outcome.bundle.meta.http.join(", ") || "-",
9456
9585
  size: formatMb(outcome.bundle.gzipBytes),
9457
- ui: String(outcome.ui.length)
9586
+ ui: String(outcome.ui.length),
9587
+ icon: outcome.listing.icon?.path ?? cli_app_listing_none(),
9588
+ readme: outcome.listing.readme?.path ?? cli_app_listing_none()
9458
9589
  }));
9459
9590
  return 0;
9460
9591
  }
@@ -9486,6 +9617,13 @@ async function appDeploy(flags, io) {
9486
9617
  path: file.path,
9487
9618
  content: file.bytes.toString("base64")
9488
9619
  })),
9620
+ listing: {
9621
+ icon: outcome.listing.icon ? {
9622
+ contentType: outcome.listing.icon.contentType,
9623
+ content: outcome.listing.icon.bytes.toString("base64")
9624
+ } : null,
9625
+ readme: outcome.listing.readme?.text ?? null
9626
+ },
9489
9627
  ...flags.message ? { message: flags.message } : {}
9490
9628
  });
9491
9629
  printBuild(version$1, io);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sayren",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "type": "module",
5
5
  "description": "sayren CLI — 호스팅 스토어프론트(init·pull·deploy)와 서드파티 앱(app init·dev·deploy·release)",
6
6
  "bin": {
@@ -14,7 +14,7 @@
14
14
  },
15
15
  "dependencies": {
16
16
  "esbuild": "^0.28.2",
17
- "@sayren/store-sdk": "^0.30.8"
17
+ "@sayren/store-sdk": "^0.30.10"
18
18
  },
19
19
  "devDependencies": {
20
20
  "@biomejs/biome": "^2.5.14",