inibase 1.5.7 → 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,7 +959,7 @@ 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
  });
@@ -989,7 +995,8 @@ export default class Inibase {
989
995
  }
990
996
  return acc[key];
991
997
  }, obj);
992
- target[lastKey] = value;
998
+ if (lastKey !== undefined)
999
+ target[lastKey] = value;
993
1000
  }
994
1001
  async applyCriteria(tableName, options, criteria, allTrue, searchIn) {
995
1002
  const tablePath = join(this.databasePath, tableName);
@@ -1002,7 +1009,7 @@ export default class Inibase {
1002
1009
  const criteriaOR = criteria.or;
1003
1010
  if (criteriaOR)
1004
1011
  delete criteria.or;
1005
- const schema = globalConfig[this.databasePath].tables.get(tableName).schema;
1012
+ const schema = globalConfig[this.databasePath].tables?.get(tableName)?.schema ?? [];
1006
1013
  if (Object.keys(criteria).length) {
1007
1014
  if (allTrue === undefined)
1008
1015
  allTrue = true;
@@ -1030,15 +1037,17 @@ export default class Inibase {
1030
1037
  if (nestedAnd || nestedOr) {
1031
1038
  const logicalChild = nestedAnd ?? nestedOr;
1032
1039
  const logic = nestedAnd ? "and" : "or";
1033
- const crit = Object.entries(logicalChild)
1034
- .map((item) => typeof item[1] === "string"
1035
- ? Utils.FormatObjectCriteriaValue(item[1])
1036
- : ["=", item[1]])
1037
- .filter(Boolean);
1038
- if (crit.length) {
1039
- searchOperator = crit.map((c) => c[0]);
1040
- searchComparedAtValue = crit.map((c) => c[1]);
1041
- 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
+ }
1042
1051
  }
1043
1052
  delete value[logic];
1044
1053
  }
@@ -1052,14 +1061,14 @@ export default class Inibase {
1052
1061
  searchOperator = "=";
1053
1062
  searchComparedAtValue = value;
1054
1063
  }
1055
- 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, {
1056
1065
  ...field,
1057
1066
  databasePath: this.databasePath,
1058
1067
  table: field.table ?? tableName,
1059
- }, options.perPage < 0 ? undefined : options.perPage, options.perPage < 0
1068
+ }, (options.perPage ?? 0) < 0 ? undefined : options.perPage, (options.perPage ?? 0) < 0
1060
1069
  ? undefined
1061
1070
  : (options.page - 1) * options.perPage +
1062
- (options.page > 1 ? 1 : 0), true);
1071
+ ((options.page ?? 1) > 1 ? 1 : 0), true);
1063
1072
  if (!searchResult) {
1064
1073
  if (allTrue)
1065
1074
  return null;
@@ -1153,10 +1162,10 @@ export default class Inibase {
1153
1162
  options.perPage = options.perPage || 15;
1154
1163
  let total;
1155
1164
  let RETURN;
1156
- let schema = structuredClone((await this.getTable(tableName)).schema);
1165
+ let schema = structuredClone((await this.getTable(tableName))?.schema);
1157
1166
  if (!schema)
1158
1167
  throw this.createError("NO_SCHEMA", tableName);
1159
- let pagination;
1168
+ let pagination = [0, 0];
1160
1169
  for await (const paginationFileName of glob("*.pagination", {
1161
1170
  cwd: tablePath,
1162
1171
  }))
@@ -1185,7 +1194,7 @@ export default class Inibase {
1185
1194
  .map((column) => [column, true]);
1186
1195
  let cacheKey = "";
1187
1196
  // Criteria
1188
- if (globalConfig[this.databasePath].tables.get(tableName).config.cache)
1197
+ if (globalConfig[this.databasePath].tables?.get(tableName)?.config.cache)
1189
1198
  cacheKey = UtilsServer.hashString(inspect(sortArray, { sorted: true }));
1190
1199
  if (where) {
1191
1200
  const lineNumbers = await this.get(tableName, where, undefined, undefined, true);
@@ -1223,7 +1232,7 @@ export default class Inibase {
1223
1232
  if (cacheKey)
1224
1233
  await File.lock(join(tablePath, ".tmp"), cacheKey);
1225
1234
  // Combine && Execute the commands synchronously
1226
- 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
1227
1236
  ? (await File.isExists(join(tablePath, ".cache", `${cacheKey}${this.fileExtension}`)))
1228
1237
  ? `${awkCommand} '${join(tablePath, ".cache", `${cacheKey}${this.fileExtension}`)}'`
1229
1238
  : `${pasteCommand} | ${sortCommand} -o '${join(tablePath, ".cache", `${cacheKey}${this.fileExtension}`)}' && ${awkCommand} '${join(tablePath, ".cache", `${cacheKey}${this.fileExtension}`)}'`
@@ -1232,9 +1241,9 @@ export default class Inibase {
1232
1241
  })).stdout
1233
1242
  .trimEnd()
1234
1243
  .split("\n");
1235
- if (where)
1244
+ if (where && options.perPage >= 0)
1236
1245
  lines = lines.slice((options.page - 1) * options.perPage, options.page * options.perPage);
1237
- else if (!this.totalItems.has(`${tableName}-*`))
1246
+ else if (!where && !this.totalItems.has(`${tableName}-*`))
1238
1247
  this.totalItems.set(`${tableName}-*`, pagination[1]);
1239
1248
  if (!lines.length)
1240
1249
  return null;
@@ -1247,7 +1256,7 @@ export default class Inibase {
1247
1256
  const field = Utils.getField(parse(fileName).name, schema);
1248
1257
  if (field) {
1249
1258
  if (field.key === "id" &&
1250
- globalConfig[this.databasePath].tables.get(tableName).config
1259
+ globalConfig[this.databasePath].tables?.get(tableName)?.config
1251
1260
  .decodeID)
1252
1261
  outputObject[field.key] = splitedFileColumns[index];
1253
1262
  else
@@ -1256,7 +1265,9 @@ export default class Inibase {
1256
1265
  });
1257
1266
  return outputObject;
1258
1267
  });
1259
- 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));
1260
1271
  return restOfColumns
