@nexusbloom/cli 0.9.7 → 0.9.9
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/package.json +1 -1
- package/src/pipe.js +16 -2
- package/src/values.js +31 -4
package/package.json
CHANGED
package/src/pipe.js
CHANGED
|
@@ -295,9 +295,23 @@ function splitSteps(text) {
|
|
|
295
295
|
return chunks;
|
|
296
296
|
}
|
|
297
297
|
|
|
298
|
+
/**
|
|
299
|
+
* Is this pipe the start of a transform chain rather than the next step?
|
|
300
|
+
*
|
|
301
|
+
* The word after the pipe has to be a *whole* transform name, optionally
|
|
302
|
+
* followed by an argument list. Without the terminator check, a tool whose slug
|
|
303
|
+
* merely starts with a transform name — `base64-tool`, `split-words` — was read
|
|
304
|
+
* as the transform `base64` plus a stray slug, and the pipeline failed to parse
|
|
305
|
+
* with a confusing "Expected key=value" pointing at the pipe.
|
|
306
|
+
*/
|
|
298
307
|
function isKnownTransformWord(rest) {
|
|
299
|
-
const
|
|
300
|
-
|
|
308
|
+
const m = /^\s*([a-zA-Z_][a-zA-Z0-9_]*)/.exec(rest);
|
|
309
|
+
if (!m) return false;
|
|
310
|
+
if (!Object.hasOwn(TRANSFORMS, m[1])) return false;
|
|
311
|
+
// Whatever follows the name decides it: `len()`, `len` at the end of a step,
|
|
312
|
+
// then whitespace, or a pipe. A hyphen or dot means this is a slug.
|
|
313
|
+
const after = rest.slice(m.index + m[1].length);
|
|
314
|
+
return after === "" || /^\s*[(\s|]/.test(after);
|
|
301
315
|
}
|
|
302
316
|
|
|
303
317
|
/**
|
package/src/values.js
CHANGED
|
@@ -274,8 +274,22 @@ export const TRANSFORMS = {
|
|
|
274
274
|
const nums = v.filter((n) => typeof n === "number");
|
|
275
275
|
return nums.length ? nums.reduce((a, b) => a + b, 0) / nums.length : MISSING;
|
|
276
276
|
},
|
|
277
|
-
|
|
278
|
-
|
|
277
|
+
// These collapse an *array*; they do not compare against a bound. `clamp(lo,hi)`
|
|
278
|
+
// is the one that takes bounds. Ignoring a second argument silently was the
|
|
279
|
+
// reason `min(120, 60)` returned 120 instead of erroring, so it now returns the
|
|
280
|
+
// value untouched rather than pretending to have clamped anything.
|
|
281
|
+
min: (v, ...ignored) =>
|
|
282
|
+
ignored.length
|
|
283
|
+
? v
|
|
284
|
+
: Array.isArray(v) && v.length
|
|
285
|
+
? Math.min(...v.filter((n) => typeof n === "number"))
|
|
286
|
+
: v,
|
|
287
|
+
max: (v, ...ignored) =>
|
|
288
|
+
ignored.length
|
|
289
|
+
? v
|
|
290
|
+
: Array.isArray(v) && v.length
|
|
291
|
+
? Math.max(...v.filter((n) => typeof n === "number"))
|
|
292
|
+
: v,
|
|
279
293
|
// Sum one field across an array of objects — the common "total of the column".
|
|
280
294
|
sumBy: (v, key) => {
|
|
281
295
|
if (!Array.isArray(v)) return v;
|
|
@@ -319,6 +333,11 @@ export const TRANSFORMS = {
|
|
|
319
333
|
(Array.isArray(v) && v.length === 0) ||
|
|
320
334
|
(typeof v === "object" && !Array.isArray(v) && Object.keys(v).length === 0),
|
|
321
335
|
|
|
336
|
+
// Builds one string out of several values. Without this a report can only ever
|
|
337
|
+
// be as long as whatever single tool produced it: `join` needs an array that
|
|
338
|
+
// already exists, and there is no way to place an upstream value inside text.
|
|
339
|
+
concat: (v, ...parts) => [v, ...parts].map((p) => (p === MISSING || p === undefined || p === null ? "" : String(p))).join(""),
|
|
340
|
+
|
|
322
341
|
// — branching —
|
|
323
342
|
//
|
|
324
343
|
// `then(cond, whenTrue, whenFalse)` is what a linear pipeline was missing: pick
|
|
@@ -471,6 +490,11 @@ export const TRANSFORM_ARITY = {
|
|
|
471
490
|
// understate it, so the builder asks for one and a written pipeline may list
|
|
472
491
|
// as many as it likes.
|
|
473
492
|
coalesce: 2,
|
|
493
|
+
// Arity is the *minimum* argument count the parser enforces, so a variadic
|
|
494
|
+
// transform declares 1 — `concat()` with nothing after it is legal and just
|
|
495
|
+
// stringifies the piped value. `coalesce` above keeps its 2 because a coalesce
|
|
496
|
+
// with no fallback does nothing.
|
|
497
|
+
concat: 1,
|
|
474
498
|
pluck: 2, chunk: 2, without: 2, where: 4,
|
|
475
499
|
truncate: 3, repeat: 2, mask: 2,
|
|
476
500
|
};
|
|
@@ -535,8 +559,8 @@ export const TRANSFORM_DOCS = {
|
|
|
535
559
|
clamp: "keep a number between a low and a high",
|
|
536
560
|
sum: "add up every number in an array",
|
|
537
561
|
avg: "average of every number in an array",
|
|
538
|
-
min: "smallest number in an array",
|
|
539
|
-
max: "largest number in an array",
|
|
562
|
+
min: "smallest number in an array (to bound a single number use clamp)",
|
|
563
|
+
max: "largest number in an array (to bound a single number use clamp)",
|
|
540
564
|
sumBy: "add up one field across an array of objects",
|
|
541
565
|
eq: "is it equal to a value",
|
|
542
566
|
ne: "is it different from a value",
|
|
@@ -551,6 +575,7 @@ export const TRANSFORM_DOCS = {
|
|
|
551
575
|
matches: "does a string match a regular expression",
|
|
552
576
|
isEmpty: "is the value empty — blank, [], or {}",
|
|
553
577
|
then: "choose between two values on a condition",
|
|
578
|
+
concat: "combine several values into one string",
|
|
554
579
|
coalesce: "first value that is not empty",
|
|
555
580
|
pluck: "pull one field out of every object in an array",
|
|
556
581
|
unique: "remove duplicates from an array",
|
|
@@ -607,6 +632,8 @@ export const TRANSFORM_GROUPS = [
|
|
|
607
632
|
names: ["json", "unjson"] },
|
|
608
633
|
{ label: "default(v) / coalesce(a,b,…) / then(cond,a,b)", doc: "supply a value when one is missing, or choose on a condition",
|
|
609
634
|
names: ["default", "coalesce", "then"] },
|
|
635
|
+
{ label: "concat(a,b,…)", doc: "combine several values into one string",
|
|
636
|
+
names: ["concat"] },
|
|
610
637
|
{ label: "eq / ne / gt / gte / lt / lte / between(a,b)", doc: "compare — the result is true or false",
|
|
611
638
|
names: ["eq", "ne", "gt", "gte", "lt", "lte", "between"] },
|
|
612
639
|
{ label: "includes / startsWith / endsWith / matches(re) / isEmpty", doc: "test text and arrays",
|