@forge/kvs 1.2.7-next.0-experimental-3fd1b86 → 1.3.0-next.1-experimental-fd4d1a2
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/out/__test__/index.test.js +432 -18
- package/out/entity-query.d.ts +3 -2
- package/out/entity-query.d.ts.map +1 -1
- package/out/entity-query.js +10 -3
- package/out/entity.d.ts +6 -4
- package/out/entity.d.ts.map +1 -1
- package/out/entity.js +5 -4
- package/out/interfaces/entity-query.d.ts +2 -1
- package/out/interfaces/entity-query.d.ts.map +1 -1
- package/out/interfaces/kvs-api.d.ts +19 -10
- package/out/interfaces/kvs-api.d.ts.map +1 -1
- package/out/interfaces/kvs.d.ts +13 -6
- package/out/interfaces/kvs.d.ts.map +1 -1
- package/out/interfaces/query.d.ts +2 -1
- package/out/interfaces/query.d.ts.map +1 -1
- package/out/interfaces/transaction.d.ts +3 -1
- package/out/interfaces/transaction.d.ts.map +1 -1
- package/out/interfaces/types.d.ts +32 -1
- package/out/interfaces/types.d.ts.map +1 -1
- package/out/interfaces/types.js +6 -1
- package/out/kvs.d.ts +10 -5
- package/out/kvs.d.ts.map +1 -1
- package/out/kvs.js +6 -6
- package/out/query.d.ts.map +1 -1
- package/out/query.js +5 -1
- package/out/storage-api.d.ts +5 -4
- package/out/storage-api.d.ts.map +1 -1
- package/out/storage-api.js +52 -24
- package/out/transaction-api.d.ts +2 -1
- package/out/transaction-api.d.ts.map +1 -1
- package/out/transaction-api.js +3 -2
- package/out/utils/transaction-request-builder.d.ts.map +1 -1
- package/out/utils/transaction-request-builder.js +3 -2
- package/package.json +2 -2
|
@@ -30,23 +30,62 @@ describe('KVS', () => {
|
|
|
30
30
|
body: JSON.stringify({ key: 'foo' })
|
|
31
31
|
}));
|
|
32
32
|
});
|
|
33
|
-
it('should get with metadata fields correctly', async () => {
|
|
34
|
-
const response = new Response(JSON.stringify({
|
|
33
|
+
it('should get with metadata fields correctly for get', async () => {
|
|
34
|
+
const response = new Response(JSON.stringify({
|
|
35
|
+
key: 'foo',
|
|
36
|
+
value: 'bar',
|
|
37
|
+
createdAt: 1718236800,
|
|
38
|
+
updatedAt: 1718236800,
|
|
39
|
+
expireTime: '2025-01-12T13:00:00Z'
|
|
40
|
+
}), {
|
|
35
41
|
status: 200,
|
|
36
42
|
headers: { 'x-trace-id': traceId }
|
|
37
43
|
});
|
|
38
44
|
const { sut, apiClient } = prepare(response);
|
|
39
|
-
const rs = await sut.get('foo', {
|
|
45
|
+
const rs = await sut.get('foo', {
|
|
46
|
+
metadataFields: [types_1.MetadataField.CREATED_AT, types_1.MetadataField.UPDATED_AT, types_1.MetadataField.EXPIRE_TIME]
|
|
47
|
+
});
|
|
40
48
|
expect(rs).toEqual({
|
|
41
49
|
key: 'foo',
|
|
42
50
|
value: 'bar',
|
|
43
51
|
createdAt: 1718236800,
|
|
44
|
-
updatedAt: 1718236800
|
|
52
|
+
updatedAt: 1718236800,
|
|
53
|
+
expireTime: '2025-01-12T13:00:00Z'
|
|
45
54
|
});
|
|
46
55
|
expect(apiClient).toHaveBeenCalledWith('/api/v1/get', expect.objectContaining({
|
|
47
56
|
body: JSON.stringify({
|
|
48
57
|
key: 'foo',
|
|
49
|
-
options: { metadataFields: [types_1.MetadataField.CREATED_AT, types_1.MetadataField.UPDATED_AT] }
|
|
58
|
+
options: { metadataFields: [types_1.MetadataField.CREATED_AT, types_1.MetadataField.UPDATED_AT, types_1.MetadataField.EXPIRE_TIME] }
|
|
59
|
+
})
|
|
60
|
+
}));
|
|
61
|
+
});
|
|
62
|
+
it('should get with metadata fields correctly for entity.get', async () => {
|
|
63
|
+
const response = new Response(JSON.stringify({
|
|
64
|
+
key: 'key-foo',
|
|
65
|
+
value: { foo: 'bar' },
|
|
66
|
+
createdAt: 1718236800,
|
|
67
|
+
updatedAt: 1718236800,
|
|
68
|
+
expireTime: '2025-01-12T13:00:00Z'
|
|
69
|
+
}), {
|
|
70
|
+
status: 200,
|
|
71
|
+
headers: { 'x-trace-id': traceId }
|
|
72
|
+
});
|
|
73
|
+
const { sut, apiClient } = prepare(response);
|
|
74
|
+
const rs = await sut.entity('foo').get('key-foo', {
|
|
75
|
+
metadataFields: [types_1.MetadataField.CREATED_AT, types_1.MetadataField.UPDATED_AT, types_1.MetadataField.EXPIRE_TIME]
|
|
76
|
+
});
|
|
77
|
+
expect(rs).toEqual({
|
|
78
|
+
key: 'key-foo',
|
|
79
|
+
value: { foo: 'bar' },
|
|
80
|
+
createdAt: 1718236800,
|
|
81
|
+
updatedAt: 1718236800,
|
|
82
|
+
expireTime: '2025-01-12T13:00:00Z'
|
|
83
|
+
});
|
|
84
|
+
expect(apiClient).toHaveBeenCalledWith('/api/v1/entity/get', expect.objectContaining({
|
|
85
|
+
body: JSON.stringify({
|
|
86
|
+
entityName: 'foo',
|
|
87
|
+
key: 'key-foo',
|
|
88
|
+
options: { metadataFields: [types_1.MetadataField.CREATED_AT, types_1.MetadataField.UPDATED_AT, types_1.MetadataField.EXPIRE_TIME] }
|
|
50
89
|
})
|
|
51
90
|
}));
|
|
52
91
|
});
|
|
@@ -104,22 +143,31 @@ describe('KVS', () => {
|
|
|
104
143
|
}));
|
|
105
144
|
});
|
|
106
145
|
it('should getSecret with metadata fields correctly', async () => {
|
|
107
|
-
const response = new Response(JSON.stringify({
|
|
146
|
+
const response = new Response(JSON.stringify({
|
|
147
|
+
key: 'foo',
|
|
148
|
+
value: 'bar',
|
|
149
|
+
createdAt: 1718236800,
|
|
150
|
+
updatedAt: 1718236800,
|
|
151
|
+
expireTime: '2025-01-12T13:00:00Z'
|
|
152
|
+
}), {
|
|
108
153
|
status: 200,
|
|
109
154
|
headers: { 'x-trace-id': traceId }
|
|
110
155
|
});
|
|
111
156
|
const { sut, apiClient } = prepare(response);
|
|
112
|
-
const rs = await sut.getSecret('foo', {
|
|
157
|
+
const rs = await sut.getSecret('foo', {
|
|
158
|
+
metadataFields: [types_1.MetadataField.CREATED_AT, types_1.MetadataField.UPDATED_AT, types_1.MetadataField.EXPIRE_TIME]
|
|
159
|
+
});
|
|
113
160
|
expect(rs).toEqual({
|
|
114
161
|
key: 'foo',
|
|
115
162
|
value: 'bar',
|
|
116
163
|
createdAt: 1718236800,
|
|
117
|
-
updatedAt: 1718236800
|
|
164
|
+
updatedAt: 1718236800,
|
|
165
|
+
expireTime: '2025-01-12T13:00:00Z'
|
|
118
166
|
});
|
|
119
167
|
expect(apiClient).toHaveBeenCalledWith('/api/v1/secret/get', expect.objectContaining({
|
|
120
168
|
body: JSON.stringify({
|
|
121
169
|
key: 'foo',
|
|
122
|
-
options: { metadataFields: [types_1.MetadataField.CREATED_AT, types_1.MetadataField.UPDATED_AT] }
|
|
170
|
+
options: { metadataFields: [types_1.MetadataField.CREATED_AT, types_1.MetadataField.UPDATED_AT, types_1.MetadataField.EXPIRE_TIME] }
|
|
123
171
|
})
|
|
124
172
|
}));
|
|
125
173
|
});
|
|
@@ -148,25 +196,32 @@ describe('KVS', () => {
|
|
|
148
196
|
}));
|
|
149
197
|
});
|
|
150
198
|
it('should getEntity with metadata fields correctly', async () => {
|
|
151
|
-
const response = new Response(JSON.stringify({
|
|
199
|
+
const response = new Response(JSON.stringify({
|
|
200
|
+
key: 'foo',
|
|
201
|
+
value: { name: 'Jane Doe' },
|
|
202
|
+
createdAt: 1718236800,
|
|
203
|
+
updatedAt: 1718236800,
|
|
204
|
+
expireTime: '2025-01-12T13:00:00Z'
|
|
205
|
+
}), {
|
|
152
206
|
status: 200,
|
|
153
207
|
headers: { 'x-trace-id': traceId }
|
|
154
208
|
});
|
|
155
209
|
const { sut, apiClient } = prepare(response);
|
|
156
210
|
const rs = await sut
|
|
157
211
|
.entity('employees')
|
|
158
|
-
.get('foo', { metadataFields: [types_1.MetadataField.CREATED_AT, types_1.MetadataField.UPDATED_AT] });
|
|
212
|
+
.get('foo', { metadataFields: [types_1.MetadataField.CREATED_AT, types_1.MetadataField.UPDATED_AT, types_1.MetadataField.EXPIRE_TIME] });
|
|
159
213
|
expect(rs).toEqual({
|
|
160
214
|
key: 'foo',
|
|
161
215
|
value: { name: 'Jane Doe' },
|
|
162
216
|
createdAt: 1718236800,
|
|
163
|
-
updatedAt: 1718236800
|
|
217
|
+
updatedAt: 1718236800,
|
|
218
|
+
expireTime: '2025-01-12T13:00:00Z'
|
|
164
219
|
});
|
|
165
220
|
expect(apiClient).toHaveBeenCalledWith('/api/v1/entity/get', expect.objectContaining({
|
|
166
221
|
body: JSON.stringify({
|
|
167
222
|
entityName: 'employees',
|
|
168
223
|
key: 'foo',
|
|
169
|
-
options: { metadataFields: [types_1.MetadataField.CREATED_AT, types_1.MetadataField.UPDATED_AT] }
|
|
224
|
+
options: { metadataFields: [types_1.MetadataField.CREATED_AT, types_1.MetadataField.UPDATED_AT, types_1.MetadataField.EXPIRE_TIME] }
|
|
170
225
|
})
|
|
171
226
|
}));
|
|
172
227
|
});
|
|
@@ -194,6 +249,93 @@ describe('KVS', () => {
|
|
|
194
249
|
body: JSON.stringify({ key: 'foo', value: 'bar' })
|
|
195
250
|
}));
|
|
196
251
|
});
|
|
252
|
+
it('should set correctly with TTL', async () => {
|
|
253
|
+
const response = new Response(undefined, {
|
|
254
|
+
status: 204,
|
|
255
|
+
headers: { 'x-trace-id': traceId }
|
|
256
|
+
});
|
|
257
|
+
const { sut, apiClient } = prepare(response);
|
|
258
|
+
const rs = await sut.set('foo', 'bar', { ttl: { value: 5, unit: 'MINUTES' } });
|
|
259
|
+
expect(rs).toBeUndefined();
|
|
260
|
+
expect(apiClient).toHaveBeenCalledWith('/api/v1/set', expect.objectContaining({
|
|
261
|
+
body: JSON.stringify({ key: 'foo', value: 'bar', options: { ttl: { value: 5, unit: 'MINUTES' } } })
|
|
262
|
+
}));
|
|
263
|
+
});
|
|
264
|
+
it('should set correctly with keyPolicy "FAIL_IF_EXISTS"', async () => {
|
|
265
|
+
const response = new Response(undefined, {
|
|
266
|
+
status: 204,
|
|
267
|
+
headers: { 'x-trace-id': traceId }
|
|
268
|
+
});
|
|
269
|
+
const { sut, apiClient } = prepare(response);
|
|
270
|
+
const rs = await sut.set('foo', 'bar', { ttl: { value: 5, unit: 'MINUTES' }, keyPolicy: 'FAIL_IF_EXISTS' });
|
|
271
|
+
expect(rs).toBeUndefined();
|
|
272
|
+
expect(apiClient).toHaveBeenCalledWith('/api/v1/set', expect.objectContaining({
|
|
273
|
+
body: JSON.stringify({
|
|
274
|
+
key: 'foo',
|
|
275
|
+
value: 'bar',
|
|
276
|
+
options: { ttl: { value: 5, unit: 'MINUTES' }, keyPolicy: 'FAIL_IF_EXISTS' }
|
|
277
|
+
})
|
|
278
|
+
}));
|
|
279
|
+
});
|
|
280
|
+
it('should set correctly with keyPolicy "OVERRIDE" and return value', async () => {
|
|
281
|
+
const responseBody = {
|
|
282
|
+
key: 'foo',
|
|
283
|
+
value: 'bar',
|
|
284
|
+
createdAt: Date.now(),
|
|
285
|
+
updatedAt: Date.now(),
|
|
286
|
+
expireTime: '2025-01-12T13:00:00Z'
|
|
287
|
+
};
|
|
288
|
+
const response = new Response(JSON.stringify(responseBody), {
|
|
289
|
+
status: 200,
|
|
290
|
+
headers: { 'x-trace-id': traceId }
|
|
291
|
+
});
|
|
292
|
+
const { sut, apiClient } = prepare(response);
|
|
293
|
+
const rs = await sut.set('foo', 'bar', {
|
|
294
|
+
ttl: { value: 5, unit: 'MINUTES' },
|
|
295
|
+
keyPolicy: 'OVERRIDE',
|
|
296
|
+
returnValue: 'LATEST',
|
|
297
|
+
returnMetadataFields: [types_1.MetadataField.CREATED_AT, types_1.MetadataField.UPDATED_AT, types_1.MetadataField.EXPIRE_TIME]
|
|
298
|
+
});
|
|
299
|
+
expect(rs).toEqual(responseBody);
|
|
300
|
+
expect(apiClient).toHaveBeenCalledWith('/api/v1/set', expect.objectContaining({
|
|
301
|
+
body: JSON.stringify({
|
|
302
|
+
key: 'foo',
|
|
303
|
+
value: 'bar',
|
|
304
|
+
options: {
|
|
305
|
+
ttl: { value: 5, unit: 'MINUTES' },
|
|
306
|
+
keyPolicy: 'OVERRIDE',
|
|
307
|
+
returnValue: 'LATEST',
|
|
308
|
+
returnMetadataFields: [types_1.MetadataField.CREATED_AT, types_1.MetadataField.UPDATED_AT, types_1.MetadataField.EXPIRE_TIME]
|
|
309
|
+
}
|
|
310
|
+
})
|
|
311
|
+
}));
|
|
312
|
+
});
|
|
313
|
+
it('should set and return undefined when no previous value exists', async () => {
|
|
314
|
+
const response = new Response(undefined, {
|
|
315
|
+
status: 204,
|
|
316
|
+
headers: { 'x-trace-id': traceId }
|
|
317
|
+
});
|
|
318
|
+
const { sut, apiClient } = prepare(response);
|
|
319
|
+
const rs = await sut.set('foo', 'bar', {
|
|
320
|
+
ttl: { value: 5, unit: 'MINUTES' },
|
|
321
|
+
keyPolicy: 'OVERRIDE',
|
|
322
|
+
returnValue: 'PREVIOUS',
|
|
323
|
+
returnMetadataFields: [types_1.MetadataField.CREATED_AT, types_1.MetadataField.UPDATED_AT, types_1.MetadataField.EXPIRE_TIME]
|
|
324
|
+
});
|
|
325
|
+
expect(rs).toEqual(undefined);
|
|
326
|
+
expect(apiClient).toHaveBeenCalledWith('/api/v1/set', expect.objectContaining({
|
|
327
|
+
body: JSON.stringify({
|
|
328
|
+
key: 'foo',
|
|
329
|
+
value: 'bar',
|
|
330
|
+
options: {
|
|
331
|
+
ttl: { value: 5, unit: 'MINUTES' },
|
|
332
|
+
keyPolicy: 'OVERRIDE',
|
|
333
|
+
returnValue: 'PREVIOUS',
|
|
334
|
+
returnMetadataFields: [types_1.MetadataField.CREATED_AT, types_1.MetadataField.UPDATED_AT, types_1.MetadataField.EXPIRE_TIME]
|
|
335
|
+
}
|
|
336
|
+
})
|
|
337
|
+
}));
|
|
338
|
+
});
|
|
197
339
|
it('should setSecret correctly', async () => {
|
|
198
340
|
const response = new Response(undefined, {
|
|
199
341
|
status: 204,
|
|
@@ -206,6 +348,93 @@ describe('KVS', () => {
|
|
|
206
348
|
body: JSON.stringify({ key: 'foo', value: 'bar' })
|
|
207
349
|
}));
|
|
208
350
|
});
|
|
351
|
+
it('should setSecret correctly with TTL', async () => {
|
|
352
|
+
const response = new Response(undefined, {
|
|
353
|
+
status: 204,
|
|
354
|
+
headers: { 'x-trace-id': traceId }
|
|
355
|
+
});
|
|
356
|
+
const { sut, apiClient } = prepare(response);
|
|
357
|
+
const rs = await sut.setSecret('foo', 'bar', { ttl: { value: 5, unit: 'MINUTES' } });
|
|
358
|
+
expect(rs).toBeUndefined();
|
|
359
|
+
expect(apiClient).toHaveBeenCalledWith('/api/v1/secret/set', expect.objectContaining({
|
|
360
|
+
body: JSON.stringify({ key: 'foo', value: 'bar', options: { ttl: { value: 5, unit: 'MINUTES' } } })
|
|
361
|
+
}));
|
|
362
|
+
});
|
|
363
|
+
it('should setSecret correctly with keyPolicy "FAIL_IF_EXISTS"', async () => {
|
|
364
|
+
const response = new Response(undefined, {
|
|
365
|
+
status: 204,
|
|
366
|
+
headers: { 'x-trace-id': traceId }
|
|
367
|
+
});
|
|
368
|
+
const { sut, apiClient } = prepare(response);
|
|
369
|
+
const rs = await sut.setSecret('foo', 'bar', { ttl: { value: 5, unit: 'MINUTES' }, keyPolicy: 'FAIL_IF_EXISTS' });
|
|
370
|
+
expect(rs).toBeUndefined();
|
|
371
|
+
expect(apiClient).toHaveBeenCalledWith('/api/v1/secret/set', expect.objectContaining({
|
|
372
|
+
body: JSON.stringify({
|
|
373
|
+
key: 'foo',
|
|
374
|
+
value: 'bar',
|
|
375
|
+
options: { ttl: { value: 5, unit: 'MINUTES' }, keyPolicy: 'FAIL_IF_EXISTS' }
|
|
376
|
+
})
|
|
377
|
+
}));
|
|
378
|
+
});
|
|
379
|
+
it('should setSecret correctly with keyPolicy "OVERRIDE" and return value', async () => {
|
|
380
|
+
const responseBody = {
|
|
381
|
+
key: 'foo',
|
|
382
|
+
value: 'bar',
|
|
383
|
+
createdAt: Date.now(),
|
|
384
|
+
updatedAt: Date.now(),
|
|
385
|
+
expireTime: '2025-01-12T13:00:00Z'
|
|
386
|
+
};
|
|
387
|
+
const response = new Response(JSON.stringify(responseBody), {
|
|
388
|
+
status: 200,
|
|
389
|
+
headers: { 'x-trace-id': traceId }
|
|
390
|
+
});
|
|
391
|
+
const { sut, apiClient } = prepare(response);
|
|
392
|
+
const rs = await sut.setSecret('foo', 'bar', {
|
|
393
|
+
ttl: { value: 5, unit: 'MINUTES' },
|
|
394
|
+
keyPolicy: 'OVERRIDE',
|
|
395
|
+
returnValue: 'LATEST',
|
|
396
|
+
returnMetadataFields: [types_1.MetadataField.CREATED_AT, types_1.MetadataField.UPDATED_AT, types_1.MetadataField.EXPIRE_TIME]
|
|
397
|
+
});
|
|
398
|
+
expect(rs).toEqual(responseBody);
|
|
399
|
+
expect(apiClient).toHaveBeenCalledWith('/api/v1/secret/set', expect.objectContaining({
|
|
400
|
+
body: JSON.stringify({
|
|
401
|
+
key: 'foo',
|
|
402
|
+
value: 'bar',
|
|
403
|
+
options: {
|
|
404
|
+
ttl: { value: 5, unit: 'MINUTES' },
|
|
405
|
+
keyPolicy: 'OVERRIDE',
|
|
406
|
+
returnValue: 'LATEST',
|
|
407
|
+
returnMetadataFields: [types_1.MetadataField.CREATED_AT, types_1.MetadataField.UPDATED_AT, types_1.MetadataField.EXPIRE_TIME]
|
|
408
|
+
}
|
|
409
|
+
})
|
|
410
|
+
}));
|
|
411
|
+
});
|
|
412
|
+
it('should setSecret and return undefined when no previous value exists', async () => {
|
|
413
|
+
const response = new Response(undefined, {
|
|
414
|
+
status: 204,
|
|
415
|
+
headers: { 'x-trace-id': traceId }
|
|
416
|
+
});
|
|
417
|
+
const { sut, apiClient } = prepare(response);
|
|
418
|
+
const rs = await sut.setSecret('foo', 'bar', {
|
|
419
|
+
ttl: { value: 5, unit: 'MINUTES' },
|
|
420
|
+
keyPolicy: 'OVERRIDE',
|
|
421
|
+
returnValue: 'PREVIOUS',
|
|
422
|
+
returnMetadataFields: [types_1.MetadataField.CREATED_AT, types_1.MetadataField.UPDATED_AT, types_1.MetadataField.EXPIRE_TIME]
|
|
423
|
+
});
|
|
424
|
+
expect(rs).toEqual(undefined);
|
|
425
|
+
expect(apiClient).toHaveBeenCalledWith('/api/v1/secret/set', expect.objectContaining({
|
|
426
|
+
body: JSON.stringify({
|
|
427
|
+
key: 'foo',
|
|
428
|
+
value: 'bar',
|
|
429
|
+
options: {
|
|
430
|
+
ttl: { value: 5, unit: 'MINUTES' },
|
|
431
|
+
keyPolicy: 'OVERRIDE',
|
|
432
|
+
returnValue: 'PREVIOUS',
|
|
433
|
+
returnMetadataFields: [types_1.MetadataField.CREATED_AT, types_1.MetadataField.UPDATED_AT, types_1.MetadataField.EXPIRE_TIME]
|
|
434
|
+
}
|
|
435
|
+
})
|
|
436
|
+
}));
|
|
437
|
+
});
|
|
209
438
|
it('should setEntity correctly', async () => {
|
|
210
439
|
const response = new Response(undefined, {
|
|
211
440
|
status: 204,
|
|
@@ -218,6 +447,103 @@ describe('KVS', () => {
|
|
|
218
447
|
body: JSON.stringify({ entityName: 'employees', key: 'foo', value: { name: 'Jane Doe' } })
|
|
219
448
|
}));
|
|
220
449
|
});
|
|
450
|
+
it('should setEntity correctly with TTL', async () => {
|
|
451
|
+
const response = new Response(undefined, {
|
|
452
|
+
status: 204,
|
|
453
|
+
headers: { 'x-trace-id': traceId }
|
|
454
|
+
});
|
|
455
|
+
const { sut, apiClient } = prepare(response);
|
|
456
|
+
const rs = await sut.entity('employees').set('foo', { name: 'Jane Doe' }, { ttl: { value: 5, unit: 'MINUTES' } });
|
|
457
|
+
expect(rs).toBeUndefined();
|
|
458
|
+
expect(apiClient).toHaveBeenCalledWith('/api/v1/entity/set', expect.objectContaining({
|
|
459
|
+
body: JSON.stringify({
|
|
460
|
+
entityName: 'employees',
|
|
461
|
+
key: 'foo',
|
|
462
|
+
value: { name: 'Jane Doe' },
|
|
463
|
+
options: { ttl: { value: 5, unit: 'MINUTES' } }
|
|
464
|
+
})
|
|
465
|
+
}));
|
|
466
|
+
});
|
|
467
|
+
it('should setEntity correctly with "FAIL_IF_EXISTS"', async () => {
|
|
468
|
+
const response = new Response(undefined, {
|
|
469
|
+
status: 204,
|
|
470
|
+
headers: { 'x-trace-id': traceId }
|
|
471
|
+
});
|
|
472
|
+
const { sut, apiClient } = prepare(response);
|
|
473
|
+
const rs = await sut
|
|
474
|
+
.entity('employees')
|
|
475
|
+
.set('foo', { name: 'Jane Doe' }, { ttl: { value: 5, unit: 'MINUTES' }, keyPolicy: 'FAIL_IF_EXISTS' });
|
|
476
|
+
expect(rs).toBeUndefined();
|
|
477
|
+
expect(apiClient).toHaveBeenCalledWith('/api/v1/entity/set', expect.objectContaining({
|
|
478
|
+
body: JSON.stringify({
|
|
479
|
+
entityName: 'employees',
|
|
480
|
+
key: 'foo',
|
|
481
|
+
value: { name: 'Jane Doe' },
|
|
482
|
+
options: { ttl: { value: 5, unit: 'MINUTES' }, keyPolicy: 'FAIL_IF_EXISTS' }
|
|
483
|
+
})
|
|
484
|
+
}));
|
|
485
|
+
});
|
|
486
|
+
it('should setEntity correctly with "OVERRIDE" and return value', async () => {
|
|
487
|
+
const responseBody = {
|
|
488
|
+
key: 'foo',
|
|
489
|
+
value: 'bar',
|
|
490
|
+
createdAt: Date.now(),
|
|
491
|
+
updatedAt: Date.now(),
|
|
492
|
+
expireTime: '2025-01-12T13:00:00Z'
|
|
493
|
+
};
|
|
494
|
+
const response = new Response(JSON.stringify(responseBody), {
|
|
495
|
+
status: 200,
|
|
496
|
+
headers: { 'x-trace-id': traceId }
|
|
497
|
+
});
|
|
498
|
+
const { sut, apiClient } = prepare(response);
|
|
499
|
+
const rs = await sut.entity('employees').set('foo', { name: 'Jane Doe' }, {
|
|
500
|
+
ttl: { value: 5, unit: 'MINUTES' },
|
|
501
|
+
keyPolicy: 'OVERRIDE',
|
|
502
|
+
returnValue: 'LATEST',
|
|
503
|
+
returnMetadataFields: [types_1.MetadataField.CREATED_AT, types_1.MetadataField.UPDATED_AT, types_1.MetadataField.EXPIRE_TIME]
|
|
504
|
+
});
|
|
505
|
+
expect(rs).toEqual(responseBody);
|
|
506
|
+
expect(apiClient).toHaveBeenCalledWith('/api/v1/entity/set', expect.objectContaining({
|
|
507
|
+
body: JSON.stringify({
|
|
508
|
+
entityName: 'employees',
|
|
509
|
+
key: 'foo',
|
|
510
|
+
value: { name: 'Jane Doe' },
|
|
511
|
+
options: {
|
|
512
|
+
ttl: { value: 5, unit: 'MINUTES' },
|
|
513
|
+
keyPolicy: 'OVERRIDE',
|
|
514
|
+
returnValue: 'LATEST',
|
|
515
|
+
returnMetadataFields: [types_1.MetadataField.CREATED_AT, types_1.MetadataField.UPDATED_AT, types_1.MetadataField.EXPIRE_TIME]
|
|
516
|
+
}
|
|
517
|
+
})
|
|
518
|
+
}));
|
|
519
|
+
});
|
|
520
|
+
it('should setEntity and return undefined when no previous value exists', async () => {
|
|
521
|
+
const response = new Response(undefined, {
|
|
522
|
+
status: 204,
|
|
523
|
+
headers: { 'x-trace-id': traceId }
|
|
524
|
+
});
|
|
525
|
+
const { sut, apiClient } = prepare(response);
|
|
526
|
+
const rs = await sut.entity('employees').set('foo', { name: 'Jane Doe' }, {
|
|
527
|
+
ttl: { value: 5, unit: 'MINUTES' },
|
|
528
|
+
keyPolicy: 'OVERRIDE',
|
|
529
|
+
returnValue: 'PREVIOUS',
|
|
530
|
+
returnMetadataFields: [types_1.MetadataField.CREATED_AT, types_1.MetadataField.UPDATED_AT, types_1.MetadataField.EXPIRE_TIME]
|
|
531
|
+
});
|
|
532
|
+
expect(rs).toBeUndefined();
|
|
533
|
+
expect(apiClient).toHaveBeenCalledWith('/api/v1/entity/set', expect.objectContaining({
|
|
534
|
+
body: JSON.stringify({
|
|
535
|
+
entityName: 'employees',
|
|
536
|
+
key: 'foo',
|
|
537
|
+
value: { name: 'Jane Doe' },
|
|
538
|
+
options: {
|
|
539
|
+
ttl: { value: 5, unit: 'MINUTES' },
|
|
540
|
+
keyPolicy: 'OVERRIDE',
|
|
541
|
+
returnValue: 'PREVIOUS',
|
|
542
|
+
returnMetadataFields: [types_1.MetadataField.CREATED_AT, types_1.MetadataField.UPDATED_AT, types_1.MetadataField.EXPIRE_TIME]
|
|
543
|
+
}
|
|
544
|
+
})
|
|
545
|
+
}));
|
|
546
|
+
});
|
|
221
547
|
it('should delete correctly', async () => {
|
|
222
548
|
const response = new Response(undefined, {
|
|
223
549
|
status: 204,
|
|
@@ -314,6 +640,36 @@ describe('KVS', () => {
|
|
|
314
640
|
})
|
|
315
641
|
}));
|
|
316
642
|
});
|
|
643
|
+
it('should query correctly with TTL fields', async () => {
|
|
644
|
+
const response = new Response(JSON.stringify({
|
|
645
|
+
cursor: 'third-page',
|
|
646
|
+
data: [{ key: 'foo', value: 'bar', expireTime: '2025-01-12T13:00:00Z' }]
|
|
647
|
+
}), {
|
|
648
|
+
status: 200,
|
|
649
|
+
headers: { 'x-trace-id': traceId }
|
|
650
|
+
});
|
|
651
|
+
const { sut, apiClient } = prepare(response);
|
|
652
|
+
const rs = await sut
|
|
653
|
+
.query({ metadataFields: [types_1.MetadataField.EXPIRE_TIME] })
|
|
654
|
+
.cursor('second-page')
|
|
655
|
+
.limit(1)
|
|
656
|
+
.where('key', conditions_1.WhereConditions.beginsWith('fo'))
|
|
657
|
+
.getMany();
|
|
658
|
+
expect(rs).toEqual({
|
|
659
|
+
results: [{ key: 'foo', value: 'bar', expireTime: '2025-01-12T13:00:00Z' }],
|
|
660
|
+
nextCursor: 'third-page'
|
|
661
|
+
});
|
|
662
|
+
expect(apiClient).toHaveBeenCalledWith('/api/v1/query', expect.objectContaining({
|
|
663
|
+
body: JSON.stringify({
|
|
664
|
+
limit: 1,
|
|
665
|
+
after: 'second-page',
|
|
666
|
+
where: [{ property: 'key', condition: 'BEGINS_WITH', values: ['fo'] }],
|
|
667
|
+
options: {
|
|
668
|
+
metadataFields: [types_1.MetadataField.EXPIRE_TIME]
|
|
669
|
+
}
|
|
670
|
+
})
|
|
671
|
+
}));
|
|
672
|
+
});
|
|
317
673
|
it('should getOne out of a list of results', async () => {
|
|
318
674
|
const response = new Response(JSON.stringify({
|
|
319
675
|
data: [
|
|
@@ -343,25 +699,28 @@ describe('KVS', () => {
|
|
|
343
699
|
body: JSON.stringify({ limit: 1 })
|
|
344
700
|
}));
|
|
345
701
|
});
|
|
346
|
-
it('should query entity correctly and pass partition with index', async () => {
|
|
347
|
-
const response = new Response(JSON.stringify({ data: [{ key: 'foo', value: { name: 'Jane Doe' } }] }), {
|
|
702
|
+
it('should query entity correctly and pass partition with index and metadata fields', async () => {
|
|
703
|
+
const response = new Response(JSON.stringify({ data: [{ key: 'foo', value: { name: 'Jane Doe' }, expireTime: '2025-01-12T13:00:00Z' }] }), {
|
|
348
704
|
status: 200,
|
|
349
705
|
headers: { 'x-trace-id': traceId }
|
|
350
706
|
});
|
|
351
707
|
const { sut, apiClient } = prepare(response);
|
|
352
708
|
const rs = await sut
|
|
353
709
|
.entity('employees')
|
|
354
|
-
.query()
|
|
710
|
+
.query({ metadataFields: [types_1.MetadataField.EXPIRE_TIME] })
|
|
355
711
|
.index('by-employmentyear', { partition: [2000] })
|
|
356
712
|
.getMany();
|
|
357
713
|
expect(rs).toEqual({
|
|
358
|
-
results: [{ key: 'foo', value: { name: 'Jane Doe' } }]
|
|
714
|
+
results: [{ key: 'foo', value: { name: 'Jane Doe' }, expireTime: '2025-01-12T13:00:00Z' }]
|
|
359
715
|
});
|
|
360
716
|
expect(apiClient).toHaveBeenCalledWith('/api/v1/entity/query', expect.objectContaining({
|
|
361
717
|
body: JSON.stringify({
|
|
362
718
|
entityName: 'employees',
|
|
363
719
|
indexName: 'by-employmentyear',
|
|
364
|
-
partition: [2000]
|
|
720
|
+
partition: [2000],
|
|
721
|
+
options: {
|
|
722
|
+
metadataFields: [types_1.MetadataField.EXPIRE_TIME]
|
|
723
|
+
}
|
|
365
724
|
})
|
|
366
725
|
}));
|
|
367
726
|
});
|
|
@@ -658,6 +1017,24 @@ describe('KVS', () => {
|
|
|
658
1017
|
})
|
|
659
1018
|
}));
|
|
660
1019
|
});
|
|
1020
|
+
it('should sumbit transaction correctly with TTL', async () => {
|
|
1021
|
+
const response = new Response(undefined, {
|
|
1022
|
+
status: 200,
|
|
1023
|
+
headers: { 'x-trace-id': traceId }
|
|
1024
|
+
});
|
|
1025
|
+
const { sut, apiClient } = prepare(response);
|
|
1026
|
+
await sut
|
|
1027
|
+
.transact()
|
|
1028
|
+
.set('foo', 'bar', undefined, { ttl: { unit: 'SECONDS', value: 5 } })
|
|
1029
|
+
.delete('bar')
|
|
1030
|
+
.execute();
|
|
1031
|
+
expect(apiClient).toHaveBeenCalledWith('/api/v1/transaction', expect.objectContaining({
|
|
1032
|
+
body: JSON.stringify({
|
|
1033
|
+
set: [{ key: 'foo', value: 'bar', options: { ttl: { unit: 'SECONDS', value: 5 } } }],
|
|
1034
|
+
delete: [{ key: 'bar' }]
|
|
1035
|
+
})
|
|
1036
|
+
}));
|
|
1037
|
+
});
|
|
661
1038
|
it('should batchSet correctly with mixed entity and non-entity items', async () => {
|
|
662
1039
|
const response = new Response(JSON.stringify({
|
|
663
1040
|
successfulKeys: [{ key: 'foo', entityName: 'employees' }, { key: 'bar' }],
|
|
@@ -693,6 +1070,43 @@ describe('KVS', () => {
|
|
|
693
1070
|
body: JSON.stringify(items)
|
|
694
1071
|
}));
|
|
695
1072
|
});
|
|
1073
|
+
it('should batchSet correctly with mixed entity and non-entity items with TTL', async () => {
|
|
1074
|
+
const response = new Response(JSON.stringify({
|
|
1075
|
+
successfulKeys: [{ key: 'foo', entityName: 'employees' }, { key: 'bar' }, { key: 'xxx' }],
|
|
1076
|
+
failedKeys: [
|
|
1077
|
+
{
|
|
1078
|
+
key: 'baz',
|
|
1079
|
+
entityName: 'departments',
|
|
1080
|
+
error: { code: 'VALIDATION_ERROR', message: 'Invalid value' }
|
|
1081
|
+
}
|
|
1082
|
+
]
|
|
1083
|
+
}), {
|
|
1084
|
+
status: 200,
|
|
1085
|
+
headers: { 'x-trace-id': traceId }
|
|
1086
|
+
});
|
|
1087
|
+
const { sut, apiClient } = prepare(response);
|
|
1088
|
+
const ttl = { value: 10, unit: 'MINUTES' };
|
|
1089
|
+
const items = [
|
|
1090
|
+
{ key: 'foo', value: 'John Doe', entityName: 'employees', options: { ttl } },
|
|
1091
|
+
{ key: 'bar', value: 'simple value', options: { ttl } },
|
|
1092
|
+
{ key: 'baz', value: 'IT Department', entityName: 'departments', options: { ttl } },
|
|
1093
|
+
{ key: 'xxx', value: 'yyy', options: { ttl } }
|
|
1094
|
+
];
|
|
1095
|
+
const rs = await sut.batchSet(items);
|
|
1096
|
+
expect(rs).toEqual({
|
|
1097
|
+
successfulKeys: [{ key: 'foo', entityName: 'employees' }, { key: 'bar' }, { key: 'xxx' }],
|
|
1098
|
+
failedKeys: [
|
|
1099
|
+
{
|
|
1100
|
+
key: 'baz',
|
|
1101
|
+
entityName: 'departments',
|
|
1102
|
+
error: { code: 'VALIDATION_ERROR', message: 'Invalid value' }
|
|
1103
|
+
}
|
|
1104
|
+
]
|
|
1105
|
+
});
|
|
1106
|
+
expect(apiClient).toHaveBeenCalledWith('/api/v1/batch/set', expect.objectContaining({
|
|
1107
|
+
body: JSON.stringify(items)
|
|
1108
|
+
}));
|
|
1109
|
+
});
|
|
696
1110
|
it('should handle batchSet with all successful keys', async () => {
|
|
697
1111
|
const response = new Response(JSON.stringify({
|
|
698
1112
|
successfulKeys: [{ key: 'foo' }, { key: 'bar' }],
|
package/out/entity-query.d.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import { EntityFilterClauses, AndFilter, EntityQueryBuilder, IndexQueryBuilder, Filter, FilterItem, IndexOptions, OrFilter } from './interfaces/entity-query';
|
|
1
|
+
import { EntityFilterClauses, EntityQueryOptions, AndFilter, EntityQueryBuilder, IndexQueryBuilder, Filter, FilterItem, IndexOptions, OrFilter } from './interfaces/entity-query';
|
|
2
2
|
import { StorageApi } from './storage-api';
|
|
3
3
|
export declare class KvsIndexQueryBuilder<T> implements IndexQueryBuilder<T> {
|
|
4
4
|
private readonly entityName;
|
|
5
5
|
private readonly storageApi;
|
|
6
|
-
|
|
6
|
+
private readonly options?;
|
|
7
|
+
constructor(entityName: string, storageApi: StorageApi, options?: Partial<EntityQueryOptions<T>> | undefined);
|
|
7
8
|
index(name: string, indexOptions?: IndexOptions<T>): EntityQueryBuilder<T>;
|
|
8
9
|
}
|
|
9
10
|
export declare abstract class BaseFilter<T> {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"entity-query.d.ts","sourceRoot":"","sources":["../src/entity-query.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,mBAAmB,
|
|
1
|
+
{"version":3,"file":"entity-query.d.ts","sourceRoot":"","sources":["../src/entity-query.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,mBAAmB,EACnB,kBAAkB,EAElB,SAAS,EACT,kBAAkB,EAClB,iBAAiB,EACjB,MAAM,EACN,UAAU,EACV,YAAY,EACZ,QAAQ,EACT,MAAM,2BAA2B,CAAC;AAEnC,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAE3C,qBAAa,oBAAoB,CAAC,CAAC,CAAE,YAAW,iBAAiB,CAAC,CAAC,CAAC;IAEhE,OAAO,CAAC,QAAQ,CAAC,UAAU;IAC3B,OAAO,CAAC,QAAQ,CAAC,UAAU;IAC3B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC;gBAFR,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,UAAU,EACtB,OAAO,CAAC,4CAAgC;IAG3D,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,YAAY,CAAC,CAAC,CAAC,GAAG,kBAAkB,CAAC,CAAC,CAAC;CAU3E;AA4DD,8BAAsB,UAAU,CAAC,CAAC;IACpB,SAAS,CAAC,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;gBAA3B,KAAK,GAAE,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,CAAM;IAExD,OAAO,IAAI,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;IAI/B,QAAQ;CAGhB;AAED,qBAAa,aAAa,CAAC,CAAC,CAAE,SAAQ,UAAU,CAAC,CAAC,CAAE,YAAW,MAAM,CAAC,CAAC,CAAC;IACtE,GAAG,CAAC,KAAK,EAAE,MAAM,CAAC,EAAE,SAAS,EAAE,mBAAmB,GAAG,gBAAgB,CAAC,CAAC,CAAC;IAIxE,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,EAAE,SAAS,EAAE,mBAAmB,GAAG,eAAe,CAAC,CAAC,CAAC;CAGvE;AAED,cAAM,gBAAgB,CAAC,CAAC,CAAE,SAAQ,UAAU,CAAC,CAAC,CAAE,YAAW,SAAS,CAAC,CAAC,CAAC;IACrE,GAAG,CAAC,KAAK,EAAE,MAAM,CAAC,EAAE,SAAS,EAAE,mBAAmB,GAAG,IAAI;CAI1D;AAED,cAAM,eAAe,CAAC,CAAC,CAAE,SAAQ,UAAU,CAAC,CAAC,CAAE,YAAW,QAAQ,CAAC,CAAC,CAAC;IACnE,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,EAAE,SAAS,EAAE,mBAAmB,GAAG,IAAI;CAIzD"}
|
package/out/entity-query.js
CHANGED
|
@@ -4,12 +4,15 @@ exports.FilterBuilder = exports.BaseFilter = exports.KvsIndexQueryBuilder = void
|
|
|
4
4
|
class KvsIndexQueryBuilder {
|
|
5
5
|
entityName;
|
|
6
6
|
storageApi;
|
|
7
|
-
|
|
7
|
+
options;
|
|
8
|
+
constructor(entityName, storageApi, options) {
|
|
8
9
|
this.entityName = entityName;
|
|
9
10
|
this.storageApi = storageApi;
|
|
11
|
+
this.options = options;
|
|
10
12
|
}
|
|
11
13
|
index(name, indexOptions) {
|
|
12
14
|
return new KvsEntityQueryBuilder(this.storageApi, {
|
|
15
|
+
...this.options,
|
|
13
16
|
entityName: this.entityName,
|
|
14
17
|
indexName: name,
|
|
15
18
|
partition: indexOptions?.partition,
|
|
@@ -54,16 +57,20 @@ class KvsEntityQueryBuilder {
|
|
|
54
57
|
return undefined;
|
|
55
58
|
}
|
|
56
59
|
async getMany() {
|
|
57
|
-
const
|
|
60
|
+
const maybeOptions = {
|
|
61
|
+
...(this.queryOptions?.metadataFields ? { options: { metadataFields: this.queryOptions.metadataFields } } : {})
|
|
62
|
+
};
|
|
63
|
+
const { filters, filterOperator, metadataFields, ...rest } = this.queryOptions;
|
|
58
64
|
if (filters && filterOperator && filters.length > 0) {
|
|
59
65
|
return this.storageApi.queryEntity({
|
|
60
66
|
...rest,
|
|
67
|
+
...maybeOptions,
|
|
61
68
|
filters: {
|
|
62
69
|
[filterOperator]: filters
|
|
63
70
|
}
|
|
64
71
|
});
|
|
65
72
|
}
|
|
66
|
-
return this.storageApi.queryEntity(rest);
|
|
73
|
+
return this.storageApi.queryEntity({ ...rest, ...maybeOptions });
|
|
67
74
|
}
|
|
68
75
|
}
|
|
69
76
|
class BaseFilter {
|
package/out/entity.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { IndexQueryBuilder } from './interfaces/entity-query';
|
|
1
|
+
import { EntityQueryOptions, IndexQueryBuilder } from './interfaces/entity-query';
|
|
2
2
|
import { KvsEntity } from './interfaces/kvs';
|
|
3
|
-
import { GetOptions, GetResult } from './interfaces/types';
|
|
3
|
+
import { GetOptions, GetResult, SetOptions, PolicySetOptions, OverrideAndReturnSetOptions, SetResult } from './interfaces/types';
|
|
4
4
|
import { StorageApi } from './storage-api';
|
|
5
5
|
export declare class EntityImpl<T> implements KvsEntity<T> {
|
|
6
6
|
private readonly entityName;
|
|
@@ -8,8 +8,10 @@ export declare class EntityImpl<T> implements KvsEntity<T> {
|
|
|
8
8
|
constructor(entityName: string, storageApi: StorageApi);
|
|
9
9
|
get(key: string): Promise<T | undefined>;
|
|
10
10
|
get(key: string, options: GetOptions): Promise<GetResult<T> | undefined>;
|
|
11
|
-
set(key: string, value: T): Promise<void>;
|
|
11
|
+
set(key: string, value: T, options?: SetOptions): Promise<void>;
|
|
12
|
+
set(key: string, value: T, options: PolicySetOptions): Promise<void>;
|
|
13
|
+
set<U = T>(key: string, value: T, options: OverrideAndReturnSetOptions): Promise<SetResult<U> | undefined>;
|
|
12
14
|
delete(key: string): Promise<void>;
|
|
13
|
-
query(): IndexQueryBuilder<T>;
|
|
15
|
+
query(options?: Pick<EntityQueryOptions<T>, 'metadataFields'>): IndexQueryBuilder<T>;
|
|
14
16
|
}
|
|
15
17
|
//# sourceMappingURL=entity.d.ts.map
|
package/out/entity.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"entity.d.ts","sourceRoot":"","sources":["../src/entity.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;
|
|
1
|
+
{"version":3,"file":"entity.d.ts","sourceRoot":"","sources":["../src/entity.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAClF,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EACL,UAAU,EACV,SAAS,EACT,UAAU,EACV,gBAAgB,EAChB,2BAA2B,EAC3B,SAAS,EAEV,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAE3C,qBAAa,UAAU,CAAC,CAAC,CAAE,YAAW,SAAS,CAAC,CAAC,CAAC;IAE9C,OAAO,CAAC,QAAQ,CAAC,UAAU;IAC3B,OAAO,CAAC,QAAQ,CAAC,UAAU;gBADV,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,UAAU;IAGzC,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC;IACxC,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC;IASxE,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,CAAC,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAC/D,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC;IACpE,GAAG,CAAC,CAAC,GAAG,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,2BAA2B,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC;IAU1G,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAOlC,KAAK,CAAC,OAAO,CAAC,EAAE,IAAI,CAAC,kBAAkB,CAAC,CAAC,CAAC,EAAE,gBAAgB,CAAC,GAAG,iBAAiB,CAAC,CAAC,CAAC;CAGrF"}
|