inibase 1.0.0-rc.49 → 1.0.0-rc.50
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/README.md +6 -5
- package/dist/file.d.ts +6 -6
- package/dist/file.js +4 -8
- package/dist/index.d.ts +9 -25
- package/dist/index.js +77 -49
- package/dist/utils.server.d.ts +2 -0
- package/dist/utils.server.js +32 -15
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -284,8 +284,9 @@ const product_schema = [
|
|
|
284
284
|
type: "number",
|
|
285
285
|
},
|
|
286
286
|
{
|
|
287
|
-
key: "
|
|
287
|
+
key: "createdBy",
|
|
288
288
|
type: "table",
|
|
289
|
+
table: "user",
|
|
289
290
|
required: true,
|
|
290
291
|
},
|
|
291
292
|
];
|
|
@@ -294,12 +295,12 @@ const product_data = [
|
|
|
294
295
|
{
|
|
295
296
|
title: "Product 1",
|
|
296
297
|
price: 16,
|
|
297
|
-
|
|
298
|
+
createdBy: "1d88385d4b1581f8fb059334dec30f4c",
|
|
298
299
|
},
|
|
299
300
|
{
|
|
300
301
|
title: "Product 2",
|
|
301
302
|
price: 10,
|
|
302
|
-
|
|
303
|
+
createdBy: "5011c230aa44481bf7e8dcfe0710474f",
|
|
303
304
|
},
|
|
304
305
|
];
|
|
305
306
|
|
|
@@ -309,7 +310,7 @@ const product = await db.post("product", product_data);
|
|
|
309
310
|
// "id": "1d88385d4b1581f8fb059334dec30f4c",
|
|
310
311
|
// "title": "Product 1",
|
|
311
312
|
// "price": 16,
|
|
312
|
-
// "
|
|
313
|
+
// "createdBy": {
|
|
313
314
|
// "id": "1d88385d4b1581f8fb059334dec30f4c",
|
|
314
315
|
// "username": "user1",
|
|
315
316
|
// "email": "user1@example.com",
|
|
@@ -320,7 +321,7 @@ const product = await db.post("product", product_data);
|
|
|
320
321
|
// "id": "5011c230aa44481bf7e8dcfe0710474f",
|
|
321
322
|
// "title": "Product 2",
|
|
322
323
|
// "price": 10,
|
|
323
|
-
// "
|
|
324
|
+
// "createdBy": {
|
|
324
325
|
// "id": "5011c230aa44481bf7e8dcfe0710474f",
|
|
325
326
|
// "username": "user2",
|
|
326
327
|
// ...
|
package/dist/file.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/// <reference types="node" resolution-mode="require"/>
|
|
2
|
-
import { ComparisonOperator, FieldType } from "./index.js";
|
|
2
|
+
import { ComparisonOperator, FieldType, Schema } from "./index.js";
|
|
3
3
|
export declare const lock: (folderPath: string, prefix?: string) => Promise<void>;
|
|
4
4
|
export declare const unlock: (folderPath: string, prefix?: string) => Promise<void>;
|
|
5
5
|
export declare const write: (filePath: string, data: any, disableCompression?: boolean) => Promise<void>;
|
|
@@ -30,7 +30,7 @@ export declare const encode: (input: string | number | boolean | null | (string
|
|
|
30
30
|
* @param secretKey - Optional secret key for decoding, can be a string or Buffer.
|
|
31
31
|
* @returns Decoded value as a string, number, boolean, or array of these, or null if no fieldType or input is null/empty.
|
|
32
32
|
*/
|
|
33
|
-
export declare const decode: (input: string | null | number, fieldType?: FieldType | FieldType[], fieldChildrenType?: FieldType | FieldType[], secretKey?: string | Buffer) => string | number | boolean | null | (string | number | null | boolean)[];
|
|
33
|
+
export declare const decode: (input: string | null | number, fieldType?: FieldType | FieldType[], fieldChildrenType?: FieldType | FieldType[] | Schema, secretKey?: string | Buffer) => string | number | boolean | null | (string | number | null | boolean)[];
|
|
34
34
|
/**
|
|
35
35
|
* Asynchronously reads and decodes data from a file at specified line numbers.
|
|
36
36
|
* Decodes each line based on specified field types and an optional secret key.
|
|
@@ -44,7 +44,7 @@ export declare const decode: (input: string | null | number, fieldType?: FieldTy
|
|
|
44
44
|
* 1. Record of line numbers and their decoded content or null if no lines are read.
|
|
45
45
|
* 2. Total count of lines processed.
|
|
46
46
|
*/
|
|
47
|
-
export declare function get(filePath: string, lineNumbers?: number | number[], fieldType?: FieldType | FieldType[], fieldChildrenType?: FieldType | FieldType[], secretKey?: string | Buffer, readWholeFile?: false): Promise<Record<number, string | number | boolean | null | (string | number | boolean | (string | number | boolean)[] | null)[]> | null>;
|
|
47
|
+
export declare function get(filePath: string, lineNumbers?: number | number[], fieldType?: FieldType | FieldType[], fieldChildrenType?: FieldType | FieldType[] | Schema, secretKey?: string | Buffer, readWholeFile?: false): Promise<Record<number, string | number | boolean | null | (string | number | boolean | (string | number | boolean)[] | null)[]> | null>;
|
|
48
48
|
export declare function get(filePath: string, lineNumbers: undefined | number | number[], fieldType: undefined | FieldType | FieldType[], fieldChildrenType: undefined | FieldType | FieldType[], secretKey: undefined | string | Buffer, readWholeFile: true): Promise<[
|
|
49
49
|
Record<number, string | number | boolean | null | (string | number | boolean | (string | number | boolean)[] | null)[]> | null,
|
|
50
50
|
number
|
|
@@ -98,7 +98,7 @@ export declare const remove: (filePath: string, linesToDelete: number | number[]
|
|
|
98
98
|
*
|
|
99
99
|
* Note: Decodes each line for comparison and can handle complex queries with multiple conditions.
|
|
100
100
|
*/
|
|
101
|
-
export declare const search: (filePath: string, operator: ComparisonOperator | ComparisonOperator[], comparedAtValue: string | number | boolean | null | (string | number | boolean | null)[], logicalOperator?: "and" | "or", fieldType?: FieldType | FieldType[], fieldChildrenType?: FieldType | FieldType[], limit?: number, offset?: number, readWholeFile?: boolean, secretKey?: string | Buffer) => Promise<[
|
|
101
|
+
export declare const search: (filePath: string, operator: ComparisonOperator | ComparisonOperator[], comparedAtValue: string | number | boolean | null | (string | number | boolean | null)[], logicalOperator?: "and" | "or", fieldType?: FieldType | FieldType[], fieldChildrenType?: FieldType | FieldType[] | Schema, limit?: number, offset?: number, readWholeFile?: boolean, secretKey?: string | Buffer) => Promise<[
|
|
102
102
|
Record<number, string | number | boolean | null | (string | number | boolean | null)[]> | null,
|
|
103
103
|
number,
|
|
104
104
|
Set<number> | null
|
|
@@ -158,10 +158,10 @@ export declare const sort: (filePath: string, sortDirection: 1 | -1 | "asc" | "d
|
|
|
158
158
|
export default class File {
|
|
159
159
|
static get: typeof get;
|
|
160
160
|
static remove: (filePath: string, linesToDelete: number | number[]) => Promise<string[]>;
|
|
161
|
-
static search: (filePath: string, operator: ComparisonOperator | ComparisonOperator[], comparedAtValue: string | number | boolean | (string | number | boolean | null)[] | null, logicalOperator?: "and" | "or" | undefined, fieldType?: FieldType | FieldType[] | undefined, fieldChildrenType?: FieldType | FieldType[] | undefined, limit?: number | undefined, offset?: number | undefined, readWholeFile?: boolean | undefined, secretKey?: string | Buffer | undefined) => Promise<[Record<number, string | number | boolean | (string | number | boolean | null)[] | null> | null, number, Set<number> | null]>;
|
|
161
|
+
static search: (filePath: string, operator: ComparisonOperator | ComparisonOperator[], comparedAtValue: string | number | boolean | (string | number | boolean | null)[] | null, logicalOperator?: "and" | "or" | undefined, fieldType?: FieldType | FieldType[] | undefined, fieldChildrenType?: FieldType | FieldType[] | Schema | undefined, limit?: number | undefined, offset?: number | undefined, readWholeFile?: boolean | undefined, secretKey?: string | Buffer | undefined) => Promise<[Record<number, string | number | boolean | (string | number | boolean | null)[] | null> | null, number, Set<number> | null]>;
|
|
162
162
|
static replace: (filePath: string, replacements: string | number | boolean | (string | number | boolean | null)[] | Record<number, string | number | boolean | (string | number | boolean | null)[] | null> | null) => Promise<string[]>;
|
|
163
163
|
static encode: (input: string | number | boolean | (string | number | boolean | null)[] | null) => string | number | boolean | null;
|
|
164
|
-
static decode: (input: string | number | null, fieldType?: FieldType | FieldType[] | undefined, fieldChildrenType?: FieldType | FieldType[] | undefined, secretKey?: string | Buffer | undefined) => string | number | boolean | (string | number | boolean | null)[] | null;
|
|
164
|
+
static decode: (input: string | number | null, fieldType?: FieldType | FieldType[] | undefined, fieldChildrenType?: FieldType | FieldType[] | Schema | undefined, secretKey?: string | Buffer | undefined) => string | number | boolean | (string | number | boolean | null)[] | null;
|
|
165
165
|
static isExists: (path: string) => Promise<boolean>;
|
|
166
166
|
static sum: (filePath: string, lineNumbers?: number | number[] | undefined) => Promise<number>;
|
|
167
167
|
static min: (filePath: string, lineNumbers?: number | number[] | undefined) => Promise<number>;
|
package/dist/file.js
CHANGED
|
@@ -5,7 +5,7 @@ import { pipeline } from "node:stream/promises";
|
|
|
5
5
|
import { createGzip, createGunzip, gunzipSync, gzipSync } from "node:zlib";
|
|
6
6
|
import { join } from "node:path";
|
|
7
7
|
import { Worker } from "node:worker_threads";
|
|
8
|
-
import { detectFieldType, isJSON, isNumber, isObject } from "./utils.js";
|
|
8
|
+
import { detectFieldType, isArrayOfObjects, isJSON, isNumber, isObject, } from "./utils.js";
|
|
9
9
|
import { encodeID, compare } from "./utils.server.js";
|
|
10
10
|
import Config from "./config.js";
|
|
11
11
|
import Inison from "inison";
|
|
@@ -122,7 +122,7 @@ const unSecureString = (input) => {
|
|
|
122
122
|
if (isNumber(input))
|
|
123
123
|
return Number(input);
|
|
124
124
|
if (typeof input === "string")
|
|
125
|
-
return input.replace(
|
|
125
|
+
return input.replace(/\\n/g, "\n") || null;
|
|
126
126
|
return null;
|
|
127
127
|
};
|
|
128
128
|
/**
|
|
@@ -146,7 +146,7 @@ const decodeHelper = (value, fieldType, fieldChildrenType, secretKey) => {
|
|
|
146
146
|
case "array":
|
|
147
147
|
if (!Array.isArray(value))
|
|
148
148
|
return [value];
|
|
149
|
-
if (fieldChildrenType)
|
|
149
|
+
if (fieldChildrenType && !isArrayOfObjects(fieldChildrenType))
|
|
150
150
|
return fieldChildrenType
|
|
151
151
|
? value.map((v) => decode(v, Array.isArray(fieldChildrenType)
|
|
152
152
|
? detectFieldType(v, fieldChildrenType)
|
|
@@ -423,11 +423,7 @@ export const search = async (filePath, operator, comparedAtValue, logicalOperato
|
|
|
423
423
|
}
|
|
424
424
|
// Convert the Map to an object using Object.fromEntries and return the result.
|
|
425
425
|
return foundItems
|
|
426
|
-
? [
|
|
427
|
-
matchingLines,
|
|
428
|
-
readWholeFile ? foundItems : foundItems - 1,
|
|
429
|
-
linesNumbers.size ? linesNumbers : null,
|
|
430
|
-
]
|
|
426
|
+
? [matchingLines, foundItems, linesNumbers.size ? linesNumbers : null]
|
|
431
427
|
: [null, 0, null];
|
|
432
428
|
}
|
|
433
429
|
finally {
|
package/dist/index.d.ts
CHANGED
|
@@ -6,32 +6,15 @@ export interface Data {
|
|
|
6
6
|
updatedAt?: number;
|
|
7
7
|
}
|
|
8
8
|
export type FieldType = "string" | "number" | "boolean" | "date" | "email" | "url" | "table" | "object" | "array" | "password" | "html" | "ip" | "json" | "id";
|
|
9
|
-
type
|
|
9
|
+
export type Field = {
|
|
10
10
|
id?: string | number;
|
|
11
11
|
key: string;
|
|
12
|
+
type: FieldType | FieldType[];
|
|
12
13
|
required?: boolean;
|
|
14
|
+
table?: string;
|
|
15
|
+
unique?: boolean;
|
|
16
|
+
children?: FieldType | FieldType[] | Schema;
|
|
13
17
|
};
|
|
14
|
-
type FieldStringType = {
|
|
15
|
-
type: Exclude<FieldType, "array" | "object">;
|
|
16
|
-
children?: never;
|
|
17
|
-
};
|
|
18
|
-
type FieldStringArrayType = {
|
|
19
|
-
type: Array<Exclude<FieldType, "object">>;
|
|
20
|
-
children?: never;
|
|
21
|
-
};
|
|
22
|
-
type FieldArrayType = {
|
|
23
|
-
type: "array";
|
|
24
|
-
children: Exclude<FieldType, "array"> | Array<Exclude<FieldType, "array">> | Schema;
|
|
25
|
-
};
|
|
26
|
-
type FieldArrayArrayType = {
|
|
27
|
-
type: Array<"array" | Exclude<FieldType, "array" | "object">>;
|
|
28
|
-
children: Exclude<FieldType, "array" | "object"> | Array<Exclude<FieldType, "array" | "object">>;
|
|
29
|
-
};
|
|
30
|
-
type FieldObjectType = {
|
|
31
|
-
type: "object";
|
|
32
|
-
children: Schema;
|
|
33
|
-
};
|
|
34
|
-
export type Field = FieldDefault & (FieldStringType | FieldStringArrayType | FieldArrayArrayType | FieldObjectType | FieldArrayType);
|
|
35
18
|
export type Schema = Field[];
|
|
36
19
|
export interface Options {
|
|
37
20
|
page?: number;
|
|
@@ -57,26 +40,27 @@ declare global {
|
|
|
57
40
|
entries<T extends object>(o: T): Entries<T>;
|
|
58
41
|
}
|
|
59
42
|
}
|
|
60
|
-
export type ErrorCodes = "FIELD_REQUIRED" | "NO_SCHEMA" | "NO_ITEMS" | "NO_RESULTS" | "INVALID_ID" | "INVALID_TYPE" | "INVALID_PARAMETERS";
|
|
43
|
+
export type ErrorCodes = "FIELD_UNIQUE" | "FIELD_REQUIRED" | "NO_SCHEMA" | "NO_ITEMS" | "NO_RESULTS" | "INVALID_ID" | "INVALID_TYPE" | "INVALID_PARAMETERS";
|
|
61
44
|
export type ErrorLang = "en";
|
|
62
45
|
export default class Inibase {
|
|
63
46
|
folder: string;
|
|
64
47
|
database: string;
|
|
65
48
|
table: string | null;
|
|
66
49
|
pageInfo: Record<string, pageInfo>;
|
|
50
|
+
private checkIFunique;
|
|
67
51
|
private isThreadEnabled;
|
|
68
52
|
private totalItems;
|
|
69
53
|
salt: Buffer;
|
|
70
54
|
constructor(database: string, mainFolder?: string, _table?: string | null, _totalItems?: Record<string, number>, _pageInfo?: Record<string, pageInfo>, _isThreadEnabled?: boolean);
|
|
71
55
|
private throwError;
|
|
72
56
|
createWorker(functionName: "get" | "post" | "put" | "delete" | "sum" | "min" | "max" | "sort", arg: any[]): Promise<any>;
|
|
73
|
-
private _decodeIdFromSchema;
|
|
74
57
|
private _schemaToIdsPath;
|
|
75
58
|
setTableSchema(tableName: string, schema: Schema): Promise<void>;
|
|
76
|
-
getTableSchema(tableName: string): Promise<Schema | undefined>;
|
|
59
|
+
getTableSchema(tableName: string, encodeIDs?: boolean): Promise<Schema | undefined>;
|
|
77
60
|
getSchemaWhenTableNotEmpty(tableName: string, schema?: Schema): Promise<never | Schema>;
|
|
78
61
|
private validateData;
|
|
79
62
|
private formatField;
|
|
63
|
+
private checkUnique;
|
|
80
64
|
private formatData;
|
|
81
65
|
private getDefaultValue;
|
|
82
66
|
private _combineObjectsToArray;
|
package/dist/index.js
CHANGED
|
@@ -14,6 +14,7 @@ export default class Inibase {
|
|
|
14
14
|
database;
|
|
15
15
|
table;
|
|
16
16
|
pageInfo;
|
|
17
|
+
checkIFunique;
|
|
17
18
|
isThreadEnabled = false;
|
|
18
19
|
totalItems;
|
|
19
20
|
salt;
|
|
@@ -24,6 +25,7 @@ export default class Inibase {
|
|
|
24
25
|
this.totalItems = _totalItems;
|
|
25
26
|
this.pageInfo = _pageInfo;
|
|
26
27
|
this.isThreadEnabled = _isThreadEnabled;
|
|
28
|
+
this.checkIFunique = {};
|
|
27
29
|
if (!existsSync(".env") || !process.env.INIBASE_SECRET) {
|
|
28
30
|
this.salt = scryptSync(randomBytes(16), randomBytes(16), 32);
|
|
29
31
|
appendFileSync(".env", `\nINIBASE_SECRET=${this.salt.toString("hex")}\n`);
|
|
@@ -34,6 +36,7 @@ export default class Inibase {
|
|
|
34
36
|
throwError(code, variable, language = "en") {
|
|
35
37
|
const errorMessages = {
|
|
36
38
|
en: {
|
|
39
|
+
FIELD_UNIQUE: "Field {variable} should be unique, got {variable} instead",
|
|
37
40
|
FIELD_REQUIRED: "Field {variable} is required",
|
|
38
41
|
NO_SCHEMA: "Table {variable} does't have a schema",
|
|
39
42
|
NO_ITEMS: "Table {variable} is empty",
|
|
@@ -73,15 +76,6 @@ export default class Inibase {
|
|
|
73
76
|
worker.on("error", reject);
|
|
74
77
|
});
|
|
75
78
|
}
|
|
76
|
-
_decodeIdFromSchema = (schema) => schema.map((field) => {
|
|
77
|
-
if ((field.type === "array" || field.type === "object") &&
|
|
78
|
-
field.children &&
|
|
79
|
-
Utils.isArrayOfObjects(field.children))
|
|
80
|
-
field.children = this._decodeIdFromSchema(field.children);
|
|
81
|
-
if (field.id && !Utils.isNumber(field.id))
|
|
82
|
-
field.id = UtilsServer.decodeID(field.id, this.salt);
|
|
83
|
-
return field;
|
|
84
|
-
});
|
|
85
79
|
_schemaToIdsPath = (schema, prefix = "") => {
|
|
86
80
|
let RETURN = {};
|
|
87
81
|
for (const field of schema)
|
|
@@ -90,16 +84,14 @@ export default class Inibase {
|
|
|
90
84
|
Utils.isArrayOfObjects(field.children)) {
|
|
91
85
|
Utils.deepMerge(RETURN, this._schemaToIdsPath(field.children, (prefix ?? "") + field.key + "."));
|
|
92
86
|
}
|
|
93
|
-
else if (
|
|
94
|
-
RETURN[
|
|
95
|
-
(prefix ?? "") + field.key + ".inib";
|
|
87
|
+
else if (field.id)
|
|
88
|
+
RETURN[field.id] = (prefix ?? "") + field.key + ".inib";
|
|
96
89
|
return RETURN;
|
|
97
90
|
};
|
|
98
91
|
async setTableSchema(tableName, schema) {
|
|
99
92
|
const tablePath = join(this.folder, this.database, tableName), tableSchemaPath = join(tablePath, "schema.json"), isTablePathExists = await File.isExists(tablePath);
|
|
100
93
|
// remove id from schema
|
|
101
94
|
schema = schema.filter(({ key }) => !["id", "createdAt", "updatedAt"].includes(key));
|
|
102
|
-
schema = UtilsServer.addIdToSchema(schema, UtilsServer.findLastIdNumber(schema, this.salt), this.salt, isTablePathExists);
|
|
103
95
|
if (!isTablePathExists)
|
|
104
96
|
await mkdir(tablePath, { recursive: true });
|
|
105
97
|
if (!(await File.isExists(join(tablePath, ".tmp"))))
|
|
@@ -108,16 +100,24 @@ export default class Inibase {
|
|
|
108
100
|
await mkdir(join(tablePath, ".cache"));
|
|
109
101
|
if (await File.isExists(tableSchemaPath)) {
|
|
110
102
|
// update columns files names based on field id
|
|
111
|
-
const
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
103
|
+
const currentSchema = await this.getTableSchema(tableName, false);
|
|
104
|
+
schema = UtilsServer.addIdToSchema(schema, currentSchema && currentSchema.length
|
|
105
|
+
? UtilsServer.findLastIdNumber(currentSchema, this.salt)
|
|
106
|
+
: undefined, this.salt, false);
|
|
107
|
+
if (currentSchema && currentSchema.length) {
|
|
108
|
+
const replaceOldPathes = Utils.findChangedProperties(this._schemaToIdsPath(currentSchema), this._schemaToIdsPath(schema));
|
|
109
|
+
if (replaceOldPathes)
|
|
110
|
+
await Promise.all(Object.entries(replaceOldPathes).map(async ([oldPath, newPath]) => {
|
|
111
|
+
if (await File.isExists(join(tablePath, oldPath)))
|
|
112
|
+
await rename(join(tablePath, oldPath), join(tablePath, newPath));
|
|
113
|
+
}));
|
|
114
|
+
}
|
|
117
115
|
}
|
|
118
|
-
|
|
116
|
+
else
|
|
117
|
+
schema = UtilsServer.addIdToSchema(schema, undefined, this.salt, false);
|
|
118
|
+
await File.write(join(tablePath, "schema.json"), JSON.stringify(schema, null, 2), true);
|
|
119
119
|
}
|
|
120
|
-
async getTableSchema(tableName) {
|
|
120
|
+
async getTableSchema(tableName, encodeIDs = true) {
|
|
121
121
|
const tableSchemaPath = join(this.folder, this.database, tableName, "schema.json");
|
|
122
122
|
if (!(await File.isExists(tableSchemaPath)))
|
|
123
123
|
return undefined;
|
|
@@ -125,27 +125,30 @@ export default class Inibase {
|
|
|
125
125
|
if (!schemaFile)
|
|
126
126
|
return undefined;
|
|
127
127
|
const schema = JSON.parse(schemaFile), lastIdNumber = UtilsServer.findLastIdNumber(schema, this.salt);
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
128
|
+
if (!encodeIDs)
|
|
129
|
+
return schema;
|
|
130
|
+
else
|
|
131
|
+
return [
|
|
132
|
+
{
|
|
133
|
+
id: UtilsServer.encodeID(0, this.salt),
|
|
134
|
+
key: "id",
|
|
135
|
+
type: "id",
|
|
136
|
+
required: true,
|
|
137
|
+
},
|
|
138
|
+
...UtilsServer.encodeSchemaID(schema, this.salt),
|
|
139
|
+
{
|
|
140
|
+
id: UtilsServer.encodeID(lastIdNumber + 1, this.salt),
|
|
141
|
+
key: "createdAt",
|
|
142
|
+
type: "date",
|
|
143
|
+
required: true,
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
id: UtilsServer.encodeID(lastIdNumber + 2, this.salt),
|
|
147
|
+
key: "updatedAt",
|
|
148
|
+
type: "date",
|
|
149
|
+
required: false,
|
|
150
|
+
},
|
|
151
|
+
];
|
|
149
152
|
}
|
|
150
153
|
async getSchemaWhenTableNotEmpty(tableName, schema) {
|
|
151
154
|
const tablePath = join(this.folder, this.database, tableName);
|
|
@@ -186,6 +189,11 @@ export default class Inibase {
|
|
|
186
189
|
field.children &&
|
|
187
190
|
Utils.isArrayOfObjects(field.children))
|
|
188
191
|
this.validateData(data[field.key], field.children, skipRequiredField);
|
|
192
|
+
else if (field.unique) {
|
|
193
|
+
if (!this.checkIFunique[field.key])
|
|
194
|
+
this.checkIFunique[field.key] = [];
|
|
195
|
+
this.checkIFunique[`${field.key}`].push(data[field.key]);
|
|
196
|
+
}
|
|
189
197
|
}
|
|
190
198
|
}
|
|
191
199
|
}
|
|
@@ -271,8 +279,22 @@ export default class Inibase {
|
|
|
271
279
|
}
|
|
272
280
|
return null;
|
|
273
281
|
}
|
|
282
|
+
async checkUnique(tableName, schema) {
|
|
283
|
+
const tablePath = join(this.folder, this.database, tableName);
|
|
284
|
+
for await (const [key, values] of Object.entries(this.checkIFunique)) {
|
|
285
|
+
const field = Utils.getField(key, schema);
|
|
286
|
+
if (!field)
|
|
287
|
+
continue;
|
|
288
|
+
const [searchResult, totalLines] = await File.search(join(tablePath, key + ".inib"), Array.isArray(values) ? "=" : "[]", values, undefined, field.type, field.children, 1, undefined, false, this.salt);
|
|
289
|
+
if (searchResult && totalLines > 0)
|
|
290
|
+
throw this.throwError("FIELD_UNIQUE", [
|
|
291
|
+
field.key,
|
|
292
|
+
Array.isArray(values) ? values.join(", ") : values,
|
|
293
|
+
]);
|
|
294
|
+
}
|
|
295
|
+
this.checkIFunique = {};
|
|
296
|
+
}
|
|
274
297
|
formatData(data, schema, formatOnlyAvailiableKeys) {
|
|
275
|
-
this.validateData(data, schema, formatOnlyAvailiableKeys);
|
|
276
298
|
if (Utils.isArrayOfObjects(data))
|
|
277
299
|
return data.map((single_data) => this.formatData(single_data, schema, formatOnlyAvailiableKeys));
|
|
278
300
|
else if (Utils.isObject(data)) {
|
|
@@ -311,6 +333,8 @@ export default class Inibase {
|
|
|
311
333
|
]
|
|
312
334
|
: null;
|
|
313
335
|
case "object":
|
|
336
|
+
if (!field.children || !Utils.isArrayOfObjects(field.children))
|
|
337
|
+
return null;
|
|
314
338
|
return Utils.combineObjects(field.children.map((f) => ({
|
|
315
339
|
[f.key]: this.getDefaultValue(f),
|
|
316
340
|
})));
|
|
@@ -464,7 +488,8 @@ export default class Inibase {
|
|
|
464
488
|
else if (field.children === "table" ||
|
|
465
489
|
(Array.isArray(field.type) && field.type.includes("table")) ||
|
|
466
490
|
(Array.isArray(field.children) && field.children.includes("table"))) {
|
|
467
|
-
if (
|
|
491
|
+
if (field.table &&
|
|
492
|
+
(await File.isExists(join(this.folder, this.database, field.table))) &&
|
|
468
493
|
(await File.isExists(join(tablePath, (prefix ?? "") + field.key + ".inib")))) {
|
|
469
494
|
if (options.columns)
|
|
470
495
|
options.columns = options.columns
|
|
@@ -476,13 +501,13 @@ export default class Inibase {
|
|
|
476
501
|
if (!RETURN[index])
|
|
477
502
|
RETURN[index] = {};
|
|
478
503
|
RETURN[index][field.key] = item
|
|
479
|
-
? await this.get(field.
|
|
504
|
+
? await this.get(field.table, item, options)
|
|
480
505
|
: this.getDefaultValue(field);
|
|
481
506
|
}));
|
|
482
507
|
}
|
|
483
508
|
}
|
|
484
509
|
else if (await File.isExists(join(tablePath, (prefix ?? "") + field.key + ".inib"))) {
|
|
485
|
-
const items = await File.get(join(tablePath, (prefix ?? "") + field.key + ".inib"), linesNumber, field.type, field
|
|
510
|
+
const items = await File.get(join(tablePath, (prefix ?? "") + field.key + ".inib"), linesNumber, field.type, field.children, this.salt);
|
|
486
511
|
if (items)
|
|
487
512
|
for (const [index, item] of Object.entries(items)) {
|
|
488
513
|
if (!RETURN[index])
|
|
@@ -510,8 +535,7 @@ export default class Inibase {
|
|
|
510
535
|
(await File.isExists(join(tablePath, (prefix ?? "") + field.key + ".inib")))) {
|
|
511
536
|
if (options.columns)
|
|
512
537
|
options.columns = options.columns
|
|
513
|
-
.filter((column) => column.includes(`${field.key}.`)
|
|
514
|
-
!column.includes(`${field.key}.`))
|
|
538
|
+
.filter((column) => column.includes(`${field.key}.`))
|
|
515
539
|
.map((column) => column.replace(`${field.key}.`, ""));
|
|
516
540
|
const items = await File.get(join(tablePath, (prefix ?? "") + field.key + ".inib"), linesNumber, "number", undefined, this.salt);
|
|
517
541
|
if (items)
|
|
@@ -525,7 +549,7 @@ export default class Inibase {
|
|
|
525
549
|
}
|
|
526
550
|
}
|
|
527
551
|
else if (await File.isExists(join(tablePath, (prefix ?? "") + field.key + ".inib"))) {
|
|
528
|
-
const items = await File.get(join(tablePath, (prefix ?? "") + field.key + ".inib"), linesNumber, field.type, field
|
|
552
|
+
const items = await File.get(join(tablePath, (prefix ?? "") + field.key + ".inib"), linesNumber, field.type, field.children, this.salt);
|
|
529
553
|
if (items)
|
|
530
554
|
for (const [index, item] of Object.entries(items)) {
|
|
531
555
|
if (!RETURN[index])
|
|
@@ -833,6 +857,8 @@ export default class Inibase {
|
|
|
833
857
|
...rest,
|
|
834
858
|
createdAt: Date.now(),
|
|
835
859
|
}))(data);
|
|
860
|
+
this.validateData(RETURN, schema);
|
|
861
|
+
await this.checkUnique(tableName, schema);
|
|
836
862
|
RETURN = this.formatData(RETURN, schema);
|
|
837
863
|
const pathesContents = this.joinPathesContents(tablePath, Config.isReverseEnabled
|
|
838
864
|
? Array.isArray(RETURN)
|
|
@@ -871,6 +897,8 @@ export default class Inibase {
|
|
|
871
897
|
}, returnUpdatedData) {
|
|
872
898
|
let renameList = [];
|
|
873
899
|
const tablePath = join(this.folder, this.database, tableName), schema = await this.getSchemaWhenTableNotEmpty(tableName);
|
|
900
|
+
this.validateData(data, schema, true);
|
|
901
|
+
await this.checkUnique(tableName, schema);
|
|
874
902
|
data = this.formatData(data, schema, true);
|
|
875
903
|
if (!where) {
|
|
876
904
|
if (Utils.isArrayOfObjects(data)) {
|
package/dist/utils.server.d.ts
CHANGED
|
@@ -52,6 +52,7 @@ export declare const findLastIdNumber: (schema: Schema, secretKeyOrSalt: string
|
|
|
52
52
|
* @returns The updated schema with encoded IDs.
|
|
53
53
|
*/
|
|
54
54
|
export declare const addIdToSchema: (schema: Schema, oldIndex: number | undefined, secretKeyOrSalt: string | number | Buffer, encodeIDs?: boolean) => import("./index.js").Field[];
|
|
55
|
+
export declare const encodeSchemaID: (schema: Schema, secretKeyOrSalt: string | number | Buffer) => Schema;
|
|
55
56
|
export declare const hashString: (str: string) => string;
|
|
56
57
|
/**
|
|
57
58
|
* Evaluates a comparison between two values based on a specified operator and field types.
|
|
@@ -104,4 +105,5 @@ export default class UtilsServer {
|
|
|
104
105
|
static isEqual: (originalValue: string | number | boolean | (string | number | boolean | null)[] | null, comparedAtValue: string | number | boolean | (string | number | boolean | null)[] | null, fieldType?: FieldType | FieldType[] | undefined) => boolean;
|
|
105
106
|
static isArrayEqual: (originalValue: string | number | boolean | (string | number | boolean | null)[] | null, comparedAtValue: string | number | boolean | (string | number | boolean | null)[] | null) => boolean;
|
|
106
107
|
static isWildcardMatch: (originalValue: string | number | boolean | (string | number | boolean | null)[] | null, comparedAtValue: string | number | boolean | (string | number | boolean | null)[] | null) => boolean;
|
|
108
|
+
static encodeSchemaID: (schema: Schema, secretKeyOrSalt: string | number | Buffer) => Schema;
|
|
107
109
|
}
|
package/dist/utils.server.js
CHANGED
|
@@ -64,6 +64,19 @@ export const decodeID = (input, secretKeyOrSalt) => {
|
|
|
64
64
|
}
|
|
65
65
|
return Number(decipher.update(input, "hex", "utf8") + decipher.final("utf8"));
|
|
66
66
|
};
|
|
67
|
+
// Function to recursively flatten an array of objects and their nested children
|
|
68
|
+
const _flattenSchema = (schema, secretKeyOrSalt) => {
|
|
69
|
+
const result = [];
|
|
70
|
+
for (const field of schema) {
|
|
71
|
+
if (field.id)
|
|
72
|
+
result.push(typeof field.id === "number"
|
|
73
|
+
? field.id
|
|
74
|
+
: decodeID(field.id, secretKeyOrSalt));
|
|
75
|
+
if (field.children && isArrayOfObjects(field.children))
|
|
76
|
+
result.push(..._flattenSchema(field.children, secretKeyOrSalt));
|
|
77
|
+
}
|
|
78
|
+
return result;
|
|
79
|
+
};
|
|
67
80
|
/**
|
|
68
81
|
* Finds the last ID number in a schema, potentially decoding it if encrypted.
|
|
69
82
|
*
|
|
@@ -71,19 +84,7 @@ export const decodeID = (input, secretKeyOrSalt) => {
|
|
|
71
84
|
* @param secretKeyOrSalt - The secret key or salt for decoding an encrypted ID, can be a string, number, or Buffer.
|
|
72
85
|
* @returns The last ID number in the schema, decoded if necessary.
|
|
73
86
|
*/
|
|
74
|
-
export const findLastIdNumber = (schema, secretKeyOrSalt) =>
|
|
75
|
-
const lastField = schema[schema.length - 1];
|
|
76
|
-
if (lastField) {
|
|
77
|
-
if ((lastField.type === "array" || lastField.type === "object") &&
|
|
78
|
-
isArrayOfObjects(lastField.children))
|
|
79
|
-
return findLastIdNumber(lastField.children, secretKeyOrSalt);
|
|
80
|
-
else if (lastField.id)
|
|
81
|
-
return isValidID(lastField.id)
|
|
82
|
-
? decodeID(lastField.id, secretKeyOrSalt)
|
|
83
|
-
: lastField.id;
|
|
84
|
-
}
|
|
85
|
-
return 0;
|
|
86
|
-
};
|
|
87
|
+
export const findLastIdNumber = (schema, secretKeyOrSalt) => Math.max(..._flattenSchema(schema, secretKeyOrSalt));
|
|
87
88
|
/**
|
|
88
89
|
* Adds or updates IDs in a schema, encoding them using a provided secret key or salt.
|
|
89
90
|
*
|
|
@@ -99,11 +100,15 @@ export const addIdToSchema = (schema, oldIndex = 0, secretKeyOrSalt, encodeIDs)
|
|
|
99
100
|
field.id = encodeIDs ? encodeID(oldIndex, secretKeyOrSalt) : oldIndex;
|
|
100
101
|
}
|
|
101
102
|
else {
|
|
102
|
-
if (
|
|
103
|
+
if (isValidID(field.id)) {
|
|
103
104
|
oldIndex = decodeID(field.id, secretKeyOrSalt);
|
|
105
|
+
if (!encodeIDs)
|
|
106
|
+
field.id = oldIndex;
|
|
107
|
+
}
|
|
104
108
|
else {
|
|
105
109
|
oldIndex = field.id;
|
|
106
|
-
|
|
110
|
+
if (encodeIDs)
|
|
111
|
+
field.id = encodeID(field.id, secretKeyOrSalt);
|
|
107
112
|
}
|
|
108
113
|
}
|
|
109
114
|
if ((field.type === "array" || field.type === "object") &&
|
|
@@ -113,6 +118,17 @@ export const addIdToSchema = (schema, oldIndex = 0, secretKeyOrSalt, encodeIDs)
|
|
|
113
118
|
}
|
|
114
119
|
return field;
|
|
115
120
|
});
|
|
121
|
+
export const encodeSchemaID = (schema, secretKeyOrSalt) => schema.map((field) => ({
|
|
122
|
+
...field,
|
|
123
|
+
id: isNumber(field.id) ? encodeID(field.id, secretKeyOrSalt) : field.id,
|
|
124
|
+
...(field.children
|
|
125
|
+
? isArrayOfObjects(field.children)
|
|
126
|
+
? {
|
|
127
|
+
children: encodeSchemaID(field.children, secretKeyOrSalt),
|
|
128
|
+
}
|
|
129
|
+
: { children: field.children }
|
|
130
|
+
: {}),
|
|
131
|
+
}));
|
|
116
132
|
export const hashString = (str) => createHash("sha256").update(str).digest("hex");
|
|
117
133
|
/**
|
|
118
134
|
* Evaluates a comparison between two values based on a specified operator and field types.
|
|
@@ -251,4 +267,5 @@ export default class UtilsServer {
|
|
|
251
267
|
static isEqual = isEqual;
|
|
252
268
|
static isArrayEqual = isArrayEqual;
|
|
253
269
|
static isWildcardMatch = isWildcardMatch;
|
|
270
|
+
static encodeSchemaID = encodeSchemaID;
|
|
254
271
|
}
|