sayren 0.4.0 → 0.5.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 +1882 -170
- package/package.json +4 -3
package/dist/index.mjs
CHANGED
|
@@ -2,11 +2,13 @@
|
|
|
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 { ApiError, ApprovalPendingError, STOREFRONT_BUILD_TERMINAL_STATUSES, STOREFRONT_SOURCE_BLOB_BATCH_MAX, createStoreClient, normalizeStorefrontPath } from "@sayren/store-sdk";
|
|
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";
|
|
6
6
|
import { chmodSync, closeSync, existsSync, lstatSync, mkdirSync, openSync, readFileSync, readdirSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
7
|
-
import { dirname, join, relative, resolve, sep } from "node:path";
|
|
8
|
-
import { createHash, randomBytes } from "node:crypto";
|
|
9
7
|
import { createServer } from "node:http";
|
|
8
|
+
import { dirname, extname, join, relative, resolve, sep } from "node:path";
|
|
9
|
+
import { pathToFileURL } from "node:url";
|
|
10
|
+
import { gzipSync } from "node:zlib";
|
|
11
|
+
import { createHash, randomBytes } from "node:crypto";
|
|
10
12
|
|
|
11
13
|
//#region ../i18n/src/paraglide/runtime.js
|
|
12
14
|
/** @type {any} */
|
|
@@ -592,185 +594,973 @@ function defineCustomClientStrategy(strategy$1, handler) {
|
|
|
592
594
|
* }} MessageMarkupTag
|
|
593
595
|
*/
|
|
594
596
|
/**
|
|
595
|
-
* Type-level schema for all markup tags in a message.
|
|
596
|
-
*
|
|
597
|
-
* @typedef {Record<string, MessageMarkupTag>} MessageMarkupSchema
|
|
597
|
+
* Type-level schema for all markup tags in a message.
|
|
598
|
+
*
|
|
599
|
+
* @typedef {Record<string, MessageMarkupTag>} MessageMarkupSchema
|
|
600
|
+
*/
|
|
601
|
+
/**
|
|
602
|
+
* Type-only metadata attached to compiled message functions.
|
|
603
|
+
*
|
|
604
|
+
* @template Inputs
|
|
605
|
+
* @template Options
|
|
606
|
+
* @template {MessageMarkupSchema} [Markup = MessageMarkupSchema]
|
|
607
|
+
* @typedef {{
|
|
608
|
+
* readonly __paraglide?: {
|
|
609
|
+
* inputs: Inputs;
|
|
610
|
+
* options: Options;
|
|
611
|
+
* markup: Markup;
|
|
612
|
+
* };
|
|
613
|
+
* }} MessageMetadata
|
|
614
|
+
*/
|
|
615
|
+
/**
|
|
616
|
+
* A compiled, framework-neutral message part.
|
|
617
|
+
*
|
|
618
|
+
* @typedef {{
|
|
619
|
+
* type: "text";
|
|
620
|
+
* value: string;
|
|
621
|
+
* } | {
|
|
622
|
+
* type: "markup-start";
|
|
623
|
+
* name: string;
|
|
624
|
+
* options: MessageMarkupOptions;
|
|
625
|
+
* attributes: MessageMarkupAttributes;
|
|
626
|
+
* } | {
|
|
627
|
+
* type: "markup-end";
|
|
628
|
+
* name: string;
|
|
629
|
+
* options: MessageMarkupOptions;
|
|
630
|
+
* attributes: MessageMarkupAttributes;
|
|
631
|
+
* } | {
|
|
632
|
+
* type: "markup-standalone";
|
|
633
|
+
* name: string;
|
|
634
|
+
* options: MessageMarkupOptions;
|
|
635
|
+
* attributes: MessageMarkupAttributes;
|
|
636
|
+
* }} MessagePart
|
|
637
|
+
*/
|
|
638
|
+
/**
|
|
639
|
+
* A message function is a message for a specific locale.
|
|
640
|
+
*
|
|
641
|
+
* @example
|
|
642
|
+
* m.hello({ name: 'world' })
|
|
643
|
+
*
|
|
644
|
+
* @typedef {(inputs?: Record<string, never>) => LocalizedString} MessageFunction
|
|
645
|
+
*/
|
|
646
|
+
/**
|
|
647
|
+
* A message bundle function that selects the message to be returned.
|
|
648
|
+
*
|
|
649
|
+
* Uses `getLocale()` under the hood to determine the locale with an option.
|
|
650
|
+
*
|
|
651
|
+
* @template {string} T
|
|
652
|
+
*
|
|
653
|
+
* @example
|
|
654
|
+
* * m.hello({ name: 'world' }, { locale: "en" })
|
|
655
|
+
*
|
|
656
|
+
* @typedef {(params: Record<string, never>, options: { locale: T }) => LocalizedString} MessageBundleFunction
|
|
657
|
+
*/
|
|
658
|
+
|
|
659
|
+
//#endregion
|
|
660
|
+
//#region ../i18n/src/locale.ts
|
|
661
|
+
/**
|
|
662
|
+
* 로케일 판정(이슈 #73) — 1순위 쿼리 `?lang=`, 2순위 `Accept-Language`(브라우저는 `navigator.languages`), 마지막 `ko`.
|
|
663
|
+
* 순서는 컴파일 옵션(`scripts/compile.mjs`의 strategy)이 정하고, 여기서는 Paraglide에 없는 쿼리 판정만 등록한다.
|
|
664
|
+
* api는 같은 순서를 `@nestjs/i18n` 리졸버(Query → AcceptLanguage)로 맞춘다.
|
|
665
|
+
*/
|
|
666
|
+
const LOCALE_QUERY_PARAM = "lang";
|
|
667
|
+
/** `?lang=ko-KR` → `ko`. 지원하지 않는 값·없으면 undefined(다음 판정으로 넘어간다) */
|
|
668
|
+
function localeFromSearch(search) {
|
|
669
|
+
const value = new URLSearchParams(search).get(LOCALE_QUERY_PARAM);
|
|
670
|
+
if (!value) return void 0;
|
|
671
|
+
return toLocale(value) ?? toLocale(value.split("-")[0]);
|
|
672
|
+
}
|
|
673
|
+
defineCustomServerStrategy("custom-query", { getLocale: (request) => request ? localeFromSearch(new URL(request.url).search) : void 0 });
|
|
674
|
+
/** 브라우저의 현재 주소 쿼리 — DOM 타입에 기대지 않는다(DOM lib 없는 소비자, 예: mcp-gateway도 이 소스를 타입 검사한다) */
|
|
675
|
+
function currentSearch() {
|
|
676
|
+
return globalThis.location?.search;
|
|
677
|
+
}
|
|
678
|
+
defineCustomClientStrategy("custom-query", {
|
|
679
|
+
getLocale: () => {
|
|
680
|
+
const search = currentSearch();
|
|
681
|
+
return search === void 0 ? void 0 : localeFromSearch(search);
|
|
682
|
+
},
|
|
683
|
+
setLocale: () => {}
|
|
684
|
+
});
|
|
685
|
+
|
|
686
|
+
//#endregion
|
|
687
|
+
//#region ../i18n/src/paraglide/messages/cli_access_token_expired.js
|
|
688
|
+
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
689
|
+
/** @typedef {{}} Cli_Access_Token_ExpiredInputs */
|
|
690
|
+
const ko_cli_access_token_expired = () => {
|
|
691
|
+
return `액세스 토큰이 만료됐습니다. 새 토큰을 만들거나 npx sayren login으로 다시 로그인하십시오`;
|
|
692
|
+
};
|
|
693
|
+
/**
|
|
694
|
+
|
|
695
|
+
* @param {Cli_Access_Token_ExpiredInputs} inputs
|
|
696
|
+
* @param {{ locale?: "ko" }} options
|
|
697
|
+
* @returns {LocalizedString}
|
|
698
|
+
*/
|
|
699
|
+
const cli_access_token_expired = ((inputs = {}, options = {}) => {
|
|
700
|
+
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
701
|
+
return ko_cli_access_token_expired(inputs);
|
|
702
|
+
});
|
|
703
|
+
|
|
704
|
+
//#endregion
|
|
705
|
+
//#region ../i18n/src/paraglide/messages/cli_account_mcp_written.js
|
|
706
|
+
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
707
|
+
/** @typedef {{ file: NonNullable<unknown>, mcpFile: NonNullable<unknown> }} Cli_Account_Mcp_WrittenInputs */
|
|
708
|
+
const ko_cli_account_mcp_written = (i) => {
|
|
709
|
+
return `${i?.file} 작성 — 원격 MCP 주소와 상점. ${i?.mcpFile}에는 토큰을 쓰지 않았습니다 — MCP 클라이언트가 연결할 때 Sayren 계정으로 로그인합니다`;
|
|
710
|
+
};
|
|
711
|
+
/**
|
|
712
|
+
|
|
713
|
+
* @param {Cli_Account_Mcp_WrittenInputs} inputs
|
|
714
|
+
* @param {{ locale?: "ko" }} options
|
|
715
|
+
* @returns {LocalizedString}
|
|
716
|
+
*/
|
|
717
|
+
const cli_account_mcp_written = ((inputs, options = {}) => {
|
|
718
|
+
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
719
|
+
return ko_cli_account_mcp_written(inputs);
|
|
720
|
+
});
|
|
721
|
+
|
|
722
|
+
//#endregion
|
|
723
|
+
//#region ../i18n/src/paraglide/messages/cli_accounts_origin_unknown.js
|
|
724
|
+
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
725
|
+
/** @typedef {{ origin: NonNullable<unknown> }} Cli_Accounts_Origin_UnknownInputs */
|
|
726
|
+
const ko_cli_accounts_origin_unknown = (i) => {
|
|
727
|
+
return `이 api 주소(${i?.origin})의 계정 서버를 모릅니다. --accounts-origin 또는 SAYREN_ACCOUNTS_ORIGIN으로 주십시오`;
|
|
728
|
+
};
|
|
729
|
+
/**
|
|
730
|
+
|
|
731
|
+
* @param {Cli_Accounts_Origin_UnknownInputs} inputs
|
|
732
|
+
* @param {{ locale?: "ko" }} options
|
|
733
|
+
* @returns {LocalizedString}
|
|
734
|
+
*/
|
|
735
|
+
const cli_accounts_origin_unknown = ((inputs, options = {}) => {
|
|
736
|
+
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
737
|
+
return ko_cli_accounts_origin_unknown(inputs);
|
|
738
|
+
});
|
|
739
|
+
|
|
740
|
+
//#endregion
|
|
741
|
+
//#region ../i18n/src/paraglide/messages/cli_accounts_unreachable.js
|
|
742
|
+
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
743
|
+
/** @typedef {{}} Cli_Accounts_UnreachableInputs */
|
|
744
|
+
const ko_cli_accounts_unreachable = () => {
|
|
745
|
+
return `계정 서버에 연결하지 못했습니다. 네트워크를 확인하십시오`;
|
|
746
|
+
};
|
|
747
|
+
/**
|
|
748
|
+
|
|
749
|
+
* @param {Cli_Accounts_UnreachableInputs} inputs
|
|
750
|
+
* @param {{ locale?: "ko" }} options
|
|
751
|
+
* @returns {LocalizedString}
|
|
752
|
+
*/
|
|
753
|
+
const cli_accounts_unreachable = ((inputs = {}, options = {}) => {
|
|
754
|
+
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
755
|
+
return ko_cli_accounts_unreachable(inputs);
|
|
756
|
+
});
|
|
757
|
+
|
|
758
|
+
//#endregion
|
|
759
|
+
//#region ../i18n/src/paraglide/messages/cli_app_bridge_missing.js
|
|
760
|
+
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
761
|
+
/** @typedef {{}} Cli_App_Bridge_MissingInputs */
|
|
762
|
+
const ko_cli_app_bridge_missing = () => {
|
|
763
|
+
return `앱 폴더에 @sayren/app이 설치돼 있지 않습니다(ui/_sayren/ui.js를 만들 수 없습니다). npm install을 먼저 하십시오`;
|
|
764
|
+
};
|
|
765
|
+
/**
|
|
766
|
+
|
|
767
|
+
* @param {Cli_App_Bridge_MissingInputs} inputs
|
|
768
|
+
* @param {{ locale?: "ko" }} options
|
|
769
|
+
* @returns {LocalizedString}
|
|
770
|
+
*/
|
|
771
|
+
const cli_app_bridge_missing = ((inputs = {}, options = {}) => {
|
|
772
|
+
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
773
|
+
return ko_cli_app_bridge_missing(inputs);
|
|
774
|
+
});
|
|
775
|
+
|
|
776
|
+
//#endregion
|
|
777
|
+
//#region ../i18n/src/paraglide/messages/cli_app_build_failed.js
|
|
778
|
+
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
779
|
+
/** @typedef {{ stage: NonNullable<unknown>, code: NonNullable<unknown>, tail: NonNullable<unknown> }} Cli_App_Build_FailedInputs */
|
|
780
|
+
const ko_cli_app_build_failed = (i) => {
|
|
781
|
+
return `빌드 실패: ${i?.stage} (${i?.code})
|
|
782
|
+
${i?.tail}`;
|
|
783
|
+
};
|
|
784
|
+
/**
|
|
785
|
+
|
|
786
|
+
* @param {Cli_App_Build_FailedInputs} inputs
|
|
787
|
+
* @param {{ locale?: "ko" }} options
|
|
788
|
+
* @returns {LocalizedString}
|
|
789
|
+
*/
|
|
790
|
+
const cli_app_build_failed = ((inputs, options = {}) => {
|
|
791
|
+
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
792
|
+
return ko_cli_app_build_failed(inputs);
|
|
793
|
+
});
|
|
794
|
+
|
|
795
|
+
//#endregion
|
|
796
|
+
//#region ../i18n/src/paraglide/messages/cli_app_bundle_failed.js
|
|
797
|
+
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
798
|
+
/** @typedef {{ tail: NonNullable<unknown> }} Cli_App_Bundle_FailedInputs */
|
|
799
|
+
const ko_cli_app_bundle_failed = (i) => {
|
|
800
|
+
return `서버 번들을 만들지 못했습니다:
|
|
801
|
+
${i?.tail}`;
|
|
802
|
+
};
|
|
803
|
+
/**
|
|
804
|
+
|
|
805
|
+
* @param {Cli_App_Bundle_FailedInputs} inputs
|
|
806
|
+
* @param {{ locale?: "ko" }} options
|
|
807
|
+
* @returns {LocalizedString}
|
|
808
|
+
*/
|
|
809
|
+
const cli_app_bundle_failed = ((inputs, options = {}) => {
|
|
810
|
+
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
811
|
+
return ko_cli_app_bundle_failed(inputs);
|
|
812
|
+
});
|
|
813
|
+
|
|
814
|
+
//#endregion
|
|
815
|
+
//#region ../i18n/src/paraglide/messages/cli_app_bundle_too_large.js
|
|
816
|
+
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
817
|
+
/** @typedef {{ size: NonNullable<unknown> }} Cli_App_Bundle_Too_LargeInputs */
|
|
818
|
+
const ko_cli_app_bundle_too_large = (i) => {
|
|
819
|
+
return `서버 번들이 ${i?.size}(gzip)입니다. 상한은 3MB입니다 (ARTIFACT_TOO_LARGE)`;
|
|
820
|
+
};
|
|
821
|
+
/**
|
|
822
|
+
|
|
823
|
+
* @param {Cli_App_Bundle_Too_LargeInputs} inputs
|
|
824
|
+
* @param {{ locale?: "ko" }} options
|
|
825
|
+
* @returns {LocalizedString}
|
|
826
|
+
*/
|
|
827
|
+
const cli_app_bundle_too_large = ((inputs, options = {}) => {
|
|
828
|
+
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
829
|
+
return ko_cli_app_bundle_too_large(inputs);
|
|
830
|
+
});
|
|
831
|
+
|
|
832
|
+
//#endregion
|
|
833
|
+
//#region ../i18n/src/paraglide/messages/cli_app_default_export_missing.js
|
|
834
|
+
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
835
|
+
/** @typedef {{}} Cli_App_Default_Export_MissingInputs */
|
|
836
|
+
const ko_cli_app_default_export_missing = () => {
|
|
837
|
+
return `src/server.ts가 defineApp(...)을 기본 내보내기(export default)해야 합니다`;
|
|
838
|
+
};
|
|
839
|
+
/**
|
|
840
|
+
|
|
841
|
+
* @param {Cli_App_Default_Export_MissingInputs} inputs
|
|
842
|
+
* @param {{ locale?: "ko" }} options
|
|
843
|
+
* @returns {LocalizedString}
|
|
844
|
+
*/
|
|
845
|
+
const cli_app_default_export_missing = ((inputs = {}, options = {}) => {
|
|
846
|
+
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
847
|
+
return ko_cli_app_default_export_missing(inputs);
|
|
848
|
+
});
|
|
849
|
+
|
|
850
|
+
//#endregion
|
|
851
|
+
//#region ../i18n/src/paraglide/messages/cli_app_deploy_done.js
|
|
852
|
+
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
853
|
+
/** @typedef {{ number: NonNullable<unknown> }} Cli_App_Deploy_DoneInputs */
|
|
854
|
+
const ko_cli_app_deploy_done = (i) => {
|
|
855
|
+
return `버전 ${i?.number}을 만들었습니다. 적용하려면: npx sayren app release ${i?.number}`;
|
|
856
|
+
};
|
|
857
|
+
/**
|
|
858
|
+
|
|
859
|
+
* @param {Cli_App_Deploy_DoneInputs} inputs
|
|
860
|
+
* @param {{ locale?: "ko" }} options
|
|
861
|
+
* @returns {LocalizedString}
|
|
862
|
+
*/
|
|
863
|
+
const cli_app_deploy_done = ((inputs, options = {}) => {
|
|
864
|
+
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
865
|
+
return ko_cli_app_deploy_done(inputs);
|
|
866
|
+
});
|
|
867
|
+
|
|
868
|
+
//#endregion
|
|
869
|
+
//#region ../i18n/src/paraglide/messages/cli_app_dev_error.js
|
|
870
|
+
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
871
|
+
/** @typedef {{ message: NonNullable<unknown> }} Cli_App_Dev_ErrorInputs */
|
|
872
|
+
const ko_cli_app_dev_error = (i) => {
|
|
873
|
+
return `로컬 실행 오류: ${i?.message}`;
|
|
874
|
+
};
|
|
875
|
+
/**
|
|
876
|
+
|
|
877
|
+
* @param {Cli_App_Dev_ErrorInputs} inputs
|
|
878
|
+
* @param {{ locale?: "ko" }} options
|
|
879
|
+
* @returns {LocalizedString}
|
|
880
|
+
*/
|
|
881
|
+
const cli_app_dev_error = ((inputs, options = {}) => {
|
|
882
|
+
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
883
|
+
return ko_cli_app_dev_error(inputs);
|
|
884
|
+
});
|
|
885
|
+
|
|
886
|
+
//#endregion
|
|
887
|
+
//#region ../i18n/src/paraglide/messages/cli_app_dev_listening.js
|
|
888
|
+
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
889
|
+
/** @typedef {{ port: NonNullable<unknown>, store: NonNullable<unknown>, events: NonNullable<unknown> }} Cli_App_Dev_ListeningInputs */
|
|
890
|
+
const ko_cli_app_dev_listening = (i) => {
|
|
891
|
+
return `앱 로컬 실행 중 http://localhost:${i?.port} — 상점 ${i?.store} · 이벤트 ${i?.events}
|
|
892
|
+
이벤트: npx sayren app trigger ORDER.PAID --order <orderId>
|
|
893
|
+
HTTP: http://localhost:${i?.port}/_http/<경로>
|
|
894
|
+
설정·비밀 값: .env의 SAYREN_APP_SETTING_*·SAYREN_APP_SECRET_*
|
|
895
|
+
종료는 Ctrl-C`;
|
|
896
|
+
};
|
|
897
|
+
/**
|
|
898
|
+
|
|
899
|
+
* @param {Cli_App_Dev_ListeningInputs} inputs
|
|
900
|
+
* @param {{ locale?: "ko" }} options
|
|
901
|
+
* @returns {LocalizedString}
|
|
902
|
+
*/
|
|
903
|
+
const cli_app_dev_listening = ((inputs, options = {}) => {
|
|
904
|
+
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
905
|
+
return ko_cli_app_dev_listening(inputs);
|
|
906
|
+
});
|
|
907
|
+
|
|
908
|
+
//#endregion
|
|
909
|
+
//#region ../i18n/src/paraglide/messages/cli_app_dev_store_created.js
|
|
910
|
+
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
911
|
+
/** @typedef {{ name: NonNullable<unknown>, storeId: NonNullable<unknown> }} Cli_App_Dev_Store_CreatedInputs */
|
|
912
|
+
const ko_cli_app_dev_store_created = (i) => {
|
|
913
|
+
return `개발 상점을 만들었습니다: ${i?.name} (${i?.storeId})`;
|
|
914
|
+
};
|
|
915
|
+
/**
|
|
916
|
+
|
|
917
|
+
* @param {Cli_App_Dev_Store_CreatedInputs} inputs
|
|
918
|
+
* @param {{ locale?: "ko" }} options
|
|
919
|
+
* @returns {LocalizedString}
|
|
920
|
+
*/
|
|
921
|
+
const cli_app_dev_store_created = ((inputs, options = {}) => {
|
|
922
|
+
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
923
|
+
return ko_cli_app_dev_store_created(inputs);
|
|
924
|
+
});
|
|
925
|
+
|
|
926
|
+
//#endregion
|
|
927
|
+
//#region ../i18n/src/paraglide/messages/cli_app_dev_store_empty.js
|
|
928
|
+
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
929
|
+
/** @typedef {{}} Cli_App_Dev_Store_EmptyInputs */
|
|
930
|
+
const ko_cli_app_dev_store_empty = () => {
|
|
931
|
+
return `개발 상점이 없습니다. npx sayren app dev-store create <이름>`;
|
|
932
|
+
};
|
|
933
|
+
/**
|
|
934
|
+
|
|
935
|
+
* @param {Cli_App_Dev_Store_EmptyInputs} inputs
|
|
936
|
+
* @param {{ locale?: "ko" }} options
|
|
937
|
+
* @returns {LocalizedString}
|
|
938
|
+
*/
|
|
939
|
+
const cli_app_dev_store_empty = ((inputs = {}, options = {}) => {
|
|
940
|
+
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
941
|
+
return ko_cli_app_dev_store_empty(inputs);
|
|
942
|
+
});
|
|
943
|
+
|
|
944
|
+
//#endregion
|
|
945
|
+
//#region ../i18n/src/paraglide/messages/cli_app_dev_store_name_required.js
|
|
946
|
+
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
947
|
+
/** @typedef {{}} Cli_App_Dev_Store_Name_RequiredInputs */
|
|
948
|
+
const ko_cli_app_dev_store_name_required = () => {
|
|
949
|
+
return `개발 상점 이름이 필요합니다(2~50자)`;
|
|
950
|
+
};
|
|
951
|
+
/**
|
|
952
|
+
|
|
953
|
+
* @param {Cli_App_Dev_Store_Name_RequiredInputs} inputs
|
|
954
|
+
* @param {{ locale?: "ko" }} options
|
|
955
|
+
* @returns {LocalizedString}
|
|
956
|
+
*/
|
|
957
|
+
const cli_app_dev_store_name_required = ((inputs = {}, options = {}) => {
|
|
958
|
+
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
959
|
+
return ko_cli_app_dev_store_name_required(inputs);
|
|
960
|
+
});
|
|
961
|
+
|
|
962
|
+
//#endregion
|
|
963
|
+
//#region ../i18n/src/paraglide/messages/cli_app_dir_exists.js
|
|
964
|
+
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
965
|
+
/** @typedef {{ dir: NonNullable<unknown> }} Cli_App_Dir_ExistsInputs */
|
|
966
|
+
const ko_cli_app_dir_exists = (i) => {
|
|
967
|
+
return `${i?.dir}에 이미 sayren.app.json이 있습니다`;
|
|
968
|
+
};
|
|
969
|
+
/**
|
|
970
|
+
|
|
971
|
+
* @param {Cli_App_Dir_ExistsInputs} inputs
|
|
972
|
+
* @param {{ locale?: "ko" }} options
|
|
973
|
+
* @returns {LocalizedString}
|
|
974
|
+
*/
|
|
975
|
+
const cli_app_dir_exists = ((inputs, options = {}) => {
|
|
976
|
+
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
977
|
+
return ko_cli_app_dir_exists(inputs);
|
|
978
|
+
});
|
|
979
|
+
|
|
980
|
+
//#endregion
|
|
981
|
+
//#region ../i18n/src/paraglide/messages/cli_app_entry_missing.js
|
|
982
|
+
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
983
|
+
/** @typedef {{ entry: NonNullable<unknown> }} Cli_App_Entry_MissingInputs */
|
|
984
|
+
const ko_cli_app_entry_missing = (i) => {
|
|
985
|
+
return `서버 진입 파일(${i?.entry})이 없습니다`;
|
|
986
|
+
};
|
|
987
|
+
/**
|
|
988
|
+
|
|
989
|
+
* @param {Cli_App_Entry_MissingInputs} inputs
|
|
990
|
+
* @param {{ locale?: "ko" }} options
|
|
991
|
+
* @returns {LocalizedString}
|
|
992
|
+
*/
|
|
993
|
+
const cli_app_entry_missing = ((inputs, options = {}) => {
|
|
994
|
+
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
995
|
+
return ko_cli_app_entry_missing(inputs);
|
|
996
|
+
});
|
|
997
|
+
|
|
998
|
+
//#endregion
|
|
999
|
+
//#region ../i18n/src/paraglide/messages/cli_app_handle_invalid.js
|
|
1000
|
+
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
1001
|
+
/** @typedef {{}} Cli_App_Handle_InvalidInputs */
|
|
1002
|
+
const ko_cli_app_handle_invalid = () => {
|
|
1003
|
+
return `handle은 소문자·숫자·하이픈 3~40자입니다. 예: npx sayren app init order-mail`;
|
|
1004
|
+
};
|
|
1005
|
+
/**
|
|
1006
|
+
|
|
1007
|
+
* @param {Cli_App_Handle_InvalidInputs} inputs
|
|
1008
|
+
* @param {{ locale?: "ko" }} options
|
|
1009
|
+
* @returns {LocalizedString}
|
|
1010
|
+
*/
|
|
1011
|
+
const cli_app_handle_invalid = ((inputs = {}, options = {}) => {
|
|
1012
|
+
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
1013
|
+
return ko_cli_app_handle_invalid(inputs);
|
|
1014
|
+
});
|
|
1015
|
+
|
|
1016
|
+
//#endregion
|
|
1017
|
+
//#region ../i18n/src/paraglide/messages/cli_app_handle_mismatch.js
|
|
1018
|
+
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
1019
|
+
/** @typedef {{ manifest: NonNullable<unknown>, registered: NonNullable<unknown> }} Cli_App_Handle_MismatchInputs */
|
|
1020
|
+
const ko_cli_app_handle_mismatch = (i) => {
|
|
1021
|
+
return `매니페스트 handle(${i?.manifest})이 등록된 앱(${i?.registered})과 다릅니다`;
|
|
1022
|
+
};
|
|
1023
|
+
/**
|
|
1024
|
+
|
|
1025
|
+
* @param {Cli_App_Handle_MismatchInputs} inputs
|
|
1026
|
+
* @param {{ locale?: "ko" }} options
|
|
1027
|
+
* @returns {LocalizedString}
|
|
1028
|
+
*/
|
|
1029
|
+
const cli_app_handle_mismatch = ((inputs, options = {}) => {
|
|
1030
|
+
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
1031
|
+
return ko_cli_app_handle_mismatch(inputs);
|
|
1032
|
+
});
|
|
1033
|
+
|
|
1034
|
+
//#endregion
|
|
1035
|
+
//#region ../i18n/src/paraglide/messages/cli_app_handler_missing.js
|
|
1036
|
+
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
1037
|
+
/** @typedef {{ events: NonNullable<unknown> }} Cli_App_Handler_MissingInputs */
|
|
1038
|
+
const ko_cli_app_handler_missing = (i) => {
|
|
1039
|
+
return `매니페스트 events에 있지만 이벤트 함수가 없습니다: ${i?.events} (HANDLER_MISSING)`;
|
|
1040
|
+
};
|
|
1041
|
+
/**
|
|
1042
|
+
|
|
1043
|
+
* @param {Cli_App_Handler_MissingInputs} inputs
|
|
1044
|
+
* @param {{ locale?: "ko" }} options
|
|
1045
|
+
* @returns {LocalizedString}
|
|
1046
|
+
*/
|
|
1047
|
+
const cli_app_handler_missing = ((inputs, options = {}) => {
|
|
1048
|
+
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
1049
|
+
return ko_cli_app_handler_missing(inputs);
|
|
1050
|
+
});
|
|
1051
|
+
|
|
1052
|
+
//#endregion
|
|
1053
|
+
//#region ../i18n/src/paraglide/messages/cli_app_handler_unlisted.js
|
|
1054
|
+
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
1055
|
+
/** @typedef {{ events: NonNullable<unknown> }} Cli_App_Handler_UnlistedInputs */
|
|
1056
|
+
const ko_cli_app_handler_unlisted = (i) => {
|
|
1057
|
+
return `이벤트 함수는 있지만 매니페스트 events에 없어 호출되지 않습니다: ${i?.events}`;
|
|
1058
|
+
};
|
|
1059
|
+
/**
|
|
1060
|
+
|
|
1061
|
+
* @param {Cli_App_Handler_UnlistedInputs} inputs
|
|
1062
|
+
* @param {{ locale?: "ko" }} options
|
|
1063
|
+
* @returns {LocalizedString}
|
|
1064
|
+
*/
|
|
1065
|
+
const cli_app_handler_unlisted = ((inputs, options = {}) => {
|
|
1066
|
+
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
1067
|
+
return ko_cli_app_handler_unlisted(inputs);
|
|
1068
|
+
});
|
|
1069
|
+
|
|
1070
|
+
//#endregion
|
|
1071
|
+
//#region ../i18n/src/paraglide/messages/cli_app_init_done.js
|
|
1072
|
+
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
1073
|
+
/** @typedef {{ dir: NonNullable<unknown>, handle: NonNullable<unknown>, appId: NonNullable<unknown> }} Cli_App_Init_DoneInputs */
|
|
1074
|
+
const ko_cli_app_init_done = (i) => {
|
|
1075
|
+
return `앱을 만들었습니다: ${i?.dir} (${i?.handle}, ${i?.appId})
|
|
1076
|
+
|
|
1077
|
+
cd ${i?.dir}
|
|
1078
|
+
npm install
|
|
1079
|
+
npx sayren app dev --store <storeId>
|
|
1080
|
+
npx sayren app deploy -m "첫 버전"`;
|
|
1081
|
+
};
|
|
1082
|
+
/**
|
|
1083
|
+
|
|
1084
|
+
* @param {Cli_App_Init_DoneInputs} inputs
|
|
1085
|
+
* @param {{ locale?: "ko" }} options
|
|
1086
|
+
* @returns {LocalizedString}
|
|
1087
|
+
*/
|
|
1088
|
+
const cli_app_init_done = ((inputs, options = {}) => {
|
|
1089
|
+
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
1090
|
+
return ko_cli_app_init_done(inputs);
|
|
1091
|
+
});
|
|
1092
|
+
|
|
1093
|
+
//#endregion
|
|
1094
|
+
//#region ../i18n/src/paraglide/messages/cli_app_key_invalid.js
|
|
1095
|
+
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
1096
|
+
/** @typedef {{}} Cli_App_Key_InvalidInputs */
|
|
1097
|
+
const ko_cli_app_key_invalid = () => {
|
|
1098
|
+
return `앱 배포 키 형식이 아닙니다(sak_…). developer.sayren.app에서 발행한 키를 쓰십시오`;
|
|
1099
|
+
};
|
|
1100
|
+
/**
|
|
1101
|
+
|
|
1102
|
+
* @param {Cli_App_Key_InvalidInputs} inputs
|
|
1103
|
+
* @param {{ locale?: "ko" }} options
|
|
1104
|
+
* @returns {LocalizedString}
|
|
1105
|
+
*/
|
|
1106
|
+
const cli_app_key_invalid = ((inputs = {}, options = {}) => {
|
|
1107
|
+
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
1108
|
+
return ko_cli_app_key_invalid(inputs);
|
|
1109
|
+
});
|
|
1110
|
+
|
|
1111
|
+
//#endregion
|
|
1112
|
+
//#region ../i18n/src/paraglide/messages/cli_app_logs_empty.js
|
|
1113
|
+
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
1114
|
+
/** @typedef {{}} Cli_App_Logs_EmptyInputs */
|
|
1115
|
+
const ko_cli_app_logs_empty = () => {
|
|
1116
|
+
return `실행 기록이 없습니다`;
|
|
1117
|
+
};
|
|
1118
|
+
/**
|
|
1119
|
+
|
|
1120
|
+
* @param {Cli_App_Logs_EmptyInputs} inputs
|
|
1121
|
+
* @param {{ locale?: "ko" }} options
|
|
1122
|
+
* @returns {LocalizedString}
|
|
1123
|
+
*/
|
|
1124
|
+
const cli_app_logs_empty = ((inputs = {}, options = {}) => {
|
|
1125
|
+
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
1126
|
+
return ko_cli_app_logs_empty(inputs);
|
|
1127
|
+
});
|
|
1128
|
+
|
|
1129
|
+
//#endregion
|
|
1130
|
+
//#region ../i18n/src/paraglide/messages/cli_app_manifest_invalid.js
|
|
1131
|
+
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
1132
|
+
/** @typedef {{ problems: NonNullable<unknown> }} Cli_App_Manifest_InvalidInputs */
|
|
1133
|
+
const ko_cli_app_manifest_invalid = (i) => {
|
|
1134
|
+
return `매니페스트가 올바르지 않습니다:
|
|
1135
|
+
${i?.problems}`;
|
|
1136
|
+
};
|
|
1137
|
+
/**
|
|
1138
|
+
|
|
1139
|
+
* @param {Cli_App_Manifest_InvalidInputs} inputs
|
|
1140
|
+
* @param {{ locale?: "ko" }} options
|
|
1141
|
+
* @returns {LocalizedString}
|
|
1142
|
+
*/
|
|
1143
|
+
const cli_app_manifest_invalid = ((inputs, options = {}) => {
|
|
1144
|
+
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
1145
|
+
return ko_cli_app_manifest_invalid(inputs);
|
|
1146
|
+
});
|
|
1147
|
+
|
|
1148
|
+
//#endregion
|
|
1149
|
+
//#region ../i18n/src/paraglide/messages/cli_app_manifest_missing.js
|
|
1150
|
+
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
1151
|
+
/** @typedef {{ file: NonNullable<unknown> }} Cli_App_Manifest_MissingInputs */
|
|
1152
|
+
const ko_cli_app_manifest_missing = (i) => {
|
|
1153
|
+
return `${i?.file}이 없습니다. 앱 폴더에서 실행하거나 --dir을 주십시오`;
|
|
1154
|
+
};
|
|
1155
|
+
/**
|
|
1156
|
+
|
|
1157
|
+
* @param {Cli_App_Manifest_MissingInputs} inputs
|
|
1158
|
+
* @param {{ locale?: "ko" }} options
|
|
1159
|
+
* @returns {LocalizedString}
|
|
1160
|
+
*/
|
|
1161
|
+
const cli_app_manifest_missing = ((inputs, options = {}) => {
|
|
1162
|
+
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
1163
|
+
return ko_cli_app_manifest_missing(inputs);
|
|
1164
|
+
});
|
|
1165
|
+
|
|
1166
|
+
//#endregion
|
|
1167
|
+
//#region ../i18n/src/paraglide/messages/cli_app_not_registered.js
|
|
1168
|
+
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
1169
|
+
/** @typedef {{ file: NonNullable<unknown> }} Cli_App_Not_RegisteredInputs */
|
|
1170
|
+
const ko_cli_app_not_registered = (i) => {
|
|
1171
|
+
return `등록된 앱이 아닙니다(${i?.file}은 있지만 .sayren/app.json이 없습니다). npx sayren app init <handle>로 만든 폴더에서 실행하십시오`;
|
|
1172
|
+
};
|
|
1173
|
+
/**
|
|
1174
|
+
|
|
1175
|
+
* @param {Cli_App_Not_RegisteredInputs} inputs
|
|
1176
|
+
* @param {{ locale?: "ko" }} options
|
|
1177
|
+
* @returns {LocalizedString}
|
|
1178
|
+
*/
|
|
1179
|
+
const cli_app_not_registered = ((inputs, options = {}) => {
|
|
1180
|
+
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
1181
|
+
return ko_cli_app_not_registered(inputs);
|
|
1182
|
+
});
|
|
1183
|
+
|
|
1184
|
+
//#endregion
|
|
1185
|
+
//#region ../i18n/src/paraglide/messages/cli_app_port_invalid.js
|
|
1186
|
+
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
1187
|
+
/** @typedef {{ value: NonNullable<unknown> }} Cli_App_Port_InvalidInputs */
|
|
1188
|
+
const ko_cli_app_port_invalid = (i) => {
|
|
1189
|
+
return `--port가 올바르지 않습니다: ${i?.value}`;
|
|
1190
|
+
};
|
|
1191
|
+
/**
|
|
1192
|
+
|
|
1193
|
+
* @param {Cli_App_Port_InvalidInputs} inputs
|
|
1194
|
+
* @param {{ locale?: "ko" }} options
|
|
1195
|
+
* @returns {LocalizedString}
|
|
1196
|
+
*/
|
|
1197
|
+
const cli_app_port_invalid = ((inputs, options = {}) => {
|
|
1198
|
+
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
1199
|
+
return ko_cli_app_port_invalid(inputs);
|
|
1200
|
+
});
|
|
1201
|
+
|
|
1202
|
+
//#endregion
|
|
1203
|
+
//#region ../i18n/src/paraglide/messages/cli_app_release_done.js
|
|
1204
|
+
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
1205
|
+
/** @typedef {{ number: NonNullable<unknown>, applied: NonNullable<unknown>, pending: NonNullable<unknown> }} Cli_App_Release_DoneInputs */
|
|
1206
|
+
const ko_cli_app_release_done = (i) => {
|
|
1207
|
+
return `버전 ${i?.number}이 현재 버전입니다 — 곧바로 적용 ${i?.applied}, 재동의 대기 ${i?.pending}`;
|
|
1208
|
+
};
|
|
1209
|
+
/**
|
|
1210
|
+
|
|
1211
|
+
* @param {Cli_App_Release_DoneInputs} inputs
|
|
1212
|
+
* @param {{ locale?: "ko" }} options
|
|
1213
|
+
* @returns {LocalizedString}
|
|
1214
|
+
*/
|
|
1215
|
+
const cli_app_release_done = ((inputs, options = {}) => {
|
|
1216
|
+
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
1217
|
+
return ko_cli_app_release_done(inputs);
|
|
1218
|
+
});
|
|
1219
|
+
|
|
1220
|
+
//#endregion
|
|
1221
|
+
//#region ../i18n/src/paraglide/messages/cli_app_secret_deleted.js
|
|
1222
|
+
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
1223
|
+
/** @typedef {{ name: NonNullable<unknown> }} Cli_App_Secret_DeletedInputs */
|
|
1224
|
+
const ko_cli_app_secret_deleted = (i) => {
|
|
1225
|
+
return `비밀 값 ${i?.name}을 지웠습니다`;
|
|
1226
|
+
};
|
|
1227
|
+
/**
|
|
1228
|
+
|
|
1229
|
+
* @param {Cli_App_Secret_DeletedInputs} inputs
|
|
1230
|
+
* @param {{ locale?: "ko" }} options
|
|
1231
|
+
* @returns {LocalizedString}
|
|
1232
|
+
*/
|
|
1233
|
+
const cli_app_secret_deleted = ((inputs, options = {}) => {
|
|
1234
|
+
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
1235
|
+
return ko_cli_app_secret_deleted(inputs);
|
|
1236
|
+
});
|
|
1237
|
+
|
|
1238
|
+
//#endregion
|
|
1239
|
+
//#region ../i18n/src/paraglide/messages/cli_app_secret_empty.js
|
|
1240
|
+
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
1241
|
+
/** @typedef {{}} Cli_App_Secret_EmptyInputs */
|
|
1242
|
+
const ko_cli_app_secret_empty = () => {
|
|
1243
|
+
return `비밀 값이 없습니다`;
|
|
1244
|
+
};
|
|
1245
|
+
/**
|
|
1246
|
+
|
|
1247
|
+
* @param {Cli_App_Secret_EmptyInputs} inputs
|
|
1248
|
+
* @param {{ locale?: "ko" }} options
|
|
1249
|
+
* @returns {LocalizedString}
|
|
1250
|
+
*/
|
|
1251
|
+
const cli_app_secret_empty = ((inputs = {}, options = {}) => {
|
|
1252
|
+
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
1253
|
+
return ko_cli_app_secret_empty(inputs);
|
|
1254
|
+
});
|
|
1255
|
+
|
|
1256
|
+
//#endregion
|
|
1257
|
+
//#region ../i18n/src/paraglide/messages/cli_app_secret_in_source.js
|
|
1258
|
+
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
1259
|
+
/** @typedef {{}} Cli_App_Secret_In_SourceInputs */
|
|
1260
|
+
const ko_cli_app_secret_in_source = () => {
|
|
1261
|
+
return `번들에 액세스 토큰·PG 키·개인 키·앱 배포 키 모양의 값이 들어 있습니다. 비밀 값은 npx sayren app secret put <이름>으로 넣으십시오 (SECRET_IN_SOURCE)`;
|
|
1262
|
+
};
|
|
1263
|
+
/**
|
|
1264
|
+
|
|
1265
|
+
* @param {Cli_App_Secret_In_SourceInputs} inputs
|
|
1266
|
+
* @param {{ locale?: "ko" }} options
|
|
1267
|
+
* @returns {LocalizedString}
|
|
1268
|
+
*/
|
|
1269
|
+
const cli_app_secret_in_source = ((inputs = {}, options = {}) => {
|
|
1270
|
+
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
1271
|
+
return ko_cli_app_secret_in_source(inputs);
|
|
1272
|
+
});
|
|
1273
|
+
|
|
1274
|
+
//#endregion
|
|
1275
|
+
//#region ../i18n/src/paraglide/messages/cli_app_secret_name_required.js
|
|
1276
|
+
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
1277
|
+
/** @typedef {{}} Cli_App_Secret_Name_RequiredInputs */
|
|
1278
|
+
const ko_cli_app_secret_name_required = () => {
|
|
1279
|
+
return `비밀 값 이름이 필요합니다. 예: npx sayren app secret put RESEND_API_KEY`;
|
|
1280
|
+
};
|
|
1281
|
+
/**
|
|
1282
|
+
|
|
1283
|
+
* @param {Cli_App_Secret_Name_RequiredInputs} inputs
|
|
1284
|
+
* @param {{ locale?: "ko" }} options
|
|
1285
|
+
* @returns {LocalizedString}
|
|
1286
|
+
*/
|
|
1287
|
+
const cli_app_secret_name_required = ((inputs = {}, options = {}) => {
|
|
1288
|
+
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
1289
|
+
return ko_cli_app_secret_name_required(inputs);
|
|
1290
|
+
});
|
|
1291
|
+
|
|
1292
|
+
//#endregion
|
|
1293
|
+
//#region ../i18n/src/paraglide/messages/cli_app_secret_saved.js
|
|
1294
|
+
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
1295
|
+
/** @typedef {{ name: NonNullable<unknown> }} Cli_App_Secret_SavedInputs */
|
|
1296
|
+
const ko_cli_app_secret_saved = (i) => {
|
|
1297
|
+
return `비밀 값 ${i?.name}을 저장했습니다`;
|
|
1298
|
+
};
|
|
1299
|
+
/**
|
|
1300
|
+
|
|
1301
|
+
* @param {Cli_App_Secret_SavedInputs} inputs
|
|
1302
|
+
* @param {{ locale?: "ko" }} options
|
|
1303
|
+
* @returns {LocalizedString}
|
|
1304
|
+
*/
|
|
1305
|
+
const cli_app_secret_saved = ((inputs, options = {}) => {
|
|
1306
|
+
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
1307
|
+
return ko_cli_app_secret_saved(inputs);
|
|
1308
|
+
});
|
|
1309
|
+
|
|
1310
|
+
//#endregion
|
|
1311
|
+
//#region ../i18n/src/paraglide/messages/cli_app_secret_value_required.js
|
|
1312
|
+
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
1313
|
+
/** @typedef {{ name: NonNullable<unknown> }} Cli_App_Secret_Value_RequiredInputs */
|
|
1314
|
+
const ko_cli_app_secret_value_required = (i) => {
|
|
1315
|
+
return `값을 표준 입력으로 주십시오. 예: printf '%s' "$VALUE" | npx sayren app secret put ${i?.name}`;
|
|
1316
|
+
};
|
|
1317
|
+
/**
|
|
1318
|
+
|
|
1319
|
+
* @param {Cli_App_Secret_Value_RequiredInputs} inputs
|
|
1320
|
+
* @param {{ locale?: "ko" }} options
|
|
1321
|
+
* @returns {LocalizedString}
|
|
1322
|
+
*/
|
|
1323
|
+
const cli_app_secret_value_required = ((inputs, options = {}) => {
|
|
1324
|
+
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
1325
|
+
return ko_cli_app_secret_value_required(inputs);
|
|
1326
|
+
});
|
|
1327
|
+
|
|
1328
|
+
//#endregion
|
|
1329
|
+
//#region ../i18n/src/paraglide/messages/cli_app_since_invalid.js
|
|
1330
|
+
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
1331
|
+
/** @typedef {{ value: NonNullable<unknown> }} Cli_App_Since_InvalidInputs */
|
|
1332
|
+
const ko_cli_app_since_invalid = (i) => {
|
|
1333
|
+
return `--since 형식이 아닙니다: ${i?.value} (예: 30m, 1h, 2d)`;
|
|
1334
|
+
};
|
|
1335
|
+
/**
|
|
1336
|
+
|
|
1337
|
+
* @param {Cli_App_Since_InvalidInputs} inputs
|
|
1338
|
+
* @param {{ locale?: "ko" }} options
|
|
1339
|
+
* @returns {LocalizedString}
|
|
1340
|
+
*/
|
|
1341
|
+
const cli_app_since_invalid = ((inputs, options = {}) => {
|
|
1342
|
+
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
1343
|
+
return ko_cli_app_since_invalid(inputs);
|
|
1344
|
+
});
|
|
1345
|
+
|
|
1346
|
+
//#endregion
|
|
1347
|
+
//#region ../i18n/src/paraglide/messages/cli_app_state_invalid.js
|
|
1348
|
+
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
1349
|
+
/** @typedef {{ file: NonNullable<unknown> }} Cli_App_State_InvalidInputs */
|
|
1350
|
+
const ko_cli_app_state_invalid = (i) => {
|
|
1351
|
+
return `${i?.file}을 읽을 수 없습니다. 파일을 지우고 npx sayren app init을 다시 실행하십시오`;
|
|
1352
|
+
};
|
|
1353
|
+
/**
|
|
1354
|
+
|
|
1355
|
+
* @param {Cli_App_State_InvalidInputs} inputs
|
|
1356
|
+
* @param {{ locale?: "ko" }} options
|
|
1357
|
+
* @returns {LocalizedString}
|
|
1358
|
+
*/
|
|
1359
|
+
const cli_app_state_invalid = ((inputs, options = {}) => {
|
|
1360
|
+
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
1361
|
+
return ko_cli_app_state_invalid(inputs);
|
|
1362
|
+
});
|
|
1363
|
+
|
|
1364
|
+
//#endregion
|
|
1365
|
+
//#region ../i18n/src/paraglide/messages/cli_app_status.js
|
|
1366
|
+
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
1367
|
+
/** @typedef {{ name: NonNullable<unknown>, handle: NonNullable<unknown>, appId: NonNullable<unknown>, status: NonNullable<unknown>, current: NonNullable<unknown>, active: NonNullable<unknown>, pending: NonNullable<unknown> }} Cli_App_StatusInputs */
|
|
1368
|
+
const ko_cli_app_status = (i) => {
|
|
1369
|
+
return `${i?.name} (${i?.handle}, ${i?.appId})
|
|
1370
|
+
상태 ${i?.status} · 현재 버전 ${i?.current} · 설치 ${i?.active} · 재동의 대기 ${i?.pending}
|
|
1371
|
+
최근 버전:`;
|
|
1372
|
+
};
|
|
1373
|
+
/**
|
|
1374
|
+
|
|
1375
|
+
* @param {Cli_App_StatusInputs} inputs
|
|
1376
|
+
* @param {{ locale?: "ko" }} options
|
|
1377
|
+
* @returns {LocalizedString}
|
|
598
1378
|
*/
|
|
1379
|
+
const cli_app_status = ((inputs, options = {}) => {
|
|
1380
|
+
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
1381
|
+
return ko_cli_app_status(inputs);
|
|
1382
|
+
});
|
|
1383
|
+
|
|
1384
|
+
//#endregion
|
|
1385
|
+
//#region ../i18n/src/paraglide/messages/cli_app_subcommand_unknown.js
|
|
1386
|
+
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
1387
|
+
/** @typedef {{ command: NonNullable<unknown> }} Cli_App_Subcommand_UnknownInputs */
|
|
1388
|
+
const ko_cli_app_subcommand_unknown = (i) => {
|
|
1389
|
+
return `알 수 없는 앱 명령: ${i?.command}
|
|
1390
|
+
|
|
1391
|
+
사용법: npx sayren app <init|dev|trigger|verify|deploy|release|status|logs|secret|dev-store>`;
|
|
1392
|
+
};
|
|
599
1393
|
/**
|
|
600
|
-
|
|
601
|
-
*
|
|
602
|
-
* @
|
|
603
|
-
* @
|
|
604
|
-
* @template {MessageMarkupSchema} [Markup = MessageMarkupSchema]
|
|
605
|
-
* @typedef {{
|
|
606
|
-
* readonly __paraglide?: {
|
|
607
|
-
* inputs: Inputs;
|
|
608
|
-
* options: Options;
|
|
609
|
-
* markup: Markup;
|
|
610
|
-
* };
|
|
611
|
-
* }} MessageMetadata
|
|
1394
|
+
|
|
1395
|
+
* @param {Cli_App_Subcommand_UnknownInputs} inputs
|
|
1396
|
+
* @param {{ locale?: "ko" }} options
|
|
1397
|
+
* @returns {LocalizedString}
|
|
612
1398
|
*/
|
|
1399
|
+
const cli_app_subcommand_unknown = ((inputs, options = {}) => {
|
|
1400
|
+
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
1401
|
+
return ko_cli_app_subcommand_unknown(inputs);
|
|
1402
|
+
});
|
|
1403
|
+
|
|
1404
|
+
//#endregion
|
|
1405
|
+
//#region ../i18n/src/paraglide/messages/cli_app_trigger_data_invalid.js
|
|
1406
|
+
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
1407
|
+
/** @typedef {{}} Cli_App_Trigger_Data_InvalidInputs */
|
|
1408
|
+
const ko_cli_app_trigger_data_invalid = () => {
|
|
1409
|
+
return `--data가 JSON이 아닙니다`;
|
|
1410
|
+
};
|
|
613
1411
|
/**
|
|
614
|
-
|
|
615
|
-
*
|
|
616
|
-
* @
|
|
617
|
-
*
|
|
618
|
-
* value: string;
|
|
619
|
-
* } | {
|
|
620
|
-
* type: "markup-start";
|
|
621
|
-
* name: string;
|
|
622
|
-
* options: MessageMarkupOptions;
|
|
623
|
-
* attributes: MessageMarkupAttributes;
|
|
624
|
-
* } | {
|
|
625
|
-
* type: "markup-end";
|
|
626
|
-
* name: string;
|
|
627
|
-
* options: MessageMarkupOptions;
|
|
628
|
-
* attributes: MessageMarkupAttributes;
|
|
629
|
-
* } | {
|
|
630
|
-
* type: "markup-standalone";
|
|
631
|
-
* name: string;
|
|
632
|
-
* options: MessageMarkupOptions;
|
|
633
|
-
* attributes: MessageMarkupAttributes;
|
|
634
|
-
* }} MessagePart
|
|
1412
|
+
|
|
1413
|
+
* @param {Cli_App_Trigger_Data_InvalidInputs} inputs
|
|
1414
|
+
* @param {{ locale?: "ko" }} options
|
|
1415
|
+
* @returns {LocalizedString}
|
|
635
1416
|
*/
|
|
1417
|
+
const cli_app_trigger_data_invalid = ((inputs = {}, options = {}) => {
|
|
1418
|
+
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
1419
|
+
return ko_cli_app_trigger_data_invalid(inputs);
|
|
1420
|
+
});
|
|
1421
|
+
|
|
1422
|
+
//#endregion
|
|
1423
|
+
//#region ../i18n/src/paraglide/messages/cli_app_trigger_failed.js
|
|
1424
|
+
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
1425
|
+
/** @typedef {{ status: NonNullable<unknown> }} Cli_App_Trigger_FailedInputs */
|
|
1426
|
+
const ko_cli_app_trigger_failed = (i) => {
|
|
1427
|
+
return `로컬 실행에 보내지 못했습니다(HTTP ${i?.status}). npx sayren app dev가 떠 있는지 확인하십시오`;
|
|
1428
|
+
};
|
|
636
1429
|
/**
|
|
637
|
-
|
|
638
|
-
*
|
|
639
|
-
* @
|
|
640
|
-
*
|
|
641
|
-
*
|
|
642
|
-
* @typedef {(inputs?: Record<string, never>) => LocalizedString} MessageFunction
|
|
1430
|
+
|
|
1431
|
+
* @param {Cli_App_Trigger_FailedInputs} inputs
|
|
1432
|
+
* @param {{ locale?: "ko" }} options
|
|
1433
|
+
* @returns {LocalizedString}
|
|
643
1434
|
*/
|
|
1435
|
+
const cli_app_trigger_failed = ((inputs, options = {}) => {
|
|
1436
|
+
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
1437
|
+
return ko_cli_app_trigger_failed(inputs);
|
|
1438
|
+
});
|
|
1439
|
+
|
|
1440
|
+
//#endregion
|
|
1441
|
+
//#region ../i18n/src/paraglide/messages/cli_app_trigger_type_required.js
|
|
1442
|
+
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
1443
|
+
/** @typedef {{}} Cli_App_Trigger_Type_RequiredInputs */
|
|
1444
|
+
const ko_cli_app_trigger_type_required = () => {
|
|
1445
|
+
return `이벤트 종류가 필요합니다. 예: npx sayren app trigger ORDER.PAID --order ord_…`;
|
|
1446
|
+
};
|
|
644
1447
|
/**
|
|
645
|
-
|
|
646
|
-
*
|
|
647
|
-
*
|
|
648
|
-
*
|
|
649
|
-
* @template {string} T
|
|
650
|
-
*
|
|
651
|
-
* @example
|
|
652
|
-
* * m.hello({ name: 'world' }, { locale: "en" })
|
|
653
|
-
*
|
|
654
|
-
* @typedef {(params: Record<string, never>, options: { locale: T }) => LocalizedString} MessageBundleFunction
|
|
1448
|
+
|
|
1449
|
+
* @param {Cli_App_Trigger_Type_RequiredInputs} inputs
|
|
1450
|
+
* @param {{ locale?: "ko" }} options
|
|
1451
|
+
* @returns {LocalizedString}
|
|
655
1452
|
*/
|
|
1453
|
+
const cli_app_trigger_type_required = ((inputs = {}, options = {}) => {
|
|
1454
|
+
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
1455
|
+
return ko_cli_app_trigger_type_required(inputs);
|
|
1456
|
+
});
|
|
656
1457
|
|
|
657
1458
|
//#endregion
|
|
658
|
-
//#region ../i18n/src/
|
|
1459
|
+
//#region ../i18n/src/paraglide/messages/cli_app_ui_index_missing.js
|
|
1460
|
+
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
1461
|
+
/** @typedef {{ dir: NonNullable<unknown> }} Cli_App_Ui_Index_MissingInputs */
|
|
1462
|
+
const ko_cli_app_ui_index_missing = (i) => {
|
|
1463
|
+
return `매니페스트에 ui.adminPage가 있지만 ${i?.dir}/index.html이 없습니다`;
|
|
1464
|
+
};
|
|
659
1465
|
/**
|
|
660
|
-
|
|
661
|
-
*
|
|
662
|
-
*
|
|
1466
|
+
|
|
1467
|
+
* @param {Cli_App_Ui_Index_MissingInputs} inputs
|
|
1468
|
+
* @param {{ locale?: "ko" }} options
|
|
1469
|
+
* @returns {LocalizedString}
|
|
663
1470
|
*/
|
|
664
|
-
const
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
const value = new URLSearchParams(search).get(LOCALE_QUERY_PARAM);
|
|
668
|
-
if (!value) return void 0;
|
|
669
|
-
return toLocale(value) ?? toLocale(value.split("-")[0]);
|
|
670
|
-
}
|
|
671
|
-
defineCustomServerStrategy("custom-query", { getLocale: (request) => request ? localeFromSearch(new URL(request.url).search) : void 0 });
|
|
672
|
-
/** 브라우저의 현재 주소 쿼리 — DOM 타입에 기대지 않는다(DOM lib 없는 소비자, 예: mcp-gateway도 이 소스를 타입 검사한다) */
|
|
673
|
-
function currentSearch() {
|
|
674
|
-
return globalThis.location?.search;
|
|
675
|
-
}
|
|
676
|
-
defineCustomClientStrategy("custom-query", {
|
|
677
|
-
getLocale: () => {
|
|
678
|
-
const search = currentSearch();
|
|
679
|
-
return search === void 0 ? void 0 : localeFromSearch(search);
|
|
680
|
-
},
|
|
681
|
-
setLocale: () => {}
|
|
1471
|
+
const cli_app_ui_index_missing = ((inputs, options = {}) => {
|
|
1472
|
+
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
1473
|
+
return ko_cli_app_ui_index_missing(inputs);
|
|
682
1474
|
});
|
|
683
1475
|
|
|
684
1476
|
//#endregion
|
|
685
|
-
//#region ../i18n/src/
|
|
1477
|
+
//#region ../i18n/src/paraglide/messages/cli_app_ui_too_large.js
|
|
1478
|
+
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
1479
|
+
/** @typedef {{ limit: NonNullable<unknown> }} Cli_App_Ui_Too_LargeInputs */
|
|
1480
|
+
const ko_cli_app_ui_too_large = (i) => {
|
|
1481
|
+
return `앱 화면 파일이 상한(${i?.limit})을 넘습니다 (ARTIFACT_TOO_LARGE)`;
|
|
1482
|
+
};
|
|
686
1483
|
/**
|
|
687
|
-
|
|
688
|
-
*
|
|
689
|
-
*
|
|
690
|
-
*
|
|
691
|
-
* ```ts
|
|
692
|
-
* export const ROLE_LABEL = lazyMessages<StoreRole>({ OWNER: m.store_shell_role_owner, ... });
|
|
693
|
-
* ```
|
|
1484
|
+
|
|
1485
|
+
* @param {Cli_App_Ui_Too_LargeInputs} inputs
|
|
1486
|
+
* @param {{ locale?: "ko" }} options
|
|
1487
|
+
* @returns {LocalizedString}
|
|
694
1488
|
*/
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
get: () => table[key]()
|
|
700
|
-
});
|
|
701
|
-
return Object.freeze(labels);
|
|
702
|
-
}
|
|
1489
|
+
const cli_app_ui_too_large = ((inputs, options = {}) => {
|
|
1490
|
+
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
1491
|
+
return ko_cli_app_ui_too_large(inputs);
|
|
1492
|
+
});
|
|
703
1493
|
|
|
704
1494
|
//#endregion
|
|
705
|
-
//#region ../i18n/src/paraglide/messages/
|
|
1495
|
+
//#region ../i18n/src/paraglide/messages/cli_app_uploading.js
|
|
706
1496
|
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
707
|
-
/** @typedef {{}}
|
|
708
|
-
const
|
|
709
|
-
return
|
|
1497
|
+
/** @typedef {{ size: NonNullable<unknown>, ui: NonNullable<unknown> }} Cli_App_UploadingInputs */
|
|
1498
|
+
const ko_cli_app_uploading = (i) => {
|
|
1499
|
+
return `올리는 중 — 서버 번들 ${i?.size}(gzip), 화면 파일 ${i?.ui}개`;
|
|
710
1500
|
};
|
|
711
1501
|
/**
|
|
712
1502
|
|
|
713
|
-
* @param {
|
|
1503
|
+
* @param {Cli_App_UploadingInputs} inputs
|
|
714
1504
|
* @param {{ locale?: "ko" }} options
|
|
715
1505
|
* @returns {LocalizedString}
|
|
716
1506
|
*/
|
|
717
|
-
const
|
|
1507
|
+
const cli_app_uploading = ((inputs, options = {}) => {
|
|
718
1508
|
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
719
|
-
return
|
|
1509
|
+
return ko_cli_app_uploading(inputs);
|
|
720
1510
|
});
|
|
721
1511
|
|
|
722
1512
|
//#endregion
|
|
723
|
-
//#region ../i18n/src/paraglide/messages/
|
|
1513
|
+
//#region ../i18n/src/paraglide/messages/cli_app_verify_ok.js
|
|
724
1514
|
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
725
|
-
/** @typedef {{
|
|
726
|
-
const
|
|
727
|
-
return
|
|
1515
|
+
/** @typedef {{ events: NonNullable<unknown>, http: NonNullable<unknown>, size: NonNullable<unknown>, ui: NonNullable<unknown> }} Cli_App_Verify_OkInputs */
|
|
1516
|
+
const ko_cli_app_verify_ok = (i) => {
|
|
1517
|
+
return `검사 통과 — 이벤트 ${i?.events} · HTTP ${i?.http} · 서버 번들 ${i?.size}(gzip) · 화면 파일 ${i?.ui}개`;
|
|
728
1518
|
};
|
|
729
1519
|
/**
|
|
730
1520
|
|
|
731
|
-
* @param {
|
|
1521
|
+
* @param {Cli_App_Verify_OkInputs} inputs
|
|
732
1522
|
* @param {{ locale?: "ko" }} options
|
|
733
1523
|
* @returns {LocalizedString}
|
|
734
1524
|
*/
|
|
735
|
-
const
|
|
1525
|
+
const cli_app_verify_ok = ((inputs, options = {}) => {
|
|
736
1526
|
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
737
|
-
return
|
|
1527
|
+
return ko_cli_app_verify_ok(inputs);
|
|
738
1528
|
});
|
|
739
1529
|
|
|
740
1530
|
//#endregion
|
|
741
|
-
//#region ../i18n/src/paraglide/messages/
|
|
1531
|
+
//#region ../i18n/src/paraglide/messages/cli_app_version_number_required.js
|
|
742
1532
|
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
743
|
-
/** @typedef {{
|
|
744
|
-
const
|
|
745
|
-
return
|
|
1533
|
+
/** @typedef {{}} Cli_App_Version_Number_RequiredInputs */
|
|
1534
|
+
const ko_cli_app_version_number_required = () => {
|
|
1535
|
+
return `버전 번호가 필요합니다. 예: npx sayren app release 1`;
|
|
746
1536
|
};
|
|
747
1537
|
/**
|
|
748
1538
|
|
|
749
|
-
* @param {
|
|
1539
|
+
* @param {Cli_App_Version_Number_RequiredInputs} inputs
|
|
750
1540
|
* @param {{ locale?: "ko" }} options
|
|
751
1541
|
* @returns {LocalizedString}
|
|
752
1542
|
*/
|
|
753
|
-
const
|
|
1543
|
+
const cli_app_version_number_required = ((inputs = {}, options = {}) => {
|
|
754
1544
|
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
755
|
-
return
|
|
1545
|
+
return ko_cli_app_version_number_required(inputs);
|
|
756
1546
|
});
|
|
757
1547
|
|
|
758
1548
|
//#endregion
|
|
759
|
-
//#region ../i18n/src/paraglide/messages/
|
|
1549
|
+
//#region ../i18n/src/paraglide/messages/cli_app_warning.js
|
|
760
1550
|
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
761
|
-
/** @typedef {{}}
|
|
762
|
-
const
|
|
763
|
-
return
|
|
1551
|
+
/** @typedef {{ message: NonNullable<unknown> }} Cli_App_WarningInputs */
|
|
1552
|
+
const ko_cli_app_warning = (i) => {
|
|
1553
|
+
return `경고: ${i?.message}`;
|
|
764
1554
|
};
|
|
765
1555
|
/**
|
|
766
1556
|
|
|
767
|
-
* @param {
|
|
1557
|
+
* @param {Cli_App_WarningInputs} inputs
|
|
768
1558
|
* @param {{ locale?: "ko" }} options
|
|
769
1559
|
* @returns {LocalizedString}
|
|
770
1560
|
*/
|
|
771
|
-
const
|
|
1561
|
+
const cli_app_warning = ((inputs, options = {}) => {
|
|
772
1562
|
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
773
|
-
return
|
|
1563
|
+
return ko_cli_app_warning(inputs);
|
|
774
1564
|
});
|
|
775
1565
|
|
|
776
1566
|
//#endregion
|
|
@@ -4332,6 +5122,7 @@ const ko_cli_usage = (i) => {
|
|
|
4332
5122
|
npx sayren deploy [--no-publish] [--skip-build] [--message <메모>] [--force [--yes]]
|
|
4333
5123
|
npx sayren status
|
|
4334
5124
|
npx sayren verify
|
|
5125
|
+
npx sayren app <명령> # 서드파티 앱(init·dev·trigger·verify·deploy·release·status·logs·secret·dev-store)
|
|
4335
5126
|
|
|
4336
5127
|
명령:
|
|
4337
5128
|
login Sayren 계정으로 로그인합니다. 로그인 토큰은 사용자 설정 폴더(~/.config/sayren/credentials.json)에 둡니다
|
|
@@ -4342,6 +5133,7 @@ const ko_cli_usage = (i) => {
|
|
|
4342
5133
|
deploy 로컬 검사 → 로컬 빌드 → 작업 트리 올리기 → 빌드 → 발행. 원격이 pull 이후 바뀌었으면 덮어쓰지 않습니다
|
|
4343
5134
|
status 사이트·발행 버전·최근 빌드·편집 환경
|
|
4344
5135
|
verify 로컬 규칙·경로·크기 검사(서버 빌드와 같은 규칙)
|
|
5136
|
+
app 서드파티 앱 — init <handle>로 만들고 dev로 로컬 실행, deploy로 버전을 올리고 release로 적용합니다. 자세히: https://docs.sayren.app/guides/apps-getting-started
|
|
4345
5137
|
|
|
4346
5138
|
토큰은 Sayren 계정 토큰만 받고 --token → SAYREN_TOKEN 환경 변수 → 로그인(credentials.json) 순서로 읽습니다.
|
|
4347
5139
|
계정 토큰은 계정에 발급됩니다 — 상점은 --store → SAYREN_STORE_ID → 폴더 설정 순서로 고릅니다(하나면 자동).
|
|
@@ -4364,6 +5156,14 @@ const ko_cli_usage = (i) => {
|
|
|
4364
5156
|
--api-origin <주소> api 주소(기본 https://api.sayren.app, SAYREN_API_ORIGIN). https만, localhost만 http
|
|
4365
5157
|
--accounts-origin <주소> 계정 서버 주소(기본: api 주소에 맞춘 값, SAYREN_ACCOUNTS_ORIGIN)
|
|
4366
5158
|
--token <토큰> 토큰을 직접 줍니다. 셸 기록·프로세스 목록에 남으니 login·SAYREN_TOKEN을 권합니다
|
|
5159
|
+
--key <키> app: 앱 배포 키(developer.sayren.app에서 발행, SAYREN_APP_KEY). CI에서 deploy·release·status·logs·secret에 씁니다
|
|
5160
|
+
-m, --message <메모> app deploy: 버전 메모
|
|
5161
|
+
--name <이름> app init: 앱 이름(기본: handle)
|
|
5162
|
+
--port <포트> app dev·trigger: 로컬 포트(기본 4020)
|
|
5163
|
+
--since <기간> app logs: 최근 기간만(30m·1h·2d)
|
|
5164
|
+
--tail app logs: 새 기록을 계속 봅니다
|
|
5165
|
+
--data <json> app trigger: 이벤트 데이터
|
|
5166
|
+
--order <orderId> app trigger: 주문 id를 데이터에 넣습니다
|
|
4367
5167
|
--lang <로케일> 메시지 언어(기본: SAYREN_LANG → 시스템 로케일 → ko)
|
|
4368
5168
|
|
|
4369
5169
|
종료 코드: 0 성공 · 1 실패 · 3 승인 대기(에이전트 토큰)`;
|
|
@@ -5437,7 +6237,7 @@ const mcp_rule_checkout_availability_title = ((inputs = {}, options = {}) => {
|
|
|
5437
6237
|
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
5438
6238
|
/** @typedef {{}} Mcp_Rule_Checkout_Availability_WhyInputs */
|
|
5439
6239
|
const ko_mcp_rule_checkout_availability_why = () => {
|
|
5440
|
-
return
|
|
6240
|
+
return `결제수단이 없거나 공개한 쇼핑몰의 실결제가 아직 열리지 않은 상점은 결제 요청이 409 \`PAYMENT_NOT_CONFIGURED\`다. 버튼을 그대로 두면 구매자가 담고 주문서까지 가서야 결제가 안 된다는 것을 안다`;
|
|
5441
6241
|
};
|
|
5442
6242
|
/**
|
|
5443
6243
|
|
|
@@ -6314,6 +7114,82 @@ const mcp_rule_payment_return_route_why = ((inputs = {}, options = {}) => {
|
|
|
6314
7114
|
return ko_mcp_rule_payment_return_route_why(inputs);
|
|
6315
7115
|
});
|
|
6316
7116
|
|
|
7117
|
+
//#endregion
|
|
7118
|
+
//#region ../i18n/src/paraglide/messages/mcp_rule_product_link_example.js
|
|
7119
|
+
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
7120
|
+
/** @typedef {{}} Mcp_Rule_Product_Link_ExampleInputs */
|
|
7121
|
+
const ko_mcp_rule_product_link_example = () => {
|
|
7122
|
+
return `import { productPathParam } from "../lib/product-path";
|
|
7123
|
+
|
|
7124
|
+
<Link to="/products/$productId" params={{ productId: productPathParam(product) }}>
|
|
7125
|
+
{product.name}
|
|
7126
|
+
</Link>`;
|
|
7127
|
+
};
|
|
7128
|
+
/**
|
|
7129
|
+
|
|
7130
|
+
* @param {Mcp_Rule_Product_Link_ExampleInputs} inputs
|
|
7131
|
+
* @param {{ locale?: "ko" }} options
|
|
7132
|
+
* @returns {LocalizedString}
|
|
7133
|
+
*/
|
|
7134
|
+
const mcp_rule_product_link_example = ((inputs = {}, options = {}) => {
|
|
7135
|
+
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
7136
|
+
return ko_mcp_rule_product_link_example(inputs);
|
|
7137
|
+
});
|
|
7138
|
+
|
|
7139
|
+
//#endregion
|
|
7140
|
+
//#region ../i18n/src/paraglide/messages/mcp_rule_product_link_fix.js
|
|
7141
|
+
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
7142
|
+
/** @typedef {{}} Mcp_Rule_Product_Link_FixInputs */
|
|
7143
|
+
const ko_mcp_rule_product_link_fix = () => {
|
|
7144
|
+
return `링크의 경로 값을 \`productPathParam(product)\`(\`src/lib/product-path.ts\`)로 만든다. 상품번호가 있으면 번호, 없는 옛 응답이면 상품 id다. 장바구니 담기·주문서 같은 서버 요청에는 계속 \`productId\`를 보낸다`;
|
|
7145
|
+
};
|
|
7146
|
+
/**
|
|
7147
|
+
|
|
7148
|
+
* @param {Mcp_Rule_Product_Link_FixInputs} inputs
|
|
7149
|
+
* @param {{ locale?: "ko" }} options
|
|
7150
|
+
* @returns {LocalizedString}
|
|
7151
|
+
*/
|
|
7152
|
+
const mcp_rule_product_link_fix = ((inputs = {}, options = {}) => {
|
|
7153
|
+
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
7154
|
+
return ko_mcp_rule_product_link_fix(inputs);
|
|
7155
|
+
});
|
|
7156
|
+
|
|
7157
|
+
//#endregion
|
|
7158
|
+
//#region ../i18n/src/paraglide/messages/mcp_rule_product_link_title.js
|
|
7159
|
+
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
7160
|
+
/** @typedef {{}} Mcp_Rule_Product_Link_TitleInputs */
|
|
7161
|
+
const ko_mcp_rule_product_link_title = () => {
|
|
7162
|
+
return `상품 링크는 상품번호 주소로 그린다(\`/products/{productNo}\`)`;
|
|
7163
|
+
};
|
|
7164
|
+
/**
|
|
7165
|
+
|
|
7166
|
+
* @param {Mcp_Rule_Product_Link_TitleInputs} inputs
|
|
7167
|
+
* @param {{ locale?: "ko" }} options
|
|
7168
|
+
* @returns {LocalizedString}
|
|
7169
|
+
*/
|
|
7170
|
+
const mcp_rule_product_link_title = ((inputs = {}, options = {}) => {
|
|
7171
|
+
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
7172
|
+
return ko_mcp_rule_product_link_title(inputs);
|
|
7173
|
+
});
|
|
7174
|
+
|
|
7175
|
+
//#endregion
|
|
7176
|
+
//#region ../i18n/src/paraglide/messages/mcp_rule_product_link_why.js
|
|
7177
|
+
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
7178
|
+
/** @typedef {{}} Mcp_Rule_Product_Link_WhyInputs */
|
|
7179
|
+
const ko_mcp_rule_product_link_why = () => {
|
|
7180
|
+
return `상품 주소는 상점 안에서 유일한 숫자 상품번호다. 상품 id(\`prod_…\`) 주소도 열리지만 상세 화면이 상품번호 주소로 301 이동시키므로 링크마다 왕복이 한 번 더 생기고 공유·검색 주소가 둘로 갈린다`;
|
|
7181
|
+
};
|
|
7182
|
+
/**
|
|
7183
|
+
|
|
7184
|
+
* @param {Mcp_Rule_Product_Link_WhyInputs} inputs
|
|
7185
|
+
* @param {{ locale?: "ko" }} options
|
|
7186
|
+
* @returns {LocalizedString}
|
|
7187
|
+
*/
|
|
7188
|
+
const mcp_rule_product_link_why = ((inputs = {}, options = {}) => {
|
|
7189
|
+
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
7190
|
+
return ko_mcp_rule_product_link_why(inputs);
|
|
7191
|
+
});
|
|
7192
|
+
|
|
6317
7193
|
//#endregion
|
|
6318
7194
|
//#region ../i18n/src/paraglide/messages/mcp_rule_server_only_env_example.js
|
|
6319
7195
|
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
@@ -7063,6 +7939,24 @@ const mcp_verify_payment_return_route = ((inputs = {}, options = {}) => {
|
|
|
7063
7939
|
return ko_mcp_verify_payment_return_route(inputs);
|
|
7064
7940
|
});
|
|
7065
7941
|
|
|
7942
|
+
//#endregion
|
|
7943
|
+
//#region ../i18n/src/paraglide/messages/mcp_verify_product_link.js
|
|
7944
|
+
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
7945
|
+
/** @typedef {{}} Mcp_Verify_Product_LinkInputs */
|
|
7946
|
+
const ko_mcp_verify_product_link = () => {
|
|
7947
|
+
return `상품 링크의 경로 값이 상품 id다 — \`productPathParam(product)\`로 상품번호 주소를 쓴다`;
|
|
7948
|
+
};
|
|
7949
|
+
/**
|
|
7950
|
+
|
|
7951
|
+
* @param {Mcp_Verify_Product_LinkInputs} inputs
|
|
7952
|
+
* @param {{ locale?: "ko" }} options
|
|
7953
|
+
* @returns {LocalizedString}
|
|
7954
|
+
*/
|
|
7955
|
+
const mcp_verify_product_link = ((inputs = {}, options = {}) => {
|
|
7956
|
+
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
7957
|
+
return ko_mcp_verify_product_link(inputs);
|
|
7958
|
+
});
|
|
7959
|
+
|
|
7066
7960
|
//#endregion
|
|
7067
7961
|
//#region ../i18n/src/paraglide/messages/mcp_verify_server_only_env.js
|
|
7068
7962
|
/** @typedef {import('../runtime.js').LocalizedString} LocalizedString */
|
|
@@ -7148,10 +8042,30 @@ const ko_mcp_verify_whole_project = () => {
|
|
|
7148
8042
|
* @param {{ locale?: "ko" }} options
|
|
7149
8043
|
* @returns {LocalizedString}
|
|
7150
8044
|
*/
|
|
7151
|
-
const mcp_verify_whole_project = ((inputs = {}, options = {}) => {
|
|
7152
|
-
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
7153
|
-
return ko_mcp_verify_whole_project(inputs);
|
|
7154
|
-
});
|
|
8045
|
+
const mcp_verify_whole_project = ((inputs = {}, options = {}) => {
|
|
8046
|
+
experimentalStaticLocale ?? options.locale ?? getLocale();
|
|
8047
|
+
return ko_mcp_verify_whole_project(inputs);
|
|
8048
|
+
});
|
|
8049
|
+
|
|
8050
|
+
//#endregion
|
|
8051
|
+
//#region ../i18n/src/lazy-messages.ts
|
|
8052
|
+
/**
|
|
8053
|
+
* 라벨 표 — 값을 읽을 때마다 메시지 함수를 불러 그 순간의 로케일 문구를 돌려준다(이슈 #73).
|
|
8054
|
+
* 모듈을 읽을 때 문구를 만들면 서버 렌더링에서 요청 로케일이 아니라 기동 시점 로케일로 굳는다.
|
|
8055
|
+
* 타입은 `Record<K, string>`이라 호출부(`LABEL[key]`, `Object.entries(LABEL)`)를 바꾸지 않는다.
|
|
8056
|
+
*
|
|
8057
|
+
* ```ts
|
|
8058
|
+
* export const ROLE_LABEL = lazyMessages<StoreRole>({ OWNER: m.store_shell_role_owner, ... });
|
|
8059
|
+
* ```
|
|
8060
|
+
*/
|
|
8061
|
+
function lazyMessages(table) {
|
|
8062
|
+
const labels = {};
|
|
8063
|
+
for (const key of Object.keys(table)) Object.defineProperty(labels, key, {
|
|
8064
|
+
enumerable: true,
|
|
8065
|
+
get: () => table[key]()
|
|
8066
|
+
});
|
|
8067
|
+
return Object.freeze(labels);
|
|
8068
|
+
}
|
|
7155
8069
|
|
|
7156
8070
|
//#endregion
|
|
7157
8071
|
//#region ../i18n/src/process-locale.ts
|
|
@@ -7202,47 +8116,6 @@ function takeLangFlag(argv) {
|
|
|
7202
8116
|
};
|
|
7203
8117
|
}
|
|
7204
8118
|
|
|
7205
|
-
//#endregion
|
|
7206
|
-
//#region package.json
|
|
7207
|
-
var version = "0.4.0";
|
|
7208
|
-
|
|
7209
|
-
//#endregion
|
|
7210
|
-
//#region src/client.ts
|
|
7211
|
-
/**
|
|
7212
|
-
* 관리 API 클라이언트 — `@sayren/store-sdk`(계약의 원천)를 쓴다. User-Agent `sayren-cli/{버전}`은 api가 버전의 만든 경로
|
|
7213
|
-
* (`createdVia: CLI`)를 적는 표시다(권한과 무관하다. 에이전트용 액세스 토큰이면 행위자는 여전히 에이전트이고 발행은 승인 대기다).
|
|
7214
|
-
* 토큰은 헤더에만 싣고 화면·로그에 찍지 않는다.
|
|
7215
|
-
*/
|
|
7216
|
-
const CLI_VERSION = version;
|
|
7217
|
-
const CLI_USER_AGENT = `sayren-cli/${CLI_VERSION}`;
|
|
7218
|
-
/**
|
|
7219
|
-
* 계정 토큰(#71)을 Bearer로, 고른 상점(`auth.storeId`)을 `X-Store-Id`로 싣는다. 401이면 `refresh`(자격 증명 파일의 refresh 회전)를
|
|
7220
|
-
* 한 번 부른 뒤 다시 보낸다. 토큰·상점은 함수로 읽어 갱신·선택된 값을 따라간다.
|
|
7221
|
-
*/
|
|
7222
|
-
function clientOf(auth, fetchImpl = fetch, refresh) {
|
|
7223
|
-
return createStoreClient({
|
|
7224
|
-
baseUrl: `${auth.apiOrigin}/v1`,
|
|
7225
|
-
accessToken: () => auth.token,
|
|
7226
|
-
storeId: () => auth.storeId ?? null,
|
|
7227
|
-
...refresh ? { refresh } : {},
|
|
7228
|
-
fetch: (input, init$1) => {
|
|
7229
|
-
const headers = new Headers(init$1?.headers);
|
|
7230
|
-
headers.set("user-agent", CLI_USER_AGENT);
|
|
7231
|
-
return fetchImpl(input, {
|
|
7232
|
-
...init$1,
|
|
7233
|
-
headers
|
|
7234
|
-
});
|
|
7235
|
-
}
|
|
7236
|
-
});
|
|
7237
|
-
}
|
|
7238
|
-
/** 오류 코드 — `ApiError`가 아니면 null */
|
|
7239
|
-
function codeOf(error) {
|
|
7240
|
-
return error instanceof ApiError ? error.code : null;
|
|
7241
|
-
}
|
|
7242
|
-
function detailsOf(error) {
|
|
7243
|
-
return error instanceof ApiError ? error.details ?? {} : {};
|
|
7244
|
-
}
|
|
7245
|
-
|
|
7246
8119
|
//#endregion
|
|
7247
8120
|
//#region ../mcp/src/account-token.ts
|
|
7248
8121
|
/**
|
|
@@ -8212,6 +9085,807 @@ async function selectStore(auth, client, io, candidates, folderStore = null) {
|
|
|
8212
9085
|
return auth.storeId;
|
|
8213
9086
|
}
|
|
8214
9087
|
|
|
9088
|
+
//#endregion
|
|
9089
|
+
//#region package.json
|
|
9090
|
+
var version = "0.5.0";
|
|
9091
|
+
|
|
9092
|
+
//#endregion
|
|
9093
|
+
//#region src/client.ts
|
|
9094
|
+
/**
|
|
9095
|
+
* 관리 API 클라이언트 — `@sayren/store-sdk`(계약의 원천)를 쓴다. User-Agent `sayren-cli/{버전}`은 api가 버전의 만든 경로
|
|
9096
|
+
* (`createdVia: CLI`)를 적는 표시다(권한과 무관하다. 에이전트용 액세스 토큰이면 행위자는 여전히 에이전트이고 발행은 승인 대기다).
|
|
9097
|
+
* 토큰은 헤더에만 싣고 화면·로그에 찍지 않는다.
|
|
9098
|
+
*/
|
|
9099
|
+
const CLI_VERSION = version;
|
|
9100
|
+
const CLI_USER_AGENT = `sayren-cli/${CLI_VERSION}`;
|
|
9101
|
+
/**
|
|
9102
|
+
* 계정 토큰(#71)을 Bearer로, 고른 상점(`auth.storeId`)을 `X-Store-Id`로 싣는다. 401이면 `refresh`(자격 증명 파일의 refresh 회전)를
|
|
9103
|
+
* 한 번 부른 뒤 다시 보낸다. 토큰·상점은 함수로 읽어 갱신·선택된 값을 따라간다.
|
|
9104
|
+
*/
|
|
9105
|
+
function clientOf(auth, fetchImpl = fetch, refresh) {
|
|
9106
|
+
return createStoreClient({
|
|
9107
|
+
baseUrl: `${auth.apiOrigin}/v1`,
|
|
9108
|
+
accessToken: () => auth.token,
|
|
9109
|
+
storeId: () => auth.storeId ?? null,
|
|
9110
|
+
...refresh ? { refresh } : {},
|
|
9111
|
+
fetch: (input, init$1) => {
|
|
9112
|
+
const headers = new Headers(init$1?.headers);
|
|
9113
|
+
headers.set("user-agent", CLI_USER_AGENT);
|
|
9114
|
+
return fetchImpl(input, {
|
|
9115
|
+
...init$1,
|
|
9116
|
+
headers
|
|
9117
|
+
});
|
|
9118
|
+
}
|
|
9119
|
+
});
|
|
9120
|
+
}
|
|
9121
|
+
/** 오류 코드 — `ApiError`가 아니면 null */
|
|
9122
|
+
function codeOf(error) {
|
|
9123
|
+
return error instanceof ApiError ? error.code : null;
|
|
9124
|
+
}
|
|
9125
|
+
function detailsOf(error) {
|
|
9126
|
+
return error instanceof ApiError ? error.details ?? {} : {};
|
|
9127
|
+
}
|
|
9128
|
+
|
|
9129
|
+
//#endregion
|
|
9130
|
+
//#region src/app-commands.ts
|
|
9131
|
+
/**
|
|
9132
|
+
* `sayren app …`(이슈 #22, 매뉴얼 「앱 만들기」) — 앱 폴더(`sayren.app.json`)에서 돈다. 서버는 코드를 빌드하지 않는다(A1):
|
|
9133
|
+
* CLI가 esbuild로 서버 번들 한 파일을 만들어 올리고 api가 `DEPENDENCIES`·`VERIFY`·`BUNDLE`·`UPLOAD`를 돈다. 앱 화면(`ui/`)은
|
|
9134
|
+
* 정적 파일 그대로 올린다.
|
|
9135
|
+
*
|
|
9136
|
+
* 인증은 둘이다. 앱 배포 키(`--key`·`SAYREN_APP_KEY`, developer.sayren.app에서 발행, `sak_…`)는 그 앱의 경로에만 쓰이고(CI 용도),
|
|
9137
|
+
* 그 밖에는 `sayren login`의 계정 토큰이다. 앱 등록(`init`)·개발 상점은 계정 토큰만 된다.
|
|
9138
|
+
*/
|
|
9139
|
+
const APP_MANIFEST_FILE = "sayren.app.json";
|
|
9140
|
+
const APP_STATE_FILE = `${STATE_DIR}/app.json`;
|
|
9141
|
+
const APP_DEV_PORT = 4020;
|
|
9142
|
+
const SERVER_ENTRY_CANDIDATES = [
|
|
9143
|
+
"src/server.ts",
|
|
9144
|
+
"src/server.js",
|
|
9145
|
+
"src/server.mjs"
|
|
9146
|
+
];
|
|
9147
|
+
const UI_DIR = "ui";
|
|
9148
|
+
const SETTING_ENV_PREFIX = "SAYREN_APP_SETTING_";
|
|
9149
|
+
const SECRET_ENV_PREFIX = "SAYREN_APP_SECRET_";
|
|
9150
|
+
/** `dev`의 개발 설치 토큰을 만료 몇 초 전에 다시 받는가 */
|
|
9151
|
+
const DEV_TOKEN_REFRESH_MARGIN_MS = 6e4;
|
|
9152
|
+
function readAppState(dir) {
|
|
9153
|
+
const path = join(dir, APP_STATE_FILE);
|
|
9154
|
+
if (!existsSync(path)) return null;
|
|
9155
|
+
try {
|
|
9156
|
+
const parsed = JSON.parse(readFileSync(path, "utf8"));
|
|
9157
|
+
if (typeof parsed.appId === "string" && typeof parsed.handle === "string" && typeof parsed.apiOrigin === "string") return {
|
|
9158
|
+
appId: parsed.appId,
|
|
9159
|
+
handle: parsed.handle,
|
|
9160
|
+
apiOrigin: parsed.apiOrigin
|
|
9161
|
+
};
|
|
9162
|
+
} catch {}
|
|
9163
|
+
throw new CliError(cli_app_state_invalid({ file: APP_STATE_FILE }));
|
|
9164
|
+
}
|
|
9165
|
+
function writeAppState(dir, state) {
|
|
9166
|
+
mkdirSync(join(dir, STATE_DIR), { recursive: true });
|
|
9167
|
+
writeFileSync(join(dir, APP_STATE_FILE), `${JSON.stringify(state, null, 2)}\n`, { mode: 384 });
|
|
9168
|
+
}
|
|
9169
|
+
/** 매니페스트 원문(JSON)과 검증 결과 — 문제가 있으면 CliError */
|
|
9170
|
+
function loadManifest(dir) {
|
|
9171
|
+
const path = join(dir, APP_MANIFEST_FILE);
|
|
9172
|
+
if (!existsSync(path)) throw new CliError(cli_app_manifest_missing({ file: APP_MANIFEST_FILE }));
|
|
9173
|
+
let raw;
|
|
9174
|
+
try {
|
|
9175
|
+
raw = JSON.parse(readFileSync(path, "utf8"));
|
|
9176
|
+
} catch (error) {
|
|
9177
|
+
throw new CliError(cli_app_manifest_invalid({ problems: `- ${error instanceof Error ? error.message : String(error)}` }));
|
|
9178
|
+
}
|
|
9179
|
+
const parsed = appManifestSchema.safeParse(raw);
|
|
9180
|
+
if (!parsed.success) throw new CliError(cli_app_manifest_invalid({ problems: parsed.error.issues.map((issue) => `- ${issue.path.join(".") || "(root)"}: ${issue.message}`).join("\n") }));
|
|
9181
|
+
const problems = appManifestProblems(parsed.data);
|
|
9182
|
+
if (problems.length) throw new CliError(cli_app_manifest_invalid({ problems: problems.map((p) => `- ${p.path}: ${p.message} (${p.code})`).join("\n") }));
|
|
9183
|
+
return {
|
|
9184
|
+
raw,
|
|
9185
|
+
manifest: parsed.data
|
|
9186
|
+
};
|
|
9187
|
+
}
|
|
9188
|
+
function appDir(flags, io) {
|
|
9189
|
+
return resolve(io.cwd, flags.dir ?? ".");
|
|
9190
|
+
}
|
|
9191
|
+
function requireState(dir) {
|
|
9192
|
+
const state = readAppState(dir);
|
|
9193
|
+
if (!state) throw new CliError(cli_app_not_registered({ file: APP_MANIFEST_FILE }));
|
|
9194
|
+
return state;
|
|
9195
|
+
}
|
|
9196
|
+
/** 배포 키(`--key` → `SAYREN_APP_KEY`)가 있으면 그것, 없으면 계정 토큰(`resolveAuth`) */
|
|
9197
|
+
async function developerAuth(flags, dir, io, options = {}) {
|
|
9198
|
+
const state = readAppState(dir);
|
|
9199
|
+
const key = flags.key?.trim() || io.env.SAYREN_APP_KEY?.trim();
|
|
9200
|
+
if (key && !options.accountOnly) {
|
|
9201
|
+
if (!APP_DEPLOY_KEY_PATTERN.test(key)) throw new CliError(cli_app_key_invalid());
|
|
9202
|
+
const { apiOrigin } = resolveApiOrigin({ apiOrigin: flags.apiOrigin ?? state?.apiOrigin }, dir, null, io.env);
|
|
9203
|
+
return {
|
|
9204
|
+
apiOrigin,
|
|
9205
|
+
bearer: key,
|
|
9206
|
+
kind: "key"
|
|
9207
|
+
};
|
|
9208
|
+
}
|
|
9209
|
+
const auth = resolveAuth({
|
|
9210
|
+
...flags,
|
|
9211
|
+
apiOrigin: flags.apiOrigin ?? state?.apiOrigin
|
|
9212
|
+
}, dir, null, io.env, credentialTokenReader(io.env));
|
|
9213
|
+
const refresh = await readyAuth(auth, io);
|
|
9214
|
+
return {
|
|
9215
|
+
apiOrigin: auth.apiOrigin,
|
|
9216
|
+
get bearer() {
|
|
9217
|
+
return auth.token;
|
|
9218
|
+
},
|
|
9219
|
+
kind: "account",
|
|
9220
|
+
...refresh ? { refresh } : {}
|
|
9221
|
+
};
|
|
9222
|
+
}
|
|
9223
|
+
async function devApi(auth, io, method, path, body, retried = false) {
|
|
9224
|
+
const res = await io.fetch(`${auth.apiOrigin}/platform/v1/developer${path}`, {
|
|
9225
|
+
method,
|
|
9226
|
+
headers: {
|
|
9227
|
+
authorization: `Bearer ${auth.bearer}`,
|
|
9228
|
+
"user-agent": CLI_USER_AGENT,
|
|
9229
|
+
...body === void 0 ? {} : { "content-type": "application/json" }
|
|
9230
|
+
},
|
|
9231
|
+
...body === void 0 ? {} : { body: JSON.stringify(body) }
|
|
9232
|
+
});
|
|
9233
|
+
if (res.status === 204) return void 0;
|
|
9234
|
+
const text = await res.text();
|
|
9235
|
+
let json = {};
|
|
9236
|
+
try {
|
|
9237
|
+
json = text ? JSON.parse(text) : {};
|
|
9238
|
+
} catch {
|
|
9239
|
+
throw new ApiError(res.status, "INVALID_RESPONSE", text.slice(0, 200));
|
|
9240
|
+
}
|
|
9241
|
+
if (res.ok && json.meta?.isSuccess !== false) return json.data;
|
|
9242
|
+
if (res.status === 401 && auth.refresh && !retried && await auth.refresh()) return devApi(auth, io, method, path, body, true);
|
|
9243
|
+
throw new ApiError(res.status, json.error?.code ?? json.meta?.code ?? "ERROR", json.error?.message ?? json.meta?.message ?? `HTTP ${res.status}`, void 0, json, json.error?.details);
|
|
9244
|
+
}
|
|
9245
|
+
function serverEntryOf(dir) {
|
|
9246
|
+
const found = SERVER_ENTRY_CANDIDATES.find((candidate) => existsSync(join(dir, candidate)));
|
|
9247
|
+
if (!found) throw new CliError(cli_app_entry_missing({ entry: SERVER_ENTRY_CANDIDATES[0] }));
|
|
9248
|
+
return join(dir, found);
|
|
9249
|
+
}
|
|
9250
|
+
/** esbuild로 서버 번들을 만든다. 앱 의존성은 앱 폴더의 `node_modules`에서 푼다(설치돼 있어야 한다) */
|
|
9251
|
+
async function bundleServer(dir) {
|
|
9252
|
+
const entry = serverEntryOf(dir);
|
|
9253
|
+
const esbuild = await import("esbuild");
|
|
9254
|
+
try {
|
|
9255
|
+
const file = (await esbuild.build({
|
|
9256
|
+
entryPoints: [entry],
|
|
9257
|
+
bundle: true,
|
|
9258
|
+
write: false,
|
|
9259
|
+
format: "esm",
|
|
9260
|
+
platform: "browser",
|
|
9261
|
+
target: "es2022",
|
|
9262
|
+
conditions: [
|
|
9263
|
+
"workerd",
|
|
9264
|
+
"worker",
|
|
9265
|
+
"browser"
|
|
9266
|
+
],
|
|
9267
|
+
mainFields: [
|
|
9268
|
+
"browser",
|
|
9269
|
+
"module",
|
|
9270
|
+
"main"
|
|
9271
|
+
],
|
|
9272
|
+
absWorkingDir: dir,
|
|
9273
|
+
logLevel: "silent",
|
|
9274
|
+
legalComments: "none",
|
|
9275
|
+
define: { "process.env.NODE_ENV": "\"production\"" }
|
|
9276
|
+
})).outputFiles?.[0];
|
|
9277
|
+
if (!file) throw new CliError(cli_app_bundle_failed({ tail: "(빈 산출물)" }));
|
|
9278
|
+
return file.text;
|
|
9279
|
+
} catch (error) {
|
|
9280
|
+
if (error instanceof CliError) throw error;
|
|
9281
|
+
const tail = error && typeof error === "object" && "errors" in error ? (error.errors ?? []).map((e) => e.location ? `${e.location.file}:${e.location.line}: ${e.text}` : e.text).join("\n") : error instanceof Error ? error.message : String(error);
|
|
9282
|
+
throw new CliError(cli_app_bundle_failed({ tail }));
|
|
9283
|
+
}
|
|
9284
|
+
}
|
|
9285
|
+
/** 번들을 불러 `defineApp` 메타데이터를 읽는다 — `HANDLER_MISSING`·기본 내보내기 검사. `dev`도 이 모듈을 실행한다 */
|
|
9286
|
+
async function loadBundle(dir, server) {
|
|
9287
|
+
const out = join(dir, STATE_DIR, "build");
|
|
9288
|
+
mkdirSync(out, { recursive: true });
|
|
9289
|
+
const file = join(out, `server.${Date.now().toString(36)}.mjs`);
|
|
9290
|
+
writeFileSync(file, server);
|
|
9291
|
+
let mod;
|
|
9292
|
+
try {
|
|
9293
|
+
mod = await import(pathToFileURL(file).href);
|
|
9294
|
+
} catch (error) {
|
|
9295
|
+
throw new CliError(cli_app_bundle_failed({ tail: error instanceof Error ? error.message : String(error) }));
|
|
9296
|
+
}
|
|
9297
|
+
const app = mod.default;
|
|
9298
|
+
const meta = app?.__sayrenApp;
|
|
9299
|
+
if (!app || typeof app.fetch !== "function" || !meta) throw new CliError(cli_app_default_export_missing());
|
|
9300
|
+
return {
|
|
9301
|
+
app,
|
|
9302
|
+
meta: {
|
|
9303
|
+
events: [...meta.events],
|
|
9304
|
+
http: [...meta.http]
|
|
9305
|
+
}
|
|
9306
|
+
};
|
|
9307
|
+
}
|
|
9308
|
+
/** 로컬 검사 — 매니페스트, 번들, 이벤트 함수 존재, 비밀 값 형식, 크기. 서버 `VERIFY`·`BUNDLE`과 같은 규칙 */
|
|
9309
|
+
async function verifyApp(dir) {
|
|
9310
|
+
const { raw, manifest } = loadManifest(dir);
|
|
9311
|
+
const server = await bundleServer(dir);
|
|
9312
|
+
if (APP_SECRET_PATTERNS.find((pattern) => pattern.test(server))) throw new CliError(cli_app_secret_in_source());
|
|
9313
|
+
const gzipBytes = gzipSync(server).byteLength;
|
|
9314
|
+
if (gzipBytes > APP_SERVER_BUNDLE_MAX_GZIP_BYTES) throw new CliError(cli_app_bundle_too_large({ size: formatMb(gzipBytes) }));
|
|
9315
|
+
const { app, meta } = await loadBundle(dir, server);
|
|
9316
|
+
const missing = manifest.events.filter((event) => !meta.events.includes(event));
|
|
9317
|
+
if (missing.length) throw new CliError(cli_app_handler_missing({ events: missing.join(", ") }));
|
|
9318
|
+
const warnings = [];
|
|
9319
|
+
const listed = manifest.events;
|
|
9320
|
+
const extra = meta.events.filter((event) => !listed.includes(event));
|
|
9321
|
+
if (extra.length) warnings.push(cli_app_handler_unlisted({ events: extra.join(", ") }));
|
|
9322
|
+
const ui = manifest.ui?.adminPage ? collectUi(dir) : [];
|
|
9323
|
+
if (manifest.ui?.adminPage && !ui.some((file) => file.path === "index.html")) throw new CliError(cli_app_ui_index_missing({ dir: UI_DIR }));
|
|
9324
|
+
return {
|
|
9325
|
+
manifest,
|
|
9326
|
+
raw,
|
|
9327
|
+
bundle: {
|
|
9328
|
+
server,
|
|
9329
|
+
gzipBytes,
|
|
9330
|
+
meta
|
|
9331
|
+
},
|
|
9332
|
+
app,
|
|
9333
|
+
ui,
|
|
9334
|
+
warnings
|
|
9335
|
+
};
|
|
9336
|
+
}
|
|
9337
|
+
function collectUi(dir) {
|
|
9338
|
+
const root = join(dir, UI_DIR);
|
|
9339
|
+
if (!existsSync(root)) throw new CliError(cli_app_ui_index_missing({ dir: UI_DIR }));
|
|
9340
|
+
const files = [];
|
|
9341
|
+
const walk = (current) => {
|
|
9342
|
+
for (const name of readdirSync(current)) {
|
|
9343
|
+
if (name.startsWith(".") || name === "node_modules" || name === "_sayren") continue;
|
|
9344
|
+
const full = join(current, name);
|
|
9345
|
+
if (statSync(full).isDirectory()) walk(full);
|
|
9346
|
+
else files.push({
|
|
9347
|
+
path: relative(root, full).split(sep).join("/"),
|
|
9348
|
+
bytes: readFileSync(full)
|
|
9349
|
+
});
|
|
9350
|
+
}
|
|
9351
|
+
};
|
|
9352
|
+
walk(root);
|
|
9353
|
+
const bridge = join(dir, "node_modules/@sayren/app/dist/ui.mjs");
|
|
9354
|
+
if (!existsSync(bridge)) throw new CliError(cli_app_bridge_missing());
|
|
9355
|
+
files.push({
|
|
9356
|
+
path: "_sayren/ui.js",
|
|
9357
|
+
bytes: readFileSync(bridge)
|
|
9358
|
+
});
|
|
9359
|
+
if (files.length > APP_UI_MAX_FILES) throw new CliError(cli_app_ui_too_large({ limit: `${APP_UI_MAX_FILES} files` }));
|
|
9360
|
+
if (files.reduce((sum, file) => sum + file.bytes.byteLength, 0) > APP_UI_MAX_BYTES) throw new CliError(cli_app_ui_too_large({ limit: formatMb(APP_UI_MAX_BYTES) }));
|
|
9361
|
+
return files;
|
|
9362
|
+
}
|
|
9363
|
+
function formatMb(bytes) {
|
|
9364
|
+
return `${(bytes / 1024 / 1024).toFixed(1)}MB`;
|
|
9365
|
+
}
|
|
9366
|
+
const HANDLE_RE = /^[a-z0-9][a-z0-9-]{1,38}[a-z0-9]$/;
|
|
9367
|
+
function initFiles(handle, name) {
|
|
9368
|
+
const manifest = {
|
|
9369
|
+
handle,
|
|
9370
|
+
name,
|
|
9371
|
+
description: "",
|
|
9372
|
+
scopes: ["order:r"],
|
|
9373
|
+
network: { hosts: [] },
|
|
9374
|
+
events: ["ORDER.PAID"],
|
|
9375
|
+
settings: {}
|
|
9376
|
+
};
|
|
9377
|
+
return {
|
|
9378
|
+
[APP_MANIFEST_FILE]: `${JSON.stringify(manifest, null, 2)}\n`,
|
|
9379
|
+
"package.json": `${JSON.stringify({
|
|
9380
|
+
name: handle,
|
|
9381
|
+
private: true,
|
|
9382
|
+
type: "module",
|
|
9383
|
+
scripts: {
|
|
9384
|
+
dev: "sayren app dev",
|
|
9385
|
+
verify: "sayren app verify",
|
|
9386
|
+
deploy: "sayren app deploy"
|
|
9387
|
+
},
|
|
9388
|
+
dependencies: { "@sayren/app": `^${CLI_VERSION}` },
|
|
9389
|
+
devDependencies: {
|
|
9390
|
+
sayren: `^${CLI_VERSION}`,
|
|
9391
|
+
typescript: "^5.9.0"
|
|
9392
|
+
}
|
|
9393
|
+
}, null, 2)}\n`,
|
|
9394
|
+
"tsconfig.json": `${JSON.stringify({
|
|
9395
|
+
compilerOptions: {
|
|
9396
|
+
target: "ES2022",
|
|
9397
|
+
module: "ESNext",
|
|
9398
|
+
moduleResolution: "bundler",
|
|
9399
|
+
strict: true,
|
|
9400
|
+
noEmit: true,
|
|
9401
|
+
lib: ["ES2022", "DOM"],
|
|
9402
|
+
skipLibCheck: true
|
|
9403
|
+
},
|
|
9404
|
+
include: ["src"]
|
|
9405
|
+
}, null, 2)}\n`,
|
|
9406
|
+
"src/server.ts": `import { defineApp } from "@sayren/app";
|
|
9407
|
+
|
|
9408
|
+
export default defineApp({
|
|
9409
|
+
events: {
|
|
9410
|
+
"ORDER.PAID": async (event, ctx) => {
|
|
9411
|
+
const { orderId } = event.data as { orderId: string };
|
|
9412
|
+
const order = await ctx.api.orders.get(orderId);
|
|
9413
|
+
ctx.log.info("결제된 주문", order.orderNo, order.totalAmount);
|
|
9414
|
+
},
|
|
9415
|
+
},
|
|
9416
|
+
});
|
|
9417
|
+
`,
|
|
9418
|
+
".gitignore": "node_modules/\ndist/\n.env\n.sayren/\n",
|
|
9419
|
+
".env.example": `# 로컬 실행(sayren app dev)의 설정·비밀 값\n# ${SETTING_ENV_PREFIX}<설정 키>=값\n# ${SECRET_ENV_PREFIX}<비밀 값 이름>=값\n`
|
|
9420
|
+
};
|
|
9421
|
+
}
|
|
9422
|
+
async function appInit(handle, flags, io) {
|
|
9423
|
+
if (!handle || !HANDLE_RE.test(handle)) throw new CliError(cli_app_handle_invalid());
|
|
9424
|
+
const dir = resolve(io.cwd, flags.dir ?? handle);
|
|
9425
|
+
if (existsSync(join(dir, APP_MANIFEST_FILE))) throw new CliError(cli_app_dir_exists({ dir: relative(io.cwd, dir) || "." }));
|
|
9426
|
+
const name = flags.name?.trim() || handle;
|
|
9427
|
+
const auth = await developerAuth(flags, dir, io, { accountOnly: true });
|
|
9428
|
+
const app = await devApi(auth, io, "POST", "/apps", {
|
|
9429
|
+
handle,
|
|
9430
|
+
name
|
|
9431
|
+
});
|
|
9432
|
+
mkdirSync(join(dir, "src"), { recursive: true });
|
|
9433
|
+
for (const [path, content] of Object.entries(initFiles(handle, name))) {
|
|
9434
|
+
const full = join(dir, path);
|
|
9435
|
+
mkdirSync(join(full, ".."), { recursive: true });
|
|
9436
|
+
if (!existsSync(full)) writeFileSync(full, content);
|
|
9437
|
+
}
|
|
9438
|
+
writeAppState(dir, {
|
|
9439
|
+
appId: app.appId,
|
|
9440
|
+
handle: app.handle,
|
|
9441
|
+
apiOrigin: auth.apiOrigin
|
|
9442
|
+
});
|
|
9443
|
+
io.out(cli_app_init_done({
|
|
9444
|
+
dir: relative(io.cwd, dir) || ".",
|
|
9445
|
+
appId: app.appId,
|
|
9446
|
+
handle
|
|
9447
|
+
}));
|
|
9448
|
+
return 0;
|
|
9449
|
+
}
|
|
9450
|
+
async function appVerify(flags, io) {
|
|
9451
|
+
const outcome = await verifyApp(appDir(flags, io));
|
|
9452
|
+
for (const warning of outcome.warnings) io.err(cli_app_warning({ message: warning }));
|
|
9453
|
+
io.out(cli_app_verify_ok({
|
|
9454
|
+
events: outcome.bundle.meta.events.join(", ") || "-",
|
|
9455
|
+
http: outcome.bundle.meta.http.join(", ") || "-",
|
|
9456
|
+
size: formatMb(outcome.bundle.gzipBytes),
|
|
9457
|
+
ui: String(outcome.ui.length)
|
|
9458
|
+
}));
|
|
9459
|
+
return 0;
|
|
9460
|
+
}
|
|
9461
|
+
function printBuild(version$1, io) {
|
|
9462
|
+
for (const stage of version$1.build?.stages ?? []) io.out(` ${stage.ok ? "✓" : "✗"} ${stage.stage} ${stage.durationMs}ms`);
|
|
9463
|
+
}
|
|
9464
|
+
async function appDeploy(flags, io) {
|
|
9465
|
+
const dir = appDir(flags, io);
|
|
9466
|
+
const state = requireState(dir);
|
|
9467
|
+
const outcome = await verifyApp(dir);
|
|
9468
|
+
if (outcome.manifest.handle !== state.handle) throw new CliError(cli_app_handle_mismatch({
|
|
9469
|
+
manifest: outcome.manifest.handle,
|
|
9470
|
+
registered: state.handle
|
|
9471
|
+
}));
|
|
9472
|
+
for (const warning of outcome.warnings) io.err(cli_app_warning({ message: warning }));
|
|
9473
|
+
const auth = await developerAuth(flags, dir, io);
|
|
9474
|
+
const packageJson = existsSync(join(dir, "package.json")) ? readFileSync(join(dir, "package.json"), "utf8") : "{}";
|
|
9475
|
+
const lockfile = existsSync(join(dir, "pnpm-lock.yaml")) ? readFileSync(join(dir, "pnpm-lock.yaml"), "utf8") : null;
|
|
9476
|
+
io.out(cli_app_uploading({
|
|
9477
|
+
size: formatMb(outcome.bundle.gzipBytes),
|
|
9478
|
+
ui: String(outcome.ui.length)
|
|
9479
|
+
}));
|
|
9480
|
+
const version$1 = await devApi(auth, io, "PUT", `/apps/${state.appId}/versions`, {
|
|
9481
|
+
manifest: outcome.raw,
|
|
9482
|
+
packageJson,
|
|
9483
|
+
lockfile,
|
|
9484
|
+
server: Buffer.from(outcome.bundle.server, "utf8").toString("base64"),
|
|
9485
|
+
ui: outcome.ui.map((file) => ({
|
|
9486
|
+
path: file.path,
|
|
9487
|
+
content: file.bytes.toString("base64")
|
|
9488
|
+
})),
|
|
9489
|
+
...flags.message ? { message: flags.message } : {}
|
|
9490
|
+
});
|
|
9491
|
+
printBuild(version$1, io);
|
|
9492
|
+
if (version$1.status !== "READY") {
|
|
9493
|
+
io.err(cli_app_build_failed({
|
|
9494
|
+
stage: version$1.build?.failureStage ?? "-",
|
|
9495
|
+
code: version$1.build?.errorCode ?? "-",
|
|
9496
|
+
tail: version$1.build?.logTail ?? ""
|
|
9497
|
+
}));
|
|
9498
|
+
return 1;
|
|
9499
|
+
}
|
|
9500
|
+
io.out(cli_app_deploy_done({ number: String(version$1.number) }));
|
|
9501
|
+
return 0;
|
|
9502
|
+
}
|
|
9503
|
+
async function appRelease(numberRaw, flags, io) {
|
|
9504
|
+
const number = Number(numberRaw);
|
|
9505
|
+
if (!Number.isInteger(number) || number < 1) throw new CliError(cli_app_version_number_required());
|
|
9506
|
+
const dir = appDir(flags, io);
|
|
9507
|
+
const state = requireState(dir);
|
|
9508
|
+
const result = await devApi(await developerAuth(flags, dir, io), io, "POST", `/apps/${state.appId}/versions/${number}/release`);
|
|
9509
|
+
io.out(cli_app_release_done({
|
|
9510
|
+
number: String(result.version.number),
|
|
9511
|
+
applied: String(result.applied),
|
|
9512
|
+
pending: String(result.pendingReconsent)
|
|
9513
|
+
}));
|
|
9514
|
+
return 0;
|
|
9515
|
+
}
|
|
9516
|
+
async function appStatus(flags, io) {
|
|
9517
|
+
const dir = appDir(flags, io);
|
|
9518
|
+
const state = requireState(dir);
|
|
9519
|
+
const auth = await developerAuth(flags, dir, io);
|
|
9520
|
+
const [app, versions] = await Promise.all([devApi(auth, io, "GET", `/apps/${state.appId}`), devApi(auth, io, "GET", `/apps/${state.appId}/versions`)]);
|
|
9521
|
+
io.out(cli_app_status({
|
|
9522
|
+
handle: app.handle,
|
|
9523
|
+
name: app.name,
|
|
9524
|
+
appId: app.appId,
|
|
9525
|
+
status: app.status,
|
|
9526
|
+
current: app.currentVersionNumber === null ? "-" : String(app.currentVersionNumber),
|
|
9527
|
+
active: String(app.installations.active),
|
|
9528
|
+
pending: String(app.installations.pendingReconsent)
|
|
9529
|
+
}));
|
|
9530
|
+
for (const version$1 of versions.slice(0, 5)) io.out(` v${version$1.number} ${version$1.status}${version$1.current ? " *" : ""} ${version$1.createdAt}${version$1.message ? ` — ${version$1.message}` : ""}${version$1.build?.errorCode ? ` (${version$1.build.errorCode})` : ""}`);
|
|
9531
|
+
return 0;
|
|
9532
|
+
}
|
|
9533
|
+
const SINCE_RE = /^(\d+)([smhd])$/;
|
|
9534
|
+
function sinceMs(value) {
|
|
9535
|
+
if (!value) return null;
|
|
9536
|
+
const match = SINCE_RE.exec(value.trim());
|
|
9537
|
+
if (!match) throw new CliError(cli_app_since_invalid({ value }));
|
|
9538
|
+
const unit = {
|
|
9539
|
+
s: 1e3,
|
|
9540
|
+
m: 6e4,
|
|
9541
|
+
h: 36e5,
|
|
9542
|
+
d: 864e5
|
|
9543
|
+
}[match[2]];
|
|
9544
|
+
return Number(match[1]) * unit;
|
|
9545
|
+
}
|
|
9546
|
+
function invocationLine(row) {
|
|
9547
|
+
const head = `${row.createdAt} ${row.outcome.padEnd(14)} ${row.kind}${row.eventType ? ` ${row.eventType}` : ""} ${row.storeName ?? row.storeId} v${row.versionNumber ?? "-"} ${row.durationMs}ms`;
|
|
9548
|
+
const lines = row.logs.map((line) => ` [${line.level}] ${line.message}`);
|
|
9549
|
+
if (row.errorMessage) lines.push(` ! ${row.errorMessage.split("\n")[0]}`);
|
|
9550
|
+
return [head, ...lines].join("\n");
|
|
9551
|
+
}
|
|
9552
|
+
async function appLogs(flags, io) {
|
|
9553
|
+
const dir = appDir(flags, io);
|
|
9554
|
+
const state = requireState(dir);
|
|
9555
|
+
const auth = await developerAuth(flags, dir, io);
|
|
9556
|
+
const since = sinceMs(flags.since);
|
|
9557
|
+
const seen = /* @__PURE__ */ new Set();
|
|
9558
|
+
const query = new URLSearchParams({ limit: "50" });
|
|
9559
|
+
if (flags.store) query.set("storeId", flags.store);
|
|
9560
|
+
const fetchOnce = async () => {
|
|
9561
|
+
const rows = await devApi(auth, io, "GET", `/apps/${state.appId}/invocations?${query}`);
|
|
9562
|
+
const now = io.now?.() ?? Date.now();
|
|
9563
|
+
const fresh = rows.filter((row) => !seen.has(row.invocationId)).filter((row) => since === null || now - Date.parse(row.createdAt) <= since).reverse();
|
|
9564
|
+
for (const row of fresh) {
|
|
9565
|
+
seen.add(row.invocationId);
|
|
9566
|
+
io.out(invocationLine(row));
|
|
9567
|
+
}
|
|
9568
|
+
return fresh.length;
|
|
9569
|
+
};
|
|
9570
|
+
const count = await fetchOnce();
|
|
9571
|
+
if (!flags.tail) {
|
|
9572
|
+
if (count === 0) io.out(cli_app_logs_empty());
|
|
9573
|
+
return 0;
|
|
9574
|
+
}
|
|
9575
|
+
while (!io.signal?.aborted) {
|
|
9576
|
+
await io.sleep(io.pollMs);
|
|
9577
|
+
await fetchOnce();
|
|
9578
|
+
}
|
|
9579
|
+
return 0;
|
|
9580
|
+
}
|
|
9581
|
+
async function appSecret(action, name, flags, io) {
|
|
9582
|
+
const dir = appDir(flags, io);
|
|
9583
|
+
const state = requireState(dir);
|
|
9584
|
+
const auth = await developerAuth(flags, dir, io);
|
|
9585
|
+
const base = `/apps/${state.appId}/secrets`;
|
|
9586
|
+
if (action === "list") {
|
|
9587
|
+
const list = await devApi(auth, io, "GET", base);
|
|
9588
|
+
if (list.length === 0) io.out(cli_app_secret_empty());
|
|
9589
|
+
for (const item of list) io.out(` ${item.name} ${item.updatedAt}`);
|
|
9590
|
+
return 0;
|
|
9591
|
+
}
|
|
9592
|
+
if (!name) throw new CliError(cli_app_secret_name_required());
|
|
9593
|
+
if (action === "put") {
|
|
9594
|
+
const value = (await (io.readStdin ?? readStdin)()).replace(/\r?\n$/, "");
|
|
9595
|
+
if (!value) throw new CliError(cli_app_secret_value_required({ name }));
|
|
9596
|
+
await devApi(auth, io, "PUT", `${base}/${encodeURIComponent(name)}`, { value });
|
|
9597
|
+
io.out(cli_app_secret_saved({ name }));
|
|
9598
|
+
return 0;
|
|
9599
|
+
}
|
|
9600
|
+
if (action === "delete") {
|
|
9601
|
+
await devApi(auth, io, "DELETE", `${base}/${encodeURIComponent(name)}`);
|
|
9602
|
+
io.out(cli_app_secret_deleted({ name }));
|
|
9603
|
+
return 0;
|
|
9604
|
+
}
|
|
9605
|
+
throw new CliError(cli_app_subcommand_unknown({ command: `secret ${action ?? ""}`.trim() }));
|
|
9606
|
+
}
|
|
9607
|
+
function readStdin() {
|
|
9608
|
+
return new Promise((resolve$1, reject) => {
|
|
9609
|
+
let data = "";
|
|
9610
|
+
process.stdin.setEncoding("utf8");
|
|
9611
|
+
process.stdin.on("data", (chunk) => {
|
|
9612
|
+
data += chunk;
|
|
9613
|
+
});
|
|
9614
|
+
process.stdin.once("end", () => resolve$1(data));
|
|
9615
|
+
process.stdin.once("error", reject);
|
|
9616
|
+
});
|
|
9617
|
+
}
|
|
9618
|
+
async function appDevStore(action, name, flags, io) {
|
|
9619
|
+
const auth = await developerAuth(flags, appDir(flags, io), io, { accountOnly: true });
|
|
9620
|
+
if (action === "list") {
|
|
9621
|
+
const list = await devApi(auth, io, "GET", "/dev-stores");
|
|
9622
|
+
if (list.length === 0) io.out(cli_app_dev_store_empty());
|
|
9623
|
+
for (const store of list) io.out(` ${store.storeId} ${store.storeCode} ${store.name} ${store.status}`);
|
|
9624
|
+
return 0;
|
|
9625
|
+
}
|
|
9626
|
+
if (action === "create") {
|
|
9627
|
+
if (!name?.trim()) throw new CliError(cli_app_dev_store_name_required());
|
|
9628
|
+
const store = await devApi(auth, io, "POST", "/dev-stores", { name: name.trim() });
|
|
9629
|
+
io.out(cli_app_dev_store_created({
|
|
9630
|
+
storeId: store.storeId,
|
|
9631
|
+
name: store.name
|
|
9632
|
+
}));
|
|
9633
|
+
return 0;
|
|
9634
|
+
}
|
|
9635
|
+
throw new CliError(cli_app_subcommand_unknown({ command: `dev-store ${action ?? ""}`.trim() }));
|
|
9636
|
+
}
|
|
9637
|
+
/** `.env`의 `SAYREN_APP_SETTING_*`·`SAYREN_APP_SECRET_*` — 설정은 매니페스트 타입으로 바꾼다 */
|
|
9638
|
+
function localSettingsOf(manifest, env) {
|
|
9639
|
+
const settings = {};
|
|
9640
|
+
const secrets = {};
|
|
9641
|
+
for (const [key, field] of Object.entries(manifest.settings ?? {})) {
|
|
9642
|
+
const raw = env.get(`${SETTING_ENV_PREFIX}${key}`);
|
|
9643
|
+
if (raw === void 0) {
|
|
9644
|
+
if (field.default !== void 0) settings[key] = field.default;
|
|
9645
|
+
continue;
|
|
9646
|
+
}
|
|
9647
|
+
settings[key] = field.type === "number" ? Number(raw) : field.type === "boolean" ? raw === "true" || raw === "1" : raw;
|
|
9648
|
+
}
|
|
9649
|
+
for (const [key, value] of env) if (key.startsWith(SECRET_ENV_PREFIX)) secrets[key.slice(18)] = value;
|
|
9650
|
+
return {
|
|
9651
|
+
settings,
|
|
9652
|
+
secrets
|
|
9653
|
+
};
|
|
9654
|
+
}
|
|
9655
|
+
let devSeq = 0;
|
|
9656
|
+
function devEnvelope(input) {
|
|
9657
|
+
devSeq += 1;
|
|
9658
|
+
return {
|
|
9659
|
+
v: 1,
|
|
9660
|
+
invocationId: `inv_dev_${Date.now().toString(36)}_${devSeq}`,
|
|
9661
|
+
kind: input.kind,
|
|
9662
|
+
installation: {
|
|
9663
|
+
installationId: input.token?.installationId ?? "inst_dev",
|
|
9664
|
+
appId: input.appId,
|
|
9665
|
+
versionNumber: 0,
|
|
9666
|
+
storeId: input.token?.storeId ?? "store_dev",
|
|
9667
|
+
storeName: "(dev)",
|
|
9668
|
+
apiBaseUrl: input.apiBaseUrl
|
|
9669
|
+
},
|
|
9670
|
+
token: input.token?.token ?? null,
|
|
9671
|
+
settings: input.settings,
|
|
9672
|
+
secrets: input.secrets,
|
|
9673
|
+
...input.kind === "http" ? { user: {
|
|
9674
|
+
userId: "user_dev",
|
|
9675
|
+
role: "OWNER"
|
|
9676
|
+
} } : {},
|
|
9677
|
+
...input.event ? { event: {
|
|
9678
|
+
id: `evt_dev_${devSeq}`,
|
|
9679
|
+
type: input.event.type,
|
|
9680
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
9681
|
+
storeId: input.token?.storeId ?? "store_dev",
|
|
9682
|
+
sequence: devSeq,
|
|
9683
|
+
data: input.event.data
|
|
9684
|
+
} } : {},
|
|
9685
|
+
...input.request ? { request: input.request } : {}
|
|
9686
|
+
};
|
|
9687
|
+
}
|
|
9688
|
+
async function invokeLocal(app, envelope) {
|
|
9689
|
+
const res = await app.fetch(new Request("https://app.local/_invoke", {
|
|
9690
|
+
method: "POST",
|
|
9691
|
+
headers: { "content-type": "application/json" },
|
|
9692
|
+
body: JSON.stringify(envelope)
|
|
9693
|
+
}));
|
|
9694
|
+
if (!res.ok) throw new CliError(cli_app_bundle_failed({ tail: await res.text() }));
|
|
9695
|
+
return await res.json();
|
|
9696
|
+
}
|
|
9697
|
+
function printResult(label, result, io) {
|
|
9698
|
+
io.out(`${label} ${result.outcome} ${result.durationMs}ms`);
|
|
9699
|
+
for (const line of result.logs) io.out(` [${line.level}] ${line.message}`);
|
|
9700
|
+
if (result.errorMessage) io.err(` ! ${result.errorMessage}`);
|
|
9701
|
+
}
|
|
9702
|
+
const MIME = {
|
|
9703
|
+
".html": "text/html; charset=utf-8",
|
|
9704
|
+
".js": "text/javascript; charset=utf-8",
|
|
9705
|
+
".mjs": "text/javascript; charset=utf-8",
|
|
9706
|
+
".css": "text/css; charset=utf-8",
|
|
9707
|
+
".json": "application/json",
|
|
9708
|
+
".svg": "image/svg+xml",
|
|
9709
|
+
".png": "image/png",
|
|
9710
|
+
".jpg": "image/jpeg",
|
|
9711
|
+
".ico": "image/x-icon"
|
|
9712
|
+
};
|
|
9713
|
+
function readBody(req) {
|
|
9714
|
+
return new Promise((resolve$1, reject) => {
|
|
9715
|
+
const chunks = [];
|
|
9716
|
+
req.on("data", (chunk) => chunks.push(chunk));
|
|
9717
|
+
req.once("end", () => resolve$1(Buffer.concat(chunks).toString("utf8")));
|
|
9718
|
+
req.once("error", reject);
|
|
9719
|
+
});
|
|
9720
|
+
}
|
|
9721
|
+
/**
|
|
9722
|
+
* 로컬 실행(매뉴얼 「2. 로컬에서 실행하기」) — 번들을 이 프로세스에서 불러 `/_trigger`(이벤트)·`/_http/*`(HTTP 핸들러)·`/`(앱 화면)를 받는다.
|
|
9723
|
+
* 설치 토큰은 `POST …/dev-token`(1시간)으로 받고 만료 전에 다시 받는다. `--store`가 없으면 토큰 없이 돈다(`ctx.api`는 401).
|
|
9724
|
+
*/
|
|
9725
|
+
async function appDev(flags, io) {
|
|
9726
|
+
const dir = appDir(flags, io);
|
|
9727
|
+
const state = requireState(dir);
|
|
9728
|
+
const { manifest } = loadManifest(dir);
|
|
9729
|
+
const { app } = await loadBundle(dir, await bundleServer(dir));
|
|
9730
|
+
const port = Number(flags.port ?? io.env.SAYREN_APP_PORT ?? APP_DEV_PORT);
|
|
9731
|
+
if (!Number.isInteger(port) || port < 1 || port > 65535) throw new CliError(cli_app_port_invalid({ value: String(flags.port) }));
|
|
9732
|
+
const storeId = flags.store?.trim() || io.env.SAYREN_STORE_ID?.trim() || null;
|
|
9733
|
+
const auth = storeId ? await developerAuth(flags, dir, io) : null;
|
|
9734
|
+
let token = null;
|
|
9735
|
+
const ensureToken = async () => {
|
|
9736
|
+
if (!auth || !storeId) return null;
|
|
9737
|
+
const now = io.now?.() ?? Date.now();
|
|
9738
|
+
if (token && Date.parse(token.expiresAt) - now > DEV_TOKEN_REFRESH_MARGIN_MS) return token;
|
|
9739
|
+
token = await devApi(auth, io, "POST", `/apps/${state.appId}/dev-token`, { storeId });
|
|
9740
|
+
return token;
|
|
9741
|
+
};
|
|
9742
|
+
await ensureToken();
|
|
9743
|
+
const local = () => localSettingsOf(manifest, readEnvFile(dir));
|
|
9744
|
+
const apiBaseUrl = `${state.apiOrigin}/v1`;
|
|
9745
|
+
const uiRoot = join(dir, UI_DIR);
|
|
9746
|
+
const handle = async (req, res) => {
|
|
9747
|
+
const url = new URL(req.url ?? "/", `http://localhost:${port}`);
|
|
9748
|
+
try {
|
|
9749
|
+
if (req.method === "POST" && url.pathname === "/_trigger") {
|
|
9750
|
+
const body = JSON.parse(await readBody(req) || "{}");
|
|
9751
|
+
if (!body.type) {
|
|
9752
|
+
res.writeHead(400, { "content-type": "application/json" });
|
|
9753
|
+
res.end(JSON.stringify({ error: "TYPE_REQUIRED" }));
|
|
9754
|
+
return;
|
|
9755
|
+
}
|
|
9756
|
+
const { settings, secrets } = local();
|
|
9757
|
+
const result = await invokeLocal(app, devEnvelope({
|
|
9758
|
+
kind: "event",
|
|
9759
|
+
token: await ensureToken(),
|
|
9760
|
+
apiBaseUrl,
|
|
9761
|
+
appId: state.appId,
|
|
9762
|
+
settings,
|
|
9763
|
+
secrets,
|
|
9764
|
+
event: {
|
|
9765
|
+
type: body.type,
|
|
9766
|
+
data: body.data ?? {}
|
|
9767
|
+
}
|
|
9768
|
+
}));
|
|
9769
|
+
printResult(`[event] ${body.type}`, result, io);
|
|
9770
|
+
res.writeHead(200, { "content-type": "application/json" });
|
|
9771
|
+
res.end(JSON.stringify(result));
|
|
9772
|
+
return;
|
|
9773
|
+
}
|
|
9774
|
+
if (url.pathname.startsWith("/_http/")) {
|
|
9775
|
+
const headers = {};
|
|
9776
|
+
for (const [key, value] of Object.entries(req.headers)) if (typeof value === "string" && key !== "host") headers[key] = value;
|
|
9777
|
+
const method = (req.method ?? "GET").toUpperCase();
|
|
9778
|
+
const { settings, secrets } = local();
|
|
9779
|
+
const result = await invokeLocal(app, devEnvelope({
|
|
9780
|
+
kind: "http",
|
|
9781
|
+
token: await ensureToken(),
|
|
9782
|
+
apiBaseUrl,
|
|
9783
|
+
appId: state.appId,
|
|
9784
|
+
settings,
|
|
9785
|
+
secrets,
|
|
9786
|
+
request: {
|
|
9787
|
+
method,
|
|
9788
|
+
path: `${url.pathname.slice(6)}${url.search}`,
|
|
9789
|
+
headers,
|
|
9790
|
+
body: method === "GET" ? null : await readBody(req)
|
|
9791
|
+
}
|
|
9792
|
+
}));
|
|
9793
|
+
printResult(`[http] ${method} ${url.pathname}`, result, io);
|
|
9794
|
+
if (!result.response) {
|
|
9795
|
+
res.writeHead(500, { "content-type": "application/json" });
|
|
9796
|
+
res.end(JSON.stringify({
|
|
9797
|
+
error: result.outcome,
|
|
9798
|
+
message: result.errorMessage
|
|
9799
|
+
}));
|
|
9800
|
+
return;
|
|
9801
|
+
}
|
|
9802
|
+
res.writeHead(result.response.status, result.response.headers);
|
|
9803
|
+
res.end(result.response.body ?? "");
|
|
9804
|
+
return;
|
|
9805
|
+
}
|
|
9806
|
+
const file = resolve(uiRoot, url.pathname === "/" ? "index.html" : url.pathname.slice(1));
|
|
9807
|
+
if (file.startsWith(uiRoot) && existsSync(file) && statSync(file).isFile()) {
|
|
9808
|
+
res.writeHead(200, { "content-type": MIME[extname(file)] ?? "application/octet-stream" });
|
|
9809
|
+
res.end(readFileSync(file));
|
|
9810
|
+
return;
|
|
9811
|
+
}
|
|
9812
|
+
res.writeHead(404, { "content-type": "text/plain; charset=utf-8" });
|
|
9813
|
+
res.end("not found");
|
|
9814
|
+
} catch (error) {
|
|
9815
|
+
io.err(cli_app_dev_error({ message: error instanceof Error ? error.message : String(error) }));
|
|
9816
|
+
if (!res.headersSent) res.writeHead(500, { "content-type": "application/json" });
|
|
9817
|
+
res.end(JSON.stringify({ error: "DEV_ERROR" }));
|
|
9818
|
+
}
|
|
9819
|
+
};
|
|
9820
|
+
const serverHandle = createServer((req, res) => {
|
|
9821
|
+
handle(req, res);
|
|
9822
|
+
});
|
|
9823
|
+
await new Promise((resolveListen, reject) => {
|
|
9824
|
+
serverHandle.once("error", reject);
|
|
9825
|
+
serverHandle.listen(port, "127.0.0.1", () => resolveListen());
|
|
9826
|
+
});
|
|
9827
|
+
io.out(cli_app_dev_listening({
|
|
9828
|
+
port: String(port),
|
|
9829
|
+
store: storeId ?? "-",
|
|
9830
|
+
events: manifest.events.join(", ") || "-"
|
|
9831
|
+
}));
|
|
9832
|
+
await new Promise((resolveClose) => {
|
|
9833
|
+
const stop = () => {
|
|
9834
|
+
serverHandle.close();
|
|
9835
|
+
resolveClose();
|
|
9836
|
+
};
|
|
9837
|
+
if (io.signal) if (io.signal.aborted) stop();
|
|
9838
|
+
else io.signal.addEventListener("abort", stop, { once: true });
|
|
9839
|
+
else {
|
|
9840
|
+
process.once("SIGINT", stop);
|
|
9841
|
+
process.once("SIGTERM", stop);
|
|
9842
|
+
}
|
|
9843
|
+
});
|
|
9844
|
+
return 0;
|
|
9845
|
+
}
|
|
9846
|
+
async function appTrigger(type, flags, io) {
|
|
9847
|
+
if (!type) throw new CliError(cli_app_trigger_type_required());
|
|
9848
|
+
let data = {};
|
|
9849
|
+
if (flags.data) try {
|
|
9850
|
+
data = JSON.parse(flags.data);
|
|
9851
|
+
} catch {
|
|
9852
|
+
throw new CliError(cli_app_trigger_data_invalid());
|
|
9853
|
+
}
|
|
9854
|
+
if (flags.order) data = {
|
|
9855
|
+
...data,
|
|
9856
|
+
orderId: flags.order
|
|
9857
|
+
};
|
|
9858
|
+
const port = Number(flags.port ?? io.env.SAYREN_APP_PORT ?? APP_DEV_PORT);
|
|
9859
|
+
const res = await io.fetch(`http://127.0.0.1:${port}/_trigger`, {
|
|
9860
|
+
method: "POST",
|
|
9861
|
+
headers: { "content-type": "application/json" },
|
|
9862
|
+
body: JSON.stringify({
|
|
9863
|
+
type,
|
|
9864
|
+
data
|
|
9865
|
+
})
|
|
9866
|
+
});
|
|
9867
|
+
if (!res.ok) throw new CliError(cli_app_trigger_failed({ status: String(res.status) }));
|
|
9868
|
+
const result = await res.json();
|
|
9869
|
+
printResult(`[event] ${type}`, result, io);
|
|
9870
|
+
return result.outcome === "OK" ? 0 : 1;
|
|
9871
|
+
}
|
|
9872
|
+
async function appMain(positional, flags, io) {
|
|
9873
|
+
const [sub, ...rest] = positional;
|
|
9874
|
+
switch (sub) {
|
|
9875
|
+
case "init": return appInit(rest[0], flags, io);
|
|
9876
|
+
case "verify": return appVerify(flags, io);
|
|
9877
|
+
case "deploy": return appDeploy(flags, io);
|
|
9878
|
+
case "release": return appRelease(rest[0], flags, io);
|
|
9879
|
+
case "status": return appStatus(flags, io);
|
|
9880
|
+
case "logs": return appLogs(flags, io);
|
|
9881
|
+
case "secret": return appSecret(rest[0], rest[1], flags, io);
|
|
9882
|
+
case "dev-store": return appDevStore(rest[0], rest.slice(1).join(" ") || void 0, flags, io);
|
|
9883
|
+
case "dev": return appDev(flags, io);
|
|
9884
|
+
case "trigger": return appTrigger(rest[0], flags, io);
|
|
9885
|
+
default: throw new CliError(cli_app_subcommand_unknown({ command: sub ?? "" }));
|
|
9886
|
+
}
|
|
9887
|
+
}
|
|
9888
|
+
|
|
8215
9889
|
//#endregion
|
|
8216
9890
|
//#region ../mcp/src/rules.ts
|
|
8217
9891
|
/** 규칙의 심각도 — 없으면 `error` */
|
|
@@ -8427,6 +10101,16 @@ const RULES = [
|
|
|
8427
10101
|
templateFile: "src/lib/ordering.ts",
|
|
8428
10102
|
severity: "warning"
|
|
8429
10103
|
}),
|
|
10104
|
+
defineRule({
|
|
10105
|
+
id: "product-link",
|
|
10106
|
+
title: mcp_rule_product_link_title,
|
|
10107
|
+
why: mcp_rule_product_link_why,
|
|
10108
|
+
fix: mcp_rule_product_link_fix,
|
|
10109
|
+
example: mcp_rule_product_link_example,
|
|
10110
|
+
doc: "/guides/order-numbers",
|
|
10111
|
+
templateFile: "src/lib/product-path.ts",
|
|
10112
|
+
severity: "warning"
|
|
10113
|
+
}),
|
|
8430
10114
|
defineRule({
|
|
8431
10115
|
id: "server-only-import",
|
|
8432
10116
|
title: mcp_rule_server_only_import_title,
|
|
@@ -8522,6 +10206,8 @@ function verifySources(files, options = {}) {
|
|
|
8522
10206
|
inFile("custom-input-private", file, mcp_verify_custom_input_private({ where: where() }), pattern);
|
|
8523
10207
|
break;
|
|
8524
10208
|
}
|
|
10209
|
+
const productIdLink = /params=\{\{\s*productId:\s*[\w.?]+\.productId\s*\}\}/;
|
|
10210
|
+
if (/\/products\/\$productId/.test(file.content) && productIdLink.test(file.content)) inFile("product-link", file, mcp_verify_product_link(), productIdLink);
|
|
8525
10211
|
const analyticsFactory = /createAnalytics\(/;
|
|
8526
10212
|
if (isServer$1 && analyticsFactory.test(file.content)) inFile("analytics-start", file, mcp_verify_analytics_start_server_file(), analyticsFactory);
|
|
8527
10213
|
if (isServer$1 && /createStorefrontClient\(/.test(file.content) && !/analyticsIdsFromCookie|visitorId/.test(file.content)) inFile("analytics-ids", file, mcp_verify_analytics_ids(), /createStorefrontClient\(/);
|
|
@@ -10075,7 +11761,13 @@ const VALUE_FLAGS = new Set([
|
|
|
10075
11761
|
"api-origin",
|
|
10076
11762
|
"store",
|
|
10077
11763
|
"accounts-origin",
|
|
10078
|
-
"template"
|
|
11764
|
+
"template",
|
|
11765
|
+
"key",
|
|
11766
|
+
"name",
|
|
11767
|
+
"port",
|
|
11768
|
+
"since",
|
|
11769
|
+
"data",
|
|
11770
|
+
"order"
|
|
10079
11771
|
]);
|
|
10080
11772
|
const BOOL_FLAGS = new Set([
|
|
10081
11773
|
"force",
|
|
@@ -10085,7 +11777,8 @@ const BOOL_FLAGS = new Set([
|
|
|
10085
11777
|
"help",
|
|
10086
11778
|
"version",
|
|
10087
11779
|
"device",
|
|
10088
|
-
"paste"
|
|
11780
|
+
"paste",
|
|
11781
|
+
"tail"
|
|
10089
11782
|
]);
|
|
10090
11783
|
function parseArgs(argv) {
|
|
10091
11784
|
const out = {
|
|
@@ -10097,7 +11790,11 @@ function parseArgs(argv) {
|
|
|
10097
11790
|
const arg = argv[i];
|
|
10098
11791
|
if (arg === "-h") out.flags.help = true;
|
|
10099
11792
|
else if (arg === "-v") out.flags.version = true;
|
|
10100
|
-
else if (arg
|
|
11793
|
+
else if (arg === "-m") {
|
|
11794
|
+
const value = argv[++i];
|
|
11795
|
+
if (value === void 0 || value.startsWith("--")) throw new CliError(cli_flag_value_required({ name: "message" }));
|
|
11796
|
+
out.flags.message = value;
|
|
11797
|
+
} else if (arg.startsWith("--")) {
|
|
10101
11798
|
const body = arg.slice(2);
|
|
10102
11799
|
const eq = body.indexOf("=");
|
|
10103
11800
|
const name = eq < 0 ? body : body.slice(0, eq);
|
|
@@ -10142,6 +11839,21 @@ async function main(argv, io) {
|
|
|
10142
11839
|
accountsOrigin: str(parsed.flags["accounts-origin"])
|
|
10143
11840
|
};
|
|
10144
11841
|
switch (parsed.command) {
|
|
11842
|
+
case "app": return appMain(parsed.positional, {
|
|
11843
|
+
token: str(parsed.flags.token),
|
|
11844
|
+
key: str(parsed.flags.key),
|
|
11845
|
+
apiOrigin: str(parsed.flags["api-origin"]),
|
|
11846
|
+
dir: str(parsed.flags.dir),
|
|
11847
|
+
store: str(parsed.flags.store),
|
|
11848
|
+
message: str(parsed.flags.message),
|
|
11849
|
+
name: str(parsed.flags.name),
|
|
11850
|
+
port: str(parsed.flags.port),
|
|
11851
|
+
since: str(parsed.flags.since),
|
|
11852
|
+
tail: parsed.flags.tail === true,
|
|
11853
|
+
data: str(parsed.flags.data),
|
|
11854
|
+
order: str(parsed.flags.order),
|
|
11855
|
+
yes: parsed.flags.yes === true
|
|
11856
|
+
}, io);
|
|
10145
11857
|
case "init": return init({
|
|
10146
11858
|
...common,
|
|
10147
11859
|
template: str(parsed.flags.template),
|