@atproto/oauth-client-browser 0.0.0-spaces-alpha-20260818163953

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 (60) hide show
  1. package/CHANGELOG.md +702 -0
  2. package/LICENSE.txt +7 -0
  3. package/README.md +339 -0
  4. package/dist/browser-oauth-client.d.ts +58 -0
  5. package/dist/browser-oauth-client.d.ts.map +1 -0
  6. package/dist/browser-oauth-client.js +358 -0
  7. package/dist/browser-oauth-client.js.map +1 -0
  8. package/dist/browser-oauth-database.d.ts +54 -0
  9. package/dist/browser-oauth-database.d.ts.map +1 -0
  10. package/dist/browser-oauth-database.js +160 -0
  11. package/dist/browser-oauth-database.js.map +1 -0
  12. package/dist/browser-runtime-implementation.d.ts +9 -0
  13. package/dist/browser-runtime-implementation.d.ts.map +1 -0
  14. package/dist/browser-runtime-implementation.js +40 -0
  15. package/dist/browser-runtime-implementation.js.map +1 -0
  16. package/dist/errors.d.ts +9 -0
  17. package/dist/errors.d.ts.map +1 -0
  18. package/dist/errors.js +11 -0
  19. package/dist/errors.js.map +1 -0
  20. package/dist/index.d.ts +8 -0
  21. package/dist/index.d.ts.map +1 -0
  22. package/dist/index.js +8 -0
  23. package/dist/index.js.map +1 -0
  24. package/dist/indexed-db/db-index.d.ts +12 -0
  25. package/dist/indexed-db/db-index.d.ts.map +1 -0
  26. package/dist/indexed-db/db-index.js +40 -0
  27. package/dist/indexed-db/db-index.js.map +1 -0
  28. package/dist/indexed-db/db-object-store.d.ts +17 -0
  29. package/dist/indexed-db/db-object-store.d.ts.map +1 -0
  30. package/dist/indexed-db/db-object-store.js +38 -0
  31. package/dist/indexed-db/db-object-store.js.map +1 -0
  32. package/dist/indexed-db/db-transaction.d.ts +13 -0
  33. package/dist/indexed-db/db-transaction.d.ts.map +1 -0
  34. package/dist/indexed-db/db-transaction.js +45 -0
  35. package/dist/indexed-db/db-transaction.js.map +1 -0
  36. package/dist/indexed-db/db.d.ts +17 -0
  37. package/dist/indexed-db/db.d.ts.map +1 -0
  38. package/dist/indexed-db/db.js +94 -0
  39. package/dist/indexed-db/db.js.map +1 -0
  40. package/dist/indexed-db/index.d.ts +5 -0
  41. package/dist/indexed-db/index.d.ts.map +1 -0
  42. package/dist/indexed-db/index.js +5 -0
  43. package/dist/indexed-db/index.js.map +1 -0
  44. package/dist/indexed-db/schema.d.ts +3 -0
  45. package/dist/indexed-db/schema.d.ts.map +1 -0
  46. package/dist/indexed-db/schema.js +2 -0
  47. package/dist/indexed-db/schema.js.map +1 -0
  48. package/dist/indexed-db/util.d.ts +3 -0
  49. package/dist/indexed-db/util.d.ts.map +1 -0
  50. package/dist/indexed-db/util.js +22 -0
  51. package/dist/indexed-db/util.js.map +1 -0
  52. package/dist/indexed-db-store.d.ts +18 -0
  53. package/dist/indexed-db-store.d.ts.map +1 -0
  54. package/dist/indexed-db-store.js +54 -0
  55. package/dist/indexed-db-store.js.map +1 -0
  56. package/dist/util.d.ts +18 -0
  57. package/dist/util.d.ts.map +1 -0
  58. package/dist/util.js +15 -0
  59. package/dist/util.js.map +1 -0
  60. package/package.json +50 -0
