@livequery/client 2.0.31 → 2.0.105

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 (51) hide show
  1. package/README.md +477 -0
  2. package/dist/LivequeryClient.d.ts +67 -0
  3. package/dist/LivequeryClient.d.ts.map +1 -0
  4. package/dist/LivequeryClient.js +343 -0
  5. package/dist/LivequeryClient.js.map +1 -0
  6. package/dist/LivequeryCollection.d.ts +92 -0
  7. package/dist/LivequeryCollection.d.ts.map +1 -0
  8. package/dist/LivequeryCollection.js +231 -0
  9. package/dist/LivequeryCollection.js.map +1 -0
  10. package/dist/LivequeryDocument.d.ts +23 -0
  11. package/dist/LivequeryDocument.d.ts.map +1 -0
  12. package/dist/LivequeryDocument.js +22 -0
  13. package/dist/LivequeryDocument.js.map +1 -0
  14. package/dist/LivequeryMemoryStorage.d.ts +14 -0
  15. package/dist/LivequeryMemoryStorage.d.ts.map +1 -0
  16. package/dist/LivequeryMemoryStorage.js +89 -0
  17. package/dist/LivequeryMemoryStorage.js.map +1 -0
  18. package/dist/LivequeryStorge.d.ts +12 -0
  19. package/dist/LivequeryStorge.d.ts.map +1 -0
  20. package/dist/LivequeryStorge.js +2 -0
  21. package/dist/LivequeryStorge.js.map +1 -0
  22. package/dist/LivequeryTransporter.d.ts +22 -0
  23. package/dist/LivequeryTransporter.d.ts.map +1 -0
  24. package/dist/LivequeryTransporter.js +2 -0
  25. package/dist/LivequeryTransporter.js.map +1 -0
  26. package/dist/helpers/filterDocs.d.ts +5 -0
  27. package/dist/helpers/filterDocs.d.ts.map +1 -0
  28. package/dist/helpers/filterDocs.js +80 -0
  29. package/dist/helpers/filterDocs.js.map +1 -0
  30. package/dist/helpers/tryCatch.d.ts +2 -0
  31. package/dist/helpers/tryCatch.d.ts.map +1 -0
  32. package/dist/helpers/tryCatch.js +10 -0
  33. package/dist/helpers/tryCatch.js.map +1 -0
  34. package/dist/helpers/whenCompleted.d.ts +3 -0
  35. package/dist/helpers/whenCompleted.d.ts.map +1 -0
  36. package/dist/helpers/whenCompleted.js +5 -0
  37. package/dist/helpers/whenCompleted.js.map +1 -0
  38. package/dist/index.d.ts +9 -0
  39. package/dist/index.d.ts.map +1 -0
  40. package/dist/index.js +9 -0
  41. package/dist/index.js.map +1 -0
  42. package/dist/types.d.ts +70 -0
  43. package/dist/types.d.ts.map +1 -0
  44. package/dist/types.js +2 -0
  45. package/dist/types.js.map +1 -0
  46. package/package.json +95 -18
  47. package/build/Collection.d.ts +0 -52
  48. package/build/Collection.js +0 -366
  49. package/build/index.d.ts +0 -1
  50. package/build/index.js +0 -1
  51. package/build/tsconfig.tsbuildinfo +0 -1
