sayren 0.17.0 → 0.17.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.mjs +92 -26
  2. package/package.json +4 -3
package/dist/index.mjs CHANGED
@@ -9,6 +9,7 @@ import { delimiter, dirname, extname, join, relative, resolve, sep } from "node:
9
9
  import { pathToFileURL } from "node:url";
10
10
  import { gzipSync } from "node:zlib";
11
11
  import { createHash, randomBytes } from "node:crypto";
12
+ import ignore from "ignore";
12
13
 
13
14
  //#region ../mcp/src/account-token.ts
14
15
  /**
@@ -7200,6 +7201,25 @@ const cli_link_next_steps = ((inputs, options = {}) => {
7200
7201
  return ko_cli_link_next_steps(inputs);
7201
7202
  });
7202
7203
 
7204
+ //#endregion
7205
+ //#region src/paraglide/messages/cli_link_next_steps_static.js
7206
+ /** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
7207
+ /** @typedef {{}} Cli_Link_Next_Steps_StaticInputs */
7208
+ const ko_cli_link_next_steps_static = () => {
7209
+ return `다음:
7210
+ npx sayren deploy # 올리기 → 버전 → 발행`;
7211
+ };
7212
+ /**
7213
+
7214
+ * @param {Cli_Link_Next_Steps_StaticInputs} inputs
7215
+ * @param {{ locale?: "ko" }} options
7216
+ * @returns {LocalizedString}
7217
+ */
7218
+ const cli_link_next_steps_static = ((inputs = {}, options = {}) => {
7219
+ experimentalStaticLocale ?? options.locale ?? getLocale();
7220
+ return ko_cli_link_next_steps_static(inputs);
7221
+ });
7222
+
7203
7223
  //#endregion
7204
7224
  //#region src/paraglide/messages/cli_link_site_created.js
7205
7225
  /** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
@@ -9227,7 +9247,8 @@ const ko_cli_usage = (i) => {
9227
9247
  npx sayren whoami
9228
9248
  npx sayren create <이름> [--template <템플릿>] # 시작 코드를 새 폴더에 만듭니다(파일만, 로그인 없음)
9229
9249
  npx sayren link [폴더] [--store <storeId>] # 폴더를 상점 사이트에 연결합니다(없으면 사이트를 만듭니다)
9230
- npx sayren env pull # 연결된 상점 값을 .env에 씁니다
9250
+ npx sayren env pull # 연결된 상점 값·일반 환경 변수를 .env에 씁니다
9251
+ npx sayren env ls | add <키> <값> [--secret] | rm <키> # 사이트 환경 변수
9231
9252
  npx sayren pull --project <storeId> [폴더] [--force [--yes]]
9232
9253
  npx sayren deploy [--no-publish] [--skip-build] [--message <메모>] [--force [--yes]]
9233
9254
  npx sayren status
@@ -9241,7 +9262,7 @@ const ko_cli_usage = (i) => {
9241
9262
  whoami 쓰는 계정·토큰 종류·상점 범위
9242
9263
  create 시작 코드(상점 이름·상품 목록 홈, SDK·문구·분석·에이전트 안내)를 <이름> 폴더에 만듭니다. 상점을 묻지 않고 호스팅 사이트도 만들지 않습니다
9243
9264
  link 상점을 고르고(--store → 토큰 범위 → 선택) 사이트가 없으면 만든 뒤 .sayren/project.json·.env·.mcp.json을 씁니다
9244
- env env pull: 연결된 상점의 SAYREN_STORE_CODE·SAYREN_API_URL을 .env에 씁니다
9265
+ env pull: 연결된 상점의 SAYREN_STORE_CODE·SAYREN_API_URL과 일반 환경 변수를 .env에 씁니다(비밀 값 제외). ls·add·rm: 사이트 환경 변수(바꾸면 올라가 있는 버전에 곧바로 반영)
9245
9266
  pull 호스팅 중인 사이트의 작업 트리를 폴더(기본: 상점 코드)로 받고 .mcp.json·.env·.sayren/project.json을 씁니다
9246
9267
  deploy 로컬 검사 → 로컬 빌드 → 작업 트리 올리기 → 빌드 → 발행. 연결이 없으면 link부터 하고 다음 버전으로 교체합니다. 원격이 마지막 받기·올리기 이후 바뀌었으면 덮어쓰지 않습니다
9247
9268
  status 사이트·발행 버전·최근 빌드·편집 환경
@@ -9262,6 +9283,7 @@ const ko_cli_usage = (i) => {
9262
9283
  --force pull: 같은 사이트를 받은 폴더에 원격을 다시 받습니다(지울·덮을 파일을 보여 주고 묻습니다)
9263
9284
  deploy: 원격 변경·열린 편집 환경을 무시하고 덮어씁니다(확인을 묻습니다)
9264
9285
  --yes --force의 확인을 묻지 않습니다(비대화형)
9286
+ --secret env add: 비밀 값으로 둡니다(배포된 사이트에만 들어가고 값을 다시 볼 수 없습니다)
9265
9287
  --no-publish deploy: 버전까지만 만들고 발행하지 않습니다
9266
9288
  --skip-build deploy: 올리기 전 로컬 빌드(npm run build)를 건너뜁니다
9267
9289
  --message <메모> deploy: 버전 메모
@@ -10196,7 +10218,7 @@ async function selectStore(auth, client, io, candidates, folderStore = null) {
10196
10218
 
10197
10219
  //#endregion
10198
10220
  //#region package.json
10199
- var version = "0.17.0";
10221
+ var version = "0.17.2";
10200
10222
 
10201
10223
  //#endregion
10202
10224
  //#region src/client.ts
@@ -11551,6 +11573,8 @@ function lineOf(content, pattern) {
11551
11573
  }
11552
11574
  const CLIENT_FILE = /\.(ts|tsx)$/;
11553
11575
  const SERVER_FILE = /\.server\.(ts|tsx)$/;
11576
+ /** 루트의 빌드 도구 설정(`vite.config.ts`·`vitest.config.ts` 등) — Node에서만 돌고 브라우저 번들에 들어가지 않는다 */
11577
+ const BUILD_CONFIG_FILE = /^[^/]+\.config\.(ts|mts|cts)$/;
11554
11578
  /** 토큰을 브라우저 저장소에 쓰는 코드 */
