webhanger 1.0.6 → 1.0.9

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
@@ -1,34 +1,32 @@
1
1
  # WebHanger
2
2
 
3
- > **Component-as-a-Service (CaaS)** — Bundle once. Encrypt with AES-256. Deliver via edge CDN. Load anywhere with zero code.
3
+ > **Component-as-a-Service (CaaS)** — Bundle once. AES-256 encrypt. Deploy to edge CDN. Load anywhere with zero code.
4
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.
5
+ WebHanger is a secure, edge-delivered component distribution platform. Deploy encrypted UI components to a CDN and load them into any website or framework with a single tag — no tokens in HTML, no exposed secrets, no configuration.
6
6
 
7
7
  ---
8
8
 
9
+
9
10
  ## Packages
10
11
 
11
12
  | Package | Install | Description |
12
13
  |---|---|---|
13
14
  | `webhanger` | `npm install -g webhanger` | CLI + Node.js library |
14
- | `webhanger-front` | `npm install webhanger-front` | Browser SDK |
15
+ | `webhanger-front` | `npm install webhanger-front` | Browser + ESM SDK |
16
+ | `webhanger-admin` | `npm install webhanger-admin` | Admin SDK + dashboard |
17
+ | `webhanger-auth` | `npm install webhanger-auth` | OAuth authentication SDK |
15
18
 
16
19
  ---
17
20
 
18
21
  ## Quick Start
19
22
 
20
23
  ```bash
21
- # 1. Install CLI
22
24
  npm install -g webhanger
23
-
24
- # 2. Setup your project (provisions S3 + CloudFront automatically)
25
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
26
+ wh ship ./components ./site 1.0.0 ./dist
29
27
  ```
30
28
 
31
- Load in any website — zero JS required:
29
+ Load in any HTML:
32
30
 
33
31
  ```html
34
32
  <script src="https://unpkg.com/webhanger-front@latest/browser.min.js"></script>
@@ -36,185 +34,109 @@ Load in any website — zero JS required:
36
34
 
37
35
  <wh-component name="navbar"></wh-component>
38
36
  <wh-component name="hero"></wh-component>
39
- <wh-component name="footer"></wh-component>
37
+ <wh-component name="footer" sandbox></wh-component>
40
38
  ```
41
39
 
42
- ---
43
-
44
- ## CLI Reference
45
-
46
- ### `wh init`
40
+ Load in Next.js / React / Vite:
47
41
 
48
- Interactive project setup. Provisions infrastructure automatically.
42
+ ```js
43
+ import { load } from "webhanger-front";
49
44
 
50
- ```bash
51
- wh init
45
+ const manifest = await fetch("/wh-manifest.json").then(r => r.json());
46
+ const c = manifest.components["navbar"];
47
+ await load(c.urls || c.url, manifest.pid, c.token, c.expires, "#nav-mount");
52
48
  ```
53
49
 
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
50
  ---
67
51
 
68
- ### `wh ship` ⭐ The main command
69
-
70
- Deploy + build + zip in one shot.
52
+ ## CLI Reference
71
53
 
72
- ```bash
73
- wh ship <components-dir> <site-dir> [version] [out-dir]
74
- ```
54
+ ### `wh init`
55
+ Interactive setup. Provisions S3 bucket + CloudFront automatically. Supports Firebase, Supabase, MongoDB. Optional Cloudflare Edge Worker setup.
75
56
 
57
+ ### `wh ship` ⭐
58
+ Deploy + build + zip in one shot.
76
59
  ```bash
77
- wh ship ./components ./docs 1.0.0 ./dist
60
+ wh ship ./components ./site 1.0.0 ./dist
78
61
  ```
79
-
80
- What it does:
81
- 1. Deploys all components (bundle → AES encrypt → upload → sign → register)
62
+ 1. Deploys all components (bundle → AES-256 encrypt → upload → HMAC sign → register)
82
63
  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
- ---
64
+ 3. Writes `wh-manifest.json`
65
+ 4. Production builds the site
66
+ 5. Zips for upload
106
67
 
107
68
  ### `wh deploy`
108
-
109
- Deploy a single component.
110
-
111
69
  ```bash
112
- wh deploy <component-dir> <name> <version>
113
70
  wh deploy ./components/navbar navbar 1.0.0
