Compare commits
	
		
			1 Commits
		
	
	
		
			kurt-refac
			...
			frank-xsta
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| ac64b9fa45 | 
| @ -26,7 +26,7 @@ | |||||||
|     "@types/react-dom": "^18.0.0", |     "@types/react-dom": "^18.0.0", | ||||||
|     "@uiw/react-codemirror": "^4.21.20", |     "@uiw/react-codemirror": "^4.21.20", | ||||||
|     "@xstate/inspect": "^0.8.0", |     "@xstate/inspect": "^0.8.0", | ||||||
|     "@xstate/react": "^3.2.2", |     "@xstate/react": "^4.1.0", | ||||||
|     "crypto-js": "^4.2.0", |     "crypto-js": "^4.2.0", | ||||||
|     "debounce-promise": "^3.1.2", |     "debounce-promise": "^3.1.2", | ||||||
|     "formik": "^2.4.3", |     "formik": "^2.4.3", | ||||||
| @ -50,7 +50,7 @@ | |||||||
|     "three": "^0.160.0", |     "three": "^0.160.0", | ||||||
|     "toml": "^3.0.0", |     "toml": "^3.0.0", | ||||||
|     "ts-node": "^10.9.1", |     "ts-node": "^10.9.1", | ||||||
|     "typescript": "^5.2.2", |     "typescript": "^5.4.2", | ||||||
|     "uuid": "^9.0.1", |     "uuid": "^9.0.1", | ||||||
|     "vitest": "^1.3.1", |     "vitest": "^1.3.1", | ||||||
|     "vscode-jsonrpc": "^8.1.0", |     "vscode-jsonrpc": "^8.1.0", | ||||||
| @ -58,7 +58,7 @@ | |||||||
|     "wasm-pack": "^0.12.1", |     "wasm-pack": "^0.12.1", | ||||||
|     "web-vitals": "^3.5.0", |     "web-vitals": "^3.5.0", | ||||||
|     "ws": "^8.13.0", |     "ws": "^8.13.0", | ||||||
|     "xstate": "^4.38.2", |     "xstate": "^5.9.1", | ||||||
|     "zustand": "^4.4.5" |     "zustand": "^4.4.5" | ||||||
|   }, |   }, | ||||||
|   "scripts": { |   "scripts": { | ||||||
|  | |||||||
| @ -1,12 +1,11 @@ | |||||||
|  | import { AppMachineContext } from 'machines/appMachine' | ||||||
| import Loading from './components/Loading' | import Loading from './components/Loading' | ||||||
| import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext' |  | ||||||
|  |  | ||||||
| // Wrapper around protected routes, used in src/Router.tsx | // Wrapper around protected routes, used in src/Router.tsx | ||||||
| export const Auth = ({ children }: React.PropsWithChildren) => { | export const Auth = ({ children }: React.PropsWithChildren) => { | ||||||
|   const { auth } = useSettingsAuthContext() |   const isAuthenticating = AppMachineContext.useSelector(s => s.matches('Loading')) | ||||||
|   const isLoggingIn = auth?.state.matches('checkIfLoggedIn') |  | ||||||
|  |  | ||||||
|   return isLoggingIn ? ( |   return isAuthenticating ? ( | ||||||
|     <Loading> |     <Loading> | ||||||
|       <span data-testid="initial-load">Loading Modeling App...</span> |       <span data-testid="initial-load">Loading Modeling App...</span> | ||||||
|     </Loading> |     </Loading> | ||||||
|  | |||||||
| @ -16,8 +16,6 @@ import makeUrlPathRelative from './lib/makeUrlPathRelative' | |||||||
| import DownloadAppBanner from 'components/DownloadAppBanner' | import DownloadAppBanner from 'components/DownloadAppBanner' | ||||||
| import { WasmErrBanner } from 'components/WasmErrBanner' | import { WasmErrBanner } from 'components/WasmErrBanner' | ||||||
| import { CommandBar } from 'components/CommandBar/CommandBar' | import { CommandBar } from 'components/CommandBar/CommandBar' | ||||||
| import ModelingMachineProvider from 'components/ModelingMachineProvider' |  | ||||||
| import FileMachineProvider from 'components/FileMachineProvider' |  | ||||||
| import { paths } from 'lib/paths' | import { paths } from 'lib/paths' | ||||||
| import { | import { | ||||||
|   fileLoader, |   fileLoader, | ||||||
| @ -25,10 +23,7 @@ import { | |||||||
|   indexLoader, |   indexLoader, | ||||||
|   onboardingRedirectLoader, |   onboardingRedirectLoader, | ||||||
| } from 'lib/routeLoaders' | } from 'lib/routeLoaders' | ||||||
| import { CommandBarProvider } from 'components/CommandBar/CommandBarProvider' | import { AppMachineProvider } from 'components/AppMachineProvider' | ||||||
| import SettingsAuthProvider from 'components/SettingsAuthProvider' |  | ||||||
| import LspProvider from 'components/LspProvider' |  | ||||||
| import { KclContextProvider } from 'lang/KclSingleton' |  | ||||||
|  |  | ||||||
| export const BROWSER_FILE_NAME = 'new' | export const BROWSER_FILE_NAME = 'new' | ||||||
|  |  | ||||||
| @ -37,15 +32,9 @@ const router = createBrowserRouter([ | |||||||
|     loader: indexLoader, |     loader: indexLoader, | ||||||
|     id: paths.INDEX, |     id: paths.INDEX, | ||||||
|     element: ( |     element: ( | ||||||
|       <CommandBarProvider> |       <AppMachineProvider> | ||||||
|         <KclContextProvider> |         <Outlet /> | ||||||
|           <SettingsAuthProvider> |       </AppMachineProvider> | ||||||
|             <LspProvider> |  | ||||||
|               <Outlet /> |  | ||||||
|             </LspProvider> |  | ||||||
|           </SettingsAuthProvider> |  | ||||||
|         </KclContextProvider> |  | ||||||
|       </CommandBarProvider> |  | ||||||
|     ), |     ), | ||||||
|     children: [ |     children: [ | ||||||
|       { |       { | ||||||
| @ -61,14 +50,10 @@ const router = createBrowserRouter([ | |||||||
|         id: paths.FILE, |         id: paths.FILE, | ||||||
|         element: ( |         element: ( | ||||||
|           <Auth> |           <Auth> | ||||||
|             <FileMachineProvider> |             <Outlet /> | ||||||
|               <ModelingMachineProvider> |             <App /> | ||||||
|                 <Outlet /> |             <CommandBar /> | ||||||
|                 <App /> |             <WasmErrBanner /> | ||||||
|                 <CommandBar /> |  | ||||||
|               </ModelingMachineProvider> |  | ||||||
|               <WasmErrBanner /> |  | ||||||
|             </FileMachineProvider> |  | ||||||
|             {!isTauri() && import.meta.env.PROD && <DownloadAppBanner />} |             {!isTauri() && import.meta.env.PROD && <DownloadAppBanner />} | ||||||
|           </Auth> |           </Auth> | ||||||
|         ), |         ), | ||||||
|  | |||||||
							
								
								
									
										21
									
								
								src/components/AppMachineProvider.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								src/components/AppMachineProvider.tsx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,21 @@ | |||||||
|  | import { AppMachineContext } from 'machines/appMachine' | ||||||
|  | import { PropsWithChildren } from 'react' | ||||||
|  | import { useLocation, useNavigate } from 'react-router-dom' | ||||||
|  |  | ||||||
|  | export const AppMachineProvider = ({ children }: PropsWithChildren) => { | ||||||
|  |   const navigate = useNavigate() | ||||||
|  |   const location = useLocation() | ||||||
|  |  | ||||||
|  |   return ( | ||||||
|  |     <AppMachineContext.Provider | ||||||
|  |       options={{ | ||||||
|  |         input: { | ||||||
|  |           navigate, | ||||||
|  |           location, | ||||||
|  |         }, | ||||||
|  |       }} | ||||||
|  |     > | ||||||
|  |       {children} | ||||||
|  |     </AppMachineContext.Provider> | ||||||
|  |   ) | ||||||
|  | } | ||||||
| @ -13,6 +13,7 @@ import { | |||||||
| } from 'components/NetworkHealthIndicator' | } from 'components/NetworkHealthIndicator' | ||||||
| import { useKclContext } from 'lang/KclSingleton' | import { useKclContext } from 'lang/KclSingleton' | ||||||
| import { useStore } from 'useStore' | import { useStore } from 'useStore' | ||||||
|  | import { AppMachineContext } from 'machines/appMachine' | ||||||
|  |  | ||||||
| // This might not be necessary, AnyStateMachine from xstate is working | // This might not be necessary, AnyStateMachine from xstate is working | ||||||
| export type AllMachines = | export type AllMachines = | ||||||
| @ -46,7 +47,7 @@ export default function useStateMachineCommands< | |||||||
|   allCommandsRequireNetwork = false, |   allCommandsRequireNetwork = false, | ||||||
|   onCancel, |   onCancel, | ||||||
| }: UseStateMachineCommandsArgs<T, S>) { | }: UseStateMachineCommandsArgs<T, S>) { | ||||||
|   const { commandBarSend } = useCommandsContext() |   const commandsActorRef = AppMachineContext.useSelector(s => s.children.commands) | ||||||
|   const { overallState } = useNetworkStatus() |   const { overallState } = useNetworkStatus() | ||||||
|   const { isExecuting } = useKclContext() |   const { isExecuting } = useKclContext() | ||||||
|   const { isStreamReady } = useStore((s) => ({ |   const { isStreamReady } = useStore((s) => ({ | ||||||
| @ -72,10 +73,10 @@ export default function useStateMachineCommands< | |||||||
|       ) |       ) | ||||||
|       .filter((c) => c !== null) as Command[] // TS isn't smart enough to know this filter removes nulls |       .filter((c) => c !== null) as Command[] // TS isn't smart enough to know this filter removes nulls | ||||||
|  |  | ||||||
|     commandBarSend({ type: 'Add commands', data: { commands: newCommands } }) |     commandsActorRef?.send({ type: 'Add commands', data: { commands: newCommands } }) | ||||||
|  |  | ||||||
|     return () => { |     return () => { | ||||||
|       commandBarSend({ |       commandsActorRef?.send({ | ||||||
|         type: 'Remove commands', |         type: 'Remove commands', | ||||||
|         data: { commands: newCommands }, |         data: { commands: newCommands }, | ||||||
|       }) |       }) | ||||||
|  | |||||||
							
								
								
									
										264
									
								
								src/machines/appMachine.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										264
									
								
								src/machines/appMachine.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,264 @@ | |||||||
|  | import { ProjectWithEntryPointMetadata } from 'lib/types' | ||||||
|  | import { assign, fromCallback, fromPromise, setup } from 'xstate' | ||||||
|  | import { settingsMachine } from './settingsMachine' | ||||||
|  | import { commandBarMachine } from './commandBarMachine' | ||||||
|  | import { homeMachine } from './homeMachine' | ||||||
|  | import { modelingMachine } from './modelingMachine' | ||||||
|  | import { fileMachine } from './fileMachine' | ||||||
|  | import { useLocation, useNavigate } from 'react-router-dom' | ||||||
|  | import { TOKEN_PERSIST_KEY, getUser, persistedToken } from './authMachine' | ||||||
|  | import { Models } from '@kittycad/lib/dist/types/src' | ||||||
|  | import { createActorContext } from '@xstate/react' | ||||||
|  | import { paths } from 'lib/paths' | ||||||
|  | import { isTauri } from 'lib/isTauri' | ||||||
|  | import { sep } from '@tauri-apps/api/path' | ||||||
|  |  | ||||||
|  | export const appMachine = setup({ | ||||||
|  |   types: { | ||||||
|  |     context: {} as { | ||||||
|  |       token?: string | ||||||
|  |       user?: Models['User_type'] | ||||||
|  |       currentProject?: ProjectWithEntryPointMetadata | ||||||
|  |       currentFile?: string | ||||||
|  |       navigate: ReturnType<typeof useNavigate> | ||||||
|  |       location: ReturnType<typeof useLocation> | ||||||
|  |     }, | ||||||
|  |     events: {} as | ||||||
|  |       | { type: 'Sign out' } | ||||||
|  |       | { | ||||||
|  |           type: 'Open project' | ||||||
|  |           data: { | ||||||
|  |             project: ProjectWithEntryPointMetadata | ||||||
|  |             file: string | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |       | { type: 'Close project' } | ||||||
|  |       | { type: 'AuthActor.done'; output: Models['User_type'] }, | ||||||
|  |     input: {} as { | ||||||
|  |       navigate: ReturnType<typeof useNavigate> | ||||||
|  |       location: ReturnType<typeof useLocation> | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  |   actions: { | ||||||
|  |     'Clear currentProject and currentFile': assign({ | ||||||
|  |       currentProject: undefined, | ||||||
|  |       currentFile: undefined, | ||||||
|  |     }), | ||||||
|  |     'Set currentProject': assign({ | ||||||
|  |       currentProject: ({ event, context }) => | ||||||
|  |         event.type === 'Open project' | ||||||
|  |           ? event.data.project | ||||||
|  |           : context.currentProject, | ||||||
|  |     }), | ||||||
|  |     'Set currentFile': assign({ | ||||||
|  |       currentFile: ({ event, context }) => | ||||||
|  |         event.type === 'Open project' ? event.data.file : context.currentFile, | ||||||
|  |     }), | ||||||
|  |     'Delete auth state': () => { | ||||||
|  |       localStorage.removeItem(TOKEN_PERSIST_KEY) | ||||||
|  |       assign({ token: undefined }) | ||||||
|  |     }, | ||||||
|  |     'Go to sign in page': ({ context }) => { | ||||||
|  |       context.navigate(paths.SIGN_IN) | ||||||
|  |     }, | ||||||
|  |     'Navigate home': ({ context }) => { | ||||||
|  |       context.navigate(paths.INDEX) | ||||||
|  |     }, | ||||||
|  |     'Navigate to file': ({ context, event }) => { | ||||||
|  |       if (event.type !== 'Open project') return | ||||||
|  |       context.navigate( | ||||||
|  |         paths.FILE + | ||||||
|  |           event.data.project.path + | ||||||
|  |           (isTauri() ? sep : '/') + | ||||||
|  |           event.data.file | ||||||
|  |       ) | ||||||
|  |     }, | ||||||
|  |     'Assign user': assign({ | ||||||
|  |       user: ({ event, context }) => | ||||||
|  |         event.type === 'AuthActor.done' ? event.output : context.user, | ||||||
|  |     }), | ||||||
|  |     'Persist auth state': ({ context }) => { | ||||||
|  |       localStorage.setItem(TOKEN_PERSIST_KEY, context.token || '') | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  |   actors: { | ||||||
|  |     SettingsActor: settingsMachine, | ||||||
|  |     EngineConnectionActor: fromCallback(({ sendBack, receive, input }) => { | ||||||
|  |       // TODO implement actor | ||||||
|  |     }), | ||||||
|  |     CommandBarMachine: commandBarMachine, | ||||||
|  |     LspActor: fromCallback(({ sendBack, receive }) => { | ||||||
|  |       // TODO implement actor | ||||||
|  |     }), | ||||||
|  |     HomeMachine: homeMachine, | ||||||
|  |     ModelingMachine: modelingMachine, | ||||||
|  |     ClientSideSceneActor: fromCallback(({ sendBack, receive }) => { | ||||||
|  |       // TODO implement actor | ||||||
|  |     }), | ||||||
|  |     ProjectMachine: fileMachine, | ||||||
|  |     KclActor: fromCallback(({ sendBack, receive }) => { | ||||||
|  |       // TODO implement actor | ||||||
|  |     }), | ||||||
|  |     AuthActor: fromPromise( | ||||||
|  |       ({ input }: { input: { token: string | undefined } }) => | ||||||
|  |         getUser(input.token) | ||||||
|  |     ), | ||||||
|  |   }, | ||||||
|  |   schemas: { | ||||||
|  |     events: { | ||||||
|  |       'Sign out': { | ||||||
|  |         type: 'object', | ||||||
|  |         properties: {}, | ||||||
|  |       }, | ||||||
|  |       'Open project': { | ||||||
|  |         type: 'object', | ||||||
|  |         properties: {}, | ||||||
|  |       }, | ||||||
|  |       'Close project': { | ||||||
|  |         type: 'object', | ||||||
|  |         properties: {}, | ||||||
|  |       }, | ||||||
|  |       '': { | ||||||
|  |         type: 'object', | ||||||
|  |         properties: {}, | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  | }).createMachine({ | ||||||
|  |   /** @xstate-layout N4IgpgJg5mDOIC5QEEAOqAEBZAhgYwAsBLAOzADoBlIqMiDUgYmtowHsBXAFwG0AGALqJQqNrCJcibEsJAAPRADYA7AEZyAFgCcG1QGZVygKyKAHMq3KANCACeiAExH1WvUY2Lnejct0a9AL4BNmiYuISkFCx0DCTkABJsALZgjADyqGAkGKgATmwAVmB4vIKyouKS0rIKCNp85A7KTnxqGq3m5jb2CMqKeuSWWqYjfKoeRmNBIejY+MRkVDQxpOQACvlFJeyZJIwAwgA2YmA5m8WlQkggFRJSMte19ZqqRsp6fHqKfIqq-UbdJRmcjKPjOZymVRaMHvaYgUJzCKLAAybBwEFIUEYEGkFFIADc2ABrCg4bgEfhXERiO7VR6IPTecimLRGYbDIymTyqUyAhAORSKcitVw8zx6Bx6ZpwhHhBYUVHozGMMC5fK5cioQ44LgAMzYuSS5DJXApZWutyqD1AtSMDgc5E8fW0ouUfQcfIcP00GhZpl90PeziCwRAJDYEDgsll80i5RpVpqiAAtEZGhpJkY3F8+KZc74+ao+Bphe1-VL-Vm+PaZbM5ZElrRILF45V7km6g7jNoPB49FooaoBXyNA5TMKVHxi99jO5VLWwrHFtFm6tEilW7TrfJENCQe4dIo+wOB3o+YphsLWu0s8NfK4F4j5Y2VnENoULjsspvE-Tev2r3GAV70FFRz3aRpWiPHwtCcBxVHnUMYyRKJlmbTguB-ds-yHBDyCze1OhUe1jE9PN8OcdpvFeBCL0fesUTRDESCgLC6RtRBJkGJoswQ0EuXaaw7EQQdyC+KVQW+b4Pg0EMAiAA */ | ||||||
|  |   context: ({ input }) => ({ | ||||||
|  |     token: persistedToken, | ||||||
|  |     navigate: input.navigate, | ||||||
|  |     location: input.location, | ||||||
|  |   }), | ||||||
|  |   id: 'App Machine', | ||||||
|  |   initial: 'Loading', | ||||||
|  |   states: { | ||||||
|  |     'Signed in': { | ||||||
|  |       initial: 'Home', | ||||||
|  |       on: { | ||||||
|  |         'Sign out': { | ||||||
|  |           target: 'Signed out', | ||||||
|  |           actions: [ | ||||||
|  |             { | ||||||
|  |               type: 'Delete auth state', | ||||||
|  |             }, | ||||||
|  |             { | ||||||
|  |               type: 'Go to sign in page', | ||||||
|  |             }, | ||||||
|  |           ], | ||||||
|  |         }, | ||||||
|  |       }, | ||||||
|  |       invoke: [ | ||||||
|  |         { | ||||||
|  |           id: 'settings', | ||||||
|  |           systemId: 'settings', | ||||||
|  |           input: {}, | ||||||
|  |           src: 'SettingsActor', | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |           id: 'engine', | ||||||
|  |           systemId: 'engine', | ||||||
|  |           input: {}, | ||||||
|  |           src: 'EngineConnectionActor', | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |           id: 'commands', | ||||||
|  |           systemId: 'commands', | ||||||
|  |           input: {}, | ||||||
|  |           src: 'CommandBarMachine', | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |           id: 'lsp', | ||||||
|  |           systemId: 'lsp', | ||||||
|  |           input: {}, | ||||||
|  |           src: 'LspActor', | ||||||
|  |         }, | ||||||
|  |       ], | ||||||
|  |       states: { | ||||||
|  |         Home: { | ||||||
|  |           on: { | ||||||
|  |             'Open project': { | ||||||
|  |               target: 'Project open', | ||||||
|  |               actions: { | ||||||
|  |                 type: 'Navigate to file', | ||||||
|  |               }, | ||||||
|  |             }, | ||||||
|  |           }, | ||||||
|  |           entry: { | ||||||
|  |             type: 'Clear currentProject and currentFile', | ||||||
|  |           }, | ||||||
|  |           invoke: { | ||||||
|  |             id: 'home', | ||||||
|  |             input: {}, | ||||||
|  |             src: 'HomeMachine', | ||||||
|  |           }, | ||||||
|  |         }, | ||||||
|  |         'Project open': { | ||||||
|  |           on: { | ||||||
|  |             'Close project': { | ||||||
|  |               target: 'Home', | ||||||
|  |               actions: { | ||||||
|  |                 type: 'Navigate home', | ||||||
|  |               }, | ||||||
|  |             }, | ||||||
|  |           }, | ||||||
|  |           entry: [ | ||||||
|  |             { | ||||||
|  |               type: 'Set currentProject', | ||||||
|  |             }, | ||||||
|  |             { | ||||||
|  |               type: 'Set currentFile', | ||||||
|  |             }, | ||||||
|  |           ], | ||||||
|  |           invoke: [ | ||||||
|  |             { | ||||||
|  |               id: 'modeling', | ||||||
|  |               systemId: 'modeling', | ||||||
|  |               input: {}, | ||||||
|  |               src: 'ModelingMachine', | ||||||
|  |             }, | ||||||
|  |             { | ||||||
|  |               input: {}, | ||||||
|  |               src: 'ClientSideSceneActor', | ||||||
|  |             }, | ||||||
|  |             { | ||||||
|  |               id: 'project', | ||||||
|  |               systemId: 'project', | ||||||
|  |               input: {}, | ||||||
|  |               src: 'ProjectMachine', | ||||||
|  |             }, | ||||||
|  |             { | ||||||
|  |               input: {}, | ||||||
|  |               src: 'KclActor', | ||||||
|  |             }, | ||||||
|  |           ], | ||||||
|  |         }, | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |  | ||||||
|  |     'Signed out': { | ||||||
|  |       type: 'final', | ||||||
|  |     }, | ||||||
|  |  | ||||||
|  |     Loading: { | ||||||
|  |       invoke: { | ||||||
|  |         src: 'AuthActor', | ||||||
|  |         id: 'auth', | ||||||
|  |         input: ({ context: { token } }) => ({ token }), | ||||||
|  |         onDone: { | ||||||
|  |           target: 'Signed in', | ||||||
|  |           actions: [ | ||||||
|  |             assign({ | ||||||
|  |               user: ({ event }) => event.output, | ||||||
|  |             }), | ||||||
|  |             'Persist auth state', | ||||||
|  |           ], | ||||||
|  |         }, | ||||||
|  |         onError: 'Signed out', | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  | }) | ||||||
|  |  | ||||||
|  | export const AppMachineContext = createActorContext(appMachine) | ||||||
| @ -1,4 +1,4 @@ | |||||||
| import { createMachine, assign } from 'xstate' | import { createMachine, assign, setup, fromPromise } from 'xstate' | ||||||
| import { Models } from '@kittycad/lib' | import { Models } from '@kittycad/lib' | ||||||
| import withBaseURL from '../lib/withBaseURL' | import withBaseURL from '../lib/withBaseURL' | ||||||
| import { isTauri } from 'lib/isTauri' | import { isTauri } from 'lib/isTauri' | ||||||
| @ -39,86 +39,82 @@ export type Events = | |||||||
|     } |     } | ||||||
|  |  | ||||||
| export const TOKEN_PERSIST_KEY = 'TOKEN_PERSIST_KEY' | export const TOKEN_PERSIST_KEY = 'TOKEN_PERSIST_KEY' | ||||||
| const persistedToken = | export const persistedToken = | ||||||
|   localStorage?.getItem(TOKEN_PERSIST_KEY) || |   localStorage?.getItem(TOKEN_PERSIST_KEY) || | ||||||
|   getCookie('__Secure-next-auth.session-token') || |   getCookie('__Secure-next-auth.session-token') || | ||||||
|   '' |   undefined | ||||||
|  |  | ||||||
| export const authMachine = createMachine<UserContext, Events>( | export const authMachine = setup({ | ||||||
|   { |   types: { | ||||||
|     id: 'Auth', |     events: {} as { type: 'Log out' } | { type: 'Log in' }, | ||||||
|     initial: 'checkIfLoggedIn', |     context: {} as UserContext, | ||||||
|     states: { |   }, | ||||||
|       checkIfLoggedIn: { |   actors: { | ||||||
|         id: 'check-if-logged-in', |     fetchUser: fromPromise(({ input }: { input: { token: string | undefined }}) => getUser(input.token)), | ||||||
|         invoke: { |   }, | ||||||
|           src: 'getUser', |   actions: { | ||||||
|           id: 'check-logged-in', |     goToIndexPage: () => ({}), | ||||||
|           onDone: [ |     goToSignInPage: () => ({}), | ||||||
|             { |   } | ||||||
|               target: 'loggedIn', | }).createMachine({ | ||||||
|               actions: assign({ |   id: 'Auth', | ||||||
|                 user: (context, event) => event.data, |   preserveActionOrder: true, | ||||||
|               }), |   initial: 'checkIfLoggedIn', | ||||||
|             }, |   context: { | ||||||
|           ], |     token: persistedToken, | ||||||
|           onError: [ |   }, | ||||||
|             { |   states: { | ||||||
|               target: 'loggedOut', |     checkIfLoggedIn: { | ||||||
|               actions: assign({ |       id: 'check-if-logged-in', | ||||||
|                 user: () => undefined, |       invoke: { | ||||||
|               }), |         id: 'getUser', | ||||||
|             }, |         src: 'fetchUser', | ||||||
|           ], |         input: ({ context: { token } }) => ({ token }), | ||||||
|         }, |         onDone: [ | ||||||
|       }, |           { | ||||||
|       loggedIn: { |             target: 'loggedIn', | ||||||
|         entry: ['goToIndexPage'], |  | ||||||
|         on: { |  | ||||||
|           'Log out': { |  | ||||||
|             target: 'loggedOut', |  | ||||||
|           }, |  | ||||||
|         }, |  | ||||||
|       }, |  | ||||||
|       loggedOut: { |  | ||||||
|         entry: ['goToSignInPage'], |  | ||||||
|         on: { |  | ||||||
|           'Log in': { |  | ||||||
|             target: 'checkIfLoggedIn', |  | ||||||
|             actions: assign({ |             actions: assign({ | ||||||
|               token: (_, event) => { |               user: ({ event }) => event.output, | ||||||
|                 const token = event.token || '' |  | ||||||
|                 localStorage.setItem(TOKEN_PERSIST_KEY, token) |  | ||||||
|                 return token |  | ||||||
|               }, |  | ||||||
|             }), |             }), | ||||||
|           }, |           }, | ||||||
|  |         ], | ||||||
|  |         onError: [ | ||||||
|  |           { | ||||||
|  |             target: 'loggedOut', | ||||||
|  |             actions: assign({ | ||||||
|  |               user: () => undefined, | ||||||
|  |             }), | ||||||
|  |           }, | ||||||
|  |         ], | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |     loggedIn: { | ||||||
|  |       entry: ['goToIndexPage'], | ||||||
|  |       on: { | ||||||
|  |         'Log out': { | ||||||
|  |           target: 'loggedOut', | ||||||
|         }, |         }, | ||||||
|       }, |       }, | ||||||
|     }, |     }, | ||||||
|     schema: { events: {} as { type: 'Log out' } | { type: 'Log in' } }, |     loggedOut: { | ||||||
|     predictableActionArguments: true, |       entry: { type: 'goToIndexPage' }, | ||||||
|     preserveActionOrder: true, |       on: { | ||||||
|     context: { |         'Log in': { | ||||||
|       token: persistedToken, |           target: 'checkIfLoggedIn', | ||||||
|  |         }, | ||||||
|  |       }, | ||||||
|     }, |     }, | ||||||
|   }, |   }, | ||||||
|   { | }) | ||||||
|     actions: {}, |  | ||||||
|     services: { getUser }, |  | ||||||
|     guards: {}, |  | ||||||
|     delays: {}, |  | ||||||
|   } |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| async function getUser(context: UserContext) { | export async function getUser(token?: string) { | ||||||
|   const url = withBaseURL('/user') |   const url = withBaseURL('/user') | ||||||
|   const headers: { [key: string]: string } = { |   const headers: { [key: string]: string } = { | ||||||
|     'Content-Type': 'application/json', |     'Content-Type': 'application/json', | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   if (!context.token && isTauri()) throw new Error('No token found') |   if (!token && isTauri()) throw new Error('No token found') | ||||||
|   if (context.token) headers['Authorization'] = `Bearer ${context.token}` |   if (token) headers['Authorization'] = `Bearer ${token}` | ||||||
|   if (SKIP_AUTH) return LOCAL_USER |   if (SKIP_AUTH) return LOCAL_USER | ||||||
|  |  | ||||||
|   const userPromise = !isTauri() |   const userPromise = !isTauri() | ||||||
| @ -130,7 +126,7 @@ async function getUser(context: UserContext) { | |||||||
|         .then((res) => res.json()) |         .then((res) => res.json()) | ||||||
|         .catch((err) => console.error('error from Browser getUser', err)) |         .catch((err) => console.error('error from Browser getUser', err)) | ||||||
|     : invoke<Models['User_type'] | Record<'error_code', unknown>>('get_user', { |     : invoke<Models['User_type'] | Record<'error_code', unknown>>('get_user', { | ||||||
|         token: context.token, |         token: token, | ||||||
|         hostname: VITE_KC_API_BASE_URL, |         hostname: VITE_KC_API_BASE_URL, | ||||||
|       }).catch((err) => console.error('error from Tauri getUser', err)) |       }).catch((err) => console.error('error from Tauri getUser', err)) | ||||||
|  |  | ||||||
| @ -138,10 +134,10 @@ async function getUser(context: UserContext) { | |||||||
|  |  | ||||||
|   if ('error_code' in user) throw new Error(user.message) |   if ('error_code' in user) throw new Error(user.message) | ||||||
|  |  | ||||||
|   return user |   return user as Models['User_type'] | ||||||
| } | } | ||||||
|  |  | ||||||
| function getCookie(cname: string): string { | export function getCookie(cname: string): string { | ||||||
|   if (isTauri()) { |   if (isTauri()) { | ||||||
|     return '' |     return '' | ||||||
|   } |   } | ||||||
|  | |||||||
| @ -1,28 +1,54 @@ | |||||||
| import { assign, createMachine } from 'xstate' | import { assign, createMachine, fromPromise, setup } from 'xstate' | ||||||
| import { type ProjectWithEntryPointMetadata } from 'lib/types' | import { type ProjectWithEntryPointMetadata } from 'lib/types' | ||||||
| import { HomeCommandSchema } from 'lib/commandBarConfigs/homeCommandConfig' | import { HomeCommandSchema } from 'lib/commandBarConfigs/homeCommandConfig' | ||||||
|  | import { useNavigate } from 'react-router-dom' | ||||||
|  |  | ||||||
| export const homeMachine = createMachine( | export const homeMachine = setup({ | ||||||
|  |   types: { | ||||||
|  |     context: {} as { | ||||||
|  |       projects: ProjectWithEntryPointMetadata[] | ||||||
|  |       defaultProjectName: string | ||||||
|  |       defaultDirectory: string | ||||||
|  |       navigate: ReturnType<typeof useNavigate> | ||||||
|  |     }, | ||||||
|  |     events: {} as | ||||||
|  |       | { type: 'Open project'; data: HomeCommandSchema['Open project'] } | ||||||
|  |       | { type: 'Rename project'; data: HomeCommandSchema['Rename project'] } | ||||||
|  |       | { type: 'Create project'; data: HomeCommandSchema['Create project'] } | ||||||
|  |       | { type: 'Delete project'; data: HomeCommandSchema['Delete project'] }, | ||||||
|  |     input: {} as { | ||||||
|  |       navigate: ReturnType<typeof useNavigate> | ||||||
|  |       defaultProjectName: string | ||||||
|  |       defaultDirectory: string | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  |   actions: {} as { toastSuccess: () => void; toastError: () => void }, | ||||||
|  |   actors: { | ||||||
|  |     readProjects: fromPromise(async () => { | ||||||
|  |       return [] | ||||||
|  |     }), | ||||||
|  |     createProject: fromPromise(async () => { | ||||||
|  |       return | ||||||
|  |     }), | ||||||
|  |     renameProject: fromPromise(async () => { | ||||||
|  |       return | ||||||
|  |     }), | ||||||
|  |     deleteProject: fromPromise(async () => { | ||||||
|  |       return | ||||||
|  |     }), | ||||||
|  |   }, | ||||||
|  | }).createMachine( | ||||||
|   { |   { | ||||||
|     /** @xstate-layout N4IgpgJg5mDOIC5QAkD2BbMACdBDAxgBYCWAdmAHTK6xampYAOATqgFZj4AusAxAMLMwuLthbtOXANoAGALqJQjVLGJdiqUopAAPRAHYAbPooAWABwBGUwE5zAJgeGArM-MAaEAE9EN0wGYKGX97GX1nGVNDS0MbfwBfeM80TBwCEnIqGiZWDm4+ACUwUlxU8TzpeW1lVXVNbT0EcJNg02d-fzt7fU77Tx8EQ0iKCPtnfUsjGRtLGXtE5IxsPCIySmpacsk+QWFRHIluWQUkEBq1DS1TxqN7ChjzOxtXf0t7a37EcwsRibH-ZzRezA8wLEApZbpNZZTa5ba8AAiYAANmB9lsjlVTuc6ldQDdDOYKP5bm0os5TDJDJ8mlEzPpzIZHA4bO9umCIWlVpkNgcKnwAPKMYp8yTHaoqC71a6IEmBUz6BkWZzWDq2Uw0qzOIJAwz+PXWfSmeZJcFLLkZSi7ERkKCi7i8CCaShkABuqAA1pR8EIRGAALQYyonJSS3ENRDA2wUeyvd6dPVhGw0-RhGOp8IA8xGFkc80rS0Ua3qUh2oO8MDMVjMCiMZEiABmqGY6AoPr2AaD4uxYcuEYQoQpQWNNjsMnMgLGKbT3TC7TcOfsNjzqQL0KKJXQtvtXEdzoobs9lCEm87cMxIbOvel+MQqtMQRmS5ks31sZpAUsZkcIX+cQZJIrpC3KUBupTbuWlbVrW9ZcE2LYUCepRnocwYSrUfYyggbzvBQ+jMq49imLYwTUt4iCft+5i-u0-7UfoQEWtCSKoiWZbnruTqZIeXoUBAKJoihFTdqGGE3rod7UdqsQTI8hiGAqrIauRA7RvYeoqhO1jtAqjFrpkLFohBHEVlWzYwY2zatvxrFCWKWKiVKeISdh4yBJE-jGs4fhhA4zg0kRNgxhplhaW0nn4XpUKZEUuAQMZqF8FxLqkO6vG+hAgYcbAIlXmJzmNERdy0RYNiKgpthxDSEU6q8MSTJYjWGFFIEULF8WljuSX7jxx7CJlQY5ZYl44pht4IP61gyPc8njt0lIuH51UKrVVITEyMy2C1hbtQl-KmdBdaWQhGVZYluWjeJjSTf402shMEyuEyljPAFL0UNmMiuN86lWHMiSmvQ-HwKcnL6WA6FOf2k3mESMRDA4RpUm4U4qf6gSEt0QIvvqfjOCaiyrtF6zZPQXWQ+GWFlUEsbmNMf1TV9NLeXDcqRIySnNaaYPEzC5M9vl-b+IyFCjupryPF9jKWP5Kks-cbMWLERHRNt0LFntkgU2NLk4dqsz43YsTK++Kk2C+MbTOOcxzOMrhqzFxTgZ1Qba1dd6BUE1jGsLMxxK9KlDNqm3tMLUQvqYlgO5QhlsTubsFXesTTUuPTfHExshDS0RftRftGgEnTZtHbX9Zr+QJ-2S4Y3qnmTC+4tMyp1EfeOnmeQqdOhyXQrFOXXCV1hCkmLDOnBJYvRRDSsyRzGjiKj0lKdAkANAA */ |     /** @xstate-layout N4IgpgJg5mDOIC5QAkD2BbMACdBDAxgBYCWAdmAMS6yzFSkDaADALqKgAOqtALsaqXYgAHogAsAJgA0IAJ6IJATgAcAOgBsAdgCsYzYoCMEsdu3LNAXwsy0mHARLlVyallKosHAE6oAVmHweWAoAYS8wXB5sbz8AnmY2JBAuXn5BJNEEAzF1MVVdXUV1JnVtCXUVGXkECW0DVTEAZgNlRvUDbINS5SsbDGw8IjIwZ1cY-0DggCUwUlw7cbiEoRTiPgEhTI6O-KZtJnMzOs0dKsRNVtU9xqbG5Wzc7V6QWwGHYdHYTx8JoNDwyLRH5LVgrbhrNKbRAPPIFMRFEplCrKM4IXL1MRdCq1C7KRSKRrPV72IZOFxfRaTCgAETAABswFFvrFAsskqt1ulQFscrCTPDiqVypU5OdlNpVBJlMUzLkVCcJET+iTHCNyczfsEAPIcWYakGJTjgzlQrK8-L8hFC5Go1oS7aFfSKJpKuyDVWqMIRPikKD6wIUCACEZkABuqAA1iN8ACogBaSnxUHs42QjLQ81wq1IkXVEzqVTSlQlJgGRoEgyut6kkZeyJkP2JihgLw+LyqDh0yIAM1QXnQqhj3rACeBrOTRtSG3TZtKheUTFLEnL2hXYlt9UaEiUBmdYjx6illesL2V7o+Mzm6Ab-p4geDqjDkZG4SvI8TbMnEOn3MQjR0hb4uoFSNGUkhMIoqKrnkByHioigQUoRRViqF6zPMN5Ni2bYdl2PC9v2qivvM75jkmhrJKmP4iH+AF4kUIFgRIEGomImKFkYyiSNo6h3EYigoeeTi0gyPqNmR95OE+UaqBA9KMqRLLkWCU5cjRs4SkwzH3Ioq56eYUHwqo2RboeoHOgYmjqIJ7zCfJYm3s2rZ9rhPZ9gOcmiYpvyfpRqmmg8BagauoHSroXHrqKaIElcRiHlxjRMCuTwnsSQkjDMuAQJhZHBEGUmkOGMkAhAo5KbAvkcmmv4IIozHGXUpYVGxByNKiFRMKoJz4icOSGGYYg2TWqiZdlvq3nlD7SS+ESlYmFUGBRVXUZkdWdQYjVdHurWos0ErLolJxcR05hWUNHqjTl5VOThnZuYRJVlZqlVUWpq31RtBhNdtrSotuEiqLpRR6IelklMeJ7uHJ8BJGltlgCp35vYgcbqKiqPnR86ruBNiMmjOnGaA0pilJomI6FKf11Pk+KtPiJTOkwg2pWe8OfLjKb+QTShqN1bHKOUOQC21UWaJZXV6V0G1MJoUopX0bps3WDmJnj1XqWDm73CYuiy0Ypiol9BZ4gN4raDomh8ZjTiXhh42q5zSOmpIUE3PkvH7KU7QmPo1sjCJjJXb8asrdCmilqofE63oEj69oUEVF1i6x7LBzStZLOK8Nl327lIfIwgZiKF1Nw3GWShJXsu31ZbiUErXuRnZn1YejqsxB3E+cBeHWvZKYMdxxu9TSvuYhNdKjR3FYVhAA */ | ||||||
|     id: 'Home machine', |     id: 'Home machine', | ||||||
|  |  | ||||||
|     initial: 'Reading projects', |     initial: 'Reading projects', | ||||||
|  |  | ||||||
|     context: { |     context: ({ input }) => ({ | ||||||
|       projects: [] as ProjectWithEntryPointMetadata[], |       projects: [] as ProjectWithEntryPointMetadata[], | ||||||
|       defaultProjectName: '', |       ...input, | ||||||
|       defaultDirectory: '', |     }), | ||||||
|     }, |  | ||||||
|  |  | ||||||
|     on: { |  | ||||||
|       assign: { |  | ||||||
|         actions: assign((_, event) => ({ |  | ||||||
|           ...event.data, |  | ||||||
|         })), |  | ||||||
|         target: '.Reading projects', |  | ||||||
|       }, |  | ||||||
|     }, |  | ||||||
|     states: { |     states: { | ||||||
|       'Has no projects': { |       'Has no projects': { | ||||||
|         on: { |         on: { | ||||||
| @ -135,30 +161,9 @@ export const homeMachine = createMachine( | |||||||
|         entry: ['navigateToProject'], |         entry: ['navigateToProject'], | ||||||
|       }, |       }, | ||||||
|     }, |     }, | ||||||
|  |  | ||||||
|     schema: { |  | ||||||
|       events: {} as |  | ||||||
|         | { type: 'Open project'; data: HomeCommandSchema['Open project'] } |  | ||||||
|         | { type: 'Rename project'; data: HomeCommandSchema['Rename project'] } |  | ||||||
|         | { type: 'Create project'; data: HomeCommandSchema['Create project'] } |  | ||||||
|         | { type: 'Delete project'; data: HomeCommandSchema['Delete project'] } |  | ||||||
|         | { type: 'navigate'; data: { name: string } } |  | ||||||
|         | { |  | ||||||
|             type: 'done.invoke.read-projects' |  | ||||||
|             data: ProjectWithEntryPointMetadata[] |  | ||||||
|           } |  | ||||||
|         | { type: 'assign'; data: { [key: string]: any } }, |  | ||||||
|     }, |     }, | ||||||
|  |  | ||||||
|     predictableActionArguments: true, |     predictableActionArguments: true, | ||||||
|     preserveActionOrder: true, |     preserveActionOrder: true, | ||||||
|     tsTypes: {} as import('./homeMachine.typegen').Typegen0, |  | ||||||
|   }, |   }, | ||||||
|   { |  | ||||||
|     actions: { |  | ||||||
|       setProjects: assign((_, event) => { |  | ||||||
|         return { projects: event.data as ProjectWithEntryPointMetadata[] } |  | ||||||
|       }), |  | ||||||
|     }, |  | ||||||
|   } |  | ||||||
| ) | ) | ||||||
|  | |||||||
| @ -39,12 +39,17 @@ import { isTauri } from 'lib/isTauri' | |||||||
| import { kclManager } from 'lang/KclSingleton' | import { kclManager } from 'lang/KclSingleton' | ||||||
| import { useLspContext } from 'components/LspProvider' | import { useLspContext } from 'components/LspProvider' | ||||||
| import { useValidateSettings } from 'hooks/useValidateSettings' | import { useValidateSettings } from 'hooks/useValidateSettings' | ||||||
|  | import { AppMachineContext } from 'machines/appMachine' | ||||||
|  |  | ||||||
| // This route only opens in the Tauri desktop context for now, | // This route only opens in the Tauri desktop context for now, | ||||||
| // as defined in Router.tsx, so we can use the Tauri APIs and types. | // as defined in Router.tsx, so we can use the Tauri APIs and types. | ||||||
| const Home = () => { | const Home = () => { | ||||||
|   useValidateSettings() |   useValidateSettings() | ||||||
|   const { commandBarSend } = useCommandsContext() |   const home = AppMachineContext.useSelector(s => s.children.home) | ||||||
|  |   home?.on('done.invoke.read-projects', (e) => { | ||||||
|  |     // do some nonsense | ||||||
|  |   }) | ||||||
|  |   const commands = AppMachineContext.useSelector(s => s.children.commands) | ||||||
|   const navigate = useNavigate() |   const navigate = useNavigate() | ||||||
|   const { projects: loadedProjects } = useLoaderData() as HomeLoaderData |   const { projects: loadedProjects } = useLoaderData() as HomeLoaderData | ||||||
|   const { |   const { | ||||||
|  | |||||||
							
								
								
									
										23
									
								
								yarn.lock
									
									
									
									
									
								
							
							
						
						
									
										23
									
								
								yarn.lock
									
									
									
									
									
								
							| @ -2868,10 +2868,10 @@ | |||||||
|     "@babel/types" "^7.21.4" |     "@babel/types" "^7.21.4" | ||||||
|     recast "^0.23.1" |     recast "^0.23.1" | ||||||
|  |  | ||||||
| "@xstate/react@^3.2.2": | "@xstate/react@^4.1.0": | ||||||
|   version "3.2.2" |   version "4.1.0" | ||||||
|   resolved "https://registry.yarnpkg.com/@xstate/react/-/react-3.2.2.tgz#ddf0f9d75e2c19375b1e1b7335e72cb99762aed8" |   resolved "https://registry.yarnpkg.com/@xstate/react/-/react-4.1.0.tgz#369378951e1f7f9326700f65ed02847598aad704" | ||||||
|   integrity sha512-feghXWLedyq8JeL13yda3XnHPZKwYDN5HPBLykpLeuNpr9178tQd2/3d0NrH6gSd0sG5mLuLeuD+ck830fgzLQ== |   integrity sha512-Fh89luCwuMXIVXIua67d8pNuVgdGpqke2jHfIIL+ZjkfNh6YFtPDSwNSZZDhdNUsOW1zZYSbtUzbC8MIUyTSHQ== | ||||||
|   dependencies: |   dependencies: | ||||||
|     use-isomorphic-layout-effect "^1.1.2" |     use-isomorphic-layout-effect "^1.1.2" | ||||||
|     use-sync-external-store "^1.0.0" |     use-sync-external-store "^1.0.0" | ||||||
| @ -8372,10 +8372,10 @@ typed-array-length@^1.0.4: | |||||||
|     for-each "^0.3.3" |     for-each "^0.3.3" | ||||||
|     is-typed-array "^1.1.9" |     is-typed-array "^1.1.9" | ||||||
|  |  | ||||||
| typescript@^5.2.2: | typescript@^5.4.2: | ||||||
|   version "5.3.2" |   version "5.4.2" | ||||||
|   resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.3.2.tgz#00d1c7c1c46928c5845c1ee8d0cc2791031d4c43" |   resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.4.2.tgz#0ae9cebcfae970718474fe0da2c090cad6577372" | ||||||
|   integrity sha512-6l+RyNy7oAHDfxC4FzSJcz9vnjTKxrLpDG5M2Vu4SHRVNg6xzqZp6LYSR9zjqQTu8DU/f5xwxUdADOkbrIX2gQ== |   integrity sha512-+2/g0Fds1ERlP6JsakQQDXjZdZMM+rqpamFZJEKh4kwTIn3iDkgKtby0CeNd5ATNZ4Ry1ax15TMx0W2V+miizQ== | ||||||
|  |  | ||||||
| ua-parser-js@^1.0.35: | ua-parser-js@^1.0.35: | ||||||
|   version "1.0.35" |   version "1.0.35" | ||||||
| @ -8912,11 +8912,16 @@ ws@^8.8.0: | |||||||
|   resolved "https://registry.yarnpkg.com/xstate/-/xstate-5.0.0-beta.54.tgz#d80f1a9e43ad883a65fc9b399161bd39633bd9bf" |   resolved "https://registry.yarnpkg.com/xstate/-/xstate-5.0.0-beta.54.tgz#d80f1a9e43ad883a65fc9b399161bd39633bd9bf" | ||||||
|   integrity sha512-BTnCPBQ2iTKe4uCnHEe1hNx6VTbXU+5mQGybSQHOjTLiBi4Ryi+tL9T6N1tmqagvM8rfl4XRfvndogfWCWcdpw== |   integrity sha512-BTnCPBQ2iTKe4uCnHEe1hNx6VTbXU+5mQGybSQHOjTLiBi4Ryi+tL9T6N1tmqagvM8rfl4XRfvndogfWCWcdpw== | ||||||
|  |  | ||||||
| xstate@^4.33.4, xstate@^4.38.2: | xstate@^4.33.4: | ||||||
|   version "4.38.3" |   version "4.38.3" | ||||||
|   resolved "https://registry.yarnpkg.com/xstate/-/xstate-4.38.3.tgz#4e15e7ad3aa0ca1eea2010548a5379966d8f1075" |   resolved "https://registry.yarnpkg.com/xstate/-/xstate-4.38.3.tgz#4e15e7ad3aa0ca1eea2010548a5379966d8f1075" | ||||||
|   integrity sha512-SH7nAaaPQx57dx6qvfcIgqKRXIh4L0A1iYEqim4s1u7c9VoCgzZc+63FY90AKU4ZzOC2cfJzTnpO4zK7fCUzzw== |   integrity sha512-SH7nAaaPQx57dx6qvfcIgqKRXIh4L0A1iYEqim4s1u7c9VoCgzZc+63FY90AKU4ZzOC2cfJzTnpO4zK7fCUzzw== | ||||||
|  |  | ||||||
|  | xstate@^5.9.1: | ||||||
|  |   version "5.9.1" | ||||||
|  |   resolved "https://registry.yarnpkg.com/xstate/-/xstate-5.9.1.tgz#1f167fae423cadd362762e2ff697d84fd774979c" | ||||||
|  |   integrity sha512-85edx7iMqRJSRlEPevDwc98EWDYUlT5zEQ54AXuRVR+G76gFbcVTAUdtAeqOVxy8zYnUr9FBB5114iK6enljjw== | ||||||
|  |  | ||||||
| y18n@^5.0.5: | y18n@^5.0.5: | ||||||
|   version "5.0.8" |   version "5.0.8" | ||||||
|   resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" |   resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" | ||||||
|  | |||||||
		Reference in New Issue
	
	Block a user
	