@aerogel/plugin-solid 0.0.1 → 0.1.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.
Files changed (39) hide show
  1. package/dist/InruptAuthenticator.lazy-BvpQLGoj.js +9 -0
  2. package/dist/InruptAuthenticator.lazy-BvpQLGoj.js.map +1 -0
  3. package/dist/LegacyAuthenticator.lazy-ChFFqP14.js +5 -0
  4. package/dist/LegacyAuthenticator.lazy-ChFFqP14.js.map +1 -0
  5. package/dist/aerogel-plugin-solid.d.ts +120 -92
  6. package/dist/aerogel-plugin-solid.js +724 -0
  7. package/dist/aerogel-plugin-solid.js.map +1 -0
  8. package/package.json +35 -28
  9. package/src/auth/Authenticator.ts +10 -8
  10. package/src/auth/authenticators/InruptAuthenticator.lazy.ts +1 -0
  11. package/src/auth/authenticators/InruptAuthenticator.ts +41 -12
  12. package/src/auth/authenticators/LegacyAuthenticator.lazy.ts +1 -0
  13. package/src/auth/authenticators/LegacyAuthenticator.ts +80 -0
  14. package/src/auth/index.ts +3 -1
  15. package/src/components/SolidAvatar.vue +35 -0
  16. package/src/components/SolidImage.vue +33 -0
  17. package/src/components/SolidLogin.vue +141 -0
  18. package/src/components/index.ts +3 -4
  19. package/src/errors/AuthenticationFailedError.ts +22 -0
  20. package/src/errors/index.ts +3 -4
  21. package/src/forms/validation.ts +11 -0
  22. package/src/index.css +1 -0
  23. package/src/index.test.ts +24 -0
  24. package/src/index.ts +65 -0
  25. package/src/services/Solid.state.ts +12 -16
  26. package/src/services/Solid.ts +317 -139
  27. package/src/testing/setup.ts +11 -0
  28. package/src/types/env.d.ts +1 -0
  29. package/src/types/solid-auth-client.d.ts +21 -0
  30. package/.eslintrc.js +0 -7
  31. package/dist/aerogel-plugin-solid.cjs.js +0 -2
  32. package/dist/aerogel-plugin-solid.cjs.js.map +0 -1
  33. package/dist/aerogel-plugin-solid.esm.js +0 -2
  34. package/dist/aerogel-plugin-solid.esm.js.map +0 -1
  35. package/noeldemartin.config.js +0 -2
  36. package/src/components/AGSolidStatus.ts +0 -5
  37. package/src/components/AGSolidStatus.vue +0 -60
  38. package/src/main.ts +0 -43
  39. package/tsconfig.json +0 -10
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aerogel-plugin-solid.js","sources":["../src/errors/AuthenticationCancelledError.ts","../src/errors/AuthenticationFailedError.ts","../src/errors/ContainerAlreadyInUse.ts","../src/auth/Authenticator.ts","../src/auth/authenticators/InruptAuthenticator.ts","../src/auth/authenticators/LegacyAuthenticator.ts","../src/auth/index.ts","../src/services/Solid.state.ts","../src/services/Solid.ts","../src/forms/validation.ts","../src/components/SolidImage.vue","../src/components/SolidAvatar.vue","../src/components/SolidLogin.vue","../src/index.ts"],"sourcesContent":["import { JSError } from '@noeldemartin/utils';\n\nexport default class AuthenticationCancelledError extends JSError {}\n","import { JSError } from '@noeldemartin/utils';\nimport type { JSErrorOptions } from '@noeldemartin/utils';\n\nfunction getErrorMessage(message: string, description?: string | null): string {\n if (!description) {\n return message;\n }\n\n return `${message} (${description})`;\n}\n\nexport default class AuthenticationFailedError extends JSError {\n\n public readonly description?: string | null;\n\n constructor(message: string, description?: string | null, options?: JSErrorOptions) {\n super(getErrorMessage(message), options);\n\n this.description = description;\n }\n\n}\n","import { JSError } from '@noeldemartin/utils';\nimport type { JSErrorOptions } from '@noeldemartin/utils';\nimport type { SolidContainer } from 'soukai-solid';\n\nexport default class ContainerAlreadyInUse extends JSError {\n\n constructor(container: SolidContainer, options?: JSErrorOptions) {\n super(`Container at ${container.url} is already in use`, options);\n }\n\n}\n","import { PromisedValue, arr, fail } from '@noeldemartin/utils';\nimport { SolidEngine } from 'soukai-solid';\nimport type { Closure, FluentArray, GetClosureArgs } from '@noeldemartin/utils';\nimport type { Engine } from 'soukai';\nimport type { Fetch } from 'soukai-solid';\nimport type { SolidUserProfile } from '@noeldemartin/solid-utils';\nimport type { ErrorSource } from '@aerogel/core';\n\nimport type { AuthenticatorName } from '@aerogel/plugin-solid/auth';\n\ntype ListenerEvent = keyof AuthenticatorListener;\ntype ListenerEventPayload<\n Event extends ListenerEvent,\n Callback = AuthenticatorListener[Event],\n> = Callback extends Closure ? GetClosureArgs<Callback> : never;\n\nexport interface AuthSession {\n user: SolidUserProfile;\n loginUrl: string;\n authenticator: Authenticator;\n}\n\nexport interface AuthenticatorListener {\n onSessionStarted?: (session: AuthSession) => Promise<void> | void;\n onSessionFailed?: (loginUrl: string, error: ErrorSource) => Promise<void> | void;\n onSessionEnded?: () => Promise<void> | void;\n onAuthenticatedFetchReady?: (fetch: Fetch) => Promise<void> | void;\n}\n\nexport default abstract class Authenticator {\n\n public name!: AuthenticatorName;\n\n protected authenticatedFetch?: Fetch;\n protected booted?: PromisedValue<void>;\n protected listeners: FluentArray<AuthenticatorListener> = arr<AuthenticatorListener>([]);\n protected _engine?: SolidEngine;\n\n public abstract login(loginUrl: string, user?: SolidUserProfile | null): Promise<AuthSession>;\n\n public abstract logout(): Promise<void>;\n\n public get engine(): Engine {\n this._engine = this._engine ?? new SolidEngine(this.requireAuthenticatedFetch());\n\n return this._engine;\n }\n\n public getAuthenticatedFetch(): Fetch | null {\n return this.authenticatedFetch ?? null;\n }\n\n public requireAuthenticatedFetch(): Fetch {\n return this.authenticatedFetch ?? fail('Authenticated fetch is not ready');\n }\n\n public addListener(listener: AuthenticatorListener): () => void {\n if (!this.listeners.includes(listener)) this.listeners.push(listener);\n\n return () => {\n this.removeListener(listener);\n };\n }\n\n public removeListener(listener: AuthenticatorListener): void {\n if (!this.listeners.includes(listener)) return;\n\n this.listeners.remove(listener);\n }\n\n public async boot(): Promise<void> {\n if (this.booted) {\n return this.booted;\n }\n\n this.booted = new PromisedValue();\n\n await this.restoreSession();\n\n this.booted.resolve();\n }\n\n protected abstract restoreSession(): Promise<void>;\n\n protected async initAuthenticatedFetch(fetch: Fetch): Promise<void> {\n this.authenticatedFetch = fetch;\n\n await this.notifyListeners('onAuthenticatedFetchReady', fetch);\n }\n\n protected async startSession(sessionData: Omit<AuthSession, 'authenticator'>): Promise<AuthSession> {\n const session: AuthSession = {\n authenticator: this,\n ...sessionData,\n };\n\n await this.notifyListeners('onSessionStarted', session);\n\n return session;\n }\n\n protected async failSession(loginUrl: string, error: ErrorSource): Promise<void> {\n await this.notifyListeners('onSessionFailed', loginUrl, error);\n }\n\n protected async endSession(): Promise<void> {\n await this.notifyListeners('onSessionEnded');\n }\n\n protected async notifyListeners<Event extends ListenerEvent>(\n event: Event,\n ...payload: ListenerEventPayload<Event>\n ): Promise<void> {\n await Promise.all(\n this.listeners.toArray().map(async (listener) => {\n const callback = listener[event] as Closure | undefined;\n\n await callback?.(...payload);\n }),\n );\n }\n\n}\n","import AerogelSolid from 'virtual:aerogel-solid';\n\nimport { Storage, after, fail } from '@noeldemartin/utils';\nimport type { Fetch } from 'soukai-solid';\nimport type {\n ILoginInputOptions,\n events,\n handleIncomingRedirect,\n login,\n logout,\n} from '@inrupt/solid-client-authn-browser';\n\nimport AuthenticationFailedError from '@aerogel/plugin-solid/errors/AuthenticationFailedError';\nimport Authenticator from '@aerogel/plugin-solid/auth/Authenticator';\nimport Solid from '@aerogel/plugin-solid/services/Solid';\nimport type { AuthSession } from '@aerogel/plugin-solid/auth/Authenticator';\n\nconst STORAGE_KEY = 'inrupt-authenticator';\n\nexport default class InruptAuthenticator extends Authenticator {\n\n private _events!: typeof events;\n private _fetch!: Fetch;\n private _login!: typeof login;\n private _logout!: typeof logout;\n private _handleIncomingRedirect!: typeof handleIncomingRedirect;\n\n public async login(loginUrl: string): Promise<AuthSession> {\n Storage.set<string>(STORAGE_KEY, loginUrl);\n\n const options: ILoginInputOptions = {\n oidcIssuer: loginUrl,\n };\n\n if (AerogelSolid.clientID) {\n options.clientId = AerogelSolid.clientID.client_id;\n options.clientName = AerogelSolid.clientID.client_name;\n options.redirectUrl = AerogelSolid.clientID.redirect_uris[0];\n }\n\n await this._login(options);\n\n // Browser should redirect, so just make it wait for a while.\n await after({ seconds: 60 });\n\n return fail('Browser should have redirected, but it didn\\'t');\n }\n\n public async logout(): Promise<void> {\n await this._logout();\n await this.endSession();\n }\n\n protected async restoreSession(): Promise<void> {\n const { events, fetch, handleIncomingRedirect, login, logout } = await import('./InruptAuthenticator.lazy');\n\n this._fetch = fetch;\n this._login = login;\n this._logout = logout;\n this._events = events;\n this._handleIncomingRedirect = handleIncomingRedirect;\n\n await this.loginFromRedirect();\n }\n\n protected async loginFromRedirect(): Promise<void> {\n const loginUrl = Storage.get<string>(STORAGE_KEY);\n if (!loginUrl) {\n return;\n }\n\n this._events().on('error', (error: string | null, errorDescription?: string | Error | null) => {\n error ??= 'Error using Inrupt Authenticator method';\n\n if (typeof errorDescription === 'object') {\n this.failSession(loginUrl, new AuthenticationFailedError(error, null, { cause: errorDescription }));\n } else {\n this.failSession(loginUrl, new AuthenticationFailedError(error, errorDescription));\n }\n });\n\n const session = await this._handleIncomingRedirect(globalThis.location.href);\n\n Storage.remove(STORAGE_KEY);\n\n if (session?.isLoggedIn && session.webId) {\n await this.initSession(session.webId);\n }\n }\n\n protected async initSession(webId: string): Promise<void> {\n await this.initAuthenticatedFetch(this._fetch);\n\n try {\n const user = await Solid.requireUserProfile(webId);\n\n await this.startSession({ user, loginUrl: webId });\n } catch (error) {\n await this.failSession(webId, error);\n }\n }\n\n}\n","import { after, required } from '@noeldemartin/utils';\nimport { persistent } from '@aerogel/core';\nimport type { SolidUserProfile } from '@noeldemartin/solid-utils';\nimport type { UnwrapNestedRefs } from 'vue';\n\nimport Authenticator from '@aerogel/plugin-solid/auth/Authenticator';\nimport AuthenticationFailedError from '@aerogel/plugin-solid/errors/AuthenticationFailedError';\nimport Solid from '@aerogel/plugin-solid/services/Solid';\nimport type { AuthSession } from '@aerogel/plugin-solid/auth/Authenticator';\n\ninterface Data {\n webId?: string;\n loginUrl?: string;\n}\n\nexport default class LegacyAuthenticator extends Authenticator {\n\n private store: UnwrapNestedRefs<Data>;\n\n constructor() {\n super();\n\n this.store = persistent('legacy-authenticator', {});\n }\n\n public async login(loginUrl: string, user?: SolidUserProfile | null): Promise<AuthSession> {\n const { SolidAuthClient } = await import('./LegacyAuthenticator.lazy');\n\n this.store.webId = user?.webId;\n this.store.loginUrl = user?.oidcIssuerUrl ?? loginUrl;\n\n const result = await SolidAuthClient.login(this.store.loginUrl);\n\n if (result === null) {\n delete this.store.webId;\n delete this.store.loginUrl;\n\n throw new AuthenticationFailedError('Could not log in with Solid using the legacy authenticator');\n }\n\n // Browser should redirect, so just make it wait for a while.\n await after({ seconds: 60 });\n\n throw new Error('Browser should have redirected, but it didn\\'t');\n }\n\n public async logout(): Promise<void> {\n const { SolidAuthClient } = await import('./LegacyAuthenticator.lazy');\n\n delete this.store.webId;\n delete this.store.loginUrl;\n\n await SolidAuthClient.logout();\n await this.endSession();\n }\n\n protected async restoreSession(): Promise<void> {\n if (!this.store.loginUrl) {\n return;\n }\n\n const { SolidAuthClient } = await import('./LegacyAuthenticator.lazy');\n\n const session = await SolidAuthClient.currentSession();\n const loginUrl = required(this.store.loginUrl);\n\n await this.initAuthenticatedFetch((input: RequestInfo, options?: Object) => {\n return SolidAuthClient.fetch(input, options);\n });\n\n try {\n const user = await Solid.requireUserProfile(required(session?.webId ?? this.store.webId));\n\n await this.startSession({ user, loginUrl });\n } catch (error) {\n await this.failSession(loginUrl, error);\n }\n }\n\n}\n","import InruptAuthenticator from './authenticators/InruptAuthenticator';\nimport LegacyAuthenticator from './authenticators/LegacyAuthenticator';\nimport type Authenticator from './Authenticator';\n\nconst _authenticators = {} as Authenticators;\n\ntype BaseAuthenticators = typeof authenticators;\n\nexport * from './Authenticator';\nexport { default as Authenticator } from './Authenticator';\n\nexport const authenticators = {\n inrupt: new InruptAuthenticator(),\n legacy: new LegacyAuthenticator(),\n};\n\nexport function setDefaultAuthenticator(authenticator: Authenticator): void {\n _authenticators.default = authenticator;\n}\n\nexport function getAuthenticator<T extends AuthenticatorName>(name: T): Authenticators[T] {\n return _authenticators[name];\n}\n\nexport function registerAuthenticators(customAuthenticators: Record<string, Authenticator>): void {\n Object.entries(customAuthenticators).forEach(([name, authenticator]) =>\n registerAuthenticator(name as AuthenticatorName, authenticator));\n}\n\nexport function registerAuthenticator<T extends AuthenticatorName>(name: T, authenticator: Authenticators[T]): void {\n _authenticators[name] = authenticator;\n\n authenticator.name = authenticator.name ?? name;\n}\n\nexport type AuthenticatorName = keyof Authenticators;\n\nexport interface Authenticators extends BaseAuthenticators {\n default: Authenticator;\n}\n","import { defineServiceState } from '@aerogel/core';\nimport type { ErrorSource } from '@aerogel/core';\nimport type { Fetch, SolidUserProfile } from '@noeldemartin/solid-utils';\n\nimport type { AuthSession } from '@aerogel/plugin-solid/auth/Authenticator';\nimport type { AuthenticatorName } from '@aerogel/plugin-solid/auth';\n\nexport const DEFAULT_STATE = { autoReconnect: true };\n\nexport default defineServiceState({\n name: 'solid',\n persist: ['autoReconnect', 'dismissed', 'previousSession', 'profiles', 'staleProfiles'],\n initialState: {\n autoReconnect: DEFAULT_STATE.autoReconnect,\n dismissed: false,\n ignorePreviousSessionError: false,\n loginStartupError: null as ErrorSource | null,\n loginOngoing: false,\n preferredAuthenticator: null as AuthenticatorName | null,\n previousSession: null as {\n profile?: SolidUserProfile;\n authenticator: AuthenticatorName;\n loginUrl: string;\n error: ErrorSource | null;\n } | null,\n profiles: {} as Record<string, SolidUserProfile>,\n session: null as AuthSession | null,\n loginStale: false,\n staleProfiles: [] as string[],\n },\n serialize(state) {\n if (state.previousSession?.error instanceof Error) {\n state.previousSession.error = state.previousSession.error.message;\n }\n\n return state;\n },\n computed: {\n user: (state) => state.session?.user ?? state.previousSession?.profile ?? null,\n authenticator: (state) => state.session?.authenticator ?? null,\n fetch(): Fetch {\n return this.authenticator?.getAuthenticatedFetch() ?? globalThis.fetch.bind(globalThis);\n },\n error(state): ErrorSource | null {\n if (state.loginStartupError) {\n return state.loginStartupError ?? null;\n }\n\n if (state.ignorePreviousSessionError) {\n return null;\n }\n\n return state.previousSession?.error ?? null;\n },\n },\n});\n","import {\n after,\n arrayFilter,\n arrayUnique,\n arrayWithout,\n asyncMemo,\n facade,\n fail,\n getLocationQueryParameter,\n hasLocationQueryParameter,\n isDevelopment,\n objectWithout,\n objectWithoutEmpty,\n parseBoolean,\n requireUrlParse,\n resetAsyncMemo,\n setAsyncMemo,\n tap,\n urlRoot,\n} from '@noeldemartin/utils';\nimport { App, Errors, Events, Storage, UI, translateWithDefault } from '@aerogel/core';\nimport { fetchLoginUserProfile } from '@noeldemartin/solid-utils';\nimport { setEngine } from 'soukai';\nimport { SolidACLAuthorization, SolidContainer, SolidDocument, SolidTypeIndex } from 'soukai-solid';\nimport type { ErrorSource } from '@aerogel/core';\nimport type { Fetch, SolidModelConstructor } from 'soukai-solid';\nimport type { NullablePartial } from '@noeldemartin/utils';\nimport type { SolidStore, SolidUserProfile } from '@noeldemartin/solid-utils';\n\nimport AuthenticationCancelledError from '@aerogel/plugin-solid/errors/AuthenticationCancelledError';\nimport { ContainerAlreadyInUse } from '@aerogel/plugin-solid/errors';\nimport { getAuthenticator } from '@aerogel/plugin-solid/auth';\nimport type Authenticator from '@aerogel/plugin-solid/auth/Authenticator';\nimport type { AuthenticatorName } from '@aerogel/plugin-solid/auth';\nimport type { AuthSession } from '@aerogel/plugin-solid/auth/Authenticator';\n\nimport Service, { DEFAULT_STATE } from './Solid.state';\n\nexport type LoginOptions = NullablePartial<{\n authenticator: AuthenticatorName;\n onError(error: ErrorSource): unknown;\n fallbackUrl: string;\n loading: boolean;\n}>;\n\nexport type UserProfileOptions = NullablePartial<{\n required: boolean;\n markStale: boolean;\n}>;\n\nexport type ReconnectOptions = Omit<LoginOptions, 'authenticator'> & {\n force?: boolean;\n};\n\nexport class SolidService extends Service {\n\n public isLoggedIn(): this is { session: AuthSession; user: SolidUserProfile; authenticator: Authenticator } {\n return !!this.session;\n }\n\n public wasLoggedIn(): this is { user: SolidUserProfile } {\n return !!this.previousSession;\n }\n\n public hasLoggedIn(): this is { user: SolidUserProfile } {\n return !!(this.session || this.previousSession);\n }\n\n public requireAuthenticator(): Authenticator {\n return this.authenticator ?? fail('Could not get authenticator');\n }\n\n public requireUser(): SolidUserProfile {\n return this.user ?? fail('Could not get user profile (are you logged in?)');\n }\n\n public requireUserProfile(url: string): Promise<SolidUserProfile> {\n return this.getUserProfile(url, { required: true });\n }\n\n /* eslint-disable max-len */\n public async getUserProfile(url: string, options: Omit<UserProfileOptions, 'required'> & { required: true }): Promise<SolidUserProfile>; // prettier-ignore\n public async getUserProfile(url: string, options?: UserProfileOptions): Promise<SolidUserProfile | null>;\n /* eslint-enable max-len */\n\n public async getUserProfile(url: string, options: UserProfileOptions = {}): Promise<SolidUserProfile | null> {\n let profileStore: SolidStore | null = null;\n const markStale = options.markStale ?? !this.authenticator?.getAuthenticatedFetch();\n const userProfile =\n this.profiles[url] ??\n (await tap(\n await fetchLoginUserProfile(url, {\n fetch: this.fetch,\n required: options.required ?? false,\n onLoaded(store) {\n profileStore = store;\n },\n }),\n async (profile) => {\n if (!profile) {\n return;\n }\n\n if (profileStore) {\n await Events.emit('solid:user-profile-loaded', [profile, profileStore]);\n }\n\n this.rememberProfile(profile);\n },\n ));\n\n if (userProfile && markStale) {\n this.setState({ staleProfiles: arrayUnique(this.staleProfiles.concat([userProfile.webId])) });\n }\n\n return userProfile;\n }\n\n public async refreshUserProfile(): Promise<void> {\n if (!this.isLoggedIn()) {\n return;\n }\n\n this.setState({ profiles: objectWithout(this.profiles, [this.user.webId]) });\n\n const user = await this.requireUserProfile(this.user.webId);\n\n this.setState({\n session: {\n ...this.session,\n user,\n },\n });\n }\n\n public async login(loginUrl: string, options: LoginOptions = {}): Promise<boolean> {\n if (isDevelopment() && loginUrl === 'dev') {\n loginUrl = 'http://localhost:3000';\n }\n\n if (!/^https?:\\/\\//.test(loginUrl)) {\n options.fallbackUrl ??= `http://${loginUrl}`;\n loginUrl = `https://${loginUrl}`;\n }\n\n if (this.isLoggedIn()) {\n return true;\n }\n\n if (this.loginOngoing) {\n throw new Error('Authentication already in progress');\n }\n\n if (!options.loading) {\n return this.attemptLogin(loginUrl, options);\n }\n\n return UI.loading(translateWithDefault('solid.loginOngoing', 'Logging in...'), () => {\n return this.attemptLogin(loginUrl, options);\n });\n }\n\n public async reconnect(options: ReconnectOptions = {}): Promise<void> {\n const { force, ...loginOptions } = options;\n if (this.isLoggedIn() || !this.previousSession || (this.previousSession.error !== null && !force)) {\n return;\n }\n\n await this.login(this.previousSession.loginUrl, {\n authenticator: this.previousSession.authenticator,\n ...loginOptions,\n loading: loginOptions.loading ?? false,\n });\n }\n\n public async logout(force: boolean = false): Promise<void> {\n const isCloudReady = !App.plugin('@aerogel/local-first') || !!App.service('$cloud')?.ready;\n const hasLocalChanges = !App.plugin('@aerogel/local-first') || !!App.service('$cloud')?.dirty;\n const [confirmLogout, options] = force\n ? [true, { wipeLocalData: isCloudReady }]\n : await UI.confirm(\n isCloudReady\n ? translateWithDefault('solid.logoutConfirmTitle', 'Log out from this device?')\n : translateWithDefault('solid.disconnectConfirmTitle', 'Disconnect account?'),\n isCloudReady\n ? hasLocalChanges\n ? translateWithDefault(\n 'solid.logoutConfirmMessageWithLocalChanges',\n 'There are some changes that haven\\'t been synchronized and will be lost, ' +\n 'but the rest of the data will remain in your Solid POD.',\n )\n : translateWithDefault(\n 'solid.logoutConfirmMessage',\n 'Logging out will remove all the data from this device, ' +\n 'but you\\'ll still have it in your Solid POD.',\n )\n : translateWithDefault(\n 'solid.disconnectConfirmMessage',\n 'You\\'ll need to introduce your credentials again to connect to your Solid POD.',\n ),\n {\n acceptText: translateWithDefault('solid.logoutConfirmAccept', 'Log out'),\n acceptVariant: 'danger',\n cancelText: translateWithDefault(\n 'solid.logoutConfirmCancel',\n translateWithDefault('ui.cancel', 'Cancel'),\n ),\n checkboxes: isCloudReady\n ? hasLocalChanges\n ? {\n wipeLocalData: {\n label: translateWithDefault(\n 'solid.logoutConfirmWipeNotice',\n 'I understand that changes that haven\\'t been synchronized will be lost.',\n ),\n default: false,\n required: true,\n },\n }\n : undefined\n : {\n wipeLocalData: {\n label: translateWithDefault(\n 'solid.logoutConfirmWipe',\n 'Also remove all existing data',\n ),\n default: false,\n },\n },\n },\n );\n\n if (!confirmLogout) {\n return;\n }\n\n if (!this.previousSession) {\n return;\n }\n\n await UI.loading(translateWithDefault('solid.logoutOngoing', 'Logging out...'), async () => {\n this.setState({\n dismissed: false,\n ignorePreviousSessionError: false,\n loginStartupError: null,\n previousSession: null,\n ...DEFAULT_STATE,\n });\n\n this.isLoggedIn() && (await this.authenticator.logout());\n\n if (options.wipeLocalData ?? true) {\n await Storage.purge();\n }\n\n await Events.emit('auth:logout');\n await Events.emit('auth:after-logout');\n });\n }\n\n public async whenLoggedIn<T>(callback: (session: AuthSession) => T): Promise<T> {\n if (this.isLoggedIn()) {\n return callback(this.session);\n }\n\n return new Promise((resolve) => {\n const onLogin = (session: AuthSession) => resolve(callback(session));\n\n Events.once('auth:login', onLogin);\n });\n }\n\n public dismiss(): void {\n this.setState({ dismissed: true });\n }\n\n public async findOrCreatePrivateTypeIndex(): Promise<SolidTypeIndex> {\n return (await this.findPrivateTypeIndex()) ?? (await this.createPrivateTypeIndex());\n }\n\n public async findPrivateTypeIndex(options: { fresh?: boolean } = {}): Promise<SolidTypeIndex | null> {\n const { webId, privateTypeIndexUrl } = this.requireUser();\n const engine = this.requireAuthenticator().engine;\n\n if (!privateTypeIndexUrl) {\n return null;\n }\n\n if (options.fresh) {\n resetAsyncMemo(`${webId}-privateTypeIndex`);\n }\n\n return asyncMemo(`${webId}-privateTypeIndex`, () =>\n SolidTypeIndex.withEngine(engine).find(privateTypeIndexUrl));\n }\n\n public async findOrCreatePublicTypeIndex(): Promise<SolidTypeIndex> {\n return (await this.findPublicTypeIndex()) ?? (await this.createPublicTypeIndex());\n }\n\n public async findPublicTypeIndex(options: { fresh?: boolean } = {}): Promise<SolidTypeIndex | null> {\n const { webId, publicTypeIndexUrl } = this.requireUser();\n const engine = this.requireAuthenticator().engine;\n\n if (!publicTypeIndexUrl) {\n return null;\n }\n\n if (options.fresh) {\n resetAsyncMemo(`${webId}-publicTypeIndex`);\n }\n\n return asyncMemo(`${webId}-publicTypeIndex`, () => SolidTypeIndex.withEngine(engine).find(publicTypeIndexUrl));\n }\n\n public async createPrivateContainer(options: {\n url: string;\n name?: string;\n registerFor?: SolidModelConstructor;\n reuseExisting?: boolean;\n }): Promise<SolidContainer> {\n const engine = this.requireAuthenticator().engine;\n\n return SolidContainer.withEngine(engine, async () => {\n const existingContainer = await SolidContainer.find(options.url);\n\n if (existingContainer && !options.reuseExisting) {\n throw new ContainerAlreadyInUse(existingContainer);\n }\n\n const container = existingContainer ?? new SolidContainer({ url: options.url, name: options.name });\n\n await container.save();\n\n if (options.registerFor) {\n const typeIndex = await this.findOrCreatePrivateTypeIndex();\n\n await container.register(typeIndex, options.registerFor);\n }\n\n return container;\n });\n }\n\n protected override async boot(): Promise<void> {\n await Errors.booted;\n\n if (hasLocationQueryParameter('authenticator')) {\n this.setState({\n preferredAuthenticator: getLocationQueryParameter('authenticator') as AuthenticatorName,\n });\n }\n\n if (hasLocationQueryParameter('refreshProfile')) {\n this.setState({\n staleProfiles: arrayUnique(\n this.staleProfiles.concat(arrayFilter([this.session?.user.webId, this.previousSession?.loginUrl])),\n ),\n });\n }\n\n await this.restorePreviousSession();\n await this.startupReconnect();\n }\n\n protected async createPrivateTypeIndex(): Promise<SolidTypeIndex> {\n const user = this.requireUser();\n const authenticator = this.requireAuthenticator();\n const typeIndex = await SolidTypeIndex.withEngine(authenticator.engine).createPrivate(user);\n\n return tap(typeIndex, () => {\n user.privateTypeIndexUrl = typeIndex.url;\n\n setAsyncMemo(`${user.webId}-privateTypeIndex`, typeIndex);\n\n this.rememberProfile(user);\n });\n }\n\n public async createPublicTypeIndex(): Promise<SolidTypeIndex> {\n const user = this.requireUser();\n const authenticator = this.requireAuthenticator();\n const typeIndex = await SolidTypeIndex.withEngine(authenticator.engine).createPublic(user);\n\n return tap(typeIndex, () => {\n user.privateTypeIndexUrl = typeIndex.url;\n\n setAsyncMemo(`${user.webId}-publicTypeIndex`, typeIndex);\n\n this.rememberProfile(user);\n });\n }\n\n protected async attemptLogin(loginUrl: string, options: LoginOptions): Promise<boolean> {\n const authenticatorName = options.authenticator ?? this.preferredAuthenticator ?? 'default';\n const handleLoginError =\n options.onError ??\n ((error: ErrorSource) =>\n App.isMounted() ? Errors.report(error) : this.setState({ loginStartupError: error }));\n const staleTimeout = setTimeout(() => (this.loginStale = true), 10000);\n const initialState = {\n dismissed: this.dismissed,\n ignorePreviousSessionError: this.ignorePreviousSessionError,\n previousSession: this.previousSession,\n };\n\n this.loginOngoing = true;\n\n try {\n const profile = await this.getUserProfile(loginUrl, { markStale: true });\n const oidcIssuerUrl = profile?.oidcIssuerUrl ?? urlRoot(profile?.webId ?? loginUrl);\n const authenticator = await this.bootAuthenticator(authenticatorName);\n const { domain: loginDomain } = requireUrlParse(loginUrl);\n\n this.setState({\n dismissed: false,\n ignorePreviousSessionError: true,\n previousSession: objectWithoutEmpty({\n profile,\n loginUrl,\n authenticator: authenticatorName,\n error: translateWithDefault(\n 'solid.stuckConnecting',\n 'We didn\\'t hear back from the identity provider at `{domain}`, maybe try reconnecting?',\n { domain: loginDomain },\n ),\n }),\n });\n\n await Events.emit('auth:before-login');\n\n // This should redirect away from the app, so in most cases\n // the rest of the code won't be reached.\n await authenticator.login(oidcIssuerUrl, profile);\n\n return true;\n } catch (error) {\n this.setState(initialState);\n\n if (error instanceof AuthenticationCancelledError) {\n return false;\n }\n\n if (options.fallbackUrl) {\n return after().then(() => this.login(options.fallbackUrl ?? '', objectWithout(options, 'fallbackUrl')));\n }\n\n handleLoginError(error);\n\n return false;\n } finally {\n clearTimeout(staleTimeout);\n\n this.setState({\n loginOngoing: false,\n loginStale: false,\n });\n }\n }\n\n private async restorePreviousSession(): Promise<void> {\n if (!this.previousSession) {\n return;\n }\n\n await this.bootAuthenticator(this.previousSession.authenticator);\n }\n\n private async startupReconnect(): Promise<void> {\n if (!this.reconnectOnStartup()) {\n return;\n }\n\n await this.reconnect({ force: this.forcingReconnect() });\n }\n\n private reconnectOnStartup(): boolean {\n if (hasLocationQueryParameter('autoReconnect')) {\n return parseBoolean(getLocationQueryParameter('autoReconnect'));\n }\n\n return this.autoReconnect;\n }\n\n private forcingReconnect(): boolean {\n return hasLocationQueryParameter('autoReconnect') && parseBoolean(getLocationQueryParameter('autoReconnect'));\n }\n\n private async bootAuthenticator(name: AuthenticatorName): Promise<Authenticator> {\n const authenticator = getAuthenticator(name);\n\n authenticator.addListener({\n onSessionStarted: async (session) => {\n this.setState({\n session,\n previousSession: {\n authenticator: authenticator.name,\n loginUrl: session.loginUrl,\n profile: session.user,\n error: null,\n },\n });\n\n if (this.staleProfiles.includes(session.user.webId)) {\n await this.refreshUserProfile();\n }\n\n App.plugin('@aerogel/local-first') || setEngine(session.authenticator.engine);\n\n SolidACLAuthorization.setEngine(session.authenticator.engine);\n SolidTypeIndex.setEngine(session.authenticator.engine);\n SolidDocument.setEngine(session.authenticator.engine);\n\n await Events.emit('auth:login', session);\n },\n onSessionFailed: async (loginUrl, error) => {\n this.setState({\n session: null,\n previousSession: {\n profile: this.previousSession?.profile,\n authenticator: authenticator.name,\n loginUrl,\n error,\n },\n });\n },\n onSessionEnded: () => this.setState({ session: null }),\n onAuthenticatedFetchReady: (fetch) => Events.emit('auth:fetch-ready', fetch),\n });\n\n await authenticator.boot();\n\n return authenticator;\n }\n\n private rememberProfile(profile: SolidUserProfile): void {\n this.setState({\n profiles: {\n ...this.profiles,\n [profile.webId]: profile,\n },\n staleProfiles: arrayWithout(this.staleProfiles, profile.webId),\n });\n }\n\n}\n\nexport default facade(SolidService);\n\ndeclare module '@aerogel/core' {\n export interface EventsPayload {\n 'auth:fetch-ready': Fetch;\n 'auth:before-login': void;\n 'auth:login': AuthSession;\n 'auth:logout': void;\n 'auth:after-logout': void;\n 'solid:user-profile-loaded': [SolidUserProfile, SolidStore];\n }\n}\n","import { defineFormValidationRule } from '@aerogel/core';\n\nexport function defineFormValidationRules(): void {\n defineFormValidationRule<string>('container_url', (value) => {\n if (value.endsWith('/')) {\n return;\n }\n\n return 'containerEndingSlashMissing';\n });\n}\n","<template>\n <img :src=\"sourceUrl\">\n</template>\n\n<script setup lang=\"ts\">\nimport { Cache, computedAsync } from '@aerogel/core';\n\nimport Solid from '@aerogel/plugin-solid/services/Solid';\n\nconst { src } = defineProps<{ src: string }>();\nconst sourceUrl = computedAsync(async () => {\n const cachedResponse = (await Cache.get(src)) ?? (await downloadImage(src));\n const blob = await cachedResponse?.blob();\n\n return blob && URL.createObjectURL(blob);\n});\n\nasync function downloadImage(url: string) {\n try {\n const response = await Solid.fetch(url);\n\n if (response.status !== 200) {\n return null;\n }\n\n await Cache.store(url, response);\n\n return Cache.get(url);\n } catch (error) {\n return null;\n }\n}\n</script>\n","<template>\n <div class=\"relative flex items-center justify-center rounded-full bg-gray-200\">\n <SolidImage\n v-if=\"$solid.user?.avatarUrl\"\n :src=\"$solid.user?.avatarUrl\"\n alt=\"\"\n class=\"absolute inset-0 size-full rounded-full\"\n />\n <span v-else-if=\"userInitials\" class=\"text-sm font-semibold tracking-wider text-gray-500\" aria-hidden=\"true\">\n {{ userInitials }}\n </span>\n <IconAnonymous v-else class=\"size-6 text-gray-500\" />\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport IconAnonymous from '~icons/ooui/user-anonymous';\n\nimport { computed } from 'vue';\n\nimport Solid from '@aerogel/plugin-solid/services/Solid';\n\nimport SolidImage from './SolidImage.vue';\n\nconst userInitials = computed(() => {\n const name = Solid.user?.name ?? '';\n\n return name\n .trim()\n .split(' ')\n .map((part) => part[0]?.toUpperCase())\n .join('')\n .slice(0, 3);\n});\n</script>\n","<template>\n <Form :form :class=\"renderedClasses\" @submit=\"submit\">\n <div\n class=\"flex w-full flex-col items-center gap-2\"\n :class=\"{ 'md:flex-row': layout === 'horizontal' && !form.authenticator }\"\n >\n <Input\n v-if=\"showInput\"\n name=\"url\"\n :aria-label=\"$td('solid.logIn.label', 'Login url')\"\n :placeholder=\"$td('solid.logIn.placeholder', 'https://me.solidcommunity.net')\"\n class=\"w-full md:min-w-96\"\n />\n <Select\n v-if=\"form.authenticator\"\n name=\"authenticator\"\n class=\"w-full max-w-full md:min-w-96\"\n label-class=\"sr-only\"\n options-class=\"md:w-96 w-full max-w-[calc(100vw-4rem)]\"\n :label=\"$td('solid.logIn.switchAuthenticatorLabel', 'Authentication method')\"\n :options=\"authenticatorOptions\"\n :render-option=\"renderAuthenticator\"\n />\n <div v-if=\"showDevLogin && !usingManualUrl\" class=\"flex w-full justify-center gap-2\">\n <Button\n variant=\"secondary\"\n class=\"group gap-0\"\n :title=\"$td('solid.logIn.manualUrl', 'Enter url')\"\n @click=\"usingManualUrl = true\"\n >\n <IconLogin class=\"size-4\" />\n <span\n v-measure.css=\"() => (measured = true)\"\n class=\"overflow-hidden pl-1 whitespace-nowrap\"\n :class=\"{\n 'opacity-0': !measured,\n 'w-0 transition-[width] md:group-hover:w-(--width)': measured,\n }\"\n >{{ $td('solid.logIn.manualUrl', 'Enter url') }}</span>\n </Button>\n <Button\n :disabled=\"noLoading && $solid.loginOngoing\"\n submit\n class=\"whitespace-nowrap\"\n @click=\"form.url = 'dev'\"\n >\n {{ $td('solid.logIn.dev', 'Log in to dev server') }}\n </Button>\n </div>\n <Button\n v-else\n :disabled=\"noLoading && $solid.loginOngoing\"\n submit\n :class=\"renderedButtonClasses\"\n >\n {{ $td('solid.logIn.submit', 'Log in') }}\n </Button>\n </div>\n <Link\n v-if=\"allowLegacyAuthenticator && showInput && !form.authenticator\"\n class=\"text-sm font-normal text-gray-700\"\n @click=\"form.authenticator = 'inrupt'\"\n >\n {{ $td('solid.logIn.switchAuthenticator', \"Can't log in? Try using a different authentication method\") }}\n </Link>\n </Form>\n</template>\n\n<script setup lang=\"ts\">\nimport IconLogin from '~icons/ic/baseline-log-in';\n\nimport {\n App,\n Button,\n Form,\n Input,\n Link,\n Select,\n UI,\n classes,\n requiredStringInput,\n stringInput,\n translateWithDefault,\n useForm,\n} from '@aerogel/core';\nimport { computed, ref } from 'vue';\nimport type { HTMLAttributes } from 'vue';\n\nimport Solid from '@aerogel/plugin-solid/services/Solid';\nimport type { AuthenticatorName } from '@aerogel/plugin-solid/auth';\n\nconst AUTHENTICATOR_LABELS = {\n inrupt: 'Log in using Inrupt\\'s authentication library',\n legacy: 'Log in using the legacy authentication library',\n} satisfies Partial<Record<AuthenticatorName, string>>;\n\nconst {\n class: rootClasses = '',\n layout = 'horizontal',\n buttonClass,\n noLoading,\n allowLegacyAuthenticator,\n} = defineProps<{\n class?: HTMLAttributes['class'];\n buttonClass?: HTMLAttributes['class'];\n layout?: 'vertical' | 'horizontal';\n noLoading?: boolean;\n allowLegacyAuthenticator?: boolean;\n}>();\nconst form = useForm({\n url: requiredStringInput(),\n authenticator: stringInput(),\n});\nconst authenticatorOptions = Object.keys(AUTHENTICATOR_LABELS) as (keyof typeof AUTHENTICATOR_LABELS)[];\nconst measured = ref(false);\nconst usingManualUrl = ref(false);\nconst showInput = computed(() => !showDevLogin.value || usingManualUrl.value);\nconst showDevLogin = computed(\n () => App.development && (!form.url || form.url === 'dev' || form.url.trim().length === 0),\n);\nconst renderedClasses = computed(() => classes('flex flex-col items-center gap-2 w-full md:w-auto', rootClasses));\nconst renderedButtonClasses = computed(() =>\n classes('w-full whitespace-nowrap', { 'md:w-auto': layout === 'horizontal' && !form.authenticator }, buttonClass));\n\nfunction renderAuthenticator(option: keyof typeof AUTHENTICATOR_LABELS) {\n return translateWithDefault(\n `solid.logIn.switchAuthenticator${option.charAt(0).toUpperCase() + option.slice(1)}`,\n AUTHENTICATOR_LABELS[option],\n );\n}\n\nasync function submit() {\n if (noLoading) {\n await Solid.login(form.url, { authenticator: form.authenticator as AuthenticatorName });\n\n return;\n }\n\n await UI.loading(translateWithDefault('solid.loginOngoing', 'Logging in...'), Solid.login(form.url));\n}\n</script>\n","import { Events, bootServices, registerErrorHandler } from '@aerogel/core';\nimport { bootSolidModels } from 'soukai-solid';\nimport type { Plugin } from '@aerogel/core';\nimport type { SolidStore, SolidUserProfile } from '@noeldemartin/solid-utils';\n\nimport Solid from '@aerogel/plugin-solid/services/Solid';\nimport { DEFAULT_STATE } from '@aerogel/plugin-solid/services/Solid.state';\nimport { defineFormValidationRules } from '@aerogel/plugin-solid/forms/validation';\nimport {\n authenticators as baseAuthenticators,\n getAuthenticator,\n registerAuthenticators,\n setDefaultAuthenticator,\n} from '@aerogel/plugin-solid/auth';\nimport { AuthenticationFailedError } from '@aerogel/plugin-solid/errors';\nimport type Authenticator from '@aerogel/plugin-solid/auth/Authenticator';\nimport type { AuthenticatorName } from '@aerogel/plugin-solid/auth';\n\nconst services = { $solid: Solid };\n\nexport * from './auth';\nexport * from './components';\nexport * from './errors';\nexport * from './services/Solid';\nexport { Solid };\n\nexport interface Options {\n autoReconnect?: boolean;\n authenticators?: Record<string, Authenticator>;\n defaultAuthenticator?: AuthenticatorName;\n onUserProfileLoaded?(user: SolidUserProfile, store: SolidStore): Promise<unknown> | unknown;\n}\n\nexport type SolidServices = typeof services;\n\nexport default function solid(options: Options = {}): Plugin {\n return {\n async install(app) {\n bootSolidModels();\n registerAuthenticators({ ...baseAuthenticators, ...(options.authenticators ?? {}) });\n defineFormValidationRules();\n setDefaultAuthenticator(getAuthenticator(options.defaultAuthenticator ?? 'inrupt'));\n registerErrorHandler((error) => {\n if (!(error instanceof AuthenticationFailedError)) {\n return;\n }\n return error.description ? `${error.message} (${error.description})` : error.message;\n });\n if (typeof options.autoReconnect === 'boolean') {\n DEFAULT_STATE.autoReconnect = options.autoReconnect;\n Solid.hasPersistedState() || (Solid.autoReconnect = options.autoReconnect);\n }\n if (options.onUserProfileLoaded) {\n Events.on('solid:user-profile-loaded', ([user, store]) => {\n options.onUserProfileLoaded?.(user, store);\n });\n }\n await bootServices(app, services);\n },\n };\n}\n\ndeclare module '@aerogel/core' {\n interface Services extends SolidServices {}\n}\n"],"names":["AuthenticationCancelledError","JSError","getErrorMessage","message","description","AuthenticationFailedError","options","__publicField","ContainerAlreadyInUse","container","Authenticator","arr","SolidEngine","fail","listener","PromisedValue","fetch","sessionData","session","loginUrl","error","event","payload","callback","STORAGE_KEY","InruptAuthenticator","Storage","AerogelSolid","after","events","handleIncomingRedirect","login","logout","errorDescription","webId","user","Solid","LegacyAuthenticator","persistent","SolidAuthClient","required","input","_authenticators","authenticators","setDefaultAuthenticator","authenticator","getAuthenticator","name","registerAuthenticators","customAuthenticators","registerAuthenticator","DEFAULT_STATE","Service","defineServiceState","state","_a","_b","SolidService","url","profileStore","markStale","userProfile","tap","fetchLoginUserProfile","store","profile","Events","arrayUnique","objectWithout","isDevelopment","UI","translateWithDefault","force","loginOptions","isCloudReady","App","hasLocalChanges","confirmLogout","resolve","onLogin","privateTypeIndexUrl","engine","resetAsyncMemo","asyncMemo","SolidTypeIndex","publicTypeIndexUrl","SolidContainer","existingContainer","typeIndex","Errors","hasLocationQueryParameter","getLocationQueryParameter","arrayFilter","setAsyncMemo","authenticatorName","handleLoginError","staleTimeout","initialState","oidcIssuerUrl","urlRoot","loginDomain","requireUrlParse","objectWithoutEmpty","parseBoolean","setEngine","SolidACLAuthorization","SolidDocument","arrayWithout","facade","defineFormValidationRules","defineFormValidationRule","value","sourceUrl","computedAsync","cachedResponse","Cache","__props","downloadImage","blob","response","userInitials","computed","part","AUTHENTICATOR_LABELS","form","useForm","requiredStringInput","stringInput","authenticatorOptions","measured","ref","usingManualUrl","showInput","showDevLogin","renderedClasses","classes","renderedButtonClasses","renderAuthenticator","option","submit","services","solid","app","bootSolidModels","baseAuthenticators","registerErrorHandler","bootServices"],"mappings":";;;;;;;;;;AAEA,MAAqBA,WAAqCC,EAAQ;AAAC;ACCnE,SAASC,GAAgBC,GAAiBC,GAAqC;AAEhE,SAAAD;AAIf;AAEA,MAAqBE,UAAkCJ,EAAQ;AAAA,EAI3D,YAAYE,GAAiBC,GAA6BE,GAA0B;AAC1E,UAAAJ,GAAgBC,CAAO,GAAGG,CAAO;AAH3B,IAAAC,EAAA;AAKZ,SAAK,cAAcH;AAAA,EAAA;AAG3B;ACjBA,MAAqBI,WAA8BP,EAAQ;AAAA,EAEvD,YAAYQ,GAA2BH,GAA0B;AAC7D,UAAM,gBAAgBG,EAAU,GAAG,sBAAsBH,CAAO;AAAA,EAAA;AAGxE;ACmBA,MAA8BI,GAAc;AAAA,EAA5C;AAEW,IAAAH,EAAA;AAEG,IAAAA,EAAA;AACA,IAAAA,EAAA;AACA,IAAAA,EAAA,mBAAgDI,GAA2B,EAAE;AAC7E,IAAAJ,EAAA;AAAA;AAAA,EAMV,IAAW,SAAiB;AACxB,gBAAK,UAAU,KAAK,WAAW,IAAIK,GAAY,KAAK,2BAA2B,GAExE,KAAK;AAAA,EAAA;AAAA,EAGT,wBAAsC;AACzC,WAAO,KAAK,sBAAsB;AAAA,EAAA;AAAA,EAG/B,4BAAmC;AAC/B,WAAA,KAAK,sBAAsBC,EAAK,kCAAkC;AAAA,EAAA;AAAA,EAGtE,YAAYC,GAA6C;AACxD,WAAC,KAAK,UAAU,SAASA,CAAQ,KAAG,KAAK,UAAU,KAAKA,CAAQ,GAE7D,MAAM;AACT,WAAK,eAAeA,CAAQ;AAAA,IAChC;AAAA,EAAA;AAAA,EAGG,eAAeA,GAAuC;AACzD,IAAK,KAAK,UAAU,SAASA,CAAQ,KAEhC,KAAA,UAAU,OAAOA,CAAQ;AAAA,EAAA;AAAA,EAGlC,MAAa,OAAsB;AAC/B,QAAI,KAAK;AACL,aAAO,KAAK;AAGX,SAAA,SAAS,IAAIC,GAAc,GAEhC,MAAM,KAAK,eAAe,GAE1B,KAAK,OAAO,QAAQ;AAAA,EAAA;AAAA,EAKxB,MAAgB,uBAAuBC,GAA6B;AAChE,SAAK,qBAAqBA,GAEpB,MAAA,KAAK,gBAAgB,6BAA6BA,CAAK;AAAA,EAAA;AAAA,EAGjE,MAAgB,aAAaC,GAAuE;AAChG,UAAMC,IAAuB;AAAA,MACzB,eAAe;AAAA,MACf,GAAGD;AAAA,IACP;AAEM,iBAAA,KAAK,gBAAgB,oBAAoBC,CAAO,GAE/CA;AAAA,EAAA;AAAA,EAGX,MAAgB,YAAYC,GAAkBC,GAAmC;AAC7E,UAAM,KAAK,gBAAgB,mBAAmBD,GAAUC,CAAK;AAAA,EAAA;AAAA,EAGjE,MAAgB,aAA4B;AAClC,UAAA,KAAK,gBAAgB,gBAAgB;AAAA,EAAA;AAAA,EAG/C,MAAgB,gBACZC,MACGC,GACU;AACb,UAAM,QAAQ;AAAA,MACV,KAAK,UAAU,QAAU,EAAA,IAAI,OAAOR,MAAa;AACvC,cAAAS,IAAWT,EAASO,CAAK;AAEzB,eAAAE,KAAA,gBAAAA,EAAW,GAAGD;AAAA,MACvB,CAAA;AAAA,IACL;AAAA,EAAA;AAGR;ACzGA,MAAME,IAAc;AAEpB,MAAqBC,WAA4Bf,GAAc;AAAA,EAA/D;AAAA;AAEY,IAAAH,EAAA;AACA,IAAAA,EAAA;AACA,IAAAA,EAAA;AACA,IAAAA,EAAA;AACA,IAAAA,EAAA;AAAA;AAAA,EAER,MAAa,MAAMY,GAAwC;AAC/C,IAAAO,EAAA,IAAYF,GAAaL,CAAQ;AAEzC,UAAMb,IAA8B;AAAA,MAChC,YAAYa;AAAA,IAChB;AAEA,WAAIQ,EAAa,aACLrB,EAAA,WAAWqB,EAAa,SAAS,WACjCrB,EAAA,aAAaqB,EAAa,SAAS,aAC3CrB,EAAQ,cAAcqB,EAAa,SAAS,cAAc,CAAC,IAGzD,MAAA,KAAK,OAAOrB,CAAO,GAGzB,MAAMsB,EAAM,EAAE,SAAS,IAAI,GAEpBf,EAAK,+CAAgD;AAAA,EAAA;AAAA,EAGhE,MAAa,SAAwB;AACjC,UAAM,KAAK,QAAQ,GACnB,MAAM,KAAK,WAAW;AAAA,EAAA;AAAA,EAG1B,MAAgB,iBAAgC;AACtC,UAAA,EAAE,QAAAgB,GAAQ,OAAAb,GAAO,wBAAAc,GAAwB,OAAAC,GAAO,QAAAC,EAAO,IAAI,MAAM,OAAO,wCAA4B;AAE1G,SAAK,SAAShB,GACd,KAAK,SAASe,GACd,KAAK,UAAUC,GACf,KAAK,UAAUH,GACf,KAAK,0BAA0BC,GAE/B,MAAM,KAAK,kBAAkB;AAAA,EAAA;AAAA,EAGjC,MAAgB,oBAAmC;AACzC,UAAAX,IAAWO,EAAQ,IAAYF,CAAW;AAChD,QAAI,CAACL;AACD;AAGJ,SAAK,UAAU,GAAG,SAAS,CAACC,GAAsBa,MAA6C;AACjF,MAAAb,UAAA,4CAEN,OAAOa,KAAqB,WACvB,KAAA,YAAYd,GAAU,IAAId,EAA0Be,GAAO,MAAM,EAAE,OAAOa,EAAiB,CAAC,CAAC,IAElG,KAAK,YAAYd,GAAU,IAAId,EAA0Be,GAAOa,CAAgB,CAAC;AAAA,IACrF,CACH;AAED,UAAMf,IAAU,MAAM,KAAK,wBAAwB,WAAW,SAAS,IAAI;AAE3E,IAAAQ,EAAQ,OAAOF,CAAW,GAEtBN,KAAA,QAAAA,EAAS,cAAcA,EAAQ,SACzB,MAAA,KAAK,YAAYA,EAAQ,KAAK;AAAA,EACxC;AAAA,EAGJ,MAAgB,YAAYgB,GAA8B;AAChD,UAAA,KAAK,uBAAuB,KAAK,MAAM;AAEzC,QAAA;AACA,YAAMC,IAAO,MAAMC,EAAM,mBAAmBF,CAAK;AAEjD,YAAM,KAAK,aAAa,EAAE,MAAAC,GAAM,UAAUD,GAAO;AAAA,aAC5Cd,GAAO;AACN,YAAA,KAAK,YAAYc,GAAOd,CAAK;AAAA,IAAA;AAAA,EACvC;AAGR;ACvFA,MAAqBiB,WAA4B3B,GAAc;AAAA,EAI3D,cAAc;AACJ,UAAA;AAHF,IAAAH,EAAA;AAKJ,SAAK,QAAQ+B,GAAW,wBAAwB,CAAA,CAAE;AAAA,EAAA;AAAA,EAGtD,MAAa,MAAMnB,GAAkBgB,GAAsD;AACvF,UAAM,EAAE,iBAAAI,EAAA,IAAoB,MAAM,OAAO,wCAA4B;AAOrE,UALK,KAAA,MAAM,QAAQJ,KAAA,gBAAAA,EAAM,OACpB,KAAA,MAAM,YAAWA,KAAA,gBAAAA,EAAM,kBAAiBhB,GAE9B,MAAMoB,EAAgB,MAAM,KAAK,MAAM,QAAQ,MAE/C,QACX,OAAO,KAAK,MAAM,OAClB,OAAO,KAAK,MAAM,UAEZ,IAAIlC,EAA0B,4DAA4D,MAIpG,MAAMuB,EAAM,EAAE,SAAS,IAAI,GAErB,IAAI,MAAM,+CAAgD;AAAA,EAAA;AAAA,EAGpE,MAAa,SAAwB;AACjC,UAAM,EAAE,iBAAAW,EAAA,IAAoB,MAAM,OAAO,wCAA4B;AAErE,WAAO,KAAK,MAAM,OAClB,OAAO,KAAK,MAAM,UAElB,MAAMA,EAAgB,OAAO,GAC7B,MAAM,KAAK,WAAW;AAAA,EAAA;AAAA,EAG1B,MAAgB,iBAAgC;AACxC,QAAA,CAAC,KAAK,MAAM;AACZ;AAGJ,UAAM,EAAE,iBAAAA,EAAA,IAAoB,MAAM,OAAO,wCAA4B,GAE/DrB,IAAU,MAAMqB,EAAgB,eAAe,GAC/CpB,IAAWqB,EAAS,KAAK,MAAM,QAAQ;AAE7C,UAAM,KAAK,uBAAuB,CAACC,GAAoBnC,MAC5CiC,EAAgB,MAAME,GAAOnC,CAAO,CAC9C;AAEG,QAAA;AACM,YAAA6B,IAAO,MAAMC,EAAM,mBAAmBI,GAAStB,KAAA,gBAAAA,EAAS,UAAS,KAAK,MAAM,KAAK,CAAC;AAExF,YAAM,KAAK,aAAa,EAAE,MAAAiB,GAAM,UAAAhB,GAAU;AAAA,aACrCC,GAAO;AACN,YAAA,KAAK,YAAYD,GAAUC,CAAK;AAAA,IAAA;AAAA,EAC1C;AAGR;AC3EA,MAAMsB,IAAkB,CAAC,GAOZC,KAAiB;AAAA,EAC1B,QAAQ,IAAIlB,GAAoB;AAAA,EAChC,QAAQ,IAAIY,GAAoB;AACpC;AAEO,SAASO,GAAwBC,GAAoC;AACxE,EAAAH,EAAgB,UAAUG;AAC9B;AAEO,SAASC,GAA8CC,GAA4B;AACtF,SAAOL,EAAgBK,CAAI;AAC/B;AAEO,SAASC,GAAuBC,GAA2D;AAC9F,SAAO,QAAQA,CAAoB,EAAE,QAAQ,CAAC,CAACF,GAAMF,CAAa,MAC9DK,GAAsBH,GAA2BF,CAAa,CAAC;AACvE;AAEgB,SAAAK,GAAmDH,GAASF,GAAwC;AAChH,EAAAH,EAAgBK,CAAI,IAAIF,GAEVA,EAAA,OAAOA,EAAc,QAAQE;AAC/C;AC1Ba,MAAAI,IAAgB,EAAE,eAAe,GAAK,GAEnDC,KAAeC,GAAmB;AAAA,EAC9B,MAAM;AAAA,EACN,SAAS,CAAC,iBAAiB,aAAa,mBAAmB,YAAY,eAAe;AAAA,EACtF,cAAc;AAAA,IACV,eAAeF,EAAc;AAAA,IAC7B,WAAW;AAAA,IACX,4BAA4B;AAAA,IAC5B,mBAAmB;AAAA,IACnB,cAAc;AAAA,IACd,wBAAwB;AAAA,IACxB,iBAAiB;AAAA,IAMjB,UAAU,CAAC;AAAA,IACX,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,eAAe,CAAA;AAAA,EACnB;AAAA,EACA,UAAUG,GAAO;;AACT,aAAAC,IAAAD,EAAM,oBAAN,gBAAAC,EAAuB,kBAAiB,UACxCD,EAAM,gBAAgB,QAAQA,EAAM,gBAAgB,MAAM,UAGvDA;AAAA,EACX;AAAA,EACA,UAAU;AAAA,IACN,MAAM,CAACA,MAAU;;AAAA,eAAAC,IAAAD,EAAM,YAAN,gBAAAC,EAAe,WAAQC,IAAAF,EAAM,oBAAN,gBAAAE,EAAuB,YAAW;AAAA;AAAA,IAC1E,eAAe,CAACF,MAAU;;AAAA,eAAAC,IAAAD,EAAM,YAAN,gBAAAC,EAAe,kBAAiB;AAAA;AAAA,IAC1D,QAAe;;AACX,eAAOA,IAAA,KAAK,kBAAL,gBAAAA,EAAoB,4BAA2B,WAAW,MAAM,KAAK,UAAU;AAAA,IAC1F;AAAA,IACA,MAAMD,GAA2B;;AAC7B,aAAIA,EAAM,oBACCA,EAAM,qBAAqB,OAGlCA,EAAM,6BACC,SAGJC,IAAAD,EAAM,oBAAN,gBAAAC,EAAuB,UAAS;AAAA,IAAA;AAAA,EAC3C;AAER,CAAC;ACDM,MAAME,WAAqBL,GAAQ;AAAA,EAE/B,aAAqG;AACjG,WAAA,CAAC,CAAC,KAAK;AAAA,EAAA;AAAA,EAGX,cAAkD;AAC9C,WAAA,CAAC,CAAC,KAAK;AAAA,EAAA;AAAA,EAGX,cAAkD;AACrD,WAAO,CAAC,EAAE,KAAK,WAAW,KAAK;AAAA,EAAA;AAAA,EAG5B,uBAAsC;AAClC,WAAA,KAAK,iBAAiBvC,EAAK,6BAA6B;AAAA,EAAA;AAAA,EAG5D,cAAgC;AAC5B,WAAA,KAAK,QAAQA,EAAK,iDAAiD;AAAA,EAAA;AAAA,EAGvE,mBAAmB6C,GAAwC;AAC9D,WAAO,KAAK,eAAeA,GAAK,EAAE,UAAU,IAAM;AAAA,EAAA;AAAA;AAAA,EAQtD,MAAa,eAAeA,GAAapD,IAA8B,IAAsC;;AACzG,QAAIqD,IAAkC;AACtC,UAAMC,IAAYtD,EAAQ,aAAa,GAACiD,IAAA,KAAK,kBAAL,QAAAA,EAAoB,0BACtDM,IACF,KAAK,SAASH,CAAG,KAChB,MAAMI;AAAA,MACH,MAAMC,GAAsBL,GAAK;AAAA,QAC7B,OAAO,KAAK;AAAA,QACZ,UAAUpD,EAAQ,YAAY;AAAA,QAC9B,SAAS0D,GAAO;AACG,UAAAL,IAAAK;AAAA,QAAA;AAAA,MACnB,CACH;AAAA,MACD,OAAOC,MAAY;AACf,QAAKA,MAIDN,KACA,MAAMO,EAAO,KAAK,6BAA6B,CAACD,GAASN,CAAY,CAAC,GAG1E,KAAK,gBAAgBM,CAAO;AAAA,MAAA;AAAA,IAEpC;AAEJ,WAAIJ,KAAeD,KACf,KAAK,SAAS,EAAE,eAAeO,EAAY,KAAK,cAAc,OAAO,CAACN,EAAY,KAAK,CAAC,CAAC,GAAG,GAGzFA;AAAA,EAAA;AAAA,EAGX,MAAa,qBAAoC;AACzC,QAAA,CAAC,KAAK;AACN;AAGJ,SAAK,SAAS,EAAE,UAAUO,EAAc,KAAK,UAAU,CAAC,KAAK,KAAK,KAAK,CAAC,EAAA,CAAG;AAE3E,UAAMjC,IAAO,MAAM,KAAK,mBAAmB,KAAK,KAAK,KAAK;AAE1D,SAAK,SAAS;AAAA,MACV,SAAS;AAAA,QACL,GAAG,KAAK;AAAA,QACR,MAAAA;AAAA,MAAA;AAAA,IACJ,CACH;AAAA,EAAA;AAAA,EAGL,MAAa,MAAMhB,GAAkBb,IAAwB,IAAsB;AAU3E,QATA+D,GAAA,KAAmBlD,MAAa,UACrBA,IAAA,0BAGV,eAAe,KAAKA,CAAQ,MACrBb,EAAA,gBAAAA,EAAA,cAAgB,UAAUa,CAAQ,KAC1CA,IAAW,WAAWA,CAAQ,KAG9B,KAAK;AACE,aAAA;AAGX,QAAI,KAAK;AACC,YAAA,IAAI,MAAM,oCAAoC;AAGpD,WAACb,EAAQ,UAINgE,EAAG,QAAQC,EAAqB,sBAAsB,eAAe,GAAG,MACpE,KAAK,aAAapD,GAAUb,CAAO,CAC7C,IALU,KAAK,aAAaa,GAAUb,CAAO;AAAA,EAK7C;AAAA,EAGL,MAAa,UAAUA,IAA4B,IAAmB;AAClE,UAAM,EAAE,OAAAkE,GAAO,GAAGC,EAAA,IAAiBnE;AAC/B,IAAA,KAAK,gBAAgB,CAAC,KAAK,mBAAoB,KAAK,gBAAgB,UAAU,QAAQ,CAACkE,KAI3F,MAAM,KAAK,MAAM,KAAK,gBAAgB,UAAU;AAAA,MAC5C,eAAe,KAAK,gBAAgB;AAAA,MACpC,GAAGC;AAAA,MACH,SAASA,EAAa,WAAW;AAAA,IAAA,CACpC;AAAA,EAAA;AAAA,EAGL,MAAa,OAAOD,IAAiB,IAAsB;;AACjD,UAAAE,IAAe,CAACC,EAAI,OAAO,sBAAsB,KAAK,CAAC,GAACpB,IAAAoB,EAAI,QAAQ,QAAQ,MAApB,QAAApB,EAAuB,QAC/EqB,IAAkB,CAACD,EAAI,OAAO,sBAAsB,KAAK,CAAC,GAACnB,IAAAmB,EAAI,QAAQ,QAAQ,MAApB,QAAAnB,EAAuB,QAClF,CAACqB,GAAevE,CAAO,IAAIkE,IAC3B,CAAC,IAAM,EAAE,eAAeE,EAAA,CAAc,IACtC,MAAMJ,EAAG;AAAA,MACPI,IACMH,EAAqB,4BAA4B,2BAA2B,IAC5EA,EAAqB,gCAAgC,qBAAqB;AAAA,MAChFG,IACME,IACIL;AAAA,QACE;AAAA,QACA;AAAA,MAAA,IAGFA;AAAA,QACE;AAAA,QACA;AAAA,MAAA,IAGNA;AAAA,QACE;AAAA,QACA;AAAA,MACJ;AAAA,MACJ;AAAA,QACI,YAAYA,EAAqB,6BAA6B,SAAS;AAAA,QACvE,eAAe;AAAA,QACf,YAAYA;AAAA,UACR;AAAA,UACAA,EAAqB,aAAa,QAAQ;AAAA,QAC9C;AAAA,QACA,YAAYG,IACNE,IACI;AAAA,UACE,eAAe;AAAA,YACX,OAAOL;AAAA,cACH;AAAA,cACA;AAAA,YACJ;AAAA,YACA,SAAS;AAAA,YACT,UAAU;AAAA,UAAA;AAAA,YAGhB,SACJ;AAAA,UACE,eAAe;AAAA,YACX,OAAOA;AAAA,cACH;AAAA,cACA;AAAA,YACJ;AAAA,YACA,SAAS;AAAA,UAAA;AAAA,QACb;AAAA,MACJ;AAAA,IAEZ;AAEJ,IAAKM,KAIA,KAAK,mBAIV,MAAMP,EAAG,QAAQC,EAAqB,uBAAuB,gBAAgB,GAAG,YAAY;AACxF,WAAK,SAAS;AAAA,QACV,WAAW;AAAA,QACX,4BAA4B;AAAA,QAC5B,mBAAmB;AAAA,QACnB,iBAAiB;AAAA,QACjB,GAAGpB;AAAA,MAAA,CACN,GAED,KAAK,WAAW,KAAM,MAAM,KAAK,cAAc,OAAO,IAElD7C,EAAQ,iBAAiB,OACzB,MAAMoB,GAAQ,MAAM,GAGlB,MAAAwC,EAAO,KAAK,aAAa,GACzB,MAAAA,EAAO,KAAK,mBAAmB;AAAA,IAAA,CACxC;AAAA,EAAA;AAAA,EAGL,MAAa,aAAgB3C,GAAmD;AACxE,WAAA,KAAK,eACEA,EAAS,KAAK,OAAO,IAGzB,IAAI,QAAQ,CAACuD,MAAY;AAC5B,YAAMC,IAAU,CAAC7D,MAAyB4D,EAAQvD,EAASL,CAAO,CAAC;AAE5D,MAAAgD,EAAA,KAAK,cAAca,CAAO;AAAA,IAAA,CACpC;AAAA,EAAA;AAAA,EAGE,UAAgB;AACnB,SAAK,SAAS,EAAE,WAAW,GAAA,CAAM;AAAA,EAAA;AAAA,EAGrC,MAAa,+BAAwD;AACjE,WAAQ,MAAM,KAAK,qBAA4B,KAAA,MAAM,KAAK,uBAAuB;AAAA,EAAA;AAAA,EAGrF,MAAa,qBAAqBzE,IAA+B,IAAoC;AACjG,UAAM,EAAE,OAAA4B,GAAO,qBAAA8C,MAAwB,KAAK,YAAY,GAClDC,IAAS,KAAK,qBAAA,EAAuB;AAE3C,WAAKD,KAID1E,EAAQ,SACO4E,GAAA,GAAGhD,CAAK,mBAAmB,GAGvCiD,GAAU,GAAGjD,CAAK,qBAAqB,MAC1CkD,EAAe,WAAWH,CAAM,EAAE,KAAKD,CAAmB,CAAC,KARpD;AAAA,EAQoD;AAAA,EAGnE,MAAa,8BAAuD;AAChE,WAAQ,MAAM,KAAK,oBAA2B,KAAA,MAAM,KAAK,sBAAsB;AAAA,EAAA;AAAA,EAGnF,MAAa,oBAAoB1E,IAA+B,IAAoC;AAChG,UAAM,EAAE,OAAA4B,GAAO,oBAAAmD,MAAuB,KAAK,YAAY,GACjDJ,IAAS,KAAK,qBAAA,EAAuB;AAE3C,WAAKI,KAID/E,EAAQ,SACO4E,GAAA,GAAGhD,CAAK,kBAAkB,GAGtCiD,GAAU,GAAGjD,CAAK,oBAAoB,MAAMkD,EAAe,WAAWH,CAAM,EAAE,KAAKI,CAAkB,CAAC,KAPlG;AAAA,EAOkG;AAAA,EAGjH,MAAa,uBAAuB/E,GAKR;AAClB,UAAA2E,IAAS,KAAK,qBAAA,EAAuB;AAEpC,WAAAK,EAAe,WAAWL,GAAQ,YAAY;AACjD,YAAMM,IAAoB,MAAMD,EAAe,KAAKhF,EAAQ,GAAG;AAE3D,UAAAiF,KAAqB,CAACjF,EAAQ;AACxB,cAAA,IAAIE,GAAsB+E,CAAiB;AAG/C,YAAA9E,IAAY8E,KAAqB,IAAID,EAAe,EAAE,KAAKhF,EAAQ,KAAK,MAAMA,EAAQ,KAAA,CAAM;AAIlG,UAFA,MAAMG,EAAU,KAAK,GAEjBH,EAAQ,aAAa;AACf,cAAAkF,IAAY,MAAM,KAAK,6BAA6B;AAE1D,cAAM/E,EAAU,SAAS+E,GAAWlF,EAAQ,WAAW;AAAA,MAAA;AAGpD,aAAAG;AAAA,IAAA,CACV;AAAA,EAAA;AAAA,EAGL,MAAyB,OAAsB;;AAC3C,UAAMgF,EAAO,QAETC,EAA0B,eAAe,KACzC,KAAK,SAAS;AAAA,MACV,wBAAwBC,EAA0B,eAAe;AAAA,IAAA,CACpE,GAGDD,EAA0B,gBAAgB,KAC1C,KAAK,SAAS;AAAA,MACV,eAAevB;AAAA,QACX,KAAK,cAAc,OAAOyB,GAAY,EAACrC,IAAA,KAAK,YAAL,gBAAAA,EAAc,KAAK,QAAOC,IAAA,KAAK,oBAAL,gBAAAA,EAAsB,QAAQ,CAAC,CAAC;AAAA,MAAA;AAAA,IACrG,CACH,GAGL,MAAM,KAAK,uBAAuB,GAClC,MAAM,KAAK,iBAAiB;AAAA,EAAA;AAAA,EAGhC,MAAgB,yBAAkD;AACxD,UAAArB,IAAO,KAAK,YAAY,GACxBU,IAAgB,KAAK,qBAAqB,GAC1C2C,IAAY,MAAMJ,EAAe,WAAWvC,EAAc,MAAM,EAAE,cAAcV,CAAI;AAEnF,WAAA2B,EAAI0B,GAAW,MAAM;AACxB,MAAArD,EAAK,sBAAsBqD,EAAU,KAErCK,GAAa,GAAG1D,EAAK,KAAK,qBAAqBqD,CAAS,GAExD,KAAK,gBAAgBrD,CAAI;AAAA,IAAA,CAC5B;AAAA,EAAA;AAAA,EAGL,MAAa,wBAAiD;AACpD,UAAAA,IAAO,KAAK,YAAY,GACxBU,IAAgB,KAAK,qBAAqB,GAC1C2C,IAAY,MAAMJ,EAAe,WAAWvC,EAAc,MAAM,EAAE,aAAaV,CAAI;AAElF,WAAA2B,EAAI0B,GAAW,MAAM;AACxB,MAAArD,EAAK,sBAAsBqD,EAAU,KAErCK,GAAa,GAAG1D,EAAK,KAAK,oBAAoBqD,CAAS,GAEvD,KAAK,gBAAgBrD,CAAI;AAAA,IAAA,CAC5B;AAAA,EAAA;AAAA,EAGL,MAAgB,aAAahB,GAAkBb,GAAyC;AACpF,UAAMwF,IAAoBxF,EAAQ,iBAAiB,KAAK,0BAA0B,WAC5EyF,IACFzF,EAAQ,YACP,CAACc,MACEuD,EAAI,cAAcc,EAAO,OAAOrE,CAAK,IAAI,KAAK,SAAS,EAAE,mBAAmBA,GAAO,IACrF4E,IAAe,WAAW,MAAO,KAAK,aAAa,IAAO,GAAK,GAC/DC,IAAe;AAAA,MACjB,WAAW,KAAK;AAAA,MAChB,4BAA4B,KAAK;AAAA,MACjC,iBAAiB,KAAK;AAAA,IAC1B;AAEA,SAAK,eAAe;AAEhB,QAAA;AACM,YAAAhC,IAAU,MAAM,KAAK,eAAe9C,GAAU,EAAE,WAAW,IAAM,GACjE+E,KAAgBjC,KAAA,gBAAAA,EAAS,kBAAiBkC,IAAQlC,KAAA,gBAAAA,EAAS,UAAS9C,CAAQ,GAC5E0B,IAAgB,MAAM,KAAK,kBAAkBiD,CAAiB,GAC9D,EAAE,QAAQM,MAAgBC,GAAgBlF,CAAQ;AAExD,kBAAK,SAAS;AAAA,QACV,WAAW;AAAA,QACX,4BAA4B;AAAA,QAC5B,iBAAiBmF,GAAmB;AAAA,UAChC,SAAArC;AAAA,UACA,UAAA9C;AAAA,UACA,eAAe2E;AAAA,UACf,OAAOvB;AAAA,YACH;AAAA,YACA;AAAA,YACA,EAAE,QAAQ6B,EAAY;AAAA,UAAA;AAAA,QAE7B,CAAA;AAAA,MAAA,CACJ,GAEK,MAAAlC,EAAO,KAAK,mBAAmB,GAI/B,MAAArB,EAAc,MAAMqD,GAAejC,CAAO,GAEzC;AAAA,aACF7C,GAAO;AAGZ,aAFA,KAAK,SAAS6E,CAAY,GAEtB7E,aAAiBpB,KACV,KAGPM,EAAQ,cACDsB,EAAM,EAAE,KAAK,MAAM,KAAK,MAAMtB,EAAQ,eAAe,IAAI8D,EAAc9D,GAAS,aAAa,CAAC,CAAC,KAG1GyF,EAAiB3E,CAAK,GAEf;AAAA,IAAA,UACT;AACE,mBAAa4E,CAAY,GAEzB,KAAK,SAAS;AAAA,QACV,cAAc;AAAA,QACd,YAAY;AAAA,MAAA,CACf;AAAA,IAAA;AAAA,EACL;AAAA,EAGJ,MAAc,yBAAwC;AAC9C,IAAC,KAAK,mBAIV,MAAM,KAAK,kBAAkB,KAAK,gBAAgB,aAAa;AAAA,EAAA;AAAA,EAGnE,MAAc,mBAAkC;AACxC,IAAC,KAAK,wBAIV,MAAM,KAAK,UAAU,EAAE,OAAO,KAAK,iBAAA,GAAoB;AAAA,EAAA;AAAA,EAGnD,qBAA8B;AAC9B,WAAAN,EAA0B,eAAe,IAClCa,GAAaZ,EAA0B,eAAe,CAAC,IAG3D,KAAK;AAAA,EAAA;AAAA,EAGR,mBAA4B;AAChC,WAAOD,EAA0B,eAAe,KAAKa,GAAaZ,EAA0B,eAAe,CAAC;AAAA,EAAA;AAAA,EAGhH,MAAc,kBAAkB5C,GAAiD;AACvE,UAAAF,IAAgBC,GAAiBC,CAAI;AAE3C,WAAAF,EAAc,YAAY;AAAA,MACtB,kBAAkB,OAAO3B,MAAY;AACjC,aAAK,SAAS;AAAA,UACV,SAAAA;AAAA,UACA,iBAAiB;AAAA,YACb,eAAe2B,EAAc;AAAA,YAC7B,UAAU3B,EAAQ;AAAA,YAClB,SAASA,EAAQ;AAAA,YACjB,OAAO;AAAA,UAAA;AAAA,QACX,CACH,GAEG,KAAK,cAAc,SAASA,EAAQ,KAAK,KAAK,KAC9C,MAAM,KAAK,mBAAmB,GAGlCyD,EAAI,OAAO,sBAAsB,KAAK6B,GAAUtF,EAAQ,cAAc,MAAM,GAEtDuF,GAAA,UAAUvF,EAAQ,cAAc,MAAM,GAC7CkE,EAAA,UAAUlE,EAAQ,cAAc,MAAM,GACvCwF,GAAA,UAAUxF,EAAQ,cAAc,MAAM,GAE9C,MAAAgD,EAAO,KAAK,cAAchD,CAAO;AAAA,MAC3C;AAAA,MACA,iBAAiB,OAAOC,GAAUC,MAAU;;AACxC,aAAK,SAAS;AAAA,UACV,SAAS;AAAA,UACT,iBAAiB;AAAA,YACb,UAASmC,IAAA,KAAK,oBAAL,gBAAAA,EAAsB;AAAA,YAC/B,eAAeV,EAAc;AAAA,YAC7B,UAAA1B;AAAA,YACA,OAAAC;AAAA,UAAA;AAAA,QACJ,CACH;AAAA,MACL;AAAA,MACA,gBAAgB,MAAM,KAAK,SAAS,EAAE,SAAS,MAAM;AAAA,MACrD,2BAA2B,CAACJ,MAAUkD,EAAO,KAAK,oBAAoBlD,CAAK;AAAA,IAAA,CAC9E,GAED,MAAM6B,EAAc,KAAK,GAElBA;AAAA,EAAA;AAAA,EAGH,gBAAgBoB,GAAiC;AACrD,SAAK,SAAS;AAAA,MACV,UAAU;AAAA,QACN,GAAG,KAAK;AAAA,QACR,CAACA,EAAQ,KAAK,GAAGA;AAAA,MACrB;AAAA,MACA,eAAe0C,GAAa,KAAK,eAAe1C,EAAQ,KAAK;AAAA,IAAA,CAChE;AAAA,EAAA;AAGT;AAEA,MAAe7B,IAAAwE,GAAOnD,EAAY;ACjiB3B,SAASoD,KAAkC;AACb,EAAAC,GAAA,iBAAiB,CAACC,MAAU;AACrD,QAAA,CAAAA,EAAM,SAAS,GAAG;AAIf,aAAA;AAAA,EAAA,CACV;AACL;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACAM,UAAAC,IAAYC,GAAc,YAAY;AAClC,YAAAC,IAAkB,MAAMC,EAAM,IAAIC,EAAG,GAAA,KAAO,MAAMC,EAAcD,EAAA,GAAG,GACnEE,IAAO,OAAMJ,KAAA,gBAAAA,EAAgB;AAE5B,aAAAI,KAAQ,IAAI,gBAAgBA,CAAI;AAAA,IAAA,CAC1C;AAED,mBAAeD,EAAc3D,GAAa;AAClC,UAAA;AACA,cAAM6D,IAAW,MAAMnF,EAAM,MAAMsB,CAAG;AAElC,eAAA6D,EAAS,WAAW,MACb,QAGL,MAAAJ,EAAM,MAAMzD,GAAK6D,CAAQ,GAExBJ,EAAM,IAAIzD,CAAG;AAAA,cACR;AACL,eAAA;AAAA,MAAA;AAAA,IACX;;;;;;;;;;ACNE,UAAA8D,IAAeC,EAAS,MAAM;;AAGzB,gBAFMlE,IAAAnB,EAAM,SAAN,gBAAAmB,EAAY,SAAQ,IAG5B,OACA,MAAM,GAAG,EACT,IAAI,CAACmE,MAAS;;AAAA,gBAAAnE,IAAAmE,EAAK,CAAC,MAAN,gBAAAnE,EAAS;AAAA,OAAa,EACpC,KAAK,EAAE,EACP,MAAM,GAAG,CAAC;AAAA,IAAA,CAClB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AC0DD,UAAMoE,IAAuB;AAAA,MACzB,QAAQ;AAAA,MACR,QAAQ;AAAA,IACZ,GAeMC,IAAOC,GAAQ;AAAA,MACjB,KAAKC,GAAoB;AAAA,MACzB,eAAeC,GAAY;AAAA,IAAA,CAC9B,GACKC,IAAuB,OAAO,KAAKL,CAAoB,GACvDM,IAAWC,GAAI,EAAK,GACpBC,IAAiBD,GAAI,EAAK,GAC1BE,IAAYX,EAAS,MAAM,CAACY,EAAa,SAASF,EAAe,KAAK,GACtEE,IAAeZ;AAAA,MACjB,MAAM9C,EAAI,gBAAgB,CAACiD,EAAK,OAAOA,EAAK,QAAQ,SAASA,EAAK,IAAI,OAAO,WAAW;AAAA,IAC5F,GACMU,IAAkBb,EAAS,MAAMc,EAAQ,qDAAqDnB,EAAW,KAAA,CAAC,GAC1GoB,IAAwBf,EAAS,MACnCc,EAAQ,4BAA4B,EAAE,aAAanB,EAAM,WAAK,gBAAgB,CAACQ,EAAK,cAAA,GAAiBR,EAAW,WAAA,CAAC;AAErH,aAASqB,EAAoBC,GAA2C;AAC7D,aAAAnE;AAAA,QACH,kCAAkCmE,EAAO,OAAO,CAAC,EAAE,YAAgB,IAAAA,EAAO,MAAM,CAAC,CAAC;AAAA,QAClFf,EAAqBe,CAAM;AAAA,MAC/B;AAAA,IAAA;AAGJ,mBAAeC,KAAS;AACpB,UAAIvB,EAAS,WAAE;AACL,cAAAhF,EAAM,MAAMwF,EAAK,KAAK,EAAE,eAAeA,EAAK,eAAoC;AAEtF;AAAA,MAAA;AAGE,YAAAtD,EAAG,QAAQC,EAAqB,sBAAsB,eAAe,GAAGnC,EAAM,MAAMwF,EAAK,GAAG,CAAC;AAAA,IAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ICxHjGgB,KAAW,EAAE,QAAQxG,EAAM;AAiBT,SAAAyG,GAAMvI,IAAmB,IAAY;AAClD,SAAA;AAAA,IACH,MAAM,QAAQwI,GAAK;AACC,MAAAC,GAAA,GACO/F,GAAA,EAAE,GAAGgG,IAAoB,GAAI1I,EAAQ,kBAAkB,CAAA,GAAK,GACzDuG,GAAA,GAC1BjE,GAAwBE,GAAiBxC,EAAQ,wBAAwB,QAAQ,CAAC,GAClF2I,GAAqB,CAAC7H,MAAU;AACxB,YAAEA,aAAiBf;AAGhB,iBAAAe,EAAM,cAAc,GAAGA,EAAM,OAAO,KAAKA,EAAM,WAAW,MAAMA,EAAM;AAAA,MAAA,CAChF,GACG,OAAOd,EAAQ,iBAAkB,cACjC6C,EAAc,gBAAgB7C,EAAQ,eACtC8B,EAAM,kBAAkB,MAAMA,EAAM,gBAAgB9B,EAAQ,iBAE5DA,EAAQ,uBACR4D,EAAO,GAAG,6BAA6B,CAAC,CAAC/B,GAAM6B,CAAK,MAAM;;AAC9C,SAAAT,IAAAjD,EAAA,wBAAA,QAAAiD,EAAA,KAAAjD,GAAsB6B,GAAM6B;AAAA,MAAK,CAC5C,GAEC,MAAAkF,GAAaJ,GAAKF,EAAQ;AAAA,IAAA;AAAA,EAExC;AACJ;"}
package/package.json CHANGED
@@ -1,42 +1,49 @@
1
1
  {
2
2
  "name": "@aerogel/plugin-solid",
3
- "description": "Aerogel Solid plugin",
4
- "version": "0.0.1",
5
- "main": "dist/aerogel-plugin-solid.cjs.js",
6
- "module": "dist/aerogel-plugin-solid.esm.js",
7
- "types": "dist/aerogel-plugin-solid.d.ts",
3
+ "version": "0.1.0",
4
+ "type": "module",
8
5
  "sideEffects": false,
9
- "scripts": {
10
- "build": "rm dist -rf && npm run build:js && npm run build:types",
11
- "build:js": "noeldemartin-build-javascript",
12
- "build:types": "noeldemartin-build-types",
13
- "publish-next": "noeldemartin-publish-next",
14
- "lint": "noeldemartin-lint src"
15
- },
16
- "engines": {
17
- "node": ">=18.x"
18
- },
19
- "repository": {
20
- "type": "git",
21
- "url": "https://github.com/NoelDeMartin/aerogeljs.git"
6
+ "exports": {
7
+ "types": "./dist/aerogel-plugin-solid.d.ts",
8
+ "style": "./src/index.css",
9
+ "default": "./dist/aerogel-plugin-solid.js"
22
10
  },
23
- "keywords": [
24
- "javascript",
25
- "aerogel"
11
+ "files": [
12
+ "src",
13
+ "dist"
26
14
  ],
27
- "author": "Noel De Martin",
28
15
  "license": "MIT",
29
- "bugs": {
30
- "url": "https://github.com/NoelDeMartin/aerogeljs/issues"
16
+ "author": "Noel De Martin",
17
+ "repository": "github:NoelDeMartin/aerogel",
18
+ "scripts": {
19
+ "build": "vite build",
20
+ "lint": "noeldemartin-lint src",
21
+ "test": "vitest --run",
22
+ "test:ci": "vitest --run --reporter verbose",
23
+ "verify": "noeldemartin-verify"
31
24
  },
32
25
  "peerDependencies": {
33
26
  "@aerogel/core": "*",
27
+ "@aerogel/vite": "*",
28
+ "soukai": "*",
34
29
  "soukai-solid": "*"
35
30
  },
36
31
  "dependencies": {
37
- "@inrupt/solid-client-authn-browser": "^1.17.1",
38
- "@noeldemartin/solid-utils": "^0.3.0",
39
- "@noeldemartin/utils": "0.4.0-next.ac00beaecf32bb02ed8e335225d7948d946d73bd",
40
- "vue": "^3.3.0"
32
+ "@inrupt/solid-client-authn-browser": "^2.3.0",
33
+ "@noeldemartin/solid-utils": "^0.6.1",
34
+ "@noeldemartin/utils": "^0.7.1",
35
+ "solid-auth-client": "2.4.1"
36
+ },
37
+ "devDependencies": {
38
+ "@aerogel/core": "*",
39
+ "@aerogel/plugin-local-first": "0.0.0-next.4c77ee2164e8a4f72bdd920cb56ec059299ccc5f",
40
+ "@aerogel/vite": "*",
41
+ "soukai": "*",
42
+ "soukai-solid": "*"
43
+ },
44
+ "eslintConfig": {
45
+ "extends": [
46
+ "@noeldemartin/eslint-config-vue"
47
+ ]
41
48
  }
42
49
  }
@@ -6,12 +6,12 @@ import type { Fetch } from 'soukai-solid';
6
6
  import type { SolidUserProfile } from '@noeldemartin/solid-utils';
7
7
  import type { ErrorSource } from '@aerogel/core';
8
8
 
9
- import type { AuthenticatorName } from '@/auth';
9
+ import type { AuthenticatorName } from '@aerogel/plugin-solid/auth';
10
10
 
11
11
  type ListenerEvent = keyof AuthenticatorListener;
12
12
  type ListenerEventPayload<
13
13
  Event extends ListenerEvent,
14
- Callback = AuthenticatorListener[Event]
14
+ Callback = AuthenticatorListener[Event],
15
15
  > = Callback extends Closure ? GetClosureArgs<Callback> : never;
16
16
 
17
17
  export interface AuthSession {
@@ -31,12 +31,12 @@ export default abstract class Authenticator {
31
31
 
32
32
  public name!: AuthenticatorName;
33
33
 
34
- private authenticatedFetch?: Fetch;
35
- private booted?: PromisedValue<void>;
36
- private listeners: FluentArray<AuthenticatorListener> = arr<AuthenticatorListener>([]);
37
- private _engine?: SolidEngine;
34
+ protected authenticatedFetch?: Fetch;
35
+ protected booted?: PromisedValue<void>;
36
+ protected listeners: FluentArray<AuthenticatorListener> = arr<AuthenticatorListener>([]);
37
+ protected _engine?: SolidEngine;
38
38
 
39
- public abstract login(loginUrl: string, user?: SolidUserProfile): Promise<AuthSession>;
39
+ public abstract login(loginUrl: string, user?: SolidUserProfile | null): Promise<AuthSession>;
40
40
 
41
41
  public abstract logout(): Promise<void>;
42
42
 
@@ -69,7 +69,9 @@ export default abstract class Authenticator {
69
69
  }
70
70
 
71
71
  public async boot(): Promise<void> {
72
- if (this.booted) return this.booted;
72
+ if (this.booted) {
73
+ return this.booted;
74
+ }
73
75
 
74
76
  this.booted = new PromisedValue();
75
77
 
@@ -0,0 +1 @@
1
+ export { events, fetch, handleIncomingRedirect, login, logout } from '@inrupt/solid-client-authn-browser';
@@ -1,26 +1,44 @@
1
+ import AerogelSolid from 'virtual:aerogel-solid';
2
+
1
3
  import { Storage, after, fail } from '@noeldemartin/utils';
2
4
  import type { Fetch } from 'soukai-solid';
3
- import type { handleIncomingRedirect, login, logout } from '@inrupt/solid-client-authn-browser';
4
-
5
- import Authenticator from '@/auth/Authenticator';
6
- import type { AuthSession } from '@/auth/Authenticator';
5
+ import type {
6
+ ILoginInputOptions,
7
+ events,
8
+ handleIncomingRedirect,
9
+ login,
10
+ logout,
11
+ } from '@inrupt/solid-client-authn-browser';
12
+
13
+ import AuthenticationFailedError from '@aerogel/plugin-solid/errors/AuthenticationFailedError';
14
+ import Authenticator from '@aerogel/plugin-solid/auth/Authenticator';
15
+ import Solid from '@aerogel/plugin-solid/services/Solid';
16
+ import type { AuthSession } from '@aerogel/plugin-solid/auth/Authenticator';
7
17
 
8
18
  const STORAGE_KEY = 'inrupt-authenticator';
9
19
 
10
20
  export default class InruptAuthenticator extends Authenticator {
11
21
 
22
+ private _events!: typeof events;
12
23
  private _fetch!: Fetch;
13
24
  private _login!: typeof login;
14
25
  private _logout!: typeof logout;
15
26
  private _handleIncomingRedirect!: typeof handleIncomingRedirect;
16
27
 
17
28
  public async login(loginUrl: string): Promise<AuthSession> {
18
- Storage.set<boolean>(STORAGE_KEY, true);
29
+ Storage.set<string>(STORAGE_KEY, loginUrl);
19
30
 
20
- await this._login({
31
+ const options: ILoginInputOptions = {
21
32
  oidcIssuer: loginUrl,
22
- // TODO clientId, clientName, and redirectUrl
23
- });
33
+ };
34
+
35
+ if (AerogelSolid.clientID) {
36
+ options.clientId = AerogelSolid.clientID.client_id;
37
+ options.clientName = AerogelSolid.clientID.client_name;
38
+ options.redirectUrl = AerogelSolid.clientID.redirect_uris[0];
39
+ }
40
+
41
+ await this._login(options);
24
42
 
25
43
  // Browser should redirect, so just make it wait for a while.
26
44
  await after({ seconds: 60 });
@@ -34,22 +52,34 @@ export default class InruptAuthenticator extends Authenticator {
34
52
  }
35
53
 
36
54
  protected async restoreSession(): Promise<void> {
37
- const { fetch, handleIncomingRedirect, login, logout } = await import('@inrupt/solid-client-authn-browser');
55
+ const { events, fetch, handleIncomingRedirect, login, logout } = await import('./InruptAuthenticator.lazy');
38
56
 
39
57
  this._fetch = fetch;
40
58
  this._login = login;
41
59
  this._logout = logout;
60
+ this._events = events;
42
61
  this._handleIncomingRedirect = handleIncomingRedirect;
43
62
 
44
63
  await this.loginFromRedirect();
45
64
  }
46
65
 
47
66
  protected async loginFromRedirect(): Promise<void> {
48
- if (!Storage.has(STORAGE_KEY)) {
67
+ const loginUrl = Storage.get<string>(STORAGE_KEY);
68
+ if (!loginUrl) {
49
69
  return;
50
70
  }
51
71
 
52
- const session = await this._handleIncomingRedirect(window.location.href);
72
+ this._events().on('error', (error: string | null, errorDescription?: string | Error | null) => {
73
+ error ??= 'Error using Inrupt Authenticator method';
74
+
75
+ if (typeof errorDescription === 'object') {
76
+ this.failSession(loginUrl, new AuthenticationFailedError(error, null, { cause: errorDescription }));
77
+ } else {
78
+ this.failSession(loginUrl, new AuthenticationFailedError(error, errorDescription));
79
+ }
80
+ });
81
+
82
+ const session = await this._handleIncomingRedirect(globalThis.location.href);
53
83
 
54
84
  Storage.remove(STORAGE_KEY);
55
85
 
@@ -62,7 +92,6 @@ export default class InruptAuthenticator extends Authenticator {
62
92
  await this.initAuthenticatedFetch(this._fetch);
63
93
 
64
94
  try {
65
- const { default: Solid } = await import('@/services/Solid');
66
95
  const user = await Solid.requireUserProfile(webId);
67
96
 
68
97
  await this.startSession({ user, loginUrl: webId });
@@ -0,0 +1 @@
1
+ export { default as SolidAuthClient } from 'solid-auth-client';
@@ -0,0 +1,80 @@
1
+ import { after, required } from '@noeldemartin/utils';
2
+ import { persistent } from '@aerogel/core';
3
+ import type { SolidUserProfile } from '@noeldemartin/solid-utils';
4
+ import type { UnwrapNestedRefs } from 'vue';
5
+
6
+ import Authenticator from '@aerogel/plugin-solid/auth/Authenticator';
7
+ import AuthenticationFailedError from '@aerogel/plugin-solid/errors/AuthenticationFailedError';
8
+ import Solid from '@aerogel/plugin-solid/services/Solid';
9
+ import type { AuthSession } from '@aerogel/plugin-solid/auth/Authenticator';
10
+
11
+ interface Data {
12
+ webId?: string;
13
+ loginUrl?: string;
14
+ }
15
+
16
+ export default class LegacyAuthenticator extends Authenticator {
17
+
18
+ private store: UnwrapNestedRefs<Data>;
19
+
20
+ constructor() {
21
+ super();
22
+
23
+ this.store = persistent('legacy-authenticator', {});
24
+ }
25
+
26
+ public async login(loginUrl: string, user?: SolidUserProfile | null): Promise<AuthSession> {
27
+ const { SolidAuthClient } = await import('./LegacyAuthenticator.lazy');
28
+
29
+ this.store.webId = user?.webId;
30
+ this.store.loginUrl = user?.oidcIssuerUrl ?? loginUrl;
31
+
32
+ const result = await SolidAuthClient.login(this.store.loginUrl);
33
+
34
+ if (result === null) {
35
+ delete this.store.webId;
36
+ delete this.store.loginUrl;
37
+
38
+ throw new AuthenticationFailedError('Could not log in with Solid using the legacy authenticator');
39
+ }
40
+
41
+ // Browser should redirect, so just make it wait for a while.
42
+ await after({ seconds: 60 });
43
+
44
+ throw new Error('Browser should have redirected, but it didn\'t');
45
+ }
46
+
47
+ public async logout(): Promise<void> {
48
+ const { SolidAuthClient } = await import('./LegacyAuthenticator.lazy');
49
+
50
+ delete this.store.webId;
51
+ delete this.store.loginUrl;
52
+
53
+ await SolidAuthClient.logout();
54
+ await this.endSession();
55
+ }
56
+
57
+ protected async restoreSession(): Promise<void> {
58
+ if (!this.store.loginUrl) {
59
+ return;
60
+ }
61
+
62
+ const { SolidAuthClient } = await import('./LegacyAuthenticator.lazy');
63
+
64
+ const session = await SolidAuthClient.currentSession();
65
+ const loginUrl = required(this.store.loginUrl);
66
+
67
+ await this.initAuthenticatedFetch((input: RequestInfo, options?: Object) => {
68
+ return SolidAuthClient.fetch(input, options);
69
+ });
70
+
71
+ try {
72
+ const user = await Solid.requireUserProfile(required(session?.webId ?? this.store.webId));
73
+
74
+ await this.startSession({ user, loginUrl });
75
+ } catch (error) {
76
+ await this.failSession(loginUrl, error);
77
+ }
78
+ }
79
+
80
+ }
package/src/auth/index.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import InruptAuthenticator from './authenticators/InruptAuthenticator';
2
+ import LegacyAuthenticator from './authenticators/LegacyAuthenticator';
2
3
  import type Authenticator from './Authenticator';
3
4
 
4
5
  const _authenticators = {} as Authenticators;
@@ -6,10 +7,11 @@ const _authenticators = {} as Authenticators;
6
7
  type BaseAuthenticators = typeof authenticators;
7
8
 
8
9
  export * from './Authenticator';
9
- export { Authenticator };
10
+ export { default as Authenticator } from './Authenticator';
10
11
 
11
12
  export const authenticators = {
12
13
  inrupt: new InruptAuthenticator(),
14
+ legacy: new LegacyAuthenticator(),
13
15
  };
14
16
 
15
17
  export function setDefaultAuthenticator(authenticator: Authenticator): void {
@@ -0,0 +1,35 @@
1
+ <template>
2
+ <div class="relative flex items-center justify-center rounded-full bg-gray-200">
3
+ <SolidImage
4
+ v-if="$solid.user?.avatarUrl"
5
+ :src="$solid.user?.avatarUrl"
6
+ alt=""
7
+ class="absolute inset-0 size-full rounded-full"
8
+ />
9
+ <span v-else-if="userInitials" class="text-sm font-semibold tracking-wider text-gray-500" aria-hidden="true">
10
+ {{ userInitials }}
11
+ </span>
12
+ <IconAnonymous v-else class="size-6 text-gray-500" />
13
+ </div>
14
+ </template>
15
+
16
+ <script setup lang="ts">
17
+ import IconAnonymous from '~icons/ooui/user-anonymous';
18
+
19
+ import { computed } from 'vue';
20
+
21
+ import Solid from '@aerogel/plugin-solid/services/Solid';
22
+
23
+ import SolidImage from './SolidImage.vue';
24
+
25
+ const userInitials = computed(() => {
26
+ const name = Solid.user?.name ?? '';
27
+
28
+ return name
29
+ .trim()
30
+ .split(' ')
31
+ .map((part) => part[0]?.toUpperCase())
32
+ .join('')
33
+ .slice(0, 3);
34
+ });
35
+ </script>
@@ -0,0 +1,33 @@
1
+ <template>
2
+ <img :src="sourceUrl">
3
+ </template>
4
+
5
+ <script setup lang="ts">
6
+ import { Cache, computedAsync } from '@aerogel/core';
7
+
8
+ import Solid from '@aerogel/plugin-solid/services/Solid';
9
+
10
+ const { src } = defineProps<{ src: string }>();
11
+ const sourceUrl = computedAsync(async () => {
12
+ const cachedResponse = (await Cache.get(src)) ?? (await downloadImage(src));
13
+ const blob = await cachedResponse?.blob();
14
+
15
+ return blob && URL.createObjectURL(blob);
16
+ });
17
+
18
+ async function downloadImage(url: string) {
19
+ try {
20
+ const response = await Solid.fetch(url);
21
+
22
+ if (response.status !== 200) {
23
+ return null;
24
+ }
25
+
26
+ await Cache.store(url, response);
27
+
28
+ return Cache.get(url);
29
+ } catch (error) {
30
+ return null;
31
+ }
32
+ }
33
+ </script>