Skip to content

Commit fe7e9f0

Browse files
authored
Add /tidewave/control (#62)
1 parent e71e786 commit fe7e9f0

22 files changed

Lines changed: 1428 additions & 142 deletions

bun.lock

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,13 +66,15 @@
6666
"@modelcontextprotocol/sdk": "^1.17.4",
6767
"body-parser": "^2.2.0",
6868
"typescript": "^5",
69+
"ws": "^8.21.1",
6970
"zod": "3.25.76"
7071
},
7172
"devDependencies": {
7273
"@eslint/js": "^9.16.0",
7374
"@types/body-parser": "^1.19.6",
7475
"@types/bun": "latest",
7576
"@types/node": "^22.10.1",
77+
"@types/ws": "^8.18.1",
7678
"@typescript-eslint/eslint-plugin": "^8.18.0",
7779
"@typescript-eslint/parser": "^8.18.0",
7880
"@vitest/ui": "^3.2.4",

src/config.ts

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
import type { TidewaveConfig } from './core';
2+
import { default as tidewavePackage } from '../package.json' with { type: 'json' };
3+
4+
export type LocalScheme = 'http' | 'https';
5+
6+
export interface LocalRequestInfo {
7+
port: number | undefined;
8+
scheme: LocalScheme;
9+
}
10+
11+
export type LocalRequestInfoGetter<Request = unknown> = (request?: Request) => LocalRequestInfo;
12+
13+
export interface TidewaveConfigPayload {
14+
project_name: string;
15+
framework_type: string;
16+
tidewave_version: string;
17+
team: NonNullable<TidewaveConfig['team']> | Record<string, never>;
18+
local_port: number | undefined;
19+
local_scheme: LocalScheme;
20+
tmp_dir: string;
21+
}
22+
23+
export interface TidewaveMetaConfig {
24+
tidewave: TidewaveConfigPayload;
25+
root: string;
26+
wsl_distro: string | null;
27+
framework: Record<string, string>;
28+
}
29+
30+
export function tidewaveConfig<Request = unknown>(
31+
config: TidewaveConfig,
32+
getLocalRequestInfo?: LocalRequestInfoGetter<Request>,
33+
request?: Request,
34+
): TidewaveConfigPayload {
35+
const localRequestInfo = getLocalRequestInfo?.(request) ?? defaultLocalRequestInfo();
36+
37+
if (!config.projectName) {
38+
throw new Error('Tidewave projectName must be configured');
39+
}
40+
41+
if (!config.framework) {
42+
throw new Error('Tidewave framework must be configured');
43+
}
44+
45+
return {
46+
project_name: config.projectName,
47+
framework_type: config.framework,
48+
tidewave_version: tidewavePackage.version,
49+
team: config.team || {},
50+
local_port: localRequestInfo.port,
51+
local_scheme: localRequestInfo.scheme,
52+
tmp_dir: config.tmpDir || 'tmp',
53+
};
54+
}
55+
56+
export function tidewaveConfigMeta<Request = unknown>(
57+
config: TidewaveConfig,
58+
getLocalRequestInfo?: LocalRequestInfoGetter<Request>,
59+
request?: Request,
60+
): TidewaveMetaConfig {
61+
return {
62+
tidewave: tidewaveConfig(config, getLocalRequestInfo, request),
63+
root: process.cwd(),
64+
wsl_distro: process.env['WSL_DISTRO_NAME'] ?? null,
65+
framework: {},
66+
};
67+
}
68+
69+
export function tidewaveConfigMetaHtml<Request = unknown>(
70+
config: TidewaveConfig,
71+
getLocalRequestInfo?: LocalRequestInfoGetter<Request>,
72+
request?: Request,
73+
): string {
74+
return `<meta name="tidewave:config" content="${escapeHtmlAttribute(
75+
JSON.stringify(tidewaveConfigMeta(config, getLocalRequestInfo, request)),
76+
)}" />`;
77+
}
78+
79+
function defaultLocalRequestInfo(): LocalRequestInfo {
80+
return { port: undefined, scheme: 'http' };
81+
}
82+
83+
export function escapeHtmlAttribute(value: string): string {
84+
return value
85+
.replaceAll('&', '&amp;')
86+
.replaceAll('"', '&quot;')
87+
.replaceAll('<', '&lt;')
88+
.replaceAll('>', '&gt;');
89+
}

0 commit comments

Comments
 (0)