@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.js
CHANGED
|
@@ -1742,46 +1742,87 @@ var Params = class _Params {
|
|
|
1742
1742
|
paramGetters = [];
|
|
1743
1743
|
trackedParams = [];
|
|
1744
1744
|
_currentModule = "script";
|
|
1745
|
-
/**
|
|
1746
|
-
|
|
1745
|
+
/**
|
|
1746
|
+
* Resolved early in constructor so cleanup does not read params lazily.
|
|
1747
|
+
* One of: false (off) | "end" (print at exit) | "top" (print after init,
|
|
1748
|
+
* via context.showUsedParamsIfNeeded()) | "stop" (print after init, then
|
|
1749
|
+
* exit the process — also via context.showUsedParamsIfNeeded()).
|
|
1750
|
+
*/
|
|
1751
|
+
_showUsedParamsMode = false;
|
|
1752
|
+
/** Guard so the dump prints at most once (top OR end, never both). */
|
|
1753
|
+
_usedParamsPrinted = false;
|
|
1747
1754
|
constructor(context, options = {}) {
|
|
1748
1755
|
this.context = context;
|
|
1749
1756
|
this.args = context.args;
|
|
1750
1757
|
if (Object.keys(options).length > 0) {
|
|
1751
1758
|
this.configure(options);
|
|
1752
1759
|
}
|
|
1753
|
-
this.
|
|
1760
|
+
this._resolveShowUsedParams();
|
|
1754
1761
|
if (context && typeof context.registerCleanup === "function") {
|
|
1755
1762
|
context.registerCleanup((ctx) => {
|
|
1756
|
-
if (!ctx.params.
|
|
1757
|
-
|
|
1758
|
-
const modules = Object.keys(byModule).sort();
|
|
1759
|
-
if (modules.length === 0) return;
|
|
1760
|
-
const logger = ctx.logger;
|
|
1761
|
-
logger.debug("[Params]: list of used params:");
|
|
1762
|
-
if (typeof logger.highlight !== "function") {
|
|
1763
|
-
for (const mod of modules) {
|
|
1764
|
-
logger.debug(` [${mod}]`);
|
|
1765
|
-
for (const [key, entry] of Object.entries(byModule[mod])) {
|
|
1766
|
-
logger.debug(` ${key}: ${JSON.stringify(entry.value)} (${entry.source})`);
|
|
1767
|
-
}
|
|
1768
|
-
}
|
|
1769
|
-
return;
|
|
1770
|
-
}
|
|
1771
|
-
for (const mod of modules) {
|
|
1772
|
-
logger.debug(` [${mod}]`);
|
|
1773
|
-
for (const [key, entry] of Object.entries(byModule[mod])) {
|
|
1774
|
-
const valueStr = JSON.stringify(entry.value);
|
|
1775
|
-
const display = entry.source === "default" ? valueStr : logger.highlight(valueStr);
|
|
1776
|
-
logger.debug(` ${key}: ${display} (${entry.source})`);
|
|
1777
|
-
}
|
|
1778
|
-
}
|
|
1763
|
+
if (!ctx.params.getShowUsedParamsMode()) return;
|
|
1764
|
+
ctx.params.printUsedParams(ctx.logger);
|
|
1779
1765
|
});
|
|
1780
1766
|
}
|
|
1781
1767
|
}
|
|
1782
|
-
/**
|
|
1768
|
+
/**
|
|
1769
|
+
* Resolve the --showUsedParams mode. The flag is intentionally dual-typed:
|
|
1770
|
+
* (absent) / --no-showUsedParams / =false -> false (off)
|
|
1771
|
+
* --showUsedParams / =true -> "end" (print at exit)
|
|
1772
|
+
* --showUsedParams=top -> "top" (print after init)
|
|
1773
|
+
* --showUsedParams=stop -> "stop" (print after init, then exit)
|
|
1774
|
+
* Read raw (uncoerced) from args so the string "top"/"stop" isn't forced to
|
|
1775
|
+
* a boolean, then track it under the "script" module for the dump itself.
|
|
1776
|
+
*/
|
|
1777
|
+
_resolveShowUsedParams() {
|
|
1778
|
+
const raw = this.args.get("showUsedParams");
|
|
1779
|
+
const source = this.args.getSource?.("showUsedParams") ?? "default";
|
|
1780
|
+
let mode = false;
|
|
1781
|
+
if (raw === void 0 || raw === null) {
|
|
1782
|
+
mode = false;
|
|
1783
|
+
} else if (typeof raw === "string" && raw.trim().toLowerCase() === "top") {
|
|
1784
|
+
mode = "top";
|
|
1785
|
+
} else if (typeof raw === "string" && raw.trim().toLowerCase() === "stop") {
|
|
1786
|
+
mode = "stop";
|
|
1787
|
+
} else {
|
|
1788
|
+
const s = typeof raw === "string" ? raw.trim().toLowerCase() : raw;
|
|
1789
|
+
const falsey = s === false || s === "false" || s === "0" || s === "no" || s === "off";
|
|
1790
|
+
mode = falsey ? false : "end";
|
|
1791
|
+
}
|
|
1792
|
+
this._showUsedParamsMode = mode;
|
|
1793
|
+
this.trackParam("showUsedParams", "string", mode, raw === void 0 ? "default" : source, "script");
|
|
1794
|
+
return mode;
|
|
1795
|
+
}
|
|
1796
|
+
/** Whether --showUsedParams was requested in any mode (truthy = on). */
|
|
1783
1797
|
getShowUsedParams() {
|
|
1784
|
-
return this.
|
|
1798
|
+
return this._showUsedParamsMode !== false;
|
|
1799
|
+
}
|
|
1800
|
+
/** Resolved mode: false | "end" | "top" | "stop". */
|
|
1801
|
+
getShowUsedParamsMode() {
|
|
1802
|
+
return this._showUsedParamsMode;
|
|
1803
|
+
}
|
|
1804
|
+
/**
|
|
1805
|
+
* Print the module-grouped list of figured params (the --showUsedParams
|
|
1806
|
+
* dump). Idempotent: only the first call prints, so callers can invoke it
|
|
1807
|
+
* at the top (long-running services) without double-printing at exit.
|
|
1808
|
+
*/
|
|
1809
|
+
printUsedParams(logger) {
|
|
1810
|
+
if (this._usedParamsPrinted) return;
|
|
1811
|
+
const byModule = this.getFiguredByModule();
|
|
1812
|
+
const modules = Object.keys(byModule).sort();
|
|
1813
|
+
if (modules.length === 0) return;
|
|
1814
|
+
this._usedParamsPrinted = true;
|
|
1815
|
+
logger = logger ?? this.context?.logger ?? console;
|
|
1816
|
+
const hasHighlight = typeof logger.highlight === "function";
|
|
1817
|
+
logger.debug("[Params]: list of used params:");
|
|
1818
|
+
for (const mod of modules) {
|
|
1819
|
+
logger.debug(` [${mod}]`);
|
|
1820
|
+
for (const [key, entry] of Object.entries(byModule[mod])) {
|
|
1821
|
+
const valueStr = JSON.stringify(entry.value);
|
|
1822
|
+
const display = hasHighlight && entry.source !== "default" ? logger.highlight(valueStr) : valueStr;
|
|
1823
|
+
logger.debug(` ${key}: ${display} (${entry.source})`);
|
|
1824
|
+
}
|
|
1825
|
+
}
|
|
1785
1826
|
}
|
|
1786
1827
|
/**
|
|
1787
1828
|
* Configure parameters
|
|
@@ -2466,7 +2507,25 @@ function setup(opts = {}) {
|
|
|
2466
2507
|
emitter: partialContext.emitter,
|
|
2467
2508
|
isStop: partialContext.isStop,
|
|
2468
2509
|
cleanupFunctions: partialContext.cleanupFunctions,
|
|
2469
|
-
registerCleanup: partialContext.registerCleanup
|
|
2510
|
+
registerCleanup: partialContext.registerCleanup,
|
|
2511
|
+
// For long-running scripts (servers): with --showUsedParams=top, print
|
|
2512
|
+
// the used-params list now (after the script has initialized all its
|
|
2513
|
+
// own components), instead of at exit. No-op for the default mode,
|
|
2514
|
+
// which prints at exit via the cleanup registered by Params.
|
|
2515
|
+
//
|
|
2516
|
+
// --showUsedParams=stop behaves like "top" but then exits immediately —
|
|
2517
|
+
// a quick "show me the figured params and quit" that skips the flow's
|
|
2518
|
+
// actual work. Like --stopAfter=init, this is a hard exit(0) (registered
|
|
2519
|
+
// cleanups are skipped); call it once components/params are resolved.
|
|
2520
|
+
showUsedParamsIfNeeded: () => {
|
|
2521
|
+
const mode = params.getShowUsedParamsMode?.();
|
|
2522
|
+
if (mode !== "top" && mode !== "stop") return;
|
|
2523
|
+
params.printUsedParams(logger);
|
|
2524
|
+
if (mode === "stop") {
|
|
2525
|
+
logger.debug?.("[showUsedParams=stop] params printed \u2014 exiting");
|
|
2526
|
+
process.exit(0);
|
|
2527
|
+
}
|
|
2528
|
+
}
|
|
2470
2529
|
};
|
|
2471
2530
|
logger.debug("[setup] completed successfully");
|
|
2472
2531
|
return context;
|
|
@@ -2533,15 +2592,19 @@ async function init(flow2, opts = {}) {
|
|
|
2533
2592
|
process.exit(0);
|
|
2534
2593
|
}
|
|
2535
2594
|
let sigintCount = 0;
|
|
2595
|
+
let firstSigintAt = 0;
|
|
2536
2596
|
process.on("SIGINT", async () => {
|
|
2537
2597
|
if (!context) return;
|
|
2538
|
-
|
|
2539
|
-
if (sigintCount ===
|
|
2598
|
+
const now = Date.now();
|
|
2599
|
+
if (sigintCount === 0) {
|
|
2600
|
+
sigintCount = 1;
|
|
2601
|
+
firstSigintAt = now;
|
|
2540
2602
|
stop = true;
|
|
2541
2603
|
context.logger.info(`>> emitting stop with allowance ${stopAllowance}`);
|
|
2542
2604
|
context.emitter.emit("stop", stopAllowance);
|
|
2543
2605
|
return;
|
|
2544
2606
|
}
|
|
2607
|
+
if (now - firstSigintAt < 250) return;
|
|
2545
2608
|
context.logger.warn("[process] second SIGINT: running cleanup then exit");
|
|
2546
2609
|
await runRegisteredCleanups(context);
|
|
2547
2610
|
process.exit(2);
|
|
@@ -3179,7 +3242,7 @@ function defineTasksTable(t, db, tableNameForIndex) {
|
|
|
3179
3242
|
t.timestamp("past_due").defaultTo(null);
|
|
3180
3243
|
t.text("name").notNullable();
|
|
3181
3244
|
t.text("opid");
|
|
3182
|
-
t.
|
|
3245
|
+
t.jsonb("params");
|
|
3183
3246
|
t.text("service_group");
|
|
3184
3247
|
t.integer("instance_number");
|
|
3185
3248
|
t.text("service_name");
|
|
@@ -3188,7 +3251,7 @@ function defineTasksTable(t, db, tableNameForIndex) {
|
|
|
3188
3251
|
t.timestamp("status_changed_at").defaultTo(null);
|
|
3189
3252
|
t.text("progress");
|
|
3190
3253
|
t.boolean("success");
|
|
3191
|
-
t.
|
|
3254
|
+
t.jsonb("results");
|
|
3192
3255
|
t.index(["service_group", "status", "priority", "created_at"], `${tableNameForIndex}_claim_idx`);
|
|
3193
3256
|
t.index(["service_group", "name"], `${tableNameForIndex}_group_name_idx`);
|
|
3194
3257
|
}
|
|
@@ -3203,11 +3266,29 @@ function taskHistoryInsertFromQueueRow(row, overrides) {
|
|
|
3203
3266
|
async function ensureTaskTables(context, options = {}) {
|
|
3204
3267
|
const queueName = options.queueName ?? "tasks";
|
|
3205
3268
|
const recreate = options.recreate ?? false;
|
|
3269
|
+
const dryRun = options.dryRun ?? false;
|
|
3206
3270
|
const db = getDb(context);
|
|
3271
|
+
const log = context.logger ?? console;
|
|
3207
3272
|
const { tasksTable, historyTable, registryTable } = queueToTableNames(queueName);
|
|
3208
3273
|
const needsTasks = recreate ? true : !await db.tableExists(tasksTable);
|
|
3209
3274
|
const needsHistory = recreate ? true : !await db.tableExists(historyTable);
|
|
3210
3275
|
const needsRegistry = recreate ? true : !await db.tableExists(registryTable);
|
|
3276
|
+
if (dryRun) {
|
|
3277
|
+
const plan = [];
|
|
3278
|
+
if (recreate) {
|
|
3279
|
+
plan.push(`DROP TABLE IF EXISTS ${historyTable}, ${tasksTable}, ${registryTable}`);
|
|
3280
|
+
}
|
|
3281
|
+
if (needsTasks) plan.push(`CREATE TABLE ${tasksTable} (tasks queue)`);
|
|
3282
|
+
if (needsHistory) plan.push(`CREATE TABLE ${historyTable} (history mirror)`);
|
|
3283
|
+
if (needsRegistry) plan.push(`CREATE TABLE ${registryTable} (services registry)`);
|
|
3284
|
+
if (plan.length === 0) {
|
|
3285
|
+
log.info?.(`[tasks-schema] dryRun \u2014 queue "${queueName}" already up to date; no DDL`);
|
|
3286
|
+
} else {
|
|
3287
|
+
log.info?.(`[tasks-schema] dryRun \u2014 would run ${plan.length} statement(s) for queue "${queueName}":`);
|
|
3288
|
+
for (const s of plan) log.info?.(` - ${s}`);
|
|
3289
|
+
}
|
|
3290
|
+
return;
|
|
3291
|
+
}
|
|
3211
3292
|
if (recreate) {
|
|
3212
3293
|
await db.schema.dropTableIfExists(historyTable);
|
|
3213
3294
|
await db.schema.dropTableIfExists(tasksTable);
|
|
@@ -5630,9 +5711,8 @@ async function executeClaimedTask(context, tasksTable, historyTable, row, regist
|
|
|
5630
5711
|
const dbName = String(context?.params?.get?.("dbName") || "local");
|
|
5631
5712
|
const tableName = String(context?.params?.get?.("table") || "tasks");
|
|
5632
5713
|
const fallbackRecoverCommand = [
|
|
5633
|
-
"
|
|
5634
|
-
"
|
|
5635
|
-
"examples/tasks/recover-task.ts",
|
|
5714
|
+
"node",
|
|
5715
|
+
"examples/tasks/recover-task.js",
|
|
5636
5716
|
`--dbName='${dbName.replace(/'/g, `'\\''`)}'`,
|
|
5637
5717
|
`--table='${tableName.replace(/'/g, `'\\''`)}'`,
|
|
5638
5718
|
`--id='${String(row.id).replace(/'/g, `'\\''`)}'`
|
|
@@ -5698,6 +5778,8 @@ async function claimNextRunnableTask(context, tasksTable, serviceGroup, registry
|
|
|
5698
5778
|
const db = getDb3(context);
|
|
5699
5779
|
let query = db(tasksTable).where({ status: "idle" }).where(function() {
|
|
5700
5780
|
this.whereNull("service_group").orWhere({ service_group: serviceGroup });
|
|
5781
|
+
}).where(function() {
|
|
5782
|
+
this.whereNull("next_run_at").orWhere("next_run_at", "<=", db.fn.now());
|
|
5701
5783
|
}).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);
|
|
5702
5784
|
if (taskNames && taskNames.length > 0) {
|
|
5703
5785
|
query = query.whereIn("name", taskNames);
|