@nmakarov/cli-toolkit 0.25.0 → 0.29.0
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/cli-runner.cjs +118 -36
- package/dist/cli-runner.cjs.map +1 -1
- package/dist/cli-runner.js +118 -36
- package/dist/cli-runner.js.map +1 -1
- package/dist/index.cjs +114 -36
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +114 -36
- package/dist/index.js.map +1 -1
- package/dist/init.cjs +94 -31
- package/dist/init.cjs.map +1 -1
- package/dist/init.js +94 -31
- package/dist/init.js.map +1 -1
- package/dist/params.cjs +69 -28
- package/dist/params.cjs.map +1 -1
- package/dist/params.js +69 -28
- package/dist/params.js.map +1 -1
- package/dist/s3.cjs +1 -0
- package/dist/s3.cjs.map +1 -1
- package/dist/s3.js +1 -0
- package/dist/s3.js.map +1 -1
- package/dist/tasks.cjs +25 -7
- package/dist/tasks.cjs.map +1 -1
- package/dist/tasks.js +25 -7
- package/dist/tasks.js.map +1 -1
- package/package.json +1 -1
package/dist/cli-runner.cjs
CHANGED
|
@@ -1758,46 +1758,87 @@ var Params = class _Params {
|
|
|
1758
1758
|
paramGetters = [];
|
|
1759
1759
|
trackedParams = [];
|
|
1760
1760
|
_currentModule = "script";
|
|
1761
|
-
/**
|
|
1762
|
-
|
|
1761
|
+
/**
|
|
1762
|
+
* Resolved early in constructor so cleanup does not read params lazily.
|
|
1763
|
+
* One of: false (off) | "end" (print at exit) | "top" (print after init,
|
|
1764
|
+
* via context.showUsedParamsIfNeeded()) | "stop" (print after init, then
|
|
1765
|
+
* exit the process — also via context.showUsedParamsIfNeeded()).
|
|
1766
|
+
*/
|
|
1767
|
+
_showUsedParamsMode = false;
|
|
1768
|
+
/** Guard so the dump prints at most once (top OR end, never both). */
|
|
1769
|
+
_usedParamsPrinted = false;
|
|
1763
1770
|
constructor(context, options = {}) {
|
|
1764
1771
|
this.context = context;
|
|
1765
1772
|
this.args = context.args;
|
|
1766
1773
|
if (Object.keys(options).length > 0) {
|
|
1767
1774
|
this.configure(options);
|
|
1768
1775
|
}
|
|
1769
|
-
this.
|
|
1776
|
+
this._resolveShowUsedParams();
|
|
1770
1777
|
if (context && typeof context.registerCleanup === "function") {
|
|
1771
1778
|
context.registerCleanup((ctx) => {
|
|
1772
|
-
if (!ctx.params.
|
|
1773
|
-
|
|
1774
|
-
const modules = Object.keys(byModule).sort();
|
|
1775
|
-
if (modules.length === 0) return;
|
|
1776
|
-
const logger = ctx.logger;
|
|
1777
|
-
logger.debug("[Params]: list of used params:");
|
|
1778
|
-
if (typeof logger.highlight !== "function") {
|
|
1779
|
-
for (const mod of modules) {
|
|
1780
|
-
logger.debug(` [${mod}]`);
|
|
1781
|
-
for (const [key, entry] of Object.entries(byModule[mod])) {
|
|
1782
|
-
logger.debug(` ${key}: ${JSON.stringify(entry.value)} (${entry.source})`);
|
|
1783
|
-
}
|
|
1784
|
-
}
|
|
1785
|
-
return;
|
|
1786
|
-
}
|
|
1787
|
-
for (const mod of modules) {
|
|
1788
|
-
logger.debug(` [${mod}]`);
|
|
1789
|
-
for (const [key, entry] of Object.entries(byModule[mod])) {
|
|
1790
|
-
const valueStr = JSON.stringify(entry.value);
|
|
1791
|
-
const display = entry.source === "default" ? valueStr : logger.highlight(valueStr);
|
|
1792
|
-
logger.debug(` ${key}: ${display} (${entry.source})`);
|
|
1793
|
-
}
|
|
1794
|
-
}
|
|
1779
|
+
if (!ctx.params.getShowUsedParamsMode()) return;
|
|
1780
|
+
ctx.params.printUsedParams(ctx.logger);
|
|
1795
1781
|
});
|
|
1796
1782
|
}
|
|
1797
1783
|
}
|
|
1798
|
-
/**
|
|
1784
|
+
/**
|
|
1785
|
+
* Resolve the --showUsedParams mode. The flag is intentionally dual-typed:
|
|
1786
|
+
* (absent) / --no-showUsedParams / =false -> false (off)
|
|
1787
|
+
* --showUsedParams / =true -> "end" (print at exit)
|
|
1788
|
+
* --showUsedParams=top -> "top" (print after init)
|
|
1789
|
+
* --showUsedParams=stop -> "stop" (print after init, then exit)
|
|
1790
|
+
* Read raw (uncoerced) from args so the string "top"/"stop" isn't forced to
|
|
1791
|
+
* a boolean, then track it under the "script" module for the dump itself.
|
|
1792
|
+
*/
|
|
1793
|
+
_resolveShowUsedParams() {
|
|
1794
|
+
const raw = this.args.get("showUsedParams");
|
|
1795
|
+
const source = this.args.getSource?.("showUsedParams") ?? "default";
|
|
1796
|
+
let mode = false;
|
|
1797
|
+
if (raw === void 0 || raw === null) {
|
|
1798
|
+
mode = false;
|
|
1799
|
+
} else if (typeof raw === "string" && raw.trim().toLowerCase() === "top") {
|
|
1800
|
+
mode = "top";
|
|
1801
|
+
} else if (typeof raw === "string" && raw.trim().toLowerCase() === "stop") {
|
|
1802
|
+
mode = "stop";
|
|
1803
|
+
} else {
|
|
1804
|
+
const s = typeof raw === "string" ? raw.trim().toLowerCase() : raw;
|
|
1805
|
+
const falsey = s === false || s === "false" || s === "0" || s === "no" || s === "off";
|
|
1806
|
+
mode = falsey ? false : "end";
|
|
1807
|
+
}
|
|
1808
|
+
this._showUsedParamsMode = mode;
|
|
1809
|
+
this.trackParam("showUsedParams", "string", mode, raw === void 0 ? "default" : source, "script");
|
|
1810
|
+
return mode;
|
|
1811
|
+
}
|
|
1812
|
+
/** Whether --showUsedParams was requested in any mode (truthy = on). */
|
|
1799
1813
|
getShowUsedParams() {
|
|
1800
|
-
return this.
|
|
1814
|
+
return this._showUsedParamsMode !== false;
|
|
1815
|
+
}
|
|
1816
|
+
/** Resolved mode: false | "end" | "top" | "stop". */
|
|
1817
|
+
getShowUsedParamsMode() {
|
|
1818
|
+
return this._showUsedParamsMode;
|
|
1819
|
+
}
|
|
1820
|
+
/**
|
|
1821
|
+
* Print the module-grouped list of figured params (the --showUsedParams
|
|
1822
|
+
* dump). Idempotent: only the first call prints, so callers can invoke it
|
|
1823
|
+
* at the top (long-running services) without double-printing at exit.
|
|
1824
|
+
*/
|
|
1825
|
+
printUsedParams(logger) {
|
|
1826
|
+
if (this._usedParamsPrinted) return;
|
|
1827
|
+
const byModule = this.getFiguredByModule();
|
|
1828
|
+
const modules = Object.keys(byModule).sort();
|
|
1829
|
+
if (modules.length === 0) return;
|
|
1830
|
+
this._usedParamsPrinted = true;
|
|
1831
|
+
logger = logger ?? this.context?.logger ?? console;
|
|
1832
|
+
const hasHighlight = typeof logger.highlight === "function";
|
|
1833
|
+
logger.debug("[Params]: list of used params:");
|
|
1834
|
+
for (const mod of modules) {
|
|
1835
|
+
logger.debug(` [${mod}]`);
|
|
1836
|
+
for (const [key, entry] of Object.entries(byModule[mod])) {
|
|
1837
|
+
const valueStr = JSON.stringify(entry.value);
|
|
1838
|
+
const display = hasHighlight && entry.source !== "default" ? logger.highlight(valueStr) : valueStr;
|
|
1839
|
+
logger.debug(` ${key}: ${display} (${entry.source})`);
|
|
1840
|
+
}
|
|
1841
|
+
}
|
|
1801
1842
|
}
|
|
1802
1843
|
/**
|
|
1803
1844
|
* Configure parameters
|
|
@@ -2482,7 +2523,25 @@ function setup(opts = {}) {
|
|
|
2482
2523
|
emitter: partialContext.emitter,
|
|
2483
2524
|
isStop: partialContext.isStop,
|
|
2484
2525
|
cleanupFunctions: partialContext.cleanupFunctions,
|
|
2485
|
-
registerCleanup: partialContext.registerCleanup
|
|
2526
|
+
registerCleanup: partialContext.registerCleanup,
|
|
2527
|
+
// For long-running scripts (servers): with --showUsedParams=top, print
|
|
2528
|
+
// the used-params list now (after the script has initialized all its
|
|
2529
|
+
// own components), instead of at exit. No-op for the default mode,
|
|
2530
|
+
// which prints at exit via the cleanup registered by Params.
|
|
2531
|
+
//
|
|
2532
|
+
// --showUsedParams=stop behaves like "top" but then exits immediately —
|
|
2533
|
+
// a quick "show me the figured params and quit" that skips the flow's
|
|
2534
|
+
// actual work. Like --stopAfter=init, this is a hard exit(0) (registered
|
|
2535
|
+
// cleanups are skipped); call it once components/params are resolved.
|
|
2536
|
+
showUsedParamsIfNeeded: () => {
|
|
2537
|
+
const mode = params.getShowUsedParamsMode?.();
|
|
2538
|
+
if (mode !== "top" && mode !== "stop") return;
|
|
2539
|
+
params.printUsedParams(logger);
|
|
2540
|
+
if (mode === "stop") {
|
|
2541
|
+
logger.debug?.("[showUsedParams=stop] params printed \u2014 exiting");
|
|
2542
|
+
process.exit(0);
|
|
2543
|
+
}
|
|
2544
|
+
}
|
|
2486
2545
|
};
|
|
2487
2546
|
logger.debug("[setup] completed successfully");
|
|
2488
2547
|
return context;
|
|
@@ -2549,15 +2608,19 @@ async function init(flow2, opts = {}) {
|
|
|
2549
2608
|
process.exit(0);
|
|
2550
2609
|
}
|
|
2551
2610
|
let sigintCount = 0;
|
|
2611
|
+
let firstSigintAt = 0;
|
|
2552
2612
|
process.on("SIGINT", async () => {
|
|
2553
2613
|
if (!context) return;
|
|
2554
|
-
|
|
2555
|
-
if (sigintCount ===
|
|
2614
|
+
const now = Date.now();
|
|
2615
|
+
if (sigintCount === 0) {
|
|
2616
|
+
sigintCount = 1;
|
|
2617
|
+
firstSigintAt = now;
|
|
2556
2618
|
stop = true;
|
|
2557
2619
|
context.logger.info(`>> emitting stop with allowance ${stopAllowance}`);
|
|
2558
2620
|
context.emitter.emit("stop", stopAllowance);
|
|
2559
2621
|
return;
|
|
2560
2622
|
}
|
|
2623
|
+
if (now - firstSigintAt < 250) return;
|
|
2561
2624
|
context.logger.warn("[process] second SIGINT: running cleanup then exit");
|
|
2562
2625
|
await runRegisteredCleanups(context);
|
|
2563
2626
|
process.exit(2);
|
|
@@ -3195,7 +3258,7 @@ function defineTasksTable(t, db, tableNameForIndex) {
|
|
|
3195
3258
|
t.timestamp("past_due").defaultTo(null);
|
|
3196
3259
|
t.text("name").notNullable();
|
|
3197
3260
|
t.text("opid");
|
|
3198
|
-
t.
|
|
3261
|
+
t.jsonb("params");
|
|
3199
3262
|
t.text("service_group");
|
|
3200
3263
|
t.integer("instance_number");
|
|
3201
3264
|
t.text("service_name");
|
|
@@ -3204,7 +3267,7 @@ function defineTasksTable(t, db, tableNameForIndex) {
|
|
|
3204
3267
|
t.timestamp("status_changed_at").defaultTo(null);
|
|
3205
3268
|
t.text("progress");
|
|
3206
3269
|
t.boolean("success");
|
|
3207
|
-
t.
|
|
3270
|
+
t.jsonb("results");
|
|
3208
3271
|
t.index(["service_group", "status", "priority", "created_at"], `${tableNameForIndex}_claim_idx`);
|
|
3209
3272
|
t.index(["service_group", "name"], `${tableNameForIndex}_group_name_idx`);
|
|
3210
3273
|
}
|
|
@@ -3219,11 +3282,29 @@ function taskHistoryInsertFromQueueRow(row, overrides) {
|
|
|
3219
3282
|
async function ensureTaskTables(context, options = {}) {
|
|
3220
3283
|
const queueName = options.queueName ?? "tasks";
|
|
3221
3284
|
const recreate = options.recreate ?? false;
|
|
3285
|
+
const dryRun = options.dryRun ?? false;
|
|
3222
3286
|
const db = getDb(context);
|
|
3287
|
+
const log = context.logger ?? console;
|
|
3223
3288
|
const { tasksTable, historyTable, registryTable } = queueToTableNames(queueName);
|
|
3224
3289
|
const needsTasks = recreate ? true : !await db.tableExists(tasksTable);
|
|
3225
3290
|
const needsHistory = recreate ? true : !await db.tableExists(historyTable);
|
|
3226
3291
|
const needsRegistry = recreate ? true : !await db.tableExists(registryTable);
|
|
3292
|
+
if (dryRun) {
|
|
3293
|
+
const plan = [];
|
|
3294
|
+
if (recreate) {
|
|
3295
|
+
plan.push(`DROP TABLE IF EXISTS ${historyTable}, ${tasksTable}, ${registryTable}`);
|
|
3296
|
+
}
|
|
3297
|
+
if (needsTasks) plan.push(`CREATE TABLE ${tasksTable} (tasks queue)`);
|
|
3298
|
+
if (needsHistory) plan.push(`CREATE TABLE ${historyTable} (history mirror)`);
|
|
3299
|
+
if (needsRegistry) plan.push(`CREATE TABLE ${registryTable} (services registry)`);
|
|
3300
|
+
if (plan.length === 0) {
|
|
3301
|
+
log.info?.(`[tasks-schema] dryRun \u2014 queue "${queueName}" already up to date; no DDL`);
|
|
3302
|
+
} else {
|
|
3303
|
+
log.info?.(`[tasks-schema] dryRun \u2014 would run ${plan.length} statement(s) for queue "${queueName}":`);
|
|
3304
|
+
for (const s of plan) log.info?.(` - ${s}`);
|
|
3305
|
+
}
|
|
3306
|
+
return;
|
|
3307
|
+
}
|
|
3227
3308
|
if (recreate) {
|
|
3228
3309
|
await db.schema.dropTableIfExists(historyTable);
|
|
3229
3310
|
await db.schema.dropTableIfExists(tasksTable);
|
|
@@ -5646,9 +5727,8 @@ async function executeClaimedTask(context, tasksTable, historyTable, row, regist
|
|
|
5646
5727
|
const dbName = String(context?.params?.get?.("dbName") || "local");
|
|
5647
5728
|
const tableName = String(context?.params?.get?.("table") || "tasks");
|
|
5648
5729
|
const fallbackRecoverCommand = [
|
|
5649
|
-
"
|
|
5650
|
-
"
|
|
5651
|
-
"examples/tasks/recover-task.ts",
|
|
5730
|
+
"node",
|
|
5731
|
+
"examples/tasks/recover-task.js",
|
|
5652
5732
|
`--dbName='${dbName.replace(/'/g, `'\\''`)}'`,
|
|
5653
5733
|
`--table='${tableName.replace(/'/g, `'\\''`)}'`,
|
|
5654
5734
|
`--id='${String(row.id).replace(/'/g, `'\\''`)}'`
|
|
@@ -5714,6 +5794,8 @@ async function claimNextRunnableTask(context, tasksTable, serviceGroup, registry
|
|
|
5714
5794
|
const db = getDb3(context);
|
|
5715
5795
|
let query = db(tasksTable).where({ status: "idle" }).where(function() {
|
|
5716
5796
|
this.whereNull("service_group").orWhere({ service_group: serviceGroup });
|
|
5797
|
+
}).where(function() {
|
|
5798
|
+
this.whereNull("next_run_at").orWhere("next_run_at", "<=", db.fn.now());
|
|
5717
5799
|
}).orderByRaw("CASE WHEN past_due IS NULL THEN 1 ELSE 0 END ASC").orderBy([{ column: "priority", order: "asc" }]).orderByRaw("CASE WHEN completed_at IS NULL THEN 0 ELSE 1 END ASC").orderBy([{ column: "completed_at", order: "asc" }, { column: "created_at", order: "asc" }]).limit(scanLimit);
|
|
5718
5800
|
if (taskNames && taskNames.length > 0) {
|
|
5719
5801
|
query = query.whereIn("name", taskNames);
|