zastro-websockets-node 0.0.0-729d313 → 0.0.0-ca05a2f

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.
Files changed (2) hide show
  1. package/README.md +745 -0
  2. package/package.json +1 -1
package/README.md ADDED
@@ -0,0 +1,745 @@
1
+ # zastro-websockets 🔌
2
+
3
+ Universal WebSocket support for Astro SSR Apps with **pre-patched adapters** - no manual patching required!
4
+
5
+ ## Overview
6
+
7
+ ZAstroWebsockets is a monorepo containing WebSocket-enabled Astro adapters for Node.js and Cloudflare Workers. Each adapter is distributed as a separate package with WebSocket support built-in, eliminating the need for manual patching.
8
+
9
+ ## Features
10
+
11
+ - ✅ **Pre-patched adapters**: Ships with WebSocket-enabled versions of Astro adapters
12
+ - ✅ **Multi-runtime support**: Node.js and Cloudflare Workers
13
+ - ✅ **Works with current Astro versions** (v4 & v5)
14
+ - ✅ **Unified API**: Same WebSocket API across all runtimes
15
+ - ✅ **TypeScript support**: Full type safety and IntelliSense
16
+ - ✅ **Drop-in replacement**: Simply replace your adapter import
17
+ - ✅ **Monorepo structure**: Separate packages for each runtime
18
+ - ✅ **Auto-versioning**: Synced with upstream Astro submodule
19
+
20
+ ## Installation
21
+
22
+ Install the specific adapter package you need:
23
+
24
+ ### For Node.js
25
+
26
+ ```bash
27
+ npm install zastro-websockets-node
28
+ ```
29
+
30
+ ### For Cloudflare Workers
31
+
32
+ ```bash
33
+ npm install zastro-websockets-cloudflare
34
+ ```
35
+
36
+ ## Usage
37
+
38
+ ### Node.js Adapter
39
+
40
+ Replace your existing `@astrojs/node` import with the WebSocket-enabled version:
41
+
42
+ ```js
43
+ // astro.config.mjs
44
+ import { defineConfig } from "astro/config"
45
+ import node from "zastro-websockets-node"
46
+
47
+ export default defineConfig({
48
+ output: "server",
49
+ adapter: node({ mode: "standalone" }),
50
+ });
51
+ ```
52
+
53
+ ### Cloudflare Adapter
54
+
55
+ Replace your existing `@astrojs/cloudflare` import with the WebSocket-enabled version:
56
+
57
+ ```js
58
+ // astro.config.mjs
59
+ import { defineConfig } from "astro/config"
60
+ import cloudflare from "zastro-websockets-cloudflare"
61
+
62
+ export default defineConfig({
63
+ output: "server",
64
+ adapter: cloudflare(),
65
+ });
66
+ ```
67
+
68
+
69
+ ## WebSocket API
70
+
71
+ The WebSocket API is consistent across all runtimes:
72
+
73
+ ### API Routes
74
+
75
+ ```ts
76
+ // src/pages/api/ws.ts
77
+ import type { APIRoute } from "astro"
78
+
79
+ export const GET: APIRoute = (ctx) => {
80
+ // Check if this is a WebSocket upgrade request
81
+ if (ctx.locals.isUpgradeRequest) {
82
+ // Upgrade the connection to a WebSocket
83
+ const { response, socket } = ctx.locals.upgradeWebSocket()
84
+
85
+ // Set up your WebSocket handlers
86
+ socket.onopen = () => {
87
+ console.log("WebSocket connection opened")
88
+ }
89
+
90
+ socket.onmessage = (event) => {
91
+ console.log("Received:", event.data)
92
+ if (event.data === "ping") {
93
+ socket.send("pong")
94
+ }
95
+ }
96
+
97
+ socket.onclose = () => {
98
+ console.log("WebSocket connection closed")
99
+ }
100
+
101
+ socket.onerror = (error) => {
102
+ console.error("WebSocket error:", error)
103
+ }
104
+
105
+ // Return the upgrade response
106
+ return response
107
+ }
108
+
109
+ // Return error for non-WebSocket requests
110
+ return new Response("Upgrade required", { status: 426 })
111
+ }
112
+ ```
113
+
114
+ ### Client-side JavaScript
115
+
116
+ ```html
117
+ <!-- src/pages/index.astro -->
118
+ <script>
119
+ const ws = new WebSocket("ws://localhost:4321/api/ws")
120
+
121
+ ws.onopen = () => {
122
+ console.log("Connected to WebSocket")
123
+ ws.send("Hello, Server!")
124
+ }
125
+
126
+ ws.onmessage = (event) => {
127
+ console.log("Received:", event.data)
128
+ }
129
+
130
+ ws.onclose = () => {
131
+ console.log("Disconnected from WebSocket")
132
+ }
133
+ </script>
134
+ ```
135
+
136
+ ## Project Structure
137
+
138
+ This is a monorepo containing:
139
+
140
+ ```
141
+ zastro-websockets/
142
+ ├── packages/
143
+ │ ├── node/ # zastro-websockets-node package
144
+ │ └── cloudflare/ # zastro-websockets-cloudflare package
145
+ ├── tests/projects/ # Test projects for both adapters
146
+ ├── patches/ # Patch files for upstream Astro adapters
147
+ └── scripts/ # Build and sync scripts
148
+ ```
149
+
150
+ ### Available Packages
151
+
152
+ - **`zastro-websockets-node`** - WebSocket-enabled Node.js adapter
153
+ - **`zastro-websockets-cloudflare`** - WebSocket-enabled Cloudflare Workers adapter
154
+
155
+ ## TypeScript Support
156
+
157
+ Both packages provide full TypeScript support with proper type definitions:
158
+
159
+ ```ts
160
+ // Types are automatically available in your Astro.locals
161
+ declare global {
162
+ namespace App {
163
+ interface Locals {
164
+ isUpgradeRequest: boolean
165
+ upgradeWebSocket(): { socket: WebSocket, response: Response }
166
+ runtime?: any // Runtime-specific context (Cloudflare only)
167
+ }
168
+ }
169
+ }
170
+ ```
171
+
172
+ ## How the Dynamic Build System Works
173
+
174
+ ### Modern Code-Transformation Approach
175
+
176
+ Instead of maintaining fragile patch files, **ZAstroWebsockets uses a modern dynamic build system** that applies WebSocket modifications as **code transformations** directly to the upstream Astro source code.
177
+
178
+ ### Node.js Adapter Architecture
179
+
180
+ The Node.js adapter build system performs **live code modifications** to integrate WebSocket support:
181
+
182
+ #### Build Process Steps
183
+
184
+ 1. **Copy Upstream Source**
185
+ - Copies fresh source from `astro-upstream/packages/integrations/node/`
186
+ - Maintains sync with latest Astro releases
187
+
188
+ 2. **Apply Code Transformations**
189
+ - **Modifies `serve-app.ts`**: Adds WebSocket upgrade handling
190
+ - **Modifies `standalone.ts`**: Integrates WebSocket server with HTTP server
191
+ - **Modifies `types.ts`**: Exports WebSocket types
192
+ - **Modifies `index.ts`**: Updates package name references
193
+ - **Creates WebSocket Files**: Complete WebSocket implementation
194
+
195
+ 3. **WebSocket Server Integration**
196
+ ```typescript
197
+ // Dynamically injected into serve-app.ts
198
+ const wsServer = new ws.WebSocketServer({ noServer: true })
199
+
200
+ // Handles HTTP upgrade requests
201
+ httpServer.on('upgrade', (req, socket, head) => {
202
+ wsServer.handleUpgrade(req, socket, head, (ws) => {
203
+ attach(websocket, ws, metadata)
204
+ })
205
+ })
206
+ ```
207
+
208
+ 4. **Generated WebSocket Files**
209
+ - **`websocket/index.ts`**: Main WebSocket wrapper with browser-compatible API
210
+ - **`websocket/stats.ts`**: Connection tracking and statistics
211
+ - **`websocket/connection-manager.ts`**: Advanced connection lifecycle management
212
+ - **`websocket/attach.ts`**: WebSocket attachment utilities
213
+ - **`websocket/dev-middleware.ts`**: Development server integration
214
+ - **`websocket/serve-websocket.ts`**: Production server integration
215
+
216
+ 5. **Package Configuration**
217
+ - **Adds Dependencies**: `ws@^8.18.0` and `@types/ws@^8.5.12`
218
+ - **Adds Exports**: `./websocket` and `./stats` exports
219
+ - **Resolves Workspace Deps**: Converts `@astrojs/internal-helpers@workspace:*` to actual versions
220
+
221
+ ### Cloudflare Adapter Architecture
222
+
223
+ The Cloudflare adapter uses the same **dynamic transformation approach**:
224
+
225
+ #### Build Process Steps
226
+
227
+ 1. **Copy Upstream Source**
228
+ - Copies fresh source from `astro-upstream/packages/integrations/cloudflare/`
229
+ - Maintains compatibility with Cloudflare Workers runtime
230
+
231
+ 2. **Apply Code Transformations**
232
+ - **Modifies `index.ts`**: Updates package name references
233
+ - **Creates WebSocket Files**: Cloudflare Workers-specific implementation
234
+
235
+ 3. **WebSocket Implementation**
236
+ ```typescript
237
+ // Dynamically generated in websocket/index.ts
238
+ export function upgradeWebSocket(): WebSocketUpgrade {
239
+ // Uses Cloudflare's WebSocketPair API
240
+ const { 0: client, 1: server } = new WebSocketPair()
241
+
242
+ // Create upgrade response
243
+ const response = createWebSocketResponse(client)
244
+
245
+ // Return wrapped server socket
246
+ return { socket: new CloudflareWebSocket(server), response }
247
+ }
248
+ ```
249
+
250
+ 4. **Generated WebSocket Files**
251
+ - **`websocket/index.ts`**: Main WebSocket implementation for Cloudflare Workers
252
+ - **`websocket/response.ts`**: HTTP upgrade response handling
253
+ - **`websocket/websocket.ts`**: WebSocket wrapper classes
254
+ - **`websocket/dev-middleware.ts`**: Development server simulation
255
+
256
+ 5. **WebSocket Wrapper Classes**
257
+ - **`CloudflareWebSocket`**: Production wrapper for native WebSocket
258
+ - **`DevWebSocket`**: Development simulation with same API
259
+ - **`UpgradeResponse`**: Proper HTTP 101 upgrade response
260
+
261
+ ### Build System Architecture
262
+
263
+ The build system uses **3 main scripts**:
264
+
265
+ ```typescript
266
+ // scripts/dynamic-build.ts - Main orchestrator
267
+ export function buildAll() {
268
+ // 1. Clean and prepare upstream
269
+ // 2. Install upstream dependencies
270
+ // 3. Build Node.js adapter
271
+ // 4. Build Cloudflare adapter
272
+ // 5. Copy README.md to packages
273
+ }
274
+
275
+ // scripts/dynamic-build-node.ts - Node.js specific
276
+ export function buildNodeAdapter() {
277
+ // 1. Copy upstream source
278
+ // 2. Apply all WebSocket modifications
279
+ // 3. Update package.json and resolve dependencies
280
+ // 4. Build TypeScript locally
281
+ }
282
+
283
+ // scripts/dynamic-build-cloudflare.ts - Cloudflare specific
284
+ export function buildCloudflareAdapter() {
285
+ // 1. Copy upstream source
286
+ // 2. Apply WebSocket modifications
287
+ // 3. Update package.json
288
+ // 4. Build TypeScript locally
289
+ }
290
+ ```
291
+
292
+ ### Automatic Version Management
293
+
294
+ The build system uses **adapter-specific versioning**:
295
+
296
+ ```javascript
297
+ // GitHub Actions workflow
298
+ const nodeAdapterVersion = require('./packages/integrations/node/package.json').version
299
+ const cloudflareAdapterVersion = require('./packages/integrations/cloudflare/package.json').version
300
+ const ourCommit = process.env.GITHUB_SHA.substring(0, 7)
301
+
302
+ // Version format: 8.3.4-abc1234 (adapter version + our commit)
303
+ const finalVersion = `${nodeAdapterVersion}-${ourCommit}`
304
+ ```
305
+
306
+ ### Key Advantages of Dynamic Build System
307
+
308
+ | Aspect | Old Patch System | New Dynamic System |
309
+ |--------|------------------|-------------------|
310
+ | **Reliability** | Patches break on updates | Code transformations adapt |
311
+ | **Maintenance** | Manual patch updates | Automated code generation |
312
+ | **Debugging** | Patch application errors | Clear TypeScript errors |
313
+ | **Dependency Management** | Workspace conflicts | Isolated local dependencies |
314
+ | **Build Speed** | Slow patch application | Fast code transformations |
315
+ | **Flexibility** | Limited to patch changes | Full code control |
316
+
317
+
318
+ ## Migration from Official Adapters
319
+
320
+ ### From @astrojs/node
321
+
322
+ ```diff
323
+ - import node from "@astrojs/node"
324
+ + import node from "zastro-websockets-node"
325
+ ```
326
+
327
+ ### From @astrojs/cloudflare
328
+
329
+ ```diff
330
+ - import cloudflare from "@astrojs/cloudflare"
331
+ + import cloudflare from "zastro-websockets-cloudflare"
332
+ ```
333
+
334
+ That's it! No other changes needed.
335
+
336
+ ## Comparison with Manual Patching
337
+
338
+ | Feature | Manual Patching | zastro-websockets |
339
+ |---------|----------------|-------------------|
340
+ | Setup complexity | High (manual patch application) | Low (just change import) |
341
+ | Reliability | Depends on patch compatibility | High (pre-tested combinations) |
342
+ | Updates | Manual re-patching required | Automatic with package updates |
343
+ | Maintenance | User responsibility | Package maintainer responsibility |
344
+ | Risk | Patches might break on updates | Version-locked compatibility |
345
+ | Connection Management | Manual implementation | Built-in tracking and cleanup |
346
+
347
+ ## Available Adapters
348
+
349
+ - ✅ **Node.js** (`zastro-websockets-node`) - Production ready
350
+ - ✅ **Cloudflare Workers** (`zastro-websockets-cloudflare`) - Production ready
351
+
352
+ > **Note**: The Deno adapter is no longer supported as it has been moved to the Deno organization. Please use the [official Deno adapter](https://github.com/denoland/deno-astro-adapter) for Deno deployments.
353
+
354
+ ## Troubleshooting
355
+
356
+ ### WebSocket connection fails
357
+ - Ensure you're using the correct adapter package (`zastro-websockets-node` or `zastro-websockets-cloudflare`)
358
+ - Check that your hosting environment supports WebSockets
359
+ - Verify the WebSocket URL matches your deployment
360
+
361
+ ### TypeScript errors
362
+ - Make sure to import the adapter from the correct package name
363
+ - Update your `tsconfig.json` to include the package types
364
+ - Restart your TypeScript server after installation
365
+
366
+ ### Development vs Production
367
+ - The adapters work in both development and production
368
+ - Additional logging is available in development mode
369
+ - Make sure to set `output: "server"` in your Astro config
370
+
371
+ ### Package Issues
372
+ - If you're upgrading from the old single package, uninstall `zastro-websockets` first
373
+ - Install the specific adapter package you need (`zastro-websockets-node` or `zastro-websockets-cloudflare`)
374
+ - Update your import statements to use the new package names
375
+
376
+ ## Advanced Configuration
377
+
378
+ ### Runtime-Specific Features
379
+
380
+ #### Cloudflare Workers
381
+
382
+ When using the Cloudflare adapter, you get access to the runtime context:
383
+
384
+ ```ts
385
+ export const GET: APIRoute = (ctx) => {
386
+ if (ctx.locals.isUpgradeRequest) {
387
+ const { response, socket } = ctx.locals.upgradeWebSocket()
388
+
389
+ // Access Cloudflare runtime features
390
+ const env = ctx.locals.runtime?.env
391
+ const cf = ctx.locals.runtime?.cf
392
+
393
+ // Use Cloudflare-specific features
394
+ const kv = env.MY_KV_NAMESPACE
395
+ const country = cf.country
396
+
397
+ return response
398
+ }
399
+
400
+ return new Response("Upgrade required", { status: 426 })
401
+ }
402
+ ```
403
+
404
+ #### Node.js
405
+
406
+ The Node.js adapter provides standard WebSocket functionality with full Node.js compatibility.
407
+
408
+ ### Advanced Connection Management (Node.js)
409
+
410
+ The Node.js adapter includes a powerful ConnectionManager for production-grade WebSocket applications:
411
+
412
+ #### Basic Usage
413
+
414
+ ```ts
415
+ import { ConnectionManagerAPI } from 'zastro-websockets-node/connection-manager';
416
+
417
+ // Get connection statistics
418
+ const stats = ConnectionManagerAPI.getStats();
419
+ console.log(`Active connections: ${stats.totalManagedConnections}`);
420
+
421
+ // Perform health checks
422
+ const healthResults = await ConnectionManagerAPI.healthCheck();
423
+
424
+ // Close connections by criteria
425
+ const closedCount = ConnectionManagerAPI.closeConnections({
426
+ idleMoreThan: 300000, // Close connections idle for more than 5 minutes
427
+ tags: ['temporary'] // Close connections tagged as temporary
428
+ });
429
+ ```
430
+
431
+ #### Advanced Configuration
432
+
433
+ ```ts
434
+ import { getConnectionManager } from 'zastro-websockets-node/connection-manager';
435
+
436
+ const manager = getConnectionManager({
437
+ maxConnections: 1000, // Global connection limit
438
+ maxConnectionsPerIP: 10, // Per-IP connection limit
439
+ idleTimeout: 300000, // 5 minutes idle timeout
440
+ rateLimitWindow: 60000, // 1 minute rate limit window
441
+ rateLimitMaxConnections: 5, // Max 5 connections per IP per minute
442
+ enableHealthMonitoring: true, // Enable automatic health checks
443
+ customCleanupPolicy: (connection) => {
444
+ // Custom logic for connection cleanup
445
+ return connection.tags.has('temporary') && connection.age > 300000;
446
+ }
447
+ });
448
+
449
+ // Event listeners for monitoring
450
+ manager.on('connection:added', (connection) => {
451
+ console.log(`New connection: ${connection.id}`);
452
+ });
453
+
454
+ manager.on('pool:full', (rejected) => {
455
+ console.warn(`Connection pool full, rejected: ${rejected.ip}`);
456
+ });
457
+
458
+ manager.on('ratelimit:exceeded', (ip, attempts) => {
459
+ console.warn(`Rate limit exceeded for ${ip}`);
460
+ });
461
+ ```
462
+
463
+ #### Connection Tagging and Metadata
464
+
465
+ ```ts
466
+ // In your WebSocket route
467
+ socket.addEventListener('open', () => {
468
+ const connectionId = getConnectionId(socket);
469
+ if (connectionId) {
470
+ // Add tags for grouping
471
+ manager.addConnectionTag(connectionId, 'user-session');
472
+ manager.addConnectionTag(connectionId, 'real-time-updates');
473
+
474
+ // Store custom metadata
475
+ manager.setConnectionData(connectionId, 'userId', user.id);
476
+ manager.setConnectionData(connectionId, 'sessionStart', Date.now());
477
+ }
478
+ });
479
+
480
+ // Later, find connections by criteria
481
+ const userSessions = manager.getConnectionsByTag('user-session');
482
+ const temporaryConnections = manager.getConnectionsByTag('temporary');
483
+ ```
484
+
485
+ #### Health Monitoring
486
+
487
+ ```ts
488
+ // Manual health check
489
+ const result = await manager.performHealthCheck(connectionId);
490
+ if (!result.healthy) {
491
+ console.warn(`Connection ${connectionId} is unhealthy: ${result.error}`);
492
+ }
493
+
494
+ // Automatic background health monitoring
495
+ manager.on('connection:health', (connectionId, result) => {
496
+ if (!result.healthy) {
497
+ console.warn(`Health check failed for ${connectionId}: ${result.error}`);
498
+ }
499
+ });
500
+ ```
501
+
502
+ #### Graceful Shutdown
503
+
504
+ ```ts
505
+ // Graceful shutdown with timeout
506
+ await ConnectionManagerAPI.shutdown({
507
+ timeout: 10000, // 10 second timeout
508
+ closeCode: 1001, // WebSocket close code
509
+ closeReason: 'Server shutting down'
510
+ });
511
+ ```
512
+
513
+ ## Examples
514
+
515
+ Check out the test projects for complete working examples:
516
+ - `/tests/projects/node` - Node.js WebSocket implementation
517
+ - `/tests/projects/cloudflare` - Cloudflare Workers WebSocket implementation
518
+ - `/example-usage.md` - Detailed usage guide with examples
519
+ - `/example-websocket.ts` - Basic WebSocket server example
520
+ - `/example-cloudflare-websocket.ts` - Cloudflare-specific example
521
+ - `/example-connection-management.ts` - Basic connection tracking and cleanup
522
+ - `/example-advanced-connection-management.ts` - Advanced connection management with pooling, rate limiting, and health monitoring
523
+
524
+ ## Contributing
525
+
526
+ Contributions are welcome! Please:
527
+ 1. Fork the repository
528
+ 2. Create your feature branch
529
+ 3. Add tests for your changes
530
+ 4. Update documentation
531
+ 5. Submit a pull request
532
+
533
+ ### Development Setup
534
+
535
+ ```bash
536
+ git clone https://github.com/zachhandley/zastro-websockets.git
537
+ cd zastro-websockets
538
+ git submodule update --init --recursive
539
+ pnpm install
540
+ pnpm run build
541
+ ```
542
+
543
+ ## Development Guide
544
+
545
+ ### How the Build System Works
546
+
547
+ This project uses a **dynamic build system** that automatically applies WebSocket patches to upstream Astro adapters. Here's how it works:
548
+
549
+ #### 1. Project Structure
550
+ ```
551
+ zastro-websockets/
552
+ ├── astro-upstream/ # Git submodule of official Astro repo
553
+ ├── packages/
554
+ │ ├── node/ # Built Node.js adapter (generated)
555
+ │ │ ├── dist/ # Built TypeScript files
556
+ │ │ ├── src/ # Copied & modified upstream source
557
+ │ │ └── package.json # Modified package.json
558
+ │ └── cloudflare/ # Built Cloudflare adapter (generated)
559
+ │ ├── dist/ # Built TypeScript files
560
+ │ ├── src/ # Copied & modified upstream source
561
+ │ └── package.json # Modified package.json
562
+ ├── patches/ # Legacy patch files (reference only)
563
+ ├── scripts/ # Build automation scripts
564
+ │ ├── dynamic-build.ts # Main build orchestrator
565
+ │ ├── dynamic-build-node.ts # Node.js adapter build script
566
+ │ ├── dynamic-build-cloudflare.ts # Cloudflare adapter build script
567
+ │ └── reset-submodule.ts # Submodule management
568
+ └── tests/projects/ # Test projects for both adapters
569
+ ```
570
+
571
+ #### 2. Build Process Overview
572
+
573
+ The build system uses a **6-step process** for each adapter:
574
+
575
+ 1. **Copy Upstream Source** → Copy source files from `astro-upstream/packages/integrations/[adapter]` to `packages/[adapter]/`
576
+ 2. **Apply Code Transformations** → Apply all WebSocket modifications as live code transformations
577
+ 3. **Update Package.json** → Change package name, add WebSocket dependencies, resolve workspace dependencies
578
+ 4. **Install Dependencies** → Install dependencies in upstream workspace (with `--no-frozen-lockfile`)
579
+ 5. **Build Upstream** → Compile TypeScript in upstream workspace
580
+ 6. **Copy Built Files** → Copy compiled `dist/` and modified `package.json` to final packages + README.md
581
+
582
+ #### 3. Key Build Scripts
583
+
584
+ **Main Build Script: `scripts/dynamic-build.ts`**
585
+ - Orchestrates the entire build process
586
+ - Cleans upstream and installs dependencies
587
+ - Runs both Node.js and Cloudflare builds sequentially
588
+
589
+ **Node.js Build: `scripts/dynamic-build-node.ts`**
590
+ - Copies upstream Node.js adapter source
591
+ - Applies all WebSocket modifications as code transformations
592
+ - Creates WebSocket files: `websocket/`, `middleware/`
593
+ - Adds `ws` dependency and WebSocket exports (`./websocket`, `./stats`)
594
+ - Resolves workspace dependencies to actual versions
595
+ - Builds in upstream workspace, then copies to final package
596
+
597
+ **Cloudflare Build: `scripts/dynamic-build-cloudflare.ts`**
598
+ - Copies upstream Cloudflare adapter source
599
+ - Applies WebSocket modifications for Cloudflare Workers
600
+ - Creates WebSocket files and entrypoint modifications
601
+ - Adds WebSocket export to package.json
602
+ - Resolves workspace dependencies to actual versions
603
+ - Builds in upstream workspace, then copies to final package
604
+
605
+ #### 4. Available Scripts
606
+
607
+ ```bash
608
+ # Full build process (recommended)
609
+ pnpm run build
610
+
611
+ # Test the Node.js adapter
612
+ pnpm run test:node
613
+
614
+ # Test the Cloudflare adapter
615
+ pnpm run test:cloudflare
616
+
617
+ # Test both adapters
618
+ pnpm run test
619
+
620
+ # Reset the astro-upstream submodule to latest Astro tag
621
+ pnpm run reset
622
+
623
+ # Publish packages to npm (CI only)
624
+ pnpm run publish:packages
625
+ ```
626
+
627
+ #### 5. Automated CI/CD Pipeline
628
+
629
+ The project uses **GitHub Actions** for automated building and publishing:
630
+
631
+ **Workflow: `.github/workflows/auto-publish.yml`**
632
+ - **Triggers**: Push to main, daily at 2 AM UTC, or manual dispatch
633
+ - **Process**:
634
+ 1. Checks out latest Astro tag from submodule
635
+ 2. Installs dependencies with workspace filtering
636
+ 3. Builds both adapters with dynamic build system
637
+ 4. Tests both adapters
638
+ 5. Updates package versions to `{adapter-version}-{commit-hash}`
639
+ 6. Publishes to npm with automation tokens
640
+ 7. Commits built packages to repo
641
+ 8. Creates GitHub release with version tag
642
+
643
+ **Version Strategy**:
644
+ - Uses **adapter-specific versions** (not Astro core version)
645
+ - Format: `8.3.4-abc1234` (Node adapter v8.3.4 + commit abc1234)
646
+ - Allows patch releases for fixes without waiting for Astro updates
647
+
648
+ #### 6. Key Features of Build System
649
+
650
+ **Workspace Dependency Resolution**:
651
+ - Automatically converts `@astrojs/internal-helpers@workspace:*` to actual versions
652
+ - Maps packages to correct workspace locations
653
+ - Handles complex monorepo dependency resolution
654
+
655
+ **README.md Distribution**:
656
+ - Automatically copies root README.md to each package
657
+ - Ensures npm packages have proper documentation
658
+
659
+ **Authentication & Publishing**:
660
+ - Uses npm automation tokens for 2FA bypass
661
+ - Creates `.npmrc` files in package directories
662
+ - Publishes to npm with proper access controls
663
+
664
+ #### 7. Development Workflow
665
+
666
+ 1. **Make Changes to Build Scripts**
667
+ - Edit `scripts/dynamic-build-node.ts` for Node.js changes
668
+ - Edit `scripts/dynamic-build-cloudflare.ts` for Cloudflare changes
669
+
670
+ 2. **Test Your Changes**
671
+ ```bash
672
+ pnpm run build
673
+ pnpm run test
674
+ ```
675
+
676
+ 3. **Debug Build Issues**
677
+ - Check `packages/node/` and `packages/cloudflare/` for generated files
678
+ - Review TypeScript errors in build output
679
+ - Verify WebSocket files are created correctly
680
+ - Check workspace dependency resolution
681
+
682
+ 4. **Update Tests**
683
+ - Test projects are in `tests/projects/`
684
+ - Update test projects to match API changes
685
+
686
+ #### 8. Key Differences from Patch-Based Approach
687
+
688
+ | Aspect | Old Patch System | New Dynamic System |
689
+ |--------|------------------|-------------------|
690
+ | **Modification Method** | Git patches | Code transformations |
691
+ | **Build Location** | Upstream directory | Upstream → Local packages |
692
+ | **Dependency Management** | Workspace conflicts | Automated workspace resolution |
693
+ | **TypeScript Compilation** | Upstream tsconfig | Upstream build → Copy dist |
694
+ | **Maintenance** | Manual patch updates | Automated code generation |
695
+ | **Debugging** | Patch application errors | Clear TypeScript errors |
696
+ | **CI/CD** | Manual publishing | Automated GitHub Actions |
697
+
698
+ #### 9. Adding New Features
699
+
700
+ To add new WebSocket features:
701
+
702
+ 1. **Update the build scripts** to include your new files/modifications
703
+ 2. **Add the files to the appropriate `createWebSocketFiles()` function**
704
+ 3. **Update package.json exports** if needed in `updateUpstreamPackageJson()`
705
+ 4. **Test the build process** with `pnpm run build`
706
+ 5. **Update the test projects** to demonstrate the new features
707
+
708
+ #### 10. Troubleshooting Development Issues
709
+
710
+ **Build Fails with Workspace Dependency Errors:**
711
+ - Check that workspace dependency mapping is correct
712
+ - Verify that upstream packages exist at expected paths
713
+ - Ensure `--no-frozen-lockfile` is used in CI
714
+
715
+ **Missing WebSocket Files:**
716
+ - Check that `createWebSocketFiles()` creates all necessary files
717
+ - Verify file paths and exports are correct
718
+ - Ensure TypeScript compilation includes all source files
719
+
720
+ **Publishing Fails with ENEEDAUTH:**
721
+ - Verify npm automation token is stored as repository secret
722
+ - Check that `.npmrc` files are created in package directories
723
+ - Ensure registry-url is set correctly in GitHub Actions
724
+
725
+ **Version Mismatch Issues:**
726
+ - Verify that adapter versions are read from correct package.json paths
727
+ - Check that version update logic handles different adapter versions
728
+ - Ensure commit hash is properly extracted
729
+
730
+ This dynamic build system eliminates the need for manual patch management while providing automated CI/CD and reliable publishing to npm.
731
+
732
+ ## License
733
+
734
+ MIT - see LICENSE file for details.
735
+
736
+ ## Credits
737
+
738
+ This package is inspired by:
739
+ - [gratelets](https://github.com/gratelets/gratelets) - For the pre-patched adapter approach
740
+ - [astro-node-websocket](https://github.com/lilnasy/gratelets/tree/main/packages/node-websocket) - Original WebSocket integration concept
741
+ - The Astro team for the excellent framework and adapter architecture
742
+
743
+ ## Changelog
744
+
745
+ See [CHANGELOG.md](./CHANGELOG.md) for version history and updates.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "zastro-websockets-node",
3
3
  "description": "Deploy your site to a Node.js server with WebSocket support",
4
- "version": "0.0.0-729d313",
4
+ "version": "0.0.0-ca05a2f",
5
5
  "type": "module",
6
6
  "types": "./dist/index.d.ts",
7
7
  "author": "Zach Handley <zach@zachhandley.com>",