@korajs/cli 0.3.0 → 0.3.2
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/bin.cjs +875 -121
- package/dist/bin.cjs.map +1 -1
- package/dist/bin.js +1 -1
- package/dist/{chunk-E4JG7THU.js → chunk-E7OCVRYL.js} +812 -60
- package/dist/chunk-E7OCVRYL.js.map +1 -0
- package/dist/index.cjs +839 -85
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/package.json +2 -2
- package/dist/chunk-E4JG7THU.js.map +0 -1
package/dist/bin.cjs
CHANGED
|
@@ -1295,62 +1295,24 @@ function resolveKoraVersion() {
|
|
|
1295
1295
|
}
|
|
1296
1296
|
|
|
1297
1297
|
// src/commands/deploy/deploy-command.ts
|
|
1298
|
-
var
|
|
1298
|
+
var import_node_path16 = require("path");
|
|
1299
1299
|
var import_citty2 = require("citty");
|
|
1300
1300
|
|
|
1301
1301
|
// src/commands/deploy/adapters/adapter.ts
|
|
1302
|
-
var DEPLOY_PLATFORMS = ["fly", "railway", "render", "docker", "kora-cloud"];
|
|
1302
|
+
var DEPLOY_PLATFORMS = ["fly", "railway", "aws-ecs", "aws-lightsail", "render", "docker", "kora-cloud"];
|
|
1303
1303
|
function isDeployPlatform(value) {
|
|
1304
1304
|
return DEPLOY_PLATFORMS.includes(value);
|
|
1305
1305
|
}
|
|
1306
1306
|
|
|
1307
|
-
// src/commands/deploy/adapters/
|
|
1307
|
+
// src/commands/deploy/adapters/aws-ecs-adapter.ts
|
|
1308
1308
|
var import_node_child_process4 = require("child_process");
|
|
1309
|
-
var
|
|
1310
|
-
|
|
1311
|
-
// src/commands/deploy/artifacts/fly-toml-generator.ts
|
|
1312
|
-
var import_promises5 = require("fs/promises");
|
|
1313
|
-
var import_node_path6 = require("path");
|
|
1314
|
-
var GENERATED_HEADER = "# Generated by kora deploy \u2014 do not edit manually";
|
|
1315
|
-
function generateFlyToml(options) {
|
|
1316
|
-
const internalPort = options.internalPort ?? 3e3;
|
|
1317
|
-
return [
|
|
1318
|
-
GENERATED_HEADER,
|
|
1319
|
-
"",
|
|
1320
|
-
`app = "${options.appName}"`,
|
|
1321
|
-
`primary_region = "${options.region}"`,
|
|
1322
|
-
"",
|
|
1323
|
-
"[build]",
|
|
1324
|
-
` dockerfile = "Dockerfile"`,
|
|
1325
|
-
"",
|
|
1326
|
-
"[http_service]",
|
|
1327
|
-
` internal_port = ${String(internalPort)}`,
|
|
1328
|
-
" force_https = true",
|
|
1329
|
-
' auto_stop_machines = "stop"',
|
|
1330
|
-
" auto_start_machines = true",
|
|
1331
|
-
" min_machines_running = 0",
|
|
1332
|
-
"",
|
|
1333
|
-
" [[http_service.checks]]",
|
|
1334
|
-
' grace_period = "10s"',
|
|
1335
|
-
' interval = "30s"',
|
|
1336
|
-
' method = "GET"',
|
|
1337
|
-
' timeout = "5s"',
|
|
1338
|
-
' path = "/"',
|
|
1339
|
-
""
|
|
1340
|
-
].join("\n");
|
|
1341
|
-
}
|
|
1342
|
-
async function writeFlyTomlArtifact(deployDirectory, options) {
|
|
1343
|
-
await (0, import_promises5.mkdir)(deployDirectory, { recursive: true });
|
|
1344
|
-
const filePath = (0, import_node_path6.join)(deployDirectory, "fly.toml");
|
|
1345
|
-
await (0, import_promises5.writeFile)(filePath, generateFlyToml(options), "utf-8");
|
|
1346
|
-
return filePath;
|
|
1347
|
-
}
|
|
1309
|
+
var import_node_path8 = require("path");
|
|
1348
1310
|
|
|
1349
1311
|
// src/commands/deploy/builder/client-builder.ts
|
|
1350
1312
|
var import_node_child_process3 = require("child_process");
|
|
1351
|
-
var
|
|
1313
|
+
var import_promises5 = require("fs/promises");
|
|
1352
1314
|
var import_node_fs3 = require("fs");
|
|
1353
|
-
var
|
|
1315
|
+
var import_node_path6 = require("path");
|
|
1354
1316
|
async function buildClient(options) {
|
|
1355
1317
|
const viteEntryPoint = await resolveProjectBinaryEntryPoint(options.projectRoot, "vite", "vite");
|
|
1356
1318
|
if (!viteEntryPoint) {
|
|
@@ -1371,15 +1333,15 @@ async function buildClient(options) {
|
|
|
1371
1333
|
return { outDir: options.outDir };
|
|
1372
1334
|
}
|
|
1373
1335
|
async function patchSqliteWasmAssets(projectRoot, outDir) {
|
|
1374
|
-
const assetsDir = (0,
|
|
1336
|
+
const assetsDir = (0, import_node_path6.join)(outDir, "assets");
|
|
1375
1337
|
if (!(0, import_node_fs3.existsSync)(assetsDir)) return;
|
|
1376
|
-
const files = await (0,
|
|
1338
|
+
const files = await (0, import_promises5.readdir)(assetsDir);
|
|
1377
1339
|
const hashedWasm = files.find((f) => /^sqlite3-.+\.wasm$/.test(f));
|
|
1378
1340
|
if (hashedWasm && !files.includes("sqlite3.wasm")) {
|
|
1379
|
-
await (0,
|
|
1341
|
+
await (0, import_promises5.copyFile)((0, import_node_path6.join)(assetsDir, hashedWasm), (0, import_node_path6.join)(assetsDir, "sqlite3.wasm"));
|
|
1380
1342
|
}
|
|
1381
1343
|
if (!files.includes("sqlite3-opfs-async-proxy.js")) {
|
|
1382
|
-
const proxyFile = (0,
|
|
1344
|
+
const proxyFile = (0, import_node_path6.resolve)(
|
|
1383
1345
|
projectRoot,
|
|
1384
1346
|
"node_modules",
|
|
1385
1347
|
"@sqlite.org",
|
|
@@ -1389,7 +1351,7 @@ async function patchSqliteWasmAssets(projectRoot, outDir) {
|
|
|
1389
1351
|
"sqlite3-opfs-async-proxy.js"
|
|
1390
1352
|
);
|
|
1391
1353
|
if ((0, import_node_fs3.existsSync)(proxyFile)) {
|
|
1392
|
-
await (0,
|
|
1354
|
+
await (0, import_promises5.copyFile)(proxyFile, (0, import_node_path6.join)(assetsDir, "sqlite3-opfs-async-proxy.js"));
|
|
1393
1355
|
}
|
|
1394
1356
|
}
|
|
1395
1357
|
}
|
|
@@ -1414,9 +1376,9 @@ async function runProcess(command, args, cwd) {
|
|
|
1414
1376
|
}
|
|
1415
1377
|
|
|
1416
1378
|
// src/commands/deploy/builder/server-bundler.ts
|
|
1379
|
+
var import_promises6 = require("fs/promises");
|
|
1417
1380
|
var import_promises7 = require("fs/promises");
|
|
1418
|
-
var
|
|
1419
|
-
var import_node_path8 = require("path");
|
|
1381
|
+
var import_node_path7 = require("path");
|
|
1420
1382
|
var import_esbuild = require("esbuild");
|
|
1421
1383
|
var DEFAULT_ENTRY_CANDIDATES = [
|
|
1422
1384
|
"server.ts",
|
|
@@ -1432,8 +1394,8 @@ async function bundleServer(options) {
|
|
|
1432
1394
|
`Could not find a server entry file in ${options.projectRoot}. Looked for: ${candidates.join(", ")}`
|
|
1433
1395
|
);
|
|
1434
1396
|
}
|
|
1435
|
-
await (0,
|
|
1436
|
-
const outputFilePath = (0,
|
|
1397
|
+
await (0, import_promises6.mkdir)(options.deployDirectory, { recursive: true });
|
|
1398
|
+
const outputFilePath = (0, import_node_path7.join)(options.deployDirectory, "server-bundled.js");
|
|
1437
1399
|
await (0, import_esbuild.build)({
|
|
1438
1400
|
entryPoints: [entryFilePath],
|
|
1439
1401
|
outfile: outputFilePath,
|
|
@@ -1455,9 +1417,9 @@ async function bundleServer(options) {
|
|
|
1455
1417
|
}
|
|
1456
1418
|
async function resolveServerEntry(projectRoot, candidates) {
|
|
1457
1419
|
for (const candidate of candidates) {
|
|
1458
|
-
const fullPath = (0,
|
|
1420
|
+
const fullPath = (0, import_node_path7.join)(projectRoot, candidate);
|
|
1459
1421
|
try {
|
|
1460
|
-
await (0,
|
|
1422
|
+
await (0, import_promises7.access)(fullPath);
|
|
1461
1423
|
return fullPath;
|
|
1462
1424
|
} catch {
|
|
1463
1425
|
}
|
|
@@ -1465,6 +1427,785 @@ async function resolveServerEntry(projectRoot, candidates) {
|
|
|
1465
1427
|
return null;
|
|
1466
1428
|
}
|
|
1467
1429
|
|
|
1430
|
+
// src/commands/deploy/adapters/aws-ecs-adapter.ts
|
|
1431
|
+
var AwsEcsAdapter = class {
|
|
1432
|
+
name = "aws-ecs";
|
|
1433
|
+
logger = createLogger();
|
|
1434
|
+
runner;
|
|
1435
|
+
currentContext;
|
|
1436
|
+
constructor(options = {}) {
|
|
1437
|
+
this.runner = options.runner ?? new NodeAwsCommandRunner();
|
|
1438
|
+
this.currentContext = options.context ?? null;
|
|
1439
|
+
}
|
|
1440
|
+
setContext(context) {
|
|
1441
|
+
this.currentContext = context;
|
|
1442
|
+
}
|
|
1443
|
+
async detect() {
|
|
1444
|
+
const result = await this.runner.run("aws", ["--version"], process.cwd());
|
|
1445
|
+
return result.exitCode === 0;
|
|
1446
|
+
}
|
|
1447
|
+
async install() {
|
|
1448
|
+
const available = await this.detect();
|
|
1449
|
+
if (!available) {
|
|
1450
|
+
throw new Error(
|
|
1451
|
+
"AWS CLI is required but not installed. Install from https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html"
|
|
1452
|
+
);
|
|
1453
|
+
}
|
|
1454
|
+
}
|
|
1455
|
+
async authenticate() {
|
|
1456
|
+
const cwd = this.currentContext?.projectRoot ?? process.cwd();
|
|
1457
|
+
const result = await this.runner.run("aws", ["sts", "get-caller-identity"], cwd);
|
|
1458
|
+
if (result.exitCode !== 0) {
|
|
1459
|
+
throw new Error(
|
|
1460
|
+
"AWS CLI is not authenticated. Run `aws configure` or set AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY environment variables."
|
|
1461
|
+
);
|
|
1462
|
+
}
|
|
1463
|
+
}
|
|
1464
|
+
async provision(config) {
|
|
1465
|
+
this.currentContext = {
|
|
1466
|
+
projectRoot: config.projectRoot,
|
|
1467
|
+
appName: config.appName,
|
|
1468
|
+
region: config.region ?? "us-east-1"
|
|
1469
|
+
};
|
|
1470
|
+
const region = config.region ?? "us-east-1";
|
|
1471
|
+
const repoName = `kora/${config.appName}`;
|
|
1472
|
+
const createRepo = await this.runner.run("aws", [
|
|
1473
|
+
"ecr",
|
|
1474
|
+
"create-repository",
|
|
1475
|
+
"--repository-name",
|
|
1476
|
+
repoName,
|
|
1477
|
+
"--region",
|
|
1478
|
+
region,
|
|
1479
|
+
"--image-scanning-configuration",
|
|
1480
|
+
"scanOnPush=true"
|
|
1481
|
+
], config.projectRoot);
|
|
1482
|
+
if (createRepo.exitCode !== 0 && !createRepo.stderr.includes("RepositoryAlreadyExistsException")) {
|
|
1483
|
+
throw new Error(`Failed to create ECR repository: ${createRepo.stderr}`);
|
|
1484
|
+
}
|
|
1485
|
+
const identity = await this.runner.run("aws", ["sts", "get-caller-identity", "--query", "Account", "--output", "text"], config.projectRoot);
|
|
1486
|
+
const accountId = identity.stdout.trim();
|
|
1487
|
+
await this.runner.run("aws", [
|
|
1488
|
+
"ecs",
|
|
1489
|
+
"create-cluster",
|
|
1490
|
+
"--cluster-name",
|
|
1491
|
+
config.appName,
|
|
1492
|
+
"--region",
|
|
1493
|
+
region
|
|
1494
|
+
], config.projectRoot);
|
|
1495
|
+
await this.runner.run("aws", [
|
|
1496
|
+
"logs",
|
|
1497
|
+
"create-log-group",
|
|
1498
|
+
"--log-group-name",
|
|
1499
|
+
`/ecs/${config.appName}`,
|
|
1500
|
+
"--region",
|
|
1501
|
+
region
|
|
1502
|
+
], config.projectRoot);
|
|
1503
|
+
return {
|
|
1504
|
+
applicationId: `${accountId}.dkr.ecr.${region}.amazonaws.com/${repoName}`,
|
|
1505
|
+
databaseId: null,
|
|
1506
|
+
secretsSet: ["PORT"]
|
|
1507
|
+
};
|
|
1508
|
+
}
|
|
1509
|
+
async build(config) {
|
|
1510
|
+
this.currentContext = {
|
|
1511
|
+
projectRoot: config.projectRoot,
|
|
1512
|
+
appName: config.appName,
|
|
1513
|
+
region: config.region
|
|
1514
|
+
};
|
|
1515
|
+
const deployDirectory = (0, import_node_path8.join)(config.projectRoot, ".kora", "deploy");
|
|
1516
|
+
await bundleServer({
|
|
1517
|
+
projectRoot: config.projectRoot,
|
|
1518
|
+
deployDirectory
|
|
1519
|
+
});
|
|
1520
|
+
const client = await buildClient({
|
|
1521
|
+
projectRoot: config.projectRoot,
|
|
1522
|
+
outDir: (0, import_node_path8.join)(deployDirectory, "dist"),
|
|
1523
|
+
mode: "production"
|
|
1524
|
+
});
|
|
1525
|
+
return {
|
|
1526
|
+
clientDirectory: client.outDir,
|
|
1527
|
+
serverBundlePath: (0, import_node_path8.join)(deployDirectory, "server-bundled.js"),
|
|
1528
|
+
deployDirectory
|
|
1529
|
+
};
|
|
1530
|
+
}
|
|
1531
|
+
async deploy(artifacts) {
|
|
1532
|
+
const context = this.requireContext();
|
|
1533
|
+
const region = context.region ?? "us-east-1";
|
|
1534
|
+
const repoName = `kora/${context.appName}`;
|
|
1535
|
+
const loginPassword = await this.runner.run("aws", [
|
|
1536
|
+
"ecr",
|
|
1537
|
+
"get-login-password",
|
|
1538
|
+
"--region",
|
|
1539
|
+
region
|
|
1540
|
+
], context.projectRoot);
|
|
1541
|
+
if (loginPassword.exitCode !== 0) {
|
|
1542
|
+
throw new Error(`ECR login failed: ${loginPassword.stderr}`);
|
|
1543
|
+
}
|
|
1544
|
+
const identity = await this.runner.run("aws", [
|
|
1545
|
+
"sts",
|
|
1546
|
+
"get-caller-identity",
|
|
1547
|
+
"--query",
|
|
1548
|
+
"Account",
|
|
1549
|
+
"--output",
|
|
1550
|
+
"text"
|
|
1551
|
+
], context.projectRoot);
|
|
1552
|
+
const accountId = identity.stdout.trim();
|
|
1553
|
+
const ecrUri = `${accountId}.dkr.ecr.${region}.amazonaws.com`;
|
|
1554
|
+
const imageUri = `${ecrUri}/${repoName}:latest`;
|
|
1555
|
+
const dockerLogin = await this.runner.run("docker", [
|
|
1556
|
+
"login",
|
|
1557
|
+
"--username",
|
|
1558
|
+
"AWS",
|
|
1559
|
+
"--password-stdin",
|
|
1560
|
+
ecrUri
|
|
1561
|
+
], artifacts.deployDirectory);
|
|
1562
|
+
this.logger.step("Building Docker image...");
|
|
1563
|
+
const dockerBuild = await this.runner.run("docker", [
|
|
1564
|
+
"build",
|
|
1565
|
+
"--platform",
|
|
1566
|
+
"linux/amd64",
|
|
1567
|
+
"-t",
|
|
1568
|
+
imageUri,
|
|
1569
|
+
"."
|
|
1570
|
+
], artifacts.deployDirectory);
|
|
1571
|
+
if (dockerBuild.exitCode !== 0) {
|
|
1572
|
+
throw new Error(`Docker build failed: ${dockerBuild.stderr}`);
|
|
1573
|
+
}
|
|
1574
|
+
this.logger.step("Pushing image to ECR...");
|
|
1575
|
+
const dockerPush = await this.runner.run("docker", [
|
|
1576
|
+
"push",
|
|
1577
|
+
imageUri
|
|
1578
|
+
], artifacts.deployDirectory);
|
|
1579
|
+
if (dockerPush.exitCode !== 0) {
|
|
1580
|
+
throw new Error(`Docker push failed: ${dockerPush.stderr}`);
|
|
1581
|
+
}
|
|
1582
|
+
const taskDef = JSON.stringify({
|
|
1583
|
+
family: context.appName,
|
|
1584
|
+
networkMode: "awsvpc",
|
|
1585
|
+
requiresCompatibilities: ["FARGATE"],
|
|
1586
|
+
cpu: "256",
|
|
1587
|
+
memory: "512",
|
|
1588
|
+
executionRoleArn: `arn:aws:iam::${accountId}:role/ecsTaskExecutionRole`,
|
|
1589
|
+
containerDefinitions: [{
|
|
1590
|
+
name: context.appName,
|
|
1591
|
+
image: imageUri,
|
|
1592
|
+
essential: true,
|
|
1593
|
+
portMappings: [{ containerPort: 3001, protocol: "tcp" }],
|
|
1594
|
+
logConfiguration: {
|
|
1595
|
+
logDriver: "awslogs",
|
|
1596
|
+
options: {
|
|
1597
|
+
"awslogs-group": `/ecs/${context.appName}`,
|
|
1598
|
+
"awslogs-region": region,
|
|
1599
|
+
"awslogs-stream-prefix": "ecs"
|
|
1600
|
+
}
|
|
1601
|
+
},
|
|
1602
|
+
healthCheck: {
|
|
1603
|
+
command: ["CMD-SHELL", "curl -f http://localhost:3001/health || exit 1"],
|
|
1604
|
+
interval: 30,
|
|
1605
|
+
timeout: 5,
|
|
1606
|
+
retries: 3,
|
|
1607
|
+
startPeriod: 60
|
|
1608
|
+
}
|
|
1609
|
+
}]
|
|
1610
|
+
});
|
|
1611
|
+
const registerTask = await this.runner.run("aws", [
|
|
1612
|
+
"ecs",
|
|
1613
|
+
"register-task-definition",
|
|
1614
|
+
"--cli-input-json",
|
|
1615
|
+
taskDef,
|
|
1616
|
+
"--region",
|
|
1617
|
+
region
|
|
1618
|
+
], context.projectRoot);
|
|
1619
|
+
if (registerTask.exitCode !== 0) {
|
|
1620
|
+
throw new Error(`Task definition registration failed: ${registerTask.stderr}`);
|
|
1621
|
+
}
|
|
1622
|
+
const updateService = await this.runner.run("aws", [
|
|
1623
|
+
"ecs",
|
|
1624
|
+
"update-service",
|
|
1625
|
+
"--cluster",
|
|
1626
|
+
context.appName,
|
|
1627
|
+
"--service",
|
|
1628
|
+
context.appName,
|
|
1629
|
+
"--task-definition",
|
|
1630
|
+
context.appName,
|
|
1631
|
+
"--force-new-deployment",
|
|
1632
|
+
"--region",
|
|
1633
|
+
region
|
|
1634
|
+
], context.projectRoot);
|
|
1635
|
+
const deploymentId = (/* @__PURE__ */ new Date()).toISOString();
|
|
1636
|
+
if (updateService.exitCode !== 0) {
|
|
1637
|
+
this.logger.step("Service not found, creating new service...");
|
|
1638
|
+
this.logger.step(
|
|
1639
|
+
`Task definition registered. Create the ECS service with:
|
|
1640
|
+
aws ecs create-service \\
|
|
1641
|
+
--cluster ${context.appName} \\
|
|
1642
|
+
--service-name ${context.appName} \\
|
|
1643
|
+
--task-definition ${context.appName} \\
|
|
1644
|
+
--desired-count 1 \\
|
|
1645
|
+
--launch-type FARGATE \\
|
|
1646
|
+
--network-configuration "awsvpcConfiguration={subnets=[<subnet-id>],securityGroups=[<sg-id>],assignPublicIp=ENABLED}" \\
|
|
1647
|
+
--region ${region}`
|
|
1648
|
+
);
|
|
1649
|
+
}
|
|
1650
|
+
return {
|
|
1651
|
+
deploymentId,
|
|
1652
|
+
liveUrl: `https://${context.appName}.${region}.amazonaws.com`,
|
|
1653
|
+
syncUrl: `wss://${context.appName}.${region}.amazonaws.com/kora-sync`
|
|
1654
|
+
};
|
|
1655
|
+
}
|
|
1656
|
+
async rollback(deploymentId) {
|
|
1657
|
+
const context = this.requireContext();
|
|
1658
|
+
const region = context.region ?? "us-east-1";
|
|
1659
|
+
const result = await this.runner.run("aws", [
|
|
1660
|
+
"ecs",
|
|
1661
|
+
"update-service",
|
|
1662
|
+
"--cluster",
|
|
1663
|
+
context.appName,
|
|
1664
|
+
"--service",
|
|
1665
|
+
context.appName,
|
|
1666
|
+
"--task-definition",
|
|
1667
|
+
`${context.appName}:${deploymentId}`,
|
|
1668
|
+
"--force-new-deployment",
|
|
1669
|
+
"--region",
|
|
1670
|
+
region
|
|
1671
|
+
], context.projectRoot);
|
|
1672
|
+
if (result.exitCode !== 0) {
|
|
1673
|
+
throw new Error(`ECS rollback failed: ${result.stderr}`);
|
|
1674
|
+
}
|
|
1675
|
+
}
|
|
1676
|
+
async *logs(options) {
|
|
1677
|
+
const context = this.requireContext();
|
|
1678
|
+
const region = context.region ?? "us-east-1";
|
|
1679
|
+
const args = [
|
|
1680
|
+
"logs",
|
|
1681
|
+
"get-log-events",
|
|
1682
|
+
"--log-group-name",
|
|
1683
|
+
`/ecs/${context.appName}`,
|
|
1684
|
+
"--log-stream-name",
|
|
1685
|
+
"ecs/latest",
|
|
1686
|
+
"--region",
|
|
1687
|
+
region
|
|
1688
|
+
];
|
|
1689
|
+
if (options.tail) {
|
|
1690
|
+
args.push("--limit", String(options.tail));
|
|
1691
|
+
}
|
|
1692
|
+
const result = await this.runner.run("aws", args, context.projectRoot);
|
|
1693
|
+
if (result.exitCode !== 0) {
|
|
1694
|
+
return;
|
|
1695
|
+
}
|
|
1696
|
+
try {
|
|
1697
|
+
const parsed = JSON.parse(result.stdout);
|
|
1698
|
+
for (const event of parsed.events ?? []) {
|
|
1699
|
+
yield {
|
|
1700
|
+
timestamp: new Date(event.timestamp).toISOString(),
|
|
1701
|
+
level: inferLogLevel(event.message),
|
|
1702
|
+
message: event.message
|
|
1703
|
+
};
|
|
1704
|
+
}
|
|
1705
|
+
} catch {
|
|
1706
|
+
for (const line of result.stdout.split("\n").filter(Boolean)) {
|
|
1707
|
+
yield { timestamp: (/* @__PURE__ */ new Date()).toISOString(), level: "info", message: line };
|
|
1708
|
+
}
|
|
1709
|
+
}
|
|
1710
|
+
}
|
|
1711
|
+
async status() {
|
|
1712
|
+
const context = this.requireContext();
|
|
1713
|
+
const region = context.region ?? "us-east-1";
|
|
1714
|
+
const result = await this.runner.run("aws", [
|
|
1715
|
+
"ecs",
|
|
1716
|
+
"describe-services",
|
|
1717
|
+
"--cluster",
|
|
1718
|
+
context.appName,
|
|
1719
|
+
"--services",
|
|
1720
|
+
context.appName,
|
|
1721
|
+
"--region",
|
|
1722
|
+
region
|
|
1723
|
+
], context.projectRoot);
|
|
1724
|
+
if (result.exitCode !== 0) {
|
|
1725
|
+
return { state: "failed", message: result.stderr };
|
|
1726
|
+
}
|
|
1727
|
+
try {
|
|
1728
|
+
const parsed = JSON.parse(result.stdout);
|
|
1729
|
+
const service = parsed.services?.[0];
|
|
1730
|
+
if (!service) {
|
|
1731
|
+
return { state: "unknown", message: "Service not found" };
|
|
1732
|
+
}
|
|
1733
|
+
if (service.status === "ACTIVE" && service.runningCount > 0) {
|
|
1734
|
+
return {
|
|
1735
|
+
state: "healthy",
|
|
1736
|
+
message: `Running ${service.runningCount}/${service.desiredCount} tasks`
|
|
1737
|
+
};
|
|
1738
|
+
}
|
|
1739
|
+
return {
|
|
1740
|
+
state: service.runningCount === 0 ? "pending" : "healthy",
|
|
1741
|
+
message: `Status: ${service.status}, running: ${service.runningCount}/${service.desiredCount}`
|
|
1742
|
+
};
|
|
1743
|
+
} catch {
|
|
1744
|
+
return { state: "unknown", message: "Could not parse service status" };
|
|
1745
|
+
}
|
|
1746
|
+
}
|
|
1747
|
+
requireContext() {
|
|
1748
|
+
if (!this.currentContext) {
|
|
1749
|
+
throw new Error("AWS ECS adapter context is not initialized. Run provision() first.");
|
|
1750
|
+
}
|
|
1751
|
+
return this.currentContext;
|
|
1752
|
+
}
|
|
1753
|
+
};
|
|
1754
|
+
var NodeAwsCommandRunner = class {
|
|
1755
|
+
async run(command, args, cwd) {
|
|
1756
|
+
return new Promise((resolve9) => {
|
|
1757
|
+
const child = (0, import_node_child_process4.spawn)(command, args, {
|
|
1758
|
+
cwd,
|
|
1759
|
+
env: process.env,
|
|
1760
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
1761
|
+
});
|
|
1762
|
+
let stdout = "";
|
|
1763
|
+
let stderr = "";
|
|
1764
|
+
child.stdout?.on("data", (chunk) => {
|
|
1765
|
+
stdout += chunk.toString("utf-8");
|
|
1766
|
+
});
|
|
1767
|
+
child.stderr?.on("data", (chunk) => {
|
|
1768
|
+
stderr += chunk.toString("utf-8");
|
|
1769
|
+
});
|
|
1770
|
+
child.on("error", (error) => {
|
|
1771
|
+
resolve9({ exitCode: 1, stdout, stderr: `${stderr}
|
|
1772
|
+
${error.message}` });
|
|
1773
|
+
});
|
|
1774
|
+
child.on("exit", (code) => {
|
|
1775
|
+
resolve9({ exitCode: code ?? 1, stdout: stdout.trim(), stderr: stderr.trim() });
|
|
1776
|
+
});
|
|
1777
|
+
});
|
|
1778
|
+
}
|
|
1779
|
+
};
|
|
1780
|
+
function inferLogLevel(line) {
|
|
1781
|
+
const normalized = line.toLowerCase();
|
|
1782
|
+
if (normalized.includes("error")) return "error";
|
|
1783
|
+
if (normalized.includes("warn")) return "warn";
|
|
1784
|
+
if (normalized.includes("debug")) return "debug";
|
|
1785
|
+
return "info";
|
|
1786
|
+
}
|
|
1787
|
+
|
|
1788
|
+
// src/commands/deploy/adapters/aws-lightsail-adapter.ts
|
|
1789
|
+
var import_node_child_process5 = require("child_process");
|
|
1790
|
+
var import_node_path9 = require("path");
|
|
1791
|
+
var AwsLightsailAdapter = class {
|
|
1792
|
+
name = "aws-lightsail";
|
|
1793
|
+
logger = createLogger();
|
|
1794
|
+
runner;
|
|
1795
|
+
currentContext;
|
|
1796
|
+
constructor(options = {}) {
|
|
1797
|
+
this.runner = options.runner ?? new NodeAwsLightsailCommandRunner();
|
|
1798
|
+
this.currentContext = options.context ?? null;
|
|
1799
|
+
}
|
|
1800
|
+
setContext(context) {
|
|
1801
|
+
this.currentContext = context;
|
|
1802
|
+
}
|
|
1803
|
+
async detect() {
|
|
1804
|
+
const result = await this.runner.run("aws", ["--version"], process.cwd());
|
|
1805
|
+
return result.exitCode === 0;
|
|
1806
|
+
}
|
|
1807
|
+
async install() {
|
|
1808
|
+
const available = await this.detect();
|
|
1809
|
+
if (!available) {
|
|
1810
|
+
throw new Error(
|
|
1811
|
+
"AWS CLI is required but not installed. Install from https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html"
|
|
1812
|
+
);
|
|
1813
|
+
}
|
|
1814
|
+
}
|
|
1815
|
+
async authenticate() {
|
|
1816
|
+
const cwd = this.currentContext?.projectRoot ?? process.cwd();
|
|
1817
|
+
const result = await this.runner.run("aws", ["sts", "get-caller-identity"], cwd);
|
|
1818
|
+
if (result.exitCode !== 0) {
|
|
1819
|
+
throw new Error(
|
|
1820
|
+
"AWS CLI is not authenticated. Run `aws configure` or set AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY environment variables."
|
|
1821
|
+
);
|
|
1822
|
+
}
|
|
1823
|
+
}
|
|
1824
|
+
async provision(config) {
|
|
1825
|
+
this.currentContext = {
|
|
1826
|
+
projectRoot: config.projectRoot,
|
|
1827
|
+
appName: config.appName,
|
|
1828
|
+
region: config.region ?? "us-east-1"
|
|
1829
|
+
};
|
|
1830
|
+
const region = config.region ?? "us-east-1";
|
|
1831
|
+
const serviceName = sanitizeLightsailName(config.appName);
|
|
1832
|
+
const createService = await this.runner.run("aws", [
|
|
1833
|
+
"lightsail",
|
|
1834
|
+
"create-container-service",
|
|
1835
|
+
"--service-name",
|
|
1836
|
+
serviceName,
|
|
1837
|
+
"--power",
|
|
1838
|
+
"nano",
|
|
1839
|
+
"--scale",
|
|
1840
|
+
"1",
|
|
1841
|
+
"--region",
|
|
1842
|
+
region
|
|
1843
|
+
], config.projectRoot);
|
|
1844
|
+
if (createService.exitCode !== 0 && !createService.stderr.includes("already exists")) {
|
|
1845
|
+
throw new Error(`Failed to create Lightsail container service: ${createService.stderr}`);
|
|
1846
|
+
}
|
|
1847
|
+
return {
|
|
1848
|
+
applicationId: serviceName,
|
|
1849
|
+
databaseId: null,
|
|
1850
|
+
secretsSet: ["PORT"]
|
|
1851
|
+
};
|
|
1852
|
+
}
|
|
1853
|
+
async build(config) {
|
|
1854
|
+
this.currentContext = {
|
|
1855
|
+
projectRoot: config.projectRoot,
|
|
1856
|
+
appName: config.appName,
|
|
1857
|
+
region: config.region
|
|
1858
|
+
};
|
|
1859
|
+
const deployDirectory = (0, import_node_path9.join)(config.projectRoot, ".kora", "deploy");
|
|
1860
|
+
await bundleServer({
|
|
1861
|
+
projectRoot: config.projectRoot,
|
|
1862
|
+
deployDirectory
|
|
1863
|
+
});
|
|
1864
|
+
const client = await buildClient({
|
|
1865
|
+
projectRoot: config.projectRoot,
|
|
1866
|
+
outDir: (0, import_node_path9.join)(deployDirectory, "dist"),
|
|
1867
|
+
mode: "production"
|
|
1868
|
+
});
|
|
1869
|
+
return {
|
|
1870
|
+
clientDirectory: client.outDir,
|
|
1871
|
+
serverBundlePath: (0, import_node_path9.join)(deployDirectory, "server-bundled.js"),
|
|
1872
|
+
deployDirectory
|
|
1873
|
+
};
|
|
1874
|
+
}
|
|
1875
|
+
async deploy(artifacts) {
|
|
1876
|
+
const context = this.requireContext();
|
|
1877
|
+
const region = context.region ?? "us-east-1";
|
|
1878
|
+
const serviceName = sanitizeLightsailName(context.appName);
|
|
1879
|
+
const imageTag = `${serviceName}:latest`;
|
|
1880
|
+
this.logger.step("Building Docker image...");
|
|
1881
|
+
const dockerBuild = await this.runner.run("docker", [
|
|
1882
|
+
"build",
|
|
1883
|
+
"--platform",
|
|
1884
|
+
"linux/amd64",
|
|
1885
|
+
"-t",
|
|
1886
|
+
imageTag,
|
|
1887
|
+
"."
|
|
1888
|
+
], artifacts.deployDirectory);
|
|
1889
|
+
if (dockerBuild.exitCode !== 0) {
|
|
1890
|
+
throw new Error(`Docker build failed: ${dockerBuild.stderr}`);
|
|
1891
|
+
}
|
|
1892
|
+
this.logger.step("Pushing image to Lightsail...");
|
|
1893
|
+
const pushImage = await this.runner.run("aws", [
|
|
1894
|
+
"lightsail",
|
|
1895
|
+
"push-container-image",
|
|
1896
|
+
"--service-name",
|
|
1897
|
+
serviceName,
|
|
1898
|
+
"--label",
|
|
1899
|
+
"latest",
|
|
1900
|
+
"--image",
|
|
1901
|
+
imageTag,
|
|
1902
|
+
"--region",
|
|
1903
|
+
region
|
|
1904
|
+
], artifacts.deployDirectory);
|
|
1905
|
+
if (pushImage.exitCode !== 0) {
|
|
1906
|
+
throw new Error(`Lightsail image push failed: ${pushImage.stderr}`);
|
|
1907
|
+
}
|
|
1908
|
+
const lightsailImage = parseLightsailImageRef(pushImage.stdout) ?? `:${serviceName}.latest.1`;
|
|
1909
|
+
this.logger.step("Creating Lightsail deployment...");
|
|
1910
|
+
const environment = { PORT: "3001" };
|
|
1911
|
+
for (const key of PASSTHROUGH_ENV_VARS) {
|
|
1912
|
+
const value = process.env[key];
|
|
1913
|
+
if (value) {
|
|
1914
|
+
environment[key] = value;
|
|
1915
|
+
}
|
|
1916
|
+
}
|
|
1917
|
+
const containers = JSON.stringify({
|
|
1918
|
+
[serviceName]: {
|
|
1919
|
+
image: lightsailImage,
|
|
1920
|
+
ports: { "3001": "HTTP" },
|
|
1921
|
+
environment
|
|
1922
|
+
}
|
|
1923
|
+
});
|
|
1924
|
+
const publicEndpoint = JSON.stringify({
|
|
1925
|
+
containerName: serviceName,
|
|
1926
|
+
containerPort: 3001,
|
|
1927
|
+
healthCheck: {
|
|
1928
|
+
path: "/health",
|
|
1929
|
+
intervalSeconds: 30,
|
|
1930
|
+
timeoutSeconds: 5,
|
|
1931
|
+
healthyThreshold: 2,
|
|
1932
|
+
unhealthyThreshold: 3
|
|
1933
|
+
}
|
|
1934
|
+
});
|
|
1935
|
+
const createDeploy = await this.runner.run("aws", [
|
|
1936
|
+
"lightsail",
|
|
1937
|
+
"create-container-service-deployment",
|
|
1938
|
+
"--service-name",
|
|
1939
|
+
serviceName,
|
|
1940
|
+
"--containers",
|
|
1941
|
+
containers,
|
|
1942
|
+
"--public-endpoint",
|
|
1943
|
+
publicEndpoint,
|
|
1944
|
+
"--region",
|
|
1945
|
+
region
|
|
1946
|
+
], context.projectRoot);
|
|
1947
|
+
if (createDeploy.exitCode !== 0) {
|
|
1948
|
+
throw new Error(`Lightsail deployment failed: ${createDeploy.stderr}`);
|
|
1949
|
+
}
|
|
1950
|
+
const serviceInfo = await this.runner.run("aws", [
|
|
1951
|
+
"lightsail",
|
|
1952
|
+
"get-container-services",
|
|
1953
|
+
"--service-name",
|
|
1954
|
+
serviceName,
|
|
1955
|
+
"--region",
|
|
1956
|
+
region
|
|
1957
|
+
], context.projectRoot);
|
|
1958
|
+
const rawUrl = parseLightsailUrl(serviceInfo.stdout) ?? `https://${serviceName}.${region}.cs.amazonlightsail.com`;
|
|
1959
|
+
const serviceUrl = rawUrl.replace(/\/+$/, "");
|
|
1960
|
+
const deploymentId = (/* @__PURE__ */ new Date()).toISOString();
|
|
1961
|
+
return {
|
|
1962
|
+
deploymentId,
|
|
1963
|
+
liveUrl: serviceUrl,
|
|
1964
|
+
syncUrl: `${serviceUrl.replace("https://", "wss://")}/kora-sync`
|
|
1965
|
+
};
|
|
1966
|
+
}
|
|
1967
|
+
async rollback(_deploymentId) {
|
|
1968
|
+
const context = this.requireContext();
|
|
1969
|
+
const region = context.region ?? "us-east-1";
|
|
1970
|
+
const serviceName = sanitizeLightsailName(context.appName);
|
|
1971
|
+
const deployments = await this.runner.run("aws", [
|
|
1972
|
+
"lightsail",
|
|
1973
|
+
"get-container-service-deployments",
|
|
1974
|
+
"--service-name",
|
|
1975
|
+
serviceName,
|
|
1976
|
+
"--region",
|
|
1977
|
+
region
|
|
1978
|
+
], context.projectRoot);
|
|
1979
|
+
if (deployments.exitCode !== 0) {
|
|
1980
|
+
throw new Error(`Lightsail rollback failed: ${deployments.stderr}`);
|
|
1981
|
+
}
|
|
1982
|
+
const previousDeployment = parsePreviousDeployment(deployments.stdout, serviceName);
|
|
1983
|
+
if (!previousDeployment) {
|
|
1984
|
+
throw new Error("No previous deployment found to rollback to.");
|
|
1985
|
+
}
|
|
1986
|
+
const redeploy = await this.runner.run("aws", [
|
|
1987
|
+
"lightsail",
|
|
1988
|
+
"create-container-service-deployment",
|
|
1989
|
+
"--service-name",
|
|
1990
|
+
serviceName,
|
|
1991
|
+
"--containers",
|
|
1992
|
+
JSON.stringify(previousDeployment.containers),
|
|
1993
|
+
"--public-endpoint",
|
|
1994
|
+
JSON.stringify(previousDeployment.publicEndpoint),
|
|
1995
|
+
"--region",
|
|
1996
|
+
region
|
|
1997
|
+
], context.projectRoot);
|
|
1998
|
+
if (redeploy.exitCode !== 0) {
|
|
1999
|
+
throw new Error(`Lightsail rollback deployment failed: ${redeploy.stderr}`);
|
|
2000
|
+
}
|
|
2001
|
+
}
|
|
2002
|
+
async *logs(options) {
|
|
2003
|
+
const context = this.requireContext();
|
|
2004
|
+
const region = context.region ?? "us-east-1";
|
|
2005
|
+
const serviceName = sanitizeLightsailName(context.appName);
|
|
2006
|
+
const args = [
|
|
2007
|
+
"lightsail",
|
|
2008
|
+
"get-container-log",
|
|
2009
|
+
"--service-name",
|
|
2010
|
+
serviceName,
|
|
2011
|
+
"--container-name",
|
|
2012
|
+
serviceName,
|
|
2013
|
+
"--region",
|
|
2014
|
+
region
|
|
2015
|
+
];
|
|
2016
|
+
if (options.since) {
|
|
2017
|
+
args.push("--start-time", options.since);
|
|
2018
|
+
}
|
|
2019
|
+
const result = await this.runner.run("aws", args, context.projectRoot);
|
|
2020
|
+
if (result.exitCode !== 0) {
|
|
2021
|
+
return;
|
|
2022
|
+
}
|
|
2023
|
+
try {
|
|
2024
|
+
const parsed = JSON.parse(result.stdout);
|
|
2025
|
+
const events = parsed.logEvents ?? [];
|
|
2026
|
+
const limited = options.tail ? events.slice(-options.tail) : events;
|
|
2027
|
+
for (const event of limited) {
|
|
2028
|
+
yield {
|
|
2029
|
+
timestamp: event.createdAt,
|
|
2030
|
+
level: inferLogLevel2(event.message),
|
|
2031
|
+
message: event.message
|
|
2032
|
+
};
|
|
2033
|
+
}
|
|
2034
|
+
} catch {
|
|
2035
|
+
for (const line of result.stdout.split("\n").filter(Boolean)) {
|
|
2036
|
+
yield { timestamp: (/* @__PURE__ */ new Date()).toISOString(), level: "info", message: line };
|
|
2037
|
+
}
|
|
2038
|
+
}
|
|
2039
|
+
}
|
|
2040
|
+
async status() {
|
|
2041
|
+
const context = this.requireContext();
|
|
2042
|
+
const region = context.region ?? "us-east-1";
|
|
2043
|
+
const serviceName = sanitizeLightsailName(context.appName);
|
|
2044
|
+
const result = await this.runner.run("aws", [
|
|
2045
|
+
"lightsail",
|
|
2046
|
+
"get-container-services",
|
|
2047
|
+
"--service-name",
|
|
2048
|
+
serviceName,
|
|
2049
|
+
"--region",
|
|
2050
|
+
region
|
|
2051
|
+
], context.projectRoot);
|
|
2052
|
+
if (result.exitCode !== 0) {
|
|
2053
|
+
return { state: "failed", message: result.stderr };
|
|
2054
|
+
}
|
|
2055
|
+
try {
|
|
2056
|
+
const parsed = JSON.parse(result.stdout);
|
|
2057
|
+
const service = parsed.containerServices?.[0];
|
|
2058
|
+
if (!service) {
|
|
2059
|
+
return { state: "unknown", message: "Container service not found" };
|
|
2060
|
+
}
|
|
2061
|
+
const deployState = service.currentDeployment?.state ?? "UNKNOWN";
|
|
2062
|
+
if (service.state === "RUNNING" && deployState === "ACTIVE") {
|
|
2063
|
+
return {
|
|
2064
|
+
state: "healthy",
|
|
2065
|
+
message: "Lightsail container service is running",
|
|
2066
|
+
liveUrl: service.url
|
|
2067
|
+
};
|
|
2068
|
+
}
|
|
2069
|
+
if (service.state === "DEPLOYING" || deployState === "ACTIVATING") {
|
|
2070
|
+
return {
|
|
2071
|
+
state: "pending",
|
|
2072
|
+
message: `Service: ${service.state}, Deployment: ${deployState}`
|
|
2073
|
+
};
|
|
2074
|
+
}
|
|
2075
|
+
return {
|
|
2076
|
+
state: service.state === "DISABLED" ? "failed" : "unknown",
|
|
2077
|
+
message: `Service: ${service.state}, Deployment: ${deployState}`
|
|
2078
|
+
};
|
|
2079
|
+
} catch {
|
|
2080
|
+
return { state: "unknown", message: "Could not parse service status" };
|
|
2081
|
+
}
|
|
2082
|
+
}
|
|
2083
|
+
requireContext() {
|
|
2084
|
+
if (!this.currentContext) {
|
|
2085
|
+
throw new Error("AWS Lightsail adapter context is not initialized. Run provision() first.");
|
|
2086
|
+
}
|
|
2087
|
+
return this.currentContext;
|
|
2088
|
+
}
|
|
2089
|
+
};
|
|
2090
|
+
var NodeAwsLightsailCommandRunner = class {
|
|
2091
|
+
async run(command, args, cwd) {
|
|
2092
|
+
return new Promise((resolve9) => {
|
|
2093
|
+
const child = (0, import_node_child_process5.spawn)(command, args, {
|
|
2094
|
+
cwd,
|
|
2095
|
+
env: process.env,
|
|
2096
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
2097
|
+
});
|
|
2098
|
+
let stdout = "";
|
|
2099
|
+
let stderr = "";
|
|
2100
|
+
child.stdout?.on("data", (chunk) => {
|
|
2101
|
+
stdout += chunk.toString("utf-8");
|
|
2102
|
+
});
|
|
2103
|
+
child.stderr?.on("data", (chunk) => {
|
|
2104
|
+
stderr += chunk.toString("utf-8");
|
|
2105
|
+
});
|
|
2106
|
+
child.on("error", (error) => {
|
|
2107
|
+
resolve9({ exitCode: 1, stdout, stderr: `${stderr}
|
|
2108
|
+
${error.message}` });
|
|
2109
|
+
});
|
|
2110
|
+
child.on("exit", (code) => {
|
|
2111
|
+
resolve9({ exitCode: code ?? 1, stdout: stdout.trim(), stderr: stderr.trim() });
|
|
2112
|
+
});
|
|
2113
|
+
});
|
|
2114
|
+
}
|
|
2115
|
+
};
|
|
2116
|
+
var PASSTHROUGH_ENV_VARS = [
|
|
2117
|
+
"DATABASE_URL",
|
|
2118
|
+
"AUTH_SECRET",
|
|
2119
|
+
"PUBLIC_URL",
|
|
2120
|
+
"NODE_ENV"
|
|
2121
|
+
];
|
|
2122
|
+
function sanitizeLightsailName(name) {
|
|
2123
|
+
return name.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-{2,}/g, "-").replace(/^-|-$/g, "").slice(0, 255) || "kora-app";
|
|
2124
|
+
}
|
|
2125
|
+
function parseLightsailImageRef(output) {
|
|
2126
|
+
const match = output.match(/Refer to this image as\s+"?(:[^"\s]+)"?/i);
|
|
2127
|
+
if (match?.[1]) {
|
|
2128
|
+
return match[1];
|
|
2129
|
+
}
|
|
2130
|
+
const refMatch = output.match(/(:\S+\.\S+\.\d+)/);
|
|
2131
|
+
return refMatch?.[1] ?? null;
|
|
2132
|
+
}
|
|
2133
|
+
function parseLightsailUrl(rawJson) {
|
|
2134
|
+
try {
|
|
2135
|
+
const parsed = JSON.parse(rawJson);
|
|
2136
|
+
const url = parsed.containerServices?.[0]?.url;
|
|
2137
|
+
if (typeof url === "string" && url.length > 0) {
|
|
2138
|
+
return url.startsWith("https://") ? url : `https://${url}`;
|
|
2139
|
+
}
|
|
2140
|
+
return null;
|
|
2141
|
+
} catch {
|
|
2142
|
+
return null;
|
|
2143
|
+
}
|
|
2144
|
+
}
|
|
2145
|
+
function parsePreviousDeployment(rawJson, serviceName) {
|
|
2146
|
+
try {
|
|
2147
|
+
const parsed = JSON.parse(rawJson);
|
|
2148
|
+
const deployments = parsed.deployments ?? [];
|
|
2149
|
+
const previous = deployments.length > 1 ? deployments[1] : null;
|
|
2150
|
+
if (!previous) return null;
|
|
2151
|
+
return {
|
|
2152
|
+
containers: previous.containers,
|
|
2153
|
+
publicEndpoint: previous.publicEndpoint
|
|
2154
|
+
};
|
|
2155
|
+
} catch {
|
|
2156
|
+
return null;
|
|
2157
|
+
}
|
|
2158
|
+
}
|
|
2159
|
+
function inferLogLevel2(line) {
|
|
2160
|
+
const normalized = line.toLowerCase();
|
|
2161
|
+
if (normalized.includes("error")) return "error";
|
|
2162
|
+
if (normalized.includes("warn")) return "warn";
|
|
2163
|
+
if (normalized.includes("debug")) return "debug";
|
|
2164
|
+
return "info";
|
|
2165
|
+
}
|
|
2166
|
+
|
|
2167
|
+
// src/commands/deploy/adapters/fly-adapter.ts
|
|
2168
|
+
var import_node_child_process6 = require("child_process");
|
|
2169
|
+
var import_node_path11 = require("path");
|
|
2170
|
+
|
|
2171
|
+
// src/commands/deploy/artifacts/fly-toml-generator.ts
|
|
2172
|
+
var import_promises8 = require("fs/promises");
|
|
2173
|
+
var import_node_path10 = require("path");
|
|
2174
|
+
var GENERATED_HEADER = "# Generated by kora deploy \u2014 do not edit manually";
|
|
2175
|
+
function generateFlyToml(options) {
|
|
2176
|
+
const internalPort = options.internalPort ?? 3e3;
|
|
2177
|
+
return [
|
|
2178
|
+
GENERATED_HEADER,
|
|
2179
|
+
"",
|
|
2180
|
+
`app = "${options.appName}"`,
|
|
2181
|
+
`primary_region = "${options.region}"`,
|
|
2182
|
+
"",
|
|
2183
|
+
"[build]",
|
|
2184
|
+
` dockerfile = "Dockerfile"`,
|
|
2185
|
+
"",
|
|
2186
|
+
"[http_service]",
|
|
2187
|
+
` internal_port = ${String(internalPort)}`,
|
|
2188
|
+
" force_https = true",
|
|
2189
|
+
' auto_stop_machines = "stop"',
|
|
2190
|
+
" auto_start_machines = true",
|
|
2191
|
+
" min_machines_running = 0",
|
|
2192
|
+
"",
|
|
2193
|
+
" [[http_service.checks]]",
|
|
2194
|
+
' grace_period = "10s"',
|
|
2195
|
+
' interval = "30s"',
|
|
2196
|
+
' method = "GET"',
|
|
2197
|
+
' timeout = "5s"',
|
|
2198
|
+
' path = "/"',
|
|
2199
|
+
""
|
|
2200
|
+
].join("\n");
|
|
2201
|
+
}
|
|
2202
|
+
async function writeFlyTomlArtifact(deployDirectory, options) {
|
|
2203
|
+
await (0, import_promises8.mkdir)(deployDirectory, { recursive: true });
|
|
2204
|
+
const filePath = (0, import_node_path10.join)(deployDirectory, "fly.toml");
|
|
2205
|
+
await (0, import_promises8.writeFile)(filePath, generateFlyToml(options), "utf-8");
|
|
2206
|
+
return filePath;
|
|
2207
|
+
}
|
|
2208
|
+
|
|
1468
2209
|
// src/commands/deploy/adapters/fly-adapter.ts
|
|
1469
2210
|
var FlyAdapter = class {
|
|
1470
2211
|
name = "fly";
|
|
@@ -1555,7 +2296,7 @@ var FlyAdapter = class {
|
|
|
1555
2296
|
appName: config.appName,
|
|
1556
2297
|
region: config.region
|
|
1557
2298
|
};
|
|
1558
|
-
const deployDirectory = (0,
|
|
2299
|
+
const deployDirectory = (0, import_node_path11.join)(config.projectRoot, ".kora", "deploy");
|
|
1559
2300
|
await writeFlyTomlArtifact(deployDirectory, {
|
|
1560
2301
|
appName: config.appName,
|
|
1561
2302
|
region: config.region ?? "iad"
|
|
@@ -1566,12 +2307,12 @@ var FlyAdapter = class {
|
|
|
1566
2307
|
});
|
|
1567
2308
|
const client = await buildClient({
|
|
1568
2309
|
projectRoot: config.projectRoot,
|
|
1569
|
-
outDir: (0,
|
|
2310
|
+
outDir: (0, import_node_path11.join)(deployDirectory, "dist"),
|
|
1570
2311
|
mode: "production"
|
|
1571
2312
|
});
|
|
1572
2313
|
return {
|
|
1573
2314
|
clientDirectory: client.outDir,
|
|
1574
|
-
serverBundlePath: (0,
|
|
2315
|
+
serverBundlePath: (0, import_node_path11.join)(deployDirectory, "server-bundled.js"),
|
|
1575
2316
|
deployDirectory
|
|
1576
2317
|
};
|
|
1577
2318
|
}
|
|
@@ -1633,7 +2374,7 @@ var FlyAdapter = class {
|
|
|
1633
2374
|
for (const line of lines) {
|
|
1634
2375
|
yield {
|
|
1635
2376
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1636
|
-
level:
|
|
2377
|
+
level: inferLogLevel3(line),
|
|
1637
2378
|
message: line
|
|
1638
2379
|
};
|
|
1639
2380
|
}
|
|
@@ -1698,7 +2439,7 @@ var FlyAdapter = class {
|
|
|
1698
2439
|
var NodeFlyCommandRunner = class {
|
|
1699
2440
|
async run(command, args, cwd) {
|
|
1700
2441
|
return await new Promise((resolve9) => {
|
|
1701
|
-
const child = (0,
|
|
2442
|
+
const child = (0, import_node_child_process6.spawn)(command, args, {
|
|
1702
2443
|
cwd,
|
|
1703
2444
|
env: process.env,
|
|
1704
2445
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -1773,7 +2514,7 @@ function parseJsonRecord(value) {
|
|
|
1773
2514
|
return null;
|
|
1774
2515
|
}
|
|
1775
2516
|
}
|
|
1776
|
-
function
|
|
2517
|
+
function inferLogLevel3(line) {
|
|
1777
2518
|
const normalized = line.toLowerCase();
|
|
1778
2519
|
if (normalized.includes("error")) return "error";
|
|
1779
2520
|
if (normalized.includes("warn")) return "warn";
|
|
@@ -1792,12 +2533,12 @@ ${stdout}`.toLowerCase();
|
|
|
1792
2533
|
}
|
|
1793
2534
|
|
|
1794
2535
|
// src/commands/deploy/adapters/railway-adapter.ts
|
|
1795
|
-
var
|
|
1796
|
-
var
|
|
2536
|
+
var import_node_child_process7 = require("child_process");
|
|
2537
|
+
var import_node_path13 = require("path");
|
|
1797
2538
|
|
|
1798
2539
|
// src/commands/deploy/artifacts/railway-json-generator.ts
|
|
1799
2540
|
var import_promises9 = require("fs/promises");
|
|
1800
|
-
var
|
|
2541
|
+
var import_node_path12 = require("path");
|
|
1801
2542
|
var GENERATED_HEADER2 = "Generated by kora deploy - do not edit manually.";
|
|
1802
2543
|
function generateRailwayJson(options) {
|
|
1803
2544
|
const file = {
|
|
@@ -1823,7 +2564,7 @@ function generateRailwayJson(options) {
|
|
|
1823
2564
|
}
|
|
1824
2565
|
async function writeRailwayJsonArtifact(deployDirectory, options) {
|
|
1825
2566
|
await (0, import_promises9.mkdir)(deployDirectory, { recursive: true });
|
|
1826
|
-
const filePath = (0,
|
|
2567
|
+
const filePath = (0, import_node_path12.join)(deployDirectory, "railway.json");
|
|
1827
2568
|
await (0, import_promises9.writeFile)(filePath, generateRailwayJson(options), "utf-8");
|
|
1828
2569
|
return filePath;
|
|
1829
2570
|
}
|
|
@@ -1913,7 +2654,7 @@ var RailwayAdapter = class {
|
|
|
1913
2654
|
appName: config.appName,
|
|
1914
2655
|
region: config.region
|
|
1915
2656
|
};
|
|
1916
|
-
const deployDirectory = (0,
|
|
2657
|
+
const deployDirectory = (0, import_node_path13.join)(config.projectRoot, ".kora", "deploy");
|
|
1917
2658
|
await writeRailwayJsonArtifact(deployDirectory, {
|
|
1918
2659
|
appName: config.appName,
|
|
1919
2660
|
environment: config.environment
|
|
@@ -1924,12 +2665,12 @@ var RailwayAdapter = class {
|
|
|
1924
2665
|
});
|
|
1925
2666
|
const client = await buildClient({
|
|
1926
2667
|
projectRoot: config.projectRoot,
|
|
1927
|
-
outDir: (0,
|
|
2668
|
+
outDir: (0, import_node_path13.join)(deployDirectory, "dist"),
|
|
1928
2669
|
mode: "production"
|
|
1929
2670
|
});
|
|
1930
2671
|
return {
|
|
1931
2672
|
clientDirectory: client.outDir,
|
|
1932
|
-
serverBundlePath: (0,
|
|
2673
|
+
serverBundlePath: (0, import_node_path13.join)(deployDirectory, "server-bundled.js"),
|
|
1933
2674
|
deployDirectory
|
|
1934
2675
|
};
|
|
1935
2676
|
}
|
|
@@ -1976,7 +2717,7 @@ var RailwayAdapter = class {
|
|
|
1976
2717
|
for (const line of lines) {
|
|
1977
2718
|
yield {
|
|
1978
2719
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1979
|
-
level:
|
|
2720
|
+
level: inferLogLevel4(line),
|
|
1980
2721
|
message: line
|
|
1981
2722
|
};
|
|
1982
2723
|
}
|
|
@@ -2034,7 +2775,7 @@ var RailwayAdapter = class {
|
|
|
2034
2775
|
var NodeRailwayCommandRunner = class {
|
|
2035
2776
|
async run(command, args, cwd) {
|
|
2036
2777
|
return await new Promise((resolve9) => {
|
|
2037
|
-
const child = (0,
|
|
2778
|
+
const child = (0, import_node_child_process7.spawn)(command, args, {
|
|
2038
2779
|
cwd,
|
|
2039
2780
|
env: process.env,
|
|
2040
2781
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -2125,7 +2866,7 @@ function ensureHttpsUrl(value) {
|
|
|
2125
2866
|
}
|
|
2126
2867
|
return `https://${value}`;
|
|
2127
2868
|
}
|
|
2128
|
-
function
|
|
2869
|
+
function inferLogLevel4(line) {
|
|
2129
2870
|
const normalized = line.toLowerCase();
|
|
2130
2871
|
if (normalized.includes("error")) return "error";
|
|
2131
2872
|
if (normalized.includes("warn")) return "warn";
|
|
@@ -2198,6 +2939,10 @@ function createDeployAdapter(platform) {
|
|
|
2198
2939
|
return new FlyAdapter();
|
|
2199
2940
|
case "railway":
|
|
2200
2941
|
return new RailwayAdapter();
|
|
2942
|
+
case "aws-ecs":
|
|
2943
|
+
return new AwsEcsAdapter();
|
|
2944
|
+
case "aws-lightsail":
|
|
2945
|
+
return new AwsLightsailAdapter();
|
|
2201
2946
|
case "render":
|
|
2202
2947
|
case "docker":
|
|
2203
2948
|
case "kora-cloud":
|
|
@@ -2211,7 +2956,7 @@ function createDeployAdapter(platform) {
|
|
|
2211
2956
|
|
|
2212
2957
|
// src/commands/deploy/artifacts/dockerfile-generator.ts
|
|
2213
2958
|
var import_promises10 = require("fs/promises");
|
|
2214
|
-
var
|
|
2959
|
+
var import_node_path14 = require("path");
|
|
2215
2960
|
var GENERATED_HEADER3 = "# Generated by kora deploy \u2014 do not edit manually";
|
|
2216
2961
|
function generateDockerfile(options = {}) {
|
|
2217
2962
|
const nodeVersion = options.nodeVersion ?? "20-alpine";
|
|
@@ -2275,31 +3020,31 @@ function generateDockerIgnore() {
|
|
|
2275
3020
|
}
|
|
2276
3021
|
async function writeDockerfileArtifact(deployDirectory, options = {}) {
|
|
2277
3022
|
await (0, import_promises10.mkdir)(deployDirectory, { recursive: true });
|
|
2278
|
-
const dockerfilePath = (0,
|
|
3023
|
+
const dockerfilePath = (0, import_node_path14.join)(deployDirectory, "Dockerfile");
|
|
2279
3024
|
await (0, import_promises10.writeFile)(dockerfilePath, generateDockerfile(options), "utf-8");
|
|
2280
3025
|
if (options.nativeDependencies && Object.keys(options.nativeDependencies).length > 0) {
|
|
2281
|
-
const pkgJsonPath = (0,
|
|
3026
|
+
const pkgJsonPath = (0, import_node_path14.join)(deployDirectory, "package.json");
|
|
2282
3027
|
await (0, import_promises10.writeFile)(pkgJsonPath, generateDeployPackageJson(options.nativeDependencies), "utf-8");
|
|
2283
3028
|
}
|
|
2284
3029
|
return dockerfilePath;
|
|
2285
3030
|
}
|
|
2286
3031
|
async function writeDockerIgnoreArtifact(deployDirectory) {
|
|
2287
3032
|
await (0, import_promises10.mkdir)(deployDirectory, { recursive: true });
|
|
2288
|
-
const dockerIgnorePath = (0,
|
|
3033
|
+
const dockerIgnorePath = (0, import_node_path14.join)(deployDirectory, ".dockerignore");
|
|
2289
3034
|
await (0, import_promises10.writeFile)(dockerIgnorePath, generateDockerIgnore(), "utf-8");
|
|
2290
3035
|
return dockerIgnorePath;
|
|
2291
3036
|
}
|
|
2292
3037
|
|
|
2293
3038
|
// src/commands/deploy/state/deploy-state.ts
|
|
2294
3039
|
var import_promises11 = require("fs/promises");
|
|
2295
|
-
var
|
|
2296
|
-
var KORA_DEPLOY_DIRECTORY = (0,
|
|
3040
|
+
var import_node_path15 = require("path");
|
|
3041
|
+
var KORA_DEPLOY_DIRECTORY = (0, import_node_path15.join)(".kora", "deploy");
|
|
2297
3042
|
var DEPLOY_STATE_FILENAME = "deploy.json";
|
|
2298
3043
|
function resolveDeployDirectory(projectRoot) {
|
|
2299
|
-
return (0,
|
|
3044
|
+
return (0, import_node_path15.join)(projectRoot, KORA_DEPLOY_DIRECTORY);
|
|
2300
3045
|
}
|
|
2301
3046
|
function resolveDeployStatePath(projectRoot) {
|
|
2302
|
-
return (0,
|
|
3047
|
+
return (0, import_node_path15.join)(resolveDeployDirectory(projectRoot), DEPLOY_STATE_FILENAME);
|
|
2303
3048
|
}
|
|
2304
3049
|
async function readDeployState(projectRoot) {
|
|
2305
3050
|
const statePath = resolveDeployStatePath(projectRoot);
|
|
@@ -2583,7 +3328,8 @@ var deployCommand = (0, import_citty2.defineCommand)({
|
|
|
2583
3328
|
await writeDockerfileArtifact(deployDirectory, {
|
|
2584
3329
|
nativeDependencies: {
|
|
2585
3330
|
"better-sqlite3": "^11.0.0",
|
|
2586
|
-
"drizzle-orm": "^0.45.2"
|
|
3331
|
+
"drizzle-orm": "^0.45.2",
|
|
3332
|
+
"postgres": "^3.4.0"
|
|
2587
3333
|
}
|
|
2588
3334
|
});
|
|
2589
3335
|
await writeDockerIgnoreArtifact(deployDirectory);
|
|
@@ -2650,6 +3396,14 @@ async function resolvePlatform(options) {
|
|
|
2650
3396
|
label: "Railway",
|
|
2651
3397
|
value: "railway"
|
|
2652
3398
|
},
|
|
3399
|
+
{
|
|
3400
|
+
label: "AWS ECS Fargate (production multi-instance)",
|
|
3401
|
+
value: "aws-ecs"
|
|
3402
|
+
},
|
|
3403
|
+
{
|
|
3404
|
+
label: "AWS Lightsail Containers (simple, cost-effective)",
|
|
3405
|
+
value: "aws-lightsail"
|
|
3406
|
+
},
|
|
2653
3407
|
{
|
|
2654
3408
|
label: "Render",
|
|
2655
3409
|
value: "render"
|
|
@@ -2676,7 +3430,7 @@ function resolveAppName(argValue, storedValue, projectRoot, confirm) {
|
|
|
2676
3430
|
"Missing app name in --confirm mode. Provide --app or run an interactive deploy first."
|
|
2677
3431
|
);
|
|
2678
3432
|
}
|
|
2679
|
-
return sanitizeAppName((0,
|
|
3433
|
+
return sanitizeAppName((0, import_node_path16.basename)(projectRoot));
|
|
2680
3434
|
}
|
|
2681
3435
|
function resolveRegion(argValue, storedValue, confirm) {
|
|
2682
3436
|
if (typeof argValue === "string" && argValue.length > 0) return argValue;
|
|
@@ -2716,14 +3470,14 @@ function hasContextSetter(adapter) {
|
|
|
2716
3470
|
|
|
2717
3471
|
// src/commands/dev/dev-command.ts
|
|
2718
3472
|
var import_promises13 = require("fs/promises");
|
|
2719
|
-
var
|
|
2720
|
-
var
|
|
3473
|
+
var import_node_path19 = require("path");
|
|
3474
|
+
var import_node_path20 = require("path");
|
|
2721
3475
|
var import_citty3 = require("citty");
|
|
2722
3476
|
|
|
2723
3477
|
// src/commands/dev/kora-config.ts
|
|
2724
|
-
var
|
|
3478
|
+
var import_node_child_process8 = require("child_process");
|
|
2725
3479
|
var import_promises12 = require("fs/promises");
|
|
2726
|
-
var
|
|
3480
|
+
var import_node_path17 = require("path");
|
|
2727
3481
|
var import_node_url3 = require("url");
|
|
2728
3482
|
var CONFIG_CANDIDATES = [
|
|
2729
3483
|
"kora.config.ts",
|
|
@@ -2736,7 +3490,7 @@ var CONFIG_CANDIDATES = [
|
|
|
2736
3490
|
async function loadKoraConfig(projectRoot) {
|
|
2737
3491
|
const configPath = await findKoraConfigFile(projectRoot);
|
|
2738
3492
|
if (!configPath) return null;
|
|
2739
|
-
const ext = (0,
|
|
3493
|
+
const ext = (0, import_node_path17.extname)(configPath);
|
|
2740
3494
|
if (ext === ".ts" || ext === ".mts" || ext === ".cts") {
|
|
2741
3495
|
const loaded2 = await loadTypeScriptConfig(configPath, projectRoot);
|
|
2742
3496
|
return toConfigObject(loaded2);
|
|
@@ -2746,7 +3500,7 @@ async function loadKoraConfig(projectRoot) {
|
|
|
2746
3500
|
}
|
|
2747
3501
|
async function findKoraConfigFile(projectRoot) {
|
|
2748
3502
|
for (const file of CONFIG_CANDIDATES) {
|
|
2749
|
-
const candidate = (0,
|
|
3503
|
+
const candidate = (0, import_node_path17.join)(projectRoot, file);
|
|
2750
3504
|
try {
|
|
2751
3505
|
await (0, import_promises12.access)(candidate);
|
|
2752
3506
|
return candidate;
|
|
@@ -2775,7 +3529,7 @@ async function loadTypeScriptConfig(configPath, projectRoot) {
|
|
|
2775
3529
|
}
|
|
2776
3530
|
async function runCommand(command, args, cwd) {
|
|
2777
3531
|
return await new Promise((resolve9, reject) => {
|
|
2778
|
-
const child = (0,
|
|
3532
|
+
const child = (0, import_node_child_process8.spawn)(command, args, {
|
|
2779
3533
|
cwd,
|
|
2780
3534
|
stdio: ["ignore", "pipe", "pipe"],
|
|
2781
3535
|
env: process.env
|
|
@@ -2812,7 +3566,7 @@ function toConfigObject(mod) {
|
|
|
2812
3566
|
}
|
|
2813
3567
|
|
|
2814
3568
|
// src/commands/dev/process-manager.ts
|
|
2815
|
-
var
|
|
3569
|
+
var import_node_child_process9 = require("child_process");
|
|
2816
3570
|
var ProcessManager = class {
|
|
2817
3571
|
processes = /* @__PURE__ */ new Map();
|
|
2818
3572
|
spawn(config) {
|
|
@@ -2821,7 +3575,7 @@ var ProcessManager = class {
|
|
|
2821
3575
|
env: { ...process.env, ...config.env },
|
|
2822
3576
|
stdio: ["ignore", "pipe", "pipe"]
|
|
2823
3577
|
};
|
|
2824
|
-
const child = (0,
|
|
3578
|
+
const child = (0, import_node_child_process9.spawn)(config.command, config.args, options);
|
|
2825
3579
|
let resolveExit;
|
|
2826
3580
|
const runningProcess = {
|
|
2827
3581
|
child,
|
|
@@ -2899,9 +3653,9 @@ function delay(ms) {
|
|
|
2899
3653
|
}
|
|
2900
3654
|
|
|
2901
3655
|
// src/commands/dev/schema-watcher.ts
|
|
2902
|
-
var
|
|
3656
|
+
var import_node_child_process10 = require("child_process");
|
|
2903
3657
|
var import_node_fs4 = require("fs");
|
|
2904
|
-
var
|
|
3658
|
+
var import_node_path18 = require("path");
|
|
2905
3659
|
var SchemaWatcher = class {
|
|
2906
3660
|
constructor(config) {
|
|
2907
3661
|
this.config = config;
|
|
@@ -2929,7 +3683,7 @@ var SchemaWatcher = class {
|
|
|
2929
3683
|
this.watcher = null;
|
|
2930
3684
|
}
|
|
2931
3685
|
async regenerate() {
|
|
2932
|
-
const koraBinJs = (0,
|
|
3686
|
+
const koraBinJs = (0, import_node_path18.join)(this.config.projectRoot, "node_modules", "@korajs", "cli", "dist", "bin.js");
|
|
2933
3687
|
const hasTsx = await hasTsxInstalled(this.config.projectRoot);
|
|
2934
3688
|
const command = process.execPath;
|
|
2935
3689
|
const args = hasTsx ? ["--import", "tsx", koraBinJs, "generate", "types", "--schema", this.config.schemaPath] : [koraBinJs, "generate", "types", "--schema", this.config.schemaPath];
|
|
@@ -2950,7 +3704,7 @@ var SchemaWatcher = class {
|
|
|
2950
3704
|
};
|
|
2951
3705
|
async function spawnCommand(command, args, cwd) {
|
|
2952
3706
|
await new Promise((resolve9, reject) => {
|
|
2953
|
-
const child = (0,
|
|
3707
|
+
const child = (0, import_node_child_process10.spawn)(command, args, {
|
|
2954
3708
|
cwd,
|
|
2955
3709
|
stdio: ["ignore", "pipe", "pipe"],
|
|
2956
3710
|
env: process.env
|
|
@@ -3028,7 +3782,7 @@ var devCommand = (0, import_citty3.defineCommand)({
|
|
|
3028
3782
|
const watchDebounceMs = typeof config?.dev?.watch === "object" && typeof config.dev.watch.debounceMs === "number" ? config.dev.watch.debounceMs : 300;
|
|
3029
3783
|
const viteEntryPoint = await resolveProjectBinaryEntryPoint(projectRoot, "vite", "vite");
|
|
3030
3784
|
if (!viteEntryPoint) {
|
|
3031
|
-
throw new DevServerError("vite", (0,
|
|
3785
|
+
throw new DevServerError("vite", (0, import_node_path19.join)(projectRoot, "node_modules", ".bin", "vite"));
|
|
3032
3786
|
}
|
|
3033
3787
|
const syncServerFile = await findSyncServerFile(projectRoot);
|
|
3034
3788
|
let managedSyncStore = normalizeManagedSyncStore(config, projectRoot);
|
|
@@ -3044,7 +3798,7 @@ var devCommand = (0, import_citty3.defineCommand)({
|
|
|
3044
3798
|
}
|
|
3045
3799
|
if (shouldStartSync && syncServerFile === null && managedSyncStore) {
|
|
3046
3800
|
const hasServerPackage = await fileExists2(
|
|
3047
|
-
(0,
|
|
3801
|
+
(0, import_node_path19.join)(projectRoot, "node_modules", "@korajs", "server", "package.json")
|
|
3048
3802
|
);
|
|
3049
3803
|
if (!hasServerPackage) {
|
|
3050
3804
|
logger.warn(
|
|
@@ -3061,7 +3815,7 @@ var devCommand = (0, import_citty3.defineCommand)({
|
|
|
3061
3815
|
}
|
|
3062
3816
|
let configuredSchemaPath = null;
|
|
3063
3817
|
if (typeof config?.schema === "string") {
|
|
3064
|
-
const candidate = (0,
|
|
3818
|
+
const candidate = (0, import_node_path20.resolve)(projectRoot, config.schema);
|
|
3065
3819
|
if (await fileExists2(candidate)) {
|
|
3066
3820
|
configuredSchemaPath = candidate;
|
|
3067
3821
|
} else {
|
|
@@ -3173,7 +3927,7 @@ var devCommand = (0, import_citty3.defineCommand)({
|
|
|
3173
3927
|
}
|
|
3174
3928
|
});
|
|
3175
3929
|
async function findSyncServerFile(projectRoot) {
|
|
3176
|
-
const candidates = [(0,
|
|
3930
|
+
const candidates = [(0, import_node_path19.join)(projectRoot, "server.ts"), (0, import_node_path19.join)(projectRoot, "server.js")];
|
|
3177
3931
|
for (const candidate of candidates) {
|
|
3178
3932
|
try {
|
|
3179
3933
|
await (0, import_promises13.access)(candidate);
|
|
@@ -3197,7 +3951,7 @@ function normalizeManagedSyncStore(config, projectRoot) {
|
|
|
3197
3951
|
const store = sync.store;
|
|
3198
3952
|
if (store === void 0) return { type: "memory" };
|
|
3199
3953
|
if (store === "memory") return { type: "memory" };
|
|
3200
|
-
if (store === "sqlite") return { type: "sqlite", filename: (0,
|
|
3954
|
+
if (store === "sqlite") return { type: "sqlite", filename: (0, import_node_path19.join)(projectRoot, "kora-sync.db") };
|
|
3201
3955
|
if (store === "postgres") {
|
|
3202
3956
|
const connectionString = process.env.DATABASE_URL;
|
|
3203
3957
|
if (!connectionString) return null;
|
|
@@ -3206,7 +3960,7 @@ function normalizeManagedSyncStore(config, projectRoot) {
|
|
|
3206
3960
|
if (typeof store === "object" && store !== null) {
|
|
3207
3961
|
if (store.type === "memory") return { type: "memory" };
|
|
3208
3962
|
if (store.type === "sqlite") {
|
|
3209
|
-
const filename = typeof store.filename === "string" && store.filename.length > 0 ? (0,
|
|
3963
|
+
const filename = typeof store.filename === "string" && store.filename.length > 0 ? (0, import_node_path20.resolve)(projectRoot, store.filename) : (0, import_node_path19.join)(projectRoot, "kora-sync.db");
|
|
3210
3964
|
return { type: "sqlite", filename };
|
|
3211
3965
|
}
|
|
3212
3966
|
if (store.type === "postgres" && typeof store.connectionString === "string") {
|
|
@@ -3270,7 +4024,7 @@ await new Promise(() => {});
|
|
|
3270
4024
|
|
|
3271
4025
|
// src/commands/generate/generate-command.ts
|
|
3272
4026
|
var import_promises14 = require("fs/promises");
|
|
3273
|
-
var
|
|
4027
|
+
var import_node_path21 = require("path");
|
|
3274
4028
|
var import_citty4 = require("citty");
|
|
3275
4029
|
|
|
3276
4030
|
// src/commands/generate/type-generator.ts
|
|
@@ -3401,7 +4155,7 @@ var generateCommand = (0, import_citty4.defineCommand)({
|
|
|
3401
4155
|
}
|
|
3402
4156
|
let schemaPath;
|
|
3403
4157
|
if (args.schema && typeof args.schema === "string") {
|
|
3404
|
-
schemaPath = (0,
|
|
4158
|
+
schemaPath = (0, import_node_path21.resolve)(args.schema);
|
|
3405
4159
|
} else {
|
|
3406
4160
|
const found = await findSchemaFile(projectRoot);
|
|
3407
4161
|
if (!found) {
|
|
@@ -3424,8 +4178,8 @@ var generateCommand = (0, import_citty4.defineCommand)({
|
|
|
3424
4178
|
}
|
|
3425
4179
|
const output = generateTypes(schema);
|
|
3426
4180
|
const outputFile = typeof args.output === "string" ? args.output : "kora/generated/types.ts";
|
|
3427
|
-
const outputPath = (0,
|
|
3428
|
-
await (0, import_promises14.mkdir)((0,
|
|
4181
|
+
const outputPath = (0, import_node_path21.resolve)(projectRoot, outputFile);
|
|
4182
|
+
await (0, import_promises14.mkdir)((0, import_node_path21.dirname)(outputPath), { recursive: true });
|
|
3429
4183
|
await (0, import_promises14.writeFile)(outputPath, output, "utf-8");
|
|
3430
4184
|
logger.success(`Generated types at ${outputPath}`);
|
|
3431
4185
|
}
|
|
@@ -3447,7 +4201,7 @@ function isSchemaDefinition(value) {
|
|
|
3447
4201
|
|
|
3448
4202
|
// src/commands/migrate/migrate-command.ts
|
|
3449
4203
|
var import_promises15 = require("fs/promises");
|
|
3450
|
-
var
|
|
4204
|
+
var import_node_path23 = require("path");
|
|
3451
4205
|
var import_citty5 = require("citty");
|
|
3452
4206
|
|
|
3453
4207
|
// src/commands/migrate/migration-generator.ts
|
|
@@ -4016,11 +4770,11 @@ async function loadPostgresModule() {
|
|
|
4016
4770
|
}
|
|
4017
4771
|
|
|
4018
4772
|
// src/commands/migrate/schema-loader.ts
|
|
4019
|
-
var
|
|
4020
|
-
var
|
|
4773
|
+
var import_node_child_process11 = require("child_process");
|
|
4774
|
+
var import_node_path22 = require("path");
|
|
4021
4775
|
var import_node_url4 = require("url");
|
|
4022
4776
|
async function loadSchemaDefinition(schemaPath, projectRoot) {
|
|
4023
|
-
const ext = (0,
|
|
4777
|
+
const ext = (0, import_node_path22.extname)(schemaPath);
|
|
4024
4778
|
const moduleValue = ext === ".ts" || ext === ".mts" || ext === ".cts" ? await loadTypeScriptModule(schemaPath, projectRoot) : await import(`${(0, import_node_url4.pathToFileURL)(schemaPath).href}?t=${Date.now()}-${Math.random()}`);
|
|
4025
4779
|
return extractSchema2(moduleValue);
|
|
4026
4780
|
}
|
|
@@ -4060,7 +4814,7 @@ async function loadTypeScriptModule(schemaPath, projectRoot) {
|
|
|
4060
4814
|
}
|
|
4061
4815
|
async function runCommand2(command, args, cwd) {
|
|
4062
4816
|
return await new Promise((resolve9, reject) => {
|
|
4063
|
-
const child = (0,
|
|
4817
|
+
const child = (0, import_node_child_process11.spawn)(command, args, {
|
|
4064
4818
|
cwd,
|
|
4065
4819
|
stdio: ["ignore", "pipe", "pipe"],
|
|
4066
4820
|
env: process.env
|
|
@@ -4133,12 +4887,12 @@ var migrateCommand = (0, import_citty5.defineCommand)({
|
|
|
4133
4887
|
throw new InvalidProjectError(process.cwd());
|
|
4134
4888
|
}
|
|
4135
4889
|
const config = await loadKoraConfig(projectRoot);
|
|
4136
|
-
const resolvedSchemaPath = typeof args.schema === "string" ? (0,
|
|
4890
|
+
const resolvedSchemaPath = typeof args.schema === "string" ? (0, import_node_path23.resolve)(projectRoot, args.schema) : typeof config?.schema === "string" ? (0, import_node_path23.resolve)(projectRoot, config.schema) : await findSchemaFile(projectRoot);
|
|
4137
4891
|
if (!resolvedSchemaPath) {
|
|
4138
4892
|
throw new SchemaNotFoundError(["src/schema.ts", "schema.ts", "src/schema.js", "schema.js"]);
|
|
4139
4893
|
}
|
|
4140
4894
|
const currentSchema = await loadSchemaDefinition(resolvedSchemaPath, projectRoot);
|
|
4141
|
-
const snapshotFile = (0,
|
|
4895
|
+
const snapshotFile = (0, import_node_path23.join)(projectRoot, SNAPSHOT_PATH);
|
|
4142
4896
|
const previousSchema = await readSchemaSnapshot(snapshotFile);
|
|
4143
4897
|
if (!previousSchema) {
|
|
4144
4898
|
if (args["dry-run"] === true) {
|
|
@@ -4151,7 +4905,7 @@ var migrateCommand = (0, import_citty5.defineCommand)({
|
|
|
4151
4905
|
return;
|
|
4152
4906
|
}
|
|
4153
4907
|
const diff = diffSchemas(previousSchema, currentSchema);
|
|
4154
|
-
const outputDir = typeof args["output-dir"] === "string" ? (0,
|
|
4908
|
+
const outputDir = typeof args["output-dir"] === "string" ? (0, import_node_path23.resolve)(projectRoot, args["output-dir"]) : (0, import_node_path23.resolve)(projectRoot, MIGRATIONS_DIR);
|
|
4155
4909
|
if (!diff.hasChanges) {
|
|
4156
4910
|
logger.success("No schema changes detected.");
|
|
4157
4911
|
if (args.apply === true) {
|
|
@@ -4255,7 +5009,7 @@ function isInteractiveTerminal2() {
|
|
|
4255
5009
|
return process.stdin.isTTY === true && process.stdout.isTTY === true;
|
|
4256
5010
|
}
|
|
4257
5011
|
async function writeSchemaSnapshot(path, schema) {
|
|
4258
|
-
await (0, import_promises15.mkdir)((0,
|
|
5012
|
+
await (0, import_promises15.mkdir)((0, import_node_path23.dirname)(path), { recursive: true });
|
|
4259
5013
|
await (0, import_promises15.writeFile)(path, `${JSON.stringify(schema, null, 2)}
|
|
4260
5014
|
`, "utf-8");
|
|
4261
5015
|
}
|
|
@@ -4263,7 +5017,7 @@ async function writeMigrationFile(outputDir, fromVersion, toVersion, generated)
|
|
|
4263
5017
|
const existing = await (0, import_promises15.readdir)(outputDir).catch(() => []);
|
|
4264
5018
|
const sequence = existing.filter((file) => /^\d{3}-/.test(file)).length + 1;
|
|
4265
5019
|
const filename = `${String(sequence).padStart(3, "0")}-v${fromVersion}-to-v${toVersion}.ts`;
|
|
4266
|
-
const path = (0,
|
|
5020
|
+
const path = (0, import_node_path23.join)(outputDir, filename);
|
|
4267
5021
|
const migrationId = filename.replace(/\.ts$/, "");
|
|
4268
5022
|
const fileContent = [
|
|
4269
5023
|
`export const up = ${JSON.stringify(generated.up, null, 2)} as const`,
|
|
@@ -4276,7 +5030,7 @@ async function writeMigrationFile(outputDir, fromVersion, toVersion, generated)
|
|
|
4276
5030
|
""
|
|
4277
5031
|
].join("\n");
|
|
4278
5032
|
await (0, import_promises15.writeFile)(path, fileContent, "utf-8");
|
|
4279
|
-
await writeMigrationManifest((0,
|
|
5033
|
+
await writeMigrationManifest((0, import_node_path23.join)(outputDir, `${migrationId}.json`), {
|
|
4280
5034
|
id: migrationId,
|
|
4281
5035
|
fromVersion,
|
|
4282
5036
|
toVersion,
|
|
@@ -4297,13 +5051,13 @@ async function listMigrationManifests(outputDir) {
|
|
|
4297
5051
|
const manifests = [];
|
|
4298
5052
|
for (const file of migrationFiles) {
|
|
4299
5053
|
const id = file.replace(/\.ts$/, "");
|
|
4300
|
-
const manifestPath = (0,
|
|
5054
|
+
const manifestPath = (0, import_node_path23.join)(outputDir, `${id}.json`);
|
|
4301
5055
|
const jsonManifest = await readMigrationManifest(manifestPath);
|
|
4302
5056
|
if (jsonManifest) {
|
|
4303
5057
|
manifests.push({ ...jsonManifest, id });
|
|
4304
5058
|
continue;
|
|
4305
5059
|
}
|
|
4306
|
-
const migrationPath = (0,
|
|
5060
|
+
const migrationPath = (0, import_node_path23.join)(outputDir, file);
|
|
4307
5061
|
const sourceManifest = await readMigrationManifestFromSource(migrationPath, id);
|
|
4308
5062
|
manifests.push(sourceManifest);
|
|
4309
5063
|
}
|
|
@@ -4379,18 +5133,18 @@ function parseLiteralExport(source, exportName) {
|
|
|
4379
5133
|
}
|
|
4380
5134
|
function resolveSqliteApplyPath(dbArg, projectRoot, config) {
|
|
4381
5135
|
if (typeof dbArg === "string") {
|
|
4382
|
-
return (0,
|
|
5136
|
+
return (0, import_node_path23.resolve)(projectRoot, dbArg);
|
|
4383
5137
|
}
|
|
4384
5138
|
const sync = config?.dev?.sync;
|
|
4385
5139
|
if (typeof sync === "object" && sync !== null) {
|
|
4386
5140
|
if (sync.store === "sqlite") {
|
|
4387
|
-
return (0,
|
|
5141
|
+
return (0, import_node_path23.join)(projectRoot, "kora-sync.db");
|
|
4388
5142
|
}
|
|
4389
5143
|
if (typeof sync.store === "object" && sync.store !== null && sync.store.type === "sqlite") {
|
|
4390
5144
|
if (typeof sync.store.filename === "string" && sync.store.filename.length > 0) {
|
|
4391
|
-
return (0,
|
|
5145
|
+
return (0, import_node_path23.resolve)(projectRoot, sync.store.filename);
|
|
4392
5146
|
}
|
|
4393
|
-
return (0,
|
|
5147
|
+
return (0, import_node_path23.join)(projectRoot, "kora-sync.db");
|
|
4394
5148
|
}
|
|
4395
5149
|
}
|
|
4396
5150
|
return void 0;
|