@skipruntime/helpers 0.0.13 → 0.0.15
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/src/external.d.ts +5 -7
- package/dist/src/external.d.ts.map +1 -1
- package/dist/src/external.js +11 -13
- package/dist/src/external.js.map +1 -1
- package/dist/src/index.d.ts +2 -2
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +2 -2
- package/dist/src/index.js.map +1 -1
- package/dist/src/remote.d.ts +27 -5
- package/dist/src/remote.d.ts.map +1 -1
- package/dist/src/remote.js +73 -15
- package/dist/src/remote.js.map +1 -1
- package/package.json +2 -2
- package/src/external.ts +18 -20
- package/src/index.ts +6 -2
- package/src/remote.ts +107 -28
package/dist/src/external.d.ts
CHANGED
|
@@ -11,8 +11,7 @@ export declare function defaultParamEncoder(params: Json): string;
|
|
|
11
11
|
/**
|
|
12
12
|
* Description of an external HTTP endpoint and how to poll it.
|
|
13
13
|
*
|
|
14
|
-
* The URL of the external resource is formed by appending the given base `url` and the result of `encodeParams(params)` where `params` are the parameters provided to [`Context#useExternalResource`](api/core/interfaces/Context#useexternalresource)
|
|
15
|
-
*
|
|
14
|
+
* The URL of the external resource is formed by appending the given base `url` and the result of `encodeParams(params)` where `params` are the parameters provided to [`Context#useExternalResource`](https://skiplabs.io/docs/api/core/interfaces/Context#useexternalresource)
|
|
16
15
|
*/
|
|
17
16
|
export interface PolledHTTPResource {
|
|
18
17
|
/**
|
|
@@ -46,7 +45,7 @@ export interface PolledHTTPResource {
|
|
|
46
45
|
/**
|
|
47
46
|
* An external HTTP service that is kept up-to-date by polling.
|
|
48
47
|
*
|
|
49
|
-
* A `PolledExternalService` may be composed of one or more [`PolledHTTPResource`](api/helpers/interfaces/PolledHTTPResource)s, each of which describes a single endpoint and how to poll it.
|
|
48
|
+
* A `PolledExternalService` may be composed of one or more [`PolledHTTPResource`](https://skiplabs.io/docs/api/helpers/interfaces/PolledHTTPResource)s, each of which describes a single endpoint and how to poll it.
|
|
50
49
|
*/
|
|
51
50
|
export declare class PolledExternalService implements ExternalService {
|
|
52
51
|
private readonly resources;
|
|
@@ -60,10 +59,9 @@ export declare class PolledExternalService implements ExternalService {
|
|
|
60
59
|
[resource: string]: PolledHTTPResource;
|
|
61
60
|
});
|
|
62
61
|
subscribe(instance: string, resourceName: string, params: Json, callbacks: {
|
|
63
|
-
update: (updates: Entry<Json, Json>[], isInit: boolean) => void
|
|
64
|
-
error: (error:
|
|
65
|
-
|
|
66
|
-
}): void;
|
|
62
|
+
update: (updates: Entry<Json, Json>[], isInit: boolean) => Promise<void>;
|
|
63
|
+
error: (error: unknown) => void;
|
|
64
|
+
}): Promise<void>;
|
|
67
65
|
unsubscribe(instance: string): void;
|
|
68
66
|
shutdown(): Promise<void>;
|
|
69
67
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"external.d.ts","sourceRoot":"","sources":["../../src/external.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,eAAe,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AAMtE;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,IAAI,GAAG,MAAM,CASxD;AAED
|
|
1
|
+
{"version":3,"file":"external.d.ts","sourceRoot":"","sources":["../../src/external.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,eAAe,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AAMtE;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,IAAI,GAAG,MAAM,CASxD;AAED;;;;GAIG;AACH,MAAM,WAAW,kBAAkB;IACjC;;OAEG;IACH,GAAG,EAAE,MAAM,CAAC;IACZ;;OAEG;IACH,QAAQ,EAAE,MAAM,CAAC;IACjB;;OAEG;IACH,IAAI,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,KAAK,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,CAAC;IAC1C;;;;OAIG;IACH,YAAY,CAAC,EAAE,CAAC,MAAM,EAAE,IAAI,KAAK,MAAM,CAAC;IACxC;;OAEG;IACH,OAAO,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE;YAAE,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAA;SAAE,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CACxE;AAED;;;;GAIG;AACH,qBAAa,qBAAsB,YAAW,eAAe;IASzD,OAAO,CAAC,QAAQ,CAAC,SAAS;IAR5B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA8B;IAExD;;;;OAIG;gBAEgB,SAAS,EAAE;QAC1B,CAAC,QAAQ,EAAE,MAAM,GAAG,kBAAkB,CAAC;KACxC;IAGG,SAAS,CACb,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,MAAM,EACpB,MAAM,EAAE,IAAI,EACZ,SAAS,EAAE;QACT,MAAM,EAAE,CAAC,OAAO,EAAE,KAAK,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;QACzE,KAAK,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;KACjC,GACA,OAAO,CAAC,IAAI,CAAC;IAuBhB,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAQnC,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;CAO1B"}
|
package/dist/src/external.js
CHANGED
|
@@ -25,7 +25,7 @@ export function defaultParamEncoder(params) {
|
|
|
25
25
|
/**
|
|
26
26
|
* An external HTTP service that is kept up-to-date by polling.
|
|
27
27
|
*
|
|
28
|
-
* A `PolledExternalService` may be composed of one or more [`PolledHTTPResource`](api/helpers/interfaces/PolledHTTPResource)s, each of which describes a single endpoint and how to poll it.
|
|
28
|
+
* A `PolledExternalService` may be composed of one or more [`PolledHTTPResource`](https://skiplabs.io/docs/api/helpers/interfaces/PolledHTTPResource)s, each of which describes a single endpoint and how to poll it.
|
|
29
29
|
*/
|
|
30
30
|
export class PolledExternalService {
|
|
31
31
|
/**
|
|
@@ -37,24 +37,22 @@ export class PolledExternalService {
|
|
|
37
37
|
this.resources = resources;
|
|
38
38
|
this.intervals = new Map();
|
|
39
39
|
}
|
|
40
|
-
subscribe(instance, resourceName, params, callbacks) {
|
|
40
|
+
async subscribe(instance, resourceName, params, callbacks) {
|
|
41
41
|
const resource = this.resources[resourceName];
|
|
42
42
|
if (!resource)
|
|
43
43
|
throw new SkipUnknownResourceError(`Unknown resource named '${resourceName}'`);
|
|
44
44
|
const url = `${resource.url}${(resource.encodeParams ?? defaultParamEncoder)(params)}`;
|
|
45
|
-
const call = () => {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
callbacks.error(e
|
|
45
|
+
const call = async (init) => {
|
|
46
|
+
const [data, _] = await fetchJSON(url, "GET", resource.options ?? {});
|
|
47
|
+
await callbacks.update(resource.conv(data ?? []), init);
|
|
48
|
+
};
|
|
49
|
+
await call(true);
|
|
50
|
+
this.intervals.set(instance, setInterval(() => {
|
|
51
|
+
call(false).catch((e) => {
|
|
52
|
+
callbacks.error(e);
|
|
53
53
|
console.error(e);
|
|
54
54
|
});
|
|
55
|
-
};
|
|
56
|
-
call();
|
|
57
|
-
this.intervals.set(instance, setInterval(call, resource.interval));
|
|
55
|
+
}, resource.interval));
|
|
58
56
|
}
|
|
59
57
|
unsubscribe(instance) {
|
|
60
58
|
const interval = this.intervals.get(instance);
|
package/dist/src/external.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"external.js","sourceRoot":"","sources":["../../src/external.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,wBAAwB,EAAE,MAAM,mBAAmB,CAAC;AAC7D,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAItC;;;;;;;GAOG;AACH,MAAM,UAAU,mBAAmB,CAAC,MAAY;IAC9C,IAAI,OAAO,MAAM,IAAI,QAAQ,EAAE,CAAC;QAC9B,MAAM,WAAW,GAAgC,EAAE,CAAC;QACpD,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;YAClD,IAAI,OAAO,KAAK,IAAI,QAAQ;gBAAE,WAAW,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;;gBAClE,WAAW,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,QAAQ,EAAE,CAAC;QAC3C,CAAC;QACD,OAAO,IAAI,IAAI,eAAe,CAAC,WAAW,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC;IAC3D,CAAC;;QAAM,OAAO,WAAW,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC;AACpD,CAAC;
|
|
1
|
+
{"version":3,"file":"external.js","sourceRoot":"","sources":["../../src/external.ts"],"names":[],"mappings":"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"}
|
package/dist/src/index.d.ts
CHANGED
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
*
|
|
4
4
|
* @packageDocumentation
|
|
5
5
|
*/
|
|
6
|
-
export { defaultParamEncoder, PolledExternalService } from "./external.js";
|
|
7
|
-
export { SkipExternalService } from "./remote.js";
|
|
6
|
+
export { defaultParamEncoder, PolledExternalService, type PolledHTTPResource, } from "./external.js";
|
|
7
|
+
export { SkipExternalService, asLeader, asFollower } from "./remote.js";
|
|
8
8
|
export { SkipServiceBroker, fetchJSON, type Entrypoint } from "./rest.js";
|
|
9
9
|
export { Count, Max, Min, Sum } from "./utils.js";
|
|
10
10
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/src/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EACL,mBAAmB,EACnB,qBAAqB,EACrB,KAAK,kBAAkB,GACxB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,mBAAmB,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACxE,OAAO,EAAE,iBAAiB,EAAE,SAAS,EAAE,KAAK,UAAU,EAAE,MAAM,WAAW,CAAC;AAC1E,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,YAAY,CAAC"}
|
package/dist/src/index.js
CHANGED
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
*
|
|
4
4
|
* @packageDocumentation
|
|
5
5
|
*/
|
|
6
|
-
export { defaultParamEncoder, PolledExternalService } from "./external.js";
|
|
7
|
-
export { SkipExternalService } from "./remote.js";
|
|
6
|
+
export { defaultParamEncoder, PolledExternalService, } from "./external.js";
|
|
7
|
+
export { SkipExternalService, asLeader, asFollower } from "./remote.js";
|
|
8
8
|
export { SkipServiceBroker, fetchJSON } from "./rest.js";
|
|
9
9
|
export { Count, Max, Min, Sum } from "./utils.js";
|
|
10
10
|
//# sourceMappingURL=index.js.map
|
package/dist/src/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EACL,mBAAmB,EACnB,qBAAqB,GAEtB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,mBAAmB,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACxE,OAAO,EAAE,iBAAiB,EAAE,SAAS,EAAmB,MAAM,WAAW,CAAC;AAC1E,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,YAAY,CAAC"}
|
package/dist/src/remote.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Entry, ExternalService, Json } from "@skipruntime/core";
|
|
1
|
+
import type { Entry, ExternalService, Json, SkipService } from "@skipruntime/core";
|
|
2
2
|
import type { Entrypoint } from "./rest.js";
|
|
3
3
|
/**
|
|
4
4
|
* An external Skip reactive service.
|
|
@@ -22,11 +22,33 @@ export declare class SkipExternalService implements ExternalService {
|
|
|
22
22
|
*/
|
|
23
23
|
static direct(entrypoint: Entrypoint): SkipExternalService;
|
|
24
24
|
subscribe(instance: string, resource: string, params: Json, callbacks: {
|
|
25
|
-
update: (updates: Entry<Json, Json>[], isInitial: boolean) => void
|
|
26
|
-
error: (error:
|
|
27
|
-
|
|
28
|
-
}): void;
|
|
25
|
+
update: (updates: Entry<Json, Json>[], isInitial: boolean) => Promise<void>;
|
|
26
|
+
error: (error: unknown) => void;
|
|
27
|
+
}): Promise<void>;
|
|
29
28
|
unsubscribe(instance: string): void;
|
|
30
29
|
shutdown(): Promise<void>;
|
|
31
30
|
}
|
|
31
|
+
/**
|
|
32
|
+
* Run a `SkipService` as the *leader* in a leader-follower topology.
|
|
33
|
+
*
|
|
34
|
+
* Instead of running a `service` on one machine, it can be distributed across multiple in a leader-follower architecture, with one "leader" maintaining the shared computation graph and one or more "followers" across which client-requested resource instances are distributed.
|
|
35
|
+
*
|
|
36
|
+
* @returns The *leader* component to run `service` in such a configuration.
|
|
37
|
+
*/
|
|
38
|
+
export declare function asLeader(service: SkipService): SkipService;
|
|
39
|
+
/**
|
|
40
|
+
* Run a `SkipService` as a *follower* in a leader-follower topology.
|
|
41
|
+
*
|
|
42
|
+
* Instead of running a `service` on one machine, it can be distributed across multiple in a leader-follower architecture, with one "leader" maintaining the shared computation graph and one or more "followers" across which client-requested resource instances are distributed.
|
|
43
|
+
*
|
|
44
|
+
* @returns The *follower* component to run `service` in such a configuration, given the leader's address and the names of the shared computation graph collections to be mirrored from it (typically the `ResourceInputs` of `service`).
|
|
45
|
+
*/
|
|
46
|
+
export declare function asFollower(service: SkipService, leader: {
|
|
47
|
+
leader: {
|
|
48
|
+
host: string;
|
|
49
|
+
streaming_port: number;
|
|
50
|
+
control_port: number;
|
|
51
|
+
};
|
|
52
|
+
collections: string[];
|
|
53
|
+
}): SkipService;
|
|
32
54
|
//# sourceMappingURL=remote.d.ts.map
|
package/dist/src/remote.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"remote.d.ts","sourceRoot":"","sources":["../../src/remote.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"remote.d.ts","sourceRoot":"","sources":["../../src/remote.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAGV,KAAK,EACL,eAAe,EACf,IAAI,EAGJ,WAAW,EACZ,MAAM,mBAAmB,CAAC;AAG3B,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAM5C;;;;GAIG;AACH,qBAAa,mBAAoB,YAAW,eAAe;IAQvD,OAAO,CAAC,QAAQ,CAAC,GAAG;IACpB,OAAO,CAAC,QAAQ,CAAC,WAAW;IAR9B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA+B;IAEzD;;;OAGG;gBAEgB,GAAG,EAAE,MAAM,EACX,WAAW,EAAE,MAAM;IAGtC;;;;;OAKG;IAEH,MAAM,CAAC,MAAM,CAAC,UAAU,EAAE,UAAU,GAAG,mBAAmB;IAUpD,SAAS,CACb,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,IAAI,EACZ,SAAS,EAAE;QACT,MAAM,EAAE,CACN,OAAO,EAAE,KAAK,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,EAC5B,SAAS,EAAE,OAAO,KACf,OAAO,CAAC,IAAI,CAAC,CAAC;QACnB,KAAK,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;KACjC,GACA,OAAO,CAAC,IAAI,CAAC;IA8BhB,WAAW,CAAC,QAAQ,EAAE,MAAM;IAQ5B,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;CAM1B;AAqBD;;;;;;GAMG;AACH,wBAAgB,QAAQ,CAAC,OAAO,EAAE,WAAW,GAAG,WAAW,CAM1D;AAED;;;;;;GAMG;AACH,wBAAgB,UAAU,CACxB,OAAO,EAAE,WAAW,EACpB,MAAM,EAAE;IACN,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,cAAc,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAE,CAAC;IACvE,WAAW,EAAE,MAAM,EAAE,CAAC;CACvB,GACA,WAAW,CAoBb"}
|
package/dist/src/remote.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// TODO: Remove once global `EventSource` makes it out of experimental
|
|
2
2
|
// in nodejs LTS.
|
|
3
3
|
import EventSource from "eventsource";
|
|
4
|
+
import { SkipError } from "@skipruntime/core";
|
|
4
5
|
/**
|
|
5
6
|
* An external Skip reactive service.
|
|
6
7
|
*
|
|
@@ -32,33 +33,35 @@ export class SkipExternalService {
|
|
|
32
33
|
}
|
|
33
34
|
return new SkipExternalService(url, control_url);
|
|
34
35
|
}
|
|
35
|
-
subscribe(instance, resource, params, callbacks) {
|
|
36
|
-
|
|
37
|
-
fetch(`${this.control_url}/v1/streams/${resource}`, {
|
|
36
|
+
async subscribe(instance, resource, params, callbacks) {
|
|
37
|
+
const resp = await fetch(`${this.control_url}/v1/streams/${resource}`, {
|
|
38
38
|
method: "POST",
|
|
39
39
|
headers: {
|
|
40
40
|
"Content-Type": "application/json",
|
|
41
41
|
},
|
|
42
42
|
body: JSON.stringify(params),
|
|
43
|
-
})
|
|
44
|
-
|
|
45
|
-
|
|
43
|
+
});
|
|
44
|
+
const uuid = await resp.text();
|
|
45
|
+
return new Promise((resolve, reject) => {
|
|
46
46
|
const evSource = new EventSource(`${this.url}/v1/streams/${uuid}`);
|
|
47
|
+
evSource.addEventListener("open", () => {
|
|
48
|
+
this.resources.set(instance, evSource);
|
|
49
|
+
resolve();
|
|
50
|
+
});
|
|
47
51
|
evSource.addEventListener("init", (e) => {
|
|
48
52
|
const updates = JSON.parse(e.data);
|
|
49
|
-
callbacks.update(updates, true);
|
|
53
|
+
callbacks.update(updates, true).catch(console.error);
|
|
50
54
|
});
|
|
51
55
|
evSource.addEventListener("update", (e) => {
|
|
52
56
|
const updates = JSON.parse(e.data);
|
|
53
|
-
callbacks.update(updates, false);
|
|
57
|
+
callbacks.update(updates, false).catch(console.error);
|
|
58
|
+
});
|
|
59
|
+
evSource.addEventListener("error", (e) => {
|
|
60
|
+
if (this.resources.has(instance))
|
|
61
|
+
callbacks.error(e.data);
|
|
62
|
+
else
|
|
63
|
+
reject(e.data);
|
|
54
64
|
});
|
|
55
|
-
evSource.onerror = (e) => {
|
|
56
|
-
console.log(e);
|
|
57
|
-
};
|
|
58
|
-
this.resources.set(instance, evSource);
|
|
59
|
-
})
|
|
60
|
-
.catch((e) => {
|
|
61
|
-
console.log(e);
|
|
62
65
|
});
|
|
63
66
|
}
|
|
64
67
|
unsubscribe(instance) {
|
|
@@ -75,4 +78,59 @@ export class SkipExternalService {
|
|
|
75
78
|
return Promise.resolve();
|
|
76
79
|
}
|
|
77
80
|
}
|
|
81
|
+
class LeaderResource {
|
|
82
|
+
constructor(param) {
|
|
83
|
+
if (typeof param == "string")
|
|
84
|
+
this.collection = param;
|
|
85
|
+
else
|
|
86
|
+
throw new SkipError("Followers must specify a shared collection to mirror from leader.");
|
|
87
|
+
}
|
|
88
|
+
instantiate(collections) {
|
|
89
|
+
if (this.collection in collections)
|
|
90
|
+
return collections[this.collection];
|
|
91
|
+
throw new SkipError(`Unknown shared collection in leader: ${this.collection}`);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Run a `SkipService` as the *leader* in a leader-follower topology.
|
|
96
|
+
*
|
|
97
|
+
* Instead of running a `service` on one machine, it can be distributed across multiple in a leader-follower architecture, with one "leader" maintaining the shared computation graph and one or more "followers" across which client-requested resource instances are distributed.
|
|
98
|
+
*
|
|
99
|
+
* @returns The *leader* component to run `service` in such a configuration.
|
|
100
|
+
*/
|
|
101
|
+
export function asLeader(service) {
|
|
102
|
+
//TODO: add mechanism to split externals between leader/follower
|
|
103
|
+
return {
|
|
104
|
+
...service,
|
|
105
|
+
resources: { leader: LeaderResource },
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Run a `SkipService` as a *follower* in a leader-follower topology.
|
|
110
|
+
*
|
|
111
|
+
* Instead of running a `service` on one machine, it can be distributed across multiple in a leader-follower architecture, with one "leader" maintaining the shared computation graph and one or more "followers" across which client-requested resource instances are distributed.
|
|
112
|
+
*
|
|
113
|
+
* @returns The *follower* component to run `service` in such a configuration, given the leader's address and the names of the shared computation graph collections to be mirrored from it (typically the `ResourceInputs` of `service`).
|
|
114
|
+
*/
|
|
115
|
+
export function asFollower(service, leader) {
|
|
116
|
+
return {
|
|
117
|
+
...service,
|
|
118
|
+
initialData: {},
|
|
119
|
+
externalServices: {
|
|
120
|
+
...service.externalServices,
|
|
121
|
+
__skip_leader: SkipExternalService.direct(leader.leader),
|
|
122
|
+
},
|
|
123
|
+
createGraph(_inputs, context) {
|
|
124
|
+
const mirroredCollections = {};
|
|
125
|
+
for (const collection of leader.collections) {
|
|
126
|
+
mirroredCollections[collection] = context.useExternalResource({
|
|
127
|
+
service: "__skip_leader",
|
|
128
|
+
identifier: "leader",
|
|
129
|
+
params: collection,
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
return mirroredCollections;
|
|
133
|
+
},
|
|
134
|
+
};
|
|
135
|
+
}
|
|
78
136
|
//# sourceMappingURL=remote.js.map
|
package/dist/src/remote.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"remote.js","sourceRoot":"","sources":["../../src/remote.ts"],"names":[],"mappings":"AAAA,sEAAsE;AACtE,iBAAiB;AACjB,OAAO,WAAW,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"remote.js","sourceRoot":"","sources":["../../src/remote.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.15",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": "./dist/src/index.js"
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"dependencies": {
|
|
20
20
|
"eventsource": "^2.0.2",
|
|
21
21
|
"express": "^4.21.1",
|
|
22
|
-
"@skipruntime/core": "0.0.
|
|
22
|
+
"@skipruntime/core": "0.0.15"
|
|
23
23
|
},
|
|
24
24
|
"devDependencies": {
|
|
25
25
|
"@types/eventsource": "^1.1.15"
|
package/src/external.ts
CHANGED
|
@@ -26,8 +26,7 @@ export function defaultParamEncoder(params: Json): string {
|
|
|
26
26
|
/**
|
|
27
27
|
* Description of an external HTTP endpoint and how to poll it.
|
|
28
28
|
*
|
|
29
|
-
* The URL of the external resource is formed by appending the given base `url` and the result of `encodeParams(params)` where `params` are the parameters provided to [`Context#useExternalResource`](api/core/interfaces/Context#useexternalresource)
|
|
30
|
-
*
|
|
29
|
+
* The URL of the external resource is formed by appending the given base `url` and the result of `encodeParams(params)` where `params` are the parameters provided to [`Context#useExternalResource`](https://skiplabs.io/docs/api/core/interfaces/Context#useexternalresource)
|
|
31
30
|
*/
|
|
32
31
|
export interface PolledHTTPResource {
|
|
33
32
|
/**
|
|
@@ -57,7 +56,7 @@ export interface PolledHTTPResource {
|
|
|
57
56
|
/**
|
|
58
57
|
* An external HTTP service that is kept up-to-date by polling.
|
|
59
58
|
*
|
|
60
|
-
* A `PolledExternalService` may be composed of one or more [`PolledHTTPResource`](api/helpers/interfaces/PolledHTTPResource)s, each of which describes a single endpoint and how to poll it.
|
|
59
|
+
* A `PolledExternalService` may be composed of one or more [`PolledHTTPResource`](https://skiplabs.io/docs/api/helpers/interfaces/PolledHTTPResource)s, each of which describes a single endpoint and how to poll it.
|
|
61
60
|
*/
|
|
62
61
|
export class PolledExternalService implements ExternalService {
|
|
63
62
|
private readonly intervals = new Map<string, Timeout>();
|
|
@@ -73,36 +72,35 @@ export class PolledExternalService implements ExternalService {
|
|
|
73
72
|
},
|
|
74
73
|
) {}
|
|
75
74
|
|
|
76
|
-
subscribe(
|
|
75
|
+
async subscribe(
|
|
77
76
|
instance: string,
|
|
78
77
|
resourceName: string,
|
|
79
78
|
params: Json,
|
|
80
79
|
callbacks: {
|
|
81
|
-
update: (updates: Entry<Json, Json>[], isInit: boolean) => void
|
|
82
|
-
error: (error:
|
|
83
|
-
loading: () => void;
|
|
80
|
+
update: (updates: Entry<Json, Json>[], isInit: boolean) => Promise<void>;
|
|
81
|
+
error: (error: unknown) => void;
|
|
84
82
|
},
|
|
85
|
-
) {
|
|
83
|
+
): Promise<void> {
|
|
86
84
|
const resource = this.resources[resourceName];
|
|
87
85
|
if (!resource)
|
|
88
86
|
throw new SkipUnknownResourceError(
|
|
89
87
|
`Unknown resource named '${resourceName}'`,
|
|
90
88
|
);
|
|
91
|
-
|
|
92
89
|
const url = `${resource.url}${(resource.encodeParams ?? defaultParamEncoder)(params)}`;
|
|
93
|
-
const call = () => {
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
90
|
+
const call = async (init: boolean) => {
|
|
91
|
+
const [data, _] = await fetchJSON(url, "GET", resource.options ?? {});
|
|
92
|
+
await callbacks.update(resource.conv(data ?? []), init);
|
|
93
|
+
};
|
|
94
|
+
await call(true);
|
|
95
|
+
this.intervals.set(
|
|
96
|
+
instance,
|
|
97
|
+
setInterval(() => {
|
|
98
|
+
call(false).catch((e: unknown) => {
|
|
99
|
+
callbacks.error(e);
|
|
101
100
|
console.error(e);
|
|
102
101
|
});
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
this.intervals.set(instance, setInterval(call, resource.interval));
|
|
102
|
+
}, resource.interval),
|
|
103
|
+
);
|
|
106
104
|
}
|
|
107
105
|
|
|
108
106
|
unsubscribe(instance: string): void {
|
package/src/index.ts
CHANGED
|
@@ -4,7 +4,11 @@
|
|
|
4
4
|
* @packageDocumentation
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
export {
|
|
8
|
-
|
|
7
|
+
export {
|
|
8
|
+
defaultParamEncoder,
|
|
9
|
+
PolledExternalService,
|
|
10
|
+
type PolledHTTPResource,
|
|
11
|
+
} from "./external.js";
|
|
12
|
+
export { SkipExternalService, asLeader, asFollower } from "./remote.js";
|
|
9
13
|
export { SkipServiceBroker, fetchJSON, type Entrypoint } from "./rest.js";
|
|
10
14
|
export { Count, Max, Min, Sum } from "./utils.js";
|
package/src/remote.ts
CHANGED
|
@@ -2,7 +2,17 @@
|
|
|
2
2
|
// in nodejs LTS.
|
|
3
3
|
import EventSource from "eventsource";
|
|
4
4
|
|
|
5
|
-
import type {
|
|
5
|
+
import type {
|
|
6
|
+
Context,
|
|
7
|
+
EagerCollection,
|
|
8
|
+
Entry,
|
|
9
|
+
ExternalService,
|
|
10
|
+
Json,
|
|
11
|
+
NamedCollections,
|
|
12
|
+
Resource,
|
|
13
|
+
SkipService,
|
|
14
|
+
} from "@skipruntime/core";
|
|
15
|
+
import { SkipError } from "@skipruntime/core";
|
|
6
16
|
|
|
7
17
|
import type { Entrypoint } from "./rest.js";
|
|
8
18
|
|
|
@@ -44,45 +54,45 @@ export class SkipExternalService implements ExternalService {
|
|
|
44
54
|
return new SkipExternalService(url, control_url);
|
|
45
55
|
}
|
|
46
56
|
|
|
47
|
-
subscribe(
|
|
57
|
+
async subscribe(
|
|
48
58
|
instance: string,
|
|
49
59
|
resource: string,
|
|
50
60
|
params: Json,
|
|
51
61
|
callbacks: {
|
|
52
|
-
update: (
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
62
|
+
update: (
|
|
63
|
+
updates: Entry<Json, Json>[],
|
|
64
|
+
isInitial: boolean,
|
|
65
|
+
) => Promise<void>;
|
|
66
|
+
error: (error: unknown) => void;
|
|
57
67
|
},
|
|
58
|
-
): void {
|
|
59
|
-
|
|
60
|
-
fetch(`${this.control_url}/v1/streams/${resource}`, {
|
|
68
|
+
): Promise<void> {
|
|
69
|
+
const resp = await fetch(`${this.control_url}/v1/streams/${resource}`, {
|
|
61
70
|
method: "POST",
|
|
62
71
|
headers: {
|
|
63
72
|
"Content-Type": "application/json",
|
|
64
73
|
},
|
|
65
74
|
body: JSON.stringify(params),
|
|
66
|
-
})
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
const updates = JSON.parse(e.data) as Entry<Json, Json>[];
|
|
72
|
-
callbacks.update(updates, true);
|
|
73
|
-
});
|
|
74
|
-
evSource.addEventListener("update", (e: MessageEvent<string>) => {
|
|
75
|
-
const updates = JSON.parse(e.data) as Entry<Json, Json>[];
|
|
76
|
-
callbacks.update(updates, false);
|
|
77
|
-
});
|
|
78
|
-
evSource.onerror = (e) => {
|
|
79
|
-
console.log(e);
|
|
80
|
-
};
|
|
75
|
+
});
|
|
76
|
+
const uuid = await resp.text();
|
|
77
|
+
return new Promise<void>((resolve, reject) => {
|
|
78
|
+
const evSource = new EventSource(`${this.url}/v1/streams/${uuid}`);
|
|
79
|
+
evSource.addEventListener("open", () => {
|
|
81
80
|
this.resources.set(instance, evSource);
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
81
|
+
resolve();
|
|
82
|
+
});
|
|
83
|
+
evSource.addEventListener("init", (e: MessageEvent<string>) => {
|
|
84
|
+
const updates = JSON.parse(e.data) as Entry<Json, Json>[];
|
|
85
|
+
callbacks.update(updates, true).catch(console.error);
|
|
86
|
+
});
|
|
87
|
+
evSource.addEventListener("update", (e: MessageEvent<string>) => {
|
|
88
|
+
const updates = JSON.parse(e.data) as Entry<Json, Json>[];
|
|
89
|
+
callbacks.update(updates, false).catch(console.error);
|
|
85
90
|
});
|
|
91
|
+
evSource.addEventListener("error", (e) => {
|
|
92
|
+
if (this.resources.has(instance)) callbacks.error(e.data);
|
|
93
|
+
else reject(e.data as Error);
|
|
94
|
+
});
|
|
95
|
+
});
|
|
86
96
|
}
|
|
87
97
|
|
|
88
98
|
unsubscribe(instance: string) {
|
|
@@ -100,3 +110,72 @@ export class SkipExternalService implements ExternalService {
|
|
|
100
110
|
return Promise.resolve();
|
|
101
111
|
}
|
|
102
112
|
}
|
|
113
|
+
|
|
114
|
+
class LeaderResource implements Resource {
|
|
115
|
+
private collection: string;
|
|
116
|
+
|
|
117
|
+
constructor(param: Json) {
|
|
118
|
+
if (typeof param == "string") this.collection = param;
|
|
119
|
+
else
|
|
120
|
+
throw new SkipError(
|
|
121
|
+
"Followers must specify a shared collection to mirror from leader.",
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
instantiate(collections: NamedCollections): EagerCollection<Json, Json> {
|
|
126
|
+
if (this.collection in collections) return collections[this.collection]!;
|
|
127
|
+
throw new SkipError(
|
|
128
|
+
`Unknown shared collection in leader: ${this.collection}`,
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Run a `SkipService` as the *leader* in a leader-follower topology.
|
|
135
|
+
*
|
|
136
|
+
* Instead of running a `service` on one machine, it can be distributed across multiple in a leader-follower architecture, with one "leader" maintaining the shared computation graph and one or more "followers" across which client-requested resource instances are distributed.
|
|
137
|
+
*
|
|
138
|
+
* @returns The *leader* component to run `service` in such a configuration.
|
|
139
|
+
*/
|
|
140
|
+
export function asLeader(service: SkipService): SkipService {
|
|
141
|
+
//TODO: add mechanism to split externals between leader/follower
|
|
142
|
+
return {
|
|
143
|
+
...service,
|
|
144
|
+
resources: { leader: LeaderResource },
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Run a `SkipService` as a *follower* in a leader-follower topology.
|
|
150
|
+
*
|
|
151
|
+
* Instead of running a `service` on one machine, it can be distributed across multiple in a leader-follower architecture, with one "leader" maintaining the shared computation graph and one or more "followers" across which client-requested resource instances are distributed.
|
|
152
|
+
*
|
|
153
|
+
* @returns The *follower* component to run `service` in such a configuration, given the leader's address and the names of the shared computation graph collections to be mirrored from it (typically the `ResourceInputs` of `service`).
|
|
154
|
+
*/
|
|
155
|
+
export function asFollower(
|
|
156
|
+
service: SkipService,
|
|
157
|
+
leader: {
|
|
158
|
+
leader: { host: string; streaming_port: number; control_port: number };
|
|
159
|
+
collections: string[];
|
|
160
|
+
},
|
|
161
|
+
): SkipService {
|
|
162
|
+
return {
|
|
163
|
+
...service,
|
|
164
|
+
initialData: {},
|
|
165
|
+
externalServices: {
|
|
166
|
+
...service.externalServices,
|
|
167
|
+
__skip_leader: SkipExternalService.direct(leader.leader),
|
|
168
|
+
},
|
|
169
|
+
createGraph(_inputs: object, context: Context): NamedCollections {
|
|
170
|
+
const mirroredCollections: NamedCollections = {};
|
|
171
|
+
for (const collection of leader.collections) {
|
|
172
|
+
mirroredCollections[collection] = context.useExternalResource({
|
|
173
|
+
service: "__skip_leader",
|
|
174
|
+
identifier: "leader",
|
|
175
|
+
params: collection,
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
return mirroredCollections;
|
|
179
|
+
},
|
|
180
|
+
};
|
|
181
|
+
}
|