sayren 0.17.1 → 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 +80 -22
  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 */
@@ -10198,7 +10218,7 @@ async function selectStore(auth, client, io, candidates, folderStore = null) {
10198
10218
 
10199
10219
  //#endregion
10200
10220
  //#region package.json
10201
- var version = "0.17.1";
10221
+ var version = "0.17.2";
10202
10222
 
10203
10223
  //#endregion
10204
10224
  //#region src/client.ts
@@ -11553,6 +11573,8 @@ function lineOf(content, pattern) {
11553
11573
  }
11554
11574
  const CLIENT_FILE = /\.(ts|tsx)$/;
11555
11575
  const SERVER_FILE = /\.server\.(ts|tsx)$/;
11576
+ /** 루트의 빌드 도구 설정(`vite.config.ts`·`vitest.config.ts` 등) — Node에서만 돌고 브라우저 번들에 들어가지 않는다 */
11577
+ const BUILD_CONFIG_FILE = /^[^/]+\.config\.(ts|mts|cts)$/;
11556
11578
  /** 토큰을 브라우저 저장소에 쓰는 코드 */
11557
11579
  const BROWSER_TOKEN_STORAGE = /(localStorage|sessionStorage)\.setItem\(\s*[`'"][^`'"]*([Tt]oken|refresh|session|sayren_member)/;
11558
11580
  /**
@@ -11595,7 +11617,7 @@ function verifySources(files, options = {}) {
11595
11617
  });
11596
11618
  };
11597
11619
  for (const file of cleaned) {
11598
- if (!CLIENT_FILE.test(file.path)) continue;
11620
+ if (!CLIENT_FILE.test(file.path) || BUILD_CONFIG_FILE.test(file.path)) continue;
11599
11621
  const isServer$2 = SERVER_FILE.test(file.path) || /\/(server|\.server)\//.test(file.path);
11600
11622
  const env = /process\.env\.[A-Z_]/;
11601
11623
  if (!isServer$2 && env.test(file.content)) inFile("server-only-env", file, mcp_verify_server_only_env(), env);
@@ -12153,6 +12175,8 @@ async function gunzip(bytes, maxBytes) {
12153
12175
  /**
12154
12176
  * 로컬 작업 트리 — 서버 작업 트리와 **같은 경로 규칙**(편집 Sandbox `paths.ts`의 `accessOf`)으로 고른다. 거절 목록 방식이다(#298 P0): 폴더 구조와
12155
12177
  * 무관하게 올리고, `.env*`·`.mcp.json`·`.sayren/`·`.npmrc`·`node_modules`·`dist` 등 설치물·산출물·비밀·로컬 상태만 올리지 않는다.
12178
+ * 폴더마다 `.gitignore`가 무시하는 파일도 올리지 않는다(#298 P1) — 프레임워크마다 다른 산출물·캐시(`.next`·`out`·`build` 등)를 셀러가 이미 적어 둔 곳이고,
12179
+ * GitHub 연결로 받는 트리(git이 추적하는 파일)와 같아진다.
12156
12180
  * 비밀·로컬 상태 파일(`isNeverUpload`)은 서버 경로 규칙과 별개로 CLI가 먼저 막는다 — 서버 규칙이 넓어져도 토큰을 올리지 않게.
12157
12181
  */
12158
12182
  /**
@@ -12179,16 +12203,34 @@ const SKIP_DIRS = new Set([
12179
12203
  ".cache"
12180
12204
  ]);
12181
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
+ }
12182
12221
  function collectTree(root) {
12183
12222
  const files = [];
12184
12223
  const skipped = [];
12185
- const walk = (dir) => {
12224
+ const walk = (dir, base, parents) => {
12225
+ const own = gitIgnoreOf(dir, base);
12226
+ const stack = own ? [...parents, own] : parents;
12186
12227
  for (const entry of readdirSync(dir, { withFileTypes: true })) {
12187
12228
  const full = join(dir, entry.name);
12188
12229
  const rel = normalizeStorefrontPath(relative(root, full).split(sep).join("/"));
12189
12230
  if (entry.isDirectory()) {
12190
12231
  if (SKIP_DIRS.has(entry.name)) continue;
12191
- walk(full);
12232
+ if (gitIgnored(stack, rel, true) && !isNeverUpload(rel)) continue;
12233
+ walk(full, rel, stack);
12192
12234
  continue;
12193
12235
  }
12194
12236
  if (entry.isSymbolicLink() || !entry.isFile()) {
@@ -12199,6 +12241,7 @@ function collectTree(root) {
12199
12241
  skipped.push(rel);
12200
12242
  continue;
12201
12243
  }
12244
+ if (gitIgnored(stack, rel, false)) continue;
12202
12245
  const bytes = new Uint8Array(readFileSync(full));
12203
12246
  files.push({
12204
12247
  path: rel,
@@ -12207,7 +12250,7 @@ function collectTree(root) {
12207
12250
  });
12208
12251
  }
12209
12252
  };
12210
- walk(root);
12253
+ walk(root, "", []);
12211
12254
  files.sort((a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0);
12212
12255
  return {
12213
12256
  files,
@@ -12423,32 +12466,43 @@ const OUTPUT_TAIL_LINES = 40;
12423
12466
  const OUTPUT_KEEP_CHARS = 64 * 1024;
12424
12467
  /**
12425
12468
  * 빌드를 돌릴 수 있는 폴더인가 — 서버 빌드와 같은 순서로 명령을 고른다(`sayren.json` buildCommand → `build` 스크립트).
12426
- * `package.json`이 있으면 설치된 의존성(`node_modules`)이 있어야 한다
12469
+ * `package.json`이 의존성을 선언했으면 설치된 의존성(`node_modules`)이 있어야 한다
12427
12470
  */
12428
12471
  function planLocalBuild(dir) {
12429
- const hasManifest = existsSync(join(dir, "package.json"));
12472
+ const manifest = readManifest(dir);
12473
+ const missingDeps = manifest !== null && declaresDependencies(manifest) && !existsSync(join(dir, "node_modules"));
12430
12474
  const command = buildCommandOf(dir);
12431
- if (command !== null) {
12432
- if (hasManifest && !existsSync(join(dir, "node_modules"))) return { kind: "no-deps" };
12433
- return {
12434
- kind: "run",
12435
- command
12436
- };
12437
- }
12438
- let scripts;
12439
- try {
12440
- scripts = JSON.parse(readFileSync(join(dir, "package.json"), "utf8")).scripts;
12441
- } catch {
12442
- return { kind: "no-script" };
12443
- }
12475
+ if (command !== null) return missingDeps ? { kind: "no-deps" } : {
12476
+ kind: "run",
12477
+ command
12478
+ };
12479
+ const scripts = manifest?.scripts;
12444
12480
  const build = scripts && typeof scripts === "object" ? scripts.build : void 0;
12445
12481
  if (typeof build !== "string" || !build.trim()) return { kind: "no-script" };
12446
- if (!existsSync(join(dir, "node_modules"))) return { kind: "no-deps" };
12447
- return {
12482
+ return missingDeps ? { kind: "no-deps" } : {
12448
12483
  kind: "run",
12449
12484
  command: null
12450
12485
  };
12451
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
+ }
12452
12506
  /** `sayren.json`의 buildCommand. 파일이 없거나 형식이 틀리면 null(형식 오류는 로컬 검사가 먼저 막는다) */
12453
12507
  function buildCommandOf(dir) {
12454
12508
  let content;
@@ -12776,6 +12830,10 @@ async function link(flags, io) {
12776
12830
  if (!existsSync(dir)) throw new CliError(cli_link_dir_missing({ dir: relativeOf(io, dir) }));
12777
12831
  migrateLegacy(dir, io.out);
12778
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
+ }
12779
12837
  const manager = packageManagerOfDir(dir);
12780
12838
  io.out(cli_link_next_steps({
12781
12839
  install: installCommand(manager),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sayren",
3
- "version": "0.17.1",
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"