@valbuild/server 0.26.0 → 0.27.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.
Files changed (51) hide show
  1. package/package.json +7 -4
  2. package/.babelrc.json +0 -5
  3. package/CHANGELOG.md +0 -0
  4. package/jest.config.js +0 -4
  5. package/src/LocalValServer.ts +0 -167
  6. package/src/ProxyValServer.ts +0 -542
  7. package/src/SerializedModuleContent.ts +0 -36
  8. package/src/Service.ts +0 -126
  9. package/src/ValFS.ts +0 -22
  10. package/src/ValFSHost.ts +0 -66
  11. package/src/ValModuleLoader.test.ts +0 -75
  12. package/src/ValModuleLoader.ts +0 -158
  13. package/src/ValQuickJSRuntime.ts +0 -85
  14. package/src/ValServer.ts +0 -24
  15. package/src/ValSourceFileHandler.ts +0 -57
  16. package/src/createFixPatch.ts +0 -170
  17. package/src/createRequestHandler.ts +0 -27
  18. package/src/expressHelpers.ts +0 -5
  19. package/src/getCompilerOptions.ts +0 -50
  20. package/src/hosting.ts +0 -290
  21. package/src/index.ts +0 -16
  22. package/src/jwt.ts +0 -93
  23. package/src/patch/ts/ops.test.ts +0 -937
  24. package/src/patch/ts/ops.ts +0 -897
  25. package/src/patch/ts/syntax.ts +0 -371
  26. package/src/patch/ts/valModule.test.ts +0 -26
  27. package/src/patch/ts/valModule.ts +0 -110
  28. package/src/patch/validation.ts +0 -81
  29. package/src/patchValFile.ts +0 -110
  30. package/src/readValFile.test.ts +0 -49
  31. package/src/readValFile.ts +0 -96
  32. package/test/example-projects/basic-next-javascript/jsconfig.json +0 -8
  33. package/test/example-projects/basic-next-javascript/package.json +0 -23
  34. package/test/example-projects/basic-next-javascript/pages/blogs.val.js +0 -20
  35. package/test/example-projects/basic-next-javascript/val.config.js +0 -4
  36. package/test/example-projects/basic-next-src-typescript/package.json +0 -23
  37. package/test/example-projects/basic-next-src-typescript/src/pages/blogs.val.ts +0 -20
  38. package/test/example-projects/basic-next-src-typescript/src/val.config.ts +0 -5
  39. package/test/example-projects/basic-next-src-typescript/tsconfig.json +0 -24
  40. package/test/example-projects/basic-next-typescript/package.json +0 -23
  41. package/test/example-projects/basic-next-typescript/pages/blogs.val.ts +0 -20
  42. package/test/example-projects/basic-next-typescript/tsconfig.json +0 -25
  43. package/test/example-projects/basic-next-typescript/val.config.ts +0 -5
  44. package/test/example-projects/typescript-description-files/README.md +0 -2
  45. package/test/example-projects/typescript-description-files/jsconfig.json +0 -8
  46. package/test/example-projects/typescript-description-files/package.json +0 -23
  47. package/test/example-projects/typescript-description-files/pages/blogs.val.d.ts +0 -7
  48. package/test/example-projects/typescript-description-files/pages/blogs.val.js +0 -19
  49. package/test/example-projects/typescript-description-files/val.config.d.ts +0 -3
  50. package/test/example-projects/typescript-description-files/val.config.js +0 -5
  51. package/tsconfig.json +0 -12
