@workglow/storage 0.2.25 → 0.2.26
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/dist/browser.js +298 -1
- package/dist/browser.js.map +13 -11
- package/dist/bun.js +388 -1
- package/dist/bun.js.map +16 -14
- package/dist/common.d.ts +3 -0
- package/dist/common.d.ts.map +1 -1
- package/dist/node.js +388 -1
- package/dist/node.js.map +16 -14
- package/dist/tabular/BaseTabularStorage.d.ts +15 -1
- package/dist/tabular/BaseTabularStorage.d.ts.map +1 -1
- package/dist/tabular/CachedTabularStorage.d.ts +2 -1
- package/dist/tabular/CachedTabularStorage.d.ts.map +1 -1
- package/dist/tabular/CoveringIndexMissingError.d.ts +14 -0
- package/dist/tabular/CoveringIndexMissingError.d.ts.map +1 -0
- package/dist/tabular/FsFolderTabularStorage.d.ts +6 -1
- package/dist/tabular/FsFolderTabularStorage.d.ts.map +1 -1
- package/dist/tabular/ITabularStorage.d.ts +19 -1
- package/dist/tabular/ITabularStorage.d.ts.map +1 -1
- package/dist/tabular/InMemoryTabularStorage.d.ts +7 -1
- package/dist/tabular/InMemoryTabularStorage.d.ts.map +1 -1
- package/dist/tabular/IndexedDbTabularStorage.d.ts +10 -1
- package/dist/tabular/IndexedDbTabularStorage.d.ts.map +1 -1
- package/dist/tabular/PostgresTabularStorage.d.ts +11 -1
- package/dist/tabular/PostgresTabularStorage.d.ts.map +1 -1
- package/dist/tabular/SqliteTabularStorage.d.ts +11 -1
- package/dist/tabular/SqliteTabularStorage.d.ts.map +1 -1
- package/dist/tabular/SupabaseTabularStorage.d.ts +2 -1
- package/dist/tabular/SupabaseTabularStorage.d.ts.map +1 -1
- package/dist/tabular/TelemetryTabularStorage.d.ts +2 -1
- package/dist/tabular/TelemetryTabularStorage.d.ts.map +1 -1
- package/dist/tabular/coveringIndexPicker.d.ts +37 -0
- package/dist/tabular/coveringIndexPicker.d.ts.map +1 -0
- package/dist/vector/IVectorStorage.d.ts +3 -1
- package/dist/vector/IVectorStorage.d.ts.map +1 -1
- package/package.json +3 -3
package/dist/browser.js
CHANGED
|
@@ -45,6 +45,21 @@ class StorageUnsupportedError extends StorageError {
|
|
|
45
45
|
}
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
+
// src/tabular/CoveringIndexMissingError.ts
|
|
49
|
+
class CoveringIndexMissingError extends StorageError {
|
|
50
|
+
static type = "CoveringIndexMissingError";
|
|
51
|
+
table;
|
|
52
|
+
requiredColumns;
|
|
53
|
+
registeredIndexes;
|
|
54
|
+
constructor(table, requiredColumns, registeredIndexes) {
|
|
55
|
+
const indexList = registeredIndexes.map((cols) => `[${cols.join(", ")}]`).join(", ");
|
|
56
|
+
super(`No covering index for table "${table}". ` + `Required columns: [${requiredColumns.join(", ")}]. ` + `Registered indexes: ${indexList || "(none)"}.`);
|
|
57
|
+
this.table = table;
|
|
58
|
+
this.requiredColumns = requiredColumns;
|
|
59
|
+
this.registeredIndexes = registeredIndexes;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
48
63
|
// src/tabular/BaseTabularStorage.ts
|
|
49
64
|
var TABULAR_REPOSITORY = createServiceToken("storage.tabularRepository");
|
|
50
65
|
|
|
@@ -168,6 +183,15 @@ class BaseTabularStorage {
|
|
|
168
183
|
const entities = await this.query(criteria);
|
|
169
184
|
return entities?.length ?? 0;
|
|
170
185
|
}
|
|
186
|
+
queryIndex(criteria, options) {
|
|
187
|
+
this.validateSelect(options);
|
|
188
|
+
const requiredColumns = [
|
|
189
|
+
...Object.keys(criteria),
|
|
190
|
+
...(options.orderBy ?? []).map((o) => String(o.column)),
|
|
191
|
+
...options.select.map(String)
|
|
192
|
+
];
|
|
193
|
+
throw new CoveringIndexMissingError(this.constructor.name, requiredColumns, []);
|
|
194
|
+
}
|
|
171
195
|
async* records(pageSize = 100) {
|
|
172
196
|
if (pageSize <= 0) {
|
|
173
197
|
throw new RangeError(`pageSize must be greater than 0, got ${pageSize}`);
|
|
@@ -263,6 +287,18 @@ class BaseTabularStorage {
|
|
|
263
287
|
}
|
|
264
288
|
}
|
|
265
289
|
}
|
|
290
|
+
validateSelect(options) {
|
|
291
|
+
if (!options.select || options.select.length === 0) {
|
|
292
|
+
throw new StorageValidationError("queryIndex requires a non-empty select array");
|
|
293
|
+
}
|
|
294
|
+
const schemaProps = Object.keys(this.schema.properties);
|
|
295
|
+
for (const col of options.select) {
|
|
296
|
+
const colStr = String(col);
|
|
297
|
+
if (!schemaProps.includes(colStr)) {
|
|
298
|
+
throw new StorageValidationError(`queryIndex select column "${colStr}" is not in schema`);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
}
|
|
266
302
|
primaryKeyColumns() {
|
|
267
303
|
const columns = [];
|
|
268
304
|
for (const key of Object.keys(this.primaryKeySchema.properties)) {
|
|
@@ -383,6 +419,61 @@ import { createServiceToken as createServiceToken3, getLogger } from "@workglow/
|
|
|
383
419
|
|
|
384
420
|
// src/tabular/InMemoryTabularStorage.ts
|
|
385
421
|
import { createServiceToken as createServiceToken2, makeFingerprint as makeFingerprint2, uuid4 } from "@workglow/util";
|
|
422
|
+
|
|
423
|
+
// src/tabular/coveringIndexPicker.ts
|
|
424
|
+
function pickCoveringIndex(input) {
|
|
425
|
+
const { table, indexes, criteriaColumns, orderByColumns, selectColumns, primaryKeyColumns } = input;
|
|
426
|
+
const required = uniqueColumns([
|
|
427
|
+
...criteriaColumns,
|
|
428
|
+
...orderByColumns.map((o) => o.column),
|
|
429
|
+
...selectColumns
|
|
430
|
+
]);
|
|
431
|
+
const pkSet = new Set(primaryKeyColumns);
|
|
432
|
+
for (const idx of indexes) {
|
|
433
|
+
const fit = tryFitIndex(idx, criteriaColumns, orderByColumns, selectColumns, pkSet);
|
|
434
|
+
if (fit !== undefined) {
|
|
435
|
+
return { name: idx.name, keyPath: idx.keyPath, reverseDirection: fit.reverseDirection };
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
throw new CoveringIndexMissingError(table, required, indexes.map((i) => i.keyPath));
|
|
439
|
+
}
|
|
440
|
+
function tryFitIndex(index, criteria, orderBy, select, pkSet) {
|
|
441
|
+
const keyPath = index.keyPath;
|
|
442
|
+
const criteriaSet = new Set(criteria);
|
|
443
|
+
if (criteria.length > keyPath.length)
|
|
444
|
+
return;
|
|
445
|
+
for (let i = 0;i < criteria.length; i++) {
|
|
446
|
+
if (!criteriaSet.has(keyPath[i]))
|
|
447
|
+
return;
|
|
448
|
+
}
|
|
449
|
+
let reverseDirection = false;
|
|
450
|
+
if (orderBy.length > 0) {
|
|
451
|
+
const start = criteria.length;
|
|
452
|
+
if (start + orderBy.length > keyPath.length)
|
|
453
|
+
return;
|
|
454
|
+
for (let i = 0;i < orderBy.length; i++) {
|
|
455
|
+
if (keyPath[start + i] !== orderBy[i].column)
|
|
456
|
+
return;
|
|
457
|
+
}
|
|
458
|
+
const allDesc = orderBy.every((o) => o.direction === "DESC");
|
|
459
|
+
const allAsc = orderBy.every((o) => o.direction === "ASC");
|
|
460
|
+
if (allDesc)
|
|
461
|
+
reverseDirection = true;
|
|
462
|
+
else if (!allAsc)
|
|
463
|
+
return;
|
|
464
|
+
}
|
|
465
|
+
const keyPathSet = new Set(keyPath);
|
|
466
|
+
for (const col of select) {
|
|
467
|
+
if (!keyPathSet.has(col) && !pkSet.has(col))
|
|
468
|
+
return;
|
|
469
|
+
}
|
|
470
|
+
return { reverseDirection };
|
|
471
|
+
}
|
|
472
|
+
function uniqueColumns(cols) {
|
|
473
|
+
return Array.from(new Set(cols));
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
// src/tabular/InMemoryTabularStorage.ts
|
|
386
477
|
var MEMORY_TABULAR_REPOSITORY = createServiceToken2("storage.tabularRepository.inMemory");
|
|
387
478
|
|
|
388
479
|
class InMemoryTabularStorage extends BaseTabularStorage {
|
|
@@ -625,6 +716,38 @@ class InMemoryTabularStorage extends BaseTabularStorage {
|
|
|
625
716
|
this.events.emit("query", criteria, result);
|
|
626
717
|
return result;
|
|
627
718
|
}
|
|
719
|
+
async queryIndex(criteria, options) {
|
|
720
|
+
this.validateSelect(options);
|
|
721
|
+
this.validateQueryParams(criteria, options);
|
|
722
|
+
const registered = this.indexes.map((cols, i) => ({
|
|
723
|
+
name: `idx_${i}`,
|
|
724
|
+
keyPath: cols
|
|
725
|
+
}));
|
|
726
|
+
pickCoveringIndex({
|
|
727
|
+
table: "InMemoryTabularStorage",
|
|
728
|
+
indexes: registered,
|
|
729
|
+
criteriaColumns: Object.keys(criteria),
|
|
730
|
+
orderByColumns: (options.orderBy ?? []).map((o) => ({
|
|
731
|
+
column: String(o.column),
|
|
732
|
+
direction: o.direction
|
|
733
|
+
})),
|
|
734
|
+
selectColumns: options.select.map(String),
|
|
735
|
+
primaryKeyColumns: this.primaryKeyNames.map(String)
|
|
736
|
+
});
|
|
737
|
+
const full = await this.query(criteria, {
|
|
738
|
+
orderBy: options.orderBy,
|
|
739
|
+
limit: options.limit,
|
|
740
|
+
offset: options.offset
|
|
741
|
+
});
|
|
742
|
+
if (!full)
|
|
743
|
+
return [];
|
|
744
|
+
return full.map((row) => {
|
|
745
|
+
const out = {};
|
|
746
|
+
for (const k of options.select)
|
|
747
|
+
out[k] = row[k];
|
|
748
|
+
return out;
|
|
749
|
+
});
|
|
750
|
+
}
|
|
628
751
|
subscribeToChanges(callback, options) {
|
|
629
752
|
const handlePut = (entity) => {
|
|
630
753
|
callback({ type: this._lastPutWasInsert ? "INSERT" : "UPDATE", new: entity });
|
|
@@ -767,6 +890,10 @@ class CachedTabularStorage extends BaseTabularStorage {
|
|
|
767
890
|
await this.initializeCache();
|
|
768
891
|
return await this.cache.query(criteria, options);
|
|
769
892
|
}
|
|
893
|
+
async queryIndex(criteria, options) {
|
|
894
|
+
await this.initializeCache();
|
|
895
|
+
return await this.cache.queryIndex(criteria, options);
|
|
896
|
+
}
|
|
770
897
|
async deleteSearch(criteria) {
|
|
771
898
|
await this.initializeCache();
|
|
772
899
|
await this.durable.deleteSearch(criteria);
|
|
@@ -1207,6 +1334,9 @@ class TelemetryTabularStorage {
|
|
|
1207
1334
|
query(criteria, options) {
|
|
1208
1335
|
return traced("workglow.storage.tabular.query", this.storageName, () => this.inner.query(criteria, options));
|
|
1209
1336
|
}
|
|
1337
|
+
queryIndex(criteria, options) {
|
|
1338
|
+
return traced("workglow.storage.tabular.queryIndex", this.storageName, () => this.inner.queryIndex(criteria, options));
|
|
1339
|
+
}
|
|
1210
1340
|
records(pageSize) {
|
|
1211
1341
|
return this.inner.records(pageSize);
|
|
1212
1342
|
}
|
|
@@ -3196,6 +3326,105 @@ class IndexedDbTabularStorage extends BaseTabularStorage {
|
|
|
3196
3326
|
request.onerror = () => reject(request.error);
|
|
3197
3327
|
});
|
|
3198
3328
|
}
|
|
3329
|
+
async queryIndex(criteria, options) {
|
|
3330
|
+
this.validateSelect(options);
|
|
3331
|
+
this.validateQueryParams(criteria, options);
|
|
3332
|
+
const registered = this.indexes.map((cols) => {
|
|
3333
|
+
const cs = Array.isArray(cols) ? cols : [cols];
|
|
3334
|
+
return { name: cs.join("_"), keyPath: cs };
|
|
3335
|
+
});
|
|
3336
|
+
const picked = pickCoveringIndex({
|
|
3337
|
+
table: this.table,
|
|
3338
|
+
indexes: registered,
|
|
3339
|
+
criteriaColumns: Object.keys(criteria),
|
|
3340
|
+
orderByColumns: (options.orderBy ?? []).map((o) => ({
|
|
3341
|
+
column: String(o.column),
|
|
3342
|
+
direction: o.direction
|
|
3343
|
+
})),
|
|
3344
|
+
selectColumns: options.select.map(String),
|
|
3345
|
+
primaryKeyColumns: this.primaryKeyColumns().map(String)
|
|
3346
|
+
});
|
|
3347
|
+
const db = await this.getDb();
|
|
3348
|
+
return new Promise((resolve, reject) => {
|
|
3349
|
+
const tx = db.transaction(this.table, "readonly");
|
|
3350
|
+
const store = tx.objectStore(this.table);
|
|
3351
|
+
const idx = store.index(picked.name);
|
|
3352
|
+
const prefix = [];
|
|
3353
|
+
for (const col of picked.keyPath) {
|
|
3354
|
+
const c = criteria[col];
|
|
3355
|
+
if (c === undefined && !(col in criteria))
|
|
3356
|
+
break;
|
|
3357
|
+
if (isSearchCondition(c)) {
|
|
3358
|
+
if (c.operator !== "=")
|
|
3359
|
+
break;
|
|
3360
|
+
prefix.push(c.value);
|
|
3361
|
+
} else {
|
|
3362
|
+
prefix.push(c);
|
|
3363
|
+
}
|
|
3364
|
+
}
|
|
3365
|
+
const range = prefix.length === 0 ? undefined : prefix.length === picked.keyPath.length ? IDBKeyRange.only(prefix.length === 1 ? prefix[0] : prefix) : IDBKeyRange.bound(prefix, [...prefix, []]);
|
|
3366
|
+
const direction = picked.reverseDirection ? "prev" : "next";
|
|
3367
|
+
const request = idx.openKeyCursor(range, direction);
|
|
3368
|
+
const out = [];
|
|
3369
|
+
let toSkip = options.offset ?? 0;
|
|
3370
|
+
const keyPathPositions = new Map;
|
|
3371
|
+
picked.keyPath.forEach((col, i) => keyPathPositions.set(col, i));
|
|
3372
|
+
const pkCols = this.primaryKeyColumns().map(String);
|
|
3373
|
+
const pkPositions = new Map;
|
|
3374
|
+
pkCols.forEach((col, i) => pkPositions.set(col, i));
|
|
3375
|
+
request.onsuccess = () => {
|
|
3376
|
+
const cursor = request.result;
|
|
3377
|
+
if (!cursor) {
|
|
3378
|
+
resolve(out);
|
|
3379
|
+
return;
|
|
3380
|
+
}
|
|
3381
|
+
const key = cursor.key;
|
|
3382
|
+
const row = {};
|
|
3383
|
+
for (const col of options.select) {
|
|
3384
|
+
const colStr = String(col);
|
|
3385
|
+
const pos = keyPathPositions.get(colStr);
|
|
3386
|
+
if (pos !== undefined) {
|
|
3387
|
+
row[colStr] = Array.isArray(key) ? key[pos] : key;
|
|
3388
|
+
} else {
|
|
3389
|
+
if (pkCols.length === 1 && colStr === pkCols[0]) {
|
|
3390
|
+
row[colStr] = cursor.primaryKey;
|
|
3391
|
+
} else {
|
|
3392
|
+
const pkPos = pkPositions.get(colStr);
|
|
3393
|
+
if (pkPos !== undefined) {
|
|
3394
|
+
row[colStr] = Array.isArray(cursor.primaryKey) ? cursor.primaryKey[pkPos] : cursor.primaryKey;
|
|
3395
|
+
}
|
|
3396
|
+
}
|
|
3397
|
+
}
|
|
3398
|
+
}
|
|
3399
|
+
let matches = true;
|
|
3400
|
+
for (const [col, crit] of Object.entries(criteria)) {
|
|
3401
|
+
if (!isSearchCondition(crit))
|
|
3402
|
+
continue;
|
|
3403
|
+
const pos = keyPathPositions.get(col);
|
|
3404
|
+
if (pos === undefined)
|
|
3405
|
+
continue;
|
|
3406
|
+
const valFromKey = Array.isArray(key) ? key[pos] : key;
|
|
3407
|
+
if (!compareWithOperator(valFromKey, crit.operator, crit.value)) {
|
|
3408
|
+
matches = false;
|
|
3409
|
+
break;
|
|
3410
|
+
}
|
|
3411
|
+
}
|
|
3412
|
+
if (matches) {
|
|
3413
|
+
if (toSkip > 0) {
|
|
3414
|
+
toSkip -= 1;
|
|
3415
|
+
} else {
|
|
3416
|
+
out.push(row);
|
|
3417
|
+
if (options.limit !== undefined && out.length >= options.limit) {
|
|
3418
|
+
resolve(out);
|
|
3419
|
+
return;
|
|
3420
|
+
}
|
|
3421
|
+
}
|
|
3422
|
+
}
|
|
3423
|
+
cursor.continue();
|
|
3424
|
+
};
|
|
3425
|
+
request.onerror = () => reject(request.error);
|
|
3426
|
+
});
|
|
3427
|
+
}
|
|
3199
3428
|
getHybridManager() {
|
|
3200
3429
|
if (!this.hybridManager) {
|
|
3201
3430
|
const channelName = `indexeddb-tabular-${this.table}`;
|
|
@@ -3233,6 +3462,22 @@ class IndexedDbTabularStorage extends BaseTabularStorage {
|
|
|
3233
3462
|
this.db?.close();
|
|
3234
3463
|
}
|
|
3235
3464
|
}
|
|
3465
|
+
function compareWithOperator(a, op, b) {
|
|
3466
|
+
const av = a;
|
|
3467
|
+
const bv = b;
|
|
3468
|
+
switch (op) {
|
|
3469
|
+
case "=":
|
|
3470
|
+
return av === bv;
|
|
3471
|
+
case "<":
|
|
3472
|
+
return av !== null && av !== undefined && av < bv;
|
|
3473
|
+
case "<=":
|
|
3474
|
+
return av !== null && av !== undefined && av <= bv;
|
|
3475
|
+
case ">":
|
|
3476
|
+
return av !== null && av !== undefined && av > bv;
|
|
3477
|
+
case ">=":
|
|
3478
|
+
return av !== null && av !== undefined && av >= bv;
|
|
3479
|
+
}
|
|
3480
|
+
}
|
|
3236
3481
|
// src/tabular/SharedInMemoryTabularStorage.ts
|
|
3237
3482
|
import { createServiceToken as createServiceToken13 } from "@workglow/util";
|
|
3238
3483
|
var SHARED_IN_MEMORY_TABULAR_REPOSITORY = createServiceToken13("storage.tabularRepository.sharedInMemory");
|
|
@@ -4093,6 +4338,56 @@ class SupabaseTabularStorage extends BaseSqlTabularStorage {
|
|
|
4093
4338
|
this.events.emit("query", criteria, undefined);
|
|
4094
4339
|
return;
|
|
4095
4340
|
}
|
|
4341
|
+
async queryIndex(criteria, options) {
|
|
4342
|
+
this.validateSelect(options);
|
|
4343
|
+
this.validateQueryParams(criteria, options);
|
|
4344
|
+
const registered = this.indexes.map((cols, i) => {
|
|
4345
|
+
const cs = Array.isArray(cols) ? cols : [cols];
|
|
4346
|
+
return { name: `idx_${i}`, keyPath: cs };
|
|
4347
|
+
});
|
|
4348
|
+
pickCoveringIndex({
|
|
4349
|
+
table: this.table,
|
|
4350
|
+
indexes: registered,
|
|
4351
|
+
criteriaColumns: Object.keys(criteria),
|
|
4352
|
+
orderByColumns: (options.orderBy ?? []).map((o) => ({
|
|
4353
|
+
column: String(o.column),
|
|
4354
|
+
direction: o.direction
|
|
4355
|
+
})),
|
|
4356
|
+
selectColumns: options.select.map(String),
|
|
4357
|
+
primaryKeyColumns: this.primaryKeyNames.map(String)
|
|
4358
|
+
});
|
|
4359
|
+
const colList = options.select.map(String).join(",");
|
|
4360
|
+
let q = this.applyCriteriaToFilter(this.client.from(this.table).select(colList), criteria);
|
|
4361
|
+
if (options.orderBy) {
|
|
4362
|
+
for (const { column, direction } of options.orderBy) {
|
|
4363
|
+
q = q.order(String(column), { ascending: direction === "ASC" });
|
|
4364
|
+
}
|
|
4365
|
+
}
|
|
4366
|
+
if (options.offset !== undefined && options.limit === undefined) {
|
|
4367
|
+
throw new StorageValidationError("queryIndex with offset requires limit (no implicit cap)");
|
|
4368
|
+
}
|
|
4369
|
+
if (options.offset !== undefined || options.limit !== undefined) {
|
|
4370
|
+
const start = options.offset ?? 0;
|
|
4371
|
+
if (options.limit !== undefined) {
|
|
4372
|
+
q = q.range(start, start + options.limit - 1);
|
|
4373
|
+
}
|
|
4374
|
+
}
|
|
4375
|
+
const { data, error } = await q;
|
|
4376
|
+
if (error)
|
|
4377
|
+
throw error;
|
|
4378
|
+
if (!data)
|
|
4379
|
+
return [];
|
|
4380
|
+
const rows = data;
|
|
4381
|
+
const sel = new Set(options.select.map(String));
|
|
4382
|
+
for (const row of rows) {
|
|
4383
|
+
for (const key of Object.keys(row)) {
|
|
4384
|
+
if (sel.has(key)) {
|
|
4385
|
+
row[key] = this.sqlToJsValue(key, row[key]);
|
|
4386
|
+
}
|
|
4387
|
+
}
|
|
4388
|
+
}
|
|
4389
|
+
return rows;
|
|
4390
|
+
}
|
|
4096
4391
|
convertRealtimeRow(row) {
|
|
4097
4392
|
const entity = { ...row };
|
|
4098
4393
|
const record = entity;
|
|
@@ -5739,6 +6034,7 @@ class IndexedDbVectorStorage extends IndexedDbTabularStorage {
|
|
|
5739
6034
|
export {
|
|
5740
6035
|
traced,
|
|
5741
6036
|
registerTabularRepository,
|
|
6037
|
+
pickCoveringIndex,
|
|
5742
6038
|
isSearchCondition,
|
|
5743
6039
|
getVectorProperty,
|
|
5744
6040
|
getTabularRepository,
|
|
@@ -5801,9 +6097,10 @@ export {
|
|
|
5801
6097
|
EncryptedKvCredentialStore,
|
|
5802
6098
|
DefaultKeyValueSchema,
|
|
5803
6099
|
DefaultKeyValueKey,
|
|
6100
|
+
CoveringIndexMissingError,
|
|
5804
6101
|
CachedTabularStorage,
|
|
5805
6102
|
CACHED_TABULAR_REPOSITORY,
|
|
5806
6103
|
BaseTabularStorage
|
|
5807
6104
|
};
|
|
5808
6105
|
|
|
5809
|
-
//# debugId=
|
|
6106
|
+
//# debugId=2583E6527A9527C764756E2164756E21
|