webhanger 1.0.0 → 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 +594 -0
- package/bin/cli.js +502 -2
- package/core/builder.js +133 -0
- package/core/registry.js +15 -6
- package/core/resolver.js +63 -0
- package/helper/accessControl.js +112 -0
- package/helper/analyzer.js +184 -0
- package/helper/breakdown.js +63 -0
- package/helper/bundler.js +29 -15
- package/helper/converter.js +190 -0
- package/helper/crypto.js +39 -0
- package/helper/dbHandler.js +15 -1
- package/helper/loadConfig.js +25 -10
- package/index.js +5 -0
- package/package.json +13 -3
package/README.md
ADDED
|
@@ -0,0 +1,594 @@
|
|
|
1
|
+
# WebHanger
|
|
2
|
+
|
|
3
|
+
> **Component-as-a-Service (CaaS)** — Bundle once. Encrypt with AES-256. Deliver via edge CDN. Load anywhere with zero code.
|
|
4
|
+
|
|
5
|
+
WebHanger is a secure, edge-delivered component distribution platform. Think of it as npm for UI components — but instead of installing packages, you deploy encrypted components to a CDN and load them into any website with a single HTML tag.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Packages
|
|
10
|
+
|
|
11
|
+
| Package | Install | Description |
|
|
12
|
+
|---|---|---|
|
|
13
|
+
| `webhanger` | `npm install -g webhanger` | CLI + Node.js library |
|
|
14
|
+
| `webhanger-front` | `npm install webhanger-front` | Browser SDK |
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## Quick Start
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
# 1. Install CLI
|
|
22
|
+
npm install -g webhanger
|
|
23
|
+
|
|
24
|
+
# 2. Setup your project (provisions S3 + CloudFront automatically)
|
|
25
|
+
wh init
|
|
26
|
+
|
|
27
|
+
# 3. Deploy all components, build site, zip for upload — one command
|
|
28
|
+
wh ship ./components ./docs 1.0.0 ./dist
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Load in any website — zero JS required:
|
|
32
|
+
|
|
33
|
+
```html
|
|
34
|
+
<script src="https://unpkg.com/webhanger-front@latest/browser.min.js"></script>
|
|
35
|
+
<script>WebHangerFront.initialize("./wh-manifest.json");</script>
|
|
36
|
+
|
|
37
|
+
<wh-component name="navbar"></wh-component>
|
|
38
|
+
<wh-component name="hero"></wh-component>
|
|
39
|
+
<wh-component name="footer"></wh-component>
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## CLI Reference
|
|
45
|
+
|
|
46
|
+
### `wh init`
|
|
47
|
+
|
|
48
|
+
Interactive project setup. Provisions infrastructure automatically.
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
wh init
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Prompts:
|
|
55
|
+
- Project name
|
|
56
|
+
- Storage provider: `s3` | `r2` | `minio` | `local`
|
|
57
|
+
- Database provider: `firebase` | `supabase` | `mongodb`
|
|
58
|
+
- Credentials
|
|
59
|
+
|
|
60
|
+
For **S3**: automatically creates the bucket, configures CORS + versioning, spins up a CloudFront distribution. No AWS Console needed.
|
|
61
|
+
|
|
62
|
+
Optional: setup Cloudflare Edge Worker for token validation + geo routing at the edge.
|
|
63
|
+
|
|
64
|
+
Generates `webhanger.config.json`.
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
### `wh ship` ⭐ The main command
|
|
69
|
+
|
|
70
|
+
Deploy + build + zip in one shot.
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
wh ship <components-dir> <site-dir> [version] [out-dir]
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
wh ship ./components ./docs 1.0.0 ./dist
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
What it does:
|
|
81
|
+
1. Deploys all components (bundle → AES encrypt → upload → sign → register)
|
|
82
|
+
2. Resolves dependency graph
|
|
83
|
+
3. Writes `wh-manifest.json` (no sensitive data in HTML)
|
|
84
|
+
4. Production builds the site (minify HTML, inline CSS/JS)
|
|
85
|
+
5. Zips output for direct upload
|
|
86
|
+
|
|
87
|
+
```
|
|
88
|
+
🚀 [1/4] Deploying components...
|
|
89
|
+
navbar@1.0.0... ✅
|
|
90
|
+
hero@1.0.0... ✅
|
|
91
|
+
|
|
92
|
+
🔍 [2/4] Resolving dependency graph...
|
|
93
|
+
|
|
94
|
+
🏗️ [3/4] Building ./docs → ./dist...
|
|
95
|
+
index.html 4.2 kB
|
|
96
|
+
|
|
97
|
+
📦 [4/4] Zipping ./dist...
|
|
98
|
+
|
|
99
|
+
✅ Ship complete!
|
|
100
|
+
Deploy zip: ./deploy.zip (12.4 kB)
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Upload `deploy.zip` to Netlify, S3, cPanel, or any static host.
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
### `wh deploy`
|
|
108
|
+
|
|
109
|
+
Deploy a single component.
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
wh deploy <component-dir> <name> <version>
|
|
113
|
+
wh deploy ./components/navbar navbar 1.0.0
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Prompts for token (auto-generate or custom) and expiry (never or seconds).
|
|
117
|
+
|
|
118
|
+
---
|
|
119
|
+
|
|
120
|
+
### `wh graph-deploy`
|
|
121
|
+
|
|
122
|
+
Deploy all components in a directory and resolve the dependency graph.
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
wh graph-deploy <components-dir> [version] [out-dir]
|
|
126
|
+
wh graph-deploy ./components 1.0.0 ./output
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
### `wh build`
|
|
132
|
+
|
|
133
|
+
Production build — minifies HTML, inlines local CSS/JS.
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
wh build <src-dir> [out-dir]
|
|
137
|
+
wh build ./docs ./dist
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
---
|
|
141
|
+
|
|
142
|
+
### `wh zip`
|
|
143
|
+
|
|
144
|
+
Zip a directory for deployment.
|
|
145
|
+
|
|
146
|
+
```bash
|
|
147
|
+
wh zip <src-dir> [out-file]
|
|
148
|
+
wh zip ./dist ./deploy.zip
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
---
|
|
152
|
+
|
|
153
|
+
### `wh analyze`
|
|
154
|
+
|
|
155
|
+
Detect framework, styling approach, and CDN dependencies automatically.
|
|
156
|
+
|
|
157
|
+
```bash
|
|
158
|
+
wh analyze ./components/navbar
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
```
|
|
162
|
+
🔍 Component Analysis
|
|
163
|
+
|
|
164
|
+
Framework : vanilla
|
|
165
|
+
Styling : tailwind
|
|
166
|
+
Imports : gsap, axios
|
|
167
|
+
CDN Assets:
|
|
168
|
+
[script] https://cdn.tailwindcss.com
|
|
169
|
+
[script] https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/gsap.min.js
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
---
|
|
173
|
+
|
|
174
|
+
### `wh convert`
|
|
175
|
+
|
|
176
|
+
Convert a vanilla HTML/CSS/JS component to any framework.
|
|
177
|
+
|
|
178
|
+
```bash
|
|
179
|
+
wh convert <dir> <name> <target> [out-dir]
|
|
180
|
+
wh convert ./components/navbar navbar react ./output
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
Supported targets: `react` `vue` `svelte` `next` `angular` `astro`
|
|
184
|
+
|
|
185
|
+
---
|
|
186
|
+
|
|
187
|
+
### `wh access`
|
|
188
|
+
|
|
189
|
+
Role-based access control for teams.
|
|
190
|
+
|
|
191
|
+
```bash
|
|
192
|
+
wh access grant # generate API key with role
|
|
193
|
+
wh access revoke <key> # revoke a key
|
|
194
|
+
wh access list # list all keys
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
Roles:
|
|
198
|
+
|
|
199
|
+
| Role | deploy | read | delete | manage_access |
|
|
200
|
+
|---|---|---|---|---|
|
|
201
|
+
| owner | ✅ | ✅ | ✅ | ✅ |
|
|
202
|
+
| admin | ✅ | ✅ | ✅ | ✅ |
|
|
203
|
+
| deployer | ✅ | ✅ | ❌ | ❌ |
|
|
204
|
+
| viewer | ❌ | ✅ | ❌ | ❌ |
|
|
205
|
+
|
|
206
|
+
---
|
|
207
|
+
|
|
208
|
+
### `wh edge-init`
|
|
209
|
+
|
|
210
|
+
Setup Cloudflare Edge Worker for production-grade delivery.
|
|
211
|
+
|
|
212
|
+
```bash
|
|
213
|
+
wh edge-init
|
|
214
|
+
# then:
|
|
215
|
+
cd edge && wrangler deploy
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
---
|
|
219
|
+
|
|
220
|
+
## Component Structure
|
|
221
|
+
|
|
222
|
+
```
|
|
223
|
+
components/
|
|
224
|
+
navbar/
|
|
225
|
+
index.html ← markup
|
|
226
|
+
style.css ← styles
|
|
227
|
+
script.js ← behaviour
|
|
228
|
+
webhanger.component.json ← assets + dependencies
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
### `webhanger.component.json`
|
|
232
|
+
|
|
233
|
+
```json
|
|
234
|
+
{
|
|
235
|
+
"assets": [
|
|
236
|
+
{ "type": "script", "url": "https://cdn.tailwindcss.com" },
|
|
237
|
+
{ "type": "script", "url": "https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/gsap.min.js" }
|
|
238
|
+
],
|
|
239
|
+
"dependencies": ["navbar@1.0.0", "chart@2.0.0"]
|
|
240
|
+
}
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
CDN assets are auto-detected by `wh analyze` and merged automatically on deploy.
|
|
244
|
+
|
|
245
|
+
---
|
|
246
|
+
|
|
247
|
+
## Dependency Graph
|
|
248
|
+
|
|
249
|
+
npm-like dependency resolution for UI components.
|
|
250
|
+
|
|
251
|
+
```
|
|
252
|
+
dashboard@1.0.0
|
|
253
|
+
├── navbar@1.0.0
|
|
254
|
+
└── statsbar@1.0.0
|
|
255
|
+
└── chart@1.0.0
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
- Depth-first resolution
|
|
259
|
+
- Circular dependency detection with full chain error
|
|
260
|
+
- Deps load before parent component
|
|
261
|
+
- Stored in Firestore per project
|
|
262
|
+
|
|
263
|
+
```bash
|
|
264
|
+
wh graph-deploy ./components 1.0.0 ./output
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
Programmatic:
|
|
268
|
+
|
|
269
|
+
```js
|
|
270
|
+
import { resolveGraph } from "webhanger";
|
|
271
|
+
const graph = await resolveGraph(config.db, projectId, "dashboard", "1.0.0");
|
|
272
|
+
// Returns: [chart, navbar, statsbar, dashboard] — deps first
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
---
|
|
276
|
+
|
|
277
|
+
## Security
|
|
278
|
+
|
|
279
|
+
### AES-256-GCM Encryption
|
|
280
|
+
|
|
281
|
+
Every component chunk (HTML/CSS/JS) is encrypted before upload.
|
|
282
|
+
|
|
283
|
+
```
|
|
284
|
+
Key = SHA-256(projectId + salt)
|
|
285
|
+
Payload = iv:tag:ciphertext (base64)
|
|
286
|
+
Salts = "::html" | "::css" | "::js"
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
### HMAC-SHA256 Signed URLs
|
|
290
|
+
|
|
291
|
+
```
|
|
292
|
+
token = HMAC-SHA256(projectId:componentPath:expires, secretKey)
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
### Integrity Check
|
|
296
|
+
|
|
297
|
+
SHA-256 hash of raw content stored in payload. Verified after decryption — detects tampering.
|
|
298
|
+
|
|
299
|
+
### Domain Restriction
|
|
300
|
+
|
|
301
|
+
```js
|
|
302
|
+
WebHangerFront.load(url, pid, token, 0, "[data-wh]", null, [], {
|
|
303
|
+
allowedDomains: ["mysite.com", "app.io"]
|
|
304
|
+
});
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
### Token Expiry
|
|
308
|
+
|
|
309
|
+
```js
|
|
310
|
+
// Never expires
|
|
311
|
+
wh.deploy("./navbar", "navbar", "1.0.0");
|
|
312
|
+
|
|
313
|
+
// Expires in 24 hours
|
|
314
|
+
wh.deploy("./navbar", "navbar", "1.0.0", { expiresInSeconds: 86400 });
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
### Manifest-based Delivery
|
|
318
|
+
|
|
319
|
+
Tokens, projectId, and CDN URLs never appear in HTML. Fetched at runtime from `wh-manifest.json`.
|
|
320
|
+
|
|
321
|
+
---
|
|
322
|
+
|
|
323
|
+
## Edge Worker (Cloudflare Workers)
|
|
324
|
+
|
|
325
|
+
Runs at the edge before S3:
|
|
326
|
+
|
|
327
|
+
- HMAC token validation
|
|
328
|
+
- Version resolution (`latest` → `1.2.0` from KV)
|
|
329
|
+
- Geo-based routing (India → ap-south-1, Europe → eu-west-1)
|
|
330
|
+
- Rate limiting (100 req/min per IP)
|
|
331
|
+
|
|
332
|
+
```bash
|
|
333
|
+
wh init # answer yes to edge worker setup
|
|
334
|
+
cd edge && wrangler deploy
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
Update `cdn.url` in `webhanger.config.json` to your worker URL — all requests now go through the edge.
|
|
338
|
+
|
|
339
|
+
---
|
|
340
|
+
|
|
341
|
+
## Multi-CDN Failover
|
|
342
|
+
|
|
343
|
+
```json
|
|
344
|
+
{
|
|
345
|
+
"cdn": {
|
|
346
|
+
"url": "https://primary.cloudfront.net",
|
|
347
|
+
"fallbacks": [
|
|
348
|
+
"https://fallback.r2.dev",
|
|
349
|
+
"https://backup.b-cdn.net"
|
|
350
|
+
]
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
SDK tries each URL in order. If primary fails, automatically falls back — zero code changes needed.
|
|
356
|
+
|
|
357
|
+
---
|
|
358
|
+
|
|
359
|
+
## Browser SDK
|
|
360
|
+
|
|
361
|
+
### Zero-code (recommended)
|
|
362
|
+
|
|
363
|
+
```html
|
|
364
|
+
<script src="https://unpkg.com/webhanger-front@latest/browser.min.js"></script>
|
|
365
|
+
<script>WebHangerFront.initialize("./wh-manifest.json");</script>
|
|
366
|
+
|
|
367
|
+
<wh-component name="navbar"></wh-component>
|
|
368
|
+
<wh-component name="hero"></wh-component>
|
|
369
|
+
<wh-component name="footer" sandbox></wh-component>
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
### Manual load
|
|
373
|
+
|
|
374
|
+
```js
|
|
375
|
+
WebHangerFront.load(
|
|
376
|
+
cdnUrl, // string or array (multi-CDN)
|
|
377
|
+
projectId, // decrypt key
|
|
378
|
+
token, // HMAC token
|
|
379
|
+
expires, // unix timestamp, 0 = never
|
|
380
|
+
selector, // CSS selector, default "[data-wh]"
|
|
381
|
+
onSignal, // optional signal callback
|
|
382
|
+
deps, // optional pre-resolved deps array
|
|
383
|
+
options // optional options object
|
|
384
|
+
);
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
### Lifecycle hooks
|
|
388
|
+
|
|
389
|
+
```js
|
|
390
|
+
WebHangerFront.load(url, pid, token, 0, "[data-wh]", null, [], {
|
|
391
|
+
beforeMount: ({ html }) => showSpinner(),
|
|
392
|
+
afterMount: ({ target }) => hideSpinner(),
|
|
393
|
+
onError: (err) => showFallback(err),
|
|
394
|
+
sandbox: true, // Shadow DOM isolation
|
|
395
|
+
allowedDomains: ["mysite.com"] // domain restriction
|
|
396
|
+
});
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
### Signal callback
|
|
400
|
+
|
|
401
|
+
```js
|
|
402
|
+
WebHangerFront.load(url, pid, token, 0, "[data-wh]", ({ stage, ...detail }) => {
|
|
403
|
+
// stages: start → fetching → assets → deps → injecting → done | error
|
|
404
|
+
console.log(stage, detail.time, detail.source);
|
|
405
|
+
});
|
|
406
|
+
```
|
|
407
|
+
|
|
408
|
+
### Plugin system
|
|
409
|
+
|
|
410
|
+
```js
|
|
411
|
+
WebHangerFront.use({
|
|
412
|
+
install({ on, emit }) {
|
|
413
|
+
on("load", ({ time, source }) => analytics.track("load", { time, source }));
|
|
414
|
+
on("error", ({ message }) => errorTracker.capture(message));
|
|
415
|
+
on("metric", ({ name, value }) => dashboard.update(name, value));
|
|
416
|
+
}
|
|
417
|
+
});
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
### Observability
|
|
421
|
+
|
|
422
|
+
```js
|
|
423
|
+
WebHangerFront.on("load", ({ time, source }) => console.log(time, source));
|
|
424
|
+
WebHangerFront.on("metric", ({ name, value }) => console.log(name, value));
|
|
425
|
+
|
|
426
|
+
console.log(WebHangerFront.metrics);
|
|
427
|
+
// { loads: 5, cacheHits: 3, errors: 0, totalTime: 420 }
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
### Hard flush
|
|
431
|
+
|
|
432
|
+
```js
|
|
433
|
+
await WebHangerFront.clearCache();
|
|
434
|
+
// Clears: localStorage, IndexedDB, SW caches, sessionStorage, unregisters SW
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
---
|
|
438
|
+
|
|
439
|
+
## Caching
|
|
440
|
+
|
|
441
|
+
| Layer | Used for |
|
|
442
|
+
|---|---|
|
|
443
|
+
| `localStorage` | Components < 50KB |
|
|
444
|
+
| `IndexedDB` | Components ≥ 50KB |
|
|
445
|
+
| Service Worker | Offline fallback |
|
|
446
|
+
|
|
447
|
+
**Stale-while-revalidate** — returns cached version instantly, refreshes in background.
|
|
448
|
+
|
|
449
|
+
---
|
|
450
|
+
|
|
451
|
+
## Node.js API
|
|
452
|
+
|
|
453
|
+
```js
|
|
454
|
+
import { WebHanger } from "webhanger";
|
|
455
|
+
const wh = new WebHanger();
|
|
456
|
+
|
|
457
|
+
// Deploy
|
|
458
|
+
const result = await wh.deploy("./components/navbar", "navbar", "1.0.0", {
|
|
459
|
+
expiresInSeconds: 86400, // optional
|
|
460
|
+
token: "custom-token", // optional
|
|
461
|
+
dependencies: ["chart@1.0.0"]
|
|
462
|
+
});
|
|
463
|
+
// { cdnUrl, cdnUrls, token, expires, dependencies }
|
|
464
|
+
|
|
465
|
+
// Resolve
|
|
466
|
+
const comp = await wh.resolve("navbar", "1.0.0");
|
|
467
|
+
|
|
468
|
+
// Rotate token without redeploying
|
|
469
|
+
await wh.resign("navbar", "1.0.0", { expiresInSeconds: 3600 });
|
|
470
|
+
|
|
471
|
+
// Delete from storage
|
|
472
|
+
await wh.remove("navbar", "1.0.0");
|
|
473
|
+
|
|
474
|
+
// Get config
|
|
475
|
+
const config = wh.getConfig();
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
### Named exports
|
|
479
|
+
|
|
480
|
+
```js
|
|
481
|
+
import {
|
|
482
|
+
bundle, encrypt, decrypt, integrityHash,
|
|
483
|
+
signUrl, verifyToken, generateSecretKey,
|
|
484
|
+
upload, remove,
|
|
485
|
+
registerComponent, getComponent,
|
|
486
|
+
deploy, resolveGraph,
|
|
487
|
+
convert, analyzeComponent,
|
|
488
|
+
build,
|
|
489
|
+
grantAccess, revokeAccess, checkPermission, listAccess, generateApiKey,
|
|
490
|
+
provisionBucket, provisionCloudFront,
|
|
491
|
+
loadConfig
|
|
492
|
+
} from "webhanger";
|
|
493
|
+
```
|
|
494
|
+
|
|
495
|
+
---
|
|
496
|
+
|
|
497
|
+
## Storage Providers
|
|
498
|
+
|
|
499
|
+
| Provider | Notes |
|
|
500
|
+
|---|---|
|
|
501
|
+
| `s3` | AWS S3 — auto-provisions bucket + CloudFront |
|
|
502
|
+
| `r2` | Cloudflare R2 — zero egress fees |
|
|
503
|
+
| `minio` | Self-hosted MinIO — S3-compatible |
|
|
504
|
+
| `local` | Local disk — dev/testing only |
|
|
505
|
+
|
|
506
|
+
## Database Providers
|
|
507
|
+
|
|
508
|
+
| Provider | Notes |
|
|
509
|
+
|---|---|
|
|
510
|
+
| `firebase` | Firebase Firestore — free tier, real-time |
|
|
511
|
+
| `supabase` | Supabase Postgres — open source |
|
|
512
|
+
| `mongodb` | MongoDB Atlas — flexible documents |
|
|
513
|
+
|
|
514
|
+
---
|
|
515
|
+
|
|
516
|
+
## `webhanger.config.json`
|
|
517
|
+
|
|
518
|
+
```json
|
|
519
|
+
{
|
|
520
|
+
"project": "my-app",
|
|
521
|
+
"projectId": "wh_1234567890",
|
|
522
|
+
"secretKey": "64-char-hex-secret",
|
|
523
|
+
"webHangerVersion": "1.0.0",
|
|
524
|
+
"storage": {
|
|
525
|
+
"provider": "s3",
|
|
526
|
+
"accessKey": "...",
|
|
527
|
+
"secretKey": "...",
|
|
528
|
+
"bucket": "my-bucket",
|
|
529
|
+
"region": "ap-south-1",
|
|
530
|
+
"distributionId": "EXXXXX"
|
|
531
|
+
},
|
|
532
|
+
"cdn": {
|
|
533
|
+
"url": "https://primary.cloudfront.net",
|
|
534
|
+
"fallbacks": ["https://fallback.r2.dev"]
|
|
535
|
+
},
|
|
536
|
+
"db": {
|
|
537
|
+
"provider": "firebase",
|
|
538
|
+
"serviceAccountPath": "./firebase-service-account.json"
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
```
|
|
542
|
+
|
|
543
|
+
> Keep this file private. Never commit it. Add to `.gitignore`.
|
|
544
|
+
|
|
545
|
+
---
|
|
546
|
+
|
|
547
|
+
## Architecture
|
|
548
|
+
|
|
549
|
+
```
|
|
550
|
+
Developer
|
|
551
|
+
└── wh ship ./components ./docs
|
|
552
|
+
├── wh analyze → detect Tailwind, GSAP, deps
|
|
553
|
+
├── wh bundle → html + css + js → single payload
|
|
554
|
+
├── AES-256-GCM → encrypt each chunk (SHA-256 key)
|
|
555
|
+
├── SHA-256 hash → integrity fingerprint
|
|
556
|
+
├── S3 upload → store encrypted payload
|
|
557
|
+
├── HMAC sign → project-scoped signed URL
|
|
558
|
+
├── Firestore → register metadata + dep graph
|
|
559
|
+
├── wh build → minify HTML, inline assets
|
|
560
|
+
└── wh zip → deploy.zip ready for upload
|
|
561
|
+
|
|
562
|
+
Browser
|
|
563
|
+
└── <wh-component name="navbar">
|
|
564
|
+
├── fetch wh-manifest.json
|
|
565
|
+
├── check token expiry
|
|
566
|
+
├── stale-while-revalidate cache
|
|
567
|
+
├── fetch from CloudFront (or Edge Worker)
|
|
568
|
+
│ └── Edge: validate token + geo route + rate limit
|
|
569
|
+
├── multi-CDN failover if primary fails
|
|
570
|
+
├── load CDN assets (Tailwind, GSAP, etc.)
|
|
571
|
+
├── resolve + load dependency graph
|
|
572
|
+
├── AES-256-GCM decrypt in memory
|
|
573
|
+
├── SHA-256 integrity verify
|
|
574
|
+
├── domain restriction check
|
|
575
|
+
├── inject CSS → HTML → JS (or Shadow DOM)
|
|
576
|
+
├── fire lifecycle hooks
|
|
577
|
+
└── emit metrics + plugin events
|
|
578
|
+
```
|
|
579
|
+
|
|
580
|
+
---
|
|
581
|
+
|
|
582
|
+
## Real-World Use Cases
|
|
583
|
+
|
|
584
|
+
- **Enterprise micro-frontends** — shared UI across 50+ apps, update once
|
|
585
|
+
- **Education platforms** — push UI updates to all school sites instantly
|
|
586
|
+
- **Low-bandwidth regions** — cache once, serve offline via Service Worker
|
|
587
|
+
- **Security platforms** — inject warnings/banners dynamically across sites
|
|
588
|
+
- **White-label SaaS** — per-tenant component customization
|
|
589
|
+
|
|
590
|
+
---
|
|
591
|
+
|
|
592
|
+
## License
|
|
593
|
+
|
|
594
|
+
ISC
|