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.
- package/dist/index.mjs +92 -26
- 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 # 연결된 상점
|
|
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
|
|
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.
|
|
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
|
-
|
|
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`이
|
|
12469
|
+
* `package.json`이 의존성을 선언했으면 설치된 의존성(`node_modules`)이 있어야 한다
|
|
12425
12470
|
*/
|
|
12426
12471
|
function planLocalBuild(dir) {
|
|
12427
|
-
const
|
|
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
|
-
|
|
12431
|
-
|
|
12432
|
-
|
|
12433
|
-
|
|
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
|
-
|
|
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(
|
|
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.
|
|
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/
|
|
32
|
-
"@sayren/
|
|
32
|
+
"@sayren/mcp": "0.31.1",
|
|
33
|
+
"@sayren/app": "0.9.0"
|
|
33
34
|
},
|
|
34
35
|
"publishConfig": {
|
|
35
36
|
"access": "public"
|