11555
11579
  const BROWSER_TOKEN_STORAGE = /(localStorage|sessionStorage)\.setItem\(\s*[`'"][^`'"]*([Tt]oken|refresh|session|sayren_member)/;
11556
11580
  /**
@@ -11593,7 +11617,7 @@ function verifySources(files, options = {}) {
11593
11617
  });
11594
11618
  };
11595
11619
  for (const file of cleaned) {
11596
- if (!CLIENT_FILE.test(file.path)) continue;
11620
+ if (!CLIENT_FILE.test(file.path) || BUILD_CONFIG_FILE.test(file.path)) continue;
11597
11621
  const isServer$2 = SERVER_FILE.test(file.path) || /\/(server|\.server)\//.test(file.path);
11598
11622
  const env = /process\.env\.[A-Z_]/;
11599
11623
  if (!isServer$2 && env.test(file.content)) inFile("server-only-env", file, mcp_verify_server_only_env(), env);
@@ -12151,6 +12175,8 @@ async function gunzip(bytes, maxBytes) {
12151
12175
  /**
12152
12176
  * 로컬 작업 트리 — 서버 작업 트리와 **같은 경로 규칙**(편집 Sandbox `paths.ts`의 `accessOf`)으로 고른다. 거절 목록 방식이다(#298 P0): 폴더 구조와
12153
12177
  * 무관하게 올리고, `.env*`·`.mcp.json`·`.sayren/`·`.npmrc`·`node_modules`·`dist` 등 설치물·산출물·비밀·로컬 상태만 올리지 않는다.
12178
+ * 폴더마다 `.gitignore`가 무시하는 파일도 올리지 않는다(#298 P1) — 프레임워크마다 다른 산출물·캐시(`.next`·`out`·`build` 등)를 셀러가 이미 적어 둔 곳이고,
12179
+ * GitHub 연결로 받는 트리(git이 추적하는 파일)와 같아진다.
12154
12180
  * 비밀·로컬 상태 파일(`isNeverUpload`)은 서버 경로 규칙과 별개로 CLI가 먼저 막는다 — 서버 규칙이 넓어져도 토큰을 올리지 않게.
12155
12181
  */
12156
12182
  /**
@@ -12177,16 +12203,34 @@ const SKIP_DIRS = new Set([
12177
12203
  ".cache"
12178
12204
  ]);
12179
12205
  const sha256Hex = (bytes) => createHash("sha256").update(bytes).digest("hex");
12206
+ function gitIgnoreOf(dir, base) {
12207
+ const path = join(dir, ".gitignore");
12208
+ if (!existsSync(path) || isLink(path)) return null;
12209
+ return {
12210
+ base,
12211
+ rules: ignore().add(readFileSync(path, "utf8"))
12212
+ };
12213
+ }
12214
+ /** 위 폴더들의 `.gitignore` 중 하나라도 무시하는가(git과 같이 폴더는 끝에 `/`를 붙여 본다) */
12215
+ function gitIgnored(stack, rel, isDir) {
12216
+ return stack.some(({ base, rules }) => {
12217
+ const sub = base ? rel.slice(base.length + 1) : rel;
12218
+ return rules.ignores(isDir ? `${sub}/` : sub);
12219
+ });
12220
+ }
12180
12221
  function collectTree(root) {
12181
12222
  const files = [];
12182
12223
  const skipped = [];
12183
- const walk = (dir) => {
12224
+ const walk = (dir, base, parents) => {
12225
+ const own = gitIgnoreOf(dir, base);
12226
+ const stack = own ? [...parents, own] : parents;
12184
12227
  for (const entry of readdirSync(dir, { withFileTypes: true })) {
12185
12228
  const full = join(dir, entry.name);
12186
12229
  const rel = normalizeStorefrontPath(relative(root, full).split(sep).join("/"));
12187
12230
  if (entry.isDirectory()) {
12188
12231
  if (SKIP_DIRS.has(entry.name)) continue;
12189
- walk(full);
12232
+ if (gitIgnored(stack, rel, true) && !isNeverUpload(rel)) continue;
12233
+ walk(full, rel, stack);
12190
12234
  continue;
12191
12235
  }
12192
12236
  if (entry.isSymbolicLink() || !entry.isFile()) {
@@ -12197,6 +12241,7 @@ function collectTree(root) {
12197
12241
  skipped.push(rel);
12198
12242
  continue;
12199
12243
  }
12244
+ if (gitIgnored(stack, rel, false)) continue;
12200
12245
  const bytes = new Uint8Array(readFileSync(full));
12201
12246
  files.push({
12202
12247
  path: rel,
@@ -12205,7 +12250,7 @@ function collectTree(root) {
12205
12250
  });
12206
12251
  }
12207
12252
  };
12208
- walk(root);
12253
+ walk(root, "", []);
12209
12254
  files.sort((a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0);
12210
12255
  return {
12211
12256
  files,
@@ -12421,32 +12466,43 @@ const OUTPUT_TAIL_LINES = 40;
12421
12466
  const OUTPUT_KEEP_CHARS = 64 * 1024;
12422
12467
  /**
12423
12468
  * 빌드를 돌릴 수 있는 폴더인가 — 서버 빌드와 같은 순서로 명령을 고른다(`sayren.json` buildCommand → `build` 스크립트).
12424
- * `package.json`이 있으면 설치된 의존성(`node_modules`)이 있어야 한다
12469
+ * `package.json`이 의존성을 선언했으면 설치된 의존성(`node_modules`)이 있어야 한다
12425
12470
  */
12426
12471
  function planLocalBuild(dir) {
12427
- const hasManifest = existsSync(join(dir, "package.json"));
12472
+ const manifest = readManifest(dir);
12473
+ const missingDeps = manifest !== null && declaresDependencies(manifest) && !existsSync(join(dir, "node_modules"));
12428
12474
  const command = buildCommandOf(dir);
12429
- if (command !== null) {
12430
- if (hasManifest && !existsSync(join(dir, "node_modules"))) return { kind: "no-deps" };
12431
- return {
12432
- kind: "run",
12433
- command
12434
- };
12435
- }
12436
- let scripts;
12437
- try {
12438
- scripts = JSON.parse(readFileSync(join(dir, "package.json"), "utf8")).scripts;
12439
- } catch {
12440
- return { kind: "no-script" };
12441
- }
12475
+ if (command !== null) return missingDeps ? { kind: "no-deps" } : {
12476
+ kind: "run",
12477
+ command
12478
+ };
12479
+ const scripts = manifest?.scripts;
12442
12480
  const build = scripts && typeof scripts === "object" ? scripts.build : void 0;
12443
12481
  if (typeof build !== "string" || !build.trim()) return { kind: "no-script" };
12444
- if (!existsSync(join(dir, "node_modules"))) return { kind: "no-deps" };
12445
- return {
12482
+ return missingDeps ? { kind: "no-deps" } : {
12446
12483
  kind: "run",
12447
12484
  command: null
12448
12485
  };
12449
12486
  }
12487
+ function readManifest(dir) {
12488
+ try {
12489
+ const parsed = JSON.parse(readFileSync(join(dir, "package.json"), "utf8"));
12490
+ return parsed && typeof parsed === "object" ? parsed : null;
12491
+ } catch {
12492
+ return null;
12493
+ }
12494
+ }
12495
+ /** 설치할 것이 있는가 — 의존성이 하나도 없는 package.json(스크립트만 둔 프로젝트)은 `node_modules` 없이 빌드한다 */
12496
+ function declaresDependencies(manifest) {
12497
+ return [
12498
+ "dependencies",
12499
+ "devDependencies",
12500
+ "optionalDependencies"
12501
+ ].some((field) => {
12502
+ const deps = manifest[field];
12503
+ return !!deps && typeof deps === "object" && Object.keys(deps).length > 0;
12504
+ });
12505
+ }
12450
12506
  /** `sayren.json`의 buildCommand. 파일이 없거나 형식이 틀리면 null(형식 오류는 로컬 검사가 먼저 막는다) */
12451
12507
  function buildCommandOf(dir) {
12452
12508
  let content;
@@ -12774,6 +12830,10 @@ async function link(flags, io) {
12774
12830
  if (!existsSync(dir)) throw new CliError(cli_link_dir_missing({ dir: relativeOf(io, dir) }));
12775
12831
  migrateLegacy(dir, io.out);
12776
12832
  await linkDir(flags, dir, io);
12833
+ if (!existsSync(join(dir, "package.json"))) {
12834
+ io.out(cli_link_next_steps_static());
12835
+ return 0;
12836
+ }
12777
12837
  const manager = packageManagerOfDir(dir);
12778
12838
  io.out(cli_link_next_steps({
12779
12839
  install: installCommand(manager),
@@ -13725,7 +13785,8 @@ const BOOL_FLAGS = new Set([
13725
13785
  "version",
13726
13786
  "device",
13727
13787
  "paste",
13728
- "tail"
13788
+ "tail",
13789
+ "secret"
13729
13790
  ]);
13730
13791
  function parseArgs(argv) {
13731
13792
  const out = {
@@ -13817,15 +13878,20 @@ async function main(argv, io) {
13817
13878
  ...common,
13818
13879
  dir: str(parsed.flags.dir) ?? parsed.positional[1]
13819
13880
  }, io);
13820
- case "ls": return envList(common, io);
13881
+ case "ls": return envList({
13882
+ ...common,
13883
+ dir: str(parsed.flags.dir)
13884
+ }, io);
13821
13885
  case "add": return envAdd({
13822
13886
  ...common,
13887
+ dir: str(parsed.flags.dir),
13823
13888
  key: parsed.positional[1],
13824
13889
  value: parsed.positional[2],
13825
13890
  secret: parsed.flags.secret === true
13826
13891
  }, io);
13827
13892
  case "rm": return envRemove({
13828
13893
  ...common,
13894
+ dir: str(parsed.flags.dir),
13829
13895
  key: parsed.positional[1]
13830
13896
  }, io);
13831
13897
  default: throw new CliError(cli_env_usage());
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sayren",
3
- "version": "0.17.0",
3
+ "version": "0.17.2",
4
4
  "type": "module",
5
5
  "imports": {
6
6
  "#paraglide/*": "./src/paraglide/*"
@@ -17,6 +17,7 @@
17
17
  },
18
18
  "dependencies": {
19
19
  "esbuild": "^0.28.2",
20
+ "ignore": "^7.0.5",
20
21
  "@sayren/store-sdk": "^0.59.0"
21
22
  },
22
23
  "devDependencies": {
@@ -28,8 +29,8 @@
28
29
  "tsx": "^4.20.3",
29
30
  "typescript": "^6.0.3",
30
31
  "vitest": "^5.0.1",
31
- "@sayren/app": "0.9.0",
32
- "@sayren/mcp": "0.31.0"
32
+ "@sayren/mcp": "0.31.1",
33
+ "@sayren/app": "0.9.0"
33
34
  },
34
35
  "publishConfig": {
35
36
  "access": "public"