114
71
  ```
115
72
 
116
- Prompts for token (auto-generate or custom) and expiry (never or seconds).
117
-
118
- ---
119
-
120
73
  ### `wh graph-deploy`
121
-
122
- Deploy all components in a directory and resolve the dependency graph.
123
-
124
74
  ```bash
125
- wh graph-deploy <components-dir> [version] [out-dir]
126
75
  wh graph-deploy ./components 1.0.0 ./output
127
76
  ```
128
77
 
129
- ---
78
+ ### `wh atomize`
79
+ Split a single HTML page into CDN-powered components.
80
+ ```bash
81
+ wh atomize ./docs/index.html ./atomized 1.0.0
82
+ ```
130
83
 
131
84
  ### `wh build`
132
-
133
- Production build — minifies HTML, inlines local CSS/JS.
134
-
85
+ Production build — minifies HTML, extracts CSS/JS to hashed files.
135
86
  ```bash
136
- wh build <src-dir> [out-dir]
137
- wh build ./docs ./dist
87
+ wh build ./site ./dist
138
88
  ```
139
89
 
140
- ---
141
-
142
90
  ### `wh zip`
143
-
144
- Zip a directory for deployment.
145
-
146
91
  ```bash
147
- wh zip <src-dir> [out-file]
148
92
  wh zip ./dist ./deploy.zip
149
93
  ```
150
94
 
151
- ---
152
-
153
95
  ### `wh analyze`
154
-
155
- Detect framework, styling approach, and CDN dependencies automatically.
156
-
157
96
  ```bash
158
97
  wh analyze ./components/navbar
159
98
  ```
160
99
 
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
100
  ### `wh convert`
175
-
176
- Convert a vanilla HTML/CSS/JS component to any framework.
177
-
178
101
  ```bash
179
- wh convert <dir> <name> <target> [out-dir]
180
102
  wh convert ./components/navbar navbar react ./output
103
+ # targets: react | vue | svelte | next | angular | astro
181
104
  ```
182
105
 
183
- Supported targets: `react` `vue` `svelte` `next` `angular` `astro`
184
-
185
- ---
106
+ ### `wh breakdown`
107
+ Extract embedded CSS/JS from a single HTML file.
108
+ ```bash
109
+ wh breakdown ./components/navbar
110
+ ```
186
111
 
187
112
  ### `wh access`
188
-
189
- Role-based access control for teams.
190
-
191
113
  ```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
114
+ wh access grant
115
+ wh access revoke <key>
116
+ wh access list
195
117
  ```
196
118
 
197
- Roles:
198
-
199
- | Role | deploy | read | delete | manage_access |
200
- |---|---|---|---|---|
201
- | owner | ✅ | ✅ | ✅ | ✅ |
202
- | admin | ✅ | ✅ | ✅ | ✅ |
203
- | deployer | ✅ | ✅ | ❌ | ❌ |
204
- | viewer | ❌ | ✅ | ❌ | ❌ |
205
-
206
- ---
207
-
208
119
  ### `wh edge-init`
209
-
210
- Setup Cloudflare Edge Worker for production-grade delivery.
211
-
212
120
  ```bash
213
121
  wh edge-init
214
- # then:
215
122
  cd edge && wrangler deploy
216
123
  ```
217
124
 
125
+ ### `wh auth init`
126
+ Interactive OAuth setup — Google, GitHub, Facebook.
127
+ ```bash
128
+ wh auth init
129
+ ```
130
+ Prompts for providers, base URL, client IDs/secrets. Generates `wh-auth.config.json` with callback URLs.
131
+
132
+ ### `wh auth serve`
133
+ Start the OAuth callback server.
134
+ ```bash
135
+ wh auth serve
136
+ wh auth serve 3001 # custom port
137
+ ```
138
+ Handles OAuth redirects, token exchange, JWT issuance.
139
+
218
140
  ---
219
141
 
220
142
  ## Component Structure
@@ -222,64 +144,70 @@ cd edge && wrangler deploy
222
144
  ```
223
145
  components/
224
146
  navbar/
225
- index.html ← markup
226
- style.css ← styles
227
- script.js ← behaviour
228
- webhanger.component.json ← assets + dependencies
147
+ index.html
148
+ style.css
149
+ script.js
150
+ webhanger.component.json
229
151
  ```
230
152
 
231
153
  ### `webhanger.component.json`
232
-
233
154
  ```json
