inibase 1.0.0-rc.90 → 1.0.0-rc.92

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/cli.js CHANGED
@@ -7,7 +7,7 @@ import { parseArgs } from "node:util";
7
7
  import Inison from "inison";
8
8
  import { isExists } from "./file.js";
9
9
  import Inibase, {} from "./index.js";
10
- import { isJSON, isNumber, setField, unsetField } from "./utils.js";
10
+ import { isStringified, isNumber, setField, unsetField } from "./utils.js";
11
11
  const textGreen = (input) => `\u001b[1;32m${input}\u001b[0m`;
12
12
  const textRed = (input) => `\u001b[1;31m${input}\u001b[0m`;
13
13
  const textBlue = (input) => `\u001b[1;34m${input}\u001b[0m`;
@@ -215,11 +215,11 @@ rl.on("line", async (input) => {
215
215
  where = -1;
216
216
  else if (isNumber(parsedArgs.where))
217
217
  where = Number(parsedArgs.where);
218
- else if (isJSON(parsedArgs.where))
218
+ else if (isStringified(parsedArgs.where))
219
219
  where = Inison.unstringify(parsedArgs.where);
220
220
  }
221
221
  if (parsedArgs.sort) {
222
- if (isJSON(parsedArgs.sort))
222
+ if (isStringified(parsedArgs.sort))
223
223
  sort = Inison.unstringify(parsedArgs.sort);
224
224
  else
225
225
  sort = parsedArgs.sort;
@@ -227,7 +227,7 @@ rl.on("line", async (input) => {
227
227
  page = Number(parsedArgs.page) ?? undefined;
228
228
  perPage = Number(parsedArgs.perPage) ?? undefined;
229
229
  columns = parsedArgs.columns;
230
- if (parsedArgs.data && isJSON(parsedArgs.data))
230
+ if (parsedArgs.data && isStringified(parsedArgs.data))
231
231
  data = Inison.unstringify(parsedArgs.data);
232
232
  }
233
233
  switch (splitedInput[0].toLocaleLowerCase()) {
package/dist/file.js CHANGED
@@ -5,7 +5,7 @@ import { Transform } from "node:stream";
5
5
  import { pipeline } from "node:stream/promises";
6
6
  import { createGunzip, createGzip } from "node:zlib";
7
7
  import Inison from "inison";
8
- import { detectFieldType, isArrayOfObjects, isJSON, isNumber, isObject, } from "./utils.js";
8
+ import { detectFieldType, isArrayOfObjects, isStringified, isNumber, isObject, } from "./utils.js";
9
9
  import { compare, encodeID, exec, gunzip, gzip } from "./utils.server.js";
10
10
  export const lock = async (folderPath, prefix) => {
11
11
  let lockFile = null;
@@ -100,7 +100,7 @@ const secureString = (input) => {
100
100
  * @returns The secured and/or joined string.
101
101
  */
102
102
  export const encode = (input) => Array.isArray(input)
103
- ? input.every((_input) => typeof _input === "string" && isJSON(_input))
103
+ ? input.every((_input) => typeof _input === "string" && isStringified(_input))
104
104
  ? `[${input.join(",")}]`
105
105
  : Inison.stringify(input)
106
106
  : secureString(input);
@@ -174,7 +174,7 @@ export const decode = (input, fieldType, fieldChildrenType, secretKey) => {
174
174
  fieldType = detectFieldType(String(input), fieldType);
175
175
  // Decode the input using the decodeHelper function.
176
176
  return decodeHelper(typeof input === "string"
177
- ? isJSON(input)
177
+ ? isStringified(input)
178
178
  ? Inison.unstringify(input)
179
179
  : unSecureString(input)
180
180
  : input, fieldType, fieldChildrenType, secretKey);
@@ -194,7 +194,7 @@ export async function get(filePath, lineNumbers, fieldType, fieldChildrenType, s
194
194
  else if (lineNumbers == -1) {
195
195
  const command = filePath.endsWith(".gz")
196
196
  ? `zcat ${filePath} | sed -n '$p'`
197
- : `sed -n '$p' ${filePath}`, foundedLine = (await exec(command)).stdout.trim();
197
+ : `sed -n '$p' ${filePath}`, foundedLine = (await exec(command)).stdout.trimEnd();
198
198
  if (foundedLine)
199
199
  lines[linesCount] = decode(foundedLine, fieldType, fieldChildrenType, secretKey);
200
200
  }
@@ -215,7 +215,7 @@ export async function get(filePath, lineNumbers, fieldType, fieldChildrenType, s
215
215
  }
216
216
  const command = filePath.endsWith(".gz")
217
217
  ? `zcat ${filePath} | sed -n '${lineNumbers.join("p;")}p'`
218
- : `sed -n '${lineNumbers.join("p;")}p' ${filePath}`, foundedLines = (await exec(command)).stdout.trim().split("\n");
218
+ : `sed -n '${lineNumbers.join("p;")}p' ${filePath}`, foundedLines = (await exec(command)).stdout.trimEnd().split("\n");
219
219
  let index = 0;
220
220
  for (const line of foundedLines) {
221
221
  lines[lineNumbers[index]] = decode(line, fieldType, fieldChildrenType, secretKey);
@@ -461,7 +461,7 @@ export const search = async (filePath, operator, comparedAtValue, logicalOperato
461
461
  * Note: Reads through the file line by line to count the total number of lines.
462
462
  */
463
463
  export const count = async (filePath) => {
464
- // Number((await exec(`wc -l < ${filePath}`)).stdout.trim());
464
+ // Number((await exec(`wc -l < ${filePath}`)).stdout.trimEnd());
465
465
  let linesCount = 0;
466
466
  if (await isExists(filePath)) {
467
467
  let fileHandle = null;
package/dist/index.d.ts CHANGED
@@ -93,6 +93,7 @@ export default class Inibase {
93
93
  getTableSchema(tableName: string, encodeIDs?: boolean): Promise<Schema | undefined>;
94
94
  private throwErrorIfTableEmpty;
95
95
  private validateData;
96
+ private cleanObject;
96
97
  private formatField;
97
98
  private checkUnique;
98
99
  private formatData;
package/dist/index.js CHANGED
@@ -318,12 +318,22 @@ export default class Inibase {
318
318
  }
319
319
  }
320
320
  }
321
+ cleanObject(obj) {
322
+ const cleanedObject = Object.entries(obj).reduce((acc, [key, value]) => {
323
+ if (value !== undefined && value !== null && value !== "")
324
+ acc[key] = value;
325
+ return acc;
326
+ }, {});
327
+ return Object.keys(cleanedObject).length > 0 ? cleanedObject : null;
328
+ }
321
329
  formatField(value, fieldType, fieldChildrenType, _formatOnlyAvailiableKeys) {
322
330
  if (Array.isArray(fieldType))
323
331
  fieldType = (Utils.detectFieldType(value, fieldType) ??
324
332
  fieldType[0]);
325
333
  if (!value)
326
334
  return null;
335
+ if (fieldType !== "array" && Array.isArray(value))
336
+ value = value[0];
327
337
  switch (fieldType) {
328
338
  case "array":
329
339
  if (!fieldChildrenType)
@@ -332,16 +342,14 @@ export default class Inibase {
332
342
  value = [value];
333
343
  if (Utils.isArrayOfObjects(fieldChildrenType))
334
344
  return this.formatData(value, fieldChildrenType, _formatOnlyAvailiableKeys);
345
+ if (!value.length)
346
+ return null;
335
347
  return value.map((_value) => this.formatField(_value, fieldChildrenType));
336
348
  case "object":
337
- if (Array.isArray(value))
338
- value = value[0];
339
349
  if (Utils.isArrayOfObjects(fieldChildrenType))
340
350
  return this.formatData(value, fieldChildrenType, _formatOnlyAvailiableKeys);
341
351
  break;
342
352
  case "table":
343
- if (Array.isArray(value))
344
- value = value[0];
345
353
  if (Utils.isObject(value)) {
346
354
  if (Object.hasOwn(value, "id") &&
347
355
  (Utils.isValidID(value.id) ||
@@ -356,33 +364,29 @@ export default class Inibase {
356
364
  : UtilsServer.decodeID(value, this.salt);
357
365
  break;
358
366
  case "password":
359
- if (Array.isArray(value))
360
- value = value[0];
361
367
  return Utils.isPassword(value)
362
368
  ? value
363
369
  : UtilsServer.hashPassword(String(value));
364
370
  case "number":
365
- if (Array.isArray(value))
366
- value = value[0];
367
371
  return Utils.isNumber(value) ? Number(value) : null;
368
372
  case "date": {
369
- if (Array.isArray(value))
370
- value = value[0];
371
373
  if (Utils.isNumber(value))
372
374
  return value;
373
375
  const dateToTimestamp = Date.parse(value);
374
376
  return Number.isNaN(dateToTimestamp) ? null : dateToTimestamp;
375
377
  }
376
378
  case "id":
377
- if (Array.isArray(value))
378
- value = value[0];
379
379
  return Utils.isNumber(value)
380
380
  ? value
381
381
  : UtilsServer.decodeID(value, this.salt);
382
- case "json":
383
- return typeof value !== "string" || !Utils.isJSON(value)
384
- ? Inison.stringify(value)
385
- : value;
382
+ case "json": {
383
+ if (typeof value === "string" && Utils.isStringified(value))
384
+ return value;
385
+ const cleanedObject = this.cleanObject(value);
386
+ if (cleanedObject)
387
+ return Inison.stringify(cleanedObject);
388
+ return null;
389
+ }
386
390
  default:
387
391
  return value;
388
392
  }
@@ -602,19 +606,32 @@ export default class Inibase {
602
606
  if (field.table &&
603
607
  (await File.isExists(join(this.databasePath, field.table))) &&
604
608
  (await File.isExists(join(tablePath, `${(prefix ?? "") + field.key}${this.getFileExtension(tableName)}`)))) {
605
- const items = await File.get(join(tablePath, `${(prefix ?? "") + field.key}${this.getFileExtension(tableName)}`), linesNumber, field.type, field.children, this.salt);
606
- if (items)
607
- for await (const [index, item] of Object.entries(items)) {
609
+ const itemsIDs = await File.get(join(tablePath, `${(prefix ?? "") + field.key}${this.getFileExtension(tableName)}`), linesNumber, field.type, field.children, this.salt);
610
+ if (itemsIDs) {
611
+ const searchableIDs = {};
612
+ for (const [index, item] of Object.entries(itemsIDs)) {
608
613
  if (!RETURN[index])
609
614
  RETURN[index] = {};
610
615
  if (item !== null && item !== undefined)
611
- RETURN[index][field.key] = await this.get(field.table, item, {
612
- ...options,
613
- columns: options.columns
614
- ?.filter((column) => column.includes(`${field.key}.`))
615
- .map((column) => column.replace(`${field.key}.`, "")),
616
- });
616
+ searchableIDs[index] = item;
617
+ }
618
+ if (Object.keys(searchableIDs).length) {
619
+ const items = await this.get(field.table, [].concat(...Object.values(searchableIDs)), {
620
+ ...options,
621
+ perPage: Number.POSITIVE_INFINITY,
622
+ columns: options.columns
623
+ ?.filter((column) => column.includes(`${field.key}.`))
624
+ .map((column) => column.replace(`${field.key}.`, "")),
625
+ });
626
+ if (items) {
627
+ let cursor = 0;
628
+ for (const [index, Ids] of Object.entries(searchableIDs)) {
629
+ RETURN[index][field.key] = items.slice(cursor, cursor + Ids.length);
630
+ cursor += Ids.length;
631
+ }
632
+ }
617
633
  }
634
+ }
618
635
  }
619
636
  }
620
637
  else if (await File.isExists(join(tablePath, `${(prefix ?? "") + field.key}${this.getFileExtension(tableName)}`))) {
@@ -644,19 +661,32 @@ export default class Inibase {
644
661
  if (field.table &&
645
662
  (await File.isExists(join(this.databasePath, field.table))) &&
646
663
  (await File.isExists(join(tablePath, `${(prefix ?? "") + field.key}${this.getFileExtension(tableName)}`)))) {
647
- const items = await File.get(join(tablePath, `${(prefix ?? "") + field.key}${this.getFileExtension(tableName)}`), linesNumber, "number", undefined, this.salt);
648
- if (items)
649
- for await (const [index, item] of Object.entries(items)) {
664
+ const itemsIDs = await File.get(join(tablePath, `${(prefix ?? "") + field.key}${this.getFileExtension(tableName)}`), linesNumber, field.type, field.children, this.salt);
665
+ if (itemsIDs) {
666
+ const searchableIDs = {};
667
+ for (const [index, item] of Object.entries(itemsIDs)) {
650
668
  if (!RETURN[index])
651
669
  RETURN[index] = {};
652
670
  if (item !== null && item !== undefined)
653
- RETURN[index][field.key] = await this.get(field.table, UtilsServer.encodeID(item, this.salt), {
654
- ...options,
655
- columns: options.columns
656
- ?.filter((column) => column.includes(`${field.key}.`))
657
- .map((column) => column.replace(`${field.key}.`, "")),
658
- });
671
+ searchableIDs[index] = item;
672
+ }
673
+ if (Object.keys(searchableIDs).length) {
674
+ const items = await this.get(field.table, Object.values(searchableIDs), {
675
+ ...options,
676
+ perPage: Number.POSITIVE_INFINITY,
677
+ columns: options.columns
678
+ ?.filter((column) => column.includes(`${field.key}.`))
679
+ .map((column) => column.replace(`${field.key}.`, "")),
680
+ });
681
+ if (items) {
682
+ let cursor = 0;
683
+ for (const [index] of Object.entries(searchableIDs)) {
684
+ RETURN[index][field.key] = items[cursor];
685
+ cursor++;
686
+ }
687
+ }
659
688
  }
689
+ }
660
690
  }
661
691
  }
662
692
  else if (await File.isExists(join(tablePath, `${(prefix ?? "") + field.key}${this.getFileExtension(tableName)}`))) {
@@ -906,7 +936,7 @@ export default class Inibase {
906
936
  : `${pasteCommand} | ${sortCommand} | ${awkCommand}`, {
907
937
  encoding: "utf-8",
908
938
  })).stdout
909
- .trim()
939
+ .trimEnd()
910
940
  .split("\n");
911
941
  if (where)
912
942
  lines = lines.slice((options.page - 1) * options.perPage, options.page * options.perPage);
@@ -1130,7 +1160,6 @@ export default class Inibase {
1130
1160
  if (Utils.isArrayOfObjects(data)) {
1131
1161
  if (!data.every((item) => Object.hasOwn(item, "id") && Utils.isValidID(item.id)))
1132
1162
  throw this.throwError("INVALID_ID");
1133
- // TODO: Reduce I/O
1134
1163
  return this.put(tableName, data, data.map(({ id }) => id), options, returnUpdatedData || undefined);
1135
1164
  }
1136
1165
  if (Object.hasOwn(data, "id")) {
package/dist/utils.d.ts CHANGED
@@ -139,7 +139,7 @@ export declare const isValidID: (input: any) => input is string;
139
139
  * @param {string} str - The string to be checked.
140
140
  * @returns {boolean} Returns true if the string is valid JSON, otherwise false.
141
141
  */
142
- export declare const isJSON: (str: string) => boolean;
142
+ export declare const isStringified: (str: string) => boolean;
143
143
  /**
144
144
  * Identifies and returns properties that have changed between two objects.
145
145
  *
package/dist/utils.js CHANGED
@@ -170,7 +170,7 @@ export const isValidID = (input) => {
170
170
  * @param {string} str - The string to be checked.
171
171
  * @returns {boolean} Returns true if the string is valid JSON, otherwise false.
172
172
  */
173
- export const isJSON = (str) => str === "null" || str === "undefined" || str[0] === "{" || str[0] === "[";
173
+ export const isStringified = (str) => str === "null" || str === "undefined" || str[0] === "{" || str[0] === "[";
174
174
  /**
175
175
  * Identifies and returns properties that have changed between two objects.
176
176
  *
@@ -222,7 +222,7 @@ export const detectFieldType = (input, availableTypes) => {
222
222
  return "url";
223
223
  if (availableTypes.includes("password") && isPassword(input))
224
224
  return "password";
225
- if (availableTypes.includes("json") && isJSON(input))
225
+ if (availableTypes.includes("json") && isStringified(input))
226
226
  return "json";
227
227
  if (availableTypes.includes("json") && isDate(input))
228
228
  return "json";
@@ -355,7 +355,7 @@ export const validateFieldType = (value, fieldType, fieldChildrenType) => {
355
355
  case "id":
356
356
  return isNumber(value) || isValidID(value);
357
357
  case "json":
358
- return isJSON(value) || Array.isArray(value) || isObject(value);
358
+ return isStringified(value) || Array.isArray(value) || isObject(value);
359
359
  default:
360
360
  return false;
361
361
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "inibase",
3
- "version": "1.0.0-rc.90",
3
+ "version": "1.0.0-rc.92",
4
4
  "type": "module",
5
5
  "author": {
6
6
  "name": "Karim Amahtil",