@bzenky/spoti 0.3.0 → 0.5.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 +68 -6
- package/dist/app.d.ts +10 -2
- package/dist/app.js +207 -82
- package/dist/app.js.map +1 -1
- package/dist/cli.js +4 -2
- package/dist/cli.js.map +1 -1
- package/dist/services/device.service.d.ts +3 -3
- package/dist/services/device.service.js +19 -11
- package/dist/services/device.service.js.map +1 -1
- package/dist/services/player.service.d.ts +4 -2
- package/dist/services/player.service.js +35 -5
- package/dist/services/player.service.js.map +1 -1
- package/dist/services/queue.service.js +11 -6
- package/dist/services/queue.service.js.map +1 -1
- package/dist/services/search.service.d.ts +4 -4
- package/dist/services/search.service.js +10 -9
- package/dist/services/search.service.js.map +1 -1
- package/dist/spotify/client.d.ts +2 -0
- package/dist/spotify/client.js +170 -46
- package/dist/spotify/client.js.map +1 -1
- package/dist/spotify/types.d.ts +2 -0
- package/dist/storage/config.d.ts +3 -0
- package/dist/storage/config.js +3 -0
- package/dist/storage/config.js.map +1 -1
- package/dist/ui/completions.d.ts +3 -0
- package/dist/ui/completions.js +237 -0
- package/dist/ui/completions.js.map +1 -0
- package/dist/ui/interactive-search.d.ts +36 -0
- package/dist/ui/interactive-search.js +337 -0
- package/dist/ui/interactive-search.js.map +1 -0
- package/dist/ui/output.d.ts +27 -11
- package/dist/ui/output.js +87 -45
- package/dist/ui/output.js.map +1 -1
- package/dist/ui/progress.d.ts +6 -0
- package/dist/ui/progress.js +25 -0
- package/dist/ui/progress.js.map +1 -0
- package/dist/ui/prompts.d.ts +11 -1
- package/dist/ui/prompts.js +65 -13
- package/dist/ui/prompts.js.map +1 -1
- package/dist/ui/watch.js +3 -2
- package/dist/ui/watch.js.map +1 -1
- package/dist/utils/errors.js +2 -1
- package/dist/utils/errors.js.map +1 -1
- package/dist/utils/text.d.ts +2 -0
- package/dist/utils/text.js +10 -0
- package/dist/utils/text.js.map +1 -0
- package/package.json +2 -1
package/dist/spotify/client.js
CHANGED
|
@@ -1,6 +1,19 @@
|
|
|
1
1
|
import { AppError, AuthenticationRequiredError, NoActiveDeviceError, PremiumRequiredError, RateLimitedError, SpotifyApiError, toError, } from '../utils/errors.js';
|
|
2
|
+
import { sanitizeOneLineText } from '../utils/text.js';
|
|
2
3
|
const API_BASE_URL = 'https://api.spotify.com/v1';
|
|
3
4
|
const MAX_RATE_LIMIT_RETRIES = 3;
|
|
5
|
+
const PLAYBACK_CONTROL_REQUESTS = new Set([
|
|
6
|
+
'PUT /me/player',
|
|
7
|
+
'PUT /me/player/play',
|
|
8
|
+
'PUT /me/player/pause',
|
|
9
|
+
'POST /me/player/next',
|
|
10
|
+
'POST /me/player/previous',
|
|
11
|
+
'PUT /me/player/seek',
|
|
12
|
+
'PUT /me/player/repeat',
|
|
13
|
+
'PUT /me/player/volume',
|
|
14
|
+
'PUT /me/player/shuffle',
|
|
15
|
+
'POST /me/player/queue',
|
|
16
|
+
]);
|
|
4
17
|
const sleep = (milliseconds) => new Promise((resolve) => setTimeout(resolve, milliseconds));
|
|
5
18
|
export class SpotifyClient {
|
|
6
19
|
authService;
|
|
@@ -35,83 +48,194 @@ export class SpotifyClient {
|
|
|
35
48
|
headers.set('Content-Type', 'application/json');
|
|
36
49
|
body = JSON.stringify(options.body);
|
|
37
50
|
}
|
|
38
|
-
|
|
39
|
-
|
|
51
|
+
throwIfExternallyAborted(options.signal);
|
|
52
|
+
let accessToken = await this.authService.getAccessToken();
|
|
53
|
+
let refreshedToken = false;
|
|
54
|
+
let rateLimitRetries = 0;
|
|
55
|
+
while (true) {
|
|
56
|
+
throwIfExternallyAborted(options.signal);
|
|
57
|
+
headers.set('Authorization', `Bearer ${accessToken}`);
|
|
58
|
+
const attempt = createAttemptSignal(options.signal);
|
|
59
|
+
let shouldRefreshToken = false;
|
|
60
|
+
let retryDelayMs;
|
|
40
61
|
try {
|
|
41
|
-
|
|
62
|
+
const response = await this.fetcher(url, {
|
|
42
63
|
method,
|
|
43
64
|
headers,
|
|
44
|
-
signal:
|
|
65
|
+
signal: attempt.signal,
|
|
45
66
|
...(body === undefined ? {} : { body }),
|
|
46
67
|
});
|
|
68
|
+
if (response.status === 401 && !refreshedToken) {
|
|
69
|
+
shouldRefreshToken = true;
|
|
70
|
+
}
|
|
71
|
+
else if (response.status === 429 &&
|
|
72
|
+
rateLimitRetries < MAX_RATE_LIMIT_RETRIES) {
|
|
73
|
+
const retryAfterSeconds = getRetryAfterSeconds(response);
|
|
74
|
+
const exponentialBackoffMs = 500 * 2 ** rateLimitRetries;
|
|
75
|
+
rateLimitRetries += 1;
|
|
76
|
+
retryDelayMs = Math.max(retryAfterSeconds * 1_000, exponentialBackoffMs);
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
if (!response.ok) {
|
|
80
|
+
const mappedError = await mapSpotifyError(response, method, path);
|
|
81
|
+
throwIfAttemptAborted(attempt.signal, options.signal);
|
|
82
|
+
throw mappedError;
|
|
83
|
+
}
|
|
84
|
+
if (response.status === 204)
|
|
85
|
+
return undefined;
|
|
86
|
+
const text = await response.text();
|
|
87
|
+
throwIfAttemptAborted(attempt.signal, options.signal);
|
|
88
|
+
if (!text)
|
|
89
|
+
return undefined;
|
|
90
|
+
const contentType = response.headers.get('content-type')?.toLocaleLowerCase() ?? '';
|
|
91
|
+
if (!contentType.includes('json')) {
|
|
92
|
+
if (method !== 'GET')
|
|
93
|
+
return undefined;
|
|
94
|
+
throw new SpotifyApiError('Spotify returned an unexpected non-JSON response.', response.status);
|
|
95
|
+
}
|
|
96
|
+
try {
|
|
97
|
+
return JSON.parse(text);
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
throw new SpotifyApiError('Spotify returned malformed JSON.', response.status);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
47
103
|
}
|
|
48
104
|
catch (error) {
|
|
49
|
-
|
|
50
|
-
if (normalizedError.name === 'TimeoutError') {
|
|
51
|
-
throw new AppError('Spotify did not respond within 15 seconds. Try again.');
|
|
52
|
-
}
|
|
53
|
-
throw new AppError(`Unable to reach Spotify: ${normalizedError.message}`);
|
|
105
|
+
throw normalizeAttemptError(error, attempt.signal, options.signal);
|
|
54
106
|
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
while (true) {
|
|
61
|
-
response = await execute(accessToken);
|
|
62
|
-
if (response.status === 401 && !refreshedToken) {
|
|
107
|
+
finally {
|
|
108
|
+
attempt.cleanup();
|
|
109
|
+
}
|
|
110
|
+
if (shouldRefreshToken) {
|
|
111
|
+
throwIfExternallyAborted(options.signal);
|
|
63
112
|
accessToken = await this.authService.forceRefreshAccessToken();
|
|
64
113
|
refreshedToken = true;
|
|
65
114
|
continue;
|
|
66
115
|
}
|
|
67
|
-
if (
|
|
68
|
-
|
|
69
|
-
const exponentialBackoffMs = 500 * 2 ** rateLimitRetries;
|
|
70
|
-
rateLimitRetries += 1;
|
|
71
|
-
await this.sleeper(Math.max(retryAfterMs, exponentialBackoffMs));
|
|
116
|
+
if (retryDelayMs !== undefined) {
|
|
117
|
+
await this.waitForRetry(retryDelayMs, options.signal);
|
|
72
118
|
continue;
|
|
73
119
|
}
|
|
74
|
-
break;
|
|
75
120
|
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
return undefined;
|
|
87
|
-
throw new SpotifyApiError('Spotify returned an unexpected non-JSON response.', response.status);
|
|
121
|
+
}
|
|
122
|
+
async waitForRetry(milliseconds, signal) {
|
|
123
|
+
throwIfExternallyAborted(signal);
|
|
124
|
+
if (!signal) {
|
|
125
|
+
await this.sleeper(milliseconds);
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
if (this.sleeper === sleep) {
|
|
129
|
+
await abortableSleep(milliseconds, signal);
|
|
130
|
+
return;
|
|
88
131
|
}
|
|
132
|
+
let abortListener;
|
|
133
|
+
const aborted = new Promise((_resolve, reject) => {
|
|
134
|
+
abortListener = () => reject(getExternalAbortReason(signal));
|
|
135
|
+
signal.addEventListener('abort', abortListener, { once: true });
|
|
136
|
+
if (signal.aborted)
|
|
137
|
+
abortListener();
|
|
138
|
+
});
|
|
89
139
|
try {
|
|
90
|
-
|
|
140
|
+
await Promise.race([this.sleeper(milliseconds), aborted]);
|
|
91
141
|
}
|
|
92
|
-
|
|
93
|
-
|
|
142
|
+
finally {
|
|
143
|
+
if (abortListener)
|
|
144
|
+
signal.removeEventListener('abort', abortListener);
|
|
94
145
|
}
|
|
95
146
|
}
|
|
96
147
|
}
|
|
97
|
-
function
|
|
98
|
-
const
|
|
99
|
-
|
|
148
|
+
function createAttemptSignal(externalSignal) {
|
|
149
|
+
const controller = new AbortController();
|
|
150
|
+
const abortFromExternalSignal = () => {
|
|
151
|
+
if (externalSignal)
|
|
152
|
+
controller.abort(getExternalAbortReason(externalSignal));
|
|
153
|
+
};
|
|
154
|
+
externalSignal?.addEventListener('abort', abortFromExternalSignal, { once: true });
|
|
155
|
+
if (externalSignal?.aborted)
|
|
156
|
+
abortFromExternalSignal();
|
|
157
|
+
const timeout = setTimeout(() => controller.abort(new DOMException('The operation timed out.', 'TimeoutError')), 15_000);
|
|
158
|
+
timeout.unref();
|
|
159
|
+
let cleanedUp = false;
|
|
160
|
+
return {
|
|
161
|
+
signal: controller.signal,
|
|
162
|
+
cleanup: () => {
|
|
163
|
+
if (cleanedUp)
|
|
164
|
+
return;
|
|
165
|
+
cleanedUp = true;
|
|
166
|
+
clearTimeout(timeout);
|
|
167
|
+
externalSignal?.removeEventListener('abort', abortFromExternalSignal);
|
|
168
|
+
},
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
function normalizeAttemptError(error, attemptSignal, externalSignal) {
|
|
172
|
+
if (externalSignal?.aborted)
|
|
173
|
+
return getExternalAbortReason(externalSignal);
|
|
174
|
+
const normalizedError = toError(error);
|
|
175
|
+
if (normalizedError.name === 'TimeoutError' ||
|
|
176
|
+
(attemptSignal.aborted && toError(attemptSignal.reason).name === 'TimeoutError')) {
|
|
177
|
+
return new AppError('Spotify did not respond within 15 seconds. Try again.');
|
|
178
|
+
}
|
|
179
|
+
if (normalizedError instanceof AppError)
|
|
180
|
+
return normalizedError;
|
|
181
|
+
return new AppError(`Unable to reach Spotify: ${sanitizeOneLineText(normalizedError.message)}`);
|
|
182
|
+
}
|
|
183
|
+
function throwIfAttemptAborted(attemptSignal, externalSignal) {
|
|
184
|
+
throwIfExternallyAborted(externalSignal);
|
|
185
|
+
if (attemptSignal.aborted)
|
|
186
|
+
throw attemptSignal.reason;
|
|
100
187
|
}
|
|
101
|
-
|
|
188
|
+
function throwIfExternallyAborted(signal) {
|
|
189
|
+
if (signal?.aborted)
|
|
190
|
+
throw getExternalAbortReason(signal);
|
|
191
|
+
}
|
|
192
|
+
function getExternalAbortReason(signal) {
|
|
193
|
+
const reason = signal.reason;
|
|
194
|
+
if (reason instanceof Error && reason.name === 'AbortError')
|
|
195
|
+
return reason;
|
|
196
|
+
return new DOMException('The operation was aborted.', 'AbortError');
|
|
197
|
+
}
|
|
198
|
+
function abortableSleep(milliseconds, signal) {
|
|
199
|
+
return new Promise((resolve, reject) => {
|
|
200
|
+
const timer = setTimeout(() => {
|
|
201
|
+
signal.removeEventListener('abort', abortListener);
|
|
202
|
+
resolve();
|
|
203
|
+
}, milliseconds);
|
|
204
|
+
const abortListener = () => {
|
|
205
|
+
clearTimeout(timer);
|
|
206
|
+
reject(getExternalAbortReason(signal));
|
|
207
|
+
};
|
|
208
|
+
signal.addEventListener('abort', abortListener, { once: true });
|
|
209
|
+
if (signal.aborted)
|
|
210
|
+
abortListener();
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
function getRetryAfterSeconds(response) {
|
|
214
|
+
const header = response.headers.get('retry-after');
|
|
215
|
+
if (header === null)
|
|
216
|
+
return 1;
|
|
217
|
+
const seconds = Number(header);
|
|
218
|
+
if (!Number.isFinite(seconds) || seconds < 0)
|
|
219
|
+
return 1;
|
|
220
|
+
return Math.ceil(seconds);
|
|
221
|
+
}
|
|
222
|
+
async function mapSpotifyError(response, method, path) {
|
|
102
223
|
const body = await readErrorBody(response);
|
|
103
224
|
const spotifyMessage = typeof body.error === 'object' ? body.error?.message : body.error_description;
|
|
104
|
-
const
|
|
225
|
+
const sanitizedMessage = spotifyMessage ? sanitizeOneLineText(spotifyMessage) : '';
|
|
226
|
+
const message = sanitizedMessage || `Spotify API request failed with HTTP ${response.status}.`;
|
|
105
227
|
if (response.status === 401) {
|
|
106
228
|
return new AuthenticationRequiredError('Spotify authentication has expired or was revoked.\n\nRun: spoti login');
|
|
107
229
|
}
|
|
108
230
|
if (response.status === 403 && /premium/i.test(message))
|
|
109
231
|
return new PremiumRequiredError();
|
|
110
|
-
if (response.status === 404 &&
|
|
232
|
+
if (response.status === 404 &&
|
|
233
|
+
PLAYBACK_CONTROL_REQUESTS.has(`${method} ${path}`) &&
|
|
234
|
+
/\b(?:no active device|device not found)\b/i.test(message)) {
|
|
111
235
|
return new NoActiveDeviceError();
|
|
236
|
+
}
|
|
112
237
|
if (response.status === 429) {
|
|
113
|
-
|
|
114
|
-
return new RateLimitedError(Number.isFinite(retryAfter) ? retryAfter : 1);
|
|
238
|
+
return new RateLimitedError(getRetryAfterSeconds(response));
|
|
115
239
|
}
|
|
116
240
|
return new SpotifyApiError(message, response.status);
|
|
117
241
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/spotify/client.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,QAAQ,EACR,2BAA2B,EAC3B,mBAAmB,EACnB,oBAAoB,EACpB,gBAAgB,EAChB,eAAe,EACf,OAAO,GACR,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/spotify/client.ts"],"names":[],"mappings":"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"}
|
package/dist/spotify/types.d.ts
CHANGED
|
@@ -122,6 +122,8 @@ export interface SpotifyCursorPaging<T> {
|
|
|
122
122
|
}
|
|
123
123
|
export interface SpotifyPlaybackState {
|
|
124
124
|
is_playing: boolean;
|
|
125
|
+
repeat_state: string;
|
|
126
|
+
shuffle_state: boolean;
|
|
125
127
|
progress_ms: number | null;
|
|
126
128
|
item: SpotifyPlaybackItem | null;
|
|
127
129
|
device: {
|
package/dist/storage/config.d.ts
CHANGED
|
@@ -9,9 +9,11 @@ export declare const CONFIG_KEYS: readonly ["spotifyClientId", "watchAfterPlay",
|
|
|
9
9
|
export type ConfigKey = (typeof CONFIG_KEYS)[number];
|
|
10
10
|
export declare const DEFAULT_CONFIG: Readonly<AppConfig>;
|
|
11
11
|
export interface ConfigStore {
|
|
12
|
+
readonly path?: string;
|
|
12
13
|
read(): Promise<AppConfig>;
|
|
13
14
|
write(config: AppConfig): Promise<void>;
|
|
14
15
|
set<Key extends ConfigKey>(key: Key, value: AppConfig[Key]): Promise<void>;
|
|
16
|
+
resetKey<Key extends ConfigKey>(key: Key): Promise<void>;
|
|
15
17
|
reset(): Promise<void>;
|
|
16
18
|
}
|
|
17
19
|
export interface FileConfigStoreOptions {
|
|
@@ -24,6 +26,7 @@ export declare class FileConfigStore implements ConfigStore {
|
|
|
24
26
|
read(): Promise<AppConfig>;
|
|
25
27
|
write(config: AppConfig): Promise<void>;
|
|
26
28
|
set<Key extends ConfigKey>(key: Key, value: AppConfig[Key]): Promise<void>;
|
|
29
|
+
resetKey<Key extends ConfigKey>(key: Key): Promise<void>;
|
|
27
30
|
reset(): Promise<void>;
|
|
28
31
|
}
|
|
29
32
|
export declare function parseConfigValue<Key extends ConfigKey>(key: Key, value: string): AppConfig[Key];
|
package/dist/storage/config.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/storage/config.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,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,kBAAkB,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AACjE,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAEtD,MAAM,qBAAqB,GAAG,CAAC;KAC5B,MAAM,EAAE;KACR,IAAI,EAAE;KACN,GAAG,CAAC,CAAC,CAAC;KACN,GAAG,CAAC,GAAG,CAAC;KACR,KAAK,CAAC,gBAAgB,CAAC,CAAC;AAE3B,MAAM,YAAY,GAAG,CAAC,CAAC,YAAY,CAAC;IAClC,eAAe,EAAE,qBAAqB,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC;IAC/D,cAAc,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC;IAC1C,iBAAiB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC;CAC1E,CAAC,CAAC;AAIH,MAAM,CAAC,MAAM,WAAW,GAAG;IACzB,iBAAiB;IACjB,gBAAgB;IAChB,mBAAmB;CACX,CAAC;AAGX,MAAM,CAAC,MAAM,cAAc,GAAwB,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/storage/config.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,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,kBAAkB,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AACjE,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAEtD,MAAM,qBAAqB,GAAG,CAAC;KAC5B,MAAM,EAAE;KACR,IAAI,EAAE;KACN,GAAG,CAAC,CAAC,CAAC;KACN,GAAG,CAAC,GAAG,CAAC;KACR,KAAK,CAAC,gBAAgB,CAAC,CAAC;AAE3B,MAAM,YAAY,GAAG,CAAC,CAAC,YAAY,CAAC;IAClC,eAAe,EAAE,qBAAqB,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC;IAC/D,cAAc,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC;IAC1C,iBAAiB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC;CAC1E,CAAC,CAAC;AAIH,MAAM,CAAC,MAAM,WAAW,GAAG;IACzB,iBAAiB;IACjB,gBAAgB;IAChB,mBAAmB;CACX,CAAC;AAGX,MAAM,CAAC,MAAM,cAAc,GAAwB,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC;AAezF,MAAM,OAAO,eAAe;IACjB,IAAI,CAAS;IACL,SAAS,CAAS;IAEnC,YAAY,UAAkC,EAAE;QAC9C,IAAI,CAAC,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,kBAAkB,EAAE,CAAC;QAC3D,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,aAAa,CAAC,CAAC;IAClD,CAAC;IAED,KAAK,CAAC,IAAI;QACR,IAAI,QAAgB,CAAC;QACrB,IAAI,CAAC;YACH,QAAQ,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QAC/C,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,cAAc,CAAC,KAAK,CAAC;gBAAE,OAAO,EAAE,GAAG,cAAc,EAAE,CAAC;YACxD,MAAM,IAAI,kBAAkB,CAC1B,6CAA6C,OAAO,CAAC,KAAK,CAAC,CAAC,OAAO,EAAE,CACtE,CAAC;QACJ,CAAC;QAED,IAAI,MAAe,CAAC;QACpB,IAAI,CAAC;YACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAY,CAAC;QAC3C,CAAC;QAAC,MAAM,CAAC;YACP,MAAM,IAAI,kBAAkB,CAAC,8CAA8C,CAAC,CAAC;QAC/E,CAAC;QAED,MAAM,MAAM,GAAG,YAAY,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;QAC9C,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;YACpB,MAAM,IAAI,kBAAkB,CAAC,kDAAkD,CAAC,CAAC;QACnF,CAAC;QACD,OAAO,MAAM,CAAC,IAAI,CAAC;IACrB,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,MAAiB;QAC3B,MAAM,SAAS,GAAG,YAAY,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;QACjD,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,CAAC;YACvB,MAAM,IAAI,kBAAkB,CAAC,iDAAiD,CAAC,CAAC;QAClF,CAAC;QAED,IAAI,CAAC;YACH,MAAM,KAAK,CAAC,IAAI,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;YAC9D,MAAM,KAAK,CAAC,IAAI,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;QACrC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,IAAI,kBAAkB,CAC1B,8DAA8D,OAAO,CAAC,KAAK,CAAC,CAAC,OAAO,EAAE,CACvF,CAAC;QACJ,CAAC;QAED,MAAM,aAAa,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,WAAW,UAAU,EAAE,MAAM,CAAC,CAAC;QAC1E,IAAI,CAAC;YACH,MAAM,SAAS,CAAC,aAAa,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE;gBAC7E,QAAQ,EAAE,MAAM;gBAChB,IAAI,EAAE,IAAI;gBACV,IAAI,EAAE,KAAK;aACZ,CAAC,CAAC;YACH,MAAM,KAAK,CAAC,aAAa,EAAE,KAAK,CAAC,CAAC;YAClC,MAAM,MAAM,CAAC,aAAa,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;YACvC,MAAM,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QAChC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,MAAM,CAAC,aAAa,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;YACnD,MAAM,IAAI,kBAAkB,CAC1B,8CAA8C,OAAO,CAAC,KAAK,CAAC,CAAC,OAAO,EAAE,CACvE,CAAC;QACJ,CAAC;IACH,CAAC;IAED,KAAK,CAAC,GAAG,CAAwB,GAAQ,EAAE,KAAqB;QAC9D,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;QAClC,MAAM,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,OAAO,EAAE,CAAC,GAAG,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;IACjD,CAAC;IAED,KAAK,CAAC,QAAQ,CAAwB,GAAQ;QAC5C,MAAM,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC;IAC3C,CAAC;IAED,KAAK,CAAC,KAAK;QACT,IAAI,CAAC;YACH,MAAM,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC1B,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,cAAc,CAAC,KAAK,CAAC,EAAE,CAAC;gBAC3B,MAAM,IAAI,kBAAkB,CAC1B,8CAA8C,OAAO,CAAC,KAAK,CAAC,CAAC,OAAO,EAAE,CACvE,CAAC;YACJ,CAAC;QACH,CAAC;IACH,CAAC;CACF;AAED,MAAM,UAAU,gBAAgB,CAC9B,GAAQ,EACR,KAAa;IAEb,IAAI,GAAG,KAAK,iBAAiB,EAAE,CAAC;QAC9B,MAAM,QAAQ,GAAG,qBAAqB,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;QACxD,IAAI,QAAQ,CAAC,OAAO;YAAE,OAAO,QAAQ,CAAC,IAAsB,CAAC;QAC7D,MAAM,IAAI,kBAAkB,CAC1B,qEAAqE,CACtE,CAAC;IACJ,CAAC;IAED,IAAI,GAAG,KAAK,gBAAgB,EAAE,CAAC;QAC7B,IAAI,KAAK,KAAK,MAAM;YAAE,OAAO,IAAsB,CAAC;QACpD,IAAI,KAAK,KAAK,OAAO;YAAE,OAAO,KAAuB,CAAC;QACtD,MAAM,IAAI,kBAAkB,CAC1B,sEAAsE,CACvE,CAAC;IACJ,CAAC;IAED,IAAI,GAAG,KAAK,mBAAmB,EAAE,CAAC;QAChC,IAAI,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;YAC1B,MAAM,QAAQ,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;YAC/B,IAAI,MAAM,CAAC,aAAa,CAAC,QAAQ,CAAC,IAAI,QAAQ,IAAI,KAAK,IAAI,QAAQ,IAAI,MAAM,EAAE,CAAC;gBAC9E,OAAO,QAA0B,CAAC;YACpC,CAAC;QACH,CAAC;QACD,MAAM,IAAI,kBAAkB,CAC1B,8EAA8E,CAC/E,CAAC;IACJ,CAAC;IAED,MAAM,IAAI,kBAAkB,CAAC,8BAA8B,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;AAC7E,CAAC;AAED,SAAS,cAAc,CAAC,KAAc;IACpC,OAAO,CACL,OAAO,KAAK,KAAK,QAAQ;QACzB,KAAK,KAAK,IAAI;QACd,MAAM,IAAI,KAAK;QACf,KAAK,CAAC,IAAI,KAAK,QAAQ,CACxB,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
const bashCompletion = `# bash completion for spoti
|
|
2
|
+
_spoti_completion() {
|
|
3
|
+
local cur command
|
|
4
|
+
cur="\${COMP_WORDS[COMP_CWORD]}"
|
|
5
|
+
command="\${COMP_WORDS[1]}"
|
|
6
|
+
|
|
7
|
+
local commands="setup login logout status config interactive now pause resume next previous devices device seek volume queue shuffle repeat album artist playlists playlist liked like unlike recent update search play completion i p pa r np q s vol dev devs pl pls rep rec n prev"
|
|
8
|
+
local global_options="-h --help -V --version"
|
|
9
|
+
|
|
10
|
+
if (( COMP_CWORD == 1 )); then
|
|
11
|
+
COMPREPLY=( $(compgen -W "$commands $global_options" -- "$cur") )
|
|
12
|
+
return
|
|
13
|
+
fi
|
|
14
|
+
|
|
15
|
+
case "$command" in
|
|
16
|
+
config)
|
|
17
|
+
if (( COMP_CWORD == 2 )); then
|
|
18
|
+
COMPREPLY=( $(compgen -W "get set reset path unset -h --help" -- "$cur") )
|
|
19
|
+
elif (( COMP_CWORD == 3 )) && [[ "\${COMP_WORDS[2]}" == get || "\${COMP_WORDS[2]}" == set || "\${COMP_WORDS[2]}" == unset ]]; then
|
|
20
|
+
COMPREPLY=( $(compgen -W "spotifyClientId watchAfterPlay refreshIntervalMs" -- "$cur") )
|
|
21
|
+
elif (( COMP_CWORD == 4 )) && [[ "\${COMP_WORDS[2]}" == set && "\${COMP_WORDS[3]}" == watchAfterPlay ]]; then
|
|
22
|
+
COMPREPLY=( $(compgen -W "true false" -- "$cur") )
|
|
23
|
+
fi
|
|
24
|
+
;;
|
|
25
|
+
completion)
|
|
26
|
+
COMPREPLY=( $(compgen -W "bash zsh fish -h --help" -- "$cur") )
|
|
27
|
+
;;
|
|
28
|
+
now|np)
|
|
29
|
+
COMPREPLY=( $(compgen -W "-w --watch -h --help" -- "$cur") )
|
|
30
|
+
;;
|
|
31
|
+
queue|q|album|artist|playlist|pl)
|
|
32
|
+
COMPREPLY=( $(compgen -W "--first -h --help" -- "$cur") )
|
|
33
|
+
;;
|
|
34
|
+
playlists|pls|liked|recent|rec|search|s)
|
|
35
|
+
COMPREPLY=( $(compgen -W "-l --limit -h --help" -- "$cur") )
|
|
36
|
+
;;
|
|
37
|
+
update)
|
|
38
|
+
COMPREPLY=( $(compgen -W "--check -h --help" -- "$cur") )
|
|
39
|
+
;;
|
|
40
|
+
play|p)
|
|
41
|
+
COMPREPLY=( $(compgen -W "track album artist playlist --first --watch --no-watch -h --help" -- "$cur") )
|
|
42
|
+
;;
|
|
43
|
+
shuffle)
|
|
44
|
+
COMPREPLY=( $(compgen -W "on off -h --help" -- "$cur") )
|
|
45
|
+
;;
|
|
46
|
+
repeat|rep)
|
|
47
|
+
COMPREPLY=( $(compgen -W "off track context -h --help" -- "$cur") )
|
|
48
|
+
;;
|
|
49
|
+
*)
|
|
50
|
+
COMPREPLY=( $(compgen -W "-h --help" -- "$cur") )
|
|
51
|
+
;;
|
|
52
|
+
esac
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
complete -F _spoti_completion spoti
|
|
56
|
+
`;
|
|
57
|
+
const zshCompletion = `#compdef spoti
|
|
58
|
+
# zsh completion for spoti
|
|
59
|
+
|
|
60
|
+
_spoti() {
|
|
61
|
+
local context state line
|
|
62
|
+
local -a commands
|
|
63
|
+
commands=(
|
|
64
|
+
'setup:save your Spotify application client ID'
|
|
65
|
+
'login:log in to Spotify'
|
|
66
|
+
'logout:remove locally stored Spotify credentials'
|
|
67
|
+
'status:show authentication status'
|
|
68
|
+
'config:view and update spoti configuration'
|
|
69
|
+
'interactive:open the keyboard-driven Spotify search'
|
|
70
|
+
'now:show the current Spotify playback'
|
|
71
|
+
'pause:pause playback'
|
|
72
|
+
'resume:resume playback'
|
|
73
|
+
'next:skip to the next track'
|
|
74
|
+
'previous:return to the previous track'
|
|
75
|
+
'devices:list available Spotify Connect devices'
|
|
76
|
+
'device:transfer playback to a Spotify Connect device'
|
|
77
|
+
'seek:seek within the current track'
|
|
78
|
+
'volume:set or adjust the active device volume'
|
|
79
|
+
'queue:show the playback queue or add a searched track'
|
|
80
|
+
'shuffle:show or change the playback shuffle state'
|
|
81
|
+
'repeat:show or change the playback repeat mode'
|
|
82
|
+
'album:search for and show an album'
|
|
83
|
+
'artist:search for and show an artist'
|
|
84
|
+
'playlists:list and optionally play your Spotify playlists'
|
|
85
|
+
'playlist:show one of your Spotify playlists'
|
|
86
|
+
'liked:list and optionally play your liked tracks'
|
|
87
|
+
'like:add the current track to your Spotify library'
|
|
88
|
+
'unlike:remove the current track from your Spotify library'
|
|
89
|
+
'recent:show and optionally play recently played tracks'
|
|
90
|
+
'update:check for or install the latest spoti version'
|
|
91
|
+
'search:search Spotify tracks'
|
|
92
|
+
'play:play a track, album, artist, or playlist'
|
|
93
|
+
'completion:print a static shell completion script'
|
|
94
|
+
'i:alias for interactive'
|
|
95
|
+
'p:alias for play'
|
|
96
|
+
'pa:alias for pause'
|
|
97
|
+
'r:alias for resume'
|
|
98
|
+
'np:alias for now'
|
|
99
|
+
'q:alias for queue'
|
|
100
|
+
's:alias for search'
|
|
101
|
+
'vol:alias for volume'
|
|
102
|
+
'dev:alias for device'
|
|
103
|
+
'devs:alias for devices'
|
|
104
|
+
'pl:alias for playlist'
|
|
105
|
+
'pls:alias for playlists'
|
|
106
|
+
'rep:alias for repeat'
|
|
107
|
+
'rec:alias for recent'
|
|
108
|
+
'n:alias for next'
|
|
109
|
+
'prev:alias for previous'
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
_arguments -C \\
|
|
113
|
+
'(-h --help)'{-h,--help}'[display help]' \\
|
|
114
|
+
'(-V --version)'{-V,--version}'[display version]' \
|
|
115
|
+
'1:command:->command' \
|
|
116
|
+
'*::argument:->arguments'
|
|
117
|
+
|
|
118
|
+
case "$state" in
|
|
119
|
+
command)
|
|
120
|
+
_describe 'spoti command' commands
|
|
121
|
+
;;
|
|
122
|
+
arguments)
|
|
123
|
+
case "$words[2]" in
|
|
124
|
+
config)
|
|
125
|
+
local -a config_commands
|
|
126
|
+
config_commands=(
|
|
127
|
+
'get:read a configuration value'
|
|
128
|
+
'set:set a configuration value'
|
|
129
|
+
'reset:reset configuration to defaults'
|
|
130
|
+
'path:show the configuration file path'
|
|
131
|
+
'unset:remove a configuration value'
|
|
132
|
+
)
|
|
133
|
+
if (( CURRENT == 3 )); then
|
|
134
|
+
_describe 'config command' config_commands
|
|
135
|
+
elif [[ "$words[3]" == get || "$words[3]" == set || "$words[3]" == unset ]]; then
|
|
136
|
+
_values 'configuration key' spotifyClientId watchAfterPlay refreshIntervalMs
|
|
137
|
+
fi
|
|
138
|
+
;;
|
|
139
|
+
completion)
|
|
140
|
+
_values 'shell' bash zsh fish
|
|
141
|
+
;;
|
|
142
|
+
now|np)
|
|
143
|
+
_arguments '(-w --watch)'{-w,--watch}'[continuously refresh playback information]'
|
|
144
|
+
;;
|
|
145
|
+
queue|q|album|artist|playlist|pl)
|
|
146
|
+
_arguments '--first[select the first result without prompting]' '*:query:'
|
|
147
|
+
;;
|
|
148
|
+
playlists|pls|liked|recent|rec|search|s)
|
|
149
|
+
_arguments '(-l --limit)'{-l,--limit}'[maximum number of results]:number:' '*:query:'
|
|
150
|
+
;;
|
|
151
|
+
update)
|
|
152
|
+
_arguments '--check[check without installing]'
|
|
153
|
+
;;
|
|
154
|
+
play|p)
|
|
155
|
+
_arguments '--first[play the first result without prompting]' '--watch[continuously refresh playback information]' '--no-watch[return after starting playback]' '1:type:(track album artist playlist)' '*:query:'
|
|
156
|
+
;;
|
|
157
|
+
shuffle)
|
|
158
|
+
_values 'shuffle state' on off
|
|
159
|
+
;;
|
|
160
|
+
repeat|rep)
|
|
161
|
+
_values 'repeat mode' off track context
|
|
162
|
+
;;
|
|
163
|
+
esac
|
|
164
|
+
;;
|
|
165
|
+
esac
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
_spoti "$@"
|
|
169
|
+
`;
|
|
170
|
+
const fishCompletion = `# fish completion for spoti
|
|
171
|
+
complete -c spoti -f
|
|
172
|
+
complete -c spoti -s h -l help -d 'Display help'
|
|
173
|
+
complete -c spoti -s V -l version -d 'Display version'
|
|
174
|
+
|
|
175
|
+
function __spoti_needs_command
|
|
176
|
+
not __fish_seen_subcommand_from setup login logout status config interactive now pause resume next previous devices device seek volume queue shuffle repeat album artist playlists playlist liked like unlike recent update search play completion i p pa r np q s vol dev devs pl pls rep rec n prev
|
|
177
|
+
end
|
|
178
|
+
complete -c spoti -n __spoti_needs_command -a setup -d 'Save your Spotify application client ID'
|
|
179
|
+
complete -c spoti -n __spoti_needs_command -a login -d 'Log in to Spotify'
|
|
180
|
+
complete -c spoti -n __spoti_needs_command -a logout -d 'Remove locally stored Spotify credentials'
|
|
181
|
+
complete -c spoti -n __spoti_needs_command -a status -d 'Show authentication status'
|
|
182
|
+
complete -c spoti -n __spoti_needs_command -a config -d 'View and update configuration'
|
|
183
|
+
complete -c spoti -n __spoti_needs_command -a interactive -d 'Open the keyboard-driven Spotify search'
|
|
184
|
+
complete -c spoti -n __spoti_needs_command -a now -d 'Show current playback'
|
|
185
|
+
complete -c spoti -n __spoti_needs_command -a pause -d 'Pause playback'
|
|
186
|
+
complete -c spoti -n __spoti_needs_command -a resume -d 'Resume playback'
|
|
187
|
+
complete -c spoti -n __spoti_needs_command -a next -d 'Skip to the next track'
|
|
188
|
+
complete -c spoti -n __spoti_needs_command -a previous -d 'Return to the previous track'
|
|
189
|
+
complete -c spoti -n __spoti_needs_command -a devices -d 'List Spotify Connect devices'
|
|
190
|
+
complete -c spoti -n __spoti_needs_command -a device -d 'Transfer playback to a device'
|
|
191
|
+
complete -c spoti -n __spoti_needs_command -a seek -d 'Seek within the current track'
|
|
192
|
+
complete -c spoti -n __spoti_needs_command -a volume -d 'Set or adjust volume'
|
|
193
|
+
complete -c spoti -n __spoti_needs_command -a queue -d 'Show or add to the queue'
|
|
194
|
+
complete -c spoti -n __spoti_needs_command -a shuffle -d 'Show or change playback shuffle state'
|
|
195
|
+
complete -c spoti -n __spoti_needs_command -a repeat -d 'Show or change playback repeat mode'
|
|
196
|
+
complete -c spoti -n __spoti_needs_command -a album -d 'Search for an album'
|
|
197
|
+
complete -c spoti -n __spoti_needs_command -a artist -d 'Search for an artist'
|
|
198
|
+
complete -c spoti -n __spoti_needs_command -a playlists -d 'List and optionally play your playlists'
|
|
199
|
+
complete -c spoti -n __spoti_needs_command -a playlist -d 'Show a playlist'
|
|
200
|
+
complete -c spoti -n __spoti_needs_command -a liked -d 'List and optionally play liked tracks'
|
|
201
|
+
complete -c spoti -n __spoti_needs_command -a like -d 'Like the current track'
|
|
202
|
+
complete -c spoti -n __spoti_needs_command -a unlike -d 'Unlike the current track'
|
|
203
|
+
complete -c spoti -n __spoti_needs_command -a recent -d 'Show and optionally play recently played tracks'
|
|
204
|
+
complete -c spoti -n __spoti_needs_command -a update -d 'Check for or install updates'
|
|
205
|
+
complete -c spoti -n __spoti_needs_command -a search -d 'Search Spotify tracks'
|
|
206
|
+
complete -c spoti -n __spoti_needs_command -a play -d 'Play a track, album, artist, or playlist'
|
|
207
|
+
complete -c spoti -n __spoti_needs_command -a completion -d 'Print a shell completion script'
|
|
208
|
+
complete -c spoti -n __spoti_needs_command -a 'i p pa r np q s vol dev devs pl pls rep rec n prev' -d 'Command alias'
|
|
209
|
+
|
|
210
|
+
complete -c spoti -n '__fish_seen_subcommand_from config; and not __fish_seen_subcommand_from get set reset path unset' -a 'get set reset path unset'
|
|
211
|
+
complete -c spoti -n '__fish_seen_subcommand_from config; and __fish_seen_subcommand_from get set unset' -a 'spotifyClientId watchAfterPlay refreshIntervalMs'
|
|
212
|
+
complete -c spoti -n '__fish_seen_subcommand_from completion' -a 'bash zsh fish'
|
|
213
|
+
complete -c spoti -n '__fish_seen_subcommand_from now np' -s w -l watch -d 'Continuously refresh playback information'
|
|
214
|
+
complete -c spoti -n '__fish_seen_subcommand_from queue q album artist playlist pl' -l first -d 'Select the first result without prompting'
|
|
215
|
+
complete -c spoti -n '__fish_seen_subcommand_from playlists pls liked recent rec search s' -s l -l limit -r -d 'Maximum number of results'
|
|
216
|
+
complete -c spoti -n '__fish_seen_subcommand_from update' -l check -d 'Check without installing'
|
|
217
|
+
complete -c spoti -n '__fish_seen_subcommand_from play p' -l first -d 'Play the first result without prompting'
|
|
218
|
+
complete -c spoti -n '__fish_seen_subcommand_from play p' -l watch -d 'Continuously refresh playback information'
|
|
219
|
+
complete -c spoti -n '__fish_seen_subcommand_from play p' -l no-watch -d 'Return after starting playback'
|
|
220
|
+
complete -c spoti -n '__fish_seen_subcommand_from play p' -a 'track album artist playlist'
|
|
221
|
+
complete -c spoti -n '__fish_seen_subcommand_from shuffle' -a 'on off'
|
|
222
|
+
complete -c spoti -n '__fish_seen_subcommand_from repeat rep' -a 'off track context'
|
|
223
|
+
`;
|
|
224
|
+
/** Return a self-contained completion definition without running the spoti CLI. */
|
|
225
|
+
export function generateCompletionScript(shell) {
|
|
226
|
+
switch (shell) {
|
|
227
|
+
case 'bash':
|
|
228
|
+
return bashCompletion;
|
|
229
|
+
case 'zsh':
|
|
230
|
+
return zshCompletion;
|
|
231
|
+
case 'fish':
|
|
232
|
+
return fishCompletion;
|
|
233
|
+
default:
|
|
234
|
+
throw new Error(`Unsupported shell: ${String(shell)}`);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
//# sourceMappingURL=completions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"completions.js","sourceRoot":"","sources":["../../src/ui/completions.ts"],"names":[],"mappings":"AAEA,MAAM,cAAc,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAuDtB,CAAC;AAEF,MAAM,aAAa,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAgHrB,CAAC;AAEF,MAAM,cAAc,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAqDtB,CAAC;AAEF,mFAAmF;AACnF,MAAM,UAAU,wBAAwB,CAAC,KAAsB;IAC7D,QAAQ,KAAK,EAAE,CAAC;QACd,KAAK,MAAM;YACT,OAAO,cAAc,CAAC;QACxB,KAAK,KAAK;YACR,OAAO,aAAa,CAAC;QACvB,KAAK,MAAM;YACT,OAAO,cAAc,CAAC;QACxB;YACE,MAAM,IAAI,KAAK,CAAC,sBAAsB,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IAC3D,CAAC;AACH,CAAC"}
|