u-wave-announce 0.3.0 → 0.5.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/LICENSE +22 -0
- package/README.md +8 -26
- package/package.json +15 -18
- package/src/plugin.js +174 -81
- package/src/signatures.js +23 -17
- package/test.js +7 -0
- package/dist/u-wave-announce.cjs.js +0 -155
- package/dist/u-wave-announce.cjs.js.map +0 -1
- package/dist/u-wave-announce.es.js +0 -151
- package/dist/u-wave-announce.es.js.map +0 -1
- package/rollup.config.js +0 -26
package/LICENSE
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2016 Renée Kooi
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
22
|
+
|
package/README.md
CHANGED
|
@@ -5,28 +5,22 @@ server listing at https://hub.u-wave.net.
|
|
|
5
5
|
|
|
6
6
|
## Usage
|
|
7
7
|
|
|
8
|
+
The announce plugin is added by default if you use the executable provided
|
|
9
|
+
with üWave Core. If you are using the üWave Core Node.js API, install and
|
|
10
|
+
add it manually:
|
|
11
|
+
|
|
8
12
|
```bash
|
|
9
|
-
npm install
|
|
13
|
+
npm install u-wave-announce
|
|
10
14
|
```
|
|
11
15
|
|
|
12
16
|
```js
|
|
13
17
|
import announce from 'u-wave-announce';
|
|
14
18
|
|
|
15
|
-
uw.use(announce
|
|
16
|
-
name: 'Your server name',
|
|
17
|
-
subtitle: 'Very short description', // Up to about 30 characters.
|
|
18
|
-
description: `
|
|
19
|
-
Longer description about your server, perhaps with a list of rules.
|
|
20
|
-
May include _markdown_, even!
|
|
21
|
-
`,
|
|
22
|
-
url: 'https://my-uwave-server.com',
|
|
23
|
-
}));
|
|
19
|
+
uw.use(announce);
|
|
24
20
|
```
|
|
25
21
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
## Options
|
|
22
|
+
## Runtime Options
|
|
23
|
+
This plugin can be configured at runtime using the admin panel.
|
|
30
24
|
|
|
31
25
|
### `name` (required)
|
|
32
26
|
|
|
@@ -51,18 +45,6 @@ including images and links. This can be a good place to put rules, links to
|
|
|
51
45
|
social media accounts associated with your server, and whatever else you want
|
|
52
46
|
visitors to know.
|
|
53
47
|
|
|
54
|
-
Indentation is stripped from this option, so template strings can be used like:
|
|
55
|
-
|
|
56
|
-
```js
|
|
57
|
-
announce({
|
|
58
|
-
description: `
|
|
59
|
-
# My Awesome Server
|
|
60
|
-
|
|
61
|
-
We play cool stuff, and cool stuff only!
|
|
62
|
-
`
|
|
63
|
-
});
|
|
64
|
-
```
|
|
65
|
-
|
|
66
48
|
### `hub`
|
|
67
49
|
|
|
68
50
|
The announce server to announce to. Uses https://announce.u-wave.net, the server
|
package/package.json
CHANGED
|
@@ -1,27 +1,24 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "u-wave-announce",
|
|
3
|
-
"version": "0.3.0",
|
|
4
3
|
"description": "Announce your üWave server's existence to the world.",
|
|
5
|
-
"
|
|
6
|
-
"
|
|
7
|
-
"engines": {
|
|
8
|
-
"node": ">= 8.9"
|
|
9
|
-
},
|
|
4
|
+
"version": "0.5.2",
|
|
5
|
+
"author": "Renée Kooi <renee@kooi.me>",
|
|
10
6
|
"dependencies": {
|
|
11
|
-
"debug": "^4.
|
|
12
|
-
"
|
|
13
|
-
"
|
|
14
|
-
"
|
|
15
|
-
|
|
16
|
-
|
|
7
|
+
"debug": "^4.3.2",
|
|
8
|
+
"libsodium-wrappers": "^0.7.8",
|
|
9
|
+
"node-fetch": "^2.6.0",
|
|
10
|
+
"strip-indent": "^3.0.0"
|
|
11
|
+
},
|
|
12
|
+
"engines": {
|
|
13
|
+
"node": ">= 12"
|
|
17
14
|
},
|
|
18
|
-
"
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
"
|
|
22
|
-
"
|
|
15
|
+
"license": "MIT",
|
|
16
|
+
"main": "./src/plugin.js",
|
|
17
|
+
"repository": {
|
|
18
|
+
"git": "https://github.com/u-wave/hub.git",
|
|
19
|
+
"directory": "plugin"
|
|
23
20
|
},
|
|
24
21
|
"scripts": {
|
|
25
|
-
"
|
|
22
|
+
"test": "node test"
|
|
26
23
|
}
|
|
27
24
|
}
|
package/src/plugin.js
CHANGED
|
@@ -1,63 +1,118 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
1
|
+
const { promisify } = require('util');
|
|
2
|
+
const randomBytes = promisify(require('crypto').randomBytes);
|
|
3
|
+
const fetch = require('node-fetch');
|
|
4
|
+
const stripIndent = require('strip-indent');
|
|
5
|
+
const debug = require('debug')('uwave:announce');
|
|
6
|
+
const sodium = require('./signatures');
|
|
7
|
+
const pkg = require('../package.json');
|
|
8
|
+
|
|
9
|
+
const optionsSchema = {
|
|
10
|
+
type: 'object',
|
|
11
|
+
title: 'Announce',
|
|
12
|
+
description: 'Options for publically announcing this server. Announcing allows users to find this server on "Hubs", such as https://hub.u-wave.net.',
|
|
13
|
+
'uw:key': 'u-wave:announce',
|
|
14
|
+
properties: {
|
|
15
|
+
enabled: {
|
|
16
|
+
type: 'boolean',
|
|
17
|
+
title: 'Enabled',
|
|
18
|
+
description: 'Whether to announce at all.',
|
|
19
|
+
default: false,
|
|
20
|
+
},
|
|
21
|
+
seed: {
|
|
22
|
+
type: 'string',
|
|
23
|
+
pattern: /^[0-9a-f]{64}$/.source,
|
|
24
|
+
// Should not be edited by users.
|
|
25
|
+
readOnly: true,
|
|
26
|
+
// Should not be exposed to users.
|
|
27
|
+
writeOnly: true,
|
|
28
|
+
},
|
|
29
|
+
name: {
|
|
30
|
+
type: 'string',
|
|
31
|
+
title: 'Server Name',
|
|
32
|
+
},
|
|
33
|
+
subtitle: {
|
|
34
|
+
type: 'string',
|
|
35
|
+
title: 'Tagline',
|
|
36
|
+
description: 'A short description of the server\'s purpose, up to about 30 characters.',
|
|
37
|
+
examples: [
|
|
38
|
+
'EDM and more!',
|
|
39
|
+
'International K-Pop Community',
|
|
40
|
+
],
|
|
41
|
+
},
|
|
42
|
+
description: {
|
|
43
|
+
type: 'string',
|
|
44
|
+
'uw:control': 'textarea',
|
|
45
|
+
title: 'Description',
|
|
46
|
+
description: 'A long-form description of the server. '
|
|
47
|
+
+ 'The description can contain Markdown, including images and links. '
|
|
48
|
+
+ 'This can be a good place to put rules, links to social media accounts associated '
|
|
49
|
+
+ 'with your server, and whatever else you want visitors to know.',
|
|
50
|
+
},
|
|
51
|
+
url: {
|
|
52
|
+
type: 'string',
|
|
53
|
+
format: 'uri',
|
|
54
|
+
title: 'URL',
|
|
55
|
+
description: 'A URL to your server. Ideally this should be hosting a web client of some form.',
|
|
56
|
+
},
|
|
57
|
+
socketUrl: {
|
|
58
|
+
type: 'string',
|
|
59
|
+
format: 'uri',
|
|
60
|
+
title: 'WebSocket URL',
|
|
61
|
+
description: 'A WebSocket endpoint URL for your server. This defaults to `url` with the ws:// or wss:// protocol, so this almost never has to be set.',
|
|
62
|
+
},
|
|
63
|
+
apiUrl: {
|
|
64
|
+
type: 'string',
|
|
65
|
+
format: 'uri',
|
|
66
|
+
title: 'API URL',
|
|
67
|
+
description: 'The base URL for the HTTP API your server. This defaults to `url` + /v1, so this almost never has to be set.',
|
|
68
|
+
},
|
|
69
|
+
hub: {
|
|
70
|
+
type: 'string',
|
|
71
|
+
format: 'uri',
|
|
72
|
+
title: 'Hub URL',
|
|
73
|
+
description: 'The announce server to announce to. Uses https://announce.u-wave.net, the server behind https://hub.u-wave.net, by default.',
|
|
74
|
+
default: 'https://announce.u-wave.net',
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
// At least one of these must match. So, if `enabled` is _not_ false, the properties are required.
|
|
78
|
+
anyOf: [{
|
|
79
|
+
properties: {
|
|
80
|
+
enabled: { const: false },
|
|
81
|
+
},
|
|
82
|
+
}, {
|
|
83
|
+
required: ['name', 'subtitle', 'url'],
|
|
84
|
+
}],
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
function stripSlashes(url) {
|
|
88
|
+
return url.replace(/\/+$/, '');
|
|
14
89
|
}
|
|
15
90
|
|
|
16
|
-
function
|
|
17
|
-
const
|
|
18
|
-
name: pkgName,
|
|
19
|
-
create: true,
|
|
20
|
-
thunk: true
|
|
21
|
-
})('keypair.json')
|
|
22
|
-
try {
|
|
23
|
-
const { publicKey, secretKey } = JSON.parse(
|
|
24
|
-
fs.readFileSync(keyPairPath, 'utf8')
|
|
25
|
-
)
|
|
26
|
-
return {
|
|
27
|
-
publicKey: Buffer.from(publicKey, 'base64'),
|
|
28
|
-
secretKey: Buffer.from(secretKey, 'base64')
|
|
29
|
-
}
|
|
30
|
-
} catch (err) {
|
|
31
|
-
const { publicKey, secretKey } = sodium.keyPair(seed)
|
|
32
|
-
fs.writeFileSync(keyPairPath, JSON.stringify({
|
|
33
|
-
publicKey: publicKey.toString('base64'),
|
|
34
|
-
secretKey: secretKey.toString('base64')
|
|
35
|
-
}, null, 2), 'utf8')
|
|
36
|
-
return { publicKey, secretKey }
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
async function getAnnounceData (uw, options) {
|
|
41
|
-
const url = stripSlashes(options.url)
|
|
91
|
+
async function getAnnounceData(uw, options) {
|
|
92
|
+
const url = stripSlashes(options.url);
|
|
42
93
|
|
|
43
94
|
// TODO add something to üWave Core so we don't have to manually populate
|
|
44
95
|
// the relationships.
|
|
45
|
-
const entry = await uw.booth.getCurrentEntry()
|
|
96
|
+
const entry = await uw.booth.getCurrentEntry();
|
|
46
97
|
if (entry) {
|
|
47
|
-
entry.
|
|
48
|
-
|
|
98
|
+
if (entry.execPopulate) {
|
|
99
|
+
entry.populate('user media.media');
|
|
100
|
+
await entry.execPopulate();
|
|
101
|
+
} else {
|
|
102
|
+
await entry.populate('user media.media');
|
|
103
|
+
}
|
|
49
104
|
}
|
|
50
105
|
|
|
51
106
|
// TODO add something to üWave Core so we don't have to manually ask Redis for
|
|
52
107
|
// this information. Currently üWave Core may register duplicates in this
|
|
53
108
|
// list, too, which is a bit annoying!
|
|
54
109
|
// TODO add guest users here too.
|
|
55
|
-
const onlineUserIDs = await uw.redis.lrange('users', 0, -1)
|
|
56
|
-
const onlineUsersMap = {}
|
|
110
|
+
const onlineUserIDs = await uw.redis.lrange('users', 0, -1);
|
|
111
|
+
const onlineUsersMap = {};
|
|
57
112
|
onlineUserIDs.forEach((id) => {
|
|
58
|
-
onlineUsersMap[id] = true
|
|
59
|
-
})
|
|
60
|
-
const usersCount = Object.keys(onlineUsersMap).length
|
|
113
|
+
onlineUsersMap[id] = true;
|
|
114
|
+
});
|
|
115
|
+
const usersCount = Object.keys(onlineUsersMap).length;
|
|
61
116
|
|
|
62
117
|
return {
|
|
63
118
|
name: options.name,
|
|
@@ -68,11 +123,11 @@ async function getAnnounceData (uw, options) {
|
|
|
68
123
|
media: {
|
|
69
124
|
artist: entry.media.artist,
|
|
70
125
|
title: entry.media.title,
|
|
71
|
-
thumbnail: entry.media.media.thumbnail
|
|
126
|
+
thumbnail: entry.media.media.thumbnail,
|
|
72
127
|
},
|
|
73
128
|
dj: entry.user ? {
|
|
74
|
-
username: entry.user.username
|
|
75
|
-
} : null
|
|
129
|
+
username: entry.user.username,
|
|
130
|
+
} : null,
|
|
76
131
|
} : null,
|
|
77
132
|
|
|
78
133
|
usersCount,
|
|
@@ -80,47 +135,85 @@ async function getAnnounceData (uw, options) {
|
|
|
80
135
|
url,
|
|
81
136
|
// Derive URLs if not given.
|
|
82
137
|
apiUrl: options.apiUrl || `${url}/v1`,
|
|
83
|
-
socketUrl: options.socketUrl || `${url.replace(/^http/, 'ws')}
|
|
84
|
-
}
|
|
138
|
+
socketUrl: options.socketUrl || `${url.replace(/^http/, 'ws')}/`,
|
|
139
|
+
};
|
|
85
140
|
}
|
|
86
141
|
|
|
87
|
-
|
|
88
|
-
const
|
|
89
|
-
|
|
142
|
+
async function getOrGenerateSeed(uw) {
|
|
143
|
+
const options = await uw.config.get(optionsSchema['uw:key']);
|
|
144
|
+
if (!options.seed) {
|
|
145
|
+
options.seed = (await randomBytes(32)).toString('hex');
|
|
146
|
+
await uw.config.set(optionsSchema['uw:key'], options);
|
|
147
|
+
}
|
|
148
|
+
return Buffer.from(options.seed, 'hex');
|
|
149
|
+
}
|
|
90
150
|
|
|
91
|
-
|
|
151
|
+
async function announcePlugin(uw, staticOptions) {
|
|
152
|
+
uw.config.register(optionsSchema['uw:key'], optionsSchema);
|
|
92
153
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
154
|
+
const seed = staticOptions.seed || await getOrGenerateSeed(uw);
|
|
155
|
+
// This takes up to a few 100 ms but it is a one-time startup cost…
|
|
156
|
+
// Maybe it makes sense to cache this, or to not block the rest of
|
|
157
|
+
// the startup work. For now, we just do the easy thing.
|
|
158
|
+
const { publicKey, secretKey } = await sodium.keyPair(seed);
|
|
98
159
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
160
|
+
async function announce() {
|
|
161
|
+
const options = await uw.config.get(optionsSchema['uw:key']);
|
|
162
|
+
if (typeof options !== 'object') {
|
|
163
|
+
debug('announcing not configured, skipping');
|
|
164
|
+
return;
|
|
103
165
|
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
166
|
+
if (!options.enabled) {
|
|
167
|
+
debug('announcing disabled, skipping');
|
|
168
|
+
return;
|
|
107
169
|
}
|
|
108
170
|
|
|
109
|
-
|
|
110
|
-
announce().
|
|
171
|
+
const hubHost = options.hub || 'https://announce.u-wave.net';
|
|
172
|
+
const announceUrl = `${stripSlashes(hubHost)}/announce/${Buffer.from(publicKey).toString('hex')}`;
|
|
173
|
+
debug('announcing to', announceUrl);
|
|
111
174
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
175
|
+
const announcement = await getAnnounceData(uw, options);
|
|
176
|
+
const data = JSON.stringify(announcement);
|
|
177
|
+
const signature = await sodium.sign(Buffer.from(data, 'utf8'), secretKey);
|
|
178
|
+
|
|
179
|
+
await fetch(announceUrl, {
|
|
180
|
+
method: 'post',
|
|
181
|
+
headers: {
|
|
182
|
+
'user-agent': `u-wave-announce ${pkg.version}`,
|
|
183
|
+
'content-type': 'application/json',
|
|
184
|
+
},
|
|
185
|
+
body: JSON.stringify({
|
|
186
|
+
data,
|
|
187
|
+
signature: Buffer.from(signature).toString('hex'),
|
|
188
|
+
}),
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function onError(err) {
|
|
193
|
+
debug(err);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
let interval;
|
|
197
|
+
|
|
198
|
+
uw.ready().then(() => {
|
|
199
|
+
// Announce that we've started up and are now alive.
|
|
200
|
+
announce().catch(onError);
|
|
116
201
|
|
|
117
202
|
// And announce periodically in the mean time to let the Hub server know
|
|
118
203
|
// we're still alive.
|
|
119
|
-
|
|
120
|
-
announce().catch(onError)
|
|
121
|
-
},
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
204
|
+
interval = setInterval(() => {
|
|
205
|
+
announce().catch(onError);
|
|
206
|
+
}, 60_000);
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
// Announce again every time the song changes.
|
|
210
|
+
uw.on('advance', () => {
|
|
211
|
+
announce().catch(onError);
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
uw.onClose(() => {
|
|
215
|
+
clearInterval(interval);
|
|
216
|
+
});
|
|
126
217
|
}
|
|
218
|
+
|
|
219
|
+
module.exports = announcePlugin;
|
package/src/signatures.js
CHANGED
|
@@ -1,25 +1,31 @@
|
|
|
1
|
-
// https://github.com/mafintosh/sodium-signatures/blob/master/index.js
|
|
2
|
-
|
|
1
|
+
// Based on https://github.com/mafintosh/sodium-signatures/blob/master/index.js
|
|
2
|
+
const sodium = require('libsodium-wrappers');
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
const secretKey = Buffer.alloc(sodium.crypto_sign_SECRETKEYBYTES)
|
|
4
|
+
async function keyPair(seed) {
|
|
5
|
+
await sodium.ready;
|
|
7
6
|
|
|
8
|
-
|
|
9
|
-
sodium.crypto_sign_seed_keypair(
|
|
10
|
-
|
|
11
|
-
sodium.crypto_sign_keypair(publicKey, secretKey)
|
|
12
|
-
}
|
|
7
|
+
const { publicKey, privateKey } = seed
|
|
8
|
+
? sodium.crypto_sign_seed_keypair(seed)
|
|
9
|
+
: sodium.crypto_sign_keypair();
|
|
13
10
|
|
|
14
|
-
|
|
11
|
+
// Rename for consistency with sodium-signatures
|
|
12
|
+
const secretKey = privateKey;
|
|
13
|
+
|
|
14
|
+
return { publicKey, secretKey };
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
sodium.crypto_sign_detached(
|
|
20
|
-
return signature
|
|
17
|
+
async function sign(message, secretKey) {
|
|
18
|
+
await sodium.ready;
|
|
19
|
+
return sodium.crypto_sign_detached(message, secretKey);
|
|
21
20
|
}
|
|
22
21
|
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
async function verify(message, signature, publicKey) {
|
|
23
|
+
await sodium.ready;
|
|
24
|
+
return sodium.crypto_sign_verify_detached(signature, message, publicKey);
|
|
25
25
|
}
|
|
26
|
+
|
|
27
|
+
module.exports = {
|
|
28
|
+
keyPair,
|
|
29
|
+
sign,
|
|
30
|
+
verify,
|
|
31
|
+
};
|
package/test.js
ADDED
|
@@ -1,155 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
|
|
4
|
-
|
|
5
|
-
var sodium = _interopDefault(require('sodium-javascript'));
|
|
6
|
-
var fs = _interopDefault(require('fs'));
|
|
7
|
-
var got = _interopDefault(require('got'));
|
|
8
|
-
var ms = _interopDefault(require('ms'));
|
|
9
|
-
var stripIndent = _interopDefault(require('strip-indent'));
|
|
10
|
-
var findCacheDir = _interopDefault(require('find-cache-dir'));
|
|
11
|
-
var createDebug = _interopDefault(require('debug'));
|
|
12
|
-
|
|
13
|
-
// https://github.com/mafintosh/sodium-signatures/blob/master/index.js
|
|
14
|
-
|
|
15
|
-
function keyPair (seed) {
|
|
16
|
-
const publicKey = Buffer.alloc(sodium.crypto_sign_PUBLICKEYBYTES);
|
|
17
|
-
const secretKey = Buffer.alloc(sodium.crypto_sign_SECRETKEYBYTES);
|
|
18
|
-
|
|
19
|
-
if (seed) {
|
|
20
|
-
sodium.crypto_sign_seed_keypair(publicKey, secretKey, seed);
|
|
21
|
-
} else {
|
|
22
|
-
sodium.crypto_sign_keypair(publicKey, secretKey);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
return { publicKey, secretKey }
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
function sign (message, secretKey) {
|
|
29
|
-
const signature = Buffer.alloc(sodium.crypto_sign_BYTES);
|
|
30
|
-
sodium.crypto_sign_detached(signature, message, secretKey);
|
|
31
|
-
return signature
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
var name = "u-wave-announce";
|
|
35
|
-
|
|
36
|
-
const debug = createDebug('uwave:announce');
|
|
37
|
-
|
|
38
|
-
function stripSlashes (url) {
|
|
39
|
-
return url.replace(/\/+$/, '')
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
function getKeyPair (seed) {
|
|
43
|
-
const keyPairPath = findCacheDir({
|
|
44
|
-
name: name,
|
|
45
|
-
create: true,
|
|
46
|
-
thunk: true
|
|
47
|
-
})('keypair.json');
|
|
48
|
-
try {
|
|
49
|
-
const { publicKey, secretKey } = JSON.parse(
|
|
50
|
-
fs.readFileSync(keyPairPath, 'utf8')
|
|
51
|
-
);
|
|
52
|
-
return {
|
|
53
|
-
publicKey: Buffer.from(publicKey, 'base64'),
|
|
54
|
-
secretKey: Buffer.from(secretKey, 'base64')
|
|
55
|
-
}
|
|
56
|
-
} catch (err) {
|
|
57
|
-
const { publicKey, secretKey } = keyPair(seed);
|
|
58
|
-
fs.writeFileSync(keyPairPath, JSON.stringify({
|
|
59
|
-
publicKey: publicKey.toString('base64'),
|
|
60
|
-
secretKey: secretKey.toString('base64')
|
|
61
|
-
}, null, 2), 'utf8');
|
|
62
|
-
return { publicKey, secretKey }
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
async function getAnnounceData (uw, options) {
|
|
67
|
-
const url = stripSlashes(options.url);
|
|
68
|
-
|
|
69
|
-
// TODO add something to üWave Core so we don't have to manually populate
|
|
70
|
-
// the relationships.
|
|
71
|
-
const entry = await uw.booth.getCurrentEntry();
|
|
72
|
-
if (entry) {
|
|
73
|
-
entry.populate('user media.media');
|
|
74
|
-
await entry.execPopulate();
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
// TODO add something to üWave Core so we don't have to manually ask Redis for
|
|
78
|
-
// this information. Currently üWave Core may register duplicates in this
|
|
79
|
-
// list, too, which is a bit annoying!
|
|
80
|
-
// TODO add guest users here too.
|
|
81
|
-
const onlineUserIDs = await uw.redis.lrange('users', 0, -1);
|
|
82
|
-
const onlineUsersMap = {};
|
|
83
|
-
onlineUserIDs.forEach((id) => {
|
|
84
|
-
onlineUsersMap[id] = true;
|
|
85
|
-
});
|
|
86
|
-
const usersCount = Object.keys(onlineUsersMap).length;
|
|
87
|
-
|
|
88
|
-
return {
|
|
89
|
-
name: options.name,
|
|
90
|
-
subtitle: options.subtitle,
|
|
91
|
-
description: options.description ? stripIndent(options.description) : null,
|
|
92
|
-
|
|
93
|
-
booth: entry ? {
|
|
94
|
-
media: {
|
|
95
|
-
artist: entry.media.artist,
|
|
96
|
-
title: entry.media.title,
|
|
97
|
-
thumbnail: entry.media.media.thumbnail
|
|
98
|
-
},
|
|
99
|
-
dj: entry.user ? {
|
|
100
|
-
username: entry.user.username
|
|
101
|
-
} : null
|
|
102
|
-
} : null,
|
|
103
|
-
|
|
104
|
-
usersCount,
|
|
105
|
-
|
|
106
|
-
url,
|
|
107
|
-
// Derive URLs if not given.
|
|
108
|
-
apiUrl: options.apiUrl || `${url}/v1`,
|
|
109
|
-
socketUrl: options.socketUrl || `${url.replace(/^http/, 'ws')}/`
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
function announcePlugin (options) {
|
|
114
|
-
const hubHost = options.hub || 'https://announce.u-wave.net';
|
|
115
|
-
const { publicKey, secretKey } = getKeyPair(options.seed);
|
|
116
|
-
|
|
117
|
-
const announceUrl = `${stripSlashes(hubHost)}/announce/${publicKey.toString('hex')}`;
|
|
118
|
-
|
|
119
|
-
return (uw) => {
|
|
120
|
-
async function announce () {
|
|
121
|
-
const announcement = await getAnnounceData(uw, options);
|
|
122
|
-
const data = JSON.stringify(announcement);
|
|
123
|
-
const signature = sign(Buffer.from(data, 'utf8'), secretKey).toString('hex');
|
|
124
|
-
|
|
125
|
-
await got.post(announceUrl, {
|
|
126
|
-
json: true,
|
|
127
|
-
body: { data, signature }
|
|
128
|
-
});
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
function onError (err) {
|
|
132
|
-
debug(err);
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
// Announce that we've started up and are now alive.
|
|
136
|
-
announce().catch(onError);
|
|
137
|
-
|
|
138
|
-
// Announce again every time the song changes.
|
|
139
|
-
uw.on('advance', () => {
|
|
140
|
-
announce().catch(onError);
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
// And announce periodically in the mean time to let the Hub server know
|
|
144
|
-
// we're still alive.
|
|
145
|
-
const interval = setInterval(() => {
|
|
146
|
-
announce().catch(onError);
|
|
147
|
-
}, ms('1 minute'));
|
|
148
|
-
uw.on('stop', () => {
|
|
149
|
-
clearInterval(interval);
|
|
150
|
-
});
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
module.exports = announcePlugin;
|
|
155
|
-
//# sourceMappingURL=u-wave-announce.cjs.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"u-wave-announce.cjs.js","sources":["../src/signatures.js","../src/plugin.js"],"sourcesContent":["// https://github.com/mafintosh/sodium-signatures/blob/master/index.js\nimport sodium from 'sodium-javascript'\n\nexport function keyPair (seed) {\n const publicKey = Buffer.alloc(sodium.crypto_sign_PUBLICKEYBYTES)\n const secretKey = Buffer.alloc(sodium.crypto_sign_SECRETKEYBYTES)\n\n if (seed) {\n sodium.crypto_sign_seed_keypair(publicKey, secretKey, seed)\n } else {\n sodium.crypto_sign_keypair(publicKey, secretKey)\n }\n\n return { publicKey, secretKey }\n}\n\nexport function sign (message, secretKey) {\n const signature = Buffer.alloc(sodium.crypto_sign_BYTES)\n sodium.crypto_sign_detached(signature, message, secretKey)\n return signature\n}\n\nexport function verify (message, signature, publicKey) {\n return sodium.crypto_sign_verify_detached(signature, message, publicKey)\n}\n","import fs from 'fs'\nimport got from 'got'\nimport ms from 'ms'\nimport stripIndent from 'strip-indent'\nimport findCacheDir from 'find-cache-dir'\nimport createDebug from 'debug'\nimport * as sodium from './signatures'\nimport { name as pkgName } from '../package.json'\n\nconst debug = createDebug('uwave:announce')\n\nfunction stripSlashes (url) {\n return url.replace(/\\/+$/, '')\n}\n\nfunction getKeyPair (seed) {\n const keyPairPath = findCacheDir({\n name: pkgName,\n create: true,\n thunk: true\n })('keypair.json')\n try {\n const { publicKey, secretKey } = JSON.parse(\n fs.readFileSync(keyPairPath, 'utf8')\n )\n return {\n publicKey: Buffer.from(publicKey, 'base64'),\n secretKey: Buffer.from(secretKey, 'base64')\n }\n } catch (err) {\n const { publicKey, secretKey } = sodium.keyPair(seed)\n fs.writeFileSync(keyPairPath, JSON.stringify({\n publicKey: publicKey.toString('base64'),\n secretKey: secretKey.toString('base64')\n }, null, 2), 'utf8')\n return { publicKey, secretKey }\n }\n}\n\nasync function getAnnounceData (uw, options) {\n const url = stripSlashes(options.url)\n\n // TODO add something to üWave Core so we don't have to manually populate\n // the relationships.\n const entry = await uw.booth.getCurrentEntry()\n if (entry) {\n entry.populate('user media.media')\n await entry.execPopulate()\n }\n\n // TODO add something to üWave Core so we don't have to manually ask Redis for\n // this information. Currently üWave Core may register duplicates in this\n // list, too, which is a bit annoying!\n // TODO add guest users here too.\n const onlineUserIDs = await uw.redis.lrange('users', 0, -1)\n const onlineUsersMap = {}\n onlineUserIDs.forEach((id) => {\n onlineUsersMap[id] = true\n })\n const usersCount = Object.keys(onlineUsersMap).length\n\n return {\n name: options.name,\n subtitle: options.subtitle,\n description: options.description ? stripIndent(options.description) : null,\n\n booth: entry ? {\n media: {\n artist: entry.media.artist,\n title: entry.media.title,\n thumbnail: entry.media.media.thumbnail\n },\n dj: entry.user ? {\n username: entry.user.username\n } : null\n } : null,\n\n usersCount,\n\n url,\n // Derive URLs if not given.\n apiUrl: options.apiUrl || `${url}/v1`,\n socketUrl: options.socketUrl || `${url.replace(/^http/, 'ws')}/`\n }\n}\n\nexport default function announcePlugin (options) {\n const hubHost = options.hub || 'https://announce.u-wave.net'\n const { publicKey, secretKey } = getKeyPair(options.seed)\n\n const announceUrl = `${stripSlashes(hubHost)}/announce/${publicKey.toString('hex')}`\n\n return (uw) => {\n async function announce () {\n const announcement = await getAnnounceData(uw, options)\n const data = JSON.stringify(announcement)\n const signature = sodium.sign(Buffer.from(data, 'utf8'), secretKey).toString('hex')\n\n await got.post(announceUrl, {\n json: true,\n body: { data, signature }\n })\n }\n\n function onError (err) {\n debug(err)\n }\n\n // Announce that we've started up and are now alive.\n announce().catch(onError)\n\n // Announce again every time the song changes.\n uw.on('advance', () => {\n announce().catch(onError)\n })\n\n // And announce periodically in the mean time to let the Hub server know\n // we're still alive.\n const interval = setInterval(() => {\n announce().catch(onError)\n }, ms('1 minute'))\n uw.on('stop', () => {\n clearInterval(interval)\n })\n }\n}\n"],"names":["pkgName","sodium.keyPair","sodium.sign"],"mappings":"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"}
|
|
@@ -1,151 +0,0 @@
|
|
|
1
|
-
import sodium from 'sodium-javascript';
|
|
2
|
-
import fs from 'fs';
|
|
3
|
-
import got from 'got';
|
|
4
|
-
import ms from 'ms';
|
|
5
|
-
import stripIndent from 'strip-indent';
|
|
6
|
-
import findCacheDir from 'find-cache-dir';
|
|
7
|
-
import createDebug from 'debug';
|
|
8
|
-
|
|
9
|
-
// https://github.com/mafintosh/sodium-signatures/blob/master/index.js
|
|
10
|
-
|
|
11
|
-
function keyPair (seed) {
|
|
12
|
-
const publicKey = Buffer.alloc(sodium.crypto_sign_PUBLICKEYBYTES);
|
|
13
|
-
const secretKey = Buffer.alloc(sodium.crypto_sign_SECRETKEYBYTES);
|
|
14
|
-
|
|
15
|
-
if (seed) {
|
|
16
|
-
sodium.crypto_sign_seed_keypair(publicKey, secretKey, seed);
|
|
17
|
-
} else {
|
|
18
|
-
sodium.crypto_sign_keypair(publicKey, secretKey);
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
return { publicKey, secretKey }
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
function sign (message, secretKey) {
|
|
25
|
-
const signature = Buffer.alloc(sodium.crypto_sign_BYTES);
|
|
26
|
-
sodium.crypto_sign_detached(signature, message, secretKey);
|
|
27
|
-
return signature
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
var name = "u-wave-announce";
|
|
31
|
-
|
|
32
|
-
const debug = createDebug('uwave:announce');
|
|
33
|
-
|
|
34
|
-
function stripSlashes (url) {
|
|
35
|
-
return url.replace(/\/+$/, '')
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
function getKeyPair (seed) {
|
|
39
|
-
const keyPairPath = findCacheDir({
|
|
40
|
-
name: name,
|
|
41
|
-
create: true,
|
|
42
|
-
thunk: true
|
|
43
|
-
})('keypair.json');
|
|
44
|
-
try {
|
|
45
|
-
const { publicKey, secretKey } = JSON.parse(
|
|
46
|
-
fs.readFileSync(keyPairPath, 'utf8')
|
|
47
|
-
);
|
|
48
|
-
return {
|
|
49
|
-
publicKey: Buffer.from(publicKey, 'base64'),
|
|
50
|
-
secretKey: Buffer.from(secretKey, 'base64')
|
|
51
|
-
}
|
|
52
|
-
} catch (err) {
|
|
53
|
-
const { publicKey, secretKey } = keyPair(seed);
|
|
54
|
-
fs.writeFileSync(keyPairPath, JSON.stringify({
|
|
55
|
-
publicKey: publicKey.toString('base64'),
|
|
56
|
-
secretKey: secretKey.toString('base64')
|
|
57
|
-
}, null, 2), 'utf8');
|
|
58
|
-
return { publicKey, secretKey }
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
async function getAnnounceData (uw, options) {
|
|
63
|
-
const url = stripSlashes(options.url);
|
|
64
|
-
|
|
65
|
-
// TODO add something to üWave Core so we don't have to manually populate
|
|
66
|
-
// the relationships.
|
|
67
|
-
const entry = await uw.booth.getCurrentEntry();
|
|
68
|
-
if (entry) {
|
|
69
|
-
entry.populate('user media.media');
|
|
70
|
-
await entry.execPopulate();
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
// TODO add something to üWave Core so we don't have to manually ask Redis for
|
|
74
|
-
// this information. Currently üWave Core may register duplicates in this
|
|
75
|
-
// list, too, which is a bit annoying!
|
|
76
|
-
// TODO add guest users here too.
|
|
77
|
-
const onlineUserIDs = await uw.redis.lrange('users', 0, -1);
|
|
78
|
-
const onlineUsersMap = {};
|
|
79
|
-
onlineUserIDs.forEach((id) => {
|
|
80
|
-
onlineUsersMap[id] = true;
|
|
81
|
-
});
|
|
82
|
-
const usersCount = Object.keys(onlineUsersMap).length;
|
|
83
|
-
|
|
84
|
-
return {
|
|
85
|
-
name: options.name,
|
|
86
|
-
subtitle: options.subtitle,
|
|
87
|
-
description: options.description ? stripIndent(options.description) : null,
|
|
88
|
-
|
|
89
|
-
booth: entry ? {
|
|
90
|
-
media: {
|
|
91
|
-
artist: entry.media.artist,
|
|
92
|
-
title: entry.media.title,
|
|
93
|
-
thumbnail: entry.media.media.thumbnail
|
|
94
|
-
},
|
|
95
|
-
dj: entry.user ? {
|
|
96
|
-
username: entry.user.username
|
|
97
|
-
} : null
|
|
98
|
-
} : null,
|
|
99
|
-
|
|
100
|
-
usersCount,
|
|
101
|
-
|
|
102
|
-
url,
|
|
103
|
-
// Derive URLs if not given.
|
|
104
|
-
apiUrl: options.apiUrl || `${url}/v1`,
|
|
105
|
-
socketUrl: options.socketUrl || `${url.replace(/^http/, 'ws')}/`
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
function announcePlugin (options) {
|
|
110
|
-
const hubHost = options.hub || 'https://announce.u-wave.net';
|
|
111
|
-
const { publicKey, secretKey } = getKeyPair(options.seed);
|
|
112
|
-
|
|
113
|
-
const announceUrl = `${stripSlashes(hubHost)}/announce/${publicKey.toString('hex')}`;
|
|
114
|
-
|
|
115
|
-
return (uw) => {
|
|
116
|
-
async function announce () {
|
|
117
|
-
const announcement = await getAnnounceData(uw, options);
|
|
118
|
-
const data = JSON.stringify(announcement);
|
|
119
|
-
const signature = sign(Buffer.from(data, 'utf8'), secretKey).toString('hex');
|
|
120
|
-
|
|
121
|
-
await got.post(announceUrl, {
|
|
122
|
-
json: true,
|
|
123
|
-
body: { data, signature }
|
|
124
|
-
});
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
function onError (err) {
|
|
128
|
-
debug(err);
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
// Announce that we've started up and are now alive.
|
|
132
|
-
announce().catch(onError);
|
|
133
|
-
|
|
134
|
-
// Announce again every time the song changes.
|
|
135
|
-
uw.on('advance', () => {
|
|
136
|
-
announce().catch(onError);
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
// And announce periodically in the mean time to let the Hub server know
|
|
140
|
-
// we're still alive.
|
|
141
|
-
const interval = setInterval(() => {
|
|
142
|
-
announce().catch(onError);
|
|
143
|
-
}, ms('1 minute'));
|
|
144
|
-
uw.on('stop', () => {
|
|
145
|
-
clearInterval(interval);
|
|
146
|
-
});
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
export default announcePlugin;
|
|
151
|
-
//# sourceMappingURL=u-wave-announce.es.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"u-wave-announce.es.js","sources":["../src/signatures.js","../src/plugin.js"],"sourcesContent":["// https://github.com/mafintosh/sodium-signatures/blob/master/index.js\nimport sodium from 'sodium-javascript'\n\nexport function keyPair (seed) {\n const publicKey = Buffer.alloc(sodium.crypto_sign_PUBLICKEYBYTES)\n const secretKey = Buffer.alloc(sodium.crypto_sign_SECRETKEYBYTES)\n\n if (seed) {\n sodium.crypto_sign_seed_keypair(publicKey, secretKey, seed)\n } else {\n sodium.crypto_sign_keypair(publicKey, secretKey)\n }\n\n return { publicKey, secretKey }\n}\n\nexport function sign (message, secretKey) {\n const signature = Buffer.alloc(sodium.crypto_sign_BYTES)\n sodium.crypto_sign_detached(signature, message, secretKey)\n return signature\n}\n\nexport function verify (message, signature, publicKey) {\n return sodium.crypto_sign_verify_detached(signature, message, publicKey)\n}\n","import fs from 'fs'\nimport got from 'got'\nimport ms from 'ms'\nimport stripIndent from 'strip-indent'\nimport findCacheDir from 'find-cache-dir'\nimport createDebug from 'debug'\nimport * as sodium from './signatures'\nimport { name as pkgName } from '../package.json'\n\nconst debug = createDebug('uwave:announce')\n\nfunction stripSlashes (url) {\n return url.replace(/\\/+$/, '')\n}\n\nfunction getKeyPair (seed) {\n const keyPairPath = findCacheDir({\n name: pkgName,\n create: true,\n thunk: true\n })('keypair.json')\n try {\n const { publicKey, secretKey } = JSON.parse(\n fs.readFileSync(keyPairPath, 'utf8')\n )\n return {\n publicKey: Buffer.from(publicKey, 'base64'),\n secretKey: Buffer.from(secretKey, 'base64')\n }\n } catch (err) {\n const { publicKey, secretKey } = sodium.keyPair(seed)\n fs.writeFileSync(keyPairPath, JSON.stringify({\n publicKey: publicKey.toString('base64'),\n secretKey: secretKey.toString('base64')\n }, null, 2), 'utf8')\n return { publicKey, secretKey }\n }\n}\n\nasync function getAnnounceData (uw, options) {\n const url = stripSlashes(options.url)\n\n // TODO add something to üWave Core so we don't have to manually populate\n // the relationships.\n const entry = await uw.booth.getCurrentEntry()\n if (entry) {\n entry.populate('user media.media')\n await entry.execPopulate()\n }\n\n // TODO add something to üWave Core so we don't have to manually ask Redis for\n // this information. Currently üWave Core may register duplicates in this\n // list, too, which is a bit annoying!\n // TODO add guest users here too.\n const onlineUserIDs = await uw.redis.lrange('users', 0, -1)\n const onlineUsersMap = {}\n onlineUserIDs.forEach((id) => {\n onlineUsersMap[id] = true\n })\n const usersCount = Object.keys(onlineUsersMap).length\n\n return {\n name: options.name,\n subtitle: options.subtitle,\n description: options.description ? stripIndent(options.description) : null,\n\n booth: entry ? {\n media: {\n artist: entry.media.artist,\n title: entry.media.title,\n thumbnail: entry.media.media.thumbnail\n },\n dj: entry.user ? {\n username: entry.user.username\n } : null\n } : null,\n\n usersCount,\n\n url,\n // Derive URLs if not given.\n apiUrl: options.apiUrl || `${url}/v1`,\n socketUrl: options.socketUrl || `${url.replace(/^http/, 'ws')}/`\n }\n}\n\nexport default function announcePlugin (options) {\n const hubHost = options.hub || 'https://announce.u-wave.net'\n const { publicKey, secretKey } = getKeyPair(options.seed)\n\n const announceUrl = `${stripSlashes(hubHost)}/announce/${publicKey.toString('hex')}`\n\n return (uw) => {\n async function announce () {\n const announcement = await getAnnounceData(uw, options)\n const data = JSON.stringify(announcement)\n const signature = sodium.sign(Buffer.from(data, 'utf8'), secretKey).toString('hex')\n\n await got.post(announceUrl, {\n json: true,\n body: { data, signature }\n })\n }\n\n function onError (err) {\n debug(err)\n }\n\n // Announce that we've started up and are now alive.\n announce().catch(onError)\n\n // Announce again every time the song changes.\n uw.on('advance', () => {\n announce().catch(onError)\n })\n\n // And announce periodically in the mean time to let the Hub server know\n // we're still alive.\n const interval = setInterval(() => {\n announce().catch(onError)\n }, ms('1 minute'))\n uw.on('stop', () => {\n clearInterval(interval)\n })\n }\n}\n"],"names":["pkgName","sodium.keyPair","sodium.sign"],"mappings":"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"}
|
package/rollup.config.js
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import json from 'rollup-plugin-json'
|
|
2
|
-
import nodeResolve from 'rollup-plugin-node-resolve'
|
|
3
|
-
import isBuiltinModule from 'is-builtin-module'
|
|
4
|
-
|
|
5
|
-
const pkg = require('./package.json')
|
|
6
|
-
|
|
7
|
-
const external = Object.keys(pkg.dependencies)
|
|
8
|
-
|
|
9
|
-
export default {
|
|
10
|
-
input: 'src/plugin.js',
|
|
11
|
-
output: [{
|
|
12
|
-
file: pkg.main,
|
|
13
|
-
exports: 'default',
|
|
14
|
-
format: 'cjs',
|
|
15
|
-
sourcemap: true
|
|
16
|
-
}, {
|
|
17
|
-
file: pkg.module,
|
|
18
|
-
format: 'es',
|
|
19
|
-
sourcemap: true
|
|
20
|
-
}],
|
|
21
|
-
external: id => isBuiltinModule(id) || external.some(m => id.split('/')[0] === m),
|
|
22
|
-
plugins: [
|
|
23
|
-
json(),
|
|
24
|
-
nodeResolve()
|
|
25
|
-
]
|
|
26
|
-
}
|