@valbuild/server 0.137.0 → 0.138.1
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.
|
@@ -8,7 +8,7 @@ import path__default from 'path';
|
|
|
8
8
|
import fs from 'fs';
|
|
9
9
|
import vm from 'node:vm';
|
|
10
10
|
import { Module } from 'node:module';
|
|
11
|
-
import { isSchemaSourceFixError, resolveSchemaSourceFixForError, Patch, getErrorMessageFromUnknownJson, PatchId as PatchId$1, JSONValue, PatchGroup, newestCommitSha, SerializedSchema, VAL_ENABLE_COOKIE_NAME, VAL_STATE_COOKIE, VAL_SESSION_COOKIE, Api } from '@valbuild/shared/internal';
|
|
11
|
+
import { isSchemaSourceFixError, resolveSchemaSourceFixForError, Patch, getErrorMessageFromUnknownJson, PatchId as PatchId$1, JSONValue, PatchGroup, newestCommitSha, SerializedSchema, VAL_ENABLE_COOKIE_NAME, VAL_STATE_COOKIE, VAL_SESSION_COOKIE, parseJob, Api } from '@valbuild/shared/internal';
|
|
12
12
|
import { createUIRequestHandler } from '@valbuild/ui/server';
|
|
13
13
|
import crypto$1 from 'crypto';
|
|
14
14
|
import z$1, { z } from 'zod';
|
|
@@ -5133,6 +5133,15 @@ class ValOps {
|
|
|
5133
5133
|
return null;
|
|
5134
5134
|
}
|
|
5135
5135
|
|
|
5136
|
+
/**
|
|
5137
|
+
* Does a press of Publish run as a publish job (valbuild/home,
|
|
5138
|
+
* docs/app-mode.md, "Publishing is a queued job")? Never for a project
|
|
5139
|
+
* with no content service.
|
|
5140
|
+
*/
|
|
5141
|
+
publishesAsJobs() {
|
|
5142
|
+
return false;
|
|
5143
|
+
}
|
|
5144
|
+
|
|
5136
5145
|
/**
|
|
5137
5146
|
* The branch the content service keeps this project's commits on, or `null`
|
|
5138
5147
|
* where there is no such service, or it has not said yet.
|
|
@@ -8108,6 +8117,59 @@ async function readCommittedBinaryFiles(ops, descriptors) {
|
|
|
8108
8117
|
};
|
|
8109
8118
|
}
|
|
8110
8119
|
|
|
8120
|
+
/**
|
|
8121
|
+
* A publish job's prepare, split into what the tab builds and what content
|
|
8122
|
+
* archives.
|
|
8123
|
+
*
|
|
8124
|
+
* A job's content is its base plus every change it took (valbuild/home,
|
|
8125
|
+
* docs/app-mode.md, "Publishing is a queued job"). The server has two
|
|
8126
|
+
* `prepare`s to hand: `chainOnly` -- this build's embedded source with every
|
|
8127
|
+
* commit since applied, which is the branch's head -- and `withJob`, the same
|
|
8128
|
+
* with the job's pending changes on top. Anything applied since the job
|
|
8129
|
+
* started came out of the job's own change set, so `withJob` IS the job's
|
|
8130
|
+
* content; were that ever not so, the seal's containment check would restart
|
|
8131
|
+
* the job rather than publish it.
|
|
8132
|
+
*
|
|
8133
|
+
* - The BUILD needs all of `withJob`'s source files: it is a whole site.
|
|
8134
|
+
* - The ARCHIVE is what the commit publishes, so content is sent only what the
|
|
8135
|
+
* job changes: the files whose text differs from the head's, and the modules
|
|
8136
|
+
* and binary files its changes touch. Everything else is the head's, and an
|
|
8137
|
+
* archive that listed it would say this commit changed it.
|
|
8138
|
+
*
|
|
8139
|
+
* Files are compared rather than derived from module paths because a change to
|
|
8140
|
+
* a module can write a file under another path -- a `.val.json` entry of a
|
|
8141
|
+
* record -- which only the prepared output knows about.
|
|
8142
|
+
*/
|
|
8143
|
+
function splitJobPrepare(input) {
|
|
8144
|
+
const job = new Set(input.jobPatchIds);
|
|
8145
|
+
const before = input.chainOnly.patchedSourceFiles;
|
|
8146
|
+
const after = input.withJob.patchedSourceFiles;
|
|
8147
|
+
const patchedSourceFiles = {};
|
|
8148
|
+
for (const [path, text] of Object.entries(after)) {
|
|
8149
|
+
if (!(path in before) || before[path] !== text) {
|
|
8150
|
+
patchedSourceFiles[path] = text;
|
|
8151
|
+
}
|
|
8152
|
+
}
|
|
8153
|
+
const patchedBinaryFilesDescriptors = {};
|
|
8154
|
+
for (const [path, descriptor] of Object.entries(input.withJob.patchedBinaryFilesDescriptors)) {
|
|
8155
|
+
if (job.has(descriptor.patchId)) {
|
|
8156
|
+
patchedBinaryFilesDescriptors[path] = descriptor;
|
|
8157
|
+
}
|
|
8158
|
+
}
|
|
8159
|
+
|
|
8160
|
+
// The modules any of the job's changes were applied to.
|
|
8161
|
+
const touched = new Set(Object.entries(input.withJob.appliedPatches).filter(([, applied]) => applied.some(id => job.has(id))).map(([path]) => path));
|
|
8162
|
+
const modules = Object.fromEntries(Object.entries(input.withJob.moduleVersions).filter(([path]) => touched.has(path)));
|
|
8163
|
+
return {
|
|
8164
|
+
buildSourceFiles: after,
|
|
8165
|
+
archive: {
|
|
8166
|
+
patchedSourceFiles,
|
|
8167
|
+
patchedBinaryFilesDescriptors,
|
|
8168
|
+
modules
|
|
8169
|
+
}
|
|
8170
|
+
};
|
|
8171
|
+
}
|
|
8172
|
+
|
|
8111
8173
|
/**
|
|
8112
8174
|
* Which unpublished changes a failed save has to throw away to make progress.
|
|
8113
8175
|
*
|
|
@@ -8292,7 +8354,13 @@ const GetApplicablePatches = z.object({
|
|
|
8292
8354
|
*/
|
|
8293
8355
|
project: z.object({
|
|
8294
8356
|
sourceMode: z.union([z.literal("managed"), z.literal("connected")]),
|
|
8295
|
-
branch: z.string()
|
|
8357
|
+
branch: z.string(),
|
|
8358
|
+
/**
|
|
8359
|
+
* Whether a press of Publish is a publish JOB. Absent from a content
|
|
8360
|
+
* service that predates connected jobs: then managed projects publish
|
|
8361
|
+
* as jobs, and connected ones by commit, as they did.
|
|
8362
|
+
*/
|
|
8363
|
+
publishJobs: z.boolean().optional()
|
|
8296
8364
|
}).optional()
|
|
8297
8365
|
});
|
|
8298
8366
|
const FilesResponse = z.object({
|
|
@@ -8649,6 +8717,17 @@ class ValOpsHttp extends ValOps {
|
|
|
8649
8717
|
return ((_this$projectExpectat3 = this.projectExpectation) === null || _this$projectExpectat3 === void 0 ? void 0 : _this$projectExpectat3.branch) ?? null;
|
|
8650
8718
|
}
|
|
8651
8719
|
|
|
8720
|
+
/**
|
|
8721
|
+
* Does a press of Publish run as a publish job? Every managed project, and
|
|
8722
|
+
* a connected one content says is hosted on the platform (its CI publishes
|
|
8723
|
+
* through content). `false` before anything has been heard.
|
|
8724
|
+
*/
|
|
8725
|
+
publishesAsJobs() {
|
|
8726
|
+
const expected = this.projectExpectation;
|
|
8727
|
+
if (expected === null) return false;
|
|
8728
|
+
return expected.publishJobs ?? expected.sourceMode === "managed";
|
|
8729
|
+
}
|
|
8730
|
+
|
|
8652
8731
|
/**
|
|
8653
8732
|
* The short-lived publish token, and when it stops being usable.
|
|
8654
8733
|
*
|
|
@@ -8763,22 +8842,66 @@ class ValOpsHttp extends ValOps {
|
|
|
8763
8842
|
* segment gets through.
|
|
8764
8843
|
*/
|
|
8765
8844
|
static publishApiPathAllowed(path) {
|
|
8766
|
-
if (path === "/build-target" || path === "/project-source" || path === "/update-target" || path === "/publish"
|
|
8845
|
+
if (path === "/build-target" || path === "/project-source" || path === "/update-target" || path === "/publish" ||
|
|
8846
|
+
// Where "View run" goes, for a connected build CI reported failed.
|
|
8847
|
+
// Read only: reporting a run is CI's, with its own token.
|
|
8848
|
+
path === "/ci-runs/newest") {
|
|
8849
|
+
return true;
|
|
8850
|
+
}
|
|
8851
|
+
/*
|
|
8852
|
+
* Publishing as a queued job: a press, where it is, Try again, and the
|
|
8853
|
+
* tab's reports about the job it runs. NOT `/publish-jobs/{id}/prepare`:
|
|
8854
|
+
* its body is the job's source files and modules, which this server
|
|
8855
|
+
* computes (`prepareJob`) -- a browser allowed to send it could publish
|
|
8856
|
+
* any text at all as a commit's archive.
|
|
8857
|
+
*/
|
|
8858
|
+
if (/^\/publish-requests(\/[A-Za-z0-9_-]{1,100})?$/.test(path)) {
|
|
8859
|
+
return true;
|
|
8860
|
+
}
|
|
8861
|
+
if (/^\/publish-jobs\/(next|[A-Za-z0-9_-]{1,100}\/(steps|renew|cancel|discard))$/.test(path)) {
|
|
8767
8862
|
return true;
|
|
8768
8863
|
}
|
|
8769
8864
|
return /^\/publish\/[A-Za-z0-9_-]+(\/(artifacts|verify|promote))?$/.test(path);
|
|
8770
8865
|
}
|
|
8771
8866
|
async publishApi(path, init) {
|
|
8772
|
-
const json = "application/json";
|
|
8773
8867
|
if (!ValOpsHttp.publishApiPathAllowed(path)) {
|
|
8774
8868
|
return {
|
|
8775
8869
|
status: 403,
|
|
8776
|
-
contentType: json,
|
|
8870
|
+
contentType: "application/json",
|
|
8777
8871
|
body: JSON.stringify({
|
|
8778
8872
|
message: `'${path}' is not part of the publish API.`
|
|
8779
8873
|
})
|
|
8780
8874
|
};
|
|
8781
8875
|
}
|
|
8876
|
+
return this.callPublishApi(path, init);
|
|
8877
|
+
}
|
|
8878
|
+
|
|
8879
|
+
/**
|
|
8880
|
+
* A publish job's prepare, sent to content: the job's sources, archived
|
|
8881
|
+
* there before the job is built (valbuild/home, docs/app-mode.md, "The
|
|
8882
|
+
* archive, before the seal"). With this server's publish token, as every
|
|
8883
|
+
* publish call is, and never through `publishApi` -- see the allowlist.
|
|
8884
|
+
*/
|
|
8885
|
+
async prepareJob(jobId, body) {
|
|
8886
|
+
if (!/^[A-Za-z0-9_-]{1,100}$/.test(jobId)) {
|
|
8887
|
+
return {
|
|
8888
|
+
status: 400,
|
|
8889
|
+
contentType: "application/json",
|
|
8890
|
+
body: JSON.stringify({
|
|
8891
|
+
message: `'${jobId}' is not a job id.`
|
|
8892
|
+
})
|
|
8893
|
+
};
|
|
8894
|
+
}
|
|
8895
|
+
return this.callPublishApi(`/publish-jobs/${jobId}/prepare`, {
|
|
8896
|
+
method: "POST",
|
|
8897
|
+
body: JSON.stringify({
|
|
8898
|
+
...body,
|
|
8899
|
+
root: this.root
|
|
8900
|
+
})
|
|
8901
|
+
});
|
|
8902
|
+
}
|
|
8903
|
+
async callPublishApi(path, init) {
|
|
8904
|
+
const json = "application/json";
|
|
8782
8905
|
const send = async token => fetch(`${this.contentUrl}/v1${path}`, {
|
|
8783
8906
|
method: init.method,
|
|
8784
8907
|
headers: {
|
|
@@ -12930,6 +13053,9 @@ const ValServer = (valModules, options, callbacks) => {
|
|
|
12930
13053
|
...(sourceMode ? {
|
|
12931
13054
|
sourceMode
|
|
12932
13055
|
} : {}),
|
|
13056
|
+
...(sourceMode ? {
|
|
13057
|
+
publishJobs: serverOps.publishesAsJobs()
|
|
13058
|
+
} : {}),
|
|
12933
13059
|
// Not `options.config` verbatim: in proxy mode the branch the
|
|
12934
13060
|
// server resolved is filled in where the file did not name one.
|
|
12935
13061
|
// See `clientConfig`.
|
|
@@ -14095,8 +14221,9 @@ const ValServer = (valModules, options, callbacks) => {
|
|
|
14095
14221
|
return res;
|
|
14096
14222
|
}
|
|
14097
14223
|
},
|
|
14098
|
-
"/
|
|
14099
|
-
|
|
14224
|
+
"/publish-job-prepare": {
|
|
14225
|
+
POST: async req => {
|
|
14226
|
+
var _options$config$files2;
|
|
14100
14227
|
const auth = getAuth(req.cookies);
|
|
14101
14228
|
if (auth.error) {
|
|
14102
14229
|
return {
|
|
@@ -14106,14 +14233,93 @@ const ValServer = (valModules, options, callbacks) => {
|
|
|
14106
14233
|
}
|
|
14107
14234
|
};
|
|
14108
14235
|
}
|
|
14109
|
-
if (!(serverOps instanceof ValOpsHttp) || !serverOps.embedsSource()) {
|
|
14236
|
+
if (!(serverOps instanceof ValOpsHttp) || !serverOps.publishesAsJobs() || serverOps.sourceMode() === "managed" && !serverOps.embedsSource()) {
|
|
14237
|
+
return {
|
|
14238
|
+
status: 409,
|
|
14239
|
+
json: {
|
|
14240
|
+
message: "This deployment cannot prepare a publish job for this project."
|
|
14241
|
+
}
|
|
14242
|
+
};
|
|
14243
|
+
}
|
|
14244
|
+
const refusal = serverOps.publishRefusal();
|
|
14245
|
+
if (refusal) {
|
|
14110
14246
|
return {
|
|
14111
14247
|
status: 409,
|
|
14112
14248
|
json: {
|
|
14113
|
-
message:
|
|
14249
|
+
message: refusal.message
|
|
14114
14250
|
}
|
|
14115
14251
|
};
|
|
14116
14252
|
}
|
|
14253
|
+
const {
|
|
14254
|
+
jobId,
|
|
14255
|
+
tab,
|
|
14256
|
+
patchIds
|
|
14257
|
+
} = req.body;
|
|
14258
|
+
/*
|
|
14259
|
+
* CONNECTED: the job's sources are what `/save` would commit -- its
|
|
14260
|
+
* own changes, applied as `/save` applies them -- and content pushes
|
|
14261
|
+
* them at the seal. Nothing is built here: CI builds after the push,
|
|
14262
|
+
* so the answer carries the job and no sources.
|
|
14263
|
+
*/
|
|
14264
|
+
if (serverOps.sourceMode() === "connected") {
|
|
14265
|
+
var _options$config$files;
|
|
14266
|
+
const jobPatches = await serverOps.fetchPatches({
|
|
14267
|
+
patchIds,
|
|
14268
|
+
excludePatchOps: false
|
|
14269
|
+
});
|
|
14270
|
+
if (jobPatches.error) {
|
|
14271
|
+
return {
|
|
14272
|
+
status: 500,
|
|
14273
|
+
json: {
|
|
14274
|
+
message: jobPatches.error.message
|
|
14275
|
+
}
|
|
14276
|
+
};
|
|
14277
|
+
}
|
|
14278
|
+
const found = new Set(jobPatches.patches.map(patch => patch.patchId));
|
|
14279
|
+
const gone = patchIds.filter(id => !found.has(id));
|
|
14280
|
+
if (gone.length > 0) {
|
|
14281
|
+
return {
|
|
14282
|
+
status: 409,
|
|
14283
|
+
json: {
|
|
14284
|
+
message: `This publish's changes are no longer all there (${gone.length} discarded).`
|
|
14285
|
+
}
|
|
14286
|
+
};
|
|
14287
|
+
}
|
|
14288
|
+
const prepared = await serverOps.prepare({
|
|
14289
|
+
...serverOps.analyzePatches(jobPatches.patches, jobPatches.commits, commit),
|
|
14290
|
+
...jobPatches
|
|
14291
|
+
});
|
|
14292
|
+
if (prepared.hasErrors) {
|
|
14293
|
+
return {
|
|
14294
|
+
status: 500,
|
|
14295
|
+
json: {
|
|
14296
|
+
message: "This publish's changes could not be rendered into source: " + JSON.stringify(prepared.sourceFilePatchErrors).slice(0, 500)
|
|
14297
|
+
}
|
|
14298
|
+
};
|
|
14299
|
+
}
|
|
14300
|
+
const split = splitJobPrepare({
|
|
14301
|
+
// Every file the job's changes wrote is what the push writes.
|
|
14302
|
+
chainOnly: {
|
|
14303
|
+
patchedSourceFiles: {}
|
|
14304
|
+
},
|
|
14305
|
+
withJob: prepared,
|
|
14306
|
+
jobPatchIds: patchIds
|
|
14307
|
+
});
|
|
14308
|
+
const sent = await serverOps.prepareJob(jobId, {
|
|
14309
|
+
tab,
|
|
14310
|
+
filesDirectory: ((_options$config$files = options.config.files) === null || _options$config$files === void 0 ? void 0 : _options$config$files.directory) || "/public/val",
|
|
14311
|
+
...split.archive,
|
|
14312
|
+
...(commit !== undefined ? {
|
|
14313
|
+
gitCommit: commit
|
|
14314
|
+
} : {})
|
|
14315
|
+
});
|
|
14316
|
+
return jobPrepareAnswer(sent, {
|
|
14317
|
+
sourceFiles: {},
|
|
14318
|
+
binaryFiles: {},
|
|
14319
|
+
binaryFilesUnread: [],
|
|
14320
|
+
branch: serverOps.projectBranch()
|
|
14321
|
+
});
|
|
14322
|
+
}
|
|
14117
14323
|
const chain = await serverOps.fetchPatches({
|
|
14118
14324
|
patchIds: undefined,
|
|
14119
14325
|
excludePatchOps: false
|
|
@@ -14127,37 +14333,63 @@ const ValServer = (valModules, options, callbacks) => {
|
|
|
14127
14333
|
};
|
|
14128
14334
|
}
|
|
14129
14335
|
/*
|
|
14130
|
-
* The
|
|
14336
|
+
* The job's content, in two steps from this build's embedded source.
|
|
14131
14337
|
*
|
|
14132
|
-
*
|
|
14133
|
-
*
|
|
14134
|
-
*
|
|
14135
|
-
*
|
|
14136
|
-
*
|
|
14338
|
+
* The commits since this build, as though pending, are the branch's
|
|
14339
|
+
* head. The job's pending changes on top of them are its content: whatever was applied since the job started came
|
|
14340
|
+
* out of its own changes (docs/app-mode.md, "Publishing is a queued
|
|
14341
|
+
* job"), and were that ever not so, the seal would restart the job
|
|
14342
|
+
* rather than publish it. Chain order, both times.
|
|
14137
14343
|
*/
|
|
14344
|
+
const job = new Set(patchIds);
|
|
14138
14345
|
const committed = chain.patches.filter(patch => patch.appliedAt !== null).map(patch => ({
|
|
14139
14346
|
...patch,
|
|
14140
14347
|
appliedAt: null
|
|
14141
14348
|
}));
|
|
14142
|
-
const
|
|
14143
|
-
|
|
14349
|
+
const pending = chain.patches.filter(patch => patch.appliedAt === null && job.has(patch.patchId));
|
|
14350
|
+
const known = new Set(chain.patches.map(patch => patch.patchId));
|
|
14351
|
+
const gone = patchIds.filter(id => !known.has(id));
|
|
14352
|
+
if (gone.length > 0) {
|
|
14353
|
+
return {
|
|
14354
|
+
status: 409,
|
|
14355
|
+
json: {
|
|
14356
|
+
message: `This publish's changes are no longer all there (${gone.length} discarded).`
|
|
14357
|
+
}
|
|
14358
|
+
};
|
|
14359
|
+
}
|
|
14360
|
+
const prepareOf = patches => serverOps.prepare({
|
|
14361
|
+
...serverOps.analyzePatches(patches, chain.commits, commit),
|
|
14144
14362
|
...chain,
|
|
14145
|
-
patches
|
|
14363
|
+
patches
|
|
14146
14364
|
});
|
|
14147
|
-
|
|
14365
|
+
const chainOnly = await prepareOf(committed);
|
|
14366
|
+
const withJob = await prepareOf([...committed, ...pending]);
|
|
14367
|
+
if (chainOnly.hasErrors || withJob.hasErrors) {
|
|
14148
14368
|
return {
|
|
14149
14369
|
status: 500,
|
|
14150
14370
|
json: {
|
|
14151
|
-
message: "
|
|
14371
|
+
message: "This publish's changes could not be rendered into source: " + JSON.stringify((withJob.hasErrors ? withJob : chainOnly).sourceFilePatchErrors).slice(0, 500)
|
|
14152
14372
|
}
|
|
14153
14373
|
};
|
|
14154
14374
|
}
|
|
14155
|
-
|
|
14156
|
-
|
|
14157
|
-
|
|
14158
|
-
|
|
14159
|
-
|
|
14160
|
-
|
|
14375
|
+
const split = splitJobPrepare({
|
|
14376
|
+
chainOnly,
|
|
14377
|
+
withJob,
|
|
14378
|
+
jobPatchIds: patchIds
|
|
14379
|
+
});
|
|
14380
|
+
// Read now, while the job's changes still hold their files.
|
|
14381
|
+
const binaries = await readCommittedBinaryFiles(serverOps, split.archive.patchedBinaryFilesDescriptors);
|
|
14382
|
+
const sent = await serverOps.prepareJob(jobId, {
|
|
14383
|
+
tab,
|
|
14384
|
+
filesDirectory: ((_options$config$files2 = options.config.files) === null || _options$config$files2 === void 0 ? void 0 : _options$config$files2.directory) || "/public/val",
|
|
14385
|
+
...split.archive
|
|
14386
|
+
});
|
|
14387
|
+
return jobPrepareAnswer(sent, {
|
|
14388
|
+
sourceFiles: split.buildSourceFiles,
|
|
14389
|
+
binaryFiles: binaries.files,
|
|
14390
|
+
binaryFilesUnread: binaries.unread,
|
|
14391
|
+
branch: serverOps.projectBranch()
|
|
14392
|
+
});
|
|
14161
14393
|
}
|
|
14162
14394
|
},
|
|
14163
14395
|
"/profiles": {
|
|
@@ -14612,8 +14844,8 @@ const ValServer = (valModules, options, callbacks) => {
|
|
|
14612
14844
|
const managedBranch = serverOps.sourceMode() === "managed" ? serverOps.projectBranch() : null;
|
|
14613
14845
|
const committedBinaries = serverOps.sourceMode() === "managed" ? await readCommittedBinaryFiles(serverOps, preparedCommit.patchedBinaryFilesDescriptors) : null;
|
|
14614
14846
|
const commitToGit = () => {
|
|
14615
|
-
var _options$config$
|
|
14616
|
-
return serverOps.commit(preparedCommit, message, auth.id, ((_options$config$
|
|
14847
|
+
var _options$config$files3;
|
|
14848
|
+
return serverOps.commit(preparedCommit, message, auth.id, ((_options$config$files3 = options.config.files) === null || _options$config$files3 === void 0 ? void 0 : _options$config$files3.directory) || "/public/val", undefined,
|
|
14617
14849
|
/*
|
|
14618
14850
|
* Forwarded verbatim, and only the client can decide it: the
|
|
14619
14851
|
* content API closes the group it is named without checking
|
|
@@ -16267,6 +16499,49 @@ function getIsRemoteRequired(schemas) {
|
|
|
16267
16499
|
return false;
|
|
16268
16500
|
}
|
|
16269
16501
|
|
|
16502
|
+
/**
|
|
16503
|
+
* `/publish-job-prepare`'s answer from content's: the job as content has it
|
|
16504
|
+
* now, and what the tab builds from. Content's refusal is passed on -- 409
|
|
16505
|
+
* as 409, anything else as 502, which the tab reads as "content answered,
|
|
16506
|
+
* and has counted the attempt" (see `runStudioJob`).
|
|
16507
|
+
*/
|
|
16508
|
+
function jobPrepareAnswer(sent, build) {
|
|
16509
|
+
if (sent.status !== 200) {
|
|
16510
|
+
let message = `The content service did not prepare the publish (${sent.status}).`;
|
|
16511
|
+
try {
|
|
16512
|
+
const parsed = JSON.parse(sent.body);
|
|
16513
|
+
if (typeof parsed === "object" && parsed !== null && "message" in parsed && typeof parsed.message === "string") {
|
|
16514
|
+
message = parsed.message;
|
|
16515
|
+
}
|
|
16516
|
+
} catch {
|
|
16517
|
+
// not JSON: the status says enough
|
|
16518
|
+
}
|
|
16519
|
+
return {
|
|
16520
|
+
status: sent.status === 409 ? 409 : 502,
|
|
16521
|
+
json: {
|
|
16522
|
+
message
|
|
16523
|
+
}
|
|
16524
|
+
};
|
|
16525
|
+
}
|
|
16526
|
+
try {
|
|
16527
|
+
const prepared = parseJob(JSON.parse(sent.body), "POST /v1/publish-jobs/{id}/prepare");
|
|
16528
|
+
return {
|
|
16529
|
+
status: 200,
|
|
16530
|
+
json: {
|
|
16531
|
+
job: prepared.job,
|
|
16532
|
+
...build
|
|
16533
|
+
}
|
|
16534
|
+
};
|
|
16535
|
+
} catch (e) {
|
|
16536
|
+
return {
|
|
16537
|
+
status: 502,
|
|
16538
|
+
json: {
|
|
16539
|
+
message: e instanceof Error ? e.message : String(e)
|
|
16540
|
+
}
|
|
16541
|
+
};
|
|
16542
|
+
}
|
|
16543
|
+
}
|
|
16544
|
+
|
|
16270
16545
|
async function createValServer(valModules, route, opts, config, callbacks, formatter,
|
|
16271
16546
|
/**
|
|
16272
16547
|
* Called after a save has applied its patches. EXPERIMENTAL — see
|
package/package.json
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"./package.json": "./package.json"
|
|
17
17
|
},
|
|
18
18
|
"types": "dist/valbuild-server.cjs.d.ts",
|
|
19
|
-
"version": "0.
|
|
19
|
+
"version": "0.138.1",
|
|
20
20
|
"devDependencies": {
|
|
21
21
|
"@prettier/sync": "^0.6.1",
|
|
22
22
|
"@types/jest": "^30.0.0",
|
|
@@ -30,9 +30,9 @@
|
|
|
30
30
|
"typescript": "^6.0.3",
|
|
31
31
|
"zod": "^4.4.3",
|
|
32
32
|
"zod-validation-error": "^5.0.0",
|
|
33
|
-
"@valbuild/core": "0.
|
|
34
|
-
"@valbuild/shared": "0.
|
|
35
|
-
"@valbuild/ui": "0.
|
|
33
|
+
"@valbuild/core": "0.138.0",
|
|
34
|
+
"@valbuild/shared": "0.138.0",
|
|
35
|
+
"@valbuild/ui": "0.138.1"
|
|
36
36
|
},
|
|
37
37
|
"engines": {
|
|
38
38
|
"node": "^20.19.0 || >=22"
|