@mikitasazan/notify 1.4.3 → 1.4.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.
package/dist/cli-flags.js CHANGED
@@ -12,7 +12,8 @@ export const KNOWN_FLAGS = new Set([
12
12
  'action', 'actor', 'assignee', 'author', 'body', 'branch', 'commit',
13
13
  'id', 'opened', 'reason', 'workdir',
14
14
  'command', 'command-note', 'commit-body', 'commit-title', 'commit-url', 'detail',
15
- 'expected', 'filename', 'item', 'job', 'key', 'last-seen', 'line', 'logs', 'note',
15
+ 'expected', 'filename', 'item',
16
+ 'item-group', 'job', 'key', 'last-seen', 'line', 'logs', 'note',
16
17
  'number', 'path', 'period', 'project', 'reviewer', 'stat', 'status',
17
18
  'target', 'title', 'url', 'via', 'workflow-name', 'workflow-url',
18
19
  // Флаги без значения. Живут здесь же, чтобы разбор и список не разошлись.
package/dist/cli.js CHANGED
@@ -101,11 +101,22 @@ const num = (key) => {
101
101
  }
102
102
  return n;
103
103
  };
104
- // --item "текст" или --item "текст|https://ссылка"
105
- const items = () => (flags.get('item') ?? []).map((raw) => {
106
- const idx = raw.lastIndexOf('|');
107
- return idx === -1 ? { text: raw } : { text: raw.slice(0, idx), url: raw.slice(idx + 1) };
108
- });
104
+ /**
105
+ * `--item "текст"` или `--item "текст|https://ссылка"`.
106
+ *
107
+ * Имя группы у позиции нельзя передать так же, как у `--stat`: черта здесь
108
+ * уже занята ссылкой. Поэтому `--item-group "Red checks"` — одно имя на все
109
+ * позиции этого вызова. Списки у отправителей однородные (красные проверки,
110
+ * выключенные процессы), а разнородный список — это `--json`.
111
+ */
112
+ const items = () => {
113
+ const name = flags.get('item-group')?.[0];
114
+ return (flags.get('item') ?? []).map((raw) => {
115
+ const idx = raw.lastIndexOf('|');
116
+ const base = idx === -1 ? { text: raw } : { text: raw.slice(0, idx), url: raw.slice(idx + 1) };
117
+ return name ? { ...base, group: name } : base;
118
+ });
119
+ };
109
120
  /**
110
121
  * `--stat "label=value"`, и с 25.08.2026 — `--stat "Group | label=value"`:
111
122
  * имя группы, вертикальная черта, ярлык. Черта выбрана потому, что её нет ни
package/dist/events.d.ts CHANGED
@@ -45,10 +45,19 @@ type Keyed = {
45
45
  * рендерится как обычная нумерованная/маркированная строка — так уже
46
46
  * работают дайджест-задачи и список выключенных workflow.
47
47
  */
48
+ /**
49
+ * `group` — имя блока, под которым позиция встанет. Тот же закон, что у
50
+ * `lines` и `stats`: без имени позиция идёт в общий список, как раньше.
51
+ *
52
+ * Заведено потому, что список импорта смешивал три РАЗНЫЕ вещи в одном
53
+ * перечне и различал их значком в начале строки: 🆕 вышло сегодня,
54
+ * 🔁 вышло из очереди, ⚠ не вышло совсем. Значок делал работу заголовка.
55
+ */
48
56
  export type Item = {
49
57
  text: string;
50
58
  url?: string;
51
59
  label?: string;
60
+ group?: string;
52
61
  };
53
62
  export type NotifyEvent = Keyed & (
54
63
  /** Выкатка кода на сервер. */
package/dist/render.js CHANGED
@@ -164,7 +164,11 @@ const fieldCode = (label, value) => value ? `<b>${esc(cap(label))}:</b> <code>${
164
164
  */
165
165
  const fieldRun = (value, why) => {
166
166
  const explain = field('To do', why);
167
- return value && explain !== null ? [explain, `▶ <code>${esc(value)}</code>`] : [];
167
+ // Без значка. `▶` был единственным символом такого рода на все двадцать
168
+ // видов карточек, и владелец справедливо спросил, что он значит: ничего,
169
+ // чего не сказала бы строка `To do:` над ним и моноширинный шрифт под ним —
170
+ // Telegram делает такую строку копируемой по нажатию сам.
171
+ return value && explain !== null ? [explain, `<code>${esc(value)}</code>`] : [];
168
172
  };
169
173
  /** Заголовок группы: курсив + подчёркивание, без жирности, без двоеточия. */
170
174
  const group = (name) => `<i><u>${esc(cap(name))}</u></i>`;
@@ -219,7 +223,27 @@ const join = (parts) => {
219
223
  return out.join('\n');
220
224
  };
221
225
  /** Плоский список позиций (без ярлыков) — job/report без групп. */
222
- const bullets = (items, numbered) => (items ?? []).map((it, i) => groupItem(it, i, numbered));
226
+ /**
227
+ * Позиции списка. Именованная группа ВСЕГДА печатает свой заголовок — тот же
228
+ * закон, что у `labelled`: карточка одного типа не должна выглядеть по-разному
229
+ * в разные дни. Нумерация идёт внутри блока, а не сквозная: «1, 2» под своим
230
+ * заголовком читается, сквозная «3, 4» под вторым — нет.
231
+ */
232
+ const bullets = (items, numbered) => {
233
+ const list = items ?? [];
234
+ const names = [...new Set(list.map((it) => it.group).filter((g) => !!g))];
235
+ if (names.length === 0) {
236
+ return list.map((it, i) => groupItem(it, i, numbered));
237
+ }
238
+ const out = [];
239
+ list.filter((it) => !it.group).forEach((it, i) => out.push(groupItem(it, i, numbered)));
240
+ for (const name of names) {
241
+ out.push('');
242
+ out.push(group(name));
243
+ list.filter((it) => it.group === name).forEach((it, i) => out.push(groupItem(it, i, numbered)));
244
+ }
245
+ return out;
246
+ };
223
247
  /** Именованная группа целиком: заголовок + позиции, разделены строкой пустоты внутри вызова через join. */
224
248
  const renderGroup = (g) => [
225
249
  group(g.name),
@@ -371,18 +395,23 @@ const renderJob = (e) => {
371
395
  const icon = iconFor(e);
372
396
  const hasItems = (e.items ?? []).length > 0;
373
397
  const disabledList = hasItems && e.status === 'disabled';
398
+ // The NAME goes on the type line, the way `Report:` and `Issue:` carry
399
+ // theirs. It used to sit a line below under a second label, `Task:`, and
400
+ // the owner asked what a task is doing on a card headed Job — nothing: they
401
+ // were two words for one thing, and the outcome that took the first line is
402
+ // already the icon, and now the third tag too.
403
+ //
404
+ // `disabled` and `silent` keep a word of their own. A red mark reads as
405
+ // "it broke", and neither of those is that: one was switched off on purpose
406
+ // and the other has not been heard from at all.
407
+ const state = e.status === 'disabled'
408
+ ? 'switched off, not broken'
409
+ : e.status === 'silent'
410
+ ? 'no word from it at all'
411
+ : undefined;
374
412
  return join([
375
- typeLine(icon, 'Job', e.status),
376
- '',
377
- // The name is NOT the type line: line 2 is `Job: fail` by the format's own
378
- // rule, so the name is its own field. Called `Task:` and not `Job:` because
379
- // repeating the label of the line right above it reads as a mistake.
380
- // Until now the name was dropped entirely — every caller passed it and the
381
- // owner only ever saw it as the small grey instance tag.
382
- // The run link rides on the task's own name. It used to sit at the bottom
383
- // as `Workflow: open` — every job caller passes a URL and none passes a
384
- // workflow name, so that row was the bare verb the owner objected to.
385
- fieldLink('Task', e.workflowUrl ?? e.url, e.job),
413
+ typeLine(icon, 'Job', e.job, e.workflowUrl ?? e.url),
414
+ field('State', state),
386
415
  field('Reason', e.note),
387
416
  field('Expected', e.expected),
388
417
  // `Last run` when the task is alive, `Last seen` when it is not: the same
@@ -418,7 +447,11 @@ const renderReport = (e) => {
418
447
  // уточняет вторую строку, живёт рядом с ней, а не в блоке фактов.
419
448
  // Владелец: «период пошёл не туда, он же должен быть рядом с датой».
420
449
  field('Period', e.period),
421
- '',
450
+ // Строки БЕЗ группы идут вплотную к шапке, а не отдельным куском под
451
+ // пустой строкой: это факты про сам отчёт — период, номер, — а не про
452
+ // то, о чём он. Владелец: «number отдельно и период отдельно, непонятно,
453
+ // куда их относить». `labelled` сам ставит пустую строку перед первой
454
+ // группой, поэтому её здесь больше нет.
422
455
  ...numbers,
423
456
  body.length > 0 ? '' : null,
424
457
  ...body
@@ -431,7 +464,7 @@ const renderReport = (e) => {
431
464
  typeLine(iconFor(e), 'Report', e.title, e.url),
432
465
  // Вплотную к названию — см. соседнюю ветку.
433
466
  field('Period', e.period),
434
- '',
467
+ // Вплотную к шапке — см. соседнюю ветку.
435
468
  ...labelled(e.lines),
436
469
  items.length > 0 ? '' : null,
437
470
  ...items
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mikitasazan/notify",
3
- "version": "1.4.3",
3
+ "version": "1.4.5",
4
4
  "description": "Единая типизированная отправка Telegram-уведомлений (форум-темы, маршрутизация, ретраи) для всех проектов",
5
5
  "type": "module",
6
6
  "license": "MIT",