@oino-ts/types 0.0.11 → 0.0.13

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/README.md CHANGED
@@ -22,7 +22,7 @@
22
22
  ```
23
23
 
24
24
  ### Register database and logger
25
- Register your database implementation and logger (see [`OINOConsoleLog`](https://pragmatta.github.io/oino-ts/classes/core_src.OINOConsoleLog.html) how to implement your own)
25
+ Register your database implementation and logger (see [`OINOConsoleLog`](https://pragmatta.github.io/oino-ts/classes/types_src.OINOConsoleLog.html) how to implement your own)
26
26
 
27
27
  ```
28
28
  OINOLog.setLogger(new OINOConsoleLog())
@@ -30,13 +30,13 @@
30
30
  ```
31
31
 
32
32
  ### Create a database
33
- Creating a database connection [`OINODb`](https://pragmatta.github.io/oino-ts/classes/core_src_OINODb.OINODb.html) is done by passing [`OINODbParams`](https://pragmatta.github.io/oino-ts/types/core_src.OINODbParams.html) to the factory method. For [`OINODbBunSqlite`](https://pragmatta.github.io/oino-ts/classes/bunsqlite_OINODbBunSqlite.OINODbBunSqlite.html) that means a file url for the database file, for others network host, port, credentials etc.
33
+ Creating a database connection [`OINODb`](https://pragmatta.github.io/oino-ts/classes/db_src.OINODb.html) is done by passing [`OINODbParams`](https://pragmatta.github.io/oino-ts/types/db_src.OINODbParams.html) to the factory method. For [`OINODbBunSqlite`](https://pragmatta.github.io/oino-ts/classes/db_bunsqlite_src.OINODbBunSqlite.html) that means a file url for the database file, for others network host, port, credentials etc.
34
34
  ```
35
35
  const db:OINODb = await OINOFactory.createDb( { type: "OINODbBunSqlite", url: "file://../localDb/northwind.sqlite" } )
36
36
  ```
37
37
 
38
38
  ### Create an API
39
- From a database you can create an [`OINOApi`](https://pragmatta.github.io/oino-ts/classes/core_src_OINOApi.OINOApi.html) by passing [`OINOApiParams`](https://pragmatta.github.io/oino-ts/types/core_src.OINOApiParams.html) with table name and preferences to the factory method.
39
+ From a database you can create an [`OINOApi`](https://pragmatta.github.io/oino-ts/classes/db_src.OINODbApi.html) by passing [`OINOApiParams`](https://pragmatta.github.io/oino-ts/types/db_src.OINODbApiParams.html) with table name and preferences to the factory method.
40
40
  ```
41
41
  const api_employees:OINOApi = await OINOFactory.createApi(db, { tableName: "Employees", excludeFields:["BirthDate"] })
42
42
  ```
@@ -49,7 +49,7 @@
49
49
  ```
50
50
 
51
51
  ### Write results back to HTTP Response
