@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.
@@ -1536,10 +1536,13 @@ class ValOps {
1536
1536
  }
1537
1537
 
1538
1538
  // #region analyzePatches
1539
- analyzePatches(sortedPatches) {
1539
+ analyzePatches(sortedPatches, commits, currentCommitSha) {
1540
1540
  const patchesByModule = {};
1541
1541
  const fileLastUpdatedByPatchId = {};
1542
1542
  for (const patch of sortedPatches) {
1543
+ if (patch.appliedAt) {
1544
+ continue;
1545
+ }
1543
1546
  for (const op of patch.patch) {
1544
1547
  if (op.op === "file") {
1545
1548
  const filePath = op.filePath;
@@ -1578,7 +1581,7 @@ class ValOps {
1578
1581
  const errors = {};
1579
1582
  for (const patchData of analysis.patches) {
1580
1583
  const path = patchData.path;
1581
- if (!sources[path]) {
1584
+ if (sources[path] === undefined) {
1582
1585
  if (!errors[path]) {
1583
1586
  errors[path] = [];
1584
1587
  }
@@ -1855,6 +1858,8 @@ class ValOps {
1855
1858
  patchesByModule,
1856
1859
  fileLastUpdatedByPatchId
1857
1860
  } = patchAnalysis;
1861
+ const patchedSourceFiles = {};
1862
+ const previousSourceFiles = {};
1858
1863
  const applySourceFilePatches = async (path, patches) => {
1859
1864
  const sourceFileRes = await this.getSourceFile(path);
1860
1865
  const errors = [];
@@ -1870,6 +1875,7 @@ class ValOps {
1870
1875
  };
1871
1876
  }
1872
1877
  const sourceFile = sourceFileRes.data;
1878
+ previousSourceFiles[path] = sourceFile;
1873
1879
  let tsSourceFile = ts.createSourceFile("<val>", sourceFile, ts.ScriptTarget.ES2015);
1874
1880
  const appliedPatches = [];
1875
1881
  const triedPatches = [];
@@ -1947,7 +1953,6 @@ class ValOps {
1947
1953
  const appliedPatches = {};
1948
1954
  const triedPatches = {};
1949
1955
  const skippedPatches = {};
1950
- const patchedSourceFiles = {};
1951
1956
 
1952
1957
  //
1953
1958
  const globalAppliedPatches = [];
@@ -1988,6 +1993,7 @@ class ValOps {
1988
1993
  sourceFilePatchErrors,
1989
1994
  binaryFilePatchErrors,
1990
1995
  patchedSourceFiles,
1996
+ previousSourceFiles,
1991
1997
  patchedBinaryFilesDescriptors,
1992
1998
  appliedPatches,
1993
1999
  skippedPatches,
@@ -2005,7 +2011,7 @@ class ValOps {
2005
2011
  if (parentRef.type !== "head") {
2006
2012
  // 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
2007
2013
  const patchOps = await this.fetchPatches({
2008
- omitPatch: false
2014
+ excludePatchOps: false
2009
2015
  });
2010
2016
  const patchAnalysis = this.analyzePatches(patchOps.patches);
2011
2017
  const tree = await this.getSources({
@@ -2029,7 +2035,7 @@ class ValOps {
2029
2035
  }
2030
2036
  });
2031
2037
  }
2032
- if (!source) {
2038
+ if (source === undefined) {
2033
2039
  console.error(`Cannot patch. Module source at path: '${path}' does not exist`);
2034
2040
  return result.err({
2035
2041
  errorType: "other",
@@ -2341,6 +2347,13 @@ class ValOpsFS extends ValOps {
2341
2347
  async onInit() {
2342
2348
  // do nothing
2343
2349
  }
2350
+ async getCommitSummary() {
2351
+ return {
2352
+ error: {
2353
+ message: "Val is in development / local mode. Cannot generate summary"
2354
+ }
2355
+ };
2356
+ }
2344
2357
  async getStat(params) {
2345
2358
  // In ValOpsFS, we don't have a websocket server to listen to file changes so we use long-polling.
2346
2359
  // If a file that Val depends on changes, we break the connection and tell the client to request again to get the latest values.
@@ -2522,7 +2535,7 @@ class ValOpsFS extends ValOps {
2522
2535
  }
2523
2536
  patches[parsedPatch.data.patchId] = {
2524
2537
  ...parsedPatch.data,
2525
- appliedAt: parsedBase ? parsedBase.data : null
2538
+ appliedAt: null
2526
2539
  };
2527
2540
  }
2528
2541
  });
@@ -2542,17 +2555,9 @@ class ValOpsFS extends ValOps {
2542
2555
  return parentRef.type === "head" ? "head" : parentRef.patchId;
2543
2556
  }
2544
2557
  async fetchPatches(filters) {
2545
- const fetchPatchesRes = await this.fetchPatchesFromFS(!!filters.omitPatch);
2546
- const sortedPatches = this.createPatchChain(fetchPatchesRes.patches).filter(patchData => {
2547
- if (filters.authors && !(patchData.authorId === null || filters.authors.includes(patchData.authorId))) {
2548
- return false;
2549
- }
2550
- if (filters.moduleFilePaths && !filters.moduleFilePaths.includes(patchData.path)) {
2551
- return false;
2552
- }
2553
- return true;
2554
- }).map(patchData => {
2555
- if (filters.omitPatch) {
2558
+ const fetchPatchesRes = await this.fetchPatchesFromFS(!!filters.excludePatchOps);
2559
+ const sortedPatches = this.createPatchChain(fetchPatchesRes.patches).map(patchData => {
2560
+ if (filters.excludePatchOps) {
2556
2561
  return {
2557
2562
  ...patchData,
2558
2563
  patch: undefined
@@ -2565,7 +2570,7 @@ class ValOpsFS extends ValOps {
2565
2570
  errors: fetchPatchesRes.errors
2566
2571
  };
2567
2572
  }
2568
- async fetchPatchesFromFS(omitPath) {
2573
+ async fetchPatchesFromFS(excludePatchOps) {
2569
2574
  const patches = {};
2570
2575
  const {
2571
2576
  errors,
@@ -2574,7 +2579,7 @@ class ValOpsFS extends ValOps {
2574
2579
  for (const [patchIdS, patch] of Object.entries(allPatches)) {
2575
2580
  const patchId = patchIdS;
2576
2581
  patches[patchId] = {
2577
- patch: omitPath ? undefined : patch.patch,
2582
+ patch: excludePatchOps ? undefined : patch.patch,
2578
2583
  parentRef: patch.parentRef,
2579
2584
  path: patch.path,
2580
2585
  baseSha: patch.baseSha,
@@ -3046,7 +3051,8 @@ class ValOpsFS extends ValOps {
3046
3051
 
3047
3052
  // #region profiles
3048
3053
  async getProfiles() {
3049
- throw new Error("Configuration error: cannot get profiles in local / development file system mode");
3054
+ // We do not have profiles in FS mode
3055
+ return [];
3050
3056
  }
3051
3057
 
3052
3058
  // #region fs file path helpers
@@ -3167,7 +3173,7 @@ const textEncoder = new TextEncoder();
3167
3173
  const PatchId = z.string().refine(s => !!s); // TODO: validate
3168
3174
  const CommitSha = z.string().refine(s => !!s); // TODO: validate
3169
3175
  z.string().refine(s => !!s); // TODO: validate
3170
- const AuthorId = z.string().refine(s => !!s); // TODO: validate
3176
+ z.string().refine(s => !!s); // TODO: validate
3171
3177
  const ModuleFilePath = z.string().refine(s => !!s); // TODO: validate
3172
3178
  const Metadata = z.union([z.object({
3173
3179
  mimeType: z.string(),
@@ -3181,20 +3187,28 @@ const MetadataRes = z.object({
3181
3187
  metadata: Metadata,
3182
3188
  type: z.union([z.literal("file"), z.literal("image")]).nullable()
3183
3189
  });
3184
- const BasePatchResponse = z.object({
3185
- path: ModuleFilePath,
3186
- patchId: PatchId,
3187
- authorId: AuthorId.nullable(),
3188
- createdAt: z.string().datetime(),
3189
- baseSha: z.string()
3190
+ const SummaryResponse = z.object({
3191
+ commitSummary: z.string().nullable()
3190
3192
  });
3191
- const GetPatches = z.object({
3192
- patches: z.array(z.intersection(z.object({
3193
- patch: Patch.optional()
3194
- }), BasePatchResponse)),
3195
- errors: z.array(z.object({
3196
- patchId: PatchId.optional(),
3197
- message: z.string()
3193
+ const GetApplicablePatches = z.object({
3194
+ patches: z.array(z.object({
3195
+ path: z.string(),
3196
+ patch: Patch.nullable(),
3197
+ patchId: z.string(),
3198
+ authorId: z.string().nullable(),
3199
+ baseSha: z.string(),
3200
+ createdAt: z.string(),
3201
+ applied: z.object({
3202
+ commitSha: z.string()
3203
+ }).nullable()
3204
+ })),
3205
+ commits: z.array(z.object({
3206
+ commitSha: z.string(),
3207
+ clientCommitSha: z.string(),
3208
+ parentCommitSha: z.string(),
3209
+ branch: z.string(),
3210
+ creator: z.string(),
3211
+ createdAt: z.string()
3198
3212
  })).optional()
3199
3213
  });
3200
3214
  const FilesResponse = z.object({
@@ -3231,7 +3245,7 @@ const DeletePatchesResponse = z.object({
3231
3245
  patchId: PatchId
3232
3246
  })).optional()
3233
3247
  });
3234
- z.object({
3248
+ const SavePatchFileResponse = z.object({
3235
3249
  patchId: PatchId,
3236
3250
  filePath: ModuleFilePath
3237
3251
  });
@@ -3250,11 +3264,11 @@ const ProfilesResponse = z.object({
3250
3264
  }))
3251
3265
  });
3252
3266
  class ValOpsHttp extends ValOps {
3253
- constructor(hostUrl, project, commitSha,
3267
+ constructor(contentUrl, project, commitSha,
3254
3268
  // TODO: CommitSha
3255
3269
  branch, apiKey, valModules, options) {
3256
3270
  super(valModules, options);
3257
- this.hostUrl = hostUrl;
3271
+ this.contentUrl = contentUrl;
3258
3272
  this.project = project;
3259
3273
  this.commitSha = commitSha;
3260
3274
  this.branch = branch;
@@ -3266,6 +3280,70 @@ class ValOpsHttp extends ValOps {
3266
3280
  async onInit() {
3267
3281
  // TODO: unused for now. Implement or remove
3268
3282
  }
3283
+ async getCommitSummary(preparedCommit) {
3284
+ try {
3285
+ var _res$headers$get;
3286
+ const res = await fetch(`${this.contentUrl}/v1/${this.project}/commit-summary`, {
3287
+ method: "POST",
3288
+ headers: {
3289
+ ...this.authHeaders,
3290
+ "Content-Type": "application/json"
3291
+ },
3292
+ body: JSON.stringify({
3293
+ patchedSourceFiles: preparedCommit.patchedSourceFiles,
3294
+ previousSourceFiles: preparedCommit.previousSourceFiles
3295
+ })
3296
+ });
3297
+ if (res.ok) {
3298
+ const json = await res.json();
3299
+ const parsed = SummaryResponse.safeParse(json);
3300
+ if (parsed.success) {
3301
+ return {
3302
+ commitSummary: parsed.data.commitSummary
3303
+ };
3304
+ }
3305
+ console.error(`Could not parse summary response. Error: ${fromError(parsed.error).toString()}`);
3306
+ return {
3307
+ error: {
3308
+ 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.`
3309
+ }
3310
+ };
3311
+ }
3312
+ if (res.status === 401) {
3313
+ console.error("Unauthorized to get summary");
3314
+ return {
3315
+ error: {
3316
+ 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."
3317
+ }
3318
+ };
3319
+ }
3320
+ const unknownErrorMessage = `Could not get summary. HTTP error: ${res.status} ${res.statusText}`;
3321
+ if ((_res$headers$get = res.headers.get("Content-Type")) !== null && _res$headers$get !== void 0 && _res$headers$get.includes("application/json")) {
3322
+ const json = await res.json();
3323
+ if (json.message) {
3324
+ console.error("Summary error:", json.message);
3325
+ return {
3326
+ error: {
3327
+ message: json.message
3328
+ }
3329
+ };
3330
+ }
3331
+ }
3332
+ console.error(unknownErrorMessage);
3333
+ return {
3334
+ error: {
3335
+ message: unknownErrorMessage
3336
+ }
3337
+ };
3338
+ } catch (e) {
3339
+ console.error("Could not get summary (connection error?):", e);
3340
+ return {
3341
+ error: {
3342
+ message: `Could not get summary. Error: ${e instanceof Error ? e.message : JSON.stringify(e)}`
3343
+ }
3344
+ };
3345
+ }
3346
+ }
3269
3347
  async getStat(params) {
3270
3348
  if (!(params !== null && params !== void 0 && params.profileId)) {
3271
3349
  return {
@@ -3278,11 +3356,23 @@ class ValOpsHttp extends ValOps {
3278
3356
  const currentBaseSha = await this.getBaseSha();
3279
3357
  const currentSchemaSha = await this.getSchemaSha();
3280
3358
  const allPatchData = await this.fetchPatches({
3281
- omitPatch: true,
3282
- authors: undefined,
3283
- patchIds: undefined,
3284
- moduleFilePaths: undefined
3359
+ excludePatchOps: true,
3360
+ patchIds: undefined
3285
3361
  });
3362
+ if ("error" in allPatchData && allPatchData.error && allPatchData.unauthorized) {
3363
+ return {
3364
+ type: "error",
3365
+ error: allPatchData.error,
3366
+ unauthorized: true
3367
+ };
3368
+ }
3369
+ if ("error" in allPatchData && allPatchData.error && allPatchData.networkError) {
3370
+ return {
3371
+ type: "error",
3372
+ error: allPatchData.error,
3373
+ networkError: true
3374
+ };
3375
+ }
3286
3376
  // We think these errors will be picked up else where (?), so we only return an error here if there are no patches
3287
3377
  if (allPatchData.patches.length === 0) {
3288
3378
  let message;
@@ -3329,7 +3419,7 @@ class ValOpsHttp extends ValOps {
3329
3419
  };
3330
3420
  }
3331
3421
  async getWebSocketNonce(profileId) {
3332
- return fetch(`${this.hostUrl}/v1/${this.project}/websocket/nonces`, {
3422
+ return fetch(`${this.contentUrl}/v1/${this.project}/websocket/nonces`, {
3333
3423
  method: "POST",
3334
3424
  body: JSON.stringify({
3335
3425
  branch: this.branch,
@@ -3406,16 +3496,12 @@ class ValOpsHttp extends ValOps {
3406
3496
  if (patchIds === undefined || patchIds.length === 0) {
3407
3497
  return this.fetchPatchesInternal({
3408
3498
  patchIds: patchIds,
3409
- authors: filters.authors,
3410
- moduleFilePaths: filters.moduleFilePaths,
3411
- omitPatch: filters.omitPatch
3499
+ excludePatchOps: filters.excludePatchOps
3412
3500
  });
3413
3501
  }
3414
3502
  for (const res of await Promise.all(patchIdChunks.map(patchIdChunk => this.fetchPatchesInternal({
3415
3503
  patchIds: patchIdChunk,
3416
- authors: filters.authors,
3417
- moduleFilePaths: filters.moduleFilePaths,
3418
- omitPatch: filters.omitPatch
3504
+ excludePatchOps: filters.excludePatchOps
3419
3505
  })))) {
3420
3506
  if ("error" in res) {
3421
3507
  return res;
@@ -3433,72 +3519,97 @@ class ValOpsHttp extends ValOps {
3433
3519
  async fetchPatchesInternal(filters) {
3434
3520
  const params = [];
3435
3521
  params.push(["branch", this.branch]);
3522
+ params.push(["commit", this.commitSha]);
3436
3523
  if (filters.patchIds) {
3437
3524
  for (const patchId of filters.patchIds) {
3438
3525
  params.push(["patch_id", patchId]);
3439
3526
  }
3440
3527
  }
3441
- if (filters.authors) {
3442
- for (const author of filters.authors) {
3443
- params.push(["author_id", author]);
3444
- }
3445
- }
3446
- if (filters.omitPatch) {
3447
- params.push(["omit_patch", "true"]);
3448
- }
3449
- if (filters.moduleFilePaths) {
3450
- for (const moduleFilePath of filters.moduleFilePaths) {
3451
- params.push(["module_file_path", moduleFilePath]);
3452
- }
3528
+ if (filters.excludePatchOps) {
3529
+ params.push(["exclude_patch_ops", "true"]);
3453
3530
  }
3454
3531
  const searchParams = new URLSearchParams(params);
3455
- return fetch(`${this.hostUrl}/v1/${this.project}/patches${searchParams.size > 0 ? `?${searchParams.toString()}` : ""}`, {
3456
- headers: {
3457
- ...this.authHeaders,
3458
- "Content-Type": "application/json"
3459
- }
3460
- }).then(async res => {
3532
+ try {
3533
+ const patchesRes = await fetch(`${this.contentUrl}/v1/${this.project}/applicable/patches${searchParams.size > 0 ? `?${searchParams.toString()}` : ""}`, {
3534
+ headers: this.authHeaders
3535
+ });
3461
3536
  const patches = [];
3462
- if (res.ok) {
3463
- const json = await res.json();
3464
- const parsed = GetPatches.safeParse(json);
3537
+ if (patchesRes.ok) {
3538
+ const json = await patchesRes.json();
3539
+ const parsed = GetApplicablePatches.safeParse(json);
3465
3540
  if (parsed.success) {
3466
3541
  const errors = [];
3467
3542
  const data = parsed.data;
3468
3543
  for (const patchesRes of data.patches) {
3544
+ var _patchesRes$applied;
3469
3545
  patches.push({
3470
3546
  authorId: patchesRes.authorId,
3471
3547
  createdAt: patchesRes.createdAt,
3472
- appliedAt: null,
3473
3548
  patchId: patchesRes.patchId,
3474
3549
  path: patchesRes.path,
3475
3550
  baseSha: patchesRes.baseSha,
3476
- patch: filters.omitPatch ? undefined : patchesRes.patch
3551
+ patch: patchesRes.patch,
3552
+ appliedAt: (_patchesRes$applied = patchesRes.applied) !== null && _patchesRes$applied !== void 0 && _patchesRes$applied.commitSha ? {
3553
+ commitSha: patchesRes.applied.commitSha
3554
+ } : null
3477
3555
  });
3478
3556
  }
3557
+ const commits = [];
3558
+ if (data.commits) {
3559
+ for (const commit of data.commits) {
3560
+ commits.push({
3561
+ commitSha: commit.commitSha,
3562
+ clientCommitSha: commit.clientCommitSha,
3563
+ parentCommitSha: commit.parentCommitSha,
3564
+ branch: commit.branch,
3565
+ creator: commit.creator,
3566
+ createdAt: commit.createdAt
3567
+ });
3568
+ }
3569
+ }
3479
3570
  return {
3571
+ commits,
3480
3572
  patches,
3481
3573
  errors
3482
3574
  };
3483
3575
  }
3576
+ console.error("Could not parse patches response. Error: " + fromError(parsed.error));
3577
+ return {
3578
+ patches,
3579
+ error: {
3580
+ 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.`
3581
+ }
3582
+ };
3583
+ }
3584
+ console.error("Could not get patches. HTTP error: " + patchesRes.status + " " + patchesRes.statusText);
3585
+ if (patchesRes.status === 401) {
3484
3586
  return {
3485
3587
  patches,
3486
3588
  error: {
3487
- message: `Could not parse get patches response. Error: ${fromError(parsed.error)}`
3589
+ 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."
3488
3590
  }
3489
3591
  };
3490
3592
  }
3491
3593
  return {
3492
3594
  patches,
3493
3595
  error: {
3494
- message: "Could not get patches. HTTP error: " + res.status + " " + res.statusText
3596
+ message: "Could not your changes. It is most likely due to a network issue. Check your network connection and please try again."
3495
3597
  }
3496
3598
  };
3497
- });
3599
+ } catch (err) {
3600
+ console.error("Could not get patches (connection error):", err instanceof Error ? err.message : JSON.stringify(err));
3601
+ return {
3602
+ patches: [],
3603
+ networkError: true,
3604
+ error: {
3605
+ message: `Error: ${err instanceof Error ? err.message : JSON.stringify(err)}`
3606
+ }
3607
+ };
3608
+ }
3498
3609
  }
3499
3610
  async saveSourceFilePatch(path, patch, parentRef, authorId) {
3500
3611
  const baseSha = await this.getBaseSha();
3501
- return fetch(`${this.hostUrl}/v1/${this.project}/patches`, {
3612
+ return fetch(`${this.contentUrl}/v1/${this.project}/patches`, {
3502
3613
  method: "POST",
3503
3614
  headers: {
3504
3615
  ...this.authHeaders,
@@ -3515,7 +3626,7 @@ class ValOpsHttp extends ValOps {
3515
3626
  coreVersion: Internal.VERSION.core
3516
3627
  })
3517
3628
  }).then(async res => {
3518
- var _res$headers$get;
3629
+ var _res$headers$get2;
3519
3630
  if (res.ok) {
3520
3631
  const parsed = SavePatchResponse.safeParse(await res.json());
3521
3632
  if (parsed.success) {
@@ -3534,7 +3645,7 @@ class ValOpsHttp extends ValOps {
3534
3645
  message: "Conflict: " + (await res.text())
3535
3646
  });
3536
3647
  }
3537
- if ((_res$headers$get = res.headers.get("Content-Type")) !== null && _res$headers$get !== void 0 && _res$headers$get.includes("application/json")) {
3648
+ if ((_res$headers$get2 = res.headers.get("Content-Type")) !== null && _res$headers$get2 !== void 0 && _res$headers$get2.includes("application/json")) {
3538
3649
  const json = await res.json();
3539
3650
  return result.err({
3540
3651
  errorType: "other",
@@ -3553,7 +3664,45 @@ class ValOpsHttp extends ValOps {
3553
3664
  });
3554
3665
  }
3555
3666
  async saveBase64EncodedBinaryFileFromPatch(filePath, parentRef, patchId, data, type, metadata) {
3556
- throw Error("TODO: implement");
3667
+ return fetch(`${this.contentUrl}/v1/${this.project}/patches/${patchId}/files`, {
3668
+ method: "POST",
3669
+ headers: {
3670
+ ...this.authHeaders,
3671
+ "Content-Type": "application/json"
3672
+ },
3673
+ body: JSON.stringify({
3674
+ filePath: filePath,
3675
+ data,
3676
+ type,
3677
+ metadata
3678
+ })
3679
+ }).then(async res => {
3680
+ if (res.ok) {
3681
+ const parsed = SavePatchFileResponse.safeParse(await res.json());
3682
+ if (parsed.success) {
3683
+ return {
3684
+ patchId: parsed.data.patchId,
3685
+ filePath: parsed.data.filePath
3686
+ };
3687
+ }
3688
+ return {
3689
+ error: {
3690
+ message: `Could not parse save patch file response. Error: ${fromError(parsed.error)}`
3691
+ }
3692
+ };
3693
+ }
3694
+ return {
3695
+ error: {
3696
+ message: "Could not save patch file. HTTP error: " + res.status + " " + res.statusText
3697
+ }
3698
+ };
3699
+ }).catch(e => {
3700
+ return {
3701
+ error: {
3702
+ message: `Could save source binary file in patch (connection error?): ${e.toString()}`
3703
+ }
3704
+ };
3705
+ });
3557
3706
  }
3558
3707
  async getHttpFiles(files) {
3559
3708
  const params = new URLSearchParams();
@@ -3564,7 +3713,7 @@ class ValOpsHttp extends ValOps {
3564
3713
  params.set("body_sha",
3565
3714
  // We use this for cache invalidation
3566
3715
  Internal.getSHA256Hash(textEncoder.encode(stringifiedFiles)));
3567
- return fetch(`${this.hostUrl}/v1/${this.project}/files?${params}`, {
3716
+ return fetch(`${this.contentUrl}/v1/${this.project}/files?${params}`, {
3568
3717
  method: "PUT",
3569
3718
  // Yes, PUT is weird. Weirder to have a body in a GET request.
3570
3719
  headers: {
@@ -3657,7 +3806,7 @@ class ValOpsHttp extends ValOps {
3657
3806
  const params = new URLSearchParams();
3658
3807
  params.set("file_path", filePath);
3659
3808
  try {
3660
- const metadataRes = await fetch(`${this.hostUrl}/v1/${this.project}/patches/${patchId}/files?${params}`, {
3809
+ const metadataRes = await fetch(`${this.contentUrl}/v1/${this.project}/patches/${patchId}/files?${params}`, {
3661
3810
  headers: {
3662
3811
  ...this.authHeaders,
3663
3812
  "Content-Type": "application/json"
@@ -3711,7 +3860,7 @@ class ValOpsHttp extends ValOps {
3711
3860
  };
3712
3861
  }
3713
3862
  async deletePatches(patchIds) {
3714
- return fetch(`${this.hostUrl}/v1/${this.project}/patches`, {
3863
+ return fetch(`${this.contentUrl}/v1/${this.project}/patches`, {
3715
3864
  method: "DELETE",
3716
3865
  headers: {
3717
3866
  ...this.authHeaders,
@@ -3757,11 +3906,44 @@ class ValOpsHttp extends ValOps {
3757
3906
  };
3758
3907
  });
3759
3908
  }
3760
- async commit(prepared, message, committer, newBranch) {
3909
+ async getCommitMessage(preparedCommit) {
3910
+ var _res$headers$get3;
3911
+ const res = await fetch(`${this.contentUrl}/v1/${this.project}/commit-summary`, {
3912
+ method: "POST",
3913
+ headers: {
3914
+ ...this.authHeaders,
3915
+ "Content-Type": "application/json"
3916
+ },
3917
+ body: JSON.stringify({
3918
+ patchedSourceFiles: preparedCommit.patchedSourceFiles,
3919
+ previousSourceFiles: preparedCommit.previousSourceFiles
3920
+ })
3921
+ });
3922
+ if (res.ok) {
3923
+ const json = await res.json();
3924
+ return {
3925
+ commitSummary: json.commitSummary
3926
+ };
3927
+ }
3928
+ if ((_res$headers$get3 = res.headers.get("Content-Type")) !== null && _res$headers$get3 !== void 0 && _res$headers$get3.includes("application/json")) {
3929
+ const json = await res.json();
3930
+ return {
3931
+ error: {
3932
+ message: json.message
3933
+ }
3934
+ };
3935
+ }
3936
+ return {
3937
+ error: {
3938
+ message: "Could not get commit message. HTTP error: " + res.status + " " + res.statusText
3939
+ }
3940
+ };
3941
+ }
3942
+ async commit(prepared, message, committer, filesDirectory, newBranch) {
3761
3943
  try {
3762
- var _res$headers$get2;
3944
+ var _res$headers$get4;
3763
3945
  const existingBranch = this.branch;
3764
- const res = await fetch(`${this.hostUrl}/v1/${this.project}/commit`, {
3946
+ const res = await fetch(`${this.contentUrl}/v1/${this.project}/commit`, {
3765
3947
  method: "POST",
3766
3948
  headers: {
3767
3949
  ...this.authHeaders,
@@ -3773,6 +3955,7 @@ class ValOpsHttp extends ValOps {
3773
3955
  appliedPatches: prepared.appliedPatches,
3774
3956
  commit: this.commitSha,
3775
3957
  root: this.root,
3958
+ filesDirectory,
3776
3959
  baseSha: await this.getBaseSha(),
3777
3960
  committer,
3778
3961
  message,
@@ -3795,7 +3978,7 @@ class ValOpsHttp extends ValOps {
3795
3978
  }
3796
3979
  };
3797
3980
  }
3798
- if ((_res$headers$get2 = res.headers.get("Content-Type")) !== null && _res$headers$get2 !== void 0 && _res$headers$get2.includes("application/json")) {
3981
+ if ((_res$headers$get4 = res.headers.get("Content-Type")) !== null && _res$headers$get4 !== void 0 && _res$headers$get4.includes("application/json")) {
3799
3982
  const json = await res.json();
3800
3983
  if (json.isNotFastForward) {
3801
3984
  return {
@@ -3827,8 +4010,8 @@ class ValOpsHttp extends ValOps {
3827
4010
 
3828
4011
  // #region profiles
3829
4012
  async getProfiles() {
3830
- var _res$headers$get3;
3831
- const res = await fetch(`${this.hostUrl}/v1/${this.project}/profiles`, {
4013
+ var _res$headers$get5;
4014
+ const res = await fetch(`${this.contentUrl}/v1/${this.project}/profiles`, {
3832
4015
  headers: {
3833
4016
  ...this.authHeaders,
3834
4017
  "Content-Type": "application/json"
@@ -3842,7 +4025,7 @@ class ValOpsHttp extends ValOps {
3842
4025
  }
3843
4026
  return parsed.data.profiles;
3844
4027
  }
3845
- if ((_res$headers$get3 = res.headers.get("Content-Type")) !== null && _res$headers$get3 !== void 0 && _res$headers$get3.includes("application/json")) {
4028
+ if ((_res$headers$get5 = res.headers.get("Content-Type")) !== null && _res$headers$get5 !== void 0 && _res$headers$get5.includes("application/json")) {
3846
4029
  const json = await res.json();
3847
4030
  throw Error(`Could not get profiles (status: ${res.status}): ${"message" in json ? json.message : "Unknown error"}`);
3848
4031
  }
@@ -3880,6 +4063,7 @@ const ValServer = (valModules, options, callbacks) => {
3880
4063
  url.searchParams.set("state", token);
3881
4064
  return url.toString();
3882
4065
  };
4066
+ const commit = options.mode === "http" ? options.commit : undefined;
3883
4067
  const getAppErrorUrl = error => {
3884
4068
  if (!options.project) {
3885
4069
  throw new Error("Project is not set");
@@ -4342,6 +4526,22 @@ const ValServer = (valModules, options, callbacks) => {
4342
4526
  ...req.body,
4343
4527
  profileId: "id" in auth ? auth.id : undefined
4344
4528
  });
4529
+ if (currentStat.type === "error" && currentStat.networkError) {
4530
+ return {
4531
+ status: 503,
4532
+ json: {
4533
+ message: "Network error"
4534
+ }
4535
+ };
4536
+ }
4537
+ if (currentStat.type === "error" && currentStat.unauthorized) {
4538
+ return {
4539
+ status: 401,
4540
+ json: {
4541
+ message: "Unauthorized"
4542
+ }
4543
+ };
4544
+ }
4345
4545
  if (currentStat.type === "error") {
4346
4546
  return {
4347
4547
  status: 500,
@@ -4352,6 +4552,7 @@ const ValServer = (valModules, options, callbacks) => {
4352
4552
  status: 200,
4353
4553
  json: {
4354
4554
  ...currentStat,
4555
+ mode: serverOps instanceof ValOpsFS ? "fs" : serverOps instanceof ValOpsHttp ? "http" : "unknown",
4355
4556
  config: options.config
4356
4557
  }
4357
4558
  };
@@ -4379,7 +4580,7 @@ const ValServer = (valModules, options, callbacks) => {
4379
4580
  };
4380
4581
  }
4381
4582
  const patches = req.body.patches;
4382
- const parentRef = req.body.parentRef;
4583
+ let parentRef = req.body.parentRef;
4383
4584
  const authorId = "id" in auth ? auth.id : null;
4384
4585
  const newPatchIds = [];
4385
4586
  for (const patch of patches) {
@@ -4410,6 +4611,10 @@ const ValServer = (valModules, options, callbacks) => {
4410
4611
  };
4411
4612
  }
4412
4613
  } else {
4614
+ parentRef = {
4615
+ type: "patch",
4616
+ patchId: createPatchRes.value.patchId
4617
+ };
4413
4618
  newPatchIds.push(createPatchRes.value.patchId);
4414
4619
  }
4415
4620
  }
@@ -4417,10 +4622,7 @@ const ValServer = (valModules, options, callbacks) => {
4417
4622
  status: 200,
4418
4623
  json: {
4419
4624
  newPatchIds,
4420
- parentRef: {
4421
- type: "patch",
4422
- patchId: newPatchIds[newPatchIds.length - 1]
4423
- }
4625
+ parentRef
4424
4626
  }
4425
4627
  };
4426
4628
  },
@@ -4445,13 +4647,10 @@ const ValServer = (valModules, options, callbacks) => {
4445
4647
  }
4446
4648
  };
4447
4649
  }
4448
- const omit_patch = query.omit_patch === true;
4449
- const authors = query.author;
4650
+ const excludePatchOps = query.exclude_patch_ops === true;
4450
4651
  const fetchedPatchesRes = await serverOps.fetchPatches({
4451
- authors,
4452
4652
  patchIds: query.patch_id,
4453
- omitPatch: omit_patch,
4454
- moduleFilePaths: query.module_file_path
4653
+ excludePatchOps: excludePatchOps
4455
4654
  });
4456
4655
  if (fetchedPatchesRes.error) {
4457
4656
  // Error is singular
@@ -4632,8 +4831,17 @@ const ValServer = (valModules, options, callbacks) => {
4632
4831
  }
4633
4832
  const patchOps = await serverOps.fetchPatches({
4634
4833
  patchIds: undefined,
4635
- omitPatch: false
4834
+ excludePatchOps: false
4636
4835
  });
4836
+ // We check authorization here, because it is the first call to the backend
4837
+ if (patchOps.error && patchOps.unauthorized) {
4838
+ return {
4839
+ status: 401,
4840
+ json: {
4841
+ message: "Unauthorized"
4842
+ }
4843
+ };
4844
+ }
4637
4845
  const patchAnalysis = serverOps.analyzePatches(patchOps.patches);
4638
4846
  let sourcesRes = await serverOps.getSources();
4639
4847
  const onlyPatchedTreeModules = await serverOps.getSources({
@@ -4712,17 +4920,16 @@ const ValServer = (valModules, options, callbacks) => {
4712
4920
  },
4713
4921
  "/profiles": {
4714
4922
  GET: async req => {
4715
- // const cookies = req.cookies;
4716
- // const auth = getAuth(cookies);
4717
- // if (auth.error) {
4718
- // return {
4719
- // status: 401,
4720
- // json: {
4721
- // message: auth.error,
4722
- // },
4723
- // };
4724
- // }
4725
-
4923
+ const cookies = req.cookies;
4924
+ const auth = getAuth(cookies);
4925
+ if (auth.error) {
4926
+ return {
4927
+ status: 401,
4928
+ json: {
4929
+ message: auth.error
4930
+ }
4931
+ };
4932
+ }
4726
4933
  const profiles = await serverOps.getProfiles();
4727
4934
  return {
4728
4935
  status: 200,
@@ -4732,6 +4939,49 @@ const ValServer = (valModules, options, callbacks) => {
4732
4939
  };
4733
4940
  }
4734
4941
  },
4942
+ "/commit-summary": {
4943
+ GET: async req => {
4944
+ const cookies = req.cookies;
4945
+ const auth = getAuth(cookies);
4946
+ if (auth.error) {
4947
+ return {
4948
+ status: 401,
4949
+ json: {
4950
+ message: auth.error
4951
+ }
4952
+ };
4953
+ }
4954
+ const query = req.query;
4955
+ const patchIds = query.patch_id;
4956
+ const patches = await serverOps.fetchPatches({
4957
+ patchIds,
4958
+ excludePatchOps: false
4959
+ });
4960
+ const analysis = serverOps.analyzePatches(patches.patches, patches.commits, commit);
4961
+ const preparedCommit = await serverOps.prepare({
4962
+ ...analysis,
4963
+ ...patches
4964
+ });
4965
+ const res = await serverOps.getCommitSummary(preparedCommit);
4966
+ if (res.error) {
4967
+ console.error("Failed to summarize", res.error);
4968
+ return {
4969
+ status: 400,
4970
+ json: {
4971
+ message: res.error.message
4972
+ }
4973
+ };
4974
+ }
4975
+ return {
4976
+ status: 200,
4977
+ json: {
4978
+ baseSha: await serverOps.getBaseSha(),
4979
+ patchIds,
4980
+ commitSummary: res.commitSummary
4981
+ }
4982
+ };
4983
+ }
4984
+ },
4735
4985
  "/save": {
4736
4986
  POST: async req => {
4737
4987
  const cookies = req.cookies;
@@ -4764,9 +5014,9 @@ const ValServer = (valModules, options, callbacks) => {
4764
5014
  } = bodyRes.data;
4765
5015
  const patches = await serverOps.fetchPatches({
4766
5016
  patchIds,
4767
- omitPatch: false
5017
+ excludePatchOps: false
4768
5018
  });
4769
- const analysis = serverOps.analyzePatches(patches.patches);
5019
+ const analysis = serverOps.analyzePatches(patches.patches, patches.commits, commit);
4770
5020
  const preparedCommit = await serverOps.prepare({
4771
5021
  ...analysis,
4772
5022
  ...patches
@@ -4798,7 +5048,24 @@ const ValServer = (valModules, options, callbacks) => {
4798
5048
  };
4799
5049
  } else if (serverOps instanceof ValOpsHttp) {
4800
5050
  if (auth.error === undefined && auth.id) {
4801
- const commitRes = await serverOps.commit(preparedCommit, "Update content: " + Object.keys(analysis.patchesByModule) + " modules changed", auth.id);
5051
+ var _options$config$ai, _options$config$files;
5052
+ let message = body.message || "Update content: " + Object.keys(analysis.patchesByModule) + " modules changed";
5053
+ 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)) {
5054
+ const res = await serverOps.getCommitMessage(preparedCommit);
5055
+ console.log({
5056
+ res
5057
+ });
5058
+ if (res.error) {
5059
+ // ignore
5060
+ console.error("Failed to get commit message", res.error.message);
5061
+ } else {
5062
+ message = res.commitSummary;
5063
+ }
5064
+ }
5065
+ console.log({
5066
+ message
5067
+ });
5068
+ 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");
4802
5069
  if (commitRes.error) {
4803
5070
  console.error("Failed to commit", commitRes.error);
4804
5071
  if ("isNotFastForward" in commitRes && commitRes.isNotFastForward) {