@b9g/shovel 0.2.6 → 0.2.7
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 +12 -0
- package/bin/create.js +124 -63
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to Shovel will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## [0.2.7] - 2026-02-06
|
|
6
|
+
|
|
7
|
+
### Features
|
|
8
|
+
|
|
9
|
+
- **Request logger middleware** - New `logger()` middleware in `@b9g/router/middleware` logs requests and responses with timing via LogTape (default category: `["app", "router"]`)
|
|
10
|
+
- **CLI flags for create-shovel** - `--template`, `--typescript`/`--no-typescript`, `--platform` flags to bypass interactive prompts. `--template crank` is shorthand for static-site + Crank.js.
|
|
11
|
+
- **Logger in generated templates** - All Router-based templates (api, full-stack) now include `router.use(logger())` out of the box
|
|
12
|
+
|
|
13
|
+
### Dependencies
|
|
14
|
+
|
|
15
|
+
- **`@b9g/router`** `0.2.2` - Added `@logtape/logtape` as explicit dependency (was previously resolved via workspace only)
|
|
16
|
+
|
|
5
17
|
## [0.2.6] - 2026-02-06
|
|
6
18
|
|
|
7
19
|
### Features
|
package/bin/create.js
CHANGED
|
@@ -20,10 +20,26 @@ function validateProjectName(name) {
|
|
|
20
20
|
return "Use lowercase letters, numbers, and hyphens only";
|
|
21
21
|
return void 0;
|
|
22
22
|
}
|
|
23
|
+
function parseFlags(args) {
|
|
24
|
+
const flags = {};
|
|
25
|
+
for (let i = 0; i < args.length; i++) {
|
|
26
|
+
const arg = args[i];
|
|
27
|
+
if (arg === "--template" && args[i + 1])
|
|
28
|
+
flags.template = args[++i];
|
|
29
|
+
else if (arg === "--typescript")
|
|
30
|
+
flags.typescript = true;
|
|
31
|
+
else if (arg === "--no-typescript")
|
|
32
|
+
flags.typescript = false;
|
|
33
|
+
else if (arg === "--platform" && args[i + 1])
|
|
34
|
+
flags.platform = args[++i];
|
|
35
|
+
}
|
|
36
|
+
return flags;
|
|
37
|
+
}
|
|
23
38
|
async function main() {
|
|
24
39
|
console.info("");
|
|
25
40
|
intro("Create Shovel App");
|
|
26
|
-
|
|
41
|
+
const flags = parseFlags(process.argv.slice(2));
|
|
42
|
+
let projectName = process.argv[2]?.startsWith("-") ? void 0 : process.argv[2];
|
|
27
43
|
if (projectName) {
|
|
28
44
|
const validationError = validateProjectName(projectName);
|
|
29
45
|
if (validationError) {
|
|
@@ -52,37 +68,53 @@ async function main() {
|
|
|
52
68
|
process.exit(0);
|
|
53
69
|
}
|
|
54
70
|
}
|
|
55
|
-
|
|
56
|
-
message: "Choose a starter template:",
|
|
57
|
-
options: [
|
|
58
|
-
{
|
|
59
|
-
value: "hello-world",
|
|
60
|
-
label: "Hello World",
|
|
61
|
-
hint: "Minimal fetch handler to get started"
|
|
62
|
-
},
|
|
63
|
-
{
|
|
64
|
-
value: "api",
|
|
65
|
-
label: "API",
|
|
66
|
-
hint: "REST endpoints with JSON responses"
|
|
67
|
-
},
|
|
68
|
-
{
|
|
69
|
-
value: "static-site",
|
|
70
|
-
label: "Static Site",
|
|
71
|
-
hint: "Server-rendered HTML pages"
|
|
72
|
-
},
|
|
73
|
-
{
|
|
74
|
-
value: "full-stack",
|
|
75
|
-
label: "Full Stack",
|
|
76
|
-
hint: "HTML pages + API routes"
|
|
77
|
-
}
|
|
78
|
-
]
|
|
79
|
-
});
|
|
80
|
-
if (typeof template === "symbol") {
|
|
81
|
-
outro("Project creation cancelled");
|
|
82
|
-
process.exit(0);
|
|
83
|
-
}
|
|
71
|
+
let template;
|
|
84
72
|
let uiFramework = "vanilla";
|
|
85
|
-
if (template === "
|
|
73
|
+
if (flags.template === "crank") {
|
|
74
|
+
template = "static-site";
|
|
75
|
+
uiFramework = "crank";
|
|
76
|
+
} else if (flags.template) {
|
|
77
|
+
const valid = ["hello-world", "api", "static-site", "full-stack"];
|
|
78
|
+
if (!valid.includes(flags.template)) {
|
|
79
|
+
console.error(
|
|
80
|
+
`Error: Unknown template "${flags.template}". Valid options: ${valid.join(", ")}, crank`
|
|
81
|
+
);
|
|
82
|
+
process.exit(1);
|
|
83
|
+
}
|
|
84
|
+
template = flags.template;
|
|
85
|
+
} else {
|
|
86
|
+
const templateResult = await select({
|
|
87
|
+
message: "Choose a starter template:",
|
|
88
|
+
options: [
|
|
89
|
+
{
|
|
90
|
+
value: "hello-world",
|
|
91
|
+
label: "Hello World",
|
|
92
|
+
hint: "Minimal fetch handler to get started"
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
value: "api",
|
|
96
|
+
label: "API",
|
|
97
|
+
hint: "REST endpoints with JSON responses"
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
value: "static-site",
|
|
101
|
+
label: "Static Site",
|
|
102
|
+
hint: "Server-rendered HTML pages"
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
value: "full-stack",
|
|
106
|
+
label: "Full Stack",
|
|
107
|
+
hint: "HTML pages + API routes"
|
|
108
|
+
}
|
|
109
|
+
]
|
|
110
|
+
});
|
|
111
|
+
if (typeof templateResult === "symbol") {
|
|
112
|
+
outro("Project creation cancelled");
|
|
113
|
+
process.exit(0);
|
|
114
|
+
}
|
|
115
|
+
template = templateResult;
|
|
116
|
+
}
|
|
117
|
+
if (uiFramework === "vanilla" && (template === "static-site" || template === "full-stack")) {
|
|
86
118
|
const framework = await select({
|
|
87
119
|
message: "UI framework:",
|
|
88
120
|
initialValue: "crank",
|
|
@@ -115,39 +147,58 @@ async function main() {
|
|
|
115
147
|
}
|
|
116
148
|
uiFramework = framework;
|
|
117
149
|
}
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
150
|
+
let typescript;
|
|
151
|
+
if (flags.typescript !== void 0) {
|
|
152
|
+
typescript = flags.typescript;
|
|
153
|
+
} else {
|
|
154
|
+
const tsResult = await confirm({
|
|
155
|
+
message: "Use TypeScript?",
|
|
156
|
+
initialValue: true
|
|
157
|
+
});
|
|
158
|
+
if (typeof tsResult === "symbol") {
|
|
159
|
+
outro("Project creation cancelled");
|
|
160
|
+
process.exit(0);
|
|
161
|
+
}
|
|
162
|
+
typescript = tsResult;
|
|
125
163
|
}
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
164
|
+
let platform;
|
|
165
|
+
if (flags.platform) {
|
|
166
|
+
const valid = ["node", "bun", "cloudflare"];
|
|
167
|
+
if (!valid.includes(flags.platform)) {
|
|
168
|
+
console.error(
|
|
169
|
+
`Error: Unknown platform "${flags.platform}". Valid options: ${valid.join(", ")}`
|
|
170
|
+
);
|
|
171
|
+
process.exit(1);
|
|
172
|
+
}
|
|
173
|
+
platform = flags.platform;
|
|
174
|
+
} else {
|
|
175
|
+
const detectedPlatform = detectPlatform();
|
|
176
|
+
const platformResult = await select({
|
|
177
|
+
message: "Which platform?",
|
|
178
|
+
initialValue: detectedPlatform,
|
|
179
|
+
options: [
|
|
180
|
+
{
|
|
181
|
+
value: "node",
|
|
182
|
+
label: "Node.js",
|
|
183
|
+
hint: detectedPlatform === "node" ? "detected" : void 0
|
|
184
|
+
},
|
|
185
|
+
{
|
|
186
|
+
value: "bun",
|
|
187
|
+
label: "Bun",
|
|
188
|
+
hint: detectedPlatform === "bun" ? "detected" : void 0
|
|
189
|
+
},
|
|
190
|
+
{
|
|
191
|
+
value: "cloudflare",
|
|
192
|
+
label: "Cloudflare Workers",
|
|
193
|
+
hint: "Edge runtime"
|
|
194
|
+
}
|
|
195
|
+
]
|
|
196
|
+
});
|
|
197
|
+
if (typeof platformResult === "symbol") {
|
|
198
|
+
outro("Project creation cancelled");
|
|
199
|
+
process.exit(0);
|
|
200
|
+
}
|
|
201
|
+
platform = platformResult;
|
|
151
202
|
}
|
|
152
203
|
const config = {
|
|
153
204
|
name: projectName,
|
|
@@ -287,8 +338,10 @@ self.addEventListener("fetch", (event) => {
|
|
|
287
338
|
}
|
|
288
339
|
function generateApi(config) {
|
|
289
340
|
return `import { Router } from "@b9g/router";
|
|
341
|
+
import { logger } from "@b9g/router/middleware";
|
|
290
342
|
|
|
291
343
|
const router = new Router();
|
|
344
|
+
router.use(logger());
|
|
292
345
|
|
|
293
346
|
// In-memory data store
|
|
294
347
|
const users = [
|
|
@@ -622,8 +675,10 @@ function generateFullStackVanilla(config) {
|
|
|
622
675
|
const ext = config.typescript ? "ts" : "js";
|
|
623
676
|
const t = config.typescript;
|
|
624
677
|
return `import { Router } from "@b9g/router";
|
|
678
|
+
import { logger } from "@b9g/router/middleware";
|
|
625
679
|
|
|
626
680
|
const router = new Router();
|
|
681
|
+
router.use(logger());
|
|
627
682
|
|
|
628
683
|
// API routes
|
|
629
684
|
router.route("/api/hello").get(() => {
|
|
@@ -688,8 +743,10 @@ function generateFullStackHtmx(config) {
|
|
|
688
743
|
const ext = config.typescript ? "ts" : "js";
|
|
689
744
|
const t = config.typescript;
|
|
690
745
|
return `import { Router } from "@b9g/router";
|
|
746
|
+
import { logger } from "@b9g/router/middleware";
|
|
691
747
|
|
|
692
748
|
const router = new Router();
|
|
749
|
+
router.use(logger());
|
|
693
750
|
|
|
694
751
|
// API routes \u2014 return HTML fragments when HTMX requests, JSON otherwise
|
|
695
752
|
router.route("/api/hello").get((req) => {
|
|
@@ -764,8 +821,10 @@ function generateFullStackAlpine(config) {
|
|
|
764
821
|
const ext = config.typescript ? "ts" : "js";
|
|
765
822
|
const t = config.typescript;
|
|
766
823
|
return `import { Router } from "@b9g/router";
|
|
824
|
+
import { logger } from "@b9g/router/middleware";
|
|
767
825
|
|
|
768
826
|
const router = new Router();
|
|
827
|
+
router.use(logger());
|
|
769
828
|
|
|
770
829
|
// API routes
|
|
771
830
|
router.route("/api/hello").get(() => {
|
|
@@ -837,9 +896,11 @@ self.addEventListener("fetch", (event) => {
|
|
|
837
896
|
function generateFullStackCrank(config) {
|
|
838
897
|
const t = config.typescript;
|
|
839
898
|
return `import { Router } from "@b9g/router";
|
|
899
|
+
import { logger } from "@b9g/router/middleware";
|
|
840
900
|
import {renderer} from "@b9g/crank/html";
|
|
841
901
|
|
|
842
902
|
const router = new Router();
|
|
903
|
+
router.use(logger());
|
|
843
904
|
|
|
844
905
|
const css = \`
|
|
845
906
|
${css}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@b9g/shovel",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.7",
|
|
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": {
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"@b9g/assets": "^0.2.1",
|
|
37
37
|
"@b9g/crank": "^0.7.2",
|
|
38
38
|
"@b9g/libuild": "^0.1.22",
|
|
39
|
-
"@b9g/router": "^0.2.
|
|
39
|
+
"@b9g/router": "^0.2.2",
|
|
40
40
|
"@logtape/file": "^1.0.0",
|
|
41
41
|
"@types/bun": "^1.3.4",
|
|
42
42
|
"@typescript-eslint/eslint-plugin": "^8.0.0",
|