veryfront 0.1.37 → 0.1.39

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.37",
3
+ "version": "0.1.39",
4
4
  "license": "Apache-2.0",
5
5
  "nodeModulesDir": "auto",
6
6
  "exclude": [
@@ -352,7 +352,8 @@ export default {
352
352
  ],
353
353
  "exclude": [
354
354
  "dist/",
355
- "coverage/"
355
+ "coverage/",
356
+ "src/studio/bridge/"
356
357
  ],
357
358
  "rules": {
358
359
  "tags": [
@@ -1 +1 @@
1
- {"version":3,"file":"dev-scripts.d.ts","sourceRoot":"","sources":["../../../src/src/html/dev-scripts.ts"],"names":[],"mappings":"AAAA,wBAAgB,YAAY,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CA+BnD;AAMD,wBAAgB,aAAa,CAAC,QAAQ,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAMvE;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAOnE;AAED,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,iEAAiE;IACjE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,8DAA8D;IAC9D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,6DAA6D;IAC7D,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,mBAAmB,GAAG,MAAM,CAiBrE"}
1
+ {"version":3,"file":"dev-scripts.d.ts","sourceRoot":"","sources":["../../../src/src/html/dev-scripts.ts"],"names":[],"mappings":"AAAA,wBAAgB,YAAY,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CA+BnD;AAMD,wBAAgB,aAAa,CAAC,QAAQ,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAMvE;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAOnE;AAED,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,iEAAiE;IACjE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,8DAA8D;IAC9D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,6DAA6D;IAC7D,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,mBAAmB,GAAG,MAAM,CAwBrE"}
@@ -47,16 +47,22 @@ export function getProdScripts(slug, nonce) {
47
47
  }
48
48
  export function getStudioScripts(options) {
49
49
  const nonceAttr = getNonceAttr(options.nonce);
50
- const params = new URLSearchParams({
50
+ const bridgeConfig = {
51
51
  projectId: options.projectId,
52
52
  pageId: options.pageId,
53
- ...(options.pagePath ? { pagePath: options.pagePath } : {}),
54
- ...(options.wsUrl ? { wsUrl: options.wsUrl } : {}),
55
- ...(options.yjsGuid ? { yjsGuid: options.yjsGuid } : {}),
56
- }).toString();
53
+ pagePath: options.pagePath ?? options.pageId,
54
+ };
55
+ if (options.wsUrl)
56
+ bridgeConfig.wsUrl = options.wsUrl;
57
+ if (options.yjsGuid)
58
+ bridgeConfig.yjsGuid = options.yjsGuid;
57
59
  const sourceHashScript = options.sourceHash
58
- ? `<script${nonceAttr}>window.__VERYFRONT_SOURCE_HASH__="${options.sourceHash}";</script>\n `
60
+ ? `<script${nonceAttr}>window.__VERYFRONT_SOURCE_HASH__=${JSON.stringify(options.sourceHash).replace(/</g, "\\u003c")};</script>\n `
59
61
  : "";
62
+ // Escape </script> sequences to prevent XSS breakout from inline JSON
63
+ const safeJson = JSON.stringify(bridgeConfig).replace(/</g, "\\u003c");
64
+ const configScript = `<script${nonceAttr}>window.__VF_BRIDGE_CONFIG__=${safeJson};</script>`;
60
65
  return `
61
- ${sourceHashScript}<script type="module" src="/_veryfront/studio-bridge.js?${params}"${nonceAttr}></script>`;
66
+ ${sourceHashScript}${configScript}
67
+ <script type="module" src="/_veryfront/studio-bridge.js"${nonceAttr}></script>`;
62
68
  }
@@ -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,uFAAuF;IACvF,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAuED;;;;;;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;AAKrD,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;AA0ED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,mBAAmB,GAAG,MAAM,CA2FzE"}
@@ -1,4 +1,3 @@
1
- import { generateStudioBridgeScript } from "../../../studio/bridge-template.js";
2
1
  import { escapeHtml } from "../../../utils/html-escape.js";
3
2
  /**
4
3
  * Detect the preferred color theme from request parameters and client hints.
@@ -49,13 +48,19 @@ function buildStudioScript(url, projectId, filePath, branchId, apiBaseUrl) {
49
48
  }
50
49
  }
51
50
  const yjsGuid = branchId ? `${canonicalProjectId}:${branchId}` : canonicalProjectId;
52
- return `<script>${generateStudioBridgeScript({
51
+ const bridgeConfig = {
53
52
  projectId: canonicalProjectId,
54
53
  pageId: canonicalPageId,
55
54
  pagePath: filePath,
56
- wsUrl,
57
- yjsGuid,
58
- })}</script>`;
55
+ };
56
+ if (wsUrl)
57
+ bridgeConfig.wsUrl = wsUrl;
58
+ if (yjsGuid)
59
+ bridgeConfig.yjsGuid = yjsGuid;
60
+ // Escape </script> sequences to prevent XSS breakout from inline JSON
61
+ const safeJson = JSON.stringify(bridgeConfig).replace(/</g, "\\u003c");
62
+ return `<script>window.__VF_BRIDGE_CONFIG__=${safeJson};</script>
63
+ <script type="module" src="/_veryfront/studio-bridge.js"></script>`;
59
64
  }
60
65
  /**
61
66
  * Generate a complete HTML document for markdown preview rendering.
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Studio Bridge Handler
3
+ *
4
+ * Bundles the decomposed bridge TypeScript modules into a single JS file
5
+ * using esbuild (same JIT pipeline as DevFileHandler).
6
+ *
7
+ * Route: /_veryfront/studio-bridge.js
8
+ *
9
+ * @module server/handlers/studio/bridge-modules
10
+ */
11
+ import * as dntShim from "../../../../_dnt.shims.js";
12
+ import { BaseHandler } from "../../../security/index.js";
13
+ import type { HandlerContext, HandlerMetadata, HandlerResult } from "../types.js";
14
+ export declare class StudioBridgeModulesHandler extends BaseHandler {
15
+ metadata: HandlerMetadata;
16
+ handle(req: dntShim.Request, ctx: HandlerContext): Promise<HandlerResult>;
17
+ }
18
+ /**
19
+ * Invalidate the bundle cache.
20
+ * Used by dev file watchers to bust the cache on source changes.
21
+ */
22
+ export declare function invalidateBridgeModuleCache(): void;
23
+ //# sourceMappingURL=bridge-modules.handler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bridge-modules.handler.d.ts","sourceRoot":"","sources":["../../../../../src/src/server/handlers/studio/bridge-modules.handler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,KAAK,OAAO,MAAM,2BAA2B,CAAC;AAGrD,OAAO,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AACzD,OAAO,KAAK,EACV,cAAc,EACd,eAAe,EAEf,aAAa,EACd,MAAM,aAAa,CAAC;AAkDrB,qBAAa,0BAA2B,SAAQ,WAAW;IACzD,QAAQ,EAAE,eAAe,CAKvB;IAEI,MAAM,CAAC,GAAG,EAAE,OAAO,CAAC,OAAO,EAAE,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC;CAwChF;AAED;;;GAGG;AACH,wBAAgB,2BAA2B,IAAI,IAAI,CAElD"}
@@ -0,0 +1,100 @@
1
+ /**
2
+ * Studio Bridge Handler
3
+ *
4
+ * Bundles the decomposed bridge TypeScript modules into a single JS file
5
+ * using esbuild (same JIT pipeline as DevFileHandler).
6
+ *
7
+ * Route: /_veryfront/studio-bridge.js
8
+ *
9
+ * @module server/handlers/studio/bridge-modules
10
+ */
11
+ import * as dntShim from "../../../../_dnt.shims.js";
12
+ import { BaseHandler } from "../../../security/index.js";
13
+ import { HTTP_OK, PRIORITY_HIGH_DEV } from "../../../utils/constants/index.js";
14
+ /** Cached bundle output. */
15
+ let bundleCache = null;
16
+ /** Resolve the bridge source directory from this module's location. */
17
+ const BRIDGE_DIR = new URL("../../../studio/bridge/", globalThis[Symbol.for("import-meta-ponyfill-esmodule")](import.meta).url).pathname;
18
+ /**
19
+ * Compute a content hash for ETag.
20
+ */
21
+ async function computeEtag(content) {
22
+ const data = new TextEncoder().encode(content);
23
+ const hashBuffer = await dntShim.crypto.subtle.digest("SHA-256", data);
24
+ const hashArray = Array.from(new Uint8Array(hashBuffer));
25
+ return hashArray.slice(0, 16).map((b) => b.toString(16).padStart(2, "0")).join("");
26
+ }
27
+ /**
28
+ * Bundle the bridge coordinator (and all its imports) into a single JS file.
29
+ * Uses esbuild.build() with bundle:true — same approach as DevFileHandler.
30
+ */
31
+ async function bundleBridge() {
32
+ if (bundleCache)
33
+ return bundleCache;
34
+ const entryPoint = `${BRIDGE_DIR}bridge-coordinator.ts`;
35
+ const source = await dntShim.Deno.readTextFile(entryPoint);
36
+ const { build } = await import("esbuild");
37
+ const { outputFiles } = await build({
38
+ bundle: true,
39
+ write: false,
40
+ format: "esm",
41
+ platform: "browser",
42
+ target: "es2022",
43
+ stdin: {
44
+ contents: source,
45
+ loader: "ts",
46
+ resolveDir: BRIDGE_DIR,
47
+ sourcefile: entryPoint,
48
+ },
49
+ });
50
+ const js = outputFiles?.[0]?.text ?? "";
51
+ const etag = await computeEtag(js);
52
+ bundleCache = { js, etag };
53
+ return bundleCache;
54
+ }
55
+ export class StudioBridgeModulesHandler extends BaseHandler {
56
+ metadata = {
57
+ name: "StudioBridgeModulesHandler",
58
+ priority: PRIORITY_HIGH_DEV,
59
+ patterns: [{ pattern: "/_veryfront/studio-bridge.js", exact: true }],
60
+ enabled: () => true,
61
+ };
62
+ async handle(req, ctx) {
63
+ if (!this.shouldHandle(req, ctx)) {
64
+ return this.continue();
65
+ }
66
+ const ifNoneMatch = req.headers.get("if-none-match");
67
+ try {
68
+ const { js, etag } = await bundleBridge();
69
+ if (ifNoneMatch === `"${etag}"`) {
70
+ return this.respond(new dntShim.Response(null, {
71
+ status: 304,
72
+ headers: { ETag: `"${etag}"`, "Cache-Control": "no-cache" },
73
+ }));
74
+ }
75
+ return this.respond(new dntShim.Response(js, {
76
+ status: HTTP_OK,
77
+ headers: {
78
+ "Content-Type": "application/javascript; charset=utf-8",
79
+ "Cache-Control": "no-cache",
80
+ ETag: `"${etag}"`,
81
+ },
82
+ }));
83
+ }
84
+ catch (error) {
85
+ const message = error instanceof Error ? error.message : String(error);
86
+ console.error("[StudioBridgeHandler] Bundle error:", message);
87
+ return this.respond(new dntShim.Response(`// Bundle error: ${message}`, {
88
+ status: 500,
89
+ headers: { "Content-Type": "application/javascript; charset=utf-8" },
90
+ }));
91
+ }
92
+ }
93
+ }
94
+ /**
95
+ * Invalidate the bundle cache.
96
+ * Used by dev file watchers to bust the cache on source changes.
97
+ */
98
+ export function invalidateBridgeModuleCache() {
99
+ bundleCache = null;
100
+ }
@@ -28,7 +28,7 @@ import { DevEndpointsHandler } from "../handlers/dev/endpoints.handler.js";
28
28
  import { DevFileHandler } from "../handlers/dev/files/index.js";