@@ -1,50 +0,0 @@
1
- import path from "path";
2
- import ts from "typescript";
3
-
4
- export const getCompilerOptions = (
5
- rootDir: string,
6
- parseConfigHost: ts.ParseConfigHost
7
- ): ts.CompilerOptions => {
8
- const tsConfigPath = path.resolve(rootDir, "tsconfig.json");
9
- const jsConfigPath = path.resolve(rootDir, "jsconfig.json");
10
- let configFilePath: string;
11
- if (parseConfigHost.fileExists(jsConfigPath)) {
12
- configFilePath = jsConfigPath;
13
- } else if (parseConfigHost.fileExists(tsConfigPath)) {
14
- configFilePath = tsConfigPath;
15
- } else {
16
- throw Error(
17
- `Could not read config from: "${tsConfigPath}" nor "${jsConfigPath}". Root dir: "${rootDir}"`
18
- );
19
- }
20
- const { config, error } = ts.readConfigFile(
21
- configFilePath,
22
- parseConfigHost.readFile.bind(parseConfigHost)
23
- );
24
- if (error) {
25
- if (typeof error.messageText === "string") {
26
- throw Error(
27
- `Could not parse config file: ${configFilePath}. Error: ${error.messageText}`
28
- );
29
- }
30
- throw Error(
31
- `Could not parse config file: ${configFilePath}. Error: ${error.messageText.messageText}`
32
- );
33
- }
34
- const optionsOverrides = undefined;
35
- const parsedConfigFile = ts.parseJsonConfigFileContent(
36
- config,
37
- parseConfigHost,
38
- rootDir,
39
- optionsOverrides,
40
- configFilePath
41
- );
42
- if (parsedConfigFile.errors.length > 0) {
43
- throw Error(
44
- `Could not parse config file: ${configFilePath}. Errors: ${parsedConfigFile.errors
45
- .map((e) => e.messageText)
46
- .join("\n")}`
47
- );
48
- }
49
- return parsedConfigFile.options;
50
- };
package/src/hosting.ts DELETED
@@ -1,290 +0,0 @@
1
- import type { RequestListener } from "node:http";
2
- import express from "express";
3
- import { createService, ServiceOptions } from "./Service";
4
- import { createRequestHandler } from "./createRequestHandler";
5
- import { ValServer } from "./ValServer";
6
- import { LocalValServer, LocalValServerOptions } from "./LocalValServer";
7
- import { ProxyValServer, ProxyValServerOptions } from "./ProxyValServer";
8
- import { promises as fs } from "fs";
9
- import * as path from "path";
10
-
11
- type Opts = ValServerOverrides & ServiceOptions;
12
-
13
- type ValServerOverrides = Partial<{
14
- /**
15
- * Override the Val API key.
16
- *
17
- * Typically this is set using VAL_API_KEY env var.
18
- *
19
- * NOTE: if this is set you must also set valSecret or VAL_SECRET env var.
20
- */
21
- apiKey: string;
22
- /**
23
- * Override the Val session key.
24
- *
25
- * This can be any randomly generated string.
26
- * It will be used for authentication between the frontend and val api
27
- * endpoints in this app.
28
- *
29
- * Typically this is set using VAL_SECRET env var.
30
- *
31
- * NOTE: if this is set you must also set apiKey or VAL_API_KEY env var.
32
- */
33
- valSecret: string;
34
- /**
35
- * Override the default the mode of operation.
36
- *
37
- * Typically this should not be set.
38
- *
39
- * "local" means that changes will be written to the local filesystem,
40
- * which is what you want when developing locally.
41
- *
42
- * "proxy" means that changes will proxied to https://app.val.build
43
- * and eventually be committed in the Git repository.
44
- *
45
- * It will automatically be "proxy" if both VAL_API_KEY env var (or the apiKey property) and VAL_SECRET env var (or the valSecret property)
46
- * is set.
47
- *
48
- * If both is missing, it will default to "local".
49
- */
50
- mode: "proxy" | "local";
51
- /**
52
- * Current git commit.
53
- *
54
- * Required if mode is "proxy".
55
- *
56
- * @example "e83c5163316f89bfbde7d9ab23ca2e25604af290"
57
- */
58
- gitCommit: string;
59
-
60
- /**
61
- * Current git branch.
62
- *
63
- * Required if mode is "proxy".
64
- *
65
- * @example "main"
66
- */
67
- gitBranch: string;
68
- /**
69
- * The base url of Val.
70
- *
71
- * Typically this should not be set.
72
- *
73
- * Can also be overridden using the VAL_BUILD_URL env var.
74
- *
75
- * @example "https://app.val.build"
76
- */
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;
88
- /**
89
- * The full name of this Val project.
90
- *
91
- * Typically this is set using the VAL_NAME env var.
92
- *
93
- * @example "myorg/my-project"
94
- */
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;
116
- }>;
117
-
118
- async function _createRequestListener(
119
- route: string,
120
- opts: Opts
121
- ): Promise<RequestListener> {
122
- const serverOpts = await initHandlerOptions(route, opts);
123
- let valServer: ValServer;
124
- if (serverOpts.mode === "proxy") {
125
- valServer = new ProxyValServer(serverOpts);
126
- } else {
127
- valServer = new LocalValServer(serverOpts);
128
- }
129
- const reqHandler = createRequestHandler(valServer);
130
- return express().use(route, reqHandler);
131
- }
132
-
133
- type ValServerOptions =
134
- | ({ mode: "proxy" } & ProxyValServerOptions)
135
- | ({ mode: "local" } & LocalValServerOptions);
136
- async function initHandlerOptions(
137
- route: string,
138
- opts: ValServerOverrides & ServiceOptions
139
- ): Promise<ValServerOptions> {
140
- const maybeApiKey = opts.apiKey || process.env.VAL_API_KEY;
141
- const maybeValSecret = opts.valSecret || process.env.VAL_SECRET;
142
- const isProxyMode =
143
- opts.mode === "proxy" ||
144
- (opts.mode === undefined && (maybeApiKey || maybeValSecret));
145
-
146
- if (isProxyMode) {
147
- const valBuildUrl =
148
- opts.valBuildUrl || process.env.VAL_BUILD_URL || "https://app.val.build";
149
- if (!maybeApiKey || !maybeValSecret) {
150
- throw new Error(
151
- "VAL_API_KEY and VAL_SECRET env vars must both be set in proxy mode"
152
- );
153
- }
154
- const valContentUrl =
155
- opts.valContentUrl ||
156
- process.env.VAL_CONTENT_URL ||
157
- "https://content.val.build";
158
- const maybeGitCommit = opts.gitCommit || process.env.VAL_GIT_COMMIT;
159
- if (!maybeGitCommit) {
160
- throw new Error("VAL_GIT_COMMIT env var must be set in proxy mode");
161
- }
162
- const maybeGitBranch = opts.gitBranch || process.env.VAL_GIT_BRANCH;
163
- if (!maybeGitBranch) {
164
- throw new Error("VAL_GIT_BRANCH env var must be set in proxy mode");
165
- }
166
- const maybeValName = opts.gitBranch || process.env.VAL_NAME;
167
- if (!maybeValName) {
168
- throw new Error("VAL_NAME env var must be set in proxy mode");
169
- }
170
- return {
171
- mode: "proxy",
172
- route,
173
- apiKey: maybeApiKey,
174
- valSecret: maybeValSecret,
175
- valBuildUrl,
176
- valContentUrl,
177
- gitCommit: maybeGitCommit,
178
- gitBranch: maybeGitBranch,
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,
184
- };
185
- } else {
186
- const cwd = process.cwd();
187
- const service = await createService(cwd, opts);
188
- const git = await safeReadGit(cwd);
189
- return {
190
- mode: "local",
191
- service,
192
- git: {
193
- commit: process.env.VAL_GIT_COMMIT || git.commit,
194
- branch: process.env.VAL_GIT_BRANCH || git.branch,
195
- },
196
- };
197
- }
198
- }
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
-
274
- // TODO: rename to createValApiHandlers?
275
- export function createRequestListener(
276
- route: string,
277
- opts: Opts
278
- ): RequestListener {
279
- const handler = _createRequestListener(route, opts);
280
- return async (req, res) => {
281
- try {
282
- return (await handler)(req, res);
283
- } catch (e) {
284
- res.statusCode = 500;
285
- res.write(e instanceof Error ? e.message : "Unknown error");
286
- res.end();
287
- return;
288
- }
289
- };
290
- }
package/src/index.ts DELETED
@@ -1,16 +0,0 @@
1
- export type { ServiceOptions } from "./Service";
2
- export { createService, Service } from "./Service";
3
- export { createRequestHandler } from "./createRequestHandler";
4
- export { createRequestListener, safeReadGit } from "./hosting";
5
- export { ValModuleLoader } from "./ValModuleLoader";
6
- export { getCompilerOptions } from "./getCompilerOptions";
7
- export { ValSourceFileHandler } from "./ValSourceFileHandler";
8
- export { ValFSHost } from "./ValFSHost";
9
- export type { IValFSHost } from "./ValFSHost";
10
- export type { ValFS } from "./ValFS";
11
- export { patchSourceFile } from "./patchValFile";
12
- export { formatSyntaxErrorTree } from "./patch/ts/syntax";
13
- export { LocalValServer } from "./LocalValServer";
14
- export { createFixPatch } from "./createFixPatch";
15
- export { PatchJSON } from "./patch/validation";
16
- export * from "./jwt";
package/src/jwt.ts DELETED
@@ -1,93 +0,0 @@
1
- import crypto from "crypto";
2
- import { z } from "zod";
3
-
4
- export type JwtPayload = {
5
- sub: string;
6
- exp: number;
7
- org: string;
8
- project: string;
9
- };
10
-
11
- export function decodeJwt(
12
- token: string,
13
- secretKey?: string
14
- ): JwtPayload | null {
15
- const [headerBase64, payloadBase64, signatureBase64, ...rest] =
16
- token.split(".");
17
- if (!headerBase64 || !payloadBase64 || !signatureBase64 || rest.length > 0) {
18
- console.debug(
19
- "Invalid JWT: format is not exactly {header}.{payload}.{signature}",
20
- token
21
- );
22
- return null;
23
- }
24
-
25
- try {
26
- const parsedHeader = JSON.parse(
27
- Buffer.from(headerBase64, "base64").toString("utf8")
28
- ) as unknown;
29
- const headerVerification = JwtHeaderSchema.safeParse(parsedHeader);
30
- if (!headerVerification.success) {
31
- console.debug("Invalid JWT: invalid header", parsedHeader);
32
- return null;
33
- }
34
- if (headerVerification.data.typ !== jwtHeader.typ) {
35
- console.debug("Invalid JWT: invalid header typ", parsedHeader);
36
- return null;
37
- }
38
- if (headerVerification.data.alg !== jwtHeader.alg) {
39
- console.debug("Invalid JWT: invalid header alg", parsedHeader);
40
- return null;
41
- }
42
- } catch (err) {
43
- console.debug("Invalid JWT: could not parse header", err);
44
- return null;
45
- }
46
-
47
- if (secretKey) {
48
- const signature = crypto
49
- .createHmac("sha256", secretKey)
50
- .update(`${headerBase64}.${payloadBase64}`)
51
- .digest("base64");
52
- if (signature !== signatureBase64) {
53
- console.debug("Invalid JWT: invalid signature");
54
- return null;
55
- }
56
- }
57
- try {
58
- const parsedPayload = JSON.parse(
59
- Buffer.from(payloadBase64, "base64").toString("utf8")
60
- ) as JwtPayload;
61
- return parsedPayload;
62
- } catch (err) {
63
- console.debug("Invalid JWT: could not parse payload", err);
64
- return null;
65
- }
66
- }
67
-
68
- export function getExpire(): number {
69
- return Math.floor(Date.now() / 1000) + 60 * 60 * 24 * 4; // 4 days
70
- }
71
-
72
- const JwtHeaderSchema = z.object({
73
- alg: z.literal("HS256"),
74
- typ: z.literal("JWT"),
75
- });
76
- type JwtHeader = z.infer<typeof JwtHeaderSchema>;
77
- const jwtHeader: JwtHeader = {
78
- alg: "HS256",
79
- typ: "JWT",
80
- };
81
-
82
- const jwtHeaderBase64 = Buffer.from(JSON.stringify(jwtHeader)).toString(
83
- "base64"
84
- );
85
-
86
- export function encodeJwt(payload: object, sessionKey: string): string {
87
- // NOTE: this is only used for authentication, not for authorization (i.e. what a user can do) - this is handled when actually doing operations
88
- const payloadBase64 = Buffer.from(JSON.stringify(payload)).toString("base64");
89
- return `${jwtHeaderBase64}.${payloadBase64}.${crypto
90
- .createHmac("sha256", sessionKey)
91
- .update(`${jwtHeaderBase64}.${payloadBase64}`)
92
- .digest("base64")}`;
93
- }