inibase 1.0.0-rc.101 → 1.0.0-rc.102
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/file.d.ts +1 -1
- package/dist/file.js +123 -54
- package/dist/index.d.ts +1 -2
- package/dist/index.js +114 -122
- package/package.json +1 -1
package/dist/file.d.ts
CHANGED
|
@@ -58,7 +58,7 @@ export declare function get(filePath: string, lineNumbers: undefined | number |
|
|
|
58
58
|
*
|
|
59
59
|
* Note: If the file doesn't exist and replacements is an object, it creates a new file with the specified replacements.
|
|
60
60
|
*/
|
|
61
|
-
export declare const replace: (filePath: string, replacements: string | number | boolean | null | (string | number | boolean | null)[] | Record<number, string | boolean | number | null | (string | boolean | number | null)[]
|
|
61
|
+
export declare const replace: (filePath: string, replacements: string | number | boolean | null | (string | number | boolean | null)[] | Record<number, string | boolean | number | null | (string | boolean | number | null)[]>, totalItems?: number) => Promise<string[]>;
|
|
62
62
|
/**
|
|
63
63
|
* Asynchronously appends data to the end of a file.
|
|
64
64
|
*
|
package/dist/file.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { createWriteStream } from "node:fs";
|
|
1
2
|
import { access, appendFile, copyFile, constants as fsConstants, open, readFile, unlink, writeFile, } from "node:fs/promises";
|
|
2
3
|
import { join } from "node:path";
|
|
3
4
|
import { createInterface } from "node:readline";
|
|
@@ -7,6 +8,7 @@ import { createGunzip, createGzip } from "node:zlib";
|
|
|
7
8
|
import Inison from "inison";
|
|
8
9
|
import { detectFieldType, isArrayOfObjects, isStringified, isNumber, isObject, } from "./utils.js";
|
|
9
10
|
import { compare, encodeID, exec, gunzip, gzip } from "./utils.server.js";
|
|
11
|
+
import { spawn } from "node:child_process";
|
|
10
12
|
export const lock = async (folderPath, prefix) => {
|
|
11
13
|
let lockFile = null;
|
|
12
14
|
const lockFilePath = join(folderPath, `${prefix ?? ""}.locked`);
|
|
@@ -239,48 +241,93 @@ export async function get(filePath, lineNumbers, fieldType, fieldChildrenType, s
|
|
|
239
241
|
*
|
|
240
242
|
* Note: If the file doesn't exist and replacements is an object, it creates a new file with the specified replacements.
|
|
241
243
|
*/
|
|
242
|
-
export const replace = async (filePath, replacements) => {
|
|
244
|
+
export const replace = async (filePath, replacements, totalItems) => {
|
|
243
245
|
const fileTempPath = filePath.replace(/([^/]+)\/?$/, ".tmp/$1");
|
|
246
|
+
const isReplacementsObject = isObject(replacements);
|
|
247
|
+
const isReplacementsObjectHasLineNumbers = isReplacementsObject && !Number.isNaN(Number(Object.keys(replacements)[0]));
|
|
244
248
|
if (await isExists(filePath)) {
|
|
245
|
-
|
|
246
|
-
|
|
249
|
+
if (isReplacementsObjectHasLineNumbers) {
|
|
250
|
+
let fileHandle = null;
|
|
251
|
+
let fileTempHandle = null;
|
|
252
|
+
try {
|
|
253
|
+
let linesCount = 0;
|
|
254
|
+
fileHandle = await open(filePath, "r");
|
|
255
|
+
fileTempHandle = await open(fileTempPath, "w");
|
|
256
|
+
const rl = createReadLineInternface(filePath, fileHandle);
|
|
257
|
+
await _pipeline(filePath, rl, fileTempHandle.createWriteStream(), new Transform({
|
|
258
|
+
transform(line, _, callback) {
|
|
259
|
+
linesCount++;
|
|
260
|
+
const replacement = isReplacementsObject
|
|
261
|
+
? Object.hasOwn(replacements, linesCount)
|
|
262
|
+
? replacements[linesCount]
|
|
263
|
+
: line
|
|
264
|
+
: replacements;
|
|
265
|
+
return callback(null, `${replacement}\n`);
|
|
266
|
+
},
|
|
267
|
+
}));
|
|
268
|
+
return [fileTempPath, filePath];
|
|
269
|
+
}
|
|
270
|
+
catch {
|
|
271
|
+
return [fileTempPath, null];
|
|
272
|
+
}
|
|
273
|
+
finally {
|
|
274
|
+
// Ensure that file handles are closed, even if an error occurred
|
|
275
|
+
await fileHandle?.close();
|
|
276
|
+
await fileTempHandle?.close();
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
else {
|
|
280
|
+
return new Promise((resolve) => {
|
|
281
|
+
const sedProcess = spawn("sed", [
|
|
282
|
+
"-e",
|
|
283
|
+
`s/.*/${replacements}/`,
|
|
284
|
+
"-e",
|
|
285
|
+
`/^$/s/^/${replacements}/`,
|
|
286
|
+
filePath,
|
|
287
|
+
]);
|
|
288
|
+
const outputStream = createWriteStream(fileTempPath); // Temp file for output
|
|
289
|
+
// Pipe sed output to the temporary file
|
|
290
|
+
sedProcess.stdout.pipe(outputStream);
|
|
291
|
+
// Handle the process close
|
|
292
|
+
sedProcess.on("close", (code) => {
|
|
293
|
+
if (code === 0)
|
|
294
|
+
resolve([fileTempPath, filePath]);
|
|
295
|
+
else
|
|
296
|
+
resolve([fileTempPath, null]);
|
|
297
|
+
});
|
|
298
|
+
// Handle errors in spawning the sed process
|
|
299
|
+
sedProcess.on("error", () => {
|
|
300
|
+
resolve([fileTempPath, null]);
|
|
301
|
+
});
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
else if (isReplacementsObject) {
|
|
247
306
|
try {
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
?
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
307
|
+
if (isReplacementsObjectHasLineNumbers) {
|
|
308
|
+
const replacementsKeys = Object.keys(replacements)
|
|
309
|
+
.map(Number)
|
|
310
|
+
.toSorted((a, b) => a - b);
|
|
311
|
+
await write(fileTempPath, `${"\n".repeat(replacementsKeys[0] - 1) +
|
|
312
|
+
replacementsKeys
|
|
313
|
+
.map((lineNumber, index) => index === 0 ||
|
|
314
|
+
lineNumber - replacementsKeys[index - 1] - 1 === 0
|
|
315
|
+
? replacements[lineNumber]
|
|
316
|
+
: "\n".repeat(lineNumber - replacementsKeys[index - 1] - 1) +
|
|
317
|
+
replacements[lineNumber])
|
|
318
|
+
.join("\n")}\n`);
|
|
319
|
+
}
|
|
320
|
+
else {
|
|
321
|
+
if (!totalItems)
|
|
322
|
+
throw new Error("INVALID_PARAMETERS");
|
|
323
|
+
await write(fileTempPath, `${`${replacements}\n`.repeat(totalItems)}\n`);
|
|
324
|
+
}
|
|
263
325
|
return [fileTempPath, filePath];
|
|
264
326
|
}
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
await fileHandle?.close();
|
|
268
|
-
await fileTempHandle?.close();
|
|
327
|
+
catch {
|
|
328
|
+
return [fileTempPath, null];
|
|
269
329
|
}
|
|
270
330
|
}
|
|
271
|
-
else if (isObject(replacements)) {
|
|
272
|
-
const replacementsKeys = Object.keys(replacements)
|
|
273
|
-
.map(Number)
|
|
274
|
-
.toSorted((a, b) => a - b);
|
|
275
|
-
await write(fileTempPath, `${"\n".repeat(replacementsKeys[0] - 1) +
|
|
276
|
-
replacementsKeys
|
|
277
|
-
.map((lineNumber, index) => index === 0 || lineNumber - replacementsKeys[index - 1] - 1 === 0
|
|
278
|
-
? replacements[lineNumber]
|
|
279
|
-
: "\n".repeat(lineNumber - replacementsKeys[index - 1] - 1) +
|
|
280
|
-
replacements[lineNumber])
|
|
281
|
-
.join("\n")}\n`);
|
|
282
|
-
return [fileTempPath, filePath];
|
|
283
|
-
}
|
|
284
331
|
return [];
|
|
285
332
|
};
|
|
286
333
|
/**
|
|
@@ -293,20 +340,25 @@ export const replace = async (filePath, replacements) => {
|
|
|
293
340
|
*/
|
|
294
341
|
export const append = async (filePath, data) => {
|
|
295
342
|
const fileTempPath = filePath.replace(/([^/]+)\/?$/, ".tmp/$1");
|
|
296
|
-
|
|
297
|
-
await
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
.
|
|
304
|
-
|
|
343
|
+
try {
|
|
344
|
+
if (await isExists(filePath)) {
|
|
345
|
+
await copyFile(filePath, fileTempPath);
|
|
346
|
+
if (!filePath.endsWith(".gz")) {
|
|
347
|
+
await appendFile(fileTempPath, `${Array.isArray(data) ? data.join("\n") : data}\n`);
|
|
348
|
+
}
|
|
349
|
+
else {
|
|
350
|
+
await exec(`echo $'${(Array.isArray(data) ? data.join("\n") : data)
|
|
351
|
+
.toString()
|
|
352
|
+
.replace(/'/g, "\\'")}' | gzip - >> ${fileTempPath}`);
|
|
353
|
+
}
|
|
305
354
|
}
|
|
355
|
+
else
|
|
356
|
+
await write(fileTempPath, `${Array.isArray(data) ? data.join("\n") : data}\n`);
|
|
357
|
+
return [fileTempPath, filePath];
|
|
358
|
+
}
|
|
359
|
+
catch {
|
|
360
|
+
return [fileTempPath, null];
|
|
306
361
|
}
|
|
307
|
-
else
|
|
308
|
-
await write(fileTempPath, `${Array.isArray(data) ? data.join("\n") : data}\n`);
|
|
309
|
-
return [fileTempPath, filePath];
|
|
310
362
|
};
|
|
311
363
|
/**
|
|
312
364
|
* Asynchronously prepends data to the beginning of a file.
|
|
@@ -337,6 +389,9 @@ export const prepend = async (filePath, data) => {
|
|
|
337
389
|
},
|
|
338
390
|
}));
|
|
339
391
|
}
|
|
392
|
+
catch {
|
|
393
|
+
return [fileTempPath, null];
|
|
394
|
+
}
|
|
340
395
|
finally {
|
|
341
396
|
// Ensure that file handles are closed, even if an error occurred
|
|
342
397
|
await fileHandle?.close();
|
|
@@ -349,13 +404,22 @@ export const prepend = async (filePath, data) => {
|
|
|
349
404
|
await write(fileChildTempPath, `${Array.isArray(data) ? data.join("\n") : data}\n`);
|
|
350
405
|
await exec(`cat ${fileChildTempPath} ${filePath} > ${fileTempPath}`);
|
|
351
406
|
}
|
|
407
|
+
catch {
|
|
408
|
+
return [fileTempPath, null];
|
|
409
|
+
}
|
|
352
410
|
finally {
|
|
353
411
|
await unlink(fileChildTempPath);
|
|
354
412
|
}
|
|
355
413
|
}
|
|
356
414
|
}
|
|
357
|
-
else
|
|
358
|
-
|
|
415
|
+
else {
|
|
416
|
+
try {
|
|
417
|
+
await write(fileTempPath, `${Array.isArray(data) ? data.join("\n") : data}\n`);
|
|
418
|
+
}
|
|
419
|
+
catch {
|
|
420
|
+
return [fileTempPath, null];
|
|
421
|
+
}
|
|
422
|
+
}
|
|
359
423
|
return [fileTempPath, filePath];
|
|
360
424
|
};
|
|
361
425
|
/**
|
|
@@ -374,11 +438,16 @@ export const remove = async (filePath, linesToDelete) => {
|
|
|
374
438
|
if (linesToDelete.some(Number.isNaN))
|
|
375
439
|
throw new Error("UNVALID_LINE_NUMBERS");
|
|
376
440
|
const fileTempPath = filePath.replace(/([^/]+)\/?$/, ".tmp/$1");
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
441
|
+
try {
|
|
442
|
+
const command = filePath.endsWith(".gz")
|
|
443
|
+
? `zcat ${filePath} | sed "${linesToDelete.join("d;")}d" | gzip > ${fileTempPath}`
|
|
444
|
+
: `sed "${linesToDelete.join("d;")}d" ${filePath} > ${fileTempPath}`;
|
|
445
|
+
await exec(command);
|
|
446
|
+
return [fileTempPath, filePath];
|
|
447
|
+
}
|
|
448
|
+
catch {
|
|
449
|
+
return [fileTempPath, null];
|
|
450
|
+
}
|
|
382
451
|
};
|
|
383
452
|
/**
|
|
384
453
|
* Asynchronously searches a file for lines matching specified criteria, using comparison and logical operators.
|
package/dist/index.d.ts
CHANGED
|
@@ -49,7 +49,7 @@ declare global {
|
|
|
49
49
|
entries<T extends object>(o: T): Entries<T>;
|
|
50
50
|
}
|
|
51
51
|
}
|
|
52
|
-
export type ErrorCodes = "FIELD_UNIQUE" | "FIELD_REQUIRED" | "NO_SCHEMA" | "
|
|
52
|
+
export type ErrorCodes = "FIELD_UNIQUE" | "FIELD_REQUIRED" | "NO_SCHEMA" | "TABLE_EMPTY" | "INVALID_ID" | "INVALID_TYPE" | "INVALID_PARAMETERS" | "NO_ENV" | "TABLE_EXISTS" | "TABLE_NOT_EXISTS";
|
|
53
53
|
export type ErrorLang = "en";
|
|
54
54
|
export default class Inibase {
|
|
55
55
|
pageInfo: Record<string, pageInfo>;
|
|
@@ -72,7 +72,6 @@ export default class Inibase {
|
|
|
72
72
|
*/
|
|
73
73
|
createTable(tableName: string, schema?: Schema, config?: Config): Promise<void>;
|
|
74
74
|
private replaceStringInFile;
|
|
75
|
-
private replaceStringInSchemas;
|
|
76
75
|
/**
|
|
77
76
|
* Update table schema or config
|
|
78
77
|
*
|
package/dist/index.js
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import "dotenv/config";
|
|
2
2
|
import { randomBytes, scryptSync } from "node:crypto";
|
|
3
3
|
import { appendFileSync, existsSync, readFileSync } from "node:fs";
|
|
4
|
-
import { mkdir, readFile, readdir, rename,
|
|
4
|
+
import { glob, mkdir, readFile, readdir, rename, rm, unlink, writeFile, } from "node:fs/promises";
|
|
5
5
|
import { join, parse } from "node:path";
|
|
6
6
|
import { inspect } from "node:util";
|
|
7
7
|
import Inison from "inison";
|
|
8
8
|
import * as File from "./file.js";
|
|
9
9
|
import * as Utils from "./utils.js";
|
|
10
10
|
import * as UtilsServer from "./utils.server.js";
|
|
11
|
+
// hide ExperimentalWarning glob()
|
|
12
|
+
process.removeAllListeners("warning");
|
|
11
13
|
export default class Inibase {
|
|
12
14
|
pageInfo;
|
|
13
15
|
salt;
|
|
@@ -35,12 +37,12 @@ export default class Inibase {
|
|
|
35
37
|
Error(code, variable, language = "en") {
|
|
36
38
|
const errorMessages = {
|
|
37
39
|
en: {
|
|
38
|
-
|
|
39
|
-
FIELD_REQUIRED: "Field {variable} is required",
|
|
40
|
+
TABLE_EMPTY: "Table {variable} is empty",
|
|
40
41
|
TABLE_EXISTS: "Table {variable} already exists",
|
|
41
42
|
TABLE_NOT_EXISTS: "Table {variable} doesn't exist",
|
|
42
43
|
NO_SCHEMA: "Table {variable} does't have a schema",
|
|
43
|
-
|
|
44
|
+
FIELD_UNIQUE: "Field {variable} should be unique, got {variable} instead",
|
|
45
|
+
FIELD_REQUIRED: "Field {variable} is required",
|
|
44
46
|
INVALID_ID: "The given ID(s) is/are not valid(s)",
|
|
45
47
|
INVALID_TYPE: "Expect {variable} to be {variable}, got {variable} instead",
|
|
46
48
|
INVALID_PARAMETERS: "The given parameters are not valid",
|
|
@@ -108,8 +110,14 @@ export default class Inibase {
|
|
|
108
110
|
if (config.prepend)
|
|
109
111
|
await writeFile(join(tablePath, ".prepend.config"), "");
|
|
110
112
|
}
|
|
111
|
-
if (schema)
|
|
112
|
-
|
|
113
|
+
if (schema) {
|
|
114
|
+
const lastSchemaId = 0;
|
|
115
|
+
await writeFile(join(tablePath, "schema.json"), JSON.stringify(UtilsServer.addIdToSchema(schema, lastSchemaId, this.salt), null, 2));
|
|
116
|
+
await writeFile(join(tablePath, `${lastSchemaId}.schema`), "");
|
|
117
|
+
}
|
|
118
|
+
else
|
|
119
|
+
await writeFile(join(tablePath, "0.schema"), "");
|
|
120
|
+
await writeFile(join(tablePath, "0-0.pagination"), "");
|
|
113
121
|
}
|
|
114
122
|
// Function to replace the string in one schema.json file
|
|
115
123
|
async replaceStringInFile(filePath, targetString, replaceString) {
|
|
@@ -119,20 +127,6 @@ export default class Inibase {
|
|
|
119
127
|
await writeFile(filePath, updatedContent, "utf8");
|
|
120
128
|
}
|
|
121
129
|
}
|
|
122
|
-
// Function to process schema files one by one (sequentially)
|
|
123
|
-
async replaceStringInSchemas(directoryPath, targetString, replaceString) {
|
|
124
|
-
const files = await readdir(directoryPath);
|
|
125
|
-
for (const file of files) {
|
|
126
|
-
const fullPath = join(directoryPath, file);
|
|
127
|
-
const fileStat = await stat(fullPath);
|
|
128
|
-
if (fileStat.isDirectory()) {
|
|
129
|
-
await this.replaceStringInSchemas(fullPath, targetString, replaceString);
|
|
130
|
-
}
|
|
131
|
-
else if (file === "schema.json") {
|
|
132
|
-
await this.replaceStringInFile(fullPath, targetString, replaceString);
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
130
|
/**
|
|
137
131
|
* Update table schema or config
|
|
138
132
|
*
|
|
@@ -145,11 +139,13 @@ export default class Inibase {
|
|
|
145
139
|
if (schema) {
|
|
146
140
|
// remove id from schema
|
|
147
141
|
schema = schema.filter(({ key }) => !["id", "createdAt", "updatedAt"].includes(key));
|
|
142
|
+
let schemaIdFilePath;
|
|
143
|
+
for await (const filePath of glob("*.schema", { cwd: this.databasePath }))
|
|
144
|
+
schemaIdFilePath = filePath;
|
|
145
|
+
const lastSchemaId = Number(parse(schemaIdFilePath).name);
|
|
148
146
|
if (await File.isExists(join(tablePath, "schema.json"))) {
|
|
149
147
|
// update columns files names based on field id
|
|
150
|
-
schema = UtilsServer.addIdToSchema(schema,
|
|
151
|
-
? UtilsServer.findLastIdNumber(table.schema, this.salt)
|
|
152
|
-
: 0, this.salt);
|
|
148
|
+
schema = UtilsServer.addIdToSchema(schema, lastSchemaId, this.salt);
|
|
153
149
|
if (table.schema?.length) {
|
|
154
150
|
const replaceOldPathes = Utils.findChangedProperties(this._schemaToIdsPath(tableName, table.schema), this._schemaToIdsPath(tableName, schema));
|
|
155
151
|
if (replaceOldPathes)
|
|
@@ -160,8 +156,9 @@ export default class Inibase {
|
|
|
160
156
|
}
|
|
161
157
|
}
|
|
162
158
|
else
|
|
163
|
-
schema = UtilsServer.addIdToSchema(schema,
|
|
159
|
+
schema = UtilsServer.addIdToSchema(schema, lastSchemaId, this.salt);
|
|
164
160
|
await writeFile(join(tablePath, "schema.json"), JSON.stringify(schema, null, 2));
|
|
161
|
+
await rename(schemaIdFilePath, join(tablePath, `${lastSchemaId}.schema`));
|
|
165
162
|
}
|
|
166
163
|
if (config) {
|
|
167
164
|
if (config.compression !== undefined &&
|
|
@@ -186,8 +183,10 @@ export default class Inibase {
|
|
|
186
183
|
if (config.cache !== undefined && config.cache !== table.config.cache) {
|
|
187
184
|
if (config.cache)
|
|
188
185
|
await writeFile(join(tablePath, ".cache.config"), "");
|
|
189
|
-
else
|
|
186
|
+
else {
|
|
187
|
+
await this.clearCache(tableName);
|
|
190
188
|
await unlink(join(tablePath, ".cache.config"));
|
|
189
|
+
}
|
|
191
190
|
}
|
|
192
191
|
if (config.prepend !== undefined &&
|
|
193
192
|
config.prepend !== table.config.prepend) {
|
|
@@ -213,8 +212,12 @@ export default class Inibase {
|
|
|
213
212
|
await unlink(join(tablePath, ".prepend.config"));
|
|
214
213
|
}
|
|
215
214
|
if (config.name) {
|
|
216
|
-
await this.replaceStringInSchemas(this.databasePath, `"table": "${tableName}"`, `"table": "${config.name}"`);
|
|
217
215
|
await rename(tablePath, join(this.databasePath, config.name));
|
|
216
|
+
// replace table name in other linked tables (relationship)
|
|
217
|
+
for await (const schemaPath of glob("**/schema.json", {
|
|
218
|
+
cwd: this.databasePath,
|
|
219
|
+
}))
|
|
220
|
+
await this.replaceStringInFile(schemaPath, `"table": "${tableName}"`, `"table": "${config.name}"`);
|
|
218
221
|
}
|
|
219
222
|
}
|
|
220
223
|
delete this.tables[tableName];
|
|
@@ -241,14 +244,13 @@ export default class Inibase {
|
|
|
241
244
|
return this.tables[tableName];
|
|
242
245
|
}
|
|
243
246
|
async getTableSchema(tableName, encodeIDs = true) {
|
|
244
|
-
const
|
|
245
|
-
if (!(await File.isExists(
|
|
247
|
+
const tablePath = join(this.databasePath, tableName);
|
|
248
|
+
if (!(await File.isExists(join(tablePath, "schema.json"))))
|
|
246
249
|
return undefined;
|
|
247
|
-
const schemaFile = await readFile(
|
|
250
|
+
const schemaFile = await readFile(join(tablePath, "schema.json"), "utf8");
|
|
248
251
|
if (!schemaFile)
|
|
249
252
|
return undefined;
|
|
250
253
|
let schema = JSON.parse(schemaFile);
|
|
251
|
-
const lastIdNumber = UtilsServer.findLastIdNumber(schema, this.salt);
|
|
252
254
|
schema = [
|
|
253
255
|
{
|
|
254
256
|
id: 0,
|
|
@@ -256,19 +258,19 @@ export default class Inibase {
|
|
|
256
258
|
type: "id",
|
|
257
259
|
required: true,
|
|
258
260
|
},
|
|
259
|
-
...schema,
|
|
260
261
|
{
|
|
261
|
-
id:
|
|
262
|
+
id: -1,
|
|
262
263
|
key: "createdAt",
|
|
263
264
|
type: "date",
|
|
264
265
|
required: true,
|
|
265
266
|
},
|
|
266
267
|
{
|
|
267
|
-
id:
|
|
268
|
+
id: -2,
|
|
268
269
|
key: "updatedAt",
|
|
269
270
|
type: "date",
|
|
270
271
|
required: false,
|
|
271
272
|
},
|
|
273
|
+
...schema,
|
|
272
274
|
];
|
|
273
275
|
if (!encodeIDs)
|
|
274
276
|
return schema;
|
|
@@ -279,7 +281,7 @@ export default class Inibase {
|
|
|
279
281
|
if (!table.schema)
|
|
280
282
|
throw this.Error("NO_SCHEMA", tableName);
|
|
281
283
|
if (!(await File.isExists(join(this.databasePath, tableName, `id${this.getFileExtension(tableName)}`))))
|
|
282
|
-
throw this.Error("
|
|
284
|
+
throw this.Error("TABLE_EMPTY", tableName);
|
|
283
285
|
return table;
|
|
284
286
|
}
|
|
285
287
|
validateData(data, schema, skipRequiredField = false) {
|
|
@@ -455,15 +457,17 @@ export default class Inibase {
|
|
|
455
457
|
return null;
|
|
456
458
|
}
|
|
457
459
|
}
|
|
458
|
-
_combineObjectsToArray
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
result[key]
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
460
|
+
_combineObjectsToArray(input) {
|
|
461
|
+
return input.reduce((result, current) => {
|
|
462
|
+
for (const [key, value] of Object.entries(current))
|
|
463
|
+
if (Object.hasOwn(result, key) && Array.isArray(result[key]))
|
|
464
|
+
result[key].push(value);
|
|
465
|
+
else
|
|
466
|
+
result[key] = [value];
|
|
467
|
+
return result;
|
|
468
|
+
}, {});
|
|
469
|
+
}
|
|
470
|
+
_CombineData(data, prefix) {
|
|
467
471
|
if (Utils.isArrayOfObjects(data))
|
|
468
472
|
return this._combineObjectsToArray(data.map((single_data) => this._CombineData(single_data)));
|
|
469
473
|
const RETURN = {};
|
|
@@ -480,7 +484,7 @@ export default class Inibase {
|
|
|
480
484
|
RETURN[(prefix ?? "") + key] = File.encode(value);
|
|
481
485
|
}
|
|
482
486
|
return RETURN;
|
|
483
|
-
}
|
|
487
|
+
}
|
|
484
488
|
joinPathesContents(tableName, data) {
|
|
485
489
|
const tablePath = join(this.databasePath, tableName), combinedData = this._CombineData(data);
|
|
486
490
|
const newCombinedData = {};
|
|
@@ -834,9 +838,8 @@ export default class Inibase {
|
|
|
834
838
|
*/
|
|
835
839
|
async clearCache(tableName) {
|
|
836
840
|
const cacheFolderPath = join(this.databasePath, tableName, ".cache");
|
|
837
|
-
await
|
|
838
|
-
|
|
839
|
-
.map((file) => unlink(join(cacheFolderPath, file))));
|
|
841
|
+
await rm(cacheFolderPath, { recursive: true, force: true });
|
|
842
|
+
await mkdir(cacheFolderPath);
|
|
840
843
|
}
|
|
841
844
|
async get(tableName, where, options = {
|
|
842
845
|
page: 1,
|
|
@@ -858,7 +861,12 @@ export default class Inibase {
|
|
|
858
861
|
let schema = (await this.getTable(tableName)).schema;
|
|
859
862
|
if (!schema)
|
|
860
863
|
throw this.Error("NO_SCHEMA", tableName);
|
|
861
|
-
|
|
864
|
+
let pagination;
|
|
865
|
+
for await (const paginationFilePath of glob("*.pagination", {
|
|
866
|
+
cwd: tablePath,
|
|
867
|
+
}))
|
|
868
|
+
pagination = parse(paginationFilePath).name.split("-").map(Number);
|
|
869
|
+
if (!pagination[1])
|
|
862
870
|
return null;
|
|
863
871
|
if (options.columns?.length)
|
|
864
872
|
schema = this._filterSchemaByColumns(schema, options.columns);
|
|
@@ -931,7 +939,7 @@ export default class Inibase {
|
|
|
931
939
|
if (where)
|
|
932
940
|
lines = lines.slice((options.page - 1) * options.perPage, options.page * options.perPage);
|
|
933
941
|
else if (!this.totalItems[`${tableName}-*`])
|
|
934
|
-
this.totalItems[`${tableName}-*`] =
|
|
942
|
+
this.totalItems[`${tableName}-*`] = pagination[1];
|
|
935
943
|
if (!lines.length)
|
|
936
944
|
return null;
|
|
937
945
|
// Parse the result and extract the specified lines
|
|
@@ -964,16 +972,8 @@ export default class Inibase {
|
|
|
964
972
|
RETURN = Object.values(await this.getItemsFromSchema(tableName, schema, Array.from({ length: options.perPage }, (_, index) => (options.page - 1) * options.perPage +
|
|
965
973
|
index +
|
|
966
974
|
1), options));
|
|
967
|
-
if (
|
|
968
|
-
|
|
969
|
-
this.totalItems[`${tableName}-*`] = Number((await readFile(join(tablePath, ".cache", ".pagination"), "utf8")).split(",")[1]);
|
|
970
|
-
}
|
|
971
|
-
else {
|
|
972
|
-
const lastId = Number(Object.keys((await File.get(join(tablePath, `id${this.getFileExtension(tableName)}`), -1, "number", undefined, this.salt, true))?.[0] ?? 0));
|
|
973
|
-
if (!this.totalItems[`${tableName}-*`])
|
|
974
|
-
this.totalItems[`${tableName}-*`] = await File.count(join(tablePath, `id${this.getFileExtension(tableName)}`));
|
|
975
|
-
await writeFile(join(tablePath, ".cache", ".pagination"), `${lastId},${this.totalItems[`${tableName}-*`]}`);
|
|
976
|
-
}
|
|
975
|
+
if (!this.totalItems[`${tableName}-*`])
|
|
976
|
+
this.totalItems[`${tableName}-*`] = pagination[1];
|
|
977
977
|
}
|
|
978
978
|
else if ((Array.isArray(where) && where.every(Utils.isNumber)) ||
|
|
979
979
|
Utils.isNumber(where)) {
|
|
@@ -1076,23 +1076,17 @@ export default class Inibase {
|
|
|
1076
1076
|
returnPostedData = false;
|
|
1077
1077
|
const keys = UtilsServer.hashString(Object.keys(Array.isArray(data) ? data[0] : data).join("."));
|
|
1078
1078
|
// Skip Id and (created|updated)At
|
|
1079
|
-
this.validateData(data, schema.slice(
|
|
1080
|
-
let lastId = 0
|
|
1079
|
+
this.validateData(data, schema.slice(3));
|
|
1080
|
+
let lastId = 0;
|
|
1081
|
+
const renameList = [];
|
|
1081
1082
|
try {
|
|
1082
1083
|
await File.lock(join(tablePath, ".tmp"), keys);
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
lastId = Number(Object.keys((await File.get(join(tablePath, `id${this.getFileExtension(tableName)}`), -1, "number", undefined, this.salt, true))?.[0] ?? 0));
|
|
1090
|
-
if (!this.totalItems[`${tableName}-*`])
|
|
1091
|
-
this.totalItems[`${tableName}-*`] = await File.count(join(tablePath, `id${this.getFileExtension(tableName)}`));
|
|
1092
|
-
}
|
|
1093
|
-
}
|
|
1094
|
-
else
|
|
1095
|
-
this.totalItems[`${tableName}-*`] = 0;
|
|
1084
|
+
let paginationFilePath;
|
|
1085
|
+
for await (const filePath of glob("*.pagination", { cwd: tablePath }))
|
|
1086
|
+
paginationFilePath = filePath;
|
|
1087
|
+
[lastId, this.totalItems[`${tableName}-*`]] = parse(paginationFilePath)
|
|
1088
|
+
.name.split("-")
|
|
1089
|
+
.map(Number);
|
|
1096
1090
|
if (Utils.isArrayOfObjects(data))
|
|
1097
1091
|
for (let index = 0; index < data.length; index++) {
|
|
1098
1092
|
const element = data[index];
|
|
@@ -1112,17 +1106,18 @@ export default class Inibase {
|
|
|
1112
1106
|
? data.toReversed()
|
|
1113
1107
|
: data
|
|
1114
1108
|
: data);
|
|
1115
|
-
await Promise.
|
|
1109
|
+
await Promise.allSettled(Object.entries(pathesContents).map(async ([path, content]) => renameList.push(this.tables[tableName].config.prepend
|
|
1116
1110
|
? await File.prepend(path, content)
|
|
1117
1111
|
: await File.append(path, content))));
|
|
1118
|
-
await Promise.
|
|
1119
|
-
|
|
1112
|
+
await Promise.allSettled(renameList
|
|
1113
|
+
.filter(([_, filePath]) => filePath)
|
|
1114
|
+
.map(async ([tempPath, filePath]) => rename(tempPath, filePath)));
|
|
1120
1115
|
if (this.tables[tableName].config.cache)
|
|
1121
1116
|
await this.clearCache(tableName);
|
|
1122
1117
|
this.totalItems[`${tableName}-*`] += Array.isArray(data)
|
|
1123
1118
|
? data.length
|
|
1124
1119
|
: 1;
|
|
1125
|
-
await
|
|
1120
|
+
await rename(join(tablePath, paginationFilePath), join(tablePath, `${lastId}-${this.totalItems[`${tableName}-*`]}.pagination`));
|
|
1126
1121
|
if (returnPostedData)
|
|
1127
1122
|
return this.get(tableName, this.tables[tableName].config.prepend
|
|
1128
1123
|
? Array.isArray(data)
|
|
@@ -1144,7 +1139,7 @@ export default class Inibase {
|
|
|
1144
1139
|
page: 1,
|
|
1145
1140
|
perPage: 15,
|
|
1146
1141
|
}, returnUpdatedData) {
|
|
1147
|
-
|
|
1142
|
+
const renameList = [];
|
|
1148
1143
|
const tablePath = join(this.databasePath, tableName);
|
|
1149
1144
|
const schema = (await this.throwErrorIfTableEmpty(tableName))
|
|
1150
1145
|
.schema;
|
|
@@ -1159,13 +1154,6 @@ export default class Inibase {
|
|
|
1159
1154
|
throw this.Error("INVALID_ID", data.id);
|
|
1160
1155
|
return this.put(tableName, data, data.id, options, returnUpdatedData || undefined);
|
|
1161
1156
|
}
|
|
1162
|
-
let totalItems;
|
|
1163
|
-
if (await File.isExists(join(tablePath, ".cache", ".pagination")))
|
|
1164
|
-
totalItems = (await readFile(join(tablePath, ".cache", ".pagination"), "utf8"))
|
|
1165
|
-
.split(",")
|
|
1166
|
-
.map(Number)[1];
|
|
1167
|
-
else
|
|
1168
|
-
totalItems = await File.count(join(tablePath, `id${this.getFileExtension(tableName)}`));
|
|
1169
1157
|
this.validateData(data, schema, true);
|
|
1170
1158
|
await this.checkUnique(tableName, schema);
|
|
1171
1159
|
data = this.formatData(data, schema, true);
|
|
@@ -1175,13 +1163,16 @@ export default class Inibase {
|
|
|
1175
1163
|
});
|
|
1176
1164
|
try {
|
|
1177
1165
|
await File.lock(join(tablePath, ".tmp"));
|
|
1178
|
-
await
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
await Promise.
|
|
1166
|
+
for await (const paginationFilePath of glob("*.pagination", {
|
|
1167
|
+
cwd: tablePath,
|
|
1168
|
+
}))
|
|
1169
|
+
this.totalItems[`${tableName}-*`] = parse(paginationFilePath)
|
|
1170
|
+
.name.split("-")
|
|
1171
|
+
.map(Number)[1];
|
|
1172
|
+
await Promise.allSettled(Object.entries(pathesContents).map(async ([path, content]) => renameList.push(await File.replace(path, content, this.totalItems[`${tableName}-*`]))));
|
|
1173
|
+
await Promise.allSettled(renameList
|
|
1174
|
+
.filter(([_, filePath]) => filePath)
|
|
1175
|
+
.map(async ([tempPath, filePath]) => rename(tempPath, filePath)));
|
|
1185
1176
|
if (this.tables[tableName].config.cache)
|
|
1186
1177
|
await this.clearCache(join(tablePath, ".cache"));
|
|
1187
1178
|
if (returnUpdatedData)
|
|
@@ -1220,9 +1211,10 @@ export default class Inibase {
|
|
|
1220
1211
|
.join("."));
|
|
1221
1212
|
try {
|
|
1222
1213
|
await File.lock(join(tablePath, ".tmp"), keys);
|
|
1223
|
-
await Promise.
|
|
1224
|
-
await Promise.
|
|
1225
|
-
|
|
1214
|
+
await Promise.allSettled(Object.entries(pathesContents).map(async ([path, content]) => renameList.push(await File.replace(path, content))));
|
|
1215
|
+
await Promise.allSettled(renameList
|
|
1216
|
+
.filter(([_, filePath]) => filePath)
|
|
1217
|
+
.map(async ([tempPath, filePath]) => rename(tempPath, filePath)));
|
|
1226
1218
|
if (this.tables[tableName].config.cache)
|
|
1227
1219
|
await this.clearCache(tableName);
|
|
1228
1220
|
if (returnUpdatedData)
|
|
@@ -1236,7 +1228,8 @@ export default class Inibase {
|
|
|
1236
1228
|
}
|
|
1237
1229
|
else if (Utils.isObject(where)) {
|
|
1238
1230
|
const lineNumbers = await this.get(tableName, where, undefined, undefined, true);
|
|
1239
|
-
|
|
1231
|
+
if (lineNumbers)
|
|
1232
|
+
return this.put(tableName, data, lineNumbers, options, returnUpdatedData || undefined);
|
|
1240
1233
|
}
|
|
1241
1234
|
else
|
|
1242
1235
|
throw this.Error("INVALID_PARAMETERS");
|
|
@@ -1254,19 +1247,20 @@ export default class Inibase {
|
|
|
1254
1247
|
if (!where) {
|
|
1255
1248
|
try {
|
|
1256
1249
|
await File.lock(join(tablePath, ".tmp"));
|
|
1257
|
-
let
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1250
|
+
let paginationFilePath;
|
|
1251
|
+
let pagination;
|
|
1252
|
+
for await (const filePath of glob("*.pagination", {
|
|
1253
|
+
cwd: tablePath,
|
|
1254
|
+
})) {
|
|
1255
|
+
paginationFilePath = filePath;
|
|
1256
|
+
pagination = parse(filePath).name.split("-").map(Number);
|
|
1257
|
+
}
|
|
1264
1258
|
await Promise.all((await readdir(tablePath))
|
|
1265
1259
|
?.filter((fileName) => fileName.endsWith(this.getFileExtension(tableName)))
|
|
1266
1260
|
.map(async (file) => unlink(join(tablePath, file))));
|
|
1267
1261
|
if (this.tables[tableName].config.cache)
|
|
1268
1262
|
await this.clearCache(tableName);
|
|
1269
|
-
await
|
|
1263
|
+
await rename(join(tablePath, paginationFilePath), join(tablePath, `${pagination[0]}-0.pagination`));
|
|
1270
1264
|
return true;
|
|
1271
1265
|
}
|
|
1272
1266
|
finally {
|
|
@@ -1286,22 +1280,20 @@ export default class Inibase {
|
|
|
1286
1280
|
const renameList = [];
|
|
1287
1281
|
try {
|
|
1288
1282
|
await File.lock(join(tablePath, ".tmp"));
|
|
1289
|
-
let
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
if (!this.totalItems[`${tableName}-*`])
|
|
1297
|
-
this.totalItems[`${tableName}-*`] = await File.count(join(tablePath, `id${this.getFileExtension(tableName)}`));
|
|
1283
|
+
let paginationFilePath;
|
|
1284
|
+
let pagination;
|
|
1285
|
+
for await (const filePath of glob("*.pagination", {
|
|
1286
|
+
cwd: tablePath,
|
|
1287
|
+
})) {
|
|
1288
|
+
paginationFilePath = filePath;
|
|
1289
|
+
pagination = parse(filePath).name.split("-").map(Number);
|
|
1298
1290
|
}
|
|
1299
|
-
if (
|
|
1300
|
-
|
|
1301
|
-
(Array.isArray(where) ? where.length : 1) >
|
|
1302
|
-
0) {
|
|
1291
|
+
if (pagination[1] &&
|
|
1292
|
+
pagination[1] - (Array.isArray(where) ? where.length : 1) > 0) {
|
|
1303
1293
|
await Promise.all(files.map(async (file) => renameList.push(await File.remove(join(tablePath, file), where))));
|
|
1304
|
-
await Promise.all(renameList
|
|
1294
|
+
await Promise.all(renameList
|
|
1295
|
+
.filter(([_, filePath]) => filePath)
|
|
1296
|
+
.map(async ([tempPath, filePath]) => rename(tempPath, filePath)));
|
|
1305
1297
|
}
|
|
1306
1298
|
else
|
|
1307
1299
|
await Promise.all((await readdir(tablePath))
|
|
@@ -1309,8 +1301,7 @@ export default class Inibase {
|
|
|
1309
1301
|
.map(async (file) => unlink(join(tablePath, file))));
|
|
1310
1302
|
if (this.tables[tableName].config.cache)
|
|
1311
1303
|
await this.clearCache(tableName);
|
|
1312
|
-
await
|
|
1313
|
-
(Array.isArray(where) ? where.length : 1)}`);
|
|
1304
|
+
await rename(join(tablePath, paginationFilePath), join(tablePath, `${pagination[0]}-${pagination[1] - (Array.isArray(where) ? where.length : 1)}.pagination`));
|
|
1314
1305
|
return true;
|
|
1315
1306
|
}
|
|
1316
1307
|
finally {
|
|
@@ -1322,7 +1313,8 @@ export default class Inibase {
|
|
|
1322
1313
|
}
|
|
1323
1314
|
else if (Utils.isObject(where)) {
|
|
1324
1315
|
const lineNumbers = await this.get(tableName, where, undefined, undefined, true);
|
|
1325
|
-
|
|
1316
|
+
if (lineNumbers)
|
|
1317
|
+
return this.delete(tableName, lineNumbers);
|
|
1326
1318
|
}
|
|
1327
1319
|
else
|
|
1328
1320
|
throw this.Error("INVALID_PARAMETERS");
|