@valbuild/server 0.68.1 → 0.69.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.
@@ -1565,10 +1565,13 @@ class ValOps {
1565
1565
  }
1566
1566
 
1567
1567
  // #region analyzePatches
1568
- analyzePatches(sortedPatches) {
1568
+ analyzePatches(sortedPatches, commits, currentCommitSha) {
1569
1569
  const patchesByModule = {};
1570
1570
  const fileLastUpdatedByPatchId = {};
1571
1571
  for (const patch of sortedPatches) {
1572
+ if (patch.appliedAt) {
1573
+ continue;
1574
+ }
1572
1575
  for (const op of patch.patch) {
1573
1576
  if (op.op === "file") {
1574
1577
  const filePath = op.filePath;
@@ -1607,7 +1610,7 @@ class ValOps {
1607
1610
  const errors = {};
1608
1611
  for (const patchData of analysis.patches) {
1609
1612
  const path = patchData.path;
1610
- if (!sources[path]) {
1613
+ if (sources[path] === undefined) {
1611
1614
  if (!errors[path]) {
1612
1615
  errors[path] = [];
1613
1616
  }
@@ -1884,6 +1887,8 @@ class ValOps {
1884
1887
  patchesByModule,
1885
1888
  fileLastUpdatedByPatchId
1886
1889
  } = patchAnalysis;
1890
+ const patchedSourceFiles = {};
1891
+ const previousSourceFiles = {};
1887
1892
  const applySourceFilePatches = async (path, patches) => {
1888
1893
  const sourceFileRes = await this.getSourceFile(path);
1889
1894
  const errors = [];
@@ -1899,6 +1904,7 @@ class ValOps {
1899
1904
  };
1900
1905
  }
1901
1906
  const sourceFile = sourceFileRes.data;
1907
+ previousSourceFiles[path] = sourceFile;
1902
1908
  let tsSourceFile = ts__default["default"].createSourceFile("<val>", sourceFile, ts__default["default"].ScriptTarget.ES2015);
1903
1909
  const appliedPatches = [];
1904
1910
  const triedPatches = [];
@@ -1976,7 +1982,6 @@ class ValOps {
1976
1982
  const appliedPatches = {};
1977
1983
  const triedPatches = {};
1978
1984
  const skippedPatches = {};
1979
- const patchedSourceFiles = {};
1980
1985
 
1981
1986
  //
1982
1987
  const globalAppliedPatches = [];
@@ -2017,6 +2022,7 @@ class ValOps {
2017
2022
  sourceFilePatchErrors,
2018
2023
  binaryFilePatchErrors,
2019
2024
  patchedSourceFiles,
2025
+ previousSourceFiles,
2020
2026
  patchedBinaryFilesDescriptors,
2021
2027
  appliedPatches,
2022
2028
  skippedPatches,
@@ -2034,7 +2040,7 @@ class ValOps {
2034
2040
  if (parentRef.type !== "head") {
2035
2041
  // There's room for some optimizations here: we could do this once, then re-use every time we create a patch, then again we only create one patch at a time
2036
2042
  const patchOps = await this.fetchPatches({
2037
- omitPatch: false
2043
+ excludePatchOps: false
2038
2044
  });
2039
2045
  const patchAnalysis = this.analyzePatches(patchOps.patches);
2040
2046
  const tree = await this.getSources({
@@ -2058,7 +2064,7 @@ class ValOps {
2058
2064
  }
2059
2065
  });
2060
2066
  }
2061
- if (!source) {
2067
+ if (source === undefined) {
2062
2068
  console.error(`Cannot patch. Module source at path: '${path}' does not exist`);
2063
2069
  return fp.result.err({
2064
2070
  errorType: "other",
@@ -2370,6 +2376,13 @@ class ValOpsFS extends ValOps {
2370
2376
  async onInit() {
2371
2377
  // do nothing
2372
2378
  }
2379
+ async getCommitSummary() {
2380
+ return {
2381
+ error: {
2382
+ message: "Val is in development / local mode. Cannot generate summary"
2383
+ }
2384
+ };
2385
+ }
2373
2386
  async getStat(params) {
2374
2387
  // In ValOpsFS, we don't have a websocket server to listen to file changes so we use long-polling.
2375
2388
  // If a file that Val depends on changes, we break the connection and tell the client to request again to get the latest values.
@@ -2551,7 +2564,7 @@ class ValOpsFS extends ValOps {
2551
2564
  }
2552
2565
  patches[parsedPatch.data.patchId] = {
2553
2566
  ...parsedPatch.data,
2554
- appliedAt: parsedBase ? parsedBase.data : null
2567
+ appliedAt: null
2555
2568
  };
2556
2569
  }
2557
2570
  });
@@ -2571,17 +2584,9 @@ class ValOpsFS extends ValOps {
2571
2584
  return parentRef.type === "head" ? "head" : parentRef.patchId;
2572
2585
  }
2573
2586
  async fetchPatches(filters) {
2574
- const fetchPatchesRes = await this.fetchPatchesFromFS(!!filters.omitPatch);
2575
- const sortedPatches = this.createPatchChain(fetchPatchesRes.patches).filter(patchData => {
2576
- if (filters.authors && !(patchData.authorId === null || filters.authors.includes(patchData.authorId))) {
2577
- return false;
2578
- }
2579
- if (filters.moduleFilePaths && !filters.moduleFilePaths.includes(patchData.path)) {
2580
- return false;
2581
- }
2582
- return true;
2583
- }).map(patchData => {
2584
- if (filters.omitPatch) {
2587
+ const fetchPatchesRes = await this.fetchPatchesFromFS(!!filters.excludePatchOps);
2588
+ const sortedPatches = this.createPatchChain(fetchPatchesRes.patches).map(patchData => {
2589
+ if (filters.excludePatchOps) {
2585
2590
  return {
2586
2591
  ...patchData,
2587
2592
  patch: undefined
@@ -2594,7 +2599,7 @@ class ValOpsFS extends ValOps {
2594
2599
  errors: fetchPatchesRes.errors
2595
2600
  };
2596
2601
  }
2597
- async fetchPatchesFromFS(omitPath) {
2602
+ async fetchPatchesFromFS(excludePatchOps) {
2598
2603
  const patches = {};
2599
2604
  const {
2600
2605
  errors,
@@ -2603,7 +2608,7 @@ class ValOpsFS extends ValOps {
2603
2608
  for (const [patchIdS, patch] of Object.entries(allPatches)) {
2604
2609
  const patchId = patchIdS;
2605
2610
  patches[patchId] = {
2606
- patch: omitPath ? undefined : patch.patch,
2611
+ patch: excludePatchOps ? undefined : patch.patch,
2607
2612
  parentRef: patch.parentRef,
2608
2613
  path: patch.path,
2609
2614
  baseSha: patch.baseSha,
@@ -3075,7 +3080,8 @@ class ValOpsFS extends ValOps {
3075
3080
 
3076
3081
  // #region profiles
3077
3082
  async getProfiles() {
3078
- throw new Error("Configuration error: cannot get profiles in local / development file system mode");
3083
+ // We do not have profiles in FS mode
3084
+ return [];
3079
3085
  }
3080
3086
 
3081
3087
  // #region fs file path helpers
@@ -3196,7 +3202,7 @@ const textEncoder = new TextEncoder();
3196
3202
  const PatchId = zod.z.string().refine(s => !!s); // TODO: validate
3197
3203
  const CommitSha = zod.z.string().refine(s => !!s); // TODO: validate
3198
3204
  zod.z.string().refine(s => !!s); // TODO: validate
3199
- const AuthorId = zod.z.string().refine(s => !!s); // TODO: validate
3205
+ zod.z.string().refine(s => !!s); // TODO: validate
3200
3206
  const ModuleFilePath = zod.z.string().refine(s => !!s); // TODO: validate
3201
3207
  const Metadata = zod.z.union([zod.z.object({
3202
3208
  mimeType: zod.z.string(),
@@ -3210,20 +3216,28 @@ const MetadataRes = zod.z.object({
3210
3216
  metadata: Metadata,
3211
3217
  type: zod.z.union([zod.z.literal("file"), zod.z.literal("image")]).nullable()
3212
3218
  });
3213
- const BasePatchResponse = zod.z.object({
3214
- path: ModuleFilePath,
3215
- patchId: PatchId,
3216
- authorId: AuthorId.nullable(),
3217
- createdAt: zod.z.string().datetime(),
3218
- baseSha: zod.z.string()
3219
+ const SummaryResponse = zod.z.object({
3220
+ commitSummary: zod.z.string().nullable()
3219
3221
  });
3220
- const GetPatches = zod.z.object({
3221
- patches: zod.z.array(zod.z.intersection(zod.z.object({
3222
- patch: internal.Patch.optional()
3223
- }), BasePatchResponse)),
3224
- errors: zod.z.array(zod.z.object({
3225
- patchId: PatchId.optional(),
3226
- message: zod.z.string()
3222
+ const GetApplicablePatches = zod.z.object({
3223
+ patches: zod.z.array(zod.z.object({
3224
+ path: zod.z.string(),
3225
+ patch: internal.Patch.nullable(),
3226
+ patchId: zod.z.string(),
3227
+ authorId: zod.z.string().nullable(),
3228
+ baseSha: zod.z.string(),
3229
+ createdAt: zod.z.string(),
3230
+ applied: zod.z.object({
3231
+ commitSha: zod.z.string()
3232
+ }).nullable()
3233
+ })),
3234
+ commits: zod.z.array(zod.z.object({
3235
+ commitSha: zod.z.string(),
3236
+ clientCommitSha: zod.z.string(),
3237
+ parentCommitSha: zod.z.string(),
3238
+ branch: zod.z.string(),
3239
+ creator: zod.z.string(),
3240
+ createdAt: zod.z.string()
3227
3241
  })).optional()
3228
3242
  });
3229
3243
  const FilesResponse = zod.z.object({
@@ -3260,7 +3274,7 @@ const DeletePatchesResponse = zod.z.object({
3260
3274
  patchId: PatchId
3261
3275
  })).optional()
3262
3276
  });
3263
- zod.z.object({
3277
+ const SavePatchFileResponse = zod.z.object({
3264
3278
  patchId: PatchId,
3265
3279
  filePath: ModuleFilePath
3266
3280
  });
@@ -3279,11 +3293,11 @@ const ProfilesResponse = zod.z.object({
3279
3293
  }))
3280
3294
  });
3281
3295
  class ValOpsHttp extends ValOps {
3282
- constructor(hostUrl, project, commitSha,
3296
+ constructor(contentUrl, project, commitSha,
3283
3297
  // TODO: CommitSha
3284
3298
  branch, apiKey, valModules, options) {
3285
3299
  super(valModules, options);
3286
- this.hostUrl = hostUrl;
3300
+ this.contentUrl = contentUrl;
3287
3301
  this.project = project;
3288
3302
  this.commitSha = commitSha;
3289
3303
  this.branch = branch;
@@ -3295,6 +3309,70 @@ class ValOpsHttp extends ValOps {
3295
3309
  async onInit() {
3296
3310
  // TODO: unused for now. Implement or remove
3297
3311
  }
3312
+ async getCommitSummary(preparedCommit) {
3313
+ try {
3314
+ var _res$headers$get;
3315
+ const res = await fetch(`${this.contentUrl}/v1/${this.project}/commit-summary`, {
3316
+ method: "POST",
3317
+ headers: {
3318
+ ...this.authHeaders,
3319
+ "Content-Type": "application/json"
3320
+ },
3321
+ body: JSON.stringify({
3322
+ patchedSourceFiles: preparedCommit.patchedSourceFiles,
3323
+ previousSourceFiles: preparedCommit.previousSourceFiles
3324
+ })
3325
+ });
3326
+ if (res.ok) {
3327
+ const json = await res.json();
3328
+ const parsed = SummaryResponse.safeParse(json);
3329
+ if (parsed.success) {
3330
+ return {
3331
+ commitSummary: parsed.data.commitSummary
3332
+ };
3333
+ }
3334
+ console.error(`Could not parse summary response. Error: ${zodValidationError.fromError(parsed.error).toString()}`);
3335
+ return {
3336
+ error: {
3337
+ message: `Cannot get the summary of your changes. An error has been logged. Possible cause: the current version of Val might be too old. Please try again later or contact the developers on your team.`
3338
+ }
3339
+ };
3340
+ }
3341
+ if (res.status === 401) {
3342
+ console.error("Unauthorized to get summary");
3343
+ return {
3344
+ error: {
3345
+ message: "Could not get summary. Although your user is authorized, the application has authorization issues. Contact the developers on your team and ask them to verify the api keys."
3346
+ }
3347
+ };
3348
+ }
3349
+ const unknownErrorMessage = `Could not get summary. HTTP error: ${res.status} ${res.statusText}`;
3350
+ if ((_res$headers$get = res.headers.get("Content-Type")) !== null && _res$headers$get !== void 0 && _res$headers$get.includes("application/json")) {
3351
+ const json = await res.json();
3352
+ if (json.message) {
3353
+ console.error("Summary error:", json.message);
3354
+ return {
3355
+ error: {
3356
+ message: json.message
3357
+ }
3358
+ };
3359
+ }
3360
+ }
3361
+ console.error(unknownErrorMessage);
3362
+ return {
3363
+ error: {
3364
+ message: unknownErrorMessage
3365
+ }
3366
+ };
3367
+ } catch (e) {
3368
+ console.error("Could not get summary (connection error?):", e);
3369
+ return {
3370
+ error: {
3371
+ message: `Could not get summary. Error: ${e instanceof Error ? e.message : JSON.stringify(e)}`
3372
+ }
3373
+ };
3374
+ }
3375
+ }
3298
3376
  async getStat(params) {
3299
3377
  if (!(params !== null && params !== void 0 && params.profileId)) {
3300
3378
  return {
@@ -3307,11 +3385,23 @@ class ValOpsHttp extends ValOps {
3307
3385
  const currentBaseSha = await this.getBaseSha();
3308
3386
  const currentSchemaSha = await this.getSchemaSha();
3309
3387
  const allPatchData = await this.fetchPatches({
3310
- omitPatch: true,
3311
- authors: undefined,
3312
- patchIds: undefined,
3313
- moduleFilePaths: undefined
3388
+ excludePatchOps: true,
3389
+ patchIds: undefined
3314
3390
  });
3391
+ if ("error" in allPatchData && allPatchData.error && allPatchData.unauthorized) {
3392
+ return {
3393
+ type: "error",
3394
+ error: allPatchData.error,
3395
+ unauthorized: true
3396
+ };
3397
+ }
3398
+ if ("error" in allPatchData && allPatchData.error && allPatchData.networkError) {
3399
+ return {
3400
+ type: "error",
3401
+ error: allPatchData.error,
3402
+ networkError: true
3403
+ };
3404
+ }
3315
3405
  // We think these errors will be picked up else where (?), so we only return an error here if there are no patches
3316
3406
  if (allPatchData.patches.length === 0) {
3317
3407
  let message;
@@ -3358,7 +3448,7 @@ class ValOpsHttp extends ValOps {
3358
3448
  };
3359
3449
  }
3360
3450
  async getWebSocketNonce(profileId) {
3361
- return fetch(`${this.hostUrl}/v1/${this.project}/websocket/nonces`, {
3451
+ return fetch(`${this.contentUrl}/v1/${this.project}/websocket/nonces`, {
3362
3452
  method: "POST",
3363
3453
  body: JSON.stringify({
3364
3454
  branch: this.branch,
@@ -3435,16 +3525,12 @@ class ValOpsHttp extends ValOps {
3435
3525
  if (patchIds === undefined || patchIds.length === 0) {
3436
3526
  return this.fetchPatchesInternal({
3437
3527
  patchIds: patchIds,
3438
- authors: filters.authors,
3439
- moduleFilePaths: filters.moduleFilePaths,
3440
- omitPatch: filters.omitPatch
3528
+ excludePatchOps: filters.excludePatchOps
3441
3529
  });
3442
3530
  }
3443
3531
  for (const res of await Promise.all(patchIdChunks.map(patchIdChunk => this.fetchPatchesInternal({
3444
3532
  patchIds: patchIdChunk,
3445
- authors: filters.authors,
3446
- moduleFilePaths: filters.moduleFilePaths,
3447
- omitPatch: filters.omitPatch
3533
+ excludePatchOps: filters.excludePatchOps
3448
3534
  })))) {
3449
3535
  if ("error" in res) {
3450
3536
  return res;
@@ -3462,72 +3548,97 @@ class ValOpsHttp extends ValOps {
3462
3548
  async fetchPatchesInternal(filters) {
3463
3549
  const params = [];
3464
3550
  params.push(["branch", this.branch]);
3551
+ params.push(["commit", this.commitSha]);
3465
3552
  if (filters.patchIds) {
3466
3553
  for (const patchId of filters.patchIds) {
3467
3554
  params.push(["patch_id", patchId]);
3468
3555
  }
3469
3556
  }
3470
- if (filters.authors) {
3471
- for (const author of filters.authors) {
3472
- params.push(["author_id", author]);
3473
- }
3474
- }
3475
- if (filters.omitPatch) {
3476
- params.push(["omit_patch", "true"]);
3477
- }
3478
- if (filters.moduleFilePaths) {
3479
- for (const moduleFilePath of filters.moduleFilePaths) {
3480
- params.push(["module_file_path", moduleFilePath]);
3481
- }
3557
+ if (filters.excludePatchOps) {
3558
+ params.push(["exclude_patch_ops", "true"]);
3482
3559
  }
3483
3560
  const searchParams = new URLSearchParams(params);
3484
- return fetch(`${this.hostUrl}/v1/${this.project}/patches${searchParams.size > 0 ? `?${searchParams.toString()}` : ""}`, {
3485
- headers: {
3486
- ...this.authHeaders,
3487
- "Content-Type": "application/json"
3488
- }
3489
- }).then(async res => {
3561
+ try {
3562
+ const patchesRes = await fetch(`${this.contentUrl}/v1/${this.project}/applicable/patches${searchParams.size > 0 ? `?${searchParams.toString()}` : ""}`, {
3563
+ headers: this.authHeaders
3564
+ });
3490
3565
  const patches = [];
3491
- if (res.ok) {
3492
- const json = await res.json();
3493
- const parsed = GetPatches.safeParse(json);
3566
+ if (patchesRes.ok) {
3567
+ const json = await patchesRes.json();
3568
+ const parsed = GetApplicablePatches.safeParse(json);
3494
3569
  if (parsed.success) {
3495
3570
  const errors = [];
3496
3571
  const data = parsed.data;
3497
3572
  for (const patchesRes of data.patches) {
3573
+ var _patchesRes$applied;
3498
3574
  patches.push({
3499
3575
  authorId: patchesRes.authorId,
3500
3576
  createdAt: patchesRes.createdAt,
3501
- appliedAt: null,
3502
3577
  patchId: patchesRes.patchId,
3503
3578
  path: patchesRes.path,
3504
3579
  baseSha: patchesRes.baseSha,
3505
- patch: filters.omitPatch ? undefined : patchesRes.patch
3580
+ patch: patchesRes.patch,
3581
+ appliedAt: (_patchesRes$applied = patchesRes.applied) !== null && _patchesRes$applied !== void 0 && _patchesRes$applied.commitSha ? {
3582
+ commitSha: patchesRes.applied.commitSha
3583
+ } : null
3506
3584
  });
3507
3585
  }
3586
+ const commits = [];
3587
+ if (data.commits) {
3588
+ for (const commit of data.commits) {
3589
+ commits.push({
3590
+ commitSha: commit.commitSha,
3591
+ clientCommitSha: commit.clientCommitSha,
3592
+ parentCommitSha: commit.parentCommitSha,
3593
+ branch: commit.branch,
3594
+ creator: commit.creator,
3595
+ createdAt: commit.createdAt
3596
+ });
3597
+ }
3598
+ }
3508
3599
  return {
3600
+ commits,
3509
3601
  patches,
3510
3602
  errors
3511
3603
  };
3512
3604
  }
3605
+ console.error("Could not parse patches response. Error: " + zodValidationError.fromError(parsed.error));
3606
+ return {
3607
+ patches,
3608
+ error: {
3609
+ message: `The response that Val got from the server was not in the expected format. This might be a transient error or a configuration issue. Please try again later.`
3610
+ }
3611
+ };
3612
+ }
3613
+ console.error("Could not get patches. HTTP error: " + patchesRes.status + " " + patchesRes.statusText);
3614
+ if (patchesRes.status === 401) {
3513
3615
  return {
3514
3616
  patches,
3515
3617
  error: {
3516
- message: `Could not parse get patches response. Error: ${zodValidationError.fromError(parsed.error)}`
3618
+ message: "Although your user is authorized, the application has authorization issues. Contact the developers on your team and ask them to verify the api keys."
3517
3619
  }
3518
3620
  };
3519
3621
  }
3520
3622
  return {
3521
3623
  patches,
3522
3624
  error: {
3523
- message: "Could not get patches. HTTP error: " + res.status + " " + res.statusText
3625
+ message: "Could not your changes. It is most likely due to a network issue. Check your network connection and please try again."
3524
3626
  }
3525
3627
  };
3526
- });
3628
+ } catch (err) {
3629
+ console.error("Could not get patches (connection error):", err instanceof Error ? err.message : JSON.stringify(err));
3630
+ return {
3631
+ patches: [],
3632
+ networkError: true,
3633
+ error: {
3634
+ message: `Error: ${err instanceof Error ? err.message : JSON.stringify(err)}`
3635
+ }
3636
+ };
3637
+ }
3527
3638
  }
3528
3639
  async saveSourceFilePatch(path, patch, parentRef, authorId) {
3529
3640
  const baseSha = await this.getBaseSha();
3530
- return fetch(`${this.hostUrl}/v1/${this.project}/patches`, {
3641
+ return fetch(`${this.contentUrl}/v1/${this.project}/patches`, {
3531
3642
  method: "POST",
3532
3643
  headers: {
3533
3644
  ...this.authHeaders,
@@ -3544,7 +3655,7 @@ class ValOpsHttp extends ValOps {
3544
3655
  coreVersion: core.Internal.VERSION.core
3545
3656
  })
3546
3657
  }).then(async res => {
3547
- var _res$headers$get;
3658
+ var _res$headers$get2;
3548
3659
  if (res.ok) {
3549
3660
  const parsed = SavePatchResponse.safeParse(await res.json());
3550
3661
  if (parsed.success) {
@@ -3563,7 +3674,7 @@ class ValOpsHttp extends ValOps {
3563
3674
  message: "Conflict: " + (await res.text())
3564
3675
  });
3565
3676
  }
3566
- if ((_res$headers$get = res.headers.get("Content-Type")) !== null && _res$headers$get !== void 0 && _res$headers$get.includes("application/json")) {
3677
+ if ((_res$headers$get2 = res.headers.get("Content-Type")) !== null && _res$headers$get2 !== void 0 && _res$headers$get2.includes("application/json")) {
3567
3678
  const json = await res.json();
3568
3679
  return fp.result.err({
3569
3680
  errorType: "other",
@@ -3582,7 +3693,45 @@ class ValOpsHttp extends ValOps {
3582
3693
  });
3583
3694
  }
3584
3695
  async saveBase64EncodedBinaryFileFromPatch(filePath, parentRef, patchId, data, type, metadata) {
3585
- throw Error("TODO: implement");
3696
+ return fetch(`${this.contentUrl}/v1/${this.project}/patches/${patchId}/files`, {
3697
+ method: "POST",
3698
+ headers: {
3699
+ ...this.authHeaders,
3700
+ "Content-Type": "application/json"
3701
+ },
3702
+ body: JSON.stringify({
3703
+ filePath: filePath,
3704
+ data,
3705
+ type,
3706
+ metadata
3707
+ })
3708
+ }).then(async res => {
3709
+ if (res.ok) {
3710
+ const parsed = SavePatchFileResponse.safeParse(await res.json());
3711
+ if (parsed.success) {
3712
+ return {
3713
+ patchId: parsed.data.patchId,
3714
+ filePath: parsed.data.filePath
3715
+ };
3716
+ }
3717
+ return {
3718
+ error: {
3719
+ message: `Could not parse save patch file response. Error: ${zodValidationError.fromError(parsed.error)}`
3720
+ }
3721
+ };
3722
+ }
3723
+ return {
3724
+ error: {
3725
+ message: "Could not save patch file. HTTP error: " + res.status + " " + res.statusText
3726
+ }
3727
+ };
3728
+ }).catch(e => {
3729
+ return {
3730
+ error: {
3731
+ message: `Could save source binary file in patch (connection error?): ${e.toString()}`
3732
+ }
3733
+ };
3734
+ });
3586
3735
  }
3587
3736
  async getHttpFiles(files) {
3588
3737
  const params = new URLSearchParams();
@@ -3593,7 +3742,7 @@ class ValOpsHttp extends ValOps {
3593
3742
  params.set("body_sha",
3594
3743
  // We use this for cache invalidation
3595
3744
  core.Internal.getSHA256Hash(textEncoder.encode(stringifiedFiles)));
3596
- return fetch(`${this.hostUrl}/v1/${this.project}/files?${params}`, {
3745
+ return fetch(`${this.contentUrl}/v1/${this.project}/files?${params}`, {
3597
3746
  method: "PUT",
3598
3747
  // Yes, PUT is weird. Weirder to have a body in a GET request.
3599
3748
  headers: {
@@ -3686,7 +3835,7 @@ class ValOpsHttp extends ValOps {
3686
3835
  const params = new URLSearchParams();
3687
3836
  params.set("file_path", filePath);
3688
3837
  try {
3689
- const metadataRes = await fetch(`${this.hostUrl}/v1/${this.project}/patches/${patchId}/files?${params}`, {
3838
+ const metadataRes = await fetch(`${this.contentUrl}/v1/${this.project}/patches/${patchId}/files?${params}`, {
3690
3839
  headers: {
3691
3840
  ...this.authHeaders,
3692
3841
  "Content-Type": "application/json"
@@ -3740,7 +3889,7 @@ class ValOpsHttp extends ValOps {
3740
3889
  };
3741
3890
  }
3742
3891
  async deletePatches(patchIds) {
3743
- return fetch(`${this.hostUrl}/v1/${this.project}/patches`, {
3892
+ return fetch(`${this.contentUrl}/v1/${this.project}/patches`, {
3744
3893
  method: "DELETE",
3745
3894
  headers: {
3746
3895
  ...this.authHeaders,
@@ -3786,11 +3935,44 @@ class ValOpsHttp extends ValOps {
3786
3935
  };
3787
3936
  });
3788
3937
  }
3789
- async commit(prepared, message, committer, newBranch) {
3938
+ async getCommitMessage(preparedCommit) {
3939
+ var _res$headers$get3;
3940
+ const res = await fetch(`${this.contentUrl}/v1/${this.project}/commit-summary`, {
3941
+ method: "POST",
3942
+ headers: {
3943
+ ...this.authHeaders,
3944
+ "Content-Type": "application/json"
3945
+ },
3946
+ body: JSON.stringify({
3947
+ patchedSourceFiles: preparedCommit.patchedSourceFiles,
3948
+ previousSourceFiles: preparedCommit.previousSourceFiles
3949
+ })
3950
+ });
3951
+ if (res.ok) {
3952
+ const json = await res.json();
3953
+ return {
3954
+ commitSummary: json.commitSummary
3955
+ };
3956
+ }
3957
+ if ((_res$headers$get3 = res.headers.get("Content-Type")) !== null && _res$headers$get3 !== void 0 && _res$headers$get3.includes("application/json")) {
3958
+ const json = await res.json();
3959
+ return {
3960
+ error: {
3961
+ message: json.message
3962
+ }
3963
+ };
3964
+ }
3965
+ return {
3966
+ error: {
3967
+ message: "Could not get commit message. HTTP error: " + res.status + " " + res.statusText
3968
+ }
3969
+ };
3970
+ }
3971
+ async commit(prepared, message, committer, filesDirectory, newBranch) {
3790
3972
  try {
3791
- var _res$headers$get2;
3973
+ var _res$headers$get4;
3792
3974
  const existingBranch = this.branch;
3793
- const res = await fetch(`${this.hostUrl}/v1/${this.project}/commit`, {
3975
+ const res = await fetch(`${this.contentUrl}/v1/${this.project}/commit`, {
3794
3976
  method: "POST",
3795
3977
  headers: {
3796
3978
  ...this.authHeaders,
@@ -3802,6 +3984,7 @@ class ValOpsHttp extends ValOps {
3802
3984
  appliedPatches: prepared.appliedPatches,
3803
3985
  commit: this.commitSha,
3804
3986
  root: this.root,
3987
+ filesDirectory,
3805
3988
  baseSha: await this.getBaseSha(),
3806
3989
  committer,
3807
3990
  message,
@@ -3824,7 +4007,7 @@ class ValOpsHttp extends ValOps {
3824
4007
  }
3825
4008
  };
3826
4009
  }
3827
- if ((_res$headers$get2 = res.headers.get("Content-Type")) !== null && _res$headers$get2 !== void 0 && _res$headers$get2.includes("application/json")) {
4010
+ if ((_res$headers$get4 = res.headers.get("Content-Type")) !== null && _res$headers$get4 !== void 0 && _res$headers$get4.includes("application/json")) {
3828
4011
  const json = await res.json();
3829
4012
  if (json.isNotFastForward) {
3830
4013
  return {
@@ -3856,8 +4039,8 @@ class ValOpsHttp extends ValOps {
3856
4039
 
3857
4040
  // #region profiles
3858
4041
  async getProfiles() {
3859
- var _res$headers$get3;
3860
- const res = await fetch(`${this.hostUrl}/v1/${this.project}/profiles`, {
4042
+ var _res$headers$get5;
4043
+ const res = await fetch(`${this.contentUrl}/v1/${this.project}/profiles`, {
3861
4044
  headers: {
3862
4045
  ...this.authHeaders,
3863
4046
  "Content-Type": "application/json"
@@ -3871,7 +4054,7 @@ class ValOpsHttp extends ValOps {
3871
4054
  }
3872
4055
  return parsed.data.profiles;
3873
4056
  }
3874
- if ((_res$headers$get3 = res.headers.get("Content-Type")) !== null && _res$headers$get3 !== void 0 && _res$headers$get3.includes("application/json")) {
4057
+ if ((_res$headers$get5 = res.headers.get("Content-Type")) !== null && _res$headers$get5 !== void 0 && _res$headers$get5.includes("application/json")) {
3875
4058
  const json = await res.json();
3876
4059
  throw Error(`Could not get profiles (status: ${res.status}): ${"message" in json ? json.message : "Unknown error"}`);
3877
4060
  }
@@ -3909,6 +4092,7 @@ const ValServer = (valModules, options, callbacks) => {
3909
4092
  url.searchParams.set("state", token);
3910
4093
  return url.toString();
3911
4094
  };
4095
+ const commit = options.mode === "http" ? options.commit : undefined;
3912
4096
  const getAppErrorUrl = error => {
3913
4097
  if (!options.project) {
3914
4098
  throw new Error("Project is not set");
@@ -4371,6 +4555,22 @@ const ValServer = (valModules, options, callbacks) => {
4371
4555
  ...req.body,
4372
4556
  profileId: "id" in auth ? auth.id : undefined
4373
4557
  });
4558
+ if (currentStat.type === "error" && currentStat.networkError) {
4559
+ return {
4560
+ status: 503,
4561
+ json: {
4562
+ message: "Network error"
4563
+ }
4564
+ };
4565
+ }
4566
+ if (currentStat.type === "error" && currentStat.unauthorized) {
4567
+ return {
4568
+ status: 401,
4569
+ json: {
4570
+ message: "Unauthorized"
4571
+ }
4572
+ };
4573
+ }
4374
4574
  if (currentStat.type === "error") {
4375
4575
  return {
4376
4576
  status: 500,
@@ -4381,6 +4581,7 @@ const ValServer = (valModules, options, callbacks) => {
4381
4581
  status: 200,
4382
4582
  json: {
4383
4583
  ...currentStat,
4584
+ mode: serverOps instanceof ValOpsFS ? "fs" : serverOps instanceof ValOpsHttp ? "http" : "unknown",
4384
4585
  config: options.config
4385
4586
  }
4386
4587
  };
@@ -4408,7 +4609,7 @@ const ValServer = (valModules, options, callbacks) => {
4408
4609
  };
4409
4610
  }
4410
4611
  const patches = req.body.patches;
4411
- const parentRef = req.body.parentRef;
4612
+ let parentRef = req.body.parentRef;
4412
4613
  const authorId = "id" in auth ? auth.id : null;
4413
4614
  const newPatchIds = [];
4414
4615
  for (const patch of patches) {
@@ -4439,6 +4640,10 @@ const ValServer = (valModules, options, callbacks) => {
4439
4640
  };
4440
4641
  }
4441
4642
  } else {
4643
+ parentRef = {
4644
+ type: "patch",
4645
+ patchId: createPatchRes.value.patchId
4646
+ };
4442
4647
  newPatchIds.push(createPatchRes.value.patchId);
4443
4648
  }
4444
4649
  }
@@ -4446,10 +4651,7 @@ const ValServer = (valModules, options, callbacks) => {
4446
4651
  status: 200,
4447
4652
  json: {
4448
4653
  newPatchIds,
4449
- parentRef: {
4450
- type: "patch",
4451
- patchId: newPatchIds[newPatchIds.length - 1]
4452
- }
4654
+ parentRef
4453
4655
  }
4454
4656
  };
4455
4657
  },
@@ -4474,13 +4676,10 @@ const ValServer = (valModules, options, callbacks) => {
4474
4676
  }
4475
4677
  };
4476
4678
  }
4477
- const omit_patch = query.omit_patch === true;
4478
- const authors = query.author;
4679
+ const excludePatchOps = query.exclude_patch_ops === true;
4479
4680
  const fetchedPatchesRes = await serverOps.fetchPatches({
4480
- authors,
4481
4681
  patchIds: query.patch_id,
4482
- omitPatch: omit_patch,
4483
- moduleFilePaths: query.module_file_path
4682
+ excludePatchOps: excludePatchOps
4484
4683
  });
4485
4684
  if (fetchedPatchesRes.error) {
4486
4685
  // Error is singular
@@ -4661,8 +4860,17 @@ const ValServer = (valModules, options, callbacks) => {
4661
4860
  }
4662
4861
  const patchOps = await serverOps.fetchPatches({
4663
4862
  patchIds: undefined,
4664
- omitPatch: false
4863
+ excludePatchOps: false
4665
4864
  });
4865
+ // We check authorization here, because it is the first call to the backend
4866
+ if (patchOps.error && patchOps.unauthorized) {
4867
+ return {
4868
+ status: 401,
4869
+ json: {
4870
+ message: "Unauthorized"
4871
+ }
4872
+ };
4873
+ }
4666
4874
  const patchAnalysis = serverOps.analyzePatches(patchOps.patches);
4667
4875
  let sourcesRes = await serverOps.getSources();
4668
4876
  const onlyPatchedTreeModules = await serverOps.getSources({
@@ -4741,17 +4949,16 @@ const ValServer = (valModules, options, callbacks) => {
4741
4949
  },
4742
4950
  "/profiles": {
4743
4951
  GET: async req => {
4744
- // const cookies = req.cookies;
4745
- // const auth = getAuth(cookies);
4746
- // if (auth.error) {
4747
- // return {
4748
- // status: 401,
4749
- // json: {
4750
- // message: auth.error,
4751
- // },
4752
- // };
4753
- // }
4754
-
4952
+ const cookies = req.cookies;
4953
+ const auth = getAuth(cookies);
4954
+ if (auth.error) {
4955
+ return {
4956
+ status: 401,
4957
+ json: {
4958
+ message: auth.error
4959
+ }
4960
+ };
4961
+ }
4755
4962
  const profiles = await serverOps.getProfiles();
4756
4963
  return {
4757
4964
  status: 200,
@@ -4761,6 +4968,49 @@ const ValServer = (valModules, options, callbacks) => {
4761
4968
  };
4762
4969
  }
4763
4970
  },
4971
+ "/commit-summary": {
4972
+ GET: async req => {
4973
+ const cookies = req.cookies;
4974
+ const auth = getAuth(cookies);
4975
+ if (auth.error) {
4976
+ return {
4977
+ status: 401,
4978
+ json: {
4979
+ message: auth.error
4980
+ }
4981
+ };
4982
+ }
4983
+ const query = req.query;
4984
+ const patchIds = query.patch_id;
4985
+ const patches = await serverOps.fetchPatches({
4986
+ patchIds,
4987
+ excludePatchOps: false
4988
+ });
4989
+ const analysis = serverOps.analyzePatches(patches.patches, patches.commits, commit);
4990
+ const preparedCommit = await serverOps.prepare({
4991
+ ...analysis,
4992
+ ...patches
4993
+ });
4994
+ const res = await serverOps.getCommitSummary(preparedCommit);
4995
+ if (res.error) {
4996
+ console.error("Failed to summarize", res.error);
4997
+ return {
4998
+ status: 400,
4999
+ json: {
5000
+ message: res.error.message
5001
+ }
5002
+ };
5003
+ }
5004
+ return {
5005
+ status: 200,
5006
+ json: {
5007
+ baseSha: await serverOps.getBaseSha(),
5008
+ patchIds,
5009
+ commitSummary: res.commitSummary
5010
+ }
5011
+ };
5012
+ }
5013
+ },
4764
5014
  "/save": {
4765
5015
  POST: async req => {
4766
5016
  const cookies = req.cookies;
@@ -4793,9 +5043,9 @@ const ValServer = (valModules, options, callbacks) => {
4793
5043
  } = bodyRes.data;
4794
5044
  const patches = await serverOps.fetchPatches({
4795
5045
  patchIds,
4796
- omitPatch: false
5046
+ excludePatchOps: false
4797
5047
  });
4798
- const analysis = serverOps.analyzePatches(patches.patches);
5048
+ const analysis = serverOps.analyzePatches(patches.patches, patches.commits, commit);
4799
5049
  const preparedCommit = await serverOps.prepare({
4800
5050
  ...analysis,
4801
5051
  ...patches
@@ -4827,7 +5077,24 @@ const ValServer = (valModules, options, callbacks) => {
4827
5077
  };
4828
5078
  } else if (serverOps instanceof ValOpsHttp) {
4829
5079
  if (auth.error === undefined && auth.id) {
4830
- const commitRes = await serverOps.commit(preparedCommit, "Update content: " + Object.keys(analysis.patchesByModule) + " modules changed", auth.id);
5080
+ var _options$config$ai, _options$config$files;
5081
+ let message = body.message || "Update content: " + Object.keys(analysis.patchesByModule) + " modules changed";
5082
+ 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)) {
5083
+ const res = await serverOps.getCommitMessage(preparedCommit);
5084
+ console.log({
5085
+ res
5086
+ });
5087
+ if (res.error) {
5088
+ // ignore
5089
+ console.error("Failed to get commit message", res.error.message);
5090
+ } else {
5091
+ message = res.commitSummary;
5092
+ }
5093
+ }
5094
+ console.log({
5095
+ message
5096
+ });
5097
+ 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");
4831
5098
  if (commitRes.error) {
4832
5099
  console.error("Failed to commit", commitRes.error);
4833
5100
  if ("isNotFastForward" in commitRes && commitRes.isNotFastForward) {