@igoruehara/canvas-flow 0.1.11 → 0.1.12
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/bin/canvas-flow.js +120 -41
- package/package.json +1 -1
package/bin/canvas-flow.js
CHANGED
|
@@ -835,6 +835,70 @@ function sleep(ms) {
|
|
|
835
835
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
836
836
|
}
|
|
837
837
|
|
|
838
|
+
function createStartupProgress() {
|
|
839
|
+
const frames = ['-', '\\', '|', '/'];
|
|
840
|
+
const useTty = Boolean(process.stdout.isTTY && !process.env.CI);
|
|
841
|
+
let frameIndex = 0;
|
|
842
|
+
let lastLength = 0;
|
|
843
|
+
let interval;
|
|
844
|
+
let percent = 0;
|
|
845
|
+
let message = 'starting';
|
|
846
|
+
|
|
847
|
+
const line = () => `${frames[frameIndex]} Canvas Flow startup ${String(percent).padStart(3, ' ')}% - ${message}`;
|
|
848
|
+
const clearLine = () => {
|
|
849
|
+
if (!useTty || !lastLength) return;
|
|
850
|
+
process.stdout.write(`\r${' '.repeat(lastLength)}\r`);
|
|
851
|
+
lastLength = 0;
|
|
852
|
+
};
|
|
853
|
+
const render = () => {
|
|
854
|
+
if (!useTty) return;
|
|
855
|
+
const text = line();
|
|
856
|
+
const padded = text.padEnd(lastLength, ' ');
|
|
857
|
+
lastLength = Math.max(lastLength, text.length);
|
|
858
|
+
process.stdout.write(`\r${padded}`);
|
|
859
|
+
};
|
|
860
|
+
const ensureInterval = () => {
|
|
861
|
+
if (!useTty || interval) return;
|
|
862
|
+
interval = setInterval(() => {
|
|
863
|
+
frameIndex = (frameIndex + 1) % frames.length;
|
|
864
|
+
render();
|
|
865
|
+
}, 140);
|
|
866
|
+
if (typeof interval.unref === 'function') interval.unref();
|
|
867
|
+
};
|
|
868
|
+
const stopInterval = () => {
|
|
869
|
+
if (!interval) return;
|
|
870
|
+
clearInterval(interval);
|
|
871
|
+
interval = undefined;
|
|
872
|
+
};
|
|
873
|
+
|
|
874
|
+
return {
|
|
875
|
+
update(nextPercent, nextMessage) {
|
|
876
|
+
percent = Math.max(percent, Math.min(99, Number(nextPercent) || percent));
|
|
877
|
+
message = nextMessage || message;
|
|
878
|
+
ensureInterval();
|
|
879
|
+
if (useTty) render();
|
|
880
|
+
else console.log(`Canvas Flow startup ${percent}% - ${message}`);
|
|
881
|
+
},
|
|
882
|
+
log(text) {
|
|
883
|
+
clearLine();
|
|
884
|
+
console.log(text);
|
|
885
|
+
render();
|
|
886
|
+
},
|
|
887
|
+
done(text) {
|
|
888
|
+
percent = 100;
|
|
889
|
+
message = 'ready';
|
|
890
|
+
stopInterval();
|
|
891
|
+
clearLine();
|
|
892
|
+
console.log(text || 'Canvas Flow ready (100%)');
|
|
893
|
+
},
|
|
894
|
+
fail(text) {
|
|
895
|
+
stopInterval();
|
|
896
|
+
clearLine();
|
|
897
|
+
if (text) console.log(text);
|
|
898
|
+
},
|
|
899
|
+
};
|
|
900
|
+
}
|
|
901
|
+
|
|
838
902
|
async function checkHttpOk(url, timeoutMs = 1200) {
|
|
839
903
|
const controller = new AbortController();
|
|
840
904
|
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
@@ -851,37 +915,37 @@ async function checkHttpOk(url, timeoutMs = 1200) {
|
|
|
851
915
|
function startStartupStatus(publicUrl, options = {}) {
|
|
852
916
|
const healthUrl = `${String(publicUrl || '').replace(/\/$/, '')}/health`;
|
|
853
917
|
const startedAt = Date.now();
|
|
918
|
+
const progress = options.progress || createStartupProgress();
|
|
854
919
|
let stopped = false;
|
|
855
|
-
|
|
856
|
-
console.log('Canvas Flow startup: loading application bundle...');
|
|
857
|
-
console.log(`Canvas Flow startup: waiting for backend health at ${healthUrl}`);
|
|
858
|
-
|
|
859
|
-
const interval = setInterval(() => {
|
|
860
|
-
const elapsedSeconds = Math.max(1, Math.round((Date.now() - startedAt) / 1000));
|
|
861
|
-
console.log(`Canvas Flow startup: still starting (${elapsedSeconds}s elapsed)...`);
|
|
862
|
-
}, 2500);
|
|
920
|
+
let nextHealthLogAt = 0;
|
|
863
921
|
|
|
864
922
|
const stop = () => {
|
|
865
923
|
if (stopped) return;
|
|
866
924
|
stopped = true;
|
|
867
|
-
clearInterval(interval);
|
|
868
925
|
};
|
|
869
926
|
|
|
870
927
|
void (async () => {
|
|
871
928
|
const deadline = Date.now() + 90000;
|
|
929
|
+
progress.update(90, `waiting for backend health at ${healthUrl}`);
|
|
872
930
|
while (!stopped && Date.now() < deadline) {
|
|
873
931
|
if (await checkHttpOk(healthUrl)) {
|
|
874
932
|
stop();
|
|
875
|
-
|
|
933
|
+
progress.done(`Canvas Flow ready (100%): ${publicUrl}`);
|
|
876
934
|
if (options.openBrowser) openBrowser(publicUrl);
|
|
877
935
|
return;
|
|
878
936
|
}
|
|
937
|
+
const elapsedSeconds = Math.max(1, Math.round((Date.now() - startedAt) / 1000));
|
|
938
|
+
if (elapsedSeconds >= nextHealthLogAt) {
|
|
939
|
+
const nextPercent = Math.min(98, 90 + Math.floor(elapsedSeconds / 15));
|
|
940
|
+
progress.update(nextPercent, `waiting for backend health (${elapsedSeconds}s elapsed)`);
|
|
941
|
+
nextHealthLogAt = elapsedSeconds + 3;
|
|
942
|
+
}
|
|
879
943
|
await sleep(500);
|
|
880
944
|
}
|
|
881
945
|
|
|
882
946
|
if (!stopped) {
|
|
883
947
|
stop();
|
|
884
|
-
|
|
948
|
+
progress.fail('Canvas Flow startup: health check is still pending. Keep this terminal open and watch the backend logs above.');
|
|
885
949
|
}
|
|
886
950
|
})();
|
|
887
951
|
|
|
@@ -1161,8 +1225,11 @@ async function doctor(flags) {
|
|
|
1161
1225
|
reporter.finish();
|
|
1162
1226
|
}
|
|
1163
1227
|
|
|
1164
|
-
async function waitForMongo(config, flags, paths) {
|
|
1165
|
-
if (flags['skip-mongo-check'] === true)
|
|
1228
|
+
async function waitForMongo(config, flags, paths, progress) {
|
|
1229
|
+
if (flags['skip-mongo-check'] === true) {
|
|
1230
|
+
if (progress) progress.update(45, 'MongoDB preflight skipped');
|
|
1231
|
+
return;
|
|
1232
|
+
}
|
|
1166
1233
|
if (!config.database.mongoUrl) {
|
|
1167
1234
|
throw new Error(`database.mongoUrl is required. Edit the config with: canvas-flow config --edit`);
|
|
1168
1235
|
}
|
|
@@ -1174,15 +1241,18 @@ async function waitForMongo(config, flags, paths) {
|
|
|
1174
1241
|
let lastMessage = '';
|
|
1175
1242
|
|
|
1176
1243
|
for (let attempt = 1; attempt <= attempts; attempt += 1) {
|
|
1244
|
+
if (progress) progress.update(30, `checking MongoDB (${attempt}/${attempts})`);
|
|
1177
1245
|
const result = await checkMongoConnection(config.database.mongoUrl, options);
|
|
1178
1246
|
if (result.ok) {
|
|
1179
|
-
|
|
1247
|
+
if (progress) progress.update(55, 'MongoDB connected');
|
|
1248
|
+
else console.log('MongoDB preflight: connected');
|
|
1180
1249
|
return;
|
|
1181
1250
|
}
|
|
1182
1251
|
|
|
1183
1252
|
lastMessage = result.message;
|
|
1184
1253
|
if (attempt < attempts) {
|
|
1185
|
-
|
|
1254
|
+
if (progress) progress.update(35, `waiting for MongoDB (${attempt}/${attempts})`);
|
|
1255
|
+
else console.log(`MongoDB preflight waiting (${attempt}/${attempts}): ${result.message}`);
|
|
1186
1256
|
await sleep(1500);
|
|
1187
1257
|
}
|
|
1188
1258
|
}
|
|
@@ -1196,35 +1266,44 @@ async function waitForMongo(config, flags, paths) {
|
|
|
1196
1266
|
}
|
|
1197
1267
|
|
|
1198
1268
|
async function start(flags) {
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
if (flags['with-docker'] === true || flags.infra === true) {
|
|
1202
|
-
infra('up', flags);
|
|
1203
|
-
}
|
|
1204
|
-
const paths = resolvePaths(flags);
|
|
1205
|
-
ensureDir(paths.homeDir);
|
|
1206
|
-
const configExisted = fs.existsSync(paths.configPath);
|
|
1207
|
-
const config = loadConfig(paths.configPath);
|
|
1208
|
-
const runtime = applyEnvironment(config, paths, flags);
|
|
1209
|
-
await waitForMongo(config, flags, paths);
|
|
1210
|
-
|
|
1211
|
-
process.chdir(paths.homeDir);
|
|
1212
|
-
|
|
1213
|
-
console.log(`Canvas Flow config: ${paths.configPath}`);
|
|
1214
|
-
console.log(`Canvas Flow home: ${paths.homeDir}`);
|
|
1215
|
-
console.log(`Canvas Flow URL: ${runtime.publicUrl}`);
|
|
1216
|
-
if (!configExisted) {
|
|
1217
|
-
console.log('Created the default config.json.');
|
|
1218
|
-
console.log('Edit it with: canvas-flow config --edit');
|
|
1219
|
-
console.log('Show it with: canvas-flow config --show');
|
|
1220
|
-
}
|
|
1221
|
-
|
|
1222
|
-
const shouldOpen = flags.open === true || (flags.open !== false && config.server.openBrowser === true);
|
|
1223
|
-
const startupStatus = startStartupStatus(runtime.publicUrl, { openBrowser: shouldOpen });
|
|
1269
|
+
const progress = createStartupProgress();
|
|
1270
|
+
let startupStatus;
|
|
1224
1271
|
try {
|
|
1272
|
+
progress.update(5, 'checking package bundle');
|
|
1273
|
+
assertBundleExists();
|
|
1274
|
+
progress.update(10, 'loading runtime dependencies');
|
|
1275
|
+
addSourceDependencyFallback();
|
|
1276
|
+
if (flags['with-docker'] === true || flags.infra === true) {
|
|
1277
|
+
progress.log('Canvas Flow startup: starting Docker infrastructure...');
|
|
1278
|
+
infra('up', flags);
|
|
1279
|
+
}
|
|
1280
|
+
progress.update(15, 'loading config');
|
|
1281
|
+
const paths = resolvePaths(flags);
|
|
1282
|
+
ensureDir(paths.homeDir);
|
|
1283
|
+
const configExisted = fs.existsSync(paths.configPath);
|
|
1284
|
+
const config = loadConfig(paths.configPath);
|
|
1285
|
+
progress.update(25, 'applying environment');
|
|
1286
|
+
const runtime = applyEnvironment(config, paths, flags);
|
|
1287
|
+
await waitForMongo(config, flags, paths, progress);
|
|
1288
|
+
|
|
1289
|
+
process.chdir(paths.homeDir);
|
|
1290
|
+
|
|
1291
|
+
progress.log(`Canvas Flow config: ${paths.configPath}`);
|
|
1292
|
+
progress.log(`Canvas Flow home: ${paths.homeDir}`);
|
|
1293
|
+
progress.log(`Canvas Flow URL: ${runtime.publicUrl}`);
|
|
1294
|
+
if (!configExisted) {
|
|
1295
|
+
progress.log('Created the default config.json.');
|
|
1296
|
+
progress.log('Edit it with: canvas-flow config --edit');
|
|
1297
|
+
progress.log('Show it with: canvas-flow config --show');
|
|
1298
|
+
}
|
|
1299
|
+
|
|
1300
|
+
const shouldOpen = flags.open === true || (flags.open !== false && config.server.openBrowser === true);
|
|
1301
|
+
progress.update(75, 'starting Canvas Flow API');
|
|
1302
|
+
startupStatus = startStartupStatus(runtime.publicUrl, { openBrowser: shouldOpen, progress });
|
|
1225
1303
|
require(SERVER_ENTRY);
|
|
1226
1304
|
} catch (error) {
|
|
1227
|
-
startupStatus.stop();
|
|
1305
|
+
if (startupStatus) startupStatus.stop();
|
|
1306
|
+
progress.fail('Canvas Flow startup failed.');
|
|
1228
1307
|
throw error;
|
|
1229
1308
|
}
|
|
1230
1309
|
}
|
package/package.json
CHANGED