@springfield/ham-radio-utils 2.1.0 → 2.2.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.
@@ -1,48 +1,48 @@
1
- import { LogLayer } from 'loglayer';
2
1
  import { BaseTransport } from '@loglayer/transport';
2
+ import { LogLayer } from 'loglayer';
3
3
  import { UILogger } from './ui-logger.js';
4
4
  /**
5
5
  * Custom LogLayer transport for capturing UI log entries.
6
6
  */
7
- class UILogTransport extends BaseTransport {
7
+ const UILogTransport = class extends BaseTransport {
8
8
  #callback;
9
9
  constructor(callback) {
10
10
  // Pass a dummy logger to BaseTransport
11
- super({ logger: { log: () => { } }, id: 'ui-logger-transport', enabled: true });
11
+ super({ enabled: true, id: 'ui-logger-transport', logger: { log: () => { } } });
12
12
  this.#callback = callback;
13
13
  }
14
14
  shipToLogger(params) {
15
15
  const logEntry = {
16
- timestamp: new Date().toISOString(),
17
- level: params.logLevel,
18
- message: params.messages.join(' '),
19
16
  data: params.data,
20
17
  hasData: params.hasData,
18
+ level: params.logLevel,
19
+ message: params.messages.join(' '),
20
+ timestamp: new Date().toISOString(),
21
21
  };
22
22
  this.#callback(logEntry);
23
23
  return [];
24
24
  }
25
- }
26
- export function createUILogger(config = {}) {
25
+ };
26
+ export const createUILogger = function createUILogger(config = {}) {
27
27
  // By default, just print to console as JSON
28
28
  const defaultCallback = (logEntry) => {
29
29
  console.log(JSON.stringify(logEntry));
30
30
  };
31
31
  const transport = config.customTransport || new UILogTransport(defaultCallback);
32
32
  const logger = new LogLayer({
33
- transport,
34
33
  consoleDebug: config.enableConsoleDebug ?? false,
34
+ transport,
35
35
  });
36
36
  logger.setLevel('debug');
37
37
  return new UILogger(logger);
38
- }
39
- export function createUILoggerWithCallback(callback, config = {}) {
38
+ };
39
+ export const createUILoggerWithCallback = function createUILoggerWithCallback(callback, config = {}) {
40
40
  const transport = new UILogTransport(callback);
41
41
  const logger = new LogLayer({
42
- transport,
43
42
  consoleDebug: config.enableConsoleDebug ?? false,
43
+ transport,
44
44
  });
45
45
  logger.setLevel('debug');
46
46
  return new UILogger(logger);
47
- }
47
+ };
48
48
  //# sourceMappingURL=ui-logger-factory.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ui-logger-factory.js","sourceRoot":"","sources":["../../src/utils/ui-logger-factory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AACpC,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE1C;;GAEG;AACH,MAAM,cAAe,SAAQ,aAAkB;IAC7C,SAAS,CAA0B;IAEnC,YAAY,QAAiC;QAC3C,uCAAuC;QACvC,KAAK,CAAC,EAAE,MAAM,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,GAAE,CAAC,EAAE,EAAE,EAAE,EAAE,qBAAqB,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;QAC/E,IAAI,CAAC,SAAS,GAAG,QAAQ,CAAC;IAC5B,CAAC;IAED,YAAY,CAAC,MAAW;QACtB,MAAM,QAAQ,GAAG;YACf,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;YACnC,KAAK,EAAE,MAAM,CAAC,QAAQ;YACtB,OAAO,EAAE,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC;YAClC,IAAI,EAAE,MAAM,CAAC,IAAI;YACjB,OAAO,EAAE,MAAM,CAAC,OAAO;SACxB,CAAC;QACF,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;QACzB,OAAO,EAAE,CAAC;IACZ,CAAC;CACF;AAOD,MAAM,UAAU,cAAc,CAAC,SAAyB,EAAE;IACxD,4CAA4C;IAC5C,MAAM,eAAe,GAAG,CAAC,QAAa,EAAE,EAAE;QACxC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC,CAAC;IACxC,CAAC,CAAC;IACF,MAAM,SAAS,GAAG,MAAM,CAAC,eAAe,IAAI,IAAI,cAAc,CAAC,eAAe,CAAC,CAAC;IAChF,MAAM,MAAM,GAAG,IAAI,QAAQ,CAAC;QAC1B,SAAS;QACT,YAAY,EAAE,MAAM,CAAC,kBAAkB,IAAI,KAAK;KACjD,CAAC,CAAC;IACH,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;IACzB,OAAO,IAAI,QAAQ,CAAC,MAAM,CAAC,CAAC;AAC9B,CAAC;AAED,MAAM,UAAU,0BAA0B,CAAC,QAAiC,EAAE,SAAyB,EAAE;IACvG,MAAM,SAAS,GAAG,IAAI,cAAc,CAAC,QAAQ,CAAC,CAAC;IAC/C,MAAM,MAAM,GAAG,IAAI,QAAQ,CAAC;QAC1B,SAAS;QACT,YAAY,EAAE,MAAM,CAAC,kBAAkB,IAAI,KAAK;KACjD,CAAC,CAAC;IACH,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;IACzB,OAAO,IAAI,QAAQ,CAAC,MAAM,CAAC,CAAC;AAC9B,CAAC","sourcesContent":["import { LogLayer } from 'loglayer';\nimport { BaseTransport } from '@loglayer/transport';\nimport { UILogger } from './ui-logger.js';\n\n/**\n * Custom LogLayer transport for capturing UI log entries.\n */\nclass UILogTransport extends BaseTransport<any> {\n #callback: (logEntry: any) => void;\n\n constructor(callback: (logEntry: any) => void) {\n // Pass a dummy logger to BaseTransport\n super({ logger: { log: () => {} }, id: 'ui-logger-transport', enabled: true });\n this.#callback = callback;\n }\n\n shipToLogger(params: any) {\n const logEntry = {\n timestamp: new Date().toISOString(),\n level: params.logLevel,\n message: params.messages.join(' '),\n data: params.data,\n hasData: params.hasData,\n };\n this.#callback(logEntry);\n return [];\n }\n}\n\nexport interface UILoggerConfig {\n enableConsoleDebug?: boolean;\n customTransport?: any;\n}\n\nexport function createUILogger(config: UILoggerConfig = {}): UILogger {\n // By default, just print to console as JSON\n const defaultCallback = (logEntry: any) => {\n console.log(JSON.stringify(logEntry));\n };\n const transport = config.customTransport || new UILogTransport(defaultCallback);\n const logger = new LogLayer({\n transport,\n consoleDebug: config.enableConsoleDebug ?? false,\n });\n logger.setLevel('debug');\n return new UILogger(logger);\n}\n\nexport function createUILoggerWithCallback(callback: (logEntry: any) => void, config: UILoggerConfig = {}): UILogger {\n const transport = new UILogTransport(callback);\n const logger = new LogLayer({\n transport,\n consoleDebug: config.enableConsoleDebug ?? false,\n });\n logger.setLevel('debug');\n return new UILogger(logger);\n}\n"]}
