@syncbridge/common 1.1.4 → 2.0.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.
Files changed (163) hide show
  1. package/README.md +192 -1
  2. package/app-factory.d.ts +29 -0
  3. package/{processor-factory.js → app-factory.js} +63 -44
  4. package/classes/{processor-base.d.ts → app-base.d.ts} +3 -3
  5. package/classes/app-base.js +10 -0
  6. package/classes/base-element.d.ts +3 -0
  7. package/classes/base-element.js +2 -1
  8. package/classes/component-base.d.ts +6 -6
  9. package/classes/component-base.js +2 -3
  10. package/classes/console-logger.js +0 -1
  11. package/classes/frame-stream.js +0 -1
  12. package/classes/log-channel-writer.d.ts +32 -0
  13. package/classes/log-channel-writer.js +96 -0
  14. package/classes/runnable.d.ts +6 -0
  15. package/classes/runnable.js +22 -1
  16. package/classes/sb-error.js +0 -1
  17. package/classes/stack-executor.js +0 -1
  18. package/constants.d.ts +1 -1
  19. package/constants.js +1 -2
  20. package/decorators/app-events.decorator.d.ts +5 -0
  21. package/decorators/{processor-events.decorator.js → app-events.decorator.js} +0 -1
  22. package/decorators/app.decorator.d.ts +29 -0
  23. package/decorators/{processor.decorator.js → app.decorator.js} +24 -19
  24. package/decorators/component.decorator.js +13 -6
  25. package/decorators/decorator-helpers.js +0 -1
  26. package/decorators/define-variable.decorator.d.ts +6 -1
  27. package/decorators/define-variable.decorator.js +5 -2
  28. package/decorators/log-channel.decorator.d.ts +97 -0
  29. package/decorators/log-channel.decorator.js +33 -0
  30. package/decorators/use-component.decorator.d.ts +2 -2
  31. package/decorators/use-component.decorator.js +1 -2
  32. package/decorators/use-variables.decorator.d.ts +2 -2
  33. package/decorators/use-variables.decorator.js +0 -1
  34. package/host-link/host-identity.d.ts +53 -0
  35. package/host-link/host-identity.js +139 -0
  36. package/host-link/host-link.d.ts +43 -0
  37. package/host-link/host-link.js +53 -0
  38. package/host-link/host-packages.d.ts +25 -0
  39. package/host-link/host-packages.js +9 -0
  40. package/host-link/host-workers.d.ts +75 -0
  41. package/host-link/host-workers.js +28 -0
  42. package/host-link/multiplexer.d.ts +115 -0
  43. package/host-link/multiplexer.js +288 -0
  44. package/host-link/sealed-box.d.ts +21 -0
  45. package/host-link/sealed-box.js +103 -0
  46. package/index.d.ts +16 -3
  47. package/index.js +16 -4
  48. package/interfaces/index.js +0 -1
  49. package/interfaces/logger.interface.js +0 -1
  50. package/models/enums/enum-source.enum.d.ts +11 -0
  51. package/models/enums/enum-source.enum.js +21 -0
  52. package/models/enums/log-channel-kind.d.ts +7 -0
  53. package/models/enums/log-channel-kind.js +13 -0
  54. package/models/enums/log-level.js +0 -1
  55. package/models/enums/service-status.d.ts +5 -1
  56. package/models/enums/service-status.js +4 -1
  57. package/models/enums/variable-format.enum.js +0 -1
  58. package/models/enums/variable-type.enum.js +0 -1
  59. package/models/enums/worker-permission.enum.d.ts +16 -0
  60. package/models/enums/worker-permission.enum.js +27 -0
  61. package/models/index.d.ts +6 -1
  62. package/models/index.js +6 -2
  63. package/models/metadata/app-metadata.d.ts +6 -0
  64. package/models/metadata/{processor-metadata.js → app-metadata.js} +6 -7
  65. package/models/metadata/component-metadata.js +0 -1
  66. package/models/metadata/element-component-metadata.d.ts +2 -0
  67. package/models/metadata/element-component-metadata.js +10 -3
  68. package/models/metadata/element-metadata.d.ts +8 -0
  69. package/models/metadata/element-metadata.js +40 -1
  70. package/models/metadata/log-channel-metadata.d.ts +23 -0
  71. package/models/metadata/log-channel-metadata.js +72 -0
  72. package/models/metadata/package-metadata.js +0 -1
  73. package/models/metadata/plugin-metadata.d.ts +15 -0
  74. package/models/metadata/plugin-metadata.js +65 -0
  75. package/models/metadata/variable-metadata.d.ts +3 -0
  76. package/models/metadata/variable-metadata.js +15 -2
  77. package/models/profile/log-options.d.ts +17 -0
  78. package/models/profile/log-options.js +58 -2
  79. package/models/profile/profile-component.js +0 -1
  80. package/models/profile/profile.d.ts +2 -2
  81. package/models/profile/profile.js +9 -10
  82. package/models-document.js +0 -1
  83. package/package.json +6 -2
  84. package/registry/docs-sidebar.d.ts +59 -0
  85. package/registry/docs-sidebar.js +169 -0
  86. package/registry/extension-package.d.ts +40 -13
  87. package/registry/extension-package.js +59 -19
  88. package/registry/extension-registry.d.ts +16 -16
  89. package/registry/extension-registry.js +59 -45
  90. package/registry/module-catalog.d.ts +31 -0
  91. package/registry/module-catalog.js +88 -0
  92. package/registry/package-docs.d.ts +98 -0
  93. package/registry/package-docs.js +159 -0
  94. package/registry/package-manifest.d.ts +54 -3
  95. package/registry/package-manifest.js +59 -2
  96. package/utils/decode-profile.d.ts +18 -2
  97. package/utils/decode-profile.js +65 -10
  98. package/utils/encrypt-helpers.d.ts +7 -0
  99. package/utils/encrypt-helpers.js +12 -6
  100. package/utils/locate-package-json.js +0 -1
  101. package/utils/materialize-metadata.d.ts +3 -3
  102. package/utils/materialize-metadata.js +16 -13
  103. package/utils/normalize-metadata.d.ts +23 -4
  104. package/utils/normalize-metadata.js +52 -7
  105. package/utils/registry-names.js +0 -1
  106. package/utils/resolve-promises.js +0 -1
  107. package/utils/type-guards.d.ts +2 -2
  108. package/utils/type-guards.js +4 -5
  109. package/utils/uri-to-id.js +0 -1
  110. package/utils/worker-permissions.d.ts +25 -0
  111. package/utils/worker-permissions.js +59 -0
  112. package/classes/base-element.js.map +0 -1
  113. package/classes/component-base.js.map +0 -1
  114. package/classes/console-logger.js.map +0 -1
  115. package/classes/frame-stream.js.map +0 -1
  116. package/classes/processor-base.js +0 -11
  117. package/classes/processor-base.js.map +0 -1
  118. package/classes/runnable.js.map +0 -1
  119. package/classes/sb-error.js.map +0 -1
  120. package/classes/stack-executor.js.map +0 -1
  121. package/constants.js.map +0 -1
  122. package/decorators/component.decorator.js.map +0 -1
  123. package/decorators/decorator-helpers.js.map +0 -1
  124. package/decorators/define-variable.decorator.js.map +0 -1
  125. package/decorators/processor-events.decorator.d.ts +0 -5
  126. package/decorators/processor-events.decorator.js.map +0 -1
  127. package/decorators/processor.decorator.d.ts +0 -29
  128. package/decorators/processor.decorator.js.map +0 -1
  129. package/decorators/use-component.decorator.js.map +0 -1
  130. package/decorators/use-variables.decorator.js.map +0 -1
  131. package/index.js.map +0 -1
  132. package/interfaces/index.js.map +0 -1
  133. package/interfaces/logger.interface.js.map +0 -1
  134. package/models/enums/log-level.js.map +0 -1
  135. package/models/enums/service-status.js.map +0 -1
  136. package/models/enums/variable-format.enum.js.map +0 -1
  137. package/models/enums/variable-type.enum.js.map +0 -1
  138. package/models/index.js.map +0 -1
  139. package/models/metadata/component-metadata.js.map +0 -1
  140. package/models/metadata/element-component-metadata.js.map +0 -1
  141. package/models/metadata/element-metadata.js.map +0 -1
  142. package/models/metadata/package-metadata.js.map +0 -1
  143. package/models/metadata/processor-metadata.d.ts +0 -6
  144. package/models/metadata/processor-metadata.js.map +0 -1
  145. package/models/metadata/variable-metadata.js.map +0 -1
  146. package/models/profile/log-options.js.map +0 -1
  147. package/models/profile/profile-component.js.map +0 -1
  148. package/models/profile/profile.js.map +0 -1
  149. package/models-document.js.map +0 -1
  150. package/processor-factory.d.ts +0 -26
  151. package/processor-factory.js.map +0 -1
  152. package/registry/extension-package.js.map +0 -1
  153. package/registry/extension-registry.js.map +0 -1
  154. package/registry/package-manifest.js.map +0 -1
  155. package/utils/decode-profile.js.map +0 -1
  156. package/utils/encrypt-helpers.js.map +0 -1
  157. package/utils/locate-package-json.js.map +0 -1
  158. package/utils/materialize-metadata.js.map +0 -1
  159. package/utils/normalize-metadata.js.map +0 -1
  160. package/utils/registry-names.js.map +0 -1
  161. package/utils/resolve-promises.js.map +0 -1
  162. package/utils/type-guards.js.map +0 -1
  163. package/utils/uri-to-id.js.map +0 -1
