@@ -31,102 +31,52 @@ type GlobalStore = {
3131 reload : undefined | "pending" | "complete"
3232}
3333
34- function waitForPaint ( ) {
35- return new Promise < void > ( ( resolve ) => {
36- let done = false
37- const finish = ( ) => {
38- if ( done ) return
39- done = true
40- resolve ( )
41- }
42- const timer = setTimeout ( finish , 50 )
43- if ( typeof requestAnimationFrame !== "function" ) return
44- requestAnimationFrame ( ( ) => {
45- clearTimeout ( timer )
46- finish ( )
47- } )
48- } )
49- }
50-
51- function errors ( list : PromiseSettledResult < unknown > [ ] ) {
52- return list . filter ( ( item ) : item is PromiseRejectedResult => item . status === "rejected" ) . map ( ( item ) => item . reason )
53- }
54-
55- function runAll ( list : Array < ( ) => Promise < unknown > > ) {
56- return Promise . allSettled ( list . map ( ( item ) => item ( ) ) )
57- }
58-
59- function showErrors ( input : {
60- errors : unknown [ ]
61- title : string
62- translate : ( key : string , vars ?: Record < string , string | number > ) => string
63- formatMoreCount : ( count : number ) => string
64- } ) {
65- if ( input . errors . length === 0 ) return
66- const message = formatServerError ( input . errors [ 0 ] , input . translate )
67- const more = input . errors . length > 1 ? input . formatMoreCount ( input . errors . length - 1 ) : ""
68- showToast ( {
69- variant : "error" ,
70- title : input . title ,
71- description : message + more ,
72- } )
73- }
74-
7534export async function bootstrapGlobal ( input : {
7635 globalSDK : OpencodeClient
7736 requestFailedTitle : string
7837 translate : ( key : string , vars ?: Record < string , string | number > ) => string
7938 formatMoreCount : ( count : number ) => string
8039 setGlobalStore : SetStoreFunction < GlobalStore >
8140} ) {
82- const fast = [
83- ( ) =>
84- retry ( ( ) =>
85- input . globalSDK . path . get ( ) . then ( ( x ) => {
86- input . setGlobalStore ( "path" , x . data ! )
87- } ) ,
88- ) ,
89- ( ) =>
90- retry ( ( ) =>
91- input . globalSDK . global . config . get ( ) . then ( ( x ) => {
92- input . setGlobalStore ( "config" , x . data ! )
93- } ) ,
94- ) ,
95- ( ) =>
96- retry ( ( ) =>
97- input . globalSDK . provider . list ( ) . then ( ( x ) => {
98- input . setGlobalStore ( "provider" , normalizeProviderList ( x . data ! ) )
99- } ) ,
100- ) ,
41+ const tasks = [
42+ retry ( ( ) =>
43+ input . globalSDK . path . get ( ) . then ( ( x ) => {
44+ input . setGlobalStore ( "path" , x . data ! )
45+ } ) ,
46+ ) ,
47+ retry ( ( ) =>
48+ input . globalSDK . global . config . get ( ) . then ( ( x ) => {
49+ input . setGlobalStore ( "config" , x . data ! )
50+ } ) ,
51+ ) ,
52+ retry ( ( ) =>
53+ input . globalSDK . project . list ( ) . then ( ( x ) => {
54+ const projects = ( x . data ?? [ ] )
55+ . filter ( ( p ) => ! ! p ?. id )
56+ . filter ( ( p ) => ! ! p . worktree && ! p . worktree . includes ( "opencode-test" ) )
57+ . slice ( )
58+ . sort ( ( a , b ) => cmp ( a . id , b . id ) )
59+ input . setGlobalStore ( "project" , projects )
60+ } ) ,
61+ ) ,
62+ retry ( ( ) =>
63+ input . globalSDK . provider . list ( ) . then ( ( x ) => {
64+ input . setGlobalStore ( "provider" , normalizeProviderList ( x . data ! ) )
65+ } ) ,
66+ ) ,
10167 ]
10268
103- const slow = [
104- ( ) =>
105- retry ( ( ) =>
106- input . globalSDK . project . list ( ) . then ( ( x ) => {
107- const projects = ( x . data ?? [ ] )
108- . filter ( ( p ) => ! ! p ?. id )
109- . filter ( ( p ) => ! ! p . worktree && ! p . worktree . includes ( "opencode-test" ) )
110- . slice ( )
111- . sort ( ( a , b ) => cmp ( a . id , b . id ) )
112- input . setGlobalStore ( "project" , projects )
113- } ) ,
114- ) ,
115- ]
116-
117- showErrors ( {
118- errors : errors ( await runAll ( fast ) ) ,
119- title : input . requestFailedTitle ,
120- translate : input . translate ,
121- formatMoreCount : input . formatMoreCount ,
122- } )
123- await waitForPaint ( )
124- showErrors ( {
125- errors : errors ( await runAll ( slow ) ) ,
126- title : input . requestFailedTitle ,
127- translate : input . translate ,
128- formatMoreCount : input . formatMoreCount ,
129- } )
69+ const results = await Promise . allSettled ( tasks )
70+ const errors = results . filter ( ( r ) : r is PromiseRejectedResult => r . status === "rejected" ) . map ( ( r ) => r . reason )
71+ if ( errors . length ) {
72+ const message = formatServerError ( errors [ 0 ] , input . translate )
73+ const more = errors . length > 1 ? input . formatMoreCount ( errors . length - 1 ) : ""
74+ showToast ( {
75+ variant : "error" ,
76+ title : input . requestFailedTitle ,
77+ description : message + more ,
78+ } )
79+ }
13080 input . setGlobalStore ( "ready" , true )
13181}
13282
@@ -169,113 +119,95 @@ export async function bootstrapDirectory(input: {
169119 }
170120 if ( loading ) input . setStore ( "status" , "partial" )
171121
172- const fast = [
173- ( ) =>
174- seededProject
175- ? Promise . resolve ( )
176- : retry ( ( ) => input . sdk . project . current ( ) ) . then ( ( x ) => input . setStore ( "project" , x . data ! . id ) ) ,
177- ( ) => retry ( ( ) => input . sdk . app . agents ( ) . then ( ( x ) => input . setStore ( "agent" , x . data ?? [ ] ) ) ) ,
178- ( ) => retry ( ( ) => input . sdk . config . get ( ) . then ( ( x ) => input . setStore ( "config" , x . data ! ) ) ) ,
179- ( ) =>
180- retry ( ( ) =>
181- input . sdk . path . get ( ) . then ( ( x ) => {
182- input . setStore ( "path" , x . data ! )
183- const next = projectID ( x . data ?. directory ?? input . directory , input . global . project )
184- if ( next ) input . setStore ( "project" , next )
185- } ) ,
186- ) ,
187- ( ) => retry ( ( ) => input . sdk . session . status ( ) . then ( ( x ) => input . setStore ( "session_status" , x . data ! ) ) ) ,
188- ( ) =>
189- retry ( ( ) =>
190- input . sdk . vcs . get ( ) . then ( ( x ) => {
191- const next = x . data ?? input . store . vcs
192- input . setStore ( "vcs" , next )
193- if ( next ?. branch ) input . vcsCache . setStore ( "value" , next )
194- } ) ,
195- ) ,
196- ( ) => retry ( ( ) => input . sdk . command . list ( ) . then ( ( x ) => input . setStore ( "command" , x . data ?? [ ] ) ) ) ,
197- ( ) =>
198- retry ( ( ) =>
199- input . sdk . permission . list ( ) . then ( ( x ) => {
200- const grouped = groupBySession (
201- ( x . data ?? [ ] ) . filter ( ( perm ) : perm is PermissionRequest => ! ! perm ?. id && ! ! perm . sessionID ) ,
202- )
203- batch ( ( ) => {
204- for ( const sessionID of Object . keys ( input . store . permission ) ) {
205- if ( grouped [ sessionID ] ) continue
206- input . setStore ( "permission" , sessionID , [ ] )
207- }
208- for ( const [ sessionID , permissions ] of Object . entries ( grouped ) ) {
209- input . setStore (
210- "permission" ,
211- sessionID ,
212- reconcile (
213- permissions . filter ( ( p ) => ! ! p ?. id ) . sort ( ( a , b ) => cmp ( a . id , b . id ) ) ,
214- { key : "id" } ,
215- ) ,
216- )
217- }
218- } )
219- } ) ,
220- ) ,
221- ( ) =>
222- retry ( ( ) =>
223- input . sdk . question . list ( ) . then ( ( x ) => {
224- const grouped = groupBySession ( ( x . data ?? [ ] ) . filter ( ( q ) : q is QuestionRequest => ! ! q ?. id && ! ! q . sessionID ) )
225- batch ( ( ) => {
226- for ( const sessionID of Object . keys ( input . store . question ) ) {
227- if ( grouped [ sessionID ] ) continue
228- input . setStore ( "question" , sessionID , [ ] )
229- }
230- for ( const [ sessionID , questions ] of Object . entries ( grouped ) ) {
231- input . setStore (
232- "question" ,
233- sessionID ,
234- reconcile (
235- questions . filter ( ( q ) => ! ! q ?. id ) . sort ( ( a , b ) => cmp ( a . id , b . id ) ) ,
236- { key : "id" } ,
237- ) ,
238- )
239- }
240- } )
241- } ) ,
242- ) ,
243- ]
244-
245- const slow = [
246- ( ) =>
247- retry ( ( ) =>
248- input . sdk . provider . list ( ) . then ( ( x ) => {
249- input . setStore ( "provider" , normalizeProviderList ( x . data ! ) )
250- } ) ,
251- ) ,
252- ( ) => Promise . resolve ( input . loadSessions ( input . directory ) ) ,
253- ( ) => retry ( ( ) => input . sdk . mcp . status ( ) . then ( ( x ) => input . setStore ( "mcp" , x . data ! ) ) ) ,
254- ( ) => retry ( ( ) => input . sdk . lsp . status ( ) . then ( ( x ) => input . setStore ( "lsp" , x . data ! ) ) ) ,
255- ]
256-
257- const errs = errors ( await runAll ( fast ) )
258- if ( errs . length > 0 ) {
259- console . error ( "Failed to bootstrap instance" , errs [ 0 ] )
260- const project = getFilename ( input . directory )
261- showToast ( {
262- variant : "error" ,
263- title : input . translate ( "toast.project.reloadFailed.title" , { project } ) ,
264- description : formatServerError ( errs [ 0 ] , input . translate ) ,
265- } )
266- }
267-
268- await waitForPaint ( )
269- const slowErrs = errors ( await runAll ( slow ) )
270- if ( slowErrs . length > 0 ) {
271- console . error ( "Failed to finish bootstrap instance" , slowErrs [ 0 ] )
122+ const results = await Promise . allSettled ( [
123+ seededProject
124+ ? Promise . resolve ( )
125+ : retry ( ( ) => input . sdk . project . current ( ) ) . then ( ( x ) => input . setStore ( "project" , x . data ! . id ) ) ,
126+ retry ( ( ) =>
127+ input . sdk . provider . list ( ) . then ( ( x ) => {
128+ input . setStore ( "provider" , normalizeProviderList ( x . data ! ) )
129+ } ) ,
130+ ) ,
131+ retry ( ( ) => input . sdk . app . agents ( ) . then ( ( x ) => input . setStore ( "agent" , x . data ?? [ ] ) ) ) ,
132+ retry ( ( ) => input . sdk . config . get ( ) . then ( ( x ) => input . setStore ( "config" , x . data ! ) ) ) ,
133+ retry ( ( ) =>
134+ input . sdk . path . get ( ) . then ( ( x ) => {
135+ input . setStore ( "path" , x . data ! )
136+ const next = projectID ( x . data ?. directory ?? input . directory , input . global . project )
137+ if ( next ) input . setStore ( "project" , next )
138+ } ) ,
139+ ) ,
140+ retry ( ( ) => input . sdk . command . list ( ) . then ( ( x ) => input . setStore ( "command" , x . data ?? [ ] ) ) ) ,
141+ retry ( ( ) => input . sdk . session . status ( ) . then ( ( x ) => input . setStore ( "session_status" , x . data ! ) ) ) ,
142+ input . loadSessions ( input . directory ) ,
143+ retry ( ( ) => input . sdk . mcp . status ( ) . then ( ( x ) => input . setStore ( "mcp" , x . data ! ) ) ) ,
144+ retry ( ( ) => input . sdk . lsp . status ( ) . then ( ( x ) => input . setStore ( "lsp" , x . data ! ) ) ) ,
145+ retry ( ( ) =>
146+ input . sdk . vcs . get ( ) . then ( ( x ) => {
147+ const next = x . data ?? input . store . vcs
148+ input . setStore ( "vcs" , next )
149+ if ( next ?. branch ) input . vcsCache . setStore ( "value" , next )
150+ } ) ,
151+ ) ,
152+ retry ( ( ) =>
153+ input . sdk . permission . list ( ) . then ( ( x ) => {
154+ const grouped = groupBySession (
155+ ( x . data ?? [ ] ) . filter ( ( perm ) : perm is PermissionRequest => ! ! perm ?. id && ! ! perm . sessionID ) ,
156+ )
157+ batch ( ( ) => {
158+ for ( const sessionID of Object . keys ( input . store . permission ) ) {
159+ if ( grouped [ sessionID ] ) continue
160+ input . setStore ( "permission" , sessionID , [ ] )
161+ }
162+ for ( const [ sessionID , permissions ] of Object . entries ( grouped ) ) {
163+ input . setStore (
164+ "permission" ,
165+ sessionID ,
166+ reconcile (
167+ permissions . filter ( ( p ) => ! ! p ?. id ) . sort ( ( a , b ) => cmp ( a . id , b . id ) ) ,
168+ { key : "id" } ,
169+ ) ,
170+ )
171+ }
172+ } )
173+ } ) ,
174+ ) ,
175+ retry ( ( ) =>
176+ input . sdk . question . list ( ) . then ( ( x ) => {
177+ const grouped = groupBySession ( ( x . data ?? [ ] ) . filter ( ( q ) : q is QuestionRequest => ! ! q ?. id && ! ! q . sessionID ) )
178+ batch ( ( ) => {
179+ for ( const sessionID of Object . keys ( input . store . question ) ) {
180+ if ( grouped [ sessionID ] ) continue
181+ input . setStore ( "question" , sessionID , [ ] )
182+ }
183+ for ( const [ sessionID , questions ] of Object . entries ( grouped ) ) {
184+ input . setStore (
185+ "question" ,
186+ sessionID ,
187+ reconcile (
188+ questions . filter ( ( q ) => ! ! q ?. id ) . sort ( ( a , b ) => cmp ( a . id , b . id ) ) ,
189+ { key : "id" } ,
190+ ) ,
191+ )
192+ }
193+ } )
194+ } ) ,
195+ ) ,
196+ ] )
197+
198+ const errors = results
199+ . filter ( ( item ) : item is PromiseRejectedResult => item . status === "rejected" )
200+ . map ( ( item ) => item . reason )
201+ if ( errors . length > 0 ) {
202+ console . error ( "Failed to bootstrap instance" , errors [ 0 ] )
272203 const project = getFilename ( input . directory )
273204 showToast ( {
274205 variant : "error" ,
275206 title : input . translate ( "toast.project.reloadFailed.title" , { project } ) ,
276- description : formatServerError ( slowErrs [ 0 ] , input . translate ) ,
207+ description : formatServerError ( errors [ 0 ] , input . translate ) ,
277208 } )
209+ return
278210 }
279211
280- if ( loading && errs . length === 0 && slowErrs . length === 0 ) input . setStore ( "status" , "complete" )
212+ if ( loading ) input . setStore ( "status" , "complete" )
281213}
0 commit comments