@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.
@@ -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 { Internal } from "@valbuild/core";
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 getIds(
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
- console.log(req.params);
29
- const path = getPathFromParams(req.params);
30
- const [moduleId, modulePath] = Internal.splitModuleIdAndModulePath(path);
31
- const valModule = await this.options.service.get(moduleId, modulePath);
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
- res.json(valModule);
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 patchIds(
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
- const valModule = await this.options.service.patch(id, patch.value);
59
- res.json(valModule);
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(401).send(err.message);
132
+ res.status(400).send({ message: err.message });
63
133
  } else {
64
134
  console.error(err);
65
- res
66
- .status(500)
67
- .send(err instanceof Error ? err.message : "Unknown error");
135
+ res.status(500).send({
136
+ message: err instanceof Error ? err.message : "Unknown error",
137
+ });
68
138
  }
69
139
  }
70
140
  }
@@ -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 = "val_session";
12
- const VAL_STATE_COOKIE = "val_state";
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 getIds(
136
- req: express.Request<{ 0: string }>,
137
- res: express.Response
138
- ): Promise<void> {
139
- return this.withAuth(req, res, async ({ token }) => {
140
- const id = getPathFromParams(req.params);
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
- `/api/val/modules/${encodeURIComponent(this.options.gitCommit)}${id}`,
143
- this.options.valBuildUrl
154
+ `/v1/tree/${this.options.valName}/heads/${this.options.gitBranch}/${req.params["0"]}/?${params}`,
155
+ this.options.valContentUrl
144
156
  );
145
- const fetchRes = await fetch(url, {
146
- headers: this.getAuthHeaders(token),
147
- });
148
- if (fetchRes.ok) {
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 patchIds(
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
- `/api/val/modules/${encodeURIComponent(this.options.gitCommit)}${id}`,
178
- this.options.valBuildUrl
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: "PATCH",
183
- headers: this.getAuthHeaders(token, "application/json-patch+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 = [moduleId, resolved.path].join(".") as 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<SelectorSource>
87
+ resolved.schema instanceof Schema
87
88
  ? resolved.schema.serialize()
88
89
  : resolved.schema,
89
90
  source: resolved.source,
@@ -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
- getIds(
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.get<{ 0: string }>("/ids/*", valServer.getIds.bind(valServer));
14
- router.patch<{ 0: string }>(
15
- "/ids/*",
13
+ router.post<{ 0: string }>(
14
+ "/patches/*",
16
15
  express.json({
17
- type: "application/json-patch+json",
16
+ type: "application/json",
18
17
  limit: "10mb",
19
18
  }),
20
- valServer.patchIds.bind(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 service = await createService(process.cwd(), opts);
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";
@@ -1,4 +1,3 @@
1
- import { describe, test, expect } from "@jest/globals";
2
1
  import ts from "typescript";
3
2
  import { TSOps } from "./ops";
4
3
  import { result, array, pipe } from "@valbuild/core/fp";
@@ -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
  };
@@ -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.getRawSource(valModule?.default),
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.getRawSource(valModule?.default)
22
+ valModule?.default && Internal.getSource(valModule?.default)
23
23
  )
24
24
  };
25
25
  `;