inibase 1.0.0-rc.16 → 1.0.0-rc.18

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
@@ -1,4 +1,4 @@
1
- import { open, stat, writeFile } from "node:fs/promises";
1
+ import { open, rename, stat, writeFile } from "node:fs/promises";
2
2
  import { createInterface } from "node:readline";
3
3
  import { detectFieldType, isArrayOfArrays, isNumber } from "./utils.js";
4
4
  import { encodeID, comparePassword } from "./utils.server.js";
@@ -119,9 +119,9 @@ export const decode = (input, fieldType, fieldChildrenType, secretKey) => {
119
119
  };
120
120
  export const get = async (filePath, lineNumbers, fieldType, fieldChildrenType, secretKey) => {
121
121
  const fileHandle = await open(filePath, "r"), rl = doesSupportReadLines()
122
- ? fileHandle.readLines()
122
+ ? fileHandle.readLines({ autoClose: false })
123
123
  : createInterface({
124
- input: fileHandle.createReadStream(),
124
+ input: fileHandle.createReadStream({ autoClose: false }),
125
125
  crlfDelay: Infinity,
126
126
  });
127
127
  let lines = new Map(), lineCount = 0;
@@ -154,12 +154,12 @@ export const get = async (filePath, lineNumbers, fieldType, fieldChildrenType, s
154
154
  };
155
155
  export const replace = async (filePath, replacements) => {
156
156
  if (await isExists(filePath)) {
157
- const fileHandle = await open(filePath, "r+"), rl = doesSupportReadLines()
157
+ const fileHandle = await open(filePath, "r"), rl = doesSupportReadLines()
158
158
  ? fileHandle.readLines({ autoClose: false })
159
159
  : createInterface({
160
160
  input: fileHandle.createReadStream({ autoClose: false }),
161
161
  crlfDelay: Infinity,
162
- }), writeStream = fileHandle.createWriteStream();
162
+ }), fileTempPath = `${filePath.replace(".inib", "")}-${Date.now()}.tmp`, fileTempHandle = await open(fileTempPath, "w+"), writeStream = fileTempHandle.createWriteStream({ autoClose: false });
163
163
  if (typeof replacements === "object" && !Array.isArray(replacements)) {
164
164
  if (!(replacements instanceof Map))
165
165
  replacements = new Map(Object.entries(replacements));
@@ -181,7 +181,10 @@ export const replace = async (filePath, replacements) => {
181
181
  else
182
182
  for await (const _line of rl)
183
183
  writeStream.write(replacements + "\n");
184
- writeStream.end(async () => await fileHandle.close());
184
+ // writeStream.end(async () => await rename(fileTempPath, filePath));
185
+ await fileHandle.close();
186
+ await fileTempHandle.close();
187
+ await rename(fileTempPath, filePath);
185
188
  }
186
189
  else if (typeof replacements === "object" && !Array.isArray(replacements)) {
187
190
  if (!(replacements instanceof Map))
@@ -195,7 +198,7 @@ export const replace = async (filePath, replacements) => {
195
198
  };
196
199
  export const append = async (filePath, data, startsAt = 1) => {
197
200
  const doesFileExists = await isExists(filePath);
198
- const fileHandle = await open(filePath, "a"), writeStream = fileHandle.createWriteStream();
201
+ const fileHandle = await open(filePath, "a"), writeStream = fileHandle.createWriteStream({ autoClose: false });
199
202
  if (doesFileExists) {
200
203
  const currentNumberOfLines = await count(filePath);
201
204
  if (startsAt - currentNumberOfLines - 1 > 0)
@@ -221,25 +224,28 @@ export const append = async (filePath, data, startsAt = 1) => {
221
224
  };
222
225
  export const remove = async (filePath, linesToDelete) => {
223
226
  let lineCount = 0;
224
- const linesToDeleteArray = new Set(Array.isArray(linesToDelete) ? linesToDelete : [linesToDelete]), fileHandle = await open(filePath, "r+"), rl = doesSupportReadLines()
227
+ const linesToDeleteArray = new Set(Array.isArray(linesToDelete) ? linesToDelete : [linesToDelete]), fileHandle = await open(filePath, "r"), rl = doesSupportReadLines()
225
228
  ? fileHandle.readLines({ autoClose: false })
226
229
  : createInterface({
227
230
  input: fileHandle.createReadStream({ autoClose: false }),
228
231
  crlfDelay: Infinity,
229
- }), writeStream = fileHandle.createWriteStream();
232
+ }), fileTempPath = `${filePath.replace(".inib", "")}-${Date.now()}.tmp`, fileTempHandle = await open(fileTempPath, "w+"), writeStream = fileTempHandle.createWriteStream({ autoClose: false });
230
233
  for await (const line of rl) {
231
234
  lineCount++;
232
235
  if (!linesToDeleteArray.has(lineCount))
233
236
  writeStream.write(`${line}\n`);
234
237
  }
235
- writeStream.end(async () => await fileHandle.close()); // Rename the temp file to the original file name
238
+ // writeStream.end(async () => await rename(fileTempPath, filePath));
239
+ await fileHandle.close();
240
+ await fileTempHandle.close();
241
+ await rename(fileTempPath, filePath);
236
242
  };
237
243
  export const count = async (filePath) => {
238
244
  let lineCount = 0;
239
245
  const fileHandle = await open(filePath, "r"), rl = doesSupportReadLines()
240
- ? fileHandle.readLines()
246
+ ? fileHandle.readLines({ autoClose: false })
241
247
  : createInterface({
242
- input: fileHandle.createReadStream(),
248
+ input: fileHandle.createReadStream({ autoClose: false }),
243
249
  crlfDelay: Infinity,
244
250
  });
245
251
  for await (const line of rl)
@@ -301,9 +307,9 @@ const handleComparisonOperator = (operator, originalValue, comparedAtValue, fiel
301
307
  export const search = async (filePath, operator, comparedAtValue, logicalOperator, fieldType, fieldChildrenType, limit, offset, readWholeFile, secretKey) => {
302
308
  let RETURN = new Map(), lineCount = 0, foundItems = 0;
303
309
  const fileHandle = await open(filePath, "r"), rl = doesSupportReadLines()
304
- ? fileHandle.readLines()
310
+ ? fileHandle.readLines({ autoClose: false })
305
311
  : createInterface({
306
- input: fileHandle.createReadStream(),
312
+ input: fileHandle.createReadStream({ autoClose: false }),
307
313
  crlfDelay: Infinity,
308
314
  });
309
315
  for await (const line of rl) {
@@ -335,9 +341,9 @@ export const search = async (filePath, operator, comparedAtValue, logicalOperato
335
341
  };
336
342
  export const sum = async (filePath, lineNumbers) => {
337
343
  const fileHandle = await open(filePath, "r"), rl = doesSupportReadLines()
338
- ? fileHandle.readLines()
344
+ ? fileHandle.readLines({ autoClose: false })
339
345
  : createInterface({
340
- input: fileHandle.createReadStream(),
346
+ input: fileHandle.createReadStream({ autoClose: false }),
341
347
  crlfDelay: Infinity,
342
348
  });
343
349
  let sum = 0;
@@ -357,14 +363,13 @@ export const sum = async (filePath, lineNumbers) => {
357
363
  else
358
364
  for await (const line of rl)
359
365
  sum += +decode(line, "number");
360
- await fileHandle.close();
361
366
  return sum;
362
367
  };
363
368
  export const max = async (filePath, lineNumbers) => {
364
369
  const fileHandle = await open(filePath, "r"), rl = doesSupportReadLines()
365
- ? fileHandle.readLines()
370
+ ? fileHandle.readLines({ autoClose: false })
366
371
  : createInterface({
367
- input: fileHandle.createReadStream(),
372
+ input: fileHandle.createReadStream({ autoClose: false }),
368
373
  crlfDelay: Infinity,
369
374
  });
370
375
  let max = 0;
@@ -389,14 +394,13 @@ export const max = async (filePath, lineNumbers) => {
389
394
  if (lineContentNum > max)
390
395
  max = lineContentNum;
391
396
  }
392
- await fileHandle.close();
393
397
  return max;
394
398
  };
395
399
  export const min = async (filePath, lineNumbers) => {
396
400
  const fileHandle = await open(filePath, "r"), rl = doesSupportReadLines()
397
- ? fileHandle.readLines()
401
+ ? fileHandle.readLines({ autoClose: false })
398
402
  : createInterface({
399
- input: fileHandle.createReadStream(),
403
+ input: fileHandle.createReadStream({ autoClose: false }),
400
404
  crlfDelay: Infinity,
401
405
  });
402
406
  let min = 0;
@@ -421,21 +425,18 @@ export const min = async (filePath, lineNumbers) => {
421
425
  if (lineContentNum < min)
422
426
  min = lineContentNum;
423
427
  }
424
- await fileHandle.close();
425
428
  return min;
426
429
  };
427
430
  export const sort = async (filePath, sortDirection, lineNumbers, _lineNumbersPerChunk = 100000) => {
428
431
  const fileHandle = await open(filePath, "r"), rl = doesSupportReadLines()
429
- ? fileHandle.readLines()
432
+ ? fileHandle.readLines({ autoClose: false })
430
433
  : createInterface({
431
- input: fileHandle.createReadStream(),
434
+ input: fileHandle.createReadStream({ autoClose: false }),
432
435
  crlfDelay: Infinity,
433
436
  });
434
437
  let lineCount = 0;
435
- for await (const line of rl) {
438
+ for await (const line of rl)
436
439
  lineCount++;
437
- }
438
- await fileHandle.close();
439
440
  };
440
441
  export default class File {
441
442
  static get = get;
package/dist/index.d.ts CHANGED
@@ -2,8 +2,8 @@
2
2
  export interface Data {
3
3
  id?: number | string;
4
4
  [key: string]: any;
5
- createdAt?: Date;
6
- updatedAt?: Date;
5
+ createdAt?: number;
6
+ updatedAt?: number;
7
7
  }
8
8
  export type FieldType = "string" | "number" | "boolean" | "date" | "email" | "url" | "table" | "object" | "array" | "password" | "html" | "ip" | "id";
9
9
  type FieldDefault = {
@@ -75,8 +75,12 @@ export default class Inibase {
75
75
  private joinPathesContents;
76
76
  get(tableName: string, where?: string | number | (string | number)[] | Criteria | undefined, options?: Options | undefined, onlyOne?: true, onlyLinesNumbers?: undefined): Promise<Data | null>;
77
77
  get(tableName: string, where?: string | number | (string | number)[] | Criteria | undefined, options?: Options | undefined, onlyOne?: boolean | undefined, onlyLinesNumbers?: true): Promise<number[] | null>;
78
- post<DataType extends Data | Data[]>(tableName: string, data: DataType, options?: Options, returnPostedData?: boolean): Promise<DataType extends Data ? Data | null | void : Data[] | null | void>;
79
- put<returnPostedDataType extends boolean = true>(tableName: string, data: Data | Data[], where?: number | string | (number | string)[] | Criteria, options?: Options, returnPostedData?: returnPostedDataType): Promise<(returnPostedDataType extends true ? Data | Data[] : void) | null>;
78
+ post(tableName: string, data: Data | Data[], options: Options | undefined, returnPostedData?: false): Promise<void | null>;
79
+ post(tableName: string, data: Data, options: Options | undefined, returnPostedData: true): Promise<Data | null>;
80
+ post(tableName: string, data: Data[], options: Options | undefined, returnPostedData: true): Promise<Data[] | null>;
81
+ put(tableName: string, data: Data | Data[], where?: number | string | (number | string)[] | Criteria | undefined, options?: Options | undefined, returnPostedData?: false): Promise<void | null>;
82
+ put(tableName: string, data: Data, where: number | string | (number | string)[] | Criteria | undefined, options: Options | undefined, returnPostedData: true): Promise<Data | null>;
83
+ put(tableName: string, data: Data[], where: number | string | (number | string)[] | Criteria | undefined, options: Options | undefined, returnPostedData: true): Promise<Data[] | null>;
80
84
  delete(tableName: string, where?: number | string | (number | string)[] | Criteria, _id?: string | string[]): Promise<string | string[] | null>;
81
85
  sum(tableName: string, columns: string, where?: number | string | (number | string)[] | Criteria): Promise<number>;
82
86
  sum(tableName: string, columns: string[], where?: number | string | (number | string)[] | Criteria): Promise<Record<string, number>>;
package/dist/index.js CHANGED
@@ -505,7 +505,9 @@ export default class Inibase {
505
505
  if (!lineNumbers)
506
506
  throw this.throwError("INVALID_ID", where);
507
507
  if (onlyLinesNumbers)
508
- return Object.keys(lineNumbers).map(Number);
508
+ return Object.keys(lineNumbers).length
509
+ ? Object.keys(lineNumbers).map(Number)
510
+ : null;
509
511
  RETURN = Object.values((await getItemsFromSchema(join(this.folder, this.database, tableName), schema, Object.keys(lineNumbers).map(Number))) ?? {});
510
512
  if (RETURN.length && !Array.isArray(where))
511
513
  RETURN = RETURN[0];
@@ -695,13 +697,13 @@ export default class Inibase {
695
697
  RETURN = data.map(({ id, updatedAt, createdAt, ...rest }) => ({
696
698
  id: ++last_id,
697
699
  ...rest,
698
- createdAt: new Date(),
700
+ createdAt: new Date().getTime(),
699
701
  }));
700
702
  else
701
703
  RETURN = (({ id, updatedAt, createdAt, ...rest }) => ({
702
704
  id: ++last_id,
703
705
  ...rest,
704
- createdAt: new Date(),
706
+ createdAt: new Date().getTime(),
705
707
  }))(data);
706
708
  if (!RETURN)
707
709
  throw this.throwError("NO_DATA");
@@ -742,11 +744,11 @@ export default class Inibase {
742
744
  const pathesContents = this.joinPathesContents(join(this.folder, this.database, tableName), Utils.isArrayOfObjects(data)
743
745
  ? data.map((item) => ({
744
746
  ...(({ id, ...restOfData }) => restOfData)(item),
745
- updatedAt: new Date(),
747
+ updatedAt: new Date().getTime(),
746
748
  }))
747
749
  : {
748
750
  ...(({ id, ...restOfData }) => restOfData)(data),
749
- updatedAt: new Date(),
751
+ updatedAt: new Date().getTime(),
750
752
  });
751
753
  for await (const [path, content] of Object.entries(pathesContents))
752
754
  await File.replace(path, content);
@@ -769,9 +771,9 @@ export default class Inibase {
769
771
  const pathesContents = Object.fromEntries(Object.entries(this.joinPathesContents(join(this.folder, this.database, tableName), Utils.isArrayOfObjects(data)
770
772
  ? data.map((item) => ({
771
773
  ...item,
772
- updatedAt: new Date(),
774
+ updatedAt: new Date().getTime(),
773
775
  }))
774
- : { ...data, updatedAt: new Date() })).map(([path, content]) => [
776
+ : { ...data, updatedAt: new Date().getTime() })).map(([path, content]) => [
775
777
  path,
776
778
  [...(Array.isArray(where) ? where : [where])].reduce((obj, lineNum, index) => ({
777
779
  ...obj,
@@ -784,9 +786,9 @@ export default class Inibase {
784
786
  return this.get(tableName, where, options, !Array.isArray(where));
785
787
  }
786
788
  }
787
- else if (typeof where === "object") {
788
- const lineNumbers = this.get(tableName, where, undefined, undefined, true);
789
- if (!lineNumbers || !Array.isArray(lineNumbers) || !lineNumbers.length)
789
+ else if (Utils.isObject(where)) {
790
+ const lineNumbers = await this.get(tableName, where, undefined, undefined, true);
791
+ if (!lineNumbers || !lineNumbers.length)
790
792
  throw this.throwError("NO_ITEMS", tableName);
791
793
  return this.put(tableName, data, lineNumbers);
792
794
  }
@@ -823,16 +825,18 @@ export default class Inibase {
823
825
  const files = await readdir(join(this.folder, this.database, tableName));
824
826
  if (files.length) {
825
827
  if (!_id)
826
- _id = Object.values((await File.get(join(this.folder, this.database, tableName, "id.inib"), where, "number", undefined, this.salt))[0]).map((id) => UtilsServer.encodeID(Number(id), this.salt));
828
+ _id = Object.values((await File.get(join(this.folder, this.database, tableName, "id.inib"), where, "number", undefined, this.salt))[0] ?? {}).map((id) => UtilsServer.encodeID(Number(id), this.salt));
829
+ if (!_id.length)
830
+ throw this.throwError("NO_ITEMS", tableName);
827
831
  for (const file of files.filter((fileName) => fileName.endsWith(".inib")))
828
832
  await File.remove(join(this.folder, this.database, tableName, file), where);
829
833
  return Array.isArray(_id) && _id.length === 1 ? _id[0] : _id;
830
834
  }
831
835
  }
832
836
  }
833
- else if (typeof where === "object" && !Array.isArray(where)) {
834
- const lineNumbers = this.get(tableName, where, undefined, undefined, true);
835
- if (!lineNumbers || !Array.isArray(lineNumbers) || !lineNumbers.length)
837
+ else if (Utils.isObject(where)) {
838
+ const lineNumbers = await this.get(tableName, where, undefined, undefined, true);
839
+ if (!lineNumbers || !lineNumbers.length)
836
840
  throw this.throwError("NO_ITEMS", tableName);
837
841
  return this.delete(tableName, lineNumbers);
838
842
  }
package/dist/utils.js CHANGED
@@ -81,7 +81,8 @@ export const isBoolean = (input) => typeof input === "boolean" ||
81
81
  input === true ||
82
82
  input === false;
83
83
  export const isPassword = (input) => input.length === 161;
84
- export const isDate = (input) => !isNaN(Date.parse(String(input))) && Date.parse(String(input)) >= 0;
84
+ export const isDate = (input) => (isNumber(input) && new Date(input).getTime() > 0) ||
85
+ (!isNaN(Date.parse(String(input))) && Date.parse(String(input)) >= 0);
85
86
  export const isValidID = (input) => {
86
87
  return typeof input === "string" && input.length === 32;
87
88
  };
@@ -198,7 +199,7 @@ export const objectToDotNotation = (input) => {
198
199
  // If the property is an array of strings or numbers, keep the array as is
199
200
  result[newKey] = value;
200
201
  }
201
- else if (typeof value === "object" && value !== null) {
202
+ else if (isObject(value)) {
202
203
  // If the property is an object, push it onto the stack for further processing
203
204
  stack.push({ obj: value, parentKey: newKey });
204
205
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "inibase",
3
- "version": "1.0.0-rc.16",
3
+ "version": "1.0.0-rc.18",
4
4
  "description": "File-based Relational Database for large data",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist",