@b9g/shovel 0.2.2 → 0.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +30 -194
- package/LICENSE +1 -1
- package/README.md +58 -48
- package/bin/cli.js +11 -3
- package/bin/create.js +2 -2
- package/package.json +17 -20
- package/src/_chunks/{build-5DSZYRHR.js → build-KBQU2OA7.js} +2 -2
- package/src/_chunks/{chunk-NZVIBZYG.js → chunk-7GONPLNW.js} +1 -1
- package/src/_chunks/{chunk-ZIKUWTXK.js → chunk-ABGHNBNM.js} +45 -5
- package/src/_chunks/{develop-DNLCO6BX.js → develop-JUQG2G7M.js} +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -2,215 +2,51 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to Shovel will be documented in this file.
|
|
4
4
|
|
|
5
|
-
## [0.2.
|
|
6
|
-
|
|
7
|
-
### Bug Fixes
|
|
8
|
-
|
|
9
|
-
- **Fix asset manifest invalidation in dev mode** - Assets with new content hashes after client bundle changes no longer return 404. The root cause was build-time manifest resolution reading stale data from disk during rebuilds. ([#36](https://github.com/bikeshaving/shovel/pull/36), fixes [#35](https://github.com/bikeshaving/shovel/issues/35))
|
|
10
|
-
|
|
11
|
-
## [0.2.0-beta.12] - 2026-01-14
|
|
12
|
-
|
|
13
|
-
### Breaking Changes
|
|
14
|
-
|
|
15
|
-
- **`shovel activate` command removed** - Use `shovel build --lifecycle` instead
|
|
16
|
-
- **`loadServiceWorker()` removed** - Use `platform.serviceWorker.register()` instead
|
|
17
|
-
- **`getEntryWrapper()` removed** - Use `getProductionEntryPoints()` instead
|
|
5
|
+
## [0.2.3] - 2026-02-02
|
|
18
6
|
|
|
19
|
-
###
|
|
7
|
+
### Features
|
|
20
8
|
|
|
21
|
-
|
|
9
|
+
- **Enable code splitting for client-side bundles** - Dynamic imports in client scripts now create separate chunks instead of being inlined, allowing heavy dependencies to be lazy-loaded. ([#39](https://github.com/bikeshaving/shovel/pull/39), fixes [#38](https://github.com/bikeshaving/shovel/issues/38))
|
|
22
10
|
|
|
23
|
-
|
|
24
|
-
- **Platform-driven entry points** - Platforms define their output structure via `getProductionEntryPoints()`:
|
|
25
|
-
- Node/Bun: `{ index: "<supervisor>", worker: "<worker>" }` - two files
|
|
26
|
-
- Cloudflare: `{ worker: "<code>" }` - single file
|
|
27
|
-
- **Deleted `activate` command** - Replaced with `shovel build --lifecycle` flag
|
|
28
|
-
- `--lifecycle` - runs activate stage (default)
|
|
29
|
-
- `--lifecycle install` - runs install stage only
|
|
30
|
-
|
|
31
|
-
### API Changes
|
|
32
|
-
|
|
33
|
-
- **New `platform.serviceWorker.register()` API** - Mirrors browser's `navigator.serviceWorker.register()`
|
|
34
|
-
- **Deleted `loadServiceWorker()`** - Use `serviceWorker.register()` instead
|
|
35
|
-
- **New `platform.listen()` / `close()`** - Server lifecycle management
|
|
36
|
-
- **New `runLifecycle()` and `dispatchRequest()`** - Public runtime utilities
|
|
11
|
+
### Bug Fixes
|
|
37
12
|
|
|
38
|
-
|
|
13
|
+
- **Use unique manifest keys for chunks** - Chunk files are now keyed by URL to avoid collisions when the same chunk appears in different assetBase directories.
|
|
39
14
|
|
|
40
|
-
|
|
41
|
-
- Added JSDoc to platform `create*` methods documenting defaults
|
|
42
|
-
- Standardized import organization (node builtins → external → @b9g/* → relative)
|
|
43
|
-
- Renamed `isDynamicCode` → `containsRuntimeExpressions` for clarity
|
|
15
|
+
## [0.2.2] - 2026-01-30
|
|
44
16
|
|
|
45
|
-
###
|
|
17
|
+
### Bug Fixes
|
|
46
18
|
|
|
47
|
-
-
|
|
48
|
-
- `@b9g/platform-node` → 0.1.14-beta.0
|
|
49
|
-
- `@b9g/platform-bun` → 0.1.12-beta.0
|
|
50
|
-
- `@b9g/platform-cloudflare` → 0.1.12-beta.0
|
|
19
|
+
- **Fix asset manifest invalidation in dev mode** - Assets with new content hashes after client bundle changes no longer return 404. The root cause was build-time manifest resolution reading stale data from disk during rebuilds. ([#36](https://github.com/bikeshaving/shovel/pull/36), fixes [#35](https://github.com/bikeshaving/shovel/issues/35))
|
|
51
20
|
|
|
52
|
-
|
|
21
|
+
## [0.2.1] - 2026-01-28
|
|
53
22
|
|
|
54
|
-
|
|
55
|
-
- `src/utils/watcher.ts` - Merged into `ServerBundler`
|
|
56
|
-
- `src/plugins/shovel.ts` - Split into `config.ts` + `entry.ts`
|
|
57
|
-
- `packages/platform/test/single-threaded.test.ts`
|
|
58
|
-
- `SingleThreadedRuntime` class - All platforms now use `ServiceWorkerPool`
|
|
23
|
+
### Improvements
|
|
59
24
|
|
|
60
|
-
|
|
25
|
+
- **Improved create-shovel UX** - Better templates and user experience for project scaffolding
|
|
61
26
|
|
|
62
|
-
## [0.2.0
|
|
27
|
+
## [0.2.0] - 2026-01-28
|
|
63
28
|
|
|
64
|
-
|
|
65
|
-
- **ESBuild configuration support (#18)** - Custom esbuild options in shovel.json
|
|
66
|
-
- **Config expression syntax** - `$PORT || 3000`, `$DATABASE_URL ?? null`
|
|
67
|
-
- **Null fallback fix** - Allow intentional null fallbacks in config expressions
|
|
68
|
-
- **DatabaseStorage API redesign** - New open/get pattern with IndexedDB-style migrations
|
|
69
|
-
- **Migrated from Drizzle to @b9g/zen** - Simpler, more portable database layer
|
|
70
|
-
- **Logging DX improvements** - Better defaults, consolidated categories
|
|
71
|
-
- **`impl` key unification** - Simplified resource configuration
|
|
72
|
-
- **CI/lint enforcement** - ESLint and Prettier standardized
|
|
73
|
-
- **Documentation** - Comprehensive docs for all APIs
|
|
29
|
+
This is a major release that establishes Shovel as a complete ServiceWorker-based meta-framework with locked-down APIs for core packages, a unified configuration system, and comprehensive platform support.
|
|
74
30
|
|
|
75
|
-
###
|
|
76
|
-
- `@b9g/router` → 0.2.0-beta.1 (CORS middleware, trailingSlash moved)
|
|
77
|
-
- `@b9g/node-webworker` → 0.2.0-beta.1 (CloseEvent, onclose, env option)
|
|
78
|
-
- `@b9g/cache-redis` → 0.2.0-beta.1 (logger category fix)
|
|
79
|
-
- `@b9g/assets` → 0.2.0-beta.0
|
|
80
|
-
- `@b9g/async-context` → 0.2.0-beta.0
|
|
81
|
-
- `@b9g/cache` → 0.2.0-beta.0
|
|
82
|
-
- `@b9g/http-errors` → 0.2.0-beta.0
|
|
83
|
-
- `@b9g/match-pattern` → 0.2.0-beta.0
|
|
31
|
+
### Highlights
|
|
84
32
|
|
|
85
|
-
|
|
33
|
+
- **Consistent Worker Execution Model** - ServiceWorker code always runs in a worker thread
|
|
34
|
+
- **ESBuild Configuration Support** - Custom esbuild options in shovel.json
|
|
35
|
+
- **Config Expression Syntax** - `$PORT || 3000`, `$DATABASE_URL ?? null`
|
|
36
|
+
- **Comprehensive Logging System** - Built-in LogTape integration
|
|
37
|
+
- **Database Storage API** - IndexedDB-style migrations with `self.databases`
|
|
38
|
+
- **CORS Middleware** - New middleware in `@b9g/router/middleware`
|
|
39
|
+
- **Unified Build System** - New `ServerBundler` class, platform-driven entry points
|
|
86
40
|
|
|
87
|
-
|
|
41
|
+
### Breaking Changes
|
|
88
42
|
|
|
89
|
-
|
|
43
|
+
- `self.buckets` renamed to `self.directories`
|
|
44
|
+
- `shovel activate` command removed - Use `shovel build --lifecycle` instead
|
|
45
|
+
- `loadServiceWorker()` removed - Use `platform.serviceWorker.register()` instead
|
|
46
|
+
- `@b9g/router` trailingSlash middleware moved to `@b9g/router/middleware`
|
|
47
|
+
- `self.loggers.get()` now takes an array: `self.loggers.get(["app", "db"])`
|
|
90
48
|
|
|
91
|
-
###
|
|
49
|
+
### CLI
|
|
92
50
|
|
|
93
|
-
-
|
|
94
|
-
-
|
|
95
|
-
- **`self.loggers.get()` signature changed** - Now takes an array: `self.loggers.get(["app", "db"])` instead of dot notation
|
|
96
|
-
- **Config `module`/`export` unified to `impl`** - Resource configurations now use a single `impl` key for reified implementations
|
|
97
|
-
|
|
98
|
-
### New Features
|
|
99
|
-
|
|
100
|
-
#### Consistent Worker Execution Model (#17)
|
|
101
|
-
ServiceWorker code now ALWAYS runs in a worker thread, never the main thread. This ensures:
|
|
102
|
-
- Same globals/environment in dev and prod (eliminates mode-only bugs)
|
|
103
|
-
- Worker isolation preserved
|
|
104
|
-
- Simplified mental model
|
|
105
|
-
|
|
106
|
-
#### ESBuild Configuration Support (#18)
|
|
107
|
-
Custom ESBuild options can now be specified in `shovel.json`:
|
|
108
|
-
```json
|
|
109
|
-
{
|
|
110
|
-
"esbuild": {
|
|
111
|
-
"external": ["lightningcss"],
|
|
112
|
-
"define": { "DEBUG": "true" }
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
```
|
|
116
|
-
|
|
117
|
-
#### Config Expression Syntax
|
|
118
|
-
Environment variables and expressions in `shovel.json`:
|
|
119
|
-
```json
|
|
120
|
-
{
|
|
121
|
-
"port": "$PORT || 3000",
|
|
122
|
-
"databases": {
|
|
123
|
-
"main": {
|
|
124
|
-
"url": "$DATABASE_URL"
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
```
|
|
129
|
-
|
|
130
|
-
#### Comprehensive Logging System
|
|
131
|
-
- Built-in LogTape integration with console sink by default
|
|
132
|
-
- Configurable sinks (file, OpenTelemetry, Sentry, etc.)
|
|
133
|
-
- Category-based log levels
|
|
134
|
-
- `shovel` category logs at `info` level by default
|
|
135
|
-
|
|
136
|
-
#### Database Storage API
|
|
137
|
-
New `self.databases` API with IndexedDB-style migrations:
|
|
138
|
-
```typescript
|
|
139
|
-
self.addEventListener("activate", (event) => {
|
|
140
|
-
event.waitUntil(
|
|
141
|
-
databases.open("main", 2, (e) => {
|
|
142
|
-
e.waitUntil(e.db.exec`CREATE TABLE users (...)`);
|
|
143
|
-
})
|
|
144
|
-
);
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
// In fetch handlers
|
|
148
|
-
const db = databases.get("main");
|
|
149
|
-
const users = await db.all`SELECT * FROM users`;
|
|
150
|
-
```
|
|
151
|
-
|
|
152
|
-
#### CORS Middleware
|
|
153
|
-
New CORS middleware in `@b9g/router/middleware`:
|
|
154
|
-
```typescript
|
|
155
|
-
import { cors } from "@b9g/router/middleware";
|
|
156
|
-
router.use(cors({ origin: "https://example.com" }));
|
|
157
|
-
```
|
|
158
|
-
|
|
159
|
-
### Package Updates
|
|
160
|
-
|
|
161
|
-
#### Locked at 0.2.0-beta.0 (API stable)
|
|
162
|
-
- `@b9g/async-context` - AsyncContext polyfill
|
|
163
|
-
- `@b9g/match-pattern` - URLPattern implementation
|
|
164
|
-
- `@b9g/assets` - Static asset pipeline with content hashing
|
|
165
|
-
- `@b9g/cache` - Cache API implementation (memory, postmessage)
|
|
166
|
-
- `@b9g/http-errors` - HTTP error classes
|
|
167
|
-
|
|
168
|
-
#### Updated to 0.2.0-beta.1
|
|
169
|
-
- `@b9g/router` - Added CORS middleware, moved trailingSlash to /middleware
|
|
170
|
-
- `@b9g/node-webworker` - Added CloseEvent, onclose handler, env option
|
|
171
|
-
- `@b9g/cache-redis` - Logger category updates
|
|
172
|
-
|
|
173
|
-
#### Still Evolving (0.1.x)
|
|
174
|
-
- `@b9g/platform` - Core runtime and platform abstraction
|
|
175
|
-
- `@b9g/platform-bun` - Bun platform adapter
|
|
176
|
-
- `@b9g/platform-node` - Node.js platform adapter
|
|
177
|
-
- `@b9g/platform-cloudflare` - Cloudflare Workers adapter
|
|
178
|
-
- `@b9g/filesystem` - File System Access API implementation
|
|
179
|
-
- `@b9g/filesystem-s3` - S3 filesystem adapter
|
|
180
|
-
|
|
181
|
-
### CLI Changes
|
|
182
|
-
|
|
183
|
-
- `shovel develop` - Development server with hot reload (note: `dev` alias removed)
|
|
184
|
-
- `shovel build` - Production build (use `--lifecycle` flag to run lifecycle events)
|
|
185
|
-
- Removed `--verbose` flags (use logging config instead)
|
|
186
|
-
|
|
187
|
-
### Infrastructure
|
|
188
|
-
|
|
189
|
-
- Migrated from Drizzle ORM to `@b9g/zen` for database operations
|
|
190
|
-
- Added GitHub Actions CI with parallel test execution
|
|
191
|
-
- ESLint and Prettier configuration standardized
|
|
192
|
-
- Comprehensive test suites with `bun:test`
|
|
193
|
-
|
|
194
|
-
### Documentation
|
|
195
|
-
|
|
196
|
-
New documentation pages:
|
|
197
|
-
- Getting Started guide
|
|
198
|
-
- CLI reference
|
|
199
|
-
- Configuration (shovel.json) reference
|
|
200
|
-
- Deployment guide
|
|
201
|
-
- ServiceWorker lifecycle
|
|
202
|
-
- Routing and middleware
|
|
203
|
-
- Storage APIs (databases, caches, directories)
|
|
204
|
-
- Cookies and AsyncContext
|
|
205
|
-
|
|
206
|
-
---
|
|
207
|
-
|
|
208
|
-
## [0.1.x] - Previous Releases
|
|
209
|
-
|
|
210
|
-
Initial development releases establishing core architecture:
|
|
211
|
-
- ServiceWorker-based request handling
|
|
212
|
-
- Platform abstraction layer
|
|
213
|
-
- Router with generator-based middleware
|
|
214
|
-
- Cache API implementations
|
|
215
|
-
- File System Access API
|
|
216
|
-
- Hot reload in development
|
|
51
|
+
- `shovel develop` - Development server with hot reload
|
|
52
|
+
- `shovel build` - Production build with `--lifecycle` flag for lifecycle events
|
package/LICENSE
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
Copyright
|
|
1
|
+
Copyright 2026 Brian Kim
|
|
2
2
|
|
|
3
3
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
4
4
|
|
package/README.md
CHANGED
|
@@ -1,15 +1,31 @@
|
|
|
1
|
-
# Shovel.js
|
|
1
|
+
# Shovel.js
|
|
2
2
|
|
|
3
|
-
**
|
|
3
|
+
**Run Service Workers anywhere.**
|
|
4
4
|
|
|
5
|
-
Shovel is a
|
|
5
|
+
Shovel is a meta-framework for building server applications using the ServiceWorker API. Write once, deploy to Node.js, Bun, or Cloudflare Workers.
|
|
6
6
|
|
|
7
|
-
```
|
|
8
|
-
//
|
|
7
|
+
```typescript
|
|
8
|
+
// server.ts
|
|
9
9
|
import {Router} from "@b9g/router";
|
|
10
|
+
|
|
10
11
|
const router = new Router();
|
|
11
12
|
|
|
12
|
-
router.route("/")
|
|
13
|
+
router.route("/kv/:key")
|
|
14
|
+
.get(async (req, ctx) => {
|
|
15
|
+
const cache = await self.caches.open("kv");
|
|
16
|
+
const cached = await cache.match(ctx.params.key);
|
|
17
|
+
return cached ?? new Response(null, {status: 404});
|
|
18
|
+
})
|
|
19
|
+
.put(async (req, ctx) => {
|
|
20
|
+
const cache = await self.caches.open("kv");
|
|
21
|
+
await cache.put(ctx.params.key, new Response(await req.text()));
|
|
22
|
+
return new Response(null, {status: 201});
|
|
23
|
+
})
|
|
24
|
+
.delete(async (req, ctx) => {
|
|
25
|
+
const cache = await self.caches.open("kv");
|
|
26
|
+
await cache.delete(ctx.params.key);
|
|
27
|
+
return new Response(null, {status: 204});
|
|
28
|
+
});
|
|
13
29
|
|
|
14
30
|
self.addEventListener("fetch", (ev) => {
|
|
15
31
|
ev.respondWith(router.handle(ev.request));
|
|
@@ -17,30 +33,20 @@ self.addEventListener("fetch", (ev) => {
|
|
|
17
33
|
```
|
|
18
34
|
|
|
19
35
|
```bash
|
|
20
|
-
shovel develop
|
|
21
|
-
|
|
22
|
-
## Quick Start
|
|
36
|
+
$ shovel develop server.ts
|
|
37
|
+
listening on http://localhost:7777
|
|
23
38
|
|
|
24
|
-
|
|
25
|
-
// src/server.js
|
|
26
|
-
import {Router} from "@b9g/router";
|
|
39
|
+
$ curl -X PUT :7777/kv/hello -d "world"
|
|
27
40
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
router.route("/").get(() => new Response("Hello World"));
|
|
31
|
-
|
|
32
|
-
router.route("/greet/:name").get((request, {params}) => {
|
|
33
|
-
return new Response(`Hello ${params.name}`);
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
self.addEventListener("fetch", (event) => {
|
|
37
|
-
event.respondWith(router.handle(event.request));
|
|
38
|
-
});
|
|
41
|
+
$ curl :7777/kv/hello
|
|
42
|
+
world
|
|
39
43
|
```
|
|
40
44
|
|
|
45
|
+
## Quick Start
|
|
46
|
+
|
|
41
47
|
```bash
|
|
42
48
|
# Create a new project
|
|
43
|
-
npm create
|
|
49
|
+
npm create shovel my-app
|
|
44
50
|
|
|
45
51
|
# Development with hot reload
|
|
46
52
|
npx @b9g/shovel develop src/server.ts
|
|
@@ -51,19 +57,23 @@ npx @b9g/shovel build src/server.ts --platform=bun
|
|
|
51
57
|
npx @b9g/shovel build src/server.ts --platform=cloudflare
|
|
52
58
|
```
|
|
53
59
|
|
|
60
|
+
## Documentation
|
|
61
|
+
|
|
62
|
+
Visit [shovel.js.org](https://shovel.js.org) for guides and API reference.
|
|
54
63
|
|
|
55
64
|
## Web Standards
|
|
65
|
+
|
|
56
66
|
Shovel is obsessively standards-first. All Shovel APIs use web standards, and Shovel implements/shims useful standards when they're missing.
|
|
57
67
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
68
|
+
| API | Standard | Purpose |
|
|
69
|
+
|-----|----------|---------|
|
|
70
|
+
| `fetch()` | [Fetch](https://fetch.spec.whatwg.org) | Networking |
|
|
71
|
+
| `install`, `activate`, `fetch` events | [Service Workers](https://w3c.github.io/ServiceWorker/) | Server lifecycle |
|
|
72
|
+
| `AsyncContext.Variable` | [TC39 Stage 2](https://github.com/tc39/proposal-async-context) | Request-scoped state |
|
|
73
|
+
| `self.caches` | [Cache API](https://w3c.github.io/ServiceWorker/#cache-interface) | Response caching |
|
|
74
|
+
| `self.directories` | [FileSystem API](https://fs.spec.whatwg.org/) | Storage (local, S3, R2) |
|
|
75
|
+
| `self.cookieStore` | [CookieStore API](https://cookiestore.spec.whatwg.org) | Cookie management |
|
|
76
|
+
| `URLPattern` | [URLPattern](https://urlpattern.spec.whatwg.org/) | Route matching |
|
|
67
77
|
|
|
68
78
|
Your code uses standards. Shovel makes them work everywhere.
|
|
69
79
|
|
|
@@ -87,7 +97,7 @@ The core abstraction is the **ServiceWorker-style storage pattern**. Globals pro
|
|
|
87
97
|
const cache = await self.caches.open("sessions"); // Cache API
|
|
88
98
|
const dir = await self.directories.open("uploads"); // FileSystem API
|
|
89
99
|
const db = self.databases.get("main"); // Zen DB (opened on activate)
|
|
90
|
-
const logger = self.loggers.get(["app", "requests"]);
|
|
100
|
+
const logger = self.loggers.get(["app", "requests"]); // LogTape
|
|
91
101
|
```
|
|
92
102
|
|
|
93
103
|
Each storage type is:
|
|
@@ -168,27 +178,27 @@ Shovel's configuration follows these principles:
|
|
|
168
178
|
|
|
169
179
|
```json
|
|
170
180
|
{
|
|
171
|
-
"port": "PORT ||
|
|
172
|
-
"host": "HOST || localhost",
|
|
173
|
-
"workers": "WORKERS ?? 1",
|
|
181
|
+
"port": "$PORT || 7777",
|
|
182
|
+
"host": "$HOST || localhost",
|
|
183
|
+
"workers": "$WORKERS ?? 1",
|
|
174
184
|
"caches": {
|
|
175
185
|
"sessions": {
|
|
176
186
|
"module": "@b9g/cache-redis",
|
|
177
187
|
"export": "RedisCache",
|
|
178
|
-
"url": "REDIS_URL"
|
|
188
|
+
"url": "$REDIS_URL"
|
|
179
189
|
}
|
|
180
190
|
},
|
|
181
191
|
"directories": {
|
|
182
192
|
"uploads": {
|
|
183
193
|
"module": "@b9g/filesystem-s3",
|
|
184
194
|
"export": "S3Directory",
|
|
185
|
-
"bucket": "S3_BUCKET"
|
|
195
|
+
"bucket": "$S3_BUCKET"
|
|
186
196
|
}
|
|
187
197
|
},
|
|
188
198
|
"databases": {
|
|
189
199
|
"main": {
|
|
190
200
|
"module": "@b9g/zen/bun",
|
|
191
|
-
"url": "DATABASE_URL"
|
|
201
|
+
"url": "$DATABASE_URL"
|
|
192
202
|
}
|
|
193
203
|
},
|
|
194
204
|
"logging": {
|
|
@@ -213,7 +223,7 @@ Configure cache backends using `module` and `export`:
|
|
|
213
223
|
"sessions": {
|
|
214
224
|
"module": "@b9g/cache-redis",
|
|
215
225
|
"export": "RedisCache",
|
|
216
|
-
"url": "REDIS_URL"
|
|
226
|
+
"url": "$REDIS_URL"
|
|
217
227
|
}
|
|
218
228
|
}
|
|
219
229
|
}
|
|
@@ -292,7 +302,7 @@ Configure database drivers using the same `module`/`export` pattern:
|
|
|
292
302
|
"databases": {
|
|
293
303
|
"main": {
|
|
294
304
|
"module": "@b9g/zen/bun",
|
|
295
|
-
"url": "DATABASE_URL"
|
|
305
|
+
"url": "$DATABASE_URL"
|
|
296
306
|
}
|
|
297
307
|
}
|
|
298
308
|
}
|
|
@@ -359,7 +369,7 @@ $DATADIR/uploads → joins env var with path segment
|
|
|
359
369
|
|
|
360
370
|
```json
|
|
361
371
|
{
|
|
362
|
-
"port": "$PORT ||
|
|
372
|
+
"port": "$PORT || 7777",
|
|
363
373
|
"host": "$HOST || 0.0.0.0",
|
|
364
374
|
"directories": {
|
|
365
375
|
"server": { "path": "[outdir]/server" },
|
|
@@ -388,17 +398,17 @@ console.log(config.port); // Resolved value
|
|
|
388
398
|
| Package | Description |
|
|
389
399
|
|---------|-------------|
|
|
390
400
|
| `@b9g/shovel` | CLI for development and deployment |
|
|
391
|
-
| `@b9g/platform` | Core runtime and platform APIs |
|
|
392
|
-
| `@b9g/platform-node` | Node.js adapter |
|
|
393
|
-
| `@b9g/platform-bun` | Bun.js adapter |
|
|
394
|
-
| `@b9g/platform-cloudflare` | Cloudflare Workers adapter |
|
|
395
401
|
| `@b9g/router` | URLPattern-based routing with middleware |
|
|
396
402
|
| `@b9g/cache` | Cache API implementation |
|
|
397
403
|
| `@b9g/filesystem` | File System Access implementation |
|
|
398
|
-
| `@b9g/match-pattern` | URLPattern with extensions (100% WPT) |
|
|
399
404
|
| `@b9g/async-context` | AsyncContext.Variable implementation |
|
|
400
405
|
| `@b9g/http-errors` | Standard HTTP error classes |
|
|
401
406
|
| `@b9g/assets` | Static asset handling |
|
|
407
|
+
| `@b9g/platform` | Core runtime and platform APIs |
|
|
408
|
+
| `@b9g/platform-node` | Node.js adapter |
|
|
409
|
+
| `@b9g/platform-bun` | Bun adapter |
|
|
410
|
+
| `@b9g/platform-cloudflare` | Cloudflare Workers adapter |
|
|
411
|
+
| `@b9g/match-pattern` | URLPattern with extensions (100% WPT) |
|
|
402
412
|
|
|
403
413
|
## License
|
|
404
414
|
|
package/bin/cli.js
CHANGED
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
DEFAULTS,
|
|
6
6
|
findProjectRoot,
|
|
7
7
|
loadConfig
|
|
8
|
-
} from "../src/_chunks/chunk-
|
|
8
|
+
} from "../src/_chunks/chunk-7GONPLNW.js";
|
|
9
9
|
|
|
10
10
|
// bin/cli.ts
|
|
11
11
|
import { resolve, relative } from "path";
|
|
@@ -75,15 +75,23 @@ program.command("develop <entrypoint>").description("Start development server wi
|
|
|
75
75
|
DEFAULTS.WORKERS
|
|
76
76
|
).option("--platform <name>", "Runtime platform (node, cloudflare, bun)").action(async (entrypoint, options) => {
|
|
77
77
|
checkPlatformReexec(options);
|
|
78
|
-
const { developCommand } = await import("../src/_chunks/develop-
|
|
78
|
+
const { developCommand } = await import("../src/_chunks/develop-JUQG2G7M.js");
|
|
79
79
|
await developCommand(entrypoint, options, config);
|
|
80
80
|
});
|
|
81
|
+
program.command("create [name]").description("Create a new Shovel project").action(async (name) => {
|
|
82
|
+
if (name) {
|
|
83
|
+
process.argv = [process.argv[0], process.argv[1], name];
|
|
84
|
+
} else {
|
|
85
|
+
process.argv = [process.argv[0], process.argv[1]];
|
|
86
|
+
}
|
|
87
|
+
await import("./create.js");
|
|
88
|
+
});
|
|
81
89
|
program.command("build <entrypoint>").description("Build app for production").option("--platform <name>", "Runtime platform (node, cloudflare, bun)").option(
|
|
82
90
|
"--lifecycle [stage]",
|
|
83
91
|
"Run ServiceWorker lifecycle after build (install or activate, default: activate)"
|
|
84
92
|
).action(async (entrypoint, options) => {
|
|
85
93
|
checkPlatformReexec(options);
|
|
86
|
-
const { buildCommand } = await import("../src/_chunks/build-
|
|
94
|
+
const { buildCommand } = await import("../src/_chunks/build-KBQU2OA7.js");
|
|
87
95
|
await buildCommand(entrypoint, options, config);
|
|
88
96
|
process.exit(0);
|
|
89
97
|
});
|
package/bin/create.js
CHANGED
|
@@ -134,7 +134,7 @@ async function main() {
|
|
|
134
134
|
console.info(` npm install`);
|
|
135
135
|
console.info(` npm run dev`);
|
|
136
136
|
console.info("");
|
|
137
|
-
console.info("Your app will be available at: http://localhost:
|
|
137
|
+
console.info("Your app will be available at: http://localhost:7777");
|
|
138
138
|
console.info("");
|
|
139
139
|
} catch (error) {
|
|
140
140
|
s.stop("Failed to create project");
|
|
@@ -497,7 +497,7 @@ npm install
|
|
|
497
497
|
npm run dev
|
|
498
498
|
\`\`\`
|
|
499
499
|
|
|
500
|
-
Open http://localhost:
|
|
500
|
+
Open http://localhost:7777
|
|
501
501
|
|
|
502
502
|
## Scripts
|
|
503
503
|
|
package/package.json
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@b9g/shovel",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.4",
|
|
4
4
|
"description": "ServiceWorker-first universal deployment platform. Write ServiceWorker apps once, deploy anywhere (Node/Bun/Cloudflare). Registry-based multi-app orchestration.",
|
|
5
5
|
"license": "MIT",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "https://github.com/bikeshaving/shovel.git"
|
|
9
|
+
},
|
|
6
10
|
"bin": {
|
|
7
11
|
"shovel": "bin/cli.js",
|
|
8
12
|
"create-shovel": "bin/create.js",
|
|
@@ -10,15 +14,15 @@
|
|
|
10
14
|
"create": "bin/create.js"
|
|
11
15
|
},
|
|
12
16
|
"dependencies": {
|
|
13
|
-
"@b9g/async-context": "^0.2.
|
|
14
|
-
"@b9g/cache": "^0.2.
|
|
15
|
-
"@b9g/filesystem": "^0.1.
|
|
16
|
-
"@b9g/http-errors": "^0.2.
|
|
17
|
-
"@b9g/node-webworker": "^0.2.
|
|
18
|
-
"@b9g/platform": "^0.1.
|
|
19
|
-
"@b9g/platform-bun": "^0.1.
|
|
20
|
-
"@b9g/platform-cloudflare": "^0.1.
|
|
21
|
-
"@b9g/platform-node": "^0.1.
|
|
17
|
+
"@b9g/async-context": "^0.2.1",
|
|
18
|
+
"@b9g/cache": "^0.2.1",
|
|
19
|
+
"@b9g/filesystem": "^0.1.9",
|
|
20
|
+
"@b9g/http-errors": "^0.2.1",
|
|
21
|
+
"@b9g/node-webworker": "^0.2.1",
|
|
22
|
+
"@b9g/platform": "^0.1.16",
|
|
23
|
+
"@b9g/platform-bun": "^0.1.14",
|
|
24
|
+
"@b9g/platform-cloudflare": "^0.1.14",
|
|
25
|
+
"@b9g/platform-node": "^0.1.16",
|
|
22
26
|
"@clack/prompts": "^0.7.0",
|
|
23
27
|
"@esbuild-plugins/node-globals-polyfill": "^0.2.3",
|
|
24
28
|
"@esbuild-plugins/node-modules-polyfill": "^0.2.2",
|
|
@@ -29,18 +33,11 @@
|
|
|
29
33
|
"zod": "^3.23.0"
|
|
30
34
|
},
|
|
31
35
|
"devDependencies": {
|
|
32
|
-
"@b9g/assets": "^0.2.
|
|
33
|
-
"@b9g/cache": "^0.2.0",
|
|
36
|
+
"@b9g/assets": "^0.2.1",
|
|
34
37
|
"@b9g/crank": "^0.7.2",
|
|
35
|
-
"@logtape/file": "^1.0.0",
|
|
36
|
-
"@b9g/filesystem": "^0.1.8",
|
|
37
|
-
"@b9g/http-errors": "^0.2.0",
|
|
38
38
|
"@b9g/libuild": "^0.1.22",
|
|
39
|
-
"@b9g/
|
|
40
|
-
"@
|
|
41
|
-
"@b9g/platform-cloudflare": "^0.1.13",
|
|
42
|
-
"@b9g/platform-node": "^0.1.15",
|
|
43
|
-
"@b9g/router": "^0.2.0",
|
|
39
|
+
"@b9g/router": "^0.2.1",
|
|
40
|
+
"@logtape/file": "^1.0.0",
|
|
44
41
|
"@types/bun": "^1.3.4",
|
|
45
42
|
"@typescript-eslint/eslint-plugin": "^8.0.0",
|
|
46
43
|
"@typescript-eslint/parser": "^8.0.0",
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
2
|
ServerBundler,
|
|
3
3
|
loadPlatformModule
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-ABGHNBNM.js";
|
|
5
5
|
import {
|
|
6
6
|
findProjectRoot,
|
|
7
7
|
findWorkspaceRoot
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-7GONPLNW.js";
|
|
9
9
|
|
|
10
10
|
// src/commands/build.ts
|
|
11
11
|
import { resolve, join, dirname, basename } from "path";
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
generateStorageTypes,
|
|
5
5
|
getNodeModulesPath,
|
|
6
6
|
loadRawConfig
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-7GONPLNW.js";
|
|
8
8
|
|
|
9
9
|
// src/utils/bundler.ts
|
|
10
10
|
import * as ESBuild2 from "esbuild";
|
|
@@ -98,6 +98,7 @@ function assetsPlugin(options = {}) {
|
|
|
98
98
|
let content;
|
|
99
99
|
let outputExt = ext;
|
|
100
100
|
let mimeType;
|
|
101
|
+
let chunkFiles = [];
|
|
101
102
|
if (needsTranspilation) {
|
|
102
103
|
const defaultPlugins = [
|
|
103
104
|
NodeModulesPolyfillPlugin(),
|
|
@@ -111,11 +112,12 @@ function assetsPlugin(options = {}) {
|
|
|
111
112
|
entryPoints: [args.path],
|
|
112
113
|
bundle: true,
|
|
113
114
|
format: "esm",
|
|
115
|
+
splitting: true,
|
|
114
116
|
target: ["es2022", "chrome90"],
|
|
115
117
|
platform: "browser",
|
|
116
118
|
write: false,
|
|
117
119
|
minify: true,
|
|
118
|
-
// outdir is required for esbuild to know where to put extracted CSS
|
|
120
|
+
// outdir is required for esbuild to know where to put extracted CSS and chunks
|
|
119
121
|
outdir: outDir,
|
|
120
122
|
// Apply polyfills and user-provided build options
|
|
121
123
|
plugins,
|
|
@@ -152,10 +154,10 @@ function assetsPlugin(options = {}) {
|
|
|
152
154
|
outputExt = ".css";
|
|
153
155
|
mimeType = "text/css";
|
|
154
156
|
} else {
|
|
155
|
-
const
|
|
157
|
+
const jsOutputFiles = result.outputFiles.filter(
|
|
156
158
|
(f) => f.path.endsWith(".js")
|
|
157
159
|
);
|
|
158
|
-
if (
|
|
160
|
+
if (jsOutputFiles.length === 0) {
|
|
159
161
|
return {
|
|
160
162
|
errors: [
|
|
161
163
|
{
|
|
@@ -164,9 +166,30 @@ function assetsPlugin(options = {}) {
|
|
|
164
166
|
]
|
|
165
167
|
};
|
|
166
168
|
}
|
|
167
|
-
|
|
169
|
+
const entryBaseName = name + ".js";
|
|
170
|
+
const entryFile = jsOutputFiles.find(
|
|
171
|
+
(f) => basename(f.path) === entryBaseName
|
|
172
|
+
);
|
|
173
|
+
if (!entryFile) {
|
|
174
|
+
return {
|
|
175
|
+
errors: [
|
|
176
|
+
{
|
|
177
|
+
text: `Could not find entry file ${entryBaseName} in output`
|
|
178
|
+
}
|
|
179
|
+
]
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
content = Buffer.from(entryFile.text);
|
|
168
183
|
outputExt = ".js";
|
|
169
184
|
mimeType = "application/javascript";
|
|
185
|
+
for (const file of jsOutputFiles) {
|
|
186
|
+
if (file === entryFile)
|
|
187
|
+
continue;
|
|
188
|
+
chunkFiles.push({
|
|
189
|
+
filename: basename(file.path),
|
|
190
|
+
content: Buffer.from(file.text)
|
|
191
|
+
});
|
|
192
|
+
}
|
|
170
193
|
}
|
|
171
194
|
} else if (needsCSSBundling) {
|
|
172
195
|
const entryPath = args.path;
|
|
@@ -274,6 +297,23 @@ function assetsPlugin(options = {}) {
|
|
|
274
297
|
}
|
|
275
298
|
const outputPath = join(outputDir, filename);
|
|
276
299
|
writeFileSync(outputPath, content);
|
|
300
|
+
for (const chunk of chunkFiles) {
|
|
301
|
+
const chunkPath = join(outputDir, chunk.filename);
|
|
302
|
+
writeFileSync(chunkPath, chunk.content);
|
|
303
|
+
const chunkUrl = `${basePath}${chunk.filename}`;
|
|
304
|
+
const chunkHash = createHash("sha256").update(chunk.content).digest("hex").slice(0, HASH_LENGTH);
|
|
305
|
+
manifest.assets[chunkUrl] = {
|
|
306
|
+
source: chunk.filename,
|
|
307
|
+
output: chunk.filename,
|
|
308
|
+
url: chunkUrl,
|
|
309
|
+
hash: chunkHash,
|
|
310
|
+
size: chunk.content.length,
|
|
311
|
+
type: "application/javascript"
|
|
312
|
+
};
|
|
313
|
+
if (sharedManifest) {
|
|
314
|
+
sharedManifest.assets[chunkUrl] = manifest.assets[chunkUrl];
|
|
315
|
+
}
|
|
316
|
+
}
|
|
277
317
|
const sourcePath = relative(process.cwd(), args.path);
|
|
278
318
|
const manifestEntry = {
|
|
279
319
|
source: sourcePath,
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
ServerBundler,
|
|
3
3
|
loadPlatformModule
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-ABGHNBNM.js";
|
|
5
5
|
import {
|
|
6
6
|
DEFAULTS
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-7GONPLNW.js";
|
|
8
8
|
|
|
9
9
|
// src/commands/develop.ts
|
|
10
10
|
import { getLogger } from "@logtape/logtape";
|