1
+ {"version":3,"file":"ui-logger-factory.js","sourceRoot":"","sources":["../../src/utils/ui-logger-factory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AACpC,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE1C;;GAEG;AACH,MAAM,cAAc,GAAG,KAAM,SAAQ,aAAkB;IACrD,SAAS,CAA0B;IAEnC,YAAY,QAAiC;QAC3C,uCAAuC;QACvC,KAAK,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,qBAAqB,EAAE,MAAM,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,GAAE,CAAC,EAAE,EAAE,CAAC,CAAC;QAC/E,IAAI,CAAC,SAAS,GAAG,QAAQ,CAAC;IAC5B,CAAC;IAED,YAAY,CAAC,MAAW;QACtB,MAAM,QAAQ,GAAG;YACf,IAAI,EAAE,MAAM,CAAC,IAAI;YACjB,OAAO,EAAE,MAAM,CAAC,OAAO;YACvB,KAAK,EAAE,MAAM,CAAC,QAAQ;YACtB,OAAO,EAAE,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC;YAClC,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;SACpC,CAAC;QACF,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;QACzB,OAAO,EAAE,CAAC;IACZ,CAAC;CACF,CAAC;AAOF,MAAM,CAAC,MAAM,cAAc,GAAG,SAAS,cAAc,CAAC,SAAyB,EAAE;IAC/E,4CAA4C;IAC5C,MAAM,eAAe,GAAG,CAAC,QAAa,EAAE,EAAE;QACxC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC,CAAC;IACxC,CAAC,CAAC;IACF,MAAM,SAAS,GAAG,MAAM,CAAC,eAAe,IAAI,IAAI,cAAc,CAAC,eAAe,CAAC,CAAC;IAChF,MAAM,MAAM,GAAG,IAAI,QAAQ,CAAC;QAC1B,YAAY,EAAE,MAAM,CAAC,kBAAkB,IAAI,KAAK;QAChD,SAAS;KACV,CAAC,CAAC;IACH,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;IACzB,OAAO,IAAI,QAAQ,CAAC,MAAM,CAAC,CAAC;AAC9B,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,0BAA0B,GAAG,SAAS,0BAA0B,CAAC,QAAiC,EAAE,SAAyB,EAAE;IAC1I,MAAM,SAAS,GAAG,IAAI,cAAc,CAAC,QAAQ,CAAC,CAAC;IAC/C,MAAM,MAAM,GAAG,IAAI,QAAQ,CAAC;QAC1B,YAAY,EAAE,MAAM,CAAC,kBAAkB,IAAI,KAAK;QAChD,SAAS;KACV,CAAC,CAAC;IACH,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;IACzB,OAAO,IAAI,QAAQ,CAAC,MAAM,CAAC,CAAC;AAC9B,CAAC,CAAC","sourcesContent":["import { BaseTransport } from '@loglayer/transport';\nimport { LogLayer } from 'loglayer';\nimport { UILogger } from './ui-logger.js';\n\n/**\n * Custom LogLayer transport for capturing UI log entries.\n */\nconst UILogTransport = class extends BaseTransport<any> {\n #callback: (logEntry: any) => void;\n\n constructor(callback: (logEntry: any) => void) {\n // Pass a dummy logger to BaseTransport\n super({ enabled: true, id: 'ui-logger-transport', logger: { log: () => {} } });\n this.#callback = callback;\n }\n\n shipToLogger(params: any) {\n const logEntry = {\n data: params.data,\n hasData: params.hasData,\n level: params.logLevel,\n message: params.messages.join(' '),\n timestamp: new Date().toISOString(),\n };\n this.#callback(logEntry);\n return [];\n }\n};\n\nexport interface UILoggerConfig {\n customTransport?: any;\n enableConsoleDebug?: boolean;\n}\n\nexport const createUILogger = function createUILogger(config: UILoggerConfig = {}): UILogger {\n // By default, just print to console as JSON\n const defaultCallback = (logEntry: any) => {\n console.log(JSON.stringify(logEntry));\n };\n const transport = config.customTransport || new UILogTransport(defaultCallback);\n const logger = new LogLayer({\n consoleDebug: config.enableConsoleDebug ?? false,\n transport,\n });\n logger.setLevel('debug');\n return new UILogger(logger);\n};\n\nexport const createUILoggerWithCallback = function createUILoggerWithCallback(callback: (logEntry: any) => void, config: UILoggerConfig = {}): UILogger {\n const transport = new UILogTransport(callback);\n const logger = new LogLayer({\n consoleDebug: config.enableConsoleDebug ?? false,\n transport,\n });\n logger.setLevel('debug');\n return new UILogger(logger);\n};\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"ui-logger.d.ts","sourceRoot":"","sources":["../../src/utils/ui-logger.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAE1C;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;CACpB;AAED;;GAEG;AACH,qBAAa,QAAQ;IACnB,OAAO,CAAC,MAAM,CAAY;IAC1B,OAAO,CAAC,iBAAiB,CAA6B;gBAE1C,MAAM,EAAE,SAAS;IAI7B,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,cAAc,GAAG,IAAI;IAqBlG,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,cAAc,EAAE,OAAO,EAAE,GAAG,GAAG,IAAI;IA4CrH,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,cAAc,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,GAAG,IAAI;IAkCnI,OAAO,CAAC,cAAc;IAUtB,OAAO,CAAC,kBAAkB;IAS1B,OAAO,CAAC,eAAe;CAaxB"}
1
+ {"version":3,"file":"ui-logger.d.ts","sourceRoot":"","sources":["../../src/utils/ui-logger.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAE1C;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;CACpB;AAED;;GAEG;AACH,qBAAa,QAAQ;IACnB,OAAO,CAAC,MAAM,CAAY;IAC1B,OAAO,CAAC,iBAAiB,CAA6B;gBAE1C,MAAM,EAAE,SAAS;IAK7B,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,cAAc,GAAG,IAAI;IAsBlG,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,cAAc,EAAE,OAAO,EAAE,GAAG,GAAG,IAAI;IA6CrH,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,cAAc,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,GAAG,IAAI;IAkCnI,OAAO,CAAC,cAAc;IAsBtB,OAAO,CAAC,kBAAkB;IAmB1B,OAAO,CAAC,eAAe;CAiBxB"}
@@ -7,6 +7,7 @@ export class UILogger {
7
7
  constructor(logger) {
8
8
  this.logger = logger;
9
9
  }
10
+ // eslint-disable-next-line max-params
10
11
  startCommand(stepIndex, totalSteps, operation, step) {
11
12
  const commandId = `${operation}-${stepIndex}`;
12
13
  const startTime = Date.now();
@@ -18,13 +19,14 @@ export class UILogger {
18
19
  commandId,
19
20
  commandType,
20
21
  description,
21
- stepIndex,
22
- totalSteps,
23
22
  operation,
24
23
  startTime,
24
+ stepIndex,
25
+ totalSteps,
25
26
  })
26
27
  .info('Command started');
27
28
  }
29
+ // eslint-disable-next-line max-params
28
30
  logCommandSuccess(stepIndex, totalSteps, operation, step, context) {
29
31
  const commandId = `${operation}-${stepIndex}`;
30
32
  const startTime = this.commandStartTimes.get(commandId) || Date.now();
@@ -38,31 +40,32 @@ export class UILogger {
38
40
  const dataExpected = this.getExpectedData(step);
39
41
  // For readSegment, also extract chunk logs if present
40
42
  let dataChunks = undefined;
41
- if (commandType === 'readSegment') {
43
+ if ('readSegment' === commandType) {
42
44
  dataChunks = context.variables?.get('lastReadSegmentChunks');
43
45
  // For readSegment, we don't need the flattened dataSent/dataReceived since we have dataChunks
44
46
  }
45
47
  // Convert dataSent and dataReceived to byte array format if they exist
46
- const dataSentArray = dataSent ? Array.from(dataSent) : undefined;
47
- const dataReceivedArray = dataReceived ? Array.from(dataReceived) : undefined;
48
+ const dataSentArray = dataSent ? [...dataSent] : undefined;
49
+ const dataReceivedArray = dataReceived ? [...dataReceived] : undefined;
48
50
  this.logger
49
51
  .withMetadata({
50
52
  commandType,
51
- description,
52
- dataSent: dataSentArray,
53
+ dataChunks,
53
54
  dataExpected,
54
55
  dataReceived: dataReceivedArray,
55
- dataChunks,
56
- success: true,
57
- startTime,
58
- endTime,
56
+ dataSent: dataSentArray,
57
+ description,
59
58
  duration,
59
+ endTime,
60
+ operation,
61
+ startTime,
60
62
  stepIndex,
63
+ success: true,
61
64
  totalSteps,
62
- operation,
63
65
  })
64
66
  .info('Command completed successfully');
65
67
  }
68
+ // eslint-disable-next-line max-params
66
69
  logCommandFailure(stepIndex, totalSteps, operation, step, error, context) {
67
70
  const commandId = `${operation}-${stepIndex}`;
68
71
  const startTime = this.commandStartTimes.get(commandId) || Date.now();
@@ -74,62 +77,75 @@ export class UILogger {
74
77
  const dataSent = context.variables?.get('lastSentData');
75
78
  const dataExpected = this.getExpectedData(step);
76
79
  // Convert dataSent to byte array format if it exists
77
- const dataSentArray = dataSent ? Array.from(dataSent) : undefined;
80
+ const dataSentArray = dataSent ? [...dataSent] : undefined;
78
81
  this.logger
79
82
  .withMetadata({
80
83
  commandType,
81
- description,
82
- dataSent: dataSentArray,
83
84
  dataExpected,
85
+ dataSent: dataSentArray,
86
+ description,
87
+ duration,
88
+ endTime,
84
89
  error: error.message,
85
- success: false,
90
+ operation,
86
91
  startTime,
87
- endTime,
88
- duration,
89
92
  stepIndex,
93
+ success: false,
90
94
  totalSteps,
91
- operation,
92
95
  })
93
96
  .info('Command failed');
94
97
  }
95
98
  getCommandType(step) {
96
- if ('sendReceive' in step)
99
+ if ('sendReceive' in step) {
97
100
  return 'sendReceive';
98
- if ('send' in step)
101
+ }
102
+ if ('send' in step) {
99
103
  return 'send';
100
- if ('receive' in step)
104
+ }
105
+ if ('receive' in step) {
101
106
  return 'receive';
102
- if ('readSegment' in step)
107
+ }
108
+ if ('readSegment' in step) {
103
109
  return 'readSegment';
104
- if ('writeSegment' in step)
110
+ }
111
+ if ('writeSegment' in step) {
105
112
  return 'writeSegment';
106
- if ('setVariable' in step)
113
+ }
114
+ if ('setVariable' in step) {
107
115
  return 'setVariable';
116
+ }
108
117
  return 'unknown';
109
118
  }
110
119
  getStepDescription(step) {
111
- if ('sendReceive' in step && step.sendReceive.description)
120
+ if ('sendReceive' in step && step.sendReceive.description) {
112
121
  return step.sendReceive.description;
113
- if ('send' in step && step.send.description)
122
+ }
123
+ if ('send' in step && step.send.description) {
114
124
  return step.send.description;
115
- if ('receive' in step && step.receive.description)
125
+ }
126
+ if ('receive' in step && step.receive.description) {
116
127
  return step.receive.description;
117
- if ('readSegment' in step && step.readSegment.description)
128
+ }
129
+ if ('readSegment' in step && step.readSegment.description) {
118
130
  return step.readSegment.description;
119
- if ('writeSegment' in step && step.writeSegment.description)
131
+ }
132
+ if ('writeSegment' in step && step.writeSegment.description) {
120
133
  return step.writeSegment.description;
134
+ }
121
135
  return 'No description';
122
136
  }
123
137
  getExpectedData(step) {
124
- if ('sendReceive' in step && step.sendReceive.receive)
138
+ if ('sendReceive' in step && step.sendReceive.receive) {
125
139
  return step.sendReceive.receive;
126
- if ('receive' in step)
140
+ }
141
+ if ('receive' in step) {
127
142
  return step.receive;
143
+ }
128
144
  if ('readSegment' in step) {
129
145
  // For readSegment, return both start and end chunk receive patterns
130
146
  return {
131
- startChunk: step.readSegment.startChunk.receive,
132
147
  endChunk: step.readSegment.endChunk.receive,
148
+ startChunk: step.readSegment.startChunk.receive,
133
149
  type: 'readSegment',
134
150
  };
135
151
  }
@@ -1 +1 @@
1
- {"version":3,"file":"ui-logger.js","sourceRoot":"","sources":["../../src/utils/ui-logger.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ILogLayer } from 'loglayer';\n\n/**\n * UI Logger: Command-Level Logging for UI Display\n *\n * This module provides a specialized logger for capturing command-level information\n * that can be easily parsed and displayed in a UI. It captures details about\n * commands, data sent, data expected, and data received in a structured format.\n *\n * Purpose:\n * - Captures command-level information for UI display\n * - Provides structured JSON logging for easy parsing\n * - Tracks command execution details including timing\n * - Supports protocol debugging in UI environments\n * - Maintains separation from debug logging\n *\n * Design Rationale:\n * - Single log entry per command provides clean UI display\n * - JSON structure enables easy parsing and filtering\n * - Command-level granularity is appropriate for UI debugging\n * - Structured data supports rich UI representations\n * - Separate from debug logging avoids UI noise\n */\n\n/**\n * Generic protocol step interface for UI logging\n */\nexport interface UIProtocolStep {\n [key: string]: any;\n}\n\n/**\n * UI Logger for capturing command-level information for UI display\n */\nexport class UILogger {\n private logger: ILogLayer;\n private commandStartTimes = new Map<string, number>();\n\n constructor(logger: ILogLayer) {\n this.logger = logger;\n }\n\n startCommand(stepIndex: number, totalSteps: number, operation: string, step: UIProtocolStep): void {\n const commandId = `${operation}-${stepIndex}`;\n const startTime = Date.now();\n this.commandStartTimes.set(commandId, startTime);\n\n const commandType = this.getCommandType(step);\n const description = this.getStepDescription(step);\n\n this.logger\n .withMetadata({\n commandId,\n commandType,\n description,\n stepIndex,\n totalSteps,\n operation,\n startTime,\n })\n .info('Command started');\n }\n\n logCommandSuccess(stepIndex: number, totalSteps: number, operation: string, step: UIProtocolStep, context: any): void {\n const commandId = `${operation}-${stepIndex}`;\n const startTime = this.commandStartTimes.get(commandId) || Date.now();\n const endTime = Date.now();\n const duration = endTime - startTime;\n\n const commandType = this.getCommandType(step);\n const description = this.getStepDescription(step);\n\n // Extract sent and received data from context\n const dataSent = context.variables?.get('lastSentData');\n const dataReceived = context.variables?.get('lastReceivedData');\n const dataExpected = this.getExpectedData(step);\n\n // For readSegment, also extract chunk logs if present\n let dataChunks = undefined;\n if (commandType === 'readSegment') {\n dataChunks = context.variables?.get('lastReadSegmentChunks');\n // For readSegment, we don't need the flattened dataSent/dataReceived since we have dataChunks\n }\n\n // Convert dataSent and dataReceived to byte array format if they exist\n const dataSentArray = dataSent ? Array.from(dataSent) : undefined;\n const dataReceivedArray = dataReceived ? Array.from(dataReceived) : undefined;\n\n this.logger\n .withMetadata({\n commandType,\n description,\n dataSent: dataSentArray,\n dataExpected,\n dataReceived: dataReceivedArray,\n dataChunks,\n success: true,\n startTime,\n endTime,\n duration,\n stepIndex,\n totalSteps,\n operation,\n })\n .info('Command completed successfully');\n }\n\n logCommandFailure(stepIndex: number, totalSteps: number, operation: string, step: UIProtocolStep, error: Error, context: any): void {\n const commandId = `${operation}-${stepIndex}`;\n const startTime = this.commandStartTimes.get(commandId) || Date.now();\n const endTime = Date.now();\n const duration = endTime - startTime;\n\n const commandType = this.getCommandType(step);\n const description = this.getStepDescription(step);\n\n // Extract sent data from context\n const dataSent = context.variables?.get('lastSentData');\n const dataExpected = this.getExpectedData(step);\n\n // Convert dataSent to byte array format if it exists\n const dataSentArray = dataSent ? Array.from(dataSent) : undefined;\n\n this.logger\n .withMetadata({\n commandType,\n description,\n dataSent: dataSentArray,\n dataExpected,\n error: error.message,\n success: false,\n startTime,\n endTime,\n duration,\n stepIndex,\n totalSteps,\n operation,\n })\n .info('Command failed');\n }\n\n private getCommandType(step: UIProtocolStep): string {\n if ('sendReceive' in step) return 'sendReceive';\n if ('send' in step) return 'send';\n if ('receive' in step) return 'receive';\n if ('readSegment' in step) return 'readSegment';\n if ('writeSegment' in step) return 'writeSegment';\n if ('setVariable' in step) return 'setVariable';\n return 'unknown';\n }\n\n private getStepDescription(step: UIProtocolStep): string {\n if ('sendReceive' in step && step.sendReceive.description) return step.sendReceive.description;\n if ('send' in step && step.send.description) return step.send.description;\n if ('receive' in step && step.receive.description) return step.receive.description;\n if ('readSegment' in step && step.readSegment.description) return step.readSegment.description;\n if ('writeSegment' in step && step.writeSegment.description) return step.writeSegment.description;\n return 'No description';\n }\n\n private getExpectedData(step: UIProtocolStep): any {\n if ('sendReceive' in step && step.sendReceive.receive) return step.sendReceive.receive;\n if ('receive' in step) return step.receive;\n if ('readSegment' in step) {\n // For readSegment, return both start and end chunk receive patterns\n return {\n startChunk: step.readSegment.startChunk.receive,\n endChunk: step.readSegment.endChunk.receive,\n type: 'readSegment',\n };\n }\n return undefined;\n }\n}\n"]}
1
+ {"version":3,"file":"ui-logger.js","sourceRoot":"","sources":["../../src/utils/ui-logger.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ILogLayer } from 'loglayer';\n\n/**\n * UI Logger: Command-Level Logging for UI Display\n *\n * This module provides a specialized logger for capturing command-level information\n * that can be easily parsed and displayed in a UI. It captures details about\n * commands, data sent, data expected, and data received in a structured format.\n *\n * Purpose:\n * - Captures command-level information for UI display\n * - Provides structured JSON logging for easy parsing\n * - Tracks command execution details including timing\n * - Supports protocol debugging in UI environments\n * - Maintains separation from debug logging\n *\n * Design Rationale:\n * - Single log entry per command provides clean UI display\n * - JSON structure enables easy parsing and filtering\n * - Command-level granularity is appropriate for UI debugging\n * - Structured data supports rich UI representations\n * - Separate from debug logging avoids UI noise\n */\n\n/**\n * Generic protocol step interface for UI logging\n */\nexport interface UIProtocolStep {\n [key: string]: any;\n}\n\n/**\n * UI Logger for capturing command-level information for UI display\n */\nexport class UILogger {\n private logger: ILogLayer;\n private commandStartTimes = new Map<string, number>();\n\n constructor(logger: ILogLayer) {\n this.logger = logger;\n }\n\n // eslint-disable-next-line max-params\n startCommand(stepIndex: number, totalSteps: number, operation: string, step: UIProtocolStep): void {\n const commandId = `${operation}-${stepIndex}`;\n const startTime = Date.now();\n this.commandStartTimes.set(commandId, startTime);\n\n const commandType = this.getCommandType(step);\n const description = this.getStepDescription(step);\n\n this.logger\n .withMetadata({\n commandId,\n commandType,\n description,\n operation,\n startTime,\n stepIndex,\n totalSteps,\n })\n .info('Command started');\n }\n\n // eslint-disable-next-line max-params\n logCommandSuccess(stepIndex: number, totalSteps: number, operation: string, step: UIProtocolStep, context: any): void {\n const commandId = `${operation}-${stepIndex}`;\n const startTime = this.commandStartTimes.get(commandId) || Date.now();\n const endTime = Date.now();\n const duration = endTime - startTime;\n\n const commandType = this.getCommandType(step);\n const description = this.getStepDescription(step);\n\n // Extract sent and received data from context\n const dataSent = context.variables?.get('lastSentData');\n const dataReceived = context.variables?.get('lastReceivedData');\n const dataExpected = this.getExpectedData(step);\n\n // For readSegment, also extract chunk logs if present\n let dataChunks = undefined;\n if ('readSegment' === commandType) {\n dataChunks = context.variables?.get('lastReadSegmentChunks');\n // For readSegment, we don't need the flattened dataSent/dataReceived since we have dataChunks\n }\n\n // Convert dataSent and dataReceived to byte array format if they exist\n const dataSentArray = dataSent ? [...dataSent] : undefined;\n const dataReceivedArray = dataReceived ? [...dataReceived] : undefined;\n\n this.logger\n .withMetadata({\n commandType,\n dataChunks,\n dataExpected,\n dataReceived: dataReceivedArray,\n dataSent: dataSentArray,\n description,\n duration,\n endTime,\n operation,\n startTime,\n stepIndex,\n success: true,\n totalSteps,\n })\n .info('Command completed successfully');\n }\n\n // eslint-disable-next-line max-params\n logCommandFailure(stepIndex: number, totalSteps: number, operation: string, step: UIProtocolStep, error: Error, context: any): void {\n const commandId = `${operation}-${stepIndex}`;\n const startTime = this.commandStartTimes.get(commandId) || Date.now();\n const endTime = Date.now();\n const duration = endTime - startTime;\n\n const commandType = this.getCommandType(step);\n const description = this.getStepDescription(step);\n\n // Extract sent data from context\n const dataSent = context.variables?.get('lastSentData');\n const dataExpected = this.getExpectedData(step);\n\n // Convert dataSent to byte array format if it exists\n const dataSentArray = dataSent ? [...dataSent] : undefined;\n\n this.logger\n .withMetadata({\n commandType,\n dataExpected,\n dataSent: dataSentArray,\n description,\n duration,\n endTime,\n error: error.message,\n operation,\n startTime,\n stepIndex,\n success: false,\n totalSteps,\n })\n .info('Command failed');\n }\n\n private getCommandType(step: UIProtocolStep): string {\n if ('sendReceive' in step) {\n return 'sendReceive';\n }\n if ('send' in step) {\n return 'send';\n }\n if ('receive' in step) {\n return 'receive';\n }\n if ('readSegment' in step) {\n return 'readSegment';\n }\n if ('writeSegment' in step) {\n return 'writeSegment';\n }\n if ('setVariable' in step) {\n return 'setVariable';\n }\n return 'unknown';\n }\n\n private getStepDescription(step: UIProtocolStep): string {\n if ('sendReceive' in step && step.sendReceive.description) {\n return step.sendReceive.description;\n }\n if ('send' in step && step.send.description) {\n return step.send.description;\n }\n if ('receive' in step && step.receive.description) {\n return step.receive.description;\n }\n if ('readSegment' in step && step.readSegment.description) {\n return step.readSegment.description;\n }\n if ('writeSegment' in step && step.writeSegment.description) {\n return step.writeSegment.description;\n }\n return 'No description';\n }\n\n private getExpectedData(step: UIProtocolStep): any {\n if ('sendReceive' in step && step.sendReceive.receive) {\n return step.sendReceive.receive;\n }\n if ('receive' in step) {\n return step.receive;\n }\n if ('readSegment' in step) {\n // For readSegment, return both start and end chunk receive patterns\n return {\n endChunk: step.readSegment.endChunk.receive,\n startChunk: step.readSegment.startChunk.receive,\n type: 'readSegment',\n };\n }\n return undefined;\n }\n}\n"]}
package/package.json CHANGED
@@ -1,7 +1,8 @@
1
1
  {
2
2
  "name": "@springfield/ham-radio-utils",
3
- "version": "2.1.0",
3
+ "version": "2.2.0",
4
4
  "author": "Bryan Hunt",
5
+ "license": "MIT",
5
6
  "packageManager": "yarn@4.9.2",
6
7
  "readme": "README.md",
7
8
  "type": "module",
@@ -12,7 +13,7 @@
12
13
  "dist/"
13
14
  ],
14
15
  "scripts": {
15
- "build": "tsc && cp src/db/* dist/db/",
16
+ "build": "tsc && cp src/db/* dist/db/ && cp src/schemas/* dist/schemas/",
16
17
  "commitlint": "commitlint --edit",
17
18
  "lint": "oxlint",
18
19
  "lint:fix": "oxlint --fix --fix-suggestions",
@@ -50,7 +51,9 @@
50
51
  "access": "public"
51
52
  },
52
53
  "dependencies": {
53
- "@springfield/ham-radio-api": "^14.0.0",
54
+ "@springfield/ham-radio-api": "^16.1.1",
55
+ "ajv": "^8.17.1",
56
+ "ajv-formats": "^3.0.1",
54
57
  "fishery": "^2.3.1",
55
58
  "loglayer": "^6.4.3",
56
59
  "sprintf-js": "^1.1.3"
@@ -61,9 +64,11 @@
61
64
  "@semantic-release/changelog": "6.0.3",
62
65
  "@semantic-release/git": "10.0.1",
63
66
  "@semantic-release/gitlab": "13.2.6",
67
+ "@types/chai": "^5.2.2",
64
68
  "@types/sprintf-js": "1.1.4",
69
+ "chai": "^5.2.0",
65
70
  "husky": "9.1.7",
66
- "oxlint": "1.1.0",
71
+ "oxlint": "1.3.0",
67
72
  "semantic-release": "23.1.1",
68
73
  "semantic-release-yarn": "3.0.2",
69
74
  "typescript": "5.8.3"
package/src/index.ts CHANGED
@@ -9,6 +9,10 @@ export * from './utils/frequency-display.js';
9
9
  export * from './utils/memory-channel-utils.js';
10
10
  export * from './utils/memory-data-utils.js';
11
11
  export * from './utils/number-to-bcd.js';
12
+ export * from './utils/schema-validator.js';
12
13
  export * from './utils/to-hex-words.js';
13
14
  export * from './utils/ui-logger.js';
14
15
  export * from './utils/ui-logger-factory.js';
16
+
17
+ // Export the radio protocol schema
18
+ export { default as radioProtocolSchema } from './schemas/radio-protocol-schema.json' with { type: 'json' };