@valbuild/server 0.74.0 → 0.75.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.
@@ -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 } from '@valbuild/core';
4
+ import { FILE_REF_PROP, FILE_REF_SUBTYPE_TAG, VAL_EXTENSION, derefPatch, Internal, Schema, ImageSchema, RichTextSchema, FileSchema, DEFAULT_CONTENT_HOST } 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';
@@ -1454,7 +1454,11 @@ const tsOps = new TSOps(document => {
1454
1454
  class ValOps {
1455
1455
  /** Sources from val modules, immutable (without patches or anything) */
1456
1456
 
1457
- /** The sha256 / hash of sources + schema + config */
1457
+ /** The sha256 / hash of all sources + all schemas + config */
1458
+
1459
+ /** The sha256 / hash of all sources */
1460
+
1461
+ /** The sha256 / hash of config */
1458
1462
 
1459
1463
  /** Schema from val modules, immutable */
1460
1464
 
@@ -1467,6 +1471,8 @@ class ValOps {
1467
1471
  this.baseSha = null;
1468
1472
  this.schemas = null;
1469
1473
  this.schemaSha = null;
1474
+ this.sourcesSha = null;
1475
+ this.configSha = null;
1470
1476
  this.modulesErrors = null;
1471
1477
  }
1472
1478
  hash(input) {
@@ -1524,7 +1530,7 @@ class ValOps {
1524
1530
 
1525
1531
  // #region initTree
1526
1532
  async initSources() {
1527
- if (this.baseSha === null || this.schemaSha === null || this.sources === null || this.schemas === null || this.modulesErrors === null) {
1533
+ if (this.baseSha === null || this.sourcesSha === null || this.configSha === null || this.schemaSha === null || this.sources === null || this.schemas === null || this.modulesErrors === null) {
1528
1534
  const currentModulesErrors = [];
1529
1535
  const addModuleError = (message, index, path) => {
1530
1536
  currentModulesErrors[index] = {
@@ -1534,8 +1540,10 @@ class ValOps {
1534
1540
  };
1535
1541
  const currentSources = {};
1536
1542
  const currentSchemas = {};
1537
- let baseSha = this.hash(JSON.stringify(this.valModules.config));
1538
- let schemaSha = baseSha;
1543
+ const configSha = this.hash(JSON.stringify(this.valModules.config));
1544
+ let sourcesSha = "";
1545
+ let baseSha = configSha;
1546
+ let schemaSha = configSha;
1539
1547
  for (let moduleIdx = 0; moduleIdx < this.valModules.modules.length; moduleIdx++) {
1540
1548
  const module = this.valModules.modules[moduleIdx];
1541
1549
  if (!module.def) {
@@ -1590,6 +1598,10 @@ class ValOps {
1590
1598
  currentSources[pathM] = source;
1591
1599
  currentSchemas[pathM] = schema;
1592
1600
  // make sure the checks above is enough that this does not fail - even if val modules are not set up correctly
1601
+ sourcesSha = this.hash(sourcesSha + JSON.stringify({
1602
+ path,
1603
+ source
1604
+ }));
1593
1605
  baseSha = this.hash(baseSha + JSON.stringify({
1594
1606
  path,
1595
1607
  schema: serializedSchema,
@@ -1603,11 +1615,15 @@ class ValOps {
1603
1615
  this.schemas = currentSchemas;
1604
1616
  this.baseSha = baseSha;
1605
1617
  this.schemaSha = schemaSha;
1618
+ this.sourcesSha = sourcesSha;
1619
+ this.configSha = configSha;
1606
1620
  this.modulesErrors = currentModulesErrors;
1607
1621
  }
1608
1622
  return {
1609
1623
  baseSha: this.baseSha,
1610
1624
  schemaSha: this.schemaSha,
1625
+ sourcesSha: this.sourcesSha,
1626
+ configSha: this.configSha,
1611
1627
  sources: this.sources,
1612
1628
  schemas: this.schemas,
1613
1629
  moduleErrors: this.modulesErrors
@@ -1632,6 +1648,12 @@ class ValOps {
1632
1648
  async getBaseSha() {
1633
1649
  return this.initSources().then(result => result.baseSha);
1634
1650
  }
1651
+ async getConfigSha() {
1652
+ return this.initSources().then(result => result.configSha);
1653
+ }
1654
+ async getSourcesSha() {
1655
+ return this.initSources().then(result => result.sourcesSha);
1656
+ }
1635
1657
  async getSchemaSha() {
1636
1658
  return this.initSources().then(result => result.schemaSha);
1637
1659
  }
@@ -2191,7 +2213,7 @@ class ValOps {
2191
2213
  }
2192
2214
 
2193
2215
  // #region createPatch
2194
- async createPatch(path, patch, parentRef, authorId) {
2216
+ async createPatch(path, patch, patchId, parentRef, authorId) {
2195
2217
  const initTree = await this.initSources();
2196
2218
  const schemas = initTree.schemas;
2197
2219
  const moduleErrors = initTree.moduleErrors;
@@ -2280,7 +2302,7 @@ class ValOps {
2280
2302
  }
2281
2303
  }
2282
2304
  }
2283
- const saveRes = await this.saveSourceFilePatch(path, patch, parentRef, authorId);
2305
+ const saveRes = await this.saveSourceFilePatch(path, patch, patchId, parentRef, authorId);
2284
2306
  if (result.isErr(saveRes)) {
2285
2307
  console.error(`Could not save source patch at path: '${path}'. Error: ${saveRes.error.errorType === "other" ? saveRes.error.message : saveRes.error.errorType}`);
2286
2308
  if (saveRes.error.errorType === "patch-head-conflict") {
@@ -2293,7 +2315,6 @@ class ValOps {
2293
2315
  error: saveRes.error
2294
2316
  });
2295
2317
  }
2296
- const patchId = saveRes.value.patchId;
2297
2318
  const saveFileRes = await Promise.all(Object.entries(files).map(async ([filePath, data]) => {
2298
2319
  if (data.error) {
2299
2320
  return {
@@ -2603,6 +2624,7 @@ class ValOpsFS extends ValOps {
2603
2624
  var _this$options, _this$options2;
2604
2625
  const currentBaseSha = await this.getBaseSha();
2605
2626
  const currentSchemaSha = await this.getSchemaSha();
2627
+ const currentSourcesSha = await this.getSourcesSha();
2606
2628
  const moduleFilePaths = Object.keys(await this.getSchemas());
2607
2629
  const patchData = await this.readPatches();
2608
2630
  const patches = [];
@@ -2613,12 +2635,15 @@ class ValOpsFS extends ValOps {
2613
2635
  patches.push(patchId);
2614
2636
  }
2615
2637
  // something changed: return immediately
2616
- const didChange = !params || currentBaseSha !== params.baseSha || currentSchemaSha !== params.schemaSha || patches.length !== params.patches.length || patches.some((p, i) => p !== params.patches[i]);
2638
+ const didChange = !params || currentBaseSha !== params.baseSha ||
2639
+ // base sha covers both sources sha and schema sha, so we could remove checks for schema sha and sources sha
2640
+ currentSourcesSha !== params.sourcesSha || currentSchemaSha !== params.schemaSha || patches.length !== params.patches.length || patches.some((p, i) => p !== params.patches[i]);
2617
2641
  if (didChange) {
2618
2642
  return {
2619
2643
  type: "did-change",
2620
2644
  baseSha: currentBaseSha,
2621
2645
  schemaSha: currentSchemaSha,
2646
+ sourcesSha: currentSourcesSha,
2622
2647
  patches
2623
2648
  };
2624
2649
  }
@@ -2732,6 +2757,7 @@ class ValOpsFS extends ValOps {
2732
2757
  type,
2733
2758
  baseSha: currentBaseSha,
2734
2759
  schemaSha: currentSchemaSha,
2760
+ sourcesSha: currentSourcesSha,
2735
2761
  patches
2736
2762
  };
2737
2763
  } catch (err) {
@@ -2806,6 +2832,11 @@ class ValOpsFS extends ValOps {
2806
2832
  };
2807
2833
  }
2808
2834
  return patchData;
2835
+ }).filter(patchData => {
2836
+ if (filters.patchIds && filters.patchIds.length > 0) {
2837
+ return filters.patchIds.includes(patchData.patchId);
2838
+ }
2839
+ return true;
2809
2840
  });
2810
2841
  return {
2811
2842
  patches: sortedPatches,
@@ -2942,11 +2973,10 @@ class ValOpsFS extends ValOps {
2942
2973
  };
2943
2974
  }
2944
2975
  }
2945
- async saveSourceFilePatch(path, patch, parentRef, authorId) {
2976
+ async saveSourceFilePatch(path, patch, patchId, parentRef, authorId) {
2946
2977
  const patchDir = this.getParentPatchIdFromParentRef(parentRef);
2947
2978
  try {
2948
2979
  const baseSha = await this.getBaseSha();
2949
- const patchId = crypto.randomUUID();
2950
2980
  const data = {
2951
2981
  patch,
2952
2982
  patchId,
@@ -2957,6 +2987,13 @@ class ValOpsFS extends ValOps {
2957
2987
  coreVersion: Internal.VERSION.core,
2958
2988
  createdAt: new Date().toISOString()
2959
2989
  };
2990
+ const headParentPatchId = "head";
2991
+ if (patchDir !== headParentPatchId && !this.host.fileExists(this.getPatchFilePath(headParentPatchId))) {
2992
+ console.error("Val: out-of-band patch detected.", this.getPatchFilePath(headParentPatchId));
2993
+ return result.err({
2994
+ errorType: "patch-head-conflict"
2995
+ });
2996
+ }
2960
2997
  const writeRes = this.host.tryWriteUf8File(this.getPatchFilePath(patchDir), JSON.stringify(data));
2961
2998
  if (writeRes.type === "error") {
2962
2999
  return writeRes.errorType === "dir-already-exists" ? result.err({
@@ -3128,6 +3165,28 @@ class ValOpsFS extends ValOps {
3128
3165
  deleted
3129
3166
  };
3130
3167
  }
3168
+ async deleteAllPatches() {
3169
+ const patchesCacheDir = this.getPatchesDir();
3170
+ const tmpDir = fsPath__default.join(this.rootDir, ValOpsFS.VAL_DIR, "patches-deleted-" + crypto.randomUUID());
3171
+ try {
3172
+ this.host.moveDir(patchesCacheDir, tmpDir);
3173
+ this.host.deleteDir(tmpDir);
3174
+ return {};
3175
+ } catch (err) {
3176
+ if (err instanceof Error) {
3177
+ return {
3178
+ error: {
3179
+ message: `Got an error while deleting patches: ${err.message}`
3180
+ }
3181
+ };
3182
+ }
3183
+ return {
3184
+ error: {
3185
+ message: "Got an unexpected error while deleting patches"
3186
+ }
3187
+ };
3188
+ }
3189
+ }
3131
3190
  updateOrderedPatches(updates, patchDirMap, deletePatchIds) {
3132
3191
  for (const patchId of deletePatchIds) {
3133
3192
  const patchDir = patchDirMap[patchId];
@@ -3170,7 +3229,7 @@ class ValOpsFS extends ValOps {
3170
3229
  }
3171
3230
  }
3172
3231
  }
3173
- async saveOrUploadFiles(preparedCommit, auth, testMode) {
3232
+ async saveOrUploadFiles(preparedCommit, mode, auth) {
3174
3233
  const updatedFiles = [];
3175
3234
  const uploadedRemoteRefs = [];
3176
3235
  const errors = {};
@@ -3188,42 +3247,50 @@ class ValOpsFS extends ValOps {
3188
3247
  }]) => [ref, {
3189
3248
  patchId
3190
3249
  }]);
3191
- for (const [ref, {
3192
- patchId
3193
- }] of remoteFileDescriptors) {
3194
- var _this$options4;
3195
- const splitRemoteRefRes = Internal.remote.splitRemoteRef(ref);
3196
- if (splitRemoteRefRes.status === "error") {
3197
- errors[ref] = {
3198
- message: "Failed to split remote ref: " + ref
3250
+ if (mode === "upload-remote") {
3251
+ if (!auth) {
3252
+ errors["auth"] = {
3253
+ message: "No auth provided"
3199
3254
  };
3200
- continue;
3201
- }
3202
- const fileBuffer = await this.getBase64EncodedBinaryFileFromPatch(splitRemoteRefRes.filePath, patchId);
3203
- if (!fileBuffer) {
3204
- errors[ref] = {
3205
- message: "Failed to get binary file from patch. Ref: " + ref + ". PatchId: " + patchId
3206
- };
3207
- continue;
3208
- }
3209
- if (testMode === "test-skip-remote") {
3210
- console.log("Skip remote flag enabled. Skipping file upload", ref);
3211
- continue;
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"
3255
+ return {
3256
+ updatedFiles,
3257
+ uploadedRemoteRefs,
3258
+ errors
3216
3259
  };
3217
- continue;
3218
3260
  }
3219
- console.log("Uploading remote file", ref);
3220
- const res = await uploadRemoteFile(this.contentUrl, this.options.config.project, splitRemoteRefRes.bucket, splitRemoteRefRes.fileHash, getFileExt(splitRemoteRefRes.filePath), fileBuffer, auth);
3221
- if (!res.success) {
3222
- console.error("Failed to upload remote file", ref, res.error);
3223
- throw new Error(`Failed to upload remote file: ${ref}. ${res.error}`);
3261
+ for (const [ref, {
3262
+ patchId
3263
+ }] of remoteFileDescriptors) {
3264
+ var _this$options4;
3265
+ const splitRemoteRefRes = Internal.remote.splitRemoteRef(ref);
3266
+ if (splitRemoteRefRes.status === "error") {
3267
+ errors[ref] = {
3268
+ message: "Failed to split remote ref: " + ref
3269
+ };
3270
+ continue;
3271
+ }
3272
+ const fileBuffer = await this.getBase64EncodedBinaryFileFromPatch(splitRemoteRefRes.filePath, patchId);
3273
+ if (!fileBuffer) {
3274
+ errors[ref] = {
3275
+ message: "Failed to get binary file from patch. Ref: " + ref + ". PatchId: " + patchId
3276
+ };
3277
+ continue;
3278
+ }
3279
+ if (!((_this$options4 = this.options) !== null && _this$options4 !== void 0 && _this$options4.config.project)) {
3280
+ errors[ref] = {
3281
+ message: "No project found in config"
3282
+ };
3283
+ continue;
3284
+ }
3285
+ console.log("Uploading remote file", ref);
3286
+ const res = await uploadRemoteFile(this.contentUrl, this.options.config.project, splitRemoteRefRes.bucket, splitRemoteRefRes.fileHash, getFileExt(splitRemoteRefRes.filePath), fileBuffer, auth);
3287
+ if (!res.success) {
3288
+ console.error("Failed to upload remote file", ref, res.error);
3289
+ throw new Error(`Failed to upload remote file: ${ref}. ${res.error}`);
3290
+ }
3291
+ console.log("Completed remote file", ref);
3292
+ uploadedRemoteRefs.push(ref);
3224
3293
  }
3225
- console.log("Completed remote file", ref);
3226
- uploadedRemoteRefs.push(ref);
3227
3294
  }
3228
3295
  const patchIdToPatchDirMapRes = await this.getParentPatchIdFromPatchIdMap();
3229
3296
  if (result.isErr(patchIdToPatchDirMapRes)) {
@@ -3522,9 +3589,17 @@ const GetApplicablePatches = z.object({
3522
3589
  commitSha: z.string(),
3523
3590
  clientCommitSha: z.string(),
3524
3591
  parentCommitSha: z.string(),
3592
+ commitMessage: z.string().nullable(),
3525
3593
  branch: z.string(),
3526
3594
  creator: z.string(),
3527
3595
  createdAt: z.string()
3596
+ })).optional(),
3597
+ deployments: z.array(z.object({
3598
+ deploymentId: z.string(),
3599
+ commitSha: z.string(),
3600
+ deploymentState: z.string(),
3601
+ createdAt: z.string(),
3602
+ updatedAt: z.string()
3528
3603
  })).optional()
3529
3604
  });
3530
3605
  const FilesResponse = z.object({
@@ -3576,6 +3651,8 @@ const ProfilesResponse = z.object({
3576
3651
  profiles: z.array(z.object({
3577
3652
  profileId: z.string(),
3578
3653
  fullName: z.string(),
3654
+ email: z.string().optional(),
3655
+ // TODO: make this required once this can be guaranteed
3579
3656
  avatar: z.object({
3580
3657
  url: z.string()
3581
3658
  }).nullable()
@@ -3673,6 +3750,7 @@ class ValOpsHttp extends ValOps {
3673
3750
  }
3674
3751
  const currentBaseSha = await this.getBaseSha();
3675
3752
  const currentSchemaSha = await this.getSchemaSha();
3753
+ const currentSourcesSha = await this.getSourcesSha();
3676
3754
  const allPatchData = await this.fetchPatches({
3677
3755
  excludePatchOps: true,
3678
3756
  patchIds: undefined
@@ -3732,6 +3810,9 @@ class ValOpsHttp extends ValOps {
3732
3810
  nonce,
3733
3811
  baseSha: currentBaseSha,
3734
3812
  schemaSha: currentSchemaSha,
3813
+ sourcesSha: currentSourcesSha,
3814
+ commits: allPatchData.commits || [],
3815
+ deployments: allPatchData.deployments || [],
3735
3816
  patches,
3736
3817
  commitSha: this.commitSha
3737
3818
  };
@@ -3741,7 +3822,8 @@ class ValOpsHttp extends ValOps {
3741
3822
  method: "POST",
3742
3823
  body: JSON.stringify({
3743
3824
  branch: this.branch,
3744
- profileId
3825
+ profileId,
3826
+ commitSha: this.commitSha
3745
3827
  }),
3746
3828
  headers: {
3747
3829
  ...this.authHeaders,
@@ -3881,12 +3963,26 @@ class ValOpsHttp extends ValOps {
3881
3963
  parentCommitSha: commit.parentCommitSha,
3882
3964
  branch: commit.branch,
3883
3965
  creator: commit.creator,
3884
- createdAt: commit.createdAt
3966
+ createdAt: commit.createdAt,
3967
+ commitMessage: commit.commitMessage
3968
+ });
3969
+ }
3970
+ }
3971
+ const deployments = [];
3972
+ if (data.deployments) {
3973
+ for (const deployment of data.deployments) {
3974
+ deployments.push({
3975
+ commitSha: deployment.commitSha,
3976
+ deploymentId: deployment.deploymentId,
3977
+ deploymentState: deployment.deploymentState,
3978
+ createdAt: deployment.createdAt,
3979
+ updatedAt: deployment.updatedAt
3885
3980
  });
3886
3981
  }
3887
3982
  }
3888
3983
  return {
3889
3984
  commits,
3985
+ deployments,
3890
3986
  patches,
3891
3987
  errors
3892
3988
  };
@@ -3925,7 +4021,7 @@ class ValOpsHttp extends ValOps {
3925
4021
  };
3926
4022
  }
3927
4023
  }
3928
- async saveSourceFilePatch(path, patch, parentRef, authorId) {
4024
+ async saveSourceFilePatch(path, patch, patchId, parentRef, authorId) {
3929
4025
  const baseSha = await this.getBaseSha();
3930
4026
  return fetch(`${this.contentUrl}/v1/${this.project}/patches`, {
3931
4027
  method: "POST",
@@ -3937,6 +4033,7 @@ class ValOpsHttp extends ValOps {
3937
4033
  path,
3938
4034
  patch,
3939
4035
  authorId,
4036
+ patchId,
3940
4037
  parentPatchId: parentRef.type === "patch" ? parentRef.patchId : null,
3941
4038
  baseSha,
3942
4039
  commit: this.commitSha,
@@ -4382,7 +4479,7 @@ class ValOpsHttp extends ValOps {
4382
4479
  }
4383
4480
  }
4384
4481
 
4385
- const host = process.env.VAL_CONTENT_URL || "https://content.val.build";
4482
+ const host = process.env.VAL_CONTENT_URL || DEFAULT_CONTENT_HOST;
4386
4483
  const SettingsSchema = z.object({
4387
4484
  publicProjectId: z.string(),
4388
4485
  remoteFileBuckets: z.array(z.object({
@@ -4485,6 +4582,51 @@ function parsePersonalAccessTokenFile(content) {
4485
4582
  }
4486
4583
  }
4487
4584
 
4585
+ /**
4586
+ * Iterates through all schemas and find if there is 1 or more remote files in them
4587
+ */
4588
+ function hasRemoteFileSchema(schema) {
4589
+ if (schema.type === "file" || schema.type === "image") {
4590
+ return !!schema.remote;
4591
+ } else if (schema.type === "richtext") {
4592
+ var _schema$options;
4593
+ if (typeof ((_schema$options = schema.options) === null || _schema$options === void 0 || (_schema$options = _schema$options.inline) === null || _schema$options === void 0 ? void 0 : _schema$options.img) === "object") {
4594
+ return hasRemoteFileSchema(schema.options.inline.img);
4595
+ }
4596
+ return false;
4597
+ } else if (schema.type === "array" || schema.type === "record") {
4598
+ return hasRemoteFileSchema(schema.item);
4599
+ } else if (schema.type === "object") {
4600
+ for (const key in schema.items) {
4601
+ const hasRemoteFile = hasRemoteFileSchema(schema.items[key]);
4602
+ if (hasRemoteFile) {
4603
+ return true;
4604
+ }
4605
+ }
4606
+ return false;
4607
+ } else if (schema.type === "union") {
4608
+ const unionStringSchema = typeof schema.key === "object" && schema.key.type === "literal" ? schema : undefined;
4609
+ const unionObjectSchema = typeof schema.key === "string" ? schema : undefined;
4610
+ if (unionStringSchema) {
4611
+ return false;
4612
+ }
4613
+ if (unionObjectSchema) {
4614
+ for (const key in unionObjectSchema.items) {
4615
+ const hasRemoteFile = hasRemoteFileSchema(unionObjectSchema.items[key]);
4616
+ if (hasRemoteFile) {
4617
+ return true;
4618
+ }
4619
+ }
4620
+ }
4621
+ return false;
4622
+ } else if (schema.type === "boolean" || schema.type === "number" || schema.type === "string" || schema.type === "literal" || schema.type === "date" || schema.type === "keyOf") {
4623
+ return false;
4624
+ } else {
4625
+ const exhaustiveCheck = schema;
4626
+ throw new Error(`Unexpected schema: ${JSON.stringify(exhaustiveCheck)}`);
4627
+ }
4628
+ }
4629
+
4488
4630
  /* eslint-disable @typescript-eslint/no-unused-vars */
4489
4631
  const ValServer = (valModules, options, callbacks) => {
4490
4632
  let serverOps;
@@ -4655,7 +4797,7 @@ const ValServer = (valModules, options, callbacks) => {
4655
4797
  apiKey: options.apiKey
4656
4798
  };
4657
4799
  } else if (serverOps instanceof ValOpsFS) {
4658
- const projectRootDir = options.config.root;
4800
+ const projectRootDir = options.config.root || ".";
4659
4801
  if (!projectRootDir) {
4660
4802
  return {
4661
4803
  status: 400,
@@ -5009,15 +5151,59 @@ const ValServer = (valModules, options, callbacks) => {
5009
5151
  }
5010
5152
  },
5011
5153
  "/logout": {
5012
- GET: async () => {
5154
+ GET: async req => {
5155
+ const query = req.query;
5156
+ const redirectTo = query.redirect_to;
5157
+ if (redirectTo) {
5158
+ return {
5159
+ status: 302,
5160
+ redirectTo: redirectTo,
5161
+ cookies: {
5162
+ [VAL_SESSION_COOKIE]: {
5163
+ value: "empty",
5164
+ options: {
5165
+ httpOnly: true,
5166
+ sameSite: "strict",
5167
+ path: "/",
5168
+ secure: true,
5169
+ expires: new Date(0)
5170
+ }
5171
+ },
5172
+ [VAL_STATE_COOKIE]: {
5173
+ value: "empty",
5174
+ options: {
5175
+ httpOnly: true,
5176
+ sameSite: "strict",
5177
+ path: "/",
5178
+ secure: true,
5179
+ expires: new Date(0)
5180
+ }
5181
+ }
5182
+ }
5183
+ };
5184
+ }
5013
5185
  return {
5014
5186
  status: 200,
5015
5187
  cookies: {
5016
5188
  [VAL_SESSION_COOKIE]: {
5017
- value: null
5189
+ value: "empty",
5190
+ options: {
5191
+ httpOnly: true,
5192
+ sameSite: "strict",
5193
+ path: "/",
5194
+ secure: true,
5195
+ expires: new Date(0)
5196
+ }
5018
5197
  },
5019
5198
  [VAL_STATE_COOKIE]: {
5020
- value: null
5199
+ value: "empty",
5200
+ options: {
5201
+ httpOnly: true,
5202
+ sameSite: "strict",
5203
+ path: "/",
5204
+ secure: true,
5205
+ expires: new Date(0)
5206
+ }
5021
5207
  }
5022
5208
  }
5023
5209
  };
@@ -5101,9 +5287,10 @@ const ValServer = (valModules, options, callbacks) => {
5101
5287
  }
5102
5288
  };
5103
5289
  }
5290
+ const profileId = "id" in auth ? auth.id : undefined;
5104
5291
  const currentStat = await serverOps.getStat({
5105
5292
  ...req.body,
5106
- profileId: "id" in auth ? auth.id : undefined
5293
+ profileId
5107
5294
  });
5108
5295
  if (currentStat.type === "error" && currentStat.networkError) {
5109
5296
  return {
@@ -5127,11 +5314,21 @@ const ValServer = (valModules, options, callbacks) => {
5127
5314
  json: currentStat.error
5128
5315
  };
5129
5316
  }
5317
+ const mode = serverOps instanceof ValOpsFS ? "fs" : serverOps instanceof ValOpsHttp ? "http" : "unknown";
5318
+ if (mode === "unknown") {
5319
+ return {
5320
+ status: 500,
5321
+ json: {
5322
+ message: "Server mode is neither fs nor http - this is an internal Val bug"
5323
+ }
5324
+ };
5325
+ }
5130
5326
  return {
5131
5327
  status: 200,
5132
5328
  json: {
5133
5329
  ...currentStat,
5134
- mode: serverOps instanceof ValOpsFS ? "fs" : serverOps instanceof ValOpsHttp ? "http" : "unknown",
5330
+ profileId: profileId ?? null,
5331
+ mode,
5135
5332
  config: options.config
5136
5333
  }
5137
5334
  };
@@ -5163,7 +5360,7 @@ const ValServer = (valModules, options, callbacks) => {
5163
5360
  const authorId = "id" in auth ? auth.id : null;
5164
5361
  const newPatchIds = [];
5165
5362
  for (const patch of patches) {
5166
- const createPatchRes = await serverOps.createPatch(patch.path, patch.patch, parentRef, authorId);
5363
+ const createPatchRes = await serverOps.createPatch(patch.path, patch.patch, patch.patchId, parentRef, authorId);
5167
5364
  if (result.isErr(createPatchRes)) {
5168
5365
  if (createPatchRes.error.errorType === "patch-head-conflict") {
5169
5366
  return {
@@ -5397,6 +5594,7 @@ const ValServer = (valModules, options, callbacks) => {
5397
5594
  }
5398
5595
  };
5399
5596
  }
5597
+ const sourcesSha = await serverOps.getSourcesSha();
5400
5598
  const moduleErrors = await serverOps.getModuleErrors();
5401
5599
  if ((moduleErrors === null || moduleErrors === void 0 ? void 0 : moduleErrors.length) > 0) {
5402
5600
  console.error("Val: Module errors", moduleErrors);
@@ -5457,7 +5655,9 @@ const ValServer = (valModules, options, callbacks) => {
5457
5655
  const modules = {};
5458
5656
  for (const [moduleFilePathS, module] of Object.entries(sourcesRes.sources)) {
5459
5657
  const moduleFilePath = moduleFilePathS;
5460
- if (moduleFilePath.startsWith(moduleFilePath)) {
5658
+ // TODO: currently sourcesRes contains ALL MODULES.
5659
+ // We should only evaluate exactly what we need
5660
+ if (!req.path || moduleFilePath.startsWith(req.path)) {
5461
5661
  var _patchAnalysis$patche, _sourcesValidation$er;
5462
5662
  const skippedPatches = [];
5463
5663
  const patchErrors = {};
@@ -5494,6 +5694,7 @@ const ValServer = (valModules, options, callbacks) => {
5494
5694
  status: 200,
5495
5695
  json: {
5496
5696
  schemaSha,
5697
+ sourcesSha,
5497
5698
  modules
5498
5699
  }
5499
5700
  };
@@ -5622,33 +5823,33 @@ const ValServer = (valModules, options, callbacks) => {
5622
5823
  };
5623
5824
  }
5624
5825
  if (serverOps instanceof ValOpsFS) {
5625
- const remoteFileAuthRes = await getRemoteFileAuth();
5626
- if (remoteFileAuthRes.status !== 200) {
5826
+ var _remoteFileAuthRes;
5827
+ const isRemoteRequired = getIsRemoteRequired(await serverOps.getSchemas());
5828
+ let mode;
5829
+ let remoteFileAuthRes;
5830
+ if (isRemoteRequired) {
5831
+ mode = "upload-remote";
5832
+ remoteFileAuthRes = await getRemoteFileAuth();
5833
+ } else {
5834
+ mode = "skip-remote";
5835
+ }
5836
+ if (remoteFileAuthRes && remoteFileAuthRes.status !== 200) {
5627
5837
  return remoteFileAuthRes;
5628
5838
  }
5629
- const remoteFileAuth = remoteFileAuthRes.json.remoteFileAuth;
5630
- await serverOps.saveOrUploadFiles(preparedCommit, remoteFileAuth);
5631
- await serverOps.deletePatches(patchIds);
5839
+ const remoteFileAuth = (_remoteFileAuthRes = remoteFileAuthRes) === null || _remoteFileAuthRes === void 0 || (_remoteFileAuthRes = _remoteFileAuthRes.json) === null || _remoteFileAuthRes === void 0 ? void 0 : _remoteFileAuthRes.remoteFileAuth;
5840
+ const deleteRes = await serverOps.deleteAllPatches();
5841
+ await serverOps.saveOrUploadFiles(preparedCommit, mode, remoteFileAuth);
5842
+ if (deleteRes.error) {
5843
+ console.error(`Val got an error while cleaning up patches after publish: ${deleteRes.error.message}`);
5844
+ }
5632
5845
  return {
5633
5846
  status: 200,
5634
5847
  json: {} // TODO:
5635
5848
  };
5636
5849
  } else if (serverOps instanceof ValOpsHttp) {
5637
5850
  if (auth.error === undefined && auth.id) {
5638
- var _options$config$ai, _options$config$files;
5639
- let message = body.message || "Update content: " + Object.keys(analysis.patchesByModule) + " modules changed";
5640
- if (!((_options$config$ai = options.config.ai) !== null && _options$config$ai !== void 0 && (_options$config$ai = _options$config$ai.commitMessages) !== null && _options$config$ai !== void 0 && _options$config$ai.disabled)) {
5641
- const res = await serverOps.getCommitMessage(preparedCommit);
5642
- if (res.error) {
5643
- // ignore
5644
- console.error("Failed to get commit message", res.error.message);
5645
- } else {
5646
- message = res.commitSummary;
5647
- }
5648
- }
5649
- console.log({
5650
- message
5651
- });
5851
+ var _options$config$files;
5852
+ const message = body.message || "Val CMS update (" + Object.keys(analysis.patchesByModule).length + " files changed)";
5652
5853
  const commitRes = await serverOps.commit(preparedCommit, message, auth.id, ((_options$config$files = options.config.files) === null || _options$config$files === void 0 ? void 0 : _options$config$files.directory) || "/public/val");
5653
5854
  if (commitRes.error) {
5654
5855
  console.error("Failed to commit", commitRes.error);
@@ -6041,6 +6242,17 @@ function guessMimeTypeFromPath(filePath) {
6041
6242
  }
6042
6243
  return null;
6043
6244
  }
6245
+ function getIsRemoteRequired(schemas) {
6246
+ for (const moduleFilePathS in schemas) {
6247
+ const moduleFilePath = moduleFilePathS;
6248
+ const schema = schemas[moduleFilePath].serialize();
6249
+ const isRemoteRequired = hasRemoteFileSchema(schema);
6250
+ if (isRemoteRequired) {
6251
+ return true;
6252
+ }
6253
+ }
6254
+ return false;
6255
+ }
6044
6256
 
6045
6257
  async function createValServer(valModules, route, opts, config, callbacks, formatter) {
6046
6258
  const valServerConfig = await initHandlerOptions(route, opts, config);
@@ -6057,7 +6269,7 @@ async function initHandlerOptions(route, opts, config) {
6057
6269
  const valDisableRedirectUrl = opts.valDisableRedirectUrl || process.env.VAL_DISABLE_REDIRECT_URL;
6058
6270
  const maybeValProject = opts.project || process.env.VAL_PROJECT;
6059
6271
  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";
6272
+ const valContentUrl = opts.valContentUrl || process.env.VAL_CONTENT_URL || DEFAULT_CONTENT_HOST;
6061
6273
  if (isProxyMode) {
6062
6274
  var _opts$versions, _opts$versions2;
6063
6275
  if (!maybeApiKey || !maybeValSecret) {