@clockworkdog/cogs-client 1.5.5 → 2.0.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2023 Clockwork Dog
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -30,10 +30,61 @@ yarn add @clockworkdog/cogs-client
30
30
 
31
31
  ## Usage
32
32
 
33
- ### Create a `cogs-plugin-manifest.json` file
33
+ ### Create a `cogs-plugin-manifest.js` file
34
34
 
35
35
  See [PluginManifestJson](https://clockwork-dog.github.io/cogs-client-lib/interfaces/PluginManifestJson.html) for details of what to include.
36
36
 
37
+ If using Typescript set `"allowJs": true` in your `tsconfig.json`.
38
+
39
+ Use the `@type {const}` JSDoc annotation to allow the manifest to be imported as a literal type and `@satisfies {import("@clockworkdog/cogs-client").PluginManifestJson}` to allow your editor to check the validity of the manifest.
40
+
41
+ e.g.
42
+
43
+ ```js
44
+ module.exports =
45
+ /**
46
+ * @type {const}
47
+ * @satisfies {import("@clockworkdog/cogs-client").PluginManifestJsonReadonly}
48
+ */
49
+ ({
50
+ name: 'Big Button',
51
+ icon: 'bullseye-pointer',
52
+ description: 'A big, colorful touchscreen button',
53
+ version: '1',
54
+ config: [
55
+ {
56
+ name: 'Color',
57
+ value: { type: 'string', default: 'red' },
58
+ },
59
+ ],
60
+ state: [
61
+ {
62
+ name: 'Enabled',
63
+ value: { type: 'boolean', default: false },
64
+ writableFromCogs: true,
65
+ },
66
+ ],
67
+ events: {
68
+ toCogs: [
69
+ {
70
+ name: 'Pressed',
71
+ value: { type: 'boolean' },
72
+ },
73
+ ],
74
+ fromCogs: [
75
+ {
76
+ name: 'Explosion',
77
+ },
78
+ ],
79
+ },
80
+ media: {
81
+ audio: true,
82
+ video: true,
83
+ images: true,
84
+ },
85
+ });
86
+ ```
87
+
37
88
  ### Import the library
38
89
 
39
90
  #### Browser
@@ -48,7 +99,7 @@ const { CogsConnection, CogsAudioPlayer } = COGS;
48
99
  const { CogsConnection, CogsAudioPlayer } = require('@clockworkdog/cogs-client');
49
100
  ```
50
101
 
51
- #### Typesript / ES6
102
+ #### Typescript / ES6
52
103
 
53
104
  ```ts
54
105
  import { CogsConnection, CogsAudioPlayer } from '@clockworkdog/cogs-client';
@@ -56,35 +107,35 @@ import { CogsConnection, CogsAudioPlayer } from '@clockworkdog/cogs-client';
56
107
 
57
108
  ### Connect to COGS
58
109
 
110
+ Initialize a [CogsConnection](https://clockwork-dog.github.io/cogs-client-lib/interfaces/CogsConnection.html) with the manifest you created above.
111
+
59
112
  ```ts
60
113
  let connected = false;
61
114
 
62
- const cogsConnection = new CogsConnection();
115
+ import manifest from './cogs-plugin-manifest.js'; // Requires `"allowJs": true` in `tsconfig.json`
116
+
117
+ const cogsConnection = new CogsConnection(manifest);
63
118
  cogsConnection.addEventListener('open', () => {
64
119
  connected = true;
65
120
  });
66
121
  cogsConnection.addEventListener('close', () => {
67
122
  connected = false;
68
123
  });
69
- cogsConnection.addEventListener('config', (event) => {
70
- const config = event.detail;
71
- // Handle new config.
72
- // `config` is of type `{ [configKey: string]: number | string | boolean }`
124
+ cogsConnection.addEventListener('config', ({ config }) => {
125
+ // Handle new config
126
+ // `config` is of type `{ [name: string]: number | string | boolean }`
73
127
  });
74
- cogsConnection.addEventListener('updates', (event) => {
75
- const updates = event.detail;
76
- // Handle input port updates.
77
- // `updates` is of type `{ [portName: string]: number | string | boolean }`
128
+ cogsConnection.addEventListener('state', ({ state }) => {
129
+ // Handle state updates
130
+ // `state` is of type `{ [name: string]: number | string | boolean }`
78
131
  });
79
- cogsConnection.addEventListener('event', (event) => {
80
- const { key, value } = event.detail;
81
- // Handle event. See 'types/Callback.ts`
82
- // `key` is the event name.
83
- // `value` is the type defined in COGS, one of `number | string | boolean | undefined`
132
+ cogsConnection.addEventListener('event', ({ name, value }) => {
133
+ // Handle events from COGS
134
+ // `name` is the event name.
135
+ // `value` is of the type defined in manifest, one of `number | string | boolean | undefined`.
84
136
  });
85
- cogsConnection.addEventListener('message', (event) => {
86
- const message = event.detail;
87
- // Handle message. See `types/CogsClientMessage.ts`
137
+ cogsConnection.addEventListener('message', ({ message }) => {
138
+ // Handle low-level COGS messages. See `types/CogsClientMessage.ts`
88
139
  });
89
140
 
90
141
  function sendEventToCogs() {
@@ -92,12 +143,30 @@ function sendEventToCogs() {
92
143
  }
93
144
 
94
145
  function sendPortUpdateToCogs() {
95
- cogsConnection.setOutputPortValues({ port1: 100 });
146
+ cogsConnection.setState({ port1: 100 });
147
+ }
148
+ ```
149
+
150
+ ### Support audio actions
151
+
152
+ Add `audio` to `cogs-plugin-manifest.js`:
153
+
154
+ ```js
155
+ {
156
+ media: {
157
+ audio: true;
158
+ }
96
159
  }
160
+ ```
161
+
162
+ Add [CogsAudioPlayer](https://clockwork-dog.github.io/cogs-client-lib/classes/CogsAudioPlayer.html) to your page:
97
163
 
164
+ ```ts
98
165
  const audioPlayer = new CogsAudioPlayer(cogsConnection);
166
+
167
+ // Optional
99
168
  audioPlayer.addEventListener('state', (audioState) => {
100
- // Handle audio state. See `types/AudioState.ts`
169
+ // Handle audio state changes. See `types/AudioState.ts`
101
170
  });
102
171
  ```
103
172
 
@@ -1,7 +1,7 @@
1
1
  import CogsConnection from './CogsConnection';
2
2
  import { AudioState } from './types/AudioState';
3
3
  import MediaClipStateMessage from './types/MediaClipStateMessage';
4
- declare type EventTypes = {
4
+ type EventTypes = {
5
5
  state: AudioState;
6
6
  audioClipState: MediaClipStateMessage;
7
7
  };
@@ -10,7 +10,7 @@ export default class AudioPlayer {
10
10
  private globalVolume;
11
11
  private audioClipPlayers;
12
12
  private sinkId;
13
- constructor(cogsConnection: CogsConnection);
13
+ constructor(cogsConnection: CogsConnection<any>);
14
14
  setGlobalVolume(volume: number): void;
15
15
  playAudioClip(path: string, { playId, volume, fade, loop }: {
16
16
  playId: string;
@@ -16,8 +16,7 @@ class AudioPlayer {
16
16
  cogsConnection.sendMediaClipState(detail);
17
17
  });
18
18
  // Listen for audio control messages
19
- cogsConnection.addEventListener('message', (event) => {
20
- const message = event.detail;
19
+ cogsConnection.addEventListener('message', ({ message }) => {
21
20
  switch (message.type) {
22
21
  case 'media_config_update':
23
22
  if (this.globalVolume !== message.globalVolume) {
@@ -95,7 +94,7 @@ class AudioPlayer {
95
94
  .filter(([, { state }]) => state.type === 'pausing')
96
95
  .map(([id]) => parseInt(id));
97
96
  pausedSoundIds.forEach((soundId) => {
98
- log('Resuming paused clip', { soundId });
97
+ log('Resuming paused clip', soundId);
99
98
  clipPlayer.player.play(soundId);
100
99
  });
101
100
  // Clips with pause requested no longer need to pause, they can continue playing now
@@ -106,7 +105,7 @@ class AudioPlayer {
106
105
  const newSoundIds = pausedSoundIds.length > 0 || pausingSoundIds.length > 0 || pauseRequestedSoundIds.length > 0 ? [] : [clipPlayer.player.play()];
107
106
  // Pausing clips are technically currently playing as far as Howler is concerned
108
107
  pausingSoundIds.forEach((soundId) => {
109
- log('Stopping fade and resuming pausing clip', { soundId });
108
+ log('Stopping fade and resuming pausing clip', soundId);
110
109
  // Stop the fade callback
111
110
  clipPlayer.player.off('fade', undefined, soundId);
112
111
  // Set loop property
@@ -135,7 +134,6 @@ class AudioPlayer {
135
134
  // Non-preloaded clips don't yet have an HTML audio node
136
135
  // so we need to set the audio output when it's playing
137
136
  clipPlayer.player.once('play', () => {
138
- log('play() callback - setPlayerSinkId', { soundId });
139
137
  setPlayerSinkId(clipPlayer.player, this.sinkId);
140
138
  });
141
139
  clipPlayer.player.once('stop', () => this.handleStoppedClip(path, playId, soundId), soundId);
@@ -152,12 +150,11 @@ class AudioPlayer {
152
150
  loop,
153
151
  volume,
154
152
  };
155
- log('CLIP -> play_requested');
153
+ log('CLIP -> play_requested', soundId);
156
154
  // Once clip starts, check if it should actually be paused or stopped
157
155
  // If not, then update state to 'playing'
158
156
  clipPlayer.player.once('play', () => {
159
157
  var _a;
160
- log('play() callback - update state', { soundId });
161
158
  const clipState = (_a = clipPlayer.activeClips[soundId]) === null || _a === void 0 ? void 0 : _a.state;
162
159
  if ((clipState === null || clipState === void 0 ? void 0 : clipState.type) === 'pause_requested') {
163
160
  log('Clip started playing but should be paused', { path, soundId });
@@ -168,7 +165,7 @@ class AudioPlayer {
168
165
  this.stopAudioClip(path, { fade: clipState.fade }, soundId, true);
169
166
  }
170
167
  else {
171
- log('CLIP -> playing');
168
+ log('CLIP -> playing', soundId);
172
169
  this.updateActiveAudioClip(path, soundId, (clip) => ({ ...clip, state: { type: 'playing' } }));
173
170
  }
174
171
  }, soundId);
@@ -178,7 +175,6 @@ class AudioPlayer {
178
175
  clipPlayer.player.volume(0, soundId);
179
176
  clipPlayer.player.mute(false, soundId);
180
177
  clipPlayer.player.once('play', () => {
181
- log('play() callback - fade volume', { soundId });
182
178
  fadeAudioPlayerVolume(clipPlayer.player, volume, fade * 1000, soundId);
183
179
  }, soundId);
184
180
  }
@@ -208,25 +204,25 @@ class AudioPlayer {
208
204
  // Fade then pause
209
205
  clipPlayer.player.once('fade', (soundId) => {
210
206
  clipPlayer.player.pause(soundId);
211
- log('CLIP -> paused (after fade)');
207
+ log('CLIP -> paused (after fade)', soundId);
212
208
  this.updateActiveAudioClip(path, soundId, (clip) => ({ ...clip, state: { type: 'paused' } }));
213
209
  this.notifyClipStateListeners(clip.playId, path, 'paused');
214
210
  }, soundId);
215
211
  fadeAudioPlayerVolume(clipPlayer.player, 0, fade * 1000, soundId);
216
- log('CLIP -> pausing');
212
+ log('CLIP -> pausing', soundId);
217
213
  clip.state = { type: 'pausing' };
218
214
  }
219
215
  else {
220
216
  // Pause now
221
217
  clipPlayer.player.pause(soundId);
222
- log('CLIP -> paused');
218
+ log('CLIP -> paused', soundId);
223
219
  clip.state = { type: 'paused' };
224
220
  this.notifyClipStateListeners(clip.playId, path, 'paused');
225
221
  }
226
222
  }
227
223
  // Clip hasn't started playing yet, or has already had pause_requested (but fade may have changed so update here)
228
224
  else if (clip.state.type === 'play_requested' || clip.state.type === 'pause_requested') {
229
- log('CLIP -> pause_requested');
225
+ log('CLIP -> pause_requested', soundId);
230
226
  clip.state = { type: 'pause_requested', fade };
231
227
  }
232
228
  }
@@ -236,9 +232,10 @@ class AudioPlayer {
236
232
  });
237
233
  }
238
234
  stopAudioClip(path, { fade }, onlySoundId, allowIfStopRequested) {
239
- var _a, _b;
235
+ var _a, _b, _c;
236
+ log('Stop audio clip', { activeClips: (_a = this.audioClipPlayers[path]) === null || _a === void 0 ? void 0 : _a.activeClips });
240
237
  // No active clips to stop
241
- if (Object.keys((_b = (_a = this.audioClipPlayers[path]) === null || _a === void 0 ? void 0 : _a.activeClips) !== null && _b !== void 0 ? _b : {}).length === 0) {
238
+ if (Object.keys((_c = (_b = this.audioClipPlayers[path]) === null || _b === void 0 ? void 0 : _b.activeClips) !== null && _c !== void 0 ? _c : {}).length === 0) {
242
239
  return;
243
240
  }
244
241
  this.updateAudioClipPlayer(path, (clipPlayer) => {
@@ -257,11 +254,16 @@ class AudioPlayer {
257
254
  clipPlayer.player.off('fade', soundId);
258
255
  fadeAudioPlayerVolume(clipPlayer.player, 0, fade * 1000, soundId);
259
256
  // Set callback after starting new fade, otherwise it will fire straight away as the previous fade is cancelled
260
- clipPlayer.player.once('fade', (soundId) => clipPlayer.player.stop(soundId), soundId);
261
- log('CLIP -> stopping');
257
+ clipPlayer.player.once('fade', (soundId) => {
258
+ clipPlayer.player.loop(false, soundId);
259
+ clipPlayer.player.stop(soundId), soundId;
260
+ });
261
+ log('CLIP -> stopping', soundId);
262
262
  clip.state = { type: 'stopping' };
263
263
  }
264
264
  else {
265
+ log('Stop clip', soundId);
266
+ clipPlayer.player.loop(false, soundId);
265
267
  clipPlayer.player.stop(soundId);
266
268
  }
267
269
  }
@@ -269,7 +271,7 @@ class AudioPlayer {
269
271
  // or has pause_requested, but stop takes precedence
270
272
  else if (clip.state.type === 'play_requested' || clip.state.type === 'pause_requested' || clip.state.type === 'stop_requested') {
271
273
  log("Trying to stop clip which hasn't started playing yet", { path, soundId });
272
- log('CLIP -> stop_requested');
274
+ log('CLIP -> stop_requested', soundId);
273
275
  clip.state = { type: 'stop_requested', fade };
274
276
  }
275
277
  }
@@ -405,7 +407,7 @@ class AudioPlayer {
405
407
  }
406
408
  createPlayer(path, config) {
407
409
  const player = new howler_1.Howl({
408
- src: urls_1.assetUrl(path),
410
+ src: (0, urls_1.assetUrl)(path),
409
411
  autoplay: false,
410
412
  loop: false,
411
413
  volume: 1,
@@ -471,9 +473,8 @@ function setAudioPlayerVolume(howl, volume, soundId) {
471
473
  /**
472
474
  * Fade to audio volume
473
475
  *
474
- * This doesn't work on iOS (volume is read-only) so at least mute it if the volume is zero
476
+ * Note: This doesn't work on iOS (volume is read-only)
475
477
  */
476
478
  function fadeAudioPlayerVolume(howl, volume, fade, soundId) {
477
- howl.mute(false, soundId);
478
479
  howl.fade(howl.volume(soundId), volume, fade, soundId);
479
480
  }
@@ -1,57 +1,18 @@
1
- import { ConfigValue, EventKeyValue, EventValue, PortValue, ShowPhase } from './types/valueTypes';
1
+ import ShowPhase from './types/ShowPhase';
2
2
  import CogsClientMessage from './types/CogsClientMessage';
3
3
  import MediaClipStateMessage from './types/MediaClipStateMessage';
4
4
  import AllMediaClipStatesMessage from './types/AllMediaClipStatesMessage';
5
- interface ConnectionEventListeners<CustomTypes extends {
6
- config?: {
7
- [configKey: string]: ConfigValue;
8
- };
9
- inputPorts?: {
10
- [port: string]: PortValue;
11
- };
12
- inputEvents?: {
13
- [key: string]: EventValue | null;
14
- };
15
- }> {
16
- open: undefined;
17
- close: undefined;
18
- message: CogsClientMessage;
19
- config: CustomTypes['config'];
20
- updates: Partial<CustomTypes['inputPorts']>;
21
- event: CustomTypes['inputEvents'] extends {
22
- [key: string]: EventValue | null;
23
- } ? EventKeyValue<CustomTypes['inputEvents']> : Record<string, never>;
24
- }
25
- export declare type TimerState = Omit<Extract<CogsClientMessage, {
26
- type: 'adjustable_timer_update';
27
- }>, 'type'> & {
28
- startedAt: number;
29
- };
30
- export default class CogsConnection<CustomTypes extends {
31
- config?: {
32
- [configKey: string]: ConfigValue;
33
- };
34
- inputPorts?: {
35
- [port: string]: PortValue;
36
- };
37
- outputPorts?: {
38
- [port: string]: PortValue;
39
- };
40
- inputEvents?: {
41
- [key: string]: EventValue | null;
42
- };
43
- outputEvents?: {
44
- [key: string]: EventValue | null;
45
- };
46
- } = Record<never, never>> {
5
+ import { PluginManifestEventJson } from './types/PluginManifestJson';
6
+ import * as ManifestTypes from './types/ManifestTypes';
7
+ import { DeepReadonly } from './types/utils';
8
+ export default class CogsConnection<Manifest extends ManifestTypes.PluginManifest> {
9
+ readonly manifest: Manifest;
47
10
  private websocket;
48
11
  private eventTarget;
49
12
  private currentConfig;
50
- get config(): CustomTypes['config'];
51
- private currentInputPortValues;
52
- get inputPortValues(): CustomTypes['inputPorts'];
53
- private currentOutputPortValues;
54
- get outputPortValues(): CustomTypes['outputPorts'];
13
+ get config(): ManifestTypes.ConfigAsObject<Manifest>;
14
+ private currentState;
15
+ get state(): ManifestTypes.StateAsObject<Manifest>;
55
16
  private _showPhase;
56
17
  get showPhase(): ShowPhase;
57
18
  private _timerState;
@@ -62,14 +23,18 @@ export default class CogsConnection<CustomTypes extends {
62
23
  private audioOutputs;
63
24
  private _selectedAudioOutput;
64
25
  get selectedAudioOutput(): string;
65
- constructor({ hostname, port }?: {
26
+ constructor(manifest: Manifest, { hostname, port }?: {
66
27
  hostname?: string;
67
28
  port?: number;
68
- }, outputPortValues?: CustomTypes['outputPorts']);
29
+ }, initialClientState?: Partial<ManifestTypes.StateAsObject<Manifest, {
30
+ writableFromClient: true;
31
+ }>> | undefined);
69
32
  get isConnected(): boolean;
70
33
  close(): void;
71
- sendEvent<EventName extends keyof CustomTypes['outputEvents']>(eventName: EventName, ...[eventValue]: CustomTypes['outputEvents'][EventName] extends null ? [] : [CustomTypes['outputEvents'][EventName]]): void;
72
- setOutputPortValues(values: Partial<CustomTypes['outputPorts']>): void;
34
+ sendEvent<EventName extends ManifestTypes.EventToCogsKey<Manifest>>(eventName: EventName, ...[eventValue]: ManifestTypes.EventToCogsAsObject<Manifest>[EventName] extends undefined ? [] : [ManifestTypes.EventToCogsAsObject<Manifest>[EventName]]): void;
35
+ setState(values: Partial<ManifestTypes.StateAsObject<Manifest, {
36
+ writableFromClient: true;
37
+ }>>): void;
73
38
  getAudioSinkId(audioOutput: string): string | undefined;
74
39
  sendInitialMediaClipStates(allMediaClipStates: AllMediaClipStatesMessage): void;
75
40
  sendMediaClipState(mediaClipState: MediaClipStateMessage): void;
@@ -80,8 +45,50 @@ export default class CogsConnection<CustomTypes extends {
80
45
  * This is only relevant for plugins, not for Media Master content.
81
46
  */
82
47
  setPluginWindowVisible(visible: boolean): void;
83
- addEventListener<EventName extends keyof ConnectionEventListeners<CustomTypes>, EventValue extends ConnectionEventListeners<CustomTypes>[EventName]>(type: EventName, listener: (ev: CustomEvent<EventValue>) => void, options?: boolean | AddEventListenerOptions): void;
84
- removeEventListener<EventName extends keyof ConnectionEventListeners<CustomTypes>, EventValue extends ConnectionEventListeners<CustomTypes>[EventName]>(type: EventName, listener: (ev: CustomEvent<EventValue>) => void, options?: boolean | EventListenerOptions): void;
48
+ addEventListener<EventType extends CogsConnectionEvent<Manifest>['type']>(type: EventType, listener: (event: CogsConnectionEvent<Manifest> & {
49
+ type: EventType;
50
+ }) => void, options?: boolean | AddEventListenerOptions): void;
51
+ removeEventListener<EventType extends CogsConnectionEvent<Manifest>['type']>(type: EventType, listener: (event: Extract<CogsConnectionEvent<Manifest>, {
52
+ type: EventType;
53
+ }>) => void, options?: boolean | EventListenerOptions): void;
85
54
  private dispatchEvent;
86
55
  }
87
- export {};
56
+ export type TimerState = Omit<Extract<CogsClientMessage, {
57
+ type: 'adjustable_timer_update';
58
+ }>, 'type'> & {
59
+ startedAt: number;
60
+ };
61
+ export declare class CogsConnectionOpenEvent extends Event {
62
+ readonly type = "open";
63
+ constructor();
64
+ }
65
+ export declare class CogsConnectionCloseEvent extends Event {
66
+ readonly type = "close";
67
+ constructor();
68
+ }
69
+ export declare class CogsMessageEvent extends Event {
70
+ readonly message: CogsClientMessage;
71
+ readonly type = "message";
72
+ constructor(message: CogsClientMessage);
73
+ }
74
+ export declare class CogsConfigChangedEvent<CogsConfig> extends Event {
75
+ readonly config: CogsConfig;
76
+ readonly type = "config";
77
+ constructor(config: CogsConfig);
78
+ }
79
+ export declare class CogsStateChangedEvent<CogsState> extends Event {
80
+ readonly state: CogsState;
81
+ readonly type = "state";
82
+ constructor(state: CogsState);
83
+ }
84
+ export declare class CogsIncomingEvent<CogsEvent extends DeepReadonly<PluginManifestEventJson> | PluginManifestEventJson> extends Event {
85
+ readonly name: CogsEvent['name'];
86
+ readonly value: ManifestTypes.TypeFromCogsValueType<CogsEvent['value']>;
87
+ readonly type = "event";
88
+ constructor(name: CogsEvent['name'], value: ManifestTypes.TypeFromCogsValueType<CogsEvent['value']>);
89
+ }
90
+ /**
91
+ * Allows CogsIncomingEvent of each supported value type
92
+ */
93
+ export type CogsIncomingEventTypes<CogsEvent extends DeepReadonly<PluginManifestEventJson> | PluginManifestEventJson> = CogsEvent extends unknown ? CogsIncomingEvent<CogsEvent> : never;
94
+ export type CogsConnectionEvent<Manifest extends ManifestTypes.PluginManifest> = CogsConnectionOpenEvent | CogsConnectionCloseEvent | CogsMessageEvent | CogsConfigChangedEvent<ManifestTypes.ConfigAsObject<Manifest>> | CogsStateChangedEvent<Partial<ManifestTypes.StateAsObject<Manifest>>> | CogsIncomingEventTypes<ManifestTypes.EventsFromCogs<Manifest>>;