ueberdb2 4.0.15 → 4.1.1

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.
@@ -62,7 +62,7 @@ jobs:
62
62
  - uses: actions/checkout@v3
63
63
  - uses: actions/setup-node@v3
64
64
  with:
65
- node-version: 16
65
+ node-version: 20
66
66
  - run: npm ci
67
67
  # Optional dependencies must be installed manually.
68
68
  - run: npm i sqlite3
@@ -92,7 +92,7 @@ jobs:
92
92
  -
93
93
  uses: actions/setup-node@v3
94
94
  with:
95
- node-version: 16
95
+ node-version: 20
96
96
  registry-url: https://registry.npmjs.org/
97
97
  cache: 'npm'
98
98
  # This is required if the package has a prepare script that uses something
@@ -124,7 +124,7 @@ jobs:
124
124
  # back-to-back merges will cause the first merge's workflow to fail but
125
125
  # the second's will succeed.
126
126
  -
127
- run: npm publish
127
+ run: npm run publish
128
128
  env:
129
129
  NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
130
130
  -
@@ -0,0 +1,237 @@
1
+ "use strict";
2
+ /**
3
+ * Licensed under the Apache License, Version 2.0 (the "License");
4
+ * you may not use this file except in compliance with the License.
5
+ * You may obtain a copy of the License at
6
+ *
7
+ * http://www.apache.org/licenses/LICENSE-2.0
8
+ *
9
+ * Unless required by applicable law or agreed to in writing, software
10
+ * distributed under the License is distributed on an "AS-IS" BASIS,
11
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ * See the License for the specific language governing permissions and
13
+ * limitations under the License.
14
+ */
15
+ var __importDefault = (this && this.__importDefault) || function (mod) {
16
+ return (mod && mod.__esModule) ? mod : { "default": mod };
17
+ };
18
+ Object.defineProperty(exports, "__esModule", { value: true });
19
+ exports.Database = void 0;
20
+ const AbstractDatabase_1 = __importDefault(require("../lib/AbstractDatabase"));
21
+ const cassandra_driver_1 = __importDefault(require("cassandra-driver"));
22
+ const Database = class Cassandra_db extends AbstractDatabase_1.default {
23
+ client;
24
+ pool;
25
+ /**
26
+ * @param {Object} settings The required settings object to initiate the Cassandra database
27
+ * @param {String[]} settings.clientOptions See
28
+ * http://www.datastax.com/drivers/nodejs/2.0/global.html#ClientOptions for a full set of
29
+ * options that can be used
30
+ * @param {String} settings.columnFamily The column family that should be used to store data. The
31
+ * column family will be created if it doesn't exist
32
+ * @param {Function} [settings.logger] Function that will be used to pass on log events emitted by
33
+ * the Cassandra driver. See https://github.com/datastax/nodejs-driver#logging for more
34
+ * information
35
+ */
36
+ constructor(settings) {
37
+ super();
38
+ if (!settings.clientOptions) {
39
+ throw new Error('The Cassandra client options should be defined');
40
+ }
41
+ if (!settings.columnFamily) {
42
+ throw new Error('The Cassandra column family should be defined');
43
+ }
44
+ this.settings = { database: settings.database };
45
+ this.settings.clientOptions = settings.clientOptions;
46
+ this.settings.columnFamily = settings.columnFamily;
47
+ this.settings.logger = settings.logger;
48
+ }
49
+ /**
50
+ * Initializes the Cassandra client, connects to Cassandra and creates the CF if it didn't exist
51
+ * already
52
+ *
53
+ * @param {Function} callback Standard callback method.
54
+ * @param {Error} callback.err An error object (if any.)
55
+ */
56
+ init(callback) {
57
+ // Create a client
58
+ this.client = new cassandra_driver_1.default.Client(this.settings.clientOptions);
59
+ // Pass on log messages if a logger has been configured
60
+ if (this.settings.logger) {
61
+ this.client.on('log', this.settings.logger);
62
+ }
63
+ // Check whether our column family already exists and create it if necessary
64
+ this.client.execute('SELECT columnfamily_name FROM system.schema_columnfamilies WHERE keyspace_name = ?', [this.settings.clientOptions.keyspace], (err, result) => {
65
+ if (err) {
66
+ return callback(err);
67
+ }
68
+ let isDefined = false;
69
+ const length = result.rows.length;
70
+ for (let i = 0; i < length; i++) {
71
+ if (result.rows[i].columnfamily_name === this.settings.columnFamily) {
72
+ isDefined = true;
73
+ break;
74
+ }
75
+ }
76
+ if (isDefined) {
77
+ return callback(null);
78
+ }
79
+ else {
80
+ const cql = `CREATE COLUMNFAMILY "${this.settings.columnFamily}" ` +
81
+ '(key text PRIMARY KEY, data text)';
82
+ this.client && this.client.execute(cql, callback);
83
+ }
84
+ });
85
+ }
86
+ /**
87
+ * Gets a value from Cassandra
88
+ *
89
+ * @param {String} key The key for which the value should be retrieved
90
+ * @param {Function} callback Standard callback method
91
+ * @param {Error} callback.err An error object, if any
92
+ * @param {String} callback.value The value for the given key (if any)
93
+ */
94
+ get(key, callback) {
95
+ const cql = `SELECT data FROM "${this.settings.columnFamily}" WHERE key = ?`;
96
+ this.client && this.client.execute(cql, [key], (err, result) => {
97
+ if (err) {
98
+ return callback(err);
99
+ }
100
+ if (!result.rows || result.rows.length === 0) {
101
+ return callback(null, null);
102
+ }
103
+ return callback(null, result.rows[0].data);
104
+ });
105
+ }
106
+ /**
107
+ * Cassandra has no native `findKeys` method. This function implements a naive filter by
108
+ * retrieving *all* the keys and filtering those. This should obviously be used with the utmost
109
+ * care and is probably not something you want to run in production.
110
+ *
111
+ * @param {String} key The filter for keys that should match
112
+ * @param {String} [notKey] The filter for keys that shouldn't match
113
+ * @param {Function} callback Standard callback method
114
+ * @param {Error} callback.err An error object, if any
115
+ * @param {String[]} callback.keys An array of keys that match the specified filters
116
+ */
117
+ findKeys(key, notKey, callback) {
118
+ let cql = null;
119
+ if (!notKey) {
120
+ // Get all the keys
121
+ cql = `SELECT key FROM "${this.settings.columnFamily}"`;
122
+ this.client && this.client.execute(cql, (err, result) => {
123
+ if (err) {
124
+ return callback(err);
125
+ }
126
+ // Construct a regular expression based on the given key
127
+ const regex = new RegExp(`^${key.replace(/\*/g, '.*')}$`);
128
+ const keys = [];
129
+ result.rows.forEach((row) => {
130
+ if (regex.test(row.key)) {
131
+ keys.push(row.key);
132
+ }
133
+ });
134
+ return callback(null, keys);
135
+ });
136
+ }
137
+ else if (notKey === '*:*:*') {
138
+ // restrict key to format 'text:*'
139
+ const matches = /^([^:]+):\*$/.exec(key);
140
+ if (matches) {
141
+ // Get the 'text' bit out of the key and get all those keys from a special column.
142
+ // We can retrieve them from this column as we're duplicating them on .set/.remove
143
+ cql = `SELECT * from "${this.settings.columnFamily}" WHERE key = ?`;
144
+ this.client &&
145
+ this.client
146
+ .execute(cql, [`ueberdb:keys:${matches[1]}`], (err, result) => {
147
+ if (err) {
148
+ return callback(err);
149
+ }
150
+ if (!result.rows || result.rows.length === 0) {
151
+ return callback(null, []);
152
+ }
153
+ const keys = result.rows.map((row) => row.data);
154
+ return callback(null, keys);
155
+ });
156
+ }
157
+ else {
158
+ const msg = 'Cassandra db only supports key patterns like pad:* when notKey is set to *:*:*';
159
+ return callback(new Error(msg), null);
160
+ }
161
+ }
162
+ else {
163
+ return callback(new Error('Cassandra db currently only supports *:*:* as notKey'), null);
164
+ }
165
+ }
166
+ /**
167
+ * Sets a value for a key
168
+ *
169
+ * @param {String} key The key to set
170
+ * @param {String} value The value associated to this key
171
+ * @param {Function} callback Standard callback method
172
+ * @param {Error} callback.err An error object, if any
173
+ */
174
+ set(key, value, callback) {
175
+ this.doBulk([{ type: 'set', key, value }], callback);
176
+ }
177
+ /**
178
+ * Removes a key and it's value from the column family
179
+ *
180
+ * @param {String} key The key to remove
181
+ * @param {Function} callback Standard callback method
182
+ * @param {Error} callback.err An error object, if any
183
+ */
184
+ remove(key, callback) {
185
+ this.doBulk([{ type: 'remove', key }], callback);
186
+ }
187
+ /**
188
+ * Performs multiple operations in one action
189
+ *
190
+ * @param {Object[]} bulk The set of operations that should be performed
191
+ * @param {Function} callback Standard callback method
192
+ * @param {Error} callback.err An error object, if any
193
+ */
194
+ doBulk(bulk, callback) {
195
+ const queries = [];
196
+ bulk.forEach((operation) => {
197
+ // We support finding keys of the form `test:*`. If anything matches, we will try and save
198
+ // this
199
+ const matches = /^([^:]+):([^:]+)$/.exec(operation.key);
200
+ if (operation.type === 'set') {
201
+ queries.push({
202
+ query: `UPDATE "${this.settings.columnFamily}" SET data = ? WHERE key = ?`,
203
+ params: [operation.value, operation.key],
204
+ });
205
+ if (matches) {
206
+ queries.push({
207
+ query: `UPDATE "${this.settings.columnFamily}" SET data = ? WHERE key = ?`,
208
+ params: ['1', `ueberdb:keys:${matches[1]}`],
209
+ });
210
+ }
211
+ }
212
+ else if (operation.type === 'remove') {
213
+ queries.push({
214
+ query: `DELETE FROM "${this.settings.columnFamily}" WHERE key=?`,
215
+ params: [operation.key],
216
+ });
217
+ if (matches) {
218
+ queries.push({
219
+ query: `DELETE FROM "${this.settings.columnFamily}" WHERE key = ?`,
220
+ params: [`ueberdb:keys:${matches[1]}`],
221
+ });
222
+ }
223
+ }
224
+ });
225
+ this.client && this.client.batch(queries, { prepare: true }, callback);
226
+ }
227
+ /**
228
+ * Closes the Cassandra connection
229
+ *
230
+ * @param {Function} callback Standard callback method
231
+ * @param {Error} callback.err Error object in case something goes wrong
232
+ */
233
+ close(callback) {
234
+ this.pool.shutdown(callback);
235
+ }
236
+ };
237
+ exports.Database = Database;
@@ -0,0 +1,181 @@
1
+ "use strict";
2
+ /**
3
+ * 2012 Max 'Azul' Wiehle
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS-IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+ var __importDefault = (this && this.__importDefault) || function (mod) {
18
+ return (mod && mod.__esModule) ? mod : { "default": mod };
19
+ };
20
+ Object.defineProperty(exports, "__esModule", { value: true });
21
+ exports.Database = void 0;
22
+ const AbstractDatabase_1 = __importDefault(require("../lib/AbstractDatabase"));
23
+ const http_1 = __importDefault(require("http"));
24
+ const nano_1 = __importDefault(require("nano"));
25
+ const Database = class Couch_db extends AbstractDatabase_1.default {
26
+ agent;
27
+ db;
28
+ constructor(settings) {
29
+ super();
30
+ this.agent = null;
31
+ this.db = null;
32
+ this.settings = settings;
33
+ // force some settings
34
+ // used by CacheAndBufferLayer.js
35
+ this.settings.cache = 1000;
36
+ this.settings.writeInterval = 100;
37
+ this.settings.json = false;
38
+ }
39
+ get isAsync() { return true; }
40
+ async init() {
41
+ this.agent = new http_1.default.Agent({
42
+ keepAlive: true,
43
+ maxSockets: this.settings.maxListeners || 1,
44
+ });
45
+ const coudhDBSettings = {
46
+ url: `http://${this.settings.host}:${this.settings.port}`,
47
+ requestDefaults: {
48
+ agent: this.agent,
49
+ },
50
+ };
51
+ if (this.settings.user && this.settings.password) {
52
+ coudhDBSettings.requestDefaults.auth = {
53
+ username: this.settings.user,
54
+ password: this.settings.password,
55
+ };
56
+ }
57
+ const client = (0, nano_1.default)(coudhDBSettings);
58
+ try {
59
+ if (this.settings.database != null) {
60
+ await client.db.get(this.settings.database);
61
+ }
62
+ }
63
+ catch (err) {
64
+ if (err.statusCode !== 404)
65
+ throw err;
66
+ if (this.settings.database != null) {
67
+ await client.db.create(this.settings.database);
68
+ }
69
+ }
70
+ if (this.settings.database != null) {
71
+ this.db = client.use(this.settings.database);
72
+ }
73
+ }
74
+ async get(key) {
75
+ let doc;
76
+ try {
77
+ if (this.db) {
78
+ doc = await this.db.get(key);
79
+ }
80
+ }
81
+ catch (err) {
82
+ if (err.statusCode === 404)
83
+ return null;
84
+ throw err;
85
+ }
86
+ if (doc && 'value' in doc) {
87
+ return doc.value;
88
+ }
89
+ return '';
90
+ }
91
+ async findKeys(key, notKey) {
92
+ const pfxLen = key.indexOf('*');
93
+ if (!this.db) {
94
+ return;
95
+ }
96
+ const pfx = pfxLen < 0 ? key : key.slice(0, pfxLen);
97
+ const results = await this.db.find({
98
+ selector: {
99
+ _id: pfxLen < 0 ? pfx : {
100
+ $gte: pfx,
101
+ // https://docs.couchdb.org/en/3.2.2/ddocs/views/collation.html#string-ranges
102
+ $lte: `${pfx}\ufff0`,
103
+ $regex: this.createFindRegex(key, notKey).source,
104
+ },
105
+ },
106
+ fields: ['_id'],
107
+ });
108
+ return results.docs.map((doc) => doc._id);
109
+ }
110
+ async set(key, value) {
111
+ let doc;
112
+ if (!this.db) {
113
+ return;
114
+ }
115
+ try {
116
+ doc = await this.db.get(key);
117
+ }
118
+ catch (err) {
119
+ if (err.statusCode !== 404)
120
+ throw err;
121
+ }
122
+ await this.db.insert({
123
+ _id: key,
124
+ // @ts-ignore
125
+ value,
126
+ ...doc == null ? {} : {
127
+ _rev: doc._rev,
128
+ },
129
+ });
130
+ }
131
+ async remove(key) {
132
+ let header;
133
+ if (!this.db) {
134
+ return;
135
+ }
136
+ try {
137
+ header = await this.db.head(key);
138
+ }
139
+ catch (err) {
140
+ if (err.statusCode === 404)
141
+ return;
142
+ throw err;
143
+ }
144
+ // etag has additional quotation marks, remove them
145
+ const etag = JSON.parse(header.etag);
146
+ await this.db.destroy(key, etag);
147
+ }
148
+ async doBulk(bulk) {
149
+ if (!this.db) {
150
+ return;
151
+ }
152
+ const keys = bulk.map((op) => op.key);
153
+ const revs = {};
154
+ // @ts-ignore
155
+ for (const { key, value } of (await this.db.fetchRevs({ keys })).rows) {
156
+ // couchDB will return error instead of value if key does not exist
157
+ if (value != null)
158
+ revs[key] = value.rev;
159
+ }
160
+ const setters = [];
161
+ for (const item of bulk) {
162
+ const set = { _id: item.key, _rev: undefined,
163
+ _deleted: false, value: '' };
164
+ if (revs[item.key] != null)
165
+ set._rev = revs[item.key];
166
+ if (item.type === 'set')
167
+ set.value = item.value;
168
+ if (item.type === 'remove')
169
+ set._deleted = true;
170
+ setters.push(set);
171
+ }
172
+ await this.db && await this.db.bulk({ docs: setters });
173
+ }
174
+ async close() {
175
+ this.db = null;
176
+ if (this.agent)
177
+ this.agent.destroy();
178
+ this.agent = null;
179
+ }
180
+ };
181
+ exports.Database = Database;
@@ -0,0 +1,78 @@
1
+ "use strict";
2
+ /**
3
+ * 2011 Peter 'Pita' Martischka
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS-IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+ var __importDefault = (this && this.__importDefault) || function (mod) {
18
+ return (mod && mod.__esModule) ? mod : { "default": mod };
19
+ };
20
+ Object.defineProperty(exports, "__esModule", { value: true });
21
+ exports.Database = void 0;
22
+ /*
23
+ *
24
+ * Fair warning that length may not provide the correct value upon load.
25
+ * See https://github.com/ether/etherpad-lite/pull/3984
26
+ *
27
+ */
28
+ const AbstractDatabase_1 = __importDefault(require("../lib/AbstractDatabase"));
29
+ // @ts-ignore
30
+ const dirty_1 = __importDefault(require("dirty"));
31
+ const Database = class extends AbstractDatabase_1.default {
32
+ db;
33
+ constructor(settings) {
34
+ super();
35
+ this.db = null;
36
+ if (!settings || !settings.filename) {
37
+ // @ts-ignore
38
+ settings = { filename: null };
39
+ }
40
+ this.settings = settings;
41
+ // set default settings
42
+ this.settings.cache = 0;
43
+ this.settings.writeInterval = 0;
44
+ this.settings.json = false;
45
+ }
46
+ init(callback) {
47
+ this.db = new dirty_1.default(this.settings.filename);
48
+ this.db.on('load', (err) => {
49
+ callback();
50
+ });
51
+ }
52
+ get(key, callback) {
53
+ callback(null, this.db.get(key));
54
+ }
55
+ findKeys(key, notKey, callback) {
56
+ const keys = [];
57
+ const regex = this.createFindRegex(key, notKey);
58
+ this.db.forEach((key, val) => {
59
+ if (key.search(regex) !== -1) {
60
+ keys.push(key);
61
+ }
62
+ });
63
+ callback(null, keys);
64
+ }
65
+ set(key, value, callback) {
66
+ this.db.set(key, value, callback);
67
+ }
68
+ remove(key, callback) {
69
+ this.db.rm(key, callback);
70
+ }
71
+ close(callback) {
72
+ this.db.close();
73
+ this.db = null;
74
+ if (callback)
75
+ callback();
76
+ }
77
+ };
78
+ exports.Database = Database;
@@ -0,0 +1,77 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.Database = void 0;
7
+ const AbstractDatabase_1 = __importDefault(require("../lib/AbstractDatabase"));
8
+ /**
9
+ * 2011 Peter 'Pita' Martischka
10
+ *
11
+ * Licensed under the Apache License, Version 2.0 (the "License");
12
+ * you may not use this file except in compliance with the License.
13
+ * You may obtain a copy of the License at
14
+ *
15
+ * http://www.apache.org/licenses/LICENSE-2.0
16
+ *
17
+ * Unless required by applicable law or agreed to in writing, software
18
+ * distributed under the License is distributed on an "AS-IS" BASIS,
19
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20
+ * See the License for the specific language governing permissions and
21
+ * limitations under the License.
22
+ */
23
+ // @ts-ignore
24
+ const dirty_1 = require("dirty");
25
+ const Database = class extends AbstractDatabase_1.default {
26
+ db;
27
+ constructor(settings) {
28
+ super();
29
+ // @ts-ignore
30
+ this.db = null;
31
+ if (!settings || !settings.filename) {
32
+ settings = {};
33
+ }
34
+ this.settings = settings;
35
+ // set default settings
36
+ this.settings.cache = 0;
37
+ this.settings.writeInterval = 0;
38
+ this.settings.json = false;
39
+ }
40
+ init(callback) {
41
+ this.db = new dirty_1.Dirty(this.settings.filename);
42
+ this.db.on('load', (err) => {
43
+ callback();
44
+ });
45
+ }
46
+ get(key, callback) {
47
+ callback(null, this.db.get(key));
48
+ }
49
+ findKeys(key, notKey, callback) {
50
+ const keys = [];
51
+ const regex = this.createFindRegex(key, notKey);
52
+ this.db.forEach((key, val) => {
53
+ if (key.search(regex) !== -1) {
54
+ keys.push(key);
55
+ }
56
+ });
57
+ callback(null, keys);
58
+ }
59
+ set(key, value, callback) {
60
+ this.db.set(key, value, callback);
61
+ const databasePath = require('path').dirname(this.settings.filename);
62
+ require('simple-git')(databasePath)
63
+ .silent(true)
64
+ .add('./*.db')
65
+ .commit('Automated commit...')
66
+ .push(['-u', 'origin', 'master'], () => console.debug('Stored git commit'));
67
+ }
68
+ remove(key, callback) {
69
+ this.db.rm(key, callback);
70
+ }
71
+ close(callback) {
72
+ this.db.close();
73
+ if (callback)
74
+ callback();
75
+ }
76
+ };
77
+ exports.Database = Database;