inibase 1.0.0-rc.0 → 1.0.0-rc.100

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.
@@ -0,0 +1,248 @@
1
+ import { exec as execSync, execFile as execFileSync } from "node:child_process";
2
+ import { createCipheriv, createDecipheriv, createHash, randomBytes, scryptSync, } from "node:crypto";
3
+ import { gunzip as gunzipSync, gzip as gzipSync } from "node:zlib";
4
+ import { promisify } from "node:util";
5
+ import { detectFieldType, isArrayOfObjects, isNumber, isPassword, isValidID, } from "./utils.js";
6
+ export const exec = promisify(execSync);
7
+ export const execFile = promisify(execFileSync);
8
+ export const gzip = promisify(gzipSync);
9
+ export const gunzip = promisify(gunzipSync);
10
+ /**
11
+ * Generates a hashed password using SHA-256.
12
+ *
13
+ * @param password - The plain text password to hash.
14
+ * @returns A string containing the salt and the hashed password, separated by a colon.
15
+ */
16
+ export const hashPassword = (password) => {
17
+ const salt = randomBytes(16).toString("hex");
18
+ const hash = createHash("sha256")
19
+ .update(password + salt)
20
+ .digest("hex");
21
+ return `${salt}:${hash}`;
22
+ };
23
+ /**
24
+ * Compares a hashed password with an input password to verify a match.
25
+ *
26
+ * @param hashedPassword - The hashed password, containing both the salt and the hash, separated by a colon.
27
+ * @param inputPassword - The plain text input password to compare against the hashed password.
28
+ * @returns A boolean indicating whether the input password matches the hashed password.
29
+ */
30
+ export const comparePassword = (hash, password) => {
31
+ const [salt, originalHash] = hash.split(":");
32
+ const inputHash = createHash("sha256")
33
+ .update(password + salt)
34
+ .digest("hex");
35
+ return inputHash === originalHash;
36
+ };
37
+ // Cache for derived keys if using scrypt
38
+ const derivedKeyCache = new Map();
39
+ // Helper function to create cipher or decipher
40
+ const getKeyAndIv = (secretKeyOrSalt) => {
41
+ if (Buffer.isBuffer(secretKeyOrSalt)) {
42
+ return { key: secretKeyOrSalt, iv: secretKeyOrSalt.subarray(0, 16) };
43
+ }
44
+ const cacheKey = secretKeyOrSalt.toString();
45
+ let key = derivedKeyCache.get(cacheKey);
46
+ if (!key) {
47
+ key = scryptSync(cacheKey, `${INIBASE_SECRET}`, 32);
48
+ derivedKeyCache.set(cacheKey, key); // Cache the derived key
49
+ }
50
+ return { key, iv: key.subarray(0, 16) };
51
+ };
52
+ // Ensure the environment variable is read once
53
+ const INIBASE_SECRET = process.env.INIBASE_SECRET ?? "inibase";
54
+ // Optimized encodeID
55
+ export const encodeID = (id, secretKeyOrSalt) => {
56
+ const { key, iv } = getKeyAndIv(secretKeyOrSalt);
57
+ const cipher = createCipheriv("aes-256-cbc", key, iv);
58
+ return cipher.update(id.toString(), "utf8", "hex") + cipher.final("hex");
59
+ };
60
+ // Optimized decodeID
61
+ export const decodeID = (input, secretKeyOrSalt) => {
62
+ const { key, iv } = getKeyAndIv(secretKeyOrSalt);
63
+ const decipher = createDecipheriv("aes-256-cbc", key, iv);
64
+ return Number(decipher.update(input, "hex", "utf8") + decipher.final("utf8"));
65
+ };
66
+ // Function to recursively flatten an array of objects and their nested children
67
+ export const extractIdsFromSchema = (schema, secretKeyOrSalt) => {
68
+ const result = [];
69
+ for (const field of schema) {
70
+ if (field.id)
71
+ result.push(typeof field.id === "number"
72
+ ? field.id
73
+ : decodeID(field.id, secretKeyOrSalt));
74
+ if (field.children && isArrayOfObjects(field.children))
75
+ result.push(...extractIdsFromSchema(field.children, secretKeyOrSalt));
76
+ }
77
+ return result;
78
+ };
79
+ /**
80
+ * Finds the last ID number in a schema, potentially decoding it if encrypted.
81
+ *
82
+ * @param schema - The schema to search, defined as an array of schema objects.
83
+ * @param secretKeyOrSalt - The secret key or salt for decoding an encrypted ID, can be a string, number, or Buffer.
84
+ * @returns The last ID number in the schema, decoded if necessary.
85
+ */
86
+ export const findLastIdNumber = (schema, secretKeyOrSalt) => Math.max(...extractIdsFromSchema(schema, secretKeyOrSalt));
87
+ /**
88
+ * Adds or updates IDs in a schema, encoding them using a provided secret key or salt.
89
+ *
90
+ * @param schema - The schema to update, defined as an array of schema objects.
91
+ * @param oldIndex - The starting index for generating new IDs, defaults to 0.
92
+ * @param secretKeyOrSalt - The secret key or salt for encoding IDs, can be a string, number, or Buffer.
93
+ * @param encodeIDs - If true, IDs will be encoded, else they will remain as numbers.
94
+ * @returns The updated schema with encoded IDs.
95
+ */
96
+ export const addIdToSchema = (schema, startWithID, secretKeyOrSalt, encodeIDs) => {
97
+ function _addIdToField(field) {
98
+ if (!field.id) {
99
+ startWithID++;
100
+ field.id = encodeIDs
101
+ ? encodeID(startWithID, secretKeyOrSalt)
102
+ : startWithID;
103
+ }
104
+ else {
105
+ if (isValidID(field.id)) {
106
+ if (!encodeIDs)
107
+ field.id = decodeID(field.id, secretKeyOrSalt);
108
+ }
109
+ else if (encodeIDs)
110
+ field.id = encodeID(field.id, secretKeyOrSalt);
111
+ }
112
+ if ((field.type === "array" || field.type === "object") &&
113
+ isArrayOfObjects(field.children))
114
+ field.children = _addIdToSchema(field.children);
115
+ return field;
116
+ }
117
+ const _addIdToSchema = (schema) => schema.map(_addIdToField);
118
+ return _addIdToSchema(schema);
119
+ };
120
+ export const encodeSchemaID = (schema, secretKeyOrSalt) => schema.map((field) => ({
121
+ ...field,
122
+ id: isNumber(field.id) ? encodeID(field.id, secretKeyOrSalt) : field.id,
123
+ ...(field.children
124
+ ? isArrayOfObjects(field.children)
125
+ ? {
126
+ children: encodeSchemaID(field.children, secretKeyOrSalt),
127
+ }
128
+ : { children: field.children }
129
+ : {}),
130
+ }));
131
+ export const hashString = (str) => createHash("sha256").update(str).digest("hex");
132
+ /**
133
+ * Evaluates a comparison between two values based on a specified operator and field types.
134
+ *
135
+ * @param operator - The comparison operator (e.g., '=', '!=', '>', '<', '>=', '<=', '[]', '![]', '*', '!*').
136
+ * @param originalValue - The value to compare, can be a single value or an array of values.
137
+ * @param comparedValue - The value or values to compare against.
138
+ * @param fieldType - Optional type of the field to guide comparison (e.g., 'password', 'boolean').
139
+ * @param fieldChildrenType - Optional type for child elements in array inputs.
140
+ * @returns boolean - Result of the comparison operation.
141
+ *
142
+ * Note: Handles various data types and comparison logic, including special handling for passwords and regex patterns.
143
+ */
144
+ export const compare = (operator, originalValue, comparedValue, fieldType) => {
145
+ // Determine the field type if it's an array of potential types.
146
+ if (Array.isArray(fieldType))
147
+ fieldType = detectFieldType(String(originalValue), fieldType);
148
+ // Handle comparisons involving arrays.
149
+ if (Array.isArray(comparedValue) && !["[]", "![]"].includes(operator))
150
+ return comparedValue.some((value) => compare(operator, originalValue, value, fieldType));
151
+ // Switch statement for different comparison operators.
152
+ switch (operator) {
153
+ // Equal (Case Insensitive for strings, specific handling for passwords and booleans).
154
+ case "=":
155
+ return isEqual(originalValue, comparedValue, fieldType);
156
+ // Not Equal.
157
+ case "!=":
158
+ return !isEqual(originalValue, comparedValue, fieldType);
159
+ // Greater Than.
160
+ case ">":
161
+ return compareNonNullValues(originalValue, comparedValue, (a, b) => a > b);
162
+ // Less Than.
163
+ case "<":
164
+ return compareNonNullValues(originalValue, comparedValue, (a, b) => a < b);
165
+ // Greater Than or Equal.
166
+ case ">=":
167
+ return compareNonNullValues(originalValue, comparedValue, (a, b) => a >= b);
168
+ // Less Than or Equal.
169
+ case "<=":
170
+ return compareNonNullValues(originalValue, comparedValue, (a, b) => a <= b);
171
+ // Array Contains (equality check for arrays).
172
+ case "[]":
173
+ return isArrayEqual(originalValue, comparedValue);
174
+ // Array Does Not Contain.
175
+ case "![]":
176
+ return !isArrayEqual(originalValue, comparedValue);
177
+ // Wildcard Match (using regex pattern).
178
+ case "*":
179
+ return isWildcardMatch(originalValue, comparedValue);
180
+ // Not Wildcard Match.
181
+ case "!*":
182
+ return !isWildcardMatch(originalValue, comparedValue);
183
+ // Unsupported operator.
184
+ default:
185
+ throw new Error(`Unsupported operator: ${operator}`);
186
+ }
187
+ };
188
+ /**
189
+ * Helper function to handle non-null comparisons.
190
+ */
191
+ const compareNonNullValues = (originalValue, comparedValue, comparator) => {
192
+ return (originalValue !== null &&
193
+ comparedValue !== null &&
194
+ comparator(originalValue, comparedValue));
195
+ };
196
+ /**
197
+ * Helper function to check equality based on the field type.
198
+ *
199
+ * @param originalValue - The original value.
200
+ * @param comparedValue - The value to compare against.
201
+ * @param fieldType - Type of the field.
202
+ * @returns boolean - Result of the equality check.
203
+ */
204
+ export const isEqual = (originalValue, comparedValue, fieldType) => {
205
+ switch (fieldType) {
206
+ case "password":
207
+ return isPassword(originalValue) && typeof comparedValue === "string"
208
+ ? comparePassword(originalValue, comparedValue)
209
+ : false;
210
+ case "boolean":
211
+ return Number(originalValue) === Number(comparedValue);
212
+ default:
213
+ return originalValue == comparedValue;
214
+ }
215
+ };
216
+ /**
217
+ * Helper function to check array equality.
218
+ *
219
+ * @param originalValue - The original value.
220
+ * @param comparedValue - The value to compare against.
221
+ * @returns boolean - Result of the array equality check.
222
+ */
223
+ export const isArrayEqual = (originalValue, comparedValue) => {
224
+ if (Array.isArray(originalValue) && Array.isArray(comparedValue))
225
+ return originalValue.some((v) => comparedValue.includes(v));
226
+ if (Array.isArray(originalValue))
227
+ return originalValue.includes(comparedValue);
228
+ if (Array.isArray(comparedValue))
229
+ return comparedValue.includes(originalValue);
230
+ return originalValue == comparedValue;
231
+ };
232
+ /**
233
+ * Helper function to check wildcard pattern matching using regex.
234
+ *
235
+ * @param originalValue - The original value.
236
+ * @param comparedValue - The value with wildcard pattern.
237
+ * @returns boolean - Result of the wildcard pattern matching.
238
+ */
239
+ export const isWildcardMatch = (originalValue, comparedValue) => {
240
+ const comparedValueStr = String(comparedValue);
241
+ const originalValueStr = String(originalValue);
242
+ if (!comparedValueStr.includes("%") &&
243
+ (comparedValueStr === originalValueStr ||
244
+ comparedValueStr.toLowerCase() === originalValueStr.toLowerCase()))
245
+ return true;
246
+ const wildcardPattern = `^${(comparedValueStr.includes("%") ? comparedValueStr : `%${comparedValueStr}%`).replace(/%/g, ".*")}$`;
247
+ return new RegExp(wildcardPattern, "i").test(originalValueStr);
248
+ };
package/package.json CHANGED
@@ -1,37 +1,85 @@
1
1
  {
2
2
  "name": "inibase",
3
- "version": "1.0.0-rc.0",
4
- "description": "File-based Relational Database for large data",
5
- "main": "index.ts",
6
- "repository": {
7
- "type": "git",
8
- "url": "git+https://github.com/inicontent/inibase.git"
3
+ "version": "1.0.0-rc.100",
4
+ "type": "module",
5
+ "author": {
6
+ "name": "Karim Amahtil",
7
+ "email": "karim.amahtil@gmail.com"
9
8
  },
9
+ "repository": "inicontent/inibase",
10
+ "main": "./dist/index.js",
11
+ "exports": {
12
+ ".": "./dist/index.js",
13
+ "./file": "./dist/file.js",
14
+ "./utils": "./dist/utils.js",
15
+ "./utils.server": "./dist/utils.server.js"
16
+ },
17
+ "bugs": {
18
+ "url": "https://github.com/inicontent/inibase/issues"
19
+ },
20
+ "description": "A file-based & memory-efficient, serverless, ACID compliant, relational database management system",
21
+ "engines": {
22
+ "node": ">=16"
23
+ },
24
+ "files": [
25
+ "/dist"
26
+ ],
27
+ "funding": "https://github.com/sponsors/inicontent",
28
+ "homepage": "https://github.com/inicontent/inibase#readme",
10
29
  "keywords": [
11
- "db",
12
30
  "nosql",
13
- "mongoose",
14
- "mongodb",
31
+ "rdms",
15
32
  "database",
33
+ "db",
34
+ "mongoose",
16
35
  "relational",
17
36
  "local",
37
+ "file",
18
38
  "storage",
19
39
  "json",
20
- "sql",
21
40
  "sqlite",
41
+ "sql",
22
42
  "supabase",
23
- "firebase"
43
+ "better-sqlite",
44
+ "mongodb",
45
+ "firebase",
46
+ "postgresql",
47
+ "pocketbase"
24
48
  ],
25
- "author": "Inicontent",
26
49
  "license": "MIT",
27
- "bugs": {
28
- "url": "https://github.com/inicontent/inibase/issues"
50
+ "bin": {
51
+ "inibase": "./dist/cli.js"
52
+ },
53
+ "types": "./dist",
54
+ "typesVersions": {
55
+ "*": {
56
+ ".": [
57
+ "./dist/index.d.ts"
58
+ ],
59
+ "file": [
60
+ "./dist/file.d.ts"
61
+ ],
62
+ "utils": [
63
+ "./dist/utils.d.ts"
64
+ ],
65
+ "utils.server": [
66
+ "./dist/utils.server.d.ts"
67
+ ]
68
+ }
29
69
  },
30
- "homepage": "https://github.com/inicontent/inibase#readme",
31
70
  "devDependencies": {
32
- "@types/node": "^20.6.0"
71
+ "@types/bun": "^1.1.10",
72
+ "@types/node": "^22.7.4",
73
+ "tinybench": "^2.6.0",
74
+ "typescript": "^5.6.2"
75
+ },
76
+ "dependencies": {
77
+ "dotenv": "^16.4.5",
78
+ "inison": "1.0.0-rc.4"
33
79
  },
34
80
  "scripts": {
35
- "test": "echo \"Error: no test specified\" && exit 1"
81
+ "prepublish": "npx tsc",
82
+ "build": "npx tsc",
83
+ "benchmark": "./benchmark/run.js"
36
84
  }
37
85
  }
package/file.ts DELETED
@@ -1,322 +0,0 @@
1
- import { createWriteStream, unlinkSync, renameSync, existsSync } from "fs";
2
- import { open } from "fs/promises";
3
- import { parse } from "path";
4
- import { ComparisonOperator, FieldType } from ".";
5
- import Utils from "./utils";
6
-
7
- export const encodeFileName = (fileName: string, extension?: string) => {
8
- return (
9
- fileName.replaceAll("%", "%25").replaceAll("*", "%") +
10
- (extension ? `.${extension}` : "")
11
- );
12
- };
13
-
14
- export const decodeFileName = (fileName: string) => {
15
- return fileName.replaceAll("%", "*").replaceAll("*25", "%");
16
- };
17
-
18
- export const get = async (
19
- filePath: string,
20
- fieldType?: FieldType,
21
- lineNumbers?: number | number[]
22
- ) => {
23
- const file = await open(filePath);
24
- let lines: Record<
25
- number,
26
- | string
27
- | number
28
- | boolean
29
- | (string | number | boolean | (string | number | boolean)[] | null)[]
30
- | null
31
- > = {},
32
- lineCount = 0;
33
-
34
- if (!lineNumbers) {
35
- for await (const line of file.readLines())
36
- lineCount++, (lines[lineCount] = Utils.decode(line, fieldType));
37
- } else if (lineNumbers === -1) {
38
- let lastLine;
39
- for await (const line of file.readLines()) lineCount++, (lastLine = line);
40
- if (lastLine) lines = { [lineCount]: Utils.decode(lastLine, fieldType) };
41
- } else {
42
- let lineNumbersArray = [
43
- ...(Array.isArray(lineNumbers) ? lineNumbers : [lineNumbers]),
44
- ];
45
- for await (const line of file.readLines()) {
46
- lineCount++;
47
- if (!lineNumbersArray.includes(lineCount)) continue;
48
- const indexOfLineCount = lineNumbersArray.indexOf(lineCount);
49
- lines[lineCount] = Utils.decode(line, fieldType);
50
- lineNumbersArray[indexOfLineCount] = 0;
51
- if (!lineNumbersArray.filter((lineN) => lineN !== 0).length) break;
52
- }
53
- }
54
-
55
- return lines ?? null;
56
- };
57
-
58
- export const replace = async (
59
- filePath: string,
60
- replacements:
61
- | string
62
- | number
63
- | boolean
64
- | null
65
- | (string | number | boolean | null)[]
66
- | Record<
67
- number,
68
- string | boolean | number | null | (string | boolean | number | null)[]
69
- >
70
- ) => {
71
- if (existsSync(filePath)) {
72
- const file = await open(filePath, "w+"),
73
- writeStream = file.createWriteStream();
74
- if (typeof replacements === "object" && !Array.isArray(replacements)) {
75
- let lineCount = 0;
76
- for await (const line of file.readLines()) {
77
- lineCount++;
78
- writeStream.write(
79
- (lineCount in replacements
80
- ? Utils.encode(replacements[lineCount])
81
- : line) + "\n"
82
- );
83
- }
84
- } else
85
- for await (const _line of file.readLines())
86
- writeStream.write(Utils.encode(replacements) + "\n");
87
-
88
- writeStream.end();
89
- } else if (typeof replacements === "object" && !Array.isArray(replacements)) {
90
- const file = await open(filePath, "w"),
91
- writeStream = file.createWriteStream(),
92
- largestLinesNumbers =
93
- Math.max(...Object.keys(replacements).map(Number)) + 1;
94
- for (let lineCount = 1; lineCount < largestLinesNumbers; lineCount++) {
95
- writeStream.write(
96
- (lineCount in replacements
97
- ? Utils.encode(replacements[lineCount])
98
- : "") + "\n"
99
- );
100
- }
101
- writeStream.end();
102
- }
103
- };
104
-
105
- export const remove = async (
106
- filePath: string,
107
- linesToDelete: number | number[]
108
- ): Promise<void> => {
109
- let lineCount = 0;
110
-
111
- const tempFilePath = `${filePath}.tmp`,
112
- linesToDeleteArray = [
113
- ...(Array.isArray(linesToDelete) ? linesToDelete : [linesToDelete]),
114
- ],
115
- writeStream = createWriteStream(tempFilePath),
116
- file = await open(filePath);
117
-
118
- for await (const line of file.readLines()) {
119
- lineCount++;
120
- if (!linesToDeleteArray.includes(lineCount)) {
121
- writeStream.write(`${line}\n`);
122
- }
123
- }
124
- writeStream.end();
125
- writeStream.on("finish", () => {
126
- unlinkSync(filePath); // Remove the original file
127
- renameSync(tempFilePath, filePath); // Rename the temp file to the original file name
128
- });
129
- };
130
-
131
- export const count = async (filePath: string): Promise<number> => {
132
- let lineCount = 0;
133
-
134
- const file = await open(filePath);
135
-
136
- for await (const line of file.readLines()) lineCount++;
137
-
138
- return lineCount;
139
- };
140
-
141
- export const search = async (
142
- filePath: string,
143
- fieldType: FieldType,
144
- operator: ComparisonOperator | ComparisonOperator[],
145
- comparedAtValue:
146
- | string
147
- | number
148
- | boolean
149
- | null
150
- | (string | number | boolean | null)[],
151
- logicalOperator?: "and" | "or",
152
- limit?: number,
153
- offset?: number,
154
- readWholeFile?: boolean
155
- ): Promise<
156
- [
157
- Record<
158
- number,
159
- Record<
160
- string,
161
- string | number | boolean | (string | number | boolean | null)[] | null
162
- >
163
- > | null,
164
- number
165
- ]
166
- > => {
167
- const handleComparisonOperator = (
168
- operator: ComparisonOperator,
169
- value:
170
- | string
171
- | number
172
- | boolean
173
- | null
174
- | (string | number | boolean | null)[],
175
- comparedAtValue:
176
- | string
177
- | number
178
- | boolean
179
- | null
180
- | (string | number | boolean | null)[],
181
- fieldType: FieldType
182
- ): boolean => {
183
- // check if not array or object
184
- switch (operator) {
185
- case "=":
186
- return fieldType === "password" &&
187
- typeof value === "string" &&
188
- typeof comparedAtValue === "string"
189
- ? Utils.comparePassword(value, comparedAtValue)
190
- : value === comparedAtValue;
191
- case "!=":
192
- return !handleComparisonOperator(
193
- "=",
194
- value,
195
- comparedAtValue,
196
- fieldType
197
- );
198
- case ">":
199
- return (
200
- value !== null && comparedAtValue !== null && value > comparedAtValue
201
- );
202
- case "<":
203
- return (
204
- value !== null && comparedAtValue !== null && value < comparedAtValue
205
- );
206
- case ">=":
207
- return (
208
- value !== null && comparedAtValue !== null && value >= comparedAtValue
209
- );
210
- case "<=":
211
- return (
212
- value !== null && comparedAtValue !== null && value <= comparedAtValue
213
- );
214
- case "[]":
215
- return (
216
- (Array.isArray(value) &&
217
- Array.isArray(comparedAtValue) &&
218
- value.some(comparedAtValue.includes)) ||
219
- (Array.isArray(value) &&
220
- !Array.isArray(comparedAtValue) &&
221
- value.includes(comparedAtValue)) ||
222
- (!Array.isArray(value) &&
223
- Array.isArray(comparedAtValue) &&
224
- comparedAtValue.includes(value))
225
- );
226
- case "![]":
227
- return !handleComparisonOperator(
228
- "[]",
229
- value,
230
- comparedAtValue,
231
- fieldType
232
- );
233
- case "*":
234
- return (
235
- value !== null &&
236
- comparedAtValue !== null &&
237
- new RegExp(
238
- `^${comparedAtValue.toString().replace(/%/g, ".*")}$`,
239
- "i"
240
- ).test(value.toString())
241
- );
242
- case "!*":
243
- return !handleComparisonOperator(
244
- "*",
245
- value,
246
- comparedAtValue,
247
- fieldType
248
- );
249
- default:
250
- throw new Error(operator);
251
- }
252
- };
253
-
254
- let RETURN: Record<
255
- number,
256
- Record<
257
- string,
258
- string | number | boolean | null | (string | number | boolean | null)[]
259
- >
260
- > = {},
261
- lineCount = 0,
262
- foundItems = 0;
263
-
264
- const file = await open(filePath),
265
- columnName = decodeFileName(parse(filePath).name);
266
-
267
- for await (const line of file.readLines()) {
268
- lineCount++;
269
- const decodedLine = Utils.decode(line, fieldType);
270
- if (
271
- decodedLine &&
272
- ((Array.isArray(operator) &&
273
- Array.isArray(comparedAtValue) &&
274
- ((logicalOperator &&
275
- logicalOperator === "or" &&
276
- operator.some((single_operator, index) =>
277
- handleComparisonOperator(
278
- single_operator,
279
- decodedLine,
280
- comparedAtValue[index],
281
- fieldType
282
- )
283
- )) ||
284
- operator.every((single_operator, index) =>
285
- handleComparisonOperator(
286
- single_operator,
287
- decodedLine,
288
- comparedAtValue[index],
289
- fieldType
290
- )
291
- ))) ||
292
- (!Array.isArray(operator) &&
293
- handleComparisonOperator(
294
- operator,
295
- decodedLine,
296
- comparedAtValue,
297
- fieldType
298
- )))
299
- ) {
300
- foundItems++;
301
- if (offset && foundItems < offset) continue;
302
- if (limit && foundItems > limit)
303
- if (readWholeFile) continue;
304
- else break;
305
- if (!RETURN[lineCount]) RETURN[lineCount] = {};
306
- RETURN[lineCount][columnName] = decodedLine;
307
- }
308
- }
309
- if (foundItems) {
310
- return [RETURN, readWholeFile ? foundItems : foundItems - 1];
311
- } else return [null, 0];
312
- };
313
-
314
- export default class File {
315
- static get = get;
316
- static count = count;
317
- static remove = remove;
318
- static search = search;
319
- static replace = replace;
320
- static encodeFileName = encodeFileName;
321
- static decodeFileName = decodeFileName;
322
- }