From fe1615399050fea4b3b17f10ed2f54055747297c Mon Sep 17 00:00:00 2001 From: kitsuyui Date: Thu, 18 Jun 2026 21:10:38 +0900 Subject: [PATCH 1/2] fix: remove non-hex hexspeak patterns that can never match a SHA SHA hashes consist only of [0-9a-f]. The patterns 'defecated' (contains 't'), '0ffice' (contains 'i'), and 'badcable' (contains 'l') include non-hex characters and can never match any commit SHA, making them dead patterns that also inflate the expectedCommitHitsHexspeak probability. Remove the three dead patterns from the regex and the probability calculation. Add a regression test to document the hex-only constraint. --- src/rarity.ts | 11 +---------- src/rules.spec.ts | 14 ++++++++++++++ src/rules.ts | 2 +- 3 files changed, 16 insertions(+), 11 deletions(-) diff --git a/src/rarity.ts b/src/rarity.ts index 3ae9748..4a6901f 100644 --- a/src/rarity.ts +++ b/src/rarity.ts @@ -83,16 +83,7 @@ export function expectedCommitHitsSameNumbers( export function expectedCommitHitsHexspeak( repositoryCommitCount: number ): number { - const patterns = [ - 'f00d', - 'feed', - 'cafe', - 'c0ffee', - 'deadbeef', - 'defecated', - '0ffice', - 'badcable', - ] + const patterns = ['f00d', 'feed', 'cafe', 'c0ffee', 'deadbeef'] const probability = patterns.reduce((sum, pattern) => { return sum + slidingWindows(pattern.length) / 16 ** pattern.length diff --git a/src/rules.spec.ts b/src/rules.spec.ts index afe2451..b7ec7b2 100644 --- a/src/rules.spec.ts +++ b/src/rules.spec.ts @@ -88,4 +88,18 @@ describe('parseRules', () => { }) ).toBeGreaterThan(0) }) + + it('hexspeak rule only matches patterns composed of valid hex characters', () => { + const rule = Rules.commit_hits_hexspeak.rule + // SHA hashes consist only of [0-9a-f]; these former patterns contain non-hex chars + expect(rule.test('defecated')).toBe(false) // 't' is not hex + expect(rule.test('0ffice')).toBe(false) // 'i' is not hex + expect(rule.test('badcable')).toBe(false) // 'l' is not hex + // Valid hexspeak patterns should still match + expect(rule.test('f00d')).toBe(true) + expect(rule.test('deadbeef')).toBe(true) + expect(rule.test('cafe')).toBe(true) + expect(rule.test('c0ffee')).toBe(true) + expect(rule.test('feed')).toBe(true) + }) }) diff --git a/src/rules.ts b/src/rules.ts index 9026d9e..6ecbd2c 100644 --- a/src/rules.ts +++ b/src/rules.ts @@ -115,7 +115,7 @@ export const Rules: NamedMessageForRuleSet = { commit_hits_hexspeak: { id: 'commit_hits_hexspeak', kind: 'commit', - rule: /(?:(?:f00d|feed|cafe|c0ffee|deadbeef|defecated|0ffice|badcable))/i, + rule: /(?:(?:f00d|feed|cafe|c0ffee|deadbeef))/, message: 'Commit `{{commitId}}` is lucky! It contains **{{matched}}**!.', expectedOccurrences: ({ repositoryCommitCount }) => expectedCommitHitsHexspeak(repositoryCommitCount), From 987ba80298aea33e13abc8ed48680cfefe6cd82d Mon Sep 17 00:00:00 2001 From: kitsuyui Date: Fri, 19 Jun 2026 07:45:39 +0900 Subject: [PATCH 2/2] build: rebuild dist after removing non-hex hexspeak patterns The committed dist artifacts were out of sync with src/rules.ts; the `git diff --exit-code -- dist` step in CI failed. Rebuild with tsdown. --- dist/rarity.d.mts.map | 2 +- dist/rarity.mjs | 2 +- dist/rarity.mjs.map | 2 +- dist/rules.mjs | 2 +- dist/rules.mjs.map | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/dist/rarity.d.mts.map b/dist/rarity.d.mts.map index 15a732c..70c8fce 100644 --- a/dist/rarity.d.mts.map +++ b/dist/rarity.d.mts.map @@ -1 +1 @@ -{"version":3,"file":"rarity.d.mts","names":[],"sources":["../rarity.ts"],"mappings":";UAEiB,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA;EACf,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA;EACA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA;AAAA;AAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAiBc,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAoB,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA;AAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAYpB,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAoC,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA;AAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAapC,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAoB,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA;AAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAKpB,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAkB,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA;AAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAalB,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAsB,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA;AAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAItB,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAsB,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA;AAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAItB,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAsB,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA;AAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAItB,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CACd,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA;AAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAKc,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CACd,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA;AAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAoBc,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CACd,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CACA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA"} \ No newline at end of file +{"version":3,"file":"rarity.d.mts","names":[],"sources":["../rarity.ts"],"mappings":";UAEiB,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA;EACf,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA;EACA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA;AAAA;AAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAiBc,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAoB,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA;AAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAYpB,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAoC,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA;AAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAapC,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAoB,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA;AAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAKpB,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAkB,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA;AAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAalB,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAsB,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA;AAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAItB,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAsB,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA;AAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAItB,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAsB,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA;AAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAItB,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CACd,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA;AAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAKc,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CACd,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA;AAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAWc,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CACd,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CACA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA"} \ No newline at end of file diff --git a/dist/rarity.mjs b/dist/rarity.mjs index 7f4a6ee..8a823d6 100644 --- a/dist/rarity.mjs +++ b/dist/rarity.mjs @@ -1,2 +1,2 @@ -function e(e){return Math.max(0,40-e+1)}function t(t,n){return t*(e(n)/16**n)}function n(e){let t=0,n=10;for(;n<=e;)t+=1,n*=10;return t}function r(e){if(e<10)return 0;let t=e.toString().length,n=(t-2)*9,r=10**(t-1),i=Math.floor(e/r);return n+Math.min(i,9)}function i(e){return[512,1024,2048,4096,8192,16384,32768,65536].filter(t=>t<=e).length}function a(e){let t=0;for(let n=3;n<=e.toString().length;n+=1)Number.parseInt(`7`.repeat(n),10)<=e&&(t+=1);return t}function o(e){return t(e,3)}function s(e){return t(e,3)}function c(e){return t(e,3)}function l(t){return t*(e(5)/16**4)}function u(t){return t*[`f00d`,`feed`,`cafe`,`c0ffee`,`deadbeef`,`defecated`,`0ffice`,`badcable`].reduce((t,n)=>t+e(n.length)/16**n.length,0)}function d(e,t){return t===void 0?!0:e<=t}export{a as expectedAllSevens,c as expectedCommitHits123,s as expectedCommitHits666,o as expectedCommitHits777,u as expectedCommitHitsHexspeak,l as expectedCommitHitsSameNumbers,n as expectedPowersOfTen,i as expectedPowersOfTwo,r as expectedSingleNonzeroDigitPrNumbers,d as isRareEnough}; +function e(e){return Math.max(0,40-e+1)}function t(t,n){return t*(e(n)/16**n)}function n(e){let t=0,n=10;for(;n<=e;)t+=1,n*=10;return t}function r(e){if(e<10)return 0;let t=e.toString().length,n=(t-2)*9,r=10**(t-1),i=Math.floor(e/r);return n+Math.min(i,9)}function i(e){return[512,1024,2048,4096,8192,16384,32768,65536].filter(t=>t<=e).length}function a(e){let t=0;for(let n=3;n<=e.toString().length;n+=1)Number.parseInt(`7`.repeat(n),10)<=e&&(t+=1);return t}function o(e){return t(e,3)}function s(e){return t(e,3)}function c(e){return t(e,3)}function l(t){return t*(e(5)/16**4)}function u(t){return t*[`f00d`,`feed`,`cafe`,`c0ffee`,`deadbeef`].reduce((t,n)=>t+e(n.length)/16**n.length,0)}function d(e,t){return t===void 0?!0:e<=t}export{a as expectedAllSevens,c as expectedCommitHits123,s as expectedCommitHits666,o as expectedCommitHits777,u as expectedCommitHitsHexspeak,l as expectedCommitHitsSameNumbers,n as expectedPowersOfTen,i as expectedPowersOfTwo,r as expectedSingleNonzeroDigitPrNumbers,d as isRareEnough}; //# sourceMappingURL=rarity.mjs.map \ No newline at end of file diff --git a/dist/rarity.mjs.map b/dist/rarity.mjs.map index b0165ef..27578b7 100644 --- a/dist/rarity.mjs.map +++ b/dist/rarity.mjs.map @@ -1 +1 @@ -{"version":3,"file":"rarity.mjs","names":[],"sources":["../src/rarity.ts"],"sourcesContent":["const SHA_LENGTH = 40\n\nexport interface RarityContext {\n prNum: number\n repositoryCommitCount: number\n}\n\nfunction slidingWindows(length: number): number {\n return Math.max(0, SHA_LENGTH - length + 1)\n}\n\nfunction expectedOccurrencesFromSubstringProbability(\n repositoryCommitCount: number,\n substringLength: number\n): number {\n return (\n repositoryCommitCount *\n (slidingWindows(substringLength) / 16 ** substringLength)\n )\n}\n\nexport function expectedPowersOfTen(prNum: number): number {\n let count = 0\n let current = 10\n\n while (current <= prNum) {\n count += 1\n current *= 10\n }\n\n return count\n}\n\nexport function expectedSingleNonzeroDigitPrNumbers(prNum: number): number {\n if (prNum < 10) {\n return 0\n }\n\n const digits = prNum.toString().length\n const shorterDigitMatches = (digits - 2) * 9\n const currentDigitBase = 10 ** (digits - 1)\n const currentLeadingDigit = Math.floor(prNum / currentDigitBase)\n\n return shorterDigitMatches + Math.min(currentLeadingDigit, 9)\n}\n\nexport function expectedPowersOfTwo(prNum: number): number {\n const values = [512, 1024, 2048, 4096, 8192, 16384, 32768, 65536]\n return values.filter((value) => value <= prNum).length\n}\n\nexport function expectedAllSevens(prNum: number): number {\n let count = 0\n\n for (let digits = 3; digits <= prNum.toString().length; digits += 1) {\n const value = Number.parseInt('7'.repeat(digits), 10)\n if (value <= prNum) {\n count += 1\n }\n }\n\n return count\n}\n\nexport function expectedCommitHits777(repositoryCommitCount: number): number {\n return expectedOccurrencesFromSubstringProbability(repositoryCommitCount, 3)\n}\n\nexport function expectedCommitHits666(repositoryCommitCount: number): number {\n return expectedOccurrencesFromSubstringProbability(repositoryCommitCount, 3)\n}\n\nexport function expectedCommitHits123(repositoryCommitCount: number): number {\n return expectedOccurrencesFromSubstringProbability(repositoryCommitCount, 3)\n}\n\nexport function expectedCommitHitsSameNumbers(\n repositoryCommitCount: number\n): number {\n return repositoryCommitCount * (slidingWindows(5) / 16 ** 4)\n}\n\nexport function expectedCommitHitsHexspeak(\n repositoryCommitCount: number\n): number {\n const patterns = [\n 'f00d',\n 'feed',\n 'cafe',\n 'c0ffee',\n 'deadbeef',\n 'defecated',\n '0ffice',\n 'badcable',\n ]\n\n const probability = patterns.reduce((sum, pattern) => {\n return sum + slidingWindows(pattern.length) / 16 ** pattern.length\n }, 0)\n\n return repositoryCommitCount * probability\n}\n\nexport function isRareEnough(\n expectedOccurrences: number,\n maxExpectedOccurrences?: number\n): boolean {\n if (maxExpectedOccurrences === undefined) {\n return true\n }\n\n return expectedOccurrences <= maxExpectedOccurrences\n}\n"],"mappings":"AAOA,SAAS,EAAe,EAAwB,CAC9C,OAAO,KAAK,IAAI,EAAG,GAAa,EAAS,EAAE,CAG7C,SAAS,EACP,EACA,EACQ,CACR,OACE,GACC,EAAe,EAAgB,CAAG,IAAM,GAI7C,SAAgB,EAAoB,EAAuB,CACzD,IAAI,EAAQ,EACR,EAAU,GAEd,KAAO,GAAW,GAChB,GAAS,EACT,GAAW,GAGb,OAAO,EAGT,SAAgB,EAAoC,EAAuB,CACzE,GAAI,EAAQ,GACV,MAAO,GAGT,IAAM,EAAS,EAAM,UAAU,CAAC,OAC1B,GAAuB,EAAS,GAAK,EACrC,EAAmB,KAAO,EAAS,GACnC,EAAsB,KAAK,MAAM,EAAQ,EAAiB,CAEhE,OAAO,EAAsB,KAAK,IAAI,EAAqB,EAAE,CAG/D,SAAgB,EAAoB,EAAuB,CAEzD,MADe,CAAC,IAAK,KAAM,KAAM,KAAM,KAAM,MAAO,MAAO,MAAM,CACnD,OAAQ,GAAU,GAAS,EAAM,CAAC,OAGlD,SAAgB,EAAkB,EAAuB,CACvD,IAAI,EAAQ,EAEZ,IAAK,IAAI,EAAS,EAAG,GAAU,EAAM,UAAU,CAAC,OAAQ,GAAU,EAClD,OAAO,SAAS,IAAI,OAAO,EAAO,CAAE,GAAG,EACxC,IACX,GAAS,GAIb,OAAO,EAGT,SAAgB,EAAsB,EAAuC,CAC3E,OAAO,EAA4C,EAAuB,EAAE,CAG9E,SAAgB,EAAsB,EAAuC,CAC3E,OAAO,EAA4C,EAAuB,EAAE,CAG9E,SAAgB,EAAsB,EAAuC,CAC3E,OAAO,EAA4C,EAAuB,EAAE,CAG9E,SAAgB,EACd,EACQ,CACR,OAAO,GAAyB,EAAe,EAAE,CAAG,IAAM,GAG5D,SAAgB,EACd,EACQ,CAgBR,OAAO,EAfU,CACf,OACA,OACA,OACA,SACA,WACA,YACA,SACA,WACD,CAE4B,QAAQ,EAAK,IACjC,EAAM,EAAe,EAAQ,OAAO,CAAG,IAAM,EAAQ,OAC3D,EAAE,CAKP,SAAgB,EACd,EACA,EACS,CAKT,OAJI,IAA2B,IAAA,GACtB,GAGF,GAAuB"} \ No newline at end of file +{"version":3,"file":"rarity.mjs","names":[],"sources":["../src/rarity.ts"],"sourcesContent":["const SHA_LENGTH = 40\n\nexport interface RarityContext {\n prNum: number\n repositoryCommitCount: number\n}\n\nfunction slidingWindows(length: number): number {\n return Math.max(0, SHA_LENGTH - length + 1)\n}\n\nfunction expectedOccurrencesFromSubstringProbability(\n repositoryCommitCount: number,\n substringLength: number\n): number {\n return (\n repositoryCommitCount *\n (slidingWindows(substringLength) / 16 ** substringLength)\n )\n}\n\nexport function expectedPowersOfTen(prNum: number): number {\n let count = 0\n let current = 10\n\n while (current <= prNum) {\n count += 1\n current *= 10\n }\n\n return count\n}\n\nexport function expectedSingleNonzeroDigitPrNumbers(prNum: number): number {\n if (prNum < 10) {\n return 0\n }\n\n const digits = prNum.toString().length\n const shorterDigitMatches = (digits - 2) * 9\n const currentDigitBase = 10 ** (digits - 1)\n const currentLeadingDigit = Math.floor(prNum / currentDigitBase)\n\n return shorterDigitMatches + Math.min(currentLeadingDigit, 9)\n}\n\nexport function expectedPowersOfTwo(prNum: number): number {\n const values = [512, 1024, 2048, 4096, 8192, 16384, 32768, 65536]\n return values.filter((value) => value <= prNum).length\n}\n\nexport function expectedAllSevens(prNum: number): number {\n let count = 0\n\n for (let digits = 3; digits <= prNum.toString().length; digits += 1) {\n const value = Number.parseInt('7'.repeat(digits), 10)\n if (value <= prNum) {\n count += 1\n }\n }\n\n return count\n}\n\nexport function expectedCommitHits777(repositoryCommitCount: number): number {\n return expectedOccurrencesFromSubstringProbability(repositoryCommitCount, 3)\n}\n\nexport function expectedCommitHits666(repositoryCommitCount: number): number {\n return expectedOccurrencesFromSubstringProbability(repositoryCommitCount, 3)\n}\n\nexport function expectedCommitHits123(repositoryCommitCount: number): number {\n return expectedOccurrencesFromSubstringProbability(repositoryCommitCount, 3)\n}\n\nexport function expectedCommitHitsSameNumbers(\n repositoryCommitCount: number\n): number {\n return repositoryCommitCount * (slidingWindows(5) / 16 ** 4)\n}\n\nexport function expectedCommitHitsHexspeak(\n repositoryCommitCount: number\n): number {\n const patterns = ['f00d', 'feed', 'cafe', 'c0ffee', 'deadbeef']\n\n const probability = patterns.reduce((sum, pattern) => {\n return sum + slidingWindows(pattern.length) / 16 ** pattern.length\n }, 0)\n\n return repositoryCommitCount * probability\n}\n\nexport function isRareEnough(\n expectedOccurrences: number,\n maxExpectedOccurrences?: number\n): boolean {\n if (maxExpectedOccurrences === undefined) {\n return true\n }\n\n return expectedOccurrences <= maxExpectedOccurrences\n}\n"],"mappings":"AAOA,SAAS,EAAe,EAAwB,CAC9C,OAAO,KAAK,IAAI,EAAG,GAAa,EAAS,EAAE,CAG7C,SAAS,EACP,EACA,EACQ,CACR,OACE,GACC,EAAe,EAAgB,CAAG,IAAM,GAI7C,SAAgB,EAAoB,EAAuB,CACzD,IAAI,EAAQ,EACR,EAAU,GAEd,KAAO,GAAW,GAChB,GAAS,EACT,GAAW,GAGb,OAAO,EAGT,SAAgB,EAAoC,EAAuB,CACzE,GAAI,EAAQ,GACV,MAAO,GAGT,IAAM,EAAS,EAAM,UAAU,CAAC,OAC1B,GAAuB,EAAS,GAAK,EACrC,EAAmB,KAAO,EAAS,GACnC,EAAsB,KAAK,MAAM,EAAQ,EAAiB,CAEhE,OAAO,EAAsB,KAAK,IAAI,EAAqB,EAAE,CAG/D,SAAgB,EAAoB,EAAuB,CAEzD,MADe,CAAC,IAAK,KAAM,KAAM,KAAM,KAAM,MAAO,MAAO,MAAM,CACnD,OAAQ,GAAU,GAAS,EAAM,CAAC,OAGlD,SAAgB,EAAkB,EAAuB,CACvD,IAAI,EAAQ,EAEZ,IAAK,IAAI,EAAS,EAAG,GAAU,EAAM,UAAU,CAAC,OAAQ,GAAU,EAClD,OAAO,SAAS,IAAI,OAAO,EAAO,CAAE,GAAG,EACxC,IACX,GAAS,GAIb,OAAO,EAGT,SAAgB,EAAsB,EAAuC,CAC3E,OAAO,EAA4C,EAAuB,EAAE,CAG9E,SAAgB,EAAsB,EAAuC,CAC3E,OAAO,EAA4C,EAAuB,EAAE,CAG9E,SAAgB,EAAsB,EAAuC,CAC3E,OAAO,EAA4C,EAAuB,EAAE,CAG9E,SAAgB,EACd,EACQ,CACR,OAAO,GAAyB,EAAe,EAAE,CAAG,IAAM,GAG5D,SAAgB,EACd,EACQ,CAOR,OAAO,EANU,CAAC,OAAQ,OAAQ,OAAQ,SAAU,WAAW,CAElC,QAAQ,EAAK,IACjC,EAAM,EAAe,EAAQ,OAAO,CAAG,IAAM,EAAQ,OAC3D,EAAE,CAKP,SAAgB,EACd,EACA,EACS,CAKT,OAJI,IAA2B,IAAA,GACtB,GAGF,GAAuB"} \ No newline at end of file diff --git a/dist/rules.mjs b/dist/rules.mjs index 4e7af62..475c5ca 100644 --- a/dist/rules.mjs +++ b/dist/rules.mjs @@ -1,2 +1,2 @@ -import{expectedAllSevens as e,expectedCommitHits123 as t,expectedCommitHits666 as n,expectedCommitHits777 as r,expectedCommitHitsHexspeak as i,expectedCommitHitsSameNumbers as a,expectedPowersOfTwo as o,expectedSingleNonzeroDigitPrNumbers as s}from"./rarity.mjs";import"./ajv-QtvvyOqs.mjs";import{validateRules as c}from"./validate.mjs";function l(e){let t;try{t=JSON.parse(e)}catch{throw Error(`Invalid JSON`)}if(c(t))return t;throw Error(`Invalid rules: ${JSON.stringify(c.errors)}`)}function u(e){let t=l(e),n=[];for(let e of t)try{n.push({kind:e.kind,rule:new RegExp(e.rule),message:e.message})}catch{throw Error(`Invalid rule: ${e.rule}`)}return n}const d={pr_reaches_contain_only_one_nonzero_digit:{id:`pr_reaches_contain_only_one_nonzero_digit`,kind:`pr`,rule:/(?:^[1-9]0+$)/,message:`Now pull request issue number reaches **{{prNum}}**. It's time to celebrate!`,expectedOccurrences:({prNum:e})=>s(e)},pr_reaches_power_of_2:{id:`pr_reaches_power_of_2`,kind:`pr`,rule:/(?:^(512|1024|2048|4096|8192|16384|32768|65536)$)/,message:`Now pull request issue number reaches **{{prNum}}** (power of 2). It's time to celebrate!`,expectedOccurrences:({prNum:e})=>o(e)},pr_reaches_777:{id:`pr_reaches_777`,kind:`pr`,rule:/(?:^7{3,}$)/,message:`Now pull request issue number reaches **{{prNum}}** (777). It's time to celebrate!`,expectedOccurrences:({prNum:t})=>e(t)},commit_hits_777:{id:`commit_hits_777`,kind:`commit`,rule:/(?:7{3,})/,message:"Commit `{{commitId}}` is lucky! It contains **{{matched}}**!.",expectedOccurrences:({repositoryCommitCount:e})=>r(e)},commit_hits_same_numbers:{id:`commit_hits_same_numbers`,kind:`commit`,rule:/(?:([0-9a-f])\1{4,})/,message:"Commit `{{commitId}}` is lucky! It contains **{{matched}}**!.",expectedOccurrences:({repositoryCommitCount:e})=>a(e)},commit_hits_123:{id:`commit_hits_123`,kind:`commit`,rule:/(?:123(?:4(?:5(?:6(?:7(?:8(?:9)?)?)?)?)?)?)/,message:"Commit `{{commitId}}` is lucky! It contains **{{matched}}**!.",expectedOccurrences:({repositoryCommitCount:e})=>t(e)},commit_hits_hexspeak:{id:`commit_hits_hexspeak`,kind:`commit`,rule:/(?:(?:f00d|feed|cafe|c0ffee|deadbeef|defecated|0ffice|badcable))/i,message:"Commit `{{commitId}}` is lucky! It contains **{{matched}}**!.",expectedOccurrences:({repositoryCommitCount:e})=>i(e)},commit_hits_666:{id:`commit_hits_666`,kind:`commit`,rule:/(?:666)/,message:"Commit `{{commitId}}` is unlucky... It contains **{{matched}}**!.",expectedOccurrences:({repositoryCommitCount:e})=>n(e)}};export{d as Rules,u as parseRules}; +import{expectedAllSevens as e,expectedCommitHits123 as t,expectedCommitHits666 as n,expectedCommitHits777 as r,expectedCommitHitsHexspeak as i,expectedCommitHitsSameNumbers as a,expectedPowersOfTwo as o,expectedSingleNonzeroDigitPrNumbers as s}from"./rarity.mjs";import"./ajv-QtvvyOqs.mjs";import{validateRules as c}from"./validate.mjs";function l(e){let t;try{t=JSON.parse(e)}catch{throw Error(`Invalid JSON`)}if(c(t))return t;throw Error(`Invalid rules: ${JSON.stringify(c.errors)}`)}function u(e){let t=l(e),n=[];for(let e of t)try{n.push({kind:e.kind,rule:new RegExp(e.rule),message:e.message})}catch{throw Error(`Invalid rule: ${e.rule}`)}return n}const d={pr_reaches_contain_only_one_nonzero_digit:{id:`pr_reaches_contain_only_one_nonzero_digit`,kind:`pr`,rule:/(?:^[1-9]0+$)/,message:`Now pull request issue number reaches **{{prNum}}**. It's time to celebrate!`,expectedOccurrences:({prNum:e})=>s(e)},pr_reaches_power_of_2:{id:`pr_reaches_power_of_2`,kind:`pr`,rule:/(?:^(512|1024|2048|4096|8192|16384|32768|65536)$)/,message:`Now pull request issue number reaches **{{prNum}}** (power of 2). It's time to celebrate!`,expectedOccurrences:({prNum:e})=>o(e)},pr_reaches_777:{id:`pr_reaches_777`,kind:`pr`,rule:/(?:^7{3,}$)/,message:`Now pull request issue number reaches **{{prNum}}** (777). It's time to celebrate!`,expectedOccurrences:({prNum:t})=>e(t)},commit_hits_777:{id:`commit_hits_777`,kind:`commit`,rule:/(?:7{3,})/,message:"Commit `{{commitId}}` is lucky! It contains **{{matched}}**!.",expectedOccurrences:({repositoryCommitCount:e})=>r(e)},commit_hits_same_numbers:{id:`commit_hits_same_numbers`,kind:`commit`,rule:/(?:([0-9a-f])\1{4,})/,message:"Commit `{{commitId}}` is lucky! It contains **{{matched}}**!.",expectedOccurrences:({repositoryCommitCount:e})=>a(e)},commit_hits_123:{id:`commit_hits_123`,kind:`commit`,rule:/(?:123(?:4(?:5(?:6(?:7(?:8(?:9)?)?)?)?)?)?)/,message:"Commit `{{commitId}}` is lucky! It contains **{{matched}}**!.",expectedOccurrences:({repositoryCommitCount:e})=>t(e)},commit_hits_hexspeak:{id:`commit_hits_hexspeak`,kind:`commit`,rule:/(?:(?:f00d|feed|cafe|c0ffee|deadbeef))/,message:"Commit `{{commitId}}` is lucky! It contains **{{matched}}**!.",expectedOccurrences:({repositoryCommitCount:e})=>i(e)},commit_hits_666:{id:`commit_hits_666`,kind:`commit`,rule:/(?:666)/,message:"Commit `{{commitId}}` is unlucky... It contains **{{matched}}**!.",expectedOccurrences:({repositoryCommitCount:e})=>n(e)}};export{d as Rules,u as parseRules}; //# sourceMappingURL=rules.mjs.map \ No newline at end of file diff --git a/dist/rules.mjs.map b/dist/rules.mjs.map index c5ca1ba..9a95d6c 100644 --- a/dist/rules.mjs.map +++ b/dist/rules.mjs.map @@ -1 +1 @@ -{"version":3,"file":"rules.mjs","names":[],"sources":["../src/rules.ts"],"sourcesContent":["import type {\n MessageForRuleSet,\n NamedMessageForRuleSet,\n RuleStringPattern,\n RuleStringPatterns,\n} from './interfaces'\nimport {\n expectedAllSevens,\n expectedCommitHits123,\n expectedCommitHits666,\n expectedCommitHits777,\n expectedCommitHitsHexspeak,\n expectedCommitHitsSameNumbers,\n expectedPowersOfTwo,\n expectedSingleNonzeroDigitPrNumbers,\n} from './rarity'\nimport { validateRules } from './validate'\n\nexport type { RuleStringPattern }\n\n/**\n * Parse the rule pattern from JSON string\n * this function does not validate the RegExp pattern\n * @param json {string} the JSON string\n * @returns {RuleStringPatterns} the rule pattern\n * @throws {Error} if the JSON is invalid\n * @throws {Error} if the rule pattern is invalid\n */\nfunction parseRulePatternFromJson(json: string): RuleStringPatterns {\n let parsed: unknown\n try {\n parsed = JSON.parse(json)\n } catch (_e: unknown) {\n throw new Error('Invalid JSON')\n }\n const validated = validateRules(parsed)\n if (validated) {\n return parsed as RuleStringPatterns\n }\n throw new Error(`Invalid rules: ${JSON.stringify(validateRules.errors)}`)\n}\n\n/**\n * Parse the rule pattern from JSON string and convert it to rule set\n * @param json {string} the JSON string\n * @returns {MessageForRuleSet} the rule set\n * @throws {Error} if the rule is invalid\n * @throws {Error} if the JSON is invalid\n * @throws {Error} if the rule set is invalid\n */\nexport function parseRules(json: string): MessageForRuleSet {\n const parsed = parseRulePatternFromJson(json)\n const rules: MessageForRuleSet = []\n for (const rule of parsed) {\n try {\n rules.push({\n kind: rule.kind,\n rule: new RegExp(rule.rule),\n message: rule.message,\n })\n } catch (_e: unknown) {\n throw new Error(`Invalid rule: ${rule.rule}`)\n }\n }\n return rules\n}\n\nexport const Rules: NamedMessageForRuleSet = {\n pr_reaches_contain_only_one_nonzero_digit: {\n id: 'pr_reaches_contain_only_one_nonzero_digit',\n kind: 'pr',\n rule: /(?:^[1-9]0+$)/,\n message: `Now pull request issue number reaches **{{prNum}}**. It's time to celebrate!`,\n expectedOccurrences: ({ prNum }) =>\n expectedSingleNonzeroDigitPrNumbers(prNum),\n },\n pr_reaches_power_of_2: {\n id: 'pr_reaches_power_of_2',\n kind: 'pr',\n rule: /(?:^(512|1024|2048|4096|8192|16384|32768|65536)$)/,\n message: `Now pull request issue number reaches **{{prNum}}** (power of 2). It's time to celebrate!`,\n expectedOccurrences: ({ prNum }) => expectedPowersOfTwo(prNum),\n },\n pr_reaches_777: {\n id: 'pr_reaches_777',\n kind: 'pr',\n rule: /(?:^7{3,}$)/,\n message: `Now pull request issue number reaches **{{prNum}}** (777). It's time to celebrate!`,\n expectedOccurrences: ({ prNum }) => expectedAllSevens(prNum),\n },\n commit_hits_777: {\n id: 'commit_hits_777',\n kind: 'commit',\n rule: /(?:7{3,})/,\n message: 'Commit `{{commitId}}` is lucky! It contains **{{matched}}**!.',\n expectedOccurrences: ({ repositoryCommitCount }) =>\n expectedCommitHits777(repositoryCommitCount),\n },\n commit_hits_same_numbers: {\n id: 'commit_hits_same_numbers',\n kind: 'commit',\n rule: /(?:([0-9a-f])\\1{4,})/,\n message: 'Commit `{{commitId}}` is lucky! It contains **{{matched}}**!.',\n expectedOccurrences: ({ repositoryCommitCount }) =>\n expectedCommitHitsSameNumbers(repositoryCommitCount),\n },\n commit_hits_123: {\n id: 'commit_hits_123',\n kind: 'commit',\n rule: /(?:123(?:4(?:5(?:6(?:7(?:8(?:9)?)?)?)?)?)?)/,\n message: 'Commit `{{commitId}}` is lucky! It contains **{{matched}}**!.',\n expectedOccurrences: ({ repositoryCommitCount }) =>\n expectedCommitHits123(repositoryCommitCount),\n },\n commit_hits_hexspeak: {\n id: 'commit_hits_hexspeak',\n kind: 'commit',\n rule: /(?:(?:f00d|feed|cafe|c0ffee|deadbeef|defecated|0ffice|badcable))/i,\n message: 'Commit `{{commitId}}` is lucky! It contains **{{matched}}**!.',\n expectedOccurrences: ({ repositoryCommitCount }) =>\n expectedCommitHitsHexspeak(repositoryCommitCount),\n },\n commit_hits_666: {\n id: 'commit_hits_666',\n kind: 'commit',\n rule: /(?:666)/,\n message:\n 'Commit `{{commitId}}` is unlucky... It contains **{{matched}}**!.',\n expectedOccurrences: ({ repositoryCommitCount }) =>\n expectedCommitHits666(repositoryCommitCount),\n },\n} as const\n\nexport type RulesKey = keyof typeof Rules\n"],"mappings":"iVA4BA,SAAS,EAAyB,EAAkC,CAClE,IAAI,EACJ,GAAI,CACF,EAAS,KAAK,MAAM,EAAK,MACL,CACpB,MAAU,MAAM,eAAe,CAGjC,GADkB,EAAc,EAAO,CAErC,OAAO,EAET,MAAU,MAAM,kBAAkB,KAAK,UAAU,EAAc,OAAO,GAAG,CAW3E,SAAgB,EAAW,EAAiC,CAC1D,IAAM,EAAS,EAAyB,EAAK,CACvC,EAA2B,EAAE,CACnC,IAAK,IAAM,KAAQ,EACjB,GAAI,CACF,EAAM,KAAK,CACT,KAAM,EAAK,KACX,KAAM,IAAI,OAAO,EAAK,KAAK,CAC3B,QAAS,EAAK,QACf,CAAC,MACkB,CACpB,MAAU,MAAM,iBAAiB,EAAK,OAAO,CAGjD,OAAO,EAGT,MAAa,EAAgC,CAC3C,0CAA2C,CACzC,GAAI,4CACJ,KAAM,KACN,KAAM,gBACN,QAAS,+EACT,qBAAsB,CAAE,WACtB,EAAoC,EAAA,CACvC,CACD,sBAAuB,CACrB,GAAI,wBACJ,KAAM,KACN,KAAM,oDACN,QAAS,4FACT,qBAAsB,CAAE,WAAY,EAAoB,EAAA,CACzD,CACD,eAAgB,CACd,GAAI,iBACJ,KAAM,KACN,KAAM,cACN,QAAS,qFACT,qBAAsB,CAAE,WAAY,EAAkB,EAAA,CACvD,CACD,gBAAiB,CACf,GAAI,kBACJ,KAAM,SACN,KAAM,YACN,QAAS,gEACT,qBAAsB,CAAE,2BACtB,EAAsB,EAAA,CACzB,CACD,yBAA0B,CACxB,GAAI,2BACJ,KAAM,SACN,KAAM,uBACN,QAAS,gEACT,qBAAsB,CAAE,2BACtB,EAA8B,EAAA,CACjC,CACD,gBAAiB,CACf,GAAI,kBACJ,KAAM,SACN,KAAM,8CACN,QAAS,gEACT,qBAAsB,CAAE,2BACtB,EAAsB,EAAA,CACzB,CACD,qBAAsB,CACpB,GAAI,uBACJ,KAAM,SACN,KAAM,oEACN,QAAS,gEACT,qBAAsB,CAAE,2BACtB,EAA2B,EAAA,CAC9B,CACD,gBAAiB,CACf,GAAI,kBACJ,KAAM,SACN,KAAM,UACN,QACE,oEACF,qBAAsB,CAAE,2BACtB,EAAsB,EAAA,EAE3B"} \ No newline at end of file +{"version":3,"file":"rules.mjs","names":[],"sources":["../src/rules.ts"],"sourcesContent":["import type {\n MessageForRuleSet,\n NamedMessageForRuleSet,\n RuleStringPattern,\n RuleStringPatterns,\n} from './interfaces'\nimport {\n expectedAllSevens,\n expectedCommitHits123,\n expectedCommitHits666,\n expectedCommitHits777,\n expectedCommitHitsHexspeak,\n expectedCommitHitsSameNumbers,\n expectedPowersOfTwo,\n expectedSingleNonzeroDigitPrNumbers,\n} from './rarity'\nimport { validateRules } from './validate'\n\nexport type { RuleStringPattern }\n\n/**\n * Parse the rule pattern from JSON string\n * this function does not validate the RegExp pattern\n * @param json {string} the JSON string\n * @returns {RuleStringPatterns} the rule pattern\n * @throws {Error} if the JSON is invalid\n * @throws {Error} if the rule pattern is invalid\n */\nfunction parseRulePatternFromJson(json: string): RuleStringPatterns {\n let parsed: unknown\n try {\n parsed = JSON.parse(json)\n } catch (_e: unknown) {\n throw new Error('Invalid JSON')\n }\n const validated = validateRules(parsed)\n if (validated) {\n return parsed as RuleStringPatterns\n }\n throw new Error(`Invalid rules: ${JSON.stringify(validateRules.errors)}`)\n}\n\n/**\n * Parse the rule pattern from JSON string and convert it to rule set\n * @param json {string} the JSON string\n * @returns {MessageForRuleSet} the rule set\n * @throws {Error} if the rule is invalid\n * @throws {Error} if the JSON is invalid\n * @throws {Error} if the rule set is invalid\n */\nexport function parseRules(json: string): MessageForRuleSet {\n const parsed = parseRulePatternFromJson(json)\n const rules: MessageForRuleSet = []\n for (const rule of parsed) {\n try {\n rules.push({\n kind: rule.kind,\n rule: new RegExp(rule.rule),\n message: rule.message,\n })\n } catch (_e: unknown) {\n throw new Error(`Invalid rule: ${rule.rule}`)\n }\n }\n return rules\n}\n\nexport const Rules: NamedMessageForRuleSet = {\n pr_reaches_contain_only_one_nonzero_digit: {\n id: 'pr_reaches_contain_only_one_nonzero_digit',\n kind: 'pr',\n rule: /(?:^[1-9]0+$)/,\n message: `Now pull request issue number reaches **{{prNum}}**. It's time to celebrate!`,\n expectedOccurrences: ({ prNum }) =>\n expectedSingleNonzeroDigitPrNumbers(prNum),\n },\n pr_reaches_power_of_2: {\n id: 'pr_reaches_power_of_2',\n kind: 'pr',\n rule: /(?:^(512|1024|2048|4096|8192|16384|32768|65536)$)/,\n message: `Now pull request issue number reaches **{{prNum}}** (power of 2). It's time to celebrate!`,\n expectedOccurrences: ({ prNum }) => expectedPowersOfTwo(prNum),\n },\n pr_reaches_777: {\n id: 'pr_reaches_777',\n kind: 'pr',\n rule: /(?:^7{3,}$)/,\n message: `Now pull request issue number reaches **{{prNum}}** (777). It's time to celebrate!`,\n expectedOccurrences: ({ prNum }) => expectedAllSevens(prNum),\n },\n commit_hits_777: {\n id: 'commit_hits_777',\n kind: 'commit',\n rule: /(?:7{3,})/,\n message: 'Commit `{{commitId}}` is lucky! It contains **{{matched}}**!.',\n expectedOccurrences: ({ repositoryCommitCount }) =>\n expectedCommitHits777(repositoryCommitCount),\n },\n commit_hits_same_numbers: {\n id: 'commit_hits_same_numbers',\n kind: 'commit',\n rule: /(?:([0-9a-f])\\1{4,})/,\n message: 'Commit `{{commitId}}` is lucky! It contains **{{matched}}**!.',\n expectedOccurrences: ({ repositoryCommitCount }) =>\n expectedCommitHitsSameNumbers(repositoryCommitCount),\n },\n commit_hits_123: {\n id: 'commit_hits_123',\n kind: 'commit',\n rule: /(?:123(?:4(?:5(?:6(?:7(?:8(?:9)?)?)?)?)?)?)/,\n message: 'Commit `{{commitId}}` is lucky! It contains **{{matched}}**!.',\n expectedOccurrences: ({ repositoryCommitCount }) =>\n expectedCommitHits123(repositoryCommitCount),\n },\n commit_hits_hexspeak: {\n id: 'commit_hits_hexspeak',\n kind: 'commit',\n rule: /(?:(?:f00d|feed|cafe|c0ffee|deadbeef))/,\n message: 'Commit `{{commitId}}` is lucky! It contains **{{matched}}**!.',\n expectedOccurrences: ({ repositoryCommitCount }) =>\n expectedCommitHitsHexspeak(repositoryCommitCount),\n },\n commit_hits_666: {\n id: 'commit_hits_666',\n kind: 'commit',\n rule: /(?:666)/,\n message:\n 'Commit `{{commitId}}` is unlucky... It contains **{{matched}}**!.',\n expectedOccurrences: ({ repositoryCommitCount }) =>\n expectedCommitHits666(repositoryCommitCount),\n },\n} as const\n\nexport type RulesKey = keyof typeof Rules\n"],"mappings":"iVA4BA,SAAS,EAAyB,EAAkC,CAClE,IAAI,EACJ,GAAI,CACF,EAAS,KAAK,MAAM,EAAK,MACL,CACpB,MAAU,MAAM,eAAe,CAGjC,GADkB,EAAc,EAAO,CAErC,OAAO,EAET,MAAU,MAAM,kBAAkB,KAAK,UAAU,EAAc,OAAO,GAAG,CAW3E,SAAgB,EAAW,EAAiC,CAC1D,IAAM,EAAS,EAAyB,EAAK,CACvC,EAA2B,EAAE,CACnC,IAAK,IAAM,KAAQ,EACjB,GAAI,CACF,EAAM,KAAK,CACT,KAAM,EAAK,KACX,KAAM,IAAI,OAAO,EAAK,KAAK,CAC3B,QAAS,EAAK,QACf,CAAC,MACkB,CACpB,MAAU,MAAM,iBAAiB,EAAK,OAAO,CAGjD,OAAO,EAGT,MAAa,EAAgC,CAC3C,0CAA2C,CACzC,GAAI,4CACJ,KAAM,KACN,KAAM,gBACN,QAAS,+EACT,qBAAsB,CAAE,WACtB,EAAoC,EAAA,CACvC,CACD,sBAAuB,CACrB,GAAI,wBACJ,KAAM,KACN,KAAM,oDACN,QAAS,4FACT,qBAAsB,CAAE,WAAY,EAAoB,EAAA,CACzD,CACD,eAAgB,CACd,GAAI,iBACJ,KAAM,KACN,KAAM,cACN,QAAS,qFACT,qBAAsB,CAAE,WAAY,EAAkB,EAAA,CACvD,CACD,gBAAiB,CACf,GAAI,kBACJ,KAAM,SACN,KAAM,YACN,QAAS,gEACT,qBAAsB,CAAE,2BACtB,EAAsB,EAAA,CACzB,CACD,yBAA0B,CACxB,GAAI,2BACJ,KAAM,SACN,KAAM,uBACN,QAAS,gEACT,qBAAsB,CAAE,2BACtB,EAA8B,EAAA,CACjC,CACD,gBAAiB,CACf,GAAI,kBACJ,KAAM,SACN,KAAM,8CACN,QAAS,gEACT,qBAAsB,CAAE,2BACtB,EAAsB,EAAA,CACzB,CACD,qBAAsB,CACpB,GAAI,uBACJ,KAAM,SACN,KAAM,yCACN,QAAS,gEACT,qBAAsB,CAAE,2BACtB,EAA2B,EAAA,CAC9B,CACD,gBAAiB,CACf,GAAI,kBACJ,KAAM,SACN,KAAM,UACN,QACE,oEACF,qBAAsB,CAAE,2BACtB,EAAsB,EAAA,EAE3B"} \ No newline at end of file