@ubean/preset 0.1.13 → 0.2.0
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/dist/index.d.ts +702 -1
- package/dist/index.js +962 -29
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -64,8 +64,10 @@ function diagnoseCapabilities(presetName, presetCapabilities, requirements) {
|
|
|
64
64
|
required: req.required,
|
|
65
65
|
message: supported ? `${req.capability} is supported` : message
|
|
66
66
|
});
|
|
67
|
-
if (!supported)
|
|
68
|
-
|
|
67
|
+
if (!supported) {
|
|
68
|
+
if (req.required) errors.push(`[${presetName}] ${message}`);
|
|
69
|
+
else warnings.push(`[${presetName}] ${message}`);
|
|
70
|
+
}
|
|
69
71
|
}
|
|
70
72
|
return {
|
|
71
73
|
valid: errors.length === 0,
|
|
@@ -243,6 +245,443 @@ function getPresetNames() {
|
|
|
243
245
|
function getPresetAliases() {
|
|
244
246
|
return new Map(presetNames);
|
|
245
247
|
}
|
|
248
|
+
/**
|
|
249
|
+
* AWS Lambda preset —— AWS Lambda + API Gateway / Function URL 模式
|
|
250
|
+
*
|
|
251
|
+
* 构建输出:`dist/aws/lambda/index.mjs`(Lambda handler)
|
|
252
|
+
* 预览命令:`sam local start-api`(通过 AWS SAM 本地模拟)
|
|
253
|
+
* 部署命令:`sam deploy --guided`(交互式部署)
|
|
254
|
+
*
|
|
255
|
+
* Lambda handler 签名:
|
|
256
|
+
* ```typescript
|
|
257
|
+
* export const handler = async (event, context) => { ... };
|
|
258
|
+
* ```
|
|
259
|
+
*/
|
|
260
|
+
const awsPreset = definePreset({
|
|
261
|
+
extends: "node",
|
|
262
|
+
capabilities: createCapabilitySet({
|
|
263
|
+
staticServe: true,
|
|
264
|
+
websocket: false,
|
|
265
|
+
sse: true,
|
|
266
|
+
cronTriggers: true,
|
|
267
|
+
queues: true,
|
|
268
|
+
kv: true,
|
|
269
|
+
storage: true,
|
|
270
|
+
database: true,
|
|
271
|
+
envVars: true,
|
|
272
|
+
secrets: true,
|
|
273
|
+
nodeCompat: true,
|
|
274
|
+
streaming: true,
|
|
275
|
+
compression: true,
|
|
276
|
+
https: true,
|
|
277
|
+
http2: true,
|
|
278
|
+
middleware: false,
|
|
279
|
+
bodyLimit: true,
|
|
280
|
+
multipart: true,
|
|
281
|
+
rpc: false
|
|
282
|
+
}),
|
|
283
|
+
entry: "server",
|
|
284
|
+
exportConditions: ["aws", "aws-lambda"],
|
|
285
|
+
build: {
|
|
286
|
+
outputDir: "dist/aws",
|
|
287
|
+
format: "esm",
|
|
288
|
+
externals: [
|
|
289
|
+
"hono",
|
|
290
|
+
"c12",
|
|
291
|
+
"citty",
|
|
292
|
+
"tslog",
|
|
293
|
+
"defu",
|
|
294
|
+
"hookable",
|
|
295
|
+
"pathe",
|
|
296
|
+
"ufo",
|
|
297
|
+
"zod",
|
|
298
|
+
"@aws-sdk/*"
|
|
299
|
+
]
|
|
300
|
+
},
|
|
301
|
+
output: {
|
|
302
|
+
dir: "dist/aws",
|
|
303
|
+
serverDir: "dist/aws/lambda",
|
|
304
|
+
publicDir: "dist/aws/public"
|
|
305
|
+
},
|
|
306
|
+
runtime: {
|
|
307
|
+
entry: "lambda/index.mjs",
|
|
308
|
+
handler: "handler",
|
|
309
|
+
compatibilityDate: "2024-09-01"
|
|
310
|
+
},
|
|
311
|
+
serve: {
|
|
312
|
+
host: "localhost",
|
|
313
|
+
port: 3001
|
|
314
|
+
},
|
|
315
|
+
commands: {
|
|
316
|
+
preview: "sam local start-api",
|
|
317
|
+
deploy: "sam deploy --guided"
|
|
318
|
+
},
|
|
319
|
+
hooks: {
|
|
320
|
+
"build:before": async () => {},
|
|
321
|
+
"build:after": async () => {}
|
|
322
|
+
}
|
|
323
|
+
}, {
|
|
324
|
+
name: "aws",
|
|
325
|
+
aliases: [
|
|
326
|
+
"aws-lambda",
|
|
327
|
+
"lambda",
|
|
328
|
+
"amazon",
|
|
329
|
+
"sam"
|
|
330
|
+
],
|
|
331
|
+
stdName: "aws_lambda",
|
|
332
|
+
dev: true,
|
|
333
|
+
compatibilityDate: "2024-09-01",
|
|
334
|
+
url: "https://docs.aws.amazon.com/lambda/latest/dg/welcome.html"
|
|
335
|
+
});
|
|
336
|
+
/**
|
|
337
|
+
* 生成 AWS SAM template.yaml 配置对象
|
|
338
|
+
*/
|
|
339
|
+
function generateAwsSamConfig(options) {
|
|
340
|
+
const handler = options.handler || "dist/aws/lambda/index.handler";
|
|
341
|
+
const runtime = options.runtime || "nodejs20.x";
|
|
342
|
+
const codeUri = options.codeUri || "dist/aws/lambda";
|
|
343
|
+
const events = { ApiEvent: {
|
|
344
|
+
Type: "Api",
|
|
345
|
+
Properties: {
|
|
346
|
+
Path: "/{proxy+}",
|
|
347
|
+
Method: "ANY",
|
|
348
|
+
RestApiId: { Ref: "ApiGateway" }
|
|
349
|
+
}
|
|
350
|
+
} };
|
|
351
|
+
if (options.cronSchedules) for (const cron of options.cronSchedules) events[`Cron${cron.name}`] = {
|
|
352
|
+
Type: "Schedule",
|
|
353
|
+
Properties: {
|
|
354
|
+
Schedule: cron.schedule,
|
|
355
|
+
Enabled: cron.enabled ?? true,
|
|
356
|
+
Name: cron.name
|
|
357
|
+
}
|
|
358
|
+
};
|
|
359
|
+
const template = {
|
|
360
|
+
AWSTemplateFormatVersion: "2010-09-09",
|
|
361
|
+
Transform: "AWS::Serverless-2016-10-31",
|
|
362
|
+
Description: "ubean application deployed to AWS Lambda",
|
|
363
|
+
Globals: { Function: {
|
|
364
|
+
Runtime: runtime,
|
|
365
|
+
MemorySize: options.memorySize ?? 256,
|
|
366
|
+
Timeout: options.timeout ?? 30,
|
|
367
|
+
Handler: handler
|
|
368
|
+
} },
|
|
369
|
+
Resources: {
|
|
370
|
+
ApiGateway: {
|
|
371
|
+
Type: "AWS::Serverless::Api",
|
|
372
|
+
Properties: {
|
|
373
|
+
StageName: options.apiStage || "prod",
|
|
374
|
+
EndpointConfiguration: { Type: "REGIONAL" }
|
|
375
|
+
}
|
|
376
|
+
},
|
|
377
|
+
UbeanFunction: {
|
|
378
|
+
Type: "AWS::Serverless::Function",
|
|
379
|
+
Properties: {
|
|
380
|
+
FunctionName: options.functionName || "ubean-app",
|
|
381
|
+
Handler: handler,
|
|
382
|
+
Runtime: runtime,
|
|
383
|
+
CodeUri: codeUri,
|
|
384
|
+
MemorySize: options.memorySize ?? 256,
|
|
385
|
+
Timeout: options.timeout ?? 30,
|
|
386
|
+
Events: events,
|
|
387
|
+
Policies: ["AmazonDynamoDBReadOnlyAccess", "AmazonS3ReadOnlyAccess"]
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
},
|
|
391
|
+
Outputs: {
|
|
392
|
+
ApiUrl: {
|
|
393
|
+
Description: "URL of the API endpoint",
|
|
394
|
+
Value: { "Fn::Sub": `https://\${ApiGateway}.execute-api.\${AWS::Region}.amazonaws.com/${options.apiStage || "prod"}` }
|
|
395
|
+
},
|
|
396
|
+
FunctionArn: {
|
|
397
|
+
Description: "ARN of the Lambda function",
|
|
398
|
+
Value: { "Fn::GetAtt": ["UbeanFunction", "Arn"] }
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
};
|
|
402
|
+
if (options.environment && Object.keys(options.environment).length > 0) template.Resources.UbeanFunction.Properties.Environment = { Variables: options.environment };
|
|
403
|
+
return template;
|
|
404
|
+
}
|
|
405
|
+
/**
|
|
406
|
+
* 序列化 AWS SAM template 为 YAML 字符串
|
|
407
|
+
*/
|
|
408
|
+
function serializeAwsSamConfig(template) {
|
|
409
|
+
return serializeYaml(template, 0);
|
|
410
|
+
}
|
|
411
|
+
function serializeYaml(value, indent) {
|
|
412
|
+
const pad = " ".repeat(indent);
|
|
413
|
+
if (value === null || value === void 0) return "null";
|
|
414
|
+
if (typeof value === "string") {
|
|
415
|
+
if (value.startsWith("Fn::") || value.startsWith("AWS::")) return value;
|
|
416
|
+
if (/^[\w./-]+$/.test(value) && !value.includes(" ")) return value;
|
|
417
|
+
return `"${escapeYamlString(value)}"`;
|
|
418
|
+
}
|
|
419
|
+
if (typeof value === "number" || typeof value === "boolean") return String(value);
|
|
420
|
+
if (Array.isArray(value)) {
|
|
421
|
+
if (value.length === 0) return "[]";
|
|
422
|
+
return value.map((item) => `${pad}- ${serializeYaml(item, indent + 1).trimStart()}`).join("\n");
|
|
423
|
+
}
|
|
424
|
+
if (typeof value === "object") {
|
|
425
|
+
const entries = Object.entries(value).filter(([, v]) => v !== void 0 && v !== null);
|
|
426
|
+
if (entries.length === 0) return "{}";
|
|
427
|
+
return entries.map(([key, val]) => {
|
|
428
|
+
if (val !== null && typeof val === "object" && !Array.isArray(val)) {
|
|
429
|
+
const nested = serializeYaml(val, indent + 1);
|
|
430
|
+
if (nested === "{}") return `${pad}${key}: {}`;
|
|
431
|
+
return `${pad}${key}:\n${nested}`;
|
|
432
|
+
}
|
|
433
|
+
if (Array.isArray(val) && val.length > 0) {
|
|
434
|
+
const items = val.map((item) => {
|
|
435
|
+
if (item !== null && typeof item === "object") {
|
|
436
|
+
const nested = serializeYaml(item, indent + 2).trimStart();
|
|
437
|
+
return `${pad} - ${nested}`;
|
|
438
|
+
}
|
|
439
|
+
return `${pad} - ${serializeYaml(item, 0)}`;
|
|
440
|
+
}).join("\n");
|
|
441
|
+
return `${pad}${key}:\n${items}`;
|
|
442
|
+
}
|
|
443
|
+
return `${pad}${key}: ${serializeYaml(val, 0)}`;
|
|
444
|
+
}).join("\n");
|
|
445
|
+
}
|
|
446
|
+
return String(value);
|
|
447
|
+
}
|
|
448
|
+
function escapeYamlString(str) {
|
|
449
|
+
return str.replace(/\\/g, "\\\\").replace(/"/g, "\\\"");
|
|
450
|
+
}
|
|
451
|
+
/**
|
|
452
|
+
* Azure Static Web Apps preset
|
|
453
|
+
*
|
|
454
|
+
* 构建输出:`dist/azure/functions/index.mjs`(Azure Functions handler)
|
|
455
|
+
* 预览命令:`swa start`(Azure SWA CLI 本地模拟)
|
|
456
|
+
* 部署命令:`swa deploy`
|
|
457
|
+
*
|
|
458
|
+
* Azure Functions handler 签名:
|
|
459
|
+
* ```typescript
|
|
460
|
+
* export default async function handler(context, req) { ... };
|
|
461
|
+
* ```
|
|
462
|
+
*/
|
|
463
|
+
const azurePreset = definePreset({
|
|
464
|
+
extends: "node",
|
|
465
|
+
capabilities: createCapabilitySet({
|
|
466
|
+
staticServe: true,
|
|
467
|
+
websocket: false,
|
|
468
|
+
sse: true,
|
|
469
|
+
cronTriggers: true,
|
|
470
|
+
queues: true,
|
|
471
|
+
kv: true,
|
|
472
|
+
storage: true,
|
|
473
|
+
database: true,
|
|
474
|
+
envVars: true,
|
|
475
|
+
secrets: true,
|
|
476
|
+
nodeCompat: true,
|
|
477
|
+
streaming: true,
|
|
478
|
+
compression: true,
|
|
479
|
+
https: true,
|
|
480
|
+
http2: true,
|
|
481
|
+
middleware: true,
|
|
482
|
+
bodyLimit: true,
|
|
483
|
+
multipart: true,
|
|
484
|
+
rpc: false
|
|
485
|
+
}),
|
|
486
|
+
entry: "server",
|
|
487
|
+
exportConditions: ["azure", "azure-functions"],
|
|
488
|
+
build: {
|
|
489
|
+
outputDir: "dist/azure",
|
|
490
|
+
format: "esm",
|
|
491
|
+
externals: [
|
|
492
|
+
"hono",
|
|
493
|
+
"c12",
|
|
494
|
+
"citty",
|
|
495
|
+
"tslog",
|
|
496
|
+
"defu",
|
|
497
|
+
"hookable",
|
|
498
|
+
"pathe",
|
|
499
|
+
"ufo",
|
|
500
|
+
"zod",
|
|
501
|
+
"@azure/*"
|
|
502
|
+
]
|
|
503
|
+
},
|
|
504
|
+
output: {
|
|
505
|
+
dir: "dist/azure",
|
|
506
|
+
serverDir: "dist/azure/functions",
|
|
507
|
+
publicDir: "dist/azure/public"
|
|
508
|
+
},
|
|
509
|
+
runtime: {
|
|
510
|
+
entry: "functions/index.mjs",
|
|
511
|
+
handler: "handler",
|
|
512
|
+
compatibilityDate: "2024-09-01"
|
|
513
|
+
},
|
|
514
|
+
serve: {
|
|
515
|
+
host: "localhost",
|
|
516
|
+
port: 4280
|
|
517
|
+
},
|
|
518
|
+
commands: {
|
|
519
|
+
preview: "swa start",
|
|
520
|
+
deploy: "swa deploy"
|
|
521
|
+
},
|
|
522
|
+
hooks: {
|
|
523
|
+
"build:before": async () => {},
|
|
524
|
+
"build:after": async () => {}
|
|
525
|
+
}
|
|
526
|
+
}, {
|
|
527
|
+
name: "azure",
|
|
528
|
+
aliases: [
|
|
529
|
+
"azure-swa",
|
|
530
|
+
"azure-static-web-apps",
|
|
531
|
+
"swa",
|
|
532
|
+
"azure-functions"
|
|
533
|
+
],
|
|
534
|
+
stdName: "azure_static_web_apps",
|
|
535
|
+
dev: true,
|
|
536
|
+
compatibilityDate: "2024-09-01",
|
|
537
|
+
url: "https://learn.microsoft.com/en-us/azure/static-web-apps/"
|
|
538
|
+
});
|
|
539
|
+
/**
|
|
540
|
+
* 生成 Azure Static Web Apps 配置对象
|
|
541
|
+
*/
|
|
542
|
+
function generateStaticWebAppConfig(options) {
|
|
543
|
+
const config = {
|
|
544
|
+
platform: { apiRuntime: options.apiRuntime || "node:20" },
|
|
545
|
+
routes: options.routes || [{
|
|
546
|
+
route: "/api/*",
|
|
547
|
+
rewrite: options.apiEntry || "/api/functions"
|
|
548
|
+
}],
|
|
549
|
+
navigationFallback: { rewrite: options.navigationFallback || "/index.html" }
|
|
550
|
+
};
|
|
551
|
+
if (options.globalHeaders && Object.keys(options.globalHeaders).length > 0) config.globalHeaders = options.globalHeaders;
|
|
552
|
+
return config;
|
|
553
|
+
}
|
|
554
|
+
/**
|
|
555
|
+
* 序列化 staticwebapp.config.json 为 JSON 字符串
|
|
556
|
+
*/
|
|
557
|
+
function serializeStaticWebAppConfig(config) {
|
|
558
|
+
return `${JSON.stringify(config, null, 2)}\n`;
|
|
559
|
+
}
|
|
560
|
+
/**
|
|
561
|
+
* Bun preset —— Bun 运行时模式
|
|
562
|
+
*
|
|
563
|
+
* 继承 Node preset 的全部能力,使用 Bun 运行时特性:
|
|
564
|
+
* - 原生 TypeScript 支持(无需编译)
|
|
565
|
+
* - 内置 SQLite (bun:sqlite)
|
|
566
|
+
* - 原生 WebSocket (Bun.serve)
|
|
567
|
+
* - 更快的启动和运行速度
|
|
568
|
+
*
|
|
569
|
+
* 构建输出:`dist/bun/server/index.mjs`
|
|
570
|
+
* 预览命令:`bun run dist/bun/server/index.mjs`
|
|
571
|
+
* 部署命令:`bun run dist/bun/server/index.mjs`
|
|
572
|
+
*/
|
|
573
|
+
const bunPreset = definePreset({
|
|
574
|
+
extends: "node",
|
|
575
|
+
capabilities: createCapabilitySet({
|
|
576
|
+
staticServe: true,
|
|
577
|
+
websocket: true,
|
|
578
|
+
sse: true,
|
|
579
|
+
cronTriggers: true,
|
|
580
|
+
queues: false,
|
|
581
|
+
kv: false,
|
|
582
|
+
storage: true,
|
|
583
|
+
database: true,
|
|
584
|
+
envVars: true,
|
|
585
|
+
secrets: true,
|
|
586
|
+
nodeCompat: true,
|
|
587
|
+
streaming: true,
|
|
588
|
+
compression: true,
|
|
589
|
+
https: true,
|
|
590
|
+
http2: true,
|
|
591
|
+
middleware: true,
|
|
592
|
+
bodyLimit: true,
|
|
593
|
+
multipart: true,
|
|
594
|
+
rpc: false
|
|
595
|
+
}),
|
|
596
|
+
entry: "server",
|
|
597
|
+
exportConditions: ["bun"],
|
|
598
|
+
build: {
|
|
599
|
+
outputDir: "dist/bun",
|
|
600
|
+
format: "esm",
|
|
601
|
+
externals: [
|
|
602
|
+
"hono",
|
|
603
|
+
"c12",
|
|
604
|
+
"citty",
|
|
605
|
+
"tslog",
|
|
606
|
+
"defu",
|
|
607
|
+
"hookable",
|
|
608
|
+
"pathe",
|
|
609
|
+
"ufo",
|
|
610
|
+
"zod",
|
|
611
|
+
"bun:sqlite"
|
|
612
|
+
]
|
|
613
|
+
},
|
|
614
|
+
output: {
|
|
615
|
+
dir: "dist/bun",
|
|
616
|
+
serverDir: "dist/bun/server",
|
|
617
|
+
publicDir: "dist/bun/public"
|
|
618
|
+
},
|
|
619
|
+
runtime: {
|
|
620
|
+
entry: "server/index.mjs",
|
|
621
|
+
handler: "handler",
|
|
622
|
+
compatibilityDate: "2024-09-01"
|
|
623
|
+
},
|
|
624
|
+
serve: {
|
|
625
|
+
host: "localhost",
|
|
626
|
+
port: 3e3
|
|
627
|
+
},
|
|
628
|
+
commands: {
|
|
629
|
+
preview: "bun run dist/bun/server/index.mjs",
|
|
630
|
+
deploy: "bun run dist/bun/server/index.mjs"
|
|
631
|
+
},
|
|
632
|
+
hooks: {
|
|
633
|
+
"build:before": async () => {},
|
|
634
|
+
"build:after": async () => {}
|
|
635
|
+
}
|
|
636
|
+
}, {
|
|
637
|
+
name: "bun",
|
|
638
|
+
aliases: ["bun-runtime"],
|
|
639
|
+
stdName: "bun",
|
|
640
|
+
dev: true,
|
|
641
|
+
compatibilityDate: "2024-09-01",
|
|
642
|
+
url: "https://bun.sh/docs/api/http"
|
|
643
|
+
});
|
|
644
|
+
/**
|
|
645
|
+
* 生成 Bunfig 配置对象
|
|
646
|
+
*/
|
|
647
|
+
function generateBunfigConfig(options) {
|
|
648
|
+
const config = {};
|
|
649
|
+
if (options.registry !== void 0 || options.lockfile !== void 0 || options.production !== void 0) {
|
|
650
|
+
config.install = {};
|
|
651
|
+
if (options.registry) config.install.registry = options.registry;
|
|
652
|
+
if (options.lockfile !== void 0) config.install.lockfile = options.lockfile;
|
|
653
|
+
if (options.production !== void 0) config.install.production = options.production;
|
|
654
|
+
}
|
|
655
|
+
if (options.preload && options.preload.length > 0) config.test = { preload: options.preload };
|
|
656
|
+
return config;
|
|
657
|
+
}
|
|
658
|
+
/**
|
|
659
|
+
* 序列化 Bunfig 配置为 TOML 字符串
|
|
660
|
+
*/
|
|
661
|
+
function serializeBunfigConfig(config) {
|
|
662
|
+
const lines = [];
|
|
663
|
+
if (config.install) {
|
|
664
|
+
lines.push("[install]");
|
|
665
|
+
if (config.install.registry) lines.push(`registry = "${escapeToml$2(config.install.registry)}"`);
|
|
666
|
+
if (config.install.lockfile !== void 0) lines.push(`lockfile = ${config.install.lockfile}`);
|
|
667
|
+
if (config.install.production !== void 0) lines.push(`production = ${config.install.production}`);
|
|
668
|
+
lines.push("");
|
|
669
|
+
}
|
|
670
|
+
if (config.test) {
|
|
671
|
+
lines.push("[test]");
|
|
672
|
+
if (config.test.coverage !== void 0) lines.push(`coverage = ${config.test.coverage}`);
|
|
673
|
+
if (config.test.coverageThreshold !== void 0) lines.push(`coverageThreshold = ${config.test.coverageThreshold}`);
|
|
674
|
+
if (config.test.preload && config.test.preload.length > 0) {
|
|
675
|
+
const preloads = config.test.preload.map((p) => `"${escapeToml$2(p)}"`).join(", ");
|
|
676
|
+
lines.push(`preload = [${preloads}]`);
|
|
677
|
+
}
|
|
678
|
+
lines.push("");
|
|
679
|
+
}
|
|
680
|
+
return `${lines.join("\n")}\n`;
|
|
681
|
+
}
|
|
682
|
+
function escapeToml$2(str) {
|
|
683
|
+
return str.replace(/\\/g, "\\\\").replace(/"/g, "\\\"");
|
|
684
|
+
}
|
|
246
685
|
const cloudflarePreset = definePreset({
|
|
247
686
|
capabilities: createCapabilitySet({
|
|
248
687
|
staticServe: true,
|
|
@@ -275,7 +714,7 @@ const cloudflarePreset = definePreset({
|
|
|
275
714
|
"hono",
|
|
276
715
|
"c12",
|
|
277
716
|
"citty",
|
|
278
|
-
"
|
|
717
|
+
"tslog",
|
|
279
718
|
"defu",
|
|
280
719
|
"hookable",
|
|
281
720
|
"pathe",
|
|
@@ -374,18 +813,18 @@ function serializeWranglerToml(config) {
|
|
|
374
813
|
const indent = "";
|
|
375
814
|
function push(key, value, prefix = "") {
|
|
376
815
|
if (value === void 0 || value === null) return;
|
|
377
|
-
if (typeof value === "string") lines.push(`${prefix}${key} = "${escapeToml(value)}"`);
|
|
816
|
+
if (typeof value === "string") lines.push(`${prefix}${key} = "${escapeToml$1(value)}"`);
|
|
378
817
|
else if (typeof value === "boolean") lines.push(`${prefix}${key} = ${value}`);
|
|
379
818
|
else if (typeof value === "number") lines.push(`${prefix}${key} = ${value}`);
|
|
380
819
|
else if (Array.isArray(value)) {
|
|
381
820
|
if (value.length === 0) return;
|
|
382
821
|
if (typeof value[0] === "string") {
|
|
383
|
-
const items = value.map((v) => `"${escapeToml(String(v))}"`).join(", ");
|
|
822
|
+
const items = value.map((v) => `"${escapeToml$1(String(v))}"`).join(", ");
|
|
384
823
|
lines.push(`${prefix}${key} = [${items}]`);
|
|
385
824
|
} else {
|
|
386
825
|
lines.push(`${prefix}[[${key}]]`);
|
|
387
826
|
for (const item of value) lines.push(`${prefix} { ${Object.entries(item).filter(([, v]) => v !== void 0 && v !== null).map(([k, v]) => {
|
|
388
|
-
if (typeof v === "string") return `${k} = "${escapeToml(v)}"`;
|
|
827
|
+
if (typeof v === "string") return `${k} = "${escapeToml$1(v)}"`;
|
|
389
828
|
if (typeof v === "boolean") return `${k} = ${v}`;
|
|
390
829
|
return `${k} = ${v}`;
|
|
391
830
|
}).join(", ")} }`);
|
|
@@ -399,11 +838,11 @@ function serializeWranglerToml(config) {
|
|
|
399
838
|
for (const item of v) {
|
|
400
839
|
const itemEntries = Object.entries(item).filter(([, iv]) => iv !== void 0 && iv !== null);
|
|
401
840
|
lines.push(itemEntries.map(([ik, iv]) => {
|
|
402
|
-
if (typeof iv === "string") return `${prefix} ${ik} = "${escapeToml(iv)}"`;
|
|
841
|
+
if (typeof iv === "string") return `${prefix} ${ik} = "${escapeToml$1(iv)}"`;
|
|
403
842
|
return `${prefix} ${ik} = ${iv}`;
|
|
404
843
|
}).join("\n"));
|
|
405
844
|
}
|
|
406
|
-
} else if (typeof v === "string") lines.push(`${prefix}${k} = "${escapeToml(v)}"`);
|
|
845
|
+
} else if (typeof v === "string") lines.push(`${prefix}${k} = "${escapeToml$1(v)}"`);
|
|
407
846
|
else if (typeof v === "boolean") lines.push(`${prefix}${k} = ${v}`);
|
|
408
847
|
else if (typeof v === "number") lines.push(`${prefix}${k} = ${v}`);
|
|
409
848
|
}
|
|
@@ -418,44 +857,291 @@ function serializeWranglerToml(config) {
|
|
|
418
857
|
if (config.observability) push("observability", config.observability);
|
|
419
858
|
if (config.kv_namespaces && config.kv_namespaces.length > 0) for (const ns of config.kv_namespaces) {
|
|
420
859
|
const nsLines = [`${indent}[[kv_namespaces]]`];
|
|
421
|
-
if (ns.binding) nsLines.push(`${indent}binding = "${escapeToml(ns.binding)}"`);
|
|
422
|
-
if (ns.id) nsLines.push(`${indent}id = "${escapeToml(ns.id)}"`);
|
|
423
|
-
if (ns.preview_id) nsLines.push(`${indent}preview_id = "${escapeToml(ns.preview_id)}"`);
|
|
860
|
+
if (ns.binding) nsLines.push(`${indent}binding = "${escapeToml$1(ns.binding)}"`);
|
|
861
|
+
if (ns.id) nsLines.push(`${indent}id = "${escapeToml$1(ns.id)}"`);
|
|
862
|
+
if (ns.preview_id) nsLines.push(`${indent}preview_id = "${escapeToml$1(ns.preview_id)}"`);
|
|
424
863
|
lines.push(nsLines.join("\n"));
|
|
425
864
|
}
|
|
426
865
|
if (config.vars && Object.keys(config.vars).length > 0) {
|
|
427
866
|
lines.push(`${indent}[vars]`);
|
|
428
|
-
for (const [k, v] of Object.entries(config.vars)) lines.push(`${indent}${k} = "${escapeToml(v)}"`);
|
|
867
|
+
for (const [k, v] of Object.entries(config.vars)) lines.push(`${indent}${k} = "${escapeToml$1(v)}"`);
|
|
429
868
|
}
|
|
430
869
|
if (config.d1_databases && config.d1_databases.length > 0) for (const db of config.d1_databases) {
|
|
431
870
|
const dbLines = [`${indent}[[d1_databases]]`];
|
|
432
|
-
if (db.binding) dbLines.push(`${indent}binding = "${escapeToml(db.binding)}"`);
|
|
433
|
-
if (db.database_id) dbLines.push(`${indent}database_id = "${escapeToml(db.database_id)}"`);
|
|
434
|
-
if (db.preview_database_id) dbLines.push(`${indent}preview_database_id = "${escapeToml(db.preview_database_id)}"`);
|
|
871
|
+
if (db.binding) dbLines.push(`${indent}binding = "${escapeToml$1(db.binding)}"`);
|
|
872
|
+
if (db.database_id) dbLines.push(`${indent}database_id = "${escapeToml$1(db.database_id)}"`);
|
|
873
|
+
if (db.preview_database_id) dbLines.push(`${indent}preview_database_id = "${escapeToml$1(db.preview_database_id)}"`);
|
|
435
874
|
lines.push(dbLines.join("\n"));
|
|
436
875
|
}
|
|
437
876
|
if (config.r2_buckets && config.r2_buckets.length > 0) for (const b of config.r2_buckets) {
|
|
438
877
|
const bLines = [`${indent}[[r2_buckets]]`];
|
|
439
|
-
if (b.binding) bLines.push(`${indent}binding = "${escapeToml(b.binding)}"`);
|
|
440
|
-
if (b.bucket_name) bLines.push(`${indent}bucket_name = "${escapeToml(b.bucket_name)}"`);
|
|
441
|
-
if (b.preview_bucket_name) bLines.push(`${indent}preview_bucket_name = "${escapeToml(b.preview_bucket_name)}"`);
|
|
878
|
+
if (b.binding) bLines.push(`${indent}binding = "${escapeToml$1(b.binding)}"`);
|
|
879
|
+
if (b.bucket_name) bLines.push(`${indent}bucket_name = "${escapeToml$1(b.bucket_name)}"`);
|
|
880
|
+
if (b.preview_bucket_name) bLines.push(`${indent}preview_bucket_name = "${escapeToml$1(b.preview_bucket_name)}"`);
|
|
442
881
|
lines.push(bLines.join("\n"));
|
|
443
882
|
}
|
|
444
883
|
if (config.queues) {
|
|
445
884
|
if (config.queues.producers && config.queues.producers.length > 0) for (const p of config.queues.producers) {
|
|
446
885
|
const pLines = [`${indent}[[queues.producers]]`];
|
|
447
|
-
if (p.binding) pLines.push(`${indent}binding = "${escapeToml(p.binding)}"`);
|
|
448
|
-
if (p.queue) pLines.push(`${indent}queue = "${escapeToml(p.queue)}"`);
|
|
886
|
+
if (p.binding) pLines.push(`${indent}binding = "${escapeToml$1(p.binding)}"`);
|
|
887
|
+
if (p.queue) pLines.push(`${indent}queue = "${escapeToml$1(p.queue)}"`);
|
|
449
888
|
lines.push(pLines.join("\n"));
|
|
450
889
|
}
|
|
451
890
|
if (config.queues.consumers && config.queues.consumers.length > 0) for (const c of config.queues.consumers) {
|
|
452
891
|
const cLines = [`${indent}[[queues.consumers]]`];
|
|
453
|
-
if (c.queue) cLines.push(`${indent}queue = "${escapeToml(c.queue)}"`);
|
|
892
|
+
if (c.queue) cLines.push(`${indent}queue = "${escapeToml$1(c.queue)}"`);
|
|
454
893
|
lines.push(cLines.join("\n"));
|
|
455
894
|
}
|
|
456
895
|
}
|
|
457
896
|
return `${lines.join("\n")}\n`;
|
|
458
897
|
}
|
|
898
|
+
function escapeToml$1(str) {
|
|
899
|
+
return str.replace(/\\/g, "\\\\").replace(/"/g, "\\\"");
|
|
900
|
+
}
|
|
901
|
+
/**
|
|
902
|
+
* Deno preset —— Deno 运行时模式
|
|
903
|
+
*
|
|
904
|
+
* 继承 Node preset 的配置,使用 Deno 运行时特性:
|
|
905
|
+
* - 原生 TypeScript 支持(无需编译)
|
|
906
|
+
* - 内置 Deno KV / Deno.cron / Deno.Queue
|
|
907
|
+
* - 原生 WebSocket (Deno.serve)
|
|
908
|
+
* - 安全沙箱(默认无文件系统/网络访问,需显式授权)
|
|
909
|
+
*
|
|
910
|
+
* 构建输出:`dist/deno/server/index.mjs`
|
|
911
|
+
* 预览命令:`deno run --allow-net dist/deno/server/index.mjs`
|
|
912
|
+
* 部署命令:`deno run --allow-net dist/deno/server/index.mjs`
|
|
913
|
+
*/
|
|
914
|
+
const denoPreset = definePreset({
|
|
915
|
+
extends: "node",
|
|
916
|
+
capabilities: createCapabilitySet({
|
|
917
|
+
staticServe: true,
|
|
918
|
+
websocket: true,
|
|
919
|
+
sse: true,
|
|
920
|
+
cronTriggers: true,
|
|
921
|
+
queues: true,
|
|
922
|
+
kv: true,
|
|
923
|
+
storage: true,
|
|
924
|
+
database: true,
|
|
925
|
+
envVars: true,
|
|
926
|
+
secrets: true,
|
|
927
|
+
nodeCompat: true,
|
|
928
|
+
streaming: true,
|
|
929
|
+
compression: true,
|
|
930
|
+
https: true,
|
|
931
|
+
http2: true,
|
|
932
|
+
middleware: true,
|
|
933
|
+
bodyLimit: true,
|
|
934
|
+
multipart: true,
|
|
935
|
+
rpc: false
|
|
936
|
+
}),
|
|
937
|
+
entry: "server",
|
|
938
|
+
exportConditions: ["deno"],
|
|
939
|
+
build: {
|
|
940
|
+
outputDir: "dist/deno",
|
|
941
|
+
format: "esm",
|
|
942
|
+
externals: [
|
|
943
|
+
"hono",
|
|
944
|
+
"c12",
|
|
945
|
+
"citty",
|
|
946
|
+
"tslog",
|
|
947
|
+
"defu",
|
|
948
|
+
"hookable",
|
|
949
|
+
"pathe",
|
|
950
|
+
"ufo",
|
|
951
|
+
"zod",
|
|
952
|
+
"node:*"
|
|
953
|
+
]
|
|
954
|
+
},
|
|
955
|
+
output: {
|
|
956
|
+
dir: "dist/deno",
|
|
957
|
+
serverDir: "dist/deno/server",
|
|
958
|
+
publicDir: "dist/deno/public"
|
|
959
|
+
},
|
|
960
|
+
runtime: {
|
|
961
|
+
entry: "server/index.mjs",
|
|
962
|
+
handler: "handler",
|
|
963
|
+
compatibilityDate: "2024-09-01"
|
|
964
|
+
},
|
|
965
|
+
serve: {
|
|
966
|
+
host: "localhost",
|
|
967
|
+
port: 8e3
|
|
968
|
+
},
|
|
969
|
+
commands: {
|
|
970
|
+
preview: "deno run --allow-net --allow-env --allow-read dist/deno/server/index.mjs",
|
|
971
|
+
deploy: "deno run --allow-net --allow-env --allow-read dist/deno/server/index.mjs"
|
|
972
|
+
},
|
|
973
|
+
hooks: {
|
|
974
|
+
"build:before": async () => {},
|
|
975
|
+
"build:after": async () => {}
|
|
976
|
+
}
|
|
977
|
+
}, {
|
|
978
|
+
name: "deno",
|
|
979
|
+
aliases: ["deno-deploy", "deno-runtime"],
|
|
980
|
+
stdName: "deno",
|
|
981
|
+
dev: true,
|
|
982
|
+
compatibilityDate: "2024-09-01",
|
|
983
|
+
url: "https://docs.deno.com/runtime/manual"
|
|
984
|
+
});
|
|
985
|
+
/**
|
|
986
|
+
* 生成 Deno 配置对象
|
|
987
|
+
*
|
|
988
|
+
* @param options.lock - 传 `false` 禁用 lockfile,传字符串指定 lockfile 路径
|
|
989
|
+
*/
|
|
990
|
+
function generateDenoConfig(options) {
|
|
991
|
+
const config = {};
|
|
992
|
+
if (options.tasks) config.tasks = options.tasks;
|
|
993
|
+
if (options.importMap) config.importMap = options.importMap;
|
|
994
|
+
if (options.lock !== void 0) config.lock = options.lock;
|
|
995
|
+
if (options.nodeModulesDir !== void 0) config.nodeModulesDir = options.nodeModulesDir;
|
|
996
|
+
if (options.unstable && options.unstable.length > 0) config.unstable = options.unstable;
|
|
997
|
+
return config;
|
|
998
|
+
}
|
|
999
|
+
/**
|
|
1000
|
+
* 序列化 Deno 配置为 JSON 字符串
|
|
1001
|
+
*/
|
|
1002
|
+
function serializeDenoConfig(config) {
|
|
1003
|
+
return `${JSON.stringify(config, null, 2)}\n`;
|
|
1004
|
+
}
|
|
1005
|
+
/**
|
|
1006
|
+
* Netlify preset —— Netlify Functions 模式
|
|
1007
|
+
*
|
|
1008
|
+
* 构建输出:`dist/netlify/functions/index.mjs`(Netlify 自动检测 `functions/` 目录)
|
|
1009
|
+
* 预览命令:`netlify dev`
|
|
1010
|
+
* 部署命令:`netlify deploy --prod`
|
|
1011
|
+
*/
|
|
1012
|
+
const netlifyPreset = definePreset({
|
|
1013
|
+
extends: "node",
|
|
1014
|
+
capabilities: createCapabilitySet({
|
|
1015
|
+
staticServe: true,
|
|
1016
|
+
websocket: false,
|
|
1017
|
+
sse: true,
|
|
1018
|
+
cronTriggers: true,
|
|
1019
|
+
queues: false,
|
|
1020
|
+
kv: true,
|
|
1021
|
+
storage: true,
|
|
1022
|
+
database: true,
|
|
1023
|
+
envVars: true,
|
|
1024
|
+
secrets: true,
|
|
1025
|
+
nodeCompat: true,
|
|
1026
|
+
streaming: true,
|
|
1027
|
+
compression: true,
|
|
1028
|
+
https: true,
|
|
1029
|
+
http2: true,
|
|
1030
|
+
middleware: true,
|
|
1031
|
+
bodyLimit: true,
|
|
1032
|
+
multipart: true,
|
|
1033
|
+
rpc: false
|
|
1034
|
+
}),
|
|
1035
|
+
entry: "server",
|
|
1036
|
+
exportConditions: ["netlify"],
|
|
1037
|
+
build: {
|
|
1038
|
+
outputDir: "dist/netlify",
|
|
1039
|
+
format: "esm",
|
|
1040
|
+
externals: [
|
|
1041
|
+
"hono",
|
|
1042
|
+
"c12",
|
|
1043
|
+
"citty",
|
|
1044
|
+
"tslog",
|
|
1045
|
+
"defu",
|
|
1046
|
+
"hookable",
|
|
1047
|
+
"pathe",
|
|
1048
|
+
"ufo",
|
|
1049
|
+
"zod"
|
|
1050
|
+
]
|
|
1051
|
+
},
|
|
1052
|
+
output: {
|
|
1053
|
+
dir: "dist/netlify",
|
|
1054
|
+
serverDir: "dist/netlify/functions",
|
|
1055
|
+
publicDir: "dist/netlify/public"
|
|
1056
|
+
},
|
|
1057
|
+
runtime: {
|
|
1058
|
+
entry: "functions/index.mjs",
|
|
1059
|
+
handler: "handler"
|
|
1060
|
+
},
|
|
1061
|
+
serve: {
|
|
1062
|
+
host: "localhost",
|
|
1063
|
+
port: 8888
|
|
1064
|
+
},
|
|
1065
|
+
commands: {
|
|
1066
|
+
preview: "netlify dev",
|
|
1067
|
+
deploy: "netlify deploy --prod"
|
|
1068
|
+
},
|
|
1069
|
+
hooks: {
|
|
1070
|
+
"build:before": async () => {},
|
|
1071
|
+
"build:after": async () => {}
|
|
1072
|
+
}
|
|
1073
|
+
}, {
|
|
1074
|
+
name: "netlify",
|
|
1075
|
+
aliases: ["netlify-functions", "netlify-node"],
|
|
1076
|
+
stdName: "netlify",
|
|
1077
|
+
dev: true,
|
|
1078
|
+
compatibilityDate: "2024-09-01",
|
|
1079
|
+
url: "https://docs.netlify.com/functions/overview/"
|
|
1080
|
+
});
|
|
1081
|
+
/**
|
|
1082
|
+
* 生成 Netlify 配置对象
|
|
1083
|
+
*/
|
|
1084
|
+
function generateNetlifyConfig(options) {
|
|
1085
|
+
const config = {
|
|
1086
|
+
build: {
|
|
1087
|
+
command: options.buildCommand || "pnpm build",
|
|
1088
|
+
publish: options.publishDir || "dist/netlify/public",
|
|
1089
|
+
functions: options.functionsDir || "dist/netlify/functions"
|
|
1090
|
+
},
|
|
1091
|
+
functions: {
|
|
1092
|
+
directory: options.functionsDir || "dist/netlify/functions",
|
|
1093
|
+
node_bundler: "esbuild"
|
|
1094
|
+
}
|
|
1095
|
+
};
|
|
1096
|
+
if (options.redirects && options.redirects.length > 0) config.redirects = options.redirects;
|
|
1097
|
+
if (options.headers && options.headers.length > 0) config.headers = options.headers;
|
|
1098
|
+
if (options.environment && Object.keys(options.environment).length > 0) config.build.environment = options.environment;
|
|
1099
|
+
return config;
|
|
1100
|
+
}
|
|
1101
|
+
/**
|
|
1102
|
+
* 序列化 Netlify 配置为 TOML 字符串
|
|
1103
|
+
*/
|
|
1104
|
+
function serializeNetlifyConfig(config) {
|
|
1105
|
+
const lines = [];
|
|
1106
|
+
if (config.build) {
|
|
1107
|
+
lines.push("[build]");
|
|
1108
|
+
if (config.build.command) lines.push(`command = "${escapeToml(config.build.command)}"`);
|
|
1109
|
+
if (config.build.publish) lines.push(`publish = "${escapeToml(config.build.publish)}"`);
|
|
1110
|
+
if (config.build.functions) lines.push(`functions = "${escapeToml(config.build.functions)}"`);
|
|
1111
|
+
if (config.build.environment) {
|
|
1112
|
+
lines.push("");
|
|
1113
|
+
lines.push("[build.environment]");
|
|
1114
|
+
for (const [k, v] of Object.entries(config.build.environment)) lines.push(`${k} = "${escapeToml(v)}"`);
|
|
1115
|
+
}
|
|
1116
|
+
lines.push("");
|
|
1117
|
+
}
|
|
1118
|
+
if (config.functions) {
|
|
1119
|
+
lines.push("[functions]");
|
|
1120
|
+
if (config.functions.directory) lines.push(`directory = "${escapeToml(config.functions.directory)}"`);
|
|
1121
|
+
if (config.functions.node_bundler) lines.push(`node_bundler = "${config.functions.node_bundler}"`);
|
|
1122
|
+
if (config.functions.external_node_modules && config.functions.external_node_modules.length > 0) {
|
|
1123
|
+
const mods = config.functions.external_node_modules.map((m) => `"${escapeToml(m)}"`).join(", ");
|
|
1124
|
+
lines.push(`external_node_modules = [${mods}]`);
|
|
1125
|
+
}
|
|
1126
|
+
lines.push("");
|
|
1127
|
+
}
|
|
1128
|
+
if (config.redirects) for (const r of config.redirects) {
|
|
1129
|
+
lines.push("[[redirects]]");
|
|
1130
|
+
lines.push(`from = "${escapeToml(r.from)}"`);
|
|
1131
|
+
lines.push(`to = "${escapeToml(r.to)}"`);
|
|
1132
|
+
if (r.status) lines.push(`status = ${r.status}`);
|
|
1133
|
+
if (r.force) lines.push(`force = ${r.force}`);
|
|
1134
|
+
lines.push("");
|
|
1135
|
+
}
|
|
1136
|
+
if (config.headers) for (const h of config.headers) {
|
|
1137
|
+
lines.push("[[headers]]");
|
|
1138
|
+
lines.push(`for = "${escapeToml(h.for)}"`);
|
|
1139
|
+
lines.push("[headers.values]");
|
|
1140
|
+
for (const [k, v] of Object.entries(h.values)) lines.push(`"${k}" = "${escapeToml(v)}"`);
|
|
1141
|
+
lines.push("");
|
|
1142
|
+
}
|
|
1143
|
+
return `${lines.join("\n")}\n`;
|
|
1144
|
+
}
|
|
459
1145
|
function escapeToml(str) {
|
|
460
1146
|
return str.replace(/\\/g, "\\\\").replace(/"/g, "\\\"");
|
|
461
1147
|
}
|
|
@@ -471,7 +1157,7 @@ const nodePreset = definePreset({
|
|
|
471
1157
|
"hono",
|
|
472
1158
|
"c12",
|
|
473
1159
|
"citty",
|
|
474
|
-
"
|
|
1160
|
+
"tslog",
|
|
475
1161
|
"defu",
|
|
476
1162
|
"hookable",
|
|
477
1163
|
"pathe",
|
|
@@ -512,7 +1198,7 @@ const standardPreset = definePreset({
|
|
|
512
1198
|
"hono",
|
|
513
1199
|
"c12",
|
|
514
1200
|
"citty",
|
|
515
|
-
"
|
|
1201
|
+
"tslog",
|
|
516
1202
|
"defu",
|
|
517
1203
|
"hookable",
|
|
518
1204
|
"pathe",
|
|
@@ -532,12 +1218,225 @@ const standardPreset = definePreset({
|
|
|
532
1218
|
dev: true
|
|
533
1219
|
});
|
|
534
1220
|
//#endregion
|
|
1221
|
+
//#region src/vercel.ts
|
|
1222
|
+
/**
|
|
1223
|
+
* Vercel 能力矩阵
|
|
1224
|
+
*
|
|
1225
|
+
* Vercel 支持两种部署模式:
|
|
1226
|
+
* - **Serverless Functions**(`api/` 目录):Node.js 运行时,有冷启动,
|
|
1227
|
+
* 不支持 WebSocket / cron(无持久连接),支持大多数 Node.js API
|
|
1228
|
+
* - **Edge Functions**(基于 V8 isolates):无 Node.js 兼容,无 fs,
|
|
1229
|
+
* 支持流式响应,全球低延迟
|
|
1230
|
+
*
|
|
1231
|
+
* 默认按 Serverless Functions 配置(更通用);用户可通过 `extends: 'vercel-edge'`
|
|
1232
|
+
* 切换到 Edge 模式。
|
|
1233
|
+
*/
|
|
1234
|
+
const VERCEL_CAPABILITIES = createCapabilitySet({
|
|
1235
|
+
staticServe: true,
|
|
1236
|
+
websocket: false,
|
|
1237
|
+
sse: true,
|
|
1238
|
+
cronTriggers: true,
|
|
1239
|
+
queues: false,
|
|
1240
|
+
kv: true,
|
|
1241
|
+
storage: true,
|
|
1242
|
+
database: true,
|
|
1243
|
+
envVars: true,
|
|
1244
|
+
secrets: true,
|
|
1245
|
+
nodeCompat: true,
|
|
1246
|
+
streaming: true,
|
|
1247
|
+
compression: true,
|
|
1248
|
+
https: true,
|
|
1249
|
+
http2: true,
|
|
1250
|
+
middleware: true,
|
|
1251
|
+
bodyLimit: true,
|
|
1252
|
+
multipart: true,
|
|
1253
|
+
rpc: false
|
|
1254
|
+
});
|
|
1255
|
+
/**
|
|
1256
|
+
* Vercel Edge 能力矩阵
|
|
1257
|
+
*
|
|
1258
|
+
* Edge Functions 基于 V8 isolates,无 Node.js 兼容层,
|
|
1259
|
+
* 但支持流式响应和全球低延迟。
|
|
1260
|
+
*/
|
|
1261
|
+
const VERCEL_EDGE_CAPABILITIES = createCapabilitySet({
|
|
1262
|
+
staticServe: true,
|
|
1263
|
+
websocket: false,
|
|
1264
|
+
sse: true,
|
|
1265
|
+
cronTriggers: false,
|
|
1266
|
+
queues: false,
|
|
1267
|
+
kv: true,
|
|
1268
|
+
storage: true,
|
|
1269
|
+
database: false,
|
|
1270
|
+
envVars: true,
|
|
1271
|
+
secrets: true,
|
|
1272
|
+
nodeCompat: false,
|
|
1273
|
+
streaming: true,
|
|
1274
|
+
compression: false,
|
|
1275
|
+
https: true,
|
|
1276
|
+
http2: true,
|
|
1277
|
+
middleware: true,
|
|
1278
|
+
bodyLimit: true,
|
|
1279
|
+
multipart: false,
|
|
1280
|
+
rpc: false
|
|
1281
|
+
});
|
|
1282
|
+
/**
|
|
1283
|
+
* Vercel preset —— Serverless Functions 模式
|
|
1284
|
+
*
|
|
1285
|
+
* 构建输出:`dist/vercel/server/index.mjs`(供 Vercel 自动检测为 Serverless Function)
|
|
1286
|
+
* 预览命令:`vercel dev`
|
|
1287
|
+
* 部署命令:`vercel --prod`
|
|
1288
|
+
*/
|
|
1289
|
+
const vercelPreset = definePreset({
|
|
1290
|
+
extends: "node",
|
|
1291
|
+
capabilities: VERCEL_CAPABILITIES,
|
|
1292
|
+
entry: "server",
|
|
1293
|
+
exportConditions: ["vercel"],
|
|
1294
|
+
build: {
|
|
1295
|
+
outputDir: "dist/vercel",
|
|
1296
|
+
format: "esm",
|
|
1297
|
+
externals: [
|
|
1298
|
+
"hono",
|
|
1299
|
+
"c12",
|
|
1300
|
+
"citty",
|
|
1301
|
+
"tslog",
|
|
1302
|
+
"defu",
|
|
1303
|
+
"hookable",
|
|
1304
|
+
"pathe",
|
|
1305
|
+
"ufo",
|
|
1306
|
+
"zod"
|
|
1307
|
+
]
|
|
1308
|
+
},
|
|
1309
|
+
output: {
|
|
1310
|
+
dir: "dist/vercel",
|
|
1311
|
+
serverDir: "dist/vercel/server",
|
|
1312
|
+
publicDir: "dist/vercel/public"
|
|
1313
|
+
},
|
|
1314
|
+
runtime: {
|
|
1315
|
+
entry: "server/index.mjs",
|
|
1316
|
+
handler: "handler"
|
|
1317
|
+
},
|
|
1318
|
+
serve: {
|
|
1319
|
+
host: "localhost",
|
|
1320
|
+
port: 3e3
|
|
1321
|
+
},
|
|
1322
|
+
commands: {
|
|
1323
|
+
preview: "vercel dev",
|
|
1324
|
+
deploy: "vercel --prod"
|
|
1325
|
+
},
|
|
1326
|
+
hooks: {
|
|
1327
|
+
"build:before": async () => {},
|
|
1328
|
+
"build:after": async () => {}
|
|
1329
|
+
}
|
|
1330
|
+
}, {
|
|
1331
|
+
name: "vercel",
|
|
1332
|
+
aliases: ["vercel-serverless", "vercel-node"],
|
|
1333
|
+
stdName: "vercel",
|
|
1334
|
+
dev: true,
|
|
1335
|
+
compatibilityDate: "2024-09-01",
|
|
1336
|
+
url: "https://vercel.com/docs/functions/serverless-functions"
|
|
1337
|
+
});
|
|
1338
|
+
/**
|
|
1339
|
+
* Vercel Edge preset —— Edge Functions 模式
|
|
1340
|
+
*
|
|
1341
|
+
* 基于 V8 isolates,无 Node.js 兼容,全球低延迟。
|
|
1342
|
+
* 构建输出:`dist/vercel-edge/edge/index.mjs`
|
|
1343
|
+
*/
|
|
1344
|
+
const vercelEdgePreset = definePreset({
|
|
1345
|
+
extends: "standard",
|
|
1346
|
+
capabilities: VERCEL_EDGE_CAPABILITIES,
|
|
1347
|
+
entry: "worker",
|
|
1348
|
+
exportConditions: ["edge-light", "vercel-edge"],
|
|
1349
|
+
build: {
|
|
1350
|
+
outputDir: "dist/vercel-edge",
|
|
1351
|
+
format: "esm",
|
|
1352
|
+
minify: true,
|
|
1353
|
+
externals: [
|
|
1354
|
+
"hono",
|
|
1355
|
+
"c12",
|
|
1356
|
+
"citty",
|
|
1357
|
+
"tslog",
|
|
1358
|
+
"defu",
|
|
1359
|
+
"hookable",
|
|
1360
|
+
"pathe",
|
|
1361
|
+
"ufo",
|
|
1362
|
+
"zod"
|
|
1363
|
+
],
|
|
1364
|
+
rollupConfig: { external: ["node:*"] }
|
|
1365
|
+
},
|
|
1366
|
+
output: {
|
|
1367
|
+
dir: "dist/vercel-edge",
|
|
1368
|
+
serverDir: "dist/vercel-edge/edge",
|
|
1369
|
+
publicDir: "dist/vercel-edge/public"
|
|
1370
|
+
},
|
|
1371
|
+
runtime: {
|
|
1372
|
+
entry: "edge/index.mjs",
|
|
1373
|
+
handler: "fetch",
|
|
1374
|
+
compatibilityDate: "2024-09-01"
|
|
1375
|
+
},
|
|
1376
|
+
serve: {
|
|
1377
|
+
host: "localhost",
|
|
1378
|
+
port: 3e3
|
|
1379
|
+
},
|
|
1380
|
+
commands: {
|
|
1381
|
+
preview: "vercel dev",
|
|
1382
|
+
deploy: "vercel --prod"
|
|
1383
|
+
}
|
|
1384
|
+
}, {
|
|
1385
|
+
name: "vercel-edge",
|
|
1386
|
+
aliases: ["vercel-edge-function"],
|
|
1387
|
+
stdName: "vercel_edge",
|
|
1388
|
+
dev: true,
|
|
1389
|
+
compatibilityDate: "2024-09-01",
|
|
1390
|
+
url: "https://vercel.com/docs/functions/edge-functions"
|
|
1391
|
+
});
|
|
1392
|
+
/**
|
|
1393
|
+
* 生成 Vercel 配置对象
|
|
1394
|
+
*/
|
|
1395
|
+
function generateVercelConfig(options) {
|
|
1396
|
+
const entry = options.entry || "dist/vercel/server/index.mjs";
|
|
1397
|
+
const config = {
|
|
1398
|
+
version: 2,
|
|
1399
|
+
functions: {}
|
|
1400
|
+
};
|
|
1401
|
+
config.functions[entry] = {
|
|
1402
|
+
memory: 1024,
|
|
1403
|
+
maxDuration: 10
|
|
1404
|
+
};
|
|
1405
|
+
if (options.functions) for (const [path, opts] of Object.entries(options.functions)) config.functions[path] = {
|
|
1406
|
+
memory: opts.memory ?? 1024,
|
|
1407
|
+
maxDuration: opts.maxDuration ?? 10
|
|
1408
|
+
};
|
|
1409
|
+
if (options.rewrites && options.rewrites.length > 0) config.rewrites = options.rewrites;
|
|
1410
|
+
if (options.redirects && options.redirects.length > 0) config.redirects = options.redirects;
|
|
1411
|
+
if (options.headers && options.headers.length > 0) config.headers = options.headers.map((h) => ({
|
|
1412
|
+
source: h.source,
|
|
1413
|
+
headers: Object.entries(h.headers).map(([key, value]) => ({
|
|
1414
|
+
key,
|
|
1415
|
+
value
|
|
1416
|
+
}))
|
|
1417
|
+
}));
|
|
1418
|
+
if (options.cron && options.cron.length > 0) config.cron = options.cron;
|
|
1419
|
+
if (options.env && Object.keys(options.env).length > 0) config.env = options.env;
|
|
1420
|
+
return config;
|
|
1421
|
+
}
|
|
1422
|
+
/**
|
|
1423
|
+
* 序列化 Vercel 配置为 JSON 字符串
|
|
1424
|
+
*/
|
|
1425
|
+
function serializeVercelConfig(config) {
|
|
1426
|
+
return `${JSON.stringify(config, null, 2)}\n`;
|
|
1427
|
+
}
|
|
1428
|
+
//#endregion
|
|
535
1429
|
//#region src/detect.ts
|
|
536
1430
|
function getPresetDef(preset) {
|
|
537
1431
|
return typeof preset === "function" ? preset() : preset;
|
|
538
1432
|
}
|
|
539
1433
|
function detectByConfigFiles(cwd) {
|
|
540
1434
|
if (existsSync(join(cwd, "wrangler.toml")) || existsSync(join(cwd, "wrangler.json"))) return "cloudflare";
|
|
1435
|
+
if (existsSync(join(cwd, "vercel.json"))) return "vercel";
|
|
1436
|
+
if (existsSync(join(cwd, "netlify.toml"))) return "netlify";
|
|
1437
|
+
if (existsSync(join(cwd, "deno.json")) || existsSync(join(cwd, "deno.jsonc"))) return "deno";
|
|
1438
|
+
if (existsSync(join(cwd, "template.yaml")) || existsSync(join(cwd, "samconfig.toml"))) return "aws";
|
|
1439
|
+
if (existsSync(join(cwd, "staticwebapp.config.json"))) return "azure";
|
|
541
1440
|
if (existsSync(join(cwd, "package.json"))) try {
|
|
542
1441
|
const pkg = JSON.parse(readFileSync(join(cwd, "package.json"), "utf-8"));
|
|
543
1442
|
const deps = {
|
|
@@ -545,14 +1444,27 @@ function detectByConfigFiles(cwd) {
|
|
|
545
1444
|
...pkg.devDependencies
|
|
546
1445
|
};
|
|
547
1446
|
if ("wrangler" in deps || "@cloudflare/workers-types" in deps) return "cloudflare";
|
|
1447
|
+
if ("vercel" in deps || "@vercel/node" in deps || "@vercel/edge" in deps) return "vercel";
|
|
1448
|
+
if ("netlify-cli" in deps || "netlify-lambda" in deps) return "netlify";
|
|
1449
|
+
if ("aws-sam-cli" in deps || "aws-cdk" in deps || "@aws-sdk/client-lambda" in deps) return "aws";
|
|
1450
|
+
if ("@azure/static-web-apps-cli" in deps || "@azure/functions" in deps) return "azure";
|
|
548
1451
|
} catch {}
|
|
549
1452
|
return null;
|
|
550
1453
|
}
|
|
551
1454
|
function detectByEnvironment(env, g) {
|
|
552
|
-
if (env.CF_WORKERS || env.WRANGLER || env.CLOUDFLARE_WORKER) return "cloudflare";
|
|
553
|
-
if (env.NODE_ENV === void 0 && "process" in g) return "node";
|
|
554
1455
|
const gRecord = g;
|
|
1456
|
+
if (env.CF_WORKERS || env.WRANGLER || env.CLOUDFLARE_WORKER) return "cloudflare";
|
|
1457
|
+
if (env.VERCEL || env.VERCEL_ENV || env.NOW_ID) return "vercel";
|
|
1458
|
+
if (env.NETLIFY || env.NETLIFY_DEV || env.NETLIFY_IMAGES_CDN_DOMAIN !== void 0) return "netlify";
|
|
1459
|
+
if (env.AWS_LAMBDA_FUNCTION_NAME || env.AWS_EXECUTION_ENV || env.AWS_LAMBDA_RUNTIME_API) return "aws";
|
|
1460
|
+
if (env.AZURE_FUNCTIONS_ENVIRONMENT || env.WEBSITE_SITE_NAME || env.AZURE_HTTP_FUNCTION) return "azure";
|
|
1461
|
+
if (gRecord.Deno !== void 0) return "deno";
|
|
1462
|
+
if (gRecord.Bun !== void 0) return "bun";
|
|
1463
|
+
if (gRecord.process !== void 0) {
|
|
1464
|
+
if (gRecord.process.versions?.bun) return "bun";
|
|
1465
|
+
}
|
|
555
1466
|
if (gRecord.CacheStorage !== void 0 && gRecord.Deno === void 0 && gRecord.process === void 0) return "cloudflare";
|
|
1467
|
+
if (env.NODE_ENV === void 0 && "process" in g) return "node";
|
|
556
1468
|
if (gRecord.process !== void 0) {
|
|
557
1469
|
if (gRecord.process.versions?.node) return "node";
|
|
558
1470
|
}
|
|
@@ -579,7 +1491,14 @@ function detectPreset(hints = {}) {
|
|
|
579
1491
|
if (preset) return {
|
|
580
1492
|
preset,
|
|
581
1493
|
source: "config-file",
|
|
582
|
-
reason:
|
|
1494
|
+
reason: {
|
|
1495
|
+
cloudflare: "detected wrangler.toml or Cloudflare dependencies",
|
|
1496
|
+
vercel: "detected vercel.json or Vercel dependencies",
|
|
1497
|
+
netlify: "detected netlify.toml or Netlify dependencies",
|
|
1498
|
+
deno: "detected deno.json",
|
|
1499
|
+
aws: "detected template.yaml (AWS SAM) or AWS dependencies",
|
|
1500
|
+
azure: "detected staticwebapp.config.json or Azure dependencies"
|
|
1501
|
+
}[configDetected] || `detected ${configDetected} configuration`
|
|
583
1502
|
};
|
|
584
1503
|
}
|
|
585
1504
|
const envDetected = detectByEnvironment(env, g);
|
|
@@ -608,7 +1527,14 @@ function listDetectablePresets() {
|
|
|
608
1527
|
getPresetDef(standardPreset),
|
|
609
1528
|
getPresetDef(nodePreset),
|
|
610
1529
|
getPresetDef(cloudflarePreset),
|
|
611
|
-
getPresetDef(cloudflareDevPreset)
|
|
1530
|
+
getPresetDef(cloudflareDevPreset),
|
|
1531
|
+
getPresetDef(vercelPreset),
|
|
1532
|
+
getPresetDef(vercelEdgePreset),
|
|
1533
|
+
getPresetDef(netlifyPreset),
|
|
1534
|
+
getPresetDef(bunPreset),
|
|
1535
|
+
getPresetDef(denoPreset),
|
|
1536
|
+
getPresetDef(awsPreset),
|
|
1537
|
+
getPresetDef(azurePreset)
|
|
612
1538
|
];
|
|
613
1539
|
}
|
|
614
1540
|
//#endregion
|
|
@@ -617,7 +1543,14 @@ const builtinPresets = [
|
|
|
617
1543
|
standardPreset,
|
|
618
1544
|
nodePreset,
|
|
619
1545
|
cloudflarePreset,
|
|
620
|
-
cloudflareDevPreset
|
|
1546
|
+
cloudflareDevPreset,
|
|
1547
|
+
vercelPreset,
|
|
1548
|
+
vercelEdgePreset,
|
|
1549
|
+
netlifyPreset,
|
|
1550
|
+
bunPreset,
|
|
1551
|
+
denoPreset,
|
|
1552
|
+
awsPreset,
|
|
1553
|
+
azurePreset
|
|
621
1554
|
];
|
|
622
1555
|
function registerBuiltinPresets() {
|
|
623
1556
|
for (const preset of builtinPresets) registerPreset(preset);
|
|
@@ -647,4 +1580,4 @@ function resolvePresetByName(name) {
|
|
|
647
1580
|
};
|
|
648
1581
|
}
|
|
649
1582
|
//#endregion
|
|
650
|
-
export { DEV_REQUIREMENTS, NODE_CAPABILITIES, NODE_REQUIREMENTS, STANDARD_CAPABILITIES, WORKER_CAPABILITIES, cloudflareDevPreset, cloudflarePreset, createCapabilitySet, definePreset, detectPreset, diagnoseCapabilities, generateWranglerConfig, getPresetAliases, getPresetNames, getRegisteredPresets, listDetectablePresets, nodePreset, registerBuiltinPresets, registerPreset, requireCapability, resolvePreset, resolvePresetByName, resolvePresetWithDetection, serializeWranglerToml, standardPreset };
|
|
1583
|
+
export { DEV_REQUIREMENTS, NODE_CAPABILITIES, NODE_REQUIREMENTS, STANDARD_CAPABILITIES, WORKER_CAPABILITIES, awsPreset, azurePreset, bunPreset, cloudflareDevPreset, cloudflarePreset, createCapabilitySet, definePreset, denoPreset, detectPreset, diagnoseCapabilities, generateAwsSamConfig, generateBunfigConfig, generateDenoConfig, generateNetlifyConfig, generateStaticWebAppConfig, generateVercelConfig, generateWranglerConfig, getPresetAliases, getPresetNames, getRegisteredPresets, listDetectablePresets, netlifyPreset, nodePreset, registerBuiltinPresets, registerPreset, requireCapability, resolvePreset, resolvePresetByName, resolvePresetWithDetection, serializeAwsSamConfig, serializeBunfigConfig, serializeDenoConfig, serializeNetlifyConfig, serializeStaticWebAppConfig, serializeVercelConfig, serializeWranglerToml, standardPreset, vercelEdgePreset, vercelPreset };
|