@valbuild/server 0.73.1 → 0.74.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.
@@ -9,6 +9,7 @@ export type ValServerOptions = {
|
|
9
9
|
valDisableRedirectUrl?: string;
|
10
10
|
formatter?: (code: string, filePath: string) => string | Promise<string>;
|
11
11
|
valBuildUrl?: string;
|
12
|
+
valContentUrl: string;
|
12
13
|
valSecret?: string;
|
13
14
|
apiKey?: string;
|
14
15
|
project?: string;
|
@@ -20,7 +21,6 @@ export type ValServerConfig = ValServerOptions & ({
|
|
20
21
|
config: ValConfig;
|
21
22
|
} | {
|
22
23
|
mode: "http";
|
23
|
-
valContentUrl: string;
|
24
24
|
apiKey: string;
|
25
25
|
project: string;
|
26
26
|
commit: string;
|
@@ -1,24 +1,11 @@
|
|
1
1
|
/// <reference types="node" />
|
2
2
|
/// <reference types="node" />
|
3
|
-
|
4
|
-
export declare function uploadRemoteFile(remoteHost: string, fileBuffer: Buffer, publicProjectId: string, bucket: string, filePath: `public/val/${string}`, schema: SerializedImageSchema | SerializedFileSchema, metadata: Record<string, unknown> | undefined, auth: {
|
3
|
+
export declare function uploadRemoteFile(contentHost: string, project: string, bucket: string, fileHash: string, fileExt: string, fileBuffer: Buffer, auth: {
|
5
4
|
pat: string;
|
6
5
|
} | {
|
7
6
|
apiKey: string;
|
8
7
|
}): Promise<{
|
9
8
|
success: true;
|
10
|
-
ref: string;
|
11
|
-
} | {
|
12
|
-
success: false;
|
13
|
-
error: string;
|
14
|
-
}>;
|
15
|
-
export declare function uploadRemoteRef(fileBuffer: Buffer, ref: string, auth: {
|
16
|
-
pat: string;
|
17
|
-
} | {
|
18
|
-
apiKey: string;
|
19
|
-
}): Promise<{
|
20
|
-
success: true;
|
21
|
-
ref: string;
|
22
9
|
} | {
|
23
10
|
success: false;
|
24
11
|
error: string;
|
@@ -1474,7 +1474,7 @@ function encodeJwt(payload, sessionKey) {
|
|
1474
1474
|
}
|
1475
1475
|
|
1476
1476
|
/* eslint-disable @typescript-eslint/no-unused-vars */
|
1477
|
-
const textEncoder$
|
1477
|
+
const textEncoder$2 = new TextEncoder();
|
1478
1478
|
const jsonOps = new patch.JSONOps();
|
1479
1479
|
const tsOps = new TSOps(document => {
|
1480
1480
|
return fp.pipe(analyzeValModule(document), fp.result.map(({
|
@@ -1504,12 +1504,12 @@ class ValOps {
|
|
1504
1504
|
if (typeof input === "object") {
|
1505
1505
|
return this.hashObject(input);
|
1506
1506
|
}
|
1507
|
-
return core.Internal.getSHA256Hash(textEncoder$
|
1507
|
+
return core.Internal.getSHA256Hash(textEncoder$2.encode(input));
|
1508
1508
|
}
|
1509
1509
|
hashObject(obj) {
|
1510
1510
|
const collector = [];
|
1511
1511
|
this.collectObjectRecursive(obj, collector);
|
1512
|
-
return core.Internal.getSHA256Hash(textEncoder$
|
1512
|
+
return core.Internal.getSHA256Hash(textEncoder$2.encode(collector.join("")));
|
1513
1513
|
}
|
1514
1514
|
collectObjectRecursive(item, collector) {
|
1515
1515
|
if (typeof item === "string") {
|
@@ -2293,7 +2293,7 @@ class ValOps {
|
|
2293
2293
|
error: new patch.PatchError("Value is not a string")
|
2294
2294
|
};
|
2295
2295
|
} else {
|
2296
|
-
const sha256 = core.Internal.getSHA256Hash(textEncoder$
|
2296
|
+
const sha256 = core.Internal.getSHA256Hash(textEncoder$2.encode(value));
|
2297
2297
|
files[filePath] = {
|
2298
2298
|
value,
|
2299
2299
|
sha256,
|
@@ -2558,33 +2558,13 @@ function computeChangedPatchParentRefs(currentPatches, deletePatchIds) {
|
|
2558
2558
|
};
|
2559
2559
|
}
|
2560
2560
|
|
2561
|
-
function
|
2562
|
-
// NOTE: We do not import the path module. This code is copied in different projects. We want the same implementation and which means that this might running in browser where path is not available).
|
2563
|
-
return filePath.split(".").pop() || "";
|
2564
|
-
}
|
2565
|
-
|
2566
|
-
const textEncoder$2 = new TextEncoder();
|
2567
|
-
async function uploadRemoteFile(remoteHost, fileBuffer, publicProjectId, bucket, filePath, schema, metadata, auth) {
|
2568
|
-
const fileHash = core.Internal.remote.getFileHash(fileBuffer);
|
2569
|
-
const coreVersion = core.Internal.VERSION.core || "unknown";
|
2570
|
-
const fileExt = getFileExt(filePath);
|
2571
|
-
const ref = core.Internal.remote.createRemoteRef(remoteHost, {
|
2572
|
-
publicProjectId,
|
2573
|
-
coreVersion,
|
2574
|
-
bucket,
|
2575
|
-
validationHash: core.Internal.remote.getValidationHash(coreVersion, schema, fileExt, metadata, fileHash, textEncoder$2),
|
2576
|
-
fileHash,
|
2577
|
-
filePath
|
2578
|
-
});
|
2579
|
-
return uploadRemoteRef(fileBuffer, ref, auth);
|
2580
|
-
}
|
2581
|
-
async function uploadRemoteRef(fileBuffer, ref, auth) {
|
2561
|
+
async function uploadRemoteFile(contentHost, project, bucket, fileHash, fileExt, fileBuffer, auth) {
|
2582
2562
|
const authHeader = "apiKey" in auth ? {
|
2583
2563
|
Authorization: `Bearer ${auth.apiKey}`
|
2584
2564
|
} : {
|
2585
2565
|
"x-val-pat": auth.pat
|
2586
2566
|
};
|
2587
|
-
const res = await fetch(
|
2567
|
+
const res = await fetch(`${contentHost}/v1/${project}/remote/files/b/${bucket}/f/${fileHash}.${fileExt}`, {
|
2588
2568
|
method: "PUT",
|
2589
2569
|
headers: {
|
2590
2570
|
...authHeader,
|
@@ -2597,8 +2577,7 @@ async function uploadRemoteRef(fileBuffer, ref, auth) {
|
|
2597
2577
|
if (res.status === 409) {
|
2598
2578
|
// File already exists
|
2599
2579
|
return {
|
2600
|
-
success: true
|
2601
|
-
ref
|
2580
|
+
success: true
|
2602
2581
|
};
|
2603
2582
|
}
|
2604
2583
|
if ((_res$headers$get = res.headers.get("content-type")) !== null && _res$headers$get !== void 0 && _res$headers$get.includes("application/json")) {
|
@@ -2606,30 +2585,35 @@ async function uploadRemoteRef(fileBuffer, ref, auth) {
|
|
2606
2585
|
if (json.message) {
|
2607
2586
|
return {
|
2608
2587
|
success: false,
|
2609
|
-
error:
|
2588
|
+
error: `Failed to upload remote file: ${json.message}.`
|
2610
2589
|
};
|
2611
2590
|
} else {
|
2612
2591
|
return {
|
2613
2592
|
success: false,
|
2614
|
-
error:
|
2593
|
+
error: `Failed to upload remote file: ${JSON.stringify(json)}.`
|
2615
2594
|
};
|
2616
2595
|
}
|
2617
2596
|
}
|
2618
2597
|
return {
|
2619
2598
|
success: false,
|
2620
|
-
error:
|
2599
|
+
error: `An unexpected error occurred while uploading remote file. HTTP status was: ${await res.text()}.`
|
2621
2600
|
};
|
2622
2601
|
}
|
2623
2602
|
return {
|
2624
|
-
success: true
|
2625
|
-
ref
|
2603
|
+
success: true
|
2626
2604
|
};
|
2627
2605
|
}
|
2628
2606
|
|
2607
|
+
function getFileExt(filePath) {
|
2608
|
+
// NOTE: We do not import the path module. This code is copied in different projects. We want the same implementation and which means that this might running in browser where path is not available).
|
2609
|
+
return filePath.split(".").pop() || "";
|
2610
|
+
}
|
2611
|
+
|
2629
2612
|
class ValOpsFS extends ValOps {
|
2630
2613
|
static VAL_DIR = ".val";
|
2631
|
-
constructor(rootDir, valModules, options) {
|
2614
|
+
constructor(contentUrl, rootDir, valModules, options) {
|
2632
2615
|
super(valModules, options);
|
2616
|
+
this.contentUrl = contentUrl;
|
2633
2617
|
this.rootDir = rootDir;
|
2634
2618
|
this.host = new FSOpsHost();
|
2635
2619
|
}
|
@@ -3238,6 +3222,7 @@ class ValOpsFS extends ValOps {
|
|
3238
3222
|
for (const [ref, {
|
3239
3223
|
patchId
|
3240
3224
|
}] of remoteFileDescriptors) {
|
3225
|
+
var _this$options4;
|
3241
3226
|
const splitRemoteRefRes = core.Internal.remote.splitRemoteRef(ref);
|
3242
3227
|
if (splitRemoteRefRes.status === "error") {
|
3243
3228
|
errors[ref] = {
|
@@ -3256,8 +3241,14 @@ class ValOpsFS extends ValOps {
|
|
3256
3241
|
console.log("Skip remote flag enabled. Skipping file upload", ref);
|
3257
3242
|
continue;
|
3258
3243
|
}
|
3244
|
+
if (!((_this$options4 = this.options) !== null && _this$options4 !== void 0 && _this$options4.config.project)) {
|
3245
|
+
errors[ref] = {
|
3246
|
+
message: "No project found in config"
|
3247
|
+
};
|
3248
|
+
continue;
|
3249
|
+
}
|
3259
3250
|
console.log("Uploading remote file", ref);
|
3260
|
-
const res = await
|
3251
|
+
const res = await uploadRemoteFile(this.contentUrl, this.options.config.project, splitRemoteRefRes.bucket, splitRemoteRefRes.fileHash, getFileExt(splitRemoteRefRes.filePath), fileBuffer, auth);
|
3261
3252
|
if (!res.success) {
|
3262
3253
|
console.error("Failed to upload remote file", ref, res.error);
|
3263
3254
|
throw new Error(`Failed to upload remote file: ${ref}. ${res.error}`);
|
@@ -4443,7 +4434,7 @@ async function getSettings(projectName, auth) {
|
|
4443
4434
|
if (response.status === 404) {
|
4444
4435
|
return {
|
4445
4436
|
success: false,
|
4446
|
-
message: `Project '${projectName}' not found:
|
4437
|
+
message: `Project '${projectName}' not found: that the name of the project is correct and that you have access to it.`
|
4447
4438
|
};
|
4448
4439
|
}
|
4449
4440
|
if (response.status !== 200) {
|
@@ -4527,10 +4518,9 @@ function parsePersonalAccessTokenFile(content) {
|
|
4527
4518
|
|
4528
4519
|
/* eslint-disable @typescript-eslint/no-unused-vars */
|
4529
4520
|
const ValServer = (valModules, options, callbacks) => {
|
4530
|
-
process.env.VAL_REMOTE_HOST || core.DEFAULT_VAL_REMOTE_HOST;
|
4531
4521
|
let serverOps;
|
4532
4522
|
if (options.mode === "fs") {
|
4533
|
-
serverOps = new ValOpsFS(options.cwd, valModules, {
|
4523
|
+
serverOps = new ValOpsFS(options.valContentUrl, options.cwd, valModules, {
|
4534
4524
|
formatter: options.formatter,
|
4535
4525
|
config: options.config
|
4536
4526
|
});
|
@@ -5107,7 +5097,7 @@ const ValServer = (valModules, options, callbacks) => {
|
|
5107
5097
|
status: 400,
|
5108
5098
|
json: {
|
5109
5099
|
errorCode: "error-could-not-get-settings",
|
5110
|
-
message: `Could not get settings: ${settingsRes.message}`
|
5100
|
+
message: `Could not get settings id: ${settingsRes.message}`
|
5111
5101
|
}
|
5112
5102
|
};
|
5113
5103
|
}
|
@@ -6098,12 +6088,12 @@ async function initHandlerOptions(route, opts, config) {
|
|
6098
6088
|
const valDisableRedirectUrl = opts.valDisableRedirectUrl || process.env.VAL_DISABLE_REDIRECT_URL;
|
6099
6089
|
const maybeValProject = opts.project || process.env.VAL_PROJECT;
|
6100
6090
|
const valBuildUrl = opts.valBuildUrl || process.env.VAL_BUILD_URL || "https://app.val.build";
|
6091
|
+
const valContentUrl = opts.valContentUrl || process.env.VAL_CONTENT_URL || "https://content.val.build";
|
6101
6092
|
if (isProxyMode) {
|
6102
6093
|
var _opts$versions, _opts$versions2;
|
6103
6094
|
if (!maybeApiKey || !maybeValSecret) {
|
6104
6095
|
throw new Error("VAL_API_KEY and VAL_SECRET env vars must both be set in proxy mode");
|
6105
6096
|
}
|
6106
|
-
const valContentUrl = opts.valContentUrl || process.env.VAL_CONTENT_URL || "https://content.val.build";
|
6107
6097
|
const maybeGitCommit = opts.gitCommit || process.env.VAL_GIT_COMMIT;
|
6108
6098
|
if (!maybeGitCommit) {
|
6109
6099
|
throw new Error("VAL_GIT_COMMIT env var must be set in proxy mode");
|
@@ -6148,6 +6138,7 @@ async function initHandlerOptions(route, opts, config) {
|
|
6148
6138
|
valDisableRedirectUrl,
|
6149
6139
|
valEnableRedirectUrl,
|
6150
6140
|
valBuildUrl,
|
6141
|
+
valContentUrl,
|
6151
6142
|
apiKey: maybeApiKey,
|
6152
6143
|
valSecret: maybeValSecret,
|
6153
6144
|
project: maybeValProject,
|
@@ -6794,15 +6785,23 @@ async function createFixPatch(config, apply, sourcePath, validationError, remote
|
|
6794
6785
|
}
|
6795
6786
|
}
|
6796
6787
|
} else if (fix === "image:add-metadata") {
|
6797
|
-
|
6798
|
-
|
6799
|
-
|
6800
|
-
|
6801
|
-
|
6802
|
-
|
6803
|
-
|
6804
|
-
|
6805
|
-
|
6788
|
+
if (!imageMetadata.mimeType) {
|
6789
|
+
remainingErrors.push({
|
6790
|
+
...validationError,
|
6791
|
+
message: "Failed to get image metadata",
|
6792
|
+
fixes: undefined
|
6793
|
+
});
|
6794
|
+
} else {
|
6795
|
+
patch$1.push({
|
6796
|
+
op: "add",
|
6797
|
+
path: patch.sourceToPatchPath(sourcePath).concat("metadata"),
|
6798
|
+
value: {
|
6799
|
+
width: imageMetadata.width,
|
6800
|
+
height: imageMetadata.height,
|
6801
|
+
mimeType: imageMetadata.mimeType
|
6802
|
+
}
|
6803
|
+
});
|
6804
|
+
}
|
6806
6805
|
}
|
6807
6806
|
} else if (fix === "file:add-metadata" || fix === "file:check-metadata") {
|
6808
6807
|
const fileMetadata = await getFileMetadata(config.projectRoot, validationError);
|
@@ -6864,7 +6863,21 @@ async function createFixPatch(config, apply, sourcePath, validationError, remote
|
|
6864
6863
|
}
|
6865
6864
|
} else if (fix === "image:upload-remote" || fix === "file:upload-remote") {
|
6866
6865
|
const remoteFile = remoteFiles[sourcePath];
|
6867
|
-
|
6866
|
+
let metadata = remoteFile.metadata;
|
6867
|
+
if (!metadata) {
|
6868
|
+
if (fix === "image:upload-remote") {
|
6869
|
+
metadata = await getImageMetadata(config.projectRoot, validationError);
|
6870
|
+
} else if (fix === "file:upload-remote") {
|
6871
|
+
metadata = await getFileMetadata(config.projectRoot, validationError);
|
6872
|
+
}
|
6873
|
+
}
|
6874
|
+
if (!metadata) {
|
6875
|
+
remainingErrors.push({
|
6876
|
+
...validationError,
|
6877
|
+
message: "Failed to get metadata for remote file",
|
6878
|
+
fixes: undefined
|
6879
|
+
});
|
6880
|
+
} else if (!remoteFile) {
|
6868
6881
|
remainingErrors.push({
|
6869
6882
|
...validationError,
|
6870
6883
|
message: "Cannot fix local to remote image: remote image was not uploaded",
|
@@ -6876,7 +6889,7 @@ async function createFixPatch(config, apply, sourcePath, validationError, remote
|
|
6876
6889
|
value: {
|
6877
6890
|
_type: "remote",
|
6878
6891
|
_ref: remoteFile.ref,
|
6879
|
-
metadata
|
6892
|
+
metadata
|
6880
6893
|
},
|
6881
6894
|
path: patch.sourceToPatchPath(sourcePath)
|
6882
6895
|
});
|
@@ -1474,7 +1474,7 @@ function encodeJwt(payload, sessionKey) {
|
|
1474
1474
|
}
|
1475
1475
|
|
1476
1476
|
/* eslint-disable @typescript-eslint/no-unused-vars */
|
1477
|
-
const textEncoder$
|
1477
|
+
const textEncoder$2 = new TextEncoder();
|
1478
1478
|
const jsonOps = new patch.JSONOps();
|
1479
1479
|
const tsOps = new TSOps(document => {
|
1480
1480
|
return fp.pipe(analyzeValModule(document), fp.result.map(({
|
@@ -1504,12 +1504,12 @@ class ValOps {
|
|
1504
1504
|
if (typeof input === "object") {
|
1505
1505
|
return this.hashObject(input);
|
1506
1506
|
}
|
1507
|
-
return core.Internal.getSHA256Hash(textEncoder$
|
1507
|
+
return core.Internal.getSHA256Hash(textEncoder$2.encode(input));
|
1508
1508
|
}
|
1509
1509
|
hashObject(obj) {
|
1510
1510
|
const collector = [];
|
1511
1511
|
this.collectObjectRecursive(obj, collector);
|
1512
|
-
return core.Internal.getSHA256Hash(textEncoder$
|
1512
|
+
return core.Internal.getSHA256Hash(textEncoder$2.encode(collector.join("")));
|
1513
1513
|
}
|
1514
1514
|
collectObjectRecursive(item, collector) {
|
1515
1515
|
if (typeof item === "string") {
|
@@ -2293,7 +2293,7 @@ class ValOps {
|
|
2293
2293
|
error: new patch.PatchError("Value is not a string")
|
2294
2294
|
};
|
2295
2295
|
} else {
|
2296
|
-
const sha256 = core.Internal.getSHA256Hash(textEncoder$
|
2296
|
+
const sha256 = core.Internal.getSHA256Hash(textEncoder$2.encode(value));
|
2297
2297
|
files[filePath] = {
|
2298
2298
|
value,
|
2299
2299
|
sha256,
|
@@ -2558,33 +2558,13 @@ function computeChangedPatchParentRefs(currentPatches, deletePatchIds) {
|
|
2558
2558
|
};
|
2559
2559
|
}
|
2560
2560
|
|
2561
|
-
function
|
2562
|
-
// NOTE: We do not import the path module. This code is copied in different projects. We want the same implementation and which means that this might running in browser where path is not available).
|
2563
|
-
return filePath.split(".").pop() || "";
|
2564
|
-
}
|
2565
|
-
|
2566
|
-
const textEncoder$2 = new TextEncoder();
|
2567
|
-
async function uploadRemoteFile(remoteHost, fileBuffer, publicProjectId, bucket, filePath, schema, metadata, auth) {
|
2568
|
-
const fileHash = core.Internal.remote.getFileHash(fileBuffer);
|
2569
|
-
const coreVersion = core.Internal.VERSION.core || "unknown";
|
2570
|
-
const fileExt = getFileExt(filePath);
|
2571
|
-
const ref = core.Internal.remote.createRemoteRef(remoteHost, {
|
2572
|
-
publicProjectId,
|
2573
|
-
coreVersion,
|
2574
|
-
bucket,
|
2575
|
-
validationHash: core.Internal.remote.getValidationHash(coreVersion, schema, fileExt, metadata, fileHash, textEncoder$2),
|
2576
|
-
fileHash,
|
2577
|
-
filePath
|
2578
|
-
});
|
2579
|
-
return uploadRemoteRef(fileBuffer, ref, auth);
|
2580
|
-
}
|
2581
|
-
async function uploadRemoteRef(fileBuffer, ref, auth) {
|
2561
|
+
async function uploadRemoteFile(contentHost, project, bucket, fileHash, fileExt, fileBuffer, auth) {
|
2582
2562
|
const authHeader = "apiKey" in auth ? {
|
2583
2563
|
Authorization: `Bearer ${auth.apiKey}`
|
2584
2564
|
} : {
|
2585
2565
|
"x-val-pat": auth.pat
|
2586
2566
|
};
|
2587
|
-
const res = await fetch(
|
2567
|
+
const res = await fetch(`${contentHost}/v1/${project}/remote/files/b/${bucket}/f/${fileHash}.${fileExt}`, {
|
2588
2568
|
method: "PUT",
|
2589
2569
|
headers: {
|
2590
2570
|
...authHeader,
|
@@ -2597,8 +2577,7 @@ async function uploadRemoteRef(fileBuffer, ref, auth) {
|
|
2597
2577
|
if (res.status === 409) {
|
2598
2578
|
// File already exists
|
2599
2579
|
return {
|
2600
|
-
success: true
|
2601
|
-
ref
|
2580
|
+
success: true
|
2602
2581
|
};
|
2603
2582
|
}
|
2604
2583
|
if ((_res$headers$get = res.headers.get("content-type")) !== null && _res$headers$get !== void 0 && _res$headers$get.includes("application/json")) {
|
@@ -2606,30 +2585,35 @@ async function uploadRemoteRef(fileBuffer, ref, auth) {
|
|
2606
2585
|
if (json.message) {
|
2607
2586
|
return {
|
2608
2587
|
success: false,
|
2609
|
-
error:
|
2588
|
+
error: `Failed to upload remote file: ${json.message}.`
|
2610
2589
|
};
|
2611
2590
|
} else {
|
2612
2591
|
return {
|
2613
2592
|
success: false,
|
2614
|
-
error:
|
2593
|
+
error: `Failed to upload remote file: ${JSON.stringify(json)}.`
|
2615
2594
|
};
|
2616
2595
|
}
|
2617
2596
|
}
|
2618
2597
|
return {
|
2619
2598
|
success: false,
|
2620
|
-
error:
|
2599
|
+
error: `An unexpected error occurred while uploading remote file. HTTP status was: ${await res.text()}.`
|
2621
2600
|
};
|
2622
2601
|
}
|
2623
2602
|
return {
|
2624
|
-
success: true
|
2625
|
-
ref
|
2603
|
+
success: true
|
2626
2604
|
};
|
2627
2605
|
}
|
2628
2606
|
|
2607
|
+
function getFileExt(filePath) {
|
2608
|
+
// NOTE: We do not import the path module. This code is copied in different projects. We want the same implementation and which means that this might running in browser where path is not available).
|
2609
|
+
return filePath.split(".").pop() || "";
|
2610
|
+
}
|
2611
|
+
|
2629
2612
|
class ValOpsFS extends ValOps {
|
2630
2613
|
static VAL_DIR = ".val";
|
2631
|
-
constructor(rootDir, valModules, options) {
|
2614
|
+
constructor(contentUrl, rootDir, valModules, options) {
|
2632
2615
|
super(valModules, options);
|
2616
|
+
this.contentUrl = contentUrl;
|
2633
2617
|
this.rootDir = rootDir;
|
2634
2618
|
this.host = new FSOpsHost();
|
2635
2619
|
}
|
@@ -3238,6 +3222,7 @@ class ValOpsFS extends ValOps {
|
|
3238
3222
|
for (const [ref, {
|
3239
3223
|
patchId
|
3240
3224
|
}] of remoteFileDescriptors) {
|
3225
|
+
var _this$options4;
|
3241
3226
|
const splitRemoteRefRes = core.Internal.remote.splitRemoteRef(ref);
|
3242
3227
|
if (splitRemoteRefRes.status === "error") {
|
3243
3228
|
errors[ref] = {
|
@@ -3256,8 +3241,14 @@ class ValOpsFS extends ValOps {
|
|
3256
3241
|
console.log("Skip remote flag enabled. Skipping file upload", ref);
|
3257
3242
|
continue;
|
3258
3243
|
}
|
3244
|
+
if (!((_this$options4 = this.options) !== null && _this$options4 !== void 0 && _this$options4.config.project)) {
|
3245
|
+
errors[ref] = {
|
3246
|
+
message: "No project found in config"
|
3247
|
+
};
|
3248
|
+
continue;
|
3249
|
+
}
|
3259
3250
|
console.log("Uploading remote file", ref);
|
3260
|
-
const res = await
|
3251
|
+
const res = await uploadRemoteFile(this.contentUrl, this.options.config.project, splitRemoteRefRes.bucket, splitRemoteRefRes.fileHash, getFileExt(splitRemoteRefRes.filePath), fileBuffer, auth);
|
3261
3252
|
if (!res.success) {
|
3262
3253
|
console.error("Failed to upload remote file", ref, res.error);
|
3263
3254
|
throw new Error(`Failed to upload remote file: ${ref}. ${res.error}`);
|
@@ -4443,7 +4434,7 @@ async function getSettings(projectName, auth) {
|
|
4443
4434
|
if (response.status === 404) {
|
4444
4435
|
return {
|
4445
4436
|
success: false,
|
4446
|
-
message: `Project '${projectName}' not found:
|
4437
|
+
message: `Project '${projectName}' not found: that the name of the project is correct and that you have access to it.`
|
4447
4438
|
};
|
4448
4439
|
}
|
4449
4440
|
if (response.status !== 200) {
|
@@ -4527,10 +4518,9 @@ function parsePersonalAccessTokenFile(content) {
|
|
4527
4518
|
|
4528
4519
|
/* eslint-disable @typescript-eslint/no-unused-vars */
|
4529
4520
|
const ValServer = (valModules, options, callbacks) => {
|
4530
|
-
process.env.VAL_REMOTE_HOST || core.DEFAULT_VAL_REMOTE_HOST;
|
4531
4521
|
let serverOps;
|
4532
4522
|
if (options.mode === "fs") {
|
4533
|
-
serverOps = new ValOpsFS(options.cwd, valModules, {
|
4523
|
+
serverOps = new ValOpsFS(options.valContentUrl, options.cwd, valModules, {
|
4534
4524
|
formatter: options.formatter,
|
4535
4525
|
config: options.config
|
4536
4526
|
});
|
@@ -5107,7 +5097,7 @@ const ValServer = (valModules, options, callbacks) => {
|
|
5107
5097
|
status: 400,
|
5108
5098
|
json: {
|
5109
5099
|
errorCode: "error-could-not-get-settings",
|
5110
|
-
message: `Could not get settings: ${settingsRes.message}`
|
5100
|
+
message: `Could not get settings id: ${settingsRes.message}`
|
5111
5101
|
}
|
5112
5102
|
};
|
5113
5103
|
}
|
@@ -6098,12 +6088,12 @@ async function initHandlerOptions(route, opts, config) {
|
|
6098
6088
|
const valDisableRedirectUrl = opts.valDisableRedirectUrl || process.env.VAL_DISABLE_REDIRECT_URL;
|
6099
6089
|
const maybeValProject = opts.project || process.env.VAL_PROJECT;
|
6100
6090
|
const valBuildUrl = opts.valBuildUrl || process.env.VAL_BUILD_URL || "https://app.val.build";
|
6091
|
+
const valContentUrl = opts.valContentUrl || process.env.VAL_CONTENT_URL || "https://content.val.build";
|
6101
6092
|
if (isProxyMode) {
|
6102
6093
|
var _opts$versions, _opts$versions2;
|
6103
6094
|
if (!maybeApiKey || !maybeValSecret) {
|
6104
6095
|
throw new Error("VAL_API_KEY and VAL_SECRET env vars must both be set in proxy mode");
|
6105
6096
|
}
|
6106
|
-
const valContentUrl = opts.valContentUrl || process.env.VAL_CONTENT_URL || "https://content.val.build";
|
6107
6097
|
const maybeGitCommit = opts.gitCommit || process.env.VAL_GIT_COMMIT;
|
6108
6098
|
if (!maybeGitCommit) {
|
6109
6099
|
throw new Error("VAL_GIT_COMMIT env var must be set in proxy mode");
|
@@ -6148,6 +6138,7 @@ async function initHandlerOptions(route, opts, config) {
|
|
6148
6138
|
valDisableRedirectUrl,
|
6149
6139
|
valEnableRedirectUrl,
|
6150
6140
|
valBuildUrl,
|
6141
|
+
valContentUrl,
|
6151
6142
|
apiKey: maybeApiKey,
|
6152
6143
|
valSecret: maybeValSecret,
|
6153
6144
|
project: maybeValProject,
|
@@ -6794,15 +6785,23 @@ async function createFixPatch(config, apply, sourcePath, validationError, remote
|
|
6794
6785
|
}
|
6795
6786
|
}
|
6796
6787
|
} else if (fix === "image:add-metadata") {
|
6797
|
-
|
6798
|
-
|
6799
|
-
|
6800
|
-
|
6801
|
-
|
6802
|
-
|
6803
|
-
|
6804
|
-
|
6805
|
-
|
6788
|
+
if (!imageMetadata.mimeType) {
|
6789
|
+
remainingErrors.push({
|
6790
|
+
...validationError,
|
6791
|
+
message: "Failed to get image metadata",
|
6792
|
+
fixes: undefined
|
6793
|
+
});
|
6794
|
+
} else {
|
6795
|
+
patch$1.push({
|
6796
|
+
op: "add",
|
6797
|
+
path: patch.sourceToPatchPath(sourcePath).concat("metadata"),
|
6798
|
+
value: {
|
6799
|
+
width: imageMetadata.width,
|
6800
|
+
height: imageMetadata.height,
|
6801
|
+
mimeType: imageMetadata.mimeType
|
6802
|
+
}
|
6803
|
+
});
|
6804
|
+
}
|
6806
6805
|
}
|
6807
6806
|
} else if (fix === "file:add-metadata" || fix === "file:check-metadata") {
|
6808
6807
|
const fileMetadata = await getFileMetadata(config.projectRoot, validationError);
|
@@ -6864,7 +6863,21 @@ async function createFixPatch(config, apply, sourcePath, validationError, remote
|
|
6864
6863
|
}
|
6865
6864
|
} else if (fix === "image:upload-remote" || fix === "file:upload-remote") {
|
6866
6865
|
const remoteFile = remoteFiles[sourcePath];
|
6867
|
-
|
6866
|
+
let metadata = remoteFile.metadata;
|
6867
|
+
if (!metadata) {
|
6868
|
+
if (fix === "image:upload-remote") {
|
6869
|
+
metadata = await getImageMetadata(config.projectRoot, validationError);
|
6870
|
+
} else if (fix === "file:upload-remote") {
|
6871
|
+
metadata = await getFileMetadata(config.projectRoot, validationError);
|
6872
|
+
}
|
6873
|
+
}
|
6874
|
+
if (!metadata) {
|
6875
|
+
remainingErrors.push({
|
6876
|
+
...validationError,
|
6877
|
+
message: "Failed to get metadata for remote file",
|
6878
|
+
fixes: undefined
|
6879
|
+
});
|
6880
|
+
} else if (!remoteFile) {
|
6868
6881
|
remainingErrors.push({
|
6869
6882
|
...validationError,
|
6870
6883
|
message: "Cannot fix local to remote image: remote image was not uploaded",
|
@@ -6876,7 +6889,7 @@ async function createFixPatch(config, apply, sourcePath, validationError, remote
|
|
6876
6889
|
value: {
|
6877
6890
|
_type: "remote",
|
6878
6891
|
_ref: remoteFile.ref,
|
6879
|
-
metadata
|
6892
|
+
metadata
|
6880
6893
|
},
|
6881
6894
|
path: patch.sourceToPatchPath(sourcePath)
|
6882
6895
|
});
|
@@ -1,7 +1,7 @@
|
|
1
1
|
import { newQuickJSWASMModule } from 'quickjs-emscripten';
|
2
2
|
import ts from 'typescript';
|
3
3
|
import { result, pipe } from '@valbuild/core/fp';
|
4
|
-
import { FILE_REF_PROP, FILE_REF_SUBTYPE_TAG, VAL_EXTENSION, derefPatch, Internal, Schema, ImageSchema, RichTextSchema, FileSchema
|
4
|
+
import { FILE_REF_PROP, FILE_REF_SUBTYPE_TAG, VAL_EXTENSION, derefPatch, Internal, Schema, ImageSchema, RichTextSchema, FileSchema } from '@valbuild/core';
|
5
5
|
import { deepEqual, isNotRoot, PatchError, parseAndValidateArrayIndex, applyPatch, JSONOps, deepClone, sourceToPatchPath } from '@valbuild/core/patch';
|
6
6
|
import * as fsPath from 'path';
|
7
7
|
import fsPath__default from 'path';
|
@@ -1443,7 +1443,7 @@ function encodeJwt(payload, sessionKey) {
|
|
1443
1443
|
}
|
1444
1444
|
|
1445
1445
|
/* eslint-disable @typescript-eslint/no-unused-vars */
|
1446
|
-
const textEncoder$
|
1446
|
+
const textEncoder$2 = new TextEncoder();
|
1447
1447
|
const jsonOps = new JSONOps();
|
1448
1448
|
const tsOps = new TSOps(document => {
|
1449
1449
|
return pipe(analyzeValModule(document), result.map(({
|
@@ -1473,12 +1473,12 @@ class ValOps {
|
|
1473
1473
|
if (typeof input === "object") {
|
1474
1474
|
return this.hashObject(input);
|
1475
1475
|
}
|
1476
|
-
return Internal.getSHA256Hash(textEncoder$
|
1476
|
+
return Internal.getSHA256Hash(textEncoder$2.encode(input));
|
1477
1477
|
}
|
1478
1478
|
hashObject(obj) {
|
1479
1479
|
const collector = [];
|
1480
1480
|
this.collectObjectRecursive(obj, collector);
|
1481
|
-
return Internal.getSHA256Hash(textEncoder$
|
1481
|
+
return Internal.getSHA256Hash(textEncoder$2.encode(collector.join("")));
|
1482
1482
|
}
|
1483
1483
|
collectObjectRecursive(item, collector) {
|
1484
1484
|
if (typeof item === "string") {
|
@@ -2262,7 +2262,7 @@ class ValOps {
|
|
2262
2262
|
error: new PatchError("Value is not a string")
|
2263
2263
|
};
|
2264
2264
|
} else {
|
2265
|
-
const sha256 = Internal.getSHA256Hash(textEncoder$
|
2265
|
+
const sha256 = Internal.getSHA256Hash(textEncoder$2.encode(value));
|
2266
2266
|
files[filePath] = {
|
2267
2267
|
value,
|
2268
2268
|
sha256,
|
@@ -2527,33 +2527,13 @@ function computeChangedPatchParentRefs(currentPatches, deletePatchIds) {
|
|
2527
2527
|
};
|
2528
2528
|
}
|
2529
2529
|
|
2530
|
-
function
|
2531
|
-
// NOTE: We do not import the path module. This code is copied in different projects. We want the same implementation and which means that this might running in browser where path is not available).
|
2532
|
-
return filePath.split(".").pop() || "";
|
2533
|
-
}
|
2534
|
-
|
2535
|
-
const textEncoder$2 = new TextEncoder();
|
2536
|
-
async function uploadRemoteFile(remoteHost, fileBuffer, publicProjectId, bucket, filePath, schema, metadata, auth) {
|
2537
|
-
const fileHash = Internal.remote.getFileHash(fileBuffer);
|
2538
|
-
const coreVersion = Internal.VERSION.core || "unknown";
|
2539
|
-
const fileExt = getFileExt(filePath);
|
2540
|
-
const ref = Internal.remote.createRemoteRef(remoteHost, {
|
2541
|
-
publicProjectId,
|
2542
|
-
coreVersion,
|
2543
|
-
bucket,
|
2544
|
-
validationHash: Internal.remote.getValidationHash(coreVersion, schema, fileExt, metadata, fileHash, textEncoder$2),
|
2545
|
-
fileHash,
|
2546
|
-
filePath
|
2547
|
-
});
|
2548
|
-
return uploadRemoteRef(fileBuffer, ref, auth);
|
2549
|
-
}
|
2550
|
-
async function uploadRemoteRef(fileBuffer, ref, auth) {
|
2530
|
+
async function uploadRemoteFile(contentHost, project, bucket, fileHash, fileExt, fileBuffer, auth) {
|
2551
2531
|
const authHeader = "apiKey" in auth ? {
|
2552
2532
|
Authorization: `Bearer ${auth.apiKey}`
|
2553
2533
|
} : {
|
2554
2534
|
"x-val-pat": auth.pat
|
2555
2535
|
};
|
2556
|
-
const res = await fetch(
|
2536
|
+
const res = await fetch(`${contentHost}/v1/${project}/remote/files/b/${bucket}/f/${fileHash}.${fileExt}`, {
|
2557
2537
|
method: "PUT",
|
2558
2538
|
headers: {
|
2559
2539
|
...authHeader,
|
@@ -2566,8 +2546,7 @@ async function uploadRemoteRef(fileBuffer, ref, auth) {
|
|
2566
2546
|
if (res.status === 409) {
|
2567
2547
|
// File already exists
|
2568
2548
|
return {
|
2569
|
-
success: true
|
2570
|
-
ref
|
2549
|
+
success: true
|
2571
2550
|
};
|
2572
2551
|
}
|
2573
2552
|
if ((_res$headers$get = res.headers.get("content-type")) !== null && _res$headers$get !== void 0 && _res$headers$get.includes("application/json")) {
|
@@ -2575,30 +2554,35 @@ async function uploadRemoteRef(fileBuffer, ref, auth) {
|
|
2575
2554
|
if (json.message) {
|
2576
2555
|
return {
|
2577
2556
|
success: false,
|
2578
|
-
error:
|
2557
|
+
error: `Failed to upload remote file: ${json.message}.`
|
2579
2558
|
};
|
2580
2559
|
} else {
|
2581
2560
|
return {
|
2582
2561
|
success: false,
|
2583
|
-
error:
|
2562
|
+
error: `Failed to upload remote file: ${JSON.stringify(json)}.`
|
2584
2563
|
};
|
2585
2564
|
}
|
2586
2565
|
}
|
2587
2566
|
return {
|
2588
2567
|
success: false,
|
2589
|
-
error:
|
2568
|
+
error: `An unexpected error occurred while uploading remote file. HTTP status was: ${await res.text()}.`
|
2590
2569
|
};
|
2591
2570
|
}
|
2592
2571
|
return {
|
2593
|
-
success: true
|
2594
|
-
ref
|
2572
|
+
success: true
|
2595
2573
|
};
|
2596
2574
|
}
|
2597
2575
|
|
2576
|
+
function getFileExt(filePath) {
|
2577
|
+
// NOTE: We do not import the path module. This code is copied in different projects. We want the same implementation and which means that this might running in browser where path is not available).
|
2578
|
+
return filePath.split(".").pop() || "";
|
2579
|
+
}
|
2580
|
+
|
2598
2581
|
class ValOpsFS extends ValOps {
|
2599
2582
|
static VAL_DIR = ".val";
|
2600
|
-
constructor(rootDir, valModules, options) {
|
2583
|
+
constructor(contentUrl, rootDir, valModules, options) {
|
2601
2584
|
super(valModules, options);
|
2585
|
+
this.contentUrl = contentUrl;
|
2602
2586
|
this.rootDir = rootDir;
|
2603
2587
|
this.host = new FSOpsHost();
|
2604
2588
|
}
|
@@ -3207,6 +3191,7 @@ class ValOpsFS extends ValOps {
|
|
3207
3191
|
for (const [ref, {
|
3208
3192
|
patchId
|
3209
3193
|
}] of remoteFileDescriptors) {
|
3194
|
+
var _this$options4;
|
3210
3195
|
const splitRemoteRefRes = Internal.remote.splitRemoteRef(ref);
|
3211
3196
|
if (splitRemoteRefRes.status === "error") {
|
3212
3197
|
errors[ref] = {
|
@@ -3225,8 +3210,14 @@ class ValOpsFS extends ValOps {
|
|
3225
3210
|
console.log("Skip remote flag enabled. Skipping file upload", ref);
|
3226
3211
|
continue;
|
3227
3212
|
}
|
3213
|
+
if (!((_this$options4 = this.options) !== null && _this$options4 !== void 0 && _this$options4.config.project)) {
|
3214
|
+
errors[ref] = {
|
3215
|
+
message: "No project found in config"
|
3216
|
+
};
|
3217
|
+
continue;
|
3218
|
+
}
|
3228
3219
|
console.log("Uploading remote file", ref);
|
3229
|
-
const res = await
|
3220
|
+
const res = await uploadRemoteFile(this.contentUrl, this.options.config.project, splitRemoteRefRes.bucket, splitRemoteRefRes.fileHash, getFileExt(splitRemoteRefRes.filePath), fileBuffer, auth);
|
3230
3221
|
if (!res.success) {
|
3231
3222
|
console.error("Failed to upload remote file", ref, res.error);
|
3232
3223
|
throw new Error(`Failed to upload remote file: ${ref}. ${res.error}`);
|
@@ -4412,7 +4403,7 @@ async function getSettings(projectName, auth) {
|
|
4412
4403
|
if (response.status === 404) {
|
4413
4404
|
return {
|
4414
4405
|
success: false,
|
4415
|
-
message: `Project '${projectName}' not found:
|
4406
|
+
message: `Project '${projectName}' not found: that the name of the project is correct and that you have access to it.`
|
4416
4407
|
};
|
4417
4408
|
}
|
4418
4409
|
if (response.status !== 200) {
|
@@ -4496,10 +4487,9 @@ function parsePersonalAccessTokenFile(content) {
|
|
4496
4487
|
|
4497
4488
|
/* eslint-disable @typescript-eslint/no-unused-vars */
|
4498
4489
|
const ValServer = (valModules, options, callbacks) => {
|
4499
|
-
process.env.VAL_REMOTE_HOST || DEFAULT_VAL_REMOTE_HOST;
|
4500
4490
|
let serverOps;
|
4501
4491
|
if (options.mode === "fs") {
|
4502
|
-
serverOps = new ValOpsFS(options.cwd, valModules, {
|
4492
|
+
serverOps = new ValOpsFS(options.valContentUrl, options.cwd, valModules, {
|
4503
4493
|
formatter: options.formatter,
|
4504
4494
|
config: options.config
|
4505
4495
|
});
|
@@ -5076,7 +5066,7 @@ const ValServer = (valModules, options, callbacks) => {
|
|
5076
5066
|
status: 400,
|
5077
5067
|
json: {
|
5078
5068
|
errorCode: "error-could-not-get-settings",
|
5079
|
-
message: `Could not get settings: ${settingsRes.message}`
|
5069
|
+
message: `Could not get settings id: ${settingsRes.message}`
|
5080
5070
|
}
|
5081
5071
|
};
|
5082
5072
|
}
|
@@ -6067,12 +6057,12 @@ async function initHandlerOptions(route, opts, config) {
|
|
6067
6057
|
const valDisableRedirectUrl = opts.valDisableRedirectUrl || process.env.VAL_DISABLE_REDIRECT_URL;
|
6068
6058
|
const maybeValProject = opts.project || process.env.VAL_PROJECT;
|
6069
6059
|
const valBuildUrl = opts.valBuildUrl || process.env.VAL_BUILD_URL || "https://app.val.build";
|
6060
|
+
const valContentUrl = opts.valContentUrl || process.env.VAL_CONTENT_URL || "https://content.val.build";
|
6070
6061
|
if (isProxyMode) {
|
6071
6062
|
var _opts$versions, _opts$versions2;
|
6072
6063
|
if (!maybeApiKey || !maybeValSecret) {
|
6073
6064
|
throw new Error("VAL_API_KEY and VAL_SECRET env vars must both be set in proxy mode");
|
6074
6065
|
}
|
6075
|
-
const valContentUrl = opts.valContentUrl || process.env.VAL_CONTENT_URL || "https://content.val.build";
|
6076
6066
|
const maybeGitCommit = opts.gitCommit || process.env.VAL_GIT_COMMIT;
|
6077
6067
|
if (!maybeGitCommit) {
|
6078
6068
|
throw new Error("VAL_GIT_COMMIT env var must be set in proxy mode");
|
@@ -6117,6 +6107,7 @@ async function initHandlerOptions(route, opts, config) {
|
|
6117
6107
|
valDisableRedirectUrl,
|
6118
6108
|
valEnableRedirectUrl,
|
6119
6109
|
valBuildUrl,
|
6110
|
+
valContentUrl,
|
6120
6111
|
apiKey: maybeApiKey,
|
6121
6112
|
valSecret: maybeValSecret,
|
6122
6113
|
project: maybeValProject,
|
@@ -6763,15 +6754,23 @@ async function createFixPatch(config, apply, sourcePath, validationError, remote
|
|
6763
6754
|
}
|
6764
6755
|
}
|
6765
6756
|
} else if (fix === "image:add-metadata") {
|
6766
|
-
|
6767
|
-
|
6768
|
-
|
6769
|
-
|
6770
|
-
|
6771
|
-
|
6772
|
-
|
6773
|
-
|
6774
|
-
|
6757
|
+
if (!imageMetadata.mimeType) {
|
6758
|
+
remainingErrors.push({
|
6759
|
+
...validationError,
|
6760
|
+
message: "Failed to get image metadata",
|
6761
|
+
fixes: undefined
|
6762
|
+
});
|
6763
|
+
} else {
|
6764
|
+
patch.push({
|
6765
|
+
op: "add",
|
6766
|
+
path: sourceToPatchPath(sourcePath).concat("metadata"),
|
6767
|
+
value: {
|
6768
|
+
width: imageMetadata.width,
|
6769
|
+
height: imageMetadata.height,
|
6770
|
+
mimeType: imageMetadata.mimeType
|
6771
|
+
}
|
6772
|
+
});
|
6773
|
+
}
|
6775
6774
|
}
|
6776
6775
|
} else if (fix === "file:add-metadata" || fix === "file:check-metadata") {
|
6777
6776
|
const fileMetadata = await getFileMetadata(config.projectRoot, validationError);
|
@@ -6833,7 +6832,21 @@ async function createFixPatch(config, apply, sourcePath, validationError, remote
|
|
6833
6832
|
}
|
6834
6833
|
} else if (fix === "image:upload-remote" || fix === "file:upload-remote") {
|
6835
6834
|
const remoteFile = remoteFiles[sourcePath];
|
6836
|
-
|
6835
|
+
let metadata = remoteFile.metadata;
|
6836
|
+
if (!metadata) {
|
6837
|
+
if (fix === "image:upload-remote") {
|
6838
|
+
metadata = await getImageMetadata(config.projectRoot, validationError);
|
6839
|
+
} else if (fix === "file:upload-remote") {
|
6840
|
+
metadata = await getFileMetadata(config.projectRoot, validationError);
|
6841
|
+
}
|
6842
|
+
}
|
6843
|
+
if (!metadata) {
|
6844
|
+
remainingErrors.push({
|
6845
|
+
...validationError,
|
6846
|
+
message: "Failed to get metadata for remote file",
|
6847
|
+
fixes: undefined
|
6848
|
+
});
|
6849
|
+
} else if (!remoteFile) {
|
6837
6850
|
remainingErrors.push({
|
6838
6851
|
...validationError,
|
6839
6852
|
message: "Cannot fix local to remote image: remote image was not uploaded",
|
@@ -6845,7 +6858,7 @@ async function createFixPatch(config, apply, sourcePath, validationError, remote
|
|
6845
6858
|
value: {
|
6846
6859
|
_type: "remote",
|
6847
6860
|
_ref: remoteFile.ref,
|
6848
|
-
metadata
|
6861
|
+
metadata
|
6849
6862
|
},
|
6850
6863
|
path: sourceToPatchPath(sourcePath)
|
6851
6864
|
});
|
package/package.json
CHANGED
@@ -12,7 +12,7 @@
|
|
12
12
|
"./package.json": "./package.json"
|
13
13
|
},
|
14
14
|
"types": "dist/valbuild-server.cjs.d.ts",
|
15
|
-
"version": "0.
|
15
|
+
"version": "0.74.0",
|
16
16
|
"scripts": {
|
17
17
|
"typecheck": "tsc --noEmit",
|
18
18
|
"test": "jest",
|
@@ -23,9 +23,9 @@
|
|
23
23
|
"@types/jest": "^29.2.5"
|
24
24
|
},
|
25
25
|
"dependencies": {
|
26
|
-
"@valbuild/core": "~0.
|
27
|
-
"@valbuild/shared": "~0.
|
28
|
-
"@valbuild/ui": "~0.
|
26
|
+
"@valbuild/core": "~0.74.0",
|
27
|
+
"@valbuild/shared": "~0.74.0",
|
28
|
+
"@valbuild/ui": "~0.74.0",
|
29
29
|
"chokidar": "^4.0.1",
|
30
30
|
"image-size": "^1.0.2",
|
31
31
|
"minimatch": "^3.0.4",
|