README.md
1# libnpmhook [![npm version](https://img.shields.io/npm/v/libnpmhook.svg)](https://npm.im/libnpmhook) [![license](https://img.shields.io/npm/l/libnpmhook.svg)](https://npm.im/libnpmhook) [![Travis](https://img.shields.io/travis/npm/libnpmhook.svg)](https://travis-ci.org/npm/libnpmhook) [![AppVeyor](https://ci.appveyor.com/api/projects/status/github/zkat/libnpmhook?svg=true)](https://ci.appveyor.com/project/zkat/libnpmhook) [![Coverage Status](https://coveralls.io/repos/github/npm/libnpmhook/badge.svg?branch=latest)](https://coveralls.io/github/npm/libnpmhook?branch=latest)
2
3[`libnpmhook`](https://github.com/npm/libnpmhook) is a Node.js library for
4programmatically managing the npm registry's server-side hooks.
5
6For a more general introduction to managing hooks, see [the introductory blog
7post](https://blog.npmjs.org/post/145260155635/introducing-hooks-get-notifications-of-npm).
8
9## Example
10
11```js
12const hooks = require('libnpmhook')
13
14console.log(await hooks.ls('mypkg', {token: 'deadbeef'}))
15// array of hook objects on `mypkg`.
16```
17
18## Install
19
20`$ npm install libnpmhook`
21
22## Table of Contents
23
24* [Example](#example)
25* [Install](#install)
26* [API](#api)
27 * [hook opts](#opts)
28 * [`add()`](#add)
29 * [`rm()`](#rm)
30 * [`ls()`](#ls)
31 * [`ls.stream()`](#ls-stream)
32 * [`update()`](#update)
33
34### API
35
36#### <a name="opts"></a> `opts` for `libnpmhook` commands
37
38`libnpmhook` uses [`npm-registry-fetch`](https://npm.im/npm-registry-fetch).
39All options are passed through directly to that library, so please refer to [its
40own `opts`
41documentation](https://www.npmjs.com/package/npm-registry-fetch#fetch-options)
42for options that can be passed in.
43
44A couple of options of note for those in a hurry:
45
46* `opts.token` - can be passed in and will be used as the authentication token for the registry. For other ways to pass in auth details, see the n-r-f docs.
47* `opts.otp` - certain operations will require an OTP token to be passed in. If a `libnpmhook` command fails with `err.code === EOTP`, please retry the request with `{otp: <2fa token>}`
48* `opts.Promise` - If you pass this in, the Promises returned by `libnpmhook` commands will use this Promise class instead. For example: `{Promise: require('bluebird')}`
49
50#### <a name="add"></a> `> hooks.add(name, endpoint, secret, [opts]) -> Promise`
51
52`name` is the name of the package, org, or user/org scope to watch. The type is
53determined by the name syntax: `'@foo/bar'` and `'foo'` are treated as packages,
54`@foo` is treated as a scope, and `~user` is treated as an org name or scope.
55Each type will attach to different events.
56
57The `endpoint` should be a fully-qualified http URL for the endpoint the hook
58will send its payload to when it fires. `secret` is a shared secret that the
59hook will send to that endpoint to verify that it's actually coming from the
60registry hook.
61
62The returned Promise resolves to the full hook object that was created,
63including its generated `id`.
64
65See also: [`POST
66/v1/hooks/hook`](https://github.com/npm/registry/blob/master/docs/hooks/endpoints.md#post-v1hookshook)
67
68##### Example
69
70```javascript
71await hooks.add('~zkat', 'https://zkat.tech/api/added', 'supersekrit', {
72 token: 'myregistrytoken',
73 otp: '694207'
74})
75
76=>
77
78{ id: '16f7xoal',
79 username: 'zkat',
80 name: 'zkat',
81 endpoint: 'https://zkat.tech/api/added',
82 secret: 'supersekrit',
83 type: 'owner',
84 created: '2018-08-21T20:05:25.125Z',
85 updated: '2018-08-21T20:05:25.125Z',
86 deleted: false,
87 delivered: false,
88 last_delivery: null,
89 response_code: 0,
90 status: 'active' }
91```
92
93#### <a name="find"></a> `> hooks.find(id, [opts]) -> Promise`
94
95Returns the hook identified by `id`.
96
97The returned Promise resolves to the full hook object that was found, or error
98with `err.code` of `'E404'` if it didn't exist.
99
100See also: [`GET
101/v1/hooks/hook/:id`](https://github.com/npm/registry/blob/master/docs/hooks/endpoints.md#get-v1hookshookid)
102
103##### Example
104
105```javascript
106await hooks.find('16f7xoal', {token: 'myregistrytoken'})
107
108=>
109
110{ id: '16f7xoal',
111 username: 'zkat',
112 name: 'zkat',
113 endpoint: 'https://zkat.tech/api/added',
114 secret: 'supersekrit',
115 type: 'owner',
116 created: '2018-08-21T20:05:25.125Z',
117 updated: '2018-08-21T20:05:25.125Z',
118 deleted: false,
119 delivered: false,
120 last_delivery: null,
121 response_code: 0,
122 status: 'active' }
123```
124
125#### <a name="rm"></a> `> hooks.rm(id, [opts]) -> Promise`
126
127Removes the hook identified by `id`.
128
129The returned Promise resolves to the full hook object that was removed, if it
130existed, or `null` if no such hook was there (instead of erroring).
131
132See also: [`DELETE
133/v1/hooks/hook/:id`](https://github.com/npm/registry/blob/master/docs/hooks/endpoints.md#delete-v1hookshookid)
134
135##### Example
136
137```javascript
138await hooks.rm('16f7xoal', {
139 token: 'myregistrytoken',
140 otp: '694207'
141})
142
143=>
144
145{ id: '16f7xoal',
146 username: 'zkat',
147 name: 'zkat',
148 endpoint: 'https://zkat.tech/api/added',
149 secret: 'supersekrit',
150 type: 'owner',
151 created: '2018-08-21T20:05:25.125Z',
152 updated: '2018-08-21T20:05:25.125Z',
153 deleted: true,
154 delivered: false,
155 last_delivery: null,
156 response_code: 0,
157 status: 'active' }
158
159// Repeat it...
160await hooks.rm('16f7xoal', {
161 token: 'myregistrytoken',
162 otp: '694207'
163})
164
165=> null
166```
167
168#### <a name="update"></a> `> hooks.update(id, endpoint, secret, [opts]) -> Promise`
169
170The `id` should be a hook ID from a previously-created hook.
171
172The `endpoint` should be a fully-qualified http URL for the endpoint the hook
173will send its payload to when it fires. `secret` is a shared secret that the
174hook will send to that endpoint to verify that it's actually coming from the
175registry hook.
176
177The returned Promise resolves to the full hook object that was updated, if it
178existed. Otherwise, it will error with an `'E404'` error code.
179
180See also: [`PUT
181/v1/hooks/hook/:id`](https://github.com/npm/registry/blob/master/docs/hooks/endpoints.md#put-v1hookshookid)
182
183##### Example
184
185```javascript
186await hooks.update('16fxoal', 'https://zkat.tech/api/other', 'newsekrit', {
187 token: 'myregistrytoken',
188 otp: '694207'
189})
190
191=>
192
193{ id: '16f7xoal',
194 username: 'zkat',
195 name: 'zkat',
196 endpoint: 'https://zkat.tech/api/other',
197 secret: 'newsekrit',
198 type: 'owner',
199 created: '2018-08-21T20:05:25.125Z',
200 updated: '2018-08-21T20:14:41.964Z',
201 deleted: false,
202 delivered: false,
203 last_delivery: null,
204 response_code: 0,
205 status: 'active' }
206```
207
208#### <a name="ls"></a> `> hooks.ls([opts]) -> Promise`
209
210Resolves to an array of hook objects associated with the account you're
211authenticated as.
212
213Results can be further filtered with three values that can be passed in through
214`opts`:
215
216* `opts.package` - filter results by package name
217* `opts.limit` - maximum number of hooks to return
218* `opts.offset` - pagination offset for results (use with `opts.limit`)
219
220See also:
221 * [`hooks.ls.stream()`](#ls-stream)
222 * [`GET
223/v1/hooks`](https://github.com/npm/registry/blob/master/docs/hooks/endpoints.md#get-v1hooks)
224
225##### Example
226
227```javascript
228await hooks.ls({token: 'myregistrytoken'})
229
230=>
231[
232 { id: '16f7xoal', ... },
233 { id: 'wnyf98a1', ... },
234 ...
235]
236```
237
238#### <a name="ls-stream"></a> `> hooks.ls.stream([opts]) -> Stream`
239
240Returns a stream of hook objects associated with the account you're
241authenticated as. The returned stream is a valid `Symbol.asyncIterator` on
242`node@>=10`.
243
244Results can be further filtered with three values that can be passed in through
245`opts`:
246
247* `opts.package` - filter results by package name
248* `opts.limit` - maximum number of hooks to return
249* `opts.offset` - pagination offset for results (use with `opts.limit`)
250
251See also:
252 * [`hooks.ls()`](#ls)
253 * [`GET
254/v1/hooks`](https://github.com/npm/registry/blob/master/docs/hooks/endpoints.md#get-v1hooks)
255
256##### Example
257
258```javascript
259for await (let hook of hooks.ls.stream({token: 'myregistrytoken'})) {
260 console.log('found hook:', hook.id)
261}
262
263=>
264// outputs:
265// found hook: 16f7xoal
266// found hook: wnyf98a1
267```
268