inibase 1.0.0-rc.72 → 1.0.0-rc.74

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
@@ -1,13 +1,13 @@
1
1
  #!/usr/bin/env node
2
2
  import "dotenv/config";
3
+ import { readFileSync } from "node:fs";
4
+ import { basename, join } from "node:path";
3
5
  import { createInterface } from "node:readline/promises";
4
6
  import { parseArgs } from "node:util";
5
- import { basename, join } from "node:path";
6
- import { readFileSync } from "node:fs";
7
7
  import Inison from "inison";
8
+ import { isExists } from "./file.js";
8
9
  import Inibase from "./index.js";
9
10
  import { isJSON, isNumber, setField, unsetField } from "./utils.js";
10
- import { isExists } from "./file.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`;
package/dist/file.js CHANGED
@@ -1,12 +1,12 @@
1
- import { open, access, writeFile, readFile, constants as fsConstants, unlink, copyFile, appendFile, } from "node:fs/promises";
1
+ import { access, appendFile, copyFile, constants as fsConstants, open, readFile, unlink, writeFile, } from "node:fs/promises";
2
+ import { join } from "node:path";
2
3
  import { createInterface } from "node:readline";
3
4
  import { Transform } from "node:stream";
4
5
  import { pipeline } from "node:stream/promises";
5
- import { createGzip, createGunzip } from "node:zlib";
6
- import { join } from "node:path";
6
+ import { createGunzip, createGzip } from "node:zlib";
7
7
  import Inison from "inison";
8
8
  import { detectFieldType, isArrayOfObjects, isJSON, isNumber, isObject, } from "./utils.js";