package/LICENSE.txt ADDED
@@ -0,0 +1,7 @@
1
+ Dual MIT/Apache-2.0 License
2
+
3
+ Copyright (c) 2022-2026 Bluesky Social PBC, and Contributors
4
+
5
+ Except as otherwise noted in individual files, this software is licensed under the MIT license (<http://opensource.org/licenses/MIT>), or the Apache License, Version 2.0 (<http://www.apache.org/licenses/LICENSE-2.0>).
6
+
7
+ Downstream projects and end users may chose either license individually, or both together, at their discretion. The motivation for this dual-licensing is the additional software patent assurance provided by Apache 2.0.
package/README.md ADDED
@@ -0,0 +1,339 @@
1
+ # atproto OAuth Client for the Browser
2
+
3
+ This package provides a browser specific OAuth client implementation for
4
+ atproto. It implements all the OAuth features required by [ATPROTO] (PKCE, DPoP,
5
+ etc.).
6
+
7
+ `@atproto/oauth-client-browser` is designed for front-end applications that do
8
+ not have a backend server to manage OAuth sessions, a.k.a "Single Page
9
+ Applications" (SPA).
10
+
11
+ > [!IMPORTANT]
12
+ >
13
+ > When a backend server is available, it is recommended to use
14
+ > [`@atproto/oauth-client-node`](https://www.npmjs.com/package/@atproto/oauth-client-node)
15
+ > to manage OAuth sessions from the server side and use a session cookie to map
16
+ > the OAuth session to the front-end. Because this mechanism allows the backend
17
+ > to invalidate OAuth credentials at scale, this method is more secure than
18
+ > managing OAuth sessions from the front-end directly. Thanks to the added
19
+ > security, the OAuth server will provide longer lived tokens when issued to a
20
+ > BFF (Backend-for-frontend).
21
+
22
+ ## Setup
23
+
24
+ ### Client ID
25
+
26
+ The `client_id` is what identifies your application to the OAuth server. It is
27
+ used to fetch the client metadata and to initiate the OAuth flow. The
28
+ `client_id` must be a URL that points to the [client
29
+ metadata](#client-metadata).
30
+
31
+ ### Client Metadata
32
+
33
+ Your OAuth client metadata should be hosted at a URL that corresponds to the
34
+ `client_id` of your application. This URL should return a JSON object with the
35
+ client metadata. The client metadata should be configured according to the
36
+ needs of your application and must respect the [ATPROTO] spec.
37
+
38
+ ```json
39
+ {
40
+ // Must be the same URL as the one used to obtain this JSON object
41
+ "client_id": "https://my-app.com/client-metadata.json",
42
+ "client_name": "My App",
43
+ "client_uri": "https://my-app.com",
44
+ "logo_uri": "https://my-app.com/logo.png",
45
+ "tos_uri": "https://my-app.com/tos",
46
+ "policy_uri": "https://my-app.com/policy",
47
+ "redirect_uris": ["https://my-app.com/callback"],
48
+ "scope": "atproto",
49
+ "grant_types": ["authorization_code", "refresh_token"],
50
+ "response_types": ["code"],
51
+ "token_endpoint_auth_method": "none",
52
+ "application_type": "web",
53
+ "dpop_bound_access_tokens": true
54
+ }
55
+ ```
56
+
57
+ The client metadata is used to instantiate an OAuth client. There are two ways
58
+ of doing this:
59
+
60
+ 1. Either you "burn" the metadata into your application:
61
+
62
+ ```typescript
63
+ import { BrowserOAuthClient } from '@atproto/oauth-client-browser'
64
+
65
+ const client = new BrowserOAuthClient({
66
+ clientMetadata: {
67
+ // Exact same JSON object as the one returned by the client_id URL
68
+ },
69
+ // ...
70
+ })
71
+ ```
72
+
73
+ 2. Or you load it asynchronously from the URL:
74
+
75
+ ```typescript
76
+ import { OAuthClient } from '@atproto/oauth-client-browser'
77
+
78
+ const client = await BrowserOAuthClient.load({
79
+ clientId: 'https://my-app.com/client-metadata.json',
80
+ // ...
81
+ })
82
+ ```
83
+
84
+ If performances are important to you, it is recommended to burn the metadata
85
+ into the script. Server side rendering techniques can also be used to inject the
86
+ metadata into the script at runtime.
87
+
88
+ ### Handle Resolver
89
+
90
+ Whenever your application initiates an OAuth flow, it will start to resolve
91
+ the (user provider) APTROTO handle of the user. This is typically done though a
92
+ DNS request. However, because DNS resolution is not available in the browser, a
93
+ backend service must be provided.
94
+
95
+ > [!CAUTION]
96
+ >
97
+ > Using Bluesky-hosted services for handle resolution (eg, the `bsky.social`
98
+ > endpoint) will leak both user IP addresses and handle identifiers to Bluesky,
99
+ > a third party. While Bluesky has a declared privacy policy, both developers
100
+ > and users of applications need to be informed and aware of the privacy
101
+ > implications of this arrangement. Application developers are encouraged to
102
+ > improve user privacy by operating their own handle resolution service when
103
+ > possible. If you are a PDS self-hoster, you can use your PDS's URL for
104
+ > `handleResolver`.
105
+
106
+ If a `string` or `URL` object is used as `handleResolver`, the library will
107
+ expect this value to be the URL of a service running the
108
+ `com.atproto.identity.resolveHandle` XRPC Lexicon method.
109
+
110
+ > [!TIP]
111
+ >
112
+ > If you host your own PDS, you can use its URL as a handle resolver.
113
+
114
+ ```typescript
115
+ import { BrowserOAuthClient } from '@atproto/oauth-client-browser'
116
+
117
+ const client = new BrowserOAuthClient({
118
+ handleResolver: 'https://my-pds.example.com',
119
+ // ...
120
+ })
121
+ ```
122
+
123
+ Alternatively, if a "DNS over HTTPS" (DoH) service is available, it can be used
124
+ to resolve the handle. In this case, the `handleResolver` should be initialized
125
+ with a `AtprotoDohHandleResolver` instance:
126
+
127
+ ```typescript
128
+ import {
129
+ BrowserOAuthClient,
130
+ AtprotoDohHandleResolver,
131
+ } from '@atproto/oauth-client-browser'
132
+
133
+ const client = new BrowserOAuthClient({
134
+ handleResolver: new AtprotoDohHandleResolver('https://my-doh.example.com'),
135
+ // ...
136
+ })
137
+ ```
138
+
139
+ ### Other configuration options
140
+
141
+ In addition to [Client Metadata](#client-metadata) and [Handle
142
+ Resolver](#handle-resolver), the `BrowserOAuthClient` constructor accepts the
143
+ following optional configuration options:
144
+
145
+ - `fetch`: A custom wrapper around the `fetch` function. This can be useful to
146
+ add custom headers, logging, or to use a different fetch implementation.
147
+ Defaults to `window.fetch`.
148
+
149
+ - `responseMode`: `query` or `fragment`. Determines how the authorization
150
+ response is returned to the client. Defaults to `fragment`.
151
+
152
+ - `plcDirectoryUrl`: The URL of the PLC directory. This will typically not be
153
+ needed unless you run an entire atproto stack locally. Defaults to
154
+ `https://plc.directory`.
155
+
156
+ ## Usage
157
+
158
+ Once the `client` is set up, it can be used to initiate & manage OAuth sessions.
159
+
160
+ ### Initializing the client
161
+
162
+ The client will manage the sessions for you. In order to do so, it must first
163
+ initialize itself. Note that this operation must be performed once (and **only
164
+ once**) whenever the web app is loaded.
165
+
166
+ ```typescript
167
+ const result: undefined | { session: OAuthSession; state?: string } =
168
+ await client.init()
169
+
170
+ if (result) {
171
+ const { session, state } = result
172
+ if (state != null) {
173
+ console.log(
174
+ `${session.sub} was successfully authenticated (state: ${state})`,
175
+ )
176
+ } else {
177
+ console.log(`${session.sub} was restored (last active session)`)
178
+ }
179
+ }
180
+ ```
181
+
182
+ The return value can be used to determine if the client was able to restore the
183
+ last used session (`session` is defined) or if the current navigation is the
184
+ result of an authorization redirect (both `session` and `state` are defined).
185
+
186
+ ### Initiating an OAuth flow
187
+
188
+ In order to initiate an OAuth flow, we must first determine which PDS the
189
+ authentication flow will be initiated from. This means that the user must
190
+ provide one of the following information:
191
+
192
+ - The user's handle
193
+ - The user's DID
194
+ - A PDS/Entryway URL
195
+
196
+ Using that information, the OAuthClient will resolve all the needed information
197
+ to initiate the OAuth flow, and redirect the user to the OAuth server.
198
+
199
+ ```typescript
200
+ try {
201
+ await client.signIn('my.handle.com', {
202
+ state: 'some value needed later',
203
+ prompt: 'none', // Attempt to sign in without user interaction (SSO)
204
+ ui_locales: 'fr-CA fr en', // Only supported by some OAuth servers (requires OpenID Connect support + i18n support)
205
+ signal: new AbortController().signal, // Optional, allows to cancel the sign in (and destroy the pending authorization, for better security)
206
+ })
207
+
208
+ console.log('Never executed')
209
+ } catch (err) {
210
+ console.log('The user aborted the authorization process by navigating "back"')
211
+ }
212
+ ```
213
+
214
+ The returned promise will never resolve (because the user will be redirected to
215
+ the OAuth server). The promise will reject if the user cancels the sign in
216
+ (using an `AbortSignal`), or if the user navigates back from the OAuth server
217
+ (because of browser's back-forward cache).
218
+
219
+ ### Handling the OAuth response
220
+
221
+ When the user is redirected back to the application, the OAuth response will be
222
+ available in the URL. The `BrowserOAuthClient` will automatically detect the
223
+ response and handle it when `client.init()` is called. Alternatively, the
224
+ application can manually handle the response using the
225
+ `client.callback(urlQueryParams)` method.
226
+
227
+ ### Restoring a session
228
+
229
+ The client keeps track of all the sessions that it manages through an internal
230
+ store. Regardless of the session that was returned from the `client.init()`
231
+ call, any other session can be loaded using the `client.restore()` method. This
232
+ method will throw an error if the session is no longer available or if it has
233
+ become expired.
234
+
235
+ ```ts
236
+ const aliceSession = await client.restore('did:plc:alice')
237
+ const bobSession = await client.restore('did:plc:bob')
238
+ ```
239
+
240
+ In its current form, the client does not expose methods to list all sessions
241
+ in its store. The app will have to keep track of those itself.
242
+
243
+ ### Watching for session invalidation
244
+
245
+ The client will emit events whenever a session becomes unavailable, allowing to
246
+ trigger global behaviors (e.g. show the login page).
247
+
248
+ ```ts
249
+ client.addEventListener(
250
+ 'deleted',
251
+ (
252
+ event: CustomEvent<{
253
+ sub: string
254
+ cause: TokenRefreshError | TokenRevokedError | TokenInvalidError
255
+ }>,
256
+ ) => {
257
+ const { sub, cause } = event.detail
258
+ console.error(`Session for ${sub} is no longer available (cause: ${cause})`)
259
+ },
260
+ )
261
+ ```
262
+
263
+ ## Usage with `@atproto/api`
264
+
265
+ The `@atproto/api` package provides a way to interact with multiple Bluesky
266
+ specific XRPC lexicons (`com.atproto`, `app.bsky`, `chat.bsky`, `tools.ozone`)
267
+ through the `Agent` interface. The `oauthSession` returned by the
268
+ `BrowserOAuthClient` can be used to instantiate an `Agent` instance.
269
+
270
+ ```typescript
271
+ import { Agent } from '@atproto/api'
272
+
273
+ const session = await client.restore('did:plc:alice')
274
+
275
+ const agent = new Agent(session)
276
+
277
+ await agent.getProfile({ actor: agent.accountDid })
278
+ ```
279
+
280
+ Any refresh of the credentials will happen under the hood, and the new tokens
281
+ will be saved in the session store (in the browser's indexed DB).
282
+
283
+ ## Advances use-cases
284
+
285
+ ### Using in development (localhost)
286
+
287
+ The OAuth server must be able to fetch the `client_metadata` object. The best
288
+ way to do this if you didn't already deployed your app is to use a tunneling
289
+ service like [ngrok](https://ngrok.com/).
290
+
291
+ The `client_id` will then be something like
292
+ `https://<your-ngrok-id>.ngrok.io/<path_to_your_client_metadata>`.
293
+
294
+ There is however a special case for loopback clients. A loopback client is a
295
+ client that runs on `localhost`. In this case, the OAuth server will not be able
296
+ to fetch the `client_metadata` object because `localhost` is not accessible from
297
+ the outside. To work around this, atproto OAuth servers are required to support
298
+ this case by providing an hard coded `client_metadata` object for the client.
299
+
300
+ This has several restrictions:
301
+
302
+ 1. There is no way of configuring the client metadata (name, logo, etc.)
303
+ 2. The validity of the refresh tokens (if any) will be very limited (typically 1
304
+ day)
305
+ 3. Silent-sign-in will not be allowed
306
+ 4. Only `http://127.0.0.1:<any_port>` and `http://[::1]:<any_port>` can be used
307
+ as origin for your app, and **not** `http://localhost:<any_port>`. This
308
+ library will automatically redirect the user to an IP based origin
309
+ (`http://127.0.0.1:<port>`) when visiting an origin with `localhost`.
310
+
311
+ Using a loopback client is only recommended for development purposes. A loopback
312
+ client can be instantiated like this:
313
+
314
+ ```typescript
315
+ import { BrowserOAuthClient } from '@atproto/oauth-client-browser'
316
+
317
+ const client = new BrowserOAuthClient({
318
+ handleResolver: 'https://bsky.social',
319
+ // Only works if the current origin is a loopback address:
320
+ clientMetadata: undefined,
321
+ })
322
+ ```
323
+
324
+ If you need to use a special `redirect_uris`, you can configure them like this:
325
+
326
+ ```typescript
327
+ import { BrowserOAuthClient } from '@atproto/oauth-client-browser'
328
+
329
+ const client = new BrowserOAuthClient({
330
+ handleResolver: 'https://bsky.social',
331
+ // Note that the origin of the "client_id" URL must be "http://localhost" when
332
+ // using this configuration, regardless of the actual hostname ("127.0.0.1" or
333
+ // "[::1]"), port or pathname. Only the `redirect_uris` must contain the
334
+ // actual url that will be used to redirect the user back to the application.
335
+ clientMetadata: `http://localhost?redirect_uri=${encodeURIComponent('http://127.0.0.1:8080/callback')}`,
336
+ })
337
+ ```
338
+
339
+ [ATPROTO]: https://atproto.com/ 'AT Protocol'
@@ -0,0 +1,58 @@
1
+ import { type AuthorizeOptions, type Fetch, OAuthClient, type OAuthClientOptions, type OAuthSession } from '@atproto/oauth-client';
2
+ import { type OAuthClientMetadataInput, type OAuthResponseMode } from '@atproto/oauth-types';
3
+ import { type Simplify } from './util.js';
4
+ export type BrowserOAuthClientOptions = Simplify<{
5
+ clientMetadata?: Readonly<OAuthClientMetadataInput>;
6
+ responseMode?: Exclude<OAuthResponseMode, 'form_post'>;
7
+ fetch?: Fetch;
8
+ } & Omit<OAuthClientOptions, 'clientMetadata' | 'responseMode' | 'keyset' | 'fetch' | 'runtimeImplementation' | 'sessionStore' | 'stateStore' | 'didCache' | 'handleCache' | 'dpopNonceCache' | 'authorizationServerMetadataCache' | 'protectedResourceMetadataCache'>>;
9
+ export type BrowserOAuthClientLoadOptions = Simplify<{
10
+ clientId: string;
11
+ signal?: AbortSignal;
12
+ } & Omit<BrowserOAuthClientOptions, 'clientMetadata'>>;
13
+ export declare class BrowserOAuthClient extends OAuthClient implements AsyncDisposable {
14
+ static load({ clientId, ...options }: BrowserOAuthClientLoadOptions): Promise<BrowserOAuthClient>;
15
+ private readonly ac;
16
+ private readonly database;
17
+ constructor({ clientMetadata, responseMode, ...options }: BrowserOAuthClientOptions);
18
+ /**
19
+ * This method will automatically restore any existing session, or attempt to
20
+ * process login callback if the URL contains oauth parameters.
21
+ *
22
+ * Use {@link BrowserOAuthClient.initCallback} instead of this method if you
23
+ * want to force a login callback. This can be esp. useful if you are using
24
+ * this lib from a framework that has some kind of URL manipulation (like a
25
+ * client side router).
26
+ *
27
+ * Use {@link BrowserOAuthClient.initRestore} instead of this method if you
28
+ * want to only restore existing sessions, and bypass the automatic processing
29
+ * of login callbacks.
30
+ */
31
+ init(refresh?: boolean): Promise<{
32
+ session: OAuthSession;
33
+ state?: never;
34
+ } | {
35
+ session: OAuthSession;
36
+ state: string | null;
37
+ } | undefined>;
38
+ initRestore(refresh?: boolean): Promise<{
39
+ session: OAuthSession;
40
+ } | undefined>;
41
+ restore(sub: string, refresh?: boolean): Promise<OAuthSession>;
42
+ revoke(sub: string): Promise<void>;
43
+ signIn(input: string, options?: AuthorizeOptions): Promise<OAuthSession>;
44
+ signInRedirect(input: string, options?: AuthorizeOptions): Promise<never>;
45
+ signInPopup(input: string, options?: Omit<AuthorizeOptions, 'state'> & {
46
+ popupName?: string;
47
+ popupFeatures?: string;
48
+ }): Promise<OAuthSession>;
49
+ findRedirectUrl(): "http://127.0.0.1" | `${string}.${string}:/${string}` | `http://127.0.0.1#${string}` | `http://127.0.0.1/${string}` | `http://127.0.0.1:${string}` | `http://127.0.0.1?${string}` | `http://[::1]${string}` | `https://${string}` | undefined;
50
+ readCallbackParams(): URLSearchParams | null;
51
+ initCallback(params?: URLSearchParams | null, redirectUri?: "http://127.0.0.1" | `${string}.${string}:/${string}` | `http://127.0.0.1#${string}` | `http://127.0.0.1/${string}` | `http://127.0.0.1:${string}` | `http://127.0.0.1?${string}` | `http://[::1]${string}` | `https://${string}` | undefined): Promise<{
52
+ session: OAuthSession;
53
+ state: string | null;
54
+ }>;
55
+ [Symbol.asyncDispose](): Promise<void>;
56
+ dispose(): Promise<void>;
57
+ }
58
+ //# sourceMappingURL=browser-oauth-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser-oauth-client.d.ts","sourceRoot":"","sources":["../src/browser-oauth-client.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,gBAAgB,EAErB,KAAK,KAAK,EAEV,WAAW,EACX,KAAK,kBAAkB,EACvB,KAAK,YAAY,EAElB,MAAM,uBAAuB,CAAA;AAC9B,OAAO,EACL,KAAK,wBAAwB,EAC7B,KAAK,iBAAiB,EAIvB,MAAM,sBAAsB,CAAA;AAI7B,OAAO,EAAE,KAAK,QAAQ,EAAyB,MAAM,WAAW,CAAA;AAEhE,MAAM,MAAM,yBAAyB,GAAG,QAAQ,CAC9C;IACE,cAAc,CAAC,EAAE,QAAQ,CAAC,wBAAwB,CAAC,CAAA;IACnD,YAAY,CAAC,EAAE,OAAO,CAAC,iBAAiB,EAAE,WAAW,CAAC,CAAA;IACtD,KAAK,CAAC,EAAE,KAAK,CAAA;CACd,GAAG,IAAI,CACN,kBAAkB,EAEhB,gBAAgB,GAChB,cAAc,GACd,QAAQ,GACR,OAAO,GAEP,uBAAuB,GACvB,cAAc,GACd,YAAY,GACZ,UAAU,GACV,aAAa,GACb,gBAAgB,GAChB,kCAAkC,GAClC,gCAAgC,CACnC,CACF,CAAA;AAmCD,MAAM,MAAM,6BAA6B,GAAG,QAAQ,CAClD;IACE,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,CAAC,EAAE,WAAW,CAAA;CACrB,GAAG,IAAI,CAAC,yBAAyB,EAAE,gBAAgB,CAAC,CACtD,CAAA;AAID,qBAAa,kBAAmB,SAAQ,WAAY,YAAW,eAAe;IAC5E,OAAa,IAAI,CAAC,EAAE,QAAQ,EAAE,GAAG,OAAO,EAAE,EAAE,6BAA6B,+BAcxE;IAED,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAwB;IAE3C,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAsB;IAE/C,YAAY,EACV,cAEC,EAED,YAAyB,EACzB,GAAG,OAAO,EACX,EAAE,yBAAyB,EA2E3B;IAED;;;;;;;;;;;;OAYG;IACG,IAAI,CAAC,OAAO,CAAC,EAAE,OAAO,GAAG,OAAO,CAElC;QAAE,OAAO,EAAE,YAAY,CAAC;QAAC,KAAK,CAAC,EAAE,KAAK,CAAA;KAAE,GAExC;QAAE,OAAO,EAAE,YAAY,CAAC;QAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,GAE/C,SAAS,CACZ,CAUA;IAEK,WAAW,CAAC,OAAO,CAAC,EAAE,OAAO;;mBAelC;IAEK,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,YAAY,CAAC,CAInE;IAEK,MAAM,CAAC,GAAG,EAAE,MAAM,iBAGvB;IAEK,MAAM,CACV,KAAK,EAAE,MAAM,EACb,OAAO,CAAC,EAAE,gBAAgB,GACzB,OAAO,CAAC,YAAY,CAAC,CAMvB;IAEK,cAAc,CAClB,KAAK,EAAE,MAAM,EACb,OAAO,CAAC,EAAE,gBAAgB,GACzB,OAAO,CAAC,KAAK,CAAC,CAmBhB;IAEK,WAAW,CACf,KAAK,EAAE,MAAM,EACb,OAAO,CAAC,EAAE,IAAI,CAAC,gBAAgB,EAAE,OAAO,CAAC,GAAG;QAC1C,SAAS,CAAC,EAAE,MAAM,CAAA;QAClB,aAAa,CAAC,EAAE,MAAM,CAAA;KACvB,GACA,OAAO,CAAC,YAAY,CAAC,CAgFvB;IAEM,eAAe,kPAYrB;IAEM,kBAAkB,IAAI,eAAe,GAAG,IAAI,CAYlD;IAEY,YAAY,CACvB,MAAM,yBAA4B,EAClC,WAAW,gPAAyB,GACnC,OAAO,CAAC;QACT,OAAO,EAAE,YAAY,CAAA;QACrB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;KACrB,CAAC,CAwFD;IAEK,CAAC,MAAM,CAAC,YAAY,CAAC,kBAM1B;IAEK,OAAO,kBAEZ;CACF"}