234
155
  {
235
156
  "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" }
157
+ { "type": "script", "url": "https://cdn.tailwindcss.com" }
238
158
  ],
239
- "dependencies": ["navbar@1.0.0", "chart@2.0.0"]
159
+ "dependencies": ["chart@1.0.0"],
160
+ "props": {
161
+ "brand": { "type": "string", "default": "MyApp" },
162
+ "ctaText": { "type": "string", "default": "Get Started" },
163
+ "ctaHref": { "type": "string", "default": "/signup" }
164
+ }
240
165
  }
241
166
  ```
242
167
 
243
- CDN assets are auto-detected by `wh analyze` and merged automatically on deploy.
244
-
245
168
  ---
246
169
 
247
- ## Dependency Graph
170
+ ## Component Props System
248
171
 
249
- npm-like dependency resolution for UI components.
172
+ Pass dynamic data into components via HTML attributes — no redeployment needed.
250
173
 
174
+ ### In your component HTML, use `{{wh.propName}}` placeholders:
175
+ ```html
176
+ <!-- index.html -->
177
+ <nav>
178
+ <span class="brand">{{wh.brand}}</span>
179
+ <a href="{{wh.ctaHref}}" class="cta">{{wh.ctaText}}</a>
180
+ </nav>
251
181
  ```
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
182
 
263
- ```bash
264
- wh graph-deploy ./components 1.0.0 ./output
183
+ ### Pass props via attributes:
184
+ ```html
185
+ <wh-component
186
+ name="navbar"
187
+ wh-brand="Acme Corp"
188
+ wh-cta-text="Sign Up Free"
189
+ wh-cta-href="/register">
190
+ </wh-component>
265
191
  ```
266
192
 
267
- Programmatic:
268
-
193
+ ### Or programmatically:
269
194
  ```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
195
+ await load(url, pid, token, 0, "#mount", null, [], {
196
+ props: {
197
+ brand: "Acme Corp",
198
+ ctaText: "Sign Up Free",
199
+ ctaHref: "/register"
200
+ }
201
+ });
273
202
  ```
274
203
 
204
+ Props are resolved **after decryption** — the encrypted payload on CDN contains `{{wh.brand}}` literally. The SDK substitutes values in memory before DOM injection. Defaults from `webhanger.component.json` are used when no prop is passed.
205
+
275
206
  ---
276
207
 
277
208
  ## Security
278
209
 
279
210
  ### AES-256-GCM Encryption
280
-
281
- Every component chunk (HTML/CSS/JS) is encrypted before upload.
282
-
283
211
  ```
284
212
  Key = SHA-256(projectId + salt)
285
213
  Payload = iv:tag:ciphertext (base64)
@@ -287,151 +215,349 @@ Salts = "::html" | "::css" | "::js"
287
215
  ```
288
216
 
289
217
  ### HMAC-SHA256 Signed URLs
290
-
291
218
  ```
292
- token = HMAC-SHA256(projectId:componentPath:expires, secretKey)
219
+ token = HMAC-SHA256(projectId:path:expires, secretKey)
293
220
  ```
294
221
 
295
222
  ### Integrity Check
296
-
297
- SHA-256 hash of raw content stored in payload. Verified after decryption — detects tampering.
223
+ SHA-256 hash verified after decryption — detects tampering.
298
224
 
299
225
  ### Domain Restriction
300
-
301
226
  ```js
302
- WebHangerFront.load(url, pid, token, 0, "[data-wh]", null, [], {
303
- allowedDomains: ["mysite.com", "app.io"]
227
+ load(url, pid, token, 0, "[data-wh]", null, [], {
228
+ allowedDomains: ["mysite.com"]
304
229
  });
305
230
  ```
306
231
 
307
- ### Token Expiry
232
+ ### Manifest-based Delivery
233
+ Tokens, projectId, CDN URLs never in HTML. Fetched at runtime from `wh-manifest.json`.
308
234
 
