@deadair/plugin-sdk 0.32.1 → 0.33.1
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 +52 -0
- package/dist/boundary.json.safe.d.ts +6 -4
- package/dist/boundary.json.safe.d.ts.map +1 -1
- package/dist/capabilities/messaging.d.ts +32 -2
- package/dist/capabilities/messaging.d.ts.map +1 -1
- package/dist/capabilities/speech.d.ts +23 -0
- package/dist/capabilities/speech.d.ts.map +1 -1
- package/dist/capabilities/weather.d.ts +8 -0
- package/dist/capabilities/weather.d.ts.map +1 -1
- package/dist/html.text.d.ts.map +1 -1
- package/dist/index.js +5 -1
- package/dist/index.js.map +1 -1
- package/dist/plugin.host.d.ts +56 -0
- package/dist/plugin.host.d.ts.map +1 -1
- package/dist/plugin.manifest.d.ts +1 -0
- package/dist/plugin.manifest.d.ts.map +1 -1
- package/dist/plugin.permissions.d.ts +10 -0
- package/dist/plugin.permissions.d.ts.map +1 -1
- package/dist/testing/fake.plugin.host.d.ts +26 -1
- package/dist/testing/fake.plugin.host.d.ts.map +1 -1
- package/dist/testing/index.js +49 -0
- package/dist/testing/index.js.map +1 -1
- package/package.json +1 -1
package/dist/plugin.host.d.ts
CHANGED
|
@@ -43,6 +43,45 @@ export interface HostFetchInit {
|
|
|
43
43
|
*/
|
|
44
44
|
signal?: AbortSignal;
|
|
45
45
|
}
|
|
46
|
+
/** What {@link PluginHost.socket} may be told. */
|
|
47
|
+
export interface PluginSocketOptions {
|
|
48
|
+
/**
|
|
49
|
+
* How long to wait for the socket to open, in ms. Clamped to the host's own
|
|
50
|
+
* ceiling; asking for more buys nothing.
|
|
51
|
+
*/
|
|
52
|
+
connectTimeoutMs?: number;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* An open outbound WebSocket, from {@link PluginHost.socket}.
|
|
56
|
+
*
|
|
57
|
+
* Text frames only, and the shape `@maroonedsoftware/slack/socketmode` and
|
|
58
|
+
* `@maroonedsoftware/discord/gateway` call `SocketLike`, so either client takes
|
|
59
|
+
* one as it is. There is no `onOpen`: the promise that handed it over resolved
|
|
60
|
+
* when it opened.
|
|
61
|
+
*/
|
|
62
|
+
export interface PluginSocket {
|
|
63
|
+
/**
|
|
64
|
+
* Send one text frame.
|
|
65
|
+
*
|
|
66
|
+
* Does nothing once the socket has closed, rather than throwing: a
|
|
67
|
+
* heartbeat timer racing a close would otherwise throw from a timer, where
|
|
68
|
+
* nothing can catch it.
|
|
69
|
+
*/
|
|
70
|
+
send(text: string): void;
|
|
71
|
+
/**
|
|
72
|
+
* Close it. `code` must be 1000 or in 3000–4999, the only ones a client may
|
|
73
|
+
* send; anything else closes with 1000.
|
|
74
|
+
*/
|
|
75
|
+
close(code?: number, reason?: string): void;
|
|
76
|
+
/** Called with every inbound text frame. A listener that throws is logged, never propagated. */
|
|
77
|
+
onMessage(listener: (text: string) => void): void;
|
|
78
|
+
/**
|
|
79
|
+
* Called once when the socket closes, for any reason but one: a socket the
|
|
80
|
+
* host closes because the plugin is being disposed tells nobody, since there
|
|
81
|
+
* is nobody left to reconnect.
|
|
82
|
+
*/
|
|
83
|
+
onClose(listener: (code?: number, reason?: string) => void): void;
|
|
84
|
+
}
|
|
46
85
|
/**
|
|
47
86
|
* Namespaced key/value store, private to this plugin. Values must be
|
|
48
87
|
* JSON-serialisable. Requires the `storage` permission.
|
|
@@ -205,6 +244,23 @@ export interface PluginHost {
|
|
|
205
244
|
* actually sent.
|
|
206
245
|
*/
|
|
207
246
|
fetch(url: string, init?: HostFetchInit): Promise<Response>;
|
|
247
|
+
/**
|
|
248
|
+
* Open an outbound WebSocket, for a platform that delivers over one (Slack
|
|
249
|
+
* Socket Mode, the Discord Gateway). Requires the `sockets` permission.
|
|
250
|
+
*
|
|
251
|
+
* The same policy as {@link fetch}: `wss:` only, the hostname must be in
|
|
252
|
+
* `permissions.network`, a host reached through `network.open` must not
|
|
253
|
+
* resolve to a private address, and the connect costs one token from the
|
|
254
|
+
* matching rate bucket. Resolves once the socket is open; rejects with a
|
|
255
|
+
* `PluginError` when it is refused or never opens.
|
|
256
|
+
*
|
|
257
|
+
* NOT tied to the call that opened it: a socket is meant to outlive it. The
|
|
258
|
+
* host bounds it instead: a frame over its size limit closes it, a plugin
|
|
259
|
+
* may hold only a few at once, and disposing the plugin closes every one it
|
|
260
|
+
* still has. Close your own in a `register` disposer all the same; the host's
|
|
261
|
+
* is the backstop, not the plan.
|
|
262
|
+
*/
|
|
263
|
+
socket(url: string, options?: PluginSocketOptions): Promise<PluginSocket>;
|
|
208
264
|
/**
|
|
209
265
|
* Aborts when the host gives up on the call you are currently inside.
|
|
210
266
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.host.d.ts","sourceRoot":"","sources":["../src/plugin.host.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"plugin.host.d.ts","sourceRoot":"","sources":["../src/plugin.host.ts"],"names":[],"mappings":"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"}
|
|
@@ -254,6 +254,7 @@ export declare const pluginManifestSchema: z.ZodObject<{
|
|
|
254
254
|
storage: z.ZodBoolean;
|
|
255
255
|
oauth: z.ZodBoolean;
|
|
256
256
|
trackFetcher: z.ZodOptional<z.ZodBoolean>;
|
|
257
|
+
sockets: z.ZodOptional<z.ZodBoolean>;
|
|
257
258
|
grants: z.ZodOptional<z.ZodArray<z.ZodType<import("./plugin.permissions.js").PluginGrantRequest, unknown, z.core.$ZodTypeInternals<import("./plugin.permissions.js").PluginGrantRequest, unknown>>>>;
|
|
258
259
|
}, z.core.$strip>;
|
|
259
260
|
configFields: z.ZodArray<z.ZodObject<{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.manifest.d.ts","sourceRoot":"","sources":["../src/plugin.manifest.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAqB,KAAK,WAAW,EAAE,MAAM,2BAA2B,CAAC;AAChF,OAAO,EAA2B,KAAK,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAE1F;;;;;;;;GAQG;AACH,eAAO,MAAM,yBAAyB,YAAY,CAAC;AAEnD;;;;;GAKG;AACH,eAAO,MAAM,wBAAwB,WAAW,CAAC;AAEjD;;;;GAIG;AACH,eAAO,MAAM,uBAAuB,UAAU,CAAC;AAC/C,eAAO,MAAM,uBAAuB,UAAU,CAAC;AAC/C,eAAO,MAAM,4BAA4B,eAAe,CAAC;AAEzD,iEAAiE;AACjE,eAAO,MAAM,wBAAwB,WAAW,CAAC;AAEjD;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,QAAQ,CAAC;AAE3C;;;;;;;GAOG;AACH,eAAO,MAAM,0BAA0B,aAAa,CAAC;AAErD;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,uBAAuB,UAAU,CAAC;AAE/C;;;;;;;;;GASG;AACH,eAAO,MAAM,wBAAwB,WAAW,CAAC;AAEjD;;;;;;;;GAQG;AACH,eAAO,MAAM,sBAAsB,SAAS,CAAC;AAE7C;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,2BAA2B,cAAc,CAAC;AAEvD;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,yBAAyB,YAAY,CAAC;AAEnD;;;;;;;GAOG;AACH,eAAO,MAAM,4BAA4B,eAAe,CAAC;AAEzD;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,wBAAwB,WAAW,CAAC;AAEjD;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,yBAAyB,YAAY,CAAC;AAEnD;;;;;;;GAOG;AACH;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,yBAAyB,YAAY,CAAC;AAEnD,eAAO,MAAM,0BAA0B,aAAa,CAAC;AAErD;;;;GAIG;AACH,eAAO,MAAM,2BAA2B,cAAc,CAAC;AAEvD,eAAO,MAAM,yBAAyB,uNAoB5B,CAAC;AAEX,MAAM,MAAM,qBAAqB,GAAG,CAAC,OAAO,yBAAyB,CAAC,CAAC,MAAM,CAAC,CAAC;AAE/E;;;;GAIG;AACH,MAAM,MAAM,gBAAgB,GAAG,qBAAqB,GAAG,CAAC,MAAM,GAAG,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC;AAEvF;;;GAGG;AACH,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,OAAO,CAAC;AAE3C;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,kBAAkB,CAKvE;AAED;;;;GAIG;AACH,MAAM,WAAW,cAAc;IAC3B,+FAA+F;IAC/F,EAAE,EAAE,MAAM,CAAC;IAEX,oCAAoC;IACpC,IAAI,EAAE,MAAM,CAAC;IAEb,2CAA2C;IAC3C,OAAO,EAAE,MAAM,CAAC;IAEhB,0EAA0E;IAC1E,YAAY,EAAE,gBAAgB,EAAE,CAAC;IAEjC;;;OAGG;IACH,UAAU,EAAE,MAAM,CAAC;IAEnB,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB,6DAA6D;IAC7D,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd,WAAW,EAAE,iBAAiB,CAAC;IAE/B,iCAAiC;IACjC,YAAY,EAAE,WAAW,EAAE,CAAC;IAE5B;;;;OAIG;IACH,YAAY,EAAE,kBAAkB,CAAC;CACpC;AAKD;;;GAGG;AACH,eAAO,MAAM,oBAAoB
|
|
1
|
+
{"version":3,"file":"plugin.manifest.d.ts","sourceRoot":"","sources":["../src/plugin.manifest.ts"],"names":[],"mappings":"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"}
|
|
@@ -160,6 +160,15 @@ export interface PluginPermissions {
|
|
|
160
160
|
* only one provider has ever needed.
|
|
161
161
|
*/
|
|
162
162
|
trackFetcher?: boolean;
|
|
163
|
+
/**
|
|
164
|
+
* Whether the plugin may use `host.socket` (an outbound WebSocket, held to
|
|
165
|
+
* the same {@link network} allowlist as `host.fetch`).
|
|
166
|
+
*
|
|
167
|
+
* Optional for `trackFetcher`'s reason: only a plugin whose platform delivers
|
|
168
|
+
* over a socket wants one. A disclosure rather than a grant, because the
|
|
169
|
+
* hosts it may reach are already the ones listed above.
|
|
170
|
+
*/
|
|
171
|
+
sockets?: boolean;
|
|
163
172
|
/**
|
|
164
173
|
* Capabilities this plugin is ASKING for, each with the reason an operator
|
|
165
174
|
* reads before deciding. See {@link PluginGrantRequest}.
|
|
@@ -180,6 +189,7 @@ export declare const pluginPermissionsSchema: z.ZodObject<{
|
|
|
180
189
|
storage: z.ZodBoolean;
|
|
181
190
|
oauth: z.ZodBoolean;
|
|
182
191
|
trackFetcher: z.ZodOptional<z.ZodBoolean>;
|
|
192
|
+
sockets: z.ZodOptional<z.ZodBoolean>;
|
|
183
193
|
grants: z.ZodOptional<z.ZodArray<z.ZodType<PluginGrantRequest, unknown, z.core.$ZodTypeInternals<PluginGrantRequest, unknown>>>>;
|
|
184
194
|
}, z.core.$strip>;
|
|
185
195
|
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.permissions.d.ts","sourceRoot":"","sources":["../src/plugin.permissions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,uDAAuD;AACvD,UAAU,uBAAuB;IAC7B;;;;;;;;;OASG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB;;;;;;;;;OASG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,qBAAsB,SAAQ,uBAAuB;IAClE;;;;OAIG;IACH,IAAI,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,MAAM,WAAW,2BAA4B,SAAQ,uBAAuB;IACxE,mEAAmE;IACnE,UAAU,EAAE,MAAM,CAAC;CACtB;AAED,yEAAyE;AACzE,MAAM,MAAM,iBAAiB,GAAG,MAAM,GAAG,qBAAqB,GAAG,2BAA2B,CAAC;AAE7F;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,MAAM,WAAW,kBAAmB,SAAQ,uBAAuB;IAC/D,+DAA+D;IAC/D,UAAU,EAAE,MAAM,CAAC;IACnB;;;;;;;;OAQG;IACH,MAAM,EAAE,MAAM,CAAC;CAClB;AAED;;;;GAIG;AACH,MAAM,WAAW,iBAAiB;IAC9B;;;;;;;;;;;;;;;OAeG;IACH,OAAO,EAAE,iBAAiB,EAAE,CAAC;IAE7B,8EAA8E;IAC9E,OAAO,EAAE,OAAO,CAAC;IAEjB,4EAA4E;IAC5E,KAAK,EAAE,OAAO,CAAC;IAEf;;;;;;;;OAQG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IAEvB;;;;;;;;;;OAUG;IACH,MAAM,CAAC,EAAE,kBAAkB,EAAE,CAAC;CACjC;AASD,eAAO,MAAM,uBAAuB,EAAE,CAAC,CAAC,OAAO,CAAC,iBAAiB,CAI/D,CAAC;AAEH,eAAO,MAAM,kBAAkB,EAAE,CAAC,CAAC,OAAO,CAAC,kBAAkB,CAO3D,CAAC;AAEH,eAAO,MAAM,uBAAuB
|
|
1
|
+
{"version":3,"file":"plugin.permissions.d.ts","sourceRoot":"","sources":["../src/plugin.permissions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,uDAAuD;AACvD,UAAU,uBAAuB;IAC7B;;;;;;;;;OASG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB;;;;;;;;;OASG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,qBAAsB,SAAQ,uBAAuB;IAClE;;;;OAIG;IACH,IAAI,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,MAAM,WAAW,2BAA4B,SAAQ,uBAAuB;IACxE,mEAAmE;IACnE,UAAU,EAAE,MAAM,CAAC;CACtB;AAED,yEAAyE;AACzE,MAAM,MAAM,iBAAiB,GAAG,MAAM,GAAG,qBAAqB,GAAG,2BAA2B,CAAC;AAE7F;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,MAAM,WAAW,kBAAmB,SAAQ,uBAAuB;IAC/D,+DAA+D;IAC/D,UAAU,EAAE,MAAM,CAAC;IACnB;;;;;;;;OAQG;IACH,MAAM,EAAE,MAAM,CAAC;CAClB;AAED;;;;GAIG;AACH,MAAM,WAAW,iBAAiB;IAC9B;;;;;;;;;;;;;;;OAeG;IACH,OAAO,EAAE,iBAAiB,EAAE,CAAC;IAE7B,8EAA8E;IAC9E,OAAO,EAAE,OAAO,CAAC;IAEjB,4EAA4E;IAC5E,KAAK,EAAE,OAAO,CAAC;IAEf;;;;;;;;OAQG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IAEvB;;;;;;;OAOG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAElB;;;;;;;;;;OAUG;IACH,MAAM,CAAC,EAAE,kBAAkB,EAAE,CAAC;CACjC;AASD,eAAO,MAAM,uBAAuB,EAAE,CAAC,CAAC,OAAO,CAAC,iBAAiB,CAI/D,CAAC;AAEH,eAAO,MAAM,kBAAkB,EAAE,CAAC,CAAC,OAAO,CAAC,kBAAkB,CAO3D,CAAC;AAEH,eAAO,MAAM,uBAAuB;;;;;;;iBAOlC,CAAC"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { HostFetchInit, HostFetchMethod, PluginHost, ProviderStream, ProviderTrack } from '../index.js';
|
|
1
|
+
import type { HostFetchInit, HostFetchMethod, PluginHost, PluginSocket, ProviderStream, ProviderTrack } from '../index.js';
|
|
2
2
|
/**
|
|
3
3
|
* Shipped from the SDK rather than copied into each plugin, which three of them
|
|
4
4
|
* were doing — two of those carrying a comment saying the copy was deliberate
|
|
@@ -21,6 +21,25 @@ export interface RecordedFetchCall {
|
|
|
21
21
|
headers: Record<string, string> | undefined;
|
|
22
22
|
body: string | undefined;
|
|
23
23
|
}
|
|
24
|
+
/**
|
|
25
|
+
* One `host.socket` the plugin opened, driven from the test: frames the plugin sent are in `sent`,
|
|
26
|
+
* and `receive` / `closeFromServer` play the far end.
|
|
27
|
+
*/
|
|
28
|
+
export interface FakePluginSocket extends PluginSocket {
|
|
29
|
+
readonly url: string;
|
|
30
|
+
/** Every text frame the plugin sent, in order. */
|
|
31
|
+
readonly sent: string[];
|
|
32
|
+
/** Whether either end has closed it. */
|
|
33
|
+
readonly closed: boolean;
|
|
34
|
+
/** The frames the plugin sent, each parsed as JSON. */
|
|
35
|
+
sentJson(): unknown[];
|
|
36
|
+
/** Deliver one text frame to the plugin's listeners. */
|
|
37
|
+
receive(text: string): void;
|
|
38
|
+
/** Deliver `JSON.stringify(value)`. */
|
|
39
|
+
receiveJson(value: unknown): void;
|
|
40
|
+
/** Close it from the far end, telling the plugin's close listeners. */
|
|
41
|
+
closeFromServer(code?: number, reason?: string): void;
|
|
42
|
+
}
|
|
24
43
|
/**
|
|
25
44
|
* A `PluginHost` for tests. `fetch` replays a queue of scripted
|
|
26
45
|
* `Response`s (FIFO, one per call) — or a custom handler installed
|
|
@@ -61,6 +80,10 @@ export interface FakePluginHost extends PluginHost {
|
|
|
61
80
|
* failed and is a different case for a plugin to handle.
|
|
62
81
|
*/
|
|
63
82
|
seedFetchedPlaylist(tracks: ProviderTrack[] | Error | undefined): void;
|
|
83
|
+
/** Every socket `host.socket` opened, in order, closed ones included. */
|
|
84
|
+
readonly sockets: FakePluginSocket[];
|
|
85
|
+
/** Make every later `host.socket` reject with `error`; `undefined` lets them open again. */
|
|
86
|
+
refuseSockets(error: Error | undefined): void;
|
|
64
87
|
}
|
|
65
88
|
/**
|
|
66
89
|
* What a test says a scripted reply should be.
|
|
@@ -87,5 +110,7 @@ export interface FakeResponseInit {
|
|
|
87
110
|
}
|
|
88
111
|
/** Builds a default 200 `Response`, overridable field by field. */
|
|
89
112
|
export declare function fakeHostFetchResponse(init?: FakeResponseInit): Response;
|
|
113
|
+
/** A socket that is open until either end closes it. Exported for a test that wants one on its own. */
|
|
114
|
+
export declare function createFakePluginSocket(url: string): FakePluginSocket;
|
|
90
115
|
export declare function createFakePluginHost(): FakePluginHost;
|
|
91
116
|
//# sourceMappingURL=fake.plugin.host.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fake.plugin.host.d.ts","sourceRoot":"","sources":["../../src/testing/fake.plugin.host.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,eAAe,EAAE,UAAU,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"fake.plugin.host.d.ts","sourceRoot":"","sources":["../../src/testing/fake.plugin.host.ts"],"names":[],"mappings":"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"}
|
package/dist/testing/index.js
CHANGED
|
@@ -26,6 +26,42 @@ function fakeHostFetchResponse(init = {}) {
|
|
|
26
26
|
return response;
|
|
27
27
|
}
|
|
28
28
|
__name(fakeHostFetchResponse, "fakeHostFetchResponse");
|
|
29
|
+
function createFakePluginSocket(url) {
|
|
30
|
+
const messageListeners = [];
|
|
31
|
+
const closeListeners = [];
|
|
32
|
+
const sent = [];
|
|
33
|
+
let closed = false;
|
|
34
|
+
const shut = /* @__PURE__ */ __name((code, reason) => {
|
|
35
|
+
if (closed) return;
|
|
36
|
+
closed = true;
|
|
37
|
+
for (const listener of closeListeners) listener(code, reason);
|
|
38
|
+
}, "shut");
|
|
39
|
+
return {
|
|
40
|
+
url,
|
|
41
|
+
sent,
|
|
42
|
+
get closed() {
|
|
43
|
+
return closed;
|
|
44
|
+
},
|
|
45
|
+
send: vi.fn((text) => {
|
|
46
|
+
if (!closed) sent.push(text);
|
|
47
|
+
}),
|
|
48
|
+
close: vi.fn((code, reason) => shut(code ?? 1e3, reason)),
|
|
49
|
+
onMessage: /* @__PURE__ */ __name((listener) => void messageListeners.push(listener), "onMessage"),
|
|
50
|
+
onClose: /* @__PURE__ */ __name((listener) => void closeListeners.push(listener), "onClose"),
|
|
51
|
+
sentJson: /* @__PURE__ */ __name(() => sent.map((text) => JSON.parse(text)), "sentJson"),
|
|
52
|
+
receive(text) {
|
|
53
|
+
if (closed) throw new Error(`fake plugin socket: ${url} is closed`);
|
|
54
|
+
for (const listener of messageListeners) listener(text);
|
|
55
|
+
},
|
|
56
|
+
receiveJson(value) {
|
|
57
|
+
this.receive(JSON.stringify(value));
|
|
58
|
+
},
|
|
59
|
+
closeFromServer(code = 1e3, reason = "") {
|
|
60
|
+
shut(code, reason);
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
__name(createFakePluginSocket, "createFakePluginSocket");
|
|
29
65
|
function createFakePluginHost() {
|
|
30
66
|
const calls = [];
|
|
31
67
|
const queue = [];
|
|
@@ -40,6 +76,8 @@ function createFakePluginHost() {
|
|
|
40
76
|
expiresAt: 1893456e6
|
|
41
77
|
};
|
|
42
78
|
let fetchedPlaylist;
|
|
79
|
+
const sockets = [];
|
|
80
|
+
let socketRefusal;
|
|
43
81
|
const fetchImpl = vi.fn(async (url, init) => {
|
|
44
82
|
calls.push({
|
|
45
83
|
url,
|
|
@@ -60,6 +98,12 @@ function createFakePluginHost() {
|
|
|
60
98
|
error: vi.fn()
|
|
61
99
|
},
|
|
62
100
|
fetch: fetchImpl,
|
|
101
|
+
socket: vi.fn(async (url) => {
|
|
102
|
+
if (socketRefusal !== void 0) throw socketRefusal;
|
|
103
|
+
const socket = createFakePluginSocket(url);
|
|
104
|
+
sockets.push(socket);
|
|
105
|
+
return socket;
|
|
106
|
+
}),
|
|
63
107
|
// Never aborts: these tests are about what a plugin does with a reply,
|
|
64
108
|
// not about being cancelled half way through one.
|
|
65
109
|
signal: new AbortController().signal,
|
|
@@ -100,6 +144,10 @@ function createFakePluginHost() {
|
|
|
100
144
|
})
|
|
101
145
|
},
|
|
102
146
|
calls,
|
|
147
|
+
sockets,
|
|
148
|
+
refuseSockets(error) {
|
|
149
|
+
socketRefusal = error;
|
|
150
|
+
},
|
|
103
151
|
seedRemainingMs(ms) {
|
|
104
152
|
remainingMs = ms;
|
|
105
153
|
},
|
|
@@ -144,6 +192,7 @@ function createFakePluginHost() {
|
|
|
144
192
|
__name(createFakePluginHost, "createFakePluginHost");
|
|
145
193
|
export {
|
|
146
194
|
createFakePluginHost,
|
|
195
|
+
createFakePluginSocket,
|
|
147
196
|
fakeHostFetchResponse
|
|
148
197
|
};
|
|
149
198
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/testing/fake.plugin.host.ts"],"sourcesContent":["import { vi } from 'vitest';\n\nimport type { HostFetchInit, HostFetchMethod, PluginHost, ProviderStream, ProviderTrack } from '../index.js';\n\n/**\n * Shipped from the SDK rather than copied into each plugin, which three of them\n * were doing — two of those carrying a comment saying the copy was deliberate\n * because plugins do not depend on each other.\n *\n * That reasoning held for the copies and does not hold for this: every plugin\n * already depends on the SDK, and `PluginHost` is the SDK's own contract, so its\n * test double belongs beside the interface it doubles. A plugin importing this\n * gains no dependency on any other plugin. It also means a host method added to\n * the contract turns up in one fake rather than being added to three and\n * forgotten in the other three.\n *\n * Reached as `@deadair/plugin-sdk/testing`, which is kept out of the runtime\n * entry so nothing ships a dependency on vitest.\n */\n\n/** One `host.fetch` call, recorded with only the fields tests care about. */\nexport interface RecordedFetchCall {\n url: string;\n method: HostFetchMethod | undefined;\n headers: Record<string, string> | undefined;\n body: string | undefined;\n}\n\n/**\n * A `PluginHost` for tests. `fetch` replays a queue of scripted\n * `Response`s (FIFO, one per call) — or a custom handler installed\n * with `setFetchImpl`, for call-count-driven scenarios like a 401-then-200\n * retry — and records every call it received in `calls`. `storage`, `config`,\n * `secrets` and the `oauth` vault are in-memory and can be seeded or read\n * directly, bypassing the `PluginHost` methods, so a test can assert on state\n * without going through the code under test twice.\n */\nexport interface FakePluginHost extends PluginHost {\n /** Every `host.fetch` call so far, in call order. */\n readonly calls: RecordedFetchCall[];\n /** Set what `host.remainingMs()` reports, for testing budget shedding. */\n seedRemainingMs(ms: number): void;\n /** Push one scripted response onto the back of the reply queue. */\n queueResponse(response: FakeResponseInit): void;\n /** Replace the fetch handler outright; overrides the queue while set. */\n setFetchImpl(impl: (url: string, init?: HostFetchInit) => Promise<Response>): void;\n /** Seed the value `host.config.get()` resolves to. */\n seedConfig(config: Record<string, unknown>): void;\n /** Seed a value `host.secrets.get(key)` resolves to. */\n seedSecret(key: string, value: string): void;\n /** Seed a storage entry directly, bypassing `host.storage.set`. */\n seedStorage(key: string, value: unknown): void;\n /** Read a storage entry directly, bypassing `host.storage.get`. */\n getStorageEntry(key: string): unknown;\n /** All storage keys currently set, unfiltered. */\n storageKeys(): string[];\n /** Seed the oauth vault directly, bypassing `host.oauth.saveTokens`. */\n seedTokens(tokens: Record<string, string>): void;\n /** Read the oauth vault directly, bypassing `host.oauth.getTokens`. */\n getVaultTokens(): Record<string, string> | undefined;\n /** Set what `host.trackFetcher.serve()` answers with; `undefined` means \"this station has no fetcher\". */\n seedFetchedTrack(stream: ProviderStream | undefined): void;\n /**\n * Set what `host.trackFetcher.playlistTracks()` answers with. `undefined` means \"this station\n * has no fetcher\"; pass an `Error` to make it throw, which is the fetcher having tried and\n * failed and is a different case for a plugin to handle.\n */\n seedFetchedPlaylist(tracks: ProviderTrack[] | Error | undefined): void;\n}\n\n/**\n * What `host.remainingMs()` reports unless a test says otherwise. Matches the\n * host's default per-call deadline, so a plugin that only sheds work when the\n * budget is tight behaves in tests the way it does on a healthy station.\n */\nconst DEFAULT_REMAINING_MS = 15_000;\n\n/**\n * What a test says a scripted reply should be.\n *\n * Not `ResponseInit`, because `url` is not on it: `host.fetch` reports the last\n * hop of the redirect chain there, and a `Response` built by hand has an empty\n * one unless it is defined in.\n */\nexport interface FakeResponseInit {\n status?: number;\n statusText?: string;\n headers?: Record<string, string>;\n /**\n * Text for a JSON or error reply, bytes for a binary one.\n *\n * `Uint8Array<ArrayBuffer>` rather than a bare `Uint8Array`: the DOM lib's\n * `BodyInit` accepts a view over a real `ArrayBuffer`, and the unparameterized\n * form widens to `ArrayBufferLike`, which includes `SharedArrayBuffer` and is\n * not something `Response` will take. `new Uint8Array([...])` already has the\n * narrow type, so no test has to say so.\n */\n body?: string | Uint8Array<ArrayBuffer>;\n url?: string;\n}\n\n/** Statuses whose `Response` must carry a null body, or the constructor throws. */\nconst NULL_BODY_STATUSES = new Set([101, 204, 205, 304]);\n\n/** Builds a default 200 `Response`, overridable field by field. */\nexport function fakeHostFetchResponse(init: FakeResponseInit = {}): Response {\n const status = init.status ?? 200;\n\n const response = new Response(NULL_BODY_STATUSES.has(status) ? null : (init.body ?? ''), {\n status,\n statusText: init.statusText ?? 'OK',\n headers: init.headers ?? { 'content-type': 'application/json' },\n });\n\n // Read-only on a constructed response, and part of what the host promises.\n Object.defineProperty(response, 'url', { value: init.url ?? 'https://example.test/' });\n\n return response;\n}\n\nexport function createFakePluginHost(): FakePluginHost {\n const calls: RecordedFetchCall[] = [];\n const queue: Response[] = [];\n let customImpl: ((url: string, init?: HostFetchInit) => Promise<Response>) | undefined;\n\n const storageData = new Map<string, unknown>();\n let configData: Record<string, unknown> = {};\n const secretsData = new Map<string, string>();\n let tokens: Record<string, string> | undefined;\n let remainingMs = DEFAULT_REMAINING_MS;\n let fetchedTrack: ProviderStream | undefined = { url: 'http://127.0.0.1:3679/track/song-1?t=signed', expiresAt: 1_893_456_000_000 };\n // Nothing by default: a plugin should ask its own API first, and a test that means to exercise\n // the fetcher says so.\n let fetchedPlaylist: ProviderTrack[] | Error | undefined;\n\n const fetchImpl = vi.fn(async (url: string, init?: HostFetchInit): Promise<Response> => {\n calls.push({ url, method: init?.method, headers: init?.headers, body: init?.body });\n if (customImpl) return customImpl(url, init);\n const next = queue.shift();\n if (!next) throw new Error(`fake plugin host: no response queued for ${init?.method ?? 'GET'} ${url}`);\n return next;\n });\n\n const host: FakePluginHost = {\n logger: { debug: vi.fn(), info: vi.fn(), warn: vi.fn(), error: vi.fn() },\n fetch: fetchImpl,\n // Never aborts: these tests are about what a plugin does with a reply,\n // not about being cancelled half way through one.\n signal: new AbortController().signal,\n remainingMs: vi.fn(() => remainingMs),\n storage: {\n get: vi.fn(async (key: string) => storageData.get(key)),\n set: vi.fn(async (key: string, value: unknown) => {\n storageData.set(key, value);\n }),\n delete: vi.fn(async (key: string) => {\n storageData.delete(key);\n }),\n list: vi.fn(async (prefix?: string) => [...storageData.keys()].filter(key => !prefix || key.startsWith(prefix))),\n },\n secrets: { get: vi.fn(async (key: string) => secretsData.get(key)) },\n config: { get: vi.fn(async () => configData) },\n oauth: {\n getRedirectUri: vi.fn(async () => 'https://example.test/callback'),\n saveTokens: vi.fn(async (next: Record<string, string>) => {\n tokens = next;\n }),\n getTokens: vi.fn(async () => tokens),\n },\n events: { emit: vi.fn() },\n trackFetcher: {\n serve: vi.fn(async () => fetchedTrack),\n playlistTracks: vi.fn(async () => {\n if (fetchedPlaylist instanceof Error) throw fetchedPlaylist;\n return fetchedPlaylist;\n }),\n },\n calls,\n seedRemainingMs(ms) {\n remainingMs = ms;\n },\n queueResponse(response: FakeResponseInit) {\n queue.push(fakeHostFetchResponse(response));\n },\n setFetchImpl(impl) {\n customImpl = impl;\n },\n seedConfig(config) {\n configData = config;\n },\n seedSecret(key, value) {\n secretsData.set(key, value);\n },\n seedStorage(key, value) {\n storageData.set(key, value);\n },\n getStorageEntry(key) {\n return storageData.get(key);\n },\n storageKeys() {\n return [...storageData.keys()];\n },\n seedTokens(next) {\n tokens = next;\n },\n getVaultTokens() {\n return tokens;\n },\n seedFetchedTrack(stream) {\n fetchedTrack = stream;\n },\n seedFetchedPlaylist(tracks) {\n fetchedPlaylist = tracks;\n },\n };\n\n return host;\n}\n"],"mappings":"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","names":["vi","DEFAULT_REMAINING_MS","NULL_BODY_STATUSES","Set","fakeHostFetchResponse","init","status","response","Response","has","body","statusText","headers","Object","defineProperty","value","url","createFakePluginHost","calls","queue","customImpl","storageData","Map","configData","secretsData","tokens","remainingMs","fetchedTrack","expiresAt","fetchedPlaylist","fetchImpl","vi","fn","push","method","next","shift","Error","host","logger","debug","info","warn","error","fetch","signal","AbortController","storage","get","key","set","delete","list","prefix","keys","filter","startsWith","secrets","config","oauth","getRedirectUri","saveTokens","getTokens","events","emit","trackFetcher","serve","playlistTracks","seedRemainingMs","ms","queueResponse","setFetchImpl","impl","seedConfig","seedSecret","seedStorage","getStorageEntry","storageKeys","seedTokens","getVaultTokens","seedFetchedTrack","stream","seedFetchedPlaylist","tracks"]}
|
|
1
|
+
{"version":3,"sources":["../../src/testing/fake.plugin.host.ts"],"sourcesContent":["import { vi } from 'vitest';\n\nimport type { HostFetchInit, HostFetchMethod, PluginHost, PluginSocket, ProviderStream, ProviderTrack } from '../index.js';\n\n/**\n * Shipped from the SDK rather than copied into each plugin, which three of them\n * were doing — two of those carrying a comment saying the copy was deliberate\n * because plugins do not depend on each other.\n *\n * That reasoning held for the copies and does not hold for this: every plugin\n * already depends on the SDK, and `PluginHost` is the SDK's own contract, so its\n * test double belongs beside the interface it doubles. A plugin importing this\n * gains no dependency on any other plugin. It also means a host method added to\n * the contract turns up in one fake rather than being added to three and\n * forgotten in the other three.\n *\n * Reached as `@deadair/plugin-sdk/testing`, which is kept out of the runtime\n * entry so nothing ships a dependency on vitest.\n */\n\n/** One `host.fetch` call, recorded with only the fields tests care about. */\nexport interface RecordedFetchCall {\n url: string;\n method: HostFetchMethod | undefined;\n headers: Record<string, string> | undefined;\n body: string | undefined;\n}\n\n/**\n * One `host.socket` the plugin opened, driven from the test: frames the plugin sent are in `sent`,\n * and `receive` / `closeFromServer` play the far end.\n */\nexport interface FakePluginSocket extends PluginSocket {\n readonly url: string;\n /** Every text frame the plugin sent, in order. */\n readonly sent: string[];\n /** Whether either end has closed it. */\n readonly closed: boolean;\n /** The frames the plugin sent, each parsed as JSON. */\n sentJson(): unknown[];\n /** Deliver one text frame to the plugin's listeners. */\n receive(text: string): void;\n /** Deliver `JSON.stringify(value)`. */\n receiveJson(value: unknown): void;\n /** Close it from the far end, telling the plugin's close listeners. */\n closeFromServer(code?: number, reason?: string): void;\n}\n\n/**\n * A `PluginHost` for tests. `fetch` replays a queue of scripted\n * `Response`s (FIFO, one per call) — or a custom handler installed\n * with `setFetchImpl`, for call-count-driven scenarios like a 401-then-200\n * retry — and records every call it received in `calls`. `storage`, `config`,\n * `secrets` and the `oauth` vault are in-memory and can be seeded or read\n * directly, bypassing the `PluginHost` methods, so a test can assert on state\n * without going through the code under test twice.\n */\nexport interface FakePluginHost extends PluginHost {\n /** Every `host.fetch` call so far, in call order. */\n readonly calls: RecordedFetchCall[];\n /** Set what `host.remainingMs()` reports, for testing budget shedding. */\n seedRemainingMs(ms: number): void;\n /** Push one scripted response onto the back of the reply queue. */\n queueResponse(response: FakeResponseInit): void;\n /** Replace the fetch handler outright; overrides the queue while set. */\n setFetchImpl(impl: (url: string, init?: HostFetchInit) => Promise<Response>): void;\n /** Seed the value `host.config.get()` resolves to. */\n seedConfig(config: Record<string, unknown>): void;\n /** Seed a value `host.secrets.get(key)` resolves to. */\n seedSecret(key: string, value: string): void;\n /** Seed a storage entry directly, bypassing `host.storage.set`. */\n seedStorage(key: string, value: unknown): void;\n /** Read a storage entry directly, bypassing `host.storage.get`. */\n getStorageEntry(key: string): unknown;\n /** All storage keys currently set, unfiltered. */\n storageKeys(): string[];\n /** Seed the oauth vault directly, bypassing `host.oauth.saveTokens`. */\n seedTokens(tokens: Record<string, string>): void;\n /** Read the oauth vault directly, bypassing `host.oauth.getTokens`. */\n getVaultTokens(): Record<string, string> | undefined;\n /** Set what `host.trackFetcher.serve()` answers with; `undefined` means \"this station has no fetcher\". */\n seedFetchedTrack(stream: ProviderStream | undefined): void;\n /**\n * Set what `host.trackFetcher.playlistTracks()` answers with. `undefined` means \"this station\n * has no fetcher\"; pass an `Error` to make it throw, which is the fetcher having tried and\n * failed and is a different case for a plugin to handle.\n */\n seedFetchedPlaylist(tracks: ProviderTrack[] | Error | undefined): void;\n /** Every socket `host.socket` opened, in order, closed ones included. */\n readonly sockets: FakePluginSocket[];\n /** Make every later `host.socket` reject with `error`; `undefined` lets them open again. */\n refuseSockets(error: Error | undefined): void;\n}\n\n/**\n * What `host.remainingMs()` reports unless a test says otherwise. Matches the\n * host's default per-call deadline, so a plugin that only sheds work when the\n * budget is tight behaves in tests the way it does on a healthy station.\n */\nconst DEFAULT_REMAINING_MS = 15_000;\n\n/**\n * What a test says a scripted reply should be.\n *\n * Not `ResponseInit`, because `url` is not on it: `host.fetch` reports the last\n * hop of the redirect chain there, and a `Response` built by hand has an empty\n * one unless it is defined in.\n */\nexport interface FakeResponseInit {\n status?: number;\n statusText?: string;\n headers?: Record<string, string>;\n /**\n * Text for a JSON or error reply, bytes for a binary one.\n *\n * `Uint8Array<ArrayBuffer>` rather than a bare `Uint8Array`: the DOM lib's\n * `BodyInit` accepts a view over a real `ArrayBuffer`, and the unparameterized\n * form widens to `ArrayBufferLike`, which includes `SharedArrayBuffer` and is\n * not something `Response` will take. `new Uint8Array([...])` already has the\n * narrow type, so no test has to say so.\n */\n body?: string | Uint8Array<ArrayBuffer>;\n url?: string;\n}\n\n/** Statuses whose `Response` must carry a null body, or the constructor throws. */\nconst NULL_BODY_STATUSES = new Set([101, 204, 205, 304]);\n\n/** Builds a default 200 `Response`, overridable field by field. */\nexport function fakeHostFetchResponse(init: FakeResponseInit = {}): Response {\n const status = init.status ?? 200;\n\n const response = new Response(NULL_BODY_STATUSES.has(status) ? null : (init.body ?? ''), {\n status,\n statusText: init.statusText ?? 'OK',\n headers: init.headers ?? { 'content-type': 'application/json' },\n });\n\n // Read-only on a constructed response, and part of what the host promises.\n Object.defineProperty(response, 'url', { value: init.url ?? 'https://example.test/' });\n\n return response;\n}\n\n/** A socket that is open until either end closes it. Exported for a test that wants one on its own. */\nexport function createFakePluginSocket(url: string): FakePluginSocket {\n const messageListeners: ((text: string) => void)[] = [];\n const closeListeners: ((code?: number, reason?: string) => void)[] = [];\n const sent: string[] = [];\n let closed = false;\n\n const shut = (code: number | undefined, reason: string | undefined): void => {\n if (closed) return;\n closed = true;\n for (const listener of closeListeners) listener(code, reason);\n };\n\n return {\n url,\n sent,\n get closed() {\n return closed;\n },\n send: vi.fn((text: string) => {\n if (!closed) sent.push(text);\n }),\n close: vi.fn((code?: number, reason?: string) => shut(code ?? 1000, reason)),\n onMessage: listener => void messageListeners.push(listener),\n onClose: listener => void closeListeners.push(listener),\n sentJson: () => sent.map(text => JSON.parse(text) as unknown),\n receive(text) {\n if (closed) throw new Error(`fake plugin socket: ${url} is closed`);\n for (const listener of messageListeners) listener(text);\n },\n receiveJson(value) {\n this.receive(JSON.stringify(value));\n },\n closeFromServer(code = 1000, reason = '') {\n shut(code, reason);\n },\n };\n}\n\nexport function createFakePluginHost(): FakePluginHost {\n const calls: RecordedFetchCall[] = [];\n const queue: Response[] = [];\n let customImpl: ((url: string, init?: HostFetchInit) => Promise<Response>) | undefined;\n\n const storageData = new Map<string, unknown>();\n let configData: Record<string, unknown> = {};\n const secretsData = new Map<string, string>();\n let tokens: Record<string, string> | undefined;\n let remainingMs = DEFAULT_REMAINING_MS;\n let fetchedTrack: ProviderStream | undefined = { url: 'http://127.0.0.1:3679/track/song-1?t=signed', expiresAt: 1_893_456_000_000 };\n // Nothing by default: a plugin should ask its own API first, and a test that means to exercise\n // the fetcher says so.\n let fetchedPlaylist: ProviderTrack[] | Error | undefined;\n const sockets: FakePluginSocket[] = [];\n let socketRefusal: Error | undefined;\n\n const fetchImpl = vi.fn(async (url: string, init?: HostFetchInit): Promise<Response> => {\n calls.push({ url, method: init?.method, headers: init?.headers, body: init?.body });\n if (customImpl) return customImpl(url, init);\n const next = queue.shift();\n if (!next) throw new Error(`fake plugin host: no response queued for ${init?.method ?? 'GET'} ${url}`);\n return next;\n });\n\n const host: FakePluginHost = {\n logger: { debug: vi.fn(), info: vi.fn(), warn: vi.fn(), error: vi.fn() },\n fetch: fetchImpl,\n socket: vi.fn(async (url: string) => {\n if (socketRefusal !== undefined) throw socketRefusal;\n const socket = createFakePluginSocket(url);\n sockets.push(socket);\n return socket;\n }),\n // Never aborts: these tests are about what a plugin does with a reply,\n // not about being cancelled half way through one.\n signal: new AbortController().signal,\n remainingMs: vi.fn(() => remainingMs),\n storage: {\n get: vi.fn(async (key: string) => storageData.get(key)),\n set: vi.fn(async (key: string, value: unknown) => {\n storageData.set(key, value);\n }),\n delete: vi.fn(async (key: string) => {\n storageData.delete(key);\n }),\n list: vi.fn(async (prefix?: string) => [...storageData.keys()].filter(key => !prefix || key.startsWith(prefix))),\n },\n secrets: { get: vi.fn(async (key: string) => secretsData.get(key)) },\n config: { get: vi.fn(async () => configData) },\n oauth: {\n getRedirectUri: vi.fn(async () => 'https://example.test/callback'),\n saveTokens: vi.fn(async (next: Record<string, string>) => {\n tokens = next;\n }),\n getTokens: vi.fn(async () => tokens),\n },\n events: { emit: vi.fn() },\n trackFetcher: {\n serve: vi.fn(async () => fetchedTrack),\n playlistTracks: vi.fn(async () => {\n if (fetchedPlaylist instanceof Error) throw fetchedPlaylist;\n return fetchedPlaylist;\n }),\n },\n calls,\n sockets,\n refuseSockets(error) {\n socketRefusal = error;\n },\n seedRemainingMs(ms) {\n remainingMs = ms;\n },\n queueResponse(response: FakeResponseInit) {\n queue.push(fakeHostFetchResponse(response));\n },\n setFetchImpl(impl) {\n customImpl = impl;\n },\n seedConfig(config) {\n configData = config;\n },\n seedSecret(key, value) {\n secretsData.set(key, value);\n },\n seedStorage(key, value) {\n storageData.set(key, value);\n },\n getStorageEntry(key) {\n return storageData.get(key);\n },\n storageKeys() {\n return [...storageData.keys()];\n },\n seedTokens(next) {\n tokens = next;\n },\n getVaultTokens() {\n return tokens;\n },\n seedFetchedTrack(stream) {\n fetchedTrack = stream;\n },\n seedFetchedPlaylist(tracks) {\n fetchedPlaylist = tracks;\n },\n };\n\n return host;\n}\n"],"mappings":"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","names":["vi","DEFAULT_REMAINING_MS","NULL_BODY_STATUSES","Set","fakeHostFetchResponse","init","status","response","Response","has","body","statusText","headers","Object","defineProperty","value","url","createFakePluginSocket","messageListeners","closeListeners","sent","closed","shut","code","reason","listener","send","vi","fn","text","push","close","onMessage","onClose","sentJson","map","JSON","parse","receive","Error","receiveJson","stringify","closeFromServer","createFakePluginHost","calls","queue","customImpl","storageData","Map","configData","secretsData","tokens","remainingMs","fetchedTrack","expiresAt","fetchedPlaylist","sockets","socketRefusal","fetchImpl","method","next","shift","host","logger","debug","info","warn","error","fetch","socket","undefined","signal","AbortController","storage","get","key","set","delete","list","prefix","keys","filter","startsWith","secrets","config","oauth","getRedirectUri","saveTokens","getTokens","events","emit","trackFetcher","serve","playlistTracks","refuseSockets","seedRemainingMs","ms","queueResponse","setFetchImpl","impl","seedConfig","seedSecret","seedStorage","getStorageEntry","storageKeys","seedTokens","getVaultTokens","seedFetchedTrack","stream","seedFetchedPlaylist","tracks"]}
|
package/package.json
CHANGED