@dainprotocol/cli 1.2.36 → 1.3.1
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/commands/build.d.ts +9 -0
- package/dist/commands/build.js +108 -260
- package/dist/commands/config.d.ts +1 -0
- package/dist/commands/config.js +37 -96
- package/dist/commands/deploy.d.ts +3 -0
- package/dist/commands/deploy.js +123 -267
- package/dist/commands/dev.d.ts +6 -0
- package/dist/commands/dev.js +202 -314
- package/dist/commands/init.d.ts +1 -0
- package/dist/commands/init.js +28 -31
- package/dist/commands/logs.d.ts +4 -0
- package/dist/commands/logs.js +53 -143
- package/dist/commands/start.d.ts +3 -0
- package/dist/commands/start.js +16 -22
- package/dist/commands/status.d.ts +8 -0
- package/dist/commands/status.js +26 -83
- package/dist/commands/testchat.d.ts +5 -0
- package/dist/commands/testchat.js +82 -197
- package/dist/commands/undeploy.d.ts +3 -0
- package/dist/commands/undeploy.js +18 -74
- package/dist/index.d.ts +2 -0
- package/dist/index.js +35 -38
- package/dist/templates/default/package.json +1 -1
- package/dist/templates/default/tsconfig.json +3 -3
- package/dist/utils.d.ts +62 -0
- package/dist/utils.js +149 -247
- package/package.json +5 -4
- package/dist/__tests__/build.test.js +0 -289
- package/dist/__tests__/deploy.test.js +0 -126
- package/dist/__tests__/dev.test.js +0 -321
- package/dist/__tests__/init.test.js +0 -290
- package/dist/__tests__/integration.test.js +0 -134
- package/dist/__tests__/logs.test.js +0 -135
- package/dist/__tests__/testchat.test.js +0 -214
- package/dist/__tests__/utils.test.js +0 -335
package/dist/commands/dev.js
CHANGED
|
@@ -1,79 +1,26 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
20
|
-
});
|
|
21
|
-
};
|
|
22
|
-
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
23
|
-
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
|
|
24
|
-
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
25
|
-
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
26
|
-
function step(op) {
|
|
27
|
-
if (f) throw new TypeError("Generator is already executing.");
|
|
28
|
-
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
|
29
|
-
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
30
|
-
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
31
|
-
switch (op[0]) {
|
|
32
|
-
case 0: case 1: t = op; break;
|
|
33
|
-
case 4: _.label++; return { value: op[1], done: false };
|
|
34
|
-
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
35
|
-
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
36
|
-
default:
|
|
37
|
-
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
38
|
-
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
39
|
-
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
40
|
-
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
41
|
-
if (t[2]) _.ops.pop();
|
|
42
|
-
_.trys.pop(); continue;
|
|
43
|
-
}
|
|
44
|
-
op = body.call(thisArg, _);
|
|
45
|
-
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
46
|
-
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
47
|
-
}
|
|
48
|
-
};
|
|
49
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
50
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
51
|
-
};
|
|
52
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
53
|
-
exports.default = dev;
|
|
54
|
-
var child_process_1 = require("child_process");
|
|
55
|
-
var utils_1 = require("../utils");
|
|
56
|
-
var ora_1 = __importDefault(require("ora"));
|
|
57
|
-
var chokidar_1 = __importDefault(require("chokidar"));
|
|
58
|
-
var path_1 = __importDefault(require("path"));
|
|
59
|
-
var miniflare_1 = require("miniflare");
|
|
60
|
-
var build_1 = __importDefault(require("./build"));
|
|
61
|
-
var fs_extra_1 = __importDefault(require("fs-extra"));
|
|
62
|
-
var net_1 = require("net");
|
|
63
|
-
var childProcess = null;
|
|
64
|
-
var watcher = null;
|
|
65
|
-
var mf = null;
|
|
66
|
-
var tunnelUrl = null;
|
|
67
|
-
var isFirstStart = true;
|
|
68
|
-
var proxyServer = null;
|
|
69
|
-
var isCleaningUp = false;
|
|
70
|
-
var debounceTimer = null;
|
|
71
|
-
var KILL_TIMEOUT_MS = 5000;
|
|
1
|
+
import { spawn } from 'child_process';
|
|
2
|
+
import { getDainConfig, setupProxy, logError, logInfo, logSuccess, getStaticFilesPath, displayTunnelUrl, } from '../utils.js';
|
|
3
|
+
import ora from 'ora';
|
|
4
|
+
import chokidar from 'chokidar';
|
|
5
|
+
import path from 'path';
|
|
6
|
+
import { Log, LogLevel, Miniflare } from 'miniflare';
|
|
7
|
+
import build from './build.js';
|
|
8
|
+
import fs from 'fs-extra';
|
|
9
|
+
import { createServer } from 'net';
|
|
10
|
+
let childProcess = null;
|
|
11
|
+
let watcher = null;
|
|
12
|
+
let mf = null;
|
|
13
|
+
let tunnelUrl = null;
|
|
14
|
+
let isFirstStart = true;
|
|
15
|
+
let proxyServer = null;
|
|
16
|
+
let isCleaningUp = false;
|
|
17
|
+
let debounceTimer = null;
|
|
18
|
+
const KILL_TIMEOUT_MS = 5000;
|
|
72
19
|
function isPortAvailable(port) {
|
|
73
|
-
return new Promise(
|
|
74
|
-
|
|
75
|
-
.listen(port,
|
|
76
|
-
.on('error',
|
|
20
|
+
return new Promise((resolve) => {
|
|
21
|
+
const server = createServer()
|
|
22
|
+
.listen(port, () => { server.close(); resolve(true); })
|
|
23
|
+
.on('error', () => resolve(false));
|
|
77
24
|
});
|
|
78
25
|
}
|
|
79
26
|
/**
|
|
@@ -81,13 +28,13 @@ function isPortAvailable(port) {
|
|
|
81
28
|
* Sends SIGTERM first, escalates to SIGKILL after timeout.
|
|
82
29
|
*/
|
|
83
30
|
function killProcess(proc) {
|
|
84
|
-
return new Promise(
|
|
31
|
+
return new Promise((resolve) => {
|
|
85
32
|
if (!proc.pid || proc.killed) {
|
|
86
33
|
resolve();
|
|
87
34
|
return;
|
|
88
35
|
}
|
|
89
|
-
|
|
90
|
-
|
|
36
|
+
let resolved = false;
|
|
37
|
+
const done = () => {
|
|
91
38
|
if (resolved)
|
|
92
39
|
return;
|
|
93
40
|
resolved = true;
|
|
@@ -98,255 +45,196 @@ function killProcess(proc) {
|
|
|
98
45
|
proc.once('exit', done);
|
|
99
46
|
proc.kill('SIGTERM');
|
|
100
47
|
// Escalate to SIGKILL if process doesn't exit within timeout
|
|
101
|
-
|
|
48
|
+
const forceKillTimer = setTimeout(() => {
|
|
102
49
|
if (!resolved) {
|
|
103
50
|
try {
|
|
104
51
|
proc.kill('SIGKILL');
|
|
105
52
|
}
|
|
106
|
-
catch
|
|
53
|
+
catch { }
|
|
107
54
|
// Resolve after SIGKILL — don't wait forever
|
|
108
55
|
setTimeout(done, 500);
|
|
109
56
|
}
|
|
110
57
|
}, KILL_TIMEOUT_MS);
|
|
111
58
|
});
|
|
112
59
|
}
|
|
113
|
-
function cleanup() {
|
|
114
|
-
|
|
115
|
-
return
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
watcher = null;
|
|
135
|
-
}
|
|
136
|
-
if (mf) {
|
|
137
|
-
mf.dispose();
|
|
138
|
-
mf = null;
|
|
139
|
-
}
|
|
140
|
-
(0, utils_1.logInfo)('Development server and file watcher stopped.');
|
|
141
|
-
return [2 /*return*/];
|
|
142
|
-
}
|
|
143
|
-
});
|
|
144
|
-
});
|
|
60
|
+
async function cleanup() {
|
|
61
|
+
if (isCleaningUp)
|
|
62
|
+
return;
|
|
63
|
+
isCleaningUp = true;
|
|
64
|
+
if (debounceTimer) {
|
|
65
|
+
clearTimeout(debounceTimer);
|
|
66
|
+
debounceTimer = null;
|
|
67
|
+
}
|
|
68
|
+
if (childProcess) {
|
|
69
|
+
await killProcess(childProcess);
|
|
70
|
+
childProcess = null;
|
|
71
|
+
}
|
|
72
|
+
if (watcher) {
|
|
73
|
+
watcher.close();
|
|
74
|
+
watcher = null;
|
|
75
|
+
}
|
|
76
|
+
if (mf) {
|
|
77
|
+
mf.dispose();
|
|
78
|
+
mf = null;
|
|
79
|
+
}
|
|
80
|
+
logInfo('Development server and file watcher stopped.');
|
|
145
81
|
}
|
|
146
|
-
function gracefulShutdown() {
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
_b.label = 2;
|
|
157
|
-
case 2:
|
|
158
|
-
_b.trys.push([2, 4, , 5]);
|
|
159
|
-
return [4 /*yield*/, proxyServer.stop()];
|
|
160
|
-
case 3:
|
|
161
|
-
_b.sent();
|
|
162
|
-
(0, utils_1.logInfo)('Proxy server closed.');
|
|
163
|
-
return [3 /*break*/, 5];
|
|
164
|
-
case 4:
|
|
165
|
-
_a = _b.sent();
|
|
166
|
-
return [3 /*break*/, 5];
|
|
167
|
-
case 5:
|
|
168
|
-
process.exit(exitCode);
|
|
169
|
-
return [2 /*return*/];
|
|
170
|
-
}
|
|
171
|
-
});
|
|
172
|
-
});
|
|
82
|
+
async function gracefulShutdown(exitCode = 0) {
|
|
83
|
+
await cleanup();
|
|
84
|
+
if (proxyServer) {
|
|
85
|
+
try {
|
|
86
|
+
await proxyServer.stop();
|
|
87
|
+
logInfo('Proxy server closed.');
|
|
88
|
+
}
|
|
89
|
+
catch { }
|
|
90
|
+
}
|
|
91
|
+
process.exit(exitCode);
|
|
173
92
|
}
|
|
174
|
-
function startProcess(
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
if (!fs_extra_1.default.existsSync(tsNodePath)) {
|
|
193
|
-
spinner.fail('ts-node not found. Run: npm install ts-node typescript');
|
|
194
|
-
return [2 /*return*/];
|
|
195
|
-
}
|
|
196
|
-
if (!fs_extra_1.default.existsSync(mainFile)) {
|
|
197
|
-
spinner.fail("Main file not found: ".concat(mainFile));
|
|
198
|
-
return [2 /*return*/];
|
|
199
|
-
}
|
|
200
|
-
childProcess = (0, child_process_1.spawn)(tsNodePath, [mainFile], {
|
|
201
|
-
env: __assign(__assign({}, process.env), envVars),
|
|
202
|
-
stdio: ['inherit', 'pipe', 'pipe'],
|
|
203
|
-
shell: false,
|
|
204
|
-
});
|
|
205
|
-
markStarted = function (success) {
|
|
206
|
-
if (hasStarted)
|
|
207
|
-
return;
|
|
208
|
-
hasStarted = true;
|
|
209
|
-
if (success) {
|
|
210
|
-
spinner.succeed(isRestart ? 'Development server restarted.' : 'Development server started.');
|
|
211
|
-
if (tunnelUrl && isFirstStart) {
|
|
212
|
-
(0, utils_1.displayTunnelUrl)(tunnelUrl);
|
|
213
|
-
isFirstStart = false;
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
else {
|
|
217
|
-
spinner.fail('Development server error.');
|
|
218
|
-
}
|
|
219
|
-
};
|
|
220
|
-
(_a = childProcess.stdout) === null || _a === void 0 ? void 0 : _a.on('data', function (data) { markStarted(true); process.stdout.write(data.toString()); });
|
|
221
|
-
(_b = childProcess.stderr) === null || _b === void 0 ? void 0 : _b.on('data', function (data) {
|
|
222
|
-
var output = data.toString();
|
|
223
|
-
markStarted(output.includes('Error:') || output.includes('error:') ? false : true);
|
|
224
|
-
process.stderr.write(output);
|
|
225
|
-
});
|
|
226
|
-
childProcess.on('close', function (code) {
|
|
227
|
-
if (code !== 0 && code !== null && !hasStarted)
|
|
228
|
-
spinner.fail("Development server exited with code ".concat(code));
|
|
229
|
-
childProcess = null;
|
|
230
|
-
});
|
|
231
|
-
childProcess.on('error', function (error) { spinner.fail("Failed to start: ".concat(error.message)); childProcess = null; });
|
|
232
|
-
return [2 /*return*/];
|
|
233
|
-
}
|
|
93
|
+
async function startProcess(mainFile, envVars, isRestart = false) {
|
|
94
|
+
// Kill previous process and WAIT for it to fully exit before spawning a new one
|
|
95
|
+
if (childProcess) {
|
|
96
|
+
await killProcess(childProcess);
|
|
97
|
+
childProcess = null;
|
|
98
|
+
}
|
|
99
|
+
const spinner = ora(isRestart ? 'Restarting development server...' : 'Starting development server...').start();
|
|
100
|
+
let hasStarted = false;
|
|
101
|
+
if (!fs.existsSync(mainFile)) {
|
|
102
|
+
spinner.fail(`Main file not found: ${mainFile}`);
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
const runtime = envVars.DAIN_RUNTIME || "node";
|
|
106
|
+
if (runtime === "bun") {
|
|
107
|
+
childProcess = spawn("bun", [mainFile], {
|
|
108
|
+
env: { ...process.env, ...envVars },
|
|
109
|
+
stdio: ["inherit", "pipe", "pipe"],
|
|
110
|
+
shell: false,
|
|
234
111
|
});
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
portNumber = parseInt(port, 10);
|
|
247
|
-
if (isNaN(portNumber) || portNumber < 1 || portNumber > 65535) {
|
|
248
|
-
(0, utils_1.logError)("Invalid port: ".concat(port, ". Must be 1-65535. Using default port 2022"));
|
|
249
|
-
port = '2022';
|
|
250
|
-
}
|
|
251
|
-
runtime = options.runtime || config.runtime || 'node';
|
|
252
|
-
mainFile = config['main-file'];
|
|
253
|
-
resolvedMain = path_1.default.resolve(process.cwd(), mainFile);
|
|
254
|
-
if (!resolvedMain.startsWith(process.cwd())) {
|
|
255
|
-
(0, utils_1.logError)('Invalid main-file path: must be within project directory');
|
|
256
|
-
process.exit(1);
|
|
257
|
-
}
|
|
258
|
-
envVars = {
|
|
259
|
-
PORT: port,
|
|
260
|
-
DAIN_API_KEY: config['api-key'],
|
|
261
|
-
DAIN_PROJECT_ID: config['project-id'],
|
|
262
|
-
DAIN_ENVIRONMENT: config['environment'],
|
|
263
|
-
DAIN_OUT_DIR: config['out-dir'],
|
|
264
|
-
};
|
|
265
|
-
process.once('SIGINT', function () { return gracefulShutdown(0); });
|
|
266
|
-
process.once('SIGTERM', function () { return gracefulShutdown(0); });
|
|
267
|
-
process.once('uncaughtException', function (error) { (0, utils_1.logError)('Uncaught Exception:', error); gracefulShutdown(1); });
|
|
268
|
-
process.once('unhandledRejection', function (reason) { (0, utils_1.logError)('Unhandled Rejection:', reason); gracefulShutdown(1); });
|
|
269
|
-
_a.label = 1;
|
|
270
|
-
case 1:
|
|
271
|
-
_a.trys.push([1, 10, , 11]);
|
|
272
|
-
return [4 /*yield*/, isPortAvailable(portNumber)];
|
|
273
|
-
case 2:
|
|
274
|
-
if (!(_a.sent())) {
|
|
275
|
-
(0, utils_1.logError)("Port ".concat(portNumber, " is already in use. Use --port to specify a different port."));
|
|
276
|
-
process.exit(1);
|
|
277
|
-
}
|
|
278
|
-
process.env.PORT = port;
|
|
279
|
-
if (!!options.noproxy) return [3 /*break*/, 4];
|
|
280
|
-
if (!config['api-key'])
|
|
281
|
-
throw new Error("'api-key' is required when using development proxy");
|
|
282
|
-
return [4 /*yield*/, (0, utils_1.setupProxy)(port, config['api-key'], config)];
|
|
283
|
-
case 3:
|
|
284
|
-
proxySetup = _a.sent();
|
|
285
|
-
proxyServer = proxySetup.client;
|
|
286
|
-
tunnelUrl = proxySetup.tunnelUrl;
|
|
287
|
-
_a.label = 4;
|
|
288
|
-
case 4:
|
|
289
|
-
if (!(runtime === 'node')) return [3 /*break*/, 6];
|
|
290
|
-
return [4 /*yield*/, startProcess(mainFile, envVars)];
|
|
291
|
-
case 5:
|
|
292
|
-
_a.sent();
|
|
293
|
-
watchPaths = [
|
|
294
|
-
path_1.default.dirname(mainFile),
|
|
295
|
-
config['static-dir'] ? path_1.default.join(process.cwd(), config['static-dir']) : (0, utils_1.getStaticFilesPath)(),
|
|
296
|
-
].filter(function (p) { return fs_extra_1.default.existsSync(p); });
|
|
297
|
-
isRestarting_1 = false;
|
|
298
|
-
watcher = chokidar_1.default.watch(watchPaths, { ignored: /(^|[\/\\])\./, persistent: true, ignoreInitial: true });
|
|
299
|
-
watcher.on('change', function (changedPath) {
|
|
300
|
-
if (debounceTimer)
|
|
301
|
-
clearTimeout(debounceTimer);
|
|
302
|
-
debounceTimer = setTimeout(function () { return __awaiter(_this, void 0, void 0, function () {
|
|
303
|
-
return __generator(this, function (_a) {
|
|
304
|
-
switch (_a.label) {
|
|
305
|
-
case 0:
|
|
306
|
-
if (isRestarting_1)
|
|
307
|
-
return [2 /*return*/];
|
|
308
|
-
isRestarting_1 = true;
|
|
309
|
-
(0, utils_1.logInfo)("File ".concat(changedPath, " changed. Restarting..."));
|
|
310
|
-
return [4 /*yield*/, startProcess(mainFile, envVars, true)];
|
|
311
|
-
case 1:
|
|
312
|
-
_a.sent();
|
|
313
|
-
isRestarting_1 = false;
|
|
314
|
-
return [2 /*return*/];
|
|
315
|
-
}
|
|
316
|
-
});
|
|
317
|
-
}); }, 300);
|
|
318
|
-
});
|
|
319
|
-
(0, utils_1.logInfo)('Watching for file changes...');
|
|
320
|
-
return [3 /*break*/, 9];
|
|
321
|
-
case 6:
|
|
322
|
-
if (!(runtime === 'workers')) return [3 /*break*/, 8];
|
|
323
|
-
dainDir = path_1.default.join(process.cwd(), '.dain');
|
|
324
|
-
outFile = path_1.default.join(dainDir, path_1.default.basename(config['main-file'], '.ts') + '.mjs');
|
|
325
|
-
return [4 /*yield*/, (0, build_1.default)({ config: options.config, runtime: 'workers', watch: true })];
|
|
326
|
-
case 7:
|
|
327
|
-
_a.sent();
|
|
328
|
-
MFconfig_1 = { scriptPath: outFile, modules: true, port: parseInt(port, 10), log: new miniflare_1.Log(miniflare_1.LogLevel.DEBUG), liveReload: true };
|
|
329
|
-
mf = new miniflare_1.Miniflare(MFconfig_1);
|
|
330
|
-
(0, utils_1.logSuccess)("Miniflare server started on port ".concat(port));
|
|
331
|
-
fs_extra_1.default.watch(dainDir, { recursive: true }, function (_eventType, filename) {
|
|
332
|
-
if (debounceTimer)
|
|
333
|
-
clearTimeout(debounceTimer);
|
|
334
|
-
debounceTimer = setTimeout(function () { if (mf) {
|
|
335
|
-
mf.setOptions(MFconfig_1);
|
|
336
|
-
(0, utils_1.logInfo)("Build updated (".concat(filename, ")"));
|
|
337
|
-
} }, 300);
|
|
338
|
-
});
|
|
339
|
-
(0, utils_1.logInfo)('Watching for file changes in source and build directories...');
|
|
340
|
-
return [3 /*break*/, 9];
|
|
341
|
-
case 8: throw new Error("Unsupported runtime: ".concat(runtime));
|
|
342
|
-
case 9: return [3 /*break*/, 11];
|
|
343
|
-
case 10:
|
|
344
|
-
error_1 = _a.sent();
|
|
345
|
-
(0, utils_1.logError)("Error in dev process for ".concat(runtime, " runtime:"), error_1);
|
|
346
|
-
gracefulShutdown(1);
|
|
347
|
-
return [3 /*break*/, 11];
|
|
348
|
-
case 11: return [2 /*return*/];
|
|
349
|
-
}
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
const tsNodePath = path.join(process.cwd(), "node_modules", ".bin", "ts-node");
|
|
115
|
+
if (!fs.existsSync(tsNodePath)) {
|
|
116
|
+
spinner.fail("ts-node not found. Run: npm install ts-node typescript");
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
childProcess = spawn(tsNodePath, [mainFile], {
|
|
120
|
+
env: { ...process.env, ...envVars },
|
|
121
|
+
stdio: ["inherit", "pipe", "pipe"],
|
|
122
|
+
shell: false,
|
|
350
123
|
});
|
|
124
|
+
}
|
|
125
|
+
const markStarted = (success) => {
|
|
126
|
+
if (hasStarted)
|
|
127
|
+
return;
|
|
128
|
+
hasStarted = true;
|
|
129
|
+
if (success) {
|
|
130
|
+
spinner.succeed(isRestart ? 'Development server restarted.' : 'Development server started.');
|
|
131
|
+
if (tunnelUrl && isFirstStart) {
|
|
132
|
+
displayTunnelUrl(tunnelUrl);
|
|
133
|
+
isFirstStart = false;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
spinner.fail('Development server error.');
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
childProcess.stdout?.on('data', (data) => { markStarted(true); process.stdout.write(data.toString()); });
|
|
141
|
+
childProcess.stderr?.on('data', (data) => {
|
|
142
|
+
const output = data.toString();
|
|
143
|
+
markStarted(output.includes('Error:') || output.includes('error:') ? false : true);
|
|
144
|
+
process.stderr.write(output);
|
|
145
|
+
});
|
|
146
|
+
childProcess.on('close', (code) => {
|
|
147
|
+
if (code !== 0 && code !== null && !hasStarted)
|
|
148
|
+
spinner.fail(`Development server exited with code ${code}`);
|
|
149
|
+
childProcess = null;
|
|
351
150
|
});
|
|
151
|
+
childProcess.on('error', (error) => { spinner.fail(`Failed to start: ${error.message}`); childProcess = null; });
|
|
152
|
+
}
|
|
153
|
+
export default async function dev(options) {
|
|
154
|
+
const config = getDainConfig(options.config);
|
|
155
|
+
let port = process.env.PORT || options.port || '2022';
|
|
156
|
+
const portNumber = parseInt(port, 10);
|
|
157
|
+
if (isNaN(portNumber) || portNumber < 1 || portNumber > 65535) {
|
|
158
|
+
logError(`Invalid port: ${port}. Must be 1-65535. Using default port 2022`);
|
|
159
|
+
port = '2022';
|
|
160
|
+
}
|
|
161
|
+
const runtime = options.runtime || config.runtime || 'node';
|
|
162
|
+
const mainFile = config['main-file'];
|
|
163
|
+
const resolvedMain = path.resolve(process.cwd(), mainFile);
|
|
164
|
+
if (!resolvedMain.startsWith(process.cwd())) {
|
|
165
|
+
logError('Invalid main-file path: must be within project directory');
|
|
166
|
+
process.exit(1);
|
|
167
|
+
}
|
|
168
|
+
const envVars = {
|
|
169
|
+
PORT: port,
|
|
170
|
+
DAIN_API_KEY: config['api-key'],
|
|
171
|
+
DAIN_PROJECT_ID: config['project-id'],
|
|
172
|
+
DAIN_ENVIRONMENT: config['environment'],
|
|
173
|
+
DAIN_OUT_DIR: config['out-dir'],
|
|
174
|
+
DAIN_RUNTIME: runtime,
|
|
175
|
+
};
|
|
176
|
+
process.once('SIGINT', () => gracefulShutdown(0));
|
|
177
|
+
process.once('SIGTERM', () => gracefulShutdown(0));
|
|
178
|
+
process.once('uncaughtException', (error) => { logError('Uncaught Exception:', error); gracefulShutdown(1); });
|
|
179
|
+
process.once('unhandledRejection', (reason) => { logError('Unhandled Rejection:', reason); gracefulShutdown(1); });
|
|
180
|
+
try {
|
|
181
|
+
if (!(await isPortAvailable(portNumber))) {
|
|
182
|
+
logError(`Port ${portNumber} is already in use. Use --port to specify a different port.`);
|
|
183
|
+
process.exit(1);
|
|
184
|
+
}
|
|
185
|
+
process.env.PORT = port;
|
|
186
|
+
if (!options.noproxy) {
|
|
187
|
+
if (!config['api-key'])
|
|
188
|
+
throw new Error("'api-key' is required when using development proxy");
|
|
189
|
+
const proxySetup = await setupProxy(port, config['api-key'], config);
|
|
190
|
+
proxyServer = proxySetup.client;
|
|
191
|
+
tunnelUrl = proxySetup.tunnelUrl;
|
|
192
|
+
}
|
|
193
|
+
if (runtime === 'node' || runtime === 'bun') {
|
|
194
|
+
await startProcess(mainFile, envVars);
|
|
195
|
+
const watchPaths = [
|
|
196
|
+
path.dirname(mainFile),
|
|
197
|
+
config['static-dir'] ? path.join(process.cwd(), config['static-dir']) : getStaticFilesPath(),
|
|
198
|
+
].filter((p) => fs.existsSync(p));
|
|
199
|
+
let isRestarting = false;
|
|
200
|
+
watcher = chokidar.watch(watchPaths, { ignored: /(^|[\/\\])\./, persistent: true, ignoreInitial: true });
|
|
201
|
+
watcher.on('change', (changedPath) => {
|
|
202
|
+
if (debounceTimer)
|
|
203
|
+
clearTimeout(debounceTimer);
|
|
204
|
+
debounceTimer = setTimeout(async () => {
|
|
205
|
+
if (isRestarting)
|
|
206
|
+
return;
|
|
207
|
+
isRestarting = true;
|
|
208
|
+
logInfo(`File ${changedPath} changed. Restarting...`);
|
|
209
|
+
await startProcess(mainFile, envVars, true);
|
|
210
|
+
isRestarting = false;
|
|
211
|
+
}, 300);
|
|
212
|
+
});
|
|
213
|
+
logInfo('Watching for file changes...');
|
|
214
|
+
}
|
|
215
|
+
else if (runtime === 'workers') {
|
|
216
|
+
const dainDir = path.join(process.cwd(), '.dain');
|
|
217
|
+
const outFile = path.join(dainDir, path.basename(config['main-file'], '.ts') + '.mjs');
|
|
218
|
+
await build({ config: options.config, runtime: 'workers', watch: true });
|
|
219
|
+
const MFconfig = { scriptPath: outFile, modules: true, port: parseInt(port, 10), log: new Log(LogLevel.DEBUG), liveReload: true };
|
|
220
|
+
mf = new Miniflare(MFconfig);
|
|
221
|
+
logSuccess(`Miniflare server started on port ${port}`);
|
|
222
|
+
fs.watch(dainDir, { recursive: true }, (_eventType, filename) => {
|
|
223
|
+
if (debounceTimer)
|
|
224
|
+
clearTimeout(debounceTimer);
|
|
225
|
+
debounceTimer = setTimeout(() => { if (mf) {
|
|
226
|
+
mf.setOptions(MFconfig);
|
|
227
|
+
logInfo(`Build updated (${filename})`);
|
|
228
|
+
} }, 300);
|
|
229
|
+
});
|
|
230
|
+
logInfo('Watching for file changes in source and build directories...');
|
|
231
|
+
}
|
|
232
|
+
else {
|
|
233
|
+
throw new Error(`Unsupported runtime: ${runtime}`);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
catch (error) {
|
|
237
|
+
logError(`Error in dev process for ${runtime} runtime:`, error);
|
|
238
|
+
gracefulShutdown(1);
|
|
239
|
+
}
|
|
352
240
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default function init(projectName: string): void;
|
package/dist/commands/init.js
CHANGED
|
@@ -1,46 +1,43 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
};
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
var spinner = (0, ora_1.default)('Initializing Dain project...').start();
|
|
13
|
-
var projectDir = path_1.default.join(process.cwd(), projectName);
|
|
14
|
-
var templateDir = path_1.default.join(__dirname, '..', '..', 'templates', 'default');
|
|
1
|
+
import fs from 'fs-extra';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
import { logError, logSuccess, logInfo } from '../utils.js';
|
|
5
|
+
import ora from 'ora';
|
|
6
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
7
|
+
const __dirname = path.dirname(__filename);
|
|
8
|
+
export default function init(projectName) {
|
|
9
|
+
const spinner = ora('Initializing Dain project...').start();
|
|
10
|
+
const projectDir = path.join(process.cwd(), projectName);
|
|
11
|
+
const templateDir = path.join(__dirname, '..', '..', 'templates', 'default');
|
|
15
12
|
try {
|
|
16
|
-
|
|
13
|
+
const nodeVersion = Number(process.version.slice(1).split('.')[0]);
|
|
17
14
|
if (nodeVersion < 20) {
|
|
18
15
|
spinner.fail('Node.js version 20.7 or higher is required');
|
|
19
|
-
|
|
20
|
-
|
|
16
|
+
logError('Please upgrade Node.js to version 20.7 or higher');
|
|
17
|
+
logInfo('You can download it from: https://nodejs.org/');
|
|
21
18
|
process.exit(1);
|
|
22
19
|
}
|
|
23
|
-
if (
|
|
24
|
-
spinner.fail(
|
|
20
|
+
if (fs.existsSync(projectDir)) {
|
|
21
|
+
spinner.fail(`Directory ${projectName} already exists`);
|
|
25
22
|
process.exit(1);
|
|
26
23
|
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
24
|
+
fs.ensureDirSync(projectDir);
|
|
25
|
+
fs.copySync(templateDir, projectDir);
|
|
26
|
+
const packageJsonPath = path.join(projectDir, 'package.json');
|
|
27
|
+
const packageJson = fs.readJsonSync(packageJsonPath);
|
|
31
28
|
packageJson.name = projectName;
|
|
32
|
-
|
|
33
|
-
spinner.succeed(
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
29
|
+
fs.writeJsonSync(packageJsonPath, packageJson, { spaces: 2 });
|
|
30
|
+
spinner.succeed(`Initialized Dain project: ${projectName}`);
|
|
31
|
+
logSuccess(`Project created at ${projectDir}`);
|
|
32
|
+
logInfo('Run the following commands to get started:');
|
|
33
|
+
logInfo(` cd ${projectName}`);
|
|
34
|
+
logInfo(' npm install');
|
|
35
|
+
logInfo(' npm run dev');
|
|
39
36
|
process.exit(0);
|
|
40
37
|
}
|
|
41
38
|
catch (error) {
|
|
42
39
|
spinner.fail('Project initialization failed');
|
|
43
|
-
|
|
40
|
+
logError('Error initializing project', error);
|
|
44
41
|
process.exit(1);
|
|
45
42
|
}
|
|
46
43
|
}
|