@hesed/sentry 0.1.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 (47) hide show
  1. package/README.md +453 -0
  2. package/bin/dev.cmd +3 -0
  3. package/bin/dev.js +5 -0
  4. package/bin/run.cmd +3 -0
  5. package/bin/run.js +5 -0
  6. package/dist/commands/sentry/auth/add.d.ts +14 -0
  7. package/dist/commands/sentry/auth/add.js +56 -0
  8. package/dist/commands/sentry/auth/test.d.ts +10 -0
  9. package/dist/commands/sentry/auth/test.js +32 -0
  10. package/dist/commands/sentry/auth/update.d.ts +14 -0
  11. package/dist/commands/sentry/auth/update.js +68 -0
  12. package/dist/commands/sentry/event/get.d.ts +13 -0
  13. package/dist/commands/sentry/event/get.js +31 -0
  14. package/dist/commands/sentry/event/source-maps.d.ts +15 -0
  15. package/dist/commands/sentry/event/source-maps.js +40 -0
  16. package/dist/commands/sentry/issue/event.d.ts +13 -0
  17. package/dist/commands/sentry/issue/event.js +34 -0
  18. package/dist/commands/sentry/issue/events.d.ts +18 -0
  19. package/dist/commands/sentry/issue/events.js +47 -0
  20. package/dist/commands/sentry/issue/get.d.ts +12 -0
  21. package/dist/commands/sentry/issue/get.js +28 -0
  22. package/dist/commands/sentry/issue/hashes.d.ts +13 -0
  23. package/dist/commands/sentry/issue/hashes.js +32 -0
  24. package/dist/commands/sentry/issue/tag-values.d.ts +15 -0
  25. package/dist/commands/sentry/issue/tag-values.js +36 -0
  26. package/dist/commands/sentry/issue/tag.d.ts +14 -0
  27. package/dist/commands/sentry/issue/tag.js +33 -0
  28. package/dist/commands/sentry/issue/update.d.ts +18 -0
  29. package/dist/commands/sentry/issue/update.js +54 -0
  30. package/dist/commands/sentry/org/issues.d.ts +19 -0
  31. package/dist/commands/sentry/org/issues.js +57 -0
  32. package/dist/commands/sentry/project/events.d.ts +17 -0
  33. package/dist/commands/sentry/project/events.js +44 -0
  34. package/dist/commands/sentry/project/issues.d.ts +16 -0
  35. package/dist/commands/sentry/project/issues.js +44 -0
  36. package/dist/config.d.ts +10 -0
  37. package/dist/config.js +18 -0
  38. package/dist/format.d.ts +4 -0
  39. package/dist/format.js +10 -0
  40. package/dist/index.d.ts +1 -0
  41. package/dist/index.js +1 -0
  42. package/dist/sentry/sentry-api.d.ts +127 -0
  43. package/dist/sentry/sentry-api.js +176 -0
  44. package/dist/sentry/sentry-client.d.ts +61 -0
  45. package/dist/sentry/sentry-client.js +66 -0
  46. package/oclif.manifest.json +937 -0
  47. package/package.json +105 -0
