@urugus/slack-cli 0.2.12 → 0.3.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 (123) hide show
  1. package/README.md +45 -0
  2. package/dist/commands/history-display.d.ts +5 -1
  3. package/dist/commands/history-display.d.ts.map +1 -1
  4. package/dist/commands/history-display.js +3 -3
  5. package/dist/commands/history-display.js.map +1 -1
  6. package/dist/commands/history.d.ts.map +1 -1
  7. package/dist/commands/history.js +28 -11
  8. package/dist/commands/history.js.map +1 -1
  9. package/dist/commands/search.d.ts +3 -0
  10. package/dist/commands/search.d.ts.map +1 -0
  11. package/dist/commands/search.js +51 -0
  12. package/dist/commands/search.js.map +1 -0
  13. package/dist/index.js +2 -0
  14. package/dist/index.js.map +1 -1
  15. package/dist/types/commands.d.ts +10 -0
  16. package/dist/types/commands.d.ts.map +1 -1
  17. package/dist/utils/constants.d.ts +5 -0
  18. package/dist/utils/constants.d.ts.map +1 -1
  19. package/dist/utils/constants.js +5 -0
  20. package/dist/utils/constants.js.map +1 -1
  21. package/dist/utils/formatters/search-formatters.d.ts +10 -0
  22. package/dist/utils/formatters/search-formatters.d.ts.map +1 -0
  23. package/dist/utils/formatters/search-formatters.js +91 -0
  24. package/dist/utils/formatters/search-formatters.js.map +1 -0
  25. package/dist/utils/slack-api-client.d.ts +5 -0
  26. package/dist/utils/slack-api-client.d.ts.map +1 -1
  27. package/dist/utils/slack-api-client.js +8 -0
  28. package/dist/utils/slack-api-client.js.map +1 -1
  29. package/dist/utils/slack-operations/index.d.ts +1 -0
  30. package/dist/utils/slack-operations/index.d.ts.map +1 -1
  31. package/dist/utils/slack-operations/index.js +3 -1
  32. package/dist/utils/slack-operations/index.js.map +1 -1
  33. package/dist/utils/slack-operations/message-operations.d.ts +1 -0
  34. package/dist/utils/slack-operations/message-operations.d.ts.map +1 -1
  35. package/dist/utils/slack-operations/message-operations.js +21 -0
  36. package/dist/utils/slack-operations/message-operations.js.map +1 -1
  37. package/dist/utils/slack-operations/search-operations.d.ts +29 -0
  38. package/dist/utils/slack-operations/search-operations.d.ts.map +1 -0
  39. package/dist/utils/slack-operations/search-operations.js +37 -0
  40. package/dist/utils/slack-operations/search-operations.js.map +1 -0
  41. package/dist/utils/validators.d.ts +16 -0
  42. package/dist/utils/validators.d.ts.map +1 -1
  43. package/dist/utils/validators.js +50 -0
  44. package/dist/utils/validators.js.map +1 -1
  45. package/package.json +5 -2
  46. package/.claude/settings.local.json +0 -75
  47. package/.github/dependabot.yml +0 -18
  48. package/.github/workflows/ci.yml +0 -70
  49. package/.github/workflows/pr-validation.yml +0 -41
  50. package/.prettierignore +0 -11
  51. package/.prettierrc +0 -10
  52. package/CHANGELOG.md +0 -61
  53. package/CLAUDE.md +0 -16
  54. package/eslint.config.js +0 -38
  55. package/src/commands/channels.ts +0 -50
  56. package/src/commands/config-subcommands.ts +0 -63
  57. package/src/commands/config.ts +0 -50
  58. package/src/commands/history-display.ts +0 -19
  59. package/src/commands/history-validators.ts +0 -46
  60. package/src/commands/history.ts +0 -61
  61. package/src/commands/scheduled.ts +0 -71
  62. package/src/commands/send.ts +0 -69
  63. package/src/commands/unread.ts +0 -122
  64. package/src/index.ts +0 -27
  65. package/src/types/commands.ts +0 -58
  66. package/src/types/config.ts +0 -20
  67. package/src/utils/channel-formatter.ts +0 -45
  68. package/src/utils/channel-resolver.ts +0 -82
  69. package/src/utils/client-factory.ts +0 -10
  70. package/src/utils/command-wrapper.ts +0 -27
  71. package/src/utils/config/config-file-manager.ts +0 -56
  72. package/src/utils/config/profile-manager.ts +0 -79
  73. package/src/utils/config/token-crypto-service.ts +0 -80
  74. package/src/utils/config-helper.ts +0 -21
  75. package/src/utils/constants.ts +0 -78
  76. package/src/utils/date-utils.ts +0 -8
  77. package/src/utils/error-utils.ts +0 -6
  78. package/src/utils/errors.ts +0 -33
  79. package/src/utils/format-utils.ts +0 -9
  80. package/src/utils/formatters/base-formatter.ts +0 -34
  81. package/src/utils/formatters/channel-formatters.ts +0 -71
  82. package/src/utils/formatters/channels-list-formatters.ts +0 -55
  83. package/src/utils/formatters/history-formatters.ts +0 -123
  84. package/src/utils/formatters/message-formatters.ts +0 -85
  85. package/src/utils/mention-utils.ts +0 -47
  86. package/src/utils/option-parsers.ts +0 -100
  87. package/src/utils/profile-config.ts +0 -161
  88. package/src/utils/schedule-utils.ts +0 -41
  89. package/src/utils/slack-api-client.ts +0 -135
  90. package/src/utils/slack-operations/base-client.ts +0 -30
  91. package/src/utils/slack-operations/channel-operations.ts +0 -161
  92. package/src/utils/slack-operations/index.ts +0 -3
  93. package/src/utils/slack-operations/message-operations.ts +0 -176
  94. package/src/utils/slack-patterns.ts +0 -9
  95. package/src/utils/token-utils.ts +0 -17
  96. package/src/utils/validators.ts +0 -263
  97. package/tests/commands/channels.test.ts +0 -250
  98. package/tests/commands/config.test.ts +0 -158
  99. package/tests/commands/history.test.ts +0 -403
  100. package/tests/commands/scheduled.test.ts +0 -131
  101. package/tests/commands/send.test.ts +0 -414
  102. package/tests/commands/unread.test.ts +0 -492
  103. package/tests/index.test.ts +0 -40
  104. package/tests/test-utils.ts +0 -28
  105. package/tests/utils/channel-resolver.test.ts +0 -161
  106. package/tests/utils/config/config-file-manager.test.ts +0 -118
  107. package/tests/utils/config/profile-manager.test.ts +0 -266
  108. package/tests/utils/config/token-crypto-service.test.ts +0 -98
  109. package/tests/utils/config.test.ts +0 -400
  110. package/tests/utils/date-utils.test.ts +0 -30
  111. package/tests/utils/error-utils.test.ts +0 -34
  112. package/tests/utils/format-utils.test.ts +0 -61
  113. package/tests/utils/mention-utils.test.ts +0 -100
  114. package/tests/utils/option-parsers.test.ts +0 -173
  115. package/tests/utils/profile-config.test.ts +0 -282
  116. package/tests/utils/schedule-utils.test.ts +0 -63
  117. package/tests/utils/slack-api-client.test.ts +0 -313
  118. package/tests/utils/slack-operations/channel-operations.test.ts +0 -248
  119. package/tests/utils/slack-operations/message-operations.test.ts +0 -163
  120. package/tests/utils/token-utils.test.ts +0 -33
  121. package/tests/utils/validators.test.ts +0 -307
  122. package/tsconfig.json +0 -22
  123. package/vitest.config.ts +0 -27
