@mikitasazan/notify 1.26.0 → 1.26.1
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/send.d.ts +15 -0
- package/dist/send.js +73 -1
- package/package.json +1 -1
package/dist/send.d.ts
CHANGED
|
@@ -5,6 +5,21 @@ export declare const dedupe: (e: NotifyEvent, now?: number) => {
|
|
|
5
5
|
action: "send" | "suppress";
|
|
6
6
|
stillRed?: number;
|
|
7
7
|
};
|
|
8
|
+
/**
|
|
9
|
+
* One slot per OFFENDER — project, type and the card's own instance key —
|
|
10
|
+
* not per type (15.09.2026). Keyed by type, the red about a broken weekly
|
|
11
|
+
* script went green the moment ANY other script of that type sent a clean
|
|
12
|
+
* card, the reactor deleted the red and closed its issue, and next week the
|
|
13
|
+
* same red counted as a first day again: an issue never filed. The slot is
|
|
14
|
+
* both the watchdog card's key and the entry in the red memory below.
|
|
15
|
+
*/
|
|
16
|
+
export declare const brokenSlot: (e: NotifyEvent) => string;
|
|
17
|
+
/** Exported for tests: remember that this offender's red is in the feed. */
|
|
18
|
+
export declare const rememberBroken: (slot: string, now?: number) => void;
|
|
19
|
+
/** Exported for tests: true once — the slot was red and is forgotten now. */
|
|
20
|
+
export declare const forgetBroken: (slot: string) => boolean;
|
|
21
|
+
/** Exported for tests: the green that pairs with brokenCardEvent's red — same key, same project. */
|
|
22
|
+
export declare const healedCardEvent: (e: NotifyEvent) => NotifyEvent;
|
|
8
23
|
/** Exported for tests: the watchdog's own card must provably pass the lint. */
|
|
9
24
|
export declare const brokenCardEvent: (e: NotifyEvent, faults: string[], offenderHtml: string) => NotifyEvent;
|
|
10
25
|
export declare const notify: (e: NotifyEvent) => Promise<SendResult>;
|
package/dist/send.js
CHANGED
|
@@ -412,6 +412,62 @@ const journal = (line) => {
|
|
|
412
412
|
// the journal is best-effort: a card must never be lost to bookkeeping
|
|
413
413
|
}
|
|
414
414
|
};
|
|
415
|
+
/**
|
|
416
|
+
* One slot per OFFENDER — project, type and the card's own instance key —
|
|
417
|
+
* not per type (15.09.2026). Keyed by type, the red about a broken weekly
|
|
418
|
+
* script went green the moment ANY other script of that type sent a clean
|
|
419
|
+
* card, the reactor deleted the red and closed its issue, and next week the
|
|
420
|
+
* same red counted as a first day again: an issue never filed. The slot is
|
|
421
|
+
* both the watchdog card's key and the entry in the red memory below.
|
|
422
|
+
*/
|
|
423
|
+
export const brokenSlot = (e) => `notify-broken-${String(e.type)}-${String(e.project)}-${eventKey(e)}`;
|
|
424
|
+
const brokenStatePath = () => join(dirname(statePath()), 'notify-broken.red.json');
|
|
425
|
+
const readBroken = () => {
|
|
426
|
+
try {
|
|
427
|
+
const parsed = JSON.parse(readFileSync(brokenStatePath(), 'utf8'));
|
|
428
|
+
return parsed && typeof parsed === 'object' && !Array.isArray(parsed) ? parsed : {};
|
|
429
|
+
}
|
|
430
|
+
catch {
|
|
431
|
+
return {};
|
|
432
|
+
}
|
|
433
|
+
};
|
|
434
|
+
const writeBroken = (state) => {
|
|
435
|
+
try {
|
|
436
|
+
const file = brokenStatePath();
|
|
437
|
+
mkdirSync(dirname(file), { recursive: true });
|
|
438
|
+
const tmp = `${file}.tmp${process.pid}`;
|
|
439
|
+
writeFileSync(tmp, JSON.stringify(state, null, 1));
|
|
440
|
+
renameSync(tmp, file);
|
|
441
|
+
}
|
|
442
|
+
catch {
|
|
443
|
+
// memory is best-effort: a lost entry costs one green, never a card
|
|
444
|
+
}
|
|
445
|
+
};
|
|
446
|
+
/** Exported for tests: remember that this offender's red is in the feed. */
|
|
447
|
+
export const rememberBroken = (slot, now = Date.now()) => {
|
|
448
|
+
const state = readBroken();
|
|
449
|
+
state[slot] = new Date(now).toISOString().slice(0, 10);
|
|
450
|
+
writeBroken(state);
|
|
451
|
+
};
|
|
452
|
+
/** Exported for tests: true once — the slot was red and is forgotten now. */
|
|
453
|
+
export const forgetBroken = (slot) => {
|
|
454
|
+
const state = readBroken();
|
|
455
|
+
if (!Object.hasOwn(state, slot)) {
|
|
456
|
+
return false;
|
|
457
|
+
}
|
|
458
|
+
delete state[slot];
|
|
459
|
+
writeBroken(state);
|
|
460
|
+
return true;
|
|
461
|
+
};
|
|
462
|
+
/** Exported for tests: the green that pairs with brokenCardEvent's red — same key, same project. */
|
|
463
|
+
export const healedCardEvent = (e) => ({
|
|
464
|
+
type: 'job',
|
|
465
|
+
project: 'mac-config',
|
|
466
|
+
job: 'notify: a card broke the standard',
|
|
467
|
+
status: 'ok',
|
|
468
|
+
note: `the ${String(e.type)} card for ${String(e.project)} passes the standard again`,
|
|
469
|
+
key: brokenSlot(e)
|
|
470
|
+
});
|
|
415
471
|
/** Exported for tests: the watchdog's own card must provably pass the lint. */
|
|
416
472
|
export const brokenCardEvent = (e, faults, offenderHtml) => {
|
|
417
473
|
const offender = offenderHtml
|
|
@@ -436,7 +492,7 @@ export const brokenCardEvent = (e, faults, offenderHtml) => {
|
|
|
436
492
|
detail: offender || undefined,
|
|
437
493
|
detailLabel: 'Offender',
|
|
438
494
|
check: 'config jobs --log notify-broken',
|
|
439
|
-
key:
|
|
495
|
+
key: brokenSlot(e)
|
|
440
496
|
};
|
|
441
497
|
};
|
|
442
498
|
const reportBrokenCard = async (e, faults, offenderHtml) => {
|
|
@@ -455,6 +511,19 @@ const reportBrokenCard = async (e, faults, offenderHtml) => {
|
|
|
455
511
|
journal(`notify-watchdog: own card failed lint: ${ownFaults.join('; ')}`);
|
|
456
512
|
}
|
|
457
513
|
await deliver(targets(broken), html).catch(() => undefined);
|
|
514
|
+
rememberBroken(brokenSlot(e));
|
|
515
|
+
};
|
|
516
|
+
/**
|
|
517
|
+
* The green half of the watchdog: the offender's next clean card heals its
|
|
518
|
+
* own red, and only its own. Sent through `deliver` directly, not `notify`,
|
|
519
|
+
* so a green can never be swallowed by the 20h window a red just used.
|
|
520
|
+
*/
|
|
521
|
+
const reportHealedCard = async (e) => {
|
|
522
|
+
if (!forgetBroken(brokenSlot(e))) {
|
|
523
|
+
return;
|
|
524
|
+
}
|
|
525
|
+
const healed = healedCardEvent(e);
|
|
526
|
+
await deliver(targets(healed), render(healed)).catch(() => undefined);
|
|
458
527
|
};
|
|
459
528
|
export const notify = async (e) => {
|
|
460
529
|
// Object.hasOwn, not `in`: `in` walks the prototype chain, and
|
|
@@ -486,5 +555,8 @@ export const notify = async (e) => {
|
|
|
486
555
|
if (faults.length > 0) {
|
|
487
556
|
await reportBrokenCard(e, faults, html);
|
|
488
557
|
}
|
|
558
|
+
else {
|
|
559
|
+
await reportHealedCard(e);
|
|
560
|
+
}
|
|
489
561
|
return result;
|
|
490
562
|
};
|
package/package.json
CHANGED