@rool-dev/sdk 0.11.2 → 0.11.3-dev.521c0dc

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 (46) hide show
  1. package/README.md +181 -10
  2. package/dist/auth-base.d.ts +100 -0
  3. package/dist/auth-base.d.ts.map +1 -0
  4. package/dist/auth-base.js +377 -0
  5. package/dist/auth-base.js.map +1 -0
  6. package/dist/auth-browser.d.ts +6 -77
  7. package/dist/auth-browser.d.ts.map +1 -1
  8. package/dist/auth-browser.js +32 -328
  9. package/dist/auth-browser.js.map +1 -1
  10. package/dist/auth-native.d.ts +72 -0
  11. package/dist/auth-native.d.ts.map +1 -0
  12. package/dist/auth-native.js +260 -0
  13. package/dist/auth-native.js.map +1 -0
  14. package/dist/auth.d.ts +17 -1
  15. package/dist/auth.d.ts.map +1 -1
  16. package/dist/auth.js +36 -5
  17. package/dist/auth.js.map +1 -1
  18. package/dist/channel.d.ts +1 -4
  19. package/dist/channel.d.ts.map +1 -1
  20. package/dist/channel.js +0 -5
  21. package/dist/channel.js.map +1 -1
  22. package/dist/client.d.ts +30 -3
  23. package/dist/client.d.ts.map +1 -1
  24. package/dist/client.js +52 -6
  25. package/dist/client.js.map +1 -1
  26. package/dist/graphql.d.ts +9 -5
  27. package/dist/graphql.d.ts.map +1 -1
  28. package/dist/graphql.js +51 -29
  29. package/dist/graphql.js.map +1 -1
  30. package/dist/index.d.ts +5 -1
  31. package/dist/index.d.ts.map +1 -1
  32. package/dist/index.js +4 -0
  33. package/dist/index.js.map +1 -1
  34. package/dist/rest.d.ts +10 -1
  35. package/dist/rest.d.ts.map +1 -1
  36. package/dist/rest.js +30 -0
  37. package/dist/rest.js.map +1 -1
  38. package/dist/space.d.ts +21 -10
  39. package/dist/space.d.ts.map +1 -1
  40. package/dist/space.js +22 -21
  41. package/dist/space.js.map +1 -1
  42. package/dist/subscription.js +1 -1
  43. package/dist/subscription.js.map +1 -1
  44. package/dist/types.d.ts +84 -8
  45. package/dist/types.d.ts.map +1 -1
  46. package/package.json +1 -1