309
- ```js
310
- // Never expires
311
- wh.deploy("./navbar", "navbar", "1.0.0");
235
+ ---
236
+
237
+ ## Dependency Graph
312
238
 
313
- // Expires in 24 hours
314
- wh.deploy("./navbar", "navbar", "1.0.0", { expiresInSeconds: 86400 });
239
+ ```
240
+ dashboard@1.0.0
241
+ ├── navbar@1.0.0
242
+ └── statsbar@1.0.0
243
+ └── chart@1.0.0
315
244
  ```
316
245
 
317
- ### Manifest-based Delivery
246
+ ```js
247
+ import { resolveGraph } from "webhanger";
248
+ const graph = await resolveGraph(config.db, projectId, "dashboard", "1.0.0");
249
+ // Returns: [chart, navbar, statsbar, dashboard]
250
+ ```
318
251
 
319
- Tokens, projectId, and CDN URLs never appear in HTML. Fetched at runtime from `wh-manifest.json`.
252
+ ---
253
+
254
+ ## Multi-CDN Failover
255
+
256
+ ```json
257
+ {
258
+ "cdn": {
259
+ "url": "https://primary.cloudfront.net",
260
+ "fallbacks": ["https://fallback.r2.dev"]
261
+ }
262
+ }
263
+ ```
320
264
 
321
265
  ---
322
266
 
323
267
  ## Edge Worker (Cloudflare Workers)
324
268
 
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)
269
+ - HMAC token validation at edge
270
+ - Version resolution (`latest` → `1.2.0`)
271
+ - Geo-based routing
330
272
  - Rate limiting (100 req/min per IP)
331
273
 
274
+ ---
275
+
276
+ ## OAuth Authentication (webhanger-auth)
277
+
278
+ Drop-in OAuth for any website. Supports Google, GitHub, Facebook. Zero backend code required beyond `wh auth serve`.
279
+
280
+ ### Setup
281
+
332
282
  ```bash
333
- wh init # answer yes to edge worker setup
334
- cd edge && wrangler deploy
283
+ npm install -g webhanger # CLI
284
+ npm install webhanger-auth # browser SDK
285
+
286
+ wh auth init # configure providers
287
+ wh auth serve # start OAuth server (default port 3001)
335
288
  ```
336
289
 
337
- Update `cdn.url` in `webhanger.config.json` to your worker URL — all requests now go through the edge.
290
+ ### Zero-code HTML
338
291
 
339
- ---
292
+ ```html
293
+ <script src="https://unpkg.com/webhanger-auth/browser.min.js"></script>
340
294
 
341
- ## Multi-CDN Failover
295
+ <!-- Renders a styled OAuth button, handles the full flow -->
296
+ <wh-auth
297
+ provider="google"
298
+ on-success="/dashboard?name={{name}}&email={{email}}"
299
+ on-error="/login?error={{message}}"
300
+ theme="dark">
301
+ </wh-auth>
302
+
303
+ <wh-auth provider="github" on-success="/dashboard"></wh-auth>
304
+ <wh-auth provider="facebook" on-success="/dashboard"></wh-auth>
305
+ ```
306
+
307
+ Available `on-success` template variables: `{{name}}` `{{email}}` `{{avatar}}` `{{provider}}` `{{id}}`
308
+
309
+ ### Programmatic
310
+
311
+ ```js
312
+ // Login
313
+ WHAuth.login("google", {
314
+ onSuccess: (user) => {
315
+ console.log(user.email, user.name, user.avatar, user.provider);
316
+ redirect("/dashboard");
317
+ },
318
+ onError: (err) => showError(err.message)
319
+ });
320
+
321
+ // Check session
322
+ WHAuth.isLoggedIn(); // true/false
323
+ WHAuth.getUser(); // { email, name, avatar, provider, id }
324
+ WHAuth.getToken(); // JWT string
325
+
326
+ // Logout
327
+ WHAuth.logout();
328
+
329
+ // Events
330
+ WHAuth.on("success", ({ user, token }) => analytics.track("login", user));
331
+ WHAuth.on("error", ({ message }) => errorTracker.capture(message));
332
+ WHAuth.on("logout", () => clearSession());
333
+ ```
334
+
335
+ ### `wh-auth.config.json` (generated by `wh auth init`)
342
336
 
343
337
  ```json
