inibase 2.0.0 → 2.0.1
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.d.ts +1 -0
- package/dist/file.js +14 -5
- package/dist/index.d.ts +10 -0
- package/dist/index.js +33 -11
- package/dist/utils.d.ts +2 -1
- package/dist/utils.js +20 -4
- package/package.json +1 -1
package/dist/file.d.ts
CHANGED
|
@@ -113,6 +113,7 @@ export declare const search: (filePath: string, operator: ComparisonOperator | C
|
|
|
113
113
|
databasePath?: string;
|
|
114
114
|
}, limit?: number, offset?: number, readWholeFile?: boolean) => Promise<[Record<number, string | number | boolean | null | undefined | (string | number | boolean | null)[]> | null, number, Set<number> | null]>;
|
|
115
115
|
export declare const sum: (fp: string, ln?: number | number[]) => Promise<number>;
|
|
116
|
+
export declare const avg: (fp: string, ln?: number | number[]) => Promise<number>;
|
|
116
117
|
export declare const min: (fp: string, ln?: number | number[]) => Promise<number>;
|
|
117
118
|
export declare const max: (fp: string, ln?: number | number[]) => Promise<number>;
|
|
118
119
|
export declare const getFileDate: (path: string) => Promise<Date>;
|
package/dist/file.js
CHANGED
|
@@ -132,7 +132,9 @@ const unSecureString = (input) => {
|
|
|
132
132
|
// Fast path: the common case has no `\n` escape sequence, so avoid
|
|
133
133
|
// allocating a replacement string (and a fresh RegExp) per cell.
|
|
134
134
|
if (typeof input === "string")
|
|
135
|
-
return input.includes("\\n")
|
|
135
|
+
return input.includes("\\n")
|
|
136
|
+
? input.replaceAll("\\n", "\n") || null
|
|
137
|
+
: input;
|
|
136
138
|
return null;
|
|
137
139
|
};
|
|
138
140
|
/**
|
|
@@ -831,11 +833,11 @@ export const search = async (filePath, operator, comparedAtValue, logicalOperato
|
|
|
831
833
|
}
|
|
832
834
|
};
|
|
833
835
|
/**
|
|
834
|
-
* Reads the file once and returns either the sum, min or max of the
|
|
836
|
+
* Reads the file once and returns either the sum, average, min or max of the
|
|
835
837
|
* (optionally-selected) numeric lines.
|
|
836
838
|
*
|
|
837
839
|
* @param filePath Absolute path of the column file (may be .gz-compressed).
|
|
838
|
-
* @param wanted Metric to compute: "sum" (default), "min" or "max".
|
|
840
|
+
* @param wanted Metric to compute: "sum" (default), "avg", "min" or "max".
|
|
839
841
|
* @param lineNumbers Specific line-number(s) to restrict the scan to.
|
|
840
842
|
*
|
|
841
843
|
* @returns Promise<number> The requested metric, or 0 if no numeric value found.
|
|
@@ -864,7 +866,7 @@ async function reduceNumbers(filePath, wanted = "sum", lineNumbers) {
|
|
|
864
866
|
if (Number.isNaN(num))
|
|
865
867
|
continue;
|
|
866
868
|
processed++;
|
|
867
|
-
if (wanted === "sum") {
|
|
869
|
+
if (wanted === "sum" || wanted === "avg") {
|
|
868
870
|
sum += num;
|
|
869
871
|
}
|
|
870
872
|
else if (wanted === "min") {
|
|
@@ -885,10 +887,17 @@ async function reduceNumbers(filePath, wanted = "sum", lineNumbers) {
|
|
|
885
887
|
}
|
|
886
888
|
if (processed === 0)
|
|
887
889
|
return 0; // nothing numeric found
|
|
888
|
-
return wanted === "sum"
|
|
890
|
+
return wanted === "sum"
|
|
891
|
+
? sum
|
|
892
|
+
: wanted === "avg"
|
|
893
|
+
? sum / processed
|
|
894
|
+
: wanted === "min"
|
|
895
|
+
? min
|
|
896
|
+
: max;
|
|
889
897
|
}
|
|
890
898
|
/* Optional convenience wrappers (signatures unchanged) */
|
|
891
899
|
export const sum = (fp, ln) => reduceNumbers(fp, "sum", ln);
|
|
900
|
+
export const avg = (fp, ln) => reduceNumbers(fp, "avg", ln);
|
|
892
901
|
export const min = (fp, ln) => reduceNumbers(fp, "min", ln);
|
|
893
902
|
export const max = (fp, ln) => reduceNumbers(fp, "max", ln);
|
|
894
903
|
export const getFileDate = (path) => stat(path)
|
package/dist/index.d.ts
CHANGED
|
@@ -231,6 +231,16 @@ export default class Inibase {
|
|
|
231
231
|
*/
|
|
232
232
|
sum(tableName: string, columns: string, where?: number | string | (number | string)[] | Criteria): Promise<number>;
|
|
233
233
|
sum(tableName: string, columns: string[], where?: number | string | (number | string)[] | Criteria): Promise<Record<string, number>>;
|
|
234
|
+
/**
|
|
235
|
+
* Generate average of column(s) in a table
|
|
236
|
+
*
|
|
237
|
+
* @param {string} tableName
|
|
238
|
+
* @param {string} columns
|
|
239
|
+
* @param {(number | string | (number | string)[] | Criteria)} [where]
|
|
240
|
+
* @return {*} {Promise<number | Record<string, number>>}
|
|
241
|
+
*/
|
|
242
|
+
avg(tableName: string, columns: string, where?: number | string | (number | string)[] | Criteria): Promise<number>;
|
|
243
|
+
avg(tableName: string, columns: string[], where?: number | string | (number | string)[] | Criteria): Promise<Record<string, number>>;
|
|
234
244
|
/**
|
|
235
245
|
* Generate max of column(s) in a table
|
|
236
246
|
*
|
package/dist/index.js
CHANGED
|
@@ -536,6 +536,8 @@ export default class Inibase {
|
|
|
536
536
|
? value
|
|
537
537
|
: Number(value.trim())
|
|
538
538
|
: 0;
|
|
539
|
+
case "date":
|
|
540
|
+
return Utils.dateToTimestamp(value);
|
|
539
541
|
case "id":
|
|
540
542
|
return Utils.isNumber(value)
|
|
541
543
|
? value
|
|
@@ -842,9 +844,7 @@ export default class Inibase {
|
|
|
842
844
|
key: field.key,
|
|
843
845
|
config: {
|
|
844
846
|
...field,
|
|
845
|
-
type: field.key === "id" && decodeID
|
|
846
|
-
? "number"
|
|
847
|
-
: field.type,
|
|
847
|
+
type: field.key === "id" && decodeID ? "number" : field.type,
|
|
848
848
|
databasePath: this.databasePath,
|
|
849
849
|
},
|
|
850
850
|
});
|
|
@@ -1326,16 +1326,15 @@ export default class Inibase {
|
|
|
1326
1326
|
const itemsIDs = Object.values((await File.get(join(tablePath, `id${this.getFileExtension(tableName)}`), lineNumbers, { key: "BLABLA", type: "number" })) ?? {}).map(Number);
|
|
1327
1327
|
awkCommand = `awk '${itemsIDs.map((id) => `$1 == ${id}`).join(" || ")}'`;
|
|
1328
1328
|
}
|
|
1329
|
+
// perPage < 0 means "no limit": select every line instead of
|
|
1330
|
+
// generating an empty awk window (with perPage -1 the old code
|
|
1331
|
+
// produced `awk ''`, which prints nothing and the empty stdout
|
|
1332
|
+
// decoded into a single hollow row).
|
|
1329
1333
|
else
|
|
1330
|
-
// perPage < 0 means "no limit": select every line instead of
|
|
1331
|
-
// generating an empty awk window (with perPage -1 the old code
|
|
1332
|
-
// produced `awk ''`, which prints nothing and the empty stdout
|
|
1333
|
-
// decoded into a single hollow row).
|
|
1334
1334
|
awkCommand =
|
|
1335
1335
|
options.perPage < 0
|
|
1336
1336
|
? "awk '1'"
|
|
1337
|
-
: `awk '${Array.from({ length: options.perPage }, (_, index) => (options.page - 1) *
|
|
1338
|
-
options.perPage +
|
|
1337
|
+
: `awk '${Array.from({ length: options.perPage }, (_, index) => (options.page - 1) * options.perPage +
|
|
1339
1338
|
index +
|
|
1340
1339
|
1)
|
|
1341
1340
|
.map((lineNumber) => `NR==${lineNumber}`)
|
|
@@ -1455,8 +1454,7 @@ export default class Inibase {
|
|
|
1455
1454
|
let countItems = 0;
|
|
1456
1455
|
const isDecodeID = globalConfig[this.databasePath].tables?.get(tableName)?.config
|
|
1457
1456
|
.decodeID === true &&
|
|
1458
|
-
!globalConfig[this.databasePath].tables?.get(tableName)?.config
|
|
1459
|
-
.prepend;
|
|
1457
|
+
!globalConfig[this.databasePath].tables?.get(tableName)?.config.prepend;
|
|
1460
1458
|
if (isDecodeID &&
|
|
1461
1459
|
this.idDensity.get(tableName) &&
|
|
1462
1460
|
Ids.every(Utils.isNumber)) {
|
|
@@ -1976,6 +1974,30 @@ export default class Inibase {
|
|
|
1976
1974
|
}
|
|
1977
1975
|
return columns.length > 1 ? RETURN : Object.values(RETURN)[0];
|
|
1978
1976
|
}
|
|
1977
|
+
async avg(tableName, columns, where) {
|
|
1978
|
+
this.validateName(tableName);
|
|
1979
|
+
if (!Array.isArray(columns))
|
|
1980
|
+
columns = [columns];
|
|
1981
|
+
for (const column of columns)
|
|
1982
|
+
this.validateName(column);
|
|
1983
|
+
await this.throwErrorIfTableEmpty(tableName);
|
|
1984
|
+
const RETURN = {};
|
|
1985
|
+
const tablePath = join(this.databasePath, tableName);
|
|
1986
|
+
for await (const column of columns) {
|
|
1987
|
+
const columnPath = join(tablePath, `${column}${this.getFileExtension(tableName)}`);
|
|
1988
|
+
if (await File.isExists(columnPath)) {
|
|
1989
|
+
if (where) {
|
|
1990
|
+
const lineNumbers = await this.get(tableName, where, undefined, undefined, true);
|
|
1991
|
+
RETURN[column] = lineNumbers
|
|
1992
|
+
? await File.avg(columnPath, lineNumbers)
|
|
1993
|
+
: 0;
|
|
1994
|
+
}
|
|
1995
|
+
else
|
|
1996
|
+
RETURN[column] = await File.avg(columnPath);
|
|
1997
|
+
}
|
|
1998
|
+
}
|
|
1999
|
+
return columns.length > 1 ? RETURN : Object.values(RETURN)[0];
|
|
2000
|
+
}
|
|
1979
2001
|
async max(tableName, columns, where) {
|
|
1980
2002
|
this.validateName(tableName);
|
|
1981
2003
|
if (!Array.isArray(columns))
|
package/dist/utils.d.ts
CHANGED
|
@@ -115,7 +115,8 @@ export declare const isPassword: (input: unknown) => input is string;
|
|
|
115
115
|
* @param input - The input to be checked, can be of any type.
|
|
116
116
|
* @returns A boolean indicating whether the input is a valid date.
|
|
117
117
|
*/
|
|
118
|
-
export declare const
|
|
118
|
+
export declare const dateToTimestamp: (input: unknown) => number | null;
|
|
119
|
+
export declare const isDate: (input: unknown) => boolean;
|
|
119
120
|
/**
|
|
120
121
|
* Checks if the input is a valid ID.
|
|
121
122
|
*
|
package/dist/utils.js
CHANGED
|
@@ -163,20 +163,36 @@ export const isPassword = (input) => typeof input === "string" && input.length =
|
|
|
163
163
|
* @param input - The input to be checked, can be of any type.
|
|
164
164
|
* @returns A boolean indicating whether the input is a valid date.
|
|
165
165
|
*/
|
|
166
|
-
export const
|
|
166
|
+
export const dateToTimestamp = (input) => {
|
|
167
167
|
// Check if the input is null, undefined, or an empty string
|
|
168
168
|
if (input == null || input === "")
|
|
169
|
-
return
|
|
169
|
+
return null;
|
|
170
|
+
if (typeof input === "string") {
|
|
171
|
+
const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(input);
|
|
172
|
+
if (match) {
|
|
173
|
+
const year = Number(match[1]);
|
|
174
|
+
const month = Number(match[2]);
|
|
175
|
+
const day = Number(match[3]);
|
|
176
|
+
const timestamp = Date.UTC(year, month - 1, day);
|
|
177
|
+
const date = new Date(timestamp);
|
|
178
|
+
return date.getUTCFullYear() === year &&
|
|
179
|
+
date.getUTCMonth() === month - 1 &&
|
|
180
|
+
date.getUTCDate() === day
|
|
181
|
+
? timestamp
|
|
182
|
+
: null;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
170
185
|
// Convert to number and check if it's a valid number
|
|
171
186
|
const numTimestamp = Number(input);
|
|
172
187
|
// Check if the converted number is NaN or not finite
|
|
173
188
|
if (Number.isNaN(numTimestamp) || !Number.isFinite(numTimestamp))
|
|
174
|
-
return
|
|
189
|
+
return null;
|
|
175
190
|
// Create a Date object from the timestamp
|
|
176
191
|
const date = new Date(numTimestamp);
|
|
177
192
|
// Check if the date is valid
|
|
178
|
-
return date.getTime() === numTimestamp;
|
|
193
|
+
return date.getTime() === numTimestamp ? numTimestamp : null;
|
|
179
194
|
};
|
|
195
|
+
export const isDate = (input) => dateToTimestamp(input) !== null;
|
|
180
196
|
/**
|
|
181
197
|
* Checks if the input is a valid ID.
|
|
182
198
|
*
|