@zalkera/cli 0.31.1 → 0.32.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/README.md CHANGED
@@ -31,7 +31,7 @@ zalkera publish # 올린 것을 새 버전으로 만든다(그래
31
31
  zalkera rollback <판번호> # 라이브를 그 버전으로 되돌린다
32
32
  zalkera discard # 사이트 쪽에서 편집 중인 것을 버린다(판은 안 옮긴다)
33
33
  zalkera baseline # 기준 기록만 다시 세운다(파일은 안 건드린다)
34
- zalkera logout
34
+ zalkera logout # 로그인 정보를 지우고 이 컴퓨터의 서버 쪽 로그인도 끊는다
35
35
  ```
36
36
 
37
37
  `--site` 는 **폴더가 소속을 모를 때만** 필요하다. 한 번 받으면 폴더가 그것을 기억한다.
package/dist/main.js CHANGED
@@ -552,6 +552,7 @@ function escapeHtml(value) {
552
552
  // ../core/dist/auth/oauth.js
553
553
  init_errors();
554
554
  var REFRESH_SKEW_MS = 2e4;
555
+ var REVOKE_TIMEOUT_MS = 5e3;
555
556
  async function login(config, store, options) {
556
557
  const pkce = createPkce();
557
558
  const state = createState();
@@ -633,7 +634,30 @@ async function refreshToken(config, store) {
633
634
  }
634
635
  async function logout(store) {
635
636
  logoutEpoch.set(store, epochOf(store) + 1);
637
+ const current = await store.read().catch(() => null);
636
638
  await store.clear();
639
+ const serverEnded = current?.clientId ? revokeRefreshToken(current, current.clientId) : Promise.resolve(false);
640
+ return { serverEnded };
641
+ }
642
+ async function revokeRefreshToken(tokens, clientId) {
643
+ const issuer = httpUrl(tokens.issuer);
644
+ if (!issuer)
645
+ return false;
646
+ try {
647
+ const response = await fetch(`${trimSlash(issuer.href)}/protocol/openid-connect/revoke`, {
648
+ method: "POST",
649
+ headers: { "content-type": "application/x-www-form-urlencoded" },
650
+ body: new URLSearchParams({
651
+ client_id: clientId,
652
+ token: tokens.refreshToken,
653
+ token_type_hint: "refresh_token"
654
+ }),
655
+ signal: AbortSignal.timeout(REVOKE_TIMEOUT_MS)
656
+ });
657
+ return response.ok;
658
+ } catch {
659
+ return false;
660
+ }
637
661
  }
638
662
  async function exchange(config, params) {
639
663
  const body = new URLSearchParams({ client_id: config.clientId, ...params });
@@ -654,7 +678,8 @@ async function exchange(config, params) {
654
678
  accessToken: token.access_token,
655
679
  refreshToken: token.refresh_token,
656
680
  expiresAt: Date.now() + token.expires_in * 1e3,
657
- issuer: config.issuer
681
+ issuer: config.issuer,
682
+ clientId: config.clientId
658
683
  };
659
684
  }
660
685
  async function safeText(response) {
@@ -3715,7 +3740,9 @@ var FileTokenStore = class {
3715
3740
  accessToken: t.accessToken,
3716
3741
  refreshToken: t.refreshToken,
3717
3742
  expiresAt: t.expiresAt,
3718
- issuer: t.issuer
3743
+ issuer: t.issuer,
3744
+ // 없으면 로그아웃이 서버 세션을 못 끊는다 — 옛 파일은 칸이 없어 로컬만 지운다.
3745
+ ...typeof t.clientId === "string" ? { clientId: t.clientId } : {}
3719
3746
  };
3720
3747
  } catch {
3721
3748
  return null;
@@ -3971,7 +3998,7 @@ function help() {
3971
3998
  return `\uC798\uCEE4\uB77C \u2014 \uC0AC\uC774\uD2B8 \uC18C\uC2A4\uB97C \uB85C\uCEEC\uC5D0\uC11C \uB2E4\uB8E8\uB294 \uB3C4\uAD6C (v${version()})
3972
3999
 
3973
4000
  zalkera login \uBE0C\uB77C\uC6B0\uC800\uB85C \uB85C\uADF8\uC778
3974
- zalkera logout \uB85C\uADF8\uC778 \uC815\uBCF4\uB97C \uC9C0\uC6B4\uB2E4
4001
+ zalkera logout \uB85C\uADF8\uC778 \uC815\uBCF4\uB97C \uC9C0\uC6B0\uACE0 \uC774 \uCEF4\uD4E8\uD130\uC758 \uC11C\uBC84 \uCABD \uB85C\uADF8\uC778\uB3C4 \uB04A\uB294\uB2E4
3975
4002
  zalkera status \uC774 \uD3F4\uB354\uC640 \uC0AC\uC774\uD2B8\uAC00 \uC5B4\uB5BB\uAC8C \uB2E4\uB978\uC9C0 \uBCF8\uB2E4
3976
4003
  zalkera pull \uC0AC\uC774\uD2B8\uC758 \uC9C0\uAE08 \uD310\uC744 \uC774 \uD3F4\uB354\uC5D0 \uBC1B\uB294\uB2E4
3977
4004
  zalkera push \uC774 \uD3F4\uB354\uC5D0\uC11C \uACE0\uCE5C \uAC83\uC744 \uC0AC\uC774\uD2B8 \uCABD\uC5D0 \uC62C\uB9B0\uB2E4(\uCF1C\uC9C0\uC9C0\uB294 \uC54A\uB294\uB2E4)
@@ -4026,8 +4053,12 @@ async function main(argv) {
4026
4053
  return 0;
4027
4054
  }
4028
4055
  case "logout": {
4029
- await logout(new FileTokenStore());
4030
- process.stdout.write("\uB85C\uADF8\uC778 \uC815\uBCF4\uB97C \uC9C0\uC6E0\uC2B5\uB2C8\uB2E4.\n");
4056
+ const store = new FileTokenStore();
4057
+ const hadLogin = await store.read() !== null;
4058
+ const serverEnded = await (await logout(store)).serverEnded;
4059
+ process.stdout.write(
4060
+ !hadLogin ? "\uB85C\uADF8\uC778 \uC815\uBCF4\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4.\n" : serverEnded ? "\uB85C\uADF8\uC778 \uC815\uBCF4\uB97C \uC9C0\uC6B0\uACE0 \uC774 \uAE30\uAE30\uC758 \uC11C\uBC84 \uCABD \uB85C\uADF8\uC778\uB3C4 \uB05D\uB0C8\uC2B5\uB2C8\uB2E4.\n" : "\uB85C\uADF8\uC778 \uC815\uBCF4\uB97C \uC9C0\uC6E0\uC2B5\uB2C8\uB2E4. \uC11C\uBC84 \uCABD \uB85C\uADF8\uC778\uC740 \uB04A\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4(\uC5F0\uACB0 \uC5C6\uC74C\xB7\uC608\uC804 \uB85C\uADF8\uC778).\n"
4061
+ );
4031
4062
  return 0;
4032
4063
  }
4033
4064
  case "status": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zalkera/cli",
3
- "version": "0.31.1",
3
+ "version": "0.32.0",
4
4
  "description": "잘커라 사이트 소스를 터미널에서 받고·올리고·발행합니다. 편집기 없이, 스크립트에서도.",
5
5
  "license": "MIT",
6
6
  "type": "module",