@valbuild/server 0.16.3 → 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 +8 -4
- package/dist/declarations/src/ValServer.d.ts +4 -4
- package/dist/declarations/src/hosting.d.ts +34 -0
- package/dist/declarations/src/index.d.ts +1 -1
- package/dist/declarations/src/patchValFile.d.ts +1 -1
- package/dist/valbuild-server.cjs.dev.js +324 -116
- package/dist/valbuild-server.cjs.prod.js +324 -116
- package/dist/valbuild-server.esm.js +308 -118
- package/package.json +3 -3
- package/src/LocalValServer.ts +89 -19
- package/src/ProxyValServer.ts +86 -28
- package/src/Service.ts +4 -3
- package/src/ValQuickJSRuntime.ts +18 -0
- package/src/ValServer.ts +6 -6
- package/src/createRequestHandler.ts +7 -5
- package/src/hosting.ts +122 -1
- package/src/index.ts +1 -1
- package/src/patch/ts/ops.test.ts +0 -1
- package/src/patchValFile.ts +8 -1
- package/src/readValFile.ts +2 -2
package/src/LocalValServer.ts
CHANGED
|
@@ -1,14 +1,21 @@
|
|
|
1
1
|
import express from "express";
|
|
2
2
|
import { Service } from "./Service";
|
|
3
|
-
import { PatchJSON } from "./patch/validation";
|
|
4
3
|
import { result } from "@valbuild/core/fp";
|
|
5
4
|
import { parsePatch, PatchError } from "@valbuild/core/patch";
|
|
6
5
|
import { getPathFromParams } from "./expressHelpers";
|
|
6
|
+
import { PatchJSON } from "./patch/validation";
|
|
7
7
|
import { ValServer } from "./ValServer";
|
|
8
|
-
import {
|
|
8
|
+
import { ApiTreeResponse, ModuleId, ModulePath } from "@valbuild/core";
|
|
9
|
+
import { disable, enable } from "./ProxyValServer";
|
|
10
|
+
import { promises as fs } from "fs";
|
|
11
|
+
import path from "path";
|
|
9
12
|
|
|
10
13
|
export type LocalValServerOptions = {
|
|
11
14
|
service: Service;
|
|
15
|
+
git: {
|
|
16
|
+
commit?: string;
|
|
17
|
+
branch?: string;
|
|
18
|
+
};
|
|
12
19
|
};
|
|
13
20
|
|
|
14
21
|
export class LocalValServer implements ValServer {
|
|
@@ -20,29 +27,93 @@ export class LocalValServer implements ValServer {
|
|
|
20
27
|
});
|
|
21
28
|
}
|
|
22
29
|
|
|
23
|
-
async
|
|
24
|
-
req: express.Request<{ 0: string }>,
|
|
25
|
-
res: express.Response
|
|
26
|
-
): Promise<void> {
|
|
30
|
+
async getTree(req: express.Request, res: express.Response): Promise<void> {
|
|
27
31
|
try {
|
|
28
|
-
|
|
29
|
-
const
|
|
30
|
-
const
|
|
31
|
-
const
|
|
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
|
+
}
|
|
55
|
+
moduleIds.push(
|
|
56
|
+
path
|
|
57
|
+
.join(dir, file)
|
|
58
|
+
.replace(rootDir, "")
|
|
59
|
+
.replace(".val.js", "")
|
|
60
|
+
.replace(".val.ts", "")
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
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
|
+
});
|
|
32
75
|
|
|
33
|
-
|
|
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
|
+
})
|
|
86
|
+
);
|
|
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
|
+
});
|
|
34
94
|
} catch (err) {
|
|
35
95
|
console.error(err);
|
|
36
96
|
res.sendStatus(500);
|
|
37
97
|
}
|
|
38
98
|
}
|
|
39
99
|
|
|
40
|
-
async
|
|
100
|
+
async enable(req: express.Request, res: express.Response): Promise<void> {
|
|
101
|
+
return enable(req, res);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
async disable(req: express.Request, res: express.Response): Promise<void> {
|
|
105
|
+
return disable(req, res);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
async postPatches(
|
|
41
109
|
req: express.Request<{ 0: string }>,
|
|
42
110
|
res: express.Response
|
|
43
111
|
): Promise<void> {
|
|
112
|
+
const id = getPathFromParams(req.params)?.replace("/~", "");
|
|
113
|
+
|
|
44
114
|
// First validate that the body has the right structure
|
|
45
115
|
const patchJSON = PatchJSON.safeParse(req.body);
|
|
116
|
+
console.log("patch id", id, patchJSON);
|
|
46
117
|
if (!patchJSON.success) {
|
|
47
118
|
res.status(401).json(patchJSON.error.issues);
|
|
48
119
|
return;
|
|
@@ -53,18 +124,17 @@ export class LocalValServer implements ValServer {
|
|
|
53
124
|
res.status(401).json(patch.error);
|
|
54
125
|
return;
|
|
55
126
|
}
|
|
56
|
-
const id = getPathFromParams(req.params);
|
|
57
127
|
try {
|
|
58
|
-
|
|
59
|
-
res.json(
|
|
128
|
+
await this.options.service.patch(id, patch.value);
|
|
129
|
+
res.json({});
|
|
60
130
|
} catch (err) {
|
|
61
131
|
if (err instanceof PatchError) {
|
|
62
|
-
res.status(
|
|
132
|
+
res.status(400).send({ message: err.message });
|
|
63
133
|
} else {
|
|
64
134
|
console.error(err);
|
|
65
|
-
res
|
|
66
|
-
.
|
|
67
|
-
|
|
135
|
+
res.status(500).send({
|
|
136
|
+
message: err instanceof Error ? err.message : "Unknown error",
|
|
137
|
+
});
|
|
68
138
|
}
|
|
69
139
|
}
|
|
70
140
|
}
|
package/src/ProxyValServer.ts
CHANGED
|
@@ -3,22 +3,26 @@ 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";
|
|
9
|
+
import { Internal } from "@valbuild/core";
|
|
10
10
|
|
|
11
|
-
const VAL_SESSION_COOKIE =
|
|
12
|
-
const VAL_STATE_COOKIE =
|
|
11
|
+
const VAL_SESSION_COOKIE = Internal.VAL_SESSION_COOKIE;
|
|
12
|
+
const VAL_STATE_COOKIE = Internal.VAL_STATE_COOKIE;
|
|
13
|
+
const VAL_ENABLED_COOKIE = Internal.VAL_ENABLE_COOKIE_NAME;
|
|
13
14
|
|
|
14
15
|
export type ProxyValServerOptions = {
|
|
15
16
|
apiKey: string;
|
|
16
17
|
route: string;
|
|
17
18
|
valSecret: string;
|
|
18
19
|
valBuildUrl: string;
|
|
20
|
+
valContentUrl: string;
|
|
19
21
|
gitCommit: string;
|
|
20
22
|
gitBranch: string;
|
|
21
23
|
valName: string;
|
|
24
|
+
valEnableRedirectUrl?: string;
|
|
25
|
+
valDisableRedirectUrl?: string;
|
|
22
26
|
};
|
|
23
27
|
|
|
24
28
|
export class ProxyValServer implements ValServer {
|
|
@@ -47,6 +51,13 @@ export class ProxyValServer implements ValServer {
|
|
|
47
51
|
.redirect(appAuthorizeUrl);
|
|
48
52
|
}
|
|
49
53
|
|
|
54
|
+
async enable(req: express.Request, res: express.Response): Promise<void> {
|
|
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);
|
|
59
|
+
}
|
|
60
|
+
|
|
50
61
|
async callback(req: express.Request, res: express.Response): Promise<void> {
|
|
51
62
|
const { success: callbackReqSuccess, error: callbackReqError } =
|
|
52
63
|
verifyCallbackReq(req.cookies[VAL_STATE_COOKIE], req.query);
|
|
@@ -113,7 +124,6 @@ export class ProxyValServer implements ValServer {
|
|
|
113
124
|
}
|
|
114
125
|
|
|
115
126
|
async session(req: express.Request, res: express.Response): Promise<void> {
|
|
116
|
-
console.log("hit session");
|
|
117
127
|
return this.withAuth(req, res, async (data) => {
|
|
118
128
|
const url = new URL(
|
|
119
129
|
`/api/val/${this.options.valName}/auth/session`,
|
|
@@ -132,33 +142,37 @@ export class ProxyValServer implements ValServer {
|
|
|
132
142
|
});
|
|
133
143
|
}
|
|
134
144
|
|
|
135
|
-
async
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
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
|
+
});
|
|
141
153
|
const url = new URL(
|
|
142
|
-
`/
|
|
143
|
-
this.options.
|
|
154
|
+
`/v1/tree/${this.options.valName}/heads/${this.options.gitBranch}/${req.params["0"]}/?${params}`,
|
|
155
|
+
this.options.valContentUrl
|
|
144
156
|
);
|
|
145
|
-
const
|
|
146
|
-
headers: this.getAuthHeaders(token),
|
|
147
|
-
});
|
|
148
|
-
|
|
149
|
-
res.status(fetchRes.status).json(await fetchRes.json());
|
|
150
|
-
} else {
|
|
151
|
-
res.sendStatus(fetchRes.status);
|
|
152
|
-
}
|
|
153
|
-
}).catch((e) => {
|
|
154
|
-
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);
|
|
155
161
|
});
|
|
156
162
|
}
|
|
157
163
|
|
|
158
|
-
async
|
|
164
|
+
async postPatches(
|
|
159
165
|
req: express.Request<{ 0: string }>,
|
|
160
166
|
res: express.Response
|
|
161
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
|
+
});
|
|
162
176
|
this.withAuth(req, res, async ({ token }) => {
|
|
163
177
|
// First validate that the body has the right structure
|
|
164
178
|
const patchJSON = PatchJSON.safeParse(req.body);
|
|
@@ -172,15 +186,14 @@ export class ProxyValServer implements ValServer {
|
|
|
172
186
|
res.status(401).json(patch.error);
|
|
173
187
|
return;
|
|
174
188
|
}
|
|
175
|
-
const id = getPathFromParams(req.params);
|
|
176
189
|
const url = new URL(
|
|
177
|
-
`/
|
|
178
|
-
this.options.
|
|
190
|
+
`/v1/tree/${this.options.valName}/heads/${this.options.gitBranch}/${req.params["0"]}/?${params}`,
|
|
191
|
+
this.options.valContentUrl
|
|
179
192
|
);
|
|
180
193
|
// Proxy patch to val.build
|
|
181
194
|
const fetchRes = await fetch(url, {
|
|
182
|
-
method: "
|
|
183
|
-
headers: this.getAuthHeaders(token, "application/json
|
|
195
|
+
method: "POST",
|
|
196
|
+
headers: this.getAuthHeaders(token, "application/json"),
|
|
184
197
|
body: JSON.stringify(patch),
|
|
185
198
|
});
|
|
186
199
|
if (fetchRes.ok) {
|
|
@@ -387,6 +400,51 @@ function getStateFromCookie(stateCookie: string):
|
|
|
387
400
|
}
|
|
388
401
|
}
|
|
389
402
|
|
|
403
|
+
export async function enable(
|
|
404
|
+
req: express.Request,
|
|
405
|
+
res: express.Response,
|
|
406
|
+
redirectUrl?: string
|
|
407
|
+
): Promise<void> {
|
|
408
|
+
const { redirect_to } = req.query;
|
|
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
|
+
}
|
|
415
|
+
res
|
|
416
|
+
.cookie(VAL_ENABLED_COOKIE, "true", {
|
|
417
|
+
httpOnly: false,
|
|
418
|
+
sameSite: "lax",
|
|
419
|
+
})
|
|
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);
|
|
443
|
+
} else {
|
|
444
|
+
res.sendStatus(400);
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
|
|
390
448
|
function createStateCookie(state: StateCookie): string {
|
|
391
449
|
return Buffer.from(JSON.stringify(state), "utf8").toString("base64");
|
|
392
450
|
}
|
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,15 +9,15 @@ 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
|
-
patchIds(
|
|
12
|
+
postPatches(
|
|
18
13
|
req: express.Request<{ 0: string }>,
|
|
19
14
|
res: express.Response
|
|
20
15
|
): Promise<void>;
|
|
21
16
|
|
|
22
17
|
commit(req: express.Request, res: express.Response): Promise<void>;
|
|
18
|
+
|
|
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>;
|
|
23
23
|
}
|
|
@@ -10,15 +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
|
-
"/ids/*",
|
|
13
|
+
router.post<{ 0: string }>(
|
|
14
|
+
"/patches/*",
|
|
16
15
|
express.json({
|
|
17
|
-
type: "application/json
|
|
16
|
+
type: "application/json",
|
|
18
17
|
limit: "10mb",
|
|
19
18
|
}),
|
|
20
|
-
valServer.
|
|
19
|
+
valServer.postPatches.bind(valServer)
|
|
21
20
|
);
|
|
22
21
|
router.post("/commit", valServer.commit.bind(valServer));
|
|
22
|
+
router.get("/enable", valServer.enable.bind(valServer));
|
|
23
|
+
router.get("/disable", valServer.disable.bind(valServer));
|
|
24
|
+
router.get("/tree/*", valServer.getTree.bind(valServer));
|
|
23
25
|
return router;
|
|
24
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/patchValFile.ts
CHANGED
|
@@ -95,8 +95,15 @@ function convertDataUrlToBase64(dataUrl: string): Buffer {
|
|
|
95
95
|
}
|
|
96
96
|
|
|
97
97
|
export const patchSourceFile = (
|
|
98
|
-
sourceFile: ts.SourceFile,
|
|
98
|
+
sourceFile: ts.SourceFile | string,
|
|
99
99
|
patch: Patch
|
|
100
100
|
): result.Result<ts.SourceFile, ValSyntaxErrorTree | PatchError> => {
|
|
101
|
+
if (typeof sourceFile === "string") {
|
|
102
|
+
return applyPatch(
|
|
103
|
+
ts.createSourceFile("<val>", sourceFile, ts.ScriptTarget.ES2015),
|
|
104
|
+
ops,
|
|
105
|
+
patch
|
|
106
|
+
);
|
|
107
|
+
}
|
|
101
108
|
return applyPatch(sourceFile, ops, patch);
|
|
102
109
|
};
|
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
|
`;
|