9
- import { encodeID, compare, exec, gzip, gunzip } from "./utils.server.js";
9
+ import { compare, encodeID, exec, gunzip, gzip } from "./utils.server.js";
10
10
  export const lock = async (folderPath, prefix) => {
11
11
  let lockFile = null;
12
12
  const lockFilePath = join(folderPath, `${prefix ?? ""}.locked`);
@@ -109,7 +109,7 @@ export const encode = (input) => Array.isArray(input)
109
109
  */
110
110
  const unSecureString = (input) => {
111
111
  if (isNumber(input))
112
- return Number(input);
112
+ return String(input).at(0) === "0" ? input : Number(input);
113
113
  if (typeof input === "string")
114
114
  return input.replace(/\\n/g, "\n") || null;
115
115
  return null;
@@ -401,7 +401,6 @@ export const search = async (filePath, operator, comparedAtValue, logicalOperato
401
401
  const matchingLines = {};
402
402
  // Initialize counters for line number, found items, and processed items.
403
403
  let linesCount = 0;
404
- let foundItems = 0;
405
404
  const linesNumbers = new Set();
406
405
  let fileHandle = null;
407
406
  try {
@@ -426,13 +425,12 @@ export const search = async (filePath, operator, comparedAtValue, logicalOperato
426
425
  // If the line meets the conditions, process it.
427
426
  if (meetsConditions) {
428
427
  // Increment the found items counter.
429
- foundItems++;
430
428
  linesNumbers.add(linesCount);
431
429
  // Check if the line should be skipped based on the offset.
432
- if (offset && foundItems < offset)
430
+ if (offset && linesNumbers.size < offset)
433
431
  continue;
434
432
  // Check if the limit has been reached.
435
- if (limit && foundItems > limit) {
433
+ if (limit && linesNumbers.size > limit + (offset ?? 0)) {
436
434
  if (readWholeFile)
437
435
  continue;
438
436
  break;
@@ -442,8 +440,8 @@ export const search = async (filePath, operator, comparedAtValue, logicalOperato
442
440
  }
443
441
  }
444
442
  // Convert the Map to an object using Object.fromEntries and return the result.
445
- return foundItems
446
- ? [matchingLines, foundItems, linesNumbers.size ? linesNumbers : null]
443
+ return linesNumbers.size
444
+ ? [matchingLines, linesNumbers.size, linesNumbers]
447
445
  : [null, 0, null];
448
446
  }
449
447
  finally {
package/dist/index.d.ts CHANGED
@@ -150,12 +150,9 @@ export default class Inibase {
150
150
  *
151
151
  * @param {string} tableName
152
152
  * @param {(number | string | (number | string)[] | Criteria)} [where]
153
- * @return {*} {(Promise<string | number | (string | number)[] | null>)}
153
+ * @return {boolean | null} {(Promise<boolean | null>)}
154
154
  */
155
- delete(tableName: string, where?: number | string, _id?: string | string[]): Promise<string | null>;
156
- delete(tableName: string, where?: (number | string)[] | Criteria, _id?: string | string[]): Promise<string[] | null>;
157
- delete(tableName: string, where?: number, _id?: string | string[]): Promise<number | null>;
158
- delete(tableName: string, where?: number[], _id?: string | string[]): Promise<number[] | null>;
155
+ delete(tableName: string, where?: number | string | (number | string)[] | Criteria, _id?: string | string[]): Promise<boolean | null>;
159
156
  /**
160
157
  * Generate sum of column(s) in a table
161
158
  *
package/dist/index.js CHANGED
@@ -1,8 +1,8 @@
1
1
  import "dotenv/config";
2
- import { unlink, rename, mkdir, readdir, writeFile, readFile, } from "node:fs/promises";
3
- import { existsSync, appendFileSync, readFileSync } from "node:fs";
2
+ import { randomBytes, scryptSync } from "node:crypto";
3
+ import { appendFileSync, existsSync, readFileSync } from "node:fs";
4
+ import { mkdir, readFile, readdir, rename, unlink, writeFile, } from "node:fs/promises";
4
5
  import { join, parse } from "node:path";
5
- import { scryptSync, randomBytes } from "node:crypto";
6
6
  import { inspect } from "node:util";
7
7
  import Inison from "inison";
8
8
  import * as File from "./file.js";
@@ -97,7 +97,7 @@ export default class Inibase {
97
97
  // if config not set => load default global env config
98
98
  if (!config)
99
99
  config = {
100
- compression: process.env.INIBASE_COMPRESSION === "true",
100
+ compression: process.env.INIBASE_COMPRESSION == "true",
101
101
  cache: process.env.INIBASE_CACHE === "true",
102
102
  prepend: process.env.INIBASE_PREPEND === "true",
103
103
  };
@@ -110,7 +110,7 @@ export default class Inibase {
110
110
  await writeFile(join(tablePath, ".prepend.config"), "");
111
111
  }
112
112
  if (schema)
113
- await writeFile(join(tablePath, "schema.json"), JSON.stringify(UtilsServer.addIdToSchema(schema, 0, this.salt, false), null, 2));
113
+ await writeFile(join(tablePath, "schema.json"), JSON.stringify(UtilsServer.addIdToSchema(schema, 0, this.salt), null, 2));
114
114
  }
115
115
  /**
116
116
  * Update table schema or config
@@ -178,7 +178,7 @@ export default class Inibase {
178
178
  // update columns files names based on field id
179
179
  schema = UtilsServer.addIdToSchema(schema, table.schema?.length
180
180
  ? UtilsServer.findLastIdNumber(table.schema, this.salt)
181
- : 0, this.salt, false);
181
+ : 0, this.salt);
182
182
  if (table.schema?.length) {
183
183
  const replaceOldPathes = Utils.findChangedProperties(this._schemaToIdsPath(tableName, table.schema), this._schemaToIdsPath(tableName, schema));
184
184
  if (replaceOldPathes)
@@ -189,7 +189,7 @@ export default class Inibase {
189
189
  }
190
190
  }
191
191
  else
192
- schema = UtilsServer.addIdToSchema(schema, 0, this.salt, false);
192
+ schema = UtilsServer.addIdToSchema(schema, 0, this.salt);
193
193
  await writeFile(join(tablePath, "schema.json"), JSON.stringify(schema, null, 2));
194
194
  }
195
195
  delete this.tables[tableName];
@@ -277,22 +277,7 @@ export default class Inibase {
277
277
  throw this.throwError("INVALID_TYPE", [
278
278
  field.key,
279
279
  Array.isArray(field.type) ? field.type.join(", ") : field.type,
280
- Utils.detectFieldType(data[field.key], [
281
- "string",
282
- "number",
283
- "boolean",
284
- "date",
285
- "email",
286
- "url",
287
- "table",
288
- "object",
289
- "array",
290
- "password",
291
- "html",
292
- "ip",
293
- "json",
294
- "id",
295
- ]) ?? "undefinedType",
280
+ data[field.key],
296
281
  ]);
297
282
  if ((field.type === "array" || field.type === "object") &&
298
283
  field.children &&
@@ -306,46 +291,26 @@ export default class Inibase {
306
291
  }
307
292
  }
308
293
  }
309
- formatField(value, field, formatOnlyAvailiableKeys) {
310
- if (Array.isArray(field.type))
311
- field.type = (Utils.detectFieldType(value, field.type) ??
312
- field.type[0]);
313
- switch (field.type) {
294
+ formatField(value, fieldType, fieldChildrenType, _formatOnlyAvailiableKeys) {
295
+ if (Array.isArray(fieldType))
296
+ fieldType = (Utils.detectFieldType(value, fieldType) ??
297
+ fieldType[0]);
298
+ if (!value)
299
+ return null;
300
+ switch (fieldType) {
314
301
  case "array":
315
- if (typeof field.children === "string") {
316
- if (field.children === "table") {
317
- if (Array.isArray(value)) {
318
- if (Utils.isArrayOfObjects(value)) {
319
- if (value.every((item) => Object.hasOwn(item, "id") &&
320
- (Utils.isValidID(item.id) || Utils.isNumber(item.id))))
321
- value.map((item) => item.id
322
- ? Utils.isNumber(item.id)
323
- ? Number(item.id)
324
- : UtilsServer.decodeID(item.id, this.salt)
325
- : null);
326
- }
327
- else if (value.every(Utils.isValidID) ||
328
- value.every(Utils.isNumber))
329
- return value.map((item) => Utils.isNumber(item)
330
- ? Number(item)
331
- : UtilsServer.decodeID(item, this.salt));
332
- }
333
- else if (Utils.isValidID(value))
334
- return [UtilsServer.decodeID(value, this.salt)];
335
- else if (Utils.isNumber(value))
336
- return [Number(value)];
337
- }
338
- else
339
- return Array.isArray(value) ? value : [value];
340
- }
341
- else if (Utils.isArrayOfObjects(field.children))
342
- return this.formatData(value, field.children, formatOnlyAvailiableKeys);
343
- else if (Array.isArray(field.children))
344
- return Array.isArray(value) ? value : [value];
345
- break;
302
+ if (!fieldChildrenType)
303
+ return null;
304
+ if (!Array.isArray(value))
305
+ value = [value];
306
+ if (Utils.isArrayOfObjects(fieldChildrenType))
307
+ return this.formatData(value, fieldChildrenType, _formatOnlyAvailiableKeys);
308
+ return value.map((_value) => this.formatField(_value, fieldChildrenType));
346
309
  case "object":
347
- if (Utils.isArrayOfObjects(field.children))
348
- return this.formatData(value, field.children, formatOnlyAvailiableKeys);
310
+ if (Array.isArray(value))
311
+ value = value[0];
312
+ if (Utils.isArrayOfObjects(fieldChildrenType))
313
+ return this.formatData(value, fieldChildrenType, _formatOnlyAvailiableKeys);
349
314
  break;
350
315
  case "table":
351
316
  if (Array.isArray(value))
@@ -373,6 +338,14 @@ export default class Inibase {
373
338
  if (Array.isArray(value))
374
339
  value = value[0];
375
340
  return Utils.isNumber(value) ? Number(value) : null;
341
+ case "date": {
342
+ if (Array.isArray(value))
343
+ value = value[0];
344
+ if (Utils.isNumber(value))
345
+ return value;
346
+ const dateToTimestamp = Date.parse(value);
347
+ return Number.isNaN(dateToTimestamp) ? null : dateToTimestamp;
348
+ }
376
349
  case "id":
377
350
  if (Array.isArray(value))
378
351
  value = value[0];
@@ -410,12 +383,12 @@ export default class Inibase {
410
383
  const RETURN = {};
411
384
  for (const field of schema) {
412
385
  if (!Object.hasOwn(data, field.key)) {
413
- if (formatOnlyAvailiableKeys || !field.required)
386
+ if (formatOnlyAvailiableKeys)
414
387
  continue;
415
388
  RETURN[field.key] = this.getDefaultValue(field);
416
389
  continue;
417
390
  }
418
- RETURN[field.key] = this.formatField(data[field.key], field, formatOnlyAvailiableKeys);
391
+ RETURN[field.key] = this.formatField(data[field.key], field.type, field.children, formatOnlyAvailiableKeys);
419
392
  }
420
393
  return RETURN;
421
394
  }
@@ -764,7 +737,7 @@ export default class Inibase {
764
737
  searchOperator = "=";
765
738
  searchComparedAtValue = value;
766
739
  }
767
- const [searchResult, totalLines, linesNumbers] = await File.search(join(tablePath, `${key}${this.getFileExtension(tableName)}`), searchOperator ?? "=", searchComparedAtValue ?? null, searchLogicalOperator, field?.type, field?.children, options.perPage, options.page - 1 * options.perPage + 1, true, this.salt);
740
+ const [searchResult, totalLines, linesNumbers] = await File.search(join(tablePath, `${key}${this.getFileExtension(tableName)}`), searchOperator ?? "=", searchComparedAtValue ?? null, searchLogicalOperator, field?.type, field?.children, options.perPage, (options.page - 1) * options.perPage + 1, true, this.salt);
768
741
  if (searchResult) {
769
742
  RETURN = Utils.deepMerge(RETURN, Object.fromEntries(Object.entries(searchResult).map(([id, value]) => [
770
743
  id,
@@ -813,7 +786,10 @@ export default class Inibase {
813
786
  * @param {string} tableName
814
787
  */
815
788
  async clearCache(tableName) {
816
- await Promise.all((await readdir(join(this.databasePath, tableName, ".cache"))).map((file) => unlink(join(this.databasePath, tableName, ".cache", file))));
789
+ const cacheFolderPath = join(this.databasePath, tableName, ".cache");
790
+ await Promise.all((await readdir(cacheFolderPath))
791
+ .filter((file) => file !== ".pagination")
792
+ .map((file) => unlink(join(cacheFolderPath, file))));
817
793
  }
818
794
  async get(tableName, where, options = {
819
795
  page: 1,
@@ -834,8 +810,11 @@ export default class Inibase {
834
810
  options.page = options.page || 1;
835
811
  options.perPage = options.perPage || 15;
836
812
  let RETURN;
837
- let schema = (await this.throwErrorIfTableEmpty(tableName))
838
- .schema;
813
+ let schema = (await this.getTable(tableName)).schema;
814
+ if (!schema)
815
+ throw this.throwError("NO_SCHEMA", tableName);
816
+ if (!(await File.isExists(join(tablePath, `id${this.getFileExtension(tableName)}`))))
817
+ return null;
839
818
  if (options.columns?.length)
840
819
  schema = this._filterSchemaByColumns(schema, options.columns);
841
820
  if (where &&
@@ -847,12 +826,12 @@ export default class Inibase {
847
826
  RETURN = Object.values(await this.getItemsFromSchema(tableName, schema, Array.from({ length: options.perPage }, (_, index) => (options.page - 1) * options.perPage +
848
827
  index +
849
828
  1), options));
850
- if (await File.isExists(join(tablePath, ".pagination")))
851
- this.totalItems[`${tableName}-*`] = Number((await readFile(join(tablePath, ".pagination"), "utf8")).split(",")[1]);
829
+ if (await File.isExists(join(tablePath, ".cache", ".pagination")))
830
+ this.totalItems[`${tableName}-*`] = Number((await readFile(join(tablePath, ".cache", ".pagination"), "utf8")).split(",")[1]);
852
831
  else {
853
832
  const lastId = Number(Object.keys((await File.get(join(tablePath, `id${this.getFileExtension(tableName)}`), -1, "number", undefined, this.salt, true))?.[0] ?? 0));
854
833
  this.totalItems[`${tableName}-*`] = await File.count(join(tablePath, `id${this.getFileExtension(tableName)}`));
855
- await writeFile(join(tablePath, ".pagination"), `${lastId},${this.totalItems[`${tableName}-*`]}`);
834
+ await writeFile(join(tablePath, ".cache", ".pagination"), `${lastId},${this.totalItems[`${tableName}-*`]}`);
856
835
  }
857
836
  }
858
837
  else if ((Array.isArray(where) && where.every(Utils.isNumber)) ||
@@ -908,8 +887,11 @@ export default class Inibase {
908
887
  if (RETURN && linesNumbers) {
909
888
  if (onlyLinesNumbers)
910
889
  return Object.keys(RETURN).map(Number);
911
- const alreadyExistsColumns = Object.keys(Object.values(RETURN)[0]);
912
- RETURN = Object.values(Utils.deepMerge(RETURN, await this.getItemsFromSchema(tableName, schema.filter(({ key }) => !alreadyExistsColumns.includes(key)), Object.keys(RETURN).map(Number), options)));
890
+ const alreadyExistsColumns = Object.keys(Object.values(RETURN)[0]), alreadyExistsColumnsIDs = Utils.flattenSchema(schema)
891
+ .filter(({ key }) => alreadyExistsColumns.includes(key))
892
+ .map(({ id }) => id);
893
+ RETURN = Object.values(Utils.deepMerge(RETURN, await this.getItemsFromSchema(tableName, Utils.filterSchema(schema, ({ id, type, children }) => !alreadyExistsColumnsIDs.includes(id) ||
894
+ Utils.isFieldType("table", type, children)), Object.keys(RETURN).map(Number), options)));
913
895
  if (this.tables[tableName].config.cache)
914
896
  await writeFile(cachedFilePath, Array.from(linesNumbers).join(","));
915
897
  }
@@ -947,8 +929,8 @@ export default class Inibase {
947
929
  try {
948
930
  await File.lock(join(tablePath, ".tmp"), keys);
949
931
  if (await File.isExists(join(tablePath, `id${this.getFileExtension(tableName)}`))) {
950
- if (await File.isExists(join(tablePath, ".pagination")))
951
- [lastId, this.totalItems[`${tableName}-*`]] = (await readFile(join(tablePath, ".pagination"), "utf8"))
932
+ if (await File.isExists(join(tablePath, ".cache", ".pagination")))
933
+ [lastId, this.totalItems[`${tableName}-*`]] = (await readFile(join(tablePath, ".cache", ".pagination"), "utf8"))
952
934
  .split(",")
953
935
  .map(Number);
954
936
  else {
@@ -988,7 +970,7 @@ export default class Inibase {
988
970
  this.totalItems[`${tableName}-*`] += Array.isArray(RETURN)
989
971
  ? RETURN.length
990
972
  : 1;
991
- await writeFile(join(tablePath, ".pagination"), `${lastId},${this.totalItems[`${tableName}-*`]}`);
973
+ await writeFile(join(tablePath, ".cache", ".pagination"), `${lastId},${this.totalItems[`${tableName}-*`]}`);
992
974
  if (returnPostedData)
993
975
  return this.get(tableName, this.tables[tableName].config.prepend
994
976
  ? Array.isArray(RETURN)
@@ -1028,8 +1010,8 @@ export default class Inibase {
1028
1010
  return this.put(tableName, data, data.id);
1029
1011
  }
1030
1012
  let totalItems;
1031
- if (await File.isExists(join(tablePath, ".pagination")))
1032
- totalItems = (await readFile(join(tablePath, ".pagination"), "utf8"))
1013
+ if (await File.isExists(join(tablePath, ".cache", ".pagination")))
1014
+ totalItems = (await readFile(join(tablePath, ".cache", ".pagination"), "utf8"))
1033
1015
  .split(",")
1034
1016
  .map(Number)[1];
1035
1017
  else
@@ -1078,7 +1060,7 @@ export default class Inibase {
1078
1060
  }), {}),
1079
1061
  ]));
1080
1062
  const keys = UtilsServer.hashString(Object.keys(pathesContents)
1081
- .map((path) => path.replaceAll(".inib", ""))
1063
+ .map((path) => path.replaceAll(this.getFileExtension(tableName), ""))
1082
1064
  .join("."));
1083
1065
  try {
1084
1066
  await File.lock(join(tablePath, ".tmp"), keys);
@@ -1105,6 +1087,13 @@ export default class Inibase {
1105
1087
  else
1106
1088
  throw this.throwError("INVALID_PARAMETERS");
1107
1089
  }
1090
+ /**
1091
+ * Delete item(s) in a table
1092
+ *
1093
+ * @param {string} tableName
1094
+ * @param {(number | string | (number | string)[] | Criteria)} [where]
1095
+ * @return {boolean | null} {(Promise<boolean | null>)}
1096
+ */
1108
1097
  async delete(tableName, where, _id) {
1109
1098
  const renameList = [];
1110
1099
  const tablePath = join(this.databasePath, tableName);
@@ -1113,15 +1102,15 @@ export default class Inibase {
1113
1102
  try {
1114
1103
  await File.lock(join(tablePath, ".tmp"));
1115
1104
  await Promise.all((await readdir(tablePath))
1116
- ?.filter((fileName) => fileName.endsWith(".inib"))
1105
+ ?.filter((fileName) => fileName.endsWith(this.getFileExtension(tableName)))
1117
1106
  .map(async (file) => unlink(join(tablePath, file))));
1118
1107
  if (this.tables[tableName].config.cache)
1119
1108
  await this.clearCache(tableName);
1109
+ return true;
1120
1110
  }
1121
1111
  finally {
1122
1112
  await File.unlock(join(tablePath, ".tmp"));
1123
1113
  }
1124
- return "*";
1125
1114
  }
1126
1115
  if ((Array.isArray(where) && where.every(Utils.isValidID)) ||
1127
1116
  Utils.isValidID(where)) {
@@ -1139,15 +1128,13 @@ export default class Inibase {
1139
1128
  await Promise.all(renameList.map(async ([tempPath, filePath]) => rename(tempPath, filePath)));
1140
1129
  if (this.tables[tableName].config.cache)
1141
1130
  await this.clearCache(tableName);
1142
- if (await File.isExists(join(tablePath, ".pagination"))) {
1143
- const [lastId, totalItems] = (await readFile(join(tablePath, ".pagination"), "utf8"))
1131
+ if (await File.isExists(join(tablePath, ".cache", ".pagination"))) {
1132
+ const [lastId, totalItems] = (await readFile(join(tablePath, ".cache", ".pagination"), "utf8"))
1144
1133
  .split(",")
1145
1134
  .map(Number);
1146
- await writeFile(join(tablePath, ".pagination"), `${lastId},${totalItems - (Array.isArray(where) ? where.length : 1)}`);
1135
+ await writeFile(join(tablePath, ".cache", ".pagination"), `${lastId},${totalItems - (Array.isArray(where) ? where.length : 1)}`);
1147
1136
  }
1148
- if (_id)
1149
- return Array.isArray(_id) && _id.length === 1 ? _id[0] : _id;
1150
- return where;
1137
+ return true;
1151
1138
  }
1152
1139
  finally {
1153
1140
  if (renameList.length)
@@ -1162,7 +1149,7 @@ export default class Inibase {
1162
1149
  }
1163
1150
  else
1164
1151
  throw this.throwError("INVALID_PARAMETERS");
1165
- return null;
1152
+ return false;
1166
1153
  }
1167
1154
  async sum(tableName, columns, where) {
1168
1155
  const RETURN = {};
package/dist/utils.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { FieldType, ComparisonOperator, Field, Schema } from "./index.js";
1
+ import type { ComparisonOperator, Field, FieldType, Schema } from "./index.js";
2
2
  /**
3
3
  * Type guard function to check if the input is an array of objects.
4
4
  *
@@ -156,6 +156,9 @@ export declare const findChangedProperties: (obj1: Record<string, string>, obj2:
156
156
  * @returns The detected field type as a string, or undefined if no matching type is found.
157
157
  */
158
158
  export declare const detectFieldType: (input: any, availableTypes: FieldType[]) => FieldType | undefined;
159
+ export declare const isFieldType: (compareAtType: string | string[], fieldType?: FieldType | FieldType[], fieldChildrenType?: FieldType | FieldType[] | Schema) => boolean;
160
+ export declare const flattenSchema: (schema: Schema, keepParents?: boolean) => Schema;
161
+ export declare const filterSchema: (schema: Schema, callback: (arg0: Field) => boolean) => Field[];
159
162
  /**
160
163
  * Validates if the given value matches the specified field type(s).
161
164
  *
@@ -165,7 +168,7 @@ export declare const detectFieldType: (input: any, availableTypes: FieldType[])
165
168
  * @returns A boolean indicating whether the value matches the specified field type(s).
166
169
  */
167
170
  export declare const validateFieldType: (value: any, fieldType: FieldType | FieldType[], fieldChildrenType?: FieldType | FieldType[]) => boolean;
168
- export declare function FormatObjectCriteriaValue(value: string, isParentArray?: boolean): [
171
+ export declare function FormatObjectCriteriaValue(value: string): [
169
172
  ComparisonOperator,
170
173
  string | number | boolean | null | (string | number | null)[]
171
174
  ];
package/dist/utils.js CHANGED
@@ -118,7 +118,7 @@ export const isHTML = (input) => /<\/?\s*[a-z-][^>]*\s*>|(\&(?:[\w\d]+|#\d+|#x[a
118
118
  * Note: Validates the input against being a number, boolean, email, URL, or IP address to ensure it's a general string.
119
119
  */
120
120
  export const isString = (input) => Object.prototype.toString.call(input) === "[object String]" &&
121
- !isNumber(input);
121
+ (!isNumber(input) || String(input).at(0) === "0");
122
122
  /**
123
123
  * Checks if the input is a valid IP address format.
124
124
  *
@@ -152,7 +152,9 @@ export const isPassword = (input) => typeof input === "string" && input.length =
152
152
  * @param input - The input to be checked, can be of any type.
153
153
  * @returns A boolean indicating whether the input is a valid date.
154
154
  */
155
- export const isDate = (input) => !Number.isNaN(new Date(input).getTime()) || !Number.isNaN(Date.parse(input));
155
+ export const isDate = (input) => !Number.isNaN(new Date(input).getTime()) ||
156
+ !Number.isNaN(Date.parse(input)) ||
157
+ !!input.match(/\b\d{2}[/.-]\d{2}[/.-]\d{4}\b/);
156
158
  /**
157
159
  * Checks if the input is a valid ID.
158
160
  *
@@ -206,6 +208,8 @@ export const detectFieldType = (input, availableTypes) => {
206
208
  return "date";
207
209
  if (availableTypes.includes("number"))
208
210
  return "number";
211
+ if (availableTypes.includes("string") && String(input).at(0) === "0")
212
+ return "string";
209
213
  }
210
214
  else if (typeof input === "string") {
211
215
  if (availableTypes.includes("table") && isValidID(input))
@@ -232,6 +236,59 @@ export const detectFieldType = (input, availableTypes) => {
232
236
  return "array";
233
237
  return undefined;
234
238
  };
239
+ export const isFieldType = (compareAtType, fieldType, fieldChildrenType) => {
240
+ if (fieldType) {
241
+ if (Array.isArray(fieldType)) {
242
+ if (fieldType.some((type) => Array.isArray(compareAtType)
243
+ ? compareAtType.includes(type)
244
+ : compareAtType === type))
245
+ return true;
246
+ }
247
+ else if ((Array.isArray(compareAtType) && compareAtType.includes(fieldType)) ||
248
+ compareAtType === fieldType)
249
+ return true;
250
+ }
251
+ if (fieldChildrenType) {
252
+ if (Array.isArray(fieldChildrenType)) {
253
+ if (!isArrayOfObjects(fieldChildrenType)) {
254
+ if (fieldChildrenType.some((type) => Array.isArray(compareAtType)
255
+ ? compareAtType.includes(type)
256
+ : compareAtType === type))
257
+ return true;
258
+ }
259
+ }
260
+ else if ((Array.isArray(compareAtType) &&
261
+ compareAtType.includes(fieldChildrenType)) ||
262
+ compareAtType === fieldChildrenType)
263
+ return true;
264
+ }
265
+ return false;
266
+ };
267
+ // Function to recursively flatten an array of objects and their nested children
268
+ export const flattenSchema = (schema, keepParents = false) => {
269
+ const result = [];
270
+ function _flattenHelper(item, parentKey) {
271
+ if (item.children && isArrayOfObjects(item.children)) {
272
+ if (keepParents)
273
+ result.push((({ children, ...rest }) => rest)(item));
274
+ for (const child of item.children)
275
+ _flattenHelper(child, item.key);
276
+ }
277
+ else
278
+ result.push({
279
+ ...item,
280
+ key: parentKey ? `${parentKey}.${item.key}` : item.key,
281
+ });
282
+ }
283
+ for (const item of schema)
284
+ _flattenHelper(item, "");
285
+ return result;
286
+ };
287
+ export const filterSchema = (schema, callback) => schema.filter((field) => {
288
+ if (field.children && isArrayOfObjects(field.children))
289
+ field.children = filterSchema(field.children, callback);
290
+ return callback(field);
291
+ });
235
292
  /**
236
293
  * Validates if the given value matches the specified field type(s).
237
294
  *
@@ -303,7 +360,7 @@ export const validateFieldType = (value, fieldType, fieldChildrenType) => {
303
360
  return false;
304
361
  }
305
362
  };
306
- export function FormatObjectCriteriaValue(value, isParentArray = false) {
363
+ export function FormatObjectCriteriaValue(value) {
307
364
  switch (value[0]) {
308
365
  case ">":
309
366
  case "<":
@@ -339,15 +396,10 @@ export function FormatObjectCriteriaValue(value, isParentArray = false) {
339
396
  value.slice(1),
340
397
  ];
341
398
  case "=":
342
- return isParentArray
343
- ? [
344
- value.slice(0, 1),
345
- value.slice(1),
346
- ]
347
- : [
348
- value.slice(0, 1),
349
- `${value.slice(1)},`,
350
- ];
399
+ return [
400
+ value.slice(0, 1),
401
+ value.slice(1),
402
+ ];
351
403
  case "*":
352
404
  return [
353
405
  value.slice(0, 1),
@@ -1,9 +1,9 @@
1
1
  /// <reference types="node" resolution-mode="require"/>
2
2
  /// <reference types="node" resolution-mode="require"/>
3
3
  /// <reference types="node" resolution-mode="require"/>
4
- import type { ComparisonOperator, FieldType, Schema } from "./index.js";
5
4
  import { exec as execAsync, execFile as execFileAsync } from "node:child_process";
6
5
  import { gunzip as gunzipAsync, gzip as gzipAsync } from "node:zlib";
6
+ import type { ComparisonOperator, Field, FieldType, Schema } from "./index.js";
7
7
  export declare const exec: typeof execAsync.__promisify__;
8
8
  export declare const execFile: typeof execFileAsync.__promisify__;
9
9
  export declare const gzip: typeof gzipAsync.__promisify__;
@@ -39,7 +39,7 @@ export declare const encodeID: (id: number | string, secretKeyOrSalt: string | n
39
39
  * @returns The decoded ID as a number.
40
40
  */
41
41
  export declare const decodeID: (input: string, secretKeyOrSalt: string | number | Buffer) => number;
42
- export declare const flattenSchema: (schema: Schema, secretKeyOrSalt: string | number | Buffer) => number[];
42
+ export declare const extractIdsFromSchema: (schema: Schema, secretKeyOrSalt: string | number | Buffer) => number[];
43
43
  /**
44
44
  * Finds the last ID number in a schema, potentially decoding it if encrypted.
45
45
  *
@@ -57,7 +57,7 @@ export declare const findLastIdNumber: (schema: Schema, secretKeyOrSalt: string
57
57
  * @param encodeIDs - If true, IDs will be encoded, else they will remain as numbers.
58
58
  * @returns The updated schema with encoded IDs.
59
59
  */
60
- export declare const addIdToSchema: (schema: Schema, oldIndex: number, secretKeyOrSalt: string | number | Buffer, encodeIDs?: boolean) => import("./index.js").Field[];
60
+ export declare const addIdToSchema: (schema: Schema, startWithID: number, secretKeyOrSalt: string | number | Buffer, encodeIDs?: boolean) => Field[];
61
61
  export declare const encodeSchemaID: (schema: Schema, secretKeyOrSalt: string | number | Buffer) => Schema;
62
62
  export declare const hashString: (str: string) => string;
63
63
  /**
@@ -1,8 +1,8 @@
1
- import { createCipheriv, createDecipheriv, randomBytes, scryptSync, createHash, } from "node:crypto";
2
- import { detectFieldType, isArrayOfObjects, isNumber, isPassword, isValidID, } from "./utils.js";
3
- import { promisify } from "node:util";
4
1
  import { exec as execAsync, execFile as execFileAsync, } from "node:child_process";
2
+ import { createCipheriv, createDecipheriv, createHash, randomBytes, scryptSync, } from "node:crypto";
3
+ import { promisify } from "node:util";
5
4
  import { gunzip as gunzipAsync, gzip as gzipAsync } from "node:zlib";
5
+ import { detectFieldType, isArrayOfObjects, isNumber, isPassword, isValidID, } from "./utils.js";
6
6
  export const exec = promisify(execAsync);
7
7
  export const execFile = promisify(execFileAsync);
8
8
  export const gzip = promisify(gzipAsync);
@@ -69,7 +69,7 @@ export const decodeID = (input, secretKeyOrSalt) => {
69
69
  return Number(decipher.update(input, "hex", "utf8") + decipher.final("utf8"));
70
70
  };
71
71
  // Function to recursively flatten an array of objects and their nested children
72
- export const flattenSchema = (schema, secretKeyOrSalt) => {
72
+ export const extractIdsFromSchema = (schema, secretKeyOrSalt) => {
73
73
  const result = [];
74
74
  for (const field of schema) {
75
75
  if (field.id)
@@ -77,7 +77,7 @@ export const flattenSchema = (schema, secretKeyOrSalt) => {
77
77
  ? field.id
78
78
  : decodeID(field.id, secretKeyOrSalt));
79
79
  if (field.children && isArrayOfObjects(field.children))
80
- result.push(...flattenSchema(field.children, secretKeyOrSalt));
80
+ result.push(...extractIdsFromSchema(field.children, secretKeyOrSalt));
81
81
  }
82
82
  return result;
83
83
  };
@@ -88,7 +88,7 @@ export const flattenSchema = (schema, secretKeyOrSalt) => {
88
88
  * @param secretKeyOrSalt - The secret key or salt for decoding an encrypted ID, can be a string, number, or Buffer.
89
89
  * @returns The last ID number in the schema, decoded if necessary.
90
90
  */
91
- export const findLastIdNumber = (schema, secretKeyOrSalt) => Math.max(...flattenSchema(schema, secretKeyOrSalt));
91
+ export const findLastIdNumber = (schema, secretKeyOrSalt) => Math.max(...extractIdsFromSchema(schema, secretKeyOrSalt));
92
92
  /**
93
93
  * Adds or updates IDs in a schema, encoding them using a provided secret key or salt.
94
94
  *
@@ -98,30 +98,30 @@ export const findLastIdNumber = (schema, secretKeyOrSalt) => Math.max(...flatten
98
98
  * @param encodeIDs - If true, IDs will be encoded, else they will remain as numbers.
99
99
  * @returns The updated schema with encoded IDs.
100
100
  */
101
- export const addIdToSchema = (schema, oldIndex, secretKeyOrSalt, encodeIDs) => schema.map((field) => {
102
- if (!field.id) {
103
- oldIndex++;
104
- field.id = encodeIDs ? encodeID(oldIndex, secretKeyOrSalt) : oldIndex;
105
- }
106
- else {
107
- if (isValidID(field.id)) {
108
- oldIndex = decodeID(field.id, secretKeyOrSalt);
109
- if (!encodeIDs)
110
- field.id = oldIndex;
101
+ export const addIdToSchema = (schema, startWithID, secretKeyOrSalt, encodeIDs) => {
102
+ function _addIdToField(field) {
103
+ if (!field.id) {
104
+ startWithID++;
105
+ field.id = encodeIDs
106
+ ? encodeID(startWithID, secretKeyOrSalt)
107
+ : startWithID;
111
108
  }
112
109
  else {
113
- oldIndex = field.id;
114
- if (encodeIDs)
110
+ if (isValidID(field.id)) {
111
+ if (!encodeIDs)
112
+ field.id = decodeID(field.id, secretKeyOrSalt);
113
+ }
114
+ else if (encodeIDs)
115
115
  field.id = encodeID(field.id, secretKeyOrSalt);
116
116
  }
117
+ if ((field.type === "array" || field.type === "object") &&
118
+ isArrayOfObjects(field.children))
119
+ field.children = _addIdToSchema(field.children);
120
+ return field;
117
121
  }
118
- if ((field.type === "array" || field.type === "object") &&
119
- isArrayOfObjects(field.children)) {
120
- field.children = addIdToSchema(field.children, oldIndex, secretKeyOrSalt, encodeIDs);
121
- oldIndex += field.children.length;
122
- }
123
- return field;
124
- });
122
+ const _addIdToSchema = (schema) => schema.map(_addIdToField);
123
+ return _addIdToSchema(schema);
124
+ };
125
125
  export const encodeSchemaID = (schema, secretKeyOrSalt) => schema.map((field) => ({
126
126
  ...field,
127
127
  id: isNumber(field.id) ? encodeID(field.id, secretKeyOrSalt) : field.id,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "inibase",
3
- "version": "1.0.0-rc.72",
3
+ "version": "1.0.0-rc.74",
4
4
  "type": "module",
5
5
  "author": {
6
6
  "name": "Karim Amahtil",
@@ -74,7 +74,7 @@
74
74
  },
75
75
  "dependencies": {
76
76
  "dotenv": "^16.4.5",
77
- "inison": "^1.0.0-rc.2"
77
+ "inison": "1.0.0-rc.3"
78
78
  },
79
79
  "scripts": {
80
80
  "build": "npx tsc",