@skipruntime/helpers 0.0.5 → 0.0.7
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/dist/external.d.ts +34 -25
- package/dist/external.d.ts.map +1 -1
- package/dist/external.js +46 -11
- package/dist/external.js.map +1 -1
- package/dist/index.d.ts +8 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -1
- package/dist/index.js.map +1 -1
- package/dist/remote.d.ts +33 -0
- package/dist/remote.d.ts.map +1 -0
- package/dist/remote.js +88 -0
- package/dist/remote.js.map +1 -0
- package/dist/rest.d.ts +80 -40
- package/dist/rest.d.ts.map +1 -1
- package/dist/rest.js +70 -33
- package/dist/rest.js.map +1 -1
- package/package.json +3 -2
- package/src/external.ts +54 -19
- package/src/index.ts +9 -1
- package/src/remote.ts +109 -0
- package/src/rest.ts +100 -55
package/dist/rest.js
CHANGED
|
@@ -4,15 +4,30 @@ function toHttp(entrypoint) {
|
|
|
4
4
|
return `https://${entrypoint.host}:${entrypoint.control_port}`;
|
|
5
5
|
return `http://${entrypoint.host}:${entrypoint.control_port}`;
|
|
6
6
|
}
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
/**
|
|
8
|
+
* Perform an HTTP fetch where input and output data is `Json`.
|
|
9
|
+
*
|
|
10
|
+
* @typeParam V - Type response is *assumed* to have.
|
|
11
|
+
* @param url - URL from which to fetch.
|
|
12
|
+
* @param method - HTTP method of request.
|
|
13
|
+
* @param options - Optional parameters.
|
|
14
|
+
* @param options.headers - Additional headers to add to request.
|
|
15
|
+
* @param options.body - Data to convert to JSON and send in request body.
|
|
16
|
+
* @param options.timeout - Timeout for request, in milliseconds. Defaults to 1000ms.
|
|
17
|
+
* @returns Response parsed as JSON, and headers.
|
|
18
|
+
*/
|
|
19
|
+
export async function fetchJSON(url, method = "GET", options = {
|
|
20
|
+
headers: {},
|
|
21
|
+
timeout: 1000,
|
|
22
|
+
}) {
|
|
23
|
+
const body = options.body ? JSON.stringify(options.body) : undefined;
|
|
9
24
|
const response = await fetch(url, {
|
|
10
25
|
method,
|
|
11
26
|
body,
|
|
12
27
|
headers: {
|
|
13
28
|
"Content-Type": "application/json",
|
|
14
29
|
Accept: "application/json",
|
|
15
|
-
...headers,
|
|
30
|
+
...options.headers,
|
|
16
31
|
},
|
|
17
32
|
signal: AbortSignal.timeout(1000),
|
|
18
33
|
});
|
|
@@ -34,8 +49,9 @@ export async function fetchJSON(url, method = "GET", headers = {}, data) {
|
|
|
34
49
|
export class SkipServiceBroker {
|
|
35
50
|
/**
|
|
36
51
|
* Construct a broker for a Skip service at the given entry point.
|
|
37
|
-
*
|
|
38
|
-
* @
|
|
52
|
+
*
|
|
53
|
+
* @param entrypoint - Entry point of backing service.
|
|
54
|
+
* @returns Method-call broker to service.
|
|
39
55
|
*/
|
|
40
56
|
constructor(entrypoint = {
|
|
41
57
|
host: "localhost",
|
|
@@ -47,32 +63,40 @@ export class SkipServiceBroker {
|
|
|
47
63
|
/**
|
|
48
64
|
* Read the entire contents of a resource.
|
|
49
65
|
*
|
|
50
|
-
* @
|
|
51
|
-
* @
|
|
52
|
-
* @
|
|
66
|
+
* @typeParam K - Type of keys.
|
|
67
|
+
* @typeParam V - Type of values.
|
|
68
|
+
* @param resource - Name of resource, must be a key of the `resources` field of the `SkipService` running at `entrypoint`.
|
|
69
|
+
* @param params - Resource instance parameters.
|
|
70
|
+
* @returns All entries in resource.
|
|
53
71
|
*/
|
|
54
72
|
async getAll(resource, params) {
|
|
55
|
-
const [data, _headers] = await fetchJSON(`${this.entrypoint}/v1/snapshot`, "POST", {
|
|
73
|
+
const [data, _headers] = await fetchJSON(`${this.entrypoint}/v1/snapshot/${resource}`, "POST", { body: params });
|
|
56
74
|
return data ?? [];
|
|
57
75
|
}
|
|
58
76
|
/**
|
|
59
77
|
* Read the values a resource associates with a single key.
|
|
60
|
-
*
|
|
61
|
-
* @
|
|
62
|
-
* @
|
|
63
|
-
* @
|
|
78
|
+
*
|
|
79
|
+
* @typeParam K - Type of keys.
|
|
80
|
+
* @typeParam V - Type of values.
|
|
81
|
+
* @param resource - Name of resource, must be a key of the `resources` field of the `SkipService` running at `entrypoint`.
|
|
82
|
+
* @param params - Resource instance parameters.
|
|
83
|
+
* @param key - Key to read.
|
|
84
|
+
* @returns The values associated to the key.
|
|
64
85
|
*/
|
|
65
86
|
async getArray(resource, params, key) {
|
|
66
|
-
const [data, _headers] = await fetchJSON(`${this.entrypoint}/v1/snapshot`, "POST", {
|
|
87
|
+
const [data, _headers] = await fetchJSON(`${this.entrypoint}/v1/snapshot/${resource}/lookup`, "POST", { body: { key, params } });
|
|
67
88
|
return data ?? [];
|
|
68
89
|
}
|
|
69
90
|
/**
|
|
70
91
|
* Read the single value a resource associates with a key.
|
|
71
|
-
*
|
|
72
|
-
* @
|
|
73
|
-
* @
|
|
74
|
-
* @
|
|
75
|
-
* @
|
|
92
|
+
*
|
|
93
|
+
* @typeParam K - Type of keys.
|
|
94
|
+
* @typeParam V - Type of values.
|
|
95
|
+
* @param resource - Name of resource, must be a key of the `resources` field of the `SkipService` running at `entrypoint`.
|
|
96
|
+
* @param params - Resource instance parameters.
|
|
97
|
+
* @param key - Key to read.
|
|
98
|
+
* @returns The value associated to the key.
|
|
99
|
+
* @throws `NonUniqueValueException` when the key is associated to either zero or multiple values.
|
|
76
100
|
*/
|
|
77
101
|
async getUnique(resource, params, key) {
|
|
78
102
|
return this.getArray(resource, params, key).then((values) => {
|
|
@@ -83,9 +107,12 @@ export class SkipServiceBroker {
|
|
|
83
107
|
}
|
|
84
108
|
/**
|
|
85
109
|
* Write the values for a single key in a collection.
|
|
86
|
-
*
|
|
87
|
-
* @
|
|
88
|
-
* @
|
|
110
|
+
*
|
|
111
|
+
* @typeParam K - Type of keys.
|
|
112
|
+
* @typeParam V - Type of values.
|
|
113
|
+
* @param collection - Name of the input collection to update, must be a key of the `Inputs` type parameter of the `SkipService` running at `entrypoint`.
|
|
114
|
+
* @param key - Key of entry to write.
|
|
115
|
+
* @param values - Values of entry to write.
|
|
89
116
|
* @returns {void}
|
|
90
117
|
*/
|
|
91
118
|
async put(collection, key, values) {
|
|
@@ -93,17 +120,24 @@ export class SkipServiceBroker {
|
|
|
93
120
|
}
|
|
94
121
|
/**
|
|
95
122
|
* Write multiple entries to a collection.
|
|
96
|
-
*
|
|
97
|
-
* @
|
|
123
|
+
*
|
|
124
|
+
* @typeParam K - Type of keys.
|
|
125
|
+
* @typeParam V - Type of values.
|
|
126
|
+
* @param collection - Name of the input collection to update, must be a key of the `Inputs` type parameter of the `SkipService` running at `entrypoint`.
|
|
127
|
+
* @param entries - Entries to write.
|
|
98
128
|
* @returns {void}
|
|
99
129
|
*/
|
|
100
130
|
async patch(collection, entries) {
|
|
101
|
-
await fetchJSON(`${this.entrypoint}/v1/inputs/${collection}`, "PATCH", {
|
|
131
|
+
await fetchJSON(`${this.entrypoint}/v1/inputs/${collection}`, "PATCH", {
|
|
132
|
+
body: entries,
|
|
133
|
+
});
|
|
102
134
|
}
|
|
103
135
|
/**
|
|
104
136
|
* Remove all values associated with a key in a collection.
|
|
105
|
-
*
|
|
106
|
-
* @
|
|
137
|
+
*
|
|
138
|
+
* @typeParam K - Type of keys.
|
|
139
|
+
* @param collection - Name of the input collection to update, must be a key of the `Inputs` type parameter of the `SkipService` running at `entrypoint`.
|
|
140
|
+
* @param key - Key of entry to delete.
|
|
107
141
|
* @returns {void}
|
|
108
142
|
*/
|
|
109
143
|
async deleteKey(collection, key) {
|
|
@@ -111,15 +145,18 @@ export class SkipServiceBroker {
|
|
|
111
145
|
}
|
|
112
146
|
/**
|
|
113
147
|
* Create a resource instance UUID.
|
|
114
|
-
*
|
|
115
|
-
* @
|
|
116
|
-
* @
|
|
148
|
+
*
|
|
149
|
+
* @typeParam K - Type of keys.
|
|
150
|
+
* @typeParam V - Type of values.
|
|
151
|
+
* @param resource - Name of resource, must be a key of the `resources` field of the `SkipService` running at `entrypoint`.
|
|
152
|
+
* @param params - Resource instance parameters.
|
|
153
|
+
* @returns UUID that can be used to subscribe to updates to resource instance.
|
|
117
154
|
*/
|
|
118
155
|
async getStreamUUID(resource, params = {}) {
|
|
119
|
-
return fetch(`${this.entrypoint}/v1/streams`, {
|
|
156
|
+
return fetch(`${this.entrypoint}/v1/streams/${resource}`, {
|
|
120
157
|
method: "POST",
|
|
121
158
|
headers: { "Content-Type": "application/json" },
|
|
122
|
-
body: JSON.stringify(
|
|
159
|
+
body: JSON.stringify(params),
|
|
123
160
|
}).then((res) => res.text());
|
|
124
161
|
}
|
|
125
162
|
/**
|
|
@@ -127,7 +164,7 @@ export class SkipServiceBroker {
|
|
|
127
164
|
*
|
|
128
165
|
* Under normal circumstances, resource instances are deleted automatically after some period of inactivity; this method enables immediately deleting live streams under exceptional circumstances.
|
|
129
166
|
*
|
|
130
|
-
* @param uuid -
|
|
167
|
+
* @param uuid - Resource instance UUID.
|
|
131
168
|
* @returns {void}
|
|
132
169
|
*/
|
|
133
170
|
async deleteUUID(uuid) {
|
package/dist/rest.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rest.js","sourceRoot":"","sources":["../src/rest.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,uBAAuB,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"rest.js","sourceRoot":"","sources":["../src/rest.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@skipruntime/helpers",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.7",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": "./dist/index.js",
|
|
@@ -21,7 +21,8 @@
|
|
|
21
21
|
"dependencies": {
|
|
22
22
|
"eventsource": "^2.0.2",
|
|
23
23
|
"express": "^4.21.1",
|
|
24
|
-
"@skipruntime/
|
|
24
|
+
"@skipruntime/core": "^0.0.4",
|
|
25
|
+
"@skipruntime/api": "^0.0.5"
|
|
25
26
|
},
|
|
26
27
|
"devDependencies": {
|
|
27
28
|
"@types/eventsource": "^1.1.15"
|
package/src/external.ts
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import type { Entry, ExternalService, Json } from "@skipruntime/api";
|
|
2
2
|
import { fetchJSON } from "./rest.js";
|
|
3
3
|
|
|
4
|
+
/**
|
|
5
|
+
* Interface required by `GenericExternalService` for external resources.
|
|
6
|
+
*/
|
|
4
7
|
export interface ExternalResource {
|
|
5
8
|
open(
|
|
6
|
-
params:
|
|
9
|
+
params: Json,
|
|
7
10
|
callbacks: {
|
|
8
11
|
update: (updates: Entry<Json, Json>[], isInit: boolean) => void;
|
|
9
12
|
error: (error: Json) => void;
|
|
@@ -11,17 +14,25 @@ export interface ExternalResource {
|
|
|
11
14
|
},
|
|
12
15
|
): void;
|
|
13
16
|
|
|
14
|
-
close(params:
|
|
17
|
+
close(params: Json): void;
|
|
15
18
|
}
|
|
16
19
|
|
|
20
|
+
/**
|
|
21
|
+
* A generic external service providing external resources.
|
|
22
|
+
*
|
|
23
|
+
* `GenericExternalService` provides an implementation of `ExternalService` for external resources by lifting the `open` and `close` operations from `ExternalResource` to the `subscribe` and `unsubscribe` operations required by `ExternalService`.
|
|
24
|
+
*/
|
|
17
25
|
export class GenericExternalService implements ExternalService {
|
|
26
|
+
/**
|
|
27
|
+
* @param resources - Association of resource names to `ExternalResource`s.
|
|
28
|
+
*/
|
|
18
29
|
constructor(
|
|
19
30
|
private readonly resources: { [name: string]: ExternalResource },
|
|
20
31
|
) {}
|
|
21
32
|
|
|
22
33
|
subscribe(
|
|
23
34
|
resourceName: string,
|
|
24
|
-
params:
|
|
35
|
+
params: Json,
|
|
25
36
|
callbacks: {
|
|
26
37
|
update: (updates: Entry<Json, Json>[], isInit: boolean) => void;
|
|
27
38
|
error: (error: Json) => void;
|
|
@@ -37,7 +48,7 @@ export class GenericExternalService implements ExternalService {
|
|
|
37
48
|
resource.open(params, callbacks);
|
|
38
49
|
}
|
|
39
50
|
|
|
40
|
-
unsubscribe(resourceName: string, params:
|
|
51
|
+
unsubscribe(resourceName: string, params: Json) {
|
|
41
52
|
const resource = this.resources[resourceName] as
|
|
42
53
|
| ExternalResource
|
|
43
54
|
| undefined;
|
|
@@ -58,7 +69,7 @@ export class TimerResource implements ExternalResource {
|
|
|
58
69
|
private readonly intervals = new Map<string, { [name: string]: Timeout }>();
|
|
59
70
|
|
|
60
71
|
open(
|
|
61
|
-
params:
|
|
72
|
+
params: Json,
|
|
62
73
|
callbacks: {
|
|
63
74
|
update: (updates: Entry<Json, Json>[], isInit: boolean) => void;
|
|
64
75
|
error: (error: Json) => void;
|
|
@@ -85,7 +96,7 @@ export class TimerResource implements ExternalResource {
|
|
|
85
96
|
this.intervals.set(id, intervals);
|
|
86
97
|
}
|
|
87
98
|
|
|
88
|
-
close(params:
|
|
99
|
+
close(params: Json): void {
|
|
89
100
|
const intervals = this.intervals.get(toId(params));
|
|
90
101
|
if (intervals != null) {
|
|
91
102
|
for (const interval of Object.values(intervals)) {
|
|
@@ -95,19 +106,46 @@ export class TimerResource implements ExternalResource {
|
|
|
95
106
|
}
|
|
96
107
|
}
|
|
97
108
|
|
|
109
|
+
function defaultParamEncoder(params: Json): string {
|
|
110
|
+
if (typeof params == "object") {
|
|
111
|
+
const queryParams: { [param: string]: string } = {};
|
|
112
|
+
for (const [key, value] of Object.entries(params)) {
|
|
113
|
+
if (typeof value == "object") queryParams[key] = JSON.stringify(value);
|
|
114
|
+
else queryParams[key] = value.toString();
|
|
115
|
+
}
|
|
116
|
+
return new URLSearchParams(queryParams).toString();
|
|
117
|
+
} else return `params=${JSON.stringify(params)}`;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* An external resource that is refreshed at some polling interval.
|
|
122
|
+
*
|
|
123
|
+
* @typeParam S - Type of data received from external resource.
|
|
124
|
+
* @typeParam K - Type of keys.
|
|
125
|
+
* @typeParam V - Type of values.
|
|
126
|
+
*/
|
|
98
127
|
export class Polled<S extends Json, K extends Json, V extends Json>
|
|
99
128
|
implements ExternalResource
|
|
100
129
|
{
|
|
101
130
|
private readonly intervals = new Map<string, Timeout>();
|
|
102
131
|
|
|
132
|
+
/**
|
|
133
|
+
* @param url - HTTP endpoint of external resource to poll.
|
|
134
|
+
* @param duration - Refresh interval, in milliseconds.
|
|
135
|
+
* @param conv - Function to convert data of type `S` received from external resource to `key`-`value` entries.
|
|
136
|
+
* @param encodeParams - Function to use to encode params of type `Json` for external resource request.
|
|
137
|
+
*/
|
|
103
138
|
constructor(
|
|
104
139
|
private readonly url: string,
|
|
105
140
|
private readonly duration: number,
|
|
106
141
|
private readonly conv: (data: S) => Entry<K, V>[],
|
|
142
|
+
private readonly encodeParams: (
|
|
143
|
+
params: Json,
|
|
144
|
+
) => string = defaultParamEncoder,
|
|
107
145
|
) {}
|
|
108
146
|
|
|
109
147
|
open(
|
|
110
|
-
params:
|
|
148
|
+
params: Json,
|
|
111
149
|
callbacks: {
|
|
112
150
|
update: (updates: Entry<Json, Json>[], isInit: boolean) => void;
|
|
113
151
|
error: (error: Json) => void;
|
|
@@ -115,12 +153,7 @@ export class Polled<S extends Json, K extends Json, V extends Json>
|
|
|
115
153
|
},
|
|
116
154
|
): void {
|
|
117
155
|
this.close(params);
|
|
118
|
-
const
|
|
119
|
-
for (const [key, value] of Object.entries(params)) {
|
|
120
|
-
queryParams[key] = value.toString();
|
|
121
|
-
}
|
|
122
|
-
const strParams = new URLSearchParams(queryParams).toString();
|
|
123
|
-
const url = `${this.url}?${strParams}`;
|
|
156
|
+
const url = `${this.url}?${this.encodeParams(params)}`;
|
|
124
157
|
const call = () => {
|
|
125
158
|
callbacks.loading();
|
|
126
159
|
fetchJSON(url, "GET", {})
|
|
@@ -136,7 +169,7 @@ export class Polled<S extends Json, K extends Json, V extends Json>
|
|
|
136
169
|
this.intervals.set(toId(params), setInterval(call, this.duration));
|
|
137
170
|
}
|
|
138
171
|
|
|
139
|
-
close(params:
|
|
172
|
+
close(params: Json): void {
|
|
140
173
|
const interval = this.intervals.get(toId(params));
|
|
141
174
|
if (interval) {
|
|
142
175
|
clearInterval(interval);
|
|
@@ -144,9 +177,11 @@ export class Polled<S extends Json, K extends Json, V extends Json>
|
|
|
144
177
|
}
|
|
145
178
|
}
|
|
146
179
|
|
|
147
|
-
function toId(params:
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
180
|
+
function toId(params: Json): string {
|
|
181
|
+
if (typeof params == "object") {
|
|
182
|
+
const strparams = Object.entries(params)
|
|
183
|
+
.map(([key, value]) => `${key}:${btoa(JSON.stringify(value))}`)
|
|
184
|
+
.sort();
|
|
185
|
+
return `[${strparams.join(",")}]`;
|
|
186
|
+
} else return btoa(JSON.stringify(params));
|
|
152
187
|
}
|
package/src/index.ts
CHANGED
|
@@ -1,6 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This package contains items that may be useful for working with the Skip framework, but are not strictly necessary.
|
|
3
|
+
*
|
|
4
|
+
* @packageDocumentation
|
|
5
|
+
*/
|
|
6
|
+
|
|
1
7
|
export {
|
|
2
8
|
type ExternalResource,
|
|
3
9
|
GenericExternalService,
|
|
4
10
|
Polled,
|
|
5
11
|
} from "./external.js";
|
|
6
|
-
export {
|
|
12
|
+
export { SkipExternalService } from "./remote.js";
|
|
13
|
+
export { SkipServiceBroker, fetchJSON, type Entrypoint } from "./rest.js";
|
|
14
|
+
export { Count, Max, Min, Sum } from "@skipruntime/core";
|
package/src/remote.ts
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
// TODO: Remove once global `EventSource` makes it out of experimental
|
|
2
|
+
// in nodejs LTS.
|
|
3
|
+
import EventSource from "eventsource";
|
|
4
|
+
|
|
5
|
+
import type { Entry, ExternalService, Json } from "@skipruntime/api";
|
|
6
|
+
|
|
7
|
+
import type { Entrypoint } from "./rest.js";
|
|
8
|
+
|
|
9
|
+
interface Closable {
|
|
10
|
+
close(): void;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* An external Skip reactive service.
|
|
15
|
+
*
|
|
16
|
+
* `SkipExternalService` provides an implementation of `ExternalService` for an external Skip service.
|
|
17
|
+
*/
|
|
18
|
+
export class SkipExternalService implements ExternalService {
|
|
19
|
+
private readonly resources = new Map<string, Closable>();
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* @param url - URL to use for the service's streaming interface.
|
|
23
|
+
* @param control_url - URL to use for the service's control interface.
|
|
24
|
+
*/
|
|
25
|
+
constructor(
|
|
26
|
+
private readonly url: string,
|
|
27
|
+
private readonly control_url: string,
|
|
28
|
+
) {}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Constructor accepting an `Entrypoint`.
|
|
32
|
+
*
|
|
33
|
+
* @param entrypoint - The entry point for the external Skip service.
|
|
34
|
+
* @returns An `ExternalService` to interact with the service running at `entrypoint`.
|
|
35
|
+
*/
|
|
36
|
+
// TODO: Support Skip external services going through a gateway.
|
|
37
|
+
static direct(entrypoint: Entrypoint): SkipExternalService {
|
|
38
|
+
let url = `http://${entrypoint.host}:${entrypoint.streaming_port.toString()}`;
|
|
39
|
+
let control_url = `http://${entrypoint.host}:${entrypoint.control_port.toString()}`;
|
|
40
|
+
if (entrypoint.secured) {
|
|
41
|
+
url = `https://${entrypoint.host}:${entrypoint.streaming_port.toString()}`;
|
|
42
|
+
control_url = `https://${entrypoint.host}:${entrypoint.control_port.toString()}`;
|
|
43
|
+
}
|
|
44
|
+
return new SkipExternalService(url, control_url);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
subscribe(
|
|
48
|
+
resource: string,
|
|
49
|
+
params: Json,
|
|
50
|
+
callbacks: {
|
|
51
|
+
update: (updates: Entry<Json, Json>[], isInitial: boolean) => void;
|
|
52
|
+
// FIXME: What is `error()` used for?
|
|
53
|
+
error: (error: Json) => void;
|
|
54
|
+
// FIXME: What is `loading()` used for?
|
|
55
|
+
loading: () => void;
|
|
56
|
+
},
|
|
57
|
+
): void {
|
|
58
|
+
// TODO Manage Status
|
|
59
|
+
fetch(`${this.control_url}/v1/streams`, {
|
|
60
|
+
method: "POST",
|
|
61
|
+
headers: {
|
|
62
|
+
"Content-Type": "application/json",
|
|
63
|
+
},
|
|
64
|
+
body: JSON.stringify({
|
|
65
|
+
resource,
|
|
66
|
+
params,
|
|
67
|
+
}),
|
|
68
|
+
})
|
|
69
|
+
.then((resp) => resp.text())
|
|
70
|
+
.then((uuid) => {
|
|
71
|
+
const evSource = new EventSource(`${this.url}/v1/streams/${uuid}`);
|
|
72
|
+
evSource.addEventListener("init", (e: MessageEvent<string>) => {
|
|
73
|
+
const updates = JSON.parse(e.data) as Entry<Json, Json>[];
|
|
74
|
+
callbacks.update(updates, true);
|
|
75
|
+
});
|
|
76
|
+
evSource.addEventListener("update", (e: MessageEvent<string>) => {
|
|
77
|
+
const updates = JSON.parse(e.data) as Entry<Json, Json>[];
|
|
78
|
+
callbacks.update(updates, false);
|
|
79
|
+
});
|
|
80
|
+
evSource.onerror = (e) => {
|
|
81
|
+
console.log(e);
|
|
82
|
+
};
|
|
83
|
+
this.resources.set(this.toId(resource, params), evSource);
|
|
84
|
+
})
|
|
85
|
+
.catch((e: unknown) => {
|
|
86
|
+
console.log(e);
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
unsubscribe(resource: string, params: Json) {
|
|
91
|
+
const closable = this.resources.get(this.toId(resource, params));
|
|
92
|
+
if (closable) closable.close();
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
shutdown(): void {
|
|
96
|
+
for (const res of this.resources.values()) {
|
|
97
|
+
res.close();
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
private toId(resource: string, params: Json): string {
|
|
102
|
+
if (typeof params == "object") {
|
|
103
|
+
const strparams = Object.entries(params)
|
|
104
|
+
.map(([key, value]) => `${key}:${btoa(JSON.stringify(value))}`)
|
|
105
|
+
.sort();
|
|
106
|
+
return `${resource}[${strparams.join(",")}]`;
|
|
107
|
+
} else return `${resource}[${btoa(JSON.stringify(params))}]`;
|
|
108
|
+
}
|
|
109
|
+
}
|