inibase 1.5.6 → 1.5.8

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.js CHANGED
@@ -227,7 +227,7 @@ export async function get(filePath, lineNumbers, field, readWholeFile = false) {
227
227
  else if (lineNumbers === -1) {
228
228
  const escapedFilePath = escapeShellPath(filePath);
229
229
  const command = filePath.endsWith(".gz")
230
- ? `zcat ${escapedFilePath} | sed -n '$p'`
230
+ ? `gunzip -c ${escapedFilePath} | sed -n '$p'`
231
231
  : `sed -n '$p' ${escapedFilePath}`;
232
232
  const foundedLine = (await exec(command)).stdout.trimEnd();
233
233
  if (foundedLine)
@@ -250,7 +250,7 @@ export async function get(filePath, lineNumbers, field, readWholeFile = false) {
250
250
  }
251
251
  const escapedFilePath = escapeShellPath(filePath);
252
252
  const command = filePath.endsWith(".gz")
253
- ? `zcat ${escapedFilePath} | sed -n '${_groupIntoRanges(lineNumbers)}'`
253
+ ? `gunzip -c ${escapedFilePath} | sed -n '${_groupIntoRanges(lineNumbers)}'`
254
254
  : `sed -n '${_groupIntoRanges(lineNumbers)}' ${escapedFilePath}`;
255
255
  const foundedLines = (await exec(command)).stdout.trimEnd().split("\n");
256
256
  let index = 0;
@@ -336,7 +336,7 @@ export const replace = async (filePath, replacements, totalItems) => {
336
336
  const escapedFileTempPath = escapeShellPath(fileTempPath);
337
337
  const sedCommand = `sed -e s/.*/${replacements}/ -e /^$/s/^/${replacements}/ ${escapedFilePath}`;
338
338
  const command = filePath.endsWith(".gz")
339
- ? `zcat ${escapedFilePath} | ${sedCommand} | gzip > ${escapedFileTempPath}`
339
+ ? `gunzip -c ${escapedFilePath} | ${sedCommand} | gzip > ${escapedFileTempPath}`
340
340
  : `${sedCommand} > ${escapedFileTempPath}`;
341
341
  try {
342
342
  await exec(command);
@@ -491,7 +491,7 @@ export const remove = async (filePath, linesToDelete) => {
491
491
  const escapedFilePath = escapeShellPath(filePath);
492
492
  const escapedFileTempPath = escapeShellPath(fileTempPath);
493
493
  const command = filePath.endsWith(".gz")
494
- ? `zcat ${escapedFilePath} | sed '${_groupIntoRanges(linesToDelete, "d")}' | gzip > ${escapedFileTempPath}`
494
+ ? `gunzip -c ${escapedFilePath} | sed '${_groupIntoRanges(linesToDelete, "d")}' | gzip > ${escapedFileTempPath}`
495
495
  : `sed '${_groupIntoRanges(linesToDelete, "d")}' ${escapedFilePath} > ${escapedFileTempPath}`;
496
496
  await exec(command);
497
497
  return [fileTempPath, filePath];
package/dist/index.d.ts CHANGED
@@ -104,9 +104,9 @@ export default class Inibase {
104
104
  * Get table schema and config
105
105
  *
106
106
  * @param {string} tableName
107
- * @return {*} {Promise<TableObject>}
107
+ * @return {*} {Promise<TableObject | undefined>}
108
108
  */
109
- getTable(tableName: string): Promise<TableObject>;
109
+ getTable(tableName: string): Promise<TableObject | undefined>;
110
110
  getTableSchema(tableName: string): Promise<Schema>;
111
111
  private throwErrorIfTableEmpty;
112
112
  validateData(data: Data | Data[], schema: Schema, skipRequiredField?: boolean): void;
package/dist/index.js CHANGED
@@ -137,9 +137,9 @@ export default class Inibase {
137
137
  getFileExtension(tableName) {
138
138
  let mainExtension = this.fileExtension;
139
139
  // TODO: ADD ENCRYPTION
140
- // if(globalConfig[this.databasePath].tables.get(tableName).config.encryption)
140
+ // if(globalConfig[this.databasePath].tables?.get(tableName)?.config.encryption)
141
141
  // mainExtension += ".enc"
142
- if (globalConfig[this.databasePath].tables.get(tableName).config.compression)
142
+ if (globalConfig[this.databasePath].tables?.get(tableName)?.config.compression)
143
143
  mainExtension += ".gz";
144
144
  return mainExtension;
145
145
  }
@@ -212,11 +212,13 @@ export default class Inibase {
212
212
  */
213
213
  async updateTable(tableName, schema, config) {
214
214
  const table = await this.getTable(tableName);
215
+ if (!table)
216
+ return;
215
217
  const tablePath = join(this.databasePath, tableName);
216
218
  if (schema) {
217
219
  // remove id from schema
218
220
  schema = schema.filter(({ key }) => !["id", "createdAt", "updatedAt"].includes(key));
219
- let schemaIdFilePath;
221
+ let schemaIdFilePath = "";
220
222
  for await (const fileName of glob("*.schema", { cwd: tablePath }))
221
223
  schemaIdFilePath = join(tablePath, fileName);
222
224
  const lastSchemaID = {
@@ -293,8 +295,8 @@ export default class Inibase {
293
295
  "sh",
294
296
  "-c",
295
297
  `for file; do ${config.compression
296
- ? 'zcat "$file" | tac | gzip > "$file.reversed" && mv "$file.reversed" "$file"'
297
- : 'tac "$file" > "$file.reversed" && mv "$file.reversed" "$file"'}; done`,
298
+ ? `zcat "$file" | ${process.platform === "darwin" ? "tail -r" : "tac"} | gzip > "$file.reversed" && mv "$file.reversed" "$file"`
299
+ : `${process.platform === "darwin" ? "tail -r" : "tac"} "$file" > "$file.reversed" && mv "$file.reversed" "$file"`}; done`,
298
300
  "_",
299
301
  "{}",
300
302
  "+",
@@ -319,22 +321,22 @@ export default class Inibase {
319
321
  : `table:${config.name}`);
320
322
  }
321
323
  }
322
- globalConfig[this.databasePath].tables.delete(tableName);
324
+ globalConfig[this.databasePath].tables?.delete(tableName);
323
325
  }
324
326
  /**
325
327
  * Get table schema and config
326
328
  *
327
329
  * @param {string} tableName
328
- * @return {*} {Promise<TableObject>}
330
+ * @return {*} {Promise<TableObject | undefined>}
329
331
  */
330
332
  async getTable(tableName) {
331
333
  const tablePath = join(this.databasePath, tableName);
332
334
  if (!(await File.isExists(tablePath)))
333
335
  throw this.createError("TABLE_NOT_EXISTS", tableName);
334
- if (!globalConfig[this.databasePath].tables.has(tableName) ||
335
- globalConfig[this.databasePath].tables.get(tableName).timestamp !==
336
+ if (!globalConfig[this.databasePath].tables?.has(tableName) ||
337
+ globalConfig[this.databasePath].tables?.get(tableName)?.timestamp !==
336
338
  (await File.getFileDate(join(tablePath, `schema.${this.schemaFileExtension}`))))
337
- globalConfig[this.databasePath].tables.set(tableName, {
339
+ globalConfig[this.databasePath].tables?.set(tableName, {
338
340
  schema: await this.getTableSchema(tableName),
339
341
  config: {
340
342
  compression: await File.isExists(join(tablePath, ".compression.config")),
@@ -344,7 +346,7 @@ export default class Inibase {
344
346
  },
345
347
  timestamp: await File.getFileDate(join(tablePath, `schema.${this.schemaFileExtension}`)),
346
348
  });
347
- return globalConfig[this.databasePath].tables.get(tableName);
349
+ return globalConfig[this.databasePath].tables?.get(tableName);
348
350
  }
349
351
  async getTableSchema(tableName) {
350
352
  const tablePath = join(this.databasePath, tableName);
@@ -382,7 +384,7 @@ export default class Inibase {
382
384
  type: "id",
383
385
  required: true,
384
386
  },
385
- ...schema,
387
+ ...(schema || []),
386
388
  {
387
389
  id: -1,
388
390
  key: "createdAt",
@@ -398,7 +400,7 @@ export default class Inibase {
398
400
  }
399
401
  async throwErrorIfTableEmpty(tableName) {
400
402
  const table = await this.getTable(tableName);
401
- if (!table.schema)
403
+ if (!table?.schema)
402
404
  throw this.createError("NO_SCHEMA", tableName);
403
405
  if (!(await File.isExists(join(this.databasePath, tableName, `id${this.getFileExtension(tableName)}`))))
404
406
  throw this.createError("TABLE_EMPTY", tableName);
@@ -448,27 +450,24 @@ export default class Inibase {
448
450
  !regex.test(String(data[field.key])))
449
451
  throw this.createError("INVALID_REGEX_MATCH", [field.key]);
450
452
  }
451
- if (field.unique) {
452
- let uniqueKey;
453
- if (typeof field.unique === "boolean")
454
- uniqueKey = field.id;
455
- else
456
- uniqueKey = field.unique;
453
+ if (field.unique && field.id !== undefined) {
454
+ const fieldId = field.id;
455
+ const uniqueKey = typeof field.unique === "boolean" ? fieldId : field.unique;
457
456
  if (!this.uniqueMap.has(uniqueKey))
458
457
  this.uniqueMap.set(uniqueKey, {
459
458
  exclude: new Set(),
460
459
  columnsValues: new Map(),
461
460
  });
462
- if (!this.uniqueMap.get(uniqueKey).columnsValues.has(field.id))
461
+ if (!this.uniqueMap.get(uniqueKey)?.columnsValues.has(fieldId))
463
462
  this.uniqueMap
464
- .get(uniqueKey)
465
- .columnsValues.set(field.id, new Set());
463
+ ?.get(uniqueKey)
464
+ ?.columnsValues.set(fieldId, new Set());
466
465
  if (data.id)
467
- this.uniqueMap.get(uniqueKey).exclude.add(-data.id);
466
+ this.uniqueMap.get(uniqueKey)?.exclude.add(-data.id);
468
467
  this.uniqueMap
469
468
  .get(uniqueKey)
470
- .columnsValues.get(field.id)
471
- .add(data[field.key]);
469
+ ?.columnsValues.get(fieldId)
470
+ ?.add(data[field.key]);
472
471
  }
473
472
  }
474
473
  }
@@ -477,7 +476,9 @@ export default class Inibase {
477
476
  async validateTableData(tableName, data, skipRequiredField = false) {
478
477
  const clonedData = structuredClone(data);
479
478
  // Skip ID and (created|updated)At
480
- this.validateData(clonedData, globalConfig[this.databasePath].tables.get(tableName).schema.slice(1, -2), skipRequiredField);
479
+ this.validateData(clonedData, globalConfig[this.databasePath].tables
480
+ ?.get(tableName)
481
+ ?.schema?.slice(1, -2) ?? [], skipRequiredField);
481
482
  await this.checkUnique(tableName);
482
483
  }
483
484
  cleanObject(obj) {
@@ -491,6 +492,8 @@ export default class Inibase {
491
492
  formatField(value, field, _formatOnlyAvailiableKeys) {
492
493
  if (value === null || value === undefined || value === "")
493
494
  return value;
495
+ if (!field)
496
+ return value;
494
497
  let _fieldType = field.type;
495
498
  if (Array.isArray(_fieldType))
496
499
  _fieldType = Utils.detectFieldType(value, _fieldType) ?? _fieldType[0];
@@ -561,7 +564,7 @@ export default class Inibase {
561
564
  }
562
565
  async checkUnique(tableName) {
563
566
  const tablePath = join(this.databasePath, tableName);
564
- const flattenSchema = Utils.flattenSchema(globalConfig[this.databasePath].tables.get(tableName).schema);
567
+ const flattenSchema = Utils.flattenSchema(globalConfig[this.databasePath].tables?.get(tableName)?.schema ?? []);
565
568
  function hasDuplicates(setA, setB) {
566
569
  for (const value of setA)
567
570
  if (setB.has(value))
@@ -576,12 +579,14 @@ export default class Inibase {
576
579
  for await (const [columnID, values] of valueObject.columnsValues) {
577
580
  index++;
578
581
  const field = flattenSchema.find(({ id }) => id === columnID);
582
+ if (!field)
583
+ continue;
579
584
  fieldsKeys.push(field.key);
580
585
  const [_, totalLines, lineNumbers] = await File.search(join(tablePath, `${field.key}${this.getFileExtension(tableName)}`), "[]", Array.from(values), undefined, valueObject.exclude, { ...field, databasePath: this.databasePath }, 1, undefined, false);
581
586
  if (totalLines > 0) {
582
587
  if (valueObject.columnsValues.size === 1 ||
583
588
  (valueObject.columnsValues.size === index &&
584
- hasDuplicates(lineNumbers, mergedLineNumbers))) {
589
+ !!(lineNumbers && hasDuplicates(lineNumbers, mergedLineNumbers)))) {
585
590
  this.uniqueMap = new Map();
586
591
  if (valueObject.columnsValues.size > 1)
587
592
  throw this.createError("GROUP_UNIQUE", [
@@ -593,7 +598,7 @@ export default class Inibase {
593
598
  Array.from(values).join(", "),
594
599
  ]);
595
600
  }
596
- lineNumbers.forEach(mergedLineNumbers.add, mergedLineNumbers);
601
+ lineNumbers?.forEach(mergedLineNumbers.add, mergedLineNumbers);
597
602
  }
598
603
  else {
599
604
  shouldContinueParent = true; // Flag to skip the rest of this inner loop
@@ -778,7 +783,8 @@ export default class Inibase {
778
783
  const items = await File.get(fieldPath, linesNumber, {
779
784
  ...field,
780
785
  type: field.key === "id" &&
781
- globalConfig[this.databasePath].tables.get(tableName).config.decodeID
786
+ globalConfig[this.databasePath].tables?.get(tableName)?.config
787
+ .decodeID
782
788
  ? "number"
783
789
  : field.type,
784
790
  databasePath: this.databasePath,
@@ -886,11 +892,11 @@ export default class Inibase {
886
892
  }
887
893
  }
888
894
  }
889
- else if (this.isSimpleField(field.children)) {
895
+ else if (field.children != null && this.isSimpleField(field.children)) {
890
896
  // If `children` is FieldType, handle it as an array of simple types (no recursion needed here)
891
897
  await this.processSimpleField(tableName, field, RETURN, linesNumber, prefix);
892
898
  }
893
- else if (this.isTableField(field.children)) {
899
+ else if (field.children != null && this.isTableField(field.children)) {
894
900
  await this.processTableField(tableName, field, RETURN, linesNumber, options, prefix);
895
901
  }
896
902
  }
@@ -953,16 +959,18 @@ export default class Inibase {
953
959
  .filter((item) => item), {
954
960
  ...options,
955
961
  perPage: -1,
956
- columns: options.columns
962
+ columns: options?.columns
957
963
  ?.filter((column) => column.includes(`${field.key}.`))
958
964
  .map((column) => column.replace(`${field.key}.`, "")),
959
965
  });
960
966
  const formatLineContent = (lineContent) => Array.isArray(lineContent)
961
- ? lineContent.map((singleContent) => singleContent
967
+ ? lineContent
968
+ .map((singleContent) => singleContent
962
969
  ? Array.isArray(singleContent)
963
970
  ? singleContent.map(formatLineContent)
964
971
  : items?.find(({ id }) => singleContent === id)
965
972
  : singleContent)
973
+ .filter((item) => item !== undefined)
966
974
  : items?.find(({ id }) => lineContent === id);
967
975
  for (const [lineNumber, lineContent] of searchableIDs.entries()) {
968
976
  if (!lineContent)
@@ -987,7 +995,8 @@ export default class Inibase {
987
995
  }
988
996
  return acc[key];
989
997
  }, obj);
990
- target[lastKey] = value;
998
+ if (lastKey !== undefined)
999
+ target[lastKey] = value;
991
1000
  }
992
1001
  async applyCriteria(tableName, options, criteria, allTrue, searchIn) {
993
1002
  const tablePath = join(this.databasePath, tableName);
@@ -1000,7 +1009,7 @@ export default class Inibase {
1000
1009
  const criteriaOR = criteria.or;
1001
1010
  if (criteriaOR)
1002
1011
  delete criteria.or;
1003
- const schema = globalConfig[this.databasePath].tables.get(tableName).schema;
1012
+ const schema = globalConfig[this.databasePath].tables?.get(tableName)?.schema ?? [];
1004
1013
  if (Object.keys(criteria).length) {
1005
1014
  if (allTrue === undefined)
1006
1015
  allTrue = true;
@@ -1028,15 +1037,17 @@ export default class Inibase {
1028
1037
  if (nestedAnd || nestedOr) {
1029
1038
  const logicalChild = nestedAnd ?? nestedOr;
1030
1039
  const logic = nestedAnd ? "and" : "or";
1031
- const crit = Object.entries(logicalChild)
1032
- .map((item) => typeof item[1] === "string"
1033
- ? Utils.FormatObjectCriteriaValue(item[1])
1034
- : ["=", item[1]])
1035
- .filter(Boolean);
1036
- if (crit.length) {
1037
- searchOperator = crit.map((c) => c[0]);
1038
- searchComparedAtValue = crit.map((c) => c[1]);
1039
- searchLogicalOperator = logic;
1040
+ if (logicalChild && !Array.isArray(logicalChild)) {
1041
+ const crit = Object.entries(logicalChild)
1042
+ .map((item) => typeof item[1] === "string"
1043
+ ? Utils.FormatObjectCriteriaValue(item[1])
1044
+ : ["=", item[1]])
1045
+ .filter(Boolean);
1046
+ if (crit.length) {
1047
+ searchOperator = crit.map((c) => c[0]);
1048
+ searchComparedAtValue = crit.map((c) => c[1]);
1049
+ searchLogicalOperator = logic;
1050
+ }
1040
1051
  }
1041
1052
  delete value[logic];
1042
1053
  }
@@ -1050,14 +1061,14 @@ export default class Inibase {
1050
1061
  searchOperator = "=";
1051
1062
  searchComparedAtValue = value;
1052
1063
  }
1053
- const [searchResult, totalLines, linesNumbers] = await File.search(join(tablePath, `${key}${this.getFileExtension(tableName)}`), searchOperator ?? "=", searchComparedAtValue, searchLogicalOperator, searchIn, {
1064
+ const [searchResult, totalLines, linesNumbers] = await File.search(join(tablePath, `${key}${this.getFileExtension(tableName)}`), searchOperator ?? "=", searchComparedAtValue ?? null, searchLogicalOperator, searchIn, {
1054
1065
  ...field,
1055
1066
  databasePath: this.databasePath,
1056
1067
  table: field.table ?? tableName,
1057
- }, options.perPage < 0 ? undefined : options.perPage, options.perPage < 0
1068
+ }, (options.perPage ?? 0) < 0 ? undefined : options.perPage, (options.perPage ?? 0) < 0
1058
1069
  ? undefined
1059
1070
  : (options.page - 1) * options.perPage +
1060
- (options.page > 1 ? 1 : 0), true);
1071
+ ((options.page ?? 1) > 1 ? 1 : 0), true);
1061
1072
  if (!searchResult) {
1062
1073
  if (allTrue)
1063
1074
  return null;
@@ -1151,10 +1162,10 @@ export default class Inibase {
1151
1162
  options.perPage = options.perPage || 15;
1152
1163
  let total;
1153
1164
  let RETURN;
1154
- let schema = structuredClone((await this.getTable(tableName)).schema);
1165
+ let schema = structuredClone((await this.getTable(tableName))?.schema);
1155
1166
  if (!schema)
1156
1167
  throw this.createError("NO_SCHEMA", tableName);
1157
- let pagination;
1168
+ let pagination = [0, 0];
1158
1169
  for await (const paginationFileName of glob("*.pagination", {
1159
1170
  cwd: tablePath,
1160
1171
  }))
@@ -1183,7 +1194,7 @@ export default class Inibase {
1183
1194
  .map((column) => [column, true]);
1184
1195
  let cacheKey = "";
1185
1196
  // Criteria
1186
- if (globalConfig[this.databasePath].tables.get(tableName).config.cache)
1197
+ if (globalConfig[this.databasePath].tables?.get(tableName)?.config.cache)
1187
1198
  cacheKey = UtilsServer.hashString(inspect(sortArray, { sorted: true }));
1188
1199
  if (where) {
1189
1200
  const lineNumbers = await this.get(tableName, where, undefined, undefined, true);
@@ -1221,7 +1232,7 @@ export default class Inibase {
1221
1232
  if (cacheKey)
1222
1233
  await File.lock(join(tablePath, ".tmp"), cacheKey);
1223
1234
  // Combine && Execute the commands synchronously
1224
- let lines = (await UtilsServer.exec(globalConfig[this.databasePath].tables.get(tableName).config.cache
1235
+ let lines = (await UtilsServer.exec(globalConfig[this.databasePath].tables?.get(tableName)?.config.cache
1225
1236
  ? (await File.isExists(join(tablePath, ".cache", `${cacheKey}${this.fileExtension}`)))
1226
1237
  ? `${awkCommand} '${join(tablePath, ".cache", `${cacheKey}${this.fileExtension}`)}'`
1227
1238
  : `${pasteCommand} | ${sortCommand} -o '${join(tablePath, ".cache", `${cacheKey}${this.fileExtension}`)}' && ${awkCommand} '${join(tablePath, ".cache", `${cacheKey}${this.fileExtension}`)}'`
@@ -1230,9 +1241,9 @@ export default class Inibase {
1230
1241
  })).stdout
1231
1242
  .trimEnd()
1232
1243
  .split("\n");
1233
- if (where)
1244
+ if (where && options.perPage >= 0)
1234
1245
  lines = lines.slice((options.page - 1) * options.perPage, options.page * options.perPage);
1235
- else if (!this.totalItems.has(`${tableName}-*`))
1246
+ else if (!where && !this.totalItems.has(`${tableName}-*`))
1236
1247
  this.totalItems.set(`${tableName}-*`, pagination[1]);
1237
1248
  if (!lines.length)
1238
1249
  return null;
@@ -1245,7 +1256,7 @@ export default class Inibase {
1245
1256
  const field = Utils.getField(parse(fileName).name, schema);
1246
1257
  if (field) {
1247
1258
  if (field.key === "id" &&
1248
- globalConfig[this.databasePath].tables.get(tableName).config
1259
+ globalConfig[this.databasePath].tables?.get(tableName)?.config
1249
1260
  .decodeID)
1250
1261
  outputObject[field.key] = splitedFileColumns[index];
1251
1262
  else
@@ -1254,7 +1265,9 @@ export default class Inibase {
1254
1265
  });
1255
1266
  return outputObject;
1256
1267
  });
1257
- const restOfColumns = await this.get(tableName, outputArray.map(({ id }) => id), (({ sort, ...rest }) => rest)(options));
1268
+ const restOfColumns = await this.get(tableName, outputArray
1269
+ .map(({ id }) => id)
1270
+ .filter((id) => id !== undefined), (({ sort, ...rest }) => rest)(options));
1258
1271
  return restOfColumns
1259
1272
  ? outputArray.map((item) => ({
1260
1273
  ...item,
@@ -1279,7 +1292,8 @@ export default class Inibase {
1279
1292
  else if (((Array.isArray(where) && where.every(Utils.isNumber)) ||
1280
1293
  Utils.isNumber(where)) &&
1281
1294
  (_whereIsLinesNumbers ||
1282
- !globalConfig[this.databasePath].tables.get(tableName).config.decodeID)) {
1295
+ !globalConfig[this.databasePath].tables?.get(tableName)?.config
1296
+ .decodeID)) {
1283
1297
  // "where" in this case, is the line(s) number(s) and not id(s)
1284
1298
  let lineNumbers = where;
1285
1299
  if (!Array.isArray(lineNumbers))
@@ -1293,7 +1307,8 @@ export default class Inibase {
1293
1307
  RETURN = RETURN[0];
1294
1308
  }
1295
1309
  else if ((!_whereIsLinesNumbers &&
1296
- globalConfig[this.databasePath].tables.get(tableName).config.decodeID &&
1310
+ globalConfig[this.databasePath].tables?.get(tableName)?.config
1311
+ .decodeID &&
1297
1312
  ((Array.isArray(where) && where.every(Utils.isNumber)) ||
1298
1313
  Utils.isNumber(where))) ||
1299
1314
  (Array.isArray(where) && where.every(Utils.isValidID)) ||
@@ -1321,7 +1336,7 @@ export default class Inibase {
1321
1336
  else if (Utils.isObject(where)) {
1322
1337
  let cachedFilePath = "";
1323
1338
  // Criteria
1324
- if (globalConfig[this.databasePath].tables.get(tableName).config.cache) {
1339
+ if (globalConfig[this.databasePath].tables?.get(tableName)?.config.cache) {
1325
1340
  cachedFilePath = join(tablePath, ".cache", `${UtilsServer.hashString(inspect(where, { sorted: true }))}${this.fileExtension}`);
1326
1341
  if (await File.isExists(cachedFilePath)) {
1327
1342
  const cachedItems = (await readFile(cachedFilePath, "utf8")).split(",");
@@ -1352,7 +1367,7 @@ export default class Inibase {
1352
1367
  for (const [key] of this.totalItems)
1353
1368
  if (key.startsWith(`${tableName}-`) && key !== `${tableName}-id`)
1354
1369
  this.totalItems.delete(key);
1355
- if (globalConfig[this.databasePath].tables.get(tableName).config.cache)
1370
+ if (globalConfig[this.databasePath].tables?.get(tableName)?.config.cache)
1356
1371
  await writeFile(cachedFilePath, Object.keys(LineNumberDataObj).join(","));
1357
1372
  }
1358
1373
  }
@@ -1362,7 +1377,7 @@ export default class Inibase {
1362
1377
  return null;
1363
1378
  if (total === undefined)
1364
1379
  total = this.totalItems.has(`${tableName}-*`)
1365
- ? this.totalItems.get(`${tableName}-*`)
1380
+ ? (this.totalItems.get(`${tableName}-*`) ?? 0)
1366
1381
  : Math.max(...[...this.totalItems.entries()]
1367
1382
  .filter(([k]) => k.startsWith(`${tableName}-`))
1368
1383
  .map(([, v]) => v));
@@ -1382,7 +1397,7 @@ export default class Inibase {
1382
1397
  };
1383
1398
  const tablePath = join(this.databasePath, tableName);
1384
1399
  await this.getTable(tableName);
1385
- if (!globalConfig[this.databasePath].tables.get(tableName).schema)
1400
+ if (!globalConfig[this.databasePath].tables?.get(tableName)?.schema)
1386
1401
  throw this.createError("NO_SCHEMA", tableName);
1387
1402
  if (!returnPostedData)
1388
1403
  returnPostedData = false;
@@ -1392,7 +1407,7 @@ export default class Inibase {
1392
1407
  const renameList = [];
1393
1408
  try {
1394
1409
  await File.lock(join(tablePath, ".tmp"), keys);
1395
- let paginationFilePath;
1410
+ let paginationFilePath = "";
1396
1411
  for await (const fileName of glob("*.pagination", { cwd: tablePath }))
1397
1412
  paginationFilePath = join(tablePath, fileName);
1398
1413
  let [lastId, _totalItems] = parse(paginationFilePath)
@@ -1411,36 +1426,38 @@ export default class Inibase {
1411
1426
  clonedData.createdAt = Date.now();
1412
1427
  clonedData.updatedAt = undefined;
1413
1428
  }
1414
- clonedData = this.formatData(clonedData, globalConfig[this.databasePath].tables.get(tableName).schema, false);
1415
- const pathesContents = this.joinPathesContents(tableName, globalConfig[this.databasePath].tables.get(tableName).config.prepend
1429
+ clonedData = this.formatData(clonedData, globalConfig[this.databasePath].tables?.get(tableName)?.schema ?? [], false);
1430
+ const pathesContents = this.joinPathesContents(tableName, globalConfig[this.databasePath].tables?.get(tableName)?.config.prepend
1416
1431
  ? Array.isArray(clonedData)
1417
1432
  ? clonedData.toReversed()
1418
1433
  : clonedData
1419
1434
  : clonedData);
1420
- await Promise.allSettled(Object.entries(pathesContents).map(async ([path, content]) => renameList.push(globalConfig[this.databasePath].tables.get(tableName).config.prepend
1435
+ await Promise.allSettled(Object.entries(pathesContents).map(async ([path, content]) => renameList.push(globalConfig[this.databasePath].tables?.get(tableName)?.config
1436
+ .prepend
1421
1437
  ? await File.prepend(path, content)
1422
1438
  : await File.append(path, content))));
1423
1439
  await Promise.allSettled(renameList
1424
1440
  .filter(([_, filePath]) => filePath)
1425
1441
  .map(async ([tempPath, filePath]) => rename(tempPath, filePath)));
1426
- if (globalConfig[this.databasePath].tables.get(tableName).config.cache)
1442
+ if (globalConfig[this.databasePath].tables?.get(tableName)?.config.cache)
1427
1443
  await this.clearCache(tableName);
1428
1444
  const currentValue = this.totalItems.get(`${tableName}-*`) || 0;
1429
1445
  this.totalItems.set(`${tableName}-*`, currentValue + (Array.isArray(data) ? data.length : 1));
1430
1446
  await rename(paginationFilePath, join(tablePath, `${lastId}-${this.totalItems.get(`${tableName}-*`)}.pagination`));
1431
1447
  if (returnPostedData)
1432
- return this.get(tableName, globalConfig[this.databasePath].tables.get(tableName).config.prepend
1448
+ return this.get(tableName, globalConfig[this.databasePath].tables?.get(tableName)?.config.prepend
1433
1449
  ? Array.isArray(clonedData)
1434
1450
  ? clonedData.map((_, index) => index + 1).toReversed()
1435
1451
  : 1
1436
1452
  : Array.isArray(clonedData)
1437
1453
  ? clonedData
1438
- .map((_, index) => this.totalItems.get(`${tableName}-*`) - index)
1454
+ .map((_, index) => (this.totalItems.get(`${tableName}-*`) ?? 0) - index)
1439
1455
  .toReversed()
1440
1456
  : this.totalItems.get(`${tableName}-*`), options, !Utils.isArrayOfObjects(clonedData), // return only one item if data is not array of objects
1441
1457
  undefined, true);
1442
1458
  return Array.isArray(clonedData)
1443
- ? (globalConfig[this.databasePath].tables.get(tableName).config.prepend
1459
+ ? (globalConfig[this.databasePath].tables?.get(tableName)?.config
1460
+ .prepend
1444
1461
  ? clonedData.toReversed()
1445
1462
  : clonedData).map(({ id }) => UtilsServer.encodeID(id))
1446
1463
  : UtilsServer.encodeID(clonedData.id);
@@ -1465,7 +1482,9 @@ export default class Inibase {
1465
1482
  if (Utils.isArrayOfObjects(clonedData)) {
1466
1483
  if (!clonedData.every((item) => Object.hasOwn(item, "id") && Utils.isValidID(item.id)))
1467
1484
  throw this.createError("INVALID_ID");
1468
- return this.put(tableName, clonedData, clonedData.map(({ id }) => id), options, returnUpdatedData);
1485
+ return this.put(tableName, clonedData, clonedData
1486
+ .map(({ id }) => id)
1487
+ .filter((id) => id !== undefined), options, returnUpdatedData);
1469
1488
  }
1470
1489
  if (Object.hasOwn(clonedData, "id")) {
1471
1490
  if (!Utils.isValidID(clonedData.id))
@@ -1473,7 +1492,7 @@ export default class Inibase {
1473
1492
  return this.put(tableName, clonedData, clonedData.id, options, returnUpdatedData);
1474
1493
  }
1475
1494
  await this.validateTableData(tableName, clonedData, true);
1476
- clonedData = this.formatData(clonedData, globalConfig[this.databasePath].tables.get(tableName).schema, true);
1495
+ clonedData = this.formatData(clonedData, globalConfig[this.databasePath].tables?.get(tableName)?.schema ?? [], true);
1477
1496
  const pathesContents = this.joinPathesContents(tableName, {
1478
1497
  ...(({ id, ...restOfData }) => restOfData)(clonedData),
1479
1498
  updatedAt: Date.now(),
@@ -1488,7 +1507,7 @@ export default class Inibase {
1488
1507
  await Promise.allSettled(renameList
1489
1508
  .filter(([_, filePath]) => filePath)
1490
1509
  .map(async ([tempPath, filePath]) => rename(tempPath, filePath)));
1491
- if (globalConfig[this.databasePath].tables.get(tableName).config.cache)
1510
+ if (globalConfig[this.databasePath].tables?.get(tableName)?.config.cache)
1492
1511
  await this.clearCache(join(tablePath, ".cache"));
1493
1512
  if (returnUpdatedData)
1494
1513
  return await this.get(tableName, undefined, options);
@@ -1504,10 +1523,11 @@ export default class Inibase {
1504
1523
  else if (((Array.isArray(where) && where.every(Utils.isNumber)) ||
1505
1524
  Utils.isNumber(where)) &&
1506
1525
  (_whereIsLinesNumbers ||
1507
- !globalConfig[this.databasePath].tables.get(tableName).config.decodeID)) {
1526
+ !globalConfig[this.databasePath].tables?.get(tableName)?.config
1527
+ .decodeID)) {
1508
1528
  // "where" in this case, is the line(s) number(s) and not id(s)
1509
1529
  await this.validateTableData(tableName, clonedData, true);
1510
- clonedData = this.formatData(clonedData, globalConfig[this.databasePath].tables.get(tableName).schema, true);
1530
+ clonedData = this.formatData(clonedData, globalConfig[this.databasePath].tables?.get(tableName)?.schema ?? [], true);
1511
1531
  const pathesContents = Object.fromEntries(Object.entries(this.joinPathesContents(tableName, Array.isArray(clonedData)
1512
1532
  ? clonedData.map((item) => ({
1513
1533
  ...item,
@@ -1529,7 +1549,7 @@ export default class Inibase {
1529
1549
  await Promise.allSettled(renameList
1530
1550
  .filter(([_, filePath]) => filePath)
1531
1551
  .map(async ([tempPath, filePath]) => rename(tempPath, filePath)));
1532
- if (globalConfig[this.databasePath].tables.get(tableName).config.cache)
1552
+ if (globalConfig[this.databasePath].tables?.get(tableName)?.config.cache)
1533
1553
  await this.clearCache(tableName);
1534
1554
  if (returnUpdatedData)
1535
1555
  return this.get(tableName, where, options, !Array.isArray(where), undefined, true);
@@ -1543,7 +1563,8 @@ export default class Inibase {
1543
1563
  }
1544
1564
  }
1545
1565
  else if ((!_whereIsLinesNumbers &&
1546
- globalConfig[this.databasePath].tables.get(tableName).config.decodeID &&
1566
+ globalConfig[this.databasePath].tables?.get(tableName)?.config
1567
+ .decodeID &&
1547
1568
  ((Array.isArray(where) && where.every(Utils.isNumber)) ||
1548
1569
  Utils.isNumber(where))) ||
1549
1570
  (Array.isArray(where) && where.every(Utils.isValidID)) ||
@@ -1573,8 +1594,8 @@ export default class Inibase {
1573
1594
  if (!where) {
1574
1595
  try {
1575
1596
  await File.lock(join(tablePath, ".tmp"));
1576
- let paginationFilePath;
1577
- let pagination;
1597
+ let paginationFilePath = "";
1598
+ let pagination = [0, 0];
1578
1599
  for await (const paginationFileName of glob("*.pagination", {
1579
1600
  cwd: tablePath,
1580
1601
  })) {
@@ -1586,7 +1607,7 @@ export default class Inibase {
1586
1607
  await Promise.allSettled((await readdir(tablePath))
1587
1608
  ?.filter((fileName) => fileName.endsWith(this.getFileExtension(tableName)))
1588
1609
  .map(async (file) => unlink(join(tablePath, file))));
1589
- if (globalConfig[this.databasePath].tables.get(tableName).config.cache)
1610
+ if (globalConfig[this.databasePath].tables?.get(tableName)?.config.cache)
1590
1611
  await this.clearCache(tableName);
1591
1612
  await rename(paginationFilePath, join(tablePath, `${pagination[0]}-0.pagination`));
1592
1613
  return true;
@@ -1598,15 +1619,16 @@ export default class Inibase {
1598
1619
  if (((Array.isArray(where) && where.every(Utils.isNumber)) ||
1599
1620
  Utils.isNumber(where)) &&
1600
1621
  (_whereIsLinesNumbers ||
1601
- !globalConfig[this.databasePath].tables.get(tableName).config.decodeID)) {
1622
+ !globalConfig[this.databasePath].tables?.get(tableName)?.config
1623
+ .decodeID)) {
1602
1624
  // "where" in this case, is the line(s) number(s) and not id(s)
1603
1625
  const files = (await readdir(tablePath))?.filter((fileName) => fileName.endsWith(this.getFileExtension(tableName)));
1604
1626
  if (files.length) {
1605
1627
  const renameList = [];
1606
1628
  try {
1607
1629
  await File.lock(join(tablePath, ".tmp"));
1608
- let paginationFilePath;
1609
- let pagination;
1630
+ let paginationFilePath = "";
1631
+ let pagination = [0, 0];
1610
1632
  for await (const paginationFileName of glob("*.pagination", {
1611
1633
  cwd: tablePath,
1612
1634
  })) {
@@ -1626,7 +1648,7 @@ export default class Inibase {
1626
1648
  await Promise.allSettled((await readdir(tablePath))
1627
1649
  ?.filter((fileName) => fileName.endsWith(this.getFileExtension(tableName)))
1628
1650
  .map(async (file) => unlink(join(tablePath, file))));
1629
- if (globalConfig[this.databasePath].tables.get(tableName).config.cache)
1651
+ if (globalConfig[this.databasePath].tables?.get(tableName)?.config.cache)
1630
1652
  await this.clearCache(tableName);
1631
1653
  await rename(paginationFilePath, join(tablePath, `${pagination[0]}-${pagination[1] - (Array.isArray(where) ? where.length : 1)}.pagination`));
1632
1654
  return true;
@@ -1641,7 +1663,8 @@ export default class Inibase {
1641
1663
  }
1642
1664
  }
1643
1665
  if ((!_whereIsLinesNumbers &&
1644
- globalConfig[this.databasePath].tables.get(tableName).config.decodeID &&
1666
+ globalConfig[this.databasePath].tables?.get(tableName)?.config
1667
+ .decodeID &&
1645
1668
  ((Array.isArray(where) && where.every(Utils.isNumber)) ||
1646
1669
  Utils.isNumber(where))) ||
1647
1670
  (Array.isArray(where) && where.every(Utils.isValidID)) ||
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "inibase",
3
- "version": "1.5.6",
3
+ "version": "1.5.8",
4
4
  "type": "module",
5
5
  "author": {
6
6
  "name": "Karim Amahtil",