@toa.io/extensions.realtime 1.0.0-alpha.15 → 1.0.0-alpha.154

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.
Files changed (49) hide show
  1. package/components/streams/manifest.toa.yaml +0 -1
  2. package/components/streams/operations/create.d.ts +4 -2
  3. package/components/streams/operations/create.js +15 -9
  4. package/components/streams/operations/create.js.map +1 -1
  5. package/components/streams/operations/lib/{stream.d.ts → Stream.d.ts} +5 -1
  6. package/components/streams/operations/lib/{stream.js → Stream.js} +20 -2
  7. package/components/streams/operations/lib/Stream.js.map +1 -0
  8. package/components/streams/operations/{types.d.ts → lib/types.d.ts} +4 -1
  9. package/components/streams/operations/lib/types.js.map +1 -0
  10. package/components/streams/operations/push.d.ts +1 -1
  11. package/components/streams/operations/tsconfig.tsbuildinfo +1 -1
  12. package/components/streams/source/create.ts +20 -9
  13. package/components/streams/source/lib/Stream.ts +61 -0
  14. package/components/streams/source/{types.ts → lib/types.ts} +4 -1
  15. package/components/streams/source/push.ts +1 -1
  16. package/features/static.feature +34 -0
  17. package/features/steps/Realtime.ts +1 -1
  18. package/features/steps/components/messages/manifest.toa.yaml +2 -0
  19. package/package.json +6 -2
  20. package/readme.md +19 -6
  21. package/source/Composition.ts +1 -7
  22. package/source/Realtime.ts +4 -2
  23. package/source/Receiver.ts +49 -0
  24. package/source/Routes.ts +6 -26
  25. package/source/extension.ts +62 -0
  26. package/source/index.ts +1 -0
  27. package/transpiled/Composition.d.ts +1 -1
  28. package/transpiled/Composition.js +2 -5
  29. package/transpiled/Composition.js.map +1 -1
  30. package/transpiled/Realtime.js +5 -3
  31. package/transpiled/Realtime.js.map +1 -1
  32. package/transpiled/Receiver.d.ts +11 -0
  33. package/transpiled/Receiver.js +41 -0
  34. package/transpiled/Receiver.js.map +1 -0
  35. package/transpiled/Routes.d.ts +0 -1
  36. package/transpiled/Routes.js +5 -17
  37. package/transpiled/Routes.js.map +1 -1
  38. package/transpiled/extension.d.ts +5 -0
  39. package/transpiled/extension.js +45 -0
  40. package/transpiled/extension.js.map +1 -0
  41. package/transpiled/index.d.ts +1 -0
  42. package/transpiled/index.js +15 -0
  43. package/transpiled/index.js.map +1 -1
  44. package/transpiled/tsconfig.tsbuildinfo +1 -1
  45. package/components/streams/operations/lib/stream.js.map +0 -1
  46. package/components/streams/operations/types.js.map +0 -1
  47. package/components/streams/source/lib/stream.ts +0 -37
  48. package/stage/streams.test.ts +0 -128
  49. /package/components/streams/operations/{types.js → lib/types.js} +0 -0
@@ -30,3 +30,37 @@ Feature: Static routes
30
30
  recipient: 004e02a959c04cecaf111827f91caa36
31
31
  text: Hello!