52
- The results for a GET request will contain [`OINOModelSet`](https://pragmatta.github.io/oino-ts/classes/core_src_OINOModelSet.OINOModelSet.html) data that can be written out as JSON or CSV as needed. For other requests result is just success or error with messages.
52
+ The results for a GET request will contain [`OINOModelSet`](https://pragmatta.github.io/oino-ts/classes/db_src.OINODbModelSet.html) data that can be written out as JSON or CSV as needed. For other requests result is just success or error with messages.
53
53
  ```
54
54
  return new Response(result.modelset.writeString(OINOContentType.json))
55
55
  ```
@@ -134,7 +134,7 @@
134
134
  To support tables with multipart primary keys OINO generates a composite key `_OINOID_` that is included in the result and can be used as the REST ID. For example in the example above table `OrderDetails` has two primary keys `OrderID` and `ProductID` making the `_OINOID_` of form `11077:99`.
135
135
 
136
136
  ## Power Of SQL
137
- Since OINO controls the SQL, WHERE-conditions can be defined with [`OINOSqlFilter`](https://pragmatta.github.io/oino-ts/classes/core_src.OINOSqlFilter.html) and order with [`OINOSqlOrder`](https://pragmatta.github.io/oino-ts/classes/core_src.OINOSqlOrder.html) that are passed as HTTP request parameters. No more API development where you make unique API endpoints for each filter that fetch all data with original API and filter in backend code. Every API can be filtered when and as needed without unnessecary data tranfer and utilizing SQL indexing when available.
137
+ Since OINO controls the SQL, WHERE-conditions can be defined with [`OINOSqlFilter`](https://pragmatta.github.io/oino-ts/classes/db_src.OINODbSqlFilter.html) and order with [`OINOSqlOrder`](https://pragmatta.github.io/oino-ts/classes/db_src.OINODbSqlOrder.html) that are passed as HTTP request parameters. No more API development where you make unique API endpoints for each filter that fetch all data with original API and filter in backend code. Every API can be filtered when and as needed without unnessecary data tranfer and utilizing SQL indexing when available.
138
138
 
139
139
  ## Swagger Support
140
140
  Swagger is great as long as the definitions are updated and with OINO you can automatically get a Swagger definition including a data model schema.
@@ -149,7 +149,7 @@
149
149
  OINO is developped Typescript first but compiles to standard CommonJS and the NPM packages should work on either ESM / CommonJS. Checkout sample apps `readmeApp` (ESM) and `nodeApp` (CommonJS).
150
150
 
151
151
  ## HTMX support
152
- OINO is [htmx.org](https://htmx.org) friendly, allowing easy translation of [`OINODataRow`](https://pragmatta.github.io/oino-ts/types/core_src.OINODataRow.html) to HTML output using templates (cf. the [htmx sample app](https://github.com/pragmatta/oino-ts/tree/main/samples/htmxApp)).
152
+ OINO is [htmx.org](https://htmx.org) friendly, allowing easy translation of [`OINODataRow`](https://pragmatta.github.io/oino-ts/types/db_src.OINODataRow.html) to HTML output using templates (cf. the [htmx sample app](https://github.com/pragmatta/oino-ts/tree/main/samples/htmxApp)).
153
153
 
154
154
  ### Hashids
155
155
  Autoinc numeric id's are very pragmatic and fit well with OINO (e.g. using a form without primary key fields to insert new rows with database assigned ids). However it's not always sensible to share information about the sequence. Hashids solve this by masking the original values by encrypting the ids using AES-128 and some randomness. Length of the hashid can be chosen from 12-32 characters where longer ids provide more security. However this should not be considereded a cryptographic solution for keeping ids secret but rather making it infeasible to iterate all ids.
@@ -52,15 +52,50 @@ class OINOBenchmark {
52
52
  * Complete benchmark timing
53
53
  *
54
54
  * @param name of the benchmark
55
+ * @param category optional subcategory of the benchmark
55
56
  */
56
- static end(name) {
57
+ static end(name, category) {
57
58
  let result = 0;
58
59
  if (this._benchmarkEnabled[name]) {
60
+ const duration = performance.now() - this._benchmarkStart[name];
59
61
  this._benchmarkCount[name] += 1;
60
- this._benchmarkData[name] += performance.now() - this._benchmarkStart[name];
62
+ this._benchmarkData[name] += duration;
63
+ if (category) {
64
+ const category_name = name + "." + category;
65
+ if (this._benchmarkCount[category_name] == undefined) {
66
+ this._benchmarkCount[category_name] = 0;
67
+ this._benchmarkData[category_name] = 0;
68
+ }
69
+ this._benchmarkCount[category_name] += 1;
70
+ this._benchmarkData[category_name] += duration;
71
+ }
61
72
  result = this._benchmarkData[name] / this._benchmarkCount[name];
62
73
  }
63
74
  return result;
64
75
  }
76
+ /**
77
+ * Get given benchmark data.
78
+ *
79
+ * @param name of the benchmark
80
+ */
81
+ static get(name) {
82
+ if (this._benchmarkEnabled[name]) {
83
+ return this._benchmarkData[name] / this._benchmarkCount[name];
84
+ }
85
+ return -1;
86
+ }
87
+ /**
88
+ * Get all benchmark data.
89
+ *
90
+ */
91
+ static getAll() {
92
+ let result = {};
93
+ for (const name in this._benchmarkData) {
94
+ if (this._benchmarkCount[name] > 0) {
95
+ result[name] = this._benchmarkData[name] / this._benchmarkCount[name];
96
+ }
97
+ }
98
+ return result;
99
+ }
65
100
  }
66
101
  exports.OINOBenchmark = OINOBenchmark;
@@ -0,0 +1,120 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.OINOHtmlTemplate = void 0;
4
+ const _1 = require(".");
5
+ /**
6
+ * Class for rendering HTML from data.
7
+ */
8
+ class OINOHtmlTemplate {
9
+ /** HTML template string */
10
+ template;
11
+ /** Cache modified value for template */
12
+ modified;
13
+ /** Cache expiration value for template */
14
+ expires;
15
+ /**
16
+ * Creates HTML Response from a key-value-pair.
17
+ *
18
+ * @param template template string
19
+ *
20
+ */
21
+ constructor(template) {
22
+ this.template = template;
23
+ this.modified = 0;
24
+ this.expires = 0;
25
+ }
26
+ /**
27
+ * @returns whether template is empty
28
+ */
29
+ isEmpty() {
30
+ return this.template == "";
31
+ }
32
+ /**
33
+ * Creates HTML Response from a key-value-pair.
34
+ *
35
+ * @param key key
36
+ * @param value value
37
+ *
38
+ */
39
+ renderFromKeyValue(key, value) {
40
+ const html = this.template.replaceAll('###' + key + '###', _1.OINOStr.encode(value, _1.OINOContentType.html));
41
+ const result = new _1.OINOHttpResult(html);
42
+ if (this.expires >= 1) {
43
+ result.expires = Math.round(this.expires);
44
+ }
45
+ if (this.modified >= 1) {
46
+ result.lastModified = this.modified;
47
+ }
48
+ return result;
49
+ }
50
+ /**
51
+ * Creates HTML Response from object properties.
52
+ *
53
+ * @param object object
54
+ *
55
+ */
56
+ renderFromObject(object) {
57
+ let html = this.template;
58
+ if (object) {
59
+ for (let key in object) {
60
+ const value = object[key];
61
+ if (value) {
62
+ html = html.replaceAll('###' + key + '###', _1.OINOStr.encode(value.toString(), _1.OINOContentType.html));
63
+ }
64
+ }
65
+ }
66
+ html = html.replace(/###[^#]*###/g, "");
67
+ const result = new _1.OINOHttpResult(html);
68
+ if (this.expires >= 1) {
69
+ result.expires = Math.round(this.expires);
70
+ }
71
+ if (this.modified >= 1) {
72
+ result.lastModified = this.modified;
73
+ }
74
+ return result;
75
+ }
76
+ /**
77
+ * Creates HTML Response from API result.
78
+ *
79
+ * @param result OINOResult-object
80
+ * @param includeErrorMessages include debug messages in result
81
+ * @param includeWarningMessages include debug messages in result
82
+ * @param includeInfoMessages include debug messages in result
83
+ * @param includeDebugMessages include debug messages in result
84
+ *
85
+ */
86
+ renderFromResult(result, includeErrorMessages = false, includeWarningMessages = false, includeInfoMessages = false, includeDebugMessages = false) {
87
+ let html = this.template;
88
+ html = html.replaceAll('###statusCode###', _1.OINOStr.encode(result.statusCode.toString(), _1.OINOContentType.html));
89
+ html = html.replaceAll('###statusMessage###', _1.OINOStr.encode(result.statusMessage.toString(), _1.OINOContentType.html));
90
+ let messages = "";
91
+ for (let i = 0; i < result.messages.length; i++) {
92
+ if (includeErrorMessages && result.messages[i].startsWith(_1.OINO_ERROR_PREFIX)) {
93
+ messages += "<li>" + _1.OINOStr.encode(result.messages[i], _1.OINOContentType.html) + "</li>";
94
+ }
95
+ if (includeWarningMessages && result.messages[i].startsWith(_1.OINO_WARNING_PREFIX)) {
96
+ messages += "<li>" + _1.OINOStr.encode(result.messages[i], _1.OINOContentType.html) + "</li>";
97
+ }
98
+ if (includeInfoMessages && result.messages[i].startsWith(_1.OINO_INFO_PREFIX)) {
99
+ messages += "<li>" + _1.OINOStr.encode(result.messages[i], _1.OINOContentType.html) + "</li>";
100
+ }
101
+ if (includeDebugMessages && result.messages[i].startsWith(_1.OINO_DEBUG_PREFIX)) {
102
+ messages += "<li>" + _1.OINOStr.encode(result.messages[i], _1.OINOContentType.html) + "</li>";
103
+ }
104
+ }
105
+ if (messages) {
106
+ html = html.replaceAll('###messages###', "<ul>" + messages + "</ul>");
107
+ }
108
+ html = html.replace(/###[^#]*###/g, "");
109
+ const http_result = new _1.OINOHttpResult(html);
110
+ if (this.expires >= 1) {
111
+ http_result.expires = Math.round(this.expires);
112
+ }
113
+ if (this.modified >= 1) {
114
+ http_result.lastModified = this.modified;
115
+ }
116
+ return http_result;
117
+ }
118
+ }
119
+ exports.OINOHtmlTemplate = OINOHtmlTemplate;
120
+ ;
@@ -5,7 +5,8 @@
5
5
  * file, You can obtain one at https://mozilla.org/MPL/2.0/.
6
6
  */
7
7
  Object.defineProperty(exports, "__esModule", { value: true });
8
- exports.OINOResult = void 0;
8
+ exports.OINOHttpResult = exports.OINOResult = void 0;
9
+ const node_crypto_1 = require("node:crypto");
9
10
  const _1 = require(".");
10
11
  /**
11
12
  * OINO API request result object with returned data and/or http status code/message and
@@ -31,6 +32,17 @@ class OINOResult {
31
32
  this.statusMessage = "OK";
32
33
  this.messages = [];
33
34
  }
35
+ /**
36
+ * Copy values from different result.
37
+ *
38
+ * @param result source value
39
+ */
40
+ copy(result) {
41
+ this.success = result.success;
42
+ this.statusCode = result.statusCode;
43
+ this.statusMessage = result.statusMessage;
44
+ this.messages = result.messages.slice();
45
+ }
34
46
  /**
35
47
  * Set HTTP OK status (does not reset messages).
36
48
  *
@@ -41,7 +53,7 @@ class OINOResult {
41
53
  this.statusMessage = "OK";
42
54
  }
43
55
  /**
44
- * Set HTTP error status using given code and message.
56
+ * Set HTTP error status using given code and message. Returns self reference for chaining.
45
57
  *
46
58
  * @param statusCode HTTP status code
47
59
  * @param statusMessage HTTP status message
@@ -60,9 +72,10 @@ class OINOResult {
60
72
  else {
61
73
  this.statusMessage = _1.OINO_ERROR_PREFIX + " (" + operation + "): " + statusMessage;
62
74
  }
75
+ return this;
63
76
  }
64
77
  /**
65
- * Add warning message.
78
+ * Add warning message. Returns self reference for chaining.
66
79
  *
67
80
  * @param message HTTP status message
68
81
  * @param operation operation where warning occured
@@ -73,9 +86,10 @@ class OINOResult {
73
86
  if (message) {
74
87
  this.messages.push(_1.OINO_WARNING_PREFIX + " (" + operation + "): " + message);
75
88
  }
89
+ return this;
76
90
  }
77
91
  /**
78
- * Add info message.
92
+ * Add info message. Returns self reference for chaining.
79
93
  *
80
94
  * @param message HTTP status message
81
95
  * @param operation operation where info occured
@@ -86,9 +100,10 @@ class OINOResult {
86
100
  if (message) {
87
101
  this.messages.push(_1.OINO_INFO_PREFIX + " (" + operation + "): " + message);
88
102
  }
103
+ return this;
89
104
  }
90
105
  /**
91
- * Add debug message.
106
+ * Add debug message. Returns self reference for chaining.
92
107
  *
93
108
  * @param message HTTP status message
94
109
  * @param operation operation where debug occured
@@ -99,6 +114,7 @@ class OINOResult {
99
114
  if (message) {
100
115
  this.messages.push(_1.OINO_DEBUG_PREFIX + " (" + operation + "): " + message);
101
116
  }
117
+ return this;
102
118
  }
103
119
  /**
104
120
  * Copy given messages to HTTP headers.
@@ -134,3 +150,60 @@ class OINOResult {
134
150
  }
135
151
  }
136
152
  exports.OINOResult = OINOResult;
153
+ /**
154
+ * Specialized result for HTTP responses.
155
+ */
156
+ class OINOHttpResult extends OINOResult {
157
+ _etag;
158
+ /** HTTP body data */
159
+ body;
160
+ /** HTTP cache expiration value */
161
+ expires;
162
+ /** HTTP cache last-modified value */
163
+ lastModified;
164
+ /**
165
+ * Constructor for a `OINOHttpResult`
166
+ *
167
+ * @param body HTTP body
168
+ *
169
+ */
170
+ constructor(body) {
171
+ super();
172
+ this.body = body;
173
+ this.expires = 0;
174
+ this.lastModified = 0;
175
+ this._etag = "";
176
+ }
177
+ /**
178
+ * Get the ETag value for the body opportunistically, i.e. don't calculate until requested and reuse value.
179
+ *
180
+ */
181
+ getEtag() {
182
+ if (this._etag == "") {
183
+ const hash = (0, node_crypto_1.createHash)("sha256");
184
+ this._etag = hash.update(this.body).digest("hex");
185
+ }
186
+ return this._etag;
187
+ }
188
+ /**
189
+ * Get a Response object from the result values.
190
+ *
191
+ * @param headers HTTP headers (overrides existing values)
192
+ */
193
+ getResponse(headers) {
194
+ const result = new Response(this.body, { status: this.statusCode, statusText: this.statusMessage, headers: headers });
195
+ result.headers.set('Content-Length', this.body.length.toString());
196
+ if (this.lastModified > 0) {
197
+ result.headers.set('Last-Modified', new Date(this.lastModified).toUTCString());
198
+ }
199
+ if (this.expires >= 0) {
200
+ result.headers.set('Expires', Math.round(this.expires).toString());
201
+ if (this.expires == 0) {
202
+ result.headers.set('Pragma', 'no-cache');
203
+ }
204
+ }
205
+ result.headers.set("ETag", this.getEtag());
206
+ return result;
207
+ }
208
+ }
209
+ exports.OINOHttpResult = OINOHttpResult;
package/dist/cjs/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.OINOContentType = exports.OINO_DEBUG_PREFIX = exports.OINO_INFO_PREFIX = exports.OINO_WARNING_PREFIX = exports.OINO_ERROR_PREFIX = exports.OINOStr = exports.OINOResult = exports.OINOConsoleLog = exports.OINOLogLevel = exports.OINOLog = exports.OINOBenchmark = void 0;
3
+ exports.OINOContentType = exports.OINO_DEBUG_PREFIX = exports.OINO_INFO_PREFIX = exports.OINO_WARNING_PREFIX = exports.OINO_ERROR_PREFIX = exports.OINOHtmlTemplate = exports.OINOStr = exports.OINOHttpResult = exports.OINOResult = exports.OINOConsoleLog = exports.OINOLogLevel = exports.OINOLog = exports.OINOBenchmark = void 0;
4
4
  var OINOBenchmark_js_1 = require("./OINOBenchmark.js");
5
5
  Object.defineProperty(exports, "OINOBenchmark", { enumerable: true, get: function () { return OINOBenchmark_js_1.OINOBenchmark; } });
6
6
  var OINOLog_js_1 = require("./OINOLog.js");
@@ -9,8 +9,11 @@ Object.defineProperty(exports, "OINOLogLevel", { enumerable: true, get: function
9
9
  Object.defineProperty(exports, "OINOConsoleLog", { enumerable: true, get: function () { return OINOLog_js_1.OINOConsoleLog; } });
10
10
  var OINOResult_js_1 = require("./OINOResult.js");
11
11
  Object.defineProperty(exports, "OINOResult", { enumerable: true, get: function () { return OINOResult_js_1.OINOResult; } });
12
+ Object.defineProperty(exports, "OINOHttpResult", { enumerable: true, get: function () { return OINOResult_js_1.OINOHttpResult; } });
12
13
  var OINOStr_js_1 = require("./OINOStr.js");
13
14
  Object.defineProperty(exports, "OINOStr", { enumerable: true, get: function () { return OINOStr_js_1.OINOStr; } });
15
+ var OINOHtmlTemplate_js_1 = require("./OINOHtmlTemplate.js");
16
+ Object.defineProperty(exports, "OINOHtmlTemplate", { enumerable: true, get: function () { return OINOHtmlTemplate_js_1.OINOHtmlTemplate; } });
14
17
  /** OINO error message prefix */
15
18
  exports.OINO_ERROR_PREFIX = "OINO ERROR";
16
19
  /** OINO warning message prefix */
@@ -49,14 +49,49 @@ export class OINOBenchmark {
49
49
  * Complete benchmark timing
50
50
  *
51
51
  * @param name of the benchmark
52
+ * @param category optional subcategory of the benchmark
52
53
  */
53
- static end(name) {
54
+ static end(name, category) {
54
55
  let result = 0;
55
56
  if (this._benchmarkEnabled[name]) {
57
+ const duration = performance.now() - this._benchmarkStart[name];
56
58
  this._benchmarkCount[name] += 1;
57
- this._benchmarkData[name] += performance.now() - this._benchmarkStart[name];
59
+ this._benchmarkData[name] += duration;
60
+ if (category) {
61
+ const category_name = name + "." + category;
62
+ if (this._benchmarkCount[category_name] == undefined) {
63
+ this._benchmarkCount[category_name] = 0;
64
+ this._benchmarkData[category_name] = 0;
65
+ }
66
+ this._benchmarkCount[category_name] += 1;
67
+ this._benchmarkData[category_name] += duration;
68
+ }
58
69
  result = this._benchmarkData[name] / this._benchmarkCount[name];
59
70
  }
60
71
  return result;
61
72
  }
73
+ /**
74
+ * Get given benchmark data.
75
+ *
76
+ * @param name of the benchmark
77
+ */
78
+ static get(name) {
79
+ if (this._benchmarkEnabled[name]) {
80
+ return this._benchmarkData[name] / this._benchmarkCount[name];
81
+ }
82
+ return -1;
83
+ }
84
+ /**
85
+ * Get all benchmark data.
86
+ *
87
+ */
88
+ static getAll() {
89
+ let result = {};
90
+ for (const name in this._benchmarkData) {
91
+ if (this._benchmarkCount[name] > 0) {
92
+ result[name] = this._benchmarkData[name] / this._benchmarkCount[name];
93
+ }
94
+ }
95
+ return result;
96
+ }
62
97
  }
@@ -0,0 +1,116 @@
1
+ import { OINOStr, OINOContentType, OINOHttpResult, OINO_ERROR_PREFIX, OINO_WARNING_PREFIX, OINO_INFO_PREFIX, OINO_DEBUG_PREFIX } from ".";
2
+ /**
3
+ * Class for rendering HTML from data.
4
+ */
5
+ export class OINOHtmlTemplate {
6
+ /** HTML template string */
7
+ template;
8
+ /** Cache modified value for template */
9
+ modified;
10
+ /** Cache expiration value for template */
11
+ expires;
12
+ /**
13
+ * Creates HTML Response from a key-value-pair.
14
+ *
15
+ * @param template template string
16
+ *
17
+ */
18
+ constructor(template) {
19
+ this.template = template;
20
+ this.modified = 0;
21
+ this.expires = 0;
22
+ }
23
+ /**
24
+ * @returns whether template is empty
25
+ */
26
+ isEmpty() {
27
+ return this.template == "";
28
+ }
29
+ /**
30
+ * Creates HTML Response from a key-value-pair.
31
+ *
32
+ * @param key key
33
+ * @param value value
34
+ *
35
+ */
36
+ renderFromKeyValue(key, value) {
37
+ const html = this.template.replaceAll('###' + key + '###', OINOStr.encode(value, OINOContentType.html));
38
+ const result = new OINOHttpResult(html);
39
+ if (this.expires >= 1) {
40
+ result.expires = Math.round(this.expires);
41
+ }
42
+ if (this.modified >= 1) {
43
+ result.lastModified = this.modified;
44
+ }
45
+ return result;
46
+ }
47
+ /**
48
+ * Creates HTML Response from object properties.
49
+ *
50
+ * @param object object
51
+ *
52
+ */
53
+ renderFromObject(object) {
54
+ let html = this.template;
55
+ if (object) {
56
+ for (let key in object) {
57
+ const value = object[key];
58
+ if (value) {
59
+ html = html.replaceAll('###' + key + '###', OINOStr.encode(value.toString(), OINOContentType.html));
60
+ }
61
+ }
62
+ }
63
+ html = html.replace(/###[^#]*###/g, "");
64
+ const result = new OINOHttpResult(html);
65
+ if (this.expires >= 1) {
66
+ result.expires = Math.round(this.expires);
67
+ }
68
+ if (this.modified >= 1) {
69
+ result.lastModified = this.modified;
70
+ }
71
+ return result;
72
+ }
73
+ /**
74
+ * Creates HTML Response from API result.
75
+ *
76
+ * @param result OINOResult-object
77
+ * @param includeErrorMessages include debug messages in result
78
+ * @param includeWarningMessages include debug messages in result
79
+ * @param includeInfoMessages include debug messages in result
80
+ * @param includeDebugMessages include debug messages in result
81
+ *
82
+ */
83
+ renderFromResult(result, includeErrorMessages = false, includeWarningMessages = false, includeInfoMessages = false, includeDebugMessages = false) {
84
+ let html = this.template;
85
+ html = html.replaceAll('###statusCode###', OINOStr.encode(result.statusCode.toString(), OINOContentType.html));
86
+ html = html.replaceAll('###statusMessage###', OINOStr.encode(result.statusMessage.toString(), OINOContentType.html));
87
+ let messages = "";
88
+ for (let i = 0; i < result.messages.length; i++) {
89
+ if (includeErrorMessages && result.messages[i].startsWith(OINO_ERROR_PREFIX)) {
90
+ messages += "<li>" + OINOStr.encode(result.messages[i], OINOContentType.html) + "</li>";
91
+ }
92
+ if (includeWarningMessages && result.messages[i].startsWith(OINO_WARNING_PREFIX)) {
93
+ messages += "<li>" + OINOStr.encode(result.messages[i], OINOContentType.html) + "</li>";
94
+ }
95
+ if (includeInfoMessages && result.messages[i].startsWith(OINO_INFO_PREFIX)) {
96
+ messages += "<li>" + OINOStr.encode(result.messages[i], OINOContentType.html) + "</li>";
97
+ }
98
+ if (includeDebugMessages && result.messages[i].startsWith(OINO_DEBUG_PREFIX)) {
99
+ messages += "<li>" + OINOStr.encode(result.messages[i], OINOContentType.html) + "</li>";
100
+ }
101
+ }
102
+ if (messages) {
103
+ html = html.replaceAll('###messages###', "<ul>" + messages + "</ul>");
104
+ }
105
+ html = html.replace(/###[^#]*###/g, "");
106
+ const http_result = new OINOHttpResult(html);
107
+ if (this.expires >= 1) {
108
+ http_result.expires = Math.round(this.expires);
109
+ }
110
+ if (this.modified >= 1) {
111
+ http_result.lastModified = this.modified;
112
+ }
113
+ return http_result;
114
+ }
115
+ }
116
+ ;
@@ -3,6 +3,7 @@
3
3
  * License, v. 2.0. If a copy of the MPL was not distributed with this
4
4
  * file, You can obtain one at https://mozilla.org/MPL/2.0/.
5
5
  */
6
+ import { createHash } from "node:crypto";
6
7
  import { OINO_DEBUG_PREFIX, OINO_ERROR_PREFIX, OINO_INFO_PREFIX, OINO_WARNING_PREFIX } from ".";
7
8
  /**
8
9
  * OINO API request result object with returned data and/or http status code/message and
@@ -28,6 +29,17 @@ export class OINOResult {
28
29
  this.statusMessage = "OK";
29
30
  this.messages = [];
30
31
  }
32
+ /**
33
+ * Copy values from different result.
34
+ *
35
+ * @param result source value
36
+ */
37
+ copy(result) {
38
+ this.success = result.success;
39
+ this.statusCode = result.statusCode;
40
+ this.statusMessage = result.statusMessage;
41
+ this.messages = result.messages.slice();
42
+ }
31
43
  /**
32
44
  * Set HTTP OK status (does not reset messages).
33
45
  *
@@ -38,7 +50,7 @@ export class OINOResult {
38
50
  this.statusMessage = "OK";
39
51
  }
40
52
  /**
41
- * Set HTTP error status using given code and message.
53
+ * Set HTTP error status using given code and message. Returns self reference for chaining.
42
54
  *
43
55
  * @param statusCode HTTP status code
44
56
  * @param statusMessage HTTP status message
@@ -57,9 +69,10 @@ export class OINOResult {
57
69
  else {
58
70
  this.statusMessage = OINO_ERROR_PREFIX + " (" + operation + "): " + statusMessage;
59
71
  }
72
+ return this;
60
73
  }
61
74
  /**
62
- * Add warning message.
75
+ * Add warning message. Returns self reference for chaining.
63
76
  *
64
77
  * @param message HTTP status message
65
78
  * @param operation operation where warning occured
@@ -70,9 +83,10 @@ export class OINOResult {
70
83
  if (message) {
71
84
  this.messages.push(OINO_WARNING_PREFIX + " (" + operation + "): " + message);
72
85
  }
86
+ return this;
73
87
  }
74
88
  /**
75
- * Add info message.
89
+ * Add info message. Returns self reference for chaining.
76
90
  *
77
91
  * @param message HTTP status message
78
92
  * @param operation operation where info occured
@@ -83,9 +97,10 @@ export class OINOResult {
83
97
  if (message) {
84
98
  this.messages.push(OINO_INFO_PREFIX + " (" + operation + "): " + message);
85
99
  }
100
+ return this;
86
101
  }
87
102
  /**
88
- * Add debug message.
103
+ * Add debug message. Returns self reference for chaining.
89
104
  *
90
105
  * @param message HTTP status message
91
106
  * @param operation operation where debug occured
@@ -96,6 +111,7 @@ export class OINOResult {
96
111
  if (message) {
97
112
  this.messages.push(OINO_DEBUG_PREFIX + " (" + operation + "): " + message);
98
113
  }
114
+ return this;
99
115
  }
100
116
  /**
101
117
  * Copy given messages to HTTP headers.
@@ -130,3 +146,59 @@ export class OINOResult {
130
146
  }
131
147
  }
132
148
  }
149
+ /**
150
+ * Specialized result for HTTP responses.
151
+ */
152
+ export class OINOHttpResult extends OINOResult {
153
+ _etag;
154
+ /** HTTP body data */
155
+ body;
156
+ /** HTTP cache expiration value */
157
+ expires;
158
+ /** HTTP cache last-modified value */
159
+ lastModified;
160
+ /**
161
+ * Constructor for a `OINOHttpResult`
162
+ *
163
+ * @param body HTTP body
164
+ *
165
+ */
166
+ constructor(body) {
167
+ super();
168
+ this.body = body;
169
+ this.expires = 0;
170
+ this.lastModified = 0;
171
+ this._etag = "";
172
+ }
173
+ /**
174
+ * Get the ETag value for the body opportunistically, i.e. don't calculate until requested and reuse value.
175
+ *
176
+ */
177
+ getEtag() {
178
+ if (this._etag == "") {
179
+ const hash = createHash("sha256");
180
+ this._etag = hash.update(this.body).digest("hex");
181
+ }
182
+ return this._etag;
183
+ }
184
+ /**
185
+ * Get a Response object from the result values.
186
+ *
187
+ * @param headers HTTP headers (overrides existing values)
188
+ */
189
+ getResponse(headers) {
190
+ const result = new Response(this.body, { status: this.statusCode, statusText: this.statusMessage, headers: headers });
191
+ result.headers.set('Content-Length', this.body.length.toString());
192
+ if (this.lastModified > 0) {
193
+ result.headers.set('Last-Modified', new Date(this.lastModified).toUTCString());
194
+ }
195
+ if (this.expires >= 0) {
196
+ result.headers.set('Expires', Math.round(this.expires).toString());
197
+ if (this.expires == 0) {
198
+ result.headers.set('Pragma', 'no-cache');
199
+ }
200
+ }
201
+ result.headers.set("ETag", this.getEtag());
202
+ return result;
203
+ }
204
+ }
package/dist/esm/index.js CHANGED
@@ -1,7 +1,8 @@
1
1
  export { OINOBenchmark } from "./OINOBenchmark.js";
2
2
  export { OINOLog, OINOLogLevel, OINOConsoleLog } from "./OINOLog.js";
3
- export { OINOResult } from "./OINOResult.js";
3
+ export { OINOResult, OINOHttpResult } from "./OINOResult.js";
4
4
  export { OINOStr } from "./OINOStr.js";
5
+ export { OINOHtmlTemplate } from "./OINOHtmlTemplate.js";
5
6
  /** OINO error message prefix */
6
7
  export const OINO_ERROR_PREFIX = "OINO ERROR";
7
8
  /** OINO warning message prefix */
@@ -28,6 +28,18 @@ export declare class OINOBenchmark {
28
28
  * Complete benchmark timing
29
29
  *
30
30
  * @param name of the benchmark
31
+ * @param category optional subcategory of the benchmark
31
32
  */
32
- static end(name: string): number;
33
+ static end(name: string, category?: string): number;
34
+ /**
35
+ * Get given benchmark data.
36
+ *
37
+ * @param name of the benchmark
38
+ */
39
+ static get(name: string): number;
40
+ /**
41
+ * Get all benchmark data.
42
+ *
43
+ */
44
+ static getAll(): number;
33
45
  }
@@ -0,0 +1,49 @@
1
+ import { OINOResult, OINOHttpResult } from ".";
2
+ /**
3
+ * Class for rendering HTML from data.
4
+ */
5
+ export declare class OINOHtmlTemplate {
6
+ /** HTML template string */
7
+ template: string;
8
+ /** Cache modified value for template */
9
+ modified: number;
10
+ /** Cache expiration value for template */
11
+ expires: number;
12
+ /**
13
+ * Creates HTML Response from a key-value-pair.
14
+ *
15
+ * @param template template string
16
+ *
17
+ */
18
+ constructor(template: string);
19
+ /**
20
+ * @returns whether template is empty
21
+ */
22
+ isEmpty(): boolean;
23
+ /**
24
+ * Creates HTML Response from a key-value-pair.
25
+ *
26
+ * @param key key
27
+ * @param value value
28
+ *
29
+ */
30
+ renderFromKeyValue(key: string, value: string): OINOHttpResult;
31
+ /**
32
+ * Creates HTML Response from object properties.
33
+ *
34
+ * @param object object
35
+ *
36
+ */
37
+ renderFromObject(object: any): OINOHttpResult;
38
+ /**
39
+ * Creates HTML Response from API result.
40
+ *
41
+ * @param result OINOResult-object
42
+ * @param includeErrorMessages include debug messages in result
43
+ * @param includeWarningMessages include debug messages in result
44
+ * @param includeInfoMessages include debug messages in result
45
+ * @param includeDebugMessages include debug messages in result
46
+ *
47
+ */
48
+ renderFromResult(result: OINOResult, includeErrorMessages?: boolean, includeWarningMessages?: boolean, includeInfoMessages?: boolean, includeDebugMessages?: boolean): OINOHttpResult;
49
+ }
@@ -17,44 +17,50 @@ export declare class OINOResult {
17
17
  *
18
18
  */
19
19
  constructor();
20
+ /**
21
+ * Copy values from different result.
22
+ *
23
+ * @param result source value
24
+ */
25
+ copy(result: OINOResult): void;
20
26
  /**
21
27
  * Set HTTP OK status (does not reset messages).
22
28
  *
23
29
  */
24
30
  setOk(): void;
25
31
  /**
26
- * Set HTTP error status using given code and message.
32
+ * Set HTTP error status using given code and message. Returns self reference for chaining.
27
33
  *
28
34
  * @param statusCode HTTP status code
29
35
  * @param statusMessage HTTP status message
30
36
  * @param operation operation where error occured
31
37
  *
32
38
  */
33
- setError(statusCode: number, statusMessage: string, operation: string): void;
39
+ setError(statusCode: number, statusMessage: string, operation: string): OINOResult;
34
40
  /**
35
- * Add warning message.
41
+ * Add warning message. Returns self reference for chaining.
36
42
  *
37
43
  * @param message HTTP status message
38
44
  * @param operation operation where warning occured
39
45
  *
40
46
  */
41
- addWarning(message: string, operation: string): void;
47
+ addWarning(message: string, operation: string): OINOResult;
42
48
  /**
43
- * Add info message.
49
+ * Add info message. Returns self reference for chaining.
44
50
  *
45
51
  * @param message HTTP status message
46
52
  * @param operation operation where info occured
47
53
  *
48
54
  */
49
- addInfo(message: string, operation: string): void;
55
+ addInfo(message: string, operation: string): OINOResult;
50
56
  /**
51
- * Add debug message.
57
+ * Add debug message. Returns self reference for chaining.
52
58
  *
53
59
  * @param message HTTP status message
54
60
  * @param operation operation where debug occured
55
61
  *
56
62
  */
57
- addDebug(message: string, operation: string): void;
63
+ addDebug(message: string, operation: string): OINOResult;
58
64
  /**
59
65
  * Copy given messages to HTTP headers.
60
66
  *
@@ -67,3 +73,33 @@ export declare class OINOResult {
67
73
  */
68
74
  copyMessagesToHeaders(headers: Headers, copyErrors?: boolean, copyWarnings?: boolean, copyInfos?: boolean, copyDebug?: boolean): void;
69
75
  }
76
+ /**
77
+ * Specialized result for HTTP responses.
78
+ */
79
+ export declare class OINOHttpResult extends OINOResult {
80
+ private _etag;
81
+ /** HTTP body data */
82
+ readonly body: string;
83
+ /** HTTP cache expiration value */
84
+ expires: number;
85
+ /** HTTP cache last-modified value */
86
+ lastModified: number;
87
+ /**
88
+ * Constructor for a `OINOHttpResult`
89
+ *
90
+ * @param body HTTP body
91
+ *
92
+ */
93
+ constructor(body: string);
94
+ /**
95
+ * Get the ETag value for the body opportunistically, i.e. don't calculate until requested and reuse value.
96
+ *
97
+ */
98
+ getEtag(): string;
99
+ /**
100
+ * Get a Response object from the result values.
101
+ *
102
+ * @param headers HTTP headers (overrides existing values)
103
+ */
104
+ getResponse(headers?: Record<string, string>): Response;
105
+ }
@@ -1,7 +1,8 @@
1
1
  export { OINOBenchmark } from "./OINOBenchmark.js";
2
2
  export { OINOLog, OINOLogLevel, OINOConsoleLog } from "./OINOLog.js";
3
- export { OINOResult } from "./OINOResult.js";
3
+ export { OINOResult, OINOHttpResult } from "./OINOResult.js";
4
4
  export { OINOStr } from "./OINOStr.js";
5
+ export { OINOHtmlTemplate } from "./OINOHtmlTemplate.js";
5
6
  /** OINO error message prefix */
6
7
  export declare const OINO_ERROR_PREFIX = "OINO ERROR";
7
8
  /** OINO warning message prefix */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oino-ts/types",
3
- "version": "0.0.11",
3
+ "version": "0.0.13",
4
4
  "description": "OINO TS package for types.",
5
5
  "author": "Matias Kiviniemi (pragmatta)",
6
6
  "license": "MPL-2.0",
@@ -55,14 +55,51 @@ export class OINOBenchmark {
55
55
  * Complete benchmark timing
56
56
  *
57
57
  * @param name of the benchmark
58
+ * @param category optional subcategory of the benchmark
58
59
  */
59
- static end(name:string):number {
60
+ static end(name:string, category?:string):number {
60
61
  let result:number = 0
61
62
  if (this._benchmarkEnabled[name]) {
63
+ const duration = performance.now() - this._benchmarkStart[name]
62
64
  this._benchmarkCount[name] += 1
63
- this._benchmarkData[name] += performance.now() - this._benchmarkStart[name]
65
+ this._benchmarkData[name] += duration
66
+ if (category) {
67
+ const category_name = name + "." + category
68
+ if (this._benchmarkCount[category_name] == undefined) {
69
+ this._benchmarkCount[category_name] = 0
70
+ this._benchmarkData[category_name] = 0
71
+ }
72
+ this._benchmarkCount[category_name] += 1
73
+ this._benchmarkData[category_name] += duration
74
+ }
64
75
  result = this._benchmarkData[name] / this._benchmarkCount[name]
65
76
  }
66
77
  return result
67
78
  }
79
+
80
+ /**
81
+ * Get given benchmark data.
82
+ *
83
+ * @param name of the benchmark
84
+ */
85
+ static get(name:string):number {
86
+ if (this._benchmarkEnabled[name]) {
87
+ return this._benchmarkData[name] / this._benchmarkCount[name]
88
+ }
89
+ return -1
90
+ }
91
+
92
+ /**
93
+ * Get all benchmark data.
94
+ *
95
+ */
96
+ static getAll():number {
97
+ let result:any = {}
98
+ for (const name in this._benchmarkData) {
99
+ if (this._benchmarkCount[name] > 0) {
100
+ result[name] = this._benchmarkData[name] / this._benchmarkCount[name]
101
+ }
102
+ }
103
+ return result
104
+ }
68
105
  }
@@ -0,0 +1,124 @@
1
+ import { OINOStr, OINOContentType, OINOResult, OINOHttpResult, OINO_ERROR_PREFIX, OINO_WARNING_PREFIX, OINO_INFO_PREFIX, OINO_DEBUG_PREFIX } from "."
2
+
3
+ /**
4
+ * Class for rendering HTML from data.
5
+ */
6
+ export class OINOHtmlTemplate {
7
+ /** HTML template string */
8
+ template: string;
9
+
10
+ /** Cache modified value for template */
11
+ modified: number;
12
+
13
+ /** Cache expiration value for template */
14
+ expires: number;
15
+
16
+ /**
17
+ * Creates HTML Response from a key-value-pair.
18
+ *
19
+ * @param template template string
20
+ *
21
+ */
22
+ constructor (template:string) {
23
+ this.template = template
24
+ this.modified = 0
25
+ this.expires = 0
26
+ }
27
+
28
+ /**
29
+ * @returns whether template is empty
30
+ */
31
+ isEmpty():boolean {
32
+ return this.template == ""
33
+ }
34
+
35
+ /**
36
+ * Creates HTML Response from a key-value-pair.
37
+ *
38
+ * @param key key
39
+ * @param value value
40
+ *
41
+ */
42
+ renderFromKeyValue(key:string, value:string):OINOHttpResult {
43
+ const html:string = this.template.replaceAll('###' + key + '###', OINOStr.encode(value, OINOContentType.html))
44
+ const result:OINOHttpResult = new OINOHttpResult(html)
45
+ if (this.expires >= 1) {
46
+ result.expires = Math.round(this.expires)
47
+ }
48
+ if (this.modified >= 1) {
49
+ result.lastModified = this.modified
50
+ }
51
+ return result
52
+ }
53
+
54
+ /**
55
+ * Creates HTML Response from object properties.
56
+ *
57
+ * @param object object
58
+ *
59
+ */
60
+ renderFromObject(object:any):OINOHttpResult {
61
+ let html:string = this.template
62
+ if (object) {
63
+ for (let key in object) {
64
+ const value = object[key]
65
+ if (value) {
66
+ html = html.replaceAll('###' + key + '###', OINOStr.encode(value.toString(), OINOContentType.html))
67
+ }
68
+ }
69
+ }
70
+ html = html.replace(/###[^#]*###/g, "")
71
+ const result:OINOHttpResult = new OINOHttpResult(html)
72
+ if (this.expires >= 1) {
73
+ result.expires = Math.round(this.expires)
74
+ }
75
+ if (this.modified >= 1) {
76
+ result.lastModified = this.modified
77
+ }
78
+ return result
79
+ }
80
+
81
+ /**
82
+ * Creates HTML Response from API result.
83
+ *
84
+ * @param result OINOResult-object
85
+ * @param includeErrorMessages include debug messages in result
86
+ * @param includeWarningMessages include debug messages in result
87
+ * @param includeInfoMessages include debug messages in result
88
+ * @param includeDebugMessages include debug messages in result
89
+ *
90
+ */
91
+ renderFromResult(result:OINOResult, includeErrorMessages:boolean=false, includeWarningMessages:boolean=false, includeInfoMessages:boolean=false, includeDebugMessages:boolean=false):OINOHttpResult {
92
+ let html:string = this.template
93
+ html = html.replaceAll('###statusCode###', OINOStr.encode(result.statusCode.toString(), OINOContentType.html))
94
+ html = html.replaceAll('###statusMessage###', OINOStr.encode(result.statusMessage.toString(), OINOContentType.html))
95
+ let messages = ""
96
+ for (let i:number = 0; i<result.messages.length; i++) {
97
+ if (includeErrorMessages && result.messages[i].startsWith(OINO_ERROR_PREFIX)) {
98
+ messages += "<li>" + OINOStr.encode(result.messages[i], OINOContentType.html) + "</li>"
99
+ }
100
+ if (includeWarningMessages && result.messages[i].startsWith(OINO_WARNING_PREFIX)) {
101
+ messages += "<li>" + OINOStr.encode(result.messages[i], OINOContentType.html) + "</li>"
102
+ }
103
+ if (includeInfoMessages && result.messages[i].startsWith(OINO_INFO_PREFIX)) {
104
+ messages += "<li>" + OINOStr.encode(result.messages[i], OINOContentType.html) + "</li>"
105
+ }
106
+ if (includeDebugMessages && result.messages[i].startsWith(OINO_DEBUG_PREFIX)) {
107
+ messages += "<li>" + OINOStr.encode(result.messages[i], OINOContentType.html) + "</li>"
108
+ }
109
+
110
+ }
111
+ if (messages) {
112
+ html = html.replaceAll('###messages###', "<ul>" + messages + "</ul>")
113
+ }
114
+ html = html.replace(/###[^#]*###/g, "")
115
+ const http_result:OINOHttpResult = new OINOHttpResult(html)
116
+ if (this.expires >= 1) {
117
+ http_result.expires = Math.round(this.expires)
118
+ }
119
+ if (this.modified >= 1) {
120
+ http_result.lastModified = this.modified
121
+ }
122
+ return http_result
123
+ }
124
+ };
package/src/OINOResult.ts CHANGED
@@ -4,6 +4,7 @@
4
4
  * file, You can obtain one at https://mozilla.org/MPL/2.0/.
5
5
  */
6
6
 
7
+ import { createHash, Hash } from "node:crypto";
7
8
  import { OINO_DEBUG_PREFIX, OINO_ERROR_PREFIX, OINO_INFO_PREFIX, OINO_WARNING_PREFIX } from ".";
8
9
 
9
10
  /**
@@ -35,6 +36,18 @@ export class OINOResult {
35
36
  this.messages = []
36
37
  }
37
38
 
39
+ /**
40
+ * Copy values from different result.
41
+ *
42
+ * @param result source value
43
+ */
44
+ copy(result: OINOResult) {
45
+ this.success = result.success
46
+ this.statusCode = result.statusCode
47
+ this.statusMessage = result.statusMessage
48
+ this.messages = result.messages.slice()
49
+ }
50
+
38
51
  /**
39
52
  * Set HTTP OK status (does not reset messages).
40
53
  *
@@ -46,14 +59,14 @@ export class OINOResult {
46
59
  }
47
60
 
48
61
  /**
49
- * Set HTTP error status using given code and message.
62
+ * Set HTTP error status using given code and message. Returns self reference for chaining.
50
63
  *
51
64
  * @param statusCode HTTP status code
52
65
  * @param statusMessage HTTP status message
53
66
  * @param operation operation where error occured
54
67
  *
55
68
  */
56
- setError(statusCode:number, statusMessage:string, operation:string) {
69
+ setError(statusCode:number, statusMessage:string, operation:string):OINOResult {
57
70
  this.success = false
58
71
  this.statusCode = statusCode
59
72
  if (this.statusMessage != "OK") {
@@ -64,48 +77,52 @@ export class OINOResult {
64
77
  } else {
65
78
  this.statusMessage = OINO_ERROR_PREFIX + " (" + operation + "): " + statusMessage
66
79
  }
80
+ return this
67
81
  }
68
82
 
69
83
  /**
70
- * Add warning message.
84
+ * Add warning message. Returns self reference for chaining.
71
85
  *
72
86
  * @param message HTTP status message
73
87
  * @param operation operation where warning occured
74
88
  *
75
89
  */
76
- addWarning(message:string, operation:string) {
90
+ addWarning(message:string, operation:string):OINOResult {
77
91
  message = message.trim()
78
92
  if (message) {
79
93
  this.messages.push(OINO_WARNING_PREFIX + " (" + operation + "): " + message)
80
94
  }
95
+ return this
81
96
  }
82
97
 
83
98
  /**
84
- * Add info message.
99
+ * Add info message. Returns self reference for chaining.
85
100
  *
86
101
  * @param message HTTP status message
87
102
  * @param operation operation where info occured
88
103
  *
89
104
  */
90
- addInfo(message:string, operation:string) {
105
+ addInfo(message:string, operation:string):OINOResult {
91
106
  message = message.trim()
92
107
  if (message) {
93
108
  this.messages.push(OINO_INFO_PREFIX + " (" + operation + "): " + message)
94
109
  }
110
+ return this
95
111
  }
96
112
 
97
113
  /**
98
- * Add debug message.
114
+ * Add debug message. Returns self reference for chaining.
99
115
  *
100
116
  * @param message HTTP status message
101
117
  * @param operation operation where debug occured
102
118
  *
103
119
  */
104
- addDebug(message:string, operation:string) {
120
+ addDebug(message:string, operation:string):OINOResult {
105
121
  message = message.trim()
106
122
  if (message) {
107
123
  this.messages.push(OINO_DEBUG_PREFIX + " (" + operation + "): " + message)
108
124
  }
125
+ return this
109
126
  }
110
127
 
111
128
  /**
@@ -141,3 +158,69 @@ export class OINOResult {
141
158
  }
142
159
  }
143
160
  }
161
+
162
+ /**
163
+ * Specialized result for HTTP responses.
164
+ */
165
+ export class OINOHttpResult extends OINOResult {
166
+ private _etag:string
167
+
168
+ /** HTTP body data */
169
+ readonly body: string
170
+
171
+ /** HTTP cache expiration value */
172
+ expires: number
173
+
174
+ /** HTTP cache last-modified value */
175
+ lastModified: number
176
+
177
+ /**
178
+ * Constructor for a `OINOHttpResult`
179
+ *
180
+ * @param body HTTP body
181
+ *
182
+ */
183
+ constructor(body:string) {
184
+ super()
185
+ this.body = body
186
+ this.expires = 0
187
+ this.lastModified = 0
188
+ this._etag = ""
189
+ }
190
+
191
+ /**
192
+ * Get the ETag value for the body opportunistically, i.e. don't calculate until requested and reuse value.
193
+ *
194
+ */
195
+ getEtag():string {
196
+ if (this._etag == "") {
197
+ const hash:Hash = createHash("sha256")
198
+ this._etag = hash.update(this.body).digest("hex")
199
+ }
200
+ return this._etag
201
+ }
202
+
203
+ /**
204
+ * Get a Response object from the result values.
205
+ *
206
+ * @param headers HTTP headers (overrides existing values)
207
+ */
208
+ getResponse(headers?:Record<string, string>):Response {
209
+ const result:Response = new Response(this.body, {status:this.statusCode, statusText: this.statusMessage, headers: headers})
210
+ result.headers.set('Content-Length', this.body.length.toString())
211
+ if (this.lastModified > 0) {
212
+ result.headers.set('Last-Modified', new Date(this.lastModified).toUTCString())
213
+ }
214
+ if (this.expires >= 0) {
215
+ result.headers.set('Expires', Math.round(this.expires).toString())
216
+ if (this.expires == 0) {
217
+ result.headers.set('Pragma', 'no-cache')
218
+ }
219
+ }
220
+ result.headers.set("ETag", this.getEtag())
221
+ return result
222
+ }
223
+
224
+
225
+
226
+ }
package/src/index.ts CHANGED
@@ -1,7 +1,8 @@
1
1
  export { OINOBenchmark } from "./OINOBenchmark.js"
2
2
  export { OINOLog, OINOLogLevel, OINOConsoleLog } from "./OINOLog.js"
3
- export { OINOResult } from "./OINOResult.js"
3
+ export { OINOResult, OINOHttpResult } from "./OINOResult.js"
4
4
  export { OINOStr } from "./OINOStr.js"
5
+ export { OINOHtmlTemplate } from "./OINOHtmlTemplate.js"
5
6
 
6
7
  /** OINO error message prefix */
7
8
  export const OINO_ERROR_PREFIX = "OINO ERROR"