@b9g/shovel 0.2.17 → 0.2.18

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,6 +2,44 @@
2
2
 
3
3
  All notable changes to Shovel will be documented in this file.
4
4
 
5
+ ## [0.2.18] - 2026-03-16
6
+
7
+ ### Features
8
+
9
+ - **Glob asset imports** — Import multiple static files with a single glob pattern:
10
+ `import urls from "./public/**/*" with { assetBase: "/", assetName: "[name].[ext]" }`
11
+ Files flow through the existing asset pipeline (hashing, manifesting, dist/public/).
12
+ Works across all platforms (Node, Bun, Cloudflare). ([#75](https://github.com/bikeshaving/shovel/issues/75), [PR #77](https://github.com/bikeshaving/shovel/pull/77))
13
+
14
+ ### Dependencies
15
+
16
+ - Added `glob` package for glob pattern expansion in asset imports
17
+
18
+ ## @b9g/platform-bun 0.1.17 - 2026-03-16
19
+
20
+ ### Features
21
+
22
+ - **BroadcastChannel relay** — Worker code generation now includes BroadcastChannel message relay between supervisor and workers
23
+
24
+ ## @b9g/broadcastchannel-redis 0.1.0 - 2026-03-16
25
+
26
+ ### Features
27
+
28
+ - **Initial release** — Redis pub/sub backend for Shovel BroadcastChannel, enabling cross-process message broadcasting via Redis
29
+
30
+ ## @b9g/platform-cloudflare 0.1.17 - 2026-03-03
31
+
32
+ ### Bug Fixes
33
+
34
+ - **`@b9g/platform-cloudflare`** - Fix Miniflare dev server returning 404 for all routes by adding `routerConfig: {has_user_worker: true}` to assets config (#72)
35
+
36
+ ## @b9g/platform-cloudflare 0.1.16, create-shovel 0.1.3 - 2026-03-02
37
+
38
+ ### Features
39
+
40
+ - **`@b9g/platform-cloudflare`** - Miniflare dev server now configures static assets directory and `ASSETS` binding, matching Cloudflare Workers production behavior (#71)
41
+ - **`create-shovel`** - Cloudflare projects now generate a `wrangler.toml` with assets directory config (#71)
42
+
5
43
  ## [0.2.17] - 2026-02-26
6
44
 
7
45
  ### Bug Fixes
package/bin/cli.js CHANGED
@@ -5,7 +5,7 @@ import {
5
5
  DEFAULTS,
6
6
  findProjectRoot,
7
7
  loadConfig
8
- } from "../src/_chunks/chunk-ZOLZXZ6E.js";
8
+ } from "../src/_chunks/chunk-MUNERPYV.js";
9
9
 
10
10
  // bin/cli.ts
11
11
  import { resolve, relative } from "path";
@@ -75,7 +75,7 @@ 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-BLCM4BBB.js");
78
+ const { developCommand } = await import("../src/_chunks/develop-CJOPWKU5.js");
79
79
  await developCommand(entrypoint, options, config);
80
80
  });
81
81
  program.command("create [name]").description("Create a new Shovel project").action(async (name) => {
@@ -91,7 +91,7 @@ program.command("build <entrypoint>").description("Build app for production").op
91
91
  "Run ServiceWorker lifecycle after build (install or activate, default: activate)"
92
92
  ).action(async (entrypoint, options) => {
93
93
  checkPlatformReexec(options);
94
- const { buildCommand } = await import("../src/_chunks/build-FZTZ3R4X.js");
94
+ const { buildCommand } = await import("../src/_chunks/build-CVI2DIGJ.js");
95
95
  await buildCommand(entrypoint, options, config);
96
96
  process.exit(0);
97
97
  });
package/bin/create.js CHANGED
@@ -406,6 +406,17 @@ dist/
406
406
  .DS_Store
407
407
  `;
408
408
  await writeFile(join(projectPath, ".gitignore"), gitignore);
409
+ if (config.platform === "cloudflare") {
410
+ const wranglerToml = `name = "${config.name}"
411
+ main = "dist/server/worker.js"
412
+ compatibility_date = "2024-09-23"
413
+ compatibility_flags = ["nodejs_compat"]
414
+
415
+ [assets]
416
+ directory = "./dist/public"
417
+ `;
418
+ await writeFile(join(projectPath, "wrangler.toml"), wranglerToml);
419
+ }
409
420
  }
410
421
  function generateAppFile(config) {
411
422
  switch (config.template) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@b9g/shovel",
3
- "version": "0.2.17",
3
+ "version": "0.2.18",
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
6
  "repository": {
@@ -19,15 +19,16 @@
19
19
  "@b9g/filesystem": "^0.2.0",
20
20
  "@b9g/http-errors": "^0.2.1",
21
21
  "@b9g/node-webworker": "^0.2.1",
22
- "@b9g/platform": "^0.1.18",
23
- "@b9g/platform-bun": "^0.1.16",
24
- "@b9g/platform-cloudflare": "^0.1.15",
22
+ "@b9g/platform": "^0.1.19",
23
+ "@b9g/platform-bun": "^0.1.17",
24
+ "@b9g/platform-cloudflare": "^0.1.17",
25
25
  "@b9g/platform-node": "^0.1.17",
26
26
  "@clack/prompts": "^0.7.0",
27
27
  "@logtape/logtape": "^2.0.0",
28
28
  "commander": "^13.1.0",
29
29
  "esbuild": "^0.27.2",
30
30
  "esbuild-plugins-node-modules-polyfill": "^1.8.1",
31
+ "glob": "^13.0.6",
31
32
  "mime": "^4.0.4",
32
33
  "zod": "^3.23.0"
33
34
  },
@@ -44,7 +45,7 @@
44
45
  "eslint": "^9.0.0",
45
46
  "eslint-config-prettier": "^10.0.0",
46
47
  "eslint-plugin-prettier": "^5.0.0",
47
- "miniflare": "^4.0.0",
48
+ "miniflare": "^4.20260305.0",
48
49
  "mitata": "^1.0.34",
49
50
  "typescript": "^5.7.3"
50
51
  },
@@ -1,11 +1,11 @@
1
1
  import {
2
2
  ServerBundler,
3
3
  loadPlatformModule
4
- } from "./chunk-RAQUFQW6.js";
4
+ } from "./chunk-5Z2WUZM5.js";
5
5
  import {
6
6
  findProjectRoot,
7
7
  findWorkspaceRoot
8
- } from "./chunk-ZOLZXZ6E.js";
8
+ } from "./chunk-MUNERPYV.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-ZOLZXZ6E.js";
7
+ } from "./chunk-MUNERPYV.js";
8
8
 
9
9
  // src/utils/bundler.ts
10
10
  import * as ESBuild2 from "esbuild";
@@ -369,6 +369,95 @@ function assetsPlugin(options = {}) {
369
369
  };
370
370
  }
371
371
 
372
+ // src/plugins/glob-assets.ts
373
+ import { globSync } from "glob";
374
+ import { posix } from "path";
375
+ var GLOB_NAMESPACE = "shovel-glob-assets";
376
+ function getGlobRoot(pattern) {
377
+ const parts = pattern.split("/");
378
+ const rootParts = [];
379
+ for (const part of parts) {
380
+ if (part.includes("*"))
381
+ break;
382
+ rootParts.push(part);
383
+ }
384
+ return rootParts.join("/") || ".";
385
+ }
386
+ function globAssetsPlugin() {
387
+ return {
388
+ name: "shovel-glob-assets",
389
+ setup(build2) {
390
+ build2.onResolve({ filter: /\*/ }, (args) => {
391
+ if (!args.with?.assetBase)
392
+ return null;
393
+ return {
394
+ path: args.path,
395
+ namespace: GLOB_NAMESPACE,
396
+ pluginData: {
397
+ resolveDir: args.resolveDir,
398
+ importAttributes: args.with
399
+ }
400
+ };
401
+ });
402
+ build2.onLoad({ filter: /.*/, namespace: GLOB_NAMESPACE }, (args) => {
403
+ const { resolveDir, importAttributes } = args.pluginData;
404
+ const pattern = args.path;
405
+ const assetBase = importAttributes.assetBase;
406
+ const files = globSync(pattern, {
407
+ cwd: resolveDir,
408
+ nodir: true,
409
+ dot: false
410
+ });
411
+ if (files.length === 0) {
412
+ return {
413
+ warnings: [
414
+ {
415
+ text: `Glob pattern "${pattern}" matched no files`
416
+ }
417
+ ],
418
+ contents: "export default {};",
419
+ loader: "js"
420
+ };
421
+ }
422
+ const globRoot = getGlobRoot(pattern);
423
+ const imports = [];
424
+ const exports = [];
425
+ for (let i = 0; i < files.length; i++) {
426
+ const file = files[i];
427
+ const relativeToRoot = posix.relative(globRoot, file);
428
+ const fileDir = posix.dirname(relativeToRoot);
429
+ const fileAttrs = {
430
+ ...importAttributes
431
+ };
432
+ let fileAssetBase = assetBase;
433
+ if (fileDir && fileDir !== ".") {
434
+ fileAssetBase = posix.join(assetBase, fileDir, "/");
435
+ }
436
+ if (!fileAssetBase.endsWith("/")) {
437
+ fileAssetBase += "/";
438
+ }
439
+ fileAttrs.assetBase = fileAssetBase;
440
+ const withClause = Object.entries(fileAttrs).map(([k, v]) => `${k}: ${JSON.stringify(v)}`).join(", ");
441
+ const importPath = file.startsWith(".") ? file : `./${file}`;
442
+ imports.push(
443
+ `import _${i} from ${JSON.stringify(importPath)} with { ${withClause} };`
444
+ );
445
+ exports.push(`${JSON.stringify(relativeToRoot)}: _${i}`);
446
+ }
447
+ const contents = [
448
+ ...imports,
449
+ `export default { ${exports.join(", ")} };`
450
+ ].join("\n");
451
+ return {
452
+ contents,
453
+ loader: "js",
454
+ resolveDir
455
+ };
456
+ });
457
+ }
458
+ };
459
+ }
460
+
372
461
  // src/plugins/import-meta.ts
373
462
  import { readFile } from "fs/promises";
374
463
  import { dirname as dirname2 } from "path";
@@ -791,6 +880,7 @@ var ServerBundler = class {
791
880
  this.#userEntryPath = entryPath;
792
881
  const outputDir = resolve(this.#projectRoot, this.#options.outDir);
793
882
  await mkdir(join5(outputDir, "server"), { recursive: true });
883
+ await mkdir(join5(outputDir, "public"), { recursive: true });
794
884
  const initialBuildPromise = new Promise((resolve2) => {
795
885
  this.#initialBuildResolve = resolve2;
796
886
  });
@@ -868,6 +958,8 @@ var ServerBundler = class {
868
958
  }),
869
959
  createEntryPlugin(this.#projectRoot, platformEntryPoints),
870
960
  importMetaPlugin(),
961
+ // globAssetsPlugin expands glob patterns into individual imports for assetsPlugin
962
+ globAssetsPlugin(),
871
963
  // assetsPlugin must come before assetsManifestPlugin so onEnd order is correct
872
964
  assetsPlugin({
873
965
  outDir: outputDir,
@@ -879,6 +879,9 @@ function generateConfigModule(rawConfig, options) {
879
879
  const platformConfig = platformCaches[name] || {};
880
880
  const userConfig = userCaches[name] || {};
881
881
  const mergedConfig = { ...platformConfig, ...userConfig };
882
+ if (userConfig.module && !userConfig.export) {
883
+ delete mergedConfig.export;
884
+ }
882
885
  caches[name] = reifyModule(mergedConfig, "cache", name);
883
886
  }
884
887
  config2.caches = caches;
@@ -895,6 +898,9 @@ function generateConfigModule(rawConfig, options) {
895
898
  const platformConfig = platformDirectories[name] || {};
896
899
  const userConfig = userDirectories[name] || {};
897
900
  const mergedConfig = { ...platformConfig, ...userConfig };
901
+ if (userConfig.module && !userConfig.export) {
902
+ delete mergedConfig.export;
903
+ }
898
904
  directories[name] = reifyModule(mergedConfig, "directory", name);
899
905
  }
900
906
  config2.directories = directories;
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  ServerBundler,
3
3
  loadPlatformModule
4
- } from "./chunk-RAQUFQW6.js";
4
+ } from "./chunk-5Z2WUZM5.js";
5
5
  import {
6
6
  DEFAULTS
7
- } from "./chunk-ZOLZXZ6E.js";
7
+ } from "./chunk-MUNERPYV.js";
8
8
 
9
9
  // src/commands/develop.ts
10
10
  import { getLogger } from "@logtape/logtape";