@ticketlayer/live 0.2.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 (89) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +198 -0
  3. package/dist/auto.d.ts +31 -0
  4. package/dist/auto.d.ts.map +1 -0
  5. package/dist/auto.js +78 -0
  6. package/dist/auto.js.map +1 -0
  7. package/dist/client.d.ts +538 -0
  8. package/dist/client.d.ts.map +1 -0
  9. package/dist/client.js +495 -0
  10. package/dist/client.js.map +1 -0
  11. package/dist/clientHeader.d.ts +4 -0
  12. package/dist/clientHeader.d.ts.map +1 -0
  13. package/dist/clientHeader.js +25 -0
  14. package/dist/clientHeader.js.map +1 -0
  15. package/dist/core/liveClient.d.ts +176 -0
  16. package/dist/core/liveClient.d.ts.map +1 -0
  17. package/dist/core/liveClient.js +372 -0
  18. package/dist/core/liveClient.js.map +1 -0
  19. package/dist/createLiveClient.d.ts +49 -0
  20. package/dist/createLiveClient.d.ts.map +1 -0
  21. package/dist/createLiveClient.js +91 -0
  22. package/dist/createLiveClient.js.map +1 -0
  23. package/dist/elements.d.ts +88 -0
  24. package/dist/elements.d.ts.map +1 -0
  25. package/dist/elements.js +165 -0
  26. package/dist/elements.js.map +1 -0
  27. package/dist/generated/types.d.ts +6110 -0
  28. package/dist/generated/types.d.ts.map +1 -0
  29. package/dist/generated/types.js +6 -0
  30. package/dist/generated/types.js.map +1 -0
  31. package/dist/index.d.ts +35 -0
  32. package/dist/index.d.ts.map +1 -0
  33. package/dist/index.js +18 -0
  34. package/dist/index.js.map +1 -0
  35. package/dist/native/asyncStorageStorage.d.ts +24 -0
  36. package/dist/native/asyncStorageStorage.d.ts.map +1 -0
  37. package/dist/native/asyncStorageStorage.js +24 -0
  38. package/dist/native/asyncStorageStorage.js.map +1 -0
  39. package/dist/native/checkoutUrl.d.ts +85 -0
  40. package/dist/native/checkoutUrl.d.ts.map +1 -0
  41. package/dist/native/checkoutUrl.js +147 -0
  42. package/dist/native/checkoutUrl.js.map +1 -0
  43. package/dist/native/index.d.ts +119 -0
  44. package/dist/native/index.d.ts.map +1 -0
  45. package/dist/native/index.js +145 -0
  46. package/dist/native/index.js.map +1 -0
  47. package/dist/native/secureStoreStorage.d.ts +33 -0
  48. package/dist/native/secureStoreStorage.d.ts.map +1 -0
  49. package/dist/native/secureStoreStorage.js +39 -0
  50. package/dist/native/secureStoreStorage.js.map +1 -0
  51. package/dist/native/storage.d.ts +29 -0
  52. package/dist/native/storage.d.ts.map +1 -0
  53. package/dist/native/storage.js +24 -0
  54. package/dist/native/storage.js.map +1 -0
  55. package/dist/server.d.ts +95 -0
  56. package/dist/server.d.ts.map +1 -0
  57. package/dist/server.js +188 -0
  58. package/dist/server.js.map +1 -0
  59. package/dist/state/buyModal.d.ts +24 -0
  60. package/dist/state/buyModal.d.ts.map +1 -0
  61. package/dist/state/buyModal.js +56 -0
  62. package/dist/state/buyModal.js.map +1 -0
  63. package/dist/state/checkoutModal.d.ts +26 -0
  64. package/dist/state/checkoutModal.d.ts.map +1 -0
  65. package/dist/state/checkoutModal.js +126 -0
  66. package/dist/state/checkoutModal.js.map +1 -0
  67. package/dist/state/emitter.d.ts +21 -0
  68. package/dist/state/emitter.d.ts.map +1 -0
  69. package/dist/state/emitter.js +26 -0
  70. package/dist/state/emitter.js.map +1 -0
  71. package/dist/state/loadElements.d.ts +8 -0
  72. package/dist/state/loadElements.d.ts.map +1 -0
  73. package/dist/state/loadElements.js +39 -0
  74. package/dist/state/loadElements.js.map +1 -0
  75. package/dist/state/registry.d.ts +39 -0
  76. package/dist/state/registry.d.ts.map +1 -0
  77. package/dist/state/registry.js +65 -0
  78. package/dist/state/registry.js.map +1 -0
  79. package/dist/state/theme.d.ts +20 -0
  80. package/dist/state/theme.d.ts.map +1 -0
  81. package/dist/state/theme.js +47 -0
  82. package/dist/state/theme.js.map +1 -0
  83. package/dist/ticketlayer.js +5 -0
  84. package/dist/ticketlayer.js.map +7 -0
  85. package/dist/version.d.ts +7 -0
  86. package/dist/version.d.ts.map +1 -0
  87. package/dist/version.js +7 -0
  88. package/dist/version.js.map +1 -0
  89. package/package.json +80 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Ticketlayer Ltd
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,198 @@
1
+ # @ticketlayer/live
2
+
3
+ The buyer-facing Ticketlayer SDK. It runs in the browser (`@ticketlayer/live`),
4
+ in React Native (`@ticketlayer/live/native`) and, from a separate entry, on
5
+ the server (`@ticketlayer/live/server`).
6
+
7
+ ```bash
8
+ npm install @ticketlayer/live
9
+ ```
10
+
11
+ Not on npm yet: the package is built and versioned here but the first
12
+ release waits on the `NPM_TOKEN` secret (see "Releasing"). Until then our
13
+ apps use a `file:` link to this repository, and third-party pages load the
14
+ CDN build. The vault page `architecture/sdk-architecture.md` lists what is
15
+ on npm.
16
+
17
+ ## Browser
18
+
19
+ ```ts
20
+ import { createLiveClient } from '@ticketlayer/live';
21
+
22
+ const tl = await createLiveClient({
23
+ baseUrl: 'https://live.staging.t9r.dev',
24
+ publishableKey: 'tlpk_...', // the channel's publishable key (embed); omit on a hosted storefront
25
+ checkout: { mode: 'modal', embedBaseUrl: 'https://hbo.staging.t9r.dev' },
26
+ });
27
+ ```
28
+
29
+ The CDN build (`dist/ticketlayer.js`, served at `/v1/ticketlayer.js`) boots
30
+ itself from `data-*` attributes on its script tag; see `src/auto.ts` and
31
+ `live-elements/cdn/README.md`. `CLAUDE.md` is the working agreement.
32
+
33
+ ## React Native
34
+
35
+ `@ticketlayer/live/native` is the same stateful client (events, cart, auth,
36
+ orders, presale, the event bus) built for an app instead of a page: nothing
37
+ defaults from `window.location`, nothing is published on `window`, no theme
38
+ is injected and no iframe modal is mounted. The Live session id travels in
39
+ the `X-Live-Session` header (as the browser embed's does) and is kept in an
40
+ async token storage you inject, so a cart or a magic-link sign-in survives
41
+ an app restart. Card details never enter the app: the hosted checkout opens
42
+ in a WebView.
43
+
44
+ ```ts
45
+ import * as SecureStore from 'expo-secure-store';
46
+ import { createNativeLiveClient, secureStoreStorage } from '@ticketlayer/live/native';
47
+
48
+ const tl = await createNativeLiveClient({
49
+ apiUrl: 'https://live-api.ticketlayer.com', // required: no page to default from
50
+ publishableKey: 'tlpk_...',
51
+ checkoutBaseUrl: 'https://checkout.ticketlayer.com', // the host serving /embed/checkout
52
+ tokenStorage: secureStoreStorage(SecureStore), // or asyncStorageStorage(AsyncStorage)
53
+ });
54
+
55
+ const listings = await tl.events.list();
56
+ await tl.cart.add({ eventOccurrenceId, categoryId, ticketTypeId, quantity: 1 });
57
+ ```
58
+
59
+ Token storage is `{ get, set, remove }` (all async). Two adapters ship with
60
+ the package, each taking the platform module as a parameter so the SDK
61
+ depends on neither: `secureStoreStorage(SecureStore, { key?, options? })`
62
+ for `expo-secure-store` (the keychain: preferred) and
63
+ `asyncStorageStorage(AsyncStorage, { key? })` for
64
+ `@react-native-async-storage/async-storage`. `memoryStorage()` is the
65
+ default and forgets the session on restart. On start the client resumes a
66
+ stored session (`GET /session`); a token the API rejects is removed and the
67
+ client starts lazily, creating a session on the first cart mutation or
68
+ sign-in, as the browser client does.
69
+
70
+ Checkout, following `live-hostedboxoffice/docs/embed-checkout-contract.md`:
71
+
72
+ ```tsx
73
+ import { WebView } from 'react-native-webview';
74
+ import { Linking } from 'react-native';
75
+ import { parseCheckoutMessage } from '@ticketlayer/live/native';
76
+
77
+ const RETURN_URL = 'myapp://tickets/complete'; // on the checkout host's RETURN_URL_ALLOWLIST
78
+
79
+ // 1. The URL hands the session (and so the cart) to the hosted checkout.
80
+ const url = await tl.buildCheckoutUrl({ returnUrl: RETURN_URL });
81
+
82
+ // 2. Success arrives as a tl:complete message ...
83
+ <WebView
84
+ source={{ uri: url }}
85
+ onMessage={(e) => {
86
+ const m = parseCheckoutMessage(e.nativeEvent.data);
87
+ if (m?.type === 'tl:complete') tl.completeCheckout(m); // emits checkout:completed, drops the cart
88
+ if (m?.type === 'tl:close') close();
89
+ }}
90
+ onShouldStartLoadWithRequest={(req) => {
91
+ if (req.url.startsWith(RETURN_URL)) { Linking.openURL(req.url); return false; }
92
+ return req.url.startsWith('https://');
93
+ }}
94
+ />
95
+
96
+ // 3. ... and as the return_url deep link (also on a cold start).
97
+ Linking.addEventListener('url', ({ url }) => {
98
+ const done = tl.parseCheckoutReturn(url, { returnUrl: RETURN_URL });
99
+ if (done) tl.completeCheckout(done); // { orderId, orderReference }
100
+ });
101
+ ```
102
+
103
+ `tl.signOut()` ends the session, removes the stored token and clears the
104
+ local cart; the next mutation mints a fresh session. The pure helpers
105
+ (`buildCheckoutUrl`, `parseCheckoutReturn`, `parseCheckoutMessage`) are
106
+ exported too for hosts that manage the session themselves.
107
+
108
+ A complete illustrative app is in `examples/expo/` (type-checked against the
109
+ built package with `npx tsc -p examples/expo/tsconfig.json`).
110
+
111
+ ## Server-side rendering
112
+
113
+ `@ticketlayer/live/server` is a stateless catalogue client for Node and edge
114
+ runtimes: plain `fetch`, no DOM, no window guards, no Elements. It reads the
115
+ channel's listings, event detail and theme, keeps a small in-memory TTL cache
116
+ (60s by default, `cache: false` to disable), and never pulls the browser client
117
+ into your server bundle. Nothing from it is included in the browser build.
118
+
119
+ ```ts
120
+ import { createServerClient } from '@ticketlayer/live/server';
121
+
122
+ export const live = createServerClient({
123
+ apiUrl: process.env.LIVE_API_INTERNAL_URL!,
124
+ // One of: a channel publishable key, or the storefront domain (resolved
125
+ // once via GET /channel and cached).
126
+ publishableKey: process.env.LIVE_PUBLISHABLE_KEY,
127
+ // domain: 'tickets.example.com',
128
+ cache: { ttlMs: 60_000 }, // or false when the framework caches for you
129
+ });
130
+ ```
131
+
132
+ A Next.js server component:
133
+
134
+ ```tsx
135
+ // app/events/[id]/page.tsx
136
+ import { notFound } from 'next/navigation';
137
+ import { createServerClient, LiveAPIError } from '@ticketlayer/live/server';
138
+
139
+ const live = createServerClient({
140
+ apiUrl: process.env.LIVE_API_INTERNAL_URL!,
141
+ publishableKey: process.env.LIVE_PUBLISHABLE_KEY!,
142
+ // Let Next's fetch cache own revalidation instead of the SDK's TTL cache.
143
+ cache: false,
144
+ });
145
+
146
+ export default async function EventPage({ params }: { params: Promise<{ id: string }> }) {
147
+ const { id } = await params;
148
+ try {
149
+ const [event, theme] = await Promise.all([
150
+ live.events.get(id, { fetchInit: { next: { revalidate: 300, tags: ['events', `event:${id}`] } } }),
151
+ live.theme.get({ fetchInit: { next: { revalidate: 300, tags: ['theme'] } } }),
152
+ ]);
153
+ return (
154
+ <main style={{ fontFamily: theme.typography?.fontFamily }}>
155
+ <h1>{event.name}</h1>
156
+ </main>
157
+ );
158
+ } catch (err) {
159
+ if (err instanceof LiveAPIError && err.status === 404) notFound();
160
+ throw err;
161
+ }
162
+ }
163
+ ```
164
+
165
+ Surface:
166
+
167
+ | Call | Returns |
168
+ |------|---------|
169
+ | `events.list(opts?)` | `Listing[]` - the channel's active listings |
170
+ | `events.get(eventId, opts?)` | `EventDetail` - throws `LiveAPIError` (404) when not listed |
171
+ | `theme.get(opts?)` | `Theme` - throws `LiveAPIError` (404) when none is assigned |
172
+ | `session.create()` | `{ sessionToken, channel, customer }` - a Live session id, e.g. to hand to a WebView checkout |
173
+ | `get(path, opts?)` | Unwrapped JSend `data` for any other Live GET |
174
+ | `clearCache()` | Drops cached reads and the domain-resolved key |
175
+
176
+ `opts.fetchInit` is merged into that request (headers merge, everything else
177
+ overrides), so framework-specific fetch options pass straight through.
178
+ `opts.noCache` bypasses the in-memory cache for one call.
179
+
180
+ ## Scripts
181
+
182
+ ```bash
183
+ npm run build # tsc -> dist/ (index.js, server.js, native/, types)
184
+ npm run build:cdn # + esbuild IIFE -> dist/ticketlayer.js
185
+ npm test # builds, then node --test test/*.test.mjs
186
+ npm run docs:gen # typedoc -> docs-output/
187
+ ```
188
+
189
+ ## Releasing
190
+
191
+ Versioning and publishing are driven by [changesets](.changeset/README.md):
192
+ add a changeset with your change, and on merge to `main` the release workflow
193
+ keeps a "Release @ticketlayer/live" pull request up to date; merging that
194
+ publishes to npm with provenance. `src/client.ts` and `src/generated/` are
195
+ generated by `backstage-sdk-tooling` (regeneration PRs arrive automatically
196
+ when backstage-api publishes a contract) and are shipped exactly as committed.
197
+ The `ticketlayer` block in `package.json` records the spec version, dated API
198
+ version and generator each release was built from.
package/dist/auto.d.ts ADDED
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Drop-in auto-init for the CDN/UMD build (`dist/ticketlayer.js`).
3
+ *
4
+ * This is the "one line of code" embed - what a Webflow/Framer/any site drops in:
5
+ *
6
+ * <script src="https://cdn.ticketlayer.com/v1/ticketlayer.js"
7
+ * data-publishable-key="tlpk_..."
8
+ * data-api-url="https://live-api.ticketlayer.com"
9
+ * data-checkout-base="https://checkout.ticketlayer.com"></script>
10
+ *
11
+ * On load it boots a Live client from the script tag's data-* attributes:
12
+ * publishes `window.Ticketlayer` (so `@ticketlayer/elements` bind), injects the
13
+ * Elements bundle, and installs the hosted iframe checkout modal. Plain HTML
14
+ * custom-element tags (`<tl-event-list>`, `<tl-buy-tickets-button event-id>`)
15
+ * then just work - no bundler, no framework.
16
+ *
17
+ * The IIFE also exposes the SDK on `window.TicketlayerLive` (createLiveClient,
18
+ * boot) for sites that want manual control instead of the data-attribute path.
19
+ *
20
+ * NOTE: index.ts (the npm/bundler entry) does NOT import this module, so only
21
+ * the CDN build auto-boots. Booting is idempotent and a no-op without a key.
22
+ */
23
+ import { createLiveClient, TicketlayerLive } from './createLiveClient.js';
24
+ /**
25
+ * Boot a Live client from the script tag's data-* attributes. Returns the
26
+ * client (or null when there's no publishable key / not in a browser). Safe to
27
+ * call once; subsequent calls no-op.
28
+ */
29
+ export declare function boot(): Promise<TicketlayerLive | null>;
30
+ export { createLiveClient, TicketlayerLive };
31
+ //# sourceMappingURL=auto.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auto.d.ts","sourceRoot":"","sources":["../src/auto.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAkC1E;;;;GAIG;AACH,wBAAgB,IAAI,IAAI,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CA0BtD;AAGD,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,CAAC"}
package/dist/auto.js ADDED
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Drop-in auto-init for the CDN/UMD build (`dist/ticketlayer.js`).
3
+ *
4
+ * This is the "one line of code" embed - what a Webflow/Framer/any site drops in:
5
+ *
6
+ * <script src="https://cdn.ticketlayer.com/v1/ticketlayer.js"
7
+ * data-publishable-key="tlpk_..."
8
+ * data-api-url="https://live-api.ticketlayer.com"
9
+ * data-checkout-base="https://checkout.ticketlayer.com"></script>
10
+ *
11
+ * On load it boots a Live client from the script tag's data-* attributes:
12
+ * publishes `window.Ticketlayer` (so `@ticketlayer/elements` bind), injects the
13
+ * Elements bundle, and installs the hosted iframe checkout modal. Plain HTML
14
+ * custom-element tags (`<tl-event-list>`, `<tl-buy-tickets-button event-id>`)
15
+ * then just work - no bundler, no framework.
16
+ *
17
+ * The IIFE also exposes the SDK on `window.TicketlayerLive` (createLiveClient,
18
+ * boot) for sites that want manual control instead of the data-attribute path.
19
+ *
20
+ * NOTE: index.ts (the npm/bundler entry) does NOT import this module, so only
21
+ * the CDN build auto-boots. Booting is idempotent and a no-op without a key.
22
+ */
23
+ import { createLiveClient, TicketlayerLive } from './createLiveClient.js';
24
+ /** Read config off the embedding <script> tag's data-* attributes. */
25
+ function readConfig() {
26
+ if (typeof document === 'undefined')
27
+ return null;
28
+ const el = document.currentScript ??
29
+ document.querySelector('script[data-publishable-key]');
30
+ if (!el)
31
+ return null;
32
+ const d = el.dataset;
33
+ return {
34
+ publishableKey: d.publishableKey,
35
+ apiUrl: d.apiUrl,
36
+ elementsUrl: d.elementsUrl,
37
+ checkoutBase: d.checkoutBase,
38
+ // Inject @ticketlayer/elements unless explicitly opted out (data-elements="false").
39
+ autoElements: d.elements !== 'false',
40
+ };
41
+ }
42
+ /**
43
+ * Boot a Live client from the script tag's data-* attributes. Returns the
44
+ * client (or null when there's no publishable key / not in a browser). Safe to
45
+ * call once; subsequent calls no-op.
46
+ */
47
+ export function boot() {
48
+ if (typeof window === 'undefined')
49
+ return Promise.resolve(null);
50
+ const w = window;
51
+ if (w.__tlBooted)
52
+ return w.ticketlayer?.ready ?? Promise.resolve(null);
53
+ const cfg = readConfig();
54
+ if (!cfg?.publishableKey)
55
+ return Promise.resolve(null); // manual usage - nothing to boot
56
+ if (!cfg.apiUrl) {
57
+ console.error('[ticketlayer] Missing data-api-url on the embed script; cannot auto-init.');
58
+ return Promise.resolve(null);
59
+ }
60
+ w.__tlBooted = true;
61
+ const ready = createLiveClient({
62
+ baseUrl: cfg.apiUrl,
63
+ publishableKey: cfg.publishableKey,
64
+ elements: cfg.autoElements,
65
+ ...(cfg.elementsUrl ? { elementsUrl: cfg.elementsUrl } : {}),
66
+ checkout: { mode: 'modal', ...(cfg.checkoutBase ? { embedBaseUrl: cfg.checkoutBase } : {}) },
67
+ }).catch((err) => {
68
+ console.error('[ticketlayer] Failed to initialise:', err);
69
+ return null;
70
+ });
71
+ w.ticketlayer = { ready };
72
+ return ready;
73
+ }
74
+ // Manual-use surface on the global (the IIFE's globalName is TicketlayerLive).
75
+ export { createLiveClient, TicketlayerLive };
76
+ // Auto-boot on load.
77
+ void boot();
78
+ //# sourceMappingURL=auto.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auto.js","sourceRoot":"","sources":["../src/auto.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAgB1E,sEAAsE;AACtE,SAAS,UAAU;IACjB,IAAI,OAAO,QAAQ,KAAK,WAAW;QAAE,OAAO,IAAI,CAAC;IACjD,MAAM,EAAE,GACL,QAAQ,CAAC,aAA0C;QACpD,QAAQ,CAAC,aAAa,CAAoB,8BAA8B,CAAC,CAAC;IAC5E,IAAI,CAAC,EAAE;QAAE,OAAO,IAAI,CAAC;IACrB,MAAM,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC;IACrB,OAAO;QACL,cAAc,EAAE,CAAC,CAAC,cAAc;QAChC,MAAM,EAAE,CAAC,CAAC,MAAM;QAChB,WAAW,EAAE,CAAC,CAAC,WAAW;QAC1B,YAAY,EAAE,CAAC,CAAC,YAAY;QAC5B,oFAAoF;QACpF,YAAY,EAAE,CAAC,CAAC,QAAQ,KAAK,OAAO;KACrC,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,IAAI;IAClB,IAAI,OAAO,MAAM,KAAK,WAAW;QAAE,OAAO,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAChE,MAAM,CAAC,GAAG,MAA+B,CAAC;IAC1C,IAAI,CAAC,CAAC,UAAU;QAAE,OAAO,CAAC,CAAC,WAAW,EAAE,KAAK,IAAI,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAEvE,MAAM,GAAG,GAAG,UAAU,EAAE,CAAC;IACzB,IAAI,CAAC,GAAG,EAAE,cAAc;QAAE,OAAO,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,iCAAiC;IACzF,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC;QAChB,OAAO,CAAC,KAAK,CAAC,2EAA2E,CAAC,CAAC;QAC3F,OAAO,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/B,CAAC;IAED,CAAC,CAAC,UAAU,GAAG,IAAI,CAAC;IACpB,MAAM,KAAK,GAAG,gBAAgB,CAAC;QAC7B,OAAO,EAAE,GAAG,CAAC,MAAM;QACnB,cAAc,EAAE,GAAG,CAAC,cAAc;QAClC,QAAQ,EAAE,GAAG,CAAC,YAAY;QAC1B,GAAG,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,GAAG,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC5D,QAAQ,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,GAAG,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE;KAC7F,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;QACf,OAAO,CAAC,KAAK,CAAC,qCAAqC,EAAE,GAAG,CAAC,CAAC;QAC1D,OAAO,IAAI,CAAC;IACd,CAAC,CAAC,CAAC;IAEH,CAAC,CAAC,WAAW,GAAG,EAAE,KAAK,EAAE,CAAC;IAC1B,OAAO,KAAK,CAAC;AACf,CAAC;AAED,+EAA+E;AAC/E,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,CAAC;AAE7C,qBAAqB;AACrB,KAAK,IAAI,EAAE,CAAC"}