dbgate-api-premium 5.5.7-alpha.45

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.
Files changed (125) hide show
  1. package/.env +19 -0
  2. package/.yarnrc +2 -0
  3. package/README.md +1 -0
  4. package/env/dblogin/.env +14 -0
  5. package/env/portal/.env +70 -0
  6. package/env/singledb/.env +17 -0
  7. package/env/storage/.env +43 -0
  8. package/package.json +89 -0
  9. package/src/auth/authCommon.js +16 -0
  10. package/src/auth/authProvider.js +343 -0
  11. package/src/auth/storageAuthProvider.js +393 -0
  12. package/src/controllers/apps.js +280 -0
  13. package/src/controllers/archive.js +217 -0
  14. package/src/controllers/auth.js +136 -0
  15. package/src/controllers/config.js +271 -0
  16. package/src/controllers/connections.js +486 -0
  17. package/src/controllers/databaseConnections.js +561 -0
  18. package/src/controllers/files.js +222 -0
  19. package/src/controllers/jsldata.js +296 -0
  20. package/src/controllers/metadata.js +47 -0
  21. package/src/controllers/plugins.js +216 -0
  22. package/src/controllers/queryHistory.js +54 -0
  23. package/src/controllers/runners.js +234 -0
  24. package/src/controllers/scheduler.js +46 -0
  25. package/src/controllers/serverConnections.js +271 -0
  26. package/src/controllers/sessions.js +243 -0
  27. package/src/controllers/storage.js +380 -0
  28. package/src/controllers/storageDb.js +215 -0
  29. package/src/controllers/uploads.js +133 -0
  30. package/src/currentVersion.js +5 -0
  31. package/src/gistSecret.js +2 -0
  32. package/src/index.js +139 -0
  33. package/src/main.js +202 -0
  34. package/src/packagedPluginsContent.js +1 -0
  35. package/src/proc/connectProcess.js +38 -0
  36. package/src/proc/databaseConnectionProcess.js +431 -0
  37. package/src/proc/index.js +15 -0
  38. package/src/proc/jslDatastoreProcess.js +60 -0
  39. package/src/proc/serverConnectionProcess.js +188 -0
  40. package/src/proc/sessionProcess.js +390 -0
  41. package/src/proc/sshForwardProcess.js +75 -0
  42. package/src/shell/archiveReader.js +11 -0
  43. package/src/shell/archiveWriter.js +22 -0
  44. package/src/shell/autoIndexForeignKeysTransform.js +19 -0
  45. package/src/shell/collectorWriter.js +33 -0
  46. package/src/shell/consoleObjectWriter.js +16 -0
  47. package/src/shell/copyStream.js +48 -0
  48. package/src/shell/dataDuplicator.js +63 -0
  49. package/src/shell/dataTypeMapperTransform.js +21 -0
  50. package/src/shell/dbModelToJson.js +16 -0
  51. package/src/shell/deployDb.js +56 -0
  52. package/src/shell/download.js +15 -0
  53. package/src/shell/dropAllDbObjects.js +42 -0
  54. package/src/shell/dumpDatabase.js +49 -0
  55. package/src/shell/executeQuery.js +39 -0
  56. package/src/shell/fakeObjectReader.js +35 -0
  57. package/src/shell/finalizer.js +12 -0
  58. package/src/shell/generateDeploySql.js +95 -0
  59. package/src/shell/generateModelSql.js +30 -0
  60. package/src/shell/importDatabase.js +85 -0
  61. package/src/shell/index.js +80 -0
  62. package/src/shell/initializeApiEnvironment.js +9 -0
  63. package/src/shell/jslDataReader.js +9 -0
  64. package/src/shell/jsonLinesReader.js +52 -0
  65. package/src/shell/jsonLinesWriter.js +36 -0
  66. package/src/shell/jsonReader.js +84 -0
  67. package/src/shell/jsonToDbModel.js +9 -0
  68. package/src/shell/jsonWriter.js +97 -0
  69. package/src/shell/loadDatabase.js +27 -0
  70. package/src/shell/loadFile.js +10 -0
  71. package/src/shell/modifyJsonLinesReader.js +148 -0
  72. package/src/shell/queryReader.js +30 -0
  73. package/src/shell/registerPlugins.js +9 -0
  74. package/src/shell/requirePlugin.js +43 -0
  75. package/src/shell/runScript.js +19 -0
  76. package/src/shell/sqlDataWriter.js +52 -0
  77. package/src/shell/sqlTextReplacementTransform.js +32 -0
  78. package/src/shell/tableReader.js +39 -0
  79. package/src/shell/tableWriter.js +18 -0
  80. package/src/storageModel.js +819 -0
  81. package/src/utility/ColumnMapTransformStream.js +21 -0
  82. package/src/utility/DatastoreProxy.js +106 -0
  83. package/src/utility/EnsureStreamHeaderStream.js +31 -0
  84. package/src/utility/JsonLinesDatabase.js +148 -0
  85. package/src/utility/JsonLinesDatastore.js +232 -0
  86. package/src/utility/LineReader.js +88 -0
  87. package/src/utility/SSHConnection.js +251 -0
  88. package/src/utility/authProxy.js +133 -0
  89. package/src/utility/checkLicense.js +186 -0
  90. package/src/utility/childProcessChecker.js +21 -0
  91. package/src/utility/cleanDirectory.js +24 -0
  92. package/src/utility/cloudUpgrade.js +61 -0
  93. package/src/utility/connectUtility.js +111 -0
  94. package/src/utility/crypting.js +105 -0
  95. package/src/utility/diff2htmlPage.js +8 -0
  96. package/src/utility/directories.js +179 -0
  97. package/src/utility/downloadPackage.js +51 -0
  98. package/src/utility/downloader.js +25 -0
  99. package/src/utility/exceptions.js +9 -0
  100. package/src/utility/exportDbModel.js +31 -0
  101. package/src/utility/exportDbModelSql.js +80 -0
  102. package/src/utility/getChartExport.js +55 -0
  103. package/src/utility/getDiagramExport.js +25 -0
  104. package/src/utility/getExpressPath.js +10 -0
  105. package/src/utility/getJslFileName.js +16 -0
  106. package/src/utility/getMapExport.js +77 -0
  107. package/src/utility/hardwareFingerprint.js +89 -0
  108. package/src/utility/hasPermission.js +101 -0
  109. package/src/utility/importDbModel.js +9 -0
  110. package/src/utility/loadFilesRecursive.js +20 -0
  111. package/src/utility/loadModelFolder.js +29 -0
  112. package/src/utility/loadModelTransform.js +36 -0
  113. package/src/utility/pipeForkLogs.js +19 -0
  114. package/src/utility/platformInfo.js +62 -0
  115. package/src/utility/processArgs.js +39 -0
  116. package/src/utility/processComm.js +18 -0
  117. package/src/utility/requireEngineDriver.js +26 -0
  118. package/src/utility/requirePluginFunction.js +16 -0
  119. package/src/utility/socket.js +68 -0
  120. package/src/utility/sshTunnel.js +106 -0
  121. package/src/utility/sshTunnelProxy.js +36 -0
  122. package/src/utility/timingSafeCheckToken.js +9 -0
  123. package/src/utility/useController.js +99 -0
  124. package/tsconfig.json +13 -0
  125. package/webpack.config.js +55 -0
