@toa.io/extensions.realtime 1.0.0-alpha.29 → 1.0.0-alpha.291
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/CHANGELOG.md +35 -0
- package/components/realtime.streams/manifest.toa.yaml +49 -0
- package/components/realtime.streams/operations/create.js +65 -0
- package/components/realtime.streams/operations/lib/Stash.js +86 -0
- package/components/realtime.streams/operations/lib/Stream.js +43 -0
- package/components/realtime.streams/operations/lib/index.js +3 -0
- package/components/realtime.streams/operations/push.js +10 -0
- package/components/realtime.streams/source/create.ts +94 -0
- package/components/realtime.streams/source/lib/Stash.ts +131 -0
- package/components/realtime.streams/source/lib/Stream.ts +58 -0
- package/components/realtime.streams/source/lib/index.ts +2 -0
- package/components/realtime.streams/source/push.ts +14 -0
- package/components/{streams → realtime.streams}/tsconfig.json +2 -1
- package/components/realtime.streams/tsconfig.tsbuildinfo +1 -0
- package/components/realtime.streams/types/index.d.ts +20 -0
- package/components/realtime.streams/types/toa.d.ts +29 -0
- package/{components/context.toa.yaml → context.toa.yaml} +2 -3
- package/cucumber.mjs +8 -0
- package/features/static.feature +120 -0
- package/features/steps/Components.ts +22 -21
- package/features/steps/Realtime.ts +18 -21
- package/features/steps/Streams.ts +55 -20
- package/features/steps/components/messages/manifest.toa.yaml +36 -7
- package/features/steps/config.ts +15 -1
- package/package.json +9 -7
- package/readme.md +33 -7
- package/source/Composition.ts +26 -15
- package/source/Factory.ts +15 -16
- package/source/Realtime.ts +48 -15
- package/source/Receiver.ts +95 -0
- package/source/Routes.ts +26 -45
- package/source/extension.ts +92 -0
- package/source/index.ts +2 -1
- package/transpiled/Composition.d.ts +10 -4
- package/transpiled/Composition.js +21 -20
- package/transpiled/Composition.js.map +1 -1
- package/transpiled/Factory.d.ts +5 -5
- package/transpiled/Factory.js +13 -18
- package/transpiled/Factory.js.map +1 -1
- package/transpiled/Realtime.d.ts +3 -2
- package/transpiled/Realtime.js +36 -11
- package/transpiled/Realtime.js.map +1 -1
- package/transpiled/Receiver.d.ts +26 -0
- package/transpiled/Receiver.js +57 -0
- package/transpiled/Receiver.js.map +1 -0
- package/transpiled/Routes.d.ts +5 -10
- package/transpiled/Routes.js +18 -34
- package/transpiled/Routes.js.map +1 -1
- package/transpiled/extension.d.ts +22 -0
- package/transpiled/extension.js +55 -0
- package/transpiled/extension.js.map +1 -0
- package/transpiled/index.d.ts +2 -1
- package/transpiled/index.js +2 -5
- package/transpiled/index.js.map +1 -1
- package/tsconfig.json +2 -1
- package/tsconfig.tsbuildinfo +1 -0
- package/components/streams/manifest.toa.yaml +0 -21
- package/components/streams/operations/create.d.ts +0 -14
- package/components/streams/operations/create.js +0 -29
- package/components/streams/operations/create.js.map +0 -1
- package/components/streams/operations/lib/stream.d.ts +0 -10
- package/components/streams/operations/lib/stream.js +0 -31
- package/components/streams/operations/lib/stream.js.map +0 -1
- package/components/streams/operations/push.d.ts +0 -2
- package/components/streams/operations/push.js +0 -8
- package/components/streams/operations/push.js.map +0 -1
- package/components/streams/operations/tsconfig.tsbuildinfo +0 -1
- package/components/streams/operations/types.d.ts +0 -11
- package/components/streams/operations/types.js +0 -3
- package/components/streams/operations/types.js.map +0 -1
- package/components/streams/source/create.ts +0 -38
- package/components/streams/source/lib/stream.ts +0 -37
- package/components/streams/source/push.ts +0 -5
- package/components/streams/source/types.ts +0 -13
- package/cucumber.js +0 -8
- package/stage/streams.test.ts +0 -128
- package/transpiled/tsconfig.tsbuildinfo +0 -1
package/source/Routes.ts
CHANGED
|
@@ -1,32 +1,39 @@
|
|
|
1
1
|
import { Readable } from 'node:stream'
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
2
|
+
import { console } from 'openspan'
|
|
3
|
+
import { Connector } from '@toa.io/core'
|
|
4
|
+
import { Receiver } from './Receiver.js'
|
|
5
|
+
import { environment } from '@toa.io/generic'
|
|
6
|
+
import type { Route } from './extension.js'
|
|
7
|
+
import type { Host } from './Factory.js'
|
|
5
8
|
|
|
6
9
|
export class Routes extends Connector {
|
|
7
10
|
public events = new Events()
|
|
8
11
|
|
|
9
|
-
private readonly
|
|
12
|
+
private readonly host: Host
|
|
10
13
|
|
|
11
|
-
public constructor
|
|
14
|
+
public constructor(host: Host) {
|
|
12
15
|
super()
|
|
13
16
|
|
|
14
|
-
this.
|
|
17
|
+
this.host = host
|
|
15
18
|
}
|
|
16
19
|
|
|
17
|
-
private static read
|
|
18
|
-
|
|
19
|
-
throw new Error('TOA_REALTIME is not defined')
|
|
20
|
+
private static read(): Route[] {
|
|
21
|
+
const value = environment.get('TOA_REALTIME')
|
|
20
22
|
|
|
21
|
-
|
|
23
|
+
if (value === undefined) throw new Error('TOA_REALTIME is not defined')
|
|
24
|
+
|
|
25
|
+
return JSON.parse(value) as Route[]
|
|
22
26
|
}
|
|
23
27
|
|
|
24
|
-
public override async open
|
|
28
|
+
public override async open(): Promise<void> {
|
|
25
29
|
const routes = Routes.read()
|
|
26
30
|
const creating = []
|
|
27
31
|
|
|
28
|
-
for (const { event, properties } of routes) {
|
|
29
|
-
const consumer = this.
|
|
32
|
+
for (const { event, properties, expose } of routes) {
|
|
33
|
+
const consumer = this.host.receive(
|
|
34
|
+
event,
|
|
35
|
+
new Receiver({ event, properties, stream: this.events, expose })
|
|
36
|
+
)
|
|
30
37
|
|
|
31
38
|
creating.push(consumer)
|
|
32
39
|
}
|
|
@@ -39,46 +46,20 @@ export class Routes extends Connector {
|
|
|
39
46
|
await Promise.all(connecting)
|
|
40
47
|
this.depends(consumers)
|
|
41
48
|
|
|
42
|
-
console.
|
|
49
|
+
console.info('Event sources connected', { count: creating.length })
|
|
43
50
|
}
|
|
44
51
|
|
|
45
|
-
public override async close
|
|
46
|
-
console.
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
private getReceiver (event: string, properties: string[]): Receiver {
|
|
50
|
-
return {
|
|
51
|
-
receive: (message: Message<Record<string, string>>) => {
|
|
52
|
-
for (const property of properties) {
|
|
53
|
-
const key = message.payload[property]
|
|
54
|
-
|
|
55
|
-
if (key === undefined) {
|
|
56
|
-
console.error(`Event '${event}' does not contain the '${property}' property.`)
|
|
57
|
-
|
|
58
|
-
return
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
this.events.push({ key, event, data: message.payload })
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
}
|
|
52
|
+
public override async close(): Promise<void> {
|
|
53
|
+
console.info('Event sources disconnected')
|
|
65
54
|
}
|
|
66
55
|
}
|
|
67
56
|
|
|
68
57
|
class Events extends Readable {
|
|
69
|
-
public constructor
|
|
58
|
+
public constructor() {
|
|
70
59
|
super({ objectMode: true })
|
|
71
60
|
}
|
|
72
61
|
|
|
73
|
-
public override _read
|
|
74
|
-
}
|
|
62
|
+
public override _read(): void {}
|
|
75
63
|
}
|
|
76
64
|
|
|
77
|
-
export
|
|
78
|
-
event: string
|
|
79
|
-
properties: string[]
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
interface Receiver {
|
|
83
|
-
receive: (message: Message<Record<string, string>>) => void
|
|
84
|
-
}
|
|
65
|
+
export type { Route }
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs'
|
|
2
|
+
import { components } from './Composition.js'
|
|
3
|
+
import type { Dependency, Instances, Resources, Service } from '@toa.io/operations'
|
|
4
|
+
|
|
5
|
+
/** Where Toa's release publishes this service's image. An application takes it
|
|
6
|
+
* instead of building one when its context says `registry.services: published`. */
|
|
7
|
+
export const image = 'ghcr.io/toa-io/extension-realtime-streams'
|
|
8
|
+
|
|
9
|
+
export const standalone = true
|
|
10
|
+
export { components } from './Composition.js'
|
|
11
|
+
|
|
12
|
+
export function deployment(
|
|
13
|
+
instances: Instances<Declaration>,
|
|
14
|
+
annotation?: Declaration & Annotation
|
|
15
|
+
): Dependency {
|
|
16
|
+
const routes = []
|
|
17
|
+
const { resources, ...annotatedRoutes } = annotation ?? {}
|
|
18
|
+
const labels = components().labels
|
|
19
|
+
|
|
20
|
+
if (annotatedRoutes !== undefined) routes.push(...parse(annotatedRoutes))
|
|
21
|
+
|
|
22
|
+
for (const instance of instances) {
|
|
23
|
+
const completed: Declaration = {}
|
|
24
|
+
|
|
25
|
+
for (const [key, value] of Object.entries(instance.manifest)) {
|
|
26
|
+
const event = instance.locator.id + '.' + key
|
|
27
|
+
|
|
28
|
+
completed[event] = value
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
routes.push(...parse(completed))
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const service: Service = {
|
|
35
|
+
group: 'realtime',
|
|
36
|
+
name: 'streams',
|
|
37
|
+
image,
|
|
38
|
+
|
|
39
|
+
version: JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8'))
|
|
40
|
+
.version,
|
|
41
|
+
components: labels,
|
|
42
|
+
resources,
|
|
43
|
+
variables: [
|
|
44
|
+
{
|
|
45
|
+
name: 'TOA_REALTIME',
|
|
46
|
+
value: JSON.stringify(routes)
|
|
47
|
+
}
|
|
48
|
+
]
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return { services: [service], events: routes.map((route) => route.event) }
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function parse(declaration: Declaration): Route[] {
|
|
55
|
+
const routes: Route[] = []
|
|
56
|
+
|
|
57
|
+
for (const [event, value] of Object.entries(declaration))
|
|
58
|
+
if (isObject(value)) {
|
|
59
|
+
const properties = Array.isArray(value.key) ? value.key : [value.key]
|
|
60
|
+
|
|
61
|
+
routes.push({ event, properties, expose: value.expose })
|
|
62
|
+
} else {
|
|
63
|
+
const properties = Array.isArray(value) ? value : [value]
|
|
64
|
+
|
|
65
|
+
routes.push({ event, properties })
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return routes
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function isObject(value: Entry): value is RouteDeclaration {
|
|
72
|
+
return typeof value === 'object' && !Array.isArray(value)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export type Entry = string | string[] | RouteDeclaration
|
|
76
|
+
|
|
77
|
+
export type Declaration = Record<string, Entry>
|
|
78
|
+
|
|
79
|
+
export interface RouteDeclaration {
|
|
80
|
+
key: string | string[]
|
|
81
|
+
expose?: string[]
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export interface Route {
|
|
85
|
+
event: string
|
|
86
|
+
properties: string[]
|
|
87
|
+
expose?: string[]
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
interface Annotation {
|
|
91
|
+
resources?: Resources
|
|
92
|
+
}
|
package/source/index.ts
CHANGED
|
@@ -1 +1,2 @@
|
|
|
1
|
-
export { Factory } from './Factory'
|
|
1
|
+
export { Factory } from './Factory.js'
|
|
2
|
+
export * from './extension.js'
|
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
import { Connector } from '@toa.io/core';
|
|
2
|
-
import { type
|
|
2
|
+
import { type Host } from './Factory.js';
|
|
3
3
|
export declare class Composition extends Connector {
|
|
4
|
-
private readonly
|
|
5
|
-
constructor(
|
|
4
|
+
private readonly host;
|
|
5
|
+
constructor(host: Host);
|
|
6
6
|
protected open(): Promise<void>;
|
|
7
|
-
protected dispose(): void;
|
|
8
7
|
}
|
|
8
|
+
export declare function find(): string[];
|
|
9
|
+
export declare function components(): Components;
|
|
10
|
+
interface Components {
|
|
11
|
+
labels: string[];
|
|
12
|
+
paths: string[];
|
|
13
|
+
}
|
|
14
|
+
export {};
|
|
@@ -1,33 +1,34 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
class Composition extends core_1.Connector {
|
|
8
|
-
boot;
|
|
9
|
-
constructor(boot) {
|
|
1
|
+
import { readdirSync } from 'node:fs';
|
|
2
|
+
import { resolve } from 'node:path';
|
|
3
|
+
import { Connector } from '@toa.io/core';
|
|
4
|
+
export class Composition extends Connector {
|
|
5
|
+
host;
|
|
6
|
+
constructor(host) {
|
|
10
7
|
super();
|
|
11
|
-
this.
|
|
8
|
+
this.host = host;
|
|
12
9
|
}
|
|
13
10
|
async open() {
|
|
14
11
|
const paths = find();
|
|
15
|
-
const composition = await this.
|
|
12
|
+
const composition = await this.host.composition(paths);
|
|
16
13
|
await composition.connect();
|
|
17
14
|
this.depends(composition);
|
|
18
|
-
console.info('Composition complete.');
|
|
19
|
-
}
|
|
20
|
-
dispose() {
|
|
21
|
-
console.info('Composition shutdown complete.');
|
|
22
15
|
}
|
|
23
16
|
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
return entries().map((entry) => (0, node_path_1.resolve)(ROOT, entry.name));
|
|
17
|
+
export function find() {
|
|
18
|
+
return entries().map((entry) => resolve(ROOT, entry.name));
|
|
27
19
|
}
|
|
28
20
|
function entries() {
|
|
29
|
-
const entries =
|
|
21
|
+
const entries = readdirSync(ROOT, { withFileTypes: true });
|
|
30
22
|
return entries.filter((entry) => entry.isDirectory());
|
|
31
23
|
}
|
|
32
|
-
|
|
24
|
+
export function components() {
|
|
25
|
+
const labels = [];
|
|
26
|
+
const paths = [];
|
|
27
|
+
for (const entry of entries()) {
|
|
28
|
+
labels.push(entry.name.replace('.', '-'));
|
|
29
|
+
paths.push(resolve(ROOT, entry.name));
|
|
30
|
+
}
|
|
31
|
+
return { labels, paths };
|
|
32
|
+
}
|
|
33
|
+
const ROOT = resolve(import.meta.dirname, '../components/');
|
|
33
34
|
//# sourceMappingURL=Composition.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Composition.js","sourceRoot":"","sources":["../source/Composition.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"Composition.js","sourceRoot":"","sources":["../source/Composition.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAe,MAAM,SAAS,CAAA;AAClD,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACnC,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AAGxC,MAAM,OAAO,WAAY,SAAQ,SAAS;IACvB,IAAI,CAAM;IAE3B,YAAmB,IAAU;QAC3B,KAAK,EAAE,CAAA;QACP,IAAI,CAAC,IAAI,GAAG,IAAI,CAAA;IAClB,CAAC;IAEkB,KAAK,CAAC,IAAI;QAC3B,MAAM,KAAK,GAAG,IAAI,EAAE,CAAA;QACpB,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,CAAA;QAEtD,MAAM,WAAW,CAAC,OAAO,EAAE,CAAA;QAE3B,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,CAAA;IAC3B,CAAC;CACF;AAED,MAAM,UAAU,IAAI;IAClB,OAAO,OAAO,EAAE,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,CAAA;AAC5D,CAAC;AAED,SAAS,OAAO;IACd,MAAM,OAAO,GAAG,WAAW,CAAC,IAAI,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAA;IAE1D,OAAO,OAAO,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,CAAA;AACvD,CAAC;AAED,MAAM,UAAU,UAAU;IACxB,MAAM,MAAM,GAAa,EAAE,CAAA;IAC3B,MAAM,KAAK,GAAa,EAAE,CAAA;IAE1B,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,EAAE,CAAC;QAC9B,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CAAA;QACzC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,CAAA;IACvC,CAAC;IAED,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,CAAA;AAC1B,CAAC;AAOD,MAAM,IAAI,GAAG,OAAO,CAAC,OAAO,IAAI,CAAC,OAAO,EAAE,gBAAgB,CAAC,CAAA"}
|
package/transpiled/Factory.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { Realtime } from './Realtime';
|
|
1
|
+
import type { extensions } from '@toa.io/core/types';
|
|
2
|
+
import { Realtime } from './Realtime.js';
|
|
3
3
|
export declare class Factory implements extensions.Factory {
|
|
4
|
-
private readonly
|
|
5
|
-
constructor(
|
|
4
|
+
private readonly host;
|
|
5
|
+
constructor(host: Host);
|
|
6
6
|
service(): Realtime;
|
|
7
7
|
private discovery;
|
|
8
8
|
}
|
|
9
|
-
export type
|
|
9
|
+
export type Host = extensions.Host;
|
package/transpiled/Factory.js
CHANGED
|
@@ -1,28 +1,23 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
boot;
|
|
10
|
-
constructor(boot) {
|
|
11
|
-
this.boot = boot;
|
|
1
|
+
import { Locator } from '@toa.io/core';
|
|
2
|
+
import { Realtime } from './Realtime.js';
|
|
3
|
+
import { Composition } from './Composition.js';
|
|
4
|
+
import { Routes } from './Routes.js';
|
|
5
|
+
export class Factory {
|
|
6
|
+
host;
|
|
7
|
+
constructor(host) {
|
|
8
|
+
this.host = host;
|
|
12
9
|
}
|
|
13
10
|
service() {
|
|
14
|
-
const
|
|
15
|
-
const
|
|
16
|
-
const
|
|
17
|
-
const realtime = new Realtime_1.Realtime(routes, discovery);
|
|
11
|
+
const routes = new Routes(this.host);
|
|
12
|
+
const composition = new Composition(this.host);
|
|
13
|
+
const realtime = new Realtime(routes, async () => await this.discovery());
|
|
18
14
|
realtime.depends(routes);
|
|
19
15
|
realtime.depends(composition);
|
|
20
16
|
return realtime;
|
|
21
17
|
}
|
|
22
18
|
async discovery() {
|
|
23
|
-
const locator = new
|
|
24
|
-
return await this.
|
|
19
|
+
const locator = new Locator('streams', 'realtime');
|
|
20
|
+
return await this.host.remote(locator, { service: 'realtime' });
|
|
25
21
|
}
|
|
26
22
|
}
|
|
27
|
-
exports.Factory = Factory;
|
|
28
23
|
//# sourceMappingURL=Factory.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Factory.js","sourceRoot":"","sources":["../source/Factory.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"Factory.js","sourceRoot":"","sources":["../source/Factory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkB,OAAO,EAAE,MAAM,cAAc,CAAA;AAEtD,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AACxC,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAC9C,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAEpC,MAAM,OAAO,OAAO;IACD,IAAI,CAAM;IAE3B,YAAmB,IAAU;QAC3B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAA;IAClB,CAAC;IAEM,OAAO;QACZ,MAAM,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QACpC,MAAM,WAAW,GAAG,IAAI,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QAC9C,MAAM,QAAQ,GAAG,IAAI,QAAQ,CAAC,MAAM,EAAE,KAAK,IAAI,EAAE,CAAC,MAAM,IAAI,CAAC,SAAS,EAAE,CAAC,CAAA;QAEzE,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,CAAA;QACxB,QAAQ,CAAC,OAAO,CAAC,WAAW,CAAC,CAAA;QAE7B,OAAO,QAAQ,CAAA;IACjB,CAAC;IAEO,KAAK,CAAC,SAAS;QACrB,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC,SAAS,EAAE,UAAU,CAAC,CAAA;QAElD,OAAO,MAAM,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,EAAE,OAAO,EAAE,UAAU,EAAE,CAAC,CAAA;IACjE,CAAC;CACF"}
|
package/transpiled/Realtime.d.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { type Component, Connector } from '@toa.io/core';
|
|
2
|
-
import { type Routes } from './Routes';
|
|
2
|
+
import { type Routes } from './Routes.js';
|
|
3
3
|
export declare class Realtime extends Connector {
|
|
4
4
|
private readonly discovery;
|
|
5
5
|
private streams;
|
|
6
|
-
constructor(routes: Routes, discovery: Promise<Component>);
|
|
6
|
+
constructor(routes: Routes, discovery: () => Promise<Component>);
|
|
7
7
|
protected open(): Promise<void>;
|
|
8
8
|
protected dispose(): void;
|
|
9
9
|
private push;
|
|
10
|
+
private deliver;
|
|
10
11
|
}
|
package/transpiled/Realtime.js
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
const core_1 = require("@toa.io/core");
|
|
5
|
-
class Realtime extends core_1.Connector {
|
|
1
|
+
import { console, run } from 'openspan';
|
|
2
|
+
import { Connector } from '@toa.io/core';
|
|
3
|
+
export class Realtime extends Connector {
|
|
6
4
|
discovery;
|
|
7
5
|
streams = null;
|
|
8
6
|
constructor(routes, discovery) {
|
|
@@ -11,17 +9,44 @@ class Realtime extends core_1.Connector {
|
|
|
11
9
|
routes.events.on('data', this.push.bind(this));
|
|
12
10
|
}
|
|
13
11
|
async open() {
|
|
14
|
-
|
|
12
|
+
// the lookup belongs here, not in the constructor: dependencies connect before
|
|
13
|
+
// open, so the composition that serves realtime.streams is up to answer it. Asked
|
|
14
|
+
// any earlier the lookup goes unanswered, and it waits without a bound.
|
|
15
|
+
this.streams = await this.discovery();
|
|
15
16
|
this.depends(this.streams);
|
|
16
17
|
await this.streams.connect();
|
|
17
|
-
console.
|
|
18
|
+
console.info('Realtime service started');
|
|
18
19
|
}
|
|
19
20
|
dispose() {
|
|
20
|
-
console.
|
|
21
|
+
console.info('Realtime service shutdown complete');
|
|
21
22
|
}
|
|
22
|
-
push(event) {
|
|
23
|
-
|
|
23
|
+
push({ telemetry, ...event }) {
|
|
24
|
+
const processing = telemetry === null
|
|
25
|
+
? this.deliver(event)
|
|
26
|
+
: run(telemetry, async () => await this.deliver(event));
|
|
27
|
+
void processing.catch((error) => console.error('Realtime push failed', error));
|
|
28
|
+
}
|
|
29
|
+
async deliver(event) {
|
|
30
|
+
/*
|
|
31
|
+
* The delivery span is created on behalf of the messaging destination
|
|
32
|
+
* (same as the core Receiver), so that service graphs display the fan-out:
|
|
33
|
+
* producer -> destination -> realtime
|
|
34
|
+
*/
|
|
35
|
+
const delivery = {
|
|
36
|
+
name: `${event.event} deliver`,
|
|
37
|
+
kind: 'producer',
|
|
38
|
+
service: event.event,
|
|
39
|
+
attributes: { 'messaging.destination.name': event.event }
|
|
40
|
+
};
|
|
41
|
+
const options = {
|
|
42
|
+
name: `${event.event} push`,
|
|
43
|
+
kind: 'consumer',
|
|
44
|
+
service: 'realtime',
|
|
45
|
+
attributes: { 'messaging.destination.name': event.event }
|
|
46
|
+
};
|
|
47
|
+
await console.span(delivery, async () => await console.span(options, async () => {
|
|
48
|
+
await this.streams?.invoke('push', { input: event });
|
|
49
|
+
}));
|
|
24
50
|
}
|
|
25
51
|
}
|
|
26
|
-
exports.Realtime = Realtime;
|
|
27
52
|
//# sourceMappingURL=Realtime.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Realtime.js","sourceRoot":"","sources":["../source/Realtime.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"Realtime.js","sourceRoot":"","sources":["../source/Realtime.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAA;AACvC,OAAO,EAAkB,SAAS,EAAE,MAAM,cAAc,CAAA;AAIxD,MAAM,OAAO,QAAS,SAAQ,SAAS;IACpB,SAAS,CAA0B;IAC5C,OAAO,GAAqB,IAAI,CAAA;IAExC,YAAmB,MAAc,EAAE,SAAmC;QACpE,KAAK,EAAE,CAAA;QAEP,IAAI,CAAC,SAAS,GAAG,SAAS,CAAA;QAE1B,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAA;IAChD,CAAC;IAEkB,KAAK,CAAC,IAAI;QAC3B,+EAA+E;QAC/E,kFAAkF;QAClF,wEAAwE;QACxE,IAAI,CAAC,OAAO,GAAG,MAAM,IAAI,CAAC,SAAS,EAAE,CAAA;QACrC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;QAE1B,MAAM,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,CAAA;QAE5B,OAAO,CAAC,IAAI,CAAC,0BAA0B,CAAC,CAAA;IAC1C,CAAC;IAEkB,OAAO;QACxB,OAAO,CAAC,IAAI,CAAC,oCAAoC,CAAC,CAAA;IACpD,CAAC;IAEO,IAAI,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAQ;QACxC,MAAM,UAAU,GACd,SAAS,KAAK,IAAI;YAChB,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC;YACrB,CAAC,CAAC,GAAG,CAAC,SAAS,EAAE,KAAK,IAAI,EAAE,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAA;QAE3D,KAAK,UAAU,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,sBAAsB,EAAE,KAAK,CAAC,CAAC,CAAA;IAChF,CAAC;IAEO,KAAK,CAAC,OAAO,CAAC,KAA8B;QAClD;;;;WAIG;QACH,MAAM,QAAQ,GAAG;YACf,IAAI,EAAE,GAAG,KAAK,CAAC,KAAK,UAAU;YAC9B,IAAI,EAAE,UAAmB;YACzB,OAAO,EAAE,KAAK,CAAC,KAAK;YACpB,UAAU,EAAE,EAAE,4BAA4B,EAAE,KAAK,CAAC,KAAK,EAAE;SAC1D,CAAA;QAED,MAAM,OAAO,GAAG;YACd,IAAI,EAAE,GAAG,KAAK,CAAC,KAAK,OAAO;YAC3B,IAAI,EAAE,UAAmB;YACzB,OAAO,EAAE,UAAU;YACnB,UAAU,EAAE,EAAE,4BAA4B,EAAE,KAAK,CAAC,KAAK,EAAE;SAC1D,CAAA;QAED,MAAM,OAAO,CAAC,IAAI,CAChB,QAAQ,EACR,KAAK,IAAI,EAAE,CACT,MAAM,OAAO,CAAC,IAAI,CAAC,OAAO,EAAE,KAAK,IAAI,EAAE;YACrC,MAAM,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAA;QACtD,CAAC,CAAC,CACL,CAAA;IACH,CAAC;CACF"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { Connector } from '@toa.io/core';
|
|
2
|
+
import type { Message } from '@toa.io/core/types';
|
|
3
|
+
import type { SpanContext } from 'openspan';
|
|
4
|
+
import type { Readable } from 'node:stream';
|
|
5
|
+
export declare class Receiver extends Connector {
|
|
6
|
+
private readonly event;
|
|
7
|
+
private readonly properties;
|
|
8
|
+
private readonly expose?;
|
|
9
|
+
private readonly stream;
|
|
10
|
+
constructor({ event, properties, stream, expose }: {
|
|
11
|
+
event: string;
|
|
12
|
+
properties: string[];
|
|
13
|
+
stream: Readable;
|
|
14
|
+
expose?: string[];
|
|
15
|
+
});
|
|
16
|
+
receive(message: Message<Record<string, string>>): Promise<void>;
|
|
17
|
+
private process;
|
|
18
|
+
private fit;
|
|
19
|
+
private push;
|
|
20
|
+
}
|
|
21
|
+
export interface Push {
|
|
22
|
+
key: string;
|
|
23
|
+
event: string;
|
|
24
|
+
data: Record<string, string>;
|
|
25
|
+
telemetry: SpanContext | null;
|
|
26
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { console, decode, run } from 'openspan';
|
|
2
|
+
import { Connector } from '@toa.io/core';
|
|
3
|
+
export class Receiver extends Connector {
|
|
4
|
+
event;
|
|
5
|
+
properties;
|
|
6
|
+
expose;
|
|
7
|
+
stream;
|
|
8
|
+
constructor({ event, properties, stream, expose }) {
|
|
9
|
+
super();
|
|
10
|
+
this.event = event;
|
|
11
|
+
this.properties = properties;
|
|
12
|
+
this.expose = expose;
|
|
13
|
+
this.stream = stream;
|
|
14
|
+
}
|
|
15
|
+
async receive(message) {
|
|
16
|
+
// the push continues the trace from the producer
|
|
17
|
+
const telemetry = message.telemetry === undefined ? null : decode(message.telemetry);
|
|
18
|
+
if (telemetry === null)
|
|
19
|
+
this.process(message, telemetry);
|
|
20
|
+
else
|
|
21
|
+
run(telemetry, () => this.process(message, telemetry));
|
|
22
|
+
}
|
|
23
|
+
process(message, telemetry) {
|
|
24
|
+
const data = this.fit(message.payload);
|
|
25
|
+
for (const property of this.properties) {
|
|
26
|
+
const key = message.payload[property];
|
|
27
|
+
if (key === undefined) {
|
|
28
|
+
console.debug('Event does not contain key property', {
|
|
29
|
+
property,
|
|
30
|
+
event: this.event
|
|
31
|
+
});
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
if (Array.isArray(key))
|
|
35
|
+
// eslint-disable-next-line max-depth
|
|
36
|
+
for (const k of key)
|
|
37
|
+
this.push(k, data, telemetry);
|
|
38
|
+
else
|
|
39
|
+
this.push(key, data, telemetry);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
fit(payload) {
|
|
43
|
+
if (this.expose === undefined)
|
|
44
|
+
return payload;
|
|
45
|
+
const entries = Object.entries(payload).filter(([key]) => this.expose.includes(key));
|
|
46
|
+
return Object.fromEntries(entries);
|
|
47
|
+
}
|
|
48
|
+
push(key, data, telemetry) {
|
|
49
|
+
if (key === null || typeof key === 'undefined') {
|
|
50
|
+
console.debug('Key is null or undefined, skipping', { key, event: this.event });
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
console.debug('Pushing event to stream', { key, event: this.event, data });
|
|
54
|
+
this.stream.push({ key, event: this.event, data, telemetry });
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=Receiver.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Receiver.js","sourceRoot":"","sources":["../source/Receiver.ts"],"names":[],"mappings":"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"}
|
package/transpiled/Routes.d.ts
CHANGED
|
@@ -1,22 +1,17 @@
|
|
|
1
|
-
/// <reference types="node" />
|
|
2
1
|
import { Readable } from 'node:stream';
|
|
3
2
|
import { Connector } from '@toa.io/core';
|
|
4
|
-
import {
|
|
3
|
+
import type { Route } from './extension.js';
|
|
4
|
+
import type { Host } from './Factory.js';
|
|
5
5
|
export declare class Routes extends Connector {
|
|
6
6
|
events: Events;
|
|
7
|
-
private readonly
|
|
8
|
-
constructor(
|
|
7
|
+
private readonly host;
|
|
8
|
+
constructor(host: Host);
|
|
9
9
|
private static read;
|
|
10
10
|
open(): Promise<void>;
|
|
11
11
|
close(): Promise<void>;
|
|
12
|
-
private getReceiver;
|
|
13
12
|
}
|
|
14
13
|
declare class Events extends Readable {
|
|
15
14
|
constructor();
|
|
16
15
|
_read(): void;
|
|
17
16
|
}
|
|
18
|
-
export
|
|
19
|
-
event: string;
|
|
20
|
-
properties: string[];
|
|
21
|
-
}
|
|
22
|
-
export {};
|
|
17
|
+
export type { Route };
|
package/transpiled/Routes.js
CHANGED
|
@@ -1,26 +1,26 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
class Routes extends core_1.Connector {
|
|
1
|
+
import { Readable } from 'node:stream';
|
|
2
|
+
import { console } from 'openspan';
|
|
3
|
+
import { Connector } from '@toa.io/core';
|
|
4
|
+
import { Receiver } from './Receiver.js';
|
|
5
|
+
import { environment } from '@toa.io/generic';
|
|
6
|
+
export class Routes extends Connector {
|
|
8
7
|
events = new Events();
|
|
9
|
-
|
|
10
|
-
constructor(
|
|
8
|
+
host;
|
|
9
|
+
constructor(host) {
|
|
11
10
|
super();
|
|
12
|
-
this.
|
|
11
|
+
this.host = host;
|
|
13
12
|
}
|
|
14
13
|
static read() {
|
|
15
|
-
|
|
14
|
+
const value = environment.get('TOA_REALTIME');
|
|
15
|
+
if (value === undefined)
|
|
16
16
|
throw new Error('TOA_REALTIME is not defined');
|
|
17
|
-
return
|
|
17
|
+
return JSON.parse(value);
|
|
18
18
|
}
|
|
19
19
|
async open() {
|
|
20
20
|
const routes = Routes.read();
|
|
21
21
|
const creating = [];
|
|
22
|
-
for (const { event, properties } of routes) {
|
|
23
|
-
const consumer = this.
|
|
22
|
+
for (const { event, properties, expose } of routes) {
|
|
23
|
+
const consumer = this.host.receive(event, new Receiver({ event, properties, stream: this.events, expose }));
|
|
24
24
|
creating.push(consumer);
|
|
25
25
|
}
|
|
26
26
|
const consumers = await Promise.all(creating);
|
|
@@ -28,32 +28,16 @@ class Routes extends core_1.Connector {
|
|
|
28
28
|
const connecting = consumers.map((consumer) => consumer.connect());
|
|
29
29
|
await Promise.all(connecting);
|
|
30
30
|
this.depends(consumers);
|
|
31
|
-
console.
|
|
31
|
+
console.info('Event sources connected', { count: creating.length });
|
|
32
32
|
}
|
|
33
33
|
async close() {
|
|
34
|
-
console.
|
|
35
|
-
}
|
|
36
|
-
getReceiver(event, properties) {
|
|
37
|
-
return {
|
|
38
|
-
receive: (message) => {
|
|
39
|
-
for (const property of properties) {
|
|
40
|
-
const key = message.payload[property];
|
|
41
|
-
if (key === undefined) {
|
|
42
|
-
console.error(`Event '${event}' does not contain the '${property}' property.`);
|
|
43
|
-
return;
|
|
44
|
-
}
|
|
45
|
-
this.events.push({ key, event, data: message.payload });
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
};
|
|
34
|
+
console.info('Event sources disconnected');
|
|
49
35
|
}
|
|
50
36
|
}
|
|
51
|
-
|
|
52
|
-
class Events extends node_stream_1.Readable {
|
|
37
|
+
class Events extends Readable {
|
|
53
38
|
constructor() {
|
|
54
39
|
super({ objectMode: true });
|
|
55
40
|
}
|
|
56
|
-
_read() {
|
|
57
|
-
}
|
|
41
|
+
_read() { }
|
|
58
42
|
}
|
|
59
43
|
//# sourceMappingURL=Routes.js.map
|
package/transpiled/Routes.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Routes.js","sourceRoot":"","sources":["../source/Routes.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"Routes.js","sourceRoot":"","sources":["../source/Routes.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAA;AACtC,OAAO,EAAE,OAAO,EAAE,MAAM,UAAU,CAAA;AAClC,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AACxC,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AACxC,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAI7C,MAAM,OAAO,MAAO,SAAQ,SAAS;IAC5B,MAAM,GAAG,IAAI,MAAM,EAAE,CAAA;IAEX,IAAI,CAAM;IAE3B,YAAmB,IAAU;QAC3B,KAAK,EAAE,CAAA;QAEP,IAAI,CAAC,IAAI,GAAG,IAAI,CAAA;IAClB,CAAC;IAEO,MAAM,CAAC,IAAI;QACjB,MAAM,KAAK,GAAG,WAAW,CAAC,GAAG,CAAC,cAAc,CAAC,CAAA;QAE7C,IAAI,KAAK,KAAK,SAAS;YAAE,MAAM,IAAI,KAAK,CAAC,6BAA6B,CAAC,CAAA;QAEvE,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,CAAY,CAAA;IACrC,CAAC;IAEe,KAAK,CAAC,IAAI;QACxB,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,EAAE,CAAA;QAC5B,MAAM,QAAQ,GAAG,EAAE,CAAA;QAEnB,KAAK,MAAM,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,IAAI,MAAM,EAAE,CAAC;YACnD,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,CAChC,KAAK,EACL,IAAI,QAAQ,CAAC,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CACjE,CAAA;YAED,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;QACzB,CAAC;QAED,MAAM,SAAS,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA;QAE7C,qEAAqE;QACrE,MAAM,UAAU,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAA;QAElE,MAAM,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,CAAA;QAC7B,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,CAAA;QAEvB,OAAO,CAAC,IAAI,CAAC,yBAAyB,EAAE,EAAE,KAAK,EAAE,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAA;IACrE,CAAC;IAEe,KAAK,CAAC,KAAK;QACzB,OAAO,CAAC,IAAI,CAAC,4BAA4B,CAAC,CAAA;IAC5C,CAAC;CACF;AAED,MAAM,MAAO,SAAQ,QAAQ;IAC3B;QACE,KAAK,CAAC,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC,CAAA;IAC7B,CAAC;IAEe,KAAK,KAAU,CAAC;CACjC"}
|