dexie 4.0.8 → 4.1.0-alpha.2
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/README.md +46 -0
- package/dist/dexie.d.ts +249 -37
- package/dist/dexie.js +807 -142
- package/dist/dexie.js.map +1 -1
- package/dist/dexie.min.js +1 -1
- package/dist/dexie.min.js.map +1 -1
- package/dist/dexie.mjs +806 -142
- package/dist/dexie.mjs.map +1 -1
- package/dist/modern/dexie.min.mjs +1 -1
- package/dist/modern/dexie.min.mjs.map +1 -1
- package/dist/modern/dexie.mjs +476 -54
- package/dist/modern/dexie.mjs.map +1 -1
- package/import-wrapper-prod.mjs +4 -2
- package/import-wrapper.mjs +5 -2
- package/package.json +6 -4
package/dist/modern/dexie.mjs
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*
|
|
5
5
|
* By David Fahlander, david.fahlander@gmail.com
|
|
6
6
|
*
|
|
7
|
-
* Version 4.0.
|
|
7
|
+
* Version 4.1.0-alpha.2, Fri Oct 04 2024
|
|
8
8
|
*
|
|
9
9
|
* https://dexie.org
|
|
10
10
|
*
|
|
@@ -1094,7 +1094,7 @@ function tempTransaction(db, mode, storeNames, fn) {
|
|
|
1094
1094
|
}
|
|
1095
1095
|
}
|
|
1096
1096
|
|
|
1097
|
-
const DEXIE_VERSION = '4.0.
|
|
1097
|
+
const DEXIE_VERSION = '4.1.0-alpha.2';
|
|
1098
1098
|
const maxString = String.fromCharCode(65535);
|
|
1099
1099
|
const minKey = -Infinity;
|
|
1100
1100
|
const INVALID_KEY_ARGUMENT = "Invalid key provided. Keys must be of type string, number, Date or Array<string | number | Date>.";
|
|
@@ -1212,6 +1212,89 @@ function getUint8Array(a) {
|
|
|
1212
1212
|
return new Uint8Array(a);
|
|
1213
1213
|
}
|
|
1214
1214
|
|
|
1215
|
+
function getDocCache(db) {
|
|
1216
|
+
return db._novip['_docCache'] ??= {
|
|
1217
|
+
cache: {},
|
|
1218
|
+
get size() {
|
|
1219
|
+
return Object.keys(this.cache).length;
|
|
1220
|
+
},
|
|
1221
|
+
find(table, primaryKey, ydocProp) {
|
|
1222
|
+
const cacheKey = getYDocCacheKey(table, primaryKey, ydocProp);
|
|
1223
|
+
const docRef = this.cache[cacheKey];
|
|
1224
|
+
return docRef ? docRef.deref() : undefined;
|
|
1225
|
+
},
|
|
1226
|
+
add(doc) {
|
|
1227
|
+
const { parentTable, parentId, parentProp } = doc.meta;
|
|
1228
|
+
if (!parentTable || !parentProp || parentId == null)
|
|
1229
|
+
throw new Error(`Missing Dexie-related metadata in Y.Doc`);
|
|
1230
|
+
const cacheKey = getYDocCacheKey(parentTable, parentId, parentProp);
|
|
1231
|
+
this.cache[cacheKey] = new WeakRef(doc);
|
|
1232
|
+
docRegistry.register(doc, { cache: this.cache, key: cacheKey });
|
|
1233
|
+
},
|
|
1234
|
+
delete(doc) {
|
|
1235
|
+
docRegistry.unregister(doc);
|
|
1236
|
+
delete this.cache[getYDocCacheKey(doc.meta.parentTable, doc.meta.parentId, doc.meta.parentProp)];
|
|
1237
|
+
},
|
|
1238
|
+
};
|
|
1239
|
+
}
|
|
1240
|
+
const docRegistry = new FinalizationRegistry(({ cache, key }) => {
|
|
1241
|
+
delete cache[key];
|
|
1242
|
+
});
|
|
1243
|
+
const destroyedDocs = new WeakSet();
|
|
1244
|
+
function throwIfDestroyed(doc) {
|
|
1245
|
+
if (destroyedDocs.has(doc))
|
|
1246
|
+
throw new Error('Y.Doc has been destroyed');
|
|
1247
|
+
}
|
|
1248
|
+
function getYDocCacheKey(table, primaryKey, ydocProp) {
|
|
1249
|
+
return `${table}[${primaryKey}].${ydocProp}`;
|
|
1250
|
+
}
|
|
1251
|
+
|
|
1252
|
+
function createYDocProperty(db, Y, table, prop, updatesTable) {
|
|
1253
|
+
const pkKeyPath = table.schema.primKey.keyPath;
|
|
1254
|
+
const docCache = getDocCache(db);
|
|
1255
|
+
return {
|
|
1256
|
+
get() {
|
|
1257
|
+
const id = getByKeyPath(this, pkKeyPath);
|
|
1258
|
+
let doc = docCache.find(table.name, id, prop);
|
|
1259
|
+
if (doc)
|
|
1260
|
+
return doc;
|
|
1261
|
+
doc = new Y.Doc({
|
|
1262
|
+
meta: {
|
|
1263
|
+
db,
|
|
1264
|
+
updatesTable,
|
|
1265
|
+
parentProp: prop,
|
|
1266
|
+
parentTable: table.name,
|
|
1267
|
+
parentId: id
|
|
1268
|
+
},
|
|
1269
|
+
});
|
|
1270
|
+
docCache.add(doc);
|
|
1271
|
+
doc.on('destroy', () => {
|
|
1272
|
+
destroyedDocs.add(doc);
|
|
1273
|
+
docCache.delete(doc);
|
|
1274
|
+
});
|
|
1275
|
+
return doc;
|
|
1276
|
+
},
|
|
1277
|
+
};
|
|
1278
|
+
}
|
|
1279
|
+
|
|
1280
|
+
function builtInDeletionTrigger(table, keys, res) {
|
|
1281
|
+
const { yProps } = table.schema;
|
|
1282
|
+
if (!yProps)
|
|
1283
|
+
return res;
|
|
1284
|
+
if (keys && res.numFailures > 0)
|
|
1285
|
+
keys = keys.filter((_, i) => !res.failures[i]);
|
|
1286
|
+
return Promise.all(yProps.map(({ updatesTable }) => keys
|
|
1287
|
+
? table.db.table(updatesTable).where('k').anyOf(keys).delete()
|
|
1288
|
+
: table.db.table(updatesTable).clear())).then(() => res);
|
|
1289
|
+
}
|
|
1290
|
+
|
|
1291
|
+
function getYLibrary(db) {
|
|
1292
|
+
const Y = db._options.Y;
|
|
1293
|
+
if (!Y)
|
|
1294
|
+
throw new exceptions.MissingAPI('Y library not supplied to Dexie constructor');
|
|
1295
|
+
return Y;
|
|
1296
|
+
}
|
|
1297
|
+
|
|
1215
1298
|
class Table {
|
|
1216
1299
|
_trans(mode, fn, writeLocked) {
|
|
1217
1300
|
const trans = this._tx || PSD.trans;
|
|
@@ -1347,6 +1430,14 @@ class Table {
|
|
|
1347
1430
|
table() { return tableName; }
|
|
1348
1431
|
};
|
|
1349
1432
|
}
|
|
1433
|
+
if (this.schema.yProps) {
|
|
1434
|
+
const Y = getYLibrary(db);
|
|
1435
|
+
constructor = class extends constructor {
|
|
1436
|
+
};
|
|
1437
|
+
this.schema.yProps.forEach(({ prop, updatesTable }) => {
|
|
1438
|
+
Object.defineProperty(constructor.prototype, prop, createYDocProperty(db, Y, this, prop, updatesTable));
|
|
1439
|
+
});
|
|
1440
|
+
}
|
|
1350
1441
|
const inheritedProps = new Set();
|
|
1351
1442
|
for (let proto = constructor.prototype; proto; proto = getProto(proto)) {
|
|
1352
1443
|
Object.getOwnPropertyNames(proto).forEach(propName => inheritedProps.add(propName));
|
|
@@ -1425,11 +1516,13 @@ class Table {
|
|
|
1425
1516
|
});
|
|
1426
1517
|
}
|
|
1427
1518
|
delete(key) {
|
|
1428
|
-
return this._trans('readwrite', trans => this.core.mutate({ trans, type: 'delete', keys: [key] })
|
|
1429
|
-
.then(res =>
|
|
1519
|
+
return this._trans('readwrite', trans => this.core.mutate({ trans, type: 'delete', keys: [key] })
|
|
1520
|
+
.then(res => builtInDeletionTrigger(this, [key], res))
|
|
1521
|
+
.then(res => res.numFailures ? DexiePromise.reject(res.failures[0]) : undefined));
|
|
1430
1522
|
}
|
|
1431
1523
|
clear() {
|
|
1432
|
-
return this._trans('readwrite', trans => this.core.mutate({ trans, type: 'deleteRange', range: AnyRange })
|
|
1524
|
+
return this._trans('readwrite', trans => this.core.mutate({ trans, type: 'deleteRange', range: AnyRange })
|
|
1525
|
+
.then(res => builtInDeletionTrigger(this, null, res)))
|
|
1433
1526
|
.then(res => res.numFailures ? DexiePromise.reject(res.failures[0]) : undefined);
|
|
1434
1527
|
}
|
|
1435
1528
|
bulkGet(keys) {
|
|
@@ -1545,7 +1638,8 @@ class Table {
|
|
|
1545
1638
|
bulkDelete(keys) {
|
|
1546
1639
|
const numKeys = keys.length;
|
|
1547
1640
|
return this._trans('readwrite', trans => {
|
|
1548
|
-
return this.core.mutate({ trans, type: 'delete', keys: keys })
|
|
1641
|
+
return this.core.mutate({ trans, type: 'delete', keys: keys })
|
|
1642
|
+
.then(res => builtInDeletionTrigger(this, keys, res));
|
|
1549
1643
|
}).then(({ numFailures, lastResult, failures }) => {
|
|
1550
1644
|
if (numFailures === 0)
|
|
1551
1645
|
return lastResult;
|
|
@@ -2053,45 +2147,48 @@ class Collection {
|
|
|
2053
2147
|
totalFailures.push(failures[pos]);
|
|
2054
2148
|
}
|
|
2055
2149
|
};
|
|
2150
|
+
const isUnconditionalDelete = changes === deleteCallback;
|
|
2056
2151
|
return this.clone().primaryKeys().then(keys => {
|
|
2057
2152
|
const criteria = isPlainKeyRange(ctx) &&
|
|
2058
2153
|
ctx.limit === Infinity &&
|
|
2059
|
-
(typeof changes !== 'function' ||
|
|
2154
|
+
(typeof changes !== 'function' || isUnconditionalDelete) && {
|
|
2060
2155
|
index: ctx.index,
|
|
2061
2156
|
range: ctx.range
|
|
2062
2157
|
};
|
|
2063
2158
|
const nextChunk = (offset) => {
|
|
2064
2159
|
const count = Math.min(limit, keys.length - offset);
|
|
2065
|
-
|
|
2160
|
+
const keysInChunk = keys.slice(offset, offset + count);
|
|
2161
|
+
return (isUnconditionalDelete ? Promise.resolve([]) : coreTable.getMany({
|
|
2066
2162
|
trans,
|
|
2067
|
-
keys:
|
|
2163
|
+
keys: keysInChunk,
|
|
2068
2164
|
cache: "immutable"
|
|
2069
|
-
}).then(values => {
|
|
2165
|
+
})).then(values => {
|
|
2070
2166
|
const addValues = [];
|
|
2071
2167
|
const putValues = [];
|
|
2072
2168
|
const putKeys = outbound ? [] : null;
|
|
2073
|
-
const deleteKeys = [];
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
if (ctx.value
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2169
|
+
const deleteKeys = isUnconditionalDelete ? keysInChunk : [];
|
|
2170
|
+
if (!isUnconditionalDelete)
|
|
2171
|
+
for (let i = 0; i < count; ++i) {
|
|
2172
|
+
const origValue = values[i];
|
|
2173
|
+
const ctx = {
|
|
2174
|
+
value: deepClone(origValue),
|
|
2175
|
+
primKey: keys[offset + i]
|
|
2176
|
+
};
|
|
2177
|
+
if (modifyer.call(ctx, ctx.value, ctx) !== false) {
|
|
2178
|
+
if (ctx.value == null) {
|
|
2179
|
+
deleteKeys.push(keys[offset + i]);
|
|
2180
|
+
}
|
|
2181
|
+
else if (!outbound && cmp(extractKey(origValue), extractKey(ctx.value)) !== 0) {
|
|
2182
|
+
deleteKeys.push(keys[offset + i]);
|
|
2183
|
+
addValues.push(ctx.value);
|
|
2184
|
+
}
|
|
2185
|
+
else {
|
|
2186
|
+
putValues.push(ctx.value);
|
|
2187
|
+
if (outbound)
|
|
2188
|
+
putKeys.push(keys[offset + i]);
|
|
2189
|
+
}
|
|
2092
2190
|
}
|
|
2093
2191
|
}
|
|
2094
|
-
}
|
|
2095
2192
|
return Promise.resolve(addValues.length > 0 &&
|
|
2096
2193
|
coreTable.mutate({ trans, type: 'add', values: addValues })
|
|
2097
2194
|
.then(res => {
|
|
@@ -2109,14 +2206,15 @@ class Collection {
|
|
|
2109
2206
|
changeSpec: typeof changes !== 'function'
|
|
2110
2207
|
&& changes,
|
|
2111
2208
|
isAdditionalChunk: offset > 0
|
|
2112
|
-
}).then(res => applyMutateResult(putValues.length, res))).then(() => (deleteKeys.length > 0 || (criteria &&
|
|
2209
|
+
}).then(res => applyMutateResult(putValues.length, res))).then(() => (deleteKeys.length > 0 || (criteria && isUnconditionalDelete)) &&
|
|
2113
2210
|
coreTable.mutate({
|
|
2114
2211
|
trans,
|
|
2115
2212
|
type: 'delete',
|
|
2116
2213
|
keys: deleteKeys,
|
|
2117
2214
|
criteria,
|
|
2118
2215
|
isAdditionalChunk: offset > 0
|
|
2119
|
-
}).then(res =>
|
|
2216
|
+
}).then(res => builtInDeletionTrigger(ctx.table, deleteKeys, res))
|
|
2217
|
+
.then(res => applyMutateResult(deleteKeys.length, res))).then(() => {
|
|
2120
2218
|
return keys.length > offset + count && nextChunk(offset + limit);
|
|
2121
2219
|
});
|
|
2122
2220
|
});
|
|
@@ -2132,6 +2230,7 @@ class Collection {
|
|
|
2132
2230
|
delete() {
|
|
2133
2231
|
var ctx = this._ctx, range = ctx.range;
|
|
2134
2232
|
if (isPlainKeyRange(ctx) &&
|
|
2233
|
+
!ctx.table.schema.yProps &&
|
|
2135
2234
|
(ctx.isPrimKey || range.type === 3 ))
|
|
2136
2235
|
{
|
|
2137
2236
|
return this._write(trans => {
|
|
@@ -2139,7 +2238,7 @@ class Collection {
|
|
|
2139
2238
|
const coreRange = range;
|
|
2140
2239
|
return ctx.table.core.count({ trans, query: { index: primaryKey, range: coreRange } }).then(count => {
|
|
2141
2240
|
return ctx.table.core.mutate({ trans, type: 'deleteRange', range: coreRange })
|
|
2142
|
-
.then(({ failures,
|
|
2241
|
+
.then(({ failures, numFailures }) => {
|
|
2143
2242
|
if (numFailures)
|
|
2144
2243
|
throw new ModifyError("Could not delete some values", Object.keys(failures).map(pos => failures[pos]), count - numFailures);
|
|
2145
2244
|
return count - numFailures;
|
|
@@ -2723,6 +2822,12 @@ class Transaction {
|
|
|
2723
2822
|
|
|
2724
2823
|
function createTransactionConstructor(db) {
|
|
2725
2824
|
return makeClassConstructor(Transaction.prototype, function Transaction(mode, storeNames, dbschema, chromeTransactionDurability, parent) {
|
|
2825
|
+
if (mode !== 'readonly')
|
|
2826
|
+
storeNames.forEach(storeName => {
|
|
2827
|
+
const yProps = dbschema[storeName]?.yProps;
|
|
2828
|
+
if (yProps)
|
|
2829
|
+
storeNames = storeNames.concat(yProps.map(p => p.updatesTable));
|
|
2830
|
+
});
|
|
2726
2831
|
this.db = db;
|
|
2727
2832
|
this.mode = mode;
|
|
2728
2833
|
this.storeNames = storeNames;
|
|
@@ -2758,7 +2863,7 @@ function createTransactionConstructor(db) {
|
|
|
2758
2863
|
});
|
|
2759
2864
|
}
|
|
2760
2865
|
|
|
2761
|
-
function createIndexSpec(name, keyPath, unique, multi, auto, compound, isPrimKey) {
|
|
2866
|
+
function createIndexSpec(name, keyPath, unique, multi, auto, compound, isPrimKey, type) {
|
|
2762
2867
|
return {
|
|
2763
2868
|
name,
|
|
2764
2869
|
keyPath,
|
|
@@ -2766,7 +2871,8 @@ function createIndexSpec(name, keyPath, unique, multi, auto, compound, isPrimKey
|
|
|
2766
2871
|
multi,
|
|
2767
2872
|
auto,
|
|
2768
2873
|
compound,
|
|
2769
|
-
src: (unique && !isPrimKey ? '&' : '') + (multi ? '*' : '') + (auto ? "++" : "") + nameFromKeyPath(keyPath)
|
|
2874
|
+
src: (unique && !isPrimKey ? '&' : '') + (multi ? '*' : '') + (auto ? "++" : "") + nameFromKeyPath(keyPath),
|
|
2875
|
+
type
|
|
2770
2876
|
};
|
|
2771
2877
|
}
|
|
2772
2878
|
function nameFromKeyPath(keyPath) {
|
|
@@ -2775,13 +2881,17 @@ function nameFromKeyPath(keyPath) {
|
|
|
2775
2881
|
keyPath ? ('[' + [].join.call(keyPath, '+') + ']') : "";
|
|
2776
2882
|
}
|
|
2777
2883
|
|
|
2778
|
-
function createTableSchema(name, primKey, indexes) {
|
|
2884
|
+
function createTableSchema(name, primKey, indexes, yProps) {
|
|
2779
2885
|
return {
|
|
2780
2886
|
name,
|
|
2781
2887
|
primKey,
|
|
2782
2888
|
indexes,
|
|
2783
2889
|
mappedClass: null,
|
|
2784
|
-
|
|
2890
|
+
yProps: yProps?.map((prop) => ({
|
|
2891
|
+
prop,
|
|
2892
|
+
updatesTable: `$${name}.${prop}_updates`,
|
|
2893
|
+
})),
|
|
2894
|
+
idxByName: arrayToObject(indexes, (index) => [index.name, index]),
|
|
2785
2895
|
};
|
|
2786
2896
|
}
|
|
2787
2897
|
|
|
@@ -3507,49 +3617,68 @@ function adjustToExistingIndexNames(db, schema, idbtrans) {
|
|
|
3507
3617
|
}
|
|
3508
3618
|
function parseIndexSyntax(primKeyAndIndexes) {
|
|
3509
3619
|
return primKeyAndIndexes.split(',').map((index, indexNum) => {
|
|
3510
|
-
|
|
3620
|
+
const typeSplit = index.split(':');
|
|
3621
|
+
const type = typeSplit[1]?.trim();
|
|
3622
|
+
index = typeSplit[0].trim();
|
|
3623
|
+
if (type && type !== 'Y')
|
|
3624
|
+
throw new exceptions.Schema(`Unsupported type '${type}'`);
|
|
3511
3625
|
const name = index.replace(/([&*]|\+\+)/g, "");
|
|
3512
3626
|
const keyPath = /^\[/.test(name) ? name.match(/^\[(.*)\]$/)[1].split('+') : name;
|
|
3513
|
-
return createIndexSpec(name, keyPath || null, /\&/.test(index), /\*/.test(index), /\+\+/.test(index), isArray(keyPath), indexNum === 0);
|
|
3627
|
+
return createIndexSpec(name, keyPath || null, /\&/.test(index), /\*/.test(index), /\+\+/.test(index), isArray(keyPath), indexNum === 0, type);
|
|
3514
3628
|
});
|
|
3515
3629
|
}
|
|
3516
3630
|
|
|
3517
3631
|
class Version {
|
|
3518
3632
|
_parseStoresSpec(stores, outSchema) {
|
|
3519
|
-
keys(stores).forEach(tableName => {
|
|
3633
|
+
keys(stores).forEach((tableName) => {
|
|
3520
3634
|
if (stores[tableName] !== null) {
|
|
3521
|
-
|
|
3522
|
-
|
|
3635
|
+
let indexes = parseIndexSyntax(stores[tableName]);
|
|
3636
|
+
const yProps = indexes.filter((idx) => idx.type === 'Y').map((idx) => idx.name);
|
|
3637
|
+
indexes = indexes.filter((idx) => idx.type !== 'Y');
|
|
3638
|
+
const primKey = indexes.shift();
|
|
3639
|
+
if (!primKey) {
|
|
3640
|
+
throw new exceptions.Schema('Invalid schema for table ' + tableName + ': ' + stores[tableName]);
|
|
3641
|
+
}
|
|
3523
3642
|
primKey.unique = true;
|
|
3524
3643
|
if (primKey.multi)
|
|
3525
|
-
throw new exceptions.Schema(
|
|
3526
|
-
indexes.forEach(idx => {
|
|
3644
|
+
throw new exceptions.Schema('Primary key cannot be multiEntry*');
|
|
3645
|
+
indexes.forEach((idx) => {
|
|
3527
3646
|
if (idx.auto)
|
|
3528
|
-
throw new exceptions.Schema(
|
|
3647
|
+
throw new exceptions.Schema('Only primary key can be marked as autoIncrement (++)');
|
|
3529
3648
|
if (!idx.keyPath)
|
|
3530
|
-
throw new exceptions.Schema(
|
|
3649
|
+
throw new exceptions.Schema('Index must have a name and cannot be an empty string');
|
|
3531
3650
|
});
|
|
3532
|
-
|
|
3651
|
+
const tblSchema = createTableSchema(tableName, primKey, indexes, yProps.length ? yProps : undefined);
|
|
3652
|
+
outSchema[tableName] = tblSchema;
|
|
3653
|
+
for (const yProp of tblSchema.yProps || []) {
|
|
3654
|
+
this._parseStoresSpec(
|
|
3655
|
+
{ [yProp.updatesTable]: '++i,k' }, outSchema);
|
|
3656
|
+
}
|
|
3533
3657
|
}
|
|
3534
3658
|
});
|
|
3535
3659
|
}
|
|
3536
3660
|
stores(stores) {
|
|
3537
3661
|
const db = this.db;
|
|
3538
|
-
this._cfg.storesSource = this._cfg.storesSource
|
|
3539
|
-
extend(this._cfg.storesSource, stores)
|
|
3540
|
-
stores;
|
|
3662
|
+
this._cfg.storesSource = this._cfg.storesSource
|
|
3663
|
+
? extend(this._cfg.storesSource, stores)
|
|
3664
|
+
: stores;
|
|
3541
3665
|
const versions = db._versions;
|
|
3542
3666
|
const storesSpec = {};
|
|
3543
3667
|
let dbschema = {};
|
|
3544
|
-
versions.forEach(version => {
|
|
3668
|
+
versions.forEach((version) => {
|
|
3545
3669
|
extend(storesSpec, version._cfg.storesSource);
|
|
3546
|
-
dbschema =
|
|
3670
|
+
dbschema = version._cfg.dbschema = {};
|
|
3547
3671
|
version._parseStoresSpec(storesSpec, dbschema);
|
|
3548
3672
|
});
|
|
3549
3673
|
db._dbSchema = dbschema;
|
|
3550
3674
|
removeTablesApi(db, [db._allTables, db, db.Transaction.prototype]);
|
|
3551
3675
|
setApiOnPlace(db, [db._allTables, db, db.Transaction.prototype, this._cfg.tables], keys(dbschema), dbschema);
|
|
3552
3676
|
db._storeNames = keys(dbschema);
|
|
3677
|
+
db._storeNames.forEach((tableName) => {
|
|
3678
|
+
if (dbschema[tableName].yProps) {
|
|
3679
|
+
db.table(tableName).mapToClass(dbschema[tableName].mappedClass || Object);
|
|
3680
|
+
}
|
|
3681
|
+
});
|
|
3553
3682
|
return this;
|
|
3554
3683
|
}
|
|
3555
3684
|
upgrade(upgradeFunction) {
|
|
@@ -5119,6 +5248,124 @@ function vipify(target, vipDb) {
|
|
|
5119
5248
|
});
|
|
5120
5249
|
}
|
|
5121
5250
|
|
|
5251
|
+
function compressYDocs(db, interval) {
|
|
5252
|
+
let p = Promise.resolve();
|
|
5253
|
+
for (const table of db.tables) {
|
|
5254
|
+
for (const yProp of table.schema.yProps || []) {
|
|
5255
|
+
p = p.then(() => compressYDocsTable(db, yProp, interval));
|
|
5256
|
+
}
|
|
5257
|
+
}
|
|
5258
|
+
return p;
|
|
5259
|
+
}
|
|
5260
|
+
function compressYDocsTable(db, { updatesTable }, skipIfRunnedSince
|
|
5261
|
+
) {
|
|
5262
|
+
const updTbl = db.table(updatesTable);
|
|
5263
|
+
return Promise.all([
|
|
5264
|
+
updTbl
|
|
5265
|
+
.where('i')
|
|
5266
|
+
.startsWith('')
|
|
5267
|
+
.toArray(),
|
|
5268
|
+
db.transaction('rw', updatesTable, () => updTbl.get(0).then((lastCompressed) => {
|
|
5269
|
+
if (lastCompressed &&
|
|
5270
|
+
skipIfRunnedSince &&
|
|
5271
|
+
lastCompressed.lastRun.getTime() > Date.now() - skipIfRunnedSince) {
|
|
5272
|
+
return null;
|
|
5273
|
+
}
|
|
5274
|
+
lastCompressed = lastCompressed || { i: 0, lastCompressed: 0 };
|
|
5275
|
+
return updTbl
|
|
5276
|
+
.put({
|
|
5277
|
+
...lastCompressed,
|
|
5278
|
+
lastRun: new Date(),
|
|
5279
|
+
})
|
|
5280
|
+
.then(() => lastCompressed);
|
|
5281
|
+
})),
|
|
5282
|
+
]).then(([syncers, stamp]) => {
|
|
5283
|
+
if (!stamp)
|
|
5284
|
+
return;
|
|
5285
|
+
const lastCompressedUpdate = stamp.lastCompressed;
|
|
5286
|
+
const unsyncedFrom = Math.min(...syncers.map((s) => Math.min(s.unsentFrom || Infinity, s.receivedUntil != null ? s.receivedUntil + 1 : Infinity)));
|
|
5287
|
+
return updTbl
|
|
5288
|
+
.where('i')
|
|
5289
|
+
.between(lastCompressedUpdate, Infinity, false)
|
|
5290
|
+
.toArray((addedUpdates) => {
|
|
5291
|
+
if (addedUpdates.length <= 1)
|
|
5292
|
+
return;
|
|
5293
|
+
const docsToCompress = [];
|
|
5294
|
+
let lastUpdateToCompress = lastCompressedUpdate + 1;
|
|
5295
|
+
for (let j = 0; j < addedUpdates.length; ++j) {
|
|
5296
|
+
const updateRow = addedUpdates[j];
|
|
5297
|
+
const { i, f, k } = updateRow;
|
|
5298
|
+
if (i >= unsyncedFrom && (f & 0x01))
|
|
5299
|
+
break;
|
|
5300
|
+
const entry = docsToCompress.find((entry) => cmp(entry.docId, k) === 0);
|
|
5301
|
+
if (entry)
|
|
5302
|
+
entry.updates.push(updateRow);
|
|
5303
|
+
else
|
|
5304
|
+
docsToCompress.push({ docId: k, updates: [updateRow] });
|
|
5305
|
+
lastUpdateToCompress = i;
|
|
5306
|
+
}
|
|
5307
|
+
let p = Promise.resolve();
|
|
5308
|
+
for (const { docId, updates } of docsToCompress) {
|
|
5309
|
+
p = p.then(() => compressUpdatesForDoc(db, updatesTable, docId, updates));
|
|
5310
|
+
}
|
|
5311
|
+
return p.then(() => {
|
|
5312
|
+
return db.transaction('rw', updTbl, () => updTbl.get(0).then((current) => updTbl.put({
|
|
5313
|
+
...current,
|
|
5314
|
+
lastCompressed: Math.max(lastUpdateToCompress, current?.lastCompressed || 0),
|
|
5315
|
+
})));
|
|
5316
|
+
});
|
|
5317
|
+
});
|
|
5318
|
+
});
|
|
5319
|
+
}
|
|
5320
|
+
function compressUpdatesForDoc(db, updatesTable, parentId, addedUpdatesToCompress) {
|
|
5321
|
+
if (addedUpdatesToCompress.length < 1)
|
|
5322
|
+
throw new Error('Invalid input');
|
|
5323
|
+
return db.transaction('rw', updatesTable, (tx) => {
|
|
5324
|
+
const updTbl = tx.table(updatesTable);
|
|
5325
|
+
return updTbl.where({ k: parentId }).first((mainUpdate) => {
|
|
5326
|
+
const updates = [mainUpdate].concat(addedUpdatesToCompress);
|
|
5327
|
+
const Y = getYLibrary(db);
|
|
5328
|
+
const doc = new Y.Doc({ gc: true });
|
|
5329
|
+
updates.forEach((update) => {
|
|
5330
|
+
Y.applyUpdateV2(doc, update.u);
|
|
5331
|
+
});
|
|
5332
|
+
const compressedUpdate = Y.encodeStateAsUpdateV2(doc);
|
|
5333
|
+
const lastUpdate = updates.pop();
|
|
5334
|
+
return updTbl
|
|
5335
|
+
.put({
|
|
5336
|
+
i: lastUpdate.i,
|
|
5337
|
+
k: parentId,
|
|
5338
|
+
u: compressedUpdate,
|
|
5339
|
+
})
|
|
5340
|
+
.then(() => updTbl.bulkDelete(updates.map((update) => update.i)));
|
|
5341
|
+
});
|
|
5342
|
+
});
|
|
5343
|
+
}
|
|
5344
|
+
|
|
5345
|
+
const INTERVAL = 10000;
|
|
5346
|
+
function periodicGC(db) {
|
|
5347
|
+
let timer = null;
|
|
5348
|
+
db.on('ready', (db) => {
|
|
5349
|
+
if (db.tables.some(tbl => tbl.schema.yProps)) {
|
|
5350
|
+
const gc = () => {
|
|
5351
|
+
if (!db.isOpen())
|
|
5352
|
+
return;
|
|
5353
|
+
compressYDocs(db, INTERVAL).catch(err => {
|
|
5354
|
+
console.debug('Error during periodic GC', err);
|
|
5355
|
+
}).then(() => {
|
|
5356
|
+
timer = setTimeout(gc, INTERVAL);
|
|
5357
|
+
});
|
|
5358
|
+
};
|
|
5359
|
+
timer = setTimeout(gc, INTERVAL);
|
|
5360
|
+
}
|
|
5361
|
+
}, true);
|
|
5362
|
+
db.on('close', () => {
|
|
5363
|
+
if (timer)
|
|
5364
|
+
clearTimeout(timer);
|
|
5365
|
+
timer = null;
|
|
5366
|
+
});
|
|
5367
|
+
}
|
|
5368
|
+
|
|
5122
5369
|
class Dexie$1 {
|
|
5123
5370
|
constructor(name, options) {
|
|
5124
5371
|
this._middlewares = {};
|
|
@@ -5164,7 +5411,14 @@ class Dexie$1 {
|
|
|
5164
5411
|
});
|
|
5165
5412
|
this._state = state;
|
|
5166
5413
|
this.name = name;
|
|
5167
|
-
this.on = Events(this, "populate", "blocked", "versionchange", "close", { ready: [promisableChain, nop] });
|
|
5414
|
+
this.on = Events(this, "populate", "blocked", "versionchange", "close", "y", { ready: [promisableChain, nop] });
|
|
5415
|
+
this.once = (event, callback) => {
|
|
5416
|
+
const fn = (...args) => {
|
|
5417
|
+
this.on(event).unsubscribe(fn);
|
|
5418
|
+
callback.apply(this, args);
|
|
5419
|
+
};
|
|
5420
|
+
return this.on(event, fn);
|
|
5421
|
+
};
|
|
5168
5422
|
this.on.ready.subscribe = override(this.on.ready.subscribe, subscribe => {
|
|
5169
5423
|
return (subscriber, bSticky) => {
|
|
5170
5424
|
Dexie$1.vip(() => {
|
|
@@ -5243,6 +5497,8 @@ class Dexie$1 {
|
|
|
5243
5497
|
}
|
|
5244
5498
|
});
|
|
5245
5499
|
this.vip = vipDB;
|
|
5500
|
+
if (options?.Y)
|
|
5501
|
+
periodicGC(this);
|
|
5246
5502
|
addons.forEach(addon => addon(this));
|
|
5247
5503
|
}
|
|
5248
5504
|
version(versionNumber) {
|
|
@@ -5299,6 +5555,7 @@ class Dexie$1 {
|
|
|
5299
5555
|
() => dexieOpen(this));
|
|
5300
5556
|
}
|
|
5301
5557
|
_close() {
|
|
5558
|
+
this.on.close.fire(new CustomEvent('close'));
|
|
5302
5559
|
const state = this._state;
|
|
5303
5560
|
const idx = connections.indexOf(this);
|
|
5304
5561
|
if (idx >= 0)
|
|
@@ -5447,6 +5704,9 @@ class Dexie$1 {
|
|
|
5447
5704
|
}
|
|
5448
5705
|
return this._allTables[tableName];
|
|
5449
5706
|
}
|
|
5707
|
+
gc() {
|
|
5708
|
+
return compressYDocs(this);
|
|
5709
|
+
}
|
|
5450
5710
|
}
|
|
5451
5711
|
|
|
5452
5712
|
const symbolObservable = typeof Symbol !== "undefined" && "observable" in Symbol
|
|
@@ -5765,8 +6025,170 @@ function replacePrefix(a, b) {
|
|
|
5765
6025
|
return new PropModification({ replacePrefix: [a, b] });
|
|
5766
6026
|
}
|
|
5767
6027
|
|
|
6028
|
+
function observeYDocUpdates(provider, doc, db, parentTableName, updatesTableName, parentId, Y) {
|
|
6029
|
+
let lastUpdateId = 0;
|
|
6030
|
+
let initial = true;
|
|
6031
|
+
const subscription = liveQuery(() => {
|
|
6032
|
+
throwIfDestroyed(doc);
|
|
6033
|
+
const updatesTable = db.table(updatesTableName);
|
|
6034
|
+
return Promise.all([
|
|
6035
|
+
(lastUpdateId > 0
|
|
6036
|
+
? updatesTable
|
|
6037
|
+
.where('i')
|
|
6038
|
+
.between(lastUpdateId, Infinity, false)
|
|
6039
|
+
.toArray()
|
|
6040
|
+
.then((updates) => updates.filter((update) => cmp(update.k, parentId) === 0))
|
|
6041
|
+
: updatesTable.where({ k: parentId }).toArray()).then((updates) => {
|
|
6042
|
+
if (updates.length > 0)
|
|
6043
|
+
lastUpdateId = updates[updates.length - 1].i;
|
|
6044
|
+
return updates;
|
|
6045
|
+
}),
|
|
6046
|
+
db.table(parentTableName).where(':id').equals(parentId).toArray(),
|
|
6047
|
+
]);
|
|
6048
|
+
}).subscribe(([updates, parentRow]) => {
|
|
6049
|
+
if (parentRow.length === 0) {
|
|
6050
|
+
doc.destroy();
|
|
6051
|
+
return;
|
|
6052
|
+
}
|
|
6053
|
+
throwIfDestroyed(doc);
|
|
6054
|
+
if (updates.length > 0) {
|
|
6055
|
+
Y.transact(doc, () => {
|
|
6056
|
+
updates.forEach((update) => {
|
|
6057
|
+
try {
|
|
6058
|
+
DexieYProvider.currentUpdateRow = update;
|
|
6059
|
+
Y.applyUpdateV2(doc, update.u);
|
|
6060
|
+
}
|
|
6061
|
+
finally {
|
|
6062
|
+
DexieYProvider.currentUpdateRow = null;
|
|
6063
|
+
}
|
|
6064
|
+
});
|
|
6065
|
+
}, provider, false);
|
|
6066
|
+
}
|
|
6067
|
+
if (initial) {
|
|
6068
|
+
initial = false;
|
|
6069
|
+
doc.emit('load', [doc]);
|
|
6070
|
+
}
|
|
6071
|
+
}, (error) => {
|
|
6072
|
+
provider.on('error').fire(error);
|
|
6073
|
+
});
|
|
6074
|
+
const onUpdate = (update, origin) => {
|
|
6075
|
+
if (origin === provider)
|
|
6076
|
+
return;
|
|
6077
|
+
db.table(updatesTableName)
|
|
6078
|
+
.add({
|
|
6079
|
+
k: parentId,
|
|
6080
|
+
u: update,
|
|
6081
|
+
f: 1,
|
|
6082
|
+
})
|
|
6083
|
+
.then((i) => {
|
|
6084
|
+
if (i === lastUpdateId - 1)
|
|
6085
|
+
++lastUpdateId;
|
|
6086
|
+
})
|
|
6087
|
+
.catch((error) => {
|
|
6088
|
+
provider.on('error').fire(error);
|
|
6089
|
+
});
|
|
6090
|
+
};
|
|
6091
|
+
const stopObserving = () => {
|
|
6092
|
+
subscription.unsubscribe();
|
|
6093
|
+
doc.off('updateV2', onUpdate);
|
|
6094
|
+
doc.off('destroy', stopObserving);
|
|
6095
|
+
};
|
|
6096
|
+
doc.on('updateV2', onUpdate);
|
|
6097
|
+
doc.on('destroy', stopObserving);
|
|
6098
|
+
return stopObserving;
|
|
6099
|
+
}
|
|
6100
|
+
|
|
6101
|
+
const wm = new WeakMap();
|
|
6102
|
+
function createEvents() {
|
|
6103
|
+
return Events(null, 'load', 'sync', 'error');
|
|
6104
|
+
}
|
|
6105
|
+
class DexieYProvider {
|
|
6106
|
+
static load(doc) {
|
|
6107
|
+
let p = wm.get(doc);
|
|
6108
|
+
if (p) {
|
|
6109
|
+
++p.refCount;
|
|
6110
|
+
}
|
|
6111
|
+
else {
|
|
6112
|
+
p = new DexieYProvider(doc);
|
|
6113
|
+
wm.set(doc, p);
|
|
6114
|
+
}
|
|
6115
|
+
return p;
|
|
6116
|
+
}
|
|
6117
|
+
static release(doc, options) {
|
|
6118
|
+
if (destroyedDocs.has(doc))
|
|
6119
|
+
return;
|
|
6120
|
+
const p = wm.get(doc);
|
|
6121
|
+
if (!p) {
|
|
6122
|
+
if (--p.refCount === 0) {
|
|
6123
|
+
if (isNaN(options?.gracePeriod)) {
|
|
6124
|
+
doc.destroy();
|
|
6125
|
+
}
|
|
6126
|
+
else {
|
|
6127
|
+
setTimeout(() => p.refCount || doc.destroy(),
|
|
6128
|
+
options.gracePeriod
|
|
6129
|
+
);
|
|
6130
|
+
}
|
|
6131
|
+
}
|
|
6132
|
+
}
|
|
6133
|
+
else {
|
|
6134
|
+
doc.destroy();
|
|
6135
|
+
}
|
|
6136
|
+
}
|
|
6137
|
+
static for(doc) {
|
|
6138
|
+
return wm.get(doc);
|
|
6139
|
+
}
|
|
6140
|
+
constructor(doc) {
|
|
6141
|
+
this.refCount = 1;
|
|
6142
|
+
this.cleanupHandlers = [];
|
|
6143
|
+
this.destroyed = false;
|
|
6144
|
+
const { meta: { db, parentTable, parentId, updatesTable }, } = doc || {};
|
|
6145
|
+
if (!db || !parentTable || !updatesTable)
|
|
6146
|
+
throw new Error('Y.Doc not generated by Dexie');
|
|
6147
|
+
if (!db.table(parentTable) || !db.table(updatesTable)) {
|
|
6148
|
+
throw new Error(`Table ${parentTable} or ${updatesTable} not found in db`);
|
|
6149
|
+
}
|
|
6150
|
+
throwIfDestroyed(doc);
|
|
6151
|
+
const Y = getYLibrary(db);
|
|
6152
|
+
this.on = createEvents();
|
|
6153
|
+
this.doc = doc;
|
|
6154
|
+
this.off = (name, f) => this.on[name]?.unsubscribe(f);
|
|
6155
|
+
this.whenLoaded = new Promise((resolve, reject) => {
|
|
6156
|
+
this.on('load', resolve);
|
|
6157
|
+
this.on('error', reject);
|
|
6158
|
+
});
|
|
6159
|
+
this.whenSynced = new Promise((resolve, reject) => {
|
|
6160
|
+
this.on('sync', resolve);
|
|
6161
|
+
this.on('error', reject);
|
|
6162
|
+
});
|
|
6163
|
+
if ('dispose' in Symbol) {
|
|
6164
|
+
this[Symbol.dispose] = this.dispose;
|
|
6165
|
+
}
|
|
6166
|
+
this.stopObserving = observeYDocUpdates(this, doc, db, parentTable, updatesTable, parentId, Y);
|
|
6167
|
+
doc.on('load', () => this.on('load').fire());
|
|
6168
|
+
doc.on('sync', (sync) => sync !== false && this.on('sync').fire());
|
|
6169
|
+
doc.on('destroy', this.destroy.bind(this));
|
|
6170
|
+
db.on.y.fire(this, Y);
|
|
6171
|
+
}
|
|
6172
|
+
destroy() {
|
|
6173
|
+
wm.delete(this.doc);
|
|
6174
|
+
this.doc = null;
|
|
6175
|
+
this.destroyed = true;
|
|
6176
|
+
this.refCount = 0;
|
|
6177
|
+
this.stopObserving?.();
|
|
6178
|
+
this.on = createEvents();
|
|
6179
|
+
this.cleanupHandlers.forEach((cleanup) => cleanup());
|
|
6180
|
+
}
|
|
6181
|
+
addCleanupHandler(cleanupHandler) {
|
|
6182
|
+
this.cleanupHandlers.push(typeof cleanupHandler === 'function'
|
|
6183
|
+
? cleanupHandler
|
|
6184
|
+
: () => cleanupHandler.unsubscribe());
|
|
6185
|
+
}
|
|
6186
|
+
}
|
|
6187
|
+
DexieYProvider.getDocCache = getDocCache;
|
|
6188
|
+
DexieYProvider.currentUpdateRow = null;
|
|
6189
|
+
|
|
5768
6190
|
DexiePromise.rejectionMapper = mapError;
|
|
5769
6191
|
setDebug(debug);
|
|
5770
6192
|
|
|
5771
|
-
export { Dexie$1 as Dexie, Entity, PropModSymbol, PropModification, RangeSet, add, cmp, Dexie$1 as default, liveQuery, mergeRanges, rangesOverlap, remove, replacePrefix };
|
|
6193
|
+
export { Dexie$1 as Dexie, DexieYProvider, Entity, PropModSymbol, PropModification, RangeSet, add, cmp, Dexie$1 as default, liveQuery, mergeRanges, rangesOverlap, remove, replacePrefix };
|
|
5772
6194
|
//# sourceMappingURL=dexie.mjs.map
|