veryfront 0.1.34 → 0.1.36

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/esm/deno.js CHANGED
@@ -1,6 +1,6 @@
1
1
  export default {
2
2
  "name": "veryfront",
3
- "version": "0.1.34",
3
+ "version": "0.1.36",
4
4
  "license": "Apache-2.0",
5
5
  "nodeModulesDir": "auto",
6
6
  "exclude": [
@@ -7,6 +7,8 @@ export interface EnvironmentConfig {
7
7
  denoTesting: boolean;
8
8
  perfEnabled: boolean;
9
9
  apiBaseUrl: string;
10
+ /** Public-facing API URL for browser-injected scripts (WebSocket URLs, etc.). */
11
+ publicApiBaseUrl: string;
10
12
  apiUrl: string | undefined;
11
13
  apiToken: string | undefined;
12
14
  projectSlug: string | undefined;
@@ -1 +1 @@
1
- {"version":3,"file":"environment-config.d.ts","sourceRoot":"","sources":["../../../src/src/config/environment-config.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,aAAa,GAAG,YAAY,GAAG,MAAM,GAAG,MAAM,CAAC;IACxD,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IAEtB,KAAK,EAAE,OAAO,CAAC;IACf,EAAE,EAAE,OAAO,CAAC;IACZ,WAAW,EAAE,OAAO,CAAC;IACrB,WAAW,EAAE,OAAO,CAAC;IAErB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B,WAAW,EAAE,MAAM,GAAG,SAAS,CAAC;IAEhC,OAAO,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,aAAa,EAAE,MAAM,GAAG,SAAS,CAAC;IAElC,qBAAqB,EAAE,OAAO,CAAC;IAC/B,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,OAAO,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,cAAc,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,aAAa,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,eAAe,EAAE,MAAM,GAAG,SAAS,CAAC;IACpC,GAAG,EAAE,MAAM,GAAG,SAAS,CAAC;IAExB,eAAe,EAAE,OAAO,CAAC;IAEzB,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B,kBAAkB,EAAE,OAAO,CAAC;IAE5B,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAE3B,IAAI,EAAE,MAAM,CAAC;IACb,gBAAgB,EAAE,MAAM,GAAG,SAAS,CAAC;IACrC,kBAAkB,EAAE,MAAM,GAAG,SAAS,CAAC;IACvC,0BAA0B,EAAE,MAAM,CAAC;IAEnC,WAAW,EAAE,OAAO,CAAC;IACrB,eAAe,EAAE,MAAM,GAAG,SAAS,CAAC;IACpC,YAAY,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,kBAAkB,EAAE,MAAM,GAAG,SAAS,CAAC;IACvC,mBAAmB,EAAE,MAAM,GAAG,SAAS,CAAC;IACxC,kBAAkB,EAAE,MAAM,GAAG,SAAS,CAAC;IACvC,mBAAmB,EAAE,MAAM,GAAG,SAAS,CAAC;IACxC,WAAW,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,kBAAkB,EAAE,OAAO,CAAC;IAE5B,YAAY,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,aAAa,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,eAAe,EAAE,MAAM,GAAG,SAAS,CAAC;IACpC,gBAAgB,EAAE,MAAM,GAAG,SAAS,CAAC;IACrC,YAAY,EAAE,MAAM,GAAG,SAAS,CAAC;IAEjC,WAAW,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,WAAW,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,UAAU,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC;IAE9B,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,EAAE,OAAO,CAAC;IAEpB,WAAW,EAAE,MAAM,CAAC;IACpB,iBAAiB,EAAE,MAAM,GAAG,SAAS,CAAC;IAEtC,gBAAgB,EAAE,MAAM,GAAG,SAAS,CAAC;CACtC;AA4GD,wBAAgB,qBAAqB,IAAI,iBAAiB,CAWzD;AAED,wBAAgB,wBAAwB,IAAI,iBAAiB,CAI5D;AAgBD,wBAAgB,oBAAoB,IAAI,iBAAiB,CAgBxD;AAED,wBAAgB,8BAA8B,IAAI,OAAO,CAExD;AAED,wBAAgB,2BAA2B,CACzC,SAAS,GAAE,OAAO,CAAC,iBAAiB,CAAM,GACzC,iBAAiB,CAWnB;AAED,wBAAgB,+BAA+B,CAAC,GAAG,EAAE,OAAO,CAAC,iBAAiB,CAAC,GAAG,IAAI,CAGrF;AAED,wBAAgB,uBAAuB,IAAI,IAAI,CAI9C"}
1
+ {"version":3,"file":"environment-config.d.ts","sourceRoot":"","sources":["../../../src/src/config/environment-config.ts"],"names":[],"mappings":"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"}
@@ -34,6 +34,8 @@ function readEnvSnapshot() {
34
34
  apiBaseUrl: getEnv("VERYFRONT_API_BASE_URL") ||
35
35
  apiUrl?.replace("/graphql", "/api") ||
36
36
  DEFAULTS.apiBaseUrl,
37
+ publicApiBaseUrl: getEnv("VERYFRONT_PUBLIC_API_BASE_URL") ||
38
+ DEFAULTS.apiBaseUrl,
37
39
  apiUrl,
38
40
  apiToken: getEnv("VERYFRONT_API_TOKEN") || undefined,
39
41
  projectSlug: getEnv("VERYFRONT_PROJECT_SLUG") || undefined,
@@ -26,8 +26,8 @@ export interface MarkdownHtmlOptions {
26
26
  filePath: string;
27
27
  /** Branch ID for Yjs room GUID computation. */
28
28
  branchId?: string | null;
29
- /** Request host for computing the WebSocket URL. */
30
- requestHost?: string;
29
+ /** API base URL for computing the WebSocket URL (e.g. "https://api.veryfront.com"). */
30
+ apiBaseUrl?: string;
31
31
  }
32
32
  /**
33
33
  * Generate a complete HTML document for markdown preview rendering.
@@ -1 +1 @@
1
- {"version":3,"file":"markdown-html-generator.d.ts","sourceRoot":"","sources":["../../../../../src/src/server/handlers/preview/markdown-html-generator.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,KAAK,OAAO,MAAM,2BAA2B,CAAC;AAMrD,oDAAoD;AACpD,MAAM,WAAW,mBAAmB;IAClC,wDAAwD;IACxD,OAAO,EAAE,MAAM,CAAC;IAChB,kDAAkD;IAClD,KAAK,EAAE,MAAM,CAAC;IACd,yCAAyC;IACzC,WAAW,EAAE,MAAM,CAAC;IACpB,iDAAiD;IACjD,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC;IACzB,gDAAgD;IAChD,GAAG,EAAE,GAAG,CAAC;IACT,gDAAgD;IAChD,SAAS,EAAE,MAAM,CAAC;IAClB,sCAAsC;IACtC,QAAQ,EAAE,MAAM,CAAC;IACjB,+CAA+C;IAC/C,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,oDAAoD;IACpD,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAoED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,mBAAmB,GAAG,MAAM,CA2FzE"}
1
+ {"version":3,"file":"markdown-html-generator.d.ts","sourceRoot":"","sources":["../../../../../src/src/server/handlers/preview/markdown-html-generator.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,KAAK,OAAO,MAAM,2BAA2B,CAAC;AAMrD,oDAAoD;AACpD,MAAM,WAAW,mBAAmB;IAClC,wDAAwD;IACxD,OAAO,EAAE,MAAM,CAAC;IAChB,kDAAkD;IAClD,KAAK,EAAE,MAAM,CAAC;IACd,yCAAyC;IACzC,WAAW,EAAE,MAAM,CAAC;IACpB,iDAAiD;IACjD,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC;IACzB,gDAAgD;IAChD,GAAG,EAAE,GAAG,CAAC;IACT,gDAAgD;IAChD,SAAS,EAAE,MAAM,CAAC;IAClB,sCAAsC;IACtC,QAAQ,EAAE,MAAM,CAAC;IACjB,+CAA+C;IAC/C,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,uFAAuF;IACvF,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAuED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,mBAAmB,GAAG,MAAM,CA2FzE"}
@@ -25,7 +25,7 @@ function detectTheme(req, url) {
25
25
  * Injected when embedded in Studio (`studio_embed=true`) or for standalone
26
26
  * markdown/MDX pages so the edit button and editor features are available.
27
27
  */
28
- function buildStudioScript(req, url, projectId, filePath, branchId, requestHost) {
28
+ function buildStudioScript(url, projectId, filePath, branchId, apiBaseUrl) {
29
29
  const studioEmbed = url.searchParams.get("studio_embed") === "true";
30
30
  const isMarkdown = /\.mdx?$/i.test(filePath);
31
31
  if (!studioEmbed && !isMarkdown)
@@ -36,13 +36,18 @@ function buildStudioScript(req, url, projectId, filePath, branchId, requestHost)
36
36
  const queryFileId = url.searchParams.get("vf_file_id")?.trim() || "";
37
37
  const canonicalProjectId = queryProjectId || projectId;
38
38
  const canonicalPageId = queryFileId || filePath;
39
- // Compute Yjs connection config for the bridge to self-connect
40
- // Use x-forwarded-proto to detect the public-facing protocol (internal K8s URL is always http)
41
- const forwardedProto = req.headers.get("x-forwarded-proto")?.split(",")[0]?.trim();
42
- const isSecure = forwardedProto === "https" || url.protocol === "https:";
43
- const wsProtocol = isSecure ? "wss" : "ws";
44
- const host = requestHost || url.host;
45
- const wsUrl = `${wsProtocol}://${host}/api/ws/${canonicalProjectId}/yjs`;
39
+ // Compute Yjs WebSocket URL from the API base URL (Yjs endpoint lives on the API server)
40
+ let wsUrl = "";
41
+ if (apiBaseUrl) {
42
+ try {
43
+ const apiUrl = new URL(apiBaseUrl);
44
+ const wsProtocol = apiUrl.protocol === "https:" ? "wss:" : "ws:";
45
+ wsUrl = `${wsProtocol}//${apiUrl.host}/api/ws/${canonicalProjectId}/yjs`;
46
+ }
47
+ catch {
48
+ // Invalid API URL — wsUrl stays empty, bridge won't self-connect
49
+ }
50
+ }
46
51
  const yjsGuid = branchId ? `${canonicalProjectId}:${branchId}` : canonicalProjectId;
47
52
  return `<script>${generateStudioBridgeScript({
48
53
  projectId: canonicalProjectId,
@@ -60,9 +65,9 @@ function buildStudioScript(req, url, projectId, filePath, branchId, requestHost)
60
65
  * studio bridge integration.
61
66
  */
62
67
  export function generateMarkdownHtml(options) {
63
- const { rawHtml, title, description, request, url, projectId, filePath, branchId, requestHost } = options;
68
+ const { rawHtml, title, description, request, url, projectId, filePath, branchId, apiBaseUrl } = options;
64
69
  const theme = detectTheme(request, url);
65
- const studioScript = buildStudioScript(request, url, projectId, filePath, branchId, requestHost);
70
+ const studioScript = buildStudioScript(url, projectId, filePath, branchId, apiBaseUrl);
66
71
  const themeAttrs = theme ? ` data-theme="${theme}" style="color-scheme: ${theme};"` : "";
67
72
  return `<!DOCTYPE html>
68
73
  <html lang="en"${themeAttrs}>
@@ -1 +1 @@
1
- {"version":3,"file":"markdown-preview.handler.d.ts","sourceRoot":"","sources":["../../../../../src/src/server/handlers/preview/markdown-preview.handler.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,OAAO,MAAM,2BAA2B,CAAC;AAGrD,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAmB,aAAa,EAAE,MAAM,aAAa,CAAC;AAgBnG,qBAAa,sBAAuB,SAAQ,WAAW;IACrD,QAAQ,EAAE,eAAe,CAKvB;IAEI,MAAM,CAAC,GAAG,EAAE,OAAO,CAAC,OAAO,EAAE,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC;YAiEjE,cAAc;CAmF7B"}
1
+ {"version":3,"file":"markdown-preview.handler.d.ts","sourceRoot":"","sources":["../../../../../src/src/server/handlers/preview/markdown-preview.handler.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,OAAO,MAAM,2BAA2B,CAAC;AAGrD,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAmB,aAAa,EAAE,MAAM,aAAa,CAAC;AAiBnG,qBAAa,sBAAuB,SAAQ,WAAW;IACrD,QAAQ,EAAE,eAAe,CAKvB;IAEI,MAAM,CAAC,GAAG,EAAE,OAAO,CAAC,OAAO,EAAE,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC;YAiEjE,cAAc;CAkF7B"}
@@ -7,6 +7,7 @@ import { isExtendedFSAdapter } from "../../../platform/adapters/fs/wrapper.js";
7
7
  import { getEnv } from "../../../platform/compat/process.js";
8
8
  import { tryNotFoundFallback } from "../request/ssr/not-found-fallback.js";
9
9
  import { generateMarkdownHtml } from "./markdown-html-generator.js";
10
+ import { getEnvironmentConfig } from "../../../config/environment-config.js";
10
11
  import { validatePathSync } from "../../../security/index.js";
11
12
  const logger = serverLogger.component("markdown-preview-handler");
12
13
  // Priority 900: between MEDIUM (600) and LOW/SSR (1000)
@@ -109,8 +110,7 @@ export class MarkdownPreviewHandler extends BaseHandler {
109
110
  projectId: ctx.projectSlug || ctx.projectId || "markdown-preview",
110
111
  filePath,
111
112
  branchId: ctx.parsedDomain?.branch ?? null,
112
- requestHost: req.headers.get("x-forwarded-host")?.split(",")[0]?.trim() ||
113
- req.headers.get("host") || url.host,
113
+ apiBaseUrl: getEnvironmentConfig().publicApiBaseUrl,
114
114
  });
115
115
  const responseBuilder = this.createResponseBuilder(ctx)
116
116
  .withCache("no-cache")
@@ -3959,7 +3959,7 @@ export function generateStudioBridgeScript(options) {
3959
3959
  setupMarkdownYjsConnection({
3960
3960
  wsUrl: WS_URL,
3961
3961
  guid: YJS_GUID,
3962
- fileId: markdownFileId
3962
+ fileId: markdownFileId,
3963
3963
  });
3964
3964
  }
3965
3965
  } else {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "veryfront",
3
- "version": "0.1.34",
3
+ "version": "0.1.36",
4
4
  "description": "The simplest way to build AI-powered apps",
5
5
  "keywords": [
6
6
  "react",
package/src/deno.js CHANGED
@@ -1,6 +1,6 @@
1
1
  export default {
2
2
  "name": "veryfront",
3
- "version": "0.1.34",
3
+ "version": "0.1.36",
4
4
  "license": "Apache-2.0",
5
5
  "nodeModulesDir": "auto",
6
6
  "exclude": [
@@ -14,6 +14,8 @@ export interface EnvironmentConfig {
14
14
  perfEnabled: boolean;
15
15
 
16
16
  apiBaseUrl: string;
17
+ /** Public-facing API URL for browser-injected scripts (WebSocket URLs, etc.). */
18
+ publicApiBaseUrl: string;
17
19
  apiUrl: string | undefined;
18
20
  apiToken: string | undefined;
19
21
  projectSlug: string | undefined;
@@ -113,6 +115,8 @@ function readEnvSnapshot(): EnvironmentConfig {
113
115
  apiBaseUrl: getEnv("VERYFRONT_API_BASE_URL") ||
114
116
  apiUrl?.replace("/graphql", "/api") ||
115
117
  DEFAULTS.apiBaseUrl,
118
+ publicApiBaseUrl: getEnv("VERYFRONT_PUBLIC_API_BASE_URL") ||
119
+ DEFAULTS.apiBaseUrl,
116
120
  apiUrl,
117
121
  apiToken: getEnv("VERYFRONT_API_TOKEN") || undefined,
118
122
  projectSlug: getEnv("VERYFRONT_PROJECT_SLUG") || undefined,
@@ -31,8 +31,8 @@ export interface MarkdownHtmlOptions {
31
31
  filePath: string;
32
32
  /** Branch ID for Yjs room GUID computation. */
33
33
  branchId?: string | null;
34
- /** Request host for computing the WebSocket URL. */
35
- requestHost?: string;
34
+ /** API base URL for computing the WebSocket URL (e.g. "https://api.veryfront.com"). */
35
+ apiBaseUrl?: string;
36
36
  }
37
37
 
38
38
  /**
@@ -63,12 +63,11 @@ function detectTheme(req: dntShim.Request, url: URL): "light" | "dark" | null {
63
63
  * markdown/MDX pages so the edit button and editor features are available.
64
64
  */
65
65
  function buildStudioScript(
66
- req: dntShim.Request,
67
66
  url: URL,
68
67
  projectId: string,
69
68
  filePath: string,
70
69
  branchId?: string | null,
71
- requestHost?: string,
70
+ apiBaseUrl?: string,
72
71
  ): string {
73
72
  const studioEmbed = url.searchParams.get("studio_embed") === "true";
74
73
  const isMarkdown = /\.mdx?$/i.test(filePath);
@@ -81,13 +80,17 @@ function buildStudioScript(
81
80
  const canonicalProjectId = queryProjectId || projectId;
82
81
  const canonicalPageId = queryFileId || filePath;
83
82
 
84
- // Compute Yjs connection config for the bridge to self-connect
85
- // Use x-forwarded-proto to detect the public-facing protocol (internal K8s URL is always http)
86
- const forwardedProto = req.headers.get("x-forwarded-proto")?.split(",")[0]?.trim();
87
- const isSecure = forwardedProto === "https" || url.protocol === "https:";
88
- const wsProtocol = isSecure ? "wss" : "ws";
89
- const host = requestHost || url.host;
90
- const wsUrl = `${wsProtocol}://${host}/api/ws/${canonicalProjectId}/yjs`;
83
+ // Compute Yjs WebSocket URL from the API base URL (Yjs endpoint lives on the API server)
84
+ let wsUrl = "";
85
+ if (apiBaseUrl) {
86
+ try {
87
+ const apiUrl = new URL(apiBaseUrl);
88
+ const wsProtocol = apiUrl.protocol === "https:" ? "wss:" : "ws:";
89
+ wsUrl = `${wsProtocol}//${apiUrl.host}/api/ws/${canonicalProjectId}/yjs`;
90
+ } catch {
91
+ // Invalid API URL — wsUrl stays empty, bridge won't self-connect
92
+ }
93
+ }
91
94
  const yjsGuid = branchId ? `${canonicalProjectId}:${branchId}` : canonicalProjectId;
92
95
 
93
96
  return `<script>${
@@ -109,11 +112,11 @@ function buildStudioScript(
109
112
  * studio bridge integration.
110
113
  */
111
114
  export function generateMarkdownHtml(options: MarkdownHtmlOptions): string {
112
- const { rawHtml, title, description, request, url, projectId, filePath, branchId, requestHost } =
115
+ const { rawHtml, title, description, request, url, projectId, filePath, branchId, apiBaseUrl } =
113
116
  options;
114
117
 
115
118
  const theme = detectTheme(request, url);
116
- const studioScript = buildStudioScript(request, url, projectId, filePath, branchId, requestHost);
119
+ const studioScript = buildStudioScript(url, projectId, filePath, branchId, apiBaseUrl);
117
120
  const themeAttrs = theme ? ` data-theme="${theme}" style="color-scheme: ${theme};"` : "";
118
121
 
119
122
  return `<!DOCTYPE html>
@@ -19,6 +19,7 @@ import { isExtendedFSAdapter } from "../../../platform/adapters/fs/wrapper.js";
19
19
  import { getEnv } from "../../../platform/compat/process.js";
20
20
  import { tryNotFoundFallback } from "../request/ssr/not-found-fallback.js";
21
21
  import { generateMarkdownHtml } from "./markdown-html-generator.js";
22
+ import { getEnvironmentConfig } from "../../../config/environment-config.js";
22
23
  import { validatePathSync } from "../../../security/index.js";
23
24
 
24
25
  const logger = serverLogger.component("markdown-preview-handler");
@@ -160,8 +161,7 @@ export class MarkdownPreviewHandler extends BaseHandler {
160
161
  projectId: ctx.projectSlug || ctx.projectId || "markdown-preview",
161
162
  filePath,
162
163
  branchId: ctx.parsedDomain?.branch ?? null,
163
- requestHost: req.headers.get("x-forwarded-host")?.split(",")[0]?.trim() ||
164
- req.headers.get("host") || url.host,
164
+ apiBaseUrl: getEnvironmentConfig().publicApiBaseUrl,
165
165
  });
166
166
 
167
167
  const responseBuilder = this.createResponseBuilder(ctx)
@@ -3969,7 +3969,7 @@ export function generateStudioBridgeScript(options: StudioBridgeOptions): string
3969
3969
  setupMarkdownYjsConnection({
3970
3970
  wsUrl: WS_URL,
3971
3971
  guid: YJS_GUID,
3972
- fileId: markdownFileId
3972
+ fileId: markdownFileId,
3973
3973
  });
3974
3974
  }
3975
3975
  } else {