@gobing-ai/ts-infra 0.3.3 → 0.3.5

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.
@@ -0,0 +1,245 @@
1
+ /**
2
+ * Node/Bun convenience bootstrap for `@gobing-ai/ts-infra`.
3
+ *
4
+ * Composes the portable `runApplication` with runtime-specific wiring:
5
+ * - YAML config loading via `@gobing-ai/ts-runtime`
6
+ * - Application-specific config validation with caller-provided validator
7
+ * - File log sink via `node:fs`
8
+ * - Bun SQLite DB adapter creation from config
9
+ * - Optional Node OTel exporter initialization
10
+ * - Optional Node scheduler adapter
11
+ *
12
+ * This subpath may import runtime-specific adapters. The portable bootstrap
13
+ * subpath (`@gobing-ai/ts-infra/application`) must not.
14
+ *
15
+ * @module application-node
16
+ */
17
+ import { appendFileSync, mkdirSync, readFileSync } from 'node:fs';
18
+ import { dirname } from 'node:path';
19
+ import { createDbAdapter } from '@gobing-ai/ts-db';
20
+ import { interpolateTree, parseYamlObject } from '@gobing-ai/ts-runtime';
21
+ import { runApplication } from './application/index.js';
22
+ import { NodeSchedulerAdapter } from './scheduler-node.js';
23
+ import { initNodeTelemetry, shutdownNodeTelemetry } from './telemetry/otel-node.js';
24
+ // ── Errors ────────────────────────────────────────────────────────────────
25
+ /** Error thrown when application config validation fails. Includes file path and section name. */
26
+ export class ConfigValidationError extends Error {
27
+ constructor(message) {
28
+ super(message);
29
+ this.name = 'ConfigValidationError';
30
+ }
31
+ }
32
+ // ── Config validation helper ──────────────────────────────────────────────
33
+ /**
34
+ * Validate a raw config section using the caller-provided validator.
35
+ * Supports structural `safeParse`, `validate()`, `parse()`, and bare function forms.
36
+ */
37
+ function validateAppConfig(validator, raw, section, filePath) {
38
+ if (typeof validator === 'object' && 'safeParse' in validator) {
39
+ const result = validator.safeParse(raw);
40
+ if (!result.success) {
41
+ const details = result.errors?.map((e) => `${e.path}: ${e.message}`).join('; ') ?? 'unknown error';
42
+ throw new ConfigValidationError(`Application config validation failed in section "${section}"` +
43
+ (filePath ? ` (file: ${filePath})` : '') +
44
+ `: ${details}`);
45
+ }
46
+ return result.data;
47
+ }
48
+ if (typeof validator === 'function') {
49
+ return validator(raw);
50
+ }
51
+ if (typeof validator === 'object' && 'validate' in validator) {
52
+ return validator.validate(raw);
53
+ }
54
+ if (typeof validator === 'object' && 'parse' in validator) {
55
+ return validator.parse(raw);
56
+ }
57
+ throw new ConfigValidationError(`Unsupported validator shape for section "${section}"`);
58
+ }
59
+ // ── File sink helper ──────────────────────────────────────────────────────
60
+ function createFileSink(filePath) {
61
+ return (line) => {
62
+ try {
63
+ mkdirSync(dirname(filePath), { recursive: true });
64
+ appendFileSync(filePath, line);
65
+ }
66
+ catch {
67
+ // Best-effort — don't crash on log write failure
68
+ }
69
+ };
70
+ }
71
+ function loadYamlConfig(loader, fileContent) {
72
+ const bootstrapSection = loader.bootstrapSection ?? 'bootstrap';
73
+ let raw;
74
+ if (fileContent !== undefined) {
75
+ raw = parseYamlObject(fileContent);
76
+ }
77
+ else if (loader.overrides) {
78
+ raw = { ...loader.overrides };
79
+ }
80
+ else {
81
+ raw = {};
82
+ }
83
+ // Interpolate environment variables (Node/Bun only)
84
+ raw = interpolateTree(raw);
85
+ // Extract bootstrap section
86
+ const bootstrapSection_ = raw[bootstrapSection];
87
+ const bootstrapConfig = typeof bootstrapSection_ === 'object' && bootstrapSection_ !== null
88
+ ? bootstrapSection_
89
+ : {};
90
+ // Extract app config section
91
+ let appConfig;
92
+ if (loader.appConfig) {
93
+ const appSection = loader.appSection;
94
+ const appRaw = appSection
95
+ ? raw[appSection]
96
+ : (() => {
97
+ // Default: full remaining object minus bootstrap section
98
+ const remaining = {};
99
+ for (const [key, value] of Object.entries(raw)) {
100
+ if (key !== bootstrapSection) {
101
+ remaining[key] = value;
102
+ }
103
+ }
104
+ return remaining;
105
+ })();
106
+ appConfig = validateAppConfig(loader.appConfig, appRaw, appSection ?? '*', loader.configFile);
107
+ }
108
+ return { bootstrapConfig, appConfig };
109
+ }
110
+ // ── Public API ────────────────────────────────────────────────────────────
111
+ /**
112
+ * Node/Bun convenience application bootstrap.
113
+ *
114
+ * Extends the portable `runApplication` with:
115
+ * - YAML config file loading with section splitting
116
+ * - Application-specific config validation
117
+ * - File log sink creation from `logging.filePath`
118
+ * - Bun SQLite DB adapter creation when `database.enabled` + `database.driver`
119
+ * - Optional Node OTel telemetry exporter
120
+ * - Optional Node scheduler adapter
121
+ *
122
+ * @example
123
+ * ```ts
124
+ * import { runNodeApplication } from '@gobing-ai/ts-infra/application-node';
125
+ *
126
+ * await runNodeApplication({
127
+ * configLoader: {
128
+ * configFile: 'config/app.yaml',
129
+ * bootstrapSection: 'bootstrap',
130
+ * appSection: 'billing',
131
+ * appConfig: {
132
+ * safeParse(raw) {
133
+ * return billingSchema.safeParse(raw);
134
+ * },
135
+ * },
136
+ * },
137
+ * async start(app) {
138
+ * app.logger.info('started');
139
+ * },
140
+ * });
141
+ * ```
142
+ */
143
+ export async function runNodeApplication(options) {
144
+ let nodeTelemetryInitialized = false;
145
+ // ── Load config ─────────────────────────────────────────────────────
146
+ let loadedAppConfig;
147
+ let yamlBootstrap = {};
148
+ if (options.configLoader?.configFile) {
149
+ const fileContent = readFileSync(options.configLoader.configFile, 'utf-8');
150
+ const loaded = loadYamlConfig(options.configLoader, fileContent);
151
+ yamlBootstrap = loaded.bootstrapConfig;
152
+ loadedAppConfig = loaded.appConfig;
153
+ }
154
+ else if (options.configLoader) {
155
+ const loaded = loadYamlConfig(options.configLoader, undefined);
156
+ yamlBootstrap = loaded.bootstrapConfig;
157
+ loadedAppConfig = loaded.appConfig;
158
+ }
159
+ // ── Resolve bootstrap config from YAML + inline options ────────────
160
+ // YAML loads as Record<string, unknown>; bridge into typed options.
161
+ // Inline options (options.config) take precedence over YAML sections.
162
+ const yamlLog = yamlBootstrap.logging;
163
+ const yamlTel = yamlBootstrap.telemetry;
164
+ const yamlSched = yamlBootstrap.scheduler;
165
+ const databaseOpts = (yamlBootstrap.database ?? {});
166
+ const loggingOpts = {
167
+ ...yamlLog,
168
+ ...options.config?.logging,
169
+ };
170
+ const telemetryOpts = {
171
+ ...yamlTel,
172
+ ...options.config?.telemetry,
173
+ };
174
+ const schedulerOpts = {
175
+ ...yamlSched,
176
+ ...options.config?.scheduler,
177
+ };
178
+ // File sink from logging.filePath
179
+ const logFilePath = yamlBootstrap.logging?.filePath;
180
+ const loggingConfig = typeof logFilePath === 'string' ? { ...loggingOpts, fileSink: createFileSink(logFilePath) } : loggingOpts;
181
+ // ── Node OTel telemetry ─────────────────────────────────────────────
182
+ const rawTel = { ...telemetryOpts };
183
+ if (rawTel.enabled !== false && rawTel.endpoint) {
184
+ initNodeTelemetry({
185
+ serviceName: rawTel.serviceName ?? 'ts-libs',
186
+ endpoint: rawTel.endpoint,
187
+ headers: rawTel.headers,
188
+ });
189
+ nodeTelemetryInitialized = true;
190
+ }
191
+ // ── DB adapter ──────────────────────────────────────────────────────
192
+ let dbAdapter = options.services?.db;
193
+ if (!dbAdapter && databaseOpts.enabled === true) {
194
+ const driver = databaseOpts.driver;
195
+ if (driver === 'bun-sqlite') {
196
+ const adapter = await createDbAdapter({
197
+ driver: 'bun-sqlite',
198
+ url: databaseOpts.url,
199
+ });
200
+ dbAdapter = adapter;
201
+ }
202
+ else {
203
+ throw new ConfigValidationError(`database.enabled is true but driver ${driver ? `"${driver}"` : 'is missing'} is not supported ` +
204
+ `(expected "bun-sqlite"). Provide a supported driver or inject a DbAdapter via services.db.`);
205
+ }
206
+ }
207
+ // ── Scheduler adapter ───────────────────────────────────────────────
208
+ const schedulerConfig = {};
209
+ const rawSched = { ...schedulerOpts };
210
+ if (rawSched.enabled === true) {
211
+ schedulerConfig.enabled = true;
212
+ schedulerConfig.autoStart = schedulerOpts.autoStart;
213
+ // Use Node scheduler adapter by default in this subpath
214
+ schedulerConfig.adapter = new NodeSchedulerAdapter();
215
+ }
216
+ // ── Delegate to portable runApplication ─────────────────────────────
217
+ const app = await runApplication({
218
+ config: {
219
+ ...options.config,
220
+ logging: loggingConfig,
221
+ telemetry: telemetryOpts,
222
+ scheduler: schedulerConfig,
223
+ },
224
+ appConfig: loadedAppConfig,
225
+ services: {
226
+ ...options.services,
227
+ ...(dbAdapter ? { db: dbAdapter } : {}),
228
+ },
229
+ start: options.start,
230
+ stop: options.stop,
231
+ });
232
+ // ── Compose a handle with Node-specific cleanup on stop ─────────────
233
+ const originalStop = app.stop.bind(app);
234
+ return {
235
+ ...app,
236
+ stop: async (reason) => {
237
+ await originalStop(reason);
238
+ // Node-specific cleanup (after portable shutdown):
239
+ // 5. Shut down Node telemetry exporter
240
+ if (nodeTelemetryInitialized) {
241
+ await shutdownNodeTelemetry();
242
+ }
243
+ },
244
+ };
245
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gobing-ai/ts-infra",
3
- "version": "0.3.3",
3
+ "version": "0.3.5",
4
4
  "description": "@gobing-ai/ts-infra — Infrastructure backbone: event bus, job queue, scheduler, telemetry, API client, and logging.",
5
5
  "keywords": [
6
6
  "typescript",
@@ -47,6 +47,14 @@
47
47
  "./scheduler-node": {
48
48
  "types": "./dist/scheduler-node.d.ts",
49
49
  "import": "./dist/scheduler-node.js"
50
+ },
51
+ "./application": {
52
+ "types": "./dist/application/index.d.ts",
53
+ "import": "./dist/application/index.js"
54
+ },
55
+ "./application-node": {
56
+ "types": "./dist/application-node.d.ts",
57
+ "import": "./dist/application-node.js"
50
58
  }
51
59
  },
52
60
  "files": [
@@ -69,7 +77,8 @@
69
77
  "@logtape/logtape": "^2.0.0"
70
78
  },
71
79
  "peerDependencies": {
72
- "@gobing-ai/ts-db": "^0.3.3",
80
+ "@gobing-ai/ts-db": "^0.3.5",
81
+ "@gobing-ai/ts-runtime": "^0.3.5",
73
82
  "@opentelemetry/api": "^1.9.0",
74
83
  "@opentelemetry/sdk-trace-node": "^2.0.0",
75
84
  "@opentelemetry/sdk-metrics": "^2.0.0",
@@ -82,6 +91,9 @@
82
91
  "@gobing-ai/ts-db": {
83
92
  "optional": true
84
93
  },
94
+ "@gobing-ai/ts-runtime": {
95
+ "optional": true
96
+ },
85
97
  "@opentelemetry/sdk-trace-node": {
86
98
  "optional": true
87
99
  },
@@ -99,7 +111,8 @@
99
111
  }
100
112
  },