package/README.md ADDED
@@ -0,0 +1,477 @@
1
+ # @livequery/client
2
+
3
+ Reactive local-first data primitives for browser clients.
4
+
5
+ This repository is the client library package, not an application. Changes here should preserve reusable public API behavior unless a task explicitly targets a breaking change.
6
+
7
+ This package provides the core building blocks behind Livequery collections: reactive document state, pluggable local storage, pluggable transporters, optimistic mutations, and typed inline filters.
8
+
9
+ ## AI Agent Guidance
10
+
11
+ Repository-specific agent guidance lives in `AGENTS.md` and `copilot-instructions.md`.
12
+
13
+ - `AGENTS.md` is the implementation-focused guide for coding agents modifying this package.
14
+ - `copilot-instructions.md` provides repo-level instructions for Copilot when generating or reviewing code in this workspace.
15
+ - Both documents assume this repo is a library package, so agent changes should avoid app-specific scaffolding and should preserve public API compatibility by default.
16
+ - Agents generating consumer code should also follow the usage patterns documented below: create a shared `LivequeryClient`, initialize collections before querying, and subscribe to collection state instead of relying on one-time `.value` reads.
17
+
18
+ ## Installation
19
+
20
+ ```bash
21
+ bun add @livequery/client rxjs
22
+ ```
23
+
24
+ For React projects:
25
+
26
+ ```bash
27
+ bun add @livequery/client @livequery/react rxjs
28
+ ```
29
+
30
+ The package is published as ESM and targets browser usage.
31
+
32
+ ## Public Exports
33
+
34
+ The package re-exports:
35
+
36
+ ```ts
37
+ export * from "./LivequeryCollection"
38
+ export * from "./LivequeryClient"
39
+ export * from "./LivequeryMemoryStorage"
40
+ export * from "./LivequeryStorge"
41
+ export * from "./LivequeryTransporter"
42
+ export * from "./types"
43
+ export * from "./helpers/filterDocs"
44
+ export * from "./LivequeryDocument"
45
+ ```
46
+
47
+ ## Core Types
48
+
49
+ ### `Doc`
50
+
51
+ Every record must have an `id`.
52
+
53
+ ```ts
54
+ type Doc<T = {}> = T & {
55
+ id: string
56
+ }
57
+ ```
58
+
59
+ ### `DocState`
60
+
61
+ Collections and documents expose `DocState<T>`, which adds optimistic mutation metadata.
62
+
63
+ ```ts
64
+ type DocState<T extends Doc> = T & {
65
+ _deleting?: boolean
66
+ _deleting_error?: { code: string; message: string; transporter_id: string }
67
+ _updating?: boolean
68
+ _updating_error?: { code: string; message: string; transporter_id: string }
69
+ _adding?: boolean
70
+ _adding_error?: { code: string; message: string; transporter_id: string }
71
+ _remotes?: Record<string, string | number>
72
+ _prev?: Partial<T>
73
+ }
74
+ ```
75
+
76
+ ### `DataChangeEvent`
77
+
78
+ Transporters stream incremental change events back into the core.
79
+
80
+ ```ts
81
+ type DataChangeEvent = {
82
+ collection_ref: string
83
+ id: string
84
+ type: "added" | "removed" | "modified"
85
+ data?: Record<string, any>
86
+ }
87
+ ```
88
+
89
+ ## Architecture
90
+
91
+ ```text
92
+ LivequeryCollection / LivequeryDocument
93
+ |
94
+ v
95
+ LivequeryClient
96
+ / \
97
+ v v
98
+ LivequeryStorge LivequeryTransporter(s)
99
+ ```
100
+
101
+ - `LivequeryCollection` owns the reactive state for one collection ref or one document ref.
102
+ - `LivequeryDocument` wraps an item as a `BehaviorSubject` with convenience mutation methods.
103
+ - `LivequeryClient` coordinates storage, transporters, optimistic writes, and fan-out to watchers.
104
+ - `LivequeryStorge` is the local persistence contract.
105
+ - `LivequeryTransporter` is the remote sync contract.
106
+
107
+ ## Quick Start
108
+
109
+ ```ts
110
+ import {
111
+ LivequeryCollection,
112
+ LivequeryClient,
113
+ LivequeryMemoryStorage,
114
+ type Doc,
115
+ type LivequeryQueryResult,
116
+ type LivequeryTransporter,
117
+ } from "@livequery/client"
118
+ import { of } from "rxjs"
119
+
120
+ type Todo = Doc<{
121
+ title: string
122
+ done: boolean
123
+ createdAt: number
124
+ }>
125
+
126
+ const storage = new LivequeryMemoryStorage()
127
+
128
+ const transporter: LivequeryTransporter = {
129
+ query(_query) {
130
+ return of<Partial<LivequeryQueryResult>>({
131
+ changes: [],
132
+ summary: {},
133
+ paging: { total: 0, current: 0 },
134
+ metadata: {},
135
+ source: "query",
136
+ })
137
+ },
138
+ async add(_ref, doc) {
139
+ return { id: crypto.randomUUID(), ...doc } as Todo
140
+ },
141
+ async update(_ref, id, doc) {
142
+ return { id, ...doc } as Todo
143
+ },
144
+ async delete(_ref, id) {
145
+ return { id } as Todo
146
+ },
147
+ async trigger(_action) {
148
+ return { ok: true }
149
+ },
150
+ }
151
+
152
+ const core = new LivequeryClient({
153
+ storage,
154
+ transporters: {
155
+ primary: transporter,
156
+ },
157
+ })
158
+
159
+ const todos = new LivequeryCollection<Todo>(core, {
160
+ filters: { "createdAt:sort": "desc" },
161
+ mode: "server-first",
162
+ })
163
+
164
+ todos.initialize("todos")
165
+
166
+ todos.items.subscribe((items) => {
167
+ console.log(items.map((doc) => doc.value))
168
+ })
169
+
170
+ await todos.query({ ":limit": 20, "createdAt:sort": "desc" })
171
+ await todos.add({ title: "Buy milk", done: false, createdAt: Date.now() })
172
+ await todos.update("todo-1", { done: true })
173
+ await todos.delete("todo-1")
174
+ ```
175
+
176
+ ## `LivequeryClient`
177
+
178
+ Create one core with a storage adapter and one or more transporters:
179
+
180
+ ```ts
181
+ const core = new LivequeryClient({
182
+ storage,
183
+ transporters: {
184
+ primary: transporter,
185
+ },
186
+ })
187
+ ```
188
+
189
+ ### Mutation flow
190
+
191
+ For `add`, `update`, and `delete`, the core:
192
+
193
+ 1. writes to local storage first
194
+ 2. broadcasts the optimistic change to active watchers
195
+ 3. pushes the mutation to each transporter
196
+ 4. clears optimistic flags or stores mutation errors after the remote call finishes
197
+
198
+ Documents created locally receive ids prefixed with `local:` until a transporter returns a persisted id.
199
+
200
+ ### Query modes
201
+
202
+ Collections support three modes through `LivequeryCollectionOptions.mode`:
203
+
204
+ - `server-first`: queries are driven by transporters, and collection state is built from streamed change events.
205
+ - `cache-first`: first query can hydrate from local storage, then transporters refresh the result.
206
+ - `local-first`: queries resolve from local storage while remote sync runs in the background and rebroadcasts matching changes.
207
+
208
+ Implementation detail: in `local-first` mode, filters are applied by the storage adapter, while the remote query path is triggered with empty filters and matching is re-checked when added events are broadcast locally.
209
+
210
+ ## `LivequeryCollection`
211
+
212
+ `LivequeryCollection<T>` manages one collection or one document ref.
213
+
214
+ ```ts
215
+ type LivequeryCollectionOptions<T extends Doc> = {
216
+ core: LivequeryClient
217
+ filters: Partial<LivequeryFilters<T>>
218
+ lazy: boolean
219
+ debounce: number
220
+ mode: "server-first" | "local-first" | "cache-first"
221
+ }
222
+ ```
223
+
224
+ ### Create and initialize a collection
225
+
226
+ The current constructor takes `core` as the first argument and options as the second argument.
227
+
228
+ ```ts
229
+ const posts = new LivequeryCollection<Post>(core, {
230
+ filters: { "publishedAt:sort": "desc" },
231
+ lazy: false,
232
+ debounce: 250,
233
+ mode: "cache-first",
234
+ })
235
+
236
+ posts.initialize("posts")
237
+ ```
238
+
239
+ `initialize(ref)` subscribes the collection to `LivequeryClient.watch(ref, id, mode)`. In the current implementation, it is browser-only and returns early when `window` is unavailable.
240
+
241
+ ### Collection refs and document refs
242
+
243
+ If a ref has an even number of path segments, the last segment is treated as a document id.
244
+
245
+ ```ts
246
+ posts.initialize("posts")
247
+ singlePost.initialize("posts/post-1")
248
+ ```
249
+
250
+ For collection mutations, `add`, `update`, and `delete` always target the collection portion of the ref.
251
+
252
+ ### Reactive state
253
+
254
+ - `items`: `BehaviorSubject<LivequeryDocument<DocState<T>>[]>`
255
+ - `summary`: `BehaviorSubject<Record<string, any>>`
256
+ - `loading`: `BehaviorSubject<null | "all" | "next" | "prev">`
257
+ - `filters`: `BehaviorSubject<Partial<LivequeryFilters<T>>>`
258
+ - `paging`: `BehaviorSubject<LivequeryPaging>`
259
+ - `error`: `BehaviorSubject<{ code: string; message: string } | null>`
260
+
261
+ `items` is a `BehaviorSubject`, not a plain array. Reading `collection.items.value` gives the current snapshot only. If you need live updates, subscribe.
262
+
263
+ ```ts
264
+ const subscription = posts.items.subscribe((items) => {
265
+ console.log("realtime items", items.map((doc) => doc.value))
266
+ })
267
+
268
+ subscription.unsubscribe()
269
+ ```
270
+
271
+ In React, reading only `collection.items.value` during render will not trigger rerenders when new events arrive. Bridge the `BehaviorSubject` into component state.
272
+
273
+ ```tsx
274
+ function TodoList({ collection }: { collection: LivequeryCollection<Todo> }) {
275
+ const [items, setItems] = useState(() => collection.items.value)
276
+
277
+ useEffect(() => {
278
+ const subscription = collection.items.subscribe(setItems)
279
+ return () => subscription.unsubscribe()
280
+ }, [collection])
281
+
282
+ return (
283
+ <ul>
284
+ {items.map((item) => (
285
+ <li key={item.value.id}>{item.value.title}</li>
286
+ ))}
287
+ </ul>
288
+ )
289
+ }
290
+ ```
291
+
292
+ ### Main methods
293
+
294
+ ```ts
295
+ query(filters: Partial<LivequeryFilters<T>>): Promise<void>
296
+ debounceQuery(filters: Partial<LivequeryFilters<T>>): Promise<void>
297
+ loadMore(): Promise<void>
298
+ loadPrev(): Promise<void>
299
+ loadAround(cursor: string): Promise<void>
300
+ add(payload: Partial<T>): Promise<T>
301
+ update(id: string, payload: Partial<T>): Promise<T | undefined>
302
+ delete(id: string): Promise<void | T | undefined>
303
+ trigger<R>(action: string, payload?: Record<string, any>): Observable<{ data: R; error?: Error }>
304
+ resetError(): void
305
+ watch(check: (prev: T, next: T) => boolean): Observable<[DocState<T>, DocState<T>]>
306
+ ```
307
+
308
+ Notes about current behavior:
309
+
310
+ - `query()` requires `initialize()` to have run first so the collection has a `ref` and watcher registration.
311
+ - `debounceQuery()` only emits through the debounced path when `options.debounce` is truthy.
312
+ - `loadMore()` uses `paging.next.cursor` as `:after`.
313
+ - `loadPrev()` uses `paging.prev.cursor` as `:before`.
314
+ - `loadAround()` currently sets both `:after` and `:before` to the provided cursor.
315
+
316
+ ## `LivequeryDocument`
317
+
318
+ Each entry inside `collection.items` is a `LivequeryDocument`, which extends `BehaviorSubject<DocState<T>>`.
319
+
320
+ ```ts
321
+ class LivequeryDocument<T extends Doc> extends BehaviorSubject<DocState<T>> {
322
+ update(data: Partial<T>): Promise<T | undefined>
323
+ del(): Promise<void | T | undefined>
324
+ trigger<R>(action: string, payload: Record<string, any>): Observable<{ data: R; error?: Error }>
325
+ }
326
+ ```
327
+
328
+ Example:
329
+
330
+ ```ts
331
+ const first = todos.items.value[0]
332
+
333
+ first.subscribe((doc) => {
334
+ console.log(doc.title, doc._updating)
335
+ })
336
+
337
+ await first.update({ done: true })
338
+ await first.del()
339
+ first.trigger("archive", { reason: "completed" }).subscribe()
340
+ ```
341
+
342
+ ## `LivequeryStorge`
343
+
344
+ Local persistence adapters must implement:
345
+
346
+ ```ts
347
+ type LivequeryStorge = {
348
+ query<T extends Doc>(
349
+ collection: string,
350
+ filters?: Record<string, any>
351
+ ): Promise<{
352
+ documents: T[]
353
+ paging: LivequeryPaging
354
+ }>
355
+ get<T extends Doc>(ref: string, id: string): Promise<T | null>
356
+ add<T extends Doc>(collection: string, document: T): Promise<T>
357
+ update<T extends Doc>(collection: string, id: string, document: Record<string, any>): Promise<T | null>
358
+ delete<T extends Doc>(collection: string, id: string): Promise<T | null>
359
+ }
360
+ ```
361
+
362
+ The package ships with `LivequeryMemoryStorage`, an in-memory adapter useful for tests, demos, and ephemeral state.
363
+
364
+ ### `LivequeryMemoryStorage`
365
+
366
+ The built-in adapter:
367
+
368
+ - stores documents in `Map<string, Map<string, Doc>>`
369
+ - generates a local id with `local:${crypto.randomUUID()}` when `id` is missing
370
+ - applies filters through the exported `filterDocs()` helper
371
+ - supports nested sort keys such as `profile.createdAt:sort`
372
+
373
+ ## `LivequeryTransporter`
374
+
375
+ Remote adapters must implement:
376
+
377
+ ```ts
378
+ type LivequeryTransporter = {
379
+ query<T extends Doc>(query: LivequeryQueryParams<T>): Observable<Partial<LivequeryQueryResult>>
380
+ add<T extends Doc>(ref: string, doc: Omit<T, "id">): Promise<T>
381
+ update<T extends Doc>(ref: string, id: string, doc: Partial<T>): Promise<T>
382
+ delete<T extends Doc>(ref: string, id: string): Promise<T>
383
+ trigger<T>(action: LivequeryAction): Promise<T>
384
+ }
385
+ ```
386
+
387
+ ### Query result shape
388
+
389
+ ```ts
390
+ type LivequeryQueryResult = {
391
+ error: { code: string; message: string }
392
+ changes: DataChangeEvent[]
393
+ summary: Record<string, any>
394
+ paging: LivequeryPaging
395
+ metadata: Record<string, any>
396
+ source: "query" | "action" | "realtime"
397
+ loading?: "all" | "next" | "prev" | null
398
+ }
399
+ ```
400
+
401
+ Transporters can emit partial results. In practice, the most useful fields are `changes`, `paging`, `summary`, `metadata`, and `error`.
402
+
403
+ ## Query Filters
404
+
405
+ Filters are flat keys derived from the document type.
406
+
407
+ ### Pagination keys
408
+
409
+ - `:limit`
410
+ - `:before`
411
+ - `:after`
412
+ - `:around`
413
+ - `:page`
414
+
415
+ ### Supported operators
416
+
417
+ - `field:sort` with `"asc" | "desc"` for string and number fields
418
+ - `field:gt`, `field:gte`, `field:lt`, `field:lte` for numeric fields
419
+ - `field:eq-number` for numeric equality
420
+ - `field` for string equality
421
+ - `field:in`, `field:nin` for string or number membership
422
+ - `field:include` for array containment
423
+ - `field:boolean` with `"true" | "false" | "not-true" | "not-false"`
424
+ - `field:like` for case-insensitive substring matching on strings
425
+ - `field:null` with `"null-only" | "not-null"`
426
+
427
+ Nested field paths are supported, for example `"profile.createdAt:sort"`.
428
+
429
+ ```ts
430
+ await todos.query({
431
+ ":limit": 20,
432
+ "done:boolean": "false",
433
+ "title:like": "milk",
434
+ "createdAt:gte": 1714176000000,
435
+ "createdAt:sort": "desc",
436
+ })
437
+ ```
438
+
439
+ ## Helper Exports
440
+
441
+ ### `filterDocs()`
442
+
443
+ ```ts
444
+ import { filterDocs } from "@livequery/client"
445
+
446
+ const visible = filterDocs(docs, {
447
+ "done:boolean": "false",
448
+ "title:like": "milk",
449
+ })
450
+ ```
451
+
452
+ ### `matchesAllFilters()`
453
+
454
+ The helper module also exports `matchesAllFilters(doc, filters)` for direct predicate checks.
455
+
456
+ ## Caveats
457
+
458
+ - `initialize()` is browser-only because it exits early when `window` is unavailable.
459
+ - The public storage interface name is intentionally spelled `LivequeryStorge`, matching the source.
460
+ - Optimistic flags such as `_adding`, `_updating`, `_deleting`, and `_prev` are system-managed fields.
461
+ - Transporter query streams are expected to emit incremental `changes`, not full snapshots.
462
+ - `LivequeryCollection` declares a `metadata` subject but does not initialize it in the constructor, so transporter-emitted `metadata` is not safe to rely on yet.
463
+ - `trigger()` is typed at the collection and document layer as `Observable<{ data, error? }>` but currently forwards raw transporter results from `LivequeryCore.trigger()`.
464
+
465
+ ## Development
466
+
467
+ ```bash
468
+ bun run build
469
+ ```
470
+
471
+ Available scripts:
472
+
473
+ - `bun run clean`
474
+ - `bun run build:js`
475
+ - `bun run build:types`
476
+ - `bun run build`
477
+ - `bun run build:watch`
@@ -0,0 +1,67 @@
1
+ import { Observable, Subject } from "rxjs";
2
+ import type { LivequeryStorge } from "./LivequeryStorge.js";
3
+ import type { LivequeryQueryResult, LivequeryTransporter } from "./LivequeryTransporter.js";
4
+ import type { DataChangeEvent, LivequeryAction, Doc, LivequeryQueryParams, LivequeryFilters, RealtimeChangeSource } from "./types.js";
5
+ export type LivequeryClientOptions = {
6
+ transporters: Record<string, LivequeryTransporter>;
7
+ storage: LivequeryStorge;
8
+ };
9
+ export type LivequeryLoadingState = null | 'next' | 'prev' | 'all';
10
+ export type SyncRequest = DataChangeEvent & {
11
+ ref: string;
12
+ collection_ref: string;
13
+ source: RealtimeChangeSource;
14
+ };
15
+ export type ConflictResolverFunction = <T extends Doc>(e: {
16
+ from: Record<string, string | number | boolean>;
17
+ old_document: T;
18
+ change: DataChangeEvent;
19
+ }) => {
20
+ approved: boolean;
21
+ document: T;
22
+ };
23
+ export type LivequeryClientConfig = {
24
+ storage: LivequeryStorge;
25
+ transporters: Record<string, LivequeryTransporter>;
26
+ };
27
+ export type CollectionMetadata = {
28
+ collection_id: string;
29
+ document_id?: string;
30
+ data$: Subject<Partial<LivequeryQueryResult> & {
31
+ from: RealtimeChangeSource;
32
+ }>;
33
+ collection_ref: string;
34
+ mode: 'server-first' | 'local-first' | 'cache-first';
35
+ filters: Partial<LivequeryFilters<any>>;
36
+ };
37
+ export declare class LivequeryClient {
38
+ #private;
39
+ private readonly config;
40
+ constructor(config: LivequeryClientConfig);
41
+ watch(ref: string, collection_id: string, mode: CollectionMetadata['mode']): Observable<Partial<LivequeryQueryResult> & {
42
+ from: RealtimeChangeSource;
43
+ }>;
44
+ query<T extends Doc>(req: LivequeryQueryParams<T> & {
45
+ collection_id: string;
46
+ }): Promise<{
47
+ documents: T[];
48
+ paging: import("./types.js").LivequeryPaging;
49
+ } | {
50
+ documents: T[];
51
+ } | undefined>;
52
+ add<T extends Doc>(collection_ref: string, doc: Record<string, any>): Promise<{
53
+ [key: string]: T;
54
+ }>;
55
+ update<T extends Doc>(collection_ref: string, id: string, data: Record<string, any>): Promise<{
56
+ [key: string]: {
57
+ id: string;
58
+ } & Partial<T>;
59
+ } | undefined>;
60
+ delete<T extends Doc>(collection_ref: string, id: string): Promise<{
61
+ [key: string]: {
62
+ id: string;
63
+ };
64
+ } | undefined>;
65
+ trigger<Response>(action: LivequeryAction): Observable<Response>;
66
+ }
67
+ //# sourceMappingURL=LivequeryClient.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LivequeryClient.d.ts","sourceRoot":"","sources":["../src/LivequeryClient.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmH,UAAU,EAAiC,OAAO,EAAiD,MAAM,MAAM,CAAA;AACzO,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAA;AAC3D,OAAO,KAAK,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAA;AAC3F,OAAO,KAAK,EAAE,eAAe,EAAE,eAAe,EAAE,GAAG,EAAE,oBAAoB,EAAY,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAA;AAM/I,MAAM,MAAM,sBAAsB,GAAG;IACjC,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAAA;IAClD,OAAO,EAAE,eAAe,CAAA;CAC3B,CAAA;AAED,MAAM,MAAM,qBAAqB,GAAG,IAAI,GAAG,MAAM,GAAG,MAAM,GAAG,KAAK,CAAA;AAKlE,MAAM,MAAM,WAAW,GAAG,eAAe,GAAG;IACxC,GAAG,EAAE,MAAM,CAAC;IACZ,cAAc,EAAE,MAAM,CAAA;IACtB,MAAM,EAAE,oBAAoB,CAAA;CAC/B,CAAA;AAID,MAAM,MAAM,wBAAwB,GAAG,CAAC,CAAC,SAAS,GAAG,EAAE,CAAC,EAAE;IACtD,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,CAAA;IAC/C,YAAY,EAAE,CAAC,CAAA;IACf,MAAM,EAAE,eAAe,CAAA;CAC1B,KAAK;IACF,QAAQ,EAAE,OAAO,CAAA;IACjB,QAAQ,EAAE,CAAC,CAAA;CACd,CAAA;AAGD,MAAM,MAAM,qBAAqB,GAAG;IAChC,OAAO,EAAE,eAAe,CAAA;IACxB,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAAA;CACrD,CAAA;AAED,MAAM,MAAM,kBAAkB,GAAG;IAC7B,aAAa,EAAE,MAAM,CAAA;IACrB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,KAAK,EAAE,OAAO,CAAC,OAAO,CAAC,oBAAoB,CAAC,GAAG;QAC3C,IAAI,EAAE,oBAAoB,CAAA;KAC7B,CAAC,CAAA;IACF,cAAc,EAAE,MAAM,CAAA;IACtB,IAAI,EAAE,cAAc,GAAG,aAAa,GAAG,aAAa,CAAA;IACpD,OAAO,EAAE,OAAO,CAAC,gBAAgB,CAAC,GAAG,CAAC,CAAC,CAAA;CAC1C,CAAA;AAMD,qBAAa,eAAe;;IAOZ,OAAO,CAAC,QAAQ,CAAC,MAAM;gBAAN,MAAM,EAAE,qBAAqB;IA+H1D,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,IAAI,EAAE,kBAAkB,CAAC,MAAM,CAAC;cAjJhE,oBAAoB;;IA4KxB,KAAK,CAAC,CAAC,SAAS,GAAG,EAAE,GAAG,EAAE,oBAAoB,CAAC,CAAC,CAAC,GAAG;QAAE,aAAa,EAAE,MAAM,CAAA;KAAE;;;;;;IAoL7E,GAAG,CAAC,CAAC,SAAS,GAAG,EAAE,cAAc,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;;;IAkBnE,MAAM,CAAC,CAAC,SAAS,GAAG,EAAE,cAAc,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;;gBAyB5C,MAAM;;;IAG7C,MAAM,CAAC,CAAC,SAAS,GAAG,EAAE,cAAc,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM;;gBAsBjB,MAAM;;;IAGnD,OAAO,CAAC,QAAQ,EAAE,MAAM,EAAE,eAAe;CAK5C"}