@akc42/app-utils 3.2.2 → 3.3.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/config-promise.js CHANGED
@@ -20,12 +20,12 @@
20
20
 
21
21
  let configPromise;
22
22
 
23
- export function mockConfig(promise) {
24
- configPromise = promise;
23
+ export function setConfig(promise) {
24
+ configPromise = promise; //set to undefined to allow config call to request from server again.
25
25
  }
26
26
 
27
27
  async function config() {
28
- if (configPromise === undefined) {
28
+ if (typeof configPromise === 'undefined') {
29
29
  let resolved = false;
30
30
  let resolver;
31
31
  configPromise = new Promise(accept => resolver = accept);
package/debug.js CHANGED
@@ -21,7 +21,7 @@
21
21
  /*
22
22
  The purpose of this module is to provide a debugable capability which can be
23
23
  dynamically switched on and off browser by setting a key in the config
24
- returned by the server. It will post request to '/api/log' url with
24
+ returned by the server. It will post request to '/api/debuglog' url with
25
25
  application/json body part containing message, topic and gap, where message is
26
26
  the concatenation of the debug parameters separated by space, topic is the
27
27
  topic of this debug message and gap is the number of milliseconds since the
@@ -41,31 +41,89 @@
41
41
 
42
42
  debug(..messages) //messages will be concatenated by space
43
43
 
44
- the debug function will only log the message if config.debug (see
45
- config-promise) is set to a string which is a comma separated list of topics
44
+ the debug function will only log the message on the server if config.debug (see
45
+ config-promise) is set to a string which is a colon separated list of topics
46
46
  and that list has the topic for this debug call in it.
47
47
 
48
48
  NOTE: It is normally expected for the server to provide a mechanism to update
49
- the config before it is returned and for the client to be restarted to enable
50
- the appropriate debug topics, an alternative could be for client side function
51
- to use the `mockConfig` call to replace the promise with one which contained a
52
- different list of topics. debug checks the list of topics on every call so
53
- would dynamically pick up the changes
49
+ the config before it is returned, However an alternative approach would be to
50
+ specifically overwrite sessionStorage 'debug' item with a new list of topics when you want
51
+ debug to switch on and off dynamically.
52
+
53
+ regardless of whether the message is logged on the server, it is also added to the performance.mark buffer
54
+ so that it can be sent to the server on a crash.
55
+
56
+ Although Debug is the default export this module also provides the following named exports
57
+
58
+ initialiseDebug - this function is used to manage tracing of debug messages regardless of whether the topic is set
59
+ It also adds an event handler to handle resource buffer full events. NOTE if the buffer fills up priority is given
60
+
61
+ unloadDebug - this function tidies up and reverses the initialiseDebug
62
+
63
+ debugDump - perform a dump to the server (and and a clearing out of the buffered info) of the debug calls made to date
54
64
 
55
65
  */
56
66
  import config from './config-promise.js';
57
67
 
68
+ const BUFFER_SIZE = 50;
69
+ const KEY_TOPIC = 'key'; //topic name which will get kept from a full resource buffer when we empty it.
70
+ let buffer = []; //buffer of up to 50 topic/message pairs to allow us to hold some if resource buffer becomes full;
71
+
58
72
  const topicMap = new Map();
59
73
 
74
+ let initialised = false;
75
+
76
+ function bufferFull() {
77
+ const entries = performance.getEntriesByType('mark');
78
+ performance.clearMarks();
79
+ if (entries.length > BUFFER_SIZE) {
80
+ let i = 0;
81
+ buffer = entries.filter((entry) => {
82
+ if (entry.name !== KEY_TOPIC) i++; //don't count critical topics in our
83
+ return i <= BUFFER_SIZE || entry.name === KEY_TOPIC;
84
+ }).map(entry => ({ topic: entry.name, message: entry.detail, time: Math.round(entry.startTime) }));
85
+ } else {
86
+ const len = buffer.length;
87
+ if ((len + entries.length) > BUFFER_SIZE) {
88
+ buffer.splice(0, len + entries.length - BUFFER_SIZE); //get rid of the head entries that would cause overflow
89
+ }
90
+ buffer = buffer.concat(entries.map(entry => ({ topic: entry.name, message: entry.detail, time: Math.round(entry.startTime) })))
91
+ }
92
+
93
+ }
94
+
95
+ function initialiseDebug() {
96
+ initialised = true;
97
+ buffer = [];
98
+ performance.setResourceTimingBufferSize(150)
99
+ window.addEventListener('resourcetimingbufferfull', bufferFull)
100
+ }
101
+
102
+ function unloadDebug() {
103
+ window.removeEventListener('resourcetimingbufferfull', bufferFull);
104
+ }
105
+ function debugDump() {
106
+ bufferFull(); //this clears out the marks and gives us a buffer to now send to
107
+ buffer.reverse();
108
+ for (let i = 0; i < buffer.length; i++) {
109
+ const blob = new Blob([JSON.stringify({
110
+ message: buffer[i].message,
111
+ gap: i < buffer.length - 1 ? buffer[i].time - buffer[i + 1].time : 0
112
+ })], { type: 'application/json' });
113
+ navigator.sendBeacon(`/api/debuglog/${buffer[i].topic}`, blob);
114
+ }
115
+ buffer = []; //we will start our buffer from scratch again
116
+
117
+ }
60
118
 
61
119
 
62
- function Debug (t) {
120
+ function Debug(t) {
63
121
  if (typeof t !== 'string' || t.length === 0 || !/^[a-zA-Z]+$/.test(t)) {
64
122
  console.error('Debug requires topic which is a non zero length string of only letters', t, 'Received');
65
123
  throw new Error('Invalid Debug Topic');
66
124
  }
67
- const tl = t.toLowerCase();
68
- if (topicMap.has(tl) ) {
125
+ const tl = t.toLowerCase();
126
+ if (topicMap.has(tl)) {
69
127
  const topic = topicMap.get(tl);
70
128
  return topic.debug;
71
129
  }
@@ -79,9 +137,12 @@ function Debug (t) {
79
137
  const now = new Date().getTime();
80
138
  const gap = now - this.timestamp;
81
139
  this.timestamp = now;
140
+ const message = args.reduce((cum, arg) => {
141
+ return `${cum} ${arg}`.trim();
142
+ }, '');
143
+ if (initialised) performance.mark(this.topic, { detail: message }); //save our message locally regardless of if enabled
82
144
  if (!this.defined) {
83
- //just await the config the first time through. Thereafter we can assume its done
84
- await config();
145
+ await config();
85
146
  this.defined = true;
86
147
  }
87
148
  let enabled = false;
@@ -90,16 +151,11 @@ function Debug (t) {
90
151
  const topics = debugConf.split(':');
91
152
  if (topics.includes(this.topic)) enabled = true;
92
153
  }
93
-
94
154
  if (enabled) {
95
- const message = args.reduce((cum, arg) => {
96
- return `${cum} ${arg}`.trim();
97
- }, '');
98
- console.log(`+${gap}ms`, this.topic, message);
99
155
  const blob = new Blob([JSON.stringify({
100
156
  message: message,
101
157
  gap: gap
102
- })], { type: 'application/json' })
158
+ })], { type: 'application/json' });
103
159
 
104
160
  navigator.sendBeacon(`/api/debuglog/${this.topic}`, blob);
105
161
  }
@@ -110,4 +166,4 @@ function Debug (t) {
110
166
  return topicHandler.debug
111
167
  }
112
168
 
113
- export default Debug;
169
+ export { Debug as default, initialiseDebug, unloadDebug, debugDump };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@akc42/app-utils",
3
- "version": "3.2.2",
3
+ "version": "3.3.0",
4
4
  "description": "General Utilities for SPAs",
5
5
  "exports": {
6
6
  ".": "./*.js"