@valbuild/server 0.132.1 → 0.133.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.
- package/CHANGELOG.md +63 -0
- package/dist/declarations/src/ValOps.d.ts +50 -1
- package/dist/declarations/src/ValOpsHttp.d.ts +83 -6
- package/dist/declarations/src/ValRouter.d.ts +23 -10
- package/dist/declarations/src/ValServer.d.ts +11 -2
- package/dist/declarations/src/history/types.d.ts +4 -2
- package/dist/valbuild-server.cjs.dev.js +346 -38
- package/dist/valbuild-server.cjs.prod.js +346 -38
- package/dist/valbuild-server.esm.js +346 -38
- package/package.json +4 -4
|
@@ -4313,7 +4313,25 @@ class ValOps {
|
|
|
4313
4313
|
};
|
|
4314
4314
|
};
|
|
4315
4315
|
const patchedJsonEntries = {};
|
|
4316
|
-
|
|
4316
|
+
/*
|
|
4317
|
+
* Patching the FILE TEXT, for a store that has somewhere to put it.
|
|
4318
|
+
*
|
|
4319
|
+
* Where there is nowhere -- an http project with no repository -- the
|
|
4320
|
+
* patches are still all applied, because they are: `getSources(analysis)`
|
|
4321
|
+
* below applies them to Source, and that is what the commit records and
|
|
4322
|
+
* what every reader of this project sees. What is skipped is rendering
|
|
4323
|
+
* that data back out as `.val.ts` and `*.val.json`, which would be a
|
|
4324
|
+
* mirror with no repository to mirror into -- and which cannot even be
|
|
4325
|
+
* attempted, because producing it starts by READING the current text from
|
|
4326
|
+
* the repository that is not there.
|
|
4327
|
+
*/
|
|
4328
|
+
const allResults = await Promise.all(Object.entries(patchesByModule).map(([path, patches]) => this.mirrorsSourceFiles ? applySourceFilePatches(path, patches) : Promise.resolve({
|
|
4329
|
+
path: path,
|
|
4330
|
+
appliedPatches: patches.map(p => p.patchId),
|
|
4331
|
+
result: null,
|
|
4332
|
+
extraFiles: {},
|
|
4333
|
+
jsonEntries: {}
|
|
4334
|
+
})));
|
|
4317
4335
|
let hasErrors = false;
|
|
4318
4336
|
const sourceFilePatchErrors = {};
|
|
4319
4337
|
const appliedPatches = {};
|
|
@@ -4488,6 +4506,46 @@ class ValOps {
|
|
|
4488
4506
|
});
|
|
4489
4507
|
}
|
|
4490
4508
|
|
|
4509
|
+
/**
|
|
4510
|
+
* Why a publish cannot happen here, or `null` when one can.
|
|
4511
|
+
*
|
|
4512
|
+
* Named rather than thrown, and asked BEFORE the click: the Studio shows the
|
|
4513
|
+
* reason and disables the action, instead of letting someone write a commit
|
|
4514
|
+
* message and then meeting a failure from four layers down.
|
|
4515
|
+
*
|
|
4516
|
+
* `no-base` is the only code so far and means what it says: there is nowhere
|
|
4517
|
+
* for this publish's commit to be based. Note which way round that is --
|
|
4518
|
+
* a project whose content service is the store of record always HAS a base
|
|
4519
|
+
* (the service's own chain, which mints its own shas), so the refusal is not
|
|
4520
|
+
* about missing git. It is about a deployment that cannot do what its
|
|
4521
|
+
* project requires.
|
|
4522
|
+
*/
|
|
4523
|
+
publishRefusal() {
|
|
4524
|
+
return null;
|
|
4525
|
+
}
|
|
4526
|
+
|
|
4527
|
+
/**
|
|
4528
|
+
* Whether a commit here produces `.val.ts` TEXT as well as data.
|
|
4529
|
+
*
|
|
4530
|
+
* True everywhere there is somewhere to put it: a working tree in `fs` mode,
|
|
4531
|
+
* a host holding its own source in memory mode, a git repository in `http`
|
|
4532
|
+
* mode. False for an `http` project whose content service is the store of
|
|
4533
|
+
* record and which has no repository attached -- see `git` on
|
|
4534
|
+
* {@link ValApiOptions}.
|
|
4535
|
+
*
|
|
4536
|
+
* WHAT IS NOT AFFECTED, and it is the part worth being sure of:
|
|
4537
|
+
* `moduleVersions` -- what each changed module IS after the commit, with its
|
|
4538
|
+
* schema -- comes from `getSources(analysis)`, which applies the ops to
|
|
4539
|
+
* Source in the stores. It does not go near the file text. So a commit with
|
|
4540
|
+
* no mirror still records everything history and a later `connect-github`
|
|
4541
|
+
* fold need; what it does not record is a rendering of that data as code.
|
|
4542
|
+
*
|
|
4543
|
+
* WHAT IS: the ops are no longer applied to the file text as well, so a
|
|
4544
|
+
* patch that would not fit the `.val.ts` is not reported here. That check
|
|
4545
|
+
* only ever existed for the text being produced, and there is none.
|
|
4546
|
+
*/
|
|
4547
|
+
mirrorsSourceFiles = true;
|
|
4548
|
+
|
|
4491
4549
|
/**
|
|
4492
4550
|
* Take the `.val.ts` text a commit produced as the new committed source.
|
|
4493
4551
|
*
|
|
@@ -4652,6 +4710,16 @@ function formatPatchSourceError(error) {
|
|
|
4652
4710
|
return "Unknown patch source error: " + JSON.stringify(_exhaustiveCheck);
|
|
4653
4711
|
}
|
|
4654
4712
|
}
|
|
4713
|
+
|
|
4714
|
+
/**
|
|
4715
|
+
* Why a publish is refused, in a form a person can be shown.
|
|
4716
|
+
*
|
|
4717
|
+
* `code` is for the Studio to branch on and `message` is what it says. Both,
|
|
4718
|
+
* rather than a code and a lookup table on the client: the server knows what
|
|
4719
|
+
* is actually missing -- which branch, which commit -- and a client-side table
|
|
4720
|
+
* could only ever say the generic version.
|
|
4721
|
+
*/
|
|
4722
|
+
|
|
4655
4723
|
/**
|
|
4656
4724
|
* The patches a json entry render should apply, out of the whole chain.
|
|
4657
4725
|
*
|
|
@@ -7491,8 +7559,14 @@ const GetApplicablePatches = z.object({
|
|
|
7491
7559
|
})),
|
|
7492
7560
|
commits: z.array(z.object({
|
|
7493
7561
|
commitSha: z.string(),
|
|
7494
|
-
|
|
7495
|
-
|
|
7562
|
+
/*
|
|
7563
|
+
* Nullable since the content service mints its own commit shas: a
|
|
7564
|
+
* root commit has no parent, and a publisher that did not say where
|
|
7565
|
+
* it was has no client sha. Parsed rather than trusted, so a service
|
|
7566
|
+
* that sends null gets null here instead of failing the whole poll.
|
|
7567
|
+
*/
|
|
7568
|
+
clientCommitSha: z.string().nullable(),
|
|
7569
|
+
parentCommitSha: z.string().nullable(),
|
|
7496
7570
|
commitMessage: z.string().nullable(),
|
|
7497
7571
|
branch: z.string(),
|
|
7498
7572
|
creator: z.string(),
|
|
@@ -7508,7 +7582,21 @@ const GetApplicablePatches = z.object({
|
|
|
7508
7582
|
// `ValDeployment`: this is the only source for a deployment that Val
|
|
7509
7583
|
// did not publish, and zod would otherwise strip it here.
|
|
7510
7584
|
commitMessage: z.string().nullable().optional()
|
|
7511
|
-
})).optional()
|
|
7585
|
+
})).optional(),
|
|
7586
|
+
/**
|
|
7587
|
+
* What the project EXPECTS of whoever publishes it.
|
|
7588
|
+
*
|
|
7589
|
+
* Optional because a content service that predates it sends nothing, and
|
|
7590
|
+
* absent means "not reported" -- never "managed". The difference decides
|
|
7591
|
+
* whether a publish is refused, so guessing either way would be worse than
|
|
7592
|
+
* not checking: guessing `managed` would let a deployment publish a
|
|
7593
|
+
* connected project it cannot mirror, and guessing `connected` would refuse
|
|
7594
|
+
* every publish against an older service.
|
|
7595
|
+
*/
|
|
7596
|
+
project: z.object({
|
|
7597
|
+
sourceMode: z.union([z.literal("managed"), z.literal("connected")]),
|
|
7598
|
+
branch: z.string()
|
|
7599
|
+
}).optional()
|
|
7512
7600
|
});
|
|
7513
7601
|
const FilesResponse = z.object({
|
|
7514
7602
|
files: z.array(z.union([z.object({
|
|
@@ -7581,8 +7669,10 @@ const CommitResponse = z.object({
|
|
|
7581
7669
|
// commit changed nothing", which is indistinguishable from a real answer.
|
|
7582
7670
|
const HistoricalCommitResponse = z.object({
|
|
7583
7671
|
commitSha: z.string(),
|
|
7584
|
-
|
|
7585
|
-
|
|
7672
|
+
/** `null` for a root commit; see the note on the applicable-patches schema. */
|
|
7673
|
+
parentCommitSha: z.string().nullable(),
|
|
7674
|
+
/** `null` when the publisher did not say where it was. */
|
|
7675
|
+
clientCommitSha: z.string().nullable(),
|
|
7586
7676
|
branch: z.string(),
|
|
7587
7677
|
createdBranch: z.string().nullable(),
|
|
7588
7678
|
creator: z.string().nullable(),
|
|
@@ -7600,8 +7690,8 @@ const CommitPatchesResponse = z.object({
|
|
|
7600
7690
|
commitSha: z.string(),
|
|
7601
7691
|
commit: z.object({
|
|
7602
7692
|
commitSha: z.string(),
|
|
7603
|
-
parentCommitSha: z.string(),
|
|
7604
|
-
clientCommitSha: z.string(),
|
|
7693
|
+
parentCommitSha: z.string().nullable(),
|
|
7694
|
+
clientCommitSha: z.string().nullable(),
|
|
7605
7695
|
branch: z.string(),
|
|
7606
7696
|
createdBranch: z.string().nullable(),
|
|
7607
7697
|
creator: z.string().nullable(),
|
|
@@ -7701,9 +7791,45 @@ class ValOpsHttp extends ValOps {
|
|
|
7701
7791
|
patchesAreLocal = false;
|
|
7702
7792
|
/** See {@link ValOps.requiresAuth}. */
|
|
7703
7793
|
requiresAuth = true;
|
|
7704
|
-
|
|
7705
|
-
|
|
7706
|
-
|
|
7794
|
+
/**
|
|
7795
|
+
* A commit mirrors into `.val.ts` only when there is a repository.
|
|
7796
|
+
*
|
|
7797
|
+
* See {@link ValOps.mirrorsSourceFiles}. Set in the constructor rather than
|
|
7798
|
+
* as an initialiser because it depends on `git`, and a class field
|
|
7799
|
+
* initialiser runs before the constructor body has assigned it.
|
|
7800
|
+
*/
|
|
7801
|
+
mirrorsSourceFiles;
|
|
7802
|
+
/**
|
|
7803
|
+
* What the content service last said this project expects of its publisher.
|
|
7804
|
+
*
|
|
7805
|
+
* `null` until something has asked it, which in practice is the first poll.
|
|
7806
|
+
* It is remembered rather than asked for on demand because the answer is
|
|
7807
|
+
* only wanted on the publish path, and that path already fetches the
|
|
7808
|
+
* patches it is publishing -- so a dedicated request would be a second round
|
|
7809
|
+
* trip for two fields that just arrived.
|
|
7810
|
+
*
|
|
7811
|
+
* It can be one poll out of date, and that is the right amount: the thing it
|
|
7812
|
+
* changes is whether this deployment can mirror commits into a repository,
|
|
7813
|
+
* which changes when a project CONNECTS one -- and a project that has just
|
|
7814
|
+
* connected is one whose builds are about to be replaced anyway.
|
|
7815
|
+
*/
|
|
7816
|
+
projectExpectation = null;
|
|
7817
|
+
constructor(contentUrl, project,
|
|
7818
|
+
/**
|
|
7819
|
+
* The repository this project's commits are mirrored into, or `null`.
|
|
7820
|
+
*
|
|
7821
|
+
* `null` is a project whose content service is the store of record: it
|
|
7822
|
+
* mints its own commit shas, and it knows this project's branch from the
|
|
7823
|
+
* project itself. Every request below that would have carried a branch and
|
|
7824
|
+
* a commit omits them instead, and the service answers from the project's
|
|
7825
|
+
* own chain -- which is where those answers always came from.
|
|
7826
|
+
*
|
|
7827
|
+
* It is NOT a degraded mode. The one thing that genuinely needs a
|
|
7828
|
+
* repository is producing the `.val.ts` text a commit mirrors, and a
|
|
7829
|
+
* project with no repository has nothing to mirror into. See `git` on
|
|
7830
|
+
* {@link ValApiOptions}.
|
|
7831
|
+
*/
|
|
7832
|
+
git,
|
|
7707
7833
|
/**
|
|
7708
7834
|
* An api key (how the app itself authenticates) or a personal access token
|
|
7709
7835
|
* (how the CLI authenticates after `val login`). Same two shapes as
|
|
@@ -7713,14 +7839,49 @@ class ValOpsHttp extends ValOps {
|
|
|
7713
7839
|
super(valModules, options);
|
|
7714
7840
|
this.contentUrl = contentUrl;
|
|
7715
7841
|
this.project = project;
|
|
7716
|
-
this.
|
|
7717
|
-
this.branch = branch;
|
|
7842
|
+
this.git = git;
|
|
7718
7843
|
this.authHeaders = "pat" in auth ? {
|
|
7719
7844
|
"x-val-pat": auth.pat
|
|
7720
7845
|
} : {
|
|
7721
7846
|
Authorization: `Bearer ${auth.apiKey}`
|
|
7722
7847
|
};
|
|
7723
7848
|
this.root = (options === null || options === void 0 ? void 0 : options.root) ?? "";
|
|
7849
|
+
this.mirrorsSourceFiles = git !== null;
|
|
7850
|
+
}
|
|
7851
|
+
/**
|
|
7852
|
+
* A deployment that cannot mirror a project which expects to be mirrored.
|
|
7853
|
+
*
|
|
7854
|
+
* This is the one shape of "no base" that exists, and it is not the one it
|
|
7855
|
+
* sounds like. A project with no repository is fine: the content service is
|
|
7856
|
+
* the store of record for it and mints its own commit shas, so there is
|
|
7857
|
+
* always somewhere for the commit to go. What is refused is the mismatch --
|
|
7858
|
+
* a project whose commits are mirrored into a repository, being published by
|
|
7859
|
+
* a build that was made before it had one and so has no commit to produce
|
|
7860
|
+
* that mirror against.
|
|
7861
|
+
*
|
|
7862
|
+
* It is a REAL state rather than a defensive check: it is exactly what a
|
|
7863
|
+
* deployment looks like between a project connecting a repository and its
|
|
7864
|
+
* next build going out. Left unchecked, such a publish writes the data and
|
|
7865
|
+
* silently fails to mirror it, and the repository quietly falls behind the
|
|
7866
|
+
* content nobody is told about.
|
|
7867
|
+
*
|
|
7868
|
+
* `null` when the service did not say (see `project` on the response
|
|
7869
|
+
* schema): an older content service is not evidence of anything, and
|
|
7870
|
+
* refusing every publish against one would be a worse failure than not
|
|
7871
|
+
* checking.
|
|
7872
|
+
*/
|
|
7873
|
+
publishRefusal() {
|
|
7874
|
+
var _this$projectExpectat;
|
|
7875
|
+
if (this.git !== null) {
|
|
7876
|
+
return null;
|
|
7877
|
+
}
|
|
7878
|
+
if (((_this$projectExpectat = this.projectExpectation) === null || _this$projectExpectat === void 0 ? void 0 : _this$projectExpectat.sourceMode) !== "connected") {
|
|
7879
|
+
return null;
|
|
7880
|
+
}
|
|
7881
|
+
return {
|
|
7882
|
+
code: "no-base",
|
|
7883
|
+
message: `This project mirrors its content into a git repository (branch ` + `'${this.projectExpectation.branch}'), but this deployment was not ` + "built from one, so it does not know which commit to write that " + "mirror against. Publishing would save the content and silently " + "leave the repository behind. Deploy this project again from its " + "repository, and publishing will work from that build on."
|
|
7884
|
+
};
|
|
7724
7885
|
}
|
|
7725
7886
|
async onInit() {
|
|
7726
7887
|
// TODO: unused for now. Implement or remove
|
|
@@ -7911,16 +8072,31 @@ class ValOpsHttp extends ValOps {
|
|
|
7911
8072
|
* decided against.
|
|
7912
8073
|
*/
|
|
7913
8074
|
headCommitSha: newestCommitSha(allPatchData.commits) ?? undefined,
|
|
7914
|
-
|
|
8075
|
+
/*
|
|
8076
|
+
* Spread: a project with no repository has no such commit, and saying
|
|
8077
|
+
* so by leaving the key out is different from sending an empty string
|
|
8078
|
+
* the Studio would try to show.
|
|
8079
|
+
*/
|
|
8080
|
+
...(this.git ? {
|
|
8081
|
+
commitSha: this.git.commit
|
|
8082
|
+
} : {})
|
|
7915
8083
|
};
|
|
7916
8084
|
}
|
|
7917
8085
|
async getWebSocketNonce(profileId) {
|
|
7918
8086
|
return fetch(`${this.contentUrl}/v1/${this.project}/websocket/nonces`, {
|
|
7919
8087
|
method: "POST",
|
|
7920
8088
|
body: JSON.stringify({
|
|
7921
|
-
branch: this.branch,
|
|
7922
8089
|
profileId,
|
|
7923
|
-
|
|
8090
|
+
/*
|
|
8091
|
+
* Omitted when there is no repository, like every other request here.
|
|
8092
|
+
* The content service knows this project's branch -- it is a column on
|
|
8093
|
+
* the project -- and a nonce is scoped to the project and the person,
|
|
8094
|
+
* not to a position in a chain.
|
|
8095
|
+
*/
|
|
8096
|
+
...(this.git ? {
|
|
8097
|
+
branch: this.git.branch,
|
|
8098
|
+
commitSha: this.git.commit
|
|
8099
|
+
} : {})
|
|
7924
8100
|
}),
|
|
7925
8101
|
headers: {
|
|
7926
8102
|
...this.authHeaders,
|
|
@@ -8068,8 +8244,20 @@ class ValOpsHttp extends ValOps {
|
|
|
8068
8244
|
}
|
|
8069
8245
|
async fetchPatchesInternal(filters) {
|
|
8070
8246
|
const params = [];
|
|
8071
|
-
|
|
8072
|
-
|
|
8247
|
+
/*
|
|
8248
|
+
* A position in the chain, WHEN THIS BUILD HAS ONE.
|
|
8249
|
+
*
|
|
8250
|
+
* `commit` tells the content service where this deployment sits, so it can
|
|
8251
|
+
* answer with the commits at or after that point. A build with no
|
|
8252
|
+
* repository has no such commit baked into it, and asking the service to
|
|
8253
|
+
* resolve the position itself is strictly better than inventing one: it
|
|
8254
|
+
* holds the chain, and for a project it is the only publisher of, the
|
|
8255
|
+
* position IS the head.
|
|
8256
|
+
*/
|
|
8257
|
+
if (this.git) {
|
|
8258
|
+
params.push(["branch", this.git.branch]);
|
|
8259
|
+
params.push(["commit", this.git.commit]);
|
|
8260
|
+
}
|
|
8073
8261
|
if (filters.patchIds) {
|
|
8074
8262
|
for (const patchId of filters.patchIds) {
|
|
8075
8263
|
params.push(["patch_id", patchId]);
|
|
@@ -8090,6 +8278,17 @@ class ValOpsHttp extends ValOps {
|
|
|
8090
8278
|
if (parsed.success) {
|
|
8091
8279
|
const errors = [];
|
|
8092
8280
|
const data = parsed.data;
|
|
8281
|
+
/*
|
|
8282
|
+
* What the project expects of its publisher, remembered.
|
|
8283
|
+
*
|
|
8284
|
+
* Recorded here rather than returned because every caller of this
|
|
8285
|
+
* already has what it needs and only the publish path asks the
|
|
8286
|
+
* question -- and that path calls this first. See
|
|
8287
|
+
* {@link publishRefusal}.
|
|
8288
|
+
*/
|
|
8289
|
+
if (data.project) {
|
|
8290
|
+
this.projectExpectation = data.project;
|
|
8291
|
+
}
|
|
8093
8292
|
for (const patchesRes of data.patches) {
|
|
8094
8293
|
var _patchesRes$applied;
|
|
8095
8294
|
patches.push({
|
|
@@ -8310,7 +8509,7 @@ class ValOpsHttp extends ValOps {
|
|
|
8310
8509
|
* `saveSourceFilePatch`): groups are per branch, so a request without one
|
|
8311
8510
|
* is not merely under-specified, it is rejected.
|
|
8312
8511
|
*/
|
|
8313
|
-
const params = new URLSearchParams([["branch", this.branch]]);
|
|
8512
|
+
const params = new URLSearchParams(this.git ? [["branch", this.git.branch]] : []);
|
|
8314
8513
|
const res = await fetch(`${this.contentUrl}/v1/${this.project}/patch-groups?${params}`, {
|
|
8315
8514
|
headers: this.authHeaders
|
|
8316
8515
|
});
|
|
@@ -8462,8 +8661,10 @@ class ValOpsHttp extends ValOps {
|
|
|
8462
8661
|
patchId,
|
|
8463
8662
|
parentPatchId: parentRef.type === "patch" ? parentRef.patchId : null,
|
|
8464
8663
|
baseSha,
|
|
8465
|
-
|
|
8466
|
-
|
|
8664
|
+
...(this.git ? {
|
|
8665
|
+
commit: this.git.commit,
|
|
8666
|
+
branch: this.git.branch
|
|
8667
|
+
} : {}),
|
|
8467
8668
|
coreVersion: Internal.VERSION.core,
|
|
8468
8669
|
/*
|
|
8469
8670
|
* Group membership in the SAME request as the patch.
|
|
@@ -8629,11 +8830,28 @@ class ValOpsHttp extends ValOps {
|
|
|
8629
8830
|
});
|
|
8630
8831
|
}
|
|
8631
8832
|
async getSourceFile(path) {
|
|
8833
|
+
/*
|
|
8834
|
+
* There is no file to read without a repository to read it from.
|
|
8835
|
+
*
|
|
8836
|
+
* Reachable only through the CLI's debug snapshot now: the publish path
|
|
8837
|
+
* does not call this for such a project at all -- see
|
|
8838
|
+
* {@link ValOps.mirrorsSourceFiles} -- because there is nothing for it to
|
|
8839
|
+
* produce. It is an error rather than an empty string because an empty
|
|
8840
|
+
* `.val.ts` would be patched successfully and committed as a module that
|
|
8841
|
+
* had lost all its content.
|
|
8842
|
+
*/
|
|
8843
|
+
if (!this.git) {
|
|
8844
|
+
return {
|
|
8845
|
+
error: {
|
|
8846
|
+
message: `Cannot read the source of ${path}: this project has no ` + "repository, so there is no `.val.ts` to read. Its content lives " + "in Val's content service, which is the store of record for it."
|
|
8847
|
+
}
|
|
8848
|
+
};
|
|
8849
|
+
}
|
|
8632
8850
|
const filesRes = await this.getHttpFiles([{
|
|
8633
8851
|
filePath: path,
|
|
8634
8852
|
location: "repo",
|
|
8635
8853
|
root: this.root,
|
|
8636
|
-
commitSha: this.
|
|
8854
|
+
commitSha: this.git.commit
|
|
8637
8855
|
}]);
|
|
8638
8856
|
if (filesRes.error) {
|
|
8639
8857
|
return filesRes;
|
|
@@ -8653,11 +8871,22 @@ class ValOpsHttp extends ValOps {
|
|
|
8653
8871
|
async getBinaryFile(filePath) {
|
|
8654
8872
|
// We could also just get this from public/ on the running server. Current approach feels more clean, but will be slower / puts more server load... We might want to change this
|
|
8655
8873
|
const requestFiles = [];
|
|
8874
|
+
if (!this.git) {
|
|
8875
|
+
/*
|
|
8876
|
+
* A published binary lives in the repository, and there is none.
|
|
8877
|
+
*
|
|
8878
|
+
* `null` is this method's existing "not found", which is what a caller
|
|
8879
|
+
* already handles: the Studio falls back to the patch's own copy, which
|
|
8880
|
+
* is where a managed project's files stay. See the note on local files
|
|
8881
|
+
* in the content service's commit handler.
|
|
8882
|
+
*/
|
|
8883
|
+
return null;
|
|
8884
|
+
}
|
|
8656
8885
|
requestFiles.push({
|
|
8657
8886
|
filePath: filePath,
|
|
8658
8887
|
location: "repo",
|
|
8659
8888
|
root: this.root,
|
|
8660
|
-
commitSha: this.
|
|
8889
|
+
commitSha: this.git.commit
|
|
8661
8890
|
});
|
|
8662
8891
|
const filesRes = await this.getHttpFiles(requestFiles);
|
|
8663
8892
|
if (filesRes.error) {
|
|
@@ -8836,7 +9065,6 @@ class ValOpsHttp extends ValOps {
|
|
|
8836
9065
|
patchGroupId) {
|
|
8837
9066
|
try {
|
|
8838
9067
|
var _res$headers$get3;
|
|
8839
|
-
const existingBranch = this.branch;
|
|
8840
9068
|
const res = await fetch(`${this.contentUrl}/v1/${this.project}/commit`, {
|
|
8841
9069
|
method: "POST",
|
|
8842
9070
|
headers: {
|
|
@@ -8880,13 +9108,29 @@ class ValOpsHttp extends ValOps {
|
|
|
8880
9108
|
* moved on.
|
|
8881
9109
|
*/
|
|
8882
9110
|
modules: prepared.moduleVersions,
|
|
8883
|
-
|
|
9111
|
+
/*
|
|
9112
|
+
* WHERE THIS BUILD THOUGHT IT WAS -- and only for a project with a
|
|
9113
|
+
* repository, which is the only thing that still reads it.
|
|
9114
|
+
*
|
|
9115
|
+
* The content service mints its own commit shas, so this is no
|
|
9116
|
+
* longer the parent, and it never was the concurrency check: that is
|
|
9117
|
+
* `expectedHeadCommitSha` against `newestCommitSha(patches.commits)`,
|
|
9118
|
+
* which `/save` runs before it gets here. What is left is the git
|
|
9119
|
+
* fast-forward check, which needs to know the commit this build read
|
|
9120
|
+
* the branch at to tell whether the branch has moved under it.
|
|
9121
|
+
*
|
|
9122
|
+
* The branch goes with it. The project's branch is a column on the
|
|
9123
|
+
* project, so a build asserting one could only ever disagree with
|
|
9124
|
+
* the project it is publishing to.
|
|
9125
|
+
*/
|
|
9126
|
+
...(this.git ? {
|
|
9127
|
+
commit: this.git.commit
|
|
9128
|
+
} : {}),
|
|
8884
9129
|
root: this.root,
|
|
8885
9130
|
filesDirectory,
|
|
8886
9131
|
baseSha: await this.getBaseSha(),
|
|
8887
9132
|
committer,
|
|
8888
9133
|
message,
|
|
8889
|
-
existingBranch,
|
|
8890
9134
|
newBranch,
|
|
8891
9135
|
...(patchGroupId !== undefined ? {
|
|
8892
9136
|
patchGroupId
|
|
@@ -10635,7 +10879,7 @@ async function initHandlerOptions(route, opts, config) {
|
|
|
10635
10879
|
valContentUrl
|
|
10636
10880
|
});
|
|
10637
10881
|
if (isProxyMode) {
|
|
10638
|
-
var _opts$versions, _opts$versions2;
|
|
10882
|
+
var _opts$git, _opts$git2, _opts$versions, _opts$versions2;
|
|
10639
10883
|
/*
|
|
10640
10884
|
* Why this app is in http mode, in the message that says what is missing.
|
|
10641
10885
|
*
|
|
@@ -10648,13 +10892,30 @@ async function initHandlerOptions(route, opts, config) {
|
|
|
10648
10892
|
if (!maybeApiKey || !maybeValSecret) {
|
|
10649
10893
|
throw new Error("VAL_API_KEY and VAL_SECRET env vars must both be set in proxy mode" + because);
|
|
10650
10894
|
}
|
|
10651
|
-
|
|
10652
|
-
|
|
10653
|
-
|
|
10654
|
-
|
|
10655
|
-
|
|
10656
|
-
|
|
10657
|
-
|
|
10895
|
+
/*
|
|
10896
|
+
* A COMMIT IS NOT WHAT PUTS AN APP IN HTTP MODE. Credentials are.
|
|
10897
|
+
*
|
|
10898
|
+
* Both of these used to be required here, and the requirement was a
|
|
10899
|
+
* repository disguised as a configuration check: a deployment with no
|
|
10900
|
+
* commit to name -- one whose content service owns its content, which is
|
|
10901
|
+
* now the normal case -- threw at boot, or, worse, never reached this
|
|
10902
|
+
* branch at all and fell through to `fs` mode, looking for a working tree
|
|
10903
|
+
* that was not there.
|
|
10904
|
+
*
|
|
10905
|
+
* Absent is a project with no repository to mirror commits into. The
|
|
10906
|
+
* content service mints its own commit shas and is the store of record for
|
|
10907
|
+
* content, so there is nothing missing: see `git` on {@link ValApiOptions}
|
|
10908
|
+
* for what a commit is still FOR where there is one.
|
|
10909
|
+
*
|
|
10910
|
+
* Taken together or not at all. A commit without a branch names a point
|
|
10911
|
+
* with no line of work to publish to, and a branch without a commit names
|
|
10912
|
+
* a line with no position in it; either alone would be a half-configured
|
|
10913
|
+
* repository that fails later, at a publish, rather than here.
|
|
10914
|
+
*/
|
|
10915
|
+
const maybeGitCommit = ((_opts$git = opts.git) === null || _opts$git === void 0 ? void 0 : _opts$git.commit) || process.env.VAL_GIT_COMMIT;
|
|
10916
|
+
const maybeGitBranch = ((_opts$git2 = opts.git) === null || _opts$git2 === void 0 ? void 0 : _opts$git2.branch) || process.env.VAL_GIT_BRANCH;
|
|
10917
|
+
if (!!maybeGitCommit !== !!maybeGitBranch) {
|
|
10918
|
+
throw new Error(`Val is configured with a git ${maybeGitCommit ? "commit" : "branch"} ` + `but no ${maybeGitCommit ? "branch" : "commit"}. Set both (the ` + "`git` option, or VAL_GIT_COMMIT and VAL_GIT_BRANCH) for a project " + "whose content is mirrored into a repository, or neither for one " + "whose content service is the store of record." + because);
|
|
10658
10919
|
}
|
|
10659
10920
|
if (!maybeValProject) {
|
|
10660
10921
|
throw new Error("Proxy mode does not work unless the 'project' option in val.config is defined or the VAL_PROJECT env var is set." + because);
|
|
@@ -10672,8 +10933,19 @@ async function initHandlerOptions(route, opts, config) {
|
|
|
10672
10933
|
route,
|
|
10673
10934
|
apiKey: maybeApiKey,
|
|
10674
10935
|
valSecret: maybeValSecret,
|
|
10675
|
-
|
|
10676
|
-
|
|
10936
|
+
/*
|
|
10937
|
+
* Spread, so a project with no repository has no `git` key at all rather
|
|
10938
|
+
* than one holding undefined. `ValOpsHttp` asks `git === null` to decide
|
|
10939
|
+
* whether to send a branch and a commit with every request, and a key
|
|
10940
|
+
* that is present-but-undefined is one more thing for that check to get
|
|
10941
|
+
* wrong.
|
|
10942
|
+
*/
|
|
10943
|
+
...(maybeGitCommit && maybeGitBranch ? {
|
|
10944
|
+
git: {
|
|
10945
|
+
commit: maybeGitCommit,
|
|
10946
|
+
branch: maybeGitBranch
|
|
10947
|
+
}
|
|
10948
|
+
} : {}),
|
|
10677
10949
|
root: opts.root,
|
|
10678
10950
|
project: maybeValProject,
|
|
10679
10951
|
valEnableRedirectUrl,
|
|
@@ -10736,7 +11008,7 @@ function createValOps(valModules, options) {
|
|
|
10736
11008
|
});
|
|
10737
11009
|
}
|
|
10738
11010
|
if (options.mode === "http") {
|
|
10739
|
-
return new ValOpsHttp(options.valContentUrl, options.project, options.
|
|
11011
|
+
return new ValOpsHttp(options.valContentUrl, options.project, options.git ?? null, {
|
|
10740
11012
|
apiKey: options.apiKey
|
|
10741
11013
|
}, valModules, {
|
|
10742
11014
|
formatter: options.formatter,
|
|
@@ -10942,6 +11214,7 @@ async function resolveRemoteFileAuth(options) {
|
|
|
10942
11214
|
/** What a publish reports back, in either shape. */
|
|
10943
11215
|
|
|
10944
11216
|
const ValServer = (valModules, options, callbacks) => {
|
|
11217
|
+
var _options$git;
|
|
10945
11218
|
const AIContentBlock = z.union([z.object({
|
|
10946
11219
|
type: z.literal("text"),
|
|
10947
11220
|
text: z.string()
|
|
@@ -10973,7 +11246,7 @@ const ValServer = (valModules, options, callbacks) => {
|
|
|
10973
11246
|
url.searchParams.set("state", token);
|
|
10974
11247
|
return url.toString();
|
|
10975
11248
|
};
|
|
10976
|
-
const commit = options.mode === "http" ? options.commit : undefined;
|
|
11249
|
+
const commit = options.mode === "http" ? (_options$git = options.git) === null || _options$git === void 0 ? void 0 : _options$git.commit : undefined;
|
|
10977
11250
|
const getAppErrorUrl = error => {
|
|
10978
11251
|
if (!options.project) {
|
|
10979
11252
|
throw new Error("Project is not set");
|
|
@@ -11570,12 +11843,24 @@ const ValServer = (valModules, options, callbacks) => {
|
|
|
11570
11843
|
* start against a server that was working.
|
|
11571
11844
|
*/
|
|
11572
11845
|
const mode = serverOps.patchesAreLocal ? "fs" : "http";
|
|
11846
|
+
/*
|
|
11847
|
+
* Why publishing is unavailable, so the Studio can say so.
|
|
11848
|
+
*
|
|
11849
|
+
* Spread rather than set to null, so a server that can publish sends
|
|
11850
|
+
* no such key at all: the Studio's "can I publish" is then the
|
|
11851
|
+
* presence of the field, and there is no null to mistake for a reason
|
|
11852
|
+
* it failed to compute.
|
|
11853
|
+
*/
|
|
11854
|
+
const publishRefusal = serverOps.publishRefusal();
|
|
11573
11855
|
return {
|
|
11574
11856
|
status: 200,
|
|
11575
11857
|
json: {
|
|
11576
11858
|
...currentStat,
|
|
11577
11859
|
profileId: profileId ?? null,
|
|
11578
11860
|
mode,
|
|
11861
|
+
...(publishRefusal ? {
|
|
11862
|
+
publishRefusal
|
|
11863
|
+
} : {}),
|
|
11579
11864
|
config: options.config
|
|
11580
11865
|
}
|
|
11581
11866
|
};
|
|
@@ -12764,6 +13049,29 @@ const ValServer = (valModules, options, callbacks) => {
|
|
|
12764
13049
|
patchIds,
|
|
12765
13050
|
excludePatchOps: false
|
|
12766
13051
|
});
|
|
13052
|
+
/*
|
|
13053
|
+
* Can this deployment publish this project AT ALL?
|
|
13054
|
+
*
|
|
13055
|
+
* After the fetch above, deliberately: that call is what tells the
|
|
13056
|
+
* data layer what the project expects, and asking before it would be
|
|
13057
|
+
* asking a question nothing has answered yet. It is still before
|
|
13058
|
+
* anything is written, which is the part that matters.
|
|
13059
|
+
*
|
|
13060
|
+
* The Studio already knows -- `/stat` carries the same refusal, so the
|
|
13061
|
+
* action is disabled with the reason shown. This is the second half of
|
|
13062
|
+
* that: a stat can be stale by a poll, and nothing may reach `prepare`
|
|
13063
|
+
* on a project it cannot mirror.
|
|
13064
|
+
*/
|
|
13065
|
+
const refusal = serverOps.publishRefusal();
|
|
13066
|
+
if (refusal) {
|
|
13067
|
+
return {
|
|
13068
|
+
status: 409,
|
|
13069
|
+
json: {
|
|
13070
|
+
message: refusal.message,
|
|
13071
|
+
publishRefusal: refusal
|
|
13072
|
+
}
|
|
13073
|
+
};
|
|
13074
|
+
}
|
|
12767
13075
|
/*
|
|
12768
13076
|
* Exactly the patches this request consumes, and the ONLY ones it may
|
|
12769
13077
|
* delete afterwards.
|
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.133.0",
|
|
20
20
|
"devDependencies": {
|
|
21
21
|
"@prettier/sync": "^0.6.1",
|
|
22
22
|
"@types/jest": "^30.0.0"
|
|
@@ -29,9 +29,9 @@
|
|
|
29
29
|
"typescript": "^6.0.3",
|
|
30
30
|
"zod": "^4.4.3",
|
|
31
31
|
"zod-validation-error": "^5.0.0",
|
|
32
|
-
"@valbuild/
|
|
33
|
-
"@valbuild/
|
|
34
|
-
"@valbuild/
|
|
32
|
+
"@valbuild/core": "0.130.0",
|
|
33
|
+
"@valbuild/shared": "0.133.0",
|
|
34
|
+
"@valbuild/ui": "0.133.0"
|
|
35
35
|
},
|
|
36
36
|
"engines": {
|
|
37
37
|
"node": "^20.19.0 || >=22"
|