@@ -0,0 +1,21 @@
1
+ const stream = require('stream');
2
+ const { transformRowUsingColumnMap } = require('dbgate-tools');
3
+
4
+ class ColumnMapTransformStream extends stream.Transform {
5
+ constructor(columns) {
6
+ super({ objectMode: true });
7
+ this.columns = columns;
8
+ }
9
+ _transform(chunk, encoding, done) {
10
+ if (chunk.__isStreamHeader) {
11
+ // skip stream header
12
+ done();
13
+ return;
14
+ }
15
+
16
+ this.push(transformRowUsingColumnMap(chunk, this.columns));
17
+ done();
18
+ }
19
+ }
20
+
21
+ module.exports = ColumnMapTransformStream;
@@ -0,0 +1,106 @@
1
+ const crypto = require('crypto');
2
+ const { fork } = require('child_process');
3
+ const { handleProcessCommunication } = require('./processComm');
4
+ const processArgs = require('../utility/processArgs');
5
+ const pipeForkLogs = require('./pipeForkLogs');
6
+ const { getLogger, extractErrorLogData } = require('dbgate-tools');
7
+ const logger = getLogger('DatastoreProxy');
8
+
9
+ class DatastoreProxy {
10
+ constructor(file) {
11
+ this.subprocess = null;
12
+ this.disconnected = false;
13
+ this.file = file;
14
+ this.requests = {};
15
+ this.handle_response = this.handle_response.bind(this);
16
+ this.handle_ping = this.handle_ping.bind(this);
17
+ this.notifyChangedCallback = null;
18
+ }
19
+
20
+ handle_response({ msgid, rows }) {
21
+ const [resolve, reject] = this.requests[msgid];
22
+ resolve(rows);
23
+ delete this.requests[msgid];
24
+ }
25
+
26
+ handle_ping() {}
27
+
28
+ handle_notify({ msgid }) {
29
+ const [resolve, reject] = this.requests[msgid];
30
+ resolve();
31
+ delete this.requests[msgid];
32
+ }
33
+
34
+ async ensureSubprocess() {
35
+ if (!this.subprocess) {
36
+ this.subprocess = fork(
37
+ global['API_PACKAGE'] || process.argv[1],
38
+ [
39
+ '--is-forked-api',
40
+ '--start-process',
41
+ 'jslDatastoreProcess',
42
+ ...processArgs.getPassArgs(),
43
+ // ...process.argv.slice(3),
44
+ ],
45
+ {
46
+ stdio: ['ignore', 'pipe', 'pipe', 'ipc'],
47
+ }
48
+ );
49
+ pipeForkLogs(this.subprocess);
50
+
51
+ this.subprocess.on('message', message => {
52
+ // @ts-ignore
53
+ const { msgtype } = message;
54
+ if (handleProcessCommunication(message, this.subprocess)) return;
55
+
56
+ // if (this.disconnected) return;
57
+ this[`handle_${msgtype}`](message);
58
+ });
59
+ this.subprocess.on('exit', () => {
60
+ // if (this.disconnected) return;
61
+ this.subprocess = null;
62
+ });
63
+ this.subprocess.send({ msgtype: 'open', file: this.file });
64
+ }
65
+ return this.subprocess;
66
+ }
67
+
68
+ async getRows(offset, limit) {
69
+ await this.ensureSubprocess();
70
+ const msgid = crypto.randomUUID();
71
+ const promise = new Promise((resolve, reject) => {
72
+ this.requests[msgid] = [resolve, reject];
73
+ try {
74
+ this.subprocess.send({ msgtype: 'read', msgid, offset, limit });
75
+ } catch (err) {
76
+ logger.error(extractErrorLogData(err), 'Error getting rows');
77
+ this.subprocess = null;
78
+ }
79
+ });
80
+ return promise;
81
+ }
82
+
83
+ async notifyChangedCore() {
84
+ const msgid = crypto.randomUUID();
85
+ const promise = new Promise((resolve, reject) => {
86
+ this.requests[msgid] = [resolve, reject];
87
+ try {
88
+ this.subprocess.send({ msgtype: 'notify', msgid });
89
+ } catch (err) {
90
+ logger.error(extractErrorLogData(err), 'Error notifying subprocess');
91
+ this.subprocess = null;
92
+ }
93
+ });
94
+ return promise;
95
+ }
96
+
97
+ async notifyChanged(callback) {
98
+ this.notifyChangedCallback = callback;
99
+ await this.notifyChangedCore();
100
+ const call = this.notifyChangedCallback;
101
+ this.notifyChangedCallback = null;
102
+ if (call) call();
103
+ }
104
+ }
105
+
106
+ module.exports = DatastoreProxy;
@@ -0,0 +1,31 @@
1
+ const stream = require('stream');
2
+
3
+ class EnsureStreamHeaderStream extends stream.Transform {
4
+ constructor() {
5
+ super({ objectMode: true });
6
+ this.wasHeader = false;
7
+ }
8
+ _transform(chunk, encoding, done) {
9
+ if (!this.wasHeader) {
10
+ if (chunk.__isDynamicStructure) {
11
+ // ignore dynamic structure header
12
+ done();
13
+ return;
14
+ }
15
+
16
+ if (!chunk.__isStreamHeader) {
17
+ this.push({
18
+ __isStreamHeader: true,
19
+ __isComputedStructure: true,
20
+ columns: Object.keys(chunk).map(columnName => ({ columnName })),
21
+ });
22
+ }
23
+
24
+ this.wasHeader = true;
25
+ }
26
+ this.push(chunk);
27
+ done();
28
+ }
29
+ }
30
+
31
+ module.exports = EnsureStreamHeaderStream;
@@ -0,0 +1,148 @@
1
+ const crypto = require('crypto');
2
+ const AsyncLock = require('async-lock');
3
+ const fs = require('fs-extra');
4
+
5
+ const lock = new AsyncLock();
6
+
7
+ // const lineReader = require('line-reader');
8
+ // const { fetchNextLineFromReader } = require('./JsonLinesDatastore');
9
+
10
+ class JsonLinesDatabase {
11
+ constructor(filename) {
12
+ this.filename = filename;
13
+ this.data = [];
14
+ this.loadedOk = false;
15
+ this.loadPerformed = false;
16
+ }
17
+
18
+ async _save() {
19
+ if (!this.loadedOk) {
20
+ // don't override data
21
+ return;
22
+ }
23
+ await fs.writeFile(this.filename, this.data.map(x => JSON.stringify(x)).join('\n'));
24
+ }
25
+
26
+ async _ensureLoaded() {
27
+ if (!this.loadPerformed) {
28
+ await lock.acquire('reader', async () => {
29
+ if (!this.loadPerformed) {
30
+ if (!(await fs.exists(this.filename))) {
31
+ this.loadedOk = true;
32
+ this.loadPerformed = true;
33
+ return;
34
+ }
35
+ try {
36
+ const text = await fs.readFile(this.filename, { encoding: 'utf-8' });
37
+ this.data = text
38
+ .split('\n')
39
+ .filter(x => x.trim())
40
+ .map(x => JSON.parse(x));
41
+ this.loadedOk = true;
42
+ } catch (err) {
43
+ console.error(`Error loading file ${this.filename}`, err);
44
+ }
45
+ this.loadPerformed = true;
46
+ }
47
+ });
48
+ }
49
+ }
50
+
51
+ async insert(obj) {
52
+ await this._ensureLoaded();
53
+ if (obj._id && (await this.get(obj._id))) {
54
+ throw new Error(`Cannot insert duplicate ID ${obj._id} into ${this.filename}`);
55
+ }
56
+ const elem = obj._id
57
+ ? obj
58
+ : {
59
+ ...obj,
60
+ _id: crypto.randomUUID(),
61
+ };
62
+ this.data.push(elem);
63
+ await this._save();
64
+ return elem;
65
+ }
66
+
67
+ async get(id) {
68
+ await this._ensureLoaded();
69
+ return this.data.find(x => x._id == id);
70
+ }
71
+
72
+ async find(cond) {
73
+ await this._ensureLoaded();
74
+ if (cond) {
75
+ return this.data.filter(x => {
76
+ for (const key of Object.keys(cond)) {
77
+ if (x[key] != cond[key]) return false;
78
+ }
79
+ return true;
80
+ });
81
+ } else {
82
+ return this.data;
83
+ }
84
+ }
85
+
86
+ async update(obj) {
87
+ await this._ensureLoaded();
88
+ this.data = this.data.map(x => (x._id == obj._id ? obj : x));
89
+ await this._save();
90
+ return obj;
91
+ }
92
+
93
+ async updateAll(mapFunction) {
94
+ await this._ensureLoaded();
95
+ this.data = this.data.map(mapFunction);
96
+ await this._save();
97
+ }
98
+
99
+ async patch(id, values) {
100
+ await this._ensureLoaded();
101
+ this.data = this.data.map(x => (x._id == id ? { ...x, ...values } : x));
102
+ await this._save();
103
+ return this.data.find(x => x._id == id);
104
+ }
105
+
106
+ async remove(id) {
107
+ await this._ensureLoaded();
108
+ const removed = this.data.find(x => x._id == id);
109
+ this.data = this.data.filter(x => x._id != id);
110
+ await this._save();
111
+ return removed;
112
+ }
113
+
114
+ // async _openReader() {
115
+ // return new Promise((resolve, reject) =>
116
+ // lineReader.open(this.filename, (err, reader) => {
117
+ // if (err) reject(err);
118
+ // resolve(reader);
119
+ // })
120
+ // );
121
+ // }
122
+
123
+ // async _read() {
124
+ // this.data = [];
125
+ // if (!(await fs.exists(this.filename))) return;
126
+ // try {
127
+ // const reader = await this._openReader();
128
+ // for (;;) {
129
+ // const line = await fetchNextLineFromReader(reader);
130
+ // if (!line) break;
131
+ // this.data.push(JSON.parse(line));
132
+ // }
133
+ // } catch (err) {
134
+ // console.error(`Error loading file ${this.filename}`, err);
135
+ // }
136
+ // }
137
+
138
+ // async _write() {
139
+ // const fw = fs.createWriteStream(this.filename);
140
+ // for (const obj of this.data) {
141
+ // await fw.write(JSON.stringify(obj));
142
+ // await fw.write('\n');
143
+ // }
144
+ // await fw.end();
145
+ // }
146
+ }
147
+
148
+ module.exports = JsonLinesDatabase;
@@ -0,0 +1,232 @@
1
+ const crypto = require('crypto');
2
+ const fs = require('fs');
3
+ const os = require('os');
4
+ const rimraf = require('rimraf');
5
+ const path = require('path');
6
+ const AsyncLock = require('async-lock');
7
+ const lock = new AsyncLock();
8
+ const stableStringify = require('json-stable-stringify');
9
+ const { evaluateCondition } = require('dbgate-sqltree');
10
+ const requirePluginFunction = require('./requirePluginFunction');
11
+ const esort = require('external-sorting');
12
+ const { jsldir } = require('./directories');
13
+ const LineReader = require('./LineReader');
14
+
15
+ class JsonLinesDatastore {
16
+ constructor(file, formatterFunction) {
17
+ this.file = file;
18
+ this.formatterFunction = formatterFunction;
19
+ this.reader = null;
20
+ this.readedDataRowCount = 0;
21
+ this.readedSchemaRow = false;
22
+ // this.firstRowToBeReturned = null;
23
+ this.notifyChangedCallback = null;
24
+ this.currentFilter = null;
25
+ this.currentSort = null;
26
+ this.rowFormatter = requirePluginFunction(formatterFunction);
27
+ this.sortedFiles = {};
28
+ }
29
+
30
+ static async sortFile(infile, outfile, sort) {
31
+ const tempDir = path.join(os.tmpdir(), crypto.randomUUID());
32
+ fs.mkdirSync(tempDir);
33
+
34
+ await esort
35
+ .default({
36
+ input: fs.createReadStream(infile),
37
+ output: fs.createWriteStream(outfile),
38
+ deserializer: JSON.parse,
39
+ serializer: JSON.stringify,
40
+ tempDir,
41
+ maxHeap: 100,
42
+ comparer: (a, b) => {
43
+ for (const item of sort) {
44
+ const { uniqueName, order } = item;
45
+ if (a[uniqueName] < b[uniqueName]) {
46
+ return order == 'ASC' ? -1 : 1;
47
+ }
48
+ if (a[uniqueName] > b[uniqueName]) {
49
+ return order == 'ASC' ? 1 : -1;
50
+ }
51
+ }
52
+ return 0;
53
+ },
54
+ })
55
+ .asc();
56
+
57
+ await new Promise(resolve => rimraf(tempDir, resolve));
58
+ }
59
+
60
+ async _closeReader() {
61
+ // console.log('CLOSING READER', this.reader);
62
+ if (!this.reader) return;
63
+ const reader = this.reader;
64
+ this.reader = null;
65
+ this.readedDataRowCount = 0;
66
+ this.readedSchemaRow = false;
67
+ // this.firstRowToBeReturned = null;
68
+ this.currentFilter = null;
69
+ this.currentSort = null;
70
+ await reader.close();
71
+ }
72
+
73
+ async notifyChanged(callback) {
74
+ this.notifyChangedCallback = callback;
75
+ await lock.acquire('reader', async () => {
76
+ this._closeReader();
77
+ });
78
+ const call = this.notifyChangedCallback;
79
+ this.notifyChangedCallback = null;
80
+ if (call) call();
81
+ }
82
+
83
+ async _openReader(fileName) {
84
+ // console.log('OPENING READER', fileName);
85
+ // console.log(fs.readFileSync(fileName, 'utf-8'));
86
+
87
+ const fileStream = fs.createReadStream(fileName);
88
+ return new LineReader(fileStream);
89
+ }
90
+
91
+ parseLine(line) {
92
+ const res = JSON.parse(line);
93
+ return this.rowFormatter ? this.rowFormatter(res) : res;
94
+ }
95
+
96
+ async _readLine(parse) {
97
+ // if (this.firstRowToBeReturned) {
98
+ // const res = this.firstRowToBeReturned;
99
+ // this.firstRowToBeReturned = null;
100
+ // return res;
101
+ // }
102
+ for (;;) {
103
+ const line = await this.reader.readLine();
104
+ if (!line) {
105
+ // EOF
106
+ return null;
107
+ }
108
+
109
+ if (!this.readedSchemaRow) {
110
+ this.readedSchemaRow = true;
111
+ const parsedLine = JSON.parse(line);
112
+ if (parsedLine.__isStreamHeader) {
113
+ // skip to next line
114
+ continue;
115
+ }
116
+ }
117
+ if (this.currentFilter) {
118
+ const parsedLine = this.parseLine(line);
119
+ if (evaluateCondition(this.currentFilter, parsedLine)) {
120
+ this.readedDataRowCount += 1;
121
+ return parse ? parsedLine : true;
122
+ }
123
+ } else {
124
+ this.readedDataRowCount += 1;
125
+ return parse ? this.parseLine(line) : true;
126
+ }
127
+ }
128
+
129
+ // return new Promise((resolve, reject) => {
130
+ // const reader = this.reader;
131
+ // if (!reader.hasNextLine()) {
132
+ // resolve(null);
133
+ // return;
134
+ // }
135
+
136
+ // reader.nextLine((err, line) => {
137
+ // if (err) {
138
+ // reject(err);
139
+ // return;
140
+ // }
141
+ // if (!this.readedSchemaRow) {
142
+ // this.readedSchemaRow = true;
143
+ // resolve(true);
144
+ // return;
145
+ // }
146
+ // if (this.currentFilter) {
147
+ // const parsedLine = JSON.parse(line);
148
+ // if (evaluateCondition(this.currentFilter, parsedLine)) {
149
+ // console.log('TRUE');
150
+ // resolve(parse ? parsedLine : true);
151
+ // this.readedDataRowCount += 1;
152
+ // return;
153
+ // } else {
154
+ // console.log('FALSE');
155
+ // // skip row
156
+ // return;
157
+ // }
158
+ // }
159
+
160
+ // this.readedDataRowCount += 1;
161
+ // resolve(parse ? JSON.parse(line) : true);
162
+ // });
163
+ // });
164
+ }
165
+
166
+ async _ensureReader(offset, filter, sort) {
167
+ if (
168
+ this.readedDataRowCount > offset ||
169
+ stableStringify(filter) != stableStringify(this.currentFilter) ||
170
+ stableStringify(sort) != stableStringify(this.currentSort)
171
+ ) {
172
+ this._closeReader();
173
+ }
174
+ if (!this.reader) {
175
+ const reader = await this._openReader(sort ? this.sortedFiles[stableStringify(sort)] : this.file);
176
+ this.reader = reader;
177
+ this.currentFilter = filter;
178
+ this.currentSort = sort;
179
+ }
180
+ // if (!this.readedSchemaRow) {
181
+ // const line = await this._readLine(true); // skip structure
182
+ // if (!line.__isStreamHeader) {
183
+ // // line contains data
184
+ // this.firstRowToBeReturned = line;
185
+ // }
186
+ // }
187
+ while (this.readedDataRowCount < offset) {
188
+ const line = await this._readLine(false);
189
+ if (line == null) break;
190
+ // if (this.firstRowToBeReturned) {
191
+ // this.firstRowToBeReturned = null;
192
+ // } else {
193
+ // await this._readLine(false);
194
+ // }
195
+ }
196
+ }
197
+
198
+ async enumRows(eachRow) {
199
+ await lock.acquire('reader', async () => {
200
+ await this._ensureReader(0, null);
201
+ for (;;) {
202
+ const line = await this._readLine(true);
203
+ if (line == null) break;
204
+ const shouldContinue = eachRow(line);
205
+ if (!shouldContinue) break;
206
+ }
207
+ });
208
+ }
209
+
210
+ async getRows(offset, limit, filter, sort) {
211
+ const res = [];
212
+ if (sort && !this.sortedFiles[stableStringify(sort)]) {
213
+ const jslid = crypto.randomUUID();
214
+ const sortedFile = path.join(jsldir(), `${jslid}.jsonl`);
215
+ await JsonLinesDatastore.sortFile(this.file, sortedFile, sort);
216
+ this.sortedFiles[stableStringify(sort)] = sortedFile;
217
+ }
218
+ await lock.acquire('reader', async () => {
219
+ await this._ensureReader(offset, filter, sort);
220
+ // console.log(JSON.stringify(this.currentFilter, undefined, 2));
221
+ for (let i = 0; i < limit; i += 1) {
222
+ const line = await this._readLine(true);
223
+ // console.log('READED LINE', i);
224
+ if (line == null) break;
225
+ res.push(line);
226
+ }
227
+ });
228
+ return res;
229
+ }
230
+ }
231
+
232
+ module.exports = JsonLinesDatastore;
@@ -0,0 +1,88 @@
1
+ const readline = require('readline');
2
+
3
+ class Queue {
4
+ constructor() {
5
+ this.elements = {};
6
+ this.head = 0;
7
+ this.tail = 0;
8
+ }
9
+ enqueue(element) {
10
+ this.elements[this.tail] = element;
11
+ this.tail++;
12
+ }
13
+ dequeue() {
14
+ const item = this.elements[this.head];
15
+ delete this.elements[this.head];
16
+ this.head++;
17
+ return item;
18
+ }
19
+ peek() {
20
+ return this.elements[this.head];
21
+ }
22
+ getLength() {
23
+ return this.tail - this.head;
24
+ }
25
+ isEmpty() {
26
+ return this.getLength() === 0;
27
+ }
28
+ }
29
+
30
+ class LineReader {
31
+ constructor(input) {
32
+ this.input = input;
33
+ this.queue = new Queue();
34
+ this.resolve = null;
35
+ this.isEnded = false;
36
+ this.rl = readline.createInterface({
37
+ input,
38
+ });
39
+ this.input.pause();
40
+
41
+ this.rl.on('line', line => {
42
+ this.input.pause();
43
+ if (this.resolve) {
44
+ const resolve = this.resolve;
45
+ this.resolve = null;
46
+ resolve(line);
47
+ return;
48
+ }
49
+ this.queue.enqueue(line);
50
+ });
51
+
52
+ this.rl.on('close', () => {
53
+ if (this.resolve) {
54
+ const resolve = this.resolve;
55
+ this.resolve = null;
56
+ this.isEnded = true;
57
+ resolve(null);
58
+ return;
59
+ }
60
+ this.queue.enqueue(null);
61
+ });
62
+ }
63
+
64
+ readLine() {
65
+ if (this.isEnded) {
66
+ return Promise.resolve(null);
67
+ }
68
+
69
+ if (!this.queue.isEmpty()) {
70
+ const res = this.queue.dequeue();
71
+ if (res == null) this.isEnded = true;
72
+ return Promise.resolve(res);
73
+ }
74
+
75
+ this.input.resume();
76
+
77
+ return new Promise(resolve => {
78
+ this.resolve = resolve;
79
+ });
80
+ }
81
+
82
+ close() {
83
+ this.isEnded = true;
84
+ return new Promise(resolve => this.input.close(resolve));
85
+ }
86
+ }
87
+
88
+ module.exports = LineReader;