package/README.md CHANGED
@@ -142,6 +142,74 @@ if (!authenticated) {
142
142
  if (!authenticated) throw new Error('Login required');
143
143
  ```
144
144
 
145
+ ### Native (Capacitor, Cordova, Tauri, ...)
146
+
147
+ Use the native PKCE provider for JS app shells that sign in through an external
148
+ system browser. `login()`/`signup()` open the auth server's `/authorize` page
149
+ via the `openExternal` callback you supply; when the deep link returns, feed it
150
+ to `client.handleAuthRedirect(url)` from your platform's deep-link handler. The
151
+ code is exchanged for a session at `/token` and tokens are stored like the
152
+ browser provider.
153
+
154
+ ```typescript
155
+ import { RoolClient, NativePkceAuthProvider } from '@rool-dev/sdk';
156
+ import { Browser } from '@capacitor/browser';
157
+ import { App } from '@capacitor/app';
158
+
159
+ const client = new RoolClient({
160
+ authProvider: new NativePkceAuthProvider({
161
+ redirectUri: 'roolandroidauth://auth/callback', // must match the server allowlist
162
+ defaultProvider: 'google', // 'google' | 'apple'
163
+ openExternal: (url) => Browser.open({ url }),
164
+ }),
165
+ });
166
+
167
+ // Complete sign-in when the OS hands the app its deep link.
168
+ App.addListener('appUrlOpen', async ({ url }) => {
169
+ if (await client.handleAuthRedirect(url)) {
170
+ await Browser.close();
171
+ // Now authenticated — refresh your UI.
172
+ }
173
+ });
174
+
175
+ if (!(await client.initialize())) {
176
+ // Opens the system browser; completion arrives via the listener above.
177
+ await client.login('My App'); // pass { provider: 'apple' } to override the default
178
+ }
179
+ ```
180
+
181
+ #### Email + password and magic links (native)
182
+
183
+ The native provider also supports email/password sign-in and magic links —
184
+ no system browser, the server returns the token set as JSON directly.
185
+
186
+ ```typescript
187
+ // Password sign-in
188
+ const result = await client.signInWithPassword(email, password);
189
+ if (result.status === 'signed_in') {
190
+ // authenticated — refresh your UI
191
+ } else {
192
+ // status === 'verify_required': the email isn't verified yet and the server
193
+ // has emailed a magic link. Tell the user to check their inbox.
194
+ }
195
+
196
+ // Or request a magic link explicitly
197
+ await client.requestMagicLink(email);
198
+ ```
199
+
200
+ The magic link carries a `?verify=<token>` param; complete sign-in by passing
201
+ that token to `client.verify(token)` once the link lands back in the app.
202
+
203
+ > **⚠️ Magic links open the website, not the app, until Universal Links / App
204
+ > Links are configured.** The emailed link is an `https://` URL for the Rool
205
+ > web app. On native, an `https` link only re-opens your app if you've set up
206
+ > [iOS Universal Links](https://developer.apple.com/ios/universal-links/) /
207
+ > [Android App Links](https://developer.android.com/training/app-links) for that
208
+ > domain (custom-scheme deep links don't apply to email links). **Without that
209
+ > setup the magic link completes sign-in in the browser/website, not in the
210
+ > native app** — so for now treat magic links on native as a website hand-off,
211
+ > and prefer password or social sign-in for an in-app experience.
212
+
145
213
  ### Auth API
146
214
 
147
215
  | Method | Description |
@@ -150,6 +218,9 @@ if (!authenticated) throw new Error('Login required');
150
218
  | `login(appName, params?): Promise<void>` | Start login flow. |
151
219
  | `signup(appName, params?): Promise<void>` | Start signup flow. |
152
220
  | `verify(token): Promise<boolean>` | Complete email verification token flow; returns `false` when the active auth provider does not implement verification. |
221
+ | `handleAuthRedirect(url): Promise<boolean>` | Complete a native PKCE sign-in from a deep-link callback URL. Returns `false` when the active auth provider does not implement it. |
222
+ | `signInWithPassword(email, password): Promise<PasswordSignInResult>` | Email + password sign-in (native provider). Resolves `{ status: 'signed_in' }`, or `{ status: 'verify_required' }` when the email is unverified (a magic link was emailed). Rejects on bad credentials. |
223
+ | `requestMagicLink(email): Promise<void>` | Email the user a magic sign-in link (native provider). See the caveat below — on native the link opens the **website**, not the app, until Universal Links / App Links are configured. |
153
224
  | `logout(): void` | Clear auth state and close open spaces. |
154
225
  | `isAuthenticated(): Promise<boolean>` | Whether credentials are held locally. No network call — a server outage does not read as logged out. |
155
226
  | `getAuthUser(): AuthUser` | Return auth identity decoded from the token. |
@@ -490,16 +561,53 @@ High-level WebDAV methods that validate response status throw `WebDAVError` with
490
561
 
491
562
  ## Collaboration
492
563
 
564
+ New members join a space by redeeming an invite. Owners and admins mint invites; the returned `url` contains the secret token and is only available at mint time.
565
+
493
566
  ```typescript
494
- const user = await client.searchUser('colleague@example.com');
495
- if (user) {
496
- await space.addUser(user.id, 'editor');
567
+ // Shareable invite link
568
+ const invite = await space.createInvite('editor', { expiresInDays: 7 });
569
+ console.log(invite.url);
570
+
571
+ // Email-guarded invite: single-use, locked to that address, sent by mail
572
+ const emailed = await space.createInvite('viewer', { email: 'colleague@example.com' });
573
+ if (emailed.emailStatus !== 'sent') {
574
+ // Mail did not go out (e.g. no mail provider configured) — share emailed.url yourself
497
575
  }
498
576
 
499
- await space.setLinkAccess('viewer'); // 'none' | 'viewer' | 'editor'
577
+ // Manage outstanding invites
578
+ const invites = await space.listInvites();
579
+ await space.revokeInvite(invites[0].inviteId);
580
+
581
+ // Change an existing member's role, or remove them
582
+ await space.setUserRole(userId, 'admin');
583
+ await space.removeUser(userId);
584
+ ```
585
+
586
+ On the join page, look up the invite before sign-in and redeem it once authenticated:
587
+
588
+ ```typescript
589
+ const preview = await client.previewInvite(token); // no auth required
590
+ console.log(preview.spaceName, preview.role, preview.inviterName);
591
+
592
+ const result = await client.redeemInvite(token);
593
+ console.log(result.spaceId, result.status); // 'joined' | 'upgraded' | 'already_member'
594
+ ```
595
+
596
+ Invalid, expired, revoked, exhausted, or email-mismatched invites throw `InviteError` with a `code` of `'INVITE_INVALID' | 'INVITE_EXPIRED' | 'INVITE_REVOKED' | 'INVITE_EXHAUSTED' | 'INVITE_EMAIL_MISMATCH'`.
597
+
598
+ ```typescript
599
+ import { InviteError } from '@rool-dev/sdk';
600
+
601
+ try {
602
+ await client.redeemInvite(token);
603
+ } catch (error) {
604
+ if (error instanceof InviteError && error.code === 'INVITE_EXPIRED') {
605
+ // Ask for a fresh invite
606
+ }
607
+ }
500
608
  ```
501
609
 
502
- Roles:
610
+ Roles (invites grant `admin`, `editor`, or `viewer` — never `owner`):
503
611
 
504
612
  | Role | Capabilities |
505
613
  | --- | --- |
@@ -529,7 +637,8 @@ const client = new RoolClient({
529
637
  | `getCurrentUser(): Promise<CurrentUser>` | Fetch current user. |
530
638
  | `updateCurrentUser(input): Promise<CurrentUser>` | Update `name`, `slug`, or `marketingOptIn`. |
531
639
  | `deleteCurrentUser(): Promise<void>` | Mark account for deletion and log out. |
532
- | `searchUser(email): Promise<UserResult | null>` | Exact email lookup. |
640
+ | `previewInvite(token): Promise<InvitePreview>` | Look up an invite link without redeeming it. No auth required. |
641
+ | `redeemInvite(token): Promise<InviteRedeemResult>` | Redeem an invite, joining (or upgrading in) its space. |
533
642
  | `listSpaces(): Promise<RoolSpaceInfo[]>` | List accessible spaces. |
534
643
  | `openSpace(id): Promise<RoolSpace>` | Open/cached live space handle. |
535
644
  | `createSpace(name): Promise<RoolSpace>` | Create and open a space. |
@@ -561,7 +670,7 @@ client.on('error', (error, context) => void 0);
561
670
 
562
671
  ## RoolSpace API
563
672
 
564
- Properties: `id`, `name`, `role`, `linkAccess`, `memberCount`, `channels`, `route`, `webdav`.
673
+ Properties: `id`, `name`, `role`, `memberCount`, `channels`, `route`, `webdav`.
565
674
 
566
675
  | Method | Description |
567
676
  | --- | --- |
@@ -570,9 +679,11 @@ Properties: `id`, `name`, `role`, `linkAccess`, `memberCount`, `channels`, `rout
570
679
  | `rename(newName): Promise<void>` | Rename the space. |
571
680
  | `delete(): Promise<void>` | Permanently delete the space. |
572
681
  | `listUsers(): Promise<SpaceMember[]>` | List collaborators. |
573
- | `addUser(userId, role): Promise<void>` | Add collaborator. |
682
+ | `setUserRole(userId, role): Promise<void>` | Change an existing member's role. |
574
683
  | `removeUser(userId): Promise<void>` | Remove collaborator. |
575
- | `setLinkAccess(linkAccess): Promise<void>` | Set URL sharing level. |
684
+ | `createInvite(role, options?): Promise<SpaceInviteCreated>` | Mint an invite link; `options` takes `email`, `expiresInDays`, `maxUses`. |
685
+ | `listInvites(): Promise<SpaceInvite[]>` | List currently redeemable invites. |
686
+ | `revokeInvite(inviteId): Promise<boolean>` | Revoke an invite so its link stops working. |
576
687
  | `renameChannel(channelId, name): Promise<void>` | Rename a channel. |
577
688
  | `deleteChannel(channelId): Promise<void>` | Delete a channel and history. |
578
689
  | `exportArchive(): Promise<Blob>` | Export a space archive. |
@@ -593,7 +704,7 @@ space.on('connectionStateChanged', (state) => void 0);
593
704
 
594
705
  ## RoolChannel API
595
706
 
596
- Properties: `id` (space ID), `name` (space name), `role`, `linkAccess`, `userId`, `channelId`, `channelName`, `conversationId`, `isReadOnly`, `activeLeafId`.
707
+ Properties: `id` (space ID), `name` (space name), `role`, `userId`, `channelId`, `channelName`, `conversationId`, `isReadOnly`, `activeLeafId`.
597
708
 
598
709
  | Area | Methods |
599
710
  | --- | --- |
@@ -631,6 +742,10 @@ Archives include objects, metadata, channels/conversations, and file storage.
631
742
  ## Data Types
632
743
 
633
744
  ```typescript
745
+ // Outcome of signInWithPassword. 'verify_required' means the account's email
746
+ // isn't verified yet and the server has emailed a magic link.
747
+ type PasswordSignInResult = { status: 'signed_in' | 'verify_required' };
748
+
634
749
  type FieldType =
635
750
  | { kind: 'string' }
636
751
  | { kind: 'number' }
@@ -663,6 +778,62 @@ interface GetObjectsResult {
663
778
  missing: string[];
664
779
  }
665
780
 
781
+ type InviteRole = 'admin' | 'editor' | 'viewer';
782
+
783
+ interface SpaceInvite {
784
+ inviteId: string;
785
+ spaceId: string;
786
+ role: InviteRole;
787
+ email: string | null;
788
+ createdBy: string;
789
+ createdAt: string;
790
+ expiresAt: string;
791
+ maxUses: number | null;
792
+ useCount: number;
793
+ }
794
+
795
+ // Outcome of the invite email send. Null when no email was involved (open link).
796
+ // The invite is always minted and its `url` is usable regardless of this value;
797
+ // only the email delivery is reflected here.
798
+ // - 'sent': email dispatched
799
+ // - 'not_configured': server has no mail provider (local dev)
800
+ // - 'failed': provider rejected the send
801
+ // - 'cooldown': a recent invite to this same address was already emailed
802
+ // - 'rate_limited': the inviter hit their daily email-invite cap
803
+ // Treat unknown values as not sent.
804
+ type InviteEmailStatus =
805
+ | 'sent'
806
+ | 'not_configured'
807
+ | 'failed'
808
+ | 'cooldown'
809
+ | 'rate_limited'
810
+ | (string & {});
811
+
812
+ interface SpaceInviteCreated {
813
+ inviteId: string;
814
+ spaceId: string;
815
+ role: InviteRole;
816
+ email: string | null;
817
+ expiresAt: string;
818
+ maxUses: number | null;
819
+ url: string; // contains the secret token; only available at mint time
820
+ emailStatus: InviteEmailStatus | null;
821
+ }
822
+
823
+ interface InvitePreview {
824
+ spaceId: string;
825
+ spaceName: string;
826
+ role: InviteRole;
827
+ email: string | null;
828
+ inviterName: string | null;
829
+ }
830
+
831
+ interface InviteRedeemResult {
832
+ spaceId: string;
833
+ role: RoolUserRole;
834
+ status: 'joined' | 'upgraded' | 'already_member';
835
+ }
836
+
666
837
  interface RoolObjectStat {
667
838
  path: string;
668
839
  modifiedAt: number;
@@ -0,0 +1,100 @@
1
+ import type { AuthProvider, AuthUser } from './types.js';
2
+ import { type Logger } from './logger.js';
3
+ export interface BaseTokenAuthConfig {
4
+ /** Auth URL (e.g. https://rool.dev/auth). Injected by RoolClient if omitted. */
5
+ authUrl?: string;
6
+ /** Injected by RoolClient if omitted. */
7
+ logger?: Logger;
8
+ /** Injected by RoolClient if omitted, so auth-state reaches client events. */
9
+ onAuthStateChanged?: (authenticated: boolean) => void;
10
+ }
11
+ export declare abstract class BaseTokenAuthProvider implements AuthProvider {
12
+ protected logger: Logger;
13
+ private _authUrl;
14
+ private _onAuthStateChanged;
15
+ private refreshPromise;
16
+ private refreshTimeoutId;
17
+ private boundVisibilityHandler;
18
+ constructor(config?: BaseTokenAuthConfig);
19
+ setAuthUrl(url: string): void;
20
+ setLogger(logger: Logger): void;
21
+ setAuthStateChangedHandler(handler: (authenticated: boolean) => void): void;
22
+ abstract initialize(): boolean;
23
+ abstract login(appName: string, params?: Record<string, string>): Promise<void> | void;
24
+ abstract signup(appName: string, params?: Record<string, string>): Promise<void> | void;
25
+ /**
26
+ * Check if user is currently authenticated.
27
+ *
28
+ * This reports identity (do we hold credentials), NOT server reachability.
29
+ * It deliberately does not perform a network refresh: a backend outage must
30
+ * not read as "logged out". A genuinely invalid/expired refresh token
31
+ * surfaces later as a 401 on first real use, which clears tokens and fires
32
+ * onAuthStateChanged(false) — the only path that ends the session.
33
+ */
34
+ isAuthenticated(): Promise<boolean>;
35
+ /**
36
+ * Get current access token and rool token, refreshing if expired.
37
+ * Returns undefined if not authenticated.
38
+ */
39
+ getTokens(): Promise<{
40
+ accessToken: string;
41
+ roolToken: string;
42
+ } | undefined>;
43
+ /**
44
+ * Get auth identity decoded from JWT token.
45
+ */
46
+ getAuthUser(): AuthUser;
47
+ /**
48
+ * Complete an email verification flow. Exchanges a verify JWT (from the
49
+ * verification email link) for a fresh token set and signs the user in.
50
+ */
51
+ verify(token: string): Promise<boolean>;
52
+ /**
53
+ * Logout - clear all tokens and state.
54
+ */
55
+ logout(): void;
56
+ /**
57
+ * Destroy auth manager - clear refresh timers and listeners.
58
+ */
59
+ destroy(): void;
60
+ /** Auth URL without trailing slash */
61
+ protected get authBaseUrl(): string;
62
+ protected notifyAuthState(authenticated: boolean): void;
63
+ /** Persist a fresh token set and (re)arm the background refresh timer. */
64
+ protected acceptTokens(accessToken: string, refreshToken: string | null, roolToken: string | null, expiresAt: number): void;
65
+ /** Arm auto-refresh + visibility re-scheduling. Call from initialize(). */
66
+ protected initBase(): void;
67
+ /**
68
+ * Overridable hook: clear subclass-owned transient state on logout/cancel
69
+ * (e.g. an in-flight OAuth `state` or PKCE verifier). No-op by default.
70
+ */
71
+ protected clearTransientState(): void;
72
+ protected get storagePrefix(): string;
73
+ /** Build a storage key scoped to this auth endpoint. */
74
+ protected keyFor(name: string): string;
75
+ protected get storageKeys(): {
76
+ readonly access: `${string}access_token`;
77
+ readonly refresh: `${string}refresh_token`;
78
+ readonly rool: `${string}rool_token`;
79
+ readonly expiresAt: `${string}token_expires_at`;
80
+ };
81
+ protected readString(key: string): string | null;
82
+ protected writeString(key: string, value: string): void;
83
+ protected removeString(key: string): void;
84
+ protected scheduleTokenRefresh(): void;
85
+ protected writeTokens(accessToken: string | null, refreshToken: string | null, expiresAt: number | null): void;
86
+ protected writeRoolToken(token: string | null): void;
87
+ protected clearTokens(): void;
88
+ protected readAccessToken(): string | null;
89
+ protected readRoolToken(): string;
90
+ protected readExpiresAt(): number | null;
91
+ /**
92
+ * Get a short hash of the auth URL for scoping storage by endpoint.
93
+ */
94
+ private get endpointHash();
95
+ private tryRefreshToken;
96
+ private listenForVisibility;
97
+ private cancelScheduledRefresh;
98
+ private decodeAuthUser;
99
+ }
100
+ //# sourceMappingURL=auth-base.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth-base.d.ts","sourceRoot":"","sources":["../src/auth-base.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACzD,OAAO,EAAiB,KAAK,MAAM,EAAE,MAAM,aAAa,CAAC;AAKzD,MAAM,WAAW,mBAAmB;IAChC,gFAAgF;IAChF,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,yCAAyC;IACzC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,8EAA8E;IAC9E,kBAAkB,CAAC,EAAE,CAAC,aAAa,EAAE,OAAO,KAAK,IAAI,CAAC;CACzD;AAED,8BAAsB,qBAAsB,YAAW,YAAY;IAC/D,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC;IACzB,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,mBAAmB,CAAmC;IAC9D,OAAO,CAAC,cAAc,CAAiC;IACvD,OAAO,CAAC,gBAAgB,CAA8C;IACtE,OAAO,CAAC,sBAAsB,CAA6B;gBAE/C,MAAM,GAAE,mBAAwB;IAS5C,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAI7B,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAI/B,0BAA0B,CAAC,OAAO,EAAE,CAAC,aAAa,EAAE,OAAO,KAAK,IAAI,GAAG,IAAI;IAK3E,QAAQ,CAAC,UAAU,IAAI,OAAO;IAC9B,QAAQ,CAAC,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI;IACtF,QAAQ,CAAC,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI;IAEvF;;;;;;;;OAQG;IACG,eAAe,IAAI,OAAO,CAAC,OAAO,CAAC;IAIzC;;;OAGG;IACG,SAAS,IAAI,OAAO,CAAC;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,GAAG,SAAS,CAAC;IAiBlF;;OAEG;IACH,WAAW,IAAI,QAAQ;IAMvB;;;OAGG;IACG,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IA4C7C;;OAEG;IACH,MAAM,IAAI,IAAI;IAOd;;OAEG;IACH,OAAO,IAAI,IAAI;IAYf,sCAAsC;IACtC,SAAS,KAAK,WAAW,IAAI,MAAM,CAElC;IAED,SAAS,CAAC,eAAe,CAAC,aAAa,EAAE,OAAO,GAAG,IAAI;IAIvD,0EAA0E;IAC1E,SAAS,CAAC,YAAY,CAClB,WAAW,EAAE,MAAM,EACnB,YAAY,EAAE,MAAM,GAAG,IAAI,EAC3B,SAAS,EAAE,MAAM,GAAG,IAAI,EACxB,SAAS,EAAE,MAAM,GAClB,IAAI;IAMP,2EAA2E;IAC3E,SAAS,CAAC,QAAQ,IAAI,IAAI;IAK1B;;;OAGG;IACH,SAAS,CAAC,mBAAmB,IAAI,IAAI;IAErC,SAAS,KAAK,aAAa,IAAI,MAAM,CAEpC;IAED,wDAAwD;IACxD,SAAS,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;IAItC,SAAS,KAAK,WAAW;;;;;MAQxB;IAED,SAAS,CAAC,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAQhD,SAAS,CAAC,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAQvD,SAAS,CAAC,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAQzC,SAAS,CAAC,oBAAoB,IAAI,IAAI;IAmBtC,SAAS,CAAC,WAAW,CACjB,WAAW,EAAE,MAAM,GAAG,IAAI,EAC1B,YAAY,EAAE,MAAM,GAAG,IAAI,EAC3B,SAAS,EAAE,MAAM,GAAG,IAAI,GACzB,IAAI;IAoBP,SAAS,CAAC,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI;IAQpD,SAAS,CAAC,WAAW,IAAI,IAAI;IAK7B,SAAS,CAAC,eAAe,IAAI,MAAM,GAAG,IAAI;IAI1C,SAAS,CAAC,aAAa,IAAI,MAAM;IAIjC,SAAS,CAAC,aAAa,IAAI,MAAM,GAAG,IAAI;IAWxC;;OAEG;IACH,OAAO,KAAK,YAAY,GAQvB;YAEa,eAAe;IAoE7B,OAAO,CAAC,mBAAmB;IAY3B,OAAO,CAAC,sBAAsB;IAO9B,OAAO,CAAC,cAAc;CAYzB"}