@@ -0,0 +1,44 @@
1
+ import { Args, Command, Flags } from '@oclif/core';
2
+ import { readConfig } from '../../../config.js';
3
+ import { formatAsToon } from '../../../format.js';
4
+ import { clearClients, listProjectIssues } from '../../../sentry/sentry-client.js';
5
+ export default class ProjectIssues extends Command {
6
+ static args = {
7
+ projectSlug: Args.string({ description: 'Project slug', required: true }),
8
+ };
9
+ static description = "List a Sentry project's issues";
10
+ static examples = [
11
+ '<%= config.bin %> <%= command.id %> my-project',
12
+ '<%= config.bin %> <%= command.id %> my-project --query "is:unresolved"',
13
+ ];
14
+ static flags = {
15
+ cursor: Flags.string({ description: 'Pagination cursor', required: false }),
16
+ query: Flags.string({ description: 'Search query (e.g. "is:unresolved")', required: false }),
17
+ 'short-id-lookup': Flags.boolean({ description: 'Enable short ID lookup', required: false }),
18
+ 'stats-period': Flags.string({ description: 'Time period (e.g. 24h, 7d)', required: false }),
19
+ toon: Flags.boolean({ description: 'Format output as toon', required: false }),
20
+ };
21
+ async run() {
22
+ const { args, flags } = await this.parse(ProjectIssues);
23
+ const config = await readConfig(this.config.configDir, this.log.bind(this));
24
+ if (!config)
25
+ return;
26
+ const params = {};
27
+ if (flags.cursor)
28
+ params.cursor = flags.cursor;
29
+ if (flags.query)
30
+ params.query = flags.query;
31
+ if (flags['short-id-lookup'])
32
+ params.shortIdLookup = flags['short-id-lookup'];
33
+ if (flags['stats-period'])
34
+ params.statsPeriod = flags['stats-period'];
35
+ const result = await listProjectIssues(config.auth, args.projectSlug, params);
36
+ clearClients();
37
+ if (flags.toon) {
38
+ this.log(formatAsToon(result));
39
+ }
40
+ else {
41
+ this.logJson(result);
42
+ }
43
+ }
44
+ }
@@ -0,0 +1,10 @@
1
+ interface AuthConfig {
2
+ authToken: string;
3
+ host: string;
4
+ organization: string;
5
+ }
6
+ interface Config {
7
+ auth: AuthConfig;
8
+ }
9
+ export declare function readConfig(configDir: string, log: (message: string) => void): Promise<Config | undefined>;
10
+ export {};
package/dist/config.js ADDED
@@ -0,0 +1,18 @@
1
+ import { default as fs } from 'fs-extra';
2
+ import { default as path } from 'node:path';
3
+ export async function readConfig(configDir, log) {
4
+ const configPath = path.join(configDir, 'sentry-config.json');
5
+ try {
6
+ return await fs.readJSON(configPath);
7
+ }
8
+ catch (error) {
9
+ const msg = error instanceof Error ? error.message : String(error);
10
+ if (msg.toLowerCase().includes('no such file or directory')) {
11
+ log('Missing authentication config');
12
+ }
13
+ else {
14
+ log(msg);
15
+ }
16
+ return undefined;
17
+ }
18
+ }
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Format data as TOON (Token-Oriented Object Notation)
3
+ */
4
+ export declare function formatAsToon(data: unknown): string;
package/dist/format.js ADDED
@@ -0,0 +1,10 @@
1
+ import { encode } from '@toon-format/toon';
2
+ /**
3
+ * Format data as TOON (Token-Oriented Object Notation)
4
+ */
5
+ export function formatAsToon(data) {
6
+ if (!data) {
7
+ return '';
8
+ }
9
+ return encode(data);
10
+ }
@@ -0,0 +1 @@
1
+ export { run } from '@oclif/core';
package/dist/index.js ADDED
@@ -0,0 +1 @@
1
+ export { run } from '@oclif/core';
@@ -0,0 +1,127 @@
1
+ /**
2
+ * Generic API result
3
+ */
4
+ export interface ApiResult {
5
+ data?: unknown;
6
+ error?: unknown;
7
+ success: boolean;
8
+ }
9
+ export interface Config {
10
+ authToken: string;
11
+ host: string;
12
+ organization: string;
13
+ }
14
+ /**
15
+ * Sentry REST API client
16
+ */
17
+ export declare class SentryApi {
18
+ private config;
19
+ constructor(config: Config);
20
+ /**
21
+ * Clear client (no persistent client for REST API)
22
+ */
23
+ clearClients(): void;
24
+ /**
25
+ * Debug source maps for a project event
26
+ */
27
+ debugSourceMaps(projectSlug: string, eventId: string, params?: {
28
+ exception_idx?: string;
29
+ frame_idx?: string;
30
+ }): Promise<ApiResult>;
31
+ /**
32
+ * Retrieve an event for a project
33
+ */
34
+ getEvent(projectSlug: string, eventId: string): Promise<ApiResult>;
35
+ /**
36
+ * Retrieve an issue
37
+ */
38
+ getIssue(issueId: string): Promise<ApiResult>;
39
+ /**
40
+ * Retrieve a specific event from an issue
41
+ */
42
+ getIssueEvent(issueId: string, eventId: string): Promise<ApiResult>;
43
+ /**
44
+ * Retrieve tag details for an issue
45
+ */
46
+ getTagDetails(issueId: string, tagKey: string, params?: {
47
+ environment?: string[];
48
+ }): Promise<ApiResult>;
49
+ /**
50
+ * List an issue's events
51
+ */
52
+ listIssueEvents(issueId: string, params?: {
53
+ cursor?: string;
54
+ end?: string;
55
+ environment?: string[];
56
+ full?: boolean;
57
+ start?: string;
58
+ statsPeriod?: string;
59
+ }): Promise<ApiResult>;
60
+ /**
61
+ * List an issue's hashes
62
+ */
63
+ listIssueHashes(issueId: string, params?: {
64
+ cursor?: string;
65
+ }): Promise<ApiResult>;
66
+ /**
67
+ * List an organization's issues
68
+ */
69
+ listOrgIssues(params?: {
70
+ cursor?: string;
71
+ end?: string;
72
+ environment?: string[];
73
+ limit?: number;
74
+ project?: number[];
75
+ query?: string;
76
+ sort?: string;
77
+ start?: string;
78
+ statsPeriod?: string;
79
+ }): Promise<ApiResult>;
80
+ /**
81
+ * List a project's events
82
+ */
83
+ listProjectEvents(projectSlug: string, params?: {
84
+ cursor?: string;
85
+ end?: string;
86
+ full?: boolean;
87
+ start?: string;
88
+ statsPeriod?: string;
89
+ }): Promise<ApiResult>;
90
+ /**
91
+ * List a project's issues
92
+ */
93
+ listProjectIssues(projectSlug: string, params?: {
94
+ cursor?: string;
95
+ query?: string;
96
+ shortIdLookup?: boolean;
97
+ statsPeriod?: string;
98
+ }): Promise<ApiResult>;
99
+ /**
100
+ * List a tag's values for an issue
101
+ */
102
+ listTagValues(issueId: string, tagKey: string, params?: {
103
+ cursor?: string;
104
+ environment?: string[];
105
+ }): Promise<ApiResult>;
106
+ /**
107
+ * Test Sentry API connection
108
+ */
109
+ testConnection(): Promise<ApiResult>;
110
+ /**
111
+ * Update an issue
112
+ */
113
+ updateIssue(issueId: string, data: {
114
+ assignedTo?: string;
115
+ hasSeen?: boolean;
116
+ isBookmarked?: boolean;
117
+ isPublic?: boolean;
118
+ isSubscribed?: boolean;
119
+ status?: string;
120
+ statusDetails?: Record<string, unknown>;
121
+ }): Promise<ApiResult>;
122
+ /**
123
+ * Make an authenticated request to the Sentry API
124
+ */
125
+ private buildQueryString;
126
+ private request;
127
+ }
@@ -0,0 +1,176 @@
1
+ /**
2
+ * Sentry REST API client
3
+ */
4
+ export class SentryApi {
5
+ config;
6
+ constructor(config) {
7
+ this.config = config;
8
+ }
9
+ /**
10
+ * Clear client (no persistent client for REST API)
11
+ */
12
+ clearClients() { }
13
+ /**
14
+ * Debug source maps for a project event
15
+ */
16
+ async debugSourceMaps(projectSlug, eventId, params) {
17
+ return this.request(`/projects/${this.config.organization}/${projectSlug}/events/${eventId}/source-map-debug/`, {
18
+ params,
19
+ });
20
+ }
21
+ /**
22
+ * Retrieve an event for a project
23
+ */
24
+ async getEvent(projectSlug, eventId) {
25
+ return this.request(`/projects/${this.config.organization}/${projectSlug}/events/${eventId}/`);
26
+ }
27
+ /**
28
+ * Retrieve an issue
29
+ */
30
+ async getIssue(issueId) {
31
+ return this.request(`/organizations/${this.config.organization}/issues/${issueId}/`);
32
+ }
33
+ /**
34
+ * Retrieve a specific event from an issue
35
+ */
36
+ async getIssueEvent(issueId, eventId) {
37
+ return this.request(`/organizations/${this.config.organization}/issues/${issueId}/events/${eventId}/`);
38
+ }
39
+ /**
40
+ * Retrieve tag details for an issue
41
+ */
42
+ async getTagDetails(issueId, tagKey, params) {
43
+ return this.request(`/organizations/${this.config.organization}/issues/${issueId}/tags/${tagKey}/`, { params });
44
+ }
45
+ /**
46
+ * List an issue's events
47
+ */
48
+ async listIssueEvents(issueId, params) {
49
+ return this.request(`/organizations/${this.config.organization}/issues/${issueId}/events/`, { params });
50
+ }
51
+ /**
52
+ * List an issue's hashes
53
+ */
54
+ async listIssueHashes(issueId, params) {
55
+ return this.request(`/organizations/${this.config.organization}/issues/${issueId}/hashes/`, { params });
56
+ }
57
+ /**
58
+ * List an organization's issues
59
+ */
60
+ async listOrgIssues(params) {
61
+ return this.request(`/organizations/${this.config.organization}/issues/`, { params });
62
+ }
63
+ /**
64
+ * List a project's events
65
+ */
66
+ async listProjectEvents(projectSlug, params) {
67
+ return this.request(`/projects/${this.config.organization}/${projectSlug}/events/`, { params });
68
+ }
69
+ /**
70
+ * List a project's issues
71
+ */
72
+ async listProjectIssues(projectSlug, params) {
73
+ return this.request(`/projects/${this.config.organization}/${projectSlug}/issues/`, { params });
74
+ }
75
+ /**
76
+ * List a tag's values for an issue
77
+ */
78
+ async listTagValues(issueId, tagKey, params) {
79
+ return this.request(`/organizations/${this.config.organization}/issues/${issueId}/tags/${tagKey}/values/`, { params });
80
+ }
81
+ /**
82
+ * Test Sentry API connection
83
+ */
84
+ async testConnection() {
85
+ const result = await this.request(`/organizations/${this.config.organization}/issues/?limit=1`);
86
+ if (result.success) {
87
+ return {
88
+ data: { baseUrl: this.config.host, organization: this.config.organization },
89
+ success: true,
90
+ };
91
+ }
92
+ return result;
93
+ }
94
+ /**
95
+ * Update an issue
96
+ */
97
+ async updateIssue(issueId, data) {
98
+ return this.request(`/organizations/${this.config.organization}/issues/${issueId}/`, {
99
+ body: JSON.stringify(data),
100
+ method: 'PUT',
101
+ });
102
+ }
103
+ /**
104
+ * Make an authenticated request to the Sentry API
105
+ */
106
+ buildQueryString(params) {
107
+ const searchParams = new URLSearchParams();
108
+ for (const [key, value] of Object.entries(params)) {
109
+ if (value === undefined || value === null)
110
+ continue;
111
+ if (Array.isArray(value)) {
112
+ for (const item of value)
113
+ searchParams.append(key, String(item));
114
+ }
115
+ else {
116
+ searchParams.set(key, String(value));
117
+ }
118
+ }
119
+ return searchParams.toString();
120
+ }
121
+ async request(path, options) {
122
+ try {
123
+ let url = `${this.config.host}${path}`;
124
+ if (options?.params) {
125
+ const qs = this.buildQueryString(options.params);
126
+ if (qs)
127
+ url += `?${qs}`;
128
+ }
129
+ const headers = {
130
+ Accept: 'application/json',
131
+ Authorization: `Bearer ${this.config.authToken}`,
132
+ };
133
+ if (options?.body) {
134
+ headers['Content-Type'] = 'application/json';
135
+ }
136
+ // eslint-disable-next-line n/no-unsupported-features/node-builtins -- fetch is available in Node 18+
137
+ const response = await fetch(url, {
138
+ body: options?.body,
139
+ headers,
140
+ method: options?.method ?? 'GET',
141
+ });
142
+ if (!response.ok) {
143
+ const errorText = await response.text();
144
+ let errorData;
145
+ try {
146
+ errorData = JSON.parse(errorText);
147
+ }
148
+ catch {
149
+ errorData = errorText;
150
+ }
151
+ return {
152
+ error: errorData,
153
+ success: false,
154
+ };
155
+ }
156
+ const text = await response.text();
157
+ if (!text) {
158
+ return {
159
+ data: true,
160
+ success: true,
161
+ };
162
+ }
163
+ const data = JSON.parse(text);
164
+ return {
165
+ data,
166
+ success: true,
167
+ };
168
+ }
169
+ catch (error) {
170
+ return {
171
+ error: error instanceof Error ? error.message : String(error),
172
+ success: false,
173
+ };
174
+ }
175
+ }
176
+ }
@@ -0,0 +1,61 @@
1
+ import { type ApiResult, type Config } from './sentry-api.js';
2
+ export declare function clearClients(): void;
3
+ export declare function testConnection(config: Config): Promise<ApiResult>;
4
+ export declare function listOrgIssues(config: Config, params?: {
5
+ cursor?: string;
6
+ end?: string;
7
+ environment?: string[];
8
+ limit?: number;
9
+ project?: number[];
10
+ query?: string;
11
+ sort?: string;
12
+ start?: string;
13
+ statsPeriod?: string;
14
+ }): Promise<ApiResult>;
15
+ export declare function getIssue(config: Config, issueId: string): Promise<ApiResult>;
16
+ export declare function updateIssue(config: Config, issueId: string, data: {
17
+ assignedTo?: string;
18
+ hasSeen?: boolean;
19
+ isBookmarked?: boolean;
20
+ isPublic?: boolean;
21
+ isSubscribed?: boolean;
22
+ status?: string;
23
+ statusDetails?: Record<string, unknown>;
24
+ }): Promise<ApiResult>;
25
+ export declare function listIssueEvents(config: Config, issueId: string, params?: {
26
+ cursor?: string;
27
+ end?: string;
28
+ environment?: string[];
29
+ full?: boolean;
30
+ start?: string;
31
+ statsPeriod?: string;
32
+ }): Promise<ApiResult>;
33
+ export declare function getIssueEvent(config: Config, issueId: string, eventId: string): Promise<ApiResult>;
34
+ export declare function listIssueHashes(config: Config, issueId: string, params?: {
35
+ cursor?: string;
36
+ }): Promise<ApiResult>;
37
+ export declare function getTagDetails(config: Config, issueId: string, tagKey: string, params?: {
38
+ environment?: string[];
39
+ }): Promise<ApiResult>;
40
+ export declare function listTagValues(config: Config, issueId: string, tagKey: string, params?: {
41
+ cursor?: string;
42
+ environment?: string[];
43
+ }): Promise<ApiResult>;
44
+ export declare function listProjectEvents(config: Config, projectSlug: string, params?: {
45
+ cursor?: string;
46
+ end?: string;
47
+ full?: boolean;
48
+ start?: string;
49
+ statsPeriod?: string;
50
+ }): Promise<ApiResult>;
51
+ export declare function listProjectIssues(config: Config, projectSlug: string, params?: {
52
+ cursor?: string;
53
+ query?: string;
54
+ shortIdLookup?: boolean;
55
+ statsPeriod?: string;
56
+ }): Promise<ApiResult>;
57
+ export declare function getEvent(config: Config, projectSlug: string, eventId: string): Promise<ApiResult>;
58
+ export declare function debugSourceMaps(config: Config, projectSlug: string, eventId: string, params?: {
59
+ exception_idx?: string;
60
+ frame_idx?: string;
61
+ }): Promise<ApiResult>;
@@ -0,0 +1,66 @@
1
+ import { SentryApi } from './sentry-api.js';
2
+ let sentryApi = null;
3
+ function initSentry(config) {
4
+ if (!sentryApi) {
5
+ sentryApi = new SentryApi(config);
6
+ }
7
+ return sentryApi;
8
+ }
9
+ export function clearClients() {
10
+ if (sentryApi) {
11
+ sentryApi.clearClients();
12
+ sentryApi = null;
13
+ }
14
+ }
15
+ export async function testConnection(config) {
16
+ const api = initSentry(config);
17
+ return api.testConnection();
18
+ }
19
+ export async function listOrgIssues(config, params) {
20
+ const api = initSentry(config);
21
+ return api.listOrgIssues(params);
22
+ }
23
+ export async function getIssue(config, issueId) {
24
+ const api = initSentry(config);
25
+ return api.getIssue(issueId);
26
+ }
27
+ export async function updateIssue(config, issueId, data) {
28
+ const api = initSentry(config);
29
+ return api.updateIssue(issueId, data);
30
+ }
31
+ export async function listIssueEvents(config, issueId, params) {
32
+ const api = initSentry(config);
33
+ return api.listIssueEvents(issueId, params);
34
+ }
35
+ export async function getIssueEvent(config, issueId, eventId) {
36
+ const api = initSentry(config);
37
+ return api.getIssueEvent(issueId, eventId);
38
+ }
39
+ export async function listIssueHashes(config, issueId, params) {
40
+ const api = initSentry(config);
41
+ return api.listIssueHashes(issueId, params);
42
+ }
43
+ export async function getTagDetails(config, issueId, tagKey, params) {
44
+ const api = initSentry(config);
45
+ return api.getTagDetails(issueId, tagKey, params);
46
+ }
47
+ export async function listTagValues(config, issueId, tagKey, params) {
48
+ const api = initSentry(config);
49
+ return api.listTagValues(issueId, tagKey, params);
50
+ }
51
+ export async function listProjectEvents(config, projectSlug, params) {
52
+ const api = initSentry(config);
53
+ return api.listProjectEvents(projectSlug, params);
54
+ }
55
+ export async function listProjectIssues(config, projectSlug, params) {
56
+ const api = initSentry(config);
57
+ return api.listProjectIssues(projectSlug, params);
58
+ }
59
+ export async function getEvent(config, projectSlug, eventId) {
60
+ const api = initSentry(config);
61
+ return api.getEvent(projectSlug, eventId);
62
+ }
63
+ export async function debugSourceMaps(config, projectSlug, eventId, params) {
64
+ const api = initSentry(config);
65
+ return api.debugSourceMaps(projectSlug, eventId, params);
66
+ }