@jhits/plugin-telemetry 0.0.4 → 0.0.6
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/dist/TelemetryProvider.d.ts +8 -0
- package/dist/TelemetryProvider.d.ts.map +1 -0
- package/dist/TelemetryProvider.js +20 -0
- package/dist/TelemetryService.d.ts +42 -0
- package/dist/TelemetryService.d.ts.map +1 -0
- package/dist/TelemetryService.js +285 -0
- package/dist/api/handler.d.ts +20 -0
- package/dist/api/handler.d.ts.map +1 -0
- package/dist/api/handler.js +229 -0
- package/dist/api/route.d.ts +9 -0
- package/dist/api/route.d.ts.map +1 -0
- package/dist/api/route.js +8 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +10 -0
- package/dist/server.d.ts +55 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +313 -0
- package/dist/types.d.ts +26 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +5 -0
- package/dist/utils/logCleaner.d.ts +62 -0
- package/dist/utils/logCleaner.d.ts.map +1 -0
- package/dist/utils/logCleaner.js +52 -0
- package/package.json +26 -25
- package/src/server.ts +141 -0
package/dist/server.d.ts
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import 'server-only';
|
|
2
|
+
/**
|
|
3
|
+
* Server-only exports for @jhits/plugin-telemetry
|
|
4
|
+
* These exports should only be used in server-side code (API routes, server components)
|
|
5
|
+
*/
|
|
6
|
+
interface PluginRegistration {
|
|
7
|
+
name: string;
|
|
8
|
+
version: string;
|
|
9
|
+
description?: string;
|
|
10
|
+
routes: Array<{
|
|
11
|
+
path: string;
|
|
12
|
+
component: string;
|
|
13
|
+
}>;
|
|
14
|
+
apiHandlers: Array<{
|
|
15
|
+
method: string;
|
|
16
|
+
path: string;
|
|
17
|
+
handler: string;
|
|
18
|
+
}>;
|
|
19
|
+
cssFiles: string[];
|
|
20
|
+
setup?: () => Promise<void>;
|
|
21
|
+
dependencies?: string[];
|
|
22
|
+
config?: Record<string, any>;
|
|
23
|
+
}
|
|
24
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
25
|
+
/**
|
|
26
|
+
* Main API handler for telemetry plugin
|
|
27
|
+
*/
|
|
28
|
+
export declare function handleApi(req: NextRequest, path: string[], config: any): Promise<NextResponse>;
|
|
29
|
+
/**
|
|
30
|
+
* Plugin registration for auto-configuration
|
|
31
|
+
*/
|
|
32
|
+
export declare function registerPlugin(): Promise<PluginRegistration>;
|
|
33
|
+
/**
|
|
34
|
+
* API handler for retrieving logs
|
|
35
|
+
*/
|
|
36
|
+
export declare function getLogsHandler(req: NextRequest, path: string[], config: any): Promise<NextResponse>;
|
|
37
|
+
/**
|
|
38
|
+
* Telemetry Handler
|
|
39
|
+
* Server-only function for processing telemetry data
|
|
40
|
+
*
|
|
41
|
+
* @param data - Telemetry entry or array of entries
|
|
42
|
+
* @param context - Optional context with userId and sessionId
|
|
43
|
+
* @returns Result object with success status and logging information
|
|
44
|
+
*/
|
|
45
|
+
export declare function telemetryHandler(data: any, context?: {
|
|
46
|
+
userId?: string;
|
|
47
|
+
sessionId?: string;
|
|
48
|
+
}): Promise<{
|
|
49
|
+
success: boolean;
|
|
50
|
+
logged?: number;
|
|
51
|
+
rejected?: number;
|
|
52
|
+
error?: string;
|
|
53
|
+
}>;
|
|
54
|
+
export {};
|
|
55
|
+
//# sourceMappingURL=server.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,aAAa,CAAC;AAErB;;;GAGG;AAGH,UAAU,kBAAkB;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACnD,WAAW,EAAE,KAAK,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACtE,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5B,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAC9B;AAED,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAoExD;;GAEG;AACH,wBAAsB,SAAS,CAC7B,GAAG,EAAE,WAAW,EAChB,IAAI,EAAE,MAAM,EAAE,EACd,MAAM,EAAE,GAAG,GACV,OAAO,CAAC,YAAY,CAAC,CA8BvB;AAED;;GAEG;AACH,wBAAsB,cAAc,IAAI,OAAO,CAAC,kBAAkB,CAAC,CAyClE;AAED;;GAEG;AACH,wBAAsB,cAAc,CAClC,GAAG,EAAE,WAAW,EAChB,IAAI,EAAE,MAAM,EAAE,EACd,MAAM,EAAE,GAAG,GACV,OAAO,CAAC,YAAY,CAAC,CAkCvB;AA8FD;;;;;;;GAOG;AACH,wBAAsB,gBAAgB,CAClC,IAAI,EAAE,GAAG,EACT,OAAO,CAAC,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,GAClD,OAAO,CAAC;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CA2DnF"}
|
package/dist/server.js
ADDED
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
import 'server-only';
|
|
2
|
+
import { NextResponse } from 'next/server';
|
|
3
|
+
import fs from 'fs/promises';
|
|
4
|
+
import path from 'path';
|
|
5
|
+
const LOG_DIR = path.join(process.cwd(), 'logs');
|
|
6
|
+
const LOG_FILE = path.join(LOG_DIR, 'jhits-system.log');
|
|
7
|
+
const OLD_LOG_FILE = path.join(LOG_DIR, 'jhits-system.old.log');
|
|
8
|
+
const MAX_LOG_SIZE = 10 * 1024 * 1024; // 10MB
|
|
9
|
+
const MAX_BATCH_SIZE = 100; // Maximum entries per request
|
|
10
|
+
const MAX_ENTRY_SIZE = 100 * 1024; // 100KB per entry
|
|
11
|
+
/**
|
|
12
|
+
* Ensure logs directory exists
|
|
13
|
+
*/
|
|
14
|
+
async function ensureLogDir() {
|
|
15
|
+
try {
|
|
16
|
+
await fs.access(LOG_DIR);
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
await fs.mkdir(LOG_DIR, { recursive: true });
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Check log file size and rotate if necessary
|
|
24
|
+
*/
|
|
25
|
+
async function rotateLogIfNeeded() {
|
|
26
|
+
try {
|
|
27
|
+
const stats = await fs.stat(LOG_FILE);
|
|
28
|
+
if (stats.size > MAX_LOG_SIZE) {
|
|
29
|
+
// Rename current log to old log
|
|
30
|
+
try {
|
|
31
|
+
await fs.rename(LOG_FILE, OLD_LOG_FILE);
|
|
32
|
+
}
|
|
33
|
+
catch (error) {
|
|
34
|
+
// If old log exists, remove it first
|
|
35
|
+
if (error.code === 'EEXIST' || error.code === 'ENOENT') {
|
|
36
|
+
try {
|
|
37
|
+
await fs.unlink(OLD_LOG_FILE);
|
|
38
|
+
await fs.rename(LOG_FILE, OLD_LOG_FILE);
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
// If still fails, just overwrite
|
|
42
|
+
await fs.copyFile(LOG_FILE, OLD_LOG_FILE);
|
|
43
|
+
await fs.unlink(LOG_FILE);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
throw error;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
catch (error) {
|
|
53
|
+
// If file doesn't exist, that's fine - we'll create it
|
|
54
|
+
if (error.code !== 'ENOENT') {
|
|
55
|
+
console.error('[Telemetry Handler] Error checking log size:', error);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Main API handler for telemetry plugin
|
|
61
|
+
*/
|
|
62
|
+
export async function handleApi(req, path, config) {
|
|
63
|
+
try {
|
|
64
|
+
const { searchParams } = new URL(req.url);
|
|
65
|
+
const action = path[0] || 'log';
|
|
66
|
+
switch (action) {
|
|
67
|
+
case 'log':
|
|
68
|
+
const data = await req.json();
|
|
69
|
+
const result = await telemetryHandler(data, {
|
|
70
|
+
userId: req.headers.get('x-user-id') || undefined,
|
|
71
|
+
sessionId: req.headers.get('x-session-id') || undefined
|
|
72
|
+
});
|
|
73
|
+
return NextResponse.json(result);
|
|
74
|
+
case 'logs':
|
|
75
|
+
return await getLogsHandler(req, path, config);
|
|
76
|
+
default:
|
|
77
|
+
return NextResponse.json({
|
|
78
|
+
success: false,
|
|
79
|
+
error: 'Unknown telemetry action'
|
|
80
|
+
}, { status: 404 });
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
console.error('[Telemetry API] Error:', error);
|
|
85
|
+
return NextResponse.json({
|
|
86
|
+
success: false,
|
|
87
|
+
error: 'Internal server error'
|
|
88
|
+
}, { status: 500 });
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Plugin registration for auto-configuration
|
|
93
|
+
*/
|
|
94
|
+
export async function registerPlugin() {
|
|
95
|
+
return {
|
|
96
|
+
name: 'telemetry',
|
|
97
|
+
version: '0.0.1',
|
|
98
|
+
description: 'System telemetry and logging plugin',
|
|
99
|
+
routes: [
|
|
100
|
+
{
|
|
101
|
+
path: '/dashboard/telemetry',
|
|
102
|
+
component: 'TelemetryDashboard'
|
|
103
|
+
}
|
|
104
|
+
],
|
|
105
|
+
apiHandlers: [
|
|
106
|
+
{
|
|
107
|
+
method: 'POST',
|
|
108
|
+
path: '/log',
|
|
109
|
+
handler: 'telemetryHandler'
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
method: 'GET',
|
|
113
|
+
path: '/logs',
|
|
114
|
+
handler: 'getLogsHandler'
|
|
115
|
+
}
|
|
116
|
+
],
|
|
117
|
+
cssFiles: ['telemetry-styles.css'],
|
|
118
|
+
setup: async () => {
|
|
119
|
+
// Create logs directory
|
|
120
|
+
const { mkdir } = await import('fs/promises');
|
|
121
|
+
const { join } = await import('path');
|
|
122
|
+
try {
|
|
123
|
+
await mkdir(join(process.cwd(), 'logs'), { recursive: true });
|
|
124
|
+
}
|
|
125
|
+
catch (error) {
|
|
126
|
+
// Directory might already exist
|
|
127
|
+
}
|
|
128
|
+
},
|
|
129
|
+
dependencies: [],
|
|
130
|
+
config: {
|
|
131
|
+
maxLogSize: 10 * 1024 * 1024, // 10MB
|
|
132
|
+
maxBatchSize: 100,
|
|
133
|
+
maxEntrySize: 100 * 1024 // 100KB
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* API handler for retrieving logs
|
|
139
|
+
*/
|
|
140
|
+
export async function getLogsHandler(req, path, config) {
|
|
141
|
+
try {
|
|
142
|
+
const fs = await import('fs/promises');
|
|
143
|
+
const path = await import('path');
|
|
144
|
+
const LOG_FILE = path.join(process.cwd(), 'logs', 'jhits-system.log');
|
|
145
|
+
const { searchParams } = new URL(req.url);
|
|
146
|
+
const lines = parseInt(searchParams.get('lines') || '100');
|
|
147
|
+
const category = searchParams.get('category');
|
|
148
|
+
// Read log file
|
|
149
|
+
const logContent = await fs.readFile(LOG_FILE, 'utf8');
|
|
150
|
+
const logLines = logContent.split('\n').filter(line => line.trim());
|
|
151
|
+
// Filter by category if specified
|
|
152
|
+
const filteredLines = category
|
|
153
|
+
? logLines.filter(line => line.includes(`[${category}]`))
|
|
154
|
+
: logLines;
|
|
155
|
+
// Get last N lines
|
|
156
|
+
const recentLines = filteredLines.slice(-lines);
|
|
157
|
+
return NextResponse.json({
|
|
158
|
+
success: true,
|
|
159
|
+
logs: recentLines,
|
|
160
|
+
total: filteredLines.length
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
catch (error) {
|
|
164
|
+
return NextResponse.json({
|
|
165
|
+
success: false,
|
|
166
|
+
error: 'Failed to read logs',
|
|
167
|
+
logs: []
|
|
168
|
+
}, { status: 500 });
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Format log entry as single-line JSON (or pretty-printed in debug mode)
|
|
173
|
+
* CRITICAL: Single-line format for easier grep/tailing, but pretty-printed if debug flag is on
|
|
174
|
+
*/
|
|
175
|
+
function formatLogEntry(entry, debug = false) {
|
|
176
|
+
const timestamp = new Date(entry.timestamp).toISOString();
|
|
177
|
+
const context = entry.context || 'unknown';
|
|
178
|
+
const userId = entry.userId || 'anonymous';
|
|
179
|
+
const sessionId = entry.sessionId || 'unknown';
|
|
180
|
+
// Check for debug mode via environment variable
|
|
181
|
+
const isDebugMode = debug || process.env.TELEMETRY_DEBUG === 'true';
|
|
182
|
+
if (isDebugMode) {
|
|
183
|
+
// Pretty-printed format for debugging
|
|
184
|
+
const logObject = {
|
|
185
|
+
timestamp,
|
|
186
|
+
context,
|
|
187
|
+
userId,
|
|
188
|
+
sessionId,
|
|
189
|
+
category: entry.category,
|
|
190
|
+
message: entry.message,
|
|
191
|
+
data: entry.data,
|
|
192
|
+
...(entry.stack && { stack: entry.stack })
|
|
193
|
+
};
|
|
194
|
+
return JSON.stringify(logObject, null, 2) + '\n';
|
|
195
|
+
}
|
|
196
|
+
else {
|
|
197
|
+
// Single-line JSON format for production (easier grep/tailing)
|
|
198
|
+
const sanitizedData = typeof entry.data === 'object'
|
|
199
|
+
? JSON.stringify(entry.data).replace(/\n/g, '\\n').replace(/\r/g, '\\r')
|
|
200
|
+
: String(entry.data);
|
|
201
|
+
const stackStr = entry.stack ? ` "stack":"${entry.stack.replace(/"/g, '\\"').replace(/\n/g, '\\n')}"` : '';
|
|
202
|
+
return `[${timestamp}] [${context}] [${userId}] [${sessionId}] [${entry.category}] ${entry.message} ${sanitizedData}${stackStr}\n`;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Validate and sanitize telemetry entry
|
|
207
|
+
*/
|
|
208
|
+
function validateEntry(entry) {
|
|
209
|
+
// Basic validation
|
|
210
|
+
if (!entry || typeof entry !== 'object') {
|
|
211
|
+
return null;
|
|
212
|
+
}
|
|
213
|
+
// Check required fields
|
|
214
|
+
if (!entry.id || !entry.timestamp || !entry.category || !entry.message) {
|
|
215
|
+
return null;
|
|
216
|
+
}
|
|
217
|
+
// Size check to prevent log injection attacks
|
|
218
|
+
const entrySize = JSON.stringify(entry).length;
|
|
219
|
+
if (entrySize > MAX_ENTRY_SIZE) {
|
|
220
|
+
console.warn('[Telemetry Handler] Entry too large, skipping:', entry.id);
|
|
221
|
+
return null;
|
|
222
|
+
}
|
|
223
|
+
// Sanitize string fields
|
|
224
|
+
return {
|
|
225
|
+
id: String(entry.id).substring(0, 100),
|
|
226
|
+
timestamp: Number(entry.timestamp),
|
|
227
|
+
category: String(entry.category).substring(0, 50),
|
|
228
|
+
message: String(entry.message).substring(0, 500),
|
|
229
|
+
data: entry.data || {},
|
|
230
|
+
stack: entry.stack ? String(entry.stack).substring(0, 5000) : undefined,
|
|
231
|
+
context: entry.context ? String(entry.context).substring(0, 100) : undefined,
|
|
232
|
+
userId: entry.userId ? String(entry.userId).substring(0, 100) : undefined,
|
|
233
|
+
sessionId: entry.sessionId ? String(entry.sessionId).substring(0, 100) : undefined,
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Extract user/session info from request headers or cookies
|
|
238
|
+
*/
|
|
239
|
+
function extractContext(request) {
|
|
240
|
+
// Try to get user ID from headers (custom header)
|
|
241
|
+
const userId = request.headers.get('x-user-id') ||
|
|
242
|
+
request.headers.get('x-userid') ||
|
|
243
|
+
undefined;
|
|
244
|
+
// Try to get session ID from headers or cookies
|
|
245
|
+
const sessionId = request.headers.get('x-session-id') ||
|
|
246
|
+
request.headers.get('x-sessionid') ||
|
|
247
|
+
request.cookies.get('sessionId')?.value ||
|
|
248
|
+
request.cookies.get('session')?.value ||
|
|
249
|
+
undefined;
|
|
250
|
+
return { userId, sessionId };
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Telemetry Handler
|
|
254
|
+
* Server-only function for processing telemetry data
|
|
255
|
+
*
|
|
256
|
+
* @param data - Telemetry entry or array of entries
|
|
257
|
+
* @param context - Optional context with userId and sessionId
|
|
258
|
+
* @returns Result object with success status and logging information
|
|
259
|
+
*/
|
|
260
|
+
export async function telemetryHandler(data, context) {
|
|
261
|
+
try {
|
|
262
|
+
// Ensure log directory exists
|
|
263
|
+
await ensureLogDir();
|
|
264
|
+
// Rotate log if needed
|
|
265
|
+
await rotateLogIfNeeded();
|
|
266
|
+
// Handle single entry or batch of entries
|
|
267
|
+
const entries = Array.isArray(data) ? data : [data];
|
|
268
|
+
// Rate limiting: Check batch size
|
|
269
|
+
if (entries.length > MAX_BATCH_SIZE) {
|
|
270
|
+
return {
|
|
271
|
+
success: false,
|
|
272
|
+
error: `Batch size exceeds maximum of ${MAX_BATCH_SIZE}`
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
// Validate and sanitize entries
|
|
276
|
+
const validEntries = [];
|
|
277
|
+
for (const entry of entries) {
|
|
278
|
+
const validated = validateEntry(entry);
|
|
279
|
+
if (validated) {
|
|
280
|
+
// Add context if provided
|
|
281
|
+
if (context?.userId && !validated.userId) {
|
|
282
|
+
validated.userId = context.userId;
|
|
283
|
+
}
|
|
284
|
+
if (context?.sessionId && !validated.sessionId) {
|
|
285
|
+
validated.sessionId = context.sessionId;
|
|
286
|
+
}
|
|
287
|
+
validEntries.push(validated);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
if (validEntries.length === 0) {
|
|
291
|
+
return {
|
|
292
|
+
success: false,
|
|
293
|
+
error: 'No valid entries to log'
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
// Format and append each entry (check for debug mode)
|
|
297
|
+
const debugMode = process.env.TELEMETRY_DEBUG === 'true';
|
|
298
|
+
const logLines = validEntries.map(entry => formatLogEntry(entry, debugMode)).join('');
|
|
299
|
+
await fs.appendFile(LOG_FILE, logLines, 'utf8');
|
|
300
|
+
return {
|
|
301
|
+
success: true,
|
|
302
|
+
logged: validEntries.length,
|
|
303
|
+
rejected: entries.length - validEntries.length
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
catch (error) {
|
|
307
|
+
console.error('[Telemetry Handler] Error writing log:', error);
|
|
308
|
+
return {
|
|
309
|
+
success: false,
|
|
310
|
+
error: 'Failed to write log',
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Telemetry Types
|
|
3
|
+
* Type definitions for the telemetry system
|
|
4
|
+
*/
|
|
5
|
+
export type TelemetryCategory = 'DRAG_DROP' | 'STATE' | 'ERROR' | 'UI';
|
|
6
|
+
export interface TelemetryEntry {
|
|
7
|
+
id: string;
|
|
8
|
+
timestamp: number;
|
|
9
|
+
category: TelemetryCategory;
|
|
10
|
+
message: string;
|
|
11
|
+
data: any;
|
|
12
|
+
stack?: string;
|
|
13
|
+
context?: string;
|
|
14
|
+
userId?: string;
|
|
15
|
+
sessionId?: string;
|
|
16
|
+
}
|
|
17
|
+
export interface TelemetryService {
|
|
18
|
+
log: (category: TelemetryCategory, message: string, data?: any, context?: string) => void;
|
|
19
|
+
error: (message: string, error: Error | unknown, data?: any, context?: string) => void;
|
|
20
|
+
exportLogs: () => void;
|
|
21
|
+
clearLogs: () => void;
|
|
22
|
+
getLogs: () => TelemetryEntry[];
|
|
23
|
+
flush: () => Promise<void>;
|
|
24
|
+
setContext: (context: string) => void;
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,MAAM,iBAAiB,GAAG,WAAW,GAAG,OAAO,GAAG,OAAO,GAAG,IAAI,CAAC;AAEvE,MAAM,WAAW,cAAc;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,iBAAiB,CAAC;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,GAAG,CAAC;IACV,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,gBAAgB;IAC7B,GAAG,EAAE,CAAC,QAAQ,EAAE,iBAAiB,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,GAAG,EAAE,OAAO,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1F,KAAK,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,GAAG,OAAO,EAAE,IAAI,CAAC,EAAE,GAAG,EAAE,OAAO,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IACvF,UAAU,EAAE,MAAM,IAAI,CAAC;IACvB,SAAS,EAAE,MAAM,IAAI,CAAC;IACtB,OAAO,EAAE,MAAM,cAAc,EAAE,CAAC;IAChC,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3B,UAAU,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CACzC"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Log Cleaner Utilities
|
|
3
|
+
* Optimizes telemetry payloads by summarizing large data structures
|
|
4
|
+
*/
|
|
5
|
+
interface BlockLike {
|
|
6
|
+
id: string | number;
|
|
7
|
+
type?: string;
|
|
8
|
+
nestedBlocks?: BlockLike[];
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Summarized block representation for telemetry
|
|
12
|
+
*/
|
|
13
|
+
export interface BlockSummary {
|
|
14
|
+
id: string;
|
|
15
|
+
type: string;
|
|
16
|
+
childCount: number;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Summarize a single block to reduce log payload size
|
|
20
|
+
* Only includes essential information: id, type, and child count
|
|
21
|
+
*/
|
|
22
|
+
export declare function summarizeBlock(block: BlockLike | {
|
|
23
|
+
id: string;
|
|
24
|
+
type: string;
|
|
25
|
+
nestedBlocks?: any[];
|
|
26
|
+
}): BlockSummary;
|
|
27
|
+
/**
|
|
28
|
+
* Summarize an array of blocks to reduce log payload size
|
|
29
|
+
* Recursively summarizes nested blocks up to a maximum depth
|
|
30
|
+
*/
|
|
31
|
+
export declare function summarizeBlocks(blocks: BlockLike[] | undefined, maxDepth?: number): BlockSummary[];
|
|
32
|
+
/**
|
|
33
|
+
* Extract action-specific data for telemetry
|
|
34
|
+
*/
|
|
35
|
+
export interface MoveBlockData {
|
|
36
|
+
blockId: string;
|
|
37
|
+
fromParent: string | null;
|
|
38
|
+
toParent: string | null;
|
|
39
|
+
fromIndex?: number;
|
|
40
|
+
toIndex: number;
|
|
41
|
+
}
|
|
42
|
+
export interface DeleteBlockData {
|
|
43
|
+
blockId: string;
|
|
44
|
+
blockType: string;
|
|
45
|
+
parentId: string | null;
|
|
46
|
+
}
|
|
47
|
+
export interface InsertBlockData {
|
|
48
|
+
newBlockType: string;
|
|
49
|
+
parentId: string | null;
|
|
50
|
+
index: number;
|
|
51
|
+
newBlockId?: string;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Create optimized telemetry payload for block operations
|
|
55
|
+
* Removes full block arrays and only includes essential action data
|
|
56
|
+
*/
|
|
57
|
+
export declare function createBlockActionPayload(actionType: 'MOVE_BLOCK' | 'DELETE_BLOCK' | 'INSERT_BLOCK', data: MoveBlockData | DeleteBlockData | InsertBlockData, options?: {
|
|
58
|
+
includeBlocks?: boolean;
|
|
59
|
+
affectedBlockId?: string;
|
|
60
|
+
}): Record<string, any>;
|
|
61
|
+
export {};
|
|
62
|
+
//# sourceMappingURL=logCleaner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logCleaner.d.ts","sourceRoot":"","sources":["../../src/utils/logCleaner.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,UAAU,SAAS;IACf,EAAE,EAAE,MAAM,GAAG,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,YAAY,CAAC,EAAE,SAAS,EAAE,CAAC;CAC9B;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;CACtB;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,SAAS,GAAG;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,GAAG,EAAE,CAAA;CAAE,GAAG,YAAY,CAMlH;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAC3B,MAAM,EAAE,SAAS,EAAE,GAAG,SAAS,EAC/B,QAAQ,GAAE,MAAU,GACrB,YAAY,EAAE,CAoBhB;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,eAAe;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED,MAAM,WAAW,eAAe;IAC5B,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;;GAGG;AACH,wBAAgB,wBAAwB,CACpC,UAAU,EAAE,YAAY,GAAG,cAAc,GAAG,cAAc,EAC1D,IAAI,EAAE,aAAa,GAAG,eAAe,GAAG,eAAe,EACvD,OAAO,CAAC,EAAE;IACN,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC5B,GACF,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAYrB"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Log Cleaner Utilities
|
|
3
|
+
* Optimizes telemetry payloads by summarizing large data structures
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Summarize a single block to reduce log payload size
|
|
7
|
+
* Only includes essential information: id, type, and child count
|
|
8
|
+
*/
|
|
9
|
+
export function summarizeBlock(block) {
|
|
10
|
+
return {
|
|
11
|
+
id: String(block.id),
|
|
12
|
+
type: String(block.type || 'unknown'),
|
|
13
|
+
childCount: Array.isArray(block.nestedBlocks) ? block.nestedBlocks.length : 0
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Summarize an array of blocks to reduce log payload size
|
|
18
|
+
* Recursively summarizes nested blocks up to a maximum depth
|
|
19
|
+
*/
|
|
20
|
+
export function summarizeBlocks(blocks, maxDepth = 2) {
|
|
21
|
+
if (!Array.isArray(blocks) || blocks.length === 0) {
|
|
22
|
+
return [];
|
|
23
|
+
}
|
|
24
|
+
return blocks.map(block => {
|
|
25
|
+
const summary = {
|
|
26
|
+
id: String(block.id),
|
|
27
|
+
type: String(block.type || 'unknown'),
|
|
28
|
+
childCount: Array.isArray(block.nestedBlocks) ? block.nestedBlocks.length : 0
|
|
29
|
+
};
|
|
30
|
+
// Optionally include nested summaries if depth allows (for debugging)
|
|
31
|
+
if (maxDepth > 0 && block.nestedBlocks && block.nestedBlocks.length > 0) {
|
|
32
|
+
// Only include nested summaries in debug mode
|
|
33
|
+
// For production, just the childCount is sufficient
|
|
34
|
+
}
|
|
35
|
+
return summary;
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Create optimized telemetry payload for block operations
|
|
40
|
+
* Removes full block arrays and only includes essential action data
|
|
41
|
+
*/
|
|
42
|
+
export function createBlockActionPayload(actionType, data, options) {
|
|
43
|
+
const payload = {
|
|
44
|
+
actionType,
|
|
45
|
+
...data
|
|
46
|
+
};
|
|
47
|
+
// Only include full blocks for critical errors
|
|
48
|
+
if (options?.includeBlocks && options.affectedBlockId) {
|
|
49
|
+
payload.affectedBlockId = options.affectedBlockId;
|
|
50
|
+
}
|
|
51
|
+
return payload;
|
|
52
|
+
}
|
package/package.json
CHANGED
|
@@ -1,37 +1,39 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jhits/plugin-telemetry",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.6",
|
|
4
4
|
"description": "System logging and telemetry utilities for the JHITS ecosystem",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
8
|
-
"main": "./
|
|
9
|
-
"types": "./
|
|
8
|
+
"main": "./dist/index.js",
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "tsc"
|
|
12
|
+
},
|
|
10
13
|
"exports": {
|
|
11
14
|
".": {
|
|
12
|
-
"types": "./
|
|
13
|
-
"default": "./
|
|
15
|
+
"types": "./dist/index.d.ts",
|
|
16
|
+
"default": "./dist/index.js"
|
|
14
17
|
},
|
|
15
18
|
"./server": {
|
|
16
|
-
"types": "./
|
|
17
|
-
"default": "./
|
|
19
|
+
"types": "./dist/server.d.ts",
|
|
20
|
+
"default": "./dist/server.js"
|
|
18
21
|
},
|
|
19
22
|
"./api/route": {
|
|
20
|
-
"types": "./
|
|
21
|
-
"default": "./
|
|
23
|
+
"types": "./dist/api/route.d.ts",
|
|
24
|
+
"default": "./dist/api/route.js"
|
|
22
25
|
},
|
|
23
26
|
"./api/handler": {
|
|
24
|
-
"types": "./
|
|
25
|
-
"default": "./
|
|
27
|
+
"types": "./dist/api/handler.d.ts",
|
|
28
|
+
"default": "./dist/api/handler.js"
|
|
26
29
|
},
|
|
27
30
|
"./utils/logCleaner": {
|
|
28
|
-
"types": "./
|
|
29
|
-
"default": "./
|
|
31
|
+
"types": "./dist/utils/logCleaner.d.ts",
|
|
32
|
+
"default": "./dist/utils/logCleaner.js"
|
|
30
33
|
}
|
|
31
34
|
},
|
|
32
35
|
"dependencies": {
|
|
33
|
-
"@jhits/plugin-core": "^0.0.
|
|
34
|
-
"server-only": "^0.0.1"
|
|
36
|
+
"@jhits/plugin-core": "^0.0.2"
|
|
35
37
|
},
|
|
36
38
|
"peerDependencies": {
|
|
37
39
|
"next": ">=15.0.0",
|
|
@@ -39,18 +41,17 @@
|
|
|
39
41
|
"react-dom": ">=18.0.0"
|
|
40
42
|
},
|
|
41
43
|
"devDependencies": {
|
|
42
|
-
"@types/node": "^
|
|
43
|
-
"@types/react": "^19",
|
|
44
|
-
"@types/react-dom": "^19",
|
|
45
|
-
"next": "16.1.
|
|
46
|
-
"react": "19.2.
|
|
47
|
-
"react-dom": "19.2.
|
|
48
|
-
"typescript": "^5"
|
|
44
|
+
"@types/node": "^25.2.3",
|
|
45
|
+
"@types/react": "^19.2.14",
|
|
46
|
+
"@types/react-dom": "^19.2.3",
|
|
47
|
+
"next": "16.1.6",
|
|
48
|
+
"react": "19.2.4",
|
|
49
|
+
"react-dom": "19.2.4",
|
|
50
|
+
"typescript": "^5.9.3"
|
|
49
51
|
},
|
|
50
52
|
"files": [
|
|
51
|
-
"
|
|
52
|
-
"
|
|
53
|
-
"!src/**/README.md",
|
|
53
|
+
"dist",
|
|
54
|
+
"src",
|
|
54
55
|
"package.json"
|
|
55
56
|
]
|
|
56
57
|
}
|