101
113
  "devDependencies": {
102
- "@gobing-ai/ts-db": "^0.3.3",
114
+ "@gobing-ai/ts-db": "^0.3.5",
115
+ "@gobing-ai/ts-runtime": "^0.3.5",
103
116
  "@types/bun": "1.3.14",
104
117
  "@opentelemetry/api": "^1.9.0",
105
118
  "@opentelemetry/sdk-trace-node": "^2.0.0",
@@ -0,0 +1,278 @@
1
+ /**
2
+ * Portable `runApplication` — DI bootstrap over existing ts-infra primitives.
3
+ *
4
+ * Orchestrates logger, telemetry, event bus, (optional) DB adapter, and
5
+ * (optional) scheduler into a deterministic startup/shutdown lifecycle.
6
+ * The portable subpath never opens files, creates DB connections, or wires
7
+ * runtime-specific exporters — those are injected or handled by the
8
+ * Node/Bun convenience subpath.
9
+ *
10
+ * @module application
11
+ */
12
+
13
+ import { attachDefaultObservers, createLifecycleBus } from '../event-bus/default-observers';
14
+ import { EventBus } from '../event-bus/event-bus';
15
+ import type { BusLifecycleEvents, EventMap } from '../event-bus/types';
16
+ import type { InfraEvents } from '../events';
17
+ import { getLogger, initializeLogger, type Logger } from '../logger';
18
+ import { initScheduler, setSchedulerAdapter } from '../scheduler/factory';
19
+ import type { SchedulerAdapter } from '../scheduler/types';
20
+ import { initTelemetry, shutdownTelemetry } from '../telemetry/sdk';
21
+ import type {
22
+ ApplicationBootstrapConfig,
23
+ ApplicationBootstrapOptions,
24
+ ApplicationRuntime,
25
+ ApplicationStopReason,
26
+ DbAdapterLike,
27
+ } from './types';
28
+
29
+ // ── Internal runtime state ────────────────────────────────────────────────
30
+
31
+ interface RuntimeState<TAppConfig, TEvents extends EventMap> {
32
+ app: ApplicationRuntime<TAppConfig, TEvents> | undefined;
33
+ userStop?: (app: ApplicationRuntime<TAppConfig, TEvents>, reason: ApplicationStopReason) => Promise<void> | void;
34
+ schedulerAdapter?: SchedulerAdapter;
35
+ schedulerStarted: boolean;
36
+ loggerInitialized: boolean;
37
+ telemetryInitialized: boolean;
38
+ stopped: boolean;
39
+ }
40
+
41
+ // ── Shutdown (deterministic reverse order per R5) ─────────────────────────
42
+
43
+ async function performShutdown<TAppConfig, TEvents extends EventMap>(
44
+ state: RuntimeState<TAppConfig, TEvents>,
45
+ reason: ApplicationStopReason,
46
+ ): Promise<void> {
47
+ if (state.stopped) return;
48
+ state.stopped = true;
49
+
50
+ const app = state.app;
51
+ if (!app) return;
52
+
53
+ // 1. User stop callback
54
+ if (state.userStop) {
55
+ await state.userStop(app, reason);
56
+ }
57
+
58
+ // 2. Stop scheduler
59
+ if (state.schedulerStarted && state.schedulerAdapter) {
60
+ await state.schedulerAdapter.stop().catch(() => {});
61
+ state.schedulerStarted = false;
62
+ }
63
+
64
+ // 3. Close DB adapter
65
+ if (app.db) {
66
+ try {
67
+ app.db.close();
68
+ } catch {
69
+ /* best-effort */
70
+ }
71
+ }
72
+
73
+ // 4. Shutdown telemetry
74
+ if (state.telemetryInitialized) {
75
+ await shutdownTelemetry();
76
+ state.telemetryInitialized = false;
77
+ }
78
+ }
79
+
80
+ // ── Public API ────────────────────────────────────────────────────────────
81
+
82
+ /**
83
+ * Portable application bootstrap.
84
+ *
85
+ * Orchestrates logger, telemetry, events, optional DB, and optional scheduler.
86
+ * Accepts injected dependencies; never opens files, reads config from disk,
87
+ * or wires runtime-specific exporters.
88
+ *
89
+ * Startup order (deterministic, per R5):
90
+ * 1. Resolve bootstrap config + app config
91
+ * 2. Initialize logger
92
+ * 3. Initialize telemetry
93
+ * 4. Create lifecycle bus + application EventBus
94
+ * 5. Register DB adapter (injected only)
95
+ * 6. Initialize scheduler + register entries
96
+ * 7. Call user `start(app)` callback
97
+ * 8. Start scheduler if `autoStart`
98
+ *
99
+ * Shutdown order (reverse, per R5):
100
+ * 1. User `stop(app, reason)` callback
101
+ * 2. Stop scheduler
102
+ * 3. Close DB adapter
103
+ * 4. Shut down telemetry
104
+ *
105
+ * If any startup step fails, already-started services are cleaned up in reverse
106
+ * order before rethrowing. `stop()` is idempotent.
107
+ *
108
+ * @example
109
+ * ```ts
110
+ * import { runApplication } from '@gobing-ai/ts-infra/application';
111
+ *
112
+ * const app = await runApplication({
113
+ * config: { logging: { level: 'debug' } },
114
+ * async start(app) {
115
+ * app.logger.info('started');
116
+ * },
117
+ * });
118
+ * ```
119
+ */
120
+ export async function runApplication<TAppConfig = unknown, TEvents extends EventMap = InfraEvents>(
121
+ options: ApplicationBootstrapOptions<TAppConfig, TEvents>,
122
+ ): Promise<ApplicationRuntime<TAppConfig, TEvents>> {
123
+ // ── Resolve config ─────────────────────────────────────────────────
124
+
125
+ const logOpts = options.config?.logging;
126
+ const loggingConfig: ApplicationBootstrapConfig['logging'] = {
127
+ enabled: logOpts?.enabled ?? true,
128
+ level: logOpts?.level ?? 'info',
129
+ console: logOpts?.console ?? true,
130
+ json: logOpts?.json ?? true,
131
+ ...(logOpts?.fileSink ? { fileSink: logOpts.fileSink } : {}),
132
+ };
133
+
134
+ const telOpts = options.config?.telemetry;
135
+ const telemetryConfig: ApplicationBootstrapConfig['telemetry'] = {
136
+ enabled: telOpts?.enabled ?? true,
137
+ serviceName: telOpts?.serviceName ?? 'ts-libs',
138
+ environment: telOpts?.environment ?? 'development',
139
+ dbStatementDebug: telOpts?.dbStatementDebug ?? false,
140
+ };
141
+
142
+ const schedOpts = options.config?.scheduler;
143
+ const schedulerConfig: ApplicationBootstrapConfig['scheduler'] = {
144
+ enabled: schedOpts?.enabled ?? false,
145
+ autoStart: schedOpts?.autoStart ?? true,
146
+ };
147
+
148
+ const eventsEnabled = options.config?.events?.enabled ?? true;
149
+ const eventsLifecycle = options.config?.events?.lifecycle ?? true;
150
+ const eventsDefaultObservers = options.config?.events?.defaultObservers ?? true;
151
+
152
+ const state: RuntimeState<TAppConfig, TEvents> = {
153
+ app: undefined,
154
+ userStop: options.stop,
155
+ schedulerAdapter: undefined,
156
+ schedulerStarted: false,
157
+ loggerInitialized: false,
158
+ telemetryInitialized: false,
159
+ stopped: false,
160
+ };
161
+
162
+ try {
163
+ // ── 1. Initialize logger ────────────────────────────────────────
164
+ let logger: Logger;
165
+ if (options.services?.logger) {
166
+ logger = options.services.logger;
167
+ } else if (loggingConfig.enabled) {
168
+ await initializeLogger({
169
+ level: loggingConfig.level,
170
+ console: loggingConfig.console,
171
+ fileSink: loggingConfig.fileSink,
172
+ json: loggingConfig.json,
173
+ });
174
+ logger = getLogger('bootstrap');
175
+ } else {
176
+ logger = getLogger('bootstrap');
177
+ }
178
+
179
+ // ── 2. Initialize telemetry ────────────────────────────────────
180
+ if (telemetryConfig.enabled) {
181
+ initTelemetry({
182
+ enabled: telemetryConfig.enabled,
183
+ serviceName: telemetryConfig.serviceName,
184
+ environment: telemetryConfig.environment,
185
+ dbStatementDebug: telemetryConfig.dbStatementDebug,
186
+ });
187
+ state.telemetryInitialized = true;
188
+ }
189
+
190
+ // ── 3. Create lifecycle bus + EventBus ─────────────────────────
191
+ const lifecycleBus =
192
+ eventsEnabled && eventsLifecycle ? (options.services?.lifecycleBus ?? createLifecycleBus()) : undefined;
193
+
194
+ if (lifecycleBus && eventsDefaultObservers) {
195
+ attachDefaultObservers(lifecycleBus);
196
+ }
197
+
198
+ const events = options.services?.events
199
+ ? options.services.events
200
+ : new EventBus<TEvents>({ lifecycleBus: lifecycleBus as EventBus<BusLifecycleEvents> | undefined });
201
+
202
+ // ── 4. Database (injected only) ────────────────────────────────
203
+ const db: DbAdapterLike | undefined = options.services?.db;
204
+
205
+ // ── 5. Scheduler ───────────────────────────────────────────────
206
+ let scheduler: SchedulerAdapter | undefined;
207
+ if (schedulerConfig.enabled) {
208
+ const adapter = options.services?.scheduler ?? schedOpts?.adapter;
209
+ if (adapter) {
210
+ setSchedulerAdapter(adapter);
211
+ }
212
+ scheduler = initScheduler(schedOpts?.entries);
213
+ state.schedulerAdapter = scheduler;
214
+ }
215
+
216
+ // ── Build resolved config ──────────────────────────────────────
217
+ const resolvedConfig: ApplicationBootstrapConfig = {
218
+ logging: loggingConfig,
219
+ events: { enabled: eventsEnabled, lifecycle: eventsLifecycle, defaultObservers: eventsDefaultObservers },
220
+ telemetry: telemetryConfig,
221
+ scheduler: schedulerConfig,
222
+ };
223
+
224
+ // ── Build runtime handle ───────────────────────────────────────
225
+ const app: ApplicationRuntime<TAppConfig, TEvents> = {
226
+ config: resolvedConfig,
227
+ appConfig: options.appConfig as TAppConfig,
228
+ logger,
229
+ events,
230
+ lifecycleBus,
231
+ db,
232
+ scheduler,
233
+ stop: (reason?: ApplicationStopReason) => performShutdown(state, reason ?? 'manual'),
234
+ };
235
+ state.app = app;
236
+
237
+ // ── 6. User start callback ─────────────────────────────────────
238
+ await options.start(app);
239
+
240
+ // ── 7. Start scheduler ─────────────────────────────────────────
241
+ if (schedulerConfig.enabled && schedulerConfig.autoStart && scheduler) {
242
+ await scheduler.start();
243
+ state.schedulerStarted = true;
244
+ }
245
+
246
+ return app;
247
+ } catch (error) {
248
+ // Reverse-order cleanup of services this bootstrap owns.
249
+ // Scheduler: start() is the last async op before return; if it throws,
250
+ // schedulerStarted is still false, so there is nothing started to stop.
251
+ // DB: injected by the caller (portable bootstrap never creates one), so
252
+ // its lifecycle is caller-owned and not closed here.
253
+ // Telemetry: owned by the bootstrap — shut it down if it was initialized.
254
+ if (state.telemetryInitialized) {
255
+ await shutdownTelemetry();
256
+ }
257
+ throw error;
258
+ }
259
+ }
260
+
261
+ export type { BusLifecycleEvents, EventMap } from '../event-bus/types';
262
+ export type { InfraEvents } from '../events';
263
+ // Re-export types
264
+ export type {
265
+ ApplicationBootstrapConfig,
266
+ ApplicationBootstrapOptions,
267
+ ApplicationConfigLoader,
268
+ ApplicationConfigValidator,
269
+ ApplicationRuntime,
270
+ ApplicationServices,
271
+ ApplicationStopReason,
272
+ ConfigValidationResult,
273
+ DbAdapterLike,
274
+ EventsOptions,
275
+ LoggingOptions,
276
+ SchedulerOptions,
277
+ TelemetryOptions,
278
+ } from './types';