29
29
  import { DebugContextHandler } from "../handlers/dev/debug-context.handler.js";
30
30
  import { StylesCSSHandler } from "../handlers/dev/styles-css.handler.js";
31
- import { StudioEndpointsHandler } from "../handlers/studio/endpoints.handler.js";
31
+ import { StudioBridgeModulesHandler } from "../handlers/studio/bridge-modules.handler.js";
32
32
  import { StaticHandler } from "../handlers/request/static.handler.js";
33
33
  import { SnippetHandler } from "../handlers/request/snippet.handler.js";
34
34
  import { LibModulesHandler } from "../handlers/request/lib-modules.handler.js";
@@ -123,7 +123,7 @@ export function createVeryfrontHandler(projectDir, adapter, opts = { projectDir
123
123
  new OpenAPIDocsHandler(),
124
124
  new DevDashboardHandler(),
125
125
  new ProjectsHandler(),
126
- new StudioEndpointsHandler(),
126
+ new StudioBridgeModulesHandler(),
127
127
  new CSSHandler(),
128
128
  new DevFileHandler(),
129
129
  new SnippetHandler(),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "veryfront",
3
- "version": "0.1.37",
3
+ "version": "0.1.39",
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.37",
3
+ "version": "0.1.39",
4
4
  "license": "Apache-2.0",
5
5
  "nodeModulesDir": "auto",
6
6
  "exclude": [
@@ -352,7 +352,8 @@ export default {
352
352
  ],
353
353
  "exclude": [
354
354
  "dist/",
355
- "coverage/"
355
+ "coverage/",
356
+ "src/studio/bridge/"
356
357
  ],
357
358
  "rules": {
358
359
  "tags": [
@@ -68,18 +68,25 @@ export interface StudioScriptOptions {
68
68
  export function getStudioScripts(options: StudioScriptOptions): string {
69
69
  const nonceAttr = getNonceAttr(options.nonce);
70
70
 
71
- const params = new URLSearchParams({
71
+ const bridgeConfig: Record<string, unknown> = {
72
72
  projectId: options.projectId,
73
73
  pageId: options.pageId,
74
- ...(options.pagePath ? { pagePath: options.pagePath } : {}),
75
- ...(options.wsUrl ? { wsUrl: options.wsUrl } : {}),
76
- ...(options.yjsGuid ? { yjsGuid: options.yjsGuid } : {}),
77
- }).toString();
74
+ pagePath: options.pagePath ?? options.pageId,
75
+ };
76
+ if (options.wsUrl) bridgeConfig.wsUrl = options.wsUrl;
77
+ if (options.yjsGuid) bridgeConfig.yjsGuid = options.yjsGuid;
78
78
 
79
79
  const sourceHashScript = options.sourceHash
80
- ? `<script${nonceAttr}>window.__VERYFRONT_SOURCE_HASH__="${options.sourceHash}";</script>\n `
80
+ ? `<script${nonceAttr}>window.__VERYFRONT_SOURCE_HASH__=${
81
+ JSON.stringify(options.sourceHash).replace(/</g, "\\u003c")
82
+ };</script>\n `
81
83
  : "";
82
84
 
85
+ // Escape </script> sequences to prevent XSS breakout from inline JSON
86
+ const safeJson = JSON.stringify(bridgeConfig).replace(/</g, "\\u003c");
87
+ const configScript = `<script${nonceAttr}>window.__VF_BRIDGE_CONFIG__=${safeJson};</script>`;
88
+
83
89
  return `
84
- ${sourceHashScript}<script type="module" src="/_veryfront/studio-bridge.js?${params}"${nonceAttr}></script>`;
90
+ ${sourceHashScript}${configScript}
91
+ <script type="module" src="/_veryfront/studio-bridge.js"${nonceAttr}></script>`;
85
92
  }
@@ -10,7 +10,6 @@
10
10
  import * as dntShim from "../../../../_dnt.shims.js";
11
11
 
12
12
 
13
- import { generateStudioBridgeScript } from "../../../studio/bridge-template.js";
14
13
  import { escapeHtml } from "../../../utils/html-escape.js";
15
14
 
16
15
  /** Options for generating markdown preview HTML. */
@@ -93,15 +92,18 @@ function buildStudioScript(
93
92
  }
94
93
  const yjsGuid = branchId ? `${canonicalProjectId}:${branchId}` : canonicalProjectId;
95
94
 
96
- return `<script>${
97
- generateStudioBridgeScript({
98
- projectId: canonicalProjectId,
99
- pageId: canonicalPageId,
100
- pagePath: filePath,
101
- wsUrl,
102
- yjsGuid,
103
- })
104
- }</script>`;
95
+ const bridgeConfig: Record<string, unknown> = {
96
+ projectId: canonicalProjectId,
97
+ pageId: canonicalPageId,
98
+ pagePath: filePath,
99
+ };
100
+ if (wsUrl) bridgeConfig.wsUrl = wsUrl;
101
+ if (yjsGuid) bridgeConfig.yjsGuid = yjsGuid;
102
+
103
+ // Escape </script> sequences to prevent XSS breakout from inline JSON
104
+ const safeJson = JSON.stringify(bridgeConfig).replace(/</g, "\\u003c");
105
+ return `<script>window.__VF_BRIDGE_CONFIG__=${safeJson};</script>
106
+ <script type="module" src="/_veryfront/studio-bridge.js"></script>`;
105
107
  }
106
108
 
107
109
  /**
@@ -0,0 +1,126 @@
1
+ /**
2
+ * Studio Bridge Handler
3
+ *
4
+ * Bundles the decomposed bridge TypeScript modules into a single JS file
5
+ * using esbuild (same JIT pipeline as DevFileHandler).
6
+ *
7
+ * Route: /_veryfront/studio-bridge.js
8
+ *
9
+ * @module server/handlers/studio/bridge-modules
10
+ */
11
+ import * as dntShim from "../../../../_dnt.shims.js";
12
+
13
+
14
+ import { BaseHandler } from "../../../security/index.js";
15
+ import type {
16
+ HandlerContext,
17
+ HandlerMetadata,
18
+ HandlerPriority,
19
+ HandlerResult,
20
+ } from "../types.js";
21
+ import { HTTP_OK, PRIORITY_HIGH_DEV } from "../../../utils/constants/index.js";
22
+
23
+ /** Cached bundle output. */
24
+ let bundleCache: { js: string; etag: string } | null = null;
25
+
26
+ /** Resolve the bridge source directory from this module's location. */
27
+ const BRIDGE_DIR = new URL("../../../studio/bridge/", import.meta.url).pathname;
28
+
29
+ /**
30
+ * Compute a content hash for ETag.
31
+ */
32
+ async function computeEtag(content: string): Promise<string> {
33
+ const data = new TextEncoder().encode(content);
34
+ const hashBuffer = await dntShim.crypto.subtle.digest("SHA-256", data);
35
+ const hashArray = Array.from(new Uint8Array(hashBuffer));
36
+ return hashArray.slice(0, 16).map((b) => b.toString(16).padStart(2, "0")).join("");
37
+ }
38
+
39
+ /**
40
+ * Bundle the bridge coordinator (and all its imports) into a single JS file.
41
+ * Uses esbuild.build() with bundle:true — same approach as DevFileHandler.
42
+ */
43
+ async function bundleBridge(): Promise<{ js: string; etag: string }> {
44
+ if (bundleCache) return bundleCache;
45
+
46
+ const entryPoint = `${BRIDGE_DIR}bridge-coordinator.ts`;
47
+ const source = await dntShim.Deno.readTextFile(entryPoint);
48
+
49
+ const { build } = await import("esbuild");
50
+ const { outputFiles } = await build({
51
+ bundle: true,
52
+ write: false,
53
+ format: "esm",
54
+ platform: "browser",
55
+ target: "es2022",
56
+ stdin: {
57
+ contents: source,
58
+ loader: "ts",
59
+ resolveDir: BRIDGE_DIR,
60
+ sourcefile: entryPoint,
61
+ },
62
+ });
63
+
64
+ const js = outputFiles?.[0]?.text ?? "";
65
+ const etag = await computeEtag(js);
66
+ bundleCache = { js, etag };
67
+ return bundleCache;
68
+ }
69
+
70
+ export class StudioBridgeModulesHandler extends BaseHandler {
71
+ metadata: HandlerMetadata = {
72
+ name: "StudioBridgeModulesHandler",
73
+ priority: PRIORITY_HIGH_DEV as HandlerPriority,
74
+ patterns: [{ pattern: "/_veryfront/studio-bridge.js", exact: true }],
75
+ enabled: () => true,
76
+ };
77
+
78
+ async handle(req: dntShim.Request, ctx: HandlerContext): Promise<HandlerResult> {
79
+ if (!this.shouldHandle(req, ctx)) {
80
+ return this.continue();
81
+ }
82
+
83
+ const ifNoneMatch = req.headers.get("if-none-match");
84
+
85
+ try {
86
+ const { js, etag } = await bundleBridge();
87
+
88
+ if (ifNoneMatch === `"${etag}"`) {
89
+ return this.respond(
90
+ new dntShim.Response(null, {
91
+ status: 304,
92
+ headers: { ETag: `"${etag}"`, "Cache-Control": "no-cache" },
93
+ }),
94
+ );
95
+ }
96
+
97
+ return this.respond(
98
+ new dntShim.Response(js, {
99
+ status: HTTP_OK,
100
+ headers: {
101
+ "Content-Type": "application/javascript; charset=utf-8",
102
+ "Cache-Control": "no-cache",
103
+ ETag: `"${etag}"`,
104
+ },
105
+ }),
106
+ );
107
+ } catch (error) {
108
+ const message = error instanceof Error ? error.message : String(error);
109
+ console.error("[StudioBridgeHandler] Bundle error:", message);
110
+ return this.respond(
111
+ new dntShim.Response(`// Bundle error: ${message}`, {
112
+ status: 500,
113
+ headers: { "Content-Type": "application/javascript; charset=utf-8" },
114
+ }),
115
+ );
116
+ }
117
+ }
118
+ }
119
+
120
+ /**
121
+ * Invalidate the bundle cache.
122
+ * Used by dev file watchers to bust the cache on source changes.
123
+ */
124
+ export function invalidateBridgeModuleCache(): void {
125
+ bundleCache = null;
126
+ }
@@ -39,7 +39,7 @@ import { DevEndpointsHandler } from "../handlers/dev/endpoints.handler.js";
39
39
  import { DevFileHandler } from "../handlers/dev/files/index.js";
40
40
  import { DebugContextHandler } from "../handlers/dev/debug-context.handler.js";
41
41
  import { StylesCSSHandler } from "../handlers/dev/styles-css.handler.js";
42
- import { StudioEndpointsHandler } from "../handlers/studio/endpoints.handler.js";
42
+ import { StudioBridgeModulesHandler } from "../handlers/studio/bridge-modules.handler.js";
43
43
  import { StaticHandler } from "../handlers/request/static.handler.js";
44
44
  import { SnippetHandler } from "../handlers/request/snippet.handler.js";
45
45
  import { LibModulesHandler } from "../handlers/request/lib-modules.handler.js";
@@ -206,7 +206,7 @@ export function createVeryfrontHandler(
206
206
  new OpenAPIDocsHandler(),
207
207
  new DevDashboardHandler(),
208
208
  new ProjectsHandler(),
209
- new StudioEndpointsHandler(),
209
+ new StudioBridgeModulesHandler(),
210
210
  new CSSHandler(),
211
211
  new DevFileHandler(),
212
212
  new SnippetHandler(),
@@ -1,12 +0,0 @@
1
- /**
2
- * Studio Endpoints Handler
3
- * Handles studio bridge script and other studio-specific endpoints
4
- */
5
- import * as dntShim from "../../../../_dnt.shims.js";
6
- import { BaseHandler } from "../../../security/index.js";
7
- import type { HandlerContext, HandlerMetadata, HandlerResult } from "../types.js";
8
- export declare class StudioEndpointsHandler extends BaseHandler {
9
- metadata: HandlerMetadata;
10
- handle(req: dntShim.Request, ctx: HandlerContext): Promise<HandlerResult>;
11
- }
12
- //# sourceMappingURL=endpoints.handler.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"endpoints.handler.d.ts","sourceRoot":"","sources":["../../../../../src/src/server/handlers/studio/endpoints.handler.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,OAAO,MAAM,2BAA2B,CAAC;AAGrD,OAAO,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AACzD,OAAO,KAAK,EACV,cAAc,EACd,eAAe,EAEf,aAAa,EACd,MAAM,aAAa,CAAC;AAIrB,qBAAa,sBAAuB,SAAQ,WAAW;IACrD,QAAQ,EAAE,eAAe,CAKvB;IAEF,MAAM,CAAC,GAAG,EAAE,OAAO,CAAC,OAAO,EAAE,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC;CAuB1E"}
@@ -1,29 +0,0 @@
1
- import { BaseHandler } from "../../../security/index.js";
2
- import { HTTP_OK, PRIORITY_HIGH_DEV } from "../../../utils/constants/index.js";
3
- import { generateStudioBridgeScript } from "../../../studio/bridge-template.js";
4
- export class StudioEndpointsHandler extends BaseHandler {
5
- metadata = {
6
- name: "StudioEndpointsHandler",
7
- priority: PRIORITY_HIGH_DEV,
8
- patterns: [{ pattern: "/_veryfront/studio-bridge.js", exact: true }],
9
- enabled: () => true, // Always enabled - studio_embed check is in the script
10
- };
11
- handle(req, ctx) {
12
- if (!this.shouldHandle(req, ctx)) {
13
- return Promise.resolve(this.continue());
14
- }
15
- const url = new URL(req.url);
16
- if (url.pathname !== "/_veryfront/studio-bridge.js") {
17
- return Promise.resolve(this.continue());
18
- }
19
- const builder = this.createResponseBuilder(ctx);
20
- const projectId = url.searchParams.get("projectId") ?? "";
21
- const pageId = url.searchParams.get("pageId") ?? "";
22
- const pagePath = url.searchParams.get("pagePath") ?? undefined;
23
- const wsUrl = url.searchParams.get("wsUrl") ?? undefined;
24
- const yjsGuid = url.searchParams.get("yjsGuid") ?? undefined;
25
- const script = generateStudioBridgeScript({ projectId, pageId, pagePath, wsUrl, yjsGuid });
26
- const response = builder.withCache("no-cache").javascript(script, HTTP_OK);
27
- return Promise.resolve(this.respond(response));
28
- }
29
- }
@@ -1,11 +0,0 @@
1
- export interface StudioBridgeOptions {
2
- projectId: string;
3
- pageId: string;
4
- pagePath?: string;
5
- wsUrl?: string;
6
- yjsGuid?: string;
7
- debugSkipInit?: boolean;
8
- debugExposeInternals?: boolean;
9
- }
10
- export declare function generateStudioBridgeScript(options: StudioBridgeOptions): string;
11
- //# sourceMappingURL=bridge-template.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"bridge-template.d.ts","sourceRoot":"","sources":["../../../src/src/studio/bridge-template.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,oBAAoB,CAAC,EAAE,OAAO,CAAC;CAChC;AAED,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,mBAAmB,GAAG,MAAM,CAsvI/E"}