32
32
  """
33
+
34
+ Scenario: Routing an event using array
35
+ Given the `messages` component is running with routes:
36
+ """yaml
37
+ created: watchers
38
+ """
39
+ And the stream `51c15a7290ce47e0af8ec41d60dccb32` is consumed
40
+ And the stream `bb27366509a64178a39313aac42435ae` is consumed
41
+ When the `messages.create` is called with:
42
+ """yaml
43
+ input:
44
+ watchers:
45
+ - 51c15a7290ce47e0af8ec41d60dccb32
46
+ - bb27366509a64178a39313aac42435ae
47
+ sender: 96db5a47a8244eb3b21820781b7d596e
48
+ recipient: 004e02a959c04cecaf111827f91caa36
49
+ text: Hello!
50
+ """
51
+ Then an event is received from the stream `51c15a7290ce47e0af8ec41d60dccb32`:
52
+ """yaml
53
+ event: default.messages.created
54
+ data:
55
+ sender: 96db5a47a8244eb3b21820781b7d596e
56
+ recipient: 004e02a959c04cecaf111827f91caa36
57
+ text: Hello!
58
+ """
59
+ And an event is received from the stream `bb27366509a64178a39313aac42435ae`:
60
+ """yaml
61
+ event: default.messages.created
62
+ data:
63
+ sender: 96db5a47a8244eb3b21820781b7d596e
64
+ recipient: 004e02a959c04cecaf111827f91caa36
65
+ text: Hello!
66
+ """
@@ -2,7 +2,7 @@ import * as boot from '@toa.io/boot'
2
2
  import { encode } from '@toa.io/generic'
3
3
  import { type Connector } from '@toa.io/core'
4
4
  import { after, binding } from 'cucumber-tsflow'
5
- import { Factory } from '../../source/Factory'
5
+ import { Factory } from '../../source'
6
6
 
7
7
  @binding()
8
8
  export class Realtime {
@@ -5,6 +5,7 @@ entity:
5
5
  sender*: string(32)
6
6
  recipient*: string(32)
7
7
  text*: string
8
+ watchers: [string(32)]
8
9
 
9
10
  operations:
10
11
  create:
@@ -14,3 +15,4 @@ operations:
14
15
  sender*: .
15
16
  recipient*: .
16
17
  text*: .
18
+ watchers: .
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@toa.io/extensions.realtime",
3
- "version": "1.0.0-alpha.15",
3
+ "version": "1.0.0-alpha.154",
4
4
  "description": "Toa Realtime",
5
5
  "author": "temich <tema.gurtovoy@gmail.com>",
6
6
  "homepage": "https://github.com/toa-io/toa#readme",
@@ -16,6 +16,10 @@
16
16
  "publishConfig": {
17
17
  "access": "public"
18
18
  },
19
+ "dependencies": {
20
+ "@toa.io/core": "1.0.0-alpha.154",
21
+ "openspan": "1.0.0-alpha.93"
22
+ },
19
23
  "jest": {
20
24
  "preset": "ts-jest",
21
25
  "testEnvironment": "node"
@@ -24,5 +28,5 @@
24
28
  "transpile": "npx tsc && npx tsc -p ./components/streams",
25
29
  "features": "npx cucumber-js"
26
30
  },
27
- "gitHead": "8c42f0b136162a579859d6baa7f940bd16c66821"
31
+ "gitHead": "01832fc47f9906d6a63180d5c2813db86763506a"
28
32
  }
package/readme.md CHANGED
@@ -10,17 +10,20 @@
10
10
  </a>
11
11
 
12
12
  Realtime extension combines application events into streams according to defined routes.
13
- Clients may consume these streams [via Exposition](#exposition).
13
+ Clients may consume these streams [via Exposition](/extensions/exposition).
14
+
15
+ If stream is idle for 16 seconds, a `heartbeat` message is sent.
14
16
 
15
17
  ## Static routes
16
18
 
17
19
  Static route specifies an event that should be combined into a stream using specified property of
18
- event's payload as a stream key.
20
+ event's payload as a stream key or an array of stream keys.
19
21
 
20
22
  Static routes may be defined in Component manifest or the Context annotation.
21
23
 
22
24
  ```yaml
23
25
  # manifest.toa.yaml
26
+
24
27
  name: users
25
28
 
26
29
  realtime:
@@ -29,13 +32,24 @@ realtime:
29
32
 
30
33
  ```yaml
31
34
  # context.toa.yaml
35
+
32
36
  realtime:
33
37
  users.updated: id
34
- orders.created: custromer_id
38
+ orders.created: customer_id
35
39
  ```
36
40
 
37
41
  In case of conflict, the Context annotation takes precedence.
38
42
 
43
+ Multiple stream keys may be defined for a single event.
44
+
45
+ ```yaml
46
+ # manifest.toa.yaml
47
+ name: messages
48
+
49
+ realtime:
50
+ updated: [sender_id, recipient_id]
51
+ ```
52
+
39
53
  ### Static route examples
40
54
 
41
55
  Given two rules: `users.updated: id` and `orders.created: customer_id`,
@@ -92,8 +106,7 @@ Realtime extension, and are
92
106
  accessible via the `/realtime/streams/:key/` resource with
93
107
  the [`auth:id: key`](/extensions/exposition/documentation/access.md#id) authorization rule.
94
108
 
95
- Refer to the [Exposition extension](/extensions/exposition) for more
96
- details:
109
+ Refer to the [Exposition extension](/extensions/exposition) for more details:
97
110
 
98
- - [Streams](/extensions/exposition/documentation/protocol.md#streams)
111
+ - [Multipart responses](/extensions/exposition/documentation/protocol.md#multipart-types)
99
112
  - [Access authorization](/extensions/exposition/documentation/access.md)
@@ -18,16 +18,10 @@ export class Composition extends Connector {
18
18
  await composition.connect()
19
19
 
20
20
  this.depends(composition)
21
-
22
- console.info('Composition complete.')
23
- }
24
-
25
- protected override dispose (): void {
26
- console.info('Composition shutdown complete.')
27
21
  }
28
22
  }
29
23
 
30
- function find (): string[] {
24
+ export function find (): string[] {
31
25
  return entries().map((entry) => resolve(ROOT, entry.name))
32
26
  }
33
27
 
@@ -1,3 +1,4 @@
1
+ import { console } from 'openspan'
1
2
  import { type Component, Connector } from '@toa.io/core'
2
3
  import { type Routes } from './Routes'
3
4
 
@@ -19,15 +20,16 @@ export class Realtime extends Connector {
19
20
 
20
21
  await this.streams.connect()
21
22
 
22
- console.log('Realtime has started.')
23
+ console.info('Realtime service started')
23
24
  }
24
25
 
25
26
  protected override dispose (): void {
26
- console.log('Realtime shutdown complete.')
27
+ console.info('Realtime service shutdown complete')
27
28
  }
28
29
 
29
30
  private push (event: Event): void {
30
31
  void this.streams?.invoke('push', { input: event })
32
+ .catch((error) => console.error('Realtime push failed', error))
31
33
  }
32
34
  }
33
35
 
@@ -0,0 +1,49 @@
1
+ import { console } from 'openspan'
2
+ import { Connector, type Message } from '@toa.io/core'
3
+ import type { Readable } from 'node:stream'
4
+
5
+ export class Receiver extends Connector {
6
+ private readonly event: string
7
+ private readonly properties: string[]
8
+ private readonly stream: Readable
9
+
10
+ public constructor (event: string, properties: string[], stream: Readable) {
11
+ super()
12
+
13
+ this.event = event
14
+ this.properties = properties
15
+ this.stream = stream
16
+ }
17
+
18
+ public receive (message: Message<Record<string, string>>): void {
19
+ for (const property of this.properties) {
20
+ const key = message.payload[property]
21
+
22
+ if (key === undefined) {
23
+ console.debug('Event does not contain key property',
24
+ { property, event: this.event })
25
+
26
+ continue
27
+ }
28
+
29
+ if (Array.isArray(key))
30
+ // eslint-disable-next-line max-depth
31
+ for (const k of key as string[])
32
+ this.push(k, message.payload)
33
+ else
34
+ this.push(key, message.payload)
35
+ }
36
+ }
37
+
38
+ private push (key: string | null, data: Record<string, string>): void {
39
+ if (key === null || typeof key === 'undefined') {
40
+ console.debug('Key is null or undefined, skipping', { key, event: this.event })
41
+
42
+ return
43
+ }
44
+
45
+ console.debug('Pushing event to stream', { key, event: this.event, data })
46
+
47
+ this.stream.push({ key, event: this.event, data })
48
+ }
49
+ }
package/source/Routes.ts CHANGED
@@ -1,7 +1,9 @@
1
1
  import { Readable } from 'node:stream'
2
- import { Connector, type Message } from '@toa.io/core'
2
+ import { console } from 'openspan'
3
+ import { Connector } from '@toa.io/core'
3
4
  import { decode } from '@toa.io/generic'
4
5
  import { type Bootloader } from './Factory'
6
+ import { Receiver } from './Receiver'
5
7
 
6
8
  export class Routes extends Connector {
7
9
  public events = new Events()
@@ -26,7 +28,7 @@ export class Routes extends Connector {
26
28
  const creating = []
27
29
 
28
30
  for (const { event, properties } of routes) {
29
- const consumer = this.boot.receive(event, this.getReceiver(event, properties))
31
+ const consumer = this.boot.receive(event, new Receiver(event, properties, this.events))
30
32
 
31
33
  creating.push(consumer)
32
34
  }
@@ -39,29 +41,11 @@ export class Routes extends Connector {
39
41
  await Promise.all(connecting)
40
42
  this.depends(consumers)
41
43
 
42
- console.log(`Event sources (${creating.length}) connected.`)
44
+ console.info('Event sources connected', { count: creating.length })
43
45
  }
44
46
 
45
47
  public override async close (): Promise<void> {
46
- console.log('Event sources disconnected.')
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
- }
48
+ console.info('Event sources disconnected')
65
49
  }
66
50
  }
67
51
 
@@ -78,7 +62,3 @@ export interface Route {
78
62
  event: string
79
63
  properties: string[]
80
64
  }
81
-
82
- interface Receiver {
83
- receive: (message: Message<Record<string, string>>) => void
84
- }
@@ -0,0 +1,62 @@
1
+ import { basename } from 'node:path'
2
+ import { encode } from '@toa.io/generic'
3
+ import { find } from './Composition'
4
+ import type { Dependency, Instances, Service } from '@toa.io/operations'
5
+
6
+ export const standalone = true
7
+
8
+ export function deployment (instances: Instances<Declaration>, annotation?: Declaration): Dependency {
9
+ const routes = []
10
+
11
+ if (annotation !== undefined)
12
+ routes.push(...parse(annotation))
13
+
14
+ for (const instance of instances) {
15
+ const completed: Declaration = {}
16
+
17
+ for (const [key, value] of Object.entries(instance.manifest)) {
18
+ const event = instance.locator.id + '.' + key
19
+
20
+ completed[event] = value
21
+ }
22
+
23
+ routes.push(...parse(completed))
24
+ }
25
+
26
+ const service: Service = {
27
+ group: 'realtime',
28
+ name: 'streams',
29
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
30
+ version: require('../package.json').version,
31
+ components: labels(),
32
+ variables: [{
33
+ name: 'TOA_REALTIME',
34
+ value: encode(routes)
35
+ }]
36
+ }
37
+
38
+ return { services: [service] }
39
+ }
40
+
41
+ function parse (declaration: Declaration): Route[] {
42
+ const routes: Route[] = []
43
+
44
+ for (const [event, value] of Object.entries(declaration)) {
45
+ const properties = Array.isArray(value) ? value : [value]
46
+
47
+ routes.push({ event, properties })
48
+ }
49
+
50
+ return routes
51
+ }
52
+
53
+ function labels (): string[] {
54
+ return find().map((path) => 'realtime-' + basename(path))
55
+ }
56
+
57
+ type Declaration = Record<string, string | string[]>
58
+
59
+ interface Route {
60
+ event: string
61
+ properties: string[]
62
+ }
package/source/index.ts CHANGED
@@ -1 +1,2 @@
1
1
  export { Factory } from './Factory'
2
+ export * from './extension'
@@ -4,5 +4,5 @@ export declare class Composition extends Connector {
4
4
  private readonly boot;
5
5
  constructor(boot: Bootloader);
6
6
  protected open(): Promise<void>;
7
- protected dispose(): void;
8
7
  }
8
+ export declare function find(): string[];
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.Composition = void 0;
3
+ exports.find = exports.Composition = void 0;
4
4
  const node_fs_1 = require("node:fs");
5
5
  const node_path_1 = require("node:path");
6
6
  const core_1 = require("@toa.io/core");
@@ -15,16 +15,13 @@ class Composition extends core_1.Connector {
15
15
  const composition = await this.boot.composition(paths);
16
16
  await composition.connect();
17
17
  this.depends(composition);
18
- console.info('Composition complete.');
19
- }
20
- dispose() {
21
- console.info('Composition shutdown complete.');
22
18
  }
23
19
  }
24
20
  exports.Composition = Composition;
25
21
  function find() {
26
22
  return entries().map((entry) => (0, node_path_1.resolve)(ROOT, entry.name));
27
23
  }
24
+ exports.find = find;
28
25
  function entries() {
29
26
  const entries = (0, node_fs_1.readdirSync)(ROOT, { withFileTypes: true });
30
27
  return entries.filter((entry) => entry.isDirectory());
@@ -1 +1 @@
1
- {"version":3,"file":"Composition.js","sourceRoot":"","sources":["../source/Composition.ts"],"names":[],"mappings":";;;AAAA,qCAAkD;AAClD,yCAAmC;AACnC,uCAAwC;AAGxC,MAAa,WAAY,SAAQ,gBAAS;IACvB,IAAI,CAAY;IAEjC,YAAoB,IAAgB;QAClC,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;QAEzB,OAAO,CAAC,IAAI,CAAC,uBAAuB,CAAC,CAAA;IACvC,CAAC;IAEkB,OAAO;QACxB,OAAO,CAAC,IAAI,CAAC,gCAAgC,CAAC,CAAA;IAChD,CAAC;CACF;AAtBD,kCAsBC;AAED,SAAS,IAAI;IACX,OAAO,OAAO,EAAE,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,IAAA,mBAAO,EAAC,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,CAAA;AAC5D,CAAC;AAED,SAAS,OAAO;IACd,MAAM,OAAO,GAAG,IAAA,qBAAW,EAAC,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,IAAI,GAAG,IAAA,mBAAO,EAAC,SAAS,EAAE,gBAAgB,CAAC,CAAA"}
1
+ {"version":3,"file":"Composition.js","sourceRoot":"","sources":["../source/Composition.ts"],"names":[],"mappings":";;;AAAA,qCAAkD;AAClD,yCAAmC;AACnC,uCAAwC;AAGxC,MAAa,WAAY,SAAQ,gBAAS;IACvB,IAAI,CAAY;IAEjC,YAAoB,IAAgB;QAClC,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;AAhBD,kCAgBC;AAED,SAAgB,IAAI;IAClB,OAAO,OAAO,EAAE,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,IAAA,mBAAO,EAAC,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,CAAA;AAC5D,CAAC;AAFD,oBAEC;AAED,SAAS,OAAO;IACd,MAAM,OAAO,GAAG,IAAA,qBAAW,EAAC,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,IAAI,GAAG,IAAA,mBAAO,EAAC,SAAS,EAAE,gBAAgB,CAAC,CAAA"}
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.Realtime = void 0;
4
+ const openspan_1 = require("openspan");
4
5
  const core_1 = require("@toa.io/core");
5
6
  class Realtime extends core_1.Connector {
6
7
  discovery;
@@ -14,13 +15,14 @@ class Realtime extends core_1.Connector {
14
15
  this.streams = await this.discovery;
15
16
  this.depends(this.streams);
16
17
  await this.streams.connect();
17
- console.log('Realtime has started.');
18
+ openspan_1.console.info('Realtime service started');
18
19
  }
19
20
  dispose() {
20
- console.log('Realtime shutdown complete.');
21
+ openspan_1.console.info('Realtime service shutdown complete');
21
22
  }
22
23
  push(event) {
23
- void this.streams?.invoke('push', { input: event });
24
+ void this.streams?.invoke('push', { input: event })
25
+ .catch((error) => openspan_1.console.error('Realtime push failed', error));
24
26
  }
25
27
  }
26
28
  exports.Realtime = Realtime;
@@ -1 +1 @@
1
- {"version":3,"file":"Realtime.js","sourceRoot":"","sources":["../source/Realtime.ts"],"names":[],"mappings":";;;AAAA,uCAAwD;AAGxD,MAAa,QAAS,SAAQ,gBAAS;IACpB,SAAS,CAAoB;IACtC,OAAO,GAAqB,IAAI,CAAA;IAExC,YAAoB,MAAc,EAAE,SAA6B;QAC/D,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,IAAI,CAAC,OAAO,GAAG,MAAM,IAAI,CAAC,SAAS,CAAA;QACnC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;QAE1B,MAAM,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,CAAA;QAE5B,OAAO,CAAC,GAAG,CAAC,uBAAuB,CAAC,CAAA;IACtC,CAAC;IAEkB,OAAO;QACxB,OAAO,CAAC,GAAG,CAAC,6BAA6B,CAAC,CAAA;IAC5C,CAAC;IAEO,IAAI,CAAE,KAAY;QACxB,KAAK,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAA;IACrD,CAAC;CACF;AA5BD,4BA4BC"}
1
+ {"version":3,"file":"Realtime.js","sourceRoot":"","sources":["../source/Realtime.ts"],"names":[],"mappings":";;;AAAA,uCAAkC;AAClC,uCAAwD;AAGxD,MAAa,QAAS,SAAQ,gBAAS;IACpB,SAAS,CAAoB;IACtC,OAAO,GAAqB,IAAI,CAAA;IAExC,YAAoB,MAAc,EAAE,SAA6B;QAC/D,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,IAAI,CAAC,OAAO,GAAG,MAAM,IAAI,CAAC,SAAS,CAAA;QACnC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;QAE1B,MAAM,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,CAAA;QAE5B,kBAAO,CAAC,IAAI,CAAC,0BAA0B,CAAC,CAAA;IAC1C,CAAC;IAEkB,OAAO;QACxB,kBAAO,CAAC,IAAI,CAAC,oCAAoC,CAAC,CAAA;IACpD,CAAC;IAEO,IAAI,CAAE,KAAY;QACxB,KAAK,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;aAChD,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,kBAAO,CAAC,KAAK,CAAC,sBAAsB,EAAE,KAAK,CAAC,CAAC,CAAA;IACnE,CAAC;CACF;AA7BD,4BA6BC"}
@@ -0,0 +1,11 @@
1
+ /// <reference types="node" />
2
+ import { Connector, type Message } from '@toa.io/core';
3
+ import type { Readable } from 'node:stream';
4
+ export declare class Receiver extends Connector {
5
+ private readonly event;
6
+ private readonly properties;
7
+ private readonly stream;
8
+ constructor(event: string, properties: string[], stream: Readable);
9
+ receive(message: Message<Record<string, string>>): void;
10
+ private push;
11
+ }
@@ -0,0 +1,41 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Receiver = void 0;
4
+ const openspan_1 = require("openspan");
5
+ const core_1 = require("@toa.io/core");
6
+ class Receiver extends core_1.Connector {
7
+ event;
8
+ properties;
9
+ stream;
10
+ constructor(event, properties, stream) {
11
+ super();
12
+ this.event = event;
13
+ this.properties = properties;
14
+ this.stream = stream;
15
+ }
16
+ receive(message) {
17
+ for (const property of this.properties) {
18
+ const key = message.payload[property];
19
+ if (key === undefined) {
20
+ openspan_1.console.debug('Event does not contain key property', { property, event: this.event });
21
+ continue;
22
+ }
23
+ if (Array.isArray(key))
24
+ // eslint-disable-next-line max-depth
25
+ for (const k of key)
26
+ this.push(k, message.payload);
27
+ else
28
+ this.push(key, message.payload);
29
+ }
30
+ }
31
+ push(key, data) {
32
+ if (key === null || typeof key === 'undefined') {
33
+ openspan_1.console.debug('Key is null or undefined, skipping', { key, event: this.event });
34
+ return;
35
+ }
36
+ openspan_1.console.debug('Pushing event to stream', { key, event: this.event, data });
37
+ this.stream.push({ key, event: this.event, data });
38
+ }
39
+ }
40
+ exports.Receiver = Receiver;
41
+ //# sourceMappingURL=Receiver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Receiver.js","sourceRoot":"","sources":["../source/Receiver.ts"],"names":[],"mappings":";;;AAAA,uCAAkC;AAClC,uCAAsD;AAGtD,MAAa,QAAS,SAAQ,gBAAS;IACpB,KAAK,CAAQ;IACb,UAAU,CAAU;IACpB,MAAM,CAAU;IAEjC,YAAoB,KAAa,EAAE,UAAoB,EAAE,MAAgB;QACvE,KAAK,EAAE,CAAA;QAEP,IAAI,CAAC,KAAK,GAAG,KAAK,CAAA;QAClB,IAAI,CAAC,UAAU,GAAG,UAAU,CAAA;QAC5B,IAAI,CAAC,MAAM,GAAG,MAAM,CAAA;IACtB,CAAC;IAEM,OAAO,CAAE,OAAwC;QACtD,KAAK,MAAM,QAAQ,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YACvC,MAAM,GAAG,GAAG,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAA;YAErC,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;gBACtB,kBAAO,CAAC,KAAK,CAAC,qCAAqC,EACjD,EAAE,QAAQ,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC,CAAA;gBAElC,SAAQ;YACV,CAAC;YAED,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC;gBACpB,qCAAqC;gBACrC,KAAK,MAAM,CAAC,IAAI,GAAe;oBAC7B,IAAI,CAAC,IAAI,CAAC,CAAC,EAAE,OAAO,CAAC,OAAO,CAAC,CAAA;;gBAE/B,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,OAAO,CAAC,OAAO,CAAC,CAAA;QACnC,CAAC;IACH,CAAC;IAEO,IAAI,CAAE,GAAkB,EAAE,IAA4B;QAC5D,IAAI,GAAG,KAAK,IAAI,IAAI,OAAO,GAAG,KAAK,WAAW,EAAE,CAAC;YAC/C,kBAAO,CAAC,KAAK,CAAC,oCAAoC,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC,CAAA;YAE/E,OAAM;QACR,CAAC;QAED,kBAAO,CAAC,KAAK,CAAC,yBAAyB,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAA;QAE1E,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAA;IACpD,CAAC;CACF;AA5CD,4BA4CC"}
@@ -9,7 +9,6 @@ export declare class Routes extends Connector {
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();
@@ -2,8 +2,10 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.Routes = void 0;
4
4
  const node_stream_1 = require("node:stream");
5
+ const openspan_1 = require("openspan");
5
6
  const core_1 = require("@toa.io/core");
6
7
  const generic_1 = require("@toa.io/generic");
8
+ const Receiver_1 = require("./Receiver");
7
9
  class Routes extends core_1.Connector {
8
10
  events = new Events();
9
11
  boot;
@@ -20,7 +22,7 @@ class Routes extends core_1.Connector {
20
22
  const routes = Routes.read();
21
23
  const creating = [];
22
24
  for (const { event, properties } of routes) {
23
- const consumer = this.boot.receive(event, this.getReceiver(event, properties));
25
+ const consumer = this.boot.receive(event, new Receiver_1.Receiver(event, properties, this.events));
24
26
  creating.push(consumer);
25
27
  }
26
28
  const consumers = await Promise.all(creating);
@@ -28,24 +30,10 @@ class Routes extends core_1.Connector {
28
30
  const connecting = consumers.map((consumer) => consumer.connect());
29
31
  await Promise.all(connecting);
30
32
  this.depends(consumers);
31
- console.log(`Event sources (${creating.length}) connected.`);
33
+ openspan_1.console.info('Event sources connected', { count: creating.length });
32
34
  }
33
35
  async close() {
34
- console.log('Event sources disconnected.');
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
- };
36
+ openspan_1.console.info('Event sources disconnected');
49
37
  }
50
38
  }
51
39
  exports.Routes = Routes;
@@ -1 +1 @@
1
- {"version":3,"file":"Routes.js","sourceRoot":"","sources":["../source/Routes.ts"],"names":[],"mappings":";;;AAAA,6CAAsC;AACtC,uCAAsD;AACtD,6CAAwC;AAGxC,MAAa,MAAO,SAAQ,gBAAS;IAC5B,MAAM,GAAG,IAAI,MAAM,EAAE,CAAA;IAEX,IAAI,CAAY;IAEjC,YAAoB,IAAgB;QAClC,KAAK,EAAE,CAAA;QAEP,IAAI,CAAC,IAAI,GAAG,IAAI,CAAA;IAClB,CAAC;IAEO,MAAM,CAAC,IAAI;QACjB,IAAI,OAAO,CAAC,GAAG,CAAC,YAAY,KAAK,SAAS;YACxC,MAAM,IAAI,KAAK,CAAC,6BAA6B,CAAC,CAAA;QAEhD,OAAO,IAAA,gBAAM,EAAU,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,CAAA;IAClD,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,IAAI,MAAM,EAAE,CAAC;YAC3C,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC,CAAA;YAE9E,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,GAAG,CAAC,kBAAkB,QAAQ,CAAC,MAAM,cAAc,CAAC,CAAA;IAC9D,CAAC;IAEe,KAAK,CAAC,KAAK;QACzB,OAAO,CAAC,GAAG,CAAC,6BAA6B,CAAC,CAAA;IAC5C,CAAC;IAEO,WAAW,CAAE,KAAa,EAAE,UAAoB;QACtD,OAAO;YACL,OAAO,EAAE,CAAC,OAAwC,EAAE,EAAE;gBACpD,KAAK,MAAM,QAAQ,IAAI,UAAU,EAAE,CAAC;oBAClC,MAAM,GAAG,GAAG,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAA;oBAErC,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;wBACtB,OAAO,CAAC,KAAK,CAAC,UAAU,KAAK,2BAA2B,QAAQ,aAAa,CAAC,CAAA;wBAE9E,OAAM;oBACR,CAAC;oBAED,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,CAAC,OAAO,EAAE,CAAC,CAAA;gBACzD,CAAC;YACH,CAAC;SACF,CAAA;IACH,CAAC;CACF;AA5DD,wBA4DC;AAED,MAAM,MAAO,SAAQ,sBAAQ;IAC3B;QACE,KAAK,CAAC,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC,CAAA;IAC7B,CAAC;IAEe,KAAK;IACrB,CAAC;CACF"}
1
+ {"version":3,"file":"Routes.js","sourceRoot":"","sources":["../source/Routes.ts"],"names":[],"mappings":";;;AAAA,6CAAsC;AACtC,uCAAkC;AAClC,uCAAwC;AACxC,6CAAwC;AAExC,yCAAqC;AAErC,MAAa,MAAO,SAAQ,gBAAS;IAC5B,MAAM,GAAG,IAAI,MAAM,EAAE,CAAA;IAEX,IAAI,CAAY;IAEjC,YAAoB,IAAgB;QAClC,KAAK,EAAE,CAAA;QAEP,IAAI,CAAC,IAAI,GAAG,IAAI,CAAA;IAClB,CAAC;IAEO,MAAM,CAAC,IAAI;QACjB,IAAI,OAAO,CAAC,GAAG,CAAC,YAAY,KAAK,SAAS;YACxC,MAAM,IAAI,KAAK,CAAC,6BAA6B,CAAC,CAAA;QAEhD,OAAO,IAAA,gBAAM,EAAU,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,CAAA;IAClD,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,IAAI,MAAM,EAAE,CAAC;YAC3C,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,IAAI,mBAAQ,CAAC,KAAK,EAAE,UAAU,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC,CAAA;YAEvF,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,kBAAO,CAAC,IAAI,CAAC,yBAAyB,EAAE,EAAE,KAAK,EAAE,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAA;IACrE,CAAC;IAEe,KAAK,CAAC,KAAK;QACzB,kBAAO,CAAC,IAAI,CAAC,4BAA4B,CAAC,CAAA;IAC5C,CAAC;CACF;AA1CD,wBA0CC;AAED,MAAM,MAAO,SAAQ,sBAAQ;IAC3B;QACE,KAAK,CAAC,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC,CAAA;IAC7B,CAAC;IAEe,KAAK;IACrB,CAAC;CACF"}
@@ -0,0 +1,5 @@
1
+ import type { Dependency, Instances } from '@toa.io/operations';
2
+ export declare const standalone = true;
3
+ export declare function deployment(instances: Instances<Declaration>, annotation?: Declaration): Dependency;
4
+ type Declaration = Record<string, string | string[]>;
5
+ export {};
@@ -0,0 +1,45 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.deployment = exports.standalone = void 0;
4
+ const node_path_1 = require("node:path");
5
+ const generic_1 = require("@toa.io/generic");
6
+ const Composition_1 = require("./Composition");
7
+ exports.standalone = true;
8
+ function deployment(instances, annotation) {
9
+ const routes = [];
10
+ if (annotation !== undefined)
11
+ routes.push(...parse(annotation));
12
+ for (const instance of instances) {
13
+ const completed = {};
14
+ for (const [key, value] of Object.entries(instance.manifest)) {
15
+ const event = instance.locator.id + '.' + key;
16
+ completed[event] = value;
17
+ }
18
+ routes.push(...parse(completed));
19
+ }
20
+ const service = {
21
+ group: 'realtime',
22
+ name: 'streams',
23
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
24
+ version: require('../package.json').version,
25
+ components: labels(),
26
+ variables: [{
27
+ name: 'TOA_REALTIME',
28
+ value: (0, generic_1.encode)(routes)
29
+ }]
30
+ };
31
+ return { services: [service] };
32
+ }
33
+ exports.deployment = deployment;
34
+ function parse(declaration) {
35
+ const routes = [];
36
+ for (const [event, value] of Object.entries(declaration)) {
37
+ const properties = Array.isArray(value) ? value : [value];
38
+ routes.push({ event, properties });
39
+ }
40
+ return routes;
41
+ }
42
+ function labels() {
43
+ return (0, Composition_1.find)().map((path) => 'realtime-' + (0, node_path_1.basename)(path));
44
+ }
45
+ //# sourceMappingURL=extension.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extension.js","sourceRoot":"","sources":["../source/extension.ts"],"names":[],"mappings":";;;AAAA,yCAAoC;AACpC,6CAAwC;AACxC,+CAAoC;AAGvB,QAAA,UAAU,GAAG,IAAI,CAAA;AAE9B,SAAgB,UAAU,CAAE,SAAiC,EAAE,UAAwB;IACrF,MAAM,MAAM,GAAG,EAAE,CAAA;IAEjB,IAAI,UAAU,KAAK,SAAS;QAC1B,MAAM,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,UAAU,CAAC,CAAC,CAAA;IAEnC,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;QACjC,MAAM,SAAS,GAAgB,EAAE,CAAA;QAEjC,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC7D,MAAM,KAAK,GAAG,QAAQ,CAAC,OAAO,CAAC,EAAE,GAAG,GAAG,GAAG,GAAG,CAAA;YAE7C,SAAS,CAAC,KAAK,CAAC,GAAG,KAAK,CAAA;QAC1B,CAAC;QAED,MAAM,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC,CAAA;IAClC,CAAC;IAED,MAAM,OAAO,GAAY;QACvB,KAAK,EAAE,UAAU;QACjB,IAAI,EAAE,SAAS;QACf,8DAA8D;QAC9D,OAAO,EAAE,OAAO,CAAC,iBAAiB,CAAC,CAAC,OAAO;QAC3C,UAAU,EAAE,MAAM,EAAE;QACpB,SAAS,EAAE,CAAC;gBACV,IAAI,EAAE,cAAc;gBACpB,KAAK,EAAE,IAAA,gBAAM,EAAC,MAAM,CAAC;aACtB,CAAC;KACH,CAAA;IAED,OAAO,EAAE,QAAQ,EAAE,CAAC,OAAO,CAAC,EAAE,CAAA;AAChC,CAAC;AA/BD,gCA+BC;AAED,SAAS,KAAK,CAAE,WAAwB;IACtC,MAAM,MAAM,GAAY,EAAE,CAAA;IAE1B,KAAK,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE,CAAC;QACzD,MAAM,UAAU,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAA;QAEzD,MAAM,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC,CAAA;IACpC,CAAC;IAED,OAAO,MAAM,CAAA;AACf,CAAC;AAED,SAAS,MAAM;IACb,OAAO,IAAA,kBAAI,GAAE,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,WAAW,GAAG,IAAA,oBAAQ,EAAC,IAAI,CAAC,CAAC,CAAA;AAC3D,CAAC"}
@@ -1 +1,2 @@
1
1
  export { Factory } from './Factory';
2
+ export * from './extension';
@@ -1,6 +1,21 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
2
16
  Object.defineProperty(exports, "__esModule", { value: true });
3
17
  exports.Factory = void 0;
4
18
  var Factory_1 = require("./Factory");
5
19
  Object.defineProperty(exports, "Factory", { enumerable: true, get: function () { return Factory_1.Factory; } });
20
+ __exportStar(require("./extension"), exports);
6
21
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../source/index.ts"],"names":[],"mappings":";;;AAAA,qCAAmC;AAA1B,kGAAA,OAAO,OAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../source/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;AAAA,qCAAmC;AAA1B,kGAAA,OAAO,OAAA;AAChB,8CAA2B"}