@enbox/api 0.2.3 → 0.3.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 (73) hide show
  1. package/README.md +235 -35
  2. package/dist/browser.mjs +13 -13
  3. package/dist/browser.mjs.map +4 -4
  4. package/dist/esm/dwn-api.js +24 -10
  5. package/dist/esm/dwn-api.js.map +1 -1
  6. package/dist/esm/index.js +6 -0
  7. package/dist/esm/index.js.map +1 -1
  8. package/dist/esm/live-query.js +34 -5
  9. package/dist/esm/live-query.js.map +1 -1
  10. package/dist/esm/permission-grant.js +3 -6
  11. package/dist/esm/permission-grant.js.map +1 -1
  12. package/dist/esm/permission-request.js +4 -7
  13. package/dist/esm/permission-request.js.map +1 -1
  14. package/dist/esm/record-data.js +131 -0
  15. package/dist/esm/record-data.js.map +1 -0
  16. package/dist/esm/record-types.js +9 -0
  17. package/dist/esm/record-types.js.map +1 -0
  18. package/dist/esm/record.js +58 -184
  19. package/dist/esm/record.js.map +1 -1
  20. package/dist/esm/repository-types.js +13 -0
  21. package/dist/esm/repository-types.js.map +1 -0
  22. package/dist/esm/repository.js +347 -0
  23. package/dist/esm/repository.js.map +1 -0
  24. package/dist/esm/typed-live-query.js +129 -0
  25. package/dist/esm/typed-live-query.js.map +1 -0
  26. package/dist/esm/typed-record.js +227 -0
  27. package/dist/esm/typed-record.js.map +1 -0
  28. package/dist/esm/typed-web5.js +134 -23
  29. package/dist/esm/typed-web5.js.map +1 -1
  30. package/dist/esm/web5.js +83 -22
  31. package/dist/esm/web5.js.map +1 -1
  32. package/dist/types/dwn-api.d.ts.map +1 -1
  33. package/dist/types/index.d.ts +6 -0
  34. package/dist/types/index.d.ts.map +1 -1
  35. package/dist/types/live-query.d.ts +43 -4
  36. package/dist/types/live-query.d.ts.map +1 -1
  37. package/dist/types/permission-grant.d.ts +1 -1
  38. package/dist/types/permission-grant.d.ts.map +1 -1
  39. package/dist/types/permission-request.d.ts +1 -1
  40. package/dist/types/permission-request.d.ts.map +1 -1
  41. package/dist/types/record-data.d.ts +49 -0
  42. package/dist/types/record-data.d.ts.map +1 -0
  43. package/dist/types/record-types.d.ts +145 -0
  44. package/dist/types/record-types.d.ts.map +1 -0
  45. package/dist/types/record.d.ts +13 -144
  46. package/dist/types/record.d.ts.map +1 -1
  47. package/dist/types/repository-types.d.ts +137 -0
  48. package/dist/types/repository-types.d.ts.map +1 -0
  49. package/dist/types/repository.d.ts +59 -0
  50. package/dist/types/repository.d.ts.map +1 -0
  51. package/dist/types/typed-live-query.d.ts +111 -0
  52. package/dist/types/typed-live-query.d.ts.map +1 -0
  53. package/dist/types/typed-record.d.ts +179 -0
  54. package/dist/types/typed-record.d.ts.map +1 -0
  55. package/dist/types/typed-web5.d.ts +55 -24
  56. package/dist/types/typed-web5.d.ts.map +1 -1
  57. package/dist/types/web5.d.ts +54 -4
  58. package/dist/types/web5.d.ts.map +1 -1
  59. package/package.json +8 -7
  60. package/src/dwn-api.ts +30 -13
  61. package/src/index.ts +6 -0
  62. package/src/live-query.ts +71 -7
  63. package/src/permission-grant.ts +2 -3
  64. package/src/permission-request.ts +3 -4
  65. package/src/record-data.ts +155 -0
  66. package/src/record-types.ts +188 -0
  67. package/src/record.ts +86 -389
  68. package/src/repository-types.ts +249 -0
  69. package/src/repository.ts +391 -0
  70. package/src/typed-live-query.ts +200 -0
  71. package/src/typed-record.ts +309 -0
  72. package/src/typed-web5.ts +202 -49
  73. package/src/web5.ts +162 -27
