@agoric/smart-wallet 0.5.4-other-dev-8f8782b.0 → 0.5.4-other-dev-fbe72e7.0.fbe72e7
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.
- package/package.json +43 -29
- package/src/index.d.ts +2 -0
- package/src/index.d.ts.map +1 -0
- package/src/index.js +6 -0
- package/src/invitations.d.ts +14 -10
- package/src/invitations.d.ts.map +1 -1
- package/src/invitations.js +39 -33
- package/src/marshal-contexts.d.ts +45 -41
- package/src/marshal-contexts.d.ts.map +1 -1
- package/src/marshal-contexts.js +69 -61
- package/src/offerWatcher.d.ts +54 -0
- package/src/offerWatcher.d.ts.map +1 -0
- package/src/offerWatcher.js +343 -0
- package/src/offers.d.ts +60 -30
- package/src/offers.d.ts.map +1 -1
- package/src/offers.js +38 -182
- package/src/proposals/upgrade-wallet-factory2-proposal.js +61 -0
- package/src/proposals/upgrade-walletFactory-proposal.js +46 -23
- package/src/smartWallet.d.ts +132 -68
- package/src/smartWallet.d.ts.map +1 -1
- package/src/smartWallet.js +718 -217
- package/src/typeGuards.d.ts +1 -1
- package/src/typeGuards.js +29 -1
- package/src/types-index.d.ts +2 -0
- package/src/types-index.js +2 -0
- package/src/types.d.ts +36 -41
- package/src/types.d.ts.map +1 -0
- package/src/types.ts +90 -0
- package/src/utils.d.ts +17 -14
- package/src/utils.d.ts.map +1 -1
- package/src/utils.js +19 -6
- package/src/walletFactory.d.ts +24 -78
- package/src/walletFactory.d.ts.map +1 -1
- package/src/walletFactory.js +64 -37
- package/CHANGELOG.md +0 -180
- package/src/payments.d.ts +0 -20
- package/src/payments.d.ts.map +0 -1
- package/src/payments.js +0 -89
- package/src/proposals/upgrade-walletFactory-proposal.d.ts +0 -17
- package/src/proposals/upgrade-walletFactory-proposal.d.ts.map +0 -1
package/src/marshal-contexts.js
CHANGED
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
// @ts-check
|
|
2
|
+
import { Fail, q } from '@endo/errors';
|
|
3
|
+
import { HandledPromise } from '@endo/eventual-send'; // TODO: convince tsc this isn't needed
|
|
4
|
+
|
|
2
5
|
import { makeScalarMapStore } from '@agoric/store';
|
|
3
6
|
import { Far, makeMarshal, Remotable } from '@endo/marshal';
|
|
4
|
-
import { HandledPromise } from '@endo/eventual-send'; // TODO: convince tsc this isn't needed
|
|
5
7
|
import { DEFAULT_PREFIX } from '@agoric/vats/src/lib-board.js';
|
|
6
8
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
9
|
+
/**
|
|
10
|
+
* @import {PassableCap, RemotableObject} from '@endo/marshal';
|
|
11
|
+
* @import {Key} from '@endo/patterns';
|
|
12
|
+
* @import {MapStore} from '@agoric/swingset-liveslots';
|
|
13
|
+
* @import {BoardId} from '@agoric/vats/src/lib-board.js';
|
|
14
|
+
*/
|
|
10
15
|
|
|
11
16
|
/**
|
|
12
17
|
* ID from a board made with { prefix: DEFAULT_PREFIX }
|
|
@@ -19,11 +24,10 @@ const isDefaultBoardId = specimen => {
|
|
|
19
24
|
};
|
|
20
25
|
|
|
21
26
|
/**
|
|
22
|
-
* When marshaling a purse, payment, etc. we partition the slots
|
|
23
|
-
* using prefixes.
|
|
27
|
+
* When marshaling a purse, payment, etc. we partition the slots using prefixes.
|
|
24
28
|
*
|
|
25
|
-
* @template {Record<string, IdTable
|
|
26
|
-
* @typedef {`${string & keyof T}:${Digits}`} WalletSlot
|
|
29
|
+
* @template {Record<string, IdTable<any, any>>} T
|
|
30
|
+
* @typedef {`${string & keyof T}:${Digits}`} WalletSlot
|
|
27
31
|
*/
|
|
28
32
|
/**
|
|
29
33
|
* @template {string} K
|
|
@@ -31,7 +35,7 @@ const isDefaultBoardId = specimen => {
|
|
|
31
35
|
*/
|
|
32
36
|
|
|
33
37
|
/**
|
|
34
|
-
* @template {Record<string, IdTable
|
|
38
|
+
* @template {Record<string, IdTable<any, any>>} T
|
|
35
39
|
* @param {T} _tables
|
|
36
40
|
* @param {string & keyof T} kind
|
|
37
41
|
* @param {number} id
|
|
@@ -43,10 +47,10 @@ const makeWalletSlot = (_tables, kind, id) => {
|
|
|
43
47
|
};
|
|
44
48
|
|
|
45
49
|
/**
|
|
46
|
-
* @template {Record<string, IdTable
|
|
50
|
+
* @template {Record<string, IdTable<any, any>>} T
|
|
47
51
|
* @param {T} record
|
|
48
52
|
* @param {(value: string, index: number, obj: string[]) => boolean} predicate
|
|
49
|
-
* @returns {string & keyof T | undefined}
|
|
53
|
+
* @returns {(string & keyof T) | undefined}
|
|
50
54
|
*/
|
|
51
55
|
const findKey = (record, predicate) => {
|
|
52
56
|
const key = Object.keys(record).find(predicate);
|
|
@@ -54,10 +58,10 @@ const findKey = (record, predicate) => {
|
|
|
54
58
|
};
|
|
55
59
|
|
|
56
60
|
/**
|
|
57
|
-
* @template {Record<string, IdTable
|
|
61
|
+
* @template {Record<string, IdTable<any, any>>} T
|
|
58
62
|
* @param {T} tables
|
|
59
63
|
* @param {string} slot
|
|
60
|
-
* @returns {{ kind: undefined | string & keyof T
|
|
64
|
+
* @returns {{ kind: undefined | (string & keyof T); id: number }}
|
|
61
65
|
*/
|
|
62
66
|
const parseWalletSlot = (tables, slot) => {
|
|
63
67
|
const kind = findKey(tables, k => slot.startsWith(`${k}:`));
|
|
@@ -66,32 +70,30 @@ const parseWalletSlot = (tables, slot) => {
|
|
|
66
70
|
};
|
|
67
71
|
|
|
68
72
|
/**
|
|
69
|
-
* Since KindSlots always include a colon and BoardIds never do,
|
|
70
|
-
*
|
|
73
|
+
* Since KindSlots always include a colon and BoardIds never do, we an mix them
|
|
74
|
+
* without confusion.
|
|
71
75
|
*
|
|
72
|
-
* @template {Record<string, IdTable
|
|
73
|
-
* @typedef {WalletSlot<T> | BoardId} MixedSlot
|
|
76
|
+
* @template {Record<string, IdTable<any, any>>} T
|
|
77
|
+
* @typedef {WalletSlot<T> | BoardId} MixedSlot
|
|
74
78
|
*/
|
|
75
79
|
/**
|
|
76
|
-
* @typedef {`1` | `12` | `123`} Digits - 1 or more digits.
|
|
77
|
-
*
|
|
78
|
-
* actual usage.
|
|
80
|
+
* @typedef {`1` | `12` | `123`} Digits - 1 or more digits. NOTE: the typescript
|
|
81
|
+
* definition here is more restrictive than actual usage.
|
|
79
82
|
*/
|
|
80
83
|
|
|
81
84
|
/**
|
|
82
|
-
* @template Slot
|
|
83
|
-
* @template Val
|
|
84
|
-
*
|
|
85
|
+
* @template {Key} Slot
|
|
86
|
+
* @template {PassableCap} Val
|
|
85
87
|
* @typedef {{
|
|
86
|
-
* bySlot: MapStore<Slot, Val
|
|
87
|
-
* byVal: MapStore<Val, Slot
|
|
88
|
+
* bySlot: MapStore<Slot, Val>;
|
|
89
|
+
* byVal: MapStore<Val, Slot>;
|
|
88
90
|
* }} IdTable<Value>
|
|
89
91
|
*/
|
|
90
92
|
|
|
91
93
|
/**
|
|
92
|
-
* @template Slot
|
|
93
|
-
* @template Val
|
|
94
|
-
* @param {IdTable<Slot,
|
|
94
|
+
* @template {Key} Slot
|
|
95
|
+
* @template {PassableCap} Val
|
|
96
|
+
* @param {IdTable<Slot, PassableCap>} table
|
|
95
97
|
* @param {Slot} slot
|
|
96
98
|
* @param {Val} val
|
|
97
99
|
*/
|
|
@@ -101,17 +103,20 @@ const initSlotVal = (table, slot, val) => {
|
|
|
101
103
|
};
|
|
102
104
|
|
|
103
105
|
/**
|
|
104
|
-
* Make context for exporting wallet data where brands etc. can be recognized by
|
|
105
|
-
* Export for use outside the smart wallet.
|
|
106
|
+
* Make context for exporting wallet data where brands etc. can be recognized by
|
|
107
|
+
* boardId. Export for use outside the smart wallet.
|
|
106
108
|
*
|
|
107
109
|
* When serializing wallet state for, there's a tension between
|
|
108
110
|
*
|
|
109
|
-
*
|
|
110
|
-
*
|
|
111
|
+
* - keeping purses etc. closely held
|
|
112
|
+
* - recognizing identity of brands also referenced in the state of contracts such
|
|
113
|
+
* as the AMM
|
|
114
|
+
*
|
|
115
|
+
* `makeMarshal()` is parameterized by the type of slots. Here we use a disjoint
|
|
116
|
+
* union of
|
|
111
117
|
*
|
|
112
|
-
*
|
|
113
|
-
*
|
|
114
|
-
* - kind:seq ids for closely held objects; for example purse:123
|
|
118
|
+
* - board ids for widely shared objects
|
|
119
|
+
* - kind:seq ids for closely held objects; for example purse:123
|
|
115
120
|
*/
|
|
116
121
|
export const makeExportContext = () => {
|
|
117
122
|
const walletObjects = {
|
|
@@ -126,27 +131,26 @@ export const makeExportContext = () => {
|
|
|
126
131
|
byVal: makeScalarMapStore(),
|
|
127
132
|
},
|
|
128
133
|
// TODO: offer, contact, dapp
|
|
129
|
-
/** @type {IdTable<number,
|
|
134
|
+
/** @type {IdTable<number, PassableCap>} */
|
|
130
135
|
unknown: {
|
|
131
136
|
bySlot: makeScalarMapStore(),
|
|
132
137
|
byVal: makeScalarMapStore(),
|
|
133
138
|
},
|
|
134
139
|
};
|
|
135
|
-
/** @type {IdTable<BoardId,
|
|
140
|
+
/** @type {IdTable<BoardId, PassableCap>} */
|
|
136
141
|
const boardObjects = {
|
|
137
142
|
bySlot: makeScalarMapStore(),
|
|
138
143
|
byVal: makeScalarMapStore(),
|
|
139
144
|
};
|
|
140
145
|
|
|
141
146
|
/**
|
|
142
|
-
* Look up the slot in mappings from published data
|
|
143
|
-
*
|
|
144
|
-
*
|
|
145
|
-
* @throws if not found (a slotToVal function typically
|
|
146
|
-
* conjures a new identity)
|
|
147
|
+
* Look up the slot in mappings from published data else try walletObjects
|
|
148
|
+
* that we have seen.
|
|
147
149
|
*
|
|
148
150
|
* @param {MixedSlot<typeof walletObjects>} slot
|
|
149
151
|
* @param {string} _iface
|
|
152
|
+
* @throws if not found (a slotToVal function typically conjures a new
|
|
153
|
+
* identity)
|
|
150
154
|
*/
|
|
151
155
|
const slotToVal = (slot, _iface) => {
|
|
152
156
|
if (isDefaultBoardId(slot) && boardObjects.bySlot.has(slot)) {
|
|
@@ -161,7 +165,7 @@ export const makeExportContext = () => {
|
|
|
161
165
|
let unknownNonce = 0;
|
|
162
166
|
|
|
163
167
|
/**
|
|
164
|
-
* @param {
|
|
168
|
+
* @param {PassableCap} val
|
|
165
169
|
* @returns {MixedSlot<typeof walletObjects>}
|
|
166
170
|
*/
|
|
167
171
|
const valToSlot = val => {
|
|
@@ -181,7 +185,7 @@ export const makeExportContext = () => {
|
|
|
181
185
|
};
|
|
182
186
|
|
|
183
187
|
/**
|
|
184
|
-
* @template V
|
|
188
|
+
* @template {PassableCap} V
|
|
185
189
|
* @param {string & keyof typeof walletObjects} kind
|
|
186
190
|
* @param {IdTable<number, V>} table
|
|
187
191
|
*/
|
|
@@ -208,14 +212,14 @@ export const makeExportContext = () => {
|
|
|
208
212
|
purseEntries: walletObjects.purse.bySlot.entries,
|
|
209
213
|
/**
|
|
210
214
|
* @param {BoardId} id
|
|
211
|
-
* @param {
|
|
215
|
+
* @param {RemotableObject} val
|
|
212
216
|
*/
|
|
213
217
|
initBoardId: (id, val) => {
|
|
214
218
|
initSlotVal(boardObjects, id, val);
|
|
215
219
|
},
|
|
216
220
|
/**
|
|
217
221
|
* @param {BoardId} id
|
|
218
|
-
* @param {
|
|
222
|
+
* @param {RemotableObject} val
|
|
219
223
|
*/
|
|
220
224
|
ensureBoardId: (id, val) => {
|
|
221
225
|
if (boardObjects.byVal.has(val)) {
|
|
@@ -235,38 +239,38 @@ const defaultMakePresence = iface => {
|
|
|
235
239
|
};
|
|
236
240
|
|
|
237
241
|
/**
|
|
238
|
-
* Make context for marshalling wallet or board data.
|
|
239
|
-
*
|
|
242
|
+
* Make context for marshalling wallet or board data. To be imported into the
|
|
243
|
+
* client, which never exports objects.
|
|
240
244
|
*
|
|
241
|
-
* @param {(iface: string) =>
|
|
245
|
+
* @param {(iface: string) => PassableCap} [makePresence]
|
|
242
246
|
*/
|
|
243
247
|
export const makeImportContext = (makePresence = defaultMakePresence) => {
|
|
244
248
|
const walletObjects = {
|
|
245
|
-
/** @type {IdTable<number,
|
|
249
|
+
/** @type {IdTable<number, PassableCap>} */
|
|
246
250
|
purse: {
|
|
247
251
|
bySlot: makeScalarMapStore(),
|
|
248
252
|
byVal: makeScalarMapStore(),
|
|
249
253
|
},
|
|
250
|
-
/** @type {IdTable<number,
|
|
254
|
+
/** @type {IdTable<number, PassableCap>} */
|
|
251
255
|
payment: {
|
|
252
256
|
bySlot: makeScalarMapStore(),
|
|
253
257
|
byVal: makeScalarMapStore(),
|
|
254
258
|
},
|
|
255
|
-
/** @type {IdTable<number,
|
|
259
|
+
/** @type {IdTable<number, PassableCap>} */
|
|
256
260
|
unknown: {
|
|
257
261
|
bySlot: makeScalarMapStore(),
|
|
258
262
|
byVal: makeScalarMapStore(),
|
|
259
263
|
},
|
|
260
264
|
};
|
|
261
|
-
/** @type {IdTable<BoardId,
|
|
265
|
+
/** @type {IdTable<BoardId, PassableCap>} */
|
|
262
266
|
const boardObjects = {
|
|
263
267
|
bySlot: makeScalarMapStore(),
|
|
264
268
|
byVal: makeScalarMapStore(),
|
|
265
269
|
};
|
|
266
270
|
|
|
267
271
|
/**
|
|
268
|
-
* @template Slot
|
|
269
|
-
* @template Val
|
|
272
|
+
* @template {Key} Slot
|
|
273
|
+
* @template {PassableCap} Val
|
|
270
274
|
* @param {IdTable<Slot, Val>} table
|
|
271
275
|
* @param {Slot} slot
|
|
272
276
|
* @param {string} iface
|
|
@@ -308,7 +312,7 @@ export const makeImportContext = (makePresence = defaultMakePresence) => {
|
|
|
308
312
|
|
|
309
313
|
const valToSlot = {
|
|
310
314
|
fromBoard: val => boardObjects.byVal.get(val),
|
|
311
|
-
/** @param {
|
|
315
|
+
/** @param {PassableCap} val */
|
|
312
316
|
fromMyWallet: val => {
|
|
313
317
|
const kind = findKey(walletObjects, k => walletObjects[k].byVal.has(val));
|
|
314
318
|
if (kind === undefined) {
|
|
@@ -334,14 +338,14 @@ export const makeImportContext = (makePresence = defaultMakePresence) => {
|
|
|
334
338
|
return harden({
|
|
335
339
|
/**
|
|
336
340
|
* @param {BoardId} id
|
|
337
|
-
* @param {
|
|
341
|
+
* @param {PassableCap} val
|
|
338
342
|
*/
|
|
339
343
|
initBoardId: (id, val) => {
|
|
340
344
|
initSlotVal(boardObjects, id, val);
|
|
341
345
|
},
|
|
342
346
|
/**
|
|
343
347
|
* @param {BoardId} id
|
|
344
|
-
* @param {
|
|
348
|
+
* @param {PassableCap} val
|
|
345
349
|
*/
|
|
346
350
|
ensureBoardId: (id, val) => {
|
|
347
351
|
if (boardObjects.byVal.has(val)) {
|
|
@@ -359,13 +363,17 @@ export const makeImportContext = (makePresence = defaultMakePresence) => {
|
|
|
359
363
|
/**
|
|
360
364
|
* @param {string} iface
|
|
361
365
|
* @param {{
|
|
362
|
-
* applyMethod: (
|
|
363
|
-
*
|
|
366
|
+
* applyMethod: (
|
|
367
|
+
* target: unknown,
|
|
368
|
+
* method: string | symbol,
|
|
369
|
+
* args: unknown[],
|
|
370
|
+
* ) => void;
|
|
371
|
+
* applyFunction: (target: unknown, args: unknown[]) => void;
|
|
364
372
|
* }} handler
|
|
365
373
|
*/
|
|
366
374
|
const makePresence = (iface, handler) => {
|
|
367
375
|
let obj;
|
|
368
|
-
|
|
376
|
+
|
|
369
377
|
void new HandledPromise((resolve, reject, resolveWithPresence) => {
|
|
370
378
|
obj = resolveWithPresence(handler);
|
|
371
379
|
});
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
export function makeWatchOfferOutcomes(vowTools: VowTools): (watchers: OutcomeWatchers, seat: UserSeat) => Promise<any[]>;
|
|
2
|
+
export function prepareOfferWatcher(baggage: Baggage, vowTools: VowTools): (walletHelper: any, deposit: {
|
|
3
|
+
receive: (payment: Payment) => Promise<Amount>;
|
|
4
|
+
}, offerSpec: OfferSpec, address: string, invitationAmount: import("@agoric/ertp").SetAmount<import("@endo/patterns").Key>, seatRef: UserSeat) => import("@endo/exo").GuardedKit<{
|
|
5
|
+
helper: {
|
|
6
|
+
/**
|
|
7
|
+
* @param {Record<string, unknown>} offerStatusUpdates
|
|
8
|
+
*/
|
|
9
|
+
updateStatus(offerStatusUpdates: Record<string, unknown>): void;
|
|
10
|
+
/**
|
|
11
|
+
* @param {string} offerId
|
|
12
|
+
* @param {Amount<'set'>} invitationAmount
|
|
13
|
+
* @param {import('./types.js').InvitationMakers} invitationMakers
|
|
14
|
+
* @param {import('./types.js').PublicSubscribers} publicSubscribers
|
|
15
|
+
*/
|
|
16
|
+
onNewContinuingOffer(offerId: string, invitationAmount: Amount<"set">, invitationMakers: import("./types.js").InvitationMakers, publicSubscribers: import("./types.js").PublicSubscribers): void;
|
|
17
|
+
/** @param {Passable | ContinuingOfferResult} result */
|
|
18
|
+
publishResult(result: Passable | ContinuingOfferResult): void;
|
|
19
|
+
/**
|
|
20
|
+
* Called when the offer result promise rejects. The other two watchers
|
|
21
|
+
* are waiting for particular values out of Zoe but they settle at the
|
|
22
|
+
* same time and don't need their own error handling.
|
|
23
|
+
*
|
|
24
|
+
* @param {Error} err
|
|
25
|
+
*/
|
|
26
|
+
handleError(err: Error): void;
|
|
27
|
+
};
|
|
28
|
+
/** @type {OutcomeWatchers['paymentWatcher']} */
|
|
29
|
+
paymentWatcher: OutcomeWatchers["paymentWatcher"];
|
|
30
|
+
/** @type {OutcomeWatchers['resultWatcher']} */
|
|
31
|
+
resultWatcher: OutcomeWatchers["resultWatcher"];
|
|
32
|
+
/** @type {OutcomeWatchers['numWantsWatcher']} */
|
|
33
|
+
numWantsWatcher: OutcomeWatchers["numWantsWatcher"];
|
|
34
|
+
}>;
|
|
35
|
+
/**
|
|
36
|
+
* <T, [UserSeat]
|
|
37
|
+
*/
|
|
38
|
+
export type OfferPromiseWatcher<T extends unknown> = PromiseWatcher<T, [UserSeat]>;
|
|
39
|
+
export type OutcomeWatchers = {
|
|
40
|
+
resultWatcher: OfferPromiseWatcher<Passable>;
|
|
41
|
+
numWantsWatcher: OfferPromiseWatcher<number>;
|
|
42
|
+
paymentWatcher: OfferPromiseWatcher<PaymentPKeywordRecord>;
|
|
43
|
+
};
|
|
44
|
+
export type MakeOfferWatcher = ReturnType<typeof prepareOfferWatcher>;
|
|
45
|
+
export type OfferWatcher = ReturnType<MakeOfferWatcher>;
|
|
46
|
+
import type { VowTools } from '@agoric/vow';
|
|
47
|
+
import type { UserSeat } from '@agoric/zoe';
|
|
48
|
+
import type { Baggage } from '@agoric/vat-data';
|
|
49
|
+
import type { OfferSpec } from './offers.js';
|
|
50
|
+
import type { Passable } from '@endo/pass-style';
|
|
51
|
+
import type { ContinuingOfferResult } from './types.js';
|
|
52
|
+
import type { PromiseWatcher } from '@agoric/swingset-liveslots';
|
|
53
|
+
import type { PaymentPKeywordRecord } from '@agoric/zoe';
|
|
54
|
+
//# sourceMappingURL=offerWatcher.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"offerWatcher.d.ts","sourceRoot":"","sources":["offerWatcher.js"],"names":[],"mappings":"AAyEO,iDADK,QAAQ,cAKP,eAAe,QACf,QAAQ,oBAiBpB;AAmCM,6CAHI,OAAO,YACP,QAAQ;aAWO,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,MAAM,CAAC;;;QAgBvD;;WAEG;yCADQ,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;QAQlC;;;;;WAKG;sCAJQ,MAAM,oBACN,MAAM,CAAC,KAAK,CAAC,oBACb,OAAO,YAAY,EAAE,gBAAgB,qBACrC,OAAO,YAAY,EAAE,iBAAiB;QAkBjD,uDAAuD;8BAA3C,QAAQ,GAAG,qBAAqB;QAoC5C;;;;;;WAMG;yBADQ,KAAK;;IAclB,gDAAgD;oBAArC,eAAe,CAAC,gBAAgB,CAAC;IA+B5C,+CAA+C;mBAApC,eAAe,CAAC,eAAe,CAAC;IAuC3C,iDAAiD;qBAAtC,eAAe,CAAC,iBAAiB,CAAC;GA2BlD;;;;gCAzTkB,CAAC,oBACP,eAAe,CAAC,EAAE,CAAC,QAAQ,CAAC,CAAC;8BAI7B;IACR,aAAa,EAAE,mBAAmB,CAAC,QAAQ,CAAC,CAAC;IAC7C,eAAe,EAAE,mBAAmB,CAAC,MAAM,CAAC,CAAC;IAC7C,cAAc,EAAE,mBAAmB,CAAC,qBAAqB,CAAC,CAAC;CAC5D;+BAmTU,UAAU,CAAC,OAAO,mBAAmB,CAAC;2BACtC,UAAU,CAAC,gBAAgB,CAAC;8BAlUV,aAAa;8BAC2B,aAAa;6BAF3D,kBAAkB;+BAJhB,aAAa;8BAEd,kBAAkB;2CADL,YAAY;oCAEnB,4BAA4B;2CAGW,aAAa"}
|