@valbuild/cli 0.93.0 → 0.95.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cli/dist/valbuild-cli-cli.cjs.dev.js +415 -160
- package/cli/dist/valbuild-cli-cli.cjs.prod.js +415 -160
- package/cli/dist/valbuild-cli-cli.esm.js +413 -158
- package/package.json +4 -4
- package/src/__fixtures__/basic/content/basic-errors.val.ts +7 -0
- package/src/__fixtures__/basic/content/basic-files.val.ts +14 -0
- package/src/__fixtures__/basic/content/basic-gallery-2.val.ts +17 -0
- package/src/__fixtures__/basic/content/basic-gallery-fail-on-non-unique-dir.val.ts +17 -0
- package/src/__fixtures__/basic/content/basic-gallery-missing-tracked.val.ts +17 -0
- package/src/__fixtures__/basic/content/basic-gallery-wrong-metadata.val.ts +17 -0
- package/src/__fixtures__/basic/content/basic-gallery.val.ts +18 -0
- package/src/__fixtures__/basic/content/basic-image-from-galleries.val.ts +15 -0
- package/src/__fixtures__/basic/content/basic-image-from-gallery.val.ts +12 -0
- package/src/__fixtures__/basic/content/basic-image.val.ts +7 -0
- package/src/__fixtures__/basic/content/basic-valid.val.ts +7 -0
- package/src/__fixtures__/basic/public/val/files/tracked.txt +1 -0
- package/src/__fixtures__/basic/public/val/files/untracked.txt +1 -0
- package/src/__fixtures__/basic/public/val/image.png +0 -0
- package/src/__fixtures__/basic/public/val/images/image.png +0 -0
- package/src/__fixtures__/basic/public/val/images2/image.png +0 -0
- package/src/__fixtures__/basic/public/val/images3/image.png +0 -0
- package/src/__fixtures__/basic/tsconfig.json +12 -0
- package/src/__fixtures__/basic/val.config.ts +5 -0
- package/src/runValidation.test.ts +386 -0
- package/src/runValidation.ts +1096 -0
- package/src/validate.ts +131 -887
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
import meow from 'meow';
|
|
2
2
|
import chalk from 'chalk';
|
|
3
3
|
import path from 'path';
|
|
4
|
-
import
|
|
5
|
-
import { DEFAULT_VAL_REMOTE_HOST, DEFAULT_CONTENT_HOST, Internal, FILE_REF_PROP, VAL_EXTENSION } from '@valbuild/core';
|
|
6
|
-
import { filterRoutesByPatterns, validateRoutePatterns } from '@valbuild/shared/internal';
|
|
7
|
-
import { glob } from 'fast-glob';
|
|
8
|
-
import picocolors from 'picocolors';
|
|
4
|
+
import pc from 'picocolors';
|
|
9
5
|
import fs from 'fs/promises';
|
|
6
|
+
import { glob } from 'fast-glob';
|
|
7
|
+
import { Internal, FILE_REF_PROP, DEFAULT_VAL_REMOTE_HOST, DEFAULT_CONTENT_HOST, VAL_EXTENSION } from '@valbuild/core';
|
|
8
|
+
import { createService, createFixPatch, getPersonalAccessTokenPath, parsePersonalAccessTokenFile, getSettings, uploadRemoteFile } from '@valbuild/server';
|
|
10
9
|
import vm from 'node:vm';
|
|
11
10
|
import ts from 'typescript';
|
|
12
11
|
import z from 'zod';
|
|
13
12
|
import { createRequire } from 'node:module';
|
|
14
|
-
import
|
|
13
|
+
import { filterRoutesByPatterns, validateRoutePatterns } from '@valbuild/shared/internal';
|
|
14
|
+
import nodeFs from 'fs';
|
|
15
15
|
|
|
16
16
|
function error(message) {
|
|
17
17
|
console.error(chalk.red("❌Error: ") + message);
|
|
@@ -98,6 +98,7 @@ const textEncoder = new TextEncoder();
|
|
|
98
98
|
|
|
99
99
|
// Handler functions
|
|
100
100
|
async function handleFileMetadata(ctx) {
|
|
101
|
+
var _fileSource$source;
|
|
101
102
|
const [, modulePath] = Internal.splitModuleFilePathAndModulePath(ctx.sourcePath);
|
|
102
103
|
if (!ctx.valModule.source || !ctx.valModule.schema) {
|
|
103
104
|
return {
|
|
@@ -106,24 +107,21 @@ async function handleFileMetadata(ctx) {
|
|
|
106
107
|
};
|
|
107
108
|
}
|
|
108
109
|
const fileSource = Internal.resolvePath(modulePath, ctx.valModule.source, ctx.valModule.schema);
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
errorMessage: `Expected file to be defined at: ${ctx.sourcePath} but no file was found`
|
|
125
|
-
};
|
|
126
|
-
}
|
|
110
|
+
|
|
111
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
112
|
+
const fileRefProp = (_fileSource$source = fileSource.source) === null || _fileSource$source === void 0 ? void 0 : _fileSource$source[FILE_REF_PROP];
|
|
113
|
+
if (!fileRefProp) {
|
|
114
|
+
return {
|
|
115
|
+
success: false,
|
|
116
|
+
errorMessage: `Expected file to be defined at: ${ctx.sourcePath} but no file was found`
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
const filePath = path.join(ctx.projectRoot, fileRefProp);
|
|
120
|
+
if (!ctx.fs.fileExists(filePath)) {
|
|
121
|
+
return {
|
|
122
|
+
success: false,
|
|
123
|
+
errorMessage: `File ${filePath} does not exist`
|
|
124
|
+
};
|
|
127
125
|
}
|
|
128
126
|
return {
|
|
129
127
|
success: true,
|
|
@@ -165,7 +163,7 @@ async function handleKeyOfCheck(ctx) {
|
|
|
165
163
|
};
|
|
166
164
|
}
|
|
167
165
|
async function handleRemoteFileUpload(ctx) {
|
|
168
|
-
var
|
|
166
|
+
var _resolvedRemoteFileAt;
|
|
169
167
|
if (!ctx.fix) {
|
|
170
168
|
return {
|
|
171
169
|
success: false,
|
|
@@ -180,35 +178,37 @@ async function handleRemoteFileUpload(ctx) {
|
|
|
180
178
|
};
|
|
181
179
|
}
|
|
182
180
|
const resolvedRemoteFileAtSourcePath = Internal.resolvePath(modulePath, ctx.valModule.source, ctx.valModule.schema);
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
errorMessage: `Expected file to be defined at: ${ctx.sourcePath} but no file was found`
|
|
199
|
-
};
|
|
200
|
-
}
|
|
181
|
+
|
|
182
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
183
|
+
const fileRefProp = (_resolvedRemoteFileAt = resolvedRemoteFileAtSourcePath.source) === null || _resolvedRemoteFileAt === void 0 ? void 0 : _resolvedRemoteFileAt[FILE_REF_PROP];
|
|
184
|
+
if (!fileRefProp) {
|
|
185
|
+
return {
|
|
186
|
+
success: false,
|
|
187
|
+
errorMessage: `Expected file to be defined at: ${ctx.sourcePath} but no file was found`
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
const filePath = path.join(ctx.projectRoot, fileRefProp);
|
|
191
|
+
if (!ctx.fs.fileExists(filePath)) {
|
|
192
|
+
return {
|
|
193
|
+
success: false,
|
|
194
|
+
errorMessage: `File ${filePath} does not exist`
|
|
195
|
+
};
|
|
201
196
|
}
|
|
202
197
|
const patFile = getPersonalAccessTokenPath(ctx.projectRoot);
|
|
203
|
-
|
|
204
|
-
await fs.access(patFile);
|
|
205
|
-
} catch {
|
|
198
|
+
if (!ctx.fs.fileExists(patFile)) {
|
|
206
199
|
return {
|
|
207
200
|
success: false,
|
|
208
201
|
errorMessage: `File: ${path.join(ctx.projectRoot, ctx.file)} has remote images that are not uploaded and you are not logged in.\n\nFix this error by logging in:\n\t"npx val login"\n`
|
|
209
202
|
};
|
|
210
203
|
}
|
|
211
|
-
const
|
|
204
|
+
const patFileContent = ctx.fs.readFile(patFile);
|
|
205
|
+
if (patFileContent === undefined) {
|
|
206
|
+
return {
|
|
207
|
+
success: false,
|
|
208
|
+
errorMessage: `Could not read personal access token file at ${patFile}`
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
const parsedPatFile = parsePersonalAccessTokenFile(patFileContent);
|
|
212
212
|
if (!parsedPatFile.success) {
|
|
213
213
|
return {
|
|
214
214
|
success: false,
|
|
@@ -219,9 +219,12 @@ async function handleRemoteFileUpload(ctx) {
|
|
|
219
219
|
pat
|
|
220
220
|
} = parsedPatFile.data;
|
|
221
221
|
if (ctx.remoteFiles[ctx.sourcePath]) {
|
|
222
|
-
console.log(picocolors.yellow("⚠"), `Remote file ${filePath} already uploaded`);
|
|
223
222
|
return {
|
|
224
|
-
success: true
|
|
223
|
+
success: true,
|
|
224
|
+
events: [{
|
|
225
|
+
type: "remote-already-uploaded",
|
|
226
|
+
filePath
|
|
227
|
+
}]
|
|
225
228
|
};
|
|
226
229
|
}
|
|
227
230
|
if (!resolvedRemoteFileAtSourcePath.schema) {
|
|
@@ -239,22 +242,18 @@ async function handleRemoteFileUpload(ctx) {
|
|
|
239
242
|
errorMessage: `Could not resolve schema for remote file: ${ctx.sourcePath}`
|
|
240
243
|
};
|
|
241
244
|
}
|
|
245
|
+
const projectName = ctx.project;
|
|
242
246
|
let publicProjectId = ctx.publicProjectId;
|
|
243
247
|
let remoteFileBuckets = ctx.remoteFileBuckets;
|
|
244
248
|
let remoteFilesCounter = ctx.remoteFilesCounter;
|
|
245
249
|
if (!publicProjectId || !remoteFileBuckets) {
|
|
246
|
-
let projectName = process.env.VAL_PROJECT;
|
|
247
|
-
if (!projectName) {
|
|
248
|
-
var _ctx$valConfigFile;
|
|
249
|
-
projectName = (_ctx$valConfigFile = ctx.valConfigFile) === null || _ctx$valConfigFile === void 0 ? void 0 : _ctx$valConfigFile.project;
|
|
250
|
-
}
|
|
251
250
|
if (!projectName) {
|
|
252
251
|
return {
|
|
253
252
|
success: false,
|
|
254
|
-
errorMessage: "Project name not found.
|
|
253
|
+
errorMessage: "Project name not found. Add project name to val.config or set the VAL_PROJECT environment variable"
|
|
255
254
|
};
|
|
256
255
|
}
|
|
257
|
-
const settingsRes = await getSettings(projectName, {
|
|
256
|
+
const settingsRes = await ctx.remote.getSettings(projectName, {
|
|
258
257
|
pat
|
|
259
258
|
});
|
|
260
259
|
if (!settingsRes.success) {
|
|
@@ -272,7 +271,7 @@ async function handleRemoteFileUpload(ctx) {
|
|
|
272
271
|
errorMessage: "Could not get public project id"
|
|
273
272
|
};
|
|
274
273
|
}
|
|
275
|
-
if (!
|
|
274
|
+
if (!projectName) {
|
|
276
275
|
return {
|
|
277
276
|
success: false,
|
|
278
277
|
errorMessage: `Could not get project. Check that your val.config has the 'project' field set, or set it using the VAL_PROJECT environment variable`
|
|
@@ -292,13 +291,11 @@ async function handleRemoteFileUpload(ctx) {
|
|
|
292
291
|
errorMessage: `Internal error: could not allocate a bucket for the remote file located at ${ctx.sourcePath}`
|
|
293
292
|
};
|
|
294
293
|
}
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
fileBuffer = await fs.readFile(filePath);
|
|
298
|
-
} catch (e) {
|
|
294
|
+
const fileBuffer = ctx.fs.readBuffer(filePath);
|
|
295
|
+
if (fileBuffer === undefined) {
|
|
299
296
|
return {
|
|
300
297
|
success: false,
|
|
301
|
-
errorMessage: `Error reading file: ${
|
|
298
|
+
errorMessage: `Error reading file: ${filePath}`
|
|
302
299
|
};
|
|
303
300
|
}
|
|
304
301
|
const relativeFilePath = path.relative(ctx.projectRoot, filePath).split(path.sep).join("/");
|
|
@@ -313,7 +310,7 @@ async function handleRemoteFileUpload(ctx) {
|
|
|
313
310
|
const fileExt = getFileExt(filePath);
|
|
314
311
|
const schema = resolveRemoteFileSchema;
|
|
315
312
|
const metadata = fileSourceMetadata;
|
|
316
|
-
const ref = Internal.remote.createRemoteRef(ctx.
|
|
313
|
+
const ref = Internal.remote.createRemoteRef(ctx.remote.remoteHost, {
|
|
317
314
|
publicProjectId,
|
|
318
315
|
coreVersion,
|
|
319
316
|
bucket,
|
|
@@ -321,8 +318,7 @@ async function handleRemoteFileUpload(ctx) {
|
|
|
321
318
|
fileHash,
|
|
322
319
|
filePath: relativeFilePath
|
|
323
320
|
});
|
|
324
|
-
|
|
325
|
-
const remoteFileUpload = await uploadRemoteFile(ctx.contentHostUrl, ctx.valConfigFile.project, bucket, fileHash, fileExt, fileBuffer, {
|
|
321
|
+
const remoteFileUpload = await ctx.remote.uploadFile(projectName, bucket, fileHash, fileExt, fileBuffer, {
|
|
326
322
|
pat
|
|
327
323
|
});
|
|
328
324
|
if (!remoteFileUpload.success) {
|
|
@@ -331,7 +327,6 @@ async function handleRemoteFileUpload(ctx) {
|
|
|
331
327
|
errorMessage: `Could not upload remote file: '${ref}'. Error: ${remoteFileUpload.error}`
|
|
332
328
|
};
|
|
333
329
|
}
|
|
334
|
-
console.log(picocolors.green("✔"), `Completed upload of remote file: '${ref}'`);
|
|
335
330
|
ctx.remoteFiles[ctx.sourcePath] = {
|
|
336
331
|
ref,
|
|
337
332
|
metadata: fileSourceMetadata
|
|
@@ -341,15 +336,25 @@ async function handleRemoteFileUpload(ctx) {
|
|
|
341
336
|
shouldApplyPatch: true,
|
|
342
337
|
publicProjectId,
|
|
343
338
|
remoteFileBuckets,
|
|
344
|
-
remoteFilesCounter
|
|
339
|
+
remoteFilesCounter,
|
|
340
|
+
events: [{
|
|
341
|
+
type: "remote-uploading",
|
|
342
|
+
ref
|
|
343
|
+
}, {
|
|
344
|
+
type: "remote-uploaded",
|
|
345
|
+
ref
|
|
346
|
+
}]
|
|
345
347
|
};
|
|
346
348
|
}
|
|
347
349
|
async function handleRemoteFileDownload(ctx) {
|
|
348
350
|
if (ctx.fix) {
|
|
349
|
-
console.log(picocolors.yellow("⚠"), `Downloading remote file in ${ctx.sourcePath}...`);
|
|
350
351
|
return {
|
|
351
352
|
success: true,
|
|
352
|
-
shouldApplyPatch: true
|
|
353
|
+
shouldApplyPatch: true,
|
|
354
|
+
events: [{
|
|
355
|
+
type: "remote-downloading",
|
|
356
|
+
sourcePath: ctx.sourcePath
|
|
357
|
+
}]
|
|
353
358
|
};
|
|
354
359
|
} else {
|
|
355
360
|
return {
|
|
@@ -514,6 +519,103 @@ async function handleRouteCheck(ctx) {
|
|
|
514
519
|
success: true
|
|
515
520
|
};
|
|
516
521
|
}
|
|
522
|
+
async function handleUniqueFolderCheck(ctx) {
|
|
523
|
+
const value = ctx.validationError.value;
|
|
524
|
+
if (!value || typeof value.directory !== "string") {
|
|
525
|
+
return {
|
|
526
|
+
success: false,
|
|
527
|
+
errorMessage: `Unexpected value in unique folder check for ${ctx.sourcePath}`
|
|
528
|
+
};
|
|
529
|
+
}
|
|
530
|
+
const {
|
|
531
|
+
directory
|
|
532
|
+
} = value;
|
|
533
|
+
const conflicts = [];
|
|
534
|
+
for (const file of ctx.valFiles) {
|
|
535
|
+
const otherModuleFilePath = `/${file}`;
|
|
536
|
+
if (otherModuleFilePath === ctx.moduleFilePath) continue;
|
|
537
|
+
const otherModule = await ctx.service.get(otherModuleFilePath, "", {
|
|
538
|
+
source: false,
|
|
539
|
+
schema: true,
|
|
540
|
+
validate: false
|
|
541
|
+
});
|
|
542
|
+
const schema = otherModule.schema;
|
|
543
|
+
if ((schema === null || schema === void 0 ? void 0 : schema.type) === "record" && schema.directory === directory && schema.mediaType) {
|
|
544
|
+
conflicts.push(otherModuleFilePath);
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
if (conflicts.length > 0) {
|
|
548
|
+
return {
|
|
549
|
+
success: false,
|
|
550
|
+
errorMessage: `Gallery directory '${directory}' in ${ctx.moduleFilePath} is also used by: ${conflicts.join(", ")}. Each gallery must use a unique directory.`
|
|
551
|
+
};
|
|
552
|
+
}
|
|
553
|
+
return {
|
|
554
|
+
success: true
|
|
555
|
+
};
|
|
556
|
+
}
|
|
557
|
+
async function handleCheckAllFiles(ctx) {
|
|
558
|
+
const value = ctx.validationError.value;
|
|
559
|
+
if (!value || typeof value.directory !== "string") {
|
|
560
|
+
return {
|
|
561
|
+
success: false,
|
|
562
|
+
errorMessage: `Unexpected value in check-all-files for ${ctx.sourcePath}`
|
|
563
|
+
};
|
|
564
|
+
}
|
|
565
|
+
const {
|
|
566
|
+
directory
|
|
567
|
+
} = value;
|
|
568
|
+
const source = ctx.valModule.source;
|
|
569
|
+
if (!source || typeof source !== "object" || Array.isArray(source)) {
|
|
570
|
+
return {
|
|
571
|
+
success: false,
|
|
572
|
+
errorMessage: `Could not get source for ${ctx.sourcePath}`
|
|
573
|
+
};
|
|
574
|
+
}
|
|
575
|
+
const trackedFiles = new Set(Object.keys(source));
|
|
576
|
+
|
|
577
|
+
// Check that all tracked files exist on disk
|
|
578
|
+
const missingTrackedFiles = [...trackedFiles].filter(f => {
|
|
579
|
+
return !ctx.fs.fileExists(path.join(ctx.projectRoot, f));
|
|
580
|
+
});
|
|
581
|
+
if (missingTrackedFiles.length > 0) {
|
|
582
|
+
if (!ctx.fix) {
|
|
583
|
+
return {
|
|
584
|
+
success: false,
|
|
585
|
+
errorMessage: `Gallery in ${ctx.moduleFilePath} has tracked files that do not exist on disk: ${missingTrackedFiles.join(", ")}. Add the files or remove them from the gallery.`
|
|
586
|
+
};
|
|
587
|
+
}
|
|
588
|
+
// fix: true — let createFixPatch remove the missing entries
|
|
589
|
+
return {
|
|
590
|
+
success: true,
|
|
591
|
+
shouldApplyPatch: true
|
|
592
|
+
};
|
|
593
|
+
}
|
|
594
|
+
const dirPath = path.join(ctx.projectRoot, directory);
|
|
595
|
+
const filesInDir = [];
|
|
596
|
+
try {
|
|
597
|
+
const entries = ctx.fs.readDirectory(dirPath, undefined, undefined, ["**/*"]);
|
|
598
|
+
for (const entry of entries) {
|
|
599
|
+
const relPath = "/" + path.relative(ctx.projectRoot, entry).split(path.sep).join("/");
|
|
600
|
+
filesInDir.push(relPath);
|
|
601
|
+
}
|
|
602
|
+
} catch {
|
|
603
|
+
// directory doesn't exist — no untracked files possible
|
|
604
|
+
}
|
|
605
|
+
const untrackedFiles = filesInDir.filter(f => !trackedFiles.has(f));
|
|
606
|
+
if (untrackedFiles.length > 0) {
|
|
607
|
+
return {
|
|
608
|
+
success: false,
|
|
609
|
+
errorMessage: `Gallery in ${ctx.moduleFilePath} has files not tracked: ${untrackedFiles.join(", ")}. Add these files to the gallery or remove them from the directory.`
|
|
610
|
+
};
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
// All files accounted for — trigger metadata verification via createFixPatch
|
|
614
|
+
return {
|
|
615
|
+
success: true,
|
|
616
|
+
shouldApplyPatch: true
|
|
617
|
+
};
|
|
618
|
+
}
|
|
517
619
|
|
|
518
620
|
// Fix handler registry
|
|
519
621
|
const currentFixHandlers = {
|
|
@@ -528,7 +630,13 @@ const currentFixHandlers = {
|
|
|
528
630
|
"image:download-remote": handleRemoteFileDownload,
|
|
529
631
|
"file:download-remote": handleRemoteFileDownload,
|
|
530
632
|
"image:check-remote": handleRemoteFileCheck,
|
|
531
|
-
"
|
|
633
|
+
"images:check-remote": handleRemoteFileCheck,
|
|
634
|
+
"file:check-remote": handleRemoteFileCheck,
|
|
635
|
+
"files:check-remote": handleRemoteFileCheck,
|
|
636
|
+
"images:check-unique-folder": handleUniqueFolderCheck,
|
|
637
|
+
"files:check-unique-folder": handleUniqueFolderCheck,
|
|
638
|
+
"images:check-all-files": handleCheckAllFiles,
|
|
639
|
+
"files:check-all-files": handleCheckAllFiles
|
|
532
640
|
};
|
|
533
641
|
const deprecatedFixHandlers = {
|
|
534
642
|
"image:replace-metadata": handleFileMetadata
|
|
@@ -537,30 +645,36 @@ const fixHandlers = {
|
|
|
537
645
|
...deprecatedFixHandlers,
|
|
538
646
|
...currentFixHandlers
|
|
539
647
|
};
|
|
540
|
-
|
|
648
|
+
function createDefaultValFSHost() {
|
|
649
|
+
return {
|
|
650
|
+
...ts.sys,
|
|
651
|
+
writeFile: (fileName, data, encoding) => {
|
|
652
|
+
nodeFs.mkdirSync(path.dirname(fileName), {
|
|
653
|
+
recursive: true
|
|
654
|
+
});
|
|
655
|
+
nodeFs.writeFileSync(fileName, typeof data === "string" ? data : new Uint8Array(data), encoding);
|
|
656
|
+
},
|
|
657
|
+
rmFile: nodeFs.rmSync,
|
|
658
|
+
readBuffer: fileName => {
|
|
659
|
+
try {
|
|
660
|
+
return nodeFs.readFileSync(fileName);
|
|
661
|
+
} catch {
|
|
662
|
+
return undefined;
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
};
|
|
666
|
+
}
|
|
667
|
+
async function* runValidation({
|
|
541
668
|
root,
|
|
542
|
-
fix
|
|
669
|
+
fix,
|
|
670
|
+
valFiles,
|
|
671
|
+
project,
|
|
672
|
+
remote,
|
|
673
|
+
fs
|
|
543
674
|
}) {
|
|
544
|
-
const
|
|
545
|
-
const
|
|
546
|
-
const projectRoot = root ? path.resolve(root) : process.cwd();
|
|
547
|
-
const valConfigFile = (await evalValConfigFile(projectRoot, "val.config.ts")) || (await evalValConfigFile(projectRoot, "val.config.js"));
|
|
548
|
-
console.log(picocolors.greenBright(`Validating project${valConfigFile !== null && valConfigFile !== void 0 && valConfigFile.project ? ` '${picocolors.inverse(valConfigFile === null || valConfigFile === void 0 ? void 0 : valConfigFile.project)}'` : ""}...`));
|
|
549
|
-
const service = await createService(projectRoot, {});
|
|
550
|
-
let prettier;
|
|
551
|
-
try {
|
|
552
|
-
prettier = (await import('prettier')).default;
|
|
553
|
-
} catch {
|
|
554
|
-
console.log("Prettier not found, skipping formatting");
|
|
555
|
-
}
|
|
556
|
-
const valFiles = await glob("**/*.val.{js,ts}", {
|
|
557
|
-
ignore: ["node_modules/**"],
|
|
558
|
-
cwd: projectRoot
|
|
559
|
-
});
|
|
675
|
+
const projectRoot = path.resolve(root);
|
|
676
|
+
const service = await createService(projectRoot, {}, fs);
|
|
560
677
|
let errors = 0;
|
|
561
|
-
console.log(picocolors.greenBright(`Found ${valFiles.length} files...`));
|
|
562
|
-
let publicProjectId;
|
|
563
|
-
let didFix = false;
|
|
564
678
|
|
|
565
679
|
// Create caches that persist across all file validations
|
|
566
680
|
const keyOfCache = new Map();
|
|
@@ -568,7 +682,7 @@ async function validate({
|
|
|
568
682
|
loaded: false,
|
|
569
683
|
modules: {}
|
|
570
684
|
};
|
|
571
|
-
async function validateFile(file) {
|
|
685
|
+
async function* validateFile(file) {
|
|
572
686
|
const moduleFilePath = `/${file}`; // TODO: check if this always works? (Windows?)
|
|
573
687
|
const start = Date.now();
|
|
574
688
|
const valModule = await service.get(moduleFilePath, "", {
|
|
@@ -580,10 +694,14 @@ async function validate({
|
|
|
580
694
|
let remoteFileBuckets = undefined;
|
|
581
695
|
let remoteFilesCounter = 0;
|
|
582
696
|
if (!valModule.errors) {
|
|
583
|
-
|
|
584
|
-
|
|
697
|
+
yield {
|
|
698
|
+
type: "file-valid",
|
|
699
|
+
file: moduleFilePath,
|
|
700
|
+
durationMs: Date.now() - start
|
|
701
|
+
};
|
|
702
|
+
return;
|
|
585
703
|
} else {
|
|
586
|
-
let
|
|
704
|
+
let fileErrors = 0;
|
|
587
705
|
let fixedErrors = 0;
|
|
588
706
|
if (valModule.errors) {
|
|
589
707
|
if (valModule.errors.validation) {
|
|
@@ -591,8 +709,12 @@ async function validate({
|
|
|
591
709
|
for (const v of validationErrors) {
|
|
592
710
|
if (!v.fixes || v.fixes.length === 0) {
|
|
593
711
|
// No fixes available - just report error
|
|
594
|
-
|
|
595
|
-
|
|
712
|
+
fileErrors += 1;
|
|
713
|
+
yield {
|
|
714
|
+
type: "validation-error",
|
|
715
|
+
sourcePath,
|
|
716
|
+
message: v.message
|
|
717
|
+
};
|
|
596
718
|
continue;
|
|
597
719
|
}
|
|
598
720
|
|
|
@@ -600,8 +722,12 @@ async function validate({
|
|
|
600
722
|
const fixType = v.fixes[0]; // Take first fix
|
|
601
723
|
const handler = fixHandlers[fixType];
|
|
602
724
|
if (!handler) {
|
|
603
|
-
|
|
604
|
-
|
|
725
|
+
yield {
|
|
726
|
+
type: "unknown-fix",
|
|
727
|
+
sourcePath,
|
|
728
|
+
fixes: v.fixes
|
|
729
|
+
};
|
|
730
|
+
fileErrors += 1;
|
|
605
731
|
continue;
|
|
606
732
|
}
|
|
607
733
|
|
|
@@ -616,21 +742,25 @@ async function validate({
|
|
|
616
742
|
valFiles,
|
|
617
743
|
moduleFilePath,
|
|
618
744
|
file,
|
|
745
|
+
fs,
|
|
619
746
|
remoteFiles,
|
|
620
|
-
publicProjectId,
|
|
747
|
+
publicProjectId: undefined,
|
|
621
748
|
remoteFileBuckets,
|
|
622
749
|
remoteFilesCounter,
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
valConfigFile: valConfigFile ?? undefined,
|
|
750
|
+
remote,
|
|
751
|
+
project,
|
|
626
752
|
keyOfCache,
|
|
627
753
|
routerModulesCache
|
|
628
754
|
});
|
|
629
755
|
|
|
630
|
-
//
|
|
631
|
-
if (result.
|
|
632
|
-
|
|
756
|
+
// Yield any events from handler
|
|
757
|
+
if (result.events) {
|
|
758
|
+
for (const event of result.events) {
|
|
759
|
+
yield event;
|
|
760
|
+
}
|
|
633
761
|
}
|
|
762
|
+
|
|
763
|
+
// Update shared state from handler result
|
|
634
764
|
if (result.remoteFileBuckets !== undefined) {
|
|
635
765
|
remoteFileBuckets = result.remoteFileBuckets;
|
|
636
766
|
}
|
|
@@ -638,69 +768,98 @@ async function validate({
|
|
|
638
768
|
remoteFilesCounter = result.remoteFilesCounter;
|
|
639
769
|
}
|
|
640
770
|
if (!result.success) {
|
|
641
|
-
|
|
642
|
-
|
|
771
|
+
yield {
|
|
772
|
+
type: "validation-error",
|
|
773
|
+
sourcePath,
|
|
774
|
+
message: result.errorMessage ?? "Unknown error"
|
|
775
|
+
};
|
|
776
|
+
fileErrors += 1;
|
|
643
777
|
continue;
|
|
644
778
|
}
|
|
645
779
|
|
|
646
780
|
// Apply patch if needed
|
|
647
781
|
if (result.shouldApplyPatch) {
|
|
648
|
-
var _fixPatch$remainingEr;
|
|
649
782
|
const fixPatch = await createFixPatch({
|
|
650
783
|
projectRoot,
|
|
651
|
-
remoteHost:
|
|
784
|
+
remoteHost: remote.remoteHost
|
|
652
785
|
}, !!fix, sourcePath, v, remoteFiles, valModule.source, valModule.schema);
|
|
653
786
|
if (fix && fixPatch !== null && fixPatch !== void 0 && fixPatch.patch && (fixPatch === null || fixPatch === void 0 ? void 0 : fixPatch.patch.length) > 0) {
|
|
654
787
|
await service.patch(moduleFilePath, fixPatch.patch);
|
|
655
|
-
didFix = true;
|
|
656
788
|
fixedErrors += 1;
|
|
657
|
-
|
|
789
|
+
yield {
|
|
790
|
+
type: "fix-applied",
|
|
791
|
+
file,
|
|
792
|
+
sourcePath
|
|
793
|
+
};
|
|
794
|
+
} else if (!fix && fixPatch !== null && fixPatch !== void 0 && fixPatch.patch && (fixPatch === null || fixPatch === void 0 ? void 0 : fixPatch.patch.length) > 0) {
|
|
795
|
+
fileErrors += 1;
|
|
796
|
+
yield {
|
|
797
|
+
type: "validation-fixable-error",
|
|
798
|
+
sourcePath,
|
|
799
|
+
message: v.message,
|
|
800
|
+
fixable: true
|
|
801
|
+
};
|
|
802
|
+
}
|
|
803
|
+
for (const e of (fixPatch === null || fixPatch === void 0 ? void 0 : fixPatch.remainingErrors) ?? []) {
|
|
804
|
+
fileErrors += 1;
|
|
805
|
+
yield {
|
|
806
|
+
type: "validation-fixable-error",
|
|
807
|
+
sourcePath,
|
|
808
|
+
message: e.message,
|
|
809
|
+
fixable: !!(e.fixes && e.fixes.length)
|
|
810
|
+
};
|
|
658
811
|
}
|
|
659
|
-
fixPatch === null || fixPatch === void 0 || (_fixPatch$remainingEr = fixPatch.remainingErrors) === null || _fixPatch$remainingEr === void 0 || _fixPatch$remainingEr.forEach(e => {
|
|
660
|
-
errors += 1;
|
|
661
|
-
console.log(e.fixes && e.fixes.length ? picocolors.yellow("⚠") : picocolors.red("✘"), `Got ${e.fixes && e.fixes.length ? "fixable " : ""}error in`, `${sourcePath}:`, e.message);
|
|
662
|
-
});
|
|
663
812
|
}
|
|
664
813
|
}
|
|
665
814
|
}
|
|
666
815
|
}
|
|
667
|
-
if (fixedErrors ===
|
|
668
|
-
|
|
816
|
+
if (fixedErrors === fileErrors && (!valModule.errors.fatal || valModule.errors.fatal.length == 0)) {
|
|
817
|
+
yield {
|
|
818
|
+
type: "file-valid",
|
|
819
|
+
file: moduleFilePath,
|
|
820
|
+
durationMs: Date.now() - start
|
|
821
|
+
};
|
|
669
822
|
}
|
|
670
823
|
for (const fatalError of valModule.errors.fatal || []) {
|
|
671
|
-
|
|
672
|
-
|
|
824
|
+
fileErrors += 1;
|
|
825
|
+
yield {
|
|
826
|
+
type: "fatal-error",
|
|
827
|
+
file: moduleFilePath,
|
|
828
|
+
message: fatalError.message
|
|
829
|
+
};
|
|
673
830
|
}
|
|
674
831
|
} else {
|
|
675
|
-
|
|
832
|
+
yield {
|
|
833
|
+
type: "file-valid",
|
|
834
|
+
file: moduleFilePath,
|
|
835
|
+
durationMs: Date.now() - start
|
|
836
|
+
};
|
|
676
837
|
}
|
|
677
|
-
if (
|
|
678
|
-
|
|
838
|
+
if (fileErrors > 0) {
|
|
839
|
+
yield {
|
|
840
|
+
type: "file-error-count",
|
|
841
|
+
file: `/${file}`,
|
|
842
|
+
errorCount: fileErrors,
|
|
843
|
+
durationMs: Date.now() - start
|
|
844
|
+
};
|
|
679
845
|
}
|
|
680
|
-
|
|
846
|
+
errors += fileErrors;
|
|
681
847
|
}
|
|
682
848
|
}
|
|
683
849
|
for (const file of valFiles.sort()) {
|
|
684
|
-
|
|
685
|
-
errors += await validateFile(file);
|
|
686
|
-
if (prettier && didFix) {
|
|
687
|
-
var _prettier;
|
|
688
|
-
const filePath = path.join(projectRoot, file);
|
|
689
|
-
const fileContent = await fs.readFile(filePath, "utf-8");
|
|
690
|
-
const formattedContent = await ((_prettier = prettier) === null || _prettier === void 0 ? void 0 : _prettier.format(fileContent, {
|
|
691
|
-
filepath: filePath
|
|
692
|
-
}));
|
|
693
|
-
await fs.writeFile(filePath, formattedContent);
|
|
694
|
-
}
|
|
850
|
+
yield* validateFile(file);
|
|
695
851
|
}
|
|
852
|
+
service.dispose();
|
|
696
853
|
if (errors > 0) {
|
|
697
|
-
|
|
698
|
-
|
|
854
|
+
yield {
|
|
855
|
+
type: "summary-errors",
|
|
856
|
+
count: errors
|
|
857
|
+
};
|
|
699
858
|
} else {
|
|
700
|
-
|
|
859
|
+
yield {
|
|
860
|
+
type: "summary-success"
|
|
861
|
+
};
|
|
701
862
|
}
|
|
702
|
-
service.dispose();
|
|
703
|
-
return;
|
|
704
863
|
}
|
|
705
864
|
|
|
706
865
|
// GPT generated levenshtein distance algorithm:
|
|
@@ -728,6 +887,102 @@ function findSimilar(key, targets) {
|
|
|
728
887
|
})).sort((a, b) => a.distance - b.distance);
|
|
729
888
|
}
|
|
730
889
|
|
|
890
|
+
async function validate({
|
|
891
|
+
root,
|
|
892
|
+
fix
|
|
893
|
+
}) {
|
|
894
|
+
const projectRoot = root ? path.resolve(root) : process.cwd();
|
|
895
|
+
const valConfigFile = (await evalValConfigFile(projectRoot, "val.config.ts")) || (await evalValConfigFile(projectRoot, "val.config.js"));
|
|
896
|
+
const resolvedValConfigFile = valConfigFile ? {
|
|
897
|
+
...valConfigFile,
|
|
898
|
+
project: process.env.VAL_PROJECT || valConfigFile.project
|
|
899
|
+
} : process.env.VAL_PROJECT ? {
|
|
900
|
+
project: process.env.VAL_PROJECT
|
|
901
|
+
} : undefined;
|
|
902
|
+
console.log(pc.greenBright(`Validating project${resolvedValConfigFile !== null && resolvedValConfigFile !== void 0 && resolvedValConfigFile.project ? ` '${pc.inverse(resolvedValConfigFile.project)}'` : ""}...`));
|
|
903
|
+
const valFiles = await glob("**/*.val.{js,ts}", {
|
|
904
|
+
ignore: ["node_modules/**"],
|
|
905
|
+
cwd: projectRoot
|
|
906
|
+
});
|
|
907
|
+
console.log(pc.greenBright(`Found ${valFiles.length} files...`));
|
|
908
|
+
let prettier;
|
|
909
|
+
try {
|
|
910
|
+
prettier = (await import('prettier')).default;
|
|
911
|
+
} catch {
|
|
912
|
+
console.log("Prettier not found, skipping formatting");
|
|
913
|
+
}
|
|
914
|
+
const fixedFiles = new Set();
|
|
915
|
+
let totalErrors = 0;
|
|
916
|
+
for await (const event of runValidation({
|
|
917
|
+
root: projectRoot,
|
|
918
|
+
fix: !!fix,
|
|
919
|
+
valFiles,
|
|
920
|
+
project: resolvedValConfigFile === null || resolvedValConfigFile === void 0 ? void 0 : resolvedValConfigFile.project,
|
|
921
|
+
remote: {
|
|
922
|
+
remoteHost: process.env.VAL_REMOTE_HOST || DEFAULT_VAL_REMOTE_HOST,
|
|
923
|
+
getSettings: (projectName, options) => getSettings(projectName, options),
|
|
924
|
+
uploadFile: (project, bucket, fileHash, fileExt, fileBuffer, options) => uploadRemoteFile(process.env.VAL_CONTENT_URL || DEFAULT_CONTENT_HOST, project, bucket, fileHash, fileExt ?? "", fileBuffer, options)
|
|
925
|
+
},
|
|
926
|
+
fs: createDefaultValFSHost()
|
|
927
|
+
})) {
|
|
928
|
+
switch (event.type) {
|
|
929
|
+
case "file-valid":
|
|
930
|
+
console.log(pc.green("✔"), event.file, "is valid (" + event.durationMs + "ms)");
|
|
931
|
+
break;
|
|
932
|
+
case "file-error-count":
|
|
933
|
+
console.log(pc.red("✘"), `${event.file} contains ${event.errorCount} error${event.errorCount > 1 ? "s" : ""}`, " (" + event.durationMs + "ms)");
|
|
934
|
+
totalErrors += event.errorCount;
|
|
935
|
+
break;
|
|
936
|
+
case "validation-error":
|
|
937
|
+
console.log(pc.red("✘"), "Got error in", `${event.sourcePath}:`, event.message);
|
|
938
|
+
break;
|
|
939
|
+
case "validation-fixable-error":
|
|
940
|
+
console.log(event.fixable ? pc.yellow("⚠") : pc.red("✘"), `Got ${event.fixable ? "fixable " : ""}error in`, `${event.sourcePath}:`, event.message);
|
|
941
|
+
break;
|
|
942
|
+
case "unknown-fix":
|
|
943
|
+
console.log(pc.red("✘"), "Unknown fix", event.fixes, "for", event.sourcePath);
|
|
944
|
+
break;
|
|
945
|
+
case "fix-applied":
|
|
946
|
+
console.log(pc.yellow("⚠"), "Applied fix for", event.sourcePath);
|
|
947
|
+
fixedFiles.add(event.file);
|
|
948
|
+
break;
|
|
949
|
+
case "fatal-error":
|
|
950
|
+
console.log(pc.red("✘"), event.file, "is invalid:", event.message);
|
|
951
|
+
break;
|
|
952
|
+
case "remote-uploading":
|
|
953
|
+
console.log(pc.yellow("⚠"), `Uploading remote file: '${event.ref}'...`);
|
|
954
|
+
break;
|
|
955
|
+
case "remote-uploaded":
|
|
956
|
+
console.log(pc.green("✔"), `Completed upload of remote file: '${event.ref}'`);
|
|
957
|
+
break;
|
|
958
|
+
case "remote-already-uploaded":
|
|
959
|
+
console.log(pc.yellow("⚠"), `Remote file ${event.filePath} already uploaded`);
|
|
960
|
+
break;
|
|
961
|
+
case "remote-downloading":
|
|
962
|
+
console.log(pc.yellow("⚠"), `Downloading remote file in ${event.sourcePath}...`);
|
|
963
|
+
break;
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
// Run prettier on files that had fixes applied
|
|
968
|
+
if (prettier) {
|
|
969
|
+
for (const file of fixedFiles) {
|
|
970
|
+
const filePath = path.join(projectRoot, file);
|
|
971
|
+
const fileContent = await fs.readFile(filePath, "utf-8");
|
|
972
|
+
const formattedContent = await prettier.format(fileContent, {
|
|
973
|
+
filepath: filePath
|
|
974
|
+
});
|
|
975
|
+
await fs.writeFile(filePath, formattedContent);
|
|
976
|
+
}
|
|
977
|
+
}
|
|
978
|
+
if (totalErrors > 0) {
|
|
979
|
+
console.log(pc.red("✘"), "Got", totalErrors, "error" + (totalErrors > 1 ? "s" : ""));
|
|
980
|
+
process.exit(1);
|
|
981
|
+
} else {
|
|
982
|
+
console.log(pc.green("✔"), "No validation errors found");
|
|
983
|
+
}
|
|
984
|
+
}
|
|
985
|
+
|
|
731
986
|
async function listUnusedFiles({
|
|
732
987
|
root
|
|
733
988
|
}) {
|
|
@@ -835,7 +1090,7 @@ async function connect(options) {
|
|
|
835
1090
|
params.set("github_repo", [maybeGitOwnerAndRepo.owner, maybeGitOwnerAndRepo.repo].join("/"));
|
|
836
1091
|
}
|
|
837
1092
|
const url = `${host$1}/connect?${params.toString()}`;
|
|
838
|
-
console.log(
|
|
1093
|
+
console.log(pc.dim(`\nFollow the instructions in your browser to complete the setup:\n${pc.cyan(url)}\n`));
|
|
839
1094
|
}
|
|
840
1095
|
async function tryGetProject(projectRoot) {
|
|
841
1096
|
const valConfigFile = (await evalValConfigFile(projectRoot, "val.config.ts")) || (await evalValConfigFile(projectRoot, "val.config.js"));
|
|
@@ -847,7 +1102,7 @@ async function tryGetProject(projectRoot) {
|
|
|
847
1102
|
projectName: parts[1]
|
|
848
1103
|
};
|
|
849
1104
|
} else {
|
|
850
|
-
console.error(
|
|
1105
|
+
console.error(pc.red(`Invalid project format in val.config file: "${valConfigFile.project}". Expected format "orgName/projectName".`));
|
|
851
1106
|
process.exit(1);
|
|
852
1107
|
}
|
|
853
1108
|
}
|
|
@@ -882,7 +1137,7 @@ async function tryGetGitRemote(root) {
|
|
|
882
1137
|
}
|
|
883
1138
|
return null;
|
|
884
1139
|
} catch (error) {
|
|
885
|
-
console.error(
|
|
1140
|
+
console.error(pc.red("Failed to read .git/config file."), error);
|
|
886
1141
|
return null;
|
|
887
1142
|
}
|
|
888
1143
|
}
|
|
@@ -891,8 +1146,8 @@ function tryGetGitConfig(root) {
|
|
|
891
1146
|
let lastDir = null;
|
|
892
1147
|
while (currentDir !== lastDir) {
|
|
893
1148
|
const gitConfigPath = path.join(currentDir, ".git", "config");
|
|
894
|
-
if (
|
|
895
|
-
return
|
|
1149
|
+
if (nodeFs.existsSync(gitConfigPath)) {
|
|
1150
|
+
return nodeFs.readFileSync(gitConfigPath, "utf-8");
|
|
896
1151
|
}
|
|
897
1152
|
lastDir = currentDir;
|
|
898
1153
|
currentDir = path.dirname(currentDir);
|
|
@@ -907,7 +1162,7 @@ const host = process.env.VAL_BUILD_URL || "https://admin.val.build";
|
|
|
907
1162
|
async function login(options) {
|
|
908
1163
|
try {
|
|
909
1164
|
var _response$headers$get;
|
|
910
|
-
console.log(
|
|
1165
|
+
console.log(pc.cyan("\nStarting login process...\n"));
|
|
911
1166
|
|
|
912
1167
|
// Step 1: Initiate login and get token and URL
|
|
913
1168
|
const response = await fetch(`${host}/api/login`, {
|
|
@@ -920,7 +1175,7 @@ async function login(options) {
|
|
|
920
1175
|
let url;
|
|
921
1176
|
if (!((_response$headers$get = response.headers.get("content-type")) !== null && _response$headers$get !== void 0 && _response$headers$get.includes("application/json"))) {
|
|
922
1177
|
const text = await response.text();
|
|
923
|
-
console.error(
|
|
1178
|
+
console.error(pc.red("Unexpected failure while trying to login (content type was not JSON). "), text ? `Server response: ${text} (status: ${response.status})` : `Status: ${response.status}`);
|
|
924
1179
|
process.exit(1);
|
|
925
1180
|
}
|
|
926
1181
|
const json = await response.json();
|
|
@@ -929,12 +1184,12 @@ async function login(options) {
|
|
|
929
1184
|
url = json.url;
|
|
930
1185
|
}
|
|
931
1186
|
if (!token || !url) {
|
|
932
|
-
console.error(
|
|
1187
|
+
console.error(pc.red("Unexpected response from the server."), json);
|
|
933
1188
|
process.exit(1);
|
|
934
1189
|
}
|
|
935
|
-
console.log(
|
|
936
|
-
console.log(
|
|
937
|
-
console.log(
|
|
1190
|
+
console.log(pc.green("Open the following URL in your browser to log in:"));
|
|
1191
|
+
console.log(pc.underline(pc.blue(url)));
|
|
1192
|
+
console.log(pc.dim("\nWaiting for login confirmation...\n"));
|
|
938
1193
|
|
|
939
1194
|
// Step 2: Poll for login confirmation
|
|
940
1195
|
const result = await pollForConfirmation(token);
|
|
@@ -943,7 +1198,7 @@ async function login(options) {
|
|
|
943
1198
|
const filePath = getPersonalAccessTokenPath(options.root || process.cwd());
|
|
944
1199
|
saveToken(result, filePath);
|
|
945
1200
|
} catch (error) {
|
|
946
|
-
console.error(
|
|
1201
|
+
console.error(pc.red("An error occurred during the login process. Check your internet connection. Details:"), error instanceof Error ? error.message : JSON.stringify(error, null, 2));
|
|
947
1202
|
process.exit(1);
|
|
948
1203
|
}
|
|
949
1204
|
}
|
|
@@ -956,7 +1211,7 @@ async function pollForConfirmation(token) {
|
|
|
956
1211
|
method: "POST"
|
|
957
1212
|
});
|
|
958
1213
|
if (response.status === 500) {
|
|
959
|
-
console.error(
|
|
1214
|
+
console.error(pc.red("An error occurred on the server."));
|
|
960
1215
|
process.exit(1);
|
|
961
1216
|
}
|
|
962
1217
|
if (response.status === 200) {
|
|
@@ -965,21 +1220,21 @@ async function pollForConfirmation(token) {
|
|
|
965
1220
|
if (typeof json.profile.email === "string" && typeof json.pat === "string") {
|
|
966
1221
|
return json;
|
|
967
1222
|
} else {
|
|
968
|
-
console.error(
|
|
1223
|
+
console.error(pc.red("Unexpected response from the server."));
|
|
969
1224
|
process.exit(1);
|
|
970
1225
|
}
|
|
971
1226
|
}
|
|
972
1227
|
}
|
|
973
1228
|
}
|
|
974
|
-
console.error(
|
|
1229
|
+
console.error(pc.red("Login confirmation timed out."));
|
|
975
1230
|
process.exit(1);
|
|
976
1231
|
}
|
|
977
1232
|
function saveToken(result, filePath) {
|
|
978
|
-
|
|
1233
|
+
nodeFs.mkdirSync(path.dirname(filePath), {
|
|
979
1234
|
recursive: true
|
|
980
1235
|
});
|
|
981
|
-
|
|
982
|
-
console.log(
|
|
1236
|
+
nodeFs.writeFileSync(filePath, JSON.stringify(result, null, 2));
|
|
1237
|
+
console.log(pc.green(`Token for ${pc.cyan(result.profile.email)} saved to ${pc.cyan(filePath)}`));
|
|
983
1238
|
}
|
|
984
1239
|
|
|
985
1240
|
async function main() {
|