11import { asObject , asString , type JsonValue } from "./api-json.js"
22import type { AuthSnapshot , ProjectAuthSnapshot } from "./menu-types.js"
33
4- const readNumber = ( value : JsonValue | undefined ) : number | null =>
5- typeof value === "number" ? value : null
4+ type RawAuthSnapshot = {
5+ readonly globalEnvPath : string | null
6+ readonly claudeAuthPath : string | null
7+ readonly geminiAuthPath : string | null
8+ readonly totalEntries : number | null
9+ readonly githubTokenEntries : number | null
10+ readonly gitTokenEntries : number | null
11+ readonly gitUserEntries : number | null
12+ readonly claudeAuthEntries : number | null
13+ readonly geminiAuthEntries : number | null
14+ }
15+
16+ type RawProjectAuthSnapshot = {
17+ readonly projectDir : string | null
18+ readonly projectName : string | null
19+ readonly envGlobalPath : string | null
20+ readonly envProjectPath : string | null
21+ readonly claudeAuthPath : string | null
22+ readonly geminiAuthPath : string | null
23+ readonly githubTokenEntries : number | null
24+ readonly gitTokenEntries : number | null
25+ readonly claudeAuthEntries : number | null
26+ readonly geminiAuthEntries : number | null
27+ readonly activeGithubLabel : string | null
28+ readonly activeGitLabel : string | null
29+ readonly activeClaudeLabel : string | null
30+ readonly activeGeminiLabel : string | null
31+ }
32+
33+ const readNumber = ( value : JsonValue | undefined ) : number | null => typeof value === "number" ? value : null
34+ const stringOrEmpty = ( value : string | null ) : string => value ?? ""
35+ const numberOrZero = ( value : number | null ) : number => value ?? 0
36+ const hasNullValue = (
37+ values : ReadonlyArray < string | number | null >
38+ ) : boolean => values . includes ( null )
639
740const resolveSnapshotObject = ( payload : JsonValue ) => {
841 const object = asObject ( payload )
942 return asObject ( object ?. [ "snapshot" ] ?? payload )
1043}
1144
12- export const decodeAuthSnapshot = ( payload : JsonValue ) : AuthSnapshot | null => {
13- const snapshot = resolveSnapshotObject ( payload )
45+ const readAuthSnapshot = (
46+ snapshot : ReturnType < typeof resolveSnapshotObject >
47+ ) : RawAuthSnapshot | null => {
1448 if ( snapshot === null ) {
1549 return null
1650 }
1751
18- const decoded = {
52+ return {
1953 globalEnvPath : asString ( snapshot [ "globalEnvPath" ] ) ,
2054 claudeAuthPath : asString ( snapshot [ "claudeAuthPath" ] ) ,
2155 geminiAuthPath : asString ( snapshot [ "geminiAuthPath" ] ) ,
@@ -26,29 +60,34 @@ export const decodeAuthSnapshot = (payload: JsonValue): AuthSnapshot | null => {
2660 claudeAuthEntries : readNumber ( snapshot [ "claudeAuthEntries" ] ) ,
2761 geminiAuthEntries : readNumber ( snapshot [ "geminiAuthEntries" ] )
2862 }
63+ }
64+
65+ const decodeRequiredAuthSnapshot = ( snapshot : RawAuthSnapshot ) : AuthSnapshot | null => {
66+ if ( hasNullValue ( Object . values ( snapshot ) ) ) {
67+ return null
68+ }
2969
30- return Object . values ( decoded ) . includes ( null )
31- ? null
32- : {
33- globalEnvPath : decoded . globalEnvPath ?? "" ,
34- claudeAuthPath : decoded . claudeAuthPath ?? "" ,
35- geminiAuthPath : decoded . geminiAuthPath ?? "" ,
36- totalEntries : decoded . totalEntries ?? 0 ,
37- githubTokenEntries : decoded . githubTokenEntries ?? 0 ,
38- gitTokenEntries : decoded . gitTokenEntries ?? 0 ,
39- gitUserEntries : decoded . gitUserEntries ?? 0 ,
40- claudeAuthEntries : decoded . claudeAuthEntries ?? 0 ,
41- geminiAuthEntries : decoded . geminiAuthEntries ?? 0
42- }
70+ return {
71+ globalEnvPath : stringOrEmpty ( snapshot . globalEnvPath ) ,
72+ claudeAuthPath : stringOrEmpty ( snapshot . claudeAuthPath ) ,
73+ geminiAuthPath : stringOrEmpty ( snapshot . geminiAuthPath ) ,
74+ totalEntries : numberOrZero ( snapshot . totalEntries ) ,
75+ githubTokenEntries : numberOrZero ( snapshot . githubTokenEntries ) ,
76+ gitTokenEntries : numberOrZero ( snapshot . gitTokenEntries ) ,
77+ gitUserEntries : numberOrZero ( snapshot . gitUserEntries ) ,
78+ claudeAuthEntries : numberOrZero ( snapshot . claudeAuthEntries ) ,
79+ geminiAuthEntries : numberOrZero ( snapshot . geminiAuthEntries )
80+ }
4381}
4482
45- export const decodeProjectAuthSnapshot = ( payload : JsonValue ) : ProjectAuthSnapshot | null => {
46- const snapshot = resolveSnapshotObject ( payload )
83+ const readProjectAuthSnapshot = (
84+ snapshot : ReturnType < typeof resolveSnapshotObject >
85+ ) : RawProjectAuthSnapshot | null => {
4786 if ( snapshot === null ) {
4887 return null
4988 }
5089
51- const decoded = {
90+ return {
5291 projectDir : asString ( snapshot [ "projectDir" ] ) ,
5392 projectName : asString ( snapshot [ "projectName" ] ) ,
5493 envGlobalPath : asString ( snapshot [ "envGlobalPath" ] ) ,
@@ -64,37 +103,54 @@ export const decodeProjectAuthSnapshot = (payload: JsonValue): ProjectAuthSnapsh
64103 activeClaudeLabel : asString ( snapshot [ "activeClaudeLabel" ] ) ,
65104 activeGeminiLabel : asString ( snapshot [ "activeGeminiLabel" ] )
66105 }
106+ }
67107
108+ const decodeRequiredProjectAuthSnapshot = (
109+ snapshot : RawProjectAuthSnapshot
110+ ) : ProjectAuthSnapshot | null => {
68111 const requiredValues = [
69- decoded . projectDir ,
70- decoded . projectName ,
71- decoded . envGlobalPath ,
72- decoded . envProjectPath ,
73- decoded . claudeAuthPath ,
74- decoded . geminiAuthPath ,
75- decoded . githubTokenEntries ,
76- decoded . gitTokenEntries ,
77- decoded . claudeAuthEntries ,
78- decoded . geminiAuthEntries
112+ snapshot . projectDir ,
113+ snapshot . projectName ,
114+ snapshot . envGlobalPath ,
115+ snapshot . envProjectPath ,
116+ snapshot . claudeAuthPath ,
117+ snapshot . geminiAuthPath ,
118+ snapshot . githubTokenEntries ,
119+ snapshot . gitTokenEntries ,
120+ snapshot . claudeAuthEntries ,
121+ snapshot . geminiAuthEntries
79122 ]
80- if ( requiredValues . includes ( null ) ) {
123+
124+ if ( hasNullValue ( requiredValues ) ) {
81125 return null
82126 }
83127
84128 return {
85- projectDir : decoded . projectDir ?? "" ,
86- projectName : decoded . projectName ?? "" ,
87- envGlobalPath : decoded . envGlobalPath ?? "" ,
88- envProjectPath : decoded . envProjectPath ?? "" ,
89- claudeAuthPath : decoded . claudeAuthPath ?? "" ,
90- geminiAuthPath : decoded . geminiAuthPath ?? "" ,
91- githubTokenEntries : decoded . githubTokenEntries ?? 0 ,
92- gitTokenEntries : decoded . gitTokenEntries ?? 0 ,
93- claudeAuthEntries : decoded . claudeAuthEntries ?? 0 ,
94- geminiAuthEntries : decoded . geminiAuthEntries ?? 0 ,
95- activeGithubLabel : decoded . activeGithubLabel ,
96- activeGitLabel : decoded . activeGitLabel ,
97- activeClaudeLabel : decoded . activeClaudeLabel ,
98- activeGeminiLabel : decoded . activeGeminiLabel
129+ projectDir : stringOrEmpty ( snapshot . projectDir ) ,
130+ projectName : stringOrEmpty ( snapshot . projectName ) ,
131+ envGlobalPath : stringOrEmpty ( snapshot . envGlobalPath ) ,
132+ envProjectPath : stringOrEmpty ( snapshot . envProjectPath ) ,
133+ claudeAuthPath : stringOrEmpty ( snapshot . claudeAuthPath ) ,
134+ geminiAuthPath : stringOrEmpty ( snapshot . geminiAuthPath ) ,
135+ githubTokenEntries : numberOrZero ( snapshot . githubTokenEntries ) ,
136+ gitTokenEntries : numberOrZero ( snapshot . gitTokenEntries ) ,
137+ claudeAuthEntries : numberOrZero ( snapshot . claudeAuthEntries ) ,
138+ geminiAuthEntries : numberOrZero ( snapshot . geminiAuthEntries ) ,
139+ activeGithubLabel : snapshot . activeGithubLabel ,
140+ activeGitLabel : snapshot . activeGitLabel ,
141+ activeClaudeLabel : snapshot . activeClaudeLabel ,
142+ activeGeminiLabel : snapshot . activeGeminiLabel
99143 }
100144}
145+
146+ export const decodeAuthSnapshot = ( payload : JsonValue ) : AuthSnapshot | null => {
147+ const snapshot = readAuthSnapshot ( resolveSnapshotObject ( payload ) )
148+ return snapshot === null ? null : decodeRequiredAuthSnapshot ( snapshot )
149+ }
150+
151+ export const decodeProjectAuthSnapshot = (
152+ payload : JsonValue
153+ ) : ProjectAuthSnapshot | null => {
154+ const snapshot = readProjectAuthSnapshot ( resolveSnapshotObject ( payload ) )
155+ return snapshot === null ? null : decodeRequiredProjectAuthSnapshot ( snapshot )
156+ }
0 commit comments