@@ -1,135 +0,0 @@
1
- import { ChatPostMessageResponse, ChatScheduleMessageResponse } from '@slack/web-api';
2
- import { ChannelOperations } from './slack-operations/channel-operations';
3
- import { MessageOperations } from './slack-operations/message-operations';
4
-
5
- export interface Channel {
6
- id: string;
7
- name: string;
8
- is_channel?: boolean;
9
- is_group?: boolean;
10
- is_im?: boolean;
11
- is_mpim?: boolean;
12
- is_private: boolean;
13
- created: number;
14
- is_archived?: boolean;
15
- is_general?: boolean;
16
- unlinked?: number;
17
- name_normalized?: string;
18
- is_shared?: boolean;
19
- is_ext_shared?: boolean;
20
- is_org_shared?: boolean;
21
- is_member?: boolean;
22
- num_members?: number;
23
- unread_count?: number;
24
- unread_count_display?: number;
25
- last_read?: string;
26
- topic?: {
27
- value: string;
28
- creator?: string;
29
- last_set?: number;
30
- };
31
- purpose?: {
32
- value: string;
33
- creator?: string;
34
- last_set?: number;
35
- };
36
- }
37
-
38
- export interface ListChannelsOptions {
39
- types: string;
40
- exclude_archived: boolean;
41
- limit: number;
42
- }
43
-
44
- export interface HistoryOptions {
45
- limit: number;
46
- oldest?: string;
47
- }
48
-
49
- export interface Message {
50
- type: string;
51
- text?: string;
52
- user?: string;
53
- bot_id?: string;
54
- ts: string;
55
- thread_ts?: string;
56
- attachments?: unknown[];
57
- blocks?: unknown[];
58
- }
59
-
60
- export interface ScheduledMessage {
61
- id: string;
62
- channel_id: string;
63
- post_at: number;
64
- date_created: number;
65
- text?: string;
66
- }
67
-
68
- export interface HistoryResult {
69
- messages: Message[];
70
- users: Map<string, string>;
71
- }
72
-
73
- export interface ChannelUnreadResult {
74
- channel: Channel;
75
- messages: Message[];
76
- users: Map<string, string>;
77
- }
78
-
79
- export class SlackApiClient {
80
- private channelOps: ChannelOperations;
81
- private messageOps: MessageOperations;
82
-
83
- constructor(token: string) {
84
- this.channelOps = new ChannelOperations(token);
85
- this.messageOps = new MessageOperations(token);
86
- }
87
-
88
- async sendMessage(
89
- channel: string,
90
- text: string,
91
- thread_ts?: string
92
- ): Promise<ChatPostMessageResponse> {
93
- return this.messageOps.sendMessage(channel, text, thread_ts);
94
- }
95
-
96
- async scheduleMessage(
97
- channel: string,
98
- text: string,
99
- post_at: number,
100
- thread_ts?: string
101
- ): Promise<ChatScheduleMessageResponse> {
102
- return this.messageOps.scheduleMessage(channel, text, post_at, thread_ts);
103
- }
104
-
105
- async listScheduledMessages(channel?: string, limit = 50): Promise<ScheduledMessage[]> {
106
- return this.messageOps.listScheduledMessages(channel, limit);
107
- }
108
-
109
- async listChannels(options: ListChannelsOptions): Promise<Channel[]> {
110
- return this.channelOps.listChannels(options);
111
- }
112
-
113
- async getHistory(channel: string, options: HistoryOptions): Promise<HistoryResult> {
114
- return this.messageOps.getHistory(channel, options);
115
- }
116
-
117
- async listUnreadChannels(): Promise<Channel[]> {
118
- return this.channelOps.listUnreadChannels();
119
- }
120
-
121
- async getChannelUnread(channelNameOrId: string): Promise<ChannelUnreadResult> {
122
- return this.messageOps.getChannelUnread(channelNameOrId);
123
- }
124
-
125
- async markAsRead(channelId: string): Promise<void> {
126
- return this.messageOps.markAsRead(channelId);
127
- }
128
- }
129
-
130
- export const slackApiClient = {
131
- listChannels: async (token: string, options: ListChannelsOptions): Promise<Channel[]> => {
132
- const client = new SlackApiClient(token);
133
- return client.listChannels(options);
134
- },
135
- };
@@ -1,30 +0,0 @@
1
- import { WebClient, LogLevel } from '@slack/web-api';
2
- import pLimit from 'p-limit';
3
- import { RATE_LIMIT } from '../constants';
4
-
5
- export class BaseSlackClient {
6
- protected client: WebClient;
7
- protected rateLimiter: ReturnType<typeof pLimit>;
8
-
9
- constructor(token: string) {
10
- this.client = new WebClient(token, {
11
- retryConfig: {
12
- retries: 0, // Disable automatic retries to handle rate limits manually
13
- },
14
- logLevel: LogLevel.ERROR, // Reduce noise from WebClient logs
15
- });
16
- // Limit concurrent API calls to avoid rate limiting
17
- this.rateLimiter = pLimit(RATE_LIMIT.CONCURRENT_REQUESTS);
18
- }
19
-
20
- protected async handleRateLimit(error: unknown): Promise<void> {
21
- if (error instanceof Error && error.message?.includes('rate limit')) {
22
- // If we hit rate limit, wait longer
23
- await new Promise((resolve) => setTimeout(resolve, 5000));
24
- }
25
- }
26
-
27
- protected async delay(ms: number): Promise<void> {
28
- await new Promise((resolve) => setTimeout(resolve, ms));
29
- }
30
- }
@@ -1,161 +0,0 @@
1
- import { BaseSlackClient } from './base-client';
2
- import { channelResolver } from '../channel-resolver';
3
- import { DEFAULTS } from '../constants';
4
- import { Channel, ListChannelsOptions } from '../slack-api-client';
5
- import { WebClient } from '@slack/web-api';
6
-
7
- interface ChannelWithUnreadInfo extends Channel {
8
- unread_count: number;
9
- unread_count_display: number;
10
- last_read?: string;
11
- }
12
-
13
- export class ChannelOperations extends BaseSlackClient {
14
- constructor(tokenOrClient: string | WebClient) {
15
- if (typeof tokenOrClient === 'string') {
16
- super(tokenOrClient);
17
- } else {
18
- super('dummy-token'); // Call parent constructor
19
- this.client = tokenOrClient; // Override the client for testing
20
- }
21
- }
22
-
23
- async listChannels(options: ListChannelsOptions): Promise<Channel[]> {
24
- const channels: Channel[] = [];
25
- let cursor: string | undefined;
26
-
27
- // Paginate through all channels
28
- do {
29
- const response = await this.client.conversations.list({
30
- types: options.types,
31
- exclude_archived: options.exclude_archived,
32
- limit: options.limit,
33
- cursor,
34
- });
35
-
36
- if (response.channels) {
37
- channels.push(...(response.channels as Channel[]));
38
- }
39
-
40
- cursor = response.response_metadata?.next_cursor;
41
- } while (cursor);
42
-
43
- return channels;
44
- }
45
-
46
- async listUnreadChannels(): Promise<Channel[]> {
47
- const channels = await this.fetchAllChannels();
48
- const channelsWithUnread: Channel[] = [];
49
-
50
- // Process channels one by one with delay to avoid rate limits
51
- for (const channel of channels) {
52
- try {
53
- const unreadInfo = await this.getChannelUnreadInfo(channel);
54
- if (unreadInfo) {
55
- channelsWithUnread.push(unreadInfo);
56
- }
57
-
58
- // Add delay between API calls to avoid rate limiting
59
- await this.delay(100);
60
- } catch (error) {
61
- // Skip channels that fail
62
- await this.handleRateLimit(error);
63
- }
64
- }
65
-
66
- return channelsWithUnread;
67
- }
68
-
69
- private async fetchAllChannels(): Promise<Channel[]> {
70
- const response = await this.client.conversations.list({
71
- types: 'public_channel,private_channel,im,mpim',
72
- exclude_archived: true,
73
- limit: 1000,
74
- });
75
-
76
- return response.channels as Channel[];
77
- }
78
-
79
- private async getChannelUnreadInfo(channel: Channel): Promise<Channel | null> {
80
- const channelInfo = await this.fetchChannelInfo(channel.id);
81
- const unreadCount = await this.calculateUnreadCount(channel.id, channelInfo);
82
-
83
- if (unreadCount > 0) {
84
- return {
85
- ...channel,
86
- unread_count: unreadCount,
87
- unread_count_display: unreadCount,
88
- last_read: channelInfo.last_read,
89
- };
90
- }
91
-
92
- return null;
93
- }
94
-
95
- private async fetchChannelInfo(channelId: string): Promise<ChannelWithUnreadInfo> {
96
- const info = await this.client.conversations.info({
97
- channel: channelId,
98
- include_num_members: false,
99
- });
100
- return info.channel as ChannelWithUnreadInfo;
101
- }
102
-
103
- private async calculateUnreadCount(
104
- channelId: string,
105
- channelInfo: ChannelWithUnreadInfo
106
- ): Promise<number> {
107
- // Get the latest message to check if channel has any messages
108
- const latestMessage = await this.fetchLatestMessage(channelId);
109
- if (!latestMessage) {
110
- return 0;
111
- }
112
-
113
- if (channelInfo.last_read) {
114
- return await this.fetchUnreadMessageCount(channelId, channelInfo.last_read);
115
- } else {
116
- // If no last_read, all messages are unread
117
- return await this.fetchAllMessageCount(channelId);
118
- }
119
- }
120
-
121
- private async fetchLatestMessage(channelId: string): Promise<any> {
122
- const history = await this.client.conversations.history({
123
- channel: channelId,
124
- limit: 1,
125
- });
126
- return history.messages && history.messages.length > 0 ? history.messages[0] : null;
127
- }
128
-
129
- private async fetchUnreadMessageCount(channelId: string, lastRead: string): Promise<number> {
130
- const unreadHistory = await this.client.conversations.history({
131
- channel: channelId,
132
- oldest: lastRead,
133
- limit: 100, // Get up to 100 unread messages
134
- });
135
- return unreadHistory.messages?.length || 0;
136
- }
137
-
138
- private async fetchAllMessageCount(channelId: string): Promise<number> {
139
- const allHistory = await this.client.conversations.history({
140
- channel: channelId,
141
- limit: 100,
142
- });
143
- return allHistory.messages?.length || 0;
144
- }
145
-
146
- async getChannelInfo(channelNameOrId: string): Promise<ChannelWithUnreadInfo> {
147
- const channelId = await channelResolver.resolveChannelId(channelNameOrId, () =>
148
- this.listChannels({
149
- types: 'public_channel,private_channel,im,mpim',
150
- exclude_archived: true,
151
- limit: DEFAULTS.CHANNELS_LIMIT,
152
- })
153
- );
154
-
155
- const info = await this.client.conversations.info({
156
- channel: channelId,
157
- });
158
-
159
- return info.channel as ChannelWithUnreadInfo;
160
- }
161
- }
@@ -1,3 +0,0 @@
1
- export { BaseSlackClient } from './base-client';
2
- export { ChannelOperations } from './channel-operations';
3
- export { MessageOperations } from './message-operations';
@@ -1,176 +0,0 @@
1
- import {
2
- ChatPostMessageResponse,
3
- ChatPostMessageArguments,
4
- ChatScheduleMessageArguments,
5
- ChatScheduleMessageResponse,
6
- } from '@slack/web-api';
7
- import { BaseSlackClient } from './base-client';
8
- import { channelResolver } from '../channel-resolver';
9
- import { DEFAULTS } from '../constants';
10
- import {
11
- Message,
12
- HistoryOptions,
13
- HistoryResult,
14
- ChannelUnreadResult,
15
- ScheduledMessage,
16
- } from '../slack-api-client';
17
- import { ChannelOperations } from './channel-operations';
18
- import { extractAllUserIds } from '../mention-utils';
19
-
20
- export class MessageOperations extends BaseSlackClient {
21
- private channelOps: ChannelOperations;
22
-
23
- constructor(token: string) {
24
- super(token);
25
- this.channelOps = new ChannelOperations(token);
26
- }
27
-
28
- async sendMessage(
29
- channel: string,
30
- text: string,
31
- thread_ts?: string
32
- ): Promise<ChatPostMessageResponse> {
33
- const params: ChatPostMessageArguments = {
34
- channel,
35
- text,
36
- };
37
-
38
- if (thread_ts) {
39
- params.thread_ts = thread_ts;
40
- }
41
-
42
- return await this.client.chat.postMessage(params);
43
- }
44
-
45
- async scheduleMessage(
46
- channel: string,
47
- text: string,
48
- post_at: number,
49
- thread_ts?: string
50
- ): Promise<ChatScheduleMessageResponse> {
51
- const params: ChatScheduleMessageArguments = {
52
- channel,
53
- text,
54
- post_at,
55
- };
56
-
57
- if (thread_ts) {
58
- params.thread_ts = thread_ts;
59
- }
60
-
61
- return await this.client.chat.scheduleMessage(params);
62
- }
63
-
64
- async listScheduledMessages(channel?: string, limit = 50): Promise<ScheduledMessage[]> {
65
- const channelId = channel
66
- ? await channelResolver.resolveChannelId(channel, () =>
67
- this.channelOps.listChannels({
68
- types: 'public_channel,private_channel,im,mpim',
69
- exclude_archived: true,
70
- limit: DEFAULTS.CHANNELS_LIMIT,
71
- })
72
- )
73
- : undefined;
74
-
75
- const params: { channel?: string; limit: number } = {
76
- limit,
77
- };
78
-
79
- if (channelId) {
80
- params.channel = channelId;
81
- }
82
-
83
- const response = await this.client.chat.scheduledMessages.list(params as any);
84
- return (response.scheduled_messages || []) as ScheduledMessage[];
85
- }
86
-
87
- async getHistory(channel: string, options: HistoryOptions): Promise<HistoryResult> {
88
- // Resolve channel name to ID if needed
89
- const channelId = await channelResolver.resolveChannelId(channel, () =>
90
- this.channelOps.listChannels({
91
- types: 'public_channel,private_channel,im,mpim',
92
- exclude_archived: true,
93
- limit: DEFAULTS.CHANNELS_LIMIT,
94
- })
95
- );
96
-
97
- const response = await this.client.conversations.history({
98
- channel: channelId,
99
- limit: options.limit,
100
- oldest: options.oldest,
101
- });
102
-
103
- const messages = response.messages as Message[];
104
-
105
- // Extract all unique user IDs (authors and mentioned users)
106
- const userIds = extractAllUserIds(messages);
107
- const users = await this.fetchUserInfo(userIds);
108
-
109
- return { messages, users };
110
- }
111
-
112
- async getChannelUnread(channelNameOrId: string): Promise<ChannelUnreadResult> {
113
- const channel = await this.channelOps.getChannelInfo(channelNameOrId);
114
-
115
- // Get unread messages
116
- let messages: Message[] = [];
117
- let users = new Map<string, string>();
118
- let actualUnreadCount = 0;
119
-
120
- if (channel.last_read) {
121
- // Always fetch messages after last_read to get accurate unread count
122
- const historyResult = await this.getHistory(channel.id, {
123
- limit: 100, // Fetch up to 100 messages after last_read
124
- oldest: channel.last_read,
125
- });
126
- messages = historyResult.messages;
127
- users = historyResult.users;
128
- actualUnreadCount = messages.length;
129
- } else if (!channel.last_read) {
130
- // If no last_read, all messages are unread
131
- const historyResult = await this.getHistory(channel.id, {
132
- limit: 100,
133
- });
134
- messages = historyResult.messages;
135
- users = historyResult.users;
136
- actualUnreadCount = messages.length;
137
- }
138
-
139
- return {
140
- channel: {
141
- ...channel,
142
- unread_count: actualUnreadCount,
143
- unread_count_display: actualUnreadCount,
144
- },
145
- messages,
146
- users,
147
- };
148
- }
149
-
150
- private async fetchUserInfo(userIds: string[]): Promise<Map<string, string>> {
151
- const users = new Map<string, string>();
152
-
153
- if (userIds.length > 0) {
154
- for (const userId of userIds) {
155
- try {
156
- const userInfo = await this.client.users.info({ user: userId });
157
- if (userInfo.user?.name) {
158
- users.set(userId, userInfo.user.name);
159
- }
160
- } catch (error) {
161
- // If we can't get user info, we'll use the ID
162
- users.set(userId, userId);
163
- }
164
- }
165
- }
166
-
167
- return users;
168
- }
169
-
170
- async markAsRead(channelId: string): Promise<void> {
171
- await this.client.conversations.mark({
172
- channel: channelId,
173
- ts: Date.now() / 1000 + '',
174
- });
175
- }
176
- }
@@ -1,9 +0,0 @@
1
- /**
2
- * Common regex patterns for Slack message parsing
3
- */
4
-
5
- // Matches Slack user mentions in the format <@USERID>
6
- export const USER_MENTION_PATTERN = /<@([A-Z0-9]+)>/g;
7
-
8
- // Matches a single user mention (non-global)
9
- export const SINGLE_USER_MENTION_PATTERN = /<@([A-Z0-9]+)>/;
@@ -1,17 +0,0 @@
1
- import { TOKEN_MASK_LENGTH, TOKEN_MIN_LENGTH } from './constants';
2
-
3
- /**
4
- * Masks a token for display purposes, showing only first and last few characters
5
- * @param token The token to mask
6
- * @returns Masked token in format "xoxb-****-****-abcd"
7
- */
8
- export function maskToken(token: string): string {
9
- if (token.length <= TOKEN_MIN_LENGTH) {
10
- return '****';
11
- }
12
-
13
- const prefix = token.substring(0, TOKEN_MASK_LENGTH);
14
- const suffix = token.substring(token.length - TOKEN_MASK_LENGTH);
15
-
16
- return `${prefix}-****-****-${suffix}`;
17
- }