@mikitasazan/notify 1.6.2 → 1.8.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-flags.js CHANGED
@@ -14,7 +14,7 @@ export const KNOWN_FLAGS = new Set([
14
14
  'command', 'command-note', 'commit-body', 'commit-title', 'commit-url', 'detail',
15
15
  'expected', 'filename', 'item',
16
16
  'item-group', 'job', 'key', 'last-seen', 'line', 'logs', 'note',
17
- 'number', 'path', 'period', 'project', 'reviewer', 'stat', 'status',
17
+ 'aside', 'number', 'path', 'period', 'project', 'reviewer', 'stat', 'status',
18
18
  'target', 'title', 'url', 'via', 'workflow-name', 'workflow-url',
19
19
  // Флаги без значения. Живут здесь же, чтобы разбор и список не разошлись.
20
20
  'json', 'recovered', 'dry-run'
package/dist/cli.js CHANGED
@@ -236,6 +236,7 @@ else {
236
236
  project: project(),
237
237
  job: one('job') ?? '(no name)',
238
238
  status: jobStatus(),
239
+ aside: one('aside'),
239
240
  expected: one('expected'),
240
241
  lastSeen: one('last-seen'),
241
242
  stats: pairs('stat'),
@@ -254,7 +255,9 @@ else {
254
255
  type: 'report',
255
256
  project: project(),
256
257
  title: one('title') ?? '(no title)',
257
- period: one('period'),
258
+ // `--period` is the old spelling of `--aside`; both fill the same
259
+ // slot, and two senders still use the old one.
260
+ aside: one('aside') ?? one('period'),
258
261
  lines: pairs('line'),
259
262
  items: items(),
260
263
  url: one('url')
package/dist/events.d.ts CHANGED
@@ -111,6 +111,14 @@ export type NotifyEvent = Keyed & (
111
111
  * ПОЛНОМУ тегу. Один поток на задачу это чинит.
112
112
  */
113
113
  status: 'ok' | 'fail' | 'disabled' | 'silent';
114
+ /**
115
+ * The one qualifier the name needs to be readable on its own, printed in
116
+ * brackets right after it: `Yandex game import (reporting again)`. It is
117
+ * the same slot a report uses for the day it covers. Anything that is a
118
+ * FACT about the job goes in a row of its own; this is for the word that
119
+ * finishes the name.
120
+ */
121
+ aside?: string;
114
122
  /** Как часто задача обязана отмечаться — для `silent` и для возврата из него. */
115
123
  expected?: string;
116
124
  /** Когда её видели в последний раз. */
@@ -162,7 +170,13 @@ export type NotifyEvent = Keyed & (
162
170
  type: 'report';
163
171
  project: Project;
164
172
  title: string;
165
- period?: string;
173
+ /**
174
+ * Printed in brackets after the title — which day the report covers, or
175
+ * which day its arrows are measured against. Same slot, same name, as a
176
+ * job's. `period` is the old spelling of this field and the CLI still
177
+ * accepts `--period` for it.
178
+ */
179
+ aside?: string;
166
180
  /** Пусто/не передано, когда используются `groups` — два вида отчёта не смешиваются в одном событии. */
167
181
  lines?: Array<[label: string, value: string | number, group?: string]>;
168
182
  /**
package/dist/render.d.ts CHANGED
@@ -46,6 +46,24 @@ export declare const slug: (raw: string) => string;
46
46
  * заголовок и есть единственное стабильное поле).
47
47
  */
48
48
  export declare const eventKey: (e: NotifyEvent) => string;
49
+ /**
50
+ * Третий тег — ИСХОД, и он есть всегда. Владелец: «не хватает тега fail или
51
+ * похожего, чтобы фейлы можно было группировать и ок можно было группировать».
52
+ * Одно нажатие в Telegram собирает все падения проекта разом, каким бы типом
53
+ * они ни пришли — выкатка, проверка, задача по расписанию, авария.
54
+ *
55
+ * The value comes from the ICON, never from the status word. The icon is
56
+ * already the single source of truth for the sound, and a second list of "what
57
+ * counts as broken" would drift from the first — it already did once, when a
58
+ * red card arrived silent.
59
+ *
60
+ * One icon meaning, one tag. A watchdog that SWITCHED SOMETHING OFF is not a
61
+ * failure and must not be filed under the same word as one: the owner read
62
+ * `#fail` under a 🚫 and said so. Nor is a task that has simply gone quiet —
63
+ * nobody knows yet whether it broke, and `#unknown` is the honest word for it.
64
+ */
65
+ export declare const OUTCOME_TAG: Readonly<Record<string, string>>;
66
+ export declare const outcomeTag: (e: NotifyEvent) => string;
49
67
  /**
50
68
  * Строка тегов для свободного HTML (`sendReport`). Тег — это ФИЛЬТР владельца,
51
69
  * и к формату тела он отношения не имеет: дневной отчёт остаётся свободным
package/dist/render.js CHANGED
@@ -19,7 +19,7 @@
19
19
  * строка разделяет БЛОКИ ПО СМЫСЛУ (шапка / суть / действия), не механически
20
20
  * после каждой строки.
21
21
  */
22
- import { ICON, LOUD, iconFor } from "./events.js";
22
+ import { ICON, iconFor } from "./events.js";
23
23
  /** Первая буква — заглавная, остальное как есть (ga4/GitHub остаются собой). */
24
24
  /**
25
25
  * Ярлык с большой буквы — но НЕ у имени, которое пишется со строчной нарочно:
@@ -414,6 +414,10 @@ const renderDeploy = (e) => {
414
414
  ...twoBlocks([field('Target', e.target), field('Reason', e.note)], [fieldLink('Commit', e.commitUrl, e.commit), titleField(e.commitTitle), bodyQuote(e.commitBody)])
415
415
  ]);
416
416
  };
417
+ const schedule = (expected, lastSeen, lastLabel) => {
418
+ const rows = [field('Expected', expected), field(lastLabel, lastSeen)].filter((r) => r !== null);
419
+ return rows.length > 0 ? ['', group('Schedule'), ...rows] : [];
420
+ };
417
421
  const renderJob = (e) => {
418
422
  const icon = iconFor(e);
419
423
  const hasItems = (e.items ?? []).length > 0;
@@ -424,22 +428,18 @@ const renderJob = (e) => {
424
428
  // were two words for one thing, and the outcome that took the first line is
425
429
  // already the icon, and now the third tag too.
426
430
  //
427
- // `disabled` and `silent` keep a word of their own. A red mark reads as
428
- // "it broke", and neither of those is that: one was switched off on purpose
429
- // and the other has not been heard from at all.
430
- const state = e.status === 'disabled'
431
- ? 'switched off, not broken'
432
- : e.status === 'silent'
433
- ? 'no word from it at all'
434
- : undefined;
431
+ // There is no `State:` row. It said "switched off, not broken" under a 🚫
432
+ // and "no word from it at all" under a the third way of saying what the
433
+ // icon says and what the third tag now says too. The icon table on the
434
+ // catalogue page defines both marks.
435
435
  return join([
436
- typeLine(icon, 'Job', e.job, e.workflowUrl ?? e.url),
437
- field('State', state),
436
+ typeLine(icon, 'Job', e.job, e.workflowUrl ?? e.url, e.aside),
438
437
  field('Reason', e.note),
439
- field('Expected', e.expected),
440
- // `Last run` when the task is alive, `Last seen` when it is not: the same
441
- // timestamp answers two different questions.
442
- field(e.status === 'silent' ? 'Last seen' : 'Last run', e.lastSeen),
438
+ // The timetable is a different subject from this event: how often the task
439
+ // owes a sign of life and when it last gave one. It stood in a bare run
440
+ // under `Reason:` and read as more of the same. `Last run` when the task
441
+ // is alive, `Last seen` when it is not — one timestamp, two questions.
442
+ ...schedule(e.expected, e.lastSeen, e.status === 'silent' ? 'Last seen' : 'Last run'),
443
443
  ...labelled(e.stats),
444
444
  hasItems ? '' : null,
445
445
  // Heading ONLY for `disabled`. It used to print for any job carrying a
@@ -464,7 +464,7 @@ const renderReport = (e) => {
464
464
  // without a word.
465
465
  const numbers = labelled(e.lines);
466
466
  return join([
467
- typeLine(iconFor(e), 'Report', e.title, e.url, e.period),
467
+ typeLine(iconFor(e), 'Report', e.title, e.url, e.aside),
468
468
  // Rows with no group of their own sit flush against the header instead of
469
469
  // forming a separate slab under a blank line. `labelled` puts the blank
470
470
  // line before the first group itself, so there is none here.
@@ -477,7 +477,7 @@ const renderReport = (e) => {
477
477
  return join([
478
478
  // Both analytics jobs send a link to the day's snapshot in docs/. It used to
479
479
  // hang off a trailing `Details: open` row; now it is the report's own name.
480
- typeLine(iconFor(e), 'Report', e.title, e.url, e.period),
480
+ typeLine(iconFor(e), 'Report', e.title, e.url, e.aside),
481
481
  // Flush against the header — see the branch above.
482
482
  ...labelled(e.lines),
483
483
  items.length > 0 ? '' : null,
@@ -560,8 +560,7 @@ const renderHeartbeatMiss = (e) => {
560
560
  // copy of that watchdog can, and the card it gets must obey the template.
561
561
  typeLine(icon, 'Heartbeat', e.job, undefined, action),
562
562
  field('Reason', e.note),
563
- field('Expected', e.expected),
564
- field(e.recovered ? 'Last run' : 'Last seen', e.lastSeen)
563
+ ...schedule(e.expected, e.lastSeen, e.recovered ? 'Last run' : 'Last seen')
565
564
  ]);
566
565
  };
567
566
  const RENDERERS = {
@@ -640,21 +639,28 @@ export const eventKey = (e) => {
640
639
  * Одно нажатие в Telegram собирает все падения проекта разом, каким бы типом
641
640
  * они ни пришли — выкатка, проверка, задача по расписанию, авария.
642
641
  *
643
- * Значение берётся у ЗНАЧКА, а не у слова статуса, и это не мелочь: значок уже
644
- * единственный источник правды про звук, и второй список «что считать
645
- * падением» разошёлся бы с первымтак уже было, когда красная карточка
646
- * приходила беззвучной. Громкий значок — `#fail`, зелёный — `#ok`, всё
647
- * остальное (завели задачу, открыли PR, попросили правки, отчёт) — `#news`:
648
- * это новость, а не приговор робота.
642
+ * The value comes from the ICON, never from the status word. The icon is
643
+ * already the single source of truth for the sound, and a second list of "what
644
+ * counts as broken" would drift from the first it already did once, when a
645
+ * red card arrived silent.
646
+ *
647
+ * One icon meaning, one tag. A watchdog that SWITCHED SOMETHING OFF is not a
648
+ * failure and must not be filed under the same word as one: the owner read
649
+ * `#fail` under a 🚫 and said so. Nor is a task that has simply gone quiet —
650
+ * nobody knows yet whether it broke, and `#unknown` is the honest word for it.
649
651
  */
650
- const OK_ICONS = new Set([ICON.ok, ICON.landed, ICON.approved]);
651
- const outcomeTag = (e) => {
652
- const icon = iconFor(e);
653
- if (LOUD.has(icon)) {
654
- return 'fail';
655
- }
656
- return OK_ICONS.has(icon) ? 'ok' : 'news';
652
+ export const OUTCOME_TAG = {
653
+ [ICON.red]: 'fail',
654
+ [ICON.alarm]: 'fail',
655
+ [ICON.off]: 'off',
656
+ [ICON.unknown]: 'unknown',
657
+ [ICON.ok]: 'ok',
658
+ [ICON.landed]: 'ok',
659
+ [ICON.approved]: 'ok'
657
660
  };
661
+ // Everything left over — a task was opened, a PR opened, edits asked for, a
662
+ // digest — is news: something happened, no verdict was passed.
663
+ export const outcomeTag = (e) => OUTCOME_TAG[iconFor(e)] ?? 'news';
658
664
  const tagsLine = (e) => `#${TYPE_TAG[e.type]} #${esc(eventKey(e))} #${outcomeTag(e)}`;
659
665
  /**
660
666
  * Строка тегов для свободного HTML (`sendReport`). Тег — это ФИЛЬТР владельца,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mikitasazan/notify",
3
- "version": "1.6.2",
3
+ "version": "1.8.0",
4
4
  "description": "Единая типизированная отправка Telegram-уведомлений (форум-темы, маршрутизация, ретраи) для всех проектов",
5
5
  "type": "module",
6
6
  "license": "MIT",