@bzenky/spoti 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +74 -2
- package/dist/app.d.ts +8 -0
- package/dist/app.js +206 -2
- package/dist/app.js.map +1 -1
- package/dist/auth/spotify-auth-client.d.ts +1 -0
- package/dist/auth/spotify-auth-client.js +4 -0
- package/dist/auth/spotify-auth-client.js.map +1 -1
- package/dist/cli.js +8 -1
- package/dist/cli.js.map +1 -1
- package/dist/services/auth.service.js +8 -1
- package/dist/services/auth.service.js.map +1 -1
- package/dist/services/device.service.d.ts +20 -0
- package/dist/services/device.service.js +61 -0
- package/dist/services/device.service.js.map +1 -0
- package/dist/services/player.service.d.ts +8 -1
- package/dist/services/player.service.js +61 -3
- package/dist/services/player.service.js.map +1 -1
- package/dist/services/queue.service.d.ts +18 -0
- package/dist/services/queue.service.js +43 -0
- package/dist/services/queue.service.js.map +1 -0
- package/dist/spotify/scopes.d.ts +2 -1
- package/dist/spotify/scopes.js +5 -1
- package/dist/spotify/scopes.js.map +1 -1
- package/dist/spotify/types.d.ts +2 -0
- package/dist/storage/config.d.ts +29 -0
- package/dist/storage/config.js +111 -0
- package/dist/storage/config.js.map +1 -0
- package/dist/storage/credentials.d.ts +1 -0
- package/dist/storage/credentials.js +1 -0
- package/dist/storage/credentials.js.map +1 -1
- package/dist/storage/index.d.ts +2 -0
- package/dist/storage/index.js +1 -0
- package/dist/storage/index.js.map +1 -1
- package/dist/ui/watch.d.ts +7 -0
- package/dist/ui/watch.js +44 -0
- package/dist/ui/watch.js.map +1 -0
- package/dist/version.d.ts +2 -0
- package/dist/version.js +11 -0
- package/dist/version.js.map +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -78,12 +78,43 @@ Control playback:
|
|
|
78
78
|
|
|
79
79
|
```bash
|
|
80
80
|
spoti now
|
|
81
|
+
spoti now --watch
|
|
81
82
|
spoti play "Numb"
|
|
82
83
|
spoti play "Numb" --first
|
|
84
|
+
spoti play "Numb" --watch
|
|
85
|
+
spoti play "Numb" --no-watch
|
|
83
86
|
spoti pause
|
|
84
87
|
spoti resume
|
|
85
88
|
spoti next
|
|
86
89
|
spoti previous
|
|
90
|
+
spoti volume 50
|
|
91
|
+
spoti volume +10
|
|
92
|
+
spoti volume -10
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Manage playback devices:
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
spoti devices
|
|
99
|
+
spoti device "My Computer"
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
When playback has no active device, `spoti play` and `spoti resume` automatically target it if exactly one controllable device is available.
|
|
103
|
+
|
|
104
|
+
Seek within the current track:
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
spoti seek 1:30
|
|
108
|
+
spoti seek +30
|
|
109
|
+
spoti seek -10
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
View the queue or search for a track to add:
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
spoti queue
|
|
116
|
+
spoti queue "Faint"
|
|
117
|
+
spoti queue "Faint" --first
|
|
87
118
|
```
|
|
88
119
|
|
|
89
120
|
Search without starting playback:
|
|
@@ -101,6 +132,34 @@ spoti logout
|
|
|
101
132
|
|
|
102
133
|
When attached to an interactive terminal, `spoti play <query>` asks you to select a result. In non-interactive usage it chooses the first result automatically; `--first` makes that behavior explicit.
|
|
103
134
|
|
|
135
|
+
Watch mode continuously refreshes the current track and progress until `Ctrl+C` is pressed. `--watch` enables it for one command, while `--no-watch` overrides a saved preference.
|
|
136
|
+
|
|
137
|
+
## Configuration
|
|
138
|
+
|
|
139
|
+
View all settings:
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
spoti config
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
Read, update, or reset settings:
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
spoti config get watchAfterPlay
|
|
149
|
+
spoti config set watchAfterPlay true
|
|
150
|
+
spoti config set refreshIntervalMs 2000
|
|
151
|
+
spoti config reset
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
Available settings:
|
|
155
|
+
|
|
156
|
+
| Setting | Default | Description |
|
|
157
|
+
| --- | ---: | --- |
|
|
158
|
+
| `watchAfterPlay` | `false` | Keep `spoti play` open in watch mode after playback starts. |
|
|
159
|
+
| `refreshIntervalMs` | `1000` | Watch refresh interval from `1000` to `30000` milliseconds. |
|
|
160
|
+
|
|
161
|
+
Command flags take precedence over saved configuration. Application preferences are stored in `$XDG_CONFIG_HOME/spoti/config.json`, or `~/.config/spoti/config.json` when `XDG_CONFIG_HOME` is not set.
|
|
162
|
+
|
|
104
163
|
## Credentials
|
|
105
164
|
|
|
106
165
|
Credentials are stored locally in:
|
|
@@ -115,7 +174,7 @@ or, when `XDG_CONFIG_HOME` is not set:
|
|
|
115
174
|
~/.config/spoti/credentials.json
|
|
116
175
|
```
|
|
117
176
|
|
|
118
|
-
The credentials file is created with user-only permissions (`0600`). Access tokens refresh automatically. Keep `SPOTIFY_CLIENT_ID` available in the environment because Spotify requires it during token refresh. Never provide or store a Spotify client secret in `spoti`.
|
|
177
|
+
The credentials file is created with user-only permissions (`0600`). Access tokens refresh automatically. Version `0.2.0` adds the `user-read-currently-playing` scope for queue access; credentials created by `0.1.0` require one new `spoti login` authorization. Keep `SPOTIFY_CLIENT_ID` available in the environment because Spotify requires it during token refresh. Never provide or store a Spotify client secret in `spoti`.
|
|
119
178
|
|
|
120
179
|
## Spotify API policy
|
|
121
180
|
|
|
@@ -132,6 +191,19 @@ npm test
|
|
|
132
191
|
npm run build
|
|
133
192
|
```
|
|
134
193
|
|
|
194
|
+
## Releases
|
|
195
|
+
|
|
196
|
+
GitHub Releases are created automatically when a version tag is pushed. The tag must match the version in `package.json`:
|
|
197
|
+
|
|
198
|
+
```bash
|
|
199
|
+
npm version patch
|
|
200
|
+
npm run verify
|
|
201
|
+
git push origin main
|
|
202
|
+
git push origin v0.1.1
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
The release workflow attaches the npm package tarball and a `SHA256SUMS` file, and generates release notes from the Git history. Publishing to npm remains a separate explicit step.
|
|
206
|
+
|
|
135
207
|
## Current scope
|
|
136
208
|
|
|
137
|
-
Version `0.
|
|
209
|
+
Version `0.2.0` supports authentication, track search and playback, current playback and watch mode, pause/resume, next/previous, persistent configuration, volume and seek controls, queue management, device listing and selection, and automatic single-device fallback. Shuffle, repeat, album/artist/playlist context playback, library features, JSON output, update notifications, and a TUI are planned for later releases.
|
package/dist/app.d.ts
CHANGED
|
@@ -1,14 +1,22 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
2
|
import type { AuthService } from './services/auth.service.js';
|
|
3
|
+
import type { DeviceService } from './services/device.service.js';
|
|
3
4
|
import type { PlayerService } from './services/player.service.js';
|
|
5
|
+
import type { QueueService } from './services/queue.service.js';
|
|
4
6
|
import type { SearchService } from './services/search.service.js';
|
|
7
|
+
import { type ConfigStore } from './storage/config.js';
|
|
5
8
|
import { type Output } from './ui/output.js';
|
|
6
9
|
import { selectTrack } from './ui/prompts.js';
|
|
10
|
+
import { type PlaybackWatcher } from './ui/watch.js';
|
|
7
11
|
export interface AppDependencies {
|
|
8
12
|
auth: AuthService;
|
|
9
13
|
player: PlayerService;
|
|
10
14
|
search: SearchService;
|
|
15
|
+
device: DeviceService;
|
|
16
|
+
queue: QueueService;
|
|
17
|
+
config: ConfigStore;
|
|
11
18
|
output: Output;
|
|
12
19
|
chooseTrack?: typeof selectTrack;
|
|
20
|
+
watchPlayback?: PlaybackWatcher;
|
|
13
21
|
}
|
|
14
22
|
export declare function createProgram(dependencies: AppDependencies): Command;
|
package/dist/app.js
CHANGED
|
@@ -1,13 +1,19 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
|
+
import { CONFIG_KEYS, parseConfigValue, } from './storage/config.js';
|
|
2
3
|
import { formatPlayback, formatTrack } from './ui/output.js';
|
|
3
4
|
import { selectTrack } from './ui/prompts.js';
|
|
5
|
+
import { watchPlayback } from './ui/watch.js';
|
|
6
|
+
import { ConfigurationError } from './utils/errors.js';
|
|
7
|
+
import { formatDuration } from './utils/time.js';
|
|
8
|
+
import { VERSION } from './version.js';
|
|
4
9
|
export function createProgram(dependencies) {
|
|
5
10
|
const program = new Command();
|
|
6
11
|
const chooseTrack = dependencies.chooseTrack ?? selectTrack;
|
|
12
|
+
const startWatching = dependencies.watchPlayback ?? watchPlayback;
|
|
7
13
|
program
|
|
8
14
|
.name('spoti')
|
|
9
15
|
.description('Control Spotify from your terminal')
|
|
10
|
-
.version(
|
|
16
|
+
.version(VERSION);
|
|
11
17
|
program
|
|
12
18
|
.command('login')
|
|
13
19
|
.description('Log in to Spotify')
|
|
@@ -34,10 +40,52 @@ export function createProgram(dependencies) {
|
|
|
34
40
|
const user = await dependencies.auth.getCurrentUser();
|
|
35
41
|
dependencies.output.log(`✓ Logged in as ${user.displayName}`);
|
|
36
42
|
});
|
|
43
|
+
const configCommand = program
|
|
44
|
+
.command('config')
|
|
45
|
+
.description('View and update spoti configuration')
|
|
46
|
+
.action(async () => {
|
|
47
|
+
dependencies.output.log(formatConfig(await dependencies.config.read()));
|
|
48
|
+
});
|
|
49
|
+
configCommand
|
|
50
|
+
.command('get')
|
|
51
|
+
.description('Read a configuration value')
|
|
52
|
+
.argument('<key>', 'configuration key')
|
|
53
|
+
.action(async (keyName) => {
|
|
54
|
+
const key = parseConfigKey(keyName);
|
|
55
|
+
const config = await dependencies.config.read();
|
|
56
|
+
dependencies.output.log(String(config[key]));
|
|
57
|
+
});
|
|
58
|
+
configCommand
|
|
59
|
+
.command('set')
|
|
60
|
+
.description('Set a configuration value')
|
|
61
|
+
.argument('<key>', 'configuration key')
|
|
62
|
+
.argument('<value>', 'new value')
|
|
63
|
+
.action(async (keyName, rawValue) => {
|
|
64
|
+
const key = parseConfigKey(keyName);
|
|
65
|
+
const value = parseConfigValue(key, rawValue);
|
|
66
|
+
await dependencies.config.set(key, value);
|
|
67
|
+
dependencies.output.log(`✓ ${key} = ${String(value)}`);
|
|
68
|
+
});
|
|
69
|
+
configCommand
|
|
70
|
+
.command('reset')
|
|
71
|
+
.description('Reset configuration to defaults')
|
|
72
|
+
.action(async () => {
|
|
73
|
+
await dependencies.config.reset();
|
|
74
|
+
dependencies.output.log('✓ Configuration reset');
|
|
75
|
+
});
|
|
37
76
|
program
|
|
38
77
|
.command('now')
|
|
39
78
|
.description('Show the current Spotify playback')
|
|
40
|
-
.
|
|
79
|
+
.option('-w, --watch', 'continuously refresh playback information')
|
|
80
|
+
.action(async (options) => {
|
|
81
|
+
if (options.watch) {
|
|
82
|
+
const config = await dependencies.config.read();
|
|
83
|
+
await startWatching({
|
|
84
|
+
player: dependencies.player,
|
|
85
|
+
refreshIntervalMs: config.refreshIntervalMs,
|
|
86
|
+
});
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
41
89
|
const playback = await dependencies.player.getCurrentPlayback();
|
|
42
90
|
dependencies.output.log(playback ? formatPlayback(playback) : 'Nothing is currently playing.');
|
|
43
91
|
});
|
|
@@ -71,6 +119,99 @@ export function createProgram(dependencies) {
|
|
|
71
119
|
await dependencies.player.previous();
|
|
72
120
|
dependencies.output.log('⏮ Returned to previous track');
|
|
73
121
|
});
|
|
122
|
+
program
|
|
123
|
+
.command('devices')
|
|
124
|
+
.description('List available Spotify Connect devices')
|
|
125
|
+
.action(async () => {
|
|
126
|
+
const devices = await dependencies.device.getDevices();
|
|
127
|
+
if (devices.length === 0) {
|
|
128
|
+
dependencies.output.log('No Spotify devices are available. Open Spotify on a device and try again.');
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
dependencies.output.log(devices
|
|
132
|
+
.map((device, index) => {
|
|
133
|
+
const state = device.isActive
|
|
134
|
+
? 'active'
|
|
135
|
+
: device.isRestricted
|
|
136
|
+
? 'restricted'
|
|
137
|
+
: 'available';
|
|
138
|
+
const volume = device.volumePercent === null ? '' : ` · ${device.volumePercent}%`;
|
|
139
|
+
return `${index + 1}. ${device.name} · ${device.type} · ${state}${volume}`;
|
|
140
|
+
})
|
|
141
|
+
.join('\n'));
|
|
142
|
+
});
|
|
143
|
+
program
|
|
144
|
+
.command('device')
|
|
145
|
+
.description('Transfer playback to a Spotify Connect device')
|
|
146
|
+
.argument('<name-or-id...>', 'exact device name or ID')
|
|
147
|
+
.action(async (nameOrIdParts) => {
|
|
148
|
+
const device = await dependencies.device.findDevice(nameOrIdParts.join(' '));
|
|
149
|
+
if (!device.id)
|
|
150
|
+
throw new ConfigurationError('The selected device has no usable ID.');
|
|
151
|
+
await dependencies.device.transferPlayback(device.id);
|
|
152
|
+
dependencies.output.log(`✓ Active device: ${device.name}`);
|
|
153
|
+
});
|
|
154
|
+
program
|
|
155
|
+
.command('seek')
|
|
156
|
+
.description('Seek within the current track')
|
|
157
|
+
.argument('<position>', 'absolute time such as 1:30, or a change such as +30 or -10')
|
|
158
|
+
.allowUnknownOption()
|
|
159
|
+
.action(async (rawPosition) => {
|
|
160
|
+
const input = parseSeekInput(rawPosition);
|
|
161
|
+
const position = input.relative
|
|
162
|
+
? await dependencies.player.changePosition(input.milliseconds)
|
|
163
|
+
: await dependencies.player.seek(input.milliseconds);
|
|
164
|
+
dependencies.output.log(`⏩ Position: ${formatDuration(position)}`);
|
|
165
|
+
});
|
|
166
|
+
program
|
|
167
|
+
.command('volume')
|
|
168
|
+
.description('Set or adjust the active device volume')
|
|
169
|
+
.argument('<value>', 'volume from 0-100, or a relative change such as +10 or -10')
|
|
170
|
+
.allowUnknownOption()
|
|
171
|
+
.action(async (rawValue) => {
|
|
172
|
+
const input = parseVolumeInput(rawValue);
|
|
173
|
+
const volume = input.relative
|
|
174
|
+
? await dependencies.player.changeVolume(input.value)
|
|
175
|
+
: await dependencies.player.setVolume(input.value);
|
|
176
|
+
dependencies.output.log(`${volume === 0 ? '🔇' : '🔊'} Volume: ${volume}%`);
|
|
177
|
+
});
|
|
178
|
+
program
|
|
179
|
+
.command('queue')
|
|
180
|
+
.description('Show the playback queue or add a searched track')
|
|
181
|
+
.argument('[query...]', 'track name to add')
|
|
182
|
+
.option('--first', 'queue the first search result without prompting')
|
|
183
|
+
.action(async (queryParts, options) => {
|
|
184
|
+
const query = queryParts.join(' ').trim();
|
|
185
|
+
if (!query) {
|
|
186
|
+
const playbackQueue = await dependencies.queue.getQueue();
|
|
187
|
+
const lines = playbackQueue.currentlyPlaying
|
|
188
|
+
? [
|
|
189
|
+
`Now: ${formatQueueItem(playbackQueue.currentlyPlaying)}`,
|
|
190
|
+
'',
|
|
191
|
+
'Up next:',
|
|
192
|
+
]
|
|
193
|
+
: ['Up next:'];
|
|
194
|
+
if (playbackQueue.queue.length === 0)
|
|
195
|
+
lines.push('Queue is empty.');
|
|
196
|
+
else {
|
|
197
|
+
lines.push(...playbackQueue.queue.map((item, index) => `${index + 1}. ${formatQueueItem(item)}`));
|
|
198
|
+
}
|
|
199
|
+
dependencies.output.log(lines.join('\n'));
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
const tracks = await dependencies.search.searchTracks(query);
|
|
203
|
+
if (tracks.length === 0) {
|
|
204
|
+
dependencies.output.log(`No tracks found for "${query}".`);
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
const track = options.first ? tracks[0] : await chooseTrack(tracks);
|
|
208
|
+
if (!track) {
|
|
209
|
+
dependencies.output.log('Selection cancelled.');
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
await dependencies.queue.addItem(track.uri);
|
|
213
|
+
dependencies.output.log(`✓ Queued ${track.name} — ${track.artists.join(', ')}`);
|
|
214
|
+
});
|
|
74
215
|
program
|
|
75
216
|
.command('search')
|
|
76
217
|
.description('Search Spotify tracks')
|
|
@@ -90,11 +231,21 @@ export function createProgram(dependencies) {
|
|
|
90
231
|
.description('Search for and play a track, or resume with no query')
|
|
91
232
|
.argument('[query...]', 'track name to search for')
|
|
92
233
|
.option('--first', 'play the first search result without prompting')
|
|
234
|
+
.option('--watch', 'continuously refresh playback information')
|
|
235
|
+
.option('--no-watch', 'return to the shell after starting playback')
|
|
93
236
|
.action(async (queryParts, options) => {
|
|
94
237
|
const query = queryParts.join(' ').trim();
|
|
95
238
|
if (!query) {
|
|
239
|
+
const config = await dependencies.config.read();
|
|
240
|
+
const shouldWatch = options.watch ?? config.watchAfterPlay;
|
|
96
241
|
await dependencies.player.resume();
|
|
97
242
|
dependencies.output.log('▶ Resumed');
|
|
243
|
+
if (shouldWatch) {
|
|
244
|
+
await startWatching({
|
|
245
|
+
player: dependencies.player,
|
|
246
|
+
refreshIntervalMs: config.refreshIntervalMs,
|
|
247
|
+
});
|
|
248
|
+
}
|
|
98
249
|
return;
|
|
99
250
|
}
|
|
100
251
|
const tracks = await dependencies.search.searchTracks(query);
|
|
@@ -107,11 +258,64 @@ export function createProgram(dependencies) {
|
|
|
107
258
|
dependencies.output.log('Selection cancelled.');
|
|
108
259
|
return;
|
|
109
260
|
}
|
|
261
|
+
const config = await dependencies.config.read();
|
|
262
|
+
const shouldWatch = options.watch ?? config.watchAfterPlay;
|
|
110
263
|
await dependencies.player.playTrack(track.uri);
|
|
111
264
|
dependencies.output.log(`▶ Playing ${track.name} — ${track.artists.join(', ')}`);
|
|
265
|
+
if (shouldWatch) {
|
|
266
|
+
await startWatching({
|
|
267
|
+
player: dependencies.player,
|
|
268
|
+
refreshIntervalMs: config.refreshIntervalMs,
|
|
269
|
+
});
|
|
270
|
+
}
|
|
112
271
|
});
|
|
113
272
|
return program;
|
|
114
273
|
}
|
|
274
|
+
function parseSeekInput(value) {
|
|
275
|
+
const relative = value.startsWith('+') || value.startsWith('-');
|
|
276
|
+
const sign = value.startsWith('-') ? -1 : 1;
|
|
277
|
+
const time = relative ? value.slice(1) : value;
|
|
278
|
+
const parts = time.split(':');
|
|
279
|
+
if (parts.length > 2 || parts.some((part) => !/^\d+$/.test(part))) {
|
|
280
|
+
throw new ConfigurationError('Seek position must be seconds or minutes:seconds, such as 90, 1:30, +30, or -10.');
|
|
281
|
+
}
|
|
282
|
+
const minutes = parts.length === 2 ? Number(parts[0]) : 0;
|
|
283
|
+
const seconds = Number(parts.at(-1));
|
|
284
|
+
if (!Number.isSafeInteger(minutes) || !Number.isSafeInteger(seconds) || seconds >= 60 && parts.length === 2) {
|
|
285
|
+
throw new ConfigurationError('Seek position must be seconds or minutes:seconds, such as 90, 1:30, +30, or -10.');
|
|
286
|
+
}
|
|
287
|
+
return {
|
|
288
|
+
relative,
|
|
289
|
+
milliseconds: sign * (minutes * 60 + seconds) * 1_000,
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
function formatQueueItem(item) {
|
|
293
|
+
return `${item.name} — ${item.subtitle} · ${formatDuration(item.durationMs)}`;
|
|
294
|
+
}
|
|
295
|
+
function parseVolumeInput(value) {
|
|
296
|
+
if (/^[+-]\d+$/.test(value)) {
|
|
297
|
+
const delta = Number(value);
|
|
298
|
+
if (Number.isSafeInteger(delta) && Math.abs(delta) <= 100) {
|
|
299
|
+
return { relative: true, value: delta };
|
|
300
|
+
}
|
|
301
|
+
throw new ConfigurationError('Relative volume must be between -100 and +100.');
|
|
302
|
+
}
|
|
303
|
+
if (/^\d+$/.test(value)) {
|
|
304
|
+
const volume = Number(value);
|
|
305
|
+
if (Number.isSafeInteger(volume) && volume <= 100) {
|
|
306
|
+
return { relative: false, value: volume };
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
throw new ConfigurationError('Volume must be from 0 to 100, or a change such as +10 or -10.');
|
|
310
|
+
}
|
|
311
|
+
function parseConfigKey(value) {
|
|
312
|
+
if (CONFIG_KEYS.includes(value))
|
|
313
|
+
return value;
|
|
314
|
+
throw new ConfigurationError(`Unknown configuration key "${value}". Valid keys: ${CONFIG_KEYS.join(', ')}.`);
|
|
315
|
+
}
|
|
316
|
+
function formatConfig(config) {
|
|
317
|
+
return CONFIG_KEYS.map((key) => `${key}: ${String(config[key])}`).join('\n');
|
|
318
|
+
}
|
|
115
319
|
function parseLimit(value) {
|
|
116
320
|
const limit = /^\d+$/.test(value) ? Number(value) : Number.NaN;
|
|
117
321
|
if (!Number.isInteger(limit) || limit < 1 || limit > 10) {
|
package/dist/app.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app.js","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"app.js","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"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"}
|
|
@@ -8,6 +8,7 @@ const tokenResponseSchema = z.object({
|
|
|
8
8
|
access_token: z.string().min(1),
|
|
9
9
|
refresh_token: z.string().min(1).optional(),
|
|
10
10
|
expires_in: z.number().int().positive(),
|
|
11
|
+
scope: z.string().optional(),
|
|
11
12
|
});
|
|
12
13
|
const userProfileSchema = z.object({
|
|
13
14
|
id: z.string().min(1),
|
|
@@ -89,6 +90,9 @@ export class SpotifyAuthClient {
|
|
|
89
90
|
if (token.data.refresh_token !== undefined) {
|
|
90
91
|
result.refreshToken = token.data.refresh_token;
|
|
91
92
|
}
|
|
93
|
+
if (token.data.scope !== undefined) {
|
|
94
|
+
result.scopes = token.data.scope.split(' ').filter(Boolean);
|
|
95
|
+
}
|
|
92
96
|
return result;
|
|
93
97
|
}
|
|
94
98
|
async fetchWithTimeout(url, init) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"spotify-auth-client.js","sourceRoot":"","sources":["../../src/auth/spotify-auth-client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAEvD,MAAM,cAAc,GAAG,wCAAwC,CAAC;AAChE,MAAM,qBAAqB,GAAG,+BAA+B,CAAC;AAC9D,MAAM,sBAAsB,GAAG,CAAC,CAAC;AAIjC,MAAM,KAAK,GAAU,CAAC,YAAY,EAAE,EAAE,CACpC,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC,CAAC;AAE9D,MAAM,mBAAmB,GAAG,CAAC,CAAC,MAAM,CAAC;IACnC,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAC/B,aAAa,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE;IAC3C,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE;
|
|
1
|
+
{"version":3,"file":"spotify-auth-client.js","sourceRoot":"","sources":["../../src/auth/spotify-auth-client.ts"],"names":[],"mappings":"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"}
|
package/dist/cli.js
CHANGED
|
@@ -1,17 +1,24 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createProgram } from './app.js';
|
|
3
3
|
import { AuthService } from './services/auth.service.js';
|
|
4
|
+
import { DeviceService } from './services/device.service.js';
|
|
4
5
|
import { PlayerService } from './services/player.service.js';
|
|
6
|
+
import { QueueService } from './services/queue.service.js';
|
|
5
7
|
import { SearchService } from './services/search.service.js';
|
|
8
|
+
import { FileConfigStore } from './storage/config.js';
|
|
6
9
|
import { SpotifyClient } from './spotify/client.js';
|
|
7
10
|
import { consoleOutput } from './ui/output.js';
|
|
8
11
|
import { AppError, toError } from './utils/errors.js';
|
|
9
12
|
const auth = new AuthService();
|
|
10
13
|
const spotify = new SpotifyClient(auth);
|
|
14
|
+
const device = new DeviceService(spotify);
|
|
11
15
|
const program = createProgram({
|
|
12
16
|
auth,
|
|
13
|
-
|
|
17
|
+
device,
|
|
18
|
+
player: new PlayerService(spotify, device),
|
|
19
|
+
queue: new QueueService(spotify),
|
|
14
20
|
search: new SearchService(spotify),
|
|
21
|
+
config: new FileConfigStore(),
|
|
15
22
|
output: consoleOutput,
|
|
16
23
|
});
|
|
17
24
|
try {
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AACzD,OAAO,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAC7D,OAAO,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAC7D,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAEtD,MAAM,IAAI,GAAG,IAAI,WAAW,EAAE,CAAC;AAC/B,MAAM,OAAO,GAAG,IAAI,aAAa,CAAC,IAAI,CAAC,CAAC;AACxC,MAAM,OAAO,GAAG,aAAa,CAAC;IAC5B,IAAI;IACJ,MAAM,EAAE,IAAI,aAAa,CAAC,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AACzD,OAAO,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAC7D,OAAO,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAC7D,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAC3D,OAAO,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAC7D,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAEtD,MAAM,IAAI,GAAG,IAAI,WAAW,EAAE,CAAC;AAC/B,MAAM,OAAO,GAAG,IAAI,aAAa,CAAC,IAAI,CAAC,CAAC;AACxC,MAAM,MAAM,GAAG,IAAI,aAAa,CAAC,OAAO,CAAC,CAAC;AAC1C,MAAM,OAAO,GAAG,aAAa,CAAC;IAC5B,IAAI;IACJ,MAAM;IACN,MAAM,EAAE,IAAI,aAAa,CAAC,OAAO,EAAE,MAAM,CAAC;IAC1C,KAAK,EAAE,IAAI,YAAY,CAAC,OAAO,CAAC;IAChC,MAAM,EAAE,IAAI,aAAa,CAAC,OAAO,CAAC;IAClC,MAAM,EAAE,IAAI,eAAe,EAAE;IAC7B,MAAM,EAAE,aAAa;CACtB,CAAC,CAAC;AAEH,IAAI,CAAC;IACH,MAAM,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;AACzC,CAAC;AAAC,OAAO,KAAK,EAAE,CAAC;IACf,MAAM,eAAe,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;IACvC,aAAa,CAAC,KAAK,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC;IAC7C,OAAO,CAAC,QAAQ,GAAG,KAAK,YAAY,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;AACpE,CAAC"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { SPOTIFY_SCOPES } from '../spotify/scopes.js';
|
|
1
|
+
import { LEGACY_SPOTIFY_SCOPES, SPOTIFY_SCOPES, } from '../spotify/scopes.js';
|
|
2
2
|
import { generateOAuthState, generatePkcePair, } from '../auth/pkce.js';
|
|
3
3
|
import { loadAuthConfig } from '../auth/config.js';
|
|
4
4
|
import { requestAuthorizationCode } from '../auth/callback.js';
|
|
@@ -44,6 +44,7 @@ export class AuthService {
|
|
|
44
44
|
accessToken: token.accessToken,
|
|
45
45
|
refreshToken: token.refreshToken,
|
|
46
46
|
expiresAt: this.clock() + token.expiresIn * 1_000,
|
|
47
|
+
scopes: token.scopes ?? [...SPOTIFY_SCOPES],
|
|
47
48
|
};
|
|
48
49
|
const user = await this.spotifyAuthApi.getCurrentUser(credentials.accessToken);
|
|
49
50
|
await this.credentialStore.write(credentials);
|
|
@@ -56,6 +57,11 @@ export class AuthService {
|
|
|
56
57
|
const credentials = await this.credentialStore.read();
|
|
57
58
|
if (!credentials)
|
|
58
59
|
throw new AuthenticationRequiredError();
|
|
60
|
+
const grantedScopes = credentials.scopes ?? [...LEGACY_SPOTIFY_SCOPES];
|
|
61
|
+
const missingScopes = SPOTIFY_SCOPES.filter((scope) => !grantedScopes.includes(scope));
|
|
62
|
+
if (missingScopes.length > 0) {
|
|
63
|
+
throw new AuthenticationRequiredError(`spoti needs new Spotify permissions (${missingScopes.join(', ')}).\n\nRun: spoti login`);
|
|
64
|
+
}
|
|
59
65
|
if (!isTokenExpired(credentials, this.clock()))
|
|
60
66
|
return credentials.accessToken;
|
|
61
67
|
return this.startTokenRefresh(credentials);
|
|
@@ -106,6 +112,7 @@ export class AuthService {
|
|
|
106
112
|
accessToken: token.accessToken,
|
|
107
113
|
refreshToken: token.refreshToken ?? credentials.refreshToken,
|
|
108
114
|
expiresAt: this.clock() + token.expiresIn * 1_000,
|
|
115
|
+
scopes: token.scopes ?? credentials.scopes ?? [...LEGACY_SPOTIFY_SCOPES],
|
|
109
116
|
};
|
|
110
117
|
await this.credentialStore.write(updatedCredentials);
|
|
111
118
|
return updatedCredentials.accessToken;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.service.js","sourceRoot":"","sources":["../../src/services/auth.service.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"auth.service.js","sourceRoot":"","sources":["../../src/services/auth.service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { SpotifyApi } from '../spotify/client.js';
|
|
2
|
+
export interface Device {
|
|
3
|
+
id: string | null;
|
|
4
|
+
isActive: boolean;
|
|
5
|
+
isPrivateSession: boolean;
|
|
6
|
+
isRestricted: boolean;
|
|
7
|
+
name: string;
|
|
8
|
+
type: string;
|
|
9
|
+
volumePercent: number | null;
|
|
10
|
+
supportsVolume: boolean;
|
|
11
|
+
}
|
|
12
|
+
export declare class DeviceService {
|
|
13
|
+
private readonly spotify;
|
|
14
|
+
constructor(spotify: SpotifyApi);
|
|
15
|
+
getDevices(): Promise<Device[]>;
|
|
16
|
+
getActiveDevice(): Promise<Device>;
|
|
17
|
+
getControllableDevices(): Promise<Device[]>;
|
|
18
|
+
findDevice(nameOrId: string): Promise<Device>;
|
|
19
|
+
transferPlayback(deviceId: string, play?: boolean): Promise<void>;
|
|
20
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { AppError, NoActiveDeviceError } from '../utils/errors.js';
|
|
2
|
+
export class DeviceService {
|
|
3
|
+
spotify;
|
|
4
|
+
constructor(spotify) {
|
|
5
|
+
this.spotify = spotify;
|
|
6
|
+
}
|
|
7
|
+
async getDevices() {
|
|
8
|
+
const response = await this.spotify.get('/me/player/devices');
|
|
9
|
+
return response.devices.map(mapDevice);
|
|
10
|
+
}
|
|
11
|
+
async getActiveDevice() {
|
|
12
|
+
const activeDevice = (await this.getDevices()).find((device) => device.isActive);
|
|
13
|
+
if (!activeDevice)
|
|
14
|
+
throw new NoActiveDeviceError();
|
|
15
|
+
return activeDevice;
|
|
16
|
+
}
|
|
17
|
+
async getControllableDevices() {
|
|
18
|
+
return (await this.getDevices()).filter((device) => device.id !== null && !device.isRestricted);
|
|
19
|
+
}
|
|
20
|
+
async findDevice(nameOrId) {
|
|
21
|
+
const query = nameOrId.trim();
|
|
22
|
+
const normalizedQuery = query.toLocaleLowerCase();
|
|
23
|
+
const devices = await this.getControllableDevices();
|
|
24
|
+
const idMatch = devices.find((device) => device.id?.toLocaleLowerCase() === normalizedQuery);
|
|
25
|
+
if (idMatch)
|
|
26
|
+
return idMatch;
|
|
27
|
+
const nameMatches = devices.filter((device) => device.name.toLocaleLowerCase() === normalizedQuery);
|
|
28
|
+
if (nameMatches.length === 1)
|
|
29
|
+
return nameMatches[0];
|
|
30
|
+
if (nameMatches.length > 1) {
|
|
31
|
+
const deviceIds = nameMatches.map((device) => device.id).join(', ');
|
|
32
|
+
throw new AppError(`Multiple controllable Spotify devices are named "${query}". Use a device ID instead: ${deviceIds}.`);
|
|
33
|
+
}
|
|
34
|
+
throw new AppError(`No controllable Spotify device matches "${query}". Check the device name or ID and ensure Spotify is open on that device.`);
|
|
35
|
+
}
|
|
36
|
+
async transferPlayback(deviceId, play) {
|
|
37
|
+
const normalizedDeviceId = deviceId.trim();
|
|
38
|
+
if (!normalizedDeviceId) {
|
|
39
|
+
throw new AppError('A Spotify device ID is required to transfer playback.');
|
|
40
|
+
}
|
|
41
|
+
await this.spotify.put('/me/player', {
|
|
42
|
+
body: {
|
|
43
|
+
device_ids: [normalizedDeviceId],
|
|
44
|
+
...(play === undefined ? {} : { play }),
|
|
45
|
+
},
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
function mapDevice(device) {
|
|
50
|
+
return {
|
|
51
|
+
id: device.id,
|
|
52
|
+
isActive: device.is_active,
|
|
53
|
+
isPrivateSession: device.is_private_session,
|
|
54
|
+
isRestricted: device.is_restricted,
|
|
55
|
+
name: device.name,
|
|
56
|
+
type: device.type,
|
|
57
|
+
volumePercent: device.volume_percent,
|
|
58
|
+
supportsVolume: device.supports_volume,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=device.service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"device.service.js","sourceRoot":"","sources":["../../src/services/device.service.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,12 +1,19 @@
|
|
|
1
1
|
import type { SpotifyApi } from '../spotify/client.js';
|
|
2
|
+
import type { DeviceService } from './device.service.js';
|
|
2
3
|
import type { CurrentPlayback } from './models.js';
|
|
3
4
|
export declare class PlayerService {
|
|
4
5
|
private readonly spotify;
|
|
5
|
-
|
|
6
|
+
private readonly deviceService?;
|
|
7
|
+
constructor(spotify: SpotifyApi, deviceService?: Pick<DeviceService, "getControllableDevices"> | undefined);
|
|
6
8
|
getCurrentPlayback(): Promise<CurrentPlayback | null>;
|
|
7
9
|
playTrack(uri: string): Promise<void>;
|
|
8
10
|
pause(): Promise<void>;
|
|
9
11
|
resume(): Promise<void>;
|
|
10
12
|
next(): Promise<void>;
|
|
11
13
|
previous(): Promise<void>;
|
|
14
|
+
setVolume(volumePercent: number): Promise<number>;
|
|
15
|
+
changeVolume(delta: number): Promise<number>;
|
|
16
|
+
seek(positionMs: number): Promise<number>;
|
|
17
|
+
changePosition(deltaMs: number): Promise<number>;
|
|
18
|
+
private putWithDeviceFallback;
|
|
12
19
|
}
|
|
@@ -1,7 +1,10 @@
|
|
|
1
|
+
import { AppError, NoActiveDeviceError } from '../utils/errors.js';
|
|
1
2
|
export class PlayerService {
|
|
2
3
|
spotify;
|
|
3
|
-
|
|
4
|
+
deviceService;
|
|
5
|
+
constructor(spotify, deviceService) {
|
|
4
6
|
this.spotify = spotify;
|
|
7
|
+
this.deviceService = deviceService;
|
|
5
8
|
}
|
|
6
9
|
async getCurrentPlayback() {
|
|
7
10
|
const playback = await this.spotify.get('/me/player');
|
|
@@ -15,13 +18,13 @@ export class PlayerService {
|
|
|
15
18
|
};
|
|
16
19
|
}
|
|
17
20
|
async playTrack(uri) {
|
|
18
|
-
await this.
|
|
21
|
+
await this.putWithDeviceFallback('/me/player/play', { body: { uris: [uri] } });
|
|
19
22
|
}
|
|
20
23
|
async pause() {
|
|
21
24
|
await this.spotify.put('/me/player/pause');
|
|
22
25
|
}
|
|
23
26
|
async resume() {
|
|
24
|
-
await this.
|
|
27
|
+
await this.putWithDeviceFallback('/me/player/play');
|
|
25
28
|
}
|
|
26
29
|
async next() {
|
|
27
30
|
await this.spotify.post('/me/player/next');
|
|
@@ -29,6 +32,61 @@ export class PlayerService {
|
|
|
29
32
|
async previous() {
|
|
30
33
|
await this.spotify.post('/me/player/previous');
|
|
31
34
|
}
|
|
35
|
+
async setVolume(volumePercent) {
|
|
36
|
+
const volume = clampVolume(volumePercent);
|
|
37
|
+
await this.spotify.put('/me/player/volume', {
|
|
38
|
+
query: { volume_percent: volume },
|
|
39
|
+
});
|
|
40
|
+
return volume;
|
|
41
|
+
}
|
|
42
|
+
async changeVolume(delta) {
|
|
43
|
+
const playback = await this.spotify.get('/me/player');
|
|
44
|
+
if (!playback)
|
|
45
|
+
throw new NoActiveDeviceError();
|
|
46
|
+
if (!playback.device.supports_volume || playback.device.volume_percent === null) {
|
|
47
|
+
throw new AppError('The active Spotify device does not support volume control.');
|
|
48
|
+
}
|
|
49
|
+
return this.setVolume(playback.device.volume_percent + delta);
|
|
50
|
+
}
|
|
51
|
+
async seek(positionMs) {
|
|
52
|
+
const position = Math.max(0, Math.round(positionMs));
|
|
53
|
+
await this.spotify.put('/me/player/seek', {
|
|
54
|
+
query: { position_ms: position },
|
|
55
|
+
});
|
|
56
|
+
return position;
|
|
57
|
+
}
|
|
58
|
+
async changePosition(deltaMs) {
|
|
59
|
+
const playback = await this.spotify.get('/me/player');
|
|
60
|
+
if (!playback?.item) {
|
|
61
|
+
throw new AppError('Nothing is currently playing, so there is no position to seek.');
|
|
62
|
+
}
|
|
63
|
+
return this.seek((playback.progress_ms ?? 0) + deltaMs);
|
|
64
|
+
}
|
|
65
|
+
async putWithDeviceFallback(path, options) {
|
|
66
|
+
try {
|
|
67
|
+
await this.spotify.put(path, options);
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
catch (error) {
|
|
71
|
+
if (!(error instanceof NoActiveDeviceError) || !this.deviceService)
|
|
72
|
+
throw error;
|
|
73
|
+
}
|
|
74
|
+
const devices = await this.deviceService.getControllableDevices();
|
|
75
|
+
const device = devices.length === 1 ? devices[0] : undefined;
|
|
76
|
+
if (!device?.id) {
|
|
77
|
+
if (devices.length > 1) {
|
|
78
|
+
throw new AppError('Multiple Spotify devices are available, but none is active.\n\nRun: spoti devices\nThen select one with: spoti device "<name>"');
|
|
79
|
+
}
|
|
80
|
+
throw new NoActiveDeviceError();
|
|
81
|
+
}
|
|
82
|
+
await this.spotify.put(path, {
|
|
83
|
+
...options,
|
|
84
|
+
query: { ...options?.query, device_id: device.id },
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
function clampVolume(volume) {
|
|
89
|
+
return Math.min(100, Math.max(0, Math.round(volume)));
|
|
32
90
|
}
|
|
33
91
|
function mapTrack(track) {
|
|
34
92
|
const externalUrl = track.external_urls?.spotify;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"player.service.js","sourceRoot":"","sources":["../../src/services/player.service.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"player.service.js","sourceRoot":"","sources":["../../src/services/player.service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { SpotifyApi } from '../spotify/client.js';
|
|
2
|
+
export interface QueueItem {
|
|
3
|
+
name: string;
|
|
4
|
+
uri: string;
|
|
5
|
+
type: 'track' | 'episode';
|
|
6
|
+
subtitle: string;
|
|
7
|
+
durationMs: number;
|
|
8
|
+
}
|
|
9
|
+
export interface PlaybackQueue {
|
|
10
|
+
currentlyPlaying: QueueItem | null;
|
|
11
|
+
queue: QueueItem[];
|
|
12
|
+
}
|
|
13
|
+
export declare class QueueService {
|
|
14
|
+
private readonly spotify;
|
|
15
|
+
constructor(spotify: SpotifyApi);
|
|
16
|
+
getQueue(): Promise<PlaybackQueue>;
|
|
17
|
+
addItem(uri: string): Promise<void>;
|
|
18
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
export class QueueService {
|
|
2
|
+
spotify;
|
|
3
|
+
constructor(spotify) {
|
|
4
|
+
this.spotify = spotify;
|
|
5
|
+
}
|
|
6
|
+
async getQueue() {
|
|
7
|
+
const response = await this.spotify.get('/me/player/queue');
|
|
8
|
+
return {
|
|
9
|
+
currentlyPlaying: response.currently_playing
|
|
10
|
+
? mapQueueItem(response.currently_playing)
|
|
11
|
+
: null,
|
|
12
|
+
queue: response.queue
|
|
13
|
+
.map(mapQueueItem)
|
|
14
|
+
.filter((item) => item !== null),
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
async addItem(uri) {
|
|
18
|
+
await this.spotify.post('/me/player/queue', { query: { uri } });
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
function mapQueueItem(item) {
|
|
22
|
+
switch (item.type) {
|
|
23
|
+
case 'track':
|
|
24
|
+
return {
|
|
25
|
+
name: item.name,
|
|
26
|
+
uri: item.uri,
|
|
27
|
+
type: item.type,
|
|
28
|
+
subtitle: item.artists.map((artist) => artist.name).join(', '),
|
|
29
|
+
durationMs: item.duration_ms,
|
|
30
|
+
};
|
|
31
|
+
case 'episode':
|
|
32
|
+
return {
|
|
33
|
+
name: item.name,
|
|
34
|
+
uri: item.uri,
|
|
35
|
+
type: item.type,
|
|
36
|
+
subtitle: item.show.name,
|
|
37
|
+
durationMs: item.duration_ms,
|
|
38
|
+
};
|
|
39
|
+
default:
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=queue.service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"queue.service.js","sourceRoot":"","sources":["../../src/services/queue.service.ts"],"names":[],"mappings":"AAsCA,MAAM,OAAO,YAAY;IACM;IAA7B,YAA6B,OAAmB;QAAnB,YAAO,GAAP,OAAO,CAAY;IAAG,CAAC;IAEpD,KAAK,CAAC,QAAQ;QACZ,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,GAAG,CAAuB,kBAAkB,CAAC,CAAC;QAElF,OAAO;YACL,gBAAgB,EAAE,QAAQ,CAAC,iBAAiB;gBAC1C,CAAC,CAAC,YAAY,CAAC,QAAQ,CAAC,iBAAiB,CAAC;gBAC1C,CAAC,CAAC,IAAI;YACR,KAAK,EAAE,QAAQ,CAAC,KAAK;iBAClB,GAAG,CAAC,YAAY,CAAC;iBACjB,MAAM,CAAC,CAAC,IAAI,EAAqB,EAAE,CAAC,IAAI,KAAK,IAAI,CAAC;SACtD,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,GAAW;QACvB,MAAM,IAAI,CAAC,OAAO,CAAC,IAAI,CAAO,kBAAkB,EAAE,EAAE,KAAK,EAAE,EAAE,GAAG,EAAE,EAAE,CAAC,CAAC;IACxE,CAAC;CACF;AAED,SAAS,YAAY,CAAC,IAAsB;IAC1C,QAAQ,IAAI,CAAC,IAAI,EAAE,CAAC;QAClB,KAAK,OAAO;YACV,OAAO;gBACL,IAAI,EAAE,IAAI,CAAC,IAAI;gBACf,GAAG,EAAE,IAAI,CAAC,GAAG;gBACb,IAAI,EAAE,IAAI,CAAC,IAAI;gBACf,QAAQ,EAAE,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;gBAC9D,UAAU,EAAE,IAAI,CAAC,WAAW;aAC7B,CAAC;QACJ,KAAK,SAAS;YACZ,OAAO;gBACL,IAAI,EAAE,IAAI,CAAC,IAAI;gBACf,GAAG,EAAE,IAAI,CAAC,GAAG;gBACb,IAAI,EAAE,IAAI,CAAC,IAAI;gBACf,QAAQ,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI;gBACxB,UAAU,EAAE,IAAI,CAAC,WAAW;aAC7B,CAAC;QACJ;YACE,OAAO,IAAI,CAAC;IAChB,CAAC;AACH,CAAC"}
|
package/dist/spotify/scopes.d.ts
CHANGED
|
@@ -1 +1,2 @@
|
|
|
1
|
-
export declare const
|
|
1
|
+
export declare const LEGACY_SPOTIFY_SCOPES: readonly ["user-read-playback-state", "user-modify-playback-state"];
|
|
2
|
+
export declare const SPOTIFY_SCOPES: readonly ["user-read-playback-state", "user-modify-playback-state", "user-read-currently-playing"];
|
package/dist/spotify/scopes.js
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
|
-
export const
|
|
1
|
+
export const LEGACY_SPOTIFY_SCOPES = [
|
|
2
2
|
'user-read-playback-state',
|
|
3
3
|
'user-modify-playback-state',
|
|
4
4
|
];
|
|
5
|
+
export const SPOTIFY_SCOPES = [
|
|
6
|
+
...LEGACY_SPOTIFY_SCOPES,
|
|
7
|
+
'user-read-currently-playing',
|
|
8
|
+
];
|
|
5
9
|
//# sourceMappingURL=scopes.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scopes.js","sourceRoot":"","sources":["../../src/spotify/scopes.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,
|
|
1
|
+
{"version":3,"file":"scopes.js","sourceRoot":"","sources":["../../src/spotify/scopes.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,qBAAqB,GAAG;IACnC,0BAA0B;IAC1B,4BAA4B;CACpB,CAAC;AAEX,MAAM,CAAC,MAAM,cAAc,GAAG;IAC5B,GAAG,qBAAqB;IACxB,6BAA6B;CACrB,CAAC"}
|
package/dist/spotify/types.d.ts
CHANGED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
declare const configSchema: z.ZodObject<{
|
|
3
|
+
watchAfterPlay: z.ZodDefault<z.ZodBoolean>;
|
|
4
|
+
refreshIntervalMs: z.ZodDefault<z.ZodNumber>;
|
|
5
|
+
}, z.core.$strict>;
|
|
6
|
+
export type AppConfig = z.infer<typeof configSchema>;
|
|
7
|
+
export declare const CONFIG_KEYS: readonly ["watchAfterPlay", "refreshIntervalMs"];
|
|
8
|
+
export type ConfigKey = (typeof CONFIG_KEYS)[number];
|
|
9
|
+
export declare const DEFAULT_CONFIG: Readonly<AppConfig>;
|
|
10
|
+
export interface ConfigStore {
|
|
11
|
+
read(): Promise<AppConfig>;
|
|
12
|
+
write(config: AppConfig): Promise<void>;
|
|
13
|
+
set<Key extends ConfigKey>(key: Key, value: AppConfig[Key]): Promise<void>;
|
|
14
|
+
reset(): Promise<void>;
|
|
15
|
+
}
|
|
16
|
+
export interface FileConfigStoreOptions {
|
|
17
|
+
directory?: string;
|
|
18
|
+
}
|
|
19
|
+
export declare class FileConfigStore implements ConfigStore {
|
|
20
|
+
readonly path: string;
|
|
21
|
+
private readonly directory;
|
|
22
|
+
constructor(options?: FileConfigStoreOptions);
|
|
23
|
+
read(): Promise<AppConfig>;
|
|
24
|
+
write(config: AppConfig): Promise<void>;
|
|
25
|
+
set<Key extends ConfigKey>(key: Key, value: AppConfig[Key]): Promise<void>;
|
|
26
|
+
reset(): Promise<void>;
|
|
27
|
+
}
|
|
28
|
+
export declare function parseConfigValue<Key extends ConfigKey>(key: Key, value: string): AppConfig[Key];
|
|
29
|
+
export {};
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { chmod, mkdir, readFile, rename, unlink, writeFile } from 'node:fs/promises';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { z } from 'zod';
|
|
5
|
+
import { ConfigurationError, toError } from '../utils/errors.js';
|
|
6
|
+
import { getConfigDirectory } from './credentials.js';
|
|
7
|
+
const configSchema = z.strictObject({
|
|
8
|
+
watchAfterPlay: z.boolean().default(false),
|
|
9
|
+
refreshIntervalMs: z.number().int().min(1_000).max(30_000).default(1_000),
|
|
10
|
+
});
|
|
11
|
+
export const CONFIG_KEYS = ['watchAfterPlay', 'refreshIntervalMs'];
|
|
12
|
+
export const DEFAULT_CONFIG = Object.freeze(configSchema.parse({}));
|
|
13
|
+
export class FileConfigStore {
|
|
14
|
+
path;
|
|
15
|
+
directory;
|
|
16
|
+
constructor(options = {}) {
|
|
17
|
+
this.directory = options.directory ?? getConfigDirectory();
|
|
18
|
+
this.path = join(this.directory, 'config.json');
|
|
19
|
+
}
|
|
20
|
+
async read() {
|
|
21
|
+
let contents;
|
|
22
|
+
try {
|
|
23
|
+
contents = await readFile(this.path, 'utf8');
|
|
24
|
+
}
|
|
25
|
+
catch (error) {
|
|
26
|
+
if (isFileNotFound(error))
|
|
27
|
+
return { ...DEFAULT_CONFIG };
|
|
28
|
+
throw new ConfigurationError(`Unable to read application configuration: ${toError(error).message}`);
|
|
29
|
+
}
|
|
30
|
+
let parsed;
|
|
31
|
+
try {
|
|
32
|
+
parsed = JSON.parse(contents);
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
throw new ConfigurationError('Application configuration is not valid JSON.');
|
|
36
|
+
}
|
|
37
|
+
const config = configSchema.safeParse(parsed);
|
|
38
|
+
if (!config.success) {
|
|
39
|
+
throw new ConfigurationError('Application configuration has an invalid format.');
|
|
40
|
+
}
|
|
41
|
+
return config.data;
|
|
42
|
+
}
|
|
43
|
+
async write(config) {
|
|
44
|
+
const validated = configSchema.safeParse(config);
|
|
45
|
+
if (!validated.success) {
|
|
46
|
+
throw new ConfigurationError('Cannot store invalid application configuration.');
|
|
47
|
+
}
|
|
48
|
+
try {
|
|
49
|
+
await mkdir(this.directory, { recursive: true, mode: 0o700 });
|
|
50
|
+
await chmod(this.directory, 0o700);
|
|
51
|
+
}
|
|
52
|
+
catch (error) {
|
|
53
|
+
throw new ConfigurationError(`Unable to prepare the application configuration directory: ${toError(error).message}`);
|
|
54
|
+
}
|
|
55
|
+
const temporaryPath = join(this.directory, `.config-${randomUUID()}.tmp`);
|
|
56
|
+
try {
|
|
57
|
+
await writeFile(temporaryPath, `${JSON.stringify(validated.data, null, 2)}\n`, {
|
|
58
|
+
encoding: 'utf8',
|
|
59
|
+
flag: 'wx',
|
|
60
|
+
mode: 0o600,
|
|
61
|
+
});
|
|
62
|
+
await chmod(temporaryPath, 0o600);
|
|
63
|
+
await rename(temporaryPath, this.path);
|
|
64
|
+
await chmod(this.path, 0o600);
|
|
65
|
+
}
|
|
66
|
+
catch (error) {
|
|
67
|
+
await unlink(temporaryPath).catch(() => undefined);
|
|
68
|
+
throw new ConfigurationError(`Unable to store application configuration: ${toError(error).message}`);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
async set(key, value) {
|
|
72
|
+
const current = await this.read();
|
|
73
|
+
await this.write({ ...current, [key]: value });
|
|
74
|
+
}
|
|
75
|
+
async reset() {
|
|
76
|
+
try {
|
|
77
|
+
await unlink(this.path);
|
|
78
|
+
}
|
|
79
|
+
catch (error) {
|
|
80
|
+
if (!isFileNotFound(error)) {
|
|
81
|
+
throw new ConfigurationError(`Unable to reset application configuration: ${toError(error).message}`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
export function parseConfigValue(key, value) {
|
|
87
|
+
if (key === 'watchAfterPlay') {
|
|
88
|
+
if (value === 'true')
|
|
89
|
+
return true;
|
|
90
|
+
if (value === 'false')
|
|
91
|
+
return false;
|
|
92
|
+
throw new ConfigurationError('Invalid value for watchAfterPlay: expected either "true" or "false".');
|
|
93
|
+
}
|
|
94
|
+
if (key === 'refreshIntervalMs') {
|
|
95
|
+
if (/^-?\d+$/.test(value)) {
|
|
96
|
+
const interval = Number(value);
|
|
97
|
+
if (Number.isSafeInteger(interval) && interval >= 1_000 && interval <= 30_000) {
|
|
98
|
+
return interval;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
throw new ConfigurationError('Invalid value for refreshIntervalMs: expected an integer from 1000 to 30000.');
|
|
102
|
+
}
|
|
103
|
+
throw new ConfigurationError(`Unknown configuration key: ${String(key)}.`);
|
|
104
|
+
}
|
|
105
|
+
function isFileNotFound(error) {
|
|
106
|
+
return (typeof error === 'object' &&
|
|
107
|
+
error !== null &&
|
|
108
|
+
'code' in error &&
|
|
109
|
+
error.code === 'ENOENT');
|
|
110
|
+
}
|
|
111
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/storage/config.ts"],"names":[],"mappings":"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"}
|
|
@@ -3,6 +3,7 @@ declare const credentialsSchema: z.ZodObject<{
|
|
|
3
3
|
accessToken: z.ZodString;
|
|
4
4
|
refreshToken: z.ZodString;
|
|
5
5
|
expiresAt: z.ZodNumber;
|
|
6
|
+
scopes: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
6
7
|
}, z.core.$strict>;
|
|
7
8
|
export type Credentials = z.infer<typeof credentialsSchema>;
|
|
8
9
|
export interface CredentialStore {
|
|
@@ -8,6 +8,7 @@ const credentialsSchema = z.strictObject({
|
|
|
8
8
|
accessToken: z.string().min(1),
|
|
9
9
|
refreshToken: z.string().min(1),
|
|
10
10
|
expiresAt: z.number().int().nonnegative(),
|
|
11
|
+
scopes: z.array(z.string().min(1)).optional(),
|
|
11
12
|
});
|
|
12
13
|
export function getConfigDirectory(environment = process.env, homeDirectory = homedir()) {
|
|
13
14
|
const xdgConfigHome = environment.XDG_CONFIG_HOME?.trim();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"credentials.js","sourceRoot":"","sources":["../../src/storage/credentials.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACrF,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,kBAAkB,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAEjE,MAAM,iBAAiB,GAAG,CAAC,CAAC,YAAY,CAAC;IACvC,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAC9B,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAC/B,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE;
|
|
1
|
+
{"version":3,"file":"credentials.js","sourceRoot":"","sources":["../../src/storage/credentials.ts"],"names":[],"mappings":"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"}
|
package/dist/storage/index.d.ts
CHANGED
|
@@ -1,2 +1,4 @@
|
|
|
1
|
+
export { CONFIG_KEYS, DEFAULT_CONFIG, FileConfigStore, parseConfigValue, } from './config.js';
|
|
2
|
+
export type { AppConfig, ConfigKey, ConfigStore, FileConfigStoreOptions, } from './config.js';
|
|
1
3
|
export { FileCredentialStore, getConfigDirectory, isTokenExpired, } from './credentials.js';
|
|
2
4
|
export type { CredentialStore, Credentials, FileCredentialStoreOptions, } from './credentials.js';
|
package/dist/storage/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/storage/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,mBAAmB,EACnB,kBAAkB,EAClB,cAAc,GACf,MAAM,kBAAkB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/storage/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,WAAW,EACX,cAAc,EACd,eAAe,EACf,gBAAgB,GACjB,MAAM,aAAa,CAAC;AAOrB,OAAO,EACL,mBAAmB,EACnB,kBAAkB,EAClB,cAAc,GACf,MAAM,kBAAkB,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { PlayerService } from '../services/player.service.js';
|
|
2
|
+
export interface WatchPlaybackOptions {
|
|
3
|
+
player: Pick<PlayerService, 'getCurrentPlayback'>;
|
|
4
|
+
refreshIntervalMs: number;
|
|
5
|
+
}
|
|
6
|
+
export type PlaybackWatcher = (options: WatchPlaybackOptions) => Promise<void>;
|
|
7
|
+
export declare const watchPlayback: PlaybackWatcher;
|
package/dist/ui/watch.js
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { stdin, stdout } from 'node:process';
|
|
2
|
+
import { AppError } from '../utils/errors.js';
|
|
3
|
+
import { formatPlayback } from './output.js';
|
|
4
|
+
export const watchPlayback = async ({ player, refreshIntervalMs }) => {
|
|
5
|
+
if (!stdin.isTTY || !stdout.isTTY) {
|
|
6
|
+
throw new AppError('Watch mode requires an interactive terminal.');
|
|
7
|
+
}
|
|
8
|
+
let stopped = false;
|
|
9
|
+
let resolveStop = () => undefined;
|
|
10
|
+
const stopPromise = new Promise((resolve) => {
|
|
11
|
+
resolveStop = resolve;
|
|
12
|
+
});
|
|
13
|
+
const stop = () => {
|
|
14
|
+
stopped = true;
|
|
15
|
+
resolveStop();
|
|
16
|
+
};
|
|
17
|
+
process.once('SIGINT', stop);
|
|
18
|
+
stdout.write('\u001B[?1049h\u001B[?25l');
|
|
19
|
+
try {
|
|
20
|
+
while (!stopped) {
|
|
21
|
+
const playback = await player.getCurrentPlayback();
|
|
22
|
+
const content = playback
|
|
23
|
+
? formatPlayback(playback)
|
|
24
|
+
: 'Nothing is currently playing.';
|
|
25
|
+
stdout.write(`\u001B[2J\u001B[H${content}\n\nPress Ctrl+C to exit`);
|
|
26
|
+
if (!stopped)
|
|
27
|
+
await waitForRefresh(refreshIntervalMs, stopPromise);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
finally {
|
|
31
|
+
process.removeListener('SIGINT', stop);
|
|
32
|
+
stdout.write('\u001B[?25h\u001B[?1049l');
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
async function waitForRefresh(milliseconds, stopPromise) {
|
|
36
|
+
let timer;
|
|
37
|
+
const timerPromise = new Promise((resolve) => {
|
|
38
|
+
timer = setTimeout(resolve, milliseconds);
|
|
39
|
+
});
|
|
40
|
+
await Promise.race([timerPromise, stopPromise]);
|
|
41
|
+
if (timer)
|
|
42
|
+
clearTimeout(timer);
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=watch.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"watch.js","sourceRoot":"","sources":["../../src/ui/watch.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAG7C,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAS7C,MAAM,CAAC,MAAM,aAAa,GAAoB,KAAK,EAAE,EAAE,MAAM,EAAE,iBAAiB,EAAE,EAAE,EAAE;IACpF,IAAI,CAAC,KAAK,CAAC,KAAK,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;QAClC,MAAM,IAAI,QAAQ,CAAC,8CAA8C,CAAC,CAAC;IACrE,CAAC;IAED,IAAI,OAAO,GAAG,KAAK,CAAC;IACpB,IAAI,WAAW,GAAe,GAAG,EAAE,CAAC,SAAS,CAAC;IAC9C,MAAM,WAAW,GAAG,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;QAChD,WAAW,GAAG,OAAO,CAAC;IACxB,CAAC,CAAC,CAAC;IACH,MAAM,IAAI,GAAG,GAAS,EAAE;QACtB,OAAO,GAAG,IAAI,CAAC;QACf,WAAW,EAAE,CAAC;IAChB,CAAC,CAAC;IAEF,OAAO,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;IAC7B,MAAM,CAAC,KAAK,CAAC,0BAA0B,CAAC,CAAC;IAEzC,IAAI,CAAC;QACH,OAAO,CAAC,OAAO,EAAE,CAAC;YAChB,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,kBAAkB,EAAE,CAAC;YACnD,MAAM,OAAO,GAAG,QAAQ;gBACtB,CAAC,CAAC,cAAc,CAAC,QAAQ,CAAC;gBAC1B,CAAC,CAAC,+BAA+B,CAAC;YACpC,MAAM,CAAC,KAAK,CAAC,oBAAoB,OAAO,0BAA0B,CAAC,CAAC;YACpE,IAAI,CAAC,OAAO;gBAAE,MAAM,cAAc,CAAC,iBAAiB,EAAE,WAAW,CAAC,CAAC;QACrE,CAAC;IACH,CAAC;YAAS,CAAC;QACT,OAAO,CAAC,cAAc,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;QACvC,MAAM,CAAC,KAAK,CAAC,0BAA0B,CAAC,CAAC;IAC3C,CAAC;AACH,CAAC,CAAC;AAEF,KAAK,UAAU,cAAc,CAAC,YAAoB,EAAE,WAA0B;IAC5E,IAAI,KAAiC,CAAC;IACtC,MAAM,YAAY,GAAG,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;QACjD,KAAK,GAAG,UAAU,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC;IAC5C,CAAC,CAAC,CAAC;IACH,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC,YAAY,EAAE,WAAW,CAAC,CAAC,CAAC;IAChD,IAAI,KAAK;QAAE,YAAY,CAAC,KAAK,CAAC,CAAC;AACjC,CAAC"}
|
package/dist/version.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
export function readPackageVersion() {
|
|
3
|
+
const packageUrl = new URL('../package.json', import.meta.url);
|
|
4
|
+
const metadata = JSON.parse(readFileSync(packageUrl, 'utf8'));
|
|
5
|
+
if (typeof metadata.version !== 'string' || metadata.version.length === 0) {
|
|
6
|
+
throw new Error('package.json does not contain a valid version.');
|
|
7
|
+
}
|
|
8
|
+
return metadata.version;
|
|
9
|
+
}
|
|
10
|
+
export const VERSION = readPackageVersion();
|
|
11
|
+
//# sourceMappingURL=version.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"version.js","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAMvC,MAAM,UAAU,kBAAkB;IAChC,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC,iBAAiB,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC/D,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,UAAU,EAAE,MAAM,CAAC,CAAoB,CAAC;IACjF,IAAI,OAAO,QAAQ,CAAC,OAAO,KAAK,QAAQ,IAAI,QAAQ,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1E,MAAM,IAAI,KAAK,CAAC,gDAAgD,CAAC,CAAC;IACpE,CAAC;IACD,OAAO,QAAQ,CAAC,OAAO,CAAC;AAC1B,CAAC;AAED,MAAM,CAAC,MAAM,OAAO,GAAG,kBAAkB,EAAE,CAAC"}
|