@valbuild/server 0.135.0 → 0.136.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.
- package/CHANGELOG.md +108 -0
- package/dist/declarations/src/ValOps.d.ts +41 -0
- package/dist/declarations/src/ValOpsHttp.d.ts +76 -0
- package/dist/declarations/src/index.d.ts +1 -1
- package/dist/declarations/src/loadValModules.d.ts +23 -0
- package/dist/valbuild-server.cjs.dev.js +362 -7
- package/dist/valbuild-server.cjs.prod.js +362 -7
- package/dist/valbuild-server.esm.js +362 -8
- package/package.json +4 -4
|
@@ -1007,18 +1007,29 @@ function createValModuleFileInspector(projectRoot, host = ts__default["default"]
|
|
|
1007
1007
|
}
|
|
1008
1008
|
|
|
1009
1009
|
/**
|
|
1010
|
-
*
|
|
1010
|
+
* The statement that exports a RUNTIME value as `default`, without evaluating
|
|
1011
|
+
* it — or `undefined` when the file has no such export.
|
|
1012
|
+
*
|
|
1013
|
+
* This is the one rule that separates a Val module from a `*.val.ts` that
|
|
1014
|
+
* merely wears the naming convention: a shared schema or helper is exported by
|
|
1015
|
+
* name, a module is exported by default. Every caller that needs to tell those
|
|
1016
|
+
* apart goes through this — `val validate` to decide whether an unregistered
|
|
1017
|
+
* file is worth reporting, and the language server to decide the same thing and
|
|
1018
|
+
* to put its diagnostic on the export rather than on line 1.
|
|
1011
1019
|
*
|
|
1012
1020
|
* Two things deliberately do not count, because neither exists once the file is
|
|
1013
1021
|
* transpiled — and treating either as a default export would send a pure helper
|
|
1014
1022
|
* off to be evaluated and reported:
|
|
1015
1023
|
*
|
|
1016
1024
|
* - `export * from "./x"`, since a star re-export never carries the default;
|
|
1017
|
-
* - a type-only export, in
|
|
1018
|
-
* (`export type { T as default }
|
|
1025
|
+
* - a type-only export, in any of its three spellings
|
|
1026
|
+
* (`export type { T as default }`, `export { type T as default }` and
|
|
1027
|
+
* `export default interface T {}` — the last one parses as a declaration
|
|
1028
|
+
* carrying a `default` modifier, exactly like `export default class`, and is
|
|
1029
|
+
* the one that looks like a runtime export and is not).
|
|
1019
1030
|
*/
|
|
1020
|
-
function
|
|
1021
|
-
return sourceFile.statements.
|
|
1031
|
+
function findDefaultExport(sourceFile) {
|
|
1032
|
+
return sourceFile.statements.find(statement => {
|
|
1022
1033
|
// `export default <expr>` — but not `export = x`, which shares this node.
|
|
1023
1034
|
if (ts__default["default"].isExportAssignment(statement)) {
|
|
1024
1035
|
return !statement.isExportEquals;
|
|
@@ -1029,10 +1040,17 @@ function hasDefaultExport(sourceFile) {
|
|
|
1029
1040
|
}
|
|
1030
1041
|
// `export default function f() {}` / `export default class C {}`, which are
|
|
1031
1042
|
// declarations carrying a `default` modifier rather than export assignments.
|
|
1032
|
-
|
|
1043
|
+
// `export default interface T {}` is spelled the same way and is a type, so
|
|
1044
|
+
// it is excluded here rather than by the `isTypeOnly` checks above.
|
|
1045
|
+
return !ts__default["default"].isInterfaceDeclaration(statement) && ts__default["default"].canHaveModifiers(statement) && (ts__default["default"].getModifiers(statement) ?? []).some(modifier => modifier.kind === ts__default["default"].SyntaxKind.DefaultKeyword);
|
|
1033
1046
|
});
|
|
1034
1047
|
}
|
|
1035
1048
|
|
|
1049
|
+
/** Whether the file exports a runtime value as `default`. */
|
|
1050
|
+
function hasDefaultExport(sourceFile) {
|
|
1051
|
+
return findDefaultExport(sourceFile) !== undefined;
|
|
1052
|
+
}
|
|
1053
|
+
|
|
1036
1054
|
/** A short, human-readable "what you exported instead" for the error message. */
|
|
1037
1055
|
function describeDefaultExport(value) {
|
|
1038
1056
|
if (value === null) {
|
|
@@ -4558,6 +4576,52 @@ class ValOps {
|
|
|
4558
4576
|
return null;
|
|
4559
4577
|
}
|
|
4560
4578
|
|
|
4579
|
+
/**
|
|
4580
|
+
* How this project's SOURCE is kept, or `null` when it is nobody's question.
|
|
4581
|
+
*
|
|
4582
|
+
* `"managed"` -- the content service is the store of record, there is no
|
|
4583
|
+
* repository, and nothing outside the browser will ever pick a commit up.
|
|
4584
|
+
* `"connected"` -- commits are mirrored into a repository a host watches.
|
|
4585
|
+
*
|
|
4586
|
+
* `null` is the honest answer for `fs` and memory mode, where publishing is
|
|
4587
|
+
* writing to disk and there is no project to have a mode, and for an `http`
|
|
4588
|
+
* project whose content service predates the field. The Studio treats it as
|
|
4589
|
+
* "the story I have always told", which is the connected one -- the feed, and
|
|
4590
|
+
* a `building` state that something outside resolves. Guessing `managed`
|
|
4591
|
+
* instead would take the deploy feed away from every project running against
|
|
4592
|
+
* an older service.
|
|
4593
|
+
*/
|
|
4594
|
+
sourceMode() {
|
|
4595
|
+
return null;
|
|
4596
|
+
}
|
|
4597
|
+
|
|
4598
|
+
/**
|
|
4599
|
+
* Forward one call to the content service's publish API, as this project.
|
|
4600
|
+
*
|
|
4601
|
+
* The Studio builds a managed project in the tab and then has to publish what
|
|
4602
|
+
* it built -- but a browser cannot talk to content directly. It holds a
|
|
4603
|
+
* session cookie for THIS origin and no credential content would accept, so
|
|
4604
|
+
* the conversation goes through here, exactly as patches already do.
|
|
4605
|
+
*
|
|
4606
|
+
* Refused by default, and that is the honest answer rather than a gap: `fs`
|
|
4607
|
+
* and memory mode have no content service to forward to, and publishing there
|
|
4608
|
+
* is writing to disk.
|
|
4609
|
+
*
|
|
4610
|
+
* The path is content's, minus the `/v1` prefix -- `/publish`,
|
|
4611
|
+
* `/publish/{id}/artifacts`, `/build-target`. The ALLOW LIST lives in the
|
|
4612
|
+
* implementation rather than here, because it is what keeps this from being
|
|
4613
|
+
* a way to reach the rest of content with the project's credential.
|
|
4614
|
+
*/
|
|
4615
|
+
async publishApi(_path, _init) {
|
|
4616
|
+
return {
|
|
4617
|
+
status: 501,
|
|
4618
|
+
contentType: "application/json",
|
|
4619
|
+
body: JSON.stringify({
|
|
4620
|
+
message: "This Val server has no content service to publish through. " + "Publishing from the browser is for a project whose content is " + "served over HTTP."
|
|
4621
|
+
})
|
|
4622
|
+
};
|
|
4623
|
+
}
|
|
4624
|
+
|
|
4561
4625
|
/**
|
|
4562
4626
|
* Whether a commit here produces `.val.ts` TEXT as well as data.
|
|
4563
4627
|
*
|
|
@@ -7917,6 +7981,202 @@ class ValOpsHttp extends ValOps {
|
|
|
7917
7981
|
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."
|
|
7918
7982
|
};
|
|
7919
7983
|
}
|
|
7984
|
+
|
|
7985
|
+
/**
|
|
7986
|
+
* What the content service last said this project's source mode is.
|
|
7987
|
+
*
|
|
7988
|
+
* Read off the same remembered expectation {@link publishRefusal} uses. It is
|
|
7989
|
+
* CURRENT wherever it matters rather than a poll behind, and by construction
|
|
7990
|
+
* rather than by luck: the only caller is `/stat`, which awaits `getStat`
|
|
7991
|
+
* first, and that fetches the patches -- which is the response the expectation
|
|
7992
|
+
* is recorded from.
|
|
7993
|
+
*
|
|
7994
|
+
* `null` before anything has fetched patches, which is the same "not
|
|
7995
|
+
* reported" the wire field means, and reads as connected. The alternative
|
|
7996
|
+
* would be to ask for it separately, which is a round trip for a field that
|
|
7997
|
+
* has just arrived.
|
|
7998
|
+
*/
|
|
7999
|
+
sourceMode() {
|
|
8000
|
+
var _this$projectExpectat2;
|
|
8001
|
+
return ((_this$projectExpectat2 = this.projectExpectation) === null || _this$projectExpectat2 === void 0 ? void 0 : _this$projectExpectat2.sourceMode) ?? null;
|
|
8002
|
+
}
|
|
8003
|
+
|
|
8004
|
+
/**
|
|
8005
|
+
* The short-lived publish token, and when it stops being usable.
|
|
8006
|
+
*
|
|
8007
|
+
* `null` until something publishes, which for most deployments is never.
|
|
8008
|
+
*/
|
|
8009
|
+
publishToken = null;
|
|
8010
|
+
|
|
8011
|
+
/**
|
|
8012
|
+
* Trade this deployment's credential for one that can do one thing.
|
|
8013
|
+
*
|
|
8014
|
+
* Content's publish API takes a PROJECT TOKEN and nothing else --
|
|
8015
|
+
* `authenticateProjectToken` refuses anything that is not one, deliberately,
|
|
8016
|
+
* because a personal access token is a person's credential and does not name
|
|
8017
|
+
* a project. What this deployment holds is the project's api key, which is
|
|
8018
|
+
* neither.
|
|
8019
|
+
*
|
|
8020
|
+
* `POST /v1/{org}/{project}/publish-token` is the exchange, and it was built
|
|
8021
|
+
* for exactly this: its own docblock names the case where "the caller was the
|
|
8022
|
+
* project's api key ... a machine exchanging one machine credential for a
|
|
8023
|
+
* narrower one". What comes back can publish one project for ten minutes.
|
|
8024
|
+
*
|
|
8025
|
+
* So the api key never leaves this process and the browser never sees any
|
|
8026
|
+
* credential at all. That is the point of routing the publish through here
|
|
8027
|
+
* rather than letting the tab talk to content.
|
|
8028
|
+
*/
|
|
8029
|
+
async mintPublishToken() {
|
|
8030
|
+
const res = await fetch(`${this.contentUrl}/v1/${this.project}/publish-token`, {
|
|
8031
|
+
method: "POST",
|
|
8032
|
+
headers: this.authHeaders
|
|
8033
|
+
});
|
|
8034
|
+
const text = await res.text();
|
|
8035
|
+
if (!res.ok) {
|
|
8036
|
+
return {
|
|
8037
|
+
status: res.status,
|
|
8038
|
+
error: `Could not get a publish token for '${this.project}': ` + `${res.status} ${text.slice(0, 300)}`
|
|
8039
|
+
};
|
|
8040
|
+
}
|
|
8041
|
+
let parsed;
|
|
8042
|
+
try {
|
|
8043
|
+
parsed = JSON.parse(text);
|
|
8044
|
+
} catch {
|
|
8045
|
+
return {
|
|
8046
|
+
status: 502,
|
|
8047
|
+
error: "The publish token exchange did not answer with JSON."
|
|
8048
|
+
};
|
|
8049
|
+
}
|
|
8050
|
+
const token = typeof parsed === "object" && parsed !== null && "token" in parsed && typeof parsed.token === "string" ? parsed.token : null;
|
|
8051
|
+
if (token === null) {
|
|
8052
|
+
return {
|
|
8053
|
+
status: 502,
|
|
8054
|
+
error: "The publish token exchange sent no token."
|
|
8055
|
+
};
|
|
8056
|
+
}
|
|
8057
|
+
const expiresAtRaw = typeof parsed === "object" && parsed !== null && "expiresAt" in parsed ? parsed.expiresAt : null;
|
|
8058
|
+
const expiresAt = typeof expiresAtRaw === "string" ? Date.parse(expiresAtRaw) : NaN;
|
|
8059
|
+
return {
|
|
8060
|
+
token,
|
|
8061
|
+
/*
|
|
8062
|
+
* A token with no expiry, or one we cannot read, is treated as expiring
|
|
8063
|
+
* NOW -- so it is used for this call and minted again for the next.
|
|
8064
|
+
* Caching one we cannot reason about is how a publish starts failing
|
|
8065
|
+
* halfway through, days later, for no reason anyone can see.
|
|
8066
|
+
*/
|
|
8067
|
+
expiresAt: Number.isFinite(expiresAt) ? expiresAt : 0
|
|
8068
|
+
};
|
|
8069
|
+
}
|
|
8070
|
+
|
|
8071
|
+
/**
|
|
8072
|
+
* A usable publish token, minting one when what we have will not last.
|
|
8073
|
+
*
|
|
8074
|
+
* The margin is what stops a token that is valid when the publish starts from
|
|
8075
|
+
* expiring in the middle of it: a publish is five calls and an upload of
|
|
8076
|
+
* every artifact, and the upload is the slow one.
|
|
8077
|
+
*/
|
|
8078
|
+
async currentPublishToken() {
|
|
8079
|
+
const margin = 60_000;
|
|
8080
|
+
if (this.publishToken !== null && this.publishToken.expiresAt - margin > Date.now()) {
|
|
8081
|
+
return {
|
|
8082
|
+
token: this.publishToken.token
|
|
8083
|
+
};
|
|
8084
|
+
}
|
|
8085
|
+
const minted = await this.mintPublishToken();
|
|
8086
|
+
if ("error" in minted) {
|
|
8087
|
+
this.publishToken = null;
|
|
8088
|
+
return minted;
|
|
8089
|
+
}
|
|
8090
|
+
this.publishToken = minted;
|
|
8091
|
+
return {
|
|
8092
|
+
token: minted.token
|
|
8093
|
+
};
|
|
8094
|
+
}
|
|
8095
|
+
|
|
8096
|
+
/**
|
|
8097
|
+
* The content paths this may reach, and nothing else.
|
|
8098
|
+
*
|
|
8099
|
+
* An allow list rather than a prefix check, because this method holds a
|
|
8100
|
+
* credential and the browser chooses the path. `/publish/{id}` and its three
|
|
8101
|
+
* steps are the publish conversation; `/build-target` is what a build needs
|
|
8102
|
+
* to know before it starts; `/project-source` is what it builds.
|
|
8103
|
+
*
|
|
8104
|
+
* `/project-source` is here rather than on a route of its own because it is
|
|
8105
|
+
* one of the three things a publish asks for and none of them are useful
|
|
8106
|
+
* apart -- and because the credential is the same one. The Studio cannot get
|
|
8107
|
+
* the project's files any other way: it runs inside the deployment, which
|
|
8108
|
+
* holds a session for its own origin and an api key for content, and content
|
|
8109
|
+
* is the only thing it is allowed to talk to at all.
|
|
8110
|
+
*
|
|
8111
|
+
* A publish id is opaque and content-generated, so it is matched rather than
|
|
8112
|
+
* parsed -- what matters is that nothing with a `..`, a query or another
|
|
8113
|
+
* segment gets through.
|
|
8114
|
+
*/
|
|
8115
|
+
static publishApiPathAllowed(path) {
|
|
8116
|
+
if (path === "/build-target" || path === "/project-source" || path === "/publish") {
|
|
8117
|
+
return true;
|
|
8118
|
+
}
|
|
8119
|
+
return /^\/publish\/[A-Za-z0-9_-]+(\/(artifacts|verify|promote))?$/.test(path);
|
|
8120
|
+
}
|
|
8121
|
+
async publishApi(path, init) {
|
|
8122
|
+
const json = "application/json";
|
|
8123
|
+
if (!ValOpsHttp.publishApiPathAllowed(path)) {
|
|
8124
|
+
return {
|
|
8125
|
+
status: 403,
|
|
8126
|
+
contentType: json,
|
|
8127
|
+
body: JSON.stringify({
|
|
8128
|
+
message: `'${path}' is not part of the publish API.`
|
|
8129
|
+
})
|
|
8130
|
+
};
|
|
8131
|
+
}
|
|
8132
|
+
const send = async token => fetch(`${this.contentUrl}/v1${path}`, {
|
|
8133
|
+
method: init.method,
|
|
8134
|
+
headers: {
|
|
8135
|
+
Authorization: `Bearer ${token}`,
|
|
8136
|
+
...(init.body === undefined ? {} : {
|
|
8137
|
+
"Content-Type": json
|
|
8138
|
+
})
|
|
8139
|
+
},
|
|
8140
|
+
...(init.body === undefined ? {} : {
|
|
8141
|
+
body: init.body
|
|
8142
|
+
})
|
|
8143
|
+
});
|
|
8144
|
+
const credential = await this.currentPublishToken();
|
|
8145
|
+
if ("error" in credential) {
|
|
8146
|
+
return {
|
|
8147
|
+
status: credential.status,
|
|
8148
|
+
contentType: json,
|
|
8149
|
+
body: JSON.stringify({
|
|
8150
|
+
message: credential.error
|
|
8151
|
+
})
|
|
8152
|
+
};
|
|
8153
|
+
}
|
|
8154
|
+
let res = await send(credential.token);
|
|
8155
|
+
if (res.status === 401) {
|
|
8156
|
+
/*
|
|
8157
|
+
* Revoked, or expired sooner than it said. One retry with a fresh token,
|
|
8158
|
+
* because the alternative is a publish that fails for a reason the editor
|
|
8159
|
+
* cannot act on and a retry that fails the same way.
|
|
8160
|
+
*/
|
|
8161
|
+
this.publishToken = null;
|
|
8162
|
+
const second = await this.currentPublishToken();
|
|
8163
|
+
if ("error" in second) {
|
|
8164
|
+
return {
|
|
8165
|
+
status: second.status,
|
|
8166
|
+
contentType: json,
|
|
8167
|
+
body: JSON.stringify({
|
|
8168
|
+
message: second.error
|
|
8169
|
+
})
|
|
8170
|
+
};
|
|
8171
|
+
}
|
|
8172
|
+
res = await send(second.token);
|
|
8173
|
+
}
|
|
8174
|
+
return {
|
|
8175
|
+
status: res.status,
|
|
8176
|
+
contentType: res.headers.get("content-type") ?? json,
|
|
8177
|
+
body: await res.text()
|
|
8178
|
+
};
|
|
8179
|
+
}
|
|
7920
8180
|
async onInit() {
|
|
7921
8181
|
// TODO: unused for now. Implement or remove
|
|
7922
8182
|
}
|
|
@@ -11475,6 +11735,38 @@ const ValServer = (valModules, options, callbacks) => {
|
|
|
11475
11735
|
}
|
|
11476
11736
|
};
|
|
11477
11737
|
};
|
|
11738
|
+
|
|
11739
|
+
/**
|
|
11740
|
+
* Hand one publish-API call to content and carry the answer back.
|
|
11741
|
+
*
|
|
11742
|
+
* `JSON.parse` and nothing more. That is not validation -- there is no schema
|
|
11743
|
+
* here and no knowledge of content's shapes -- it is the round trip a JSON
|
|
11744
|
+
* body has to make to travel as `json` rather than as a string. A body that
|
|
11745
|
+
* does not parse is a gateway's error page rather than content answering, so
|
|
11746
|
+
* it comes back as a message with the original status instead of throwing:
|
|
11747
|
+
* a publish that failed is something the editor has to be told in words.
|
|
11748
|
+
*/
|
|
11749
|
+
const proxyPublishApi = async (path, method, body) => {
|
|
11750
|
+
const answer = await serverOps.publishApi(path ?? "", {
|
|
11751
|
+
method,
|
|
11752
|
+
...(body === undefined ? {} : {
|
|
11753
|
+
body
|
|
11754
|
+
})
|
|
11755
|
+
});
|
|
11756
|
+
try {
|
|
11757
|
+
return {
|
|
11758
|
+
status: answer.status,
|
|
11759
|
+
json: JSON.parse(answer.body)
|
|
11760
|
+
};
|
|
11761
|
+
} catch {
|
|
11762
|
+
return {
|
|
11763
|
+
status: answer.status,
|
|
11764
|
+
json: {
|
|
11765
|
+
message: answer.body.trim() === "" ? `The publish API answered ${answer.status} with no body.` : `The publish API answered ${answer.status}: ${answer.body.slice(0, 300)}`
|
|
11766
|
+
}
|
|
11767
|
+
};
|
|
11768
|
+
}
|
|
11769
|
+
};
|
|
11478
11770
|
return {
|
|
11479
11771
|
"/draft/enable": {
|
|
11480
11772
|
GET: async req => {
|
|
@@ -11898,6 +12190,16 @@ const ValServer = (valModules, options, callbacks) => {
|
|
|
11898
12190
|
* it failed to compute.
|
|
11899
12191
|
*/
|
|
11900
12192
|
const publishRefusal = serverOps.publishRefusal();
|
|
12193
|
+
/*
|
|
12194
|
+
* How this project's source is kept, when the store knows.
|
|
12195
|
+
*
|
|
12196
|
+
* Spread for the same reason `publishRefusal` is: absent has to keep
|
|
12197
|
+
* meaning "not reported" rather than becoming a mode. The Studio
|
|
12198
|
+
* narrates a publish differently for a managed project -- there is
|
|
12199
|
+
* nothing outside the browser to finish it -- so a wrong default here
|
|
12200
|
+
* shows one story to a project that lives by the other.
|
|
12201
|
+
*/
|
|
12202
|
+
const sourceMode = serverOps.sourceMode();
|
|
11901
12203
|
return {
|
|
11902
12204
|
status: 200,
|
|
11903
12205
|
json: {
|
|
@@ -11907,11 +12209,55 @@ const ValServer = (valModules, options, callbacks) => {
|
|
|
11907
12209
|
...(publishRefusal ? {
|
|
11908
12210
|
publishRefusal
|
|
11909
12211
|
} : {}),
|
|
12212
|
+
...(sourceMode ? {
|
|
12213
|
+
sourceMode
|
|
12214
|
+
} : {}),
|
|
11910
12215
|
config: options.config
|
|
11911
12216
|
}
|
|
11912
12217
|
};
|
|
11913
12218
|
}
|
|
11914
12219
|
},
|
|
12220
|
+
/**
|
|
12221
|
+
* The content service's publish API, reached through this deployment.
|
|
12222
|
+
*
|
|
12223
|
+
* The Studio builds a managed project in its own tab and then publishes
|
|
12224
|
+
* what it built. It cannot talk to content directly -- it holds a session
|
|
12225
|
+
* cookie for this origin and no credential content would accept -- so the
|
|
12226
|
+
* whole conversation comes through here.
|
|
12227
|
+
*
|
|
12228
|
+
* Three things happen, and nothing else: the session is checked, the
|
|
12229
|
+
* credential is swapped for a publish-scoped one (see
|
|
12230
|
+
* `ValOpsHttp.publishApi`), and content's answer is carried back with its
|
|
12231
|
+
* status intact. The Studio parses that answer with the same module
|
|
12232
|
+
* `val publish` uses, so there is no copy of content's shapes here to
|
|
12233
|
+
* fall out of step.
|
|
12234
|
+
*/
|
|
12235
|
+
"/publish-api": {
|
|
12236
|
+
GET: async req => {
|
|
12237
|
+
const auth = getAuth(req.cookies);
|
|
12238
|
+
if (auth.error) {
|
|
12239
|
+
return {
|
|
12240
|
+
status: 401,
|
|
12241
|
+
json: {
|
|
12242
|
+
message: auth.error
|
|
12243
|
+
}
|
|
12244
|
+
};
|
|
12245
|
+
}
|
|
12246
|
+
return proxyPublishApi(req.path, "GET");
|
|
12247
|
+
},
|
|
12248
|
+
POST: async req => {
|
|
12249
|
+
const auth = getAuth(req.cookies);
|
|
12250
|
+
if (auth.error) {
|
|
12251
|
+
return {
|
|
12252
|
+
status: 401,
|
|
12253
|
+
json: {
|
|
12254
|
+
message: auth.error
|
|
12255
|
+
}
|
|
12256
|
+
};
|
|
12257
|
+
}
|
|
12258
|
+
return proxyPublishApi(req.path, "POST", req.body === undefined ? undefined : JSON.stringify(req.body));
|
|
12259
|
+
}
|
|
12260
|
+
},
|
|
11915
12261
|
"/upload/patches": {
|
|
11916
12262
|
POST: async req => {
|
|
11917
12263
|
if (serverOps instanceof ValOpsHttp) {
|
|
@@ -13469,7 +13815,15 @@ const ValServer = (valModules, options, callbacks) => {
|
|
|
13469
13815
|
return {
|
|
13470
13816
|
status: 200,
|
|
13471
13817
|
json: {
|
|
13472
|
-
commitSha: commitRes.commit
|
|
13818
|
+
commitSha: commitRes.commit,
|
|
13819
|
+
/*
|
|
13820
|
+
* For the Studio to BUILD, when it is the deployer. See
|
|
13821
|
+
* `sourceFiles` on the route: the build must contain the text
|
|
13822
|
+
* this commit wrote, and only this handler has it.
|
|
13823
|
+
*/
|
|
13824
|
+
...(serverOps.sourceMode() === "managed" ? {
|
|
13825
|
+
sourceFiles: preparedCommit.patchedSourceFiles
|
|
13826
|
+
} : {})
|
|
13473
13827
|
}
|
|
13474
13828
|
};
|
|
13475
13829
|
}
|
|
@@ -18039,6 +18393,7 @@ exports.extractFileMetadata = extractFileMetadata;
|
|
|
18039
18393
|
exports.extractImageMetadata = extractImageMetadata;
|
|
18040
18394
|
exports.extractJsonValuesEntry = extractJsonValuesEntry;
|
|
18041
18395
|
exports.findAndEvalValConfigFile = findAndEvalValConfigFile;
|
|
18396
|
+
exports.findDefaultExport = findDefaultExport;
|
|
18042
18397
|
exports.findJsonEntryFilePath = findJsonEntryFilePath;
|
|
18043
18398
|
exports.fixHandlers = fixHandlers;
|
|
18044
18399
|
exports.formatPatchSourceError = formatPatchSourceError;
|