chore: build

This commit is contained in:
Ewout Stortenbeker 2023-05-18 19:44:57 +02:00
parent 927381a650
commit 104f28d06f
25 changed files with 265 additions and 35 deletions

30
dist/browser.js vendored
View file

@ -4588,6 +4588,7 @@ class Storage extends acebase_core_1.SimpleEventEmitter {
// private _validation = new Map<string, { validate?: (previous: any, value: any) => boolean, schema?: SchemaDefinition }>;
this._schemas = [];
this._indexes = [];
this._annoucedIndexes = new Map();
this.indexes = {
/**
* Tests if (the default storage implementation of) indexes are supported in the environment.
@ -4689,9 +4690,18 @@ class Storage extends acebase_core_1.SimpleEventEmitter {
if (existingIndex) {
return existingIndex;
}
else if (this._annoucedIndexes.has(fileName)) {
// Index is already in the process of being added, wait until it becomes availabe
const index = await this._annoucedIndexes.get(fileName);
return index;
}
try {
const index = await data_index_1.DataIndex.readFromFile(this, fileName);
// Announce the index to prevent race condition in between reading and receiving the IPC index.created notification
const indexPromise = data_index_1.DataIndex.readFromFile(this, fileName);
this._annoucedIndexes.set(fileName, indexPromise);
const index = await indexPromise;
this._indexes.push(index);
this._annoucedIndexes.delete(fileName);
return index;
}
catch (err) {
@ -5324,6 +5334,9 @@ class Storage extends acebase_core_1.SimpleEventEmitter {
else if (type === 'child_removed') {
trigger = oldValue !== null && newValue === null;
}
if (!trigger) {
return;
}
const pathKeys = acebase_core_1.PathInfo.getPathKeys(sub.dataPath);
variables.forEach(variable => {
// only replaces first occurrence (so multiple *'s will be processed 1 by 1)
@ -5332,7 +5345,7 @@ class Storage extends acebase_core_1.SimpleEventEmitter {
pathKeys[index] = variable.value;
});
const dataPath = pathKeys.reduce((path, key) => acebase_core_1.PathInfo.getChildPath(path, key), '');
trigger && this.subscriptions.trigger(sub.type, sub.subscriptionPath, dataPath, oldValue, newValue, options.context);
this.subscriptions.trigger(sub.type, sub.subscriptionPath, dataPath, oldValue, newValue, options.context);
};
const prepareMutationEvents = (currentPath, oldValue, newValue, compareResult) => {
const batch = [];
@ -9087,10 +9100,15 @@ class DataReferenceQuery {
}
ev = eventData;
}
listeners.forEach(callback => { try {
callback(ev);
}
catch (e) { } });
listeners.forEach(callback => {
var _a, _b;
try {
callback(ev);
}
catch (err) {
this.ref.db.debug.error(`Error executing "${ev.name}" event handler of realtime query on path "${this.ref.path}": ${(_b = (_a = err === null || err === void 0 ? void 0 : err.stack) !== null && _a !== void 0 ? _a : err === null || err === void 0 ? void 0 : err.message) !== null && _b !== void 0 ? _b : err}`);
}
});
};
// Check if there are event listeners set for realtime changes
options.monitor = { add: false, change: false, remove: false };

2
dist/browser.min.js vendored

File diff suppressed because one or more lines are too long

View file

@ -605,7 +605,8 @@ class BinaryBPlusTree {
(0, utils_1._appendToArray)(bytes, valData);
});
// update ext_block_free_length:
(0, binary_1.writeByteLength)(bytes, 4, self._length - bytes.length);
const freeBytes = self._length - bytes.length + 8; // Do not count 8 header bytes
(0, binary_1.writeByteLength)(bytes, 4, freeBytes);
const valueListLengthData = (0, binary_1.writeByteLength)([], 0, self.totalValues - 1);
await Promise.all([
// write ext_data_block
@ -614,7 +615,7 @@ class BinaryBPlusTree {
tree._writeFn(valueListLengthData, self._listLengthIndex),
]);
self.totalValues--;
self._freeBytes = self._length - bytes.length;
self._freeBytes = freeBytes;
},
};
entry.loadValues = async function loadValues() {
@ -794,7 +795,7 @@ class BinaryBPlusTree {
throw new detailed_error_1.DetailedError('write-node-fail', `Failed to write node: ${err.message}`, err);
}
}
async _writeLeaf(leafInfo) {
async _writeLeaf(leafInfo, options = { addFreeSpace: true }) {
(0, assert_1.assert)(leafInfo.entries.every((entry, index, arr) => index === 0 || (0, typesafe_compare_1._isMore)(entry.key, arr[index - 1].key)), 'Leaf entries are not sorted ok');
try {
const builder = new binary_tree_builder_1.BinaryBPlusTreeBuilder({
@ -814,7 +815,7 @@ class BinaryBPlusTree {
rebuild: leafInfo.extData.loaded,
}
: null;
const addFreeSpace = true;
const addFreeSpace = options.addFreeSpace !== false;
const writes = [];
const bytes = builder.createLeaf({
index: leafInfo.index,
@ -1941,7 +1942,7 @@ class BinaryBPlusTree {
if (options.rollbackOnFailure === false) {
return;
}
return this._writeLeaf(leaf);
return this._writeLeaf(leaf, { addFreeSpace: false });
}
else {
return this._registerFreeSpace(allocated.index, allocated.length);

File diff suppressed because one or more lines are too long

View file

@ -104,6 +104,7 @@ class Storage extends acebase_core_1.SimpleEventEmitter {
// private _validation = new Map<string, { validate?: (previous: any, value: any) => boolean, schema?: SchemaDefinition }>;
this._schemas = [];
this._indexes = [];
this._annoucedIndexes = new Map();
this.indexes = {
/**
* Tests if (the default storage implementation of) indexes are supported in the environment.
@ -205,9 +206,18 @@ class Storage extends acebase_core_1.SimpleEventEmitter {
if (existingIndex) {
return existingIndex;
}
else if (this._annoucedIndexes.has(fileName)) {
// Index is already in the process of being added, wait until it becomes availabe
const index = await this._annoucedIndexes.get(fileName);
return index;
}
try {
const index = await data_index_1.DataIndex.readFromFile(this, fileName);
// Announce the index to prevent race condition in between reading and receiving the IPC index.created notification
const indexPromise = data_index_1.DataIndex.readFromFile(this, fileName);
this._annoucedIndexes.set(fileName, indexPromise);
const index = await indexPromise;
this._indexes.push(index);
this._annoucedIndexes.delete(fileName);
return index;
}
catch (err) {
@ -840,6 +850,9 @@ class Storage extends acebase_core_1.SimpleEventEmitter {
else if (type === 'child_removed') {
trigger = oldValue !== null && newValue === null;
}
if (!trigger) {
return;
}
const pathKeys = acebase_core_1.PathInfo.getPathKeys(sub.dataPath);
variables.forEach(variable => {
// only replaces first occurrence (so multiple *'s will be processed 1 by 1)
@ -848,7 +861,7 @@ class Storage extends acebase_core_1.SimpleEventEmitter {
pathKeys[index] = variable.value;
});
const dataPath = pathKeys.reduce((path, key) => acebase_core_1.PathInfo.getChildPath(path, key), '');
trigger && this.subscriptions.trigger(sub.type, sub.subscriptionPath, dataPath, oldValue, newValue, options.context);
this.subscriptions.trigger(sub.type, sub.subscriptionPath, dataPath, oldValue, newValue, options.context);
};
const prepareMutationEvents = (currentPath, oldValue, newValue, compareResult) => {
const batch = [];

File diff suppressed because one or more lines are too long

90
dist/cjs/test/issue-225.spec.js vendored Normal file
View file

@ -0,0 +1,90 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const tempdb_1 = require("./tempdb");
describe('issue #225', () => {
let db;
let removeDB;
beforeAll(async () => {
({ db, removeDB } = await (0, tempdb_1.createTempDB)({ logLevel: 'warn' }));
});
afterAll(async () => {
await removeDB();
});
it('realtime query and indexed deletes', async () => {
// Create indexes
await db.indexes.create('items', 'location');
await db.indexes.create('items', 'category');
await db.indexes.create('items', 'status');
const eventStats = { add: 0, change: 0, remove: 0 };
// Setup realtime query
const realtimeQuery = db.query('items')
.filter('category', '==', 'main')
.filter('status', '==', 1)
.on('add', event => {
eventStats.add++;
const item = event.snapshot.val();
console.log(`Added item ${event.ref.key}:`, item);
})
.on('change', event => {
eventStats.change++;
const item = event.snapshot.val();
console.log(`Changed item ${event.ref.key}:`, item);
})
.on('remove', event => {
eventStats.remove++;
console.log(`Removed item ${event.ref.key}`);
});
// Get initial (no) results
const results = await realtimeQuery.get();
// Add a bunch of items, should trigger "add" events on realtime query
const TEST_SIZE = 1000;
const itemIds = [];
const locations = ['Amsterdam', 'Cape Town', 'Sydney', 'Miami', 'Toronto', 'Berlin', 'Paris'];
for (let i = 0; i < TEST_SIZE; i++) {
const ref = await db.ref('items').push({
location: locations[Math.floor(Math.random() * locations.length)],
category: 'main',
status: 1,
});
itemIds.push(ref.key);
}
// Update every other item to status 2, should trigger 500 "remove" events on realtime query
for (let i = 0; i < itemIds.length; i += 2) {
await db.ref('items').child(itemIds[i]).update({
status: 2,
});
}
// Update every 3rd item to location 'Amsterdam', should trigger "change" events on realtime query
for (let i = 0; i < itemIds.length; i += 3) {
await db.ref('items').child(itemIds[i]).update({
location: 'Amsterdam',
});
}
// Update every 3rd item to status 3, should trigger some more "remove" events on realtime query
for (let i = 0; i < itemIds.length; i += 3) {
await db.ref('items').child(itemIds[i]).update({
status: 3,
});
}
// Remove items with status 1, should trigger all remaining "remove" events on realtime query (0 results left)
await db.query('items').filter('status', '==', 1).remove();
// Wait for all remove events to fire
console.log('Waiting for all remove events to fire');
await new Promise(resolve => {
let eventsFired = eventStats.remove;
const check = () => {
if (eventsFired === eventStats.remove) {
// All fired
return resolve();
}
eventsFired = eventStats.remove;
setTimeout(check, 100); // Schedule next check
};
setTimeout(check, 1000); // Schedule first check
});
// Stop live query
await realtimeQuery.stop();
console.log(`${eventStats.add} added, ${eventStats.change} changed, ${eventStats.remove} removed`);
}, 600000);
});
//# sourceMappingURL=issue-225.spec.js.map

1
dist/cjs/test/issue-225.spec.js.map vendored Normal file
View file

@ -0,0 +1 @@
{"version":3,"file":"issue-225.spec.js","sourceRoot":"","sources":["../../../src/test/issue-225.spec.ts"],"names":[],"mappings":"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"}

View file

@ -600,7 +600,8 @@ class BinaryBPlusTree {
_appendToArray(bytes, valData);
});
// update ext_block_free_length:
writeByteLength(bytes, 4, self._length - bytes.length);
const freeBytes = self._length - bytes.length + 8; // Do not count 8 header bytes
writeByteLength(bytes, 4, freeBytes);
const valueListLengthData = writeByteLength([], 0, self.totalValues - 1);
await Promise.all([
// write ext_data_block
@ -609,7 +610,7 @@ class BinaryBPlusTree {
tree._writeFn(valueListLengthData, self._listLengthIndex),
]);
self.totalValues--;
self._freeBytes = self._length - bytes.length;
self._freeBytes = freeBytes;
},
};
entry.loadValues = async function loadValues() {
@ -787,7 +788,7 @@ class BinaryBPlusTree {
throw new DetailedError('write-node-fail', `Failed to write node: ${err.message}`, err);
}
}
async _writeLeaf(leafInfo) {
async _writeLeaf(leafInfo, options = { addFreeSpace: true }) {
assert(leafInfo.entries.every((entry, index, arr) => index === 0 || _isMore(entry.key, arr[index - 1].key)), 'Leaf entries are not sorted ok');
try {
const builder = new BinaryBPlusTreeBuilder({
@ -807,7 +808,7 @@ class BinaryBPlusTree {
rebuild: leafInfo.extData.loaded,
}
: null;
const addFreeSpace = true;
const addFreeSpace = options.addFreeSpace !== false;
const writes = [];
const bytes = builder.createLeaf({
index: leafInfo.index,
@ -1932,7 +1933,7 @@ class BinaryBPlusTree {
if (options.rollbackOnFailure === false) {
return;
}
return this._writeLeaf(leaf);
return this._writeLeaf(leaf, { addFreeSpace: false });
}
else {
return this._registerFreeSpace(allocated.index, allocated.length);

File diff suppressed because one or more lines are too long

View file

@ -37,7 +37,7 @@ export class IPCSocketPeer extends AceBaseIPCPeer {
if (!isMaster) {
// Try starting IPC service if it is not running yet.
// Use maxIdleTime 0 to allow tests to remove database files when done, make this configurable!
const service = spawn('node', [/file:\/{2,3}(.+)\/[^/]/.exec(import.meta.url)[1] + '/service/start.js', dbFile, '--loglevel', storage.debug.level, '--maxidletime', '0'], { detached: true, stdio: 'ignore' });
const service = spawn('node', [`${process.platform === 'win32' ? '' : '/'}${/file:\/{2,3}(.+)\/[^/]/.exec(import.meta.url)[1]}` + '/service/start.js', dbFile, '--loglevel', storage.debug.level, '--maxidletime', '0'], { detached: true, stdio: 'ignore' });
service.unref(); // Process is detached and allowed to keep running after we exit. Do not keep a reference to it, possibly preventing app exit.
// For testing:
// startServer(dbFile, {

View file

@ -99,6 +99,7 @@ export class Storage extends SimpleEventEmitter {
// private _validation = new Map<string, { validate?: (previous: any, value: any) => boolean, schema?: SchemaDefinition }>;
this._schemas = [];
this._indexes = [];
this._annoucedIndexes = new Map();
this.indexes = {
/**
* Tests if (the default storage implementation of) indexes are supported in the environment.
@ -200,9 +201,18 @@ export class Storage extends SimpleEventEmitter {
if (existingIndex) {
return existingIndex;
}
else if (this._annoucedIndexes.has(fileName)) {
// Index is already in the process of being added, wait until it becomes availabe
const index = await this._annoucedIndexes.get(fileName);
return index;
}
try {
const index = await DataIndex.readFromFile(this, fileName);
// Announce the index to prevent race condition in between reading and receiving the IPC index.created notification
const indexPromise = DataIndex.readFromFile(this, fileName);
this._annoucedIndexes.set(fileName, indexPromise);
const index = await indexPromise;
this._indexes.push(index);
this._annoucedIndexes.delete(fileName);
return index;
}
catch (err) {
@ -835,6 +845,9 @@ export class Storage extends SimpleEventEmitter {
else if (type === 'child_removed') {
trigger = oldValue !== null && newValue === null;
}
if (!trigger) {
return;
}
const pathKeys = PathInfo.getPathKeys(sub.dataPath);
variables.forEach(variable => {
// only replaces first occurrence (so multiple *'s will be processed 1 by 1)
@ -843,7 +856,7 @@ export class Storage extends SimpleEventEmitter {
pathKeys[index] = variable.value;
});
const dataPath = pathKeys.reduce((path, key) => PathInfo.getChildPath(path, key), '');
trigger && this.subscriptions.trigger(sub.type, sub.subscriptionPath, dataPath, oldValue, newValue, options.context);
this.subscriptions.trigger(sub.type, sub.subscriptionPath, dataPath, oldValue, newValue, options.context);
};
const prepareMutationEvents = (currentPath, oldValue, newValue, compareResult) => {
const batch = [];

File diff suppressed because one or more lines are too long

View file

@ -6,7 +6,7 @@ export async function readDataSet(name) {
return JSON.parse(file);
}
export function getDataSetPath(name) {
const path = resolve(/file:\/{2,3}(.+)\/[^/]/.exec(import.meta.url)[1], '../../../spec/dataset'); // dir relative to dist/[cjs|esm]/test
const path = resolve(`${process.platform === 'win32' ? '' : '/'}${/file:\/{2,3}(.+)\/[^/]/.exec(import.meta.url)[1]}`, '../../../spec/dataset'); // dir relative to dist/[cjs|esm]/test
return `${path}/${name}.json`;
}
//# sourceMappingURL=dataset.js.map

88
dist/esm/test/issue-225.spec.js vendored Normal file
View file

@ -0,0 +1,88 @@
import { createTempDB } from './tempdb.js';
describe('issue #225', () => {
let db;
let removeDB;
beforeAll(async () => {
({ db, removeDB } = await createTempDB({ logLevel: 'warn' }));
});
afterAll(async () => {
await removeDB();
});
it('realtime query and indexed deletes', async () => {
// Create indexes
await db.indexes.create('items', 'location');
await db.indexes.create('items', 'category');
await db.indexes.create('items', 'status');
const eventStats = { add: 0, change: 0, remove: 0 };
// Setup realtime query
const realtimeQuery = db.query('items')
.filter('category', '==', 'main')
.filter('status', '==', 1)
.on('add', event => {
eventStats.add++;
const item = event.snapshot.val();
console.log(`Added item ${event.ref.key}:`, item);
})
.on('change', event => {
eventStats.change++;
const item = event.snapshot.val();
console.log(`Changed item ${event.ref.key}:`, item);
})
.on('remove', event => {
eventStats.remove++;
console.log(`Removed item ${event.ref.key}`);
});
// Get initial (no) results
const results = await realtimeQuery.get();
// Add a bunch of items, should trigger "add" events on realtime query
const TEST_SIZE = 1000;
const itemIds = [];
const locations = ['Amsterdam', 'Cape Town', 'Sydney', 'Miami', 'Toronto', 'Berlin', 'Paris'];
for (let i = 0; i < TEST_SIZE; i++) {
const ref = await db.ref('items').push({
location: locations[Math.floor(Math.random() * locations.length)],
category: 'main',
status: 1,
});
itemIds.push(ref.key);
}
// Update every other item to status 2, should trigger 500 "remove" events on realtime query
for (let i = 0; i < itemIds.length; i += 2) {
await db.ref('items').child(itemIds[i]).update({
status: 2,
});
}
// Update every 3rd item to location 'Amsterdam', should trigger "change" events on realtime query
for (let i = 0; i < itemIds.length; i += 3) {
await db.ref('items').child(itemIds[i]).update({
location: 'Amsterdam',
});
}
// Update every 3rd item to status 3, should trigger some more "remove" events on realtime query
for (let i = 0; i < itemIds.length; i += 3) {
await db.ref('items').child(itemIds[i]).update({
status: 3,
});
}
// Remove items with status 1, should trigger all remaining "remove" events on realtime query (0 results left)
await db.query('items').filter('status', '==', 1).remove();
// Wait for all remove events to fire
console.log('Waiting for all remove events to fire');
await new Promise(resolve => {
let eventsFired = eventStats.remove;
const check = () => {
if (eventsFired === eventStats.remove) {
// All fired
return resolve();
}
eventsFired = eventStats.remove;
setTimeout(check, 100); // Schedule next check
};
setTimeout(check, 1000); // Schedule first check
});
// Stop live query
await realtimeQuery.stop();
console.log(`${eventStats.add} added, ${eventStats.change} changed, ${eventStats.remove} removed`);
}, 600000);
});
//# sourceMappingURL=issue-225.spec.js.map

1
dist/esm/test/issue-225.spec.js.map vendored Normal file
View file

@ -0,0 +1 @@
{"version":3,"file":"issue-225.spec.js","sourceRoot":"","sources":["../../../src/test/issue-225.spec.ts"],"names":[],"mappings":"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"}

View file

@ -17,7 +17,7 @@ describe('readonly databases', () => {
await db.ref('test').set({ text: 'This is a test' });
await db.close();
// Open it readonly
db = new AceBase(db.name, { logLevel: 'verbose', storage: { path: /file:\/{2,3}(.+)\/[^/]/.exec(import.meta.url)[1], readOnly: true } });
db = new AceBase(db.name, { logLevel: 'verbose', storage: { path: `${process.platform === 'win32' ? '' : '/'}${/file:\/{2,3}(.+)\/[^/]/.exec(import.meta.url)[1]}`, readOnly: true } });
try {
// Try writing to it
await db.ref('test').update({ test: 'Not allowed' });
@ -35,7 +35,7 @@ describe('readonly databases', () => {
await db.ref('test').set({ text: 'This is a test' });
await db.close();
// Open it readonly
db = new AceBase(db.name, { logLevel: 'verbose', storage: { path: /file:\/{2,3}(.+)\/[^/]/.exec(import.meta.url)[1], readOnly: true } });
db = new AceBase(db.name, { logLevel: 'verbose', storage: { path: `${process.platform === 'win32' ? '' : '/'}${/file:\/{2,3}(.+)\/[^/]/.exec(import.meta.url)[1]}`, readOnly: true } });
// Try reading from it
const snap = await db.ref('test').get();
const val = snap.val();

View file

@ -3,7 +3,7 @@ import { readdir, rm, rmdir } from 'fs/promises';
export async function createTempDB(enable = {}) {
// Create temp db
const dbname = 'test-' + ID.generate();
const options = { storage: { path: /file:\/{2,3}(.+)\/[^/]/.exec(import.meta.url)[1] }, logLevel: enable.logLevel || 'log' };
const options = { storage: { path: `${process.platform === 'win32' ? '' : '/'}${/file:\/{2,3}(.+)\/[^/]/.exec(import.meta.url)[1]}` }, logLevel: enable.logLevel || 'log' };
if (enable.transactionLogging === true) {
options.transactions = { log: true };
}
@ -18,7 +18,7 @@ export async function createTempDB(enable = {}) {
// Close database
await db.close();
// Remove database
const dbdir = `${/file:\/{2,3}(.+)\/[^/]/.exec(import.meta.url)[1]}/${dbname}.acebase`;
const dbdir = `${`${process.platform === 'win32' ? '' : '/'}${/file:\/{2,3}(.+)\/[^/]/.exec(import.meta.url)[1]}`}/${dbname}.acebase`;
if (nodeVersion.major < 12) {
// console.error(`Node ${process.version} cannot remove temp database directory ${dbdir}. Remove it manually!`);
const files = await readdir(dbdir);

File diff suppressed because one or more lines are too long

View file

@ -12,7 +12,7 @@ export declare class NodeInfo {
childCount?: number;
constructor(info: Partial<NodeInfo>);
get valueType(): NodeValueType;
get valueTypeName(): "object" | "string" | "number" | "binary" | "bigint" | "boolean" | "date" | "array" | "reference";
get valueTypeName(): "object" | "string" | "number" | "array" | "boolean" | "date" | "bigint" | "binary" | "reference";
toString(): string;
}
//# sourceMappingURL=node-info.d.ts.map

View file

@ -11,7 +11,7 @@ declare const nodeValueTypes: {
};
export type NodeValueType = typeof nodeValueTypes[keyof typeof nodeValueTypes];
export declare const VALUE_TYPES: Record<"OBJECT" | "ARRAY" | "NUMBER" | "BOOLEAN" | "STRING" | "BIGINT" | "DATETIME" | "BINARY" | "REFERENCE", NodeValueType>;
export declare function getValueTypeName(valueType: number): "object" | "string" | "number" | "binary" | "bigint" | "boolean" | "date" | "array" | "reference";
export declare function getValueTypeName(valueType: number): "object" | "string" | "number" | "array" | "boolean" | "date" | "bigint" | "binary" | "reference";
export declare function getNodeValueType(value: unknown): NodeValueType;
export declare function getValueType(value: unknown): NodeValueType;
export {};

View file

@ -119,6 +119,7 @@ export declare class Storage extends SimpleEventEmitter {
*/
constructor(name: string, settings: StorageSettings, env: StorageEnv);
private _indexes;
private _annoucedIndexes;
indexes: {
/**
* Tests if (the default storage implementation of) indexes are supported in the environment.

File diff suppressed because one or more lines are too long

2
dist/types/test/issue-225.spec.d.ts vendored Normal file
View file

@ -0,0 +1,2 @@
export {};
//# sourceMappingURL=issue-225.spec.d.ts.map

View file

@ -0,0 +1 @@
{"version":3,"file":"issue-225.spec.d.ts","sourceRoot":"","sources":["../../../src/test/issue-225.spec.ts"],"names":[],"mappings":""}