@valbuild/server 0.73.0 → 0.73.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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
- import { SerializedFileSchema, SerializedImageSchema } from "@valbuild/core";
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$3 = new 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$3.encode(input));
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$3.encode(collector.join("")));
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$3.encode(value));
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 getFileExt(filePath) {
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(ref, {
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: `${ref}. Failed to upload file: ${json.message}.`
2588
+ error: `Failed to upload remote file: ${json.message}.`
2610
2589
  };
2611
2590
  } else {
2612
2591
  return {
2613
2592
  success: false,
2614
- error: `${ref}. Failed to upload file: ${JSON.stringify(json)}.`
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: `${ref}. Failed to upload file: ${await res.text()}.`
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 uploadRemoteRef(fileBuffer, ref, auth);
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. Verify that user has access to project and that it exists.`
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
- patch$1.push({
6798
- op: "add",
6799
- path: patch.sourceToPatchPath(sourcePath).concat("metadata"),
6800
- value: {
6801
- width: imageMetadata.width,
6802
- height: imageMetadata.height,
6803
- mimeType: imageMetadata.mimeType
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
- if (!remoteFile) {
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: remoteFile.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$3 = new 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$3.encode(input));
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$3.encode(collector.join("")));
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$3.encode(value));
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 getFileExt(filePath) {
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(ref, {
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: `${ref}. Failed to upload file: ${json.message}.`
2588
+ error: `Failed to upload remote file: ${json.message}.`
2610
2589
  };
2611
2590
  } else {
2612
2591
  return {
2613
2592
  success: false,
2614
- error: `${ref}. Failed to upload file: ${JSON.stringify(json)}.`
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: `${ref}. Failed to upload file: ${await res.text()}.`
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 uploadRemoteRef(fileBuffer, ref, auth);
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. Verify that user has access to project and that it exists.`
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
- patch$1.push({
6798
- op: "add",
6799
- path: patch.sourceToPatchPath(sourcePath).concat("metadata"),
6800
- value: {
6801
- width: imageMetadata.width,
6802
- height: imageMetadata.height,
6803
- mimeType: imageMetadata.mimeType
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
- if (!remoteFile) {
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: remoteFile.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, DEFAULT_VAL_REMOTE_HOST } from '@valbuild/core';
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$3 = new 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$3.encode(input));
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$3.encode(collector.join("")));
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$3.encode(value));
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 getFileExt(filePath) {
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(ref, {
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: `${ref}. Failed to upload file: ${json.message}.`
2557
+ error: `Failed to upload remote file: ${json.message}.`
2579
2558
  };
2580
2559
  } else {
2581
2560
  return {
2582
2561
  success: false,
2583
- error: `${ref}. Failed to upload file: ${JSON.stringify(json)}.`
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: `${ref}. Failed to upload file: ${await res.text()}.`
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 uploadRemoteRef(fileBuffer, ref, auth);
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. Verify that user has access to project and that it exists.`
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
- patch.push({
6767
- op: "add",
6768
- path: sourceToPatchPath(sourcePath).concat("metadata"),
6769
- value: {
6770
- width: imageMetadata.width,
6771
- height: imageMetadata.height,
6772
- mimeType: imageMetadata.mimeType
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
- if (!remoteFile) {
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: remoteFile.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.73.0",
15
+ "version": "0.73.2",
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.73.0",
27
- "@valbuild/shared": "~0.73.0",
28
- "@valbuild/ui": "~0.73.0",
26
+ "@valbuild/core": "~0.73.2",
27
+ "@valbuild/shared": "~0.73.2",
28
+ "@valbuild/ui": "~0.73.2",
29
29
  "chokidar": "^4.0.1",
30
30
  "image-size": "^1.0.2",
31
31
  "minimatch": "^3.0.4",