@ix-xs/node-comfort 1.0.3 → 1.0.5

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.
Files changed (41) hide show
  1. package/index.js +12 -14
  2. package/package.json +17 -17
  3. package/src/checker.js +76 -0
  4. package/src/cli.js +86 -0
  5. package/{core/FS.js → src/fs.js} +121 -230
  6. package/src/localStorage.js +242 -0
  7. package/{core/Logger.js → src/logger.js} +23 -97
  8. package/src/utils.js +225 -0
  9. package/tsconfig.json +1 -1
  10. package/types/index.d.ts +73 -50
  11. package/types/src/checker.d.ts +49 -0
  12. package/types/src/checker.d.ts.map +1 -0
  13. package/types/src/cli.d.ts +26 -0
  14. package/types/src/cli.d.ts.map +1 -0
  15. package/types/src/fs.d.ts +210 -0
  16. package/types/src/fs.d.ts.map +1 -0
  17. package/types/src/localStorage.d.ts +97 -0
  18. package/types/src/localStorage.d.ts.map +1 -0
  19. package/types/src/logger.d.ts +132 -0
  20. package/types/src/logger.d.ts.map +1 -0
  21. package/types/src/utils.d.ts +92 -0
  22. package/types/src/utils.d.ts.map +1 -0
  23. package/core/CLI.js +0 -95
  24. package/core/Checker.js +0 -127
  25. package/core/Stepper.js +0 -115
  26. package/core/Storage.js +0 -348
  27. package/core/Utils.js +0 -206
  28. package/types/core/CLI.d.ts +0 -25
  29. package/types/core/CLI.d.ts.map +0 -1
  30. package/types/core/Checker.d.ts +0 -73
  31. package/types/core/Checker.d.ts.map +0 -1
  32. package/types/core/FS.d.ts +0 -211
  33. package/types/core/FS.d.ts.map +0 -1
  34. package/types/core/Logger.d.ts +0 -253
  35. package/types/core/Logger.d.ts.map +0 -1
  36. package/types/core/Stepper.d.ts +0 -49
  37. package/types/core/Stepper.d.ts.map +0 -1
  38. package/types/core/Storage.d.ts +0 -111
  39. package/types/core/Storage.d.ts.map +0 -1
  40. package/types/core/Utils.d.ts +0 -55
  41. package/types/core/Utils.d.ts.map +0 -1
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
- };
package/core/Utils.js DELETED
@@ -1,206 +0,0 @@
1
- const { config } = require("@dotenvx/dotenvx");
2
- const { log } = require("./Logger");
3
-
4
- /**
5
- * @module Utils
6
- * @description
7
- */
8
- module.exports = {
9
- getEnv(env) {
10
- config({ quiet: true });
11
- return process.env[env];
12
- },
13
- /**
14
- * Creates a Promise that resolves after specified milliseconds.
15
- *
16
- * @param {number} ms - Milliseconds to wait (positive integer).
17
- * @returns {Promise<void>} Promise that resolves after delay.
18
- *
19
- * @example
20
- * // Sequential delays
21
- * await wait(100); // 100ms
22
- * await wait(500); // 500ms
23
- * await wait(2000); // 2 seconds
24
- */
25
- wait(ms) {
26
- return new Promise((resolve) => setTimeout(resolve, ms));
27
- },
28
- /**
29
- *
30
- * @param {boolean|Promise<boolean>} predicate
31
- * @param {*} handler
32
- * @param {object} [options]
33
- * @param {number} [options.interval]
34
- * @param {number|null} [options.timeout]
35
- * @param {number|null} [options.max]
36
- */
37
- when(predicate, handler, options) {
38
- const interval = options?.interval ?? 50;
39
- const timeout = options?.timeout ?? null;
40
- const max = options?.max ?? null;
41
-
42
- const listeners = {
43
- trigger: new Set(),
44
- error: new Set(),
45
- timeout: new Set(),
46
- };
47
-
48
- let _timer = null;
49
- let _timeoutTimer = null;
50
- let _stopped = false;
51
- let _count = 0;
52
-
53
- function emit(event, payload) {
54
- for (const fn of listeners[event]) {
55
- try {
56
- fn(payload);
57
- } catch (error) {
58
- console.error(`[when] listener error on "${event}":`, error);
59
- }
60
- }
61
- }
62
-
63
- async function evalPredicate() {
64
- if (typeof predicate === "function") {
65
- return await predicate();
66
- }
67
- return Boolean(predicate);
68
- }
69
-
70
- async function tick() {
71
- if (_stopped) return;
72
-
73
- try {
74
- const ok = await evalPredicate();
75
-
76
- if (ok) {
77
- _count++;
78
- emit("trigger", handler);
79
-
80
- if (max != null && _count >= max) {
81
- _stopInternal();
82
- return;
83
- }
84
- }
85
-
86
- if (!_stopped) {
87
- _timer = setTimeout(tick, interval);
88
- }
89
- } catch (error) {
90
- emit("error", error);
91
- if (!_stopped) {
92
- _timer = setTimeout(tick, interval);
93
- }
94
- }
95
- }
96
-
97
- function _stopInternal() {
98
- _stopped = true;
99
- if (_timer) {
100
- clearTimeout(_timer);
101
- _timer = null;
102
- }
103
- if (_timeoutTimer) {
104
- clearTimeout(_timeoutTimer);
105
- _timeoutTimer = null;
106
- }
107
- }
108
-
109
- const api = {
110
- start() {
111
- if (_stopped) return api;
112
-
113
- if (!_timer) {
114
- _timer = setTimeout(tick, 0);
115
- }
116
-
117
- if (timeout != null && !_timeoutTimer) {
118
- _timeoutTimer = setTimeout(() => {
119
- if (_stopped) return;
120
- emit("timeout", handler);
121
- _stopInternal();
122
- }, timeout);
123
- }
124
-
125
- return api;
126
- },
127
- stop() {
128
- _stopInternal();
129
- return api;
130
- },
131
- /**
132
- * @param {"error"|"trigger"|"timeout"} event
133
- * @param {(payload: any) => void} fn
134
- */
135
- on(event, fn) {
136
- listeners[event]?.add(fn);
137
- return api;
138
- },
139
- /**
140
- * @param {"error"|"trigger"|"timeout"} event
141
- * @param {(payload: any) => void} fn
142
- */
143
- off(event, fn) {
144
- listeners[event]?.delete(fn);
145
- return api;
146
- },
147
- };
148
-
149
- return api;
150
- },
151
- dontCrash() {
152
- const defaults = {
153
- error: (error) => {
154
- log(
155
- `<% red [@ix-xs/node-comfort] Process Error %>\n<% gray ${error.stack ?? error} %>`,
156
- );
157
- },
158
- exit: (code) => {
159
- log(
160
- `<% yellow [@ix-xs/node-comfort] Process Exit %>\n→ Code: <% gray ${code} %>`,
161
- );
162
- },
163
- sig: (signal) => {
164
- log(
165
- `<% yellow [@ix-xs/node-comfort] Process SIG* %>\n→ Signal: <% gray ${signal} %>`,
166
- );
167
-
168
- process.exit(0);
169
- },
170
- beforeExit: (code) => {},
171
- };
172
-
173
- const _ = {
174
- /**
175
- * @template { "error" | "exit" | "sig" | "beforeExit" } E
176
- * @param { E } event
177
- * @param { { error: (error: Error | unknown) => void | Promise<void>, exit: (code: number) => void | Promise<void>, sig: (signal: "SIGINT" | "SIGTERM" | "SIGQUIT") => void | Promise<void>, beforeExit: (code: number) => void|Promise<void> }[E] } handler
178
- */
179
- on(event, handler) {
180
- if (event === "error") {
181
- process.removeAllListeners("uncaughtException");
182
- process.removeAllListeners("unhandledRejection");
183
- process.on("uncaughtException", handler ?? defaults.error);
184
- process.on("unhandledRejection", handler ?? defaults.error);
185
- } else if (event === "exit") {
186
- process.removeAllListeners("exit");
187
- process.on("exit", handler ?? defaults.exit);
188
- } else if (event === "sig") {
189
- for (const signal of ["SIGINT", "SIGTERM", "SIGQUIT"]) {
190
- process.removeAllListeners(signal);
191
- process.on(signal, handler ?? defaults.sig);
192
- }
193
- } else if (event === "beforeExit") {
194
- process.removeAllListeners("beforeExit");
195
- process.on("beforeExit", handler ?? defaults.beforeExit);
196
- }
197
-
198
- return _;
199
- },
200
- };
201
-
202
- _.on("error").on("exit").on("sig");
203
-
204
- return _;
205
- },
206
- };
@@ -1,25 +0,0 @@
1
- export class cli_stream {
2
- constructor(command: any, args: any);
3
- /**
4
- * @typedef {"error"|"data"|"stopped"} EventName
5
- * @typedef {{
6
- * error: (error: Error) => void|Promise<void>,
7
- * data: (message: string) => void|Promise<void>,
8
- * stopped: (code: number, signal: keyof import("node:child_process").ChildProcessEventMap) => void|Promise<void>,
9
- * }} EventHandler
10
- */
11
- /**
12
- * @template {EventName} E
13
- * @param {E} event
14
- * @param {EventHandler[E]} handler
15
- */
16
- on<E extends "data" | "error" | "stopped">(event: E, handler: {
17
- error: (error: Error) => void | Promise<void>;
18
- data: (message: string) => void | Promise<void>;
19
- stopped: (code: number, signal: keyof import("node:child_process").ChildProcessEventMap) => void | Promise<void>;
20
- }[E]): this;
21
- stop(): Promise<any>;
22
- #private;
23
- }
24
- export declare function cli_run(command: string): NonSharedBuffer & string;
25
- //# sourceMappingURL=CLI.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"CLI.d.ts","sourceRoot":"","sources":["../../core/CLI.js"],"names":[],"mappings":"AAGA;IAIE,qCAqBC;IAED;;;;;;;SAOE;IACF;;;;SAIE;IACF,GAJwB,CAAC,SAAb,4BAAW,SACb,CAAC;eAPF,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,GAAC,OAAO,CAAC,IAAI,CAAC;cACrC,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,GAAC,OAAO,CAAC,IAAI,CAAC;iBACpC,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,OAAO,oBAAoB,EAAE,oBAAoB,KAAK,IAAI,GAAC,OAAO,CAAC,IAAI,CAAC;gBAW/G;IAED,qBA4BC;;CACF;AAUU,yCAFC,MAAM,4BAOf"}
@@ -1,73 +0,0 @@
1
- /**
2
- * Checks if the value is an array.
3
- * @param {*} value - The value to check.
4
- * @returns {boolean} Returns `true` if the value is an array, otherwise `false`.
5
- */
6
- export function isArray(value: any): boolean;
7
- /**
8
- * Checks if the value is a valid number (not NaN).
9
- * @param {*} value - The value to check.
10
- * @returns {boolean} Returns `true` if the value is a valid number, otherwise `false`.
11
- */
12
- export function isNumber(value: any): boolean;
13
- /**
14
- * Checks if the value is a function.
15
- * @param {*} value - The value to check.
16
- * @returns {boolean} Returns `true` if the value is a function, otherwise `false`.
17
- */
18
- export function isFunction(value: any): boolean;
19
- /**
20
- * Checks if the value is an object (and not `null`).
21
- * @param {*} value - The value to check.
22
- * @returns {boolean} Returns `true` if the value is a non-null object, otherwise `false`.
23
- */
24
- export function isObject(value: any): boolean;
25
- /**
26
- * Checks if the value is a boolean.
27
- * @param {*} value - The value to check.
28
- * @returns {boolean} Returns `true` if the value is a boolean, otherwise `false`.
29
- */
30
- export function isBoolean(value: any): boolean;
31
- /**
32
- * Checks if the value is a string.
33
- * @param {*} value - The value to check.
34
- * @returns {boolean} Returns `true` if the value is a string, otherwise `false`.
35
- */
36
- export function isString(value: any): boolean;
37
- /**
38
- * Checks if the value is `undefined`.
39
- * @param {*} value - The value to check.
40
- * @returns {boolean} Returns `true` if the value is `undefined`, otherwise `false`.
41
- */
42
- export function isUndefined(value: any): boolean;
43
- /**
44
- * Checks if the value is `null`.
45
- * @param {*} value - The value to check.
46
- * @returns {boolean} Returns `true` if the value is `null`, otherwise `false`.
47
- */
48
- export function isNull(value: any): boolean;
49
- /**
50
- * Checks if the value is a valid `Date` object.
51
- * @param {*} value - The value to check.
52
- * @returns {boolean} Returns `true` if the value is an instance of `Date`, otherwise `false`.
53
- */
54
- export function isDate(value: any): boolean;
55
- /**
56
- * Checks if the value is a `Map` instance.
57
- * @param {*} value - The value to check.
58
- * @returns {boolean} Returns `true` if the value is a Map, otherwise `false`.
59
- */
60
- export function isMap(value: any): boolean;
61
- /**
62
- * Checks if the value is a `Set` instance.
63
- * @param {*} value - The value to check.
64
- * @returns {boolean} Returns `true` if the value is a Set, otherwise `false`.
65
- */
66
- export function isSet(value: any): boolean;
67
- /**
68
- * Checks if the value is a native JavaScript error (`Error`, `TypeError`, etc.).
69
- * @param {*} value - The value to check.
70
- * @returns {boolean} Returns `true` if the value is a native error, otherwise `false`.
71
- */
72
- export function isError(value: any): boolean;
73
- //# sourceMappingURL=Checker.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"Checker.d.ts","sourceRoot":"","sources":["../../core/Checker.js"],"names":[],"mappings":"AAmBE;;;;KAIE;AACF,+BAHU,GAAC,GACC,OAAO,CAIlB;AAED;;;;KAIE;AACF,gCAHU,GAAC,GACC,OAAO,CAIlB;AAED;;;;KAIE;AACF,kCAHU,GAAC,GACC,OAAO,CAIlB;AAED;;;;KAIE;AACF,gCAHU,GAAC,GACC,OAAO,CAIlB;AAED;;;;KAIE;AACF,iCAHU,GAAC,GACC,OAAO,CAIlB;AAED;;;;KAIE;AACF,gCAHU,GAAC,GACC,OAAO,CAIlB;AAED;;;;KAIE;AACF,mCAHU,GAAC,GACC,OAAO,CAIlB;AAED;;;;KAIE;AACF,8BAHU,GAAC,GACC,OAAO,CAIlB;AAED;;;;KAIE;AACF,8BAHU,GAAC,GACC,OAAO,CAIlB;AAED;;;;KAIE;AACF,6BAHU,GAAC,GACC,OAAO,CAIlB;AAED;;;;KAIE;AACF,6BAHU,GAAC,GACC,OAAO,CAIlB;AAED;;;;KAIE;AACF,+BAHU,GAAC,GACC,OAAO,CAIlB"}