@valbuild/server 0.16.4 → 0.17.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/dist/declarations/src/LocalValServer.d.ts +7 -5
- package/dist/declarations/src/ValServer.d.ts +3 -7
- package/dist/declarations/src/hosting.d.ts +34 -0
- package/dist/declarations/src/index.d.ts +1 -1
- package/dist/valbuild-server.cjs.dev.js +249 -95
- package/dist/valbuild-server.cjs.prod.js +249 -95
- package/dist/valbuild-server.esm.js +232 -96
- package/package.json +3 -3
- package/src/LocalValServer.ts +82 -60
- package/src/ProxyValServer.ts +66 -36
- package/src/Service.ts +4 -3
- package/src/ValQuickJSRuntime.ts +18 -0
- package/src/ValServer.ts +4 -11
- package/src/createRequestHandler.ts +6 -6
- package/src/hosting.ts +122 -1
- package/src/index.ts +1 -1
- package/src/patch/ts/ops.test.ts +0 -1
- package/src/readValFile.ts +2 -2
package/src/LocalValServer.ts
CHANGED
@@ -5,30 +5,53 @@ import { parsePatch, PatchError } from "@valbuild/core/patch";
|
|
5
5
|
import { getPathFromParams } from "./expressHelpers";
|
6
6
|
import { PatchJSON } from "./patch/validation";
|
7
7
|
import { ValServer } from "./ValServer";
|
8
|
-
import {
|
9
|
-
import { enable } from "./ProxyValServer";
|
8
|
+
import { ApiTreeResponse, ModuleId, ModulePath } from "@valbuild/core";
|
9
|
+
import { disable, enable } from "./ProxyValServer";
|
10
10
|
import { promises as fs } from "fs";
|
11
11
|
import path from "path";
|
12
12
|
|
13
13
|
export type LocalValServerOptions = {
|
14
14
|
service: Service;
|
15
|
+
git: {
|
16
|
+
commit?: string;
|
17
|
+
branch?: string;
|
18
|
+
};
|
15
19
|
};
|
16
20
|
|
17
21
|
export class LocalValServer implements ValServer {
|
18
22
|
constructor(readonly options: LocalValServerOptions) {}
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
23
|
+
|
24
|
+
async session(_req: express.Request, res: express.Response): Promise<void> {
|
25
|
+
res.json({
|
26
|
+
mode: "local",
|
27
|
+
});
|
28
|
+
}
|
29
|
+
|
30
|
+
async getTree(req: express.Request, res: express.Response): Promise<void> {
|
31
|
+
try {
|
32
|
+
// TODO: use the params: patch, schema, source
|
33
|
+
const treePath = req.params["0"].replace("~", "");
|
34
|
+
const rootDir = process.cwd();
|
35
|
+
const moduleIds: string[] = [];
|
36
|
+
// iterate over all .val files in the root directory
|
37
|
+
const walk = async (dir: string) => {
|
38
|
+
const files = await fs.readdir(dir);
|
39
|
+
for (const file of files) {
|
40
|
+
if ((await fs.stat(path.join(dir, file))).isDirectory()) {
|
41
|
+
if (file === "node_modules") continue;
|
42
|
+
await walk(path.join(dir, file));
|
43
|
+
} else {
|
44
|
+
const isValFile =
|
45
|
+
file.endsWith(".val.js") || file.endsWith(".val.ts");
|
46
|
+
if (!isValFile) {
|
47
|
+
continue;
|
48
|
+
}
|
49
|
+
if (
|
50
|
+
treePath &&
|
51
|
+
!path.join(dir, file).replace(rootDir, "").startsWith(treePath)
|
52
|
+
) {
|
53
|
+
continue;
|
54
|
+
}
|
32
55
|
moduleIds.push(
|
33
56
|
path
|
34
57
|
.join(dir, file)
|
@@ -38,59 +61,59 @@ export class LocalValServer implements ValServer {
|
|
38
61
|
);
|
39
62
|
}
|
40
63
|
}
|
41
|
-
}
|
42
|
-
|
64
|
+
};
|
65
|
+
const serializedModuleContent = await walk(rootDir).then(async () => {
|
66
|
+
return Promise.all(
|
67
|
+
moduleIds.map(async (moduleId) => {
|
68
|
+
return await this.options.service.get(
|
69
|
+
moduleId as ModuleId,
|
70
|
+
"" as ModulePath
|
71
|
+
);
|
72
|
+
})
|
73
|
+
);
|
74
|
+
});
|
43
75
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
)
|
55
|
-
)
|
76
|
+
//
|
77
|
+
const modules = Object.fromEntries(
|
78
|
+
serializedModuleContent.map((serializedModuleContent) => {
|
79
|
+
const module: ApiTreeResponse["modules"][keyof ApiTreeResponse["modules"]] =
|
80
|
+
{
|
81
|
+
schema: serializedModuleContent.schema,
|
82
|
+
source: serializedModuleContent.source,
|
83
|
+
};
|
84
|
+
return [serializedModuleContent.path, module];
|
85
|
+
})
|
56
86
|
);
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
87
|
+
const apiTreeResponse: ApiTreeResponse = {
|
88
|
+
modules,
|
89
|
+
git: this.options.git,
|
90
|
+
};
|
91
|
+
return walk(rootDir).then(async () => {
|
92
|
+
res.send(JSON.stringify(apiTreeResponse));
|
93
|
+
});
|
94
|
+
} catch (err) {
|
95
|
+
console.error(err);
|
96
|
+
res.sendStatus(500);
|
97
|
+
}
|
64
98
|
}
|
65
99
|
|
66
100
|
async enable(req: express.Request, res: express.Response): Promise<void> {
|
67
101
|
return enable(req, res);
|
68
102
|
}
|
69
103
|
|
70
|
-
async
|
71
|
-
req
|
72
|
-
res: express.Response
|
73
|
-
): Promise<void> {
|
74
|
-
try {
|
75
|
-
console.log(req.params);
|
76
|
-
const path = getPathFromParams(req.params);
|
77
|
-
const [moduleId, modulePath] = Internal.splitModuleIdAndModulePath(path);
|
78
|
-
|
79
|
-
const valModule = await this.options.service.get(moduleId, modulePath);
|
80
|
-
|
81
|
-
res.json(valModule);
|
82
|
-
} catch (err) {
|
83
|
-
console.error(err);
|
84
|
-
res.sendStatus(500);
|
85
|
-
}
|
104
|
+
async disable(req: express.Request, res: express.Response): Promise<void> {
|
105
|
+
return disable(req, res);
|
86
106
|
}
|
87
107
|
|
88
|
-
async
|
108
|
+
async postPatches(
|
89
109
|
req: express.Request<{ 0: string }>,
|
90
110
|
res: express.Response
|
91
111
|
): Promise<void> {
|
112
|
+
const id = getPathFromParams(req.params)?.replace("/~", "");
|
113
|
+
|
92
114
|
// First validate that the body has the right structure
|
93
115
|
const patchJSON = PatchJSON.safeParse(req.body);
|
116
|
+
console.log("patch id", id, patchJSON);
|
94
117
|
if (!patchJSON.success) {
|
95
118
|
res.status(401).json(patchJSON.error.issues);
|
96
119
|
return;
|
@@ -101,18 +124,17 @@ export class LocalValServer implements ValServer {
|
|
101
124
|
res.status(401).json(patch.error);
|
102
125
|
return;
|
103
126
|
}
|
104
|
-
const id = getPathFromParams(req.params);
|
105
127
|
try {
|
106
|
-
|
107
|
-
res.json(
|
128
|
+
await this.options.service.patch(id, patch.value);
|
129
|
+
res.json({});
|
108
130
|
} catch (err) {
|
109
131
|
if (err instanceof PatchError) {
|
110
|
-
res.status(
|
132
|
+
res.status(400).send({ message: err.message });
|
111
133
|
} else {
|
112
134
|
console.error(err);
|
113
|
-
res
|
114
|
-
.
|
115
|
-
|
135
|
+
res.status(500).send({
|
136
|
+
message: err instanceof Error ? err.message : "Unknown error",
|
137
|
+
});
|
116
138
|
}
|
117
139
|
}
|
118
140
|
}
|
package/src/ProxyValServer.ts
CHANGED
@@ -3,14 +3,13 @@ import crypto from "crypto";
|
|
3
3
|
import { decodeJwt, encodeJwt, getExpire } from "./jwt";
|
4
4
|
import { PatchJSON } from "./patch/validation";
|
5
5
|
import { result } from "@valbuild/core/fp";
|
6
|
-
import { getPathFromParams } from "./expressHelpers";
|
7
6
|
import { ValServer } from "./ValServer";
|
8
7
|
import { z } from "zod";
|
9
8
|
import { parsePatch } from "@valbuild/core/patch";
|
10
9
|
import { Internal } from "@valbuild/core";
|
11
10
|
|
12
|
-
const VAL_SESSION_COOKIE =
|
13
|
-
const VAL_STATE_COOKIE =
|
11
|
+
const VAL_SESSION_COOKIE = Internal.VAL_SESSION_COOKIE;
|
12
|
+
const VAL_STATE_COOKIE = Internal.VAL_STATE_COOKIE;
|
14
13
|
const VAL_ENABLED_COOKIE = Internal.VAL_ENABLE_COOKIE_NAME;
|
15
14
|
|
16
15
|
export type ProxyValServerOptions = {
|
@@ -18,20 +17,17 @@ export type ProxyValServerOptions = {
|
|
18
17
|
route: string;
|
19
18
|
valSecret: string;
|
20
19
|
valBuildUrl: string;
|
20
|
+
valContentUrl: string;
|
21
21
|
gitCommit: string;
|
22
22
|
gitBranch: string;
|
23
23
|
valName: string;
|
24
|
+
valEnableRedirectUrl?: string;
|
25
|
+
valDisableRedirectUrl?: string;
|
24
26
|
};
|
25
27
|
|
26
28
|
export class ProxyValServer implements ValServer {
|
27
29
|
constructor(readonly options: ProxyValServerOptions) {}
|
28
30
|
|
29
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
30
|
-
getAllModules(_req: express.Request, _res: express.Response): Promise<void> {
|
31
|
-
// TODO:
|
32
|
-
throw new Error("Method not implemented.");
|
33
|
-
}
|
34
|
-
|
35
31
|
async authorize(req: express.Request, res: express.Response): Promise<void> {
|
36
32
|
const { redirect_to } = req.query;
|
37
33
|
if (typeof redirect_to !== "string") {
|
@@ -56,7 +52,10 @@ export class ProxyValServer implements ValServer {
|
|
56
52
|
}
|
57
53
|
|
58
54
|
async enable(req: express.Request, res: express.Response): Promise<void> {
|
59
|
-
return enable(req, res);
|
55
|
+
return enable(req, res, this.options.valEnableRedirectUrl);
|
56
|
+
}
|
57
|
+
async disable(req: express.Request, res: express.Response): Promise<void> {
|
58
|
+
return disable(req, res, this.options.valEnableRedirectUrl);
|
60
59
|
}
|
61
60
|
|
62
61
|
async callback(req: express.Request, res: express.Response): Promise<void> {
|
@@ -143,33 +142,37 @@ export class ProxyValServer implements ValServer {
|
|
143
142
|
});
|
144
143
|
}
|
145
144
|
|
146
|
-
async
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
145
|
+
async getTree(req: express.Request, res: express.Response): Promise<void> {
|
146
|
+
return this.withAuth(req, res, async (data) => {
|
147
|
+
const { patch, schema, source } = req.query;
|
148
|
+
const params = new URLSearchParams({
|
149
|
+
patch: (patch === "true").toString(),
|
150
|
+
schema: (schema === "true").toString(),
|
151
|
+
source: (source === "true").toString(),
|
152
|
+
});
|
152
153
|
const url = new URL(
|
153
|
-
`/
|
154
|
-
this.options.
|
154
|
+
`/v1/tree/${this.options.valName}/heads/${this.options.gitBranch}/${req.params["0"]}/?${params}`,
|
155
|
+
this.options.valContentUrl
|
155
156
|
);
|
156
|
-
const
|
157
|
-
headers: this.getAuthHeaders(token),
|
158
|
-
});
|
159
|
-
|
160
|
-
res.status(fetchRes.status).json(await fetchRes.json());
|
161
|
-
} else {
|
162
|
-
res.sendStatus(fetchRes.status);
|
163
|
-
}
|
164
|
-
}).catch((e) => {
|
165
|
-
res.status(500).send({ error: { message: e?.message, status: 500 } });
|
157
|
+
const json = await fetch(url, {
|
158
|
+
headers: this.getAuthHeaders(data.token, "application/json"),
|
159
|
+
}).then((res) => res.json());
|
160
|
+
res.send(json);
|
166
161
|
});
|
167
162
|
}
|
168
163
|
|
169
|
-
async
|
164
|
+
async postPatches(
|
170
165
|
req: express.Request<{ 0: string }>,
|
171
166
|
res: express.Response
|
172
167
|
): Promise<void> {
|
168
|
+
const { commit } = req.query;
|
169
|
+
if (typeof commit !== "string" || typeof commit === "undefined") {
|
170
|
+
res.status(401).json({ error: "Missing or invalid commit query param" });
|
171
|
+
return;
|
172
|
+
}
|
173
|
+
const params = new URLSearchParams({
|
174
|
+
commit,
|
175
|
+
});
|
173
176
|
this.withAuth(req, res, async ({ token }) => {
|
174
177
|
// First validate that the body has the right structure
|
175
178
|
const patchJSON = PatchJSON.safeParse(req.body);
|
@@ -183,15 +186,14 @@ export class ProxyValServer implements ValServer {
|
|
183
186
|
res.status(401).json(patch.error);
|
184
187
|
return;
|
185
188
|
}
|
186
|
-
const id = getPathFromParams(req.params);
|
187
189
|
const url = new URL(
|
188
|
-
`/
|
189
|
-
this.options.
|
190
|
+
`/v1/tree/${this.options.valName}/heads/${this.options.gitBranch}/${req.params["0"]}/?${params}`,
|
191
|
+
this.options.valContentUrl
|
190
192
|
);
|
191
193
|
// Proxy patch to val.build
|
192
194
|
const fetchRes = await fetch(url, {
|
193
|
-
method: "
|
194
|
-
headers: this.getAuthHeaders(token, "application/json
|
195
|
+
method: "POST",
|
196
|
+
headers: this.getAuthHeaders(token, "application/json"),
|
195
197
|
body: JSON.stringify(patch),
|
196
198
|
});
|
197
199
|
if (fetchRes.ok) {
|
@@ -400,16 +402,44 @@ function getStateFromCookie(stateCookie: string):
|
|
400
402
|
|
401
403
|
export async function enable(
|
402
404
|
req: express.Request,
|
403
|
-
res: express.Response
|
405
|
+
res: express.Response,
|
406
|
+
redirectUrl?: string
|
404
407
|
): Promise<void> {
|
405
408
|
const { redirect_to } = req.query;
|
406
409
|
if (typeof redirect_to === "string" || typeof redirect_to === "undefined") {
|
410
|
+
let redirectUrlToUse = redirect_to || "/";
|
411
|
+
if (redirectUrl) {
|
412
|
+
redirectUrlToUse =
|
413
|
+
redirectUrl + "?redirect_to=" + encodeURIComponent(redirectUrlToUse);
|
414
|
+
}
|
407
415
|
res
|
408
416
|
.cookie(VAL_ENABLED_COOKIE, "true", {
|
409
417
|
httpOnly: false,
|
410
418
|
sameSite: "lax",
|
411
419
|
})
|
412
|
-
.redirect(
|
420
|
+
.redirect(redirectUrlToUse);
|
421
|
+
} else {
|
422
|
+
res.sendStatus(400);
|
423
|
+
}
|
424
|
+
}
|
425
|
+
export async function disable(
|
426
|
+
req: express.Request,
|
427
|
+
res: express.Response,
|
428
|
+
redirectUrl?: string
|
429
|
+
): Promise<void> {
|
430
|
+
const { redirect_to } = req.query;
|
431
|
+
if (typeof redirect_to === "string" || typeof redirect_to === "undefined") {
|
432
|
+
let redirectUrlToUse = redirect_to || "/";
|
433
|
+
if (redirectUrl) {
|
434
|
+
redirectUrlToUse =
|
435
|
+
redirectUrl + "?redirect_to=" + encodeURIComponent(redirectUrlToUse);
|
436
|
+
}
|
437
|
+
res
|
438
|
+
.cookie(VAL_ENABLED_COOKIE, "false", {
|
439
|
+
httpOnly: false,
|
440
|
+
sameSite: "lax",
|
441
|
+
})
|
442
|
+
.redirect(redirectUrlToUse);
|
413
443
|
} else {
|
414
444
|
res.sendStatus(400);
|
415
445
|
}
|
package/src/Service.ts
CHANGED
@@ -16,7 +16,6 @@ import {
|
|
16
16
|
Internal,
|
17
17
|
SourcePath,
|
18
18
|
Schema,
|
19
|
-
SelectorSource,
|
20
19
|
} from "@valbuild/core";
|
21
20
|
|
22
21
|
export type ServiceOptions = {
|
@@ -79,11 +78,13 @@ export class Service {
|
|
79
78
|
valModule.source,
|
80
79
|
valModule.schema
|
81
80
|
);
|
82
|
-
const sourcePath =
|
81
|
+
const sourcePath = (
|
82
|
+
resolved.path ? [moduleId, resolved.path].join(".") : moduleId
|
83
|
+
) as SourcePath;
|
83
84
|
return {
|
84
85
|
path: sourcePath,
|
85
86
|
schema:
|
86
|
-
resolved.schema instanceof Schema
|
87
|
+
resolved.schema instanceof Schema
|
87
88
|
? resolved.schema.serialize()
|
88
89
|
: resolved.schema,
|
89
90
|
source: resolved.source,
|
package/src/ValQuickJSRuntime.ts
CHANGED
@@ -34,6 +34,18 @@ export async function newValQuickJSRuntime(
|
|
34
34
|
"export const useVal = () => { throw Error(`Cannot use 'useVal' in this type of file`) }; export const fetchVal = () => { throw Error(`Cannot use 'fetchVal' in this type of file`) }; export const autoTagJSX = () => { /* ignore */ };",
|
35
35
|
};
|
36
36
|
}
|
37
|
+
if (modulePath.startsWith("next")) {
|
38
|
+
return {
|
39
|
+
value:
|
40
|
+
"export default new Proxy({}, { get() { return () => { throw new Error(`Cannot import 'next' in this file`) } } } )",
|
41
|
+
};
|
42
|
+
}
|
43
|
+
if (modulePath.startsWith("react")) {
|
44
|
+
return {
|
45
|
+
value:
|
46
|
+
"export default new Proxy({}, { get() { return () => { throw new Error(`Cannot import 'react' in this file`) } } } )",
|
47
|
+
};
|
48
|
+
}
|
37
49
|
return { value: moduleLoader.getModule(modulePath) };
|
38
50
|
} catch (e) {
|
39
51
|
return {
|
@@ -49,6 +61,12 @@ export async function newValQuickJSRuntime(
|
|
49
61
|
if (requestedName === "@valbuild/react/stega") {
|
50
62
|
return { value: requestedName };
|
51
63
|
}
|
64
|
+
if (requestedName.startsWith("next")) {
|
65
|
+
return { value: requestedName };
|
66
|
+
}
|
67
|
+
if (requestedName.startsWith("react")) {
|
68
|
+
return { value: requestedName };
|
69
|
+
}
|
52
70
|
const modulePath = moduleLoader.resolveModulePath(
|
53
71
|
baseModuleName,
|
54
72
|
requestedName
|
package/src/ValServer.ts
CHANGED
@@ -9,17 +9,7 @@ export interface ValServer {
|
|
9
9
|
|
10
10
|
session(req: express.Request, res: express.Response): Promise<void>;
|
11
11
|
|
12
|
-
|
13
|
-
req: express.Request<{ 0: string }>,
|
14
|
-
res: express.Response
|
15
|
-
): Promise<void>;
|
16
|
-
|
17
|
-
getAllModules(
|
18
|
-
req: express.Request<{ 0: string }>,
|
19
|
-
res: express.Response
|
20
|
-
): Promise<void>;
|
21
|
-
|
22
|
-
patchIds(
|
12
|
+
postPatches(
|
23
13
|
req: express.Request<{ 0: string }>,
|
24
14
|
res: express.Response
|
25
15
|
): Promise<void>;
|
@@ -27,4 +17,7 @@ export interface ValServer {
|
|
27
17
|
commit(req: express.Request, res: express.Response): Promise<void>;
|
28
18
|
|
29
19
|
enable(req: express.Request, res: express.Response): Promise<void>;
|
20
|
+
disable(req: express.Request, res: express.Response): Promise<void>;
|
21
|
+
|
22
|
+
getTree(req: express.Request, res: express.Response): Promise<void>;
|
30
23
|
}
|
@@ -10,17 +10,17 @@ export function createRequestHandler(valServer: ValServer): RequestHandler {
|
|
10
10
|
router.get("/authorize", valServer.authorize.bind(valServer));
|
11
11
|
router.get("/callback", valServer.callback.bind(valServer));
|
12
12
|
router.get("/logout", valServer.logout.bind(valServer));
|
13
|
-
router.
|
14
|
-
|
15
|
-
router.patch<{ 0: string }>(
|
16
|
-
"/ids/*",
|
13
|
+
router.post<{ 0: string }>(
|
14
|
+
"/patches/*",
|
17
15
|
express.json({
|
18
|
-
type: "application/json
|
16
|
+
type: "application/json",
|
19
17
|
limit: "10mb",
|
20
18
|
}),
|
21
|
-
valServer.
|
19
|
+
valServer.postPatches.bind(valServer)
|
22
20
|
);
|
23
21
|
router.post("/commit", valServer.commit.bind(valServer));
|
24
22
|
router.get("/enable", valServer.enable.bind(valServer));
|
23
|
+
router.get("/disable", valServer.disable.bind(valServer));
|
24
|
+
router.get("/tree/*", valServer.getTree.bind(valServer));
|
25
25
|
return router;
|
26
26
|
}
|
package/src/hosting.ts
CHANGED
@@ -5,6 +5,8 @@ import { createRequestHandler } from "./createRequestHandler";
|
|
5
5
|
import { ValServer } from "./ValServer";
|
6
6
|
import { LocalValServer, LocalValServerOptions } from "./LocalValServer";
|
7
7
|
import { ProxyValServer, ProxyValServerOptions } from "./ProxyValServer";
|
8
|
+
import { promises as fs } from "fs";
|
9
|
+
import * as path from "path";
|
8
10
|
|
9
11
|
type Opts = ValServerOverrides & ServiceOptions;
|
10
12
|
|
@@ -73,6 +75,16 @@ type ValServerOverrides = Partial<{
|
|
73
75
|
* @example "https://app.val.build"
|
74
76
|
*/
|
75
77
|
valBuildUrl: string;
|
78
|
+
/**
|
79
|
+
* The base url of Val content.
|
80
|
+
*
|
81
|
+
* Typically this should not be set.
|
82
|
+
*
|
83
|
+
* Can also be overridden using the VAL_CONTENT_URL env var.
|
84
|
+
*
|
85
|
+
* @example "https://content.val.build"
|
86
|
+
*/
|
87
|
+
valContentUrl: string;
|
76
88
|
/**
|
77
89
|
* The full name of this Val project.
|
78
90
|
*
|
@@ -81,6 +93,26 @@ type ValServerOverrides = Partial<{
|
|
81
93
|
* @example "myorg/my-project"
|
82
94
|
*/
|
83
95
|
valName: string;
|
96
|
+
/**
|
97
|
+
* After Val is enabled, redirect to this url.
|
98
|
+
*
|
99
|
+
* May be used to setup a custom flow after enabling Val.
|
100
|
+
*
|
101
|
+
*This can be set using the VAL_ENABLE_REDIRECT_URL env var.
|
102
|
+
*
|
103
|
+
* @example "/api/draft/enable"
|
104
|
+
*/
|
105
|
+
valEnableRedirectUrl?: string;
|
106
|
+
/**
|
107
|
+
* After Val is disabled, redirect to this url.
|
108
|
+
*
|
109
|
+
* May be used to setup a custom flow after disabling Val.
|
110
|
+
*
|
111
|
+
* This can be set using the VAL_DISABLE_REDIRECT_URL env var.
|
112
|
+
*
|
113
|
+
* @example "/api/draft/enable"
|
114
|
+
*/
|
115
|
+
valDisableRedirectUrl?: string;
|
84
116
|
}>;
|
85
117
|
|
86
118
|
async function _createRequestListener(
|
@@ -119,6 +151,10 @@ async function initHandlerOptions(
|
|
119
151
|
"VAL_API_KEY and VAL_SECRET env vars must both be set in proxy mode"
|
120
152
|
);
|
121
153
|
}
|
154
|
+
const valContentUrl =
|
155
|
+
opts.valContentUrl ||
|
156
|
+
process.env.VAL_CONTENT_URL ||
|
157
|
+
"https://content.val.build";
|
122
158
|
const maybeGitCommit = opts.gitCommit || process.env.VAL_GIT_COMMIT;
|
123
159
|
if (!maybeGitCommit) {
|
124
160
|
throw new Error("VAL_GIT_COMMIT env var must be set in proxy mode");
|
@@ -137,19 +173,104 @@ async function initHandlerOptions(
|
|
137
173
|
apiKey: maybeApiKey,
|
138
174
|
valSecret: maybeValSecret,
|
139
175
|
valBuildUrl,
|
176
|
+
valContentUrl,
|
140
177
|
gitCommit: maybeGitCommit,
|
141
178
|
gitBranch: maybeGitBranch,
|
142
179
|
valName: maybeValName,
|
180
|
+
valEnableRedirectUrl:
|
181
|
+
opts.valEnableRedirectUrl || process.env.VAL_ENABLE_REDIRECT_URL,
|
182
|
+
valDisableRedirectUrl:
|
183
|
+
opts.valDisableRedirectUrl || process.env.VAL_DISABLE_REDIRECT_URL,
|
143
184
|
};
|
144
185
|
} else {
|
145
|
-
const
|
186
|
+
const cwd = process.cwd();
|
187
|
+
const service = await createService(cwd, opts);
|
188
|
+
const git = await safeReadGit(cwd);
|
146
189
|
return {
|
147
190
|
mode: "local",
|
148
191
|
service,
|
192
|
+
git: {
|
193
|
+
commit: process.env.VAL_GIT_COMMIT || git.commit,
|
194
|
+
branch: process.env.VAL_GIT_BRANCH || git.branch,
|
195
|
+
},
|
149
196
|
};
|
150
197
|
}
|
151
198
|
}
|
152
199
|
|
200
|
+
export async function safeReadGit(
|
201
|
+
cwd: string
|
202
|
+
): Promise<{ commit?: string; branch?: string }> {
|
203
|
+
async function findGitHead(
|
204
|
+
currentDir: string,
|
205
|
+
depth: number
|
206
|
+
): Promise<{ commit?: string; branch?: string }> {
|
207
|
+
const gitHeadPath = path.join(currentDir, ".git", "HEAD");
|
208
|
+
if (depth > 1000) {
|
209
|
+
console.error(
|
210
|
+
`Reached max depth while scanning for .git folder. Current working dir: ${cwd}.`
|
211
|
+
);
|
212
|
+
return {
|
213
|
+
commit: undefined,
|
214
|
+
branch: undefined,
|
215
|
+
};
|
216
|
+
}
|
217
|
+
|
218
|
+
try {
|
219
|
+
const headContents = await fs.readFile(gitHeadPath, "utf-8");
|
220
|
+
const match = headContents.match(/^ref: refs\/heads\/(.+)/);
|
221
|
+
if (match) {
|
222
|
+
const branchName = match[1];
|
223
|
+
return {
|
224
|
+
branch: branchName,
|
225
|
+
commit: await readCommit(currentDir, branchName),
|
226
|
+
};
|
227
|
+
} else {
|
228
|
+
return {
|
229
|
+
commit: undefined,
|
230
|
+
branch: undefined,
|
231
|
+
};
|
232
|
+
}
|
233
|
+
} catch (error) {
|
234
|
+
const parentDir = path.dirname(currentDir);
|
235
|
+
|
236
|
+
// We've reached the root directory
|
237
|
+
if (parentDir === currentDir) {
|
238
|
+
return {
|
239
|
+
commit: undefined,
|
240
|
+
branch: undefined,
|
241
|
+
};
|
242
|
+
}
|
243
|
+
return findGitHead(parentDir, depth + 1);
|
244
|
+
}
|
245
|
+
}
|
246
|
+
|
247
|
+
try {
|
248
|
+
return findGitHead(cwd, 0);
|
249
|
+
} catch (err) {
|
250
|
+
console.error("Error while reading .git", err);
|
251
|
+
return {
|
252
|
+
commit: undefined,
|
253
|
+
branch: undefined,
|
254
|
+
};
|
255
|
+
}
|
256
|
+
}
|
257
|
+
|
258
|
+
async function readCommit(
|
259
|
+
gitDir: string,
|
260
|
+
branchName: string
|
261
|
+
): Promise<string | undefined> {
|
262
|
+
try {
|
263
|
+
return (
|
264
|
+
await fs.readFile(
|
265
|
+
path.join(gitDir, ".git", "refs", "heads", branchName),
|
266
|
+
"utf-8"
|
267
|
+
)
|
268
|
+
).trim();
|
269
|
+
} catch (err) {
|
270
|
+
return undefined;
|
271
|
+
}
|
272
|
+
}
|
273
|
+
|
153
274
|
// TODO: rename to createValApiHandlers?
|
154
275
|
export function createRequestListener(
|
155
276
|
route: string,
|
package/src/index.ts
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
export type { ServiceOptions } from "./Service";
|
2
2
|
export { createService, Service } from "./Service";
|
3
3
|
export { createRequestHandler } from "./createRequestHandler";
|
4
|
-
export { createRequestListener } from "./hosting";
|
4
|
+
export { createRequestListener, safeReadGit } from "./hosting";
|
5
5
|
export { ValModuleLoader } from "./ValModuleLoader";
|
6
6
|
export { getCompilerOptions } from "./getCompilerOptions";
|
7
7
|
export { ValSourceFileHandler } from "./ValSourceFileHandler";
|
package/src/patch/ts/ops.test.ts
CHANGED
package/src/readValFile.ts
CHANGED
@@ -16,10 +16,10 @@ import { Internal } from "@valbuild/core";
|
|
16
16
|
globalThis.valModule = {
|
17
17
|
id: valModule?.default && Internal.getValPath(valModule?.default),
|
18
18
|
schema: valModule?.default && Internal.getSchema(valModule?.default)?.serialize(),
|
19
|
-
source: valModule?.default && Internal.
|
19
|
+
source: valModule?.default && Internal.getSource(valModule?.default),
|
20
20
|
validation: valModule?.default && Internal.getSchema(valModule?.default)?.validate(
|
21
21
|
valModule?.default && Internal.getValPath(valModule?.default) || "/",
|
22
|
-
valModule?.default && Internal.
|
22
|
+
valModule?.default && Internal.getSource(valModule?.default)
|
23
23
|
)
|
24
24
|
};
|
25
25
|
`;
|