sayren 0.16.0 → 0.17.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.
- package/dist/index.mjs +422 -69
- package/package.json +3 -3
package/dist/index.mjs
CHANGED
|
@@ -5,7 +5,7 @@ import { createInterface } from "node:readline/promises";
|
|
|
5
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, STORE_CODE_PATTERN, 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
|
-
import { dirname, extname, join, relative, resolve, sep } from "node:path";
|
|
8
|
+
import { delimiter, dirname, extname, join, relative, resolve, sep } from "node:path";
|
|
9
9
|
import { pathToFileURL } from "node:url";
|
|
10
10
|
import { gzipSync } from "node:zlib";
|
|
11
11
|
import { createHash, randomBytes } from "node:crypto";
|
|
@@ -4367,7 +4367,7 @@ const cli_app_default_export_missing = ((inputs = {}, options = {}) => {
|
|
|
4367
4367
|
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
4368
4368
|
/** @typedef {{ number: NonNullable<unknown> }} Cli_App_Deploy_DoneInputs */
|
|
4369
4369
|
const ko_cli_app_deploy_done = (i) => {
|
|
4370
|
-
return
|
|
4370
|
+
return `${i?.number}번 버전을 만들었습니다. 적용하려면: npx sayren app release ${i?.number}`;
|
|
4371
4371
|
};
|
|
4372
4372
|
/**
|
|
4373
4373
|
|
|
@@ -4738,7 +4738,7 @@ const cli_app_manifest_legacy = ((inputs = {}, options = {}) => {
|
|
|
4738
4738
|
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
4739
4739
|
/** @typedef {{ file: NonNullable<unknown> }} Cli_App_Manifest_MissingInputs */
|
|
4740
4740
|
const ko_cli_app_manifest_missing = (i) => {
|
|
4741
|
-
return `${i?.file}
|
|
4741
|
+
return `${i?.file} 파일이 없습니다. 앱 폴더에서 실행하거나 --dir을 주십시오`;
|
|
4742
4742
|
};
|
|
4743
4743
|
/**
|
|
4744
4744
|
|
|
@@ -4756,7 +4756,7 @@ const cli_app_manifest_missing = ((inputs, options = {}) => {
|
|
|
4756
4756
|
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
4757
4757
|
/** @typedef {{ file: NonNullable<unknown> }} Cli_App_Not_RegisteredInputs */
|
|
4758
4758
|
const ko_cli_app_not_registered = (i) => {
|
|
4759
|
-
return `등록된 앱이 아닙니다(${i?.file}
|
|
4759
|
+
return `등록된 앱이 아닙니다(${i?.file} 파일은 있지만 .sayren/app.json이 없습니다). npx sayren app init <handle>로 만든 폴더에서 실행하십시오`;
|
|
4760
4760
|
};
|
|
4761
4761
|
/**
|
|
4762
4762
|
|
|
@@ -4828,7 +4828,7 @@ const cli_app_readme_too_large = ((inputs, options = {}) => {
|
|
|
4828
4828
|
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
4829
4829
|
/** @typedef {{ number: NonNullable<unknown>, applied: NonNullable<unknown>, pending: NonNullable<unknown> }} Cli_App_Release_DoneInputs */
|
|
4830
4830
|
const ko_cli_app_release_done = (i) => {
|
|
4831
|
-
return
|
|
4831
|
+
return `${i?.number}번 버전이 현재 버전입니다 — 곧바로 적용 ${i?.applied}, 재동의 대기 ${i?.pending}`;
|
|
4832
4832
|
};
|
|
4833
4833
|
/**
|
|
4834
4834
|
|
|
@@ -4846,7 +4846,7 @@ const cli_app_release_done = ((inputs, options = {}) => {
|
|
|
4846
4846
|
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
4847
4847
|
/** @typedef {{ name: NonNullable<unknown> }} Cli_App_Secret_DeletedInputs */
|
|
4848
4848
|
const ko_cli_app_secret_deleted = (i) => {
|
|
4849
|
-
return
|
|
4849
|
+
return `${i?.name} 비밀 값을 지웠습니다`;
|
|
4850
4850
|
};
|
|
4851
4851
|
/**
|
|
4852
4852
|
|
|
@@ -4918,7 +4918,7 @@ const cli_app_secret_name_required = ((inputs = {}, options = {}) => {
|
|
|
4918
4918
|
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
4919
4919
|
/** @typedef {{ name: NonNullable<unknown> }} Cli_App_Secret_SavedInputs */
|
|
4920
4920
|
const ko_cli_app_secret_saved = (i) => {
|
|
4921
|
-
return
|
|
4921
|
+
return `${i?.name} 비밀 값을 저장했습니다`;
|
|
4922
4922
|
};
|
|
4923
4923
|
/**
|
|
4924
4924
|
|
|
@@ -4972,7 +4972,7 @@ const cli_app_since_invalid = ((inputs, options = {}) => {
|
|
|
4972
4972
|
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
4973
4973
|
/** @typedef {{ file: NonNullable<unknown> }} Cli_App_State_InvalidInputs */
|
|
4974
4974
|
const ko_cli_app_state_invalid = (i) => {
|
|
4975
|
-
return `${i?.file}
|
|
4975
|
+
return `${i?.file} 파일을 읽을 수 없습니다. 파일을 지우고 npx sayren app init을 다시 실행하십시오`;
|
|
4976
4976
|
};
|
|
4977
4977
|
/**
|
|
4978
4978
|
|
|
@@ -5832,7 +5832,7 @@ const cli_create_usage = ((inputs = {}, options = {}) => {
|
|
|
5832
5832
|
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
5833
5833
|
/** @typedef {{ path: NonNullable<unknown> }} Cli_Credentials_Git_TrackedInputs */
|
|
5834
5834
|
const ko_cli_credentials_git_tracked = (i) => {
|
|
5835
|
-
return `${i?.path}
|
|
5835
|
+
return `${i?.path} 파일이 git에 커밋돼 있어 토큰을 쓰지 않았습니다. 추적을 해제하거나 XDG_CONFIG_HOME으로 다른 폴더를 지정하십시오`;
|
|
5836
5836
|
};
|
|
5837
5837
|
/**
|
|
5838
5838
|
|
|
@@ -5886,7 +5886,7 @@ const cli_credentials_no_config_dir = ((inputs = {}, options = {}) => {
|
|
|
5886
5886
|
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
5887
5887
|
/** @typedef {{ path: NonNullable<unknown> }} Cli_Credentials_SymlinkInputs */
|
|
5888
5888
|
const ko_cli_credentials_symlink = (i) => {
|
|
5889
|
-
return `${i?.path}
|
|
5889
|
+
return `${i?.path} 파일이 심볼릭 링크라 읽지 않았습니다. 링크를 지운 뒤 다시 로그인하십시오`;
|
|
5890
5890
|
};
|
|
5891
5891
|
/**
|
|
5892
5892
|
|
|
@@ -5904,7 +5904,7 @@ const cli_credentials_symlink = ((inputs, options = {}) => {
|
|
|
5904
5904
|
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
5905
5905
|
/** @typedef {{ path: NonNullable<unknown> }} Cli_Credentials_UnreadableInputs */
|
|
5906
5906
|
const ko_cli_credentials_unreadable = (i) => {
|
|
5907
|
-
return `${i?.path}
|
|
5907
|
+
return `${i?.path} 파일을 읽지 못했습니다. 파일을 지우고 다시 로그인하십시오`;
|
|
5908
5908
|
};
|
|
5909
5909
|
/**
|
|
5910
5910
|
|
|
@@ -6247,7 +6247,7 @@ const cli_deploy_force_yes_required = ((inputs = {}, options = {}) => {
|
|
|
6247
6247
|
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
6248
6248
|
/** @typedef {{ dir: NonNullable<unknown> }} Cli_Deploy_LinkingInputs */
|
|
6249
6249
|
const ko_cli_deploy_linking = (i) => {
|
|
6250
|
-
return `${i?.dir}
|
|
6250
|
+
return `${i?.dir} 폴더는 아직 상점에 연결되지 않았습니다 — 먼저 연결합니다(npx sayren link)`;
|
|
6251
6251
|
};
|
|
6252
6252
|
/**
|
|
6253
6253
|
|
|
@@ -6284,7 +6284,7 @@ const cli_deploy_merged = ((inputs = {}, options = {}) => {
|
|
|
6284
6284
|
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
6285
6285
|
/** @typedef {{ version: NonNullable<unknown> }} Cli_Deploy_Not_PublishedInputs */
|
|
6286
6286
|
const ko_cli_deploy_not_published = (i) => {
|
|
6287
|
-
return `발행하지 않았습니다(--no-publish). 상점 플랫폼 › 스토어프론트 › 버전에서 v${i?.version}
|
|
6287
|
+
return `발행하지 않았습니다(--no-publish). 상점 플랫폼 › 스토어프론트 › 버전에서 v${i?.version} 버전을 발행할 수 있습니다`;
|
|
6288
6288
|
};
|
|
6289
6289
|
/**
|
|
6290
6290
|
|
|
@@ -6302,7 +6302,7 @@ const cli_deploy_not_published = ((inputs, options = {}) => {
|
|
|
6302
6302
|
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
6303
6303
|
/** @typedef {{ version: NonNullable<unknown> }} Cli_Deploy_Publish_ApprovalInputs */
|
|
6304
6304
|
const ko_cli_deploy_publish_approval = (i) => {
|
|
6305
|
-
return `발행은 승인이 필요합니다(에이전트 토큰). 셀러가 승인하면 v${i?.version}
|
|
6305
|
+
return `발행은 승인이 필요합니다(에이전트 토큰). 셀러가 승인하면 v${i?.version} 버전이 발행됩니다`;
|
|
6306
6306
|
};
|
|
6307
6307
|
/**
|
|
6308
6308
|
|
|
@@ -6374,7 +6374,7 @@ const cli_deploy_seller_git = ((inputs, options = {}) => {
|
|
|
6374
6374
|
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
6375
6375
|
/** @typedef {{ version: NonNullable<unknown> }} Cli_Deploy_Site_ChangedInputs */
|
|
6376
6376
|
const ko_cli_deploy_site_changed = (i) => {
|
|
6377
|
-
return `다른 사람이 그 사이 다른 버전을 발행했습니다. v${i?.version}
|
|
6377
|
+
return `다른 사람이 그 사이 다른 버전을 발행했습니다. v${i?.version} 버전은 만들어 뒀습니다 — 상점 플랫폼에서 확인한 뒤 발행하십시오`;
|
|
6378
6378
|
};
|
|
6379
6379
|
/**
|
|
6380
6380
|
|
|
@@ -6621,6 +6621,42 @@ const cli_edit_session_open = ((inputs = {}, options = {}) => {
|
|
|
6621
6621
|
return ko_cli_edit_session_open(inputs);
|
|
6622
6622
|
});
|
|
6623
6623
|
|
|
6624
|
+
//#endregion
|
|
6625
|
+
//#region src/paraglide/messages/cli_env_approval.js
|
|
6626
|
+
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
6627
|
+
/** @typedef {{}} Cli_Env_ApprovalInputs */
|
|
6628
|
+
const ko_cli_env_approval = () => {
|
|
6629
|
+
return `env 변경은 승인 대기입니다. 상점 플랫폼에서 승인하면 반영됩니다`;
|
|
6630
|
+
};
|
|
6631
|
+
/**
|
|
6632
|
+
|
|
6633
|
+
* @param {Cli_Env_ApprovalInputs} inputs
|
|
6634
|
+
* @param {{ locale?: "ko" }} options
|
|
6635
|
+
* @returns {LocalizedString}
|
|
6636
|
+
*/
|
|
6637
|
+
const cli_env_approval = ((inputs = {}, options = {}) => {
|
|
6638
|
+
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
6639
|
+
return ko_cli_env_approval(inputs);
|
|
6640
|
+
});
|
|
6641
|
+
|
|
6642
|
+
//#endregion
|
|
6643
|
+
//#region src/paraglide/messages/cli_env_empty.js
|
|
6644
|
+
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
6645
|
+
/** @typedef {{}} Cli_Env_EmptyInputs */
|
|
6646
|
+
const ko_cli_env_empty = () => {
|
|
6647
|
+
return `설정한 env가 없습니다. npx sayren env add <키> <값>으로 추가하십시오`;
|
|
6648
|
+
};
|
|
6649
|
+
/**
|
|
6650
|
+
|
|
6651
|
+
* @param {Cli_Env_EmptyInputs} inputs
|
|
6652
|
+
* @param {{ locale?: "ko" }} options
|
|
6653
|
+
* @returns {LocalizedString}
|
|
6654
|
+
*/
|
|
6655
|
+
const cli_env_empty = ((inputs = {}, options = {}) => {
|
|
6656
|
+
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
6657
|
+
return ko_cli_env_empty(inputs);
|
|
6658
|
+
});
|
|
6659
|
+
|
|
6624
6660
|
//#endregion
|
|
6625
6661
|
//#region src/paraglide/messages/cli_env_not_linked.js
|
|
6626
6662
|
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
@@ -6639,6 +6675,24 @@ const cli_env_not_linked = ((inputs, options = {}) => {
|
|
|
6639
6675
|
return ko_cli_env_not_linked(inputs);
|
|
6640
6676
|
});
|
|
6641
6677
|
|
|
6678
|
+
//#endregion
|
|
6679
|
+
//#region src/paraglide/messages/cli_env_pull_skipped.js
|
|
6680
|
+
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
6681
|
+
/** @typedef {{ keys: NonNullable<unknown> }} Cli_Env_Pull_SkippedInputs */
|
|
6682
|
+
const ko_cli_env_pull_skipped = (i) => {
|
|
6683
|
+
return `.env 한 줄로 같은 값을 쓸 수 없어 건너뛴 env가 있습니다: ${i?.keys}. 로컬에서는 직접 넣으십시오`;
|
|
6684
|
+
};
|
|
6685
|
+
/**
|
|
6686
|
+
|
|
6687
|
+
* @param {Cli_Env_Pull_SkippedInputs} inputs
|
|
6688
|
+
* @param {{ locale?: "ko" }} options
|
|
6689
|
+
* @returns {LocalizedString}
|
|
6690
|
+
*/
|
|
6691
|
+
const cli_env_pull_skipped = ((inputs, options = {}) => {
|
|
6692
|
+
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
6693
|
+
return ko_cli_env_pull_skipped(inputs);
|
|
6694
|
+
});
|
|
6695
|
+
|
|
6642
6696
|
//#endregion
|
|
6643
6697
|
//#region src/paraglide/messages/cli_env_pulled.js
|
|
6644
6698
|
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
@@ -6657,6 +6711,78 @@ const cli_env_pulled = ((inputs, options = {}) => {
|
|
|
6657
6711
|
return ko_cli_env_pulled(inputs);
|
|
6658
6712
|
});
|
|
6659
6713
|
|
|
6714
|
+
//#endregion
|
|
6715
|
+
//#region src/paraglide/messages/cli_env_pulled_vars.js
|
|
6716
|
+
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
6717
|
+
/** @typedef {{ file: NonNullable<unknown>, count: NonNullable<unknown> }} Cli_Env_Pulled_VarsInputs */
|
|
6718
|
+
const ko_cli_env_pulled_vars = (i) => {
|
|
6719
|
+
return `${i?.file}에 일반 env ${i?.count}개를 썼습니다(비밀 값은 배포된 사이트에만 들어가 받지 않습니다)`;
|
|
6720
|
+
};
|
|
6721
|
+
/**
|
|
6722
|
+
|
|
6723
|
+
* @param {Cli_Env_Pulled_VarsInputs} inputs
|
|
6724
|
+
* @param {{ locale?: "ko" }} options
|
|
6725
|
+
* @returns {LocalizedString}
|
|
6726
|
+
*/
|
|
6727
|
+
const cli_env_pulled_vars = ((inputs, options = {}) => {
|
|
6728
|
+
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
6729
|
+
return ko_cli_env_pulled_vars(inputs);
|
|
6730
|
+
});
|
|
6731
|
+
|
|
6732
|
+
//#endregion
|
|
6733
|
+
//#region src/paraglide/messages/cli_env_removed.js
|
|
6734
|
+
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
6735
|
+
/** @typedef {{ key: NonNullable<unknown> }} Cli_Env_RemovedInputs */
|
|
6736
|
+
const ko_cli_env_removed = (i) => {
|
|
6737
|
+
return `env ${i?.key}을(를) 지웠습니다. 올라가 있는 버전에서도 곧바로 빠집니다`;
|
|
6738
|
+
};
|
|
6739
|
+
/**
|
|
6740
|
+
|
|
6741
|
+
* @param {Cli_Env_RemovedInputs} inputs
|
|
6742
|
+
* @param {{ locale?: "ko" }} options
|
|
6743
|
+
* @returns {LocalizedString}
|
|
6744
|
+
*/
|
|
6745
|
+
const cli_env_removed = ((inputs, options = {}) => {
|
|
6746
|
+
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
6747
|
+
return ko_cli_env_removed(inputs);
|
|
6748
|
+
});
|
|
6749
|
+
|
|
6750
|
+
//#endregion
|
|
6751
|
+
//#region src/paraglide/messages/cli_env_secret_mask.js
|
|
6752
|
+
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
6753
|
+
/** @typedef {{}} Cli_Env_Secret_MaskInputs */
|
|
6754
|
+
const ko_cli_env_secret_mask = () => {
|
|
6755
|
+
return `(비밀 값)`;
|
|
6756
|
+
};
|
|
6757
|
+
/**
|
|
6758
|
+
|
|
6759
|
+
* @param {Cli_Env_Secret_MaskInputs} inputs
|
|
6760
|
+
* @param {{ locale?: "ko" }} options
|
|
6761
|
+
* @returns {LocalizedString}
|
|
6762
|
+
*/
|
|
6763
|
+
const cli_env_secret_mask = ((inputs = {}, options = {}) => {
|
|
6764
|
+
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
6765
|
+
return ko_cli_env_secret_mask(inputs);
|
|
6766
|
+
});
|
|
6767
|
+
|
|
6768
|
+
//#endregion
|
|
6769
|
+
//#region src/paraglide/messages/cli_env_set.js
|
|
6770
|
+
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
6771
|
+
/** @typedef {{ key: NonNullable<unknown> }} Cli_Env_SetInputs */
|
|
6772
|
+
const ko_cli_env_set = (i) => {
|
|
6773
|
+
return `env ${i?.key}을(를) 설정했습니다. 올라가 있는 버전에 곧바로 반영됩니다`;
|
|
6774
|
+
};
|
|
6775
|
+
/**
|
|
6776
|
+
|
|
6777
|
+
* @param {Cli_Env_SetInputs} inputs
|
|
6778
|
+
* @param {{ locale?: "ko" }} options
|
|
6779
|
+
* @returns {LocalizedString}
|
|
6780
|
+
*/
|
|
6781
|
+
const cli_env_set = ((inputs, options = {}) => {
|
|
6782
|
+
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
6783
|
+
return ko_cli_env_set(inputs);
|
|
6784
|
+
});
|
|
6785
|
+
|
|
6660
6786
|
//#endregion
|
|
6661
6787
|
//#region src/paraglide/messages/cli_env_store_code_unknown.js
|
|
6662
6788
|
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
@@ -6680,7 +6806,7 @@ const cli_env_store_code_unknown = ((inputs = {}, options = {}) => {
|
|
|
6680
6806
|
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
6681
6807
|
/** @typedef {{}} Cli_Env_UsageInputs */
|
|
6682
6808
|
const ko_cli_env_usage = () => {
|
|
6683
|
-
return `사용법: npx sayren env pull [--dir <폴더>]
|
|
6809
|
+
return `사용법: npx sayren env pull [--dir <폴더>] · env ls · env add <키> <값> [--secret] · env rm <키>`;
|
|
6684
6810
|
};
|
|
6685
6811
|
/**
|
|
6686
6812
|
|
|
@@ -6968,7 +7094,7 @@ const cli_legacy_env_token_removed = ((inputs, options = {}) => {
|
|
|
6968
7094
|
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
6969
7095
|
/** @typedef {{ from: NonNullable<unknown>, to: NonNullable<unknown> }} Cli_Legacy_Rc_TrackedInputs */
|
|
6970
7096
|
const ko_cli_legacy_rc_tracked = (i) => {
|
|
6971
|
-
return `${i?.from} → ${i?.to}(${i?.from}
|
|
7097
|
+
return `${i?.from} → ${i?.to}(${i?.from} 파일은 더 쓰지 않으니 커밋에서 지워도 됩니다)`;
|
|
6972
7098
|
};
|
|
6973
7099
|
/**
|
|
6974
7100
|
|
|
@@ -7040,7 +7166,7 @@ const cli_link_editing_disabled = ((inputs = {}, options = {}) => {
|
|
|
7040
7166
|
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
7041
7167
|
/** @typedef {{ lines: NonNullable<unknown> }} Cli_Link_GitignoreInputs */
|
|
7042
7168
|
const ko_cli_link_gitignore = (i) => {
|
|
7043
|
-
return `.gitignore에 ${i?.lines}
|
|
7169
|
+
return `.gitignore에 ${i?.lines} 항목을 더했습니다(배포에 올리지 않습니다)`;
|
|
7044
7170
|
};
|
|
7045
7171
|
/**
|
|
7046
7172
|
|
|
@@ -7727,7 +7853,7 @@ const cli_pull_adopt_confirm = ((inputs = {}, options = {}) => {
|
|
|
7727
7853
|
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
7728
7854
|
/** @typedef {{ dir: NonNullable<unknown>, stateFile: NonNullable<unknown>, siteId: NonNullable<unknown> }} Cli_Pull_Adopt_DirInputs */
|
|
7729
7855
|
const ko_cli_pull_adopt_dir = (i) => {
|
|
7730
|
-
return `${i?.dir}
|
|
7856
|
+
return `${i?.dir} 폴더에 ${i?.stateFile} 파일이 없습니다. 이 폴더를 사이트 ${i?.siteId}의 소스 폴더로 받아들이고 원격 작업 트리로 맞춥니다`;
|
|
7731
7857
|
};
|
|
7732
7858
|
/**
|
|
7733
7859
|
|
|
@@ -7817,7 +7943,7 @@ const cli_pull_dir_not_empty = ((inputs, options = {}) => {
|
|
|
7817
7943
|
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
7818
7944
|
/** @typedef {{ dir: NonNullable<unknown>, siteId: NonNullable<unknown>, stateFile: NonNullable<unknown>, storeId: NonNullable<unknown> }} Cli_Pull_Dir_Not_PulledInputs */
|
|
7819
7945
|
const ko_cli_pull_dir_not_pulled = (i) => {
|
|
7820
|
-
return `${i?.dir}
|
|
7946
|
+
return `${i?.dir} 폴더는 이 사이트(${i?.siteId})를 받은 폴더가 아닙니다(${i?.stateFile} 없음). 이 사이트 소스를 clone한 폴더면 --project ${i?.storeId}를 함께 주십시오. 아니면 빈 폴더를 지정하십시오`;
|
|
7821
7947
|
};
|
|
7822
7948
|
/**
|
|
7823
7949
|
|
|
@@ -7835,7 +7961,7 @@ const cli_pull_dir_not_pulled = ((inputs, options = {}) => {
|
|
|
7835
7961
|
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
7836
7962
|
/** @typedef {{ dir: NonNullable<unknown>, siteId: NonNullable<unknown> }} Cli_Pull_Dir_Other_SiteInputs */
|
|
7837
7963
|
const ko_cli_pull_dir_other_site = (i) => {
|
|
7838
|
-
return `${i?.dir}
|
|
7964
|
+
return `${i?.dir} 폴더는 다른 사이트(${i?.siteId})를 받은 폴더입니다. 빈 폴더를 지정하십시오`;
|
|
7839
7965
|
};
|
|
7840
7966
|
/**
|
|
7841
7967
|
|
|
@@ -8128,7 +8254,7 @@ const cli_pull_store_mismatch = ((inputs, options = {}) => {
|
|
|
8128
8254
|
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
8129
8255
|
/** @typedef {{ path: NonNullable<unknown>, install: NonNullable<unknown> }} Cli_Pull_Watched_ChangedInputs */
|
|
8130
8256
|
const ko_cli_pull_watched_changed = (i) => {
|
|
8131
|
-
return `알림: 원격의 ${i?.path}
|
|
8257
|
+
return `알림: 원격의 ${i?.path} 파일이 이 폴더와 다릅니다. 설치 전에 내용을 확인하십시오(${i?.install} 권장)`;
|
|
8132
8258
|
};
|
|
8133
8259
|
/**
|
|
8134
8260
|
|
|
@@ -8511,7 +8637,7 @@ const cli_state_missing_fields = ((inputs, options = {}) => {
|
|
|
8511
8637
|
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
8512
8638
|
/** @typedef {{ name: NonNullable<unknown> }} Cli_State_UnreadableInputs */
|
|
8513
8639
|
const ko_cli_state_unreadable = (i) => {
|
|
8514
|
-
return `${i?.name}
|
|
8640
|
+
return `${i?.name} 파일을 읽지 못했습니다. JSON 형식을 확인하십시오`;
|
|
8515
8641
|
};
|
|
8516
8642
|
/**
|
|
8517
8643
|
|
|
@@ -8836,7 +8962,7 @@ const cli_store_none = ((inputs = {}, options = {}) => {
|
|
|
8836
8962
|
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
8837
8963
|
/** @typedef {{ storeId: NonNullable<unknown> }} Cli_Store_Out_Of_ScopeInputs */
|
|
8838
8964
|
const ko_cli_store_out_of_scope = (i) => {
|
|
8839
|
-
return
|
|
8965
|
+
return `${i?.storeId} 상점은 이 토큰의 상점 범위 밖입니다. 그 상점이 포함되게 다시 로그인하거나(npx sayren login) 다른 토큰을 쓰십시오`;
|
|
8840
8966
|
};
|
|
8841
8967
|
/**
|
|
8842
8968
|
|
|
@@ -10070,7 +10196,7 @@ async function selectStore(auth, client, io, candidates, folderStore = null) {
|
|
|
10070
10196
|
|
|
10071
10197
|
//#endregion
|
|
10072
10198
|
//#region package.json
|
|
10073
|
-
var version = "0.
|
|
10199
|
+
var version = "0.17.0";
|
|
10074
10200
|
|
|
10075
10201
|
//#endregion
|
|
10076
10202
|
//#region src/client.ts
|
|
@@ -11086,11 +11212,114 @@ async function appMain(positional, flags, io) {
|
|
|
11086
11212
|
}
|
|
11087
11213
|
}
|
|
11088
11214
|
|
|
11215
|
+
//#endregion
|
|
11216
|
+
//#region ../../apps/hosting-sandbox/src/project-config.ts
|
|
11217
|
+
/**
|
|
11218
|
+
* 프로젝트 설정 `sayren.json`(#298 P1). 셀러 계약은 `package.json` 스크립트와 이 파일뿐이다 — 플랫폼은 프레임워크를 감지하거나 빌드를 보정하지 않는다.
|
|
11219
|
+
* - `buildCommand`: 빌드 명령. 없으면 `package.json`의 `build` 스크립트, 그것도 없으면 빌드하지 않는다(순수 HTML)
|
|
11220
|
+
* - `output.static`: 정적 파일 폴더(작업 트리 기준). `.`은 루트
|
|
11221
|
+
* - `output.server`: Web 표준 fetch 핸들러(`export default { fetch(request, env) }`)를 내보내는 ES 모듈. 없으면 정적 전용 사이트다
|
|
11222
|
+
*
|
|
11223
|
+
* 파일이 없으면 #298 이전 사이트(옛 basic·시작 코드)의 경로를 기본값으로 쓴다. 파일이 있으면 적힌 그대로다(`output.server`를 빼면 정적 전용).
|
|
11224
|
+
* 런타임 이름(Workers·wrangler)은 이 계약에 넣지 않는다 — 런타임을 옮겨도(#333) 이 파일은 그대로 둔다.
|
|
11225
|
+
* Node API를 쓰지 않는다(Sandbox Worker·CLI가 같이 쓴다).
|
|
11226
|
+
*/
|
|
11227
|
+
const PROJECT_CONFIG_FILE = "sayren.json";
|
|
11228
|
+
/** `sayren.json`이 없는 사이트의 기본값 — #298 이전 빌드가 쓰던 TanStack Start 출력 경로 */
|
|
11229
|
+
const LEGACY_PROJECT_CONFIG = {
|
|
11230
|
+
buildCommand: null,
|
|
11231
|
+
output: {
|
|
11232
|
+
static: "dist/client",
|
|
11233
|
+
server: "dist/server/server.js"
|
|
11234
|
+
}
|
|
11235
|
+
};
|
|
11236
|
+
const MAX_COMMAND_LENGTH = 1e3;
|
|
11237
|
+
const MAX_OUTPUT_PATH_LENGTH = 256;
|
|
11238
|
+
const OUTPUT_SEGMENT_RE = /^[A-Za-z0-9._@+-]+$/;
|
|
11239
|
+
/** 출력 경로 문제(없으면 null). 작업 트리 안의 상대 경로만 — 설치물·비밀 값 파일은 가리킬 수 없다 */
|
|
11240
|
+
function outputPathProblem(path, allowRoot) {
|
|
11241
|
+
if (path === ".") return allowRoot ? null : "루트(.)는 쓸 수 없습니다";
|
|
11242
|
+
if (path.length === 0 || path.length > MAX_OUTPUT_PATH_LENGTH) return "길이가 맞지 않습니다";
|
|
11243
|
+
if (path.startsWith("/")) return "작업 트리 기준 상대 경로여야 합니다";
|
|
11244
|
+
for (const seg of path.split("/")) {
|
|
11245
|
+
if (seg === "" || seg === "." || seg === "..") return "빈 조각·.·..은 쓸 수 없습니다";
|
|
11246
|
+
if (!OUTPUT_SEGMENT_RE.test(seg)) return "영문·숫자·._@+-만 쓸 수 있습니다";
|
|
11247
|
+
if (seg === "node_modules" || seg.startsWith(".env")) return "설치물·.env 파일은 가리킬 수 없습니다";
|
|
11248
|
+
}
|
|
11249
|
+
return null;
|
|
11250
|
+
}
|
|
11251
|
+
const isRecord = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
11252
|
+
/** 트리(경로 → 내용)에서 `sayren.json` 내용을 꺼낸다. 없으면 null, UTF-8이 아니면 JSON 오류로 보이게 빈 문자열 */
|
|
11253
|
+
function projectConfigOf(files) {
|
|
11254
|
+
const bytes = files.get(PROJECT_CONFIG_FILE);
|
|
11255
|
+
if (!bytes) return null;
|
|
11256
|
+
try {
|
|
11257
|
+
return new TextDecoder("utf-8", {
|
|
11258
|
+
fatal: true,
|
|
11259
|
+
ignoreBOM: false
|
|
11260
|
+
}).decode(bytes);
|
|
11261
|
+
} catch {
|
|
11262
|
+
return "";
|
|
11263
|
+
}
|
|
11264
|
+
}
|
|
11265
|
+
/** `sayren.json` 내용(없으면 null)을 읽는다. 모르는 키는 거절한다(오타가 조용히 기본값으로 빌드되지 않게) */
|
|
11266
|
+
function parseProjectConfig(content) {
|
|
11267
|
+
if (content === null) return {
|
|
11268
|
+
ok: true,
|
|
11269
|
+
config: LEGACY_PROJECT_CONFIG
|
|
11270
|
+
};
|
|
11271
|
+
const fail = (error) => ({
|
|
11272
|
+
ok: false,
|
|
11273
|
+
error: `${PROJECT_CONFIG_FILE}: ${error}`
|
|
11274
|
+
});
|
|
11275
|
+
let raw;
|
|
11276
|
+
try {
|
|
11277
|
+
raw = JSON.parse(content);
|
|
11278
|
+
} catch {
|
|
11279
|
+
return fail("JSON 형식이 아닙니다");
|
|
11280
|
+
}
|
|
11281
|
+
if (!isRecord(raw)) return fail("객체여야 합니다");
|
|
11282
|
+
const unknownKey = Object.keys(raw).find((k) => k !== "buildCommand" && k !== "output");
|
|
11283
|
+
if (unknownKey !== void 0) return fail(`모르는 키입니다: ${unknownKey}`);
|
|
11284
|
+
let buildCommand = null;
|
|
11285
|
+
if (raw.buildCommand !== void 0) {
|
|
11286
|
+
const cmd = raw.buildCommand;
|
|
11287
|
+
if (typeof cmd !== "string" || !cmd.trim()) return fail("buildCommand는 빈 문자열이 아니어야 합니다");
|
|
11288
|
+
if (cmd.length > MAX_COMMAND_LENGTH || cmd.includes("\0")) return fail(`buildCommand는 ${MAX_COMMAND_LENGTH}자 이하여야 합니다`);
|
|
11289
|
+
buildCommand = cmd;
|
|
11290
|
+
}
|
|
11291
|
+
const output = raw.output;
|
|
11292
|
+
if (!isRecord(output)) return fail("output.static이 필요합니다");
|
|
11293
|
+
const outKey = Object.keys(output).find((k) => k !== "static" && k !== "server");
|
|
11294
|
+
if (outKey !== void 0) return fail(`모르는 키입니다: output.${outKey}`);
|
|
11295
|
+
if (typeof output.static !== "string") return fail("output.static이 필요합니다");
|
|
11296
|
+
const staticProblem = outputPathProblem(output.static, true);
|
|
11297
|
+
if (staticProblem) return fail(`output.static — ${staticProblem}`);
|
|
11298
|
+
let server = null;
|
|
11299
|
+
if (output.server !== void 0) {
|
|
11300
|
+
if (typeof output.server !== "string") return fail("output.server는 문자열이어야 합니다");
|
|
11301
|
+
const serverProblem = outputPathProblem(output.server, false);
|
|
11302
|
+
if (serverProblem) return fail(`output.server — ${serverProblem}`);
|
|
11303
|
+
if (!/\.m?js$/.test(output.server)) return fail("output.server는 .js·.mjs 모듈이어야 합니다");
|
|
11304
|
+
server = output.server;
|
|
11305
|
+
}
|
|
11306
|
+
return {
|
|
11307
|
+
ok: true,
|
|
11308
|
+
config: {
|
|
11309
|
+
buildCommand,
|
|
11310
|
+
output: {
|
|
11311
|
+
static: output.static,
|
|
11312
|
+
server
|
|
11313
|
+
}
|
|
11314
|
+
}
|
|
11315
|
+
};
|
|
11316
|
+
}
|
|
11317
|
+
|
|
11089
11318
|
//#endregion
|
|
11090
11319
|
//#region ../mcp/src/rules.ts
|
|
11091
|
-
/** 규칙의 심각도 — 없으면 `
|
|
11320
|
+
/** 규칙의 심각도 — 없으면 `warning` */
|
|
11092
11321
|
function severityOf(rule) {
|
|
11093
|
-
return rule?.severity ?? "
|
|
11322
|
+
return rule?.severity ?? "warning";
|
|
11094
11323
|
}
|
|
11095
11324
|
/** 문서 사이트 — `doc` 경로를 절대 주소로 만들 때 쓴다 */
|
|
11096
11325
|
const DOCS_BASE_URL = "https://docs.sayren.io";
|
|
@@ -11131,12 +11360,13 @@ const RULES = [
|
|
|
11131
11360
|
}),
|
|
11132
11361
|
defineRule({
|
|
11133
11362
|
id: "no-module-token",
|
|
11363
|
+
severity: "error",
|
|
11134
11364
|
title: mcp_rule_no_module_token_title,
|
|
11135
11365
|
why: mcp_rule_no_module_token_why,
|
|
11136
11366
|
fix: mcp_rule_no_module_token_fix,
|
|
11137
11367
|
example: mcp_rule_no_module_token_example,
|
|
11138
11368
|
doc: "/guides/storefront-auth",
|
|
11139
|
-
templateFile: "src/lib/
|
|
11369
|
+
templateFile: "src/lib/api.server.ts"
|
|
11140
11370
|
}),
|
|
11141
11371
|
defineRule({
|
|
11142
11372
|
id: "loader-first",
|
|
@@ -11145,7 +11375,7 @@ const RULES = [
|
|
|
11145
11375
|
fix: mcp_rule_loader_first_fix,
|
|
11146
11376
|
example: mcp_rule_loader_first_example,
|
|
11147
11377
|
doc: "/guides/mcp-customize",
|
|
11148
|
-
templateFile: "src/routes/
|
|
11378
|
+
templateFile: "src/routes/index.tsx"
|
|
11149
11379
|
}),
|
|
11150
11380
|
defineRule({
|
|
11151
11381
|
id: "payment-browser-launch",
|
|
@@ -11153,8 +11383,7 @@ const RULES = [
|
|
|
11153
11383
|
why: mcp_rule_payment_browser_launch_why,
|
|
11154
11384
|
fix: mcp_rule_payment_browser_launch_fix,
|
|
11155
11385
|
example: mcp_rule_payment_browser_launch_example,
|
|
11156
|
-
doc: "/guides/checkout-flow"
|
|
11157
|
-
templateFile: "src/lib/payments.ts"
|
|
11386
|
+
doc: "/guides/checkout-flow"
|
|
11158
11387
|
}),
|
|
11159
11388
|
defineRule({
|
|
11160
11389
|
id: "payment-return-route",
|
|
@@ -11162,8 +11391,7 @@ const RULES = [
|
|
|
11162
11391
|
why: mcp_rule_payment_return_route_why,
|
|
11163
11392
|
fix: mcp_rule_payment_return_route_fix,
|
|
11164
11393
|
example: "useEffect(() => {\n void payments.result().then((result) => {\n if (result.status === \"COMPLETED\") navigate({ to: \"/checkout/complete\", search: { orderId: result.orderId } });\n });\n}, []);",
|
|
11165
|
-
doc: "/guides/checkout-flow"
|
|
11166
|
-
templateFile: "src/routes/checkout.return.tsx"
|
|
11394
|
+
doc: "/guides/checkout-flow"
|
|
11167
11395
|
}),
|
|
11168
11396
|
defineRule({
|
|
11169
11397
|
id: "payment-processing-status",
|
|
@@ -11171,8 +11399,7 @@ const RULES = [
|
|
|
11171
11399
|
why: mcp_rule_payment_processing_status_why,
|
|
11172
11400
|
fix: mcp_rule_payment_processing_status_fix,
|
|
11173
11401
|
example: mcp_rule_payment_processing_status_example,
|
|
11174
|
-
doc: "/guides/checkout-flow"
|
|
11175
|
-
templateFile: "src/routes/checkout.return.tsx"
|
|
11402
|
+
doc: "/guides/checkout-flow"
|
|
11176
11403
|
}),
|
|
11177
11404
|
defineRule({
|
|
11178
11405
|
id: "payment-amounts-final",
|
|
@@ -11180,8 +11407,7 @@ const RULES = [
|
|
|
11180
11407
|
why: mcp_rule_payment_amounts_final_why,
|
|
11181
11408
|
fix: mcp_rule_payment_amounts_final_fix,
|
|
11182
11409
|
example: mcp_rule_payment_amounts_final_example,
|
|
11183
|
-
doc: "/guides/shipping-fees"
|
|
11184
|
-
templateFile: "src/routes/checkout.index.tsx"
|
|
11410
|
+
doc: "/guides/shipping-fees"
|
|
11185
11411
|
}),
|
|
11186
11412
|
defineRule({
|
|
11187
11413
|
id: "payment-retry",
|
|
@@ -11189,8 +11415,7 @@ const RULES = [
|
|
|
11189
11415
|
why: mcp_rule_payment_retry_why,
|
|
11190
11416
|
fix: mcp_rule_payment_retry_fix,
|
|
11191
11417
|
example: mcp_rule_payment_retry_example,
|
|
11192
|
-
doc: "/guides/checkout-flow"
|
|
11193
|
-
templateFile: "src/routes/checkout.return.tsx"
|
|
11418
|
+
doc: "/guides/checkout-flow"
|
|
11194
11419
|
}),
|
|
11195
11420
|
defineRule({
|
|
11196
11421
|
id: "checkout-origin",
|
|
@@ -11198,8 +11423,7 @@ const RULES = [
|
|
|
11198
11423
|
why: mcp_rule_checkout_origin_why,
|
|
11199
11424
|
fix: mcp_rule_checkout_origin_fix,
|
|
11200
11425
|
example: "const returnUrl = new URL(PAYMENT_RETURN_PATH, new URL(getRequestUrl()).origin);\nawait api.checkout.startPayment(checkoutId, { option, returnUrl: returnUrl.toString() });",
|
|
11201
|
-
doc: "/guides/payment-settings"
|
|
11202
|
-
templateFile: "src/routes/checkout.index.tsx"
|
|
11426
|
+
doc: "/guides/payment-settings"
|
|
11203
11427
|
}),
|
|
11204
11428
|
defineRule({
|
|
11205
11429
|
id: "delivery-quote-preview",
|
|
@@ -11207,11 +11431,11 @@ const RULES = [
|
|
|
11207
11431
|
why: mcp_rule_delivery_quote_preview_why,
|
|
11208
11432
|
fix: mcp_rule_delivery_quote_preview_fix,
|
|
11209
11433
|
example: mcp_rule_delivery_quote_preview_example,
|
|
11210
|
-
doc: "/guides/shipping-fees"
|
|
11211
|
-
templateFile: "src/routes/checkout.index.tsx"
|
|
11434
|
+
doc: "/guides/shipping-fees"
|
|
11212
11435
|
}),
|
|
11213
11436
|
defineRule({
|
|
11214
11437
|
id: "buyer-token-cookie",
|
|
11438
|
+
severity: "error",
|
|
11215
11439
|
title: mcp_rule_buyer_token_cookie_title,
|
|
11216
11440
|
why: mcp_rule_buyer_token_cookie_why,
|
|
11217
11441
|
fix: mcp_rule_buyer_token_cookie_fix,
|
|
@@ -11225,8 +11449,7 @@ const RULES = [
|
|
|
11225
11449
|
why: mcp_rule_buyer_session_refresh_why,
|
|
11226
11450
|
fix: mcp_rule_buyer_session_refresh_fix,
|
|
11227
11451
|
example: mcp_rule_buyer_session_refresh_example,
|
|
11228
|
-
doc: "/guides/storefront-auth"
|
|
11229
|
-
templateFile: "src/lib/session.server.ts"
|
|
11452
|
+
doc: "/guides/storefront-auth"
|
|
11230
11453
|
}),
|
|
11231
11454
|
defineRule({
|
|
11232
11455
|
id: "buyer-reconsent",
|
|
@@ -11234,8 +11457,7 @@ const RULES = [
|
|
|
11234
11457
|
why: mcp_rule_buyer_reconsent_why,
|
|
11235
11458
|
fix: mcp_rule_buyer_reconsent_fix,
|
|
11236
11459
|
example: mcp_rule_buyer_reconsent_example,
|
|
11237
|
-
doc: "/guides/storefront-auth"
|
|
11238
|
-
templateFile: "src/routes/account.tsx"
|
|
11460
|
+
doc: "/guides/storefront-auth"
|
|
11239
11461
|
}),
|
|
11240
11462
|
defineRule({
|
|
11241
11463
|
id: "buyer-signup",
|
|
@@ -11243,8 +11465,7 @@ const RULES = [
|
|
|
11243
11465
|
why: mcp_rule_buyer_signup_why,
|
|
11244
11466
|
fix: mcp_rule_buyer_signup_fix,
|
|
11245
11467
|
example: mcp_rule_buyer_signup_example,
|
|
11246
|
-
doc: "/guides/storefront-auth"
|
|
11247
|
-
templateFile: "src/routes/signup.tsx"
|
|
11468
|
+
doc: "/guides/storefront-auth"
|
|
11248
11469
|
}),
|
|
11249
11470
|
defineRule({
|
|
11250
11471
|
id: "buyer-signout",
|
|
@@ -11252,8 +11473,7 @@ const RULES = [
|
|
|
11252
11473
|
why: mcp_rule_buyer_signout_why,
|
|
11253
11474
|
fix: mcp_rule_buyer_signout_fix,
|
|
11254
11475
|
example: "const logout = createServerFn({ method: \"POST\" }).handler(async () => {\n const accessToken = readToken();\n if (accessToken) await authFor().signOut(accessToken).catch(() => undefined);\n clearSessionCookie();\n});",
|
|
11255
|
-
doc: "/guides/storefront-auth"
|
|
11256
|
-
templateFile: "src/routes/account.tsx"
|
|
11476
|
+
doc: "/guides/storefront-auth"
|
|
11257
11477
|
}),
|
|
11258
11478
|
defineRule({
|
|
11259
11479
|
id: "analytics-start",
|
|
@@ -11275,12 +11495,12 @@ const RULES = [
|
|
|
11275
11495
|
}),
|
|
11276
11496
|
defineRule({
|
|
11277
11497
|
id: "custom-input-private",
|
|
11498
|
+
severity: "error",
|
|
11278
11499
|
title: mcp_rule_custom_input_private_title,
|
|
11279
11500
|
why: mcp_rule_custom_input_private_why,
|
|
11280
11501
|
fix: mcp_rule_custom_input_private_fix,
|
|
11281
11502
|
example: mcp_rule_custom_input_private_example,
|
|
11282
|
-
doc: "/guides/product-options"
|
|
11283
|
-
templateFile: "src/lib/direct-checkout.server.ts"
|
|
11503
|
+
doc: "/guides/product-options"
|
|
11284
11504
|
}),
|
|
11285
11505
|
defineRule({
|
|
11286
11506
|
id: "checkout-availability",
|
|
@@ -11288,9 +11508,7 @@ const RULES = [
|
|
|
11288
11508
|
why: mcp_rule_checkout_availability_why,
|
|
11289
11509
|
fix: mcp_rule_checkout_availability_fix,
|
|
11290
11510
|
example: mcp_rule_checkout_availability_example,
|
|
11291
|
-
doc: "/guides/storefront-sdk"
|
|
11292
|
-
templateFile: "src/lib/ordering.ts",
|
|
11293
|
-
severity: "warning"
|
|
11511
|
+
doc: "/guides/storefront-sdk"
|
|
11294
11512
|
}),
|
|
11295
11513
|
defineRule({
|
|
11296
11514
|
id: "product-link",
|
|
@@ -11298,9 +11516,7 @@ const RULES = [
|
|
|
11298
11516
|
why: mcp_rule_product_link_why,
|
|
11299
11517
|
fix: mcp_rule_product_link_fix,
|
|
11300
11518
|
example: mcp_rule_product_link_example,
|
|
11301
|
-
doc: "/guides/order-numbers"
|
|
11302
|
-
templateFile: "src/lib/product-path.ts",
|
|
11303
|
-
severity: "warning"
|
|
11519
|
+
doc: "/guides/order-numbers"
|
|
11304
11520
|
}),
|
|
11305
11521
|
defineRule({
|
|
11306
11522
|
id: "server-only-import",
|
|
@@ -11368,7 +11584,7 @@ function verifySources(files, options = {}) {
|
|
|
11368
11584
|
detail
|
|
11369
11585
|
});
|
|
11370
11586
|
};
|
|
11371
|
-
/** 프로젝트 전체를 보고 판정한 위반 — 고칠
|
|
11587
|
+
/** 프로젝트 전체를 보고 판정한 위반 — 고칠 방법은 규칙의 `fix`, 시작 코드에 있는 자리는 `templateFile`이 알려 준다 */
|
|
11372
11588
|
const inProject = (ruleId, detail) => {
|
|
11373
11589
|
findings.push({
|
|
11374
11590
|
ruleId,
|
|
@@ -12095,6 +12311,8 @@ function runLocalChecks(tree) {
|
|
|
12095
12311
|
const report$1 = verifyTree(map);
|
|
12096
12312
|
const blockingRules = report$1.results.filter((result) => result.status === "violation" && result.severity !== "warning");
|
|
12097
12313
|
const problems = [...limitProblems(tree)];
|
|
12314
|
+
const project = parseProjectConfig(projectConfigOf(map));
|
|
12315
|
+
if (!project.ok) problems.push(project.error);
|
|
12098
12316
|
const warnings = [];
|
|
12099
12317
|
const text = (path) => {
|
|
12100
12318
|
const bytes = map.get(path);
|
|
@@ -12201,8 +12419,20 @@ function withGuideSkill(files) {
|
|
|
12201
12419
|
const OUTPUT_TAIL_LINES = 40;
|
|
12202
12420
|
/** 모아 두는 출력 상한(문자) — 긴 빌드 로그를 다 쥐고 있지 않는다 */
|
|
12203
12421
|
const OUTPUT_KEEP_CHARS = 64 * 1024;
|
|
12204
|
-
/**
|
|
12422
|
+
/**
|
|
12423
|
+
* 빌드를 돌릴 수 있는 폴더인가 — 서버 빌드와 같은 순서로 명령을 고른다(`sayren.json` buildCommand → `build` 스크립트).
|
|
12424
|
+
* `package.json`이 있으면 설치된 의존성(`node_modules`)이 있어야 한다
|
|
12425
|
+
*/
|
|
12205
12426
|
function planLocalBuild(dir) {
|
|
12427
|
+
const hasManifest = existsSync(join(dir, "package.json"));
|
|
12428
|
+
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
|
+
}
|
|
12206
12436
|
let scripts;
|
|
12207
12437
|
try {
|
|
12208
12438
|
scripts = JSON.parse(readFileSync(join(dir, "package.json"), "utf8")).scripts;
|
|
@@ -12212,16 +12442,33 @@ function planLocalBuild(dir) {
|
|
|
12212
12442
|
const build = scripts && typeof scripts === "object" ? scripts.build : void 0;
|
|
12213
12443
|
if (typeof build !== "string" || !build.trim()) return { kind: "no-script" };
|
|
12214
12444
|
if (!existsSync(join(dir, "node_modules"))) return { kind: "no-deps" };
|
|
12215
|
-
return {
|
|
12445
|
+
return {
|
|
12446
|
+
kind: "run",
|
|
12447
|
+
command: null
|
|
12448
|
+
};
|
|
12216
12449
|
}
|
|
12217
|
-
/** `
|
|
12218
|
-
function
|
|
12450
|
+
/** `sayren.json`의 buildCommand. 파일이 없거나 형식이 틀리면 null(형식 오류는 로컬 검사가 먼저 막는다) */
|
|
12451
|
+
function buildCommandOf(dir) {
|
|
12452
|
+
let content;
|
|
12453
|
+
try {
|
|
12454
|
+
content = readFileSync(join(dir, PROJECT_CONFIG_FILE), "utf8");
|
|
12455
|
+
} catch {
|
|
12456
|
+
return null;
|
|
12457
|
+
}
|
|
12458
|
+
const parsed = parseProjectConfig(content);
|
|
12459
|
+
return parsed.ok ? parsed.config.buildCommand : null;
|
|
12460
|
+
}
|
|
12461
|
+
/**
|
|
12462
|
+
* 빌드를 돌린다 — `command`가 없으면 `npm run build`, 있으면 그 명령을 셸로(셀러가 자기 컴퓨터에서 적은 명령이다, `node_modules/.bin`을 PATH에 얹는다).
|
|
12463
|
+
* 출력은 모아 두고(실패할 때 꼬리를 보여 준다) 화면에 흘리지 않는다
|
|
12464
|
+
*/
|
|
12465
|
+
function runNpmBuild(dir, env, command = null) {
|
|
12219
12466
|
return new Promise((resolve$1, reject) => {
|
|
12220
12467
|
let output = "";
|
|
12221
12468
|
const keep = (chunk) => {
|
|
12222
12469
|
output = (output + chunk.toString("utf8")).slice(-OUTPUT_KEEP_CHARS);
|
|
12223
12470
|
};
|
|
12224
|
-
const child = spawn("npm", ["run", "build"], {
|
|
12471
|
+
const child = command === null ? spawn("npm", ["run", "build"], {
|
|
12225
12472
|
cwd: dir,
|
|
12226
12473
|
env,
|
|
12227
12474
|
stdio: [
|
|
@@ -12230,6 +12477,18 @@ function runNpmBuild(dir, env) {
|
|
|
12230
12477
|
"pipe"
|
|
12231
12478
|
],
|
|
12232
12479
|
shell: process.platform === "win32"
|
|
12480
|
+
}) : spawn(command, {
|
|
12481
|
+
cwd: dir,
|
|
12482
|
+
env: {
|
|
12483
|
+
...env,
|
|
12484
|
+
PATH: [join(dir, "node_modules", ".bin"), env.PATH].filter(Boolean).join(delimiter)
|
|
12485
|
+
},
|
|
12486
|
+
stdio: [
|
|
12487
|
+
"ignore",
|
|
12488
|
+
"pipe",
|
|
12489
|
+
"pipe"
|
|
12490
|
+
],
|
|
12491
|
+
shell: true
|
|
12233
12492
|
});
|
|
12234
12493
|
child.stdout?.on("data", keep);
|
|
12235
12494
|
child.stderr?.on("data", keep);
|
|
@@ -12602,14 +12861,96 @@ async function envPull(flags, io) {
|
|
|
12602
12861
|
store: state.storeId
|
|
12603
12862
|
}, dir, state, io);
|
|
12604
12863
|
const storeCode = await storeCodeOf(client, state.storeId);
|
|
12864
|
+
const lines = (await client.storefront.listVars().then((r) => r.vars.filter((v) => !v.secret)).catch((error) => {
|
|
12865
|
+
if (error instanceof ApiError && (error.status === 403 || error.status === 404)) return [];
|
|
12866
|
+
throw error;
|
|
12867
|
+
})).map((v) => ({
|
|
12868
|
+
key: v.key,
|
|
12869
|
+
line: dotenvValue(v.value ?? "")
|
|
12870
|
+
}));
|
|
12871
|
+
const skipped = lines.filter((l) => l.line === null).map((l) => l.key);
|
|
12605
12872
|
writeStoreEnv(dir, auth.apiOrigin, storeCode);
|
|
12873
|
+
updateEnvFile(dir, Object.fromEntries(lines.filter((l) => l.line !== null).map((l) => [l.key, l.line])));
|
|
12606
12874
|
ensureGitignore(dir);
|
|
12607
12875
|
io.out(cli_env_pulled({
|
|
12608
12876
|
file: ENV_FILE,
|
|
12609
12877
|
storeCode
|
|
12610
12878
|
}));
|
|
12879
|
+
if (lines.length > skipped.length) io.out(cli_env_pulled_vars({
|
|
12880
|
+
file: ENV_FILE,
|
|
12881
|
+
count: lines.length - skipped.length
|
|
12882
|
+
}));
|
|
12883
|
+
if (skipped.length > 0) io.err(cli_env_pull_skipped({ keys: skipped.join(", ") }));
|
|
12884
|
+
return 0;
|
|
12885
|
+
}
|
|
12886
|
+
/**
|
|
12887
|
+
* `.env` 한 줄의 값(dotenv + Vite `loadEnv`의 dotenv-expand 규칙) — `$`는 `\$`로 막아 확장되지 않게 한다. 안전한 문자만이면 그대로,
|
|
12888
|
+
* 작은따옴표·줄바꿈이 없으면 작은따옴표(안의 문자를 그대로 읽는다), 줄바꿈만 있고 큰따옴표가 없으면 큰따옴표(`\n`). 작은따옴표와 줄바꿈이
|
|
12889
|
+
* 같이 있거나 큰따옴표와 줄바꿈이 같이 있으면 같은 값으로 읽히게 쓸 수 없어 null이다(쓰지 않고 알린다)
|
|
12890
|
+
*/
|
|
12891
|
+
function dotenvValue(value) {
|
|
12892
|
+
const escaped = value.replaceAll("$", "\\$");
|
|
12893
|
+
if (/^[A-Za-z0-9_./:@+,=-]*$/.test(value)) return value;
|
|
12894
|
+
if (!value.includes("'") && !/[\r\n]/.test(value)) return `'${escaped}'`;
|
|
12895
|
+
if (!value.includes("\"") && !value.includes("\\")) return `"${escaped.replace(/\r?\n/g, "\\n")}"`;
|
|
12896
|
+
return null;
|
|
12897
|
+
}
|
|
12898
|
+
/** 연결된 폴더의 관리 API 클라이언트 — 셀러 env 명령(`env ls·add·rm`) */
|
|
12899
|
+
async function linkedClient(flags, io) {
|
|
12900
|
+
const dir = resolve(io.cwd, flags.dir ?? ".");
|
|
12901
|
+
migrateLegacy(dir, io.out);
|
|
12902
|
+
const state = readState(dir);
|
|
12903
|
+
if (!state) throw new CliError(cli_env_not_linked({ file: STATE_FILE }));
|
|
12904
|
+
return (await apiFor({
|
|
12905
|
+
...flags,
|
|
12906
|
+
store: state.storeId
|
|
12907
|
+
}, dir, state, io)).client;
|
|
12908
|
+
}
|
|
12909
|
+
/** `sayren env ls` — 셀러 env 목록(비밀 값은 값 없이) */
|
|
12910
|
+
async function envList(flags, io) {
|
|
12911
|
+
const { vars } = await (await linkedClient(flags, io)).storefront.listVars();
|
|
12912
|
+
if (vars.length === 0) {
|
|
12913
|
+
io.out(cli_env_empty());
|
|
12914
|
+
return 0;
|
|
12915
|
+
}
|
|
12916
|
+
for (const v of vars) io.out(`${v.key}=${v.secret ? cli_env_secret_mask() : v.value ?? ""}`);
|
|
12611
12917
|
return 0;
|
|
12612
12918
|
}
|
|
12919
|
+
/** `sayren env add <키> <값> [--secret]` — 넣거나 바꾼다. 에이전트 토큰이면 승인 대기다 */
|
|
12920
|
+
async function envAdd(flags, io) {
|
|
12921
|
+
if (!flags.key || flags.value === void 0) throw new CliError(cli_env_usage());
|
|
12922
|
+
const client = await linkedClient(flags, io);
|
|
12923
|
+
try {
|
|
12924
|
+
await client.storefront.putVar(flags.key, {
|
|
12925
|
+
value: flags.value,
|
|
12926
|
+
secret: flags.secret === true
|
|
12927
|
+
});
|
|
12928
|
+
} catch (error) {
|
|
12929
|
+
if (error instanceof ApprovalPendingError) return envApprovalPending(error, io);
|
|
12930
|
+
throw error;
|
|
12931
|
+
}
|
|
12932
|
+
io.out(cli_env_set({ key: flags.key }));
|
|
12933
|
+
return 0;
|
|
12934
|
+
}
|
|
12935
|
+
/** `sayren env rm <키>` */
|
|
12936
|
+
async function envRemove(flags, io) {
|
|
12937
|
+
if (!flags.key) throw new CliError(cli_env_usage());
|
|
12938
|
+
const client = await linkedClient(flags, io);
|
|
12939
|
+
try {
|
|
12940
|
+
await client.storefront.deleteVar(flags.key);
|
|
12941
|
+
} catch (error) {
|
|
12942
|
+
if (error instanceof ApprovalPendingError) return envApprovalPending(error, io);
|
|
12943
|
+
throw error;
|
|
12944
|
+
}
|
|
12945
|
+
io.out(cli_env_removed({ key: flags.key }));
|
|
12946
|
+
return 0;
|
|
12947
|
+
}
|
|
12948
|
+
function envApprovalPending(error, io) {
|
|
12949
|
+
io.out(cli_env_approval());
|
|
12950
|
+
if (error.approval.approvalUrl) io.out(cli_approval_url({ url: error.approval.approvalUrl }));
|
|
12951
|
+
else io.out(cli_approval_id({ id: error.approvalId }));
|
|
12952
|
+
return EXIT_APPROVAL_PENDING;
|
|
12953
|
+
}
|
|
12613
12954
|
async function verify(flags, io) {
|
|
12614
12955
|
const dir = resolve(io.cwd, flags.dir ?? ".");
|
|
12615
12956
|
const tree = collectTree(dir);
|
|
@@ -12775,7 +13116,7 @@ async function localBuild(dir, skip, io) {
|
|
|
12775
13116
|
const started = io.now?.() ?? Date.now();
|
|
12776
13117
|
let result;
|
|
12777
13118
|
try {
|
|
12778
|
-
result = await (io.runBuild ?? ((at) => runNpmBuild(at, io.env)))(dir);
|
|
13119
|
+
result = await (io.runBuild ?? ((at, command) => runNpmBuild(at, io.env, command)))(dir, plan.command);
|
|
12779
13120
|
} catch (error) {
|
|
12780
13121
|
throw new CliError(cli_deploy_build_local_spawn_failed({ message: error instanceof Error ? error.message : String(error) }));
|
|
12781
13122
|
}
|
|
@@ -13471,12 +13812,24 @@ async function main(argv, io) {
|
|
|
13471
13812
|
template: str(parsed.flags.template)
|
|
13472
13813
|
}, io);
|
|
13473
13814
|
case "link": return link(common, io);
|
|
13474
|
-
case "env":
|
|
13475
|
-
|
|
13476
|
-
return envPull({
|
|
13815
|
+
case "env": switch (parsed.positional[0]) {
|
|
13816
|
+
case "pull": return envPull({
|
|
13477
13817
|
...common,
|
|
13478
13818
|
dir: str(parsed.flags.dir) ?? parsed.positional[1]
|
|
13479
13819
|
}, io);
|
|
13820
|
+
case "ls": return envList(common, io);
|
|
13821
|
+
case "add": return envAdd({
|
|
13822
|
+
...common,
|
|
13823
|
+
key: parsed.positional[1],
|
|
13824
|
+
value: parsed.positional[2],
|
|
13825
|
+
secret: parsed.flags.secret === true
|
|
13826
|
+
}, io);
|
|
13827
|
+
case "rm": return envRemove({
|
|
13828
|
+
...common,
|
|
13829
|
+
key: parsed.positional[1]
|
|
13830
|
+
}, io);
|
|
13831
|
+
default: throw new CliError(cli_env_usage());
|
|
13832
|
+
}
|
|
13480
13833
|
case "init": return init();
|
|
13481
13834
|
case "pull": return pull({
|
|
13482
13835
|
...common,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sayren",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.17.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"imports": {
|
|
6
6
|
"#paraglide/*": "./src/paraglide/*"
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
},
|
|
18
18
|
"dependencies": {
|
|
19
19
|
"esbuild": "^0.28.2",
|
|
20
|
-
"@sayren/store-sdk": "^0.
|
|
20
|
+
"@sayren/store-sdk": "^0.59.0"
|
|
21
21
|
},
|
|
22
22
|
"devDependencies": {
|
|
23
23
|
"@biomejs/biome": "^2.5.14",
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"typescript": "^6.0.3",
|
|
30
30
|
"vitest": "^5.0.1",
|
|
31
31
|
"@sayren/app": "0.9.0",
|
|
32
|
-
"@sayren/mcp": "0.
|
|
32
|
+
"@sayren/mcp": "0.31.0"
|
|
33
33
|
},
|
|
34
34
|
"publishConfig": {
|
|
35
35
|
"access": "public"
|