@ftschopp/dynatable-core 2.2.0 → 2.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.
- package/CHANGELOG.md +14 -0
- package/README.md +21 -0
- package/dist/builders/update/create-update-builder.d.ts.map +1 -1
- package/dist/builders/update/create-update-builder.js +64 -0
- package/dist/builders/update/types.d.ts +22 -0
- package/dist/builders/update/types.d.ts.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
# @ftschopp/dynatable-core [2.3.0](https://github.com/ftschopp/dynatable/compare/@ftschopp/dynatable-core@2.2.1...@ftschopp/dynatable-core@2.3.0) (2026-06-03)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Features
|
|
5
|
+
|
|
6
|
+
* **core:** add setDefined to UpdateBuilder ([#58](https://github.com/ftschopp/dynatable/issues/58)) ([851c0dc](https://github.com/ftschopp/dynatable/commit/851c0dc8ab6c6eed0555c874c6230d8997530808)), closes [#56](https://github.com/ftschopp/dynatable/issues/56) [#57](https://github.com/ftschopp/dynatable/issues/57)
|
|
7
|
+
|
|
8
|
+
## @ftschopp/dynatable-core [2.2.1](https://github.com/ftschopp/dynatable/compare/@ftschopp/dynatable-core@2.2.0...@ftschopp/dynatable-core@2.2.1) (2026-06-03)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Bug Fixes
|
|
12
|
+
|
|
13
|
+
* **core:** reject undefined in update actions with actionable error ([#56](https://github.com/ftschopp/dynatable/issues/56)) ([5b8bee9](https://github.com/ftschopp/dynatable/commit/5b8bee9e9c041abdbff1b2b97d1eaba16730f257)), closes [#55](https://github.com/ftschopp/dynatable/issues/55)
|
|
14
|
+
|
|
1
15
|
# @ftschopp/dynatable-core [2.2.0](https://github.com/ftschopp/dynatable/compare/@ftschopp/dynatable-core@2.1.0...@ftschopp/dynatable-core@2.2.0) (2026-06-02)
|
|
2
16
|
|
|
3
17
|
|
package/README.md
CHANGED
|
@@ -265,6 +265,27 @@ await table.entities.User.update({ username: 'alice' })
|
|
|
265
265
|
.where((attr, op) => op.gt(attr.followerCount, 0))
|
|
266
266
|
.execute();
|
|
267
267
|
|
|
268
|
+
// Note: `.set()`, `.setIfNotExists()`, `.add()` and `.delete()` reject
|
|
269
|
+
// `undefined` values up front. DynamoDB can't encode `undefined` in
|
|
270
|
+
// `ExpressionAttributeValues`, so the builder throws with the offending
|
|
271
|
+
// keys instead of letting the request fail server-side. Use `.remove(attr)`
|
|
272
|
+
// to clear an attribute, or filter `undefined` out of your payload before
|
|
273
|
+
// calling `.set()`. `null` is allowed — it writes the DynamoDB `NULL` type.
|
|
274
|
+
|
|
275
|
+
// UPSERT via setDefined - sync from an external system where undefined
|
|
276
|
+
// means "this attribute no longer applies". Defined values go to SET,
|
|
277
|
+
// undefined keys go to REMOVE. Internally composes `.set()` + `.remove()`,
|
|
278
|
+
// so all guards (PK template, GSI templates, dedup, GSI key recomputation)
|
|
279
|
+
// apply identically.
|
|
280
|
+
await table.entities.User.update({ username: 'alice' })
|
|
281
|
+
.setDefined({
|
|
282
|
+
name: tamsRecord.name, // SET
|
|
283
|
+
email: tamsRecord.email, // may be undefined → REMOVE
|
|
284
|
+
lastSeenAt: new Date().toISOString(),
|
|
285
|
+
})
|
|
286
|
+
.setIfNotExists('createdAt', new Date().toISOString())
|
|
287
|
+
.execute();
|
|
288
|
+
|
|
268
289
|
// DELETE - Remove item
|
|
269
290
|
await table.entities.User.delete({ username: 'alice' })
|
|
270
291
|
.returning('ALL_OLD')
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"create-update-builder.d.ts","sourceRoot":"","sources":["../../../src/builders/update/create-update-builder.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAE1D,OAAO,EAAgC,SAAS,EAA4B,MAAM,WAAW,CAAC;AAC9F,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACpE,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;
|
|
1
|
+
{"version":3,"file":"create-update-builder.d.ts","sourceRoot":"","sources":["../../../src/builders/update/create-update-builder.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAE1D,OAAO,EAAgC,SAAS,EAA4B,MAAM,WAAW,CAAC;AAC9F,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACpE,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAkE7D;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EACvC,SAAS,EAAE,MAAM,EACjB,GAAG,EAAE,OAAO,CAAC,KAAK,CAAC,EACnB,MAAM,EAAE,cAAc,EACtB,cAAc,GAAE,SAAS,EAAO,EAChC,aAAa,GAAE;IACb,GAAG,EAAE,YAAY,EAAE,CAAC;IACpB,MAAM,EAAE,YAAY,EAAE,CAAC;IACvB,GAAG,EAAE,YAAY,EAAE,CAAC;IACpB,MAAM,EAAE,YAAY,EAAE,CAAC;CACuB,EAChD,UAAU,GAAE,MAAM,GAAG,SAAS,GAAG,SAAS,GAAG,aAAa,GAAG,aAAsB,EACnF,YAAY,SAAI,EAChB,gBAAgB,UAAQ,EACxB,MAAM,CAAC,EAAE,cAAc,EACvB,YAAY,CAAC,EAAE,YAAY,EAC3B,SAAS,GAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAM,EACnC,gBAAgB,CAAC,EAAE,SAAS,GAAG,OAAO,GAAG,MAAM,EAO/C,oBAAoB,GAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAM,GAC7C,aAAa,CAAC,KAAK,CAAC,CA6lBtB"}
|
|
@@ -25,6 +25,37 @@ function actionAttrName(action) {
|
|
|
25
25
|
const values = Object.values(action.names);
|
|
26
26
|
return values[0];
|
|
27
27
|
}
|
|
28
|
+
/**
|
|
29
|
+
* Reject `undefined` before it reaches DynamoDB. SET / ADD / DELETE /
|
|
30
|
+
* setIfNotExists all encode the value into `ExpressionAttributeValues` — an
|
|
31
|
+
* `undefined` there is either silently dropped by the marshaller (when
|
|
32
|
+
* `removeUndefinedValues` is on, which then fails server-side with an unused
|
|
33
|
+
* expression value error) or rejected outright. Catching it at the call site
|
|
34
|
+
* gives a useful error and a pointer to the right primitive.
|
|
35
|
+
*
|
|
36
|
+
* `null` is intentionally allowed — DynamoDB has a NULL attribute type and
|
|
37
|
+
* the caller may legitimately want to write it.
|
|
38
|
+
*/
|
|
39
|
+
function assertNoUndefined(method, payload) {
|
|
40
|
+
const undefinedKeys = 'attr' in payload
|
|
41
|
+
? payload.value === undefined
|
|
42
|
+
? [payload.attr]
|
|
43
|
+
: []
|
|
44
|
+
: Object.entries(payload)
|
|
45
|
+
.filter(([, v]) => v === undefined)
|
|
46
|
+
.map(([k]) => k);
|
|
47
|
+
if (undefinedKeys.length === 0)
|
|
48
|
+
return;
|
|
49
|
+
const guidance = method === 'set'
|
|
50
|
+
? `Use .remove(attr) to clear an attribute, or filter undefined out ` +
|
|
51
|
+
`before calling .set().`
|
|
52
|
+
: method === 'setIfNotExists'
|
|
53
|
+
? `setIfNotExists requires a concrete value; omit the key or filter ` +
|
|
54
|
+
`it out.`
|
|
55
|
+
: `.${method}() requires a concrete value for each attribute.`;
|
|
56
|
+
throw new Error(`.${method}() received undefined for key(s) [${undefinedKeys.join(', ')}]. ` +
|
|
57
|
+
`DynamoDB cannot encode undefined in ExpressionAttributeValues. ${guidance}`);
|
|
58
|
+
}
|
|
28
59
|
/**
|
|
29
60
|
* Creates an UpdateBuilder for an item key and table.
|
|
30
61
|
*
|
|
@@ -72,6 +103,7 @@ setIfNotExistsInputs = {}) {
|
|
|
72
103
|
attrOrUpdates !== null) {
|
|
73
104
|
// Multiple updates case
|
|
74
105
|
const updates = attrOrUpdates;
|
|
106
|
+
assertNoUndefined('set', updates);
|
|
75
107
|
const newActions = [];
|
|
76
108
|
const newSetInputs = { ...setInputs };
|
|
77
109
|
for (const [attr, val] of Object.entries(updates)) {
|
|
@@ -88,6 +120,7 @@ setIfNotExistsInputs = {}) {
|
|
|
88
120
|
}
|
|
89
121
|
// Single update case
|
|
90
122
|
const attrName = normalizeAttr(attrOrUpdates);
|
|
123
|
+
assertNoUndefined('set', { attr: attrName, value });
|
|
91
124
|
const valueName = getUniqueValueName(attrName);
|
|
92
125
|
const action = {
|
|
93
126
|
expression: `#${attrName} = :${valueName}`,
|
|
@@ -103,6 +136,7 @@ setIfNotExistsInputs = {}) {
|
|
|
103
136
|
typeof attrOrUpdates === 'object' &&
|
|
104
137
|
attrOrUpdates !== null) {
|
|
105
138
|
const updates = attrOrUpdates;
|
|
139
|
+
assertNoUndefined('setIfNotExists', updates);
|
|
106
140
|
const newActions = [];
|
|
107
141
|
const newSetIfNotExistsInputs = { ...setIfNotExistsInputs };
|
|
108
142
|
for (const [attr, val] of Object.entries(updates)) {
|
|
@@ -119,6 +153,7 @@ setIfNotExistsInputs = {}) {
|
|
|
119
153
|
}
|
|
120
154
|
// Single update case
|
|
121
155
|
const attrName = normalizeAttr(attrOrUpdates);
|
|
156
|
+
assertNoUndefined('setIfNotExists', { attr: attrName, value });
|
|
122
157
|
const valueName = getUniqueValueName(attrName);
|
|
123
158
|
const action = {
|
|
124
159
|
expression: `#${attrName} = if_not_exists(#${attrName}, :${valueName})`,
|
|
@@ -127,6 +162,33 @@ setIfNotExistsInputs = {}) {
|
|
|
127
162
|
};
|
|
128
163
|
return createUpdateBuilder(tableName, key, client, conditions, { ...updateActions, set: [...updateActions.set, action] }, returnMode, valueCounter, enableTimestamps, logger, indexContext, setInputs, consumedCapacity, { ...setIfNotExistsInputs, [attrName]: value });
|
|
129
164
|
},
|
|
165
|
+
setDefined(updates) {
|
|
166
|
+
// Split the payload, then route through the public `.set()` and
|
|
167
|
+
// `.remove()` paths so all of their guards apply unchanged: PK
|
|
168
|
+
// template immutability, GSI template guard 2 (which will reject
|
|
169
|
+
// any `undefined` targeting a GSI-template field), dedup, and the
|
|
170
|
+
// index-key recomputation triggered by `setInputs`. Composing the
|
|
171
|
+
// builder instead of re-emitting actions keeps the surface area
|
|
172
|
+
// of this method as close to zero as possible.
|
|
173
|
+
const defined = {};
|
|
174
|
+
const toRemove = [];
|
|
175
|
+
for (const [attr, val] of Object.entries(updates)) {
|
|
176
|
+
if (val === undefined) {
|
|
177
|
+
toRemove.push(attr);
|
|
178
|
+
}
|
|
179
|
+
else {
|
|
180
|
+
defined[attr] = val;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
let next = build();
|
|
184
|
+
if (Object.keys(defined).length > 0) {
|
|
185
|
+
next = next.set(defined);
|
|
186
|
+
}
|
|
187
|
+
for (const attr of toRemove) {
|
|
188
|
+
next = next.remove(attr);
|
|
189
|
+
}
|
|
190
|
+
return next;
|
|
191
|
+
},
|
|
130
192
|
remove(attr) {
|
|
131
193
|
const attrName = normalizeAttr(attr);
|
|
132
194
|
const action = {
|
|
@@ -137,6 +199,7 @@ setIfNotExistsInputs = {}) {
|
|
|
137
199
|
},
|
|
138
200
|
add(attr, value) {
|
|
139
201
|
const attrName = normalizeAttr(attr);
|
|
202
|
+
assertNoUndefined('add', { attr: attrName, value });
|
|
140
203
|
const valueName = getUniqueValueName(attrName);
|
|
141
204
|
const action = {
|
|
142
205
|
expression: `#${attrName} :${valueName}`,
|
|
@@ -147,6 +210,7 @@ setIfNotExistsInputs = {}) {
|
|
|
147
210
|
},
|
|
148
211
|
delete(attr, value) {
|
|
149
212
|
const attrName = normalizeAttr(attr);
|
|
213
|
+
assertNoUndefined('delete', { attr: attrName, value });
|
|
150
214
|
const valueName = getUniqueValueName(attrName);
|
|
151
215
|
const action = {
|
|
152
216
|
expression: `#${attrName} :${valueName}`,
|
|
@@ -59,6 +59,28 @@ export interface UpdateBuilder<Model> extends Omit<OperationBuilder<Model>, 'dbP
|
|
|
59
59
|
*/
|
|
60
60
|
setIfNotExists(attr: keyof Model | AttrRef, value: any): UpdateBuilder<Model>;
|
|
61
61
|
setIfNotExists(updates: Partial<Model>): UpdateBuilder<Model>;
|
|
62
|
+
/**
|
|
63
|
+
* Upsert helper: writes attributes whose value is defined with SET, and
|
|
64
|
+
* removes attributes whose value is `undefined`. Equivalent to splitting
|
|
65
|
+
* the payload between `.set()` and `.remove()` at the call site, but
|
|
66
|
+
* makes the intent explicit at the API.
|
|
67
|
+
*
|
|
68
|
+
* Typical use case: syncing from an external system where a missing field
|
|
69
|
+
* in the source payload means "this attribute no longer applies" rather
|
|
70
|
+
* than "leave the existing value alone" — the latter is what `.set()`
|
|
71
|
+
* with `undefined` would imply, and it's a destructive footgun the strict
|
|
72
|
+
* `.set()` rightly rejects.
|
|
73
|
+
*
|
|
74
|
+
* Internally routes through `.set()` and `.remove()`, so every existing
|
|
75
|
+
* guard (primary-key template, secondary-index templates, dedup,
|
|
76
|
+
* GSI-key recomputation) applies identically — including the rejection
|
|
77
|
+
* of `.remove()` on fields used in a GSI template, which will surface as
|
|
78
|
+
* an error here whenever an `undefined` targets such a field.
|
|
79
|
+
*
|
|
80
|
+
* `null` is treated as a defined value and writes the DynamoDB `NULL`
|
|
81
|
+
* type — only `undefined` triggers the REMOVE branch.
|
|
82
|
+
*/
|
|
83
|
+
setDefined(updates: Partial<Model>): UpdateBuilder<Model>;
|
|
62
84
|
/**
|
|
63
85
|
* Removes an attribute from the item
|
|
64
86
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/builders/update/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAChE,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,EAAE,gBAAgB,EAAE,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAEzF;;GAEG;AACH,MAAM,MAAM,YAAY,GAAG;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAC9B,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,MAAM,YAAY,GAAG;IACzB,KAAK,EAAE,eAAe,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAC9B,CAAC;AAEF;;GAEG;AACH,MAAM,WAAW,aAAa,CAAC,KAAK,CAAE,SAAQ,IAAI,CAAC,gBAAgB,CAAC,KAAK,CAAC,EAAE,UAAU,GAAG,OAAO,CAAC;IAC/F;;;;;;;;OAQG;IACH,KAAK,CAAC,EAAE,EAAE,CAAC,IAAI,EAAE,WAAW,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,SAAS,KAAK,SAAS,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;IAExF;;OAEG;IACH,GAAG,CAAC,IAAI,EAAE,MAAM,KAAK,GAAG,OAAO,EAAE,KAAK,EAAE,GAAG,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;IACnE,GAAG,CAAC,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;IAEnD;;;;;;;;;;;;;;;;OAgBG;IACH,cAAc,CAAC,IAAI,EAAE,MAAM,KAAK,GAAG,OAAO,EAAE,KAAK,EAAE,GAAG,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;IAC9E,cAAc,CAAC,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;IAE9D;;OAEG;IACH,MAAM,CAAC,IAAI,EAAE,MAAM,KAAK,GAAG,OAAO,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;IAE1D;;OAEG;IACH,GAAG,CAAC,IAAI,EAAE,MAAM,KAAK,GAAG,OAAO,EAAE,KAAK,EAAE,GAAG,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;IAEnE;;OAEG;IACH,MAAM,CAAC,IAAI,EAAE,MAAM,KAAK,GAAG,OAAO,EAAE,KAAK,EAAE,GAAG,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;IAEtE;;OAEG;IACH,SAAS,CACP,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS,GAAG,aAAa,GAAG,aAAa,GACnE,aAAa,CAAC,KAAK,CAAC,CAAC;IAExB;;;;;;;;OAQG;IACH,sBAAsB,CAAC,IAAI,EAAE,SAAS,GAAG,OAAO,GAAG,MAAM,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;IAEjF;;OAEG;IACH,QAAQ,IAAI,kBAAkB,CAAC;CAChC"}
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/builders/update/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAChE,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,EAAE,gBAAgB,EAAE,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAEzF;;GAEG;AACH,MAAM,MAAM,YAAY,GAAG;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAC9B,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,MAAM,YAAY,GAAG;IACzB,KAAK,EAAE,eAAe,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAC9B,CAAC;AAEF;;GAEG;AACH,MAAM,WAAW,aAAa,CAAC,KAAK,CAAE,SAAQ,IAAI,CAAC,gBAAgB,CAAC,KAAK,CAAC,EAAE,UAAU,GAAG,OAAO,CAAC;IAC/F;;;;;;;;OAQG;IACH,KAAK,CAAC,EAAE,EAAE,CAAC,IAAI,EAAE,WAAW,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,SAAS,KAAK,SAAS,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;IAExF;;OAEG;IACH,GAAG,CAAC,IAAI,EAAE,MAAM,KAAK,GAAG,OAAO,EAAE,KAAK,EAAE,GAAG,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;IACnE,GAAG,CAAC,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;IAEnD;;;;;;;;;;;;;;;;OAgBG;IACH,cAAc,CAAC,IAAI,EAAE,MAAM,KAAK,GAAG,OAAO,EAAE,KAAK,EAAE,GAAG,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;IAC9E,cAAc,CAAC,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;IAE9D;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,UAAU,CAAC,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;IAE1D;;OAEG;IACH,MAAM,CAAC,IAAI,EAAE,MAAM,KAAK,GAAG,OAAO,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;IAE1D;;OAEG;IACH,GAAG,CAAC,IAAI,EAAE,MAAM,KAAK,GAAG,OAAO,EAAE,KAAK,EAAE,GAAG,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;IAEnE;;OAEG;IACH,MAAM,CAAC,IAAI,EAAE,MAAM,KAAK,GAAG,OAAO,EAAE,KAAK,EAAE,GAAG,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;IAEtE;;OAEG;IACH,SAAS,CACP,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS,GAAG,aAAa,GAAG,aAAa,GACnE,aAAa,CAAC,KAAK,CAAC,CAAC;IAExB;;;;;;;;OAQG;IACH,sBAAsB,CAAC,IAAI,EAAE,SAAS,GAAG,OAAO,GAAG,MAAM,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;IAEjF;;OAEG;IACH,QAAQ,IAAI,kBAAkB,CAAC;CAChC"}
|