@onesignal/onesignal-vue3 1.0.2 β†’ 2.0.0-beta.2

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.
package/.eslintrc.js CHANGED
@@ -11,7 +11,6 @@ module.exports = {
11
11
  extensions: [
12
12
  ".js",
13
13
  ".ts",
14
- ".tsx",
15
14
  ],
16
15
  },
17
16
  },
@@ -28,6 +27,9 @@ module.exports = {
28
27
  plugins: [
29
28
  '@typescript-eslint',
30
29
  ],
30
+ extends: [
31
+ 'plugin:@typescript-eslint/recommended',
32
+ ],
31
33
  rules: {
32
34
  "prefer-destructuring": 0,
33
35
  "no-param-reassign": 0,
@@ -35,6 +37,7 @@ module.exports = {
35
37
  "dot-notation": 0,
36
38
  "no-continue": 0,
37
39
  "no-unused-vars": "off",
38
- "@typescript-eslint/no-unused-vars": ["error"]
40
+ "@typescript-eslint/no-unused-vars": ["error"],
41
+ "no-prototype-builtins": "warn",
39
42
  },
40
43
  };
@@ -0,0 +1,19 @@
1
+ name: πŸ™‹β€β™‚οΈ Ask a question
2
+ description: Tell us what's on your mind
3
+ title: "[Question]: "
4
+ labels: ["triage"]
5
+ assignees:
6
+ - OneSignal/eng-developer-sdk
7
+ body:
8
+ - type: markdown
9
+ attributes:
10
+ value: |
11
+ Having issues integrating this SDK?
12
+ - type: textarea
13
+ id: question
14
+ attributes:
15
+ label: How can we help?
16
+ description: Specific question regarding integrating this SDK.
17
+ placeholder: How do I...?
18
+ validations:
19
+ required: true
@@ -0,0 +1,70 @@
1
+ name: πŸͺ³ Bug report
2
+ description: File a bug report
3
+ title: "[Bug]: "
4
+ labels: ["bug", "triage"]
5
+ assignees:
6
+ - OneSignal/eng-developer-sdk
7
+ body:
8
+ - type: markdown
9
+ attributes:
10
+ value: |
11
+ Thanks for taking the time to fill out this bug report!
12
+ - type: textarea
13
+ id: what-happened
14
+ attributes:
15
+ label: What happened?
16
+ description: Provide a thorough description of whats going on.
17
+ placeholder: The latest version of the SDK causes a runtime error.
18
+ validations:
19
+ required: true
20
+ - type: dropdown
21
+ id: browsers
22
+ attributes:
23
+ label: What browsers are you seeing the problem on?
24
+ multiple: true
25
+ options:
26
+ - Firefox
27
+ - Chrome (Chromium)
28
+ - Safari
29
+ - Microsoft Edge
30
+ - Opera
31
+ - Brave
32
+ - Other
33
+ validations:
34
+ required: true
35
+ - type: input
36
+ id: operating-system
37
+ attributes:
38
+ label: What operating system are you running?
39
+ description: Make sure to include the version.
40
+ placeholder: macOS Monterey 12.3.1
41
+ validations:
42
+ required: true
43
+ - type: textarea
44
+ id: reproduction-steps
45
+ attributes:
46
+ label: Steps to reproduce?
47
+ description: Provide as much detail as posible to reproduce the issue.
48
+ placeholder: |
49
+ 1. Install dependencies: vX.Y.Z, etc...
50
+ 2. Run the app
51
+ 3. Click on the notification prompt
52
+ 4. Note that this causes a runtime error and a failed subscription.
53
+ render: Markdown
54
+ validations:
55
+ required: true
56
+ - type: textarea
57
+ id: what-are-expectations
58
+ attributes:
59
+ label: What did you expect to happen?
60
+ description: Also tell us, what did you expect to happen?
61
+ placeholder: I expected the notification prompt to cause a native permission change in the browser.
62
+ validations:
63
+ required: true
64
+ - type: textarea
65
+ id: logs
66
+ attributes:
67
+ label: Relevant log output
68
+ description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks.
69
+ render: Shell
70
+
@@ -0,0 +1,19 @@
1
+ name: πŸ“£ General feedback
2
+ description: Tell us what's on your mind
3
+ title: "[Feedback]: "
4
+ labels: ["triage"]
5
+ assignees:
6
+ - OneSignal/eng-developer-sdk
7
+ body:
8
+ - type: markdown
9
+ attributes:
10
+ value: |
11
+ Thanks for sharing your valuable feedback!
12
+ - type: textarea
13
+ id: feedback
14
+ attributes:
15
+ label: What's on your mind?
16
+ description: Feedback regarding this SDK.
17
+ placeholder: Share your feedback...
18
+ validations:
19
+ required: true
@@ -0,0 +1,34 @@
1
+ # This is an action to close asana tasks that were generated by Github issues
2
+
3
+ name: Zapier web hook
4
+
5
+ # Controls when the workflow will run
6
+ on:
7
+ # Triggers the workflow on push or pull request events but only for the "main" branch
8
+ issues:
9
+ types: [closed]
10
+
11
+ permissions:
12
+ issues: read
13
+
14
+ # A workflow run is made up of one or more jobs that can run sequentially or in parallel
15
+ jobs:
16
+ # This workflow contains a single job called "build"
17
+ build:
18
+ # The type of runner that the job will run on
19
+ runs-on: ubuntu-latest
20
+
21
+ # Steps represent a sequence of tasks that will be executed as part of the job
22
+ steps:
23
+ # Runs a set of commands using the runners shell
24
+ - name: Call Zapier web hook to close Asana task
25
+ if: ${{ !github.event.issue.pull_request }}
26
+ env:
27
+ ISSUE_TITLE: ${{ github.event.issue.title }}
28
+ run: |
29
+ curl --location --request POST 'https://hooks.zapier.com/hooks/catch/12728683/b7009qc/' \
30
+ --header 'Content-Type: application/json' \
31
+ --header 'Accept: application/json' \
32
+ --data-raw '{
33
+ "task_name" : "$ISSUE_TITLE"
34
+ }'
@@ -0,0 +1,41 @@
1
+ name: Release Drafter
2
+
3
+ on:
4
+ push:
5
+ # branches to consider in the event; optional, defaults to all
6
+ branches:
7
+ - main
8
+ # pull_request event is required only for autolabeler
9
+ pull_request:
10
+ # Only following types are handled by the action, but one can default to all as well
11
+ types: [opened, reopened, synchronize]
12
+ # pull_request_target event is required for autolabeler to support PRs from forks
13
+ # pull_request_target:
14
+ # types: [opened, reopened, synchronize]
15
+
16
+ permissions:
17
+ contents: read
18
+
19
+ jobs:
20
+ update_release_draft:
21
+ permissions:
22
+ # write permission is required to create a github release
23
+ contents: write
24
+ # write permission is required for autolabeler
25
+ # otherwise, read permission is required at least
26
+ pull-requests: write
27
+ runs-on: ubuntu-latest
28
+ steps:
29
+ # (Optional) GitHub Enterprise requires GHE_HOST variable set
30
+ #- name: Set GHE_HOST
31
+ # run: |
32
+ # echo "GHE_HOST=${GITHUB_SERVER_URL##https:\/\/}" >> $GITHUB_ENV
33
+
34
+ # Drafts your next Release notes as Pull Requests are merged into "master"
35
+ - uses: release-drafter/release-drafter@v5
36
+ # (Optional) specify config name to use, relative to .github/. Default: release-drafter.yml
37
+ # with:
38
+ # config-name: my-config.yml
39
+ # disable-autolabeler: true
40
+ env:
41
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -0,0 +1,27 @@
1
+ name-template: $RESOLVED_VERSION
2
+ tag-template: $RESOLVED_VERSION
3
+ categories:
4
+ - title: πŸš€ Features
5
+ label: Enhancement / Feature
6
+ - title: πŸ› Bug Fixes
7
+ label: Bug
8
+ - title: 🧰 Improvements
9
+ label: Improvement
10
+ - title: down arrow Dependency Updates
11
+ label: Dependencies
12
+ change-template: '- $TITLE (#$NUMBER)'
13
+ version-resolver:
14
+ major:
15
+ labels:
16
+ - 'major'
17
+ minor:
18
+ labels:
19
+ - 'minor'
20
+ patch:
21
+ labels:
22
+ - 'patch'
23
+ default: patch
24
+ template: |
25
+ ## Other Changes
26
+
27
+ $CHANGES
@@ -0,0 +1,34 @@
1
+ # This is an action to close asana tasks that were generated by Github issues
2
+
3
+ name: Zapier web hook
4
+
5
+ # Controls when the workflow will run
6
+ on:
7
+ # Triggers the workflow on push or pull request events but only for the "main" branch
8
+ issues:
9
+ types: [closed]
10
+
11
+ permissions:
12
+ issues: read
13
+
14
+ # A workflow run is made up of one or more jobs that can run sequentially or in parallel
15
+ jobs:
16
+ # This workflow contains a single job called "build"
17
+ build:
18
+ # The type of runner that the job will run on
19
+ runs-on: ubuntu-latest
20
+
21
+ # Steps represent a sequence of tasks that will be executed as part of the job
22
+ steps:
23
+ # Runs a set of commands using the runners shell
24
+ - name: Call Zapier web hook to close Asana task
25
+ if: ${{ !github.event.issue.pull_request }}
26
+ env:
27
+ ISSUE_TITLE: ${{ github.event.issue.title }}
28
+ run: |
29
+ curl --location --request POST 'https://hooks.zapier.com/hooks/catch/12728683/b7009qc/' \
30
+ --header 'Content-Type: application/json' \
31
+ --header 'Accept: application/json' \
32
+ --data-raw '{
33
+ "task_name" : "$ISSUE_TITLE"
34
+ }'
@@ -0,0 +1,41 @@
1
+ name: Release Drafter
2
+
3
+ on:
4
+ push:
5
+ # branches to consider in the event; optional, defaults to all
6
+ branches:
7
+ - main
8
+ # pull_request event is required only for autolabeler
9
+ pull_request:
10
+ # Only following types are handled by the action, but one can default to all as well
11
+ types: [opened, reopened, synchronize]
12
+ # pull_request_target event is required for autolabeler to support PRs from forks
13
+ # pull_request_target:
14
+ # types: [opened, reopened, synchronize]
15
+
16
+ permissions:
17
+ contents: read
18
+
19
+ jobs:
20
+ update_release_draft:
21
+ permissions:
22
+ # write permission is required to create a github release
23
+ contents: write
24
+ # write permission is required for autolabeler
25
+ # otherwise, read permission is required at least
26
+ pull-requests: write
27
+ runs-on: ubuntu-latest
28
+ steps:
29
+ # (Optional) GitHub Enterprise requires GHE_HOST variable set
30
+ #- name: Set GHE_HOST
31
+ # run: |
32
+ # echo "GHE_HOST=${GITHUB_SERVER_URL##https:\/\/}" >> $GITHUB_ENV
33
+
34
+ # Drafts your next Release notes as Pull Requests are merged into "master"
35
+ - uses: release-drafter/release-drafter@v5
36
+ # (Optional) specify config name to use, relative to .github/. Default: release-drafter.yml
37
+ # with:
38
+ # config-name: my-config.yml
39
+ # disable-autolabeler: true
40
+ env:
41
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -0,0 +1,251 @@
1
+
2
+ # Migration Guide
3
+
4
+ ## Version 2
5
+ ### Intro
6
+ In this release, we are making a significant shift from a device-centered model to a user-centered model. This means that instead of identifying devices, we now focus on identifying individual users. This update is part of a larger effort to shift towards a user-oriented omni-channel messaging system.
7
+
8
+ To facilitate this change, the externalId approach for identifying users is being replaced by the login and logout methods. In addition, the SDK now makes use of namespaces such as User, Notifications, and Slidedown to better separate code.
9
+
10
+ This guide will walk you through these and other important changes in the version 16 update.
11
+
12
+ ### Overview
13
+ Under the new model, the concept of a "player" is being updated to include three new concepts: users, subscriptions, and aliases.
14
+
15
+ ### Users
16
+ Users own subscriptions and are identified by aliases which are used to point to users using different alias schemes.
17
+
18
+ ### Subscriptions
19
+
20
+ Subscriptions refer to the way in which a user can receive various communication methods offered by OneSignal, including push notifications, SMS, and email.
21
+
22
+ ### Aliases
23
+ Aliases are identifiers that point to users and are made up of an alias label and id. Users can have multiple aliases. Consider the need to identify a user with your own application's unique identifier as well as identifiers from other integrated applications.
24
+
25
+ The SDK will use `external_id` as the default alias label for the public `OneSignal.login("1234")` method.
26
+
27
+ **Alias Example:**
28
+ ```
29
+ "aliases": [
30
+ {
31
+ "label": "external_id",
32
+ "id": "1234"
33
+ },
34
+ {
35
+ "label": "my_alias",
36
+ "id": "5678"
37
+ }
38
+ ]
39
+ ```
40
+
41
+ ```js
42
+ // WebSDK-specific example
43
+ {
44
+ external_id: "1234",
45
+ my_alias: "5678"
46
+ }
47
+ ```
48
+
49
+ # Guide
50
+ ## 1. Setup Changes
51
+ ### Service Worker File
52
+
53
+ From:
54
+ ```js
55
+ importScripts("https://onesignal.com/sdks/OneSignalSDKWorker.js");
56
+ ```
57
+
58
+ To:
59
+ ```js
60
+ importScripts("https://onesignal.com/sdks/web/v16/OneSignalSDK.sw.js");
61
+ ```
62
+
63
+ ## 2. External User ID
64
+ Update any usages of `OneSignal.setExternalId` to `OneSignal.login` or `OneSignal.logout`
65
+ From:
66
+ ```js
67
+ OneSignal.setExternalId("myId");
68
+ ```
69
+
70
+ To:
71
+ ```js
72
+ OneSignal.login("myId");
73
+ ```
74
+
75
+ Use `OneSignal.logout();` instead anywhere you have `OneSignal.setExternalId("");` or are setting it to `null`.
76
+
77
+ ## 3. API Changes
78
+ Update your code to use the new API. The following namespaces are on the `OneSignal` object.
79
+
80
+ ### User Namespace
81
+
82
+ Example:
83
+ ```js
84
+ OneSignal.User.addAlias("my_alias", "1234");
85
+ ```
86
+
87
+ All user functions are synchronous.
88
+
89
+ | Function Name | Description | Argument List |
90
+ | --------------- | ---------------------------------------------- | ------------------------------------ |
91
+ | `addAlias` | Adds a new alias for the current user. | `label: string, id: string` |
92
+ | `addAliases` | Adds multiple aliases for the current user. | `aliases: { [key: string]: string }` |
93
+ | `removeAlias` | Removes an alias for the current user. | `label: string` |
94
+ | `removeAliases` | Removes multiple aliases for the current user. | `labels: string[]` |
95
+ | `addEmail` | Adds an email address for the current user. | `email: string` |
96
+ | `removeEmail` | Removes an email address for the current user. | `email: string` |
97
+ | `addSms` | Adds an SMS number for the current user. | `smsNumber: string` |
98
+ | `removeSms` | Removes an SMS number for the current user. | `smsNumber: string` |
99
+ | `addTag` | Adds a tag for the current user. | `key: string, value: string` |
100
+ | `addTags` | Adds multiple tags for the current user. | `tags: { [key: string]: string }` |
101
+ | `removeTag` | Removes a tag for the current user. | `key: string` |
102
+ | `removeTags` | Removes multiple tags for the current user. | `keys: string[]` |
103
+
104
+ ### Notifications Namespace
105
+
106
+ Example:
107
+ ```js
108
+ await OneSignal.Notifications.requestPermission();
109
+ ```
110
+
111
+ | Sync/Async | Function Name | Description | Argument List |
112
+ | ---------- | --------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
113
+ | `async` | `setDefaultUrl` | Sets the default URL for notifications. | `url` (string) |
114
+ | `async` | `setDefaultTitle` | Sets the default title for notifications. | `title` (string) |
115
+ | `sync` | `isPushSupported` | Returns true if the current browser supports web push. | |
116
+ | `async` | `getPermissionStatus` | Returns the browser's current notification permission. | `onComplete` (Action<NotificationPermission>) |
117
+ | `async` | `requestPermission` | Requests push notifications permission via the native browser prompt. | |
118
+ | `sync` | `addEventListener` | Adds an event listener for the following events:<br><br>- `click`<br>- `willDisplay`<br>- `dismiss`<br>- `permissionPromptDisplay`<br>- `permissionChange`*<br> * argument type: bool | - `<event>` (string)<br>- `(arg: <type>) => {}` (callback) |
119
+ | `sync` | `removeEventListener` | Removes the event listener. | `() => {}` (the event listener you want to remove) |
120
+
121
+
122
+
123
+ ### Slidedown Namespace
124
+
125
+ Example:
126
+ ```js
127
+ await OneSignal.Slidedown.promptPush();
128
+ ```
129
+
130
+ | Sync/Async | Function Name | Description | Argument List |
131
+ | ---------- | ---------------------- | ------------------------------------------------------------------------ | ---------------------------------------------------------------------------- |
132
+ | `async` | `promptPush` | Displays the notification permission prompt. | `options` (AutoPromptOptions) |
133
+ | `async` | `promptPushCategories` | Displays the notification permission prompt for notification categories. | `options` (AutoPromptOptions) |
134
+ | `async` | `promptSms` | Displays the SMS subscription prompt. | `options` (AutoPromptOptions) |
135
+ | `async` | `promptEmail` | Displays the email subscription prompt. | `options` (AutoPromptOptions) |
136
+ | `async` | `promptSmsAndEmail` | Displays the SMS and email subscription prompts. | `options` (AutoPromptOptions) |
137
+ | `sync` | `addEventListener` | Adds an event listener for the `slidedownShown` event. | - `event` ("slidedownShown"), <br>- `listener` ((wasShown: boolean) => void) |
138
+ | `sync` | `removeEventListener` | Removes an event listener for the `slidedownShown` event. | - `event` ("slidedownShown")<br>- `listener` ((wasShown: boolean) => void) |
139
+
140
+
141
+
142
+ ### Push Subscription Namespace
143
+
144
+ Example:
145
+ ```js
146
+ OneSignal.User.PushSubscription.optIn();
147
+ ```
148
+
149
+ | Sync/Async | Property/Function | Description | Argument List |
150
+ | ---------- | ----------------------- | --------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------- |
151
+ | | `id` | Gets the current user's ID. | |
152
+ | | `token` | Gets the current user's push notification token. | |
153
+ | | `optedIn` | Gets a boolean value indicating whether the current user is subscribed to push notifications. | |
154
+ | `async` | `optIn()` | Subscribes the current user to push notifications. | |
155
+ | `async` | `optOut()` | Unsubscribes the current user from push notifications. | |
156
+ | `sync` | `addEventListener()` | Adds an event listener for the `subscriptionChange` event. | - `event` ("subscriptionChange")<br>- `listener` ((change: SubscriptionChangeEvent) => void) |
157
+ | `sync` | `removeEventListener()` | Removes an event listener for the `subscriptionChange` event. | - `event` ("subscriptionChange")<br>- `listener` ((change: SubscriptionChangeEvent) => void) |
158
+
159
+ ### Debug Namespace
160
+
161
+ Example:
162
+ ```js
163
+ OneSignal.Debug.setLogLevel(β€œtrace”);
164
+ ```
165
+
166
+ | Function Name | Description | Argument List |
167
+ | --------------- | ---------------------------------------------- | ------------------------------------ |
168
+ | `setLogLevel` | Turns on logging with the given log level. | `setLogLevel: string`<br>- `"trace"`<br>- `"debug"`<br>- `"info"`<br>- `"warn"`<br>- `"error"` |
169
+
170
+ # Limitations
171
+
172
+ ## January 2023
173
+ ### Version 16 (alpha)
174
+ It is recommended this version is used **only** in development and staging envrionments.
175
+ * Switching between users via `login()` and `logout()` is unsafe. **Please stick to single user testing.**
176
+ * Any User namespace calls must be invoked **after** initialization (async). Example: `OneSignal.User.addTag("tag", "2");`
177
+ * Aliases will be available in a future release,
178
+ * HTTP environments are not supported.
179
+ * AMP environments are not supported.
180
+ * Identity verification is not functional.
181
+ * Outcomes are not functional.
182
+
183
+
184
+ # Glossary
185
+
186
+ **OneSignal user**
187
+
188
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*(noun) lowercase*
189
+
190
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;A user of the OneSignal service.
191
+
192
+ **user**
193
+
194
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*(noun) lowercase*
195
+
196
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;An end-user of an application using the OneSignal service. They may or may not have a subscription.
197
+
198
+ **user ID**
199
+
200
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*(noun) lowercase*
201
+
202
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;A OneSignal-provisioned unique identifier for Users (User.onesignal_id).
203
+
204
+
205
+ **user external ID**
206
+
207
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*(noun) lowercase*
208
+
209
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;A customer-provisioned unique identifier for Users (User.external_id).
210
+
211
+
212
+ **user alias**
213
+
214
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*(noun) lowercase*
215
+
216
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;A customer provisioned key-value pair used to uniquely identify a User.
217
+
218
+
219
+ **subscription**
220
+
221
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*(noun) lowercase*
222
+
223
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;An established communication channel between an App and its User, such as a push-subscribed device, email address, or SMS-subscribed phone number.
224
+
225
+
226
+ **subscription ID**
227
+
228
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*(noun) lowercase*
229
+
230
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;A OneSignal-provisioned unique identifier for a single subscription.
231
+
232
+
233
+ **notification**
234
+
235
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*(noun) lowercase*
236
+
237
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;A unidirectional outbound communication message from an App to one or more Users via their Subscriptions.
238
+
239
+
240
+ **notification ID**
241
+
242
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*(noun) lowercase*
243
+
244
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;A OneSignal-provisioned unique identifier for Notifications (Notification.id).
245
+
246
+
247
+ **notification external ID**
248
+
249
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*(noun) lowercase*
250
+
251
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;A customer-provisioned unique identifier for Notifications (Notification.external_id).
package/README.md CHANGED
@@ -13,6 +13,9 @@ You can find more information on OneSignal [here](https://onesignal.com/).
13
13
  * 🏠 [Homepage](https://onesignal.com)
14
14
  * πŸ–€ [npm](https://www.npmjs.com/package/@onesignal/onesignal-vue3)
15
15
 
16
+ > 🚧 Version 2 now in Beta!
17
+ See our [migration guide](./MigrationGuide.md) to get started.
18
+
16
19
  ## Contents
17
20
  - [Install](#install)
18
21
  - [Usage](#usage)