@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 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.2] - 2026-01-30
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
- ### Build System Unification
7
+ ### Features
20
8
 
21
- Major refactor of the build system to be platform-driven and unified across all commands.
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
- - **New `ServerBundler` class** - Unified bundler replacing separate build/watch logic
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
- ### Code Quality
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
- - Extracted `mergeConfigWithDefaults()` helper to reduce duplication
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
- ### Package Updates
17
+ ### Bug Fixes
46
18
 
47
- - `@b9g/platform` 0.1.14-beta.0
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
- ### Deleted Files
21
+ ## [0.2.1] - 2026-01-28
53
22
 
54
- - `src/commands/activate.ts` - Replaced by `build --lifecycle`
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-beta.11] - 2026-01-10
27
+ ## [0.2.0] - 2026-01-28
63
28
 
64
- ### Changes since beta.10
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
- ### Package Updates
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
- ## [0.2.0-beta.10] - Previous Beta
41
+ ### Breaking Changes
88
42
 
89
- 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.
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
- ### Breaking Changes
49
+ ### CLI
92
50
 
93
- - **`self.buckets` renamed to `self.directories`** - The file system API now uses `directories` to align with web standards terminology
94
- - **`@b9g/router` middleware moved** - `trailingSlash` middleware moved from main export to `@b9g/router/middleware`
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 2023 Brian Kim
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
- **The portable meta-framework built on web standards.**
3
+ **Run Service Workers anywhere.**
4
4
 
5
- Shovel is a CLI platform for developing and deploying service workers as application servers.
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
- ```javascript
8
- // src/server.ts
7
+ ```typescript
8
+ // server.ts
9
9
  import {Router} from "@b9g/router";
10
+
10
11
  const router = new Router();
11
12
 
12
- router.route("/").get(() => new Response("Hello world"));
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 src/server.ts
21
- ```
22
- ## Quick Start
36
+ $ shovel develop server.ts
37
+ listening on http://localhost:7777
23
38
 
24
- ```javascript
25
- // src/server.js
26
- import {Router} from "@b9g/router";
39
+ $ curl -X PUT :7777/kv/hello -d "world"
27
40
 
28
- const router = new Router();
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 @b9g/shovel my-app
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
- | API | Standard | Purpose |
59
- |-----|----------|--------------|
60
- | `fetch()` | [Fetch](https://fetch.spec.whatwg.org) | Networking |
61
- | `install`, `activate`, `fetch` events | [Service Workers](https://w3c.github.io/ServiceWorker/) | Server lifecycle |
62
- | `AsyncContext.Variable` | [TC39 Stage 2](https://github.com/tc39/proposal-async-context) | Request-scoped state |
63
- | `self.caches` | [Cache API](https://w3c.github.io/ServiceWorker/#cache-interface) | Response caching |
64
- | `self.directories` | [FileSystem API](https://fs.spec.whatwg.org/) | Storage (local, S3, R2) |
65
- | `self.cookieStore` | [CookieStore API](https://cookiestore.spec.whatwg.org) | Cookie management |
66
- | `URLPattern` | [URLPattern](https://urlpattern.spec.whatwg.org/) | Route matching |
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"]); // LogTape
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 || 3000",
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 || 3000",
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-NZVIBZYG.js";
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-DNLCO6BX.js");
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-5DSZYRHR.js");
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:3000");
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:3000
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.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.0",
14
- "@b9g/cache": "^0.2.0",
15
- "@b9g/filesystem": "^0.1.8",
16
- "@b9g/http-errors": "^0.2.0",
17
- "@b9g/node-webworker": "^0.2.0",
18
- "@b9g/platform": "^0.1.15",
19
- "@b9g/platform-bun": "^0.1.13",
20
- "@b9g/platform-cloudflare": "^0.1.13",
21
- "@b9g/platform-node": "^0.1.15",
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.0",
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/platform": "^0.1.15",
40
- "@b9g/platform-bun": "^0.1.13",
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-ZIKUWTXK.js";
4
+ } from "./chunk-ABGHNBNM.js";
5
5
  import {
6
6
  findProjectRoot,
7
7
  findWorkspaceRoot
8
- } from "./chunk-NZVIBZYG.js";
8
+ } from "./chunk-7GONPLNW.js";
9
9
 
10
10
  // src/commands/build.ts
11
11
  import { resolve, join, dirname, basename } from "path";
@@ -36,7 +36,7 @@ import { z } from "zod";
36
36
  var DANGEROUS_KEYS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
37
37
  var DEFAULTS = {
38
38
  SERVER: {
39
- PORT: 3e3,
39
+ PORT: 7777,
40
40
  HOST: "0.0.0.0"
41
41
  },
42
42
  WORKERS: 1
@@ -4,7 +4,7 @@ import {
4
4
  generateStorageTypes,
5
5
  getNodeModulesPath,
6
6
  loadRawConfig
7
- } from "./chunk-NZVIBZYG.js";
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 jsOutput = result.outputFiles.find(
157
+ const jsOutputFiles = result.outputFiles.filter(
156
158
  (f) => f.path.endsWith(".js")
157
159
  );
158
- if (!jsOutput) {
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
- content = Buffer.from(jsOutput.text);
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-ZIKUWTXK.js";
4
+ } from "./chunk-ABGHNBNM.js";
5
5
  import {
6
6
  DEFAULTS
7
- } from "./chunk-NZVIBZYG.js";
7
+ } from "./chunk-7GONPLNW.js";
8
8
 
9
9
  // src/commands/develop.ts
10
10
  import { getLogger } from "@logtape/logtape";