@0xbigboss/gh-pulse-core 1.0.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.
Files changed (73) hide show
  1. package/dist/cache.cjs +312 -0
  2. package/dist/cache.d.cts +58 -0
  3. package/dist/cache.d.ts +58 -0
  4. package/dist/cache.js +303 -0
  5. package/dist/config.cjs +182 -0
  6. package/dist/config.d.cts +248 -0
  7. package/dist/config.d.ts +248 -0
  8. package/dist/config.js +172 -0
  9. package/dist/filters.cjs +16 -0
  10. package/dist/filters.d.cts +3 -0
  11. package/dist/filters.d.ts +3 -0
  12. package/dist/filters.js +12 -0
  13. package/dist/github.cjs +240 -0
  14. package/dist/github.d.cts +46 -0
  15. package/dist/github.d.ts +46 -0
  16. package/dist/github.js +235 -0
  17. package/dist/index.cjs +28 -0
  18. package/dist/index.d.cts +11 -0
  19. package/dist/index.d.ts +11 -0
  20. package/dist/index.js +11 -0
  21. package/dist/reports/context.cjs +8 -0
  22. package/dist/reports/context.d.cts +7 -0
  23. package/dist/reports/context.d.ts +7 -0
  24. package/dist/reports/context.js +5 -0
  25. package/dist/reports/exec.cjs +160 -0
  26. package/dist/reports/exec.d.cts +6 -0
  27. package/dist/reports/exec.d.ts +6 -0
  28. package/dist/reports/exec.js +157 -0
  29. package/dist/reports/index.cjs +21 -0
  30. package/dist/reports/index.d.cts +5 -0
  31. package/dist/reports/index.d.ts +5 -0
  32. package/dist/reports/index.js +5 -0
  33. package/dist/reports/meta.cjs +15 -0
  34. package/dist/reports/meta.d.cts +12 -0
  35. package/dist/reports/meta.d.ts +12 -0
  36. package/dist/reports/meta.js +12 -0
  37. package/dist/reports/personal.cjs +90 -0
  38. package/dist/reports/personal.d.cts +8 -0
  39. package/dist/reports/personal.d.ts +8 -0
  40. package/dist/reports/personal.js +87 -0
  41. package/dist/reports/team.cjs +127 -0
  42. package/dist/reports/team.d.cts +6 -0
  43. package/dist/reports/team.d.ts +6 -0
  44. package/dist/reports/team.js +124 -0
  45. package/dist/reports/types.cjs +2 -0
  46. package/dist/reports/types.d.cts +144 -0
  47. package/dist/reports/types.d.ts +144 -0
  48. package/dist/reports/types.js +1 -0
  49. package/dist/reports/utils.cjs +71 -0
  50. package/dist/reports/utils.d.cts +6 -0
  51. package/dist/reports/utils.d.ts +6 -0
  52. package/dist/reports/utils.js +65 -0
  53. package/dist/repos.cjs +102 -0
  54. package/dist/repos.d.cts +12 -0
  55. package/dist/repos.d.ts +12 -0
  56. package/dist/repos.js +96 -0
  57. package/dist/sync.cjs +360 -0
  58. package/dist/sync.d.cts +24 -0
  59. package/dist/sync.d.ts +24 -0
  60. package/dist/sync.js +357 -0
  61. package/dist/team.cjs +45 -0
  62. package/dist/team.d.cts +10 -0
  63. package/dist/team.d.ts +10 -0
  64. package/dist/team.js +42 -0
  65. package/dist/time.cjs +153 -0
  66. package/dist/time.d.cts +13 -0
  67. package/dist/time.d.ts +13 -0
  68. package/dist/time.js +145 -0
  69. package/dist/types.cjs +2 -0
  70. package/dist/types.d.cts +133 -0
  71. package/dist/types.d.ts +133 -0
  72. package/dist/types.js +1 -0
  73. package/package.json +29 -0
