@getdashfy/server 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +10 -0
- package/LICENSE +661 -0
- package/README.md +768 -0
- package/dist/index.cjs +1235 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +422 -0
- package/dist/index.d.ts +422 -0
- package/dist/index.js +1225 -0
- package/dist/index.js.map +1 -0
- package/package.json +82 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,422 @@
|
|
|
1
|
+
import { PollMode, Subscription, DashfyConfig, APIRegistration } from '@getdashfy/types';
|
|
2
|
+
import { FastifyInstance } from 'fastify';
|
|
3
|
+
import { Logger } from 'pino';
|
|
4
|
+
import { Socket } from 'socket.io';
|
|
5
|
+
|
|
6
|
+
interface API {
|
|
7
|
+
mode: PollMode;
|
|
8
|
+
methods: Record<string, (...args: any[]) => Promise<unknown>>;
|
|
9
|
+
}
|
|
10
|
+
interface BusOptions {
|
|
11
|
+
logger: Logger;
|
|
12
|
+
pollInterval?: number;
|
|
13
|
+
}
|
|
14
|
+
interface ClientInfo {
|
|
15
|
+
socket: Socket;
|
|
16
|
+
subscriptions: Set<string>;
|
|
17
|
+
connectedAt: Date;
|
|
18
|
+
}
|
|
19
|
+
interface DashfyOptions {
|
|
20
|
+
logger?: Logger;
|
|
21
|
+
app?: FastifyInstance;
|
|
22
|
+
}
|
|
23
|
+
interface SubscriptionData extends Subscription {
|
|
24
|
+
clients: Set<string>;
|
|
25
|
+
timer?: NodeJS.Timeout;
|
|
26
|
+
pushDispose?: () => void;
|
|
27
|
+
cached?: {
|
|
28
|
+
id: string;
|
|
29
|
+
data: unknown;
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
interface SubscriptionInfo {
|
|
33
|
+
id: string;
|
|
34
|
+
clientCount: number;
|
|
35
|
+
hasCachedData: boolean;
|
|
36
|
+
hasTimer: boolean;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Main Dashfy server class that manages the dashboard application.
|
|
41
|
+
*
|
|
42
|
+
* Orchestrates HTTP server (Fastify), WebSocket communication (Socket.IO),
|
|
43
|
+
* API registration (Bus), and configuration handling with hot-reload support.
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* ```ts
|
|
47
|
+
* import { Dashfy } from '@getdashfy/server'
|
|
48
|
+
*
|
|
49
|
+
* const dashfy = new Dashfy()
|
|
50
|
+
*
|
|
51
|
+
* // Load configuration from JSON (or YAML)
|
|
52
|
+
* await dashfy.configureFromFile('./dashfy.config.json')
|
|
53
|
+
*
|
|
54
|
+
* // Register APIs
|
|
55
|
+
* dashfy.registerApi('github', () => ({
|
|
56
|
+
* async stars(params: { owner: string; repo: string }) {
|
|
57
|
+
* const res = await fetch(`https://api.github.com/repos/${params.owner}/${params.repo}`)
|
|
58
|
+
* const data = await res.json()
|
|
59
|
+
* return { stars: data.stargazers_count }
|
|
60
|
+
* }
|
|
61
|
+
* }))
|
|
62
|
+
*
|
|
63
|
+
* // Start server
|
|
64
|
+
* await dashfy.start()
|
|
65
|
+
* // Server running at http://0.0.0.0:5001
|
|
66
|
+
* ```
|
|
67
|
+
*
|
|
68
|
+
* @example
|
|
69
|
+
* ```ts
|
|
70
|
+
* // Push mode for real-time data
|
|
71
|
+
* dashfy.registerApi('metrics', () => ({
|
|
72
|
+
* cpuUsage(callback: (data: unknown) => void) {
|
|
73
|
+
* const interval = setInterval(() => {
|
|
74
|
+
* callback({ usage: process.cpuUsage(), timestamp: Date.now() })
|
|
75
|
+
* }, 1000)
|
|
76
|
+
* return () => clearInterval(interval) // cleanup
|
|
77
|
+
* }
|
|
78
|
+
* }), 'push')
|
|
79
|
+
* ```
|
|
80
|
+
*/
|
|
81
|
+
declare class Dashfy {
|
|
82
|
+
private app;
|
|
83
|
+
private io?;
|
|
84
|
+
private bus;
|
|
85
|
+
private config?;
|
|
86
|
+
private logger;
|
|
87
|
+
private configPath?;
|
|
88
|
+
/**
|
|
89
|
+
* Creates a new Dashfy instance.
|
|
90
|
+
*
|
|
91
|
+
* @param options - Dashfy options
|
|
92
|
+
* @param options.logger - Logger instance
|
|
93
|
+
* @param options.app - Existing Fastify instance. When provided,
|
|
94
|
+
* Dashfy will use this instance instead of creating a new one,
|
|
95
|
+
* allowing integration with existing Fastify middleware and routes.
|
|
96
|
+
*
|
|
97
|
+
* @example
|
|
98
|
+
* ```ts
|
|
99
|
+
* // Integrate Dashfy with existing Fastify app
|
|
100
|
+
* import Fastify from 'fastify'
|
|
101
|
+
* import { Dashfy } from '@getdashfy/server'
|
|
102
|
+
*
|
|
103
|
+
* const app = Fastify()
|
|
104
|
+
*
|
|
105
|
+
* // Add your custom routes
|
|
106
|
+
* app.get('/custom-api', async (request, reply) => {
|
|
107
|
+
* return { message: 'Custom endpoint' }
|
|
108
|
+
* })
|
|
109
|
+
*
|
|
110
|
+
* // Initialize Dashfy with existing app
|
|
111
|
+
* const dashfy = new Dashfy({ app })
|
|
112
|
+
* await dashfy.configureFromFile('./dashfy.config.json')
|
|
113
|
+
* await dashfy.start()
|
|
114
|
+
* ```
|
|
115
|
+
*/
|
|
116
|
+
constructor(options?: DashfyOptions);
|
|
117
|
+
/**
|
|
118
|
+
* Configures the Dashfy server with the provided configuration object.
|
|
119
|
+
*
|
|
120
|
+
* This method accepts a configuration object and applies it to the server instance.
|
|
121
|
+
* If a global poll interval is specified in the configuration, it will be applied
|
|
122
|
+
* to the message bus for all poll-mode APIs.
|
|
123
|
+
*
|
|
124
|
+
* @param config - The Dashfy configuration object containing server settings,
|
|
125
|
+
* dashboards, and optional API configuration
|
|
126
|
+
*
|
|
127
|
+
* @remarks
|
|
128
|
+
* This method should be called before {@link start}. For loading configuration
|
|
129
|
+
* from a JSON or YAML file, use {@link configureFromFile} instead.
|
|
130
|
+
*
|
|
131
|
+
* @example
|
|
132
|
+
* ```ts
|
|
133
|
+
* const dashfy = new Dashfy();
|
|
134
|
+
*
|
|
135
|
+
* // Configure with a configuration object
|
|
136
|
+
* dashfy.configure({
|
|
137
|
+
* port: 5001,
|
|
138
|
+
* host: 'localhost',
|
|
139
|
+
* dashboards: [
|
|
140
|
+
* {
|
|
141
|
+
* id: 'main',
|
|
142
|
+
* title: 'Main Dashboard',
|
|
143
|
+
* widgets: []
|
|
144
|
+
* }
|
|
145
|
+
* ],
|
|
146
|
+
* apis: {
|
|
147
|
+
* pollInterval: 30000 // 30 seconds
|
|
148
|
+
* }
|
|
149
|
+
* });
|
|
150
|
+
*
|
|
151
|
+
* await dashfy.start();
|
|
152
|
+
* ```
|
|
153
|
+
*/
|
|
154
|
+
configure(config: DashfyConfig): void;
|
|
155
|
+
/**
|
|
156
|
+
* Loads and applies configuration from a file (JSON or YAML).
|
|
157
|
+
*
|
|
158
|
+
* This method reads a configuration file (supports .json, .yml, .yaml formats),
|
|
159
|
+
* validates it against the schema, and applies it to the server instance. It also
|
|
160
|
+
* optionally watches the file for changes and automatically reloads the configuration
|
|
161
|
+
* when the file is modified. When changes are detected, all connected clients are
|
|
162
|
+
* notified via Socket.IO.
|
|
163
|
+
*
|
|
164
|
+
* @param configPath - Path to the configuration file (absolute or relative).
|
|
165
|
+
* Supports .json, .yml, .yaml extensions
|
|
166
|
+
* @param watchConfig - Whether to watch the configuration file for changes.
|
|
167
|
+
* When `true`, the configuration will be automatically reloaded
|
|
168
|
+
* on file changes. Defaults to `true`
|
|
169
|
+
*
|
|
170
|
+
* @throws {Error} If the configuration file cannot be read or fails validation
|
|
171
|
+
*
|
|
172
|
+
* @remarks
|
|
173
|
+
* This method should be called before {@link start}. If you already have a
|
|
174
|
+
* configuration object, use {@link configure} instead.
|
|
175
|
+
*
|
|
176
|
+
* When the configuration is reloaded due to file changes:
|
|
177
|
+
* - The new configuration is validated before applying
|
|
178
|
+
* - Connected clients receive the updated configuration (excluding sensitive API data)
|
|
179
|
+
* - If validation fails, the old configuration remains active
|
|
180
|
+
*
|
|
181
|
+
* @example
|
|
182
|
+
* ```ts
|
|
183
|
+
* const dashfy = new Dashfy();
|
|
184
|
+
*
|
|
185
|
+
* // Load configuration from JSON file with auto-reload
|
|
186
|
+
* await dashfy.configureFromFile('./dashfy.config.json');
|
|
187
|
+
*
|
|
188
|
+
* // Load configuration from YAML file with auto-reload
|
|
189
|
+
* await dashfy.configureFromFile('./dashfy.config.yaml');
|
|
190
|
+
*
|
|
191
|
+
* // Load without watching for changes (production)
|
|
192
|
+
* await dashfy.configureFromFile('./dashfy.config.yaml', false);
|
|
193
|
+
*
|
|
194
|
+
* await dashfy.start();
|
|
195
|
+
* ```
|
|
196
|
+
*/
|
|
197
|
+
configureFromFile(configPath: string, watchConfig?: boolean): Promise<void>;
|
|
198
|
+
/**
|
|
199
|
+
* Registers an API with the message bus for data retrieval.
|
|
200
|
+
*
|
|
201
|
+
* This method allows you to register custom APIs that widgets can subscribe to
|
|
202
|
+
* for fetching data. APIs can operate in either poll mode (periodic fetching)
|
|
203
|
+
* or push mode (real-time updates via callbacks).
|
|
204
|
+
*
|
|
205
|
+
* @param id - Unique identifier for the API (e.g., 'github', 'weather', 'analytics')
|
|
206
|
+
* @param api - Factory function that returns an object containing API methods.
|
|
207
|
+
* Each method should be async and return data that widgets can consume
|
|
208
|
+
* @param mode - The API mode: `poll` (default) for periodic polling,
|
|
209
|
+
* or `push` for real-time push-based updates
|
|
210
|
+
*
|
|
211
|
+
* @throws {Error} If an API with the same ID is already registered
|
|
212
|
+
* @throws {Error} If the API mode is invalid
|
|
213
|
+
*
|
|
214
|
+
* @remarks
|
|
215
|
+
* - In **poll mode**, the bus automatically calls API methods at regular intervals
|
|
216
|
+
* - In **push mode**, API methods receive a callback to push data when available
|
|
217
|
+
* - APIs should be registered before calling {@link start}
|
|
218
|
+
* - The core `dashfy` API is automatically registered and provides system information
|
|
219
|
+
*
|
|
220
|
+
* @example
|
|
221
|
+
* ```ts
|
|
222
|
+
* const dashfy = new Dashfy();
|
|
223
|
+
*
|
|
224
|
+
* // Register a poll-mode API (default)
|
|
225
|
+
* dashfy.registerApi('github', () => ({
|
|
226
|
+
* async stars(params: { owner: string; repo: string }) {
|
|
227
|
+
* const response = await fetch(
|
|
228
|
+
* `https://api.github.com/repos/${params.owner}/${params.repo}`
|
|
229
|
+
* );
|
|
230
|
+
* const data = await response.json();
|
|
231
|
+
* return { stars: data.stargazers_count };
|
|
232
|
+
* }
|
|
233
|
+
* }));
|
|
234
|
+
*
|
|
235
|
+
* // Register a push-mode API for real-time updates
|
|
236
|
+
* dashfy.registerApi('metrics', () => ({
|
|
237
|
+
* async cpuUsage(callback: (data: unknown) => void) {
|
|
238
|
+
* const interval = setInterval(() => {
|
|
239
|
+
* const usage = process.cpuUsage();
|
|
240
|
+
* callback({ cpu: usage });
|
|
241
|
+
* }, 1000);
|
|
242
|
+
*
|
|
243
|
+
* return () => clearInterval(interval);
|
|
244
|
+
* }
|
|
245
|
+
* }), 'push');
|
|
246
|
+
* ```
|
|
247
|
+
*/
|
|
248
|
+
registerApi(id: string, api: APIRegistration, mode?: PollMode): void;
|
|
249
|
+
/**
|
|
250
|
+
* Starts the Dashfy server and begins listening for connections.
|
|
251
|
+
*
|
|
252
|
+
* This method initializes and starts all server components including the HTTP server,
|
|
253
|
+
* WebSocket connections, static file serving, and API endpoints. It must be called
|
|
254
|
+
* after configuration is loaded via {@link configure} or {@link configureFromFile}.
|
|
255
|
+
*
|
|
256
|
+
* @throws {Error} If configuration has not been set before calling this method
|
|
257
|
+
*
|
|
258
|
+
* @remarks
|
|
259
|
+
* **What this method does:**
|
|
260
|
+
* 1. Validates that configuration is loaded
|
|
261
|
+
* 2. Sets up CORS for cross-origin requests
|
|
262
|
+
* 3. Serves static files from the `build` directory (if available)
|
|
263
|
+
* 4. Registers HTTP endpoints:
|
|
264
|
+
* - `GET /config` - Returns public configuration (excludes sensitive API data)
|
|
265
|
+
* - `GET /health` - Health check endpoint with uptime and status
|
|
266
|
+
* - `GET /api/info` - Returns registered APIs and connection info
|
|
267
|
+
* 5. Initializes Socket.IO for real-time WebSocket communication
|
|
268
|
+
* 6. Sets up Socket.IO event handlers:
|
|
269
|
+
* - `connection` - Client connects, receives configuration
|
|
270
|
+
* - `api.subscription` - Widget subscribes to data
|
|
271
|
+
* - `api.unsubscription` - Widget unsubscribes from data
|
|
272
|
+
* - `disconnect` - Client disconnects, cleanup performed
|
|
273
|
+
* - `error` - Socket error handling
|
|
274
|
+
* 7. Starts listening on configured host and port
|
|
275
|
+
*
|
|
276
|
+
* **Port and Host Resolution:**
|
|
277
|
+
* - Port: `process.env.PORT` → `config.port` → `5001` (default)
|
|
278
|
+
* - Host: `process.env.HOST` → `config.host` → `0.0.0.0` (default)
|
|
279
|
+
*
|
|
280
|
+
* @example
|
|
281
|
+
* ```ts
|
|
282
|
+
* const dashfy = new Dashfy();
|
|
283
|
+
*
|
|
284
|
+
* // Configure the server
|
|
285
|
+
* await dashfy.configureFromFile('./dashfy.config.json');
|
|
286
|
+
*
|
|
287
|
+
* // Register custom APIs
|
|
288
|
+
* dashfy.registerApi('github', () => ({
|
|
289
|
+
* async stars(params: { owner: string; repo: string }) {
|
|
290
|
+
* // ... fetch GitHub stars
|
|
291
|
+
* }
|
|
292
|
+
* }));
|
|
293
|
+
*
|
|
294
|
+
* // Start the server
|
|
295
|
+
* await dashfy.start();
|
|
296
|
+
* // Server is now running and accepting connections
|
|
297
|
+
* ```
|
|
298
|
+
*
|
|
299
|
+
* @example
|
|
300
|
+
* ```ts
|
|
301
|
+
* // With environment variables
|
|
302
|
+
* process.env.PORT = '3000';
|
|
303
|
+
* process.env.HOST = 'localhost';
|
|
304
|
+
*
|
|
305
|
+
* const dashfy = new Dashfy();
|
|
306
|
+
* await dashfy.configureFromFile('./dashfy.config.json');
|
|
307
|
+
* await dashfy.start();
|
|
308
|
+
* // Server running on http://localhost:3000
|
|
309
|
+
* ```
|
|
310
|
+
*/
|
|
311
|
+
start(): Promise<void>;
|
|
312
|
+
/**
|
|
313
|
+
* Gracefully stops the Dashfy server and cleans up all resources.
|
|
314
|
+
*
|
|
315
|
+
* This method performs a graceful shutdown of the server, closing all active
|
|
316
|
+
* connections and releasing resources. It closes the Socket.IO server (if initialized)
|
|
317
|
+
* and the underlying Fastify HTTP server.
|
|
318
|
+
*
|
|
319
|
+
* @remarks
|
|
320
|
+
* **Shutdown process:**
|
|
321
|
+
* 1. Logs shutdown initiation
|
|
322
|
+
* 2. Closes Socket.IO server (disconnects all WebSocket clients)
|
|
323
|
+
* 3. Closes Fastify HTTP server (stops accepting new connections)
|
|
324
|
+
* 4. Waits for all pending requests to complete
|
|
325
|
+
* 5. Logs shutdown completion
|
|
326
|
+
*
|
|
327
|
+
* This method is safe to call even if the server was never started or is already stopped.
|
|
328
|
+
* All connected clients will be disconnected, and their subscriptions will be cleaned up
|
|
329
|
+
* automatically by the bus.
|
|
330
|
+
*
|
|
331
|
+
* @example
|
|
332
|
+
* ```ts
|
|
333
|
+
* const dashfy = new Dashfy();
|
|
334
|
+
* await dashfy.configureFromFile('./dashfy.config.json');
|
|
335
|
+
* await dashfy.start();
|
|
336
|
+
*
|
|
337
|
+
* // Later, gracefully shut down the server
|
|
338
|
+
* await dashfy.stop();
|
|
339
|
+
* ```
|
|
340
|
+
*
|
|
341
|
+
* @example
|
|
342
|
+
* ```ts
|
|
343
|
+
* // Graceful shutdown on process signals
|
|
344
|
+
* const dashfy = new Dashfy();
|
|
345
|
+
* await dashfy.configureFromFile('./dashfy.config.json');
|
|
346
|
+
* await dashfy.start();
|
|
347
|
+
*
|
|
348
|
+
* process.on('SIGTERM', async () => {
|
|
349
|
+
* console.log('SIGTERM received, shutting down gracefully...');
|
|
350
|
+
* await dashfy.stop();
|
|
351
|
+
* process.exit(0);
|
|
352
|
+
* });
|
|
353
|
+
*
|
|
354
|
+
* process.on('SIGINT', async () => {
|
|
355
|
+
* console.log('SIGINT received, shutting down gracefully...');
|
|
356
|
+
* await dashfy.stop();
|
|
357
|
+
* process.exit(0);
|
|
358
|
+
* });
|
|
359
|
+
* ```
|
|
360
|
+
*/
|
|
361
|
+
stop(): Promise<void>;
|
|
362
|
+
/**
|
|
363
|
+
* Returns a sanitized configuration object without sensitive API data.
|
|
364
|
+
*
|
|
365
|
+
* Removes the `apis` property from the configuration before sending it to clients,
|
|
366
|
+
* as it may contain sensitive server-side settings like polling intervals or API keys.
|
|
367
|
+
*
|
|
368
|
+
* @param config - The full Dashfy configuration object
|
|
369
|
+
* @returns Configuration object without the `apis` property
|
|
370
|
+
*
|
|
371
|
+
* @internal
|
|
372
|
+
*/
|
|
373
|
+
private getPublicConfig;
|
|
374
|
+
/**
|
|
375
|
+
* Registers the core `dashfy` API that provides system introspection and monitoring.
|
|
376
|
+
*
|
|
377
|
+
* This internal method is automatically called during construction to register
|
|
378
|
+
* a built-in API that exposes server health, statistics, and debugging information.
|
|
379
|
+
* The `dashfy` API is available to all widgets for monitoring server state.
|
|
380
|
+
*
|
|
381
|
+
* @remarks
|
|
382
|
+
* **Available methods:**
|
|
383
|
+
* - `inspector()` - Returns comprehensive system information including:
|
|
384
|
+
* - `apis`: List of all registered API identifiers
|
|
385
|
+
* - `clientCount`: Number of currently connected WebSocket clients
|
|
386
|
+
* - `subscriptions`: Detailed information about all active subscriptions
|
|
387
|
+
* - `uptime`: Server process uptime in seconds
|
|
388
|
+
* - `version`: Dashfy server version (from package.json)
|
|
389
|
+
* - `nodeVersion`: Node.js runtime version
|
|
390
|
+
*
|
|
391
|
+
* This API is useful for:
|
|
392
|
+
* - Building admin/monitoring dashboards
|
|
393
|
+
* - Debugging subscription and connection issues
|
|
394
|
+
* - Health checks and system monitoring
|
|
395
|
+
* - Displaying server statistics to users
|
|
396
|
+
*
|
|
397
|
+
* @example
|
|
398
|
+
* ```ts
|
|
399
|
+
* // Widgets can subscribe to the dashfy API
|
|
400
|
+
* // In a widget configuration:
|
|
401
|
+
* {
|
|
402
|
+
* api: 'dashfy',
|
|
403
|
+
* method: 'inspector'
|
|
404
|
+
* }
|
|
405
|
+
*
|
|
406
|
+
* // This returns:
|
|
407
|
+
* {
|
|
408
|
+
* apis: ['dashfy', 'github', 'weather'],
|
|
409
|
+
* clientCount: 5,
|
|
410
|
+
* subscriptions: [...],
|
|
411
|
+
* uptime: 3600.5,
|
|
412
|
+
* version: '0.1.0',
|
|
413
|
+
* nodeVersion: 'v20.10.0'
|
|
414
|
+
* }
|
|
415
|
+
* ```
|
|
416
|
+
*
|
|
417
|
+
* @internal
|
|
418
|
+
*/
|
|
419
|
+
private registerCoreApi;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
export { type API, type BusOptions, type ClientInfo, Dashfy, type DashfyOptions, type SubscriptionData, type SubscriptionInfo };
|