@@ -0,0 +1,347 @@
1
+ /**
2
+ * Protocol-aware repository factory.
3
+ *
4
+ * `repository()` takes a `TypedWeb5` instance and returns a Proxy-backed
5
+ * object whose shape mirrors the protocol's `structure` tree with
6
+ * ergonomic CRUD methods on each node.
7
+ *
8
+ * - **Collections** (default): `create`, `query`, `get`, `delete`, `subscribe`
9
+ * - **Singletons** (`$recordLimit: { max: 1 }`): `set`, `get`, `delete`
10
+ * - **Nested types**: first argument is `parentContextId`
11
+ * - **`configure()`**: idempotent protocol installation
12
+ *
13
+ * @example
14
+ * ```ts
15
+ * const social = repository(web5.using(SocialGraphProtocol));
16
+ * await social.configure();
17
+ *
18
+ * // Root collection
19
+ * const { record } = await social.friend.create({
20
+ * data: { did: 'did:example:alice' },
21
+ * });
22
+ *
23
+ * // Nested under group
24
+ * const members = await social.group.member.query(groupContextId);
25
+ * ```
26
+ *
27
+ * @module
28
+ */
29
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
30
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
31
+ return new (P || (P = Promise))(function (resolve, reject) {
32
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
33
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
34
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
35
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
36
+ });
37
+ };
38
+ // ---------------------------------------------------------------------------
39
+ // Runtime helpers
40
+ // ---------------------------------------------------------------------------
41
+ /**
42
+ * Checks whether a DWN response status indicates that the record limit
43
+ * has been exceeded. The DWN engine returns a 400 status with a detail
44
+ * message containing "record limit" when `$recordLimit` rejects a write.
45
+ */
46
+ function isRecordLimitExceeded(status) {
47
+ return status.code === 400 && status.detail.includes('record limit');
48
+ }
49
+ /**
50
+ * Checks whether a protocol rule set at a given path is a singleton
51
+ * (has `$recordLimit: { max: 1 }`).
52
+ */
53
+ function isSingletonPath(definition, path) {
54
+ const segments = path.split('/');
55
+ let node = definition.structure;
56
+ for (const seg of segments) {
57
+ if (!node || typeof node !== 'object') {
58
+ return false;
59
+ }
60
+ node = node[seg];
61
+ }
62
+ if (!node || typeof node !== 'object') {
63
+ return false;
64
+ }
65
+ const limit = node['$recordLimit'];
66
+ return limit !== undefined
67
+ && typeof limit === 'object'
68
+ && limit !== null
69
+ && limit['max'] === 1;
70
+ }
71
+ /**
72
+ * Returns the child type keys (non-`$`-prefixed) of a rule set node
73
+ * reached by the given path.
74
+ */
75
+ function getChildKeys(definition, path) {
76
+ const segments = path.split('/');
77
+ let node = definition.structure;
78
+ for (const seg of segments) {
79
+ if (!node || typeof node !== 'object') {
80
+ return [];
81
+ }
82
+ node = node[seg];
83
+ }
84
+ if (!node || typeof node !== 'object') {
85
+ return [];
86
+ }
87
+ return Object.keys(node).filter((k) => !k.startsWith('$'));
88
+ }
89
+ // ---------------------------------------------------------------------------
90
+ // CRUD method builders
91
+ // ---------------------------------------------------------------------------
92
+ /**
93
+ * Build collection CRUD methods for a root-level path.
94
+ */
95
+ function buildRootCollectionMethods(typed, path) {
96
+ return {
97
+ create(options) {
98
+ return __awaiter(this, void 0, void 0, function* () {
99
+ const { status, record } = yield typed.records.create(path, options);
100
+ return { status, record };
101
+ });
102
+ },
103
+ query(options) {
104
+ return __awaiter(this, void 0, void 0, function* () {
105
+ const { status, records, cursor } = yield typed.records.query(path, options);
106
+ return { status, records, cursor };
107
+ });
108
+ },
109
+ get(recordId) {
110
+ return __awaiter(this, void 0, void 0, function* () {
111
+ const { record } = yield typed.records.read(path, {
112
+ filter: { recordId },
113
+ });
114
+ return record;
115
+ });
116
+ },
117
+ delete(recordId) {
118
+ return __awaiter(this, void 0, void 0, function* () {
119
+ return typed.records.delete(path, { recordId });
120
+ });
121
+ },
122
+ subscribe(options) {
123
+ return __awaiter(this, void 0, void 0, function* () {
124
+ const { liveQuery } = yield typed.records.subscribe(path, options);
125
+ return liveQuery;
126
+ });
127
+ },
128
+ };
129
+ }
130
+ /**
131
+ * Build singleton CRUD methods for a root-level path.
132
+ *
133
+ * Uses a create-first strategy: attempts to create a new record, and if the
134
+ * DWN rejects it because the record limit is reached, falls back to querying
135
+ * the existing record and updating it. This avoids the race condition inherent
136
+ * in query-then-create/update.
137
+ */
138
+ function buildRootSingletonMethods(typed, path) {
139
+ return {
140
+ set(options) {
141
+ return __awaiter(this, void 0, void 0, function* () {
142
+ // Attempt to create — if limit not yet reached, this succeeds.
143
+ const createResult = yield typed.records.create(path, options);
144
+ if (isRecordLimitExceeded(createResult.status)) {
145
+ // Record limit hit — query existing and update.
146
+ const { records } = yield typed.records.query(path);
147
+ if (records.length > 0) {
148
+ const { status, record } = yield records[0].update(Object.assign({ data: options.data }, (options.tags !== undefined ? { tags: options.tags } : {})));
149
+ return { status, record };
150
+ }
151
+ }
152
+ return { status: createResult.status, record: createResult.record };
153
+ });
154
+ },
155
+ get() {
156
+ return __awaiter(this, void 0, void 0, function* () {
157
+ const { records } = yield typed.records.query(path);
158
+ return records.length > 0 ? records[0] : undefined;
159
+ });
160
+ },
161
+ delete(recordId) {
162
+ return __awaiter(this, void 0, void 0, function* () {
163
+ return typed.records.delete(path, { recordId });
164
+ });
165
+ },
166
+ };
167
+ }
168
+ /**
169
+ * Build collection CRUD methods for a nested path.
170
+ */
171
+ function buildNestedCollectionMethods(typed, path) {
172
+ return {
173
+ create(parentContextId, options) {
174
+ return __awaiter(this, void 0, void 0, function* () {
175
+ const { status, record } = yield typed.records.create(path, Object.assign(Object.assign({}, options), { parentContextId }));
176
+ return { status, record };
177
+ });
178
+ },
179
+ query(parentContextId, options) {
180
+ return __awaiter(this, void 0, void 0, function* () {
181
+ const { status, records, cursor } = yield typed.records.query(path, Object.assign(Object.assign({}, options), { filter: Object.assign(Object.assign({}, options === null || options === void 0 ? void 0 : options.filter), { contextId: parentContextId }) }));
182
+ return { status, records, cursor };
183
+ });
184
+ },
185
+ get(recordId) {
186
+ return __awaiter(this, void 0, void 0, function* () {
187
+ const { record } = yield typed.records.read(path, {
188
+ filter: { recordId },
189
+ });
190
+ return record;
191
+ });
192
+ },
193
+ delete(recordId) {
194
+ return __awaiter(this, void 0, void 0, function* () {
195
+ return typed.records.delete(path, { recordId });
196
+ });
197
+ },
198
+ subscribe(parentContextId, options) {
199
+ return __awaiter(this, void 0, void 0, function* () {
200
+ const { liveQuery } = yield typed.records.subscribe(path, Object.assign(Object.assign({}, options), { filter: Object.assign(Object.assign({}, options === null || options === void 0 ? void 0 : options.filter), { contextId: parentContextId }) }));
201
+ return liveQuery;
202
+ });
203
+ },
204
+ };
205
+ }
206
+ /**
207
+ * Build singleton CRUD methods for a nested path.
208
+ *
209
+ * Uses the same create-first strategy as root singletons to avoid race
210
+ * conditions between concurrent set() calls.
211
+ */
212
+ function buildNestedSingletonMethods(typed, path) {
213
+ return {
214
+ set(parentContextId, options) {
215
+ return __awaiter(this, void 0, void 0, function* () {
216
+ // Attempt to create under this parent — succeeds if no record exists yet.
217
+ const createResult = yield typed.records.create(path, Object.assign(Object.assign({}, options), { parentContextId }));
218
+ if (isRecordLimitExceeded(createResult.status)) {
219
+ // Record limit hit — query existing under this parent and update.
220
+ const { records } = yield typed.records.query(path, {
221
+ filter: { contextId: parentContextId },
222
+ });
223
+ if (records.length > 0) {
224
+ const { status, record } = yield records[0].update(Object.assign({ data: options.data }, (options.tags !== undefined ? { tags: options.tags } : {})));
225
+ return { status, record };
226
+ }
227
+ }
228
+ return { status: createResult.status, record: createResult.record };
229
+ });
230
+ },
231
+ get(parentContextId) {
232
+ return __awaiter(this, void 0, void 0, function* () {
233
+ const { records } = yield typed.records.query(path, {
234
+ filter: { contextId: parentContextId },
235
+ });
236
+ return records.length > 0 ? records[0] : undefined;
237
+ });
238
+ },
239
+ delete(recordId) {
240
+ return __awaiter(this, void 0, void 0, function* () {
241
+ return typed.records.delete(path, { recordId });
242
+ });
243
+ },
244
+ };
245
+ }
246
+ // ---------------------------------------------------------------------------
247
+ // Node builder
248
+ // ---------------------------------------------------------------------------
249
+ /**
250
+ * Build a repository node for a given path, including CRUD methods
251
+ * and Proxy-based child nodes.
252
+ */
253
+ function buildNode(typed, definition, path, isNested) {
254
+ const singleton = isSingletonPath(definition, path);
255
+ // Build CRUD methods based on root/nested and singleton/collection
256
+ let methods;
257
+ if (isNested) {
258
+ methods = singleton
259
+ ? buildNestedSingletonMethods(typed, path)
260
+ : buildNestedCollectionMethods(typed, path);
261
+ }
262
+ else {
263
+ methods = singleton
264
+ ? buildRootSingletonMethods(typed, path)
265
+ : buildRootCollectionMethods(typed, path);
266
+ }
267
+ // Use a Proxy to lazily build child nodes
268
+ const childKeys = getChildKeys(definition, path);
269
+ const childCache = {};
270
+ return new Proxy(methods, {
271
+ get(target, prop) {
272
+ if (typeof prop !== 'string') {
273
+ return undefined;
274
+ }
275
+ // CRUD methods take priority
276
+ if (prop in target) {
277
+ return target[prop];
278
+ }
279
+ // Lazily build child nodes
280
+ if (childKeys.includes(prop)) {
281
+ if (!(prop in childCache)) {
282
+ childCache[prop] = buildNode(typed, definition, `${path}/${prop}`, true);
283
+ }
284
+ return childCache[prop];
285
+ }
286
+ return undefined;
287
+ },
288
+ });
289
+ }
290
+ // ---------------------------------------------------------------------------
291
+ // Public API
292
+ // ---------------------------------------------------------------------------
293
+ /**
294
+ * Creates a protocol-aware repository from a `TypedWeb5` instance.
295
+ *
296
+ * The returned object provides domain-specific CRUD methods that mirror
297
+ * the protocol's structure tree:
298
+ *
299
+ * - Root types: `repo.friend.create()`, `repo.friend.query()`
300
+ * - Nested types: `repo.group.member.create(groupCtxId, { data })`
301
+ * - Singletons: `repo.profile.set()`, `repo.profile.get()`
302
+ * - Protocol install: `repo.configure()`
303
+ *
304
+ * @param typed - A `TypedWeb5` instance from `web5.using(protocol)`.
305
+ * @returns A typed repository object.
306
+ *
307
+ * @example
308
+ * ```ts
309
+ * const social = repository(web5.using(SocialGraphProtocol));
310
+ * await social.configure();
311
+ *
312
+ * const rec = await social.friend.create({
313
+ * data: { did: 'did:example:alice' },
314
+ * });
315
+ * const friends = await social.friend.query();
316
+ * ```
317
+ */
318
+ export function repository(typed) {
319
+ const definition = typed.definition;
320
+ // Get root-level type keys from the structure
321
+ const rootKeys = Object.keys(definition.structure).filter((k) => !k.startsWith('$'));
322
+ const nodeCache = {};
323
+ const proxy = new Proxy({}, {
324
+ get(_target, prop) {
325
+ if (typeof prop !== 'string') {
326
+ return undefined;
327
+ }
328
+ // configure() method
329
+ if (prop === 'configure') {
330
+ return (options) => __awaiter(this, void 0, void 0, function* () {
331
+ const result = yield typed.configure(options);
332
+ return result;
333
+ });
334
+ }
335
+ // Root-level type nodes
336
+ if (rootKeys.includes(prop)) {
337
+ if (!(prop in nodeCache)) {
338
+ nodeCache[prop] = buildNode(typed, definition, prop, false);
339
+ }
340
+ return nodeCache[prop];
341
+ }
342
+ return undefined;
343
+ },
344
+ });
345
+ return proxy;
346
+ }
347
+ //# sourceMappingURL=repository.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repository.js","sourceRoot":"","sources":["../../src/repository.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,129 @@
1
+ /**
2
+ * A type-safe wrapper around {@link LiveQuery} that carries the data type `T`
3
+ * through initial snapshot records and real-time change events.
4
+ *
5
+ * `TypedLiveQuery<T>` wraps a `LiveQuery` so that:
6
+ * - `.records` returns `TypedRecord<T>[]` instead of `Record[]`.
7
+ * - `.on('create', handler)` provides `TypedRecord<T>` in the callback.
8
+ * - `.on('change', handler)` provides `TypedRecordChange<T>` in the callback.
9
+ * - `.on('disconnected' | 'reconnecting' | 'reconnected' | 'eose', handler)`
10
+ * forwards transport lifecycle events from the underlying `LiveQuery`.
11
+ *
12
+ * @example
13
+ * ```ts
14
+ * const { liveQuery } = await typed.records.subscribe('friend');
15
+ * // liveQuery is TypedLiveQuery<FriendData>
16
+ *
17
+ * for (const record of liveQuery.records) {
18
+ * const data = await record.data.json(); // FriendData
19
+ * }
20
+ *
21
+ * liveQuery.on('create', async (record) => {
22
+ * const data = await record.data.json(); // FriendData
23
+ * });
24
+ *
25
+ * // Connection lifecycle events
26
+ * liveQuery.on('disconnected', () => showOfflineIndicator());
27
+ * liveQuery.on('reconnected', () => hideOfflineIndicator());
28
+ * liveQuery.on('eose', () => console.log('catch-up complete'));
29
+ * ```
30
+ */
31
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
32
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
33
+ return new (P || (P = Promise))(function (resolve, reject) {
34
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
35
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
36
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
37
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
38
+ });
39
+ };
40
+ import { TypedRecord } from './typed-record.js';
41
+ // ---------------------------------------------------------------------------
42
+ // TypedLiveQuery class
43
+ // ---------------------------------------------------------------------------
44
+ /**
45
+ * A type-safe wrapper around {@link LiveQuery} that preserves the data type `T`
46
+ * through the initial snapshot and all subsequent change events.
47
+ *
48
+ * Obtain instances through `TypedWeb5.records.subscribe()` — never construct
49
+ * directly.
50
+ */
51
+ export class TypedLiveQuery {
52
+ constructor(liveQuery) {
53
+ this._liveQuery = liveQuery;
54
+ }
55
+ // -------------------------------------------------------------------------
56
+ // Escape hatch
57
+ // -------------------------------------------------------------------------
58
+ /** Access the underlying untyped {@link LiveQuery} for advanced use cases. */
59
+ get rawLiveQuery() {
60
+ return this._liveQuery;
61
+ }
62
+ // -------------------------------------------------------------------------
63
+ // Typed initial snapshot
64
+ // -------------------------------------------------------------------------
65
+ /** The initial snapshot of matching records, typed as `TypedRecord<T>[]`. */
66
+ get records() {
67
+ if (!this._typedRecords) {
68
+ this._typedRecords = this._liveQuery.records.map((record) => new TypedRecord(record));
69
+ }
70
+ return this._typedRecords;
71
+ }
72
+ /**
73
+ * Pagination cursor for fetching the next page of initial results.
74
+ *
75
+ * `undefined` when there are no more results.
76
+ */
77
+ get cursor() {
78
+ return this._liveQuery.cursor;
79
+ }
80
+ /**
81
+ * Whether the transport connection is currently active.
82
+ * Delegates to the underlying `LiveQuery.isConnected`.
83
+ */
84
+ get isConnected() {
85
+ return this._liveQuery.isConnected;
86
+ }
87
+ on(event, handler) {
88
+ // Lifecycle events: delegate directly to the underlying LiveQuery.
89
+ if (event === 'disconnected') {
90
+ return this._liveQuery.on('disconnected', handler);
91
+ }
92
+ if (event === 'reconnected') {
93
+ return this._liveQuery.on('reconnected', handler);
94
+ }
95
+ if (event === 'eose') {
96
+ return this._liveQuery.on('eose', handler);
97
+ }
98
+ if (event === 'reconnecting') {
99
+ return this._liveQuery.on('reconnecting', handler);
100
+ }
101
+ // Record change events: wrap records in TypedRecord.
102
+ if (event === 'change') {
103
+ return this._liveQuery.on('change', (change) => {
104
+ handler({
105
+ type: change.type,
106
+ record: new TypedRecord(change.record),
107
+ });
108
+ });
109
+ }
110
+ // For create/update/delete, the underlying LiveQuery handler receives a Record.
111
+ // Cast event to 'create' to satisfy the overload resolver — the actual value
112
+ // is always one of 'create' | 'update' | 'delete' at this point.
113
+ return this._liveQuery.on(event, (record) => {
114
+ handler(new TypedRecord(record));
115
+ });
116
+ }
117
+ // -------------------------------------------------------------------------
118
+ // Lifecycle
119
+ // -------------------------------------------------------------------------
120
+ /**
121
+ * Close the underlying subscription and stop dispatching events.
122
+ */
123
+ close() {
124
+ return __awaiter(this, void 0, void 0, function* () {
125
+ return this._liveQuery.close();
126
+ });
127
+ }
128
+ }
129
+ //# sourceMappingURL=typed-live-query.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"typed-live-query.js","sourceRoot":"","sources":["../../src/typed-live-query.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;;;;;;;;;;AAKH,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAoBhD,8EAA8E;AAC9E,uBAAuB;AACvB,8EAA8E;AAE9E;;;;;;GAMG;AACH,MAAM,OAAO,cAAc;IAOzB,YAAY,SAAoB;QAC9B,IAAI,CAAC,UAAU,GAAG,SAAS,CAAC;IAC9B,CAAC;IAED,4EAA4E;IAC5E,eAAe;IACf,4EAA4E;IAE5E,8EAA8E;IAC9E,IAAW,YAAY;QACrB,OAAO,IAAI,CAAC,UAAU,CAAC;IACzB,CAAC;IAED,4EAA4E;IAC5E,yBAAyB;IACzB,4EAA4E;IAE5E,6EAA6E;IAC7E,IAAW,OAAO;QAChB,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,CAAC;YACxB,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,GAAG,CAC9C,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,WAAW,CAAI,MAAM,CAAC,CACvC,CAAC;QACJ,CAAC;QACD,OAAO,IAAI,CAAC,aAAa,CAAC;IAC5B,CAAC;IAED;;;;OAIG;IACH,IAAW,MAAM;QACf,OAAO,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC;IAChC,CAAC;IAED;;;OAGG;IACH,IAAW,WAAW;QACpB,OAAO,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC;IACrC,CAAC;IAoCM,EAAE,CACP,KAA2G,EAC3G,OAGgB;QAEhB,mEAAmE;QACnE,IAAI,KAAK,KAAK,cAAc,EAAE,CAAC;YAC7B,OAAO,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC,cAAc,EAAE,OAAqB,CAAC,CAAC;QACnE,CAAC;QACD,IAAI,KAAK,KAAK,aAAa,EAAE,CAAC;YAC5B,OAAO,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC,aAAa,EAAE,OAAqB,CAAC,CAAC;QAClE,CAAC;QACD,IAAI,KAAK,KAAK,MAAM,EAAE,CAAC;YACrB,OAAO,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE,OAAqB,CAAC,CAAC;QAC3D,CAAC;QACD,IAAI,KAAK,KAAK,cAAc,EAAE,CAAC;YAC7B,OAAO,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC,cAAc,EAAE,OAAgD,CAAC,CAAC;QAC9F,CAAC;QAED,qDAAqD;QACrD,IAAI,KAAK,KAAK,QAAQ,EAAE,CAAC;YACvB,OAAO,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,CAAC,MAAoB,EAAQ,EAAE;gBAChE,OAAkD,CAAC;oBAClD,IAAI,EAAK,MAAM,CAAC,IAAI;oBACpB,MAAM,EAAG,IAAI,WAAW,CAAI,MAAM,CAAC,MAAM,CAAC;iBAC3C,CAAC,CAAC;YACL,CAAC,CAAC,CAAC;QACL,CAAC;QAED,gFAAgF;QAChF,6EAA6E;QAC7E,iEAAiE;QACjE,OAAO,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC,KAAiB,EAAE,CAAC,MAAM,EAAQ,EAAE;YAC3D,OAA4C,CAAC,IAAI,WAAW,CAAI,MAAM,CAAC,CAAC,CAAC;QAC5E,CAAC,CAAC,CAAC;IACL,CAAC;IAED,4EAA4E;IAC5E,YAAY;IACZ,4EAA4E;IAE5E;;OAEG;IACU,KAAK;;YAChB,OAAO,IAAI,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC;QACjC,CAAC;KAAA;CACF"}