package/dist/time.js ADDED
@@ -0,0 +1,145 @@
1
+ const DAY_MS = 24 * 60 * 60 * 1000;
2
+ export function periodToRange(period, now, timeZone) {
3
+ const end = now.getTime();
4
+ switch (period) {
5
+ case 'today': {
6
+ const start = startOfDay(now, timeZone);
7
+ return { start, end, label: 'today' };
8
+ }
9
+ case 'week': {
10
+ const start = end - 7 * DAY_MS;
11
+ return { start, end, label: 'last 7 days' };
12
+ }
13
+ case 'sprint': {
14
+ const start = end - 14 * DAY_MS;
15
+ return { start, end, label: 'last 14 days' };
16
+ }
17
+ case 'month': {
18
+ const start = end - 30 * DAY_MS;
19
+ return { start, end, label: 'last 30 days' };
20
+ }
21
+ case 'quarter': {
22
+ const start = end - 90 * DAY_MS;
23
+ return { start, end, label: 'last 90 days' };
24
+ }
25
+ default: {
26
+ const _exhaustive = period;
27
+ throw new Error(`Unhandled period: ${_exhaustive}`);
28
+ }
29
+ }
30
+ }
31
+ export function resolveSince(input, now) {
32
+ if (isRelative(input)) {
33
+ return now.getTime() - parseRelativeDuration(input);
34
+ }
35
+ return parseAbsoluteDate(input);
36
+ }
37
+ export function resolveUntil(input, now) {
38
+ if (isRelative(input)) {
39
+ return now.getTime() - parseRelativeDuration(input);
40
+ }
41
+ return parseAbsoluteDate(input);
42
+ }
43
+ export function formatTimestamp(timestamp, timeZone) {
44
+ const formatter = new Intl.DateTimeFormat('en-US', {
45
+ timeZone,
46
+ year: 'numeric',
47
+ month: '2-digit',
48
+ day: '2-digit',
49
+ hour: '2-digit',
50
+ minute: '2-digit',
51
+ second: '2-digit',
52
+ hour12: false,
53
+ });
54
+ return formatter.format(new Date(timestamp));
55
+ }
56
+ export function diffInDays(start, end) {
57
+ return Math.floor((end - start) / DAY_MS);
58
+ }
59
+ export function parseRelativeDuration(input) {
60
+ const match = input.trim().match(/^(\d+)([dwmy])$/i);
61
+ if (!match) {
62
+ throw new Error(`Invalid relative duration: ${input}`);
63
+ }
64
+ const value = Number(match[1]);
65
+ const unitRaw = match[2];
66
+ if (!unitRaw) {
67
+ throw new Error(`Invalid relative duration unit: ${input}`);
68
+ }
69
+ const unit = unitRaw.toLowerCase();
70
+ switch (unit) {
71
+ case 'd':
72
+ return value * DAY_MS;
73
+ case 'w':
74
+ return value * 7 * DAY_MS;
75
+ case 'm':
76
+ return value * 30 * DAY_MS;
77
+ case 'y':
78
+ return value * 365 * DAY_MS;
79
+ default: {
80
+ const _exhaustive = unit;
81
+ throw new Error(`Unhandled duration unit: ${_exhaustive}`);
82
+ }
83
+ }
84
+ }
85
+ function isRelative(input) {
86
+ return /^(\d+)([dwmy])$/i.test(input.trim());
87
+ }
88
+ function parseAbsoluteDate(input) {
89
+ const parsed = Date.parse(input);
90
+ if (Number.isNaN(parsed)) {
91
+ throw new Error(`Invalid date: ${input}`);
92
+ }
93
+ return parsed;
94
+ }
95
+ function startOfDay(now, timeZone) {
96
+ const parts = getZonedParts(now, timeZone);
97
+ const utcMs = zonedTimeToUtcMs({
98
+ ...parts,
99
+ hour: 0,
100
+ minute: 0,
101
+ second: 0,
102
+ }, timeZone);
103
+ return utcMs;
104
+ }
105
+ function getZonedParts(date, timeZone) {
106
+ const formatter = new Intl.DateTimeFormat('en-US', {
107
+ timeZone,
108
+ hour12: false,
109
+ year: 'numeric',
110
+ month: '2-digit',
111
+ day: '2-digit',
112
+ hour: '2-digit',
113
+ minute: '2-digit',
114
+ second: '2-digit',
115
+ });
116
+ const parts = formatter.formatToParts(date);
117
+ const lookup = Object.fromEntries(parts.filter((part) => part.type !== 'literal').map((part) => [part.type, part.value]));
118
+ const year = lookup.year;
119
+ const month = lookup.month;
120
+ const day = lookup.day;
121
+ const hour = lookup.hour;
122
+ const minute = lookup.minute;
123
+ const second = lookup.second;
124
+ if (!year || !month || !day || !hour || !minute || !second) {
125
+ throw new Error(`Failed to resolve timezone parts for ${timeZone}`);
126
+ }
127
+ return {
128
+ year: Number(year),
129
+ month: Number(month),
130
+ day: Number(day),
131
+ hour: Number(hour),
132
+ minute: Number(minute),
133
+ second: Number(second),
134
+ };
135
+ }
136
+ function zonedTimeToUtcMs(parts, timeZone) {
137
+ const utcGuess = Date.UTC(parts.year, parts.month - 1, parts.day, parts.hour, parts.minute, parts.second);
138
+ const offset = getTimeZoneOffsetMs(new Date(utcGuess), timeZone);
139
+ return utcGuess - offset;
140
+ }
141
+ function getTimeZoneOffsetMs(date, timeZone) {
142
+ const parts = getZonedParts(date, timeZone);
143
+ const utcFromParts = Date.UTC(parts.year, parts.month - 1, parts.day, parts.hour, parts.minute, parts.second);
144
+ return utcFromParts - date.getTime();
145
+ }
package/dist/types.cjs ADDED
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,133 @@
1
+ export type GitHubUsername = string;
2
+ export type RepoFullName = string;
3
+ export type PullRequestId = number;
4
+ export type CommitSha = string;
5
+ export type LinearIssueId = string;
6
+ export type Timestamp = number;
7
+ export type Duration = number;
8
+ export type PRState = 'open' | 'closed' | 'merged';
9
+ export type PRDraftState = 'draft' | 'ready';
10
+ export type ReviewState = 'pending' | 'commented' | 'approved' | 'changes_requested' | 'dismissed';
11
+ export interface ProgressEvent {
12
+ phase: 'resolve' | 'sync';
13
+ message: string;
14
+ current?: number;
15
+ total?: number;
16
+ }
17
+ export interface PROpenedEvent {
18
+ type: 'pr_opened';
19
+ repo: RepoFullName;
20
+ pr_number: PullRequestId;
21
+ author: GitHubUsername;
22
+ opened_at: Timestamp;
23
+ is_draft: boolean;
24
+ }
25
+ export interface PRReadyEvent {
26
+ type: 'pr_ready';
27
+ repo: RepoFullName;
28
+ pr_number: PullRequestId;
29
+ ready_at: Timestamp;
30
+ }
31
+ export interface PRMergedEvent {
32
+ type: 'pr_merged';
33
+ repo: RepoFullName;
34
+ pr_number: PullRequestId;
35
+ merged_at: Timestamp;
36
+ merged_by: GitHubUsername;
37
+ }
38
+ export interface PRClosedEvent {
39
+ type: 'pr_closed';
40
+ repo: RepoFullName;
41
+ pr_number: PullRequestId;
42
+ closed_at: Timestamp;
43
+ }
44
+ export interface ReviewSubmittedEvent {
45
+ type: 'review_submitted';
46
+ repo: RepoFullName;
47
+ pr_number: PullRequestId;
48
+ reviewer: GitHubUsername;
49
+ state: ReviewState;
50
+ submitted_at: Timestamp;
51
+ }
52
+ export interface CommentAddedEvent {
53
+ type: 'comment_added';
54
+ repo: RepoFullName;
55
+ pr_number: PullRequestId;
56
+ author: GitHubUsername;
57
+ commented_at: Timestamp;
58
+ }
59
+ export interface CommitPushedEvent {
60
+ type: 'commit_pushed';
61
+ repo: RepoFullName;
62
+ sha: CommitSha;
63
+ author: GitHubUsername;
64
+ committed_at: Timestamp;
65
+ message: string;
66
+ additions: number;
67
+ deletions: number;
68
+ files_changed: number;
69
+ }
70
+ export type Event = PROpenedEvent | PRReadyEvent | PRMergedEvent | PRClosedEvent | ReviewSubmittedEvent | CommentAddedEvent | CommitPushedEvent;
71
+ export interface PullRequest {
72
+ repo: RepoFullName;
73
+ number: PullRequestId;
74
+ title: string;
75
+ state: PRState;
76
+ draft: boolean;
77
+ author: GitHubUsername;
78
+ created_at: Timestamp;
79
+ updated_at: Timestamp;
80
+ merged_at: Timestamp | null;
81
+ closed_at: Timestamp | null;
82
+ additions: number;
83
+ deletions: number;
84
+ commits: number;
85
+ requested_reviewers: GitHubUsername[];
86
+ labels: string[];
87
+ linked_issues: LinearIssueId[];
88
+ }
89
+ export interface Commit {
90
+ repo: RepoFullName;
91
+ sha: CommitSha;
92
+ author: GitHubUsername;
93
+ committed_at: Timestamp;
94
+ message: string;
95
+ additions: number;
96
+ deletions: number;
97
+ files_changed: number;
98
+ }
99
+ export interface CycleTimeMetrics {
100
+ draft_time: Duration | null;
101
+ review_time: Duration;
102
+ merge_time: Duration;
103
+ total_time: Duration;
104
+ }
105
+ export interface ReviewEngagement {
106
+ time_to_first_comment: Duration | null;
107
+ time_to_first_review: Duration | null;
108
+ review_count: number;
109
+ comment_count: number;
110
+ }
111
+ export type LinearIssueState = 'backlog' | 'unstarted' | 'started' | 'completed' | 'canceled';
112
+ export interface LinearIssue {
113
+ id: LinearIssueId;
114
+ identifier: string;
115
+ title: string;
116
+ state: LinearIssueState;
117
+ assignee: string | null;
118
+ created_at: Timestamp;
119
+ completed_at: Timestamp | null;
120
+ linked_prs: Array<{
121
+ repo: RepoFullName;
122
+ number: PullRequestId;
123
+ }>;
124
+ }
125
+ export type LinearCorrelation = {
126
+ method: 'branch_name';
127
+ pattern: RegExp;
128
+ } | {
129
+ method: 'pr_body';
130
+ pattern: RegExp;
131
+ } | {
132
+ method: 'linear_api';
133
+ };
@@ -0,0 +1,133 @@
1
+ export type GitHubUsername = string;
2
+ export type RepoFullName = string;
3
+ export type PullRequestId = number;
4
+ export type CommitSha = string;
5
+ export type LinearIssueId = string;
6
+ export type Timestamp = number;
7
+ export type Duration = number;
8
+ export type PRState = 'open' | 'closed' | 'merged';
9
+ export type PRDraftState = 'draft' | 'ready';
10
+ export type ReviewState = 'pending' | 'commented' | 'approved' | 'changes_requested' | 'dismissed';
11
+ export interface ProgressEvent {
12
+ phase: 'resolve' | 'sync';
13
+ message: string;
14
+ current?: number;
15
+ total?: number;
16
+ }
17
+ export interface PROpenedEvent {
18
+ type: 'pr_opened';
19
+ repo: RepoFullName;
20
+ pr_number: PullRequestId;
21
+ author: GitHubUsername;
22
+ opened_at: Timestamp;
23
+ is_draft: boolean;
24
+ }
25
+ export interface PRReadyEvent {
26
+ type: 'pr_ready';
27
+ repo: RepoFullName;
28
+ pr_number: PullRequestId;
29
+ ready_at: Timestamp;
30
+ }
31
+ export interface PRMergedEvent {
32
+ type: 'pr_merged';
33
+ repo: RepoFullName;
34
+ pr_number: PullRequestId;
35
+ merged_at: Timestamp;
36
+ merged_by: GitHubUsername;
37
+ }
38
+ export interface PRClosedEvent {
39
+ type: 'pr_closed';
40
+ repo: RepoFullName;
41
+ pr_number: PullRequestId;
42
+ closed_at: Timestamp;
43
+ }
44
+ export interface ReviewSubmittedEvent {
45
+ type: 'review_submitted';
46
+ repo: RepoFullName;
47
+ pr_number: PullRequestId;
48
+ reviewer: GitHubUsername;
49
+ state: ReviewState;
50
+ submitted_at: Timestamp;
51
+ }
52
+ export interface CommentAddedEvent {
53
+ type: 'comment_added';
54
+ repo: RepoFullName;
55
+ pr_number: PullRequestId;
56
+ author: GitHubUsername;
57
+ commented_at: Timestamp;
58
+ }
59
+ export interface CommitPushedEvent {
60
+ type: 'commit_pushed';
61
+ repo: RepoFullName;
62
+ sha: CommitSha;
63
+ author: GitHubUsername;
64
+ committed_at: Timestamp;
65
+ message: string;
66
+ additions: number;
67
+ deletions: number;
68
+ files_changed: number;
69
+ }
70
+ export type Event = PROpenedEvent | PRReadyEvent | PRMergedEvent | PRClosedEvent | ReviewSubmittedEvent | CommentAddedEvent | CommitPushedEvent;
71
+ export interface PullRequest {
72
+ repo: RepoFullName;
73
+ number: PullRequestId;
74
+ title: string;
75
+ state: PRState;
76
+ draft: boolean;
77
+ author: GitHubUsername;
78
+ created_at: Timestamp;
79
+ updated_at: Timestamp;
80
+ merged_at: Timestamp | null;
81
+ closed_at: Timestamp | null;
82
+ additions: number;
83
+ deletions: number;
84
+ commits: number;
85
+ requested_reviewers: GitHubUsername[];
86
+ labels: string[];
87
+ linked_issues: LinearIssueId[];
88
+ }
89
+ export interface Commit {
90
+ repo: RepoFullName;
91
+ sha: CommitSha;
92
+ author: GitHubUsername;
93
+ committed_at: Timestamp;
94
+ message: string;
95
+ additions: number;
96
+ deletions: number;
97
+ files_changed: number;
98
+ }
99
+ export interface CycleTimeMetrics {
100
+ draft_time: Duration | null;
101
+ review_time: Duration;
102
+ merge_time: Duration;
103
+ total_time: Duration;
104
+ }
105
+ export interface ReviewEngagement {
106
+ time_to_first_comment: Duration | null;
107
+ time_to_first_review: Duration | null;
108
+ review_count: number;
109
+ comment_count: number;
110
+ }
111
+ export type LinearIssueState = 'backlog' | 'unstarted' | 'started' | 'completed' | 'canceled';
112
+ export interface LinearIssue {
113
+ id: LinearIssueId;
114
+ identifier: string;
115
+ title: string;
116
+ state: LinearIssueState;
117
+ assignee: string | null;
118
+ created_at: Timestamp;
119
+ completed_at: Timestamp | null;
120
+ linked_prs: Array<{
121
+ repo: RepoFullName;
122
+ number: PullRequestId;
123
+ }>;
124
+ }
125
+ export type LinearCorrelation = {
126
+ method: 'branch_name';
127
+ pattern: RegExp;
128
+ } | {
129
+ method: 'pr_body';
130
+ pattern: RegExp;
131
+ } | {
132
+ method: 'linear_api';
133
+ };
package/dist/types.js ADDED
@@ -0,0 +1 @@
1
+ export {};
package/package.json ADDED
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "@0xbigboss/gh-pulse-core",
3
+ "version": "1.0.0",
4
+ "files": [
5
+ "dist"
6
+ ],
7
+ "type": "module",
8
+ "main": "./dist/index.cjs",
9
+ "module": "./dist/index.js",
10
+ "types": "./dist/index.d.cts",
11
+ "exports": {
12
+ ".": {
13
+ "types": "./dist/index.d.cts",
14
+ "import": "./dist/index.js",
15
+ "require": "./dist/index.cjs"
16
+ }
17
+ },
18
+ "scripts": {
19
+ "build": "zshy"
20
+ },
21
+ "dependencies": {
22
+ "@octokit/types": "^13.10.0",
23
+ "octokit": "^3.2.1",
24
+ "picomatch": "^4.0.2",
25
+ "yaml": "^2.7.0",
26
+ "zod": "^3.25.7"
27
+ },
28
+ "zshy": "./src/index.ts"
29
+ }