@b9g/shovel 0.2.0-beta.2 → 0.2.0-beta.20
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 +184 -172
- package/README.md +313 -54
- package/bin/cli.d.ts +0 -1
- package/bin/cli.js +62 -786
- package/bin/create.js +79 -37
- package/package.json +33 -37
- package/src/_chunks/build-IWPEM2EW.js +160 -0
- package/src/_chunks/chunk-PTLNYIRW.js +1158 -0
- package/src/_chunks/chunk-VWH6D26D.js +1062 -0
- package/src/_chunks/develop-VHR5FLGQ.js +85 -0
- package/src/_chunks/info-TDUY3FZN.js +13 -0
- package/src/worker-entry.d.ts +0 -7
- package/src/worker-entry.js +0 -37
package/bin/create.js
CHANGED
|
@@ -8,7 +8,7 @@ import picocolors from "picocolors";
|
|
|
8
8
|
import { mkdir, writeFile } from "fs/promises";
|
|
9
9
|
import { join, resolve } from "path";
|
|
10
10
|
import { existsSync } from "fs";
|
|
11
|
-
var { cyan, green,
|
|
11
|
+
var { cyan, green, red, dim, bold } = picocolors;
|
|
12
12
|
async function main() {
|
|
13
13
|
console.info("");
|
|
14
14
|
intro(cyan("\u{1F680} Create Shovel App"));
|
|
@@ -113,7 +113,7 @@ async function main() {
|
|
|
113
113
|
console.info(` ${dim("$")} npm run develop`);
|
|
114
114
|
console.info("");
|
|
115
115
|
console.info(
|
|
116
|
-
`\u{1F310} Your app will be available at: ${bold("http://localhost:
|
|
116
|
+
`\u{1F310} Your app will be available at: ${bold("http://localhost:3000")}`
|
|
117
117
|
);
|
|
118
118
|
console.info("");
|
|
119
119
|
console.info(dim("Happy coding with Shovel! \u{1F680}"));
|
|
@@ -200,28 +200,49 @@ function getRequestInfo(request: Request) {
|
|
|
200
200
|
|
|
201
201
|
async function parseBody(request: Request) {
|
|
202
202
|
const contentType = request.headers.get('content-type') || '';
|
|
203
|
-
|
|
203
|
+
|
|
204
204
|
if (contentType.includes('application/json')) {
|
|
205
205
|
try {
|
|
206
206
|
return await request.json();
|
|
207
|
-
} catch {
|
|
207
|
+
} catch (err) {
|
|
208
|
+
// Only ignore JSON parse errors, rethrow others
|
|
209
|
+
if (
|
|
210
|
+
!(err instanceof SyntaxError) ||
|
|
211
|
+
!/^(Unexpected token|Expected|JSON)/i.test(String(err.message))
|
|
212
|
+
) {
|
|
213
|
+
throw err;
|
|
214
|
+
}
|
|
208
215
|
return null;
|
|
209
216
|
}
|
|
210
217
|
}
|
|
211
|
-
|
|
218
|
+
|
|
212
219
|
if (contentType.includes('application/x-www-form-urlencoded')) {
|
|
213
220
|
try {
|
|
214
221
|
const formData = await request.formData();
|
|
215
222
|
return Object.fromEntries(formData.entries());
|
|
216
|
-
} catch {
|
|
223
|
+
} catch (err) {
|
|
224
|
+
// Only ignore form data parse errors, rethrow others
|
|
225
|
+
if (
|
|
226
|
+
!(err instanceof TypeError) ||
|
|
227
|
+
!String(err.message).includes('FormData')
|
|
228
|
+
) {
|
|
229
|
+
throw err;
|
|
230
|
+
}
|
|
217
231
|
return null;
|
|
218
232
|
}
|
|
219
233
|
}
|
|
220
|
-
|
|
234
|
+
|
|
221
235
|
try {
|
|
222
236
|
const text = await request.text();
|
|
223
237
|
return text || null;
|
|
224
|
-
} catch {
|
|
238
|
+
} catch (err) {
|
|
239
|
+
// Only ignore body already consumed errors, rethrow others
|
|
240
|
+
if (
|
|
241
|
+
!(err instanceof TypeError) ||
|
|
242
|
+
!String(err.message).includes('body')
|
|
243
|
+
) {
|
|
244
|
+
throw err;
|
|
245
|
+
}
|
|
225
246
|
return null;
|
|
226
247
|
}
|
|
227
248
|
}
|
|
@@ -237,28 +258,49 @@ function getRequestInfo(request) {
|
|
|
237
258
|
|
|
238
259
|
async function parseBody(request) {
|
|
239
260
|
const contentType = request.headers.get('content-type') || '';
|
|
240
|
-
|
|
261
|
+
|
|
241
262
|
if (contentType.includes('application/json')) {
|
|
242
263
|
try {
|
|
243
264
|
return await request.json();
|
|
244
|
-
} catch {
|
|
265
|
+
} catch (err) {
|
|
266
|
+
// Only ignore JSON parse errors, rethrow others
|
|
267
|
+
if (
|
|
268
|
+
!(err instanceof SyntaxError) ||
|
|
269
|
+
!/^(Unexpected token|Expected|JSON)/i.test(String(err.message))
|
|
270
|
+
) {
|
|
271
|
+
throw err;
|
|
272
|
+
}
|
|
245
273
|
return null;
|
|
246
274
|
}
|
|
247
275
|
}
|
|
248
|
-
|
|
276
|
+
|
|
249
277
|
if (contentType.includes('application/x-www-form-urlencoded')) {
|
|
250
278
|
try {
|
|
251
279
|
const formData = await request.formData();
|
|
252
280
|
return Object.fromEntries(formData.entries());
|
|
253
|
-
} catch {
|
|
281
|
+
} catch (err) {
|
|
282
|
+
// Only ignore form data parse errors, rethrow others
|
|
283
|
+
if (
|
|
284
|
+
!(err instanceof TypeError) ||
|
|
285
|
+
!String(err.message).includes('FormData')
|
|
286
|
+
) {
|
|
287
|
+
throw err;
|
|
288
|
+
}
|
|
254
289
|
return null;
|
|
255
290
|
}
|
|
256
291
|
}
|
|
257
|
-
|
|
292
|
+
|
|
258
293
|
try {
|
|
259
294
|
const text = await request.text();
|
|
260
295
|
return text || null;
|
|
261
|
-
} catch {
|
|
296
|
+
} catch (err) {
|
|
297
|
+
// Only ignore body already consumed errors, rethrow others
|
|
298
|
+
if (
|
|
299
|
+
!(err instanceof TypeError) ||
|
|
300
|
+
!String(err.message).includes('body')
|
|
301
|
+
) {
|
|
302
|
+
throw err;
|
|
303
|
+
}
|
|
262
304
|
return null;
|
|
263
305
|
}
|
|
264
306
|
}
|
|
@@ -281,7 +323,7 @@ self.addEventListener("activate", (event) => {
|
|
|
281
323
|
// Handle HTTP requests
|
|
282
324
|
self.addEventListener("fetch", (event) => {
|
|
283
325
|
try {
|
|
284
|
-
const responsePromise = router.
|
|
326
|
+
const responsePromise = router.handle(event.request);
|
|
285
327
|
event.respondWith(responsePromise);
|
|
286
328
|
} catch (error) {
|
|
287
329
|
console.error("[${config.name}] Error handling request:", error);
|
|
@@ -323,7 +365,7 @@ router.route("/").get(async (request, context) => {
|
|
|
323
365
|
<body>
|
|
324
366
|
<h1>\u{1F680} Welcome to Shovel!</h1>
|
|
325
367
|
<p>Your ${config.template} app is running on the <strong>${config.platform}</strong> platform.</p>
|
|
326
|
-
|
|
368
|
+
|
|
327
369
|
<div class="info">
|
|
328
370
|
<strong>Try these endpoints:</strong>
|
|
329
371
|
<ul>
|
|
@@ -331,12 +373,12 @@ router.route("/").get(async (request, context) => {
|
|
|
331
373
|
<li><a href="/api/time">GET /api/time</a> - Current timestamp</li>
|
|
332
374
|
</ul>
|
|
333
375
|
</div>
|
|
334
|
-
|
|
376
|
+
|
|
335
377
|
<p>Edit <code>src/app.${config.typescript ? "ts" : "js"}</code> to customize your app!</p>
|
|
336
378
|
</body>
|
|
337
379
|
</html>
|
|
338
380
|
\`;
|
|
339
|
-
|
|
381
|
+
|
|
340
382
|
return new Response(html, {
|
|
341
383
|
headers: { "Content-Type": "text/html" }
|
|
342
384
|
});
|
|
@@ -366,7 +408,7 @@ router.route("/api/time").get(async (request, context) => {
|
|
|
366
408
|
router.route("/").get(async (request, context) => {
|
|
367
409
|
return new Response(JSON.stringify({
|
|
368
410
|
name: "${config.name}",
|
|
369
|
-
platform: "${config.platform}",
|
|
411
|
+
platform: "${config.platform}",
|
|
370
412
|
endpoints: [
|
|
371
413
|
{ method: "GET", path: "/api/users", description: "Get all users" },
|
|
372
414
|
{ method: "POST", path: "/api/users", description: "Create a user" },
|
|
@@ -388,12 +430,12 @@ const users = [
|
|
|
388
430
|
router.route("/api/users").get(async (request, context) => {
|
|
389
431
|
const url = new URL(request.url);
|
|
390
432
|
const active = url.searchParams.get('active');
|
|
391
|
-
|
|
433
|
+
|
|
392
434
|
let filteredUsers = users;
|
|
393
435
|
if (active !== null) {
|
|
394
436
|
filteredUsers = users.filter(user => user.active === (active === 'true'));
|
|
395
437
|
}
|
|
396
|
-
|
|
438
|
+
|
|
397
439
|
return new Response(JSON.stringify({
|
|
398
440
|
users: filteredUsers,
|
|
399
441
|
total: filteredUsers.length
|
|
@@ -410,9 +452,9 @@ router.route("/api/users").post(async (request, context) => {
|
|
|
410
452
|
email: userData.email || \`user\${Date.now()}@example.com\`,
|
|
411
453
|
active: userData.active !== false
|
|
412
454
|
};
|
|
413
|
-
|
|
455
|
+
|
|
414
456
|
users.push(newUser);
|
|
415
|
-
|
|
457
|
+
|
|
416
458
|
return new Response(JSON.stringify({
|
|
417
459
|
success: true,
|
|
418
460
|
user: newUser
|
|
@@ -425,7 +467,7 @@ router.route("/api/users").post(async (request, context) => {
|
|
|
425
467
|
router.route("/api/users/:id").get(async (request, context) => {
|
|
426
468
|
const id = parseInt(context.params.id);
|
|
427
469
|
const user = users.find(u => u.id === id);
|
|
428
|
-
|
|
470
|
+
|
|
429
471
|
if (!user) {
|
|
430
472
|
return new Response(JSON.stringify({
|
|
431
473
|
error: "User not found"
|
|
@@ -434,7 +476,7 @@ router.route("/api/users/:id").get(async (request, context) => {
|
|
|
434
476
|
headers: { "Content-Type": "application/json" }
|
|
435
477
|
});
|
|
436
478
|
}
|
|
437
|
-
|
|
479
|
+
|
|
438
480
|
return new Response(JSON.stringify({ user }), {
|
|
439
481
|
headers: { "Content-Type": "application/json" }
|
|
440
482
|
});
|
|
@@ -470,32 +512,32 @@ router.route("/").get(async (request, context) => {
|
|
|
470
512
|
<body>
|
|
471
513
|
<h1>\u{1F504} HTTP Echo Service</h1>
|
|
472
514
|
<p>A simple HTTP request/response inspection service.</p>
|
|
473
|
-
|
|
515
|
+
|
|
474
516
|
<div class="endpoint">
|
|
475
517
|
<strong>POST /echo</strong><br>
|
|
476
518
|
Echo back request details including headers, body, and metadata.
|
|
477
519
|
</div>
|
|
478
|
-
|
|
520
|
+
|
|
479
521
|
<div class="endpoint">
|
|
480
522
|
<strong>GET /ip</strong><br>
|
|
481
523
|
Get your IP address.
|
|
482
524
|
</div>
|
|
483
|
-
|
|
525
|
+
|
|
484
526
|
<div class="endpoint">
|
|
485
527
|
<strong>GET /headers</strong><br>
|
|
486
528
|
Get your request headers.
|
|
487
529
|
</div>
|
|
488
|
-
|
|
530
|
+
|
|
489
531
|
<div class="endpoint">
|
|
490
532
|
<strong>GET /user-agent</strong><br>
|
|
491
533
|
Get your user agent string.
|
|
492
534
|
</div>
|
|
493
|
-
|
|
535
|
+
|
|
494
536
|
<p>Try: <code>curl -X POST https://your-app.com/echo -d '{"test": "data"}'</code></p>
|
|
495
537
|
</body>
|
|
496
538
|
</html>
|
|
497
539
|
\`;
|
|
498
|
-
|
|
540
|
+
|
|
499
541
|
return new Response(html, {
|
|
500
542
|
headers: { "Content-Type": "text/html" }
|
|
501
543
|
});
|
|
@@ -504,24 +546,24 @@ router.route("/").get(async (request, context) => {
|
|
|
504
546
|
router.route("/echo").all(async (request, context) => {
|
|
505
547
|
const info = getRequestInfo(request);
|
|
506
548
|
const body = await parseBody(request);
|
|
507
|
-
|
|
549
|
+
|
|
508
550
|
const response = {
|
|
509
551
|
...info,
|
|
510
552
|
body,
|
|
511
553
|
contentType: request.headers.get("content-type") || null,
|
|
512
554
|
timestamp: new Date().toISOString()
|
|
513
555
|
};
|
|
514
|
-
|
|
556
|
+
|
|
515
557
|
return new Response(JSON.stringify(response, null, 2), {
|
|
516
558
|
headers: { "Content-Type": "application/json" }
|
|
517
559
|
});
|
|
518
560
|
});
|
|
519
561
|
|
|
520
562
|
router.route("/ip").get(async (request, context) => {
|
|
521
|
-
const ip = request.headers.get("x-forwarded-for") ||
|
|
522
|
-
request.headers.get("x-real-ip") ||
|
|
563
|
+
const ip = request.headers.get("x-forwarded-for") ||
|
|
564
|
+
request.headers.get("x-real-ip") ||
|
|
523
565
|
"127.0.0.1";
|
|
524
|
-
|
|
566
|
+
|
|
525
567
|
return new Response(JSON.stringify({ ip }), {
|
|
526
568
|
headers: { "Content-Type": "application/json" }
|
|
527
569
|
});
|
|
@@ -558,7 +600,7 @@ npm install
|
|
|
558
600
|
npm run develop
|
|
559
601
|
\`\`\`
|
|
560
602
|
|
|
561
|
-
Your app will be available at: **http://localhost:
|
|
603
|
+
Your app will be available at: **http://localhost:3000**
|
|
562
604
|
|
|
563
605
|
## \u{1F4C1} Project Structure
|
|
564
606
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@b9g/shovel",
|
|
3
|
-
"version": "0.2.0-beta.
|
|
3
|
+
"version": "0.2.0-beta.20",
|
|
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
|
"bin": {
|
|
@@ -10,41 +10,50 @@
|
|
|
10
10
|
"create": "bin/create.js"
|
|
11
11
|
},
|
|
12
12
|
"dependencies": {
|
|
13
|
-
"@b9g/async-context": "^0.
|
|
13
|
+
"@b9g/async-context": "^0.2.0-beta.0",
|
|
14
14
|
"@clack/prompts": "^0.7.0",
|
|
15
|
+
"@esbuild-plugins/node-globals-polyfill": "^0.2.3",
|
|
16
|
+
"@esbuild-plugins/node-modules-polyfill": "^0.2.2",
|
|
15
17
|
"@logtape/logtape": "^1.2.0",
|
|
16
18
|
"commander": "^13.1.0",
|
|
17
|
-
"esbuild": "^0.
|
|
19
|
+
"esbuild": "^0.27.2",
|
|
18
20
|
"magic-string": "^0.30.5",
|
|
19
21
|
"mime": "^4.0.4",
|
|
20
22
|
"picocolors": "^1.0.0",
|
|
21
|
-
"source-map": "^0.7.4"
|
|
23
|
+
"source-map": "^0.7.4",
|
|
24
|
+
"zod": "^3.23.0"
|
|
22
25
|
},
|
|
23
26
|
"devDependencies": {
|
|
24
|
-
"@b9g/assets": "^0.
|
|
25
|
-
"@b9g/cache": "^0.
|
|
27
|
+
"@b9g/assets": "^0.2.0-beta.0",
|
|
28
|
+
"@b9g/cache": "^0.2.0-beta.0",
|
|
26
29
|
"@b9g/crank": "^0.7.2",
|
|
27
|
-
"@
|
|
28
|
-
"@b9g/
|
|
29
|
-
"@b9g/
|
|
30
|
-
"@b9g/
|
|
31
|
-
"@b9g/platform
|
|
32
|
-
"@b9g/platform-
|
|
33
|
-
"@b9g/platform-
|
|
34
|
-
"@b9g/
|
|
35
|
-
"@
|
|
30
|
+
"@logtape/file": "^1.0.0",
|
|
31
|
+
"@b9g/filesystem": "^0.1.8",
|
|
32
|
+
"@b9g/http-errors": "^0.2.0-beta.0",
|
|
33
|
+
"@b9g/libuild": "^0.1.22",
|
|
34
|
+
"@b9g/platform": "^0.1.14-beta.2",
|
|
35
|
+
"@b9g/platform-bun": "^0.1.12-beta.0",
|
|
36
|
+
"@b9g/platform-cloudflare": "^0.1.12-beta.0",
|
|
37
|
+
"@b9g/platform-node": "^0.1.14-beta.0",
|
|
38
|
+
"@b9g/router": "^0.2.0-beta.1",
|
|
39
|
+
"@types/bun": "^1.3.4",
|
|
40
|
+
"@typescript-eslint/eslint-plugin": "^8.0.0",
|
|
41
|
+
"@typescript-eslint/parser": "^8.0.0",
|
|
42
|
+
"eslint": "^9.0.0",
|
|
43
|
+
"eslint-config-prettier": "^10.0.0",
|
|
44
|
+
"eslint-plugin-prettier": "^5.0.0",
|
|
36
45
|
"mitata": "^1.0.34",
|
|
37
46
|
"typescript": "^5.7.3"
|
|
38
47
|
},
|
|
39
48
|
"peerDependencies": {
|
|
40
|
-
"@b9g/node-webworker": "^0.1
|
|
41
|
-
"@b9g/platform": "^0.1.
|
|
42
|
-
"@b9g/platform-node": "^0.1.
|
|
43
|
-
"@b9g/platform-cloudflare": "^0.1.
|
|
44
|
-
"@b9g/platform-bun": "^0.1.
|
|
45
|
-
"@b9g/cache": "^0.
|
|
46
|
-
"@b9g/filesystem": "^0.1.
|
|
47
|
-
"@b9g/http-errors": "^0.
|
|
49
|
+
"@b9g/node-webworker": "^0.2.0-beta.1",
|
|
50
|
+
"@b9g/platform": "^0.1.14-beta.2",
|
|
51
|
+
"@b9g/platform-node": "^0.1.14-beta.0",
|
|
52
|
+
"@b9g/platform-cloudflare": "^0.1.12-beta.0",
|
|
53
|
+
"@b9g/platform-bun": "^0.1.12-beta.0",
|
|
54
|
+
"@b9g/cache": "^0.2.0-beta.0",
|
|
55
|
+
"@b9g/filesystem": "^0.1.8",
|
|
56
|
+
"@b9g/http-errors": "^0.2.0-beta.0"
|
|
48
57
|
},
|
|
49
58
|
"peerDependenciesMeta": {
|
|
50
59
|
"@b9g/platform": {
|
|
@@ -70,8 +79,7 @@
|
|
|
70
79
|
}
|
|
71
80
|
},
|
|
72
81
|
"type": "module",
|
|
73
|
-
"types": "src/
|
|
74
|
-
"module": "src/worker-entry.js",
|
|
82
|
+
"types": "./src/config.d.ts",
|
|
75
83
|
"exports": {
|
|
76
84
|
"./package.json": "./package.json",
|
|
77
85
|
"./bin/cli": {
|
|
@@ -89,18 +97,6 @@
|
|
|
89
97
|
"./bin/create.js": {
|
|
90
98
|
"types": "./dist/bin/create.d.ts",
|
|
91
99
|
"import": "./dist/bin/create.js"
|
|
92
|
-
},
|
|
93
|
-
".": {
|
|
94
|
-
"types": "./src/worker-entry.d.ts",
|
|
95
|
-
"import": "./src/worker-entry.js"
|
|
96
|
-
},
|
|
97
|
-
"./worker-entry": {
|
|
98
|
-
"types": "./src/worker-entry.d.ts",
|
|
99
|
-
"import": "./src/worker-entry.js"
|
|
100
|
-
},
|
|
101
|
-
"./worker-entry.js": {
|
|
102
|
-
"types": "./src/worker-entry.d.ts",
|
|
103
|
-
"import": "./src/worker-entry.js"
|
|
104
100
|
}
|
|
105
101
|
}
|
|
106
102
|
}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ServerBundler,
|
|
3
|
+
createPlatform
|
|
4
|
+
} from "./chunk-VWH6D26D.js";
|
|
5
|
+
import {
|
|
6
|
+
findProjectRoot,
|
|
7
|
+
findWorkspaceRoot
|
|
8
|
+
} from "./chunk-PTLNYIRW.js";
|
|
9
|
+
|
|
10
|
+
// src/commands/build.ts
|
|
11
|
+
import { resolve, join, dirname } from "path";
|
|
12
|
+
import { getLogger } from "@logtape/logtape";
|
|
13
|
+
import { resolvePlatform } from "@b9g/platform";
|
|
14
|
+
import { readFile, writeFile } from "fs/promises";
|
|
15
|
+
var logger = getLogger(["shovel"]);
|
|
16
|
+
async function buildForProduction({
|
|
17
|
+
entrypoint,
|
|
18
|
+
outDir,
|
|
19
|
+
platform = "node",
|
|
20
|
+
userBuildConfig,
|
|
21
|
+
lifecycle
|
|
22
|
+
}) {
|
|
23
|
+
const entryPath = resolve(entrypoint);
|
|
24
|
+
const outputDir = resolve(outDir);
|
|
25
|
+
const serverDir = join(outputDir, "server");
|
|
26
|
+
const projectRoot = findProjectRoot(dirname(entryPath));
|
|
27
|
+
logger.debug("Entry:", { entryPath });
|
|
28
|
+
logger.debug("Output:", { outputDir });
|
|
29
|
+
logger.debug("Target platform:", { platform });
|
|
30
|
+
logger.debug("Project root:", { projectRoot });
|
|
31
|
+
const platformInstance = await createPlatform(platform);
|
|
32
|
+
const platformESBuildConfig = platformInstance.getESBuildConfig();
|
|
33
|
+
const bundler = new ServerBundler({
|
|
34
|
+
entrypoint,
|
|
35
|
+
outDir,
|
|
36
|
+
platform: platformInstance,
|
|
37
|
+
platformESBuildConfig,
|
|
38
|
+
userBuildConfig,
|
|
39
|
+
lifecycle
|
|
40
|
+
});
|
|
41
|
+
const { success, outputs } = await bundler.build();
|
|
42
|
+
if (!success) {
|
|
43
|
+
throw new Error("Build failed");
|
|
44
|
+
}
|
|
45
|
+
await generatePackageJSON({ serverDir, platform, entryPath });
|
|
46
|
+
logger.debug("Built app to", { outputDir });
|
|
47
|
+
logger.debug("Server files", { dir: serverDir });
|
|
48
|
+
logger.debug("Public files", { dir: join(outputDir, "public") });
|
|
49
|
+
logger.info("Build complete: {path}", {
|
|
50
|
+
path: outputs.index || outputs.worker
|
|
51
|
+
});
|
|
52
|
+
return {
|
|
53
|
+
platform: platformInstance,
|
|
54
|
+
workerPath: outputs.worker
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
async function generatePackageJSON({
|
|
58
|
+
serverDir,
|
|
59
|
+
platform,
|
|
60
|
+
entryPath
|
|
61
|
+
}) {
|
|
62
|
+
const entryDir = dirname(entryPath);
|
|
63
|
+
const sourcePackageJsonPath = resolve(entryDir, "package.json");
|
|
64
|
+
try {
|
|
65
|
+
const packageJSONContent = await readFile(sourcePackageJsonPath, "utf8");
|
|
66
|
+
try {
|
|
67
|
+
JSON.parse(packageJSONContent);
|
|
68
|
+
} catch (parseError) {
|
|
69
|
+
throw new Error(`Invalid package.json format: ${parseError}`);
|
|
70
|
+
}
|
|
71
|
+
await writeFile(
|
|
72
|
+
join(serverDir, "package.json"),
|
|
73
|
+
packageJSONContent,
|
|
74
|
+
"utf8"
|
|
75
|
+
);
|
|
76
|
+
logger.debug("Copied package.json", { serverDir });
|
|
77
|
+
} catch (error) {
|
|
78
|
+
logger.debug("Could not copy package.json: {error}", { error });
|
|
79
|
+
try {
|
|
80
|
+
const generatedPackageJson = await generateExecutablePackageJSON(platform);
|
|
81
|
+
await writeFile(
|
|
82
|
+
join(serverDir, "package.json"),
|
|
83
|
+
JSON.stringify(generatedPackageJson, null, 2),
|
|
84
|
+
"utf8"
|
|
85
|
+
);
|
|
86
|
+
logger.debug("Generated package.json", { platform });
|
|
87
|
+
} catch (generateError) {
|
|
88
|
+
logger.debug("Could not generate package.json: {error}", {
|
|
89
|
+
error: generateError
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
async function generateExecutablePackageJSON(platform) {
|
|
95
|
+
const packageJSON = {
|
|
96
|
+
name: "shovel-executable",
|
|
97
|
+
version: "1.0.0",
|
|
98
|
+
type: "module",
|
|
99
|
+
private: true,
|
|
100
|
+
dependencies: {}
|
|
101
|
+
};
|
|
102
|
+
const isWorkspaceEnvironment = findWorkspaceRoot() !== null;
|
|
103
|
+
if (isWorkspaceEnvironment) {
|
|
104
|
+
packageJSON.dependencies = {};
|
|
105
|
+
} else {
|
|
106
|
+
switch (platform) {
|
|
107
|
+
case "node":
|
|
108
|
+
packageJSON.dependencies["@b9g/platform-node"] = "^0.1.0";
|
|
109
|
+
break;
|
|
110
|
+
case "bun":
|
|
111
|
+
packageJSON.dependencies["@b9g/platform-bun"] = "^0.1.0";
|
|
112
|
+
break;
|
|
113
|
+
case "cloudflare":
|
|
114
|
+
packageJSON.dependencies["@b9g/platform-cloudflare"] = "^0.1.0";
|
|
115
|
+
break;
|
|
116
|
+
default:
|
|
117
|
+
packageJSON.dependencies["@b9g/platform"] = "^0.1.0";
|
|
118
|
+
}
|
|
119
|
+
packageJSON.dependencies["@b9g/cache"] = "^0.1.0";
|
|
120
|
+
packageJSON.dependencies["@b9g/filesystem"] = "^0.1.0";
|
|
121
|
+
}
|
|
122
|
+
return packageJSON;
|
|
123
|
+
}
|
|
124
|
+
async function buildCommand(entrypoint, options, config) {
|
|
125
|
+
const platform = resolvePlatform({ ...options, config });
|
|
126
|
+
let lifecycleOption;
|
|
127
|
+
if (options.lifecycle) {
|
|
128
|
+
const stage = typeof options.lifecycle === "string" ? options.lifecycle : "activate";
|
|
129
|
+
if (stage !== "install" && stage !== "activate") {
|
|
130
|
+
throw new Error(
|
|
131
|
+
`Invalid lifecycle stage: ${stage}. Must be "install" or "activate".`
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
lifecycleOption = { stage };
|
|
135
|
+
}
|
|
136
|
+
const { platform: platformInstance, workerPath } = await buildForProduction({
|
|
137
|
+
entrypoint,
|
|
138
|
+
outDir: "dist",
|
|
139
|
+
platform,
|
|
140
|
+
userBuildConfig: config.build,
|
|
141
|
+
lifecycle: lifecycleOption
|
|
142
|
+
});
|
|
143
|
+
if (lifecycleOption) {
|
|
144
|
+
if (!workerPath) {
|
|
145
|
+
throw new Error("No worker entry point found in build outputs");
|
|
146
|
+
}
|
|
147
|
+
logger.info("Running ServiceWorker lifecycle: {stage}", {
|
|
148
|
+
stage: lifecycleOption.stage
|
|
149
|
+
});
|
|
150
|
+
await platformInstance.serviceWorker.register(workerPath);
|
|
151
|
+
await platformInstance.serviceWorker.ready;
|
|
152
|
+
await platformInstance.serviceWorker.terminate();
|
|
153
|
+
logger.info("Lifecycle complete");
|
|
154
|
+
}
|
|
155
|
+
await platformInstance.dispose();
|
|
156
|
+
}
|
|
157
|
+
export {
|
|
158
|
+
buildCommand,
|
|
159
|
+
buildForProduction
|
|
160
|
+
};
|