344
338
  {
345
- "cdn": {
346
- "url": "https://primary.cloudfront.net",
347
- "fallbacks": [
348
- "https://fallback.r2.dev",
349
- "https://backup.b-cdn.net"
350
- ]
339
+ "baseUrl": "https://myapp.com",
340
+ "callbackPath": "/auth/callback",
341
+ "port": 3001,
342
+ "providers": {
343
+ "google": {
344
+ "clientId": "...",
345
+ "clientSecret": "...",
346
+ "callbackUrl": "https://myapp.com/auth/callback/google"
347
+ },
348
+ "github": {
349
+ "clientId": "...",
350
+ "clientSecret": "...",
351
+ "callbackUrl": "https://myapp.com/auth/callback/github"
352
+ }
353
+ },
354
+ "session": {
355
+ "secret": "auto-generated",
356
+ "expiresIn": "7d"
351
357
  }
352
358
  }
353
359
  ```
354
360
 
355
- SDK tries each URL in order. If primary fails, automatically falls back — zero code changes needed.
361
+ ### Verify JWT server-side
362
+
363
+ ```js
364
+ import { verifyAuthToken } from "webhanger-auth";
365
+
366
+ const user = await verifyAuthToken(req.headers.authorization?.split(" ")[1]);
367
+ // { email, name, avatar, provider, id, exp, iat }
368
+ ```
369
+
370
+ ### Auth flow
371
+
372
+ ```
373
+ User clicks <wh-auth provider="google">
374
+ └── popup opens → /auth/google
375
+ └── redirect to Google OAuth consent
376
+ └── Google → /auth/callback/google?code=xxx
377
+ └── exchange code → fetch profile
378
+ └── issue JWT
379
+ └── postMessage to opener
380
+ └── WHAuth.on("success") fires
381
+ └── redirect to on-success URL
382
+ ```
356
383
 
357
384
  ---
358
385
 
359
- ## Browser SDK
386
+ A local web UI + SDK for managing deployed components.
387
+
388
+ ```bash
389
+ npm install webhanger-admin
390
+
391
+ # Start dashboard
392
+ npx wh-admin ./webhanger.config.json 5000
393
+ ```
394
+
395
+ Open `http://localhost:5000`
360
396
 
361
- ### Zero-code (recommended)
397
+ Or use programmatically:
362
398
 
399
+ ```js
400
+ import { WebHangerAdmin } from "webhanger-admin";
401
+
402
+ const admin = new WebHangerAdmin("./webhanger.config.json");
403
+
404
+ const components = await admin.listComponents();
405
+ const manifest = await admin.generateManifest();
406
+ await admin.saveManifest("./public/wh-manifest.json");
407
+
408
+ const { apiKey } = await admin.grantAccess("deployer", "CI/CD");
409
+ await admin.resignComponent("navbar", "1.0.0", 86400);
410
+ await admin.deleteComponent("navbar", "1.0.0");
411
+ ```
412
+
413
+ See `webhanger-admin/README.md` for the full API reference.
414
+
415
+ ---
416
+
417
+ ## Browser SDK
418
+
419
+ ### Zero-code Custom Element
363
420
  ```html
364
421
  <script src="https://unpkg.com/webhanger-front@latest/browser.min.js"></script>
365
422
  <script>WebHangerFront.initialize("./wh-manifest.json");</script>
366
423
 
367
- <wh-component name="navbar"></wh-component>
368
- <wh-component name="hero"></wh-component>
424
+ <wh-component name="navbar" wh-brand="MyApp" wh-cta-text="Start Free"></wh-component>
369
425
  <wh-component name="footer" sandbox></wh-component>
370
426
  ```
371
427
 
372
- ### Manual load
373
-
428
+ ### ESM (Next.js / React / Vite)
374
429
  ```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
- );
430
+ import { load, use, on, registerSW, clearCache, metrics, gpu } from "webhanger-front";
385
431
  ```
386
432
 
387
- ### Lifecycle hooks
388
-
433
+ ### Manual load with props
389
434
  ```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
- });
435
+ await load(
436
+ cdnUrl,
437
+ projectId,
438
+ token,
439
+ expires,
440
+ selector,
441
+ onSignal,
442
+ deps,
443
+ {
444
+ props: { brand: "MyApp", ctaText: "Get Started" },
445
+ sandbox: true,
446
+ allowedDomains: ["mysite.com"],
447
+ beforeMount: () => showSpinner(),
448
+ afterMount: () => hideSpinner(),
449
+ onError: (err) => showFallback(err)
450
+ }
451
+ );
397
452
  ```
398
453
 
399
454
  ### Signal callback
400
-
401
455
  ```js
402
- WebHangerFront.load(url, pid, token, 0, "[data-wh]", ({ stage, ...detail }) => {
456
+ load(url, pid, token, 0, "[data-wh]", ({ stage, time, source }) => {
403
457
  // stages: start → fetching → assets → deps → injecting → done | error
404
- console.log(stage, detail.time, detail.source);
405
458
  });
406
459
  ```
407
460
 
408
461
  ### Plugin system
409
-
410
462
  ```js
411
- WebHangerFront.use({
412
- install({ on, emit }) {
463
+ use({
464
+ install({ on }) {
413
465
  on("load", ({ time, source }) => analytics.track("load", { time, source }));
414
466
  on("error", ({ message }) => errorTracker.capture(message));
415
467
  on("metric", ({ name, value }) => dashboard.update(name, value));
468
+ on("gpu", ({ supported }) => console.log("WebGPU:", supported));
469
+ on("sw", ({ scope }) => console.log("SW:", scope));
416
470
  }
417
471
  });
418
472
  ```
419
473
 
420
474
  ### Observability
475
+ ```js
476
+ on("load", ({ time, source }) => console.log(time, source));
477
+ console.log(metrics); // { loads, cacheHits, errors, totalTime }
478
+ ```
421
479
 
480
+ ### WebGPU
422
481
  ```js
423
- WebHangerFront.on("load", ({ time, source }) => console.log(time, source));
424
- WebHangerFront.on("metric", ({ name, value }) => console.log(name, value));
482
+ console.log(gpu.supported);
483
+ on("gpu", ({ supported }) => console.log("GPU:", supported));
484
+ ```
485
+
486
+ ### Offline + Service Worker
487
+ ```js
488
+ await registerSW("./webhanger.sw.js");
425
489
 
426
- console.log(WebHangerFront.metrics);
427
- // { loads: 5, cacheHits: 3, errors: 0, totalTime: 420 }
490
+ await setOfflinePage(
491
+ "<h1>Offline</h1><p>Back soon.</p>",
492
+ "body { background: #030712; color: white; }"
493
+ );
428
494
  ```
429
495
 
430
- ### Hard flush
496
+ Offline behavior:
497
+ - Online first visit → loads from CDN, caches everything
498
+ - Online repeat visit → loads from SW cache instantly, badge shows
499
+ - Offline with cache → full page works
500
+ - Offline no cache → custom offline page with "⬡ Served by WebHanger" badge
501
+
502
+ ### Smart Cache Invalidation
503
+
504
+ Only re-fetches from CDN when components actually changed. Checks admin server on every page load — if nothing changed, loads instantly from cache.
505
+
506
+ ```js
507
+ // Instead of initialize(), use smartInitialize()
508
+ WebHangerFront.smartInitialize(
509
+ "./wh-manifest.json",
510
+ "http://localhost:5000" // wh-admin server URL
511
+ );
512
+ ```
513
+
514
+ Flow:
515
+ ```
516
+ Page loads
517
+ └── GET /api/last-updated from admin server (2s timeout)
518
+ ├── same as localStorage "wh_last_updated"
519
+ │ └── load all components from cache (instant, 0ms)
520
+ └── different (components updated since last visit)
521
+ └── clear component cache
522
+ └── reload from CDN
523
+ └── update localStorage timestamp
524
+ ```
525
+
526
+ Events:
527
+ ```js
528
+ WebHangerFront.on("cache-invalidated", ({ serverTs, cachedTs }) => {
529
+ console.log("Components updated — reloading from CDN");
530
+ });
531
+ WebHangerFront.on("cache-hit", ({ serverTs }) => {
532
+ console.log("Up to date — loaded from cache");
533
+ });
534
+ ```
535
+
536
+ If admin server is unreachable (offline, not running), falls back to normal cache behavior silently — zero errors.
537
+
538
+ Test it:
539
+ ```bash
540
+ # 1. Deploy a component
541
+ node smart-cache-test/deploy.js 1.0.0
542
+
543
+ # 2. Start admin server
544
+ node admin/server.js ./webhanger.config.json 5000
545
+
546
+ # 3. Serve the test page
547
+ npx serve smart-cache-test/site
548
+
549
+ # 4. Open http://localhost:3000 — loads from CDN, caches timestamp
550
+ # 5. Refresh — loads from cache instantly (banner: "✓ Up to date")
551
+ # 6. Redeploy with new version
552
+ node smart-cache-test/deploy.js 2.0.0
553
+ # 7. Refresh — cache invalidates, loads fresh (banner: "🔄 Components updated")
554
+ ```
431
555
 
556
+ See `smart-cache-test/` for the full working demo.
557
+
558
+ ### Hard flush
432
559
  ```js
433
- await WebHangerFront.clearCache();
434
- // Clears: localStorage, IndexedDB, SW caches, sessionStorage, unregisters SW
560
+ await clearCache();
435
561
  ```
436
562
 
437
563
  ---
@@ -442,9 +568,23 @@ await WebHangerFront.clearCache();
442
568
  |---|---|
443
569
  | `localStorage` | Components < 50KB |
444
570
  | `IndexedDB` | Components ≥ 50KB |
445
- | Service Worker | Offline fallback |
571
+ | Service Worker | Offline + navigation cache |
572
+ | `wh_last_updated` | Smart cache invalidation timestamp |
573
+
574
+ **Stale-while-revalidate** — returns cached instantly, refreshes in background.
575
+
576
+ **Smart cache invalidation** — `smartInitialize()` checks admin server on every load. Only re-fetches from CDN when components actually changed. Zero CDN requests on cache hits.
446
577
 
447
- **Stale-while-revalidate** — returns cached version instantly, refreshes in background.
578
+ ---
579
+
580
+ ## Access Control
581
+
582
+ | Role | deploy | read | delete | manage_access |
583
+ |---|---|---|---|---|
584
+ | owner | ✅ | ✅ | ✅ | ✅ |
585
+ | admin | ✅ | ✅ | ✅ | ✅ |
586
+ | deployer | ✅ | ✅ | ❌ | ❌ |
587
+ | viewer | ❌ | ✅ | ❌ | ❌ |
448
588
 
449
589
  ---
450
590
 
@@ -454,44 +594,82 @@ await WebHangerFront.clearCache();
454
594
  import { WebHanger } from "webhanger";
455
595
  const wh = new WebHanger();
456
596
 
457
- // Deploy
458
597
  const result = await wh.deploy("./components/navbar", "navbar", "1.0.0", {
459
- expiresInSeconds: 86400, // optional
460
- token: "custom-token", // optional
598
+ expiresInSeconds: 86400,
461
599
  dependencies: ["chart@1.0.0"]
462
600
  });
463
- // { cdnUrl, cdnUrls, token, expires, dependencies }
464
601
 
465
- // Resolve
466
602
  const comp = await wh.resolve("navbar", "1.0.0");
467
-
468
- // Rotate token without redeploying
469
603
  await wh.resign("navbar", "1.0.0", { expiresInSeconds: 3600 });
470
-
471
- // Delete from storage
472
604
  await wh.remove("navbar", "1.0.0");
605
+ ```
606
+
607
+ ---
473
608
 
474
- // Get config
475
- const config = wh.getConfig();
609
+ ## Next.js Integration
610
+
611
+ ```bash
612
+ npm install webhanger-front
476
613
  ```
477
614
 
478
- ### Named exports
615
+ ```tsx
616
+ "use client";
617
+ import { useEffect, useRef } from "react";
618
+ import { load } from "webhanger-front";
479
619
 
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";
620
+ export default function WebHangerComponent({ name, props = {} }: { name: string; props?: Record<string, string> }) {
621
+ const ref = useRef<HTMLDivElement>(null);
622
+
623
+ useEffect(() => {
624
+ fetch("/wh-manifest.json")
625
+ .then(r => r.json())
626
+ .then(m => {
627
+ const c = m.components[name];
628
+ if (!c || !ref.current) return;
629
+ ref.current.id = `wh-${name}`;
630
+ load(c.urls || c.url, m.pid, c.token, c.expires, `#wh-${name}`, null, [], { props });
631
+ });
632
+ }, [name]);
633
+
634
+ return <div ref={ref} />;
635
+ }
636
+ ```
637
+
638
+ ```tsx
639
+ // app/page.tsx
640
+ <WebHangerComponent name="navbar" props={{ brand: "MyApp", ctaText: "Sign Up" }} />
641
+ ```
642
+
643
+ ---
644
+
645
+ ## Examples & Tests
646
+
647
+ | Folder | What it tests |
648
+ |---|---|
649
+ | `examples/` | Full component deploy + browser SDK demo |
650
+ | `showcase/` | All 4 packages together |
651
+ | `auth-test/` | OAuth login flow (Google, GitHub) |
652
+ | `smart-cache-test/` | Smart cache invalidation demo |
653
+
654
+ ```bash
655
+ # Examples
656
+ node examples/deploy.js && npx serve examples/site
657
+
658
+ # Showcase (all packages)
659
+ node showcase/deploy.js && npx serve showcase/site
660
+
661
+ # Auth test
662
+ wh auth init && wh auth serve &
663
+ npx serve . # open /auth-test/login.html
664
+
665
+ # Smart cache test
666
+ node smart-cache-test/deploy.js 1.0.0
667
+ node admin/server.js ./webhanger.config.json 5000 &
668
+ npx serve smart-cache-test/site
493
669
  ```
494
670
 
671
+ See `examples/EXAMPLE.md` for the full step-by-step guide.
672
+
495
673
  ---
496
674
 
497
675
  ## Storage Providers
@@ -500,47 +678,16 @@ import {
500
678
  |---|---|
501
679
  | `s3` | AWS S3 — auto-provisions bucket + CloudFront |
502
680
  | `r2` | Cloudflare R2 — zero egress fees |
503
- | `minio` | Self-hosted MinIO — S3-compatible |
504
- | `local` | Local disk — dev/testing only |
681
+ | `minio` | Self-hosted MinIO |
682
+ | `local` | Local disk — dev only |
505
683
 
506
684
  ## Database Providers
507
685
 
508
686
  | Provider | Notes |
509
687
  |---|---|
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`.
688
+ | `firebase` | Firebase Firestore — free tier |
689
+ | `supabase` | Supabase Postgres |
690
+ | `mongodb` | MongoDB Atlas |
544
691
 
545
692
  ---
546
693
 
@@ -548,47 +695,41 @@ import {
548
695
 
549
696
  ```
550
697
  Developer
551
- └── wh ship ./components ./docs
698
+ └── wh ship ./components ./site
552
699
  ├── wh analyze → detect Tailwind, GSAP, deps
553
- ├── wh bundle html + css + js → single payload
554
- ├── AES-256-GCM encrypt each chunk (SHA-256 key)
700
+ ├── wh breakdown extract CSS/JS from single HTML
701
+ ├── bundle html + css + js → single payload
702
+ ├── props schema → stored in payload for runtime resolution
703
+ ├── AES-256-GCM → encrypt each chunk
555
704
  ├── SHA-256 hash → integrity fingerprint
556
705
  ├── S3 upload → store encrypted payload
557
706
  ├── HMAC sign → project-scoped signed URL
558
- ├── Firestore → register metadata + dep graph
559
- ├── wh build → minify HTML, inline assets
707
+ ├── DB register metadata + dep graph
708
+ ├── wh build → minify HTML, extract CSS/JS
560
709
  └── wh zip → deploy.zip ready for upload
561
710
 
562
711
  Browser
563
- └── <wh-component name="navbar">
712
+ └── <wh-component name="navbar" wh-brand="MyApp">
564
713
  ├── fetch wh-manifest.json
565
714
  ├── check token expiry
566
715
  ├── 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
716
+ ├── fetch from CloudFront / Edge Worker
717
+ ├── multi-CDN failover
718
+ ├── load CDN assets
719
+ ├── resolve dependency graph
572
720
  ├── AES-256-GCM decrypt in memory
573
721
  ├── SHA-256 integrity verify
722
+ ├── resolve props ({{wh.brand}} → "MyApp")
574
723
  ├── domain restriction check
575
724
  ├── inject CSS → HTML → JS (or Shadow DOM)
576
725
  ├── fire lifecycle hooks
577
- └── emit metrics + plugin events
726
+ ├── emit metrics + plugin events
727
+ ├── WebGPU detection
728
+ └── Service Worker caches for offline
578
729
  ```
579
730
 
580
731
  ---
581
732
 
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
733
  ## License
593
734
 
594
735
  ISC