@ix-xs/node-comfort 1.0.4 → 1.0.51
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/index.js +12 -14
- package/package.json +13 -17
- package/src/checker.js +76 -0
- package/src/cli.js +86 -0
- package/{core/FS.js → src/fs.js} +121 -229
- package/src/localStorage.js +242 -0
- package/{core/Logger.js → src/logger.js} +23 -97
- package/src/utils.js +225 -0
- package/core/CLI.js +0 -95
- package/core/Checker.js +0 -127
- package/core/Stepper.js +0 -115
- package/core/Storage.js +0 -348
- package/core/Utils.js +0 -206
- package/tsconfig.json +0 -18
- package/types/core/CLI.d.ts +0 -25
- package/types/core/CLI.d.ts.map +0 -1
- package/types/core/Checker.d.ts +0 -73
- package/types/core/Checker.d.ts.map +0 -1
- package/types/core/FS.d.ts +0 -220
- package/types/core/FS.d.ts.map +0 -1
- package/types/core/Logger.d.ts +0 -253
- package/types/core/Logger.d.ts.map +0 -1
- package/types/core/Stepper.d.ts +0 -49
- package/types/core/Stepper.d.ts.map +0 -1
- package/types/core/Storage.d.ts +0 -111
- package/types/core/Storage.d.ts.map +0 -1
- package/types/core/Utils.d.ts +0 -55
- package/types/core/Utils.d.ts.map +0 -1
- package/types/index.d.ts +0 -175
- package/types/index.d.ts.map +0 -1
package/core/CLI.js
DELETED
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
const { spawn, execSync } = require("node:child_process");
|
|
2
|
-
const EventEmitter = require("node:events");
|
|
3
|
-
|
|
4
|
-
class cli_stream {
|
|
5
|
-
#processus;
|
|
6
|
-
#events = new EventEmitter();
|
|
7
|
-
|
|
8
|
-
constructor(command, args) {
|
|
9
|
-
this.#processus = spawn(command, args, {
|
|
10
|
-
shell: true,
|
|
11
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
this.#processus.stdout.on("data", (data) => {
|
|
15
|
-
this.#events.emit("data", data.toString());
|
|
16
|
-
});
|
|
17
|
-
this.#processus.stderr.on("data", (data) => {
|
|
18
|
-
this.#events.emit("data", data.toString());
|
|
19
|
-
});
|
|
20
|
-
this.#processus.on("error", (error) => {
|
|
21
|
-
this.#events.emit("error", error);
|
|
22
|
-
});
|
|
23
|
-
this.#processus.on("exit", async (code, signal) => {
|
|
24
|
-
await this.stop();
|
|
25
|
-
});
|
|
26
|
-
this.#processus.on("close", (code, signal) => {
|
|
27
|
-
this.#events.emit("stopped", code, signal);
|
|
28
|
-
});
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* @typedef {"error"|"data"|"stopped"} EventName
|
|
33
|
-
* @typedef {{
|
|
34
|
-
* error: (error: Error) => void|Promise<void>,
|
|
35
|
-
* data: (message: string) => void|Promise<void>,
|
|
36
|
-
* stopped: (code: number, signal: keyof import("node:child_process").ChildProcessEventMap) => void|Promise<void>,
|
|
37
|
-
* }} EventHandler
|
|
38
|
-
*/
|
|
39
|
-
/**
|
|
40
|
-
* @template {EventName} E
|
|
41
|
-
* @param {E} event
|
|
42
|
-
* @param {EventHandler[E]} handler
|
|
43
|
-
*/
|
|
44
|
-
on(event, handler) {
|
|
45
|
-
this.#events.on(event, handler);
|
|
46
|
-
return this;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
async stop() {
|
|
50
|
-
return new Promise((resolve) => {
|
|
51
|
-
if (!this.#processus || this.#processus.killed) {
|
|
52
|
-
this.#processus = null;
|
|
53
|
-
return resolve();
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
let resolved = false;
|
|
57
|
-
|
|
58
|
-
const done = () => {
|
|
59
|
-
if (!resolved) {
|
|
60
|
-
resolved = true;
|
|
61
|
-
this.#processus = null;
|
|
62
|
-
resolve();
|
|
63
|
-
}
|
|
64
|
-
};
|
|
65
|
-
|
|
66
|
-
this.#processus.once("exit", done);
|
|
67
|
-
|
|
68
|
-
this.#processus.kill("SIGTERM");
|
|
69
|
-
|
|
70
|
-
setTimeout(() => {
|
|
71
|
-
if (this.#processus && !this.#processus.killed && !resolved) {
|
|
72
|
-
this.#processus.kill("SIGKILL");
|
|
73
|
-
}
|
|
74
|
-
done();
|
|
75
|
-
}, 5000);
|
|
76
|
-
});
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
/**
|
|
81
|
-
* @module CLI
|
|
82
|
-
* @description
|
|
83
|
-
*/
|
|
84
|
-
module.exports = {
|
|
85
|
-
/**
|
|
86
|
-
* @param {string} command
|
|
87
|
-
*/
|
|
88
|
-
cli_run: (command) => {
|
|
89
|
-
return execSync(command, {
|
|
90
|
-
shell: true,
|
|
91
|
-
stdio: "pipe",
|
|
92
|
-
});
|
|
93
|
-
},
|
|
94
|
-
cli_stream,
|
|
95
|
-
};
|
package/core/Checker.js
DELETED
|
@@ -1,127 +0,0 @@
|
|
|
1
|
-
const { types } = require("node:util");
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* @module Checker
|
|
5
|
-
* @description
|
|
6
|
-
* Provides a collection of utility functions for checking JavaScript value types.
|
|
7
|
-
*
|
|
8
|
-
* These helpers wrap common JavaScript type checks and selected `node:util.types`
|
|
9
|
-
* methods for specific built-in objects such as Map, Set, Date, and Error.
|
|
10
|
-
*
|
|
11
|
-
* @example
|
|
12
|
-
* const nodeComfort = require("@ix-xs/node-comfort");
|
|
13
|
-
*
|
|
14
|
-
* console.log(nodeComfort.isArray([1, 2, 3])); // true
|
|
15
|
-
* console.log(nodeComfort.isNumber(42)); // true
|
|
16
|
-
* console.log(nodeComfort.isDate(new Date())); // true
|
|
17
|
-
*/
|
|
18
|
-
|
|
19
|
-
module.exports = {
|
|
20
|
-
/**
|
|
21
|
-
* Checks if the value is an array.
|
|
22
|
-
* @param {*} value - The value to check.
|
|
23
|
-
* @returns {boolean} Returns `true` if the value is an array, otherwise `false`.
|
|
24
|
-
*/
|
|
25
|
-
isArray(value) {
|
|
26
|
-
return Array.isArray(value);
|
|
27
|
-
},
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Checks if the value is a valid number (not NaN).
|
|
31
|
-
* @param {*} value - The value to check.
|
|
32
|
-
* @returns {boolean} Returns `true` if the value is a valid number, otherwise `false`.
|
|
33
|
-
*/
|
|
34
|
-
isNumber(value) {
|
|
35
|
-
return typeof value === "number" && !Number.isNaN(value);
|
|
36
|
-
},
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Checks if the value is a function.
|
|
40
|
-
* @param {*} value - The value to check.
|
|
41
|
-
* @returns {boolean} Returns `true` if the value is a function, otherwise `false`.
|
|
42
|
-
*/
|
|
43
|
-
isFunction(value) {
|
|
44
|
-
return typeof value === "function";
|
|
45
|
-
},
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* Checks if the value is an object (and not `null`).
|
|
49
|
-
* @param {*} value - The value to check.
|
|
50
|
-
* @returns {boolean} Returns `true` if the value is a non-null object, otherwise `false`.
|
|
51
|
-
*/
|
|
52
|
-
isObject(value) {
|
|
53
|
-
return typeof value === "object" && value !== null;
|
|
54
|
-
},
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Checks if the value is a boolean.
|
|
58
|
-
* @param {*} value - The value to check.
|
|
59
|
-
* @returns {boolean} Returns `true` if the value is a boolean, otherwise `false`.
|
|
60
|
-
*/
|
|
61
|
-
isBoolean(value) {
|
|
62
|
-
return typeof value === "boolean";
|
|
63
|
-
},
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* Checks if the value is a string.
|
|
67
|
-
* @param {*} value - The value to check.
|
|
68
|
-
* @returns {boolean} Returns `true` if the value is a string, otherwise `false`.
|
|
69
|
-
*/
|
|
70
|
-
isString(value) {
|
|
71
|
-
return typeof value === "string";
|
|
72
|
-
},
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* Checks if the value is `undefined`.
|
|
76
|
-
* @param {*} value - The value to check.
|
|
77
|
-
* @returns {boolean} Returns `true` if the value is `undefined`, otherwise `false`.
|
|
78
|
-
*/
|
|
79
|
-
isUndefined(value) {
|
|
80
|
-
return typeof value === "undefined";
|
|
81
|
-
},
|
|
82
|
-
|
|
83
|
-
/**
|
|
84
|
-
* Checks if the value is `null`.
|
|
85
|
-
* @param {*} value - The value to check.
|
|
86
|
-
* @returns {boolean} Returns `true` if the value is `null`, otherwise `false`.
|
|
87
|
-
*/
|
|
88
|
-
isNull(value) {
|
|
89
|
-
return value === null;
|
|
90
|
-
},
|
|
91
|
-
|
|
92
|
-
/**
|
|
93
|
-
* Checks if the value is a valid `Date` object.
|
|
94
|
-
* @param {*} value - The value to check.
|
|
95
|
-
* @returns {boolean} Returns `true` if the value is an instance of `Date`, otherwise `false`.
|
|
96
|
-
*/
|
|
97
|
-
isDate(value) {
|
|
98
|
-
return types.isDate(value);
|
|
99
|
-
},
|
|
100
|
-
|
|
101
|
-
/**
|
|
102
|
-
* Checks if the value is a `Map` instance.
|
|
103
|
-
* @param {*} value - The value to check.
|
|
104
|
-
* @returns {boolean} Returns `true` if the value is a Map, otherwise `false`.
|
|
105
|
-
*/
|
|
106
|
-
isMap(value) {
|
|
107
|
-
return types.isMap(value);
|
|
108
|
-
},
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* Checks if the value is a `Set` instance.
|
|
112
|
-
* @param {*} value - The value to check.
|
|
113
|
-
* @returns {boolean} Returns `true` if the value is a Set, otherwise `false`.
|
|
114
|
-
*/
|
|
115
|
-
isSet(value) {
|
|
116
|
-
return types.isSet(value);
|
|
117
|
-
},
|
|
118
|
-
|
|
119
|
-
/**
|
|
120
|
-
* Checks if the value is a native JavaScript error (`Error`, `TypeError`, etc.).
|
|
121
|
-
* @param {*} value - The value to check.
|
|
122
|
-
* @returns {boolean} Returns `true` if the value is a native error, otherwise `false`.
|
|
123
|
-
*/
|
|
124
|
-
isError(value) {
|
|
125
|
-
return types.isNativeError(value);
|
|
126
|
-
},
|
|
127
|
-
};
|
package/core/Stepper.js
DELETED
|
@@ -1,115 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @module Stepper
|
|
3
|
-
* @description
|
|
4
|
-
* Fluent step controller for sequential task execution with navigation, skipping, and indexing.
|
|
5
|
-
*
|
|
6
|
-
* Perfect for CLI wizards, build pipelines, tutorials, or any sequential workflow.
|
|
7
|
-
* Supports forward/backward navigation, jumping to specific steps, ignoring steps, and reset.
|
|
8
|
-
*
|
|
9
|
-
* @example
|
|
10
|
-
* const nodeComfort = require("@ix-xs/node-comfort");
|
|
11
|
-
*
|
|
12
|
-
* // Simple 3-step process
|
|
13
|
-
* nodeComfort.step([
|
|
14
|
-
* () => console.log("Step 1: Validate config"),
|
|
15
|
-
* () => console.log("Step 2: Build assets"),
|
|
16
|
-
* () => console.log("Step 3: Deploy")
|
|
17
|
-
* ])
|
|
18
|
-
* .next().next().next(); // Execute all steps
|
|
19
|
-
*
|
|
20
|
-
* // Interactive navigation
|
|
21
|
-
* const wizard = nodeComfort.step([
|
|
22
|
-
* () => nodeComfort.log("<%yellow 1%> Enter name"),
|
|
23
|
-
* () => nodeComfort.log("<%yellow 2%> Enter email"),
|
|
24
|
-
* () => nodeComfort.log("<%green ✓%> Complete!")
|
|
25
|
-
* ]);
|
|
26
|
-
*
|
|
27
|
-
* wizard.next().back().ignore().next(); // 1 → back to 0 → skip 1 → execute 2
|
|
28
|
-
*/
|
|
29
|
-
module.exports = {
|
|
30
|
-
/**
|
|
31
|
-
* Creates a step controller for sequential function execution.
|
|
32
|
-
*
|
|
33
|
-
* Returns fluent API with `next()`, `back()`, `at()`, `ignore()`, `reset()` methods.
|
|
34
|
-
*
|
|
35
|
-
* @param {Array<() => void>} steps - Array of step functions to execute.
|
|
36
|
-
*
|
|
37
|
-
* @example
|
|
38
|
-
* const steps = nodeComfort.step([
|
|
39
|
-
* () => console.log("Install dependencies"),
|
|
40
|
-
* () => console.log("Compile TypeScript"),
|
|
41
|
-
* () => console.log("Run tests")
|
|
42
|
-
* ]);
|
|
43
|
-
*
|
|
44
|
-
* steps.next().next(); // Execute steps 0, 1
|
|
45
|
-
* steps.back(); // Go back to step 0
|
|
46
|
-
* steps.at(2); // Jump to step 2
|
|
47
|
-
*/
|
|
48
|
-
step(steps) {
|
|
49
|
-
let i = -1;
|
|
50
|
-
|
|
51
|
-
const _ = {
|
|
52
|
-
currentIndex: i,
|
|
53
|
-
currentStep: steps[i],
|
|
54
|
-
/**
|
|
55
|
-
* Advances to next step and executes it (if exists).
|
|
56
|
-
*/
|
|
57
|
-
next() {
|
|
58
|
-
if (i < steps.length - 1) {
|
|
59
|
-
i++;
|
|
60
|
-
if (typeof steps[i] === "function") steps[i]();
|
|
61
|
-
}
|
|
62
|
-
return _;
|
|
63
|
-
},
|
|
64
|
-
/**
|
|
65
|
-
* Goes back to previous step and re-executes it (if exists).
|
|
66
|
-
*/
|
|
67
|
-
back() {
|
|
68
|
-
if (i > 0) {
|
|
69
|
-
i--;
|
|
70
|
-
if (typeof steps[i] === "function") steps[i]();
|
|
71
|
-
}
|
|
72
|
-
return _;
|
|
73
|
-
},
|
|
74
|
-
/**
|
|
75
|
-
* Jumps to specific step index and executes it.
|
|
76
|
-
* @param {number} index - Step index (0 to steps.length-1).
|
|
77
|
-
* @example
|
|
78
|
-
* steps.at(2); // Jump to 3rd step
|
|
79
|
-
*/
|
|
80
|
-
at(index) {
|
|
81
|
-
if (index >= 0 && index < steps.length) {
|
|
82
|
-
i = index;
|
|
83
|
-
if (typeof steps[i] === "function") steps[i]();
|
|
84
|
-
}
|
|
85
|
-
return _;
|
|
86
|
-
},
|
|
87
|
-
/**
|
|
88
|
-
* Advances index without executing current step.
|
|
89
|
-
* @example
|
|
90
|
-
* steps.next().ignore().next(); // Execute 0, skip 1, execute 2
|
|
91
|
-
*/
|
|
92
|
-
ignore() {
|
|
93
|
-
if (i < steps.length - 1) {
|
|
94
|
-
i++;
|
|
95
|
-
}
|
|
96
|
-
return _;
|
|
97
|
-
},
|
|
98
|
-
/**
|
|
99
|
-
* Resets to initial state (index: -1).
|
|
100
|
-
*/
|
|
101
|
-
reset() {
|
|
102
|
-
i = -1;
|
|
103
|
-
return _;
|
|
104
|
-
},
|
|
105
|
-
all() {
|
|
106
|
-
while (i < steps.length - 1) {
|
|
107
|
-
this.next();
|
|
108
|
-
}
|
|
109
|
-
return _;
|
|
110
|
-
},
|
|
111
|
-
};
|
|
112
|
-
|
|
113
|
-
return _;
|
|
114
|
-
},
|
|
115
|
-
};
|
package/core/Storage.js
DELETED
|
@@ -1,348 +0,0 @@
|
|
|
1
|
-
const FS = require("./FS");
|
|
2
|
-
const Checker = require("./Checker");
|
|
3
|
-
const { basename } = require("node:path");
|
|
4
|
-
|
|
5
|
-
const originalEmitWarning = process.emitWarning;
|
|
6
|
-
process.emitWarning = (warning, ...args) => {
|
|
7
|
-
if ((warning.name === "ExperimentalWarning" && warning.message.includes("SQLite")) || (typeof warning === "string" && warning.startsWith("SQLite is an experimental feature"))) {
|
|
8
|
-
return;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
return originalEmitWarning.call(process, warning, ...args);
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
const { DatabaseSync } = require("node:sqlite");
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* @module Storage
|
|
18
|
-
*/
|
|
19
|
-
|
|
20
|
-
module.exports = class Storage {
|
|
21
|
-
#$;
|
|
22
|
-
#statements = new Map();
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* @param {string} [path = ":memomry"]
|
|
26
|
-
*/
|
|
27
|
-
constructor(path = ":memory") {
|
|
28
|
-
if (path !== ":memory" && path.includes("/")) {
|
|
29
|
-
const split = path.split("/");
|
|
30
|
-
const file = split[split.length - 1];
|
|
31
|
-
const folder = FS.createPath(path.replace(file, ""));
|
|
32
|
-
|
|
33
|
-
FS.createFolder(folder);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
this.#$ = new DatabaseSync(path);
|
|
37
|
-
|
|
38
|
-
this.exec("PRAGMA journal_mode = WAL");
|
|
39
|
-
this.exec("PRAGMA foreign_keys = ON");
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* @param {string} sql
|
|
44
|
-
* @param {array|object} [options = []]
|
|
45
|
-
*/
|
|
46
|
-
exec(sql, options = []) {
|
|
47
|
-
try {
|
|
48
|
-
const stmt = this.#$.prepare(sql);
|
|
49
|
-
const result = stmt.run(...Checker.isArray(options) ? options : [options]);
|
|
50
|
-
|
|
51
|
-
return {
|
|
52
|
-
changes: result.changes,
|
|
53
|
-
lastInsertRowid: result.lastInsertRowid,
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
} catch (error) {
|
|
57
|
-
throw error;
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* @param {string} sql
|
|
63
|
-
* @param {array|object} [options = []]
|
|
64
|
-
*/
|
|
65
|
-
getOne(sql, options = []) {
|
|
66
|
-
try {
|
|
67
|
-
const stmt = this.#$.prepare(sql);
|
|
68
|
-
|
|
69
|
-
return stmt.get(...Checker.isArray(options) ? options : [options]) || null;
|
|
70
|
-
} catch (error) {
|
|
71
|
-
throw error;
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* @param {string} sql
|
|
77
|
-
* @param {array|object} [options = []]
|
|
78
|
-
*/
|
|
79
|
-
getAll(sql, options = []) {
|
|
80
|
-
try {
|
|
81
|
-
const stmt = this.#$.prepare(sql);
|
|
82
|
-
|
|
83
|
-
return stmt.all(...Checker.isArray(options) ? options : [options]) || [];
|
|
84
|
-
} catch (error) {
|
|
85
|
-
throw error;
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* @param {string} name
|
|
91
|
-
* @param {string} sql
|
|
92
|
-
*/
|
|
93
|
-
prepare(name, sql) {
|
|
94
|
-
if (this.#statements.has(name)) {
|
|
95
|
-
return this.#statements.get(name);
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
const stmt = this.#$.prepare(sql);
|
|
99
|
-
|
|
100
|
-
this.#statements.set(name, stmt);
|
|
101
|
-
|
|
102
|
-
return stmt;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
* @param {string} table
|
|
107
|
-
* @param {object} schema
|
|
108
|
-
*/
|
|
109
|
-
createTable(table, schema) {
|
|
110
|
-
const columns = Object.entries(schema)
|
|
111
|
-
.map(([key, type]) => `${key} ${type}`)
|
|
112
|
-
.join(", ");
|
|
113
|
-
|
|
114
|
-
const sql = `CREATE TABLE IF NOT EXISTS ${table} (${columns})`;
|
|
115
|
-
|
|
116
|
-
this.exec(sql);
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
/**
|
|
120
|
-
* @param {string} table
|
|
121
|
-
* @param {object} data
|
|
122
|
-
*/
|
|
123
|
-
insert(table, data) {
|
|
124
|
-
const keys = Object.keys(data);
|
|
125
|
-
const placeholders = keys.map(() => "?").join(", ");
|
|
126
|
-
const sql = `INSERT INTO ${table} (${keys.join(", ")}) VALUES (${placeholders})`;
|
|
127
|
-
|
|
128
|
-
const result = this.exec(sql, Object.values(data));
|
|
129
|
-
|
|
130
|
-
return result.lastInsertRowid;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
/**
|
|
134
|
-
* @param {string} table
|
|
135
|
-
* @param {number} id
|
|
136
|
-
* @returns {object|null}
|
|
137
|
-
*/
|
|
138
|
-
findById(table, id) {
|
|
139
|
-
return this.getOne(`SELECT * FROM ${table} WHERE id = ?`, [id]);
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
/**
|
|
143
|
-
* @param {string} table
|
|
144
|
-
* @param {object} [where = {}]
|
|
145
|
-
* @param {object} [options = {}]
|
|
146
|
-
*/
|
|
147
|
-
findAll(table, where, options) {
|
|
148
|
-
const { limit = 100, offset = 0, orderBy = "id" } = options;
|
|
149
|
-
|
|
150
|
-
let sql = `SELECT * FROM ${table}`;
|
|
151
|
-
|
|
152
|
-
if (Object.keys(where).length > 0) {
|
|
153
|
-
const conditions = Object.keys(where)
|
|
154
|
-
.map(key => `${key} = ?`)
|
|
155
|
-
.join(" AND ");
|
|
156
|
-
sql += ` WHERE ${conditions}`;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
sql += ` ORDER BY ${orderBy} LIMIT ? OFFSET ?`;
|
|
160
|
-
|
|
161
|
-
const params = [...Object.values(where), limit, offset];
|
|
162
|
-
|
|
163
|
-
return this.getAll(sql, params);
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
/**
|
|
167
|
-
* @param {string} table
|
|
168
|
-
* @param {object} [where]
|
|
169
|
-
*/
|
|
170
|
-
count(table, where = {}) {
|
|
171
|
-
let sql = `SELECT COUNT(*) as count FROM ${table}`;
|
|
172
|
-
|
|
173
|
-
if (Object.keys(where).length > 0) {
|
|
174
|
-
const conditions = Object.keys(where)
|
|
175
|
-
.map(key => `${key} = ?`)
|
|
176
|
-
.join(" AND ");
|
|
177
|
-
sql += ` WHERE ${conditions}`;
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
const result = this.getOne(sql, Object.values(where));
|
|
181
|
-
|
|
182
|
-
return result?.count || 0;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
/**
|
|
186
|
-
* @param {string} table
|
|
187
|
-
* @param {number} id
|
|
188
|
-
* @param {object} data
|
|
189
|
-
*/
|
|
190
|
-
update(table, id, data) {
|
|
191
|
-
const keys = Object.keys(data);
|
|
192
|
-
const setClause = keys.map(key => `${key} = ?`).join(", ");
|
|
193
|
-
const sql = `UPDATE ${table} SET ${setClause} WHERE id = ?`;
|
|
194
|
-
|
|
195
|
-
const result = this.exec(sql, [...Object.values(data), id]);
|
|
196
|
-
|
|
197
|
-
return result.changes;
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
/**
|
|
201
|
-
* @param {string} table
|
|
202
|
-
* @param {number} id
|
|
203
|
-
*/
|
|
204
|
-
delete(table, id) {
|
|
205
|
-
const sql = `DELETE FROM ${table} WHERE id = ?`;
|
|
206
|
-
const result = this.exec(sql, [id]);
|
|
207
|
-
|
|
208
|
-
return result.changes;
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
/**
|
|
212
|
-
* @param {string} table
|
|
213
|
-
* @param {object} data
|
|
214
|
-
* @param {string} [conflictColumn]
|
|
215
|
-
*/
|
|
216
|
-
upsert(table, data, conflictColumn = "id") {
|
|
217
|
-
const keys = Object.keys(data);
|
|
218
|
-
const placeholders = keys.map(() => "?").join(", ");
|
|
219
|
-
const updates = keys
|
|
220
|
-
.filter(k => k !== conflictColumn)
|
|
221
|
-
.map(k => `${k} = EXCLUDED.${k}`)
|
|
222
|
-
.join(", ");
|
|
223
|
-
|
|
224
|
-
const sql = `
|
|
225
|
-
INSERT INTO ${table} (${keys.join(", ")})
|
|
226
|
-
VALUES (${placeholders})
|
|
227
|
-
ON CONFLICT(${conflictColumn})
|
|
228
|
-
DO UPDATE SET ${updates}
|
|
229
|
-
`;
|
|
230
|
-
|
|
231
|
-
return this.exec(sql, Object.values(data));
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
/**
|
|
235
|
-
* @param {function} callback
|
|
236
|
-
*/
|
|
237
|
-
transaction(callback) {
|
|
238
|
-
try {
|
|
239
|
-
this.exec("BEGIN TRANSACTION");
|
|
240
|
-
|
|
241
|
-
const result = callback();
|
|
242
|
-
|
|
243
|
-
this.exec("COMMIT");
|
|
244
|
-
|
|
245
|
-
return result;
|
|
246
|
-
} catch (error) {
|
|
247
|
-
this.exec("ROLLBACK");
|
|
248
|
-
throw error;
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
/**
|
|
253
|
-
* @param {string} migrationsDir - Répertoire contenant les migrations
|
|
254
|
-
*/
|
|
255
|
-
runMigrations(migrationsDir) {
|
|
256
|
-
if (!FS.getFolder(migrationsDir)) {
|
|
257
|
-
FS.createFolder(migrationsDir);
|
|
258
|
-
|
|
259
|
-
return;
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
const migrationFiles = FS.getFilesIn(migrationsDir).filter(f => f.endsWith(".sql")).sort();
|
|
263
|
-
|
|
264
|
-
this.createTable("__migrations", {
|
|
265
|
-
name: "TEXT PRIMARY KEY",
|
|
266
|
-
executed_at: "DATETIME DEFAULT CURRENT_TIMESTAMP",
|
|
267
|
-
});
|
|
268
|
-
|
|
269
|
-
for (const file of migrationFiles) {
|
|
270
|
-
const migrationName = basename(file, ".sql");
|
|
271
|
-
const alreadyRun = this.getOne("SELECT * FROM __migrations WHERE name = ?", [migrationName]);
|
|
272
|
-
|
|
273
|
-
if (!alreadyRun) {
|
|
274
|
-
const sql = FS.readFile(`${migrationsDir}/${file}`);
|
|
275
|
-
|
|
276
|
-
try {
|
|
277
|
-
this.exec(sql);
|
|
278
|
-
this.insert("__migrations", { name: migrationName });
|
|
279
|
-
} catch (error) {
|
|
280
|
-
throw error;
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
/**
|
|
287
|
-
* @param {string} table
|
|
288
|
-
*/
|
|
289
|
-
export(table) {
|
|
290
|
-
return this.getAll(`SELECT * FROM ${table}`);
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
/**
|
|
294
|
-
* @param {string} table
|
|
295
|
-
* @param {array} data
|
|
296
|
-
*/
|
|
297
|
-
import(table, data) {
|
|
298
|
-
let count = 0;
|
|
299
|
-
this.transaction(() => {
|
|
300
|
-
for (const row of data) {
|
|
301
|
-
this.insert(table, row);
|
|
302
|
-
count++;
|
|
303
|
-
}
|
|
304
|
-
});
|
|
305
|
-
return count;
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
/**
|
|
309
|
-
* @param {string} table
|
|
310
|
-
*/
|
|
311
|
-
truncate(table) {
|
|
312
|
-
this.exec(`DELETE FROM ${table}`);
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
/**
|
|
316
|
-
* @param {string} indexName
|
|
317
|
-
* @param {string} table
|
|
318
|
-
* @param {string} columns
|
|
319
|
-
*/
|
|
320
|
-
createIndex(indexName, table, columns) {
|
|
321
|
-
this.exec(`CREATE INDEX IF NOT EXISTS ${indexName} ON ${table} (${columns})`);
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
/**
|
|
325
|
-
* @returns {object}
|
|
326
|
-
*/
|
|
327
|
-
stats() {
|
|
328
|
-
const tables = this.getAll("SELECT name FROM sqlite_master WHERE type='table'");
|
|
329
|
-
const stats = {};
|
|
330
|
-
|
|
331
|
-
for (const { name } of tables) {
|
|
332
|
-
if (!name.startsWith("__")) {
|
|
333
|
-
const count = this.count(name);
|
|
334
|
-
stats[name] = count;
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
return stats;
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
close() {
|
|
342
|
-
if (this.db) {
|
|
343
|
-
this.#$.close();
|
|
344
|
-
this.#statements.clear();
|
|
345
|
-
this.#$ = null;
|
|
346
|
-
}
|
|
347
|
-
}
|
|
348
|
-
};
|