@fairfox/polly 0.1.5 → 0.2.1
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/dist/cli/polly.js +75 -220
- package/dist/cli/polly.js.map +5 -4
- package/dist/cli/template-utils.js +81 -0
- package/dist/cli/template-utils.js.map +10 -0
- package/dist/vendor/verify/specs/Dockerfile +13 -0
- package/dist/vendor/verify/specs/README.md +37 -0
- package/dist/vendor/verify/specs/docker-compose.yml +9 -0
- package/dist/vendor/verify/specs/tla/MessageRouter.cfg +24 -0
- package/dist/vendor/verify/specs/tla/MessageRouter.tla +221 -0
- package/dist/vendor/verify/specs/tla/README.md +179 -0
- package/dist/vendor/verify/specs/verification.config.ts +61 -0
- package/dist/vendor/verify/src/cli.js +22 -19
- package/dist/vendor/verify/src/cli.js.map +5 -5
- package/dist/vendor/visualize/src/cli.js +379 -61
- package/dist/vendor/visualize/src/cli.js.map +11 -10
- package/package.json +2 -2
- package/templates/pwa/.gitignore.template +4 -0
- package/templates/pwa/README.md.template +144 -0
- package/templates/pwa/build.ts.template +56 -0
- package/templates/pwa/index.html.template +127 -0
- package/templates/pwa/package.json.template +19 -0
- package/templates/pwa/public/manifest.json.template +21 -0
- package/templates/pwa/server.ts.template +58 -0
- package/templates/pwa/src/main.ts.template +133 -0
- package/templates/pwa/src/service-worker.ts.template +161 -0
- package/templates/pwa/src/shared-worker.ts.template +135 -0
- package/templates/pwa/tsconfig.json.template +18 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fairfox/polly",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "Multi-execution-context framework with reactive state and cross-context messaging for Chrome extensions, PWAs, and worker-based applications",
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"types": "./dist/shared/types/messages.d.ts"
|
|
45
45
|
}
|
|
46
46
|
},
|
|
47
|
-
"files": ["dist", "README.md", "LICENSE"],
|
|
47
|
+
"files": ["dist", "templates", "README.md", "LICENSE"],
|
|
48
48
|
"scripts": {
|
|
49
49
|
"dev": "bun run build.ts --watch",
|
|
50
50
|
"build": "bun run build.ts",
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
# {{PROJECT_NAME}}
|
|
2
|
+
|
|
3
|
+
A Progressive Web App (PWA) built with Polly framework, demonstrating multi-context architecture with:
|
|
4
|
+
|
|
5
|
+
- **Main Context** - UI application
|
|
6
|
+
- **Service Worker** - Background tasks, caching, push notifications
|
|
7
|
+
- **Shared Worker** - Shared state across tabs/windows
|
|
8
|
+
|
|
9
|
+
## Architecture
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
┌─────────────────┐
|
|
13
|
+
│ Main Context │ ← User Interface
|
|
14
|
+
│ (main.ts) │
|
|
15
|
+
└────────┬────────┘
|
|
16
|
+
│
|
|
17
|
+
┌────┴────┐
|
|
18
|
+
│ │
|
|
19
|
+
▼ ▼
|
|
20
|
+
┌─────────┐ ┌──────────────┐
|
|
21
|
+
│ Service │ │ Shared │
|
|
22
|
+
│ Worker │ │ Worker │
|
|
23
|
+
│ │ │ │
|
|
24
|
+
│ Caching │ │ Shared State │
|
|
25
|
+
│ Offline │ │ Multi-tab │
|
|
26
|
+
└─────────┘ └──────────────┘
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Getting Started
|
|
30
|
+
|
|
31
|
+
1. **Install dependencies:**
|
|
32
|
+
```bash
|
|
33
|
+
bun install
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
2. **Start development server:**
|
|
37
|
+
```bash
|
|
38
|
+
bun run serve
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
3. **Open in browser:**
|
|
42
|
+
```
|
|
43
|
+
http://localhost:3000
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
4. **Try the features:**
|
|
47
|
+
- Click "Ping Service Worker" - tests communication with SW
|
|
48
|
+
- Click "Ping Shared Worker" - tests communication with shared worker
|
|
49
|
+
- Click "Broadcast" - sends message to all workers
|
|
50
|
+
- Open multiple tabs to see shared worker coordination
|
|
51
|
+
|
|
52
|
+
## Development
|
|
53
|
+
|
|
54
|
+
- `bun run serve` - Start development server with hot reload
|
|
55
|
+
- `bun run build` - Build for production
|
|
56
|
+
- `bun run typecheck` - Type check TypeScript
|
|
57
|
+
- `bun run visualize` - Generate architecture diagrams
|
|
58
|
+
|
|
59
|
+
## Project Structure
|
|
60
|
+
|
|
61
|
+
```
|
|
62
|
+
{{PROJECT_NAME}}/
|
|
63
|
+
├── src/
|
|
64
|
+
│ ├── main.ts # Main application context
|
|
65
|
+
│ ├── service-worker.ts # Service Worker (caching, offline)
|
|
66
|
+
│ └── shared-worker.ts # Shared Worker (cross-tab state)
|
|
67
|
+
├── public/
|
|
68
|
+
│ └── manifest.json # PWA manifest
|
|
69
|
+
├── index.html # Entry point
|
|
70
|
+
├── server.ts # Dev server
|
|
71
|
+
├── build.ts # Build script
|
|
72
|
+
├── package.json
|
|
73
|
+
└── tsconfig.json
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Features Demonstrated
|
|
77
|
+
|
|
78
|
+
### Service Worker
|
|
79
|
+
- Asset caching for offline support
|
|
80
|
+
- Background fetch and sync
|
|
81
|
+
- Push notifications (requires HTTPS)
|
|
82
|
+
- Message passing with main context
|
|
83
|
+
|
|
84
|
+
### Shared Worker
|
|
85
|
+
- State shared across multiple tabs
|
|
86
|
+
- Broadcast messages to all connected tabs
|
|
87
|
+
- Connection tracking
|
|
88
|
+
- Periodic heartbeat
|
|
89
|
+
|
|
90
|
+
### Main Context
|
|
91
|
+
- Service Worker registration
|
|
92
|
+
- Shared Worker connection
|
|
93
|
+
- Bidirectional communication
|
|
94
|
+
- UI updates from worker messages
|
|
95
|
+
|
|
96
|
+
## Visualization
|
|
97
|
+
|
|
98
|
+
Generate architecture diagrams:
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
bun run visualize
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
This will create:
|
|
105
|
+
- System context diagram
|
|
106
|
+
- Container diagram showing all 3 contexts
|
|
107
|
+
- Message flow diagrams
|
|
108
|
+
|
|
109
|
+
View diagrams:
|
|
110
|
+
```bash
|
|
111
|
+
bun run visualize --serve
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## Testing
|
|
115
|
+
|
|
116
|
+
Open multiple browser tabs to test:
|
|
117
|
+
1. Shared worker coordination across tabs
|
|
118
|
+
2. Service worker message broadcasting
|
|
119
|
+
3. State synchronization
|
|
120
|
+
|
|
121
|
+
## Production Build
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
bun run build
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Output in `dist/` directory. Serve with any static file server or deploy to:
|
|
128
|
+
- Netlify
|
|
129
|
+
- Vercel
|
|
130
|
+
- GitHub Pages
|
|
131
|
+
- Cloudflare Pages
|
|
132
|
+
|
|
133
|
+
## Notes
|
|
134
|
+
|
|
135
|
+
- Service Worker requires HTTPS in production (or localhost for dev)
|
|
136
|
+
- Shared Worker support varies by browser (Chrome, Firefox, Edge)
|
|
137
|
+
- Check browser DevTools → Application tab to debug workers
|
|
138
|
+
|
|
139
|
+
## Learn More
|
|
140
|
+
|
|
141
|
+
- [Polly Documentation](https://github.com/fairfox/polly)
|
|
142
|
+
- [Service Worker API](https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API)
|
|
143
|
+
- [Shared Worker API](https://developer.mozilla.org/en-US/docs/Web/API/SharedWorker)
|
|
144
|
+
- [PWA Documentation](https://web.dev/progressive-web-apps/)
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build script for PWA
|
|
3
|
+
* Uses Bun's built-in bundler
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { rmSync, cpSync, mkdirSync } from 'node:fs'
|
|
7
|
+
import { join } from 'node:path'
|
|
8
|
+
|
|
9
|
+
const distDir = './dist'
|
|
10
|
+
|
|
11
|
+
// Clean dist directory
|
|
12
|
+
console.log('🧹 Cleaning dist directory...')
|
|
13
|
+
rmSync(distDir, { recursive: true, force: true })
|
|
14
|
+
mkdirSync(distDir, { recursive: true })
|
|
15
|
+
|
|
16
|
+
// Copy static files
|
|
17
|
+
console.log('📋 Copying static files...')
|
|
18
|
+
cpSync('./public', distDir, { recursive: true, force: true })
|
|
19
|
+
cpSync('./index.html', join(distDir, 'index.html'))
|
|
20
|
+
|
|
21
|
+
// Build main application
|
|
22
|
+
console.log('📦 Building main application...')
|
|
23
|
+
await Bun.build({
|
|
24
|
+
entrypoints: ['./src/main.ts'],
|
|
25
|
+
outdir: join(distDir, 'src'),
|
|
26
|
+
format: 'esm',
|
|
27
|
+
minify: true,
|
|
28
|
+
sourcemap: 'external',
|
|
29
|
+
target: 'browser',
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
// Build service worker
|
|
33
|
+
console.log('⚙️ Building service worker...')
|
|
34
|
+
await Bun.build({
|
|
35
|
+
entrypoints: ['./src/service-worker.ts'],
|
|
36
|
+
outdir: join(distDir, 'src'),
|
|
37
|
+
format: 'esm',
|
|
38
|
+
minify: true,
|
|
39
|
+
sourcemap: 'external',
|
|
40
|
+
target: 'browser',
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
// Build shared worker
|
|
44
|
+
console.log('👥 Building shared worker...')
|
|
45
|
+
await Bun.build({
|
|
46
|
+
entrypoints: ['./src/shared-worker.ts'],
|
|
47
|
+
outdir: join(distDir, 'src'),
|
|
48
|
+
format: 'esm',
|
|
49
|
+
minify: true,
|
|
50
|
+
sourcemap: 'external',
|
|
51
|
+
target: 'browser',
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
console.log('✅ Build complete!')
|
|
55
|
+
console.log(`\n📦 Output: ${distDir}/`)
|
|
56
|
+
console.log('\n💡 To serve: bun run serve')
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<meta name="description" content="{{PROJECT_NAME}} - A PWA with Polly" />
|
|
7
|
+
<link rel="manifest" href="/manifest.json" />
|
|
8
|
+
<title>{{PROJECT_NAME}}</title>
|
|
9
|
+
<style>
|
|
10
|
+
* {
|
|
11
|
+
box-sizing: border-box;
|
|
12
|
+
margin: 0;
|
|
13
|
+
padding: 0;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
body {
|
|
17
|
+
font-family: system-ui, -apple-system, sans-serif;
|
|
18
|
+
padding: 20px;
|
|
19
|
+
max-width: 800px;
|
|
20
|
+
margin: 0 auto;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
h1 {
|
|
24
|
+
margin-bottom: 20px;
|
|
25
|
+
color: #333;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.status {
|
|
29
|
+
margin-bottom: 20px;
|
|
30
|
+
padding: 15px;
|
|
31
|
+
background: #f5f5f5;
|
|
32
|
+
border-radius: 8px;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.status-item {
|
|
36
|
+
display: flex;
|
|
37
|
+
justify-content: space-between;
|
|
38
|
+
margin-bottom: 8px;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.status-label {
|
|
42
|
+
font-weight: 600;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.status-value {
|
|
46
|
+
color: #666;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.actions {
|
|
50
|
+
display: flex;
|
|
51
|
+
gap: 10px;
|
|
52
|
+
margin-bottom: 20px;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
button {
|
|
56
|
+
padding: 10px 20px;
|
|
57
|
+
background: #007bff;
|
|
58
|
+
color: white;
|
|
59
|
+
border: none;
|
|
60
|
+
border-radius: 5px;
|
|
61
|
+
cursor: pointer;
|
|
62
|
+
font-size: 14px;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
button:hover {
|
|
66
|
+
background: #0056b3;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
button:disabled {
|
|
70
|
+
background: #ccc;
|
|
71
|
+
cursor: not-allowed;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
#messages {
|
|
75
|
+
border: 1px solid #ddd;
|
|
76
|
+
border-radius: 8px;
|
|
77
|
+
padding: 15px;
|
|
78
|
+
min-height: 200px;
|
|
79
|
+
background: white;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.message {
|
|
83
|
+
padding: 8px;
|
|
84
|
+
margin-bottom: 8px;
|
|
85
|
+
background: #f9f9f9;
|
|
86
|
+
border-left: 3px solid #007bff;
|
|
87
|
+
border-radius: 4px;
|
|
88
|
+
font-size: 14px;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
.message-time {
|
|
92
|
+
color: #666;
|
|
93
|
+
font-size: 12px;
|
|
94
|
+
margin-right: 8px;
|
|
95
|
+
}
|
|
96
|
+
</style>
|
|
97
|
+
</head>
|
|
98
|
+
<body>
|
|
99
|
+
<h1>{{PROJECT_NAME}}</h1>
|
|
100
|
+
|
|
101
|
+
<div class="status">
|
|
102
|
+
<div class="status-item">
|
|
103
|
+
<span class="status-label">Service Worker:</span>
|
|
104
|
+
<span id="sw-status" class="status-value">Checking...</span>
|
|
105
|
+
</div>
|
|
106
|
+
<div class="status-item">
|
|
107
|
+
<span class="status-label">Shared Worker:</span>
|
|
108
|
+
<span id="shared-worker-status" class="status-value">Checking...</span>
|
|
109
|
+
</div>
|
|
110
|
+
<div class="status-item">
|
|
111
|
+
<span class="status-label">Messages Received:</span>
|
|
112
|
+
<span id="message-count" class="status-value">0</span>
|
|
113
|
+
</div>
|
|
114
|
+
</div>
|
|
115
|
+
|
|
116
|
+
<div class="actions">
|
|
117
|
+
<button id="ping-sw">Ping Service Worker</button>
|
|
118
|
+
<button id="ping-shared">Ping Shared Worker</button>
|
|
119
|
+
<button id="broadcast">Broadcast Message</button>
|
|
120
|
+
</div>
|
|
121
|
+
|
|
122
|
+
<h2>Messages</h2>
|
|
123
|
+
<div id="messages"></div>
|
|
124
|
+
|
|
125
|
+
<script type="module" src="/src/main.ts"></script>
|
|
126
|
+
</body>
|
|
127
|
+
</html>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "{{PROJECT_NAME}}",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"scripts": {
|
|
6
|
+
"dev": "bun run --hot src/main.ts",
|
|
7
|
+
"build": "bun build.ts",
|
|
8
|
+
"serve": "bun --hot server.ts",
|
|
9
|
+
"typecheck": "bun tsc --noEmit",
|
|
10
|
+
"visualize": "bun ../../visualize/src/cli.ts"
|
|
11
|
+
},
|
|
12
|
+
"dependencies": {
|
|
13
|
+
"@fairfox/polly": "*"
|
|
14
|
+
},
|
|
15
|
+
"devDependencies": {
|
|
16
|
+
"typescript": "^5.0.0",
|
|
17
|
+
"@types/bun": "latest"
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "{{PROJECT_NAME}}",
|
|
3
|
+
"short_name": "{{PROJECT_NAME}}",
|
|
4
|
+
"description": "A PWA built with Polly framework",
|
|
5
|
+
"start_url": "/",
|
|
6
|
+
"display": "standalone",
|
|
7
|
+
"background_color": "#ffffff",
|
|
8
|
+
"theme_color": "#000000",
|
|
9
|
+
"icons": [
|
|
10
|
+
{
|
|
11
|
+
"src": "/icon-192.png",
|
|
12
|
+
"sizes": "192x192",
|
|
13
|
+
"type": "image/png"
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"src": "/icon-512.png",
|
|
17
|
+
"sizes": "512x512",
|
|
18
|
+
"type": "image/png"
|
|
19
|
+
}
|
|
20
|
+
]
|
|
21
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Development server for PWA
|
|
3
|
+
* Serves the application with hot reload support
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const port = 3000
|
|
7
|
+
|
|
8
|
+
const server = Bun.serve({
|
|
9
|
+
port,
|
|
10
|
+
async fetch(req) {
|
|
11
|
+
const url = new URL(req.url)
|
|
12
|
+
let filePath = url.pathname
|
|
13
|
+
|
|
14
|
+
// Serve root
|
|
15
|
+
if (filePath === '/') {
|
|
16
|
+
filePath = '/index.html'
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Try to serve from public directory first
|
|
20
|
+
const publicFile = Bun.file(`./public${filePath}`)
|
|
21
|
+
if (await publicFile.exists()) {
|
|
22
|
+
return new Response(publicFile)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Try to serve from root
|
|
26
|
+
const rootFile = Bun.file(`.${filePath}`)
|
|
27
|
+
if (await rootFile.exists()) {
|
|
28
|
+
return new Response(rootFile)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Handle TypeScript files - transpile on the fly
|
|
32
|
+
if (filePath.endsWith('.ts')) {
|
|
33
|
+
const tsFile = Bun.file(`.${filePath}`)
|
|
34
|
+
if (await tsFile.exists()) {
|
|
35
|
+
const transpiled = await Bun.build({
|
|
36
|
+
entrypoints: [`.${filePath}`],
|
|
37
|
+
format: 'esm',
|
|
38
|
+
target: 'browser',
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
if (transpiled.outputs.length > 0) {
|
|
42
|
+
return new Response(transpiled.outputs[0], {
|
|
43
|
+
headers: {
|
|
44
|
+
'Content-Type': 'application/javascript',
|
|
45
|
+
},
|
|
46
|
+
})
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// 404
|
|
52
|
+
return new Response('Not Found', { status: 404 })
|
|
53
|
+
},
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
console.log(`🚀 Server running at http://localhost:${port}`)
|
|
57
|
+
console.log(`\n📱 PWA: {{PROJECT_NAME}}`)
|
|
58
|
+
console.log(`\n💡 Open in browser and check the console for worker messages`)
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Main Application Context
|
|
3
|
+
* Coordinates between Service Worker and Shared Worker
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// Register Service Worker
|
|
7
|
+
if ('serviceWorker' in navigator) {
|
|
8
|
+
window.addEventListener('load', async () => {
|
|
9
|
+
try {
|
|
10
|
+
const registration = await navigator.serviceWorker.register('/src/service-worker.ts', {
|
|
11
|
+
type: 'module'
|
|
12
|
+
})
|
|
13
|
+
console.log('Service Worker registered:', registration.scope)
|
|
14
|
+
updateStatus('sw-status', '✓ Active')
|
|
15
|
+
} catch (error) {
|
|
16
|
+
console.error('Service Worker registration failed:', error)
|
|
17
|
+
updateStatus('sw-status', '✗ Failed')
|
|
18
|
+
}
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
// Listen for messages from Service Worker
|
|
22
|
+
navigator.serviceWorker.addEventListener('message', (event) => {
|
|
23
|
+
console.log('Message from Service Worker:', event.data)
|
|
24
|
+
addMessage(`[SW] ${JSON.stringify(event.data)}`)
|
|
25
|
+
})
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Initialize Shared Worker
|
|
29
|
+
let sharedWorker: SharedWorker | null = null
|
|
30
|
+
let messageCount = 0
|
|
31
|
+
|
|
32
|
+
if (typeof SharedWorker !== 'undefined') {
|
|
33
|
+
sharedWorker = new SharedWorker(
|
|
34
|
+
new URL('./shared-worker.ts', import.meta.url),
|
|
35
|
+
{ type: 'module', name: 'polly-shared-worker' }
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
sharedWorker.port.start()
|
|
39
|
+
|
|
40
|
+
// Listen for messages from Shared Worker
|
|
41
|
+
sharedWorker.port.addEventListener('message', (event) => {
|
|
42
|
+
console.log('Message from Shared Worker:', event.data)
|
|
43
|
+
addMessage(`[Shared] ${JSON.stringify(event.data)}`)
|
|
44
|
+
messageCount++
|
|
45
|
+
updateMessageCount()
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
updateStatus('shared-worker-status', '✓ Connected')
|
|
49
|
+
} else {
|
|
50
|
+
updateStatus('shared-worker-status', '✗ Not Supported')
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// UI Event Handlers
|
|
54
|
+
document.getElementById('ping-sw')?.addEventListener('click', async () => {
|
|
55
|
+
if (navigator.serviceWorker.controller) {
|
|
56
|
+
navigator.serviceWorker.controller.postMessage({
|
|
57
|
+
type: 'PING',
|
|
58
|
+
timestamp: Date.now()
|
|
59
|
+
})
|
|
60
|
+
addMessage('[Main] Sent PING to Service Worker')
|
|
61
|
+
} else {
|
|
62
|
+
addMessage('[Main] Service Worker not active')
|
|
63
|
+
}
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
document.getElementById('ping-shared')?.addEventListener('click', () => {
|
|
67
|
+
if (sharedWorker) {
|
|
68
|
+
sharedWorker.port.postMessage({
|
|
69
|
+
type: 'PING',
|
|
70
|
+
timestamp: Date.now()
|
|
71
|
+
})
|
|
72
|
+
addMessage('[Main] Sent PING to Shared Worker')
|
|
73
|
+
} else {
|
|
74
|
+
addMessage('[Main] Shared Worker not available')
|
|
75
|
+
}
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
document.getElementById('broadcast')?.addEventListener('click', () => {
|
|
79
|
+
const message = {
|
|
80
|
+
type: 'BROADCAST',
|
|
81
|
+
data: 'Hello from main context',
|
|
82
|
+
timestamp: Date.now()
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Send to both workers
|
|
86
|
+
if (navigator.serviceWorker.controller) {
|
|
87
|
+
navigator.serviceWorker.controller.postMessage(message)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (sharedWorker) {
|
|
91
|
+
sharedWorker.port.postMessage(message)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
addMessage('[Main] Broadcast sent to all workers')
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
// UI Helper Functions
|
|
98
|
+
function updateStatus(elementId: string, status: string) {
|
|
99
|
+
const element = document.getElementById(elementId)
|
|
100
|
+
if (element) {
|
|
101
|
+
element.textContent = status
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function updateMessageCount() {
|
|
106
|
+
const element = document.getElementById('message-count')
|
|
107
|
+
if (element) {
|
|
108
|
+
element.textContent = messageCount.toString()
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function addMessage(text: string) {
|
|
113
|
+
const messagesDiv = document.getElementById('messages')
|
|
114
|
+
if (!messagesDiv) return
|
|
115
|
+
|
|
116
|
+
const messageDiv = document.createElement('div')
|
|
117
|
+
messageDiv.className = 'message'
|
|
118
|
+
|
|
119
|
+
const time = new Date().toLocaleTimeString()
|
|
120
|
+
messageDiv.innerHTML = `<span class="message-time">${time}</span>${text}`
|
|
121
|
+
|
|
122
|
+
messagesDiv.insertBefore(messageDiv, messagesDiv.firstChild)
|
|
123
|
+
|
|
124
|
+
// Keep only last 20 messages
|
|
125
|
+
while (messagesDiv.children.length > 20) {
|
|
126
|
+
messagesDiv.removeChild(messagesDiv.lastChild!)
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Initial message
|
|
131
|
+
addMessage('[Main] Application initialized')
|
|
132
|
+
|
|
133
|
+
console.log('{{PROJECT_NAME}} initialized')
|