@b9g/shovel 0.2.0-beta.10 → 0.2.0-beta.11
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 +160 -0
- package/README.md +301 -42
- package/bin/cli.js +29 -9
- package/bin/create.js +22 -22
- package/package.json +21 -13
- package/{activate-5LWUTBLL.js → src/_chunks/activate-TP6RQP47.js} +14 -11
- package/src/_chunks/build-V3IPZGKC.js +434 -0
- package/src/_chunks/chunk-ADR5RW57.js +78 -0
- package/src/_chunks/chunk-GRAFMTEH.js +1150 -0
- package/src/_chunks/chunk-JJFM7PO2.js +468 -0
- package/src/_chunks/develop-A7EU2ZDY.js +404 -0
- package/{info-PRYEMZS4.js → src/_chunks/info-TDUY3FZN.js} +1 -1
- package/build-NDUV2F2Z.js +0 -386
- package/chunk-CSH7M4MK.js +0 -861
- package/chunk-ILQUUH2L.js +0 -164
- package/develop-5ORIPB7M.js +0 -264
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to Shovel will be documented in this file.
|
|
4
|
+
|
|
5
|
+
## [0.2.0-beta.11] - 2026-01-10
|
|
6
|
+
|
|
7
|
+
### Changes since beta.10
|
|
8
|
+
- **ESBuild configuration support (#18)** - Custom esbuild options in shovel.json
|
|
9
|
+
- **Config expression syntax** - `$PORT || 3000`, `$DATABASE_URL ?? null`
|
|
10
|
+
- **Null fallback fix** - Allow intentional null fallbacks in config expressions
|
|
11
|
+
- **DatabaseStorage API redesign** - New open/get pattern with IndexedDB-style migrations
|
|
12
|
+
- **Migrated from Drizzle to @b9g/zen** - Simpler, more portable database layer
|
|
13
|
+
- **Logging DX improvements** - Better defaults, consolidated categories
|
|
14
|
+
- **`impl` key unification** - Simplified resource configuration
|
|
15
|
+
- **CI/lint enforcement** - ESLint and Prettier standardized
|
|
16
|
+
- **Documentation** - Comprehensive docs for all APIs
|
|
17
|
+
|
|
18
|
+
### Package Updates
|
|
19
|
+
- `@b9g/router` → 0.2.0-beta.1 (CORS middleware, trailingSlash moved)
|
|
20
|
+
- `@b9g/node-webworker` → 0.2.0-beta.1 (CloseEvent, onclose, env option)
|
|
21
|
+
- `@b9g/cache-redis` → 0.2.0-beta.1 (logger category fix)
|
|
22
|
+
- `@b9g/assets` → 0.2.0-beta.0
|
|
23
|
+
- `@b9g/async-context` → 0.2.0-beta.0
|
|
24
|
+
- `@b9g/cache` → 0.2.0-beta.0
|
|
25
|
+
- `@b9g/http-errors` → 0.2.0-beta.0
|
|
26
|
+
- `@b9g/match-pattern` → 0.2.0-beta.0
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## [0.2.0-beta.10] - Previous Beta
|
|
31
|
+
|
|
32
|
+
This is a major release that establishes Shovel as a complete ServiceWorker-based meta-framework. The 0.2.0 beta introduces locked-down APIs for core packages, a unified configuration system, and comprehensive platform support.
|
|
33
|
+
|
|
34
|
+
### Breaking Changes
|
|
35
|
+
|
|
36
|
+
- **`self.buckets` renamed to `self.directories`** - The file system API now uses `directories` to align with web standards terminology
|
|
37
|
+
- **`@b9g/router` middleware moved** - `trailingSlash` middleware moved from main export to `@b9g/router/middleware`
|
|
38
|
+
- **`self.loggers.get()` signature changed** - Now takes an array: `self.loggers.get(["app", "db"])` instead of dot notation
|
|
39
|
+
- **Config `module`/`export` unified to `impl`** - Resource configurations now use a single `impl` key for reified implementations
|
|
40
|
+
|
|
41
|
+
### New Features
|
|
42
|
+
|
|
43
|
+
#### Consistent Worker Execution Model (#17)
|
|
44
|
+
ServiceWorker code now ALWAYS runs in a worker thread, never the main thread. This ensures:
|
|
45
|
+
- Same globals/environment in dev and prod (eliminates mode-only bugs)
|
|
46
|
+
- Worker isolation preserved
|
|
47
|
+
- Simplified mental model
|
|
48
|
+
|
|
49
|
+
#### ESBuild Configuration Support (#18)
|
|
50
|
+
Custom ESBuild options can now be specified in `shovel.json`:
|
|
51
|
+
```json
|
|
52
|
+
{
|
|
53
|
+
"esbuild": {
|
|
54
|
+
"external": ["lightningcss"],
|
|
55
|
+
"define": { "DEBUG": "true" }
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
#### Config Expression Syntax
|
|
61
|
+
Environment variables and expressions in `shovel.json`:
|
|
62
|
+
```json
|
|
63
|
+
{
|
|
64
|
+
"port": "$PORT || 3000",
|
|
65
|
+
"databases": {
|
|
66
|
+
"main": {
|
|
67
|
+
"url": "$DATABASE_URL"
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
#### Comprehensive Logging System
|
|
74
|
+
- Built-in LogTape integration with console sink by default
|
|
75
|
+
- Configurable sinks (file, OpenTelemetry, Sentry, etc.)
|
|
76
|
+
- Category-based log levels
|
|
77
|
+
- `shovel` category logs at `info` level by default
|
|
78
|
+
|
|
79
|
+
#### Database Storage API
|
|
80
|
+
New `self.databases` API with IndexedDB-style migrations:
|
|
81
|
+
```typescript
|
|
82
|
+
self.addEventListener("activate", (event) => {
|
|
83
|
+
event.waitUntil(
|
|
84
|
+
databases.open("main", 2, (e) => {
|
|
85
|
+
e.waitUntil(e.db.exec`CREATE TABLE users (...)`);
|
|
86
|
+
})
|
|
87
|
+
);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
// In fetch handlers
|
|
91
|
+
const db = databases.get("main");
|
|
92
|
+
const users = await db.all`SELECT * FROM users`;
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
#### CORS Middleware
|
|
96
|
+
New CORS middleware in `@b9g/router/middleware`:
|
|
97
|
+
```typescript
|
|
98
|
+
import { cors } from "@b9g/router/middleware";
|
|
99
|
+
router.use(cors({ origin: "https://example.com" }));
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### Package Updates
|
|
103
|
+
|
|
104
|
+
#### Locked at 0.2.0-beta.0 (API stable)
|
|
105
|
+
- `@b9g/async-context` - AsyncContext polyfill
|
|
106
|
+
- `@b9g/match-pattern` - URLPattern implementation
|
|
107
|
+
- `@b9g/assets` - Static asset pipeline with content hashing
|
|
108
|
+
- `@b9g/cache` - Cache API implementation (memory, postmessage)
|
|
109
|
+
- `@b9g/http-errors` - HTTP error classes
|
|
110
|
+
|
|
111
|
+
#### Updated to 0.2.0-beta.1
|
|
112
|
+
- `@b9g/router` - Added CORS middleware, moved trailingSlash to /middleware
|
|
113
|
+
- `@b9g/node-webworker` - Added CloseEvent, onclose handler, env option
|
|
114
|
+
- `@b9g/cache-redis` - Logger category updates
|
|
115
|
+
|
|
116
|
+
#### Still Evolving (0.1.x)
|
|
117
|
+
- `@b9g/platform` - Core runtime and platform abstraction
|
|
118
|
+
- `@b9g/platform-bun` - Bun platform adapter
|
|
119
|
+
- `@b9g/platform-node` - Node.js platform adapter
|
|
120
|
+
- `@b9g/platform-cloudflare` - Cloudflare Workers adapter
|
|
121
|
+
- `@b9g/filesystem` - File System Access API implementation
|
|
122
|
+
- `@b9g/filesystem-s3` - S3 filesystem adapter
|
|
123
|
+
|
|
124
|
+
### CLI Changes
|
|
125
|
+
|
|
126
|
+
- `shovel develop` - Development server with hot reload (note: `dev` alias removed)
|
|
127
|
+
- `shovel build` - Production build
|
|
128
|
+
- `shovel activate` - Static site generation
|
|
129
|
+
- Removed `--verbose` flags (use logging config instead)
|
|
130
|
+
|
|
131
|
+
### Infrastructure
|
|
132
|
+
|
|
133
|
+
- Migrated from Drizzle ORM to `@b9g/zen` for database operations
|
|
134
|
+
- Added GitHub Actions CI with parallel test execution
|
|
135
|
+
- ESLint and Prettier configuration standardized
|
|
136
|
+
- Comprehensive test suites with `bun:test`
|
|
137
|
+
|
|
138
|
+
### Documentation
|
|
139
|
+
|
|
140
|
+
New documentation pages:
|
|
141
|
+
- Getting Started guide
|
|
142
|
+
- CLI reference
|
|
143
|
+
- Configuration (shovel.json) reference
|
|
144
|
+
- Deployment guide
|
|
145
|
+
- ServiceWorker lifecycle
|
|
146
|
+
- Routing and middleware
|
|
147
|
+
- Storage APIs (databases, caches, directories)
|
|
148
|
+
- Cookies and AsyncContext
|
|
149
|
+
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
## [0.1.x] - Previous Releases
|
|
153
|
+
|
|
154
|
+
Initial development releases establishing core architecture:
|
|
155
|
+
- ServiceWorker-based request handling
|
|
156
|
+
- Platform abstraction layer
|
|
157
|
+
- Router with generator-based middleware
|
|
158
|
+
- Cache API implementations
|
|
159
|
+
- File System Access API
|
|
160
|
+
- Hot reload in development
|
package/README.md
CHANGED
|
@@ -1,49 +1,23 @@
|
|
|
1
|
-
# Shovel
|
|
1
|
+
# Shovel.js
|
|
2
2
|
|
|
3
|
-
**The
|
|
3
|
+
**The portable meta-framework built on web standards.**
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Shovel is a CLI platform for developing and deploying service workers as application servers.
|
|
6
6
|
|
|
7
7
|
```javascript
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
import {Router} from "@b9g/router";
|
|
9
|
+
const router = new Router();
|
|
10
|
+
|
|
11
|
+
router.route("/").get(() => new Response("Hello world"));
|
|
12
|
+
|
|
13
|
+
self.addEventListener("fetch", (ev) => {
|
|
14
|
+
ev.respondWith(router.handle(ev.request));
|
|
11
15
|
});
|
|
12
16
|
```
|
|
13
17
|
|
|
14
18
|
```bash
|
|
15
|
-
|
|
19
|
+
shovel develop app.js
|
|
16
20
|
```
|
|
17
|
-
|
|
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.directories` | [FileSystem API](https://fs.spec.whatwg.org/) | 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
21
|
## Quick Start
|
|
48
22
|
|
|
49
23
|
```javascript
|
|
@@ -54,12 +28,12 @@ const router = new Router();
|
|
|
54
28
|
|
|
55
29
|
router.route("/").get(() => new Response("Hello World"));
|
|
56
30
|
|
|
57
|
-
router.route("/
|
|
58
|
-
return Response
|
|
31
|
+
router.route("/greet/:name").get((request, {params}) => {
|
|
32
|
+
return new Response(`Hello ${params.name}`);
|
|
59
33
|
});
|
|
60
34
|
|
|
61
35
|
self.addEventListener("fetch", (event) => {
|
|
62
|
-
event.respondWith(router.
|
|
36
|
+
event.respondWith(router.handle(event.request));
|
|
63
37
|
});
|
|
64
38
|
```
|
|
65
39
|
|
|
@@ -76,6 +50,51 @@ npx @b9g/shovel build app.js --platform=bun
|
|
|
76
50
|
npx @b9g/shovel build app.js --platform=cloudflare
|
|
77
51
|
```
|
|
78
52
|
|
|
53
|
+
|
|
54
|
+
## Web Standards
|
|
55
|
+
Shovel is obsessively standards-first. All Shovel APIs use web standards, and Shovel implements/shims useful standards when they're missing.
|
|
56
|
+
|
|
57
|
+
| API | Standard | Purpose |
|
|
58
|
+
|-----|----------|--------------|
|
|
59
|
+
| `fetch()` | [Fetch](https://fetch.spec.whatwg.org) | Networking |
|
|
60
|
+
| `"install"`, `"activate"`, `"fetch"` events | [Service Workers](https://w3c.github.io/ServiceWorker/) | Server lifecycle |
|
|
61
|
+
| `AsyncContext.Variable` | [TC39 Stage 2](https://github.com/tc39/proposal-async-context) | Request-scoped state |
|
|
62
|
+
| `self.caches` | [Cache API](https://w3c.github.io/ServiceWorker/#cache-interface) | Response caching |
|
|
63
|
+
| `self.directories` | [FileSystem API](https://fs.spec.whatwg.org/) | Storage (local, S3, R2) |
|
|
64
|
+
| `self.cookieStore` | [CookieStore API](https://cookiestore.spec.whatwg.org) | Cookie management |
|
|
65
|
+
| `URLPattern` | [URLPattern](https://urlpattern.spec.whatwg.org/) | Route matching |
|
|
66
|
+
|
|
67
|
+
Your code uses standards. Shovel makes them work everywhere.
|
|
68
|
+
|
|
69
|
+
## Meta-Framework
|
|
70
|
+
|
|
71
|
+
Shovel is a meta-framework: it provides and implements primitives rather than opinions. Instead of dictating how you build, it gives you portable building blocks that work everywhere.
|
|
72
|
+
|
|
73
|
+
## True Portability
|
|
74
|
+
|
|
75
|
+
Same code, any runtime, any rendering strategy:
|
|
76
|
+
|
|
77
|
+
- **Server runtimes**: Node.js, Bun, Cloudflare Workers
|
|
78
|
+
- **Browser ServiceWorkers**: The same app can run as a PWA
|
|
79
|
+
- **Universal rendering**: Dynamic, static, or client-side
|
|
80
|
+
|
|
81
|
+
The core abstraction is the **ServiceWorker-style storage pattern**. Globals provide a consistent API for common web concerns:
|
|
82
|
+
|
|
83
|
+
```javascript
|
|
84
|
+
const cache = await self.caches.open("sessions"); // Cache API
|
|
85
|
+
const dir = await self.directories.open("uploads"); // FileSystem API
|
|
86
|
+
const db = self.databases.get("main"); // Zen DB (opened on activate)
|
|
87
|
+
const logger = self.loggers.get(["app", "requests"]); // LogTape
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Each storage type is:
|
|
91
|
+
- **Lazy** - connections created on first `open()`, cached thereafter
|
|
92
|
+
- **Configured uniformly** - all are configured by `shovel.json`
|
|
93
|
+
- **Platform-aware** - sensible defaults per platform, override what you need
|
|
94
|
+
|
|
95
|
+
This pattern means your app logic stays clean. Swap Redis for memory cache, S3 for local filesystem, Postgres for SQLite - change the config, not the code.
|
|
96
|
+
|
|
97
|
+
|
|
79
98
|
## Platform APIs
|
|
80
99
|
|
|
81
100
|
```javascript
|
|
@@ -121,6 +140,247 @@ Assets are served via the platform's best option:
|
|
|
121
140
|
- **Cloudflare**: Workers Assets (edge-cached, zero config)
|
|
122
141
|
- **Node/Bun**: Static file middleware or directory storage
|
|
123
142
|
|
|
143
|
+
## Configuration
|
|
144
|
+
|
|
145
|
+
Configure Shovel using `shovel.json` in your project root.
|
|
146
|
+
|
|
147
|
+
### Philosophy
|
|
148
|
+
|
|
149
|
+
Shovel's configuration follows these principles:
|
|
150
|
+
|
|
151
|
+
1. **Platform Defaults, User Overrides** - Each platform provides sensible defaults. You only configure what you want to change.
|
|
152
|
+
|
|
153
|
+
2. **Uniform Interface** - Caches, directories, databases, and loggers all use the same `{ module, export, ...options }` pattern. No magic strings or builtin aliases.
|
|
154
|
+
|
|
155
|
+
3. **Layered Resolution** - For any cache or directory name:
|
|
156
|
+
- If config specifies `module`/`export` → use that
|
|
157
|
+
- Otherwise → use platform default
|
|
158
|
+
|
|
159
|
+
4. **Platform Re-exports** - Each platform exports `DefaultCache` representing what makes sense for that environment:
|
|
160
|
+
- Cloudflare: Native Cache API
|
|
161
|
+
- Bun/Node: MemoryCache
|
|
162
|
+
|
|
163
|
+
5. **Transparency** - Config is what you see. Every backend is an explicit module path, making it easy to debug and trace.
|
|
164
|
+
|
|
165
|
+
### Basic Config
|
|
166
|
+
|
|
167
|
+
```json
|
|
168
|
+
{
|
|
169
|
+
"port": "PORT || 3000",
|
|
170
|
+
"host": "HOST || localhost",
|
|
171
|
+
"workers": "WORKERS ?? 1",
|
|
172
|
+
"caches": {
|
|
173
|
+
"sessions": {
|
|
174
|
+
"module": "@b9g/cache-redis",
|
|
175
|
+
"export": "RedisCache",
|
|
176
|
+
"url": "REDIS_URL"
|
|
177
|
+
}
|
|
178
|
+
},
|
|
179
|
+
"directories": {
|
|
180
|
+
"uploads": {
|
|
181
|
+
"module": "@b9g/filesystem-s3",
|
|
182
|
+
"export": "S3Directory",
|
|
183
|
+
"bucket": "S3_BUCKET"
|
|
184
|
+
}
|
|
185
|
+
},
|
|
186
|
+
"databases": {
|
|
187
|
+
"main": {
|
|
188
|
+
"module": "@b9g/zen/bun",
|
|
189
|
+
"url": "DATABASE_URL"
|
|
190
|
+
}
|
|
191
|
+
},
|
|
192
|
+
"logging": {
|
|
193
|
+
"loggers": [
|
|
194
|
+
{"category": ["app"], "level": "info", "sinks": ["console"]}
|
|
195
|
+
]
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
### Caches
|
|
201
|
+
|
|
202
|
+
Configure cache backends using `module` and `export`:
|
|
203
|
+
|
|
204
|
+
```json
|
|
205
|
+
{
|
|
206
|
+
"caches": {
|
|
207
|
+
"api-responses": {
|
|
208
|
+
"module": "@b9g/cache/memory",
|
|
209
|
+
"export": "MemoryCache"
|
|
210
|
+
},
|
|
211
|
+
"sessions": {
|
|
212
|
+
"module": "@b9g/cache-redis",
|
|
213
|
+
"export": "RedisCache",
|
|
214
|
+
"url": "REDIS_URL"
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
- **Default**: Platform's `DefaultCache` when no config specified (MemoryCache on Bun/Node, native on Cloudflare)
|
|
221
|
+
- **Pattern matching**: Use wildcards like `"api-*"` to match multiple cache names
|
|
222
|
+
- **Empty config**: `"my-cache": {}` uses platform default explicitly
|
|
223
|
+
|
|
224
|
+
### Directories
|
|
225
|
+
|
|
226
|
+
Configure directory backends. Platforms provide defaults for well-known directories (`server`, `public`, `tmp`):
|
|
227
|
+
|
|
228
|
+
```json
|
|
229
|
+
{
|
|
230
|
+
"directories": {
|
|
231
|
+
"uploads": {
|
|
232
|
+
"module": "@b9g/filesystem-s3",
|
|
233
|
+
"export": "S3Directory",
|
|
234
|
+
"bucket": "MY_BUCKET",
|
|
235
|
+
"region": "us-east-1"
|
|
236
|
+
},
|
|
237
|
+
"data": {
|
|
238
|
+
"module": "@b9g/filesystem/node-fs",
|
|
239
|
+
"export": "NodeFSDirectory",
|
|
240
|
+
"path": "./data"
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
- **Well-known defaults**: `server` (dist/server), `public` (dist/public), `tmp` (OS temp)
|
|
247
|
+
- **Custom directories**: Must be explicitly configured
|
|
248
|
+
|
|
249
|
+
### Logging
|
|
250
|
+
|
|
251
|
+
Shovel uses [LogTape](https://logtape.org/) for logging:
|
|
252
|
+
|
|
253
|
+
```typescript
|
|
254
|
+
const logger = self.loggers.get(["shovel", "myapp"]);
|
|
255
|
+
logger.info`Request received: ${request.url}`;
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
**Zero-config logging**: Use the `["shovel", ...]` category hierarchy to inherit Shovel's default logging (info level to console). No configuration needed.
|
|
259
|
+
|
|
260
|
+
For custom configuration, use `shovel.json`:
|
|
261
|
+
|
|
262
|
+
```json
|
|
263
|
+
{
|
|
264
|
+
"logging": {
|
|
265
|
+
"sinks": {
|
|
266
|
+
"file": {
|
|
267
|
+
"module": "@logtape/logtape",
|
|
268
|
+
"export": "getFileSink",
|
|
269
|
+
"path": "./logs/app.log"
|
|
270
|
+
}
|
|
271
|
+
},
|
|
272
|
+
"loggers": [
|
|
273
|
+
{"category": ["myapp"], "level": "info", "sinks": ["console"]},
|
|
274
|
+
{"category": ["myapp", "db"], "level": "debug", "sinks": ["file"]}
|
|
275
|
+
]
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
- **Console sink is implicit** - always available as `"console"`
|
|
281
|
+
- **Category hierarchy** - `["myapp", "db"]` inherits from `["myapp"]`
|
|
282
|
+
- **parentSinks** - use `"override"` to replace parent sinks instead of inheriting
|
|
283
|
+
|
|
284
|
+
### Databases
|
|
285
|
+
|
|
286
|
+
Configure database drivers using the same `module`/`export` pattern:
|
|
287
|
+
|
|
288
|
+
```json
|
|
289
|
+
{
|
|
290
|
+
"databases": {
|
|
291
|
+
"main": {
|
|
292
|
+
"module": "@b9g/zen/bun",
|
|
293
|
+
"url": "DATABASE_URL"
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
Open databases in `activate` (for migrations), then use `get()` in requests:
|
|
300
|
+
|
|
301
|
+
```javascript
|
|
302
|
+
self.addEventListener("activate", (event) => {
|
|
303
|
+
event.waitUntil(self.databases.open("main", 1, (e) => {
|
|
304
|
+
e.waitUntil(runMigrations(e));
|
|
305
|
+
}));
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
self.addEventListener("fetch", (event) => {
|
|
309
|
+
const db = self.databases.get("main");
|
|
310
|
+
});
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
### Expression Syntax
|
|
314
|
+
|
|
315
|
+
Configuration values support a domain-specific expression language that generates JavaScript code evaluated at runtime.
|
|
316
|
+
|
|
317
|
+
#### Environment Variables
|
|
318
|
+
|
|
319
|
+
```
|
|
320
|
+
$VAR → process.env.VAR
|
|
321
|
+
$VAR || fallback → process.env.VAR || "fallback"
|
|
322
|
+
$VAR ?? fallback → process.env.VAR ?? "fallback"
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
#### Bracket Placeholders
|
|
326
|
+
|
|
327
|
+
| Placeholder | Description | Resolution |
|
|
328
|
+
|-------------|-------------|------------|
|
|
329
|
+
| `[outdir]` | Build output directory | Build time |
|
|
330
|
+
| `[tmpdir]` | OS temp directory | Runtime |
|
|
331
|
+
| `[git]` | Git commit SHA | Build time |
|
|
332
|
+
|
|
333
|
+
The bracket syntax mirrors esbuild/webpack output filename templating (`[name]`, `[hash]`).
|
|
334
|
+
|
|
335
|
+
#### Operators
|
|
336
|
+
|
|
337
|
+
| Operator | Example | Description |
|
|
338
|
+
|----------|---------|-------------|
|
|
339
|
+
| `\|\|` | `$VAR \|\| default` | Logical OR (falsy fallback) |
|
|
340
|
+
| `??` | `$VAR ?? default` | Nullish coalescing |
|
|
341
|
+
| `&&` | `$A && $B` | Logical AND |
|
|
342
|
+
| `? :` | `$ENV === prod ? a : b` | Ternary conditional |
|
|
343
|
+
| `===`, `!==` | `$ENV === production` | Strict equality |
|
|
344
|
+
| `!` | `!$DISABLED` | Logical NOT |
|
|
345
|
+
|
|
346
|
+
#### Path Expressions
|
|
347
|
+
|
|
348
|
+
Path expressions support path segments and relative resolution:
|
|
349
|
+
|
|
350
|
+
```
|
|
351
|
+
$DATADIR/uploads → joins env var with path segment
|
|
352
|
+
[outdir]/server → joins build output with path segment
|
|
353
|
+
./data → resolved to absolute path at build time
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
#### Example
|
|
357
|
+
|
|
358
|
+
```json
|
|
359
|
+
{
|
|
360
|
+
"port": "$PORT || 3000",
|
|
361
|
+
"host": "$HOST || 0.0.0.0",
|
|
362
|
+
"directories": {
|
|
363
|
+
"server": { "path": "[outdir]/server" },
|
|
364
|
+
"public": { "path": "[outdir]/public" },
|
|
365
|
+
"tmp": { "path": "[tmpdir]" },
|
|
366
|
+
"data": { "path": "./data" },
|
|
367
|
+
"cache": { "path": "($CACHE_DIR || [tmpdir])/myapp" }
|
|
368
|
+
},
|
|
369
|
+
"cache": {
|
|
370
|
+
"provider": "$NODE_ENV === production ? redis : memory"
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
Dynamic values (containing `$VAR` or `[tmpdir]`) use getters to ensure evaluation at access time, not module load time.
|
|
376
|
+
|
|
377
|
+
### Access in Code
|
|
378
|
+
|
|
379
|
+
```javascript
|
|
380
|
+
import {config} from "shovel:config";
|
|
381
|
+
console.log(config.port); // Resolved value
|
|
382
|
+
```
|
|
383
|
+
|
|
124
384
|
## Packages
|
|
125
385
|
|
|
126
386
|
| Package | Description |
|
|
@@ -128,7 +388,7 @@ Assets are served via the platform's best option:
|
|
|
128
388
|
| `@b9g/shovel` | CLI for development and deployment |
|
|
129
389
|
| `@b9g/platform` | Core runtime and platform APIs |
|
|
130
390
|
| `@b9g/platform-node` | Node.js adapter |
|
|
131
|
-
| `@b9g/platform-bun` | Bun adapter |
|
|
391
|
+
| `@b9g/platform-bun` | Bun.js adapter |
|
|
132
392
|
| `@b9g/platform-cloudflare` | Cloudflare Workers adapter |
|
|
133
393
|
| `@b9g/router` | URLPattern-based routing with middleware |
|
|
134
394
|
| `@b9g/cache` | Cache API implementation |
|
|
@@ -136,7 +396,6 @@ Assets are served via the platform's best option:
|
|
|
136
396
|
| `@b9g/match-pattern` | URLPattern with extensions (100% WPT) |
|
|
137
397
|
| `@b9g/async-context` | AsyncContext.Variable implementation |
|
|
138
398
|
| `@b9g/http-errors` | Standard HTTP error classes |
|
|
139
|
-
| `@b9g/auth` | OAuth2/PKCE and CORS middleware |
|
|
140
399
|
| `@b9g/assets` | Static asset handling |
|
|
141
400
|
|
|
142
401
|
## License
|
package/bin/cli.js
CHANGED
|
@@ -5,41 +5,61 @@ import {
|
|
|
5
5
|
DEFAULTS,
|
|
6
6
|
findProjectRoot,
|
|
7
7
|
loadConfig
|
|
8
|
-
} from "../chunk-
|
|
8
|
+
} from "../src/_chunks/chunk-GRAFMTEH.js";
|
|
9
9
|
|
|
10
10
|
// bin/cli.ts
|
|
11
|
+
import { resolve } from "path";
|
|
11
12
|
import { configureLogging } from "@b9g/platform/runtime";
|
|
12
13
|
import { Command } from "commander";
|
|
13
14
|
import pkg from "../package.json" with { type: "json" };
|
|
14
15
|
var projectRoot = findProjectRoot();
|
|
15
16
|
var config = loadConfig(projectRoot);
|
|
16
|
-
|
|
17
|
+
async function reifySinks(sinks, baseDir) {
|
|
18
|
+
const reified = {};
|
|
19
|
+
for (const [name, sinkConfig] of Object.entries(sinks ?? {})) {
|
|
20
|
+
const { module: modulePath, export: exportName, ...rest } = sinkConfig;
|
|
21
|
+
if (modulePath) {
|
|
22
|
+
const resolvedPath = modulePath.startsWith("./") || modulePath.startsWith("../") ? resolve(baseDir, modulePath) : modulePath;
|
|
23
|
+
const mod = await import(resolvedPath);
|
|
24
|
+
const impl = exportName ? mod[exportName] : mod.default;
|
|
25
|
+
reified[name] = { ...rest, impl };
|
|
26
|
+
} else if (sinkConfig.impl) {
|
|
27
|
+
reified[name] = sinkConfig;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return reified;
|
|
31
|
+
}
|
|
32
|
+
var reifiedSinks = await reifySinks(config.logging?.sinks, projectRoot);
|
|
33
|
+
await configureLogging({
|
|
34
|
+
sinks: reifiedSinks,
|
|
35
|
+
loggers: config.logging?.loggers
|
|
36
|
+
});
|
|
17
37
|
var program = new Command();
|
|
18
38
|
program.name("shovel").description("Shovel CLI").version(pkg.version);
|
|
19
39
|
program.command("develop <entrypoint>").description("Start development server with hot reload").option("-p, --port <port>", "Port to listen on", DEFAULTS.SERVER.PORT).option("-h, --host <host>", "Host to bind to", DEFAULTS.SERVER.HOST).option(
|
|
20
40
|
"-w, --workers <count>",
|
|
21
41
|
"Number of workers (default: CPU cores)",
|
|
22
42
|
DEFAULTS.WORKERS
|
|
23
|
-
).option("
|
|
24
|
-
const { developCommand } = await import("../develop-
|
|
43
|
+
).option("--platform <name>", "Runtime platform (node, cloudflare, bun)").action(async (entrypoint, options) => {
|
|
44
|
+
const { developCommand } = await import("../src/_chunks/develop-A7EU2ZDY.js");
|
|
25
45
|
await developCommand(entrypoint, options, config);
|
|
26
46
|
});
|
|
27
|
-
program.command("build <entrypoint>").description("Build app for production").option("-w, --workers <count>", "Worker count (defaults to 1)", void 0).option("
|
|
28
|
-
const { buildCommand } = await import("../build-
|
|
47
|
+
program.command("build <entrypoint>").description("Build app for production").option("-w, --workers <count>", "Worker count (defaults to 1)", void 0).option("--platform <name>", "Runtime platform (node, cloudflare, bun)").action(async (entrypoint, options) => {
|
|
48
|
+
const { buildCommand } = await import("../src/_chunks/build-V3IPZGKC.js");
|
|
29
49
|
await buildCommand(entrypoint, options, config);
|
|
30
50
|
});
|
|
31
51
|
program.command("activate <entrypoint>").description(
|
|
32
52
|
"Activate ServiceWorker (for static site generation in activate event)"
|
|
33
|
-
).option("
|
|
53
|
+
).option("--platform <name>", "Runtime platform (node, cloudflare, bun)").option(
|
|
34
54
|
"-w, --workers <count>",
|
|
35
55
|
"Number of workers",
|
|
36
56
|
DEFAULTS.WORKERS.toString()
|
|
37
57
|
).action(async (entrypoint, options) => {
|
|
38
|
-
const { activateCommand } = await import("../activate-
|
|
58
|
+
const { activateCommand } = await import("../src/_chunks/activate-TP6RQP47.js");
|
|
39
59
|
await activateCommand(entrypoint, options, config);
|
|
40
60
|
});
|
|
41
61
|
program.command("info").description("Display platform and runtime information").action(async () => {
|
|
42
|
-
const { infoCommand } = await import("../info-
|
|
62
|
+
const { infoCommand } = await import("../src/_chunks/info-TDUY3FZN.js");
|
|
43
63
|
await infoCommand();
|
|
44
64
|
});
|
|
45
65
|
program.parse();
|