1261
1272
  ? outputArray.map((item) => ({
1262
1273
  ...item,
@@ -1281,7 +1292,8 @@ export default class Inibase {
1281
1292
  else if (((Array.isArray(where) && where.every(Utils.isNumber)) ||
1282
1293
  Utils.isNumber(where)) &&
1283
1294
  (_whereIsLinesNumbers ||
1284
- !globalConfig[this.databasePath].tables.get(tableName).config.decodeID)) {
1295
+ !globalConfig[this.databasePath].tables?.get(tableName)?.config
1296
+ .decodeID)) {
1285
1297
  // "where" in this case, is the line(s) number(s) and not id(s)
1286
1298
  let lineNumbers = where;
1287
1299
  if (!Array.isArray(lineNumbers))
@@ -1295,7 +1307,8 @@ export default class Inibase {
1295
1307
  RETURN = RETURN[0];
1296
1308
  }
1297
1309
  else if ((!_whereIsLinesNumbers &&
1298
- globalConfig[this.databasePath].tables.get(tableName).config.decodeID &&
1310
+ globalConfig[this.databasePath].tables?.get(tableName)?.config
1311
+ .decodeID &&
1299
1312
  ((Array.isArray(where) && where.every(Utils.isNumber)) ||
1300
1313
  Utils.isNumber(where))) ||
1301
1314
  (Array.isArray(where) && where.every(Utils.isValidID)) ||
@@ -1323,7 +1336,7 @@ export default class Inibase {
1323
1336
  else if (Utils.isObject(where)) {
1324
1337
  let cachedFilePath = "";
1325
1338
  // Criteria
1326
- if (globalConfig[this.databasePath].tables.get(tableName).config.cache) {
1339
+ if (globalConfig[this.databasePath].tables?.get(tableName)?.config.cache) {
1327
1340
  cachedFilePath = join(tablePath, ".cache", `${UtilsServer.hashString(inspect(where, { sorted: true }))}${this.fileExtension}`);
1328
1341
  if (await File.isExists(cachedFilePath)) {
1329
1342
  const cachedItems = (await readFile(cachedFilePath, "utf8")).split(",");
@@ -1354,7 +1367,7 @@ export default class Inibase {
1354
1367
  for (const [key] of this.totalItems)
1355
1368
  if (key.startsWith(`${tableName}-`) && key !== `${tableName}-id`)
1356
1369
  this.totalItems.delete(key);
1357
- if (globalConfig[this.databasePath].tables.get(tableName).config.cache)
1370
+ if (globalConfig[this.databasePath].tables?.get(tableName)?.config.cache)
1358
1371
  await writeFile(cachedFilePath, Object.keys(LineNumberDataObj).join(","));
1359
1372
  }
1360
1373
  }
@@ -1364,7 +1377,7 @@ export default class Inibase {
1364
1377
  return null;
1365
1378
  if (total === undefined)
1366
1379
  total = this.totalItems.has(`${tableName}-*`)
1367
- ? this.totalItems.get(`${tableName}-*`)
1380
+ ? (this.totalItems.get(`${tableName}-*`) ?? 0)
1368
1381
  : Math.max(...[...this.totalItems.entries()]
1369
1382
  .filter(([k]) => k.startsWith(`${tableName}-`))
1370
1383
  .map(([, v]) => v));
@@ -1384,7 +1397,7 @@ export default class Inibase {
1384
1397
  };
1385
1398
  const tablePath = join(this.databasePath, tableName);
1386
1399
  await this.getTable(tableName);
1387
- if (!globalConfig[this.databasePath].tables.get(tableName).schema)
1400
+ if (!globalConfig[this.databasePath].tables?.get(tableName)?.schema)
1388
1401
  throw this.createError("NO_SCHEMA", tableName);
1389
1402
  if (!returnPostedData)
1390
1403
  returnPostedData = false;
@@ -1394,7 +1407,7 @@ export default class Inibase {
1394
1407
  const renameList = [];
1395
1408
  try {
1396
1409
  await File.lock(join(tablePath, ".tmp"), keys);
1397
- let paginationFilePath;
1410
+ let paginationFilePath = "";
1398
1411
  for await (const fileName of glob("*.pagination", { cwd: tablePath }))
1399
1412
  paginationFilePath = join(tablePath, fileName);
1400
1413
  let [lastId, _totalItems] = parse(paginationFilePath)
@@ -1413,36 +1426,38 @@ export default class Inibase {
1413
1426
  clonedData.createdAt = Date.now();
1414
1427
  clonedData.updatedAt = undefined;
1415
1428
  }
1416
- clonedData = this.formatData(clonedData, globalConfig[this.databasePath].tables.get(tableName).schema, false);
1417
- 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
1418
1431
  ? Array.isArray(clonedData)
1419
1432
  ? clonedData.toReversed()
1420
1433
  : clonedData
1421
1434
  : clonedData);
1422
- 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
1423
1437
  ? await File.prepend(path, content)
1424
1438
  : await File.append(path, content))));
1425
1439
  await Promise.allSettled(renameList
1426
1440
  .filter(([_, filePath]) => filePath)
1427
1441
  .map(async ([tempPath, filePath]) => rename(tempPath, filePath)));
1428
- if (globalConfig[this.databasePath].tables.get(tableName).config.cache)
1442
+ if (globalConfig[this.databasePath].tables?.get(tableName)?.config.cache)
1429
1443
  await this.clearCache(tableName);
1430
1444
  const currentValue = this.totalItems.get(`${tableName}-*`) || 0;
1431
1445
  this.totalItems.set(`${tableName}-*`, currentValue + (Array.isArray(data) ? data.length : 1));
1432
1446
  await rename(paginationFilePath, join(tablePath, `${lastId}-${this.totalItems.get(`${tableName}-*`)}.pagination`));
1433
1447
  if (returnPostedData)
1434
- 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
1435
1449
  ? Array.isArray(clonedData)
1436
1450
  ? clonedData.map((_, index) => index + 1).toReversed()
1437
1451
  : 1
1438
1452
  : Array.isArray(clonedData)
1439
1453
  ? clonedData
1440
- .map((_, index) => this.totalItems.get(`${tableName}-*`) - index)
1454
+ .map((_, index) => (this.totalItems.get(`${tableName}-*`) ?? 0) - index)
1441
1455
  .toReversed()
1442
1456
  : this.totalItems.get(`${tableName}-*`), options, !Utils.isArrayOfObjects(clonedData), // return only one item if data is not array of objects
1443
1457
  undefined, true);
1444
1458
  return Array.isArray(clonedData)
1445
- ? (globalConfig[this.databasePath].tables.get(tableName).config.prepend
1459
+ ? (globalConfig[this.databasePath].tables?.get(tableName)?.config
1460
+ .prepend
1446
1461
  ? clonedData.toReversed()
1447
1462
  : clonedData).map(({ id }) => UtilsServer.encodeID(id))
1448
1463
  : UtilsServer.encodeID(clonedData.id);
@@ -1467,7 +1482,9 @@ export default class Inibase {
1467
1482
  if (Utils.isArrayOfObjects(clonedData)) {
1468
1483
  if (!clonedData.every((item) => Object.hasOwn(item, "id") && Utils.isValidID(item.id)))
1469
1484
  throw this.createError("INVALID_ID");
1470
- 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);
1471
1488
  }
1472
1489
  if (Object.hasOwn(clonedData, "id")) {
1473
1490
  if (!Utils.isValidID(clonedData.id))
@@ -1475,7 +1492,7 @@ export default class Inibase {
1475
1492
  return this.put(tableName, clonedData, clonedData.id, options, returnUpdatedData);
1476
1493
  }
1477
1494
  await this.validateTableData(tableName, clonedData, true);
1478
- 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);
1479
1496
  const pathesContents = this.joinPathesContents(tableName, {
1480
1497
  ...(({ id, ...restOfData }) => restOfData)(clonedData),
1481
1498
  updatedAt: Date.now(),
@@ -1490,7 +1507,7 @@ export default class Inibase {
1490
1507
  await Promise.allSettled(renameList
1491
1508
  .filter(([_, filePath]) => filePath)
1492
1509
  .map(async ([tempPath, filePath]) => rename(tempPath, filePath)));
1493
- if (globalConfig[this.databasePath].tables.get(tableName).config.cache)
1510
+ if (globalConfig[this.databasePath].tables?.get(tableName)?.config.cache)
1494
1511
  await this.clearCache(join(tablePath, ".cache"));
1495
1512
  if (returnUpdatedData)
1496
1513
  return await this.get(tableName, undefined, options);
@@ -1506,10 +1523,11 @@ export default class Inibase {
1506
1523
  else if (((Array.isArray(where) && where.every(Utils.isNumber)) ||
1507
1524
  Utils.isNumber(where)) &&
1508
1525
  (_whereIsLinesNumbers ||
1509
- !globalConfig[this.databasePath].tables.get(tableName).config.decodeID)) {
1526
+ !globalConfig[this.databasePath].tables?.get(tableName)?.config
1527
+ .decodeID)) {
1510
1528
  // "where" in this case, is the line(s) number(s) and not id(s)
1511
1529
  await this.validateTableData(tableName, clonedData, true);
1512
- 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);
1513
1531
  const pathesContents = Object.fromEntries(Object.entries(this.joinPathesContents(tableName, Array.isArray(clonedData)
1514
1532
  ? clonedData.map((item) => ({
1515
1533
  ...item,
@@ -1531,7 +1549,7 @@ export default class Inibase {
1531
1549
  await Promise.allSettled(renameList
1532
1550
  .filter(([_, filePath]) => filePath)
1533
1551
  .map(async ([tempPath, filePath]) => rename(tempPath, filePath)));
1534
- if (globalConfig[this.databasePath].tables.get(tableName).config.cache)
1552
+ if (globalConfig[this.databasePath].tables?.get(tableName)?.config.cache)
1535
1553
  await this.clearCache(tableName);
1536
1554
  if (returnUpdatedData)
1537
1555
  return this.get(tableName, where, options, !Array.isArray(where), undefined, true);
@@ -1545,7 +1563,8 @@ export default class Inibase {
1545
1563
  }
1546
1564
  }
1547
1565
  else if ((!_whereIsLinesNumbers &&
1548
- globalConfig[this.databasePath].tables.get(tableName).config.decodeID &&
1566
+ globalConfig[this.databasePath].tables?.get(tableName)?.config
1567
+ .decodeID &&
1549
1568
  ((Array.isArray(where) && where.every(Utils.isNumber)) ||
1550
1569
  Utils.isNumber(where))) ||
1551
1570
  (Array.isArray(where) && where.every(Utils.isValidID)) ||
@@ -1575,8 +1594,8 @@ export default class Inibase {
1575
1594
  if (!where) {
1576
1595
  try {
1577
1596
  await File.lock(join(tablePath, ".tmp"));
1578
- let paginationFilePath;
1579
- let pagination;
1597
+ let paginationFilePath = "";
1598
+ let pagination = [0, 0];
1580
1599
  for await (const paginationFileName of glob("*.pagination", {
1581
1600
  cwd: tablePath,
1582
1601
  })) {
@@ -1588,7 +1607,7 @@ export default class Inibase {
1588
1607
  await Promise.allSettled((await readdir(tablePath))
1589
1608
  ?.filter((fileName) => fileName.endsWith(this.getFileExtension(tableName)))
1590
1609
  .map(async (file) => unlink(join(tablePath, file))));
1591
- if (globalConfig[this.databasePath].tables.get(tableName).config.cache)
1610
+ if (globalConfig[this.databasePath].tables?.get(tableName)?.config.cache)
1592
1611
  await this.clearCache(tableName);
1593
1612
  await rename(paginationFilePath, join(tablePath, `${pagination[0]}-0.pagination`));
1594
1613
  return true;
@@ -1600,15 +1619,16 @@ export default class Inibase {
1600
1619
  if (((Array.isArray(where) && where.every(Utils.isNumber)) ||
1601
1620
  Utils.isNumber(where)) &&
1602
1621
  (_whereIsLinesNumbers ||
1603
- !globalConfig[this.databasePath].tables.get(tableName).config.decodeID)) {
1622
+ !globalConfig[this.databasePath].tables?.get(tableName)?.config
1623
+ .decodeID)) {
1604
1624
  // "where" in this case, is the line(s) number(s) and not id(s)
1605
1625
  const files = (await readdir(tablePath))?.filter((fileName) => fileName.endsWith(this.getFileExtension(tableName)));
1606
1626
  if (files.length) {
1607
1627
  const renameList = [];
1608
1628
  try {
1609
1629
  await File.lock(join(tablePath, ".tmp"));
1610
- let paginationFilePath;
1611
- let pagination;
1630
+ let paginationFilePath = "";
1631
+ let pagination = [0, 0];
1612
1632
  for await (const paginationFileName of glob("*.pagination", {
1613
1633
  cwd: tablePath,
1614
1634
  })) {
@@ -1628,7 +1648,7 @@ export default class Inibase {
1628
1648
  await Promise.allSettled((await readdir(tablePath))
1629
1649
  ?.filter((fileName) => fileName.endsWith(this.getFileExtension(tableName)))
1630
1650
  .map(async (file) => unlink(join(tablePath, file))));
1631
- if (globalConfig[this.databasePath].tables.get(tableName).config.cache)
1651
+ if (globalConfig[this.databasePath].tables?.get(tableName)?.config.cache)
1632
1652
  await this.clearCache(tableName);
1633
1653
  await rename(paginationFilePath, join(tablePath, `${pagination[0]}-${pagination[1] - (Array.isArray(where) ? where.length : 1)}.pagination`));
1634
1654
  return true;
@@ -1643,7 +1663,8 @@ export default class Inibase {
1643
1663
  }
1644
1664
  }
1645
1665
  if ((!_whereIsLinesNumbers &&
1646
- globalConfig[this.databasePath].tables.get(tableName).config.decodeID &&
1666
+ globalConfig[this.databasePath].tables?.get(tableName)?.config
1667
+ .decodeID &&
1647
1668
  ((Array.isArray(where) && where.every(Utils.isNumber)) ||
1648
1669
  Utils.isNumber(where))) ||
1649
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.7",
3
+ "version": "1.5.8",
4
4
  "type": "module",
5
5
  "author": {
6
6
  "name": "Karim Amahtil",