@b9g/shovel 0.1.10 โ 0.2.0-beta.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/CHANGELOG.md +198 -0
- package/README.md +138 -6
- package/bin/cli.d.ts +2 -0
- package/bin/cli.js +748 -0
- package/bin/create.d.ts +2 -0
- package/bin/create.js +598 -0
- package/package.json +104 -32
- package/src/worker-entry.d.ts +7 -0
- package/src/worker-entry.js +37 -0
- package/bin/shovel.js +0 -43
- package/fixtures/dependency.js +0 -2
- package/fixtures/dependency1.js +0 -1
- package/fixtures/dependent1.js +0 -2
- package/fixtures/dependent2.js +0 -2
- package/fixtures/server-dependency-goodbye.ts +0 -2
- package/fixtures/server-dependency-hello.ts +0 -1
- package/fixtures/server-dependent.ts +0 -15
- package/fixtures/server-diamond.js +0 -11
- package/fixtures/server-dynamic-dependent.ts +0 -16
- package/fixtures/server-goodbye.ts +0 -13
- package/fixtures/server-hello.ts +0 -12
- package/fixtures/static.js +0 -16
- package/src/develop.js +0 -33
- package/src/static.js +0 -21
- package/test/develop.js +0 -165
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [0.2.0-beta] - 2025-11-05
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
#### ๐ Directly Executable Production Builds
|
|
13
|
+
- **Self-contained deployment**: `shovel build` now creates directly executable production builds that don't require `shovel serve`
|
|
14
|
+
- **2-file output**: Production builds generate only `server.js` (executable) and `package.json` (dependencies)
|
|
15
|
+
- **Platform bootstrapping**: Automatic platform detection and ServiceWorker environment setup
|
|
16
|
+
- **Zero-config deployment**: Run `chmod +x server.js && ./server.js` after `npm install` in the dist directory
|
|
17
|
+
|
|
18
|
+
#### ๐ Complete ServiceWorker API Implementation
|
|
19
|
+
- **Full ServiceWorker globals**: Implemented `self`, `addEventListener`, `removeEventListener`, `dispatchEvent`
|
|
20
|
+
- **ServiceWorker lifecycle**: Proper `install` and `activate` event handling
|
|
21
|
+
- **ServiceWorker APIs**: Implemented `self.skipWaiting()` and `self.clients` with environment-aware behavior
|
|
22
|
+
- **Web standards compliance**: All APIs follow web platform standards, not runtime-specific implementations
|
|
23
|
+
|
|
24
|
+
#### ๐๏ธ New Bucket Architecture
|
|
25
|
+
- **`self.buckets` API**: Replaced `self.dirs` with standardized bucket interface for filesystem access
|
|
26
|
+
- **Factory pattern**: Dynamic adapter loading for different storage backends (memory, local, S3, R2)
|
|
27
|
+
- **Unified interface**: All storage adapters implement the same `Bucket` interface
|
|
28
|
+
- **`getDirectoryHandle(name)` API**: Standardized directory access following File System Access API patterns
|
|
29
|
+
|
|
30
|
+
#### ๐ฆ Asset Import Improvements
|
|
31
|
+
- **`assetBase` import attribute**: New syntax for asset imports with base path resolution
|
|
32
|
+
- **Automatic asset processing**: Assets are automatically copied and processed during build
|
|
33
|
+
- **Import transformation**: `import './style.css' with { assetBase: true }` generates proper asset URLs
|
|
34
|
+
- **Manifest generation**: Asset manifests track all processed assets with metadata
|
|
35
|
+
|
|
36
|
+
#### ๐งช Enhanced Cache System
|
|
37
|
+
- **Redis cache adapter**: Full Redis support with connection pooling and error handling
|
|
38
|
+
- **Multi-threaded coordination**: PostMessage-based cache coordination for worker environments
|
|
39
|
+
- **Platform-agnostic types**: Separated web standard types from platform-specific implementations
|
|
40
|
+
- **Factory pattern**: Dynamic cache adapter loading matching filesystem patterns
|
|
41
|
+
|
|
42
|
+
#### ๐ง Type System Improvements
|
|
43
|
+
- **Separated type imports**: Platform-specific types only in platform packages, web standard types in shared packages
|
|
44
|
+
- **TypeScript configuration**: Fixed Headers iteration and other compatibility issues with proper lib configuration
|
|
45
|
+
- **Comprehensive type checking**: All packages now pass strict TypeScript compilation
|
|
46
|
+
- **Web standard prioritization**: Chose webworker types over runtime-specific types for compatibility
|
|
47
|
+
|
|
48
|
+
#### โก Performance & Production Features
|
|
49
|
+
- **TechEmpower Framework Benchmarks**: Official implementation and testing completed
|
|
50
|
+
- **Production scaling**: Multi-worker support with proper resource coordination
|
|
51
|
+
- **Docker deployment**: Complete containerization support with optimized builds
|
|
52
|
+
- **Cloudflare Workers**: Enhanced support with proper ES module bundling
|
|
53
|
+
|
|
54
|
+
### Changed
|
|
55
|
+
|
|
56
|
+
#### ๐ฅ Breaking Changes
|
|
57
|
+
- **BREAKING**: Replaced `self.dirs` with `self.buckets` API
|
|
58
|
+
- **BREAKING**: Changed `getBucket()` to `getDirectoryHandle(name: string)` for standardization
|
|
59
|
+
- **BREAKING**: Asset imports now require explicit `assetBase` attribute for base path resolution
|
|
60
|
+
- **BREAKING**: Updated all filesystem adapters to implement new `Bucket` interface
|
|
61
|
+
|
|
62
|
+
#### ๐๏ธ Build System Overhaul
|
|
63
|
+
- **Simplified output**: Moved from complex templating to clean esbuild banner approach
|
|
64
|
+
- **Self-contained bundling**: Framework dependencies bundled, app dependencies preserved
|
|
65
|
+
- **Platform detection**: Automatic runtime detection and appropriate adapter loading
|
|
66
|
+
- **Production runtime**: Uses proven development patterns for production consistency
|
|
67
|
+
|
|
68
|
+
#### ๐ Architecture Improvements
|
|
69
|
+
- **Clean separation**: Platform-specific code isolated from shared/core packages
|
|
70
|
+
- **Consistent interfaces**: All adapters follow same patterns (cache, filesystem, platform)
|
|
71
|
+
- **Dynamic loading**: Runtime adapter detection and loading
|
|
72
|
+
- **Environment awareness**: Proper detection of worker vs main thread contexts
|
|
73
|
+
|
|
74
|
+
### Fixed
|
|
75
|
+
|
|
76
|
+
- **TypeScript compilation**: Resolved Headers.entries() iteration issues with DOM.Iterable configuration
|
|
77
|
+
- **Cache exports**: Fixed missing factory functions in multi-threaded cache coordination
|
|
78
|
+
- **Platform contamination**: Removed Node.js-specific imports from platform-agnostic packages
|
|
79
|
+
- **Build loops**: Fixed infinite loading where worker.js was trying to load itself
|
|
80
|
+
- **Worker globals**: Proper ServiceWorker environment setup in all execution contexts
|
|
81
|
+
- **Dependency resolution**: Fixed workspace dependency resolution and external bundling
|
|
82
|
+
|
|
83
|
+
### Technical Details
|
|
84
|
+
|
|
85
|
+
#### ServiceWorker Implementation
|
|
86
|
+
The ServiceWorker implementation provides full web platform compatibility:
|
|
87
|
+
|
|
88
|
+
```typescript
|
|
89
|
+
// Environment-aware skipWaiting implementation
|
|
90
|
+
const skipWaiting = async (): Promise<void> => {
|
|
91
|
+
if (options.isDevelopment && options.hotReload) {
|
|
92
|
+
console.info('[ServiceWorker] skipWaiting() - triggering hot reload');
|
|
93
|
+
await options.hotReload();
|
|
94
|
+
} else if (!options.isDevelopment) {
|
|
95
|
+
console.info('[ServiceWorker] skipWaiting() - production graceful restart not implemented');
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
#### Build System Architecture
|
|
101
|
+
The new build system uses esbuild banners for clean, self-contained executables:
|
|
102
|
+
|
|
103
|
+
```javascript
|
|
104
|
+
// Production bootstrap injected via esbuild banner
|
|
105
|
+
buildConfig.banner = {
|
|
106
|
+
js: `#!/usr/bin/env node
|
|
107
|
+
import { ServiceWorkerRuntime, createServiceWorkerGlobals, createBucketStorage } from '@b9g/platform';
|
|
108
|
+
|
|
109
|
+
if (import.meta.url === \`file://\${process.argv[1]}\`) {
|
|
110
|
+
const runtime = new ServiceWorkerRuntime();
|
|
111
|
+
const buckets = createBucketStorage(process.cwd());
|
|
112
|
+
|
|
113
|
+
createServiceWorkerGlobals(runtime, { buckets });
|
|
114
|
+
// ... HTTP server setup using proven patterns
|
|
115
|
+
}
|
|
116
|
+
// User's ServiceWorker code follows...`
|
|
117
|
+
};
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
#### Type Architecture
|
|
121
|
+
Clean separation ensures web standard compatibility:
|
|
122
|
+
|
|
123
|
+
```typescript
|
|
124
|
+
// Platform-agnostic cache using web standards
|
|
125
|
+
interface WorkerLike {
|
|
126
|
+
postMessage(value: any): void;
|
|
127
|
+
on(event: string, listener: (data: any) => void): void;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Web standard encoding instead of Node.js Buffer
|
|
131
|
+
body: btoa(String.fromCharCode(...new Uint8Array(body))),
|
|
132
|
+
totalSize += new TextEncoder().encode(value).length;
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### Migration Guide
|
|
136
|
+
|
|
137
|
+
#### From `self.dirs` to `self.buckets`
|
|
138
|
+
```typescript
|
|
139
|
+
// Before
|
|
140
|
+
const distDir = await self.dirs.getBucket();
|
|
141
|
+
|
|
142
|
+
// After
|
|
143
|
+
const distBucket = await self.buckets.getDirectoryHandle('dist');
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
#### Asset Imports
|
|
147
|
+
```typescript
|
|
148
|
+
// Before
|
|
149
|
+
import './style.css' with { url: true };
|
|
150
|
+
|
|
151
|
+
// After
|
|
152
|
+
import './style.css' with { assetBase: true };
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
#### Deployment
|
|
156
|
+
```bash
|
|
157
|
+
# Before
|
|
158
|
+
shovel build
|
|
159
|
+
shovel serve dist/
|
|
160
|
+
|
|
161
|
+
# After
|
|
162
|
+
shovel build
|
|
163
|
+
cd dist && npm install
|
|
164
|
+
chmod +x server.js && ./server.js
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### Performance
|
|
168
|
+
|
|
169
|
+
- **TechEmpower Benchmarks**: Competitive performance in official framework benchmarks
|
|
170
|
+
- **Production scaling**: Multi-worker coordination with proper resource management
|
|
171
|
+
- **Build optimization**: Faster builds with simplified bundling strategy
|
|
172
|
+
- **Runtime efficiency**: Direct execution without CLI overhead
|
|
173
|
+
|
|
174
|
+
### Developer Experience
|
|
175
|
+
|
|
176
|
+
- **Zero-config deployment**: No configuration required for basic deployment
|
|
177
|
+
- **Proven patterns**: Production builds use same patterns as development
|
|
178
|
+
- **Better error messages**: Improved error handling and diagnostics
|
|
179
|
+
- **Type safety**: Comprehensive TypeScript support across all packages
|
|
180
|
+
|
|
181
|
+
---
|
|
182
|
+
|
|
183
|
+
## [0.1.10] - Previous Release
|
|
184
|
+
|
|
185
|
+
### Added
|
|
186
|
+
- Initial CLI implementation
|
|
187
|
+
- Basic platform abstraction
|
|
188
|
+
- Development server functionality
|
|
189
|
+
- Asset processing pipeline
|
|
190
|
+
|
|
191
|
+
### Fixed
|
|
192
|
+
- Various development workflow issues
|
|
193
|
+
- Build system stability
|
|
194
|
+
- Type definitions
|
|
195
|
+
|
|
196
|
+
---
|
|
197
|
+
|
|
198
|
+
For detailed technical documentation, see the [README.md](README.md) and individual package documentation.
|
package/README.md
CHANGED
|
@@ -1,12 +1,144 @@
|
|
|
1
1
|
# Shovel
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**The ServiceWorker platform for server-side JavaScript.**
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Same code. Any runtime. Node.js, Bun, Cloudflare Workers.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
```javascript
|
|
8
|
+
// app.js
|
|
9
|
+
self.addEventListener("fetch", (event) => {
|
|
10
|
+
event.respondWith(new Response("Hello World"));
|
|
11
|
+
});
|
|
12
|
+
```
|
|
8
13
|
|
|
9
|
-
|
|
14
|
+
```bash
|
|
15
|
+
npx @b9g/shovel develop app.js
|
|
16
|
+
```
|
|
10
17
|
|
|
11
|
-
|
|
12
|
-
|
|
18
|
+
## Why Shovel?
|
|
19
|
+
|
|
20
|
+
Browsers have ServiceWorker. Cloudflare has Workers. Node.js and Bun have... Express?
|
|
21
|
+
|
|
22
|
+
Shovel brings the ServiceWorker programming model to server-side JavaScript. Write your app once using web standards, deploy it anywhere.
|
|
23
|
+
|
|
24
|
+
## Web Standards
|
|
25
|
+
|
|
26
|
+
Shovel implements web platform APIs that server-side JavaScript is missing:
|
|
27
|
+
|
|
28
|
+
| API | Standard | What it does |
|
|
29
|
+
|-----|----------|--------------|
|
|
30
|
+
| `fetch` event | [Service Workers](https://w3c.github.io/ServiceWorker/) | Request handling |
|
|
31
|
+
| `self.caches` | [Cache API](https://w3c.github.io/ServiceWorker/#cache-interface) | Response caching |
|
|
32
|
+
| `self.buckets` | [FileSystemDirectoryHandle](https://fs.spec.whatwg.org/#api-filesystemdirectoryhandle) | Storage (local, S3, R2) |
|
|
33
|
+
| `self.cookieStore` | [Cookie Store API](https://wicg.github.io/cookie-store/) | Cookie management |
|
|
34
|
+
| `URLPattern` | [URLPattern](https://urlpattern.spec.whatwg.org/) | Route matching (100% WPT) |
|
|
35
|
+
| `AsyncContext.Variable` | [TC39 Stage 2](https://github.com/tc39/proposal-async-context) | Request-scoped state |
|
|
36
|
+
|
|
37
|
+
Your code uses standards. Shovel makes them work everywhere.
|
|
38
|
+
|
|
39
|
+
## True Portability
|
|
40
|
+
|
|
41
|
+
Shovel is a complete meta-framework. Same code, any runtime, any rendering strategy:
|
|
42
|
+
|
|
43
|
+
- **Server runtimes**: Node.js, Bun, Cloudflare Workers for development and production
|
|
44
|
+
- **Browser ServiceWorkers**: The same app can run as a PWA service worker
|
|
45
|
+
- **Universal rendering**: Dynamic, static, or client-side - link and deploy assets automatically
|
|
46
|
+
|
|
47
|
+
## Quick Start
|
|
48
|
+
|
|
49
|
+
```javascript
|
|
50
|
+
// app.js
|
|
51
|
+
import {Router} from "@b9g/router";
|
|
52
|
+
|
|
53
|
+
const router = new Router();
|
|
54
|
+
|
|
55
|
+
router.route("/").get(() => new Response("Hello World"));
|
|
56
|
+
|
|
57
|
+
router.route("/users/:id").get((request, {params}) => {
|
|
58
|
+
return Response.json({id: params.id});
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
self.addEventListener("fetch", (event) => {
|
|
62
|
+
event.respondWith(router.handler(event.request));
|
|
63
|
+
});
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
# Create a new project
|
|
68
|
+
npm create @b9g/shovel my-app
|
|
69
|
+
|
|
70
|
+
# Development with hot reload
|
|
71
|
+
npx @b9g/shovel develop app.js
|
|
72
|
+
|
|
73
|
+
# Build for production
|
|
74
|
+
npx @b9g/shovel build app.js --platform=node
|
|
75
|
+
npx @b9g/shovel build app.js --platform=bun
|
|
76
|
+
npx @b9g/shovel build app.js --platform=cloudflare
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Platform APIs
|
|
80
|
+
|
|
81
|
+
```javascript
|
|
82
|
+
// Cache API - response caching
|
|
83
|
+
const cache = await self.caches.open("my-cache");
|
|
84
|
+
await cache.put(request, response.clone());
|
|
85
|
+
const cached = await cache.match(request);
|
|
86
|
+
|
|
87
|
+
// File System Access - storage buckets (local, S3, R2)
|
|
88
|
+
const bucket = await self.buckets.open("uploads");
|
|
89
|
+
const file = await bucket.getFileHandle("image.png");
|
|
90
|
+
const contents = await (await file.getFile()).arrayBuffer();
|
|
91
|
+
|
|
92
|
+
// Cookie Store - cookie management
|
|
93
|
+
const session = await self.cookieStore.get("session");
|
|
94
|
+
await self.cookieStore.set("theme", "dark");
|
|
95
|
+
|
|
96
|
+
// AsyncContext - request-scoped state without prop drilling
|
|
97
|
+
const requestId = new AsyncContext.Variable();
|
|
98
|
+
requestId.run(crypto.randomUUID(), async () => {
|
|
99
|
+
console.log(requestId.get()); // Works anywhere in the call stack
|
|
100
|
+
});
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## Asset Pipeline
|
|
104
|
+
|
|
105
|
+
Import any file and get its production URL with content hashing:
|
|
106
|
+
|
|
107
|
+
```javascript
|
|
108
|
+
import styles from "./styles.css" with { assetBase: "/assets" };
|
|
109
|
+
import logo from "./logo.png" with { assetBase: "/assets" };
|
|
110
|
+
|
|
111
|
+
// styles = "/assets/styles-a1b2c3d4.css"
|
|
112
|
+
// logo = "/assets/logo-e5f6g7h8.png"
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
At build time, Shovel:
|
|
116
|
+
- Copies assets to the output directory with content hashes
|
|
117
|
+
- Generates a manifest mapping original paths to hashed URLs
|
|
118
|
+
- Transforms imports to return the final URLs
|
|
119
|
+
|
|
120
|
+
Assets are served via the platform's best option:
|
|
121
|
+
- **Cloudflare**: Workers Assets (edge-cached, zero config)
|
|
122
|
+
- **Node/Bun**: Static file middleware or bucket storage
|
|
123
|
+
|
|
124
|
+
## Packages
|
|
125
|
+
|
|
126
|
+
| Package | Description |
|
|
127
|
+
|---------|-------------|
|
|
128
|
+
| `@b9g/shovel` | CLI for development and deployment |
|
|
129
|
+
| `@b9g/platform` | Core runtime and platform APIs |
|
|
130
|
+
| `@b9g/platform-node` | Node.js adapter |
|
|
131
|
+
| `@b9g/platform-bun` | Bun adapter |
|
|
132
|
+
| `@b9g/platform-cloudflare` | Cloudflare Workers adapter |
|
|
133
|
+
| `@b9g/router` | URLPattern-based routing with middleware |
|
|
134
|
+
| `@b9g/cache` | Cache API implementation |
|
|
135
|
+
| `@b9g/filesystem` | File System Access implementation |
|
|
136
|
+
| `@b9g/match-pattern` | URLPattern with extensions (100% WPT) |
|
|
137
|
+
| `@b9g/async-context` | AsyncContext.Variable implementation |
|
|
138
|
+
| `@b9g/http-errors` | Standard HTTP error classes |
|
|
139
|
+
| `@b9g/auth` | OAuth2/PKCE and CORS middleware |
|
|
140
|
+
| `@b9g/assets` | Static asset handling |
|
|
141
|
+
|
|
142
|
+
## License
|
|
143
|
+
|
|
144
|
+
MIT
|
package/bin/cli.d.ts
ADDED