From 9e694a75f37dac800d9650615d5cda71b268ce15 Mon Sep 17 00:00:00 2001 From: Luke Russell Date: Tue, 29 Apr 2025 12:22:31 -0700 Subject: [PATCH 1/5] docs: adds guides --- .../migrating-socket-mode-package-to-v2.md | 18 + docs/content/migration/migrating-to-v4.md | 316 ++++++++++++++++++ .../migrating-to-v5.md | 14 +- .../migrating-to-v6.md | 8 +- .../migrating-web-api-package-to-v7.md | 217 ++++++++++++ docs/content/support-schedule.md | 27 ++ docs/content/typescript.md | 2 +- docs/sidebars.js | 17 +- docs/static/img/support-schedule.png | Bin 0 -> 47247 bytes 9 files changed, 600 insertions(+), 19 deletions(-) create mode 100644 docs/content/migration/migrating-socket-mode-package-to-v2.md create mode 100644 docs/content/migration/migrating-to-v4.md rename docs/content/{tutorials => migration}/migrating-to-v5.md (95%) rename docs/content/{tutorials => migration}/migrating-to-v6.md (94%) create mode 100644 docs/content/migration/migrating-web-api-package-to-v7.md create mode 100644 docs/content/support-schedule.md create mode 100644 docs/static/img/support-schedule.png diff --git a/docs/content/migration/migrating-socket-mode-package-to-v2.md b/docs/content/migration/migrating-socket-mode-package-to-v2.md new file mode 100644 index 000000000..c32a41489 --- /dev/null +++ b/docs/content/migration/migrating-socket-mode-package-to-v2.md @@ -0,0 +1,18 @@ +# Migrating the `socket‐mode` package to v2.x + +This migration guide helps you transition an application written using the `v1.x` series of the `@slack/socket-mode` package to the `v2.x` series. This guide focuses specifically on the breaking changes to help get your existing projects up and running as quickly as possible. + +## Installation + +``` +npm i @slack/socket-mode +``` + +## Breaking Changes + +1. Two [Lifecycle Events](https://github.com/slackapi/node-slack-sdk/tree/main/packages/socket-mode#lifecycle-events) have been removed: + - `authenticated`. This event corresponded to the client retrieving a response from the [`apps.connections.open`](https://api.slack.com/methods/apps.connections.open) Slack API method - but it didn't signal anything about the actual socket connection that matters! If you had been listening to this event, we recommend moving instead to one of these two events: + - `connected`. This signals that the client has established a connection with Slack and has received a `hello` message from the Slack backend. Events will start flowing to your app after this event. + - `connecting`. This signals that the client is about to establish a connection with Slack. If you were using `authenticated` to be notified _before_ the client establishes a connection, we recommend using this event instead. + - `unable_to_socket_mode_start`. This event corresponded to an error happening when attempting to hit the [`apps.connections.open`](https://api.slack.com/methods/apps.connections.open) Slack API method. We recommend moving instead to `reconnecting` (if you have client reconnections enabled), or the `error` event. +2. Two public properties on the client have been removed: `connected` and `authenticated`. Instead, we recommend migrating to the `isActive()` method to determine whether the WebSocket connection powering the client is healthy. \ No newline at end of file diff --git a/docs/content/migration/migrating-to-v4.md b/docs/content/migration/migrating-to-v4.md new file mode 100644 index 000000000..8f170394f --- /dev/null +++ b/docs/content/migration/migrating-to-v4.md @@ -0,0 +1,316 @@ +# Migrating to v4.x + +This migration guide helps you transition an application written using the `v3.x` series of this package, to the `v4.x` series. This guide focuses specifically on the breaking changes to help get your existing app up and running as quickly as possible. In some cases, there may be better techniques for accomplishing what your app already does by utilizing brand new features or new API. Learn about all the new features in our [`v4.0.0` release notes](https://github.com/slackapi/node-slack-sdk/releases/tag/v4.0.0) if you'd like to go beyond a simple port. + +## WebClient + +### Constructor + +* The `slackAPIUrl` option has been renamed to `slackApiUrl` to improve readability. +* The `transport` option has been removed. If you used this option to implement proxy support, use the new `agent` option as described [below](#proxy-support-with-agent). If you used this option for setting custom TLS configuration, use the new `tls` option as described [below](#custom-tls-configuration). If you were using this for some other reason, please [open an issue](https://github.com/slackapi/node-slack-sdk/issues/new) and describe your use case so we can help you migrate. + +### All methods + +All Web API methods no longer take positional arguments. They each take one argument, an object, in which some properties are required and others are optional (depending on the method). You no longer have to memorize or look up the order of the arguments. The method arguments are described in the [API method documentation](https://api.slack.com/methods). If you are using an editor that understands TypeScript or JSDoc annotations, your editor may present you with useful information about these arguments as you type. + +If any Web API method is called with a callback parameter, and the call results in an error from the platform, you will no longer get the platform's response as the second argument to the callback. Instead, that response will exist as the `.data` property on the first argument (the error). You can consolidate this logic by using Promises instead (or continue to use callbacks if you prefer). + +**Before:** + +```javascript +const { WebClient } = require('@slack/client') +const web = new WebClient(token); + +web.chat.postMessage(channelId, text, { as_user: true, parse: 'full' }, (error, resp) => { + if (error) { + if (resp) { + // a platform error occurred, `resp.error` contains the error information + } + // some other error occurred + return; + } + + // success +}); +``` + +**After:** + +```javascript +// a new export, ErrorCode, is a dictionary of known error types +const { WebClient, ErrorCode } = require('@slack/client') +const web = new WebClient(token); + +web.chat.postMessage({ channel: channelId, text, as_user: true, parse: 'full' }) + .then((resp) => { /* success */ }) + .catch((error) => { + if (error.code === ErrorCode.PlatformError) { + // a platform error occurred, `error.message` contains error information, `error.data` contains the entire resp + } else { + // some other error occurred + } + }); +``` + +### `dm` methods + +This family of methods was always a duplicate of those under the `.im` family. These duplicates have been removed. + +### `mpdm` methods + +This family of methods was always a duplicate of those under the `.mpim` family. These duplicates have been removed. + +## `RTMClient` + +The top-level export name has changed from `RtmClient` to `RTMClient`. + +### Constructor + +* The `slackAPIUrl` option has been renamed to `slackApiUrl` to improve readability. +* The `dataStore` option has been removed. +* The `useRtmConnect` option now has a default value of `true`. We recommend querying for additional data using a `WebClient` after this client is connected. If that doesn't help, then you can set this option to `false`. +* The `socketFn` option has been removed. If you used this option to implement proxy support, use the new `agent` option as described [below](#proxy-support-with-agent). If you used this option for setting custom TLS configuration, use the new `tls` option as described [below](#custom-tls-configuration). If you were using this for some other reason, please [open an issue](https://github.com/slackapi/node-slack-sdk/issues/new) and describe your use case so we can help you migrate. +* The `wsPingInterval` and `maxPongInterval` options have been replaced with `clientPingTimeout` and `serverPongTimeout`. Most likely, you can replace these values respectively, or drop using them all together. + +### `dataStore` + +The v3.15.0 release of `@slack/client` has deprecated use of the `SlackDataStore` interface and its implementations (including `SlackMemoryDataStore`). In v4.0.0 ([release milestone](https://github.com/slackapi/node-slack-sdk/milestone/2)), these types and APIs have been removed. + +The datastore APIs have helped apps keep track of team state since this feature was released. But as the Slack platform has evolved, the model has become out of date in tricky and unexpected ways. The data store was designed to behave like the Slack web and desktop applications, managing the type of state that a logged in human user would typically need. But bots and Slack apps have a whole new (and more powerful in many ways) perspective of the world! + +At a high level, here are the design issues that could not be addressed in a backwards compatible way: + +- **Synchronous calling convention** - In order to plug in storage destinations other than memory (`SlackMemoryDataStore`) the code to reach that destination would need to be asynchronous. This is important if you want to share state across many processes while horizontally scaling an app. As a result, the maintainers have never seen an implementation of the `SlackDataStore` interface other than the memory-based one provided. + +- **Names versus IDs** - While we always thought it was a good idea to use IDs anywhere possible in programmatic use, the Slack platform wasn't overly strict about it. With the introduction of Shared Channels, we cannot preserve any guarantees of uniqueness for usernames, as we mentioned in this [changelog entry](https://api.slack.com/changelog/2017-09-the-one-about-usernames). In fact, channel names also lose these types of guarantees. The APIs in the `SlackDataStore` that operate on names instead of IDs start to break since the fundamental assumptions are not true anymore. + +- **Missing Users** - We want the SDK to be clear and easy to use across many use cases, including applications developed for Enterprise Grid and Shared Channels. In this context, an application is likely to receive events from users it does not recognize and for whom it cannot get more information. `SlackDataStore` cannot deal with these scenarios. If your application has more than one RTM client connected to different workspaces, and those workspaces are joined by a shared channel, there is no general purpose way to deduplicate the messages and arrive at a consistent deterministic state. The Slack platform has solved for this issue using the Events API (which deduplicates events on your app's behalf). + +For the full discussion, see [#330](https://github.com/slackapi/node-slack-sdk/issues/330). + +When you initialize an `RtmClient` object, turn on the `useRtmConnect` option and turn off the `dataStore` option as below: + +```javascript +const { RtmClient } = require('@slack/client'); + +const rtm = new RtmClient('your-token-here', { + useRtmConnect: true, + dataStore: false, +}); +``` + +Next look through your code for any places you might reference the `.dataStore` property of the `RtmClient`. In most cases, you'll be able to replace finding data in the dataStore with finding that data using a `WebClient`. + +```javascript +const { RtmClient, WebClient } = require('@slack/client'); +const web = new WebClient('your-token-here'); + +// Before: +// const channel = rtm.dataStore.getChannelById(channelId); +// console.log(`channel info: ${JSON.stringify(channel)}`); + +// After: +web.conversations.info(channelId) + .then(resp => console.log(`channel info: ${JSON.stringify(resp.channel)}`) + .catch(error => /* TODO: handle error */); +``` + +If you aren't sure how to translate a specific data store method into a Web API call, file a new `question` issue and we will help you figure it out. + +You'll notice that this code has become asynchronous. This will likely be the largest challenge in migrating away from the data store, but for most developers it will be worth it. + +For the majority of apps, you will be ready for v4 at this point. If your app is having performance related issues, there's room to make improvements by caching the data that is relevant to your app. This should only be taken on if you believe its necessary, since cache invalidation is one of the [only two hard things in computer science](https://martinfowler.com/bliki/TwoHardThings.html). + +The approach for caching data that we recommend is to pick out the data your app needs, store it at connection time, and update it according to a determined policy. You may want to disable the `useRtmConnect` option in order +to get more data at connection time. + +### `reconnect()` + +This method has been removed, but it can be substituted by using `disconnect()`, waiting for the `disconnected` event, and then calling `start(options)`. Reconnecting using the method was rarely used by developers, and its implementation increased the complexity of state management in the client. + +**Before:** + +```javascript +rtm.reconnect(); +``` + +**After:** + +```javascript +rtm.disconnect(); +// You will need to store the start options from the first time you connect and then reuse them here. +rtm.once('disconnected', () => rtm.start(options)); +``` + +### `updateMessage()` + +This method has been removed from the `RTMClient`, but can be substituted by using the `WebClient`. + +**Before:** + +```javascript +const message = { ts: '999999999.0000000', channel: 'C123456', text: 'updated message text' }; +rtm.updateMessage(message).then(console.log); +``` + +**After:** + +```javascript +// We recommend that you initialize this object at the same time you would have initialized the RTMClient +const web = new WebClient(token); + +const message = { ts: '999999999.0000000', channel: 'C123456', text: 'updated message text' }; +web.chat.update(message).then(console.log); +``` + +### `send()` + +This method has be repurposed, and in most cases you will instead rely on `addOutgoingEvent(awaitReply, type, body)`. + +The main difference is that if you want to know when the message is acknowledged by the server (you were using the optional callback parameter to `send()`), you'll only be able to do so using the returned Promise. If you prefer callbacks, you can translate the interface using a library like Bluebird (see: http://bluebirdjs.com/docs/api/ascallback.html) or the Node [`util.callbackify()`](https://nodejs.org/api/util.html#util_util_callbackify_original) since v8.2.0. + +As an added benefit, you will be able to send the message without worrying whether the client is in a connected state or not. + +**Before:** + +```javascript +const message = { type: 'message_type', key: 'value', foo: 'bar' }; +rtm.send(message, (error, resp) => { + if (error) { + // error handling + return; + } + // success handling +}); +``` + +**After:** + +```javascript +const message = { type: 'message_type', key: 'value', foo: 'bar' }; +rtm.addOutgoingEvent(true, message.type, message) + .then((resp) => { + // success handling + }) + .catch((error) => { + // error handling + }); +``` + +### Events + +The `RTMClient` now has more well-defined states (and substates) that you may observe using the [`EventEmitter` API pattern](https://nodejs.org/api/events.html). The following table helps describe the relationship between events in the `v3.x` series and events in the `v4.x` series. + +| Event Name (`v4.x`) | Event Name (`v3.x`) | Description | +|-----------------|-----------------|-------------| +| `disconnected` | `disconnect` | The client is not connected to the platform. This is a steady state - no attempt to connect is occurring. | +| `connecting` | `connecting` / `attempting_reconnect` | The client is in the process of connecting to the platform. | +| `authenticated` | `authenticated` | The client has authenticated with the platform. The `rtm.connect` or `rtm.start` response is emitted as an argument. This is a sub-state of `connecting`. | +| `connected` | | The client is connected to the platform and incoming events will start being emittied. | +| `ready` | `open` | The client is ready to send outgoing messages. This is a sub-state of `connected` | +| `disconnecting` | | The client is no longer connected to the platform and cleaning up its resources. It will soon transition to `disconnected`. | +| `reconnecting` | | The client is no longer connected to the platform and cleaning up its resources. It will soon transition to `connecting`. | +| `error` | `ws_error` | An error has occurred. The error is emitted as an argument. The `v4` event is a super set of the `v3` event. To test whether the event is a websocket error, check `error.code === ErrorCodes.RTMWebsocketError` | +| `unable_to_rtm_start` | `unable_to_rtm_start` | A problem occurred while connecting, a reconnect may or may not occur. Use of this event is discouraged since `disconnecting` and `reconnecting` are more meaningful. | +| `slack_event` | | An incoming Slack event has been received. The event type and event body are emitted as the arguments. | +| `{type}` | `{type}` | An incoming Slack event of type `{type}` has been received. The event is emitted as an argument. An example is `message` for all message events | +| `{type}::{subtype}` | `{type}::{subtype}` | An incoming Slack event of type `{type}` and subtype `{subtype}` has been received. The event is emitted as an argument. An example is `message::bot_message` for all bot messages. | +| `raw_message` | `raw_message` | A websocket message arrived. The message (unparsed string) is emitted as an argument. Use of this event is discouraged since `slack_event` is more useful. | +| | `ws_opening` | This event is no longer emitted, and the state of the underlying websocket is considered private. | +| | `ws_opened` | This event is no longer emitted, and the state of the underlying websocket is considered private. | +| | `ws_close` | This event is no longer emitted, and the state of the underlying websocket is considered private. | + +## Incoming webhooks + +* The following options have been renamed: + - `iconEmoji` => `icon_emoji` + - `iconUrl` => `icon_url` + - `linkNames` => `link_names` + - `unfurlLinks` => `unfurl_links` + - `unfurlMedia` => `unfurl_media` + +## Removed constants + +The `CLIENT_EVENTS`, `RTM_EVENTS` and `RTM_MESSAGE_SUBTYPES` constants have been removed. We recommend using simple strings for event names. The values that were in `CLIENT_EVENTS` have been migrated according to the [events table above](#events). The `RTM_EVENTS` dictionary isn't necessary, just directly subscribe to the event name as a string. + +**Before:** +```javascript +rtm.on(CLIENT_EVENTS.RTM.AUTHENTICATED, (connectionData) => { + console.log('RTMClient authenticated'); +}); + +rtm.on(RTM_EVENTS.MESSAGE, (event) => { + console.log(`Incoming message: ${event.ts}`); +}) +``` + +**After:** +```javascript +rtm.on('authenticated', (connectionData) => { + console.log('RTMClient authenticated'); +}); + +rtm.on('message', (event) => { + console.log(`Incoming message: ${event.ts}`); +}) +``` + +## `RETRY_POLICIES` + +The names of these policies have slightly changed for more consistency with our style guide. The dictionary of policies is now exported under the name `retryPolicies`. See `src/retry-policies.ts` for details. + +## Proxy support with `agent` + +In order to pass outgoing requests from `WebClient` or `RTMClient` through an HTTP proxy, you'll first need to install an additional package in your application: + +``` +$ npm install --save https-proxy-agent +``` + +Next, use the `agent` option in the client constructor to configure with your proxy settings. + +```javascript +const HttpsProxyAgent = require('https-proxy-agent'); +const { WebClient, RTMClient } = require('@slack/client'); + +// in this example, we read the token from an environment variable. its best practice to keep sensitive data outside +// your source code. +const token = process.env.SLACK_TOKEN; + +// its common to read the proxy URL from an environment variable, since it also may be sensitive. +const proxyUrl = process.env.http_proxy || 'http://12.34.56.78:9999'; + +// To use Slack's Web API: +const web = new WebClient(token, { agent: new HttpsProxyAgent(proxyUrl) }); + +// To use Slack's RTM API: +const rtm = new RTMClient(token, { agent: new HttpsProxyAgent(proxyUrl) }); + +// NOTE: for a more complex proxy configuration, see the https-proxy-agent documentation: +// https://github.com/TooTallNate/node-https-proxy-agent#api +``` + +## Custom TLS configuration + +You may want to use a custom TLS configuration if your application needs to send requests through a server with a +self-signed certificate. + +Example: + +```javascript +const { WebClient, RTMClient } = require('@slack/client'); + +// in this example, we read the token from an environment variable. its best practice to keep sensitive data outside +// your source code. +const token = process.env.SLACK_TOKEN; + +// Configure TLS options +const tls = { + key: fs.readFileSync('/path/to/key'), + cert: fs.readFileSync('/path/to/cert'), + ca: fs.readFileSync('/path/to/cert'), +}; + +const web = new WebClient(token, { slackApiUrl: 'https://fake.slack.com/api', tls }); +const rtm = new RTMClient(token, { slackApiUrl: 'https://fake.slack.com/api', tls }); +``` diff --git a/docs/content/tutorials/migrating-to-v5.md b/docs/content/migration/migrating-to-v5.md similarity index 95% rename from docs/content/tutorials/migrating-to-v5.md rename to docs/content/migration/migrating-to-v5.md index c50ba419c..75b6b2912 100644 --- a/docs/content/tutorials/migrating-to-v5.md +++ b/docs/content/migration/migrating-to-v5.md @@ -9,23 +9,17 @@ to using the new, improved, and independent packages, starting with `v5.0.0`. If you were not using any deprecated features, this should only take a couple minutes. -:::info +:::info[If you were using the `@slack/events-api` or `@slack/interactive-messages` packages, this migration doesn't affect your app] -If you were using the `@slack/events-api` or `@slack/interactive-messages` packages, this migration doesn't -affect your app. Those packages only moved repositories, but did not get updated in this process. You're done! +Those packages only moved repositories, but did not get updated in this process. You're done! ::: ## Update to a supported version of Node -These package have dropped support for versions of Node that are no longer supported. We recommend updating to the -latest LTS version of [Node](https://nodejs.org/en/), which at this time is v10.15.3. The minimum supported version is -v8.9.0. +These package have dropped support for versions of Node that are no longer supported. We recommend updating to the latest LTS version of [Node](https://nodejs.org/en/), which at this time is v10.15.3. The minimum supported version is v8.9.0. -:::info - -Learn more about our [support schedule](https://github.com/slackapi/node-slack-sdk/wiki/Support-Schedule) so -that you can prepare and plan for future updates. +:::info[Learn more about our [support schedule](/support-schedule) so that you can prepare and plan for future updates.] ::: diff --git a/docs/content/tutorials/migrating-to-v6.md b/docs/content/migration/migrating-to-v6.md similarity index 94% rename from docs/content/tutorials/migrating-to-v6.md rename to docs/content/migration/migrating-to-v6.md index c26cdd3a8..5c7bcc6d7 100644 --- a/docs/content/tutorials/migrating-to-v6.md +++ b/docs/content/migration/migrating-to-v6.md @@ -19,11 +19,9 @@ To migrate to the latest packages, updating your minimum Node.js to `12.13.0` is ### Minimum Node Version -Our newly released major versions all require a minimum Node version of `12.13.0` and minimum npm version of `6.12.0` . +Our newly released major versions all require a minimum Node version of `12.13.0` and minimum npm version of `6.12.0`. -:::info - -Learn more about our [support schedule](https://github.com/slackapi/node-slack-sdk/wiki/Support-Schedule) so that you can prepare and plan for future updates. +:::info[Learn more about our [support schedule](/support-schedule) so that you can prepare and plan for future updates.] ::: @@ -89,7 +87,7 @@ installationStore: { }, ``` -### Deprecating @slack/events-api & @slack/interactive-messages +### Deprecating `@slack/events-api` & `@slack/interactive-messages` packages `@slack/events-api` and `@slack/interactive-messages` will be deprecated on **January 12th, 2021**. We will only implement **critical bug fixes** until the official end of life date and close non critical issues and pull requests, which is slated for **May 31st, 2021**. At this time, development will fully stop for these packages and all remaining open issues and pull requests will be closed. Bolt for JavaScript can now perform all the same functionality as these packages. We think you’ll love the more modern set of features you get in Bolt. Here are some migration examples: diff --git a/docs/content/migration/migrating-web-api-package-to-v7.md b/docs/content/migration/migrating-web-api-package-to-v7.md new file mode 100644 index 000000000..f27e44374 --- /dev/null +++ b/docs/content/migration/migrating-web-api-package-to-v7.md @@ -0,0 +1,217 @@ +# Migrating the `web-api` package to v7.x + +This migration guide helps you transition an application written using the `v6.x` series of the `@slack/web-api` package to the `v7.x` +series. This guide focuses specifically on the breaking changes to help get your existing app up and running as +quickly as possible. + +## Installation + +``` +npm i @slack/web-api +``` + +## `@slack/web-api` v7 Changes + +**TL;DR**: this package now supports only node v18 and newer, and HTTP API arguments passed to methods in this project in the context of a TypeScript project are stricter. + +This release focusses on the type safety of Slack HTTP API method arguments provided by `@slack/web-api`. If you use this package in a TypeScript project, many of the HTTP API methods now have stricter argument typing, which hopefully helps guide developers towards proper argument usage for Slack's HTTP API methods. + +**If you use this package in a JavaScript project, no such guidance is provided and the breaking changes listed below do not apply to you.** + +This release broadly is composed of five significant changes to the `web-api` codebase: + +1. ⬆️ The minimum supported (and thus tested) version of node.js is now v18, +1. 🚨 Breaking changes to API method arguments for TypeScript users (_not_ for JavaScript users), +2. 🧹 We deprecated a few sets of methods that are on their way out, +3. 📝 Added a _ton_ of new hand-written JSDocs to provide useful method-specific context and descriptions directly in your IDE, and +4. 🧑‍🔬 Type tests for method arguments, formalizing some of the co-dependencies and constraints unique to specific API methods + +Let's dive into these three sets of changes and begin with the 🚨 Breaking Changes 🚨 to make sure we set you all up for success and an easy migration to v7. + +## Breaking Changes (TypeScript users only) + +### All Web API methods no longer allow arbitrary arguments + +Previously, the arguments provided to specific methods extended from a [`WebAPICallOptions` TypeScript interface](https://github.com/slackapi/node-slack-sdk/blob/main/packages/web-api/src/WebClient.ts#L68-L70). +This interface made all API arguments effectively type un-safe: you could place whatever properties you wanted on arguments, and +the TypeScript compiler would be fine with it. + +In v7, in an attempt to improve type safety, we have removed this argument. As a result, if you were using unknown or publicly undocumented API arguments, you will now see a TypeScript compiler error. If you _really_ want to send unsupported arguments to our APIs, you will have to tell TypeScript "no, trust me, I really want to do this" using [the `// @ts-expect-error` directive](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-9.html#-ts-expect-error-comments). + +If you find an issue with any of our method arguments, please let us know by [filing an issue in this project](https://github.com/slackapi/node-slack-sdk/issues/new/choose)! + +### Many Web API methods have new, sometimes quite-specific, type constraints + +Another change that only affects TypeScript projects. A full and detailed list of changes is at the end of this document, with migration steps where applicable. + +## Deprecating some methods + +The following sets of methods have been deprecated and will be removed in a future major version of `@slack/web-api`: + +- `channels.*` +- `groups.*` +- `im.*` +- `mpim.*` + +Generally, all of the above methods have equivalents available under the `conversations.*` APIs. + +## Adding JSDoc to methods and method arguments + +This one benefits both TypeScript and JavaScript users! Both API methods and API arguments are exhaustively documented with JSDoc. It includes the API method descriptions, mimicking the hand-written docs we have on https://api.slack.com/methods, as well as documenting each API argument and linking to relevant contextual documentation where applicable. + +## Type tests for method arguments + +All of the above-mentioned new TypeScript API argument constraints are exhaustively tested under `test/types/methods`. When in doubt, read these tests to understand exactly what combination of arguments are expected to raise a TypeScript error, and which ones are not. + +## TypeScript API argument changes + +Many of the API argument changes follow a similar pattern. Previously, API arguments would be modeled as a 'bag of optional arguments.' While this was a decent approach to at least surface what arguments were available, they did not model certain arguments _well_. In particular, API arguments that had either/or-style constraints (e.g. `chat.postMessage` accepts _either_ `channel` and `blocks` _or_ `channel` and `attachments` - but never all three) could not be modeled with this approach. This could lead developers to a situation where, at runtime, they would get error responses from Slack APIs when using a combination of arguments that were unsupported. + +Moving forward, all of our APIs were exhaustively tested and the arguments to them modeled in such a way that would prevent TypeScript developers from using the APIs with an unsupported combination of arguments - ideally preventing ever encountering these avoidable runtime errors. + +Without further ado, let's dive in the changes for each API: + +## [`admin.analytics.getFile`](https://api.slack.com/methods/admin.analytics.getFile) + +Previously, most arguments to this API were optional. Now, the specific combinations of `type`, `date` and `metadata_only` are [more strictly typed to ensure only valid combinations are possible](https://github.com/slackapi/node-slack-sdk/pull/1673/files?diff=unified&w=0#diff-49ab2c95a2115046c53fe532bcd82a4e52434c16fbf1a7fdab08a764321ac0cdR44). + +## `admin.apps.*` + +You can no longer provide _both_ `request_id` and `app_id` - these APIs will only accept one of these arguments. Similarly for `enterprise_id` and `team_id` - only one can be provided, and one of them is required as well. + +### [`admin.apps.activities.list`](https://api.slack.com/methods/admin.apps.activities.list) + +- `component_type` is no longer any `string`, but rather it must be one of: `events_api`, `workflows`, `functions`, `tables` +- `min_log_level` is no longer any `string`, but rather it must be one of: `trace`, `debug`, `info`, `warn`, `error`, `fatal` +- `sort_direction` is no longer any `string`, but rather it must be one of: `asc`, `desc` +- `source` is no longer any `string`, but rather it must be one of: `slack`, `developer` + +## `admin.auth.*` + +- `entity_type` is no longer any `string` and is now an enum that only accepts the only valid value: `USER` +- `policy_name` is no longer any `string` and is now an enum that only accepts the only valid value: `email_password` + +## `admin.barriers.*` + +The `restricted_subjects` array is no longer a `string[]` but enforces an array with the exact values `['im', 'mpim', 'call']` - which these APIs demands (see e.g. [`admin.barriers.create` usage info](https://api.slack.com/methods/admin.barriers.create#markdown)). + +## `admin.conversations.*` + +- The `channel_ids` parameter now must be passed at least one channel ID - no more empty arrays allowed! +- Similarly, `team_ids` must be passed at least one team ID. Empty arrays: no good. +- This might sound familiar, but `user_ids` must be passed at least one user ID. Keeping it consistent around here, folks. +- The `org_wide` and `team_id` parameters influence one another: if `org_wide` is true, `team_id` must not be provided. Conversely, if `team_id` is provided, `org_wide` should be omitted, or, if you really want to include it, it must be `false`. Previously, both properties were simply optional. + +### [`admin.conversations.search`](https://api.slack.com/methods/admin.conversations.search) + +The `search_channel_types` argument is [now an enumeration of specific values](https://github.com/slackapi/node-slack-sdk/pull/1673/files?diff=unified&w=0#diff-16abafd789456431bd84945a174544e42cf9d0ddda6077b4cfcdd85377a1971eR8), rather than just any old `string` you want. + +## `admin.functions.*` + +- `function_ids` now requires at least one array element. +- `visibility` is no longer any `string` and instead an enumeration: `everyone`, `app_collaborators`, `named_entities`, `no_one`. + +## `admin.roles.*` + +- `entity_ids` now requires at least one array element. +- `user_ids` must be passed at least one user ID. +- `sort_dir` will accept either `asc` or `desc`. Previously any `string` was, unfortunately, A-OK. + +## `admin.teams.*` + +- `team_discoverability` and `discoverability` are now enumerations consisting of: `open`, `closed`, `invite_only`, `unlisted`, rather than any `string`. +- `channel_ids` must be passed at least one channel ID. + +## `admin.users.*` + +- `channel_ids` must be passed at least one channel ID. Previously this parameter was a string, so you had to comma-separate your channel IDs manually, as if this was the 1800s. +- `user_ids` must be passed at least one user ID. Otherwise, what, you're going to invite 0 users? Who do you think you're kidding? + +### [`admin.users.list`](https://api.slack.com/methods/admin.users.list) + +The `team_id` and `include_deactivated_user_workspaces` parameters were previously both optional. However, they kind of depend on each other. You can either provide a `team_id` and no `include_deactivated_user_workspaces` (or set it to `false`), or set `include_deactivated_user_workspaces` to `true` and omit `team_id`. Otherwise providing both doesn't make any sense at all? We need to be logically consistent, people! + +### [`admin.users.session.list`](https://api.slack.com/methods/admin.users.session.list) + +This API will now accept either _both_ `team_id` and `user_id`, or _neither_. Previously both properties were optional, which was not ideal. + +## `admin.workflows.*` + +- `collaborator_ids` must be passed at least one collaborator ID. Collaborating with 0 other humans is not really collaborating, now is it? +- Similarly, `workflow_ids` must be passed at least one workflow ID. + +### [`admin.workflows.search`](https://api.slack.com/methods/admin.workflows.search) + +- `sort_dir` will accept either `asc` or `desc`. Previously any `string` was, unfortunately, A-OK. +- `sort` now only accepts `premium_runs`, whereas previously you could pass it any `string`. +- `source` now only accepts either `code` or `workflow_builder`, whereas before you could feed it any `string`. + +## `apps.connections.open` + +Previously this method could be called without any arguments. Now, arguments are required. The migration path is to pass an empty object (`{}`) to `apps.connections.open`. + +## `apps.manifest.*` + +Previously the `manifest` parameter was a `string`. In reality this is a very complex object, which we've done our best to model in excruciating detail. + +## `auth.test` + +Previously this method could be called without any arguments. Now, arguments are required. The migration path is to pass an empty object (`{}`) to `auth.test`. + +## `bookmarks.*` + +- `type` now accepts only one value: `link`. +- The `link` parameter (not to be confused with the `type: 'link'` value in the previous point!) was previously optional for the `bookmarks.add` API. That's just silly as a bookmark necessarily requires a link, so it is now a required property. It is, however, optional for the `bookmarks.edit` API. + +## `chat.*` + +- Previously, we didn't model message contents very well. All of the various message-posting APIs (`postEphemeral`, `postMessage`, `scheduleMessage`) would accept `text`, `attachments`, and `blocks`, but all were optional arguments. That's not exactly correct, however. Now, you _must_ provide one of these three. Previously, you could avoid all three if you wished! Additionally, if you use `attachments` or `blocks`, `text` becomes an optional addition for you to specify and will be used as fallback text for notifications. +- The properties `as_user`, `username`, `icon_emoji` and `icon_url` interact with one another: + - If you set `as_user` to `true`, you cannot set `icon_emoji`, `icon_url` or `username`. + - You can provide either `icon_emoji` or `icon_url`, but never both. +- If you set `reply_broadcast` to `true`, then you must also provide a `thread_ts`. Previously, both were optional. + +## `conversations.*` + +- For APIs that accept either `channel_id` or `invite_id` (`conversations.acceptSharedInvite`), we now enforce specifying one or the other (but not both). +- For APIs that accept either `emails` or `user_ids` (`conversations.inviteShared`), we now enforce specifying one or the other (but not both). +- For APIs that accept either `channel` or `users` (`conversations.open`), we now enforce specifying one or the other (but not both). + +## `files.*` + +- You must provide either `content` or `file`. Previously these were both optional. +- If you provide the `thread_ts` argument, you now must also provide the `channels`, or `channel_id` argument (depending on the method). +- The `files` array parameter must now be provided at least one element. +- The `files.remote.*` APIs require one of `file` or `external_id`, but not both. Previously both were optional. + +## `reactions.*` + +- `channel` and `timestamp` are now required properties for `reactions.add`. +- `reactions.add` no longer supports the `file` and `file_comment` parameters, as per our [API docs for this method](https://api.slack.com/methods/reactions.add). +- `reactions.get` and `reactions.remove` now require to supply one and only one of: + - both `channel` and `timestamp`, or + - a `file` encoded file ID, or + - a `file_comment` ID + +## `stars.*` + +The `add` and `remove` APIs require one of: + +- both `channel` and `timestamp`, or +- a `file` encoded file ID, or +- a `file_comment` ID + +## `team.*` + +- `change_type` is no longer any `string` but an enumeration of: `added`, `removed`, `enabled`, `disabled`, `updated` + +## `users.*` + +- The deprecated `presence` parameter for [`users.list`](https://api.slack.com/methods/users.list) has been removed. +- The `profile` parameter for [`users.profile.set`](https://api.slack.com/methods/users.profile.set) has been changed from a string to, basically, any object you wish. + +## `views.*` + +All `views.*` APIs now accept either a `trigger_id` or an `interactivity_pointer`, as they both effectively serve the same purpose. + +In addition, `views.update` now accepts either a `external_id` or a `view_id` parameter - but not both. \ No newline at end of file diff --git a/docs/content/support-schedule.md b/docs/content/support-schedule.md new file mode 100644 index 000000000..b610b4328 --- /dev/null +++ b/docs/content/support-schedule.md @@ -0,0 +1,27 @@ +# Support schedule + +## How long will my version be supported? + +You have at least until the next Node.js LTS version maintenance ends. According to [current schedule](https://github.com/nodejs/Release), this happens once every 12 months. + +If there is a newer major version of the Node SDK available today than the one you are using, plan your migration now. Some updates will only make it to the newest major version, and your feedback on the transition will help the community. Don't panic though, as long as you aren't a year behind on updates you still have until the next LTS version maintenance ends. + +## How our schedule works + +In order to guarantee you at least 90 days to migrate from one major version to the next, the maintainers make the following three commitments: + +1. If we've decided to end support for a major version, we will announce this at least 90 days before the next Node.js LTS version maintenance ends. +2. Within 30 days of that announcement, a new major version release will be available to start developing against. +3. After a major version's maintenance ends, there will be a 30 day period of soft-maintenance for critical fixes and merging low impact contributions. It then is considered end-of-life. + +![Node v4.x LTS maintenance end in April creates the opportunity to sunset @slack/client v3.x](/img/support-schedule.png) + +_Node v4.x LTS maintenance end in April creates the opportunity to sunset `@slack/client` v3.x_ + +These commitments are important because it enables both users and maintainers of the Node SDK to establish expectations and plan ahead. + +For users, it means you'll have at least 120 days from when you can first find out your major version will be unsupported, to when its end-of-life. It also means you have at least 90 of those days to migrate your application to use the next major version because a release will be available. Lastly, it means you have up to 30 days where you can speak up for the major version changes that are most important to you. + +For maintainers, it gives us the freedom of always developing against supported versions of Node and most dependencies. It also keeps us honest by giving us a finite amount of time to make decisions about large changes and to communicate those decisions. Maintainers can decide to create a new major version at any time in the year, but we cannot move a major version out of maintenance without meeting the transition expectations of users. + +Lastly, the 30 day soft maintenance period is not meant for new feature development. Maintainers will gladly review contributions, issues, and PRs, but we may close them without a release if they are seen as risky or out of scope. diff --git a/docs/content/typescript.md b/docs/content/typescript.md index f3e801cba..0f1b60f7b 100644 --- a/docs/content/typescript.md +++ b/docs/content/typescript.md @@ -16,7 +16,7 @@ The v6 versions of `@slack/web-api`, `@slack/rtm-api`, and `@slack/webhook` pack The v5 versions of `@slack/web-api`, `@slack/rtm-api`, and `@slack/webhook` packages are supported to build against TypeScript v3.3.0 or higher. The v4 versions of the `@slack/web-api`, `@slack/rtm-api`, and `@slack/webhook` packages are supported to build against TypeScript v2.7.0 or higher. -This project adheres to a [support schedule](https://github.com/slackapi/node-slack-sdk/wiki/Support-Schedule) so that you can predictably plan your time and effort for migration. That plan covers versions of Node.js and how they correlate to major versions of these packages. However, for TypeScript, we're using a slightly different convention. Each time a major version is released, we take the most recent version of TypeScript available, and make it the minimum. That doesn't mean that the project won't work against earlier versions of TypeScript, but it does give this project a chance to adopt new features from up to and including that version of TypeScript throughout the time that the major version of the package is supported. +This project adheres to a [support schedule](/support-schedule) so that you can predictably plan your time and effort for migration. That plan covers versions of Node.js and how they correlate to major versions of these packages. However, for TypeScript, we're using a slightly different convention. Each time a major version is released, we take the most recent version of TypeScript available, and make it the minimum. That doesn't mean that the project won't work against earlier versions of TypeScript, but it does give this project a chance to adopt new features from up to and including that version of TypeScript throughout the time that the major version of the package is supported. --- diff --git a/docs/sidebars.js b/docs/sidebars.js index b30e37290..3be53222b 100644 --- a/docs/sidebars.js +++ b/docs/sidebars.js @@ -31,6 +31,18 @@ const sidebars = { 'packages/rtm-api', 'packages/webhook', 'packages/socket-mode', + { + type: 'category', + label: 'Migration', + items: [ + 'migration/migrating-to-v4', + 'migration/migrating-to-v5', + 'migration/migrating-to-v6', + 'migration/migrating-socket-mode-package-to-v2', + 'migration/migrating-web-api-package-to-v7', + + ], + }, { type: 'category', label: 'Deprecated packages', @@ -44,9 +56,7 @@ const sidebars = { type: 'category', label: 'Tutorials', items: [ - 'tutorials/local-development', - 'tutorials/migrating-to-v5', - 'tutorials/migrating-to-v6', + 'tutorials/local-development' ], }, {type: 'html', value: '
'}, @@ -147,6 +157,7 @@ const sidebars = { ], }, {type: 'html', value: '
'}, + 'support-schedule', { type: 'link', label: 'Release notes', diff --git a/docs/static/img/support-schedule.png b/docs/static/img/support-schedule.png new file mode 100644 index 0000000000000000000000000000000000000000..9b64ae5a5d4794ba06cfab1ac56e90d7ab9130f3 GIT binary patch literal 47247 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 literal 0 HcmV?d00001 From 690742944ddb12cb8a046ca0e6dc3a2b198a24d6 Mon Sep 17 00:00:00 2001 From: Luke Russell <31357343+lukegalbraithrussell@users.noreply.github.com> Date: Tue, 29 Apr 2025 14:59:02 -0700 Subject: [PATCH 2/5] Apply suggestions from code review Co-authored-by: Haley Elmendorf <31392893+haleychaas@users.noreply.github.com> --- docs/content/migration/migrating-to-v4.md | 8 ++++---- docs/content/migration/migrating-to-v5.md | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/content/migration/migrating-to-v4.md b/docs/content/migration/migrating-to-v4.md index 8f170394f..dc83861bb 100644 --- a/docs/content/migration/migrating-to-v4.md +++ b/docs/content/migration/migrating-to-v4.md @@ -1,6 +1,6 @@ # Migrating to v4.x -This migration guide helps you transition an application written using the `v3.x` series of this package, to the `v4.x` series. This guide focuses specifically on the breaking changes to help get your existing app up and running as quickly as possible. In some cases, there may be better techniques for accomplishing what your app already does by utilizing brand new features or new API. Learn about all the new features in our [`v4.0.0` release notes](https://github.com/slackapi/node-slack-sdk/releases/tag/v4.0.0) if you'd like to go beyond a simple port. +This migration guide helps you transition an application written using the `v3.x` series of this package, to the `v4.x` series. This guide focuses specifically on the breaking changes to help get your existing app up and running as quickly as possible. In some cases, there may be better techniques for accomplishing what your app already does by utilizing brand new features or new API methods. Learn about all the new features in our [`v4.0.0` release notes](https://github.com/slackapi/node-slack-sdk/releases/tag/v4.0.0) if you'd like to go beyond a simple port. ## WebClient @@ -80,7 +80,7 @@ The datastore APIs have helped apps keep track of team state since this feature At a high level, here are the design issues that could not be addressed in a backwards compatible way: -- **Synchronous calling convention** - In order to plug in storage destinations other than memory (`SlackMemoryDataStore`) the code to reach that destination would need to be asynchronous. This is important if you want to share state across many processes while horizontally scaling an app. As a result, the maintainers have never seen an implementation of the `SlackDataStore` interface other than the memory-based one provided. +- **Synchronous calling convention** - In order to plug in storage destinations other than memory (`SlackMemoryDataStore`), the code to reach that destination would need to be asynchronous. This is important if you want to share state across many processes while horizontally scaling an app. As a result, the maintainers have never seen an implementation of the `SlackDataStore` interface other than the memory-based one provided. - **Names versus IDs** - While we always thought it was a good idea to use IDs anywhere possible in programmatic use, the Slack platform wasn't overly strict about it. With the introduction of Shared Channels, we cannot preserve any guarantees of uniqueness for usernames, as we mentioned in this [changelog entry](https://api.slack.com/changelog/2017-09-the-one-about-usernames). In fact, channel names also lose these types of guarantees. The APIs in the `SlackDataStore` that operate on names instead of IDs start to break since the fundamental assumptions are not true anymore. @@ -119,7 +119,7 @@ If you aren't sure how to translate a specific data store method into a Web API You'll notice that this code has become asynchronous. This will likely be the largest challenge in migrating away from the data store, but for most developers it will be worth it. -For the majority of apps, you will be ready for v4 at this point. If your app is having performance related issues, there's room to make improvements by caching the data that is relevant to your app. This should only be taken on if you believe its necessary, since cache invalidation is one of the [only two hard things in computer science](https://martinfowler.com/bliki/TwoHardThings.html). +For the majority of apps, you will be ready for v4 at this point. If your app is having performance related issues, there's room to make improvements by caching the data that is relevant to your app. This should only be taken on if you believe it's necessary, since cache invalidation is one of the [only two hard things in computer science](https://martinfowler.com/bliki/TwoHardThings.html). The approach for caching data that we recommend is to pick out the data your app needs, store it at connection time, and update it according to a determined policy. You may want to disable the `useRtmConnect` option in order to get more data at connection time. @@ -197,7 +197,7 @@ rtm.addOutgoingEvent(true, message.type, message) }); ``` -### Events +### Events {#events} The `RTMClient` now has more well-defined states (and substates) that you may observe using the [`EventEmitter` API pattern](https://nodejs.org/api/events.html). The following table helps describe the relationship between events in the `v3.x` series and events in the `v4.x` series. diff --git a/docs/content/migration/migrating-to-v5.md b/docs/content/migration/migrating-to-v5.md index 75b6b2912..435f7db1a 100644 --- a/docs/content/migration/migrating-to-v5.md +++ b/docs/content/migration/migrating-to-v5.md @@ -17,7 +17,7 @@ Those packages only moved repositories, but did not get updated in this process. ## Update to a supported version of Node -These package have dropped support for versions of Node that are no longer supported. We recommend updating to the latest LTS version of [Node](https://nodejs.org/en/), which at this time is v10.15.3. The minimum supported version is v8.9.0. +These packages have dropped support for versions of Node that are no longer supported. We recommend updating to the latest LTS version of [Node](https://nodejs.org/en/), which at this time is v10.15.3. The minimum supported version is v8.9.0. :::info[Learn more about our [support schedule](/support-schedule) so that you can prepare and plan for future updates.] From 4a31524a5888d2911d4629413667b79239b9f3d0 Mon Sep 17 00:00:00 2001 From: Luke Russell <31357343+lukegalbraithrussell@users.noreply.github.com> Date: Wed, 30 Apr 2025 08:32:01 -0700 Subject: [PATCH 3/5] Apply suggestions from code review Co-authored-by: Haley Elmendorf <31392893+haleychaas@users.noreply.github.com> --- docs/content/migration/migrating-web-api-package-to-v7.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/content/migration/migrating-web-api-package-to-v7.md b/docs/content/migration/migrating-web-api-package-to-v7.md index f27e44374..76d0338b4 100644 --- a/docs/content/migration/migrating-web-api-package-to-v7.md +++ b/docs/content/migration/migrating-web-api-package-to-v7.md @@ -14,7 +14,7 @@ npm i @slack/web-api **TL;DR**: this package now supports only node v18 and newer, and HTTP API arguments passed to methods in this project in the context of a TypeScript project are stricter. -This release focusses on the type safety of Slack HTTP API method arguments provided by `@slack/web-api`. If you use this package in a TypeScript project, many of the HTTP API methods now have stricter argument typing, which hopefully helps guide developers towards proper argument usage for Slack's HTTP API methods. +This release focuses on the type safety of Slack HTTP API method arguments provided by `@slack/web-api`. If you use this package in a TypeScript project, many of the HTTP API methods now have stricter argument typing, which hopefully helps guide developers towards proper argument usage for Slack's HTTP API methods. **If you use this package in a JavaScript project, no such guidance is provided and the breaking changes listed below do not apply to you.** @@ -93,7 +93,7 @@ You can no longer provide _both_ `request_id` and `app_id` - these APIs will onl ## `admin.barriers.*` -The `restricted_subjects` array is no longer a `string[]` but enforces an array with the exact values `['im', 'mpim', 'call']` - which these APIs demands (see e.g. [`admin.barriers.create` usage info](https://api.slack.com/methods/admin.barriers.create#markdown)). +The `restricted_subjects` array is no longer a `string[]` but enforces an array with the exact values `['im', 'mpim', 'call']` - which these APIs demand (see e.g. [`admin.barriers.create` usage info](https://api.slack.com/methods/admin.barriers.create#markdown)). ## `admin.conversations.*` From aef6e6853d05aac0d8a8989b50b92cd4a9c6b990 Mon Sep 17 00:00:00 2001 From: Luke Russell Date: Wed, 30 Apr 2025 08:40:03 -0700 Subject: [PATCH 4/5] docs: tweaks --- docs/content/migration/migrating-to-v4.md | 2 +- docs/content/migration/migrating-to-v6.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/content/migration/migrating-to-v4.md b/docs/content/migration/migrating-to-v4.md index dc83861bb..9b318c4d6 100644 --- a/docs/content/migration/migrating-to-v4.md +++ b/docs/content/migration/migrating-to-v4.md @@ -206,7 +206,7 @@ The `RTMClient` now has more well-defined states (and substates) that you may ob | `disconnected` | `disconnect` | The client is not connected to the platform. This is a steady state - no attempt to connect is occurring. | | `connecting` | `connecting` / `attempting_reconnect` | The client is in the process of connecting to the platform. | | `authenticated` | `authenticated` | The client has authenticated with the platform. The `rtm.connect` or `rtm.start` response is emitted as an argument. This is a sub-state of `connecting`. | -| `connected` | | The client is connected to the platform and incoming events will start being emittied. | +| `connected` | | The client is connected to the platform and incoming events will start being emitted. | | `ready` | `open` | The client is ready to send outgoing messages. This is a sub-state of `connected` | | `disconnecting` | | The client is no longer connected to the platform and cleaning up its resources. It will soon transition to `disconnected`. | | `reconnecting` | | The client is no longer connected to the platform and cleaning up its resources. It will soon transition to `connecting`. | diff --git a/docs/content/migration/migrating-to-v6.md b/docs/content/migration/migrating-to-v6.md index 5c7bcc6d7..c07dd8514 100644 --- a/docs/content/migration/migrating-to-v6.md +++ b/docs/content/migration/migrating-to-v6.md @@ -89,7 +89,7 @@ installationStore: { ### Deprecating `@slack/events-api` & `@slack/interactive-messages` packages -`@slack/events-api` and `@slack/interactive-messages` will be deprecated on **January 12th, 2021**. We will only implement **critical bug fixes** until the official end of life date and close non critical issues and pull requests, which is slated for **May 31st, 2021**. At this time, development will fully stop for these packages and all remaining open issues and pull requests will be closed. Bolt for JavaScript can now perform all the same functionality as these packages. We think you’ll love the more modern set of features you get in Bolt. Here are some migration examples: +`@slack/events-api` and `@slack/interactive-messages`are now deprecated and have reached end of life. Development has fully stopped for these packages and all remaining open issues and pull requests have been closed. Bolt for JavaScript can now perform all the same functionality as these packages. We think you’ll love the more modern set of features you get in Bolt. Here are some migration examples: Before: ```javascript From 4dd3243d4e986f9ecb7e2619ecc63230b4cddf93 Mon Sep 17 00:00:00 2001 From: Luke Russell Date: Wed, 30 Apr 2025 10:02:47 -0700 Subject: [PATCH 5/5] docs: small tweaks: --- docs/content/migration/migrating-to-v5.md | 6 +----- docs/content/migration/migrating-to-v6.md | 6 +----- docs/content/packages/web-api.md | 2 +- 3 files changed, 3 insertions(+), 11 deletions(-) diff --git a/docs/content/migration/migrating-to-v5.md b/docs/content/migration/migrating-to-v5.md index 435f7db1a..f0b5872ec 100644 --- a/docs/content/migration/migrating-to-v5.md +++ b/docs/content/migration/migrating-to-v5.md @@ -1,8 +1,4 @@ ---- -title: Migrating to v5.x ---- - -# Migration Guide (v4 to v5) +# Migrating to v5.x This tutorial will guide you through the process of updating your app from using the `@slack/client` package (`v4.x`) to using the new, improved, and independent packages, starting with `v5.0.0`. diff --git a/docs/content/migration/migrating-to-v6.md b/docs/content/migration/migrating-to-v6.md index c07dd8514..b21ffaf89 100644 --- a/docs/content/migration/migrating-to-v6.md +++ b/docs/content/migration/migrating-to-v6.md @@ -1,8 +1,4 @@ ---- -title: Migrating to v6.x ---- - -# Migration Guide (v5 to v6) +# Migrating to v6.x The following packages have been updated in the release being dubbed as `v6`: * `@slack/web-api@6.0.0` diff --git a/docs/content/packages/web-api.md b/docs/content/packages/web-api.md index cd5b4446f..6e619d346 100644 --- a/docs/content/packages/web-api.md +++ b/docs/content/packages/web-api.md @@ -1,8 +1,8 @@ --- -title: Web API slug: /web-api --- +# Web API The `@slack/web-api` package contains a simple, convenient, and configurable HTTP client for making requests to Slack's [Web API](https://api.slack.com/web). Use it in your app to call any of the over 130