@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/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
- };