@trebor/buildhtml 1.0.2 → 1.0.4
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 +89 -29
- package/index.js +16 -57
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @trebor/buildhtml
|
|
2
2
|
|
|
3
|
-
**High-performance, server-side rendering (SSR) library for Node.js.**
|
|
4
|
-
*"Build HTML at lightning speed with reactive state management."*
|
|
3
|
+
**High-performance, server-side rendering (SSR) library for Node.js.** *"Build HTML at lightning speed with reactive state management."*
|
|
5
4
|
|
|
6
5
|
---
|
|
7
6
|
|
|
@@ -9,20 +8,20 @@
|
|
|
9
8
|
|
|
10
9
|
BuildHTML is a lightweight SSR library for Node.js featuring object pooling, reactive state management, and CSS-in-JS capabilities. Build HTML on the server with minimal memory usage and blazing-fast performance.
|
|
11
10
|
|
|
12
|
-
- **Zero dependencies** – Only Node.js required
|
|
13
|
-
- **High Performance** – Object pooling and LRU caching (1-5ms render time)
|
|
14
|
-
- **Reactive State** – Built-in state management with automatic UI updates
|
|
15
|
-
- **CSS-in-JS** – Scoped and global styling with automatic CSS generation
|
|
16
|
-
- **Security** – XSS protection, CSS sanitization, and CSP nonce support
|
|
17
|
-
- **Production Ready** – HTML minification, compression, and metrics
|
|
18
|
-
- **JSON Export** – Save/restore pages with optional obfuscation
|
|
11
|
+
- **Zero dependencies** – Only Node.js required.
|
|
12
|
+
- **High Performance** – Object pooling and LRU caching (1-5ms render time).
|
|
13
|
+
- **Reactive State** – Built-in state management with automatic UI updates.
|
|
14
|
+
- **CSS-in-JS** – Scoped and global styling with automatic CSS generation.
|
|
15
|
+
- **Security** – XSS protection, CSS sanitization, and CSP nonce support.
|
|
16
|
+
- **Production Ready** – HTML minification, compression, and metrics.
|
|
17
|
+
- **JSON Export** – Save/restore pages with optional obfuscation.
|
|
19
18
|
|
|
20
19
|
---
|
|
21
20
|
|
|
22
21
|
## Installation
|
|
23
22
|
|
|
24
23
|
```bash
|
|
25
|
-
npm install buildhtml
|
|
24
|
+
npm install @trebor/buildhtml
|
|
26
25
|
```
|
|
27
26
|
|
|
28
27
|
---
|
|
@@ -30,7 +29,7 @@ npm install buildhtml
|
|
|
30
29
|
## Quick Start
|
|
31
30
|
|
|
32
31
|
```javascript
|
|
33
|
-
const { Document } = require('buildhtml');
|
|
32
|
+
const { Document } = require('@trebor/buildhtml');
|
|
34
33
|
|
|
35
34
|
// Create a document
|
|
36
35
|
const doc = new Document();
|
|
@@ -257,7 +256,7 @@ const {
|
|
|
257
256
|
resetPools, // Reset object pools
|
|
258
257
|
healthCheck, // Health check data
|
|
259
258
|
metrics // Performance metrics
|
|
260
|
-
} = require('buildhtml');
|
|
259
|
+
} = require('@trebor/buildhtml');
|
|
261
260
|
```
|
|
262
261
|
|
|
263
262
|
---
|
|
@@ -268,7 +267,7 @@ const {
|
|
|
268
267
|
|
|
269
268
|
```javascript
|
|
270
269
|
const express = require('express');
|
|
271
|
-
const { Document } = require('buildhtml');
|
|
270
|
+
const { Document } = require('@trebor/buildhtml');
|
|
272
271
|
|
|
273
272
|
const app = express();
|
|
274
273
|
|
|
@@ -400,7 +399,7 @@ app.get('/spa', (req, res) => {
|
|
|
400
399
|
## Configuration
|
|
401
400
|
|
|
402
401
|
```javascript
|
|
403
|
-
const { CONFIG } = require('buildhtml');
|
|
402
|
+
const { CONFIG } = require('@trebor/buildhtml');
|
|
404
403
|
|
|
405
404
|
CONFIG.mode = 'prod'; // 'prod' or 'dev'
|
|
406
405
|
CONFIG.poolSize = 150; // Max pooled elements
|
|
@@ -436,14 +435,14 @@ clearCache('user-'); // Clear all keys containing 'user-'
|
|
|
436
435
|
### enableCompression()
|
|
437
436
|
|
|
438
437
|
```javascript
|
|
439
|
-
const { enableCompression } = require('buildhtml');
|
|
438
|
+
const { enableCompression } = require('@trebor/buildhtml');
|
|
440
439
|
app.use(enableCompression());
|
|
441
440
|
```
|
|
442
441
|
|
|
443
442
|
### warmupCache(routes)
|
|
444
443
|
|
|
445
444
|
```javascript
|
|
446
|
-
const { warmupCache } = require('buildhtml');
|
|
445
|
+
const { warmupCache } = require('@trebor/buildhtml');
|
|
447
446
|
|
|
448
447
|
await warmupCache([
|
|
449
448
|
{ key: 'home', builder: () => buildHomePage() },
|
|
@@ -479,7 +478,7 @@ const html = doc.render();
|
|
|
479
478
|
|
|
480
479
|
```javascript
|
|
481
480
|
// Render with JSON embedded in HTML
|
|
482
|
-
const html = doc.renderJSON({
|
|
481
|
+
const html = doc.renderJSON({ obfuscate: true });
|
|
483
482
|
|
|
484
483
|
// In browser:
|
|
485
484
|
console.log(window.__SCULPTOR_DATA__);
|
|
@@ -490,31 +489,92 @@ console.log(window.__SCULPTOR_DATA__);
|
|
|
490
489
|
|
|
491
490
|
## Security
|
|
492
491
|
|
|
493
|
-
###
|
|
494
|
-
|
|
495
|
-
All text is automatically escaped:
|
|
492
|
+
### Built-in Security Features
|
|
496
493
|
|
|
494
|
+
**Automatic XSS Protection**
|
|
497
495
|
```javascript
|
|
498
496
|
doc.create('div').text('<script>alert("XSS")</script>');
|
|
499
497
|
// Output: <script>alert("XSS")</script>
|
|
500
498
|
```
|
|
501
499
|
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
Dangerous CSS characters are sanitized:
|
|
505
|
-
|
|
500
|
+
**CSS Injection Prevention**
|
|
506
501
|
```javascript
|
|
507
502
|
el.css({ background: 'red; } body { display: none; }' });
|
|
508
503
|
// Sanitized automatically
|
|
509
504
|
```
|
|
510
505
|
|
|
511
|
-
|
|
512
|
-
|
|
506
|
+
**CSP Nonce Support**
|
|
513
507
|
```javascript
|
|
514
508
|
const doc = new Document({ nonce: 'abc123' });
|
|
515
509
|
// All inline scripts/styles get nonce attribute
|
|
516
510
|
```
|
|
517
511
|
|
|
512
|
+
### Enhanced Security Features (NEW!)
|
|
513
|
+
|
|
514
|
+
**Security Headers Middleware**
|
|
515
|
+
```javascript
|
|
516
|
+
const { securityHeaders } = require('@trebor/buildhtml');
|
|
517
|
+
|
|
518
|
+
app.use(securityHeaders({
|
|
519
|
+
enableCSP: true,
|
|
520
|
+
enableHSTS: true,
|
|
521
|
+
enableXFrameOptions: true,
|
|
522
|
+
cspNonce: (req) => req.nonce
|
|
523
|
+
}));
|
|
524
|
+
```
|
|
525
|
+
|
|
526
|
+
**Input Sanitization**
|
|
527
|
+
```javascript
|
|
528
|
+
const { sanitize } = require('@trebor/buildhtml');
|
|
529
|
+
|
|
530
|
+
// Text sanitization
|
|
531
|
+
const safe = sanitize.text('<script>bad</script>');
|
|
532
|
+
|
|
533
|
+
// URL sanitization (blocks javascript:, data:, vbscript:)
|
|
534
|
+
const url = sanitize.url('javascript:alert(1)'); // Returns ''
|
|
535
|
+
|
|
536
|
+
// Email validation
|
|
537
|
+
const email = sanitize.email('user@example.com');
|
|
538
|
+
|
|
539
|
+
// Filename sanitization (prevents path traversal)
|
|
540
|
+
const filename = sanitize.filename('../../../etc/passwd');
|
|
541
|
+
```
|
|
542
|
+
|
|
543
|
+
**CSRF Protection**
|
|
544
|
+
```javascript
|
|
545
|
+
const { csrf } = require('@trebor/buildhtml');
|
|
546
|
+
|
|
547
|
+
app.use(csrf.middleware());
|
|
548
|
+
|
|
549
|
+
app.post('/submit', (req, res) => {
|
|
550
|
+
// CSRF token automatically validated
|
|
551
|
+
// Request blocked if invalid
|
|
552
|
+
});
|
|
553
|
+
```
|
|
554
|
+
|
|
555
|
+
**Rate Limiting**
|
|
556
|
+
```javascript
|
|
557
|
+
const { createRateLimiter } = require('@trebor/buildhtml');
|
|
558
|
+
|
|
559
|
+
app.use('/api', createRateLimiter({
|
|
560
|
+
windowMs: 15 * 60 * 1000, // 15 minutes
|
|
561
|
+
maxRequests: 100
|
|
562
|
+
}));
|
|
563
|
+
```
|
|
564
|
+
|
|
565
|
+
**CSP Header Generation**
|
|
566
|
+
```javascript
|
|
567
|
+
const { generateCSP } = require('@trebor/buildhtml');
|
|
568
|
+
|
|
569
|
+
const csp = generateCSP({
|
|
570
|
+
scriptSrc: ["'self'", "'nonce-abc123'"],
|
|
571
|
+
styleSrc: ["'self'"],
|
|
572
|
+
imgSrc: ["'self'", 'data:', 'https:']
|
|
573
|
+
});
|
|
574
|
+
```
|
|
575
|
+
|
|
576
|
+
📖 **See [SECURITY.md](./SECURITY.md) for complete security documentation**
|
|
577
|
+
|
|
518
578
|
---
|
|
519
579
|
|
|
520
580
|
## Advanced Features
|
|
@@ -546,7 +606,7 @@ doc.oncreate(() => {
|
|
|
546
606
|
```javascript
|
|
547
607
|
process.env.ENABLE_METRICS = 'true';
|
|
548
608
|
|
|
549
|
-
const { metrics } = require('buildhtml');
|
|
609
|
+
const { metrics } = require('@trebor/buildhtml');
|
|
550
610
|
|
|
551
611
|
// After some requests...
|
|
552
612
|
console.log(metrics.getStats());
|
|
@@ -663,7 +723,7 @@ const html = doc.render();
|
|
|
663
723
|
|
|
664
724
|
```javascript
|
|
665
725
|
const express = require('express');
|
|
666
|
-
const { Document, createCachedRenderer } = require('buildhtml');
|
|
726
|
+
const { Document, createCachedRenderer } = require('@trebor/buildhtml');
|
|
667
727
|
|
|
668
728
|
const app = express();
|
|
669
729
|
|
package/index.js
CHANGED
|
@@ -688,25 +688,20 @@ class LRUCache {
|
|
|
688
688
|
const responseCache = new LRUCache(CONFIG.cacheLimit);
|
|
689
689
|
const inFlightCache = new Map();
|
|
690
690
|
|
|
691
|
-
/* ---------------- ENCRYPTION ---------------- */
|
|
691
|
+
/* ---------------- OBFUSCATION (NOT ENCRYPTION) ---------------- */
|
|
692
692
|
// Simple Base64 obfuscation for JSON data
|
|
693
|
-
//
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
return 1;
|
|
697
|
-
}
|
|
698
|
-
|
|
699
|
-
function encryptString(str, key) {
|
|
693
|
+
// WARNING: This is obfuscation ONLY, NOT cryptographic security!
|
|
694
|
+
// It only reduces payload size and makes data less human-readable.
|
|
695
|
+
function obfuscateString(str) {
|
|
700
696
|
// Simple Base64 is fast and sufficient for obfuscation.
|
|
701
|
-
// Reversing large strings is too expensive for the client.
|
|
702
697
|
if (typeof Buffer !== 'undefined') {
|
|
703
698
|
return Buffer.from(str, 'utf-8').toString('base64');
|
|
704
699
|
}
|
|
705
700
|
return btoa(unescape(encodeURIComponent(str)));
|
|
706
701
|
}
|
|
707
702
|
|
|
708
|
-
function
|
|
709
|
-
// Optimized decoder: Just decode Base64
|
|
703
|
+
function getDeobfuscateScript() {
|
|
704
|
+
// Optimized decoder: Just decode Base64
|
|
710
705
|
return `var _d=function(e){return decodeURIComponent(escape(atob(e)));};`;
|
|
711
706
|
}
|
|
712
707
|
|
|
@@ -962,23 +957,13 @@ class Document {
|
|
|
962
957
|
doc.head.globalStyles = json.globalStyles || [];
|
|
963
958
|
doc.head.classStyles = json.classStyles || {};
|
|
964
959
|
|
|
965
|
-
// Restore global state
|
|
960
|
+
// Restore global state (data only)
|
|
966
961
|
doc._globalState = json.globalState || {};
|
|
967
962
|
|
|
968
|
-
//
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
// eslint-disable-next-line no-eval
|
|
973
|
-
const fn = eval(`(${fnStr})`);
|
|
974
|
-
doc._oncreateCallbacks.push(fn);
|
|
975
|
-
} catch (err) {
|
|
976
|
-
if (CONFIG.mode === 'dev') {
|
|
977
|
-
console.error('Failed to restore oncreate callback:', err);
|
|
978
|
-
}
|
|
979
|
-
}
|
|
980
|
-
}
|
|
981
|
-
}
|
|
963
|
+
// WARNING: Functions cannot be restored from JSON for security reasons.
|
|
964
|
+
// oncreate callbacks, computed functions, and event handlers are NOT restored.
|
|
965
|
+
// This method is intended for server-side data restoration only.
|
|
966
|
+
// For client-side hydration with functions, use renderJSON() instead.
|
|
982
967
|
|
|
983
968
|
// Restore body
|
|
984
969
|
const deserializeElement = (node) => {
|
|
@@ -1009,35 +994,9 @@ class Document {
|
|
|
1009
994
|
el._stateBindings = node.stateBindings;
|
|
1010
995
|
}
|
|
1011
996
|
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
el._computed = eval(`(${node.computed})`);
|
|
1016
|
-
} catch (err) {
|
|
1017
|
-
if (CONFIG.mode === 'dev') {
|
|
1018
|
-
console.error('Failed to restore computed function:', err);
|
|
1019
|
-
}
|
|
1020
|
-
}
|
|
1021
|
-
}
|
|
1022
|
-
|
|
1023
|
-
if (node.events && Array.isArray(node.events)) {
|
|
1024
|
-
for (const evt of node.events) {
|
|
1025
|
-
try {
|
|
1026
|
-
// eslint-disable-next-line no-eval
|
|
1027
|
-
const fn = eval(`(${evt.fn})`);
|
|
1028
|
-
el.events.push({
|
|
1029
|
-
event: evt.event,
|
|
1030
|
-
id: evt.id,
|
|
1031
|
-
targetId: evt.targetId,
|
|
1032
|
-
fn: fn
|
|
1033
|
-
});
|
|
1034
|
-
} catch (err) {
|
|
1035
|
-
if (CONFIG.mode === 'dev') {
|
|
1036
|
-
console.error('Failed to restore event handler:', err);
|
|
1037
|
-
}
|
|
1038
|
-
}
|
|
1039
|
-
}
|
|
1040
|
-
}
|
|
997
|
+
// NOTE: Functions (computed, events, oncreate) are NOT restored from JSON
|
|
998
|
+
// for security reasons. This method is for data restoration only.
|
|
999
|
+
// For full client-side hydration with functions, use renderJSON() instead.
|
|
1041
1000
|
|
|
1042
1001
|
el.hydrate = node.hydrate || false;
|
|
1043
1002
|
|
|
@@ -1206,8 +1165,8 @@ class Document {
|
|
|
1206
1165
|
|
|
1207
1166
|
// Inject JSON Data (Fast Decode)
|
|
1208
1167
|
if (obfuscate) {
|
|
1209
|
-
const obfuscated =
|
|
1210
|
-
parts.push(
|
|
1168
|
+
const obfuscated = obfuscateString(jsonStr);
|
|
1169
|
+
parts.push(getDeobfuscateScript(), `window.${jsonVarName}=JSON.parse(_d("${obfuscated}"));`);
|
|
1211
1170
|
} else {
|
|
1212
1171
|
parts.push(`window.${jsonVarName}=${jsonStr};`);
|
|
1213
1172
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@trebor/buildhtml",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.4",
|
|
4
4
|
"description": "Zero-dependency, ultra-fast HTML builder for server-side rendering (SSR).",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"files": [
|
|
@@ -35,4 +35,4 @@
|
|
|
35
35
|
"url": "https://github.com/0trebor0/buildhtml/issues"
|
|
36
36
|
},
|
|
37
37
|
"homepage": "https://github.com/0trebor0/buildhtml#readme"
|
|
38
|
-
}
|
|
38
|
+
}
|