package/README.md CHANGED
@@ -1 +1,192 @@
1
- # SyncBridge Common Library
1
+ # @syncbridge/common
2
+
3
+ What a SyncBridge module is written against. A module is an npm package
4
+ of **components** - clients, servers, connections - and **apps**, which
5
+ SyncBridge runs as workers, each a Node.js process of its own, made of
6
+ components. This package gives you the decorators that describe them, the
7
+ base classes they extend, and their lifecycle.
8
+
9
+ ```sh
10
+ npm i -D @syncbridge/common
11
+ ```
12
+
13
+ SyncBridge provides `@syncbridge/common` to every module it runs, so a
14
+ module declares it in `peerDependencies`, never in `dependencies`:
15
+
16
+ ```json
17
+ "peerDependencies": { "@syncbridge/common": "^2.0.0" }
18
+ ```
19
+
20
+ Start a module with `npx @syncbridge/syncbuild init @vendor/name`
21
+ ([`@syncbridge/syncbuild`](https://www.npmjs.com/package/@syncbridge/syncbuild)),
22
+ and test it with
23
+ [`@syncbridge/testing`](https://www.npmjs.com/package/@syncbridge/testing).
24
+
25
+ ## A component
26
+
27
+ A component is a class that extends `ComponentBase`, decorated with
28
+ `@Component`. Its settings - shown in SyncBridge's UI and kept in the
29
+ worker's profile - are a variables class, given with `UseVariables`:
30
+
31
+ ```ts
32
+ import {
33
+ Component,
34
+ ComponentBase,
35
+ DefineVariable,
36
+ VariableType,
37
+ } from '@syncbridge/common';
38
+
39
+ export class WeatherClientVariables {
40
+ @DefineVariable({ label: 'API address', required: true })
41
+ declare url: string;
42
+
43
+ @DefineVariable({ label: 'API key', type: VariableType.Secret })
44
+ declare apiKey?: string;
45
+
46
+ @DefineVariable({ label: 'Interval (s)', default: 60, minValue: 5 })
47
+ declare interval: number;
48
+ }
49
+
50
+ @Component({
51
+ displayName: 'Weather client',
52
+ description: 'Reads the weather from a web service',
53
+ tags: ['weather', 'http'],
54
+ })
55
+ export class WeatherClientComponent extends ComponentBase {
56
+ @Component.UseVariables()
57
+ declare values: WeatherClientVariables;
58
+
59
+ protected async _start(abortSignal?: AbortSignal): Promise<void> {
60
+ await super._start(abortSignal);
61
+ this.logger?.info(`Reading ${this.values.url}`);
62
+ }
63
+
64
+ protected async _stop(): Promise<void> {
65
+ /* Close what _start opened */
66
+ await super._stop();
67
+ }
68
+ }
69
+ ```
70
+
71
+ An element's URI is `<package>:<className>`. A component's class name is
72
+ its class's without `Component` (`@acme/weather:WeatherClient`); an app's
73
+ is its class's (`@acme/weather:WeatherStationApp`). Give `className` to
74
+ choose another.
75
+
76
+ ## An app
77
+
78
+ An app extends `AppBase`, decorated with `@App`. It names the components it
79
+ uses with `UseComponent`, by URI. SyncBridge creates them with the values the
80
+ worker's profile gives, and starts them before the app:
81
+
82
+ ```ts
83
+ import { App, AppBase, DefineVariable } from '@syncbridge/common';
84
+ import type { WeatherClientComponent } from './weather-client.component.js';
85
+
86
+ export class WeatherStationVariables {
87
+ @DefineVariable({ label: 'Station name', default: 'Station 1' })
88
+ declare name: string;
89
+ }
90
+
91
+ @App({ displayName: 'Weather station' })
92
+ export class WeatherStationApp extends AppBase {
93
+ @App.UseVariables()
94
+ declare values: WeatherStationVariables;
95
+
96
+ @App.UseComponent({
97
+ uri: '@acme/weather:WeatherClient',
98
+ required: true,
99
+ })
100
+ declare readonly weather: WeatherClientComponent;
101
+
102
+ protected async _start(abortSignal?: AbortSignal): Promise<void> {
103
+ await super._start(abortSignal);
104
+ this.logger?.info(`${this.values.name} started`);
105
+ }
106
+ }
107
+ ```
108
+
109
+ A component may use components too, with `Component.UseComponent`. A URI
110
+ can name another module's component, for example
111
+ `@syncbridge/net:TcpClient`. Declare that module as a peer as well.
112
+
113
+ ## Variables
114
+
115
+ `@DefineVariable` describes a setting. Its type is read from the
116
+ property's TypeScript type when not given. Some options:
117
+
118
+ | Option | Meaning |
119
+ | --- | --- |
120
+ | `label`, `description` | How it is shown |
121
+ | `type` | `VariableType`: `String`, `Secret` (a password or key, kept encrypted in the profile), `Number`, `BigInt`, `Boolean`, `Enum`, `Nested` |
122
+ | `default`, `required` | Its default value, and whether it must be given |
123
+ | `enumValues` | The values of an enum |
124
+ | `enumSource` | Values SyncBridge fills in from the server it runs on: `EnumSource.SerialPorts`, `EnumSource.NetworkAddresses` |
125
+ | `format`, `pattern` | A string's format (`url`, `email`, `date-time`...) or pattern |
126
+ | `minValue`, `maxValue` | A number's range |
127
+ | `isArray` | A list of values |
128
+ | `hotPlug` | A change applies while running, without a restart |
129
+ | `index` | Its order in the UI |
130
+
131
+ ## Lifecycle
132
+
133
+ Apps and components are `Runnable`s. Override the protected methods, and
134
+ always call `super`:
135
+
136
+ - `_init()`: once, before the first start.
137
+ - `_start(abortSignal)`: start working. The signal aborts when a stop comes
138
+ while starting.
139
+ - `_stop()`: stop, and release what `_start` took. Close every timer,
140
+ socket and pool: what is left open is logged as a warning when the
141
+ worker ends.
142
+ - `_restart()`: stop and start in place, for example when values change.
143
+
144
+ `status` is a `ServiceStatus`: `stopped`, `starting`, `started`,
145
+ `unhealthy`, `stopping`. Report trouble with
146
+ `this.setStatus(ServiceStatus.unhealthy, message)`. An app's status
147
+ follows its components'.
148
+
149
+ ## Logging
150
+
151
+ `this.logger` writes the element's log. A log channel is a log of its own
152
+ that the user can turn on, filter and keep apart, such as what a
153
+ connection sends and receives:
154
+
155
+ ```ts
156
+ import { LogChannel } from '@syncbridge/common';
157
+
158
+ @LogChannel({ name: 'communication', title: 'Communication', kind: 'transmit' })
159
+ declare readonly ioLog: LogChannel;
160
+
161
+ // later
162
+ this.ioLog.info('Sent', { bytes: 42 });
163
+ ```
164
+
165
+ ## Permissions
166
+
167
+ SyncBridge runs each worker as a Linux user of its own, under Node's
168
+ permission model. A component or app that needs more than its own data
169
+ directory declares it:
170
+
171
+ ```ts
172
+ @Component({
173
+ displayName: 'Serial client',
174
+ permissions: [WorkerPermission.SerialPorts, WorkerPermission.NativeCode],
175
+ })
176
+ ```
177
+
178
+ - `SerialPorts`: the serial devices.
179
+ - `NativeCode`: native addons (`.node`), such as `serialport` or `oracledb`.
180
+ - `ChildProcesses`: starting other programs.
181
+
182
+ A subclass has its base's permissions. `spm verify` runs a module under
183
+ the permission model and tells you what it failed to declare.
184
+
185
+ ## Examples
186
+
187
+ The modules SyncBridge ships are open source:
188
+ [syncbridge-io/public-modules](https://github.com/syncbridge-io/public-modules).
189
+
190
+ ## License
191
+
192
+ [MIT](LICENSE)
@@ -0,0 +1,29 @@
1
+ import { AppBase } from './classes/app-base.js';
2
+ import { StackExecutor } from './classes/stack-executor.js';
3
+ import type { LogChannel } from './decorators/log-channel.decorator.js';
4
+ import type { ILogger } from './interfaces/index.js';
5
+ import { Profile } from './models/index.js';
6
+ export declare namespace AppFactory {
7
+ const METADATA_KEY: unique symbol;
8
+ const PROFILE_KEY: unique symbol;
9
+ interface AppOptions {
10
+ logger?: ILogger;
11
+ dataDirectory?: string;
12
+ /** Where the log channels hand their records; without it they drop them */
13
+ logSink?: LogChannel.Sink;
14
+ }
15
+ function createApp<T extends AppBase>(profile: Profile, options?: AppOptions): Promise<T>;
16
+ function createAppSilent<T extends AppBase>(profile: Profile, options?: AppOptions): Promise<{
17
+ result: true;
18
+ app: T;
19
+ issues: StackExecutor.Issue[];
20
+ } | {
21
+ result: false;
22
+ issues: StackExecutor.Issue[];
23
+ }>;
24
+ function updateApp(app: AppBase, profile: Profile, options?: AppOptions): Promise<void>;
25
+ function updateAppSilent(app: AppBase, profile: Profile, options?: AppOptions): Promise<{
26
+ result: boolean;
27
+ issues: StackExecutor.Issue[];
28
+ }>;
29
+ }
@@ -1,21 +1,22 @@
1
1
  import { clone } from '@jsopen/objects';
2
2
  import equal from 'fast-deep-equal';
3
+ import { AppBase } from './classes/app-base.js';
3
4
  import { ComponentBase } from './classes/component-base.js';
4
- import { ProcessorBase } from './classes/processor-base.js';
5
+ import { LogChannelWriter } from './classes/log-channel-writer.js';
5
6
  import { SbError, SbValidationError } from './classes/sb-error.js';
6
7
  import { StackExecutor } from './classes/stack-executor.js';
7
- import { ElementComponentMetadataRecord, ProcessorMetadata, Profile, ProfileComponentRecord, VariableMetadataRecord, } from './models/index.js';
8
+ import { AppMetadata, ElementComponentMetadataRecord, Profile, ProfileComponentRecord, VariableMetadataRecord, } from './models/index.js';
8
9
  import { initializeModelsDocument } from './models-document.js';
9
10
  import { ExtensionRegistry } from './registry/extension-registry.js';
10
11
  import { decodeProfileSilent } from './utils/decode-profile.js';
11
- export var ProcessorFactory;
12
- (function (ProcessorFactory) {
13
- ProcessorFactory.METADATA_KEY = Symbol('METADATA');
14
- ProcessorFactory.PROFILE_KEY = Symbol('PROFILE');
15
- async function createProcessor(profile, options) {
16
- const r1 = await createProcessorSilent(profile, options);
12
+ export var AppFactory;
13
+ (function (AppFactory) {
14
+ AppFactory.METADATA_KEY = Symbol('METADATA');
15
+ AppFactory.PROFILE_KEY = Symbol('PROFILE');
16
+ async function createApp(profile, options) {
17
+ const r1 = await createAppSilent(profile, options);
17
18
  if (!r1.result) {
18
- const msg = 'Error while creating processor.\n ' +
19
+ const msg = 'Error while creating app.\n ' +
19
20
  r1.issues
20
21
  .map(issue => `- ${issue.message}. Location: /${issue.location}`)
21
22
  .join('\n ');
@@ -27,19 +28,19 @@ export var ProcessorFactory;
27
28
  issues: r1.issues,
28
29
  });
29
30
  }
30
- return r1.processor;
31
+ return r1.app;
31
32
  }
32
- ProcessorFactory.createProcessor = createProcessor;
33
- async function createProcessorSilent(profile, options) {
33
+ AppFactory.createApp = createApp;
34
+ async function createAppSilent(profile, options) {
34
35
  const ctx = {
35
36
  stackExecutor: new StackExecutor(),
36
- processor: undefined,
37
+ app: undefined,
37
38
  profile,
38
39
  options,
39
40
  };
40
41
  try {
41
42
  await initializeModelsDocument();
42
- if (!(await _configureProcessor(ctx))) {
43
+ if (!(await _configureApp(ctx))) {
43
44
  return {
44
45
  result: false,
45
46
  issues: ctx.stackExecutor.issues,
@@ -47,7 +48,7 @@ export var ProcessorFactory;
47
48
  }
48
49
  return {
49
50
  result: true,
50
- processor: ctx.processor,
51
+ app: ctx.app,
51
52
  issues: ctx.stackExecutor.issues,
52
53
  };
53
54
  }
@@ -66,11 +67,11 @@ export var ProcessorFactory;
66
67
  };
67
68
  }
68
69
  }
69
- ProcessorFactory.createProcessorSilent = createProcessorSilent;
70
- async function updateProcessor(processor, profile, options) {
71
- const r1 = await updateProcessorSilent(processor, profile, options);
70
+ AppFactory.createAppSilent = createAppSilent;
71
+ async function updateApp(app, profile, options) {
72
+ const r1 = await updateAppSilent(app, profile, options);
72
73
  if (!r1.result) {
73
- const msg = 'Error while updating processor.\n ' +
74
+ const msg = 'Error while updating app.\n ' +
74
75
  r1.issues
75
76
  .map(issue => `- ${issue.message}. Location: /${issue.location}`)
76
77
  .join('\n ');
@@ -83,20 +84,20 @@ export var ProcessorFactory;
83
84
  });
84
85
  }
85
86
  }
86
- ProcessorFactory.updateProcessor = updateProcessor;
87
- async function updateProcessorSilent(processor, profile, options) {
87
+ AppFactory.updateApp = updateApp;
88
+ async function updateAppSilent(app, profile, options) {
88
89
  if (options?.logger)
89
- processor.logger = options.logger;
90
+ app.logger = options.logger;
90
91
  if (options?.dataDirectory)
91
- processor.dataDirectory = options.dataDirectory;
92
+ app.dataDirectory = options.dataDirectory;
92
93
  const ctx = {
93
94
  stackExecutor: new StackExecutor(),
94
- processor,
95
+ app,
95
96
  profile,
96
97
  options,
97
98
  valueUpdates: new Map(),
98
99
  };
99
- if (!(await _configureProcessor(ctx))) {
100
+ if (!(await _configureApp(ctx))) {
100
101
  return {
101
102
  result: false,
102
103
  issues: ctx.stackExecutor.issues,
@@ -113,14 +114,14 @@ export var ProcessorFactory;
113
114
  await obj.emitAsyncSafe('values-updated', properties);
114
115
  }
115
116
  };
116
- await signalUpdates(processor);
117
+ await signalUpdates(app);
117
118
  return {
118
119
  result: true,
119
120
  issues: ctx.stackExecutor.issues,
120
121
  };
121
122
  }
122
- ProcessorFactory.updateProcessorSilent = updateProcessorSilent;
123
- async function _configureProcessor(ctx) {
123
+ AppFactory.updateAppSilent = updateAppSilent;
124
+ async function _configureApp(ctx) {
124
125
  /* Decode profile */
125
126
  const r1 = decodeProfileSilent(ctx.profile);
126
127
  if (!r1.result) {
@@ -128,14 +129,14 @@ export var ProcessorFactory;
128
129
  return false;
129
130
  }
130
131
  ctx.profile = r1.profile;
131
- const oldMetadata = ctx.processor?.[ProcessorFactory.METADATA_KEY];
132
- const oldProfile = ctx.processor?.[ProcessorFactory.PROFILE_KEY];
133
- if (!ctx.processor) {
134
- /* Create new processor instance */
135
- const reg = ExtensionRegistry.getProcessor(r1.profile.processorUri, r1.profile.processorVersion);
136
- const processorClass = await reg.getClass();
137
- /* Create processor instance */
138
- ctx.processor = new processorClass({
132
+ const oldMetadata = ctx.app?.[AppFactory.METADATA_KEY];
133
+ const oldProfile = ctx.app?.[AppFactory.PROFILE_KEY];
134
+ if (!ctx.app) {
135
+ /* Create new app instance */
136
+ const reg = ExtensionRegistry.getApp(r1.profile.appUri, r1.profile.appVersion);
137
+ const appClass = await reg.getClass();
138
+ /* Create app instance */
139
+ ctx.app = new appClass({
139
140
  name: r1.profile.displayName,
140
141
  logger: ctx.options?.logger,
141
142
  values: r1.profile.values ? clone(r1.profile.values) : {},
@@ -145,14 +146,14 @@ export var ProcessorFactory;
145
146
  else {
146
147
  /* Update values */
147
148
  if (!ctx.stackExecutor.execute('values', () => {
148
- _patchValues(ctx, ctx.processor, ctx.processor.values, '', r1.profile.values, r1.metadata.variables || {}, oldProfile?.values, oldMetadata.variables);
149
+ _patchValues(ctx, ctx.app, ctx.app.values, '', r1.profile.values, r1.metadata.variables || {}, oldProfile?.values, oldMetadata.variables);
149
150
  })) {
150
151
  return false;
151
152
  }
152
153
  }
153
154
  const cmpCtx = {
154
155
  ...ctx,
155
- owner: ctx.processor,
156
+ owner: ctx.app,
156
157
  curPath: '',
157
158
  newMetadataComponents: r1.metadata.components,
158
159
  newProfileComponents: r1.profile.components,
@@ -162,8 +163,9 @@ export var ProcessorFactory;
162
163
  if (!(_validateComponents(cmpCtx) && (await _configureComponents(cmpCtx)))) {
163
164
  return false;
164
165
  }
165
- ctx.processor[ProcessorFactory.METADATA_KEY] = r1.metadata;
166
- ctx.processor[ProcessorFactory.PROFILE_KEY] = r1.profile;
166
+ _configureLogChannels(ctx.app, r1.metadata.logChannels, r1.profile.settings?.logs?.channels, '', ctx.options?.logSink);
167
+ ctx.app[AppFactory.METADATA_KEY] = r1.metadata;
168
+ ctx.app[AppFactory.PROFILE_KEY] = r1.profile;
167
169
  return true;
168
170
  }
169
171
  function _validateComponents(ctx) {
@@ -199,7 +201,7 @@ export var ProcessorFactory;
199
201
  });
200
202
  }
201
203
  async function _configureComponents(ctx) {
202
- const { newMetadataComponents, newProfileComponents, oldMetadataComponents, oldProfileComponents, curPath, owner, stackExecutor, processor, } = ctx;
204
+ const { newMetadataComponents, newProfileComponents, oldMetadataComponents, oldProfileComponents, curPath, owner, stackExecutor, app, } = ctx;
203
205
  if (!newMetadataComponents)
204
206
  return true;
205
207
  owner.components = owner.components || {};
@@ -224,7 +226,7 @@ export var ProcessorFactory;
224
226
  const compClass = await compReg.getClass();
225
227
  const values = { ...newChildProfile.values };
226
228
  componentInstance = new compClass({
227
- processor: processor,
229
+ app: app,
228
230
  parent: owner,
229
231
  name: key,
230
232
  path: componentPath,
@@ -238,6 +240,7 @@ export var ProcessorFactory;
238
240
  componentInstance.values = {};
239
241
  _patchValues(ctx, componentInstance, componentInstance.values, '', newChildProfile.values || {}, newChildMetadata.variables || {}, oldChildProfile?.values, oldChildMetadata?.variables);
240
242
  }
243
+ _configureLogChannels(componentInstance, newChildMetadata.logChannels, newChildProfile.settings?.logs?.channels, componentPath, ctx.options?.logSink);
241
244
  /** Create the child logger */
242
245
  componentInstance.logger = owner.logger?.child({
243
246
  component: componentPath,
@@ -286,5 +289,21 @@ export var ProcessorFactory;
286
289
  }
287
290
  }
288
291
  }
289
- })(ProcessorFactory || (ProcessorFactory = {}));
290
- //# sourceMappingURL=processor-factory.js.map
292
+ /**
293
+ * Creates an element's log channels on first configuration - assigning
294
+ * each to its property - and applies the profile's settings to them on
295
+ * every one
296
+ */
297
+ function _configureLogChannels(element, definitions, settings, source, sink) {
298
+ for (const [name, definition] of Object.entries(definitions || {})) {
299
+ let channel = element.logChannels[name];
300
+ if (!channel) {
301
+ channel = new LogChannelWriter(definition, source, sink);
302
+ element.logChannels[name] = channel;
303
+ if (definition.propertyKey)
304
+ element[definition.propertyKey] = channel;
305
+ }
306
+ channel.configure(settings?.[name], sink);
307
+ }
308
+ }
309
+ })(AppFactory || (AppFactory = {}));
@@ -1,12 +1,12 @@
1
1
  import { BaseElement } from './base-element.js';
2
- export declare abstract class ProcessorBase<TEvents extends ProcessorBase.Events = ProcessorBase.Events> extends BaseElement<TEvents> {
2
+ export declare abstract class AppBase<TEvents extends AppBase.Events = AppBase.Events> extends BaseElement<TEvents> {
3
3
  dataDirectory?: string;
4
- constructor(init: ProcessorBase.InitArgs);
4
+ constructor(init: AppBase.InitArgs);
5
5
  }
6
6
  /**
7
7
  * @namespace
8
8
  */
9
- export declare namespace ProcessorBase {
9
+ export declare namespace AppBase {
10
10
  interface Events extends BaseElement.Events {
11
11
  }
12
12
  interface InitArgs extends BaseElement.InitArgs {
@@ -0,0 +1,10 @@
1
+ import { BaseElement } from './base-element.js';
2
+ export class AppBase extends BaseElement {
3
+ dataDirectory;
4
+ constructor(init) {
5
+ super(init);
6
+ this.dataDirectory = init.dataDirectory;
7
+ }
8
+ }
9
+ const APP_BASE_TOKEN = Symbol.for(AppBase.name);
10
+ AppBase.prototype[APP_BASE_TOKEN] = true;
@@ -1,3 +1,4 @@
1
+ import type { LogChannel } from '../decorators/log-channel.decorator.js';
1
2
  import type { ILogger } from '../interfaces/index.js';
2
3
  import { ServiceStatus } from '../models/index.js';
3
4
  import type { ComponentBase } from './component-base.js';
@@ -11,6 +12,8 @@ export declare abstract class BaseElement<TEvents extends BaseElement.Events = B
11
12
  readonly name: string;
12
13
  logger?: ILogger;
13
14
  components: Record<string, ComponentBase>;
15
+ /** The element's log channels by name; see `@LogChannel` */
16
+ readonly logChannels: Record<string, LogChannel>;
14
17
  values: Record<string, any>;
15
18
  constructor(init: BaseElement.InitArgs);
16
19
  get status(): ServiceStatus;
@@ -8,6 +8,8 @@ export class BaseElement extends Runnable {
8
8
  name;
9
9
  logger;
10
10
  components = {};
11
+ /** The element's log channels by name; see `@LogChannel` */
12
+ logChannels = {};
11
13
  values;
12
14
  constructor(init) {
13
15
  super(init);
@@ -88,4 +90,3 @@ export class BaseElement extends Runnable {
88
90
  super.onStatusChange();
89
91
  }
90
92
  }
91
- //# sourceMappingURL=base-element.js.map
@@ -1,9 +1,9 @@
1
+ import type { AppBase } from './app-base.js';
1
2
  import { BaseElement } from './base-element.js';
2
- import type { ProcessorBase } from './processor-base.js';
3
3
  export declare class ComponentBase<TEvents extends ComponentBase.Events = ComponentBase.Events> extends BaseElement<TEvents> {
4
4
  protected _context: ComponentBase.Context;
5
- readonly processor: ProcessorBase;
6
- readonly parent: ProcessorBase | ComponentBase;
5
+ readonly app: AppBase;
6
+ readonly parent: AppBase | ComponentBase;
7
7
  readonly path: string;
8
8
  constructor(init: ComponentBase.InitArgs);
9
9
  }
@@ -14,12 +14,12 @@ export declare namespace ComponentBase {
14
14
  interface Events extends BaseElement.Events {
15
15
  }
16
16
  interface InitArgs extends BaseElement.InitArgs {
17
- processor: ProcessorBase;
18
- parent: ProcessorBase | ComponentBase;
17
+ app: AppBase;
18
+ parent: AppBase | ComponentBase;
19
19
  path: string;
20
20
  }
21
21
  interface Context extends BaseElement.Context {
22
- parent: ProcessorBase | ComponentBase;
22
+ parent: AppBase | ComponentBase;
23
23
  name: string;
24
24
  path: string;
25
25
  }
@@ -1,15 +1,14 @@
1
1
  import { BaseElement } from './base-element.js';
2
2
  export class ComponentBase extends BaseElement {
3
- processor;
3
+ app;
4
4
  parent;
5
5
  path;
6
6
  constructor(init) {
7
7
  super(init);
8
- this.processor = init.processor;
8
+ this.app = init.app;
9
9
  this.parent = init.parent;
10
10
  this.path = init.path;
11
11
  }
12
12
  }
13
13
  const COMPONENT_BASE_TOKEN = Symbol.for(ComponentBase.name);
14
14
  ComponentBase.prototype[COMPONENT_BASE_TOKEN] = true;
15
- //# sourceMappingURL=component-base.js.map
@@ -35,4 +35,3 @@ export class ConsoleLogger {
35
35
  return undefined;
36
36
  }
37
37
  }
38
- //# sourceMappingURL=console-logger.js.map
@@ -138,4 +138,3 @@ export class FrameStream extends Transform {
138
138
  this.push(combined);
139
139
  }
140
140
  }
141
- //# sourceMappingURL=frame-stream.js.map
@@ -0,0 +1,32 @@
1
+ import type { LogChannel } from '../decorators/log-channel.decorator.js';
2
+ import { LogChannelKind, type LogChannelMetadata, type LogChannelSettings, LogLevel } from '../models/index.js';
3
+ /**
4
+ * The LogChannel a `@LogChannel` property holds: it applies the channel's
5
+ * settings and hands what passes them to the worker's sink.
6
+ */
7
+ export declare class LogChannelWriter implements LogChannel {
8
+ readonly metadata: LogChannelMetadata;
9
+ /** The path of the element it belongs to; '' for the app */
10
+ readonly source: string;
11
+ protected _sink?: LogChannel.Sink | undefined;
12
+ enabled: boolean;
13
+ level?: LogLevel;
14
+ /** The events written; all when not set */
15
+ events?: Set<string>;
16
+ constructor(metadata: LogChannelMetadata,
17
+ /** The path of the element it belongs to; '' for the app */
18
+ source: string, _sink?: LogChannel.Sink | undefined);
19
+ get name(): string;
20
+ get fullName(): string;
21
+ get kind(): LogChannelKind;
22
+ /** Applies the profile's settings over the definition's defaults */
23
+ configure(settings?: LogChannelSettings, sink?: LogChannel.Sink | undefined): void;
24
+ write(record: LogChannel.Record): void;
25
+ fatal(message: string, fields?: Record<string, any>): void;
26
+ error(message: string, fields?: Record<string, any>): void;
27
+ warn(message: string, fields?: Record<string, any>): void;
28
+ info(message: string, fields?: Record<string, any>): void;
29
+ verbose(message: string, fields?: Record<string, any>): void;
30
+ debug(message: string, fields?: Record<string, any>): void;
31
+ trace(message: string, fields?: Record<string, any>): void;
32
+ }